diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bbae689128796..5db811bbb7d7a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,7 @@ Replace this paragraph with a description of your changes and rationale. Provide links to external references/discussions if appropriate. -Resolves [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN). +Resolves SR-NNNN. + +* [Introduction](#introduction) +* [Motivation](#motivation) + * [Background](#background) + * [Intelligent applications](#intelligent-applications) + * [Type-safe machine learning](#type-safe-machine-learning) + * [Calculus is fun](#calculus-is-fun) + * [Animations](#animations) + * [Games](#games) + * [Simulations](#simulations) + * [Robotics](#robotics) + * [Rendering and ray tracing](#rendering-and-ray-tracing) +* [History of differentiation algorithms](#history-of-differentiation-algorithms) + * [Numerical differentiation](#numerical-differentiation) + * [Symbolic differentiation](#symbolic-differentiation) + * [Automatic differentiation](#automatic-differentiation) +* [Approaches to automatic differentiation](#approaches-to-automatic-differentiation) + * [Embedded domain-specific languages](#embedded-domain-specific-languages) + * [Source code transformation tools](#source-code-transformation-tools) + * [First-class language support](#first-class-language-support) + * [Why bake differentiation into Swift?](#why-bake-differentiation-into-swift) + * [Maximal coverage of Swift language features](#maximal-coverage-of-swift-language-features) + * [Extensibility](#extensibility) + * [Static warnings and errors](#static-warnings-and-errors) + * [The pursuit for user-defined code transformations](#the-pursuit-for-user-defined-code-transformations) +* [Math introduction](#math-introduction) + * [What is a derivative?](#what-is-a-derivative) + * [Iterative optimization](#iterative-optimization) + * [Derivatives of functions with arbitrary inputs](#derivatives-of-functions-with-arbitrary-inputs) +* [Proposed solution](#proposed-solution) + * [The `Differentiable` protocol](#the-differentiable-protocol) + * [The `@differentiable` declaration attribute](#the-differentiable-declaration-attribute) + * [`@differentiable` function types](#differentiable-function-types) + * [`@derivative` and `@transpose` attributes](#derivative-and-transpose-attributes) + * [Differential operators](#differential-operators) +* [Detailed design](#detailed-design) + * [Differentiable data structures](#differentiable-data-structures) + * [The `Differentiable` protocol](#the-differentiable-protocol-1) + * [`Differentiable` conformances](#differentiable-conformances) + * [Compiler-synthesized conformances](#compiler-synthesized-conformances) + * [Synthesis conditions](#synthesis-conditions) + * [Default synthesis](#default-synthesis) + * [Shortcut synthesis](#shortcut-synthesis) + * [Differentiable function declarations](#differentiable-function-declarations) + * [The `@differentiable` declaration attribute](#the-differentiable-declaration-attribute-1) + * [Conformance and subclassing](#conformance-and-subclassing) + * [Protocol dispatch](#protocol-dispatch) + * [Class dispatch](#class-dispatch) + * [Make a function differentiable using `@derivative` or + `@transpose`](#make-a-function-differentiable-using-derivative-or-transpose) + * [Linear maps](#linear-maps) + * [Typing rules](#typing-rules) + * [Linearity parameters](#linearity-parameters) + * [Top-level functions](#top-level-functions) + * [Static methods](#static-methods) + * [Instance methods](#instance-methods) + * [Linearity generic requirements](#linearity-generic-requirements) + * [Examples](#examples) + * [Derivative functions](#derivative-functions) + * [Typing rules](#typing-rules-1) + * [Differentiability parameters](#differentiability-parameters) + * [Differentiability generic requirements](#differentiability-generic-requirements) + * [Examples](#examples-1) + * [Differentiable function types](#differentiable-function-types-1) + * [Function subtyping and runtime representation](#function-subtyping-and-runtime-representation) + * [The `@differentiable` function type attribute](#the-differentiable-function-type-attribute) + * [Type conversion](#type-conversion) + * [Coercing function declarations into `@differentiable` function values](#coercing-function-declarations-into-differentiable-function-values) + * [Upcasting to non-`@differentiable` functions](#upcasting-to-non-differentiable-functions) + * [Implied generic constraints](#implied-generic-constraints) + * [Non-differentiable parameters](#non-differentiable-parameters) + * [Higher-order functions and currying](#higher-order-functions-and-currying) + * [Differential operators](#differential-operators) + * [Differential-producing differential operators](#differential-producing-differential-operators) + * [Pullback-producing differential operators](#pullback-producing-differential-operators) + * [Example usage](#example-usage) + * [List of differential operators](#list-of-differential-operators) + * [Static analysis](#static-analysis) + * [Cross-module opacity](#cross-module-opacity) + * [Non-differentiable type conversions](#non-differentiable-type-conversions) + * [Accidental data flow mistakes](#accidental-data-flow-mistakes) +* [Examples of differentiable programming](#examples-of-differentiable-programming) + * [Linear regression](#linear-regression) + * [Deep learning](#deep-learning) + * [Feed-forward neural networks (FFNN)](#feed-forward-neural-networks-ffnn) + * [Convolutional neural networks (CNN)](#convolutional-neural-networks-cnn) + * [Recurrent neural networks (RNN)](#recurrent-neural-networks-rnn) +* [Future directions](#future-directions) + * [Higher-order differentiation](#higher-order-differentiation) + * [Naming conventions for numerical computing](#naming-conventions-for-numerical-computing) +* [Source compatibility](#source-compatibility) + * [Differentiable protocol](#differentiable-protocol) + * [Differential operators](#differential-operators-1) +* [Alternatives considered](#alternatives-considered) + * [Not support differentiable programming](#not-support-differentiable-programming) + * [Use another language or framework for differentiable programming](#use-another-language-or-framework-for-differentiable-programming) + * [Other approaches to differentiable programming](#other-approaches-to-differentiable-programming) +* [Acknowledgements](#acknowledgements) + +## Introduction + +This proposal introduces first-class differentiable programming to Swift. +First-class differentiable programming includes five core additions: + +- The `Differentiable` protocol. +- `@differentiable` function types. +- The `@differentiable` declaration attribute for defining differentiable + functions. +- The `@derivative` and `@transpose` attributes for defining custom + derivatives. +- Differential operators (e.g. `derivative(of:)`) in the standard library. + +Differentiable programming is a new paradigm for programming in which programs +can be differentiated throughout. At a glance, differentiable programming lets +you take the derivative of functions whose parameters and results conform to the +`Differentiable` protocol. + +```swift +@differentiable +func f(_ x: Float) -> Float { + x * x +} +let dfdx = derivative(of: f) +dfdx(3) // 6 +``` + +The ability to get derivatives of programs enables a new world of numerical +computing applications, notably machine learning. With first-class support, +gradient-based learning algorithms can even be built using standard library +types such as `Float` and `SIMD64` and be differentiated using +protocol-oriented APIs such as `valueWithGradient(at:in:)`. + +```swift +struct Perceptron: @memberwise Differentiable { + var weight: SIMD2 = .random(in: -1..<1) + var bias: Float = 0 + + @differentiable + func callAsFunction(_ input: SIMD2) -> Float { + (weight * input).sum() + bias + } +} + +var model = Perceptron() +let andGateData: [(x: SIMD2, y: Float)] = [ + (x: [0, 0], y: 0), + (x: [0, 1], y: 0), + (x: [1, 0], y: 0), + (x: [1, 1], y: 1), +] +for _ in 0..<100 { + let (loss, 𝛁loss) = valueWithGradient(at: model) { model -> Float in + var loss: Float = 0 + for (x, y) in andGateData { + let ŷ = model(x) + let error = y - ŷ + loss = loss + error * error / 2 + } + return loss + } + print(loss) + model.weight -= 𝛁loss.weight * 0.02 + model.bias -= 𝛁loss.bias * 0.02 +} +``` + +Differentiable programming scales up to full machine learning models, built with +third-party libraries like [TensorFlow](https://github.com/tensorflow/swift). + +```swift +import TensorFlow + +let model = Sequential { + Dense(inputSize: 784, outputSize: 100, activation: relu) + Dense(inputSize: 100, outputSize: 30, activation: relu) + Dense(inputSize: 30, outputSize: 3, activation: identity) +} + +var classifier = Model() +let optimizer = SGD(for: classifier, learningRate: 0.02) +Context.local.learningPhase = .training +let x: Tensor = ... +let y: Tensor = ... + +for _ in 0..<1000 { + let 𝛁model = gradient(at: classifier) { classifier -> Tensor in + let ŷ = classifier(x) + let loss = softmaxCrossEntropy(logits: ŷ, labels: y) + print("Loss: \(loss)") + return loss + } + optimizer.update(&classifier, along: 𝛁model) +} +``` + +While the differentiation APIs are flexible and fully dynamic, differentiation +is based on a program transformation that happens at compile-time. This enables +many static analyses that not only help produce more efficient programs, but +also detect common numerical programming mistakes such as non-differentiable +functions and zero derivatives. + +```swift +let grad = gradient(at: 1.0) { x in + 3.squareRoot() +} +``` + +```console +test.swift:2:18: warning: result does not depend on differentiation arguments and will always have a zero derivative; do you want to add 'withoutDerivative(at:)' to make it explicit? + 3.squareRoot() + ^ + withoutDerivative(at:) +``` + +With a first-class differentiable programming language, some of the most common +runtime errors in machine learning become directly debuggable without library +boundaries. Simply step through backpropagation using LLDB to debug derivatives. + +

+ +
+ + Backpropagation debugging demo using LLDB. + +

+ +## Motivation + +### Background + +In mathematics, a derivative of a function of a real variable is another +function that computes the sensitivity to changes in the output of the original +function with respect to changes in the original function's arguments. +Differentiation is the process of computing derivatives. See the +["Math Introduction"](#math-introduction) section below for more details. + +Derivatives are a fundamental tool in calculus and have applications in many +domains, notably deep learning. As an expressive, high-performance language, +Swift is a great fit for numerical applications. The [Swift Numerics +library][swift-numerics] and recent Swift Evolution proposals have paved the way +for low-level numerical computing in Swift: [AdditiveArithmetic][SE-0233], SIMD +[[1][SE-0229]] [[2][SE-0251]], [generic math functions][SE-0246]. However, +high-level numerical computing applications, including machine learning and +artificial intelligence, require more work. + +We believe that first-class differentiable programming is a big step towards +high-level numerical computing support and will make Swift a real contender in +the numerical computing and machine learning landscape. Differentiable +programming will enable intelligent applications, machine learning models, +scientific experiments, physical simulations, and more. + +### Intelligent applications + +Intelligent applications are smart: they use machine learning techniques to +enhance user experiences. Intelligent applications can make predictions, provide +suggestions, and learn user preferences: all of these can be powered by +differentiable programming. + +The core of an intelligent application is a function with real-valued +parameters. Differentiation can be used to systematically optimize (i.e. find +"good" values for) these parameters via gradient descent. (Optimizing these +parameters via conventional algorithms is typically difficult or intractable.) + +For example, consider a podcast player that tries to automatically adjust the +playback speed based on the podcast type and the podcast section. + +```swift +enum PodcastCategory { + case comedy + case news + ... +} + +enum PodcastSection { + case advertisement + case introduction + case body + case conclusion +} + +struct PodcastState { + let category: PodcastCategory + let section: PodcastSection +} + +struct PodcastSpeedModel { + var minSpeed, maxSpeed: Float + var categoryMultipliers: [PodcastCategory: Float] + var sectionMultipliers: [PodcastSection: Float] + + /// Returns a podcast speed multiplier prediction for the given podcast category + /// and section. + func prediction(for state: PodcastState) -> Float { + let speed = categoryMultipliers[state.category] * sectionMultipliers[state.section] + if speed < minSpeed { return minSpeed } + if speed > maxSpeed { return maxSpeed } + return speed + } +} +``` + +This podcast speed model parameters that determine how quickly the podcast +should play under different circumstances: `minSpeed`, `maxSpeed`, +`categoryMultipliers`, and `sectionMultipliers`. A priori, it is not clear what +good parameter values are, and different users may prefer different parameter +values. + +An intelligent application could determine personalized parameter values as +follows: + +1. Let the user set the speed manually, and record observations whenever the + user changes the speed. + +2. After collecting enough observations, search for parameter values such that + the model predicts speeds close to the user's preferred speed. If such + values are found, offer to start automatically setting the speed. + +"Gradient descent" is an algorithm that performs this search, and a language +that supports differentiable programming makes it easy to implement gradient +descent. Here is some pseudocode illustrating gradient descent. + +First, we need an objective function for gradient descent to minimize. +[Mean absolute error](https://en.wikipedia.org/wiki/Mean_absolute_error) is used +here: + +```swift +struct Observation { + var podcastState: PodcastState + var userSpeed: Float +} + +func meanError(for model: PodcastSpeedModel, _ observations: [Observation]) -> Float { + var error: Float = 0 + for observation in observations { + error += abs(model.prediction(for: observation.state) - observation.userSpeed) + } + return error / Float(observations.count) +} +``` + +Next, we implement the gradient descent algorithm. + +```swift +var model = PodcastModel() +let observations = storage.observations() +for _ in 0..<1000 { + // The language differentiates `meanError` to get a "gradient", which is a value indicating + // how to change `model` in order to decrease the value of `meanError`. + let gradient = gradient(at: model) { meanError(for: $0, observations) } + + // Change `model` in the direction that decreased the value of `meanError`. + model -= 0.01 * gradient +} +``` + +### Type-safe machine learning + +Today, machine learning is predominantly done in dynamically-typed languages +like Python: these languages are concise and easy to use. However, some people +prefer safer programming: features like type checking and static diagnostics +help catch errors early and improve productivity. + +Differentiable programming in Swift enables safe, expressive machine learning. +Custom differentiable data structures can be declared and checked at +compile-time. Thanks to protocol-oriented programming, differentiable types are +generalized by a protocol, enabling differential operators to be defined as +higher-order functions constrained on such a protocol. Mathematical optimization +algorithms such as neural network optimizers can also be defined generically +over such a protocol and work with all differentiable types. + +### Calculus is fun + +Calculus is fun, and differentiation in the Swift toolbox will let programmers +explore that fun. Here are some interesting applications: + +#### Animations + +[Easing functions](https://stackoverflow.com/a/8317722) specify the rate of +change of parameters for animations. Differentiation enables easy manipulation +of these functions. + +#### Games + +Physics equations can be modeled using differentiable functions in game engines. +Intelligent agents in games can be trained using techniques like machine +learning that are enabled by differentiation. + +#### Simulations + +Many simulation techniques for fluids and other physical processes are based on +approximate solutions to equations defined in terms of derivatives, like the +[Euler equations](https://en.wikipedia.org/wiki/Euler_equations_\(fluid_dynamics\)) +and [Navier-Stokes](https://en.wikipedia.org/wiki/Navier–Stokes_equations). +Being able to differentiate functions is an important building block for +implementing algorithms to solve these equations. + +#### Robotics + +Control algorithms used in robotics and mechanical engineering rely on (often +higher-order) derivatives of functions that model the behavior of joints and +other physical systems. A language like Swift that can efficiently compute these +derivatives without incurring the unpredictable runtime overhead of garbage +collection may be well-placed to run aboard robots. + +#### Rendering and ray tracing + +Traditional rendering systems are black boxes that consume data structures with +scene geometry and produce images, but the physical processes they simulate are +made up of differentiable functions. Building a ray tracer out of differentiable +building blocks unlocks applications like inverse rendering (going from an image +to scene geometry). [[1]](https://github.com/BachiLi/redner) +[[2]](https://github.com/avik-pal/RayTracer.jl) + +## History of differentiation algorithms + +There are three main algorithms for computing derivatives: numerical +differentiation, symbolic differentiation, and automatic differentiation. + +### Numerical differentiation + +Numerical differentiation is a technique for estimating derivatives of +mathematical functions using values of the functions. The simplest method uses +the +[difference quotient formula](https://en.wikipedia.org/wiki/Difference_quotient), +introduced in elementary calculus courses: + +

+ +

+ +Numerical differentiation is easy to implement and generalizes to higher-order +derivatives. However, as an estimation approach, it is known to produce +inaccurate results, so it is rarely used when more accurate methods are +available. + +### Symbolic differentiation + +Symbolic differentiation is a technique for computing derivatives of math +expressions via symbolic manipulation, like differentiating an expression using +pen and paper in elementary calculus. This technique is used by computer algebra +systems like Mathematica, but it produces inefficient code when applied to +computer programs due to code bloat with common subexpressions. + +### Automatic differentiation + +Automatic differentiation (AD) is a technique for computing derivatives of +functions. Unlike symbolic differentiation, which operates on math expressions, +automatic differentiation operates on code. + +Automatic differentiation leverages the chain rule of differentiation and the +ability to define temporary values in a program. There are two styles of +automatic differentiation in the traditional sense: forward-mode AD starts with +partial derivatives at inputs and ends by computing partial derivatives at +outputs, while reverse-mode automatic differentiation starts with partial +derivatives at outputs and ends by computing partial derivatives at inputs. + +Mathematically, forward-mode AD corresponds to a fully-right association of the +chain rule of differentiation, and reverse-mode AD corresponds to a fully-left +association. Different associations of the chain rule produce the same result +but may differ in computational complexity†. + +

+ + +
+ + Top: fully-right association of chain rule, starting from partial + derivative of input; "forward-mode". +
+ Bottom: fully-left association of chain rule, starting from output; + "reverse-mode". +
+

+ +Both forward-mode AD and reverse-mode AD are well-explored. Forward-mode AD can +be implemented simply by overloading math operations to compute both original +values and derivatives. Traditionally, reverse-mode AD has been perceived as +being more complicated: implementations typically involve non-local program +transformation and/or mutable tape data structures, though recent research aims +to demystify the subject [[1]](https://arxiv.org/abs/1804.00746) +[[2]](https://arxiv.org/abs/1803.10228). + +†: Finding the optimal association of the chain rule of differentiation is +analogous to the +[matrix chain multiplication](https://en.wikipedia.org/wiki/Matrix_chain_multiplication) +problem and can be solved in `O(n^3)` time. More efficient algorithms also +exist. + +## Approaches to automatic differentiation + +In practice, automatic differentiation is the most common differentiation +algorithm because it is precise and efficient. This section summarizes +approaches to automatic differentiation. + +### Embedded domain-specific languages + +A domain-specific language (DSL) is a language designed to solve problems for a +specific domain. Some DSLs are *external*: these are standalone languages with +their own syntax and semantics, like HTML (a markup language) and SQL (a +database query language). Other DSLs are *embedded* within a more general "host" +language: these DSLs leverage host language constructs and features to define +interesting behavior. Advantages of embedded DSLs include flexibility and +portability: embedded DSLs can be imported as a library. Examples of embedded +DSLs include React (a UI language embedded in JavaScript) and LINQ (a query +language embedded in C#). + +One approach to differentiable programming is to define an embedded DSL for +differentiation *as a library*. This can be done via operator overloading: the +DSL can define a "dual number" type (representing a pair of a real number and +its derivative) and overload differentiable math operations to compute both +original values and derivative values. + +```swift +struct RealWithDerivative { + var value: T + var derivative: T = 0 +} +extension RealWithDerivative { + static func + (lhs: Self, rhs: Self) -> Self { + RealWithDerivative( + value: lhs.value + rhs.value, + derivative: lhs.derivative + rhs.derivative) + } + static func * (lhs: Self, rhs: Self) -> Self { + RealWithDerivative( + value: lhs.value * rhs.value, + derivative: lhs.derivative * rhs.value + lhs.value * rhs.derivative) + } +} + +var x = RealWithDerivative(value: 3, derivative: 1) +// Original: x^2 + x^3 = 3^2 + 3^3 = 36. +// Derivative: 2x + 3x^2 = 2*3 + 3(3)^2 = 33. +var result = x*x + x*x*x +print(result) +// RealWithDerivative(value: 36.0, derivative: 33.0) +``` + +Such a DSL could be extended to be more useful. For example, the `Real` type +could be generalized to multidimensional arrays and more differentiable +operations could be added. + +However, embedded DSLs have some limitations: + +- DSL functionality is often restricted to specific types and APIs. DSLs often + use specialized abstractions rather than general ones for simplicity and to + enable optimizations. For example, many machine learning frameworks are DSLs + that support differentiation only for a particular multidimensional array + type and only using a particular algorithm (reverse-mode automatic + differentiation). Extending a differentiation DSL beyond these limitations + is difficult and may require extra boilerplate: see below. + +- They typically involve some boilerplate. As a host language, Swift currently + supports limited metaprogramming for reducing boilerplate code. For example, + libraries cannot define automatic conformance derivation for library + protocols (though Swift provides it for `Equatable`, `Hashable`, and + `Codable`), so users must write boilerplate conformances for their custom + types. + +- They are limited by the metaprogramming capabilities of the host language. + It is not currently possible to define non-trivial code transformations + (e.g. reverse-mode automatic differentiation) in a Swift library on Swift + code. (Note: SwiftSyntax enables Swift AST transformations but has the extra + indirection of parsing Swift code from a file - it is not possible to + evaluate transformed Swift code from the same file without a general "eval" + mechanism.) To cope with this, some DSLs require explicit program "graph" + building and/or global mutable data structures to mimic the effects of code + transformation, which obfuscate the original transformation semantics. + +- They may not work well with all host language constructs. Embedded DSLs only + support a subset of the host language's features. In particular, some + differentiation DSLs do not support native mutation (e.g. assigning to a + `var`) or native control flow (e.g. `if` constructs) due to technical + limitations, even though supporting them would be ideal. + Restricting/diagnosing unsupported host language features (e.g. preventing + DSL users from using `var` in Swift) is difficult or not possible. + +- Producing good diagnostics may be difficult or impossible. DSLs have limited + access to source location information. When indirections like code + transformations are involved, showing the appropriate source locations in + diagnostic messages may be difficult. Without the aid of compiler utilities, + statically detecting and diagnosing dataflow-based errors is not possible. + +### Source code transformation tools + +Source code transformation tools are another approach to differentiable +programming. Tool users write code, select various differentiation configuration +options (the name of the function-to-differentiate, the independent and +dependent variable, etc), and provide them to the tool. The tool analyzes the +input code and generates output code that computes derivatives according to the +options. + +Historically, this is one of the oldest approaches for automatic +differentiation. Tools like +[Tapenade](https://www-sop.inria.fr/tropics/tapenade.html) and +[ADIC](https://www.mcs.anl.gov/research/projects/adic)/[ADIFOR](https://www.mcs.anl.gov/research/projects/adifor) +compute derivatives of Fortran and C code. + +An advantage of source code transformation tools is that they are essentially +*static compilers*: they can perform static analyses on input code to generate +optimized derivative-computing output code. For example, Tapenade performs +["activity analysis"](https://www-sop.inria.fr/tropics/papers/supportCoursDA.pdf) +to determine variables that do not need a derivative and "TBR (to-be-recorded) +analysis" to remove unnecessary intermediate variables during differentiation. + +However, these tools are not ideal for usability: users must interact with an +external GUI to specify inputs and they receive a textual program as output. +This external workflow is an extra indirection that takes users out of their +natural programming environment. Exposing the tool-provided differentiation +features within a language would be more ergonomic. + +

+ +
+ + Image of Tapenade web interface. +
+ User specifies input program and configuration options. +
+ Tapenade generates derivative-computing output program. +
+

+ +### First-class language support + +Another class of differentiable programming approaches is by integrating the +differentiation semantics and code transformations into a programming language +to some degree. While there are no mainstream programming languages that support +differentiable programming, research systems like +[Stalin∇](http://www-bcl.cs.may.ie/~barak/papers/toplas-reverse.pdf) add +first-class differential operators (e.g. `grad`) into the language and the +reverse-mode automatic differentiation transformation into the compiler. + +First-class language support for differentiation can reap the benefits of source +code transformation techniques (e.g. language coverage, performant derivative +code) without requiring programmers to use an external tool. Well-designed, +powerful differentiation primitives enable users to define their own custom +differentiation APIs that would otherwise not be possible in differentiation +libraries. + +### Why bake differentiation into Swift? + +First-class language support for differentiation will enable convenient, +extensible, and performant differentiable programming in Swift. + +#### Maximal coverage of Swift language features + +First-class support for differentiation in Swift enables differentiation to work +nicely with a maximal number of Swift language features, including mutation and +control flow. Users of differentiable programming do not need to write in a +restricted subset of Swift: just write normal code and use differentiation. + +#### Extensibility + +First-class language support enables an extensible differentiable programming +system. + +Custom types can be extended to be differentiable with minimal boilerplate. +Custom derivative functions can be retroactively registered for existing +functions. Users can define custom differentiation APIs using the powerful +primitive operators defined in the standard library and supported by the type +system. + +#### Static warnings and errors + +Some functions perform non-differentiable operations (on the path from +parameters to result) and thus cannot be differentiated. Functions that do not +use their parameters to compute the result are technically differentiable, but +the derivative is trivially always zero. + +With language support for differentiation, the compiler can identify these cases +statically via data flow analysis and produce a non-differentiability error or +warning. These diagnostics improve productivity and help users catch errors +ahead of time. Library-based differentiation approaches cannot generally provide +these diagnostics. + +For details on static warnings and errors, see the "Static analysis" section in +the detailed design below. + +#### The pursuit for user-defined code transformations + +The key code transformation enabling differentiable programming is "derivative +code generation". Derivative code generation implements automatic +differentiation: given an "original function" to differentiate, a derivative +function is generated by replacing function applications in the original +function with corresponding derivative function applications. The algorithm is +described in detail in the +[Swift Differentiable Programming Implementation Overview document](http://bit.ly/swift-autodiff-internals). + +Some languages provide the ability to define custom code transformations: + +- [Macros](https://en.wikipedia.org/wiki/Macro_\(computer_science\)) enable + syntax-based code transformations at compile-time. Hygienic macros (macro + systems that avoid accidental variable capture) are available in a variety + of languages, including Lisp, Julia, Rust, and Scala, to name a few. As an + example: generated type-safe schema wrappers can implemented using + [hygienic macros in Scala](https://meta.plasm.us/posts/2013/07/11/fake-type-providers-part-2). + +- Compiler plugin systems enable programmers to write plugins that extend the + behavior of a compiler. Compiler plugins are more popular in bootstrapped + languages, like + [Haskell](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/extending_ghc.html#compiler-plugins), + [Rust](https://doc.rust-lang.org/1.1.0/book/compiler-plugins.html) and + [Scala](https://docs.scala-lang.org/overviews/plugins/index.html), where the + plugin can be written in the language itself. As an example: a + continuation-passing-style code transformation can be implemented as a + [compiler plugin in Scala](https://github.com/scala/scala-continuations). + +One might make the case that derivative code generation for differentiation is +better implemented as a custom code transformation. While that may be true in +theory, Swift does not yet support custom code transformations in practice. This +proposal presents differentiable programming as a system of *high-level language +features and semantics*; derivative code generation is an implementation detail. +If a system for custom code transformations is added to Swift one day, it may be +possible to reimplement derivative code generation using that system without +changing the high-level differentiable programming features proposed here. + +## Math introduction + +### What is a derivative? + +The derivative of a function `f` measures how quickly the function's output +changes when you make small changes to the function's input. The value of this +measurement depends on the input `x` that you start with, and we call the value +of the measurement starting at that input "the derivative of `f` at `x`. + +For a single variable real function (a function with a single real input and a +single real output), the derivative of `f` at `x` can be summarized as a single +real number `f'(x)` such that `f(x + ε) ~= f(x) + f'(x) * ε`. In other words, +changing the input by a tiny amount `epsilon` changes the output by `f'(x) * ε`. + +

+ +
+ + f(x) = x changes by exactly ε whenever you change + its input by ε, so its derivative is 1 everywhere. + +

+ +

+ +
+ + Near x = 0, f(x) = x^2 changes very little when you + change its input, so its derivative at x = 0 is 0 + (see orange line). +
+ Near x = 1, f(x) = x^2 changes by approximately + 2*ε when you change its input by ε, so its + derivative at x = 1 is 2 (see green line). +
+ In general, the derivative of f(x) = x^2 at x is + 2*x. +
+

+ +### Iterative optimization + +Iterative optimization algorithms use derivatives to optimize functions (i.e. +find the inputs that minimize or maximize the output of the function). For +example, the simple "gradient descent" algorithm starts with an arbitrary input +`x` and uses the derivative of the function at `x` to determine whether it needs +to increase or decrease `x` to decrease the output of the function. Then it +mutates `x` slightly along the appropriate direction and repeats until the +output stops decreasing. + +

+ +

+ +### Derivatives of functions with arbitrary inputs + +Real world programs deal with data more complicated than single real variables. +Fortunately, there are mathematical theories that extend derivatives to +functions with nearly arbitrary inputs and outputs. + +Recall our original description of derivative: "The derivative of a function `f` +measures how quickly the function's output changes when you make small changes +to the function's input." This makes sense for arbitrary input and output types, +as long as we can describe small changes in them. + +It is easy to describe small changes in nested structures of real numbers: they +are just small changes in all the components' real numbers. For example, +consider: + +```swift +struct Point { + var x, y: Float +} + +struct PointPair { + var p1, p2: Point +} +``` + +A small change in `Point` might be "add `0.01` to `x` and add `0.02` to y". A +small change in `PointPair` might be "add `0.01` to `p1.x` and add `0.01` to +`p2.x`". + +We can define new types that capture the values of these small changes. We call +these types "tangent vectors", a term from math. For example: + +```swift +extension Point { + struct TangentVector { + // `dx` and `dy` are small changes in `x` and `y`, respectively. + var dx, dy: Float + } +} + +extension PointPair { + struct TangentVector { + // `dp1` and `dp2` are small changes in `p1` and `p2`, respectively. + var dp1, dp2: Point.TangentVector + } +} +``` + +In terms of these tangent vectors, the small changes that we described in words +above would be: + +```swift +Point.TangentVector(dx: 0.01, dy: 0.02) + +PointPair.TangentVector( + p1: Point.TangentVector(dx: 0.01, dy: 0), + p2: Point.TangentVector(dx: 0.01, dy: 0)) +``` + +In terms of tangent vectors, the derivative of a function `f: (A) -> B` is a +function `df: (A, A.TangentVector) -> B.TangentVector`. In other words, `df` +takes a starting value of type `A` and a small change `A.TangentVector` and +tells you what the resulting small change in `B` is. + +The gradient descent iterative optimization algorithm can run on any function +`f: (A) -> Float` as long as `A` is a type for which we can define a tangent +vector. It iteratively walks around different values of `A`, searching for a +value that minimizes the output of `f`. + +## Proposed solution + +To push Swift's capabilities to the next level in numerics and machine learning, +we introduce differentiable programming as a new language feature, which +includes standard library APIs and small additive changes to the type system. + +### The `Differentiable` protocol + +`Differentiable` is a standard library protocol that generalizes all data +structures that can be a parameter or result of a differentiable function. The +compiler derives protocol requirement implementations when a `@memberwise` +conformance is declared. + +```swift +extension Float: Differentiable { + typealias TangentVector = Self +} +struct Perceptron: @memberwise Differentiable { + var weight: SIMD64 + var bias: Float +} +``` + +### The `@differentiable` declaration attribute + +The `@differentiable` declaration attribute is an attribute that marks +function-like declarations (function declarations, initializers, properties, and +subscripts) as being differentiable. + +```swift +@differentiable +func cubed(_ x: Float) -> Float { + x * x * x +} +extension Perceptron { + @differentiable + func callAsFunction(_ input: SIMD64) -> Float { + (weight * input).sum() + bias + } +} +``` + +### `@differentiable` function types + +A subtype of normal function types with a different runtime representation, +which stores metadata that allows their values to be differentiated anywhere. + +```swift +func addOne(_ x: Float) -> Float { x + 1 } +let _: @differentiable (Float) -> Float = addOne +let _: @differentiable(linear) (Float) -> Float = addOne +``` + +### `@derivative` and `@transpose` attributes + +`@derivative` and `@transpose` attributes are used for declaring custom +derivative functions for some other function declaration. + +```swift +import Glibc + +@derivative(of: expf) +func _(_ x: Float) -> (value: Float, + differential: @differentiable(linear) (Float) -> Float) { + let y = expf(x) + return (value: y, differential: { v in v * y }) +} +``` + +### Differential operators + +Standard library differentiation APIs that take `@differentiable` functions and +return derivative functions or compute derivative values. + +```swift +// In the standard library: +// +// func derivative( +// of body: @escaping @differentiable (T) -> R +// ) -> (T) -> R where T.TangentVector: FloatingPoint + +@differentiable +func f(_ x: Float) -> Float { + x * x +} +let dfdx = derivative(of: f) +dfdx(3) // 6 +``` + +## Detailed design + +### Differentiable data structures + +Speaking in terms of elementary calculus, only functions are "differentiable": +only functions have derivatives and can be differentiated. In programming +languages, types are isomorphic to mathematical spaces, and functions are +isomorphic to mathematical functions over those spaces. Differentiability +depends heavily on the continuity and smoothness of points in a space (or values +of a type). For example, the `Int` type represents the space of integers, which +are discrete values, so functions over integers cannot be differentiated. In +general, when a type is said to be differentiable, it means that one can do +calculus with its values. As such, real numbers, real vector spaces, and complex +vector spaces are differentiable, but characters, strings, and integers are not. + +For full flexibility and extensibility, a protocol is introduced in the Swift +standard library to generalize all data structures that can be a parameter or a +result of a differentiable function. + +#### The `Differentiable` protocol + +The `Differentiable` protocol defines operations and structures required for a +type to be differentiated. + + +```swift +public protocol Differentiable { + /// A type that can be used to represent derivatives with respect to a + /// value whose type is `Self`. Mathematically, this is equivalent to the + /// tangent bundle of the differentiable manifold represented by the + /// differentiable type. + associatedtype TangentVector: Differentiable & AdditiveArithmetic + where TangentVector == TangentVector.TangentVector + + /// Moves `self` along the given direction. In Riemannian geometry, this is + /// equivalent to exponential map, which moves `self` on the geodesic + /// surface along the given tangent vector. + mutating func move(along direction: TangentVector) + + /// A closure that produces a zero tangent vector and does not capture `self`. + /// + /// In some cases, the zero tangent vector of `self` is equal to + /// `TangentVector.zero`. In other cases, the zero tangent vector depends on + /// information in `self`, such as shape for an n-dimensional array type. + /// For differentiable programming, it is more memory-efficient to define a + /// custom `zeroTangentVectorInitializer` property which returns a closure + /// that captures and uses only the necessary information to create a zero + /// tangent vector. For example: + /// + /// ```swift + /// struct Vector { + /// var scalars: [Float] + /// var count: Int { scalars.count } + /// init(repeating repeatedElement: Float, count: Int) { ... } + /// } + /// + /// extension Vector: Differentiable { + /// typealias TangentVector = Vector + /// + /// @noDerivative + /// var zeroTangentVectorInitializer: () -> TangentVector { + /// let count = self.count + /// return { TangentVector(repeating: 0, count: count) } + /// } + /// } + /// ``` + /// + @noDerivative + var zeroTangentVectorInitializer: () -> TangentVector { get } +} + +extension Differentiable { + /// A tangent vector such that `move(along: zeroTangentVector)` will not modify + /// `self`. + @noDerivative + var zeroTangentVector: TangentVector { zeroTangentVectorInitializer() } +} +``` + +Specifically, `Differentiable` generalizes types to satisfy the following +requirements from real-world use cases: Functions over these types can be +differentiable. Besides types, a function's differentiability also depends on +the function's body. Values of these types can be updated based on derivative +values. For full flexibility, differentiable types should not be required to be +a vector space. For example, a differentiable neural network layer can store a +`Bool` flag in addition to differentiable parameters. + +Intuitively, a `Differentiable`-conforming type allows one to do calculus with +its values. In elementary calculus, a derivative of a real-valued function at a +point is the slope of the tangent line at this point. The tangent line is the +best [linear approximation](https://en.wikipedia.org/wiki/Linear_approximation) +of the differentiated function near that input value. The same definition +applies to vector-valued functions when they are split into their coordinate +functions. The derivative of a vector-valued function at a certain point is +called a [tangent vector](https://en.wikipedia.org/wiki/Tangent_vector). Beyond +real numbers and vector spaces, there is a widely accepted mathematical +framework, +[differential geometry](https://en.wikipedia.org/wiki/Differential_geometry), +which generalizes calculus beyond Euclidean space. By bringing ideas from this +mathematical framework into the Swift standard library and the Swift compiler, +differentiable programming becomes more flexible and expressive than ever. + +

+ +
+ + Image showing two differentiable manifolds: a sphere and a spheroid, from + https://en.wikipedia.org/wiki/Pushforward_(differential). +
+ If a map, φ, carries every point on manifold M to manifold N, then the + pushforward of φ carries vectors in the tangent space at every point in M to + a tangent space at every point in N. +
+

+ +Mathematically speaking, types that conform to `Differentiable` are considered +[smooth Riemannian manifolds](https://en.wikipedia.org/wiki/Riemannian_manifold). +When differentiating a function over these manifolds, a derivative value is a +vector in the [tangent bundle](https://en.wikipedia.org/wiki/Tangent_bundle) of +this manifold and has type `TangentVector`. The associated type `TangentVector` +is required to conform to `AdditiveArithmetic` because +[additive group](https://en.wikipedia.org/wiki/Additive_group) properties +[`zero`](https://developer.apple.com/documentation/swift/additivearithmetic/3126829-zero) +and +[`+(_:_:)`](https://developer.apple.com/documentation/swift/additivearithmetic/3126821) +are necessary for initializing and accumulating derivative values. + +The `move(along:)` method is equivalent to the mathematical notion of +[exponential map](https://en.wikipedia.org/wiki/Exponential_map_\(Riemannian_geometry\)), +which takes a tangent vector (e.g. a derivative), and moves the value along the +direction specified by the tangent vector on the geodesic surface of the +manifold. In vector spaces where the tangent vector is of the same vector space +as the original differentiable space, `move(along:)` is equivalent to vector +addition. Mathematical optimization algorithms such as gradient descent will +make use of this method. + +```swift +public extension Differentiable where Self == TangentVector { + mutating func move(along direction: TangentVector) { + self += direction + } + + @noDerivative + var zeroTangentVectorInitializer: () -> TangentVector { + { .zero } + } +} +``` + +The `zeroTangentVector` property returns a tangent vector such that calling +`move(along:)` on the vector will not modify `self`. A zero tangent vector is +often used in the initialization of mathematical optimization, where tangent +vectors are initially zero and modified iteratively. This property may be +different from `TangentVector.zero` because some tangent vectors depend on +instance properties of `self`, e.g. the `count` property in `Array`. + +#### `Differentiable` conformances + +Conforming a type to `Differentiable` tells Swift that changes in values of this +type can be differentiated, and makes functions over this type be compatible +with all differentiation APIs in the standard library. Floating point numeric +types and vector types, including +[`Float`](https://developer.apple.com/documentation/swift/float), +[`Double`](https://developer.apple.com/documentation/swift/double), +[`Float80`](https://developer.apple.com/documentation/swift/float80), and +[SIMD vector types](https://developer.apple.com/documentation/swift/swift_standard_library/numbers_and_basic_values/simd_vector_types), +are extended to conform to `Differentiable`, and their `TangentVector`s equal +themselves. + +Besides numeric types, collections of numeric types are also powerful data +structures in differentiable programming. For example, the +[`Array`](https://developer.apple.com/documentation/swift/array) type in the +standard library +[conforms to `Differentiable`](https://github.com/apple/swift/blob/c224468653366119690aeb34f290843f3e5f2fd6/stdlib/public/core/Array.swift#L2052) +conditionally when the `Element` type conforms to `Differentiable`. This makes +it possible to differentiate functions over arrays, and makes it easy to express +dynamic differentiable algorithms. Similarly, other common container types in +the standard library such as +[`Optional`](https://developer.apple.com/documentation/swift/optional), +[`Dictionary`](https://developer.apple.com/documentation/swift/dictionary), and +[`Result`](https://developer.apple.com/documentation/swift/result) can also be +made differentiable via a conditional protocol conformance. + +```swift +// struct Array +extension Array: Differentiable where Element: Differentiable { + // Note: `Array.TangentVector` cannot be `Array` because `Array.+` is used for + // concatenation and therefore cannot satisfy the `AdditiveArithmetic` + // conformance constraint. + public struct TangentVector: Differentiable, AdditiveArithmetic { + public typealias TangentVector = Self + @differentiable + public var elements: [Element.TangentVector] + @differentiable + public init(_ elements: [Element.TangentVector]) { self.elements = elements } + ... + } + + public mutating func move(along direction: TangentVector) { + for i in indices { + self[i].move(along: Element.TangentVector(direction.elements[i])) + } + } + + @noDerivative + public var zeroTangentVectorInitializer: () -> TangentVector { + { [count = self.count] in + TangentVector(Array(repeating: .zero, count: count)) + } + } +} + +// struct Dictionary +extension Dictionary: Differentiable where Value: Differentiable { + public struct TangentVector: Differentiable, AdditiveArithmetic { + public typealias TangentVector = Self + @differentiable + public var elements: [Key: Value.TangentVector] + @differentiable + public init(_ elements: [Key: Value.TangentVector]) { + self.elements = elements + } + ... + } + + public mutating func move(along direction: TangentVector) { + for i in indices { + self[i].move(along: Value.TangentVector(direction.elements[i])) + } + } + + @noDerivative + public var zeroTangentVectorInitializer: () -> TangentVector { + { [keys = self.keys] in + let pairs = zip(keys, sequence(first: .zero, next: {$0})) + return TangentVector(Dictionary(uniqueKeysWithValues: pairs)) + } + } +} + +// enum Optional +extension Optional: Differentiable where Wrapped: Differentiable { + public struct TangentVector: Differentiable, AdditiveArithmetic { + public typealias TangentVector = Self + @differentiable + public var value: Wrapped.TangentVector? + @differentiable + public init(_ value: Wrapped.TangentVector?) { self.value = value } + ... + } + + public mutating func move(along direction: TangentVector) { + if let value = direction.value { + self?.move(along: value) + } + } + + @noDerivative + public var zeroTangentVectorInitializer: () -> TangentVector { + { TangentVector(.zero) } + } +} +``` + +#### Compiler-synthesized conformances + +In numerics and machine learning, high-level data structures such as neural +network layers and models are formed from smaller components stored as +properties in structure types and class types. In order to use these types for +differentiation, one must extend these types to conform to the `Differentiable` +protocol. Luckily, this need not be done manually in most cases—the compiler +automatically synthesizes conformances when a memberwise `Differentiable` +conformance is declared. + +##### Synthesis conditions + +The compiler automatically synthesizes implementations of `Differentiable` +protocol requirements for struct and class types. Here are the conditions for +synthesis: The type must declare a conformance to `Differentiable` with a +`@memberwise` attribute before the protocol name, either on the type declaration +or on an extension in the same file. All stored properties of the conforming +type must either be a `var` that conforms to `Differentiable` or be marked with +the `@noDerivative` attribute. If a non-`Differentiable` or a `let` stored +property is not marked with `@noDerivative`, then it is treated as if it has +`@noDerivative` and the compiler emits a warning (with a fix-it in IDEs) asking +the user to make the attribute explicit. + +##### Default synthesis + +By default, the compiler synthesizes a nested `TangentVector` structure type +that contains the `TangentVector`s of all stored properties that are not marked +with `@noDerivative`. In other words, `@noDerivative` makes a stored property +not be included in a type's tangent vectors. + +The synthesized `TangentVector` has the same effective access level as the +original type declaration. Properties in the synthesized `TangentVector` have +the same effective access level as their corresponding original properties. + +A `move(along:)` method is synthesized with a body that calls `move(along:)` for +each pair of the original property and its corresponding property in +`TangentVector`. Similarly, `zeroTangentVector` is synthesized to return a +tangent vector that consists of each stored property's `zeroTangentVector`. +Here's an example: + +```swift +struct Foo: @memberwise Differentiable { + // `x` and `y` are the "differentiation properties". + var x: T + var y: U + @noDerivative var customFlag: Bool + @noDerivative let helperVariable: T + + // The compiler synthesizes: + // struct TangentVector: Differentiable, AdditiveArithmetic { + // var x: T.TangentVector + // var y: U.TangentVector + // } + // mutating func move(along direction: TangentVector) { + // x.move(along: direction.x) + // y.move(along: direction.y) + // } + // @noDerivative + // var zeroTangentVectorInitializer: () -> TangentVector { + // { [xTanInit = x.zeroTangentVectorInitializer, + // yTanInit = y.zeroTangentVectorInitializer] in + // TangentVector(x: xTanInit(), y: yTanInit()) + // } + // } +} +``` + +##### Shortcut synthesis + +In certain cases, it is not ideal to keep `Self` and `TangentVector` as separate +types. A most obvious example of this is when all of the following conditions +are met: `Self` is declared to conform to `AdditiveArithmetic`. All stored +properties are declared to conform to `AdditiveArithmetic`. There are no +`@noDerivative` stored properties. + +In these cases, the compiler will make `TangentVector` be a type alias for Self. +Method `move(along:)` and property `zeroTangentVector` will not be synthesized +because a default implementation already exists. + +```swift +struct Point: @memberwise Differentiable, @memberwise AdditiveArithmetic { + // `x` and `y` are the "differentiation properties". + var x, y: T + + // The compiler synthesizes: + // typealias TangentVector = Self +} +``` + +### Differentiable function declarations + +At the heart of a differentiable programming language is the ability to express +differentiable functions, from abstract manifold operations all the way down to +floating point addition. Because differentiable programming is a flexible and +extensible language feature in Swift, the compiler is agnostic of actual +mathematical operations—it does not have special knowledge of standard library +operators such as +[Float.+(_:_:)](https://developer.apple.com/documentation/swift/float/2894068), +nor does it distinguish between primitive operations and normal functions. A +function can be differentiated with respect to certain Differentiable-conforming +parameters if it satisfies one of the following requirements: + +- Base case 1: It is linear with respect to those parameters. + +- Base case 2: A derivative function for it with respect to those parameters + exists in code. + +- Recursive case: All function calls, initializer calls, subscript accesses, + property accesses, variable assignments along the path from those parameters + to the result can be differentiated. + +#### The `@differentiable` declaration attribute + +The `@differentiable` declaration attribute can be used to mark function +declarations, initializers, properties, and subscripts as being differentiable. +When one of these entities is marked with `@differentiable`, the compiler +attempts to differentiate it with respect to all parameters (including any +implicit `self` parameter) that conform to the `Differentiable` protocol. One +can specify explicit parameters via a `wrt:` clause, e.g. `@differentiable(wrt: +x)` and `@differentiable(wrt: (self, x))`. In generic algorithms, one can also +provide a `where`-clause to specify generic constraints for parameters or the +result to make the function differentiable only when the generic constraints are +satisfied, e.g. `@differentiable(wrt: x where Scalar: FloatingPoint)`. + +```swift +@differentiable // differentiable with respect to 'x' +func silly(_ x: Float, _ n: Int) -> Float { + print("Running 'silly' on \(x) and \(n)!") + return sin(cos(x)) +} +``` + +Computed property getters behave like methods in that they accept exactly one +argument, `self`. If a computed property is marked with `@differentiable`, the +compiler attempts to differentiate its getter with respect to `self`. +`@differentiable` can also be applied to an explicit getter declaration. + +```swift +extension Float { + @differentiable + var reciprocal: Float { + 1 / self + } +} +``` + +Among these language constructs, stored properties are the least method-like in +that they are stored values and cannot have a user-defined getter. However, +access to stored properties can be considered as a projection of `self`. +Therefore, stored properties can be marked `@differentiable` and be +differentiated as a function as well. However, an explicit `@differentiable` is +only necessary for public properties in public structs or classes to support +library evolution, and are implicitly synthesized by the compiler when the +parent type's `Differentiable` conformance is synthesized by the compiler (not +user-defined). + +```swift +public struct Vector: @memberwise Differentiable { + @differentiable // Okay, though the compiler has synthesized it. + public var x, y: Float +} +``` + +#### Conformance and subclassing + +Protocol requirements and class members can be made differentiable with a +`@differentiable` attribute. Semantically, this means that this member is +guaranteed to be differentiable, and that any conformance implementation or +inheritance must maintain the differentiability. + +##### Protocol dispatch + +The `@differentiable` attribute can be used on protocol requirements. A +`@differentiable` protocol requirement requires that all conforming types +implement this requirement with a differentiable body with respect to the +specified parameters. Conforming implementations are not required to be marked +with `@differentiable` attribute unless they are `public`. + +```swift +public protocol Layer: Differentiable { + associatedtype Input: Differentiable + associatedtype Output: Differentiable + @differentiable // w.r.t. `input` and `self` + func callAsFunction(_: Input) -> Output +} +struct Perceptron: @memberwise Differentiable, Layer { + var weight: SIMD4 + var bias: Float + + func callAsFunction(_ input: SIMD4) -> Float { + (weight * input).sum() + b + } +} +``` + +In a protocol hierarchy, one can override a differentiable protocol requirement +with a `@differentiable` attribute that declares differentiability with respect +to more parameters. + +```swift +public protocol Module: Differentiable { + associatedtype Input + associatedtype Output: Differentiable + @differentiable(wrt: self) + func callAsFunction(_: Input) -> Output +} + +public protocol Layer: Module where Input: Differentiable { + @differentiable(wrt: (self, input)) + func callAsFunction(_: Input) -> Output +} +``` + +In the example above, types that are declared to conform to `Layer` (the +protocol with a refined `callAsFunction(_:)` method) can omit the +`@differentiable(wrt: self)` attribute on the method implementation and use +`@differentiable(wrt: (self, input))` (or just `@differentiable`) only. + +`Differentiable` protocol requirements are not allowed to use a `where`-clause +in the `@differentiable` attribute. This is to simplify the programming model +where protocol requirement overrides are more powerful. + +##### Class dispatch + +A differentiable non-final class method, property or subscript can be overridden +by a subclass implementation. The overriding implementation must be +`@differentiable` if the original overridden declaration is marked with +`@differentiable`. When a method/subscript call or a property access that is +dynamically dispatched is being differentiated, the derivative of the subclass +implementation will be used. + +```swift +class Superclass { + @differentiable + func foo(_ x: SIMD8) -> Float { + x.sum() + } +} + +class Subclass: Superclass { + @differentiable + override func foo(_ x: SIMD8) -> Float { + (x * x).sum() + } +} +``` + +### Make a function differentiable using `@derivative` or `@transpose` + +Any function that has `Differentiable`-conforming parameters and result can be +made differentiable by extending the function to have either an associated +derivative function or a linear transpose. In other words, derivative functions +and transpose functions provide differentiability for other functions. + +The `@derivative` attribute is used for marking a function as producing a custom +derivative for another function, hence making the other function differentiable. +The `@transpose` attribute is used for marking a function as transposing another +function, hence making the other function linear. + +A protocol requirement or class method/property/subscript can be made +differentiable via a derivative function or transpose function defined in an +extension. When a protocol requirement is not marked with `@differentiable` but +has been made differentiable by a `@derivative` or `@transpose` declaration in a +protocol extension, a dispatched call to such a member can be differentiated, +and the derivative or transpose is always the one provided in the protocol +extension. + +#### Linear maps + +[Linear maps](https://en.wikipedia.org/wiki/Linear_map) are a fundamental +concept in differentiation. Differentiating a function between two +differentiable manifolds at a certain point produces a linear map between the +[tangent space](https://en.wikipedia.org/wiki/Tangent_space) at that point in +the input manifold and the tangent space at the corresponding point at the +output manifold. This linear map is called a +[differential (or pushforward)](https://en.wikipedia.org/wiki/Pushforward_\(differential\)), +which applies the chain rule to compute directional derivatives. Gradients, on +the other hand, are computed by a linear map called +[pullback](https://en.wikipedia.org/wiki/Pullback_\(differential_geometry\)), +which is the transpose of a differential, where transposition can be thought of +as transposing the matrix representing the linear map. It is important that +functions that are used for chaining derivatives are implemented as linear maps +provided with a transpose (e.g. scalar multiplication, matrix transposition, and +matrix multiplication), because gradients can only be computed when the +differential can be transposed. + +To make an original function be linear, define a transpose function with a +`@transpose` attribute that specifies the original function. + +##### Typing rules + +A function declaration does not have a fixed transpose type. This is because +there can be multiple transpose functions that transpose the original function +differently, e.g. with respect to different parameters, transposing under +different generic constraints, etc. + +Given an original function declaration, a transpose function's type is +determined from the following configurations: +- Parameters to transpose with respect to. +- Additional generic constraints that make the original function linear. + +The type of the transpose function under such configurations is a +function that takes one argument whose type is the original function's result +type and returns results that correspond to each original function parameter +that is transposed with respect to. This definition, however, is a rough +definition because there are differences among top-level functions, instance +methods, and static methods. + +###### Linearity parameters + +Linearity parameters are parameters with respect to which a function is linear. +The `@transpose` attribute accepts a `wrt:` argument which specifies a set of +linearity parameters of the original function. If `wrt:` is not specified, +linearity parameters default to all parameters. A `wrt:` argument in +`@derivative` attributes can be a parameter index, a `self`, or a tuple of +parameter indices and `self`. When there are more than one linearity parameters +specified, parameter indices must be ascending, and `self` must be the first +parameter when exists. All linearity parameters must have a type that conforms +to both `Differentiable` and `AdditiveArithmetic` and satisfies `Self == +Self.TangentVector`. + +When linearity parameters do not include all of the original function's +parameters, those parameters must be taken in the front of the parameter list of +the transpose function. + +The argument labels of original non-linearity parameters must be preserved in +the transpose function. Other argument labels can be named freely. When there +are multiple linearity parameters, it is useful to label the elements in the +result tuple to distinguish between transposes with respect to different +parameters. + +###### Top-level functions + +_Note: Since both transpose functions and derivative functions are difficult to +name and need not be referenced directly, we make these functions unnamed (with +base name being an underscore). This is not yet valid in the official Swift +language, but the developers of the differentiable programming feature will +prototype and pitch this change through Swift Evolution._ + +```swift +func foo(_ x: T, _ y: T, _ z: T) -> T + where T == T.TangentVector { ... } + +// Transpose with respect to all parameters, making `foo(_:_:_:)` linear with +// with respect to all parameters. +@transpose(of: foo) +func _(_ v: T) -> (x: T, y: T, z: T) + where T == T.TangentVector { ... } + +// Transpose with respect to original parameter `x`, making `foo(_:_:_:)` +// linear with respect to `x`. +@transpose(of: foo, wrt: 0) +func _(y: T, z: T, v: T) -> T + where T == T.TangentVector { ... } + +// Transpose with respect to original parameters `x` and `z`, making +// `foo(_:_:_:)` linear with respect to `x` and `z`. +@transpose(of: foo, wrt: (0, 2)) +func _(y: T, v: T) -> (x: T, z: T) + where T == T.TangentVector { ... } +``` + +###### Static methods + +A transpose of a static method is exactly like top-level functions except that +it must also be defined as a static method in the same type. The implicit `self` +parameter cannot be a linearity parameter, because metatypes cannot conform to +`Differentiable & AdditiveArithmetic`. + +```swift +extension MyType { + static func foo(_ x: T, _ y: T, _ z: T) -> T + where T == T.TangentVector { ... } +} + +extension MyType { + // Transpose with respect to all parameters, making `foo(_:_:_:)` linear with + // with respect to all parameters. + @transpose(of: foo) + static func _(_ v: T) -> (x: T, y: T, z: T) + where T == T.TangentVector { ... } + + // Transpose with respect to original parameter `x`, making `foo(_:_:_:)` + // linear with respect to `x`. + @transpose(of: foo, wrt: 0) + static func _(y: T, z: T, v: T) -> T + where T == T.TangentVector { ... } + + // Transpose with respect to original parameters `x` and `z`, making + // `foo(_:_:_:)` linear with respect to `x` and `z`. + @transpose(of: foo, wrt: (0, 2)) + static func _(y: T, v: T) -> (x: T, z: T) + where T == T.TangentVector { ... } +} +``` + +The numeric addition operator +[`AdditiveArithmetic.+(_:_:)`](https://developer.apple.com/documentation/swift/additivearithmetic/3126821) +is linear, and the multiplication operator +[`Numeric.*(_:_:)`](https://developer.apple.com/documentation/swift/numeric/2883821) +is [bilinear](https://en.wikipedia.org/wiki/Bilinear_map) (i.e. linear with +respect to each parameter). Here's how they are made differentiable in the +standard library. + +```swift +extension FloatingPoint + where Self: Differentiable & AdditiveArithmetic, Self == TangentVector +{ + @transpose(of: +) + static func _(_ v: Self) -> (Self, Self) { (v, v) } + + @transpose(of: *, wrt: 0) + @transpose(of: *, wrt: 1) + static func _(lhs: Self, rhs: Self) -> Self { lhs * rhs } +} +``` + +As shown, transpose functions may be defined in a type extension or a protocol +extension that has more generic constraints than the original `+(_:_:)` and +`*(_:_:)` declarations. This makes the original functions linear only when these +extra generic constraints are satisfied. Moreover, transpose functions for +`*(_:_:)` are defined per-parameter due to the nature of bilinearity (`x + y` is +a flat plane while `x * y` is not), but fortunately its transpose functions with +respect to each parameter are just `*(_:_:)` itself. + +In vector calculus, transpose functions become less trivial. For example, here +is a hypothetical `Tensor` type, which has two transpose functions defined for +`Tensor.transposed()`, the tensor transposition method, and `matmul(_:_:)`, the +matrix multiplication function. + +```swift +extension Tensor where Scalar: FloatingPoint & Differentiable { + @transpose(of: transposed, wrt: self) + func _() -> Tensor { + self.transposed() + } +} + +@transpose(of: matmul(_:_:), wrt: 0) +func _(y: Tensor, v: Tensor) -> Tensor { + matmul(v, y.transposed()) +} + +@transpose(of: matmul(_:_:), wrt: 1) +func _(x: Tensor, v: Tensor) -> Tensor { + matmul(x.transposed(), v) +} +``` + +###### Instance methods + +A transpose of a static method is exactly like top-level functions except: +- When linearity parameters does not include `self`, it must be defined as an + instance method in the same type. +- When linearity parameters include `self`, it must be defined as a static + method in the same type. + +```swift +extension MyType { + func foo(_ x: T, _ y: T, _ z: T) -> T + where T == T.TangentVector { ... } +} + +extension MyType { + // Transpose with respect to all parameters, making `foo(_:_:_:)` linear with + // with respect to all parameters. + @transpose(of: foo) + func _(_ v: T) -> (x: T, y: T, z: T) + where T == T.TangentVector { ... } + + // Transpose with respect to original parameter `x`, making `foo(_:_:_:)` + // linear with respect to `x`. + @transpose(of: foo, wrt: 0) + func _(y: T, z: T, v: T) -> T + where T == T.TangentVector { ... } + + // Transpose with respect to original parameters `x` and `z`, making + // `foo(_:_:_:)` linear with respect to `x` and `z`. + @transpose(of: foo, wrt: (0, 2)) + func _(y: T, v: T) -> (x: T, z: T) + where T == T.TangentVector { ... } + + // Transpose with respect to original parameters `self`, making `foo(_:_:_:)` + // linear with respect to `self`. + @transpose(of: foo, wrt: self) + static func _(x: T, y: T, z: T, v: T) -> MyType + where T == T.TangentVector { ... } + + // Transpose with respect to original parameters `self`, `x` and `z`, making + // `foo(_:_:_:)` linear with respect to `self`, `x` and `z`. + @transpose(of: foo, wrt: (self, 0, 2)) + static func _(y: T, v: T) -> (self: MyType, x: T, z: T) + where T == T.TangentVector { ... } +} +``` + +###### Linearity generic requirements + +A transpose function can have additional generic constraints, called _linearity +generic requirements_. Linearity generic requirements usually serve the purpose +of making generic parameter types conform to `Differentiable & +AdditiveArithmetic`. + +Linearity generic requirements are functionally equivalent to the `where` clause +in `@differentiable` attributes. + +```swift +func foo(_ x: T, _ y: U, _ z: V) -> W { ... } + +// Transpose with respect to `x` and `z`, requiring that `T` and `V` to conform +// to `Differentiable & AdditiveArithmetic` and equal their corresponding +`TangentVector` types. +@transpose(of: foo, wrt: (x, z)) +func _< + T: Differentiable & AdditiveArithmetic, + U, + V: Differentiable & AdditiveArithmetic +>(_ y: U, _ v: W) -> (x: T, z: V) + where T.TangentVector == T, V.TangentVector == V { ... } +``` + +##### Examples + +Many floating-point operations are linear. Addition and subtraction are linear. +Multiplication is bilinear (linear with respect to each argument). + +```swift +extension FloatingPoint where Self: Differentiable, Self == TangentVector { + @inlinable + @transpose(of: +) + func _(_ v: Self) -> (Self, Self) { + (v, v) + } + + @inlinable + @transpose(of: -) + func _(_ v: Self) -> (Self, Self) { + (v, -v) + } + + @inlinable + @transpose(of: *, wrt: 0) + @transpose(of: *, wrt: 1) + func _(_ x: Self, _ v: Self) -> Self { + return x * v + } +} +``` + +Complex differentiation is representable in our system. Complex numbers behave +differently from real numbers and vectors +([forum discussion](https://forums.swift.org/t/vectornumeric-type-in-general-complex-numbers/24875/3): +they have an additional `conjugate` operation which flips the sign of the +imaginary component. + +Since complex numbers are not yet defined in the standard library, we extended +the complex number type defined in the +[NumericAnnex](https://github.com/xwu/NumericAnnex) library to be +differentiable. +[The full implementation is here](https://github.com/tensorflow/swift-apis/blob/master/Sources/third_party/Experimental/Complex.swift). +The implementation adopts the +[Autograd convention](https://github.com/HIPS/autograd/blob/master/docs/tutorial.md#complex-numbers) +for derivatives of functions with complex arguments or results, so that we can +define derivatives for non-holomorphic primitives. + +```swift +struct Complex: Numeric { + var real: Base + var imaginary: Base + + @differentiable(linear where Base: Differentiable, Base == Base.TangentVector) + init(real: Base = 0, imaginary: Base = 0) { + self.real = real + self.imaginary = imaginary + } + + ... +} + +extension Complex: @memberwise Differentiable where Base: Differentiable, Base == Base.TangentVector {} + +extension Complex { + @differentiable(where Base: Differentiable, Base == Base.TangentVector) + func complexConjugate() -> Complex { + Complex(real: real, imaginary: -imaginary) + } +} +``` + +SIMD vectors are also differentiable: mathematically, they represent a vector +space. Most SIMD operations are defined as `SIMD` protocol requirements, so +derivatives of these operations can be defined generally in a protocol extension +on `SIMD`. + +```swift +extension SIMD where Self: Differentiable, TangentVector: SIMD, Scalar: BinaryFloatingPoint, Self == Self.TangentVector { + @transpose(of: *, wrt: 0) + @transpose(of: *, wrt: 1) + static func _(v: Self, x: Self) -> Self { + v * x + } +} +``` + +Additionally, concrete types conforming to `SIMD` are extended to conditionally +conform to `Differentiable` and `AdditiveArithmetic`. For `SIMD` conforming +types, the `TangentVector` associated type is equal to `Self`. + +```swift +extension SIMD${n}: AdditiveArithmetic where Scalar: BinaryFloatingPoint {} + +extension SIMD${n}: Differentiable +where Scalar: Differentiable & BinaryFloatingPoint, + Scalar.TangentVector : BinaryFloatingPoint { + public typealias TangentVector = SIMD${n} +} + +// `subscript` is defined on `SIMD`-conforming types, so the transpose is as well. +extension SIMDScalar where Self: Differentiable & BinaryFloatingPoint { + @transpose(of: subscript) + func _(index: Int) -> SIMD${n} { + var result = SIMD${n}.zero + result[index] = self + return result + } +} +``` + +The full implementation is in +[`SIMDVector.swift`](https://github.com/apple/swift/blob/tensorflow/stdlib/public/core/SIMDVector.swift) +and +[`SIMDVectorTypes.swift.gyb`](https://github.com/apple/swift/blob/tensorflow/stdlib/public/core/SIMDVectorTypes.swift.gyb) +on the `tensorflow` branch. + +#### Derivative functions + +A derivative function has the same parameters as the original function, but +returns a linear +[differential](https://en.wikipedia.org/wiki/Pushforward_\(differential\)) +function in addition to the original value. Computing both the original value +and the differential is the most efficient way for the differential closure to +capture anything it needs from the original computation, and is important for +flexibility and performance. + +In the following example, the 32-bit floating point exponential function +[`expf(_:)`](https://en.cppreference.com/w/c/numeric/math/exp) is imported from +the C standard library. The derivative function marked with `@derivative` +makes `expf(_:)` a differentiable function. + +```swift +import Glibc + +@derivative(of: expf) +func _(_ x: Float) -> (value: Float, + differential: @differentiable(linear) (Float) -> Float) { + let y = expf(x) + return (value: y, differential: { v in v * y }) +} +``` + +##### Typing rules + +A function declaration does not have a fixed derivative type. This is because +there can be multiple derivative functions that differentiate the original +function differently, e.g. differentiating with respect to different parameters, +differentiating with different generic constraints, etc. + +Given an original function declaration, a derivative function's type is +determined from the following configurations: +- Parameters to differentiate with respect to, aka. differentiability + parameters. +- Additional generic constraints that make the original function differentiable. + +The type of the derivative function under such configurations is a function that +takes the original function's parameters and returns a tuple of an original +result (labeled `value`) and a differential (labeled `differential`). The +differential is a linear map (`@differentiable(linear)`) function that takes the +`TangentVector` nested types of all of the types of the original function's +parameters to differentiate with respect to, and returns the `TangentVector` +nested type of the orgiinal function's result type. + +###### Differentiability parameters + +The `@derivative` attribute accepts a `wrt:` argument which specifies the +differentiability parameters. If `wrt:` is not specified, the derivative +function should be differentiating the original function with respect to all of +its parameters, hence producing a differential that takes all of the original +function's parameter types' `TangentVector` types. A `wrt:` argument in +`@derivative` attributes can be a parameter name, a parameter index, or a tuple +of multiple parameter names or indices. All differentiability parameters must +have a type that conforms to `Differentiable`. + +A derivative function's argument labels must match those of the original +function. Its parameter names do not have to match those of the original +function. However, a `wrt:` argument in a `@derivative` attribute, when +referring to parameters by names, must use parameter names in the derivative +function. + +```swift +func foo(_ x: T, _ y: T, _ z: T) -> T { ... } + +// Derivative with respect to all parameters. +@derivative(of: foo) +func _(_ x: T, _ y: T, _ z: T) -> ( + value: T, + differential: @differentiable(linear) (T.TangentVector, T.TangentVector, T.TangentVector) -> T.TangentVector +) { + ... +} + +// Derivative with respect to `x`. +@derivative(of: foo, wrt: x) +func _(_ x: T, _ y: T, _ z: T) -> ( + value: T, + differential: @differentiable(linear) (T.TangentVector) -> T.TangentVector +) { + ... +} + +// Derivative with respect to `x` and `z`. +@derivative(of: foo, wrt: (x, z)) +func _(_ x: T, _ y: T, _ z: T) -> ( + value: T, + differential: @differentiable(linear) (T.TangentVector, T.TangentVector) -> T.TangentVector +) { + ... +} +``` + +One concrete example is `sinf(_:)` from the C standard library. It can be made +differentiable by defining a derivative retroactively. + +```swift +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +import func Darwin.sinf +#else +import func Glibc.sinf +#endif + +// Imported: +// public func sinf(Float) -> Float + +@derivative(of: sinf) +public func _(_ x: Float) -> ( + value: Float, + differential: @differentiable(linear) (Float) -> Float +) { + (value: sinf(x), differential: { v in cosf(x) * v }) +} +``` + +###### Differentiability generic requirements + +A derivative function can have additional generic constraints, called +_differentiability generic requirements_. Differentiability generic requirements +usually serve the purpose of making generic parameter types conform to +`Differentiable`. + +Differentiability generic requirements are functionally equivalent to the +`where` clause in `@differentiable` attributes. + +```swift +func foo(_ x: T, _ y: U, _ z: V) -> W { ... } + +// Derivative with respect to `x` and `z`, requiring that `T` and `V` to conform +// to `Differentiable`. +@derivative(of: foo, wrt: (x, z)) +func foo( + _ x: T, _ y: U, _ z: V +) -> ( + value: W, + differential: (T.TangentVector, V.TangentVector) -> W.TangentVector +) { + ... +} +``` + +##### Examples + +The `ElementaryFunctions` protocol introduced in +[SE-0246](https://github.com/apple/swift-evolution/blob/master/proposals/0246-mathable.md) +defines generic elementary functions, which are non-linear. By defining +derivatives using the `@derivative` attribute for these protocol +requirements in an extension, all conforming types now have differentiable +elementary functions. + +```swift +public protocol ElementaryFunctions { + static func sqrt(_ x: Self) -> Self + static func cos(_ x: Self) -> Self + static func asinh(_ x: Self) -> Self + static func exp(_ x: Self) -> Self + static func exp10(_ x: Self) -> Self + static func log(_ x: Self) -> Self + static func pow(_ x: Self, _ y: Self) -> Self + ... +} + +public extension ElementaryFunctions +where Self: Differentiable & FloatingPoint, Self == Self.TangentVector { + @inlinable + @derivative(of: sqrt) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + (sqrt(x), { dx in (1 / 2) * (1 / sqrt(x)) * dx }) + } + + @inlinable + @derivative(of: cos) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + (cos(x), { dx in -sin(x) * dx }) + } + + @inlinable + @derivative(of: asinh) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + (asinh(x), { dx in 1 / (1 + x * x) * dx }) + } + + @inlinable + @derivative(of: exp) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + let ret = exp(x) + return (ret, { dx in ret * dx }) + } + + @inlinable + @derivative(of: exp10) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + let ret = exp10(x) + return (ret, { dx in exp(10) * ret * dx }) + } + + @inlinable + @derivative(of: log) + static func _(_ x: Self) -> (value: Self, differential: @differentiable(linear) (Self) -> Self) { + (log(x), { dx in 1 / x * dx }) + } + + @inlinable + @derivative(of: pow) + static func _(_ x: Self, _ y: Self) -> (value: Self, differential: @differentiable(linear) (Self, Self) -> Self) { + (pow(x, y), { (dx, dy) in + let l = y * pow(x, y-1) * dx + let r = pow(x, y) * log(x) * dy + return l + r + }) + } + + ... +} +``` + +#### Default derivatives and transposes + +In a protocol extension, class definition, or class extension, providing a +derivative or transpose for a protocol extension or a non-final class member is +considered as providing a default derivative/transpose for that member. Types +that conform to the protocol or inherit from the class can inherit the default +derivative/transpose. + +If the original member does not have a `@differentiable` attribute, a default +derivative/transpose is implicitly added to all conforming/overriding +implementations. + +```swift +protocol P { + func foo(_ x: Float) -> Float +} + +extension P { + @derivative(of: foo(x:)) + func _(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + (value: foo(x), differential: { _ in 42 }) + } +} + +struct S: P { + func foo(_ x: Float) -> Float { + 33 + } +} + +let s = S() +let d = derivative(at: 0) { x in + s.foo(x) +} // ==> 42 +``` + +When a protocol requirement or class member is marked with `@differentiable`, it +is considered as a _differentiability customization point_. This means that all +conforming/overriding implementation must provide a corresponding +`@differentiable` attribute, which causes the implementation to be +differentiated. To inherit the default derivative/transpose without +differentiating the implementation, add `default` to the `@differentiable` +attribute. + +```swift +protocol P { + @differentiable + func foo(_ x: Float) -> Float +} + +extension P { + @derivative(of: foo(x:)) + func _(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + (value: foo(x), differential: { _ in 42 }) + } +} + +struct S: P { + @differentiable(default) // Inherits default derivative for `P.foo(_:)`. + func foo(_ x: Float) -> Float { + 33 + } +} + +let s = S() +let d = derivative(at: 0) { x in + s.foo(x) +} // ==> 42 +``` + +#### Access control + +Derivative and transpose functions provide differentiability for other +functions, and the access level of the differentiability can be controlled +precisely with access modifiers on derivative/transpose functions. + +When a function's differentiability is provided by a derivative/transpose +function, the access scope of differentiability is identical to the +derivative/transpose function's access scope. For example, a `fileprivate` +derivative function in `B.swift` only overrides the original function's +derivative in `B.swift`. + +```swift +// File A.swift: +internal func foo(_ x: Float) -> Float { + x * x +} +let dfdx_A = derivative(at: 3, in: foo) +// dfdx_A ==> 6 + +// File B.swift: +@derivative(of: foo) +fileprivate func _(_ x: Float) -> ( + value: Float, + differential: @differentiable(linear) (Float) -> Float +) { + (value: foo(x), differential: { _ in 42 }) +} +let dfdx_B = derivative(at: 3, in: foo) +// dfdx_B ==> 42 + +// File C.swift: +let dfdx_C = derivative(at: 3, in: foo) +// dfdx_C ==> 6 +``` + +### Differentiable function types + +Differentiability is a fundamental mathematical concept that applies not only to +declarations of functions, initializers, subscripts, and properties, but also to +function types. In Swift, functions are first-class values of function types +that can be passed around, applied, or converted. Because an important part of +differentiable programming is to be able to define +[differential operators](https://en.wikipedia.org/wiki/Differential_operator) +and custom algorithms on differentiable functions, Swift's type system has been +extended to be able to express differentiable functions as first-class values. + +A differentiable function type is a special function type that has a different +runtime representation than a normal function type, and is a subtype of a +non-differentiable function type with the same parameter types and result type. + +#### Function subtyping and runtime representation + +Subtyping of function types already exists in Swift and is primarily used for +representing different foreign calling conventions for language +interoperability. Function types and function pointer types in C, e.g. +`int(*)(int)`, are imported to Swift as function types with a `@convention(c)` +attribute, e.g. `@convention(c) (Int) -> Int`, with all parameter types and +return types converted to the corresponding Swift ones. + +These function types are also subtypes of a function type with the same +parameter types and result types but without the `@convention(c)` attribute. For +example, you can implicitly convert a `@convention(c)` function value to a Swift +function value and use it directly as an argument to higher-order functions such +as +[`map(_:)`](https://developer.apple.com/documentation/swift/array/3017522-map). + +```swift +// In a C file: +int addOne(int x) { return x + 1; } +int (*addOneFunctionPointer)(int) = addOne; +// Swift equivalent: +// let addOneFunctionPointer: (Int) -> Int = addOne + +// In a Swift file that imports the C file: +// Global variable `addOneFunctionPointer` imported as `@convention(c) (Int) -> Int`. +[1, 2, 3].map(addOneFunctionPointer) // [2, 3, 4] +``` + +One of the main differences between a Swift function value and a C function +value is their runtime representation. A C function cannot capture values from +the context where it is defined, so the runtime representation of a C function +value is just a pointer to the function in memory. A Swift function, however, +can capture values from the context, and thus contains a pointer to the +heap-allocated (or sometimes stack-allocated) context storing captured values. + +

+ +

+ +In differentiable programming, differentiable function types contain more +information than its non-differentiable counterparts. A differentiable function +contains the original function pointer so that it can be efficiently converted +to or called like the original function type. It also contains a derivative +function that will be called when this function is being differentiated. All of +these functions share the same context. A linear map, which is differentiable by +definition and whose differential at any point is itself, does not need to store +derivative functions but just a linear transpose function instead. + +

+ +

+ +#### The `@differentiable` function type attribute + +A `@differentiable` attribute on a function type specifies the function's +differentiability, just like `@differentiable` on function declarations. A +`@differentiable(linear)` attribute specifies the function's linearity with +respect to differentiation. All linear maps are infinitely differentiable, +therefore `@differentiable(linear)` is a subtype of `@differentiable`. + +`@differentiable` requires the enclosing function type to have differentiable +parameters and results. Each parameter and result must conform to the +`Differentiable` protocol unless marked `@noDerivative`. +`@differentiable(linear)` requires the closing function to have "differentiable +vector space" parameters and results, that is, each parameter and result, unless +marked `@noDerivative`, must conform to `Differentiable & AdditiveArithmetic` +and satisfy `Self == Self.TangentVector`. + +

+ +

+ +#### Type conversion + +The subtyping relation among `@differentiable(linear)`, `@differentiable`, and +non-`@differentiable` function types allow functions of different types to be +conditionally convertible to each other. Such conversions do not always succeed: +Conversion from a function declaration (`func`) to a `@differentiable` function +value succeeds if and only if the function can be differentiated. Conversion +from a `@differentiable` function value to a non-`@differentiable` function +value always succeeds. Conversion from a non-`@differentiable` function value to +a `@differentiable` function value always fails, because the function's body is +opaque to the compiler. + +##### Coercing function declarations into `@differentiable` function values + +A function declaration can be implicitly coerced into a `@differentiable` +function value, when there is a contextual `@differentiable` function type. Such +conversions succeed either if the function declaration has been marked with a +`@differentiable` declaration attribute, or if the function declaration is +defined in the same module and the function can be differentiated as if it were +marked with `@differentiable`. When neither of these conditions are met, the +function cannot be differentiated, and thus cannot be converted to a +`@differentiable` function value, in which case the compiler will produce an +error. + +```swift +func addOne(_ x: Float) -> Float { x + 1 } +let _: @differentiable (Float) -> Float = addOne // Okay! +let _: @differentiable(linear) (Float) -> Float = addOne // Okay! + +let _: @differentiable(linear) (Float) -> Float = coshf(_:) +// Error: `coshf(_:)` is from a different module and has not been marked with +// `@differentiable`. + +func mySin(_ x: Float) -> Float { sin(x) * 2 } +let _: @differentiable (Float) -> Float = mySin // Okay! +let _: @differentiable(linear) (Float) -> Float = mySin +// Error: When differentiating `mySin(_:)` as a linear map, `sin` is not linear. + +func addOneViaInt(_ x: Float) -> Float { Float(Int(x) + 1) } +let _: @differentiable (Float) -> Float = addOneViaInt +// Error: When differentiating `addOneViaInt(_:)`, `Int(x)` is not differentiable. +``` + +##### Upcasting to non-`@differentiable` functions + +As shown in the +[function subtyping and runtime representation](#function-subtyping-and-runtime-representation) +subsection, a `@differentiable` function value's runtime representation contains +the original function along with extra information that allows the function to +be differentiated (or transposed, if it is `@differentiable(linear)`). A +@differentiable or `@differentiable(linear)` function value can be called like a +non-`@differentiable` function. A `@differentiable(linear)` function value can +be implicitly converted to a `@differentiable` one, which can be implicitly +converted to a non-`@differentiable` one. + +```swift +func addOne(_ x: Float) -> Float { x + 1 } +let f0: @differentiable(linear) (Float) -> Float = addOne +let f1: @differentiable (Float) -> Float = f0 +let f2: (Float) -> Float = f1 +``` + +A `@differentiable` function can also be converted to a function which is +identical except that more of its parameters are marked with `@noDerivative`. + +```swift +func addOne(_ x: Float) -> Float { x + 1 } +let f0: @differentiable (Float, Float, Float) -> Float = addOne +let f1: @differentiable (@noDerivative Float, Float, Float) -> Float = f0 +let f2: @differentiable (@noDerivative Float, Float, @noDerivative Float) -> Float = f1 +``` + +#### Implied generic constraints + +In the declaration of a generic higher-order function, when a function type is +marked with `@differentiable` as a parameter or a result and uses generic +parameters from the parent function declaration, type inference will add +implicit generic constraints that make the `@differentiable` function type's +parameter types and result type conform to `Differentiable`. + +```swift +// With all explicit generic constraints: +func foo( + _ f: @differentiable (T, U) -> V +) { + ... +} + +// With implied constraints: +// where T: Differentiable, U: Differentiable, V: Differentiable +func foo(_ f: @differentiable (T, U) -> V) { + ... +} +``` + +Similarly, when such parameters or results are marked with +`@differentiable(linear)`, implicit generic constraints will add additional +constraints that make the `@differentiable(linear)` function type's parameter +types and result type conform to `Differentiable & AdditiveArithmetic` and +satisfy `Self == Self.TangentVector`. + +```swift +// With all explicit generic constraints: +func foo( + _ f: @differentiable(linear) (T, U) -> V +) where T.TangentVector == T, U.TangentVector == U, V.TangentVector == V +{ + ... +} + +// With implied constraints: +// where T: Differentiable & AdditiveArithmetic, +// U: Differentiable & AdditiveArithmetic, +// V: Differentiable & AdditiveArithmetic, +// T.TangentVector == T, +// U.TangentVector == U, +// V.TangentVector == V +func foo(_ f: @differentiable(linear) (T, U) -> V) { + ... +} +``` + +By extending the type system with the ability to represent differentiable +functions as first-class values, users are able to define arbitrary algorithms +and data structures that deal with differentiable function values, including: + +Arbitrary higher-order functions that require arguments to be differentiable +functions. Differential operators, e.g. `derivative(of:)`, described in the +[differential operators and differentiation APIs](#differential-operators-1) +section. Differentiable higher-order functions for collections, e.g. +[`Array.differentiableReduce(_:_:)`](https://gist.github.com/rxwei/2739515f77a62d26add66947cb179911). +Data structures that store `@differentiable` functions as a property. Neural +network layers that store activation functions, e.g. +[`Dense`](https://github.com/tensorflow/swift-apis/blob/1b3f7c9231cac7d92b6ad79c2d6fd703e51f520a/Sources/TensorFlow/Layers/Core.swift#L149). +Neural network trainer objects that store loss functions, e.g. +[`Learner` in the fast.ai Swift notebooks](https://github.com/fastai/fastai_docs/blob/d3953390dde2349f3618977c717b2f5a8e84e04b/dev_swift/FastaiNotebook_04_callbacks/Sources/FastaiNotebook_04_callbacks/04_callbacks.swift#L97). + +#### Non-differentiable parameters + +Like function declarations with a `@differentiable` attribute, differentiable +function values can also be differentiable with respect to a subset of +parameters. This is expressed as part of type information, in `@differentiable` +and `@differentiable(linear)` function types, using a `@noDerivative` attribute at +each parameter that is not being differentiated with respect to. + +By default, all parameters are being differentiated with respect to. When a +`@noDerivative` attribute is specified for a parameter in a `@differentiable` +function type, values of this function type are not differentiable (or linear) +with respect to the parameter. + +```swift +let f0: @differentiable (Float, Float) -> Float = { $0 * $1 } +let f1: @differentiable(linear) (Float, Float) -> Float = { $0 + $1 } +let f2: @differentiable(linear) (Float, @noDerivative Float) -> Float = { $0 * $1 } +let f3: @differentiable (@noDerivative Int, Float, @noDerivative Int) -> Float = { + $0 ? Float($1) + $2 : 0 +} +``` + +Differentiability of parameters in a function type is important for type +conversions and is part of the subtyping rule: Any `@differentiable` or +`@differentiable(linear)` function type is a subtype of the same function type +with more `@noDerivative` parameters than there originally are. + +```swift +let f0: @differentiable (Float, Float) -> Float = { $0 * $1 } +_ = f0 as @differentiable (Float, @noDerivative Float) -> Float +_ = f0 as @differentiable (@noDerivative Float, Float) -> Float +_ = f0 as @differentiable (@noDerivative Float, @noDerivative Float) -> Float +``` + +#### Higher-order functions and currying + +As defined above, the `@differentiable` function type attributes requires all +non-`@noDerivative` arguments and results to conform to the `@differentiable` +attribute. However, there is one exception: when the type of an argument or +result is a function type, e.g. `@differentiable (T) -> @differentiable (U) -> +V`. This is because we need to differentiate higher-order funtions. + +Mathematically, the differentiability of `@differentiable (T, U) -> V` is +similar to that of `@differentiable (T) -> @differentiable (U) -> V` in that +differentiating either one will provide derivatives with respect to parameters +`T` and `U`. Here are some examples of first-order function types and their +corresponding curried function types: + +| First-order function type | Curried function type | +|---------------------------------------------|---------------------------------------------------| +| `@differentiable (T, U) -> V` | `@differentiable (T) -> @differentiable (U) -> V` | +| `@differentiable (T, @noDerivative U) -> V` | `@differentiable (T) -> (U) -> V` | +| `@differentiable (@noDerivative T, U) -> V` | `(T) -> @differentiable (U) -> V` | + +A curried differentiable function can be formed like any curried +non-differentiable function in Swift. + +```swift +func curry( + _ f: @differentiable (T, U) -> V +) -> @differentiable (T) -> @differentiable (U) -> V { + { x in { y in f(x, y) } } +} +``` + +The way this works is that the compiler internally assigns a tangent bundle to a +closure that captures variables. This tangent bundle is existentially typed, +because closure contexts are type-erased in Swift. The theory behind the typing +rules has been published as [The Differentiable +Curry](https://www.semanticscholar.org/paper/The-Differentiable-Curry-Plotkin-Brain/187078bfb159c78cc8c78c3bbe81a9176b3a6e02). + +### Differential operators + +The core differentiation APIs are the differential operators. Differential +operators are higher-order functions that take `@differentiable` functions as +inputs and return derivative functions or evaluate derivative values. + +#### Differential-producing differential operators + +Among these differential operators, two base APIs, +`valueWithDifferential(at:in:)` and `transpose(of:)`, are used for implementing +*all other differential operators and differentiation APIs*. + +```swift +/// Returns `body(x)` and the differential of `body` at `x`. +func valueWithDifferential( + at x: T, in body: @differentiable (T) -> R +) -> (value: R, + differential: @differentiable(linear) (T.TangentVector) -> R.TangentVector) { + // Compiler built-in. + Builtin.applyDerivative_arity1(body, x) +} + + +/// Returns the transpose of the linear map `body`. +func transpose( + of body: @escaping @differentiable(linear) (T) -> R +) -> @differentiable(linear) (R) -> T { + // Compiler built-in. + { x in Builtin.applyTranspose_arity1(body, x) } +} +``` + +The most common differential operators are the ones that compute directional +derivatives. These differential operators are defined to take a differentiable +function whose parameter is a real number. + +```swift +func valueWithDerivative( + at x: T, in body: @differentiable (T) -> R +) -> (value: R, derivative: R.TangentVector) where T.TangentVector: FloatingPoint { + let (value, df) = valueWithDifferential(at: x, in: body) + return (value, df(T.TangentVector(1))) +} + +func derivative( + at x: T, in body: @differentiable (T) -> R +) -> R.TangentVector where T.TangentVector: FloatingPoint { + valueWithDerivative(at: x, in: body).derivative +} + +func derivative( + of body: @escaping @differentiable (T) -> R +) -> (T) -> R.TangentVector where T.TangentVector: FloatingPoint { + return { x in derivative(at: x, in: body) } +} +``` + +#### Pullback-producing differential operators + +Unlike directional derivatives, gradients are computed by pullbacks. Based on +the differential-producing differential operator +`valueWithDifferential(at:in:)`, `valueWithPullback(at:in:)` is defined as +returning the original value and the transpose of the differential, and +`valueWithGradient(at:in:)` is defined as evaluating the pullback at `1` when +the function being differentiated returns a real number. + +```swift +func valueWithPullback( + at x: T, in body: @differentiable (T) -> R +) -> (value: R, + pullback: @differentiable(linear) (R.TangentVector) -> T.TangentVector) { + let (value, df) = valueWithDifferential(at: x, in: body) + return (value, transpose(of: df)) +} + +func valueWithGradient( + at x: T, in body: @differentiable (T) -> R +) -> (value: R, gradient: T.TangentVector) where R.TangentVector: FloatingPoint { + let (value, pullback) = valueWithPullback(at: x, in: body) + return (value, pullback(R.TangentVector(1))) +} + +func gradient( + at x: T, in body: @differentiable (T) -> R +) -> T.TangentVector where R.TangentVector: FloatingPoint { + return valueWithGradient(at: x, in: body).gradient +} + +func gradient( + of body: @escaping @differentiable (T) -> R +) -> (T) -> T.TangentVector where R.TangentVector: FloatingPoint { + return { x in gradient(at: x, in: body) } +} +``` + +#### Example usage + +All of these APIs are designed to work nicely with Swift's trailing closure +syntax. Here is an example of training a simple deep learning model: + +```swift +for _ in 0..<1000 { + // Differentiate the loss with respect to the model `classifier` itself, + // producing a tangent vector `𝛁model` that represents partial derivatives + // with respect to all differentiable properties (trainable model parameters) + // in the model + let 𝛁model = gradient(at: classifier) { classifier -> Tensor in + let ŷ = classifier(x) + let loss = softmaxCrossEntropy(logits: ŷ, labels: y) + print("Loss: \(loss)") + return loss + } + optimizer.update(&classifier, along: 𝛁model) +} +``` + +#### List of differential operators + +Differential operators | Description +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- +[`transpose(of:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L374) | Returns transpose of linear map. +[`valueWithDifferential(at:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L383)
[`valueWithDifferential(at:_:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L390) (arity 2) | Returns original result and differential function. +[`valueWithPullback(at:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L276)
[`valueWithPullback(at:_:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L283) | Returns original result and pullback function. +[`differential(at:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L435)
[`differential(at:_:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L442) (arity 2) | Returns differential function. +[`pullback(at:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L302)
[`pullback(at:_:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L309) | Returns pullback function. +[`derivative(at:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L483)
[`derivative(at:_:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L491) (arity 2) | Returns partial derivatives with respect to arguments ("forward-mode"). +[`gradient(at:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L384)
[`gradient(at:_:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L392) | Returns partial derivatives with respect to arguments ("reverse-mode"). +[`valueWithDerivative(at:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L538)
[`valueWithDerivative(at:_:in:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L547) (arity 2) | Returns original result and partial derivatives with respect to arguments ("forward-mode"). +[`valueWithGradient(at:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L327)
[`valueWithGradient(at:_:in:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L335) | Returns original result and partial derivatives with respect to arguments ("reverse-mode"). +[`derivative(of:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L601)
[`derivative(of:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L609) (arity 2) | Returns derivative function, taking original arguments and returning and partial derivatives with respect to arguments ("forward-mode"). +[`gradient(of:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L410)
[`gradient(of:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L418) | Returns gradient function, taking original arguments and returning and partial derivatives with respect to arguments ("reverse-mode"). +[`valueWithDerivative(of:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L656)
[`valueWithDerivative(of:)`](https://github.com/apple/swift/blob/9c95e27601e9623f5b53c9cc531d185e267a83d6/stdlib/public/core/AutoDiff.swift#L664) (arity 2) | Returns function taking original arguments and returning original result and partial derivatives with respect to arguments ("forward-mode"). +[`valueWithGradient(of:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L356)
[`valueWithGradient(of:)`](https://github.com/apple/swift/blob/c1211a3f78992c89a3ab4d638c378c6f45ab8fe8/stdlib/public/core/AutoDiff.swift#L364) | Returns function taking original arguments and returning original result and partial derivatives with respect to arguments ("reverse-mode"). + +#### Static analysis + +Differentiable programming in Swift aims to provide the best static compiler +diagnostics to help users catch mistakes. Beyond error diagnostics, the compiler +and the standard library are equipped with static analyses and marker APIs that +help the user write differentiable code with explicit annotations about +non-obvious non-differentiable cases. + +##### Cross-module opacity + +Swift libraries are distributed as +[modules](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html), +which provide an API interface and an opaque binary format for client code to +use. By importing a library, we can compute derivatives of functions that have +been marked with `@differentiable` or that have been provided with a linear +transpose function or a derivative function, but not of functions that have not +been marked this way without defining a custom derivative for it. For example, +if we try to differentiate +[`sinf(_:)`](https://en.cppreference.com/w/c/numeric/math/sin) with the +`derivative(at:in:)` API, the compiler will produce error messages at +compile-time instead of producing zero derivatives. + +```swift +let y = derivative(at: 1.0) { x in + sinf(x) +} +``` + +```console +test.swift:4:5: error: expression is not differentiable + sinf(x) + ^ +test.swift:4:5: note: cannot differentiate functions that have not been marked '@differentiable' and that are defined in other modules + sinf(x) + ^ +``` + +##### Non-differentiable type conversions + +Calling functions that convert values to non-differentiable types and convert +them back makes the function no longer differentiable. The compiler is able to +detect these cases and provide error messages. + +```swift +let d = derivative(at: 1.0) { x in + Double(Int(x)) + 2 +} +``` + +```console +test.swift:1:27: error: function is not differentiable +let y = derivative(at: 1.0) { x in + ^~~~~~ +test.swift:2:12: note: cannot differentiate through a non-differentiable result; do you want to add 'withoutDerivative(at:)'? + Double(Int(x)) + 2 + ^ +``` + +##### Accidental data flow mistakes + +Even when there are no obvious non-differentiable operations on the path from +parameters to the result (like non-differentiable type conversions), it is still +possible to mistype a variable and cause numerical computation to be incorrect. +As such, the compiler is able to leverage dependency analysis to determine +whether the derivative is always zero and warns the user. + +```swift +let grad = gradient(at: 1.0) { x in + Double(3).squareRoot() +} +``` + +```console +test.swift:4:18: warning: result does not depend on differentiation arguments and will always have a zero derivative; do you want to use 'withoutDerivative(at:)' to make it explicit? + Double(3).squareRoot() + ^ + withoutDerivative(at:) +``` + +## Examples of differentiable programming + +### Linear regression + +[Linear Regression](https://en.wikipedia.org/wiki/Linear_regression) attempts to +fit a line that best fits a set of data points. There are two different ways of +finding a solution: the iterative and closed form methods. In the iterative +method, we use gradient descent to slowly find better and better values for the +slope and y-intercept. For a basic set of data points consisting of `(x, y)` +value pairs, the model would look like the following: + +```swift +struct Perceptron: @memberwise Differentiable { + var weights: SIMD64 + var bias: Float + + @differentiable + func callAsFunction(_ input: SIMD64) -> Float { + weights.dot(input) + bias + } +} +``` + +To train the model on a data set, it would look like the following: + +```swift +let iterationCount = 160 +let learningRate: Float = 0.00003 + +var model = Perceptron(weights: .zero, bias: 0) + +for i in 0.. Float in + var totalLoss: Float = 0 + for (x, y) in data { + let pred = model(x) + let diff = y - pred + totalLoss = totalLoss + diff * diff / Float(data.count) + } + return totalLoss + } + 𝛁loss.weight *= -learningRate + 𝛁loss.bias *= -learningRate + model.move(along: 𝛁loss) + if i.isMultiple(of: 10) { + print("Iteration: \(iteration) Avg Loss: \(loss / Float(data.count))") + } +} +``` + +### Deep learning + +[Swift for TensorFlow](https://github.com/tensorflow/swift) is a numerics and +machine learning library that uses the proposed differentiable programming +feature. Swift for TensorFlow has been used to implement many machine learning +models, from simple image classification models like +[ResNet](https://arxiv.org/abs/1512.03385) to advanced models using Monte Carlo +tree search to power a [Go](https://en.wikipedia.org/wiki/Go_\(game\)) game +engine. + +#### Feed-forward neural networks (FFNN) + +A neural networks is a "parameterized function approximator": it takes some +input, produces some output, and is parameterized by weights. Neural networks +are composed of *layers*, which are smaller "building block" parameterized +functions. A loss function (or cost function) measures the difference between +the output of a neural network versus the expected output. Neural networks can +improve via training: networks are applied to "training data" (input/output +pairs) and parameters are updated with their derivatives with respect to the +loss function. + +A feed-forward neural network is a simple neural network in which the output of +each layer is fed as the input to the next layer. A multi-layer perceptron is an +example of a feed-forward neural network: it is composed of multiple dense +layers, each of which performs `output = activation(matmul(weight, input) + +bias)`. + +```swift +import TensorFlow + +struct MultiLayerPerception: Layer, @memberwise Differentiable { + var dense1 = Dense(inputSize: 784, outputSize: 100, activation: relu) + var dense2 = Dense(inputSize: 100, outputSize: 30, activation: relu) + var dense3 = Dense(inputSize: 30, outputSize: 10, activation: softmax) + + @differentiable + func callAsFunction(_ input: Tensor) -> Tensor { + dense3(dense2(dense1(input))) + } +} +``` + +#### Convolutional neural networks (CNN) + +A convolution neural network is a feed-forward neural network that performs a +[cross-correlation operation](https://en.wikipedia.org/wiki/Cross-correlation), +which is a "sliding dot product" over the input. The cross-correlation operation +encodes spatial locality and translation invariance, making CNNs suited for +applications like image recognition. + +Here is a simple script that implements +[LeNet-5](http://yann.lecun.com/exdb/lenet/), a convolutional neural network for +classifying handwritten digits. + +```swift +import TensorFlow + +// Original Paper: +// "Gradient-Based Learning Applied to Document Recognition" +// Yann LeCun, Léon Bottou, Yoshua Bengio, and Patrick Haffner +// http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf +// +// Note: this implementation connects all the feature maps in the second convolutional layer. +// Additionally, ReLU is used instead of sigmoid activations. +struct LeNet: Layer, @memberwise Differentiable { + var conv1 = Conv2D(filterShape: (5, 5, 1, 6), padding: .same, activation: relu) + var pool1 = AvgPool2D(poolSize: (2, 2), strides: (2, 2)) + var conv2 = Conv2D(filterShape: (5, 5, 6, 16), activation: relu) + var pool2 = AvgPool2D(poolSize: (2, 2), strides: (2, 2)) + var flatten = Flatten() + var fc1 = Dense(inputSize: 400, outputSize: 120, activation: relu) + var fc2 = Dense(inputSize: 120, outputSize: 84, activation: relu) + var fc3 = Dense(inputSize: 84, outputSize: 10, activation: softmax) + + @differentiable + func callAsFunction(_ input: Tensor) -> Tensor { + let convolved = pool2(conv2(pool1(conv1(input)))) + return fc3(fc2(fc1(flatten(convolved)))) + } +} +``` + +#### Recurrent neural networks (RNN) + +A recurrent neural network is a feed-forward neural network wrapped in a loop +over a sequence of inputs. The feed-forward neural network within the loop is +usually referred to as the "cell" of the RNN. An RNN cell, like other neural +network layers, has a `callAsFunction(_:)` method that is differentiable with +respect to `self` and `input`, where `input` is an element of the sequence that +is the input to the RNN. + +```swift +/// A recurrent neural network cell. +protocol RNNCell: Layer +where Input == RNNCellInput, + Output == RNNCellOutput { + /// The input at a time step. + associatedtype TimeStepInput: Differentiable + /// The output at a time step. + associatedtype TimeStepOutput: Differentiable + /// The state that may be preserved across time steps. + associatedtype State: Differentiable + /// The zero state. + var zeroState: State { get } +} +``` + +Below is the cell of a +[long short-term memory (LSTM)](https://en.wikipedia.org/wiki/Long_short-term_memory) +network, which is used widely in natural language processing and speech +processing. + +```swift +/// An LSTM cell. +struct LSTMCell: RNNCell, @memberwise Differentiable { + var fusedWeight: Tensor + var fusedBias: Tensor + + @noDerivative var stateShape: TensorShape { [1, fusedWeight.shape[1] / 4] } + + var zeroState: State { + State(cell: Tensor(zeros: stateShape), hidden: Tensor(zeros: stateShape)) + } + + typealias TimeStepInput = Tensor + typealias TimeStepOutput = State + typealias Input = RNNCellInput + typealias Output = RNNCellOutput + + struct State: @memberwise Differentiable { + var cell: Tensor + var hidden: Tensor + } + + @differentiable + func callAsFunction(_ input: Input) -> Output { + let gateInput = input.input.concatenated(with: input.state.hidden, alongAxis: 1) + let fused = matmul(gateInput, fusedWeight) + fusedBias + let (batchSize, hiddenSize) = (fused.shape[0], fused.shape[1] / 4) + let fusedParts = fused.split(count: 4, alongAxis: 1) + let (inputGate, updateGate, forgetGate, outputGate) = ( + sigmoid(fusedParts[0]), + tanh(fusedParts[1]), + sigmoid(fusedParts[2]), + sigmoid(fusedParts[3]) + ) + let newCellState = input.state.cell * forgetGate + inputGate * updateGate + let newHiddenState = tanh(newCellState) * outputGate + let newState = State(cell: newCellState, hidden: newHiddenState) + return Output(output: newState, state: newState) + } +} +``` + +Since an RNN is a loop wrapped around the cell, it can be implemented as a +generic struct with a `Cell` generic parameter that conforms to `RNNCell`. + +```swift +struct RNN: Layer { + typealias Input = [Cell.TimeStepInput] + typealias Output = [Cell.TimeStepOutput] + + var cell: Cell + + init(_ cell: @autoclosure () -> Cell) { + self.cell = cell() + } + + @differentiable(wrt: (self, input)) + func callAsFunction(_ input: [Cell.TimeStepInput]) -> [Cell.TimeStepOutput] { + var currentHiddenState = zeroState + var timeStepOutputs: [Cell.TimeStepOutput] = [] + for timeStep in input { + let output = cell(input: timeStep, state: currentHiddenState) + currentHiddenState = output.state + timeStepOutputs.append(output.output) + } + return timeStepOutputs + } +} +``` + +Using generics, one can compose `RNN` with different RNN cell types. Different +RNN types can be defined in a library simply by creating a type alias. + +```swift +typealias SimpleRNN = RNN> +typealias LSTM = RNN> +``` + +## Future directions + +### Higher-order differentiation + +Distinct from differentiation of higher-order functions, higher-order +differentiation refers to taking the derivative of a derivative of a function. +As a natural next step after the first-order differentiation capability proposed +here, higher-order differentiation can be designed and implemented in various +different ways with trade-offs in performance, usability, and complexity. + +Intuitively, higher-order differentiation will enable calling a differential +operator on the result of a differential operator, e.g. + +```swift +let f = derivative(of: derivative(of: derivative(of: { x in pow(x, 3.0) }))) +``` + +This will require the differential operator `derivative(of:)` to return a +`@differentiable` function, hence semantically changing `@differentiable` to +mean infinite differentiability. + +```swift +func derivative( + _ f: @differentiable (T) -> U +) -> @differentiable (T) -> U where T: FloatingPoint, T == T.TangentVector { + { x in differential(at: x, in: f) } +} +``` + +Since `derivative(of:)` is implemented in term of `derivative(at:in:)`, which is +implemented in terms of `valueWithDifferential(at:in:)`, both +`derivative(at:in:)` and `valueWithDifferential(at:in:)` would need to be marked +with `@differentiatiable` with respect to its `x` argument. + +```swift +@differentiable(wrt: x) +func derivative( + at x: T, in body: @differentiable (T) -> U) -> U +) -> U.TangentVector where T: FloatingPoint, T == T.TangentVector { + valueWithDifferential(at: x, in: body).differential(T(1)) +} + +@differentiable(wrt: x) +func valueWithDifferential( + at x: T, in body: @differentiable (T) -> U) -> U +) -> (value: U, differential: @differentiable(linear) (T.TangentVector) -> U.TangentVector) +``` + +To differentiate `valueWithDifferential`, we need to be able to differentiate +its return value, a tuple of the original value and the differential, with +respect to its `x` argument. + +A kneejerk solution is to differentiate derivative functions generated by the +differentiation transform at compile-time, but this leads to problems. For +example, how do we repeatedly differentiate a function whose body is +unavailable? Should a `@differentiable` function contain derivative functions +for dynamically many orders? Would it require serializing SIL code as part of a +`@differentiable` function and running the differentiation transform at runtime? +Alternatively, is there a single closed-form formula that the compiler can +generate once in the differentiation transform, without performing any runtime +compilation or using large function representations? These questions are +difficult to answer, due to the complexity in both mathematical formulae (e.g. +[Faà di Bruno's formula](https://en.wikipedia.org/wiki/Faà_di_Bruno%27s_formula)) +and static compilation. Currently, we are exploring different theoretical and +practical approaches to find a beautiful design that would help us deliver the +best differentiable programming language. + +### Naming conventions for numerical computing + +The +[API Design Guidelines](https://swift.org/documentation/api-design-guidelines) +encourages naming that is both easy-to-learn for beginners and unsurprising for +experts. + +Numerical computing is full of math terminology and notation; finding good names +for math concepts is not always easy. Consider the formulas for gated recurrent +neural networks: + +

+ +

+ +Each of these mathematical variables needs a name in code. Consider the +following names for the `W_ih` variable: + +- `var W_ih`: the abbreviated name. May be difficult to learn for beginners. +- `var inputHiddenWeight`: the descriptive name. May be unfamiliar for + experts, who are accustomed to the math notation. + +Which name is the best? It is hard to say, as no naming precedent exists. +Standardizing naming conventions for math terminology will be important as +numerical computing becomes more prominent in Swift. + +## Source compatibility + +This feature does not change any existing APIs. New implicit function +conversions are added to the type system, which slightly increases type checking +complexity. We have not observed source compatibility breakages so far. + +## Effect on ABI stability + +This feature has additions to the ABI. Specifically, the `@differentiable` +function representation will be added and must be kept stable. + +## Effect on API resilience + +This feature adds the [`Differentiable` protocol](#differentiable-protocol) and +[differential operators](#differential-operators) to the standard library as +public APIs. They introduce additions to the standard library. + +### `Differentiable` protocol + +The `Differentiable` protocol contains all necessary requirements for a type to +be differentiated. Without breaking API, it will be possible to add extensions +to the `Differentiable` protocol and add new requirements with default +implementations. + +### Differential operators + +Differential operators (e.g. `derivative(of:)` and `gradient(of:)`) are added to +the standard library as lightweight top-level higher-order functions. These APIs +can be renamed or moved under some namespace without breaking ABI. + +## Alternatives considered + +### Not support differentiable programming + +We believe first-class differentiable programming is a big step towards making +Swift a real contender in the numerical computing and machine learning +landscape. Differentiable programming will enable intelligent applications, +machine learning models, scientific experiments, physical simulations, and more. + +### Use another language or framework for differentiable programming + +Dynamic languages, like Python and Julia, have established library support for +differentiable programming. While it is possible to interoperate with these +libraries via Swift, we feel that first-class differentiable programming in +Swift is leaps ahead in expressivity, usability, and safety. + +### Other approaches to differentiable programming + +See +["Approaches to automatic differentiation"](#approaches-to-automatic-differentiation) +above for an overview and comparison of automatic differentiation approaches. +First-class language support for differentiation will enable convenient, +extensible, and performant differentiable programming in Swift - more so than +library-based approaches. + +## Acknowledgements + +Many people have influenced the design and the implementation of the +differentiable programming feature. The authors would like to thank these people +(sorted alphabetically by last name) for their contributions in any form +(inspirations, ideas, discussions, code, or bikeshedding): Gogul Balakrishnan, +James Bradbury, Steve Canon, Casey Chu, Conal Elliott, Roy Frostig, Doug Gregor, +Dominik Grewe, Dmitri Gribenko, Joe Groff, Sylvain Gugger, Tim Harley, Matthew +Johnson, Chris Lattner, Dougal Maclaurin, John McCall, Bart van Merriënboer, +Slava Pestov, Anthony Platanios, Gordon Plotkin, Alexey Radul, Brennan Saeta, +Parker Schuh, and Dimitrios Vytiniotis. + + + +[Richard Wei]: https://github.com/rxwei +[Dan Zheng]: https://github.com/dan-zheng +[Marc Rasi]: https://github.com/marcrasi +[Bart Chrzaszcz]: https://github.com/bartchr808 + +[swift-numerics]: https://github.com/apple/swift-numerics +[SE-0229]: https://github.com/apple/swift-evolution/blob/master/proposals/0229-simd.md +[SE-0233]: https://github.com/apple/swift-evolution/blob/master/proposals/0233-additive-arithmetic-protocol.md +[SE-0246]: https://github.com/apple/swift-evolution/blob/master/proposals/0246-mathable.md +[SE-0251]: https://github.com/apple/swift-evolution/blob/master/proposals/0251-simd-additions.md diff --git a/docs/ErrorHandlingRationale.rst b/docs/ErrorHandlingRationale.rst index 71dd3e4de5fc3..a55956b868639 100644 --- a/docs/ErrorHandlingRationale.rst +++ b/docs/ErrorHandlingRationale.rst @@ -1180,7 +1180,7 @@ Haskell Haskell provides three different common error-propagation mechanisms. The first is that, like many other functional languages, it supports -manual propagation with a ``Maybe`` type. A function can return ``None`` +manual propagation with a ``Maybe`` type. A function can return ``Nothing`` to indicate that it couldn't produce a more useful result. This is the most common failure method for functions in the functional subset of the library. diff --git a/docs/GenericsManifesto.md b/docs/GenericsManifesto.md index f759d26974e04..7a031bf5cd51d 100644 --- a/docs/GenericsManifesto.md +++ b/docs/GenericsManifesto.md @@ -88,6 +88,30 @@ var d1 = StringDictionary() var d2: Dictionary = d1 // okay: d1 and d2 have the same type, Dictionary ``` +### Generic associatedtypes + +Associatedtypes could be allowed to carry generic parameters. + +```Swift + protocol Wrapper { + associatedtype Wrapped + + static func wrap(_ t: T) -> Wrapped + } +``` + +Generic associatedtypes would support all constraints supported by the language including where clauses. As with non-generic associatedtypes conforming types would be required to provide a nested type or typealias matching the name of the associatedtype. However, in this case the nested type or typealias would be generic. + +```Swift + enum OptionalWrapper { + typealias Wrapped = Optional + + static func wrap(_ t: T) -> Optional + } +``` + +Note: generic associatedtypes address many use cases also addressed by higher-kinded types but with lower implementation complexity. + ### Generic subscripts *This feature has been accepted in [SE-0148](https://github.com/apple/swift-evolution/blob/master/proposals/0148-generic-subscripts.md), was tracked by [SR-115](https://bugs.swift.org/browse/SR-115) and was released with Swift 4.* @@ -249,6 +273,21 @@ typealias AnyObject = protocol See the "Existentials" section, particularly "Generalized existentials", for more information. +### Generalized supertype constraints + +Currently, supertype constraints may only be specified using a concrete class or protocol type. This prevents us from abstracting over the supertype. + +```Swift +protocol P { + associatedtype Base + associatedtype Derived: Base +} +``` + +In the above example `Base` may be any type. `Derived` may be the same as `Base` or may be _any_ subtype of `Base`. All subtype relationships supported by Swift should be supported in this context including (but not limited to) classes and subclasses, existentials and conforming concrete types or refining existentials, `T?` and `T`, `((Base) -> Void)` and `((Derived) -> Void)`, etc. + +Generalized supertype constraints would be accepted in all syntactic locations where generic constraints are accepted. + ### Allowing subclasses to override requirements satisfied by defaults (*) When a superclass conforms to a protocol and has one of the protocol's requirements satisfied by a member of a protocol extension, that member currently cannot be overridden by a subclass. For example: diff --git a/docs/GitWorkflows.rst b/docs/GitWorkflows.rst deleted file mode 100644 index 2027743712173..0000000000000 --- a/docs/GitWorkflows.rst +++ /dev/null @@ -1,281 +0,0 @@ -:orphan: - -.. highlight:: bash - -Git Workflows -============= - -Purpose -------- - -Swift development has been based on SVN since its inception. As part of the -transition to Git this document helps to address questions about how common SVN -workflows we use today translate to their Git counterparts as well as to discuss -Git workflow practices we plan on having -- at least initially -- after the Git -transition. Notably we will follow a model where commits to trunk -- which is -the 'master' branch in Git -- has commits land (in the common case) via rebasing -instead of merging. This model is open to evolution later, but this mimics the -workflow we have today with SVN. - -SVN -> GIT Workflows -==================== - -The general SVN workflow consists of the following commands: - -1. Checkout: This means checking out/setting up a new repository. -2. Update: Pulling the latest remote changes into a local repository. -3. Committing: Committing a change to the remote repository. -4. Reverting: Reverting a change from a remote repository. -5. Browsing: Looking at commits. - -This document will show how to translate these commands to Git and additionally -how to configure Git. It assumes that one is attempting to manipulate a Git -repository via bash in a terminal. A lot of information since this is supposed -to be a short, actionable guide. For more information, please see the Git crash -course guide for SVN users at - -*NOTE* Whenever we say the Swift repository, we mean any repository in the -Swift project. - -Quicksetup (TLDR) ------------------ - -For those who do not want to read the full document, use the following commands -to perform a simple repo setup for the Swift repository:: - - $ git config --global user.name "" - $ git config --global user.email "" - $ mkdir swift-source && cd swift-source - $ git clone - $ git clone - $ git clone - $ (cd swift && git config branch.autosetuprebase always) - $ git clone - $ git clone - -Then to commit a new commit to the remote swift repository:: - - $ git commit - $ git push origin master - -and to pull new commits from the remote swift repository:: - - $ git pull origin master - -In order to ease updating all repositories, consider using the script in -'./utils/update-checkout'. This will automate updating the repositories in the -proper way. - -Preliminary ------------ - -Before beginning, we need to perform some global configuration of Git. Git -includes a username/email of the committer in every commit. By default this is -the current logged in user and the hostname of the machine. This is /not/ what -one wants. We configure Git globally (i.e. across all repositories) to have our -proper name and email by running the following commands:: - - $ git config --global user.name "" - $ git config --global user.email "" - -Checkout --------- - -Normally if one wishes to checkout a repository in SVN, one would use a command -like this:: - - $ SVN co - -This would then checkout the latest revision from the repository specified by -'repository url' and place it into the directory 'local directory'. In Git, -instead of checking out the repository, one clones the repository. This is done -as follows:: - - $ git clone - -This will cause Git to clone the repository at 'repository url' and check out -the 'master' branch. The 'master' branch corresponds to 'trunk' in SVN. For more -information about branching in Git please see - - -Before beginning to commit though, we /must/ perform some default configuration -of our repository to match the Swift repository default configuration by -enabling default rebasing. - -Repository Configuration (Enabling Default Rebasing) ----------------------------------------------------- - -Once we have cloned the repository, we need to configure the repository for our -use. Specifically we want to configure the swift repository so that we rebase -whenever we update the repository (see the update section below for more -details):: - - $ git config branch.autosetuprebase always - -By default when updating, Git will attempt to merge the remote changes and your -local changes. Ignoring what that sentence means, this is not an SVN-esque -model. Instead we want any local changes that we have to be applied on top of -any new remote changes. The 'branch.autosetuprebase' flag causes this to be done -automatically whenever one updates the local repository. - -Update ------- - -In SVN, one updates your local repository by running the update command:: - - $ SVN update - -In Git, instead of performing SVN update, one pulls from the remote repository:: - - $ git pull --rebase origin master - -This will pull any new remote commits into your local repository and then replay -your current local commits on top of those new commits. - -By default the '--rebase' flag is not necessary for the Swift repository because -it is configured to always rebase by setting the 'branch.autosetuprebase' flag -(see the section 'Repository Configuration (Enabling Default Rebasing)' above). - -Commit ------- - -In SVN, committing always means submitting changes to a remote repository. In -Git, committing refers to the process of first telling Git to track a change by -staging the change and then committing all staged changes into a change in the -local repository. One can have many such commits. Then when one is ready, one -pushes the new local changes to the remote repository. We go through these steps -in more detail below: - -In terms of replicating the SVN model, there are now two steps. In order to -commit changes one first stages a changed file using 'git add':: - - $ git add - -Then once all changes that you want to be apart of the commit have been staged, -a commit is created in the local repository via the 'commit' command:: - - $ git commit - -As a shortcut to commit /all/ changes to local files that are already being -tracked by Git to the local repository, you can use the '-a' command:: - - $ git commit -a - -In both of these cases, an editor will pop up to accept a commit message. To -specify a short commit message at the commandline, you can use the '-m' flag:: - - $ git commit -m 'My great commit message.' - -In order to see the diff of changes that have not been staged, run the command:: - - $ git diff - -To see all changes that have been staged, run the command:: - - $ git diff --staged - -To get a diff for a specific revision/path, perform the following command:: - - $ git diff - -In order to get a more concise view of the files that have staged and or -unstaged changes, run the command:: - - $ git status - -In order to restore a file from the last revision, one uses the checkout -command:: - - $ git checkout - -To restore a file to a specific revision, one must use a longer form of the -command:: - - $ git checkout -- - -To unstage a file, one uses the 'reset' command:: - - $ git reset - -This tells Git to reset '' in the staging area to the top of tree commit -(which in Git is called 'HEAD'). In order to correct a mistake, you can pass the -'amend' flag to Git:: - - $ git commit --amend - -This will cause all staged changes to be merged into 'HEAD'. Once one has made -all the relevant commits, in order to push the changes to the remote repository -the 'push' command is used:: - - $ git push origin master - -If a different committer has committed changes such that there are remote -commits that are not present locally, this will fail. In order to get around -this issue, perform:: - - $ git pull --rebase origin master - -in order to pull the new remote commits and replay your new commits on top. Then -try to push again. See the 'Checkout' section above how to configure the local -swift repository to always rebase allowing you to drop the '--rebase' flag. - -Revert ------- - -In SVN reverting a commit implies performing a reverse merge. In Git, this is no -longer true. Instead one now just uses the 'revert' command:: - - $ git revert - -This will cause Git to perform the reverse merge of that revision for you -against HEAD and bring up a message window for you to write a commit -message. This will be autofilled in with the title of the commit that is going -to be reverted and the revision number of that commit like so:: - - Revert "" - - This reverts commit . - -One can edit this message as one sees fit. Once this has been done, the revert -will become a normal commit in your repository like any other commit. Thus to -revert the commit in the remote repository, you need to perform a Git push:: - - $ git push origin master - -Browsing --------- - -This section explains how one can view Git changes. In order to view a history -of all changes on a branch to the beginning of time use the 'log' command:: - - $ git log - -This will for each commit show the following information:: - - commit - Author: - Date: - - - -To see history starting at a specific commit use the following form of a Git log -command:: - - $ git log - -To see an oneline summary that includes just the title of the commit and its -hash, pass the '--oneline' command:: - - $ git log --oneline - -It will not show you what was actually changed in each commit. In order to see -what was actually changed in a commit, use the command 'show':: - - $ git show - -This will show the aforementioned information shown by Git log, but additionally -will perform a diff against top of tree showing you the contents of the -change. To see the changes for a specific commit, pass the revision to Git -show:: - - $ git show diff --git a/docs/HighLevelSILOptimizations.rst b/docs/HighLevelSILOptimizations.rst index 4b97b75705577..8646e741d9b61 100644 --- a/docs/HighLevelSILOptimizations.rst +++ b/docs/HighLevelSILOptimizations.rst @@ -135,7 +135,7 @@ Array, ContiguousArray, and ArraySlice data-structures. We consider the array state to consist of a set of disjoint elements -and a storage descriptor that encapsulates nonelement data such as the +and a storage descriptor that encapsulates non-element data such as the element count and capacity. Operations that semantically write state are always *control dependent*. A control dependent operation is one that may only be executed on the control flow paths in which the diff --git a/docs/LibraryEvolution.rst b/docs/LibraryEvolution.rst index a66868a9bf4fe..2a44a40db4228 100644 --- a/docs/LibraryEvolution.rst +++ b/docs/LibraryEvolution.rst @@ -3,18 +3,42 @@ .. default-role:: term .. title:: Library Evolution Support in Swift ("Resilience") -:Author: Jordan Rose -:Author: John McCall - .. note:: This document uses some Sphinx-specific features which are not available on - GitHub. For proper rendering, download and build the docs yourself. Jordan - Rose also posts occasional snapshots at - https://jrose-apple.github.io/swift-library-evolution/. + GitHub. For proper rendering, download and build the docs yourself. + +Since Swift 5, ABI-stable platforms have supported `library evolution`_, the +ability to change a library without breaking source or binary compatibility. +This model is intended to serve library designers whose libraries will evolve +over time. Such libraries must be both `backwards-compatible`, meaning that +existing clients should continue to work even when the library is updated, and +`forwards-compatible`, meaning that future clients will be able run using the +current version of the library. In simple terms: + +- Last year's apps should work with this year's library. +- Next year's apps should work with this year's library. + +This document is intended to be a specification for *which* changes can be made +without breaking binary compatibility. When a library author wants to make a +change, they can jump to the relevant section of this document to see if it's +allowed. Anything *not* listed in this document should be assumed unsafe, i.e. +changing it will break binary compatibility. + +Library evolution was formally described in `SE-0260 `_, but this +document should be kept up to date as new features are added to the language. + +.. _library evolution: https://swift.org/blog/abi-stability-and-more/ +.. _SE0260: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md + +.. contents:: :local: + -One of Swift's primary design goals is to allow efficient execution of code -without sacrificing load-time abstraction of implementation. +Background +========== + +One of Swift's primary design goals has always been to allow efficient +execution of code without sacrificing load-time abstraction of implementation. Abstraction of implementation means that code correctly written against a published interface will correctly function when the underlying implementation @@ -49,24 +73,14 @@ This last point is a specific case of a general tenet of Swift: **the default behavior is safe**. Where possible, choices made when an entity is first published should not limit its evolution in the future. -.. contents:: :local: - Introduction ============ -This model is intended to serve library designers whose libraries will evolve -over time. Such libraries must be both `backwards-compatible`, meaning that -existing clients should continue to work even when the library is updated, and -`forwards-compatible`, meaning that future clients will be able run using the -current version of the library. In simple terms: - -- Last year's apps should work with this year's library. -- Next year's apps should work with this year's library. - This document will frequently refer to a *library* which vends public APIs, and a single *client* that uses them. The same principles apply even when multiple -libraries and multiple clients are involved. +libraries and multiple clients are involved. It also uses the term `ABI-public` +introduced in `SE-0193 `_. This document is primarily concerned with `binary compatibility`, i.e. what changes can safely be made to a library between releases that will not break @@ -91,18 +105,18 @@ library, as used by the `Swift Package Manager`_). Because a client always uses a particular version of such a library, there is no need to worry about backwards- or forwards-compatibility at the binary level. Just as developers with a single app target are not forced to think about access control, anyone -writing a bundled library should not be required to use any of the annotations -described below in order to achieve full performance. +writing a bundled library should (ideally) not be required to use any of the +annotations described below in order to achieve full performance. +.. _SE0193: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md .. _Swift Package Manager: https://swift.org/package-manager/ .. note:: This model may, however, be useful for library authors that want to - preserve *source* compatibility, and it is hoped that the tool for - `Checking Binary Compatibility`_ described below will also be useful for - this purpose. Additionally, we may decide to use some of these annotations - as performance hints for *non-*\ optimized builds. + preserve *source* compatibility, though this document mostly doesn't + discuss that. Additionally, some of these annotations are useful for + performance today, such as ``@inlinable``. The term "resilience" comes from the occasional use of "fragile" to describe certain constructs that have very strict binary compatibility rules. For @@ -112,164 +126,6 @@ changing the fields in a struct will not automatically cause problems for existing clients, so we say the struct is "resilient". -Using Versioned API -=================== - -References to a versioned API must always be guarded with the appropriate -availability checks. This means that any client entities that rely on certain -APIs from a library must themselves be restricted to contexts in which those -APIs are available. This is accomplished using the ``@available`` attribute, by -specifying the name of the client library along with the required version:: - - // Client code - @available(Magician 1.5) - class CrystalBallView : MagicView { /*...*/ } - -Library versions can also be checked dynamically using ``#available``, allowing -for fallback behavior when the requested library version is not present:: - - func scareMySiblings() { - if #available(Magician 1.2) { - summonDemons() - } else { - print("BOO!!") - } - } - -.. note:: - - Possible implementations include generating a hidden symbol into a library, - or putting the version number in some kind of metadata, like the Info.plist - in a framework bundle on Darwin platforms. - -This is essentially the same model as the availability checking released in -Swift 2.0, but generalized for checking library versions instead of just OS -versions. - - -Declaring Library Version Dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Swift's current availability model includes the notion of a *minimum deployment -target,* the version of an OS that must be present for the program being -compiled to run at all. For example, a program compiled with a minimum -deployment target of iOS 9.2 will not launch on iOS 9.0. - -The generalized model above suggests being able to make similar guarantees for -individual libraries. For example, a client program may depend on version 1.1 -of the "Magician" library; trying to run using version 1.0 will result in -errors. By declaring this at compile-time, the client code can omit -``@available`` and ``#available`` checks that are satisfied by the minimum -library version. - -Both the syntax and enforcement of this feature are not covered by this -document. - - -Publishing Versioned API -======================== - -A library's API is already marked with the ``public`` modifier, but if a -client wants to work with multiple releases of the library, the API needs -versioning information as well. A *versioned entity* represents anything with a -run-time presence that a client may rely on; its version records when the entity -was first exposed publicly in its library. Put another way, it is the oldest -version of the library where the entity may be used. - -- Classes, structs, enums, and protocols may all be versioned entities. -- Methods, properties, subscripts, and initializers may be versioned entities. -- Top-level functions, variables, and constants may be versioned entities. -- Protocol conformances may be versioned entities, despite not explicitly having - a declaration in Swift, because a client may depend on them. - See `New Conformances`_, below. -- Typealiases are treated as versioned entities for the purpose of verifying - availability, even though they have no run-time presence. - -In a versioned library, any top-level public entity from the list above may not -be made ``public`` (or ``open``) without an appropriate version. A public -entity declared within a versioned type (or an extension of a versioned type) -will default to having the same version as the type. - -In this document, the term "public" includes classes and members marked -``open``. - -Code within a library may generally use all other entities declared within the -library (barring their own availability checks), since the entire library is -shipped as a unit. That is, even if a particular API was introduced in v1.0, -its (non-public) implementation may refer to APIs introduced in later versions. - -Certain uses of ``internal`` entities require them to be part of a library's -binary interface, which means they need to be versioned as well. See -`Versioning Internal Declarations`_ below. - -In addition to versioned entities, there are also attributes that are safe to -add to declarations when releasing a new version of a library. In most cases, -clients can only take advantage of the attributes when using the new release of -the library, and therefore the attributes also need to record the version in -which they were introduced; these are called *versioned attributes.* If the -version is omitted, it is assumed to be the version of the declaration to which -the attribute is attached. - -The syntax for marking an entity as versioned has not yet been decided, but the -rest of this document will use syntax #1 described below. - - -Syntax #1: Attributes -~~~~~~~~~~~~~~~~~~~~~ - -:: - - @available(1.2) - public func summonDemons() - - @available(1.0) @inlinable(1.2) - public func summonElves() - -Using the same attribute for both publishing and using versioned APIs helps tie -the feature together and enforces a consistent set of rules. However, there are -several other annotations described later in this document that also need -versioning information, and it may not be obvious what the version number means -outside the context of ``available``. - - -Syntax #2: Version Blocks -~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - #version(1.2) - public func summonDemons() - - #version(1.0) {} - #version(1.2) { @inlinable } - public func summonElves() - -Since there are potentially many annotations on a declaration that need -versioning information, it may make sense to group them together in some way. -Only certain annotations would support being versioned in this way. - - -Syntax #3: The ``public`` modifier -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - public(1.2) func summonDemons() - - /* @inlinable ?? */ - public(1.0) func summonElves() - -Putting the version on the public modifier is the most concise option. However, -there's no obvious syntax here for adding versions to other annotations that -may apply to a declaration. - -(Also, at one point there was a proposal to tag API only intended for certain -clients using a similar syntax: ``public("Foundation")``, for example, for APIs -only meant to be used by Foundation. These could then be stripped out of the -public interface for a framework before being widely distributed. But that -could easily use an alternate syntax.) - - Supported Evolution =================== @@ -277,45 +133,44 @@ This section describes the various changes that are safe to make when releasing a new version of a library, i.e. changes that will not break binary compatibility. They are organized by declaration type. -Anything *not* listed in this document should be assumed unsafe. - - Top-Level Functions ~~~~~~~~~~~~~~~~~~~ -A versioned top-level function is fairly restricted in how it can be changed. +An ABI-public top-level function is fairly restricted in how it can be changed. The following changes are permitted: -- Changing the body of the function. +- Changing the body of the function (as long as it is not ``@inlinable``; see + below). - Changing *internal* parameter names (i.e. the names used within the function body, not the labels that are part of the function's full name). - Reordering generic requirements (but not the generic parameters themselves). - Adding a default argument expression to a parameter. - Changing or removing a default argument is a `binary-compatible source-breaking change`. -- The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a function without any additional versioning information. +- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access`` + attributes. No other changes are permitted; the following are particularly of note: -- A versioned function may not change its parameters or return type. -- A versioned function may not change its generic requirements. -- A versioned function may not change its external parameter names (labels). -- A versioned function may not add, remove, or reorder parameters, whether or +- An ABI-public function may not change its parameters or return type. +- An ABI-public function may not change its generic requirements. +- An ABI-public function may not change its external parameter names (labels). +- An ABI-public function may not add, remove, or reorder parameters, whether or not they have default arguments. -- A versioned function that throws may not become non-throwing or vice versa. +- An ABI-public function that throws may not become non-throwing or vice versa. - The ``@escaping`` attribute may not be added to or removed from a parameter. - It is not a `versioned attribute` and so there is no way to guarantee that it - is safe when a client deploys against older versions of the library. +- Adding or removing a function builder from a parameter is a + `binary-compatible source-breaking change`. Inlinable Functions ------------------- -Functions are a very common example of resilience: the function's declaration -is published as API, but its body may change between library versions as long -as it upholds the same semantic contracts. This applies to other function-like -constructs as well: initializers, accessors, and deinitializers. +Functions are a very common example of "abstraction of implementation": the +function's declaration is published as API, but its body may change between +library versions as long as it upholds the same semantic contracts. This +applies to other function-like constructs as well: initializers, accessors, and +deinitializers. However, sometimes it is useful to provide the body to clients as well. There are a few common reasons for this: @@ -331,12 +186,9 @@ are a few common reasons for this: - The function is generic and its performance may be greatly increased by specialization in the client. -A versioned function marked with the ``@inlinable`` attribute makes its body -available to clients as part of the module's public interface. ``@inlinable`` -is a `versioned attribute`; clients may not assume that the body of the -function is suitable when deploying against older versions of the library. - -Clients are not required to inline a function marked ``@inlinable``. +An ABI-public function marked with the ``@inlinable`` attribute makes its body +available to clients as part of the module's public interface. Clients are not +required to inline a function marked ``@inlinable``. .. note:: @@ -348,17 +200,15 @@ Clients are not required to inline a function marked ``@inlinable``. Any local functions or closures within an inlinable function are treated as ``@_alwaysEmitIntoClient`` (see below). A client that inlines the containing function must emit its own copy of the local functions or closures. This is -important in case it is necessary to change the inlinable function later; -existing clients should not be depending on internal details of the previous -implementation. +important in case it is necessary to change the inlinable function later. Removing the ``@inlinable`` attribute completely---say, to reference private -implementation details that should not be `versioned `---is a -safe change. However, existing clients will of course not be affected by this -change, and any future use of the function must take this into account. +implementation details that should not be ABI-public---is a safe change. +However, existing clients will of course not be affected by this change, and +any future use of the function must take this into account. Although they are not a supported feature for arbitrary libraries at this time, -`transparent`_ functions are implicitly marked ``@inlinable``. +public `transparent`_ functions are implicitly marked ``@inlinable``. .. _transparent: https://github.com/apple/swift/blob/master/docs/TransparentAttr.rst @@ -394,19 +244,15 @@ polar representation:: } and the ``x`` and ``y`` properties have now disappeared. To avoid this, the -bodies of inlinable functions have the following restrictions: +bodies of inlinable functions have the following restrictions (enforced by the +compiler): - They may not define any local types. - They must not reference any ``private`` or ``fileprivate`` entities. - They must not reference any ``internal`` entities except for those that have - been ``versioned ` and those declared ``@inlinable``. See - below for a discussion of versioning internal API. - -- They must not reference any entities from the current module introduced - after the function was made inlinable, except under appropriate availability - guards. + been declared ``@usableFromInline`` or ``@inlinable``. Always Emit Into Client @@ -428,118 +274,64 @@ is a binary-compatible source-breaking change. Default Argument Expressions ---------------------------- -Default argument expressions for functions that are public, versioned, or -inlinable are implicitly ``@_alwaysEmitIntoClient``. They are subject to -similar restrictions: - -- They may not define any local types. - -- They must not reference any non-``public`` entities. - -- They must not reference any entities from the current module introduced - after the default argument was added, except under appropriate availability - guards. - -A default argument implicitly has the same availability as the function it is -attached to. Because default argument expressions can be added and removed, a -client that uses one must always emit its own copy of the implementation. +Default argument expressions for functions that are ABI-public are implicitly +``@_alwaysEmitIntoClient``. They are subject to the same restrictions as +inlinable functions except that they also must not reference any non-``public`` +entities, even if they are ``@usableFromInline`` or ``@inlinable``. This is to +make sure a default argument expression can always be written explicitly by a +caller. Top-Level Variables and Constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Given a versioned module-scope variable declared with ``var``, the following +Given an ABI-public module-scope variable declared with ``var``, the following changes are permitted: - Adding (but not removing) a public setter to a computed variable. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing from a stored variable to a computed variable, or vice versa, as - long as a previously versioned setter is not removed. + long as a previously ABI-public setter is not removed. - As a special case of the above, adding or removing ``lazy`` from a stored property. -- Changing the body of an accessor. +- Changing the body of an accessor, if the property is not marked ``@inlinable`` + (see below). - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing variable. This is effectively the same as modifying the body of a setter. - Changing the initial value of a stored variable. -- Adding or removing ``weak`` from a variable with ``Optional`` type. -- Adding or removing ``unowned`` from a variable. +- Adding or removing ``weak`` to/from a variable with ``Optional`` type. +- Adding or removing ``unowned`` to/from a variable. - Adding or removing ``@NSCopying`` to/from a variable. - -If a public setter is added after the property is first exposed (whether the -property is stored or computed), it must be versioned independently of the -property itself. - -.. admonition:: TODO - - This needs syntax. - -Additionally, for a module-scope constant declared with ``let``, the following +- If the variable is get-only, or if it has a non-ABI-public setter, it may be + replaced by a ``let`` constant. +- Adding a property wrapper to a variable, or changing from one property + wrapper to another, as long as an ABI-public setter or projected value + (``$foo``) is not removed +- Removing a property wrapper from a variable, as long as the property wrapper + didn't have a projected value (``$foo``). + +For an ABI-public module-scope constant declared with ``let``, the following changes are permitted: - Changing the value of the constant. - -It is *not* safe to change a ``let`` constant into a variable or vice versa. -Top-level constants are assumed not to change for the entire lifetime of the -program once they have been initialized. - -.. admonition:: TODO - - We could make it safe to turn a read-only ``var`` into a ``let``, but do we - want to? We would have to come up with syntax for declaring when it - changed, at least. +- Replacing the constant with a variable. Giving Up Flexibility --------------------- -Both top-level constants and variables can be marked ``@inlinableAccess`` to -allow clients to access them more efficiently. This restricts changes a fair -amount: +Top-level computed variables can be marked ``@inlinable`` just like functions. +This restricts changes a fair amount: -- Adding a versioned setter to a computed variable is still permitted. -- Adding or removing a non-public, non-versioned setter is still permitted. -- Changing from stored to computed or vice versa is forbidden, because it would - break existing clients. -- Similarly, adding or removing ``lazy`` is forbidden. +- Adding an ABI-public setter to a computed variable is still permitted. +- Adding or removing a non-ABI-public setter is still permitted. - Changing the body of an accessor is a `binary-compatible source-breaking change`. -- Adding/removing observing accessors is likewise a `binary-compatible - source-breaking change`. -- Changing the initial value of a stored variable is still permitted. -- Changing the value of a constant is a `binary-compatible source-breaking - change`. -- Adding or removing ``weak`` is forbidden. -- Adding or removing ``unowned`` is forbidden. -- Adding or removing ``@NSCopying`` to/from a variable is `binary-compatible - source-breaking change`. - -.. admonition:: TODO - - It Would Be Nice(tm) to allow marking the *getter* of a top-level variable - inlinable while still allowing the setter to change. This would need - syntax, though. Any inlinable accessors must follow the rules for `inlinable functions`_, as described above. -Note that if a constant's initial value expression has any observable side -effects, including the allocation of class instances, it must not be treated -as inlinable. A constant must always behave as if it is initialized exactly -once. - -.. admonition:: TODO - - Is this a condition we can detect at compile-time? Do we have to be - restricted to things that can be lowered to compile-time constants? - -.. admonition:: TODO - - ``@inlinableAccess`` isn't implemented yet, but for computed properties we - already allow putting ``@inlinable`` on the accessors individually. That - doesn't support all the use cases, like promising that a stored property - will remain stored, but it also provides flexibility in only making *one* - accessor inlinable. Is that important? Structs ~~~~~~~ @@ -547,26 +339,43 @@ Structs Swift structs are a little more flexible than their C counterparts. By default, the following changes are permitted: -- Reordering any existing members, including stored properties. +- Reordering any existing members, including stored properties (unless the + struct is marked ``@frozen``; see below). - Adding any new members, including stored properties. -- Changing existing properties from stored to computed or vice versa. +- Changing existing properties from stored to computed or vice versa (unless the + struct is marked ``@frozen``; see below). - As a special case of the above, adding or removing ``lazy`` from a stored property. - Changing the body of any methods, initializers, or accessors. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from - an existing property. This is effectively the same as modifying the body of a - setter. -- Removing any non-public, non-versioned members, including stored properties. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. + an existing property (unless the struct is marked ``@frozen``; see below). + This is effectively the same as modifying the body of a setter. +- Removing any non-ABI-public members, including stored properties. +- Adding a conformance to an ABI-public protocol *that was introduced in the + same release* (see below). +- Adding or removing a conformance to a non-ABI-public protocol. +- Adding ``@dynamicCallable`` to the struct. The important most aspect of a Swift struct is its value semantics, not its -layout. +layout. Note that adding a stored property to a struct is *not* a breaking +change even with Swift's synthesis of memberwise and no-argument initializers; +these initializers are always ``internal`` and thus not exposed to clients +outside the module. It is not safe to add or remove ``mutating`` or ``nonmutating`` from a member -or accessor within a struct. These modifiers are not `versioned attributes -` and as such there is no safety guarantee for a client -deploying against an earlier version of the library. +or accessor within a struct. + +If a conformance is added to a type in version 1.1 of a library, it's important +that it isn't accessed in version 1.0. This means that it is only safe to add +new conformances to ABI-public protocols when the protocol is introduced, and +not after. If the protocol comes from a separate module, there is no safe way +to conform to it. + +.. admonition:: TODO + + Coming up with a way to do this, either with availability annotations for + protocol conformances or a way to emit a fallback copy of the conformance + for clients on older library versions to use, is highly desired. Methods and Initializers @@ -574,28 +383,21 @@ Methods and Initializers For the most part struct methods and initializers are treated exactly like top-level functions. They permit all of the same modifications and can also be -marked ``@inlinable``, with the same restrictions. Inlinable initializers must -always delegate to another initializer or assign an entire value to ``self``, -since new properties may be added between new releases. For the same reason, -initializers declared outside of the struct's module must always delegate to -another initializer or assign to ``self``. +marked ``@inlinable``, with the same restrictions. + +Inlinable initializers must always delegate to another initializer or assign an +entire value to ``self``, since new properties may be added between new +releases. For the same reason, initializers declared outside of the struct's +module must always delegate to another initializer or assign to ``self``. This +is enforced by the compiler. Properties ---------- -Struct properties behave largely the same as top-level bindings. They permit -all of the same modifications, and also allow adding or removing an initial -value entirely. - -Struct properties can also be marked ``@inlinableAccess``, with the same -restrictions as for top-level bindings. An inlinable stored property may not -become computed, but the offset of its storage within the struct is not -necessarily fixed. - -Like top-level constants, it is *not* safe to change a ``let`` property into a -variable or vice versa. Properties declared with ``let`` are assumed not to -change for the entire lifetime of the program once they have been initialized. +Struct properties behave largely the same as top-level variables and constants. +They permit all of the same modifications, and also allow adding or removing an +initial value entirely. Subscripts @@ -605,7 +407,7 @@ Subscripts behave largely the same as properties, except that there are no stored subscripts. This means that the following changes are permitted: - Adding (but not removing) a public setter. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing the body of an accessor. - Changing index parameter internal names (i.e. the names used within the accessor bodies, not the labels that are part of the subscript's full name). @@ -614,64 +416,19 @@ stored subscripts. This means that the following changes are permitted: - Changing or removing a default argument is a `binary-compatible source-breaking change`. -Like properties, subscripts can be marked ``@inlinableAccess``, which makes +Like properties, subscripts can be marked ``@inlinable``, which makes changing the body of an accessor a `binary-compatible source-breaking change`. Any inlinable accessors must follow the rules for `inlinable functions`_, as described above. -New Conformances ----------------- - -If a conformance is added to a type in version 1.1 of a library, it's important -that it isn't accessed in version 1.0. This is implied if the protocol itself -was introduced in version 1.1, but needs special handling if both the protocol -and the type were available earlier. In this case, the conformance *itself* -needs to be labeled as being introduced in version 1.1, so that the compiler -can enforce its safe use. - -.. note:: - - This may feel like a regression from Objective-C, where `duck typing` would - allow a ``Wand`` to be passed as an ``id `` without ill effects. - However, ``Wand`` would still fail a ``-conformsToProtocol:`` check in - version 1.0 of the library, and so whether or not the client code will work - is dependent on what should be implementation details of the library. - -We've considered two possible syntaxes for this:: - - @available(1.1) - extension Wand : MagicType {/*...*/} - -and - -:: - - extension Wand : @available(1.1) MagicType {/*...*/} - -The former requires fewer changes to the language grammar, but the latter could -also be used on the declaration of the type itself (i.e. the ``struct`` -declaration). - -If we went with the former syntax, applying ``@available`` to an extension -would override the default availability of entities declared within the -extension; unlike access control, entities within the extension may freely -declare themselves to be either more or less available than what the extension -provides. - -We could also implement a ``@_alwaysEmitIntoClient`` attribute for conformances. -This introduces its own challenges with runtime uniquing of witness tables now -necessary for conformances. - - Frozen Structs -------------- -To opt out of this flexibility, a struct may be marked ``@frozen``. -This promises that no stored properties will be added to or removed from the -struct, even non-public ones. Additionally, all versioned instance stored -properties in a ``@frozen`` struct are implicitly declared -``@inlinable`` (as described above for top-level variables). In effect: +To opt out of this flexibility, a struct may be marked ``@frozen``. This +promises that no stored properties will be added to or removed from the struct, +even non-ABI-public ones, and allows the compiler to optimize as such. These +stored properties also must not have any observing accessors. In effect: - Reordering stored instance properties (public or non-public) is not permitted. Reordering all other members is still permitted. @@ -681,22 +438,20 @@ properties in a ``@frozen`` struct are implicitly declared vice versa is not permitted. - Similarly, adding or removing ``lazy`` from a stored property is not permitted. -- Changing the body of any *existing* methods, initializers, computed property - accessors, or non-instance stored property accessors is permitted. Changing - the body of a stored instance property observing accessor is permitted if the - property is not `versioned `, and considered a - `binary-compatible source-breaking change` if it is. -- Adding or removing observing accessors from any - `versioned ` stored instance properties (public or - non-public) is not permitted. +- Changing the body of any *existing* methods, initializers, or computed + property accessors is still permitted. +- Adding observing accessors to any stored instance properties (public or + non-public) is not permitted (and is checked by the compiler). - Removing stored instance properties is not permitted. Removing any other - non-public, non-versioned members is still permitted. -- Adding a new protocol conformance is still permitted. -- Removing conformances to non-public protocols is still permitted. + non-ABI-public members is still permitted. +- Adding a new protocol conformance is still permitted, per the usual + restrictions. +- Removing conformances to non-ABI-public protocols is still permitted. +- Adding, changing, or removing property wrappers is not permitted. Additionally, if the type of any stored instance property includes a struct or -enum, that struct or enum must be `versioned `. This includes -generic parameters and members of tuples. +enum, that struct or enum must be ABI-public. This includes generic parameters, +members of tuples, and property wrappers for stored instance properties. .. note:: @@ -707,18 +462,16 @@ generic parameters and members of tuples. While adding or removing stored properties is forbidden, existing properties may still be modified in limited ways: -- An existing non-public, non-versioned property may change its access level to - any other non-public access level. -- A non-versioned ``internal`` property may be versioned (see `Versioning - Internal Declarations`_). -- A versioned ``internal`` property may be made ``public`` (without changing - its version). +- An existing non-ABI-public property may change its access level to any other + non-public access level. +- ``@usableFromInline`` may be added to an ``internal`` property (with the + current availability version, if necessary). +- A ``@usableFromInline`` property may be made ``public``. + +Adding or removing ``@frozen`` from an existing struct is forbidden. An initializer of a frozen struct may be declared ``@inlinable`` even -if it does not delegate to another initializer, as long as the ``@inlinable`` -attribute, or the initializer itself, is not introduced earlier than the -``@frozen`` attribute and the struct has no non-versioned stored -properties. +if it does not delegate to another initializer. A ``@frozen`` struct is *not* guaranteed to use the same layout as a C struct with a similar "shape". If such a struct is necessary, it should be @@ -726,33 +479,13 @@ defined in a C header and imported into Swift. .. note:: - We can add a *different* feature to control layout some day, or something + We may add a *different* feature to control layout some day, or something equivalent, but this feature should not restrict Swift from doing useful - things like minimizing member padding. At the very least, Swift structs - don't guarantee the same tail padding that C structs do. - -.. note:: - - Hypothetically, we could use a different model where a ``@frozen`` - struct only guarantees the "shape" of the struct, so to speak, while - leaving all property accesses to go through function calls. This would - allow stored properties to change their accessors, or (with the Behaviors - proposal) to change a behavior's implementation, or change from one - behavior to another. However, the *most common case* here is probably just - a simple C-like struct that groups together simple values, with only public - stored properties and no observing accessors, and having to opt into direct - access to those properties seems unnecessarily burdensome. The struct is - being declared ``@frozen`` for a reason, after all: it's been - discovered that its use is causing performance issues. - - Consequently, as a first pass we may just require all stored properties in - a ``@frozen`` struct, public or non-public, to have trivial - accessors, i.e. no observing accessors and no behaviors. - -``@frozen`` is a `versioned attribute`. This is so that clients can -deploy against older versions of the library, which may have a different layout -for the struct. (In this case the client must manipulate the struct as if the -``@frozen`` attribute were absent.) + things like minimizing member padding. While the layout of ``@frozen`` + structs is part of the stable ABI on Apple platforms now, it's not + something that can't be revised in the future (with appropriate + compatibility considerations). At the very least, Swift structs don't + guarantee the same tail padding that C structs do. Enums @@ -763,16 +496,17 @@ without breaking binary compatibility. As with structs, this results in a fair amount of indirection when dealing with enum values, in order to potentially accommodate new values. More specifically, the following changes are permitted: -- Adding a new case. -- Reordering existing cases is a `binary-compatible source-breaking change`. In - particular, if an enum is RawRepresentable, changing the raw representations - of cases may break existing clients who use them for serialization. +- Adding a new case (unless the enum is marked ``@frozen``; see below). +- Reordering existing cases is a `binary-compatible source-breaking change` + (unless the struct is marked ``@frozen``; see below). In particular, both + CaseIterable and RawRepresentable default implementations may affect client + behavior. - Adding a raw type to an enum that does not have one. -- Removing a non-public, non-versioned case. - Adding any other members. -- Removing any non-public, non-versioned members. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. +- Removing any non-ABI-public members. +- Adding a new protocol conformance, with the same restrictions as for structs. +- Removing conformances to non-ABI-public protocols. +- Adding ``@dynamicCallable`` to the enum. .. note:: @@ -781,12 +515,6 @@ accommodate new values. More specifically, the following changes are permitted: representation for the value, just as it may discard fields of structs that are provably never accessed. -.. note:: - - Non-public cases in public enums don't exist at the moment, but they *can* - be useful, and they require essentially the same implementation work as - cases added in future versions of a library. - Adding or removing the ``@objc`` attribute from an enum is not permitted; this affects the enum's memory representation and is not backwards-compatible. @@ -809,19 +537,18 @@ members. Frozen Enums ------------ -A library owner may opt out of this flexibility by marking a versioned enum as -``@frozen``. A "frozen" enum may not have any cases with less access than the -enum itself, and may not add new cases in the future. This guarantees to -clients that the enum cases are exhaustive. In particular: +A library owner may opt out of this flexibility by marking an ABI-public enum +as ``@frozen``. A "frozen" enum may not add new cases in the future, +guaranteeing to clients that the current set of enum cases is exhaustive. In +particular: - Adding new cases is not permitted. - Reordering existing cases is not permitted. -- Removing a non-public case is not applicable. - Adding a raw type is still permitted. - Adding any other members is still permitted. -- Removing any non-public, non-versioned members is still permitted. +- Removing any non-ABI-public members is still permitted. - Adding a new protocol conformance is still permitted. -- Removing conformances to non-public protocols is still permitted. +- Removing conformances to non-ABI-public protocols is still permitted. .. note:: @@ -829,24 +556,12 @@ clients that the enum cases are exhaustive. In particular: the library would still have to treat the enum as opaque and would still have to be able to handle unknown cases in their ``switch`` statements. -``@frozen`` is a `versioned attribute`. This is so that clients can deploy -against older versions of the library, which may have non-public cases in the -enum. (In this case the client must manipulate the enum as if the ``@frozen`` -attribute were absent.) All cases that are not versioned become implicitly -versioned with this number. +Adding or removing ``@frozen`` from an existing enum is forbidden. Even for default "non-frozen" enums, adding new cases should not be done lightly. Any clients attempting to do an exhaustive switch over all enum cases will likely not handle new cases well. -.. note:: - - One possibility would be a way to map new cases to older ones on older - clients. This would only be useful for certain kinds of enums, though, and - adds a lot of additional complexity, all of which would be tied up in - versions. Our generalized switch patterns probably make it hard to nail - down the behavior here. - Protocols ~~~~~~~~~ @@ -861,19 +576,12 @@ There are very few safe changes to make to protocols and their members: - Reordering generic requirements is permitted (but not the generic parameters themselves). - The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a function requirement without any additional versioning - information. - -New requirements can be added to a protocol. However, restrictions around -existential types mean that adding new associated types or non-type requirements -involving ``Self`` can break source compatibility. For this reason, the following -are `binary-compatible source-breaking changes `: - -- A new non-type requirement may be added to a protocol, as long as it has an - unconstrained default implementation in a protocol extension of the - protocol itself or some other protocol it refines. -- A new associated type requirement may be added as long as it has a - default. + be added to or removed from a function requirement. +- A new non-type requirement may be added (with the appropriate availability), + as long as it has an unconstrained default implementation. If the requirement + uses ``Self`` and the protocol has no other requirements using ``Self`` and + no associated types, this is a `binary-compatible source-breaking change` due + to restrictions on protocol value types. All other changes to the protocol itself are forbidden, including: @@ -902,19 +610,23 @@ support all of the following changes: - Changing existing properties from stored to computed or vice versa. - As a special case of the above, adding or removing ``lazy`` from a stored property. -- Changing the body of any methods, initializers, or accessors. +- Changing the body of any methods, initializers, accessors, or deinitializers. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing property. This is effectively the same as modifying the body of a setter. -- Removing any non-public, non-versioned members, including stored properties. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. +- Removing any non-ABI-public members, including stored properties. +- Adding a new protocol conformance (subject to the same restrictions as for + structs). +- Removing conformances to non-ABI-public protocols. +- Adding ``@dynamicCallable`` to the class. Omitted from this list is the free addition of new members. Here classes are a little more restrictive than structs; they only allow the following changes: - Adding a new convenience initializer. -- Adding a new designated initializer, if the class is not ``open``. +- Adding a new designated initializer, if the class is not ``open`` and any + ``open`` subclasses that previously inherited convenience initializers + continue to do so. - Adding a deinitializer. - Adding new, non-overriding method, subscript, or property. - Adding a new overriding member, though if the class is ``open`` the type of @@ -923,8 +635,6 @@ little more restrictive than structs; they only allow the following changes: Finally, classes allow the following changes that do not apply to structs: -- A public class may be made ``open`` if it is not already marked ``final``. -- A non-``open`` public class may be marked ``final``. - Removing an explicit deinitializer. (A class with no declared deinitializer effectively has an implicit deinitializer.) - "Moving" a method, subscript, or property up to its superclass. The @@ -935,55 +645,35 @@ Finally, classes allow the following changes that do not apply to structs: removed as long as the generic parameters, formal parameters, and return type *exactly* match the overridden declaration. Any existing callers should automatically use the superclass implementation. -- Within an ``open`` class, any public method, subscript, or property may be - marked ``open`` if it is not already marked ``final``. -- Any method, subscript, or property may be marked ``final`` if it is not - already marked ``open``. +- ``final`` can be added to or removed from any non-ABI-public class, or any + non-ABI-public member of a class. - ``@IBOutlet``, ``@IBAction``, ``@IBInspectable``, and ``@GKInspectable`` may - be added to a member without providing any extra version information. + be added to a member that is already exposed to Objective-C (either explicitly + with ``@objc`` or implicitly through overriding or protocol requirements). Removing any of these is a `binary-compatible source-breaking change` if the member remains ``@objc``, and disallowed if not. -- Likewise, ``@IBDesignable`` may be added to a class without providing any - extra version information. Removing it is considered a `binary-compatible - source-breaking change`. +- ``@IBDesignable`` may be added to a class; removing it is considered a + `binary-compatible source-breaking change`. - Changing a class's superclass ``A`` to another class ``B``, *if* class ``B`` is a subclass of ``A`` *and* class ``B``, along with any superclasses between it and class ``A``, were introduced in the latest version of the library. -.. admonition:: TODO - - This last is very tricky to get right. We've seen it happen a few times in - Apple's SDKs, but at least one of them, `NSCollectionViewItem`_ becoming a - subclass of NSViewController instead of the root class NSObject, doesn't - strictly follow the rules. While NSViewController was introduced in the - same version of the OS, its superclass, NSResponder, was already present. - If a client app was deploying to an earlier version of the OS, would - NSCollectionViewItem be a subclass of NSResponder or not? How would the - compiler be able to enforce this? - -.. admonition:: TODO - - Both ``final`` and ``open`` may be applied to a declaration after it has - been made public. However, these need to be treated as - `versioned attributes `. It's not clear what syntax - should be used for this. - -.. _NSCollectionViewItem: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSCollectionViewItem_Class/index.html - Other than those detailed above, no other changes to a class or its members are permitted. In particular: -- An ``open`` class or member cannot become non-``open``. -- ``final`` may not be removed from a class or its members. (The presence of - ``final`` enables optimization.) -- ``dynamic`` may not be added to *or* removed from any members. Existing - clients would not know to invoke the member dynamically. +- ``open`` cannot be added to or removed from an ABI-public class or member. +- ``final`` may not be added to or removed from an ABI-public class or its + ABI-public members. (The presence of ``final`` enables optimization.) +- ``dynamic`` may not be added to *or* removed from any ABI-public members. + Existing clients would not know to invoke the member dynamically. - A ``final`` override of a member may *not* be removed, even if the type matches exactly; existing clients may be performing a direct call to the implementation instead of using dynamic dispatch. - ``@objc`` and ``@nonobjc`` may not be added to or removed from the class or - any existing members. -- ``@NSManaged`` may not be added to or removed from any existing members. + any existing members, except if the member already was or was not exposed to + Objective-C. +- ``@NSManaged`` may not be added to or removed from any existing + ABI-public members. .. admonition:: TODO @@ -1004,12 +694,6 @@ A new ``required`` initializer may be added to a class only if it is a convenience initializer; that initializer may only call existing ``required`` initializers. An existing initializer may not be marked ``required``. -.. admonition:: TODO - - This implies a different rule for inheriting ``required`` convenience - initializers than non-required convenience initializers, which is not - currently implemented. - All of the modifications permitted for top-level functions are also permitted for class initializers. Convenience initializers may be marked ``@inlinable``, with the same restrictions as top-level functions; designated initializers may @@ -1030,20 +714,13 @@ little. They allow the following changes: - Adding a default argument expression to a parameter. - Changing or removing a default argument is a `binary-compatible source-breaking change`. -- The ``@discardableResult`` and ``@warn_unqualified_access`` attributes may - be added to a method without any additional versioning information. +- Adding or removing the ``@discardableResult`` and ``@warn_unqualified_access`` + attributes. Class and instance methods may be marked ``@inlinable``, with the same restrictions as struct methods. Additionally, only non-overriding ``final`` methods may be marked ``@inlinable``. -.. note:: - - A previous draft of this document allowed non-``final`` methods to be - marked ``@inlinable``, permitting inlining based on speculative - devirtualization. This was removed because of the added complexity for - users. - Properties ---------- @@ -1053,9 +730,9 @@ struct properties, but the potential for overrides complicates things a little. Variable properties (those declared with ``var``) allow the following changes: - Adding (but not removing) a computed setter to a non-``open`` property. -- Adding or removing a non-public, non-versioned setter. +- Adding or removing a non-ABI-public setter. - Changing from a stored property to a computed property, or vice versa, as - long as a previously versioned setter is not removed. + long as a previously ABI-public setter is not removed. - Changing the body of an accessor. - Adding or removing an observing accessor (``willSet`` or ``didSet``) to/from an existing variable. This is effectively the same as modifying the body of a @@ -1063,7 +740,15 @@ Variable properties (those declared with ``var``) allow the following changes: - Adding, removing, or changing the initial value of a stored variable. - Adding or removing ``weak`` from a variable with ``Optional`` type. - Adding or removing ``unowned`` from a variable. -- Adding or removing ``@NSCopying`` to/from a variable. +- Adding or removing ``@NSCopying`` from a variable. +- Adding a property wrapper to a non-``open`` variable, or changing from one + property wrapper to another, as long as an ABI-public setter or projected + value (``$foo``) is not removed. +- Adding a property wrapper to an ``open`` variable, or changing from one + property wrapper to another, as long as an ABI-public setter or projected + value (``$foo``) is not added or removed. +- Removing a property wrapper from a variable, as long as the property wrapper + didn't have a projected value (``$foo``). Adding a public setter to an ``open`` property is a `binary-compatible source-breaking change`; any existing overrides will not @@ -1072,9 +757,8 @@ know what to do with the setter and will likely not behave correctly. Constant properties (those declared with ``let``) still permit changing their value, as well as adding or removing an initial value entirely. -Non-overriding ``final`` variable and constant properties (on both instances -and classes) may be marked ``@inlinableAccess``. This behaves as described for -struct properties. +Non-overriding ``final`` computed properties (on both instances and classes) +may be marked ``@inlinable``. This behaves as described for struct properties. Subscripts @@ -1085,7 +769,7 @@ counterparts with a few small changes: - Adding (but not removing) a public setter to a non-``open`` subscript is permitted. -- Adding or removing a non-public, non-versioned setter is permitted. +- Adding or removing a non-ABI-public setter is permitted. - Changing the body of an accessor is permitted. - Changing index parameter internal names is permitted. - Reordering generic requirements (but not the generic parameters themselves) @@ -1098,23 +782,10 @@ Adding a public setter to an ``open`` subscript is a `binary-compatible source-breaking change`; any existing overrides will not know what to do with the setter and will likely not behave correctly. -Non-overriding ``final`` class subscripts may be marked ``@inlinableAccess``, +Non-overriding ``final`` class subscripts may be marked ``@inlinable``, which behaves as described for struct subscripts. -Possible Restrictions on Classes --------------------------------- - -In addition to ``final``, it may be useful to restrict the stored properties of -a class instance, like `Frozen Structs`_. However, there are open -questions about how this would actually work, and the compiler still wouldn't -be able to make much use of the information, because classes from other -libraries must almost always be allocated on the heap. - -The design of this annotation is not covered by this document. As a purely -additive feature, it can be added to the model at any time. - - Extensions ~~~~~~~~~~ @@ -1127,18 +798,19 @@ The following changes are permitted: as both extensions have the exact same constraints. - Adding any new member. - Reordering members. -- Removing any non-public, non-versioned member. +- Removing any non-ABI-public member. - Changing the body of any methods, initializers, or accessors. Additionally, non-protocol extensions allow a few additional changes: - Moving a member from an unconstrained extension to the declaration of the base type, provided that the declaration is in the same module. The reverse - is permitted for all members except stored properties, although note that - moving all initializers out of a type declaration may cause a new one to be - implicitly synthesized. -- Adding a new protocol conformance (with proper availability annotations). -- Removing conformances to non-public protocols. + is permitted for all members that would be valid to declare in an extension, + although note that moving all initializers out of a type declaration may + cause a new one to be implicitly synthesized. +- Adding a new protocol conformance (subject to the same restrictions discussed + for structs). +- Removing conformances to non-ABI-public protocols. .. note:: @@ -1159,8 +831,6 @@ existing operators are not changed at all except for the following: Any other change counts as a `binary-compatible source-breaking change`. -Operator and precedence group declarations are not versioned. - Typealiases ~~~~~~~~~~~ @@ -1173,115 +843,39 @@ clients, they cannot recompile their code and get correct behavior. Top-level typealiases only exist at compile-time, so changing the underlying type of one is a `binary-compatible source-breaking change`. However, if the -typealias is *used* in the type of any `versioned entity` in a library, it +typealias is *used* in the type of any ABI-public declaration in a library, it may be an actual breaking change and would not be permitted. It is always permitted to change the *use* of a public typealias to its underlying type, and vice versa, at any location in the program. -Typealiases are `versioned ` despite being compile-time +Typealiases require availability annotations despite being compile-time constructs in order to verify the availability of their underlying types. -A Unifying Theme -~~~~~~~~~~~~~~~~ - -So far this document has talked about ways to give up flexibility for several -different kinds of declarations: namely ``@inlinable`` for functions, and -``@frozen`` for enums and structs. Each of these has a different set of -constraints it enforces on the library author and promises it makes to clients. -However, they follow a common theme of giving up the flexibility of future -changes in exchange for improved performance and perhaps some semantic -guarantees. Therefore, these attributes are informally referred to as -"fragility attributes". - +``@usableFromInline`` +===================== -Versioning Internal Declarations -================================ - -The initial discussion on versioning focused on public APIs, making sure -that a client knows what features they can use when a specific version of a -library is present. Inlinable functions have much the same constraints, except -the inlinable function is the client and the entities being used may not be -public. - -Adding a versioning annotation to an ``internal`` entity promises that the -entity will be available at link time in the containing module's binary. This -makes it safe to refer to such an entity from an inlinable function. If the -entity is ever made ``public`` or ``open``, its availability should not be -changed; not only is it safe for new clients to rely on it, but *existing* -clients require its presence as well. +Adding ``@usableFromInline`` to an ``internal`` entity promises that the entity +will be available at link time in the containing module's binary. This makes it +safe to refer to such an entity from an inlinable function or in the stored +properties of a frozen struct. ``@usableFromInline`` declarations shipped as +part of an OS should have availability just like ``public`` declarations; if +the entity is ever made ``public`` or ``open``, its availability should not be +changed. .. note:: - Why isn't this a special form of ``public``? Because we don't want it to - imply everything that ``public`` does, such as requiring overrides to be - ``public``. + Why isn't ``@usableFromInline`` a special form of ``public``? Because we + don't want it to imply everything that ``public`` does, such as requiring + overrides to be ``public``. -In libraries without binary compatibility concerns, the equivalent annotation -is ``@usableFromInline``, since inlinable functions are the only way that a -non-public entity can be referenced from outside of a module. +Because a ``@usableFromInline`` class member may eventually be made ``open``, +the compiler must assume that new overrides may eventually appear from outside +the module if the class is marked ``open`` unless the member is marked +``final``. -Because a versioned class member may eventually be made ``open``, it must be -assumed that new overrides may eventually appear from outside the module if the -class is marked ``open`` unless the member is marked ``final``. - -Non-public conformances are never considered versioned, even if both the -conforming type and the protocol are versioned. A conformance is considered -public if and only if both the conforming type and protocol are public. - -Entities declared ``private`` or ``fileprivate`` may not be versioned; the -mangled name of such an entity includes an identifier based on the containing -file, which means moving the declaration to another file changes the entity's -mangled name. This implies that a client would not be able to find the entity -at run time if the source code is reorganized, which is unacceptable. - -.. note:: - - There are ways around this limitation, the most simple being that versioned - ``private`` entities are subject to the same cross-file redeclaration rules - as ``internal`` entities. However, this is a purely additive feature, so to - keep things simple we'll stick with the basics. - -We could do away with the entire feature if we restricted inlinable functions -and frozen structs to only refer to public entities. However, this -removes one of the primary reasons to make something inlinable: to allow -efficient access to a type while still protecting its invariants. - - -"Backdating" -============ - -*Backdating* refers to releasing a new version of a library that contains -changes, but pretending those changes were made in a previous version of the -library. For example, you might want to release version 1.2 of the "Magician" -library, but pretend that the "SpellIncantation" struct was frozen -since its introduction in version 1.0. - -**This is not safe.** - -Backdating the availability a versioned entity that was previously non-public -is clearly not safe: older versions of the library will not expose the entity -as part of their ABI. What may be less obvious is that the fragility attributes -likewise are not safe to backdate, even if you know the attributes could have -been added in the past. To give one example, the presence of ``@frozen`` may -affect the layout and calling conventions for an enum or struct. - -.. note:: - - If we add an "SPI" feature, such that the use of specific public entities - is limited to certain clients, it *will* be safe to change the set of - clients, or remove the restriction altogether. In fact, in such cases the - library author is *required* to *not* change the availability info that was - originally presented for the limited set of clients, since as mentioned - above this may affect how those existing clients use the entities declared - in the library. - -The one exception is ``@inlinable``, which does not change how a function is -called or otherwise used at the ABI level. If the implementation being provided -is compatible with a previous version of a library, and the function was -present and public (or `versioned `) there, then the library -author may choose to backdate the ``@inlinable`` annotation. +For more information, see `SE-0193 `_. Optimization @@ -1299,363 +893,27 @@ several ways. For example: - A struct may have additional members in the future, so client code must not assume it fits in any fixed-sized allocation. -In order to make sure client code doesn't make unsafe assumptions, queries -about properties that may change between library versions must be parameterized -with the `availability context` that is using the entity. An availability -context is a set of minimum platform and library versions that can be assumed -present for code executing within the context. (See `Declaring Library Version -Dependencies`_.) This allows the compiler to answer the question, "Given what I -know about where this code will be executed, what can I assume about a -particular entity being used?". - If the entity is declared within the same module as the code that's using it, then the code is permitted to know all the details of how the entity is declared. After all, if the entity is changed, the code that's using it will be -recompiled. +recompiled. However, if the entity is declared in another module, then the code +using it must be more conservative, and will therefore receive more +conservative answers to its queries. (For example, a stored property may be +treated as computed.) -However, if the entity is declared in another module, then the code using it -must be more conservative, and will therefore receive more conservative answers -to its queries. For example, a stored property may report itself as computed. - -The presence of versioned fragility attributes makes the situation more -complicated. Within a client function that requires version 1.5 of a particular -library, the compiler should be able to take advantage of any fragility -information (and performance assertions) introduced prior to version 1.5. - - -Inlinable Code -~~~~~~~~~~~~~~ - -By default, the availability context for a library always includes the latest -version of the library itself, since that code is always distributed as a unit. -However, this is not true for functions that have been marked inlinable (see -`Inlinable Functions`_ above). Inlinable code must be treated as if it is -outside the current module, since once it's inlined it will be. - -For inlinable code, the availability context is exactly the same as the -equivalent non-inlinable code except that the assumed version of the -containing library is the version attached to the ``@inlinable`` attribute, or -the version of the library in which the entity was introduced, and any `library -version dependencies <#declaring-library-version-dependencies>`_ or minimum -deployment target must be specified explicitly using ``@available``. Code -within this context must be treated as if the containing library were just a -normal dependency. - -A versioned inlinable function still has an exported symbol in the library -binary, which may be used when the function is referenced from a client rather -than called. This version of the function is not subject to the same -restrictions as the version that may be inlined, and so it may be desirable to -compile a function twice: once for inlining, once for maximum performance. - -If the body of an inlinable function is used in any way by a client module -(say, to determine that it does not read any global variables), that module -must take care to emit and use its own copy of the function. This is because -analysis of the function body may not apply to the version of the function -currently in the library. - - -Local Availability Contexts -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Swift availability contexts aren't just at the declaration level; they also -cover specific regions of code inside function bodies as well. These "local" -constructs are formed using the ``#available`` construct, which performs a -dynamic check. - -In theory, it would be legal to allow code dominated by a ``#available`` check -to take advantage of additional fragility information introduced by the more -restrictive dependencies that were checked for. However, this is an additional -optimization that may be complicated to implement (and even to represent -properly in SIL), and so it is not a first priority. - - -Other Promises About Types -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Advanced users may want to promise more specific things about various types. -These are similar to the internal ``effects`` attribute we have for functions, -except that they can be enforced by the compiler. - -- ``trivial``: Promises that assignment just requires a fixed-size bit-for-bit - copy without any indirection or reference-counting operations. - -- ``maximumFootprint(sizeInBits: N, alignmentInBits: A)``: Promises that the - type's size and required alignment are at most N bits and A bits, - respectively. (Both may be smaller.) - -- ``fixedSize``: Promises that the type has *some* size known at compile-time, - allowing optimizations like promoting allocations to the stack. Only applies - to fixed-contents structs and closed enums, which can already infer this - information; the explicit annotation allows it to be enforced. - -Collectively these features are known as "performance assertions", to -underscore the fact that they do not affect how a type is used at the source -level, but do allow for additional optimizations. We may also expose some of -these qualities to static or dynamic queries for performance-sensitive code. - -.. note:: Previous revisions of this document contained a ``noPayload`` - assertion for enums. However, this doesn't actually offer any additional - optimization opportunities over combining ``trivial`` with - ``maximumFootprint``, and the latter is more flexible. - -.. note:: None of these names / spellings are final. The name "trivial" comes - from C++, though Swift's trivial is closer to C++'s "`trivially - copyable`__". - -All of these features need to be versioned, just like the more semantic -fragility attributes above. The exact spelling is not proposed by this document. - -__ http://en.cppreference.com/w/cpp/types/is_trivially_copyable - - -Resilience Domains -================== - -As described in the `Introduction`_, the features and considerations discussed -in this document do not apply to libraries distributed in a bundle with their -clients. In this case, a client can rely on all the current implementation -details of its libraries when compiling, since the same version of the library -is guaranteed to be present at run time. This allows more optimization than -would otherwise be possible. - -In some cases, a collection of libraries may be built and delivered together, -even though their clients may be packaged separately. (For example, the ICU -project is usually built into several library binaries, but these libraries are -always distributed together.) While the *clients* cannot rely on a particular -version of any library being present, the various libraries in the collection -should be able to take advantage of the implementations of their dependencies -also in the collection---that is, it should treat all entities as if marked -with the appropriate fragility attributes. Modules in this sort of collection -are said to be in the same *resilience domain.* - -Exactly how resilience domains are specified is not covered by this document, -and indeed they are an additive feature. One possibility is that a library's -resilience domain defaults to the name of the module, but can be overridden. If -a client has the same resilience domain name as a library it is using, it may -assume that version of the library will be present at run time. - - -Deployments -~~~~~~~~~~~ - -Related to the concept of a resilience domain is a *deployment.* While a -resilience domain allows related libraries to be compiled more efficiently, -a deployment groups related libraries together to present semantic version -information to clients. The simplest example of this might be an OS release: -OS X 10.10.0 contains Foundation version 1151.16 and AppKit version 1343. A -deployment thus acts as a "virtual dependency": clients that depend on -OS X 10.10 can rely on the presence of both of the library versions above. - -The use of deployments allows clients to only have to think about aggregate -dependencies, instead of listing every library they might depend on. It also -allows library authors to build `many versions of a library`__ within a larger -release cycle, as well as allowing a vendor to bundle together many libraries -with uncoordinated release schedules and release them as a logical unit. - -__ https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/Foundation_Framework_Version_Numbers - -There are lots of details to figure out here, including how to distribute this -information. In particular, just like libraries publish the history of their -own APIs, a deployment must publish the history of their included library -versions, i.e. not just that OS X 10.10 contains Foundation 1151.16 and AppKit -1343, but also that OS X 10.9 contains Foundation 1056 and AppKit 1265, and that -OS X 10.8 contains Foundation 945.0 and AppKit 1187, and so on, back to the -earliest version of the deployment that is supported. - - - -Checking Binary Compatibility -============================= - -With this many manual controls, it's important that library owners be able to -check their work. Therefore, we intend to build a tool that can compare two -versions of a library's public interface, and present any suspect differences -for verification. Important cases include but are not limited to: - -- Removal of versioned entities. - -- Incompatible modifications to versioned entities, such as added protocol - conformances lacking versioning information. - -- Unsafe `backdating <#backdating>`_. - -- Unsafe modifications to entities marked with fragility attributes, such as - adding a stored property to a ``@frozen`` struct. - -Wherever possible, this tool should also check for `binary-compatible -source-breaking changes `, such as -changing a default argument from ``false`` to ``true``. - - -Automatic Versioning -~~~~~~~~~~~~~~~~~~~~ - -A possible extension of this "checker" would be a tool that *automatically* -generates versioning information for entities in a library, given the previous -public interface of the library. This would remove the need for versions on any -of the fragility attributes, and declaring versioned API would be as simple as -marking an entity ``public``. Obviously this would also remove the possibility -of human error in managing library versions. - -However, making this tool has a number of additional difficulties beyond the -simple checker tool: - -- The tool must be able to read past library interface formats. This is true - for a validation tool as well, but the cost of failure is much higher. - Similarly, the past version of a library *must* be available to correctly - compile a new version. - -- Because the information goes into a library's public interface, the - versioning tool must either be part of the compilation process, modify the - interface generated by compilation, or produce a sidecar file that can be - loaded when compiling the client. In any case, it must *produce* information - in addition to *consuming* it. - -- Occasionally a library owner may want to override the inferred versions. This - can be accomplished by providing explicit versioning information, as - described above. - -- Bugs in the tool manifest as bugs in client programs. - -Because this tool would require a fair amount of additional work, it is not -part of this initial model. It is something we may decide to add in the future. - - -Open Issues -=========== - -There are still a number of known issues with the model described in this -document. We should endeavor to account for each of them, and if we can't come -up with a satisfactory implementation we should at least make sure that they -will not turn into pitfalls for library or client developers. - - -Subclass and base both conform to protocol -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - // Library, version 1 - class Elf {} - protocol Summonable {} - -:: - - // Client, version 1 - class ShoemakingElf : Elf, Summonable {} - -:: - - // Library, version 2 - @available(2.0) - extension Elf : Summonable {} - -Now ``ShoemakingElf`` conforms to ``Summonable`` in two different ways, which -may be incompatible (especially if ``Summonable`` had associated types or -requirements involving ``Self``). - -Additionally, the client can't even remove ``ShoemakingElf``'s conformance to -``Summonable``, because it may itself be a library with other code depending on -it. We could fix that with an annotation to explicitly inherent the conformance -of ``Summonable`` from the base class, but even that may not be possible if -there are incompatible associated types involved (because changing a member -typealias is not a safe change). - -One solution is to disallow adding a conformance for an existing protocol to an -``open`` class. - - -Recompiling changes a protocol's implementation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - // Library, version 1 - protocol MagicType {} - protocol Wearable {} - func use(_ item: T) {} - -:: - - // Client, version 1 - struct Amulet : MagicType, Wearable {} - use(Amulet()) - -:: - - // Library, version 2 - protocol MagicType { - @available(2.0) - func equip() { print("Equipped.") } - } - - extension Wearable where Self: MagicType { - @available(2.0) - func equip() { print("You put it on.") } - } - - func use(_ item: T) { item.equip() } - -Before the client is recompiled, the implementation of ``equip()`` used for -``Amulet`` instances can only be the default implementation, i.e. the one that -prints "Equipped". However, recompiling the client will result in the -constrained implementation being considered a "better" match for the protocol -requirement, thus changing the behavior of the program. - -This should never change the *meaning* of a program, since the default -implementation for a newly-added requirement should always be *correct.* -However, it may have significantly different performance characteristics or -side effects that would make the difference in behavior a surprise. - -This is similar to adding a new overload to an existing set of functions, which -can also change the meaning of client code just by recompiling. However, the -difference here is that the before-recompilation behavior was never requested -or acknowledged by the client; it's just the best the library can do. - -A possible solution here is to require the client to acknowledge the added -requirement in some way when it is recompiled. - -(We do not want to perform overload resolution at run time to find the best -possible default implementation for a given type.) +As a special case, inlinable code must be treated as if it is outside the +current module, since once it's inlined it will be. Summary ======= -When possible, Swift gives library authors freedom to evolve their code -without breaking binary compatibility. This has implications for both the -semantics and performance of client code, and so library owners also have tools -to waive the ability to make certain future changes. The language guarantees -that client code will never accidentally introduce implicit dependencies on -specific versions of libraries. - - -Related Proposals -================= - -The following proposals (some currently in the process, some planned) will -affect the model described in this document, or concern the parts of this -document that affect language semantics: - -- Non-exhaustive enums (`SE-0192 `_) -- Inlineable functions (`SE-0193 `_) -- Frozen structs and enums (`SE-0260 `_) -- (draft) `Overridable methods in extensions`_ -- (planned) Restricting retroactive modeling (protocol conformances for types you don't own) -- (planned) `Generalized existentials (values of protocol type) `_ -- (planned) Removing the "constant" guarantee for 'let' across module boundaries -- (future) Performance annotations for types -- (future) Attributes for stored property accessors -- (future) Stored properties in extensions - -.. _Overridable methods in extensions: https://github.com/jrose-apple/swift-evolution/blob/overridable-members-in-extensions/proposals/nnnn-overridable-members-in-extensions.md -.. _Generics: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials -.. _SE0192: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md -.. _SE0193: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md -.. _SE0260: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md - -This does not mean all of these proposals need to be accepted, only that their -acceptance or rejection will affect this document. +When possible, Swift gives library authors freedom to evolve their code without +breaking binary compatibility. This has implications for both the semantics and +performance of client code, and so library owners also have tools to waive the +ability to make certain future changes. When shipping libraries as part of the +OS, the availability model guarantees that client code will never accidentally +introduce implicit dependencies on specific versions of libraries. Glossary @@ -1668,18 +926,17 @@ Glossary including things like symbol names, calling conventions, and type layout information. Stands for "Application Binary Interface". + ABI-public + Describes entities that are part of a library's `ABI`. Marked ``public``, + ``open``, ``@usableFromInline``, or ``@inlinable`` in Swift. See + `SE-0193 `_ for more information. + API An `entity` in a library that a `client` may use, or the collection of all such entities in a library. (If contrasting with `SPI`, only those entities that are available to arbitrary clients.) Marked ``public`` or ``open`` in Swift. Stands for "Application Programming Interface". - availability context - The collection of library and platform versions that can be assumed, at - minimum, to be present in a certain block of code. Availability contexts - are always properly nested, and the global availability context includes - the module's minimum deployment target and minimum dependency versions. - backwards-compatible A modification to an API that does not break existing clients. May also describe the API in question. @@ -1718,21 +975,10 @@ Glossary An API that is designed to handle future clients, perhaps allowing certain changes to be made without changing the ABI. - fragility attribute - See `A Unifying Theme`_. - module The primary unit of code sharing in Swift. Code in a module is always built together, though it may be spread across several source files. - performance assertion - See `Other Promises About Types`_. - - resilience domain - A grouping for code that will always be recompiled and distributed - together, and can thus take advantage of details about a type - even if it changes in the future. - SPI A subset of `API` that is only available to certain clients. Stands for "System Programming Interface". @@ -1741,9 +987,3 @@ Glossary In this document, a collection of code in a single Swift module that is built together; a "compilation unit". Roughly equivalent to a target in Xcode. - - versioned entity - See `Publishing Versioned API`_. - - versioned attribute - See `Publishing Versioned API`_. diff --git a/docs/LibraryEvolutionManifesto.md b/docs/LibraryEvolutionManifesto.md new file mode 100644 index 0000000000000..e2bad23a8a408 --- /dev/null +++ b/docs/LibraryEvolutionManifesto.md @@ -0,0 +1,215 @@ +# Introduction + +This document is intended to discuss current issues in and possible future expansions of Swift's model for ABI-stable _library evolution,_ described in [LibraryEvolution.rst][]. Like other "manifesto" documents in the Swift community, it sets out related tasks in service of a general goal, though this is still an exploration of the space more than a to-do list. + +[LibraryEvolution.rst]: ./LibraryEvolution.rst + + +# Open Issues + + +## Recompiling changes a protocol's implementation + +```swift +// Library, version 1 +protocol MagicType {} +protocol Wearable {} +func use(_ item: T) {} +``` + +```swift +// Client, version 1 +struct Amulet : MagicType, Wearable {} +use(Amulet()) +``` + +```swift +// Library, version 2 +protocol MagicType { + @available(dishwasherOS 2.0, *) + func equip() +} +extension MagicType { + @available(dishwasherOS 2.0, *) + func equip() { print("Equipped.") } +} + +protocol Wearable {} +extension Wearable where Self: MagicType { + @available(dishwasherOS 2.0, *) + func equip() { print("You put it on.") } +} + +func use(_ item: T) { item.equip() } +``` + +Let's say we're running dishwasherOS 2.0. Before the client is recompiled, the implementation of `equip()` used for `Amulet` instances can only be the default implementation, i.e. the one that prints "Equipped". However, recompiling the client will result in the constrained implementation being considered a "better" match for the protocol requirement, thus changing the behavior of the program. + +This should never change the *meaning* of a program, since the default implementation for a newly-added requirement should always be *correct.* However, it may have significantly different performance characteristics or side effects that would make the difference in behavior a surprise. + +This is similar to adding a new overload to an existing set of functions, which can also change the meaning of client code just by recompiling. However, the difference here is that the before-recompilation behavior was never requested or acknowledged by the client; it's just the best the library can do. + +A possible solution here is to require the client to acknowledge the added requirement in some way when it is recompiled. + +(We do not want to perform overload resolution at run time to find the best possible default implementation for a given type.) + + +# Evolving Attributes + +A number of annotations (attributes and modifiers) would benefit from being able to change between releases of a library. For example: + +- Making a `public` class `open`. +- Making a `public` class `final`. +- Making a struct or enum `@frozen`. +- Making a function `@inlinable`. + +However, all of these changes have to be associated with a specific release to be correct: + +- The newly-`open` class can't be subclassed if the client's deployment target is too old. +- Convenience initializers on a newly-`final` class can't satisfy protocol requirements if the client's deployment target is too old. +- A newly-`@frozen` struct may have had more private stored properties in the past. +- A newly-`@inlinable` function's body may not work correctly with older versions of the library. + +While solutions can be tailored to each of these problems, they have a common need to record the OS version when an annotation is added or changed. Without that, we're locked in to the way a declaration is originally published, or having to take correctness on faith. + +## Limitations + +For any annotation that affects the calling conventions of a function specifically, it becomes tricky to add or remove that annotation without breaking ABI. Consider the example of a struct becoming `@frozen`. Normally, functions that pass or return non-frozen structs have to do so indirectly (i.e. through a pointer), whereas frozen structs that are "small enough" (according to the ABI) can be passed in registers instead. So we get tables like this: + +Frozen struct | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | direct | direct +in a client with library evolution | direct | direct +in a client without library evolution (like an app) | direct | direct + +Non-frozen struct | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | indirect (no promises) | direct +in a client with library evolution | indirect (knows nothing) | indirect (knows nothing) +in a client without library evolution (like an app) | indirect (knows nothing) | indirect (knows nothing) + +However, for any library *or client* with library evolution enabled, the ABI columns must not change. That implies that marking a struct as frozen *after its initial release* is different from marking the struct frozen from the start. + +Frozen struct that used to be non-frozen | used in a module's ABI | used in non-public ways +---|---|--- +in its own module | indirect (ABI compat) | direct +in a client with library evolution | indirect (ABI compat) | direct +in a client without library evolution (like an app) | direct | direct + +There are a few ways to improve upon this, the most obvious being that any APIs with newer availability than when the struct became frozen can pass the struct around directly. It's worth noting that a library's minimum deployment target *cannot* be used for this purpose, since changing a minimum deployment target is not supposed to change a library's ABI. + +This isn't a *terrible* performance cost; remember that all C++ methods take `this` indirectly, and clients with new enough deployment targets can still *manipulate* the values directly even if the calling convention can't change. But it's something to think about for every annotation that can change after a type is introduced. + + +# Additional Annotations + +## Layout Constraints + +Developers with performance-sensitive use cases may want to promise more specific things about various types and have the compiler enforce them. + +- "trivial": Promises that assignment just requires a fixed-size bit-for-bit copy without any indirection or reference-counting operations. This may allow more efficient copying and destruction of values. + +- "maximum size N/alignment A": Promises that the type's size and required alignment are at most N bits and A bits, respectively. (Both may be smaller.) This would make it easier for the compiler to work with values on the stack. + +(Neither of these names / spellings are final. The name "trivial" comes from C++, though Swift's trivial is closer to C++'s "[trivially copyable][]".) + +Both of these annotations are things the compiler already knows when a type is declared, but writing them explicitly (a) allows the developer to check that they've made something properly optimizable, and (b) promises not to change that behavior between releases, even if, say, stored properties are added to or removed from a struct. + +[trivially copyable]: http://en.cppreference.com/w/cpp/types/is_trivially_copyable + + +## Frozen classes + +The compiler actually has basic support for frozen classes, which allow stored properties to be accessed directly by offset from the class reference. There's no real reason why this can't be supported more generally, but confusion around *what's* being frozen and the rarity of wanting to make this promise anyway kept it out of [SE-0260][]. + +[SE-0260]: https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md + + +# Generalized Availability Model + +This section is focused on various ways to extend the OS-version-based availability model. + +## Per-library availability + +As a placeholder, imagine replacing OS versions with library versions in the various parts of Swift's availability checking syntax: + +```swift +// Client code +@available(Magician 1.5) +class CrystalBallView : MagicView { /*...*/ } + +func scareMySiblings() { + if #available(Magician 1.2) { + summonDemons() + } else { + print("BOO!!") + } +} +``` + +This sort of version checking is important for *backward*-deployment, where the developer has the interface available for a new version of a library (Magician 1.5), but might end up running the client against an old version (Magician 1.0). It's not interesting for *forward*-deployment, however—that's all up to the library to preserve ABI compatibility. + +Possible implementations for version checking include generating a hidden symbol into a library, or putting the version number in some kind of metadata, like the Info.plist in a framework bundle on Apple platforms. + + +### Publishing API with availability + +A library's API is already marked with the `public` modifier, but if a client wants to work with multiple releases of the library, the API needs availability information as well. Declaring availability on an entity using the *current* library's name specifies the oldest version in which that entity can be used. + +- Classes, structs, enums, and protocols may all have availability. +- Methods, properties, subscripts, and initializers may all have availability. +- Top-level functions, variables, and constants may have availability. +- Typealiases are treated as having availability for the purpose of availability checking, even though they have no run-time presence. + +In a versioned library, any top-level public entity from the list above may not be made ABI-public (`public`, `open`, `@usableFromInline`, or `@inlinable`) without availability. A public entity declared within a type (or an extension of a type) will default to having the same availability as the type. + +Code within a library may generally use all other entities declared within the library (barring their own availability checks), since the entire library is shipped as a unit. That is, even if a particular API was introduced in v1.0, its (non-public) implementation may refer to APIs introduced in later versions. Inlinable functions are the exception to this, since inlined code ends up outside the original module. + + +### Declaring library version dependencies + +Swift's current OS-based availability model includes the notion of a _minimum deployment target,_ the version of an OS that must be present for the program being compiled to run at all. For example, a program compiled with a minimum deployment target of iOS 9.2 will not launch on iOS 9.0. + +The generalized model suggests being able to make similar guarantees for individual libraries. For example, a client program may depend on version 1.1 of the "Magician" library; trying to run using version 1.0 will result in errors. By declaring this at compile-time, the client code can omit `@available` and `#available` checks that are satisfied by the minimum library version. + + +## Resilience domains + +In general, the features around library evolution for ABI stability's sake do not apply to clients that bundle their dependencies with them (such as an iOS app embedding third-party frameworks). In this case, a client can rely on all the current implementation details of its libraries when compiling, since the same version of the library is guaranteed to be present at run time. This allows more optimization than would otherwise be possible. + +In some cases, a collection of libraries may be built and delivered together, even though their clients may be packaged separately. (For example, the ICU project is usually built into several library binaries, but these libraries are always distributed together.) While the *clients* cannot rely on a particular version of any library being present, the various libraries in the collection should be able to take advantage of the implementations of their dependencies also in the collection---that is, it should treat types as frozen (except where it would affect public-facing ABI). We've used the term _resilience domain_ to describe modules in this sort of collection. + +There's no design yet for how resilience domains should be specified. In today's compiler, every library with library evolution enabled is in its own resilience domain, and every library that *doesn't* have library evolution enabled is in the "app" resilience domain. + +### Deployments + +Related to the concept of a resilience domain is a _deployment._ While a resilience domain allows related libraries to be compiled more efficiently, a deployment groups related libraries together to present semantic version information to clients. The simplest example of this might be an OS release: OS X 10.10.0 contains Foundation version 1151.16 and AppKit version 1343. A deployment thus acts as a "virtual dependency": clients that depend on OS X 10.10 can rely on the presence of both of the library versions above. + +The use of deployments allows clients to only have to think about aggregate dependencies, instead of listing every library they might depend on. It also allows library authors to build [many versions of a library][Foundation version numbers] within a larger release cycle, as well as allowing a vendor to bundle together many libraries with uncoordinated release schedules and release them as a logical unit. + +[Foundation version numbers]: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/Foundation_Framework_Version_Numbers + +There are lots of details to figure out here, including how to distribute this information. In particular, just like libraries publish the history of their own APIs, a deployment must publish the history of their included library versions, i.e. not just that OS X 10.10 contains Foundation 1151.16 and AppKit 1343, but also that OS X 10.9 contains Foundation 1056 and AppKit 1265, and that OS X 10.8 contains Foundation 945.0 and AppKit 1187, and so on, back to the earliest version of the deployment that is supported. + +Obviously, formalizing a model here is probably most useful for people distributing ABI-stable Swift libraries as part of an OS, i.e. Apple. + + +# Automated Tooling + +## ABI Checker + +The Swift repository has a basic ABI checker in the form of swift-api-digester. This tool looks at two versions of a library and determines if there are any changes which are known to be unsafe (say, changing the type of a function parameter). It would be nice™ to integrate this into SwiftPM and Xcode in some way. + +## Automatic Versioning + +A possible extension of the ABI checker would be a tool that *automatically* generates versioning information for entities in a library, given the previous public interface of the library. This would remove the need for versions on annotations, and declaring new public API would be as simple as marking an entity `public`. Obviously this would also remove the possibility of human error in managing library versions. + +However, making this tool has a number of additional difficulties beyond the simple checker tool: + +- The tool must be able to read past library interface formats. This is true for a validation tool as well, but the cost of failure is much higher. Similarly, the past version of a library *must* be available to correctly compile a new version. + +- Because the information goes into a library's public interface, the versioning tool must either be part of the compilation process, modify the interface generated by compilation, or produce a sidecar file that can be loaded when compiling the client. In any case, it must *produce* information in addition to *consuming* it. + +- Occasionally a library owner may want to override the inferred versions. This can be accomplished by providing explicit versioning information, as described above. + +- Bugs in the tool manifest as bugs in client programs. diff --git a/docs/OptimizationTips.rst b/docs/OptimizationTips.rst index 77e31b909175b..18ed2644e6707 100644 --- a/docs/OptimizationTips.rst +++ b/docs/OptimizationTips.rst @@ -82,7 +82,7 @@ in the following code snippet, ``a.aProperty``, ``a.doSomething()`` and dynamic doSomethingElse() { ... } } - class B : A { + class B: A { override var aProperty { get { ... } set { ... } @@ -155,7 +155,7 @@ assuming ``E``, ``F`` do not have any overriding declarations in the same file: } class F { - fileprivate var myPrivateVar : Int + fileprivate var myPrivateVar: Int } func usingE(_ e: E) { @@ -193,11 +193,11 @@ Array. // Don't use a class here. struct PhonebookEntry { - var name : String - var number : [Int] + var name: String + var number: [Int] } - var a : [PhonebookEntry] + var a: [PhonebookEntry] Keep in mind that there is a trade-off between using large value types and using reference types. In certain cases, the overhead of copying and moving around @@ -277,9 +277,9 @@ safe. :: - a : [Int] - b : [Int] - c : [Int] + a: [Int] + b: [Int] + c: [Int] // Precondition: for all a[i], b[i]: a[i] + b[i] does not overflow! for i in 0 ... n { @@ -319,7 +319,7 @@ generics. Some more examples of generics: func pop() -> T { ... } } - func myAlgorithm(_ a: [T], length: Int) { ... } + func myAlgorithm(_ a: [T], length: Int) { ... } // The compiler can specialize code of MyStack var stackOfInts: MyStack @@ -368,12 +368,12 @@ represented as values, so this example is somewhat realistic. :: protocol P {} - struct Node : P { - var left, right : P? + struct Node: P { + var left, right: P? } struct Tree { - var node : P? + var node: P? init() { ... } } @@ -402,8 +402,8 @@ argument drops from being O(n), depending on the size of the tree to O(1). :: - struct Tree : P { - var node : [P?] + struct Tree: P { + var node: [P?] init() { node = [thing] } @@ -435,18 +435,18 @@ construct such a data structure: :: final class Ref { - var val : T - init(_ v : T) {val = v} + var val: T + init(_ v: T) {val = v} } struct Box { - var ref : Ref - init(_ x : T) { ref = Ref(x) } + var ref: Ref + init(_ x: T) { ref = Ref(x) } var value: T { get { return ref.val } set { - if (!isKnownUniquelyReferenced(&ref)) { + if !isKnownUniquelyReferenced(&ref) { ref = Ref(newValue) return } @@ -506,7 +506,7 @@ alive. withExtendedLifetime(Head) { // Create an Unmanaged reference. - var Ref : Unmanaged = Unmanaged.passUnretained(Head) + var Ref: Unmanaged = Unmanaged.passUnretained(Head) // Use the unmanaged reference in a call/variable access. The use of // _withUnsafeGuaranteedRef allows the compiler to remove the ultimate @@ -540,7 +540,7 @@ protocols as class-only protocols to get better runtime performance. :: - protocol Pingable : AnyObject { func ping() -> Int } + protocol Pingable: AnyObject { func ping() -> Int } .. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html diff --git a/docs/SIL.rst b/docs/SIL.rst index 3e159437bfa44..256b14beb74f0 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -1910,9 +1910,7 @@ Allocates uninitialized memory that is sufficiently aligned on the stack to contain a value of type ``T``. The result of the instruction is the address of the allocated memory. -If a type is runtime-sized, the compiler must emit code to potentially -dynamically allocate memory. So there is no guarantee that the allocated -memory is really located on the stack. +``alloc_stack`` always allocates memory on the stack even for runtime-sized type. ``alloc_stack`` marks the start of the lifetime of the value; the allocation must be balanced with a ``dealloc_stack`` instruction to @@ -2807,13 +2805,13 @@ It is expected that the strong reference count of the object is one. Furthermore, no other thread may increment the strong reference count during execution of this instruction. -copy_unowned_value -`````````````````` +strong_copy_unowned_value +````````````````````````` :: - sil-instruction ::= 'copy_unowned_value' sil-operand + sil-instruction ::= 'strong_copy_unowned_value' sil-operand - %1 = copy_unowned_value %0 : $@unowned T + %1 = strong_copy_unowned_value %0 : $@unowned T // %1 will be a strong @owned value of type $T. // $T must be a reference type @@ -3497,46 +3495,8 @@ partial_apply // %r will be of the substituted thick function type $(Z'...) -> R' Creates a closure by partially applying the function ``%0`` to a partial -sequence of its arguments. In the instruction syntax, the type of the callee is -specified after the argument list; the types of the argument and of the defined -value are derived from the function type of the callee. If the ``partial_apply`` -has an escaping function type (not ``[on_stack]``) the closure context will be -allocated with retain count 1 and initialized to contain the values ``%1``, -``%2``, etc. The closed-over values will not be retained; that must be done -separately before the ``partial_apply``. The closure does however take ownership -of the partially applied arguments (except for ``@inout_aliasable`` parameters); -when the closure reference count reaches zero, the contained values will be -destroyed. If the ``partial_apply`` has a ``@noescape`` function type -(``partial_apply [on_stack]``) the closure context is allocated on the stack and -initialized to contain the closed-over values. The closed-over values are not -retained, lifetime of the closed-over values must be managed separately. The -lifetime of the stack context of a ``partial_apply [on_stack]`` must be -terminated with a ``dealloc_stack``. - -If the callee is generic, all of its generic parameters must be bound by the -given substitution list. The arguments are given with these generic -substitutions applied, and the resulting closure is of concrete function -type with the given substitutions applied. The generic parameters themselves -cannot be partially applied; all of them must be bound. The result is always -a concrete function. - -If an address argument has ``@inout_aliasable`` convention, the closure -obtained from ``partial_apply`` will not own its underlying value. -The ``@inout_aliasable`` parameter convention is used when a ``@noescape`` -closure captures an ``inout`` argument. - -TODO: The instruction, when applied to a generic function, -currently implicitly performs abstraction difference transformations enabled -by the given substitutions, such as promoting address-only arguments and returns -to register arguments. This should be fixed. - -By default, ``partial_apply`` creates a closure whose invocation takes ownership -of the context, meaning that a call implicitly releases the closure. The -``[callee_guaranteed]`` change this to a caller-guaranteed model, where the -caller promises not to release the closure while the function is being called. - -This instruction is used to implement both curry thunks and closures. A -curried function in Swift:: +sequence of its arguments. This instruction is used to implement both curry +thunks and closures. A curried function in Swift:: func foo(_ a:A)(b:B)(c:C)(d:D) -> E { /* body of foo */ } @@ -3609,6 +3569,54 @@ lowers to an uncurried entry point and is curried in the enclosing function:: return %ret : $Int } +**Ownership Semantics of Closure Context during Invocation**: By default, an +escaping ``partial_apply`` (``partial_apply`` without ``[on_stack]]`` creates a +closure whose invocation takes ownership of the context, meaning that a call +implicitly releases the closure. If the ``partial_apply`` is marked with the +flag ``[callee_guaranteed]`` the invocation instead uses a caller-guaranteed +model, where the caller promises not to release the closure while the function +is being called. + +**Captured Value Ownership Semantics**: In the instruction syntax, the type of +the callee is specified after the argument list; the types of the argument and +of the defined value are derived from the function type of the callee. Even so, +the ownership requirements of the partial apply are not the same as that of the +callee function (and thus said signature). Instead: + +1. If the ``partial_apply`` has a ``@noescape`` function type (``partial_apply + [on_stack]``) the closure context is allocated on the stack and is + initialized to contain the closed-over values without taking ownership of + those values. The closed-over values are not retained and the lifetime of the + closed-over values must be managed by other instruction independently of the + ``partial_apply``. The lifetime of the stack context of a ``partial_apply + [on_stack]`` must be terminated with a ``dealloc_stack``. + +2. If the ``partial_apply`` has an escaping function type (not ``[on_stack]``) + then the closure context will be heap allocated with a retain count of 1. Any + closed over parameters (except for ``@inout`` parameters) will be consumed by + the partial_apply. This ensures that no matter when the ``partial_apply`` is + called, the captured arguments are alive. When the closure context's + reference count reaches zero, the contained values are destroyed. If the + callee requires an owned parameter, then the implicit partial_apply forwarder + created by IRGen will copy the underlying argument and pass it to the callee. + +3. If an address argument has ``@inout_aliasable`` convention, the closure + obtained from ``partial_apply`` will not own its underlying value. The + ``@inout_aliasable`` parameter convention is used when a ``@noescape`` + closure captures an ``inout`` argument. + +**NOTE:** If the callee is generic, all of its generic parameters must be bound +by the given substitution list. The arguments are given with these generic +substitutions applied, and the resulting closure is of concrete function type +with the given substitutions applied. The generic parameters themselves cannot +be partially applied; all of them must be bound. The result is always a concrete +function. + +**TODO:** The instruction, when applied to a generic function, currently +implicitly performs abstraction difference transformations enabled by the given +substitutions, such as promoting address-only arguments and returns to register +arguments. This should be fixed. + builtin ``````` :: @@ -3732,20 +3740,23 @@ This instruction has the same local semantics as ``retain_value`` but: The intention is that this instruction is used to implement unmanaged constructs. -copy_unmanaged_value -`````````````````````` +strong_copy_unmanaged_value +``````````````````````````` :: - sil-instruction ::= 'copy_unmanaged_value' sil-value + sil-instruction ::= 'strong_copy_unmanaged_value' sil-value - %1 = copy_unmanaged_value %0 : $@sil_unmanaged A + %1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged A // %1 will be a strong @owned $A. This instruction has the same semantics as ``copy_value`` except that its input is a trivial ``@sil_unmanaged`` type that doesn't require ref counting. This is intended to be used semantically as a "conversion" like instruction from ``unmanaged`` to ``strong`` and thus should never be removed by the optimizer. +Since the returned value is a strong owned value, this instruction semantically +should be treated as performing a strong copy of the underlying value as if by +the value's type lowering. copy_value `````````` diff --git a/docs/StandardLibraryProgrammersManual.md b/docs/StandardLibraryProgrammersManual.md index 8731c2db79392..4ef49ccd1c2ec 100644 --- a/docs/StandardLibraryProgrammersManual.md +++ b/docs/StandardLibraryProgrammersManual.md @@ -239,24 +239,3 @@ internal mutating func _roundSlowPath(_ rule: FloatingPointRoundingRule) { Because `_roundSlowPath(_:)` isn't inlinable, the version of `round(_:)` that gets called at run time will always be the version implemented in the standard library dylib. And since FloatingPointRoundingRule is *also* defined in the standard library, we know it'll never be out of sync with this version of `round(_:)`. Maybe some day we'll have special syntax in the language to say "call this method without allowing inlining" to get the same effect, but for now, this Curiously Recursive Inlinable Switch Pattern allows for safe inlining of switches over non-frozen enums with less boilerplate than you might otherwise have. Not none, but less. - - -## Productivity Hacks - -### Be a Ninja - -To *be* a productivity ninja, one must *use* `ninja`. `ninja` can be invoked inside the swift build directory, e.g. `/build/Ninja-ReleaseAssert/swift-macosx-x86_64/`. Running `ninja` (which is equivalent to `ninja all`) will build the local swift, stdlib and overlays. It doesn’t necessarily build all the testing infrastructure, benchmarks, etc. - -`ninja -t targets` gives a list of all possible targets to pass to ninja. This is useful for grepping. - -For this example, we will figure out how to quickly iterate on a change to the standard library to fix 32-bit build errors while building on a 64-bit host, suppressing warnings along the way. - -`ninja -t targets | grep stdlib | grep i386` will output many targets, but at the bottom we see `swift-stdlib-iphonesimulator-i386`, which looks like a good first step. This target will just build i386 parts and not waste our time also building the 64-bit stdlib, overlays, etc. - -Going further, ninja can spawn a web browser for you to navigate dependencies and rules. `ninja -t browse swift-stdlib-iphonesimulator-i386` will open a webpage with hyperlinks for all related targets. “target is built using” lists all this target’s dependencies, while “dependent edges build” list all the targets that depend directly on this. - -Clicking around a little bit, we can find `lib/swift/iphonesimulator/i386/libswiftCore.dylib` as a commonly-depended-upon target. This will perform just what is needed to compile the standard library for i386 and nothing else. - -Going further, for various reasons the standard library has lots of warnings. This is actively being addressed, but fixing all of them may require language features, etc. In the mean time, let’s suppress warnings in our build so that we just see the errors. `ninja -nv lib/swift/iphonesimulator/i386/libswiftCore.dylib` will show us the actual commands ninja will issue to build the i386 stdlib. (You’ll notice that an incremental build here is merely 3 commands as opposed to ~150 for `swift-stdlib-iphonesimulator-i386`). - -Copy the invocation that has ` -o /swift-macosx-x86_64/stdlib/public/core/iphonesimulator/i386/Swift.o`, so that we can perform the actual call to swiftc ourselves. Tack on `-suppress-warnings` at the end, and now we have the command to just build `Swift.o` for i386 while only displaying the actual errors. diff --git a/docs/TypeChecker.rst b/docs/TypeChecker.rst index d38378180cfe3..b41381b24d1de 100644 --- a/docs/TypeChecker.rst +++ b/docs/TypeChecker.rst @@ -370,12 +370,12 @@ guesswork. However, we note that the type of an enum member actually has a regular structure. For example, consider the ``Optional`` type:: enum Optional { - case None - case Some(T) + case none + case some(T) } -The type of ``Optional.None`` is ``Optional``, while the type of -``Optional.Some`` is ``(T) -> Optional``. In fact, the +The type of ``Optional.none`` is ``Optional``, while the type of +``Optional.some`` is ``(T) -> Optional``. In fact, the type of an enum element can have one of two forms: it can be ``T0``, for an enum element that has no extra data, or it can be ``T2 -> T0``, where ``T2`` is the data associated with the enum element. For the @@ -971,3 +971,18 @@ well-typed suggestions. meets and joins, so the solver continues until it runs out of supertypes to enumerate. +New Diagnostic Architecture +--------------------------- + +We are currently working on porting type-check based diagnostics over +to the new diagnostic framework, which is described in detail in this +`blog post +`_. + +The things in the queue yet to be ported are: + +- Problems related to calls and operator applications e.g. + + - Missing explicit ``Self.`` and ``self.`` + - Logic related to overload candidate ranking (``CalleeCandidateInfo``) + - ``diagnoseParameterErrors`` diff --git a/docs/Ubuntu14.md b/docs/Ubuntu14.md index 51e6a0eca94e5..78e2982e4ef53 100644 --- a/docs/Ubuntu14.md +++ b/docs/Ubuntu14.md @@ -3,9 +3,11 @@ ## Upgrade Clang You'll need to upgrade your clang compiler for C++14 support and create a symlink. The minimum required version of clang may change, and may not be available on Ubuntu 14.04 in the future. ```bash -sudo apt-get install clang-3.6 -sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100 -sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100 +sudo apt-get install clang-3.9 +sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.9 100 +sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.9 100 +sudo update-alternatives --set clang /usr/bin/clang-3.9 +sudo update-alternatives --set clang++ /usr/bin/clang++-3.9 ``` ## Upgrade CMake diff --git a/docs/Windows.md b/docs/Windows.md index e7f96abc156d9..134fe6c6a93ac 100644 --- a/docs/Windows.md +++ b/docs/Windows.md @@ -10,6 +10,11 @@ Currently there are two supported ways to build Swift for Windows. 1. To build on Windows using Microsoft Visual C++ (MSVC), see [Building on Windows](./WindowsBuild.md#MSVC) -## Windows Subsystem for Linux +## Windows Subsystem for Linux (WSL) [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/about) is an Ubuntu environment. Follow the instructions for building on [Linux](../README.md#linux) + +There two versions of WSL as of November 23, 2019: + +- WSL1: is the current stable version. Both Swift compilation and execution work but REPL and debugging (LLDB) hang on startup. +- WSL2: Both REPL and debugging work with WSL2. Although, WSL2 is still in development, it is available by downloading an insider build. Installing WSL2 is pretty simple if WSL1 was already installed (switch to insider, download an insider build and run some scripts). WSL2 can be installed [by following this link](https://docs.microsoft.com/windows/wsl/wsl2-install). diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md index 74571357f1a22..0fb39e7d87c07 100644 --- a/docs/WindowsBuild.md +++ b/docs/WindowsBuild.md @@ -2,391 +2,200 @@ Visual Studio 2017 or newer is needed to build swift on Windows. -## 1. Install dependencies -- Install the latest version of [Visual Studio](https://www.visualstudio.com/downloads/) -- Make sure to include "Programming Languages|Visual C++" and "Windows and Web - Development|Universal Windows App Development|Windows SDK" in your - installation. - -## 2. Clone the repositories -1. Configure git to work with Unix file endings -1. Create a folder to contain all the Swift repositories -1. Clone `apple/swift-cmark` into a folder named `cmark` -1. Clone `apple/swift-clang` into a folder named `clang` -1. Clone `apple/swift-llvm` into a folder named `llvm` -1. Clone `apple/swift-compiler-rt` into a folder named `compiler-rt` -1. Clone `apple/swift` into a folder named `swift` -1. Clone `apple/swift-corelibs-libdispatch` into a folder named `swift-corelibs-libdispatch` -1. Clone `apple/swift-corelibs-foundation` into a folder named `swift-corelibs-foundation` -1. Clone `apple/swift-corelibs-xctest` into a folder name `swift-corelibs-xctest` -1. Clone `apple/swift-lldb` into a folder named `lldb` -1. Clone `apple/swift-llbuild` into a folder named `llbuild` -1. Clone `apple/swift-package-manager` info a folder named `swift-package-manager` -1. Clone `curl` into a folder named `curl` -1. Clone `libxml2` into a folder named `libxml2` - -- Currently, other repositories in the Swift project have not been tested and - may not be supported. +The following must take place in the **developer command prompt** (provided by Visual Studio). This shows up as "x64 Native Tools Command Prompt for VS2017" (or VS2019, VS2019 Preview depending on the Visual Studio that you are using) in the Start Menu. -This guide assumes your sources live at the root of `S:`. If your sources live elsewhere, you can create a substitution for this: +## Install dependencies -```cmd -subst S: -``` +- Install the latest version of [Visual Studio](https://www.visualstudio.com/downloads/) +- Make sure to include "Programming Languages|Visual C++" and "Windows and Web Development|Universal Windows App Development|Windows SDK" in your installation. The following components are required: -```cmd -S: -git clone https://github.com/apple/swift-cmark cmark -git clone https://github.com/apple/swift-clang clang -git clone https://github.com/apple/swift-llvm llvm -git clone https://github.com/apple/swift-compiler-rt compiler-rt -git clone -c core.autocrlf=input -c core.symlinks=true https://github.com/apple/swift -git clone https://github.com/apple/swift-corelibs-libdispatch -git clone https://github.com/apple/swift-corelibs-foundation -git clone https://github.com/apple/swift-corelibs-xctest -git clone https://github.com/apple/swift-lldb lldb -git clone https://github.com/apple/swift-llbuild llbuild -git clone -c core.autocrlf=input https://github.com/apple/swift-package-manager -git clone https://github.com/curl/curl.git -git clone https://gitlab.gnome.org/GNOME/libxml2.git -``` +1. Microsoft.VisualStudio.Component.Windows10SDK +2. Microsoft.VisualStudio.Component.Windows10SDK.17763 +3. Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -## 3. Acquire ICU -1. Download ICU from [ICU Project](http://site.icu-project.org) for Windows x64 and extract the folder to a new folder called `thirdparty`. In other words, there should be a folder `S:\thirdparty\icu4c-64_2-Win64-MSVC2017` with the ICU. -1. Add the `bin64` folder to your `Path` environment variable. +The following [link](https://docs.microsoft.com/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2019)) helps in finding the component name given its ID for Visual Studio 2019. -```cmd -PATH S:\thirdparty\icu4c-64_2-Win64-MSVC2017\bin64;%PATH% -``` +## Clone the repositories -## 4. Fetch SQLite3 +1. Clone `apple/llvm-project` into a directory for the toolchain +2. Clone `apple/swift-cmark`, `apple/swift`, `apple/swift-corelibs-libdispatch`, `apple/swift-corelibs-foundation`, `apple/swift-corelibs-xctest`, `apple/swift-llbuild`, `apple/swift-package-manager` into the toolchain directory +3. Clone `compnerd/windows-swift` as a peer of the toolchain directory -```powershell -(New-Object System.Net.WebClient).DownloadFile("https://www.sqlite.org/2019/sqlite-amalgamation-3270200.zip", "S:\sqlite-amalgamation-3270200.zip") -Add-Type -A System.IO.Compression.FileSystem -[IO.Compression.ZipFile]::ExtractToDirectory("S:\sqlite-amalgamation-3270200.zip", "S:\") -``` +- Currently, other repositories in the Swift project have not been tested and may not be supported. -## 5. Get ready -- From within a **developer** command prompt (not PowerShell nor cmd, but the [Visual Studio Developer Command Prompt](https://msdn.microsoft.com/en-us/library/f35ctcxw.aspx)), execute the following command if you have an x64 PC. +This guide assumes your sources live at the root of `S:`. If your sources live elsewhere, you can create a substitution for this: ```cmd -VsDevCmd -arch=amd64 +subst S: ``` -If instead you're compiling for a 32-bit Windows target, adapt the `arch` argument to `x86` and run - ```cmd -VsDevCmd -arch=x86 +S: +git clone https://github.com/apple/llvm-project --branch swift/master toolchain +git clone -c core.autocrlf=input -c core.symlinks=true https://github.com/apple/swift toolchain/swift +git clone https://github.com/apple/swift-cmark toolchain/cmark +git clone https://github.com/apple/swift-corelibs-libdispatch toolchain/swift-corelibs-libdispatch +git clone https://github.com/apple/swift-corelibs-foundation toolchain/swift-corelibs-foundation +git clone https://github.com/apple/swift-corelibs-xctest toolchain/swift-corelibs-xctest +git clone https://github.com/apple/swift-llbuild toolchain/llbuild +git clone https://github.com/apple/swift-tools-support-core toolchain/swift-tools-support-core +git clone -c core.autocrlf=input https://github.com/apple/swift-package-manager toolchain/swiftpm +git clone https://github.com/compnerd/windows-swift windows-swift ``` -- Decide whether you want to build a release or debug version of Swift on Windows and - replace the `CMAKE_BUILD_TYPE` parameter in the build steps below with the correct value - (`Debug`, `RelWithDebInfoAssert` or `Release`) to avoid conflicts between the debug and - non-debug version of the MSCRT library. - -- Set up the `ucrt`, `visualc`, and `WinSDK` modules by copying `ucrt.modulemap` located at - `swift/stdlib/public/Platform/ucrt.modulemap` into - `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap`, copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap`, and copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` - -```cmd -mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" S:\swift\stdlib\public\Platform\ucrt.modulemap -mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" S:\swift\stdlib\public\Platform\winsdk.modulemap -mklink "%VCToolsInstallDir%\include\module.modulemap" S:\swift\stdlib\public\Platform\visualc.modulemap -mklink "%VCToolsInstallDir%\include\visualc.apinotes" S:\swift\stdlib\public\Platform\visualc.apinotes -``` +## Acquire ICU, SQLite3, curl, libxml2 and zlib -Warning: Creating the above links usually requires administrator privileges. The quick and easy way to do this is to open a second developer prompt by right clicking whatever shortcut you used to open the first one, choosing Run As Administrator, and pasting the above commands into the resulting window. You can then close the privileged prompt; this is the only step which requires elevation. +Go to [compnerd's windows-swift azure page](https://dev.azure.com/compnerd/windows-swift/_build) and open [Pipelines](https://dev.azure.com/compnerd/windows-swift/_build) where you'll see bots (hopefully green) for: -## 6. Build LLVM/Clang -- This must be done from within a developer command prompt. LLVM and Clang are - large projects, so building might take a few hours. Make sure that the build - type for LLVM/Clang is compatible with the build type for Swift. That is, - either build everything `Debug` or some variant of `Release` (e.g. `Release`, - `RelWithDebInfo`). -```cmd -md "S:\b\llvm" -cd "S:\b\llvm" -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=Release^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-windows-msvc^ - -DLLVM_ENABLE_ASSERTIONS=ON^ - -DLLVM_ENABLE_PDB=YES^ - -DLLVM_ENABLE_PROJECTS=clang^ - -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86"^ - S:/llvm -ninja -``` +- [ICU](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=9) +- [SQLite](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=12&_a=summary) +- [curl](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=11&_a=summary) +- [libxml2](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=10&_a=summary) +- [zlib](https://dev.azure.com/compnerd/windows-swift/_build?definitionId=16&_a=summary) -- Update your path to include the LLVM tools. +Download each of the zip files and copy their contents into S:/Library. The directory structure should resemble: -```cmd -path S:\b\llvm\bin;%PATH% ``` -## 7. Build CMark -- This must be done from within a developer command prompt. CMark is a fairly - small project and should only take a few minutes to build. -```cmd -md "S:\b\cmark" -cd "S:\b\cmark" -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - S:\cmark -ninja +/Library + ┝ icu-64 + │ ┕ usr/... + ├ libcurl-development + │ ┕ usr/... + ├ libxml2-development + │ ┕ usr/... + ├ sqlite-3.28.0 + │ ┕ usr/... + ┕ zlib-1.2.11 + ┕ usr/... ``` -## 8. Build Swift -- This must be done from within a developer command prompt -- Note that Visual Studio vends a 32-bit python 2.7 installation in `C:\Python27` and a 64-bit python in `C:\Python27amd64`. You may use either one based on your installation. +## One-time Setup (re-run on Visual Studio upgrades) -```cmd -md "S:\b\swift" -cd "S:\b\swift" -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DCMAKE_EXE_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ - -DSWIFT_INCLUDE_DOCS=OFF^ - -DSWIFT_PATH_TO_CMARK_SOURCE="S:\cmark"^ - -DSWIFT_PATH_TO_CMARK_BUILD="S:\b\cmark"^ - -DLLVM_DIR=S:\b\llvm\lib\cmake\llvm^ - -DClang_DIR=S:\b\llvm\lib\cmake\clang^ - -DSWIFT_PATH_TO_LIBDISPATCH_SOURCE="S:\swift-corelibs-libdispatch"^ - -DSWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="S:/thirdparty/icu4c-64_2-Win64-MSVC2017/include"^ - -DSWIFT_WINDOWS_x86_64_ICU_UC="S:/thirdparty/icu4c-64_2-Win64-MSVC2017/lib64/icuuc.lib"^ - -DSWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="S:/thirdparty/icu4c-64_2-Win64-MSVC2017/include"^ - -DSWIFT_WINDOWS_x86_64_ICU_I18N="S:/thirdparty/icu4c-64_2-Win64-MSVC2017/lib64/icuin.lib"^ - -DCMAKE_INSTALL_PREFIX="C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr"^ - -DPYTHON_EXECUTABLE=C:\Python27\python.exe^ - S:\swift -ninja -``` +Set up the `ucrt`, `visualc`, and `WinSDK` modules by: -- To create a Visual Studio project, you'll need to change the generator and, - if you have a 64 bit processor, specify the generator platform. Note that you - may get multiple build errors compiling the `swift` project due to an MSBuild - limitation that file paths cannot exceed 260 characters. These can be - ignored, as they occur after the build when writing the last build status to - a file. +- copying `ucrt.modulemap` located at `swift/stdlib/public/Platform/ucrt.modulemap` into + `${UniversalCRTSdkDir}/Include/${UCRTVersion}/ucrt` as `module.modulemap` +- copying `visualc.modulemap` located at `swift/stdlib/public/Platform/visualc.modulemap` into `${VCToolsInstallDir}/include` as `module.modulemap` +- copying `winsdk.modulemap` located at `swift/stdlib/public/Platform/winsdk.modulemap` into `${UniversalCRTSdkDir}/Include/${UCRTVersion}/um` +- and setup the `visualc.apinotes` located at `swift/stdlib/public/Platform/visualc.apinotes` into `${VCToolsInstallDir}/include` as `visualc.apinotes` ```cmd -cmake -G "Visual Studio 2017" -A x64 -T "host=x64"^ ... +mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" S:\toolchain\swift\stdlib\public\Platform\ucrt.modulemap +mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" S:\toolchain\swift\stdlib\public\Platform\winsdk.modulemap +mklink "%VCToolsInstallDir%\include\module.modulemap" S:\toolchain\swift\stdlib\public\Platform\visualc.modulemap +mklink "%VCToolsInstallDir%\include\visualc.apinotes" S:\toolchain\swift\stdlib\public\Platform\visualc.apinotes ``` -## 9. Build lldb -- This must be done from within a developer command prompt and could take hours - depending on your system. -```cmd -md "S:\b\lldb" -cd "S:\b\lldb" -cmake -G Ninja^ - -DLLVM_DIR="S:/b/llvm/lib/cmake/llvm"^ - -DClang_DIR="S:/b/llvm/lib/cmake/clang"^ - -DSwift_DIR="S:/b/swift/lib/cmake/swift"^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DLLDB_ALLOW_STATIC_BINDINGS=YES^ - -DLLVM_ENABLE_ASSERTIONS=ON^ - -DPYTHON_HOME="%ProgramFiles(x86)%\Microsoft Visual Studio\Shared\Python37_64"^ - S:\lldb -ninja -``` - -## 10. Running tests on Windows +Warning: Creating the above links usually requires administrator privileges. The quick and easy way to do this is to open a second developer prompt by right clicking whatever shortcut you used to open the first one, choosing Run As Administrator, and pasting the above commands into the resulting window. You can then close the privileged prompt; this is the only step which requires elevation. -Running the testsuite on Windows has additional external dependencies. +## Build the toolchain ```cmd -path S:\thirdparty\icu4c-64_2-Win64-MSVC2017\bin64;S:\b\swift\bin;S:\b\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin -ninja -C S:\b\swift check-swift +md "S:\b\toolchain" +cmake -B "S:\b\toolchain" -G Ninja -S S:\toolchain\llvm -C S:\windows-swift\cmake\caches\Windows-x86_64.cmake -C S:\windows-swift\cmake\caches\org.compnerd.dt.cmake -DLLVM_ENABLE_ASSERTIONS=YES -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cmark;swift;lldb;lld" -DLLVM_EXTERNAL_PROJECTS="cmark;swift" -DSWIFT_PATH_TO_LIBDISPATCH_SOURCE=S:\toolchain\swift-corelibs-libdispatch -DLLVM_ENABLE_PDB=YES -DLLDB_DISABLE_PYTHON=YES -DSWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="S:/Library/icu-64/usr/include" -DSWIFT_WINDOWS_x86_64_ICU_UC="S:/Library/icu-64/usr/lib/icuuc64.lib" -DSWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="S:/Library/icu-64/usr/include" -DSWIFT_WINDOWS_x86_64_ICU_I18N="S:/Library/icu-64/usr/lib/icuin64.lib" -DCMAKE_INSTALL_PREFIX="C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr" -DPYTHON_EXECUTABLE=C:\Python27\python.exe -DSWIFT_BUILD_DYNAMIC_STDLIB=YES -DSWIFT_BUILD_DYNAMIC_SDK_OVERLAY=YES +ninja -C S:\b\toolchain ``` -## 11. Build swift-corelibs-libdispatch +## Running Swift tests on Windows ```cmd -md "S:\b\libdispatch" -cd "S:\b\libdispatch" -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_CXX_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER=S:\b\swift\bin\swiftc.exe^ - -DENABLE_SWIFT=ON^ - -DENABLE_TESTING=OFF^ - S:\swift-corelibs-libdispatch -ninja +path S:\Library\icu-64\usr\bin;S:\b\toolchain\bin;S:\b\toolchain\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin +ninja -C S:\b\toolchain check-swift ``` -- Add libdispatch to your path: -```cmd -path S:\b\libdispatch;S:\b\libdispatch\src;%PATH% -``` - -## 12. Build curl +## Build swift-corelibs-libdispatch ```cmd -cd "S:\curl" -.\buildconf.bat -cd winbuild -nmake /f Makefile.vc mode=static VC=15 MACHINE=x64 +cmake -B S:\b\libdispatch -G Ninja -S S:\toolchain\swift-corelibs-libdispatch -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=S:/b/toolchain/bin/clang-cl.exe -DCMAKE_CXX_COMPILER=S:/b/toolchain/bin/clang-cl.exe -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DENABLE_SWIFT=YES +ninja -C S:\b\libdispatch ``` -## 13. Build libxml2 +## Test swift-corelibs-libdispatch ```cmd -cd "S:\libxml2\win32" -cscript //E:jscript configure.js iconv=no -nmake /f Makefile.msvc +ninja -C S:\b\libdispatch check ``` -## 14. Build swift-corelibs-foundation +## Build swift-corelibs-foundation ```cmd -md "S:\b\foundation" -cd "S:\b\foundation -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER=S:\b\swift\bin\swiftc.exe^ - -DCURL_LIBRARY="S:/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/lib/libcurl_a.lib"^ - -DCURL_INCLUDE_DIR="S:/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/include"^ - -DENABLE_TESTING=NO^ - -DICU_ROOT="S:/thirdparty/icu4c-64_2-Win64-MSVC2017"^ - -DLIBXML2_LIBRARY="S:/libxml2/win32/bin.msvc/libxml2_a.lib"^ - -DLIBXML2_INCLUDE_DIR="S:/libxml2/include"^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ - S:\swift-corelibs-foundation -ninja +cmake -B S:\b\foundation -G Ninja -S S:\toolchain\swift-corelibs-foundation -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=S:/b/toolchain/clang-cl.exe -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib" -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include" -DICU_ROOT="S:/Library/icu-64" -DICU_INCLUDE_DIR=S:/Library/icu-64/usr/include -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2s.lib" -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include/libxml2" -DENABLE_TESTING=NO -Ddispatch_DIR=S:/b/libdispatch/cmake/modules +ninja -C S:\b\foundation ``` - Add Foundation to your path: + ```cmd -path S:\b\foundation;%PATH% +path S:\b\foundation\Foundation;%PATH% ``` -## 15. Build swift-corelibs-xctest +## Build swift-corelibs-xctest ```cmd -md "S:\b\xctest" -cd "S:\b\xctest" -cmake -G Ninja^ - -DBUILD_SHARED_LIBS=YES^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_SWIFT_COMPILER=S:\b\swift\bin\swiftc.exe^ - -DXCTEST_PATH_TO_FOUNDATION_BUILD=S:\b\foundation^ - -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DXCTEST_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ - -DLIT_COMMAND=S:\llvm\utils\lit\lit.py^ - -DPYTHON_EXECUTABLE=C:\Python27\python.exe^ - S:\swift-corelibs-xctest -ninja +cmake -B S:\b\xctest -G Ninja -S S:\toolchain\swift-corelibs-xctest -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -Ddispatch_DIR=S:\b\dispatch\cmake\modules -DFoundation_DIR=S:\b\foundation\cmake\modules -DLIT_COMMAND=S:\toolchain\llvm\utils\lit\lit.py -DPYTHON_EXECUTABLE=C:\Python27\python.exe +ninja -C S:\b\xctest ``` - Add XCTest to your path: + ```cmd path S:\b\xctest;%PATH% ``` -## 16. Test XCTest +## Test XCTest ```cmd ninja -C S:\b\xctest check-xctest ``` -## 17. Rebuild Foundation +## Rebuild Foundation ```cmd -cd "S:\b\foundation -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER=S:\b\swift\bin\swiftc.exe^ - -DCURL_LIBRARY="S:/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/lib/libcurl_a.lib"^ - -DCURL_INCLUDE_DIR="S:/curl/builds/libcurl-vc15-x64-release-static-ipv6-sspi-winssl/include"^ - -DENABLE_TESTING=YES^ - -DICU_ROOT="S:/thirdparty/icu4c-64_2-Win64-MSVC2017"^ - -DLIBXML2_LIBRARY="S:/libxml2/win32/bin.msvc/libxml2_a.lib"^ - -DLIBXML2_INCLUDE_DIR="S:/libxml2/include"^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=S:\swift-corelibs-libdispatch^ - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=S:\b\libdispatch^ - -DFOUNDATION_PATH_TO_XCTEST_BUILD=S:\b\xctest^ - S:\swift-corelibs-foundation -ninja +cmake -B S:\b\foundation -G Ninja -S S:\toolchain\swift-corelibs-foundation -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=S:/b/toolchain/bin/clang-cl.exe -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DCURL_LIBRARY="S:/Library/libcurl-development/usr/lib/libcurl.lib" -DCURL_INCLUDE_DIR="S:/Library/libcurl-development/usr/include" -DICU_ROOT="S:/Library/icu-64" -DLIBXML2_LIBRARY="S:/Library/libxml2-development/usr/lib/libxml2.lib" -DLIBXML2_INCLUDE_DIR="S:/Library/libxml2-development/usr/include" -DENABLE_TESTING=YES -Ddisptch_DIR=S:/b/libdispatch/cmake/modules -DXCTest_DIR=S:/b/xctest/cmake/modules +ninja -C S:\b\foundation ``` -## 18. Test Foundation +## Test Foundation ```cmd cmake --build S:\b\foundation -ninja -C S:\b\foundation test +ninja -C S:\b\foundation test ``` -## 19. Build SQLite3 +## Build llbuild ```cmd -md S:\b\sqlite -cd S:\b\sqlite -cl /MD /Ox /Zi /LD /DSQLITE_API=__declspec(dllexport) S:\sqlite-amalgamation-3270200\sqlite3.c -``` - - - Add SQLite3 to your path: -```cmd -path S:\b\sqlite;%PATH% -``` - -## 20. Build llbuild - -```cmd -md S:\b\llbuild -cd S:\b\llbuild set AR=llvm-ar -cmake -G Ninja^ - -DCMAKE_BUILD_TYPE=RelWithDebInfo^ - -DCMAKE_C_COMPILER=cl^ - -DCMAKE_CXX_COMPILER=cl^ - -DFOUNDATION_BUILD_DIR=S:\b\foundation^ - -DLIBDISPATCH_BUILD_DIR=S:\b\libdispatch^ - -DLIBDISPATCH_SOURCE_DIR=S:\swift-corelibs-libdispatch^ - -DSQLite3_INCLUDE_DIR=S:\sqlite-amalgamation-3270200^ - -DSQLite3_LIBRARY=S:\b\sqlite\sqlite3.lib^ - -DLLBUILD_SUPPORT_BINDINGS=Swift^ - S:\llbuild -ninja +cmake -B S:\b\llbuild -G Ninja -S S:\toolchain\llbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_COMPILER=cl -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DFoundation_DIR=S:/b/foundation/cmake/modules -Ddispatch_DIR=S:/b/libdispatch/cmake/modules -DSQLite3_INCLUDE_DIR=S:\Library\sqlite-3.28.0\usr\include -DSQLite3_LIBRARY=S:\Library\sqlite-3.28.0\usr\lib\sqlite3.lib -DLLBUILD_SUPPORT_BINDINGS=Swift +ninja -C S:\b\llbuild ``` - - Add llbuild to your path: +- Add llbuild to your path: + ```cmd path S:\b\llbuild\bin;%PATH% ``` -## 21. Build swift-package-manager +## Build swift-tools-core-support ```cmd -md S:\b\spm -cd S:\b\spm -C:\Python27\python.exe S:\swift-package-manager\Utilities\bootstrap --foundation S:\b\foundation --libdispatch-build-dir S:\b\libdispatch --libdispatch-source-dir S:\swift-corelibs-libdispatch --llbuild-build-dir S:\b\llbuild --llbuild-source-dir S:\llbuild --sqlite-build-dir S:\b\sqlite --sqlite-source-dir S:\sqlite-amalgamation-3270200 +cmake -B S:\b\tsc -G Ninja -S S:\toolchain\swift-tools-support-core -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=cl -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DFoundation_DIR=S:/b/foundation/cmake/modules -Ddispatch_DIR=S:/b/libdispatch/cmake/modules +ninja -C S:\b\tsc ``` -## 22. Install Swift on Windows +## Build swift-package-manager -- Run ninja install: - -```cmd -ninja -C S:\b\swift install +```cmd +cmake -B S:\b\spm -G Ninja -S S:\toolchain\swiftpm -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=S:/b/toolchain/bin/clang-cl.exe -DCMAKE_CXX_COMPILER=S:/b/toolchain/bin/clang-cl.exe -DCMAKE_Swift_COMPILER=S:/b/toolchain/bin/swiftc.exe -DUSE_VENDORED_TSC=YES -DFoundation_DIR=S:/b/foundation/cmake/modules -Ddispatch_DIR=S:/b/libdispatch/cmake/modules -DLLBuild_DIR=S:/b/llbuild/cmake/modules +ninja -C S:\b\spm ``` -- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. +## Install the Swift toolchain on Windows -## Resuming Builds - -If you resume development from a new shell, the path will need to be readjusted. The following will add the correct search order to the path: +- Run ninja install: ```cmd -path S:\thirdparty\icu4c-63_1-Win64-MSVC2017\bin64;S:\b\llvm\bin;S:\b\swift\bin;S:\b\libdispatch;S:\b\libdispatch\src;S:\b\foundation;S:\b\xctest;S:\b\llbuild\bin;S:\b\sqlite;%PATH%;%ProgramFiles%\Git\usr\bin +ninja -C S:\b\toolchain install ``` + +- Add the Swift on Windows binaries path (`C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin`) to the `PATH` environment variable. diff --git a/docs/assets/DifferentiableProgramming/autodiff-reverse-debugging.gif b/docs/assets/DifferentiableProgramming/autodiff-reverse-debugging.gif new file mode 100644 index 0000000000000..4b8f20a2abd49 Binary files /dev/null and b/docs/assets/DifferentiableProgramming/autodiff-reverse-debugging.gif differ diff --git a/docs/assets/DifferentiableProgramming/chain-rule-left-assoc.png b/docs/assets/DifferentiableProgramming/chain-rule-left-assoc.png new file mode 100644 index 0000000000000..c39a2652998b5 Binary files /dev/null and b/docs/assets/DifferentiableProgramming/chain-rule-left-assoc.png differ diff --git a/docs/assets/DifferentiableProgramming/chain-rule-right-assoc.png b/docs/assets/DifferentiableProgramming/chain-rule-right-assoc.png new file mode 100644 index 0000000000000..20bbbbc1398ad Binary files /dev/null and b/docs/assets/DifferentiableProgramming/chain-rule-right-assoc.png differ diff --git a/docs/assets/DifferentiableProgramming/convention-c-function-representation.png b/docs/assets/DifferentiableProgramming/convention-c-function-representation.png new file mode 100644 index 0000000000000..432947f53f61e Binary files /dev/null and b/docs/assets/DifferentiableProgramming/convention-c-function-representation.png differ diff --git a/docs/assets/DifferentiableProgramming/difference-quotient.png b/docs/assets/DifferentiableProgramming/difference-quotient.png new file mode 100644 index 0000000000000..e9dd42faea555 Binary files /dev/null and b/docs/assets/DifferentiableProgramming/difference-quotient.png differ diff --git a/docs/assets/DifferentiableProgramming/differentiable-function-representation.png b/docs/assets/DifferentiableProgramming/differentiable-function-representation.png new file mode 100644 index 0000000000000..989250afce5e5 Binary files /dev/null and b/docs/assets/DifferentiableProgramming/differentiable-function-representation.png differ diff --git a/docs/assets/DifferentiableProgramming/differentiable-function-subtyping.png b/docs/assets/DifferentiableProgramming/differentiable-function-subtyping.png new file mode 100644 index 0000000000000..9187b08a30891 Binary files /dev/null and b/docs/assets/DifferentiableProgramming/differentiable-function-subtyping.png differ diff --git a/docs/assets/DifferentiableProgramming/differentiable-manifolds.png b/docs/assets/DifferentiableProgramming/differentiable-manifolds.png new file mode 100644 index 0000000000000..0dac1bbec942a Binary files /dev/null and b/docs/assets/DifferentiableProgramming/differentiable-manifolds.png differ diff --git a/docs/assets/DifferentiableProgramming/gated-recurrent-neural-network.png b/docs/assets/DifferentiableProgramming/gated-recurrent-neural-network.png new file mode 100644 index 0000000000000..43093849be66f Binary files /dev/null and b/docs/assets/DifferentiableProgramming/gated-recurrent-neural-network.png differ diff --git a/docs/assets/DifferentiableProgramming/iterative-optimization.png b/docs/assets/DifferentiableProgramming/iterative-optimization.png new file mode 100644 index 0000000000000..62cc27ff14d9e Binary files /dev/null and b/docs/assets/DifferentiableProgramming/iterative-optimization.png differ diff --git a/docs/assets/DifferentiableProgramming/plot-linear.png b/docs/assets/DifferentiableProgramming/plot-linear.png new file mode 100644 index 0000000000000..0ed45a976591a Binary files /dev/null and b/docs/assets/DifferentiableProgramming/plot-linear.png differ diff --git a/docs/assets/DifferentiableProgramming/plot-quadratic.png b/docs/assets/DifferentiableProgramming/plot-quadratic.png new file mode 100644 index 0000000000000..66a5686cba41f Binary files /dev/null and b/docs/assets/DifferentiableProgramming/plot-quadratic.png differ diff --git a/docs/assets/DifferentiableProgramming/tapenade.png b/docs/assets/DifferentiableProgramming/tapenade.png new file mode 100644 index 0000000000000..c0119452f1b8a Binary files /dev/null and b/docs/assets/DifferentiableProgramming/tapenade.png differ diff --git a/docs/conf.py b/docs/conf.py index 7661acef20c02..dfb43da56bb61 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,8 @@ import sys from datetime import date +from sphinx.highlighting import lexers + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -263,9 +265,7 @@ # Enable this if you want TODOs to show up in the generated documentation. todo_include_todos = True -# -# Monkeypatch pygments so it will know about the Swift lexers -# +# -- Patch pygments so it will know about the Swift lexers --------------- # Pull in the Swift lexers from os.path import abspath, dirname, join as join_paths # noqa (E402) @@ -277,22 +277,6 @@ sys.path.pop(0) -# Monkeypatch pygments.lexers.get_lexer_by_name to return our lexers. The -# ordering required to allow for monkeypatching causes the warning -# "I100 Import statements are in the wrong order." when linting using -# flake8-import-order. "noqa" is used to suppress this warning. -from pygments.lexers import get_lexer_by_name as original_get_lexer_by_name # noqa (E402) - - -def swift_get_lexer_by_name(_alias, *args, **kw): - if _alias == 'swift': - return swift_pygments_lexers.SwiftLexer() - elif _alias == 'sil': - return swift_pygments_lexers.SILLexer() - elif _alias == 'swift-console': - return swift_pygments_lexers.SwiftConsoleLexer() - else: - return original_get_lexer_by_name(_alias, *args, **kw) - -import pygments.lexers # noqa (I100 Import statements are in the wrong order.) -pygments.lexers.get_lexer_by_name = swift_get_lexer_by_name +lexers['swift'] = swift_pygments_lexers.SwiftLexer() +lexers['sil'] = swift_pygments_lexers.SILLexer() +lexers['swift-console'] = swift_pygments_lexers.SwiftConsoleLexer() diff --git a/docs/proposals/DeclarationTypeChecker.rst b/docs/proposals/DeclarationTypeChecker.rst index 711c0f6b8874b..fecc2ceed0904 100644 --- a/docs/proposals/DeclarationTypeChecker.rst +++ b/docs/proposals/DeclarationTypeChecker.rst @@ -13,13 +13,13 @@ This document describes some of the problems with our current "declaration" type Problems with the Current Approach ---------------------------------- -The current declaration type checker---in particular, ``validateDecl``, which assigns a type to a given declaration---is the source of a large number of Swift bugs, including crashes on both well-formed and ill-formed code, different behavior depending on the order of declarations within a file or across multiple files, infinite recursion, and broken ASTs. The main issues are: +The current declaration type checker is the source of a large number of Swift bugs, including crashes on both well-formed and ill-formed code, different behavior depending on the order of declarations within a file or across multiple files, infinite recursion, and broken ASTs. The main issues are: **Conceptual phases are tangled together**: We have a vague notion that there are phases within the compiler, e.g., extension binding occurs before name binding, which occurs before type checking. However, the implementations in the compiler don't respect phases: extension binding goes through type validation, which does both name binding and type checking. Name lookup attempts to do type checking so that it can establish whether one declaration shadows another. **Unprincipled recursion**: Whenever type checking some particular declaration requires information about another declaration, it recurses to type-check that declaration. There are dozens of these recursion points scattered throughout the compiler, which makes it impossible to reason about the recursion or deal with, e.g., recursion that is too deep for the program stack. -**Ad hoc recursion breaking**: When we do encounter circular dependencies, we have scattered checks for recursion based on a number of separate bits stashed in the AST: ``BeingTypeChecked``, ``EarlyAttrValidation``, ``ValidatingGenericSignature``, etc. Adding these checks is unprincipled: adding a new check in the wrong place tends to break working code (because the dependency is something the compiler should be able to handle), while missing a check permits infinite recursion to continue. +**Ad hoc recursion breaking**: When we do encounter circular dependencies, we have scattered checks for recursion based on a number of separate bits stashed in the AST: ``isComputingRequirementSignature()``, ``isComputingPatternBindingEntry()``, ``isComputingGenericSignature()/hasComputedGenericSignature()``, etc. Adding these checks is unprincipled: adding a new check in the wrong place tends to break working code (because the dependency is something the compiler should be able to handle), while missing a check permits infinite recursion to continue. **Type checker does too much work**: validating a declaration is all-or-nothing. It includes computing its type, but also checking redeclarations and overrides, as well as numerous other aspects that a user of that declaration might not care about. Aside from the performance impact of checking too much, this can introduce false circularities in type-checking, because the user might only need some very basic information to continue. @@ -156,7 +156,7 @@ The proposed architecture is significantly different from the current type check **Dependency graph and priority queue**: Extend the current-phase trait with an operation that enumerates the dependencies that need to be satisfied to bring a given AST node up to a particular phase. Start with ``TypeRepr`` nodes, and use the dependency graph and priority queue to satisfy all dependencies ahead of time, eliminating direct recursion from the type-resolution code path. Build circular-dependency detection within this test-bed. -**Incremental adoption of dependency graph**: Make other AST nodes (``Pattern``, ``VarDecl``, etc.) implement the phase-awareness trait, enumerating dependencies and updating their logic to perform minimal updates. Certain entry points that are used for ad hoc recursion (such as ``validateDecl``) can push/pop dependency graph and priority-queue instances, which leaves the existing ad hoc recursion checking in place but allows isolated subproblems to use the newer mechanisms. +**Incremental adoption of dependency graph**: Make other AST nodes (``Pattern``, ``VarDecl``, etc.) implement the phase-awareness trait, enumerating dependencies and updating their logic to perform minimal updates. Certain entry points that are used for ad hoc recursion can push/pop dependency graph and priority-queue instances, which leaves the existing ad hoc recursion checking in place but allows isolated subproblems to use the newer mechanisms. **Strengthen accessor assertions**: As ad hoc recursion gets eliminated from the type checker, strengthen assertions on the various AST nodes to make sure the AST node has been brought to the appropriate phase. diff --git a/docs/tools/swift.pod b/docs/tools/swift.pod index 1989ed851b255..ead511b2ad0de 100644 --- a/docs/tools/swift.pod +++ b/docs/tools/swift.pod @@ -86,7 +86,7 @@ for Swift, an open-source project, is located at L. If a bug can be reproduced only within an Xcode project or a playground, or if the bug is associated with an Apple NDA, please file a report to Apple's -bug reporter at L instead. +Feedback Assistant at L instead. =head1 SEE ALSO diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 2c2c7a65a0a37..951abc2cc3070 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -160,6 +160,7 @@ using TargetRelativeIndirectablePointer struct HeapObject; class WeakReference; +struct UnownedReference; template struct TargetMetadata; using Metadata = TargetMetadata; @@ -645,6 +646,9 @@ struct TargetMetadata { // NOTE: This *is* a box for copy-on-write existentials. OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const; + // Deallocate an out-of-line buffer box if one is present. + void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const; + /// Get the nominal type descriptor if this metadata describes a nominal type, /// or return null if it does not. ConstTargetMetadataPointer @@ -693,6 +697,8 @@ struct TargetMetadata { bool satisfiesClassConstraint() const; + bool isCanonicalStaticallySpecializedGenericMetadata() const; + #if SWIFT_OBJC_INTEROP /// Get the ObjC class object for this type if it has one, or return null if /// the type is not a class (or not a class with a class object). @@ -1344,6 +1350,34 @@ struct TargetStructMetadata : public TargetValueMetadata { return reinterpret_cast(asWords + offset); } + bool isCanonicalStaticallySpecializedGenericMetadata() const { + auto *description = getDescription(); + if (!description->isGeneric()) + return false; + + auto *trailingFlags = getTrailingFlags(); + if (trailingFlags == nullptr) + return false; + + return trailingFlags->isCanonicalStaticSpecialization(); + } + + const MetadataTrailingFlags *getTrailingFlags() const { + auto description = getDescription(); + auto flags = description->getFullGenericContextHeader() + .DefaultInstantiationPattern->PatternFlags; + if (!flags.hasTrailingFlags()) + return nullptr; + auto fieldOffset = description->FieldOffsetVectorOffset; + auto offset = + fieldOffset + + // Pad to the nearest pointer. + ((description->NumFields * sizeof(uint32_t) + sizeof(void *) - 1) / + sizeof(void *)); + auto asWords = reinterpret_cast(this); + return reinterpret_cast(asWords + offset); + } + static constexpr int32_t getGenericArgumentOffset() { return sizeof(TargetStructMetadata) / sizeof(StoredPointer); } @@ -2827,8 +2861,8 @@ struct TargetExtensionContextDescriptor final TrailingGenericContextObjects> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects>; public: /// A mangling of the `Self` type context that the extension extends. @@ -2868,10 +2902,10 @@ struct TargetAnonymousContextDescriptor final TargetMangledContextName> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - TargetMangledContextName>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + TargetMangledContextName>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3033,10 +3067,10 @@ struct TargetOpaqueTypeDescriptor final RelativeDirectPointer> { private: - using TrailingGenericContextObjects - = TrailingGenericContextObjects, - TargetGenericContextDescriptorHeader, - RelativeDirectPointer>; + using TrailingGenericContextObjects = + swift::TrailingGenericContextObjects, + TargetGenericContextDescriptorHeader, + RelativeDirectPointer>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3224,6 +3258,8 @@ class TargetGenericMetadataPatternTrailingObjects : /// metadata header (e.g. after the last members declared in StructMetadata). /// In class metadata, this section is relative to the end of the entire /// class metadata. + /// + /// See also: [pre-5.2-extra-data-zeroing] const GenericMetadataPartialPattern *getExtraDataPattern() const { assert(asSelf()->hasExtraDataPattern()); return this->template getTrailingObjects(); @@ -3569,7 +3605,7 @@ struct TargetSingletonMetadataInitialization { } /// This method can only be called from the runtime itself. It is defined - /// in MetadataCache.h. + /// in Metadata.cpp. TargetMetadata *allocate( const TargetTypeContextDescriptor *description) const; }; @@ -3797,16 +3833,16 @@ class TargetClassDescriptor final TargetObjCResilientClassStubInfo> { private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - TargetResilientSuperclass, - TargetForeignMetadataInitialization, - TargetSingletonMetadataInitialization, - TargetVTableDescriptorHeader, - TargetMethodDescriptor, - TargetOverrideTableHeader, - TargetMethodOverrideDescriptor, - TargetObjCResilientClassStubInfo>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + TargetResilientSuperclass, + TargetForeignMetadataInitialization, + TargetSingletonMetadataInitialization, + TargetVTableDescriptorHeader, + TargetMethodDescriptor, + TargetOverrideTableHeader, + TargetMethodOverrideDescriptor, + TargetObjCResilientClassStubInfo>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4114,10 +4150,10 @@ class TargetStructDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4190,10 +4226,10 @@ class TargetEnumDescriptor final private: using TrailingGenericContextObjects = - TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader, - ForeignMetadataInitialization, - SingletonMetadataInitialization>; + swift::TrailingGenericContextObjects, + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + SingletonMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index cc15eed09bb8c..6e1cc0356f35e 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -672,7 +672,7 @@ class ExistentialTypeFlags { private: enum : int_type { NumWitnessTablesMask = 0x00FFFFFFU, - ClassConstraintMask = 0x80000000U, + ClassConstraintMask = 0x80000000U, // Warning: Set if NOT class-constrained! HasSuperclassMask = 0x40000000U, SpecialProtocolMask = 0x3F000000U, SpecialProtocolShift = 24U, @@ -913,55 +913,6 @@ class TargetTupleTypeFlags { }; using TupleTypeFlags = TargetTupleTypeFlags; -/// Field types and flags as represented in a nominal type's field/case type -/// vector. -class FieldType { - typedef uintptr_t int_type; - // Type metadata is always at least pointer-aligned, so we get at least two - // low bits to stash flags. We could use three low bits on 64-bit, and maybe - // some high bits as well. - enum : int_type { - Indirect = 1, - Weak = 2, - - TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1), - }; - int_type Data; - - constexpr FieldType(int_type Data) : Data(Data) {} -public: - constexpr FieldType() : Data(0) {} - FieldType withType(const Metadata *T) const { - return FieldType((Data & ~TypeMask) | (uintptr_t)T); - } - - constexpr FieldType withIndirect(bool indirect) const { - return FieldType((Data & ~Indirect) - | (indirect ? Indirect : 0)); - } - - constexpr FieldType withWeak(bool weak) const { - return FieldType((Data & ~Weak) - | (weak ? Weak : 0)); - } - - bool isIndirect() const { - return bool(Data & Indirect); - } - - bool isWeak() const { - return bool(Data & Weak); - } - - const Metadata *getType() const { - return (const Metadata *)(Data & TypeMask); - } - - int_type getIntValue() const { - return Data; - } -}; - /// Flags for exclusivity-checking operations. enum class ExclusivityFlags : uintptr_t { Read = 0x0, @@ -1560,6 +1511,10 @@ class GenericMetadataPatternFlags : public FlagSet { /// Does this pattern have an extra-data pattern? HasExtraDataPattern = 0, + /// Do instances of this pattern have a bitset of flags that occur at the + /// end of the metadata, after the extra data if there is any? + HasTrailingFlags = 1, + // Class-specific flags. /// Does this pattern have an immediate-members pattern? @@ -1584,6 +1539,10 @@ class GenericMetadataPatternFlags : public FlagSet { hasExtraDataPattern, setHasExtraDataPattern) + FLAGSET_DEFINE_FLAG_ACCESSORS(HasTrailingFlags, + hasTrailingFlags, + setHasTrailingFlags) + FLAGSET_DEFINE_FIELD_ACCESSORS(Value_MetadataKind, Value_MetadataKind_width, MetadataKind, @@ -1716,6 +1675,30 @@ class MetadataRequest : public FlagSet { } }; +struct MetadataTrailingFlags : public FlagSet { + enum { + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation. + IsStaticSpecialization = 0, + + /// Whether this metadata is a specialization of a generic metadata pattern + /// which was created during compilation and made to be canonical by + /// modifying the metadata accessor. + IsCanonicalStaticSpecialization = 1, + }; + + explicit MetadataTrailingFlags(uint64_t bits) : FlagSet(bits) {} + constexpr MetadataTrailingFlags() {} + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsStaticSpecialization, + isStaticSpecialization, + setIsStaticSpecialization) + + FLAGSET_DEFINE_FLAG_ACCESSORS(IsCanonicalStaticSpecialization, + isCanonicalStaticSpecialization, + setIsCanonicalStaticSpecialization) +}; + /// Flags for Builtin.IntegerLiteral values. class IntegerLiteralFlags { public: diff --git a/include/swift/ABI/ValueWitness.def b/include/swift/ABI/ValueWitness.def index d07eca212993c..646deb4bd0d78 100644 --- a/include/swift/ABI/ValueWitness.def +++ b/include/swift/ABI/ValueWitness.def @@ -160,7 +160,8 @@ FUNCTION_VALUE_WITNESS(assignWithTake, MUTABLE_VALUE_TYPE, (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) -/// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases) +/// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases, +/// M *self); /// Given an instance of valid single payload enum with a payload of this /// witness table's type (e.g Optional) , get the tag of the enum. FUNCTION_VALUE_WITNESS(getEnumTagSinglePayload, @@ -169,7 +170,7 @@ FUNCTION_VALUE_WITNESS(getEnumTagSinglePayload, (IMMUTABLE_VALUE_TYPE, UINT_TYPE, TYPE_TYPE)) /// void (*storeEnumTagSinglePayload)(T* enum, UINT_TYPE whichCase, -/// UINT_TYPE emptyCases) +/// UINT_TYPE emptyCases, M *self); /// Given uninitialized memory for an instance of a single payload enum with a /// payload of this witness table's type (e.g Optional), store the /// tag. diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 95aef16c695d1..be98a06362e3c 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -22,8 +22,10 @@ #include "swift/AST/Identifier.h" #include "swift/AST/SearchPathOptions.h" #include "swift/AST/Type.h" +#include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" #include "swift/Basic/LangOptions.h" +#include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -61,6 +63,8 @@ namespace swift { class Decl; class DeclContext; class DefaultArgumentInitializer; + class DerivativeAttr; + class DifferentiableAttr; class ExtensionDecl; class ForeignRepresentationInfo; class FuncDecl; @@ -70,7 +74,6 @@ namespace swift { class LazyContextData; class LazyIterableDeclContextData; class LazyMemberLoader; - class LazyResolver; class PatternBindingDecl; class PatternBindingInitializer; class SourceFile; @@ -102,6 +105,7 @@ namespace swift { class SourceManager; class ValueDecl; class DiagnosticEngine; + class TypeChecker; class TypeCheckerDebugConsumer; struct RawComment; class DocComment; @@ -201,8 +205,9 @@ class ASTContext final { ASTContext(const ASTContext&) = delete; void operator=(const ASTContext&) = delete; - ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, - SourceManager &SourceMgr, DiagnosticEngine &Diags); + ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, + SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, + DiagnosticEngine &Diags); public: // Members that should only be used by ASTContext.cpp. @@ -213,10 +218,9 @@ class ASTContext final { void operator delete(void *Data) throw(); - static ASTContext *get(LangOptions &langOpts, + static ASTContext *get(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, - SourceManager &SourceMgr, - DiagnosticEngine &Diags); + SourceManager &SourceMgr, DiagnosticEngine &Diags); ~ASTContext(); /// Optional table of counters to report, nullptr when not collecting. @@ -228,6 +232,9 @@ class ASTContext final { /// The language options used for translation. LangOptions &LangOpts; + /// The type checker options. + TypeCheckerOptions &TypeCheckerOpts; + /// The search path options used by this AST context. SearchPathOptions &SearchPathOpts; @@ -271,6 +278,31 @@ class ASTContext final { /// Cache of remapped types (useful for diagnostics). llvm::StringMap RemappedTypes; + /// The # of times we have performed typo correction. + unsigned NumTypoCorrections = 0; + + /// The next auto-closure discriminator. This needs to be preserved + /// across invocations of both the parser and the type-checker. + unsigned NextAutoClosureDiscriminator = 0; + + /// Cached mapping from types to their associated tangent spaces. + llvm::DenseMap> AutoDiffTangentSpaces; + + /// Cache of `@differentiable` attributes keyed by parameter indices. Used to + /// diagnose duplicate `@differentiable` attributes for the same key. + llvm::DenseMap, DifferentiableAttr *> + DifferentiableAttrs; + + /// Cache of `@derivative` attributes keyed by parameter indices and + /// derivative function kind. Used to diagnose duplicate `@derivative` + /// attributes for the same key. + // TODO(TF-1042): remove `DerivativeAttrs` from `ASTContext`. Serialize + // derivative function configurations per original `AbstractFunctionDecl`. + llvm::DenseMap< + std::tuple, + llvm::SmallPtrSet> + DerivativeAttrs; + private: /// The current generation number, which reflects the number of /// times that external modules have been loaded. @@ -404,28 +436,18 @@ class ASTContext final { /// Set a new stats reporter. void setStatsReporter(UnifiedStatsReporter *stats); - /// Creates a new lazy resolver by passing the ASTContext and the other - /// given arguments to a newly-allocated instance of \c ResolverType. - /// - /// \returns true if a new lazy resolver was created, false if there was - /// already a lazy resolver registered. - template - bool createLazyResolverIfMissing(Args && ...args) { - if (getLazyResolver()) - return false; - - setLazyResolver(new ResolverType(*this, std::forward(args)...)); - return true; - } - - /// Retrieve the lazy resolver for this context. - LazyResolver *getLazyResolver() const; - private: - /// Set the lazy resolver for this context. - void setLazyResolver(LazyResolver *resolver); + friend class TypeChecker; + void installGlobalTypeChecker(TypeChecker *TC); public: + /// Returns if semantic AST queries are enabled. This generally means module + /// loading and name lookup can take place. + bool areSemanticQueriesEnabled() const; + + /// Retrieve the global \c TypeChecker instance associated with this context. + TypeChecker *getLegacyGlobalTypeChecker() const; + /// getIdentifier - Return the uniqued and AST-Context-owned version of the /// specified string. Identifier getIdentifier(StringRef Str) const; @@ -458,17 +480,13 @@ class ASTContext final { /// Retrieve the type Swift.Never. CanType getNeverType() const; - /// Retrieve the declaration of ObjectiveC.ObjCBool. - StructDecl *getObjCBoolDecl() const; - - /// Retrieve the declaration of Foundation.NSCopying. - ProtocolDecl *getNSCopyingDecl() const; - /// Retrieve the declaration of Foundation.NSError. - ClassDecl *getNSErrorDecl() const; - /// Retrieve the declaration of Foundation.NSNumber. - ClassDecl *getNSNumberDecl() const; - /// Retrieve the declaration of Foundation.NSValue. - ClassDecl *getNSValueDecl() const; +#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ + /** Retrieve the declaration of MODULE.NAME. */ \ + DECL_CLASS *get##NAME##Decl() const; \ +\ + /** Retrieve the type of MODULE.NAME. */ \ + Type get##NAME##Type() const; +#include "swift/AST/KnownObjCTypes.def" // Declare accessors for the known declarations. #define FUNC_DECL(Name, Id) \ @@ -481,6 +499,9 @@ class ASTContext final { /// Get the '+' function on two String. FuncDecl *getPlusFunctionOnString() const; + /// Get Sequence.makeIterator(). + FuncDecl *getSequenceMakeIterator() const; + /// Check whether the standard library provides all the correct /// intrinsic support for Optional. /// @@ -561,6 +582,19 @@ class ASTContext final { Type getBridgedToObjC(const DeclContext *dc, Type type, Type *bridgedValueType = nullptr) const; + /// Get the Clang type corresponding to a Swift function type. + /// + /// \param params The function parameters. + /// \param resultTy The Swift result type. + /// \param incompleteExtInfo Used to convey escaping and throwing + /// information, in case it is needed. + /// \param trueRep The actual calling convention, which must be C-compatible. + /// The calling convention in \p incompleteExtInfo is ignored. + const clang::Type * + getClangFunctionType(ArrayRef params, Type resultTy, + const FunctionType::ExtInfo incompleteExtInfo, + FunctionTypeRepresentation trueRep); + /// Determine whether the given Swift type is representable in a /// given foreign language. ForeignRepresentationInfo @@ -568,10 +602,6 @@ class ASTContext final { ForeignLanguage language, const DeclContext *dc); - /// Add a declaration that was synthesized to a per-source file list if - /// if is part of a source file. - void addSynthesizedDecl(Decl *decl); - /// Add a cleanup function to be called when the ASTContext is deallocated. void addCleanup(std::function cleanup); @@ -589,6 +619,23 @@ class ASTContext final { /// compiler for the target platform. AvailabilityContext getSwift51Availability(); + /// Get the runtime availability of + /// swift_getTypeByMangledNameInContextInMetadataState. + AvailabilityContext getTypesInAbstractMetadataStateAvailability(); + + /// Get the runtime availability of support for prespecialized generic + /// metadata. + AvailabilityContext getPrespecializedGenericMetadataAvailability(); + + /// Get the runtime availability of features introduced in the Swift 5.2 + /// compiler for the target platform. + AvailabilityContext getSwift52Availability(); + + /// Get the runtime availability of features that have been introduced in the + /// Swift compiler for future versions of the target platform. + AvailabilityContext getSwiftFutureAvailability(); + + //===--------------------------------------------------------------------===// // Diagnostics Helper functions //===--------------------------------------------------------------------===// @@ -696,12 +743,12 @@ class ASTContext final { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - bool canImportModule(std::pair ModulePath); + bool canImportModule(Located ModulePath); /// \returns a module with a given name that was already loaded. If the /// module was not loaded, returns nullptr. ModuleDecl *getLoadedModule( - ArrayRef> ModulePath) const; + ArrayRef> ModulePath) const; ModuleDecl *getLoadedModule(Identifier ModuleName) const; @@ -711,7 +758,7 @@ class ASTContext final { /// be returned. /// /// \returns The requested module, or NULL if the module cannot be found. - ModuleDecl *getModule(ArrayRef> ModulePath); + ModuleDecl *getModule(ArrayRef> ModulePath); ModuleDecl *getModuleByName(StringRef ModuleName); @@ -854,6 +901,11 @@ class ASTContext final { /// This guarantees that resulted \p names doesn't have duplicated names. void getVisibleTopLevelModuleNames(SmallVectorImpl &names) const; + /// Whether to perform typo correction given the pre-configured correction limit. + /// Increments \c NumTypoCorrections then checks this against the limit in + /// the language options. + bool shouldPerformTypoCorrection(); + private: /// Register the given generic signature builder to be used as the canonical /// generic signature builder for the given signature, if we don't already @@ -873,9 +925,9 @@ class ASTContext final { CanGenericSignature getSingleGenericParameterSignature() const; /// Retrieve a generic signature with a single type parameter conforming - /// to the given existential type. - CanGenericSignature getExistentialSignature(CanType existential, - ModuleDecl *mod); + /// to the given opened archetype. + CanGenericSignature getOpenedArchetypeSignature(CanType existential, + ModuleDecl *mod); GenericSignature getOverrideGenericSignature(const ValueDecl *base, const ValueDecl *derived); diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 757343aa08d75..f716daef40faf 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -192,7 +192,8 @@ class ASTMangler : public Mangler { std::string mangleAccessorEntityAsUSR(AccessorKind kind, const AbstractStorageDecl *decl, - StringRef USRPrefix); + StringRef USRPrefix, + bool IsStatic); std::string mangleLocalTypeDecl(const TypeDecl *type); @@ -244,6 +245,9 @@ class ASTMangler : public Mangler { unsigned appendBoundGenericArgs(DeclContext *dc, SubstitutionMap subs, bool &isFirstArgList); + + /// Append the bound generic arguments as a flat list, disregarding depth. + void appendFlatGenericArgs(SubstitutionMap subs); /// Append any retroactive conformances. void appendRetroactiveConformances(Type type); @@ -253,9 +257,9 @@ class ASTMangler : public Mangler { void appendContextOf(const ValueDecl *decl); - void appendContext(const DeclContext *ctx); + void appendContext(const DeclContext *ctx, StringRef useModuleName); - void appendModule(const ModuleDecl *module); + void appendModule(const ModuleDecl *module, StringRef useModuleName); void appendProtocolName(const ProtocolDecl *protocol, bool allowStandardSubstitution = true); diff --git a/include/swift/AST/ASTNode.h b/include/swift/AST/ASTNode.h index db6884b8472e6..39c4e0ac7d15d 100644 --- a/include/swift/AST/ASTNode.h +++ b/include/swift/AST/ASTNode.h @@ -18,6 +18,7 @@ #define SWIFT_AST_AST_NODE_H #include "llvm/ADT/PointerUnion.h" +#include "swift/Basic/Debug.h" #include "swift/AST/TypeAlignments.h" namespace llvm { @@ -62,9 +63,7 @@ namespace swift { FUNC(Decl) #undef FUNC - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; /// Whether the AST node is implicit. diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index 2bf7c1ee5452a..98d665b1fd728 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -14,6 +14,7 @@ #define SWIFT_AST_ASTPRINTER_H #include "swift/Basic/LLVM.h" +#include "swift/Basic/QuotedString.h" #include "swift/Basic/UUID.h" #include "swift/AST/Identifier.h" #include "llvm/ADT/StringRef.h" @@ -185,10 +186,15 @@ class ASTPrinter { return *this; } + ASTPrinter &operator<<(QuotedString s); + ASTPrinter &operator<<(unsigned long long N); ASTPrinter &operator<<(UUID UU); + ASTPrinter &operator<<(Identifier name); + ASTPrinter &operator<<(DeclBaseName name); ASTPrinter &operator<<(DeclName name); + ASTPrinter &operator<<(DeclNameRef name); // Special case for 'char', but not arbitrary things that convert to 'char'. template diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 9ec764bee5fe0..9146c9c4c3ab3 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -32,6 +32,7 @@ #include "swift/AST/NameLookup.h" // for DeclVisibilityKind #include "swift/AST/SimpleRequest.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/SourceManager.h" @@ -225,6 +226,10 @@ class ASTScopeImpl { #pragma mark - source range queries public: + /// Return signum of ranges. Centralize the invariant that ASTScopes use ends. + static int compare(SourceRange, SourceRange, const SourceManager &, + bool ensureDisjoint); + SourceRange getSourceRangeOfScope(bool omitAssertions = false) const; /// InterpolatedStringLiteralExprs and EditorPlaceHolders respond to @@ -330,8 +335,7 @@ class ASTScopeImpl { virtual NullablePtr addressForPrinting() const; public: - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dumpOneScopeMapLocation(std::pair lineColumn); @@ -408,7 +412,7 @@ class ASTScopeImpl { /// Entry point into ASTScopeImpl-land for lookups static llvm::SmallVector - unqualifiedLookup(SourceFile *, DeclName, SourceLoc, + unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, const DeclContext *startingContext, DeclConsumer); static Optional @@ -418,7 +422,7 @@ class ASTScopeImpl { #pragma mark - - lookup- starting point private: static const ASTScopeImpl *findStartingScopeForLookup(SourceFile *, - const DeclName name, + const DeclNameRef name, const SourceLoc where, const DeclContext *ctx); @@ -427,12 +431,12 @@ class ASTScopeImpl { protected: /// Not const because may reexpand some scopes. - const ASTScopeImpl *findInnermostEnclosingScope(SourceLoc, - NullablePtr); - const ASTScopeImpl *findInnermostEnclosingScopeImpl(SourceLoc, - NullablePtr, - SourceManager &, - ScopeCreator &); + ASTScopeImpl *findInnermostEnclosingScope(SourceLoc, + NullablePtr); + ASTScopeImpl *findInnermostEnclosingScopeImpl(SourceLoc, + NullablePtr, + SourceManager &, + ScopeCreator &); private: NullablePtr findChildContaining(SourceLoc loc, @@ -475,6 +479,10 @@ class ASTScopeImpl { /// asked. virtual Optional> computeSelfDCForParent() const; + /// Returns the context that should be used when a nested scope (e.g. a + /// closure) captures self explicitly. + virtual NullablePtr capturedSelfDC() const; + protected: /// Find either locals or members (no scope has both) /// \param history The scopes visited since the start of lookup (including @@ -563,6 +571,8 @@ class ASTSourceFileScope final : public ASTScopeImpl { void buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); + void expandFunctionBody(AbstractFunctionDecl *AFD); + const SourceFile *getSourceFile() const override; NullablePtr addressForPrinting() const override { return SF; } bool crossCheckWithAST(); @@ -686,6 +696,15 @@ class Portion { /// to compute the selfDC from the history. static NullablePtr computeSelfDC(ArrayRef history); + + /// If we find a lookup result that requires the dynamic implict self value, + /// we need to check the nested scopes to see if any closures explicitly + /// captured \c self. In that case, the appropriate selfDC is that of the + /// innermost closure which captures a \c self value from one of this type's + /// methods. + static NullablePtr + checkNestedScopesForSelfCapture(ArrayRef history, + size_t start); }; /// Behavior specific to representing the trailing where clause of a @@ -1270,8 +1289,7 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope { PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex, DeclVisibilityKind vis) : AbstractPatternEntryScope(pbDecl, entryIndex, vis), - initAsWrittenWhenCreated( - pbDecl->getPatternList()[entryIndex].getOriginalInit()) {} + initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {} virtual ~PatternEntryInitializerScope() {} protected: @@ -1444,6 +1462,13 @@ class ClosureParametersScope final : public AbstractClosureScope { std::string getClassName() const override; SourceRange getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + + /// Since explicit captures of \c self by closures enable the use of implicit + /// \c self, we need to make sure that the appropriate \c self is used as the + /// base decl for these uses (otherwise, the capture would be marked as + /// unused. \c ClosureParametersScope::capturedSelfDC() checks if we have such + /// a capture of self. + NullablePtr capturedSelfDC() const override; protected: ASTScopeImpl *expandSpecifically(ScopeCreator &) override; @@ -1543,6 +1568,41 @@ class SpecializeAttributeScope final : public ASTScopeImpl { DeclConsumer) const override; }; +/// A `@differentiable` attribute scope. +/// +/// This exists because `@differentiable` attribute may have a `where` clause +/// referring to generic parameters from some generic context. +class DifferentiableAttributeScope final : public ASTScopeImpl { +public: + DifferentiableAttr *const differentiableAttr; + ValueDecl *const attributedDeclaration; + + DifferentiableAttributeScope(DifferentiableAttr *diffAttr, ValueDecl *decl) + : differentiableAttr(diffAttr), attributedDeclaration(decl) {} + virtual ~DifferentiableAttributeScope() {} + + std::string getClassName() const override; + SourceRange + getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + NullablePtr addressForPrinting() const override { + return differentiableAttr; + } + + NullablePtr + getEnclosingAbstractStorageDecl() const override; + + NullablePtr getDeclAttributeIfAny() const override { + return differentiableAttr; + } + NullablePtr getReferrent() const override; + +protected: + ASTScopeImpl *expandSpecifically(ScopeCreator &) override; + bool lookupLocalsOrMembers(ArrayRef, + DeclConsumer) const override; + bool doesContextMatchStartingContext(const DeclContext *) const override; +}; + class SubscriptDeclScope final : public ASTScopeImpl { public: SubscriptDecl *const decl; diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index b5b5493550081..5d0ec6414610f 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -17,7 +17,9 @@ SWIFT_TYPEID(AncestryFlags) SWIFT_TYPEID(CtorInitializerKind) +SWIFT_TYPEID(FunctionBuilderBodyPreCheck) SWIFT_TYPEID(GenericSignature) +SWIFT_TYPEID(ImplicitMemberAction) SWIFT_TYPEID(ParamSpecifier) SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo) SWIFT_TYPEID(PropertyWrapperTypeInfo) @@ -25,6 +27,10 @@ SWIFT_TYPEID(Requirement) SWIFT_TYPEID(ResilienceExpansion) SWIFT_TYPEID(Type) SWIFT_TYPEID(TypePair) +SWIFT_TYPEID(TypeWitnessAndDecl) +SWIFT_TYPEID(Witness) +SWIFT_TYPEID_NAMED(ClosureExpr *, ClosureExpr) +SWIFT_TYPEID_NAMED(ConstructorDecl *, ConstructorDecl) SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr) SWIFT_TYPEID_NAMED(Decl *, Decl) SWIFT_TYPEID_NAMED(EnumDecl *, EnumDecl) @@ -33,13 +39,18 @@ SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType) SWIFT_TYPEID_NAMED(InfixOperatorDecl *, InfixOperatorDecl) SWIFT_TYPEID_NAMED(IterableDeclContext *, IterableDeclContext) SWIFT_TYPEID_NAMED(ModuleDecl *, ModuleDecl) +SWIFT_TYPEID_NAMED(NamedPattern *, NamedPattern) SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl) SWIFT_TYPEID_NAMED(OpaqueTypeDecl *, OpaqueTypeDecl) SWIFT_TYPEID_NAMED(OperatorDecl *, OperatorDecl) SWIFT_TYPEID_NAMED(Optional, PropertyWrapperMutability) +SWIFT_TYPEID_NAMED(ParamDecl *, ParamDecl) +SWIFT_TYPEID_NAMED(PatternBindingEntry *, PatternBindingEntry) SWIFT_TYPEID_NAMED(PrecedenceGroupDecl *, PrecedenceGroupDecl) SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) +SWIFT_TYPEID_NAMED(SourceFile *, SourceFile) SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl) SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl) SWIFT_TYPEID_NAMED(VarDecl *, VarDecl) +SWIFT_TYPEID(FingerprintAndMembers) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index bd2030608e7e6..6b38683242bbf 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -23,18 +23,23 @@ namespace swift { class AbstractFunctionDecl; class BraceStmt; +class ClosureExpr; +class ConstructorDecl; class CustomAttr; class Decl; class EnumDecl; +enum class FunctionBuilderBodyPreCheck : uint8_t; class GenericParamList; class GenericSignature; class GenericTypeParamType; class InfixOperatorDecl; class IterableDeclContext; class ModuleDecl; +class NamedPattern; class NominalTypeDecl; class OperatorDecl; class OpaqueTypeDecl; +class PatternBindingEntry; class ParamDecl; enum class ParamSpecifier : uint8_t; class PrecedenceGroupDecl; @@ -45,13 +50,18 @@ struct PropertyWrapperMutability; class ProtocolDecl; class Requirement; enum class ResilienceExpansion : unsigned; +class SourceFile; class Type; class ValueDecl; class VarDecl; +class Witness; class TypeAliasDecl; class Type; struct TypePair; +struct TypeWitnessAndDecl; enum class AncestryFlags : uint8_t; +enum class ImplicitMemberAction : uint8_t; +struct FingerprintAndMembers; // Define the AST type zone (zone 1) #define SWIFT_TYPEID_ZONE AST diff --git a/include/swift/AST/AccessScope.h b/include/swift/AST/AccessScope.h index a8e1444abd695..5b98f33a91099 100644 --- a/include/swift/AST/AccessScope.h +++ b/include/swift/AST/AccessScope.h @@ -15,6 +15,7 @@ #include "swift/AST/AttrKind.h" #include "swift/AST/DeclContext.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/PointerIntPair.h" namespace swift { @@ -87,7 +88,7 @@ class AccessScope { return None; } - void dump() const; + SWIFT_DEBUG_DUMP; }; } // end namespace swift diff --git a/include/swift/AST/AnyFunctionRef.h b/include/swift/AST/AnyFunctionRef.h index 760788e3c103d..bc57d7ae7ba8b 100644 --- a/include/swift/AST/AnyFunctionRef.h +++ b/include/swift/AST/AnyFunctionRef.h @@ -14,6 +14,7 @@ #define SWIFT_AST_ANY_FUNCTION_REF_H #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -52,13 +53,13 @@ class AnyFunctionRef { } } - const CaptureInfo &getCaptureInfo() const { + CaptureInfo getCaptureInfo() const { if (auto *AFD = TheFunction.dyn_cast()) return AFD->getCaptureInfo(); return TheFunction.get()->getCaptureInfo(); } - void setCaptureInfo(const CaptureInfo &captures) const { + void setCaptureInfo(CaptureInfo captures) const { if (auto *AFD = TheFunction.dyn_cast()) { AFD->setCaptureInfo(captures); return; @@ -177,8 +178,7 @@ class AnyFunctionRef { #pragma warning(push) #pragma warning(disable: 4996) #endif - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger") { + SWIFT_DEBUG_DUMP { if (auto afd = TheFunction.dyn_cast()) { return afd->dump(); } @@ -208,6 +208,23 @@ class AnyFunctionRef { llvm_unreachable("unexpected AnyFunctionRef representation"); } + friend bool operator==(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction == rhs.TheFunction; + } + + friend bool operator!=(AnyFunctionRef lhs, AnyFunctionRef rhs) { + return lhs.TheFunction != rhs.TheFunction; + } + + friend llvm::hash_code hash_value(AnyFunctionRef fn) { + using llvm::hash_value; + return hash_value(fn.TheFunction.getOpaqueValue()); + } + + friend SourceLoc extractNearestSourceLoc(AnyFunctionRef fn) { + return fn.getLoc(); + } + private: ArrayRef getYieldResultsImpl(SmallVectorImpl &buffer, @@ -235,6 +252,8 @@ class AnyFunctionRef { #pragma warning(pop) #endif +void simple_display(llvm::raw_ostream &out, AnyFunctionRef fn); + } // namespace swift namespace llvm { diff --git a/include/swift/AST/AnyRequest.h b/include/swift/AST/AnyRequest.h index 0b6b9b5f4cfa0..63332fd68e7a6 100644 --- a/include/swift/AST/AnyRequest.h +++ b/include/swift/AST/AnyRequest.h @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// // -// This file defines the AnyRequest class, which describes a stored request. +// This file defines type-erasing wrappers for requests used by the Evaluator +// class. // //===----------------------------------------------------------------------===// @@ -21,7 +22,6 @@ #include "swift/Basic/TypeID.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include namespace llvm { @@ -35,261 +35,410 @@ using llvm::hash_value; class DiagnosticEngine; -/// Stores a request (for the \c Evaluator class) of any kind. -/// -/// Requests must be value types and provide the following API to be stored in -/// an \c AnyRequest instance: -/// -/// - Copy constructor -/// - Equality operator (==) -/// - Hashing support (hash_value) -/// - TypeID support (see swift/Basic/TypeID.h) -/// - Display support (free function): -/// void simple_display(llvm::raw_ostream &, const T &); -/// - Cycle diagnostics operations: -/// void diagnoseCycle(DiagnosticEngine &diags) const; -/// void noteCycleStep(DiagnosticEngine &diags) const; -/// -class AnyRequest { - friend llvm::DenseMapInfo; - - static hash_code hashForHolder(uint64_t typeID, hash_code requestHash) { - return hash_combine(typeID, requestHash); - } - - /// Abstract base class used to hold the specific request kind. - class HolderBase : public llvm::RefCountedBase { - public: - /// The type ID of the request being stored. - const uint64_t typeID; - - /// Hash value for the request itself. - const hash_code hash; - - protected: - /// Initialize base with type ID and hash code. - HolderBase(uint64_t typeID, hash_code hash) - : typeID(typeID), hash(AnyRequest::hashForHolder(typeID, hash)) { } - - public: - virtual ~HolderBase(); - - /// Determine whether this request is equivalent to the \c other - /// request. - virtual bool equals(const HolderBase &other) const = 0; - - /// Display. - virtual void display(llvm::raw_ostream &out) const = 0; - - /// Diagnose a cycle detected for this request. - virtual void diagnoseCycle(DiagnosticEngine &diags) const = 0; - - /// Note that this request is part of a cycle. - virtual void noteCycleStep(DiagnosticEngine &diags) const = 0; - - /// Retrieve the nearest source location to which this request applies. - virtual SourceLoc getNearestLoc() const = 0; - }; - - /// Holds a value that can be used as a request input/output. +/// A collection of functions that describe how to perform operations for a +/// specific concrete request, obtained using +/// \c AnyRequestVTable::get(). +struct AnyRequestVTable { template - class Holder final : public HolderBase { - public: - const Request request; - - Holder(const Request &request) - : HolderBase(TypeID::value, hash_value(request)), - request(request) { } - - Holder(Request &&request) - : HolderBase(TypeID::value, hash_value(request)), - request(std::move(request)) { } - - virtual ~Holder() { } - - /// Determine whether this request is equivalent to another. - /// - /// The caller guarantees that the typeIDs are the same. - virtual bool equals(const HolderBase &other) const override { - assert(typeID == other.typeID && "Caller should match typeIDs"); - return request == static_cast &>(other).request; + struct Impl { + static void copy(const void *input, void *output) { + new (output) Request(*static_cast(input)); } - - /// Display. - virtual void display(llvm::raw_ostream &out) const override { - simple_display(out, request); + static hash_code getHash(const void *ptr) { + return hash_value(*static_cast(ptr)); } - - /// Diagnose a cycle detected for this request. - virtual void diagnoseCycle(DiagnosticEngine &diags) const override { - request.diagnoseCycle(diags); + static void deleter(void *ptr) { + static_cast(ptr)->~Request(); } - - /// Note that this request is part of a cycle. - virtual void noteCycleStep(DiagnosticEngine &diags) const override { - request.noteCycleStep(diags); + static bool isEqual(const void *lhs, const void *rhs) { + return *static_cast(lhs) == + *static_cast(rhs); } - - /// Retrieve the nearest source location to which this request applies. - virtual SourceLoc getNearestLoc() const override { - return request.getNearestLoc(); + static void simpleDisplay(const void *ptr, llvm::raw_ostream &out) { + simple_display(out, *static_cast(ptr)); + } + static void diagnoseCycle(const void *ptr, DiagnosticEngine &diags) { + static_cast(ptr)->diagnoseCycle(diags); + } + static void noteCycleStep(const void *ptr, DiagnosticEngine &diags) { + static_cast(ptr)->noteCycleStep(diags); + } + static SourceLoc getNearestLoc(const void *ptr) { + return static_cast(ptr)->getNearestLoc(); } }; - /// FIXME: Inefficient. Use the low bits. - enum class StorageKind { + const uint64_t typeID; + const size_t requestSize; + const std::function copy; + const std::function getHash; + const std::function deleter; + const std::function isEqual; + const std::function simpleDisplay; + const std::function diagnoseCycle; + const std::function noteCycleStep; + const std::function getNearestLoc; + + template + static const AnyRequestVTable *get() { + static const AnyRequestVTable vtable = { + TypeID::value, + sizeof(Request), + &Impl::copy, + &Impl::getHash, + &Impl::deleter, + &Impl::isEqual, + &Impl::simpleDisplay, + &Impl::diagnoseCycle, + &Impl::noteCycleStep, + &Impl::getNearestLoc, + }; + return &vtable; + } +}; + +/// Base class for request type-erasing wrappers. +template +class AnyRequestBase { + friend llvm::DenseMapInfo; + +protected: + static hash_code hashForHolder(uint64_t typeID, hash_code requestHash) { + return hash_combine(typeID, requestHash); + } + + enum class StorageKind : uint8_t { Normal, Empty, Tombstone, - } storageKind = StorageKind::Normal; + }; + + /// The vtable and storage kind. + llvm::PointerIntPair vtableAndKind; - /// The data stored in this value. - llvm::IntrusiveRefCntPtr stored; + StorageKind getStorageKind() const { return vtableAndKind.getInt(); } - AnyRequest(StorageKind storageKind) : storageKind(storageKind) { - assert(storageKind != StorageKind::Normal); + /// Whether this object is storing a value, and is not empty or a tombstone. + bool hasStorage() const { + switch (getStorageKind()) { + case StorageKind::Empty: + case StorageKind::Tombstone: + return false; + case StorageKind::Normal: + return true; + } + llvm_unreachable("Unhandled case in switch"); } -public: - AnyRequest(const AnyRequest &other) = default; - AnyRequest &operator=(const AnyRequest &other) = default; + /// Retrieve the vtable to perform operations on the type-erased request. + const AnyRequestVTable *getVTable() const { + assert(hasStorage() && "Shouldn't be querying empty or tombstone"); + return vtableAndKind.getPointer(); + } - AnyRequest(AnyRequest &&other) - : storageKind(other.storageKind), stored(std::move(other.stored)) { - other.storageKind = StorageKind::Empty; + AnyRequestBase(const AnyRequestVTable *vtable, StorageKind storageKind) { + vtableAndKind.setPointer(vtable); + vtableAndKind.setInt(storageKind); + assert((bool)vtable == hasStorage() && "Must have a vtable with storage"); } - AnyRequest &operator=(AnyRequest &&other) { - storageKind = other.storageKind; - stored = std::move(other.stored); - other.storageKind = StorageKind::Empty; - other.stored = nullptr; + AnyRequestBase(const AnyRequestBase &other) { + vtableAndKind = other.vtableAndKind; + } + AnyRequestBase &operator=(const AnyRequestBase &other) { + vtableAndKind = other.vtableAndKind; return *this; } - // Create a local template typename `ValueType` in the template specialization - // so that we can refer to it in the SFINAE condition as well as the body of - // the template itself. The SFINAE condition allows us to remove this - // constructor from candidacy when evaluating explicit construction with an - // instance of `AnyRequest`. If we do not do so, we will find ourselves with - // ambiguity with this constructor and the defined move constructor above. - /// Construct a new instance with the given value. - template ::type>::type, - typename = typename std::enable_if< - !std::is_same::value>::type> - explicit AnyRequest(T &&value) : storageKind(StorageKind::Normal) { - stored = llvm::IntrusiveRefCntPtr( - new Holder(std::forward(value))); +private: + Derived &asDerived() { + return *static_cast(this); + } + const Derived &asDerived() const { + return *static_cast(this); } + const void *getRawStorage() const { return asDerived().getRawStorage(); } + +public: /// Cast to a specific (known) type. template const Request &castTo() const { - assert(stored->typeID == TypeID::value && "wrong type in cast"); - return static_cast *>(stored.get())->request; + assert(getVTable()->typeID == TypeID::value && + "Wrong type in cast"); + return *static_cast(getRawStorage()); } /// Try casting to a specific (known) type, returning \c nullptr on /// failure. template const Request *getAs() const { - if (stored->typeID != TypeID::value) + if (getVTable()->typeID != TypeID::value) return nullptr; - return &static_cast *>(stored.get())->request; + return static_cast(getRawStorage()); } /// Diagnose a cycle detected for this request. void diagnoseCycle(DiagnosticEngine &diags) const { - stored->diagnoseCycle(diags); + getVTable()->diagnoseCycle(getRawStorage(), diags); } /// Note that this request is part of a cycle. void noteCycleStep(DiagnosticEngine &diags) const { - stored->noteCycleStep(diags); + getVTable()->noteCycleStep(getRawStorage(), diags); } /// Retrieve the nearest source location to which this request applies. SourceLoc getNearestLoc() const { - return stored->getNearestLoc(); + return getVTable()->getNearestLoc(getRawStorage()); } /// Compare two instances for equality. - friend bool operator==(const AnyRequest &lhs, const AnyRequest &rhs) { - if (lhs.storageKind != rhs.storageKind) { + friend bool operator==(const AnyRequestBase &lhs, + const AnyRequestBase &rhs) { + // If the storage kinds don't match, we're done. + if (lhs.getStorageKind() != rhs.getStorageKind()) return false; - } - if (lhs.storageKind != StorageKind::Normal) + // If the storage kinds do match, but there's no storage, they're trivially + // equal. + if (!lhs.hasStorage()) return true; - if (lhs.stored->typeID != rhs.stored->typeID) + // Must be storing the same kind of request. + if (lhs.getVTable()->typeID != rhs.getVTable()->typeID) return false; - return lhs.stored->equals(*rhs.stored); + return lhs.getVTable()->isEqual(lhs.getRawStorage(), rhs.getRawStorage()); } - friend bool operator!=(const AnyRequest &lhs, const AnyRequest &rhs) { + friend bool operator!=(const Derived &lhs, const Derived &rhs) { return !(lhs == rhs); } - friend hash_code hash_value(const AnyRequest &any) { - if (any.storageKind != StorageKind::Normal) + friend hash_code hash_value(const AnyRequestBase &req) { + // If there's no storage, return a trivial hash value. + if (!req.hasStorage()) return 1; - return any.stored->hash; + auto reqHash = req.getVTable()->getHash(req.getRawStorage()); + return hashForHolder(req.getVTable()->typeID, reqHash); } - friend void simple_display(llvm::raw_ostream &out, const AnyRequest &any) { - any.stored->display(out); + friend void simple_display(llvm::raw_ostream &out, + const AnyRequestBase &req) { + req.getVTable()->simpleDisplay(req.getRawStorage(), out); } +}; - /// Return the result of calling simple_display as a string. - std::string getAsString() const; +/// Provides a view onto a request that is stored on the stack. Objects of this +/// class must not outlive the request they reference. +/// +/// Requests must be value types and provide the following API: +/// +/// - Copy constructor +/// - Equality operator (==) +/// - Hashing support (hash_value) +/// - TypeID support (see swift/Basic/TypeID.h) +/// - Display support (free function): +/// void simple_display(llvm::raw_ostream &, const T &); +/// - Cycle diagnostics operations: +/// void diagnoseCycle(DiagnosticEngine &diags) const; +/// void noteCycleStep(DiagnosticEngine &diags) const; +/// +class ActiveRequest final : public AnyRequestBase { + template + friend class AnyRequestBase; + + friend class AnyRequest; + friend llvm::DenseMapInfo; + + /// Pointer to the request stored on the stack. + const void *storage; + + /// Creates an \c ActiveRequest without storage. + explicit ActiveRequest(StorageKind storageKind) + : AnyRequestBase(/*vtable*/ nullptr, storageKind) {} - static AnyRequest getEmptyKey() { - return AnyRequest(StorageKind::Empty); + const void *getRawStorage() const { return storage; } + +public: + /// Creates a new \c ActiveRequest referencing a concrete request on the + /// stack. + template + explicit ActiveRequest(const Request &request) + : AnyRequestBase(AnyRequestVTable::get(), StorageKind::Normal) { + storage = &request; } +}; - static AnyRequest getTombstoneKey() { - return AnyRequest(StorageKind::Tombstone); +/// Stores a request (for the \c Evaluator class) of any kind. Unlike +/// \c ActiveRequest, this wrapper has ownership of the underlying request. +/// +/// Requests must be value types and provide the following API to be stored in +/// an \c AnyRequest instance: +/// +/// - Copy constructor +/// - Equality operator (==) +/// - Hashing support (hash_value) +/// - TypeID support (see swift/Basic/TypeID.h) +/// - Display support (free function): +/// void simple_display(llvm::raw_ostream &, const T &); +/// - Cycle diagnostics operations: +/// void diagnoseCycle(DiagnosticEngine &diags) const; +/// void noteCycleStep(DiagnosticEngine &diags) const; +/// +class AnyRequest final : public AnyRequestBase { + template + friend class AnyRequestBase; + + friend llvm::DenseMapInfo; + + /// Pointer to the request on the heap. + void *storage; + + /// Creates an \c AnyRequest without storage. + explicit AnyRequest(StorageKind storageKind) + : AnyRequestBase(/*vtable*/ nullptr, storageKind) {} + + const void *getRawStorage() const { return storage; } + + /// Whether this wrapper is storing the same underlying request as an + /// \c ActiveRequest. + bool isStorageEqual(const ActiveRequest &other) const { + // If either wrapper isn't storing anything, just return false. + if (!hasStorage() || !other.hasStorage()) + return false; + + if (getVTable()->typeID != other.getVTable()->typeID) + return false; + + return getVTable()->isEqual(getRawStorage(), other.getRawStorage()); + } + +public: + AnyRequest(const AnyRequest &other) : AnyRequestBase(other) { + if (hasStorage()) { + // For now, just allocate a new buffer and copy across the request. This + // should only happen when performing operations on the (disabled by + // default) dependency graph. + storage = llvm::safe_malloc(getVTable()->requestSize); + getVTable()->copy(other.storage, storage); + } } + AnyRequest &operator=(const AnyRequest &other) { + if (&other != this) { + this->~AnyRequest(); + new (this) AnyRequest(other); + } + return *this; + } + + AnyRequest(AnyRequest &&other) : AnyRequestBase(other), + storage(other.storage) { + new (&other) AnyRequest(StorageKind::Empty); + } + + AnyRequest &operator=(AnyRequest &&other) { + if (&other != this) { + this->~AnyRequest(); + new (this) AnyRequest(std::move(other)); + } + return *this; + } + + // Create a local template typename `Request` in the template specialization + // so that we can refer to it in the SFINAE condition as well as the body of + // the template itself. The SFINAE condition allows us to remove this + // constructor from candidacy when evaluating explicit construction with an + // instance of `AnyRequest`. If we do not do so, we will find ourselves with + // ambiguity with this constructor and the defined move constructor above. + /// Construct a new instance with the given value. + template , + typename = typename std::enable_if< + !std::is_same::value>::type> + explicit AnyRequest(T &&request) + : AnyRequestBase(AnyRequestVTable::get(), StorageKind::Normal) { + storage = llvm::safe_malloc(sizeof(Request)); + new (storage) Request(std::forward(request)); + } + + /// Construct an \c AnyRequest from an \c ActiveRequest, allowing the + /// underlying request to persist. + explicit AnyRequest(const ActiveRequest &req) + : AnyRequestBase(req.getVTable(), StorageKind::Normal) { + assert(req.hasStorage()); + storage = llvm::safe_malloc(getVTable()->requestSize); + getVTable()->copy(req.storage, storage); + } + + ~AnyRequest() { + if (hasStorage()) { + getVTable()->deleter(storage); + std::free(storage); + } + } + + /// Return the result of calling simple_display as a string. + std::string getAsString() const; }; } // end namespace swift namespace llvm { + template<> + struct DenseMapInfo { + using ActiveRequest = swift::ActiveRequest; + static inline ActiveRequest getEmptyKey() { + return ActiveRequest(ActiveRequest::StorageKind::Empty); + } + static inline ActiveRequest getTombstoneKey() { + return ActiveRequest(ActiveRequest::StorageKind::Tombstone); + } + static unsigned getHashValue(const ActiveRequest &request) { + return hash_value(request); + } + static bool isEqual(const ActiveRequest &lhs, const ActiveRequest &rhs) { + return lhs == rhs; + } + }; + template<> struct DenseMapInfo { - static inline swift::AnyRequest getEmptyKey() { - return swift::AnyRequest::getEmptyKey(); + using AnyRequest = swift::AnyRequest; + using ActiveRequest = swift::ActiveRequest; + + static inline AnyRequest getEmptyKey() { + return AnyRequest(AnyRequest::StorageKind::Empty); } static inline swift::AnyRequest getTombstoneKey() { - return swift::AnyRequest::getTombstoneKey(); + return AnyRequest(AnyRequest::StorageKind::Tombstone); } - static unsigned getHashValue(const swift::AnyRequest &request) { + static unsigned getHashValue(const AnyRequest &request) { return hash_value(request); } template static unsigned getHashValue(const Request &request) { - return swift::AnyRequest::hashForHolder(swift::TypeID::value, - hash_value(request)); + return AnyRequest::hashForHolder(swift::TypeID::value, + hash_value(request)); } - static bool isEqual(const swift::AnyRequest &lhs, - const swift::AnyRequest &rhs) { + static unsigned getHashValue(const ActiveRequest &request) { + return hash_value(request); + } + static bool isEqual(const AnyRequest &lhs, const AnyRequest &rhs) { return lhs == rhs; } template - static bool isEqual(const Request &lhs, - const swift::AnyRequest &rhs) { - if (rhs == getEmptyKey() || rhs == getTombstoneKey()) - return false; - const Request *rhsRequest = rhs.getAs(); - if (!rhsRequest) + static bool isEqual(const Request &lhs, const AnyRequest &rhs) { + if (!rhs.hasStorage()) return false; - return lhs == *rhsRequest; + + auto *rhsRequest = rhs.getAs(); + return rhsRequest && lhs == *rhsRequest; + } + static bool isEqual(const ActiveRequest &lhs, const AnyRequest &rhs) { + return rhs.isStorageEqual(lhs); } }; diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index ebcc48a7ff67e..e5ed8dd43f19e 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -51,6 +51,8 @@ TYPE_ATTR(autoclosure) TYPE_ATTR(convention) TYPE_ATTR(noescape) TYPE_ATTR(escaping) +TYPE_ATTR(differentiable) +TYPE_ATTR(noDerivative) // SIL-specific attributes TYPE_ATTR(block_storage) @@ -202,7 +204,7 @@ DECL_ATTR(inline, Inline, ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 20) DECL_ATTR(_semantics, Semantics, - OnAbstractFunction | OnSubscript | + OnAbstractFunction | OnSubscript | OnNominalType | OnVar | AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 21) @@ -496,12 +498,50 @@ DECL_ATTR(_projectedValueProperty, ProjectedValueProperty, OnVar | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 89) +SIMPLE_DECL_ATTR(_nonEphemeral, NonEphemeral, + OnParam | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + 90) + +DECL_ATTR(differentiable, Differentiable, + OnAccessor | OnConstructor | OnFunc | OnVar | OnSubscript | LongAttribute | + AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 91) + +SIMPLE_DECL_ATTR(_hasMissingDesignatedInitializers, + HasMissingDesignatedInitializers, OnClass | UserInaccessible | NotSerialized | + APIBreakingToAdd | ABIBreakingToAdd | APIStableToRemove | ABIStableToRemove, + 92) + +SIMPLE_DECL_ATTR(_inheritsConvenienceInitializers, + InheritsConvenienceInitializers, OnClass | UserInaccessible | NotSerialized | + APIStableToAdd | ABIStableToAdd | APIBreakingToRemove | ABIBreakingToRemove, + 93) SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction, OnFunc | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 95) +DECL_ATTR(_originallyDefinedIn, OriginallyDefinedIn, + OnNominalType | OnFunc | OnVar | OnExtension | UserInaccessible | + AllowMultipleAttributes | + ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + 96) + +DECL_ATTR(derivative, Derivative, + OnFunc | LongAttribute | AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 97) + +// NOTE: 98 is unused + +DECL_ATTR(transpose, Transpose, + OnFunc | LongAttribute | AllowMultipleAttributes | + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + 99) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 42ada178624aa..476b46e6a1483 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -17,6 +17,7 @@ #ifndef SWIFT_ATTR_H #define SWIFT_ATTR_H +#include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/UUID.h" @@ -24,8 +25,10 @@ #include "swift/Basic/Range.h" #include "swift/Basic/OptimizationMode.h" #include "swift/Basic/Version.h" +#include "swift/Basic/Located.h" #include "swift/AST/Identifier.h" #include "swift/AST/AttrKind.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/KnownProtocols.h" @@ -50,6 +53,7 @@ class FuncDecl; class ClassDecl; class GenericFunctionType; class LazyConformanceLoader; +class LazyMemberLoader; class PatternBindingInitializer; class TrailingWhereClause; @@ -62,8 +66,25 @@ class TypeAttributes { /// AtLoc - This is the location of the first '@' in the attribute specifier. /// If this is an empty attribute specifier, then this will be an invalid loc. SourceLoc AtLoc; - Optional convention = None; - Optional conventionWitnessMethodProtocol = None; + + struct Convention { + StringRef Name = {}; + DeclNameRef WitnessMethodProtocol = {}; + Located ClangType = Located(StringRef(), {}); + /// Convenience factory function to create a Swift convention. + /// + /// Don't use this function if you are creating a C convention as you + /// probably need a ClangType field as well. + static Convention makeSwiftConvention(StringRef name) { + return {name, DeclNameRef(), Located("", {})}; + } + }; + + Optional ConventionArguments; + + // Indicates whether the type's '@differentiable' attribute has a 'linear' + // argument. + bool linear = false; // For an opened existential type, the known ID. Optional OpenedID; @@ -79,7 +100,15 @@ class TypeAttributes { TypeAttributes() {} bool isValid() const { return AtLoc.isValid(); } - + + bool isLinear() const { + assert( + !linear || + (linear && has(TAK_differentiable)) && + "Linear shouldn't have been true if there's no `@differentiable`"); + return linear; + } + void clearAttribute(TypeAttrKind A) { AttrLocs[A] = SourceLoc(); } @@ -120,8 +149,19 @@ class TypeAttributes { return true; } - bool hasConvention() const { return convention.hasValue(); } - StringRef getConvention() const { return *convention; } + bool hasConvention() const { return ConventionArguments.hasValue(); } + + /// Returns the primary calling convention string. + /// + /// Note: For C conventions, this may not represent the full convention. + StringRef getConventionName() const { + return ConventionArguments.getValue().Name; + } + + /// Show the string enclosed between @convention(..)'s parentheses. + /// + /// For example, @convention(foo, bar) will give the string "foo, bar". + void getConventionArguments(SmallVectorImpl &buffer) const; bool hasOwnership() const { return getOwnership() != ReferenceOwnership::Strong; @@ -151,7 +191,7 @@ class TypeAttributes { static const char *getAttrName(TypeAttrKind kind); }; -class AttributeBase { +class alignas(1 << AttrAlignInBits) AttributeBase { public: /// The location of the '@'. const SourceLoc AtLoc; @@ -996,18 +1036,32 @@ class DynamicReplacementAttr final : public DeclAttribute, private llvm::TrailingObjects { friend TrailingObjects; + friend class DynamicallyReplacedDeclRequest; - DeclName ReplacedFunctionName; - AbstractFunctionDecl *ReplacedFunction; + DeclNameRef ReplacedFunctionName; + LazyMemberLoader *Resolver = nullptr; + uint64_t ResolverContextData; /// Create an @_dynamicReplacement(for:) attribute written in the source. DynamicReplacementAttr(SourceLoc atLoc, SourceRange baseRange, - DeclName replacedFunctionName, SourceRange parenRange); + DeclNameRef replacedFunctionName, + SourceRange parenRange); - explicit DynamicReplacementAttr(DeclName name) + DynamicReplacementAttr(DeclNameRef name, AbstractFunctionDecl *f) : DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(), /*Implicit=*/false), - ReplacedFunctionName(name), ReplacedFunction(nullptr) { + ReplacedFunctionName(name), + Resolver(nullptr), ResolverContextData(0) { + Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false; + } + + DynamicReplacementAttr(DeclNameRef name, + LazyMemberLoader *Resolver = nullptr, + uint64_t Data = 0) + : DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(), + /*Implicit=*/false), + ReplacedFunctionName(name), + Resolver(Resolver), ResolverContextData(Data) { Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false; } @@ -1028,28 +1082,21 @@ class DynamicReplacementAttr final public: static DynamicReplacementAttr * create(ASTContext &Context, SourceLoc AtLoc, SourceLoc DynReplLoc, - SourceLoc LParenLoc, DeclName replacedFunction, SourceLoc RParenLoc); + SourceLoc LParenLoc, DeclNameRef replacedFunction, SourceLoc RParenLoc); static DynamicReplacementAttr *create(ASTContext &ctx, - DeclName replacedFunction); + DeclNameRef replacedFunction, + AbstractFunctionDecl *replacedFuncDecl); static DynamicReplacementAttr *create(ASTContext &ctx, - DeclName replacedFunction, - AbstractFunctionDecl *replacedFuncDecl); + DeclNameRef replacedFunction, + LazyMemberLoader *Resolver, + uint64_t Data); - DeclName getReplacedFunctionName() const { + DeclNameRef getReplacedFunctionName() const { return ReplacedFunctionName; } - AbstractFunctionDecl *getReplacedFunction() const { - return ReplacedFunction; - } - - void setReplacedFunction(AbstractFunctionDecl *f) { - assert(ReplacedFunction == nullptr); - ReplacedFunction = f; - } - /// Retrieve the location of the opening parentheses, if there is one. SourceLoc getLParenLoc() const; @@ -1532,6 +1579,386 @@ class ProjectedValuePropertyAttr : public DeclAttribute { } }; +/// Describe a symbol was originally defined in another module. For example, given +/// \code +/// @_originallyDefinedIn(module: "Original", OSX 10.15) var foo: Int +/// \endcode +/// +/// Where variable Foo has originally defined in another module called Original prior to OSX 10.15 +class OriginallyDefinedInAttr: public DeclAttribute { +public: + OriginallyDefinedInAttr(SourceLoc AtLoc, SourceRange Range, + StringRef OriginalModuleName, + PlatformKind Platform, + const llvm::VersionTuple MovedVersion, + bool Implicit) + : DeclAttribute(DAK_OriginallyDefinedIn, AtLoc, Range, Implicit), + OriginalModuleName(OriginalModuleName), + Platform(Platform), + MovedVersion(MovedVersion) {} + + // The original module name. + const StringRef OriginalModuleName; + + /// The platform of the symbol. + const PlatformKind Platform; + + /// Indicates when the symbol was moved here. + const llvm::VersionTuple MovedVersion; + + struct ActiveVersion { + StringRef ModuleName; + PlatformKind Platform; + bool IsSimulator; + llvm::VersionTuple Version; + }; + + /// Returns non-optional if this attribute is active given the current platform. + /// The value provides more details about the active platform. + Optional isActivePlatform(const ASTContext &ctx) const; + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_OriginallyDefinedIn; + } +}; + +/// A declaration name with location. +struct DeclNameRefWithLoc { + DeclNameRef Name; + DeclNameLoc Loc; +}; + +/// Attribute that marks a function as differentiable and optionally specifies +/// custom associated derivative functions: 'jvp' and 'vjp'. +/// +/// Examples: +/// @differentiable(jvp: jvpFoo where T : FloatingPoint) +/// @differentiable(wrt: (self, x, y), jvp: jvpFoo) +class DifferentiableAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + friend class DifferentiableAttributeTypeCheckRequest; + + /// The declaration on which the `@differentiable` attribute is declared. + /// May not be a valid declaration for `@differentiable` attributes. + /// Resolved during parsing and deserialization. + Decl *OriginalDeclaration = nullptr; + /// Whether this function is linear (optional). + bool Linear; + /// The number of parsed differentiability parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The JVP function. + Optional JVP; + /// The VJP function. + Optional VJP; + /// The JVP function (optional), resolved by the type checker if JVP name is + /// specified. + FuncDecl *JVPFunction = nullptr; + /// The VJP function (optional), resolved by the type checker if VJP name is + /// specified. + FuncDecl *VJPFunction = nullptr; + /// The differentiability parameter indices, resolved by the type checker. + /// The bit stores whether the parameter indices have been computed. + /// + /// Note: it is necessary to use a bit instead of `nullptr` parameter indices + /// to represent "parameter indices not yet type-checked" because invalid + /// attributes have `nullptr` parameter indices but have been type-checked. + llvm::PointerIntPair ParameterIndicesAndBit; + /// The trailing where clause (optional). + TrailingWhereClause *WhereClause = nullptr; + /// The generic signature for autodiff associated functions. Resolved by the + /// type checker based on the original function's generic signature and the + /// attribute's where clause requirements. This is set only if the attribute + /// has a where clause. + GenericSignature DerivativeGenericSignature; + + explicit DifferentiableAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, + ArrayRef parameters, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause); + + explicit DifferentiableAttr(Decl *original, bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, + IndexSubset *parameterIndices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenericSignature); + +public: + static DifferentiableAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef params, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause); + + static DifferentiableAttr *create(AbstractFunctionDecl *original, + bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, + IndexSubset *parameterIndices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig); + + Decl *getOriginalDeclaration() const { return OriginalDeclaration; } + + /// Sets the original declaration on which this attribute is declared. + /// Should only be used by parsing and deserialization. + void setOriginalDeclaration(Decl *originalDeclaration); + + /// Get the optional 'jvp:' function name and location. + /// Use this instead of `getJVPFunction` to check whether the attribute has a + /// registered JVP. + Optional getJVP() const { return JVP; } + + /// Get the optional 'vjp:' function name and location. + /// Use this instead of `getVJPFunction` to check whether the attribute has a + /// registered VJP. + Optional getVJP() const { return VJP; } + +private: + /// Returns true if the given `@differentiable` attribute has been + /// type-checked. + bool hasBeenTypeChecked() const; + +public: + IndexSubset *getParameterIndices() const; + void setParameterIndices(IndexSubset *parameterIndices); + + /// The parsed differentiability parameters, i.e. the list of parameters + /// specified in 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + bool isLinear() const { return Linear; } + + TrailingWhereClause *getWhereClause() const { return WhereClause; } + + GenericSignature getDerivativeGenericSignature() const { + return DerivativeGenericSignature; + } + void setDerivativeGenericSignature(GenericSignature derivativeGenSig) { + DerivativeGenericSignature = derivativeGenSig; + } + + FuncDecl *getJVPFunction() const { return JVPFunction; } + void setJVPFunction(FuncDecl *decl); + FuncDecl *getVJPFunction() const { return VJPFunction; } + void setVJPFunction(FuncDecl *decl); + + /// Get the derivative generic environment for the given `@differentiable` + /// attribute and original function. + GenericEnvironment * + getDerivativeGenericEnvironment(AbstractFunctionDecl *original) const; + + // Print the attribute to the given stream. + // If `omitWrtClause` is true, omit printing the `wrt:` clause. + // If `omitDerivativeFunctions` is true, omit printing derivative functions. + void print(llvm::raw_ostream &OS, const Decl *D, bool omitWrtClause = false, + bool omitDerivativeFunctions = false) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Differentiable; + } +}; + +/// The `@derivative(of:)` attribute registers a function as a derivative of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. +/// +/// The `@derivative(of:)` attribute also has an optional `wrt:` clause +/// specifying the parameters that are differentiated "with respect to", i.e. +/// the differentiability parameters. The differentiability parameters must +/// conform to the `Differentiable` protocol. +/// +/// If the `wrt:` clause is unspecified, the differentiability parameters are +/// inferred to be all parameters that conform to `Differentiable`. +/// +/// `@derivative(of:)` attribute type-checking verifies that the type of the +/// derivative function declaration is consistent with the type of the +/// referenced original declaration and the differentiability parameters. +/// +/// Examples: +/// @derivative(of: sin(_:)) +/// @derivative(of: +, wrt: (lhs, rhs)) +class DerivativeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The base type for the referenced original declaration. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. + TypeRepr *BaseTypeRepr; + /// The original function name. + DeclNameRefWithLoc OriginalFunctionName; + /// The original function declaration, resolved by the type checker. + AbstractFunctionDecl *OriginalFunction = nullptr; + /// The number of parsed differentiability parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The differentiability parameter indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + /// The derivative function kind (JVP or VJP), resolved by the type checker. + Optional Kind = None; + + explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, + ArrayRef params); + + explicit DerivativeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + +public: + static DerivativeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, + DeclNameRefWithLoc original, + ArrayRef params); + + static DerivativeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, + DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } + DeclNameRefWithLoc getOriginalFunctionName() const { + return OriginalFunctionName; + } + AbstractFunctionDecl *getOriginalFunction() const { + return OriginalFunction; + } + void setOriginalFunction(AbstractFunctionDecl *decl) { + OriginalFunction = decl; + } + + AutoDiffDerivativeFunctionKind getDerivativeKind() const { + assert(Kind && "Derivative function kind has not yet been resolved"); + return *Kind; + } + void setDerivativeKind(AutoDiffDerivativeFunctionKind kind) { Kind = kind; } + + /// The parsed differentiability parameters, i.e. the list of parameters + /// specified in 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *parameterIndices) { + ParameterIndices = parameterIndices; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Derivative; + } +}; + +/// The `@transpose(of:)` attribute registers a function as a transpose of +/// another function-like declaration: a 'func', 'init', 'subscript', or 'var' +/// computed property declaration. +/// +/// The `@transpose(of:)` attribute also has a `wrt:` clause specifying the +/// parameters that are transposed "with respect to", i.e. the linearity +/// parameters. +/// +/// Examples: +/// @transpose(of: foo) +/// @transpose(of: +, wrt: (0, 1)) +class TransposeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + /// The base type for the referenced original declaration. This field is + /// non-null only for parsed attributes that reference a qualified original + /// declaration. This field is not serialized; type-checking uses it to + /// resolve the original declaration, which is serialized. + TypeRepr *BaseTypeRepr; + /// The original function name. + DeclNameRefWithLoc OriginalFunctionName; + /// The original function declaration, resolved by the type checker. + AbstractFunctionDecl *OriginalFunction = nullptr; + /// The number of parsed linearity parameters specified in 'wrt:'. + unsigned NumParsedParameters = 0; + /// The linearity parameter indices, resolved by the type checker. + IndexSubset *ParameterIndices = nullptr; + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + explicit TransposeAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + +public: + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + ArrayRef params); + + static TransposeAttr *create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, DeclNameRefWithLoc original, + IndexSubset *parameterIndices); + + TypeRepr *getBaseTypeRepr() const { return BaseTypeRepr; } + DeclNameRefWithLoc getOriginalFunctionName() const { + return OriginalFunctionName; + } + AbstractFunctionDecl *getOriginalFunction() const { + return OriginalFunction; + } + void setOriginalFunction(AbstractFunctionDecl *decl) { + OriginalFunction = decl; + } + + /// The parsed linearity parameters, i.e. the list of parameters specified in + /// 'wrt:'. + ArrayRef getParsedParameters() const { + return {getTrailingObjects(), NumParsedParameters}; + } + MutableArrayRef getParsedParameters() { + return {getTrailingObjects(), NumParsedParameters}; + } + size_t numTrailingObjects(OverloadToken) const { + return NumParsedParameters; + } + + IndexSubset *getParameterIndices() const { + return ParameterIndices; + } + void setParameterIndices(IndexSubset *parameterIndices) { + ParameterIndices = parameterIndices; + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Transpose; + } +}; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. @@ -1570,6 +1997,11 @@ class DeclAttributes { bool isUnavailableInSwiftVersion(const version::Version &effectiveVersion) const; + /// Finds the most-specific platform-specific attribute that is + /// active for the current platform. + const AvailableAttr * + findMostSpecificActivePlatform(const ASTContext &ctx) const; + /// Returns the first @available attribute that indicates /// a declaration is unavailable, or the first one that indicates it's /// potentially unavailable, or null otherwise. @@ -1583,7 +2015,7 @@ class DeclAttributes { /// a declaration is deprecated on all deployment targets, or null otherwise. const AvailableAttr *getDeprecated(const ASTContext &ctx) const; - void dump(const Decl *D = nullptr) const; + SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr)); void print(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D = nullptr) const; static void print(ASTPrinter &Printer, const PrintOptions &Options, diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h new file mode 100644 index 0000000000000..f3c74e9b1d632 --- /dev/null +++ b/include/swift/AST/AutoDiff.h @@ -0,0 +1,317 @@ +//===--- AutoDiff.h - Swift automatic differentiation utilities -----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines utilities for automatic differentiation. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_AUTODIFF_H +#define SWIFT_AST_AUTODIFF_H + +#include + +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Identifier.h" +#include "swift/AST/IndexSubset.h" +#include "swift/AST/Type.h" +#include "swift/Basic/Range.h" +#include "swift/Basic/SourceLoc.h" + +namespace swift { + +class AnyFunctionType; +class TupleType; + +/// A function type differentiability kind. +enum class DifferentiabilityKind : uint8_t { + NonDifferentiable = 0, + Normal = 1, + Linear = 2 +}; + +/// The kind of an linear map. +struct AutoDiffLinearMapKind { + enum innerty : uint8_t { + // The differential function. + Differential = 0, + // The pullback function. + Pullback = 1 + } rawValue; + + AutoDiffLinearMapKind() = default; + AutoDiffLinearMapKind(innerty rawValue) : rawValue(rawValue) {} + operator innerty() const { return rawValue; } +}; + +/// The kind of a derivative function. +struct AutoDiffDerivativeFunctionKind { + enum innerty : uint8_t { + // The Jacobian-vector products function. + JVP = 0, + // The vector-Jacobian products function. + VJP = 1 + } rawValue; + + AutoDiffDerivativeFunctionKind() = default; + AutoDiffDerivativeFunctionKind(innerty rawValue) : rawValue(rawValue) {} + AutoDiffDerivativeFunctionKind(AutoDiffLinearMapKind linMapKind) + : rawValue(static_cast(linMapKind.rawValue)) {} + explicit AutoDiffDerivativeFunctionKind(StringRef string); + operator innerty() const { return rawValue; } + AutoDiffLinearMapKind getLinearMapKind() { + return (AutoDiffLinearMapKind::innerty)rawValue; + } +}; + +/// Identifies an autodiff derivative function configuration: +/// - Parameter indices. +/// - Result indices. +/// - Derivative generic signature (optional). +struct AutoDiffConfig { + IndexSubset *parameterIndices; + IndexSubset *resultIndices; + GenericSignature derivativeGenericSignature; + + /*implicit*/ AutoDiffConfig(IndexSubset *parameterIndices, + IndexSubset *resultIndices, + GenericSignature derivativeGenericSignature) + : parameterIndices(parameterIndices), resultIndices(resultIndices), + derivativeGenericSignature(derivativeGenericSignature) {} + + void print(llvm::raw_ostream &s = llvm::outs()) const; + SWIFT_DEBUG_DUMP; +}; + +class ParsedAutoDiffParameter { +public: + enum class Kind { Named, Ordered, Self }; + +private: + SourceLoc loc; + Kind kind; + union Value { + struct { Identifier name; } Named; + struct { unsigned index; } Ordered; + struct {} self; + Value(Identifier name) : Named({name}) {} + Value(unsigned index) : Ordered({index}) {} + Value() {} + } value; + +public: + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, Value value) + : loc(loc), kind(kind), value(value) {} + + ParsedAutoDiffParameter(SourceLoc loc, Kind kind, unsigned index) + : loc(loc), kind(kind), value(index) {} + + static ParsedAutoDiffParameter getNamedParameter(SourceLoc loc, + Identifier name) { + return { loc, Kind::Named, name }; + } + + static ParsedAutoDiffParameter getOrderedParameter(SourceLoc loc, + unsigned index) { + return { loc, Kind::Ordered, index }; + } + + static ParsedAutoDiffParameter getSelfParameter(SourceLoc loc) { + return { loc, Kind::Self, {} }; + } + + Identifier getName() const { + assert(kind == Kind::Named); + return value.Named.name; + } + + unsigned getIndex() const { + return value.Ordered.index; + } + + Kind getKind() const { + return kind; + } + + SourceLoc getLoc() const { + return loc; + } + + bool isEqual(const ParsedAutoDiffParameter &other) const { + if (getKind() != other.getKind()) + return false; + if (getKind() == Kind::Named) + return getName() == other.getName(); + return getKind() == Kind::Self; + } +}; + +/// The tangent space of a type. +/// +/// For `Differentiable`-conforming types: +/// - The tangent space is the `TangentVector` associated type. +/// +/// For tuple types: +/// - The tangent space is a tuple of the elements' tangent space types, for the +/// elements that have a tangent space. +/// +/// Other types have no tangent space. +class TangentSpace { +public: + /// A tangent space kind. + enum class Kind { + /// The `TangentVector` associated type of a `Differentiable`-conforming + /// type. + TangentVector, + /// A product of tangent spaces as a tuple. + Tuple + }; + +private: + Kind kind; + union Value { + // TangentVector + Type tangentVectorType; + // Tuple + TupleType *tupleType; + + Value(Type tangentVectorType) : tangentVectorType(tangentVectorType) {} + Value(TupleType *tupleType) : tupleType(tupleType) {} + } value; + + TangentSpace(Kind kind, Value value) : kind(kind), value(value) {} + +public: + TangentSpace() = delete; + + static TangentSpace getTangentVector(Type tangentVectorType) { + return {Kind::TangentVector, tangentVectorType}; + } + static TangentSpace getTuple(TupleType *tupleTy) { + return {Kind::Tuple, tupleTy}; + } + + bool isTangentVector() const { return kind == Kind::TangentVector; } + bool isTuple() const { return kind == Kind::Tuple; } + + Kind getKind() const { return kind; } + Type getTangentVector() const { + assert(kind == Kind::TangentVector); + return value.tangentVectorType; + } + TupleType *getTuple() const { + assert(kind == Kind::Tuple); + return value.tupleType; + } + + /// Get the tangent space type. + Type getType() const; + + /// Get the tangent space canonical type. + CanType getCanonicalType() const; + + /// Get the underlying nominal type declaration of the tangent space type. + NominalTypeDecl *getNominal() const; +}; + +/// Automatic differentiation utility namespace. +namespace autodiff { + +/// Appends the subset's parameter's types to `results`, in the order in +/// which they appear in the function type. +void getSubsetParameterTypes(IndexSubset *indices, AnyFunctionType *type, + SmallVectorImpl &results, + bool reverseCurryLevels = false); + +} // end namespace autodiff + +} // end namespace swift + +namespace llvm { + +using swift::AutoDiffConfig; +using swift::AutoDiffDerivativeFunctionKind; +using swift::GenericSignature; +using swift::IndexSubset; + +template struct DenseMapInfo; + +template <> struct DenseMapInfo { + static AutoDiffConfig getEmptyKey() { + auto *ptr = llvm::DenseMapInfo::getEmptyKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static AutoDiffConfig getTombstoneKey() { + auto *ptr = llvm::DenseMapInfo::getTombstoneKey(); + // The `derivativeGenericSignature` component must be `nullptr` so that + // `getHashValue` and `isEqual` do not try to call + // `GenericSignatureImpl::getCanonicalSignature()` on an invalid pointer. + return {static_cast(ptr), static_cast(ptr), + nullptr}; + } + + static unsigned getHashValue(const AutoDiffConfig &Val) { + auto canGenSig = + Val.derivativeGenericSignature + ? Val.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + unsigned combinedHash = hash_combine( + ~1U, DenseMapInfo::getHashValue(Val.parameterIndices), + DenseMapInfo::getHashValue(Val.resultIndices), + DenseMapInfo::getHashValue(canGenSig)); + return combinedHash; + } + + static bool isEqual(const AutoDiffConfig &LHS, const AutoDiffConfig &RHS) { + auto lhsCanGenSig = + LHS.derivativeGenericSignature + ? LHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + auto rhsCanGenSig = + RHS.derivativeGenericSignature + ? RHS.derivativeGenericSignature->getCanonicalSignature() + : nullptr; + return LHS.parameterIndices == RHS.parameterIndices && + LHS.resultIndices == RHS.resultIndices && + DenseMapInfo::isEqual(lhsCanGenSig, rhsCanGenSig); + } +}; + +template <> struct DenseMapInfo { + static AutoDiffDerivativeFunctionKind getEmptyKey() { + return static_cast( + DenseMapInfo::getEmptyKey()); + } + + static AutoDiffDerivativeFunctionKind getTombstoneKey() { + return static_cast( + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const AutoDiffDerivativeFunctionKind &Val) { + return DenseMapInfo::getHashValue(Val); + } + + static bool isEqual(const AutoDiffDerivativeFunctionKind &LHS, + const AutoDiffDerivativeFunctionKind &RHS) { + return static_cast(LHS) == + static_cast(RHS); + } +}; + +} // end namespace llvm + +#endif // SWIFT_AST_AUTODIFF_H diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 03809bcef7008..4b5cd1888a30a 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -641,8 +641,10 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special) /// globalStringTablePointer has type String -> Builtin.RawPointer. /// It returns an immortal, global string table pointer for strings constructed -/// from string literals. -BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "", Special) +/// from string literals. We consider it effects as readnone meaning that it +/// does not read any memory (note that even though it reads from a string, it +/// is a pure value and therefore we can consider it as readnone). +BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "n", Special) #undef BUILTIN_MISC_OPERATION_WITH_SILGEN diff --git a/include/swift/AST/CanTypeVisitor.h b/include/swift/AST/CanTypeVisitor.h index 1ae3473195efd..658f01488ecbf 100644 --- a/include/swift/AST/CanTypeVisitor.h +++ b/include/swift/AST/CanTypeVisitor.h @@ -31,15 +31,12 @@ class CanTypeVisitor { public: RetTy visit(CanType T, Args... args) { switch (T->getKind()) { -#define UNCHECKED_TYPE(CLASS, PARENT) \ - case TypeKind::CLASS: #define SUGARED_TYPE(CLASS, PARENT) \ case TypeKind::CLASS: #define TYPE(CLASS, PARENT) #include "swift/AST/TypeNodes.def" - llvm_unreachable("non-canonical or unchecked type"); + llvm_unreachable("non-canonical type"); -#define UNCHECKED_TYPE(CLASS, PARENT) #define SUGARED_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) \ case TypeKind::CLASS: \ @@ -64,7 +61,12 @@ class CanTypeVisitor { #define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT) #define ABSTRACT_SUGARED_TYPE(CLASS, PARENT) #define SUGARED_TYPE(CLASS, PARENT) -#define UNCHECKED_TYPE(CLASS, PARENT) + // Don't allow unchecked types by default, but allow visitors to opt-in to + // handling them. +#define UNCHECKED_TYPE(CLASS, PARENT) \ + RetTy visit##CLASS##Type(Can##CLASS##Type T, Args... args) { \ + llvm_unreachable("unchecked type"); \ + } #include "swift/AST/TypeNodes.def" }; diff --git a/include/swift/AST/CaptureInfo.h b/include/swift/AST/CaptureInfo.h index 1eeaecd27f8a0..6d2eb20735229 100644 --- a/include/swift/AST/CaptureInfo.h +++ b/include/swift/AST/CaptureInfo.h @@ -13,6 +13,7 @@ #ifndef SWIFT_AST_CAPTURE_INFO_H #define SWIFT_AST_CAPTURE_INFO_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" #include "swift/Basic/SourceLoc.h" @@ -169,7 +170,7 @@ class CaptureInfo { ArrayRef getCaptures() const { // FIXME: Ideally, everywhere that synthesizes a function should include - // its capture info. + // its capture info. if (!hasBeenComputed()) return None; return StorageAndFlags.getPointer()->getCaptures(); @@ -187,7 +188,7 @@ class CaptureInfo { /// \returns true if the function captures any generic type parameters. bool hasGenericParamCaptures() const { // FIXME: Ideally, everywhere that synthesizes a function should include - // its capture info. + // its capture info. if (!hasBeenComputed()) return false; return StorageAndFlags.getInt().contains(Flags::HasGenericParamCaptures); @@ -201,7 +202,7 @@ class CaptureInfo { /// \returns the captured dynamic Self type, if any. DynamicSelfType *getDynamicSelfType() const { // FIXME: Ideally, everywhere that synthesizes a function should include - // its capture info. + // its capture info. if (!hasBeenComputed()) return nullptr; return StorageAndFlags.getPointer()->getDynamicSelfType(); @@ -213,13 +214,13 @@ class CaptureInfo { OpaqueValueExpr *getOpaqueValue() const { // FIXME: Ideally, everywhere that synthesizes a function should include - // its capture info. + // its capture info. if (!hasBeenComputed()) return nullptr; return StorageAndFlags.getPointer()->getOpaqueValue(); } - void dump() const; + SWIFT_DEBUG_DUMP; void print(raw_ostream &OS) const; }; diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index f3e3eee1fd31e..0e3d109226fd0 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -20,6 +20,8 @@ class ASTContext; class CompilerInstance; class Preprocessor; class Sema; +class TargetInfo; +class Type; } // namespace clang namespace swift { @@ -46,6 +48,7 @@ class ClangModuleLoader : public ModuleLoader { using ModuleLoader::ModuleLoader; public: + virtual clang::TargetInfo &getTargetInfo() const = 0; virtual clang::ASTContext &getClangASTContext() const = 0; virtual clang::Preprocessor &getClangPreprocessor() const = 0; virtual clang::Sema &getClangSema() const = 0; @@ -98,6 +101,16 @@ class ClangModuleLoader : public ModuleLoader { lookupRelatedEntity(StringRef clangName, ClangTypeKind kind, StringRef relatedEntityKind, llvm::function_ref receiver) = 0; + + /// Try to parse the string as a Clang function type. + /// + /// Returns null if there was a parsing failure. + virtual const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const = 0; + + /// Print the Clang type. + virtual void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const = 0; }; } // namespace swift diff --git a/include/swift/AST/ClangSwiftTypeCorrespondence.h b/include/swift/AST/ClangSwiftTypeCorrespondence.h new file mode 100644 index 0000000000000..244505516bd76 --- /dev/null +++ b/include/swift/AST/ClangSwiftTypeCorrespondence.h @@ -0,0 +1,36 @@ +//=- ClangSwiftTypeCorrespondence.h - Relations between Clang & Swift types -=// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file describes some common relations between Clang types and Swift +// types that are need by the ClangTypeConverter and parts of ClangImporter. +// +// Since ClangTypeConverter is an implementation detail, ClangImporter should +// not depend on ClangTypeConverter.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_CLANG_SWIFT_TYPE_CORRESPONDENCE_H +#define SWIFT_AST_CLANG_SWIFT_TYPE_CORRESPONDENCE_H + +namespace clang { +class Type; +} + +namespace swift { +/// Checks whether a Clang type can be imported as a Swift Optional type. +/// +/// For example, a `const uint8_t *` could be imported as +/// `Optional>`. +bool canImportAsOptional(const clang::Type *type); +} + +#endif /* SWIFT_AST_CLANG_SWIFT_TYPE_CORRESPONDENCE_H */ diff --git a/include/swift/AST/ConcreteDeclRef.h b/include/swift/AST/ConcreteDeclRef.h index a62d57ee32752..9049e438e245a 100644 --- a/include/swift/AST/ConcreteDeclRef.h +++ b/include/swift/AST/ConcreteDeclRef.h @@ -17,6 +17,7 @@ #ifndef SWIFT_AST_CONCRETEDECLREF_H #define SWIFT_AST_CONCRETEDECLREF_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeAlignments.h" @@ -81,8 +82,8 @@ class ConcreteDeclRef { } /// Dump a debug representation of this reference. - void dump(raw_ostream &os); - void dump() LLVM_ATTRIBUTE_USED; + void dump(raw_ostream &os) const; + SWIFT_DEBUG_DUMP; }; } // end namespace swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 84ef3d4fa825d..b2727cd5572fb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -35,10 +35,12 @@ #include "swift/AST/Witness.h" #include "swift/Basic/ArrayRefView.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/OptionalEnum.h" #include "swift/Basic/Range.h" +#include "swift/Basic/Located.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/TrailingObjects.h" @@ -71,6 +73,7 @@ namespace swift { class GenericTypeParamDecl; class GenericTypeParamType; class ModuleDecl; + class NamedPattern; class EnumCaseDecl; class EnumElementDecl; class ParameterList; @@ -92,6 +95,12 @@ namespace swift { class VarDecl; class OpaqueReturnTypeRepr; + namespace ast_scope { + class AbstractPatternEntryScope; + class PatternEntryDeclScope; + class PatternEntryInitializerScope; + } // namespace ast_scope + enum class DeclKind : uint8_t { #define DECL(Id, Parent) Id, #define LAST_DECL(Id) Last_Decl = Id, @@ -168,16 +177,6 @@ enum class DescriptiveDeclKind : uint8_t { OpaqueVarType }; -/// Keeps track of stage of circularity checking for the given protocol. -enum class CircularityCheck { - /// Circularity has not yet been checked. - Unchecked, - /// We're currently checking circularity. - Checking, - /// Circularity has already been checked. - Checked -}; - /// Describes which spelling was used in the source for the 'static' or 'class' /// keyword. enum class StaticSpellingKind : uint8_t { @@ -275,17 +274,10 @@ bool conflicting(ASTContext &ctx, /// Decl - Base class for all declarations in Swift. class alignas(1 << DeclAlignInBits) Decl { -public: - enum class ValidationState { - Unchecked, - Checking, - Checked, - }; - protected: union { uint64_t OpaqueBits; - SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+2+1, + SWIFT_INLINE_BITFIELD_BASE(Decl, bitmax(NumDeclKindBits,8)+1+1+1+1+1, Kind : bitmax(NumDeclKindBits,8), /// Whether this declaration is invalid. @@ -300,9 +292,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// Use getClangNode() to retrieve the corresponding Clang AST. FromClang : 1, - /// The validation state of this declaration. - ValidationState : 2, - /// Whether this declaration was added to the surrounding /// DeclContext of an active #if config clause. EscapedFromIfConfig : 1 @@ -346,12 +335,15 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, /// Whether this declaration was an element of a capture list. IsCaptureList : 1, + + /// Whether this declaration captures the 'self' param under the same name. + IsSelfParamCapture : 1, /// Whether this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern @@ -366,7 +358,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsLazyStorageProperty : 1, /// Whether this is the backing storage for a property wrapper. - IsPropertyWrapperBackingProperty : 1 + IsPropertyWrapperBackingProperty : 1, + + /// Whether this is a lazily top-level global variable from the main file. + IsTopLevelGlobal : 1 ); SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits, @@ -408,7 +403,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasSingleExpressionBody : 1 ); - SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2, + SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1, /// Whether we've computed the 'static' flag yet. IsStaticComputed : 1, @@ -425,7 +420,12 @@ class alignas(1 << DeclAlignInBits) Decl { SelfAccessComputed : 1, /// Backing bits for 'self' access kind. - SelfAccess : 2 + SelfAccess : 2, + + /// Whether this is a top-level function which should be treated + /// as if it were in local context for the purposes of capture + /// analysis. + HasTopLevelLocalContextCaptures : 1 ); SWIFT_INLINE_BITFIELD(AccessorDecl, FuncDecl, 4+1+1, @@ -479,16 +479,19 @@ class alignas(1 << DeclAlignInBits) Decl { IsDebuggerAlias : 1 ); - SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1, + SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1, /// Whether we have already added implicitly-defined initializers /// to this declaration. AddedImplicitInitializers : 1, /// Whether there is are lazily-loaded conformances for this nominal type. - HasLazyConformances : 1 + HasLazyConformances : 1, + + /// Whether this nominal type is having its semantic members resolved. + IsComputingSemanticMembers : 1 ); - SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+2+1+1+8+16, + SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+8+16, /// Whether the \c RequiresClass bit is valid. RequiresClassValid : 1, @@ -511,9 +514,6 @@ class alignas(1 << DeclAlignInBits) Decl { /// because they could not be imported from Objective-C). HasMissingRequirements : 1, - /// The stage of the circularity check for this protocol. - Circularity : 2, - /// Whether we've computed the inherited protocols list yet. InheritedProtocolsValid : 1, @@ -530,18 +530,16 @@ class alignas(1 << DeclAlignInBits) Decl { NumRequirementsInSignature : 16 ); - SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+1+2+1+7+1+1+1+1+1+1, - /// The stage of the inheritance circularity check for this class. - Circularity : 2, - + SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 1+1+2+1+1+1+1+1+1, /// Whether this class inherits its superclass's convenience initializers. InheritsSuperclassInits : 1, + ComputedInheritsSuperclassInits : 1, /// \see ClassDecl::ForeignKind RawForeignKind : 2, /// \see ClassDecl::getEmittedMembers() - HasForcedEmittedMembers : 1, + HasForcedEmittedMembers : 1, HasMissingDesignatedInitializers : 1, ComputedHasMissingDesignatedInitializers : 1, @@ -560,10 +558,7 @@ class alignas(1 << DeclAlignInBits) Decl { HasUnreferenceableStorage : 1 ); - SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+2+1, - /// The stage of the raw type circularity check for this class. - Circularity : 2, - + SWIFT_INLINE_BITFIELD(EnumDecl, NominalTypeDecl, 2+1, /// True if the enum has cases and at least one case has associated values. HasAssociatedValues : 2, /// True if the enum has at least one case that has some availability @@ -666,6 +661,13 @@ class alignas(1 << DeclAlignInBits) Decl { void operator=(const Decl&) = delete; SourceLoc getLocFromSource() const; + struct CachedExternalSourceLocs { + SourceLoc Loc; + SourceLoc StartLoc; + SourceLoc EndLoc; + }; + mutable CachedExternalSourceLocs const *CachedLocs = nullptr; + const CachedExternalSourceLocs *calculateSerializedLocs() const; protected: Decl(DeclKind kind, llvm::PointerUnion context) @@ -675,7 +677,6 @@ class alignas(1 << DeclAlignInBits) Decl { Bits.Decl.Invalid = false; Bits.Decl.Implicit = false; Bits.Decl.FromClang = false; - Bits.Decl.ValidationState = unsigned(ValidationState::Unchecked); Bits.Decl.EscapedFromIfConfig = false; } @@ -755,6 +756,11 @@ class alignas(1 << DeclAlignInBits) Decl { return Attrs; } + /// Returns the introduced OS version in the given platform kind specified + /// by @available attribute. + /// This function won't consider the parent context to get the information. + Optional getIntroducedOSVersion(PlatformKind Kind) const; + /// Returns the starting location of the entire declaration. SourceLoc getStartLoc() const { return getSourceRange().Start; } @@ -763,7 +769,7 @@ class alignas(1 << DeclAlignInBits) Decl { /// Returns the preferred location when referring to declarations /// in diagnostics. - SourceLoc getLoc() const; + SourceLoc getLoc(bool SerializedOK = true) const; /// Returns the source range of the entire declaration. SourceRange getSourceRange() const; @@ -773,12 +779,8 @@ class alignas(1 << DeclAlignInBits) Decl { SourceLoc TrailingSemiLoc; - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); - LLVM_ATTRIBUTE_DEPRECATED( - void dump(const char *filename) const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; + SWIFT_DEBUG_DUMPER(dump(const char *filename)); void dump(raw_ostream &OS, unsigned Indent = 0) const; /// Pretty-print the given declaration. @@ -804,10 +806,10 @@ class alignas(1 << DeclAlignInBits) Decl { bool walk(ASTWalker &walker); /// Return whether this declaration has been determined invalid. - bool isInvalid() const { return Bits.Decl.Invalid; } + bool isInvalid() const; /// Mark this declaration invalid. - void setInvalid(bool isInvalid = true) { Bits.Decl.Invalid = isInvalid; } + void setInvalid(); /// Determine whether this declaration was implicitly generated by the /// compiler (rather than explicitly written in source code). @@ -816,37 +818,7 @@ class alignas(1 << DeclAlignInBits) Decl { /// Mark this declaration as implicit. void setImplicit(bool implicit = true) { Bits.Decl.Implicit = implicit; } - /// Get the validation state. - ValidationState getValidationState() const { - return ValidationState(Bits.Decl.ValidationState); - } - -private: - friend class DeclValidationRAII; - - /// Set the validation state. - void setValidationState(ValidationState VS) { - assert(VS > getValidationState() && "Validation is unidirectional"); - Bits.Decl.ValidationState = unsigned(VS); - } - public: - /// Whether the declaration is in the middle of validation or not. - bool isBeingValidated() const { - switch (getValidationState()) { - case ValidationState::Unchecked: - case ValidationState::Checked: - return false; - case ValidationState::Checking: - return true; - } - llvm_unreachable("Unknown ValidationState"); - } - - bool hasValidationStarted() const { - return getValidationState() > ValidationState::Unchecked; - } - bool escapedFromIfConfig() const { return Bits.Decl.EscapedFromIfConfig; } @@ -907,6 +879,8 @@ class alignas(1 << DeclAlignInBits) Decl { LLVM_READONLY const GenericContext *getAsGenericContext() const; + bool hasUnderscoredNaming() const; + bool isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic = true) const; AvailabilityContext getAvailabilityForLinkage() const; @@ -942,6 +916,10 @@ class alignas(1 << DeclAlignInBits) Decl { /// If this returns true, the decl can be safely casted to ValueDecl. bool isPotentiallyOverridable() const; + /// If an alternative module name is specified for this decl, e.g. using + /// @_originalDefinedIn attribute, this function returns this module name. + StringRef getAlternateModuleName() const; + /// Emit a diagnostic tied to this declaration. template InFlightDiagnostic diagnose( @@ -956,7 +934,7 @@ class alignas(1 << DeclAlignInBits) Decl { // Make vanilla new/delete illegal for Decls. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; // Only allow allocation of Decls using the allocator in ASTContext // or by doing a placement new. @@ -968,25 +946,6 @@ class alignas(1 << DeclAlignInBits) Decl { } }; -/// Use RAII to track Decl validation progress and non-reentrancy. -class DeclValidationRAII { - Decl *D; - -public: - DeclValidationRAII(const DeclValidationRAII &) = delete; - DeclValidationRAII(DeclValidationRAII &&) = delete; - void operator =(const DeclValidationRAII &) = delete; - void operator =(DeclValidationRAII &&) = delete; - - DeclValidationRAII(Decl *decl) : D(decl) { - D->setValidationState(Decl::ValidationState::Checking); - } - - ~DeclValidationRAII() { - D->setValidationState(Decl::ValidationState::Checked); - } -}; - /// Allocates memory for a Decl with the given \p baseSize. If necessary, /// it includes additional space immediately preceding the Decl for a ClangNode. /// \note \p baseSize does not need to include space for a ClangNode if @@ -1248,9 +1207,7 @@ class RequirementRepr { return repr->SecondType.getTypeRepr(); } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void print(raw_ostream &OS) const; void print(ASTPrinter &Printer) const; }; @@ -1420,7 +1377,7 @@ class GenericParamList final : GenericParamList *clone(DeclContext *dc) const; void print(raw_ostream &OS) const; - void dump(); + SWIFT_DEBUG_DUMP; }; /// A trailing where clause. @@ -1558,11 +1515,11 @@ enum class ImportKind : uint8_t { /// import Swift /// import typealias Swift.Int class ImportDecl final : public Decl, - private llvm::TrailingObjects> { + private llvm::TrailingObjects> { friend TrailingObjects; friend class Decl; public: - typedef std::pair AccessPathElement; + typedef Located AccessPathElement; private: SourceLoc ImportLoc; @@ -1632,9 +1589,9 @@ class ImportDecl final : public Decl, } SourceLoc getStartLoc() const { return ImportLoc; } - SourceLoc getLocFromSource() const { return getFullAccessPath().front().second; } + SourceLoc getLocFromSource() const { return getFullAccessPath().front().Loc; } SourceRange getSourceRange() const { - return SourceRange(ImportLoc, getFullAccessPath().back().second); + return SourceRange(ImportLoc, getFullAccessPath().back().Loc); } SourceLoc getKindLoc() const { return KindLoc; } @@ -1884,7 +1841,7 @@ class PatternBindingEntry { /// Whether the contents of this initializer were subsumed by /// some other initialization, e.g., a lazy property's initializer /// gets subsumed by the getter body. - Subsumed = 1 << 2 + Subsumed = 1 << 2, }; llvm::PointerIntPair> PatternAndFlags; @@ -1910,31 +1867,59 @@ class PatternBindingEntry { StringRef InitStringRepresentation; }; + enum class PatternFlags { + IsText = 1 << 0, + IsFullyValidated = 1 << 1, + }; /// The initializer context used for this pattern binding entry. - llvm::PointerIntPair InitContextAndIsText; + llvm::PointerIntPair> + InitContextAndFlags; /// Values captured by this initializer. CaptureInfo Captures; + friend class Parser; friend class PatternBindingInitializer; + friend class PatternBindingDecl; + friend class ast_scope::AbstractPatternEntryScope; + friend class ast_scope::PatternEntryDeclScope; + friend class ast_scope::PatternEntryInitializerScope; + +private: + // FIXME: This API is transitional. Once the callers of + // typeCheckPatternBinding are requestified, merge this bit with + // Flags::Checked. + friend class PatternBindingEntryRequest; + + bool isFullyValidated() const { + return InitContextAndFlags.getInt().contains( + PatternFlags::IsFullyValidated); + } + void setFullyValidated() { + InitContextAndFlags.setInt(InitContextAndFlags.getInt() | + PatternFlags::IsFullyValidated); + } public: /// \p E is the initializer as parsed. PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E, DeclContext *InitContext) - : PatternAndFlags(P, {}), InitExpr({E, E, EqualLoc}), - InitContextAndIsText({InitContext, false}) { - } + : PatternAndFlags(P, {}), InitExpr({E, E, EqualLoc}), + InitContextAndFlags({InitContext, None}) {} +private: Pattern *getPattern() const { return PatternAndFlags.getPointer(); } void setPattern(Pattern *P) { PatternAndFlags.setPointer(P); } /// Whether the given pattern binding entry is initialized. - bool isInitialized() const; + /// + /// \param onlyExplicit Only consider explicit initializations (rather + /// than implicitly-generated ones). + bool isInitialized(bool onlyExplicit = false) const; Expr *getInit() const { if (PatternAndFlags.getInt().contains(Flags::Removed) || - InitContextAndIsText.getInt()) + InitContextAndFlags.getInt().contains(PatternFlags::IsText)) return nullptr; return InitExpr.initAfterSynthesis; } @@ -1954,7 +1939,8 @@ class PatternBindingEntry { /// deserialized from a partial module. void setInitStringRepresentation(StringRef str) { InitStringRepresentation = str; - InitContextAndIsText.setInt(true); + InitContextAndFlags.setInt(InitContextAndFlags.getInt() | + PatternFlags::IsText); } /// Whether this pattern entry can generate a string representation of its @@ -1963,12 +1949,14 @@ class PatternBindingEntry { /// Retrieve the location of the equal '=' token. SourceLoc getEqualLoc() const { - return InitContextAndIsText.getInt() ? SourceLoc() : InitExpr.EqualLoc; + return InitContextAndFlags.getInt().contains(PatternFlags::IsText) + ? SourceLoc() + : InitExpr.EqualLoc; } /// Set the location of the equal '=' token. void setEqualLoc(SourceLoc equalLoc) { - assert(!InitContextAndIsText.getInt() && + assert(!InitContextAndFlags.getInt().contains(PatternFlags::IsText) && "cannot set equal loc for textual initializer"); InitExpr.EqualLoc = equalLoc; } @@ -1999,13 +1987,11 @@ class PatternBindingEntry { // Retrieve the declaration context for the initializer. DeclContext *getInitContext() const { - return InitContextAndIsText.getPointer(); + return InitContextAndFlags.getPointer(); } /// Override the initializer context. - void setInitContext(DeclContext *dc) { - InitContextAndIsText.setPointer(dc); - } + void setInitContext(DeclContext *dc) { InitContextAndFlags.setPointer(dc); } SourceLoc getStartLoc() const; @@ -2021,8 +2007,10 @@ class PatternBindingEntry { /// from the source range. SourceRange getSourceRange(bool omitAccessors = false) const; - const CaptureInfo &getCaptureInfo() const { return Captures; } - void setCaptureInfo(const CaptureInfo &captures) { Captures = captures; } + CaptureInfo getCaptureInfo() const { return Captures; } + void setCaptureInfo(CaptureInfo captures) { Captures = captures; } + + unsigned getNumBoundVariables() const; private: SourceLoc getLastAccessorEndLoc() const; @@ -2044,6 +2032,8 @@ class PatternBindingDecl final : public Decl, private llvm::TrailingObjects { friend TrailingObjects; friend class Decl; + friend class PatternBindingEntryRequest; + SourceLoc StaticLoc; ///< Location of the 'static/class' keyword, if present. SourceLoc VarLoc; ///< Location of the 'var' keyword. @@ -2107,6 +2097,9 @@ class PatternBindingDecl final : public Decl, Expr *getExecutableInit(unsigned i) const { return getPatternList()[i].getExecutableInit(); } + Expr *getOriginalInit(unsigned i) const { + return getPatternList()[i].getOriginalInit(); + } SourceRange getOriginalInitRange(unsigned i) const { return getPatternList()[i].getOriginalInitRange(); @@ -2126,11 +2119,11 @@ class PatternBindingDecl final : public Decl, return getPatternList()[i].getInitContext(); } - const CaptureInfo &getCaptureInfo(unsigned i) const { + CaptureInfo getCaptureInfo(unsigned i) const { return getPatternList()[i].getCaptureInfo(); } - void setCaptureInfo(unsigned i, const CaptureInfo &captures) { + void setCaptureInfo(unsigned i, CaptureInfo captures) { getMutablePatternList()[i].setCaptureInfo(captures); } @@ -2143,13 +2136,6 @@ class PatternBindingDecl final : public Decl, /// and "c" and "d" will have index 1 since they correspond to the second one. unsigned getPatternEntryIndexForVarDecl(const VarDecl *VD) const; - /// Return the PatternEntry (a pattern + initializer pair) for the specified - /// VarDecl. - const PatternBindingEntry &getPatternEntryForVarDecl( - const VarDecl *VD) const { - return getPatternList()[getPatternEntryIndexForVarDecl(VD)]; - } - bool isInitializerChecked(unsigned i) const { return getPatternList()[i].isInitializerChecked(); } @@ -2172,7 +2158,7 @@ class PatternBindingDecl final : public Decl, /// Determines whether this binding either has an initializer expression, or is /// default initialized, without performing any type checking on it. bool isDefaultInitializable() const { - for (unsigned i = 0, e = getNumPatternEntries(); i < e; ++i) + for (unsigned i : range(getNumPatternEntries())) if (!isDefaultInitializable(i)) return false; @@ -2181,11 +2167,25 @@ class PatternBindingDecl final : public Decl, /// Can the pattern at index i be default initialized? bool isDefaultInitializable(unsigned i) const; - + + /// Does this pattern have a user-provided initializer expression? + bool isExplicitlyInitialized(unsigned i) const; + + /// Whether the pattern entry at the given index can generate a string + /// representation of its initializer expression. + bool hasInitStringRepresentation(unsigned i) const { + return getPatternList()[i].hasInitStringRepresentation(); + } + + SourceLoc getEqualLoc(unsigned i) const; + /// When the pattern binding contains only a single variable with no /// destructuring, retrieve that variable. VarDecl *getSingleVar() const; + /// Return the first variable initialized by the pattern at the given index. + VarDecl *getAnchoringVarDecl(unsigned i) const; + bool isStatic() const { return Bits.PatternBindingDecl.IsStatic; } void setStatic(bool s) { Bits.PatternBindingDecl.IsStatic = s; } SourceLoc getStaticLoc() const { return StaticLoc; } @@ -2197,6 +2197,17 @@ class PatternBindingDecl final : public Decl, /// \returns the way 'static'/'class' should be spelled for this declaration. StaticSpellingKind getCorrectStaticSpelling() const; + /// Is the pattern binding entry for this variable currently being computed? + bool isComputingPatternBindingEntry(const VarDecl *vd) const; + + /// Gets the text of the initializer expression for the pattern entry at the + /// given index, stripping out inactive branches of any #ifs inside the + /// expression. + StringRef getInitStringRepresentation(unsigned i, + SmallVectorImpl &scratch) const { + return getPatternList()[i].getInitStringRepresentation(scratch); + } + static bool classof(const Decl *D) { return D->getKind() == DeclKind::PatternBinding; } @@ -2397,11 +2408,13 @@ class ValueDecl : public Decl { unsigned isIUO : 1; } LazySemanticInfo = { }; + friend class DynamicallyReplacedDeclRequest; friend class OverriddenDeclsRequest; friend class IsObjCRequest; friend class IsFinalRequest; friend class IsDynamicRequest; friend class IsImplicitlyUnwrappedOptionalRequest; + friend class InterfaceTypeRequest; friend class Decl; SourceLoc getLocFromSource() const { return NameLoc; } protected: @@ -2461,6 +2474,12 @@ class ValueDecl : public Decl { /// names. DeclBaseName getBaseName() const { return Name.getBaseName(); } + /// Generates a DeclNameRef referring to this declaration with as much + /// specificity as possible. + DeclNameRef createNameRef() const { + return DeclNameRef(getFullName()); + } + /// Retrieve the name to use for this declaration when interoperating /// with the Objective-C runtime. /// @@ -2701,6 +2720,10 @@ class ValueDecl : public Decl { AccessSemantics getAccessSemanticsFromContext(const DeclContext *DC, bool isAccessOnSelf) const; + /// Determines if a reference to this declaration from a nested function + /// should be treated like a capture of a local value. + bool isLocalCapture() const; + /// Print a reference to the given declaration. std::string printRef() const; @@ -2708,7 +2731,7 @@ class ValueDecl : public Decl { void dumpRef(raw_ostream &os) const; /// Dump a reference to the given declaration. - void dumpRef() const; + SWIFT_DEBUG_DUMPER(dumpRef()); /// Returns true if the declaration is a static member of a type. /// @@ -2756,6 +2779,11 @@ class ValueDecl : public Decl { /// Retrieve the @functionBuilder type attached to this declaration, /// if there is one. Type getFunctionBuilderType() const; + + /// If this value or its backing storage is annotated + /// @_dynamicReplacement(for: ...), compute the original declaration + /// that this declaration dynamically replaces. + ValueDecl *getDynamicallyReplacedDecl() const; }; /// This is a common base class for declarations which declare a type. @@ -2840,7 +2868,7 @@ class GenericTypeDecl : public GenericContext, public TypeDecl { /// code. One is formed implicitly when a declaration is written with an opaque /// result type, as in: /// -/// func foo() -> opaque SignedInteger { return 1 } +/// func foo() -> some SignedInteger { return 1 } /// /// The declared type is a special kind of ArchetypeType representing the /// abstracted underlying type. @@ -3219,6 +3247,14 @@ static inline bool isRawPointerKind(PointerTypeKind PTK) { llvm_unreachable("Unhandled PointerTypeKind in switch."); } +// Kinds of buffer pointer types. +enum BufferPointerTypeKind : unsigned { + BPTK_UnsafeMutableRawBufferPointer, + BPTK_UnsafeRawBufferPointer, + BPTK_UnsafeMutableBufferPointer, + BPTK_UnsafeBufferPointer, +}; + enum KeyPathTypeKind : unsigned char { KPTK_AnyKeyPath, KPTK_PartialKeyPath, @@ -3261,20 +3297,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// its extensions. /// /// The table itself is lazily constructed and updated when - /// lookupDirect() is called. The bit indicates whether the lookup - /// table has already added members by walking the declarations in - /// scope; it should be manipulated through \c isLookupTablePopulated() - /// and \c setLookupTablePopulated(). - llvm::PointerIntPair LookupTable; + /// lookupDirect() is called. + MemberLookupTable *LookupTable = nullptr; /// Prepare the lookup table to make it ready for lookups. void prepareLookupTable(); - /// True if the entries in \c LookupTable are complete--that is, if a - /// name is present, it contains all members with that name. - bool isLookupTablePopulated() const; - void setLookupTablePopulated(bool value); - /// Note that we have added a member into the iterable declaration context, /// so that it can also be added to the lookup table (if needed). void addedMember(Decl *member); @@ -3301,8 +3329,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class ExtensionDecl; friend class DeclContext; friend class IterableDeclContext; + friend class DirectLookupRequest; friend ArrayRef - ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; + ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const; protected: Type DeclaredTy; @@ -3318,6 +3347,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { Bits.NominalTypeDecl.AddedImplicitInitializers = false; ExtensionGeneration = 0; Bits.NominalTypeDecl.HasLazyConformances = false; + Bits.NominalTypeDecl.IsComputingSemanticMembers = false; } friend class ProtocolType; @@ -3374,6 +3404,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Whether to include @_implements members. /// Used by conformance-checking to find special @_implements members. IncludeAttrImplements = 1 << 0, + /// Whether to avoid loading lazy members from any new extensions that would otherwise be found + /// by deserialization. + /// + /// Used by the module loader to break recursion and as an optimization e.g. when it is known that a + /// particular member declaration will never appear in an extension. + IgnoreNewExtensions = 1 << 1, }; /// Find all of the declarations with the given name within this nominal type @@ -3438,6 +3474,35 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// with placeholders for unimportable stored properties. ArrayRef getStoredPropertiesAndMissingMemberPlaceholders() const; + /// Return the range of semantics attributes attached to this NominalTypeDecl. + auto getSemanticsAttrs() const + -> decltype(getAttrs().getAttributes()) { + return getAttrs().getAttributes(); + } + + bool hasSemanticsAttr(StringRef attrValue) const { + return llvm::any_of(getSemanticsAttrs(), [&](const SemanticsAttr *attr) { + return attrValue.equals(attr->Value); + }); + } + + /// Whether this declaration has a synthesized memberwise initializer. + bool hasMemberwiseInitializer() const; + + /// Retrieves the synthesized memberwise initializer for this declaration, + /// or \c nullptr if it does not have one. + ConstructorDecl *getMemberwiseInitializer() const; + + /// Whether this declaration has a synthesized zero parameter default + /// initializer. + bool hasDefaultInitializer() const; + + /// Retrieves the synthesized zero parameter default initializer for this + /// declaration, or \c nullptr if it doesn't have one. + ConstructorDecl *getDefaultInitializer() const; + + void synthesizeSemanticMembersIfNeeded(DeclName member); + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_NominalTypeDecl && @@ -3558,16 +3623,9 @@ class EnumDecl final : public NominalTypeDecl { for (auto elt : getAllElements()) elements.insert(elt); } - - /// Retrieve the status of circularity checking for class inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.EnumDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.EnumDecl.Circularity = static_cast(circularity); - } + + /// Whether this enum has a raw value type that recursively references itself. + bool hasCircularRawValue() const; /// Record that this enum has had all of its raw values computed. void setHasFixedRawValues(); @@ -3769,9 +3827,44 @@ class ClassDecl final : public NominalTypeDecl { Bits.ClassDecl.HasForcedEmittedMembers = true; } + Optional getCachedInheritsSuperclassInitializers() const { + if (Bits.ClassDecl.ComputedInheritsSuperclassInits) + return Bits.ClassDecl.InheritsSuperclassInits; + + return None; + } + + Optional getCachedHasMissingDesignatedInitializers() const { + if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) { + // Force loading all the members, which will add this attribute if any of + // members are determined to be missing while loading. + auto mutableThis = const_cast(this); + (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); + } + + if (Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) + return Bits.ClassDecl.HasMissingDesignatedInitializers; + + return None; + } + + void setHasMissingDesignatedInitializers(bool value) { + Bits.ClassDecl.HasMissingDesignatedInitializers = value; + Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = true; + } + + /// Marks that this class inherits convenience initializers from its + /// superclass. + void setInheritsSuperclassInitializers(bool value) { + Bits.ClassDecl.InheritsSuperclassInits = value; + Bits.ClassDecl.ComputedInheritsSuperclassInits = true; + } + friend class SuperclassDeclRequest; friend class SuperclassTypeRequest; friend class EmittedMembersRequest; + friend class HasMissingDesignatedInitializersRequest; + friend class InheritsSuperclassInitializersRequest; friend class TypeChecker; public: @@ -3811,15 +3904,8 @@ class ClassDecl final : public NominalTypeDecl { /// Set the superclass of this class. void setSuperclass(Type superclass); - /// Retrieve the status of circularity checking for class inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.ClassDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.ClassDecl.Circularity = static_cast(circularity); - } + /// Whether this class has a circular reference in its inheritance hierarchy. + bool hasCircularInheritance() const; /// Walk this class and all of the superclasses of this class, transitively, /// invoking the callback function for each class. @@ -3884,11 +3970,6 @@ class ClassDecl final : public NominalTypeDecl { /// initializers that cannot be represented in Swift. bool hasMissingDesignatedInitializers() const; - void setHasMissingDesignatedInitializers(bool newValue = true) { - Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - Bits.ClassDecl.HasMissingDesignatedInitializers = newValue; - } - /// Returns true if the class has missing members that require vtable entries. /// /// In this case, the class cannot be subclassed, because we cannot construct @@ -3927,16 +4008,7 @@ class ClassDecl final : public NominalTypeDecl { /// Determine whether this class inherits the convenience initializers /// from its superclass. - bool inheritsSuperclassInitializers(); - - /// Marks that this class inherits convenience initializers from its - /// superclass. - /// - /// This is computed as part of adding implicit initializers. - void setInheritsSuperclassInitializers() { - assert(addedImplicitInitializers()); - Bits.ClassDecl.InheritsSuperclassInits = true; - } + bool inheritsSuperclassInitializers() const; /// Walks the class hierarchy starting from this class, checking various /// conditions. @@ -4299,15 +4371,9 @@ class ProtocolDecl final : public NominalTypeDecl { return false; } - /// Retrieve the status of circularity checking for protocol inheritance. - CircularityCheck getCircularityCheck() const { - return static_cast(Bits.ProtocolDecl.Circularity); - } - - /// Record the current stage of circularity checking. - void setCircularityCheck(CircularityCheck circularity) { - Bits.ProtocolDecl.Circularity = static_cast(circularity); - } + /// Whether this protocol has a circular reference in its list of inherited + /// protocols. + bool hasCircularInheritedProtocols() const; /// Returns true if the protocol has requirements that are not listed in its /// members. @@ -4339,9 +4405,9 @@ class ProtocolDecl final : public NominalTypeDecl { /// Returns the default associated conformance witness for an associated /// type, or \c None if there is no default. - Optional getDefaultAssociatedConformanceWitness( - CanType association, - ProtocolDecl *requirement) const; + ProtocolConformanceRef + getDefaultAssociatedConformanceWitness(CanType association, + ProtocolDecl *requirement) const; /// Set the default associated conformance witness for the given /// associated conformance. @@ -4751,8 +4817,6 @@ class AbstractStorageDecl : public ValueDecl { bool hasAnyNativeDynamicAccessors() const; - bool hasAnyDynamicReplacementAccessors() const; - // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractStorageDecl && @@ -4773,6 +4837,9 @@ enum class PropertyWrapperSynthesizedPropertyKind { /// VarDecl - 'var' and 'let' declarations. class VarDecl : public AbstractStorageDecl { + friend class NamingPatternRequest; + NamedPattern *NamingPattern = nullptr; + public: enum class Introducer : uint8_t { Let = 0, @@ -4786,10 +4853,6 @@ class VarDecl : public AbstractStorageDecl { bool issCaptureList, SourceLoc nameLoc, Identifier name, DeclContext *dc, StorageIsMutable_t supportsMutation); - TypeRepr *ParentRepr = nullptr; - - Type typeInContext; - public: VarDecl(bool isStatic, Introducer introducer, bool isCaptureList, SourceLoc nameLoc, Identifier name, DeclContext *dc) @@ -4806,26 +4869,10 @@ class VarDecl : public AbstractStorageDecl { return hasName() ? getBaseName().getIdentifier().str() : "_"; } - /// Retrieve the TypeRepr corresponding to the parsed type of the parent - /// pattern, if it exists. - TypeRepr *getTypeRepr() const { return ParentRepr; } - void setTypeRepr(TypeRepr *repr) { ParentRepr = repr; } - - bool hasType() const { - // We have a type if either the type has been computed already or if - // this is a deserialized declaration with an interface type. - return !typeInContext.isNull(); - } - /// Get the type of the variable within its context. If the context is generic, /// this will use archetypes. Type getType() const; - /// Set the type of the variable within its context. - void setType(Type t); - - void markInvalid(); - /// Retrieve the source range of the variable type, or an invalid range if the /// variable's type is not explicitly written in the source. /// @@ -4864,6 +4911,14 @@ class VarDecl : public AbstractStorageDecl { /// Pattern *getParentPattern() const; + /// Returns the parsed type of this variable declaration. For parameters, this + /// is the parsed type the user explicitly wrote. For variables, this is the + /// type the user wrote in the typed pattern that binds this variable. + /// + /// Note that there are many cases where the user may elide types. This will + /// return null in those cases. + TypeRepr *getTypeReprOrParentPatternTypeRepr() const; + /// Return the statement that owns the pattern associated with this VarDecl, /// if one exists. /// @@ -4904,6 +4959,9 @@ class VarDecl : public AbstractStorageDecl { Parent = v; } + NamedPattern *getNamingPattern() const; + void setNamingPattern(NamedPattern *Pat); + /// If this is a VarDecl that does not belong to a CaseLabelItem's pattern, /// return this. Otherwise, this VarDecl must belong to a CaseStmt's /// CaseLabelItem. In that case, return the first case label item of the first @@ -4947,15 +5005,19 @@ class VarDecl : public AbstractStorageDecl { /// binding has no initial value, this returns null. /// Expr *getParentInitializer() const { - if (auto *PBD = getParentPatternBinding()) - return PBD->getPatternEntryForVarDecl(this).getInit(); + if (auto *PBD = getParentPatternBinding()) { + const auto i = PBD->getPatternEntryIndexForVarDecl(this); + return PBD->getInit(i); + } return nullptr; } /// Whether there exists an initializer for this \c VarDecl. bool isParentInitialized() const { - if (auto *PBD = getParentPatternBinding()) - return PBD->getPatternEntryForVarDecl(this).isInitialized(); + if (auto *PBD = getParentPatternBinding()) { + const auto i = PBD->getPatternEntryIndexForVarDecl(this); + return PBD->isInitialized(i); + } return false; } @@ -4987,6 +5049,12 @@ class VarDecl : public AbstractStorageDecl { /// Is this an element in a capture list? bool isCaptureList() const { return Bits.VarDecl.IsCaptureList; } + + /// Is this a capture of the self param? + bool isSelfParamCapture() const { return Bits.VarDecl.IsSelfParamCapture; } + void setIsSelfParamCapture(bool IsSelfParamCapture = true) { + Bits.VarDecl.IsSelfParamCapture = IsSelfParamCapture; + } /// Return true if this vardecl has an initial value bound to it in a way /// that isn't represented in the AST with an initializer in the pattern @@ -5019,6 +5087,10 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsLazyStorageProperty = IsLazyStorage; } + /// True if this is a top-level global variable from the main source file. + bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; } + void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; } + /// Retrieve the custom attributes that attach property wrappers to this /// property. The returned list contains all of the attached property wrapper attributes in source order, /// which means the outermost wrapper attribute is provided first. @@ -5083,16 +5155,16 @@ class VarDecl : public AbstractStorageDecl { /// Retrieve the backing storage property for a lazy property. VarDecl *getLazyStorageProperty() const; - /// Whether this is a property with a property wrapper that was initialized - /// via a value of the original type, e.g., + /// Whether the memberwise initializer parameter for a property with a + /// property wrapper type uses the wrapped type. This will occur, for example, + /// when there is an explicitly-specified initializer like: /// /// \code /// @Lazy var i = 17 /// \end - bool isPropertyWrapperInitializedWithInitialValue() const; - - /// Whether the memberwise initializer parameter for a property with a property wrapper type - /// uses the wrapped type. + /// + /// Or when there is no initializer but each composed property wrapper has + /// a suitable `init(initialValue:)`. bool isPropertyMemberwiseInitializedWithWrappedType() const; /// If this property is the backing storage for a property with an attached @@ -5138,6 +5210,20 @@ class VarDecl : public AbstractStorageDecl { /// backing property will be treated as the member-initialized property. bool isMemberwiseInitialized(bool preferDeclaredProperties) const; + /// Return the range of semantics attributes attached to this VarDecl. + auto getSemanticsAttrs() const + -> decltype(getAttrs().getAttributes()) { + return getAttrs().getAttributes(); + } + + /// Returns true if this VarDelc has the string \p attrValue as a semantics + /// attribute. + bool hasSemanticsAttr(StringRef attrValue) const { + return llvm::any_of(getSemanticsAttrs(), [&](const SemanticsAttr *attr) { + return attrValue.equals(attr->Value); + }); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() == DeclKind::Var || D->getKind() == DeclKind::Param; @@ -5153,18 +5239,31 @@ enum class ParamSpecifier : uint8_t { /// A function parameter declaration. class ParamDecl : public VarDecl { - Identifier ArgumentName; + friend class DefaultArgumentInitContextRequest; + friend class DefaultArgumentExprRequest; + + llvm::PointerIntPair ArgumentNameAndDestructured; SourceLoc ParameterNameLoc; SourceLoc ArgumentNameLoc; SourceLoc SpecifierLoc; + TypeRepr *TyRepr = nullptr; + struct StoredDefaultArgument { PointerUnion DefaultArg; - Initializer *InitContext = nullptr; + + /// Stores the context for the default argument as well as a bit to + /// indicate whether the default expression has been type-checked. + llvm::PointerIntPair InitContextAndIsTypeChecked; + StringRef StringRepresentation; CaptureInfo Captures; }; + /// Retrieve the cached initializer context for the parameter's default + /// argument without triggering a request. + Optional getCachedDefaultArgumentInitContext() const; + enum class Flags : uint8_t { /// Whether or not this parameter is vargs. IsVariadic = 1 << 0, @@ -5188,7 +5287,9 @@ class ParamDecl : public VarDecl { static ParamDecl *cloneWithoutType(const ASTContext &Ctx, ParamDecl *PD); /// Retrieve the argument (API) name for this function parameter. - Identifier getArgumentName() const { return ArgumentName; } + Identifier getArgumentName() const { + return ArgumentNameAndDestructured.getPointer(); + } /// Retrieve the parameter (local) name for this function parameter. Identifier getParameterName() const { return getName(); } @@ -5203,6 +5304,13 @@ class ParamDecl : public VarDecl { SourceLoc getSpecifierLoc() const { return SpecifierLoc; } + /// Retrieve the TypeRepr corresponding to the parsed type of the parameter, if it exists. + TypeRepr *getTypeRepr() const { return TyRepr; } + void setTypeRepr(TypeRepr *repr) { TyRepr = repr; } + + bool isDestructured() const { return ArgumentNameAndDestructured.getInt(); } + void setDestructured(bool repr) { ArgumentNameAndDestructured.setInt(repr); } + DefaultArgumentKind getDefaultArgumentKind() const { return static_cast(Bits.ParamDecl.defaultArgumentKind); } @@ -5212,8 +5320,31 @@ class ParamDecl : public VarDecl { void setDefaultArgumentKind(DefaultArgumentKind K) { Bits.ParamDecl.defaultArgumentKind = static_cast(K); } - - Expr *getDefaultValue() const { + + /// Whether this parameter has a default argument expression available. + /// + /// Note that this will return false for deserialized declarations, which only + /// have a textual representation of their default expression. + bool hasDefaultExpr() const; + + /// Whether this parameter has a caller-side default argument expression + /// such as the magic literal \c #function. + bool hasCallerSideDefaultExpr() const; + + /// Retrieve the fully type-checked default argument expression for this + /// parameter, or \c nullptr if there is no default expression. + /// + /// Note that while this will produce a type-checked expression for + /// caller-side default arguments such as \c #function, this is done purely to + /// check whether the code is valid. Such default arguments get re-created + /// at the call site in order to have the correct context information. + Expr *getTypeCheckedDefaultExpr() const; + + /// Retrieve the potentially un-type-checked default argument expression for + /// this parameter, which can be queried for information such as its source + /// range and textual representation. Returns \c nullptr if there is no + /// default expression. + Expr *getStructuralDefaultExpr() const { if (auto stored = DefaultValueAndFlags.getPointer()) return stored->DefaultArg.dyn_cast(); return nullptr; @@ -5225,24 +5356,27 @@ class ParamDecl : public VarDecl { return nullptr; } - void setDefaultValue(Expr *E); + /// Sets a new default argument expression for this parameter. This should + /// only be called internally by ParamDecl and AST walkers. + /// + /// \param E The new default argument. + /// \param isTypeChecked Whether this argument should be used as the + /// parameter's fully type-checked default argument. + void setDefaultExpr(Expr *E, bool isTypeChecked); void setStoredProperty(VarDecl *var); - Initializer *getDefaultArgumentInitContext() const { - if (auto stored = DefaultValueAndFlags.getPointer()) - return stored->InitContext; - return nullptr; - } + /// Retrieve the initializer context for the parameter's default argument. + Initializer *getDefaultArgumentInitContext() const; void setDefaultArgumentInitContext(Initializer *initContext); - const CaptureInfo &getDefaultArgumentCaptureInfo() const { + CaptureInfo getDefaultArgumentCaptureInfo() const { assert(DefaultValueAndFlags.getPointer()); return DefaultValueAndFlags.getPointer()->Captures; } - void setDefaultArgumentCaptureInfo(const CaptureInfo &captures); + void setDefaultArgumentCaptureInfo(CaptureInfo captures); /// Extracts the text of the default argument attached to the provided /// ParamDecl, removing all inactive #if clauses and providing only the @@ -5290,6 +5424,37 @@ class ParamDecl : public VarDecl { : flags - Flags::IsAutoClosure); } + /// Does this parameter reject temporary pointer conversions? + bool isNonEphemeral() const { + if (getAttrs().hasAttribute()) + return true; + + // Only pointer parameters can be non-ephemeral. + auto ty = getInterfaceType(); + if (!ty->lookThroughSingleOptionalType()->getAnyPointerElementType()) + return false; + + // Enum element pointer parameters are always non-ephemeral. + auto *parentDecl = getDeclContext()->getAsDecl(); + if (parentDecl && isa(parentDecl)) + return true; + + return false; + } + + /// Attempt to apply an implicit `@_nonEphemeral` attribute to this parameter. + void setNonEphemeralIfPossible() { + // Don't apply the attribute if this isn't a pointer param. + auto type = getInterfaceType(); + if (!type->lookThroughSingleOptionalType()->getAnyPointerElementType()) + return; + + if (!getAttrs().hasAttribute()) { + auto &ctx = getASTContext(); + getAttrs().add(new (ctx) NonEphemeralAttr(/*IsImplicit*/ true)); + } + } + /// Remove the type of this varargs element designator, without the array /// type wrapping it. A parameter like "Int..." will have formal parameter /// type of "[Int]" and this returns "Int". @@ -5573,6 +5738,20 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { private: ParameterList *Params; + /// The generation at which we last loaded derivative function configurations. + unsigned DerivativeFunctionConfigGeneration = 0; + /// Prepare to traverse the list of derivative function configurations. + void prepareDerivativeFunctionConfigurations(); + + /// A uniqued list of derivative function configurations. + /// - `@differentiable` and `@derivative` attribute type-checking is + /// responsible for populating derivative function configurations specified + /// in the current module. + /// - Module loading is responsible for populating derivative function + /// configurations from imported modules. + struct DerivativeFunctionConfigurationList; + DerivativeFunctionConfigurationList *DerivativeFunctionConfigs = nullptr; + protected: // If a function has a body at all, we have either a parsed body AST node or // we have saved the end location of the unparsed body. @@ -5706,13 +5885,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// \sa hasBody() BraceStmt *getBody(bool canSynthesize = true) const; - void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed) { - assert(getBodyKind() != BodyKind::Skipped && - "cannot set a body if it was skipped"); - - Body = S; - setBodyKind(NewBodyKind); - } + void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed); /// Note that the body was skipped for this function. Function body /// cannot be attached after this call. @@ -5731,7 +5904,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Note that parsing for the body was delayed. void setBodyDelayed(SourceRange bodyRange) { - assert(getBodyKind() == BodyKind::None); + assert(getBodyKind() == BodyKind::None || + getBodyKind() == BodyKind::Skipped); assert(bodyRange.isValid()); BodyRange = bodyRange; setBodyKind(BodyKind::Unparsed); @@ -5804,18 +5978,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { } public: - /// Compute the interface type of this function declaration from the - /// parameter types. - void computeType(AnyFunctionType::ExtInfo Info = FunctionType::ExtInfo()); - /// Retrieve the source range of the function body. SourceRange getBodySourceRange() const; /// Retrieve the source range of the function declaration name + patterns. SourceRange getSignatureSourceRange() const; - const CaptureInfo &getCaptureInfo() const { return Captures; } - void setCaptureInfo(const CaptureInfo &captures) { Captures = captures; } + CaptureInfo getCaptureInfo() const { return Captures; } + void setCaptureInfo(CaptureInfo captures) { Captures = captures; } /// Retrieve the Objective-C selector that names this method. ObjCSelector getObjCSelector(DeclName preferredName = DeclName(), @@ -5906,6 +6076,12 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// constructor. bool hasDynamicSelfResult() const; + /// Get all derivative function configurations. + ArrayRef getDerivativeFunctionConfigurations(); + + /// Add the given derivative function configuration. + void addDerivativeFunctionConfiguration(AutoDiffConfig config); + using DeclContext::operator new; using Decl::getASTContext; }; @@ -5955,6 +6131,7 @@ class FuncDecl : public AbstractFunctionDecl { Bits.FuncDecl.SelfAccessComputed = false; Bits.FuncDecl.IsStaticComputed = false; Bits.FuncDecl.IsStatic = false; + Bits.FuncDecl.HasTopLevelLocalContextCaptures = false; } private: @@ -6112,6 +6289,12 @@ class FuncDecl : public AbstractFunctionDecl { /// Perform basic checking to determine whether the @IBAction or /// @IBSegueAction attribute can be applied to this function. bool isPotentialIBActionTarget() const; + + bool hasTopLevelLocalContextCaptures() const { + return Bits.FuncDecl.HasTopLevelLocalContextCaptures; + } + + void setHasTopLevelLocalContextCaptures(bool hasCaptures=true); }; /// This represents an accessor function, such as a getter or setter. @@ -6526,6 +6709,9 @@ class ConstructorDecl : public AbstractFunctionDecl { /// initializer. BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags, ApplyExpr **init = nullptr) const; + void clearCachedDelegatingOrChainedInitKind() { + Bits.ConstructorDecl.ComputedBodyInitKind = 0; + } /// Whether this constructor is required. bool isRequired() const { @@ -6872,6 +7058,13 @@ class OperatorDecl : public Decl { SourceLoc getNameLoc() const { return NameLoc; } Identifier getName() const { return name; } + /// Get the list of identifiers after the colon in the operator declaration. + /// + /// This list includes the names of designated types. For infix operators, the + /// first item in the list is a precedence group instead. + /// + /// \todo These two purposes really ought to be in separate properties and the + /// designated type list should be of TypeReprs instead of Identifiers. ArrayRef getIdentifiers() const { return Identifiers; } @@ -7226,9 +7419,17 @@ inline EnumElementDecl *EnumDecl::getUniqueElement(bool hasValue) const { return result; } -/// Retrieve parameter declaration from the given source at given index. +/// Retrieve the parameter list for a given declaration, or nullputr if there +/// is none. +ParameterList *getParameterList(ValueDecl *source); + +/// Retrieve parameter declaration from the given source at given index, or +/// nullptr if the source does not have a parameter list. const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index); +void simple_display(llvm::raw_ostream &out, + OptionSet options); + /// Display Decl subclasses. void simple_display(llvm::raw_ostream &out, const Decl *decl); @@ -7246,6 +7447,11 @@ inline void simple_display(llvm::raw_ostream &out, simple_display(out, static_cast(decl)); } +inline void simple_display(llvm::raw_ostream &out, + const AssociatedTypeDecl *decl) { + simple_display(out, static_cast(decl)); +} + /// Display GenericContext. /// /// The template keeps this sorted down in the overload set relative to the diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 4805fdcb7c3a9..04b5ab5a6cd9f 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -23,6 +23,7 @@ #include "swift/AST/LookupKinds.h" #include "swift/AST/ResilienceExpansion.h" #include "swift/AST/TypeAlignments.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceLoc.h" @@ -49,7 +50,6 @@ namespace swift { class ExtensionDecl; class Expr; class GenericParamList; - class LazyResolver; class LazyMemberLoader; class GenericSignature; class GenericTypeParamDecl; @@ -261,7 +261,13 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// Returns the kind of context this is. DeclContextKind getContextKind() const; - + + /// Returns whether this context has value semantics. + bool hasValueSemantics() const; + + /// Returns whether this context is an extension constrained to a class type. + bool isClassConstrainedProtocolExtension() const; + /// Determines whether this context is itself a local scope in a /// code block. A context that appears in such a scope, like a /// local type declaration, does not itself become a local context. @@ -499,7 +505,7 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// lookup. /// /// \returns true if anything was found. - bool lookupQualified(Type type, DeclName member, NLOptions options, + bool lookupQualified(Type type, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const; /// Look for the set of declarations with the given name within the @@ -516,16 +522,13 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// lookup. /// /// \returns true if anything was found. - bool lookupQualified(ArrayRef types, DeclName member, + bool lookupQualified(ArrayRef types, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const; /// Perform qualified lookup for the given member in the given module. - bool lookupQualified(ModuleDecl *module, DeclName member, NLOptions options, - SmallVectorImpl &decls) const; - - /// Perform \c AnyObject lookup for the given member. - bool lookupAnyObject(DeclName member, NLOptions options, + bool lookupQualified(ModuleDecl *module, DeclNameRef member, + NLOptions options, SmallVectorImpl &decls) const; /// Look up all Objective-C methods with the given selector visible @@ -591,7 +594,7 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { /// \returns true if traversal was aborted, false otherwise. bool walkContext(ASTWalker &Walker); - void dumpContext() const; + SWIFT_DEBUG_DUMPER(dumpContext()); unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0, bool onlyAPartialLine = false) const; @@ -678,18 +681,9 @@ enum class IterableDeclContextKind : uint8_t { /// Note that an iterable declaration context must inherit from both /// \c IterableDeclContext and \c DeclContext. class IterableDeclContext { - enum LazyMembers : unsigned { - Present = 1 << 0, - - /// Lazy member loading has a variety of feedback loops that need to - /// switch to pseudo-empty-member behaviour to avoid infinite recursion; - /// we use this flag to control them. - InProgress = 1 << 1, - }; - /// The first declaration in this context along with a bit indicating whether /// the members of this context will be lazily produced. - mutable llvm::PointerIntPair FirstDeclAndLazyMembers; + mutable llvm::PointerIntPair FirstDeclAndLazyMembers; /// The last declaration in this context, used for efficient insertion, /// along with the kind of iterable declaration context. @@ -777,20 +771,7 @@ class IterableDeclContext { /// Check whether there are lazily-loaded members. bool hasLazyMembers() const { - return FirstDeclAndLazyMembers.getInt() & LazyMembers::Present; - } - - bool isLoadingLazyMembers() { - return FirstDeclAndLazyMembers.getInt() & LazyMembers::InProgress; - } - - void setLoadingLazyMembers(bool inProgress) { - LazyMembers status = FirstDeclAndLazyMembers.getInt(); - if (inProgress) - status = LazyMembers(status | LazyMembers::InProgress); - else - status = LazyMembers(status & ~LazyMembers::InProgress); - FirstDeclAndLazyMembers.setInt(status); + return FirstDeclAndLazyMembers.getInt(); } /// Setup the loader for lazily-loaded members. @@ -821,6 +802,12 @@ class IterableDeclContext { // Some Decls are IterableDeclContexts, but not all. static bool classof(const Decl *D); + /// Return a hash of all tokens in the body for dependency analysis, if + /// available. + Optional getBodyFingerprint() const; + + bool areDependenciesUsingTokenHashesForTypeBodies() const; + private: /// Add a member to the list for iteration purposes, but do not notify the /// subclass that we have done so. diff --git a/include/swift/AST/DefaultArgumentKind.h b/include/swift/AST/DefaultArgumentKind.h index a60b9101e931a..f687bb4426eac 100644 --- a/include/swift/AST/DefaultArgumentKind.h +++ b/include/swift/AST/DefaultArgumentKind.h @@ -38,6 +38,8 @@ enum class DefaultArgumentKind : uint8_t { Inherited, /// The #file default argument, which is expanded at the call site. File, + /// The #filePath default argument, which is expanded at the call site. + FilePath, /// The #line default argument, which is expanded at the call site. Line, /// The #column default argument, which is expanded at the call site. diff --git a/include/swift/AST/DiagnosticConsumer.h b/include/swift/AST/DiagnosticConsumer.h index 809463a898b28..1d77b16952b6b 100644 --- a/include/swift/AST/DiagnosticConsumer.h +++ b/include/swift/AST/DiagnosticConsumer.h @@ -45,11 +45,21 @@ struct DiagnosticInfo { DiagnosticKind Kind; StringRef FormatString; ArrayRef FormatArgs; + + /// Only used when directing diagnostics to different outputs. + /// In batch mode a diagnostic may be + /// located in a non-primary file, but there will be no .dia file for a + /// non-primary. If valid, this argument contains a location within a buffer + /// that corresponds to a primary input. The .dia file for that primary can be + /// used for the diagnostic, as if it had occurred at this location. SourceLoc BufferIndirectlyCausingDiagnostic; /// DiagnosticInfo of notes which are children of this diagnostic, if any ArrayRef ChildDiagnosticInfo; + /// Paths to "educational note" diagnostic documentation in the toolchain. + ArrayRef EducationalNotePaths; + /// Represents a fix-it, a replacement of one range of text with another. class FixIt { CharSourceRange Range; @@ -109,29 +119,9 @@ class DiagnosticConsumer { /// \param SM The source manager associated with the source locations in /// this diagnostic. /// - /// \param Loc The source location associated with this diagnostic. This - /// location may be invalid, if the diagnostic is not directly related to - /// the source (e.g., if it comes from command-line parsing). - /// - /// \param Kind The severity of the diagnostic (error, warning, note). - /// - /// \param FormatArgs The diagnostic format string arguments. - /// - /// \param Info Extra information associated with the diagnostic. - /// - /// \param bufferIndirectlyCausingDiagnostic Only used when directing - /// diagnostics to different outputs. - /// In batch mode a diagnostic may be - /// located in a non-primary file, but there will be no .dia file for a - /// non-primary. If valid, this argument contains a location within a buffer - /// that corresponds to a primary input. The .dia file for that primary can be - /// used for the diagnostic, as if it had occurred at this location. - virtual void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) = 0; + /// \param Info Information describing the diagnostic. + virtual void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) = 0; /// \returns true if an error occurred while finishing-up. virtual bool finishProcessing() { return false; } @@ -149,11 +139,7 @@ class DiagnosticConsumer { /// DiagnosticConsumer that discards all diagnostics. class NullDiagnosticConsumer : public DiagnosticConsumer { public: - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; }; /// DiagnosticConsumer that forwards diagnostics to the consumers of @@ -162,11 +148,7 @@ class ForwardingDiagnosticConsumer : public DiagnosticConsumer { DiagnosticEngine &TargetEngine; public: ForwardingDiagnosticConsumer(DiagnosticEngine &Target); - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; }; /// DiagnosticConsumer that funnels diagnostics in certain files to @@ -228,18 +210,13 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { std::unique_ptr consumer) : inputFileName(inputFileName), consumer(std::move(consumer)) {} - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) { if (!getConsumer()) return; - hasAnErrorBeenConsumed |= Kind == DiagnosticKind::Error; - getConsumer()->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, - Info, bufferIndirectlyCausingDiagnostic); + hasAnErrorBeenConsumed |= Info.Kind == DiagnosticKind::Error; + getConsumer()->handleDiagnostic(SM, Info); } - + void informDriverOfIncompleteBatchModeCompilation() { if (!hasAnErrorBeenConsumed && getConsumer()) getConsumer()->informDriverOfIncompleteBatchModeCompilation(); @@ -324,11 +301,7 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { SmallVectorImpl &consumers); public: - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; bool finishProcessing() override; @@ -348,12 +321,10 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer { subconsumerForLocation(SourceManager &SM, SourceLoc loc); Optional - findSubconsumer(SourceManager &SM, SourceLoc loc, DiagnosticKind Kind, - SourceLoc bufferIndirectlyCausingDiagnostic); + findSubconsumer(SourceManager &SM, const DiagnosticInfo &Info); Optional - findSubconsumerForNonNote(SourceManager &SM, SourceLoc loc, - SourceLoc bufferIndirectlyCausingDiagnostic); + findSubconsumerForNonNote(SourceManager &SM, const DiagnosticInfo &Info); }; } // end namespace swift diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index c632d7f251fbc..ab691bb708ecf 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -21,7 +21,7 @@ #include "swift/AST/TypeLoc.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/DiagnosticConsumer.h" -#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/VersionTuple.h" @@ -105,7 +105,7 @@ namespace swift { int IntegerVal; unsigned UnsignedVal; StringRef StringVal; - DeclName IdentifierVal; + DeclNameRef IdentifierVal; ObjCSelector ObjCSelectorVal; ValueDecl *TheValueDecl; Type TypeVal; @@ -133,14 +133,20 @@ namespace swift { : Kind(DiagnosticArgumentKind::Unsigned), UnsignedVal(I) { } + DiagnosticArgument(DeclNameRef R) + : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(R) {} + DiagnosticArgument(DeclName D) - : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(D) {} + : Kind(DiagnosticArgumentKind::Identifier), + IdentifierVal(DeclNameRef(D)) {} DiagnosticArgument(DeclBaseName D) - : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(D) {} + : Kind(DiagnosticArgumentKind::Identifier), + IdentifierVal(DeclNameRef(D)) {} DiagnosticArgument(Identifier I) - : Kind(DiagnosticArgumentKind::Identifier), IdentifierVal(I) { + : Kind(DiagnosticArgumentKind::Identifier), + IdentifierVal(DeclNameRef(I)) { } DiagnosticArgument(ObjCSelector S) @@ -225,7 +231,7 @@ namespace swift { return UnsignedVal; } - DeclName getAsIdentifier() const { + DeclNameRef getAsIdentifier() const { assert(Kind == DiagnosticArgumentKind::Identifier); return IdentifierVal; } @@ -340,7 +346,7 @@ namespace swift { std::vector ChildNotes; SourceLoc Loc; bool IsChildNote = false; - const Decl *Decl = nullptr; + const swift::Decl *Decl = nullptr; friend DiagnosticEngine; @@ -655,7 +661,7 @@ namespace swift { /// A set of all strings involved in current transactional chain. /// This is required because diagnostics are not directly emitted /// but rather stored until all transactions complete. - llvm::StringMap TransactionStrings; + llvm::StringSet TransactionStrings; /// The number of open diagnostic transactions. Diagnostics are only /// emitted once all transactions have closed. @@ -670,6 +676,12 @@ namespace swift { /// Print diagnostic names after their messages bool printDiagnosticNames = false; + /// Use descriptive diagnostic style when available. + bool useDescriptiveDiagnostics = false; + + /// Path to diagnostic documentation directory. + std::string diagnosticDocumentationPath = ""; + friend class InFlightDiagnostic; friend class DiagnosticTransaction; friend class CompoundDiagnosticTransaction; @@ -713,6 +725,20 @@ namespace swift { return printDiagnosticNames; } + void setUseDescriptiveDiagnostics(bool val) { + useDescriptiveDiagnostics = val; + } + bool getUseDescriptiveDiagnostics() const { + return useDescriptiveDiagnostics; + } + + void setDiagnosticDocumentationPath(std::string path) { + diagnosticDocumentationPath = path; + } + StringRef getDiagnosticDocumentationPath() { + return diagnosticDocumentationPath; + } + void ignoreDiagnostic(DiagID id) { state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore); } diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 285694e764b09..d74022dbb66d4 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -64,6 +64,12 @@ ERROR(bridging_header_pch_error,Fatal, "failed to emit precompiled header '%0' for bridging header '%1'", (StringRef, StringRef)) +ERROR(emit_pcm_error,Fatal, + "failed to emit precompiled module '%0' for module map '%1'", + (StringRef, StringRef)) +ERROR(dump_pcm_error,Fatal, + "failed to dump precompiled module '%0'", (StringRef)) + WARNING(invalid_swift_name_method,none, "too %select{few|many}0 parameters in swift_name attribute (expected %1; " "got %2)", (bool, unsigned, unsigned)) @@ -86,6 +92,10 @@ WARNING(unresolvable_clang_decl,none, "imported declaration '%0' could not be mapped to '%1'", (StringRef, StringRef)) +NOTE(unresolvable_clang_decl_is_a_framework_bug,none, + "please report this issue to the owners of '%0'", + (StringRef)) + WARNING(implicit_bridging_header_imported_from_module,none, "implicit import of bridging header '%0' via module %1 " "is deprecated and will be removed in a later version of Swift", diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index 29732ba8d2f08..54a2334da9aa4 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -145,9 +145,6 @@ ERROR(sdk_node_unrecognized_accessor_kind,none, ERROR(circular_reference, none, "circular reference", ()) -ERROR(redundant_type_alias_define, none, - "redundant type alias declaration", ()) - NOTE(circular_reference_through, none, "through reference here", ()) @@ -166,7 +163,7 @@ NOTE(kind_declname_declared_here,none, WARNING(warn_property_wrapper_module_scope,none, "ignoring associated type %0 in favor of module-scoped property " "wrapper %0; please qualify the reference with %1", - (DeclName, Identifier)) + (DeclNameRef, Identifier)) #ifndef DIAG_NO_UNDEF # if defined(DIAG) diff --git a/include/swift/AST/DiagnosticsDriver.def b/include/swift/AST/DiagnosticsDriver.def index 291af950315ca..2246c54348a2e 100644 --- a/include/swift/AST/DiagnosticsDriver.def +++ b/include/swift/AST/DiagnosticsDriver.def @@ -107,6 +107,9 @@ WARNING(incremental_requires_build_record_entry,none, "ignoring -incremental; output file map has no master dependencies " "entry (\"%0\" under \"\")", (StringRef)) +WARNING(unable_to_open_incremental_comparison_log,none, +"unable to open incremental comparison log file '%0'", (StringRef)) + ERROR(error_os_minimum_deployment,none, "Swift requires a minimum deployment target of %0", (StringRef)) ERROR(error_sdk_too_old,none, @@ -114,6 +117,10 @@ ERROR(error_sdk_too_old,none, ERROR(error_ios_maximum_deployment_32,none, "iOS %0 does not support 32-bit programs", (unsigned)) +ERROR(error_unsupported_target_variant,none, + "unsupported '%select{-target|-target-variant}1' value '%0'; use 'ios-macabi' instead", + (StringRef, bool)) + WARNING(warn_arclite_not_found_when_link_objc_runtime,none, "unable to find Objective-C runtime support library 'arclite'; " "pass '-no-link-objc-runtime' to silence this warning", ()) @@ -163,10 +170,34 @@ WARNING(warn_opt_remark_disabled, none, WARNING(warn_ignoring_batch_mode,none, "ignoring '-enable-batch-mode' because '%0' was also specified", (StringRef)) +WARNING(warn_ignoring_wmo, none, + "ignoring '-wmo' because '-dump-ast' was also specified", ()) + +WARNING(warn_ignoring_source_range_dependencies,none, +"ignoring '-enable-source-range-dependencies' because '%0' was also specified", (StringRef)) + +WARNING(warn_bad_swift_ranges_header,none, +"ignoring '-enable-source-range-dependencies' because of bad header in '%0'", (StringRef)) + +WARNING(warn_bad_swift_ranges_format,none, +"ignoring '-enable-source-range-dependencies' because of bad format '%1' in '%0'", (StringRef, StringRef)) + WARNING(warn_use_filelists_deprecated, none, "the option '-driver-use-filelists' is deprecated; use " "'-driver-filelist-threshold=0' instead", ()) +WARNING(warn_unable_to_load_swift_ranges, none, +"unable to load swift ranges file \"%0\", %1", +(StringRef, StringRef)) + +WARNING(warn_unable_to_load_compiled_swift, none, +"unable to load previously compiled swift file \"%0\", %1", +(StringRef, StringRef)) + +WARNING(warn_unable_to_load_primary, none, +"unable to load primary swift file \"%0\", %1", +(StringRef, StringRef)) + ERROR(cannot_find_migration_script, none, "missing migration script from path '%0'", (StringRef)) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index b171c0f7dd92b..00c708a7b85b1 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -71,6 +71,10 @@ ERROR(error_option_requires_sanitizer, none, "option '%0' requires a sanitizer to be enabled. Use -sanitize= to " "enable a sanitizer", (StringRef)) +WARNING(warning_option_requires_specific_sanitizer, none, + "option '%0' has no effect when '%1' sanitizer is disabled. Use -sanitize=%1 to " + "enable the sanitizer", (StringRef, StringRef)) + ERROR(error_option_missing_required_argument, none, "option '%0' is missing a required argument (%1)", (StringRef, StringRef)) @@ -90,6 +94,8 @@ ERROR(error_unknown_arg,none, "unknown argument: '%0'", (StringRef)) ERROR(error_invalid_arg_value,none, "invalid value '%1' in '%0'", (StringRef, StringRef)) +WARNING(warning_type_fingerprints_require_fine_grained_dependencies,none, + "Type fingerprints require fine-grained dependencies", ()) WARNING(warning_cannot_multithread_batch_mode,none, "ignoring -num-threads argument; cannot multithread batch mode", ()) ERROR(error_unsupported_option_argument,none, @@ -116,6 +122,10 @@ ERROR(error_mode_cannot_emit_dependencies,none, "this mode does not support emitting dependency files", ()) ERROR(error_mode_cannot_emit_reference_dependencies,none, "this mode does not support emitting reference dependency files", ()) +ERROR(error_mode_cannot_emit_swift_ranges,none, +"this mode does not support emitting unparsed ranges files", ()) +ERROR(error_mode_cannot_emit_compiled_source,none, +"this mode does not support emitting compiled source files", ()) ERROR(error_mode_cannot_emit_header,none, "this mode does not support emitting Objective-C headers", ()) ERROR(error_mode_cannot_emit_loaded_module_trace,none, @@ -134,6 +144,10 @@ ERROR(cannot_emit_ir_skipping_function_bodies,none, WARNING(emit_reference_dependencies_without_primary_file,none, "ignoring -emit-reference-dependencies (requires -primary-file)", ()) +WARNING(emit_swift_ranges_without_primary_file,none, +"ignoring -emit-swift-ranges (requires -primary-file)", ()) +WARNING(emit_compiled_source_without_primary_file,none, +"ignoring -emit-compiled-source (requires -primary-file)", ()) ERROR(error_bad_module_name,none, "module name \"%0\" is not a valid identifier" @@ -202,6 +216,9 @@ ERROR(repl_must_be_initialized,none, ERROR(error_doing_code_completion,none, "compiler is in code completion mode (benign diagnostic)", ()) +WARNING(completion_reusing_astcontext,none, + "completion reusing previous ASTContext (benign diagnostic)", ()) + ERROR(verify_encountered_fatal,none, "fatal error encountered while in -verify mode", ()) @@ -232,10 +249,44 @@ ERROR(error_formatting_invalid_range,none, WARNING(stats_disabled,none, "compiler was not built with support for collecting statistics", ()) +WARNING(tbd_warn_truncating_version,none, + "truncating %select{current|compatibility}0 version '%1' in TBD file " + "to fit in 32-bit space used by old mach-o format", + (unsigned, StringRef)) + +ERROR(tbd_err_invalid_version,none, + "invalid dynamic library %select{current|compatibility}0 version '%1'", + (unsigned, StringRef)) + WARNING(tbd_only_supported_in_whole_module,none, "TBD generation is only supported when the whole module can be seen", ()) +WARNING(linker_directives_choice_confusion,none, + "only one of -emit-ldadd-cfile-path and -module-installname-map-file can be specified;" + "the c file won't be generated", + ()) + +ERROR(previous_installname_map_missing,none, + "cannot open previous install name map from %0", + (StringRef)) + +ERROR(previous_installname_map_corrupted,none, + "previous install name map from %0 is malformed", + (StringRef)) + +REMARK(default_previous_install_name, none, + "default previous install name for %0 is %1", (StringRef, StringRef)) + +REMARK(platform_previous_install_name, none, + "previous install name for %0 in %1 is %2", (StringRef, StringRef, StringRef)) + +ERROR(unknown_platform_name, none, + "unkown platform name %0", (StringRef)) + +ERROR(cannot_find_install_name, none, + "cannot find previous install name for module %0 in %1", (StringRef, StringRef)) + ERROR(symbol_in_tbd_not_in_ir,none, "symbol '%0' (%1) is in TBD file, but not in generated IR", (StringRef, StringRef)) @@ -270,6 +321,14 @@ ERROR(error_invalid_debug_prefix_map, none, "invalid argument '%0' to -debug-prefix-map; it must be of the form " "'original=remapped'", (StringRef)) + +ERROR(error_unable_to_write_swift_ranges_file, none, +"unable to write unparsed ranges file '$0': %1", (StringRef, StringRef)) + +ERROR(error_unable_to_write_compiled_source_file, none, +"unable to write compiled source file: '$0': %1", (StringRef, StringRef)) + + ERROR(invalid_vfs_overlay_file,none, "invalid virtual overlay file '%0'", (StringRef)) @@ -307,13 +366,16 @@ NOTE(compiled_module_invalid,none, NOTE(compiled_module_invalid_reason,none, "unable to load compiled module '%0': %1", (StringRef, StringRef)) -ERROR(error_extracting_dependencies_from_cached_module,none, - "error extracting dependencies from cached module '%0'", - (StringRef)) ERROR(unknown_forced_module_loading_mode,none, "unknown value for SWIFT_FORCE_MODULE_LOADING variable: '%0'", (StringRef)) +REMARK(interface_file_lock_failure,none, + "could not acquire lock file for module interface '%0'", (StringRef)) + +REMARK(interface_file_lock_timed_out,none, + "timed out waiting to acquire lock file for module interface '%0'", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index 6ec0f78f191c8..d97909581b1b9 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -80,8 +80,6 @@ ERROR(decl_kind_changed,none,"%0 has been changed to a %1", (StringRef, StringRe ERROR(optional_req_changed,none,"%0 is %select{now|no longer}1 an optional requirement", (StringRef, bool)) -ERROR(var_let_changed,none,"%0 changes from %select{var|let}1 to %select{let|var}1", (StringRef, bool)) - ERROR(no_longer_open,none,"%0 is no longer open for subclassing", (StringRef)) ERROR(func_type_escaping_changed,none,"%0 has %select{removed|added}2 @escaping in %1", (StringRef, StringRef, bool)) @@ -100,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef, ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef)) +ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef)) + +ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 10baf8fd4bd2b..1cbb3766c86ee 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -207,7 +207,7 @@ ERROR(lex_conflict_marker_in_file,none, //------------------------------------------------------------------------------ NOTE(note_in_decl_extension,none, - "in %select{declaration|extension}0 of %1", (bool, Identifier)) + "in %select{declaration|extension}0 of %1", (bool, DeclNameRef)) ERROR(line_directive_style_deprecated,none, "#line directive was renamed to #sourceLocation", ()) @@ -591,8 +591,6 @@ ERROR(sil_dbg_unknown_key,none, "unknown key '%0' in debug variable declaration", (StringRef)) ERROR(sil_objc_with_tail_elements,none, "alloc_ref [objc] cannot have tail allocated elements", ()) -ERROR(found_unqualified_instruction_in_qualified_function,none, - "found unqualified instruction in qualified function '%0'", (StringRef)) ERROR(sil_expected_access_kind,none, "%0 instruction must have explicit access kind", (StringRef)) ERROR(sil_expected_access_enforcement,none, @@ -605,9 +603,6 @@ ERROR(sil_keypath_unknown_component_kind,none, ERROR(sil_keypath_computed_property_missing_part,none, "keypath %select{gettable|settable}0_property component needs an " "%select{id and getter|id, getter, and setter}0", (bool)) -ERROR(sil_keypath_external_missing_part,none, - "keypath external component with indices needs an indices_equals and " - "indices_hash function", ()) ERROR(sil_keypath_no_root,none, "keypath must have a root component declared",()) ERROR(sil_keypath_index_not_hashable,none, @@ -684,8 +679,6 @@ ERROR(sil_witness_protocol_conformance_not_found,none, "sil protocol conformance not found", ()) // SIL Coverage Map -ERROR(sil_coverage_func_not_found, none, - "sil function not found %0", (Identifier)) ERROR(sil_coverage_invalid_hash, none, "expected coverage hash", ()) ERROR(sil_coverage_expected_lbrace, none, @@ -810,6 +803,12 @@ ERROR(sil_box_expected_r_brace,none, ERROR(sil_box_expected_r_angle,none, "expected '>' to complete SIL box generic argument list", ()) +// SIL function types +ERROR(sil_function_subst_expected_l_angle,none, + "expected '<' to begin SIL function type substitution list after 'for'", ()) +ERROR(sil_function_subst_expected_r_angle,none, + "expected '>' to begin SIL function type substitution list after 'for'", ()) + // Opaque types ERROR(opaque_mid_composition,none, "'some' should appear at the beginning of a composition", ()) @@ -870,8 +869,6 @@ ERROR(expected_parameter_colon,PointsToFirstBadToken, "expected ':' following argument label and parameter name", ()) ERROR(expected_assignment_instead_of_comparison_operator,none, "expected '=' instead of '==' to assign default value for parameter", ()) -ERROR(missing_parameter_type,PointsToFirstBadToken, - "parameter requires an explicit type", ()) ERROR(multiple_parameter_ellipsis,none, "only a single variadic parameter '...' is permitted", ()) ERROR(parameter_vararg_default,none, @@ -895,6 +892,8 @@ ERROR(parameter_operator_keyword_argument,none, ERROR(parameter_unnamed,none, "unnamed parameters must be written with the empty name '_'", ()) +WARNING(parameter_unnamed_warn,none, + "unnamed parameters must be written with the empty name '_'", ()) ERROR(parameter_curry_syntax_removed,none, "cannot have more than one parameter list", ()) @@ -934,6 +933,8 @@ ERROR(statement_same_line_without_semi,none, "consecutive statements on a line must be separated by ';'", ()) ERROR(invalid_label_on_stmt,none, "labels are only valid on loops, if, and switch statements", ()) +ERROR(labeled_block_needs_do,none, + "labeled block needs 'do'", ()) ERROR(snake_case_deprecated,none, "%0 has been replaced with %1 in Swift 3", @@ -970,8 +971,6 @@ ERROR(expected_expr_throw,PointsToFirstBadToken, // Yield Statment ERROR(expected_expr_yield,PointsToFirstBadToken, "expected expression in 'yield' statement", ()) -ERROR(unexpected_arg_label_yield,none, - "unexpected argument label in 'yield' statement", ()) // Defer Statement ERROR(expected_lbrace_after_defer,PointsToFirstBadToken, @@ -1040,7 +1039,11 @@ ERROR(expected_expr_repeat_while,PointsToFirstBadToken, "expected expression in 'repeat-while' condition", ()) ERROR(do_while_now_repeat_while,none, - "'do-while' statement is not allowed; use 'repeat-while' instead", ()) + "'do-while' statement is not allowed", ()) +NOTE(do_while_expected_repeat_while, none, + "did you mean 'repeat-while' statement?", ()) +NOTE(do_while_expected_separate_stmt, none, + "did you mean separate 'do' and 'while' statements?", ()) // Do/Catch Statement ERROR(expected_lbrace_after_do,PointsToFirstBadToken, @@ -1282,12 +1285,6 @@ ERROR(expr_selector_expected_rparen,PointsToFirstBadToken, "expected ')' to complete '#selector' expression", ()) // Type-of expressions. -ERROR(expr_typeof_expected_label_of,PointsToFirstBadToken, - "expected argument label 'of:' within 'type(...)'", ()) -ERROR(expr_typeof_expected_expr,PointsToFirstBadToken, - "expected an expression within 'type(of: ...)'", ()) -ERROR(expr_typeof_expected_rparen,PointsToFirstBadToken, - "expected ')' to complete 'type(of: ...)' expression", ()) ERROR(expr_dynamictype_deprecated,PointsToFirstBadToken, "'.dynamicType' is deprecated. Use 'type(of: ...)' instead", ()) @@ -1344,6 +1341,11 @@ ERROR(attr_expected_comma,none, ERROR(attr_expected_string_literal,none, "expected string literal in '%0' attribute", (StringRef)) +ERROR(attr_missing_label,PointsToFirstBadToken, + "missing label '%0:' in '@%1' attribute", (StringRef, StringRef)) +ERROR(attr_expected_label,none, + "expected label '%0:' in '@%1' attribute", (StringRef, StringRef)) + ERROR(alignment_must_be_positive_integer,none, "alignment value must be a positive integer literal", ()) @@ -1412,6 +1414,32 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none, "unexpected version number in '%0' attribute for non-specific platform " "'*'", (StringRef)) +// originallyDefinedIn +ERROR(originally_defined_in_missing_rparen,none, + "expected ')' in @_originallyDefinedIn argument list", ()) + +ERROR(originally_defined_in_unrecognized_platform,none, + "unrecognized platform name in @_originallyDefinedIn argument list", ()) + +ERROR(originally_defined_in_unrecognized_property,none, + "unrecognized property in @_originallyDefinedIn argument list", ()) + +ERROR(originally_defined_in_need_original_module_name,none, + "expected 'module: \"original\"' in the first argument to " + "@_originallyDefinedIn", ()) + +ERROR(originally_defined_in_need_nonempty_module_name,none, + "original module name cannot be empty in @_originallyDefinedIn", ()) + +ERROR(originally_defined_in_need_platform_version,none, + "expected at least one platform version in @_originallyDefinedIn", ()) + +WARNING(originally_defined_in_major_minor_only,none, + "@_originallyDefinedIn only uses major and minor version number", ()) + +WARNING(originally_defined_in_missing_platform_name,none, + "* as platform name has no effect", ()) + // convention ERROR(convention_attribute_expected_lparen,none, "expected '(' after 'convention' attribute", ()) @@ -1419,6 +1447,13 @@ ERROR(convention_attribute_expected_name,none, "expected convention name identifier in 'convention' attribute", ()) ERROR(convention_attribute_expected_rparen,none, "expected ')' after convention name for 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_label,none, + "expected 'cType' label in 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_colon,none, + "expected ':' after 'cType' for 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_string,none, + "expected string literal containing clang type for 'cType' in " + "'convention' attribute", ()) ERROR(convention_attribute_witness_method_expected_colon,none, "expected ':' after 'witness_method' for 'convention' attribute", ()) ERROR(convention_attribute_witness_method_expected_protocol,none, @@ -1482,10 +1517,6 @@ WARNING(attr_warn_unused_result_removed,none, ERROR(attr_warn_unused_result_expected_rparen,none, "expected ')' after 'warn_unused_result' attribute", ()) -// escaping -ERROR(attr_escaping_conflicts_noescape,none, - "@escaping conflicts with @noescape", ()) - // _specialize ERROR(attr_specialize_missing_colon,none, "missing ':' after %0 in '_specialize' attribute", (StringRef)) @@ -1512,6 +1543,40 @@ ERROR(attr_specialize_expected_partial_or_full,none, ERROR(attr_implements_expected_member_name,PointsToFirstBadToken, "expected a member name as second parameter in '_implements' attribute", ()) +// differentiable +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. +ERROR(attr_differentiable_expected_function_name,PointsToFirstBadToken, + "expected a %0 function name", (StringRef)) +ERROR(attr_differentiable_expected_parameter_list,PointsToFirstBadToken, + "expected a list of parameters to differentiate with respect to", ()) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. +ERROR(attr_differentiable_use_wrt_not_withrespectto,none, + "use 'wrt:' to specify parameters to differentiate with respect to", ()) +ERROR(attr_differentiable_expected_label,none, + "expected either 'wrt:' or a function specifier label, e.g. 'jvp:', " + "or 'vjp:'", ()) +ERROR(attr_differentiable_unexpected_argument,none, + "unexpected argument '%0' in '@differentiable' attribute", (StringRef)) +// TODO(TF-1001): Remove diagnostic when deprecated `jvp:`, `vjp:` are removed. +WARNING(attr_differentiable_jvp_vjp_deprecated_warning,none, + "'jvp:' and 'vjp:' arguments in '@differentiable' attribute are " + "deprecated; use '@derivative' attribute for derivative registration " + "instead", ()) + +// differentiation `wrt` parameters clause +ERROR(expected_colon_after_label,PointsToFirstBadToken, + "expected a colon ':' after '%0'", (StringRef)) +ERROR(diff_params_clause_expected_parameter,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter name, " + "parameter index, or 'self'", ()) +ERROR(diff_params_clause_expected_parameter_unnamed,PointsToFirstBadToken, + "expected a parameter, which can be a function parameter index or 'self'", + ()) + +// Automatic differentiation attributes +ERROR(autodiff_attr_expected_original_decl_name,PointsToFirstBadToken, + "expected an original function name", ()) + //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ @@ -1528,6 +1593,8 @@ ERROR(expected_generics_type_restriction,none, (Identifier)) ERROR(requires_single_equal,none, "use '==' for same-type requirements rather than '='", ()) +ERROR(requires_comma,none, + "expected ',' to separate the requirements of this 'where' clause", ()) ERROR(expected_requirement_delim,none, "expected ':' or '==' to indicate a conformance or same-type requirement", ()) @@ -1584,6 +1651,8 @@ ERROR(empty_version_string,none, WARNING(unknown_platform_condition_argument,none, "unknown %0 for build configuration '%1'", (StringRef, StringRef)) +WARNING(renamed_platform_condition_argument,none, + "'%0' has been renamed to '%1'", (StringRef, StringRef)) WARNING(likely_simulator_platform_condition,none, "platform condition appears to be testing for simulator environment; " "use 'targetEnvironment(simulator)' instead", diff --git a/include/swift/AST/DiagnosticsRefactoring.def b/include/swift/AST/DiagnosticsRefactoring.def index 2eb5039865fc3..1925012d2cc60 100644 --- a/include/swift/AST/DiagnosticsRefactoring.def +++ b/include/swift/AST/DiagnosticsRefactoring.def @@ -64,8 +64,6 @@ ERROR(invalid_default_location, none, "given location is not on a default statem ERROR(no_parent_switch, none, "cannot find enclosing switch statement", ()) -ERROR(no_subject_enum, none, "cannot find subject enum in the enclosing switch statement", ()) - ERROR(no_remaining_cases, none, "no remaining cases to expand", ()) WARNING(mismatched_rename, none, "the name at the given location cannot be renamed to '%0'", (StringRef)) diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index f56d3653ac373..32243bb06b835 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -116,7 +116,7 @@ ERROR(unsupported_c_function_pointer_conversion,none, ERROR(c_function_pointer_from_function_with_context,none, "a C function pointer cannot be formed from a " "%select{local function|closure}0 that captures " - "%select{context|generic parameters|dynamic Self type|<}1", + "%select{context|generic parameters|dynamic Self type}1", (bool, unsigned)) ERROR(objc_selector_malformed,none,"the type ObjectiveC.Selector is malformed", @@ -130,24 +130,33 @@ ERROR(capture_before_declaration_defer,none, NOTE(captured_value_declared_here,none, "captured value declared here", ()) +#define SELECT_ESCAPING_CLOSURE_KIND "escaping %select{local function|closure}0" + // Invalid escaping capture diagnostics. ERROR(escaping_inout_capture,none, - "escaping closure captures 'inout' parameter %0", (Identifier)) + SELECT_ESCAPING_CLOSURE_KIND + " captures 'inout' parameter %1", + (unsigned, Identifier)) NOTE(inout_param_defined_here,none, "parameter %0 is declared 'inout'", (Identifier)) ERROR(escaping_mutable_self_capture,none, - "escaping closure captures mutating 'self' parameter", ()) + SELECT_ESCAPING_CLOSURE_KIND + " captures mutating 'self' parameter", (unsigned)) ERROR(escaping_noescape_param_capture,none, - "escaping closure captures non-escaping parameter %0", (Identifier)) + SELECT_ESCAPING_CLOSURE_KIND + " captures non-escaping parameter %1", (unsigned, Identifier)) NOTE(noescape_param_defined_here,none, "parameter %0 is implicitly non-escaping", (Identifier)) ERROR(escaping_noescape_var_capture,none, - "escaping closure captures non-escaping value", ()) + SELECT_ESCAPING_CLOSURE_KIND + " captures non-escaping value", (unsigned)) NOTE(value_captured_here,none,"captured here", ()) +#undef SELECT_ESCAPING_CLOSURE_KIND + NOTE(value_captured_transitively,none, "captured indirectly by this call", ()) @@ -213,8 +222,6 @@ ERROR(self_inside_catch_superselfinit,none, "'self' used inside 'catch' block reachable from " "%select{super|self}0.init call", (unsigned)) -ERROR(return_from_init_without_initing_self,none, - "return from initializer before 'self.init' call or assignment to 'self'", ()) ERROR(return_from_init_without_initing_stored_properties,none, "return from initializer without initializing all" " stored properties", ()) @@ -315,9 +322,6 @@ ERROR(integer_conversion_overflow_builtin_types,none, WARNING(integer_conversion_overflow_warn,none, "integer overflows when converted from %0 to %1", (Type, Type)) -ERROR(integer_conversion_sign_error,none, - "negative integer cannot be converted to unsigned type %0", - (Type)) ERROR(negative_integer_literal_overflow_unsigned,none, "negative integer '%1' overflows when stored into unsigned type %0", (Type, StringRef)) @@ -399,16 +403,27 @@ NOTE(constexpr_unsupported_instruction_found_here,none, "operation" NOTE(constexpr_found_callee_with_no_body, none, "encountered call to '%0' whose body is not available. " - "Imported functions must be marked '@inlinable' to constant evaluate.", + "Imported functions must be marked '@inlinable' to constant evaluate", (StringRef)) NOTE(constexpr_callee_with_no_body, none, "%select{|calls a }0function whose body is not available", (bool)) +NOTE(constexpr_found_call_with_unknown_arg, none, + "encountered call to '%0' where the %1 argument is not a constant", + (StringRef, StringRef)) +NOTE(constexpr_call_with_unknown_arg, none, + "%select{|makes a }0function call with non-constant arguments", (bool)) + NOTE(constexpr_untracked_sil_value_use_found, none, "encountered use of a variable not tracked by the evaluator", ()) NOTE(constexpr_untracked_sil_value_used_here, none, "untracked variable used %select{here|by this call}0", (bool)) +NOTE(constexpr_unevaluable_cast_found, none, + "encountered an unevaluable cast", ()) +NOTE(constexpr_unevaluable_cast_used_here, none, + "unevaluable cast encountered %select{here|by this call}0", (bool)) + NOTE(constexpr_unresolvable_witness_call, none, "encountered unresolvable witness method call: '%0'", (StringRef)) NOTE(constexpr_no_witness_table_entry, none, "cannot find witness table entry " @@ -500,24 +515,26 @@ NOTE(try_branch_doesnt_yield, none, "missing yield when error is " // OS log optimization diagnostics. -ERROR(oslog_message_argument_not_found, none, "no argument of type %0 in " - " the os log call", (Identifier)) +ERROR(oslog_non_const_interpolation_options, none, "interpolation arguments " + "like format and privacy options must be constants", ()) -ERROR(oslog_dynamic_message, none, "os log methods must be passed a string " - "interpolation literal. 'OSLogMessage' must not be constructed explicitly", - ()) +ERROR(oslog_const_evaluable_fun_error, none, "evaluation of constant-evaluable " + "function '%0' failed", (StringRef)) -ERROR(oslog_const_evaluation_error, none, "constant evaluating 'OSLogMessage'" - "implementation failed.", ()) +ERROR(oslog_fail_stop_error, none, "constant evaluation of log call failed " + "with fatal error", ()) -ERROR(oslog_non_constant_message, none, "'OSLogMessage' struct is " - "unoptimizable: 'init' is not constant evaluable", ()) +ERROR(oslog_non_constant_message, none, "'OSLogMessage' instance passed to the " + "log call is not a constant", ()) -ERROR(oslog_non_constant_interpolation, none, "'OSLogInterpolation' struct is " - "unoptimizable: 'init' is not constant evaluable", ()) +ERROR(oslog_non_constant_interpolation, none, "'OSLogInterpolation' instance " + "passed to 'OSLogMessage.init' is not a constant", ()) ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a " - "constant: formatting and privacy options must be literals", (StringRef)) + "constant", (StringRef)) + +ERROR(oslog_message_alive_after_opts, none, "OSLogMessage instance must not " + "be explicitly created and must be deletable", ()) ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer " "builtin must used only on string literals", ()) @@ -532,6 +549,11 @@ ERROR(polymorphic_builtin_passed_type_without_static_overload, none, "Static" "overload implied by passing argument of type %2", (Identifier, StringRef, Type)) +ERROR(box_to_stack_cannot_promote_box_to_stack_due_to_escape_alloc, none, + "Can not promote value from heap to stack due to value escaping", ()) +NOTE(box_to_stack_cannot_promote_box_to_stack_due_to_escape_location, none, + "value escapes here", ()) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7cb894812706f..3d46cd38c1d43 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -57,7 +57,7 @@ NOTE(opaque_return_type_declared_here,none, //------------------------------------------------------------------------------ ERROR(ambiguous_member_overload_set,none, - "ambiguous reference to member %0", (DeclName)) + "ambiguous reference to member %0", (DeclNameRef)) ERROR(ambiguous_reference_to_decl,none, "ambiguous reference to %0 %1", (DescriptiveDeclKind, DeclName)) ERROR(no_overloads_match_exactly_in_call,none, @@ -69,6 +69,13 @@ ERROR(no_overloads_match_exactly_in_call_no_labels,none, ERROR(no_overloads_match_exactly_in_call_special,none, "no exact matches in call to %0", (DescriptiveDeclKind)) +ERROR(no_overloads_match_exactly_in_assignment,none, + "no exact matches in assignment to %0", + (DeclBaseName)) + +NOTE(candidate_partial_match,none, + "candidate has partially matching parameter list %0", + (StringRef)) ERROR(ambiguous_subscript,none, "ambiguous subscript with base type %0 and index type %1", @@ -78,25 +85,25 @@ ERROR(could_not_find_value_subscript,none, (Type)) ERROR(could_not_find_tuple_member,none, - "value of tuple type %0 has no member %1", (Type, DeclName)) + "value of tuple type %0 has no member %1", (Type, DeclNameRef)) ERROR(could_not_find_value_member,none, - "value of type %0 has no member %1", (Type, DeclName)) + "value of type %0 has no member %1", (Type, DeclNameRef)) ERROR(could_not_find_value_member_corrected,none, "value of type %0 has no member %1; did you mean %2?", - (Type, DeclName, DeclName)) + (Type, DeclNameRef, DeclName)) ERROR(could_not_find_value_dynamic_member_corrected,none, "value of type %0 has no dynamic member %2 using key path from root type %1; did you mean %3?", - (Type, Type, DeclName, DeclName)) + (Type, Type, DeclNameRef, DeclName)) ERROR(could_not_find_value_dynamic_member,none, "value of type %0 has no dynamic member %2 using key path from root type %1", - (Type, Type, DeclName)) + (Type, Type, DeclNameRef)) ERROR(could_not_find_type_member,none, - "type %0 has no member %1", (Type, DeclName)) + "type %0 has no member %1", (Type, DeclNameRef)) ERROR(could_not_find_type_member_corrected,none, "type %0 has no member %1; did you mean %2?", - (Type, DeclName, DeclName)) + (Type, DeclNameRef, DeclName)) ERROR(could_not_find_subscript_member_did_you_mean,none, "value of type %0 has no property or method named 'subscript'; " @@ -104,7 +111,8 @@ ERROR(could_not_find_subscript_member_did_you_mean,none, (Type)) ERROR(could_not_find_enum_case,none, - "enum type %0 has no case %1; did you mean %2", (Type, DeclName, DeclName)) + "enum type %0 has no case %1; did you mean %2?", + (Type, DeclNameRef, DeclName)) NOTE(did_you_mean_raw_type,none, "did you mean to specify a raw type on the enum declaration?", ()) @@ -123,39 +131,37 @@ ERROR(expected_result_in_contextual_member,none, ERROR(unexpected_arguments_in_enum_case,none, "enum case %0 has no associated values", (DeclName)) -ERROR(unexpected_arguments_in_contextual_member,none, - "%0 %1 is not a function", (DescriptiveDeclKind, DeclName)) ERROR(could_not_use_value_member,none, - "member %1 cannot be used on value of type %0", (Type, DeclName)) + "member %1 cannot be used on value of type %0", (Type, DeclNameRef)) ERROR(could_not_use_type_member,none, - "member %1 cannot be used on type %0", (Type, DeclName)) + "member %1 cannot be used on type %0", (Type, DeclNameRef)) ERROR(could_not_use_type_member_on_instance,none, "static member %1 cannot be used on instance of type %0", - (Type, DeclName)) + (Type, DeclNameRef)) ERROR(could_not_use_enum_element_on_instance,none, "enum case %0 cannot be used as an instance member", - (DeclName)) + (DeclNameRef)) ERROR(could_not_use_type_member_on_protocol_metatype,none, "static member %1 cannot be used on protocol metatype %0", - (Type, DeclName)) + (Type, DeclNameRef)) ERROR(could_not_use_instance_member_on_type,none, "instance member %1" "%select{| of type %2}3 cannot be used on" "%select{| instance of nested}3 type %0", - (Type, DeclName, Type, bool)) + (Type, DeclNameRef, Type, bool)) ERROR(could_not_use_member_on_existential,none, "member %1 cannot be used on value of protocol type %0; use a generic" " constraint instead", - (Type, DeclName)) + (Type, DeclNameRef)) FIXIT(replace_with_type,"%0",(Type)) FIXIT(insert_type_qualification,"%0.",(Type)) ERROR(candidate_inaccessible,none, "%0 is inaccessible due to " "'%select{private|fileprivate|internal|%error|%error}1' protection level", - (DeclName, AccessLevel)) + (DeclBaseName, AccessLevel)) NOTE(note_candidate_inaccessible,none, "%0 is inaccessible due to " @@ -243,6 +249,8 @@ ERROR(cannot_subscript_ambiguous_base,none, ERROR(cannot_subscript_nil_literal,none, "cannot subscript a nil literal value", ()) +ERROR(conditional_cast_from_nil,none, + "nil literal cannot be the source of a conditional cast", ()) ERROR(cannot_pass_rvalue_inout_subelement,none, "cannot pass immutable value as inout argument: %0", @@ -259,9 +267,6 @@ ERROR(cannot_pass_rvalue_inout,none, ERROR(cannot_provide_default_value_inout,none, "cannot provide default value to inout parameter %0", (Identifier)) -ERROR(cannot_assign_to_literal,none, - "cannot assign to a literal value", ()) - ERROR(cannot_call_with_no_params,none, "cannot invoke %select{|initializer for type }1'%0' with no arguments", (StringRef, bool)) @@ -273,9 +278,6 @@ ERROR(cannot_call_with_params, none, ERROR(cannot_call_non_function_value,none, "cannot call value of non-function type %0", (Type)) -ERROR(wrong_argument_labels_overload,none, - "argument labels '%0' do not match any available overloads", (StringRef)) - ERROR(no_candidates_match_result_type,none, "no '%0' candidates produce the expected contextual result type %1", (StringRef, Type)) @@ -301,11 +303,11 @@ ERROR(cannot_invoke_closure_type,none, ERROR(cannot_infer_closure_type,none, "unable to infer closure type in the current context", ()) ERROR(cannot_infer_closure_result_type,none, - "unable to infer complex closure return type; " - "add explicit type to disambiguate", ()) -FIXIT(insert_closure_return_type, - "%select{| () }1-> %0 %select{|in }1", - (Type, bool)) + "unable to infer%select{ complex|}0 closure return type; " + "add explicit type to disambiguate", (bool)) +FIXIT(insert_closure_return_type_placeholder, + "%select{| () }0-> <#Result#> %select{|in }0", + (bool)) ERROR(incorrect_explicit_closure_result,none, "declared closure result %0 is incompatible with contextual type %1", @@ -336,6 +338,10 @@ ERROR(cannot_convert_initializer_value,none, "cannot convert value of type %0 to specified type %1", (Type,Type)) ERROR(cannot_convert_initializer_value_protocol,none, "value of type %0 does not conform to specified type %1", (Type,Type)) +ERROR(cannot_convert_initializer_value_anyobject,none, + "value of type %0 expected to be instance of class or " + "class-constrained type", + (Type, Type)) ERROR(cannot_convert_initializer_value_nil,none, "'nil' cannot initialize specified type %0", (Type)) @@ -344,6 +350,10 @@ ERROR(cannot_convert_to_return_type,none, (Type,Type)) ERROR(cannot_convert_to_return_type_protocol,none, "return expression of type %0 does not conform to %1", (Type,Type)) +ERROR(cannot_convert_return_type_to_anyobject,none, + "return expression of type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_to_return_type_nil,none, "'nil' is incompatible with return type %0", (Type)) @@ -397,11 +407,51 @@ ERROR(cannot_convert_argument_value_generic,none, "cannot convert value of type %0 (%1) to expected argument type %2 (%3)", (Type, StringRef, Type, StringRef)) +// @_nonEphemeral conversion diagnostics +ERROR(cannot_pass_type_to_non_ephemeral,none, + "cannot pass %0 to parameter; argument %1 must be a pointer that " + "outlives the call%select{| to %3}2", (Type, StringRef, bool, DeclName)) +WARNING(cannot_pass_type_to_non_ephemeral_warning,none, + "passing %0 to parameter, but argument %1 should be a pointer that " + "outlives the call%select{| to %3}2", (Type, StringRef, bool, DeclName)) +ERROR(cannot_use_inout_non_ephemeral,none, + "cannot use inout expression here; argument %0 must be a pointer that " + "outlives the call%select{| to %2}1", (StringRef, bool, DeclName)) +WARNING(cannot_use_inout_non_ephemeral_warning,none, + "inout expression creates a temporary pointer, but argument %0 should " + "be a pointer that outlives the call%select{| to %2}1", + (StringRef, bool, DeclName)) +ERROR(cannot_construct_dangling_pointer,none, + "initialization of %0 results in a dangling %select{|buffer }1pointer", + (Type, unsigned)) +WARNING(cannot_construct_dangling_pointer_warning,none, + "initialization of %0 results in a dangling %select{|buffer }1pointer", + (Type, unsigned)) +NOTE(ephemeral_pointer_argument_conversion_note,none, + "implicit argument conversion from %0 to %1 produces a pointer valid only " + "for the duration of the call%select{| to %3}2", + (Type, Type, bool, DeclName)) +NOTE(ephemeral_use_with_unsafe_pointer,none, + "use 'withUnsafe%select{Bytes|MutableBytes|Pointer|MutablePointer}0' in " + "order to explicitly convert argument to %select{buffer |buffer ||}0" + "pointer valid for a defined scope", (unsigned)) +NOTE(ephemeral_use_string_with_c_string,none, + "use the 'withCString' method on String in order to explicitly " + "convert argument to pointer valid for a defined scope", ()) +NOTE(ephemeral_use_array_with_unsafe_buffer,none, + "use the 'withUnsafe%select{Bytes|MutableBytes|BufferPointer|" + "MutableBufferPointer}0' method on Array in order to explicitly convert " + "argument to buffer pointer valid for a defined scope", (unsigned)) +NOTE(candidate_performs_illegal_ephemeral_conv,none, + "candidate expects pointer that outlives the call for parameter #%0", + (unsigned)) + ERROR(cannot_convert_argument_value_protocol,none, "argument type %0 does not conform to expected type %1", (Type, Type)) -ERROR(cannot_convert_partial_argument_value_protocol,none, - "in argument type %0, %1 does not conform to expected type %2", (Type, Type, Type)) - +ERROR(cannot_convert_argument_value_anyobject,none, + "argument type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_argument_value_nil,none, "'nil' is not compatible with expected argument type %0", (Type)) @@ -497,6 +547,10 @@ NOTE(assign_protocol_conformance_fix_it,none, ERROR(cannot_convert_assign_protocol,none, "value of type %0 does not conform to %1 in assignment", (Type, Type)) +ERROR(cannot_convert_assign_anyobject,none, + "value of type %0 expected to be an instance of " + "a class or class-constrained type in assignment", + (Type, Type)) ERROR(cannot_convert_assign_nil,none, "'nil' cannot be assigned to type %0", (Type)) @@ -510,6 +564,10 @@ ERROR(cannot_convert_subscript_assign_protocol,none, ERROR(cannot_convert_subscript_assign_nil,none, "'nil' cannot be assigned to subscript of type %0", (Type)) +NOTE(cannot_convert_candidate_result_to_contextual_type,none, + "%0 produces %1, not the expected contextual result type %2", + (DeclName, Type, Type)) + // for ... in expression ERROR(cannot_convert_sequence_element_value,none, "cannot convert sequence element type %0 to expected type %1", @@ -521,9 +579,6 @@ ERROR(cannot_convert_sequence_element_protocol,none, ERROR(throws_functiontype_mismatch,none, "invalid conversion from throwing function of type %0 to " "non-throwing function type %1", (Type, Type)) -ERROR(noescape_functiontype_mismatch,none, - "invalid conversion from non-escaping function of type %0 to " - "potentially escaping function type %1", (Type, Type)) // Key-path expressions. ERROR(expr_keypath_no_objc_runtime,none, @@ -539,7 +594,7 @@ WARNING(expr_keypath_swift3_objc_inference,none, (DeclName, Identifier)) ERROR(expr_keypath_type_of_property,none, "cannot refer to type member %0 within instance of type %1", - (DeclName, Type)) + (DeclNameRef, Type)) ERROR(expr_keypath_generic_type,none, "key path cannot refer to generic type %0", (DeclName)) ERROR(expr_keypath_not_property,none, @@ -743,24 +798,24 @@ NOTE(invalid_redecl_prev,none, "%0 previously declared here", (DeclName)) ERROR(ambiguous_type_base,none, - "%0 is ambiguous for type lookup in this context", (Identifier)) + "%0 is ambiguous for type lookup in this context", (DeclNameRef)) ERROR(invalid_member_type,none, - "%0 is not a member type of %1", (Identifier, Type)) + "%0 is not a member type of %1", (DeclNameRef, Type)) ERROR(invalid_member_type_suggest,none, "%0 does not have a member type named %1; did you mean %2?", - (Type, Identifier, Identifier)) + (Type, DeclNameRef, DeclName)) ERROR(invalid_member_reference,none, "%0 %1 is not a member type of %2", - (DescriptiveDeclKind, Identifier, Type)) + (DescriptiveDeclKind, DeclName, Type)) ERROR(ambiguous_member_type,none, - "ambiguous type name %0 in %1", (Identifier, Type)) + "ambiguous type name %0 in %1", (DeclNameRef, Type)) ERROR(no_module_type,none, - "no type named %0 in module %1", (Identifier, Identifier)) + "no type named %0 in module %1", (DeclNameRef, Identifier)) ERROR(ambiguous_module_type,none, - "ambiguous type name %0 in module %1", (Identifier, Identifier)) + "ambiguous type name %0 in module %1", (DeclNameRef, Identifier)) ERROR(use_nonmatching_operator,none, "%0 is not a %select{binary|prefix unary|postfix unary}1 operator", - (DeclName, unsigned)) + (DeclNameRef, unsigned)) ERROR(unsupported_recursion_in_associated_type_reference,none, "unsupported recursion for reference to %select{associated type|type alias}0 %1 of type %2", (bool, DeclName, Type)) @@ -781,18 +836,18 @@ ERROR(unspaced_unary_operator,none, ()) ERROR(use_unresolved_identifier,none, - "use of unresolved %select{identifier|operator}1 %0", (DeclName, bool)) + "use of unresolved %select{identifier|operator}1 %0", (DeclNameRef, bool)) ERROR(use_unresolved_identifier_corrected,none, "use of unresolved %select{identifier|operator}1 %0; did you mean '%2'?", - (DeclName, bool, StringRef)) + (DeclNameRef, bool, StringRef)) NOTE(confusable_character,none, "%select{identifier|operator}0 '%1' contains possibly confused characters; " "did you mean to use '%2'?", (bool, StringRef, StringRef)) ERROR(use_undeclared_type,none, - "use of undeclared type %0", (Identifier)) + "use of undeclared type %0", (DeclNameRef)) ERROR(use_undeclared_type_did_you_mean,none, - "use of undeclared type %0; did you mean to use '%1'?", (Identifier, StringRef)) + "use of undeclared type %0; did you mean to use '%1'?", (DeclNameRef, StringRef)) NOTE(note_typo_candidate_implicit_member,none, "did you mean the implicitly-synthesized %1 '%0'?", (StringRef, StringRef)) NOTE(note_remapped_type,none, @@ -809,7 +864,7 @@ NOTE(object_literal_resolve_import,none, (StringRef, StringRef, StringRef)) ERROR(use_local_before_declaration,none, - "use of local variable %0 before its declaration", (DeclName)) + "use of local variable %0 before its declaration", (DeclNameRef)) ERROR(unsupported_existential_type,none, "protocol %0 can only be used as a generic constraint because it has " "Self or associated type requirements", (Identifier)) @@ -866,8 +921,12 @@ ERROR(invalid_arg_count_for_operator,none, "operators must have one or two arguments", ()) ERROR(operator_in_local_scope,none, "operator functions can only be declared at global or in type scope", ()) -ERROR(nonstatic_operator_in_type,none, - "operator %0 declared in type %1 must be 'static'", (Identifier, Type)) +ERROR(nonstatic_operator_in_nominal,none, + "operator %0 declared in type %1 must be 'static'", + (Identifier, DeclName)) +ERROR(nonstatic_operator_in_extension,none, + "operator %0 declared in extension of %1 must be 'static'", + (Identifier, TypeRepr*)) ERROR(nonfinal_operator_in_class,none, "operator %0 declared in non-final class %1 must be 'final'", (Identifier, Type)) @@ -883,6 +942,8 @@ NOTE(found_this_precedence_group,none, ERROR(unknown_precedence_group,none, "unknown precedence group %0", (Identifier)) ERROR(precedence_group_cycle,none, + "cycle in '%select{lowerThan|higherThan}0' relation", (bool)) +ERROR(higher_than_precedence_group_cycle,none, "cycle in higherThan relation: %0", (StringRef)) ERROR(precedence_group_lower_within_module,none, "precedence group cannot be given lower precedence than group in same" @@ -892,6 +953,8 @@ ERROR(precedence_group_redeclared,none, "precedence group redeclared", ()) NOTE(previous_precedence_group_decl,none, "previous precedence group declaration here", ()) +NOTE(circular_reference_through_precedence_group, none, + "through reference to precedence group %0 here", (Identifier)) //------------------------------------------------------------------------------ // MARK: Expression Type Checking Errors @@ -904,14 +967,13 @@ NOTE(in_cast_expr_types,none, (Type, Type)) ERROR(types_not_convertible_use_bool_value,none, - "%0 is not convertible to %1; did you mean %0.boolValue", (Type, Type)) + "%0 is not convertible to %1; did you mean %0.boolValue?", (Type, Type)) ERROR(tuple_types_not_convertible_nelts,none, "%0 is not convertible to %1, " "tuples have a different number of elements", (Type, Type)) - ERROR(tuple_types_not_convertible,none, - "tuple type %0 is not convertible to tuple %1", (Type, Type)) + "tuple type %0 is not convertible to tuple type %1", (Type, Type)) ERROR(invalid_force_unwrap,none, "cannot force unwrap value of non-optional type %0", (Type)) @@ -1023,10 +1085,10 @@ NOTE(unwrap_with_guard,none, "if the optional value contains 'nil'", ()) ERROR(optional_base_not_unwrapped,none, "value of optional type %0 must be unwrapped to refer to member %1 of " - "wrapped base type %2", (Type, DeclName, Type)) + "wrapped base type %2", (Type, DeclNameRef, Type)) NOTE(optional_base_chain,none, "chain the optional using '?' to access member %0 only for non-'nil' " - "base values", (DeclName)) + "base values", (DeclNameRef)) ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " "or chain with '?'?", @@ -1091,26 +1153,21 @@ NOTE(candidate_expected_different_labels,none, "incorrect labels for candidate (have: '%0', expected: '%1')", (StringRef, StringRef)) +ERROR(member_shadows_function,none, + "use of %0 refers to %1 rather than %2 %3", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) ERROR(member_shadows_global_function,none, - "use of %0 refers to %1 %2 rather than %3 %4 in %5 %6", - (DeclName, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, DeclName, - DescriptiveDeclKind, DeclName)) -ERROR(member_shadows_global_function_near_match,none, - "use of %0 nearly matches %3 %4 in %5 %6 rather than %1 %2", - (DeclName, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, DeclName, - DescriptiveDeclKind, DeclName)) + "use of %0 refers to %1 rather than %2 %3 in module %4", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, DeclName)) ERROR(instance_member_use_on_type,none, "instance member %1 cannot be used on type %0; " - "did you mean to use a value of this type instead?", (Type, DeclName)) + "did you mean to use a value of this type instead?", (Type, DeclNameRef)) ERROR(instance_member_in_initializer,none, "cannot use instance member %0 within property initializer; " - "property initializers run before 'self' is available", (DeclName)) + "property initializers run before 'self' is available", (DeclNameRef)) ERROR(instance_member_in_default_parameter,none, - "cannot use instance member %0 as a default parameter", (DeclName)) - -ERROR(invalid_initialization_parameter_same_type,none, - "invalid initializer call with same type %0 as parameter", (Type)) + "cannot use instance member %0 as a default parameter", (DeclNameRef)) ERROR(missing_argument_named,none, "missing argument for parameter %0 in call", (Identifier)) @@ -1122,6 +1179,8 @@ ERROR(extra_argument_named,none, "extra argument %0 in call", (Identifier)) ERROR(extra_argument_positional,none, "extra argument in call", ()) +ERROR(extra_arguments_in_call,none, + "extra arguments at positions %0 in call", (StringRef)) ERROR(extra_argument_to_nullary_call,none, "argument passed to call that takes no arguments", ()) ERROR(extra_trailing_closure_in_call,none, @@ -1129,6 +1188,11 @@ ERROR(extra_trailing_closure_in_call,none, ERROR(trailing_closure_bad_param,none, "trailing closure passed to parameter of type %0 that does not " "accept a closure", (Type)) +NOTE(candidate_with_extraneous_args,none, + "candidate %0 requires %1 argument%s1, " + "but %2 %select{were|was}3 %select{provided|used in closure body}4", + (Type, unsigned, unsigned, bool, bool)) + ERROR(no_accessible_initializers,none, "%0 cannot be constructed because it has no accessible initializers", (Type)) @@ -1337,8 +1401,6 @@ ERROR(access_control_open_bad_decl,none, "only classes and overridable class members can be declared 'open';" " use 'public'", ()) -ERROR(invalid_decl_attribute_simple,none, - "attribute cannot be applied to declaration", ()) ERROR(invalid_decl_attribute,none, "'%0' attribute cannot be applied to this declaration", (DeclAttribute)) ERROR(invalid_decl_modifier,none, @@ -1410,6 +1472,24 @@ ERROR(corresponding_param_not_defaulted,none, NOTE(inherited_default_param_here,none, "corresponding parameter declared here", ()) +WARNING(option_set_zero_constant,none, + "static property %0 produces an empty option set", + (Identifier)) +NOTE(option_set_empty_set_init,none, + "use [] to silence this warning", ()) + +ERROR(originally_defined_in_dupe_platform,none, + "duplicate version number for platform %0", (StringRef)) + +ERROR(originally_definedin_topleve_decl,none, + "@%0 is only applicable to top-level decl", (StringRef)) + +ERROR(originally_definedin_need_available,none, + "need @available attribute for @%0", (StringRef)) + +ERROR(originally_definedin_must_after_available_version,none, + "moved version from @%0 must after introduced OS version", (StringRef)) + // Alignment attribute ERROR(alignment_not_power_of_two,none, "alignment value must be a power of two", ()) @@ -1419,8 +1499,6 @@ ERROR(indirect_case_without_payload,none, "enum case %0 without associated value cannot be 'indirect'", (Identifier)) ERROR(indirect_case_in_indirect_enum,none, "enum case in 'indirect' enum cannot also be 'indirect'", ()) -WARNING(enum_frozen_nonresilient,none, - "%0 has no effect without -enable-library-evolution", (DeclAttribute)) WARNING(enum_frozen_nonpublic,none, "%0 has no effect on non-public enums", (DeclAttribute)) @@ -1509,14 +1587,21 @@ ERROR(pattern_binds_no_variables,none, "%select{property|global variable}0 declaration does not bind any " "variables", (unsigned)) +ERROR(variable_bound_by_no_pattern,none, + "variable %0 is not bound by any pattern", + (DeclName)) WARNING(optional_ambiguous_case_ref,none, "assuming you mean '%0.%2'; did you mean '%1.%2' instead?", (StringRef, StringRef, StringRef)) NOTE(optional_fixit_ambiguous_case_ref,none, "explicitly specify 'Optional' to silence this warning", ()) +NOTE(optional_fixit_ambiguous_case_ref_switch,none, + "use 'nil' to silence this warning", ()) NOTE(type_fixit_optional_ambiguous_case_ref,none, "use '%0.%1' instead", (StringRef, StringRef)) +NOTE(type_fixit_optional_ambiguous_case_ref_switch,none, + "use '%0' instead", (StringRef)) ERROR(nscoding_unstable_mangled_name,none, "%select{private|fileprivate|nested|local}0 class %1 has an " @@ -1679,6 +1764,10 @@ ERROR(type_does_not_conform,none, ERROR(cannot_use_nil_with_this_type,none, "'nil' cannot be used in context expecting type %0", (Type)) +ERROR(type_cannot_conform_to_nsobject,none, + "cannot declare conformance to 'NSObjectProtocol' in Swift; %0 should " + "inherit 'NSObject' instead", (Type)) + ERROR(use_of_equal_instead_of_equality,none, "use of '=' in a boolean context, did you mean '=='?", ()) @@ -1713,12 +1802,18 @@ ERROR(type_does_not_conform_owner,none, ERROR(type_does_not_conform_in_decl_ref,none, "referencing %0 %1 on %2 requires that %3 conform to %4", (DescriptiveDeclKind, DeclName, Type, Type, Type)) +ERROR(type_does_not_conform_anyobject_in_decl_ref,none, + "referencing %0 %1 on %2 requires that %3 be a class type", + (DescriptiveDeclKind, DeclName, Type, Type, Type)) ERROR(type_does_not_conform_decl_owner,none, "%0 %1 requires that %2 conform to %3", (DescriptiveDeclKind, DeclName, Type, Type)) -ERROR(type_does_not_conform_in_opaque_return,none, - "return type of %0 %1 requires that %2 conform to %3", +ERROR(type_does_not_conform_anyobject_decl_owner,none, + "%0 %1 requires that %2 be a class type", (DescriptiveDeclKind, DeclName, Type, Type)) +ERROR(type_does_not_conform_in_opaque_return,none, + "return type of %0 %1 requires that %2 %select{conform to %3|be a class type}4", + (DescriptiveDeclKind, DeclName, Type, Type, bool)) ERROR(types_not_equal_decl,none, "%0 %1 requires the types %2 and %3 be equivalent", (DescriptiveDeclKind, DeclName, Type, Type)) @@ -1737,6 +1832,8 @@ NOTE(where_requirement_failure_both_subst,none, "where %0 = %1, %2 = %3", (Type, Type, Type, Type)) NOTE(requirement_implied_by_conditional_conformance,none, "requirement from conditional conformance of %0 to %1", (Type, Type)) +NOTE(wrapped_type_satisfies_requirement,none, + "wrapped type %0 satisfies this requirement; did you mean to unwrap?", (Type)) NOTE(candidate_types_conformance_requirement,none, "candidate requires that %0 conform to %1 " "(requirement specified as %2 == %3%4)", @@ -2109,10 +2206,10 @@ WARNING(append_interpolation_access_control,none, // Protocols and existentials ERROR(assoc_type_outside_of_protocol,none, "associated type %0 can only be used with a concrete type or " - "generic parameter base", (Identifier)) + "generic parameter base", (DeclNameRef)) ERROR(typealias_outside_of_protocol,none, "type alias %0 can only be used with a concrete type or " - "generic parameter base", (Identifier)) + "generic parameter base", (DeclNameRef)) ERROR(objc_protocol_inherits_non_objc_protocol,none, "@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type)) @@ -2155,9 +2252,6 @@ ERROR(recursive_same_type_constraint,none, "same-type constraint %0 == %1 is recursive", (Type, Type)) ERROR(recursive_superclass_constraint,none, "superclass constraint %0 : %1 is recursive", (Type, Type)) -ERROR(requires_same_type_conflict,none, - "%select{associated type|generic parameter}0 %1 cannot be equal to " - "both %2 and %3", (bool, Type, Type, Type)) ERROR(requires_generic_param_same_type_does_not_conform,none, "same-type constraint type %0 does not conform to required protocol %1", (Type, Identifier)) @@ -2650,6 +2744,9 @@ NOTE(missing_member_type_conformance_prevents_synthesis, none, "protocol %2, preventing synthesized conformance " "of %3 to %2", (unsigned, Type, Type, Type)) +NOTE(classes_automatic_protocol_synthesis,none, + "automatic synthesis of '%0' is not supported for classes", + (StringRef)) // Dynamic Self ERROR(dynamic_self_non_method,none, @@ -2686,6 +2783,9 @@ ERROR(attr_not_on_variadic_parameters,none, ERROR(attr_not_on_subscript_parameters,none, "'%0' must not be used on subscript parameters", (StringRef)) +ERROR(attr_ambiguous_reference_to_decl,none, + "ambiguous reference to %0 in '@%1' attribute", (DeclNameRef, StringRef)) + ERROR(override_final,none, "%0 overrides a 'final' %1", (DescriptiveDeclKind, DescriptiveDeclKind)) @@ -2727,6 +2827,10 @@ ERROR(escaping_non_function_parameter,none, NOTE(escaping_optional_type_argument, none, "closure is already escaping in optional type argument", ()) +// @_nonEphemeral attribute +ERROR(non_ephemeral_non_pointer_type,none, + "@_nonEphemeral attribute only applies to pointer types", ()) + // NSManaged attribute ERROR(attr_NSManaged_not_instance_member,none, "@NSManaged only allowed on an instance property or method", ()) @@ -2817,6 +2921,124 @@ ERROR(implements_attr_protocol_not_conformed_to,none, "containing type %0 does not conform to protocol %1", (DeclName, DeclName)) +// @differentiable +ERROR(differentiable_attr_void_result,none, + "cannot differentiate void function %0", (DeclName)) +ERROR(differentiable_attr_no_vjp_or_jvp_when_linear,none, + "cannot specify 'vjp:' or 'jvp:' for linear functions; use '@transpose' " + "attribute for transpose registration instead", ()) +ERROR(differentiable_attr_overload_not_found,none, + "%0 does not have expected type %1", (DeclNameRef, Type)) +// TODO(TF-482): Change duplicate `@differentiable` attribute diagnostic to also +// mention "same generic requirements". +ERROR(differentiable_attr_duplicate,none, + "duplicate '@differentiable' attribute with same parameters", ()) +NOTE(differentiable_attr_duplicate_note,none, + "other attribute declared here", ()) +ERROR(differentiable_attr_function_not_same_type_context,none, + "%0 is not defined in the current type context", (DeclNameRef)) +ERROR(differentiable_attr_derivative_not_function,none, + "registered derivative %0 must be a 'func' declaration", (DeclNameRef)) +ERROR(differentiable_attr_class_derivative_not_final,none, + "class member derivative must be final", ()) +ERROR(differentiable_attr_invalid_access,none, + "derivative function %0 is required to either be public or " + "'@usableFromInline' because the original function %1 is public or " + "'@usableFromInline'", (DeclNameRef, DeclName)) +ERROR(differentiable_attr_result_not_differentiable,none, + "can only differentiate functions with results that conform to " + "'Differentiable', but %0 does not conform to 'Differentiable'", (Type)) +ERROR(differentiable_attr_protocol_req_where_clause,none, + "'@differentiable' attribute on protocol requirement cannot specify " + "'where' clause", ()) +ERROR(differentiable_attr_protocol_req_assoc_func,none, + "'@differentiable' attribute on protocol requirement cannot specify " + "'jvp:' or 'vjp:'", ()) +ERROR(differentiable_attr_stored_property_variable_unsupported,none, + "'@differentiable' attribute on stored property cannot specify " + "'jvp:' or 'vjp:'", ()) +ERROR(differentiable_attr_class_member_no_dynamic_self,none, + "'@differentiable' attribute cannot be declared on class methods " + "returning 'Self'", ()) +// TODO(TF-654): Remove when differentiation supports class initializers. +ERROR(differentiable_attr_class_init_not_yet_supported,none, + "'@differentiable' attribute does not yet support class initializers", + ()) +ERROR(differentiable_attr_empty_where_clause,none, + "empty 'where' clause in '@differentiable' attribute", ()) +ERROR(differentiable_attr_where_clause_for_nongeneric_original,none, + "'where' clause is valid only when original function is generic %0", + (DeclName)) +ERROR(differentiable_attr_layout_req_unsupported,none, + "'@differentiable' attribute does not yet support layout requirements", + ()) +ERROR(overriding_decl_missing_differentiable_attr,none, + "overriding declaration is missing attribute '%0'", (StringRef)) +NOTE(protocol_witness_missing_differentiable_attr,none, + "candidate is missing attribute '%0'", (StringRef)) + +// @derivative +ERROR(derivative_attr_expected_result_tuple,none, + "'@derivative(of:)' attribute requires function to return a two-element " + "tuple of type '(value: T..., pullback: (U.TangentVector) -> T.TangentVector...)' " + "or '(value: T..., differential: (T.TangentVector...) -> U.TangentVector)'", ()) +ERROR(derivative_attr_invalid_result_tuple_value_label,none, + "'@derivative(of:)' attribute requires function to return a two-element " + "tuple (first element must have label 'value:')", ()) +ERROR(derivative_attr_invalid_result_tuple_func_label,none, + "'@derivative(of:)' attribute requires function to return a two-element " + "tuple (second element must have label 'pullback:' or 'differential:')", + ()) +ERROR(derivative_attr_result_value_not_differentiable,none, + "'@derivative(of:)' attribute requires function to return a two-element " + "tuple (first element type %0 must conform to 'Differentiable')", (Type)) +ERROR(derivative_attr_result_func_type_mismatch,none, + "function result's %0 type does not match %1", (Identifier, DeclName)) +NOTE(derivative_attr_result_func_type_mismatch_note,none, + "%0 does not have expected type %1", (Identifier, Type)) +NOTE(derivative_attr_result_func_original_note,none, + "%0 defined here", (DeclName)) +ERROR(derivative_attr_not_in_same_file_as_original,none, + "derivative not in the same file as the original function", ()) +ERROR(derivative_attr_original_stored_property_unsupported,none, + "cannot register derivative for stored property %0", (DeclNameRef)) +ERROR(derivative_attr_original_already_has_derivative,none, + "a derivative already exists for %0", (DeclName)) +NOTE(derivative_attr_duplicate_note,none, + "other attribute declared here", ()) + +// Automatic differentiation attributes +ERROR(autodiff_attr_original_decl_invalid_kind,none, + "%0 is not a 'func', 'init', 'subscript', or 'var' computed property " + "declaration", (DeclNameRef)) +ERROR(autodiff_attr_original_decl_none_valid_found,none, + "could not find function %0 with expected type %1", (DeclNameRef, Type)) +ERROR(autodiff_attr_original_decl_not_same_type_context,none, + "%0 is not defined in the current type context", (DeclNameRef)) + +// differentiation `wrt` parameters clause +ERROR(diff_function_no_parameters,none, + "%0 has no parameters to differentiate with respect to", (DeclName)) +ERROR(diff_params_clause_param_name_unknown,none, + "unknown parameter name %0", (Identifier)) +ERROR(diff_params_clause_self_instance_method_only,none, + "'self' parameter is only applicable to instance methods", ()) +ERROR(diff_params_clause_self_must_be_first,none, + "'self' parameter must come first in the parameter list", ()) +ERROR(diff_params_clause_params_not_original_order,none, + "parameters must be specified in original order", ()) +ERROR(diff_params_clause_param_index_out_of_range,none, + "parameter index is larger than total number of parameters", ()) +ERROR(diff_params_clause_no_inferred_parameters,PointsToFirstBadToken, + "no differentiation parameters could be inferred; must differentiate " + "with respect to at least one parameter conforming to 'Differentiable'", + ()) +ERROR(diff_params_clause_cannot_diff_wrt_inout_parameter,none, + "cannot differentiate with respect to 'inout' parameter (%0)", (Type)) +ERROR(diff_params_clause_param_not_differentiable,none, + "can only differentiate with respect to parameters that conform to " + "'Differentiable', but %0 does not conform to 'Differentiable'", (Type)) + //------------------------------------------------------------------------------ // MARK: Type Check Expressions //------------------------------------------------------------------------------ @@ -2828,12 +3050,9 @@ NOTE(found_candidate_type,none, ERROR(no_MaxBuiltinFloatType_found,none, "standard library error: _MaxBuiltinFloatType is not properly defined", ()) -ERROR(integer_literal_overflows_maxwidth, none, - "integer literal needs %1 bits, exceeding limit of %0 bits", - (unsigned, unsigned)) ERROR(no_member_of_module,none, - "module %0 has no member named %1", (Identifier, DeclName)) + "module %0 has no member named %1", (Identifier, DeclNameRef)) ERROR(super_with_no_base_class,none, "'super' members cannot be referenced in a root class", ()) @@ -2856,6 +3075,9 @@ ERROR(delegating_designated_init,none, "designated initializer for %0 cannot delegate (with 'self.init'); " "did you mean this to be a convenience initializer?", (Type)) +ERROR(delegating_designated_init_in_extension,none, + "designated initializer for %0 cannot delegate (with 'self.init')", + (Type)) NOTE(delegation_here,none, "delegation occurs here", ()) ERROR(chain_convenience_init,none, "must call a designated initializer of the superclass %0", @@ -2883,6 +3105,9 @@ ERROR(designated_init_in_extension,none, "designated initializer cannot be declared in an extension of %0; " "did you mean this to be a convenience initializer?", (DeclName)) +ERROR(cfclass_designated_init_in_extension,none, + "designated initializer cannot be declared in an extension of %0", + (DeclName)) ERROR(enumstruct_convenience_init,none, "delegating initializers in %0 are not marked with 'convenience'", (StringRef)) @@ -2892,7 +3117,6 @@ ERROR(nonclass_convenience_init,none, ERROR(cfclass_convenience_init,none, "convenience initializers are not supported in extensions of CF types", ()) - ERROR(dynamic_construct_class,none, "constructing an object of class type %0 with a metatype value must use " "a 'required' initializer", (Type)) @@ -2974,7 +3198,9 @@ ERROR(collection_literal_empty,none, ERROR(unresolved_member_no_inference,none, "reference to member %0 cannot be resolved without a contextual type", - (DeclName)) + (DeclNameRef)) +ERROR(cannot_infer_base_of_unresolved_member,none, + "cannot infer contextual base in reference to member %0", (DeclNameRef)) ERROR(unresolved_collection_literal,none, "cannot infer type for empty collection literal without a " "contextual type", ()) @@ -2996,8 +3222,6 @@ ERROR(missing_protocol,none, "missing protocol %0", (Identifier)) ERROR(nil_literal_broken_proto,none, "protocol 'ExpressibleByNilLiteral' is broken", ()) -ERROR(array_protocol_broken,none, - "protocol 'ExpressibleByArrayLiteral' is broken", ()) ERROR(builtin_integer_literal_broken_proto,none, "protocol '_ExpressibleByBuiltinIntegerLiteral' is broken", ()) ERROR(integer_literal_broken_proto,none, @@ -3038,8 +3262,6 @@ ERROR(should_use_empty_dictionary_literal,none, "use [:] to get an empty dictionary literal", ()) // Dictionary literals -ERROR(dictionary_protocol_broken,none, - "ExpressibleByDictionaryLiteral protocol definition is broken", ()) ERROR(type_is_not_dictionary,none, "contextual type %0 cannot be used with dictionary literal", (Type)) @@ -3065,9 +3287,9 @@ WARNING(use_of_void_pointer,none, // Ambiguities ERROR(ambiguous_decl_ref,none, - "ambiguous use of %0", (DeclName)) + "ambiguous use of %0", (DeclNameRef)) ERROR(ambiguous_operator_ref,none, - "ambiguous use of operator %0", (DeclName)) + "ambiguous use of operator %0", (DeclNameRef)) NOTE(ambiguous_because_of_trailing_closure,none, "%select{use an explicit argument label instead of a trailing closure|" "avoid using a trailing closure}0 to call %1", @@ -3096,11 +3318,20 @@ ERROR(self_assignment_var,none, ERROR(self_assignment_prop,none, "assigning a property to itself", ()) ERROR(property_use_in_closure_without_explicit_self,none, - "reference to property %0 in closure requires explicit 'self.' to make" - " capture semantics explicit", (Identifier)) + "reference to property %0 in closure requires explicit use of 'self' to" + " make capture semantics explicit", (Identifier)) ERROR(method_call_in_closure_without_explicit_self,none, - "call to method %0 in closure requires explicit 'self.' to make" + "call to method %0 in closure requires explicit use of 'self' to make" " capture semantics explicit", (Identifier)) +NOTE(note_capture_self_explicitly,none, + "capture 'self' explicitly to enable implicit 'self' in this closure", ()) +NOTE(note_reference_self_explicitly,none, + "reference 'self.' explicitly", ()) +NOTE(note_other_self_capture,none, + "variable other than 'self' captured here under the name 'self' does not " + "enable implicit 'self'", ()) +NOTE(note_self_captured_weakly,none, + "weak capture of 'self' here does not enable implicit 'self'", ()) ERROR(implicit_use_of_self_in_closure,none, "implicit use of 'self' in closure; use 'self.' to make" " capture semantics explicit", ()) @@ -3140,7 +3371,7 @@ NOTE(fix_unqualified_access_top_level_multi,none, WARNING(warn_deprecated_conditional_conformance_outer_access,none, "use of %0 as reference to %1 in %2 %3 will change in future versions of Swift to reference %4 in %5 %6 " "which comes via a conditional conformance", - (DeclName, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) NOTE(fix_deprecated_conditional_conformance_outer_access,none, "use '%0' to continue to reference the %1", @@ -3339,22 +3570,20 @@ ERROR(assignment_dynamic_property_has_immutable_base,none, ERROR(assignment_bang_has_immutable_subcomponent,none, "cannot assign through '!': %0", (StringRef)) +NOTE(candidate_is_not_assignable,none, + "candidate is not assignable: %0 %1", + (DescriptiveDeclKind, DeclName)) + NOTE(change_to_mutating,none, "mark %select{method|accessor}0 'mutating' to make 'self' mutable", (bool)) -NOTE(masked_instance_variable,none, - "add explicit 'self.' to refer to mutable property of %0", - (Type)) +NOTE(masked_mutable_property,none, + "add explicit '%0' to refer to mutable %1 of %2", + (StringRef, DescriptiveDeclKind, Type)) ERROR(assignment_let_property_delegating_init,none, "'let' property %0 may not be initialized directly; use " - "\"self.init(...)\" or \"self = ...\" instead", (DeclName)) - -// ForEach Stmt -ERROR(sequence_protocol_broken,none, - "SequenceType protocol definition is broken", ()) -ERROR(iterator_protocol_broken,none, - "IteratorProtocol protocol definition is broken", ()) + "\"self.init(...)\" or \"self = ...\" instead", (DeclNameRef)) ERROR(label_shadowed, none, "label %0 cannot be reused on an inner statement", (Identifier)) @@ -3375,6 +3604,11 @@ ERROR(unresolved_label_corrected,none, "use of unresolved label %0; did you mean %1?", (Identifier, Identifier)) +ERROR(foreach_sequence_does_not_conform_to_expected_protocol,none, + "for-in loop requires %0 to conform to %1" + "%select{|; did you mean to unwrap optional?}2", + (Type, Type, bool)) + // Switch Stmt ERROR(no_match_operator,none, "no binary '~=' operator available for 'switch' statement", ()) @@ -3415,9 +3649,10 @@ NOTE(add_where_newline, none, NOTE(duplicate_where, none, "duplicate the 'where' on both patterns to check both patterns",()) -ERROR(trailing_closure_requires_parens,none, - "trailing closure requires parentheses for disambiguation in this" - " context", ()) +WARNING(trailing_closure_requires_parens,none, + "trailing closure in this context is confusable with the body of the" + " statement; pass as a parenthesized argument to silence this warning", + ()) ERROR(opaque_type_var_no_init,none, "property declares an opaque return type, but has no initializer " @@ -3484,17 +3719,12 @@ ERROR(closure_tuple_parameter_destructuring,none, ERROR(closure_tuple_parameter_destructuring_implicit,none, "closure tuple parameter %0 does not support destructuring " "with implicit parameters", (Type)) -ERROR(nested_tuple_parameter_destructuring,none, - "nested tuple parameter %0 of function %1 " - "does not support destructuring", (Type, Type)) ERROR(single_tuple_parameter_mismatch_special,none, "%0 expects a single parameter of type %1%2", (DescriptiveDeclKind, Type, StringRef)) ERROR(single_tuple_parameter_mismatch_normal,none, "%0 %1 expects a single parameter of type %2%3", (DescriptiveDeclKind, DeclBaseName, Type, StringRef)) -NOTE(note_maybe_forgot_to_form_tuple,none, - "did you mean to pass a tuple?", ()) ERROR(unknown_single_tuple_parameter_mismatch,none, "single parameter of type %0 is expected in call", (Type)) ERROR(cannot_convert_single_tuple_into_multiple_arguments,none, @@ -3504,7 +3734,7 @@ ERROR(cannot_convert_single_tuple_into_multiple_arguments,none, ERROR(enum_element_pattern_assoc_values_mismatch,none, "pattern with associated values does not match enum case %0", - (Identifier)) + (DeclNameRef)) NOTE(enum_element_pattern_assoc_values_remove,none, "remove associated values to make the pattern match", ()) ERROR(tuple_pattern_length_mismatch,none, @@ -3512,14 +3742,14 @@ ERROR(tuple_pattern_length_mismatch,none, ERROR(tuple_pattern_label_mismatch,none, "tuple pattern element label %0 must be %1", (Identifier, Identifier)) ERROR(enum_element_pattern_member_not_found,none, - "enum case '%0' not found in type %1", (StringRef, Type)) + "enum case %0 not found in type %1", (DeclNameRef, Type)) ERROR(optional_element_pattern_not_valid_type,none, "'?' pattern cannot match values of type %0", (Type)) ERROR(condition_optional_element_pattern_not_valid_type,none, "initializer for conditional binding must have Optional type, not %0", (Type)) ERROR(enum_element_pattern_not_member_of_enum,none, - "enum case '%0' is not a member of type %1", (StringRef, Type)) + "enum case %0 is not a member of type %1", (DeclNameRef, Type)) ERROR(ambiguous_enum_pattern_type,none, "generic enum type %0 is ambiguous without explicit generic parameters " "when matching value of type %1", (Type, Type)) @@ -3767,6 +3997,13 @@ ERROR(unsupported_convention,none, "convention '%0' not supported", (StringRef)) ERROR(unreferenced_generic_parameter,none, "generic parameter '%0' is not used in function signature", (StringRef)) +ERROR(unexpected_ctype_for_non_c_convention,none, + "convention '%0' does not support the 'cType' argument label, did you " + "mean @convention(c, cType: \"%1\") or @convention(block, cType: \"%1\") " + "instead?", (StringRef, StringRef)) +ERROR(unable_to_parse_c_function_type,none, + "unable to parse '%0'; it should be a C function pointer type or a " + "block pointer type", (StringRef)) // Opaque types ERROR(unsupported_opaque_type,none, @@ -3779,6 +4016,11 @@ ERROR(opaque_type_in_protocol_requirement,none, "'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type?", ()) +// Function differentiability +ERROR(attr_only_on_parameters_of_differentiable,none, + "'%0' may only be used on parameters of '@differentiable' function " + "types", (StringRef)) + // SIL ERROR(opened_non_protocol,none, "@opened cannot be applied to non-protocol type %0", (Type)) @@ -4036,14 +4278,9 @@ ERROR(nonlocal_bridged_to_objc,none, "conformance of %0 to %1 can only be written in module %2", (Identifier, Identifier, Identifier)) -ERROR(broken_bridged_to_objc_protocol,none, - "_BridgedToObjectiveC protocol is broken", ()) - ERROR(missing_bridging_function,Fatal, "missing '%select{_forceBridgeFromObjectiveC|" "_conditionallyBridgeFromObjectiveC}0'", (bool)) -ERROR(missing_nserror_bridging_function,none, - "missing _bridgeNSError", ()) #define OBJC_DIAG_SELECT "%select{initializer %1|implicit initializer %1|deinitializer|implicit deinitializer|method %1|getter for %1|subscript getter|setter for %1|subscript setter}0" @@ -4152,19 +4389,20 @@ ERROR(dynamic_replacement_accessor_not_explicit, none, ERROR(dynamic_replacement_function_not_dynamic, none, "replaced function %0 is not marked dynamic", (DeclName)) ERROR(dynamic_replacement_function_not_found, none, - "replaced function %0 could not be found", (DeclName)) + "replaced function %0 could not be found", (DeclNameRef)) ERROR(dynamic_replacement_accessor_not_found, none, - "replaced accessor for %0 could not be found", (DeclName)) + "replaced accessor for %0 could not be found", (DeclNameRef)) ERROR(dynamic_replacement_accessor_ambiguous, none, - "replaced accessor for %0 occurs in multiple places", (DeclName)) + "replaced accessor for %0 occurs in multiple places", (DeclNameRef)) NOTE(dynamic_replacement_accessor_ambiguous_candidate, none, "candidate accessor found in module %0", (DeclName)) ERROR(dynamic_replacement_function_of_type_not_found, none, - "replaced function %0 of type %1 could not be found", (DeclName, Type)) + "replaced function %0 of type %1 could not be found", (DeclNameRef, Type)) NOTE(dynamic_replacement_found_function_of_type, none, "found function %0 of type %1", (DeclName, Type)) ERROR(dynamic_replacement_not_in_extension, none, - "dynamicReplacement(for:) of %0 is not defined in an extension or at the file level", (DeclName)) + "dynamicReplacement(for:) of %0 is not defined in an extension or at the " + "file level", (DeclName)) ERROR(dynamic_replacement_must_not_be_dynamic, none, "dynamicReplacement(for:) of %0 must not be dynamic itself", (DeclName)) ERROR(dynamic_replacement_replaced_not_objc_dynamic, none, @@ -4172,9 +4410,9 @@ ERROR(dynamic_replacement_replaced_not_objc_dynamic, none, ERROR(dynamic_replacement_replacement_not_objc_dynamic, none, "%0 is marked @objc dynamic", (DeclName)) ERROR(dynamic_replacement_replaced_constructor_is_convenience, none, - "replaced constructor %0 is marked as convenience", (DeclName)) + "replaced constructor %0 is marked as convenience", (DeclNameRef)) ERROR(dynamic_replacement_replaced_constructor_is_not_convenience, none, - "replaced constructor %0 is not marked as convenience", (DeclName)) + "replaced constructor %0 is not marked as convenience", (DeclNameRef)) //------------------------------------------------------------------------------ // MARK: @available @@ -4281,21 +4519,23 @@ NOTE(availability_protocol_requirement_here, none, "protocol requirement here", ()) WARNING(public_decl_needs_availability, none, - "public declarations should have an availability attribute with -require-explicit-availability", ()) + "public declarations should have an availability attribute when building " + "with -require-explicit-availability", ()) // This doesn't display as an availability diagnostic, but it's // implemented there and fires when these subscripts are marked // unavailable, so it seems appropriate to put it here. ERROR(availabilty_string_subscript_migration, none, - "subscripts returning String were obsoleted in Swift 4; explicitly construct a String from subscripted result", ()) + "subscripts returning String were obsoleted in Swift 4; explicitly " + "construct a String from subscripted result", ()) //------------------------------------------------------------------------------ // MARK: @discardableResult //------------------------------------------------------------------------------ WARNING(discardable_result_on_void_never_function, none, - "@discardableResult declared on a function returning %select{Never|Void}0 is unnecessary", - (bool)) + "@discardableResult declared on a function returning %select{Never|Void}0 " + "is unnecessary", (bool)) //------------------------------------------------------------------------------ // MARK: Resilience diagnostics @@ -4402,8 +4642,6 @@ ERROR(specialize_attr_only_generic_param_req,none, "Only requirements on generic parameters are supported by '_specialize' attribute", ()) ERROR(specialize_attr_only_one_concrete_same_type_req,none, "Only one concrete type should be used in the same-type requirement in '_specialize' attribute", ()) -ERROR(specialize_attr_non_nominal_type_constraint_req,none, - "Only conformances to nominal types are supported by '_specialize' attribute", ()) ERROR(specialize_attr_non_protocol_type_constraint_req,none, "Only conformances to protocol types are supported by '_specialize' attribute", ()) ERROR(specialize_attr_type_parameter_count_mismatch,none, @@ -4453,6 +4691,20 @@ WARNING(variable_never_mutated, none, WARNING(variable_never_read, none, "variable %0 was written to, but never read", (Identifier)) +WARNING(observe_keypath_property_not_objc_dynamic, none, + "passing reference to non-'@objc dynamic' property %0 to KVO method %1 " + "may lead to unexpected behavior or runtime trap", (DeclName, DeclName)) + +WARNING(default_magic_identifier_mismatch, none, + "parameter %0 with default argument '%1' passed to parameter %2, whose " + "default argument is '%3'", + (Identifier, StringRef, Identifier, StringRef)) +NOTE(change_caller_default_to_match_callee, none, + "did you mean for parameter %0 to default to '%1'?", + (Identifier, StringRef)) +NOTE(silence_default_magic_identifier_mismatch, none, + "add parentheses to silence this warning", ()) + //------------------------------------------------------------------------------ // MARK: Debug diagnostics @@ -4506,9 +4758,6 @@ WARNING(non_exhaustive_switch_unknown_only,none, "switch covers known cases, but %0 may have additional unknown values" "%select{|, possibly added in future versions}1", (Type, bool)) -WARNING(override_nsobject_hashvalue_warning,none, - "override of 'NSObject.hashValue' is deprecated; " - "did you mean to override 'NSObject.hash'?", ()) ERROR(override_nsobject_hashvalue_error,none, "'NSObject.hashValue' is not overridable; " "did you mean to override 'NSObject.hash'?", ()) @@ -4607,7 +4856,7 @@ WARNING(property_wrapper_init_initialValue,none, "to 'init(wrappedValue:)'; use of 'init(initialValue:)' is deprecated", ()) ERROR(property_wrapper_projection_value_missing,none, - "could not find projection value property %0", (Identifier)) + "could not find projection value property %0", (DeclNameRef)) ERROR(property_wrapper_missing_arg_init, none, "missing argument for parameter " "%0 in property wrapper initializer; add 'wrappedValue' and %0 " "arguments in '@%1(...)'", (Identifier, StringRef)) @@ -4649,6 +4898,20 @@ NOTE(previous_function_builder_here, none, "previous function builder specified here", ()) ERROR(function_builder_arguments, none, "function builder attributes cannot have arguments", ()) +WARNING(function_builder_disabled_by_return, none, + "application of function builder %0 disabled by explicit 'return' " + "statement", (Type)) +NOTE(function_builder_remove_attr, none, + "remove the attribute to explicitly disable the function builder", ()) +NOTE(function_builder_remove_returns, none, + "remove 'return' statements to apply the function builder", ()) + +//------------------------------------------------------------------------------ +// MARK: differentiable programming diagnostics +//------------------------------------------------------------------------------ +ERROR(experimental_differentiable_programming_disabled, none, + "differentiable programming is an experimental feature that is " + "currently disabled", ()) #ifndef DIAG_NO_UNDEF # if defined(DIAG) diff --git a/include/swift/AST/EducationalNotes.def b/include/swift/AST/EducationalNotes.def new file mode 100644 index 0000000000000..c748bd489df5e --- /dev/null +++ b/include/swift/AST/EducationalNotes.def @@ -0,0 +1,37 @@ +//===-- EducationalNotes.def - Diagnostic Documentation Content -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file associates diagnostics with relevant educational notes which +// explain important concepts. +// +//===----------------------------------------------------------------------===// + +#ifndef EDUCATIONAL_NOTES +# error Must define EDUCATIONAL_NOTES +#endif + +// EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...) + +EDUCATIONAL_NOTES(non_nominal_no_initializers, "nominal-types.md") +EDUCATIONAL_NOTES(non_nominal_extension, "nominal-types.md") +EDUCATIONAL_NOTES(associated_type_witness_conform_impossible, + "nominal-types.md") + +EDUCATIONAL_NOTES(cannot_infer_closure_result_type, + "complex-closure-inference.md") + +EDUCATIONAL_NOTES(invalid_dynamic_callable_type, + "dynamic-callable-requirements.md") +EDUCATIONAL_NOTES(missing_dynamic_callable_kwargs_method, + "dynamic-callable-requirements.md") + +#undef EDUCATIONAL_NOTES diff --git a/include/swift/AST/Evaluator.h b/include/swift/AST/Evaluator.h index ba02e2d957f62..6ad636f7f4566 100644 --- a/include/swift/AST/Evaluator.h +++ b/include/swift/AST/Evaluator.h @@ -20,6 +20,7 @@ #include "swift/AST/AnyRequest.h" #include "swift/Basic/AnyValue.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/DenseMap.h" @@ -186,6 +187,9 @@ class Evaluator { /// Whether to dump detailed debug info for cycles. bool debugDumpCycles; + /// Whether we're building a request dependency graph. + bool buildDependencyGraph; + /// Used to report statistics about which requests were evaluated, if /// non-null. UnifiedStatsReporter *stats = nullptr; @@ -204,7 +208,7 @@ class Evaluator { /// A vector containing all of the active evaluation requests, which /// is treated as a stack and is used to detect cycles. - llvm::SetVector activeRequests; + llvm::SetVector activeRequests; /// A cache that stores the results of requests. llvm::DenseMap cache; @@ -236,7 +240,9 @@ class Evaluator { public: /// Construct a new evaluator that can emit cyclic-dependency /// diagnostics through the given diagnostics engine. - Evaluator(DiagnosticEngine &diags, bool debugDumpCycles=false); + Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph); /// Emit GraphViz output visualizing the request graph. void emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath); @@ -252,26 +258,28 @@ class Evaluator { void registerRequestFunctions(Zone zone, ArrayRef functions); - /// Evaluate the given request and produce its result, - /// consulting/populating the cache as required. - template + /// Retrieve the result produced by evaluating a request that can + /// be cached. + template::type * = nullptr> llvm::Expected operator()(const Request &request) { - // Check for a cycle. - if (checkDependency(getCanonicalRequest(request))) { - return llvm::Error( - llvm::make_unique>(request, *this)); - } + // The request can be cached, but check a predicate to determine + // whether this particular instance is cached. This allows more + // fine-grained control over which instances get cache. + if (request.isCached()) + return getResultCached(request); - // Make sure we remove this from the set of active requests once we're - // done. - SWIFT_DEFER { - assert(activeRequests.back().castTo() == request); - activeRequests.pop_back(); - }; + return getResultUncached(request); + } - // Get the result. - return getResult(request); + /// Retrieve the result produced by evaluating a request that + /// will never be cached. + template::type * = nullptr> + llvm::Expected + operator()(const Request &request) { + return getResultUncached(request); } /// Evaluate a set of requests and return their results as a tuple. @@ -300,7 +308,7 @@ class Evaluator { typename std::enable_if::type* = nullptr> void cacheOutput(const Request &request, typename Request::OutputType &&output) { - cache.insert({getCanonicalRequest(request), std::move(output)}); + cache.insert({AnyRequest(request), std::move(output)}); } /// Clear the cache stored within this evaluator. @@ -312,24 +320,13 @@ class Evaluator { /// Is the given request, or an equivalent, currently being evaluated? template bool hasActiveRequest(const Request &request) const { - return activeRequests.count(AnyRequest(request)); + return activeRequests.count(ActiveRequest(request)); } private: - template - const AnyRequest &getCanonicalRequest(const Request &request) { - // FIXME: DenseMap ought to let us do this with one hash lookup. - auto iter = dependencies.find_as(request); - if (iter != dependencies.end()) - return iter->first; - auto insertResult = dependencies.insert({AnyRequest(request), {}}); - assert(insertResult.second && "just checked if the key was already there"); - return insertResult.first->first; - } - /// Diagnose a cycle detected in the evaluation of the given /// request. - void diagnoseCycle(const AnyRequest &request); + void diagnoseCycle(const ActiveRequest &request); /// Check the dependency from the current top of the stack to /// the given request, including cycle detection and diagnostics. @@ -337,39 +334,31 @@ class Evaluator { /// \returns true if a cycle was detected, in which case this function has /// already diagnosed the cycle. Otherwise, returns \c false and adds this /// request to the \c activeRequests stack. - bool checkDependency(const AnyRequest &request); - - /// Retrieve the result produced by evaluating a request that can - /// be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - // The request can be cached, but check a predicate to determine - // whether this particular instance is cached. This allows more - // fine-grained control over which instances get cache. - if (request.isCached()) - return getResultCached(request); - - return getResultUncached(request); - } - - /// Retrieve the result produced by evaluating a request that - /// will never be cached. - template::type * = nullptr> - llvm::Expected - getResult(const Request &request) { - return getResultUncached(request); - } + bool checkDependency(const ActiveRequest &request); /// Produce the result of the request without caching. template llvm::Expected getResultUncached(const Request &request) { + auto activeReq = ActiveRequest(request); + + // Check for a cycle. + if (checkDependency(activeReq)) { + return llvm::Error( + llvm::make_unique>(request, *this)); + } + + // Make sure we remove this from the set of active requests once we're + // done. + SWIFT_DEFER { + assert(activeRequests.back() == activeReq); + activeRequests.pop_back(); + }; + // Clear out the dependencies on this request; we're going to recompute // them now anyway. - dependencies.find_as(request)->second.clear(); + if (buildDependencyGraph) + dependencies.find_as(request)->second.clear(); PrettyStackTraceRequest prettyStackTrace(request); @@ -423,7 +412,7 @@ class Evaluator { return result; // Cache the result. - cache.insert({getCanonicalRequest(request), *result}); + cache.insert({AnyRequest(request), *result}); return result; } @@ -452,17 +441,13 @@ class Evaluator { /// Dump the dependencies of the given request to the debugging stream /// as a tree. - LLVM_ATTRIBUTE_DEPRECATED( - void dumpDependencies(const AnyRequest &request) const LLVM_ATTRIBUTE_USED, - "Only meant for use in the debugger"); + SWIFT_DEBUG_DUMPER(dumpDependencies(const AnyRequest &request)); /// Print all dependencies known to the evaluator as a single Graphviz /// directed graph. void printDependenciesGraphviz(llvm::raw_ostream &out) const; - LLVM_ATTRIBUTE_DEPRECATED( - void dumpDependenciesGraphviz() const LLVM_ATTRIBUTE_USED, - "Only meant for use in the debugger"); + SWIFT_DEBUG_DUMPER(dumpDependenciesGraphviz()); }; template diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 36e1c2035335c..c45ab227a759e 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -27,6 +27,7 @@ #include "swift/AST/TypeAlignments.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/Availability.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" #include "llvm/Support/TrailingObjects.h" #include @@ -47,6 +48,7 @@ namespace swift { class Decl; class DeclRefExpr; class OpenedArchetypeType; + class ParamDecl; class Pattern; class SubscriptDecl; class Stmt; @@ -63,6 +65,7 @@ namespace swift { class EnumElementDecl; class CallExpr; class KeyPathExpr; + class CaptureListExpr; enum class ExprKind : uint8_t { #define EXPR(Id, Parent) Id, @@ -281,6 +284,12 @@ class alignas(8) Expr { Discriminator : 16 ); + SWIFT_INLINE_BITFIELD(AutoClosureExpr, AbstractClosureExpr, 1, + /// True if this autoclosure was built for a function conversion, and + /// not an actual @autoclosure parameter. + IsThunk : 1 + ); + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1, /// True if closure parameters were synthesized from anonymous closure /// variables. @@ -494,7 +503,7 @@ class alignas(8) Expr { /// Retrieves the declaration that is being referenced by this /// expression, if any. - ConcreteDeclRef getReferencedDecl() const; + ConcreteDeclRef getReferencedDecl(bool stopAtParenExpr = false) const; /// Determine whether this expression is 'super', possibly converted to /// a base class. @@ -529,20 +538,7 @@ class alignas(8) Expr { /// the parent map. llvm::DenseMap getParentMap(); - /// Produce a mapping from each subexpression to its depth and parent, - /// in the root expression. The root expression has depth 0, its children have - /// depth 1, etc. - llvm::DenseMap> getDepthMap(); - - /// Produce a mapping from each expression to its index according to a - /// preorder traversal of the expressions. The parent has index 0, its first - /// child has index 1, its second child has index 2 if the first child is a - /// leaf node, etc. - llvm::DenseMap getPreorderIndexMap(); - - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, unsigned Indent = 0) const; void dump(raw_ostream &OS, llvm::function_ref getType, llvm::function_ref getTypeOfTypeLoc, @@ -1051,8 +1047,22 @@ class InterpolatedStringLiteralExpr : public LiteralExpr { class MagicIdentifierLiteralExpr : public LiteralExpr { public: enum Kind : unsigned { - File, Line, Column, Function, DSOHandle + File, FilePath, Line, Column, Function, DSOHandle }; + + static StringRef getKindString(MagicIdentifierLiteralExpr::Kind value) { + switch (value) { + case File: return "#file"; + case FilePath: return "#filePath"; + case Function: return "#function"; + case Line: return "#line"; + case Column: return "#column"; + case DSOHandle: return "#dsohandle"; + } + + llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in getKindString."); + } + private: SourceLoc Loc; ConcreteDeclRef BuiltinInitializer; @@ -1078,6 +1088,7 @@ class MagicIdentifierLiteralExpr : public LiteralExpr { bool isString() const { switch (getKind()) { case File: + case FilePath: case Function: return true; case Line: @@ -1350,19 +1361,19 @@ class TypeExpr : public Expr { /// Create a TypeExpr for a TypeDecl at the specified location. - static TypeExpr *createForDecl(SourceLoc Loc, TypeDecl *D, + static TypeExpr *createForDecl(DeclNameLoc Loc, TypeDecl *D, DeclContext *DC, bool isImplicit); /// Create a TypeExpr for a member TypeDecl of the given parent TypeDecl. - static TypeExpr *createForMemberDecl(SourceLoc ParentNameLoc, + static TypeExpr *createForMemberDecl(DeclNameLoc ParentNameLoc, TypeDecl *Parent, - SourceLoc NameLoc, + DeclNameLoc NameLoc, TypeDecl *Decl); /// Create a TypeExpr for a member TypeDecl of the given parent IdentTypeRepr. static TypeExpr *createForMemberDecl(IdentTypeRepr *ParentTR, - SourceLoc NameLoc, + DeclNameLoc NameLoc, TypeDecl *Decl); /// Create a TypeExpr from an IdentTypeRepr with the given arguments applied @@ -1497,11 +1508,11 @@ class OverloadedDeclRefExpr final : public OverloadSetRefExpr { /// sema), or may just be a use of an unknown identifier. /// class UnresolvedDeclRefExpr : public Expr { - DeclName Name; + DeclNameRef Name; DeclNameLoc Loc; public: - UnresolvedDeclRefExpr(DeclName name, DeclRefKind refKind, DeclNameLoc loc) + UnresolvedDeclRefExpr(DeclNameRef name, DeclRefKind refKind, DeclNameLoc loc) : Expr(ExprKind::UnresolvedDeclRef, /*Implicit=*/loc.isInvalid()), Name(name), Loc(loc) { Bits.UnresolvedDeclRefExpr.DeclRefKind = static_cast(refKind); @@ -1509,9 +1520,28 @@ class UnresolvedDeclRefExpr : public Expr { static_cast(Loc.isCompound() ? FunctionRefKind::Compound : FunctionRefKind::Unapplied); } - + + static UnresolvedDeclRefExpr *createImplicit( + ASTContext &C, DeclName name, + DeclRefKind refKind = DeclRefKind::Ordinary) { + return new (C) UnresolvedDeclRefExpr(DeclNameRef(name), refKind, + DeclNameLoc()); + } + + static UnresolvedDeclRefExpr *createImplicit( + ASTContext &C, DeclBaseName baseName, ArrayRef argLabels) { + return UnresolvedDeclRefExpr::createImplicit(C, + DeclName(C, baseName, argLabels)); + } + + static UnresolvedDeclRefExpr *createImplicit( + ASTContext &C, DeclBaseName baseName, ParameterList *paramList) { + return UnresolvedDeclRefExpr::createImplicit(C, + DeclName(C, baseName, paramList)); + } + bool hasName() const { return static_cast(Name); } - DeclName getName() const { return Name; } + DeclNameRef getName() const { return Name; } DeclRefKind getRefKind() const { return static_cast(Bits.UnresolvedDeclRefExpr.DeclRefKind); @@ -1732,17 +1762,6 @@ class DynamicSubscriptExpr final llvm::function_ref getType = [](const Expr *E) -> Type { return E->getType(); }); - /// Create a new dynamic subscript. - static DynamicSubscriptExpr *create(ASTContext &ctx, Expr *base, - SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit); - /// getIndex - Retrieve the index of the subscript expression, i.e., the /// "offset" into the base value. Expr *getIndex() const { return Index; } @@ -1779,11 +1798,11 @@ class UnresolvedMemberExpr final public TrailingCallArguments { SourceLoc DotLoc; DeclNameLoc NameLoc; - DeclName Name; + DeclNameRef Name; Expr *Argument; UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, - DeclName name, Expr *argument, + DeclNameRef name, Expr *argument, ArrayRef argLabels, ArrayRef argLabelLocs, bool hasTrailingClosure, @@ -1792,12 +1811,12 @@ class UnresolvedMemberExpr final public: /// Create a new unresolved member expression with no arguments. static UnresolvedMemberExpr *create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclName name, + DeclNameLoc nameLoc, DeclNameRef name, bool implicit); /// Create a new unresolved member expression. static UnresolvedMemberExpr *create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclName name, + DeclNameLoc nameLoc, DeclNameRef name, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, @@ -1806,7 +1825,7 @@ class UnresolvedMemberExpr final Expr *trailingClosure, bool implicit); - DeclName getName() const { return Name; } + DeclNameRef getName() const { return Name; } DeclNameLoc getNameLoc() const { return NameLoc; } SourceLoc getDotLoc() const { return DotLoc; } Expr *getArgument() const { return Argument; } @@ -2402,12 +2421,12 @@ class UnresolvedDotExpr : public Expr { Expr *SubExpr; SourceLoc DotLoc; DeclNameLoc NameLoc; - DeclName Name; + DeclNameRef Name; ArrayRef OuterAlternatives; public: UnresolvedDotExpr( - Expr *subexpr, SourceLoc dotloc, DeclName name, DeclNameLoc nameloc, + Expr *subexpr, SourceLoc dotloc, DeclNameRef name, DeclNameLoc nameloc, bool Implicit, ArrayRef outerAlternatives = ArrayRef()) : Expr(ExprKind::UnresolvedDot, Implicit), SubExpr(subexpr), @@ -2417,7 +2436,28 @@ class UnresolvedDotExpr : public Expr { static_cast(NameLoc.isCompound() ? FunctionRefKind::Compound : FunctionRefKind::Unapplied); } - + + static UnresolvedDotExpr *createImplicit( + ASTContext &C, Expr *base, DeclName name) { + return new (C) UnresolvedDotExpr(base, SourceLoc(), + DeclNameRef(name), DeclNameLoc(), + /*implicit=*/true); + } + + static UnresolvedDotExpr *createImplicit( + ASTContext &C, Expr *base, + DeclBaseName baseName, ArrayRef argLabels) { + return UnresolvedDotExpr::createImplicit(C, base, + DeclName(C, baseName, argLabels)); + } + + static UnresolvedDotExpr *createImplicit( + ASTContext &C, Expr *base, + DeclBaseName baseName, ParameterList *paramList) { + return UnresolvedDotExpr::createImplicit(C, base, + DeclName(C, baseName, paramList)); + } + SourceLoc getLoc() const { if (NameLoc.isValid()) return NameLoc.getBaseNameLoc(); @@ -2449,7 +2489,7 @@ class UnresolvedDotExpr : public Expr { Expr *getBase() const { return SubExpr; } void setBase(Expr *e) { SubExpr = e; } - DeclName getName() const { return Name; } + DeclNameRef getName() const { return Name; } DeclNameLoc getNameLoc() const { return NameLoc; } ArrayRef getOuterAlternatives() const { @@ -3449,7 +3489,7 @@ class AbstractClosureExpr : public DeclContext, public Expr { Bits.AbstractClosureExpr.Discriminator = Discriminator; } - const CaptureInfo &getCaptureInfo() const { return Captures; } + CaptureInfo getCaptureInfo() const { return Captures; } void setCaptureInfo(CaptureInfo captures) { Captures = captures; } /// Retrieve the parameters of this closure. @@ -3563,6 +3603,21 @@ class SerializedAbstractClosureExpr : public SerializedLocalDeclContext { /// \endcode class ClosureExpr : public AbstractClosureExpr { + /// The range of the brackets of the capture list, if present. + SourceRange BracketRange; + + /// The (possibly null) VarDecl captured by this closure with the literal name + /// "self". In order to recover this information at the time of name lookup, + /// we must be able to access it from the associated DeclContext. + /// Because the DeclContext inside a closure is the closure itself (and not + /// the CaptureListExpr which would normally maintain this sort of + /// information about captured variables), we need to have some way to access + /// this information directly on the ClosureExpr. + /// + /// The bit indicates whether this closure has had a function builder + /// applied to it. + llvm::PointerIntPair CapturedSelfDeclAndAppliedBuilder; + /// The location of the "throws", if present. SourceLoc ThrowsLoc; @@ -3579,16 +3634,17 @@ class ClosureExpr : public AbstractClosureExpr { /// The body of the closure, along with a bit indicating whether it /// was originally just a single expression. llvm::PointerIntPair Body; - public: - ClosureExpr(ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, + ClosureExpr(SourceRange bracketRange, VarDecl *capturedSelfDecl, + ParameterList *params, SourceLoc throwsLoc, SourceLoc arrowLoc, SourceLoc inLoc, TypeLoc explicitResultType, unsigned discriminator, DeclContext *parent) : AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false, discriminator, parent), + BracketRange(bracketRange), + CapturedSelfDeclAndAppliedBuilder(capturedSelfDecl, false), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc), InLoc(inLoc), - ExplicitResultType(explicitResultType), - Body(nullptr) { + ExplicitResultType(explicitResultType), Body(nullptr) { setParameterList(params); Bits.ClosureExpr.HasAnonymousClosureVars = false; } @@ -3620,6 +3676,8 @@ class ClosureExpr : public AbstractClosureExpr { /// explicitly-specified result type. bool hasExplicitResultType() const { return ArrowLoc.isValid(); } + /// Retrieve the range of the \c '[' and \c ']' that enclose the capture list. + SourceRange getBracketRange() const { return BracketRange; } /// Retrieve the location of the \c '->' for closures with an /// explicit result type. @@ -3685,6 +3743,24 @@ class ClosureExpr : public AbstractClosureExpr { /// Is this a completely empty closure? bool hasEmptyBody() const; + /// VarDecl captured by this closure under the literal name \c self , if any. + VarDecl *getCapturedSelfDecl() const { + return CapturedSelfDeclAndAppliedBuilder.getPointer(); + } + + /// Whether this closure captures the \c self param in its body in such a + /// way that implicit \c self is enabled within its body (i.e. \c self is + /// captured non-weakly). + bool capturesSelfEnablingImplictSelf() const; + + bool hasAppliedFunctionBuilder() const { + return CapturedSelfDeclAndAppliedBuilder.getInt(); + } + + void setAppliedFunctionBuilder(bool flag = true) { + CapturedSelfDeclAndAppliedBuilder.setInt(flag); + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Closure; } @@ -3718,7 +3794,18 @@ class AutoClosureExpr : public AbstractClosureExpr { DeclContext *Parent) : AbstractClosureExpr(ExprKind::AutoClosure, ResultTy, /*Implicit=*/true, Discriminator, Parent) { - setBody(Body); + if (Body != nullptr) + setBody(Body); + + Bits.AutoClosureExpr.IsThunk = false; + } + + bool isThunk() const { + return Bits.AutoClosureExpr.IsThunk; + } + + void setIsThunk(bool isThunk) { + Bits.AutoClosureExpr.IsThunk = isThunk; } SourceRange getSourceRange() const; @@ -3758,6 +3845,8 @@ struct CaptureListEntry { CaptureListEntry(VarDecl *Var, PatternBindingDecl *Init) : Var(Var), Init(Init) { } + + bool isSimpleSelfCapture() const; }; /// CaptureListExpr - This expression represents the capture list on an explicit @@ -3863,6 +3952,10 @@ class OpaqueValueExpr : public Expr { /// value to be specified later. bool isPlaceholder() const { return Bits.OpaqueValueExpr.IsPlaceholder; } + void setIsPlaceholder(bool value) { + Bits.OpaqueValueExpr.IsPlaceholder = value; + } + SourceRange getSourceRange() const { return Range; } static bool classof(const Expr *E) { @@ -3876,6 +3969,8 @@ class OpaqueValueExpr : public Expr { /// A DefaultArgumentExpr must only appear as a direct child of a /// ParenExpr or a TupleExpr that is itself a call argument. class DefaultArgumentExpr final : public Expr { + friend class CallerSideDefaultArgExprRequest; + /// The owning declaration. ConcreteDeclRef DefaultArgsOwner; @@ -3885,11 +3980,17 @@ class DefaultArgumentExpr final : public Expr { /// The source location of the argument list. SourceLoc Loc; + /// Stores either a DeclContext or, upon type-checking, the caller-side + /// default expression. + PointerUnion ContextOrCallerSideExpr; + public: - explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner, unsigned paramIndex, - SourceLoc loc, Type Ty) + explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner, + unsigned paramIndex, SourceLoc loc, Type Ty, + DeclContext *dc) : Expr(ExprKind::DefaultArgument, /*Implicit=*/true, Ty), - DefaultArgsOwner(defaultArgsOwner), ParamIndex(paramIndex), Loc(loc) { } + DefaultArgsOwner(defaultArgsOwner), ParamIndex(paramIndex), Loc(loc), + ContextOrCallerSideExpr(dc) { } SourceRange getSourceRange() const { return Loc; @@ -3903,46 +4004,19 @@ class DefaultArgumentExpr final : public Expr { return ParamIndex; } - static bool classof(const Expr *E) { - return E->getKind() == ExprKind::DefaultArgument; - } -}; + /// Retrieves the parameter declaration for this default argument. + const ParamDecl *getParamDecl() const; -/// An expression referring to a caller-side default argument left unspecified -/// at the call site. -/// -/// A CallerDefaultArgumentExpr must only appear as a direct child of a -/// ParenExpr or a TupleExpr that is itself a call argument. -/// -/// FIXME: This only exists to distinguish caller default arguments from arguments -/// that were specified at the call site. Once we remove SanitizeExpr, we can remove -/// this hack too. -class CallerDefaultArgumentExpr final : public Expr { - /// The expression that is evaluated to produce the default argument value. - Expr *SubExpr; + /// Checks whether this is a caller-side default argument that is emitted + /// directly at the call site. + bool isCallerSide() const; - /// The source location of the argument list. - SourceLoc Loc; - -public: - explicit CallerDefaultArgumentExpr(Expr *subExpr, SourceLoc loc, Type Ty) - : Expr(ExprKind::CallerDefaultArgument, /*Implicit=*/true, Ty), - SubExpr(subExpr), Loc(loc) { } - - SourceRange getSourceRange() const { - return Loc; - } - - Expr *getSubExpr() const { - return SubExpr; - } - - void setSubExpr(Expr *subExpr) { - SubExpr = subExpr; - } + /// For a caller-side default argument, retrieves the fully type-checked + /// expression within the context of the call site. + Expr *getCallerSideDefaultExpr() const; static bool classof(const Expr *E) { - return E->getKind() == ExprKind::CallerDefaultArgument; + return E->getKind() == ExprKind::DefaultArgument; } }; @@ -4881,7 +4955,7 @@ class KeyPathExpr : public Expr { public: /// A single stored component, which will be one of: - /// - an unresolved DeclName, which has to be type-checked + /// - an unresolved DeclNameRef, which has to be type-checked /// - a resolved ValueDecl, referring to /// - a subscript index expression, which may or may not be resolved /// - an optional chaining, forcing, or wrapping component @@ -4902,11 +4976,11 @@ class KeyPathExpr : public Expr { private: union DeclNameOrRef { - DeclName UnresolvedName; + DeclNameRef UnresolvedName; ConcreteDeclRef ResolvedDecl; DeclNameOrRef() : UnresolvedName{} {} - DeclNameOrRef(DeclName un) : UnresolvedName(un) {} + DeclNameOrRef(DeclNameRef un) : UnresolvedName(un) {} DeclNameOrRef(ConcreteDeclRef rd) : ResolvedDecl(rd) {} } Decl; @@ -4947,7 +5021,7 @@ class KeyPathExpr : public Expr { {} /// Create an unresolved component for a property. - static Component forUnresolvedProperty(DeclName UnresolvedName, + static Component forUnresolvedProperty(DeclNameRef UnresolvedName, SourceLoc Loc) { return Component(nullptr, UnresolvedName, nullptr, {}, {}, @@ -5162,7 +5236,7 @@ class KeyPathExpr : public Expr { void setSubscriptIndexHashableConformances( ArrayRef hashables); - DeclName getUnresolvedDeclName() const { + DeclNameRef getUnresolvedDeclName() const { switch (getKind()) { case Kind::UnresolvedProperty: return Decl.UnresolvedName; @@ -5408,6 +5482,12 @@ Expr *packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, [](const Expr *E) -> Type { return E->getType(); }); + +void simple_display(llvm::raw_ostream &out, const ClosureExpr *CE); +void simple_display(llvm::raw_ostream &out, const DefaultArgumentExpr *expr); + +SourceLoc extractNearestSourceLoc(const DefaultArgumentExpr *expr); + } // end namespace swift #endif diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 36849cc6f168b..7ea0fb0435e8c 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -127,7 +127,6 @@ EXPR(DynamicType, Expr) EXPR(RebindSelfInConstructor, Expr) EXPR(OpaqueValue, Expr) EXPR(DefaultArgument, Expr) -EXPR(CallerDefaultArgument, Expr) EXPR(BindOptional, Expr) EXPR(OptionalEvaluation, Expr) EXPR(ForceValue, Expr) diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index e09f13bac0756..020930dd5980a 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -152,6 +152,21 @@ class FileUnit : public DeclContext { /// The order of the results is not guaranteed to be meaningful. virtual void getTopLevelDecls(SmallVectorImpl &results) const {} + /// Finds top-level decls in this file filtered by their attributes. + /// + /// This does a simple local lookup, not recursively looking through imports. + /// The order of the results is not guaranteed to be meaningful. + /// + /// \param Results Vector collecting the decls. + /// + /// \param matchAttributes Check on the attributes of a decl to keep only + /// decls with matching attributes. The subclass SerializedASTFile checks the + /// attributes first to only deserialize decls with accepted attributes, + /// limiting deserialization work. + virtual void + getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes) const; /// Finds all precedence group decls in this file. /// diff --git a/include/swift/AST/ExperimentalDependencies.h b/include/swift/AST/FineGrainedDependencies.h similarity index 85% rename from include/swift/AST/ExperimentalDependencies.h rename to include/swift/AST/FineGrainedDependencies.h index 58a7d81fe43db..49f0b755f9e39 100644 --- a/include/swift/AST/ExperimentalDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -1,4 +1,4 @@ -//===--- ExperimentalDependencies.h -----------------------------*- C++ -*-===// +//===----- FineGrainedependencies.h -----------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,10 +10,12 @@ // //===----------------------------------------------------------------------===// -#ifndef SWIFT_AST_EXPERIMENTAL_DEPENDENCIES_H -#define SWIFT_AST_EXPERIMENTAL_DEPENDENCIES_H +#ifndef SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H +#define SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/NullablePtr.h" #include "swift/Basic/Range.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/MD5.h" @@ -26,7 +28,7 @@ #include #include -// Summary: The ExperimentalDependency* files contain infrastructure for a +// Summary: The FineGrainedDependency* files contain infrastructure for a // dependency system that, in the future, will be finer-grained than the current // dependency system. At present--12/5/18--they are using the same input // information as the current system and expected to produce the same results. @@ -35,14 +37,14 @@ // // The frontend uses the information from the compiler to built a // SourceFileDepGraph consisting of SourceFileDepGraphNodes. -// ExperimentalDependencies.* define these structures, and -// ExperimentalDependenciesProducer has the frontend-unique code used to build +// FineGrainedDependencies.* define these structures, and +// FineGrainedDependenciesProducer has the frontend-unique code used to build // the SourceFileDepGraph. // // The driver reads the SourceFileDepGraph and integrates it into its dependency // graph, a ModuleDepGraph consisting of ModuleDepGraphNodes. -// This file holds the declarations for the experimental dependency system +// This file holds the declarations for the fine-grained dependency system // that are used by both the driver and frontend. // These include the graph structures common to both programs and also // the frontend graph, which must be read by the driver. @@ -58,7 +60,7 @@ class FrontendOptions; class SourceFile; /// Use a new namespace to help keep the experimental code from clashing. -namespace experimental_dependencies { +namespace fine_grained_dependencies { using StringVec = std::vector; @@ -138,6 +140,11 @@ template class TwoStageMap { return iter2 == iter->second.end() ? None : Optional(iter2->second); } + NullablePtr find(const Key1 &k1) const { + auto iter = map.find(k1); + return iter == map.end() ? nullptr : &iter->second; + } + /// The sought value must be present. Value findAndErase(const Key1 &k1, const Key2 &k2) { auto &submap = map[k1]; @@ -236,6 +243,20 @@ class BiIndexedTwoStageMap { } Optional find(const Key2 &k2, Key1 &k1) const { return find(k1, k2); } + /// Return the submap for a given Key1. May create one, after the fashion of + /// the standard libary. + const Key2Map &operator[](const Key1 &k1) { return map1[k1]; } + /// Return the submap for a given Key2. May create one, after the fashion of + /// the standard libary. + const Key1Map &operator[](const Key2 &k2) { return map2[k2]; } + + NullablePtr find(const Key1 &k1) const { + return map1.find(k1); + } + NullablePtr find(const Key2 &k2) const { + return map2.find(k2); + } + /// Element must be present. /// Return the erased value. Value findAndErase(const Key1 &k1, const Key2 &k2) { @@ -249,12 +270,6 @@ class BiIndexedTwoStageMap { Value findAndErase(const Key2 &k2, const Key1 &k1) { return findAndErase(k1, k2); } - /// Return the submap for a given Key1. May create one, after the fashion of - /// the standard libary. - const Key2Map &operator[](const Key1 &k1) { return map1[k1]; } - /// Return the submap for a given Key2. May create one, after the fashion of - /// the standard libary. - const Key1Map &operator[](const Key2 &k2) { return map2[k2]; } /// Invoke \p fn on each Key2 and Value matching (\p k1, *) void forEachValueMatching( @@ -317,14 +332,14 @@ class BiIndexedTwoStageMap { // End of general declarations //============================================================================== -// MARK: Start of experimental-dependency-specific code +// MARK: Start of fine-grained-dependency-specific code //============================================================================== /// The entry point into this system from the frontend: /// Write out the .swiftdeps file for a frontend compilation of a primary file. bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, const DependencyTracker &depTracker, - StringRef outputPath); + StringRef outputPath, bool alsoEmitDotFile); //============================================================================== // MARK: Enums //============================================================================== @@ -437,8 +452,8 @@ class DependencyKey { name() {} /// For constructing a key in the frontend. - DependencyKey(NodeKind kind, DeclAspect aspect, std::string context, - std::string name) + DependencyKey(NodeKind kind, DeclAspect aspect, const std::string &context, + const std::string &name) : kind(kind), aspect(aspect), context(context), name(name) { assert(verify()); } @@ -448,7 +463,7 @@ class DependencyKey { StringRef getContext() const { return context; } StringRef getName() const { return name; } - StringRef getSwiftDepsFromSourceFileProvide() const { + StringRef getSwiftDepsFromASourceFileProvideNodeKey() const { assert(getKind() == NodeKind::sourceFileProvide && "Receiver must be sourceFileProvide."); return getName(); @@ -493,12 +508,20 @@ class DependencyKey { static std::string computeNameForProvidedEntity(Entity); /// Given some type of depended-upon entity create the key. - template - static DependencyKey createDependedUponKey(const Entity &); + static DependencyKey createDependedUponKey(StringRef mangledHolderName, + StringRef memberBaseName); + + template + static DependencyKey createDependedUponKey(StringRef); + + static DependencyKey createKeyForWholeSourceFile(StringRef swiftDeps); std::string humanReadableName() const; - void dump() const { llvm::errs() << asString() << "\n"; } + StringRef aspectName() const { return DeclAspectNames[size_t(aspect)]; } + + void dump(llvm::raw_ostream &os) const { os << asString() << "\n"; } + SWIFT_DEBUG_DUMP { dump(llvm::errs()); } /// For debugging, needed for \ref TwoStageMap::verify std::string asString() const; @@ -515,24 +538,31 @@ class DependencyKey { // Name conversion helpers static std::string demangleTypeAsContext(StringRef); }; -} // namespace experimental_dependencies +} // namespace fine_grained_dependencies } // namespace swift template <> -struct std::hash { +struct std::hash { size_t - operator()(const swift::experimental_dependencies::DependencyKey &key) const { + operator()(const swift::fine_grained_dependencies::DependencyKey &key) const { return key.hash(); } }; template <> -struct std::hash { +struct std::hash { size_t - operator()(const swift::experimental_dependencies::DeclAspect aspect) const { + operator()(const swift::fine_grained_dependencies::DeclAspect aspect) const { return size_t(aspect); } }; +namespace swift { +namespace fine_grained_dependencies { +using ContextNameFingerprint = + std::tuple>; +} +} // namespace swift + //============================================================================== // MARK: DepGraphNode //============================================================================== @@ -557,7 +587,7 @@ struct std::hash { /// improving it. namespace swift { -namespace experimental_dependencies { +namespace fine_grained_dependencies { class DepGraphNode { /// Def->use arcs go by DependencyKey. There may be >1 node for a given key. DependencyKey key; @@ -609,7 +639,8 @@ class DepGraphNode { /// needs to set the fingerprint *after* the node has been created. void setFingerprint(Optional fp) { fingerprint = fp; } - void dump() const; + SWIFT_DEBUG_DUMP; + void dump(llvm::raw_ostream &os) const; std::string humanReadableName(StringRef where) const; @@ -681,11 +712,13 @@ class SourceFileDepGraphNode : public DepGraphNode { } /// Record the sequence number, \p n, of another use. + /// The relationship between an interface and its implementation is NOT + /// included here. See \c + /// SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew. void addDefIDependUpon(size_t n) { if (n != getSequenceNumber()) defsIDependUpon.insert(n); } - void dump() const { DepGraphNode::dump(); } std::string humanReadableName() const { return DepGraphNode::humanReadableName("here"); @@ -694,8 +727,29 @@ class SourceFileDepGraphNode : public DepGraphNode { bool verify() const { DepGraphNode::verify(); assert(getIsProvides() || isDepends()); + assert(verifySequenceNumber()); return true; } + + bool verifySequenceNumber() const { + const auto &k = getKey(); + if (k.getKind() != NodeKind::sourceFileProvide) + return true; + switch (k.getAspect()) { + case DeclAspect::interface: + assert(getSequenceNumber() == sourceFileProvidesInterfaceSequenceNumber); + return true; + case DeclAspect::implementation: + assert(getSequenceNumber() == + sourceFileProvidesImplementationSequenceNumber); + return true; + default: + llvm_unreachable("neither interface nor implementation"); + } + } + static constexpr const size_t sourceFileProvidesInterfaceSequenceNumber = 0; + static constexpr const size_t sourceFileProvidesImplementationSequenceNumber = + 1; }; //============================================================================== @@ -703,7 +757,7 @@ class SourceFileDepGraphNode : public DepGraphNode { //============================================================================== /// The dependency graph produced by the frontend and consumed by the driver. -/// See \ref Node in ExperimentalDependencies.h +/// See \ref Node in FineGrainedDependencies.h class SourceFileDepGraph { /// Every node in the graph. Indices used for serialization. /// Use addNode instead of adding directly. @@ -725,6 +779,34 @@ class SourceFileDepGraph { SourceFileDepGraph(const SourceFileDepGraph &g) = delete; SourceFileDepGraph(SourceFileDepGraph &&g) = default; + /// Simulate loading for unit testing: + /// \param swiftDepsFileName The name of the swiftdeps file of the phony job + /// \param includePrivateDeps Whether the graph includes intra-file arcs + /// \param hadCompilationError Simulate a compilation error + /// \param interfaceHash The interface hash of the simulated graph + /// \param simpleNamesByRDK A map of vectors of names keyed by reference + /// dependency key \param compoundNamesByRDK A map of (mangledHolder, + /// baseName) pairs keyed by reference dependency key. For single-name + /// dependencies, an initial underscore indicates that the name does not + /// cascade. For compound names, it is the first name, the holder which + /// indicates non-cascading. For member names, an initial underscore indicates + /// file-privacy. + static SourceFileDepGraph + simulateLoad(std::string swiftDepsFileName, const bool includePrivateDeps, + const bool hadCompilationError, std::string interfaceHash, + llvm::StringMap> simpleNamesByRDK, + llvm::StringMap>> + compoundNamesByRDK); + + static constexpr char noncascadingOrPrivatePrefix = '#'; + static constexpr char nameFingerprintSeparator = ','; + + static std::string noncascading(std::string name); + + LLVM_ATTRIBUTE_UNUSED + static std::string privatize(std::string name); + + /// Nodes are owned by the graph. ~SourceFileDepGraph() { forEachNode([&](SourceFileDepGraphNode *n) { delete n; }); @@ -733,7 +815,7 @@ class SourceFileDepGraph { /// Goes at the start of an emitted YAML file to help tools recognize it. /// May vary in the future according to version, etc. std::string yamlProlog(const bool hadCompilationError) const { - return std::string("# Experimental\n") + + return std::string("# Fine-grained v0\n") + (!hadCompilationError ? "" : "# Dependencies are unknown because a " "compilation error occurred.\n"); @@ -744,7 +826,7 @@ class SourceFileDepGraph { InterfaceAndImplementationPair getSourceFileNodePair() const; - StringRef getSwiftDepsFromSourceFileProvide() const; + StringRef getSwiftDepsOfJobThatProducedThisGraph() const; std::string getGraphID() const { return getSourceFileNodePair().getInterface()->getKey().humanReadableName(); @@ -768,12 +850,13 @@ class SourceFileDepGraph { /// The frontend creates a pair of nodes for every tracked Decl and the source /// file itself. InterfaceAndImplementationPair - findExistingNodePairOrCreateAndAddIfNew(NodeKind k, StringRef context, - StringRef name, - Optional fingerprint); + findExistingNodePairOrCreateAndAddIfNew( + NodeKind k, const ContextNameFingerprint &contextNameFingerprint); - SourceFileDepGraphNode *findExistingNodeOrCreateIfNew( - DependencyKey key, Optional fingerprint, bool isProvides); + SourceFileDepGraphNode * + findExistingNodeOrCreateIfNew(DependencyKey key, + const Optional &fingerprint, + bool isProvides); /// \p Use is the Node that must be rebuilt when \p def changes. /// Record that fact in the graph. @@ -798,14 +881,14 @@ class SourceFileDepGraph { /// Ensure that when read, the graph is the same as what was written. bool verifyReadsWhatIsWritten(StringRef path) const; + bool verifySequenceNumber() const; + private: void addNode(SourceFileDepGraphNode *n) { n->setSequenceNumber(allNodes.size()); - assert(allNodes.size() < 2 == - (n->getKey().getKind() == NodeKind::sourceFileProvide) && - "First two and only first two nodes should be sourceFileProvide " - "nodes."); allNodes.push_back(n); + assert(n->verifySequenceNumber() && + "Certain nodes must be in certain places"); } }; @@ -890,7 +973,7 @@ template class DotFileEmitter { } void emitArcs() { g.forEachArc([&](const NodeT *def, const NodeT *use) { - if (includeGraphArc(use, def)) + if (includeGraphArc(def, use)) emitGraphArc(def, use); }); } @@ -934,7 +1017,7 @@ template class DotFileEmitter { } }; -} // end namespace experimental_dependencies +} // end namespace fine_grained_dependencies } // end namespace swift //============================================================================== @@ -946,22 +1029,22 @@ template class DotFileEmitter { #if !(defined(__linux__) || defined(_WIN64)) LLVM_YAML_DECLARE_SCALAR_TRAITS(size_t, QuotingType::None) #endif -LLVM_YAML_DECLARE_ENUM_TRAITS(swift::experimental_dependencies::NodeKind) -LLVM_YAML_DECLARE_ENUM_TRAITS(swift::experimental_dependencies::DeclAspect) +LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::NodeKind) +LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::DeclAspect) LLVM_YAML_DECLARE_MAPPING_TRAITS( - swift::experimental_dependencies::DependencyKey) -LLVM_YAML_DECLARE_MAPPING_TRAITS(swift::experimental_dependencies::DepGraphNode) + swift::fine_grained_dependencies::DependencyKey) +LLVM_YAML_DECLARE_MAPPING_TRAITS(swift::fine_grained_dependencies::DepGraphNode) namespace llvm { namespace yaml { template <> struct MappingContextTraits< - swift::experimental_dependencies::SourceFileDepGraphNode, - swift::experimental_dependencies::SourceFileDepGraph> { + swift::fine_grained_dependencies::SourceFileDepGraphNode, + swift::fine_grained_dependencies::SourceFileDepGraph> { using SourceFileDepGraphNode = - swift::experimental_dependencies::SourceFileDepGraphNode; + swift::fine_grained_dependencies::SourceFileDepGraphNode; using SourceFileDepGraph = - swift::experimental_dependencies::SourceFileDepGraph; + swift::fine_grained_dependencies::SourceFileDepGraph; static void mapping(IO &io, SourceFileDepGraphNode &node, SourceFileDepGraph &g); @@ -969,9 +1052,9 @@ struct MappingContextTraits< template <> struct SequenceTraits< - std::vector> { + std::vector> { using SourceFileDepGraphNode = - swift::experimental_dependencies::SourceFileDepGraphNode; + swift::fine_grained_dependencies::SourceFileDepGraphNode; using NodeVec = std::vector; static size_t size(IO &, NodeVec &vec); static SourceFileDepGraphNode &element(IO &, NodeVec &vec, size_t index); @@ -981,6 +1064,6 @@ struct SequenceTraits< } // namespace llvm LLVM_YAML_DECLARE_MAPPING_TRAITS( - swift::experimental_dependencies::SourceFileDepGraph) + swift::fine_grained_dependencies::SourceFileDepGraph) -#endif // SWIFT_AST_EXPERIMENTAL_DEPENDENCIES_H +#endif // SWIFT_AST_FINE_GRAINED_DEPENDENCIES_H diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index d9109424a8f16..144ed20c6621e 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -21,6 +21,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericParamKey.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" @@ -108,7 +109,7 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// Make vanilla new/delete illegal. void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) = delete; /// Only allow placement new. void *operator new(size_t Bytes, void *Mem) { @@ -160,7 +161,7 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final void dump(raw_ostream &os) const; - void dump() const; + SWIFT_DEBUG_DUMP; }; } // end namespace swift diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 57d6f51d85f61..42cafddfa3b57 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -21,6 +21,7 @@ #include "swift/AST/Requirement.h" #include "swift/AST/Type.h" #include "swift/Basic/AnyValue.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallVector.h" @@ -89,9 +90,10 @@ class ConformanceAccessPath { void print(raw_ostream &OS) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger"); + SWIFT_DEBUG_DUMP; }; +class CanGenericSignature; class GenericSignatureImpl; class GenericTypeParamType; @@ -136,9 +138,13 @@ class GenericSignature { void print(raw_ostream &OS, PrintOptions Options = PrintOptions()) const; void print(ASTPrinter &Printer, PrintOptions Opts = PrintOptions()) const; - void dump() const; + SWIFT_DEBUG_DUMP; std::string getAsString() const; + /// Returns the canonical generic signature, or \c nullptr if the underlying + /// pointer is \c nullptr. The result is cached. + CanGenericSignature getCanonicalSignature() const; + // Support for FoldingSet. void Profile(llvm::FoldingSetNodeID &id) const; @@ -269,8 +275,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final /// Look up a stored conformance in the generic signature. These are formed /// from same-type constraints placed on associated types of generic /// parameters which have conformance constraints on them. - Optional - lookupConformance(CanType depTy, ProtocolDecl *proto) const; + ProtocolConformanceRef lookupConformance(CanType depTy, + ProtocolDecl *proto) const; /// Iterate over all generic parameters, passing a flag to the callback /// indicating if the generic parameter is canonical or not. @@ -299,8 +305,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final bool isCanonical() const; ASTContext &getASTContext() const; - - /// Canonicalize the components of a generic signature. + + /// Returns the canonical generic signature. The result is cached. CanGenericSignature getCanonicalSignature() const; /// Retrieve the generic signature builder for the given generic signature. @@ -408,7 +414,7 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final void print(raw_ostream &OS, PrintOptions Options = PrintOptions()) const; void print(ASTPrinter &Printer, PrintOptions Opts = PrintOptions()) const; - void dump() const; + SWIFT_DEBUG_DUMP; std::string getAsString() const; }; diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index ba6a43eeb3b09..e9a36ac02b19b 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -28,6 +28,7 @@ #include "swift/AST/Types.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/TypeRepr.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" @@ -48,7 +49,6 @@ class DependentMemberType; class GenericParamList; class GenericSignatureBuilder; class GenericTypeParamType; -class LazyResolver; class ModuleDecl; class Pattern; class ProtocolConformance; @@ -253,9 +253,7 @@ class GenericSignatureBuilder { void dump(llvm::raw_ostream &out, GenericSignatureBuilder *builder = nullptr) const; - LLVM_ATTRIBUTE_DEPRECATED( - void dump(GenericSignatureBuilder *builder = nullptr) const, - "only for use in the debugger"); + SWIFT_DEBUG_DUMPER(dump(GenericSignatureBuilder *builder = nullptr)); /// Caches. @@ -519,10 +517,9 @@ class GenericSignatureBuilder { explicit LookUpConformanceInBuilder(GenericSignatureBuilder *builder) : builder(builder) {} - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { return builder->lookupConformance(dependentType, conformingReplacementType, conformedProtocol); @@ -534,13 +531,9 @@ class GenericSignatureBuilder { LookUpConformanceInBuilder getLookupConformanceFn(); /// Lookup a protocol conformance in a module-agnostic manner. - Optional - lookupConformance(CanType dependentType, Type conformingReplacementType, - ProtocolDecl *conformedProtocol); - - - /// Retrieve the lazy resolver, if there is one. - LazyResolver *getLazyResolver() const; + ProtocolConformanceRef lookupConformance(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol); /// Enumerate the requirements that describe the signature of this /// generic signature builder. @@ -816,12 +809,12 @@ class GenericSignatureBuilder { /// Verify all of the generic sigantures in the given module. static void verifyGenericSignaturesInModule(ModuleDecl *module); - /// Dump all of the requirements, both specified and inferred. - LLVM_ATTRIBUTE_DEPRECATED( - void dump(), - "only for use within the debugger"); + /// Dump all of the requirements, both specified and inferred. It cannot be + /// statically proven that this doesn't modify the GSB. + SWIFT_DEBUG_HELPER(void dump()); - /// Dump all of the requirements to the given output stream. + /// Dump all of the requirements to the given output stream. It cannot be + /// statically proven that this doesn't modify the GSB. void dump(llvm::raw_ostream &out); }; @@ -1338,18 +1331,13 @@ class GenericSignatureBuilder::RequirementSource final ID.AddPointer(storage3); } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; + SWIFT_DEBUG_DUMPER(print()); /// Dump the requirement source. void dump(llvm::raw_ostream &out, SourceManager *SrcMgr, unsigned indent) const; - LLVM_ATTRIBUTE_DEPRECATED( - void print() const, - "only for use within the debugger"); - /// Print the requirement source (shorter form) void print(llvm::raw_ostream &out, SourceManager *SrcMgr) const; }; @@ -1691,9 +1679,7 @@ class GenericSignatureBuilder::PotentialArchetype { /// Retrieve the AST context in which this potential archetype resides. ASTContext &getASTContext() const; - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &Out, SourceManager *SrcMgr, unsigned Indent) const; @@ -1701,6 +1687,21 @@ class GenericSignatureBuilder::PotentialArchetype { friend class GenericSignatureBuilder; }; +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(Type T) const { + return getSubjectDependentType({ })->isEqual(T); +} + +template +bool GenericSignatureBuilder::Constraint::isSubjectEqualTo(const GenericSignatureBuilder::PotentialArchetype *PA) const { + return getSubjectDependentType({ })->isEqual(PA->getDependentType({ })); +} + +template +bool GenericSignatureBuilder::Constraint::hasSameSubjectAs(const GenericSignatureBuilder::Constraint &C) const { + return getSubjectDependentType({ })->isEqual(C.getSubjectDependentType({ })); +} + /// Describes a requirement whose processing has been delayed for some reason. class GenericSignatureBuilder::DelayedRequirement { public: @@ -1724,8 +1725,7 @@ class GenericSignatureBuilder::DelayedRequirement { /// Dump a debugging representation of this delayed requirement class. void dump(llvm::raw_ostream &out) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, - "only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; /// Whether the given constraint result signals an error. diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 95622b5f31c66..699385ad271d1 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -106,6 +106,9 @@ class IRGenOptions { /// Which sanitizer is turned on. OptionSet Sanitizers; + /// Which sanitizer(s) have recovery instrumentation enabled. + OptionSet SanitizersWithRecoveryInstrumentation; + /// Path prefixes that should be rewritten in debug info. PathRemapper DebugPrefixMap; @@ -143,6 +146,9 @@ class IRGenOptions { /// objects. unsigned EmitStackPromotionChecks : 1; + /// Emit functions to separate sections. + unsigned FunctionSections : 1; + /// The maximum number of bytes used on a stack frame for stack promotion /// (includes alloc_stack allocations). unsigned StackPromotionSizeLimit = 1024; @@ -187,6 +193,10 @@ class IRGenOptions { /// Passing this flag completely disables this behavior. unsigned DisableLegacyTypeInfo : 1; + /// Create metadata specializations for generic types at statically known type + /// arguments. + unsigned PrespecializeGenericMetadata : 1; + /// The path to load legacy type layouts from. StringRef ReadLegacyTypeInfoPath; @@ -209,6 +219,10 @@ class IRGenOptions { /// Disable round-trip verification of mangled debug types. unsigned DisableRoundTripDebugTypes : 1; + /// Whether to disable shadow copies for local variables on the stack. This is + /// only used for testing. + unsigned DisableDebuggerShadowCopies : 1; + /// Path to the profdata file to be used for PGO, or the empty string. std::string UseProfile = ""; @@ -235,29 +249,30 @@ class IRGenOptions { : DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly), Verify(true), OptMode(OptimizationMode::NotSet), Sanitizers(OptionSet()), + SanitizersWithRecoveryInstrumentation(OptionSet()), DebugInfoLevel(IRGenDebugInfoLevel::None), DebugInfoFormat(IRGenDebugInfoFormat::None), - DisableClangModuleSkeletonCUs(false), - UseJIT(false), + DisableClangModuleSkeletonCUs(false), UseJIT(false), IntegratedREPL(false), DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false), DisableLLVMSLPVectorizer(false), DisableFPElim(true), Playground(false), EmitStackPromotionChecks(false), - PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None), + FunctionSections(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None), HasValueNamesSetting(false), ValueNames(false), EnableReflectionMetadata(true), EnableReflectionNames(true), EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false), LazyInitializeClassMetadata(false), LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), - UseIncrementalLLVMCodeGen(true), UseSwiftCall(false), - GenerateProfile(false), EnableDynamicReplacementChaining(false), - DisableRoundTripDebugTypes(false), CmdArgs(), - SanitizeCoverage(llvm::SanitizerCoverageOptions()), + PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), + UseSwiftCall(false), GenerateProfile(false), + EnableDynamicReplacementChaining(false), + DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false), + CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()), TypeInfoFilter(TypeInfoDumpFilter::All) {} /// Appends to \p os an arbitrary string representing all options which /// influence the llvm compilation but are not reflected in the llvm module /// itself. - void writeLLVMCodeGenOptionsTo(llvm::raw_ostream &os) { + void writeLLVMCodeGenOptionsTo(llvm::raw_ostream &os) const { // We put a letter between each value simply to keep them from running into // one another. There might be a vague correspondence between meaning and // letter, but don't sweat it. @@ -288,6 +303,16 @@ class IRGenOptions { return OptMode == OptimizationMode::ForSize; } + std::string getDebugFlags(StringRef PrivateDiscriminator) const { + std::string Flags = DebugFlags; + if (!PrivateDiscriminator.empty()) { + if (!Flags.empty()) + Flags += " "; + Flags += ("-private-discriminator " + PrivateDiscriminator).str(); + } + return Flags; + } + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/AST/Identifier.h b/include/swift/AST/Identifier.h index 035fc07c4240d..05b595d3953eb 100644 --- a/include/swift/AST/Identifier.h +++ b/include/swift/AST/Identifier.h @@ -18,6 +18,7 @@ #define SWIFT_AST_IDENTIFIER_H #include "swift/Basic/EditorPlaceholder.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerUnion.h" @@ -58,7 +59,7 @@ class Identifier { public: enum : size_t { - NumLowBitsAvailable = 2, + NumLowBitsAvailable = 3, RequiredAlignment = 1 << NumLowBitsAvailable, SpareBitMask = ((intptr_t)1 << NumLowBitsAvailable) - 1 }; @@ -71,7 +72,7 @@ class Identifier { } /// A type with the alignment expected of a valid \c Identifier::Pointer . - struct alignas(uint32_t) Aligner {}; + struct alignas(uint64_t) Aligner {}; static_assert(alignof(Aligner) >= RequiredAlignment, "Identifier table will provide enough spare bits"); @@ -187,6 +188,7 @@ class Identifier { }; class DeclName; +class DeclNameRef; class ObjCSelector; } // end namespace swift @@ -194,6 +196,7 @@ class ObjCSelector; namespace llvm { raw_ostream &operator<<(raw_ostream &OS, swift::Identifier I); raw_ostream &operator<<(raw_ostream &OS, swift::DeclName I); + raw_ostream &operator<<(raw_ostream &OS, swift::DeclNameRef I); raw_ostream &operator<<(raw_ostream &OS, swift::ObjCSelector S); // Identifiers hash just like pointers. @@ -399,9 +402,7 @@ class DeclName { size_t NumArgs; explicit CompoundDeclName(DeclBaseName BaseName, size_t NumArgs) - : BaseName(BaseName), NumArgs(NumArgs) { - assert(NumArgs > 0 && "Should use IdentifierAndCompound"); - } + : BaseName(BaseName), NumArgs(NumArgs) { } ArrayRef getArgumentNames() const { return {getTrailingObjects(), NumArgs}; @@ -419,16 +420,12 @@ class DeclName { } }; - // A single stored identifier, along with a bit stating whether it is the - // base name for a zero-argument compound name. - typedef llvm::PointerIntPair BaseNameAndCompound; - - // Either a single identifier piece stored inline (with a bit to say whether - // it is simple or compound), or a reference to a compound declaration name. - llvm::PointerUnion SimpleOrCompound; + /// Either a single identifier piece stored inline, or a reference to a + /// compound declaration name. + llvm::PointerUnion BaseNameOrCompound; - DeclName(void *Opaque) - : SimpleOrCompound(decltype(SimpleOrCompound)::getFromOpaqueValue(Opaque)) + explicit DeclName(void *Opaque) + : BaseNameOrCompound(decltype(BaseNameOrCompound)::getFromOpaqueValue(Opaque)) {} void initialize(ASTContext &C, DeclBaseName baseName, @@ -436,11 +433,11 @@ class DeclName { public: /// Build a null name. - DeclName() : SimpleOrCompound(BaseNameAndCompound()) {} + DeclName() : BaseNameOrCompound(DeclBaseName()) {} /// Build a simple value name with one component. /*implicit*/ DeclName(DeclBaseName simpleName) - : SimpleOrCompound(BaseNameAndCompound(simpleName, false)) {} + : BaseNameOrCompound(simpleName) {} /*implicit*/ DeclName(Identifier simpleName) : DeclName(DeclBaseName(simpleName)) {} @@ -459,10 +456,10 @@ class DeclName { /// such as the 'foo' in 'func foo(x:Int, y:Int)' or the 'bar' in /// 'var bar: Int'. DeclBaseName getBaseName() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->BaseName; - return SimpleOrCompound.get().getPointer(); + return BaseNameOrCompound.get(); } /// Assert that the base name is not special and return its identifier. @@ -475,7 +472,7 @@ class DeclName { /// Retrieve the names of the arguments, if there are any. ArrayRef getArgumentNames() const { - if (auto compound = SimpleOrCompound.dyn_cast()) + if (auto compound = BaseNameOrCompound.dyn_cast()) return compound->getArgumentNames(); return { }; @@ -484,25 +481,19 @@ class DeclName { bool isSpecial() const { return getBaseName().isSpecial(); } explicit operator bool() const { - if (SimpleOrCompound.dyn_cast()) + if (BaseNameOrCompound.dyn_cast()) return true; - return !SimpleOrCompound.get().getPointer().empty(); + return !BaseNameOrCompound.get().empty(); } /// True if this is a simple one-component name. bool isSimpleName() const { - if (SimpleOrCompound.dyn_cast()) - return false; - - return !SimpleOrCompound.get().getInt(); + return BaseNameOrCompound.is(); } /// True if this is a compound name. bool isCompoundName() const { - if (SimpleOrCompound.dyn_cast()) - return true; - - return SimpleOrCompound.get().getInt(); + return !isSimpleName(); } /// True if this name is a simple one-component name identical to the @@ -537,7 +528,7 @@ class DeclName { /// matches a simple name lookup or when the full compound name matches. bool matchesRef(DeclName refName) const { // Identical names always match. - if (SimpleOrCompound == refName.SimpleOrCompound) + if (BaseNameOrCompound == refName.BaseNameOrCompound) return true; // If the reference is a simple name, try simple name matching. if (refName.isSimpleName()) @@ -545,7 +536,7 @@ class DeclName { // The names don't match. return false; } - + /// Add a DeclName to a lookup table so that it can be found by its simple /// name or its compound name. template @@ -570,6 +561,11 @@ class DeclName { return !(lhs == rhs); } + friend llvm::hash_code hash_value(DeclName name) { + using llvm::hash_value; + return hash_value(name.getOpaqueValue()); + } + friend bool operator<(DeclName lhs, DeclName rhs) { return lhs.compare(rhs) < 0; } @@ -586,7 +582,7 @@ class DeclName { return lhs.compare(rhs) >= 0; } - void *getOpaqueValue() const { return SimpleOrCompound.getOpaqueValue(); } + void *getOpaqueValue() const { return BaseNameOrCompound.getOpaqueValue(); } static DeclName getFromOpaqueValue(void *p) { return DeclName(p); } /// Get a string representation of the name, @@ -610,11 +606,176 @@ class DeclName { /// fully-specified name that would be written in the source. llvm::raw_ostream &printPretty(llvm::raw_ostream &os) const; + /// Dump this name to standard error. + SWIFT_DEBUG_DUMP; +}; + +void simple_display(llvm::raw_ostream &out, DeclName name); + +/// An in-source reference to another declaration, including qualification +/// information. +class DeclNameRef { + DeclName FullName; + +public: + static DeclNameRef createSubscript(); + static DeclNameRef createConstructor(); + + DeclNameRef() : FullName() { } + + void *getOpaqueValue() const { return FullName.getOpaqueValue(); } + static DeclNameRef getFromOpaqueValue(void *p); + + explicit DeclNameRef(DeclName FullName) + : FullName(FullName) { } + + explicit DeclNameRef(DeclBaseName BaseName) + : FullName(BaseName) { } + + explicit DeclNameRef(Identifier BaseName) + : FullName(BaseName) { } + + /// The name of the declaration being referenced. + DeclName getFullName() const { + return FullName; + } + + DeclName &getFullName() { + return FullName; + } + + /// The base name of the declaration being referenced. + DeclBaseName getBaseName() const { + return FullName.getBaseName(); + } + + Identifier getBaseIdentifier() const { + return FullName.getBaseIdentifier(); + } + + ArrayRef getArgumentNames() const { + return FullName.getArgumentNames(); + } + + bool isSimpleName() const { + return FullName.isSimpleName(); + } + + bool isSimpleName(DeclBaseName name) const { + return FullName.isSimpleName(name); + } + + bool isSimpleName(StringRef name) const { + return FullName.isSimpleName(name); + } + + bool isSpecial() const { + return FullName.isSpecial(); + } + + bool isOperator() const { + return FullName.isOperator(); + } + + bool isCompoundName() const { + return FullName.isCompoundName(); + } + + explicit operator bool() const { + return (bool)FullName; + } + + /// Compare two declaration names, producing -1 if \c *this comes before + /// \c other, 1 if \c *this comes after \c other, and 0 if they are equal. + /// + /// Null declaration names come after all other declaration names. + int compare(DeclNameRef other) const { + return getFullName().compare(other.getFullName()); + } + + friend bool operator==(DeclNameRef lhs, DeclNameRef rhs) { + return lhs.getOpaqueValue() == rhs.getOpaqueValue(); + } + + friend bool operator!=(DeclNameRef lhs, DeclNameRef rhs) { + return !(lhs == rhs); + } + + friend llvm::hash_code hash_value(DeclNameRef name) { + using llvm::hash_value; + return hash_value(name.getFullName().getOpaqueValue()); + } + + friend bool operator<(DeclNameRef lhs, DeclNameRef rhs) { + return lhs.compare(rhs) < 0; + } + + friend bool operator<=(DeclNameRef lhs, DeclNameRef rhs) { + return lhs.compare(rhs) <= 0; + } + + friend bool operator>(DeclNameRef lhs, DeclNameRef rhs) { + return lhs.compare(rhs) > 0; + } + + friend bool operator>=(DeclNameRef lhs, DeclNameRef rhs) { + return lhs.compare(rhs) >= 0; + } + + DeclNameRef withoutArgumentLabels() const; + DeclNameRef withArgumentLabels(ASTContext &C, + ArrayRef argumentNames) const; + + /// Get a string representation of the name, + /// + /// \param scratch Scratch space to use. + StringRef getString(llvm::SmallVectorImpl &scratch, + bool skipEmptyArgumentNames = false) const; + + /// Print the representation of this declaration name to the given + /// stream. + /// + /// \param skipEmptyArgumentNames When true, don't print the argument labels + /// if they are all empty. + llvm::raw_ostream &print(llvm::raw_ostream &os, + bool skipEmptyArgumentNames = false) const; + + /// Print a "pretty" representation of this declaration name to the given + /// stream. + /// + /// This is the name used for diagnostics; it is not necessarily the + /// fully-specified name that would be written in the source. + llvm::raw_ostream &printPretty(llvm::raw_ostream &os) const; + /// Dump this name to standard error. LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, "only for use within the debugger"); }; +inline DeclNameRef DeclNameRef::getFromOpaqueValue(void *p) { + return DeclNameRef(DeclName::getFromOpaqueValue(p)); +} + +inline DeclNameRef DeclNameRef::withoutArgumentLabels() const { + return DeclNameRef(getBaseName()); +} + +inline DeclNameRef DeclNameRef::withArgumentLabels( + ASTContext &C, ArrayRef argumentNames) const { + return DeclNameRef(DeclName(C, getBaseName(), argumentNames)); +} + + +inline DeclNameRef DeclNameRef::createSubscript() { + return DeclNameRef(DeclBaseName::createSubscript()); +} + +inline DeclNameRef DeclNameRef::createConstructor() { + return DeclNameRef(DeclBaseName::createConstructor()); +} + +void simple_display(llvm::raw_ostream &out, DeclNameRef name); + enum class ObjCSelectorFamily : unsigned { None, #define OBJC_SELECTOR_FAMILY(LABEL, PREFIX) LABEL, @@ -693,8 +854,7 @@ class ObjCSelector { } /// Dump this selector to standard error. - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Compare two Objective-C selectors, producing -1 if \c *this comes before /// \c other, 1 if \c *this comes after \c other, and 0 if they are equal. @@ -741,7 +901,7 @@ namespace llvm { static inline swift::DeclName getFromVoidPointer(void *ptr) { return swift::DeclName::getFromOpaqueValue(ptr); } - enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 2 }; + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable - 1 }; }; // DeclNames hash just like pointers. @@ -760,6 +920,37 @@ namespace llvm { } }; + // A DeclNameRef is "pointer like" just like DeclNames. + template struct PointerLikeTypeTraits; + template<> + struct PointerLikeTypeTraits { + public: + static inline void *getAsVoidPointer(swift::DeclNameRef name) { + return name.getOpaqueValue(); + } + static inline swift::DeclNameRef getFromVoidPointer(void *ptr) { + return swift::DeclNameRef::getFromOpaqueValue(ptr); + } + enum { NumLowBitsAvailable = PointerLikeTypeTraits::NumLowBitsAvailable }; + }; + + // DeclNameRefs hash just like DeclNames. + template<> struct DenseMapInfo { + static swift::DeclNameRef getEmptyKey() { + return swift::DeclNameRef(DenseMapInfo::getEmptyKey()); + } + static swift::DeclNameRef getTombstoneKey() { + return swift::DeclNameRef(DenseMapInfo::getTombstoneKey()); + } + static unsigned getHashValue(swift::DeclNameRef Val) { + return DenseMapInfo::getHashValue(Val.getFullName()); + } + static bool isEqual(swift::DeclNameRef LHS, swift::DeclNameRef RHS) { + return DenseMapInfo::isEqual(LHS.getFullName(), + RHS.getFullName()); + } + }; + // An ObjCSelector is "pointer like". template struct PointerLikeTypeTraits; template<> diff --git a/include/swift/AST/IfConfigClause.h b/include/swift/AST/IfConfigClause.h index a3ce67401dbcf..665da3c237624 100644 --- a/include/swift/AST/IfConfigClause.h +++ b/include/swift/AST/IfConfigClause.h @@ -39,7 +39,7 @@ struct IfConfigClause { ArrayRef Elements; /// True if this is the active clause of the #if block. - bool isActive; + const bool isActive; IfConfigClause(SourceLoc Loc, Expr *Cond, ArrayRef Elements, bool isActive) diff --git a/include/swift/AST/IncrementalRanges.h b/include/swift/AST/IncrementalRanges.h new file mode 100644 index 0000000000000..f7ab5b21297ae --- /dev/null +++ b/include/swift/AST/IncrementalRanges.h @@ -0,0 +1,277 @@ +//===------------- IncrementalRanges.h -----------------------------*- C++ +//-*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_INCREMENTALRANGES_H +#define SWIFT_AST_INCREMENTALRANGES_H + +// These are the declarations for managing serializable source locations so that +// the frontend and the driver can implement incremental compilation based on +// source ranges. + +#include "swift/Basic/LLVM.h" +#include "swift/Basic/NullablePtr.h" +#include "swift/Basic/SourceLoc.h" +#include "swift/Basic/StringExtras.h" +#include "llvm/Support/YAMLTraits.h" +#include + +namespace swift { +class SourceManager; +class DiagnosticEngine; +class SourceFile; +} // namespace swift + +//============================================================================== +// MARK: Range types +//============================================================================== + +namespace swift { +namespace incremental_ranges { + +struct SerializableSourceRange; + +typedef std::vector Ranges; +typedef std::map RangesByFilename; +} // namespace incremental_ranges +} // namespace swift + +//============================================================================== +// MARK: SerializableSourceLocation +//============================================================================== +namespace swift { +namespace incremental_ranges { + +/// A source location that can be written from the frontend and read by the +/// driver. 1-origin: lines and columns start at 1 +struct SerializableSourceLocation { + uint64_t line = 0; + uint64_t column = 0; + + SerializableSourceLocation(const SourceLoc loc, const SourceManager &SM); + SerializableSourceLocation(uint64_t line, uint64_t column) + : line(line), column(column) {} + SerializableSourceLocation() = default; + static const SerializableSourceLocation endOfAnyFile; + + bool operator< (const SerializableSourceLocation &x) const { + return line < x.line ? true + : line > x.line ? false + : column < x.column; + } + bool operator==(const SerializableSourceLocation &x) const { + return line == x.line && column == x.column; + } + bool operator<=(const SerializableSourceLocation &x) const { + return *this < x || *this == x; + } + void print(raw_ostream &out) const; + void dump() const; +}; + +} // namespace incremental_ranges +} // namespace swift + +template <> +struct llvm::yaml::MappingTraits< + swift::incremental_ranges::SerializableSourceLocation> { + static const bool flow = true; + static void + mapping(llvm::yaml::IO &io, + swift::incremental_ranges::SerializableSourceLocation &loc) { + io.mapRequired("line", loc.line), io.mapRequired("column", loc.column); + } +}; +//============================================================================== +// MARK: SerializableSourceRange +//============================================================================== + +namespace swift { +namespace incremental_ranges { +/// A range in the source, that can be written by the frontend and read by the +/// driver. Half-open, to facilitate representing empty ranges. In other words, +/// an empty region will have start == end +struct SerializableSourceRange { + SerializableSourceLocation start, end; + + SerializableSourceRange(const CharSourceRange r, const SourceManager &SM); + SerializableSourceRange(SerializableSourceLocation start, + SerializableSourceLocation end); + SerializableSourceRange() = default; + + static const SerializableSourceRange wholeFile; + static Ranges RangesForWholeFile(); + + bool isEmpty() const { return start == end; } + + bool overlaps(const SerializableSourceRange &x) const { + return start < x.end && x.start < end; + } + bool operator==(const SerializableSourceRange &x) const { + return start == x.start && end == x.end; + } + bool isImproperSubsetOf(const SerializableSourceRange &) const; + bool properlyPreceeds(const SerializableSourceRange &) const; + static bool isProperlySorted(ArrayRef); + + bool + isImproperSubsetOfAny(ArrayRef supersetRanges) const; + bool isImproperSubsetOfAnySlowlyAndSimply( + ArrayRef supersetRanges) const; + + /// Optimized for fewer ranges in the subset + /// Return first outlier found in subset not in superset + static Optional + findOutlierIfAny(ArrayRef subset, + ArrayRef superset); + + static Ranges findAllOutliers(ArrayRef subset, + ArrayRef superset); + + std::string printString() const; + void print(raw_ostream &out) const; + void dump() const; +}; + +} // namespace incremental_ranges +} // namespace swift + + +template <> +struct llvm::yaml::MappingTraits< + swift::incremental_ranges::SerializableSourceRange> { + static const bool flow = true; + static void mapping(llvm::yaml::IO &io, + swift::incremental_ranges::SerializableSourceRange &sr) { + io.mapRequired("start", sr.start), io.mapRequired("end", sr.end); + } +}; + +//============================================================================== +// MARK: SwiftRangesFileContents +//============================================================================== + +namespace swift { +namespace incremental_ranges { + +/// The complete contents of the file written by the frontend and read by the +/// driver containing source range information for one primary input file. +struct SwiftRangesFileContents { + /// For each non-primary, the unparsed ranges in it. + /// At present these represent the bodies of types defined in the nonprimary + /// that are not used in the primary. + Ranges noninlinableFunctionBodies; + + SwiftRangesFileContents() = default; + + SwiftRangesFileContents(Ranges &&noninlinableFunctionBodies) + : noninlinableFunctionBodies(std::move(noninlinableFunctionBodies)) {} + + /// Return None for error. + static Optional + load(const StringRef primaryPath, const llvm::MemoryBuffer &swiftRangesBuffer, + const bool showIncrementalBuildDecisions, DiagnosticEngine &diags); + + void dump(StringRef primaryFilename) const; + + static constexpr const char *header = "### Swift source ranges file v0 ###\n"; +}; +} // namespace incremental_ranges +} // namespace swift + +template <> +struct llvm::yaml::MappingTraits< + swift::incremental_ranges::SwiftRangesFileContents> { + static void + mapping(llvm::yaml::IO &io, + swift::incremental_ranges::SwiftRangesFileContents &srfc) { + io.mapRequired("noninlinableFunctionBodies", + srfc.noninlinableFunctionBodies); + } +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(swift::incremental_ranges::SerializableSourceRange) +LLVM_YAML_IS_STRING_MAP(swift::incremental_ranges::Ranges) +LLVM_YAML_IS_STRING_MAP(swift::incremental_ranges::RangesByFilename) + +//============================================================================== +// MARK: SwiftRangesEmitter +//============================================================================== +namespace swift { +namespace incremental_ranges { +/// Gathers up the information from the frontend, processes it, and writes it. +class SwiftRangesEmitter { + const StringRef outputPath; + SourceFile *const primaryFile; + const SourceManager &sourceMgr; + DiagnosticEngine &diags; + +public: + SwiftRangesEmitter(StringRef outputPath, SourceFile *primaryFile, + const SourceManager &sourceMgr, DiagnosticEngine &diags) + : outputPath(outputPath), primaryFile(primaryFile), sourceMgr(sourceMgr), + diags(diags) {} + + /// True for error + bool emit() const; + +public: + void emitRanges(llvm::raw_ostream &out) const; + +private: + Ranges collectSortedSerializedNoninlinableFunctionBodies() const; + std::vector collectNoninlinableFunctionBodies() const; + + std::vector + sortRanges(std::vector ranges) const; + + /// Assuming \p ranges is sorted, coalesce overlapping ranges in place and + /// return end of the resultant vector. + std::vector + coalesceSortedRanges(std::vector) const; + + std::vector + serializeRanges(std::vector ranges) const; + + bool isImmediatelyBeforeOrOverlapping(CharSourceRange prev, + CharSourceRange next) const; +}; +} // namespace incremental_ranges +} // namespace swift + +//============================================================================== +// MARK: CompiledSourceEmitter +//============================================================================== +namespace swift { +namespace incremental_ranges { +/// The class that writes out the unchanged source code in the primary input so +/// that the driver can diff it later, after the user has changed the file. +class CompiledSourceEmitter { + const StringRef outputPath; + const SourceFile *const primaryFile; + const SourceManager &sourceMgr; + DiagnosticEngine &diags; + +public: + CompiledSourceEmitter(StringRef outputPath, const SourceFile *primaryFile, + const SourceManager &sourceMgr, DiagnosticEngine &diags) + : outputPath(outputPath), primaryFile(primaryFile), sourceMgr(sourceMgr), + diags(diags) {} + + /// True for error + bool emit(); +}; + +} // namespace incremental_ranges +} // namespace swift + +#endif // SWIFT_AST_INCREMENTALRANGES_H diff --git a/include/swift/AST/IndexSubset.h b/include/swift/AST/IndexSubset.h index 7daa54a30df1f..0437845e99efa 100644 --- a/include/swift/AST/IndexSubset.h +++ b/include/swift/AST/IndexSubset.h @@ -1,8 +1,8 @@ -//===---------- IndexSubset.h - Fixed-size subset of indices --------------===// +//===--- IndexSubset.h - Fixed-size subset of indices ---------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,6 +17,7 @@ #ifndef SWIFT_AST_INDEXSUBSET_H #define SWIFT_AST_INDEXSUBSET_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/Range.h" #include "swift/Basic/STLExtras.h" @@ -57,6 +58,10 @@ class IndexSubset : public llvm::FoldingSetNode { /// The number of bit words in the index subset. unsigned numBitWords; + static unsigned getNumBytesNeededForCapacity(unsigned capacity) { + return getNumBitWordsNeededForCapacity(capacity) * bitWordSize; + } + BitWord *getBitWordsData() { return reinterpret_cast(this + 1); } @@ -88,7 +93,7 @@ class IndexSubset : public llvm::FoldingSetNode { for (auto i : indices.set_bits()) { unsigned bitWordIndex, offset; std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(i); - getBitWord(bitWordIndex) |= (1 << offset); + getBitWord(bitWordIndex) |= (1ull << offset); } } @@ -176,7 +181,7 @@ class IndexSubset : public llvm::FoldingSetNode { bool contains(unsigned index) const { unsigned bitWordIndex, offset; std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(index); - return getBitWord(bitWordIndex) & (1 << offset); + return getBitWord(bitWordIndex) & (1ull << offset); } bool isEmpty() const { @@ -201,19 +206,8 @@ class IndexSubset : public llvm::FoldingSetNode { id.AddInteger(index); } - void print(llvm::raw_ostream &s = llvm::outs()) const { - s << '{'; - interleave(range(capacity), [this, &s](unsigned i) { s << contains(i); }, - [&s] { s << ", "; }); - s << '}'; - } - - void dump(llvm::raw_ostream &s = llvm::errs()) const { - s << "(index_subset capacity=" << capacity << " indices=("; - interleave(getIndices(), [&s](unsigned i) { s << i; }, - [&s] { s << ", "; }); - s << "))"; - } + void print(llvm::raw_ostream &s = llvm::outs()) const; + SWIFT_DEBUG_DUMPER(dump(llvm::raw_ostream &s = llvm::errs())); int findNext(int startIndex) const; int findFirst() const { return findNext(-1); } diff --git a/include/swift/AST/Initializer.h b/include/swift/AST/Initializer.h index d17c2622e1d19..32e644e78d1e6 100644 --- a/include/swift/AST/Initializer.h +++ b/include/swift/AST/Initializer.h @@ -150,12 +150,6 @@ class SerializedPatternBindingInitializer : public SerializedLocalDeclContext { /// A default argument expression. The parent context is the function /// (possibly a closure) for which this is a default argument. class DefaultArgumentInitializer : public Initializer { - friend class ASTContext; // calls reset on unused contexts - void reset(DeclContext *parent, unsigned index) { - setParent(parent); - SpareBits = index; - } - public: explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index) : Initializer(InitializerKind::DefaultArgument, parent) { diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 60d0eec59de44..fe8511ad4756f 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -34,6 +34,7 @@ IDENTIFIER_(bridgeToObjectiveC) IDENTIFIER(buildBlock) IDENTIFIER(buildDo) IDENTIFIER(buildEither) +IDENTIFIER(buildExpression) IDENTIFIER(buildIf) IDENTIFIER(callAsFunction) IDENTIFIER(Change) @@ -53,6 +54,7 @@ IDENTIFIER(decode) IDENTIFIER(decodeIfPresent) IDENTIFIER(Decoder) IDENTIFIER(decoder) +IDENTIFIER_(Differentiation) IDENTIFIER(dynamicallyCall) IDENTIFIER(dynamicMember) IDENTIFIER(Element) @@ -195,6 +197,9 @@ IDENTIFIER_(nsError) // Custom string interpolation type used by os log APIs. IDENTIFIER(OSLogMessage) +// Differentiable programming +IDENTIFIER(TangentVector) + #undef IDENTIFIER #undef IDENTIFIER_ #undef IDENTIFIER_WITH_NAME diff --git a/include/swift/AST/KnownObjCTypes.def b/include/swift/AST/KnownObjCTypes.def new file mode 100644 index 0000000000000..b77eb4f893554 --- /dev/null +++ b/include/swift/AST/KnownObjCTypes.def @@ -0,0 +1,37 @@ +//===--- KnownObjCTypes.def - Common Objective-C types --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This xmacro generates code for common imported Objective-C types the +// compiler has special knowledge of. +// +//===----------------------------------------------------------------------===// + +#ifndef KNOWN_OBJC_TYPE_DECL +/// KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) +/// +/// The macro is expanded for each known imported Objective-C type. MODULE is +/// bound to the name of the module the type comes from. NAME is bound to the +/// unqualified name of the type. DECL_CLASS is bound to the Decl subclass it +/// is expected to be an instance of. +#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) +#endif + +KNOWN_OBJC_TYPE_DECL(Foundation, NSCopying, ProtocolDecl) +KNOWN_OBJC_TYPE_DECL(Foundation, NSError, ClassDecl) +KNOWN_OBJC_TYPE_DECL(Foundation, NSNumber, ClassDecl) +KNOWN_OBJC_TYPE_DECL(Foundation, NSValue, ClassDecl) + +KNOWN_OBJC_TYPE_DECL(ObjectiveC, NSObject, ClassDecl) +KNOWN_OBJC_TYPE_DECL(ObjectiveC, Selector, StructDecl) +KNOWN_OBJC_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl) + +#undef KNOWN_OBJC_TYPE_DECL diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 7e1e58353ee8f..42c55f634cdf3 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -83,6 +83,8 @@ PROTOCOL_(DestructorSafeContainer) PROTOCOL(StringInterpolationProtocol) +PROTOCOL(Differentiable) + EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false) EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true) EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "Dictionary", false) diff --git a/include/swift/AST/KnownStdlibTypes.def b/include/swift/AST/KnownStdlibTypes.def index ab30681d0aa3e..15a9a2bc919a4 100644 --- a/include/swift/AST/KnownStdlibTypes.def +++ b/include/swift/AST/KnownStdlibTypes.def @@ -76,6 +76,11 @@ KNOWN_STDLIB_TYPE_DECL(UnsafePointer, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(OpaquePointer, NominalTypeDecl, 0) KNOWN_STDLIB_TYPE_DECL(AutoreleasingUnsafeMutablePointer, NominalTypeDecl, 1) +KNOWN_STDLIB_TYPE_DECL(UnsafeBufferPointer, NominalTypeDecl, 1) +KNOWN_STDLIB_TYPE_DECL(UnsafeMutableBufferPointer, NominalTypeDecl, 1) +KNOWN_STDLIB_TYPE_DECL(UnsafeRawBufferPointer, NominalTypeDecl, 0) +KNOWN_STDLIB_TYPE_DECL(UnsafeMutableRawBufferPointer, NominalTypeDecl, 0) + KNOWN_STDLIB_TYPE_DECL(Unmanaged, NominalTypeDecl, 1) KNOWN_STDLIB_TYPE_DECL(Never, NominalTypeDecl, 0) diff --git a/include/swift/AST/LayoutConstraint.h b/include/swift/AST/LayoutConstraint.h index 5fb2d5a446150..0889ba3c462ff 100644 --- a/include/swift/AST/LayoutConstraint.h +++ b/include/swift/AST/LayoutConstraint.h @@ -18,6 +18,7 @@ #define SWIFT_LAYOUT_CONSTRAINT_H #include "swift/AST/TypeAlignments.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" @@ -35,9 +36,10 @@ class ASTPrinter; enum class LayoutConstraintKind : uint8_t { // It is not a known layout constraint. UnknownLayout, - // It is a layout constraint representing a trivial type of an unknown size. + // It is a layout constraint representing a trivial type of a known size. TrivialOfExactSize, - // It is a layout constraint representing a trivial type of an unknown size. + // It is a layout constraint representing a trivial type of a size known to + // be no larger than a given size. TrivialOfAtMostSize, // It is a layout constraint representing a trivial type of an unknown size. Trivial, @@ -282,7 +284,7 @@ class LayoutConstraint { explicit operator bool() const { return Ptr != 0; } - void dump() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions()) const; diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index 970cb7ccfa26d..afd90352852cf 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the LazyResolver abstract interface. +// This file defines the abstract interfaces for lazily resolving declarations. // //===----------------------------------------------------------------------===// @@ -37,36 +37,6 @@ class TypeDecl; class ValueDecl; class VarDecl; -/// Abstract interface used to lazily resolve aspects of the AST, such as the -/// types of declarations or protocol conformance structures. -class LazyResolver { -public: - virtual ~LazyResolver(); - - /// Resolve the type witnesses for the given associated type within the given - /// protocol conformance. - virtual void resolveTypeWitness(const NormalProtocolConformance *conformance, - AssociatedTypeDecl *assocType) = 0; - - /// Resolve the witness for the given non-type requirement within - /// the given protocol conformance. - virtual void resolveWitness(const NormalProtocolConformance *conformance, - ValueDecl *requirement) = 0; - - /// Resolve the type and declaration attributes of a value. - /// - /// This can be called when the type or signature of a value is needed. - /// It does not perform full type-checking, only checks for basic - /// consistency and provides the value a type. - virtual void resolveDeclSignature(ValueDecl *VD) = 0; - - /// Resolve any implicitly-declared constructors within the given nominal. - virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) = 0; - - /// Resolve an implicitly-generated member with the given name. - virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) = 0; -}; - class LazyMemberLoader; /// Context data for lazy deserialization. @@ -133,6 +103,11 @@ class alignas(void*) LazyMemberLoader { virtual void loadRequirementSignature(const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &requirements) = 0; + + /// Returns the replaced decl for a given @_dynamicReplacement(for:) attribute. + virtual ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) = 0; }; /// A class that can lazily load conformances from a serialized format. diff --git a/include/swift/AST/LookupKinds.h b/include/swift/AST/LookupKinds.h index 71a61f5f51311..a33c2dd14880f 100644 --- a/include/swift/AST/LookupKinds.h +++ b/include/swift/AST/LookupKinds.h @@ -26,6 +26,8 @@ enum class NLKind { QualifiedLookup }; +void simple_display(llvm::raw_ostream &out, NLKind kind); + /// Constants used to customize name lookup. enum NLOptions : unsigned { /// Consider declarations within protocols to which the context type conforms. @@ -103,6 +105,8 @@ static inline NLOptions operator~(NLOptions value) { return NLOptions(~(unsigned)value); } +void simple_display(llvm::raw_ostream &out, NLOptions options); + } // end namespace swift #endif diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index e74e344c20f31..09a04adbde89c 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -58,7 +58,6 @@ namespace swift { class FileUnit; class FuncDecl; class InfixOperatorDecl; - class LazyResolver; class LinkLibrary; class ModuleLoader; class NominalTypeDecl; @@ -133,14 +132,14 @@ enum class ResilienceStrategy : unsigned { /// \sa FileUnit class ModuleDecl : public DeclContext, public TypeDecl { public: - typedef ArrayRef> AccessPathTy; + typedef ArrayRef> AccessPathTy; typedef std::pair ImportedModule; static bool matchesAccessPath(AccessPathTy AccessPath, DeclName Name) { assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); return AccessPath.empty() - || DeclName(AccessPath.front().first).matchesRef(Name); + || DeclName(AccessPath.front().Item).matchesRef(Name); } /// Arbitrarily orders ImportedModule records, for inclusion in sets and such. @@ -409,19 +408,18 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// \returns The result of the conformance search, which will be /// None if the type does not conform to the protocol or contain a /// ProtocolConformanceRef if it does conform. - Optional - lookupConformance(Type type, ProtocolDecl *protocol); + ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol); /// Look for the conformance of the given existential type to the given /// protocol. - Optional - lookupExistentialConformance(Type type, ProtocolDecl *protocol); + ProtocolConformanceRef lookupExistentialConformance(Type type, + ProtocolDecl *protocol); /// Exposes TypeChecker functionality for querying protocol conformance. /// Returns a valid ProtocolConformanceRef only if all conditional /// requirements are successfully resolved. - Optional - conformsToProtocol(Type sourceTy, ProtocolDecl *targetProtocol); + ProtocolConformanceRef conformsToProtocol(Type sourceTy, + ProtocolDecl *targetProtocol); /// Find a member named \p name in \p container that was declared in this /// module. @@ -478,6 +476,20 @@ class ModuleDecl : public DeclContext, public TypeDecl { /// The order of the results is not guaranteed to be meaningful. void getTopLevelDecls(SmallVectorImpl &Results) const; + /// Finds top-level decls of this module filtered by their attributes. + /// + /// This does a simple local lookup, not recursively looking through imports. + /// The order of the results is not guaranteed to be meaningful. + /// + /// \param Results Vector collecting the decls. + /// + /// \param matchAttributes Check on the attributes of a decl to + /// filter which decls to fully deserialize. Only decls with accepted + /// attributes are deserialized and added to Results. + void getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes) const; + /// Finds all local type decls of this module. /// /// This does a simple local lookup, not recursively looking through imports. @@ -570,7 +582,7 @@ class ModuleDecl : public DeclContext, public TypeDecl { private: // Make placement new and vanilla new/delete illegal for Modules. void *operator new(size_t Bytes) throw() = delete; - void operator delete(void *Data) throw() SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Data) throw() = delete; void *operator new(size_t Bytes, void *Mem) throw() = delete; public: // Only allow allocation of Modules using the allocator in ASTContext @@ -617,6 +629,11 @@ inline bool DeclContext::isModuleScopeContext() const { return isModuleContext(); } +/// Extract the source location from the given module declaration. +inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) { + return extractNearestSourceLoc(static_cast(mod)); +} + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/ModuleLoader.h b/include/swift/AST/ModuleLoader.h index af1d372659b69..d149611308e74 100644 --- a/include/swift/AST/ModuleLoader.h +++ b/include/swift/AST/ModuleLoader.h @@ -19,6 +19,7 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" @@ -100,7 +101,7 @@ class ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) = 0; + virtual bool canImportModule(Located named) = 0; /// Import a module with the given module path. /// @@ -113,7 +114,7 @@ class ModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule(SourceLoc importLoc, - ArrayRef> path) = 0; + ArrayRef> path) = 0; /// Load extensions to the given nominal type. /// diff --git a/include/swift/AST/ModuleNameLookup.h b/include/swift/AST/ModuleNameLookup.h index ddd29e597f90f..1881a0f83e7d3 100644 --- a/include/swift/AST/ModuleNameLookup.h +++ b/include/swift/AST/ModuleNameLookup.h @@ -39,6 +39,8 @@ enum class ResolutionKind { TypesOnly }; +void simple_display(llvm::raw_ostream &out, ResolutionKind kind); + /// Performs a lookup into the given module and it's imports. /// /// If 'moduleOrFile' is a ModuleDecl, we search the module and it's diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 367018f5ac2e8..fb0c7fc716056 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -23,6 +23,7 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Module.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/NullablePtr.h" #include "swift/Basic/SourceLoc.h" @@ -69,13 +70,15 @@ struct LookupResultEntry { /// When finding \c bar() from the function body of \c foo(), \c BaseDC is /// the method \c foo(). /// - /// \c BaseDC will be the method if \c self is needed for the lookup, - /// and will be the type if not. - /// In other words: If \c baseDC is a method, it means you found an instance - /// member and you should add an implicit 'self.' (Each method has its own - /// implicit self decl.) There's one other kind of non-method context that - /// has a 'self.' -- a lazy property initializer, which unlike a non-lazy - /// property can reference \c self) Hence: \code + /// \c BaseDC will be the type if \c self is not needed for the lookup. If + /// \c self is needed, \c baseDC will be either the method or a closure + /// which explicitly captured \c self. + /// In other words: If \c baseDC is a method or a closure, it means you + /// found an instance member and you should add an implicit 'self.' (Each + /// method has its own implicit self decl.) There's one other kind of + /// non-method, non-closure context that has a 'self.' -- a lazy property + /// initializer, which unlike a non-lazy property can reference \c self. + /// \code /// class Outer { ///   static func s() ///   func i() @@ -116,61 +119,124 @@ struct LookupResultEntry { ValueDecl *getBaseDecl() const; + friend bool operator ==(const LookupResultEntry &lhs, + const LookupResultEntry &rhs) { + return lhs.BaseDC == rhs.BaseDC && lhs.Value == rhs.Value; + } + void print(llvm::raw_ostream &) const; }; -/// This class implements and represents the result of performing -/// unqualified lookup (i.e. lookup for a plain identifier). -class UnqualifiedLookup { +/// The result of name lookup. +class LookupResult { +private: + /// The set of results found. + SmallVector Results; + size_t IndexOfFirstOuterResult = 0; + public: - enum class Flags { - /// This lookup is known to not affect downstream files. - KnownPrivate = 0x01, - /// This lookup should only return types. - TypeLookup = 0x02, - /// This lookup should consider declarations within protocols to which the - /// context type conforms. - AllowProtocolMembers = 0x04, - /// Don't check access when doing lookup into a type. - IgnoreAccessControl = 0x08, - /// This lookup should include results from outside the innermost scope with - /// results. - IncludeOuterResults = 0x10, - }; - using Options = OptionSet; + LookupResult() {} - /// Lookup an unqualified identifier \p Name in the context. - /// - /// If the current DeclContext is nested in a function body, the SourceLoc - /// is used to determine which declarations in that body are visible. - UnqualifiedLookup(DeclName Name, DeclContext *DC, - SourceLoc Loc = SourceLoc(), Options options = Options()); - - using ResultsVector = SmallVector; - ResultsVector Results; - - /// The index of the first result that isn't from the innermost scope - /// with results. - /// - /// That is, \c makeArrayRef(Results).take_front(IndexOfFirstOuterResults) - /// will be \c Results from the innermost scope that had results, and the - /// remaining elements of Results will be from parent scopes of this one. - /// - /// Allows unqualified name lookup to return results from outer scopes. - /// This is necessary for disambiguating calls to functions like `min` and - /// `max`. - size_t IndexOfFirstOuterResult; + explicit LookupResult(const SmallVectorImpl &Results, + size_t indexOfFirstOuterResult) + : Results(Results.begin(), Results.end()), + IndexOfFirstOuterResult(indexOfFirstOuterResult) {} + + using iterator = SmallVectorImpl::iterator; + iterator begin() { return Results.begin(); } + iterator end() { + return Results.begin() + IndexOfFirstOuterResult; + } + unsigned size() const { return innerResults().size(); } + bool empty() const { return innerResults().empty(); } + + ArrayRef innerResults() const { + return llvm::makeArrayRef(Results).take_front(IndexOfFirstOuterResult); + } - /// Return true if anything was found by the name lookup. - bool isSuccess() const { return !Results.empty(); } + ArrayRef outerResults() const { + return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); + } + + /// \returns An array of both the inner and outer results. + ArrayRef allResults() const { + return llvm::makeArrayRef(Results); + } + + const LookupResultEntry& operator[](unsigned index) const { + return Results[index]; + } - /// Get the result as a single type, or a null type if that fails. - TypeDecl *getSingleTypeResult() const; + LookupResultEntry front() const { return innerResults().front(); } + LookupResultEntry back() const { return innerResults().back(); } + + /// \returns The index of the first outer result within \c allResults(). + size_t getIndexOfFirstOuterResult() const { return IndexOfFirstOuterResult; } + + /// Add a result to the set of results. + void add(LookupResultEntry result, bool isOuter) { + Results.push_back(result); + if (!isOuter) { + IndexOfFirstOuterResult++; + assert(IndexOfFirstOuterResult == Results.size() && + "found an outer result before an inner one"); + } else { + assert(IndexOfFirstOuterResult > 0 && + "found outer results without an inner one"); + } + } + + void clear() { Results.clear(); } + + /// Determine whether the result set is nonempty. + explicit operator bool() const { + return !empty(); + } + + TypeDecl *getSingleTypeResult() const { + if (size() != 1) + return nullptr; + + return dyn_cast(front().getValueDecl()); + } + + friend bool operator ==(const LookupResult &lhs, const LookupResult &rhs) { + return lhs.Results == rhs.Results && + lhs.IndexOfFirstOuterResult == rhs.IndexOfFirstOuterResult; + } + + /// Filter out any results that aren't accepted by the given predicate. + void + filter(llvm::function_ref pred); + + /// Shift down results by dropping inner results while keeping outer + /// results (if any), the innermost of which are recogized as inner + /// results afterwards. + void shiftDownResults(); }; -inline UnqualifiedLookup::Options operator|(UnqualifiedLookup::Flags flag1, - UnqualifiedLookup::Flags flag2) { - return UnqualifiedLookup::Options(flag1) | flag2; +enum class UnqualifiedLookupFlags { + /// This lookup is known to not affect downstream files. + KnownPrivate = 0x01, + /// This lookup should only return types. + TypeLookup = 0x02, + /// This lookup should consider declarations within protocols to which the + /// context type conforms. + AllowProtocolMembers = 0x04, + /// Don't check access when doing lookup into a type. + IgnoreAccessControl = 0x08, + /// This lookup should include results from outside the innermost scope with + /// results. + IncludeOuterResults = 0x10, +}; + +using UnqualifiedLookupOptions = OptionSet; + +void simple_display(llvm::raw_ostream &out, UnqualifiedLookupOptions options); + +inline UnqualifiedLookupOptions operator|(UnqualifiedLookupFlags flag1, + UnqualifiedLookupFlags flag2) { + return UnqualifiedLookupOptions(flag1) | flag2; } /// Describes the reason why a certain declaration is visible. @@ -333,11 +399,11 @@ class VectorDeclConsumer : public VisibleDeclConsumer { class NamedDeclConsumer : public VisibleDeclConsumer { virtual void anchor() override; public: - DeclName name; + DeclNameRef name; SmallVectorImpl &results; bool isTypeLookup; - NamedDeclConsumer(DeclName name, + NamedDeclConsumer(DeclNameRef name, SmallVectorImpl &results, bool isTypeLookup) : name(name), results(results), isTypeLookup(isTypeLookup) {} @@ -348,7 +414,7 @@ class NamedDeclConsumer : public VisibleDeclConsumer { // to avoid circular validation. if (isTypeLookup && !isa(VD)) return; - if (VD->getFullName().matchesRef(name)) + if (VD->getFullName().matchesRef(name.getFullName())) results.push_back(LookupResultEntry(VD)); } }; @@ -429,7 +495,7 @@ void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name, void getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject); /// Retrieve the set of nominal type declarations that are directly @@ -437,7 +503,7 @@ void getDirectlyInheritedNominalTypeDecls( /// and splitting out the components of compositions. /// /// If we come across the AnyObject type, set \c anyObject true. -SmallVector, 4> +SmallVector, 4> getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject); @@ -595,12 +661,14 @@ class ASTScope { void buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); + static void expandFunctionBody(AbstractFunctionDecl *); + /// Flesh out the tree for dumping void buildFullyExpandedTree(); /// \return the scopes traversed static llvm::SmallVector - unqualifiedLookup(SourceFile *, DeclName, SourceLoc, + unqualifiedLookup(SourceFile *, DeclNameRef, SourceLoc, const DeclContext *startingContext, namelookup::AbstractASTScopeDeclConsumer &); @@ -608,8 +676,7 @@ class ASTScope { computeIsCascadingUse(ArrayRef history, Optional initialIsCascadingUse); - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &) const; void dumpOneScopeMapLocation(std::pair); @@ -627,8 +694,12 @@ class ASTScope { return Mem; } + static bool areInactiveIfConfigClausesSupported(); + private: static ast_scope::ASTSourceFileScope *createScopeTree(SourceFile *); + + void expandFunctionBodyImpl(AbstractFunctionDecl *); }; } // end namespace swift diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 87afbd2df3710..3094cf071c5df 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -25,15 +25,25 @@ namespace swift { class ClassDecl; +class DeclContext; +class DeclName; +class DeclNameRef; class DestructorDecl; class GenericContext; class GenericParamList; +class LookupResult; +enum class NLKind; +class SourceLoc; class TypeAliasDecl; class TypeDecl; +enum class UnqualifiedLookupFlags; namespace ast_scope { class ASTScopeImpl; class ScopeCreator; } // namespace ast_scope +namespace namelookup { +enum class ResolutionKind; +} // namespace namelookup /// Display a nominal type or extension thereof. void simple_display( @@ -151,6 +161,29 @@ class SuperclassDeclRequest : void cacheResult(ClassDecl *value) const; }; +/// Requests whether or not this class has designated initializers that are +/// not public or @usableFromInline. +class HasMissingDesignatedInitializersRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ClassDecl *subject) const; + +public: + // Caching + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(bool) const; +}; + /// Request the nominal declaration extended by a given extension declaration. class ExtendedNominalRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; - SourceLoc getLoc() const; +private: + friend SimpleRequest; - friend llvm::hash_code hash_value(const PrecedenceGroupDescriptor &owner) { - return hash_combine(llvm::hash_value(owner.dc), - llvm::hash_value(owner.ident.getAsOpaquePointer()), - llvm::hash_value(owner.nameLoc.getOpaquePointerValue())); + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ast_scope::ASTScopeImpl *, + ast_scope::ScopeCreator *) const; + +public: + // Separate caching. + bool isCached() const; + Optional getCachedResult() const; + void cacheResult(ast_scope::ASTScopeImpl *) const {} +}; + +/// The input type for an unqualified lookup request. +class UnqualifiedLookupDescriptor { + using LookupOptions = OptionSet; + +public: + DeclNameRef Name; + DeclContext *DC; + SourceLoc Loc; + LookupOptions Options; + + UnqualifiedLookupDescriptor(DeclNameRef name, DeclContext *dc, + SourceLoc loc = SourceLoc(), + LookupOptions options = {}) + : Name(name), DC(dc), Loc(loc), Options(options) { } + + friend llvm::hash_code hash_value(const UnqualifiedLookupDescriptor &desc) { + return llvm::hash_combine(desc.Name, desc.DC, desc.Loc, + desc.Options.toRaw()); } - friend bool operator==(const PrecedenceGroupDescriptor &lhs, - const PrecedenceGroupDescriptor &rhs) { - return lhs.dc == rhs.dc && - lhs.ident == rhs.ident && - lhs.nameLoc == rhs.nameLoc; + friend bool operator==(const UnqualifiedLookupDescriptor &lhs, + const UnqualifiedLookupDescriptor &rhs) { + return lhs.Name == rhs.Name && lhs.DC == rhs.DC && lhs.Loc == rhs.Loc && + lhs.Options.toRaw() == rhs.Options.toRaw(); } - friend bool operator!=(const PrecedenceGroupDescriptor &lhs, - const PrecedenceGroupDescriptor &rhs) { + friend bool operator!=(const UnqualifiedLookupDescriptor &lhs, + const UnqualifiedLookupDescriptor &rhs) { return !(lhs == rhs); } }; -void simple_display(llvm::raw_ostream &out, const PrecedenceGroupDescriptor &d); +void simple_display(llvm::raw_ostream &out, + const UnqualifiedLookupDescriptor &desc); + +SourceLoc extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc); -class LookupPrecedenceGroupRequest - : public SimpleRequest { +/// Performs unqualified lookup for a DeclName from a given context. +class UnqualifiedLookupRequest + : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -317,23 +384,56 @@ class LookupPrecedenceGroupRequest friend SimpleRequest; // Evaluation. - llvm::Expected - evaluate(Evaluator &evaluator, PrecedenceGroupDescriptor descriptor) const; + llvm::Expected evaluate(Evaluator &evaluator, + UnqualifiedLookupDescriptor desc) const; +}; + +using QualifiedLookupResult = SmallVector; +/// Performs a lookup into a given module and its imports. +class LookupInModuleRequest + : public SimpleRequest { public: - // Source location - SourceLoc getNearestLoc() const; - - // Separate caching. - bool isCached() const { return true; } + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, + NLKind lookupKind, namelookup::ResolutionKind resolutionKind, + const DeclContext *moduleScopeContext) const; }; -/// Expand the given ASTScope. Requestified to detect recursion. -class ExpandASTScopeRequest - : public SimpleRequest { +/// Perform \c AnyObject lookup for a given member. +class AnyObjectLookupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + llvm::Expected evaluate(Evaluator &evaluator, + const DeclContext *dc, + DeclNameRef name, + NLOptions options) const; +}; + +class ModuleQualifiedLookupRequest + : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -341,15 +441,78 @@ class ExpandASTScopeRequest friend SimpleRequest; // Evaluation. - llvm::Expected - evaluate(Evaluator &evaluator, ast_scope::ASTScopeImpl *, - ast_scope::ScopeCreator *) const; + llvm::Expected evaluate(Evaluator &evaluator, + const DeclContext *DC, + ModuleDecl *mod, DeclNameRef name, + NLOptions opts) const; +}; +class QualifiedLookupRequest + : public SimpleRequest, + DeclNameRef, NLOptions), + CacheKind::Uncached> { public: - // Separate caching. - bool isCached() const; - Optional getCachedResult() const; - void cacheResult(ast_scope::ASTScopeImpl *) const {} + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, const DeclContext *DC, + SmallVector decls, + DeclNameRef name, + NLOptions opts) const; +}; + +/// The input type for a direct lookup request. +class DirectLookupDescriptor final { + using LookupOptions = OptionSet; + +public: + NominalTypeDecl *DC; + DeclName Name; + LookupOptions Options; + + DirectLookupDescriptor(NominalTypeDecl *dc, DeclName name, + LookupOptions options = {}) + : DC(dc), Name(name), Options(options) {} + + friend llvm::hash_code hash_value(const DirectLookupDescriptor &desc) { + return llvm::hash_combine(desc.Name, desc.DC, desc.Options.toRaw()); + } + + friend bool operator==(const DirectLookupDescriptor &lhs, + const DirectLookupDescriptor &rhs) { + return lhs.Name == rhs.Name && lhs.DC == rhs.DC && + lhs.Options.toRaw() == rhs.Options.toRaw(); + } + + friend bool operator!=(const DirectLookupDescriptor &lhs, + const DirectLookupDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const DirectLookupDescriptor &desc); + +SourceLoc extractNearestSourceLoc(const DirectLookupDescriptor &desc); + +class DirectLookupRequest + : public SimpleRequest(DirectLookupDescriptor), + CacheKind::Uncached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected> + evaluate(Evaluator &evaluator, DirectLookupDescriptor desc) const; }; #define SWIFT_TYPEID_ZONE NameLookup diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index 87de96364fb49..a4563fa875b3c 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -15,9 +15,15 @@ // //===----------------------------------------------------------------------===// +SWIFT_REQUEST(NameLookup, AnyObjectLookupRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, CustomAttrNominalRequest, NominalTypeDecl *(CustomAttr *, DeclContext *), Cached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, DirectLookupRequest, + TinyPtrVector(DirectLookupDescriptor), Uncached, + NoLocationInfo) SWIFT_REQUEST(NameLookup, ExpandASTScopeRequest, ast_scope::ASTScopeImpl* (ast_scope::ASTScopeImpl*, ast_scope::ScopeCreator*), SeparatelyCached, @@ -34,17 +40,34 @@ SWIFT_REQUEST(NameLookup, InheritedDeclsReferencedRequest, DirectlyReferencedTypeDecls( llvm::PointerUnion, unsigned), Uncached, HasNearestLocation) -SWIFT_REQUEST(NameLookup, LookupPrecedenceGroupRequest, - PrecedenceGroupDecl *(DeclContext *, Identifier, SourceLoc), - Cached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, LookupInModuleRequest, + QualifiedLookupResult(const DeclContext *, DeclName, NLKind, + namelookup::ResolutionKind, + const DeclContext *), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, ModuleQualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, ModuleDecl *, DeclNameRef, NLOptions), + Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, QualifiedLookupRequest, + QualifiedLookupResult( + const DeclContext *, SmallVector, + DeclNameRef, NLOptions), + Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest, SelfBounds(llvm::PointerUnion), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, SuperclassDeclRequest, ClassDecl *(NominalTypeDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, HasMissingDesignatedInitializersRequest, + bool(ClassDecl *), + SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(NameLookup, TypeDeclsFromWhereClauseRequest, DirectlyReferencedTypeDecls(ExtensionDecl *), Uncached, NoLocationInfo) SWIFT_REQUEST(NameLookup, UnderlyingTypeDeclsReferencedRequest, DirectlyReferencedTypeDecls(TypeAliasDecl *), Uncached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, UnqualifiedLookupRequest, + LookupResult(UnqualifiedLookupDescriptor), Uncached, + NoLocationInfo) diff --git a/include/swift/AST/ParameterList.h b/include/swift/AST/ParameterList.h index 1a431a2053275..a5af4a8e81d74 100644 --- a/include/swift/AST/ParameterList.h +++ b/include/swift/AST/ParameterList.h @@ -18,6 +18,7 @@ #define SWIFT_AST_PARAMETERLIST_H #include "swift/AST/Decl.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/OptionSet.h" #include "llvm/Support/TrailingObjects.h" @@ -102,15 +103,15 @@ class alignas(ParamDecl *) ParameterList final : /// Change the DeclContext of any contained parameters to the specified /// DeclContext. void setDeclContextOfParamDecls(DeclContext *DC); - - + /// Flags used to indicate how ParameterList cloning should operate. enum CloneFlags { /// The cloned ParamDecls should be marked implicit. Implicit = 0x01, - /// The cloned pattern is for an inherited constructor; mark default - /// arguments as inherited, and mark unnamed arguments as named. + /// Mark default arguments as inherited. Inherited = 0x02, + /// Mark unnamed arguments as named. + NamedArguments = 0x04, }; friend OptionSet operator|(CloneFlags flag1, CloneFlags flag2) { @@ -131,7 +132,7 @@ class alignas(ParamDecl *) ParameterList final : SourceLoc getStartLoc() const { return getSourceRange().Start; } SourceLoc getEndLoc() const { return getSourceRange().End; } - void dump() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, unsigned Indent = 0) const; // void print(raw_ostream &OS) const; diff --git a/include/swift/AST/ParseRequests.h b/include/swift/AST/ParseRequests.h index 4bac5dce03fa7..7e9a25147f942 100644 --- a/include/swift/AST/ParseRequests.h +++ b/include/swift/AST/ParseRequests.h @@ -27,12 +27,22 @@ template void reportEvaluatedRequest(UnifiedStatsReporter &stats, const Request &request); +struct FingerprintAndMembers { + Optional fingerprint = None; + ArrayRef members = {}; + bool operator==(const FingerprintAndMembers &x) const { + return fingerprint == x.fingerprint && members == x.members; + } +}; + +void simple_display(llvm::raw_ostream &out, const FingerprintAndMembers &value); + /// Parse the members of a nominal type declaration or extension. -class ParseMembersRequest : - public SimpleRequest(IterableDeclContext *), - CacheKind::Cached> -{ +/// Return a fingerprint and the members. +class ParseMembersRequest + : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -40,8 +50,8 @@ class ParseMembersRequest : friend SimpleRequest; // Evaluation. - ArrayRef evaluate(Evaluator &evaluator, - IterableDeclContext *idc) const; + FingerprintAndMembers evaluate(Evaluator &evaluator, + IterableDeclContext *idc) const; public: // Caching diff --git a/include/swift/AST/ParseTypeIDZone.def b/include/swift/AST/ParseTypeIDZone.def index 2e46ac6bec166..229c9241d97bd 100644 --- a/include/swift/AST/ParseTypeIDZone.def +++ b/include/swift/AST/ParseTypeIDZone.def @@ -15,7 +15,7 @@ //===----------------------------------------------------------------------===// SWIFT_REQUEST(Parse, ParseMembersRequest, - ArrayRef(IterableDeclContext *), Cached, NoLocationInfo) + FingerprintAndMembers(IterableDeclContext *), Cached, NoLocationInfo) SWIFT_REQUEST(Parse, ParseAbstractFunctionBodyRequest, BraceStmt *(AbstractFunctionDecl *), SeparatelyCached, NoLocationInfo) diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index e5655dad5b37f..4bf10c283ed1d 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -20,6 +20,7 @@ #include "swift/Basic/SourceLoc.h" #include "swift/Basic/type_traits.h" #include "swift/AST/Decl.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" @@ -221,9 +222,7 @@ class alignas(8) Pattern { void print(llvm::raw_ostream &OS, const PrintOptions &Options = PrintOptions()) const; - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// walk - This recursively walks the AST rooted at this pattern. Pattern *walk(ASTWalker &walker); @@ -505,14 +504,14 @@ class IsPattern : public Pattern { class EnumElementPattern : public Pattern { TypeLoc ParentType; SourceLoc DotLoc; - SourceLoc NameLoc; - Identifier Name; + DeclNameLoc NameLoc; + DeclNameRef Name; PointerUnion ElementDeclOrUnresolvedOriginalExpr; Pattern /*nullable*/ *SubPattern; public: - EnumElementPattern(TypeLoc ParentType, SourceLoc DotLoc, SourceLoc NameLoc, - Identifier Name, EnumElementDecl *Element, + EnumElementPattern(TypeLoc ParentType, SourceLoc DotLoc, DeclNameLoc NameLoc, + DeclNameRef Name, EnumElementDecl *Element, Pattern *SubPattern, Optional Implicit = None) : Pattern(PatternKind::EnumElement), ParentType(ParentType), DotLoc(DotLoc), NameLoc(NameLoc), Name(Name), @@ -525,8 +524,8 @@ class EnumElementPattern : public Pattern { /// Create an unresolved EnumElementPattern for a `.foo` pattern relying on /// contextual type. EnumElementPattern(SourceLoc DotLoc, - SourceLoc NameLoc, - Identifier Name, + DeclNameLoc NameLoc, + DeclNameRef Name, Pattern *SubPattern, Expr *UnresolvedOriginalExpr) : Pattern(PatternKind::EnumElement), @@ -552,7 +551,7 @@ class EnumElementPattern : public Pattern { void setSubPattern(Pattern *p) { SubPattern = p; } - Identifier getName() const { return Name; } + DeclNameRef getName() const { return Name; } EnumElementDecl *getElementDecl() const { return ElementDeclOrUnresolvedOriginalExpr.dyn_cast(); @@ -568,18 +567,18 @@ class EnumElementPattern : public Pattern { return ElementDeclOrUnresolvedOriginalExpr.is(); } - SourceLoc getNameLoc() const { return NameLoc; } - SourceLoc getLoc() const { return NameLoc; } + DeclNameLoc getNameLoc() const { return NameLoc; } + SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } SourceLoc getStartLoc() const { return ParentType.hasLocation() ? ParentType.getSourceRange().Start : DotLoc.isValid() ? DotLoc - : NameLoc; + : NameLoc.getBaseNameLoc(); } SourceLoc getEndLoc() const { if (SubPattern && SubPattern->getSourceRange().isValid()) { return SubPattern->getSourceRange().End; } - return NameLoc; + return NameLoc.getEndLoc(); } SourceRange getSourceRange() const { return {getStartLoc(), getEndLoc()}; } @@ -757,7 +756,93 @@ inline Pattern *Pattern::getSemanticsProvidingPattern() { return vp->getSubPattern()->getSemanticsProvidingPattern(); return this; } - + +/// Describes a pattern and the context in which it occurs. +class ContextualPattern { + /// The pattern and whether this is the top-level pattern. + llvm::PointerIntPair patternAndTopLevel; + + /// Either the declaration context or the enclosing pattern binding + /// declaration. + llvm::PointerUnion declOrContext; + + /// Index into the pattern binding declaration, when there is one. + unsigned index = 0; + + ContextualPattern( + Pattern *pattern, bool topLevel, + llvm::PointerUnion declOrContext, + unsigned index + ) : patternAndTopLevel(pattern, topLevel), + declOrContext(declOrContext), + index(index) { } + +public: + /// Produce a contextual pattern for a pattern binding declaration entry. + static ContextualPattern forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index); + + /// Produce a contextual pattern for a raw pattern that always allows + /// inference. + static ContextualPattern forRawPattern(Pattern *pattern, DeclContext *dc) { + return ContextualPattern(pattern, /*topLevel=*/true, dc, /*index=*/0); + } + + /// Retrieve a contextual pattern for the given subpattern. + ContextualPattern forSubPattern( + Pattern *subpattern, bool retainTopLevel) const { + return ContextualPattern( + subpattern, isTopLevel() && retainTopLevel, declOrContext, index); + } + + /// Retrieve the pattern. + Pattern *getPattern() const { + return patternAndTopLevel.getPointer(); + } + + /// Whether this is the top-level pattern in this context. + bool isTopLevel() const { + return patternAndTopLevel.getInt(); + } + + /// Retrieve the declaration context of the pattern. + DeclContext *getDeclContext() const; + + /// Retrieve the pattern binding declaration that owns this pattern, if + /// there is one. + PatternBindingDecl *getPatternBindingDecl() const; + + /// Retrieve the index into the pattern binding declaration for the top-level + /// pattern. + unsigned getPatternBindingIndex() const { + assert(getPatternBindingDecl() != nullptr); + return index; + } + + /// Whether this pattern allows type inference, e.g., from an initializer + /// expression. + bool allowsInference() const; + + friend llvm::hash_code hash_value(const ContextualPattern &pattern) { + return llvm::hash_combine(pattern.getPattern(), + pattern.isTopLevel(), + pattern.declOrContext); + } + + friend bool operator==(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return lhs.patternAndTopLevel == rhs.patternAndTopLevel && + lhs.declOrContext == rhs.declOrContext; + } + + friend bool operator!=(const ContextualPattern &lhs, + const ContextualPattern &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const ContextualPattern &pattern); + } // end namespace swift #endif diff --git a/include/swift/AST/PlatformKind.h b/include/swift/AST/PlatformKind.h index 37e171ef41652..521167f6ce2d7 100644 --- a/include/swift/AST/PlatformKind.h +++ b/include/swift/AST/PlatformKind.h @@ -26,7 +26,7 @@ namespace swift { class LangOptions; /// Available platforms for the availability attribute. -enum class PlatformKind { +enum class PlatformKind: uint8_t { none, #define AVAILABILITY_PLATFORM(X, PrettyName) X, #include "swift/AST/PlatformKinds.def" @@ -51,11 +51,20 @@ StringRef prettyPlatformString(PlatformKind platform); /// restrictions are enabled, but OSXApplicationExtension is not considered /// active when the target platform is OS X and app extension restrictions are /// disabled. PlatformKind::none is always considered active. -bool isPlatformActive(PlatformKind Platform, LangOptions &LangOpts); - +/// If ForTargetVariant is true then for zippered builds the target-variant +/// triple will be used rather than the target to determine whether the +/// platform is active. +bool isPlatformActive(PlatformKind Platform, LangOptions &LangOpts, + bool ForTargetVariant = false); + /// Returns the target platform for the given language options. PlatformKind targetPlatform(LangOptions &LangOpts); - + +/// Returns true when availability attributes from the "parent" platform +/// should also apply to the "child" platform for declarations without +/// an explicit attribute for the child. +bool inheritsAvailabilityFromPlatform(PlatformKind Child, PlatformKind Parent); + } // end namespace swift #endif diff --git a/include/swift/AST/PlatformKinds.def b/include/swift/AST/PlatformKinds.def index 9192450f15236..cc9b9ede0eae8 100644 --- a/include/swift/AST/PlatformKinds.def +++ b/include/swift/AST/PlatformKinds.def @@ -30,5 +30,7 @@ AVAILABILITY_PLATFORM(iOSApplicationExtension, "application extensions for iOS") AVAILABILITY_PLATFORM(tvOSApplicationExtension, "application extensions for tvOS") AVAILABILITY_PLATFORM(watchOSApplicationExtension, "application extensions for watchOS") AVAILABILITY_PLATFORM(OSXApplicationExtension, "application extensions for macOS") +AVAILABILITY_PLATFORM(macCatalyst, "Mac Catalyst") +AVAILABILITY_PLATFORM(macCatalystApplicationExtension, "application extensions for Mac Catalyst") #undef AVAILABILITY_PLATFORM diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 6519e4f8eef14..aa5ec2a051b4b 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -270,6 +270,10 @@ struct PrintOptions { /// Whether to skip keywords with a prefix of underscore such as __consuming. bool SkipUnderscoredKeywords = false; + /// Prints type variables and unresolved types in an expanded notation suitable + /// for debugging. + bool PrintTypesForDebugging = false; + /// How to print opaque return types. enum class OpaqueReturnTypePrintingMode { /// 'some P1 & P2'. @@ -301,8 +305,21 @@ struct PrintOptions { /// List of decls that should be printed even if they are implicit and \c SkipImplicit is set to true. std::vector TreatAsExplicitDeclList; + enum class FunctionRepresentationMode : uint8_t { + /// Print the entire convention, including an arguments. + /// For example, this will print a cType argument label if applicable. + Full, + /// Print only the name of the convention, skipping extra argument labels. + NameOnly, + /// Skip printing the @convention(..) altogether. + None + }; + /// Whether to print function @convention attribute on function types. - bool PrintFunctionRepresentationAttrs = true; + // FIXME: [clang-function-type-serialization] Once we start serializing Clang + // types, we should also start printing the full type in the swiftinterface. + FunctionRepresentationMode PrintFunctionRepresentationAttrs = + FunctionRepresentationMode::NameOnly; /// Whether to print storage representation attributes on types, e.g. /// '@sil_weak', '@sil_unmanaged'. @@ -362,6 +379,13 @@ struct PrintOptions { ArgAndParamPrintingMode ArgAndParamPrinting = ArgAndParamPrintingMode::MatchSource; + /// Whether to print the default argument value string + /// representation. + bool PrintDefaultArgumentValue = true; + + /// Whether to print "_" placeholders for empty arguments. + bool PrintEmptyArgumentNames = true; + /// Whether to print documentation comments attached to declarations. /// Note that this may print documentation comments from related declarations /// (e.g. the overridden method in the superclass) if such comment is found. @@ -424,7 +448,7 @@ struct PrintOptions { /// and constructors) will be printed by this function. std::function FunctionBody; - BracketOptions BracketOptions; + swift::BracketOptions BracketOptions; // This is explicit to guarantee that it can be called from LLDB. PrintOptions() {} @@ -487,6 +511,7 @@ struct PrintOptions { QualifyNestedDeclarations::Always; result.PrintDocumentationComments = true; result.SkipUnderscoredKeywords = true; + result.EnumRawValues = EnumRawValueMode::PrintObjCOnly; return result; } @@ -496,7 +521,8 @@ struct PrintOptions { /// consistent and well-formed. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr); + static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention); /// Retrieve the set of options suitable for "Generated Interfaces", which /// are a prettified representation of the public API of a module, to be @@ -579,7 +605,8 @@ struct PrintOptions { PO.SkipUnderscoredKeywords = true; PO.EnumRawValues = EnumRawValueMode::Print; PO.PrintImplicitAttrs = false; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.PrintDocumentationComments = false; PO.ExcludeAttrList.push_back(DAK_Available); PO.SkipPrivateStdlibDecls = true; diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 71820ab76b64d..f5fa29f0f6be4 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -23,6 +23,7 @@ #include "swift/AST/TypeAlignments.h" #include "swift/AST/Witness.h" #include "swift/Basic/Compiler.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" @@ -51,7 +52,7 @@ typedef llvm::DenseMap WitnessMap; /// Map from associated type requirements to the corresponding type and /// the type declaration that was used to satisfy the requirement. -typedef llvm::DenseMap> +typedef llvm::DenseMap TypeWitnessMap; /// Describes the kind of protocol conformance structure used to encode @@ -156,7 +157,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { /// Retrieve the type witness and type decl (if one exists) /// for the given associated type. - std::pair + TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const; @@ -181,7 +182,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { continue; const auto &TWInfo = getTypeWitnessAndDecl(assocTypeReq); - if (f(assocTypeReq, TWInfo.first, TWInfo.second)) + if (f(assocTypeReq, TWInfo.getWitnessType(), TWInfo.getWitnessDecl())) return true; } @@ -284,7 +285,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { // Make vanilla new/delete illegal for protocol conformances. void *operator new(size_t bytes) = delete; - void operator delete(void *data) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *data) = delete; // Only allow allocation of protocol conformances using the allocator in // ASTContext or by doing a placement new. @@ -320,7 +321,7 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance { LookupConformanceFn conformances, SubstOptions options=None) const; - void dump() const; + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out, unsigned indent = 0) const; }; @@ -403,6 +404,9 @@ class RootProtocolConformance : public ProtocolConformance { class NormalProtocolConformance : public RootProtocolConformance, public llvm::FoldingSetNode { + friend class ValueWitnessRequest; + friend class TypeWitnessRequest; + /// The protocol being conformed to and its current state. llvm::PointerIntPair ProtocolAndState; @@ -587,10 +591,13 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Retrieve the type witness and type decl (if one exists) /// for the given associated type. - std::pair + TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const; + TypeWitnessAndDecl + getTypeWitnessUncached(AssociatedTypeDecl *requirement) const; + /// Determine whether the protocol conformance has a type witness for the /// given associated type. bool hasTypeWitness(AssociatedTypeDecl *assocType) const; @@ -609,6 +616,8 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Retrieve the value witness corresponding to the given requirement. Witness getWitness(ValueDecl *requirement) const; + Witness getWitnessUncached(ValueDecl *requirement) const; + /// Determine whether the protocol conformance has a witness for the given /// requirement. bool hasWitness(ValueDecl *requirement) const { @@ -640,7 +649,7 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Determine whether the witness for the given type requirement /// is the default definition. bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const { - TypeDecl *witnessDecl = getTypeWitnessAndDecl(requirement).second; + TypeDecl *witnessDecl = getTypeWitnessAndDecl(requirement).getWitnessDecl(); if (witnessDecl) return witnessDecl->isImplicit(); // Conservatively assume it does not. @@ -712,7 +721,7 @@ class SelfProtocolConformance : public RootProtocolConformance { llvm_unreachable("self-conformances never have associated types"); } - std::pair + TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const { llvm_unreachable("self-conformances never have associated types"); @@ -858,7 +867,7 @@ class SpecializedProtocolConformance : public ProtocolConformance, /// Retrieve the type witness and type decl (if one exists) /// for the given associated type. - std::pair + TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const; @@ -970,7 +979,7 @@ class InheritedProtocolConformance : public ProtocolConformance, /// Retrieve the type witness and type decl (if one exists) /// for the given associated type. - std::pair + TypeWitnessAndDecl getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options=None) const { return InheritedConformance->getTypeWitnessAndDecl(assocType, options); @@ -1014,6 +1023,8 @@ inline bool ProtocolConformance::hasWitness(ValueDecl *requirement) const { return getRootConformance()->hasWitness(requirement); } +void simple_display(llvm::raw_ostream &out, const ProtocolConformance *conf); + } // end namespace swift #endif // LLVM_SWIFT_AST_PROTOCOLCONFORMANCE_H diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index 9a706a27802ad..ee8d3abd8e38c 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -16,6 +16,7 @@ #ifndef SWIFT_AST_PROTOCOLCONFORMANCEREF_H #define SWIFT_AST_PROTOCOLCONFORMANCEREF_H +#include "swift/Basic/Debug.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerUnion.h" #include "swift/AST/Requirement.h" @@ -63,14 +64,19 @@ class ProtocolConformanceRef { "cannot construct ProtocolConformanceRef with null"); } + ProtocolConformanceRef(std::nullptr_t = nullptr) + : Union((ProtocolDecl *)nullptr) {} + static ProtocolConformanceRef forInvalid() { - return ProtocolConformanceRef(UnionType((ProtocolDecl *)nullptr)); + return ProtocolConformanceRef(); } bool isInvalid() const { return !Union; } + explicit operator bool() const { return !isInvalid(); } + /// Create either a concrete or an abstract protocol conformance reference, /// depending on whether ProtocolConformance is null. explicit ProtocolConformanceRef(ProtocolDecl *protocol, @@ -124,7 +130,7 @@ class ProtocolConformanceRef { getAssociatedConformance(Type origType, Type dependentType, ProtocolDecl *requirement) const; - void dump() const; + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out, unsigned indent = 0) const; bool operator==(ProtocolConformanceRef other) const { diff --git a/include/swift/AST/ReferenceStorage.def b/include/swift/AST/ReferenceStorage.def index f0dd8a891492b..2b72469e5fc00 100644 --- a/include/swift/AST/ReferenceStorage.def +++ b/include/swift/AST/ReferenceStorage.def @@ -55,7 +55,7 @@ /// UNCHECKED_REF_STORAGE /// Name##RetainValueInst /// Name##ReleaseValueInst -/// Copy##Name##ValueInst +/// StrongCopy##Name##ValueInst /// LOADABLE_REF_STORAGE /// Ref*ToNameInst /// Name*ToRefInst @@ -63,7 +63,7 @@ /// Load##Name##Inst /// Store##Name##Inst /// ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE -/// Copy##Name##ValueInst +/// StrongCopy##Name##ValueInst /// StrongRetain##Name##Inst /// Name##RetainInst /// Name##ReleaseInst @@ -78,7 +78,7 @@ /// ALWAYS_LOADABLE_CHECKED_REF_STORAGE /// Ref*ToNameInst /// Name*ToRefInst -/// Copy##Name##ValueInst +/// StrongCopy##Name##ValueInst /// StrongRetain##Name##Inst /// Name##RetainInst /// Name##ReleaseInst @@ -87,7 +87,7 @@ /// Name*ToRefInst /// Load##Name##Inst /// Store##Name##Inst -/// Copy##Name##ValueInst +/// StrongCopy##Name##ValueInst /// StrongRetain##Name##Inst /// Name##RetainInst /// Name##ReleaseInst diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index 95d0209e13772..c8b90bbc8b631 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -18,6 +18,7 @@ #define SWIFT_AST_REQUIREMENT_H #include "swift/AST/Type.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/ErrorHandling.h" @@ -126,7 +127,7 @@ class Requirement { /// Get the canonical form of this requirement. Requirement getCanonical() const; - void dump() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &out) const; void print(raw_ostream &os, const PrintOptions &opts) const; void print(ASTPrinter &printer, const PrintOptions &opts) const; diff --git a/include/swift/AST/SILGenRequests.h b/include/swift/AST/SILGenRequests.h new file mode 100644 index 0000000000000..3567673172577 --- /dev/null +++ b/include/swift/AST/SILGenRequests.h @@ -0,0 +1,118 @@ +//===--- SILGenRequests.h - SILGen Requests ---------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines SILGen requests. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILGEN_REQUESTS_H +#define SWIFT_SILGEN_REQUESTS_H + +#include "swift/AST/ASTTypeIDs.h" +#include "swift/AST/SimpleRequest.h" + +namespace swift { + +class FileUnit; +class LangOptions; +class ModuleDecl; +class SILModule; +class SILOptions; +class SourceFile; + +namespace Lowering { + class TypeConverter; +} + +/// Report that a request of the given kind is being evaluated, so it +/// can be recorded by the stats reporter. +template +void reportEvaluatedRequest(UnifiedStatsReporter &stats, + const Request &request); + +struct SILGenDescriptor { + llvm::PointerUnion context; + Lowering::TypeConverter &conv; + const SILOptions &opts; + + friend llvm::hash_code hash_value(const SILGenDescriptor &owner) { + return llvm::hash_combine(owner.context, (void *)&owner.conv, + (void *)&owner.opts); + } + + friend bool operator==(const SILGenDescriptor &lhs, + const SILGenDescriptor &rhs) { + return lhs.context == rhs.context && + &lhs.conv == &rhs.conv && + &lhs.opts == &rhs.opts; + } + + friend bool operator!=(const SILGenDescriptor &lhs, + const SILGenDescriptor &rhs) { + return !(lhs == rhs); + } + +public: + static SILGenDescriptor forFile(FileUnit &sf, Lowering::TypeConverter &conv, + const SILOptions &opts) { + return SILGenDescriptor{&sf, conv, opts}; + } + + static SILGenDescriptor forWholeModule(ModuleDecl *mod, + Lowering::TypeConverter &conv, + const SILOptions &opts) { + return SILGenDescriptor{mod, conv, opts}; + } +}; + +class GenerateSILRequest : + public SimpleRequest(SILGenDescriptor), + CacheKind::Uncached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected> + evaluate(Evaluator &evaluator, SILGenDescriptor desc) const; + +public: + bool isCached() const { return true; } +}; + +void simple_display(llvm::raw_ostream &out, const SILGenDescriptor &d); + +SourceLoc extractNearestSourceLoc(const SILGenDescriptor &desc); + +/// The zone number for SILGen. +#define SWIFT_TYPEID_ZONE SILGen +#define SWIFT_TYPEID_HEADER "swift/AST/SILGenTypeIDZone.def" +#include "swift/Basic/DefineTypeIDZone.h" +#undef SWIFT_TYPEID_ZONE +#undef SWIFT_TYPEID_HEADER + + // Set up reporting of evaluated requests. +#define SWIFT_REQUEST(Zone, RequestType, Sig, Caching, LocOptions) \ +template<> \ +inline void reportEvaluatedRequest(UnifiedStatsReporter &stats, \ + const RequestType &request) { \ + ++stats.getFrontendCounters().RequestType; \ +} +#include "swift/AST/SILGenTypeIDZone.def" +#undef SWIFT_REQUEST + +} // end namespace swift + +#endif // SWIFT_SILGEN_REQUESTS_H diff --git a/include/swift/AST/SILGenTypeIDZone.def b/include/swift/AST/SILGenTypeIDZone.def new file mode 100644 index 0000000000000..b80a7e49664e7 --- /dev/null +++ b/include/swift/AST/SILGenTypeIDZone.def @@ -0,0 +1,19 @@ +//===--- SILGenTypeIDZone.def -----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This definition file describes the requests in SILGen's zone. +// +//===----------------------------------------------------------------------===// + +SWIFT_REQUEST(SILGen, GenerateSILRequest, + std::unique_ptr(SILGenDescriptor), + Uncached, NoLocationInfo) diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index 736f141f0bd03..1ce85e82224f5 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -53,11 +53,22 @@ class SILOptions { /// Controls whether the SIL ARC optimizations are run. bool EnableARCOptimizations = true; + /// Controls whether specific OSSA optimizations are run. For benchmarking + /// purposes. + bool EnableOSSAOptimizations = true; + + /// Controls whether to turn on speculative devirtualization. + /// It is turned off by default. + bool EnableSpeculativeDevirtualization = false; + /// Should we run any SIL performance optimizations /// /// Useful when you want to enable -O LLVM opts but not -O SIL opts. bool DisableSILPerfOptimizations = false; + /// Controls whether cross module optimization is enabled. + bool CrossModuleOptimization = false; + /// Controls whether or not paranoid verification checks are run. bool VerifyAll = false; @@ -67,6 +78,10 @@ class SILOptions { /// Whether to dump verbose SIL with scope and location information. bool EmitVerboseSIL = false; + /// Should we sort SIL functions, vtables, witness tables, and global + /// variables by name when we print it out. This eases diffing of SIL files. + bool EmitSortedSIL = false; + /// Whether to stop the optimization pipeline after serializing SIL. bool StopOptimizationAfterSerialization = false; @@ -144,7 +159,7 @@ class SILOptions { /// Should the default pass pipelines strip ownership during the diagnostic /// pipeline or after serialization. - bool StripOwnershipAfterSerialization = false; + bool StripOwnershipAfterSerialization = true; /// The name of the file to which the backend should save YAML optimization /// records. diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def new file mode 100644 index 0000000000000..e810eb99c94e4 --- /dev/null +++ b/include/swift/AST/SemanticAttrs.def @@ -0,0 +1,87 @@ +//===--- Semantics.def - Semantics Attribute Definitions -------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This macro is used to map the global definition name of a semantic +/// attribute to its raw value. +/// NAME: the global name used in the compiler +/// C_STR: the raw value used in swift +/// +/// SEMANTICS_ATTR(NAME, C_STR) +/// +//===----------------------------------------------------------------------===// + +#ifndef SEMANTICS_ATTR +#error SEMANTICS_ATTR not defined. +#endif + +SEMANTICS_ATTR(STRING_EQUALS, "string.equals") +SEMANTICS_ATTR(STRING_MAKE_UTF8, "string.makeUTF8") +SEMANTICS_ATTR(STRING_ESCAPE_PERCENT_GET, "string.escapePercent.get") +SEMANTICS_ATTR(STRING_CONCAT, "string.concat") +SEMANTICS_ATTR(STRING_APPEND, "string.append") +SEMANTICS_ATTR(STRING_INIT_EMPTY, "string.init_empty") +SEMANTICS_ATTR(STRING_PLUS_EQUALS, "string.plusequals") +SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE, "findStringSwitchCase") +SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE_WITH_CACHE, "findStringSwitchCaseWithCache") + +SEMANTICS_ATTR(BINARY_INTEGER_DESCRIPTION, "binaryInteger.description") + +SEMANTICS_ATTR(SWIFT_CONCURRENT_ASYNC, "swift.concurrent.async") +SEMANTICS_ATTR(SWIFT_CONCURRENT_SAFE, "swift.concurrent.safe") +SEMANTICS_ATTR(SWIFT_CONCURRENT, "swift.concurrent") + +SEMANTICS_ATTR(ARRAY_APPEND_CONTENTS_OF, "array.append_contentsOf") +SEMANTICS_ATTR(ARRAY_APPEND_ELEMENT, "array.append_element") +SEMANTICS_ATTR(ARRAY_CHECK_INDEX, "array.check_index") +SEMANTICS_ATTR(ARRAY_CHECK_SUBSCRIPT, "array.check_subscript") +SEMANTICS_ATTR(ARRAY_GET_CAPACITY, "array.get_capacity") +SEMANTICS_ATTR(ARRAY_GET_COUNT, "array.get_count") +SEMANTICS_ATTR(ARRAY_GET_ELEMENT, "array.get_element") +SEMANTICS_ATTR(ARRAY_GET_ELEMENT_ADDRESS, "array.get_element_address") +SEMANTICS_ATTR(ARRAY_INIT, "array.init") +SEMANTICS_ATTR(ARRAY_INIT_EMPTY, "array.init.empty") +SEMANTICS_ATTR(ARRAY_MAKE_MUTABLE, "array.make_mutable") +SEMANTICS_ATTR(ARRAY_MUTATE_UNKNOWN, "array.mutate_unknown") +SEMANTICS_ATTR(ARRAY_PROPS_IS_NATIVE_TYPE_CHECKED, "array.props.isNativeTypeChecked") +SEMANTICS_ATTR(ARRAY_RESERVE_CAPACITY_FOR_APPEND, "array.reserve_capacity_for_append") +SEMANTICS_ATTR(ARRAY_UNINITIALIZED, "array.uninitialized") +SEMANTICS_ATTR(ARRAY_WITH_UNSAFE_MUTABLE_BUFFER_POINTER, "array.withUnsafeMutableBufferPointer") +SEMANTICS_ATTR(ARRAY_COUNT, "array.count") +SEMANTICS_ATTR(ARRAY_DEALLOC_UNINITIALIZED, "array.dealloc_uninitialized") +SEMANTICS_ATTR(ARRAY_UNINITIALIZED_INTRINSIC, "array.uninitialized_intrinsic") + +SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER, "optimize.sil.specialize.generic.never") +SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER, + "optimize.sil.specialize.generic.partial.never") +SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER, + "optimize.sil.specialize.generic.size.never") + +SEMANTICS_ATTR(OSLOG_INTERPOLATION_INIT, "oslog.interpolation.init") +SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_INTERPOLATION, "oslog.message.init_interpolation") +SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_STRING_LITERAL, "oslog.message.init_stringliteral") + +SEMANTICS_ATTR(TYPE_CHECKER_OPEN_EXISTENTIAL, "typechecker._openExistential(_:do:)") +SEMANTICS_ATTR(TYPE_CHECKER_TYPE, "typechecker.type(of:)") +SEMANTICS_ATTR(TYPE_CHECKER_WITHOUT_ACTUALLY_ESCAPING, "typechecker.withoutActuallyEscaping(_:do:)") + +SEMANTICS_ATTR(AVAILABILITY_OSVERSION, "availability.osversion") + +SEMANTICS_ATTR(CONSTANT_EVALUABLE, "constant_evaluable") +SEMANTICS_ATTR(EXIT, "exit") +SEMANTICS_ATTR(FASTPATH, "fastpath") +SEMANTICS_ATTR(SLOWPATH, "slowpath") +SEMANTICS_ATTR(PROGRAMTERMINATION_POINT, "programtermination_point") +SEMANTICS_ATTR(CONVERT_TO_OBJECTIVE_C, "convertToObjectiveC") + +#undef SEMANTICS_ATTR + diff --git a/include/swift/AST/SemanticAttrs.h b/include/swift/AST/SemanticAttrs.h new file mode 100644 index 0000000000000..b6b9926ca8165 --- /dev/null +++ b/include/swift/AST/SemanticAttrs.h @@ -0,0 +1,31 @@ +//===--- Semantics.h - Semantics Attribute Definitions -------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation of the matching definition file. +/// This file holds all semantics attributes as constant string literals. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SEMANTICS_H +#define SWIFT_SEMANTICS_H + +#include "llvm/ADT/StringRef.h" + +namespace swift { +namespace semantics { +#define SEMANTICS_ATTR(NAME, C_STR) constexpr static const StringLiteral NAME = C_STR; +#include "SemanticAttrs.def" +} +} + +#endif diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index b459057e4901a..30b79a78996f5 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -14,6 +14,7 @@ #define SWIFT_AST_SOURCEFILE_H #include "swift/AST/FileUnit.h" +#include "swift/Basic/Debug.h" namespace swift { @@ -124,15 +125,42 @@ class SourceFile final : public FileUnit { /// been validated. llvm::SetVector UnvalidatedDeclsWithOpaqueReturnTypes; + /// The list of top-level declarations in the source file. + std::vector Decls; + friend ASTContext; friend Impl; + public: - /// The list of top-level declarations in the source file. - std::vector Decls; + /// Appends the given declaration to the end of the top-level decls list. + void addTopLevelDecl(Decl *d) { + Decls.push_back(d); + } + + /// Prepends a declaration to the top-level decls list. + /// + /// FIXME: This entrypoint exists to support LLDB. Calls to this function are + /// always a mistake, and additional uses should not be added. + /// + /// See rdar://58355191 + void prependTopLevelDecl(Decl *d) { + Decls.insert(Decls.begin(), d); + } + + /// Retrieves an immutable view of the list of top-level decls in this file. + ArrayRef getTopLevelDecls() const { + return Decls; + } + + /// Truncates the list of top-level decls so it contains \c count elements. + void truncateTopLevelDecls(unsigned count) { + assert(count <= Decls.size() && "Can only truncate top-level decls!"); + Decls.resize(count); + } /// A cache of syntax nodes that can be reused when creating the syntax tree /// for this file. - SyntaxParsingCache *SyntaxParsingCache = nullptr; + swift::SyntaxParsingCache *SyntaxParsingCache = nullptr; /// The list of local type declarations in the source file. llvm::SetVector LocalTypeDecls; @@ -323,7 +351,7 @@ class SourceFile final : public FileUnit { /// Retrieve the scope that describes this source file. ASTScope &getScope(); - void dump() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os) const; /// Pretty-print the contents of this source file. @@ -395,17 +423,15 @@ class SourceFile final : public FileUnit { return InterfaceHash.hasValue(); } - void recordInterfaceToken(StringRef token) { - assert(!token.empty()); - InterfaceHash->update(token); - // Add null byte to separate tokens. - uint8_t a[1] = {0}; - InterfaceHash->update(a); + NullablePtr getInterfaceHashPtr() { + return InterfaceHash ? InterfaceHash.getPointer() : nullptr; } - void getInterfaceHash(llvm::SmallString<32> &str) { + void getInterfaceHash(llvm::SmallString<32> &str) const { + // Copy to preserve idempotence. + llvm::MD5 md5 = *InterfaceHash; llvm::MD5::MD5Result result; - InterfaceHash->final(result); + md5.final(result); llvm::MD5::stringifyResult(result, str); } @@ -433,6 +459,9 @@ class SourceFile final : public FileUnit { OpaqueTypeDecl *lookupOpaqueResultType(StringRef MangledName) override; + /// Do not call when inside an inactive clause (\c + /// InInactiveClauseEnvironment)) because it will later on result in a lookup + /// to something that won't be in the ASTScope tree. void addUnvalidatedDeclWithOpaqueResultType(ValueDecl *vd) { UnvalidatedDeclsWithOpaqueReturnTypes.insert(vd); } @@ -478,6 +507,11 @@ inline bool ModuleDecl::EntryPointInfoTy::markDiagnosedMainClassWithScript() { return !res; } +inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) { + assert(SF && "Cannot display null source file!"); + + out << "source_file " << '\"' << SF->getFilename() << '\"'; +} } // end namespace swift #endif diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 049cb549accb0..6b6f7164fc22a 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -23,6 +23,7 @@ #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/IfConfigClause.h" #include "swift/AST/TypeAlignments.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/NullablePtr.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/TrailingObjects.h" @@ -134,9 +135,7 @@ class alignas(8) Stmt { Stmt *walk(ASTWalker &walker); Stmt *walk(ASTWalker &&walker) { return walk(walker); } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, const ASTContext *Ctx = nullptr, unsigned Indent = 0) const; // Only allow allocation of Exprs using the allocator in ASTContext @@ -173,10 +172,13 @@ class BraceStmt final : public Stmt, SourceRange getSourceRange() const { return SourceRange(LBLoc, RBLoc); } + bool empty() const { return getNumElements() == 0; } unsigned getNumElements() const { return Bits.BraceStmt.NumElements; } - ASTNode getElement(unsigned i) const { return getElements()[i]; } - void setElement(unsigned i, ASTNode node) { getElements()[i] = node; } + ASTNode getFirstElement() const { return getElements().front(); } + ASTNode getLastElement() const { return getElements().back(); } + + void setFirstElement(ASTNode node) { getElements().front() = node; } /// The elements contained within the BraceStmt. MutableArrayRef getElements() { @@ -327,11 +329,20 @@ class alignas(8) PoundAvailableInfo final : /// The version range when this query will return true. This value is /// filled in by Sema. VersionRange AvailableRange; - + + /// For zippered builds, this is the version range for the target variant + /// that must hold for the query to return true. For example, when + /// compiling with target x86_64-macosx10.15 and target-variant + /// x86_64-ios13.0 a query of #available(macOS 10.22, iOS 20.0, *) will + /// have a variant range of [20.0, +inf). + /// This is filled in by Sema. + VersionRange VariantAvailableRange; + PoundAvailableInfo(SourceLoc PoundLoc, ArrayRef queries, SourceLoc RParenLoc) : PoundLoc(PoundLoc), RParenLoc(RParenLoc), NumQueries(queries.size()), - AvailableRange(VersionRange::empty()) { + AvailableRange(VersionRange::empty()), + VariantAvailableRange(VersionRange::empty()) { std::uninitialized_copy(queries.begin(), queries.end(), getTrailingObjects()); } @@ -354,6 +365,13 @@ class alignas(8) PoundAvailableInfo final : const VersionRange &getAvailableRange() const { return AvailableRange; } void setAvailableRange(const VersionRange &Range) { AvailableRange = Range; } + + const VersionRange &getVariantAvailableRange() const { + return VariantAvailableRange; + } + void setVariantAvailableRange(const VersionRange &Range) { + VariantAvailableRange = Range; + } }; @@ -805,9 +823,7 @@ class ForEachStmt : public LabeledStmt { BraceStmt *Body; // Set by Sema: - Optional sequenceConformance; - ConcreteDeclRef makeIterator; - ConcreteDeclRef iteratorNext; + ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef(); VarDecl *iteratorVar = nullptr; Expr *iteratorVarRef = nullptr; OpaqueValueExpr *elementExpr = nullptr; @@ -836,16 +852,10 @@ class ForEachStmt : public LabeledStmt { void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; } Expr *getConvertElementExpr() const { return convertElementExpr; } - void setMakeIterator(ConcreteDeclRef declRef) { makeIterator = declRef; } - ConcreteDeclRef getMakeIterator() const { return makeIterator; } - - void setIteratorNext(ConcreteDeclRef declRef) { iteratorNext = declRef; } - ConcreteDeclRef getIteratorNext() const { return iteratorNext; } - - void setSequenceConformance(Optional conformance) { + void setSequenceConformance(ProtocolConformanceRef conformance) { sequenceConformance = conformance; } - Optional getSequenceConformance() const { + ProtocolConformanceRef getSequenceConformance() const { return sequenceConformance; } diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index e01edc3409a08..deb897f5053bb 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -20,6 +20,8 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/ProtocolConformanceRef.h" #include "swift/AST/Type.h" +#include "swift/AST/TypeExpansionContext.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Optional.h" @@ -50,11 +52,10 @@ enum class CombineSubstitutionMaps { /// any entity that can reference type parameters, e.g., types (via /// Type::subst()) and conformances (via ProtocolConformanceRef::subst()). /// -/// SubstitutionMaps are constructed by calling the getSubstitutionMap() method -/// on a GenericSignature or (equivalently) by calling one of the static -/// \c SubstitutionMap::get() methods. However, most substitution maps are +/// SubstitutionMaps are constructed by calling the an overload of the static +/// method \c SubstitutionMap::get(). However, most substitution maps are /// computed using higher-level entry points such as -/// TypeBase::getMemberSubstitutionMap(). +/// TypeBase::getContextSubstitutionMap(). /// /// Substitution maps are ASTContext-allocated and are uniqued on construction, /// so they can be used as fields in AST nodes. @@ -128,8 +129,8 @@ class SubstitutionMap { ArrayRef getConformances() const; /// Look up a conformance for the given type to the given protocol. - Optional - lookupConformance(CanType type, ProtocolDecl *proto) const; + ProtocolConformanceRef lookupConformance(CanType type, + ProtocolDecl *proto) const; /// Whether the substitution map is empty. bool empty() const; @@ -175,7 +176,13 @@ class SubstitutionMap { SubstitutionMap subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options=None) const; - + + /// Apply type expansion lowering to all types in the substitution map. Opaque + /// archetypes will be lowered to their underlying types if the type expansion + /// context allows. + SubstitutionMap mapIntoTypeExpansionContext( + TypeExpansionContext context) const; + /// Create a substitution map for a protocol conformance. static SubstitutionMap getProtocolSubstitutions(ProtocolDecl *protocol, @@ -234,7 +241,7 @@ class SubstitutionMap { void dump(llvm::raw_ostream &out, DumpStyle style = DumpStyle::Full, unsigned indent = 0) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger"); + SWIFT_DEBUG_DUMP; /// Profile the substitution map, for use with LLVM's FoldingSet. void profile(llvm::FoldingSetNodeID &id) const; @@ -290,11 +297,10 @@ class LookUpConformanceInSubstitutionMap { public: explicit LookUpConformanceInSubstitutionMap(SubstitutionMap Subs) : Subs(Subs) {} - - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; } // end namespace swift diff --git a/include/swift/AST/SyntaxASTMap.h b/include/swift/AST/SyntaxASTMap.h index 7de315c4bb5fb..acf39714c4677 100644 --- a/include/swift/AST/SyntaxASTMap.h +++ b/include/swift/AST/SyntaxASTMap.h @@ -18,6 +18,7 @@ #define SWIFT_AST_SYNTAXASTMAP_H #include "swift/AST/ASTNode.h" +#include "swift/Basic/Debug.h" #include "swift/Syntax/Syntax.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" @@ -49,7 +50,7 @@ class SyntaxASTMap final { void clearSyntaxMap(); /// Dump the entire syntax node -> semantic node map for debugging purposes. - void dumpSyntaxMap() const; + SWIFT_DEBUG_DUMPER(dumpSyntaxMap()); }; } // end namespace swift diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 9e0422b9eb5d0..d9dce9efa09f0 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -21,6 +21,7 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/ArrayRefView.h" #include "swift/AST/LayoutConstraint.h" @@ -93,7 +94,7 @@ struct QueryTypeSubstitutionMapOrIdentity { using GenericFunction = auto(CanType dependentType, Type conformingReplacementType, ProtocolDecl *conformedProtocol) - -> Optional; + -> ProtocolConformanceRef; using LookupConformanceFn = llvm::function_ref; /// Functor class suitable for use as a \c LookupConformanceFn to look up a @@ -103,11 +104,10 @@ class LookUpConformanceInModule { public: explicit LookUpConformanceInModule(ModuleDecl *M) : M(M) {} - - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; /// Functor class suitable for use as a \c LookupConformanceFn that provides @@ -115,10 +115,9 @@ class LookUpConformanceInModule { /// type is an opaque generic type. class MakeAbstractConformanceForGenericType { public: - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; /// Functor class suitable for use as a \c LookupConformanceFn that fetches @@ -130,11 +129,10 @@ class LookUpConformanceInSignature { : Sig(Sig) { assert(Sig && "Cannot lookup conformance in null signature!"); } - - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; /// Flags that can be passed when substituting into a type. @@ -317,7 +315,7 @@ class Type { bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const; - void dump() const; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions()) const; @@ -373,6 +371,9 @@ class Type { void operator!=(Type T) const = delete; }; +/// Extract the source location from a given type. +SourceLoc extractNearestSourceLoc(Type ty); + /// CanType - This is a Type that is statically known to be canonical. To get /// one of these, use Type->getCanonicalType(). Since all CanType's can be used /// as 'Type' (they just don't have sugar) we derive from Type. diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index 149b8ae24598d..c67183dd06eec 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -30,6 +30,7 @@ namespace swift { class ArchetypeType; class AssociatedTypeDecl; class ASTContext; + class AttributeBase; class BraceStmt; class Decl; class DeclContext; @@ -52,6 +53,7 @@ namespace swift { class ValueDecl; /// We frequently use three tag bits on all of these types. + constexpr size_t AttrAlignInBits = 3; constexpr size_t DeclAlignInBits = 3; constexpr size_t DeclContextAlignInBits = 3; constexpr size_t ExprAlignInBits = 3; @@ -117,6 +119,8 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::Pattern, LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits) + static_assert(alignof(void*) >= 2, "pointer alignment is too small"); #endif diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 1b72bdf8502a9..4e0dc5e21bf63 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -16,10 +16,12 @@ #ifndef SWIFT_TYPE_CHECK_REQUESTS_H #define SWIFT_TYPE_CHECK_REQUESTS_H +#include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" +#include "swift/AST/Pattern.h" #include "swift/AST/SimpleRequest.h" #include "swift/AST/TypeResolutionStage.h" #include "swift/Basic/AnyValue.h" @@ -33,14 +35,19 @@ namespace swift { class AbstractStorageDecl; class AccessorDecl; enum class AccessorKind; +class ContextualPattern; +class DefaultArgumentExpr; class GenericParamList; class PrecedenceGroupDecl; struct PropertyWrapperBackingPropertyInfo; struct PropertyWrapperMutability; class RequirementRepr; class SpecializeAttr; +class TrailingWhereClause; class TypeAliasDecl; struct TypeLoc; +class Witness; +struct TypeWitnessAndDecl; class ValueDecl; enum class OpaqueReadOwnership: uint8_t; class StorageImplInfo; @@ -331,7 +338,8 @@ class RequirementSignatureRequest : friend SimpleRequest; // Evaluation. - llvm::Expected> evaluate(Evaluator &evaluator, ProtocolDecl *proto) const; + llvm::Expected> evaluate(Evaluator &evaluator, + ProtocolDecl *proto) const; public: // Separate caching. @@ -367,9 +375,12 @@ struct WhereClauseOwner { /// The source of the where clause, which can be a generic parameter list /// or a declaration that can have a where clause. - llvm::PointerUnion source; + llvm::PointerUnion + source; - WhereClauseOwner(Decl *decl); + WhereClauseOwner(GenericContext *genCtx); + WhereClauseOwner(AssociatedTypeDecl *atd); WhereClauseOwner(DeclContext *dc, GenericParamList *genericParams) : dc(dc), source(genericParams) {} @@ -377,16 +388,18 @@ struct WhereClauseOwner { WhereClauseOwner(DeclContext *dc, SpecializeAttr *attr) : dc(dc), source(attr) {} + WhereClauseOwner(DeclContext *dc, DifferentiableAttr *attr) + : dc(dc), source(attr) {} + SourceLoc getLoc() const; friend hash_code hash_value(const WhereClauseOwner &owner) { - return llvm::hash_combine(owner.dc, owner.source.getOpaqueValue()); + return llvm::hash_value(owner.source.getOpaqueValue()); } friend bool operator==(const WhereClauseOwner &lhs, const WhereClauseOwner &rhs) { - return lhs.dc == rhs.dc && - lhs.source.getOpaqueValue() == rhs.source.getOpaqueValue(); + return lhs.source.getOpaqueValue() == rhs.source.getOpaqueValue(); } friend bool operator!=(const WhereClauseOwner &lhs, @@ -1385,6 +1398,664 @@ class ResultTypeRequest void cacheResult(Type value) const; }; +class PatternBindingEntryRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, PatternBindingDecl *PBD, unsigned i) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(const PatternBindingEntry *value) const; +}; + +class NamingPatternRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + VarDecl *VD) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(NamedPattern *P) const; +}; + +class InterfaceTypeRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ValueDecl *decl) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Type value) const; +}; + +struct PrecedenceGroupDescriptor { + enum PathDirection : bool { + LowerThan = false, + HigherThan = true, + }; + DeclContext *dc; + Identifier ident; + SourceLoc nameLoc; + // Exists for diagnostics. Does not contribute to the descriptor otherwise. + Optional pathDirection; + + SourceLoc getLoc() const; + + friend llvm::hash_code hash_value(const PrecedenceGroupDescriptor &owner) { + return llvm::hash_combine(owner.dc, + owner.ident.getAsOpaquePointer(), + owner.nameLoc.getOpaquePointerValue()); + } + + friend bool operator==(const PrecedenceGroupDescriptor &lhs, + const PrecedenceGroupDescriptor &rhs) { + return lhs.dc == rhs.dc && + lhs.ident == rhs.ident && + lhs.nameLoc == rhs.nameLoc; + } + + friend bool operator!=(const PrecedenceGroupDescriptor &lhs, + const PrecedenceGroupDescriptor &rhs) { + return !(lhs == rhs); + } +}; + +void simple_display(llvm::raw_ostream &out, const PrecedenceGroupDescriptor &d); + +class LookupPrecedenceGroupRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, PrecedenceGroupDescriptor descriptor) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Source location + SourceLoc getNearestLoc() const; + + // Separate caching. + bool isCached() const { return true; } +}; + +/// Computes whether all of the stored properties in a nominal type have initial +/// values. +class AreAllStoredPropertiesDefaultInitableRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Computes whether this type has a user-defined designated initializer. This +/// does not include a synthesized designated initializer used to satisfy a +/// conformance. +class HasUserDefinedDesignatedInitRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Checks whether this type has a synthesized memberwise initializer. +class HasMemberwiseInitRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, StructDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Synthesizes a memberwise initializer for a given type. +class SynthesizeMemberwiseInitRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Checks whether this type has a synthesized zero parameter default +/// initializer. +class HasDefaultInitRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Synthesizes a default initializer for a given type. +class SynthesizeDefaultInitRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +class CompareDeclSpecializationRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, DeclContext *DC, + ValueDecl *VD1, ValueDecl *VD2, + bool dynamic) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +/// Checks whether this declaration inherits its superclass' designated and +/// convenience initializers. +class InheritsSuperclassInitializersRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ClassDecl *decl) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(bool value) const; +}; + +// The actions this request takes are all huge layering violations. +// +// Please do not add any more. +enum class ImplicitMemberAction : uint8_t { + ResolveImplicitInit, + ResolveCodingKeys, + ResolveEncodable, + ResolveDecodable, +}; + +class ResolveImplicitMemberRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, NominalTypeDecl *NTD, + ImplicitMemberAction action) const; + +public: + // Separate caching. + bool isCached() const { return true; } +}; + +class TypeWitnessRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, NormalProtocolConformance *conformance, + AssociatedTypeDecl *ATD) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(TypeWitnessAndDecl value) const; +}; + +class ValueWitnessRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + NormalProtocolConformance *conformance, + ValueDecl *VD) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Witness value) const; +}; + +enum class FunctionBuilderBodyPreCheck : uint8_t { + /// There were no problems pre-checking the closure. + Okay, + + /// There was an error pre-checking the closure. + Error, + + /// The closure has a return statement. + HasReturnStmt, +}; + +class PreCheckFunctionBuilderRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, AnyFunctionRef fn) const; + +public: + // Separate caching. + bool isCached() const { return true; } +}; + +/// Computes whether a class has a circular reference in its inheritance +/// hierarchy. +class HasCircularInheritanceRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ClassDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + +/// Computes whether a protocol has a circular reference in its list of +/// inherited protocols. +class HasCircularInheritedProtocolsRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ProtocolDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + +/// Computes whether an enum's raw value has a circular reference. +class HasCircularRawValueRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, EnumDecl *decl) const; + +public: + // Cycle handling. + void diagnoseCycle(DiagnosticEngine &diags) const; + void noteCycleStep(DiagnosticEngine &diags) const; + + // Cached. + bool isCached() const { return true; } +}; + +/// Computes an initializer context for a parameter with a default argument. +class DefaultArgumentInitContextRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + ParamDecl *param) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Initializer *init) const; +}; + +/// Computes the fully type-checked default argument expression for a given +/// parameter. +class DefaultArgumentExprRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, ParamDecl *param) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Expr *expr) const; +}; + +/// Computes the fully type-checked caller-side default argument within the +/// context of the call site that it will be inserted into. +class CallerSideDefaultArgExprRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + DefaultArgumentExpr *defaultExpr) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(Expr *expr) const; +}; + +/// Computes whether this is a type that supports being called through the +/// implementation of a \c callAsFunction method. +class IsCallableNominalTypeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, CanType ty, + DeclContext *dc) const; + +public: + // Cached. + bool isCached() const { return true; } +}; + +class DynamicallyReplacedDeclRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + ValueDecl *VD) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +class TypeCheckSourceFileRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + SourceFile *SF, unsigned StartElem) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(bool result) const; +}; + +/// Computes whether the specified type or a super-class/super-protocol has the +/// @dynamicMemberLookup attribute on it. +class HasDynamicMemberLookupAttributeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, CanType ty) const; + +public: + bool isCached() const { + // Don't cache types containing type variables, as they must not outlive + // the constraint system that created them. + auto ty = std::get<0>(getStorage()); + return !ty->hasTypeVariable(); + } +}; + +/// Determines the type of a given pattern. +/// +/// Note that this returns the "raw" pattern type, which can involve +/// unresolved types and unbound generic types where type inference is +/// allowed. +class PatternTypeRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate( + Evaluator &evaluator, ContextualPattern pattern) const; + +public: + bool isCached() const { return true; } + + SourceLoc getNearestLoc() const { + return std::get<0>(getStorage()).getPattern()->getLoc(); + } +}; + +/// Type-checks a `@differentiable` attribute and returns the resolved parameter +/// indices on success. On failure, emits diagnostics and returns `nullptr`. +/// +/// Currently, this request resolves other `@differentiable` attribute +/// components but mutates them in place: +/// - `JVPFunction` +/// - `VJPFunction` +/// - `DerivativeGenericSignature` +class DifferentiableAttributeTypeCheckRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected evaluate(Evaluator &evaluator, + DifferentiableAttr *attr) const; + +public: + // Separate caching. + bool isCached() const { return true; } + Optional getCachedResult() const; + void cacheResult(IndexSubset *value) const; +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> @@ -1406,6 +2077,8 @@ AnyValue::Holder::equals(const HolderBase &other) const { void simple_display(llvm::raw_ostream &out, Type value); void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR); +void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action); +void simple_display(llvm::raw_ostream &out, FunctionBuilderBodyPreCheck pck); #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 16b81444e39aa..ca3ceb160b567 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -27,13 +27,28 @@ SWIFT_REQUEST(TypeChecker, AttachedPropertyWrapperTypeRequest, SWIFT_REQUEST(TypeChecker, AttachedPropertyWrappersRequest, llvm::TinyPtrVector(VarDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, CallerSideDefaultArgExprRequest, + Expr *(DefaultArgumentExpr *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ClassAncestryFlagsRequest, AncestryFlags(ClassDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest, + bool (DeclContext *, ValueDecl *, ValueDecl *, bool), Cached, + NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest, + Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, + Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, Type(AssociatedTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultTypeRequest, Type(KnownProtocolKind, const DeclContext *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DifferentiableAttributeTypeCheckRequest, + IndexSubset *(DifferentiableAttr *), + SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DynamicallyReplacedDeclRequest, + ValueDecl *(ValueDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EmittedMembersRequest, DeclRange(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, @@ -55,6 +70,14 @@ SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *), SWIFT_REQUEST(NameLookup, GenericSignatureRequest, GenericSignature (GenericContext *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularInheritanceRequest, + bool(ClassDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularInheritedProtocolsRequest, + bool(ProtocolDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasCircularRawValueRequest, + bool(EnumDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasDynamicMemberLookupAttributeRequest, + bool(CanType), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest, GenericSignature (ModuleDecl *, GenericSignatureImpl *, GenericParamList *, @@ -65,10 +88,16 @@ SWIFT_REQUEST(TypeChecker, InheritedTypeRequest, Type(llvm::PointerUnion, unsigned, TypeResolutionStage), SeparatelyCached, HasNearestLocation) +SWIFT_REQUEST(TypeChecker, InheritsSuperclassInitializersRequest, + bool(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InitKindRequest, CtorInitializerKind(ConstructorDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InterfaceTypeRequest, + Type(ValueDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsAccessorTransparentRequest, bool(AccessorDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, IsCallableNominalTypeRequest, + bool(CanType, DeclContext *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsDynamicRequest, bool(ValueDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, IsFinalRequest, bool(ValueDecl *), SeparatelyCached, @@ -83,8 +112,13 @@ SWIFT_REQUEST(TypeChecker, IsSetterMutatingRequest, bool(AbstractStorageDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, LazyStoragePropertyRequest, VarDecl *(VarDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, LookupPrecedenceGroupRequest, + PrecedenceGroupDecl *(DeclContext *, Identifier, SourceLoc), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, MangleLocalTypeDeclRequest, std::string(const TypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, NamingPatternRequest, + NamedPattern *(VarDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, OpaqueReadOwnershipRequest, OpaqueReadOwnership(AbstractStorageDecl *), SeparatelyCached, NoLocationInfo) @@ -97,6 +131,9 @@ SWIFT_REQUEST(TypeChecker, OperatorPrecedenceGroupRequest, SWIFT_REQUEST(TypeChecker, OverriddenDeclsRequest, llvm::TinyPtrVector(ValueDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PatternBindingEntryRequest, + const PatternBindingEntry *(PatternBindingDecl *, unsigned), + SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PropertyWrapperBackingPropertyInfoRequest, PropertyWrapperBackingPropertyInfo(VarDecl *), Cached, NoLocationInfo) @@ -155,3 +192,33 @@ SWIFT_REQUEST(TypeChecker, ParamSpecifierRequest, ParamDecl::Specifier(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResultTypeRequest, Type(ValueDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, AreAllStoredPropertiesDefaultInitableRequest, + bool(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, + bool(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, + bool(StructDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, + FunctionBuilderClosurePreCheck(AnyFunctionRef), + Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, + bool(NominalTypeDecl *, ImplicitMemberAction), Uncached, + NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest, + ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest, + bool(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SynthesizeDefaultInitRequest, + ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeCheckSourceFileRequest, + bool(SouceFile *, unsigned), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeWitnessRequest, + TypeWitnessAndDecl(NormalProtocolConformance *, + AssociatedTypeDecl *), + SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, ValueWitnessRequest, + Witness(NormalProtocolConformance *, ValueDecl *), + SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PatternTypeRequest, + Type(ContextualPattern), + Cached, HasNearestLocation) diff --git a/include/swift/AST/TypeExpansionContext.h b/include/swift/AST/TypeExpansionContext.h new file mode 100644 index 0000000000000..761fa53217bd5 --- /dev/null +++ b/include/swift/AST/TypeExpansionContext.h @@ -0,0 +1,145 @@ +//===--- TypeExpansionContext.h - Swift Type Expansion Context --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the TypeExpansionContext class. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_TYPEEXPANSIONCONTEXT_H +#define SWIFT_TYPEEXPANSIONCONTEXT_H + +#include "swift/AST/ResilienceExpansion.h" +#include "llvm/ADT/DenseMap.h" + +namespace swift { + class DeclContext; + class SILFunction; + +/// Describes the context in which SIL types should eventually be expanded. +/// Required for lowering resilient types and deciding whether to look through +/// opaque result types to their underlying type. +class TypeExpansionContext { + ResilienceExpansion expansion; + // The context (module, function, ...) we are expanding the type in. + const DeclContext *inContext; + // Is the context in which we are expanding in the whole module. + bool isContextWholeModule; + + // The minimal expansion. + TypeExpansionContext() { + inContext = nullptr; + expansion = ResilienceExpansion::Minimal; + isContextWholeModule = false; + } + +public: + + // Infer the expansion for the SIL function. + TypeExpansionContext(const SILFunction &f); + + TypeExpansionContext(ResilienceExpansion expansion, + const DeclContext *inContext, bool isWholeModuleContext) + : expansion(expansion), inContext(inContext), + isContextWholeModule(isWholeModuleContext) {} + + ResilienceExpansion getResilienceExpansion() const { return expansion; } + + const DeclContext *getContext() const { return inContext; } + + bool isWholeModuleContext() const { return isContextWholeModule; } + + bool shouldLookThroughOpaqueTypeArchetypes() const { + return inContext != nullptr; + } + + bool isMinimal() const { return *this == TypeExpansionContext(); } + + static TypeExpansionContext minimal() { + return TypeExpansionContext(); + } + + static TypeExpansionContext maximal(const DeclContext *inContext, + bool isContextWholeModule) { + return TypeExpansionContext(ResilienceExpansion::Maximal, inContext, + isContextWholeModule); + } + + static TypeExpansionContext maximalResilienceExpansionOnly() { + return maximal(nullptr, false); + } + + static TypeExpansionContext + noOpaqueTypeArchetypesSubstitution(ResilienceExpansion expansion) { + return TypeExpansionContext(expansion, nullptr, false); + } + + bool operator==(const TypeExpansionContext &other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + return other.inContext == this->inContext && + other.expansion == this->expansion; + } + + bool operator<(const TypeExpansionContext other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + if (this->expansion == other.expansion) + return this->inContext < other.inContext; + return this->expansion < other.expansion; + } + + bool operator>(const TypeExpansionContext other) const { + assert(other.inContext != this->inContext || + other.isContextWholeModule == this->isContextWholeModule); + if (this->expansion == other.expansion) + return this->inContext > other.inContext; + return this->expansion > other.expansion; + } + + uintptr_t getHashKey() const { + uintptr_t key = (uintptr_t)inContext | (uintptr_t)expansion; + return key; + } +}; + +} // namespace swift + +namespace llvm { +template <> struct DenseMapInfo { + using TypeExpansionContext = swift::TypeExpansionContext; + + static TypeExpansionContext getEmptyKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getEmptyKey()), + false); + } + static TypeExpansionContext getTombstoneKey() { + return TypeExpansionContext( + swift::ResilienceExpansion::Minimal, + reinterpret_cast( + DenseMapInfo::getTombstoneKey()), + false); + } + + static unsigned getHashValue(TypeExpansionContext val) { + return DenseMapInfo::getHashValue(val.getHashKey()); + } + + static bool isEqual(TypeExpansionContext LHS, TypeExpansionContext RHS) { + return LHS == RHS; + } +}; +} // namespace llvm + +#endif diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index 5f04a5f15aece..05fdd68f26ce8 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -111,6 +111,12 @@ class TypeMatcher { TRIVIAL_CASE(BuiltinVectorType) TRIVIAL_CASE(SILTokenType) + bool visitUnresolvedType(CanUnresolvedType firstType, Type secondType, + Type sugaredFirstType) { + // Unresolved types never match. + return mismatch(firstType.getPointer(), secondType, sugaredFirstType); + } + bool visitTupleType(CanTupleType firstTuple, Type secondType, Type sugaredFirstType) { if (auto secondTuple = secondType->getAs()) { @@ -264,23 +270,6 @@ class TypeMatcher { return mismatch(firstInOut.getPointer(), secondType, sugaredFirstType); } - bool visitUnboundBoundGenericType(CanUnboundGenericType firstUBGT, - Type secondType, Type sugaredFirstType) { - if (auto secondUBGT = secondType->getAs()) { - if (firstUBGT->getDecl() != secondUBGT->getDecl()) - return mismatch(firstUBGT.getPointer(), secondUBGT, sugaredFirstType); - - if (firstUBGT.getParent()) - return this->visit(firstUBGT.getParent(), secondUBGT->getParent(), - sugaredFirstType->castTo() - ->getParent()); - - return true; - } - - return mismatch(firstUBGT.getPointer(), secondType, sugaredFirstType); - } - bool visitBoundGenericType(CanBoundGenericType firstBGT, Type secondType, Type sugaredFirstType) { auto _secondBGT = secondType->getCanonicalType(); @@ -309,8 +298,6 @@ class TypeMatcher { return mismatch(firstBGT.getPointer(), secondType, sugaredFirstType); } - TRIVIAL_CASE(TypeVariableType) - #undef TRIVIAL_CASE }; diff --git a/include/swift/AST/TypeRefinementContext.h b/include/swift/AST/TypeRefinementContext.h index 615587f4a123a..2dca10739444e 100644 --- a/include/swift/AST/TypeRefinementContext.h +++ b/include/swift/AST/TypeRefinementContext.h @@ -21,6 +21,7 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Availability.h" #include "swift/AST/Stmt.h" // for PoundAvailableInfo +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" #include "swift/Basic/STLExtras.h" @@ -255,9 +256,7 @@ class TypeRefinementContext { TypeRefinementContext *findMostRefinedSubContext(SourceLoc Loc, SourceManager &SM); - LLVM_ATTRIBUTE_DEPRECATED( - void dump(SourceManager &SrcMgr) const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMPER(dump(SourceManager &SrcMgr)); void dump(raw_ostream &OS, SourceManager &SrcMgr) const; void print(raw_ostream &OS, SourceManager &SrcMgr, unsigned Indent = 0) const; diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index 97ad28965718b..56e4995aad08c 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -25,6 +25,8 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" +#include "swift/Basic/Debug.h" +#include "swift/Basic/Located.h" #include "swift/Basic/InlineBitfield.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" @@ -160,9 +162,7 @@ class alignas(8) TypeRepr { void print(raw_ostream &OS, const PrintOptions &Opts = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &Opts) const; - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Clone the given type representation. TypeRepr *clone(const ASTContext &ctx) const; @@ -257,30 +257,30 @@ class IdentTypeRepr : public TypeRepr { }; class ComponentIdentTypeRepr : public IdentTypeRepr { - SourceLoc Loc; + DeclNameLoc Loc; /// Either the identifier or declaration that describes this /// component. /// /// The initial parsed representation is always an identifier, and /// name binding will resolve this to a specific declaration. - llvm::PointerUnion IdOrDecl; + llvm::PointerUnion IdOrDecl; /// The declaration context from which the bound declaration was /// found. only valid if IdOrDecl is a TypeDecl. DeclContext *DC; protected: - ComponentIdentTypeRepr(TypeReprKind K, SourceLoc Loc, Identifier Id) + ComponentIdentTypeRepr(TypeReprKind K, DeclNameLoc Loc, DeclNameRef Id) : IdentTypeRepr(K), Loc(Loc), IdOrDecl(Id), DC(nullptr) {} public: - SourceLoc getIdLoc() const { return Loc; } - Identifier getIdentifier() const; + DeclNameLoc getNameLoc() const { return Loc; } + DeclNameRef getNameRef() const; /// Replace the identifier with a new identifier, e.g., due to typo /// correction. - void overwriteIdentifier(Identifier newId) { IdOrDecl = newId; } + void overwriteNameRef(DeclNameRef newId) { IdOrDecl = newId; } /// Return true if this has been name-bound already. bool isBound() const { return IdOrDecl.is(); } @@ -306,20 +306,20 @@ class ComponentIdentTypeRepr : public IdentTypeRepr { protected: void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const; - SourceLoc getLocImpl() const { return Loc; } + SourceLoc getLocImpl() const { return Loc.getBaseNameLoc(); } friend class TypeRepr; }; /// A simple identifier type like "Int". class SimpleIdentTypeRepr : public ComponentIdentTypeRepr { public: - SimpleIdentTypeRepr(SourceLoc Loc, Identifier Id) + SimpleIdentTypeRepr(DeclNameLoc Loc, DeclNameRef Id) : ComponentIdentTypeRepr(TypeReprKind::SimpleIdent, Loc, Id) {} // SmallVector::emplace_back will never need to call this because // we reserve the right size, but it does try statically. SimpleIdentTypeRepr(const SimpleIdentTypeRepr &repr) - : SimpleIdentTypeRepr(repr.getLoc(), repr.getIdentifier()) { + : SimpleIdentTypeRepr(repr.getNameLoc(), repr.getNameRef()) { llvm_unreachable("should not be called dynamically"); } @@ -329,8 +329,8 @@ class SimpleIdentTypeRepr : public ComponentIdentTypeRepr { static bool classof(const SimpleIdentTypeRepr *T) { return true; } private: - SourceLoc getStartLocImpl() const { return getIdLoc(); } - SourceLoc getEndLocImpl() const { return getIdLoc(); } + SourceLoc getStartLocImpl() const { return getNameLoc().getStartLoc(); } + SourceLoc getEndLocImpl() const { return getNameLoc().getEndLoc(); } friend class TypeRepr; }; @@ -343,7 +343,7 @@ class GenericIdentTypeRepr final : public ComponentIdentTypeRepr, friend TrailingObjects; SourceRange AngleBrackets; - GenericIdentTypeRepr(SourceLoc Loc, Identifier Id, + GenericIdentTypeRepr(DeclNameLoc Loc, DeclNameRef Id, ArrayRef GenericArgs, SourceRange AngleBrackets) : ComponentIdentTypeRepr(TypeReprKind::GenericIdent, Loc, Id), @@ -360,8 +360,8 @@ class GenericIdentTypeRepr final : public ComponentIdentTypeRepr, public: static GenericIdentTypeRepr *create(const ASTContext &C, - SourceLoc Loc, - Identifier Id, + DeclNameLoc Loc, + DeclNameRef Id, ArrayRef GenericArgs, SourceRange AngleBrackets); @@ -381,7 +381,7 @@ class GenericIdentTypeRepr final : public ComponentIdentTypeRepr, static bool classof(const GenericIdentTypeRepr *T) { return true; } private: - SourceLoc getStartLocImpl() const { return getIdLoc(); } + SourceLoc getStartLocImpl() const { return getNameLoc().getStartLoc(); } SourceLoc getEndLocImpl() const { return AngleBrackets.End; } friend class TypeRepr; }; @@ -472,10 +472,12 @@ inline IdentTypeRepr::ComponentRange IdentTypeRepr::getComponentRange() { /// (x: Foo, y: Bar) -> Baz /// \endcode class FunctionTypeRepr : public TypeRepr { - // These three are only used in SIL mode, which is the only time - // we can have polymorphic function values. + // These fields are only used in SIL mode, which is the only time + // we can have polymorphic and substituted function values. GenericParamList *GenericParams; GenericEnvironment *GenericEnv; + bool GenericParamsAreImplied; + ArrayRef GenericSubs; TupleTypeRepr *ArgsTy; TypeRepr *RetTy; @@ -484,16 +486,22 @@ class FunctionTypeRepr : public TypeRepr { public: FunctionTypeRepr(GenericParamList *genericParams, TupleTypeRepr *argsTy, - SourceLoc throwsLoc, SourceLoc arrowLoc, TypeRepr *retTy) + SourceLoc throwsLoc, SourceLoc arrowLoc, TypeRepr *retTy, + bool GenericParamsAreImplied = false, + ArrayRef GenericSubs = {}) : TypeRepr(TypeReprKind::Function), GenericParams(genericParams), GenericEnv(nullptr), + GenericParamsAreImplied(GenericParamsAreImplied), + GenericSubs(GenericSubs), ArgsTy(argsTy), RetTy(retTy), ArrowLoc(arrowLoc), ThrowsLoc(throwsLoc) { } GenericParamList *getGenericParams() const { return GenericParams; } GenericEnvironment *getGenericEnvironment() const { return GenericEnv; } + bool areGenericParamsImplied() const { return GenericParamsAreImplied; } + ArrayRef getSubstitutions() const { return GenericSubs; } void setGenericEnvironment(GenericEnvironment *genericEnv) { assert(GenericEnv == nullptr); @@ -665,9 +673,9 @@ struct TupleTypeReprElement { /// \endcode class TupleTypeRepr final : public TypeRepr, private llvm::TrailingObjects> { + Located> { friend TrailingObjects; - typedef std::pair SourceLocAndIdx; + typedef Located SourceLocAndIdx; SourceRange Parens; @@ -738,12 +746,12 @@ class TupleTypeRepr final : public TypeRepr, SourceLoc getEllipsisLoc() const { return hasEllipsis() ? - getTrailingObjects()[0].first : SourceLoc(); + getTrailingObjects()[0].Loc : SourceLoc(); } unsigned getEllipsisIndex() const { return hasEllipsis() ? - getTrailingObjects()[0].second : + getTrailingObjects()[0].Item : Bits.TupleTypeRepr.NumElements; } @@ -751,8 +759,8 @@ class TupleTypeRepr final : public TypeRepr, if (hasEllipsis()) { Bits.TupleTypeRepr.HasEllipsis = false; getTrailingObjects()[0] = { - SourceLoc(), - getNumElements() + getNumElements(), + SourceLoc() }; } } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 7a7bef1cb1675..2d64b050c2f7b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -17,6 +17,7 @@ #ifndef SWIFT_TYPES_H #define SWIFT_TYPES_H +#include "swift/AST/AutoDiff.h" #include "swift/AST/DeclContext.h" #include "swift/AST/GenericParamKey.h" #include "swift/AST/Identifier.h" @@ -27,7 +28,9 @@ #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Type.h" #include "swift/AST/TypeAlignments.h" +#include "swift/AST/TypeExpansionContext.h" #include "swift/Basic/ArrayRefView.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/UUID.h" #include "llvm/ADT/ArrayRef.h" @@ -39,6 +42,11 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" +namespace clang { +class Type; +class FunctionType; +} // namespace clang + namespace llvm { struct fltSemantics; } // namespace llvm @@ -49,7 +57,9 @@ enum class AllocationArena; class ArchetypeType; class AssociatedTypeDecl; class ASTContext; +enum BufferPointerTypeKind : unsigned; class ClassDecl; +class ClangModuleLoader; class DependentMemberType; class GenericTypeParamDecl; class GenericTypeParamType; @@ -70,6 +80,7 @@ class NominalTypeDecl; class GenericTypeDecl; class EnumDecl; class EnumElementDecl; +class SILFunctionType; class StructDecl; class ProtocolDecl; class TypeVariableType; @@ -80,6 +91,8 @@ class ProtocolConformance; enum PointerTypeKind : unsigned; struct ValueOwnershipKind; +typedef CanTypeWrapper CanSILFunctionType; + enum class TypeKind : uint8_t { #define TYPE(id, parent) id, #define LAST_TYPE(id) Last_Type = id, @@ -170,7 +183,7 @@ class RecursiveTypeProperties { /// Does a type with these properties have an unresolved type somewhere in it? bool hasUnresolvedType() const { return Bits & HasUnresolvedType; } - + /// Is a type with these properties an lvalue? bool isLValue() const { return Bits & IsLValue; } @@ -295,8 +308,8 @@ class alignas(1 << TypeAlignInBits) TypeBase { } protected: - enum { NumAFTExtInfoBits = 6 }; - enum { NumSILExtInfoBits = 6 }; + enum { NumAFTExtInfoBits = 8 }; + enum { NumSILExtInfoBits = 8 }; union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + @@ -325,11 +338,11 @@ class alignas(1 << TypeAlignInBits) TypeBase { Flags : NumFlagBits ); - SWIFT_INLINE_BITFIELD_FULL(AnyFunctionType, TypeBase, NumAFTExtInfoBits+16, + SWIFT_INLINE_BITFIELD_FULL(AnyFunctionType, TypeBase, NumAFTExtInfoBits+1+16, /// Extra information which affects how the function is called, like /// regparm and the calling convention. - ExtInfo : NumAFTExtInfoBits, - + ExtInfoBits : NumAFTExtInfoBits, + HasUncommonInfo : 1, : NumPadBits, NumParams : 16 ); @@ -342,18 +355,17 @@ class alignas(1 << TypeAlignInBits) TypeBase { NumProtocols : 16 ); - SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 4+32, - : NumPadBits, - + SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 5+32, /// Type variable options. - Options : 4, - + Options : 5, + : NumPadBits, /// The unique number assigned to this type variable. ID : 32 ); - SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+3+1+2, - ExtInfo : NumSILExtInfoBits, + SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+3+1+2, + ExtInfoBits : NumSILExtInfoBits, + HasUncommonInfo : 1, CalleeConvention : 3, HasErrorResult : 1, CoroutineKind : 2 @@ -525,6 +537,8 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// Is this the 'Any' type? bool isAny(); + bool isHole(); + /// Does the type have outer parenthesis? bool hasParenSugar() const { return getKind() == TypeKind::Paren; } @@ -556,7 +570,12 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasUnresolvedType() const { return getRecursiveProperties().hasUnresolvedType(); } - + + /// Determine whether this type involves a hole. + bool hasHole() const { + return getRecursiveProperties().hasUnresolvedType(); + } + /// Determine whether the type involves a context-dependent archetype. bool hasArchetype() const { return getRecursiveProperties().hasArchetype(); @@ -575,7 +594,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { bool hasOpaqueArchetype() const { return getRecursiveProperties().hasOpaqueArchetype(); } - + /// Determine whether the type has any stored properties or enum cases that + /// involve an opaque type. + bool hasOpaqueArchetypePropertiesOrCases(); + /// Determine whether the type is an opened existential type. /// /// To determine whether there is an opened existential type @@ -713,6 +735,14 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// current type. Type wrapInPointer(PointerTypeKind kind); + /// Determines the element type of a known Unsafe[Mutable][Raw]BufferPointer + /// variant, or returns null if the type is not a buffer pointer. + Type getAnyBufferPointerElementType(BufferPointerTypeKind &BPTK); + Type getAnyBufferPointerElementType() { + BufferPointerTypeKind Ignore; + return getAnyBufferPointerElementType(Ignore); + } + /// Determine whether the given type is "specialized", meaning that /// it involves generic types for which generic arguments have been provided. /// For example, the types Vector and Vector.Element are both @@ -795,6 +825,15 @@ class alignas(1 << TypeAlignInBits) TypeBase { getAnyNominal()); } + /// Checks whether this is a type that supports being called through the + /// implementation of a \c callAsFunction method. Note that this does not + /// check access control. + bool isCallableNominalType(DeclContext *dc); + + /// Return true if the specified type or a super-class/super-protocol has the + /// @dynamicMemberLookup attribute on it. + bool hasDynamicMemberLookupAttribute(); + /// Retrieve the superclass of this type. /// /// \param useArchetypes Whether to use context archetypes for outer generic @@ -849,7 +888,19 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// True if this type contains archetypes that could be substituted with /// concrete types to form the argument type. bool isBindableTo(Type ty); + + /// Visit this type and the argument type in parallel, invoking the callback + /// function with each archetype-to-substituted-type binding. The callback + /// may return a new type to substitute into the result type, or return + /// CanType() to error out of the operation. + /// + /// Returns the substituted type, or a null CanType() if this type + /// is not bindable to the substituted type, or the callback returns + /// CanType(). + CanType substituteBindingsTo(Type ty, + llvm::function_ref substFn); + /// Determines whether this type is similar to \p other as defined by /// \p matchOptions. bool matches(Type other, TypeMatchOptions matchOptions); @@ -1058,6 +1109,9 @@ class alignas(1 << TypeAlignInBits) TypeBase { // otherwise, return the null type. Type getSwiftNewtypeUnderlyingType(); + /// Return the type T after looking through at most one optional type. + Type lookThroughSingleOptionalType(); + /// Return the type T after looking through all of the optional /// types. Type lookThroughAllOptionalTypes(); @@ -1073,10 +1127,10 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// Error. bool isExistentialWithError(); - void dump() const LLVM_ATTRIBUTE_USED; + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; - void dumpPrint() const LLVM_ATTRIBUTE_USED; + SWIFT_DEBUG_DUMPER(dumpPrint()); void print(raw_ostream &OS, const PrintOptions &PO = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &PO) const; @@ -1107,6 +1161,11 @@ class alignas(1 << TypeAlignInBits) TypeBase { /// object type. TypeTraitResult canBeClass(); + /// Return the tangent space of the given type, if it exists. Otherwise, + /// return `None`. + Optional + getAutoDiffTangentSpace(LookupConformanceFn lookupConformance); + private: // Make vanilla new/delete illegal for Types. void *operator new(size_t Bytes) throw() = delete; @@ -1749,13 +1808,14 @@ class TypeAliasType final /// escaping. class ParameterTypeFlags { enum ParameterFlags : uint8_t { - None = 0, - Variadic = 1 << 0, - AutoClosure = 1 << 1, - OwnershipShift = 2, - Ownership = 7 << OwnershipShift, - - NumBits = 5 + None = 0, + Variadic = 1 << 0, + AutoClosure = 1 << 1, + NonEphemeral = 1 << 2, + OwnershipShift = 3, + Ownership = 7 << OwnershipShift, + NoDerivative = 1 << 7, + NumBits = 7 }; OptionSet value; static_assert(NumBits < 8*sizeof(OptionSet), "overflowed"); @@ -1768,22 +1828,27 @@ class ParameterTypeFlags { return ParameterTypeFlags(OptionSet(raw)); } - ParameterTypeFlags(bool variadic, bool autoclosure, - ValueOwnership ownership) + ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral, + ValueOwnership ownership, bool noDerivative) : value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) | - uint8_t(ownership) << OwnershipShift) {} + (nonEphemeral ? NonEphemeral : 0) | + uint8_t(ownership) << OwnershipShift | + (noDerivative ? NoDerivative : 0)) {} /// Create one from what's present in the parameter type inline static ParameterTypeFlags fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure, - ValueOwnership ownership); + bool isNonEphemeral, ValueOwnership ownership, + bool isNoDerivative); bool isNone() const { return !value; } bool isVariadic() const { return value.contains(Variadic); } bool isAutoClosure() const { return value.contains(AutoClosure); } + bool isNonEphemeral() const { return value.contains(NonEphemeral); } bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; } bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;} bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; } + bool isNoDerivative() const { return value.contains(NoDerivative); } ValueOwnership getValueOwnership() const { return ValueOwnership((value.toRaw() & Ownership) >> OwnershipShift); @@ -1820,6 +1885,18 @@ class ParameterTypeFlags { : value - ParameterTypeFlags::AutoClosure); } + ParameterTypeFlags withNonEphemeral(bool isNonEphemeral) const { + return ParameterTypeFlags(isNonEphemeral + ? value | ParameterTypeFlags::NonEphemeral + : value - ParameterTypeFlags::NonEphemeral); + } + + ParameterTypeFlags withNoDerivative(bool noDerivative) const { + return ParameterTypeFlags(noDerivative + ? value | ParameterTypeFlags::NoDerivative + : value - ParameterTypeFlags::NoDerivative); + } + bool operator ==(const ParameterTypeFlags &other) const { return value.toRaw() == other.value.toRaw(); } @@ -1886,7 +1963,8 @@ class YieldTypeFlags { ParameterTypeFlags asParamFlags() const { return ParameterTypeFlags(/*variadic*/ false, /*autoclosure*/ false, - getValueOwnership()); + /*nonEphemeral*/ false, getValueOwnership(), + /*noDerivative*/ false); } bool operator ==(const YieldTypeFlags &other) const { @@ -2755,6 +2833,12 @@ class AnyFunctionType : public TypeBase { /// Whether the parameter is marked 'owned' bool isOwned() const { return Flags.isOwned(); } + /// Whether the parameter is marked '@_nonEphemeral' + bool isNonEphemeral() const { return Flags.isNonEphemeral(); } + + /// Whether the parameter is marked '@noDerivative'. + bool isNoDerivative() const { return Flags.isNoDerivative(); } + ValueOwnership getValueOwnership() const { return Flags.getValueOwnership(); } @@ -2844,43 +2928,93 @@ class AnyFunctionType : public TypeBase { // If bits are added or removed, then TypeBase::AnyFunctionTypeBits // and NumMaskBits must be updated, and they must match. // - // |representation|noEscape|throws| - // | 0 .. 3 | 4 | 5 | + // |representation|noEscape|throws|differentiability| + // | 0 .. 3 | 4 | 5 | 6 .. 7 | // enum : unsigned { - RepresentationMask = 0xF << 0, - NoEscapeMask = 1 << 4, - ThrowsMask = 1 << 5, - NumMaskBits = 6 + RepresentationMask = 0xF << 0, + NoEscapeMask = 1 << 4, + ThrowsMask = 1 << 5, + DifferentiabilityMaskOffset = 6, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 8 }; unsigned Bits; // Naturally sized for speed. - ExtInfo(unsigned Bits) : Bits(Bits) {} + public: + class Uncommon { + friend ExtInfo; + friend class AnyFunctionType; + friend class FunctionType; + // We preserve a full clang::Type *, not a clang::FunctionType * as: + // 1. We need to keep sugar in case we need to present an error to the user. + // 2. The actual type being stored is [ignoring sugar] either a + // clang::PointerType or a clang::BlockPointerType which points to a + // clang::FunctionType. + const clang::Type *ClangFunctionType; + + bool empty() const { return !ClangFunctionType; } + Uncommon(const clang::Type *type) : ClangFunctionType(type) {} + + public: + /// Use the ClangModuleLoader to print the Clang type as a string. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os); + }; + + private: + Uncommon Other; + + static void assertIsFunctionType(const clang::Type *); + + ExtInfo(unsigned Bits, Uncommon Other) : Bits(Bits), Other(Other) { + // TODO: [store-sil-clang-function-type] Once we start serializing + // the Clang type, we should also assert that the pointer is non-null. + auto Rep = Representation(Bits & RepresentationMask); + if ((Rep == Representation::CFunctionPointer) && Other.ClangFunctionType) + assertIsFunctionType(Other.ClangFunctionType); + } + + friend AnyFunctionType; + friend class FunctionType; - friend class AnyFunctionType; - public: // Constructor with all defaults. - ExtInfo() : Bits(0) { - assert(getRepresentation() == Representation::Swift); + ExtInfo() + : ExtInfo(Representation::Swift, false, false, + DifferentiabilityKind::NonDifferentiable, + nullptr) { } // Constructor for polymorphic type. - ExtInfo(Representation Rep, bool Throws) { - Bits = ((unsigned) Rep) | (Throws ? ThrowsMask : 0); + ExtInfo(Representation Rep, bool Throws) + : ExtInfo(Rep, false, Throws, DifferentiabilityKind::NonDifferentiable, + nullptr) { } // Constructor with no defaults. - ExtInfo(Representation Rep, - bool IsNoEscape, - bool Throws) - : ExtInfo(Rep, Throws) { - Bits |= (IsNoEscape ? NoEscapeMask : 0); + ExtInfo(Representation Rep, bool IsNoEscape, bool Throws, + DifferentiabilityKind DiffKind, + const clang::Type *type) + : ExtInfo(((unsigned) Rep) + | (IsNoEscape ? NoEscapeMask : 0) + | (Throws ? ThrowsMask : 0) + | (((unsigned)DiffKind << DifferentiabilityMaskOffset) + & DifferentiabilityMask), + Uncommon(type)) { } bool isNoEscape() const { return Bits & NoEscapeMask; } bool throws() const { return Bits & ThrowsMask; } + bool isDifferentiable() const { + return getDifferentiabilityKind() > + DifferentiabilityKind::NonDifferentiable; + } + DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((Bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } Representation getRepresentation() const { unsigned rawRep = Bits & RepresentationMask; assert(rawRep <= unsigned(Representation::Last) @@ -2888,6 +3022,11 @@ class AnyFunctionType : public TypeBase { return Representation(rawRep); } + /// Return the underlying Uncommon value if it is not the default value. + Optional getUncommonInfo() const { + return Other.empty() ? Optional() : Other; + } + bool hasSelfParam() const { switch (getSILRepresentation()) { case SILFunctionTypeRepresentation::Thick: @@ -2928,25 +3067,25 @@ class AnyFunctionType : public TypeBase { LLVM_NODISCARD ExtInfo withRepresentation(Representation Rep) const { return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep); + | (unsigned)Rep, Other); } LLVM_NODISCARD ExtInfo withNoEscape(bool NoEscape = true) const { - if (NoEscape) - return ExtInfo(Bits | NoEscapeMask); - else - return ExtInfo(Bits & ~NoEscapeMask); + return ExtInfo(NoEscape ? (Bits | NoEscapeMask) : (Bits & ~NoEscapeMask), + Other); } LLVM_NODISCARD ExtInfo withThrows(bool Throws = true) const { - if (Throws) - return ExtInfo(Bits | ThrowsMask); - else - return ExtInfo(Bits & ~ThrowsMask); + return ExtInfo(Throws ? (Bits | ThrowsMask) : (Bits & ~ThrowsMask), + Other); + } + LLVM_NODISCARD + ExtInfo withClangFunctionType(const clang::Type *type) const { + return ExtInfo(Bits, Uncommon(type)); } - unsigned getFuncAttrKey() const { - return Bits; + std::pair getFuncAttrKey() const { + return std::make_pair(Bits, Other.ClangFunctionType); } /// Put a SIL representation in the ExtInfo. @@ -2956,7 +3095,7 @@ class AnyFunctionType : public TypeBase { /// don't need to be parsed, printed, or serialized. ExtInfo withSILRepresentation(SILFunctionTypeRepresentation Rep) const { return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep); + | (unsigned)Rep, Other); } SILFunctionTypeRepresentation getSILRepresentation() const { @@ -2973,15 +3112,21 @@ class AnyFunctionType : public TypeBase { }; protected: + /// Create an AnyFunctionType. + /// + /// Subclasses are responsible for storing and retrieving the + /// ExtInfo::Uncommon value if one is present. AnyFunctionType(TypeKind Kind, const ASTContext *CanTypeContext, Type Output, RecursiveTypeProperties properties, unsigned NumParams, ExtInfo Info) : TypeBase(Kind, CanTypeContext, properties), Output(Output) { - Bits.AnyFunctionType.ExtInfo = Info.Bits; + Bits.AnyFunctionType.ExtInfoBits = Info.Bits; + Bits.AnyFunctionType.HasUncommonInfo = Info.getUncommonInfo().hasValue(); Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); // The use of both assert() and static_assert() is intentional. - assert(Bits.AnyFunctionType.ExtInfo == Info.Bits && "Bits were dropped!"); + assert(Bits.AnyFunctionType.ExtInfoBits == Info.Bits + && "Bits were dropped!"); static_assert(ExtInfo::NumMaskBits == NumAFTExtInfoBits, "ExtInfo and AnyFunctionTypeBitfields must agree on bit size"); } @@ -3019,8 +3164,25 @@ class AnyFunctionType : public TypeBase { GenericSignature getOptGenericSignature() const; + bool hasClangFunctionType() const { + return Bits.AnyFunctionType.HasUncommonInfo; + } + + const clang::Type *getClangFunctionType() const; + const clang::Type *getCanonicalClangFunctionType() const; + ExtInfo getExtInfo() const { - return ExtInfo(Bits.AnyFunctionType.ExtInfo); + return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, getClangFunctionType()); + } + + /// Get the canonical ExtInfo for the function type. + /// + /// The parameter useClangFunctionType is present only for staging purposes. + /// In the future, we will always use the canonical clang function type. + ExtInfo getCanonicalExtInfo(bool useClangFunctionType) const { + return ExtInfo(Bits.AnyFunctionType.ExtInfoBits, + useClangFunctionType ? getCanonicalClangFunctionType() + : nullptr); } /// Get the representation of the function type. @@ -3028,6 +3190,76 @@ class AnyFunctionType : public TypeBase { return getExtInfo().getRepresentation(); } + /// Returns the derivative function type for the given parameter indices, + /// result index, derivative function kind, derivative function generic + /// signature (optional), and other auxiliary parameters. + /// + /// Preconditions: + /// - Parameters corresponding to parameter indices must conform to + /// `Differentiable`. + /// - The result corresponding to the result index must conform to + /// `Differentiable`. + /// + /// Typing rules, given: + /// - Original function type. Three cases: + /// - Top-level function: `(T0, T1, ...) -> R` + /// - Static method: `(Self.Type) -> (T0, T1, ...) -> R` + /// - Instance method: `(Self) -> (T0, T1, ...) -> R` + /// + /// Terminology: + /// - The derivative of a `Differentiable`-conforming type has the + /// `TangentVector` associated type. `TangentVector` is abbreviated as `Tan` + /// below. + /// - "wrt" parameters refers to differentiability parameters, identified by + /// the parameter indices. + /// - "wrt" result refers to the result identified by the result index. + /// + /// JVP derivative type: + /// - Takes original parameters. + /// - Returns original result, followed by a differential function, which + /// takes "wrt" parameter derivatives and returns a "wrt" result derivative. + /// + /// \verbatim + /// (T0, T1, ...) -> (R, (T0.Tan, T1.Tan, ...) -> R.Tan) + /// ^ ^~~~~~~~~~~~~~~~~~~ ^~~~~ + /// original result | derivatives wrt params | derivative wrt result + /// + /// (Self) -> (T0, ...) -> (R, (Self.Tan, T0.Tan, ...) -> R.Tan) + /// ^ ^~~~~~~~~~~~~~~~~~~~~ ^~~~~ + /// original result | deriv. wrt params | deriv. wrt result + /// \endverbatim + /// + /// VJP derivative type: + /// - Takes original parameters. + /// - Returns original result, followed by a pullback function, which + /// takes a "wrt" result derivative and returns "wrt" parameter derivatives. + /// + /// \verbatim + /// (T0, T1, ...) -> (R, (R.Tan) -> (T0.Tan, T1.Tan, ...)) + /// ^ ^~~~~ ^~~~~~~~~~~~~~~~~~~ + /// original result | derivative wrt result | derivatives wrt params + /// + /// (Self) -> (T0, ...) -> (R, (R.Tan) -> (Self.Tan, T0.Tan, ...)) + /// ^ ^~~~~ ^~~~~~~~~~~~~~~~~~~~~ + /// original result | deriv. wrt result | deriv. wrt params + /// \endverbatim + /// + /// By default, if the original type has a `self` parameter list and parameter + /// indices include `self`, the computed derivative function type will return + /// a linear map taking/returning self's tangent *last* instead of first, for + /// consistency with SIL. + /// + /// If `makeSelfParamFirst` is true, `self`'s tangent is reordered to appear + /// first. `makeSelfParamFirst` should be true when working with user-facing + /// derivative function types, e.g. when type-checking `@differentiable` and + /// `@derivative` attributes. + AnyFunctionType *getAutoDiffDerivativeFunctionType( + IndexSubset *parameterIndices, unsigned resultIndex, + AutoDiffDerivativeFunctionKind kind, + LookupConformanceFn lookupConformance, + GenericSignature derivativeGenericSignature = GenericSignature(), + bool makeSelfParamFirst = false); + /// True if the parameter declaration it is attached to is guaranteed /// to not persist the closure for longer than the duration of the call. bool isNoEscape() const { @@ -3038,6 +3270,11 @@ class AnyFunctionType : public TypeBase { return getExtInfo().throws(); } + bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } + DifferentiabilityKind getDifferentiabilityKind() const { + return getExtInfo().getDifferentiabilityKind(); + } + /// Returns a new function type exactly like this one but with the ExtInfo /// replaced. AnyFunctionType *withExtInfo(ExtInfo info) const; @@ -3081,16 +3318,25 @@ END_CAN_TYPE_WRAPPER(AnyFunctionType, Type) inline AnyFunctionType::CanYield AnyFunctionType::Yield::getCanonical() const { return CanYield(getType()->getCanonicalType(), getFlags()); } - /// FunctionType - A monomorphic function type, specified with an arrow. /// /// For example: /// let x : (Float, Int) -> Int class FunctionType final : public AnyFunctionType, public llvm::FoldingSetNode, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend TrailingObjects; + + size_t numTrailingObjects(OverloadToken) const { + return getNumParams(); + } + + size_t numTrailingObjects(OverloadToken) const { + return hasClangFunctionType() ? 1 : 0; + } + public: /// 'Constructor' Factory Function static FunctionType *get(ArrayRef params, Type result, @@ -3101,6 +3347,14 @@ class FunctionType final : public AnyFunctionType, return {getTrailingObjects(), getNumParams()}; } + const clang::Type *getClangFunctionType() const { + if (!hasClangFunctionType()) + return nullptr; + auto *type = getTrailingObjects()->ClangFunctionType; + assert(type && "If the pointer was null, we shouldn't have stored it."); + return type; + } + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getParams(), getResult(), getExtInfo()); } @@ -3134,7 +3388,6 @@ END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType) /// has a default argument. struct ParameterListInfo { SmallBitVector defaultArguments; - std::vector functionBuilderTypes; public: ParameterListInfo() { } @@ -3152,9 +3405,6 @@ struct ParameterListInfo { /// Retrieve the number of parameters for which we have information. unsigned size() const { return defaultArguments.size(); } - - /// Retrieve the function builder type for the given parameter. - Type getFunctionBuilderType(unsigned paramIdx) const; }; /// Turn a param list into a symbolic and printable representation that does not @@ -3387,19 +3637,51 @@ inline bool isGuaranteedParameter(ParameterConvention conv) { llvm_unreachable("bad convention kind"); } +/// The differentiability of a SIL function type parameter. +enum class SILParameterDifferentiability : unsigned { + /// Either differentiable or not applicable. + /// + /// - If the function type is not `@differentiable`, parameter + /// differentiability is not applicable. This case is the default value. + /// - If the function type is `@differentiable`, the function is + /// differentiable with respect to this parameter. + DifferentiableOrNotApplicable, + + /// Not differentiable: a `@noDerivative` parameter. + /// + /// May be applied only to parameters of `@differentiable` function types. + /// The function type is not differentiable with respect to this parameter. + NotDifferentiable, +}; + /// A parameter type and the rules for passing it. class SILParameterInfo { llvm::PointerIntPair TypeAndConvention; + SILParameterDifferentiability Differentiability : 1; + public: SILParameterInfo() = default;//: Ty(), Convention((ParameterConvention)0) {} - SILParameterInfo(CanType type, ParameterConvention conv) - : TypeAndConvention(type, conv) { + SILParameterInfo( + CanType type, ParameterConvention conv, + SILParameterDifferentiability differentiability = + SILParameterDifferentiability::DifferentiableOrNotApplicable) + : TypeAndConvention(type, conv), Differentiability(differentiability) { assert(type->isLegalSILType() && "SILParameterInfo has illegal SIL type"); } - CanType getType() const { + /// Return the unsubstituted parameter type that describes the abstract + /// calling convention of the parameter. + /// + /// For most purposes, you probably want \c getArgumentType . + CanType getInterfaceType() const { return TypeAndConvention.getPointer(); } + + /// Return the type of a call argument matching this parameter. + /// + /// \c t must refer back to the function type this is a parameter for. + CanType getArgumentType(SILModule &M, + const SILFunctionType *t) const; ParameterConvention getConvention() const { return TypeAndConvention.getInt(); } @@ -3438,16 +3720,28 @@ class SILParameterInfo { return isGuaranteedParameter(getConvention()); } + SILParameterDifferentiability getDifferentiability() const { + return Differentiability; + } + + SILParameterInfo getWithDifferentiability( + SILParameterDifferentiability differentiability) const { + return SILParameterInfo(getInterfaceType(), getConvention(), + differentiability); + } + /// The SIL storage type determines the ABI for arguments based purely on the /// formal parameter conventions. The actual SIL type for the argument values /// may differ in canonical SIL. In particular, opaque values require indirect /// storage. Therefore they will be passed using an indirect formal /// convention, and this method will return an address type. However, in /// canonical SIL the opaque arguments might not have an address type. - SILType getSILStorageType() const; // in SILFunctionConventions.h + SILType getSILStorageType(SILModule &M, + const SILFunctionType *t) const; // in SILFunctionConventions.h + SILType getSILStorageInterfaceType() const; /// Return a version of this parameter info with the type replaced. - SILParameterInfo getWithType(CanType type) const { + SILParameterInfo getWithInterfaceType(CanType type) const { return SILParameterInfo(type, getConvention()); } @@ -3458,15 +3752,16 @@ class SILParameterInfo { /// Type::transform does. template SILParameterInfo map(const F &fn) const { - return getWithType(fn(getType())); + return getWithInterfaceType(fn(getInterfaceType())); } void profile(llvm::FoldingSetNodeID &id) { - id.AddPointer(getType().getPointer()); + id.AddPointer(getInterfaceType().getPointer()); id.AddInteger((unsigned)getConvention()); + id.AddInteger((unsigned)getDifferentiability()); } - void dump() const; + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &out, const PrintOptions &options = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &Options) const; @@ -3477,7 +3772,9 @@ class SILParameterInfo { } bool operator==(SILParameterInfo rhs) const { - return getType() == rhs.getType() && getConvention() == rhs.getConvention(); + return getInterfaceType() == rhs.getInterfaceType() && + getConvention() == rhs.getConvention() && + getDifferentiability() == rhs.getDifferentiability(); } bool operator!=(SILParameterInfo rhs) const { return !(*this == rhs); @@ -3529,9 +3826,20 @@ class SILResultInfo { assert(type->isLegalSILType() && "SILResultInfo has illegal SIL type"); } - CanType getType() const { + /// Return the unsubstituted parameter type that describes the abstract + /// calling convention of the parameter. + /// + /// For most purposes, you probably want \c getReturnValueType . + CanType getInterfaceType() const { return TypeAndConvention.getPointer(); } + + /// The type of a return value corresponding to this result. + /// + /// \c t must refer back to the function type this is a parameter for. + CanType getReturnValueType(SILModule &M, + const SILFunctionType *t) const; + ResultConvention getConvention() const { return TypeAndConvention.getInt(); } @@ -3541,10 +3849,11 @@ class SILResultInfo { /// storage. Therefore they will be returned using an indirect formal /// convention, and this method will return an address type. However, in /// canonical SIL the opaque results might not have an address type. - SILType getSILStorageType() const; // in SILFunctionConventions.h - + SILType getSILStorageType(SILModule &M, + const SILFunctionType *t) const; // in SILFunctionConventions.h + SILType getSILStorageInterfaceType() const; /// Return a version of this result info with the type replaced. - SILResultInfo getWithType(CanType type) const { + SILResultInfo getWithInterfaceType(CanType type) const { return SILResultInfo(type, getConvention()); } @@ -3565,14 +3874,14 @@ class SILResultInfo { /// Type::transform does. template SILResultInfo map(F &&fn) const { - return getWithType(fn(getType())); + return getWithInterfaceType(fn(getInterfaceType())); } void profile(llvm::FoldingSetNodeID &id) { id.AddPointer(TypeAndConvention.getOpaqueValue()); } - void dump() const; + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &out, const PrintOptions &options = PrintOptions()) const; void print(ASTPrinter &Printer, const PrintOptions &Options) const; @@ -3604,13 +3913,13 @@ class SILYieldInfo : public SILParameterInfo { : SILParameterInfo(type, conv) { } - SILYieldInfo getWithType(CanType type) const { + SILYieldInfo getWithInterfaceType(CanType type) const { return SILYieldInfo(type, getConvention()); } template SILYieldInfo map(const F &fn) const { - return getWithType(fn(getType())); + return getWithInterfaceType(fn(getInterfaceType())); } }; @@ -3629,10 +3938,19 @@ enum class SILCoroutineKind : uint8_t { YieldMany, }; -class SILFunctionType; -typedef CanTypeWrapper CanSILFunctionType; class SILFunctionConventions; + +CanType substOpaqueTypesWithUnderlyingTypes(CanType type, + TypeExpansionContext context, + bool allowLoweredTypes = false); +ProtocolConformanceRef +substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType, + TypeExpansionContext context); +namespace Lowering { + class TypeConverter; +}; + /// SILFunctionType - The lowered type of a function value, suitable /// for use by SIL. /// @@ -3641,7 +3959,7 @@ class SILFunctionConventions; /// function parameter and result types. class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, private llvm::TrailingObjects { + SILResultInfo, SILYieldInfo, CanType> { friend TrailingObjects; size_t numTrailingObjects(OverloadToken) const { @@ -3649,7 +3967,15 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, } size_t numTrailingObjects(OverloadToken) const { - return hasErrorResult() ? 1 : 0; + return getNumResults() + (hasErrorResult() ? 1 : 0); + } + + size_t numTrailingObjects(OverloadToken) const { + return getNumYields(); + } + + size_t numTrailingObjects(OverloadToken) const { + return hasResultCache() ? 2 : 0; } public: @@ -3662,31 +3988,62 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // If bits are added or removed, then TypeBase::SILFunctionTypeBits // and NumMaskBits must be updated, and they must match. - // |representation|pseudogeneric| noescape | - // | 0 .. 3 | 4 | 5 | + // |representation|pseudogeneric|noescape|differentiability| + // | 0 .. 3 | 4 | 5 | 6 .. 7 | // enum : unsigned { RepresentationMask = 0xF << 0, PseudogenericMask = 1 << 4, NoEscapeMask = 1 << 5, - NumMaskBits = 6 + DifferentiabilityMaskOffset = 6, + DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, + NumMaskBits = 8 }; unsigned Bits; // Naturally sized for speed. - ExtInfo(unsigned Bits) : Bits(Bits) {} + class Uncommon { + friend ExtInfo; + friend class SILFunctionType; + + // Invariant: The FunctionType is canonical. + // We store a clang::FunctionType * instead of a clang::CanQualType to + // avoid depending on the Clang AST in this header. + const clang::FunctionType *ClangFunctionType; + + bool empty() const { return !ClangFunctionType; } + Uncommon(const clang::FunctionType *type) : ClangFunctionType(type) {} + + public: + /// Analog of AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType. + void printClangFunctionType(ClangModuleLoader *cml, + llvm::raw_ostream &os) const; + }; + + Uncommon Other; + + ExtInfo(unsigned Bits, Uncommon Other) : Bits(Bits), Other(Other) {} friend class SILFunctionType; - public: // Constructor with all defaults. - ExtInfo() : Bits(0) { } + ExtInfo() : Bits(0), Other(Uncommon(nullptr)) { } // Constructor for polymorphic type. - ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape) { - Bits = ((unsigned) rep) | - (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0); + ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape, + DifferentiabilityKind diffKind, + const clang::FunctionType *type) + : ExtInfo(((unsigned) rep) + | (isPseudogeneric ? PseudogenericMask : 0) + | (isNoEscape ? NoEscapeMask : 0) + | (((unsigned)diffKind << DifferentiabilityMaskOffset) + & DifferentiabilityMask), + Uncommon(type)) { + } + + static ExtInfo getThin() { + return ExtInfo(Representation::Thin, false, false, + DifferentiabilityKind::NonDifferentiable, nullptr); } /// Is this function pseudo-generic? A pseudo-generic function @@ -3696,6 +4053,16 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // Is this function guaranteed to be no-escape by the type system? bool isNoEscape() const { return Bits & NoEscapeMask; } + bool isDifferentiable() const { + return getDifferentiabilityKind() != + DifferentiabilityKind::NonDifferentiable; + } + + DifferentiabilityKind getDifferentiabilityKind() const { + return DifferentiabilityKind((Bits & DifferentiabilityMask) >> + DifferentiabilityMaskOffset); + } + /// What is the abstract representation of this function value? Representation getRepresentation() const { return Representation(Bits & RepresentationMask); @@ -3704,6 +4071,11 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getSILFunctionLanguage(getRepresentation()); } + /// Return the underlying Uncommon value if it is not the default value. + Optional getUncommonInfo() const { + return Other.empty() ? Optional() : Other; + } + bool hasSelfParam() const { switch (getRepresentation()) { case Representation::Thick: @@ -3743,23 +4115,28 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, // the following with methods instead of mutating these objects. ExtInfo withRepresentation(Representation Rep) const { return ExtInfo((Bits & ~RepresentationMask) - | (unsigned)Rep); + | (unsigned)Rep, Other); } ExtInfo withIsPseudogeneric(bool isPseudogeneric = true) const { - if (isPseudogeneric) - return ExtInfo(Bits | PseudogenericMask); - else - return ExtInfo(Bits & ~PseudogenericMask); + return ExtInfo(isPseudogeneric + ? (Bits | PseudogenericMask) + : (Bits & ~PseudogenericMask), + Other); } ExtInfo withNoEscape(bool NoEscape = true) const { - if (NoEscape) - return ExtInfo(Bits | NoEscapeMask); - else - return ExtInfo(Bits & ~NoEscapeMask); + return ExtInfo(NoEscape ? (Bits | NoEscapeMask) : (Bits & ~NoEscapeMask), + Other); + } + ExtInfo + withDifferentiabilityKind(DifferentiabilityKind differentiability) const { + return ExtInfo( + (Bits & ~DifferentiabilityMask) | + ((unsigned)differentiability << DifferentiabilityMaskOffset), + Other); } - unsigned getFuncAttrKey() const { - return Bits; + std::pair getFuncAttrKey() const { + return std::make_pair(Bits, Other.ClangFunctionType); } bool operator==(ExtInfo Other) const { @@ -3778,51 +4155,35 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, unsigned NumAnyResults : 16; // Not including the ErrorResult. unsigned NumAnyIndirectFormalResults : 16; // Subset of NumAnyResults. + // [SILFunctionType-layout] // The layout of a SILFunctionType in memory is: // SILFunctionType // SILParameterInfo[NumParameters] // SILResultInfo[isCoroutine() ? 0 : NumAnyResults] - // SILYieldInfo[isCoroutine() ? NumAnyResults : 0] // SILResultInfo? // if hasErrorResult() + // SILYieldInfo[isCoroutine() ? NumAnyResults : 0] // CanType? // if !isCoro && NumAnyResults > 1, formal result cache // CanType? // if !isCoro && NumAnyResults > 1, all result cache - CanGenericSignature GenericSig; - Optional WitnessMethodConformance; + llvm::PointerIntPair GenericSigAndIsImplied; + ProtocolConformanceRef WitnessMethodConformance; + SubstitutionMap Substitutions; MutableArrayRef getMutableParameters() { return {getTrailingObjects(), NumParameters}; } MutableArrayRef getMutableResults() { - auto *ptr = reinterpret_cast(getMutableParameters().end()); - return {ptr, getNumResults()}; + return {getTrailingObjects(), getNumResults()}; } MutableArrayRef getMutableYields() { - auto *ptr = reinterpret_cast(getMutableParameters().end()); - return {ptr, getNumYields()}; - } - - /// Return a pointer past the end of the formal results, whether they - /// are yield-results or normal results. - void *getEndOfFormalResults() { - return isCoroutine() ? static_cast(getMutableYields().end()) - : static_cast(getMutableResults().end()); + return {getTrailingObjects(), getNumYields()}; } SILResultInfo &getMutableErrorResult() { assert(hasErrorResult()); - return *reinterpret_cast(getEndOfFormalResults()); - } - - /// Return a pointer past the end of all of the results, including the - /// error result if one is present. - void *getEndOfAllResults() { - void *end = getEndOfFormalResults(); - if (hasErrorResult()) - end = reinterpret_cast(end) + sizeof(SILResultInfo); - return end; + return *(getTrailingObjects() + getNumResults()); } /// Do we have slots for caches of the normal-result tuple type? @@ -3832,14 +4193,13 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, CanType &getMutableFormalResultsCache() const { assert(hasResultCache()); - auto *ptr = const_cast(this)->getEndOfAllResults(); - return *reinterpret_cast(ptr); + return *const_cast(this)->getTrailingObjects(); } CanType &getMutableAllResultsCache() const { assert(hasResultCache()); - auto *ptr = const_cast(this)->getEndOfAllResults(); - return *(reinterpret_cast(ptr) + 1); + return *(const_cast(this)->getTrailingObjects() + + 1); } SILFunctionType(GenericSignature genericSig, ExtInfo ext, @@ -3849,21 +4209,22 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, ArrayRef yieldResults, ArrayRef normalResults, Optional errorResult, - const ASTContext &ctx, - RecursiveTypeProperties properties, - Optional witnessMethodConformance); + SubstitutionMap substitutions, bool genericSigIsImplied, + const ASTContext &ctx, RecursiveTypeProperties properties, + ProtocolConformanceRef witnessMethodConformance); public: - static CanSILFunctionType get(GenericSignature genericSig, - ExtInfo ext, - SILCoroutineKind coroutineKind, - ParameterConvention calleeConvention, - ArrayRef interfaceParams, - ArrayRef interfaceYields, - ArrayRef interfaceResults, - Optional interfaceErrorResult, - const ASTContext &ctx, - Optional witnessMethodConformance = None); + static CanSILFunctionType + get(GenericSignature genericSig, ExtInfo ext, SILCoroutineKind coroutineKind, + ParameterConvention calleeConvention, + ArrayRef interfaceParams, + ArrayRef interfaceYields, + ArrayRef interfaceResults, + Optional interfaceErrorResult, + SubstitutionMap substitutions, bool genericSigIsImplied, + const ASTContext &ctx, + ProtocolConformanceRef witnessMethodConformance = + ProtocolConformanceRef()); /// Return a structurally-identical function type with a slightly tweaked /// ExtInfo. @@ -3882,7 +4243,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, /// - a single indirect result and no direct results. /// /// If the result is formally indirect, return the empty tuple. - SILType getFormalCSemanticResult(); + SILType getFormalCSemanticResult(SILModule &M); /// Return the convention under which the callee is passed, if this /// is a thick non-block callee. @@ -3991,14 +4352,15 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, /// this function depends on the current SIL stage and is known by /// SILFunctionConventions. It may be a wider tuple that includes formally /// indirect results. - SILType getDirectFormalResultsType(); + SILType getDirectFormalResultsType(SILModule &M); /// Get a single non-address SILType for all SIL results regardless of whether /// they are formally indirect. The actual SIL result type of an apply /// instruction that calls this function depends on the current SIL stage and /// is known by SILFunctionConventions. It may be a narrower tuple that omits /// formally indirect results. - SILType getAllResultsType(); + SILType getAllResultsSubstType(SILModule &M); + SILType getAllResultsInterfaceType(); /// Does this function have a blessed Swift-native error result? bool hasErrorResult() const { @@ -4029,31 +4391,132 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, return getParameters().back(); } - bool isPolymorphic() const { return !GenericSig.isNull(); } - CanGenericSignature getGenericSignature() const { return GenericSig; } + /// Get the generic signature used to apply the substitutions of a substituted function type + CanGenericSignature getSubstGenericSignature() const { + return GenericSigAndIsImplied.getPointer(); + } + /// Get the generic signature used by callers to invoke the function. + CanGenericSignature getInvocationGenericSignature() const { + if (isGenericSignatureImplied()) { + return CanGenericSignature(); + } else { + return getSubstGenericSignature(); + } + } + + bool isGenericSignatureImplied() const { + return GenericSigAndIsImplied.getInt(); + } + SubstitutionMap getSubstitutions() const { + return Substitutions; + } + + bool isPolymorphic() const { + return !getInvocationGenericSignature().isNull(); + } - CanType getSelfInstanceType() const; + CanType getSelfInstanceType(SILModule &M) const; /// If this is a @convention(witness_method) function with a class /// constrained self parameter, return the class constraint for the /// Self type. - ClassDecl *getWitnessMethodClass() const; - - /// If this is a @convention(witness_method) function, return the conformance - /// for which the method is a witness. - ProtocolConformanceRef getWitnessMethodConformance() const { - assert(getRepresentation() == Representation::WitnessMethod); - return *WitnessMethodConformance; - } + ClassDecl *getWitnessMethodClass(SILModule &M) const; /// If this is a @convention(witness_method) function, return the conformance - /// for which the method is a witness, if it isn't that convention, return - /// None. - Optional getWitnessMethodConformanceOrNone() const { + /// for which the method is a witness. If it isn't that convention, return + /// an invalid conformance. + ProtocolConformanceRef getWitnessMethodConformanceOrInvalid() const { return WitnessMethodConformance; } - ExtInfo getExtInfo() const { return ExtInfo(Bits.SILFunctionType.ExtInfo); } + const clang::FunctionType *getClangFunctionType() const; + + /// Returns the type of the derivative function for the given parameter + /// indices, result index, derivative function kind, derivative function + /// generic signature (optional), and other auxiliary parameters. + /// + /// Preconditions: + /// - Parameters corresponding to parameter indices must conform to + /// `Differentiable`. + /// - The result corresponding to the result index must conform to + /// `Differentiable`. + /// + /// Typing rules, given: + /// - Original function type: $(T0, T1, ...) -> (R0, R1, ...) + /// + /// Terminology: + /// - The derivative of a `Differentiable`-conforming type has the + /// `TangentVector` associated type. `TangentVector` is abbreviated as `Tan` + /// below. + /// - "wrt" parameters refers to parameters indicated by the parameter + /// indices. + /// - "wrt" result refers to the result indicated by the result index. + /// + /// JVP derivative type: + /// - Takes original parameters. + /// - Returns original results, followed by a differential function, which + /// takes "wrt" parameter derivatives and returns a "wrt" result derivative. + /// + /// $(T0, ...) -> (R0, ..., (T0.Tan, T1.Tan, ...) -> R0.Tan) + /// ^~~~~~~ ^~~~~~~~~~~~~~~~~~~ ^~~~~~ + /// original results | derivatives wrt params | derivative wrt result + /// + /// VJP derivative type: + /// - Takes original parameters. + /// - Returns original results, followed by a pullback function, which + /// takes a "wrt" result derivative and returns "wrt" parameter derivatives. + /// + /// $(T0, ...) -> (R0, ..., (R0.Tan) -> (T0.Tan, T1.Tan, ...)) + /// ^~~~~~~ ^~~~~~ ^~~~~~~~~~~~~~~~~~~ + /// original results | derivative wrt result | derivatives wrt params + /// + /// A "constrained derivative generic signature" is computed from + /// `derivativeFunctionGenericSignature`, if specified. Otherwise, it is + /// computed from the original generic signature. A "constrained derivative + /// generic signature" requires all "wrt" parameters to conform to + /// `Differentiable`; this is important for correctness. + /// + /// This "constrained derivative generic signature" is used for + /// parameter/result type lowering. It is used as the actual generic signature + /// of the derivative function type iff the original function type has a + /// generic signature and not all generic parameters are bound to concrete + /// types. Otherwise, no derivative generic signature is used. + /// + /// Other properties of the original function type are copied exactly: + /// `ExtInfo`, coroutine kind, callee convention, yields, optional error + /// result, witness method conformance, etc. + /// + /// Special cases: + /// - Reabstraction thunks have special derivative type calculation. The + /// original function-typed last parameter is transformed into a + /// `@differentiable` function-typed parameter in the derivative type. This + /// is necessary for the differentiation transform to support reabstraction + /// thunk differentiation because the function argument is opaque and cannot + /// be differentiated. Instead, the argument is made `@differentiable` and + /// reabstraction thunk JVP/VJP callers are responsible for passing a + /// `@differentiable` function. + /// - TODO(TF-1036): Investigate more efficient reabstraction thunk + /// derivative approaches. The last argument can simply be a + /// corresponding derivative function, instead of a `@differentiable` + /// function - this is more direct. It may be possible to implement + /// reabstraction thunk derivatives using "reabstraction thunks for + /// the original function's derivative", avoiding extra code generation. + /// + /// Caveats: + /// - We may support multiple result indices instead of a single result index + /// eventually. At the SIL level, this enables differentiating wrt multiple + /// function results. At the Swift level, this enables differentiating wrt + /// multiple tuple elements for tuple-returning functions. + CanSILFunctionType getAutoDiffDerivativeFunctionType( + IndexSubset *parameterIndices, unsigned resultIndex, + AutoDiffDerivativeFunctionKind kind, Lowering::TypeConverter &TC, + LookupConformanceFn lookupConformance, + CanGenericSignature derivativeFunctionGenericSignature = nullptr, + bool isReabstractionThunk = false); + + ExtInfo getExtInfo() const { + return ExtInfo(Bits.SILFunctionType.ExtInfoBits, getClangFunctionType()); + } /// Returns the language-level calling convention of the function. Language getLanguage() const { @@ -4083,7 +4546,16 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, getRepresentation() == SILFunctionTypeRepresentation::Thick; } - bool isNoReturnFunction() const; // Defined in SILType.cpp + bool isDifferentiable() const { return getExtInfo().isDifferentiable(); } + DifferentiabilityKind getDifferentiabilityKind() const { + return getExtInfo().getDifferentiabilityKind(); + } + + bool isNoReturnFunction(SILModule &M) const; // Defined in SILType.cpp + + /// Create a SILFunctionType with the same parameters, results, and attributes as this one, but with + /// a different set of substitutions. + CanSILFunctionType withSubstitutions(SubstitutionMap subs) const; class ABICompatibilityCheckResult { friend class SILFunctionType; @@ -4127,30 +4599,38 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, /// assertions are disabled, this just returns true. ABICompatibilityCheckResult isABICompatibleWith(CanSILFunctionType other, - SILFunction *context = nullptr) const; + SILFunction &context) const; CanSILFunctionType substGenericArgs(SILModule &silModule, - SubstitutionMap subs); + SubstitutionMap subs, + TypeExpansionContext context); CanSILFunctionType substGenericArgs(SILModule &silModule, TypeSubstitutionFn subs, - LookupConformanceFn conformances); - + LookupConformanceFn conformances, + TypeExpansionContext context); + CanSILFunctionType substituteOpaqueArchetypes(Lowering::TypeConverter &TC, + TypeExpansionContext context); + + SILType substInterfaceType(SILModule &M, + SILType interfaceType) const; + + /// Return the unsubstituted function type equivalent to this type; that is, the type that has the same + /// argument and result types as `this` type after substitutions, if any. + CanSILFunctionType getUnsubstitutedType(SILModule &M) const; + void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getGenericSignature(), getExtInfo(), getCoroutineKind(), - getCalleeConvention(), getParameters(), getYields(), - getResults(), getOptionalErrorResult(), - getWitnessMethodConformanceOrNone()); - } - static void Profile(llvm::FoldingSetNodeID &ID, - GenericSignature genericSig, - ExtInfo info, - SILCoroutineKind coroutineKind, - ParameterConvention calleeConvention, - ArrayRef params, - ArrayRef yields, - ArrayRef results, - Optional errorResult, - Optional conformance); + Profile(ID, getSubstGenericSignature(), getExtInfo(), getCoroutineKind(), + getCalleeConvention(), getParameters(), getYields(), getResults(), + getOptionalErrorResult(), getWitnessMethodConformanceOrInvalid(), + isGenericSignatureImplied(), getSubstitutions()); + } + static void + Profile(llvm::FoldingSetNodeID &ID, GenericSignature genericSig, ExtInfo info, + SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, + ArrayRef params, ArrayRef yields, + ArrayRef results, Optional errorResult, + ProtocolConformanceRef conformance, bool isGenericSigImplied, + SubstitutionMap substitutions); // Implement isa/cast/dyncast/etc. static bool classof(const TypeBase *T) { @@ -4864,31 +5344,51 @@ class OpaqueTypeArchetypeType final : public ArchetypeType, BEGIN_CAN_TYPE_WRAPPER(OpaqueTypeArchetypeType, ArchetypeType) END_CAN_TYPE_WRAPPER(OpaqueTypeArchetypeType, ArchetypeType) +enum class OpaqueSubstitutionKind { + // Don't substitute the opaque type for the underlying type. + DontSubstitute, + // Substitute without looking at the type and context. + // Can be done because the underlying type is from a minimally resilient + // function (therefore must not contain private or internal types). + AlwaysSubstitute, + // Substitute in the same module into a maximal resilient context. + // Can be done if the underlying type is accessible from the context we + // substitute into. Private types cannot be accessed from a different TU. + SubstituteSameModuleMaximalResilience, + // Substitute in a different module from the opaque definining decl. Can only + // be done if the underlying type is public. + SubstituteNonResilientModule +}; + /// A function object that can be used as a \c TypeSubstitutionFn and /// \c LookupConformanceFn for \c Type::subst style APIs to map opaque /// archetypes with underlying types visible at a given resilience expansion /// to their underlying types. class ReplaceOpaqueTypesWithUnderlyingTypes { public: - ModuleDecl *contextModule; + const DeclContext *inContext; ResilienceExpansion contextExpansion; - ReplaceOpaqueTypesWithUnderlyingTypes(ModuleDecl *contextModule, - ResilienceExpansion contextExpansion) - : contextModule(contextModule), contextExpansion(contextExpansion) {} + bool isContextWholeModule; + ReplaceOpaqueTypesWithUnderlyingTypes(const DeclContext *inContext, + ResilienceExpansion contextExpansion, + bool isWholeModuleContext) + : inContext(inContext), contextExpansion(contextExpansion), + isContextWholeModule(isWholeModuleContext) {} /// TypeSubstitutionFn Type operator()(SubstitutableType *maybeOpaqueType) const; /// LookupConformanceFn - Optional operator()(CanType maybeOpaqueType, - Type replacementType, - ProtocolDecl *protocol) const; + ProtocolConformanceRef operator()(CanType maybeOpaqueType, + Type replacementType, + ProtocolDecl *protocol) const; - bool shouldPerformSubstitution(OpaqueTypeDecl *opaque) const; + OpaqueSubstitutionKind + shouldPerformSubstitution(OpaqueTypeDecl *opaque) const; - static bool shouldPerformSubstitution(OpaqueTypeDecl *opaque, - ModuleDecl *contextModule, - ResilienceExpansion contextExpansion); + static OpaqueSubstitutionKind + shouldPerformSubstitution(OpaqueTypeDecl *opaque, ModuleDecl *contextModule, + ResilienceExpansion contextExpansion); }; /// An archetype that represents the dynamic type of an opened existential. @@ -5531,10 +6031,9 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const { } /// Create one from what's present in the parameter decl and type -inline ParameterTypeFlags -ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, - bool isAutoClosure, - ValueOwnership ownership) { +inline ParameterTypeFlags ParameterTypeFlags::fromParameterType( + Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral, + ValueOwnership ownership, bool isNoDerivative) { // FIXME(Remove InOut): The last caller that needs this is argument // decomposition. Start by enabling the assertion there and fixing up those // callers, then remove this, then remove @@ -5544,7 +6043,7 @@ ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic, ownership == ValueOwnership::InOut); ownership = ValueOwnership::InOut; } - return {isVariadic, isAutoClosure, ownership}; + return {isVariadic, isAutoClosure, isNonEphemeral, ownership, isNoDerivative}; } inline const Type *BoundGenericType::getTrailingObjectsPointer() const { @@ -5660,5 +6159,5 @@ struct DenseMapInfo { }; } - + #endif diff --git a/include/swift/AST/Witness.h b/include/swift/AST/Witness.h index 0721398696847..4d72ae77ffd60 100644 --- a/include/swift/AST/Witness.h +++ b/include/swift/AST/Witness.h @@ -19,6 +19,7 @@ #define SWIFT_AST_WITNESS_H #include "swift/AST/ConcreteDeclRef.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/Compiler.h" @@ -182,13 +183,45 @@ class Witness { return {}; } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out) const; }; +struct TypeWitnessAndDecl { + Type witnessType; + TypeDecl *witnessDecl = nullptr; + + TypeWitnessAndDecl() = default; + TypeWitnessAndDecl(Type ty, TypeDecl *decl) + : witnessType(ty), witnessDecl(decl) {} + +public: + Type getWitnessType() const { + return witnessType; + } + + TypeDecl *getWitnessDecl() const { + return witnessDecl; + } + + friend llvm::hash_code hash_value(const TypeWitnessAndDecl &owner) { + return llvm::hash_combine(owner.witnessType, + owner.witnessDecl); + } + + friend bool operator==(const TypeWitnessAndDecl &lhs, + const TypeWitnessAndDecl &rhs) { + return lhs.witnessType->isEqual(rhs.witnessType) && + lhs.witnessDecl == rhs.witnessDecl; + } + + friend bool operator!=(const TypeWitnessAndDecl &lhs, + const TypeWitnessAndDecl &rhs) { + return !(lhs == rhs); + } +}; + } // end namespace swift #endif // SWIFT_AST_WITNESS_H diff --git a/include/swift/Basic/AnyValue.h b/include/swift/Basic/AnyValue.h index 6d76e9ba43b75..7a24dfec61a0b 100644 --- a/include/swift/Basic/AnyValue.h +++ b/include/swift/Basic/AnyValue.h @@ -170,8 +170,9 @@ namespace llvm { void simple_display(raw_ostream &out, const Optional &opt) { if (opt) { simple_display(out, *opt); + } else { + out << "None"; } - out << "None"; } } // end namespace llvm diff --git a/include/swift/Basic/CTypeIDZone.def b/include/swift/Basic/CTypeIDZone.def index a2349a2f848ec..6be90fd879b2e 100644 --- a/include/swift/Basic/CTypeIDZone.def +++ b/include/swift/Basic/CTypeIDZone.def @@ -36,6 +36,7 @@ SWIFT_TYPEID_NAMED(std::string, String) // C++ standard library types. SWIFT_TYPEID_TEMPLATE1_NAMED(std::vector, Vector, typename T, T) +SWIFT_TYPEID_TEMPLATE1_NAMED(std::unique_ptr, UniquePtr, typename T, T) // LLVM ADT types. SWIFT_TYPEID_TEMPLATE1_NAMED(llvm::TinyPtrVector, TinyPtrVector, typename T, T) diff --git a/include/swift/Basic/ClusteredBitVector.h b/include/swift/Basic/ClusteredBitVector.h index 2a91e591740dd..d4f1a3b533352 100644 --- a/include/swift/Basic/ClusteredBitVector.h +++ b/include/swift/Basic/ClusteredBitVector.h @@ -31,6 +31,7 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/Optional.h" +#include "swift/Basic/Debug.h" #include namespace swift { @@ -276,7 +277,7 @@ class ClusteredBitVector { /// Pretty-print the vector. void print(llvm::raw_ostream &out) const; - void dump() const; + SWIFT_DEBUG_DUMP; }; } // end namespace swift diff --git a/include/swift/Basic/Compiler.h b/include/swift/Basic/Compiler.h index eea6c937c8ef8..b422175695ade 100644 --- a/include/swift/Basic/Compiler.h +++ b/include/swift/Basic/Compiler.h @@ -27,15 +27,6 @@ #define __has_attribute(x) 0 #endif -#if SWIFT_COMPILER_IS_MSVC && _MSC_VER < 1910 -// Work around MSVC bug: attempting to reference a deleted function -// https://connect.microsoft.com/VisualStudio/feedback/details/3116505 -#define SWIFT_DELETE_OPERATOR_DELETED \ - { llvm_unreachable("Delete operator should not be called."); } -#else -#define SWIFT_DELETE_OPERATOR_DELETED = delete; -#endif - // __builtin_assume() is an optimization hint. #if __has_builtin(__builtin_assume) #define SWIFT_ASSUME(x) __builtin_assume(x) diff --git a/include/swift/Basic/Debug.h b/include/swift/Basic/Debug.h new file mode 100644 index 0000000000000..ad47b7f6f398c --- /dev/null +++ b/include/swift/Basic/Debug.h @@ -0,0 +1,39 @@ +//===--- Debug.h - Compiler debugging helpers -------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +#ifndef SWIFT_BASIC_DEBUG_H +#define SWIFT_BASIC_DEBUG_H + +#include "llvm/Support/Compiler.h" + + +/// Adds attributes to the provided method signature indicating that it is a +/// debugging helper that should never be called directly from compiler code. +/// This deprecates the method so it won't be called directly and marks it as +/// used so it won't be eliminated as dead code. +#define SWIFT_DEBUG_HELPER(method) \ + LLVM_ATTRIBUTE_DEPRECATED(method LLVM_ATTRIBUTE_USED, \ + "only for use in the debugger") + +/// Declares a const void instance method with the name and parameters provided. +/// Methods declared with this macro should never be called except in the +/// debugger. +#define SWIFT_DEBUG_DUMPER(nameAndParams) \ + SWIFT_DEBUG_HELPER(void nameAndParams const) + +/// Declares an instance `void dump() const` method. Methods declared with this +/// macro should never be called except in the debugger. +#define SWIFT_DEBUG_DUMP \ + SWIFT_DEBUG_DUMPER(dump()) + +#endif + + diff --git a/include/swift/Basic/DiagnosticOptions.h b/include/swift/Basic/DiagnosticOptions.h index 53b9d48dc021c..12a39435550b2 100644 --- a/include/swift/Basic/DiagnosticOptions.h +++ b/include/swift/Basic/DiagnosticOptions.h @@ -55,6 +55,12 @@ class DiagnosticOptions { // When printing diagnostics, include the diagnostic name at the end bool PrintDiagnosticNames = false; + /// If set to true, produce more descriptive diagnostic output if available. + /// Descriptive diagnostic output is not intended to be machine-readable. + bool EnableDescriptiveDiagnostics = false; + + std::string DiagnosticDocumentationPath = ""; + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index 59e3e60c7fae7..f2a832821a0f7 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -59,6 +59,8 @@ TYPE("llvm-bc", LLVM_BC, "bc", "") TYPE("diagnostics", SerializedDiagnostics, "dia", "") TYPE("objc-header", ObjCHeader, "h", "") TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "") +TYPE("swift-ranges", SwiftRanges, "swiftranges", "") +TYPE("compiled-source", CompiledSource, "compiledsource", "") TYPE("remap", Remapping, "remap", "") TYPE("imported-modules", ImportedModules, "importedmodules", "") TYPE("tbd", TBD, "tbd", "") diff --git a/include/swift/Basic/FileTypes.h b/include/swift/Basic/FileTypes.h index 1b62e5393063b..82f9511a34500 100644 --- a/include/swift/Basic/FileTypes.h +++ b/include/swift/Basic/FileTypes.h @@ -63,6 +63,18 @@ static inline void forAllTypes(llvm::function_ref fn) { fn(static_cast(i)); } +/// Some files are produced by the frontend and read by the driver in order to +/// support incremental compilation. Invoke the passed-in function for every +/// such file type. +static inline void +forEachIncrementalOutputType(llvm::function_ref fn) { + static const std::vector incrementalOutputTypes = { + file_types::TY_SwiftDeps, file_types::TY_SwiftRanges, + file_types::TY_CompiledSource}; + for (auto type : incrementalOutputTypes) + fn(type); +} + } // end namespace file_types } // end namespace swift diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 204a6967d7a19..c125f046281c9 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -56,7 +56,7 @@ namespace swift { /// A collection of options that affect the language dialect and /// provide compiler debugging facilities. - class LangOptions { + class LangOptions final { public: /// The target we are building for. @@ -64,6 +64,17 @@ namespace swift { /// This represents the minimum deployment target. llvm::Triple Target; + /// \brief The second target for a zippered build + /// + /// This represents the target and minimum deployment version for the + /// second ('variant') target when performing a zippered build. + /// For example, if the target is x86_64-apple-macosx10.14 then + /// a target-variant of x86_64-apple-ios12.0-macabi will produce + /// a zippered binary that can be loaded into both macCatalyst and + /// macOS processes. A value of 'None' means no zippering will be + /// performed. + llvm::Optional TargetVariant; + /// /// Language features /// @@ -94,6 +105,10 @@ namespace swift { /// when using RequireExplicitAvailability. std::string RequireExplicitAvailabilityTarget; + /// If false, '#file' evaluates to the full path rather than a + /// human-readable string. + bool EnableConcisePoundFile = false; + /// /// Support for alternate usage modes /// @@ -160,63 +175,25 @@ namespace swift { /// Flags for developers /// - /// Whether we are debugging the constraint solver. - /// - /// This option enables verbose debugging output from the constraint - /// solver. - bool DebugConstraintSolver = false; - - /// Specific solution attempt for which the constraint - /// solver should be debugged. - unsigned DebugConstraintSolverAttempt = 0; - - /// Line numbers to activate the constraint solver debugger. - /// Should be stored sorted. - llvm::SmallVector DebugConstraintSolverOnLines; - /// Enable named lazy member loading. bool NamedLazyMemberLoading = true; - - /// Debug the generic signatures computed by the generic signature builder. - bool DebugGenericSignatures = false; - - /// Triggers llvm fatal_error if typechecker tries to typecheck a decl or an - /// identifier reference with the provided prefix name. - /// This is for testing purposes. - std::string DebugForbidTypecheckPrefix; - - /// Whether to dump debug info for request evaluator cycles. - bool DebugDumpCycles = false; - + /// The path to which we should emit GraphViz output for the complete /// request-evaluator graph. std::string RequestEvaluatorGraphVizPath; + + /// Whether to dump debug info for request evaluator cycles. + bool DebugDumpCycles = false; - /// The upper bound, in bytes, of temporary data that can be - /// allocated by the constraint solver. - unsigned SolverMemoryThreshold = 512 * 1024 * 1024; - - unsigned SolverBindingThreshold = 1024 * 1024; - - /// The upper bound to number of sub-expressions unsolved - /// before termination of the shrink phrase of the constraint solver. - unsigned SolverShrinkUnsolvedThreshold = 10; - - /// Enable one-way constraints in function builders. - bool FunctionBuilderOneWayConstraints = true; - - /// Disable the shrink phase of the expression type checker. - bool SolverDisableShrink = false; - - /// Disable constraint system performance hacks. - bool DisableConstraintSolverPerformanceHacks = false; + /// Whether to build a request dependency graph for debugging. + bool BuildRequestDependencyGraph = false; - /// Enable experimental operator designated types feature. - bool EnableOperatorDesignatedTypes = false; + /// Enable SIL type lowering + bool EnableSubstSILFunctionTypesForFunctionValues = false; - /// Enable constraint solver support for experimental - /// operator protocol designator feature. - bool SolverEnableOperatorDesignatedTypes = false; + /// Whether to diagnose an ephemeral to non-ephemeral conversion as an + /// error. + bool DiagnoseInvalidEphemeralnessAsError = false; /// The maximum depth to which to test decl circularity. unsigned MaxCircularityDepth = 500; @@ -228,10 +205,6 @@ namespace swift { /// Enable experimental #assert feature. bool EnableExperimentalStaticAssert = false; - /// Staging flag for treating inout parameters as Thread Sanitizer - /// accesses. - bool DisableTsanInoutInstrumentation = false; - /// Should we check the target OSs of serialized modules to see that they're /// new enough? bool EnableTargetOSChecking = true; @@ -264,6 +237,13 @@ namespace swift { /// Build the ASTScope tree lazily bool LazyASTScopes = true; + /// Use Clang function types for computing canonical types. + /// If this option is false, the clang function types will still be computed + /// but will not be used for checking type equality. + /// FIXME: [clang-function-type-serialization] This option should be turned + /// on once we start serializing clang function types. + bool UseClangFunctionTypes = false; + /// Whether to use the import as member inference system /// /// When importing a global, try to infer whether we can import it as a @@ -316,14 +296,24 @@ namespace swift { /// Whether to verify the parsed syntax tree and emit related diagnostics. bool VerifySyntaxTree = false; - /// Scaffolding to permit experimentation with finer-grained dependencies - /// and faster rebuilds. - bool EnableExperimentalDependencies = false; - + /// Emit the newer, finer-grained swiftdeps file. Eventually will support + /// faster rebuilds. + bool EnableFineGrainedDependencies = true; + + /// Instead of hashing tokens inside of NominalType and ExtensionBodies into + /// the interface hash, hash them into per-iterable-decl-context + /// fingerprints. Fine-grained dependency types won't dirty every provides + /// in a file when the user adds a member to, e.g., a struct. + bool EnableTypeFingerprints = true; + + /// When using fine-grained dependencies, emit dot files for every swiftdeps + /// file. + bool EmitFineGrainedDependencySourcefileDotFiles = false; + /// To mimic existing system, set to false. /// To experiment with including file-private and private dependency info, /// set to true. - bool ExperimentalDependenciesIncludeIntrafileOnes = false; + bool FineGrainedDependenciesIncludeIntrafileOnes = false; /// Whether to enable experimental differentiable programming features: /// `@differentiable` declaration attribute, etc. @@ -341,20 +331,13 @@ namespace swift { /// This is only implemented on certain OSs. If no target has been /// configured, returns v0.0.0. llvm::VersionTuple getMinPlatformVersion() const { - unsigned major, minor, revision; + unsigned major = 0, minor = 0, revision = 0; if (Target.isMacOSX()) { Target.getMacOSXVersion(major, minor, revision); } else if (Target.isiOS()) { Target.getiOSVersion(major, minor, revision); } else if (Target.isWatchOS()) { Target.getOSVersion(major, minor, revision); - } else if (Target.isOSLinux() || Target.isOSFreeBSD() || - Target.isAndroid() || Target.isOSWindows() || - Target.isPS4() || Target.isOSHaiku() || - Target.getTriple().empty()) { - major = minor = revision = 0; - } else { - llvm_unreachable("Unsupported target OS"); } return llvm::VersionTuple(major, minor, revision); } @@ -436,7 +419,8 @@ namespace swift { /// /// \param suggestedKind Populated with suggested replacement platform condition /// \param suggestedValues Populated with suggested replacement values - /// if a match is not found. + /// if a match is not found, or if the value has been deprecated + /// in favor of a newer one. static bool checkPlatformConditionSupported( PlatformConditionKind Kind, StringRef Value, PlatformConditionKind &suggestedKind, @@ -456,6 +440,103 @@ namespace swift { PlatformConditionValues; llvm::SmallVector CustomConditionalCompilationFlags; }; + + class TypeCheckerOptions final { + public: + /// If non-zero, warn when a function body takes longer than this many + /// milliseconds to type-check. + /// + /// Intended for debugging purposes only. + unsigned WarnLongFunctionBodies = 0; + + /// If non-zero, warn when type-checking an expression takes longer + /// than this many milliseconds. + /// + /// Intended for debugging purposes only. + unsigned WarnLongExpressionTypeChecking = 0; + + /// If non-zero, abort the expression type checker if it takes more + /// than this many seconds. + unsigned ExpressionTimeoutThreshold = 600; + + /// If non-zero, abort the switch statement exhaustiveness checker if + /// the Space::minus function is called more than this many times. + /// + /// Why this number? Times out in about a second on a 2017 iMac, Retina 5K, + /// 4.2 GHz Intel Core i7. + /// (It's arbitrary, but will keep the compiler from taking too much time.) + unsigned SwitchCheckingInvocationThreshold = 200000; + + /// Whether to delay checking that benefits from having the entire + /// module parsed, e.g., Objective-C method override checking. + bool DelayWholeModuleChecking = false; + + /// If true, the time it takes to type-check each function will be dumped + /// to llvm::errs(). + bool DebugTimeFunctionBodies = false; + + /// If true, the time it takes to type-check each expression will be + /// dumped to llvm::errs(). + bool DebugTimeExpressions = false; + + /// Indicate that the type checker is checking code that will be + /// immediately executed. This will suppress certain warnings + /// when executing scripts. + bool InImmediateMode = false; + + /// Indicate that the type checker should skip type-checking non-inlinable + /// function bodies. + bool SkipNonInlinableFunctionBodies = false; + + /// + /// Flags for developers + /// + + /// Whether we are debugging the constraint solver. + /// + /// This option enables verbose debugging output from the constraint + /// solver. + bool DebugConstraintSolver = false; + + /// Specific solution attempt for which the constraint + /// solver should be debugged. + unsigned DebugConstraintSolverAttempt = 0; + + /// Line numbers to activate the constraint solver debugger. + /// Should be stored sorted. + llvm::SmallVector DebugConstraintSolverOnLines; + + /// Debug the generic signatures computed by the generic signature builder. + bool DebugGenericSignatures = false; + + /// Triggers llvm fatal_error if typechecker tries to typecheck a decl or an + /// identifier reference with the provided prefix name. + /// This is for testing purposes. + std::string DebugForbidTypecheckPrefix; + + /// The upper bound, in bytes, of temporary data that can be + /// allocated by the constraint solver. + unsigned SolverMemoryThreshold = 512 * 1024 * 1024; + + unsigned SolverBindingThreshold = 1024 * 1024; + + /// The upper bound to number of sub-expressions unsolved + /// before termination of the shrink phrase of the constraint solver. + unsigned SolverShrinkUnsolvedThreshold = 10; + + /// Disable the shrink phase of the expression type checker. + bool SolverDisableShrink = false; + + /// Enable experimental operator designated types feature. + bool EnableOperatorDesignatedTypes = false; + + /// Disable constraint system performance hacks. + bool DisableConstraintSolverPerformanceHacks = false; + + /// Enable constraint solver support for experimental + /// operator protocol designator feature. + bool SolverEnableOperatorDesignatedTypes = false; + }; } // end namespace swift #endif // SWIFT_BASIC_LANGOPTIONS_H diff --git a/include/swift/Basic/Located.h b/include/swift/Basic/Located.h new file mode 100644 index 0000000000000..6b3430b365124 --- /dev/null +++ b/include/swift/Basic/Located.h @@ -0,0 +1,85 @@ +//===--- Located.h - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Provides a currency data type Located that should be used instead +// of std::pair. +// +//===----------------------------------------------------------------------===// + + +#ifndef SWIFT_BASIC_LOCATED_H +#define SWIFT_BASIC_LOCATED_H +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/SourceLoc.h" + +namespace swift { + +/// A currency type for keeping track of items which were found in the source code. +/// Several parts of the compiler need to keep track of a `SourceLoc` corresponding +/// to an item, in case they need to report some diagnostics later. For example, +/// the ClangImporter needs to keep track of where imports were originally written. +/// Located makes it easy to do so while making the code more readable, compared to +/// using `std::pair`. +template +struct Located { + /// The main item whose source location is being tracked. + T Item; + + /// The original source location from which the item was parsed. + SourceLoc Loc; + + Located() : Item(), Loc() {} + + Located(T Item, SourceLoc loc) : Item(Item), Loc(loc) {} + + SWIFT_DEBUG_DUMP; + void dump(raw_ostream &os) const; +}; + +template +bool operator ==(const Located &lhs, const Located &rhs) { + return lhs.Item == rhs.Item && lhs.Loc == rhs.Loc; +} + +} // end namespace swift + +namespace llvm { + +template struct DenseMapInfo; + +template +struct DenseMapInfo> { + + static inline swift::Located getEmptyKey() { + return swift::Located(DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + + static inline swift::Located getTombstoneKey() { + return swift::Located(DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const swift::Located &LocatedVal) { + return combineHashValue(DenseMapInfo::getHashValue(LocatedVal.Item), + DenseMapInfo::getHashValue(LocatedVal.Loc)); + } + + static bool isEqual(const swift::Located &LHS, const swift::Located &RHS) { + return DenseMapInfo::isEqual(LHS.Item, RHS.Item) && + DenseMapInfo::isEqual(LHS.Loc, RHS.Loc); + } +}; +} // namespace llvm + +#endif // SWIFT_BASIC_LOCATED_H diff --git a/include/swift/Basic/Mangler.h b/include/swift/Basic/Mangler.h index 884a5fc9c9629..93c0875f52d0b 100644 --- a/include/swift/Basic/Mangler.h +++ b/include/swift/Basic/Mangler.h @@ -14,6 +14,7 @@ #define SWIFT_BASIC_MANGLER_H #include "swift/Demangling/ManglingUtils.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" @@ -118,18 +119,29 @@ class Mangler { /// Verify that demangling and remangling works. static void verify(StringRef mangledName); - void dump(); + SWIFT_DEBUG_DUMP; /// Appends a mangled identifier string. void appendIdentifier(StringRef ident); + // NOTE: the addSubsitution functions perform the value computation before + // the assignment because there is no sequence point synchronising the + // computation of the value before the insertion of the new key, resulting in + // the computed value being off-by-one causing an undecoration failure during + // round-tripping. void addSubstitution(const void *ptr) { - if (UseSubstitutions) - Substitutions[ptr] = Substitutions.size() + StringSubstitutions.size(); + if (!UseSubstitutions) + return; + + auto value = Substitutions.size() + StringSubstitutions.size(); + Substitutions[ptr] = value; } void addSubstitution(StringRef Str) { - if (UseSubstitutions) - StringSubstitutions[Str] = Substitutions.size() + StringSubstitutions.size(); + if (!UseSubstitutions) + return; + + auto value = Substitutions.size() + StringSubstitutions.size(); + StringSubstitutions[Str] = value; } bool tryMangleSubstitution(const void *ptr); diff --git a/include/swift/Basic/OutputFileMap.h b/include/swift/Basic/OutputFileMap.h index a0fcb8f8cf6ea..8f3235c05e247 100644 --- a/include/swift/Basic/OutputFileMap.h +++ b/include/swift/Basic/OutputFileMap.h @@ -46,11 +46,13 @@ class OutputFileMap { /// Loads an OutputFileMap from the given \p Path into the receiver, if /// possible. - static llvm::Expected loadFromPath(StringRef Path, - StringRef workingDirectory); + static llvm::Expected + loadFromPath(StringRef Path, StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies); static llvm::Expected - loadFromBuffer(StringRef Data, StringRef workingDirectory); + loadFromBuffer(StringRef Data, StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies); /// Loads an OutputFileMap from the given \p Buffer, taking ownership /// of the buffer in the process. @@ -59,7 +61,8 @@ class OutputFileMap { /// the output file map. static llvm::Expected loadFromBuffer(std::unique_ptr Buffer, - StringRef workingDirectory); + StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies); /// Get the map of outputs for the given \p Input, if present in the /// OutputFileMap. (If not present, returns nullptr.) @@ -88,7 +91,8 @@ class OutputFileMap { /// Parses the given \p Buffer and returns either an OutputFileMap or /// error, taking ownership of \p Buffer in the process. static llvm::Expected - parse(std::unique_ptr Buffer, StringRef workingDirectory); + parse(std::unique_ptr Buffer, StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies); }; } // end namespace swift diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index 04ee3253990e9..eb21ea7a29714 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -46,6 +46,14 @@ namespace swift { /// Return true if the given triple represents any simulator. bool tripleIsAnySimulator(const llvm::Triple &triple); + /// Returns true if the given triple represents a macCatalyst environment. + bool tripleIsMacCatalystEnvironment(const llvm::Triple &triple); + + /// Returns true if the given -target triple and -target-variant triple + /// can be zippered. + bool triplesAreValidForZippering(const llvm::Triple &target, + const llvm::Triple &targetVariant); + /// Returns true if the given triple represents an OS that ships with /// ABI-stable swift libraries (eg. in /usr/lib/swift). bool tripleRequiresRPathForSwiftInOS(const llvm::Triple &triple); @@ -91,7 +99,9 @@ namespace swift { /// llvm::Triple::normalize() would not affect it. llvm::Triple getTargetSpecificModuleTriple(const llvm::Triple &triple); - + /// Computes the target triple without version information. + llvm::Triple getUnversionedTriple(const llvm::Triple &triple); + /// Get the Swift runtime version to deploy back to, given a deployment target expressed as an /// LLVM target triple. Optional diff --git a/include/swift/Basic/PrefixMap.h b/include/swift/Basic/PrefixMap.h index 59c1ec8ebe8ea..99ac3307a255d 100644 --- a/include/swift/Basic/PrefixMap.h +++ b/include/swift/Basic/PrefixMap.h @@ -35,6 +35,7 @@ #define SWIFT_BASIC_PREFIXMAP_H #include "swift/Basic/Algorithm.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/type_traits.h" #include "llvm/ADT/ArrayRef.h" @@ -641,7 +642,7 @@ class PrefixMap { [&]() -> const ValueType & { return value; }); } - void dump() const { print(llvm::errs()); } + SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { printOpaquePrefixMap(out, Root, [](raw_ostream &out, void *_node) { diff --git a/include/swift/Basic/Range.h b/include/swift/Basic/Range.h index 94f5661f16434..ba0e92fc93154 100644 --- a/include/swift/Basic/Range.h +++ b/include/swift/Basic/Range.h @@ -126,8 +126,8 @@ class IntRange { *this -= 1; return copy; } - bool operator==(iterator rhs) { return Value == rhs.Value; } - bool operator!=(iterator rhs) { return Value != rhs.Value; } + bool operator==(iterator rhs) const { return Value == rhs.Value; } + bool operator!=(iterator rhs) const { return Value != rhs.Value; } iterator &operator+=(difference_type i) { Value = Traits::addOffset(Value, i); diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h index 5c143c1b6f397..96a7c9e6a6334 100644 --- a/include/swift/Basic/STLExtras.h +++ b/include/swift/Basic/STLExtras.h @@ -638,6 +638,16 @@ inline T accumulate(const Container &C, T init, BinaryOperation op) { return std::accumulate(C.begin(), C.end(), init, op); } +template +inline bool binary_search(const Container &C, const T &value) { + return std::binary_search(C.begin(), C.end(), value); +} + +template +inline bool binary_search(const Container &C, const T &value, BinaryOperation op) { + return std::binary_search(C.begin(), C.end(), value, op); +} + /// Returns true if the range defined by \p mainBegin ..< \p mainEnd starts with /// the same elements as the range defined by \p prefixBegin ..< \p prefixEnd. /// diff --git a/include/swift/Basic/Sanitizers.def b/include/swift/Basic/Sanitizers.def index 73c5ba992cdb5..079f8350b08b1 100644 --- a/include/swift/Basic/Sanitizers.def +++ b/include/swift/Basic/Sanitizers.def @@ -24,5 +24,6 @@ SANITIZER(0, Address, address, asan) SANITIZER(1, Thread, thread, tsan) SANITIZER(2, Undefined, undefined, ubsan) SANITIZER(3, Fuzzer, fuzzer, fuzzer) +SANITIZER(4, Scudo, scudo, scudo) #undef SANITIZER diff --git a/include/swift/Basic/SourceLoc.h b/include/swift/Basic/SourceLoc.h index 7d351d675a6a9..58bdc83f3f6ec 100644 --- a/include/swift/Basic/SourceLoc.h +++ b/include/swift/Basic/SourceLoc.h @@ -17,6 +17,7 @@ #ifndef SWIFT_BASIC_SOURCELOC_H #define SWIFT_BASIC_SOURCELOC_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/StringRef.h" @@ -77,7 +78,7 @@ class SourceLoc { print(OS, SM, Tmp); } - void dump(const SourceManager &SM) const; + SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM)); friend size_t hash_value(SourceLoc loc) { return reinterpret_cast(loc.getOpaquePointerValue()); @@ -128,7 +129,7 @@ class SourceRange { print(OS, SM, Tmp, PrintText); } - void dump(const SourceManager &SM) const; + SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM)); }; /// A half-open character-based source range. @@ -219,7 +220,7 @@ class CharSourceRange { print(OS, SM, Tmp, PrintText); } - void dump(const SourceManager &SM) const; + SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM)); }; } // end namespace swift diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index 484979fc88c03..7cf754731c1ab 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.h @@ -48,6 +48,7 @@ class SourceManager { std::map VirtualFiles; mutable std::pair CachedVFile = {nullptr, nullptr}; + Optional findBufferContainingLocInternal(SourceLoc Loc) const; public: SourceManager(llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem()) @@ -116,6 +117,9 @@ class SourceManager { /// this routine always returns a valid buffer ID. unsigned findBufferContainingLoc(SourceLoc Loc) const; + /// Whether the source location is pointing to any buffer owned by the SourceManager. + bool isOwning(SourceLoc Loc) const; + /// Adds a memory buffer to the SourceManager, taking ownership of it. unsigned addNewSourceBuffer(std::unique_ptr Buffer); @@ -239,15 +243,19 @@ class SourceManager { llvm::Optional resolveOffsetForEndOfLine(unsigned BufferId, unsigned Line) const; + /// Get the length of the line + llvm::Optional getLineLength(unsigned BufferId, unsigned Line) const; + SourceLoc getLocForLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { auto Offset = resolveFromLineCol(BufferId, Line, Col); return Offset.hasValue() ? getLocForOffset(BufferId, Offset.getValue()) : SourceLoc(); } + SourceLoc getLocFromExternalSource(StringRef Path, unsigned Line, unsigned Col); private: const VirtualFile *getVirtualFile(SourceLoc Loc) const; - + unsigned getExternalSourceBufferId(StringRef Path); int getLineOffset(SourceLoc Loc) const { if (auto VFile = getVirtualFile(Loc)) return VFile->LineOffset; diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h index a3901f16029a5..0d39ca074756e 100644 --- a/include/swift/Basic/Statistic.h +++ b/include/swift/Basic/Statistic.h @@ -69,6 +69,7 @@ class SourceFile; class SourceManager; class Stmt; class TypeRepr; +struct FingerprintAndMembers; // There are a handful of cases where the swift compiler can introduce // counter-measurement noise via nondeterminism, especially via @@ -162,6 +163,10 @@ class UnifiedStatsReporter { std::unique_ptr EventProfilers; std::unique_ptr EntityProfilers; + /// Whether we are currently flushing statistics and should not therefore + /// record any additional stats until we've finished. + bool IsFlushingTracesAndProfiles; + void publishAlwaysOnStatsToLLVM(); void printAlwaysOnStatsAndTimers(raw_ostream &OS); diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def index c7b6320d8cb88..e8ba372a18c7d 100644 --- a/include/swift/Basic/Statistics.def +++ b/include/swift/Basic/Statistics.def @@ -138,24 +138,9 @@ FRONTEND_STATISTIC(AST, NumPrefixOperators) /// Number of precedence groups in the AST context. FRONTEND_STATISTIC(AST, NumPrecedenceGroups) -/// Number of qualified lookups into a nominal type. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInNominal) - -/// Number of qualified lookups into a module. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInModule) - -/// Number of qualified lookups into AnyObject. -FRONTEND_STATISTIC(AST, NumLookupQualifiedInAnyObject) - -/// Number of lookups into a module and its imports. -FRONTEND_STATISTIC(AST, NumLookupInModule) - /// Number of local lookups into a module. FRONTEND_STATISTIC(AST, NumModuleLookupValue) -/// Number of unqualified lookups. -FRONTEND_STATISTIC(AST, NumUnqualifiedLookup) - /// Number of local lookups into a module's class members, for /// AnyObject lookup. FRONTEND_STATISTIC(AST, NumModuleLookupClassMember) @@ -230,9 +215,6 @@ FRONTEND_STATISTIC(Sema, NumCyclicOneWayComponentsCollapsed) /// of material loaded from other modules. FRONTEND_STATISTIC(Sema, NumDeclsDeserialized) -/// Number of declarations validated. -FRONTEND_STATISTIC(Sema, NumDeclsValidated) - /// Number of declarations type checked. FRONTEND_STATISTIC(Sema, NumDeclsTypechecked) @@ -258,9 +240,6 @@ FRONTEND_STATISTIC(Sema, NumLazyRequirementSignaturesLoaded) /// Number of lazy iterable declaration contexts constructed. FRONTEND_STATISTIC(Sema, NumLazyIterableDeclContexts) -/// Number of direct member-name lookups performed on nominal types. -FRONTEND_STATISTIC(Sema, NominalTypeLookupDirectCount) - /// Number of member-name lookups that avoided loading all members. FRONTEND_STATISTIC(Sema, NamedLazyMemberLoadSuccessCount) @@ -287,6 +266,10 @@ FRONTEND_STATISTIC(Sema, NumUnloadedLazyIterableDeclContexts) #include "swift/IDE/IDERequestIDZone.def" #undef SWIFT_REQUEST +#define SWIFT_REQUEST(ZONE, NAME, Sig, Caching, LocOptions) FRONTEND_STATISTIC(SILGen, NAME) +#include "swift/AST/SILGenTypeIDZone.def" +#undef SWIFT_REQUEST + /// The next 10 statistics count 5 kinds of SIL entities present /// after the SILGen and SILOpt phases. The entities are functions, /// vtables, witness tables, default witness tables and global diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index f60c53353b4da..08e4c2cfbe23d 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -36,16 +36,10 @@ namespace swift { /// Determine whether the given string can be the name of a member. bool canBeMemberName(StringRef identifier); - /// Describes the kind of preposition a word is. - enum PrepositionKind { - PK_None = 0, - PK_Directional, - PK_Nondirectional - }; - - /// Determine what kind of preposition the given word is, if any, - /// ignoring case. - PrepositionKind getPrepositionKind(StringRef word); + /// Returns true if the given word is one of Swift's known prepositions. + /// + /// This can be faster than getPartOfSpeech(StringRef). + bool isPreposition(StringRef word); /// Describes the part of speech of a particular word. enum class PartOfSpeech { diff --git a/include/swift/Basic/SuccessorMap.h b/include/swift/Basic/SuccessorMap.h index 3dcac45859a97..21edec2884134 100644 --- a/include/swift/Basic/SuccessorMap.h +++ b/include/swift/Basic/SuccessorMap.h @@ -23,6 +23,7 @@ #ifndef SWIFT_BASIC_SUCCESSORMAP_H #define SWIFT_BASIC_SUCCESSORMAP_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -59,7 +60,7 @@ class SuccessorMap { K Begin, End; V Value; - void dump() const { + SWIFT_DEBUG_DUMP { dumpNode(this); } }; @@ -159,11 +160,8 @@ class SuccessorMap { #endif } - void dump() const { - // We call dump() on the object instead of using dumpNode here so - // that the former will be available in a debug build as long as - // something in the program calls dump on the collection. - if (Root) Root->dump(); + SWIFT_DEBUG_DUMP { + if (Root) dumpNode(Root); else llvm::errs() << "(empty)\n"; } diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index d62547531d037..26c17e4627e90 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -83,6 +83,22 @@ struct SupplementaryOutputPaths { /// \sa DependencyGraph std::string ReferenceDependenciesFilePath; + /// The path to which we should output a Swift "unparsed ranges" file. + /// It is valid whenever there are any inputs. + /// + /// "Unparsed ranges" track source ranges in non-primary files whose parsing + /// was skipped + /// (a.k.a. "delayed).\ + /// These files are consumed by the Swift driver (or will be someday) to + /// decide whether a source file needs to be recompiled during a build. + /// + /// \sa swift::emitSwiftRanges + std::string SwiftRangesFilePath; + + /// The path to which we should save the source code of a primary source file + /// to be compiled. Used to diff sources of primary inputs. + std::string CompiledSourceFilePath; + /// Path to a file which should contain serialized diagnostics for this /// frontend invocation. /// @@ -133,6 +149,16 @@ struct SupplementaryOutputPaths { /// \sa swift::emitSwiftInterface std::string ModuleInterfaceOutputPath; + /// The path to a .c file where we should declare $ld$add symbols for those + /// symbols moved to the current module. + /// When symbols are moved to this module, this module declares them as HIDE + /// for the OS versions prior to when the move happened. On the other hand, the + /// original module should ADD them for these OS versions. An executable + /// can choose the right library to link against depending on the deployment target. + /// This is a walk-around that linker directives cannot specify other install + /// name per symbol, we should eventually remove this. + std::string LdAddCFilePath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -142,7 +168,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty(); + ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); } }; } // namespace swift diff --git a/include/swift/Basic/TreeScopedHashTable.h b/include/swift/Basic/TreeScopedHashTable.h index d5a263f40aee5..9867fe0246ca5 100644 --- a/include/swift/Basic/TreeScopedHashTable.h +++ b/include/swift/Basic/TreeScopedHashTable.h @@ -18,6 +18,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Allocator.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/Malloc.h" #include @@ -361,10 +362,9 @@ class TreeScopedHashTable { /// Visit each entry in the map without regard to order. Meant to be used with /// in the debugger in coordination with other dumpers that can dump whatever /// is stored in the map. No-op when asserts are disabled. - LLVM_ATTRIBUTE_DEPRECATED( - void debugVisit(std::function &&func) - const LLVM_ATTRIBUTE_USED, - "Only for use in the debugger"); + SWIFT_DEBUG_HELPER( + void debugVisit(std::function &&func) const + ); /// This inserts the specified key/value at the specified /// (possibly not the current) scope. While it is ok to insert into a scope diff --git a/include/swift/Basic/TypeID.h b/include/swift/Basic/TypeID.h index d77fb1f9c6958..03145896e556a 100644 --- a/include/swift/Basic/TypeID.h +++ b/include/swift/Basic/TypeID.h @@ -37,6 +37,7 @@ enum class Zone : uint8_t { NameLookup = 9, Parse = 8, TypeChecker = 10, + SILGen = 12, // N.B. This is not a formal zone and exists solely to support the unit tests. ArithmeticEvaluator = 255, }; diff --git a/include/swift/Basic/Varint.h b/include/swift/Basic/Varint.h deleted file mode 100644 index 47b9f7f4f34f2..0000000000000 --- a/include/swift/Basic/Varint.h +++ /dev/null @@ -1,110 +0,0 @@ -//===--- Varint.h - Variable length integer encoding ------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// -/// \file Varint.h -/// Defines transformations of integral types to/from variable length -/// 7-bit encoding. -//===----------------------------------------------------------------------===// - -#include -#include - -#include "llvm/ADT/SmallVector.h" - -#ifndef SWIFT_BASIC_VARINT_H -#define SWIFT_BASIC_VARINT_H - -namespace swift { -namespace Varint { - -/// Encode an unsigned integral type to a variable length 7-bit-encoded sequence -/// of bytes. -template -llvm::SmallVector encode( - typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, T - >::type i -) { - llvm::SmallVector bytes; - do { - uint8_t b = i & 0x7F; - i >>= 7; - if (i) - b |= 0x80; - bytes.push_back(b); - } while (i); - return bytes; -} - -/// Encode a signed integral type to a variable length 7-bit-encoded sequence of -/// bytes. -/// -/// This transforms the signed value into an unsigned value and delegates -/// to the unsigned version of `encode`. -template -llvm::SmallVector encode( - typename std::enable_if< - std::is_integral::value && std::is_signed::value, T - >::type i -) { - // Zig-zag encode the signed integer into the unsigned integer type. - // Negative numbers are encoded as unsigned odd numbers in the - // unsigned type, positive numbers are even. This prioritizes the - // smaller numbers around zero, while making it compatible with - // 7-bit encoding: - // -3 -> 5 - // -2 -> 3 - // -1 -> 1 - // 0 -> 0 - // 1 -> 2 - // 2 -> 4 - // 3 -> 6 - typename std::make_unsigned::type z = i < 0 ? ~(i << 1) : (i << 1); - return encode(z); -} - -/// Decode a variable length 7-bit encoded sequence of bytes to an unsigned -/// integer type. -template -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, T ->::type -decode(const uint8_t *bytes) { - size_t i = 0; - size_t shift = 0; - T decoded = 0; - do { - decoded |= T(bytes[i] & 0x7F) << shift; - shift += 7; - } while (bytes[i++] & 0x80); - return decoded; -} - -/// Decode a variable length 7-bit-encoded sequence of bytes to a signed integer -/// type. -/// -/// This delegates to the unsigned version of `decode` and transforms the -/// value back into its signed version. -template -typename std::enable_if< - std::is_integral::value && std::is_signed::value, T ->::type -decode(const uint8_t *bytes) { - auto decoded = decode::type>(bytes); - // Zig-zag decode back into the signed integer type. - return decoded & 1 ? ~(decoded >> 1) : (decoded >> 1); -} - -} // end namespace Varint -} // end namespace swift - -#endif // SWIFT_BASIC_VARINT_H diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index eaa9ecd7a293b..9ac0d73ef7b9b 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -40,6 +40,7 @@ namespace clang { class NamedDecl; class Sema; class TargetInfo; + class Type; class VisibleDeclConsumer; class DeclarationName; } @@ -55,7 +56,6 @@ class DeclContext; class EnumDecl; class ImportDecl; class IRGenOptions; -class LazyResolver; class ModuleDecl; class NominalTypeDecl; class StructDecl; @@ -113,6 +113,17 @@ class ClangImporter final : public ClangModuleLoader { DependencyTracker *tracker, DWARFImporterDelegate *dwarfImporterDelegate); + /// Creates a clone of Clang importer's compiler instance that has been + /// configured for operations on precompiled outputs (either emitting a + /// precompiled header, emitting a precompiled module, or dumping a + /// precompiled module). + /// + /// The caller of this method should set any action-specific invocation + /// options (like FrontendOptions::ProgramAction, input files, and output + /// paths), then create the appropriate FrontendAction and execute it. + std::unique_ptr + cloneCompilerInstanceForPrecompiling(); + public: /// Create a new Clang importer that can import a suitable Clang /// module into the given ASTContext. @@ -163,7 +174,7 @@ class ClangImporter final : public ClangModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -179,7 +190,7 @@ class ClangImporter final : public ClangModuleLoader { /// emits a diagnostic and returns NULL. virtual ModuleDecl *loadModule( SourceLoc importLoc, - ArrayRef> path) + ArrayRef> path) override; /// Determine whether \c overlayDC is within an overlay module for the @@ -334,12 +345,25 @@ class ClangImporter final : public ClangModuleLoader { /// if we need to persist a PCH for later reuse. bool canReadPCH(StringRef PCHFilename); + /// Makes a temporary replica of the ClangImporter's CompilerInstance, reads a + /// module map into the replica and emits a PCM file for one of the modules it + /// declares. Delegates to clang for everything except construction of the + /// replica. + bool emitPrecompiledModule(StringRef moduleMapPath, StringRef moduleName, + StringRef outputPath); + + /// Makes a temporary replica of the ClangImporter's CompilerInstance and + /// dumps information about a PCM file (assumed to be generated by -emit-pcm + /// or in the Swift module cache). Delegates to clang for everything except + /// construction of the replica. + bool dumpPrecompiledModule(StringRef modulePath, StringRef outputPath); + const clang::Module *getClangOwningModule(ClangNode Node) const; bool hasTypedef(const clang::Decl *typeDecl) const; void verifyAllModules() override; - clang::TargetInfo &getTargetInfo() const; + clang::TargetInfo &getTargetInfo() const override; clang::ASTContext &getClangASTContext() const override; clang::Preprocessor &getClangPreprocessor() const override; clang::Sema &getClangSema() const override; @@ -376,7 +400,7 @@ class ClangImporter final : public ClangModuleLoader { /// Given the path of a Clang module, collect the names of all its submodules. /// Calling this function does not load the module. void collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const; /// Given a Clang module, decide whether this module is imported already. @@ -393,6 +417,11 @@ class ClangImporter final : public ClangModuleLoader { /// with -import-objc-header option. getPCHFilename(const ClangImporterOptions &ImporterOptions, StringRef SwiftPCHHash, bool &isExplicit); + + const clang::Type *parseClangFunctionType(StringRef type, + SourceLoc loc) const override; + void printClangType(const clang::Type *type, + llvm::raw_ostream &os) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/ClangImporter/ClangImporterOptions.h b/include/swift/ClangImporter/ClangImporterOptions.h index 3cfa1ba678cfa..14e593daf88a0 100644 --- a/include/swift/ClangImporter/ClangImporterOptions.h +++ b/include/swift/ClangImporter/ClangImporterOptions.h @@ -60,7 +60,10 @@ class ClangImporterOptions { /// Swift code. Normal, /// Set up Clang for backend compilation only. - EmbedBitcode + EmbedBitcode, + /// Set up Clang to emit a precompiled module from a C/Objective-C module + /// map or dump debugging info about a precompiled module. + PrecompiledModule }; /// Controls how Clang is initially set up. @@ -96,6 +99,10 @@ class ClangImporterOptions { /// When set, don't enforce warnings with -Werror. bool DebuggerSupport = false; + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport = false; + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Config.h.in b/include/swift/Config.h.in index 0b5534cf7424f..ec8030b3d1b03 100644 --- a/include/swift/Config.h.in +++ b/include/swift/Config.h.in @@ -6,8 +6,6 @@ #cmakedefine SWIFT_HAVE_WORKING_STD_REGEX 1 -#cmakedefine HAVE_UNICODE_LIBEDIT 1 - #cmakedefine HAVE_WAIT4 1 #cmakedefine HAVE_PROC_PID_RUSAGE 1 diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 5fd70035ced93..69b7d151203d4 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -113,6 +113,8 @@ NODE(ImplEscaping) NODE(ImplConvention) NODE(ImplFunctionAttribute) NODE(ImplFunctionType) +NODE(ImplImpliedSubstitutions) +NODE(ImplSubstitutions) CONTEXT_NODE(ImplicitClosure) NODE(ImplParameter) NODE(ImplResult) diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 24fa0007679a2..c17f157976aa8 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -23,11 +23,6 @@ //#define NODE_FACTORY_DEBUGGING -#ifdef NODE_FACTORY_DEBUGGING -#include -#endif - - using namespace swift::Demangle; using llvm::StringRef; @@ -86,7 +81,7 @@ class NodeFactory { NodeFactory() { #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "## New NodeFactory\n"; + fprintf(stderr, "%s## New NodeFactory\n", indent().c_str()); nestingLevel++; #endif } @@ -96,8 +91,7 @@ class NodeFactory { /// Only if this memory overflows, the factory begins to malloc. void providePreallocatedMemory(char *Memory, size_t Size) { #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "++ provide preallocated memory, size = " - << Size << '\n'; + fprintf(stderr, "%s++ provide preallocated memory, size = %zu\n", indent().c_str(), Size); #endif assert(!CurPtr && !End && !CurrentSlab); CurPtr = Memory; @@ -116,8 +110,7 @@ class NodeFactory { CurPtr = BorrowFrom.CurPtr; End = BorrowFrom.End; #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "++ borrow memory, size = " - << (End - CurPtr) << '\n'; + fprintf(stderr, "%s++ borrow memory, size = %zu\n", indent().c_str(), (End - CurPtr)); #endif } @@ -125,8 +118,7 @@ class NodeFactory { freeSlabs(CurrentSlab); #ifdef NODE_FACTORY_DEBUGGING nestingLevel--; - std::cerr << indent() << "## Delete NodeFactory: allocated memory = " - << allocatedMemory << '\n'; + fprintf(stderr, "%s## Delete NodeFactory: allocated memory = %zu\n", indent().c_str(), allocatedMemory) #endif if (BorrowedFrom) { BorrowedFrom->isBorrowed = false; @@ -141,8 +133,7 @@ class NodeFactory { size_t ObjectSize = NumObjects * sizeof(T); CurPtr = align(CurPtr, alignof(T)); #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "alloc " << ObjectSize << ", CurPtr = " - << (void *)CurPtr << "\n"; + fprintf(stderr, "%salloc %zu, CurPtr = %p\n", indent().c_str(), ObjectSize, (void *)CurPtr) allocatedMemory += ObjectSize; #endif @@ -163,9 +154,8 @@ class NodeFactory { End = (char *)newSlab + AllocSize; assert(CurPtr + ObjectSize <= End); #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "** new slab " << newSlab << ", allocsize = " - << AllocSize << ", CurPtr = " << (void *)CurPtr - << ", End = " << (void *)End << "\n"; + fprintf(stderr, "%s** new slab %p, allocsize = %zu, CurPtr = %p, End = %p\n", + indent().c_str(), newSlab, AllocSize, (void *)CurPtr, (void *)End); #endif } T *AllocatedObj = (T *)CurPtr; @@ -189,9 +179,8 @@ class NodeFactory { size_t AdditionalAlloc = MinGrowth * sizeof(T); #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "realloc: capacity = " << Capacity - << " (size = " << OldAllocSize << "), growth = " << MinGrowth - << " (size = " << AdditionalAlloc << ")\n"; + fprintf(stderr, "%srealloc: capacity = %d (size = %zu), growth = %zu (size = %zu)\n", + indent().c_str(), Capacity, OldAllocSize, MinGrowth, AdditionalAlloc); #endif if ((char *)Objects + OldAllocSize == CurPtr && CurPtr + AdditionalAlloc <= End) { @@ -200,7 +189,7 @@ class NodeFactory { CurPtr += AdditionalAlloc; Capacity += MinGrowth; #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "** can grow: " << (void *)CurPtr << '\n'; + fprintf(stderr, "%s** can grow: %p\n", indent().c_str(), (void *)CurPtr); allocatedMemory += AdditionalAlloc; #endif return; @@ -574,7 +563,6 @@ class Demangler : public NodeFactory { NodePointer demangleGenericType(); NodePointer demangleValueWitness(); - NodePointer demangleObjCTypeName(); NodePointer demangleTypeMangling(); NodePointer demangleSymbolicReference(unsigned char rawKind, const void *at); diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index 4dae1eb336ba0..3f4e5c4edec14 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -168,6 +168,26 @@ class CompileJobAction : public JobAction { static bool classof(const Action *A) { return A->getKind() == Action::Kind::CompileJob; } + + /// Return a _single_ TY_Swift InputAction, if one exists; + /// if 0 or >1 such inputs exist, return nullptr. + const InputAction *findSingleSwiftInput() const { + auto Inputs = getInputs(); + const InputAction *IA = nullptr; + for (auto const *I : Inputs) { + if (auto const *S = dyn_cast(I)) { + if (S->getType() == file_types::TY_Swift) { + if (IA == nullptr) { + IA = S; + } else { + // Already found one, two is too many. + return nullptr; + } + } + } + } + return IA; + } }; class InterpretJobAction : public JobAction { diff --git a/include/swift/Driver/DependencyGraph.h b/include/swift/Driver/CoarseGrainedDependencyGraph.h similarity index 80% rename from include/swift/Driver/DependencyGraph.h rename to include/swift/Driver/CoarseGrainedDependencyGraph.h index 0bd5ea99388a0..858740a2d0d55 100644 --- a/include/swift/Driver/DependencyGraph.h +++ b/include/swift/Driver/CoarseGrainedDependencyGraph.h @@ -1,4 +1,4 @@ -//===--- DependencyGraph.h - Track intra-module dependencies ----*- C++ -*-===// +//===- CoarseGrainedDependencyGraph.h - Track intra-module dependencies -*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef SWIFT_DRIVER_DEPENDENCYGRAPH_H -#define SWIFT_DRIVER_DEPENDENCYGRAPH_H +#ifndef SWIFT_DRIVER_COARSEGRAINEDDEPENDENCYGRAPH_H +#define SWIFT_DRIVER_COARSEGRAINEDDEPENDENCYGRAPH_H #include "swift/AST/DiagnosticEngine.h" #include "swift/Basic/LLVM.h" @@ -36,15 +36,15 @@ namespace swift { class UnifiedStatsReporter; -/// The non-templated implementation of DependencyGraph. +/// The non-templated implementation of CoarseGrainedDependencyGraph. /// -/// \see DependencyGraph -class DependencyGraphImpl { +/// \see CoarseGrainedDependencyGraph +class CoarseGrainedDependencyGraphImpl { public: /// Possible dependency kinds. /// - /// Clients of DependencyGraph should have no reason to use this type. - /// It is only used in the implementation. + /// Clients of CoarseGrainedDependencyGraph should have no reason to use this + /// type. It is only used in the implementation. enum class DependencyKind : uint8_t; /// Describes the result of loading a dependency file for a particular node. @@ -62,15 +62,17 @@ class DependencyGraphImpl { AffectsDownstream }; - /// The non-templated implementation of DependencyGraph::MarkTracer. + /// The non-templated implementation of + /// CoarseGrainedDependencyGraph::MarkTracer. /// - /// \see DependencyGraph::MarkTracer + /// \see CoarseGrainedDependencyGraph::MarkTracer class MarkTracerImpl { class Entry; llvm::DenseMap> Table; UnifiedStatsReporter *Stats; - friend class DependencyGraphImpl; + friend class CoarseGrainedDependencyGraphImpl; + protected: explicit MarkTracerImpl(UnifiedStatsReporter *Stats); ~MarkTracerImpl(); @@ -148,23 +150,27 @@ class DependencyGraphImpl { LoadResult loadFromString(const void *node, StringRef data); LoadResult loadFromPath(const void *node, StringRef path); - void addIndependentNode(const void *node) { + void registerJob(const void *node) { bool newlyInserted = Provides.insert({node, {}}).second; assert(newlyInserted && "node is already in graph"); (void)newlyInserted; } - /// See DependencyGraph::markTransitive. + /// See CoarseGrainedDependencyGraph::markTransitive. + + std::vector markTransitive(const void *node, MarkTracerImpl *tracer = nullptr); - void markTransitive(SmallVectorImpl &visited, - const void *node, MarkTracerImpl *tracer = nullptr); bool markIntransitive(const void *node) { assert(Provides.count(node) && "node is not in the graph"); return Marked.insert(node).second; } - void markExternal(SmallVectorImpl &visited, - StringRef externalDependency); + std::vector markExternal(StringRef externalDependency); +public: + void forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( + StringRef externalDependency, function_ref fn); + +protected: bool isMarked(const void *node) const { assert(Provides.count(node) && "node is not in the graph"); return Marked.count(node); @@ -191,21 +197,23 @@ class DependencyGraphImpl { /// The graph also supports a "mark" operation, which is intended to track /// nodes that have been not just visited but transitively marked through. template -class DependencyGraph : public DependencyGraphImpl { +class CoarseGrainedDependencyGraph : public CoarseGrainedDependencyGraphImpl { using Traits = llvm::PointerLikeTypeTraits; static_assert(Traits::NumLowBitsAvailable >= 0, "not a pointer-like type"); - static void copyBack(SmallVectorImpl &result, - ArrayRef rawNodes) { - result.reserve(result.size() + rawNodes.size()); + static std::vector copyBack(ArrayRef rawNodes) { + std::vector result; + result.reserve(rawNodes.size()); std::transform(rawNodes.begin(), rawNodes.end(), std::back_inserter(result), [](const void *rawNode) { return Traits::getFromVoidPointer(const_cast(rawNode)); }); + return result; } public: - /// Traces the graph traversal performed in DependencyGraph::markTransitive. + /// Traces the graph traversal performed in + /// CoarseGrainedDependencyGraph::markTransitive. /// /// This is intended to be a debugging aid. class MarkTracer : public MarkTracerImpl { @@ -233,10 +241,10 @@ class DependencyGraph : public DependencyGraphImpl { /// If \p node has already been marked, only its outgoing edges are updated. /// The third argument is ignored here, but must be present so that the same /// call site can polymorphically call \ref - /// experimental_dependencies::ModuleDepGraph::loadFromPath + /// fine_grained_dependencies::ModuleDepGraph::loadFromPath LoadResult loadFromPath(T node, StringRef path, DiagnosticEngine &) { - return DependencyGraphImpl::loadFromPath(Traits::getAsVoidPointer(node), - path); + return CoarseGrainedDependencyGraphImpl::loadFromPath( + Traits::getAsVoidPointer(node), path); } /// Load "depends" and "provides" data for \p node from a plain string. @@ -245,16 +253,16 @@ class DependencyGraph : public DependencyGraphImpl { /// /// \sa loadFromPath LoadResult loadFromString(T node, StringRef data) { - return DependencyGraphImpl::loadFromString(Traits::getAsVoidPointer(node), - data); + return CoarseGrainedDependencyGraphImpl::loadFromString( + Traits::getAsVoidPointer(node), data); } /// Adds \p node to the dependency graph without any connections. /// /// This can be used for new nodes that may be updated later. - void addIndependentNode(T node) { - return - DependencyGraphImpl::addIndependentNode(Traits::getAsVoidPointer(node)); + void registerJob(T node) { + return CoarseGrainedDependencyGraphImpl::registerJob( + Traits::getAsVoidPointer(node)); } /// Marks \p node and all nodes that depend on \p node, and places any nodes @@ -278,23 +286,20 @@ class DependencyGraph : public DependencyGraphImpl { /// /// The traversal routines use /// \p visited to avoid endless recursion. - template - void markTransitive(SmallVector &visited, T node, + std::vector markTransitive(T node, MarkTracer *tracer = nullptr) { - SmallVector rawMarked; - DependencyGraphImpl::markTransitive(rawMarked, - Traits::getAsVoidPointer(node), - tracer); + std::vector rawMarked = + CoarseGrainedDependencyGraphImpl::markTransitive( + Traits::getAsVoidPointer(node), tracer); // FIXME: How can we avoid this copy? - copyBack(visited, rawMarked); + return copyBack(rawMarked); } - template - void markExternal(SmallVector &visited, StringRef externalDependency) { - SmallVector rawMarked; - DependencyGraphImpl::markExternal(rawMarked, externalDependency); + std::vector + markExternal(StringRef externalDependency) { + const auto rawMarked = CoarseGrainedDependencyGraphImpl::markExternal(externalDependency); // FIXME: How can we avoid this copy? - copyBack(visited, rawMarked); + return copyBack(rawMarked); } /// Marks \p node without marking any dependencies. @@ -303,13 +308,14 @@ class DependencyGraph : public DependencyGraphImpl { /// /// \sa #markTransitive bool markIntransitive(T node) { - return - DependencyGraphImpl::markIntransitive(Traits::getAsVoidPointer(node)); + return CoarseGrainedDependencyGraphImpl::markIntransitive( + Traits::getAsVoidPointer(node)); } /// Returns true if \p node has been marked (directly or transitively). bool isMarked(T node) const { - return DependencyGraphImpl::isMarked(Traits::getAsVoidPointer(node)); + return CoarseGrainedDependencyGraphImpl::isMarked( + Traits::getAsVoidPointer(node)); } }; diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h index fc67cef99e55b..7bcf29cc7875d 100644 --- a/include/swift/Driver/Compilation.h +++ b/include/swift/Driver/Compilation.h @@ -19,6 +19,8 @@ #include "swift/Basic/ArrayRefView.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/LangOptions.h" +#include "swift/Basic/NullablePtr.h" #include "swift/Basic/OutputFileMap.h" #include "swift/Basic/Statistic.h" #include "swift/Driver/Driver.h" @@ -73,7 +75,57 @@ enum class PreserveOnSignal : bool { Yes }; +using CommandSet = llvm::SmallPtrSet; + class Compilation { +public: + class IncrementalSchemeComparator { + const bool EnableIncrementalBuildWhenConstructed; + const bool &EnableIncrementalBuild; + const bool EnableSourceRangeDependencies; + + /// If not empty, the path to use to log the comparision. + const StringRef CompareIncrementalSchemesPath; + + const unsigned SwiftInputCount; + + public: + std::string WhyIncrementalWasDisabled = ""; + + private: + DiagnosticEngine &Diags; + + CommandSet JobsWithoutRanges; + CommandSet JobsWithRanges; + + unsigned CompileStagesWithoutRanges = 0; + unsigned CompileStagesWithRanges = 0; + + public: + IncrementalSchemeComparator(const bool &EnableIncrementalBuild, + bool EnableSourceRangeDependencies, + const StringRef CompareIncrementalSchemesPath, + unsigned SwiftInputCount, + DiagnosticEngine &Diags) + : EnableIncrementalBuildWhenConstructed(EnableIncrementalBuild), + EnableIncrementalBuild(EnableIncrementalBuild), + EnableSourceRangeDependencies(EnableSourceRangeDependencies), + CompareIncrementalSchemesPath(CompareIncrementalSchemesPath), + SwiftInputCount(SwiftInputCount), Diags(Diags) {} + + /// Record scheduled jobs in support of the + /// -compare-incremental-schemes[-path] options + void update(const CommandSet &withoutRangeJobs, + const CommandSet &withRangeJobs); + + /// Write the information for the -compare-incremental-schemes[-path] + /// options + void outputComparison() const; + + private: + void outputComparison(llvm::raw_ostream &) const; + }; + public: /// The filelist threshold value to pass to ensure file lists are never used static const size_t NEVER_USE_FILELIST = SIZE_MAX; @@ -206,20 +258,48 @@ class Compilation { /// limit filelists will be used. size_t FilelistThreshold; + /// Because each frontend job outputs the same info in its .d file, only do it + /// on the first job that actually runs. Write out dummies for the rest of the + /// jobs. This hack saves a lot of time in the build system when incrementally + /// building a project with many files. Record if a scheduled job has already + /// added -emit-dependency-path. + bool HaveAlreadyAddedDependencyPath = false; + +public: + /// When set, only the first scheduled frontend job gets the argument needed + /// to produce a make-style dependency file. The other jobs create dummy files + /// in the driver. This hack speeds up incremental compilation by reducing the + /// time for the build system to read each dependency file, which are all + /// identical. This optimization can be disabled by passing + /// -disable-only-one-dependency-file on the command line. + const bool OnlyOneDependencyFile; + +private: /// Scaffolding to permit experimentation with finer-grained dependencies and /// faster rebuilds. - const bool EnableExperimentalDependencies; + const bool EnableFineGrainedDependencies; + + /// Is the parser recording token hashes for each type body? + const bool EnableTypeFingerprints; /// Helpful for debugging, but slows down the driver. So, only turn on when /// needed. - const bool VerifyExperimentalDependencyGraphAfterEveryImport; + const bool VerifyFineGrainedDependencyGraphAfterEveryImport; /// Helpful for debugging, but slows down the driver. So, only turn on when /// needed. - const bool EmitExperimentalDependencyDotFileAfterEveryImport; + const bool EmitFineGrainedDependencyDotFileAfterEveryImport; + + /// Experiment with intrafile dependencies + const bool FineGrainedDependenciesIncludeIntrafileOnes; - /// Experiment with inter-file dependencies - const bool ExperimentalDependenciesIncludeIntrafileOnes; + /// Experiment with source-range-based dependencies + const bool EnableSourceRangeDependencies; +public: + /// Will contain a comparator if an argument demands it. + Optional IncrementalComparator; + +private: template static T *unwrap(const std::unique_ptr &p) { return p.get(); @@ -250,10 +330,17 @@ class Compilation { bool SaveTemps = false, bool ShowDriverTimeCompilation = false, std::unique_ptr Stats = nullptr, - bool EnableExperimentalDependencies = false, - bool VerifyExperimentalDependencyGraphAfterEveryImport = false, - bool EmitExperimentalDependencyDotFileAfterEveryImport = false, - bool ExperimentalDependenciesIncludeIntrafileOnes = false); + bool OnlyOneDependencyFile = false, + bool EnableFineGrainedDependencies + = LangOptions().EnableFineGrainedDependencies, + bool EnableTypeFingerprints = + LangOptions().EnableTypeFingerprints, + bool VerifyFineGrainedDependencyGraphAfterEveryImport = false, + bool EmitFineGrainedDependencyDotFileAfterEveryImport = false, + bool FineGrainedDependenciesIncludeIntrafileOnes = false, + bool EnableSourceRangeDependencies = false, + bool CompareIncrementalSchemes = false, + StringRef CompareIncrementalSchemesPath = ""); // clang-format on ~Compilation(); @@ -285,6 +372,11 @@ class Compilation { } Job *addJob(std::unique_ptr J); + /// To send job list to places that don't truck in fancy array views. + std::vector getJobsSimply() const { + return std::vector(getJobs().begin(), getJobs().end()); + } + void addTemporaryFile(StringRef file, PreserveOnSignal preserve = PreserveOnSignal::No) { TempFilePaths[file] = preserve; @@ -305,24 +397,28 @@ class Compilation { bool getIncrementalBuildEnabled() const { return EnableIncrementalBuild; } - void disableIncrementalBuild() { - EnableIncrementalBuild = false; + void disableIncrementalBuild(Twine why); + + bool getEnableFineGrainedDependencies() const { + return EnableFineGrainedDependencies; } - bool getEnableExperimentalDependencies() const { - return EnableExperimentalDependencies; + bool getEnableTypeFingerprints() const { return EnableTypeFingerprints; } + + bool getVerifyFineGrainedDependencyGraphAfterEveryImport() const { + return VerifyFineGrainedDependencyGraphAfterEveryImport; } - bool getVerifyExperimentalDependencyGraphAfterEveryImport() const { - return VerifyExperimentalDependencyGraphAfterEveryImport; + bool getEmitFineGrainedDependencyDotFileAfterEveryImport() const { + return EmitFineGrainedDependencyDotFileAfterEveryImport; } - bool getEmitExperimentalDependencyDotFileAfterEveryImport() const { - return EmitExperimentalDependencyDotFileAfterEveryImport; + bool getFineGrainedDependenciesIncludeIntrafileOnes() const { + return FineGrainedDependenciesIncludeIntrafileOnes; } - bool getExperimentalDependenciesIncludeIntrafileOnes() const { - return ExperimentalDependenciesIncludeIntrafileOnes; + bool getEnableSourceRangeDependencies() const { + return EnableSourceRangeDependencies; } bool getBatchModeEnabled() const { @@ -358,6 +454,16 @@ class Compilation { return FilelistThreshold; } + /// Since every make-style dependency file contains + /// the same information, incremental builds are sped up by only emitting one + /// of those files. Since the build system expects to see the files existing, + /// create dummy files for those jobs that don't emit real dependencies. + /// \param path The dependency file path + /// \param addDependencyPath A function to add an -emit-dependency-path + /// argument + void addDependencyPathOrCreateDummy(StringRef path, + function_ref addDependencyPath); + UnifiedStatsReporter *getStatsReporter() const { return Stats.get(); } @@ -421,6 +527,22 @@ class Compilation { } } + /// How many .swift input files? + unsigned countSwiftInputs() const; + + /// Unfortunately the success or failure of a Swift compilation is currently + /// sensitive to the order in which files are processed, at least in terms of + /// the order of processing extensions (and likely other ways we haven't + /// discovered yet). So long as this is true, we need to make sure any batch + /// job we build names its inputs in an order that's a subsequence of the + /// sequence of inputs the driver was initially invoked with. + /// + /// Also use to write out information in a consistent order. + template + void sortJobsToMatchCompilationInputs( + const JobCollection &unsortedJobs, + SmallVectorImpl &sortedJobs) const; + private: /// Perform all jobs. /// diff --git a/include/swift/Driver/Driver.h b/include/swift/Driver/Driver.h index 5592f2d291d23..d927f3908a14f 100644 --- a/include/swift/Driver/Driver.h +++ b/include/swift/Driver/Driver.h @@ -158,7 +158,8 @@ class Driver { Interactive, // swift Batch, // swiftc AutolinkExtract, // swift-autolink-extract - SwiftIndent // swift-indent + SwiftIndent, // swift-indent + SymbolGraph // swift-symbolgraph }; class InputInfoMap; @@ -297,7 +298,8 @@ class Driver { /// Construct the OutputFileMap for the driver from the given arguments. Optional buildOutputFileMap(const llvm::opt::DerivedArgList &Args, - StringRef workingDirectory) const; + StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies) const; /// Add top-level Jobs to Compilation \p C for the given \p Actions and /// OutputInfo. diff --git a/include/swift/Driver/DriverIncrementalRanges.h b/include/swift/Driver/DriverIncrementalRanges.h new file mode 100644 index 0000000000000..062fdc46ee18a --- /dev/null +++ b/include/swift/Driver/DriverIncrementalRanges.h @@ -0,0 +1,124 @@ +//===---------------- DriverIncrementalRanges.h ------------------*- C++-*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// These are the declarations for managing serializable source locations so that +// the driver can implement incremental compilation based on +// source ranges. + +#ifndef SWIFT_DRIVER_DRIVERINCREMENTALRANGES_H +#define SWIFT_DRIVER_DRIVERINCREMENTALRANGES_H + +#include "swift/AST/IncrementalRanges.h" +#include "swift/Basic/FileTypes.h" +#include "swift/Driver/SourceComparator.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace swift { +namespace driver { +class Job; +class Compilation; +} +namespace incremental_ranges { + +//============================================================================== +// MARK: SourceRangeBasedInfo +//============================================================================== + +/// A per-primary collection of information about its source ranges. + +class SourceRangeBasedInfo { + const std::string primaryInputPath; + const SwiftRangesFileContents swiftRangesFileContents; + /// All changed ranges in the primary as computed by the diff in the driver + /// Both relative to the previously-compiled and the current source. + const SourceComparator::LRRanges changedRanges; + /// All of the non-local changes in the previously-compiled code: those + /// residing outside function bodies. + /// (We only have and only need function-body ranges for the + /// previously-compiled source.) + const Ranges nonlocalChangedRanges; + + //============================================================================== + // MARK: construction + //============================================================================== + +public: + static Optional + loadInfoForOneJob(const driver::Job *cmd, + const bool showIncrementalBuildDecisions, + DiagnosticEngine &diags); + + SourceRangeBasedInfo(SourceRangeBasedInfo &&); + +private: + SourceRangeBasedInfo(StringRef primaryInputPath, SwiftRangesFileContents &&, + SourceComparator::LRRanges &&changedRanges, + Ranges &&nonlocalChangedRanges); + + /// Using supplied paths, interrogate the supplementary outputs and update the + /// two references. Return None if a file was missing or corrupted. + static Optional + loadInfoForOnePrimary(StringRef primaryPath, StringRef compiledSourcePath, + StringRef swiftRangesPath, + bool showIncrementalBuildDecisions, + DiagnosticEngine &diags); + + /// Return None for error + static Optional + loadSwiftRangesFileContents(StringRef swiftRangesPath, StringRef primaryPath, + const bool showIncrementalBuildDecisions, + DiagnosticEngine &); + + /// Return None for error + /// If no error returns ranges of changes from both lhs and rhs. + /// Lhs used for scheduling jobs, rhs just for dumping and testing. + static Optional + loadChangedRanges(StringRef compiledSourcePath, StringRef primaryPath, + const bool showIncrementalBuildDecisions, + DiagnosticEngine &); + + static Ranges + computeNonlocalChangedRanges(const SwiftRangesFileContents &, + const SourceComparator::LRRanges &); + + //============================================================================== + // MARK: scheduling jobs + //============================================================================== +public: + bool + didInputChangeAtAll(DiagnosticEngine &, + function_ref noteBuilding) const; + bool didInputChangeNonlocally( + DiagnosticEngine &, + function_ref noteInitiallyCascading) const; + +private: + static Optional isFileNewerThan(StringRef lhs, StringRef rhs, + DiagnosticEngine&); + + + //============================================================================== + // MARK: printing + //============================================================================== + +public: + void dump(bool dumpCompiledSourceDiffs = true, + bool dumpSwiftRanges = true) const; + +private: + void dumpChangedRanges() const; +}; + +} // namespace incremental_ranges +} // namespace swift + +#endif /* SWIFT_DRIVER_DRIVERINCREMENTALRANGES_H */ diff --git a/include/swift/Driver/ExperimentalDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h similarity index 58% rename from include/swift/Driver/ExperimentalDependencyDriverGraph.h rename to include/swift/Driver/FineGrainedDependencyDriverGraph.h index 4a6683e918bc4..9d1e9954d2c15 100644 --- a/include/swift/Driver/ExperimentalDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -1,4 +1,4 @@ -//===--- ExperimentalDependencyModuleDepGraph.h ------------------*- C++-*-===// +//===---- FineGrainedDependencyModuleDepGraph.h ------------------*- C++-*-===// // // This source file is part of the Swift.org open source project // @@ -10,13 +10,14 @@ // //===----------------------------------------------------------------------===// -#ifndef ExperimentalDependencyGraph_h -#define ExperimentalDependencyGraph_h +#ifndef SWIFT_DRIVER_FINE_GRAINED_DEPENDENCY_DRIVER_GRAPH_H +#define SWIFT_DRIVER_FINE_GRAINED_DEPENDENCY_DRIVER_GRAPH_H -#include "swift/AST/ExperimentalDependencies.h" +#include "swift/AST/FineGrainedDependencies.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" -#include "swift/Driver/DependencyGraph.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" #include "swift/Driver/Job.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -33,11 +34,11 @@ #include #include -// Declarations for the portion experimental dependency system used by the +// Declarations for the portion fine-grained dependency system used by the // driver. namespace swift { -namespace experimental_dependencies { +namespace fine_grained_dependencies { //============================================================================== // MARK: ModuleDepGraphNode @@ -47,17 +48,24 @@ namespace experimental_dependencies { /// Keep separate type from Node for type-checking. class ModuleDepGraphNode : public DepGraphNode { - /// The swiftDeps file that holds this entity. + /// The swiftDeps file that holds this entity iff this is a provides node. /// If more than one source file has the same DependencyKey, then there - /// will be one node for each in the driver. + /// will be one node for each in the driver, distinguished by this field. Optional swiftDeps; + /// When finding transitive dependents, this node has been traversed. + bool hasBeenTracedAsADependent = false; + public: ModuleDepGraphNode(const DependencyKey &key, Optional fingerprint, Optional swiftDeps) : DepGraphNode(key, fingerprint), swiftDeps(swiftDeps) {} + bool getHasBeenTraced() const { return hasBeenTracedAsADependent; } + void setHasBeenTraced() { hasBeenTracedAsADependent = true; } + void clearHasBeenTraced() { hasBeenTracedAsADependent = false; } + /// Integrate \p integrand's fingerprint into \p dn. /// \returns true if there was a change requiring recompilation. bool integrateFingerprintFrom(const SourceFileDepGraphNode *integrand) { @@ -75,26 +83,18 @@ class ModuleDepGraphNode : public DepGraphNode { const Optional &getSwiftDeps() const { return swiftDeps; } - bool assertImplementationMustBeInAFile() const { - assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && - "Implementations must be in some file."); - return true; + std::string getSwiftDepsOrEmpty() const { + return getSwiftDeps().getValueOr(std::string()); } - std::string humanReadableName() const { - StringRef where = - !getSwiftDeps().hasValue() - ? "" - : llvm::sys::path::filename(getSwiftDeps().getValue()); - return DepGraphNode::humanReadableName(where); + std::string getSwiftDepsForMapKey() const { + // Use the empty string for nodes whose source file is unknown, + // i.e. depends. (Known depends are represented by arcs, not nodes.) + return getSwiftDepsOrEmpty(); } - void dump() const; - - bool assertProvidedEntityMustBeInAFile() const { - assert((getSwiftDeps().hasValue() || !getKey().isImplementation()) && - "Implementations must be in some file."); - return true; + const std::string &getSwiftDepsOfProvides() const { + return getSwiftDeps().getValue(); } /// Nodes can move from file to file when the driver reads the result of a @@ -102,21 +102,35 @@ class ModuleDepGraphNode : public DepGraphNode { void setSwiftDeps(Optional s) { swiftDeps = s; } bool getIsProvides() const { return getSwiftDeps().hasValue(); } -}; -/// A placeholder allowing the experimental system to fit into the driver -/// without changing as much code. -class DependencyGraphImpl { -public: - /// Use the status quo LoadResult for now. - using LoadResult = typename swift::DependencyGraphImpl::LoadResult; + /// Return true if this node describes a definition for which the job is known + bool isDefinedInAKnownFile() const { return getIsProvides(); } + + bool doesNodeProvideAnInterface() const { + return getKey().isInterface() && getIsProvides(); + } + + bool assertImplementationMustBeInAFile() const { + assert((isDefinedInAKnownFile() || !getKey().isImplementation()) && + "Implementations must be in some file."); + return true; + } + + std::string humanReadableName() const { + StringRef where = !getIsProvides() + ? "" + : llvm::sys::path::filename(getSwiftDepsOfProvides()); + return DepGraphNode::humanReadableName(where); + } + + SWIFT_DEBUG_DUMP; }; //============================================================================== // MARK: ModuleDepGraph //============================================================================== -/// See \ref Node in ExperimentalDependencies.h +/// See \ref Node in FineGrainedDependencies.h class ModuleDepGraph { /// Find nodes, first by the swiftDeps file, then by key. @@ -156,20 +170,6 @@ class ModuleDepGraph { // Supports requests from the driver to getExternalDependencies. std::unordered_set externalDependencies; - /// The new version of "Marked." - /// Record cascading jobs by swiftDepsFilename because that's what - /// nodes store directly. - /// - /// The status quo system uses "cascade" for the following: - /// Def1 -> def2 -> def3, where arrows are uses, so 3 depends on 2 which - /// depends on 1. The first use is said to "cascade" if when def1 changes, - /// def3 is dirtied. - /// TODO: Move cascadingJobs out of the graph, ultimately. - /// If marked, any Job that depends on me must be rebuilt after compiling me - /// if I have changed. - - std::unordered_set cascadingJobs; - /// Keyed by swiftdeps filename, so we can get back to Jobs. std::unordered_map jobsBySwiftDeps; @@ -185,13 +185,17 @@ class ModuleDepGraph { /// files for the same name distinct, keep a sequence number for each name. std::unordered_map dotFileSequenceNumber; - const bool verifyExperimentalDependencyGraphAfterEveryImport; - const bool emitExperimentalDependencyDotFileAfterEveryImport; + const bool verifyFineGrainedDependencyGraphAfterEveryImport; + const bool emitFineGrainedDependencyDotFileAfterEveryImport; + + const bool EnableTypeFingerprints; - /// If tracing dependencies, holds the current node traversal path + /// If tracing dependencies, holds a vector used to hold the current path + /// def - use/def - use/def - ... Optional> currentPathIfTracing; - /// If tracing dependencies, record the node sequence + /// If tracing dependencies, holds the sequence of defs used to get to the job + /// that is the key std::unordered_multimap> dependencyPathsToJobs; @@ -199,10 +203,14 @@ class ModuleDepGraph { /// For helping with performance tuning, may be null: UnifiedStatsReporter *const stats; + //============================================================================== + // MARK: ModuleDepGraph - mutating dependencies + //============================================================================== + /// Encapsulate the invariant between where the node resides in /// nodesBySwiftDepsFile and the swiftDeps node instance variable here. void addToMap(ModuleDepGraphNode *n) { - nodeMap.insert(n->getSwiftDeps().getValueOr(std::string()), n->getKey(), n); + nodeMap.insert(n->getSwiftDepsForMapKey(), n->getKey(), n); } /// When integrating a SourceFileDepGraph, there might be a node representing @@ -221,8 +229,7 @@ class ModuleDepGraph { /// Remove node from nodeMap, check invariants. ModuleDepGraphNode *eraseNodeFromMap(ModuleDepGraphNode *nodeToErase) { ModuleDepGraphNode *nodeActuallyErased = nodeMap.findAndErase( - nodeToErase->getSwiftDeps().getValueOr(std::string()), - nodeToErase->getKey()); + nodeToErase->getSwiftDepsForMapKey(), nodeToErase->getKey()); (void)nodeActuallyErased; assert( nodeToErase == nodeActuallyErased || @@ -230,6 +237,38 @@ class ModuleDepGraph { return nodeToErase; } + void eraseUsesOfNode(ModuleDepGraphNode *nodeToErase) { + for (auto &defAndUses : usesByDef) + defAndUses.second.erase(nodeToErase); + } + + void eraseNodeFromCurrentPathIfTracing(ModuleDepGraphNode *nodeToErase) { + if (currentPathIfTracing) + eraseNodeFromVector(currentPathIfTracing.getValue(), nodeToErase); + } + + void eraseNodeFromDependencyPathToJobs(ModuleDepGraphNode *nodeToErase) { + for (auto &jobAndPath : dependencyPathsToJobs) + eraseNodeFromVector(jobAndPath.second, nodeToErase); + } + + static void eraseNodeFromVector(std::vector &v, + const ModuleDepGraphNode *n) { + const auto where = std::find(v.begin(), v.end(), n); + if (where != v.end()) + v.erase(where); + } + + void eraseNodeFromGraphAndFreeIt(ModuleDepGraphNode *); + + /// If the programmer removes a Decl from a source file, the corresponding + /// ModuleDepGraphNode needs to be removed. + void eraseNodeFromJob(ModuleDepGraphNode *); + + //============================================================================ + // MARK: ModuleDepGraph - utilities for Job and swiftdeps + //============================================================================ +private: static StringRef getSwiftDeps(const driver::Job *cmd) { return cmd->getOutput().getAdditionalOutputForType( file_types::TY_SwiftDeps); @@ -244,19 +283,25 @@ class ModuleDepGraph { return iter->second; } + //============================================================================ + // MARK: ModuleDepGraph - creation + //============================================================================ + public: /// For templates such as DotFileEmitter. using NodeType = ModuleDepGraphNode; /// \p stats may be null - ModuleDepGraph(const bool verifyExperimentalDependencyGraphAfterEveryImport, - const bool emitExperimentalDependencyDotFileAfterEveryImport, + ModuleDepGraph(const bool verifyFineGrainedDependencyGraphAfterEveryImport, + const bool emitFineGrainedDependencyDotFileAfterEveryImport, + const bool EnableTypeFingerprints, const bool shouldTraceDependencies, UnifiedStatsReporter *stats) - : verifyExperimentalDependencyGraphAfterEveryImport( - verifyExperimentalDependencyGraphAfterEveryImport), - emitExperimentalDependencyDotFileAfterEveryImport( - emitExperimentalDependencyDotFileAfterEveryImport), + : verifyFineGrainedDependencyGraphAfterEveryImport( + verifyFineGrainedDependencyGraphAfterEveryImport), + emitFineGrainedDependencyDotFileAfterEveryImport( + emitFineGrainedDependencyDotFileAfterEveryImport), + EnableTypeFingerprints(EnableTypeFingerprints), currentPathIfTracing( shouldTraceDependencies ? llvm::Optional>( @@ -266,16 +311,125 @@ class ModuleDepGraph { assert(verify() && "ModuleDepGraph should be fine when created"); } - DependencyGraphImpl::LoadResult loadFromPath(const driver::Job *, StringRef, - DiagnosticEngine &); + /// For unit tests. + ModuleDepGraph(const bool EnableTypeFingerprints) + : ModuleDepGraph(true, false, EnableTypeFingerprints, false, nullptr) {} + + + //============================================================================ + // MARK: ModuleDepGraph - updating from a switdeps file + //============================================================================ +public: + using Changes = Optional>; + + /// Unlike the standard \c CoarseGrainedDependencyGraph, returns \c + /// CoarseGrainedDependencyGraphImpl::LoadResult::AffectsDownstream when + /// loading a new file, i.e. when determining the initial set. Caller + /// compensates. + Changes loadFromPath(const driver::Job *, StringRef, DiagnosticEngine &); + + + Changes loadFromSourceFileDepGraph(const driver::Job *cmd, + const SourceFileDepGraph &); + + /// Also for unit tests + Changes + simulateLoad(const driver::Job *cmd, + llvm::StringMap> simpleNames, + llvm::StringMap>> + compoundNames = {}, + const bool includePrivateDeps = false, + const bool hadCompilationError = false); + + +private: + /// Read a SourceFileDepGraph belonging to \p job from \p buffer + /// and integrate it into the ModuleDepGraph. + /// Used both the first time, and to reload the SourceFileDepGraph. + /// If any changes were observed, indicate same in the return vale. + Changes loadFromBuffer(const driver::Job *, llvm::MemoryBuffer &); + + /// Integrate a SourceFileDepGraph into the receiver. + /// Integration happens when the driver needs to read SourceFileDepGraph. + Changes + integrate(const SourceFileDepGraph &, StringRef swiftDepsOfJob); + + enum class LocationOfPreexistingNode { nowhere, here, elsewhere }; + + typedef Optional> + PreexistingNodeIfAny; + + /// Find the preexisting node here that best matches the integrand. + PreexistingNodeIfAny + findPreexistingMatch(StringRef swiftDepsOfCompilationToBeIntegrated, + const SourceFileDepGraphNode *integrand) const; + + /// Integrate the \p integrand into the receiver. + /// Return the changed node if any.. + NullablePtr + integrateSourceFileDepGraphNode(const SourceFileDepGraph &g, + const SourceFileDepGraphNode *integrand, + const PreexistingNodeIfAny preexistingMatch, + StringRef swiftDepsOfJob); + /// Integrate the \p integrand, a node that represents a Decl in the swiftDeps + /// file being integrated. \p preexistingNodeInPlace holds the node + /// representing the same Decl that already exists, if there is one. \p + /// prexisintExpat holds a node with the same key that already exists, but was + /// not known to reside in any swiftDeps file. Return a bool indicating if + /// this node represents a change that must be propagated, and the integrated + /// ModuleDepGraphNode. + std::pair + integrateSourceFileDeclNode(const SourceFileDepGraphNode *integrand, + StringRef swiftDepsOfJob, + const PreexistingNodeIfAny preexistingMatch); + + /// Create a brand-new ModuleDepGraphNode to integrate \p integrand. + ModuleDepGraphNode * + integrateByCreatingANewNode(const SourceFileDepGraphNode *integrand, + Optional swiftDepsForNewNode); + + /// After importing a provides node from the frontend, record its + /// dependencies. + /// Return true if moduleUseNode picks up a new external-dependency + bool recordWhatUseDependsUpon(const SourceFileDepGraph &g, + const SourceFileDepGraphNode *sourceFileUseNode, + ModuleDepGraphNode *moduleUseNode); + + //============================================================================ + // MARK: ModuleDepGraph - dot file support + //============================================================================ +public: /// For the dot file. std::string getGraphID() const { return "driver"; } + /// Don't want to do this after every integration--too slow-- + /// So export this hook to the driver. + bool emitDotFileAndVerify(DiagnosticEngine &) const; + + /// Use the known swiftDeps to find a directory for + /// the job-independent dot file. + std::string computePathForDotFile() const; + + /// For debugging and visualization, write out the graph to a dot file. + /// \p diags may be null if no diagnostics are needed. + void emitDotFileForJob(DiagnosticEngine &, const driver::Job *); + void emitDotFile(DiagnosticEngine &, StringRef baseName); + void emitDotFile() { emitDotFile(llvm::errs()); } + void emitDotFile(llvm::raw_ostream &); + + //============================================================================ + // MARK: ModuleDepGraph - traversal + //============================================================================ +public: + void forCorrespondingImplementationOfProvidedInterface( + const ModuleDepGraphNode *, + function_ref) const; + void forEachUseOf(const ModuleDepGraphNode *def, - function_ref); + function_ref) const; - void forEachNode(function_ref) const; + void forEachNode(function_ref) const; void forEachArc(function_ref) const; @@ -283,44 +437,103 @@ class ModuleDepGraph { /// Call \p fn for each node whose key matches \p key. void forEachMatchingNode(const DependencyKey &key, - function_ref) const; + function_ref) const; + void forEachNodeInJob(StringRef swiftDeps, + function_ref) const; + + /// Given a definition node, transitively find all previous untraced + /// dependents and add them to the array. Also returns definition if that is + /// untraced. + void findPreviouslyUntracedDependents( + std::vector &foundDependents, + ModuleDepGraphNode *definition); + + /// Givien a set of nodes, return the set of swiftDeps for the jobs those + /// nodes are in. + std::vector + computeSwiftDepsFromNodes(ArrayRef nodes) const; + + /// Record a visit to this node for later dependency printing + size_t traceArrival(const ModuleDepGraphNode *visitedNode); + /// Record end of visit to this node. + void traceDeparture(size_t pathLengthAfterArrival); + + /// For printing why a Job was compiled, record how it was found. + void recordDependencyPathToJob( + const std::vector &pathToJob, + const driver::Job *dependentJob); + + /// Dump the path that led to \p node. + void printPath(raw_ostream &out, const driver::Job *node) const; + + /// Get a printable filename, given a node's swiftDeps. + StringRef getProvidingFilename(Optional swiftDeps) const; + + /// Print one node on the dependency path. + static void printOneNodeOfPath(raw_ostream &out, const DependencyKey &key, + const StringRef filename); + + bool isCurrentPathForTracingEmpty() const { + return !currentPathIfTracing.hasValue() || currentPathIfTracing->empty(); + } + + //============================================================================ + // MARK: ModuleDepGraph - job-level queries and operations + //============================================================================ public: // This section contains the interface to the status quo code in the driver. - /// Interface to status quo code in the driver. - bool isMarked(const driver::Job *) const; + bool haveAnyNodesBeenTraversedIn(const driver::Job *) const; /// Given a "cascading" job, that is a job whose dependents must be recompiled /// when this job is recompiled, Compute two sets of jobs: /// 1. Return value (via visited) is the set of jobs needing recompilation /// after this one, and /// 2. Jobs not previously known to need dependencies reexamined after they - /// are recompiled. Such jobs are added to the \ref cascadingJobs set, and - /// accessed via \ref isMarked. - void markTransitive( - SmallVectorImpl &consequentJobsToRecompile, - const driver::Job *jobToBeRecompiled, const void *ignored = nullptr); + /// are recompiled. + /// + /// Returns jobs to be run because of changes to any/ever node in the + /// argument. Only return jobs marked that were previously unmarked, assuming + /// previously marked jobs are already scheduled. + /// TODO: rewrite above comment + std::vector + findJobsToRecompileWhenWholeJobChanges(const driver::Job *jobToBeRecompiled); - /// "Mark" this node only. - bool markIntransitive(const driver::Job *); + template + std::vector + findJobsToRecompileWhenNodesChange(const Nodes &); + +private: + std::vector + jobsContaining(const ArrayRef uses) const; +public: /// Record a new (to this graph) Job. - void addIndependentNode(const driver::Job *); + void registerJob(const driver::Job *); - std::vector getExternalDependencies() const; + /// Find jobs that were previously not known to need compilation but that + /// depend on \c externalDependency. + std::vector + findExternallyDependentUntracedJobs(StringRef externalDependency); - void markExternal(SmallVectorImpl &uses, - StringRef externalDependency); + //============================================================================ + // MARK: ModuleDepGraph - External dependencies + //============================================================================ - /// Return true or abort - bool verify() const; +public: + std::vector getExternalDependencies() const; - /// Don't want to do this after every integration--too slow-- - /// So export this hook to the driver. - bool emitDotFileAndVerify(DiagnosticEngine &); + void forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( + StringRef externalDependency, function_ref fn); + //============================================================================ + // MARK: ModuleDepGraph - verification + //============================================================================ private: + /// Return true or abort + bool verify() const; + void verifyNodeMapEntries() const; /// Called for each \ref nodeMap entry during verification. @@ -357,116 +570,16 @@ class ModuleDepGraph { static bool mapCorruption(const char *msg) { llvm_unreachable(msg); } - /// Use the known swiftDeps to find a directory for - /// the job-independent dot file. - std::string computePathForDotFile() const; - - /// Read a SourceFileDepGraph belonging to \p job from \p buffer - /// and integrate it into the ModuleDepGraph. - /// Used both the first time, and to reload the SourceFileDepGraph. - /// If any changes were observed, indicate same in the return vale. - DependencyGraphImpl::LoadResult loadFromBuffer(const driver::Job *, - llvm::MemoryBuffer &); - - /// Integrate a SourceFileDepGraph into the receiver. - /// Integration happens when the driver needs to read SourceFileDepGraph. - DependencyGraphImpl::LoadResult integrate(const SourceFileDepGraph &); - - enum class LocationOfPreexistingNode { nowhere, here, elsewhere }; - - typedef Optional> - PreexistingNodeIfAny; - - /// Find the preexisting node here that best matches the integrand. - PreexistingNodeIfAny - findPreexistingMatch(StringRef swiftDepsOfCompilationToBeIntegrated, - const SourceFileDepGraphNode *integrand); - - /// Integrate the \p integrand into the receiver. - /// Return a bool indicating if this node represents a change that must be - /// propagated. - bool - integrateSourceFileDepGraphNode(const SourceFileDepGraph &g, - const SourceFileDepGraphNode *integrand, - const PreexistingNodeIfAny preexistingMatch); - - /// Integrate the \p integrand, a node that represents a Decl in the swiftDeps - /// file being integrated. \p preexistingNodeInPlace holds the node - /// representing the same Decl that already exists, if there is one. \p - /// prexisintExpat holds a node with the same key that already exists, but was - /// not known to reside in any swiftDeps file. Return a bool indicating if - /// this node represents a change that must be propagated, and the integrated - /// ModuleDepGraphNode. - std::pair - integrateSourceFileDeclNode(const SourceFileDepGraphNode *integrand, - StringRef swiftDepsOfSourceFileGraph, - const PreexistingNodeIfAny preexistingMatch); - - /// Create a brand-new ModuleDepGraphNode to integrate \p integrand. - ModuleDepGraphNode * - integrateByCreatingANewNode(const SourceFileDepGraphNode *integrand, - Optional swiftDepsForNewNode); - - /// After importing a provides node from the frontend, record its - /// dependencies. - void recordWhatUseDependsUpon(const SourceFileDepGraph &g, - const SourceFileDepGraphNode *sourceFileUseNode, - ModuleDepGraphNode *moduleUseNode); - - /// If the programmer removes a Decl from a source file, the corresponding - /// ModuleDepGraphNode needs to be removed. - void removeNode(ModuleDepGraphNode *); - - /// Given a definition node, and a list of already found dependents, - /// recursively add transitive closure of dependents of the definition - /// into the already found dependents. - /// Also record any dependents that "cascade", i.e. whose dependencies must be - /// recomputed after recompilation so that its dependents can be recompiled. - void findDependentNodesAndRecordCascadingOnes( - std::unordered_set &foundDependents, - const ModuleDepGraphNode *definition); - - void computeUniqueJobsFromNodes( - SmallVectorImpl &uniqueJobs, - const std::unordered_set &nodes); - - /// Record a visit to this node for later dependency printing - size_t traceArrival(const ModuleDepGraphNode *visitedNode); - /// Record end of visit to this node. - void traceDeparture(size_t pathLengthAfterArrival); - - /// For printing why a Job was compiled, record how it was found. - void recordDependencyPathToJob( - const std::vector &pathToJob, - const driver::Job *dependentJob); - - /// Return true if job did not cascade before - bool rememberThatJobCascades(StringRef swiftDeps) { - return cascadingJobs.insert(swiftDeps).second; - } - - /// For debugging, write out the graph to a dot file. - /// \p diags may be null if no diagnostics are needed. - void emitDotFileForJob(DiagnosticEngine &, const driver::Job *); - void emitDotFile(DiagnosticEngine &, StringRef baseName); - void emitDotFile() { emitDotFile(llvm::errs()); } - void emitDotFile(llvm::raw_ostream &); bool ensureJobIsTracked(const std::string &swiftDeps) const { assert(swiftDeps.empty() || getJob(swiftDeps)); return true; } -public: - /// Dump the path that led to \p node. - void printPath(raw_ostream &out, const driver::Job *node) const; - -private: - bool isCurrentPathForTracingEmpty() const { - return !currentPathIfTracing.hasValue() || currentPathIfTracing->empty(); - } + static std::vector + printJobsForDebugging(const std::vector &jobs); }; -} // namespace experimental_dependencies +} // namespace fine_grained_dependencies } // namespace swift -#endif // ExperimentalDependencyGraph_h +#endif // SWIFT_DRIVER_FINE_GRAINED_DEPENDENCY_DRIVER_GRAPH_H diff --git a/include/swift/Driver/Job.h b/include/swift/Driver/Job.h index f7f7752e7bdc3..e57952545bb64 100644 --- a/include/swift/Driver/Job.h +++ b/include/swift/Driver/Job.h @@ -13,6 +13,7 @@ #ifndef SWIFT_DRIVER_JOB_H #define SWIFT_DRIVER_JOB_H +#include "swift/Basic/Debug.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OutputFileMap.h" @@ -149,6 +150,9 @@ class CommandOutput { public: CommandOutput(file_types::ID PrimaryOutputType, OutputFileMap &Derived); + /// For testing dependency graphs that use Jobs + CommandOutput(StringRef dummyBaseName, OutputFileMap &); + /// Return the primary output type for this CommandOutput. file_types::ID getPrimaryOutputType() const; @@ -193,6 +197,11 @@ class CommandOutput { /// first primary input. StringRef getAdditionalOutputForType(file_types::ID type) const; + /// Assuming (and asserting) that there are one or more input pairs, return true if there exists + /// an _additional_ (not primary) output of type \p type associated with the + /// first primary input. + bool hasAdditionalOutputForType(file_types::ID type) const; + /// Return a vector of additional (not primary) outputs of type \p type /// associated with the primary inputs. /// @@ -220,7 +229,7 @@ class CommandOutput { void writeOutputFileMap(llvm::raw_ostream &out) const; void print(raw_ostream &Stream) const; - void dump() const LLVM_ATTRIBUTE_USED; + SWIFT_DEBUG_DUMP; /// For use in assertions: check the CommandOutput's state is consistent with /// its invariants. @@ -313,6 +322,12 @@ class Job { ExtraEnvironment(std::move(ExtraEnvironment)), FilelistFileInfos(std::move(Infos)), ResponseFile(ResponseFile) {} + /// For testing dependency graphs that use Jobs + Job(OutputFileMap &OFM, StringRef dummyBaseName) + : Job(CompileJobAction(file_types::TY_Object), + SmallVector(), + std::make_unique(dummyBaseName, OFM), nullptr, {}) {} + virtual ~Job(); const JobAction &getSource() const { @@ -373,7 +388,7 @@ class Job { Callback(this, static_cast(OSPid)); } - void dump() const LLVM_ATTRIBUTE_USED; + SWIFT_DEBUG_DUMP; static void printArguments(raw_ostream &Stream, const llvm::opt::ArgStringList &Args); @@ -381,6 +396,10 @@ class Job { bool hasResponseFile() const { return ResponseFile.hasValue(); } bool writeArgsToResponseFile() const; + + /// Assumes that, if a compile job, has one primary swift input + /// May return empty if none. + StringRef getFirstSwiftPrimaryInput() const; }; /// A BatchJob comprises a _set_ of jobs, each of which is sufficiently similar diff --git a/include/swift/Driver/SourceComparator.h b/include/swift/Driver/SourceComparator.h new file mode 100644 index 0000000000000..223bfcf0b9d52 --- /dev/null +++ b/include/swift/Driver/SourceComparator.h @@ -0,0 +1,348 @@ +//===------------------------------ SourceComparator.h -----------*- C++-*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// Declares the code needed to essentially "diff" the previously-compiled and +// possibly-about-to-be-compiled versions of a source file. +// Unlike Unix diff, it can go down to the granularity of characters within a +// line. + +#ifndef SWIFT_DRIVER_SOURCECOMPARATOR_H +#define SWIFT_DRIVER_SOURCECOMPARATOR_H + +#include "swift/AST/IncrementalRanges.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include +#include + +namespace swift { +namespace incremental_ranges { + +class SourceComparator { + +public: + //============================================================================== + // MARK: Packaging indices for both sides + //============================================================================== + + /// It is useful to have structures that hold both the left- and + /// right-hand-sides of a "diff" input Describes a side: + enum class Side : size_t { L = 0, R = 1 }; + + /// A general class to hold something that exists on both sides: + template struct LR { + T contents[2]; + /// Get the contents on a particular side + T &operator[](const Side side) { return contents[size_t(side)]; } + const T &operator[](const Side side) const { + return contents[size_t(side)]; + } + + /// Get a side by name: + // TODO: Could get ride of these and just use the subscripting above + T &lhs() { return contents[size_t(Side::L)]; } + T &rhs() { return contents[size_t(Side::R)]; } + + const T &lhs() const { return contents[size_t(Side::L)]; } + const T &rhs() const { return contents[size_t(Side::R)]; } + LR(T lhs, T rhs) { + contents[0] = lhs; + contents[1] = rhs; + } + bool operator==(const LR &other) { + return lhs() == other.lhs() && rhs() == other.rhs(); + } + }; + + /// A structure holding size_t's on both sides. + /// Zero-origin + template struct Indices : public LR { + Indices(size_t lhs, size_t rhs) : LR(lhs, rhs) {} + Derived operator+(size_t x) const { return Derived(lhs() + x, rhs() + x); } + Derived operator-(size_t x) const { return Derived(lhs() - x, rhs() - x); } + size_t min() const { return std::min(lhs(), rhs()); } + bool operator<(const Derived &other) const { + return lhs() < other.lhs() && rhs() < other.rhs(); + } + bool operator<=(const Derived &other) const { + return lhs() <= other.lhs() && rhs() <= other.rhs(); + } + }; + /// Character indices into a line. + /// Zero-origin + struct CharIndices : public Indices { + CharIndices(size_t lhs, size_t rhs) : Indices(lhs, rhs) {} + CharIndices &operator++() { + ++lhs(), ++rhs(); + return *this; + } + CharIndices &operator--() { + --lhs(), --rhs(); + return *this; + } + bool bothNonZero() const { return lhs() != 0 && rhs() != 0; } + }; + + /// Indices into a collection of lines. + /// Zero-origin + struct LineIndices : public Indices { + LineIndices() : Indices(~0, ~0) {} + LineIndices(size_t lhs, size_t rhs) : Indices(lhs, rhs) {} + LineIndices &operator=(const LineIndices &) = default; + LineIndices &operator++() { + ++lhs(), ++rhs(); + return *this; + } + LineIndices &operator--() { + --lhs(), --rhs(); + return *this; + } + }; + + /// Strings on both sides. + struct StringRefs : LR { + StringRefs(StringRef lhs, StringRef rhs) : LR(lhs, rhs) {} + CharIndices size() const { return CharIndices(lhs().size(), rhs().size()); } + /// Do the strings match at the provided character indices? + bool matchAt(const CharIndices &indices) const { + return lhs()[indices.lhs()] == rhs()[indices.rhs()]; + } + }; + + /// Optional strings on both sides + struct OptStringRefs : LR> { + OptStringRefs(Optional lhs, Optional rhs) + : LR(lhs, rhs) {} + }; + + /// A half-open span of lines on both sides, from start up to but not + /// including end. + struct LineSpan { + /// [start, end) + LineIndices start, end; + + LineSpan(LineIndices start, LineIndices end) : start(start), end(end) {} + bool operator==(const LineSpan &rhs) { + return start == rhs.start && end == rhs.end; + } + bool bothNonEmpty() const { + return start.lhs() < end.lhs() && start.rhs() < end.rhs(); + } + bool eitherNonEmpty() const { + return start.lhs() < end.lhs() || start.rhs() < end.rhs(); + } + bool bothEmpty() const { return !eitherNonEmpty(); } + + LineIndices size() const { + return LineIndices(end.lhs() - start.lhs(), end.rhs() - start.rhs()); + } + }; + + /// A pair of collections of lines, one on each side + struct Lines : LR> { + Lines(std::vector lhs, std::vector rhs) + : LR(lhs, rhs) {} + + bool matchAt(LineIndices where) const { + return lhs()[where.lhs()] == rhs()[where.rhs()]; + } + StringRefs operator[](const LineIndices &indices) const { + return StringRefs(lhs()[indices.lhs()], rhs()[indices.rhs()]); + } + /// Cannot use subscripting to get a particular side here because used it + /// above for something else. So use a named function. + const std::vector &side(Side s) const { + return s == Side::L ? lhs() : rhs(); + } + LineIndices size() const { return LineIndices(lhs().size(), rhs().size()); } + + CharIndices sizeOfLastLines() const { + return CharIndices(lhs().empty() ? 0 : lhs().back().size(), + rhs().empty() ? 0 : rhs().back().size()); + } + }; + + /// A pair of SerializableSourceRanges, one per side. + /// (SerializableSourceRanges are 1-origin, and semi-open.) + struct LRSerializableRange : LR { + LRSerializableRange(const SerializableSourceRange &lhs, + const SerializableSourceRange &rhs) + : LR(lhs, rhs) {} + }; + /// A pair of vectors of SerializableSourceRanges, one per side. + struct LRRanges : LR { + LRRanges() : LRRanges({}, {}) {} + LRRanges(Ranges &&lhs, Ranges &&rhs) : LR(std::move(lhs), std::move(rhs)) {} + static LRRanges wholeFile() { + return {SerializableSourceRange::RangesForWholeFile(), + SerializableSourceRange::RangesForWholeFile()}; + } + void push_back(const LRSerializableRange &range) { + lhs().push_back(range.lhs()); + rhs().push_back(range.rhs()); + } + bool empty() const { + assert(lhs().empty() == rhs().empty() && "diff should produce at least " + "empty ranges for an addition " + "to the other side"); + return lhs().empty(); + } + }; + +private: + /// The inputs, separated into lines + Lines linesToCompare; + + /// The trimmed input regions to compare, after cutting away identical + /// beginnings and endings. + LineSpan regionsToCompare; + + /// If line i on the left matches line j on the right, match[i] == j. + /// If line i on the left has no match, match[i] == None. + std::vector> matches; + + /// Chains matching lines together (I think) + struct PRLink { + LineIndices lines; + Optional next; + PRLink(LineIndices lines, const Optional next) + : lines(lines), next(next) {} + PRLink() : PRLink(LineIndices(), None) {} + }; + + + //============================================================================== + // MARK: SortedSequence + //============================================================================== +private: + class SortedSequence { + typedef size_t Elem; + std::vector contents; + + public: + /// Find the place at which x would normally be inserted into the sequence, + /// and replace that element with x, returning the index. + /// If x is already in the sequence, do nothing and return None. + /// Append to the end if necessary. + /// Preserve sort order. + Optional replaceNextLargerWith(const Elem x); + bool empty() const { return contents.empty(); } + + Elem back() const { return contents.back(); } + + size_t size() const { return contents.size(); } + + private: + /// Return index of first greater element or the end index if there is none. + size_t locationOfFirstGreaterOrEqualElement(const Elem x) const; + }; + + //============================================================================== + // MARK: initialization + //============================================================================== + +public: + SourceComparator(StringRef s1, StringRef s2); + +private: + static std::vector splitIntoLines(StringRef s); + + //============================================================================== + // MARK: Comparing + //============================================================================== +public: + /// Run the actual diff algorithm + void compare(); + +private: + void trimStart(); + void trimEnd(); + + /// Return a map from a line in rhs to indices of all identical lines in + /// rhs + std::unordered_map> + buildEquivalenceClasses(); + + std::pair, SortedSequence> buildDAGOfSubsequences( + std::unordered_map> rhsMap); + + void scanMatchedLines( + std::pair, SortedSequence> &&linksAndThres); + + //============================================================================== + // MARK: summarizing + //============================================================================== +public: + std::vector getMatchingRegions() const; + std::vector getMismatchingRegions() const; + + void forEachMatch(function_ref) const; + void forEachMismatch(function_ref) const; + + /// A region is zero-origin, semi-open. An SerializableRange is 1-origin, + /// closed. + /// This function resolves changed regions to within lines. It's a bit tricky. + LRSerializableRange convertAMismatch(LineSpan mismatch) const; + + LRRanges convertAllMismatches() const; + +private: + bool areEitherInRange(LineIndices lines) const; + + LineIndices getFirstMatch() const; + LineIndices getFirstMismatch() const; + + LineIndices getNextMismatchAfter(const LineIndices nextMatch) const; + + LineIndices getNextMatchAfter(const LineIndices nextMismatch) const; + + //============================================================================== + // MARK: converting mismatches + //============================================================================== + CharIndices computeFirstMismatchInLine(LineSpan mismatchLines) const; + CharIndices + computeFirstMismatchInLinesOnBothSides(LineSpan mismatchLines) const; + CharIndices computeFirstMismatchInLineOnOneSide(LineSpan mismatchLines, + Side) const; + + CharIndices computeEndMismatchInLine(LineSpan mismatchLines, + CharIndices firstMismatchInLine) const; + CharIndices computeEndMismatchInLineOnBothSides( + LineSpan mismatchLines, const CharIndices firstMismatchInLine) const; + CharIndices + computeEndMismatchInLineOnOneSide(LineSpan mismatchLines, + const CharIndices firstMismatchInLine, + const Side) const; + + size_t computeEndLinesToGoWithChars(LineSpan mismatchLines, + CharIndices endMismatchInLine, + const Side) const; + //============================================================================== + // MARK: printing + //============================================================================== +public: + void dump() const { print(llvm::errs()); } + void print(raw_ostream &) const; + + void reportMismatch(const LineSpan mismatch, raw_ostream &) const; + + //============================================================================== + // MARK: testing + //============================================================================== +public: + static bool test(); +}; + +} // namespace incremental_ranges +} // namespace swift + +#endif /* SWIFT_DRIVER_SOURCECOMPARATOR_H */ diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index 921b9fb04defc..12c1b802e0716 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -201,6 +201,11 @@ class ToolChain { void getResourceDirPath(SmallVectorImpl &runtimeLibPath, const llvm::opt::ArgList &args, bool shared) const; + /// Get the secondary runtime library link path given the primary path. + void getSecondaryResourceDirPath( + SmallVectorImpl &secondaryResourceDirPath, + StringRef primaryPath) const; + /// Get the runtime library link paths, which typically include the resource /// dir path and the SDK. void getRuntimeLibraryPaths(SmallVectorImpl &runtimeLibPaths, @@ -310,7 +315,8 @@ class ToolChain { /// An override point for platform-specific subclasses to customize the /// validations that should be performed. virtual void validateArguments(DiagnosticEngine &diags, - const llvm::opt::ArgList &args) const {} + const llvm::opt::ArgList &args, + StringRef defaultTarget) const {} }; } // end namespace driver } // end namespace swift diff --git a/include/swift/Driver/Util.h b/include/swift/Driver/Util.h index 8f9fd30357fe1..8fcbd5f04e736 100644 --- a/include/swift/Driver/Util.h +++ b/include/swift/Driver/Util.h @@ -45,8 +45,9 @@ namespace driver { /// the Job this info is attached to. struct FilelistInfo { enum class WhichFiles : unsigned { - Input, - PrimaryInputs, + InputJobs, + SourceInputActions, + InputJobsAndSourceInputActions, Output, /// Batch mode frontend invocations may have so many supplementary /// outputs that they don't comfortably fit as command-line arguments. diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index b9c0c381ee285..5c6c833a4e65c 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -60,6 +60,18 @@ namespace Lowering { class TypeConverter; } +struct ModuleBuffers { + std::unique_ptr ModuleBuffer; + std::unique_ptr ModuleDocBuffer; + std::unique_ptr ModuleSourceInfoBuffer; + ModuleBuffers(std::unique_ptr ModuleBuffer, + std::unique_ptr ModuleDocBuffer = nullptr, + std::unique_ptr ModuleSourceInfoBuffer = nullptr): + ModuleBuffer(std::move(ModuleBuffer)), + ModuleDocBuffer(std::move(ModuleDocBuffer)), + ModuleSourceInfoBuffer(std::move(ModuleSourceInfoBuffer)) {} +}; + /// The abstract configuration of the compiler, including: /// - options for all stages of translation, /// - information about the build environment, @@ -71,6 +83,7 @@ class TypeConverter; /// which manages the actual compiler execution. class CompilerInvocation { LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; FrontendOptions FrontendOpts; ClangImporterOptions ClangImporterOpts; SearchPathOptions SearchPathOpts; @@ -190,6 +203,12 @@ class CompilerInvocation { void setRuntimeResourcePath(StringRef Path); + /// Computes the runtime resource path relative to the given Swift + /// executable. + static void computeRuntimeResourcePathFromExecutablePath( + StringRef mainExecutablePath, + llvm::SmallString<128> &runtimeResourcePath); + void setSDKPath(const std::string &Path); StringRef getSDKPath() const { @@ -203,6 +222,11 @@ class CompilerInvocation { return LangOpts; } + TypeCheckerOptions &getTypeCheckerOptions() { return TypeCheckerOpts; } + const TypeCheckerOptions &getTypeCheckerOptions() const { + return TypeCheckerOpts; + } + FrontendOptions &getFrontendOptions() { return FrontendOpts; } const FrontendOptions &getFrontendOptions() const { return FrontendOpts; } @@ -294,26 +318,17 @@ class CompilerInvocation { return CodeCompletionOffset != ~0U; } - void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) { - CodeCompletionFactory = Factory; - disableASTScopeLookup(); - } - /// Called from lldb, see rdar://53971116 void disableASTScopeLookup() { LangOpts.EnableASTScopeLookup = false; } - CodeCompletionCallbacksFactory *getCodeCompletionFactory() const { - return CodeCompletionFactory; - } - /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built, for use /// in generating a cached PCH file for the bridging header. std::string getPCHHash() const; - SourceFile::ImplicitModuleImportKind getImplicitModuleImportKind() { + SourceFile::ImplicitModuleImportKind getImplicitModuleImportKind() const { if (getInputKind() == InputFileKind::SIL) { return SourceFile::ImplicitModuleImportKind::None; } @@ -347,6 +362,8 @@ class CompilerInvocation { std::string getModuleOutputPathForAtMostOnePrimary() const; std::string getReferenceDependenciesFilePathForPrimary(StringRef filename) const; + std::string getSwiftRangesFilePathForPrimary(StringRef filename) const; + std::string getCompiledSourceFilePathForPrimary(StringRef filename) const; std::string getSerializedDiagnosticsPathForAtMostOnePrimary() const; /// TBDPath only makes sense in whole module compilation mode, @@ -359,9 +376,11 @@ class CompilerInvocation { /// fail an assert if not in that mode. std::string getModuleInterfaceOutputPathForWholeModule() const; + std::string getLdAddCFileOutputPathForWholeModule() const; + SerializationOptions computeSerializationOptions(const SupplementaryOutputPaths &outs, - bool moduleIsPublic); + bool moduleIsPublic) const; }; /// A class which manages the state and execution of the compiler. @@ -385,22 +404,16 @@ class CompilerInstance { /// Null if no tracker. std::unique_ptr DepTracker; - ModuleDecl *MainModule = nullptr; + mutable ModuleDecl *MainModule = nullptr; SerializedModuleLoader *SML = nullptr; MemoryBufferSerializedModuleLoader *MemoryBufferLoader = nullptr; /// Contains buffer IDs for input source code files. std::vector InputSourceCodeBufferIDs; - struct PartialModuleInputs { - std::unique_ptr ModuleBuffer; - std::unique_ptr ModuleDocBuffer; - std::unique_ptr ModuleSourceInfoBuffer; - }; - /// Contains \c MemoryBuffers for partial serialized module files and /// corresponding partial serialized module documentation files. - std::vector PartialModules; + std::vector PartialModules; enum : unsigned { NO_SUCH_BUFFER = ~0U }; unsigned MainBufferID = NO_SUCH_BUFFER; @@ -440,14 +453,16 @@ class CompilerInstance { void operator=(CompilerInstance &&) = delete; SourceManager &getSourceMgr() { return SourceMgr; } + const SourceManager &getSourceMgr() const { return SourceMgr; } DiagnosticEngine &getDiags() { return Diagnostics; } + const DiagnosticEngine &getDiags() const { return Diagnostics; } llvm::vfs::FileSystem &getFileSystem() { return *SourceMgr.getFileSystem(); } - ASTContext &getASTContext() { - return *Context; - } + ASTContext &getASTContext() { return *Context; } + const ASTContext &getASTContext() const { return *Context; } + bool hasASTContext() const { return Context != nullptr; } SILOptions &getSILOptions() { return Invocation.getSILOptions(); } @@ -461,16 +476,16 @@ class CompilerInstance { Diagnostics.addConsumer(*DC); } + void removeDiagnosticConsumer(DiagnosticConsumer *DC) { + Diagnostics.removeConsumer(*DC); + } + void createDependencyTracker(bool TrackSystemDeps) { assert(!Context && "must be called before setup()"); DepTracker = llvm::make_unique(TrackSystemDeps); } DependencyTracker *getDependencyTracker() { return DepTracker.get(); } - - /// Set the SIL module for this compilation instance. - /// - /// The CompilerInstance takes ownership of the given SILModule object. - void setSILModule(std::unique_ptr M); + const DependencyTracker *getDependencyTracker() const { return DepTracker.get(); } SILModule *getSILModule() { return TheSILModule.get(); @@ -482,7 +497,7 @@ class CompilerInstance { return static_cast(TheSILModule); } - ModuleDecl *getMainModule(); + ModuleDecl *getMainModule() const; MemoryBufferSerializedModuleLoader * getMemoryBufferSerializedModuleLoader() const { @@ -503,7 +518,7 @@ class CompilerInstance { /// Gets the set of SourceFiles which are the primary inputs for this /// CompilerInstance. - ArrayRef getPrimarySourceFiles() { + ArrayRef getPrimarySourceFiles() const { return PrimarySourceFiles; } @@ -513,7 +528,7 @@ class CompilerInstance { /// /// FIXME: This should be removed eventually, once there are no longer any /// codepaths that rely on a single primary file. - SourceFile *getPrimarySourceFile() { + SourceFile *getPrimarySourceFile() const { if (PrimarySourceFiles.empty()) { return nullptr; } else { @@ -525,6 +540,18 @@ class CompilerInstance { /// Returns true if there was an error during setup. bool setup(const CompilerInvocation &Invocation); + const CompilerInvocation &getInvocation() { + return Invocation; + } + + bool hasPersistentParserState() const { + return bool(PersistentState); + } + + PersistentParserState &getPersistentParserState() { + return *PersistentState.get(); + } + private: /// Set up the file system by loading and validating all VFS overlay YAML /// files. If the process of validating VFS files failed, or the overlay @@ -556,18 +583,6 @@ class CompilerInstance { Optional getRecordedBufferID(const InputFile &input, bool &failed); - struct ModuleBuffers { - std::unique_ptr ModuleBuffer; - std::unique_ptr ModuleDocBuffer; - std::unique_ptr ModuleSourceInfoBuffer; - ModuleBuffers(std::unique_ptr ModuleBuffer, - std::unique_ptr ModuleDocBuffer = nullptr, - std::unique_ptr ModuleSourceInfoBuffer = nullptr): - ModuleBuffer(std::move(ModuleBuffer)), - ModuleDocBuffer(std::move(ModuleDocBuffer)), - ModuleSourceInfoBuffer(std::move(ModuleSourceInfoBuffer)) {} - }; - /// Given an input file, return a buffer to use for its contents, /// and a buffer for the corresponding module doc file if one exists. /// On failure, return a null pointer for the first element of the returned @@ -590,14 +605,13 @@ class CompilerInstance { void performSema(); /// Parses the input file but does no type-checking or module imports. - /// Note that this only supports parsing an invocation with a single file. void performParseOnly(bool EvaluateConditionals = false, - bool ParseDelayedBodyOnEnd = false); + bool CanDelayBodies = true); /// Parses and performs name binding on all input files. /// - /// Like a parse-only invocation, a single file is required. Unlike a - /// parse-only invocation, module imports will be processed. + /// This is similar to a parse-only invocation, but module imports will also + /// be processed. void performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. @@ -654,14 +668,11 @@ class CompilerInstance { bool parsePartialModulesAndLibraryFiles(const ImplicitImports &implicitImports); - OptionSet computeTypeCheckingOptions(); - void forEachFileToTypeCheck(llvm::function_ref fn); - void parseAndTypeCheckMainFileUpTo(SourceFile::ASTStage_t LimitStage, - OptionSet TypeCheckOptions); + void parseAndTypeCheckMainFileUpTo(SourceFile::ASTStage_t LimitStage); - void finishTypeChecking(OptionSet TypeCheckOptions); + void finishTypeChecking(); public: const PrimarySpecificPaths & @@ -672,6 +683,16 @@ class CompilerInstance { getPrimarySpecificPathsForAtMostOnePrimary() const; const PrimarySpecificPaths & getPrimarySpecificPathsForSourceFile(const SourceFile &SF) const; + + /// Write out the unparsed (delayed) source ranges + /// Return true for error + bool emitSwiftRanges(DiagnosticEngine &diags, SourceFile *primaryFile, + StringRef outputPath) const; + + /// Return true for error + bool emitCompiledSource(DiagnosticEngine &diags, + const SourceFile *primaryFile, + StringRef outputPath) const; }; } // namespace swift diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 66a640fc24061..7daf25d40b89c 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -231,6 +231,8 @@ class FrontendInputsAndOutputs { bool hasDependenciesPath() const; bool hasReferenceDependenciesPath() const; + bool hasSwiftRangesPath() const; + bool hasCompiledSourcePath() const; bool hasObjCHeaderOutputPath() const; bool hasLoadedModuleTracePath() const; bool hasModuleOutputPath() const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index cdcc496c1e524..b68d805d9833d 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -82,28 +82,6 @@ class FrontendOptions { /// Emit index data for imported serialized swift system modules. bool IndexSystemModules = false; - /// If non-zero, warn when a function body takes longer than this many - /// milliseconds to type-check. - /// - /// Intended for debugging purposes only. - unsigned WarnLongFunctionBodies = 0; - - /// If non-zero, warn when type-checking an expression takes longer - /// than this many milliseconds. - /// - /// Intended for debugging purposes only. - unsigned WarnLongExpressionTypeChecking = 0; - - /// If non-zero, overrides the default threshold for how long we let - /// the expression type checker run before we consider an expression - /// too complex. - unsigned SolverExpressionTimeThreshold = 0; - - /// If non-zero, overrides the default threshold for how many times - /// the Space::minus function is called before we consider switch statement - /// exhaustiveness checking to be too complex. - unsigned SwitchCheckingInvocationThreshold = 0; - /// The module for which we should verify all of the generic signatures. std::string VerifyGenericSignaturesInModule; @@ -148,6 +126,9 @@ class FrontendOptions { EmitObject, ///< Emit object file DumpTypeInfo, ///< Dump IRGen type info + + EmitPCM, ///< Emit precompiled Clang module from a module map + DumpPCM, ///< Dump information about a precompiled Clang module }; /// Indicates the action the user requested that the frontend perform. @@ -156,6 +137,9 @@ class FrontendOptions { /// Indicates that the input(s) should be parsed as the Swift stdlib. bool ParseStdlib = false; + /// Ignore .swiftsourceinfo file when trying to get source locations from module imported decls. + bool IgnoreSwiftSourceInfo = false; + /// When true, emitted module files will always contain options for the /// debugger to use. When unset, the options will only be present if the /// module appears to not be a public module. @@ -180,8 +164,6 @@ class FrontendOptions { /// \sa swift::SharedTimer bool DebugTimeCompilation = false; - bool SkipNonInlinableFunctionBodies = false; - /// The path to which we should output statistics files. std::string StatsOutputDir; @@ -225,10 +207,6 @@ class FrontendOptions { /// \see ResilienceStrategy::Resilient bool EnableLibraryEvolution = false; - /// Indicates that the frontend should emit "verbose" SIL - /// (if asked to emit SIL). - bool EmitVerboseSIL = false; - /// If set, this module is part of a mixed Objective-C/Swift framework, and /// the Objective-C half should implicitly be visible to the Swift sources. bool ImportUnderlyingModule = false; @@ -265,8 +243,14 @@ class FrontendOptions { /// Indicates whether full help (including "hidden" options) should be shown. bool PrintHelpHidden = false; - /// Should we sort SIL functions, vtables, witness tables, and global - /// variables by name when we print it out. This eases diffing of SIL files. + /// Indicates that the frontend should print the target triple and then + /// exit. + bool PrintTargetInfo = false; + + /// See the \ref SILOptions.EmitVerboseSIL flag. + bool EmitVerboseSIL = false; + + /// See the \ref SILOptions.EmitSortedSIL flag. bool EmitSortedSIL = false; /// Indicates whether the dependency tracker should track system @@ -333,6 +317,8 @@ class FrontendOptions { private: static bool canActionEmitDependencies(ActionType); static bool canActionEmitReferenceDependencies(ActionType); + static bool canActionEmitSwiftRanges(ActionType); + static bool canActionEmitCompiledSource(ActionType); static bool canActionEmitObjCHeader(ActionType); static bool canActionEmitLoadedModuleTrace(ActionType); static bool canActionEmitModule(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index e4a1e16d9435e..3155e8d89e193 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -134,8 +134,9 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules, - bool RemarkOnRebuildFromInterface) - : SerializedModuleLoaderBase(ctx, tracker, loadMode), + bool RemarkOnRebuildFromInterface, bool IgnoreSwiftSourceInfoFile) + : SerializedModuleLoaderBase(ctx, tracker, loadMode, + IgnoreSwiftSourceInfoFile), CacheDir(cacheDir), PrebuiltCacheDir(prebuiltCacheDir), RemarkOnRebuildFromInterface(RemarkOnRebuildFromInterface), PreferInterfaceForModules(PreferInterfaceForModules) @@ -147,9 +148,9 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { ArrayRef PreferInterfaceForModules; std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, - StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) override; @@ -161,12 +162,14 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { create(ASTContext &ctx, StringRef cacheDir, StringRef prebuiltCacheDir, DependencyTracker *tracker, ModuleLoadingMode loadMode, ArrayRef PreferInterfaceForModules = {}, - bool RemarkOnRebuildFromInterface = false) { + bool RemarkOnRebuildFromInterface = false, + bool IgnoreSwiftSourceInfoFile = false) { return std::unique_ptr( new ModuleInterfaceLoader(ctx, cacheDir, prebuiltCacheDir, tracker, loadMode, PreferInterfaceForModules, - RemarkOnRebuildFromInterface)); + RemarkOnRebuildFromInterface, + IgnoreSwiftSourceInfoFile)); } /// Append visible module names to \p names. Note that names are possibly diff --git a/include/swift/Frontend/ModuleInterfaceSupport.h b/include/swift/Frontend/ModuleInterfaceSupport.h index 7a3e54ee8bc64..4289150c14554 100644 --- a/include/swift/Frontend/ModuleInterfaceSupport.h +++ b/include/swift/Frontend/ModuleInterfaceSupport.h @@ -31,6 +31,10 @@ struct ModuleInterfaceOptions { /// interface, or should we fully-qualify them? bool PreserveTypesAsWritten = false; + /// Should we emit the cType when printing @convention(c) or no? + /// FIXME: [clang-function-type-serialization] This check should go away. + bool PrintFullConvention = false; + /// Copy of all the command-line flags passed at .swiftinterface /// generation time, re-applied to CompilerInvocation when reading /// back .swiftinterface and reconstructing .swiftmodule. diff --git a/include/swift/Frontend/PrintingDiagnosticConsumer.h b/include/swift/Frontend/PrintingDiagnosticConsumer.h index b6768f1591f07..f0645839a7524 100644 --- a/include/swift/Frontend/PrintingDiagnosticConsumer.h +++ b/include/swift/Frontend/PrintingDiagnosticConsumer.h @@ -35,12 +35,8 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer { PrintingDiagnosticConsumer(llvm::raw_ostream &stream = llvm::errs()) : Stream(stream) { } - virtual void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + virtual void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override; void forceColors() { ForceColors = true; @@ -52,11 +48,7 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer { } private: - void printDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic); + void printDiagnostic(SourceManager &SM, const DiagnosticInfo &Info); }; } diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index e6b2d63290a49..186add43a425f 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -14,6 +14,7 @@ #define SWIFT_IDE_CODECOMPLETION_H #include "swift/AST/Identifier.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" @@ -314,7 +315,7 @@ class alignas(detail::CodeCompletionStringChunk) CodeCompletionString final : /// Print a debug representation of the code completion string to \p OS. void print(raw_ostream &OS) const; - void dump() const; + SWIFT_DEBUG_DUMP; }; /// Describes the origin of the code completion result. @@ -763,7 +764,7 @@ class CodeCompletionResult { /// Print a debug representation of the code completion result to \p OS. void print(raw_ostream &OS) const; - void dump() const; + SWIFT_DEBUG_DUMP; static CodeCompletionDeclKind getCodeCompletionDeclKind(const Decl *D); static CodeCompletionOperatorKind diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h new file mode 100644 index 0000000000000..0eb0de5eeb005 --- /dev/null +++ b/include/swift/IDE/CompletionInstance.h @@ -0,0 +1,93 @@ +//===--- CompletionInstance.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_COMPLETIONINSTANCE_H +#define SWIFT_IDE_COMPLETIONINSTANCE_H + +#include "swift/Frontend/Frontend.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace swift { + +class CompilerInstance; +class CompilerInvocation; +class DiagnosticConsumer; + +namespace ide { + +/// Copy a memory buffer inserting '\0' at the position of \c origBuf. +std::unique_ptr +makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + llvm::StringRef bufferIdentifier); + +/// Manages \c CompilerInstance for completion like operations. +class CompletionInstance { + unsigned MaxASTReuseCount = 100; + + std::mutex mtx; + + std::unique_ptr CachedCI; + llvm::hash_code CachedArgHash; + unsigned CachedReuseCount = 0; + + /// Calls \p Callback with cached \c CompilerInstance if it's usable for the + /// specified completion request. + /// Returns \c if the callback was called. Returns \c false if the compiler + /// argument has changed, primary file is not the same, the \c Offset is not + /// in function bodies, or the interface hash of the file has changed. + bool performCachedOperaitonIfPossible( + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + llvm::function_ref Callback); + + /// Calls \p Callback with new \c CompilerInstance for the completion + /// request. The \c CompilerInstace passed to the callback already performed + /// the first pass. + /// Returns \c false if it fails to setup the \c CompilerInstance. + bool performNewOperation( + llvm::Optional ArgsHash, + swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); + +public: + /// Calls \p Callback with a \c CompilerInstance which is prepared for the + /// second pass. \p Callback is resposible to perform the second pass on it. + /// The \c CompilerInstance may be reused from the previous completions, + /// and may be cached for the next completion. + /// Return \c true if \p is successfully called, \c it fails. In failure + /// cases \p Error is populated with an error message. + /// + /// NOTE: \p Args is only used for checking the equaity of the invocation. + /// Since this function assumes that it is already normalized, exact the same + /// arguments including their order is considered as the same invocation. + bool performOperation( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_COMPLETIONINSTANCE_H diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index b1566ce894dff..d00a337f4eb21 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage) KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry) KEY_BOOL(IsABIPlaceholder, isABIPlaceholder) KEY_BOOL(IsExternal, isExternal) +KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers) +KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers) KEY(kind) diff --git a/include/swift/IDE/RefactoringKinds.def b/include/swift/IDE/RefactoringKinds.def index 84daad8bc9b86..b6565439a2016 100644 --- a/include/swift/IDE/RefactoringKinds.def +++ b/include/swift/IDE/RefactoringKinds.def @@ -70,6 +70,8 @@ RANGE_REFACTORING(ConvertIfLetExprToGuardExpr, "Convert To Guard Expression", co RANGE_REFACTORING(ConvertGuardExprToIfLetExpr, "Convert To IfLet Expression", convert.to.iflet.expr) +RANGE_REFACTORING(ConvertToComputedProperty, "Convert To Computed Property", convert.to.computed.property) + // These internal refactorings are designed to be helpful for working on // the compiler/standard library, etc., but are likely to be just confusing and // noise for general development. diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index d0a216a872405..89936350ed971 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -240,7 +240,7 @@ class NameMatcher: public ASTWalker { /// The \c Expr argument of a parent \c CustomAttr (if one exists) and /// the \c SourceLoc of the type name it applies to. - llvm::Optional> CustomAttrArg; + llvm::Optional> CustomAttrArg; unsigned InactiveConfigRegionNestings = 0; unsigned SelectorNestings = 0; @@ -584,6 +584,11 @@ ClangNode getEffectiveClangNode(const Decl *decl); /// Retrieve the Clang node for the given extension, if it has one. ClangNode extensionGetClangNode(const ExtensionDecl *ext); +/// Utility for finding the referenced declaration from a call, which might +/// include a second level of function application for a 'self.' expression, +/// or a curry thunk, etc. +std::pair getReferencedDecl(Expr *expr); + } // namespace ide } // namespace swift diff --git a/include/swift/IRGen/IRGenPublic.h b/include/swift/IRGen/IRGenPublic.h index 3d1c11c487787..e92e85d18e1a9 100644 --- a/include/swift/IRGen/IRGenPublic.h +++ b/include/swift/IRGen/IRGenPublic.h @@ -31,6 +31,7 @@ class IRGenModule; std::pair createIRGenModule(SILModule *SILMod, StringRef OutputFilename, StringRef MainInputFilenameForDebugInfo, + StringRef PrivateDiscriminator, llvm::LLVMContext &LLVMContext); /// Delete the IRGenModule and IRGenerator obtained by the above call. diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index f7cd87e7f063d..56b76c07be474 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -65,7 +65,7 @@ class UniversalLinkageInfo { bool shouldAllPrivateDeclsBeVisibleFromOtherFiles() const { return HasMultipleIGMs; } - /// In case of multipe llvm modules, private lazy protocol + /// In case of multiple llvm modules, private lazy protocol /// witness table accessors could be emitted by two different IGMs during /// IRGen into different object files and the linker would complain about /// duplicate symbols. diff --git a/include/swift/Immediate/Immediate.h b/include/swift/Immediate/Immediate.h index 3a54005aeed88..e4f78f50ba517 100644 --- a/include/swift/Immediate/Immediate.h +++ b/include/swift/Immediate/Immediate.h @@ -18,6 +18,7 @@ #ifndef SWIFT_IMMEDIATE_IMMEDIATE_H #define SWIFT_IMMEDIATE_IMMEDIATE_H +#include #include #include @@ -25,6 +26,7 @@ namespace swift { class CompilerInstance; class IRGenOptions; class SILOptions; + class SILModule; // Using LLVM containers to store command-line arguments turns out // to be a lose, because LLVM's execution engine demands this vector @@ -37,7 +39,8 @@ namespace swift { /// /// \return the result returned from main(), if execution succeeded int RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, - IRGenOptions &IRGenOpts, const SILOptions &SILOpts); + const IRGenOptions &IRGenOpts, const SILOptions &SILOpts, + std::unique_ptr &&SM); void runREPL(CompilerInstance &CI, const ProcessCmdLine &CmdLine, bool ParseStdlib); diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index d3ea9867b1bc2..fa5efdedc6be4 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -37,10 +37,10 @@ struct CommentParts { Optional Brief; ArrayRef BodyNodes; ArrayRef ParamFields; - Optional ReturnsField; - Optional ThrowsField; + Optional ReturnsField; + Optional ThrowsField; llvm::SmallSetVector Tags; - Optional LocalizationKeyField; + Optional LocalizationKeyField; bool isEmpty() const { return !Brief.hasValue() && diff --git a/include/swift/Migrator/FixitApplyDiagnosticConsumer.h b/include/swift/Migrator/FixitApplyDiagnosticConsumer.h index 112cd15f5d92e..4db9bb78ddfba 100644 --- a/include/swift/Migrator/FixitApplyDiagnosticConsumer.h +++ b/include/swift/Migrator/FixitApplyDiagnosticConsumer.h @@ -62,11 +62,7 @@ class FixitApplyDiagnosticConsumer final /// output stream. void printResult(llvm::raw_ostream &OS) const; - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; unsigned getNumFixitsApplied() const { return NumFixitsApplied; diff --git a/include/swift/Migrator/FixitFilter.h b/include/swift/Migrator/FixitFilter.h index cf5e70541d21c..b13abeee62d0a 100644 --- a/include/swift/Migrator/FixitFilter.h +++ b/include/swift/Migrator/FixitFilter.h @@ -25,8 +25,7 @@ namespace migrator { struct FixitFilter { /// Returns true if the fix-it should be applied. - bool shouldTakeFixit(const DiagnosticKind Kind, - const DiagnosticInfo &Info) const { + bool shouldTakeFixit(const DiagnosticInfo &Info) const { // Do not add a semi or comma as it is wrong in most cases during migration if (Info.ID == diag::statement_same_line_without_semi.ID || Info.ID == diag::declaration_same_line_without_semi.ID || @@ -114,7 +113,7 @@ struct FixitFilter { return false; } - if (Kind == DiagnosticKind::Error) + if (Info.Kind == DiagnosticKind::Error) return true; // Fixits from warnings/notes that should be applied. diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 8a80a817e3ee6..f8fd3abd492b0 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -41,6 +41,9 @@ def emit_module_doc_path def emit_module_source_info : Flag<["-"], "emit-module-source-info">, HelpText<"Output module source info file">; +def ignore_module_source_info : Flag<["-"], "ignore-module-source-info">, + HelpText<"Avoid getting source location from .swiftsourceinfo files">; + def merge_modules : Flag<["-"], "merge-modules">, ModeOpt, HelpText<"Merge the input modules without otherwise processing them">; @@ -55,6 +58,19 @@ def emit_reference_dependencies_path : Separate<["-"], "emit-reference-dependencies-path">, MetaVarName<"">, HelpText<"Output Swift-style dependencies file to ">; + +def emit_swift_ranges : Flag<["-"], "emit-swift-ranges">, + HelpText<"Emit unparsed source ranges">; +def emit_swift_ranges_path + : Separate<["-"], "emit-swift-ranges-path">, MetaVarName<"">, + HelpText<"Output unparsed ranges to ">; + +def emit_compiled_source : Flag<["-"], "emit-compiled-source">, + HelpText<"Emit compiled source">; +def emit_compiled_source_path + : Separate<["-"], "emit-compiled-source-path">, MetaVarName<"">, + HelpText<"Output compiled source to ">; + def emit_fixits_path : Separate<["-"], "emit-fixits-path">, MetaVarName<"">, HelpText<"Output compiler fixits as source edits to ">; @@ -109,6 +125,13 @@ def verify_syntax_tree : Flag<["-"], "verify-syntax-tree">, def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">, HelpText<"Keep emitting subsequent diagnostics after a fatal error">; + +def enable_descriptive_diagnostics : Flag<["-"], "enable-descriptive-diagnostics">, + HelpText<"Show descriptive diagnostic information, if available.">; + +def diagnostic_documentation_path + : Separate<["-"], "diagnostic-documentation-path">, MetaVarName<"">, + HelpText<"Path to diagnostic documentation resources">; def enable_swiftcall : Flag<["-"], "enable-swiftcall">, HelpText<"Enable the use of LLVM swiftcall support">; @@ -141,6 +164,9 @@ def warn_if_astscope_lookup : Flag<["-"], "warn-if-astscope-lookup">, def lazy_astscopes : Flag<["-"], "lazy-astscopes">, HelpText<"Build ASTScopes lazily">; +def use_clang_function_types : Flag<["-"], "use-clang-function-types">, + HelpText<"Use stored Clang function types for computing canonical types.">; + def print_clang_stats : Flag<["-"], "print-clang-stats">, HelpText<"Print Clang importer statistics">; @@ -220,8 +246,10 @@ def debug_forbid_typecheck_prefix : Separate<["-"], "debug-forbid-typecheck-pref def debug_cycles : Flag<["-"], "debug-cycles">, HelpText<"Print out debug dumps when cycles are detected in evaluation">; +def build_request_dependency_graph : Flag<["-"], "build-request-dependency-graph">, + HelpText<"Build request dependency graph">; def output_request_graphviz : Separate<["-"], "output-request-graphviz">, - HelpText<"Emit GraphViz output visualizing the request graph">; + HelpText<"Emit GraphViz output visualizing the request dependency graph">; def debug_time_compilation : Flag<["-"], "debug-time-compilation">, HelpText<"Prints the time taken by each compilation phase">; @@ -242,12 +270,21 @@ def debug_crash_after_parse : Flag<["-"], "debug-crash-after-parse">, def debugger_support : Flag<["-"], "debugger-support">, HelpText<"Process swift code as if running in the debugger">; +def disable_clangimporter_source_import : Flag<["-"], + "disable-clangimporter-source-import">, + HelpText<"Disable ClangImporter and forward all requests straight the DWARF importer.">; + def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; +def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, + HelpText<"Don't run SIL OSSA optimization passes.">; def disable_sil_partial_apply : Flag<["-"], "disable-sil-partial-apply">, HelpText<"Disable use of partial_apply in SIL generation">; +def enable_spec_devirt : Flag<["-"], "enable-spec-devirt">, + HelpText<"Enable speculative devirtualization pass.">; + def enable_ownership_stripping_after_serialization : Flag<["-"], "enable-ownership-stripping-after-serialization">, HelpText<"Strip ownership after serialization">; @@ -300,6 +337,10 @@ def disable_reflection_names : Flag<["-"], "disable-reflection-names">, HelpText<"Disable emission of names of stored properties and enum cases in" "reflection metadata">; +def function_sections: Flag<["-"], "function-sections">, + Flags<[FrontendOption, NoInteractiveOption]>, + HelpText<"Emit functions to separate sections.">; + def stack_promotion_checks : Flag<["-"], "emit-stack-promotion-checks">, HelpText<"Emit runtime checks for correct stack promotion of objects.">; @@ -338,6 +379,10 @@ def enable_experimental_static_assert : Flag<["-"], "enable-experimental-static-assert">, HelpText<"Enable experimental #assert">; +def enable_subst_sil_function_types_for_function_values : + Flag<["-"], "enable-subst-sil-function-types-for-function-values">, + HelpText<"Use substituted function types for SIL type lowering of function values">; + def enable_deserialization_recovery : Flag<["-"], "enable-deserialization-recovery">, HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">; @@ -349,10 +394,6 @@ def disable_availability_checking : Flag<["-"], "disable-availability-checking">, HelpText<"Disable checking for potentially unavailable APIs">; -def disable_tsan_inout_instrumentation : Flag<["-"], - "disable-tsan-inout-instrumentation">, - HelpText<"Disable treatment of inout parameters as Thread Sanitizer accesses">; - def report_errors_to_debugger : Flag<["-"], "report-errors-to-debugger">, HelpText<"Deprecated, will be removed in future versions.">; @@ -414,14 +455,6 @@ def Rmodule_interface_rebuild : Flag<["-"], "Rmodule-interface-rebuild">, def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">; -def enable_function_builder_one_way_constraints : Flag<["-"], - "enable-function-builder-one-way-constraints">, - HelpText<"Enable one-way constraints in the function builder transformation">; - -def disable_function_builder_one_way_constraints : Flag<["-"], - "disable-function-builder-one-way-constraints">, - HelpText<"Disable one-way constraints in the function builder transformation">; - def solver_disable_shrink : Flag<["-"], "solver-disable-shrink">, HelpText<"Disable the shrink phase of expression type checking">; @@ -437,6 +470,14 @@ def solver_enable_operator_designated_types : Flag<["-"], "solver-enable-operator-designated-types">, HelpText<"Enable operator designated types in constraint solver">; +def enable_invalid_ephemeralness_as_error : + Flag<["-"], "enable-invalid-ephemeralness-as-error">, + HelpText<"Diagnose invalid ephemeral to non-ephemeral conversions as errors">; + +def disable_invalid_ephemeralness_as_error : + Flag<["-"], "disable-invalid-ephemeralness-as-error">, + HelpText<"Diagnose invalid ephemeral to non-ephemeral conversions as warnings">; + def switch_checking_invocation_threshold_EQ : Joined<["-"], "switch-checking-invocation-threshold=">; @@ -459,6 +500,11 @@ def debugger_testing_transform : Flag<["-"], "debugger-testing-transform">, HelpText<"Instrument the code with calls to an intrinsic that record the expected values of " "local variables so they can be compared against the results from the debugger.">; +def disable_debugger_shadow_copies : Flag<["-"], "disable-debugger-shadow-copies">, + HelpText<"Disable debugger shadow copies of local variables." + "This option is only useful for testing the compiler.">, + Flags<[FrontendOption, HelpHidden]>; + def playground : Flag<["-"], "playground">, HelpText<"Apply the playground semantics and transformation">; @@ -565,6 +611,11 @@ def module_interface_preserve_types_as_written : HelpText<"When emitting a module interface, preserve types as they were " "written in the source">; +def experimental_print_full_convention : + Flag<["-"], "experimental-print-full-convention">, + HelpText<"When emitting a module interface, emit additional @convention " + "arguments, regardless of whether they were written in the source">; + def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; @@ -600,6 +651,10 @@ def disable_verify_exclusivity : Flag<["-"], "disable-verify-exclusivity">, def disable_legacy_type_info : Flag<["-"], "disable-legacy-type-info">, HelpText<"Completely disable legacy type layout">; +def prespecialize_generic_metadata : Flag<["-"], "prespecialize-generic-metadata">, + HelpText<"Statically specialize metadata for generic types at types that " + "are known to be used in source.">; + def read_legacy_type_info_path_EQ : Joined<["-"], "read-legacy-type-info-path=">, HelpText<"Read legacy type layout from the given path instead of default path">; @@ -607,4 +662,11 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">, Flags<[FrontendOption]>, HelpText<"One of 'all', 'resilient' or 'fragile'">; +def emit_ldadd_cfile_path + : Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"">, + HelpText<"Generate .c file defining symbols to add back">; + +def previous_module_installname_map_file + : Separate<["-"], "previous-module-installname-map-file">, MetaVarName<"">, + HelpText<"Path to a Json file indicating module name to installname map for @_originallyDefinedIn">; } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 166730ec17174..dbadf83aa6318 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -104,6 +104,12 @@ def driver_show_incremental : Flag<["-"], "driver-show-incremental">, def driver_show_job_lifecycle : Flag<["-"], "driver-show-job-lifecycle">, InternalDebugOpt, HelpText<"Show every step in the lifecycle of driver jobs">; +def driver_dump_swift_ranges : Flag<["-"], "driver-dump-swift-ranges">, + InternalDebugOpt, + HelpText<"Show the unparsed ranges read upon startup">; +def driver_dump_compiled_source_diffs : Flag<["-"], "driver-dump-compiled-source-diffs">, + InternalDebugOpt, + HelpText<"Show the compiled source diffs read upon startup">; def driver_use_filelists : Flag<["-"], "driver-use-filelists">, InternalDebugOpt, HelpText<"Pass input files as filelists whenever possible">; def driver_filelist_threshold : Separate<["-"], "driver-filelist-threshold">, @@ -129,25 +135,70 @@ def driver_always_rebuild_dependents : Flag<["-"], "driver-always-rebuild-dependents">, InternalDebugOpt, HelpText<"Always rebuild dependents of files that have been modified">; -def enable_experimental_dependencies : -Flag<["-"], "enable-experimental-dependencies">, Flags<[FrontendOption, HelpHidden]>, -HelpText<"Experimental work-in-progress to be more selective about incremental recompilation">; +def enable_fine_grained_dependencies : +Flag<["-"], "enable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, +HelpText<"Be more selective about incremental recompilation">; + +def disable_fine_grained_dependencies : +Flag<["-"], "disable-fine-grained-dependencies">, Flags<[FrontendOption, HelpHidden]>, +HelpText<"Don't be more selective about incremental recompilation">; + +def enable_type_fingerprints : +Flag<["-"], "enable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, +HelpText<"Enable per-nominal and extension body fingerprints">; + +def disable_type_fingerprints : +Flag<["-"], "disable-type-fingerprints">, Flags<[FrontendOption, HelpHidden]>, +HelpText<"Disable per-nominal and extension body fingerprints">; + +def enable_only_one_dependency_file : +Flag<["-"], "enable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Enables incremental build optimization that only produces one dependencies file">; + +def disable_only_one_dependency_file : +Flag<["-"], "disable-only-one-dependency-file">, Flags<[DoesNotAffectIncrementalBuild]>, + HelpText<"Disables incremental build optimization that only produces one dependencies file">; + + +def enable_source_range_dependencies : +Flag<["-"], "enable-source-range-dependencies">, Flags<[]>, +HelpText<"Try using source range information">; -def driver_verify_experimental_dependency_graph_after_every_import : -Flag<["-"], "driver-verify-experimental-dependency-graph-after-every-import">, + +def driver_compare_incremental_schemes : +Flag<["-"], "driver-compare-incremental-schemes">, Flags<[DoesNotAffectIncrementalBuild]>, +HelpText<"Print a simple message comparing dependencies with source ranges (w/ fallback)">; + +def driver_compare_incremental_schemes_path : +Separate<["-"], "driver-compare-incremental-schemes-path">, Flags<[ArgumentIsPath,DoesNotAffectIncrementalBuild]>, +HelpText<"Path to use for machine-readable comparision">, +MetaVarName<"">; + +def driver_compare_incremental_schemes_path_EQ : +Joined<["-"], "driver-compare-incremental-schemes-path=">, Flags<[]>, +Alias; + + +def driver_verify_fine_grained_dependency_graph_after_every_import : +Flag<["-"], "driver-verify-fine-grained-dependency-graph-after-every-import">, InternalDebugOpt, HelpText<"Debug DriverGraph by verifying it after every import">; -def driver_emit_experimental_dependency_dot_file_after_every_import : -Flag<["-"], "driver-emit-experimental-dependency-dot-file-after-every-import">, +def driver_emit_fine_grained_dependency_dot_file_after_every_import : +Flag<["-"], "driver-emit-fine-grained-dependency-dot-file-after-every-import">, InternalDebugOpt, -HelpText<"Emit dot files every time driver imports an experimental swiftdeps file.">; +HelpText<"Emit dot files every time driver imports an fine-grained swiftdeps file.">; -def experimental_dependency_include_intrafile : -Flag<["-"], "experimental-dependency-include-intrafile">, -InternalDebugOpt, +def fine_grained_dependency_include_intrafile : +Flag<["-"], "fine-grained-dependency-include-intrafile">, +Flags<[FrontendOption, HelpHidden]>, HelpText<"Include within-file dependencies.">; +def emit_fine_grained_dependency_sourcefile_dot_files : +Flag<["-"], "emit-fine-grained-dependency-sourcefile-dot-files">, +InternalDebugOpt, +HelpText<"Emit dot files for every source file.">; + def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>, HelpText<"Set the driver mode to either 'swift' or 'swiftc'">; @@ -429,6 +480,11 @@ def enable_experimental_differentiable_programming : Flag<["-"], "enable-experim Flags<[FrontendOption]>, HelpText<"Enable experimental differentiable programming features">; +def enable_experimental_concise_pound_file : Flag<["-"], + "enable-experimental-concise-pound-file">, + Flags<[FrontendOption]>, + HelpText<"Enable experimental concise '#file' identifier and '#filePath' alternative">; + // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, Flags<[FrontendOption]>, @@ -564,6 +620,10 @@ def Oplayground : Flag<["-"], "Oplayground">, Group, Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>, HelpText<"Compile with optimizations appropriate for a playground">; +def CrossModuleOptimization : Flag<["-"], "cross-module-optimization">, + Flags<[HelpHidden, FrontendOption]>, + HelpText<"Perform cross-module optimization">; + def RemoveRuntimeAsserts : Flag<["-"], "remove-runtime-asserts">, Flags<[FrontendOption]>, HelpText<"Remove runtime safety checks.">; @@ -748,6 +808,9 @@ def emit_sibgen : Flag<["-"], "emit-sibgen">, def emit_imported_modules : Flag<["-"], "emit-imported-modules">, HelpText<"Emit a list of the imported modules">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def emit_pcm : Flag<["-"], "emit-pcm">, + HelpText<"Emit a precompiled Clang module from a module map">, ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; def c : Flag<["-"], "c">, Alias, Flags<[FrontendOption, NoInteractiveOption]>, ModeOpt; @@ -792,6 +855,10 @@ def print_ast : Flag<["-"], "print-ast">, HelpText<"Parse and type-check input file(s) and pretty print AST(s)">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def dump_pcm : Flag<["-"], "dump-pcm">, + HelpText<"Dump debugging information about a precompiled Clang module">, + ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; // Other Modes def repl : Flag<["-"], "repl">, @@ -807,6 +874,10 @@ def whole_module_optimization : Flag<["-"], "whole-module-optimization">, HelpText<"Optimize input files together instead of individually">, Flags<[FrontendOption, NoInteractiveOption]>; +def no_whole_module_optimization : Flag<["-"], "no-whole-module-optimization">, + HelpText<"Disable optimizing input files together instead of individually">, + Flags<[FrontendOption, NoInteractiveOption]>; + def enable_batch_mode : Flag<["-"], "enable-batch-mode">, Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>, HelpText<"Enable combining frontend jobs into batches">; @@ -852,10 +923,18 @@ def target : Separate<["-"], "target">, HelpText<"Generate code for the given target , such as x86_64-apple-macos10.9">, MetaVarName<"">; def target_legacy_spelling : Joined<["--"], "target=">, Flags<[FrontendOption]>, Alias; +def print_target_info : Flag<["-"], "print-target-info">, + Flags<[FrontendOption]>, + HelpText<"Print target information for the given target , such as x86_64-apple-macos10.9">, MetaVarName<"">; def target_cpu : Separate<["-"], "target-cpu">, Flags<[FrontendOption, ModuleInterfaceOption]>, HelpText<"Generate code for a particular CPU variant">; +def target_variant : Separate<["-"], "target-variant">, + Flags<[FrontendOption]>, + HelpText<"Generate 'zippered' code for macCatalyst that can run on the specified" + " variant target triple in addition to the main -target triple">; + def profile_generate : Flag<["-"], "profile-generate">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Generate instrumented code to collect execution counts">; @@ -889,6 +968,15 @@ def sanitize_EQ : CommaJoined<["-"], "sanitize=">, Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"">, HelpText<"Turn on runtime checks for erroneous behavior.">; +def sanitize_recover_EQ + : CommaJoined<["-"], "sanitize-recover=">, + Flags<[FrontendOption, NoInteractiveOption]>, + MetaVarName<"">, + HelpText<"Specify which sanitizer runtime checks (see -sanitize=) will " + "generate instrumentation that allows error recovery. Listed " + "checks should be comma separated. Default behavior is to not " + "allow error recovery.">; + def sanitize_coverage_EQ : CommaJoined<["-"], "sanitize-coverage=">, Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"">, diff --git a/include/swift/Option/SanitizerOptions.h b/include/swift/Option/SanitizerOptions.h index f19500c8ac4c0..8a29853e3790d 100644 --- a/include/swift/Option/SanitizerOptions.h +++ b/include/swift/Option/SanitizerOptions.h @@ -36,6 +36,10 @@ OptionSet parseSanitizerArgValues( const llvm::Triple &Triple, DiagnosticEngine &Diag, llvm::function_ref sanitizerRuntimeLibExists); +OptionSet parseSanitizerRecoverArgValues( + const llvm::opt::Arg *A, const OptionSet &enabledSanitizers, + DiagnosticEngine &Diags, bool emitWarnings); + /// Parses a -sanitize-coverage= argument's value. llvm::SanitizerCoverageOptions parseSanitizerCoverageArgValue( const llvm::opt::Arg *A, diff --git a/include/swift/Parse/ASTGen.h b/include/swift/Parse/ASTGen.h deleted file mode 100644 index 312f90f19b8e7..0000000000000 --- a/include/swift/Parse/ASTGen.h +++ /dev/null @@ -1,261 +0,0 @@ -//===--- ASTGen.h ---------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_PARSE_ASTGEN_H -#define SWIFT_PARSE_ASTGEN_H - -#include "swift/AST/ASTContext.h" -#include "swift/AST/Decl.h" -#include "swift/AST/Expr.h" -#include "swift/AST/TypeRepr.h" -#include "swift/Parse/PersistentParserState.h" -#include "swift/Syntax/SyntaxNodes.h" -#include "llvm/ADT/DenseMap.h" - -namespace swift { -class ComponentIdentTypeRepr; -class TupleTypeRepr; - -/// Generates AST nodes from Syntax nodes. -class Parser; -class ASTGen { - ASTContext &Context; - - /// Type cache to prevent multiple transformations of the same syntax node. - llvm::DenseMap TypeCache; - - Parser &P; - - // FIXME: remove when Syntax can represent all types and ASTGen can handle - // them - /// Exprs that cannot be represented by Syntax or generated by ASTGen. - llvm::DenseMap Exprs; - - /// Decl attributes that cannot be represented by Syntax or generated by - /// ASTGen. - llvm::DenseMap ParsedDeclAttrs; - -public: - ASTGen(ASTContext &Context, Parser &P) : Context(Context), P(P) {} - - SourceLoc generate(const syntax::TokenSyntax &Tok, const SourceLoc Loc); - - SourceLoc generateIdentifierDeclName(const syntax::TokenSyntax &Tok, - const SourceLoc, Identifier &Identifier); - -public: - //===--------------------------------------------------------------------===// - // Decls. - - Decl *generate(const syntax::DeclSyntax &Decl, const SourceLoc Loc); - TypeDecl *generate(const syntax::AssociatedtypeDeclSyntax &Decl, - const SourceLoc Loc); - TypeDecl *generate(const syntax::TypealiasDeclSyntax &Decl, - const SourceLoc Loc); - - TrailingWhereClause *generate(const syntax::GenericWhereClauseSyntax &syntax, - const SourceLoc Loc); - MutableArrayRef - generate(const syntax::TypeInheritanceClauseSyntax &syntax, - const SourceLoc Loc, bool allowClassRequirement); - -private: - DeclAttributes - generateDeclAttributes(const syntax::Syntax &D, SourceLoc Loc, - bool includeComments); - - void generateFreeStandingGenericWhereClause( - const syntax::GenericWhereClauseSyntax &syntax, - const SourceLoc Loc, - GenericParamList *genericParams); - -public: - //===--------------------------------------------------------------------===// - // Expressions. - - Expr *generate(const syntax::ExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::IdentifierExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::SuperRefExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::ArrayExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::DictionaryExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::TupleExprSyntax &E, const SourceLoc Loc); - Expr *generate(const syntax::FunctionCallExprSyntax &E, const SourceLoc Loc); - Expr *generate(const syntax::MemberAccessExprSyntax &E, const SourceLoc Loc); - Expr *generate(const syntax::EditorPlaceholderExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::SpecializeExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::IntegerLiteralExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::FloatLiteralExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::NilLiteralExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::BooleanLiteralExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::PoundFileExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::PoundLineExprSyntax &Expr, const SourceLoc Loc); - Expr *generate(const syntax::PoundColumnExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::PoundFunctionExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::PoundDsohandleExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::ObjcKeyPathExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::ObjectLiteralExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::CodeCompletionExprSyntax &Expr, - const SourceLoc Loc); - Expr *generate(const syntax::UnknownExprSyntax &Expr, const SourceLoc Loc); - - std::pair generateUnqualifiedDeclName( - const syntax::TokenSyntax &idTok, - const Optional &args, - const SourceLoc Loc); - - void generateExprTupleElementList(const syntax::TupleExprElementListSyntax &elements, - const SourceLoc Loc, bool isForCallArguments, - SmallVectorImpl &exprs, - SmallVectorImpl &exprLabels, - SmallVectorImpl &exprLabelLocs); - -private: - void validateCollectionElement(Expr *elementExpr); - - Expr *generateMagicIdentifierLiteralExpression( - const syntax::TokenSyntax &PoundToken, const SourceLoc Loc); - - static MagicIdentifierLiteralExpr::Kind - getMagicIdentifierLiteralKind(tok Kind); - -public: - //===--------------------------------------------------------------------===// - // Types. - - TypeRepr *generate(const syntax::TypeSyntax &Type, const SourceLoc Loc, - bool IsSILFuncDecl = false); - TypeRepr *generate(const syntax::SomeTypeSyntax &Type, const SourceLoc Loc); - TypeRepr *generate(const syntax::CompositionTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::SimpleTypeIdentifierSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::MemberTypeIdentifierSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::DictionaryTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::ArrayTypeSyntax &Type, const SourceLoc Loc); - TypeRepr *generate(const syntax::TupleTypeSyntax &Type, const SourceLoc Loc); - TypeRepr *generate(const syntax::AttributedTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::FunctionTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::MetatypeTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::OptionalTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::ImplicitlyUnwrappedOptionalTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::ClassRestrictionTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::SILBoxTypeSyntax &Type, const SourceLoc Loc, - bool IsSILFuncDecl); - TypeRepr *generate(const syntax::SILFunctionTypeSyntax &Type, - const SourceLoc Loc, bool IsSILFuncDecl); - TypeRepr *generate(const syntax::CodeCompletionTypeSyntax &Type, - const SourceLoc Loc); - TypeRepr *generate(const syntax::UnknownTypeSyntax &Type, - const SourceLoc Loc); - - TypeAttributes - generateTypeAttributes(const syntax::AttributeListSyntax &syntax, - const SourceLoc Loc); - -private: - TupleTypeRepr * - generateTuple(const syntax::TokenSyntax &LParen, - const syntax::TupleTypeElementListSyntax &Elements, - const syntax::TokenSyntax &RParen, const SourceLoc Loc, - bool IsFunction = false); - - void gatherTypeIdentifierComponents( - const syntax::TypeSyntax &Component, const SourceLoc Loc, - llvm::SmallVectorImpl &Components); - - template - TypeRepr *generateSimpleOrMemberIdentifier(const T &Type, - const SourceLoc Loc); - - template - ComponentIdentTypeRepr *generateIdentifier(const T &Type, - const SourceLoc Loc); - -public: - //===--------------------------------------------------------------------===// - // Generics. - - void generate(const syntax::GenericArgumentClauseSyntax &Arg, - const SourceLoc Loc, SourceLoc &lAngleLoc, SourceLoc &rAngleLoc, - SmallVectorImpl &args); - - GenericParamList * - generate(const syntax::GenericParameterClauseListSyntax &clause, - const SourceLoc Loc); - GenericParamList *generate(const syntax::GenericParameterClauseSyntax &clause, - const SourceLoc Loc); - Optional - generate(const syntax::GenericRequirementSyntax &req, const SourceLoc Loc); - LayoutConstraint generate(const syntax::LayoutConstraintSyntax &req, - const SourceLoc Loc); - -public: - //===--------------------------------------------------------------------===// - // Utilities. - - /// Copy a numeric literal value into AST-owned memory, stripping underscores - /// so the semantic part of the value can be parsed by APInt/APFloat parsers. - static StringRef copyAndStripUnderscores(StringRef Orig, ASTContext &Context); - -private: - StringRef copyAndStripUnderscores(StringRef Orig); - - /// Advance \p Loc to the first token of the \p Node. - /// \p Loc must be the leading trivia of the first token in the tree in which - /// \p Node resides. - static SourceLoc advanceLocBegin(const SourceLoc &Loc, - const syntax::Syntax &Node); - - /// Advance \p Loc to the last non-missing token of the \p Node or, if it - /// doesn't contain any, the last non-missing token preceding it in the tree. - /// \p Loc must be the leading trivia of the first token in the tree in which - /// \p Node resides - static SourceLoc advanceLocEnd(const SourceLoc &Loc, - const syntax::Syntax &Node); - - ValueDecl *lookupInScope(DeclName Name); - - void addToScope(ValueDecl *D, bool diagnoseRedefinitions = true); - - TypeRepr *cacheType(syntax::TypeSyntax Type, TypeRepr *TypeAST); - - TypeRepr *lookupType(syntax::TypeSyntax Type); - -public: - void addExpr(Expr *Expr, const SourceLoc Loc); - bool hasExpr(const SourceLoc Loc) const; - Expr *takeExpr(const SourceLoc Loc); - - void addDeclAttributes(DeclAttributes attrs, const SourceLoc Loc); - bool hasDeclAttributes(SourceLoc Loc) const; - DeclAttributes takeDeclAttributes(const SourceLoc Loc); -}; -} // namespace swift - -#endif // SWIFT_PARSE_ASTGEN_H diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index b221f772ce539..7b1c86308143e 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -40,8 +40,6 @@ class CodeCompletionCallbacks { /// completion. This declaration contained the code completion token. Decl *ParsedDecl = nullptr; - TypeLoc ParsedTypeLoc; - /// True if code completion is done inside a raw value expression of an enum /// case. bool InEnumElementRawValue = false; @@ -78,10 +76,6 @@ class CodeCompletionCallbacks { ParsedDecl = D; } - void setParsedTypeLoc(TypeLoc TyLoc) { - ParsedTypeLoc = TyLoc; - } - void setLeadingSequenceExprs(ArrayRef exprs) { leadingSequenceExprs.assign(exprs.begin(), exprs.end()); } @@ -124,10 +118,6 @@ class CodeCompletionCallbacks { /// Set target decl for attribute if the CC token is in attribute of the decl. virtual void setAttrTargetDeclKind(Optional DK) {} - /// Complete the whole expression. This is a fallback that should - /// produce results when more specific completion methods failed. - virtual void completeExpr() {}; - /// Complete expr-dot after we have consumed the dot. virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) {}; @@ -165,10 +155,10 @@ class CodeCompletionCallbacks { virtual void completeTypeSimpleBeginning() {}; /// Complete a given type-identifier after we have consumed the dot. - virtual void completeTypeIdentifierWithDot() {}; + virtual void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) {}; /// Complete a given type-identifier when there is no trailing dot. - virtual void completeTypeIdentifierWithoutDot() {}; + virtual void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) {}; /// Complete the beginning of a case statement at the top of switch stmt. virtual void completeCaseStmtKeyword() {}; @@ -200,7 +190,7 @@ class CodeCompletionCallbacks { /// Complete the import decl with importable modules. virtual void - completeImportDecl(std::vector> &Path) {}; + completeImportDecl(std::vector> &Path) {}; /// Complete unresolved members after dot. virtual void completeUnresolvedMember(CodeCompletionExpr *E, diff --git a/include/swift/Parse/HiddenLibSyntaxAction.h b/include/swift/Parse/HiddenLibSyntaxAction.h deleted file mode 100644 index 7f26db622a1f2..0000000000000 --- a/include/swift/Parse/HiddenLibSyntaxAction.h +++ /dev/null @@ -1,98 +0,0 @@ -//===--- HiddenLibSyntaxAction.h ------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_PARSE_HIDDENLIBSYNTAXACTION_H -#define SWIFT_PARSE_HIDDENLIBSYNTAXACTION_H - -#include "swift/Parse/SyntaxParseActions.h" -#include "swift/SyntaxParse/SyntaxTreeCreator.h" -#include "llvm/Support/Allocator.h" - -namespace swift { -namespace syntax { -class RawSyntax; -} - -/// Holds an explicitly provided action and uses it to handle all function -/// calls. Also hides an implicit SyntaxTreeCreator and ensures libSyntax nodes -/// are always created. Provides an interface to map results of the explicitly -/// provided action to the hidden libSyntax action. -// todo [gsoc]: remove when possible -class HiddenLibSyntaxAction : public SyntaxParseActions { - - struct Node { - OpaqueSyntaxNode ExplicitActionNode; - OpaqueSyntaxNode LibSyntaxNode; - - Node(OpaqueSyntaxNode ExplicitActionNode, OpaqueSyntaxNode LibSyntaxNode) - : ExplicitActionNode(ExplicitActionNode), LibSyntaxNode(LibSyntaxNode) { - } - }; - - std::shared_ptr ExplicitAction; - std::shared_ptr LibSyntaxAction; - llvm::SpecificBumpPtrAllocator NodeAllocator; - - bool areBothLibSyntax() { - return ExplicitAction->getOpaqueKind() == OpaqueSyntaxNodeKind::LibSyntax; - } - - OpaqueSyntaxNode makeHiddenNode(OpaqueSyntaxNode explicitActionNode, - OpaqueSyntaxNode libSyntaxNode); - -public: - HiddenLibSyntaxAction( - const std::shared_ptr &SPActions, - const std::shared_ptr &LibSyntaxAction) - : ExplicitAction(SPActions != nullptr ? SPActions : LibSyntaxAction), - LibSyntaxAction(LibSyntaxAction){}; - - OpaqueSyntaxNode recordToken(tok tokenKind, - ArrayRef leadingTrivia, - ArrayRef trailingTrivia, - CharSourceRange range) override; - - OpaqueSyntaxNode recordMissingToken(tok tokenKind, SourceLoc loc) override; - - OpaqueSyntaxNode recordRawSyntax(syntax::SyntaxKind kind, - ArrayRef elements, - CharSourceRange range) override; - - std::pair - lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) override; - - void discardRecordedNode(OpaqueSyntaxNode node) override; - - OpaqueSyntaxNodeKind getOpaqueKind() override { - return ExplicitAction->getOpaqueKind(); - } - - /// Returns the libSyntax node from the specified node that has been created - /// by this action. - syntax::RawSyntax *getLibSyntaxNodeFor(OpaqueSyntaxNode node); - - /// Returns the node created by explicit syntax action from the specified - /// node that has been created by this action. - OpaqueSyntaxNode getExplicitNodeFor(OpaqueSyntaxNode node); - - bool isReleaseNeeded() { - return ExplicitAction == LibSyntaxAction || !areBothLibSyntax(); - } - - /// Returns the underlying libSyntax SyntaxTreeCreator. - std::shared_ptr getLibSyntaxAction() { - return LibSyntaxAction; - } -}; -} // namespace swift - -#endif // SWIFT_PARSE_HIDDENLIBSYNTAXACTION_H diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 2849c41f1eca3..8cd9b75a415c7 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -200,8 +200,8 @@ class Lexer { ParsedTrivia &TrailingTriviaResult) { Result = NextToken; if (TriviaRetention == TriviaRetentionMode::WithTrivia) { - std::swap(LeadingTriviaResult, LeadingTrivia); - std::swap(TrailingTriviaResult, TrailingTrivia); + LeadingTriviaResult = {LeadingTrivia}; + TrailingTriviaResult = {TrailingTrivia}; } if (Result.isNot(tok::eof)) lexImpl(); @@ -293,7 +293,17 @@ class Lexer { /// resides. /// /// \param Loc The source location of the beginning of a token. - static Token getTokenAtLocation(const SourceManager &SM, SourceLoc Loc); + /// + /// \param CRM How comments should be treated by the lexer. Default is to + /// return the comments as tokens. This is needed in situations where + /// detecting the next semantically meaningful token is required, such as + /// the 'implicit self' diagnostic determining whether a capture list is + /// empty (i.e., the opening bracket is immediately followed by a closing + /// bracket, possibly with comments in between) in order to insert the + /// appropriate fix-it. + static Token getTokenAtLocation( + const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM = CommentRetentionMode::ReturnAsTokens); /// Retrieve the source location that points just past the diff --git a/include/swift/Parse/LibSyntaxGenerator.h b/include/swift/Parse/LibSyntaxGenerator.h deleted file mode 100644 index 1e8dc2b74b60c..0000000000000 --- a/include/swift/Parse/LibSyntaxGenerator.h +++ /dev/null @@ -1,81 +0,0 @@ -//===----------- LibSyntaxGenerator.h -------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_PARSE_LIBSYNTAXGENERATOR_H -#define SWIFT_PARSE_LIBSYNTAXGENERATOR_H - -#include "swift/Parse/HiddenLibSyntaxAction.h" -#include "swift/Parse/ParsedRawSyntaxNode.h" -#include "swift/Parse/ParsedRawSyntaxRecorder.h" -#include "swift/Parse/SyntaxParsingCache.h" -#include "swift/Syntax/RawSyntax.h" -#include "swift/SyntaxParse/SyntaxTreeCreator.h" - -namespace swift { -/// Generates libSyntax nodes either by looking them up using -/// HiddenLibSyntaxAction (based on provided OpaqueSyntaxNode) or by recording -/// them with ParsedRawSyntaxRecorder. -// todo [gsoc]: remove when possible -class LibSyntaxGenerator { - std::shared_ptr Actions; - ParsedRawSyntaxRecorder Recorder; - -public: - explicit LibSyntaxGenerator(std::shared_ptr TwoActions) - : Actions(std::move(TwoActions)), - Recorder(Actions->getLibSyntaxAction()) {} - - TokenSyntax createToken(ParsedRawSyntaxNode Node) { - assert(Node.isDeferredToken()); - - auto Kind = Node.getTokenKind(); - auto Range = Node.getDeferredTokenRange(); - auto LeadingTriviaPieces = Node.getDeferredLeadingTriviaPieces(); - auto TrailingTriviaPieces = Node.getDeferredTrailingTriviaPieces(); - - auto Recorded = Recorder.recordToken(Kind, Range, LeadingTriviaPieces, - TrailingTriviaPieces); - RC Raw{static_cast(Recorded.takeOpaqueNode())}; - Raw->Release(); // -1 since it's transfer of ownership. - return make(Raw); - } - - template - SyntaxNode createNode(ParsedRawSyntaxNode Node) { - assert(Node.isDeferredLayout()); - auto Kind = Node.getKind(); - auto Children = Node.getDeferredChildren(); - - auto Recorded = Recorder.recordRawSyntax(Kind, Children); - RC Raw{static_cast(Recorded.takeOpaqueNode())}; - Raw->Release(); // -1 since it's transfer of ownership. - return make(Raw); - } - - TokenSyntax getLibSyntaxTokenFor(OpaqueSyntaxNode Node) { - return getLibSyntaxNodeFor(Node); - } - - template - SyntaxNode getLibSyntaxNodeFor(OpaqueSyntaxNode Node) { - return make(Actions->getLibSyntaxNodeFor(Node)); - } - - OpaqueSyntaxNode finalizeNode(OpaqueSyntaxNode Node) { - if (Actions->isReleaseNeeded()) - Actions->getLibSyntaxNodeFor(Node)->Release(); - return Actions->getExplicitNodeFor(Node); - } -}; -} // namespace swift - -#endif // SWIFT_PARSE_LIBSYNTAXGENERATOR_H diff --git a/include/swift/Parse/LocalContext.h b/include/swift/Parse/LocalContext.h index 04af97d82394e..1c02f0dfdc9b5 100644 --- a/include/swift/Parse/LocalContext.h +++ b/include/swift/Parse/LocalContext.h @@ -19,6 +19,7 @@ #define SWIFT_PARSE_LOCALCONTEXT_H #include "llvm/ADT/DenseMap.h" +#include "swift/AST/Identifier.h" #include namespace swift { @@ -61,12 +62,7 @@ class LocalContext { }; /// Information associated with parsing the top-level context. -class TopLevelContext : public LocalContext { -public: - /// The next auto-closure discriminator. This needs to be preserved - /// across invocations of both the parser and the type-checker. - unsigned NextAutoClosureDiscriminator = 0; -}; +class TopLevelContext : public LocalContext {}; } // end namespace swift diff --git a/include/swift/Parse/ParsedRawSyntaxNode.h b/include/swift/Parse/ParsedRawSyntaxNode.h index b1cd4035a2712..00a1dc93381af 100644 --- a/include/swift/Parse/ParsedRawSyntaxNode.h +++ b/include/swift/Parse/ParsedRawSyntaxNode.h @@ -13,6 +13,7 @@ #ifndef SWIFT_PARSE_PARSEDRAWSYNTAXNODE_H #define SWIFT_PARSE_PARSEDRAWSYNTAXNODE_H +#include "swift/Basic/Debug.h" #include "swift/Basic/SourceLoc.h" #include "swift/Parse/ParsedTrivia.h" #include "swift/Parse/Token.h" @@ -188,7 +189,7 @@ class ParsedRawSyntaxNode { TokKind = uint16_t(tok::unknown); DK = DataKind::Null; IsMissing = false; - } + } ParsedRawSyntaxNode unsafeCopy() const { ParsedRawSyntaxNode copy; @@ -213,7 +214,7 @@ class ParsedRawSyntaxNode { } CharSourceRange getDeferredRange() const { - switch (DK) { + switch (DK) { case DataKind::DeferredLayout: return getDeferredLayoutRange(); case DataKind::DeferredToken: @@ -222,7 +223,7 @@ class ParsedRawSyntaxNode { llvm_unreachable("node not deferred"); } } - + // Recorded Data ===========================================================// CharSourceRange getRecordedRange() const { @@ -250,10 +251,12 @@ class ParsedRawSyntaxNode { assert(DK == DataKind::DeferredLayout); return DeferredLayout.Children; } + MutableArrayRef getDeferredChildren() { assert(DK == DataKind::DeferredLayout); return DeferredLayout.Children; } + ParsedRawSyntaxNode copyDeferred() const { ParsedRawSyntaxNode copy; switch (DK) { @@ -325,9 +328,7 @@ class ParsedRawSyntaxNode { } /// Dump this piece of syntax recursively for debugging or testing. - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Dump this piece of syntax recursively. void dump(raw_ostream &OS, unsigned Indent = 0) const; diff --git a/include/swift/Parse/ParsedSyntaxBuilders.h.gyb b/include/swift/Parse/ParsedSyntaxBuilders.h.gyb index a78ac1598fa0d..9dce2beea03ac 100644 --- a/include/swift/Parse/ParsedSyntaxBuilders.h.gyb +++ b/include/swift/Parse/ParsedSyntaxBuilders.h.gyb @@ -30,7 +30,7 @@ namespace swift { class ParsedRawSyntaxRecorder; class SyntaxParsingContext; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_buildable(): % child_count = len(node.children) class Parsed${node.name}Builder { diff --git a/include/swift/Parse/ParsedSyntaxNodes.h.gyb b/include/swift/Parse/ParsedSyntaxNodes.h.gyb index 9c08e7676eabf..5bbc07321f49d 100644 --- a/include/swift/Parse/ParsedSyntaxNodes.h.gyb +++ b/include/swift/Parse/ParsedSyntaxNodes.h.gyb @@ -28,23 +28,20 @@ namespace swift { % # Emit the non-collection classes first, then emit the collection classes % # that reference these classes. -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if not node.is_syntax_collection(): class Parsed${node.name}; % end -% if node.is_buildable(): -class Parsed${node.name}Builder; -% end % end -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_syntax_collection(): using Parsed${node.name} = ParsedSyntaxCollection; % end % end -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if not node.is_syntax_collection(): % qualifier = "" if node.is_base() else "final" % for line in dedented_lines(node.description): @@ -82,10 +79,6 @@ public: static bool classof(const ParsedSyntax *S) { return kindof(S->getKind()); } - -% if node.is_buildable(): - using Builder = Parsed${node.name}Builder; -% end }; % end diff --git a/include/swift/Parse/ParsedSyntaxRecorder.h.gyb b/include/swift/Parse/ParsedSyntaxRecorder.h.gyb index 0e7ef3f4f1111..ecdb35f1d3ac5 100644 --- a/include/swift/Parse/ParsedSyntaxRecorder.h.gyb +++ b/include/swift/Parse/ParsedSyntaxRecorder.h.gyb @@ -31,7 +31,7 @@ class SyntaxParsingContext; struct ParsedSyntaxRecorder { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.children: % child_params = [] % for child in node.children: @@ -65,29 +65,11 @@ public: static Parsed${node.name} makeBlank${node.syntax_kind}(SourceLoc loc, SyntaxParsingContext &SPCtx); -% elif node.is_unknown(): -private: - static Parsed${node.name} record${node.syntax_kind}( - MutableArrayRef layout, - ParsedRawSyntaxRecorder &rec); - static Parsed${node.name} defer${node.syntax_kind}( - MutableArrayRef layout, - SyntaxParsingContext &SPCtx); -public: - static Parsed${node.name} make${node.syntax_kind}( - MutableArrayRef elts, - SyntaxParsingContext &SPCtx); % end % end #pragma mark - Convenience APIs -public: - static ParsedTokenSyntax makeToken(const Token &Tok, - const ParsedTrivia &LeadingTrivia, - const ParsedTrivia &TrailingTrivia, - SyntaxParsingContext &SPCtx); - /// Records an unlabelled TupleTypeElementSyntax with the provided type and /// optional trailing comma. static ParsedTupleTypeElementSyntax diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index b90ae566451c6..639a50d455340 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -25,12 +25,10 @@ #include "swift/AST/Pattern.h" #include "swift/AST/Stmt.h" #include "swift/Basic/OptionSet.h" -#include "swift/Parse/ASTGen.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/LocalContext.h" #include "swift/Parse/PersistentParserState.h" #include "swift/Parse/Token.h" -#include "swift/Parse/ParsedSyntaxNodes.h" #include "swift/Parse/ParserPosition.h" #include "swift/Parse/ParserResult.h" #include "swift/Parse/SyntaxParserResult.h" @@ -45,6 +43,7 @@ namespace llvm { namespace swift { class CodeCompletionCallbacks; + class CodeCompletionCallbacksFactory; class DefaultArgumentInitializer; class DiagnosticEngine; class Expr; @@ -121,10 +120,16 @@ class Parser { DeclContext *CurDeclContext; ASTContext &Context; CodeCompletionCallbacks *CodeCompletion = nullptr; - std::vector>> AnonClosureVars; + std::vector>> AnonClosureVars; + + NullablePtr CurrentTokenHash; + void recordTokenHash(const Token Tok) { + if (!Tok.getText().empty()) + recordTokenHash(Tok.getText()); + } + + void recordTokenHash(StringRef token); - bool IsParsingInterfaceTokens = false; - /// DisabledVars is a list of variables for whom local name lookup is /// disabled. This is used when parsing a PatternBindingDecl to reject self /// uses and to disable uses of the bound variables in a let/else block. The @@ -160,19 +165,23 @@ class Parser { bool InPoundLineEnvironment = false; bool InPoundIfEnvironment = false; + /// Do not call \c addUnvalidatedDeclWithOpaqueResultType when in an inactive + /// clause because ASTScopes are not created in those contexts and lookups to + /// those decls will fail. + bool InInactiveClauseEnvironment = false; bool InSwiftKeyPath = false; LocalContext *CurLocalContext = nullptr; bool isDelayedParsingEnabled() const { - return DelayBodyParsing || SourceMgr.getCodeCompletionLoc().isValid(); + return DelayBodyParsing || isCodeCompletionFirstPass(); } void setCodeCompletionCallbacks(CodeCompletionCallbacks *Callbacks) { CodeCompletion = Callbacks; } - bool isCodeCompletionFirstPass() { + bool isCodeCompletionFirstPass() const { return L->isCodeCompletion() && !CodeCompletion; } @@ -180,8 +189,6 @@ class Parser { const std::vector &getSplitTokens() const { return SplitTokens; } - ParsedTokenSyntax markSplitTokenSyntax(tok Kind, StringRef Txt); - void markSplitToken(tok Kind, StringRef Txt); /// Returns true if the parser reached EOF with incomplete source input, due @@ -389,9 +396,6 @@ class Parser { /// Current syntax parsing context where call backs should be directed to. SyntaxParsingContext *SyntaxContext; - /// The AST generator. - ASTGen Generator; - public: Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags, SILParserTUStateBase *SIL, @@ -534,26 +538,11 @@ class Parser { assert(Tok.is(K) && "Consuming wrong token kind"); return consumeToken(); } - /// Consume a token without providing it to the SyntaxParsingContext. - ParsedTokenSyntax consumeTokenSyntax(); - ParsedTokenSyntax consumeTokenSyntax(tok K) { - assert(Tok.is(K) && "Consuming wrong token kind"); - return consumeTokenSyntax(); - } + SourceLoc leadingTriviaLoc() { return Tok.getLoc().getAdvancedLoc(-LeadingTrivia.getLength()); } - ParsedTokenSyntax consumeIdentifierSyntax(bool allowDollarIdentifier = false) { - assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); - - if (Tok.getText()[0] == '$' && !allowDollarIdentifier) - diagnoseDollarIdentifier(Tok); - - Tok.setKind(tok::identifier); - return consumeTokenSyntax(); - } - SourceLoc consumeIdentifier(Identifier *Result = nullptr, bool allowDollarIdentifier = false) { assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); @@ -579,17 +568,6 @@ class Parser { return consumeToken(); } - ParsedTokenSyntax consumeArgumentLabelSyntax() { - assert(Tok.canBeArgumentLabel()); - if (!Tok.is(tok::kw__)) { - Tok.setKind(tok::identifier); - - if (Tok.getText()[0] == '$') - diagnoseDollarIdentifier(Tok); - } - return consumeTokenSyntax(); - } - /// When we have a token that is an identifier starting with '$', /// diagnose it if not permitted in this mode. void diagnoseDollarIdentifier(const Token &tok) { @@ -608,14 +586,6 @@ class Parser { /// source location. SourceLoc getEndOfPreviousLoc() const; - /// If the current token is the specified kind, consume it and - /// return it. Otherwise, return None without consuming it. - llvm::Optional consumeTokenSyntaxIf(tok K) { - if (Tok.isNot(K)) - return llvm::None; - return consumeTokenSyntax(); - } - /// If the current token is the specified kind, consume it and /// return true. Otherwise, return false without consuming it. bool consumeIf(tok K) { @@ -646,8 +616,6 @@ class Parser { /// Read tokens until we get to one of the specified tokens, then /// return without consuming it. Because we cannot guarantee that the token /// will ever occur, this skips to some likely good stopping point. - void skipUntilSyntax(llvm::SmallVectorImpl &Skipped, tok T1, - tok T2 = tok::NUM_TOKENS); void skipUntil(tok T1, tok T2 = tok::NUM_TOKENS); void skipUntilAnyOperator(); @@ -655,9 +623,6 @@ class Parser { /// Applies heuristics that are suitable when trying to find the end of a list /// of generic parameters, generic arguments, or list of types in a protocol /// composition. - void - skipUntilGreaterInTypeListSyntax(llvm::SmallVectorImpl &Skipped, - bool protocolComposition = false); SourceLoc skipUntilGreaterInTypeList(bool protocolComposition = false); /// skipUntilDeclStmtRBrace - Skip to the next decl or '}'. @@ -667,17 +632,14 @@ class Parser { void skipUntilDeclStmtRBrace(tok T1, tok T2); void skipUntilDeclRBrace(tok T1, tok T2); - + void skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2); - void skipListUntilDeclRBraceSyntax(SmallVectorImpl &Skipped, - SourceLoc startLoc, tok T1, tok T2); - + /// Skip a single token, but match parentheses, braces, and square brackets. /// /// Note: this does \em not match angle brackets ("<" and ">")! These are /// matched in the source when they refer to a generic type, /// but not when used as comparison operators. - void skipSingleSyntax(llvm::SmallVectorImpl &Skipped); void skipSingle(); /// Skip until the next '#else', '#endif' or until eof. @@ -695,32 +657,9 @@ class Parser { /// plain Tok.is(T1) check). bool skipUntilTokenOrEndOfLine(tok T1); - //-------------------------------------------------------------------------// - // Ignore token APIs. - // This is used when we skip gabage text in the source text. - - /// Ignore the current single token. - void ignoreToken(); - void ignoreToken(tok Kind) { - /// Ignore the current single token asserting its kind. - assert(Tok.is(Kind)); - ignoreToken(); - } - /// Conditionally ignore the current single token if it matches with the \p - /// Kind. - bool ignoreIf(tok Kind) { - if (!Tok.is(Kind)) - return false; - ignoreToken(); - return true; - } - void ignoreSingle(); - void ignoreUntil(tok Kind); - - /// Ignore tokens until a token that starts with '>', and return true it if - /// found. Applies heuristics that are suitable when trying to find the end - /// of a list of generic parameters, generic arguments. - bool ignoreUntilGreaterInTypeList(); + /// Skip a braced block (e.g. function body). The current token must be '{'. + /// Returns \c true if the parser hit the eof before finding matched '}'. + bool skipBracedBlock(); /// If the parser is generating only a syntax tree, try loading the current /// node from a previously generated syntax tree. @@ -783,23 +722,26 @@ class Parser { /// Check whether the current token starts with '>'. bool startsWithGreater(Token Tok) { return startsWithSymbol(Tok, '>'); } + /// Returns true if token is an identifier with the given value. + bool isIdentifier(Token Tok, StringRef value) { + return Tok.is(tok::identifier) && Tok.getText() == value; + } + /// Consume the starting '<' of the current token, which may either /// be a complete '<' token or some kind of operator token starting with '<', /// e.g., '<>'. - ParsedTokenSyntax consumeStartingLessSyntax(); SourceLoc consumeStartingLess(); /// Consume the starting '>' of the current token, which may either /// be a complete '>' token or some kind of operator token starting with '>', /// e.g., '>>'. - ParsedTokenSyntax consumeStartingGreaterSyntax(); SourceLoc consumeStartingGreater(); /// Consume the starting character of the current token, and split the /// remainder of the token into a new token (or tokens). - ParsedTokenSyntax - consumeStartingCharacterOfCurrentTokenSyntax(tok Kind, size_t Len = 1); - SourceLoc consumeStartingCharacterOfCurrentToken(tok Kind, size_t Len = 1); + SourceLoc + consumeStartingCharacterOfCurrentToken(tok Kind = tok::oper_binary_unspaced, + size_t Len = 1); swift::ScopeInfo &getScopeInfo() { return State->getScopeInfo(); } @@ -811,7 +753,7 @@ class Parser { getScopeInfo().addToScope(D, *this, diagnoseRedefinitions); } - ValueDecl *lookupInScope(DeclName Name) { + ValueDecl *lookupInScope(DeclNameRef Name) { if (Context.LangOpts.DisableParserLookup) return nullptr; @@ -826,7 +768,6 @@ class Parser { /// /// \returns false on success, true on error. bool parseIdentifier(Identifier &Result, SourceLoc &Loc, const Diagnostic &D); - llvm::Optional parseIdentifierSyntax(const Diagnostic &D); /// Consume an identifier with a specific expected name. This is useful for /// contextually sensitive keywords that must always be present. @@ -871,14 +812,18 @@ class Parser { return parseAnyIdentifier(Result, L, Diagnostic(ID, Args...)); } + /// \brief Parse an unsigned integer and returns it in \p Result. On failure + /// emit the specified error diagnostic, and a note at the specified note + /// location. + bool parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, + const Diagnostic &D); + /// The parser expects that \p K is next token in the input. If so, /// it is consumed and false is returned. /// /// If the input is malformed, this emits the specified error diagnostic. bool parseToken(tok K, SourceLoc &TokLoc, const Diagnostic &D); - llvm::Optional parseTokenSyntax(tok K, SourceLoc &TokLoc, - const Diagnostic &D); - + template bool parseToken(tok K, Diag ID, ArgTypes... Args) { SourceLoc L; @@ -895,10 +840,6 @@ class Parser { bool parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag, SourceLoc OtherLoc); - ParsedSyntaxResult - parseMatchingTokenSyntax(tok K, Diag<> ErrorDiag, SourceLoc OtherLoc, - bool silenceDiag = false); - /// Returns the proper location for a missing right brace, parenthesis, etc. SourceLoc getLocForMissingMatchingToken() const; @@ -917,32 +858,32 @@ class Parser { bool AllowSepAfterLast, Diag<> ErrorDiag, syntax::SyntaxKind Kind, llvm::function_ref callback); - template - ParserStatus parseListSyntax( - SmallVectorImpl &elements, bool AllowEmpty, - bool AllowSepAfterLast, llvm::function_ref isAtCloseTok, - llvm::function_ref - callback); void consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD); + ParserStatus parseBraceItems(SmallVectorImpl &Decls, + BraceItemListKind Kind, + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard); ParserStatus parseBraceItems(SmallVectorImpl &Decls, BraceItemListKind Kind = BraceItemListKind::Brace, BraceItemListKind ConditionalBlockKind = - BraceItemListKind::Brace); + BraceItemListKind::Brace) { + bool IsFollowingGuard = false; + return parseBraceItems(Decls, Kind, ConditionalBlockKind, + IsFollowingGuard); + } ParserResult parseBraceItemList(Diag<> ID); - void parseTopLevelCodeDeclDelayed(); - //===--------------------------------------------------------------------===// // Decl Parsing /// Return true if parser is at the start of a decl or decl-import. bool isStartOfDecl(); - bool parseTopLevel(); + void parseTopLevel(); /// Flags that control the parsing of declarations. enum ParseDeclFlags { @@ -962,8 +903,6 @@ class Parser { /// Options that control the parsing of declarations. using ParseDeclOptions = OptionSet; - void delayParseFromBeginningToHere(ParserPosition BeginParserPosition, - ParseDeclOptions Flags); void consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel); @@ -988,9 +927,8 @@ class Parser { bool IsAtStartOfLineOrPreviousHadSemi, llvm::function_ref Handler); - void parseDeclDelayed(); - - std::vector parseDeclListDelayed(IterableDeclContext *IDC); + std::pair, Optional> + parseDeclListDelayed(IterableDeclContext *IDC); bool parseMemberDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, SourceLoc PosBeforeLB, @@ -1003,28 +941,12 @@ class Parser { bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, IterableDeclContext *IDC); - ParsedSyntaxResult - parseTypeInheritanceClauseSyntax(bool allowClassRequirement, - bool allowAnyObject); - - ParsedSyntaxResult - parseDeclAssociatedTypeSyntax(ParseDeclOptions flags, - Optional attrs, - Optional modifiers); - - ParsedSyntaxResult - parseDeclTypeAliasSyntax(ParseDeclOptions flags, - Optional attrs, - Optional modifiers); - ParserResult parseDeclTypeAlias(ParseDeclOptions Flags, - DeclAttributes &Attributes, - SourceLoc leadingLoc); + DeclAttributes &Attributes); ParserResult parseDeclAssociatedType(ParseDeclOptions Flags, - DeclAttributes &Attributes, - SourceLoc leadingLoc); - + DeclAttributes &Attributes); + /// Parse a #if ... #endif directive. /// Delegate callback function to parse elements in the blocks. ParserResult parseIfConfig( @@ -1076,6 +998,34 @@ class Parser { ParserResult parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc); + /// Parse the @differentiable attribute. + ParserResult parseDifferentiableAttribute(SourceLoc AtLoc, + SourceLoc Loc); + + /// Parse the arguments inside the @differentiable attribute. + bool parseDifferentiableAttributeArguments( + bool &linear, SmallVectorImpl ¶ms, + Optional &jvpSpec, + Optional &vjpSpec, + TrailingWhereClause *&whereClause); + + /// Parse a differentiability parameters clause, i.e. the 'wrt:' clause in + /// `@differentiable`, `@derivative`, and `@transpose` attributes. + /// + /// If `allowNamedParameters` is false, allow only index parameters and + /// 'self'. Used for `@transpose` attributes. + bool parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, + bool allowNamedParameters = true); + + /// Parse the @derivative attribute. + ParserResult parseDerivativeAttribute(SourceLoc AtLoc, + SourceLoc Loc); + + /// Parse the @transpose attribute. + ParserResult parseTransposeAttribute(SourceLoc AtLoc, + SourceLoc Loc); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc); @@ -1086,25 +1036,40 @@ class Parser { /// an error parsing. bool parseVersionTuple(llvm::VersionTuple &Version, SourceRange &Range, const Diagnostic &D); + bool parseTypeAttributeList(ParamDecl::Specifier &Specifier, SourceLoc &SpecifierLoc, - TypeAttributes &Attributes); - ParserStatus parseTypeAttributeListSyntax(Optional &specifier, - Optional &attrs); - ParsedSyntaxResult parseTypeAttributeSyntax(); + TypeAttributes &Attributes) { + if (Tok.isAny(tok::at_sign, tok::kw_inout) || + (Tok.is(tok::identifier) && + (Tok.getRawText().equals("__shared") || + Tok.getRawText().equals("__owned")))) + return parseTypeAttributeListPresent(Specifier, SpecifierLoc, Attributes); + return false; + } + bool parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, + SourceLoc &SpecifierLoc, + TypeAttributes &Attributes); + + bool parseConventionAttributeInternal(bool justChecking, + TypeAttributes::Convention &convention); + bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, + bool justChecking = false); + + ParserResult parseDeclImport(ParseDeclOptions Flags, DeclAttributes &Attributes); - ParserStatus parseInheritance(MutableArrayRef &Inherited, + ParserStatus parseInheritance(SmallVectorImpl &Inherited, bool allowClassRequirement, bool allowAnyObject); ParserStatus parseDeclItem(bool &PreviousHadSemi, Parser::ParseDeclOptions Options, llvm::function_ref handler); - std::vector parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, - Diag<> ErrorDiag, ParseDeclOptions Options, - IterableDeclContext *IDC, - bool &hadError); + std::pair, Optional> + parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, + ParseDeclOptions Options, IterableDeclContext *IDC, + bool &hadError); ParserResult parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclEnum(ParseDeclOptions Flags, @@ -1182,61 +1147,69 @@ class Parser { //===--------------------------------------------------------------------===// // Type Parsing - + ParserResult parseType(); ParserResult parseType(Diag<> MessageID, bool HandleCodeCompletion = true, bool IsSILFuncDecl = false); - ParserStatus parseGenericArguments(llvm::SmallVectorImpl &ArgsAST, + + ParserResult + parseTypeSimpleOrComposition(Diag<> MessageID, + bool HandleCodeCompletion = true); + + ParserResult parseTypeSimple(Diag<> MessageID, + bool HandleCodeCompletion = true); + + /// Parse layout constraint. + LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID); + + ParserStatus parseGenericArguments(SmallVectorImpl &Args, SourceLoc &LAngleLoc, SourceLoc &RAngleLoc); - TypeRepr *applyAttributeToType(TypeRepr *Ty, const TypeAttributes &Attr, - ParamDecl::Specifier Specifier, - SourceLoc SpecifierLoc); - ParserResult parseAnyTypeAST(); - - ParsedSyntaxResult - parseLayoutConstraintSyntax(); - - ParsedSyntaxResult parseTypeSyntax(); - ParsedSyntaxResult - parseTypeSyntax(Diag<> MessageID, bool HandleCodeCompletion = true, - bool IsSILFuncDecl = false); - - ParsedSyntaxResult - parseGenericArgumentClauseSyntax(); - - ParsedSyntaxResult - parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion); - ParsedSyntaxResult - parseTypeSimpleOrComposition(Diag<> MessageID, bool HandleCodeCompletion); - ParsedSyntaxResult parseTypeIdentifier(); - ParsedSyntaxResult parseAnyType(); - ParsedSyntaxResult parseTypeTupleBody(); - ParsedSyntaxResult parseTypeCollection(); - ParsedSyntaxResult parseMetatypeType(ParsedTypeSyntax Base); - ParsedSyntaxResult parseOptionalType(ParsedTypeSyntax Base); - ParsedSyntaxResult - parseImplicitlyUnwrappedOptionalType(ParsedTypeSyntax Base); - ParsedSyntaxResult parseSILBoxTypeSyntax( - Optional genericParams); - - ParsedSyntaxResult parseTypeArray(ParsedTypeSyntax Base, - SourceLoc BaseLoc); - ParsedSyntaxResult parseOldStyleProtocolComposition(); + + /// Parses a type identifier (e.g. 'Foo' or 'Foo.Bar.Baz'). + /// + /// When `isParsingQualifiedDeclBaseType` is true: + /// - Parses and returns the base type for a qualified declaration name, + /// positioning the parser at the '.' before the final declaration name. + // This position is important for parsing final declaration names like + // '.init' via `parseUnqualifiedDeclName`. + /// - For example, 'Foo.Bar.f' parses as 'Foo.Bar' and the parser is + /// positioned at '.f'. + /// - If there is no base type qualifier (e.g. when parsing just 'f'), returns + /// an empty parser error. + ParserResult parseTypeIdentifier( + bool isParsingQualifiedDeclBaseType = false); + ParserResult parseOldStyleProtocolComposition(); + ParserResult parseAnyType(); + ParserResult parseSILBoxType(GenericParamList *generics, + const TypeAttributes &attrs, + Optional &GenericsScope); + + ParserResult parseTypeTupleBody(); + ParserResult parseTypeArray(TypeRepr *Base); + + /// Parse a collection type. + /// type-simple: + /// '[' type ']' + /// '[' type ':' type ']' + SyntaxParserResult parseTypeCollection(); + + SyntaxParserResult + parseTypeOptional(TypeRepr *Base); + + SyntaxParserResult + parseTypeImplicitlyUnwrappedOptional(TypeRepr *Base); bool isOptionalToken(const Token &T) const; - ParsedTokenSyntax consumeOptionalTokenSyntax(); SourceLoc consumeOptionalToken(); - + bool isImplicitlyUnwrappedOptionalToken(const Token &T) const; - ParsedTokenSyntax consumeImplicitlyUnwrappedOptionalTokenSyntax(); SourceLoc consumeImplicitlyUnwrappedOptionalToken(); - ParsedSyntaxResult - applyAttributeToTypeSyntax(ParsedSyntaxResult &&ty, - Optional specifier, - Optional attrs); + TypeRepr *applyAttributeToType(TypeRepr *Ty, const TypeAttributes &Attr, + ParamDecl::Specifier Specifier, + SourceLoc SpecifierLoc); //===--------------------------------------------------------------------===// // Pattern Parsing @@ -1306,6 +1279,9 @@ class Parser { /// True if we emitted a parse error about this parameter. bool isInvalid = false; + + /// True if this parameter is potentially destructuring a tuple argument. + bool isPotentiallyDestructured = false; }; /// Describes the context in which the given parameter is being parsed. @@ -1417,6 +1393,14 @@ class Parser { bool canParseAsGenericArgumentList(); bool canParseType(); + + /// Returns true if a simple type identifier can be parsed. + /// + /// \verbatim + /// simple-type-identifier: identifier generic-argument-list? + /// \endverbatim + bool canParseSimpleTypeIdentifier(); + bool canParseTypeIdentifier(); bool canParseTypeIdentifierOrTypeComposition(); bool canParseOldStyleProtocolComposition(); @@ -1426,9 +1410,15 @@ class Parser { bool canParseTypedPattern(); + /// Returns true if a qualified declaration name base type can be parsed. + /// + /// \verbatim + /// qualified-decl-name-base-type: simple-type-identifier '.' + /// \endverbatim + bool canParseBaseTypeForQualifiedDeclName(); + //===--------------------------------------------------------------------===// // Expression Parsing - ParsedSyntaxResult parseExpressionSyntax(Diag<> ID); ParserResult parseExpr(Diag<> ID) { return parseExprImpl(ID, /*isExprBasic=*/false); } @@ -1452,25 +1442,11 @@ class Parser { ParserResult parseExprPrimary(Diag<> ID, bool isExprBasic); ParserResult parseExprUnary(Diag<> ID, bool isExprBasic); ParserResult parseExprKeyPathObjC(); - ParsedSyntaxResult parseExprObjcKeyPathSyntax(); ParserResult parseExprKeyPath(); ParserResult parseExprSelector(); ParserResult parseExprSuper(); - ParsedSyntaxResult parseExprSuperSyntax(); - ParserResult parseExprUnresolvedMember(bool isExprBasic); - ParsedSyntaxResult - parseExprUnresolvedMemberSyntax(bool isExprBasic); ParserResult parseExprStringLiteral(); - // todo [gsoc]: create new result type for ParsedSyntax - // todo [gsoc]: turn into proper non-templated methods later - template - ParsedExprSyntax parseExprSyntax(); - - // todo [gsoc]: remove when possible - template - ParserResult parseExprAST(); - StringRef copyAndStripUnderscores(StringRef text); ParserStatus parseStringSegments(SmallVectorImpl &Segments, @@ -1486,40 +1462,48 @@ class Parser { /// _) /// \param loc The location of the label (empty if it doesn't exist) void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc); - bool parseOptionalArgumentLabelSyntax(Optional &name, - Optional &colon); - /// Parse an unqualified-decl-name. + enum class DeclNameFlag : uint8_t { + /// If passed, operator basenames are allowed. + AllowOperators = 1 << 0, + + /// If passed, names that coincide with keywords are allowed. Used after a + /// dot to enable things like '.init' and '.default'. + AllowKeywords = 1 << 1, + + /// If passed, 'deinit' and 'subscript' should be parsed as special names, + /// not ordinary identifiers. + AllowKeywordsUsingSpecialNames = AllowKeywords | 1 << 2, + + /// If passed, compound names with argument lists are allowed, unless they + /// have empty argument lists. + AllowCompoundNames = 1 << 4, + + /// If passed, compound names with empty argument lists are allowed. + AllowZeroArgCompoundNames = AllowCompoundNames | 1 << 5, + }; + using DeclNameOptions = OptionSet; + + friend DeclNameOptions operator|(DeclNameFlag flag1, DeclNameFlag flag2) { + return DeclNameOptions(flag1) | flag2; + } + + /// Without \c DeclNameFlag::AllowCompoundNames, parse an + /// unqualified-decl-base-name. /// - /// unqualified-decl-name: - /// identifier - /// identifier '(' ((identifier | '_') ':') + ')' + /// unqualified-decl-base-name: identifier /// - /// \param afterDot Whether this identifier is coming after a period, which - /// enables '.init' and '.default' like expressions. - /// \param loc Will be populated with the location of the name. - /// \param diag The diagnostic to emit if this is not a name. - /// \param allowOperators Whether to allow operator basenames too. - /// \param allowZeroArgCompoundNames Whether to allow empty argument lists. - DeclName parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators=false, - bool allowZeroArgCompoundNames=false, - bool allowDeinitAndSubscript=false); - ParserStatus - parseUnqualifiedDeclNameSyntax(Optional &identTok, - Optional &declNameArg, - bool afterDot, const Diagnostic &diag, - bool allowOperators=false, - bool allowZeroArgCompoundNames=false, - bool allowDeinitAndSubscript=false); - - ParsedSyntaxResult parseExprIdentifierSyntax(); - ParsedSyntaxResult - parseExprSpecializeSyntax(ParsedExprSyntax &&); + /// With \c DeclNameFlag::AllowCompoundNames, parse an unqualified-base-name. + /// + /// unqualified-decl-name: + /// unqualified-decl-base-name + /// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')' + DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag, + DeclNameOptions flags); Expr *parseExprIdentifier(); - Expr *parseExprEditorPlaceholder(SourceLoc loc, StringRef text); + Expr *parseExprEditorPlaceholder(Token PlaceholderTok, + Identifier PlaceholderId); /// Parse a closure expression after the opening brace. /// @@ -1546,6 +1530,8 @@ class Parser { /// identifier (',' identifier)* func-signature-result? 'in' /// \endverbatim /// + /// \param bracketRange The range of the brackets enclosing a capture list, if + /// present. Needed to offer fix-its for inserting 'self' into a capture list. /// \param captureList The entries in the capture list. /// \param params The parsed parameter list, or null if none was provided. /// \param arrowLoc The location of the arrow, if present. @@ -1554,19 +1540,18 @@ class Parser { /// /// \returns true if an error occurred, false otherwise. bool parseClosureSignatureIfPresent( - SmallVectorImpl &captureList, - ParameterList *¶ms, - SourceLoc &throwsLoc, - SourceLoc &arrowLoc, - TypeRepr *&explicitResultType, - SourceLoc &inLoc); + SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfParamDecl, + ParameterList *¶ms, + SourceLoc &throwsLoc, + SourceLoc &arrowLoc, + TypeRepr *&explicitResultType, + SourceLoc &inLoc); Expr *parseExprAnonClosureArg(); - ParserResult parseExprParenOrTuple(); - ParsedSyntaxResult parseExprTupleSyntax(); - ParserStatus parseExprTupleElementListSyntax( - SmallVectorImpl &elements, - llvm::function_ref isAtCloseTok); + ParserResult parseExprList(tok LeftTok, tok RightTok, + syntax::SyntaxKind Kind); /// Parse an expression list, keeping all of the pieces separated. ParserStatus parseExprList(tok leftTok, tok rightTok, @@ -1577,40 +1562,28 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - Expr *&trailingClosure); - ParserStatus parseExprListSyntax( - tok leftK, tok rightK, bool isPostfix, bool isExprBasic, - llvm::function_ref &&, Optional &&)> - callback); + Expr *&trailingClosure, + syntax::SyntaxKind Kind); + ParserResult parseTrailingClosure(SourceRange calleeRange); - ParsedSyntaxResult - parseTrailingClosureSyntax(SourceRange calleeRange); - ParserResult parseExprObjectLiteral(bool isExprBasic); - ParsedSyntaxResult - parseExprObjectLiteralSyntax(bool isExprBasic); + /// Parse an object literal. + /// + /// \param LK The literal kind as determined by the first token. + ParserResult parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LK, + bool isExprBasic); ParserResult parseExprCallSuffix(ParserResult fn, bool isExprBasic); ParserResult parseExprCollection(); - ParsedSyntaxResult parseExprCollectionSyntax(); - ParsedSyntaxResult - parseExprArraySyntax(ParsedTokenSyntax &&LSquare, SourceLoc LSquareLoc, - ParsedSyntaxResult &&firstExpr); - ParsedSyntaxResult - parseExprDictionarySyntax(ParsedTokenSyntax &&LSquare, SourceLoc LSquareLoc, - ParsedSyntaxResult &&firstExpr); - + ParserResult parseExprCollectionElement(Optional &isDictionary); ParserResult parseExprPoundUnknown(SourceLoc LSquareLoc); - ParsedSyntaxResult - parseExprPoundUnknownSyntax(Optional &&LSquare, - SourceLoc LSquareLoc); ParserResult parseExprPoundCodeCompletion(Optional ParentKind); UnresolvedDeclRefExpr *parseExprOperator(); + void validateCollectionElement(ParserResult element); + //===--------------------------------------------------------------------===// // Statement Parsing @@ -1637,7 +1610,8 @@ class Parser { ParserResult parseStmtGuard(); ParserResult parseStmtWhile(LabeledStmtInfo LabelInfo); ParserResult parseStmtRepeat(LabeledStmtInfo LabelInfo); - ParserResult parseStmtDo(LabeledStmtInfo LabelInfo); + ParserResult parseStmtDo(LabeledStmtInfo LabelInfo, + bool shouldSkipDoTokenConsume = false); ParserResult parseStmtCatch(); ParserResult parseStmtForEach(LabeledStmtInfo LabelInfo); ParserResult parseStmtSwitch(LabeledStmtInfo LabelInfo); @@ -1648,26 +1622,12 @@ class Parser { //===--------------------------------------------------------------------===// // Generics Parsing - ParserResult parseSILGenericParams(); - - ParserStatus parseSILGenericParamsSyntax( - Optional &result); - - ParsedSyntaxResult - parseGenericParameterClauseSyntax(); - - ParsedSyntaxResult - parseGenericWhereClauseSyntax(bool &FirstTypeInComplete, - bool AllowLayoutConstraints = false); - ParserResult parseGenericParameters(); + ParserResult parseGenericParameters(SourceLoc LAngleLoc); ParserStatus parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, SmallVectorImpl &GenericParams); ParserResult maybeParseGenericParams(); void - diagnoseWhereClauseInGenericParamList(const GenericParamList *GenericParams, - SourceLoc whereLoc); - void diagnoseWhereClauseInGenericParamList(const GenericParamList *GenericParams); enum class WhereClauseKind : unsigned { @@ -1699,6 +1659,12 @@ class Parser { parsePlatformVersionConstraintSpec(); ParserResult parsePlatformAgnosticVersionConstraintSpec(); + + //===--------------------------------------------------------------------===// + // Code completion second pass. + + void performCodeCompletionSecondPassImpl( + CodeCompletionDelayedDeclState &info); }; /// Describes a parsed declaration name. @@ -1753,6 +1719,9 @@ struct ParsedDeclName { /// Form a declaration name from this parsed declaration name. DeclName formDeclName(ASTContext &ctx) const; + + /// Form a declaration name from this parsed declaration name. + DeclNameRef formDeclNameRef(ASTContext &ctx) const; }; /// Parse a stringified Swift declaration name, @@ -1766,6 +1735,13 @@ DeclName formDeclName(ASTContext &ctx, bool isFunctionName, bool isInitializer); +/// Form a Swift declaration name referemce from its constituent parts. +DeclNameRef formDeclNameRef(ASTContext &ctx, + StringRef baseName, + ArrayRef argumentLabels, + bool isFunctionName, + bool isInitializer); + /// Parse a stringified Swift declaration name, e.g. "init(frame:)". DeclName parseDeclName(ASTContext &ctx, StringRef name); diff --git a/include/swift/Parse/ParserResult.h b/include/swift/Parse/ParserResult.h index 7296da788ec36..696f5811b3ba3 100644 --- a/include/swift/Parse/ParserResult.h +++ b/include/swift/Parse/ParserResult.h @@ -94,8 +94,6 @@ template class ParserResult { void setHasCodeCompletion() { PtrAndBits.setInt(PtrAndBits.getInt() | IsError | IsCodeCompletion); } - - ParserStatus getStatus() const; }; /// Create a successful parser result. @@ -220,16 +218,6 @@ template ParserResult::ParserResult(ParserStatus Status) { setHasCodeCompletion(); } -template -ParserStatus ParserResult::getStatus() const { - ParserStatus S; - if (isParseError()) - S.setIsParseError(); - if (hasCodeCompletion()) - S.setHasCodeCompletion(); - return S; -} - } // namespace swift #endif // LLVM_SWIFT_PARSER_PARSER_RESULT_H diff --git a/include/swift/Parse/PersistentParserState.h b/include/swift/Parse/PersistentParserState.h index 8809007e0729a..9e0678c5e3971 100644 --- a/include/swift/Parse/PersistentParserState.h +++ b/include/swift/Parse/PersistentParserState.h @@ -17,7 +17,6 @@ #ifndef SWIFT_PARSE_PERSISTENTPARSERSTATE_H #define SWIFT_PARSE_PERSISTENTPARSERSTATE_H -#include "swift/AST/LazyResolver.h" #include "swift/Basic/SourceLoc.h" #include "swift/Parse/LocalContext.h" #include "swift/Parse/ParserPosition.h" @@ -25,7 +24,37 @@ #include "llvm/ADT/DenseMap.h" namespace swift { - class AbstractFunctionDecl; + +class SourceFile; +class DeclContext; +class IterableDeclContext; + +enum class CodeCompletionDelayedDeclKind { + TopLevelCodeDecl, + Decl, + FunctionBody, +}; + +class CodeCompletionDelayedDeclState { +public: + CodeCompletionDelayedDeclKind Kind; + unsigned Flags; + DeclContext *ParentContext; + SavedScope Scope; + unsigned StartOffset; + unsigned EndOffset; + unsigned PrevOffset; + + SavedScope takeScope() { return std::move(Scope); } + + CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, + SavedScope &&Scope, unsigned StartOffset, + unsigned EndOffset, unsigned PrevOffset) + : Kind(Kind), Flags(Flags), ParentContext(ParentContext), + Scope(std::move(Scope)), StartOffset(StartOffset), EndOffset(EndOffset), + PrevOffset(PrevOffset) {} +}; /// Parser state persistent across multiple parses. class PersistentParserState { @@ -37,68 +66,17 @@ class PersistentParserState { bool isValid() const { return Loc.isValid(); } }; - class FunctionBodyState { - ParserPos BodyPos; - SavedScope Scope; - friend class Parser; - - SavedScope takeScope() { - return std::move(Scope); - } - - public: - FunctionBodyState(SourceRange BodyRange, SourceLoc PreviousLoc, - SavedScope &&Scope) - : BodyPos{BodyRange.Start, PreviousLoc}, Scope(std::move(Scope)) - {} - }; - - enum class DelayedDeclKind { - TopLevelCodeDecl, - Decl, - }; - - class DelayedDeclState { - friend class PersistentParserState; - friend class Parser; - DelayedDeclKind Kind; - unsigned Flags; - DeclContext *ParentContext; - ParserPos BodyPos; - SourceLoc BodyEnd; - SavedScope Scope; - - SavedScope takeScope() { - return std::move(Scope); - } - - public: - DelayedDeclState(DelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, SourceRange BodyRange, - SourceLoc PreviousLoc, SavedScope &&Scope) - : Kind(Kind), Flags(Flags), ParentContext(ParentContext), - BodyPos{BodyRange.Start, PreviousLoc}, - BodyEnd(BodyRange.End), Scope(std::move(Scope)) - {} - }; - bool InPoundLineEnvironment = false; // FIXME: When condition evaluation moves to a later phase, remove this bit // and adjust the client call 'performParseOnly'. bool PerformConditionEvaluation = true; private: - ScopeInfo ScopeInfo; - using DelayedFunctionBodiesTy = - llvm::DenseMap>; - DelayedFunctionBodiesTy DelayedFunctionBodies; + swift::ScopeInfo ScopeInfo; /// Parser sets this if it stopped parsing before the buffer ended. ParserPosition MarkedPos; - std::unique_ptr CodeCompletionDelayedDeclState; - - std::vector DelayedDeclLists; + std::unique_ptr CodeCompletionDelayedDeclStat; /// The local context for all top-level code. TopLevelContext TopLevelCode; @@ -109,32 +87,28 @@ class PersistentParserState { PersistentParserState(ASTContext &ctx) : PersistentParserState() { } ~PersistentParserState(); - void delayDecl(DelayedDeclKind Kind, unsigned Flags, - DeclContext *ParentContext, - SourceRange BodyRange, SourceLoc PreviousLoc); - - void delayDeclList(IterableDeclContext *D); - - void delayTopLevel(TopLevelCodeDecl *TLCD, SourceRange BodyRange, - SourceLoc PreviousLoc); - - bool hasDelayedDecl() { - return CodeCompletionDelayedDeclState.get() != nullptr; - } - DelayedDeclKind getDelayedDeclKind() { - return CodeCompletionDelayedDeclState->Kind; - } - SourceLoc getDelayedDeclLoc() { - return CodeCompletionDelayedDeclState->BodyPos.Loc; + void setCodeCompletionDelayedDeclState(SourceManager &SM, unsigned BufferID, + CodeCompletionDelayedDeclKind Kind, + unsigned Flags, + DeclContext *ParentContext, + SourceRange BodyRange, + SourceLoc PreviousLoc); + void restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other); + + bool hasCodeCompletionDelayedDeclState() { + return CodeCompletionDelayedDeclStat.get() != nullptr; } - DeclContext *getDelayedDeclContext() { - return CodeCompletionDelayedDeclState->ParentContext; - } - std::unique_ptr takeDelayedDeclState() { - return std::move(CodeCompletionDelayedDeclState); + + CodeCompletionDelayedDeclState &getCodeCompletionDelayedDeclState() { + return *CodeCompletionDelayedDeclStat.get(); } - void parseAllDelayedDeclLists(); + std::unique_ptr + takeCodeCompletionDelayedDeclState() { + assert(hasCodeCompletionDelayedDeclState()); + return std::move(CodeCompletionDelayedDeclStat); + } TopLevelContext &getTopLevelContext() { return TopLevelCode; diff --git a/include/swift/Parse/Scope.h b/include/swift/Parse/Scope.h index 2fb49bf112d42..c10b97f35daee 100644 --- a/include/swift/Parse/Scope.h +++ b/include/swift/Parse/Scope.h @@ -19,6 +19,7 @@ #define SWIFT_SEMA_SCOPE_H #include "swift/AST/Identifier.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/TreeScopedHashTable.h" #include "llvm/ADT/SmallVector.h" @@ -46,7 +47,7 @@ class ScopeInfo { unsigned ResolvableDepth = 0; public: - ValueDecl *lookupValueName(DeclName Name); + ValueDecl *lookupValueName(DeclNameRef Name); Scope *getCurrentScope() const { return CurScope; } @@ -59,8 +60,7 @@ class ScopeInfo { SavedScope saveCurrentScope(); - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "Only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; enum class ScopeKind { @@ -141,6 +141,8 @@ class Scope { bool isResolvable() const; public: + Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock = false); + /// Create a lexical scope of the specified kind. Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock = false); @@ -159,7 +161,7 @@ class Scope { } }; -inline ValueDecl *ScopeInfo::lookupValueName(DeclName Name) { +inline ValueDecl *ScopeInfo::lookupValueName(DeclNameRef Name) { // FIXME: this check can go away when SIL parser parses everything in // a toplevel scope. if (!CurScope) @@ -169,7 +171,8 @@ inline ValueDecl *ScopeInfo::lookupValueName(DeclName Name) { // If we found nothing, or we found a decl at the top-level, return nothing. // We ignore results at the top-level because we may have overloading that // will be resolved properly by name binding. - std::pair Res = HT.lookup(CurScope->HTScope, Name); + std::pair Res = HT.lookup(CurScope->HTScope, + Name.getFullName()); if (Res.first < ResolvableDepth) return 0; return Res.second; diff --git a/include/swift/Parse/SyntaxParseActions.h b/include/swift/Parse/SyntaxParseActions.h index 33a788ba8e1fa..33b1cd7f6e2ec 100644 --- a/include/swift/Parse/SyntaxParseActions.h +++ b/include/swift/Parse/SyntaxParseActions.h @@ -33,12 +33,6 @@ namespace syntax { typedef void *OpaqueSyntaxNode; -// todo [gsoc]: remove when possible -enum class OpaqueSyntaxNodeKind { - SwiftSyntax, - LibSyntax, -}; - class SyntaxParseActions { virtual void _anchor(); @@ -73,9 +67,6 @@ class SyntaxParseActions { lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) { return std::make_pair(0, nullptr); } - - /// Returns what kind of OpaqueSyntaxNode is created by `recordXXX` methods. - virtual OpaqueSyntaxNodeKind getOpaqueKind() = 0; }; } // end namespace swift diff --git a/include/swift/Parse/SyntaxParserResult.h b/include/swift/Parse/SyntaxParserResult.h index 6ee74fe398178..16209d0547d8f 100644 --- a/include/swift/Parse/SyntaxParserResult.h +++ b/include/swift/Parse/SyntaxParserResult.h @@ -18,124 +18,82 @@ namespace swift { -enum class ResultDataKind : uint8_t { - Success, - Error, - CodeCompletion, -}; - -template class ParsedSyntaxResult { -public: - template - friend class ParsedSyntaxResult; +template class SyntaxParserResult { + llvm::Optional SyntaxNode; + ParserResult ASTResult; -private: - ParsedRawSyntaxNode Raw; - ParserStatus Status; + template friend class SyntaxParserResult; public: - explicit ParsedSyntaxResult() : Raw(), Status() { setIsError(); } - - ParsedSyntaxResult(ParserStatus Status) : Raw(), Status(Status) { - assert(Status.isError()); - } + SyntaxParserResult(std::nullptr_t = nullptr) + : SyntaxNode(None), ASTResult(nullptr) {} + SyntaxParserResult(ParserStatus Status) + : SyntaxNode(None), ASTResult(Status) {} + SyntaxParserResult(llvm::Optional &&SyntaxNode, AST *ASTNode) + : SyntaxNode(std::move(SyntaxNode)), ASTResult(ASTNode) {} + SyntaxParserResult(ParserStatus Status, llvm::Optional &&SyntaxNode, + AST *ASTNode) + : SyntaxNode(std::move(SyntaxNode)), ASTResult(makeParserResult(Status, ASTNode)) {} - explicit ParsedSyntaxResult(ParsedRawSyntaxNode &&Raw) - : Raw(std::move(Raw)), Status() {} + /// Convert from a different but compatible parser result. + template ::value>::type> + SyntaxParserResult(SyntaxParserResult &&Other) + : SyntaxNode(std::move(Other.SyntaxNode)), ASTResult(Other.ASTResult) {} - explicit ParsedSyntaxResult(ParsedSyntaxNode &&Node) - : ParsedSyntaxResult(Node.takeRaw()) {} - template ::value>::type> - ParsedSyntaxResult(ParsedSyntaxResult &&other) { - Raw = std::move(other.Raw); - Status = std::move(other.Status); - } - - bool isSuccess() const { - return Status.isSuccess(); - } - - bool isError() const { - return Status.isError(); - } - void setIsError() { - Status.setIsParseError(); - } + bool isNull() const { return ASTResult.isNull(); } + bool isNonNull() const { return ASTResult.isNonNull(); } + bool isParseError() const { return ASTResult.isParseError(); } + bool hasCodeCompletion() const { return ASTResult.hasCodeCompletion(); } - bool hasCodeCompletion() const { - return Status.hasCodeCompletion(); - } - void setHasCodeCompletion() { - Status.setHasCodeCompletion(); - } + void setIsParseError() { return ASTResult.setIsParserError(); } + void setHasCodeCompletion() { return ASTResult.setHasCodeCompletion(); } - ParsedSyntaxNode get() { - assert(!isNull()); - return ParsedSyntaxNode(std::move(Raw)); - } + const ParserResult &getASTResult() { return ASTResult; } - template - Optional getAs() { - assert(!isNull()); - if (NewSyntaxNode::kindof(Raw.getKind())) - return NewSyntaxNode(std::move(Raw)); - return None; - } + AST *getAST() const { return ASTResult.get(); } - Optional getOrNull() { - if (isNull()) - return None; - return get(); + bool hasSyntax() const { + return SyntaxNode.hasValue(); } - bool isNull() const { - return Raw.isNull(); + Syntax getSyntax() { + assert(SyntaxNode.hasValue() && "getSyntax from None value"); + return std::move(*SyntaxNode); } - ParserStatus getStatus() const { - return Status; - } + SyntaxParserResult & + operator=(SyntaxParserResult R){ + std::swap(*this, R); + return *this; + }; }; -template -static ParsedSyntaxResult -makeParsedResult(ParsedSyntaxNode node) { - return ParsedSyntaxResult(std::move(node)); -} - -template -static ParsedSyntaxResult -makeParsedError(ParsedSyntaxNode node) { - auto result = ParsedSyntaxResult(std::move(node)); - result.setIsError(); - return result; -} - -template -static ParsedSyntaxResult makeParsedError() { - return ParsedSyntaxResult(); +/// Create a successful parser result. +template +static inline SyntaxParserResult +makeSyntaxResult(llvm::Optional &&SyntaxNode, AST *ASTNode) { + return SyntaxParserResult(std::move(SyntaxNode), ASTNode); } -template -static ParsedSyntaxResult -makeParsedCodeCompletion(ParsedSyntaxNode node) { - auto result = ParsedSyntaxResult(std::move(node)); - result.setHasCodeCompletion(); - return result; +/// Create a result with the specified status. +template +static inline SyntaxParserResult +makeSyntaxResult(ParserStatus Status, llvm::Optional &&SyntaxNode, + AST *ASTNode) { + return SyntaxParserResult(Status, std::move(SyntaxNode), ASTNode); } -template -static ParsedSyntaxResult -makeParsedResult(ParsedSyntaxNode node, ParserStatus Status) { - auto result = ParsedSyntaxResult(std::move(node)); - if (Status.hasCodeCompletion()) - result.setHasCodeCompletion(); - else if (Status.isError()) - result.setIsError(); - return result; +/// Create a result (null or non-null) with error and code completion bits set. +template +static inline SyntaxParserResult +makeSyntaxCodeCompletionResult(AST *Result = nullptr) { + SyntaxParserResult SR; + if (Result) + SR = SyntaxParserResult(None, Result); + SR.setHasCodeCompletion(); + return SR; } } // namespace swift diff --git a/include/swift/Parse/SyntaxParsingContext.h b/include/swift/Parse/SyntaxParsingContext.h index 96d4d57924429..ea37c51432ad1 100644 --- a/include/swift/Parse/SyntaxParsingContext.h +++ b/include/swift/Parse/SyntaxParsingContext.h @@ -14,12 +14,10 @@ #define SWIFT_PARSE_SYNTAXPARSINGCONTEXT_H #include "llvm/ADT/PointerUnion.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/SourceLoc.h" -#include "swift/Parse/HiddenLibSyntaxAction.h" -#include "swift/Parse/LibSyntaxGenerator.h" #include "swift/Parse/ParsedRawSyntaxNode.h" #include "swift/Parse/ParsedRawSyntaxRecorder.h" -#include "swift/Parse/ParsedSyntaxNodes.h" namespace swift { @@ -77,7 +75,6 @@ constexpr size_t SyntaxAlignInBits = 3; /// // Now the context holds { '(' Expr ')' }. /// // From these parts, it creates ParenExpr node and add it to the parent. /// } -// todo [gsoc]: remove when/if possible class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { public: /// The shared data for all syntax parsing contexts with the same root. @@ -98,15 +95,13 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { ParsedRawSyntaxRecorder Recorder; - LibSyntaxGenerator LibSyntaxCreator; - llvm::BumpPtrAllocator ScratchAlloc; RootContextData(SourceFile &SF, DiagnosticEngine &Diags, SourceManager &SourceMgr, unsigned BufferID, - const std::shared_ptr& TwoActions) + std::shared_ptr spActions) : SF(SF), Diags(Diags), SourceMgr(SourceMgr), BufferID(BufferID), - Recorder(TwoActions), LibSyntaxCreator(TwoActions) {} + Recorder(std::move(spActions)) {} }; private: @@ -172,6 +167,9 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { /// deferred nodes bool ShouldDefer = false; + // If false, context does nothing. + bool Enabled; + /// Create a syntax node using the tail \c N elements of collected parts and /// replace those parts with the single result. void createNodeInPlace(SyntaxKind Kind, size_t N, @@ -192,20 +190,19 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { Optional bridgeAs(SyntaxContextKind Kind, MutableArrayRef Parts); - ParsedRawSyntaxNode finalizeSourceFile(); - public: /// Construct root context. SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF, unsigned BufferID, - std::shared_ptr SPActions); + std::shared_ptr SPActions); /// Designated constructor for child context. SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder) : RootDataOrParent(CtxtHolder), CtxtHolder(CtxtHolder), RootData(CtxtHolder->RootData), Offset(RootData->Storage.size()), IsBacktracking(CtxtHolder->IsBacktracking), - ShouldDefer(CtxtHolder->ShouldDefer) { + ShouldDefer(CtxtHolder->ShouldDefer), + Enabled(CtxtHolder->isEnabled()) { assert(CtxtHolder->isTopOfContextStack() && "SyntaxParsingContext cannot have multiple children"); assert(CtxtHolder->Mode != AccumulationMode::SkippedForIncrementalUpdate && @@ -233,6 +230,8 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { /// offset. If nothing is found \c 0 is returned. size_t lookupNode(size_t LexerOffset, SourceLoc Loc); + void disable() { Enabled = false; } + bool isEnabled() const { return Enabled; } bool isRoot() const { return RootDataOrParent.is(); } bool isTopOfContextStack() const { return this == CtxtHolder; } @@ -250,10 +249,6 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { return getRootData()->Storage; } - LibSyntaxGenerator &getSyntaxCreator() { - return getRootData()->LibSyntaxCreator; - } - const SyntaxParsingContext *getRoot() const; ParsedRawSyntaxRecorder &getRecorder() { return getRootData()->Recorder; } @@ -272,16 +267,8 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { /// Add Syntax to the parts. void addSyntax(ParsedSyntax Node); - template - bool isTopNode() { - auto parts = getParts(); - return (!parts.empty() && SyntaxNode::kindof(parts.back().getKind())); - } - - /// Returns the topmost Syntax node. - template SyntaxNode topNode(); - - template llvm::Optional popIf() { + template + llvm::Optional popIf() { auto &Storage = getStorage(); if (Storage.size() <= Offset) return llvm::None; @@ -321,11 +308,6 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { SynKind = Kind; } - /// On destruction, do not attempt to finalize the root node. - void setDiscard() { - Mode = AccumulationMode::Discard; - } - /// On destruction, if the parts size is 1 and it's kind of \c Kind, just /// append it to the parent context. Otherwise, create Unknown{Kind} node from /// the collected parts. @@ -364,28 +346,8 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext { void synthesize(tok Kind, SourceLoc Loc); /// Dump the nodes that are in the storage stack of the SyntaxParsingContext - LLVM_ATTRIBUTE_DEPRECATED(void dumpStorage() const LLVM_ATTRIBUTE_USED, - "Only meant for use in the debugger"); + SWIFT_DEBUG_DUMPER(dumpStorage()); }; -template -inline SyntaxNode SyntaxParsingContext::topNode() { - ParsedRawSyntaxNode &TopNode = getStorage().back(); - if (TopNode.isRecorded()) { - OpaqueSyntaxNode OpaqueNode = TopNode.getOpaqueNode(); - return getSyntaxCreator().getLibSyntaxNodeFor(OpaqueNode); - } - return getSyntaxCreator().createNode(TopNode.copyDeferred()); -} - -template <> inline TokenSyntax SyntaxParsingContext::topNode() { - ParsedRawSyntaxNode &TopNode = getStorage().back(); - if (TopNode.isRecorded()) { - OpaqueSyntaxNode OpaqueNode = TopNode.getOpaqueNode(); - return getSyntaxCreator().getLibSyntaxNodeFor(OpaqueNode); - } - return getSyntaxCreator().createToken(TopNode.copyDeferred()); -} - } // namespace swift #endif // SWIFT_SYNTAX_PARSING_CONTEXT_H diff --git a/include/swift/Parse/Token.h b/include/swift/Parse/Token.h index 74fa4df488508..42439986e06ce 100644 --- a/include/swift/Parse/Token.h +++ b/include/swift/Parse/Token.h @@ -198,7 +198,11 @@ class Token { /// True if the token is any keyword. bool isKeyword() const { - return isTokenKeyword(Kind); + switch (Kind) { +#define KEYWORD(X) case tok::kw_##X: return true; +#include "swift/Syntax/TokenKinds.def" + default: return false; + } } /// True if the token is any literal. diff --git a/include/swift/Reflection/MetadataSource.h b/include/swift/Reflection/MetadataSource.h index 0d572628b80ed..6cf0e9ce30e78 100644 --- a/include/swift/Reflection/MetadataSource.h +++ b/include/swift/Reflection/MetadataSource.h @@ -177,7 +177,7 @@ class MetadataSource { } void dump() const; - void dump(std::ostream &OS, unsigned Indent = 0) const; + void dump(FILE *file, unsigned Indent = 0) const; template static const MetadataSource *decode(Allocator &A, const std::string &str) { auto begin = str.begin(); diff --git a/include/swift/Reflection/Records.h b/include/swift/Reflection/Records.h index 431877b309420..c43ade467c6b0 100644 --- a/include/swift/Reflection/Records.h +++ b/include/swift/Reflection/Records.h @@ -266,6 +266,8 @@ struct AssociatedTypeRecordIterator { return *this; } + AssociatedTypeRecordIterator(const AssociatedTypeRecordIterator &Other) + : Cur(Other.Cur), End(Other.End) {} AssociatedTypeRecordIterator operator=(const AssociatedTypeRecordIterator &Other) { return { Other.Cur, Other.End }; diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index d534379cf54bc..512e91739b6b6 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -31,7 +31,6 @@ #include "swift/Reflection/TypeRefBuilder.h" #include "swift/Runtime/Unreachable.h" -#include #include #include #include diff --git a/include/swift/Reflection/TypeLowering.h b/include/swift/Reflection/TypeLowering.h index 31ea60e21561f..c66c86b44efcd 100644 --- a/include/swift/Reflection/TypeLowering.h +++ b/include/swift/Reflection/TypeLowering.h @@ -23,7 +23,6 @@ #include "llvm/Support/Casting.h" #include "swift/Remote/MetadataReader.h" -#include #include namespace swift { @@ -134,7 +133,7 @@ class TypeInfo { bool isBitwiseTakable() const { return BitwiseTakable; } void dump() const; - void dump(std::ostream &OS, unsigned Indent = 0) const; + void dump(FILE *file, unsigned Indent = 0) const; }; struct FieldInfo { diff --git a/include/swift/Reflection/TypeRef.h b/include/swift/Reflection/TypeRef.h index cc03f16547afc..d0c3d3249557a 100644 --- a/include/swift/Reflection/TypeRef.h +++ b/include/swift/Reflection/TypeRef.h @@ -24,8 +24,6 @@ #include "swift/Remote/MetadataReader.h" #include "swift/Runtime/Unreachable.h" -#include - namespace swift { namespace reflection { @@ -150,7 +148,7 @@ class alignas(void *) TypeRef { } void dump() const; - void dump(std::ostream &OS, unsigned Indent = 0) const; + void dump(FILE *file, unsigned Indent = 0) const; bool isConcrete() const; bool isConcreteAfterSubstitutions(const GenericArgumentMap &Subs) const; diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index abf7d17cd68ff..4c40d503b2090 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -25,7 +25,6 @@ #include "swift/Reflection/TypeRef.h" #include "llvm/ADT/Optional.h" -#include #include #include @@ -211,7 +210,7 @@ struct ClosureContextInfo { unsigned NumBindings = 0; void dump() const; - void dump(std::ostream &OS) const; + void dump(FILE *file) const; }; struct FieldTypeInfo { @@ -653,13 +652,13 @@ class TypeRefBuilder { /// Dumping typerefs, field declarations, associated types /// - void dumpTypeRef(RemoteRef mangledName, - std::ostream &OS, bool printTypeName = false); - void dumpFieldSection(std::ostream &OS); - void dumpAssociatedTypeSection(std::ostream &OS); - void dumpBuiltinTypeSection(std::ostream &OS); - void dumpCaptureSection(std::ostream &OS); - void dumpAllSections(std::ostream &OS); + void dumpTypeRef(RemoteRef MangledName, + FILE *file, bool printTypeName = false); + void dumpFieldSection(FILE *file); + void dumpAssociatedTypeSection(FILE *file); + void dumpBuiltinTypeSection(FILE *file); + void dumpCaptureSection(FILE *file); + void dumpAllSections(FILE *file); }; diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index ec413b7921ca7..c117a4d15aba5 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -2613,6 +2613,13 @@ class MetadataReader { if (!Reader->readInteger(RemoteAddress(dataPtr + OffsetToROPtr), &dataPtr)) return StoredPointer(); + // Newer Objective-C runtimes implement a size optimization where the RO + // field is a tagged union. If the low-bit is set, then the pointer needs + // to be dereferenced once more to yield the real class_ro_t pointer. + if (dataPtr & 1) + if (!Reader->readInteger(RemoteAddress(dataPtr^1), &dataPtr)) + return StoredPointer(); + return dataPtr; } diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 7377828924de6..c8cc49083ad17 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -160,6 +160,7 @@ # else # define SWIFT_CLASS_IS_SWIFT_MASK _swift_classIsSwiftMask # define SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE 1 +# define SWIFT_BUILD_HAS_BACK_DEPLOYMENT 1 # include "BackDeployment.h" # endif diff --git a/include/swift/Runtime/ExistentialContainer.h b/include/swift/Runtime/ExistentialContainer.h index 7e1d733395790..bf7e63c7cb825 100644 --- a/include/swift/Runtime/ExistentialContainer.h +++ b/include/swift/Runtime/ExistentialContainer.h @@ -95,6 +95,8 @@ struct ClassExistentialContainerImpl { using ClassExistentialContainer = ClassExistentialContainerImpl; using WeakClassExistentialContainer = ClassExistentialContainerImpl; +using UnownedClassExistentialContainer = + ClassExistentialContainerImpl; } // end swift namespace diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index e688f0576e6fa..72998fbbf6193 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -1086,4 +1086,49 @@ swift_getTypeName(const Metadata *type, bool qualified); } // end namespace swift +#if SWIFT_OBJC_INTEROP +/// Standard ObjC lifecycle methods for Swift objects +#define STANDARD_OBJC_METHOD_IMPLS_FOR_SWIFT_OBJECTS \ +- (id)retain { \ + auto SELF = reinterpret_cast(self); \ + swift_retain(SELF); \ + return self; \ +} \ +- (oneway void)release { \ + auto SELF = reinterpret_cast(self); \ + swift_release(SELF); \ +} \ +- (id)autorelease { \ + return _objc_rootAutorelease(self); \ +} \ +- (NSUInteger)retainCount { \ + return swift::swift_retainCount(reinterpret_cast(self)); \ +} \ +- (BOOL)_isDeallocating { \ + return swift_isDeallocating(reinterpret_cast(self)); \ +} \ +- (BOOL)_tryRetain { \ + return swift_tryRetain(reinterpret_cast(self)) != nullptr; \ +} \ +- (BOOL)allowsWeakReference { \ + return !swift_isDeallocating(reinterpret_cast(self)); \ +} \ +- (BOOL)retainWeakReference { \ + return swift_tryRetain(reinterpret_cast(self)) != nullptr; \ +} \ +- (void)_setWeaklyReferenced { \ + auto heapObj = reinterpret_cast(self); \ + heapObj->refCounts.setPureSwiftDeallocation(false); \ +} \ +- (void)_noteAssociatedObjects { \ + auto heapObj = reinterpret_cast(self); \ + heapObj->refCounts.setPureSwiftDeallocation(false); \ +} \ +- (void)dealloc { \ + swift_rootObjCDealloc(reinterpret_cast(self)); \ +} + +#endif // SWIFT_OBJC_INTEROP + + #endif // SWIFT_RUNTIME_ALLOC_H diff --git a/include/swift/Runtime/Mutex.h b/include/swift/Runtime/Mutex.h index 1b320e9d22a6e..a24c555ccca03 100644 --- a/include/swift/Runtime/Mutex.h +++ b/include/swift/Runtime/Mutex.h @@ -24,6 +24,8 @@ #include "swift/Runtime/MutexPThread.h" #elif defined(_WIN32) #include "swift/Runtime/MutexWin32.h" +#elif defined(__wasi__) +#include "swift/Runtime/MutexWASI.h" #else #error "Implement equivalent of MutexPThread.h/cpp for your platform." #endif diff --git a/include/swift/Runtime/MutexWASI.h b/include/swift/Runtime/MutexWASI.h new file mode 100644 index 0000000000000..153a5f87b11dd --- /dev/null +++ b/include/swift/Runtime/MutexWASI.h @@ -0,0 +1,69 @@ +//===--- MutexWASI.h - -----------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// No-op implementation of locks for the WebAssembly System Interface. The +// implementation does not need to perform locking, because as of January 2020 +// WebAssembly does not support threads. +// See the current status at https://github.com/WebAssembly/proposals and +// https://github.com/webassembly/threads +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_MUTEX_WASI_H +#define SWIFT_RUNTIME_MUTEX_WASI_H + +namespace swift { + +typedef void* ConditionHandle; +typedef void* MutexHandle; +typedef void* ReadWriteLockHandle; + +#define SWIFT_CONDITION_SUPPORTS_CONSTEXPR 1 +#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1 +#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1 + +struct ConditionPlatformHelper { + static constexpr ConditionHandle staticInit() { + return nullptr; + }; + static void init(ConditionHandle &condition) {} + static void destroy(ConditionHandle &condition) {} + static void notifyOne(ConditionHandle &condition) {} + static void notifyAll(ConditionHandle &condition) {} + static void wait(ConditionHandle &condition, MutexHandle &mutex); +}; + +struct MutexPlatformHelper { + static constexpr MutexHandle staticInit() { return nullptr; } + static void init(MutexHandle &mutex, bool checked = false) {} + static void destroy(MutexHandle &mutex) {} + static void lock(MutexHandle &mutex) {} + static void unlock(MutexHandle &mutex) {} + static bool try_lock(MutexHandle &mutex) { return true; } + static void unsafeLock(MutexHandle &mutex) {} + static void unsafeUnlock(MutexHandle &mutex) {} +}; + +struct ReadWriteLockPlatformHelper { + static constexpr ReadWriteLockHandle staticInit() { return nullptr; } + static void init(ReadWriteLockHandle &rwlock) {} + static void destroy(ReadWriteLockHandle &rwlock) {} + static void readLock(ReadWriteLockHandle &rwlock) {} + static bool try_readLock(ReadWriteLockHandle &rwlock) { return true; } + static void readUnlock(ReadWriteLockHandle &rwlock) {} + static void writeLock(ReadWriteLockHandle &rwlock) {} + static bool try_writeLock(ReadWriteLockHandle &rwlock) { return true; } + static void writeUnlock(ReadWriteLockHandle &rwlock) {} +}; +} + +#endif diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 279046f593950..7643a0b032cb4 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1409,6 +1409,20 @@ FUNCTION(GetTypeByMangledNameInContext, swift_getTypeByMangledNameInContext, ARGS(Int8PtrTy, SizeTy, TypeContextDescriptorPtrTy, Int8PtrPtrTy), ATTRS(NoUnwind, ArgMemOnly)) +// const Metadata *swift_getTypeByMangledNameInContextInMetadataState( +// size_t metadataState, +// const char *typeNameStart, +// size_t typeNameLength, +// const TargetContextDescriptor *context, +// const void * const *genericArgs) +FUNCTION(GetTypeByMangledNameInContextInMetadataState, + swift_getTypeByMangledNameInContextInMetadataState, SwiftCC, + GetTypesInAbstractMetadataStateAvailability, + RETURNS(TypeMetadataPtrTy), + ARGS(SizeTy, Int8PtrTy, SizeTy, TypeContextDescriptorPtrTy, + Int8PtrPtrTy), + ATTRS(NoUnwind, ArgMemOnly)) + #undef RETURNS #undef ARGS #undef ATTRS diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 7eddc8283c6a2..1cf4bc42ee17e 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -409,7 +409,13 @@ class AbstractionPattern { assert(hasGenericSignature()); return CanGenericSignature(GenericSig); } - + + CanGenericSignature getGenericSignatureOrNull() const { + if (!hasGenericSignature()) + return CanGenericSignature(); + return CanGenericSignature(GenericSig); + } + /// Return an open-coded abstraction pattern for a tuple. The /// caller is responsible for ensuring that the storage for the /// tuple elements is valid for as long as the abstraction pattern is. @@ -513,6 +519,15 @@ class AbstractionPattern { return pattern; } + /// Return an abstraction pattern for the type of the given struct field or enum case + /// substituted in `this` type. + /// + /// Note that, for most purposes, you should lower a field's type against its + /// *unsubstituted* interface type. + AbstractionPattern + unsafeGetSubstFieldType(ValueDecl *member, + CanType origMemberType = CanType()) const; + private: /// Return an abstraction pattern for the curried type of an /// Objective-C method. @@ -648,11 +663,34 @@ class AbstractionPattern { return getKind() != Kind::Invalid; } + bool isTypeParameterOrOpaqueArchetype() const { + switch (getKind()) { + case Kind::Opaque: + return true; + case Kind::Type: + case Kind::ClangType: + case Kind::Discard: { + auto type = getType(); + if (isa(type) || + isa(type)) { + return true; + } + if (auto archetype = dyn_cast(type)) { + return true; + } + return false; + } + default: + return false; + } + } + bool isTypeParameter() const { switch (getKind()) { case Kind::Opaque: return true; case Kind::Type: + case Kind::ClangType: case Kind::Discard: { auto type = getType(); if (isa(type) || @@ -668,12 +706,13 @@ class AbstractionPattern { return false; } } - + /// Is this an interface type that is subject to a concrete /// same-type constraint? bool isConcreteType() const; - bool requiresClass(); + bool requiresClass() const; + LayoutConstraint getLayoutConstraint() const; /// Return the Swift type which provides structure for this /// abstraction pattern. diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index b8069a93ae65a..f98608cbfbeb8 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -398,24 +398,6 @@ class ApplySite { } } - /// If this is a terminator apply site, then pass the first instruction of - /// each successor to fun. Otherwise, pass std::next(Inst). - /// - /// The intention is that this abstraction will enable the compiler writer to - /// ignore whether or not an apply site is a terminator when inserting - /// instructions after an apply site. This results in eliminating unnecessary - /// if-else code otherwise required to handle such situations. - void insertAfter(llvm::function_ref func) { - auto *ti = dyn_cast(Inst); - if (!ti) { - return func(std::next(Inst->getIterator())); - } - - for (auto *succBlock : ti->getSuccessorBlocks()) { - func(succBlock->begin()); - } - } - static ApplySite getFromOpaqueValue(void *p) { return ApplySite(p); } friend bool operator==(ApplySite lhs, ApplySite rhs) { @@ -533,6 +515,63 @@ class FullApplySite : public ApplySite { return getCalleeArgIndex(op) < getNumIndirectSILResults(); } + /// If this is a terminator apply site, then pass the first instruction of + /// each successor to fun. Otherwise, pass std::next(Inst). + /// + /// The intention is that this abstraction will enable the compiler writer to + /// ignore whether or not an apply site is a terminator when inserting + /// instructions after an apply site. This results in eliminating unnecessary + /// if-else code otherwise required to handle such situations. + /// + /// NOTE: We return std::next() for begin_apply. If one wishes to insert code + /// /after/ the end_apply/abort_apply, please use instead + /// insertAfterFullEvaluation. + void insertAfterInvocation( + function_ref func) const { + switch (getKind()) { + case FullApplySiteKind::ApplyInst: + case FullApplySiteKind::BeginApplyInst: + return func(std::next(getInstruction()->getIterator())); + case FullApplySiteKind::TryApplyInst: + for (auto *succBlock : + cast(getInstruction())->getSuccessorBlocks()) { + func(succBlock->begin()); + } + return; + } + llvm_unreachable("Covered switch isn't covered"); + } + + /// Pass to func insertion points that are guaranteed to be immediately after + /// this full apply site has completely finished executing. + /// + /// This is just like insertAfterInvocation except that if the full apply site + /// is a begin_apply, we pass the insertion points after the end_apply, + /// abort_apply rather than an insertion point right after the + /// begin_apply. For such functionality, please invoke insertAfterInvocation. + void insertAfterFullEvaluation( + function_ref func) const { + switch (getKind()) { + case FullApplySiteKind::ApplyInst: + case FullApplySiteKind::TryApplyInst: + return insertAfterInvocation(func); + case FullApplySiteKind::BeginApplyInst: + SmallVector endApplies; + SmallVector abortApplies; + auto *bai = cast(getInstruction()); + bai->getCoroutineEndPoints(endApplies, abortApplies); + for (auto *eai : endApplies) { + func(std::next(eai->getIterator())); + } + for (auto *aai : abortApplies) { + func(std::next(aai->getIterator())); + } + return; + } + + llvm_unreachable("covered switch isn't covered"); + } + static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); } static bool classof(const SILInstruction *inst) { diff --git a/include/swift/SIL/DynamicCasts.h b/include/swift/SIL/DynamicCasts.h index 92d30272dee4c..712115919f7c9 100644 --- a/include/swift/SIL/DynamicCasts.h +++ b/include/swift/SIL/DynamicCasts.h @@ -329,76 +329,73 @@ struct SILDynamicCastInst { llvm_unreachable("covered switch"); } - CanType getSourceType() const { + CanType getSourceFormalType() const { switch (getKind()) { case SILDynamicCastKind::CheckedCastAddrBranchInst: - return cast(inst)->getSourceType(); + return cast(inst)->getSourceFormalType(); case SILDynamicCastKind::CheckedCastBranchInst: - return cast(inst)->getSourceType(); + return cast(inst)->getSourceFormalType(); case SILDynamicCastKind::CheckedCastValueBranchInst: - return cast(inst)->getSourceType(); + return cast(inst)->getSourceFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: - return cast(inst)->getSourceType(); + return cast(inst)->getSourceFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastInst: - return cast(inst)->getSourceType(); + return cast(inst)->getSourceFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastValueInst: - llvm_unreachable("unsupported"); + return cast(inst)->getSourceFormalType(); } llvm_unreachable("covered switch"); } - SILType getLoweredSourceType() const { + SILType getSourceLoweredType() const { switch (getKind()) { case SILDynamicCastKind::CheckedCastAddrBranchInst: + return cast(inst)->getSourceLoweredType(); case SILDynamicCastKind::CheckedCastBranchInst: + return cast(inst)->getSourceLoweredType(); case SILDynamicCastKind::CheckedCastValueBranchInst: - llvm_unreachable("unsupported"); - case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: { - auto *uccai = cast(inst); - return uccai->getSrc()->getType(); - } + return cast(inst)->getSourceLoweredType(); + case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: + return cast(inst)->getSourceLoweredType(); case SILDynamicCastKind::UnconditionalCheckedCastInst: - return cast(inst)->getOperand()->getType(); + return cast(inst)->getSourceLoweredType(); case SILDynamicCastKind::UnconditionalCheckedCastValueInst: - llvm_unreachable("unsupported"); + return cast(inst)->getSourceLoweredType(); } } - CanType getTargetType() const { + CanType getTargetFormalType() const { switch (getKind()) { case SILDynamicCastKind::CheckedCastAddrBranchInst: - return cast(inst)->getTargetType(); + return cast(inst)->getTargetFormalType(); case SILDynamicCastKind::CheckedCastBranchInst: - return cast(inst)->getTargetType(); + return cast(inst)->getTargetFormalType(); case SILDynamicCastKind::CheckedCastValueBranchInst: - return cast(inst)->getTargetType(); + return cast(inst)->getTargetFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: - return cast(inst)->getTargetType(); + return cast(inst)->getTargetFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastInst: - return cast(inst)->getTargetType(); + return cast(inst)->getTargetFormalType(); case SILDynamicCastKind::UnconditionalCheckedCastValueInst: - llvm_unreachable("unimplemented"); + return cast(inst)->getTargetFormalType(); } llvm_unreachable("covered switch"); } - SILType getLoweredTargetType() const { + SILType getTargetLoweredType() const { switch (getKind()) { case SILDynamicCastKind::CheckedCastAddrBranchInst: - llvm_unreachable("unsupported"); + return cast(inst)->getDest()->getType(); case SILDynamicCastKind::CheckedCastBranchInst: - return cast(inst)->getCastType(); + return cast(inst)->getTargetLoweredType(); case SILDynamicCastKind::CheckedCastValueBranchInst: - return cast(inst)->getCastType(); - case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: { - auto *uccai = dyn_cast(inst); - return uccai->getDest()->getType(); - } - case SILDynamicCastKind::UnconditionalCheckedCastInst: { - return cast(inst)->getType(); - } + return cast(inst)->getTargetLoweredType(); + case SILDynamicCastKind::UnconditionalCheckedCastAddrInst: + return cast(inst)->getDest()->getType(); + case SILDynamicCastKind::UnconditionalCheckedCastInst: + return cast(inst)->getTargetLoweredType(); case SILDynamicCastKind::UnconditionalCheckedCastValueInst: - llvm_unreachable("unsupported"); + return cast(inst)->getTargetLoweredType(); } llvm_unreachable("covered switch"); } @@ -424,14 +421,15 @@ struct SILDynamicCastInst { DynamicCastFeasibility classifyFeasibility(bool allowWholeModule) const { return swift::classifyDynamicCast( - getModule().getSwiftModule(), getSourceType(), getTargetType(), + getModule().getSwiftModule(), + getSourceFormalType(), getTargetFormalType(), isSourceTypeExact(), allowWholeModule && getModule().isWholeModule()); } bool isBridgingCast() const { // Bridging casts cannot be further simplified. - auto TargetIsBridgeable = getTargetType()->isBridgeableObjectType(); - auto SourceIsBridgeable = getSourceType()->isBridgeableObjectType(); + auto TargetIsBridgeable = getTargetFormalType()->isBridgeableObjectType(); + auto SourceIsBridgeable = getSourceFormalType()->isBridgeableObjectType(); return TargetIsBridgeable != SourceIsBridgeable; } @@ -441,7 +439,7 @@ struct SILDynamicCastInst { CanType getBridgedSourceType() const { SILModule &mod = getModule(); Type t = mod.getASTContext().getBridgedToObjC(mod.getSwiftModule(), - getSourceType()); + getSourceFormalType()); if (!t) return CanType(); return t->getCanonicalType(); @@ -453,7 +451,7 @@ struct SILDynamicCastInst { CanType getBridgedTargetType() const { SILModule &mod = getModule(); Type t = mod.getASTContext().getBridgedToObjC(mod.getSwiftModule(), - getTargetType()); + getTargetFormalType()); if (!t) return CanType(); return t->getCanonicalType(); @@ -491,7 +489,7 @@ struct SILDynamicCastInst { bool canUseScalarCheckedCastInstructions() const { return swift::canUseScalarCheckedCastInstructions( - getModule(), getSourceType(), getTargetType()); + getModule(), getSourceFormalType(), getTargetFormalType()); } }; diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 83aab0876d8c6..09c7c0d7e76d5 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -488,6 +488,17 @@ AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr); /// uninitialized. bool memInstMustInitialize(Operand *memOper); +/// Is this an alloc_stack instruction that is: +/// +/// 1. Only initialized once in its own def block. +/// 2. Never written to again except by destroy_addr. +/// +/// On return, destroyingUsers contains the list of users that destroy the +/// alloc_stack. If the alloc_stack is destroyed in pieces, we do not guarantee +/// that the list of destroying users is a minimal jointly post-dominating set. +bool isSingleInitAllocStack(AllocStackInst *asi, + SmallVectorImpl &destroyingUsers); + /// Return true if the given address producer may be the source of a formal /// access (a read or write of a potentially aliased, user visible variable). /// diff --git a/include/swift/SIL/MemoryLifetime.h b/include/swift/SIL/MemoryLifetime.h index b27b066116d5f..42e0491eaa187 100644 --- a/include/swift/SIL/MemoryLifetime.h +++ b/include/swift/SIL/MemoryLifetime.h @@ -22,6 +22,16 @@ namespace swift { +void printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits); + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const SmallBitVector &bits) { + printBitsAsArray(OS, bits); + return OS; +} + +void dumpBits(const SmallBitVector &bits); + /// The MemoryLocations utility provides functions to analyze memory locations. /// /// Memory locations are limited to addresses which are guaranteed to @@ -277,6 +287,21 @@ class MemoryLocations { /// for memory locations. Consider renaming it. class MemoryDataflow { + /// What kind of terminators can be reached from a block. + enum class ExitReachability : uint8_t { + /// Worst case: the block is part of a cycle which neither reaches a + /// function-exit nor an unreachable-instruction. + InInfiniteLoop, + + /// An unreachable-instruction can be reached from the block, but not a + /// function-exit (like "return" or "throw"). + ReachesUnreachable, + + /// A function-exit can be reached from the block. + /// This is the case for most basic blocks. + ReachesExit + }; + public: using Bits = MemoryLocations::Bits; @@ -303,11 +328,10 @@ class MemoryDataflow { /// This flag is only computed if entryReachabilityAnalysis is called. bool reachableFromEntry = false; - /// True, if any function-exit block can be reached from this block, i.e. is - /// not a block which eventually ends in an unreachable instruction. + /// What kind of terminators can be reached from this block. /// - /// This flag is only computed if exitReachableAnalysis is called. - bool exitReachable = false; + /// This is only computed if exitReachableAnalysis is called. + ExitReachability exitReachability = ExitReachability::InInfiniteLoop; BlockState(SILBasicBlock *block = nullptr) : block(block) { } @@ -326,6 +350,14 @@ class MemoryDataflow { killSet |= loc->subLocations; } } + + bool exitReachable() const { + return exitReachability == ExitReachability::ReachesExit; + } + + bool isInInfiniteLoop() const { + return exitReachability == ExitReachability::InInfiniteLoop; + } }; private: diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index df84e9ec2044a..8f72bb8ef4d0e 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SIL_OWNERSHIPUTILS_H #define SWIFT_SIL_OWNERSHIPUTILS_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/SILArgument.h" @@ -182,6 +183,29 @@ class LinearLifetimeChecker { nullptr /*leakingBlocks*/) .getFoundError(); } + + bool validateLifetime(SILValue value, + ArrayRef consumingUses, + ArrayRef nonConsumingUses) { + assert(llvm::all_of( + consumingUses, + [](SILInstruction *i) { return !isa(i); }) && + "Passed cond branch to a non-BranchPropagatedUser API"); + assert(llvm::all_of( + nonConsumingUses, + [](SILInstruction *i) { return !isa(i); }) && + "Passed cond branch to a non-BranchPropagatedUser API"); + auto *consumingUsesCast = + reinterpret_cast(consumingUses.data()); + auto *nonConsumingUsesCast = + reinterpret_cast(nonConsumingUses.data()); + ArrayRef consumingUsesCastArray(consumingUsesCast, + consumingUses.size()); + ArrayRef nonConsumingUsesCastArray( + nonConsumingUsesCast, nonConsumingUses.size()); + return validateLifetime(value, consumingUsesCastArray, + nonConsumingUsesCastArray); + } }; /// Returns true if v is an address or trivial. @@ -227,7 +251,7 @@ struct BorrowScopeOperandKind { } void print(llvm::raw_ostream &os) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; /// An operand whose user instruction introduces a new borrow scope for the @@ -327,7 +351,7 @@ struct BorrowScopeIntroducingValueKind { } void print(llvm::raw_ostream &os) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, @@ -404,11 +428,11 @@ struct BorrowScopeIntroducingValue { /// /// NOTE: Scratch space is used internally to this method to store the end /// borrow scopes if needed. - bool areInstructionsWithinScope( - ArrayRef instructions, - SmallVectorImpl &scratchSpace, - SmallPtrSetImpl &visitedBlocks, - DeadEndBlocks &deadEndBlocks) const; + bool + areInstructionsWithinScope(ArrayRef instructions, + SmallVectorImpl &scratchSpace, + SmallPtrSetImpl &visitedBlocks, + DeadEndBlocks &deadEndBlocks) const; private: /// Internal constructor for failable static constructor. Please do not expand diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index 1e4324e07b29f..1ccc0fad703bc 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -293,7 +293,8 @@ class Projection { /// /// WARNING: This is not a constant time operation because it is implemented /// in terms of getVarDecl, which requests all BaseType's stored properties. - SILType getType(SILType BaseType, SILModule &M) const; + SILType getType(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; VarDecl *getVarDecl(SILType BaseType) const { assert(isValid()); @@ -402,6 +403,7 @@ class Projection { /// Given a specific SILType, return all first level projections if it is an /// aggregate. static void getFirstLevelProjections(SILType V, SILModule &Mod, + TypeExpansionContext context, llvm::SmallVectorImpl &Out); /// Is this cast which only allows for equality? @@ -584,6 +586,7 @@ class ProjectionPath { /// is a leaf node in the type tree. static void expandTypeIntoLeafProjectionPaths(SILType BaseType, SILModule *Mod, + TypeExpansionContext context, ProjectionPathList &P); /// Return true if the given projection paths in \p CPaths does not cover @@ -626,26 +629,27 @@ class ProjectionPath { SILType getBaseType() const { return BaseType; } /// Returns the most derived type of the projection path. - SILType getMostDerivedType(SILModule &M) { + SILType getMostDerivedType(SILModule &M, TypeExpansionContext context) { if (Path.empty()) return getBaseType(); if (MostDerivedType) return MostDerivedType; - MostDerivedType = getDerivedType(Path.size(), M); + MostDerivedType = getDerivedType(Path.size(), M, context); return MostDerivedType; } /// Returns the ith derived type of the path. This is zero indexed with 0 /// being the base type and n consisting of applying the up to n projections /// to the base type. - SILType getDerivedType(unsigned i, SILModule &M) const { + SILType getDerivedType(unsigned i, SILModule &M, + TypeExpansionContext context) const { assert(i <= Path.size()); SILType IterTy = getBaseType(); if (i == 0) return IterTy; for (unsigned j : range(i)) { auto &Proj = Path[j]; - IterTy = Proj.getType(IterTy, M); + IterTy = Proj.getType(IterTy, M, context); } return IterTy; } @@ -671,10 +675,11 @@ class ProjectionPath { const_reverse_iterator rbegin() const { return Path.rbegin(); } const_reverse_iterator rend() const { return Path.rend(); } - void verify(SILModule &M); + void verify(SILModule &M, TypeExpansionContext context); - raw_ostream &print(raw_ostream &OS, SILModule &M) const; - void dump(SILModule &M) const; + raw_ostream &print(raw_ostream &OS, SILModule &M, + TypeExpansionContext context) const; + void dump(SILModule &M, TypeExpansionContext context) const; }; /// Returns the hashcode for the new projection path. @@ -813,9 +818,11 @@ class ProjectionTreeNode { llvm::SmallVectorImpl &Worklist, SILValue Value); - void createNextLevelChildren(ProjectionTree &Tree); + void createNextLevelChildren(ProjectionTree &Tree, TypeExpansionContext context); - void createNextLevelChildrenForStruct(ProjectionTree &Tree, StructDecl *SD); + void createNextLevelChildrenForStruct(ProjectionTree &Tree, + TypeExpansionContext context, + StructDecl *SD); void createNextLevelChildrenForTuple(ProjectionTree &Tree, TupleType *TT); }; diff --git a/include/swift/SIL/SILAllocated.h b/include/swift/SIL/SILAllocated.h index 4e967896ceec4..767ba21d7ada5 100644 --- a/include/swift/SIL/SILAllocated.h +++ b/include/swift/SIL/SILAllocated.h @@ -31,7 +31,7 @@ class SILAllocated { void *operator new[](size_t) = delete; /// Disable non-placement delete. - void operator delete(void *) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *) = delete; void operator delete[](void *) = delete; /// Custom version of 'new' that uses the SILModule's BumpPtrAllocator with diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index f7993e3a3032c..4f23c1cd6ff46 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -62,29 +62,52 @@ struct SILArgumentKind { }; class SILArgument : public ValueBase { - void operator=(const SILArgument &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + friend class SILBasicBlock; + + SILBasicBlock *parentBlock; + const ValueDecl *decl; + +protected: + SILArgument(ValueKind subClassKind, SILBasicBlock *inputParentBlock, + SILType type, ValueOwnershipKind ownershipKind, + const ValueDecl *inputDecl = nullptr); + + SILArgument(ValueKind subClassKind, SILBasicBlock *inputParentBlock, + SILBasicBlock::arg_iterator positionInArgumentArray, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *inputDecl = nullptr); - SILBasicBlock *ParentBB; - const ValueDecl *Decl; + // A special constructor, only intended for use in + // SILBasicBlock::replacePHIArg and replaceFunctionArg. + explicit SILArgument(ValueKind subClassKind, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *inputDecl = nullptr) + : ValueBase(subClassKind, type, IsRepresentative::Yes), + parentBlock(nullptr), decl(inputDecl) { + Bits.SILArgument.VOKind = static_cast(ownershipKind); + } public: + void operator=(const SILArgument &) = delete; + void operator delete(void *, size_t) = delete; + ValueOwnershipKind getOwnershipKind() const { return static_cast(Bits.SILArgument.VOKind); } - void setOwnershipKind(ValueOwnershipKind NewKind) { - Bits.SILArgument.VOKind = static_cast(NewKind); + + void setOwnershipKind(ValueOwnershipKind newKind) { + Bits.SILArgument.VOKind = static_cast(newKind); } - SILBasicBlock *getParent() { return ParentBB; } - const SILBasicBlock *getParent() const { return ParentBB; } + SILBasicBlock *getParent() { return parentBlock; } + const SILBasicBlock *getParent() const { return parentBlock; } SILFunction *getFunction(); const SILFunction *getFunction() const; SILModule &getModule() const; - const ValueDecl *getDecl() const { return Decl; } + const ValueDecl *getDecl() const { return decl; } static bool classof(const SILInstruction *) = delete; static bool classof(const SILUndef *) = delete; @@ -94,31 +117,32 @@ class SILArgument : public ValueBase { } unsigned getIndex() const { - ArrayRef Args = getParent()->getArguments(); - for (unsigned i = 0, e = Args.size(); i != e; ++i) - if (Args[i] == this) - return i; + for (auto p : llvm::enumerate(getParent()->getArguments())) { + if (p.value() == this) { + return p.index(); + } + } llvm_unreachable("SILArgument not argument of its parent BB"); } /// Return true if this block argument is actually a phi argument as /// opposed to a cast or projection. - bool isPhiArgument(); + bool isPhiArgument() const; /// If this argument is a phi, return the incoming phi value for the given /// predecessor BB. If this argument is not a phi, return an invalid SILValue. - SILValue getIncomingPhiValue(SILBasicBlock *predBB); + SILValue getIncomingPhiValue(SILBasicBlock *predBlock) const; /// If this argument is a phi, populate `OutArray` with the incoming phi /// values for each predecessor BB. If this argument is not a phi, return /// false. - bool getIncomingPhiValues(llvm::SmallVectorImpl &ReturnedPhiValues); + bool getIncomingPhiValues(SmallVectorImpl &returnedPhiValues) const; /// If this argument is a phi, populate `OutArray` with each predecessor block /// and its incoming phi value. If this argument is not a phi, return false. - bool getIncomingPhiValues( - llvm::SmallVectorImpl> - &ReturnedPredAndPhiValuePairs); + bool + getIncomingPhiValues(SmallVectorImpl> + &returnedPredAndPhiValuePairs) const; /// Returns true if we were able to find a single terminator operand value for /// each predecessor of this arguments basic block. The found values are @@ -127,7 +151,8 @@ class SILArgument : public ValueBase { /// Note: this peeks through any projections or cast implied by the /// terminator. e.g. the incoming value for a switch_enum payload argument is /// the enum itself (the operand of the switch_enum). - bool getSingleTerminatorOperands(llvm::SmallVectorImpl &OutArray); + bool getSingleTerminatorOperands( + SmallVectorImpl &returnedSingleTermOperands) const; /// Returns true if we were able to find single terminator operand values for /// each predecessor of this arguments basic block. The found values are @@ -137,7 +162,8 @@ class SILArgument : public ValueBase { /// terminator. e.g. the incoming value for a switch_enum payload argument is /// the enum itself (the operand of the switch_enum). bool getSingleTerminatorOperands( - llvm::SmallVectorImpl> &OutArray); + SmallVectorImpl> + &returnedSingleTermOperands) const; /// If this SILArgument's parent block has a single predecessor whose /// terminator has a single operand, return the incoming operand of the @@ -153,40 +179,44 @@ class SILArgument : public ValueBase { } protected: - SILArgument(ValueKind SubClassKind, SILBasicBlock *ParentBB, SILType Ty, - ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr); - SILArgument(ValueKind SubClassKind, SILBasicBlock *ParentBB, - SILBasicBlock::arg_iterator Pos, SILType Ty, - ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr); - - // A special constructor, only intended for use in - // SILBasicBlock::replacePHIArg and replaceFunctionArg. - explicit SILArgument(ValueKind SubClassKind, SILType Ty, - ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : ValueBase(SubClassKind, Ty, IsRepresentative::Yes), ParentBB(nullptr), - Decl(D) { - Bits.SILArgument.VOKind = static_cast(OwnershipKind); + void setParent(SILBasicBlock *newParentBlock) { + parentBlock = newParentBlock; } - void setParent(SILBasicBlock *P) { ParentBB = P; } - - friend SILBasicBlock; }; class SILPhiArgument : public SILArgument { + friend class SILBasicBlock; + + SILPhiArgument(SILBasicBlock *parentBlock, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILPhiArgument, parentBlock, type, ownershipKind, + decl) {} + + SILPhiArgument(SILBasicBlock *parentBlock, + SILBasicBlock::arg_iterator argArrayInsertPt, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILPhiArgument, parentBlock, argArrayInsertPt, + type, ownershipKind, decl) {} + + // A special constructor, only intended for use in + // SILBasicBlock::replacePHIArg. + explicit SILPhiArgument(SILType type, ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILPhiArgument, type, ownershipKind, decl) {} + public: /// Return true if this is block argument is actually a phi argument as /// opposed to a cast or projection. - bool isPhiArgument(); + bool isPhiArgument() const; /// If this argument is a phi, return the incoming phi value for the given /// predecessor BB. If this argument is not a phi, return an invalid SILValue. /// /// FIXME: Once SILPhiArgument actually implies that it is a phi argument, /// this will be guaranteed to return a valid SILValue. - SILValue getIncomingPhiValue(SILBasicBlock *BB); + SILValue getIncomingPhiValue(SILBasicBlock *predBlock) const; /// If this argument is a phi, populate `OutArray` with the incoming phi /// values for each predecessor BB. If this argument is not a phi, return @@ -194,15 +224,16 @@ class SILPhiArgument : public SILArgument { /// /// FIXME: Once SILPhiArgument actually implies that it is a phi argument, /// this will always succeed. - bool getIncomingPhiValues(llvm::SmallVectorImpl &OutArray); + bool getIncomingPhiValues(SmallVectorImpl &returnedPhiValues) const; /// If this argument is a phi, populate `OutArray` with each predecessor block /// and its incoming phi value. If this argument is not a phi, return false. /// /// FIXME: Once SILPhiArgument actually implies that it is a phi argument, /// this will always succeed. - bool getIncomingPhiValues( - llvm::SmallVectorImpl> &OutArray); + bool + getIncomingPhiValues(SmallVectorImpl> + &returnedPredAndPhiValuePairs) const; /// Returns true if we were able to find a single terminator operand value for /// each predecessor of this arguments basic block. The found values are @@ -211,7 +242,8 @@ class SILPhiArgument : public SILArgument { /// Note: this peeks through any projections or cast implied by the /// terminator. e.g. the incoming value for a switch_enum payload argument is /// the enum itself (the operand of the switch_enum). - bool getSingleTerminatorOperands(llvm::SmallVectorImpl &OutArray); + bool getSingleTerminatorOperands( + SmallVectorImpl &returnedSingleTermOperands) const; /// Returns true if we were able to find single terminator operand values for /// each predecessor of this arguments basic block. The found values are @@ -221,7 +253,8 @@ class SILPhiArgument : public SILArgument { /// terminator. e.g. the incoming value for a switch_enum payload argument is /// the enum itself (the operand of the switch_enum). bool getSingleTerminatorOperands( - llvm::SmallVectorImpl> &OutArray); + SmallVectorImpl> + &returnedSingleTermOperands) const; /// If this SILArgument's parent block has a single predecessor whose /// terminator has a single operand, return the incoming operand of the @@ -236,30 +269,35 @@ class SILPhiArgument : public SILArgument { static bool classof(const SILNode *node) { return node->getKind() == SILNodeKind::SILPhiArgument; } +}; -private: - friend SILBasicBlock; - SILPhiArgument(SILBasicBlock *ParentBB, SILType Ty, ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILPhiArgument, ParentBB, Ty, OwnershipKind, D) {} - SILPhiArgument(SILBasicBlock *ParentBB, SILBasicBlock::arg_iterator Pos, - SILType Ty, ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILPhiArgument, ParentBB, Pos, Ty, OwnershipKind, D) {} +class SILFunctionArgument : public SILArgument { + friend class SILBasicBlock; + + SILFunctionArgument(SILBasicBlock *parentBlock, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILFunctionArgument, parentBlock, type, + ownershipKind, decl) {} + SILFunctionArgument(SILBasicBlock *parentBlock, + SILBasicBlock::arg_iterator argArrayInsertPt, + SILType type, ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILFunctionArgument, parentBlock, + argArrayInsertPt, type, ownershipKind, decl) {} // A special constructor, only intended for use in - // SILBasicBlock::replacePHIArg. - explicit SILPhiArgument(SILType Ty, ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILPhiArgument, Ty, OwnershipKind, D) {} -}; + // SILBasicBlock::replaceFunctionArg. + explicit SILFunctionArgument(SILType type, ValueOwnershipKind ownershipKind, + const ValueDecl *decl = nullptr) + : SILArgument(ValueKind::SILFunctionArgument, type, ownershipKind, decl) { + } -class SILFunctionArgument : public SILArgument { public: bool isIndirectResult() const { auto numIndirectResults = getFunction()->getConventions().getNumIndirectSILResults(); - return (getIndex() < numIndirectResults); + return getIndex() < numIndirectResults; } SILArgumentConvention getArgumentConvention() const { @@ -280,8 +318,8 @@ class SILFunctionArgument : public SILArgument { bool isSelf() const; /// Returns true if this SILArgument is passed via the given convention. - bool hasConvention(SILArgumentConvention P) const { - return getArgumentConvention() == P; + bool hasConvention(SILArgumentConvention convention) const { + return getArgumentConvention() == convention; } static bool classof(const SILInstruction *) = delete; @@ -289,67 +327,80 @@ class SILFunctionArgument : public SILArgument { static bool classof(const SILNode *node) { return node->getKind() == SILNodeKind::SILFunctionArgument; } - -private: - friend SILBasicBlock; - - SILFunctionArgument(SILBasicBlock *ParentBB, SILType Ty, ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILFunctionArgument, ParentBB, Ty, OwnershipKind, D) {} - SILFunctionArgument(SILBasicBlock *ParentBB, SILBasicBlock::arg_iterator Pos, - SILType Ty, ValueOwnershipKind OwnershipKind, const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILFunctionArgument, ParentBB, Pos, Ty, OwnershipKind, D) {} - - // A special constructor, only intended for use in - // SILBasicBlock::replaceFunctionArg. - explicit SILFunctionArgument(SILType Ty, ValueOwnershipKind OwnershipKind, - const ValueDecl *D = nullptr) - : SILArgument(ValueKind::SILFunctionArgument, Ty, OwnershipKind, D) {} }; //===----------------------------------------------------------------------===// // Out of line Definitions for SILArgument to avoid Forward Decl issues //===----------------------------------------------------------------------===// -inline bool SILArgument::isPhiArgument() { - if (auto *phiArg = dyn_cast(this)) - return phiArg->isPhiArgument(); - - return false; +inline bool SILArgument::isPhiArgument() const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->isPhiArgument(); + case SILArgumentKind::SILFunctionArgument: + return false; + } + llvm_unreachable("Covered switch is not covered?!"); } -inline SILValue SILArgument::getIncomingPhiValue(SILBasicBlock *BB) { - if (isa(this)) +inline SILValue +SILArgument::getIncomingPhiValue(SILBasicBlock *predBlock) const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getIncomingPhiValue(predBlock); + case SILArgumentKind::SILFunctionArgument: return SILValue(); - return cast(this)->getIncomingPhiValue(BB); + } + llvm_unreachable("Covered switch is not covered?!"); } -inline bool -SILArgument::getIncomingPhiValues(llvm::SmallVectorImpl &OutArray) { - if (isa(this)) +inline bool SILArgument::getIncomingPhiValues( + SmallVectorImpl &returnedPhiValues) const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getIncomingPhiValues(returnedPhiValues); + case SILArgumentKind::SILFunctionArgument: return false; - return cast(this)->getIncomingPhiValues(OutArray); + } + llvm_unreachable("Covered switch is not covered?!"); } inline bool SILArgument::getIncomingPhiValues( - llvm::SmallVectorImpl> &OutArray) { - if (isa(this)) + SmallVectorImpl> + &returnedPredAndPhiValuePairs) const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getIncomingPhiValues( + returnedPredAndPhiValuePairs); + case SILArgumentKind::SILFunctionArgument: return false; - return cast(this)->getIncomingPhiValues(OutArray); + } + llvm_unreachable("Covered switch is not covered?!"); } inline bool SILArgument::getSingleTerminatorOperands( - llvm::SmallVectorImpl &OutArray) { - if (isa(this)) + SmallVectorImpl &returnedSingleTermOperands) const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getSingleTerminatorOperands( + returnedSingleTermOperands); + case SILArgumentKind::SILFunctionArgument: return false; - return cast(this)->getSingleTerminatorOperands(OutArray); + } + llvm_unreachable("Covered switch is not covered?!"); } inline bool SILArgument::getSingleTerminatorOperands( - llvm::SmallVectorImpl> &OutArray) { - if (isa(this)) + SmallVectorImpl> + &returnedSingleTermOperands) const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getSingleTerminatorOperands( + returnedSingleTermOperands); + case SILArgumentKind::SILFunctionArgument: return false; - return cast(this)->getSingleTerminatorOperands(OutArray); + } + llvm_unreachable("Covered switch is not covered?!"); } } // end swift namespace diff --git a/include/swift/SIL/SILArgumentArrayRef.h b/include/swift/SIL/SILArgumentArrayRef.h index c01a656efd20d..211add0d3bcc6 100644 --- a/include/swift/SIL/SILArgumentArrayRef.h +++ b/include/swift/SIL/SILArgumentArrayRef.h @@ -26,15 +26,12 @@ namespace swift { class SILArgument; -class SILPhiArgument; -class SILFunctionArgument; -using PhiArgumentArrayRef = - TransformRange, SILPhiArgument *(*)(SILArgument *)>; - -using FunctionArgumentArrayRef = - TransformRange, - SILFunctionArgument *(*)(SILArgument *)>; +#define ARGUMENT(NAME, PARENT) \ + class NAME; \ + using NAME##ArrayRef = \ + TransformRange, NAME *(*)(SILArgument *)>; +#include "swift/SIL/SILNodes.def" } // namespace swift diff --git a/include/swift/SIL/SILArgumentConvention.h b/include/swift/SIL/SILArgumentConvention.h index fbbab02e67c1c..429ec381a38e1 100644 --- a/include/swift/SIL/SILArgumentConvention.h +++ b/include/swift/SIL/SILArgumentConvention.h @@ -17,18 +17,6 @@ namespace swift { -enum class InoutAliasingAssumption { - /// Assume that an inout indirect parameter may alias other objects. - /// This is the safe assumption an optimization should make if it may break - /// memory safety in case the inout aliasing rule is violation. - Aliasing, - - /// Assume that an inout indirect parameter cannot alias other objects. - /// Optimizations should only use this if they can guarantee that they will - /// not break memory safety even if the inout aliasing rule is violated. - NotAliasing -}; - /// Conventions for apply operands and function-entry arguments in SIL. /// /// This is simply a union of ParameterConvention and ResultConvention @@ -141,20 +129,15 @@ struct SILArgumentConvention { llvm_unreachable("covered switch isn't covered?!"); } - /// Returns true if \p Value is a not-aliasing indirect parameter. - /// The \p isInoutAliasing specifies what to assume about the inout - /// convention. - /// See InoutAliasingAssumption. - bool isNotAliasedIndirectParameter(InoutAliasingAssumption isInoutAliasing) { + /// Returns true if \p Value is a non-aliasing indirect parameter. + bool isExclusiveIndirectParameter() { switch (Value) { case SILArgumentConvention::Indirect_In: case SILArgumentConvention::Indirect_In_Constant: case SILArgumentConvention::Indirect_Out: case SILArgumentConvention::Indirect_In_Guaranteed: - return true; - case SILArgumentConvention::Indirect_Inout: - return isInoutAliasing == InoutAliasingAssumption::NotAliasing; + return true; case SILArgumentConvention::Indirect_InoutAliasable: case SILArgumentConvention::Direct_Unowned: diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index db7f53cd6a5b9..dd5748bb24951 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -26,8 +26,6 @@ namespace swift { class SILFunction; class SILArgument; -class SILPhiArgument; -class SILFunctionArgument; class SILPrintContext; class SILBasicBlock : @@ -56,7 +54,7 @@ public llvm::ilist_node, public SILAllocated { SILBasicBlock() : Parent(nullptr) {} void operator=(const SILBasicBlock &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; SILBasicBlock(SILFunction *F, SILBasicBlock *relativeToBB, bool after); @@ -193,14 +191,10 @@ public llvm::ilist_node, public SILAllocated { ArrayRef getArguments() const { return ArgumentList; } - /// Returns a transform array ref that performs llvm::cast on + /// Returns a transform array ref that performs llvm::cast /// each argument and then returns the downcasted value. - PhiArgumentArrayRef getPhiArguments() const; - - /// Returns a transform array ref that performs - /// llvm::cast on each argument and then returns the - /// downcasted value. - FunctionArgumentArrayRef getFunctionArguments() const; +#define ARGUMENT(NAME, PARENT) NAME##ArrayRef get##NAME##s() const; +#include "swift/SIL/SILNodes.def" unsigned getNumArguments() const { return ArgumentList.size(); } const SILArgument *getArgument(unsigned i) const { return ArgumentList[i]; } @@ -214,7 +208,8 @@ public llvm::ilist_node, public SILAllocated { /// Allocate a new argument of type \p Ty and append it to the argument /// list. Optionally you can pass in a value decl parameter. SILFunctionArgument *createFunctionArgument(SILType Ty, - const ValueDecl *D = nullptr); + const ValueDecl *D = nullptr, + bool disableEntryBlockVerification = false); SILFunctionArgument *insertFunctionArgument(unsigned Index, SILType Ty, ValueOwnershipKind OwnershipKind, diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 54c63fb91d109..bda95ab9f3aca 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -174,6 +174,9 @@ class SILBuilder { setInsertionPoint(I); } + SILBuilder(SILBasicBlock *BB, const SILDebugScope *DS, SILBuilder &B) + : SILBuilder(BB, DS, B.getBuilderContext()) {} + /// Build instructions before the given insertion point, inheriting the debug /// location. /// @@ -194,16 +197,23 @@ class SILBuilder { assert(F && "cannot create this instruction without a function context"); return *F; } + + TypeExpansionContext getTypeExpansionContext() const { + return TypeExpansionContext(getFunction()); + } + SILBuilderContext &getBuilderContext() const { return C; } SILModule &getModule() const { return C.Module; } ASTContext &getASTContext() const { return getModule().getASTContext(); } const Lowering::TypeLowering &getTypeLowering(SILType T) const { - auto expansion = ResilienceExpansion::Maximal; + + auto expansion = TypeExpansionContext::maximal(getModule().getSwiftModule(), + getModule().isWholeModule()); // If there's no current SILFunction, we're inserting into a global // variable initializer. - if (F) - expansion = F->getResilienceExpansion(); - + if (F) { + expansion = TypeExpansionContext(getFunction()); + } return getModule().Types.getTypeLowering(T, expansion); } @@ -333,12 +343,11 @@ class SILBuilder { // Type remapping //===--------------------------------------------------------------------===// - static SILType - getPartialApplyResultType(SILType Ty, unsigned ArgCount, SILModule &M, - SubstitutionMap subs, - ParameterConvention calleeConvention, - PartialApplyInst::OnStackKind onStack = - PartialApplyInst::OnStackKind::NotOnStack); + static SILType getPartialApplyResultType( + TypeExpansionContext context, SILType Ty, unsigned ArgCount, SILModule &M, + SubstitutionMap subs, ParameterConvention calleeConvention, + PartialApplyInst::OnStackKind onStack = + PartialApplyInst::OnStackKind::NotOnStack); //===--------------------------------------------------------------------===// // CFG Manipulation @@ -571,7 +580,8 @@ class SILBuilder { FunctionRefBaseInst *createFunctionRefFor(SILLocation Loc, SILFunction *f) { if (f->isDynamicallyReplaceable()) return createDynamicFunctionRef(Loc, f); - else return createFunctionRef(Loc, f); + else + return createFunctionRef(Loc, f); } FunctionRefBaseInst *createFunctionRef(SILLocation Loc, SILFunction *f, @@ -587,20 +597,20 @@ class SILBuilder { } FunctionRefInst *createFunctionRef(SILLocation Loc, SILFunction *f) { - return insert(new (getModule()) - FunctionRefInst(getSILDebugLocation(Loc), f)); + return insert(new (getModule()) FunctionRefInst(getSILDebugLocation(Loc), f, + getTypeExpansionContext())); } DynamicFunctionRefInst * createDynamicFunctionRef(SILLocation Loc, SILFunction *f) { return insert(new (getModule()) DynamicFunctionRefInst( - getSILDebugLocation(Loc), f)); + getSILDebugLocation(Loc), f, getTypeExpansionContext())); } PreviousDynamicFunctionRefInst * createPreviousDynamicFunctionRef(SILLocation Loc, SILFunction *f) { return insert(new (getModule()) PreviousDynamicFunctionRefInst( - getSILDebugLocation(Loc), f)); + getSILDebugLocation(Loc), f, getTypeExpansionContext())); } AllocGlobalInst *createAllocGlobal(SILLocation Loc, SILGlobalVariable *g) { @@ -608,16 +618,16 @@ class SILBuilder { AllocGlobalInst(getSILDebugLocation(Loc), g)); } GlobalAddrInst *createGlobalAddr(SILLocation Loc, SILGlobalVariable *g) { - return insert(new (getModule()) - GlobalAddrInst(getSILDebugLocation(Loc), g)); + return insert(new (getModule()) GlobalAddrInst(getSILDebugLocation(Loc), g, + getTypeExpansionContext())); } GlobalAddrInst *createGlobalAddr(SILLocation Loc, SILType Ty) { return insert(new (F->getModule()) GlobalAddrInst(getSILDebugLocation(Loc), Ty)); } GlobalValueInst *createGlobalValue(SILLocation Loc, SILGlobalVariable *g) { - return insert(new (getModule()) - GlobalValueInst(getSILDebugLocation(Loc), g)); + return insert(new (getModule()) GlobalValueInst(getSILDebugLocation(Loc), g, + getTypeExpansionContext())); } IntegerLiteralInst *createIntegerLiteral(IntegerLiteralExpr *E); @@ -911,11 +921,11 @@ class SILBuilder { return insert(new (getModule()) \ RefTo##Name##Inst(getSILDebugLocation(Loc), op, ty)); \ } \ - Copy##Name##ValueInst *createCopy##Name##Value(SILLocation Loc, \ - SILValue operand) { \ + StrongCopy##Name##ValueInst *createStrongCopy##Name##Value( \ + SILLocation Loc, SILValue operand) { \ auto type = getFunction().getLoweredType( \ operand->getType().getASTType().getReferenceStorageReferent()); \ - return insert(new (getModule()) Copy##Name##ValueInst( \ + return insert(new (getModule()) StrongCopy##Name##ValueInst( \ getSILDebugLocation(Loc), operand, type)); \ } @@ -1015,10 +1025,12 @@ class SILBuilder { } UncheckedRefCastAddrInst * - createUncheckedRefCastAddr(SILLocation Loc, SILValue src, CanType sourceType, - SILValue dest, CanType targetType) { + createUncheckedRefCastAddr(SILLocation Loc, + SILValue src, CanType sourceFormalType, + SILValue dest, CanType targetFormalType) { return insert(new (getModule()) UncheckedRefCastAddrInst( - getSILDebugLocation(Loc), src, sourceType, dest, targetType)); + getSILDebugLocation(Loc), src, sourceFormalType, + dest, targetFormalType)); } UncheckedAddrCastInst *createUncheckedAddrCast(SILLocation Loc, SILValue Op, @@ -1125,26 +1137,32 @@ class SILBuilder { } UnconditionalCheckedCastInst * - createUnconditionalCheckedCast(SILLocation Loc, SILValue op, SILType destTy) { + createUnconditionalCheckedCast(SILLocation Loc, SILValue op, + SILType destLoweredTy, + CanType destFormalTy) { return insert(UnconditionalCheckedCastInst::create( - getSILDebugLocation(Loc), op, destTy, getFunction(), - C.OpenedArchetypes)); + getSILDebugLocation(Loc), op, destLoweredTy, destFormalTy, + getFunction(), C.OpenedArchetypes)); } UnconditionalCheckedCastAddrInst * - createUnconditionalCheckedCastAddr(SILLocation Loc, SILValue src, - CanType sourceType, SILValue dest, - CanType targetType) { + createUnconditionalCheckedCastAddr(SILLocation Loc, + SILValue src, CanType sourceFormalType, + SILValue dest, CanType targetFormalType) { return insert(new (getModule()) UnconditionalCheckedCastAddrInst( - getSILDebugLocation(Loc), src, sourceType, dest, targetType)); + getSILDebugLocation(Loc), src, sourceFormalType, + dest, targetFormalType)); } UnconditionalCheckedCastValueInst * createUnconditionalCheckedCastValue(SILLocation Loc, - SILValue op, SILType destTy) { + SILValue op, CanType srcFormalTy, + SILType destLoweredTy, + CanType destFormalTy) { return insert(UnconditionalCheckedCastValueInst::create( - getSILDebugLocation(Loc), op, destTy, getFunction(), - C.OpenedArchetypes)); + getSILDebugLocation(Loc), op, srcFormalTy, + destLoweredTy, destFormalTy, + getFunction(), C.OpenedArchetypes)); } RetainValueInst *createRetainValue(SILLocation Loc, SILValue operand, @@ -1282,8 +1300,8 @@ class SILBuilder { UncheckedEnumDataInst *createUncheckedEnumData(SILLocation Loc, SILValue Operand, EnumElementDecl *Element) { - SILType EltType = - Operand->getType().getEnumElementType(Element, getModule()); + SILType EltType = Operand->getType().getEnumElementType( + Element, getModule(), getTypeExpansionContext()); return createUncheckedEnumData(Loc, Operand, Element, EltType); } @@ -1304,8 +1322,8 @@ class SILBuilder { UncheckedTakeEnumDataAddrInst * createUncheckedTakeEnumDataAddr(SILLocation Loc, SILValue Operand, EnumElementDecl *Element) { - SILType EltType = - Operand->getType().getEnumElementType(Element, getModule()); + SILType EltType = Operand->getType().getEnumElementType( + Element, getModule(), getTypeExpansionContext()); return createUncheckedTakeEnumDataAddr(Loc, Operand, Element, EltType); } @@ -1380,7 +1398,8 @@ class SILBuilder { StructExtractInst *createStructExtract(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto type = Operand->getType().getFieldType(Field, getModule()); + auto type = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createStructExtract(Loc, Operand, Field, type); } @@ -1394,7 +1413,8 @@ class SILBuilder { StructElementAddrInst * createStructElementAddr(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto ResultTy = Operand->getType().getFieldType(Field, getModule()); + auto ResultTy = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createStructElementAddr(Loc, Operand, Field, ResultTy); } @@ -1405,7 +1425,8 @@ class SILBuilder { } RefElementAddrInst *createRefElementAddr(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto ResultTy = Operand->getType().getFieldType(Field, getModule()); + auto ResultTy = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return createRefElementAddr(Loc, Operand, Field, ResultTy); } @@ -1762,12 +1783,6 @@ class SILBuilder { // Unchecked cast helpers //===--------------------------------------------------------------------===// - // Create an UncheckedRefCast if the source and dest types are legal, - // otherwise return null. - // Unwrap or wrap optional types as needed. - SingleValueInstruction *tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy); - // Create the appropriate cast instruction based on result type. SingleValueInstruction *createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty); @@ -1944,30 +1959,36 @@ class SILBuilder { CheckedCastBranchInst * createCheckedCastBranch(SILLocation Loc, bool isExact, SILValue op, - SILType destTy, SILBasicBlock *successBB, + SILType destLoweredTy, CanType destFormalTy, + SILBasicBlock *successBB, SILBasicBlock *failureBB, ProfileCounter Target1Count = ProfileCounter(), ProfileCounter Target2Count = ProfileCounter()); CheckedCastValueBranchInst * - createCheckedCastValueBranch(SILLocation Loc, SILValue op, SILType destTy, + createCheckedCastValueBranch(SILLocation Loc, + SILValue op, CanType srcFormalTy, + SILType destLoweredTy, + CanType destFormalTy, SILBasicBlock *successBB, SILBasicBlock *failureBB) { return insertTerminator(CheckedCastValueBranchInst::create( - getSILDebugLocation(Loc), op, destTy, successBB, failureBB, - getFunction(), C.OpenedArchetypes)); + getSILDebugLocation(Loc), op, srcFormalTy, + destLoweredTy, destFormalTy, + successBB, failureBB, getFunction(), C.OpenedArchetypes)); } CheckedCastAddrBranchInst * createCheckedCastAddrBranch(SILLocation Loc, CastConsumptionKind consumption, - SILValue src, CanType sourceType, SILValue dest, - CanType targetType, SILBasicBlock *successBB, + SILValue src, CanType sourceFormalType, + SILValue dest, CanType targetFormalType, + SILBasicBlock *successBB, SILBasicBlock *failureBB, ProfileCounter Target1Count = ProfileCounter(), ProfileCounter Target2Count = ProfileCounter()) { return insertTerminator(new (getModule()) CheckedCastAddrBranchInst( - getSILDebugLocation(Loc), consumption, src, sourceType, dest, - targetType, successBB, failureBB, Target1Count, Target2Count)); + getSILDebugLocation(Loc), consumption, src, sourceFormalType, dest, + targetFormalType, successBB, failureBB, Target1Count, Target2Count)); } //===--------------------------------------------------------------------===// @@ -2087,8 +2108,7 @@ class SILBuilder { /// lowering for the non-address value. void emitDestroyValueOperation(SILLocation Loc, SILValue v) { assert(!v->getType().isAddress()); - if (F->hasOwnership() && - v.getOwnershipKind() == ValueOwnershipKind::Any) + if (F->hasOwnership() && v.getOwnershipKind() == ValueOwnershipKind::None) return; auto &lowering = getTypeLowering(v->getType()); lowering.emitDestroyValue(*this, Loc, v); @@ -2129,7 +2149,8 @@ class SILBuilder { SILValue emitStructExtract(SILLocation Loc, SILValue Operand, VarDecl *Field) { - auto type = Operand->getType().getFieldType(Field, getModule()); + auto type = Operand->getType().getFieldType(Field, getModule(), + getTypeExpansionContext()); return emitStructExtract(Loc, Operand, Field, type); } @@ -2265,6 +2286,11 @@ class SILBuilderWithScope : public SILBuilder { inheritScopeFrom(InheritScopeFrom); } + explicit SILBuilderWithScope(SILBasicBlock *BB, SILBuilder &B, + SILInstruction *InheritScopeFrom) + : SILBuilder(BB, InheritScopeFrom->getDebugScope(), + B.getBuilderContext()) {} + /// Creates a new SILBuilder with an insertion point at the /// beginning of BB and the debug scope from the first /// non-metainstruction in the BB. diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index eb3ba43d2bcb8..ff751f61d20c2 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -128,14 +128,15 @@ class SILCloner : protected SILInstructionVisitor { /// blocks. /// /// This is used to clone an entire function and should not mutate the - /// original function. + /// original function except if \p replaceOriginalFunctionInPlace is true. /// /// entryArgs must have a SILValue from the cloned function corresponding to /// each argument in the original function `F`. /// /// Cloned instructions are inserted starting at the end of clonedEntryBB. void cloneFunctionBody(SILFunction *F, SILBasicBlock *clonedEntryBB, - ArrayRef entryArgs); + ArrayRef entryArgs, + bool replaceOriginalFunctionInPlace = false); /// MARK: Callback utilities used from CRTP extensions during cloning. /// These should only be called from within an instruction cloning visitor. @@ -354,8 +355,14 @@ class SILCloner : protected SILInstructionVisitor { SILLocation remapLocation(SILLocation Loc) { return Loc; } const SILDebugScope *remapScope(const SILDebugScope *DS) { return DS; } - SILType remapType(SILType Ty) { return Ty; } - CanType remapASTType(CanType Ty) { return Ty; } + SILType remapType(SILType Ty) { + return Ty; + } + + CanType remapASTType(CanType Ty) { + return Ty; + } + ProtocolConformanceRef remapConformance(Type Ty, ProtocolConformanceRef C) { return C; } @@ -612,9 +619,11 @@ void SILCloner::cloneReachableBlocks( template void SILCloner::cloneFunctionBody(SILFunction *F, SILBasicBlock *clonedEntryBB, - ArrayRef entryArgs) { + ArrayRef entryArgs, + bool replaceOriginalFunctionInPlace) { - assert(F != clonedEntryBB->getParent() && "Must clone into a new function."); + assert((replaceOriginalFunctionInPlace || F != clonedEntryBB->getParent()) && + "Must clone into a new function."); assert(BBMap.empty() && "This API does not allow clients to map blocks."); assert(ValueMap.empty() && "Stale ValueMap."); @@ -637,7 +646,7 @@ void SILCloner::clonePhiArgs(SILBasicBlock *oldBB) { auto *mappedBB = BBMap[oldBB]; // Create new arguments for each of the original block's arguments. - for (auto *Arg : oldBB->getPhiArguments()) { + for (auto *Arg : oldBB->getSILPhiArguments()) { SILValue mappedArg = mappedBB->createPhiArgument( getOpType(Arg->getType()), Arg->getOwnershipKind()); @@ -972,9 +981,9 @@ SILCloner::visitFunctionRefInst(FunctionRefInst *Inst) { getOpLocation(Inst->getLoc()), OpFunction)); } -template -void -SILCloner::visitDynamicFunctionRefInst(DynamicFunctionRefInst *Inst) { +template +void SILCloner::visitDynamicFunctionRefInst( + DynamicFunctionRefInst *Inst) { SILFunction *OpFunction = getOpFunction(Inst->getInitiallyReferencedFunction()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); @@ -1325,10 +1334,10 @@ SILCloner::visitDebugValueAddrInst(DebugValueAddrInst *Inst) { getOpType(Inst->getType()))); \ } \ template \ - void SILCloner::visitCopy##Name##ValueInst( \ - Copy##Name##ValueInst *Inst) { \ + void SILCloner::visitStrongCopy##Name##ValueInst( \ + StrongCopy##Name##ValueInst *Inst) { \ getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); \ - recordClonedInstruction(Inst, getBuilder().createCopy##Name##Value( \ + recordClonedInstruction(Inst, getBuilder().createStrongCopy##Name##Value( \ getOpLocation(Inst->getLoc()), \ getOpValue(Inst->getOperand()))); \ } @@ -1478,8 +1487,8 @@ visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *Inst) { SILLocation OpLoc = getOpLocation(Inst->getLoc()); SILValue SrcValue = getOpValue(Inst->getSrc()); SILValue DestValue = getOpValue(Inst->getDest()); - CanType SrcType = getOpASTType(Inst->getSourceType()); - CanType TargetType = getOpASTType(Inst->getTargetType()); + CanType SrcType = getOpASTType(Inst->getSourceFormalType()); + CanType TargetType = getOpASTType(Inst->getTargetFormalType()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createUncheckedRefCastAddr(OpLoc, SrcValue, SrcType, @@ -1620,10 +1629,12 @@ SILCloner::visitUnconditionalCheckedCastInst( UnconditionalCheckedCastInst *Inst) { SILLocation OpLoc = getOpLocation(Inst->getLoc()); SILValue OpValue = getOpValue(Inst->getOperand()); - SILType OpType = getOpType(Inst->getType()); + SILType OpLoweredType = getOpType(Inst->getTargetLoweredType()); + CanType OpFormalType = getOpASTType(Inst->getTargetFormalType()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction(Inst, getBuilder().createUnconditionalCheckedCast( - OpLoc, OpValue, OpType)); + OpLoc, OpValue, + OpLoweredType, OpFormalType)); } template @@ -1633,8 +1644,8 @@ SILCloner::visitUnconditionalCheckedCastAddrInst( SILLocation OpLoc = getOpLocation(Inst->getLoc()); SILValue SrcValue = getOpValue(Inst->getSrc()); SILValue DestValue = getOpValue(Inst->getDest()); - CanType SrcType = getOpASTType(Inst->getSourceType()); - CanType TargetType = getOpASTType(Inst->getTargetType()); + CanType SrcType = getOpASTType(Inst->getSourceFormalType()); + CanType TargetType = getOpASTType(Inst->getTargetFormalType()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction(Inst, getBuilder().createUnconditionalCheckedCastAddr( @@ -1646,11 +1657,17 @@ void SILCloner::visitUnconditionalCheckedCastValueInst( UnconditionalCheckedCastValueInst *Inst) { SILLocation OpLoc = getOpLocation(Inst->getLoc()); SILValue OpValue = getOpValue(Inst->getOperand()); - SILType OpType = getOpType(Inst->getType()); + CanType SrcFormalType = getOpASTType(Inst->getSourceFormalType()); + SILType OpLoweredType = getOpType(Inst->getTargetLoweredType()); + CanType OpFormalType = getOpASTType(Inst->getTargetFormalType()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, - getBuilder().createUnconditionalCheckedCastValue(OpLoc, OpValue, OpType)); + getBuilder().createUnconditionalCheckedCastValue(OpLoc, + OpValue, + SrcFormalType, + OpLoweredType, + OpFormalType)); } template @@ -2049,8 +2066,11 @@ SILCloner::visitWitnessMethodInst(WitnessMethodInst *Inst) { CanType Ty = conformance.getConcrete()->getType()->getCanonicalType(); if (Ty != newLookupType) { - assert(Ty->isExactSuperclassOf(newLookupType) && - "Should only create upcasts for sub class."); + assert( + (Ty->isExactSuperclassOf(newLookupType) || + getBuilder().getModule().Types.getLoweredRValueType( + getBuilder().getTypeExpansionContext(), Ty) == newLookupType) && + "Should only create upcasts for sub class."); // We use the super class as the new look up type. newLookupType = Ty; @@ -2315,8 +2335,8 @@ void SILCloner::visitUncheckedOwnershipConversionInst( ValueOwnershipKind Kind = SILValue(Inst).getOwnershipKind(); if (getOpValue(Inst->getOperand()).getOwnershipKind() == - ValueOwnershipKind::Any) { - Kind = ValueOwnershipKind::Any; + ValueOwnershipKind::None) { + Kind = ValueOwnershipKind::None; } recordClonedInstruction(Inst, getBuilder().createUncheckedOwnershipConversion( getOpLocation(Inst->getLoc()), @@ -2578,7 +2598,9 @@ SILCloner::visitCheckedCastBranchInst(CheckedCastBranchInst *Inst) { recordClonedInstruction( Inst, getBuilder().createCheckedCastBranch( getOpLocation(Inst->getLoc()), Inst->isExact(), - getOpValue(Inst->getOperand()), getOpType(Inst->getCastType()), + getOpValue(Inst->getOperand()), + getOpType(Inst->getTargetLoweredType()), + getOpASTType(Inst->getTargetFormalType()), OpSuccBB, OpFailBB, TrueCount, FalseCount)); } @@ -2590,8 +2612,12 @@ void SILCloner::visitCheckedCastValueBranchInst( getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createCheckedCastValueBranch( - getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - getOpType(Inst->getCastType()), OpSuccBB, OpFailBB)); + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()), + getOpASTType(Inst->getSourceFormalType()), + getOpType(Inst->getTargetLoweredType()), + getOpASTType(Inst->getTargetFormalType()), + OpSuccBB, OpFailBB)); } template @@ -2601,8 +2627,8 @@ void SILCloner::visitCheckedCastAddrBranchInst( SILBasicBlock *OpFailBB = getOpBasicBlock(Inst->getFailureBB()); SILValue SrcValue = getOpValue(Inst->getSrc()); SILValue DestValue = getOpValue(Inst->getDest()); - CanType SrcType = getOpASTType(Inst->getSourceType()); - CanType TargetType = getOpASTType(Inst->getTargetType()); + CanType SrcType = getOpASTType(Inst->getSourceFormalType()); + CanType TargetType = getOpASTType(Inst->getTargetFormalType()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); auto TrueCount = Inst->getTrueBBCount(); auto FalseCount = Inst->getFalseBBCount(); diff --git a/include/swift/SIL/SILConstants.h b/include/swift/SIL/SILConstants.h index 1de41b8805856..c360726bd1ecb 100644 --- a/include/swift/SIL/SILConstants.h +++ b/include/swift/SIL/SILConstants.h @@ -18,21 +18,24 @@ #ifndef SWIFT_SIL_CONSTANTS_H #define SWIFT_SIL_CONSTANTS_H +#include "swift/AST/SubstitutionMap.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" #include "llvm/Support/CommandLine.h" - namespace swift { class SingleValueInstruction; class SILValue; class SILBuilder; class SerializedSILLoader; +struct AggregateSymbolicValue; struct SymbolicArrayStorage; struct DerivedAddressValue; struct EnumWithPayloadSymbolicValue; struct SymbolicValueMemoryObject; struct UnknownSymbolicValue; +struct SymbolicClosure; extern llvm::cl::opt ConstExprLimit; @@ -106,6 +109,9 @@ class UnknownReason { /// Encountered an instruction not supported by the interpreter. UnsupportedInstruction, + /// Encountered a function call whose arguments are not constants. + CallArgumentUnknown, + /// Encountered a function call where the body of the called function is /// not available. CalleeImplementationUnknown, @@ -114,6 +120,10 @@ class UnknownReason { /// the interpreter. UntrackedSILValue, + /// Encountered a checked cast operation whose result cannot be evaluated + /// to a constant. + UnknownCastResult, + /// Attempted to find a concrete protocol conformance for a witness method /// and failed. UnknownWitnessMethodConformance, @@ -144,8 +154,9 @@ class UnknownReason { // Auxiliary information for different unknown kinds. union { - SILFunction *function; - const char *trapMessage; + SILFunction *function; // For CalleeImplementationUnknown + const char *trapMessage; // For Trap. + unsigned argumentIndex; // For CallArgumentUnknown } payload; public: @@ -155,6 +166,7 @@ class UnknownReason { switch (kind) { case UnknownKind::CalleeImplementationUnknown: case UnknownKind::Trap: + case UnknownKind::CallArgumentUnknown: return true; default: return false; @@ -199,6 +211,18 @@ class UnknownReason { assert(kind == UnknownKind::Trap); return payload.trapMessage; } + + static UnknownReason createCallArgumentUnknown(unsigned argIndex) { + UnknownReason reason; + reason.kind = UnknownKind::CallArgumentUnknown; + reason.payload.argumentIndex = argIndex; + return reason; + } + + unsigned getArgumentIndex() { + assert(kind == UnknownKind::CallArgumentUnknown); + return payload.argumentIndex; + } }; /// This is the symbolic value tracked for each SILValue in a scope. We @@ -261,6 +285,9 @@ class SymbolicValue { /// This represents an array. RK_Array, + + /// This represents a closure. + RK_Closure, }; union { @@ -286,8 +313,8 @@ class SymbolicValue { const char *string; /// When this SymbolicValue is of "Aggregate" kind, this pointer stores - /// information about the array elements and count. - const SymbolicValue *aggregate; + /// information about the aggregate elements, its type and count. + const AggregateSymbolicValue *aggregate; /// When this SymbolicValue is of "Enum" kind, this pointer stores /// information about the enum case type. @@ -305,7 +332,7 @@ class SymbolicValue { /// information about the memory object and access path of the access. DerivedAddressValue *derivedAddress; - // The following fields are for representing an Array. + // The following two fields are for representing an Array. // // In Swift, an array is a non-trivial struct that stores a reference to an // internal storage: _ContiguousArrayStorage. Though arrays have value @@ -329,6 +356,10 @@ class SymbolicValue { /// When this symbolic value is of an "Array" kind, this stores a memory /// object that contains a SymbolicArrayStorage value. SymbolicValueMemoryObject *array; + + /// When this symbolic value is of "Closure" kind, store a pointer to the + /// symbolic representation of the closure. + SymbolicClosure *closure; } value; RepresentationKind representationKind : 8; @@ -340,9 +371,6 @@ class SymbolicValue { /// This is the number of bytes for an RK_String representation. unsigned stringNumBytes; - - /// This is the number of elements for an RK_Aggregate representation. - unsigned aggregateNumElements; } auxInfo; public: @@ -384,6 +412,9 @@ class SymbolicValue { /// This represents an array value. Array, + /// This represents a closure. + Closure, + /// These values are generally only seen internally to the system, external /// clients shouldn't have to deal with them. UninitMemory @@ -460,11 +491,15 @@ class SymbolicValue { StringRef getStringValue() const; /// This returns an aggregate value with the specified elements in it. This - /// copies the elements into the specified Allocator. - static SymbolicValue getAggregate(ArrayRef elements, + /// copies the member values into the specified Allocator. + static SymbolicValue getAggregate(ArrayRef members, + Type aggregateType, SymbolicValueAllocator &allocator); - ArrayRef getAggregateValue() const; + ArrayRef getAggregateMembers() const; + + /// Return the type of this aggregate symbolic value. + Type getAggregateType() const; /// This returns a constant Symbolic value for the enum case in `decl`, which /// must not have an associated value. @@ -533,6 +568,23 @@ class SymbolicValue { /// Return the type of this array symbolic value. Type getArrayType() const; + /// Create and return a symbolic value that represents a closure. + /// \param target SILFunction corresponding the target of the closure. + /// \param capturedArguments an array consisting of SILValues of captured + /// arguments along with their symbolic values when available. + /// \param allocator the allocator to use for storing the contents of this + /// symbolic value. + static SymbolicValue makeClosure( + SILFunction *target, + ArrayRef>> capturedArguments, + SubstitutionMap substMap, SingleValueInstruction *closureInst, + SymbolicValueAllocator &allocator); + + SymbolicClosure *getClosure() const { + assert(getKind() == Closure); + return value.closure; + } + //===--------------------------------------------------------------------===// // Helpers @@ -558,8 +610,8 @@ class SymbolicValue { static_assert(sizeof(SymbolicValue) == 2 * sizeof(uint64_t), "SymbolicValue should stay small"); -static_assert(std::is_pod::value, - "SymbolicValue should stay POD"); +static_assert(std::is_trivial::value, + "SymbolicValue should stay trivial"); inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, SymbolicValue val) { val.print(os); @@ -573,7 +625,12 @@ struct SymbolicValueMemoryObject { Type getType() const { return type; } SymbolicValue getValue() const { return value; } - void setValue(SymbolicValue newValue) { value = newValue; } + void setValue(SymbolicValue newValue) { + assert((newValue.getKind() != SymbolicValue::Aggregate || + newValue.getAggregateType()->isEqual(type)) && + "Memory object type does not match the type of the symbolic value"); + value = newValue; + } /// Create a new memory object whose overall type is as specified. static SymbolicValueMemoryObject *create(Type type, SymbolicValue value, @@ -607,6 +664,77 @@ struct SymbolicValueMemoryObject { SymbolicValueMemoryObject(const SymbolicValueMemoryObject &) = delete; void operator=(const SymbolicValueMemoryObject &) = delete; }; + +using SymbolicClosureArgument = std::pair>; + +/// Representation of a symbolic closure. A symbolic closure consists of a +/// SILFunction and an array of SIL values, corresponding to the captured +/// arguments, and (optional) symbolic values representing the constant values +/// of the captured arguments. The symbolic values are optional as it is not +/// necessary for every captured argument to be a constant, which enables +/// representing closures whose captured arguments are not compile-time +/// constants. +struct SymbolicClosure final + : private llvm::TrailingObjects { + + friend class llvm::TrailingObjects; + +private: + + SILFunction *target; + + // The number of SIL values captured by the closure. + unsigned numCaptures; + + // True iff there exists a captured argument whose constant value is not + // known. + bool hasNonConstantCaptures = true; + + // A substitution map that partially maps the generic paramters of the + // applied function to the generic arguments of passed to the call. + SubstitutionMap substitutionMap; + + // The closure instruction such as partial apply that resulted in this + // symbolic value. This is tracked to obtain SILType and other SIL-level + // information of the symbolic closure. + SingleValueInstruction *closureInst; + + SymbolicClosure() = delete; + SymbolicClosure(const SymbolicClosure &) = delete; + SymbolicClosure(SILFunction *callee, unsigned numArguments, + SubstitutionMap substMap, SingleValueInstruction *inst, + bool nonConstantCaptures) + : target(callee), numCaptures(numArguments), + hasNonConstantCaptures(nonConstantCaptures), substitutionMap(substMap), + closureInst(inst) {} + +public: + static SymbolicClosure *create(SILFunction *callee, + ArrayRef args, + SubstitutionMap substMap, + SingleValueInstruction *closureInst, + SymbolicValueAllocator &allocator); + + ArrayRef getCaptures() const { + return {getTrailingObjects(), numCaptures}; + } + + // This is used by the llvm::TrailingObjects base class. + size_t numTrailingObjects(OverloadToken) const { + return numCaptures; + } + + SILFunction *getTarget() { + return target; + } + + SingleValueInstruction *getClosureInst() { return closureInst; } + + SILType getClosureType() { return closureInst->getType(); } + + SubstitutionMap getCallSubstitutionMap() { return substitutionMap; } +}; + } // end namespace swift #endif diff --git a/include/swift/SIL/SILDebugScope.h b/include/swift/SIL/SILDebugScope.h index 45b827d84778f..55a0a17c8f720 100644 --- a/include/swift/SIL/SILDebugScope.h +++ b/include/swift/SIL/SILDebugScope.h @@ -65,10 +65,10 @@ class SILDebugScope : public SILAllocated { SILFunction *getParentFunction() const; #ifndef NDEBUG - LLVM_ATTRIBUTE_DEPRECATED(void dump(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(), - unsigned Indent = 0) const, - "only for use in the debugger"); - LLVM_ATTRIBUTE_DEPRECATED(void dump(SILModule &Mod) const, "only for use in the debugger"); + SWIFT_DEBUG_DUMPER(dump(SourceManager &SM, + llvm::raw_ostream &OS = llvm::errs(), + unsigned Indent = 0)); + SWIFT_DEBUG_DUMPER(dump(SILModule &Mod)); #endif }; diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 2993761a62bbe..6ab70ebb84ac5 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -249,6 +249,11 @@ struct SILDeclRef { bool isStoredPropertyInitializer() const { return kind == Kind::StoredPropertyInitializer; } + /// True if the SILDeclRef references the initializer for the backing storage + /// of a property wrapper. + bool isPropertyWrapperBackingInitializer() const { + return kind == Kind::PropertyWrapperBackingInitializer; + } /// True if the SILDeclRef references the ivar initializer or deinitializer of /// a class. diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 3e8f92d571983..12087cac3151b 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -342,6 +342,14 @@ class SILFunction CanSILFunctionType getLoweredFunctionType() const { return LoweredType; } + CanSILFunctionType + getLoweredFunctionTypeInContext(TypeExpansionContext context) const; + + SILType getLoweredTypeInContext(TypeExpansionContext context) const { + return SILType::getPrimitiveObjectType( + getLoweredFunctionTypeInContext(context)); + } + SILFunctionConventions getConventions() const { return SILFunctionConventions(LoweredType, getModule()); } @@ -491,6 +499,11 @@ class SILFunction : ResilienceExpansion::Maximal); } + // Returns the type expansion context to be used inside this function. + TypeExpansionContext getTypeExpansionContext() const { + return TypeExpansionContext(*this); + } + const Lowering::TypeLowering & getTypeLowering(Lowering::AbstractionPattern orig, Type subst); @@ -502,6 +515,8 @@ class SILFunction SILType getLoweredLoadableType(Type t) const; + SILType getLoweredType(SILType t) const; + const Lowering::TypeLowering &getTypeLowering(SILType type) const; bool isTypeABIAccessible(SILType type) const; @@ -848,6 +863,10 @@ class SILFunction validateSubclassScope(getClassSubclassScope(), isThunk(), Info); SpecializationInfo = Info; } + + /// If this function is a specialization, return the original function from + /// which this function was specialized. + const SILFunction *getOriginOfSpecialization() const; /// Retrieve the generic environment containing the mapping from interface /// types to context archetypes for this function. Only present if the diff --git a/include/swift/SIL/SILFunctionConventions.h b/include/swift/SIL/SILFunctionConventions.h index b4014e418ab6e..7d55a08b9177d 100644 --- a/include/swift/SIL/SILFunctionConventions.h +++ b/include/swift/SIL/SILFunctionConventions.h @@ -37,6 +37,11 @@ namespace swift { +template class T, typename...Args> +struct delay_template_expansion { + using type = T; +}; + /// Transient wrapper for SILParameterInfo and SILResultInfo conventions. This /// abstraction helps handle the transition from canonical SIL conventions to /// lowered SIL conventions. @@ -45,37 +50,50 @@ class SILModuleConventions { friend SILResultInfo; friend SILFunctionConventions; - static bool isIndirectSILParam(SILParameterInfo param, bool loweredAddresses); + static bool isIndirectSILParam(SILParameterInfo param, + bool loweredAddresses); - static bool isIndirectSILYield(SILYieldInfo yield, bool loweredAddresses); + static bool isIndirectSILYield(SILYieldInfo yield, + bool loweredAddresses); - static bool isIndirectSILResult(SILResultInfo result, bool loweredAddresses); + static bool isIndirectSILResult(SILResultInfo result, + bool loweredAddresses); - static SILType getSILParamType(SILParameterInfo param, bool loweredAddresses); + static SILType getSILParamInterfaceType( + SILParameterInfo yield, + bool loweredAddresses); - static SILType getSILYieldType(SILYieldInfo yield, bool loweredAddresses); + static SILType getSILYieldInterfaceType( + SILYieldInfo yield, + bool loweredAddresses); - static SILType getSILResultType(SILResultInfo param, bool loweredAddresses); + static SILType getSILResultInterfaceType( + SILResultInfo param, + bool loweredAddresses); public: static bool isPassedIndirectlyInSIL(SILType type, SILModule &M); static bool isReturnedIndirectlyInSIL(SILType type, SILModule &M); - static SILModuleConventions getLoweredAddressConventions() { - return SILModuleConventions(true); + static SILModuleConventions getLoweredAddressConventions(SILModule &M) { + return SILModuleConventions(M, true); } private: + SILModule *M; bool loweredAddresses; - - SILModuleConventions(bool loweredAddresses) - : loweredAddresses(loweredAddresses) {} - + + SILModuleConventions(SILModule &M, bool loweredAddresses) + : M(&M), loweredAddresses(loweredAddresses) + {} + public: - SILModuleConventions(const SILModule &M); + SILModuleConventions(SILModule &M); SILFunctionConventions getFunctionConventions(CanSILFunctionType funcTy); + + SILModule &getModule() const { return *M; } bool useLoweredAddresses() const { return loweredAddresses; } @@ -91,16 +109,31 @@ class SILModuleConventions { return isIndirectSILResult(result, loweredAddresses); } - SILType getSILType(SILParameterInfo param) const { - return getSILParamType(param, loweredAddresses); + SILType getSILType(SILParameterInfo param, + CanSILFunctionType funcTy) const { + auto interfaceTy = getSILParamInterfaceType(param, loweredAddresses); + // TODO: Always require a function type + if (funcTy) + return funcTy->substInterfaceType(*M, interfaceTy); + return interfaceTy; } - SILType getSILType(SILYieldInfo yield) const { - return getSILYieldType(yield, loweredAddresses); + SILType getSILType(SILYieldInfo yield, + CanSILFunctionType funcTy) const { + auto interfaceTy = getSILYieldInterfaceType(yield, loweredAddresses); + // TODO: Always require a function type + if (funcTy) + return funcTy->substInterfaceType(*M, interfaceTy); + return interfaceTy; } - SILType getSILType(SILResultInfo result) const { - return getSILResultType(result, loweredAddresses); + SILType getSILType(SILResultInfo result, + CanSILFunctionType funcTy) const { + auto interfaceTy = getSILResultInterfaceType(result, loweredAddresses); + // TODO: Always require a function type + if (funcTy) + return funcTy->substInterfaceType(*M, interfaceTy); + return interfaceTy; } }; @@ -138,15 +171,15 @@ class SILFunctionConventions { } SILType getSILType(SILParameterInfo param) const { - return silConv.getSILType(param); + return silConv.getSILType(param, funcTy); } SILType getSILType(SILYieldInfo yield) const { - return silConv.getSILType(yield); + return silConv.getSILType(yield, funcTy); } SILType getSILType(SILResultInfo result) const { - return silConv.getSILType(result); + return silConv.getSILType(result, funcTy); } //===--------------------------------------------------------------------===// @@ -157,9 +190,9 @@ class SILFunctionConventions { /// This does not include indirect SIL results. SILType getSILResultType() { if (silConv.loweredAddresses) - return funcTy->getDirectFormalResultsType(); + return funcTy->getDirectFormalResultsType(silConv.getModule()); - return funcTy->getAllResultsType(); + return funcTy->getAllResultsSubstType(silConv.getModule()); } /// Get the SIL type for the single result which may be direct or indirect. @@ -198,24 +231,20 @@ class SILFunctionConventions { SILFunctionType::IndirectFormalResultFilter()); } - struct SILResultTypeFunc { - SILModuleConventions silConv; - SILResultTypeFunc(SILModuleConventions silConv) : silConv(silConv) {} - - SILType operator()(SILResultInfo result) const { - return silConv.getSILType(result); - } - }; + struct SILResultTypeFunc; - using IndirectSILResultTypeIter = - llvm::mapped_iterator; - using IndirectSILResultTypeRange = iterator_range; + // Gratuitous template parameter is to delay instantiating `mapped_iterator` + // on the incomplete type SILParameterTypeFunc. + template + using IndirectSILResultTypeIter = typename delay_template_expansion<_, + llvm::mapped_iterator, IndirectSILResultIter, SILResultTypeFunc>::type; + template + using IndirectSILResultTypeRange = iterator_range>; /// Return a range of SILTypes for each result passed as an address-typed SIL /// argument. - IndirectSILResultTypeRange getIndirectSILResultTypes() const { - return llvm::map_range(getIndirectSILResults(), SILResultTypeFunc(silConv)); - } + template + IndirectSILResultTypeRange<_> getIndirectSILResultTypes() const; /// Get the number of SIL results directly returned by SIL value. unsigned getNumDirectSILResults() const { @@ -242,15 +271,16 @@ class SILFunctionConventions { funcTy->getResults(), DirectSILResultFilter(silConv.loweredAddresses)); } - using DirectSILResultTypeIter = - llvm::mapped_iterator; - using DirectSILResultTypeRange = iterator_range; + template + using DirectSILResultTypeIter = typename delay_template_expansion<_, + llvm::mapped_iterator, DirectSILResultIter, SILResultTypeFunc>::type; + template + using DirectSILResultTypeRange = iterator_range>; /// Return a range of SILTypes for each result directly returned /// by SIL value. - DirectSILResultTypeRange getDirectSILResultTypes() const { - return llvm::map_range(getDirectSILResults(), SILResultTypeFunc(silConv)); - } + template + DirectSILResultTypeRange<_> getDirectSILResultTypes() const; //===--------------------------------------------------------------------===// // SIL parameters types. @@ -267,25 +297,22 @@ class SILFunctionConventions { return funcTy->getParameters(); } - struct SILParameterTypeFunc { - SILModuleConventions silConv; - SILParameterTypeFunc(SILModuleConventions silConv) : silConv(silConv) {} - - SILType operator()(SILParameterInfo param) const { - return silConv.getSILType(param); - } - }; - - using SILParameterTypeIter = - llvm::mapped_iterator; - using SILParameterTypeRange = iterator_range; + struct SILParameterTypeFunc; + + // Gratuitous template parameter is to delay instantiating `mapped_iterator` + // on the incomplete type SILParameterTypeFunc. + template + using SILParameterTypeIter = typename + delay_template_expansion<_, llvm::mapped_iterator, + const SILParameterInfo *, SILParameterTypeFunc>::type; + + template + using SILParameterTypeRange = iterator_range>; /// Return a range of SILTypes for each function parameter, not including /// indirect results. - SILParameterTypeRange getParameterSILTypes() const { - return llvm::map_range(funcTy->getParameters(), - SILParameterTypeFunc(silConv)); - } + template + SILParameterTypeRange<_> getParameterSILTypes() const; //===--------------------------------------------------------------------===// // SIL yield types. @@ -297,13 +324,15 @@ class SILFunctionConventions { return funcTy->getYields(); } - using SILYieldTypeIter = - llvm::mapped_iterator; - using SILYieldTypeRange = iterator_range; + template + using SILYieldTypeIter = typename + delay_template_expansion<_, llvm::mapped_iterator, + const SILYieldInfo *, SILParameterTypeFunc>::type; + template + using SILYieldTypeRange = iterator_range>; - SILYieldTypeRange getYieldSILTypes() const { - return llvm::map_range(funcTy->getYields(), SILParameterTypeFunc(silConv)); - } + template + SILYieldTypeRange<_> getYieldSILTypes() const; SILYieldInfo getYieldInfoForOperandIndex(unsigned opIndex) const { return getYields()[opIndex]; @@ -359,16 +388,65 @@ class SILFunctionConventions { // See SILArgument.h. /// Return the SIL type of the apply/entry argument at the given index. - SILType getSILArgumentType(unsigned index) const { - assert(index <= getNumSILArguments()); - if (index < getNumIndirectSILResults()) { - return *std::next(getIndirectSILResultTypes().begin(), index); - } - return getSILType( - funcTy->getParameters()[index - getNumIndirectSILResults()]); + SILType getSILArgumentType(unsigned index) const; +}; + +struct SILFunctionConventions::SILResultTypeFunc { + SILFunctionConventions silConv; + SILResultTypeFunc(const SILFunctionConventions &silConv) + : silConv(silConv) {} + + SILType operator()(SILResultInfo result) const { + return silConv.getSILType(result); + } +}; + +template +SILFunctionConventions::IndirectSILResultTypeRange<_> +SILFunctionConventions::getIndirectSILResultTypes() const { + return llvm::map_range(getIndirectSILResults(), SILResultTypeFunc(*this)); + } + +template +SILFunctionConventions::DirectSILResultTypeRange<_> +SILFunctionConventions::getDirectSILResultTypes() const { + return llvm::map_range(getDirectSILResults(), SILResultTypeFunc(*this)); +} + +struct SILFunctionConventions::SILParameterTypeFunc { + SILFunctionConventions silConv; + SILParameterTypeFunc(const SILFunctionConventions &silConv) + : silConv(silConv) {} + + SILType operator()(SILParameterInfo param) const { + return silConv.getSILType(param); } }; +template +SILFunctionConventions::SILParameterTypeRange<_> +SILFunctionConventions::getParameterSILTypes() const { + return llvm::map_range(funcTy->getParameters(), + SILParameterTypeFunc(*this)); +} + +template +SILFunctionConventions::SILYieldTypeRange<_> +SILFunctionConventions::getYieldSILTypes() const { + return llvm::map_range(funcTy->getYields(), + SILParameterTypeFunc(*this)); +} + +inline SILType +SILFunctionConventions::getSILArgumentType(unsigned index) const { + assert(index <= getNumSILArguments()); + if (index < getNumIndirectSILResults()) { + return *std::next(getIndirectSILResultTypes().begin(), index); + } + return getSILType( + funcTy->getParameters()[index - getNumIndirectSILResults()]); +} + inline SILFunctionConventions SILModuleConventions::getFunctionConventions(CanSILFunctionType funcTy) { return SILFunctionConventions(funcTy, *this); @@ -386,7 +464,7 @@ inline bool SILModuleConventions::isIndirectSILParam(SILParameterInfo param, case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_In_Guaranteed: return (loweredAddresses || - param.getType()->isOpenedExistentialWithError()); + param.getInterfaceType()->isOpenedExistentialWithError()); case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: return true; @@ -404,7 +482,7 @@ inline bool SILModuleConventions::isIndirectSILResult(SILResultInfo result, switch (result.getConvention()) { case ResultConvention::Indirect: return (loweredAddresses || - result.getType()->isOpenedExistentialWithError()); + result.getInterfaceType()->isOpenedExistentialWithError()); case ResultConvention::Owned: case ResultConvention::Unowned: case ResultConvention::UnownedInnerPointer: @@ -415,31 +493,48 @@ inline bool SILModuleConventions::isIndirectSILResult(SILResultInfo result, llvm_unreachable("Unhandled ResultConvention in switch."); } -inline SILType SILModuleConventions::getSILParamType(SILParameterInfo param, +inline SILType SILModuleConventions::getSILParamInterfaceType( + SILParameterInfo param, bool loweredAddresses) { - return SILModuleConventions::isIndirectSILParam(param, loweredAddresses) - ? SILType::getPrimitiveAddressType(param.getType()) - : SILType::getPrimitiveObjectType(param.getType()); + return SILModuleConventions::isIndirectSILParam(param,loweredAddresses) + ? SILType::getPrimitiveAddressType(param.getInterfaceType()) + : SILType::getPrimitiveObjectType(param.getInterfaceType()); } -inline SILType SILModuleConventions::getSILYieldType(SILYieldInfo yield, +inline SILType SILModuleConventions::getSILYieldInterfaceType( + SILYieldInfo yield, bool loweredAddresses) { - return getSILParamType(yield, loweredAddresses); + return getSILParamInterfaceType(yield, loweredAddresses); } -inline SILType SILModuleConventions::getSILResultType(SILResultInfo result, +inline SILType SILModuleConventions::getSILResultInterfaceType( + SILResultInfo result, bool loweredAddresses) { return SILModuleConventions::isIndirectSILResult(result, loweredAddresses) - ? SILType::getPrimitiveAddressType(result.getType()) - : SILType::getPrimitiveObjectType(result.getType()); + ? SILType::getPrimitiveAddressType(result.getInterfaceType()) + : SILType::getPrimitiveObjectType(result.getInterfaceType()); +} + +inline SILType +SILParameterInfo::getSILStorageInterfaceType() const { + return SILModuleConventions::getSILParamInterfaceType(*this, true); +} + +inline SILType +SILResultInfo::getSILStorageInterfaceType() const { + return SILModuleConventions::getSILResultInterfaceType(*this, true); } -inline SILType SILParameterInfo::getSILStorageType() const { - return SILModuleConventions::getSILParamType(*this, true); +inline SILType +SILParameterInfo::getSILStorageType(SILModule &M, + const SILFunctionType *funcTy) const { + return funcTy->substInterfaceType(M, getSILStorageInterfaceType()); } -inline SILType SILResultInfo::getSILStorageType() const { - return SILModuleConventions::getSILResultType(*this, true); +inline SILType +SILResultInfo::getSILStorageType(SILModule &M, + const SILFunctionType *funcTy) const { + return funcTy->substInterfaceType(M, getSILStorageInterfaceType()); } } // end swift namespace diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index eedc850458e3a..c40ba2c5766a1 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -105,7 +105,12 @@ class SILGlobalVariable CanSILFunctionType getLoweredFunctionType() const { return LoweredType.castTo(); } - + SILType getLoweredTypeInContext(TypeExpansionContext context) const; + CanSILFunctionType + getLoweredFunctionTypeInContext(TypeExpansionContext context) const { + return getLoweredTypeInContext(context).castTo(); + } + StringRef getName() const { return Name; } void setDeclaration(bool isD) { IsDeclaration = isD; } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 3aa4d6770bcad..4afcb22beb988 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -42,9 +42,11 @@ #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" #include "llvm/Support/TrailingObjects.h" +#include namespace swift { +class AllocationInst; class DeclRefExpr; class FloatLiteralExpr; class FuncDecl; @@ -317,7 +319,7 @@ class SILInstruction SILInstruction() = delete; void operator=(const SILInstruction &) = delete; - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; /// Check any special state of instructions that are not represented in the /// instructions operands/type. @@ -618,6 +620,15 @@ class SILInstruction return getMemoryBehavior() != MemoryBehavior::None; } + /// Return true if the instruction is "pure" in the sense that it may execute + /// multiple times without affecting behavior. This implies that it can be + /// trivially cloned at multiple use sites without preserving path + /// equivalence. + bool isPure() const { + return !mayReadOrWriteMemory() && !mayTrap() && !isa(this) + && !isa(this); + } + /// Returns true if the result of this instruction is a pointer to stack /// allocated memory. In this case there must be an adjacent deallocating /// instruction. @@ -789,7 +800,7 @@ class SingleValueInstruction : public SILInstruction, public ValueBase { SILModule &getModule() const { return SILInstruction::getModule(); } SILInstructionKind getKind() const { return SILInstruction::getKind(); } - void operator delete(void *Ptr, size_t) SWIFT_DELETE_OPERATOR_DELETED + void operator delete(void *Ptr, size_t) = delete; ValueKind getValueKind() const { return ValueBase::getKind(); @@ -842,11 +853,11 @@ inline SingleValueInstruction *SILNode::castToSingleValueInstruction() { inst->getKind() <= SILInstructionKind::Last_##ID; \ } -/// A single value inst that also forwards either owned or guaranteed ownership. +/// A single value inst that forwards a static ownership from one (or all) of +/// its operands. /// -/// The specific forwarded ownership is static since it is set upon -/// construction. After that point the instruction can not have a different form -/// of ownership. +/// The ownership kind is set on construction and afterwards must be changed +/// explicitly using setOwnershipKind(). class OwnershipForwardingSingleValueInst : public SingleValueInstruction { ValueOwnershipKind ownershipKind; @@ -940,7 +951,7 @@ class MultipleValueInstruction : public SILInstruction { : SILInstruction(kind, loc) {} public: - void operator delete(void *Ptr, size_t)SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *Ptr, size_t) = delete; MultipleValueInstruction *clone(SILInstruction *insertPt = nullptr) { return cast(SILInstruction::clone(insertPt)); @@ -1387,6 +1398,11 @@ class AllocationInst : public SingleValueInstruction { public: DEFINE_ABSTRACT_SINGLE_VALUE_INST_BOILERPLATE(AllocationInst) + + /// Return the underlying variable declaration associated with this + /// allocation, or null if this allocation inst is associated with a temporary + /// allocation. + VarDecl *getDecl() const; }; class DeallocStackInst; @@ -1429,10 +1445,6 @@ class AllocStackInst final void setDynamicLifetime() { dynamicLifetime = true; } bool hasDynamicLifetime() const { return dynamicLifetime; } - /// Return the underlying variable declaration associated with this - /// allocation, or null if this is a temporary allocation. - VarDecl *getDecl() const; - /// Return the debug variable information attached to this instruction. Optional getVarInfo() const { auto RawValue = SILInstruction::Bits.AllocStackInst.VarInfo; @@ -1675,10 +1687,6 @@ class AllocBoxInst final // Return the type of the memory stored in the alloc_box. SILType getAddressType() const; - /// Return the underlying variable declaration associated with this - /// allocation, or null if this is a temporary allocation. - VarDecl *getDecl() const; - /// Return the debug variable information attached to this instruction. Optional getVarInfo() const { return VarInfo.get(getDecl(), getTrailingObjects()); @@ -1928,7 +1936,7 @@ class ApplyInstBase : public Base { } bool isCalleeNoReturn() const { - return getSubstCalleeSILType().isNoReturnFunction(); + return getSubstCalleeSILType().isNoReturnFunction(this->getModule()); } bool isCalleeThin() const { @@ -2226,6 +2234,9 @@ class BeginApplyResult final : public MultipleValueInstructionResult { } }; +class EndApplyInst; +class AbortApplyInst; + /// BeginApplyInst - Represents the beginning of the full application of /// a yield_once coroutine (up until the coroutine yields a value back). class BeginApplyInst final @@ -2280,6 +2291,13 @@ class BeginApplyInst final bool isNonThrowing() const { return isNonThrowingApply(); } + + void getCoroutineEndPoints( + SmallVectorImpl &endApplyInsts, + SmallVectorImpl &abortApplyInsts) const; + + void getCoroutineEndPoints(SmallVectorImpl &endApplyInsts, + SmallVectorImpl &abortApplyInsts) const; }; inline BeginApplyInst *BeginApplyResult::getParent() { @@ -2347,7 +2365,7 @@ class FunctionRefBaseInst : public LiteralInst { protected: FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, - SILFunction *F); + SILFunction *F, TypeExpansionContext context); public: ~FunctionRefBaseInst(); @@ -2406,7 +2424,9 @@ class FunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -2424,7 +2444,9 @@ class DynamicFunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - DynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + DynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -2442,7 +2464,9 @@ class PreviousDynamicFunctionRefInst : public FunctionRefBaseInst { /// /// \param DebugLoc The location of the reference. /// \param F The function being referenced. - PreviousDynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + /// \param context The type expansion context of the function reference. + PreviousDynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F, + TypeExpansionContext context); public: static bool classof(const SILNode *node) { @@ -2869,6 +2893,10 @@ class KeyPathPatternComponent { return KeyPathPatternComponent(tupleIndex, ty); } + void visitReferencedFunctionsAndMethods( + std::function functionCallBack, + std::function methodCallBack) const; + void incrementRefCounts() const; void decrementRefCounts() const; @@ -2925,6 +2953,15 @@ class KeyPathPattern final ArrayRef getComponents() const; + void visitReferencedFunctionsAndMethods( + std::function functionCallBack, + std::function methodCallBack) { + for (auto &component : getComponents()) { + component.visitReferencedFunctionsAndMethods(functionCallBack, + methodCallBack); + } + } + static KeyPathPattern *get(SILModule &M, CanGenericSignature signature, CanType rootType, @@ -3107,14 +3144,16 @@ class GlobalAddrInst GlobalAccessInst> { friend SILBuilder; - GlobalAddrInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global); + GlobalAddrInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global, + TypeExpansionContext context); + public: // FIXME: This constructor should be private but is currently used // in the SILParser. /// Create a placeholder instruction with an unset global reference. GlobalAddrInst(SILDebugLocation DebugLoc, SILType Ty) - : InstructionBase(DebugLoc, Ty, nullptr) { } + : InstructionBase(DebugLoc, Ty, nullptr) {} }; /// Gives the value of a global variable. @@ -3126,7 +3165,8 @@ class GlobalValueInst GlobalAccessInst> { friend SILBuilder; - GlobalValueInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global); + GlobalValueInst(SILDebugLocation DebugLoc, SILGlobalVariable *Global, + TypeExpansionContext context); }; /// IntegerLiteralInst - Encapsulates an integer constant, as defined originally @@ -4447,11 +4487,11 @@ class UncheckedRefCastAddrInst SILValue getSrc() const { return Operands[Src].get(); } SILValue getDest() const { return Operands[Dest].get(); } - /// Returns the formal type of the source value. - CanType getSourceType() const { return SourceType; } + SILType getSourceLoweredType() const { return getSrc()->getType(); } + CanType getSourceFormalType() const { return SourceType; } - /// Returns the formal target type. - CanType getTargetType() const { return TargetType; } + SILType getTargetLoweredType() const { return getDest()->getType(); } + CanType getTargetFormalType() const { return TargetType; } ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } @@ -4723,31 +4763,28 @@ class UnconditionalCheckedCastInst final : public UnaryInstructionWithTypeDependentOperandsBase< SILInstructionKind::UnconditionalCheckedCastInst, UnconditionalCheckedCastInst, OwnershipForwardingConversionInst> { + CanType DestFormalTy; friend SILBuilder; UnconditionalCheckedCastInst(SILDebugLocation DebugLoc, SILValue Operand, ArrayRef TypeDependentOperands, - SILType DestTy) + SILType DestLoweredTy, CanType DestFormalTy) : UnaryInstructionWithTypeDependentOperandsBase( - DebugLoc, Operand, TypeDependentOperands, DestTy, - Operand.getOwnershipKind()) {} + DebugLoc, Operand, TypeDependentOperands, DestLoweredTy, + Operand.getOwnershipKind()), + DestFormalTy(DestFormalTy) {} static UnconditionalCheckedCastInst * - create(SILDebugLocation DebugLoc, SILValue Operand, SILType DestTy, + create(SILDebugLocation DebugLoc, SILValue Operand, + SILType DestLoweredTy, CanType DestFormalTy, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes); public: - /// Returns the formal type of the source value. - CanType getSourceType() const { - // This instruction is only used with types that allow this. - return getOperand()->getType().getASTType(); - } + SILType getSourceLoweredType() const { return getOperand()->getType(); } + CanType getSourceFormalType() const { return getSourceLoweredType().getASTType(); } - /// Returns the formal target type. - CanType getTargetType() const { - // This instruction is only used with types that allow this. - return getType().getASTType(); - } + CanType getTargetFormalType() const { return DestFormalTy; } + SILType getTargetLoweredType() const { return getType(); } }; /// Perform an unconditional checked cast that aborts if the cast fails. @@ -4775,11 +4812,11 @@ class UnconditionalCheckedCastAddrInst SILValue getSrc() const { return Operands[Src].get(); } SILValue getDest() const { return Operands[Dest].get(); } - /// Returns the formal type of the source value. - CanType getSourceType() const { return SourceType; } + SILType getSourceLoweredType() const { return getSrc()->getType(); } + CanType getSourceFormalType() const { return SourceType; } - /// Returns the formal target type. - CanType getTargetType() const { return TargetType; } + SILType getTargetLoweredType() const { return getDest()->getType(); } + CanType getTargetFormalType() const { return TargetType; } ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } @@ -4791,17 +4828,32 @@ class UnconditionalCheckedCastValueInst final : public UnaryInstructionWithTypeDependentOperandsBase< SILInstructionKind::UnconditionalCheckedCastValueInst, UnconditionalCheckedCastValueInst, ConversionInst> { + CanType SourceFormalTy; + CanType DestFormalTy; friend SILBuilder; - UnconditionalCheckedCastValueInst(SILDebugLocation DebugLoc, SILValue Operand, + UnconditionalCheckedCastValueInst(SILDebugLocation DebugLoc, + SILValue Operand, CanType SourceFormalTy, ArrayRef TypeDependentOperands, - SILType DestTy) + SILType DestLoweredTy, CanType DestFormalTy) : UnaryInstructionWithTypeDependentOperandsBase( - DebugLoc, Operand, TypeDependentOperands, DestTy) {} + DebugLoc, Operand, TypeDependentOperands, + DestLoweredTy), + SourceFormalTy(SourceFormalTy), + DestFormalTy(DestFormalTy) {} static UnconditionalCheckedCastValueInst * - create(SILDebugLocation DebugLoc, SILValue Operand, SILType DestTy, + create(SILDebugLocation DebugLoc, + SILValue Operand, CanType SourceFormalTy, + SILType DestLoweredTy, CanType DestFormalTy, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes); + +public: + SILType getSourceLoweredType() const { return getOperand()->getType(); } + CanType getSourceFormalType() const { return SourceFormalTy; } + + SILType getTargetLoweredType() const { return getType(); } + CanType getTargetFormalType() const { return DestFormalTy; } }; /// StructInst - Represents a constructed loadable struct. @@ -5070,7 +5122,7 @@ class ObjectInst final : public InstructionBaseWithTrailingOperands< : InstructionBaseWithTrailingOperands( Elements, DebugLoc, Ty, HasOwnership ? *mergeSILValueOwnership(Elements) - : ValueOwnershipKind(ValueOwnershipKind::Any)) { + : ValueOwnershipKind(ValueOwnershipKind::None)) { SILInstruction::Bits.ObjectInst.NumBaseElements = NumBaseElements; } @@ -5117,7 +5169,7 @@ class TupleInst final : public InstructionBaseWithTrailingOperands< : InstructionBaseWithTrailingOperands( Elems, DebugLoc, Ty, HasOwnership ? *mergeSILValueOwnership(Elems) - : ValueOwnershipKind(ValueOwnershipKind::Any)) {} + : ValueOwnershipKind(ValueOwnershipKind::None)) {} /// Construct a TupleInst. static TupleInst *create(SILDebugLocation DebugLoc, SILType Ty, @@ -5192,9 +5244,8 @@ class EnumInst : public InstructionBase getSingleTrueElement() const; }; -/// A select enum inst that produces a static OwnershipKind set upon the -/// instruction's construction. +/// A select enum inst that produces a static OwnershipKind. class OwnershipForwardingSelectEnumInstBase : public SelectEnumInstBase { ValueOwnershipKind ownershipKind; @@ -5488,7 +5538,7 @@ class SelectEnumInst final Operand, CaseValues, DebugLoc, Type, bool(DefaultValue), CaseCounts, DefaultCount, HasOwnership ? *mergeSILValueOwnership(CaseValues) - : ValueOwnershipKind(ValueOwnershipKind::Any)) { + : ValueOwnershipKind(ValueOwnershipKind::None)) { assert(CaseValues.size() - DefaultValue == CaseDecls.size()); std::uninitialized_copy(CaseDecls.begin(), CaseDecls.end(), getTrailingObjects()); @@ -6400,16 +6450,34 @@ class UncheckedOwnershipConversionInst /// the base must not be moved before any instructions which depend on the /// result of this instruction, exactly as if the address had been obviously /// derived from that operand (e.g. using ``ref_element_addr``). The result is -/// always equal to the first operand. +/// always equal to the first operand and thus forwards ownership through the +/// first operand. This is a "regular" use of the second operand (i.e. the +/// second operand must be live at the use point). +/// +/// Example: +/// +/// %base = ... +/// %value = ... @trivial value ... +/// %value_dependent_on_base = mark_dependence %value on %base +/// ... +/// use(%value_dependent_on_base) (1) +/// ... +/// destroy_value %base (2) +/// +/// (2) can never move before (1). In English this is a way for the compiler +/// writer to say to the optimizer: 'This subset of uses of "value" (the uses of +/// result) have a dependence on "base" being alive. Do not allow for things +/// that /may/ destroy base to be moved earlier than any of these uses of +/// "value"'. class MarkDependenceInst : public InstructionBase { + OwnershipForwardingSingleValueInst> { friend SILBuilder; FixedOperandList<2> Operands; MarkDependenceInst(SILDebugLocation DebugLoc, SILValue value, SILValue base) - : InstructionBase(DebugLoc, value->getType()), + : InstructionBase(DebugLoc, value->getType(), value.getOwnershipKind()), Operands{this, value, base} {} public: @@ -6481,22 +6549,24 @@ class CopyValueInst }; #define UNCHECKED_REF_STORAGE(Name, ...) \ - class Copy##Name##ValueInst \ - : public UnaryInstructionBase { \ + class StrongCopy##Name##ValueInst \ + : public UnaryInstructionBase< \ + SILInstructionKind::StrongCopy##Name##ValueInst, \ + SingleValueInstruction> { \ friend class SILBuilder; \ - Copy##Name##ValueInst(SILDebugLocation DebugLoc, SILValue operand, \ - SILType type) \ + StrongCopy##Name##ValueInst(SILDebugLocation DebugLoc, SILValue operand, \ + SILType type) \ : UnaryInstructionBase(DebugLoc, operand, type) {} \ }; #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - class Copy##Name##ValueInst \ - : public UnaryInstructionBase { \ + class StrongCopy##Name##ValueInst \ + : public UnaryInstructionBase< \ + SILInstructionKind::StrongCopy##Name##ValueInst, \ + SingleValueInstruction> { \ friend class SILBuilder; \ - Copy##Name##ValueInst(SILDebugLocation DebugLoc, SILValue operand, \ - SILType type) \ + StrongCopy##Name##ValueInst(SILDebugLocation DebugLoc, SILValue operand, \ + SILType type) \ : UnaryInstructionBase(DebugLoc, operand, type) {} \ }; #include "swift/AST/ReferenceStorage.def" @@ -6929,13 +6999,13 @@ class TermInst : public NonValueInstruction { }); } - using SuccessorBlockArgumentsListTy = - TransformRange>; + using SuccessorBlockArgumentListTy = + TransformRange( + const SILSuccessor &)>>; /// Return the range of Argument arrays for each successor of this /// block. - SuccessorBlockArgumentsListTy getSuccessorBlockArguments() const; + SuccessorBlockArgumentListTy getSuccessorBlockArgumentLists() const; using SuccessorBlockListTy = TransformRange { friend SILBuilder; - SILSuccessor DestBBs[2]; + std::array DestBBs; YieldInst(SILDebugLocation loc, ArrayRef yieldedValues, SILBasicBlock *normalBB, SILBasicBlock *unwindBB) : InstructionBaseWithTrailingOperands(yieldedValues, loc), - DestBBs{{this, normalBB}, {this, unwindBB}} {} + DestBBs{{{this, normalBB}, {this, unwindBB}}} {} static YieldInst *create(SILDebugLocation loc, ArrayRef yieldedValues, @@ -7184,7 +7254,8 @@ class CondBranchInst final FalseIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; + /// The number of arguments for the True branch. unsigned getNumTrueArgs() const { return SILInstruction::Bits.CondBranchInst.NumTrueArgs; @@ -7569,7 +7640,7 @@ class DynamicMethodBranchInst SILDeclRef Member; - SILSuccessor DestBBs[2]; + std::array DestBBs; /// The operand. FixedOperandList<1> Operands; @@ -7613,26 +7684,29 @@ class CheckedCastBranchInst final: TermInst> { friend SILBuilder; - SILType DestTy; + SILType DestLoweredTy; + CanType DestFormalTy; bool IsExact; - SILSuccessor DestBBs[2]; + std::array DestBBs; CheckedCastBranchInst(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, ArrayRef TypeDependentOperands, - SILType DestTy, SILBasicBlock *SuccessBB, - SILBasicBlock *FailureBB, ProfileCounter Target1Count, - ProfileCounter Target2Count) + SILType DestLoweredTy, CanType DestFormalTy, + SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, + ProfileCounter Target1Count, ProfileCounter Target2Count) : UnaryInstructionWithTypeDependentOperandsBase(DebugLoc, Operand, TypeDependentOperands), - DestTy(DestTy), - IsExact(IsExact), DestBBs{{this, SuccessBB, Target1Count}, - {this, FailureBB, Target2Count}} {} + DestLoweredTy(DestLoweredTy), + DestFormalTy(DestFormalTy), + IsExact(IsExact), DestBBs{{{this, SuccessBB, Target1Count}, + {this, FailureBB, Target2Count}}} {} static CheckedCastBranchInst * create(SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, - SILType DestTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, + SILType DestLoweredTy, CanType DestFormalTy, + SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, ProfileCounter Target1Count, ProfileCounter Target2Count); @@ -7643,19 +7717,11 @@ class CheckedCastBranchInst final: return DestBBs; } - /// Returns the formal type of the source value. - CanType getSourceType() const { - // This instruction is only used with types that allow this. - return getOperand()->getType().getASTType(); - } - - /// Returns the formal target type. - CanType getTargetType() const { - // This instruction is only used with types that allow this. - return getCastType().getASTType(); - } + SILType getSourceLoweredType() const { return getOperand()->getType(); } + CanType getSourceFormalType() const { return getSourceLoweredType().getASTType(); } - SILType getCastType() const { return DestTy; } + SILType getTargetLoweredType() const { return DestLoweredTy; } + CanType getTargetFormalType() const { return DestFormalTy; } SILBasicBlock *getSuccessBB() { return DestBBs[0]; } const SILBasicBlock *getSuccessBB() const { return DestBBs[0]; } @@ -7678,39 +7744,38 @@ class CheckedCastValueBranchInst final TermInst> { friend SILBuilder; - SILType DestTy; + CanType SourceFormalTy; + SILType DestLoweredTy; + CanType DestFormalTy; - SILSuccessor DestBBs[2]; + std::array DestBBs; - CheckedCastValueBranchInst(SILDebugLocation DebugLoc, SILValue Operand, + CheckedCastValueBranchInst(SILDebugLocation DebugLoc, + SILValue Operand, CanType SourceFormalTy, ArrayRef TypeDependentOperands, - SILType DestTy, SILBasicBlock *SuccessBB, - SILBasicBlock *FailureBB) + SILType DestLoweredTy, CanType DestFormalTy, + SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) : UnaryInstructionWithTypeDependentOperandsBase(DebugLoc, Operand, TypeDependentOperands), - DestTy(DestTy), DestBBs{{this, SuccessBB}, {this, FailureBB}} {} + SourceFormalTy(SourceFormalTy), + DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy), + DestBBs{{{this, SuccessBB}, {this, FailureBB}}} {} static CheckedCastValueBranchInst * - create(SILDebugLocation DebugLoc, SILValue Operand, SILType DestTy, - SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, SILFunction &F, - SILOpenedArchetypesState &OpenedArchetypes); + create(SILDebugLocation DebugLoc, + SILValue Operand, CanType SourceFormalTy, + SILType DestLoweredTy, CanType DestFormalTy, + SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, + SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes); public: SuccessorListTy getSuccessors() { return DestBBs; } - /// Returns the formal type of the source value. - CanType getSourceType() const { - // This instruction is only used with types that allow this. - return getOperand()->getType().getASTType(); - } - - /// Returns the formal target type. - CanType getTargetType() const { - // This instruction is only used with types that allow this. - return getCastType().getASTType(); - } + SILType getSourceLoweredType() const { return getOperand()->getType(); } + CanType getSourceFormalType() const { return SourceFormalTy; } - SILType getCastType() const { return DestTy; } + SILType getTargetLoweredType() const { return DestLoweredTy; } + CanType getTargetFormalType() const { return DestFormalTy; } SILBasicBlock *getSuccessBB() { return DestBBs[0]; } const SILBasicBlock *getSuccessBB() const { return DestBBs[0]; } @@ -7728,7 +7793,7 @@ class CheckedCastAddrBranchInst CastConsumptionKind ConsumptionKind; FixedOperandList<2> Operands; - SILSuccessor DestBBs[2]; + std::array DestBBs; CanType SourceType; CanType TargetType; @@ -7740,8 +7805,8 @@ class CheckedCastAddrBranchInst ProfileCounter Target1Count, ProfileCounter Target2Count) : InstructionBase(DebugLoc), ConsumptionKind(consumptionKind), - Operands{this, src, dest}, DestBBs{{this, successBB, Target1Count}, - {this, failureBB, Target2Count}}, + Operands{this, src, dest}, DestBBs{{{this, successBB, Target1Count}, + {this, failureBB, Target2Count}}}, SourceType(srcType), TargetType(targetType) { assert(ConsumptionKind != CastConsumptionKind::BorrowAlways && "BorrowAlways is not supported on addresses"); @@ -7760,11 +7825,11 @@ class CheckedCastAddrBranchInst SILValue getSrc() const { return Operands[Src].get(); } SILValue getDest() const { return Operands[Dest].get(); } - /// Returns the formal type of the source value. - CanType getSourceType() const { return SourceType; } + SILType getSourceLoweredType() const { return getSrc()->getType(); } + CanType getSourceFormalType() const { return SourceType; } - /// Returns the formal target type. - CanType getTargetType() const { return TargetType; } + SILType getTargetLoweredType() const { return getDest()->getType(); } + CanType getTargetFormalType() const { return TargetType; } ArrayRef getAllOperands() const { return Operands.asArray(); } MutableArrayRef getAllOperands() { return Operands.asArray(); } @@ -7793,7 +7858,7 @@ class TryApplyInstBase : public TermInst { ErrorIdx }; private: - SILSuccessor DestBBs[2]; + std::array DestBBs; protected: TryApplyInstBase(SILInstructionKind valueKind, SILDebugLocation Loc, diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 802bb85041b32..9b5f36b1280dd 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -166,10 +166,10 @@ class SILModule { /// Functions, which are dead (and not in the functions list anymore), /// but kept alive for debug info generation. FunctionListType zombieFunctions; - + /// Stores the names of zombie functions. llvm::BumpPtrAllocator zombieFunctionNames; - + /// Lookup table for SIL vtables from class decls. llvm::DenseMap VTableMap; @@ -194,6 +194,15 @@ class SILModule { /// The list of SILDefaultWitnessTables in the module. DefaultWitnessTableListType defaultWitnessTables; + /// Declarations which are externally visible. + /// + /// These are method declarations which are referenced from inlinable + /// functions due to cross-module-optimzation. Those declarations don't have + /// any attributes or linkage which mark them as externally visible by + /// default. + /// Currently this table is not serialized. + llvm::SetVector externallyVisible; + /// Lookup table for SIL Global Variables. llvm::StringMap GlobalVariableMap; @@ -202,7 +211,7 @@ class SILModule { // The map of SILCoverageMaps in the module. CoverageMapCollectionType coverageMaps; - + // The list of SILProperties in the module. PropertyListType properties; @@ -245,7 +254,7 @@ class SILModule { bool wholeModule; /// The options passed into this SILModule. - SILOptions &Options; + const SILOptions &Options; /// Set if the SILModule was serialized already. It is used /// to ensure that the module is serialized only once. @@ -261,7 +270,7 @@ class SILModule { // Intentionally marked private so that we need to use 'constructSIL()' // to construct a SILModule. SILModule(ModuleDecl *M, Lowering::TypeConverter &TC, - SILOptions &Options, const DeclContext *associatedDC, + const SILOptions &Options, const DeclContext *associatedDC, bool wholeModule); SILModule(const SILModule&) = delete; @@ -342,13 +351,13 @@ class SILModule { /// source file. static std::unique_ptr constructSIL(ModuleDecl *M, Lowering::TypeConverter &TC, - SILOptions &Options, FileUnit *sf = nullptr); + const SILOptions &Options, FileUnit *sf = nullptr); /// Create and return an empty SIL module that we can /// later parse SIL bodies directly into, without converting from an AST. static std::unique_ptr createEmptyModule(ModuleDecl *M, Lowering::TypeConverter &TC, - SILOptions &Options, + const SILOptions &Options, bool WholeModule = false); /// Get the Swift module associated with this SIL module. @@ -381,7 +390,7 @@ class SILModule { /// Returns true if it is the optimized OnoneSupport module. bool isOptimizedOnoneSupportModule() const; - SILOptions &getOptions() const { return Options; } + const SILOptions &getOptions() const { return Options; } using iterator = FunctionListType::iterator; using const_iterator = FunctionListType::const_iterator; @@ -446,6 +455,13 @@ class SILModule { return {defaultWitnessTables.begin(), defaultWitnessTables.end()}; } + void addExternallyVisibleDecl(ValueDecl *decl) { + externallyVisible.insert(decl); + } + bool isExternallyVisibleDecl(ValueDecl *decl) { + return externallyVisible.count(decl) != 0; + } + using sil_global_iterator = GlobalListType::iterator; using sil_global_const_iterator = GlobalListType::const_iterator; GlobalListType &getSILGlobalList() { return silGlobals; } @@ -486,7 +502,7 @@ class SILModule { PropertyListType &getPropertyList() { return properties; } const PropertyListType &getPropertyList() const { return properties; } - + /// Look for a global variable by name. /// /// \return null if this module has no such global variable @@ -510,6 +526,13 @@ class SILModule { /// succeeded, false otherwise. bool loadFunction(SILFunction *F); + /// Update the linkage of the SILFunction with the linkage of the serialized + /// function. + /// + /// The serialized SILLinkage can differ from the linkage derived from the + /// AST, e.g. cross-module-optimization can change the SIL linkages. + void updateFunctionLinkage(SILFunction *F); + /// Attempt to link the SILFunction. Returns true if linking succeeded, false /// otherwise. /// @@ -595,7 +618,7 @@ class SILModule { /// Can value operations (copies and destroys) on the given lowered type /// be performed in this module? bool isTypeABIAccessible(SILType type, - ResilienceExpansion forExpansion); + TypeExpansionContext forExpansion); /// Can type metadata for the given formal type be fetched in /// the given module? @@ -607,7 +630,7 @@ class SILModule { /// Pretty-print the module. void dump(bool Verbose = false) const; - + /// Pretty-print the module to a file. /// Useful for dumping the module when running in a debugger. /// Warning: no error handling is done. Fails with an assert if the file @@ -617,17 +640,17 @@ class SILModule { /// Pretty-print the module to the designated stream. /// - /// \param Verbose Dump SIL location information in verbose mode. /// \param M If present, the types and declarations from this module will be /// printed. The module would usually contain the types and Decls that /// the SIL module depends on. - /// \param ShouldSort If set to true sorts functions, vtables, sil global - /// variables, and witness tables by name to ease diffing. + /// \param Opts The SIL options, used to determine printing verbosity and + /// and sorting. /// \param PrintASTDecls If set to true print AST decls. - void print(raw_ostream &OS, bool Verbose = false, - ModuleDecl *M = nullptr, bool ShouldSort = false, + void print(raw_ostream& OS, + ModuleDecl *M = nullptr, + const SILOptions &Opts = SILOptions(), bool PrintASTDecls = true) const { - SILPrintContext PrintCtx(OS, Verbose, ShouldSort); + SILPrintContext PrintCtx(OS, Opts); print(PrintCtx, M, PrintASTDecls); } diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 593908e795cda..b49c8abea5331 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -563,10 +563,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) SINGLE_VALUE_INST(CopyValueInst, copy_value, SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) #define UNCHECKED_REF_STORAGE(Name, name, ...) \ - SINGLE_VALUE_INST(Copy##Name##ValueInst, copy_##name##_value, \ + SINGLE_VALUE_INST(StrongCopy##Name##ValueInst, strong_copy_##name##_value, \ SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ - SINGLE_VALUE_INST(Copy##Name##ValueInst, copy_##name##_value, \ + SINGLE_VALUE_INST(StrongCopy##Name##ValueInst, strong_copy_##name##_value, \ SingleValueInstruction, MayHaveSideEffects, DoesNotRelease) #include "swift/AST/ReferenceStorage.def" SINGLE_VALUE_INST(UncheckedOwnershipConversionInst, unchecked_ownership_conversion, diff --git a/include/swift/SIL/SILPrintContext.h b/include/swift/SIL/SILPrintContext.h index b213bdc2a05e6..f4d8a19860d5d 100644 --- a/include/swift/SIL/SILPrintContext.h +++ b/include/swift/SIL/SILPrintContext.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SIL_PRINTCONTEXT_H #define SWIFT_SIL_PRINTCONTEXT_H +#include "swift/AST/SILOptions.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILValue.h" #include "llvm/ADT/DenseMap.h" @@ -72,6 +73,11 @@ class SILPrintContext { SILPrintContext(llvm::raw_ostream &OS, bool Verbose = false, bool SortedSIL = false); + /// Constructor based on SILOptions. + /// + /// DebugInfo will be set according to the -sil-print-debuginfo option. + SILPrintContext(llvm::raw_ostream &OS, const SILOptions &Opts); + SILPrintContext(llvm::raw_ostream &OS, bool Verbose, bool SortedSIL, bool DebugInfo); diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index 249dfb1b6941b..0159b23b41ab6 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -242,9 +242,10 @@ class SILType { /// /// This is equivalent to, but possibly faster than, calling /// tc.getTypeLowering(type).isAddressOnly(). - static bool isAddressOnly(CanType type, Lowering::TypeConverter &tc, + static bool isAddressOnly(CanType type, + Lowering::TypeConverter &tc, CanGenericSignature sig, - ResilienceExpansion expansion); + TypeExpansionContext expansion); /// Return true if this type must be returned indirectly. /// @@ -253,7 +254,7 @@ class SILType { static bool isFormallyReturnedIndirectly(CanType type, Lowering::TypeConverter &tc, CanGenericSignature sig) { - return isAddressOnly(type, tc, sig, ResilienceExpansion::Minimal); + return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); } /// Return true if this type must be passed indirectly. @@ -263,7 +264,7 @@ class SILType { static bool isFormallyPassedIndirectly(CanType type, Lowering::TypeConverter &tc, CanGenericSignature sig) { - return isAddressOnly(type, tc, sig, ResilienceExpansion::Minimal); + return isAddressOnly(type, tc, sig, TypeExpansionContext::minimal()); } /// True if the type, or the referenced type of an address type, is loadable. @@ -281,19 +282,22 @@ class SILType { /// address-only. This is the opposite of isLoadable. bool isAddressOnly(const SILFunction &F) const; - /// True if the type, or the referenced type of an address type, is trivial, - /// meaning it is loadable and can be trivially copied, moved or detroyed. + /// True if the underlying AST type is trivial, meaning it is loadable and can + /// be trivially copied, moved or detroyed. Returns false for address types + /// even though they are technically trivial. bool isTrivial(const SILFunction &F) const; /// True if the type, or the referenced type of an address type, is known to - /// be a scalar reference-counted type. + /// be a scalar reference-counted type such as a class, box, or thick function + /// type. Returns false for non-trivial aggregates. bool isReferenceCounted(SILModule &M) const; /// Returns true if the referenced type is a function type that never /// returns. - bool isNoReturnFunction() const; + bool isNoReturnFunction(SILModule &M) const; - /// Returns true if the referenced type has reference semantics. + /// Returns true if the referenced AST type has reference semantics, even if + /// the lowered SIL type is known to be trivial. bool hasReferenceSemantics() const { return getASTType().hasReferenceSemantics(); } @@ -408,16 +412,20 @@ class SILType { /// the given field. Applies substitutions as necessary. The /// result will be an address type if the base type is an address /// type or a class. - SILType getFieldType(VarDecl *field, Lowering::TypeConverter &TC) const; + SILType getFieldType(VarDecl *field, Lowering::TypeConverter &TC, + TypeExpansionContext context) const; - SILType getFieldType(VarDecl *field, SILModule &M) const; + SILType getFieldType(VarDecl *field, SILModule &M, + TypeExpansionContext context) const; /// Given that this is an enum type, return the lowered type of the /// data for the given element. Applies substitutions as necessary. /// The result will have the same value category as the base type. - SILType getEnumElementType(EnumElementDecl *elt, Lowering::TypeConverter &TC) const; + SILType getEnumElementType(EnumElementDecl *elt, Lowering::TypeConverter &TC, + TypeExpansionContext context) const; - SILType getEnumElementType(EnumElementDecl *elt, SILModule &M) const; + SILType getEnumElementType(EnumElementDecl *elt, SILModule &M, + TypeExpansionContext context) const; /// Given that this is a tuple type, return the lowered type of the /// given tuple element. The result will have the same value @@ -455,11 +463,11 @@ class SILType { /// generic args with the appropriate item from the substitution. /// /// Only call this with function types! - SILType substGenericArgs(Lowering::TypeConverter &TC, - SubstitutionMap SubMap) const; + SILType substGenericArgs(Lowering::TypeConverter &TC, SubstitutionMap SubMap, + TypeExpansionContext context) const; - SILType substGenericArgs(SILModule &M, - SubstitutionMap SubMap) const; + SILType substGenericArgs(SILModule &M, SubstitutionMap SubMap, + TypeExpansionContext context) const; /// If the original type is generic, pass the signature as genericSig. /// @@ -484,8 +492,9 @@ class SILType { bool isHeapObjectReferenceType() const; /// Returns true if this SILType is an aggregate that contains \p Ty - bool aggregateContainsRecord(SILType Ty, SILModule &SILMod) const; - + bool aggregateContainsRecord(SILType Ty, SILModule &SILMod, + TypeExpansionContext context) const; + /// Returns true if this SILType is an aggregate with unreferenceable storage, /// meaning it cannot be fully destructured in SIL. bool aggregateHasUnreferenceableStorage() const; @@ -512,7 +521,8 @@ class SILType { /// Returns true if this SILType could be potentially a lowering of the given /// formal type. Meant for verification purposes/assertions. - bool isLoweringOf(SILModule &M, CanType formalType); + bool isLoweringOf(TypeExpansionContext context, SILModule &M, + CanType formalType); /// Returns the hash code for the SILType. llvm::hash_code getHashCode() const { @@ -586,12 +596,12 @@ NON_SIL_TYPE(LValue) #undef NON_SIL_TYPE CanSILFunctionType getNativeSILFunctionType( - Lowering::TypeConverter &TC, Lowering::AbstractionPattern origType, - CanAnyFunctionType substType, + Lowering::TypeConverter &TC, TypeExpansionContext context, + Lowering::AbstractionPattern origType, CanAnyFunctionType substType, Optional origConstant = None, Optional constant = None, Optional reqtSubs = None, - Optional witnessMethodConformance = None); + ProtocolConformanceRef witnessMethodConformance = ProtocolConformanceRef()); inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SILType T) { T.print(OS); @@ -614,15 +624,15 @@ inline SILType SILField::getObjectType() const { return SILType::getPrimitiveObjectType(getLoweredType()); } -CanType getSILBoxFieldLoweredType(SILBoxType *type, - Lowering::TypeConverter &TC, +CanType getSILBoxFieldLoweredType(TypeExpansionContext context, + SILBoxType *type, Lowering::TypeConverter &TC, unsigned index); -inline SILType getSILBoxFieldType(SILBoxType *type, - Lowering::TypeConverter &TC, +inline SILType getSILBoxFieldType(TypeExpansionContext context, + SILBoxType *type, Lowering::TypeConverter &TC, unsigned index) { return SILType::getPrimitiveAddressType( - getSILBoxFieldLoweredType(type, TC, index)); + getSILBoxFieldLoweredType(context, type, TC, index)); } } // end swift namespace diff --git a/include/swift/SIL/SILUndef.h b/include/swift/SIL/SILUndef.h index bb19f502e2255..94036ff791682 100644 --- a/include/swift/SIL/SILUndef.h +++ b/include/swift/SIL/SILUndef.h @@ -29,7 +29,7 @@ class SILUndef : public ValueBase { public: void operator=(const SILArgument &) = delete; - void operator delete(void *, size_t) SWIFT_DELETE_OPERATOR_DELETED; + void operator delete(void *, size_t) = delete; static SILUndef *get(SILType ty, SILModule &m, ValueOwnershipKind ownershipKind); static SILUndef *get(SILType ty, const SILFunction &f); @@ -38,7 +38,7 @@ class SILUndef : public ValueBase { static SILUndef *getSentinelValue(SILType type, OwnerTy owner) { // Ownership kind isn't used here, the value just needs to have a unique // address. - return new (*owner) SILUndef(type, ValueOwnershipKind::Any); + return new (*owner) SILUndef(type, ValueOwnershipKind::None); } ValueOwnershipKind getOwnershipKind() const { return ownershipKind; } diff --git a/include/swift/SIL/SILVTableVisitor.h b/include/swift/SIL/SILVTableVisitor.h index 3b32e971f3dc4..2972ac8a04dae 100644 --- a/include/swift/SIL/SILVTableVisitor.h +++ b/include/swift/SIL/SILVTableVisitor.h @@ -140,7 +140,7 @@ template class SILVTableVisitor { } void maybeAddMember(Decl *member) { - if (auto *ad = dyn_cast(member)) + if (isa(member)) /* handled as part of its storage */; else if (auto *fd = dyn_cast(member)) maybeAddMethod(fd); diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h index 1473444243bcd..7662ae50c29db 100644 --- a/include/swift/SIL/SILValue.h +++ b/include/swift/SIL/SILValue.h @@ -117,14 +117,14 @@ struct ValueOwnershipKind { /// instruction exactly once along any path through the program. Guaranteed, - /// A SILValue with Any ownership kind is an independent value outside of + /// A SILValue with None ownership kind is an independent value outside of /// the ownership system. It is used to model trivially typed values as well - /// as trivial cases of non-trivial enums. Naturally Any can be merged with + /// as trivial cases of non-trivial enums. Naturally None can be merged with /// any ValueOwnershipKind allowing us to naturally model merge and branch /// points in the SSA graph. - Any, + None, - LastValueOwnershipKind = Any, + LastValueOwnershipKind = None, } Value; using UnderlyingType = std::underlying_type::type; @@ -167,7 +167,7 @@ struct ValueOwnershipKind { /// kinds. UseLifetimeConstraint getForwardingLifetimeConstraint() const { switch (Value) { - case ValueOwnershipKind::Any: + case ValueOwnershipKind::None: case ValueOwnershipKind::Guaranteed: case ValueOwnershipKind::Unowned: return UseLifetimeConstraint::MustBeLive; @@ -181,14 +181,14 @@ struct ValueOwnershipKind { /// that the two ownership kinds are "compatibile". /// /// The reason why we do not compare directy is to allow for - /// ValueOwnershipKind::Any to merge into other forms of ValueOwnershipKind. + /// ValueOwnershipKind::None to merge into other forms of ValueOwnershipKind. bool isCompatibleWith(ValueOwnershipKind other) const { return merge(other).hasValue(); } template static Optional merge(RangeTy &&r) { - auto initial = Optional(ValueOwnershipKind::Any); + auto initial = Optional(ValueOwnershipKind::None); return accumulate( std::forward(r), initial, [](Optional acc, ValueOwnershipKind x) { @@ -449,7 +449,7 @@ struct OperandOwnershipKindMap { /// Return the OperandOwnershipKindMap that tests for compatibility with /// ValueOwnershipKind kind. This means that it will accept a element whose - /// ownership is ValueOwnershipKind::Any. + /// ownership is ValueOwnershipKind::None. static OperandOwnershipKindMap compatibilityMap(ValueOwnershipKind kind, UseLifetimeConstraint constraint) { OperandOwnershipKindMap set; @@ -519,7 +519,7 @@ struct OperandOwnershipKindMap { void addCompatibilityConstraint(ValueOwnershipKind kind, UseLifetimeConstraint constraint) { - add(ValueOwnershipKind::Any, UseLifetimeConstraint::MustBeLive); + add(ValueOwnershipKind::None, UseLifetimeConstraint::MustBeLive); add(kind, constraint); } @@ -532,7 +532,7 @@ struct OperandOwnershipKindMap { UseLifetimeConstraint getLifetimeConstraint(ValueOwnershipKind kind) const; void print(llvm::raw_ostream &os) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger"); + SWIFT_DEBUG_DUMP; }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, @@ -586,6 +586,9 @@ class Operand { Operand(const Operand &use) = delete; Operand &operator=(const Operand &use) = delete; + Operand(Operand &&) = default; + Operand &operator=(Operand &&) = default; + /// Return the current value being used by this operand. SILValue get() const { return TheValue; } diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 7bae63d5e3cb9..eeea1c847d8c6 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -15,6 +15,7 @@ #include "swift/ABI/MetadataValues.h" #include "swift/AST/CaptureInfo.h" +#include "swift/AST/Module.h" #include "swift/SIL/AbstractionPattern.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILInstruction.h" @@ -72,16 +73,16 @@ inline CanAnyFunctionType adjustFunctionType(CanAnyFunctionType t, CanSILFunctionType adjustFunctionType(CanSILFunctionType type, SILFunctionType::ExtInfo extInfo, ParameterConvention calleeConv, - Optional witnessMethodConformance); + ProtocolConformanceRef witnessMethodConformance); inline CanSILFunctionType adjustFunctionType(CanSILFunctionType type, SILFunctionType::ExtInfo extInfo, - Optional witnessMethodConformance) { + ProtocolConformanceRef witnessMethodConformance) { return adjustFunctionType(type, extInfo, type->getCalleeConvention(), witnessMethodConformance); } inline CanSILFunctionType adjustFunctionType(CanSILFunctionType t, SILFunctionType::Representation rep, - Optional witnessMethodConformance) { + ProtocolConformanceRef witnessMethodConformance) { if (t->getRepresentation() == rep) return t; auto extInfo = t->getExtInfo().withRepresentation(rep); auto contextConvention = DefaultThickCalleeConvention; @@ -92,13 +93,6 @@ adjustFunctionType(CanSILFunctionType t, SILFunctionType::Representation rep, witnessMethodConformance); } -/// Flag used to place context-dependent TypeLowerings in their own arena which -/// can be disposed when a generic context is exited. -enum IsDependent_t : unsigned { - IsNotDependent = false, - IsDependent = true -}; - /// Is a lowered SIL type trivial? That is, are copies ultimately just /// bit-copies, and it takes no work to destroy a value? enum IsTrivial_t : bool { @@ -181,6 +175,10 @@ class TypeLowering { (isFixedABI ? 0U : NonFixedABIFlag) | (isAddressOnly ? AddressOnlyFlag : 0U) | (isResilient ? ResilientFlag : 0U)) {} + + constexpr bool operator==(RecursiveProperties p) const { + return Flags == p.Flags; + } static constexpr RecursiveProperties forTrivial() { return {IsTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient}; @@ -227,11 +225,12 @@ class TypeLowering { SILType LoweredType; RecursiveProperties Properties; - unsigned ReferenceCounted : 1; /// The resilience expansion for this type lowering. /// If the type is not resilient at all, this is always Minimal. - unsigned ForExpansion : 1; + TypeExpansionContext expansionContext; + + unsigned ReferenceCounted : 1; /// A single linked list of lowerings for different resilience expansions. /// The first lowering is always for ResilientExpansion::Minimal. @@ -240,10 +239,9 @@ class TypeLowering { protected: TypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) - : LoweredType(type), Properties(properties), - ReferenceCounted(isRefCounted), - ForExpansion(unsigned(forExpansion)) {} + TypeExpansionContext expansionContext) + : LoweredType(type), Properties(properties), + expansionContext(expansionContext), ReferenceCounted(isRefCounted) {} public: TypeLowering(const TypeLowering &) = delete; @@ -255,28 +253,7 @@ class TypeLowering { void print(llvm::raw_ostream &os) const; /// Dump out the internal state of this type lowering to llvm::dbgs(). - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "Only for use in the debugger"); - - /// Are r-values of this type passed as arguments indirectly by formal - /// convention? - /// - /// This is independent of whether the SIL argument is address type. - bool isFormallyPassedIndirectly() const { - assert(!isResilient() || - getResilienceExpansion() == ResilienceExpansion::Minimal && - "calling convention uses minimal resilience expansion"); - return isAddressOnly(); - } - - /// Are r-values of this type returned indirectly by formal convention? - /// - /// This is independent of whether the SIL result is address type. - bool isFormallyReturnedIndirectly() const { - assert(!isResilient() || - getResilienceExpansion() == ResilienceExpansion::Minimal && - "calling convention uses minimal resilience expansion"); - return isAddressOnly(); - } + SWIFT_DEBUG_DUMP; RecursiveProperties getRecursiveProperties() const { return Properties; @@ -331,7 +308,11 @@ class TypeLowering { } ResilienceExpansion getResilienceExpansion() const { - return ResilienceExpansion(ForExpansion); + return expansionContext.getResilienceExpansion(); + } + + TypeExpansionContext getExpansionContext() const { + return expansionContext; } /// Produce an exact copy of the value in the given address as a @@ -518,10 +499,8 @@ class TypeLowering { } /// Allocate a new TypeLowering using the TypeConverter's allocator. - void *operator new(size_t size, TypeConverter &tc, - IsDependent_t dependent); - void *operator new[](size_t size, TypeConverter &tc, - IsDependent_t dependent); + void *operator new(size_t size, TypeConverter &tc); + void *operator new[](size_t size, TypeConverter &tc); // Forbid 'new FooTypeLowering' and try to forbid 'delete tl'. // The latter is made challenging because the existence of the @@ -589,18 +568,19 @@ enum class CaptureKind { class TypeConverter { friend class TypeLowering; - llvm::BumpPtrAllocator IndependentBPA; + llvm::BumpPtrAllocator TypeLoweringBPA; struct CachingTypeKey { CanGenericSignature Sig; AbstractionPattern::CachingKey OrigType; CanType SubstType; + TypeExpansionContext expansionContext; friend bool operator==(const CachingTypeKey &lhs, const CachingTypeKey &rhs) { - return lhs.Sig == rhs.Sig - && lhs.OrigType == rhs.OrigType - && lhs.SubstType == rhs.SubstType; + return lhs.Sig == rhs.Sig && lhs.OrigType == rhs.OrigType && + lhs.SubstType == rhs.SubstType && + lhs.expansionContext == rhs.expansionContext; } friend bool operator!=(const CachingTypeKey &lhs, const CachingTypeKey &rhs) { @@ -616,32 +596,34 @@ class TypeConverter { /// should be used in the lowered type. CanType SubstType; + TypeExpansionContext expansionContext; + CachingTypeKey getCachingKey() const { assert(isCacheable()); return { (OrigType.hasGenericSignature() ? OrigType.getGenericSignature() : nullptr), OrigType.getCachingKey(), - SubstType }; + SubstType, + expansionContext }; } bool isCacheable() const { return OrigType.hasCachingKey(); } - IsDependent_t isDependent() const { - if (SubstType->hasTypeParameter()) - return IsDependent; - return IsNotDependent; + TypeKey getKeyForMinimalExpansion() const { + return {OrigType, SubstType, TypeExpansionContext::minimal()}; } }; friend struct llvm::DenseMapInfo; - - TypeKey getTypeKey(AbstractionPattern origTy, CanType substTy) { - return {origTy, substTy}; + + TypeKey getTypeKey(AbstractionPattern origTy, CanType substTy, + TypeExpansionContext context) { + return {origTy, substTy, context}; } - + struct OverrideKey { SILDeclRef derived; SILDeclRef base; @@ -664,33 +646,23 @@ class TypeConverter { const TypeLowering *find(TypeKey k); /// Insert a mapping into the cache. void insert(TypeKey k, const TypeLowering *tl); - - /// Mapping for types independent on contextual generic parameters. - llvm::DenseMap IndependentTypes; - - struct DependentTypeState { - llvm::BumpPtrAllocator BPA; - CanGenericSignature Sig; - llvm::DenseMap Map; - - explicit DependentTypeState(CanGenericSignature sig) : Sig(sig) {} - - DependentTypeState(DependentTypeState &&) = default; +#ifndef NDEBUG + /// Remove the nullptr entry from the type map. + void removeNullEntry(TypeKey k); +#endif - // No copy constructor or assignment. - DependentTypeState(const DependentTypeState &) = delete; - void operator=(const DependentTypeState &) = delete; - }; + /// Mapping for types independent on contextual generic parameters. + llvm::DenseMap LoweredTypes; - llvm::SmallVector DependentTypes; + llvm::DenseMap, SILConstantInfo *> + ConstantTypes; - llvm::DenseMap ConstantTypes; - llvm::DenseMap ConstantOverrideTypes; llvm::DenseMap LoweredCaptures; + llvm::DenseMap opaqueArchetypeFields; + /// Cache of loadable SILType to number of (estimated) fields /// /// Second element is a ResilienceExpansion. @@ -704,13 +676,16 @@ class TypeConverter { #include "swift/SIL/BridgedTypes.def" const TypeLowering & - getTypeLoweringForLoweredType(TypeKey key, - ResilienceExpansion forExpansion); + getTypeLoweringForLoweredType(AbstractionPattern origType, + CanType loweredType, + TypeExpansionContext forExpansion, + bool origHadOpaqueTypeArchetype); const TypeLowering * getTypeLoweringForExpansion(TypeKey key, - ResilienceExpansion forExpansion, - const TypeLowering *lowering); + TypeExpansionContext forExpansion, + const TypeLowering *lowering, + bool origHadOpaqueTypeArchetype); public: ModuleDecl &M; @@ -723,7 +698,7 @@ class TypeConverter { /// Return the CaptureKind to use when capturing a decl. CaptureKind getDeclCaptureKind(CapturedValue capture, - ResilienceExpansion expansion); + TypeExpansionContext expansion); /// Return a most-general-possible abstraction pattern. AbstractionPattern getMostGeneralAbstraction() { @@ -748,7 +723,7 @@ class TypeConverter { static ProtocolDispatchStrategy getProtocolDispatchStrategy(ProtocolDecl *P); /// Count the total number of fields inside the given SIL Type - unsigned countNumberOfFields(SILType Ty, ResilienceExpansion expansion); + unsigned countNumberOfFields(SILType Ty, TypeExpansionContext expansion); /// True if a protocol uses witness tables for dynamic dispatch. static bool protocolRequiresWitnessTable(ProtocolDecl *P) { @@ -771,11 +746,14 @@ class TypeConverter { return isIndirectPlusZeroSelfParameter(T.getASTType()); } - /// Lowers a Swift type to a SILType, and returns the SIL TypeLowering + /// Lowers a context-independent Swift type to a SILType, and returns the SIL TypeLowering /// for that type. + /// + /// If `t` contains generic parameters, then the overload that also takes an + /// `AbstractionPattern` must be used. const TypeLowering & - getTypeLowering(Type t, ResilienceExpansion forExpansion) { - AbstractionPattern pattern(getCurGenericContext(), t->getCanonicalType()); + getTypeLowering(Type t, TypeExpansionContext forExpansion) { + AbstractionPattern pattern(t->getCanonicalType()); return getTypeLowering(pattern, t, forExpansion); } @@ -783,28 +761,38 @@ class TypeConverter { /// patterns of the given original type. const TypeLowering &getTypeLowering(AbstractionPattern origType, Type substType, - ResilienceExpansion forExpansion); + TypeExpansionContext forExpansion); /// Returns the SIL TypeLowering for an already lowered SILType. If the /// SILType is an address, returns the TypeLowering for the pointed-to /// type. + /// + /// If `t` contains type parameters, then the generic signature for its context + /// must be provided. + const TypeLowering & + getTypeLowering(SILType t, TypeExpansionContext forExpansion, + CanGenericSignature signature = nullptr); + + /// Returns the SIL TypeLowering for an already lowered SILType. If the + /// SILType is an address, returns the TypeLowering for the pointed-to + /// type in the context of the given SILFunction. const TypeLowering & - getTypeLowering(SILType t, ResilienceExpansion forExpansion); + getTypeLowering(SILType t, SILFunction &F); // Returns the lowered SIL type for a Swift type. - SILType getLoweredType(Type t, ResilienceExpansion forExpansion) { + SILType getLoweredType(Type t, TypeExpansionContext forExpansion) { return getTypeLowering(t, forExpansion).getLoweredType(); } // Returns the lowered SIL type for a Swift type. SILType getLoweredType(AbstractionPattern origType, Type substType, - ResilienceExpansion forExpansion) { + TypeExpansionContext forExpansion) { return getTypeLowering(origType, substType, forExpansion) .getLoweredType(); } SILType getLoweredLoadableType(Type t, - ResilienceExpansion forExpansion, + TypeExpansionContext forExpansion, SILModule &M) { const TypeLowering &ti = getTypeLowering(t, forExpansion); assert( @@ -813,17 +801,13 @@ class TypeConverter { return ti.getLoweredType(); } - CanType getLoweredRValueType(Type t) { - // We're ignoring the category (object vs address), so the resilience - // expansion does not matter. - return getLoweredType(t, ResilienceExpansion::Minimal).getASTType(); + CanType getLoweredRValueType(TypeExpansionContext context, Type t) { + return getLoweredType(t, context).getASTType(); } - CanType getLoweredRValueType(AbstractionPattern origType, Type substType) { - // We're ignoring the category (object vs address), so the resilience - // expansion does not matter. - return getLoweredType(origType, substType, - ResilienceExpansion::Minimal).getASTType(); + CanType getLoweredRValueType(TypeExpansionContext context, + AbstractionPattern origType, Type substType) { + return getLoweredType(origType, substType, context).getASTType(); } AbstractionPattern getAbstractionPattern(AbstractStorageDecl *storage, @@ -836,14 +820,18 @@ class TypeConverter { CanType getLoweredTypeOfGlobal(VarDecl *var); + bool hasOpaqueArchetypeOrPropertiesOrCases(CanType ty); + /// Return the SILFunctionType for a native function value of the /// given type. - CanSILFunctionType getSILFunctionType(AbstractionPattern origType, + CanSILFunctionType getSILFunctionType(TypeExpansionContext context, + AbstractionPattern origType, CanFunctionType substFnType); /// Returns the formal type, lowered AST type, and SILFunctionType /// for a constant reference. - const SILConstantInfo &getConstantInfo(SILDeclRef constant); + const SILConstantInfo &getConstantInfo(TypeExpansionContext context, + SILDeclRef constant); /// Get the generic environment for a constant. GenericSignature getConstantGenericSignature(SILDeclRef constant); @@ -852,26 +840,29 @@ class TypeConverter { GenericEnvironment *getConstantGenericEnvironment(SILDeclRef constant); /// Returns the SIL type of a constant reference. - SILType getConstantType(SILDeclRef constant) { - return getConstantInfo(constant).getSILType(); + SILType getConstantType(TypeExpansionContext context, SILDeclRef constant) { + return getConstantInfo(context, constant).getSILType(); } /// Returns the SILFunctionType for the given declaration. - CanSILFunctionType getConstantFunctionType(SILDeclRef constant) { - return getConstantInfo(constant).SILFnType; + CanSILFunctionType getConstantFunctionType(TypeExpansionContext context, + SILDeclRef constant) { + return getConstantInfo(context, constant).SILFnType; } /// Returns the SILParameterInfo for the given declaration's `self` parameter. /// `constant` must refer to a method. - SILParameterInfo getConstantSelfParameter(SILDeclRef constant); + SILParameterInfo getConstantSelfParameter(TypeExpansionContext context, + SILDeclRef constant); /// Returns the SILFunctionType that must be used to perform a vtable dispatch /// to the given declaration. /// /// Will be the same as getConstantFunctionType() if the declaration does not /// override anything. - CanSILFunctionType getConstantOverrideType(SILDeclRef constant) { - return getConstantOverrideInfo(constant).SILFnType; + CanSILFunctionType getConstantOverrideType(TypeExpansionContext context, + SILDeclRef constant) { + return getConstantOverrideInfo(context, constant).SILFnType; } /// Returns the SILConstantInfo that must be used to perform a vtable dispatch @@ -879,17 +870,19 @@ class TypeConverter { /// /// Will be the same as getConstantInfo() if the declaration does not /// override anything. - const SILConstantInfo &getConstantOverrideInfo(SILDeclRef constant) { + const SILConstantInfo &getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef constant) { // Fast path if the constant does not override anything. auto next = constant.getNextOverriddenVTableEntry(); if (next.isNull()) - return getConstantInfo(constant); + return getConstantInfo(context, constant); auto base = constant.getOverriddenVTableEntry(); - return getConstantOverrideInfo(constant, base); + return getConstantOverrideInfo(context, constant, base); } - const SILConstantInfo &getConstantOverrideInfo(SILDeclRef constant, + const SILConstantInfo &getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef constant, SILDeclRef base); /// Get the empty tuple type as a SILType. @@ -939,29 +932,10 @@ class TypeConverter { /// type of the storage of the value. /// /// \return - always an address type - SILType getSubstitutedStorageType(AbstractStorageDecl *value, + SILType getSubstitutedStorageType(TypeExpansionContext context, + AbstractStorageDecl *value, Type lvalueType); - /// Push a generic function context. See GenericContextScope for an RAII - /// interface to this function. - /// - /// Types containing generic parameter references must be lowered in a generic - /// context. There can be at most one level of generic context active at any - /// point in time. - void pushGenericContext(CanGenericSignature sig); - - /// Return the current generic context. This should only be used in - /// the type-conversion routines. - CanGenericSignature getCurGenericContext() const { - if (DependentTypes.empty()) - return CanGenericSignature(); - return DependentTypes.back().Sig; - } - - /// Pop a generic function context. See GenericContextScope for an RAII - /// interface to this function. There must be an active generic context. - void popGenericContext(CanGenericSignature sig); - /// Known types for bridging. #define BRIDGING_KNOWN_TYPE(BridgedModule,BridgedType) \ CanType get##BridgedType##Type(); @@ -973,10 +947,31 @@ class TypeConverter { bool hasLoweredLocalCaptures(SILDeclRef fn); enum class ABIDifference : uint8_t { - // No ABI differences, function can be trivially bitcast to result type. - Trivial, - // Representation difference requires thin-to-thick conversion. - ThinToThick, + // Types have compatible calling conventions and representations, so can + // be trivially bitcast. + // + // Furthermore, if two function types have + // arguments of function type that differ only in + // `CompatibleRepresentation`, those outer function types are transitively + // `CompatibleRepresentation`. (In all other cases, the outer function types + // would fall into the `NeedsThunk` case, because a thunk would be needed + // to change the representation of the function argument.) + CompatibleRepresentation, + + // No convention differences, but there may still be a representation + // difference between values of the compared function types, such as a + // different ptrauth discriminator. The conversion can be performed by a + // `convert_function` instruction. + CompatibleCallingConvention, + + // Representation difference requires thin-to-thick conversion with a + // `thin_to_thick_function` conversion. + CompatibleRepresentation_ThinToThick, + // Function types have `CompatibleCallingConvention` but additionally need + // a thin-to-thick conversion, so a `convert_function` followed by a + // `thin_to_thick_function` sequence is necessary to convert. + CompatibleCallingConvention_ThinToThick, + // Non-trivial difference requires thunk. NeedsThunk }; @@ -990,11 +985,13 @@ class TypeConverter { /// The ABI compatible relation is not symmetric on function types -- while /// T and T! are both subtypes of each other, a calling convention conversion /// of T! to T always requires a thunk. - ABIDifference checkForABIDifferences(SILType type1, SILType type2, + ABIDifference checkForABIDifferences(SILModule &M, + SILType type1, SILType type2, bool thunkOptionals = true); /// Same as above but for SIL function types. - ABIDifference checkFunctionForABIDifferences(SILFunctionType *fnTy1, + ABIDifference checkFunctionForABIDifferences(SILModule &M, + SILFunctionType *fnTy1, SILFunctionType *fnTy2); @@ -1002,9 +999,10 @@ class TypeConverter { /// \p constant. The result is not cached as part of the constant's normal /// ConstantInfo. CanSILFunctionType - getUncachedSILFunctionTypeForConstant(SILDeclRef constant, - CanAnyFunctionType origInterfaceType); - + getUncachedSILFunctionTypeForConstant(TypeExpansionContext expansion, + SILDeclRef constant, + CanAnyFunctionType origInterfaceType); + /// Get the boxed interface type to use for a capture of the given decl. CanSILBoxType getInterfaceBoxTypeForCapture(ValueDecl *captured, @@ -1018,11 +1016,13 @@ class TypeConverter { GenericEnvironment *env, bool isMutable); - CanSILBoxType getBoxTypeForEnumElement(SILType enumType, + CanSILBoxType getBoxTypeForEnumElement(TypeExpansionContext context, + SILType enumType, EnumElementDecl *elt); private: - CanType computeLoweredRValueType(AbstractionPattern origType, + CanType computeLoweredRValueType(TypeExpansionContext context, + AbstractionPattern origType, CanType substType); Type getLoweredCBridgedType(AbstractionPattern pattern, Type t, @@ -1049,26 +1049,6 @@ class TypeConverter { bool suppressOptional); }; -/// RAII interface to push a generic context. -class GenericContextScope { - TypeConverter &TC; - CanGenericSignature Sig; -public: - GenericContextScope(TypeConverter &TC, CanGenericSignature sig) - : TC(TC), Sig(sig) - { - TC.pushGenericContext(sig); - } - - ~GenericContextScope() { - TC.popGenericContext(Sig); - } - -private: - GenericContextScope(const GenericContextScope&) = delete; - GenericContextScope &operator=(const GenericContextScope&) = delete; -}; - } // namespace Lowering } // namespace swift @@ -1083,10 +1063,12 @@ namespace llvm { // Use the second field because the first field can validly be null. static CachingTypeKey getEmptyKey() { - return {nullptr, APCachingKey(), CanTypeInfo::getEmptyKey()}; + return {nullptr, APCachingKey(), CanTypeInfo::getEmptyKey(), + swift::TypeExpansionContext::minimal()}; } static CachingTypeKey getTombstoneKey() { - return {nullptr, APCachingKey(), CanTypeInfo::getTombstoneKey()}; + return {nullptr, APCachingKey(), CanTypeInfo::getTombstoneKey(), + swift::TypeExpansionContext::minimal()}; } static unsigned getHashValue(CachingTypeKey val) { auto hashSig = @@ -1095,7 +1077,10 @@ namespace llvm { CachingKeyInfo::getHashValue(val.OrigType); auto hashSubst = DenseMapInfo::getHashValue(val.SubstType); - return hash_combine(hashSig, hashOrig, hashSubst); + auto hashContext = + DenseMapInfo::getHashValue( + val.expansionContext); + return hash_combine(hashSig, hashOrig, hashSubst, hashContext); } static bool isEqual(CachingTypeKey LHS, CachingTypeKey RHS) { return LHS == RHS; diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index 3bbb363945a5e..fbd5b09f1f098 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -76,7 +76,7 @@ class TypeSubstCloner : public SILClonerWithScopes { // are the same. auto LoweredFnTy = Builder.getFunction().getLoweredFunctionType(); auto RecursiveSubstCalleeSILType = LoweredFnTy; - auto GenSig = LoweredFnTy->getGenericSignature(); + auto GenSig = LoweredFnTy->getInvocationGenericSignature(); if (GenSig) { // Compute substitutions for the specialized function. These // substitutions may be different from the original ones, e.g. @@ -84,13 +84,14 @@ class TypeSubstCloner : public SILClonerWithScopes { RecursiveSubs = SubstitutionMap::get( AI.getFunction() ->getLoweredFunctionType() - ->getGenericSignature(), + ->getInvocationGenericSignature(), Subs); // Use the new set of substitutions to compute the new // substituted callee type. RecursiveSubstCalleeSILType = LoweredFnTy->substGenericArgs( - AI.getModule(), RecursiveSubs); + AI.getModule(), RecursiveSubs, + Builder.getTypeExpansionContext()); } // The specialized recursive function may have different calling @@ -109,7 +110,8 @@ class TypeSubstCloner : public SILClonerWithScopes { assert(Subs.empty() || SubstCalleeSILType == - Callee->getType().substGenericArgs(AI.getModule(), Subs)); + Callee->getType().substGenericArgs( + AI.getModule(), Subs, Builder.getTypeExpansionContext())); } ArrayRef getArguments() const { @@ -168,17 +170,40 @@ class TypeSubstCloner : public SILClonerWithScopes { SILType &Sty = TypeCache[Ty]; if (!Sty) { Sty = Ty.subst(Original.getModule(), SubsMap); + if (!Sty.getASTType()->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return Sty; + // Remap types containing opaque result types in the current context. + Sty = getBuilder().getTypeLowering(Sty).getLoweredType().getCategoryType( + Sty.getCategory()); } return Sty; } CanType remapASTType(CanType ty) { - return ty.subst(SubsMap)->getCanonicalType(); + auto substTy = ty.subst(SubsMap)->getCanonicalType(); + if (!substTy->hasOpaqueArchetype() || + !getBuilder().getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return substTy; + // Remap types containing opaque result types in the current context. + return getBuilder().getModule().Types.getLoweredRValueType( + TypeExpansionContext(getBuilder().getFunction()), substTy); } - ProtocolConformanceRef remapConformance(Type type, + ProtocolConformanceRef remapConformance(Type ty, ProtocolConformanceRef conf) { - return conf.subst(type, SubsMap); + auto conformance = conf.subst(ty, SubsMap); + auto substTy = ty.subst(SubsMap)->getCanonicalType(); + auto context = getBuilder().getTypeExpansionContext(); + if (substTy->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes()) { + conformance = + substOpaqueTypesWithUnderlyingTypes(conformance, substTy, context); + } + return conformance; } SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { @@ -232,8 +257,8 @@ class TypeSubstCloner : public SILClonerWithScopes { SILLocation loc = getOpLocation(inst->getLoc()); SILValue src = getOpValue(inst->getSrc()); SILValue dest = getOpValue(inst->getDest()); - CanType sourceType = getOpASTType(inst->getSourceType()); - CanType targetType = getOpASTType(inst->getTargetType()); + CanType sourceType = getOpASTType(inst->getSourceFormalType()); + CanType targetType = getOpASTType(inst->getTargetFormalType()); SILBasicBlock *succBB = getOpBasicBlock(inst->getSuccessBB()); SILBasicBlock *failBB = getOpBasicBlock(inst->getFailureBB()); diff --git a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h index 1c19552cd5f5f..e71b6c58fea36 100644 --- a/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AccessSummaryAnalysis.h @@ -62,7 +62,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// Returns a description of the summary. For debugging and testing /// purposes. - std::string getDescription(SILType BaseType, SILModule &M) const; + std::string getDescription(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; }; typedef llvm::SmallDenseMap @@ -85,7 +86,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// Returns a description of the summary. For debugging and testing /// purposes. - std::string getDescription(SILType BaseType, SILModule &M) const; + std::string getDescription(SILType BaseType, SILModule &M, + TypeExpansionContext context) const; /// Returns the accesses that the function performs to subpaths of the /// argument. @@ -208,7 +210,8 @@ class AccessSummaryAnalysis : public BottomUpIPAnalysis { /// The base type must be the type of the root of the path. static std::string getSubPathDescription(SILType BaseType, const IndexTrieNode *SubPath, - SILModule &M); + SILModule &M, + TypeExpansionContext context); /// Performs a lexicographic comparison of two subpaths, first by path length /// and then by index of the last path component. Returns true when lhs diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index fe0837c9549ba..7c743f9ec45b9 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -194,10 +194,10 @@ class AliasAnalysis : public SILAnalysis { return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias; } - /// \returns True if the release of the \p Ptr can access memory accessed by - /// \p User. + /// \returns True if the release of the \p releasedReference can access or + /// free memory accessed by \p User. bool mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr); + SILValue releasedReference); /// Use the alias analysis to determine the memory behavior of Inst with /// respect to V. diff --git a/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h b/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h index 378fbedc680ac..8b5e509411936 100644 --- a/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h @@ -54,6 +54,10 @@ class CalleeList { : CalleeFunctions(llvm::makeArrayRef(List.begin(), List.end())), IsIncomplete(IsIncomplete) {} + SWIFT_DEBUG_DUMP; + + void print(llvm::raw_ostream &os) const; + /// Return an iterator for the beginning of the list. ArrayRef::iterator begin() const { return CalleeFunctions.begin(); @@ -67,7 +71,7 @@ class CalleeList { bool isIncomplete() const { return IsIncomplete; } /// Returns true if all callees are known and not external. - bool allCalleesVisible(); + bool allCalleesVisible() const; }; /// CalleeCache is a helper class that builds lists of potential @@ -106,16 +110,16 @@ class CalleeCache { /// given instruction. E.g. it could be destructors. CalleeList getCalleeList(SILInstruction *I) const; + CalleeList getCalleeList(SILDeclRef Decl) const; + private: void enumerateFunctionsInModule(); void sortAndUniqueCallees(); CalleesAndCanCallUnknown &getOrCreateCalleesForMethod(SILDeclRef Decl); - void computeClassMethodCalleesForClass(ClassDecl *CD); - void computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method); + void computeClassMethodCallees(); void computeWitnessMethodCalleesForWitnessTable(SILWitnessTable &WT); void computeMethodCallees(); SILFunction *getSingleCalleeForWitnessMethod(WitnessMethodInst *WMI) const; - CalleeList getCalleeList(SILDeclRef Decl) const; CalleeList getCalleeList(WitnessMethodInst *WMI) const; CalleeList getCalleeList(ClassMethodInst *CMI) const; CalleeList getCalleeListForCalleeKind(SILValue Callee) const; @@ -162,17 +166,22 @@ class BasicCalleeAnalysis : public SILAnalysis { Cache.reset(); } - CalleeList getCalleeList(FullApplySite FAS) { + SWIFT_DEBUG_DUMP; + + void print(llvm::raw_ostream &os) const; + + void updateCache() { if (!Cache) Cache = llvm::make_unique(M); + } + CalleeList getCalleeList(FullApplySite FAS) { + updateCache(); return Cache->getCalleeList(FAS); } CalleeList getCalleeList(SILInstruction *I) { - if (!Cache) - Cache = llvm::make_unique(M); - + updateCache(); return Cache->getCalleeList(I); } }; diff --git a/include/swift/SILOptimizer/Analysis/CallerAnalysis.h b/include/swift/SILOptimizer/Analysis/CallerAnalysis.h index b2a722f3ed6dc..0896695bc80a5 100644 --- a/include/swift/SILOptimizer/Analysis/CallerAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/CallerAnalysis.h @@ -111,8 +111,7 @@ class CallerAnalysis final : public SILAnalysis { /// invalidating parts of the call graph. const FunctionInfo &getFunctionInfo(SILFunction *f) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "Only for use in the debugger"); + SWIFT_DEBUG_DUMP; /// Print the state of the caller analysis as a sequence of yaml documents for /// each callee we are tracking. @@ -120,9 +119,7 @@ class CallerAnalysis final : public SILAnalysis { /// Print the state of the caller analysis as a sequence of yaml documents for /// each callee we are tracking to the passed in file path. - LLVM_ATTRIBUTE_DEPRECATED(void print(const char *filePath) - const LLVM_ATTRIBUTE_USED, - "Only for use in the debugger"); + SWIFT_DEBUG_DUMPER(print(const char *filePath)); void verify() const override; void verify(SILFunction *f) const override; @@ -349,8 +346,7 @@ class CallerAnalysis::FunctionInfo { return llvm::make_range(callerStates.begin(), callerStates.end()); } - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "Only for use in the debugger"); + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &os) const; }; diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index e7ba4e63f4d36..2d56b97d85695 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -43,18 +43,20 @@ /// return a /// /// Generates the following connection graph, where 'a' is in the SILValue %0: -/// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content -/// Con %0.1 Esc: G, Succ: // Represents the content of 'a' -/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' +/// Val [ref] %1 Esc: R, Succ: (%1) // Reference 'a'; points to 'a's object +/// Con [int] %2 Esc: R, Succ: (%2.1) // Object pointed to by 'a' +/// Con %2.1 Esc: G, Succ: // Fields in the object +/// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' /// /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. /// These states form a lattice in which None is the most refined, or top, state /// and Global is the least refined, or bottom, state. Merging nodes performs a -/// meet operation on their escaping states. At a call site, the callee graph is -/// merged with the callee graph by merging the respective call argument -/// nodes. A node has a "Return" escaping state if it only escapes by being -/// returned from the current function. A node has an "Argument" escaping state -/// if only escapes by being passed as an incoming argument to this function. +/// meet operation on their escaping states, moving the state down in the +/// lattice. At a call site, the callee graph is merged with the caller graph by +/// merging the respective call argument nodes. A node has a "Return" escaping +/// state if it only escapes by being returned from the current function. A node +/// has an "Argument" escaping state if only escapes by being passed as an +/// incoming argument to this function. /// /// A directed edge between two connection graph nodes indicates that the memory /// represented by the destination node memory is reachable via an address @@ -81,30 +83,37 @@ /// /// Generates the following connection graph, where the alloc_stack for variable /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. -/// Val %0 Esc: G, Succ: (%0.1) -/// Con %0.1 Esc: G, Succ: %3 -/// Val %3 Esc: G, Succ: (%3.1) -/// Con %3.1 Esc: G, Succ: -/// Ret Esc: R, Succ: %3 +/// +/// Val %0 Esc: , Succ: (%0.1) // Stack address of 'a' +/// Con [ref] %0.1 Esc: , Succ: %3 // Local reference 'a', aliased with %3 +/// Val [ref] %3 Esc: , Succ: (%4) // Local instance 'a', stored in %0.1 +/// Con [int] %4 Esc: G, Succ: (%4.1) // Object, escapes +/// Con %4.1 Esc: G, Succ: // Fields, escapes +/// Ret Esc: , Succ: (%4), %3 /// /// The value node for variable 'a' now points to local variable storage /// (%0.1). That local variable storage contains a reference. Assignment into /// that reference creates a defer edge to the allocated reference (%3). The -/// allocated reference in turn points to the object storage (%3.1). +/// allocated reference in turn points to the object storage (%4). /// -/// Note that a variable holding a single class reference and a variable -/// holding a non-trivial struct has the same graph representation. The -/// variable's content node only represents the value of the references, not the -/// memory pointed-to by the reference. +/// Note that a variable holding a single class reference and a variable holding +/// a non-trivial struct will have the same graph representation. A '[ref]' flag +/// on a node indicates that the all pointer-type fields that may be stored +/// inside the memory represented by that node are references. This allows alias +/// analysis to assume the object the node points to will not be released when +/// the node's memory is released as long as there are subsequent accesses to +/// the object accessed via a different path in the connection graph. /// /// A pointsTo edge does not necessarily indicate pointer indirection. It may -/// simply represent a derived address within the same object. This allows -/// escape analysis to view an object's memory in layers, each with separate -/// escaping properties. For example, a class object's first-level content node -/// represents the object header including the metadata pointer and reference -/// count. An object's second level content node only represents the -/// reference-holding fields within that object. Consider the connection graph -/// for a class with properties: +/// simply represent a derived address within the same object. A node that +/// points to the same logical object must be flagged as an interior node +/// ('[int]'). Interior nodes always have pointsTo content representing the rest +/// of the object. This allows escape analysis to view an object's memory in +/// layers, each with separate escaping properties. For example, a class +/// object's first-level content node represents the object header including the +/// metadata pointer and reference count. An object's second level content node +/// only represents the reference-holding fields within that object. Consider +/// the connection graph for a class with properties: /// /// class HasObj { /// var obj: AnyObject @@ -114,47 +123,50 @@ /// } /// /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: -/// Arg %0 Esc: A, Succ: (%0.1) -/// Con %0.1 Esc: A, Succ: (%0.2) -/// Con %0.2 Esc: A, Succ: %1 -/// Arg %1 Esc: A, Succ: (%1.1) -/// Con %1.1 Esc: A, Succ: (%1.2) -/// Con %1.2 Esc: G, Succ: /// -/// Node %0.1 represents the header of 'h', including reference count and -/// metadata pointer. This node points to %0.2 which represents the 'obj' -/// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to -/// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any -/// potential nontrivial properties in 'o' which may have escaped globally when -/// 'o' was released. +/// Arg [ref] %0 Esc: A, Succ: (%6) // 'h' +/// Arg [ref] %1 Esc: A, Succ: (%1.1) // 'o' +/// Con [int] %1.1 Esc: A, Succ: (%1.2) // 'o' object +/// Con %1.2 Esc: A, Succ: (%1.3) // 'o' fields +/// Con %1.3 Esc: G, Succ: // memory 'h.obj' may point to +/// Con [int] %6 Esc: A, Succ: (%7) // 'h' object +/// Con %7 Esc: A, Succ: %1 // 'h.obj' +/// +/// Node %1.1 represents the header of 'o', including reference count and +/// metadata pointer. This node points to %1.2 which represents the 'obj' +/// property. '%1.3' represents any potential nontrivial properties in 'o' which +/// may have escaped globally when 'o' was released. '%6' is a ref_element_addr +/// accessing 'h.obj'. '%7' is a load of 'h.obj'. The assignment 'h.obj = o' +/// creates a defer edge from '%7' to '%1'. /// /// The connection graph is constructed by summarizing all memory operations in /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted /// connection graph. /// -/// In addition to the connection graph, EscapeAnalysis stores information about -/// "use points". Each release operation is a use points. These instructions are -/// recorded in a table and given an ID. Each connection graph node stores a -/// bitset indicating the use points reachable via the CFG by that node. This -/// provides some flow-sensitive information on top of the otherwise flow -/// insensitive connection graph. -/// -/// Note: storing bitsets in each node may be unnecessary overhead since the -/// same information can be obtained with a graph traversal, typically of only -/// 1-3 hops. -// ===---------------------------------------------------------------------===// +/// In addition to the connection graph, EscapeAnalysis caches information about +/// "use points". Each release operation in which the released reference can be +/// identified is a considered a use point. The use point instructions are +/// recorded in a table and given an ID. Each connection graph content node +/// stores a bitset indicating the use points that may release references that +/// point to by that content node. Correctness relies on an invariant: for each +/// reference-type value in the function, all points in the function which may +/// release the reference must be identified as use points of the node that the +/// value points to. If the reference-type value may be released any other +/// way, then its content node must be marked escaping. +/// ===---------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ -#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" #include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h" +#include "swift/SILOptimizer/Analysis/ValueTracking.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallVector.h" struct CGForDotView; @@ -233,14 +245,35 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Global }; + // Must be ordered from most precise to least precise. A meet across memory + // locations (such as aggregate fields) always moves down. + enum PointerKind { NoPointer, ReferenceOnly, AnyPointer }; + static bool canOnlyContainReferences(PointerKind pointerKind) { + return pointerKind <= ReferenceOnly; + } + public: class CGNode; class ConnectionGraph; + private: class CGNodeMap; + struct CGNodeWorklist; /// The int-part is an EdgeType and specifies which kind of predecessor it is. - typedef llvm::PointerIntPair Predecessor; + struct Predecessor { + llvm::PointerIntPair predAndEdgeType; + + Predecessor(CGNode *predNode, EdgeType ty) + : predAndEdgeType(predNode, ty) {} + bool operator==(const Predecessor &pred) { + return predAndEdgeType == pred.predAndEdgeType; + } + + bool is(EdgeType ty) const { return predAndEdgeType.getInt() == ty; } + + CGNode *getPredNode() const { return predAndEdgeType.getPointer(); } + }; public: @@ -249,18 +282,31 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// pointer points to (see NodeType). class CGNode { - /// The associated value in the function. It is only used for debug printing. - /// There may be multiple nodes associated to the same value, e.g. a Content - /// node has the same V as its points-to predecessor. - ValueBase *V; + /// The associated value in the function. This is only valid for nodes that + /// are mapped to a value in the graph's Values2Nodes map. Multiple values + /// may be mapped to the same node, but only one value is associated with + /// the node via 'mappedValue'. Setting 'mappedValue' to a valid SILValue + /// for an unmapped node would result in a dangling pointer when the + /// SILValue is deleted. + /// + /// Argument and Value nodes are always initially mapped, but may become + /// unmapped when their SILValue is deleted. Content nodes are conditionally + /// mapped, only if they are associated with an explicit dereference in the + /// code (which has not been deleted). Return nodes are never mapped. + ValueBase *mappedValue; /// The outgoing points-to edge (if any) to a Content node. See also: /// pointsToIsEdge. /// If we ever want to distinguish between different fields of an object, /// then we should replace the single pointsTo edge with multiple edges - /// one for each field. + /// + /// Note: A content node with proper a "pointsTo" edge to another content + /// node often does *not* represent a pointer. There is no + /// indirection. Instead, these content nodes are all part of the same + /// object and only represent different layers within the object. CGNode *pointsTo = nullptr; - + /// The outgoing defer edges. llvm::SmallVector defersTo; @@ -281,7 +327,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { EscapeState State = EscapeState::None; /// If true, the pointsTo is a real edge in the graph. Otherwise it is not - /// and edge (e.g. this does not appear in the pointsTo Preds list), but + /// an edge (e.g. this does not appear in the pointsTo Preds list), but /// still must point to the same Content node as all successor nodes. bool pointsToIsEdge = false; @@ -291,20 +337,53 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// True if the merge is finished (see mergeTo). In this state this node /// is completely unlinked from the graph, bool isMerged = false; - + + /// True if this node's pointsTo content is part of the same object with + /// respect to SIL memory access. When this is true, then this node must + /// have a content node. + /// + /// When this flag is false, it provides a "separate access guarantee" for + /// stronger analysis. If the base object for a SIL memory access is mapped + /// to this node, then the accessed memory must have the same escaping + /// properties as the base object. In contrast, when this is true, the + /// connection graph may model the escaping properties of the base object + /// separately from the accessed memory. + bool isInteriorFlag; + + /// True if this node can only point to other nodes via a reference, not an + /// address or raw pointer. + /// + /// When this flag is true, it provides a "separate lifetime guarantee". Any + /// reference modeled by this node keeps alive all pointsTo content until + /// it is released. e.g. Given two nodes that both pointTo the same + /// content, releasing the value associated one node cannot free that + /// content as long as the released node is reference-only and the value + /// associated with the other node is accessed later. + /// + /// Note that an object field may contain a raw pointer which could point + /// anywhere, even back into the same object. In that case + /// hasReferenceOnlyFlag must be false. + /// + /// Typically, when this is true, the node represents at least one + /// reference, however, a return node may be created even when the return + /// type is NoPointer. + bool hasReferenceOnlyFlag; + /// The type of the node (mainly distinguishes between content and value /// nodes). NodeType Type; /// The constructor. - CGNode(ValueBase *V, NodeType Type) : V(V), UsePoints(0), Type(Type) { + CGNode(ValueBase *V, NodeType Type, bool isInterior, bool hasReferenceOnly) + : mappedValue(V), UsePoints(0), isInteriorFlag(isInterior), + hasReferenceOnlyFlag(hasReferenceOnly), Type(Type) { switch (Type) { case NodeType::Argument: case NodeType::Value: - assert(V); + assert(V && !isInteriorFlag); break; case NodeType::Return: - assert(!V); + assert(!V && !isInteriorFlag); break; case NodeType::Content: // A content node representing the returned value has no associated @@ -313,14 +392,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } - /// Merges the state from another state and returns true if it changed. - bool mergeEscapeState(EscapeState OtherState) { - if (OtherState > State) { - State = OtherState; - return true; - } - return false; - } + /// Get the representative node that maps to a SILValue and depth in the + /// pointsTo graph. + std::pair + getRepNode(SmallPtrSetImpl &visited) const; /// Merges the use points from another node and returns true if there are /// any changes. @@ -330,10 +405,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return Changed; } - /// Returns the Content node if this node has an outgoing points-to edge. - CGNode *getPointsToEdge() const { - return pointsToIsEdge ? pointsTo : nullptr; - } + void mergeFlags(bool isInterior, bool hasReferenceOnly); + + // Merge the properties of \p fromNode into this node. + void mergeProperties(CGNode *fromNode); /// Finds a successor node in the outgoing defer edges. llvm::SmallVectorImpl::iterator findDeferred(CGNode *Def) { @@ -352,23 +427,29 @@ class EscapeAnalysis : public BottomUpIPAnalysis { Preds.erase(Iter); } - /// Adds a defer-edge to another node \p To. Not done if \p To is this node. - bool addDeferred(CGNode *To) { - assert(!To->isMerged); + bool canAddDeferred(CGNode *To) { if (To == this) return false; for (auto *Def : defersTo) { if (Def == To) return false; } + return true; + } + + /// Adds a defer-edge to another node \p To. Not done if \p To is this node. + bool addDeferred(CGNode *To) { + assert(!To->isMerged); + if (!canAddDeferred(To)) + return false; To->Preds.push_back(Predecessor(this, EdgeType::Defer)); defersTo.push_back(To); return true; } /// Sets the outgoing points-to edge. The \p To node must be a Content node. - void setPointsTo(CGNode *To) { - assert(!To->mergeTo); + void setPointsToEdge(CGNode *To) { + assert(!To->isMerged); assert(To->Type == NodeType::Content && "Wrong node type for points-to edge"); pointsToIsEdge = true; @@ -392,32 +473,47 @@ class EscapeAnalysis : public BottomUpIPAnalysis { UsePoints.set(Idx); } - /// For debug dumping. - void dump() const; - - /// Returns a string representation of the node type. Also for debug dumping. - const char *getTypeStr() const; - /// Checks an invariant of the connection graph: The points-to nodes of /// the defer-successors must match with the points-to of this node. - bool matchPointToOfDefers() const { - for (CGNode *Def : defersTo) { - if (pointsTo != Def->pointsTo) - return false; - } - /// A defer-path in the graph must not end without the specified points-to - /// node. - if (pointsTo && !pointsToIsEdge && defersTo.empty()) - return false; - return true; - } - + bool matchPointToOfDefers(bool allowMerge = false) const; + friend class CGNodeMap; friend class ConnectionGraph; friend struct ::CGForDotView; + friend struct CGNodeWorklist; public: - + struct RepValue { + // May only be an invalid SILValue for Return nodes or deleted values. + llvm::PointerIntPair valueAndIsReturn; + unsigned depth; + + SILValue getValue() const { return valueAndIsReturn.getPointer(); } + bool isReturn() const { return valueAndIsReturn.getInt(); } + void + print(llvm::raw_ostream &stream, + const llvm::DenseMap &instToIDMap) const; + }; + // Get the representative SILValue for this node its depth relative to the + // node that is mapped to this value. + RepValue getRepValue() const; + + /// Return true if this node represents content. + bool isContent() const { return Type == NodeType::Content; } + + /// Return true if this node pointsTo the same object. + bool isInterior() const { return isInteriorFlag; } + + void setInterior(bool isInterior) { isInteriorFlag = isInterior; } + + /// Return true if this node can only point to another node via a reference, + /// as opposed to an address or raw pointer. + bool hasReferenceOnly() const { return hasReferenceOnlyFlag; } + + void setHasReferenceOnly(bool hasReferenceOnly) { + hasReferenceOnlyFlag = hasReferenceOnly; + } + /// Returns the escape state. EscapeState getEscapeState() const { return State; } @@ -425,67 +521,85 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// the return instruction. bool escapes() const { return getEscapeState() != EscapeState::None; } + /// Specifies that this content node's memory escapes to global or + /// unidentified memory. + void markEscaping() { + assert(Type == NodeType::Content); + mergeEscapeState(EscapeState::Global); + } + + /// Merges the state from another state and returns true if it changed. + bool mergeEscapeState(EscapeState OtherState) { + if (OtherState > State) { + State = OtherState; + return true; + } + return false; + } + /// Returns true if the node's value escapes within the function. This /// means that any unidentified pointer in the function may alias to /// the node's value. + /// /// Note that in the false-case the node's value can still escape via /// the return instruction. - bool escapesInsideFunction(bool isNotAliasingArgument) const { + /// + /// \p nodeValue is often the same as 'this->getRepValue().getValue()', but + /// is sometimes a more refined value specific to a content nodes. + bool valueEscapesInsideFunction(SILValue nodeValue) const { switch (getEscapeState()) { case EscapeState::None: case EscapeState::Return: return false; case EscapeState::Arguments: - return !isNotAliasingArgument; + return !nodeValue || !isExclusiveArgument(nodeValue); case EscapeState::Global: return true; } - llvm_unreachable("Unhandled EscapeState in switch."); } + /// Returns true if the node's value escapes within the function. + /// + /// This is a more conservative variant of valueEscapesInsideFunction, for + /// when the value being accessed is unknown. For content nodes, the node's + /// value cannot be used because it arbitrarily takes on one of the values + /// associated with the content. e.g. if that value is an exclusive + /// argument, it would be incorrect to assume all values for that content + /// are exclusive. + bool escapesInsideFunction() const { + SILValue nodeValue = isContent() ? SILValue() : SILValue(mappedValue); + return valueEscapesInsideFunction(nodeValue); + } - /// Returns the content node if of this node if it exists in the graph. + /// Returns the content node of this node if it exists in the graph. CGNode *getContentNodeOrNull() const { return pointsTo; } - }; -private: + /// Returns the Content node if this node has an outgoing points-to edge. + CGNode *getPointsToEdge() const { + return pointsToIsEdge ? pointsTo : nullptr; + } - /// Mapping from nodes in a callee-graph to nodes in a caller-graph. - class CGNodeMap { - /// The map itself. - llvm::DenseMap Map; + /// Visit all successors of this node in the connection graph until \p + /// visitor returns false. Return true if all successors were visited + /// without \p visitor returning false. + /// + /// Note that a node may be the pointsTo successor of itself. + template bool visitSuccessors(Visitor &&visitor) const; - /// The list of source nodes (= keys in Map), which is used as a work-list. - llvm::SmallVector MappedNodes; - public: + /// Visit all adjacent defers. Halt when the visitor returns false. Return + /// true if the visitor returned true for all defers. + template bool visitDefers(Visitor &&visitor) const; - /// Adds a mapping and pushes the \p From node into the work-list - /// MappedNodes. - void add(CGNode *From, CGNode *To) { - assert(From && To && !From->isMerged && !To->isMerged); - Map[From] = To; - if (!From->isInWorkList) { - MappedNodes.push_back(From); - From->isInWorkList = true; - } - } - /// Looks up a node in the mapping. - CGNode *get(CGNode *From) const { - auto Iter = Map.find(From); - if (Iter == Map.end()) - return nullptr; + /// For debug dumping. + void dump() const; - return Iter->second->getMergeTarget(); - } - const SmallVectorImpl &getMappedNodes() const { - return MappedNodes; - } + /// Returns a string representation of the node type. For debug dumping. + const char *getTypeStr() const; }; public: - /// The connection graph for a function. See also: EdgeType, NodeType and /// CGNode. /// A connection graph has these invariants: @@ -497,10 +611,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// 4) For any node N, all paths starting at N which consist of only /// defer-edges and a single trailing points-to edge must lead to the same /// Content node. + /// + /// Additionally, all nodes in a path consisting of defer-edges must have the + /// same pointsTo field--either all pointsTo fields are null, or they all + /// point to the same target of the points-to edges at the leaves of the defer + /// web, which must have been merged into a single content node. + /// + /// Paths comprised of points-to edges may contain cycles and self-cycles. class ConnectionGraph { - /// Backlink to the graph's function. SILFunction *F; + /// Backlink to the EscapeAnalysis + EscapeAnalysis *EA; /// Mapping from pointer SIL values to nodes in the graph. Such a value can /// never be a projection, because in case of projection-instruction the @@ -519,43 +641,45 @@ class EscapeAnalysis : public BottomUpIPAnalysis { CGNode *ReturnNode = nullptr; /// The list of use points. - llvm::SmallVector UsePointTable; + llvm::SmallVector UsePointTable; /// Mapping of use points to bit indices in CGNode::UsePoints. - llvm::DenseMap UsePoints; + llvm::DenseMap UsePoints; /// The allocator for nodes. llvm::SpecificBumpPtrAllocator NodeAllocator; /// True if this is a summary graph. bool isSummaryGraph; - + + /// Track the currently active intrusive worklist -- one at a time. + CGNodeWorklist *activeWorklist = nullptr; + /// Constructs a connection graph for a function. - ConnectionGraph(SILFunction *F, bool isSummaryGraph) : - F(F), isSummaryGraph(isSummaryGraph) { - } + ConnectionGraph(SILFunction *F, EscapeAnalysis *EA, bool isSummaryGraph) + : F(F), EA(EA), isSummaryGraph(isSummaryGraph) {} /// Returns true if the connection graph is empty. - bool isEmpty() { + bool isEmpty() const { return Values2Nodes.empty() && Nodes.empty() && UsePoints.empty(); } /// Removes all nodes from the graph. void clear(); - + /// Allocates a node of a given type. - CGNode *allocNode(ValueBase *V, NodeType Type) { - CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type); + /// + /// isInterior is always false for non-content nodes and is set for content + /// nodes based on the type and origin of the pointer. + CGNode *allocNode(ValueBase *V, NodeType Type, bool isInterior, + bool hasReferenceOnly) { + assert((Type == NodeType::Content) || !isInterior); + CGNode *Node = new (NodeAllocator.Allocate()) + CGNode(V, Type, isInterior, hasReferenceOnly); Nodes.push_back(Node); return Node; } - /// Adds a defer-edge and updates pointsTo of all defer-reachable nodes. - /// The addition of a defer-edge may invalidate the graph invariance 4). - /// If this is the case, all "mismatching" Content nodes are merged until - /// invariance 4) is reached again. - bool addDeferEdge(CGNode *From, CGNode *To); - /// Adds the node \p From (to be merged with \p To) to the ToMerge list. /// The actual merging is done in mergeAllScheduledNodes(). void scheduleToMerge(CGNode *From, CGNode *To) { @@ -569,14 +693,25 @@ class EscapeAnalysis : public BottomUpIPAnalysis { } } + /// Initialize the 'pointsTo' fields of all nodes in the defer web of \p + /// initialiNode. + /// + /// If \p createEdge is true, a proper pointsTo edge will be created from \p + /// initialNode to \p pointsTo. + void initializePointsTo(CGNode *initialNode, CGNode *newPointsTo, + bool createEdge = false); + + void initializePointsToEdge(CGNode *initialNode, CGNode *newPointsTo) { + initializePointsTo(initialNode, newPointsTo, true); + } + /// Merges all nodes which are added to the ToMerge list. void mergeAllScheduledNodes(); - /// Transitively updates pointsTo of all nodes in the defer-edge web, - /// starting at \p InitialNode. - /// If a node in the web already points to another content node, the other - /// content node is scheduled to be merged with \p pointsTo. - void updatePointsTo(CGNode *InitialNode, CGNode *pointsTo); + /// Transitively update pointsTo of all nodes in the defer-edge web, + /// reaching and reachable from \p initialNode. All nodes in this defer web + /// must already have an initialized `pointsTo`. + void mergePointsTo(CGNode *initialNode, CGNode *pointsTo); /// Utility function to clear the isInWorkList flags of all nodes in /// \p WorkList. @@ -590,94 +725,101 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// If V is a projection(-path) then the base of the projection(-path) is /// taken. This means the node is always created for the "outermost" value /// where V is contained. - /// Returns null, if V is not a "pointer". - CGNode *getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded = true); + /// Returns null, if V (or its base value) is not a "pointer". + CGNode *getNode(SILValue V); - /// Gets or creates a content node to which \a AddrNode points to during - /// initial graph construction. This may not be called after defer edges - /// have been created. Doing so would break the invariant that all - /// non-content nodes ultimately have a pointsTo edge to a single content - /// node. - CGNode *getContentNode(CGNode *AddrNode); + // Helper for getValueContent to create and return a content node with the + // given \p isInterior and \p hasReferenceOnly flags. \p addrNode + // will gain a points-to edge to the new content node. + CGNode *createContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); - /// Get or creates a pseudo node for the function return value. - CGNode *getReturnNode() { - if (!ReturnNode) { - ReturnNode = allocNode(nullptr, NodeType::Return); - if (!isSummaryGraph) - ReturnNode->mergeEscapeState(EscapeState::Return); - } - return ReturnNode; + CGNode *getOrCreateContentNode(CGNode *addrNode, bool isInterior, + bool hasReferenceOnly); + + /// Create a new content node based on an existing content node to support + /// graph merging. + /// + /// \p destAddrNode will point to to new content. The content's initial + /// state will be initialized based on the \p srcContent node. + CGNode *createMergedContent(CGNode *destAddrNode, CGNode *srcContent); + + // Helper for getValueContent to get the content node for an address, which + // may be variable content or field content. + CGNode *getOrCreateAddressContent(SILValue addrVal, CGNode *addrNode); + + // Helper for getValueContent to get the content representing a referenced + // object. \p refVal's type may or may not have reference semantics. The + // caller must knows based on the operand using \p refVal that it contains a + // reference. + CGNode *getOrCreateReferenceContent(SILValue refVal, CGNode *refNode); + + // Helper for getValueContent to get the content node for an unknown pointer + // value. This is useful to determine whether multiple nodes are in the same + // defer web, but is otherwise conservative. + CGNode *getOrCreateUnknownContent(CGNode *addrNode); + + /// Get a node representing the field data within the given object node. + /// If objNode was a recognized reference-only value, then it's content node + /// will already be initialized according to the reference type. Otherwise, + /// return null. + CGNode *getFieldContent(CGNode *objNode) { + if (!objNode->isInterior()) + return nullptr; + return objNode->getContentNodeOrNull(); } + /// Get or creates a pseudo node for the function return value. + CGNode *getReturnNode(); + /// Returns the node for the function return value if present. CGNode *getReturnNodeOrNull() const { return ReturnNode; } - /// Returns the node of the "exact" value \p V (no projections are skipped) - /// if one exists. - CGNode *lookupNode(ValueBase *V) { - CGNode *Node = Values2Nodes.lookup(V); - if (Node) - return Node->getMergeTarget(); - return nullptr; - } - /// Re-uses a node for another SIL value. void setNode(ValueBase *V, CGNode *Node) { assert(Values2Nodes.find(V) == Values2Nodes.end()); Values2Nodes[V] = Node; + if (!Node->mappedValue) + Node->mappedValue = V; } - /// Adds an argument/instruction in which the node's value is used. - int addUsePoint(CGNode *Node, SILNode *User) { - if (Node->getEscapeState() >= EscapeState::Global) - return -1; - - User = User->getRepresentativeSILNodeInObject(); - int Idx = (int)UsePoints.size(); - assert(UsePoints.count(User) == 0 && "value is already a use-point"); - UsePoints[User] = Idx; - UsePointTable.push_back(User); - assert(UsePoints.size() == UsePointTable.size()); - Node->setUsePointBit(Idx); - return Idx; + /// If \p pointer is a pointer, set it's content to global escaping. + /// + /// Only mark the content node as escaping. Marking a pointer node as + /// escaping would generally be meaningless because it may have aliases or + /// defer edges. Marking the pointer node as escaping would also be too + /// conservative because, when the pointer is mapped to a content node, it + /// would behave as if the memory containing the pointer also escaped. + /// + /// If the pointer is mapped to a node, then calling setEscapesGlobal must + /// ensure that it points to a content node. Even if we could mark the + /// pointer node as escaping, it would be insufficient because only content + /// nodes are merged into the caller graph. + void setEscapesGlobal(SILValue pointer) { + if (CGNode *content = getValueContent(pointer)) + content->markEscaping(); } - /// Specifies that the node's value escapes to global or unidentified - /// memory. - void setEscapesGlobal(CGNode *Node) { - Node->mergeEscapeState(EscapeState::Global); - - // Make sure to have a content node. Otherwise we may end up not merging - // the global-escape state into a caller graph (only content nodes are - // merged). Either the node itself is a content node or we let the node - // point to one. - if (Node->Type != NodeType::Content) - getContentNode(Node); - } + /// Adds an argument/instruction in which the node's value is used. + int addUsePoint(CGNode *Node, SILInstruction *User); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). /// Returns the \p From node or its merge-target in case \p From was merged /// during adding the edge. - /// The \p EdgeAdded is set to true if there was no defer-edge between - /// \p From and \p To, yet. - CGNode *defer(CGNode *From, CGNode *To, bool &EdgeAdded) { - if (addDeferEdge(From, To)) - EdgeAdded = true; - mergeAllScheduledNodes(); - return From->getMergeTarget(); - } + /// \p Changed is set to true if a defer edge was added or any nodes were + /// merged. + CGNode *defer(CGNode *From, CGNode *To, bool &Changed); /// Creates a defer-edge between \p From and \p To. /// This may trigger node merges to keep the graph invariance 4). /// Returns the \p From node or its merge-target in case \p From was merged /// during adding the edge. CGNode *defer(CGNode *From, CGNode *To) { - bool UnusedEdgeAddedFlag = false; - return defer(From, To, UnusedEdgeAddedFlag); + bool UnusedChangedFlag = false; + return defer(From, To, UnusedChangedFlag); } /// Merges the \p SourceGraph into this graph. The \p Mapping contains the @@ -687,24 +829,43 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Propagates the escape states through the graph. void propagateEscapeStates(); - /// Removes a value from the graph. - /// It does not delete its node but makes sure that the value cannot be - /// lookup-up with getNode() anymore. - void removeFromGraph(ValueBase *V) { Values2Nodes.erase(V); } + /// Remove a value from the graph. Do not delete the mapped node, but reset + /// mappedValue if it is set to this value, and make sure that the node + /// cannot be looked up with getNode(). + void removeFromGraph(ValueBase *V); - /// Returns true if there is a path from \p From to \p To. - bool isReachable(CGNode *From, CGNode *To); + enum class Traversal { Follow, Backtrack, Halt }; - public: + /// Traverse backward from startNode, following predecessor edges. + /// + /// CGNodeVisitor takes the current CGNode and returns Traversal::Follow if + /// traversal should proceed along its predecessors, Traversal::Backtrack, + /// if it should not follow its predecessors, and Traversal::Halt if it + /// should immediately stop visiting nodes. + /// + /// Return true if the visitor did not halt traversal. + template + bool backwardTraverse(CGNode *startNode, CGPredVisitor &&visitor); - /// Gets or creates a node for a value \p V. - /// If V is a projection(-path) then the base of the projection(-path) is - /// taken. This means the node is always created for the "outermost" value - /// where V is contained. - /// Returns null, if V is not a "pointer". - CGNode *getNodeOrNull(ValueBase *V, EscapeAnalysis *EA) { - return getNode(V, EA, false); - } + /// Traverse forward from startNode, following defer edges. + /// + /// CGNodeVisitor takes the current CGNode and returns Traversal::Follow if + /// traversal should proceed along its predecessors, Traversal::Backtrack, + /// if it should not follow its predecessors, and Traversal::Halt if it + /// should immediately stop visiting nodes. + /// + /// Return true if the visitor did not halt traversal. + template + bool forwardTraverseDefer(CGNode *startNode, CGNodeVisitor &&visitor); + + public: + /// Get the content node pointed to by \p ptrVal. + /// + /// If \p ptrVal cannot be mapped to a node, return nullptr. + /// + /// If \p ptrVal is mapped to a node, return a non-null node representing + /// the content that \p ptrVal points to. + CGNode *getValueContent(SILValue ptrVal); /// Returns the number of use-points of a node. int getNumUsePoints(CGNode *Node) { @@ -717,11 +878,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// (indirectly) somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. - bool isUsePoint(SILNode *UsePoint, CGNode *Node); + bool isUsePoint(SILInstruction *UsePoint, CGNode *Node); /// Returns all use points of \p Node in \p UsePoints. void getUsePoints(CGNode *Node, - llvm::SmallVectorImpl &UsePoints); + llvm::SmallVectorImpl &UsePoints); /// Computes the use point information. void computeUsePoints(); @@ -742,22 +903,27 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Global escaping nodes are red, argument escaping nodes are blue. void viewCG() const; - /// Checks if the graph is OK. + /// Dump the connection graph to a DOT file for remote debugging. + void dumpCG() const; + + /// Checks if the graph is valid and complete. void verify() const; - /// Just verifies the graph structure. This function can also be called - /// during the graph is modified, e.g. in mergeAllScheduledNodes(). - void verifyStructure() const; + /// Just verifies the graph nodes. + void verifyStructure(bool allowMerge = false) const; friend struct ::CGForDotView; friend class EscapeAnalysis; + friend struct CGNodeWorklist; }; + using Traversal = ConnectionGraph::Traversal; private: /// All the information we keep for a function. struct FunctionInfo : public FunctionInfoBase { - FunctionInfo(SILFunction *F) : Graph(F, false), SummaryGraph(F, true) { } + FunctionInfo(SILFunction *F, EscapeAnalysis *EA) + : Graph(F, EA, false), SummaryGraph(F, EA, true) {} /// The connection graph for the function. This is what clients of the /// analysis will see. @@ -795,6 +961,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis { MaxGraphMerges = 4 }; + using PointerKindCache = llvm::DenseMap; + /// The connection graphs for all functions (does not include external /// functions). llvm::DenseMap Function2Info; @@ -803,7 +971,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { llvm::SpecificBumpPtrAllocator Allocator; /// Cache for isPointer(). - llvm::DenseMap isPointerCache; + PointerKindCache pointerKindCache; SILModule *M; @@ -813,21 +981,31 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Callee analysis, used for determining the callees at call sites. BasicCalleeAnalysis *BCA; - /// Returns true if \p V may encapsulate a "pointer" value. - /// See EscapeAnalysis::NodeType::Value. - bool isPointer(ValueBase *V); + /// If EscapeAnalysis should consider the given value to be a derived address + /// or pointer based on one of its address or pointer operands, then return + /// that operand value. Otherwise, return an invalid value. + SILValue getPointerBase(SILValue value); + + /// Recursively find the given value's pointer base. If the value cannot be + /// represented in EscapeAnalysis as one of its operands, then return the same + /// value. + SILValue getPointerRoot(SILValue value); + + PointerKind findRecursivePointerKind(SILType Ty, const SILFunction &F) const; - /// If \p pointer is a pointer, set it to global escaping. - void setEscapesGlobal(ConnectionGraph *ConGraph, ValueBase *pointer) { - if (CGNode *Node = ConGraph->getNode(pointer, this)) - ConGraph->setEscapesGlobal(Node); + PointerKind findCachedPointerKind(SILType Ty, const SILFunction &F) const; + + // Returns true if the type \p Ty must be a reference or must transitively + // contain a reference and no other pointer or address type. + bool hasReferenceOnly(SILType Ty, const SILFunction &F) const { + return findCachedPointerKind(Ty, F) <= ReferenceOnly; } /// Gets or creates FunctionEffects for \p F. FunctionInfo *getFunctionInfo(SILFunction *F) { FunctionInfo *&FInfo = Function2Info[F]; if (!FInfo) - FInfo = new (Allocator.Allocate()) FunctionInfo(F); + FInfo = new (Allocator.Allocate()) FunctionInfo(F, this); return FInfo; } @@ -852,6 +1030,34 @@ class EscapeAnalysis : public BottomUpIPAnalysis { void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth); + // @_semantics("array.uninitialized") takes a reference to the storage and + // returns an instantiated array struct and unsafe pointer to the elements. + struct ArrayUninitCall { + SILValue arrayStorageRef; + TupleExtractInst *arrayStruct = nullptr; + TupleExtractInst *arrayElementPtr = nullptr; + + bool isValid() const { + return arrayStorageRef && arrayStruct && arrayElementPtr; + } + }; + + /// If \p ai is an optimizable @_semantics("array.uninitialized") call, return + /// valid call information. + ArrayUninitCall + canOptimizeArrayUninitializedCall(ApplyInst *ai, + const ConnectionGraph *conGraph); + + /// Return true of this tuple_extract is the result of an optimizable + /// @_semantics("array.uninitialized") call. + bool canOptimizeArrayUninitializedResult(TupleExtractInst *tei); + + /// Handle a call to "@_semantics(array.uninitialized") precisely by mapping + /// each call result to a separate graph node and relating them to the + /// argument. + void createArrayUninitializedSubgraph(ArrayUninitCall call, + ConnectionGraph *conGraph); + /// Updates the graph by analyzing instruction \p I. /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth /// reaches MaxRecursionDepth. @@ -884,10 +1090,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis { bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph); - /// Returns true if the value \p V can escape to the \p UsePoint, where - /// \p UsePoint is either a release-instruction or a function call. - bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph); + /// Returns true if the value \p value or any address within that value can + /// escape to the \p usePoint, where \p usePoint is either a + /// release-instruction or a function call. + bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint, + ConnectionGraph *conGraph); friend struct ::CGForDotView; @@ -908,6 +1115,19 @@ class EscapeAnalysis : public BottomUpIPAnalysis { return &FInfo->Graph; } + /// Return \p value's PointerKind. + PointerKind getPointerKind(ValueBase *value) const { + auto *f = value->getFunction(); + // The function can be null, e.g. if V is an undef. + if (!f) + return NoPointer; + + return findCachedPointerKind(value->getType(), *f); + } + /// Returns true if \p V may encapsulate a "pointer" value. + /// See EscapeAnalysis::NodeType::Value. + bool isPointer(ValueBase *V) const { return getPointerKind(V) > NoPointer; } + /// Returns true if the value \p V can escape to the function call \p FAS. /// This means that the called function may access the value \p V. /// If \p V has reference semantics, this function returns false if only the @@ -920,24 +1140,16 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Note that if \p RI is a retain-instruction always false is returned. bool canEscapeTo(SILValue V, RefCountingInst *RI); - /// Returns true if the value \p V can escape to any other pointer \p To. - /// This means that either \p To is the same as \p V or contains a reference - /// to \p V. - bool canEscapeToValue(SILValue V, SILValue To); - - /// Returns true if the parameter with index \p ParamIdx can escape in the - /// called function of apply site \p FAS. - /// If it is an indirect parameter and \p checkContentOfIndirectParam is true - /// then the escape status is not checked for the address itself but for the - /// referenced pointer (if the referenced type is a pointer). - bool canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam); + /// Return true if \p releasedReference deinitialization may release memory + /// pointed to by \p accessedAddress. + bool mayReleaseContent(SILValue releasedReference, SILValue accessedAddress); /// Returns true if the pointers \p V1 and \p V2 can possibly point to the /// same memory. - /// If at least one of the pointers refers to a local object and the - /// connection-graph-nodes of both pointers do not point to the same content - /// node, the pointers do not alias. + /// + /// First checks that the pointers are known not to alias outside this + /// function, then checks the connection graph to determine that their content + /// is not in the same points-to chain based on access inside this function. bool canPointToSameMemory(SILValue V1, SILValue V2); /// Invalidate all information in this analysis. @@ -962,25 +1174,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis { virtual bool needsNotifications() override { return true; } - virtual void verify() const override { -#ifndef NDEBUG - for (auto Iter : Function2Info) { - FunctionInfo *FInfo = Iter.second; - FInfo->Graph.verify(); - FInfo->SummaryGraph.verify(); - } -#endif - } - - virtual void verify(SILFunction *F) const override { -#ifndef NDEBUG - if (FunctionInfo *FInfo = Function2Info.lookup(F)) { - FInfo->Graph.verify(); - FInfo->SummaryGraph.verify(); - } -#endif - } + virtual void verify() const override; + virtual void verify(SILFunction *F) const override; }; } // end namespace swift diff --git a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h index bc70dd7a31292..69c0b7718e1cc 100644 --- a/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/TypeExpansionAnalysis.h @@ -12,6 +12,7 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_TYPEEXPANSIONANALYSIS_H #define SWIFT_SILOPTIMIZER_ANALYSIS_TYPEEXPANSIONANALYSIS_H +#include "swift/AST/TypeExpansionContext.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILType.h" #include "swift/SIL/SILValue.h" @@ -22,7 +23,9 @@ namespace swift { /// This analysis determines memory effects during destruction. class TypeExpansionAnalysis : public SILAnalysis { - llvm::DenseMap ExpansionCache; + llvm::DenseMap, ProjectionPathList> + ExpansionCache; + public: TypeExpansionAnalysis(SILModule *M) : SILAnalysis(SILAnalysisKind::TypeExpansion) {} @@ -32,7 +35,8 @@ class TypeExpansionAnalysis : public SILAnalysis { } /// Return ProjectionPath to every leaf or intermediate node of the given type. - const ProjectionPathList &getTypeExpansion(SILType B, SILModule *Mod); + const ProjectionPathList &getTypeExpansion(SILType B, SILModule *Mod, + TypeExpansionContext context); /// Invalidate all information in this analysis. virtual void invalidate() override { diff --git a/include/swift/SILOptimizer/Analysis/ValueTracking.h b/include/swift/SILOptimizer/Analysis/ValueTracking.h index 16b1cee2f9fd7..cb13571026b4d 100644 --- a/include/swift/SILOptimizer/Analysis/ValueTracking.h +++ b/include/swift/SILOptimizer/Analysis/ValueTracking.h @@ -17,6 +17,7 @@ #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_VALUETRACKING_H #define SWIFT_SILOPTIMIZER_ANALYSIS_VALUETRACKING_H +#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" @@ -24,18 +25,33 @@ namespace swift { /// Returns true if \p V is a function argument which may not alias to /// any other pointer in the function. -/// The \p assumeInoutIsNotAliasing specifies in no-aliasing is assumed for -/// the @inout convention. See swift::isNotAliasedIndirectParameter(). -bool isNotAliasingArgument(SILValue V, InoutAliasingAssumption isInoutAliasing = - InoutAliasingAssumption::Aliasing); +/// +/// This does not look through any projections. The caller must do that. +bool isExclusiveArgument(SILValue V); -/// Returns true if \p V is local inside its function. This means its underlying -/// object either is a non-aliasing function argument or a locally allocated -/// object. -/// The \p assumeInoutIsNotAliasing specifies in no-aliasing is assumed for -/// the @inout convention. See swift::isNotAliasedIndirectParameter(). -bool pointsToLocalObject(SILValue V, InoutAliasingAssumption isInoutAliasing = - InoutAliasingAssumption::Aliasing); +/// Returns true if \p V is a locally allocated object. +/// +/// Note: this may look through a single level of indirection (via +/// ref_element_addr) when \p V is the address of a class property. However, it +/// does not look through init/open_existential_addr. +bool pointsToLocalObject(SILValue V); + +/// Returns true if \p V is a uniquely identified address or reference. It may +/// be any of: +/// +/// - an address projection based on a locally allocated address with no +/// indirection +/// +/// - a locally allocated reference, or an address projection based on that +/// reference with one level of indirection (an address into the locally +/// allocated object). +/// +/// - an address projection based on an exclusive argument with no levels of +/// indirection (e.g. ref_element_addr, project_box, etc.). +inline bool isUniquelyIdentified(SILValue V) { + return pointsToLocalObject(V) + || isExclusiveArgument(getUnderlyingAddressRoot(V)); +} enum class IsZeroKind { Zero, diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index 971d851284602..ab90ef989c95f 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -97,7 +97,7 @@ class SILPassManager { /// If true, passes are also run for functions which have /// OptimizationMode::NoOptimization. - bool isMandatoryPipeline = false; + bool isMandatory = false; /// The IRGen SIL passes. These have to be dynamically added by IRGen. llvm::DenseMap IRGenPasses; @@ -114,16 +114,15 @@ class SILPassManager { /// C'tor. It creates and registers all analysis passes, which are defined /// in Analysis.def. /// - /// If \p isMandatoryPipeline is true, passes are also run for functions + /// If \p isMandatory is true, passes are also run for functions /// which have OptimizationMode::NoOptimization. SILPassManager(SILModule *M, llvm::StringRef Stage = "", - bool isMandatoryPipeline = false); + bool isMandatory = false); /// C'tor. It creates an IRGen pass manager. Passes can query for the /// IRGenModule. SILPassManager(SILModule *M, irgen::IRGenModule *IRMod, - llvm::StringRef Stage = "", - bool isMandatoryPipeline = false); + llvm::StringRef Stage = "", bool isMandatory = false); const SILOptions &getOptions() const; diff --git a/include/swift/SILOptimizer/PassManager/PassPipeline.def b/include/swift/SILOptimizer/PassManager/PassPipeline.def index 005943e86582b..12132e4d180bb 100644 --- a/include/swift/SILOptimizer/PassManager/PassPipeline.def +++ b/include/swift/SILOptimizer/PassManager/PassPipeline.def @@ -30,6 +30,7 @@ PASSPIPELINE(Onone, "Passes run at -Onone") PASSPIPELINE(InstCount, "Utility pipeline to just run the inst count pass") PASSPIPELINE(Lowering, "SIL Address Lowering") PASSPIPELINE(IRGenPrepare, "Pipeline to run during IRGen") +PASSPIPELINE(SerializeSIL, "Utility pipeline that just runs SerializeSILPass ") #undef PASSPIPELINE_WITH_OPTIONS #undef PASSPIPELINE diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 96f891f770501..b879b3a666795 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -64,6 +64,8 @@ PASS(AccessEnforcementSelection, "access-enforcement-selection", "Access Enforcement Selection") PASS(AccessEnforcementWMO, "access-enforcement-wmo", "Access Enforcement Whole Module Optimization") +PASS(CrossModuleSerializationSetup, "cross-module-serialization-setup", + "Setup serialization flags for cross-module optimization") PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") PASS(AccessedStorageDumper, "accessed-storage-dump", @@ -198,6 +200,8 @@ PASS(LICM, "licm", "Loop Invariant Code Motion") PASS(LateCodeMotion, "late-codemotion", "Late Code Motion with Release Hoisting") +PASS(LateDeadFunctionElimination, "late-deadfuncelim", + "Late Dead Function Elimination") PASS(LateInliner, "late-inline", "Late Function Inlining") PASS(LoopCanonicalizer, "loop-canonicalizer", @@ -230,8 +234,6 @@ PASS(NoReturnFolding, "noreturn-folding", "Prune Control Flow at No-Return Calls Using SIL unreachable") PASS(ObjectOutliner, "object-outliner", "Outlining of Global Objects") -PASS(OpaqueArchetypeSpecializer, "opaque-archetype-specializer", - "Opaque archetype specializer") PASS(Outliner, "outliner", "Function Outlining Optimization") PASS(OwnershipModelEliminator, "ownership-model-eliminator", @@ -295,7 +297,7 @@ PASS(StackPromotion, "stack-promotion", "Stack Promotion of Class Objects") PASS(StripDebugInfo, "strip-debug-info", "Strip Debug Information") -PASS(SwiftArrayOpts, "array-specialize", +PASS(SwiftArrayPropertyOpt, "array-property-opt", "Loop Specialization for Array Properties") PASS(UnsafeGuaranteedPeephole, "unsafe-guaranteed-peephole", "SIL retain/release Peephole Removal for Builtin.unsafeGuaranteed") @@ -305,12 +307,12 @@ PASS(OwnershipDumper, "ownership-dumper", "Print Ownership information for Testing") PASS(SemanticARCOpts, "semantic-arc-opts", "Semantic ARC Optimization") -PASS(MarkUninitializedFixup, "mark-uninitialized-fixup", - "Temporary pass for staging in mark_uninitialized changes.") PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks", "Utility pass. Removes all non-term insts from blocks with unreachable terms") PASS(SerializeSILPass, "serialize-sil", "Utility pass. Serializes the current SILModule") +PASS(CMOSerializeSILPass, "cmo-serialize-sil", + "Utility pass. Serializes the current SILModule for cross-module-optimization") PASS(NonInlinableFunctionSkippingChecker, "check-non-inlinable-function-skipping", "Utility pass to ensure -experimental-skip-non-inlinable-function-bodies " "skips everything it should") diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index 2d0bb0e457892..77e5e4b0e2780 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -22,9 +22,10 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H #define SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H -#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILCloner.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" namespace swift { @@ -61,12 +62,65 @@ bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo, SILLoopInfo *loopInfo, bool rotateSingleBlockLoops, SILBasicBlock *upToBB, bool shouldVerify); -/// Helper function to perform SSA updates in case of jump threading. -void updateSSAAfterCloning(BasicBlockCloner &cloner, SILBasicBlock *srcBB, - SILBasicBlock *destBB); +/// Sink address projections to their out-of-block uses. This is +/// required after cloning a block and before calling +/// updateSSAAfterCloning to avoid address-type phis. +/// +/// This clones address projections at their use points, but does not +/// mutate the block containing the projections. +/// +/// BasicBlockCloner handles this internally. +class SinkAddressProjections { + // Projections ordered from last to first in the chain. + SmallVector projections; + SmallSetVector inBlockDefs; + + // Transient per-projection data for use during cloning. + SmallVector usesToReplace; + llvm::SmallDenseMap firstBlockUse; + +public: + /// Check for an address projection chain ending at \p inst. Return true if + /// the given instruction is successfully analyzed. + /// + /// If \p inst does not produce an address, then return + /// true. getInBlockDefs() will contain \p inst if any of its + /// (non-address) values are used outside its block. + /// + /// If \p inst does produce an address, return true only of the + /// chain of address projections within this block is clonable at + /// their use sites. getInBlockDefs will return all non-address + /// operands in the chain that are also defined in this block. These + /// may require phis after cloning the projections. + bool analyzeAddressProjections(SILInstruction *inst); + + /// After analyzing projections, returns the list of (non-address) values + /// defined in the same block as the projections which will have uses outside + /// the block after cloning. + ArrayRef getInBlockDefs() const { + return inBlockDefs.getArrayRef(); + } + /// Clone the chain of projections at their use sites. + /// + /// Return true if anything was done. + /// + /// getInBlockProjectionOperandValues() can be called before or after cloning. + bool cloneProjections(); +}; /// Clone a single basic block and any required successor edges within the same /// function. +/// +/// Before cloning, call either canCloneBlock or call canCloneInstruction for +/// every instruction in the original block. +/// +/// To clone just the block, call cloneBlock. To also update the original +/// block's branch to jump to the newly cloned block, call cloneBranchTarget +/// instead. +/// +/// After cloning, call splitCriticalEdges, then updateSSAAfterCloning. This is +/// decoupled from cloning becaused some clients perform CFG edges updates after +/// cloning but before splitting CFG edges. class BasicBlockCloner : public SILCloner { using SuperTy = SILCloner; friend class SILCloner; @@ -75,18 +129,56 @@ class BasicBlockCloner : public SILCloner { /// The original block to be cloned. SILBasicBlock *origBB; + /// Will cloning require an SSA update? + bool needsSSAUpdate = false; + + /// Transient object for analyzing a single address projction chain. It's + /// state is reset each time analyzeAddressProjections is called. + SinkAddressProjections sinkProj; + public: /// An ordered list of old to new available value pairs. /// /// updateSSAAfterCloning() expects this public field to hold values that may /// be remapped in the cloned block and live out. - SmallVector, 16> AvailVals; + SmallVector, 16> availVals; // Clone blocks starting at `origBB`, within the same function. BasicBlockCloner(SILBasicBlock *origBB) : SILCloner(*origBB->getParent()), origBB(origBB) {} + bool canCloneBlock() { + for (auto &inst : *origBB) { + if (!canCloneInstruction(&inst)) + return false; + } + return true; + } + + /// Returns true if \p inst can be cloned. + /// + /// If canCloneBlock is not called, then this must be called for every + /// instruction in origBB, both to ensure clonability and to handle internal + /// book-keeping (needsSSAUpdate). + bool canCloneInstruction(SILInstruction *inst) { + assert(inst->getParent() == origBB); + + if (!inst->isTriviallyDuplicatable()) + return false; + + if (!sinkProj.analyzeAddressProjections(inst)) + return false; + + // Check if any of the non-address defs in the cloned block (including the + // current instruction) will still have uses outside the block after sinking + // address projections. + needsSSAUpdate |= !sinkProj.getInBlockDefs().empty(); + return true; + } + void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) { + sinkAddressProjections(); + SmallVector successorBBs; successorBBs.reserve(origBB->getSuccessors().size()); llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs)); @@ -95,6 +187,9 @@ class BasicBlockCloner : public SILCloner { /// Clone the given branch instruction's destination block, splitting /// its successors, and rewrite the branch instruction. + /// + /// Return false if the branch's destination block cannot be cloned. When + /// false is returned, no changes have been made. void cloneBranchTarget(BranchInst *bi) { assert(origBB == bi->getDestBB()); @@ -110,10 +205,16 @@ class BasicBlockCloner : public SILCloner { return remapBasicBlock(origBB); } + bool wasCloned() { return isBlockCloned(origBB); } + /// Call this after processing all instructions to fix the control flow /// graph. The branch cloner may have left critical edges. bool splitCriticalEdges(DominanceInfo *domInfo, SILLoopInfo *loopInfo); + /// Helper function to perform SSA updates after calling both + /// cloneBranchTarget and splitCriticalEdges. + void updateSSAAfterCloning(); + protected: // MARK: CRTP overrides. @@ -137,8 +238,10 @@ class BasicBlockCloner : public SILCloner { void mapValue(SILValue origValue, SILValue mappedValue) { SuperTy::mapValue(origValue, mappedValue); - AvailVals.emplace_back(origValue, mappedValue); + availVals.emplace_back(origValue, mappedValue); } + + void sinkAddressProjections(); }; // Helper class that provides a callback that can be used in @@ -173,46 +276,6 @@ class CloneCollector { } }; -/// Sink address projections to their out-of-block uses. This is -/// required after cloning a block and before calling -/// updateSSAAfterCloning to avoid address-type phis. -/// -/// This clones address projections at their use points, but does not -/// mutate the block containing the projections. -class SinkAddressProjections { - // Projections ordered from last to first in the chain. - SmallVector projections; - SmallSetVector inBlockDefs; - -public: - /// Check for an address projection chain ending at \p inst. Return true if - /// the given instruction is successfully analyzed. - /// - /// If \p inst does not produce an address, then return - /// true. getInBlockDefs() will contain \p inst if any of its - /// (non-address) values are used outside its block. - /// - /// If \p inst does produce an address, return true only of the - /// chain of address projections within this block is clonable at - /// their use sites. getInBlockDefs will return all non-address - /// operands in the chain that are also defined in this block. These - /// may require phis after cloning the projections. - bool analyzeAddressProjections(SILInstruction *inst); - - /// After analyzing projections, returns the list of (non-address) values - /// defined in the same block as the projections which will have uses outside - /// the block after cloning. - ArrayRef getInBlockDefs() const { - return inBlockDefs.getArrayRef(); - } - /// Clone the chain of projections at their use sites. - /// - /// Return true if anything was done. - /// - /// getInBlockProjectionOperandValues() can be called before or after cloning. - bool cloneProjections(); -}; - /// Utility class for cloning init values into the static initializer of a /// SILGlobalVariable. class StaticInitCloner : public SILCloner { diff --git a/include/swift/SILOptimizer/Utils/CFGOptUtils.h b/include/swift/SILOptimizer/Utils/CFGOptUtils.h index 9c3f8189ff73b..120d19ec9eb9e 100644 --- a/include/swift/SILOptimizer/Utils/CFGOptUtils.h +++ b/include/swift/SILOptimizer/Utils/CFGOptUtils.h @@ -158,36 +158,6 @@ bool mergeBasicBlockWithSuccessor(SILBasicBlock *bb, DominanceInfo *domInfo, /// quadratic. bool mergeBasicBlocks(SILFunction *f); -/// Given a list of \p UserBlocks and a list of \p DefBlocks, find a set of -/// blocks that together with \p UserBlocks joint-postdominate \p -/// DefBlocks. This is in a sense finding a set of blocks that "complete" \p -/// UserBlocks with respect to \p DefBlocks. As an example, for the following -/// CFG: -/// -/// +-----+ -/// | Def | -/// +-----+ -/// | | -/// v v -/// +-----+ +-----+ -/// | | | Use | -/// +-----+ +-----+ -/// -/// the completion of the joint post-dominance set would be the empty -/// block. This has two main uses: -/// -/// 1. This can tell you the places where if you were to sink the Def one would -/// need to insert "compensating code". -/// -/// 2. It can be used to prove ownership correctness of @owned values by -/// asserting that the set of UserBlocks has an empty completion, implying they -/// jointly-post dominate the def. -/// -/// *NOTE* This completion may not be unique. -void completeJointPostDominanceSet( - ArrayRef userBlocks, ArrayRef defBlocks, - llvm::SmallVectorImpl &completion); - /// Return true if we conservatively find all bb's that are non-failure exit /// basic blocks and place them in \p bbs. If we find something we don't /// understand, bail. diff --git a/include/swift/SILOptimizer/Utils/ConstExpr.h b/include/swift/SILOptimizer/Utils/ConstExpr.h index cb6cac6b97a6e..aa2cf66a086c7 100644 --- a/include/swift/SILOptimizer/Utils/ConstExpr.h +++ b/include/swift/SILOptimizer/Utils/ConstExpr.h @@ -187,11 +187,6 @@ class ConstExprStepEvaluator { Optional lookupConstValue(SILValue value); - /// Returns true if and only if `errorVal` denotes an error that requires - /// aborting interpretation and returning the error. Skipping an instruction - /// that produces such errors is not a valid behavior. - bool isFailStopError(SymbolicValue errorVal); - /// Return the number of instructions evaluated for the last `evaluate` /// operation. This could be used by the clients to limit the number of /// instructions that should be evaluated by the step-wise evaluator. @@ -207,9 +202,25 @@ class ConstExprStepEvaluator { const SmallPtrSetImpl &getFuncsCalledDuringEvaluation() { return evaluator.getFuncsCalledDuringEvaluation(); } + + /// Dump the internal state to standard error for debugging. + void dumpState(); }; +bool hasConstantEvaluableAnnotation(SILFunction *fun); + +bool isConstantEvaluable(SILFunction *fun); + +/// Return true if and only if the given function \p fun is specially modeled +/// by the constant evaluator. These are typically functions in the standard +/// library, such as String.+=, Array.append, whose semantics is built into the +/// evaluator. bool isKnownConstantEvaluableFunction(SILFunction *fun); +/// Return true if and only if \p errorVal denotes an error that requires +/// aborting interpretation and returning the error. Skipping an instruction +/// that produces such errors is not a valid behavior. +bool isFailStopError(SymbolicValue errorVal); + } // end namespace swift #endif diff --git a/include/swift/SILOptimizer/Utils/Existential.h b/include/swift/SILOptimizer/Utils/Existential.h index 39cb1beb302ae..75c88cf388f0a 100644 --- a/include/swift/SILOptimizer/Utils/Existential.h +++ b/include/swift/SILOptimizer/Utils/Existential.h @@ -98,8 +98,7 @@ struct ConcreteExistentialInfo { // Do a conformance lookup on ConcreteType with the given requirement, P. If P // is satisfiable based on the existential's conformance, return the new // conformance on P. Otherwise return None. - Optional - lookupExistentialConformance(ProtocolDecl *P) const { + ProtocolConformanceRef lookupExistentialConformance(ProtocolDecl *P) const { CanType selfTy = P->getSelfInterfaceType()->getCanonicalType(); return ExistentialSubs.lookupConformance(selfTy, P); } diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 50c80b51578d3..22af588a16532 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -53,10 +53,14 @@ void trySpecializeApplyOfGeneric( /// Specifically, it contains information which formal parameters and returns /// are changed from indirect values to direct values. class ReabstractionInfo { - /// A 1-bit means that this parameter/return value is converted from indirect - /// to direct. + /// A 1-bit means that this argument (= either indirect return value or + /// parameter) is converted from indirect to direct. SmallBitVector Conversions; + /// For each bit set in Conversions, there is a bit set in TrivialArgs if the + /// argument has a trivial type. + SmallBitVector TrivialArgs; + /// If set, indirect to direct conversions should be performed by the generic /// specializer. bool ConvertIndirectToDirect; @@ -100,6 +104,11 @@ class ReabstractionInfo { // Reference to the original generic non-specialized callee function. SILFunction *Callee; + // The module the specialization is created in. + ModuleDecl *TargetModule = nullptr; + + bool isWholeModule = false; + // The apply site which invokes the generic function. ApplySite Apply; @@ -116,6 +125,10 @@ class ReabstractionInfo { // Is the generated specialization going to be serialized? IsSerialized_t Serialized; + + unsigned param2ArgIndex(unsigned ParamIdx) const { + return ParamIdx + NumFormalIndirectResults; + } // Create a new substituted type with the updated signature. CanSILFunctionType createSubstitutedType(SILFunction *OrigF, @@ -140,7 +153,9 @@ class ReabstractionInfo { /// substitutions \p ParamSubs. /// If specialization is not possible getSpecializedType() will return an /// invalid type. - ReabstractionInfo(ApplySite Apply, SILFunction *Callee, + ReabstractionInfo(ModuleDecl *targetModule, + bool isModuleWholeModule, + ApplySite Apply, SILFunction *Callee, SubstitutionMap ParamSubs, IsSerialized_t Serialized, bool ConvertIndirectToDirect = true, @@ -148,23 +163,23 @@ class ReabstractionInfo { /// Constructs the ReabstractionInfo for generic function \p Callee with /// a specialization signature. - ReabstractionInfo(SILFunction *Callee, GenericSignature SpecializedSig); + ReabstractionInfo(ModuleDecl *targetModule, bool isModuleWholeModule, + SILFunction *Callee, GenericSignature SpecializedSig); IsSerialized_t isSerialized() const { return Serialized; } - ResilienceExpansion getResilienceExpansion() const { - return (Serialized - ? ResilienceExpansion::Minimal - : ResilienceExpansion::Maximal); + TypeExpansionContext getResilienceExpansion() const { + auto resilience = (Serialized ? ResilienceExpansion::Minimal + : ResilienceExpansion::Maximal); + return TypeExpansionContext(resilience, TargetModule, isWholeModule); } /// Returns true if the \p ParamIdx'th (non-result) formal parameter is /// converted from indirect to direct. bool isParamConverted(unsigned ParamIdx) const { - return ConvertIndirectToDirect && - Conversions.test(ParamIdx + NumFormalIndirectResults); + return ConvertIndirectToDirect && isArgConverted(param2ArgIndex(ParamIdx)); } /// Returns true if the \p ResultIdx'th formal result is converted from diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index 5d1af19272d1a..d67600dbd17ef 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -56,8 +56,119 @@ NullablePtr createIncrementBefore(SILValue ptr, NullablePtr createDecrementBefore(SILValue ptr, SILInstruction *insertpt); +/// A utility for deleting one or more instructions belonging to a function, and +/// cleaning up any dead code resulting from deleting those instructions. Use +/// this utility instead of +/// \c recursivelyDeleteTriviallyDeadInstruction. +class InstructionDeleter { +private: + // A set vector of instructions that are found to be dead. The ordering + // of instructions in this set is important as when a dead instruction is + // removed, new instructions will be generated to fix the lifetime of the + // instruction's operands. This has to be deterministic. + SmallSetVector deadInstructions; + + void deleteInstruction(SILInstruction *inst, + llvm::function_ref callback, + bool fixOperandLifetimes); + +public: + InstructionDeleter() {} + + /// If the instruction \p inst is dead, record it so that it can be cleaned + /// up. + void trackIfDead(SILInstruction *inst); + + /// If the instruction \p inst is dead, delete it immediately and record + /// its operands so that they can be cleaned up later. + void deleteIfDead( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. This function will add necessary + /// ownership instructions to fix the lifetimes of the operands of \c inst to + /// compensate for its deletion. This function will not clean up dead code + /// resulting from the instruction's removal. To do so, invoke the method \c + /// cleanupDeadCode of this instance, once the SIL of the contaning function + /// is made consistent. + /// + /// \pre the function containing \c inst must be using ownership SIL. + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDeleteAndFixLifetimes( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Delete the instruction \p inst and record instructions that may become + /// dead because of the removal of \c inst. If in ownership SIL, use the + /// \c forceDeleteAndFixLifetimes function instead, unless under special + /// circumstances where the client must handle fixing lifetimes of the + /// operands of the deleted instructions. This function will not fix the + /// lifetimes of the operands of \c inst once it is deleted. This function + /// will not clean up dead code resulting from the instruction's removal. To + /// do so, invoke the method \c cleanupDeadCode of this instance, once the SIL + /// of the contaning function is made consistent. + /// + /// \pre the instruction to be deleted must not have any use other than + /// incidental uses. + /// + /// \param callback a callback called whenever an instruction + /// is deleted. + void forceDelete( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Clean up dead instructions that are tracked by this instance and all + /// instructions that transitively become dead. + /// + /// \pre the function contaning dead instructions must be consistent (i.e., no + /// under or over releases). Note that if \c forceDelete call leaves the + /// function body in an inconsistent state, it needs to be made consistent + /// before this method is invoked. + /// + /// \param callback a callback called whenever an instruction is deleted. + void + cleanUpDeadInstructions(llvm::function_ref callback = + [](SILInstruction *) {}); + + /// Recursively visit users of \c inst (including \c inst)and delete + /// instructions that are dead (including \c inst). Invoke the \c callback on + /// instructions that are deleted. + void recursivelyDeleteUsersIfDead( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); +}; + +/// If \c inst is dead, delete it and recursively eliminate all code that +/// becomes dead because of that. If more than one instruction must +/// be checked/deleted use the \c InstructionDeleter utility. +/// +/// This function will add necessary compensation code to fix the lifetimes of +/// the operands of the deleted instructions. +/// +/// \pre the SIL function containing the instruction is assumed to be +/// consistent, i.e., does not have under or over releases. +/// +/// \param callback a callback called whenever an instruction is deleted. +void eliminateDeadInstruction( + SILInstruction *inst, llvm::function_ref callback = + [](SILInstruction *) {}); + +/// Return the number of @inout arguments passed to the given apply site. +unsigned getNumInOutArguments(FullApplySite applySite); + /// For each of the given instructions, if they are dead delete them -/// along with their dead operands. +/// along with their dead operands. Note this utility must be phased out and +/// replaced by \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The ArrayRef of instructions to be deleted. /// \param force If Force is set, don't check if the top level instructions @@ -69,7 +180,9 @@ void recursivelyDeleteTriviallyDeadInstructions( }); /// If the given instruction is dead, delete it along with its dead -/// operands. +/// operands. Note this utility must be phased out and replaced by +/// \c eliminateDeadInstruction and +/// \c InstructionDeleter utilities. /// /// \param inst The instruction to be deleted. /// \param force If Force is set, don't check if the top level instruction is @@ -161,12 +274,21 @@ bool tryCheckedCastBrJumpThreading( /// A structure containing callbacks that are called when an instruction is /// removed or added. struct InstModCallbacks { - using CallbackTy = std::function; - CallbackTy deleteInst = [](SILInstruction *inst) { inst->eraseFromParent(); }; - CallbackTy createdNewInst = [](SILInstruction *) {}; - - InstModCallbacks(CallbackTy deleteInst, CallbackTy createdNewInst) - : deleteInst(deleteInst), createdNewInst(createdNewInst) {} + std::function deleteInst = [](SILInstruction *inst) { + inst->eraseFromParent(); + }; + std::function createdNewInst = [](SILInstruction *) { + }; + std::function replaceValueUsesWith = + [](SILValue oldValue, SILValue newValue) { + oldValue->replaceAllUsesWith(newValue); + }; + + InstModCallbacks(decltype(deleteInst) deleteInst, + decltype(createdNewInst) createdNewInst, + decltype(replaceValueUsesWith) replaceValueUsesWith) + : deleteInst(deleteInst), createdNewInst(createdNewInst), + replaceValueUsesWith(replaceValueUsesWith) {} InstModCallbacks() = default; ~InstModCallbacks() = default; InstModCallbacks(const InstModCallbacks &) = default; @@ -316,6 +438,10 @@ void replaceLoadSequence(SILInstruction *inst, SILValue value); /// be reached by calling the function represented by Decl? bool calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl); +/// Do we have enough information to determine all callees that could +/// be reached by calling the function represented by Decl? +bool calleesAreStaticallyKnowable(SILModule &module, AbstractFunctionDecl *afd); + // Attempt to get the instance for , whose static type is the same as // its exact dynamic type, returning a null SILValue() if we cannot find it. // The information that a static type is the same as the exact dynamic, @@ -384,6 +510,13 @@ struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult { Optional findLocalApplySites(FunctionRefBaseInst *fri); +/// Gets the base implementation of a method. +AbstractFunctionDecl *getBaseMethod(AbstractFunctionDecl *FD); + +SILInstruction * +tryOptimizeApplyOfPartialApply(PartialApplyInst *pai, SILBuilder &builder, + InstModCallbacks callbacks = InstModCallbacks()); + } // end namespace swift #endif diff --git a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h index f11fbcd3042af..5a25557693b06 100644 --- a/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h +++ b/include/swift/SILOptimizer/Utils/LoadStoreOptUtils.h @@ -169,12 +169,15 @@ class LSBase { } /// Print the LSBase. - virtual void print(llvm::raw_ostream &os, SILModule *Mod) { + virtual void print(llvm::raw_ostream &os, SILModule *Mod, + TypeExpansionContext context) { os << Base; - Path.getValue().print(os, *Mod); + Path.getValue().print(os, *Mod, context); } - virtual void dump(SILModule *Mod) { print(llvm::dbgs(), Mod); } + virtual void dump(SILModule *Mod, TypeExpansionContext context) { + print(llvm::dbgs(), Mod, context); + } }; static inline llvm::hash_code hash_value(const LSBase &S) { @@ -257,16 +260,18 @@ class LSValue : public LSBase { return Path.getValue().createExtract(Base, Inst, true); } - void print(llvm::raw_ostream &os, SILModule *Mod) { + void print(llvm::raw_ostream &os, SILModule *Mod, + TypeExpansionContext context) { if (CoveringValue) { os << "Covering Value"; return; } - LSBase::print(os, Mod); + LSBase::print(os, Mod, context); } /// Expand this SILValue to all individual fields it contains. - static void expand(SILValue Base, SILModule *Mod, LSValueList &Vals, + static void expand(SILValue Base, SILModule *Mod, + TypeExpansionContext context, LSValueList &Vals, TypeExpansionAnalysis *TE); /// Given a memory location and a map between the expansions of the location @@ -329,13 +334,14 @@ class LSLocation : public LSBase { } /// Returns the type of the object the LSLocation represents. - SILType getType(SILModule *M) { - return Path.getValue().getMostDerivedType(*M); + SILType getType(SILModule *M, TypeExpansionContext context) { + return Path.getValue().getMostDerivedType(*M, context); } /// Get the first level locations based on this location's first level /// projection. - void getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod); + void getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod, + TypeExpansionContext context); /// Check whether the 2 LSLocations may alias each other or not. bool isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA); @@ -351,15 +357,18 @@ class LSLocation : public LSBase { /// In SIL, we can have a store to an aggregate and loads from its individual /// fields. Therefore, we expand all the operations on aggregates onto /// individual fields and process them separately. - static void expand(LSLocation Base, SILModule *Mod, LSLocationList &Locs, + static void expand(LSLocation Base, SILModule *Mod, + TypeExpansionContext context, LSLocationList &Locs, TypeExpansionAnalysis *TE); /// Given a set of locations derived from the same base, try to merge/reduce /// them into smallest number of LSLocations possible. - static void reduce(LSLocation Base, SILModule *Mod, LSLocationList &Locs); + static void reduce(LSLocation Base, SILModule *Mod, + TypeExpansionContext context, LSLocationList &Locs); /// Enumerate the given Mem LSLocation. - static void enumerateLSLocation(SILModule *M, SILValue Mem, + static void enumerateLSLocation(TypeExpansionContext context, SILModule *M, + SILValue Mem, std::vector &LSLocationVault, LSLocationIndexMap &LocToBit, LSLocationBaseMap &BaseToLoc, diff --git a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h index 7d84920cffc01..f222f2309cc79 100644 --- a/include/swift/SILOptimizer/Utils/SILSSAUpdater.h +++ b/include/swift/SILOptimizer/Utils/SILSSAUpdater.h @@ -133,8 +133,10 @@ class UseWrapper { /// reconstruct the use. UseWrapper(Operand *Use); + Operand *getOperand(); + /// Return the operand we wrap. Reconstructing branch operands. - operator Operand*(); + operator Operand*() { return getOperand(); } }; } // end namespace swift diff --git a/include/swift/SILOptimizer/Utils/ValueLifetime.h b/include/swift/SILOptimizer/Utils/ValueLifetime.h index 85be58077e6ac..0f048169b4dc5 100644 --- a/include/swift/SILOptimizer/Utils/ValueLifetime.h +++ b/include/swift/SILOptimizer/Utils/ValueLifetime.h @@ -22,13 +22,11 @@ namespace swift { -/// This computes the lifetime of a single SILValue. -/// -/// This does not compute a set of jointly postdominating use points. Instead it -/// assumes that the value's existing uses already jointly postdominate the -/// definition. This makes sense for values that are returned +1 from an -/// instruction, like partial_apply, and therefore must be released on all paths -/// via strong_release or apply. +/// Computes the lifetime frontier for a given value with respect to a +/// given set of uses. The lifetime frontier is the list of instructions +/// following the last uses. The set of uses can be passed by the clients +/// of the analysis and can be a super set of the uses of the SILValue +/// e.g. it can be the set of transitive uses of the SILValue. class ValueLifetimeAnalysis { public: @@ -70,20 +68,26 @@ class ValueLifetimeAnalysis { UsersMustPostDomDef }; - /// Computes and returns the lifetime frontier for the value in \p frontier. + /// Computes and returns the lifetime frontier for the value in \p frontier + /// with respect to the set of uses in the userSet. /// /// Returns true if all instructions in the frontier could be found in /// non-critical edges. /// Returns false if some frontier instructions are located on critical edges. /// In this case, if \p mode is AllowToModifyCFG, those critical edges are - /// split, otherwise nothing is done and the returned \p frontier is not - /// valid. + /// split, otherwise the returned \p frontier consists of only those + /// instructions of the frontier that are not in the critical edges. Note that + /// the method getCriticalEdges can be used to retrieve the critical edges. /// /// If \p deBlocks is provided, all dead-end blocks are ignored. This /// prevents unreachable-blocks to be included in the frontier. bool computeFrontier(Frontier &frontier, Mode mode, DeadEndBlocks *deBlocks = nullptr); + ArrayRef> getCriticalEdges() { + return criticalEdges; + } + /// Returns true if the instruction \p Inst is located within the value's /// lifetime. /// It is assumed that \p inst is located after the value's definition. @@ -91,7 +95,8 @@ class ValueLifetimeAnalysis { /// Returns true if the value is alive at the begin of block \p bb. bool isAliveAtBeginOfBlock(SILBasicBlock *bb) { - return liveBlocks.count(bb) && bb != defValue->getParent(); + return liveBlocks.count(bb) && + (bb != defValue->getParent() || hasUsersBeforeDef); } /// Checks if there is a dealloc_ref inside the value's live range. @@ -112,6 +117,14 @@ class ValueLifetimeAnalysis { /// provided with the constructor. llvm::SmallPtrSet userSet; + /// Indicates whether the basic block containing def has users of def that + /// precede def. This field is initialized by propagateLiveness. + bool hasUsersBeforeDef; + + /// Critical edges that couldn't be split to compute the frontier. This could + /// be non-empty when the analysis is invoked with DontModifyCFG mode. + llvm::SmallVector, 16> criticalEdges; + /// Propagates the liveness information up the control flow graph. void propagateLiveness(); diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 2ab4540edfde3..539a09de43349 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -35,6 +35,7 @@ namespace swift { class Expr; class ExtensionDecl; class FunctionType; + class LookupResult; class NominalTypeDecl; class PatternBindingDecl; class ProtocolDecl; @@ -81,6 +82,11 @@ namespace swift { ArrayRef getMemberDecls(InterestedMemberKind Kind); }; + /// Look up a member with the given name in the given type. + /// + /// Unlike other member lookup functions, \c swift::resolveValueMember() + /// should be used when you want to look up declarations with the same name as + /// one you already have. ResolvedMemberResult resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name); @@ -137,6 +143,9 @@ namespace swift { /// \returns true on success, false on error. bool typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD); + LookupResult + lookupSemanticMember(DeclContext *DC, Type ty, DeclName name); + struct ExtensionInfo { // The extension with the declarations to apply. ExtensionDecl *Ext; @@ -223,10 +232,6 @@ namespace swift { /// written by the user; this performs the reverse transformation. OriginalArgumentList getOriginalArgumentList(Expr *expr); - /// Return true if the specified type or a super-class/super-protocol has the - /// @dynamicMemberLookup attribute on it. - bool hasDynamicMemberLookupAttribute(Type type); - /// Returns the root type and result type of the keypath type in a keypath /// dynamic member lookup subscript, or \c None if it cannot be determined. /// diff --git a/include/swift/Sema/IDETypeCheckingRequestIDZone.def b/include/swift/Sema/IDETypeCheckingRequestIDZone.def index cb8f57df0e22a..3b08c24f458e4 100644 --- a/include/swift/Sema/IDETypeCheckingRequestIDZone.def +++ b/include/swift/Sema/IDETypeCheckingRequestIDZone.def @@ -15,8 +15,6 @@ // //===----------------------------------------------------------------------===// -SWIFT_REQUEST(IDETypeChecking, HasDynamicMemberLookupAttributeRequest, - bool(TypeBase *), Cached, NoLocationInfo) SWIFT_REQUEST(IDETypeChecking, IsDeclApplicableRequest, bool(DeclApplicabilityOwner), Cached, NoLocationInfo) SWIFT_REQUEST(IDETypeChecking, RootAndResultTypeOfKeypathDynamicMemberRequest, diff --git a/include/swift/Sema/IDETypeCheckingRequests.h b/include/swift/Sema/IDETypeCheckingRequests.h index 66db9ae7c04ad..d32238cf7a4a8 100644 --- a/include/swift/Sema/IDETypeCheckingRequests.h +++ b/include/swift/Sema/IDETypeCheckingRequests.h @@ -227,29 +227,6 @@ class RootTypeOfKeypathDynamicMemberRequest: SourceLoc getNearestLoc() const { return SourceLoc(); }; }; -//----------------------------------------------------------------------------// -// HasDynamicMemberLookupAttributeRequest -//----------------------------------------------------------------------------// -class HasDynamicMemberLookupAttributeRequest: - public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - // Evaluation. - llvm::Expected evaluate(Evaluator &evaluator, TypeBase *ty) const; - -public: - // Caching - bool isCached() const { return true; } - // Source location - SourceLoc getNearestLoc() const { return SourceLoc(); }; -}; - /// The zone number for the IDE. #define SWIFT_TYPEID_ZONE IDETypeChecking #define SWIFT_TYPEID_HEADER "swift/Sema/IDETypeCheckingRequestIDZone.def" diff --git a/include/swift/Sema/SourceLoader.h b/include/swift/Sema/SourceLoader.h index 004e7eb724ebd..cb8f4d19e9bf9 100644 --- a/include/swift/Sema/SourceLoader.h +++ b/include/swift/Sema/SourceLoader.h @@ -56,7 +56,7 @@ class SourceLoader : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -69,7 +69,7 @@ class SourceLoader : public ModuleLoader { /// returns NULL. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Load extensions to the given nominal type. /// diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 7a5f884b379b4..68910f29547df 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -20,7 +20,9 @@ namespace swift { class ModuleFile; -class LazyResolver; +namespace file_types { + enum ID : uint8_t; +} /// Spceifies how to load modules when both a module interface and serialized /// AST are present, or whether to disallow one format or the other altogether. @@ -31,8 +33,26 @@ enum class ModuleLoadingMode { OnlySerialized }; -/// Common functionality shared between \c SerializedModuleLoader and -/// \c ModuleInterfaceLoader. +/// Helper type used to pass and compute the sets of related filenames used by +/// \c SerializedModuleLoader subclasses. +struct SerializedModuleBaseName { + /// The base filename, wtihout any extension. + SmallString<256> baseName; + + /// Creates a \c SerializedModuleBaseName. + SerializedModuleBaseName(StringRef baseName) : baseName(baseName) { } + + /// Creates a \c SerializedModuleBaseName by contextualizing an existing one + /// with a \c parentDir. + SerializedModuleBaseName(StringRef parentDir, + const SerializedModuleBaseName &name); + + /// Gets the filename with a particular extension appended to it. + std::string getName(file_types::ID fileTy) const; +}; + +/// Common functionality shared between \c SerializedModuleLoader, +/// \c ModuleInterfaceLoader and \c MemoryBufferSerializedModuleLoader. class SerializedModuleLoaderBase : public ModuleLoader { /// A { module, generation # } pair. using LoadedModulePair = std::pair, unsigned>; @@ -43,14 +63,17 @@ class SerializedModuleLoaderBase : public ModuleLoader { protected: ASTContext &Ctx; ModuleLoadingMode LoadMode; + bool IgnoreSwiftSourceInfoFile; SerializedModuleLoaderBase(ASTContext &ctx, DependencyTracker *tracker, - ModuleLoadingMode LoadMode); + ModuleLoadingMode LoadMode, + bool IgnoreSwiftSourceInfoFile); void collectVisibleTopLevelModuleNamesImpl(SmallVectorImpl &names, StringRef extension) const; - using AccessPathElem = std::pair; + using AccessPathElem = Located; bool findModule(AccessPathElem moduleID, + SmallVectorImpl *moduleInterfacePath, std::unique_ptr *moduleBuffer, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, @@ -69,41 +92,40 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// modules and will defer to the remaining module loaders to look up this /// module. virtual std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, - StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) = 0; std::error_code - openModuleFiles(AccessPathElem ModuleID, - StringRef ModulePath, StringRef ModuleDocPath, - StringRef ModuleSourceInfoName, - std::unique_ptr *ModuleBuffer, - std::unique_ptr *ModuleDocBuffer, - std::unique_ptr *ModuleSourceInfoBuffer); + openModuleFile( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + std::unique_ptr *ModuleBuffer); std::error_code - openModuleDocFile(AccessPathElem ModuleID, - StringRef ModuleDocPath, - std::unique_ptr *ModuleDocBuffer); + openModuleDocFileIfPresent( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + std::unique_ptr *ModuleDocBuffer); - void - openModuleSourceInfoFileIfPresent(AccessPathElem ModuleID, - StringRef ModulePath, - StringRef ModuleSourceInfoFileName, - std::unique_ptr *ModuleSourceInfoBuffer); + std::error_code + openModuleSourceInfoFileIfPresent( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + std::unique_ptr *ModuleSourceInfoBuffer); /// If the module loader subclass knows that all options have been tried for /// loading an architecture-specific file out of a swiftmodule bundle, try /// to list the architectures that \e are present. /// /// \returns true if an error diagnostic was emitted - virtual bool maybeDiagnoseTargetMismatch(SourceLoc sourceLocation, - StringRef moduleName, - StringRef archName, - StringRef directoryPath) { + virtual bool maybeDiagnoseTargetMismatch( + SourceLoc sourceLocation, + StringRef moduleName, + const SerializedModuleBaseName &BaseName) { return false; } @@ -125,6 +147,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// If the AST cannot be loaded and \p diagLoc is present, a diagnostic is /// printed. (Note that \p diagLoc is allowed to be invalid.) FileUnit *loadAST(ModuleDecl &M, Optional diagLoc, + StringRef moduleInterfacePath, std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, @@ -135,7 +158,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// /// Note that even if this check succeeds, errors may still occur if the /// module is loaded in full. - virtual bool canImportModule(std::pair named) override; + virtual bool canImportModule(Located named) override; /// Import a module with the given module path. /// @@ -148,7 +171,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { /// emits a diagnostic and returns a FailedImportModule object. virtual ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; virtual void loadExtensions(NominalTypeDecl *nominal, @@ -168,22 +191,22 @@ class SerializedModuleLoaderBase : public ModuleLoader { class SerializedModuleLoader : public SerializedModuleLoaderBase { SerializedModuleLoader(ASTContext &ctx, DependencyTracker *tracker, - ModuleLoadingMode loadMode) - : SerializedModuleLoaderBase(ctx, tracker, loadMode) + ModuleLoadingMode loadMode, bool IgnoreSwiftSourceInfo) + : SerializedModuleLoaderBase(ctx, tracker, loadMode, IgnoreSwiftSourceInfo) {} std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, - StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) override; - bool maybeDiagnoseTargetMismatch(SourceLoc sourceLocation, - StringRef moduleName, - StringRef archName, - StringRef directoryPath) override; + bool maybeDiagnoseTargetMismatch( + SourceLoc sourceLocation, + StringRef moduleName, + const SerializedModuleBaseName &BaseName) override; public: virtual ~SerializedModuleLoader(); @@ -197,9 +220,10 @@ class SerializedModuleLoader : public SerializedModuleLoaderBase { /// into the given ASTContext. static std::unique_ptr create(ASTContext &ctx, DependencyTracker *tracker = nullptr, - ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized) { + ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized, + bool IgnoreSwiftSourceInfo = false) { return std::unique_ptr{ - new SerializedModuleLoader(ctx, tracker, loadMode) + new SerializedModuleLoader(ctx, tracker, loadMode, IgnoreSwiftSourceInfo) }; } }; @@ -211,29 +235,31 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase { MemoryBufferSerializedModuleLoader(ASTContext &ctx, DependencyTracker *tracker, - ModuleLoadingMode loadMode) - : SerializedModuleLoaderBase(ctx, tracker, loadMode) {} + ModuleLoadingMode loadMode, + bool IgnoreSwiftSourceInfo) + : SerializedModuleLoaderBase(ctx, tracker, loadMode, + IgnoreSwiftSourceInfo) {} std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, - StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) override; - bool maybeDiagnoseTargetMismatch(SourceLoc sourceLocation, - StringRef moduleName, - StringRef archName, - StringRef directoryPath) override; + bool maybeDiagnoseTargetMismatch( + SourceLoc sourceLocation, + StringRef moduleName, + const SerializedModuleBaseName &BaseName) override; public: virtual ~MemoryBufferSerializedModuleLoader(); - bool canImportModule(std::pair named) override; + bool canImportModule(Located named) override; ModuleDecl * loadModule(SourceLoc importLoc, - ArrayRef> path) override; + ArrayRef> path) override; /// Register a memory buffer that contains the serialized module for the given /// access path. This API is intended to be used by LLDB to add swiftmodules @@ -253,9 +279,11 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase { /// into the given ASTContext. static std::unique_ptr create(ASTContext &ctx, DependencyTracker *tracker = nullptr, - ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized) { + ModuleLoadingMode loadMode = ModuleLoadingMode::PreferSerialized, + bool IgnoreSwiftSourceInfo = false) { return std::unique_ptr{ - new MemoryBufferSerializedModuleLoader(ctx, tracker, loadMode)}; + new MemoryBufferSerializedModuleLoader(ctx, tracker, loadMode, + IgnoreSwiftSourceInfo)}; } }; @@ -339,6 +367,11 @@ class SerializedASTFile final : public LoadedFile { virtual void getTopLevelDecls(SmallVectorImpl &results) const override; + virtual void + getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes) const override; + virtual void getPrecedenceGroups(SmallVectorImpl &Results) const override; diff --git a/include/swift/Serialization/SerializedSILLoader.h b/include/swift/Serialization/SerializedSILLoader.h index 41a7b794e6f66..fd65af939be7d 100644 --- a/include/swift/Serialization/SerializedSILLoader.h +++ b/include/swift/Serialization/SerializedSILLoader.h @@ -56,7 +56,7 @@ class SerializedSILLoader { } ~SerializedSILLoader(); - SILFunction *lookupSILFunction(SILFunction *Callee); + SILFunction *lookupSILFunction(SILFunction *Callee, bool onlyUpdateLinkage); SILFunction * lookupSILFunction(StringRef Name, bool declarationOnly = false, Optional linkage = None); diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 31d37c8695e40..1bec596be277d 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -25,6 +25,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Mutex.h" #include @@ -66,6 +67,7 @@ namespace swift { class Token; class TopLevelContext; class TypeChecker; + class TypeCheckerOptions; struct TypeLoc; class UnifiedStatsReporter; enum class SourceFileKind; @@ -117,26 +119,20 @@ namespace swift { /// /// \param PersistentState if non-null the same PersistentState object can /// be used to resume parsing or parse delayed function bodies. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, + void parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL = nullptr, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); /// Parse a single buffer into the given source file, until the full source /// contents are parsed. - /// - /// \return true if the parser found code with side effects. - bool parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, + void parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState = nullptr, bool DelayBodyParsing = true); - /// Finish the parsing by going over the nodes that were delayed - /// during the first parsing pass. - void performDelayedParsing(DeclContext *DC, - PersistentParserState &PersistentState, - CodeCompletionCallbacksFactory *Factory); + /// Finish the code completion. + void performCodeCompletionSecondPass(PersistentParserState &PersistentState, + CodeCompletionCallbacksFactory &Factory); /// Lex and return a vector of tokens for the given buffer. std::vector tokenize(const LangOptions &LangOpts, @@ -168,32 +164,9 @@ namespace swift { /// Once parsing and name-binding are complete this optionally walks the ASTs /// to add calls to externally provided functions that simulate - /// "program counter"-like debugging events. - void performPCMacro(SourceFile &SF, TopLevelContext &TLC); - - /// Flags used to control type checking. - enum class TypeCheckingFlags : unsigned { - /// Whether to delay checking that benefits from having the entire - /// module parsed, e.g., Objective-C method override checking. - DelayWholeModuleChecking = 1 << 0, - - /// If set, dumps wall time taken to check each function body to - /// llvm::errs(). - DebugTimeFunctionBodies = 1 << 1, - - /// Indicates that the type checker is checking code that will be - /// immediately executed. - ForImmediateMode = 1 << 2, - - /// If set, dumps wall time taken to type check each expression to - /// llvm::errs(). - DebugTimeExpressions = 1 << 3, - - /// If set, the typechecker will skip typechecking non-inlinable function - /// bodies. Set this if you're trying to quickly emit a module or module - /// interface without a full compilation. - SkipNonInlinableFunctionBodies = 1 << 4, - }; + /// "program counter"-like debugging events. See the comment at the top of + /// lib/Sema/PCMacro.cpp for a description of the calls inserted. + void performPCMacro(SourceFile &SF); /// Creates a type checker instance on the given AST context, if it /// doesn't already have one. @@ -201,21 +174,15 @@ namespace swift { /// \returns a reference to the type checker instance. TypeChecker &createTypeChecker(ASTContext &Ctx); + /// Bind all 'extension' visible from \p SF to the extended nominal. + void bindExtensions(SourceFile &SF); + /// Once parsing and name-binding are complete, this walks the AST to resolve /// types and diagnose problems therein. /// /// \param StartElem Where to start for incremental type-checking in the main /// source file. - /// - /// \param WarnLongFunctionBodies If non-zero, warn when a function body takes - /// longer than this many milliseconds to type-check - void performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - OptionSet Options, - unsigned StartElem = 0, - unsigned WarnLongFunctionBodies = 0, - unsigned WarnLongExpressionTypeChecking = 0, - unsigned ExpressionTimeoutThreshold = 0, - unsigned SwitchCheckingInvocationThreshold = 0); + void performTypeChecking(SourceFile &SF, unsigned StartElem = 0); /// Now that we have type-checked an entire module, perform any type /// checking that requires the full module, e.g., Objective-C method @@ -234,9 +201,6 @@ namespace swift { /// of declarations in the module. void checkInconsistentImplementationOnlyImports(ModuleDecl *M); - /// Incrementally type-check only added external definitions. - void typeCheckExternalDefinitions(SourceFile &SF); - /// Recursively validate the specified type. /// /// This is used when dealing with partial source files (e.g. SIL parsing, @@ -261,8 +225,7 @@ namespace swift { bool ProduceDiagnostics = true); /// Expose TypeChecker's handling of GenericParamList to SIL parsing. - GenericEnvironment *handleSILGenericParams(ASTContext &Ctx, - GenericParamList *genericParams, + GenericEnvironment *handleSILGenericParams(GenericParamList *genericParams, DeclContext *DC); /// Turn the given module into SIL IR. @@ -271,12 +234,12 @@ namespace swift { /// SIL of all files in the module is present in the SILModule. std::unique_ptr performSILGeneration(ModuleDecl *M, Lowering::TypeConverter &TC, - SILOptions &options); + const SILOptions &options); /// Turn a source file into SIL IR. std::unique_ptr performSILGeneration(FileUnit &SF, Lowering::TypeConverter &TC, - SILOptions &options); + const SILOptions &options); using ModuleOrSourceFile = PointerUnion; @@ -300,33 +263,36 @@ namespace swift { /// Get the CPU, subtarget feature options, and triple to use when emitting code. std::tuple, std::string> - getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx); + getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx); /// Turn the given Swift module into either LLVM IR or native code /// and return the generated LLVM IR module. /// If you set an outModuleHash, then you need to call performLLVM. std::unique_ptr - performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, + performIRGeneration(const IRGenOptions &Opts, ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Turn the given Swift module into either LLVM IR or native code /// and return the generated LLVM IR module. /// If you set an outModuleHash, then you need to call performLLVM. std::unique_ptr - performIRGeneration(IRGenOptions &Opts, SourceFile &SF, + performIRGeneration(const IRGenOptions &Opts, SourceFile &SF, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, + StringRef PrivateDiscriminator, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash = nullptr); + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *LinkerDirectives = nullptr); /// Given an already created LLVM module, construct a pass pipeline and run /// the Swift LLVM Pipeline upon it. This does not cause the module to be /// printed, only to be optimized. - void performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, + void performLLVMOptimizations(const IRGenOptions &Opts, llvm::Module *Module, llvm::TargetMachine *TargetMachine); /// Wrap a serialized module inside a swift AST section in an object file. @@ -334,7 +300,7 @@ namespace swift { StringRef OutputPath); /// Turn the given LLVM module into native code and return true on error. - bool performLLVM(IRGenOptions &Opts, ASTContext &Ctx, llvm::Module *Module, + bool performLLVM(const IRGenOptions &Opts, ASTContext &Ctx, llvm::Module *Module, StringRef OutputFilename, UnifiedStatsReporter *Stats=nullptr); @@ -349,7 +315,7 @@ namespace swift { /// \param TargetMachine target of code gen, required. /// \param effectiveLanguageVersion version of the language, effectively. /// \param OutputFilename Filename for output. - bool performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, + bool performLLVM(const IRGenOptions &Opts, DiagnosticEngine *Diags, llvm::sys::Mutex *DiagMutex, llvm::GlobalVariable *HashGlobal, llvm::Module *Module, @@ -359,19 +325,20 @@ namespace swift { UnifiedStatsReporter *Stats=nullptr); /// Dump YAML describing all fixed-size types imported from the given module. - bool performDumpTypeInfo(IRGenOptions &Opts, + bool performDumpTypeInfo(const IRGenOptions &Opts, SILModule &SILMod, llvm::LLVMContext &LLVMContext); /// Creates a TargetMachine from the IRGen opts and AST Context. std::unique_ptr - createTargetMachine(IRGenOptions &Opts, ASTContext &Ctx); + createTargetMachine(const IRGenOptions &Opts, ASTContext &Ctx); /// A convenience wrapper for Parser functionality. class ParserUnit { public: ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &LangOpts, StringRef ModuleName, + const LangOptions &LangOpts, const TypeCheckerOptions &TyOpts, + StringRef ModuleName, std::shared_ptr spActions = nullptr, SyntaxParsingCache *SyntaxCache = nullptr); ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID); @@ -416,6 +383,12 @@ namespace swift { /// ASTContext. void registerTypeCheckerRequestFunctions(Evaluator &evaluator); + /// Register SILGen-level request functions with the evaluator. + /// + /// Clients that form an ASTContext and will perform any SIL generation + /// should call this functions after forming the ASTContext. + void registerSILGenRequestFunctions(Evaluator &evaluator); + /// Register IDE-level request functions with the evaluator. /// /// The ASTContext will automatically call these upon construction. diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h index 76878a3fa67b7..8c747404223c2 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorLegacyInterop.h @@ -353,7 +353,7 @@ swift_reflection_interop_libraryOwnsAddress( // Search the images list to see if the address is in one of them. struct SwiftReflectionInteropContextLegacyImageRangeList *Node = ContextRef->LegacyImageRangeList; - while (Node != NULL) { + while (Node != nullptr) { if (Node->Start <= Address && Address < Node->End) return 1; Node = Node->Next; @@ -380,7 +380,7 @@ swift_reflection_interop_libraryForAddress( return Library; } } - return NULL; + return nullptr; } static inline uintptr_t @@ -408,7 +408,7 @@ swift_reflection_interop_libraryForObject( if (Library->IsLegacy) return Library; } - return NULL; + return nullptr; } return swift_reflection_interop_libraryForAddress(ContextRef, Metadata); @@ -417,7 +417,7 @@ swift_reflection_interop_libraryForObject( static inline int swift_reflection_interop_loadFunctions(struct SwiftReflectionInteropContext *Context, void *Handle) { - if (Handle == NULL) + if (Handle == nullptr) return 0; struct SwiftReflectionInteropContextLibrary *Library = &Context @@ -429,7 +429,7 @@ swift_reflection_interop_loadFunctions(struct SwiftReflectionInteropContext *Con #endif #define LOAD_NAMED(field, symbol, required) do { \ Functions->field = (decltype(Functions->field))dlsym(Handle, symbol); \ - if (required && Functions->field == NULL) return 0; \ + if (required && Functions->field == nullptr) return 0; \ } while (0) #define LOAD(name) LOAD_NAMED(name, "swift_reflection_" #name, 1) #define LOAD_OPT(name) LOAD_NAMED(name, "swift_reflection_" #name, 0) @@ -441,7 +441,7 @@ swift_reflection_interop_loadFunctions(struct SwiftReflectionInteropContext *Con if (version < SWIFT_LEGACY_METADATA_MIN_VERSION) return 0; - int IsLegacy = dlsym(Handle, "swift_reflection_addImage") == NULL; + int IsLegacy = dlsym(Handle, "swift_reflection_addImage") == nullptr; if (IsLegacy) { LOAD_NAMED(createReflectionContextLegacy, "swift_reflection_createReflectionContext", 1); @@ -498,11 +498,11 @@ swift_reflection_interop_readBytesAdapter(void *reader_context, void *FreeContext; const void *ptr = Context->ReadBytes(Context->ReaderContext, address, size, &FreeContext); - if (ptr == NULL) + if (ptr == nullptr) return 0; memcpy(dest, ptr, size); - if (Context->FreeBytes != NULL) + if (Context->FreeBytes != nullptr) Context->FreeBytes(Context->ReaderContext, ptr, FreeContext); return 1; } @@ -574,7 +574,7 @@ swift_reflection_interop_createReflectionContext( return swift_reflection_interop_createReflectionContextWithDataLayout( ReaderContext, - NULL, + nullptr, FreeBytes, ReadBytes, GetStringLength, @@ -600,7 +600,7 @@ swift_reflection_interop_createReflectionContextWithDataLayout( ContextRef->GetStringLength = GetStringLength; ContextRef->GetSymbolAddress = GetSymbolAddress; - ContextRef->AddressToLibraryCache = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); + ContextRef->AddressToLibraryCache = CFDictionaryCreateMutable(nullptr, 0, nullptr, nullptr); return ContextRef; } @@ -633,7 +633,7 @@ swift_reflection_interop_addLibrary( } else { uint8_t PointerSize; int result = ContextRef->DataLayout( - ContextRef->ReaderContext, DLQ_GetPointerSize, NULL, &PointerSize); + ContextRef->ReaderContext, DLQ_GetPointerSize, nullptr, &PointerSize); if (!result) abort(); // We need the pointer size, can't proceed without it. @@ -658,14 +658,14 @@ swift_reflection_interop_destroyReflectionContext( free(ContextRef->Libraries); struct SwiftReflectionInteropContextLegacyImageRangeList *LegacyImageRangeList = ContextRef->LegacyImageRangeList; - while (LegacyImageRangeList != NULL) { + while (LegacyImageRangeList != nullptr) { struct SwiftReflectionInteropContextLegacyImageRangeList *Next = LegacyImageRangeList->Next; free(LegacyImageRangeList); LegacyImageRangeList = Next; } struct SwiftReflectionInteropContextFreeList *FreeList = ContextRef->FreeList; - while (FreeList != NULL) { + while (FreeList != nullptr) { ContextRef->FreeBytes(ContextRef->ReaderContext, FreeList->Pointer, FreeList->Context); struct SwiftReflectionInteropContextFreeList *Next = FreeList->Next; @@ -717,37 +717,37 @@ swift_reflection_interop_addImageLegacy( ImageStart, sizeof(MachHeader), &FreeContext); - if (Buf == NULL) + if (Buf == nullptr) return 0; MachHeader *Header = (MachHeader *)Buf; if (Header->magic != MH_MAGIC && Header->magic != MH_MAGIC_64) { - if (ContextRef->FreeBytes != NULL) + if (ContextRef->FreeBytes != nullptr) ContextRef->FreeBytes(ContextRef->ReaderContext, Buf, FreeContext); return 0; } // Read the commands. uint32_t Length = Header->sizeofcmds; - if (ContextRef->FreeBytes != NULL) + if (ContextRef->FreeBytes != nullptr) ContextRef->FreeBytes(ContextRef->ReaderContext, Buf, FreeContext); Buf = ContextRef->ReadBytes(ContextRef->ReaderContext, ImageStart, Length, &FreeContext); - if (Buf == NULL) + if (Buf == nullptr) return 0; Header = (MachHeader *)Buf; // Find the TEXT segment and figure out where the end is. unsigned long TextSize; uint8_t *TextSegment = getsegmentdata(Header, "__TEXT", &TextSize); - if (ContextRef->FreeBytes != NULL) + if (ContextRef->FreeBytes != nullptr) ContextRef->FreeBytes(ContextRef->ReaderContext, Buf, FreeContext); - if (TextSegment == NULL) { + if (TextSegment == nullptr) { return 0; } unsigned long TextEnd = TextSegment - (uint8_t *)Buf + TextSize; @@ -757,7 +757,7 @@ swift_reflection_interop_addImageLegacy( ImageStart, TextEnd, &FreeContext); - if (Buf == NULL) + if (Buf == nullptr) return 0; Header = (MachHeader *)Buf; @@ -785,7 +785,7 @@ swift_reflection_interop_addImageLegacy( &info.reflstr) || success; if (!success) { - if (ContextRef->FreeBytes != NULL) + if (ContextRef->FreeBytes != nullptr) ContextRef->FreeBytes(ContextRef->ReaderContext, Buf, FreeContext); return 0; } @@ -809,7 +809,7 @@ swift_reflection_interop_addImageLegacy( // If the buffer needs to be freed, save buffer and free context to free it when the // reflection context is destroyed. - if (ContextRef->FreeBytes != NULL) { + if (ContextRef->FreeBytes != nullptr) { struct SwiftReflectionInteropContextFreeList *FreeListNode = (struct SwiftReflectionInteropContextFreeList *)malloc(sizeof(*FreeListNode)); FreeListNode->Next = ContextRef->FreeList; @@ -857,7 +857,7 @@ swift_reflection_interop_lookupMetadata(SwiftReflectionInteropContextRef Context swift_metadata_interop_t Result = {}; struct SwiftReflectionInteropContextLibrary *Library = swift_reflection_interop_libraryForAddress(ContextRef, Metadata); - if (Library != NULL) { + if (Library != nullptr) { Result.Metadata = Metadata; Result.Library = (int)LIBRARY_INDEX; } @@ -881,7 +881,7 @@ swift_reflection_interop_typeRefForInstance(SwiftReflectionInteropContextRef Con swift_typeref_interop_t Result = {}; struct SwiftReflectionInteropContextLibrary *Library = swift_reflection_interop_libraryForObject(ContextRef, Object); - if (Library != NULL) { + if (Library != nullptr) { swift_typeref_t Typeref = Library->Functions.typeRefForInstance(Library->Context, Object); Result.Typeref = Typeref; @@ -967,7 +967,7 @@ swift_reflection_interop_infoForInstance(SwiftReflectionInteropContextRef Contex struct SwiftReflectionInteropContextLibrary *Library = swift_reflection_interop_libraryForObject(ContextRef, Object); - if (Library != NULL) { + if (Library != nullptr) { Result = Library->Functions.infoForInstance(Library->Context, Object); } else { Result.Kind = SWIFT_UNKNOWN; @@ -983,7 +983,7 @@ swift_reflection_interop_childOfInstance(SwiftReflectionInteropContextRef Contex swift_childinfo_interop_t Result = {}; struct SwiftReflectionInteropContextLibrary *Library = swift_reflection_interop_libraryForObject(ContextRef, Object); - if (Library != NULL) { + if (Library != nullptr) { swift_childinfo_t LibResult = Library->Functions.childOfInstance(Library->Context, Object, Index); Result.Name = LibResult.Name; @@ -1061,7 +1061,7 @@ swift_reflection_interop_dumpInfoForInstance(SwiftReflectionInteropContextRef Co uintptr_t Object) { struct SwiftReflectionInteropContextLibrary *Library = swift_reflection_interop_libraryForObject(ContextRef, Object); - if (Library != NULL) { + if (Library != nullptr) { Library->Functions.dumpInfoForInstance(Library->Context, Object); } } diff --git a/include/swift/SymbolGraphGen/SymbolGraphGen.h b/include/swift/SymbolGraphGen/SymbolGraphGen.h new file mode 100644 index 0000000000000..324a505c45e39 --- /dev/null +++ b/include/swift/SymbolGraphGen/SymbolGraphGen.h @@ -0,0 +1,41 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "swift/AST/AttrKind.h" + +namespace swift { + +class ModuleDecl; + +namespace symbolgraphgen { + +struct SymbolGraphOptions { + /// The path to output the symbol graph JSON. + StringRef OutputPath; + + /// The target of the module. + llvm::Triple Target; + + /// Pretty-print the JSON with newlines and indentation. + bool PrettyPrint; + + /// The minimum access level that symbols must have in order to be + /// included in the graph. + AccessLevel MinimumAccessLevel; +}; + +/// Emit a Symbol Graph JSON file for a module. +int emitSymbolGraphForModule(ModuleDecl *M, const SymbolGraphOptions &Options); + +} // end namespace symbolgraphgen +} // end namespace swift diff --git a/include/swift/Syntax/RawSyntax.h b/include/swift/Syntax/RawSyntax.h index 4a7a41cb4f699..db9bdcb7fd6a3 100644 --- a/include/swift/Syntax/RawSyntax.h +++ b/include/swift/Syntax/RawSyntax.h @@ -29,6 +29,7 @@ #ifndef SWIFT_SYNTAX_RAWSYNTAX_H #define SWIFT_SYNTAX_RAWSYNTAX_H +#include "swift/Basic/Debug.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Syntax/References.h" #include "swift/Syntax/SyntaxArena.h" @@ -192,7 +193,9 @@ class AbsolutePosition { /// Dump a description of this position to the given output stream /// for debugging. - void dump(llvm::raw_ostream &OS = llvm::errs()) const; + void dump(llvm::raw_ostream &OS) const; + + SWIFT_DEBUG_DUMP; }; /// An indicator of whether a Syntax node was found or written in the source. @@ -568,7 +571,7 @@ class RawSyntax final void print(llvm::raw_ostream &OS, SyntaxPrintOptions Opts) const; /// Dump this piece of syntax recursively for debugging or testing. - void dump() const; + SWIFT_DEBUG_DUMP; /// Dump this piece of syntax recursively. void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; diff --git a/include/swift/Syntax/Syntax.h b/include/swift/Syntax/Syntax.h index 0ca77543f850f..5e20285048c12 100644 --- a/include/swift/Syntax/Syntax.h +++ b/include/swift/Syntax/Syntax.h @@ -23,6 +23,7 @@ #ifndef SWIFT_SYNTAX_SYNTAX_H #define SWIFT_SYNTAX_SYNTAX_H +#include "swift/Basic/Debug.h" #include "swift/Syntax/SyntaxData.h" #include "swift/Syntax/References.h" #include "swift/Syntax/RawSyntax.h" @@ -39,7 +40,6 @@ namespace syntax { struct SyntaxVisitor; class SourceFileSyntax; -class TokenSyntax; template SyntaxNode make(RC Raw) { @@ -169,18 +169,6 @@ class Syntax { /// Returns true if the node is "present" in the source. bool isPresent() const; - /// Get the node immediately before this current node that does contain a - /// non-missing token. Return nullptr if we cannot find such node. - Optional getPreviousNode() const; - - /// Returns the first non-missing token in this syntax. Returns None if there - /// is no non-missing token. - Optional getFirstToken() const; - - /// Returns the last non-missing token in this syntax. Returns None if there - /// is no non-missing token. - Optional getLastToken() const; - /// Print the syntax node with full fidelity to the given output stream. void print(llvm::raw_ostream &OS, SyntaxPrintOptions Opts = SyntaxPrintOptions()) const; @@ -189,7 +177,7 @@ class Syntax { void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; /// Print a debug representation of the syntax node to standard error. - void dump() const; + SWIFT_DEBUG_DUMP; bool hasSameIdentityAs(const Syntax &Other) const { return Root == Other.Root && Data == Other.Data; diff --git a/include/swift/Syntax/SyntaxBuilders.h.gyb b/include/swift/Syntax/SyntaxBuilders.h.gyb index 60f90057efa57..7fe71ed159a3d 100644 --- a/include/swift/Syntax/SyntaxBuilders.h.gyb +++ b/include/swift/Syntax/SyntaxBuilders.h.gyb @@ -29,7 +29,7 @@ namespace syntax { class SyntaxArena; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_buildable(): % child_count = len(node.children) class ${node.name}Builder { diff --git a/include/swift/Syntax/SyntaxData.h b/include/swift/Syntax/SyntaxData.h index 6159f2e01e2d8..d13a1ce958984 100644 --- a/include/swift/Syntax/SyntaxData.h +++ b/include/swift/Syntax/SyntaxData.h @@ -39,6 +39,7 @@ #ifndef SWIFT_SYNTAX_SYNTAXDATA_H #define SWIFT_SYNTAX_SYNTAXDATA_H +#include "swift/Basic/Debug.h" #include "swift/Syntax/AtomicCache.h" #include "swift/Syntax/RawSyntax.h" #include "swift/Syntax/References.h" @@ -149,10 +150,6 @@ class SyntaxData final /// node does not contain non-missing tokens. RC getFirstToken() const; - /// Get the last non-missing token node in this tree. Return nullptr if this - /// node does not contain non-missing tokens. - RC getLastToken() const; - ~SyntaxData() { for (auto &I : getChildren()) I.~AtomicCache(); @@ -292,8 +289,7 @@ class SyntaxData final /// standard error. void dump(llvm::raw_ostream &OS) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "Only meant for use in the debugger"); + SWIFT_DEBUG_DUMP; }; } // end namespace syntax diff --git a/include/swift/Syntax/SyntaxFactory.h.gyb b/include/swift/Syntax/SyntaxFactory.h.gyb index ef4566cbff237..387279d73f891 100644 --- a/include/swift/Syntax/SyntaxFactory.h.gyb +++ b/include/swift/Syntax/SyntaxFactory.h.gyb @@ -71,7 +71,7 @@ struct SyntaxFactory { static Syntax makeBlankCollectionSyntax(SyntaxKind Kind); -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.children: % child_params = [] % for child in node.children: diff --git a/include/swift/Syntax/SyntaxKind.h.gyb b/include/swift/Syntax/SyntaxKind.h.gyb index 7f2bfbc5158d1..21f344a572352 100644 --- a/include/swift/Syntax/SyntaxKind.h.gyb +++ b/include/swift/Syntax/SyntaxKind.h.gyb @@ -2,7 +2,7 @@ from gyb_syntax_support import * from gyb_syntax_support.kinds import SYNTAX_BASE_KINDS grouped_nodes = { kind: [] for kind in SYNTAX_BASE_KINDS } - for node in SYNTAX_NODES + PARSEONLY_NODES: + for node in SYNTAX_NODES: grouped_nodes[node.base_kind].append(node) # -*- mode: C++ -*- # Ignore the following admonition; it applies to the resulting .h file only @@ -89,14 +89,12 @@ struct WrapperTypeTraits { return 0; case syntax::SyntaxKind::Unknown: return 1; -% for node in SYNTAX_NODES: +% for name, nodes in grouped_nodes.items(): +% for node in nodes: case syntax::SyntaxKind::${node.syntax_kind}: return ${SYNTAX_NODE_SERIALIZATION_CODES[node.syntax_kind]}; +% end % end -% for node in PARSEONLY_NODES: - case syntax::SyntaxKind::${node.syntax_kind}: -% end - llvm_unreachable("unserializable syntax kind"); } llvm_unreachable("unhandled kind"); } @@ -124,10 +122,6 @@ struct ScalarReferenceTraits { case syntax::SyntaxKind::${node.syntax_kind}: return "\"${node.syntax_kind}\""; % end -% for node in PARSEONLY_NODES: - case syntax::SyntaxKind::${node.syntax_kind}: -% end - llvm_unreachable("unserializable syntax kind"); } llvm_unreachable("unhandled kind"); } diff --git a/include/swift/Syntax/SyntaxNodes.h.gyb b/include/swift/Syntax/SyntaxNodes.h.gyb index 2f990973864c6..65219ce8489d1 100644 --- a/include/swift/Syntax/SyntaxNodes.h.gyb +++ b/include/swift/Syntax/SyntaxNodes.h.gyb @@ -32,13 +32,13 @@ namespace syntax { % # Emit the non-collection classes first, then emit the collection classes % # that reference these classes. -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if not node.is_syntax_collection(): class ${node.name}; % end % end -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_syntax_collection(): using ${node.name} = SyntaxCollection get${child.name}() const; + llvm::Optional<${child.type_name}> get${child.name}(); % else: - ${child.type_name} get${child.name}() const; + ${child.type_name} get${child.name}(); % end % child_node = NODE_MAP.get(child.syntax_kind) diff --git a/include/swift/Syntax/SyntaxVisitor.h.gyb b/include/swift/Syntax/SyntaxVisitor.h.gyb index 14b936357a783..5c172a430c693 100644 --- a/include/swift/Syntax/SyntaxVisitor.h.gyb +++ b/include/swift/Syntax/SyntaxVisitor.h.gyb @@ -32,7 +32,7 @@ namespace syntax { struct SyntaxVisitor { virtual ~SyntaxVisitor() {} -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if is_visitable(node): virtual void visit(${node.name} node); % end diff --git a/include/swift/Syntax/TokenKinds.h b/include/swift/Syntax/TokenKinds.h index 002c93c7c11b5..47b282dd0d5b8 100644 --- a/include/swift/Syntax/TokenKinds.h +++ b/include/swift/Syntax/TokenKinds.h @@ -32,9 +32,6 @@ bool isTokenTextDetermined(tok kind); /// If a token kind has determined text, return the text; otherwise assert. StringRef getTokenText(tok kind); -/// True if the token is any keyword. -bool isTokenKeyword(tok kind); - void dumpTokenKind(llvm::raw_ostream &os, tok kind); } // end namespace swift diff --git a/include/swift/Syntax/Trivia.h.gyb b/include/swift/Syntax/Trivia.h.gyb index edceb2a83918e..4adca21784be7 100644 --- a/include/swift/Syntax/Trivia.h.gyb +++ b/include/swift/Syntax/Trivia.h.gyb @@ -87,6 +87,7 @@ #ifndef SWIFT_SYNTAX_TRIVIA_H #define SWIFT_SYNTAX_TRIVIA_H +#include "swift/Basic/Debug.h" #include "swift/Basic/OwnedString.h" #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/ByteTreeSerialization.h" @@ -299,7 +300,7 @@ struct Trivia { void appendOrSquash(const TriviaPiece &Next); /// Dump a debug representation of this Trivia collection to standard error. - void dump() const; + SWIFT_DEBUG_DUMP; /// Dump a debug representation of this Trivia collection to the provided /// stream and indentation level. diff --git a/include/swift/SyntaxParse/SyntaxTreeCreator.h b/include/swift/SyntaxParse/SyntaxTreeCreator.h index 73af22262299b..680233298d590 100644 --- a/include/swift/SyntaxParse/SyntaxTreeCreator.h +++ b/include/swift/SyntaxParse/SyntaxTreeCreator.h @@ -53,6 +53,7 @@ class SyntaxTreeCreator: public SyntaxParseActions { void acceptSyntaxRoot(OpaqueSyntaxNode root, SourceFile &SF); +private: OpaqueSyntaxNode recordToken(tok tokenKind, ArrayRef leadingTrivia, ArrayRef trailingTrivia, @@ -68,10 +69,6 @@ class SyntaxTreeCreator: public SyntaxParseActions { std::pair lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) override; - - OpaqueSyntaxNodeKind getOpaqueKind() override { - return OpaqueSyntaxNodeKind::LibSyntax; - } }; } // end namespace swift diff --git a/include/swift/TBDGen/TBDGen.h b/include/swift/TBDGen/TBDGen.h index 363109c5964d6..c9152eabb79c4 100644 --- a/include/swift/TBDGen/TBDGen.h +++ b/include/swift/TBDGen/TBDGen.h @@ -33,6 +33,9 @@ struct TBDGenOptions { /// Whether this compilation is producing a TBD for InstallAPI. bool IsInstallAPI; + /// Only collect linker directive symbols. + bool LinkerDirectivesOnly = false; + /// The install_name to use in the TBD file. std::string InstallName; @@ -40,14 +43,16 @@ struct TBDGenOptions { std::string ModuleLinkName; /// The current project version to use in the generated TBD file. Defaults - /// to 1, which matches the default if the DYLIB_CURRENT_VERSION build setting - /// is not set. - version::Version CurrentVersion = {1, 0, 0}; + /// to empty string if not provided. + std::string CurrentVersion; /// The dylib compatibility-version to use in the generated TBD file. Defaults - /// to 1, which matches the default if the DYLIB_COMPATIBILITY_VERSION build - /// setting is not set. - version::Version CompatibilityVersion = {1, 0, 0}; + /// to empty string if not provided. + std::string CompatibilityVersion; + + /// The path to a Json file indicating the module name to install-name map + /// used by @_originallyDefinedIn + std::string ModuleInstallNameMapPath; }; void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 5ce3ca5def606..a5d494d2805df 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/ASTContext.h" +#include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" #include "swift/AST/ClangModuleLoader.h" @@ -47,7 +48,6 @@ #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" -#include "swift/Parse/Lexer.h" // bad dependency #include "swift/Syntax/References.h" #include "swift/Syntax/SyntaxArena.h" #include "swift/Strings.h" @@ -75,7 +75,6 @@ STATISTIC(NumCollapsedSpecializedProtocolConformances, /// GenericSignatureBuilder. #define SWIFT_GSB_EXPENSIVE_ASSERTIONS 0 -LazyResolver::~LazyResolver() = default; void ModuleLoader::anchor() {} void ClangModuleLoader::anchor() {} @@ -100,12 +99,6 @@ using AssociativityCacheType = llvm::DenseMap, Associativity>; -#define FOR_KNOWN_FOUNDATION_TYPES(MACRO) \ - MACRO(NSCopying, ProtocolDecl) \ - MACRO(NSError, ClassDecl) \ - MACRO(NSNumber, ClassDecl) \ - MACRO(NSValue, ClassDecl) - struct OverrideSignatureKey { GenericSignature baseMethodSig; GenericSignature derivedClassSig; @@ -161,8 +154,8 @@ struct ASTContext::Implementation { /// The set of cleanups to be called when the ASTContext is destroyed. std::vector> Cleanups; - /// The last resolver. - LazyResolver *Resolver = nullptr; + /// A global type checker instance.. + TypeChecker *Checker = nullptr; // FIXME: This is a StringMap rather than a StringSet because StringSet // doesn't allow passing in a pre-existing allocator. @@ -192,12 +185,20 @@ struct ASTContext::Implementation { DECL_CLASS *NAME##Decl = nullptr; #include "swift/AST/KnownStdlibTypes.def" +#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECL_CLASS) \ + /** The declaration of MODULE.NAME. */ \ + DECL_CLASS *NAME##Decl = nullptr; +#include "swift/AST/KnownObjCTypes.def" + /// The declaration of '+' function for two RangeReplaceableCollection. FuncDecl *PlusFunctionOnRangeReplaceableCollection = nullptr; /// The declaration of '+' function for two String. FuncDecl *PlusFunctionOnString = nullptr; + /// The declaration of 'Sequence.makeIterator()'. + FuncDecl *MakeIterator = nullptr; + /// The declaration of Swift.Optional.Some. EnumElementDecl *OptionalSomeDecl = nullptr; @@ -218,15 +219,6 @@ struct ASTContext::Implementation { /// The declaration of Swift.AutoreleasingUnsafeMutablePointer.memory. VarDecl *AutoreleasingUnsafeMutablePointerMemoryDecl = nullptr; - - /// The declaration of ObjectiveC.ObjCBool. - StructDecl *ObjCBoolDecl = nullptr; - -#define CACHE_FOUNDATION_DECL(NAME, DECLTYPE) \ - /** The declaration of Foundation.NAME. */ \ - DECLTYPE *NAME##Decl = nullptr; -FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) -#undef CACHE_FOUNDATION_DECL // Declare cached declarations for each of the known declarations. #define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr; @@ -475,14 +467,14 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) RC TheSyntaxArena; llvm::DenseMap overrideSigCache; + + Optional Converter; }; ASTContext::Implementation::Implementation() : IdentifierTable(Allocator), TheSyntaxArena(new syntax::SyntaxArena()) {} ASTContext::Implementation::~Implementation() { - delete Resolver; - for (auto &cleanup : Cleanups) cleanup(); } @@ -518,6 +510,7 @@ void ASTContext::operator delete(void *Data) throw() { } ASTContext *ASTContext::get(LangOptions &langOpts, + TypeCheckerOptions &typeckOpts, SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) { @@ -530,16 +523,19 @@ ASTContext *ASTContext::get(LangOptions &langOpts, auto impl = reinterpret_cast((char*)mem + sizeof(ASTContext)); impl = reinterpret_cast(llvm::alignAddr(impl,alignof(Implementation))); new (impl) Implementation(); - return new (mem) ASTContext(langOpts, SearchPathOpts, SourceMgr, Diags); + return new (mem) + ASTContext(langOpts, typeckOpts, SearchPathOpts, SourceMgr, Diags); } -ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts, +ASTContext::ASTContext(LangOptions &langOpts, TypeCheckerOptions &typeckOpts, + SearchPathOptions &SearchPathOpts, SourceManager &SourceMgr, DiagnosticEngine &Diags) : LangOpts(langOpts), - SearchPathOpts(SearchPathOpts), - SourceMgr(SourceMgr), - Diags(Diags), - evaluator(Diags, langOpts.DebugDumpCycles), + TypeCheckerOpts(typeckOpts), + SearchPathOpts(SearchPathOpts), SourceMgr(SourceMgr), Diags(Diags), + evaluator(Diags, + langOpts.DebugDumpCycles, + langOpts.BuildRequestDependencyGraph), TheBuiltinModule(createBuiltinModule(*this)), StdlibModuleName(getIdentifier(STDLIB_NAME)), SwiftShimsModuleName(getIdentifier(SWIFT_SHIMS_NAME)), @@ -628,16 +624,17 @@ RC ASTContext::getSyntaxArena() const { return getImpl().TheSyntaxArena; } -LazyResolver *ASTContext::getLazyResolver() const { - return getImpl().Resolver; +bool ASTContext::areSemanticQueriesEnabled() const { + return getLegacyGlobalTypeChecker() != nullptr; } -/// Set the lazy resolver for this context. -void ASTContext::setLazyResolver(LazyResolver *resolver) { - if (auto existing = getImpl().Resolver) - delete existing; +TypeChecker *ASTContext::getLegacyGlobalTypeChecker() const { + return getImpl().Checker; +} - getImpl().Resolver = resolver; +void ASTContext::installGlobalTypeChecker(TypeChecker *TC) { + assert(!getImpl().Checker); + getImpl().Checker = TC; } /// getIdentifier - Return the uniqued and AST-Context-owned version of the @@ -718,6 +715,31 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const { return getImpl().PlusFunctionOnString; } +FuncDecl *ASTContext::getSequenceMakeIterator() const { + if (getImpl().MakeIterator) { + return getImpl().MakeIterator; + } + + auto proto = getProtocol(KnownProtocolKind::Sequence); + if (!proto) + return nullptr; + + for (auto result : proto->lookupDirect(Id_makeIterator)) { + if (result->getDeclContext() != proto) + continue; + + if (auto func = dyn_cast(result)) { + if (func->getParameters()->size() != 0) + continue; + + getImpl().MakeIterator = func; + return func; + } + } + + return nullptr; +} + #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ if (getImpl().NAME##Decl) \ @@ -834,35 +856,15 @@ CanType ASTContext::getNeverType() const { return neverDecl->getDeclaredType()->getCanonicalType(); } -StructDecl *ASTContext::getObjCBoolDecl() const { - if (!getImpl().ObjCBoolDecl) { - SmallVector results; - auto *Context = const_cast(this); - if (ModuleDecl *M = Context->getModuleByName(Id_ObjectiveC.str())) { - M->lookupValue(getIdentifier("ObjCBool"), NLKind::UnqualifiedLookup, - results); - for (auto result : results) { - if (auto structDecl = dyn_cast(result)) { - if (structDecl->getGenericParams() == nullptr) { - getImpl().ObjCBoolDecl = structDecl; - break; - } - } - } - } - } - - return getImpl().ObjCBoolDecl; -} - -#define GET_FOUNDATION_DECL(NAME, DECLTYPE) \ +#define KNOWN_OBJC_TYPE_DECL(MODULE, NAME, DECLTYPE) \ DECLTYPE *ASTContext::get##NAME##Decl() const { \ if (!getImpl().NAME##Decl) { \ - if (ModuleDecl *M = getLoadedModule(Id_Foundation)) { \ - /* Note: lookupQualified() will search both the Foundation module \ - * and the Clang Foundation module it imports. */ \ + if (ModuleDecl *M = getLoadedModule(Id_##MODULE)) { \ + /* Note: lookupQualified() will search both the Swift overlay \ + * and the Clang module it imports. */ \ SmallVector decls; \ - M->lookupQualified(M, getIdentifier(#NAME), NL_OnlyTypes, decls); \ + M->lookupQualified(M, DeclNameRef(getIdentifier(#NAME)), NL_OnlyTypes, \ + decls); \ if (decls.size() == 1 && isa(decls[0])) { \ auto decl = cast(decls[0]); \ if (isa(decl) || decl->getGenericParams() == nullptr) { \ @@ -873,11 +875,16 @@ DECLTYPE *ASTContext::get##NAME##Decl() const { \ } \ \ return getImpl().NAME##Decl; \ +} \ +\ +Type ASTContext::get##NAME##Type() const { \ + auto *decl = get##NAME##Decl(); \ + if (!decl) \ + return Type(); \ + return decl->getDeclaredInterfaceType(); \ } -FOR_KNOWN_FOUNDATION_TYPES(GET_FOUNDATION_DECL) -#undef GET_FOUNDATION_DECL -#undef FOR_KNOWN_FOUNDATION_TYPES +#include "swift/AST/KnownObjCTypes.def" ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { // Check whether we've already looked for and cached this protocol. @@ -899,6 +906,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::CFObject: M = getLoadedModule(Id_CoreFoundation); break; + case KnownProtocolKind::Differentiable: + M = getLoadedModule(Id_Differentiation); + break; default: M = getStdlibModule(); break; @@ -924,13 +934,8 @@ static FuncDecl *findLibraryIntrinsic(const ASTContext &ctx, StringRef name) { SmallVector results; ctx.lookupInSwiftModule(name, results); - if (results.size() == 1) { - if (auto FD = dyn_cast(results.front())) { - // FIXME(InterfaceTypeRequest): Remove this. - (void)FD->getInterfaceType(); - return FD; - } - } + if (results.size() == 1) + return dyn_cast_or_null(results.front()); return nullptr; } @@ -1061,14 +1066,14 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, auto builtinProtocol = getProtocol(builtinProtocolKind); auto builtinConformance = getStdlibModule()->lookupConformance( type, builtinProtocol); - if (!builtinConformance) { + if (builtinConformance.isInvalid()) { assert(false && "Missing required conformance"); witness = ConcreteDeclRef(); return witness; } auto *ctx = const_cast(this); - witness = builtinConformance->getWitnessByName(type, initName(*ctx)); + witness = builtinConformance.getWitnessByName(type, initName(*ctx)); if (!witness) { assert(false && "Missing required witness"); witness = ConcreteDeclRef(); @@ -1384,6 +1389,10 @@ bool ASTContext::hasPointerArgumentIntrinsics() const { && getUnsafeMutablePointerDecl() && getUnsafePointerDecl() && (!LangOpts.EnableObjCInterop || getAutoreleasingUnsafeMutablePointerDecl()) + && getUnsafeBufferPointerDecl() + && getUnsafeMutableBufferPointerDecl() + && getUnsafeRawBufferPointerDecl() + && getUnsafeMutableRawBufferPointerDecl() && getConvertPointerToPointerArgument() && getConvertMutableArrayToPointerArgument() && getConvertConstArrayToPointerArgument() @@ -1397,12 +1406,6 @@ bool ASTContext::hasArrayLiteralIntrinsics() const { && getDeallocateUninitializedArray(); } -void ASTContext::addSynthesizedDecl(Decl *decl) { - auto *fileUnit = decl->getDeclContext()->getModuleScopeContext(); - if (auto *sf = dyn_cast(fileUnit)) - sf->SynthesizedDecls.push_back(decl); -} - void ASTContext::addCleanup(std::function cleanup) { getImpl().Cleanups.push_back(std::move(cleanup)); } @@ -1495,12 +1498,12 @@ ClangModuleLoader *ASTContext::getDWARFModuleLoader() const { } ModuleDecl *ASTContext::getLoadedModule( - ArrayRef> ModulePath) const { + ArrayRef> ModulePath) const { assert(!ModulePath.empty()); // TODO: Swift submodules. if (ModulePath.size() == 1) { - return getLoadedModule(ModulePath[0].first); + return getLoadedModule(ModulePath[0].Item); } return nullptr; } @@ -1522,7 +1525,7 @@ static AllocationArena getArena(GenericSignature genericSig) { void ASTContext::registerGenericSignatureBuilder( GenericSignature sig, GenericSignatureBuilder &&builder) { - auto canSig = sig->getCanonicalSignature(); + auto canSig = sig.getCanonicalSignature(); auto arena = getArena(sig); auto &genericSignatureBuilders = getImpl().getArena(arena).GenericSignatureBuilders; @@ -1561,12 +1564,12 @@ GenericSignatureBuilder *ASTContext::getOrCreateGenericSignatureBuilder( auto builderSig = builder->computeGenericSignature(SourceLoc(), /*allowConcreteGenericParams=*/true); - if (builderSig->getCanonicalSignature() != sig) { + if (builderSig.getCanonicalSignature() != sig) { llvm::errs() << "ERROR: generic signature builder is not idempotent.\n"; llvm::errs() << "Original generic signature : "; sig->print(llvm::errs()); llvm::errs() << "\nReprocessed generic signature: "; - auto reprocessedSig = builderSig->getCanonicalSignature(); + auto reprocessedSig = builderSig.getCanonicalSignature(); reprocessedSig->print(llvm::errs()); llvm::errs() << "\n"; @@ -1708,16 +1711,14 @@ void ProtocolDecl::setDefaultTypeWitness(AssociatedTypeDecl *assocType, (void)pair; } -Optional -ProtocolDecl::getDefaultAssociatedConformanceWitness( - CanType association, - ProtocolDecl *requirement) const { +ProtocolConformanceRef ProtocolDecl::getDefaultAssociatedConformanceWitness( + CanType association, ProtocolDecl *requirement) const { auto &ctx = getASTContext(); auto found = ctx.getImpl().DefaultAssociatedConformanceWitnesses.find( std::make_tuple(this, association, requirement)); if (found == ctx.getImpl().DefaultAssociatedConformanceWitnesses.end()) - return None; + return ProtocolConformanceRef::forInvalid(); return found->second; } @@ -1747,13 +1748,18 @@ void ASTContext::getVisibleTopLevelModuleNames( names.erase(std::unique(names.begin(), names.end()), names.end()); } -bool ASTContext::canImportModule(std::pair ModulePath) { +bool ASTContext::shouldPerformTypoCorrection() { + NumTypoCorrections += 1; + return NumTypoCorrections <= LangOpts.TypoCorrectionLimit; +} + +bool ASTContext::canImportModule(Located ModulePath) { // If this module has already been successfully imported, it is importable. if (getLoadedModule(ModulePath) != nullptr) return true; // If we've failed loading this module before, don't look for it again. - if (FailedModuleImportNames.count(ModulePath.first)) + if (FailedModuleImportNames.count(ModulePath.Item)) return false; // Otherwise, ask the module loaders. @@ -1763,12 +1769,12 @@ bool ASTContext::canImportModule(std::pair ModulePath) { } } - FailedModuleImportNames.insert(ModulePath.first); + FailedModuleImportNames.insert(ModulePath.Item); return false; } ModuleDecl * -ASTContext::getModule(ArrayRef> ModulePath) { +ASTContext::getModule(ArrayRef> ModulePath) { assert(!ModulePath.empty()); if (auto *M = getLoadedModule(ModulePath)) @@ -1776,7 +1782,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { auto moduleID = ModulePath[0]; for (auto &importer : getImpl().ModuleLoaders) { - if (ModuleDecl *M = importer->loadModule(moduleID.second, ModulePath)) { + if (ModuleDecl *M = importer->loadModule(moduleID.Loc, ModulePath)) { return M; } } @@ -1785,7 +1791,7 @@ ASTContext::getModule(ArrayRef> ModulePath) { } ModuleDecl *ASTContext::getModuleByName(StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1802,7 +1808,7 @@ ModuleDecl *ASTContext::getStdlibModule(bool loadIfAbsent) { if (loadIfAbsent) { auto mutableThis = const_cast(this); TheStdlibModule = - mutableThis->getModule({ std::make_pair(StdlibModuleName, SourceLoc()) }); + mutableThis->getModule({ Located(StdlibModuleName, SourceLoc()) }); } else { TheStdlibModule = getLoadedModule(StdlibModuleName); } @@ -2425,7 +2431,8 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isInitializingCtor) { // initializing constructors of value types always have an implicitly // inout self. - selfAccess = SelfAccessKind::Mutating; + if (!containerTy->hasReferenceSemantics()) + selfAccess = SelfAccessKind::Mutating; } else { // allocating constructors have metatype 'self'. isStatic = true; @@ -2453,10 +2460,6 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isStatic) return AnyFunctionType::Param(MetatypeType::get(selfTy, Ctx)); - // Reference types have 'self' of type T. - if (containerTy->hasReferenceSemantics()) - return AnyFunctionType::Param(selfTy); - auto flags = ParameterTypeFlags(); switch (selfAccess) { case SelfAccessKind::Consuming: @@ -2917,9 +2920,10 @@ void AnyFunctionType::decomposeInput( } default: - result.emplace_back(type->getInOutObjectType(), Identifier(), - ParameterTypeFlags::fromParameterType( - type, false, false, ValueOwnership::Default)); + result.emplace_back( + type->getInOutObjectType(), Identifier(), + ParameterTypeFlags::fromParameterType(type, false, false, false, + ValueOwnership::Default, false)); return; } } @@ -3003,7 +3007,9 @@ void FunctionType::Profile(llvm::FoldingSetNodeID &ID, ExtInfo info) { profileParams(ID, params); ID.AddPointer(result.getPointer()); - ID.AddInteger(info.getFuncAttrKey()); + auto infoKey = info.getFuncAttrKey(); + ID.AddInteger(infoKey.first); + ID.AddPointer(infoKey.second); } FunctionType *FunctionType::get(ArrayRef params, @@ -3023,11 +3029,21 @@ FunctionType *FunctionType::get(ArrayRef params, return funcTy; } - void *mem = ctx.Allocate(sizeof(FunctionType) + - sizeof(AnyFunctionType::Param) * params.size(), - alignof(FunctionType), arena); + Optional uncommon = info.getUncommonInfo(); + + size_t allocSize = + totalSizeToAlloc( + params.size(), uncommon.hasValue() ? 1 : 0); + void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isFunctionTypeCanonical(params, result); + if (uncommon.hasValue()) { + if (ctx.LangOpts.UseClangFunctionTypes) + isCanonical &= uncommon->ClangFunctionType->isCanonicalUnqualified(); + else + isCanonical = false; + } + auto funcTy = new (mem) FunctionType(params, result, info, isCanonical ? &ctx : nullptr, properties); @@ -3044,6 +3060,9 @@ FunctionType::FunctionType(ArrayRef params, output, properties, params.size(), info) { std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); + Optional uncommon = info.getUncommonInfo(); + if (uncommon.hasValue()) + *getTrailingObjects() = uncommon.getValue(); } void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, @@ -3054,7 +3073,9 @@ void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, ID.AddPointer(sig.getPointer()); profileParams(ID, params); ID.AddPointer(result.getPointer()); - ID.AddInteger(info.getFuncAttrKey()); + auto infoKey = info.getFuncAttrKey(); + ID.AddInteger(infoKey.first); + ID.AddPointer(infoKey.second); } GenericFunctionType *GenericFunctionType::get(GenericSignature sig, @@ -3087,9 +3108,8 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig, return funcTy; } - void *mem = ctx.Allocate(sizeof(GenericFunctionType) + - sizeof(AnyFunctionType::Param) * params.size(), - alignof(GenericFunctionType)); + size_t allocSize = totalSizeToAlloc(params.size()); + void *mem = ctx.Allocate(allocSize, alignof(GenericFunctionType)); auto properties = getGenericFunctionRecursiveProperties(params, result); auto funcTy = new (mem) GenericFunctionType(sig, params, result, info, @@ -3135,18 +3155,28 @@ ArrayRef GenericFunctionType::getRequirements() const { return Signature->getRequirements(); } -void SILFunctionType::Profile(llvm::FoldingSetNodeID &id, - GenericSignature genericParams, - ExtInfo info, - SILCoroutineKind coroutineKind, - ParameterConvention calleeConvention, - ArrayRef params, - ArrayRef yields, - ArrayRef results, - Optional errorResult, - Optional conformance) { +void SILFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) const { + cml->printClangType(ClangFunctionType, os); +} + +void SILFunctionType::Profile( + llvm::FoldingSetNodeID &id, + GenericSignature genericParams, + ExtInfo info, + SILCoroutineKind coroutineKind, + ParameterConvention calleeConvention, + ArrayRef params, + ArrayRef yields, + ArrayRef results, + Optional errorResult, + ProtocolConformanceRef conformance, + bool isGenericSignatureImplied, + SubstitutionMap substitutions) { id.AddPointer(genericParams.getPointer()); - id.AddInteger(info.getFuncAttrKey()); + auto infoKey = info.getFuncAttrKey(); + id.AddInteger(infoKey.first); + id.AddPointer(infoKey.second); id.AddInteger(unsigned(coroutineKind)); id.AddInteger(unsigned(calleeConvention)); id.AddInteger(params.size()); @@ -3162,28 +3192,38 @@ void SILFunctionType::Profile(llvm::FoldingSetNodeID &id, // Just allow the profile length to implicitly distinguish the // presence of an error result. if (errorResult) errorResult->profile(id); + id.AddBoolean(isGenericSignatureImplied); + substitutions.profile(id); + id.AddBoolean((bool)conformance); if (conformance) - id.AddPointer(conformance->getRequirement()); -} - -SILFunctionType::SILFunctionType(GenericSignature genericSig, ExtInfo ext, - SILCoroutineKind coroutineKind, - ParameterConvention calleeConvention, - ArrayRef params, - ArrayRef yields, - ArrayRef normalResults, - Optional errorResult, - const ASTContext &ctx, - RecursiveTypeProperties properties, - Optional witnessMethodConformance) + id.AddPointer(conformance.getRequirement()); +} + +SILFunctionType::SILFunctionType( + GenericSignature genericSig, + ExtInfo ext, + SILCoroutineKind coroutineKind, + ParameterConvention calleeConvention, + ArrayRef params, + ArrayRef yields, + ArrayRef normalResults, + Optional errorResult, + SubstitutionMap substitutions, + bool genericSigIsImplied, + const ASTContext &ctx, + RecursiveTypeProperties properties, + ProtocolConformanceRef witnessMethodConformance) : TypeBase(TypeKind::SILFunction, &ctx, properties), - GenericSig(genericSig), - WitnessMethodConformance(witnessMethodConformance) { + GenericSigAndIsImplied(CanGenericSignature(genericSig), + genericSigIsImplied), + WitnessMethodConformance(witnessMethodConformance), + Substitutions(substitutions) { Bits.SILFunctionType.HasErrorResult = errorResult.hasValue(); - Bits.SILFunctionType.ExtInfo = ext.Bits; + Bits.SILFunctionType.ExtInfoBits = ext.Bits; + Bits.SILFunctionType.HasUncommonInfo = false; // The use of both assert() and static_assert() below is intentional. - assert(Bits.SILFunctionType.ExtInfo == ext.Bits && "Bits were dropped!"); + assert(Bits.SILFunctionType.ExtInfoBits == ext.Bits && "Bits were dropped!"); static_assert(ExtInfo::NumMaskBits == NumSILExtInfoBits, "ExtInfo and SILFunctionTypeBitfields must agree on bit size"); Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind); @@ -3220,13 +3260,25 @@ SILFunctionType::SILFunctionType(GenericSignature genericSig, ExtInfo ext, } #ifndef NDEBUG if (ext.getRepresentation() == Representation::WitnessMethod) - assert(WitnessMethodConformance && + assert(!WitnessMethodConformance.isInvalid() && "witness_method SIL function without a conformance"); else - assert(!WitnessMethodConformance && + assert(WitnessMethodConformance.isInvalid() && "non-witness_method SIL function with a conformance"); - // Make sure the interface types are sane. + // Make sure the type follows invariants. + assert((!substitutions || genericSig) + && "can only have substitutions with a generic signature"); + assert((!genericSigIsImplied || substitutions) + && "genericSigIsImplied should only be set for a type with generic " + "types and substitutions"); + + if (substitutions) { + assert(substitutions.getGenericSignature().getCanonicalSignature() == + genericSig.getCanonicalSignature() && + "substitutions must match generic signature"); + } + if (genericSig) { assert(!genericSig->areAllParamsConcrete() && "If all generic parameters are concrete, SILFunctionType should " @@ -3239,39 +3291,48 @@ SILFunctionType::SILFunctionType(GenericSignature genericSig, ExtInfo ext, for (auto param : getParameters()) { (void)param; - assert(!param.getType()->hasError() + assert(!param.getInterfaceType()->hasError() && "interface type of parameter should not contain error types"); - assert(!param.getType()->hasArchetype() + assert(!param.getInterfaceType()->hasArchetype() && "interface type of parameter should not contain context archetypes"); } for (auto result : getResults()) { (void)result; - assert(!result.getType()->hasError() + assert(!result.getInterfaceType()->hasError() && "interface type of result should not contain error types"); - assert(!result.getType()->hasArchetype() + assert(!result.getInterfaceType()->hasArchetype() && "interface type of result should not contain context archetypes"); } for (auto yield : getYields()) { (void)yield; - assert(!yield.getType()->hasError() + assert(!yield.getInterfaceType()->hasError() && "interface type of yield should not contain error types"); - assert(!yield.getType()->hasArchetype() + assert(!yield.getInterfaceType()->hasArchetype() && "interface type of yield should not contain context archetypes"); } if (hasErrorResult()) { - assert(!getErrorResult().getType()->hasError() + assert(!getErrorResult().getInterfaceType()->hasError() && "interface type of result should not contain error types"); - assert(!getErrorResult().getType()->hasArchetype() + assert(!getErrorResult().getInterfaceType()->hasArchetype() && "interface type of result should not contain context archetypes"); } } for (auto result : getResults()) { (void)result; - if (auto *FnType = result.getType()->getAs()) { + if (auto *FnType = result.getInterfaceType()->getAs()) { assert(!FnType->isNoEscape() && "Cannot return an @noescape function type"); } } + + // Check that `@noDerivative` parameters only exist on `@differentiable` + // functions. + if (!ext.isDifferentiable()) + for (auto param : getParameters()) + assert(param.getDifferentiability() == + SILParameterDifferentiability::DifferentiableOrNotApplicable && + "non-`@differentiable` function should not have NotDifferentiable " + "parameter"); #endif } @@ -3289,16 +3350,18 @@ CanSILBlockStorageType SILBlockStorageType::get(CanType captureType) { return CanSILBlockStorageType(storageTy); } -CanSILFunctionType SILFunctionType::get(GenericSignature genericSig, - ExtInfo ext, - SILCoroutineKind coroutineKind, - ParameterConvention callee, - ArrayRef params, - ArrayRef yields, - ArrayRef normalResults, - Optional errorResult, - const ASTContext &ctx, - Optional witnessMethodConformance) { +CanSILFunctionType SILFunctionType::get( + GenericSignature genericSig, + ExtInfo ext, SILCoroutineKind coroutineKind, + ParameterConvention callee, + ArrayRef params, + ArrayRef yields, + ArrayRef normalResults, + Optional errorResult, + SubstitutionMap substitutions, + bool genericSigIsImplied, + const ASTContext &ctx, + ProtocolConformanceRef witnessMethodConformance) { assert(coroutineKind == SILCoroutineKind::None || normalResults.empty()); assert(coroutineKind != SILCoroutineKind::None || yields.empty()); assert(!ext.isPseudogeneric() || genericSig); @@ -3306,7 +3369,8 @@ CanSILFunctionType SILFunctionType::get(GenericSignature genericSig, llvm::FoldingSetNodeID id; SILFunctionType::Profile(id, genericSig, ext, coroutineKind, callee, params, yields, normalResults, errorResult, - witnessMethodConformance); + witnessMethodConformance, genericSigIsImplied, + substitutions); // Do we already have this generic function type? void *insertPos; @@ -3316,27 +3380,27 @@ CanSILFunctionType SILFunctionType::get(GenericSignature genericSig, // All SILFunctionTypes are canonical. - // Allocate storage for the object. - size_t bytes = sizeof(SILFunctionType) - + sizeof(SILParameterInfo) * params.size() - + sizeof(SILYieldInfo) * yields.size() - + sizeof(SILResultInfo) * normalResults.size() - + (errorResult ? sizeof(SILResultInfo) : 0) - + (normalResults.size() > 1 ? sizeof(CanType) * 2 : 0); + // See [SILFunctionType-layout] + bool hasResultCache = normalResults.size() > 1; + size_t bytes = + totalSizeToAlloc( + params.size(), normalResults.size() + (errorResult ? 1 : 0), + yields.size(), hasResultCache ? 2 : 0); + void *mem = ctx.Allocate(bytes, alignof(SILFunctionType)); RecursiveTypeProperties properties; static_assert(RecursiveTypeProperties::BitWidth == 11, "revisit this if you add new recursive type properties"); for (auto ¶m : params) - properties |= param.getType()->getRecursiveProperties(); + properties |= param.getInterfaceType()->getRecursiveProperties(); for (auto &yield : yields) - properties |= yield.getType()->getRecursiveProperties(); + properties |= yield.getInterfaceType()->getRecursiveProperties(); for (auto &result : normalResults) - properties |= result.getType()->getRecursiveProperties(); + properties |= result.getInterfaceType()->getRecursiveProperties(); if (errorResult) - properties |= errorResult->getType()->getRecursiveProperties(); - + properties |= errorResult->getInterfaceType()->getRecursiveProperties(); + // FIXME: If we ever have first-class polymorphic values, we'll need to // revisit this. if (genericSig) { @@ -3344,15 +3408,21 @@ CanSILFunctionType SILFunctionType::get(GenericSignature genericSig, properties.removeHasDependentMember(); } + for (auto replacement : substitutions.getReplacementTypes()) { + properties |= replacement->getRecursiveProperties(); + } + auto fnType = new (mem) SILFunctionType(genericSig, ext, coroutineKind, callee, params, yields, normalResults, errorResult, + substitutions, genericSigIsImplied, ctx, properties, witnessMethodConformance); + assert(fnType->hasResultCache() == hasResultCache); + ctx.getImpl().SILFunctionTypes.InsertNode(fnType, insertPos); return CanSILFunctionType(fnType); } - ArraySliceType *ArraySliceType::get(Type base) { auto properties = base->getRecursiveProperties(); auto arena = getArena(properties); @@ -3690,8 +3760,8 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const { auto thisType = Type(const_cast(this)); auto &ctx = thisType->getASTContext(); // Create a generic environment to represent the opened type. - auto signature = ctx.getExistentialSignature(Opened->getCanonicalType(), - nullptr); + auto signature = + ctx.getOpenedArchetypeSignature(Opened->getCanonicalType(), nullptr); auto *builder = signature->getGenericSignatureBuilder(); auto *env = GenericEnvironment::getIncomplete(signature, builder); env->addMapping(signature->getGenericParams()[0], thisType); @@ -3913,18 +3983,13 @@ void DeclName::CompoundDeclName::Profile(llvm::FoldingSetNodeID &id, void DeclName::initialize(ASTContext &C, DeclBaseName baseName, ArrayRef argumentNames) { - if (argumentNames.empty()) { - SimpleOrCompound = BaseNameAndCompound(baseName, true); - return; - } - llvm::FoldingSetNodeID id; CompoundDeclName::Profile(id, baseName, argumentNames); void *insert = nullptr; if (CompoundDeclName *compoundName = C.getImpl().CompoundNames.FindNodeOrInsertPos(id, insert)) { - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; return; } @@ -3934,7 +3999,7 @@ void DeclName::initialize(ASTContext &C, DeclBaseName baseName, auto compoundName = new (buf) CompoundDeclName(baseName,argumentNames.size()); std::uninitialized_copy(argumentNames.begin(), argumentNames.end(), compoundName->getArgumentNames().begin()); - SimpleOrCompound = compoundName; + BaseNameOrCompound = compoundName; C.getImpl().CompoundNames.InsertNode(compoundName, insert); } @@ -4133,11 +4198,11 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal, if (nominal != dc->getASTContext().getOptionalDecl()) { if (auto objcBridgeable = getProtocol(KnownProtocolKind::ObjectiveCBridgeable)) { - if (auto conformance - = dc->getParentModule()->lookupConformance( - nominal->getDeclaredType(), objcBridgeable)) { + auto conformance = dc->getParentModule()->lookupConformance( + nominal->getDeclaredType(), objcBridgeable); + if (conformance) { result = - ForeignRepresentationInfo::forBridged(conformance->getConcrete()); + ForeignRepresentationInfo::forBridged(conformance.getConcrete()); } } } @@ -4262,39 +4327,40 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, // Check whether the type is an existential that contains // Error. If so, it's bridged to NSError. if (type->isExistentialWithError()) { - if (auto nsErrorDecl = getNSErrorDecl()) { + if (auto nsErrorTy = getNSErrorType()) { // The corresponding value type is Error. if (bridgedValueType) *bridgedValueType = getErrorDecl()->getDeclaredInterfaceType(); - return nsErrorDecl->getDeclaredInterfaceType(); + return nsErrorTy; } } // Try to find a conformance that will enable bridging. auto findConformance = - [&](KnownProtocolKind known) -> Optional { - // Don't ascribe any behavior to Optional other than what we explicitly - // give it. We don't want things like AnyObject?? to work. - if (type->getAnyNominal() == getOptionalDecl()) - return None; - - // Find the protocol. - auto proto = getProtocol(known); - if (!proto) return None; - - return dc->getParentModule()->lookupConformance(type, proto); - }; + [&](KnownProtocolKind known) -> ProtocolConformanceRef { + // Don't ascribe any behavior to Optional other than what we explicitly + // give it. We don't want things like AnyObject?? to work. + if (type->getAnyNominal() == getOptionalDecl()) + return ProtocolConformanceRef::forInvalid(); + + // Find the protocol. + auto proto = getProtocol(known); + if (!proto) + return ProtocolConformanceRef::forInvalid(); + + return dc->getParentModule()->lookupConformance(type, proto); + }; // Do we conform to _ObjectiveCBridgeable? - if (auto conformance - = findConformance(KnownProtocolKind::ObjectiveCBridgeable)) { + if (auto conformance = + findConformance(KnownProtocolKind::ObjectiveCBridgeable)) { // The corresponding value type is... the type. if (bridgedValueType) *bridgedValueType = type; // Find the Objective-C class type we bridge to. - return conformance->getTypeWitnessByName(type, Id_ObjectiveCType); + return conformance.getTypeWitnessByName(type, Id_ObjectiveCType); } // Do we conform to Error? @@ -4304,14 +4370,27 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, *bridgedValueType = getErrorDecl()->getDeclaredInterfaceType(); // Bridge to NSError. - if (auto nsErrorDecl = getNSErrorDecl()) - return nsErrorDecl->getDeclaredInterfaceType(); + if (auto nsErrorTy = getNSErrorType()) + return nsErrorTy; } // No special bridging to Objective-C, but this can become an 'Any'. return Type(); } +const clang::Type * +ASTContext::getClangFunctionType(ArrayRef params, + Type resultTy, + FunctionType::ExtInfo incompleteExtInfo, + FunctionTypeRepresentation trueRep) { + auto &impl = getImpl(); + if (!impl.Converter) { + auto *cml = getClangModuleLoader(); + impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); + } + return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); +} + CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { if (auto theSig = getImpl().SingleGenericParameterSignature) return theSig; @@ -4323,8 +4402,15 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { return canonicalSig; } -CanGenericSignature ASTContext::getExistentialSignature(CanType existential, - ModuleDecl *mod) { +// Return the signature for an opened existential. The opened archetype may have +// a different set of conformances from the corresponding existential. The +// opened archetype conformances are dictated by the ABI for generic arguments, +// while the existential value conformances are dictated by their layout (see +// Type::getExistentialLayout()). In particular, the opened archetype signature +// does not have requirements for conformances inherited from superclass +// constraints while existential values do. +CanGenericSignature ASTContext::getOpenedArchetypeSignature(CanType existential, + ModuleDecl *mod) { auto found = getImpl().ExistentialSignatures.find(existential); if (found != getImpl().ExistentialSignatures.end()) return found->second; @@ -4354,44 +4440,32 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, const ValueDecl *derived) { auto baseGenericCtx = base->getAsGenericContext(); auto derivedGenericCtx = derived->getAsGenericContext(); - auto &ctx = base->getASTContext(); - if (!baseGenericCtx) { + if (!baseGenericCtx || !derivedGenericCtx) return nullptr; - } - auto baseClass = base->getDeclContext()->getSelfClassDecl(); + if (base == derived) + return derivedGenericCtx->getGenericSignature(); - if (!baseClass) { + auto baseClass = base->getDeclContext()->getSelfClassDecl(); + if (!baseClass) return nullptr; - } auto derivedClass = derived->getDeclContext()->getSelfClassDecl(); - auto baseClassSig = baseClass->getGenericSignature(); - - if (!derivedClass) { + if (!derivedClass) return nullptr; - } - if (derivedClass->getSuperclass().isNull()) { + if (derivedClass->getSuperclass().isNull()) return nullptr; - } - if (!derivedGenericCtx || !derivedGenericCtx->isGeneric()) { + if (baseGenericCtx->getGenericSignature().isNull() || + derivedGenericCtx->getGenericSignature().isNull()) return nullptr; - } - - if (derivedClass->getGenericSignature().isNull() && - !baseGenericCtx->isGeneric()) { - return nullptr; - } + auto baseClassSig = baseClass->getGenericSignature(); auto subMap = derivedClass->getSuperclass()->getContextSubstitutionMap( derivedClass->getModuleContext(), baseClass); - if (baseGenericCtx->getGenericSignature().isNull()) { - return nullptr; - } unsigned derivedDepth = 0; auto key = OverrideSignatureKey(baseGenericCtx->getGenericSignature(), @@ -4407,9 +4481,11 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, derivedDepth = derivedSig->getGenericParams().back()->getDepth() + 1; SmallVector addedGenericParams; - for (auto gp : *derivedGenericCtx->getGenericParams()) { - addedGenericParams.push_back( - gp->getDeclaredInterfaceType()->castTo()); + if (auto *gpList = derivedGenericCtx->getGenericParams()) { + for (auto gp : *gpList) { + addedGenericParams.push_back( + gp->getDeclaredInterfaceType()->castTo()); + } } unsigned baseDepth = 0; @@ -4426,12 +4502,12 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } return CanGenericTypeParamType::get( - gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), ctx); + gp->getDepth() - baseDepth + derivedDepth, gp->getIndex(), *this); }; auto lookupConformanceFn = [&](CanType depTy, Type substTy, - ProtocolDecl *proto) -> Optional { + ProtocolDecl *proto) -> ProtocolConformanceRef { if (auto conf = subMap.lookupConformance(depTy, proto)) return conf; @@ -4446,7 +4522,7 @@ ASTContext::getOverrideGenericSignature(const ValueDecl *base, } auto genericSig = evaluateOrDefault( - ctx.evaluator, + evaluator, AbstractGenericSignatureRequest{ derivedClass->getGenericSignature().getPointer(), std::move(addedGenericParams), @@ -4634,7 +4710,7 @@ IndexSubset::get(ASTContext &ctx, const SmallBitVector &indices) { if (existing) return existing; auto sizeToAlloc = sizeof(IndexSubset) + - getNumBitWordsNeededForCapacity(capacity); + getNumBytesNeededForCapacity(capacity); auto *buf = reinterpret_cast( ctx.Allocate(sizeToAlloc, alignof(IndexSubset))); auto *newNode = new (buf) IndexSubset(indices); diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index a20c44139c75c..7be40882ef5b2 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -348,33 +348,6 @@ Type ASTBuilder::createTupleType(ArrayRef eltTypes, Type ASTBuilder::createFunctionType( ArrayRef> params, Type output, FunctionTypeFlags flags) { - FunctionTypeRepresentation representation; - switch (flags.getConvention()) { - case FunctionMetadataConvention::Swift: - representation = FunctionTypeRepresentation::Swift; - break; - case FunctionMetadataConvention::Block: - representation = FunctionTypeRepresentation::Block; - break; - case FunctionMetadataConvention::Thin: - representation = FunctionTypeRepresentation::Thin; - break; - case FunctionMetadataConvention::CFunctionPointer: - representation = FunctionTypeRepresentation::CFunctionPointer; - break; - } - - auto einfo = AnyFunctionType::ExtInfo(representation, - /*throws*/ flags.throws()); - - if (representation == FunctionTypeRepresentation::Swift || - representation == FunctionTypeRepresentation::Block) { - if (flags.isEscaping()) - einfo = einfo.withNoEscape(false); - else - einfo = einfo.withNoEscape(true); - } - // The result type must be materializable. if (!output->isMaterializable()) return Type(); @@ -397,6 +370,42 @@ Type ASTBuilder::createFunctionType( funcParams.push_back(AnyFunctionType::Param(type, label, parameterFlags)); } + FunctionTypeRepresentation representation; + switch (flags.getConvention()) { + case FunctionMetadataConvention::Swift: + representation = FunctionTypeRepresentation::Swift; + break; + case FunctionMetadataConvention::Block: + representation = FunctionTypeRepresentation::Block; + break; + case FunctionMetadataConvention::Thin: + representation = FunctionTypeRepresentation::Thin; + break; + case FunctionMetadataConvention::CFunctionPointer: + representation = FunctionTypeRepresentation::CFunctionPointer; + break; + } + + auto noescape = + (representation == FunctionTypeRepresentation::Swift + || representation == FunctionTypeRepresentation::Block) + && !flags.isEscaping(); + + FunctionType::ExtInfo incompleteExtInfo( + FunctionTypeRepresentation::Swift, + noescape, flags.throws(), + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/nullptr); + + const clang::Type *clangFunctionType = nullptr; + if (representation == FunctionTypeRepresentation::CFunctionPointer) + clangFunctionType = Ctx.getClangFunctionType(funcParams, output, + incompleteExtInfo, + representation); + + auto einfo = incompleteExtInfo.withRepresentation(representation) + .withClangFunctionType(clangFunctionType); + return FunctionType::get(funcParams, output, einfo); } @@ -445,7 +454,7 @@ Type ASTBuilder::createImplFunctionType( ArrayRef> results, Optional> errorResult, ImplFunctionTypeFlags flags) { - GenericSignature genericSig = GenericSignature(); + GenericSignature genericSig; SILCoroutineKind funcCoroutineKind = SILCoroutineKind::None; ParameterConvention funcCalleeConvention = @@ -479,9 +488,11 @@ Type ASTBuilder::createImplFunctionType( break; } - auto einfo = SILFunctionType::ExtInfo(representation, - flags.isPseudogeneric(), - !flags.isEscaping()); + // TODO: [store-sil-clang-function-type] + auto einfo = SILFunctionType::ExtInfo( + representation, flags.isPseudogeneric(), !flags.isEscaping(), + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr); llvm::SmallVector funcParams; llvm::SmallVector funcYields; @@ -505,10 +516,10 @@ Type ASTBuilder::createImplFunctionType( auto conv = getResultConvention(errorResult->getConvention()); funcErrorResult.emplace(type, conv); } - return SILFunctionType::get(genericSig, einfo, funcCoroutineKind, funcCalleeConvention, funcParams, funcYields, - funcResults, funcErrorResult, Ctx); + funcResults, funcErrorResult, + SubstitutionMap(), false, Ctx); } Type ASTBuilder::createProtocolCompositionType( @@ -854,12 +865,13 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( } } - return evaluateOrDefault( - Ctx.evaluator, - AbstractGenericSignatureRequest{ - nominalDecl->getGenericSignature().getPointer(), { }, - std::move(requirements)}, - GenericSignature())->getCanonicalSignature(); + return evaluateOrDefault(Ctx.evaluator, + AbstractGenericSignatureRequest{ + nominalDecl->getGenericSignature().getPointer(), + {}, + std::move(requirements)}, + GenericSignature()) + .getCanonicalSignature(); } DeclContext * @@ -969,8 +981,7 @@ ASTBuilder::findDeclContext(NodePointer node) { continue; } - if (ext->getGenericSignature()->getCanonicalSignature() - == genericSig) { + if (ext->getGenericSignature().getCanonicalSignature() == genericSig) { return ext; } } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 97ea62b0bc404..dd93348812558 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -27,6 +27,7 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/QuotedString.h" #include "swift/Basic/STLExtras.h" +#include "clang/AST/Type.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" @@ -185,7 +186,7 @@ void GenericParamList::print(llvm::raw_ostream &OS) const { OS << '>'; } -void GenericParamList::dump() { +void GenericParamList::dump() const { print(llvm::errs()); llvm::errs() << '\n'; } @@ -299,6 +300,7 @@ static StringRef getDefaultArgumentKindString(DefaultArgumentKind value) { case DefaultArgumentKind::Column: return "#column"; case DefaultArgumentKind::DSOHandle: return "#dsohandle"; case DefaultArgumentKind::File: return "#file"; + case DefaultArgumentKind::FilePath: return "#filePath"; case DefaultArgumentKind::Function: return "#function"; case DefaultArgumentKind::Inherited: return "inherited"; case DefaultArgumentKind::Line: return "#line"; @@ -312,18 +314,6 @@ static StringRef getDefaultArgumentKindString(DefaultArgumentKind value) { llvm_unreachable("Unhandled DefaultArgumentKind in switch."); } static StringRef -getMagicIdentifierLiteralExprKindString(MagicIdentifierLiteralExpr::Kind value) { - switch (value) { - case MagicIdentifierLiteralExpr::File: return "#file"; - case MagicIdentifierLiteralExpr::Function: return "#function"; - case MagicIdentifierLiteralExpr::Line: return "#line"; - case MagicIdentifierLiteralExpr::Column: return "#column"; - case MagicIdentifierLiteralExpr::DSOHandle: return "#dsohandle"; - } - - llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch."); -} -static StringRef getObjCSelectorExprKindString(ObjCSelectorExpr::ObjCSelectorKind value) { switch (value) { case ObjCSelectorExpr::Method: return "method"; @@ -586,7 +576,7 @@ namespace { OS << " '"; interleave(ID->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { - OS << Elem.first; + OS << Elem.Item; }, [&] { OS << '.'; }); OS << "')"; @@ -725,7 +715,7 @@ namespace { if (auto *var = dyn_cast(VD)) { PrintWithColorRAII(OS, TypeColor) << " type='"; - if (var->hasType()) + if (auto varTy = var->hasInterfaceType()) var->getType().print(PrintWithColorRAII(OS, TypeColor).getOS()); else PrintWithColorRAII(OS, TypeColor) << ""; @@ -791,7 +781,7 @@ namespace { PrintWithColorRAII(OS, ASTNodeColor) << "source_file "; PrintWithColorRAII(OS, LocationColor) << '\"' << SF.getFilename() << '\"'; - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { if (D->isImplicit()) continue; @@ -895,20 +885,20 @@ namespace { void visitPatternBindingDecl(PatternBindingDecl *PBD) { printCommon(PBD, "pattern_binding_decl"); - for (auto entry : PBD->getPatternList()) { + for (auto idx : range(PBD->getNumPatternEntries())) { OS << '\n'; - printRec(entry.getPattern()); - if (entry.getOriginalInit()) { + printRec(PBD->getPattern(idx)); + if (PBD->getOriginalInit(idx)) { OS << '\n'; OS.indent(Indent + 2); OS << "Original init:\n"; - printRec(entry.getOriginalInit()); + printRec(PBD->getOriginalInit(idx)); } - if (entry.getInit()) { + if (PBD->getInit(idx)) { OS << '\n'; OS.indent(Indent + 2); OS << "Processed init:\n"; - printRec(entry.getInit()); + printRec(PBD->getInit(idx)); } } PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -954,13 +944,11 @@ namespace { PrintWithColorRAII(OS, IdentifierColor) << " apiName=" << P->getArgumentName(); - if (P->hasType()) { + if (P->hasInterfaceType()) { PrintWithColorRAII(OS, TypeColor) << " type='"; P->getType().print(PrintWithColorRAII(OS, TypeColor).getOS()); PrintWithColorRAII(OS, TypeColor) << "'"; - } - if (P->hasInterfaceType()) { PrintWithColorRAII(OS, InterfaceTypeColor) << " interface type='"; P->getInterfaceType().print( PrintWithColorRAII(OS, InterfaceTypeColor).getOS()); @@ -990,19 +978,22 @@ namespace { if (P->isAutoClosure()) OS << " autoclosure"; + if (P->isNonEphemeral()) + OS << " nonEphemeral"; + if (P->getDefaultArgumentKind() != DefaultArgumentKind::None) { printField("default_arg", getDefaultArgumentKindString(P->getDefaultArgumentKind())); } - if (P->getDefaultValue() && - !P->getDefaultArgumentCaptureInfo().isTrivial()) { + if (P->hasDefaultExpr() && + !P->getDefaultArgumentCaptureInfo().isTrivial()) { OS << " "; P->getDefaultArgumentCaptureInfo().print( PrintWithColorRAII(OS, CapturesColor).getOS()); } - if (auto init = P->getDefaultValue()) { + if (auto init = P->getStructuralDefaultExpr()) { OS << " expression=\n"; printRec(init); } @@ -1261,15 +1252,6 @@ void ParameterList::dump() const { } void ParameterList::dump(raw_ostream &OS, unsigned Indent) const { - llvm::Optional> X; - - // Make sure to print type variables if we can get to ASTContext. - if (size() != 0 && get(0)) { - auto &ctx = get(0)->getASTContext(); - X.emplace(llvm::SaveAndRestore(ctx.LangOpts.DebugConstraintSolver, - true)); - } - PrintDecl(OS, Indent).printParameterList(this); llvm::errs() << '\n'; } @@ -1292,9 +1274,6 @@ void Decl::dump(const char *filename) const { } void Decl::dump(raw_ostream &OS, unsigned Indent) const { - // Make sure to print type variables. - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); PrintDecl(OS, Indent).visit(const_cast(this)); OS << '\n'; } @@ -1406,8 +1385,6 @@ void SourceFile::dump() const { } void SourceFile::dump(llvm::raw_ostream &OS) const { - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); PrintDecl(OS).visitSourceFile(*this); llvm::errs() << '\n'; } @@ -1603,12 +1580,6 @@ class PrintStmt : public StmtVisitor { } void visitForEachStmt(ForEachStmt *S) { printCommon(S, "for_each_stmt"); - PrintWithColorRAII(OS, LiteralValueColor) << " make_generator="; - S->getMakeIterator().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); - PrintWithColorRAII(OS, LiteralValueColor) << " next="; - S->getIteratorNext().dump( - PrintWithColorRAII(OS, LiteralValueColor).getOS()); OS << '\n'; printRec(S->getPattern()); OS << '\n'; @@ -1825,13 +1796,17 @@ class PrintExpr : public ExprVisitor { } raw_ostream &printCommon(Expr *E, const char *C) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + OS.indent(Indent); PrintWithColorRAII(OS, ParenthesisColor) << '('; PrintWithColorRAII(OS, ExprColor) << C; if (E->isImplicit()) PrintWithColorRAII(OS, ExprModifierColor) << " implicit"; - PrintWithColorRAII(OS, TypeColor) << " type='" << GetTypeOfExpr(E) << '\''; + PrintWithColorRAII(OS, TypeColor) << " type='"; + PrintWithColorRAII(OS, TypeColor) << GetTypeOfExpr(E).getString(PO) << '\''; // If we have a source range and an ASTContext, print the source range. if (auto Ty = GetTypeOfExpr(E)) { @@ -1969,7 +1944,7 @@ class PrintExpr : public ExprVisitor { } void visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E) { printCommon(E, "magic_identifier_literal_expr") - << " kind=" << getMagicIdentifierLiteralExprKindString(E->getKind()); + << " kind=" << MagicIdentifierLiteralExpr::getKindString(E->getKind()); if (E->isString()) { OS << " encoding=" @@ -2503,12 +2478,6 @@ class PrintExpr : public ExprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void visitCallerDefaultArgumentExpr(CallerDefaultArgumentExpr *E) { - printCommon(E, "caller_default_argument_expr"); - printRec(E->getSubExpr()); - PrintWithColorRAII(OS, ParenthesisColor) << ')'; - } - void printArgumentLabels(ArrayRef argLabels) { PrintWithColorRAII(OS, ArgumentsColor) << " arg_labels="; for (auto label : argLabels) { @@ -2896,7 +2865,7 @@ class PrintTypeRepr : public TypeReprVisitor { OS << '\n'; printCommon("component"); PrintWithColorRAII(OS, IdentifierColor) - << " id='" << comp->getIdentifier() << '\''; + << " id='" << comp->getNameRef() << '\''; OS << " bind="; if (comp->isBound()) comp->getBoundDecl()->dumpRef(OS); @@ -2958,13 +2927,6 @@ class PrintTypeRepr : public TypeReprVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void visitImplicitlyUnwrappedOptionalTypeRepr( - ImplicitlyUnwrappedOptionalTypeRepr *T) { - printCommon("implicitly_unwrapped_optional"); - OS << "\n"; - printRec(T->getBase()); - } - void visitCompositionTypeRepr(CompositionTypeRepr *T) { printCommon("type_composite"); for (auto elem : T->getTypes()) { @@ -3003,6 +2965,41 @@ class PrintTypeRepr : public TypeReprVisitor { printRec(T->getBase()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + + void visitOptionalTypeRepr(OptionalTypeRepr *T) { + printCommon("type_optional") << '\n'; + printRec(T->getBase()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + + void visitImplicitlyUnwrappedOptionalTypeRepr( + ImplicitlyUnwrappedOptionalTypeRepr *T) { + printCommon("type_implicitly_unwrapped_optional") << '\n'; + printRec(T->getBase()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + + void visitOpaqueReturnTypeRepr(OpaqueReturnTypeRepr *T) { + printCommon("type_opaque_return"); + printRec(T->getConstraint()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + + void visitFixedTypeRepr(FixedTypeRepr *T) { + printCommon("type_fixed"); + auto Ty = T->getType(); + if (Ty) { + auto &srcMgr = Ty->getASTContext().SourceMgr; + if (T->getLoc().isValid()) { + OS << " location=@"; + T->getLoc().print(OS, srcMgr); + } else { + OS << " location=<>"; + } + } + OS << " type="; Ty.dump(OS); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } }; } // end anonymous namespace @@ -3315,6 +3312,7 @@ namespace { void dumpParameterFlags(ParameterTypeFlags paramFlags) { printFlag(paramFlags.isVariadic(), "vararg"); printFlag(paramFlags.isAutoClosure(), "autoclosure"); + printFlag(paramFlags.isNonEphemeral(), "nonEphemeral"); switch (paramFlags.getValueOwnership()) { case ValueOwnership::Default: break; case ValueOwnership::Owned: printFlag("owned"); break; @@ -3582,7 +3580,7 @@ namespace { if (auto assocType = T->getAssocType()) { printField("assoc_type", assocType->printRef()); } else { - printField("name", T->getName().str()); + printField("name", T->getName()); } printRec("base", T->getBase()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; @@ -3622,6 +3620,12 @@ namespace { OS << "\n"; Indent += 2; + if (auto *cty = T->getClangFunctionType()) { + std::string s; + llvm::raw_string_ostream os(s); + cty->dump(os); + printField("clang_type", os.str()); + } printAnyFunctionParams(T->getParams(), "input"); Indent -=2; printRec("output", T->getResult()); @@ -3754,14 +3758,14 @@ namespace { } // end anonymous namespace void Type::dump() const { - // Make sure to print type variables. dump(llvm::errs()); } void Type::dump(raw_ostream &os, unsigned indent) const { - // Make sure to print type variables. - llvm::SaveAndRestore X(getPointer()->getASTContext().LangOpts. - DebugConstraintSolver, true); + #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. + #endif + PrintType(os, indent).visit(*this, ""); os << "\n"; } @@ -3772,10 +3776,6 @@ void TypeBase::dump() const { } void TypeBase::dump(raw_ostream &os, unsigned indent) const { - auto &ctx = const_cast(this)->getASTContext(); - - // Make sure to print type variables. - llvm::SaveAndRestore X(ctx.LangOpts.DebugConstraintSolver, true); Type(const_cast(this)).dump(os, indent); } diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 668cfd6378719..1b7383421e320 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -113,7 +113,7 @@ std::string ASTMangler::mangleIVarInitDestroyEntity(const ClassDecl *decl, bool isDestroyer, SymbolKind SKind) { beginMangling(); - appendContext(decl); + appendContext(decl, decl->getAlternateModuleName()); appendOperator(isDestroyer ? "fE" : "fe"); appendSymbolKind(SKind); return finalize(); @@ -357,9 +357,10 @@ std::string ASTMangler::mangleReabstractionThunkHelper( Type SelfType, ModuleDecl *Module) { Mod = Module; - GenericSignature GenSig = ThunkType->getGenericSignature(); + assert(ThunkType->getSubstitutions().empty() && "not implemented"); + GenericSignature GenSig = ThunkType->getSubstGenericSignature(); if (GenSig) - CurGenericSignature = GenSig->getCanonicalSignature(); + CurGenericSignature = GenSig.getCanonicalSignature(); beginMangling(); appendType(FromType); @@ -435,7 +436,7 @@ std::string ASTMangler::mangleObjCRuntimeName(const NominalTypeDecl *Nominal) { assert(isa(Nominal)); Buffer << 'P'; } - appendModule(Ctx->getParentModule()); + appendModule(Ctx->getParentModule(), StringRef()); appendIdentifier(Nominal->getName().str()); if (isProto) Buffer << '_'; @@ -478,7 +479,7 @@ std::string ASTMangler::mangleObjCRuntimeName(const NominalTypeDecl *Nominal) { std::string ASTMangler::mangleTypeAsContextUSR(const NominalTypeDecl *type) { beginManglingWithoutPrefix(); llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); - appendContext(type); + appendContext(type, type->getAlternateModuleName()); return finalize(); } @@ -498,6 +499,7 @@ std::string ASTMangler::mangleTypeAsUSR(Type Ty) { std::string ASTMangler::mangleDeclAsUSR(const ValueDecl *Decl, StringRef USRPrefix) { + DWARFMangling = true; beginManglingWithoutPrefix(); llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); Buffer << USRPrefix; @@ -517,18 +519,21 @@ std::string ASTMangler::mangleDeclAsUSR(const ValueDecl *Decl, appendEntity(Decl); } - // We have a custom prefix, so finalize() won't verify for us. Do it manually. - verify(Storage.str().drop_front(USRPrefix.size())); + // We have a custom prefix, so finalize() won't verify for us. If we're not + // in invalid code (coming from an IDE caller) verify manually. + if (!Decl->isInvalid()) + verify(Storage.str().drop_front(USRPrefix.size())); return finalize(); } std::string ASTMangler::mangleAccessorEntityAsUSR(AccessorKind kind, const AbstractStorageDecl *decl, - StringRef USRPrefix) { + StringRef USRPrefix, + bool isStatic) { beginManglingWithoutPrefix(); llvm::SaveAndRestore allowUnnamedRAII(AllowNamelessEntities, true); Buffer << USRPrefix; - appendAccessorEntity(getCodeForAccessorKind(kind), decl, /*isStatic*/ false); + appendAccessorEntity(getCodeForAccessorKind(kind), decl, isStatic); // We have a custom prefix, so finalize() won't verify for us. Do it manually. verify(Storage.str().drop_front(USRPrefix.size())); return finalize(); @@ -1194,7 +1199,17 @@ void ASTMangler::bindGenericParameters(CanGenericSignature sig) { /// Bind the generic parameters from the given context and its parents. void ASTMangler::bindGenericParameters(const DeclContext *DC) { if (auto sig = DC->getGenericSignatureOfContext()) - bindGenericParameters(sig->getCanonicalSignature()); + bindGenericParameters(sig.getCanonicalSignature()); +} + +void ASTMangler::appendFlatGenericArgs(SubstitutionMap subs) { + appendOperator("y"); + + for (auto replacement : subs.getReplacementTypes()) { + if (replacement->hasArchetype()) + replacement = replacement->mapTypeOutOfContext(); + appendType(replacement); + } } unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc, @@ -1333,16 +1348,15 @@ static bool containsRetroactiveConformance( continue; ProtocolDecl *proto = requirement.getSecondType()->castTo()->getDecl(); - Optional conformance = - subMap.lookupConformance(requirement.getFirstType()->getCanonicalType(), - proto); - if (!conformance) { + auto conformance = subMap.lookupConformance( + requirement.getFirstType()->getCanonicalType(), proto); + if (conformance.isInvalid()) { // This should only happen when mangling invalid ASTs, but that happens // for indexing purposes. continue; } - if (conformance->isConcrete() && - containsRetroactiveConformance(conformance->getConcrete(), module)) { + if (conformance.isConcrete() && + containsRetroactiveConformance(conformance.getConcrete(), module)) { return true; } } @@ -1426,6 +1440,13 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { llvm::SmallVector OpArgs; + if (fn->getSubstitutions()) { + OpArgs.push_back('s'); + if (!fn->isGenericSignatureImplied()) { + OpArgs.push_back('i'); + } + } + if (fn->isPolymorphic() && fn->isPseudogeneric()) OpArgs.push_back('P'); @@ -1462,17 +1483,17 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { OpArgs.push_back('W'); break; } - + // Mangle the parameters. for (auto param : fn->getParameters()) { OpArgs.push_back(getParamConvention(param.getConvention())); - appendType(param.getType()); + appendType(param.getInterfaceType()); } // Mangle the results. for (auto result : fn->getResults()) { OpArgs.push_back(getResultConvention(result.getConvention())); - appendType(result.getType()); + appendType(result.getInterfaceType()); } // Mangle the error result if present. @@ -1480,10 +1501,15 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) { auto error = fn->getErrorResult(); OpArgs.push_back('z'); OpArgs.push_back(getResultConvention(error.getConvention())); - appendType(error.getType()); + appendType(error.getInterfaceType()); + } + if (auto sig = fn->getSubstGenericSignature()) { + appendGenericSignature(sig); + } + if (auto subs = fn->getSubstitutions()) { + appendFlatGenericArgs(subs); + appendRetroactiveConformances(subs, Mod); } - if (fn->isPolymorphic()) - appendGenericSignature(fn->getGenericSignature()); OpArgs.push_back('_'); @@ -1559,7 +1585,7 @@ void ASTMangler::appendContextOf(const ValueDecl *decl) { } // Just mangle the decl's DC. - appendContext(decl->getDeclContext()); + appendContext(decl->getDeclContext(), decl->getAlternateModuleName()); } namespace { @@ -1606,8 +1632,8 @@ namespace { /// least one name, which is probably a reasonable assumption but may /// not be adequately enforced. static Optional findFirstVariable(PatternBindingDecl *binding) { - for (auto entry : binding->getPatternList()) { - auto var = FindFirstVariable().visit(entry.getPattern()); + for (auto idx : range(binding->getNumPatternEntries())) { + auto var = FindFirstVariable().visit(binding->getPattern(idx)); if (var) return var; } // Pattern-binding bound without variables exists in erroneous code, e.g. @@ -1615,14 +1641,14 @@ static Optional findFirstVariable(PatternBindingDecl *binding) { return None; } -void ASTMangler::appendContext(const DeclContext *ctx) { +void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName) { switch (ctx->getContextKind()) { case DeclContextKind::Module: - return appendModule(cast(ctx)); + return appendModule(cast(ctx), useModuleName); case DeclContextKind::FileUnit: assert(!isa(ctx) && "mangling member of builtin module!"); - appendContext(ctx->getParent()); + appendContext(ctx->getParent(), useModuleName); return; case DeclContextKind::SerializedLocal: { @@ -1643,12 +1669,12 @@ void ASTMangler::appendContext(const DeclContext *ctx) { } else { // This is incorrect in that it does not produce a /unique/ mangling, // but it will at least produce a /valid/ mangling. - appendContext(ctx->getParent()); + appendContext(ctx->getParent(), useModuleName); } return; } case LocalDeclContextKind::TopLevelCodeDecl: - return appendContext(local->getParent()); + return appendContext(local->getParent(), useModuleName); } } @@ -1661,7 +1687,7 @@ void ASTMangler::appendContext(const DeclContext *ctx) { auto decl = ExtD->getExtendedNominal(); // Recover from erroneous extension. if (!decl) - return appendContext(ExtD->getDeclContext()); + return appendContext(ExtD->getDeclContext(), useModuleName); if (!ExtD->isEquivalentToExtendedContext()) { // Mangle the extension if: @@ -1677,7 +1703,7 @@ void ASTMangler::appendContext(const DeclContext *ctx) { // If the extension is constrained, mangle the generic signature that // constrains it. appendAnyGenericType(decl); - appendModule(ExtD->getParentModule()); + appendModule(ExtD->getParentModule(), useModuleName); if (sig && ExtD->isConstrainedExtension()) { Mod = ExtD->getModuleContext(); auto nominalSig = ExtD->getSelfNominalTypeDecl() @@ -1731,7 +1757,7 @@ void ASTMangler::appendContext(const DeclContext *ctx) { } else { // This is incorrect in that it does not produce a /unique/ mangling, // but it will at least produce a /valid/ mangling. - appendContext(ctx->getParent()); + appendContext(ctx->getParent(), useModuleName); } return; } @@ -1740,26 +1766,40 @@ void ASTMangler::appendContext(const DeclContext *ctx) { case DeclContextKind::TopLevelCodeDecl: // Mangle the containing module context. - return appendContext(ctx->getParent()); + return appendContext(ctx->getParent(), useModuleName); } llvm_unreachable("bad decl context"); } -void ASTMangler::appendModule(const ModuleDecl *module) { +void ASTMangler::appendModule(const ModuleDecl *module, + StringRef useModuleName) { assert(!module->getParent() && "cannot mangle nested modules!"); // Try the special 'swift' substitution. - if (module->isStdlibModule()) + if (module->isStdlibModule()) { + assert(useModuleName.empty()); return appendOperator("s"); + } StringRef ModName = module->getName().str(); - if (ModName == MANGLING_MODULE_OBJC) + if (ModName == MANGLING_MODULE_OBJC) { + assert(useModuleName.empty()); return appendOperator("So"); - if (ModName == MANGLING_MODULE_CLANG_IMPORTER) + } + if (ModName == MANGLING_MODULE_CLANG_IMPORTER) { + assert(useModuleName.empty()); return appendOperator("SC"); + } - appendIdentifier(ModName); + // Enabling DWARFMangling indicate the mangled names are not part of the ABI, + // probably used by the debugger or IDE (USR). These mangled names will not be + // demangled successfully if we use the original module name instead of the + // actual module name. + if (!useModuleName.empty() && !DWARFMangling) + appendIdentifier(useModuleName); + else + appendIdentifier(ModName); } /// Mangle the name of a protocol as a substitution candidate. @@ -2081,7 +2121,7 @@ void ASTMangler::appendTypeListElement(Identifier name, Type elementType, bool ASTMangler::appendGenericSignature(GenericSignature sig, GenericSignature contextSig) { - auto canSig = sig->getCanonicalSignature(); + auto canSig = sig.getCanonicalSignature(); CurGenericSignature = canSig; unsigned initialParamDepth; @@ -2091,7 +2131,7 @@ bool ASTMangler::appendGenericSignature(GenericSignature sig, if (contextSig) { // If the signature is the same as the context signature, there's nothing // to do. - if (contextSig->getCanonicalSignature() == canSig) { + if (contextSig.getCanonicalSignature() == canSig) { return false; } @@ -2325,7 +2365,7 @@ void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator, assert(discriminator != AbstractClosureExpr::InvalidDiscriminator && "closure must be marked correctly with discriminator"); - appendContext(parentContext); + appendContext(parentContext, StringRef()); if (!Ty) Ty = ErrorType::get(parentContext->getASTContext()); @@ -2337,7 +2377,7 @@ void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator, void ASTMangler::appendDefaultArgumentEntity(const DeclContext *func, unsigned index) { - appendContext(func); + appendContext(func, StringRef()); appendOperator("fA", Index(index)); } @@ -2366,7 +2406,7 @@ CanType ASTMangler::getDeclTypeForMangling( parentGenericSig = GenericSignature(); auto &C = decl->getASTContext(); - if (!decl->getInterfaceType() || decl->getInterfaceType()->is()) { + if (decl->isInvalid()) { if (isa(decl)) return CanFunctionType::get({AnyFunctionType::Param(C.TheErrorType)}, C.TheErrorType); @@ -2402,8 +2442,8 @@ CanType ASTMangler::getDeclTypeForMangling( void ASTMangler::appendDeclType(const ValueDecl *decl, bool isFunctionMangling) { Mod = decl->getModuleContext(); - GenericSignature genericSig = GenericSignature(); - GenericSignature parentGenericSig = GenericSignature(); + GenericSignature genericSig; + GenericSignature parentGenericSig; auto type = getDeclTypeForMangling(decl, genericSig, parentGenericSig); if (AnyFunctionType *FuncTy = type->getAs()) { @@ -2519,7 +2559,7 @@ void ASTMangler::appendEntity(const ValueDecl *decl) { void ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { - GenericSignature contextSig = GenericSignature(); + GenericSignature contextSig; auto topLevelContext = conformance->getDeclContext()->getModuleScopeContext(); Mod = topLevelContext->getParentModule(); @@ -2540,8 +2580,11 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) { needsModule = false; } } - if (needsModule) - appendModule(Mod); + if (needsModule) { + auto *DC = conformance->getDeclContext(); + assert(DC->getAsDecl()); + appendModule(Mod, DC->getAsDecl()->getAlternateModuleName()); + } contextSig = conformingType->getAnyNominal()->getGenericSignatureOfContext(); @@ -2563,7 +2606,10 @@ void ASTMangler::appendProtocolConformanceRef( // Same as "conformance module matches type", below. appendOperator("HP"); } else if (isRetroactiveConformance(conformance)) { - appendModule(conformance->getDeclContext()->getParentModule()); + auto *DC = conformance->getDeclContext(); + assert(DC->getAsDecl()); + appendModule(conformance->getDeclContext()->getParentModule(), + DC->getAsDecl()->getAlternateModuleName()); } else if (conformance->getDeclContext()->getParentModule() == conformance->getType()->getAnyNominal()->getParentModule()) { appendOperator("HP"); @@ -2656,7 +2702,7 @@ void ASTMangler::appendConcreteProtocolConformance( auto shouldUseConformanceSig = !CurGenericSignature && conformanceSig; llvm::SaveAndRestore savedSignature( CurGenericSignature, shouldUseConformanceSig - ? conformanceSig->getCanonicalSignature() + ? conformanceSig.getCanonicalSignature() : CurGenericSignature); // Conforming type. @@ -2699,14 +2745,14 @@ void ASTMangler::appendConcreteProtocolConformance( // Append the conformance access path with the signature of the opaque type. { llvm::SaveAndRestore savedSignature( - CurGenericSignature, opaqueSignature->getCanonicalSignature()); + CurGenericSignature, opaqueSignature.getCanonicalSignature()); appendDependentProtocolConformance(conformanceAccessPath); } appendType(canType); appendOperator("HO"); } else { auto conditionalConf = module->lookupConformance(canType, proto); - appendConcreteProtocolConformance(conditionalConf->getConcrete()); + appendConcreteProtocolConformance(conditionalConf.getConcrete()); } appendListSeparator(firstRequirement); break; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index fef8398abec95..eeabdf4de8a65 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Attr.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" @@ -56,6 +57,8 @@ using namespace swift; void PrintOptions::setBaseType(Type T) { + if (T->is()) + return; TransformContext = TypeTransformContext(T); } @@ -96,7 +99,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { +PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, + bool printFullConvention) { PrintOptions result; result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; @@ -113,6 +117,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { result.OpaqueReturnTypePrinting = OpaqueReturnTypePrintingMode::StableReference; result.PreferTypeRepr = preferTypeRepr; + if (printFullConvention) + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::Full; // We should print __consuming, __owned, etc for the module interface file. result.SkipUnderscoredKeywords = false; @@ -196,7 +203,13 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr) { // the default to 'public' and mark the 'internal' things. result.PrintAccess = true; - result.ExcludeAttrList = {DAK_AccessControl, DAK_SetterAccess, DAK_Lazy}; + result.ExcludeAttrList = { + DAK_AccessControl, + DAK_SetterAccess, + DAK_Lazy, + DAK_StaticInitializeObjCMetadata, + DAK_RestatedObjCConformance + }; return result; } @@ -313,6 +326,14 @@ void ASTPrinter::callPrintDeclPre(const Decl *D, printDeclPre(D, Bracket); } +ASTPrinter &ASTPrinter::operator<<(QuotedString s) { + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + OS << s; + printTextImpl(OS.str()); + return *this; +} + ASTPrinter &ASTPrinter::operator<<(unsigned long long N) { llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -328,6 +349,14 @@ ASTPrinter &ASTPrinter::operator<<(UUID UU) { return *this; } +ASTPrinter &ASTPrinter::operator<<(Identifier name) { + return *this << DeclName(name); +} + +ASTPrinter &ASTPrinter::operator<<(DeclBaseName name) { + return *this << DeclName(name); +} + ASTPrinter &ASTPrinter::operator<<(DeclName name) { llvm::SmallString<32> str; llvm::raw_svector_ostream os(str); @@ -336,6 +365,14 @@ ASTPrinter &ASTPrinter::operator<<(DeclName name) { return *this; } +ASTPrinter &ASTPrinter::operator<<(DeclNameRef ref) { + llvm::SmallString<32> str; + llvm::raw_svector_ostream os(str); + ref.print(os); + printTextImpl(os.str()); + return *this; +} + llvm::raw_ostream &swift:: operator<<(llvm::raw_ostream &OS, tok keyword) { switch (keyword) { @@ -648,6 +685,7 @@ class PrintAST : public ASTVisitor { FreshOptions.ExcludeAttrList = options.ExcludeAttrList; FreshOptions.ExclusiveAttrList = options.ExclusiveAttrList; FreshOptions.PrintOptionalAsImplicitlyUnwrapped = options.PrintOptionalAsImplicitlyUnwrapped; + FreshOptions.TransformContext = options.TransformContext; T.print(Printer, FreshOptions); return; } @@ -678,6 +716,8 @@ class PrintAST : public ASTVisitor { } T = T.subst(subMap, SubstFlags::DesugarMemberTypes); + + options.TransformContext = TypeTransformContext(CurrentType); } printTypeWithOptions(T, options); @@ -950,6 +990,9 @@ void PrintAST::printAttributes(const Decl *D) { #define CONTEXTUAL_SIMPLE_DECL_ATTR(X, Class, Y, Z) EXCLUDE_ATTR(Class) #define CONTEXTUAL_DECL_ATTR_ALIAS(X, Class) EXCLUDE_ATTR(Class) #include "swift/AST/Attr.def" + } else if (isa(D)) { + Options.ExcludeAttrList.push_back(DAK_Mutating); + Options.ExcludeAttrList.push_back(DAK_NonMutating); } // If the declaration is implicitly @objc, print the attribute now. @@ -960,6 +1003,22 @@ void PrintAST::printAttributes(const Decl *D) { Printer << " "; } } + + // If the declaration has designated inits that won't be visible to + // clients, or if it inherits superclass convenience initializers, + // then print those attributes specially. + if (auto CD = dyn_cast(D)) { + if (Options.PrintImplicitAttrs) { + if (CD->inheritsSuperclassInitializers()) { + Printer.printAttrName("@_inheritsConvenienceInitializers"); + Printer << " "; + } + if (CD->hasMissingDesignatedInitializers()) { + Printer.printAttrName("@_hasMissingDesignatedInitializers"); + Printer << " "; + } + } + } } D->getAttrs().print(Printer, Options, D); @@ -978,12 +1037,6 @@ void PrintAST::printAttributes(const Decl *D) { } } - // Explicitly print 'mutating' and 'nonmutating' before getters and setters - // for which that is true. - if (auto accessor = dyn_cast(D)) { - printMutatingModifiersIfNeeded(accessor); - } - Options.ExcludeAttrList.resize(originalExcludeAttrCount); } @@ -1665,8 +1718,8 @@ bool ShouldPrintChecker::shouldPrint(const Decl *D, // attributes can only be retrieved from the inside VarDecls. if (auto *PD = dyn_cast(D)) { auto ShouldPrint = false; - for (auto entry : PD->getPatternList()) { - ShouldPrint |= shouldPrint(entry.getPattern(), Options); + for (auto idx : range(PD->getNumPatternEntries())) { + ShouldPrint |= shouldPrint(PD->getPattern(idx), Options); if (ShouldPrint) return true; } @@ -1706,9 +1759,11 @@ void PrintAST::printBodyIfNecessary(const AbstractFunctionDecl *decl) { } void PrintAST::printMutatingModifiersIfNeeded(const AccessorDecl *accessor) { - if (accessor->isAssumedNonMutating() && accessor->isMutating()) { + if (accessor->isAssumedNonMutating() && accessor->isMutating() && + !Options.excludeAttrKind(DAK_Mutating)) { Printer.printKeyword("mutating", Options, " "); - } else if (accessor->isExplicitNonMutating()) { + } else if (accessor->isExplicitNonMutating() && + !Options.excludeAttrKind(DAK_NonMutating)) { Printer.printKeyword("nonmutating", Options, " "); } } @@ -1721,6 +1776,10 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) { auto impl = ASD->getImplInfo(); + // AbstractAccessors is suppressed by FunctionDefinitions. + bool PrintAbstract = + Options.AbstractAccessors && !Options.FunctionDefinitions; + // Don't print accessors for trivially stored properties... if (impl.isSimpleStored()) { // ...unless we're printing for SIL, which expects a { get set? } on @@ -1731,23 +1790,24 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) { // ...or you're private/internal(set), at which point we'll print // @_hasStorage var x: T { get } else if (ASD->isSettable(nullptr) && hasLessAccessibleSetter(ASD)) { - Printer << " {"; - { - IndentRAII indentMore(*this); + if (PrintAbstract) { + Printer << " { get }"; + } else { + Printer << " {"; + { + IndentRAII indentMore(*this); + indent(); + Printer.printNewline(); + Printer << "get"; + } indent(); Printer.printNewline(); - Printer << "get"; - Printer.printNewline(); + Printer << "}"; } - Printer << "}"; } return; } - // AbstractAccessors is suppressed by FunctionDefinitions. - bool PrintAbstract = - Options.AbstractAccessors && !Options.FunctionDefinitions; - // We sometimes want to print the accessors abstractly // instead of listing out how they're actually implemented. bool inProtocol = isa(ASD->getDeclContext()); @@ -2066,10 +2126,10 @@ void PrintAST::visitImportDecl(ImportDecl *decl) { interleave(decl->getFullAccessPath(), [&](const ImportDecl::AccessPathElement &Elem) { if (!Mods.empty()) { - Printer.printModuleRef(Mods.front(), Elem.first); + Printer.printModuleRef(Mods.front(), Elem.Item); Mods = Mods.slice(1); } else { - Printer << Elem.first.str(); + Printer << Elem.Item.str(); } }, [&] { Printer << "."; }); @@ -2162,8 +2222,8 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) { // variables are immutable, and if so, we print as 'let'. This allows us to // handle the 'let x = 4' case properly at least. const VarDecl *anyVar = nullptr; - for (auto entry : decl->getPatternList()) { - entry.getPattern()->forEachVariable([&](VarDecl *V) { + for (auto idx : range(decl->getNumPatternEntries())) { + decl->getPattern(idx)->forEachVariable([&](VarDecl *V) { anyVar = V; }); if (anyVar) break; @@ -2190,29 +2250,29 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) { } bool isFirst = true; - for (auto &entry : decl->getPatternList()) { - if (!shouldPrintPattern(entry.getPattern())) + for (auto idx : range(decl->getNumPatternEntries())) { + auto *pattern = decl->getPattern(idx); + if (!shouldPrintPattern(pattern)) continue; if (isFirst) isFirst = false; else Printer << ", "; - printPattern(entry.getPattern()); + printPattern(pattern); // We also try to print type for named patterns, e.g. var Field = 10; // and tuple patterns, e.g. var (T1, T2) = (10, 10) - if (isa(entry.getPattern()) || - isa(entry.getPattern())) { - printPatternType(entry.getPattern()); + if (isa(pattern) || isa(pattern)) { + printPatternType(pattern); } if (Options.VarInitializers) { - auto vd = entry.getAnchoringVarDecl(); - if (entry.hasInitStringRepresentation() && + auto *vd = decl->getAnchoringVarDecl(idx); + if (decl->hasInitStringRepresentation(idx) && vd->isInitExposedToClients()) { SmallString<128> scratch; - Printer << " = " << entry.getInitStringRepresentation(scratch); + Printer << " = " << decl->getInitStringRepresentation(idx, scratch); } } @@ -2471,6 +2531,8 @@ static void printParameterFlags(ASTPrinter &printer, PrintOptions options, ParameterTypeFlags flags, bool escaping) { if (!options.excludeAttrKind(TAK_autoclosure) && flags.isAutoClosure()) printer << "@autoclosure "; + if (!options.excludeAttrKind(TAK_noDerivative) && flags.isNoDerivative()) + printer << "@noDerivative "; switch (flags.getValueOwnership()) { case ValueOwnership::Default: @@ -2521,26 +2583,26 @@ void PrintAST::visitVarDecl(VarDecl *decl) { [&]{ Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl)); }); - if (decl->hasInterfaceType()) { - Printer << ": "; - TypeLoc tyLoc; - if (auto *repr = decl->getTypeRepr()) - tyLoc = TypeLoc(repr, decl->getInterfaceType()); - else - tyLoc = TypeLoc::withoutLoc(decl->getInterfaceType()); - Printer.printDeclResultTypePre(decl, tyLoc); + auto type = decl->getInterfaceType(); + Printer << ": "; + TypeLoc tyLoc; + if (auto *repr = decl->getTypeReprOrParentPatternTypeRepr()) + tyLoc = TypeLoc(repr, type); + else + tyLoc = TypeLoc::withoutLoc(type); - // HACK: When printing result types for vars with opaque result types, - // always print them using the `some` keyword instead of printing - // the full stable reference. - llvm::SaveAndRestore - x(Options.OpaqueReturnTypePrinting, - PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword); + Printer.printDeclResultTypePre(decl, tyLoc); - printTypeLocForImplicitlyUnwrappedOptional( - tyLoc, decl->isImplicitlyUnwrappedOptional()); - } + // HACK: When printing result types for vars with opaque result types, + // always print them using the `some` keyword instead of printing + // the full stable reference. + llvm::SaveAndRestore + x(Options.OpaqueReturnTypePrinting, + PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword); + + printTypeLocForImplicitlyUnwrappedOptional( + tyLoc, decl->isImplicitlyUnwrappedOptional()); printAccessors(decl); } @@ -2563,13 +2625,16 @@ void PrintAST::printOneParameter(const ParamDecl *param, auto BodyName = param->getName(); switch (Options.ArgAndParamPrinting) { case PrintOptions::ArgAndParamPrintingMode::EnumElement: - if (ArgName.empty() && BodyName.empty() && !param->getDefaultValue()) { + if (ArgName.empty() && BodyName.empty() && !param->hasDefaultExpr()) { // Don't print anything, in the style of a tuple element. return; } // Else, print the argument only. LLVM_FALLTHROUGH; case PrintOptions::ArgAndParamPrintingMode::ArgumentOnly: + if (ArgName.empty() && !Options.PrintEmptyArgumentNames) { + return; + } Printer.printName(ArgName, PrintNameContext::FunctionParameterExternal); if (!ArgNameIsAPIByDefault && !ArgName.empty()) @@ -2624,7 +2689,7 @@ void PrintAST::printOneParameter(const ParamDecl *param, if (param->isVariadic()) Printer << "..."; - if (param->isDefaultArgument()) { + if (param->isDefaultArgument() && Options.PrintDefaultArgumentValue) { SmallString<128> scratch; auto defaultArgStr = param->getDefaultValueStringRepresentation(scratch); @@ -2666,21 +2731,16 @@ void PrintAST::printParameterList(ParameterList *PL, void PrintAST::printFunctionParameters(AbstractFunctionDecl *AFD) { auto BodyParams = AFD->getParameters(); - auto curTy = AFD->hasInterfaceType() ? AFD->getInterfaceType() : nullptr; + auto curTy = AFD->getInterfaceType(); // Skip over the implicit 'self'. - if (AFD->hasImplicitSelfDecl()) { - if (curTy) - if (auto funTy = curTy->getAs()) - curTy = funTy->getResult(); - } + if (AFD->hasImplicitSelfDecl()) + if (auto funTy = curTy->getAs()) + curTy = funTy->getResult(); ArrayRef parameterListTypes; - if (curTy) { - if (auto funTy = curTy->getAs()) { - parameterListTypes = funTy->getParams(); - } - } + if (auto funTy = curTy->getAs()) + parameterListTypes = funTy->getParams(); printParameterList(BodyParams, parameterListTypes, AFD->argumentNameIsAPIByDefault()); @@ -2717,6 +2777,10 @@ bool PrintAST::printASTNodes(const ArrayRef &Elements, void PrintAST::visitAccessorDecl(AccessorDecl *decl) { printDocumentationComment(decl); printAttributes(decl); + + // Explicitly print 'mutating' and 'nonmutating' before getters and setters + // for which that is true. + printMutatingModifiersIfNeeded(decl); switch (auto kind = decl->getAccessorKind()) { case AccessorKind::Get: case AccessorKind::Address: @@ -2773,7 +2837,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) { if (!Options.SkipIntroducerKeywords) { if (decl->isStatic() && Options.PrintStaticKeyword) printStaticKeyword(decl->getCorrectStaticSpelling()); - if (decl->isMutating() && !decl->getAttrs().hasAttribute()) { + if (decl->isMutating() && !Options.excludeAttrKind(DAK_Mutating)) { Printer.printKeyword("mutating", Options, " "); } else if (decl->isConsuming() && !decl->getAttrs().hasAttribute()) { Printer.printKeyword("__consuming", Options, " "); @@ -2825,7 +2889,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) { // TypeRepr is not getting 'typechecked'. See // \c resolveTopLevelIdentTypeComponent function in TypeCheckType.cpp. if (auto *simId = dyn_cast_or_null(ResultTyLoc.getTypeRepr())) { - if (simId->getIdentifier() == Ctx.Id_Self) + if (simId->getNameRef().isSimpleName(Ctx.Id_Self)) ResultTyLoc = TypeLoc::withoutLoc(ResultTy); } Printer << " -> "; @@ -2869,13 +2933,14 @@ void PrintAST::printEnumElement(EnumElementDecl *elt) { auto params = ArrayRef(); - if (elt->hasInterfaceType() && !elt->getInterfaceType()->hasError()) { + if (!elt->isInvalid()) { // Walk to the params of the associated values. // (EnumMetaType) -> (AssocValues) -> Enum - params = elt->getInterfaceType()->castTo() - ->getResult() - ->castTo() - ->getParams(); + auto type = elt->getInterfaceType(); + params = type->castTo() + ->getResult() + ->castTo() + ->getParams(); } // @escaping is not valid in enum element position, even though the @@ -2966,9 +3031,10 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { }, [&] { // Parameters printGenericDeclGenericParams(decl); auto params = ArrayRef(); - if (decl->hasInterfaceType() && !decl->getInterfaceType()->hasError()) { + if (!decl->isInvalid()) { // Walk to the params of the subscript's indices. - params = decl->getInterfaceType()->castTo()->getParams(); + auto type = decl->getInterfaceType(); + params = type->castTo()->getParams(); } printParameterList(decl->getIndices(), params, /*isAPINameByDefault*/false); @@ -2976,10 +3042,10 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) { Printer << " -> "; TypeLoc elementTy = decl->getElementTypeLoc(); - Printer.printDeclResultTypePre(decl, elementTy); - Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); if (!elementTy.getTypeRepr()) elementTy = TypeLoc::withoutLoc(decl->getElementInterfaceType()); + Printer.printDeclResultTypePre(decl, elementTy); + Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); // HACK: When printing result types for subscripts with opaque result types, // always print them using the `some` keyword instead of printing @@ -3401,7 +3467,7 @@ bool Decl::shouldPrintInContext(const PrintOptions &PO) const { if (auto pbd = dyn_cast(this)) { if (pbd->getPatternList().size() == 1) { auto pattern = - pbd->getPatternList()[0].getPattern()->getSemanticsProvidingPattern(); + pbd->getPattern(0)->getSemanticsProvidingPattern(); if (auto named = dyn_cast(pattern)) { if (!named->getDecl()->hasStorage()) return false; @@ -3428,6 +3494,15 @@ void Pattern::print(llvm::raw_ostream &OS, const PrintOptions &Options) const { // Type Printing //===----------------------------------------------------------------------===// +template +void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { + auto *cml = Ctx.getClangModuleLoader(); + SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + info.getUncommonInfo().getValue().printClangFunctionType(cml, os); + Printer << ", cType: " << QuotedString(os.str()); +} + namespace { class TypePrinter : public TypeVisitor { using super = TypeVisitor; @@ -3513,9 +3588,6 @@ class TypePrinter : public TypeVisitor { if (Options.FullyQualifiedTypes) return true; - if (!Options.FullyQualifiedTypesIfAmbiguous) - return false; - Decl *D; if (auto *TAT = dyn_cast(T)) D = TAT->getDecl(); @@ -3528,6 +3600,11 @@ class TypePrinter : public TypeVisitor { return true; ModuleDecl *M = D->getDeclContext()->getParentModule(); + if (M->isBuiltinModule()) + return true; + + if (!Options.FullyQualifiedTypesIfAmbiguous) + return false; if (Options.CurrentModule && M == Options.CurrentModule) { return false; @@ -3562,7 +3639,6 @@ class TypePrinter : public TypeVisitor { // know we're printing a type member so it escapes `Type` and `Protocol`. if (auto parent = Ty->getParent()) { visitParentType(parent); - Printer << "."; NameContext = PrintNameContext::TypeMember; } else if (shouldPrintFullyQualified(Ty)) { printModuleContext(Ty); @@ -3587,7 +3663,7 @@ class TypePrinter : public TypeVisitor { } void visitUnresolvedType(UnresolvedType *T) { - if (T->getASTContext().LangOpts.DebugConstraintSolver) + if (Options.PrintTypesForDebugging) Printer << "<>"; else Printer << "_"; @@ -3702,13 +3778,32 @@ class TypePrinter : public TypeVisitor { } void visitParentType(Type T) { + /// Don't print the parent type if it's being printed in that type context. + if (Options.TransformContext) { + if (auto currentType = Options.TransformContext->getBaseType()) { + auto printingType = T; + if (currentType->hasArchetype()) + currentType = currentType->mapTypeOutOfContext(); + + if (auto errorTy = printingType->getAs()) + if (auto origTy = errorTy->getOriginalType()) + printingType = origTy; + + if (printingType->hasArchetype()) + printingType = printingType->mapTypeOutOfContext(); + + if (currentType->isEqual(printingType)) + return; + } + } PrintOptions innerOptions = Options; innerOptions.SynthesizeSugarOnTypes = false; if (auto sugarType = dyn_cast(T.getPointer())) T = sugarType->getImplementationType(); - TypePrinter(Printer, innerOptions).visit(T); + TypePrinter(Printer, innerOptions).printWithParensIfNotSimple(T); + Printer << "."; } void visitEnumType(EnumType *T) { @@ -3770,14 +3865,31 @@ class TypePrinter : public TypeVisitor { visit(staticSelfT); } - void printFunctionExtInfo(AnyFunctionType::ExtInfo info) { + void printFunctionExtInfo(ASTContext &Ctx, AnyFunctionType::ExtInfo info) { if (Options.SkipAttributes) return; + if (!Options.excludeAttrKind(TAK_differentiable) && + info.isDifferentiable()) { + if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { + Printer << "@differentiable(linear) "; + } else { + Printer << "@differentiable "; + } + } + + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + return; + case PrintOptions::FunctionRepresentationMode::Full: + case PrintOptions::FunctionRepresentationMode::NameOnly: + if (Options.excludeAttrKind(TAK_convention) || + info.getSILRepresentation() == SILFunctionType::Representation::Thick) + return; - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getSILRepresentation() != SILFunctionType::Representation::Thick) { + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3793,6 +3905,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3813,15 +3930,34 @@ class TypePrinter : public TypeVisitor { } } - void printFunctionExtInfo( - SILFunctionType::ExtInfo info, - Optional witnessMethodConformance) { + void printFunctionExtInfo(ASTContext &Ctx, + SILFunctionType::ExtInfo info, + ProtocolConformanceRef witnessMethodConformance) { if (Options.SkipAttributes) return; - if (Options.PrintFunctionRepresentationAttrs && - !Options.excludeAttrKind(TAK_convention) && - info.getRepresentation() != SILFunctionType::Representation::Thick) { + if (!Options.excludeAttrKind(TAK_differentiable) && + info.isDifferentiable()) { + if (info.getDifferentiabilityKind() == DifferentiabilityKind::Linear) { + Printer << "@differentiable(linear) "; + } else { + Printer << "@differentiable "; + } + } + + + SmallString<64> buf; + switch (Options.PrintFunctionRepresentationAttrs) { + case PrintOptions::FunctionRepresentationMode::None: + break; + case PrintOptions::FunctionRepresentationMode::NameOnly: + case PrintOptions::FunctionRepresentationMode::Full: + if (Options.excludeAttrKind(TAK_convention) || + info.getRepresentation() == SILFunctionType::Representation::Thick) + break; + + bool printNameOnly = Options.PrintFunctionRepresentationAttrs == + PrintOptions::FunctionRepresentationMode::NameOnly; Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); Printer << "("; @@ -3836,6 +3972,11 @@ class TypePrinter : public TypeVisitor { break; case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; + // FIXME: [clang-function-type-serialization] Once we start serializing + // Clang function types, we should be able to remove the second check. + if (printNameOnly || !info.getUncommonInfo().hasValue()) + break; + printCType(Ctx, Printer, info); break; case SILFunctionType::Representation::Method: Printer << "method"; @@ -3846,7 +3987,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::WitnessMethod: Printer << "witness_method: "; printTypeDeclName( - witnessMethodConformance->getRequirement()->getDeclaredType()); + witnessMethodConformance.getRequirement()->getDeclaredType()); break; case SILFunctionType::Representation::Closure: Printer << "closure"; @@ -3905,7 +4046,7 @@ class TypePrinter : public TypeVisitor { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); // If we're stripping argument labels from types, do it when printing. visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/false); @@ -3925,13 +4066,24 @@ class TypePrinter : public TypeVisitor { PrintAST(Printer, Options).printGenericSignature(genericSig, flags); } + void printSubstitutions(SubstitutionMap subs) { + Printer << " <"; + interleave(subs.getReplacementTypes(), + [&](Type type) { + visit(type); + }, [&]{ + Printer << ", "; + }); + Printer << ">"; + } + void visitGenericFunctionType(GenericFunctionType *T) { Printer.callPrintStructurePre(PrintStructureKind::FunctionType); SWIFT_DEFER { Printer.printStructurePost(PrintStructureKind::FunctionType); }; - printFunctionExtInfo(T->getExtInfo()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo()); printGenericSignature(T->getGenericSignature(), PrintAST::PrintParams | PrintAST::PrintRequirements); @@ -3984,51 +4136,84 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printFunctionExtInfo(T->getExtInfo(), - T->getWitnessMethodConformanceOrNone()); + printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), + T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); - if (auto sig = T->getGenericSignature()) { - printGenericSignature(sig, - PrintAST::PrintParams | - PrintAST::PrintRequirements); - Printer << " "; + + // If this is a substituted function type, then its generic signature is + // independent of the enclosing context, and defines the parameters active + // in the interface params and results. Unsubstituted types use the existing + // environment, which may be a sil decl's generic environment. + // + // Yeah, this is fiddly. In the end, we probably want all decls to have + // substituted types in terms of a generic signature declared on the decl, + // which would make this logic more uniform. + TypePrinter *sub = this; + Optional subBuffer; + PrintOptions subOptions = Options; + if (T->getSubstitutions()) { + subOptions.GenericEnv = nullptr; + subBuffer.emplace(Printer, subOptions); + sub = &*subBuffer; } + + // Capture list used here to ensure we don't print anything using `this` + // printer, but only the sub-Printer. + [T, sub, &subOptions]{ + if (auto sig = T->getSubstGenericSignature()) { + sub->printGenericSignature(sig, + PrintAST::PrintParams | + PrintAST::PrintRequirements); + sub->Printer << " "; + if (T->isGenericSignatureImplied()) { + sub->Printer << "in "; + } + } - Printer << "("; - bool first = true; - for (auto param : T->getParameters()) { - Printer.printSeparator(first, ", "); - param.print(Printer, Options); - } - Printer << ") -> "; + sub->Printer << "("; + bool first = true; + for (auto param : T->getParameters()) { + sub->Printer.printSeparator(first, ", "); + param.print(sub->Printer, subOptions); + } + sub->Printer << ") -> "; - unsigned totalResults = - T->getNumYields() + T->getNumResults() + unsigned(T->hasErrorResult()); + unsigned totalResults = + T->getNumYields() + T->getNumResults() + unsigned(T->hasErrorResult()); - if (totalResults != 1) Printer << "("; + if (totalResults != 1) + sub->Printer << "("; - first = true; + first = true; - for (auto yield : T->getYields()) { - Printer.printSeparator(first, ", "); - Printer << "@yields "; - yield.print(Printer, Options); - } + for (auto yield : T->getYields()) { + sub->Printer.printSeparator(first, ", "); + sub->Printer << "@yields "; + yield.print(sub->Printer, subOptions); + } - for (auto result : T->getResults()) { - Printer.printSeparator(first, ", "); - result.print(Printer, Options); - } + for (auto result : T->getResults()) { + sub->Printer.printSeparator(first, ", "); + result.print(sub->Printer, subOptions); + } - if (T->hasErrorResult()) { - // The error result is implicitly @owned; don't print that. - assert(T->getErrorResult().getConvention() == ResultConvention::Owned); - Printer.printSeparator(first, ", "); - Printer << "@error "; - T->getErrorResult().getType().print(Printer, Options); - } + if (T->hasErrorResult()) { + // The error result is implicitly @owned; don't print that. + assert(T->getErrorResult().getConvention() == ResultConvention::Owned); + sub->Printer.printSeparator(first, ", "); + sub->Printer << "@error "; + T->getErrorResult().getInterfaceType().print(sub->Printer, subOptions); + } - if (totalResults != 1) Printer << ")"; + if (totalResults != 1) + sub->Printer << ")"; + }(); + + // The substitution types are always in terms of the outer environment. + if (auto substitutions = T->getSubstitutions()) { + Printer << " for"; + printSubstitutions(substitutions); + } } void visitSILBlockStorageType(SILBlockStorageType *T) { @@ -4049,7 +4234,7 @@ class TypePrinter : public TypeVisitor { [&sub, T]{ if (auto sig = T->getLayout()->getGenericSignature()) { sub.printGenericSignature(sig, - PrintAST::PrintParams | PrintAST::PrintRequirements); + PrintAST::PrintParams | PrintAST::PrintRequirements); sub.Printer << " "; } sub.Printer << "{"; @@ -4068,14 +4253,7 @@ class TypePrinter : public TypeVisitor { // The arguments to the layout, if any, do come from the outer environment. if (auto subMap = T->getSubstitutions()) { - Printer << " <"; - interleave(subMap.getReplacementTypes(), - [&](Type type) { - visit(type); - }, [&]{ - Printer << ", "; - }); - Printer << ">"; + printSubstitutions(subMap); } } @@ -4165,8 +4343,7 @@ class TypePrinter : public TypeVisitor { } void visitNestedArchetypeType(NestedArchetypeType *T) { - printWithParensIfNotSimple(T->getParent()); - Printer << "."; + visitParentType(T->getParent()); printArchetypeCommon(T); } @@ -4257,7 +4434,6 @@ class TypePrinter : public TypeVisitor { void visitDependentMemberType(DependentMemberType *T) { visitParentType(T->getBase()); - Printer << "."; Printer.printName(T->getName()); } @@ -4270,7 +4446,7 @@ class TypePrinter : public TypeVisitor { #include "swift/AST/ReferenceStorage.def" void visitTypeVariableType(TypeVariableType *T) { - if (T->getASTContext().LangOpts.DebugConstraintSolver) { + if (Options.PrintTypesForDebugging) { Printer << "$T" << T->getID(); return; } @@ -4473,8 +4649,15 @@ void SILParameterInfo::print(raw_ostream &OS, const PrintOptions &Opts) const { } void SILParameterInfo::print(ASTPrinter &Printer, const PrintOptions &Opts) const { + switch (getDifferentiability()) { + case SILParameterDifferentiability::NotDifferentiable: + Printer << "@noDerivative "; + break; + default: + break; + } Printer << getStringForParameterConvention(getConvention()); - getType().print(Printer, Opts); + getInterfaceType().print(Printer, Opts); } static StringRef getStringForResultConvention(ResultConvention conv) { @@ -4498,7 +4681,7 @@ void SILResultInfo::print(raw_ostream &OS, const PrintOptions &Opts) const { } void SILResultInfo::print(ASTPrinter &Printer, const PrintOptions &Opts) const { Printer << getStringForResultConvention(getConvention()); - getType().print(Printer, Opts); + getInterfaceType().print(Printer, Opts); } std::string Type::getString(const PrintOptions &PO) const { diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 88133878bdd3b..35234adda11d1 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -39,7 +39,7 @@ using namespace ast_scope; #pragma mark ASTScope llvm::SmallVector ASTScope::unqualifiedLookup( - SourceFile *SF, DeclName name, SourceLoc loc, + SourceFile *SF, DeclNameRef name, SourceLoc loc, const DeclContext *startingContext, namelookup::AbstractASTScopeDeclConsumer &consumer) { if (auto *s = SF->getASTContext().Stats) @@ -190,10 +190,7 @@ NullablePtr CaptureListScope::getDeclContext() const { } NullablePtr AttachedPropertyWrapperScope::getDeclContext() const { - return decl->getParentPatternBinding() - ->getPatternList() - .front() - .getInitContext(); + return decl->getParentPatternBinding()->getInitContext(0); } NullablePtr AbstractFunctionDeclScope::getDeclContext() const { @@ -231,6 +228,7 @@ DEFINE_GET_CLASS_NAME(ClosureParametersScope) DEFINE_GET_CLASS_NAME(ClosureBodyScope) DEFINE_GET_CLASS_NAME(TopLevelCodeScope) DEFINE_GET_CLASS_NAME(SpecializeAttributeScope) +DEFINE_GET_CLASS_NAME(DifferentiableAttributeScope) DEFINE_GET_CLASS_NAME(SubscriptDeclScope) DEFINE_GET_CLASS_NAME(VarDeclScope) DEFINE_GET_CLASS_NAME(EnumElementScope) diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 2dfc723d5264b..f81ebe47bc7ab 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -29,6 +29,7 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" #include "swift/AST/TypeRepr.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/STLExtras.h" #include "llvm/Support/Compiler.h" #include @@ -51,7 +52,9 @@ static bool rangeableIsIgnored(const Stmt *d) { return false; // ?? } static bool rangeableIsIgnored(const ASTNode n) { - return n.is() && n.get()->isImplicit(); + return (n.is() && rangeableIsIgnored(n.get())) || + (n.is() && rangeableIsIgnored(n.get())) || + (n.is() && rangeableIsIgnored(n.get())); } template @@ -61,6 +64,9 @@ static SourceRange getRangeableSourceRange(const Rangeable *const p) { static SourceRange getRangeableSourceRange(const SpecializeAttr *a) { return a->getRange(); } +static SourceRange getRangeableSourceRange(const DifferentiableAttr *a) { + return a->getRange(); +} static SourceRange getRangeableSourceRange(const ASTNode n) { return n.getSourceRange(); } @@ -95,6 +101,17 @@ static void dumpRangeable(SpecializeAttr *r, llvm::raw_ostream &f) { llvm::errs() << "SpecializeAttr\n"; } +static void dumpRangeable(const DifferentiableAttr *a, + llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; +static void dumpRangeable(const DifferentiableAttr *a, llvm::raw_ostream &f) { + llvm::errs() << "DifferentiableAttr\n"; +} +static void dumpRangeable(DifferentiableAttr *a, + llvm::raw_ostream &f) LLVM_ATTRIBUTE_USED; +static void dumpRangeable(DifferentiableAttr *a, llvm::raw_ostream &f) { + llvm::errs() << "DifferentiableAttr\n"; +} + /// For Debugging template bool doesRangeableRangeMatch(const T *x, const SourceManager &SM, @@ -436,6 +453,22 @@ class ScopeCreator final { fn(specializeAttr); } + void forEachDifferentiableAttrInSourceOrder( + Decl *decl, function_ref fn) { + std::vector sortedDifferentiableAttrs; + for (auto *attr : decl->getAttrs()) + if (auto *diffAttr = dyn_cast(attr)) + // NOTE(TF-835): Skipping implicit `@differentiable` attributes is + // necessary to avoid verification failure in + // `ASTScopeImpl::verifyThatChildrenAreContainedWithin`. + // Perhaps this check may no longer be necessary after TF-835: robust + // `@derivative` attribute lowering. + if (!diffAttr->isImplicit()) + sortedDifferentiableAttrs.push_back(diffAttr); + for (auto *diffAttr : sortBySourceRange(sortedDifferentiableAttrs)) + fn(diffAttr); + } + std::vector expandIfConfigClausesThenCullAndSortElementsOrMembers( ArrayRef input) const { auto cleanedupNodes = sortBySourceRange(cull(expandIfConfigClauses(input))); @@ -599,25 +632,12 @@ class ScopeCreator final { template bool isNotAfter(Rangeable n1, Rangeable n2) const { - auto cmpLoc = [&](const SourceLoc l1, const SourceLoc l2) { - return l1 == l2 ? 0 : ctx.SourceMgr.isBeforeInBuffer(l1, l2) ? -1 : 1; - }; const auto r1 = getRangeableSourceRange(n1); const auto r2 = getRangeableSourceRange(n2); - const int startOrder = cmpLoc(r1.Start, r2.Start); - const int endOrder = cmpLoc(r1.End, r2.End); - -#ifndef NDEBUG - if (startOrder * endOrder == -1) { - llvm::errs() << "*** Start order contradicts end order between: ***\n"; - dumpRangeable(n1, llvm::errs()); - llvm::errs() << "\n*** and: ***\n"; - dumpRangeable(n2, llvm::errs()); - } -#endif - ASTScopeAssert(startOrder * endOrder != -1, - "Start order contradicts end order"); - return startOrder + endOrder < 1; + + const int signum = ASTScopeImpl::compare(r1, r2, ctx.SourceMgr, + /*ensureDisjoint=*/true); + return -1 == signum; } static bool isVarDeclInPatternBindingDecl(ASTNode n1, ASTNode n2) { @@ -640,8 +660,7 @@ class ScopeCreator final { // Can occur in illegal code if (auto *const s = n.dyn_cast()) { if (auto *const bs = dyn_cast(s)) - ASTScopeAssert(bs->getNumElements() == 0, - "Might mess up insertion point"); + ASTScopeAssert(bs->empty(), "Might mess up insertion point"); } return !n.isDecl(DeclKind::Var); } @@ -670,7 +689,7 @@ class ScopeCreator final { auto printDecl = [&](const Decl *d) { llvm::errs() << "\ngetAsDecl() -> " << d << " "; - d->getSourceRange().dump(ctx.SourceMgr); + d->getSourceRange().print(llvm::errs(), ctx.SourceMgr); llvm::errs() << " : "; d->dump(llvm::errs()); llvm::errs() << "\n"; @@ -710,7 +729,7 @@ class ScopeCreator final { findLocalizableDeclContextsInAST() const; public: - void dump() const { print(llvm::errs()); } + SWIFT_DEBUG_DUMP { print(llvm::errs()); } void print(raw_ostream &out) const { out << "(swift::ASTSourceFileScope*) " << sourceFileScope << "\n"; @@ -742,6 +761,20 @@ void ASTScope:: impl->buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); } +bool ASTScope::areInactiveIfConfigClausesSupported() { + return ScopeCreator::includeInactiveIfConfigClauses; +} + +void ASTScope::expandFunctionBody(AbstractFunctionDecl *AFD) { + auto *const SF = AFD->getParentSourceFile(); + if (SF->isSuitableForASTScopes()) + SF->getScope().expandFunctionBodyImpl(AFD); +} + +void ASTScope::expandFunctionBodyImpl(AbstractFunctionDecl *AFD) { + impl->expandFunctionBody(AFD); +} + ASTSourceFileScope *ASTScope::createScopeTree(SourceFile *SF) { ScopeCreator *scopeCreator = new (SF->getASTContext()) ScopeCreator(SF); return scopeCreator->sourceFileScope; @@ -759,6 +792,15 @@ void ASTSourceFileScope:: expandAndBeCurrentDetectingRecursion(*scopeCreator); } +void ASTSourceFileScope::expandFunctionBody(AbstractFunctionDecl *AFD) { + if (!AFD) + return; + auto sr = AFD->getBodySourceRange(); + if (sr.isInvalid()) + return; + ASTScopeImpl *bodyScope = findInnermostEnclosingScope(sr.Start, nullptr); + bodyScope->expandAndBeCurrentDetectingRecursion(*scopeCreator); +} ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF, ScopeCreator *scopeCreator) @@ -920,16 +962,15 @@ class NodeAdder isInTypeDecl ? DeclVisibilityKind::MemberOfCurrentNominal : DeclVisibilityKind::LocalVariable; auto *insertionPoint = parentScope; - for (unsigned i = 0; i < patternBinding->getPatternList().size(); ++i) { + for (auto i : range(patternBinding->getNumPatternEntries())) { // TODO: Won't need to do so much work to avoid creating one without // a SourceRange once parser is fixed to not create two // PatternBindingDecls with same locaiton and getSourceRangeOfThisASTNode // for PatternEntryDeclScope is simplified to use the PatternEntry's // source range. - auto &patternEntry = patternBinding->getPatternList()[i]; - if (!patternEntry.getOriginalInit()) { + if (!patternBinding->getOriginalInit(i)) { bool found = false; - patternEntry.getPattern()->forEachVariable([&](VarDecl *vd) { + patternBinding->getPattern(i)->forEachVariable([&](VarDecl *vd) { if (!vd->isImplicit()) found = true; else @@ -1028,6 +1069,13 @@ void ScopeCreator::addChildrenForAllLocalizableAccessorsInSourceOrder( return enclosingAbstractStorageDecl == ad->getStorage(); }); + // Create scopes for `@differentiable` attributes. + forEachDifferentiableAttrInSourceOrder( + asd, [&](DifferentiableAttr *diffAttr) { + ifUniqueConstructExpandAndInsert( + parent, diffAttr, asd); + }); + // Sort in order to include synthesized ones, which are out of order. for (auto *accessor : sortBySourceRange(accessorsToScope)) addToScopeTree(accessor, parent); @@ -1067,6 +1115,8 @@ void ASTScopeImpl::disownDescendants(ScopeCreator &scopeCreator) { ASTScopeImpl * ASTScopeImpl::expandAndBeCurrentDetectingRecursion(ScopeCreator &scopeCreator) { + assert(scopeCreator.getASTContext().LangOpts.EnableASTScopeLookup && + "Should not be getting here if ASTScopes are disabled"); return evaluateOrDefault(scopeCreator.getASTContext().evaluator, ExpandASTScopeRequest{this, &scopeCreator}, nullptr); } @@ -1170,6 +1220,7 @@ NO_NEW_INSERTION_POINT(WholeClosureScope) NO_EXPANSION(GenericParamScope) NO_EXPANSION(ClosureParametersScope) NO_EXPANSION(SpecializeAttributeScope) +NO_EXPANSION(DifferentiableAttributeScope) NO_EXPANSION(ConditionalClausePatternUseScope) NO_EXPANSION(LookupParentDiversionScope) @@ -1180,7 +1231,7 @@ AnnotatedInsertionPoint ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint( ScopeCreator &scopeCreator) { ASTScopeAssert(SF, "Must already have a SourceFile."); - ArrayRef decls = SF->Decls; + ArrayRef decls = SF->getTopLevelDecls(); // Assume that decls are only added at the end, in source order ArrayRef newDecls = decls.slice(numberOfDeclsAlreadySeen); std::vector newNodes(newDecls.begin(), newDecls.end()); @@ -1199,7 +1250,7 @@ ParameterListScope::expandAScopeThatCreatesANewInsertionPoint( // Unlike generic parameters or pattern initializers, it cannot refer to a // previous parameter. for (ParamDecl *pd : params->getArray()) { - if (pd->getDefaultValue()) + if (pd->hasDefaultExpr()) scopeCreator .constructExpandAndInsertUncheckable( this, pd); @@ -1340,6 +1391,13 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( scopeCreator.ifUniqueConstructExpandAndInsert( this, specializeAttr, decl); }); + // Create scopes for `@differentiable` attributes. + scopeCreator.forEachDifferentiableAttrInSourceOrder( + decl, [&](DifferentiableAttr *diffAttr) { + scopeCreator + .ifUniqueConstructExpandAndInsert( + this, diffAttr, decl); + }); // Create scopes for generic and ordinary parameters. // For a subscript declaration, the generic and ordinary parameters are in an // ancestor scope, so don't make them here. @@ -1360,8 +1418,8 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint( } // Create scope for the body. // We create body scopes when there is no body for source kit to complete - // erroneous code in bodies. But don't let compiler synthesize one. - if (decl->getBodySourceRange().isValid() && decl->getBody(false)) { + // erroneous code in bodies. + if (decl->getBodySourceRange().isValid()) { if (AbstractFunctionBodyScope::isAMethod(decl)) scopeCreator.constructExpandAndInsertUncheckable(leaf, decl); @@ -1522,7 +1580,7 @@ void ClosureBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint( void DefaultArgumentInitializerScope:: expandAScopeThatDoesNotCreateANewInsertionPoint( ScopeCreator &scopeCreator) { - auto *initExpr = decl->getDefaultValue(); + auto *initExpr = decl->getStructuralDefaultExpr(); ASTScopeAssert(initExpr, "Default argument initializer must have an initializer."); scopeCreator.addToScopeTree(initExpr, this); @@ -1668,6 +1726,10 @@ SpecializeAttributeScope::getEnclosingAbstractStorageDecl() const { return getParent().get()->getEnclosingAbstractStorageDecl(); } NullablePtr +DifferentiableAttributeScope::getEnclosingAbstractStorageDecl() const { + return getParent().get()->getEnclosingAbstractStorageDecl(); +} +NullablePtr AbstractFunctionDeclScope::getEnclosingAbstractStorageDecl() const { return getParent().get()->getEnclosingAbstractStorageDecl(); } @@ -1794,6 +1856,7 @@ GET_REFERRENT(AbstractStmtScope, getStmt()) GET_REFERRENT(CaptureListScope, getExpr()) GET_REFERRENT(WholeClosureScope, getExpr()) GET_REFERRENT(SpecializeAttributeScope, specializeAttr) +GET_REFERRENT(DifferentiableAttributeScope, differentiableAttr) GET_REFERRENT(GenericTypeOrExtensionScope, portion->getReferrentOfScope(this)); const Decl * @@ -1852,10 +1915,10 @@ void ASTScopeImpl::beCurrent() {} bool ASTScopeImpl::isCurrentIfWasExpanded() const { return true; } void ASTSourceFileScope::beCurrent() { - numberOfDeclsAlreadySeen = SF->Decls.size(); + numberOfDeclsAlreadySeen = SF->getTopLevelDecls().size(); } bool ASTSourceFileScope::isCurrentIfWasExpanded() const { - return SF->Decls.size() == numberOfDeclsAlreadySeen; + return SF->getTopLevelDecls().size() == numberOfDeclsAlreadySeen; } void IterableTypeScope::beCurrent() { portion->beCurrent(this); } @@ -1902,6 +1965,7 @@ void AbstractFunctionBodyScope::beCurrent() { bodyWhenLastExpanded = decl->getBody(false); } bool AbstractFunctionBodyScope::isCurrentIfWasExpanded() const { + // Pass in false to keep the compiler from synthesizing one. return bodyWhenLastExpanded == decl->getBody(false); } @@ -1913,27 +1977,22 @@ bool TopLevelCodeScope::isCurrentIfWasExpanded() const { // Try to avoid the work of counting static const bool assumeVarsDoNotGetAdded = true; -static unsigned countVars(const PatternBindingEntry &entry) { - unsigned varCount = 0; - entry.getPattern()->forEachVariable([&](VarDecl *) { ++varCount; }); - return varCount; -} - void PatternEntryDeclScope::beCurrent() { initWhenLastExpanded = getPatternEntry().getOriginalInit(); if (assumeVarsDoNotGetAdded && varCountWhenLastExpanded) return; - varCountWhenLastExpanded = countVars(getPatternEntry()); + varCountWhenLastExpanded = getPatternEntry().getNumBoundVariables(); } bool PatternEntryDeclScope::isCurrentIfWasExpanded() const { if (initWhenLastExpanded != getPatternEntry().getOriginalInit()) return false; if (assumeVarsDoNotGetAdded && varCountWhenLastExpanded) { - ASTScopeAssert(varCountWhenLastExpanded == countVars(getPatternEntry()), + ASTScopeAssert(varCountWhenLastExpanded == + getPatternEntry().getNumBoundVariables(), "Vars were not supposed to be added to a pattern entry."); return true; } - return countVars(getPatternEntry()) == varCountWhenLastExpanded; + return getPatternEntry().getNumBoundVariables() == varCountWhenLastExpanded; } void WholeClosureScope::beCurrent() { @@ -2060,8 +2119,8 @@ class LocalizableDeclContextCollector : public ASTWalker { } void recordInitializers(PatternBindingDecl *pbd) { - for (auto entry : pbd->getPatternList()) - record(entry.getInitContext()); + for (auto idx : range(pbd->getNumPatternEntries())) + record(pbd->getInitContext(idx)); } void catchForDebugging(Decl *D, const char *file, const unsigned line) { diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index fa575a086a939..8f165166e89a5 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -36,8 +36,10 @@ using namespace swift; using namespace namelookup; using namespace ast_scope; +static bool isLocWithinAnInactiveClause(const SourceLoc loc, SourceFile *SF); + llvm::SmallVector ASTScopeImpl::unqualifiedLookup( - SourceFile *sourceFile, const DeclName name, const SourceLoc loc, + SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc, const DeclContext *const startingContext, DeclConsumer consumer) { SmallVector history; const auto *start = @@ -48,7 +50,7 @@ llvm::SmallVector ASTScopeImpl::unqualifiedLookup( } const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( - SourceFile *sourceFile, const DeclName name, const SourceLoc loc, + SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc, const DeclContext *const startingContext) { // At present, use legacy code in unqualifiedLookup.cpp to handle module-level // lookups @@ -61,7 +63,7 @@ const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( if (name.isOperator()) return fileScope; // operators always at file scope - const auto innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); + const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr); ASTScopeAssert(innermost->getWasExpanded(), "If looking in a scope, it must have been expanded."); @@ -82,13 +84,19 @@ const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( if (!startingScope) { llvm::errs() << "ASTScopeImpl: resorting to startingScope hack, file: " << sourceFile->getFilename() << "\n"; + // The check is costly, and inactive lookups will end up here, so don't + // do the check unless we can't find the startingScope. + const bool isInInactiveClause = + isLocWithinAnInactiveClause(loc, sourceFile); + if (isInInactiveClause) + llvm::errs() << " because location is within an inactive clause\n"; llvm::errs() << "'"; name.print(llvm::errs()); llvm::errs() << "' "; llvm::errs() << "loc: "; - loc.dump(sourceFile->getASTContext().SourceMgr); + loc.print(llvm::errs(), sourceFile->getASTContext().SourceMgr); llvm::errs() << "\nstarting context:\n "; - startingContext->dumpContext(); + startingContext->printContext(llvm::errs()); // llvm::errs() << "\ninnermost: "; // innermost->dump(); // llvm::errs() << "in: \n"; @@ -98,20 +106,25 @@ const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup( // Might distort things // if (fileScope->crossCheckWithAST()) // llvm::errs() << "Tree creation missed some DeclContexts.\n"; + + // Crash compilation even if NDEBUG + if (isInInactiveClause) + llvm::report_fatal_error( + "A lookup was attempted into an inactive clause"); } ASTScopeAssert(startingScope, "ASTScopeImpl: could not find startingScope"); return startingScope; } -const ASTScopeImpl * +ASTScopeImpl * ASTScopeImpl::findInnermostEnclosingScope(SourceLoc loc, NullablePtr os) { return findInnermostEnclosingScopeImpl(loc, os, getSourceManager(), getScopeCreator()); } -const ASTScopeImpl *ASTScopeImpl::findInnermostEnclosingScopeImpl( +ASTScopeImpl *ASTScopeImpl::findInnermostEnclosingScopeImpl( SourceLoc loc, NullablePtr os, SourceManager &sourceMgr, ScopeCreator &scopeCreator) { expandAndBeCurrentDetectingRecursion(scopeCreator); @@ -139,13 +152,16 @@ ASTScopeImpl::findChildContaining(SourceLoc loc, bool operator()(const ASTScopeImpl *scope, SourceLoc loc) { ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range."); - return sourceMgr.isBeforeInBuffer(scope->getSourceRangeOfScope().End, - loc); + return -1 == ASTScopeImpl::compare(scope->getSourceRangeOfScope(), loc, + sourceMgr, + /*ensureDisjoint=*/false); } bool operator()(SourceLoc loc, const ASTScopeImpl *scope) { ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range."); - return sourceMgr.isBeforeInBuffer(loc, - scope->getSourceRangeOfScope().End); + // Alternatively, we could check that loc < start-of-scope + return 0 >= ASTScopeImpl::compare(loc, scope->getSourceRangeOfScope(), + sourceMgr, + /*ensureDisjoint=*/false); } }; auto *const *child = std::lower_bound( @@ -192,6 +208,19 @@ bool GenericParamScope::doesContextMatchStartingContext( return false; } +bool DifferentiableAttributeScope::doesContextMatchStartingContext( + const DeclContext *context) const { + // Need special logic to handle case where `attributedDeclaration` is an + // `AbstractStorageDecl` (`SubscriptDecl` or `VarDecl`). The initial starting + // context in `ASTScopeImpl::findStartingScopeForLookup` will be an accessor + // of the `attributedDeclaration`. + if (auto *asd = dyn_cast(attributedDeclaration)) + for (auto accessor : asd->getAllAccessors()) + if (up_cast(accessor) == context) + return true; + return false; +} + #pragma mark lookup methods that run once per scope void ASTScopeImpl::lookup(SmallVectorImpl &history, @@ -422,6 +451,25 @@ bool SpecializeAttributeScope::lookupLocalsOrMembers( return false; } +bool DifferentiableAttributeScope::lookupLocalsOrMembers( + ArrayRef, DeclConsumer consumer) const { + auto visitAbstractFunctionDecl = [&](AbstractFunctionDecl *afd) { + if (auto *params = afd->getGenericParams()) + for (auto *param : params->getParams()) + if (consumer.consume({param}, DeclVisibilityKind::GenericParameter)) + return true; + return false; + }; + if (auto *afd = dyn_cast(attributedDeclaration)) { + return visitAbstractFunctionDecl(afd); + } else if (auto *asd = dyn_cast(attributedDeclaration)) { + for (auto *accessor : asd->getAllAccessors()) + if (visitAbstractFunctionDecl(accessor)) + return true; + } + return false; +} + bool BraceStmtScope::lookupLocalsOrMembers(ArrayRef, DeclConsumer consumer) const { // All types and functions are visible anywhere within a brace statement @@ -444,7 +492,7 @@ bool PatternEntryInitializerScope::lookupLocalsOrMembers( ArrayRef, DeclConsumer consumer) const { // 'self' is available within the pattern initializer of a 'lazy' variable. auto *initContext = cast_or_null( - decl->getPatternList()[0].getInitContext()); + decl->getInitContext(0)); if (initContext) { if (auto *selfParam = initContext->getImplicitSelfDecl()) { return consumer.consume({selfParam}, @@ -501,12 +549,59 @@ GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC( while (i != 0) { Optional> maybeSelfDC = history[--i]->computeSelfDCForParent(); - if (maybeSelfDC) - return *maybeSelfDC; + if (maybeSelfDC) { + // If we've found a selfDC, we'll definitely be returning something. + // However, we may have captured 'self' somewhere down the tree, so we + // can't return outright without checking the nested scopes. + NullablePtr nestedCapturedSelfDC = + checkNestedScopesForSelfCapture(history, i); + return nestedCapturedSelfDC ? nestedCapturedSelfDC : *maybeSelfDC; + } } return nullptr; } +#pragma mark checkNestedScopesForSelfCapture + +NullablePtr +GenericTypeOrExtensionWhereOrBodyPortion::checkNestedScopesForSelfCapture( + ArrayRef history, size_t start) { + NullablePtr innerCapturedSelfDC; + // Start with the next scope down the tree. + size_t j = start; + + // Note: even though having this loop nested inside the while loop from + // GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC may appear to + // result in quadratic blowup, complexity actually remains linear with respect + // to the size of history. This relies on the fact that + // GenericTypeOrExtensionScope::computeSelfDCForParent returns a null pointer, + // which will cause this method to bail out of the search early. Thus, this + // method is called once per type body in the lookup history, and will not + // end up re-checking the bodies of nested types that have already been + // covered by earlier calls, so the total impact of this method across all + // calls in a single lookup is O(n). + while (j != 0) { + auto *entry = history[--j]; + Optional> selfDCForParent = + entry->computeSelfDCForParent(); + + // If we encounter a scope that should cause us to forget the self + // context (such as a nested type), bail out and use whatever the + // the last inner captured context was. + if (selfDCForParent && (*selfDCForParent).isNull()) + break; + + // Otherwise, if we have a captured self context for this scope, then + // remember it since it is now the innermost scope we have encountered. + NullablePtr capturedSelfDC = entry->capturedSelfDC(); + if (!capturedSelfDC.isNull()) + innerCapturedSelfDC = entry->capturedSelfDC(); + + // Continue searching in the next scope down. + } + return innerCapturedSelfDC; +} + #pragma mark compute isCascadingUse Optional ASTScopeImpl::computeIsCascadingUse( @@ -615,6 +710,23 @@ MethodBodyScope::computeSelfDCForParent() const { return NullablePtr(decl); } +#pragma mark capturedSelfDC + +// Closures may explicitly capture the self param, in which case the lookup +// should use the closure as the context for implicit self lookups. + +// By default, there is no such context to return. +NullablePtr ASTScopeImpl::capturedSelfDC() const { + return NullablePtr(); +} + +// Closures track this information explicitly. +NullablePtr ClosureParametersScope::capturedSelfDC() const { + if (closureExpr->capturesSelfEnablingImplictSelf()) + return NullablePtr(closureExpr); + return NullablePtr(); +} + #pragma mark ifUnknownIsCascadingUseAccordingTo static bool isCascadingUseAccordingTo(const DeclContext *const dc) { @@ -694,3 +806,36 @@ Optional PatternEntryInitializerScope::resolveIsCascadingUseForThisScope( return isCascadingUse; } + +bool isLocWithinAnInactiveClause(const SourceLoc loc, SourceFile *SF) { + class InactiveClauseTester : public ASTWalker { + const SourceLoc loc; + const SourceManager &SM; + + public: + bool wasFoundWithinInactiveClause = false; + + InactiveClauseTester(const SourceLoc loc, const SourceManager &SM) + : loc(loc), SM(SM) {} + + bool walkToDeclPre(Decl *D) override { + if (const auto *ifc = dyn_cast(D)) { + for (const auto &clause : ifc->getClauses()) { + if (clause.isActive) + continue; + for (const auto n : clause.Elements) { + SourceRange sr = n.getSourceRange(); + if (sr.isValid() && SM.rangeContainsTokenLoc(sr, loc)) { + wasFoundWithinInactiveClause = true; + return false; + } + } + } + } + return ASTWalker::walkToDeclPre(D); + } + }; + InactiveClauseTester tester(loc, SF->getASTContext().SourceMgr); + SF->walk(tester); + return tester.wasFoundWithinInactiveClause; +} diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index e870308335700..92729d5518192 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -193,6 +193,11 @@ SourceRange SpecializeAttributeScope::getSourceRangeOfThisASTNode( return specializeAttr->getRange(); } +SourceRange DifferentiableAttributeScope::getSourceRangeOfThisASTNode( + const bool omitAssertions) const { + return differentiableAttr->getRange(); +} + SourceRange AbstractFunctionBodyScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { return decl->getBodySourceRange(); @@ -225,7 +230,7 @@ SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode( SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { - if (auto *dv = decl->getDefaultValue()) + if (auto *dv = decl->getStructuralDefaultExpr()) return dv->getSourceRange(); return SourceRange(); } @@ -284,12 +289,12 @@ SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode( return SourceRange(charRange.getStart(), charRange.getEnd()); } - if (SF->Decls.empty()) + if (SF->getTopLevelDecls().empty()) return SourceRange(); // Use the source ranges of the declarations in the file. - return SourceRange(SF->Decls.front()->getStartLoc(), - SF->Decls.back()->getEndLoc()); + return SourceRange(SF->getTopLevelDecls().front()->getStartLoc(), + SF->getTopLevelDecls().back()->getEndLoc()); } SourceRange GenericTypeOrExtensionScope::getSourceRangeOfThisASTNode( @@ -764,3 +769,34 @@ SourceLoc ast_scope::extractNearestSourceLoc( const ASTScopeImpl *scope = std::get<0>(scopeAndCreator); return scope->getSourceRangeOfThisASTNode().Start; } + +int ASTScopeImpl::compare(const SourceRange lhs, const SourceRange rhs, + const SourceManager &SM, const bool ensureDisjoint) { + ASTScopeAssert(!SM.isBeforeInBuffer(lhs.End, lhs.Start), + "Range is backwards."); + ASTScopeAssert(!SM.isBeforeInBuffer(rhs.End, rhs.Start), + "Range is backwards."); + + auto cmpLoc = [&](const SourceLoc lhs, const SourceLoc rhs) { + return lhs == rhs ? 0 : SM.isBeforeInBuffer(lhs, rhs) ? -1 : 1; + }; + // Establish that we use end locations throughout ASTScopes here + const int endOrder = cmpLoc(lhs.End, rhs.End); + +#ifndef NDEBUG + if (ensureDisjoint) { + const int startOrder = cmpLoc(lhs.Start, rhs.Start); + + if (startOrder * endOrder == -1) { + llvm::errs() << "*** Start order contradicts end order between: ***\n"; + lhs.print(llvm::errs(), SM, false); + llvm::errs() << "\n*** and: ***\n"; + rhs.print(llvm::errs(), SM, false); + } + ASTScopeAssert(startOrder * endOrder != -1, + "Start order contradicts end order"); + } +#endif + + return endOrder; +} diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 4100d80450d86..2fe478a3ecfc4 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -512,8 +512,8 @@ class Verifier : public ASTWalker { if (auto *DC = dyn_cast(D)) { if (D->getDeclContext() != DC->getParent()) { Out << "Decl's DeclContext not in sync with DeclContext's parent\n"; - D->getDeclContext()->dumpContext(); - DC->getParent()->dumpContext(); + D->getDeclContext()->printContext(Out); + DC->getParent()->printContext(Out); abort(); } } @@ -1652,7 +1652,7 @@ class Verifier : public ASTWalker { if (E->getConformance().getRequirement() != hashableDecl) { Out << "conformance on AnyHashableErasureExpr was not for Hashable\n"; - E->getConformance().dump(); + E->getConformance().dump(Out); abort(); } @@ -2357,8 +2357,8 @@ class Verifier : public ASTWalker { void verifyChecked(PatternBindingDecl *binding) { // Look at all of the VarDecls being bound. - for (auto entry : binding->getPatternList()) - if (auto *P = entry.getPattern()) + for (auto idx : range(binding->getNumPatternEntries())) + if (auto *P = binding->getPattern(idx)) P->forEachVariable([&](VarDecl *VD) { // ParamDecls never get PBD's. assert(!isa(VD) && "ParamDecl has a PatternBindingDecl?"); @@ -2645,7 +2645,8 @@ class Verifier : public ASTWalker { // Make sure that the replacement type only uses archetypes allowed // in the context where the normal conformance exists. - auto replacementType = normal->getTypeWitness(assocType); + auto replacementType = + normal->getTypeWitnessUncached(assocType).getWitnessType(); Verifier(M, normal->getDeclContext()) .verifyChecked(replacementType); continue; @@ -2677,7 +2678,7 @@ class Verifier : public ASTWalker { } // Check the witness substitutions. - const auto &witness = normal->getWitness(req); + const auto &witness = normal->getWitnessUncached(req); if (auto *genericEnv = witness.getSyntheticEnvironment()) Generics.push_back(genericEnv->getGenericSignature()); @@ -2865,15 +2866,6 @@ class Verifier : public ASTWalker { void verifyParsed(ConstructorDecl *CD) { PrettyStackTraceDecl debugStack("verifying ConstructorDecl", CD); - - auto *DC = CD->getDeclContext(); - if (!isa(DC) && !isa(DC) && - !CD->isInvalid()) { - Out << "ConstructorDecls outside nominal types and extensions " - "should be marked invalid"; - abort(); - } - verifyParsedBase(CD); } @@ -2954,30 +2946,14 @@ class Verifier : public ASTWalker { Out << "DestructorDecl cannot be generic"; abort(); } - - auto *DC = DD->getDeclContext(); - if (!isa(DC) && !isa(DC) && - !DD->isInvalid()) { - Out << "DestructorDecls outside nominal types and extensions " - "should be marked invalid"; - abort(); - } - verifyParsedBase(DD); } void verifyChecked(AbstractFunctionDecl *AFD) { PrettyStackTraceDecl debugStack("verifying AbstractFunctionDecl", AFD); - if (!AFD->hasInterfaceType()) { - if (isa(AFD) && AFD->isImplicit()) - return; - - Out << "All functions except implicit accessors should be " - "validated by now\n"; - AFD->dump(Out); - abort(); - } + if (!AFD->hasInterfaceType()) + return; // If this function is generic or is within a generic context, it should // have an interface type. @@ -3639,17 +3615,7 @@ class Verifier : public ASTWalker { void checkErrors(Stmt *S) {} void checkErrors(Pattern *P) {} void checkErrors(Decl *D) {} - void checkErrors(ValueDecl *D) { - PrettyStackTraceDecl debugStack("verifying errors", D); - - if (!D->hasInterfaceType()) - return; - if (D->getInterfaceType()->hasError() && !D->isInvalid()) { - Out << "Valid decl has error type!\n"; - D->dump(Out); - abort(); - } - } + void checkErrors(ValueDecl *D) {} }; } // end anonymous namespace diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 55ee596c310d4..004a848a870d6 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -171,21 +171,19 @@ class Traversal : public ASTVisitorgetOriginalWrappedProperty() != nullptr; } - unsigned idx = 0U-1; - for (auto entry : PBD->getPatternList()) { - ++idx; - if (Pattern *Pat = doIt(entry.getPattern())) - PBD->setPattern(idx, Pat, entry.getInitContext()); + for (auto idx : range(PBD->getNumPatternEntries())) { + if (Pattern *Pat = doIt(PBD->getPattern(idx))) + PBD->setPattern(idx, Pat, PBD->getInitContext(idx)); else return true; - if (entry.getInit() && + if (PBD->getInit(idx) && !isPropertyWrapperBackingProperty && - (!entry.isInitializerSubsumed() || + (!PBD->isInitializerSubsumed(idx) || Walker.shouldWalkIntoLazyInitializers())) { #ifndef NDEBUG PrettyStackTraceDecl debugStack("walking into initializer for", PBD); #endif - if (Expr *E2 = doIt(entry.getInit())) + if (Expr *E2 = doIt(PBD->getInit(idx))) PBD->setInit(idx, E2); else return true; @@ -225,9 +223,7 @@ class Traversal : public ASTVisitorgetGenericParams() && - Walker.shouldWalkIntoGenericParams()) { - + if (Walker.shouldWalkIntoGenericParams() && TAD->getGenericParams()) { if (visitGenericParamList(TAD->getGenericParams())) return true; } @@ -239,8 +235,7 @@ class Traversal : public ASTVisitorgetGenericParams() && - Walker.shouldWalkIntoGenericParams()) { + if (Walker.shouldWalkIntoGenericParams() && OTD->getGenericParams()) { if (visitGenericParamList(OTD->getGenericParams())) return true; } @@ -274,16 +269,15 @@ class Traversal : public ASTVisitor(NTD)) { - if (auto *WhereClause = Protocol->getTrailingWhereClause()) { - for (auto &Req: WhereClause->getRequirements()) { - if (doIt(Req)) - return true; - } - } - } if (WalkGenerics) { - for (auto Req: NTD->getGenericParams()->getTrailingRequirements()) { + ArrayRef Reqs = None; + if (auto *Protocol = dyn_cast(NTD)) { + if (auto *WhereClause = Protocol->getTrailingWhereClause()) + Reqs = WhereClause->getRequirements(); + } else { + Reqs = NTD->getGenericParams()->getTrailingRequirements(); + } + for (auto Req: Reqs) { if (doIt(Req)) return true; } @@ -485,15 +479,6 @@ class Traversal : public ASTVisitorgetSubExpr())) { - E->setSubExpr(subExpr); - return E; - } - - return nullptr; - } - Expr *visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E) { if (auto oldAppendingExpr = E->getAppendingExpr()) { if (auto appendingExpr = doIt(oldAppendingExpr)) @@ -1166,10 +1151,10 @@ class Traversal : public ASTVisitorgetDefaultValue()) { + if (auto *E = P->getStructuralDefaultExpr()) { auto res = doIt(E); if (!res) return true; - P->setDefaultValue(res); + P->setDefaultExpr(res, /*isTypeChecked*/ (bool)res->getType()); } } @@ -1340,7 +1325,6 @@ class Traversal : public ASTVisitorgetGenericParams()) { visitGenericParamList(params); diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index f3fc0de11f186..8a14df9f95f4d 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -81,7 +81,7 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { // Special case for dtors and enum elements: inherit from container if (D->getKind() == DeclKind::Destructor || D->getKind() == DeclKind::EnumElement) { - if (D->isInvalid()) { + if (D->hasInterfaceType() && D->isInvalid()) { return AccessLevel::Private; } else { auto container = cast(D->getDeclContext()); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 415774c3c822c..8a1f16b00d8ef 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -20,14 +20,18 @@ #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IndexSubset.h" #include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" +#include "swift/Basic/QuotedString.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" using namespace swift; #define DECL_ATTR(_, Id, ...) \ @@ -81,6 +85,18 @@ StringRef swift::getAccessLevelSpelling(AccessLevel value) { llvm_unreachable("Unhandled AccessLevel in switch."); } +void TypeAttributes::getConventionArguments(SmallVectorImpl &buf) const { + llvm::raw_svector_ostream stream(buf); + auto &convention = ConventionArguments.getValue(); + stream << convention.Name; + if (convention.WitnessMethodProtocol) { + stream << ": " << convention.WitnessMethodProtocol; + return; + } + if (!convention.ClangType.Item.empty()) + stream << ", cType: " << QuotedString(convention.ClangType.Item); +} + /// Given a name like "autoclosure", return the type attribute ID that /// corresponds to it. This returns TAK_Count on failure. /// @@ -156,6 +172,35 @@ DeclAttributes::isUnavailableInSwiftVersion( return false; } +const AvailableAttr * +DeclAttributes::findMostSpecificActivePlatform(const ASTContext &ctx) const{ + const AvailableAttr *bestAttr = nullptr; + + for (auto attr : *this) { + auto *avAttr = dyn_cast(attr); + if (!avAttr) + continue; + + if (avAttr->isInvalid()) + continue; + + if (!avAttr->hasPlatform()) + continue; + + if (!avAttr->isActivePlatform(ctx)) + continue; + + // We have an attribute that is active for the platform, but + // is it more specific than our curent best? + if (!bestAttr || inheritsAvailabilityFromPlatform(avAttr->Platform, + bestAttr->Platform)) { + bestAttr = avAttr; + } + } + + return bestAttr; +} + const AvailableAttr * DeclAttributes::getPotentiallyUnavailable(const ASTContext &ctx) const { const AvailableAttr *potential = nullptr; @@ -201,12 +246,19 @@ DeclAttributes::getPotentiallyUnavailable(const ASTContext &ctx) const { const AvailableAttr *DeclAttributes::getUnavailable( const ASTContext &ctx) const { const AvailableAttr *conditional = nullptr; + const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx); for (auto Attr : *this) if (auto AvAttr = dyn_cast(Attr)) { if (AvAttr->isInvalid()) continue; + // If this is a platform-specific attribute and it isn't the most + // specific attribute for the current platform, we're done. + if (AvAttr->hasPlatform() && + (!bestActive || AvAttr != bestActive)) + continue; + // If this attribute doesn't apply to the active platform, we're done. if (!AvAttr->isActivePlatform(ctx) && !AvAttr->isLanguageVersionSpecific() && @@ -234,11 +286,16 @@ const AvailableAttr *DeclAttributes::getUnavailable( const AvailableAttr * DeclAttributes::getDeprecated(const ASTContext &ctx) const { const AvailableAttr *conditional = nullptr; + const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx); for (auto Attr : *this) { if (auto AvAttr = dyn_cast(Attr)) { if (AvAttr->isInvalid()) continue; + if (AvAttr->hasPlatform() && + (!bestActive || AvAttr != bestActive)) + continue; + if (!AvAttr->isActivePlatform(ctx) && !AvAttr->isLanguageVersionSpecific() && !AvAttr->isPackageDescriptionVersionSpecific()) @@ -313,6 +370,30 @@ static bool isShortAvailable(const DeclAttribute *DA) { return true; } +/// Return true when another availability attribute implies the same availability as this +/// attribute and so printing the attribute can be skipped to de-clutter the declaration +/// when printing the short form. +/// For example, iOS availability implies macCatalyst availability so if attributes for +/// both are present and they have the same 'introduced' version, we can skip printing an +/// explicit availability for macCatalyst. +static bool isShortFormAvailabilityImpliedByOther(const AvailableAttr *Attr, + ArrayRef Others) { + assert(isShortAvailable(Attr)); + + for (auto *DA : Others) { + auto *Other = cast(DA); + if (Attr->Platform == Other->Platform) + continue; + + if (!inheritsAvailabilityFromPlatform(Attr->Platform, Other->Platform)) + continue; + + if (Attr->Introduced == Other->Introduced) + return true; + } + return false; +} + /// Print the short-form @available() attribute for an array of long-form /// AvailableAttrs that can be represented in the short form. /// For example, for: @@ -343,6 +424,8 @@ static void printShortFormAvailable(ArrayRef Attrs, for (auto *DA : Attrs) { auto *AvailAttr = cast(DA); assert(AvailAttr->Introduced.hasValue()); + if (isShortFormAvailabilityImpliedByOther(AvailAttr, Attrs)) + continue; Printer << platformString(AvailAttr->Platform) << " " << AvailAttr->Introduced.getValue().getAsString() << ", "; } @@ -351,6 +434,217 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer.printNewline(); } +/// The kind of a parameter in a `wrt:` differentiation parameters clause: +/// either a differentiability parameter or a linearity parameter. Used for +/// printing `@differentiable`, `@derivative`, and `@transpose` attributes. +enum class DifferentiationParameterKind { + /// A differentiability parameter, printed by name. + /// Used for `@differentiable` and `@derivative` attribute. + Differentiability, + /// A linearity parameter, printed by index. + /// Used for `@transpose` attribute. + Linearity +}; + +/// Returns the differentiation parameters clause string for the given function, +/// parameter indices, parsed parameters, and differentiation parameter kind. +/// Use the parameter indices if specified; otherwise, use the parsed +/// parameters. +static std::string getDifferentiationParametersClauseString( + const AbstractFunctionDecl *function, IndexSubset *parameterIndices, + ArrayRef parsedParams, + DifferentiationParameterKind parameterKind) { + assert(function); + bool isInstanceMethod = function->isInstanceMember(); + bool isStaticMethod = function->isStatic(); + std::string result; + llvm::raw_string_ostream printer(result); + + // Use the parameter indices, if specified. + if (parameterIndices) { + auto parameters = parameterIndices->getBitVector(); + auto parameterCount = parameters.count(); + printer << "wrt: "; + if (parameterCount > 1) + printer << '('; + // Check if differentiating wrt `self`. If so, manually print it first. + bool isWrtSelf = + (isInstanceMethod || + (isStaticMethod && + parameterKind == DifferentiationParameterKind::Linearity)) && + parameters.test(parameters.size() - 1); + if (isWrtSelf) { + parameters.reset(parameters.size() - 1); + printer << "self"; + if (parameters.any()) + printer << ", "; + } + // Print remaining differentiation parameters. + interleave(parameters.set_bits(), [&](unsigned index) { + switch (parameterKind) { + // Print differentiability parameters by name. + case DifferentiationParameterKind::Differentiability: + printer << function->getParameters()->get(index)->getName().str(); + break; + // Print linearity parameters by index. + case DifferentiationParameterKind::Linearity: + printer << index; + break; + } + }, [&] { printer << ", "; }); + if (parameterCount > 1) + printer << ')'; + } + // Otherwise, use the parsed parameters. + else if (!parsedParams.empty()) { + printer << "wrt: "; + if (parsedParams.size() > 1) + printer << '('; + interleave(parsedParams, [&](const ParsedAutoDiffParameter ¶m) { + switch (param.getKind()) { + case ParsedAutoDiffParameter::Kind::Named: + printer << param.getName(); + break; + case ParsedAutoDiffParameter::Kind::Self: + printer << "self"; + break; + case ParsedAutoDiffParameter::Kind::Ordered: + auto *paramList = function->getParameters(); + assert(param.getIndex() <= paramList->size() && + "wrt parameter is out of range"); + auto *funcParam = paramList->get(param.getIndex()); + printer << funcParam->getNameStr(); + break; + } + }, [&] { printer << ", "; }); + if (parsedParams.size() > 1) + printer << ')'; + } + return printer.str(); +} + +/// Print the arguments of the given `@differentiable` attribute. +/// - If `omitWrtClause` is true, omit printing the `wrt:` differentiation +/// parameters clause. +/// - If `omitDerivativeFunctions` is true, omit printing the JVP/VJP derivative +/// functions. +static void printDifferentiableAttrArguments( + const DifferentiableAttr *attr, ASTPrinter &printer, PrintOptions Options, + const Decl *D, bool omitWrtClause = false, + bool omitDerivativeFunctions = false) { + assert(D); + // Create a temporary string for the attribute argument text. + std::string attrArgText; + llvm::raw_string_ostream stream(attrArgText); + + // Get original function. + auto *original = dyn_cast(D); + // Handle stored/computed properties and subscript methods. + if (auto *asd = dyn_cast(D)) + original = asd->getAccessor(AccessorKind::Get); + assert(original && "Must resolve original declaration"); + + // Print comma if not leading clause. + bool isLeadingClause = true; + auto printCommaIfNecessary = [&] { + if (isLeadingClause) { + isLeadingClause = false; + return; + } + stream << ", "; + }; + + // Print if the function is marked as linear. + if (attr->isLinear()) { + isLeadingClause = false; + stream << "linear"; + } + + // Print differentiation parameters clause, unless it is to be omitted. + if (!omitWrtClause) { + auto diffParamsString = getDifferentiationParametersClauseString( + original, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Differentiability); + // Check whether differentiation parameter clause is empty. + // Handles edge case where resolved parameter indices are unset and + // parsed parameters are empty. This case should never trigger for + // user-visible printing. + if (!diffParamsString.empty()) { + printCommaIfNecessary(); + stream << diffParamsString; + } + } + // Print derivative function names, unless they are to be omitted. + if (!omitDerivativeFunctions) { + // Print jvp function name, if specified. + if (auto jvp = attr->getJVP()) { + printCommaIfNecessary(); + stream << "jvp: " << jvp->Name; + } + // Print vjp function name, if specified. + if (auto vjp = attr->getVJP()) { + printCommaIfNecessary(); + stream << "vjp: " << vjp->Name; + } + } + // Print 'where' clause, if any. + // First, filter out requirements satisfied by the original function's + // generic signature. They should not be printed. + ArrayRef derivativeRequirements; + if (auto derivativeGenSig = attr->getDerivativeGenericSignature()) + derivativeRequirements = derivativeGenSig->getRequirements(); + auto requirementsToPrint = + llvm::make_filter_range(derivativeRequirements, [&](Requirement req) { + if (const auto &originalGenSig = original->getGenericSignature()) + if (originalGenSig->isRequirementSatisfied(req)) + return false; + return true; + }); + if (!llvm::empty(requirementsToPrint)) { + if (!isLeadingClause) + stream << ' '; + stream << "where "; + std::function getInterfaceType; + if (!original || !original->getGenericEnvironment()) { + getInterfaceType = [](Type Ty) -> Type { return Ty; }; + } else { + // Use GenericEnvironment to produce user-friendly + // names instead of something like 't_0_0'. + auto *genericEnv = original->getGenericEnvironment(); + assert(genericEnv); + getInterfaceType = [=](Type Ty) -> Type { + return genericEnv->getSugaredType(Ty); + }; + } + interleave(requirementsToPrint, [&](Requirement req) { + if (const auto &originalGenSig = original->getGenericSignature()) + if (originalGenSig->isRequirementSatisfied(req)) + return; + auto FirstTy = getInterfaceType(req.getFirstType()); + if (req.getKind() != RequirementKind::Layout) { + auto SecondTy = getInterfaceType(req.getSecondType()); + Requirement ReqWithDecls(req.getKind(), FirstTy, SecondTy); + ReqWithDecls.print(stream, Options); + } else { + Requirement ReqWithDecls(req.getKind(), FirstTy, + req.getLayoutConstraint()); + ReqWithDecls.print(stream, Options); + } + }, [&] { + stream << ", "; + }); + } + + // If the attribute argument text is empty, return. Do not print parentheses. + if (stream.str().empty()) + return; + + // Otherwise, print the attribute argument text enclosed in parentheses. + printer << '('; + printer << stream.str(); + printer << ')'; +} + void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D) const { if (!DeclAttrs) @@ -509,6 +803,17 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << "(\"" << cast(this)->Name << "\")"; break; + case DAK_OriginallyDefinedIn: { + Printer.printAttrName("@_originallyDefinedIn"); + Printer << "(module: "; + auto Attr = cast(this); + Printer << "\"" << Attr->OriginalModuleName << "\", "; + Printer << platformString(Attr->Platform) << " " << + Attr->MovedVersion.getAsString(); + Printer << ")"; + break; + } + case DAK_Available: { Printer.printAttrName("@available"); Printer << "("; @@ -684,6 +989,43 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << ")"; break; + case DAK_Differentiable: { + Printer.printAttrName("@differentiable"); + auto *attr = cast(this); + printDifferentiableAttrArguments(attr, Printer, Options, D); + break; + } + + case DAK_Derivative: { + Printer.printAttrName("@derivative"); + Printer << "(of: "; + auto *attr = cast(this); + Printer << attr->getOriginalFunctionName().Name; + auto *derivative = cast(D); + auto diffParamsString = getDifferentiationParametersClauseString( + derivative, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Differentiability); + if (!diffParamsString.empty()) + Printer << ", " << diffParamsString; + Printer << ')'; + break; + } + + case DAK_Transpose: { + Printer.printAttrName("@transpose"); + Printer << "(of: "; + auto *attr = cast(this); + Printer << attr->getOriginalFunctionName().Name; + auto *transpose = cast(D); + auto transParamsString = getDifferentiationParametersClauseString( + transpose, attr->getParameterIndices(), attr->getParsedParameters(), + DifferentiationParameterKind::Linearity); + if (!transParamsString.empty()) + Printer << ", " << transParamsString; + Printer << ')'; + break; + } + case DAK_Count: llvm_unreachable("exceed declaration attribute kinds"); @@ -814,6 +1156,14 @@ StringRef DeclAttribute::getAttrName() const { return "<>"; case DAK_ProjectedValueProperty: return "_projectedValueProperty"; + case DAK_OriginallyDefinedIn: + return "_originallyDefinedIn"; + case DAK_Differentiable: + return "differentiable"; + case DAK_Derivative: + return "derivative"; + case DAK_Transpose: + return "transpose"; } llvm_unreachable("bad DeclAttrKind"); } @@ -940,11 +1290,11 @@ PrivateImportAttr *PrivateImportAttr::create(ASTContext &Ctxt, SourceLoc AtLoc, DynamicReplacementAttr::DynamicReplacementAttr(SourceLoc atLoc, SourceRange baseRange, - DeclName name, + DeclNameRef name, SourceRange parenRange) : DeclAttribute(DAK_DynamicReplacement, atLoc, baseRange, /*Implicit=*/false), - ReplacedFunctionName(name), ReplacedFunction(nullptr) { + ReplacedFunctionName(name) { Bits.DynamicReplacementAttr.HasTrailingLocationInfo = true; getTrailingLocations()[0] = parenRange.Start; getTrailingLocations()[1] = parenRange.End; @@ -953,7 +1303,7 @@ DynamicReplacementAttr::DynamicReplacementAttr(SourceLoc atLoc, DynamicReplacementAttr * DynamicReplacementAttr::create(ASTContext &Ctx, SourceLoc AtLoc, SourceLoc DynReplLoc, SourceLoc LParenLoc, - DeclName ReplacedFunction, SourceLoc RParenLoc) { + DeclNameRef ReplacedFunction, SourceLoc RParenLoc) { void *mem = Ctx.Allocate(totalSizeToAlloc(2), alignof(DynamicReplacementAttr)); return new (mem) DynamicReplacementAttr( @@ -961,17 +1311,16 @@ DynamicReplacementAttr::create(ASTContext &Ctx, SourceLoc AtLoc, SourceRange(LParenLoc, RParenLoc)); } -DynamicReplacementAttr *DynamicReplacementAttr::create(ASTContext &Ctx, - DeclName name) { - return new (Ctx) DynamicReplacementAttr(name); +DynamicReplacementAttr * +DynamicReplacementAttr::create(ASTContext &Ctx, DeclNameRef name, + AbstractFunctionDecl *f) { + return new (Ctx) DynamicReplacementAttr(name, f); } DynamicReplacementAttr * -DynamicReplacementAttr::create(ASTContext &Ctx, DeclName name, - AbstractFunctionDecl *f) { - auto res = new (Ctx) DynamicReplacementAttr(name); - res->setReplacedFunction(f); - return res; +DynamicReplacementAttr::create(ASTContext &Ctx, DeclNameRef name, + LazyMemberLoader *Resolver, uint64_t Data) { + return new (Ctx) DynamicReplacementAttr(name, Resolver, Data); } SourceLoc DynamicReplacementAttr::getLParenLoc() const { @@ -1005,6 +1354,28 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const { return isPlatformActive(Platform, ctx.LangOpts); } +Optional +OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const { + OriginallyDefinedInAttr::ActiveVersion Result; + Result.Platform = Platform; + Result.Version = MovedVersion; + Result.ModuleName = OriginalModuleName; + if (isPlatformActive(Platform, ctx.LangOpts, /*TargetVariant*/false)) { + Result.IsSimulator = ctx.LangOpts.Target.isSimulatorEnvironment(); + return Result; + } + + // Also check if the platform is active by using target variant. This ensures + // we emit linker directives for multiple platforms when building zippered + // libraries. + if (ctx.LangOpts.TargetVariant.hasValue() && + isPlatformActive(Platform, ctx.LangOpts, /*TargetVariant*/true)) { + Result.IsSimulator = ctx.LangOpts.TargetVariant->isSimulatorEnvironment(); + return Result; + } + return None; +} + bool AvailableAttr::isLanguageVersionSpecific() const { if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::SwiftVersionSpecific) @@ -1149,6 +1520,203 @@ SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, specializedSignature); } +DifferentiableAttr::DifferentiableAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, bool linear, + ArrayRef params, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause) + : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), + Linear(linear), NumParsedParameters(params.size()), JVP(std::move(jvp)), + VJP(std::move(vjp)), WhereClause(clause) { + std::copy(params.begin(), params.end(), + getTrailingObjects()); +} + +DifferentiableAttr::DifferentiableAttr(Decl *original, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + IndexSubset *parameterIndices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig) + : DeclAttribute(DAK_Differentiable, atLoc, baseRange, implicit), + OriginalDeclaration(original), Linear(linear), JVP(std::move(jvp)), + VJP(std::move(vjp)) { + setParameterIndices(parameterIndices); + setDerivativeGenericSignature(derivativeGenSig); +} + +DifferentiableAttr * +DifferentiableAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + bool linear, + ArrayRef parameters, + Optional jvp, + Optional vjp, + TrailingWhereClause *clause) { + unsigned size = totalSizeToAlloc(parameters.size()); + void *mem = context.Allocate(size, alignof(DifferentiableAttr)); + return new (mem) DifferentiableAttr(implicit, atLoc, baseRange, linear, + parameters, std::move(jvp), + std::move(vjp), clause); +} + +DifferentiableAttr * +DifferentiableAttr::create(AbstractFunctionDecl *original, bool implicit, + SourceLoc atLoc, SourceRange baseRange, bool linear, + IndexSubset *parameterIndices, + Optional jvp, + Optional vjp, + GenericSignature derivativeGenSig) { + auto &ctx = original->getASTContext(); + void *mem = ctx.Allocate(sizeof(DifferentiableAttr), + alignof(DifferentiableAttr)); + return new (mem) DifferentiableAttr(original, implicit, atLoc, baseRange, + linear, parameterIndices, std::move(jvp), + std::move(vjp), derivativeGenSig); +} + +void DifferentiableAttr::setOriginalDeclaration(Decl *originalDeclaration) { + assert(originalDeclaration && "Original declaration must be non-null"); + assert(!OriginalDeclaration && + "Original declaration cannot have already been set"); + OriginalDeclaration = originalDeclaration; +} + +bool DifferentiableAttr::hasBeenTypeChecked() const { + return ParameterIndicesAndBit.getInt(); +} + +IndexSubset *DifferentiableAttr::getParameterIndices() const { + assert(getOriginalDeclaration() && + "Original declaration must have been resolved"); + auto &ctx = getOriginalDeclaration()->getASTContext(); + return evaluateOrDefault(ctx.evaluator, + DifferentiableAttributeTypeCheckRequest{ + const_cast(this)}, + nullptr); +} + +void DifferentiableAttr::setParameterIndices(IndexSubset *paramIndices) { + assert(getOriginalDeclaration() && + "Original declaration must have been resolved"); + auto &ctx = getOriginalDeclaration()->getASTContext(); + ctx.evaluator.cacheOutput( + DifferentiableAttributeTypeCheckRequest{ + const_cast(this)}, + std::move(paramIndices)); +} + +void DifferentiableAttr::setJVPFunction(FuncDecl *decl) { + JVPFunction = decl; + if (decl && !JVP) + JVP = {decl->createNameRef(), DeclNameLoc(decl->getNameLoc())}; +} + +void DifferentiableAttr::setVJPFunction(FuncDecl *decl) { + VJPFunction = decl; + if (decl && !VJP) + VJP = {decl->createNameRef(), DeclNameLoc(decl->getNameLoc())}; +} + +GenericEnvironment *DifferentiableAttr::getDerivativeGenericEnvironment( + AbstractFunctionDecl *original) const { + GenericEnvironment *derivativeGenEnv = original->getGenericEnvironment(); + if (auto derivativeGenSig = getDerivativeGenericSignature()) + return derivativeGenEnv = derivativeGenSig->getGenericEnvironment(); + return original->getGenericEnvironment(); +} + +void DifferentiableAttr::print(llvm::raw_ostream &OS, const Decl *D, + bool omitWrtClause, + bool omitDerivativeFunctions) const { + StreamPrinter P(OS); + P << "@" << getAttrName(); + printDifferentiableAttrArguments(this, P, PrintOptions(), D, omitWrtClause, + omitDerivativeFunctions); +} + +DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + ArrayRef params) + : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + NumParsedParameters(params.size()) { + std::copy(params.begin(), params.end(), + getTrailingObjects()); +} + +DerivativeAttr::DerivativeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) + : DeclAttribute(DAK_Derivative, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(parameterIndices) {} + +DerivativeAttr * +DerivativeAttr::create(ASTContext &context, bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + ArrayRef params) { + unsigned size = totalSizeToAlloc(params.size()); + void *mem = context.Allocate(size, alignof(DerivativeAttr)); + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, + std::move(originalName), params); +} + +DerivativeAttr *DerivativeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) { + void *mem = context.Allocate(sizeof(DerivativeAttr), alignof(DerivativeAttr)); + return new (mem) DerivativeAttr(implicit, atLoc, baseRange, baseTypeRepr, + std::move(originalName), parameterIndices); +} + +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + ArrayRef params) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + NumParsedParameters(params.size()) { + std::uninitialized_copy(params.begin(), params.end(), + getTrailingObjects()); +} + +TransposeAttr::TransposeAttr(bool implicit, SourceLoc atLoc, + SourceRange baseRange, TypeRepr *baseTypeRepr, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) + : DeclAttribute(DAK_Transpose, atLoc, baseRange, implicit), + BaseTypeRepr(baseTypeRepr), OriginalFunctionName(std::move(originalName)), + ParameterIndices(parameterIndices) {} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + ArrayRef params) { + unsigned size = totalSizeToAlloc(params.size()); + void *mem = context.Allocate(size, alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), params); +} + +TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, + SourceLoc atLoc, SourceRange baseRange, + TypeRepr *baseType, + DeclNameRefWithLoc originalName, + IndexSubset *parameterIndices) { + void *mem = context.Allocate(sizeof(TransposeAttr), alignof(TransposeAttr)); + return new (mem) TransposeAttr(implicit, atLoc, baseRange, baseType, + std::move(originalName), parameterIndices); +} + ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, TypeLoc ProtocolType, DeclName MemberName, diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp new file mode 100644 index 0000000000000..fa99620c4ab64 --- /dev/null +++ b/lib/AST/AutoDiff.cpp @@ -0,0 +1,86 @@ +//===--- AutoDiff.cpp - Swift automatic differentiation utilities ---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/AutoDiff.h" +#include "swift/AST/Types.h" + +using namespace swift; + +// TODO(TF-874): This helper is inefficient and should be removed. Unwrapping at +// most once (for curried method types) is sufficient. +static void unwrapCurryLevels(AnyFunctionType *fnTy, + SmallVectorImpl &results) { + while (fnTy != nullptr) { + results.push_back(fnTy); + fnTy = fnTy->getResult()->getAs(); + } +} + +static unsigned countNumFlattenedElementTypes(Type type) { + if (auto *tupleTy = type->getCanonicalType()->getAs()) + return accumulate(tupleTy->getElementTypes(), 0, + [&](unsigned num, Type type) { + return num + countNumFlattenedElementTypes(type); + }); + return 1; +} + +// TODO(TF-874): Simplify this helper and remove the `reverseCurryLevels` flag. +// See TF-874 for WIP. +void autodiff::getSubsetParameterTypes(IndexSubset *subset, + AnyFunctionType *type, + SmallVectorImpl &results, + bool reverseCurryLevels) { + SmallVector curryLevels; + unwrapCurryLevels(type, curryLevels); + + SmallVector curryLevelParameterIndexOffsets(curryLevels.size()); + unsigned currentOffset = 0; + for (unsigned curryLevelIndex : llvm::reverse(indices(curryLevels))) { + curryLevelParameterIndexOffsets[curryLevelIndex] = currentOffset; + currentOffset += curryLevels[curryLevelIndex]->getNumParams(); + } + + // If `reverseCurryLevels` is true, reverse the curry levels and offsets. + if (reverseCurryLevels) { + std::reverse(curryLevels.begin(), curryLevels.end()); + std::reverse(curryLevelParameterIndexOffsets.begin(), + curryLevelParameterIndexOffsets.end()); + } + + for (unsigned curryLevelIndex : indices(curryLevels)) { + auto *curryLevel = curryLevels[curryLevelIndex]; + unsigned parameterIndexOffset = + curryLevelParameterIndexOffsets[curryLevelIndex]; + for (unsigned paramIndex : range(curryLevel->getNumParams())) + if (subset->contains(parameterIndexOffset + paramIndex)) + results.push_back(curryLevel->getParams()[paramIndex].getOldType()); + } +} + +Type TangentSpace::getType() const { + switch (kind) { + case Kind::TangentVector: + return value.tangentVectorType; + case Kind::Tuple: + return value.tupleType; + } +} + +CanType TangentSpace::getCanonicalType() const { + return getType()->getCanonicalType(); +} + +NominalTypeDecl *TangentSpace::getNominal() const { + assert(isTangentVector()); + return getTangentVector()->getNominalOrBoundGenericNominal(); +} diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index d094532cb5297..538924ed8a23b 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -237,3 +237,45 @@ AvailabilityContext ASTContext::getSwift51Availability() { return AvailabilityContext::alwaysAvailable(); } } + +AvailabilityContext ASTContext::getTypesInAbstractMetadataStateAvailability() { + return getSwift52Availability(); +} + +AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() { + return getSwiftFutureAvailability(); +} + +AvailabilityContext ASTContext::getSwift52Availability() { + auto target = LangOpts.Target; + + if (target.isMacOSX() ) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(10, 99, 0))); + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(99, 0, 0))); + } else if (target.isWatchOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(9, 99, 0))); + } else { + return AvailabilityContext::alwaysAvailable(); + } +} + +AvailabilityContext ASTContext::getSwiftFutureAvailability() { + auto target = LangOpts.Target; + + if (target.isMacOSX() ) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(10, 99, 0))); + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(99, 0, 0))); + } else if (target.isWatchOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(9, 99, 0))); + } else { + return AvailabilityContext::alwaysAvailable(); + } +} diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 9e42f16f200d3..818d7ffb112d2 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -17,10 +17,8 @@ #include "swift/AST/Builtins.h" #include "swift/AST/ASTContext.h" #include "swift/AST/FileUnit.h" -#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" -#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/LLVMContext.h" #include "swift/Strings.h" #include "llvm/ADT/SmallString.h" @@ -149,8 +147,7 @@ StringRef swift::getBuiltinBaseName(ASTContext &C, StringRef Name, /// Build a builtin function declaration. static FuncDecl * -getBuiltinFunction(Identifier Id, ArrayRef argTypes, Type ResType, - FunctionType::ExtInfo Info = FunctionType::ExtInfo()) { +getBuiltinFunction(Identifier Id, ArrayRef argTypes, Type ResType) { auto &Context = ResType->getASTContext(); ModuleDecl *M = Context.TheBuiltinModule; @@ -177,7 +174,6 @@ getBuiltinFunction(Identifier Id, ArrayRef argTypes, Type ResType, /*GenericParams=*/nullptr, paramList, TypeLoc::withoutLoc(ResType), DC); - FD->computeType(Info); FD->setImplicit(); FD->setAccess(AccessLevel::Public); return FD; @@ -188,8 +184,7 @@ static FuncDecl * getBuiltinGenericFunction(Identifier Id, ArrayRef ArgParamTypes, Type ResType, - GenericParamList *GenericParams, - GenericSignature Sig) { + GenericParamList *GenericParams) { assert(GenericParams && "Missing generic parameters"); auto &Context = ResType->getASTContext(); @@ -223,8 +218,6 @@ getBuiltinGenericFunction(Identifier Id, paramList, TypeLoc::withoutLoc(ResType), DC); - func->setGenericSignature(Sig); - func->computeType(); func->setImplicit(); func->setAccess(AccessLevel::Public); @@ -432,11 +425,10 @@ createGenericParam(ASTContext &ctx, const char *name, unsigned index) { /// Create a generic parameter list with multiple generic parameters. static GenericParamList *getGenericParams(ASTContext &ctx, - unsigned numParameters, - SmallVectorImpl &genericParams) { + unsigned numParameters) { assert(numParameters <= llvm::array_lengthof(GenericParamNames)); - assert(genericParams.empty()); + SmallVector genericParams; for (unsigned i = 0; i != numParameters; ++i) genericParams.push_back(createGenericParam(ctx, GenericParamNames[i], i)); @@ -446,34 +438,19 @@ static GenericParamList *getGenericParams(ASTContext &ctx, } namespace { - class BuiltinGenericSignatureBuilder { + class BuiltinFunctionBuilder { public: ASTContext &Context; private: GenericParamList *TheGenericParamList; - SmallVector GenericTypeParams; - GenericSignature GenericSig = GenericSignature(); SmallVector InterfaceParams; Type InterfaceResult; public: - BuiltinGenericSignatureBuilder(ASTContext &ctx, unsigned numGenericParams = 1) + BuiltinFunctionBuilder(ASTContext &ctx, unsigned numGenericParams = 1) : Context(ctx) { - TheGenericParamList = getGenericParams(ctx, numGenericParams, - GenericTypeParams); - - SmallVector genericParamTypes; - for (auto gp : GenericTypeParams) { - genericParamTypes.push_back( - gp->getDeclaredInterfaceType()->castTo()); - } - - GenericSig = evaluateOrDefault( - ctx.evaluator, - AbstractGenericSignatureRequest{ - nullptr, std::move(genericParamTypes), { }}, - GenericSignature()); + TheGenericParamList = getGenericParams(ctx, numGenericParams); } template @@ -489,11 +466,10 @@ namespace { InterfaceResult = generator.build(*this); } - ValueDecl *build(Identifier name) { + FuncDecl *build(Identifier name) { return getBuiltinGenericFunction(name, InterfaceParams, InterfaceResult, - TheGenericParamList, - GenericSig); + TheGenericParamList); } // Don't use these generator classes directly; call the make{...} @@ -501,19 +477,20 @@ namespace { struct ConcreteGenerator { Type TheType; - Type build(BuiltinGenericSignatureBuilder &builder) const { + Type build(BuiltinFunctionBuilder &builder) const { return TheType; } }; struct ParameterGenerator { unsigned Index; - Type build(BuiltinGenericSignatureBuilder &builder) const { - return builder.GenericTypeParams[Index]->getDeclaredInterfaceType(); + Type build(BuiltinFunctionBuilder &builder) const { + return builder.TheGenericParamList->getParams()[Index] + ->getDeclaredInterfaceType(); } }; struct LambdaGenerator { - std::function TheFunction; - Type build(BuiltinGenericSignatureBuilder &builder) const { + std::function TheFunction; + Type build(BuiltinFunctionBuilder &builder) const { return TheFunction(builder); } }; @@ -521,28 +498,28 @@ namespace { struct MetatypeGenerator { T Object; Optional Repr; - Type build(BuiltinGenericSignatureBuilder &builder) const { + Type build(BuiltinFunctionBuilder &builder) const { return MetatypeType::get(Object.build(builder), Repr); } }; }; } // end anonymous namespace -static BuiltinGenericSignatureBuilder::ConcreteGenerator +static BuiltinFunctionBuilder::ConcreteGenerator makeConcrete(Type type) { return { type }; } -static BuiltinGenericSignatureBuilder::ParameterGenerator +static BuiltinFunctionBuilder::ParameterGenerator makeGenericParam(unsigned index = 0) { return { index }; } template -static BuiltinGenericSignatureBuilder::LambdaGenerator +static BuiltinFunctionBuilder::LambdaGenerator makeTuple(const Gs & ...elementGenerators) { return { - [=](BuiltinGenericSignatureBuilder &builder) -> Type { + [=](BuiltinFunctionBuilder &builder) -> Type { TupleTypeElt elts[] = { elementGenerators.build(builder)... }; @@ -552,11 +529,11 @@ makeTuple(const Gs & ...elementGenerators) { } template -static BuiltinGenericSignatureBuilder::LambdaGenerator +static BuiltinFunctionBuilder::LambdaGenerator makeBoundGenericType(NominalTypeDecl *decl, const Gs & ...argumentGenerators) { return { - [=](BuiltinGenericSignatureBuilder &builder) -> Type { + [=](BuiltinFunctionBuilder &builder) -> Type { Type args[] = { argumentGenerators.build(builder)... }; @@ -566,28 +543,28 @@ makeBoundGenericType(NominalTypeDecl *decl, } template -static BuiltinGenericSignatureBuilder::MetatypeGenerator +static BuiltinFunctionBuilder::MetatypeGenerator makeMetatype(const T &object, Optional repr = None) { return { object, repr }; } /// Create a function with type T -> (). static ValueDecl *getRefCountingOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam()); builder.setResult(makeConcrete(TupleType::getEmpty(Context))); return builder.build(Id); } static ValueDecl *getLoadOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.setResult(makeGenericParam()); return builder.build(Id); } static ValueDecl *getStoreOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam(), ValueOwnership::Owned); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.setResult(makeConcrete(TupleType::getEmpty(Context))); @@ -595,7 +572,7 @@ static ValueDecl *getStoreOperation(ASTContext &Context, Identifier Id) { } static ValueDecl *getDestroyOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.setResult(makeConcrete(TupleType::getEmpty(Context))); @@ -605,7 +582,7 @@ static ValueDecl *getDestroyOperation(ASTContext &Context, Identifier Id) { static ValueDecl *getDestroyArrayOperation(ASTContext &Context, Identifier Id) { auto wordType = BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(), Context); - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(wordType)); @@ -616,7 +593,7 @@ static ValueDecl *getDestroyArrayOperation(ASTContext &Context, Identifier Id) { static ValueDecl *getTransferArrayOperation(ASTContext &Context, Identifier Id){ auto wordType = BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(), Context); - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(Context.TheRawPointerType)); @@ -629,14 +606,14 @@ static ValueDecl *getIsUniqueOperation(ASTContext &Context, Identifier Id) { // (@inout T) -> Int1 Type Int1Ty = BuiltinIntegerType::get(1, Context); - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam(), ValueOwnership::InOut); builder.setResult(makeConcrete(Int1Ty)); return builder.build(Id); } static ValueDecl *getBindMemoryOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(BuiltinIntegerType::getWordType(Context))); builder.addParameter(makeMetatype(makeGenericParam())); @@ -650,7 +627,7 @@ static ValueDecl *getAllocWithTailElemsOperation(ASTContext &Context, if (NumTailTypes < 1 || 1 + NumTailTypes > (int)llvm::array_lengthof(GenericParamNames)) return nullptr; - BuiltinGenericSignatureBuilder builder(Context, 1 + NumTailTypes); + BuiltinFunctionBuilder builder(Context, 1 + NumTailTypes); builder.addParameter(makeMetatype(makeGenericParam(0))); for (int Idx = 0; Idx < NumTailTypes; ++Idx) { builder.addParameter(makeConcrete(BuiltinIntegerType::getWordType(Context))); @@ -662,7 +639,7 @@ static ValueDecl *getAllocWithTailElemsOperation(ASTContext &Context, static ValueDecl *getProjectTailElemsOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context, 2); + BuiltinFunctionBuilder builder(Context, 2); builder.addParameter(makeGenericParam(0)); builder.addParameter(makeMetatype(makeGenericParam(1))); builder.setResult(makeConcrete(Context.TheRawPointerType)); @@ -672,7 +649,7 @@ static ValueDecl *getProjectTailElemsOperation(ASTContext &Context, /// Build a getelementptr operation declaration. static ValueDecl *getGepOperation(ASTContext &Context, Identifier Id, Type ArgType) { - BuiltinGenericSignatureBuilder builder(Context, 1); + BuiltinFunctionBuilder builder(Context, 1); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(ArgType)); builder.addParameter(makeMetatype(makeGenericParam(0))); @@ -682,7 +659,7 @@ static ValueDecl *getGepOperation(ASTContext &Context, Identifier Id, static ValueDecl *getGetTailAddrOperation(ASTContext &Context, Identifier Id, Type ArgType) { - BuiltinGenericSignatureBuilder builder(Context, 2); + BuiltinFunctionBuilder builder(Context, 2); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(ArgType)); builder.addParameter(makeMetatype(makeGenericParam(0))); @@ -693,7 +670,7 @@ static ValueDecl *getGetTailAddrOperation(ASTContext &Context, Identifier Id, static ValueDecl *getBeginUnpairedAccessOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeMetatype(makeGenericParam(0))); @@ -704,7 +681,7 @@ static ValueDecl *getBeginUnpairedAccessOperation(ASTContext &Context, static ValueDecl * getPerformInstantaneousReadAccessOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeConcrete(Context.TheRawPointerType)); builder.addParameter(makeMetatype(makeGenericParam(0))); builder.setResult(makeConcrete(Context.TheEmptyTupleType)); @@ -718,35 +695,35 @@ static ValueDecl *getEndUnpairedAccessOperation(ASTContext &Context, } static ValueDecl *getSizeOrAlignOfOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::getWordType(Context))); return builder.build(Id); } static ValueDecl *getIsPODOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context))); return builder.build(Id); } static ValueDecl *getIsConcrete(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context))); return builder.build(Id); } static ValueDecl *getIsBitwiseTakable(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context))); return builder.build(Id); } static ValueDecl *getIsOptionalOperation(ASTContext &Context, Identifier Id) { - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context))); return builder.build(Id); @@ -833,7 +810,7 @@ static ValueDecl *getNativeObjectCast(ASTContext &Context, Identifier Id, llvm_unreachable("unexpected kind"); } - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); if (BV == BuiltinValueKind::CastToNativeObject || BV == BuiltinValueKind::UnsafeCastToNativeObject || BV == BuiltinValueKind::BridgeToRawPointer) { @@ -850,7 +827,7 @@ static ValueDecl *getCastToBridgeObjectOperation(ASTContext &C, Identifier Id) { auto wordType = BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(), C); - BuiltinGenericSignatureBuilder builder(C); + BuiltinFunctionBuilder builder(C); builder.addParameter(makeGenericParam(), ValueOwnership::Owned); builder.addParameter(makeConcrete(wordType)); builder.setResult(makeConcrete(C.TheBridgeObjectType)); @@ -863,7 +840,7 @@ static ValueDecl *getCastFromBridgeObjectOperation(ASTContext &C, Type BridgeTy = C.TheBridgeObjectType; switch (BV) { case BuiltinValueKind::CastReferenceFromBridgeObject: { - BuiltinGenericSignatureBuilder builder(C); + BuiltinFunctionBuilder builder(C); builder.addParameter(makeConcrete(BridgeTy), ValueOwnership::Owned); builder.setResult(makeGenericParam()); return builder.build(Id); @@ -892,7 +869,7 @@ static ValueDecl *getClassifyBridgeObject(ASTContext &C, Identifier Id) { } static ValueDecl *getValueToBridgeObject(ASTContext &C, Identifier Id) { - BuiltinGenericSignatureBuilder builder(C); + BuiltinFunctionBuilder builder(C); builder.addParameter(makeGenericParam(0)); builder.setResult(makeConcrete(C.TheBridgeObjectType)); return builder.build(Id); @@ -901,7 +878,7 @@ static ValueDecl *getValueToBridgeObject(ASTContext &C, Identifier Id) { static ValueDecl *getUnsafeGuaranteed(ASTContext &C, Identifier Id) { // T -> (T, Int8Ty) // - BuiltinGenericSignatureBuilder builder(C); + BuiltinFunctionBuilder builder(C); auto T = makeGenericParam(); builder.addParameter(T); Type Int8Ty = BuiltinIntegerType::get(8, C); @@ -923,7 +900,7 @@ static ValueDecl *getCastReferenceOperation(ASTContext &ctx, Identifier name) { // T -> U // SILGen and IRGen check additional constraints during lowering. - BuiltinGenericSignatureBuilder builder(ctx, 2); + BuiltinFunctionBuilder builder(ctx, 2); builder.addParameter(makeGenericParam(0), ValueOwnership::Owned); builder.setResult(makeGenericParam(1)); return builder.build(name); @@ -933,7 +910,7 @@ static ValueDecl *getReinterpretCastOperation(ASTContext &ctx, Identifier name) { // T -> U // SILGen and IRGen check additional constraints during lowering. - BuiltinGenericSignatureBuilder builder(ctx, 2); + BuiltinFunctionBuilder builder(ctx, 2); builder.addParameter(makeGenericParam(0), ValueOwnership::Owned); builder.setResult(makeGenericParam(1)); return builder.build(name); @@ -942,7 +919,7 @@ static ValueDecl *getReinterpretCastOperation(ASTContext &ctx, static ValueDecl *getZeroInitializerOperation(ASTContext &Context, Identifier Id) { // () -> T - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.setResult(makeGenericParam()); return builder.build(Id); } @@ -950,7 +927,7 @@ static ValueDecl *getZeroInitializerOperation(ASTContext &Context, static ValueDecl *getGetObjCTypeEncodingOperation(ASTContext &Context, Identifier Id) { // T.Type -> RawPointer - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(Context.TheRawPointerType)); return builder.build(Id); @@ -973,7 +950,7 @@ static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) { static ValueDecl *getTSanInoutAccess(ASTContext &Context, Identifier Id) { // T -> () - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam()); builder.setResult(makeConcrete(Context.TheEmptyTupleType)); return builder.build(Id); @@ -981,7 +958,7 @@ static ValueDecl *getTSanInoutAccess(ASTContext &Context, Identifier Id) { static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) { // (@inout T) -> RawPointer - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam(), ValueOwnership::InOut); builder.setResult(makeConcrete(Context.TheRawPointerType)); return builder.build(Id); @@ -990,7 +967,7 @@ static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) { static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context, Identifier Id) { // (T) -> RawPointer - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeGenericParam()); builder.setResult(makeConcrete(Context.TheRawPointerType)); return builder.build(Id); @@ -998,7 +975,7 @@ static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context, static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) { // (T.Type, U.Type) -> V.Type - BuiltinGenericSignatureBuilder builder(Context, 3); + BuiltinFunctionBuilder builder(Context, 3); builder.addParameter(makeMetatype(makeGenericParam(0))); builder.addParameter(makeMetatype(makeGenericParam(1))); builder.setResult(makeMetatype(makeGenericParam(2))); @@ -1008,7 +985,7 @@ static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) { static ValueDecl *getTypeJoinInoutOperation(ASTContext &Context, Identifier Id) { // (inout T, U.Type) -> V.Type - BuiltinGenericSignatureBuilder builder(Context, 3); + BuiltinFunctionBuilder builder(Context, 3); builder.addParameter(makeGenericParam(0), ValueOwnership::InOut); builder.addParameter(makeMetatype(makeGenericParam(1))); builder.setResult(makeMetatype(makeGenericParam(2))); @@ -1017,7 +994,7 @@ static ValueDecl *getTypeJoinInoutOperation(ASTContext &Context, static ValueDecl *getTypeJoinMetaOperation(ASTContext &Context, Identifier Id) { // (T.Type, U.Type) -> V.Type - BuiltinGenericSignatureBuilder builder(Context, 3); + BuiltinFunctionBuilder builder(Context, 3); builder.addParameter(makeMetatype(makeGenericParam(0))); builder.addParameter(makeMetatype(makeGenericParam(1))); builder.setResult(makeMetatype(makeGenericParam(2))); @@ -1033,7 +1010,7 @@ static ValueDecl *getTriggerFallbackDiagnosticOperation(ASTContext &Context, static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context, Identifier Id) { // T.Type -> Builtin.Int8 - BuiltinGenericSignatureBuilder builder(Context); + BuiltinFunctionBuilder builder(Context); builder.addParameter(makeMetatype(makeGenericParam())); builder.setResult(makeConcrete(BuiltinIntegerType::get(8, Context))); return builder.build(Id); @@ -1062,7 +1039,7 @@ static ValueDecl *getAssertConfOperation(ASTContext &C, Identifier Id) { static ValueDecl *getFixLifetimeOperation(ASTContext &C, Identifier Id) { // T -> () - BuiltinGenericSignatureBuilder builder(C); + BuiltinFunctionBuilder builder(C); builder.addParameter(makeGenericParam()); builder.setResult(makeConcrete(TupleType::getEmpty(C))); return builder.build(Id); @@ -1177,7 +1154,7 @@ static ValueDecl *getOnceOperation(ASTContext &Context, static ValueDecl *getPolymorphicBinaryOperation(ASTContext &ctx, Identifier id) { - BuiltinGenericSignatureBuilder builder(ctx); + BuiltinFunctionBuilder builder(ctx); builder.addParameter(makeGenericParam()); builder.addParameter(makeGenericParam()); builder.setResult(makeGenericParam()); diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 6def9d8b5b96e..45a6911b110b7 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -1,8 +1,4 @@ -if (SWIFT_FORCE_OPTIMIZED_TYPECHECKER) - set(EXTRA_AST_FLAGS "FORCE_BUILD_OPTIMIZED") -endif() - if(SWIFT_BUILD_ONLY_SYNTAXPARSERLIB) set(SWIFTAST_LLVM_LINK_COMPONENTS) else() @@ -29,11 +25,13 @@ add_swift_host_library(swiftAST STATIC ASTVerifier.cpp ASTWalker.cpp Attr.cpp - IndexSubset.cpp + AutoDiff.cpp Availability.cpp AvailabilitySpec.cpp Builtins.cpp CaptureInfo.cpp + ClangSwiftTypeCorrespondence.cpp + ClangTypeConverter.cpp ConcreteDeclRef.cpp ConformanceLookupTable.cpp Decl.cpp @@ -44,14 +42,16 @@ add_swift_host_library(swiftAST STATIC DiagnosticList.cpp DocComment.cpp Evaluator.cpp - ExperimentalDependencies.cpp - ExperimentalDependenciesSourceFileDepGraphConstructor.cpp Expr.cpp + FineGrainedDependencies.cpp + FineGrainedDependenciesSourceFileDepGraphConstructor.cpp GenericEnvironment.cpp GenericSignature.cpp GenericSignatureBuilder.cpp Identifier.cpp ImportCache.cpp + IncrementalRanges.cpp + IndexSubset.cpp InlinableText.cpp LayoutConstraint.cpp Module.cpp @@ -83,9 +83,15 @@ add_swift_host_library(swiftAST STATIC LLVM_LINK_COMPONENTS ${SWIFTAST_LLVM_LINK_COMPONENTS} - - ${EXTRA_AST_FLAGS} ) + +if(SWIFT_FORCE_OPTIMIZED_TYPECHECKER) + if(CMAKE_CXX_COMPILER_ID STREQUAL MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL MSVC) + target_compile_options(swiftAST PRIVATE /O2 /Ob2) + else() + target_compile_options(swiftAST PRIVATE -O3) + endif() +endif() if(NOT SWIFT_BUILD_ONLY_SYNTAXPARSERLIB) target_link_libraries(swiftAST INTERFACE clangCodeGen diff --git a/lib/AST/CaptureInfo.cpp b/lib/AST/CaptureInfo.cpp index ea641857d8693..382db84fe7170 100644 --- a/lib/AST/CaptureInfo.cpp +++ b/lib/AST/CaptureInfo.cpp @@ -57,7 +57,7 @@ CaptureInfo CaptureInfo::empty() { bool CaptureInfo::hasLocalCaptures() const { for (auto capture : getCaptures()) - if (capture.getDecl()->getDeclContext()->isLocalContext()) + if (capture.getDecl()->isLocalCapture()) return true; return false; } @@ -71,14 +71,14 @@ getLocalCaptures(SmallVectorImpl &Result) const { // Filter out global variables. for (auto capture : getCaptures()) { - if (!capture.getDecl()->getDeclContext()->isLocalContext()) + if (!capture.getDecl()->isLocalCapture()) continue; Result.push_back(capture); } } -void CaptureInfo::dump() const { +LLVM_ATTRIBUTE_USED void CaptureInfo::dump() const { print(llvm::errs()); llvm::errs() << '\n'; } diff --git a/lib/AST/ClangSwiftTypeCorrespondence.cpp b/lib/AST/ClangSwiftTypeCorrespondence.cpp new file mode 100644 index 0000000000000..bebe31ed1463d --- /dev/null +++ b/lib/AST/ClangSwiftTypeCorrespondence.cpp @@ -0,0 +1,27 @@ +//- ClangSwiftTypeCorrespondence.cpp - Relations between Clang & Swift types -// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// See description in ClangSwiftTypeCorrespondence.h. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ClangSwiftTypeCorrespondence.h" +#include "clang/AST/Type.h" + +bool swift::canImportAsOptional(const clang::Type *type) { + // Note: this mimics ImportHint::canImportAsOptional. + + // Includes CoreFoundation types such as CFStringRef (== struct CFString *). + return type && (type->isPointerType() + || type->isBlockPointerType() + || type->isObjCObjectPointerType()); +} diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp new file mode 100644 index 0000000000000..56b07d0dcd17b --- /dev/null +++ b/lib/AST/ClangTypeConverter.cpp @@ -0,0 +1,728 @@ +//===--- ClangTypeConverter.cpp - Convert Swift types to C types ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements generation of Clang AST types from Swift AST types for +// types that are representable in Objective-C interfaces. +// Large chunks of the code are lightly modified versions of the code in +// IRGen/GenClangType.cpp (which should eventually go away), so make sure +// to keep the two in sync. +// The three major differences are that, in this file: +// 1. We fail gracefully instead of asserting/UB. +// 2. We try to keep clang sugar instead of discarding it. +// 3. We use getAs instead of cast as we handle Swift types with sugar. +// +//===----------------------------------------------------------------------===// + +#include "ClangTypeConverter.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/ClangSwiftTypeCorrespondence.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/AST/Module.h" +#include "swift/AST/Type.h" +#include "swift/AST/TypeVisitor.h" +#include "swift/Basic/LLVM.h" + +#include "clang/AST/ASTContext.h" +#include "clang/Sema/Sema.h" + +using namespace swift; + +namespace { + +static Type getNamedSwiftType(ModuleDecl *stdlib, StringRef name) { + auto &ctx = stdlib->getASTContext(); + SmallVector results; + stdlib->lookupValue(ctx.getIdentifier(name), NLKind::QualifiedLookup, + results); + + // If we have one single type decl, and that decl has been + // type-checked, return its declared type. + // + // ...non-type-checked types should only ever show up here because + // of test cases using -enable-source-import, but unfortunately + // that's a real thing. + if (results.size() == 1) { + if (auto typeDecl = dyn_cast(results[0])) + return typeDecl->getDeclaredInterfaceType(); + } + return Type(); +} + +static clang::QualType +getClangBuiltinTypeFromKind(const clang::ASTContext &context, + clang::BuiltinType::Kind kind) { + switch (kind) { +#define BUILTIN_TYPE(Id, SingletonId) \ + case clang::BuiltinType::Id: \ + return context.SingletonId; +#include "clang/AST/BuiltinTypes.def" +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case clang::BuiltinType::Id: \ + return context.SingletonId; +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case clang::BuiltinType::Id: \ + return context.Id##Ty; +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case clang::BuiltinType::Id: \ + return context.SingletonId; +#include "clang/Basic/AArch64SVEACLETypes.def" + } + + // Not a valid BuiltinType. + return clang::QualType(); +} + +static clang::QualType getClangSelectorType( + const clang::ASTContext &clangCtx) { + return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy); +} + +static clang::QualType getClangMetatypeType( + const clang::ASTContext &clangCtx) { + clang::QualType clangType = + clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, nullptr, 0); + return clangCtx.getObjCObjectPointerType(clangType); +} + +static clang::QualType getClangIdType( + const clang::ASTContext &clangCtx) { + clang::QualType clangType = + clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, nullptr, 0); + return clangCtx.getObjCObjectPointerType(clangType); +} + +static clang::QualType getClangDecayedVaListType( +const clang::ASTContext &clangCtx) { + clang::QualType clangType = clangCtx.getBuiltinVaListType(); + if (clangType->isConstantArrayType()) + clangType = clangCtx.getDecayedType(clangType); + return clangType; +} + +} // end anonymous namespace + +const clang::Type *ClangTypeConverter::getFunctionType( + ArrayRef params, Type resultTy, + AnyFunctionType::Representation repr) { + + auto resultClangTy = convert(resultTy); + if (resultClangTy.isNull()) + return nullptr; + + SmallVector extParamInfos; + SmallVector paramsClangTy; + bool someParamIsConsumed = false; + for (auto p : params) { + auto pc = convert(p.getPlainType()); + if (pc.isNull()) + return nullptr; + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + if (p.getParameterFlags().isOwned()) { + someParamIsConsumed = true; + extParamInfo = extParamInfo.withIsConsumed(true); + } + extParamInfos.push_back(extParamInfo); + paramsClangTy.push_back(pc); + } + + clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); + if (someParamIsConsumed) + info.ExtParameterInfos = extParamInfos.begin(); + auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); + if (fn.isNull()) + return nullptr; + + switch (repr) { + case AnyFunctionType::Representation::CFunctionPointer: + return ClangASTContext.getPointerType(fn).getTypePtr(); + case AnyFunctionType::Representation::Block: + return ClangASTContext.getBlockPointerType(fn).getTypePtr(); + case AnyFunctionType::Representation::Swift: + case AnyFunctionType::Representation::Thin: + llvm_unreachable("Expected a C-compatible representation."); + } +} + +clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, + StringRef memberName) { + auto memberTypeDecl = cast( + DC->lookupDirect(Context.getIdentifier(memberName))[0]); + auto memberType = memberTypeDecl->getDeclaredInterfaceType(); + return convert(memberType); +} + +// TODO: It is unfortunate that we parse the name of a public library type +// in order to break it down into a vector component and length that in theory +// we could recover in some other way. +static clang::QualType getClangVectorType(const clang::ASTContext &ctx, + clang::BuiltinType::Kind eltKind, + clang::VectorType::VectorKind vecKind, + StringRef numEltsString) { + unsigned numElts; + bool failedParse = numEltsString.getAsInteger(10, numElts); + if (failedParse) + return clang::QualType(); + auto eltTy = getClangBuiltinTypeFromKind(ctx, eltKind); + if (eltTy.isNull()) + return clang::QualType(); + return ctx.getVectorType(eltTy, numElts, vecKind); +} + +clang::QualType ClangTypeConverter::visitStructType(StructType *type) { + auto &ctx = ClangASTContext; + + auto swiftDecl = type->getDecl(); + StringRef name = swiftDecl->getName().str(); + + // We assume that the importer translates all of the following types + // directly to structs in the standard library. + + // We want to recognize most of these types by name. +#define CHECK_NAMED_TYPE(NAME, CLANG_TYPE) do { \ + if (name == (NAME)) return CLANG_TYPE; \ + } while (false) + + CHECK_NAMED_TYPE("CGFloat", convertMemberType(swiftDecl, "NativeType")); + CHECK_NAMED_TYPE("OpaquePointer", ctx.VoidPtrTy); + CHECK_NAMED_TYPE("CVaListPointer", getClangDecayedVaListType(ctx)); + CHECK_NAMED_TYPE("DarwinBoolean", ctx.UnsignedCharTy); + CHECK_NAMED_TYPE(swiftDecl->getASTContext().getSwiftName( + KnownFoundationEntity::NSZone), + ctx.VoidPtrTy); + CHECK_NAMED_TYPE("WindowsBool", ctx.IntTy); + CHECK_NAMED_TYPE("ObjCBool", ctx.ObjCBuiltinBoolTy); + CHECK_NAMED_TYPE("Selector", getClangSelectorType(ctx)); + CHECK_NAMED_TYPE("UnsafeRawPointer", ctx.VoidPtrTy); + CHECK_NAMED_TYPE("UnsafeMutableRawPointer", ctx.VoidPtrTy); +#undef CHECK_NAMED_TYPE + + // Map vector types to the corresponding C vectors. +#define MAP_SIMD_TYPE(TYPE_NAME, _, BUILTIN_KIND) \ + if (name.startswith(#TYPE_NAME)) { \ + return getClangVectorType(ctx, clang::BuiltinType::BUILTIN_KIND, \ + clang::VectorType::GenericVector, \ + name.drop_front(sizeof(#TYPE_NAME)-1)); \ + } +#include "swift/ClangImporter/SIMDMappedTypes.def" + + // We might be looking at a builtin + auto ret = reverseBuiltinTypeMapping(type); + if (!ret.isNull()) + return ret; + + if (type->isPotentiallyBridgedValueType()) { + if (auto t = Context.getBridgedToObjC(type->getDecl(), type)) + return convert(t); + } + + // Out of ideas, there must've been some error. :( + return clang::QualType(); +} + +static clang::QualType +getClangBuiltinTypeFromTypedef(clang::Sema &sema, StringRef typedefName) { + auto &context = sema.getASTContext(); + auto identifier = &context.Idents.get(typedefName); + auto found = sema.LookupSingleName(sema.TUScope, identifier, + clang::SourceLocation(), + clang::Sema::LookupOrdinaryName); + auto typedefDecl = dyn_cast_or_null(found); + if (!typedefDecl) + return clang::QualType(); + + auto underlyingTy = + context.getCanonicalType(typedefDecl->getUnderlyingType()); + + if (underlyingTy->getAs()) + return underlyingTy; + return clang::QualType(); +} + +clang::QualType +ClangTypeConverter::reverseBuiltinTypeMapping(StructType *type) { + // Handle builtin types by adding entries to the cache that reverse + // the mapping done by the importer. We could try to look at the + // members of the struct instead, but even if that's ABI-equivalent + // (which it had better be!), it might erase interesting semantic + // differences like integers vs. characters. This is important + // because CC lowering isn't the only purpose of this conversion. + // + // The importer maps builtin types like 'int' to named types like + // 'CInt', which are generally typealiases. So what we do here is + // map the underlying types of those typealiases back to the builtin + // type. These typealiases frequently create a many-to-one mapping, + // so just use the first type that mapped to a particular underlying + // type. + // + // This is the last thing that happens before asserting that the + // struct type doesn't have a mapping. Furthermore, all of the + // builtin types are pre-built in the clang ASTContext. So it's not + // really a significant performance problem to just cache all them + // right here; it makes making a few more entries in the cache than + // we really need, but it also means we won't end up repeating these + // stdlib lookups multiple times, and we have to perform multiple + // lookups anyway because the MAP_BUILTIN_TYPE database uses + // typealias names (like 'CInt') that aren't obviously associated + // with the underlying C library type. + + auto stdlib = Context.getStdlibModule(); + assert(stdlib && "translating stdlib type to C without stdlib module?"); + auto &ctx = ClangASTContext; + + if (!StdlibTypesAreCached) { + auto cacheStdlibType = [&](StringRef swiftName, + clang::BuiltinType::Kind builtinKind) { + Type swiftType = getNamedSwiftType(stdlib, swiftName); + if (!swiftType) return; + + auto &sema = Context.getClangModuleLoader()->getClangSema(); + + if (Context.LangOpts.EnableObjCInterop) { + // Handle Int and UInt specially. On Apple platforms, these map to + // the NSInteger and NSUInteger typedefs. So try that if the typedefs + // are available, to ensure we get consistent ObjC @encode strings. + if (swiftType->getAnyNominal() == Context.getIntDecl()) { + auto NSIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSInteger"); + if (!NSIntegerTy.isNull()) { + Cache.insert({swiftType->getCanonicalType(), NSIntegerTy}); + return; + } + } else if (swiftType->getAnyNominal() == Context.getUIntDecl()) { + auto NSUIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSUInteger"); + if (!NSUIntegerTy.isNull()) { + Cache.insert({swiftType->getCanonicalType(), NSUIntegerTy}); + return; + } + } + } + + // For something like `typealias CInt = Int32`, reverseBuiltinTypeMapping + // will get Int32 as the input, so we need to record the desugared type. + Cache.insert({swiftType->getCanonicalType(), + getClangBuiltinTypeFromKind(ctx, builtinKind)}); + }; + +#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ + cacheStdlibType(#SWIFT_TYPE_NAME, clang::BuiltinType::CLANG_BUILTIN_KIND); +#include "swift/ClangImporter/BuiltinMappedTypes.def" + + // On 64-bit Windows, no C type is imported as an Int or UInt; CLong is + // imported as an Int32 and CLongLong as an Int64. Therefore, manually + // add mappings to C for Int and UInt. + // On 64-bit Cygwin, no manual mapping is required. + if (Triple.isOSWindows() && Triple.isArch64Bit() + && !Triple.isWindowsCygwinEnvironment()) { + // Map UInt to uintptr_t + auto swiftUIntType = getNamedSwiftType(stdlib, "UInt"); + auto clangUIntPtrType = ctx.getCanonicalType(ctx.getUIntPtrType()); + Cache.insert({swiftUIntType, clangUIntPtrType}); + + // Map Int to intptr_t + auto swiftIntType = getNamedSwiftType(stdlib, "Int"); + auto clangIntPtrType = ctx.getCanonicalType(ctx.getIntPtrType()); + Cache.insert({swiftIntType, clangIntPtrType}); + } + StdlibTypesAreCached = true; + } + + auto it = Cache.find(Type(type)); + if (it != Cache.end()) + return it->second; + + it = Cache.find(type->getCanonicalType()); + if (it != Cache.end()) { + Cache.insert({Type(type), it->second}); + return it->second; + } + + return clang::QualType(); +} + +clang::QualType ClangTypeConverter::visitTupleType(TupleType *type) { + unsigned tupleNumElements = type->getNumElements(); + if (tupleNumElements == 0) + return ClangASTContext.VoidTy; + + Type eltTy = type->getElementType(0); + for (unsigned i = 1; i < tupleNumElements; i++) { + if (!eltTy->isEqual(type->getElementType(i))) + // Only tuples where all element types are equal map to fixed-size + // arrays. + return clang::QualType(); + } + + auto clangEltTy = convert(eltTy); + if (clangEltTy.isNull()) + return clang::QualType(); + + APInt size(32, tupleNumElements); + return ClangASTContext.getConstantArrayType(clangEltTy, size, + clang::ArrayType::Normal, 0); +} + +clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { + auto proto = type->getDecl(); + auto &clangCtx = ClangASTContext; + + if (!proto->isObjC()) + return clang::QualType(); + + assert(!cast_or_null(proto->getClangDecl()) + && "We shouldn't be creating duplicate decls; see `convert`"); + + // Single protocol -> id + clang::IdentifierInfo *name = &clangCtx.Idents.get(proto->getName().get()); + auto *PDecl = clang::ObjCProtocolDecl::Create( + const_cast(clangCtx), + clangCtx.getTranslationUnitDecl(), name, + clang::SourceLocation(), clang::SourceLocation(), nullptr); + + // Attach an objc_runtime_name attribute with the Objective-C name to use + // for this protocol. + SmallString<64> runtimeNameBuffer; + PDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( + PDecl->getASTContext(), + proto->getObjCRuntimeName(runtimeNameBuffer))); + + auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, + &PDecl, 1); + return clangCtx.getObjCObjectPointerType(clangType); +} + +// TODO: [stronger-checking-in-clang-type-conversion] +// Metatypes can be converted to Class when they are metatypes for concrete +// classes. https://github.com/apple/swift/pull/27479#discussion_r344418131 +clang::QualType ClangTypeConverter::visitMetatypeType(MetatypeType *type) { + return getClangMetatypeType(ClangASTContext); +} + +// TODO: [stronger-checking-in-clang-type-conversion] +// Existential metatypes where the base is a non-metatype existential can be +// converted to Class when the protocols are all ObjC. +// https://github.com/apple/swift/pull/27479#discussion_r344418131 +clang::QualType +ClangTypeConverter::visitExistentialMetatypeType(ExistentialMetatypeType *type) { + return getClangMetatypeType(ClangASTContext); +} + +clang::QualType ClangTypeConverter::visitClassType(ClassType *type) { + auto &clangCtx = ClangASTContext; + auto swiftDecl = type->getDecl(); + + // TODO: [non-objc-class-clang-type-conversion] + // See the corresponding note in GenClangType.cpp + if (!swiftDecl->isObjC()) + return getClangIdType(clangCtx); + + assert(!cast_or_null(swiftDecl->getClangDecl()) + && "We shouldn't be creating duplicate decls; see `convert`"); + + // produce the clang type INTF * if it is imported ObjC object. + clang::IdentifierInfo *ForwardClassId = + &clangCtx.Idents.get(swiftDecl->getName().get()); + auto *CDecl = clang::ObjCInterfaceDecl::Create( + clangCtx, clangCtx.getTranslationUnitDecl(), + clang::SourceLocation(), ForwardClassId, + /*typeParamList*/nullptr, /*PrevDecl=*/nullptr, + clang::SourceLocation()); + + // Attach an objc_runtime_name attribute with the Objective-C name to use + // for this class. + SmallString<64> runtimeNameBuffer; + CDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( + CDecl->getASTContext(), + swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); + + auto clangType = clangCtx.getObjCInterfaceType(CDecl); + return clangCtx.getObjCObjectPointerType(clangType); +} + +// TODO: We should try to preserve type arguments on imported ObjC generic +// classes, instead of relying on our knowledge of clang's encoding. +// This would entail extracting the type arguments, calling `convert` to +// create clang types, extracting the ObjCProtocolDecls and then using +// getObjCObjectType with `id` as the base. +clang::QualType +ClangTypeConverter::visitBoundGenericClassType(BoundGenericClassType *type) { + // Any @objc class type in Swift that shows up in an @objc method maps 1-1 to + // "id "; with clang's encoding ignoring the protocol list. + return getClangIdType(ClangASTContext); +} + +clang::QualType +ClangTypeConverter::visitBoundGenericType(BoundGenericType *type) { + // The only possibilities are *Pointer, SIMD* and Optional. + + if (type->getDecl()->isOptionalDecl()) { + auto args = type->getGenericArgs(); + assert((args.size() == 1) && "Optional should have 1 generic argument."); + clang::QualType innerTy = convert(args[0]); + if (swift::canImportAsOptional(innerTy.getTypePtrOrNull())) + return innerTy; + return clang::QualType(); + } + + auto swiftStructDecl = type->getDecl(); + + enum class StructKind { + Invalid, + UnsafeMutablePointer, + UnsafePointer, + AutoreleasingUnsafeMutablePointer, + Unmanaged, + CFunctionPointer, + SIMD, + } kind = llvm::StringSwitch(swiftStructDecl->getName().str()) + .Case("UnsafeMutablePointer", StructKind::UnsafeMutablePointer) + .Case("UnsafePointer", StructKind::UnsafePointer) + .Case("AutoreleasingUnsafeMutablePointer", + StructKind::AutoreleasingUnsafeMutablePointer) + .Case("Unmanaged", StructKind::Unmanaged) + .Case("CFunctionPointer", StructKind::CFunctionPointer) + .StartsWith("SIMD", StructKind::SIMD) + .Default(StructKind::Invalid); + + auto args = type->getGenericArgs(); + if (args.size() != 1) + // Must've got something other than *Pointer or SIMD* + return clang::QualType(); + auto argCanonicalTy = args[0]->getCanonicalType(); + + switch (kind) { + case StructKind::Invalid: + return clang::QualType(); + + case StructKind::UnsafeMutablePointer: + case StructKind::Unmanaged: + case StructKind::AutoreleasingUnsafeMutablePointer: { + auto clangTy = convert(argCanonicalTy); + if (clangTy.isNull()) + return clang::QualType(); + return ClangASTContext.getPointerType(clangTy); + } + case StructKind::UnsafePointer: { + return ClangASTContext.getPointerType(convert(argCanonicalTy).withConst()); + } + + case StructKind::CFunctionPointer: { + auto &clangCtx = ClangASTContext; + + clang::QualType functionTy; + if (isa(argCanonicalTy->getCanonicalType())) { + functionTy = convert(argCanonicalTy); + if (functionTy.isNull()) + return clang::QualType(); + } else { + // Fall back to void(). + functionTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); + } + return clangCtx.getPointerType(functionTy); + } + + case StructKind::SIMD: { + clang::QualType scalarTy = convert(argCanonicalTy); + auto numEltsString = swiftStructDecl->getName().str(); + numEltsString.consume_front("SIMD"); + unsigned numElts; + bool failedParse = numEltsString.getAsInteger(10, numElts); + if (failedParse) + return clang::QualType(); + (void) failedParse; + auto vectorTy = ClangASTContext.getVectorType(scalarTy, numElts, + clang::VectorType::VectorKind::GenericVector); + return vectorTy; + } + } + + llvm_unreachable("Not a valid StructKind."); +} + +clang::QualType ClangTypeConverter::visitEnumType(EnumType *type) { + // Special case: Uninhabited enums are not @objc, so we don't + // know what to do below, but we can just convert to 'void'. + if (type->isUninhabited()) + return convert(Context.TheEmptyTupleType); + + if (!type->getDecl()->isObjC()) + // Can't translate something not marked with @objc + return clang::QualType(); + + // @objc enums lower to their raw types. + return convert(type->getDecl()->getRawType()); +} + +clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { + // We must've already computed it before if applicable. + return clang::QualType(type->getClangFunctionType(), 0); +} + +clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { + llvm::report_fatal_error("Expected only AST types but found a SIL function."); +} + +clang::QualType +ClangTypeConverter::visitSILBlockStorageType(SILBlockStorageType *type) { + llvm::report_fatal_error("Expected only AST types but found a SIL block."); +} + +clang::QualType +ClangTypeConverter::visitProtocolCompositionType(ProtocolCompositionType *type) { + // Any will be lowered to AnyObject, so we return the same result. + if (type->isAny()) + return getClangIdType(ClangASTContext); + + auto &clangCtx = ClangASTContext; + + // FIXME. Eventually, this will have its own helper routine. + SmallVector Protocols; + auto layout = type->getExistentialLayout(); + if (!layout.isObjC()) + // Cannot represent opaque existential in Clang + return clang::QualType(); + + // AnyObject -> id. + if (layout.isAnyObject()) + return getClangIdType(ClangASTContext); + + auto superclassTy = clangCtx.ObjCBuiltinIdTy; + if (auto layoutSuperclassTy = layout.getSuperclass()) { + auto clangTy = convert(layoutSuperclassTy); + if (clangTy.isNull()) + return clang::QualType(); + superclassTy = clangCtx.getCanonicalType( + clangTy->getAs()->getPointeeType()); + } + + for (Type t : layout.getProtocols()) { + auto clangTy = convert(t); + if (clangTy.isNull()) + return clang::QualType(); + for (auto p : clangTy->getAs()->quals()) + Protocols.push_back(p); + } + + if (Protocols.empty()) + return superclassTy; + + // id + clang::ObjCProtocolDecl **ProtoQuals = + new(clangCtx) clang::ObjCProtocolDecl*[Protocols.size()]; + memcpy(ProtoQuals, Protocols.data(), + sizeof(clang::ObjCProtocolDecl*)*Protocols.size()); + auto clangType = clangCtx.getObjCObjectType(superclassTy, + ProtoQuals, + Protocols.size()); + return clangCtx.getObjCObjectPointerType(clangType); +} + +clang::QualType +ClangTypeConverter::visitBuiltinRawPointerType(BuiltinRawPointerType *type) { + return ClangASTContext.VoidPtrTy; +} + +clang::QualType +ClangTypeConverter::visitBuiltinIntegerType(BuiltinIntegerType *type) { + auto &clangCtx = ClangASTContext; + if (type->getWidth().isPointerWidth()) { + return clangCtx.getUIntPtrType(); + } + assert(type->getWidth().isFixedWidth()); + auto width = type->getWidth().getFixedWidth(); + if (width == 1) + return clangCtx.BoolTy; + return clangCtx.getIntTypeForBitwidth(width, /*signed*/ 0); +} + +clang::QualType +ClangTypeConverter::visitBuiltinFloatType(BuiltinFloatType *type) { + auto &clangCtx = ClangASTContext; + auto &clangTargetInfo = clangCtx.getTargetInfo(); + const llvm::fltSemantics *format = &type->getAPFloatSemantics(); + if (format == &clangTargetInfo.getHalfFormat()) + return clangCtx.HalfTy; + if (format == &clangTargetInfo.getFloatFormat()) + return clangCtx.FloatTy; + if (format == &clangTargetInfo.getDoubleFormat()) + return clangCtx.DoubleTy; + if (format == &clangTargetInfo.getLongDoubleFormat()) + return clangCtx.LongDoubleTy; + llvm_unreachable("cannot translate floating-point format to C"); +} + +clang::QualType ClangTypeConverter::visitArchetypeType(ArchetypeType *type) { + // We see these in the case where we invoke an @objc function + // through a protocol. + return getClangIdType(ClangASTContext); +} + +clang::QualType ClangTypeConverter::visitDynamicSelfType(DynamicSelfType *type) { + // Dynamic Self is equivalent to 'instancetype', which is treated as + // 'id' within the Objective-C type system. + return getClangIdType(ClangASTContext); +} + +clang::QualType +ClangTypeConverter::visitGenericTypeParamType(GenericTypeParamType *type) { + // We see these in the case where we invoke an @objc function + // through a protocol argument that is a generic type. + return getClangIdType(ClangASTContext); +} + +clang::QualType +ClangTypeConverter::visitSugarType(SugarType *type) { + return convert(Type(type->getDesugaredType())); +} + +clang::QualType +ClangTypeConverter::visitType(TypeBase *type) { + // We only convert specific types. + return clang::QualType(); +} + +clang::QualType ClangTypeConverter::visit(Type type) { + return static_cast(this)->visit(type); +} + +clang::QualType ClangTypeConverter::convert(Type type) { + auto it = Cache.find(type); + if (it != Cache.end()) + return it->second; + + // Try to do this without making cache entries for obvious cases. + if (auto nominal = type->getAs()) { + auto decl = nominal->getDecl(); + if (auto clangDecl = decl->getClangDecl()) { + auto &ctx = ClangASTContext; + if (auto clangTypeDecl = dyn_cast(clangDecl)) { + return ctx.getTypeDeclType(clangTypeDecl).getUnqualifiedType(); + } else if (auto ifaceDecl = dyn_cast(clangDecl)) { + auto clangType = ctx.getObjCInterfaceType(ifaceDecl); + return ctx.getObjCObjectPointerType(clangType); + } else if (auto protoDecl = dyn_cast(clangDecl)){ + auto clangType = ctx.getObjCObjectType( + ctx.ObjCBuiltinIdTy, + const_cast(&protoDecl), + 1); + return ctx.getObjCObjectPointerType(clangType); + } + } + } + + // If that failed, convert the type, cache, and return. + clang::QualType result = visit(type); + Cache.insert({type, result}); + return result; +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h new file mode 100644 index 0000000000000..b25f6b6dffb92 --- /dev/null +++ b/lib/AST/ClangTypeConverter.h @@ -0,0 +1,111 @@ +//===-- ClangTypeConverter.h - Converting Swift types to C types-*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines utilities for translating Swift types to C types. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_CLANG_TYPE_CONVERTER_H +#define SWIFT_AST_CLANG_TYPE_CONVERTER_H + +#include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/Type.h" +#include "swift/AST/TypeVisitor.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" + +namespace swift { + +/// Compute C types corresponding to Swift AST types. +class ClangTypeConverter : + public TypeVisitor { + + using super = TypeVisitor; + + llvm::DenseMap Cache; + + bool StdlibTypesAreCached = false; + + ASTContext &Context; + + clang::ASTContext &ClangASTContext; + + const llvm::Triple Triple; + + ClangTypeConverter(const ClangTypeConverter &) = delete; + ClangTypeConverter &operator=(const ClangTypeConverter &) = delete; + +public: + + /// Create a ClangTypeConverter. + ClangTypeConverter(ASTContext &ctx, clang::ASTContext &clangCtx, + llvm::Triple triple) + : Context(ctx), ClangASTContext(clangCtx), Triple(triple) + { + }; + + /// Compute the C function type for a Swift function type. + /// + /// It is the caller's responsibility to make sure this method is only + /// called in the appropriate context. For example, it makes sense to use + /// this method for the output type of a @convention(c) function. + /// + /// Since we do not check the context, the translation is unconditional. + /// For example, String will automatically get translated to NSString + /// when bridging is available. + /// + /// Additionally, the API is expected to be used only from + /// + /// \returns The appropriate clang type on success, nullptr on failure. + /// + /// Precondition: The representation argument must be C-compatible. + const clang::Type *getFunctionType( + ArrayRef params, Type resultTy, + AnyFunctionType::Representation repr); + +private: + clang::QualType convert(Type type); + clang::QualType convertMemberType(NominalTypeDecl *DC, + StringRef memberName); + + clang::QualType reverseBuiltinTypeMapping(StructType *type); + + friend TypeVisitor; + + clang::QualType visitStructType(StructType *type); + clang::QualType visitTupleType(TupleType *type); + clang::QualType visitMetatypeType(MetatypeType *type); + clang::QualType visitExistentialMetatypeType(ExistentialMetatypeType *type); + clang::QualType visitProtocolType(ProtocolType *type); + clang::QualType visitClassType(ClassType *type); + clang::QualType visitBoundGenericClassType(BoundGenericClassType *type); + clang::QualType visitBoundGenericType(BoundGenericType *type); + clang::QualType visitEnumType(EnumType *type); + clang::QualType visitFunctionType(FunctionType *type); + clang::QualType visitProtocolCompositionType(ProtocolCompositionType *type); + clang::QualType visitBuiltinRawPointerType(BuiltinRawPointerType *type); + clang::QualType visitBuiltinIntegerType(BuiltinIntegerType *type); + clang::QualType visitBuiltinFloatType(BuiltinFloatType *type); + clang::QualType visitArchetypeType(ArchetypeType *type); + clang::QualType visitSILFunctionType(SILFunctionType *type); + clang::QualType visitGenericTypeParamType(GenericTypeParamType *type); + clang::QualType visitDynamicSelfType(DynamicSelfType *type); + clang::QualType visitSILBlockStorageType(SILBlockStorageType *type); + clang::QualType visitSugarType(SugarType *type); + clang::QualType visitType(TypeBase *type); + clang::QualType visit(Type type); +}; + +} // end namespace swift + +#endif /* SWIFT_AST_CLANG_TYPE_CONVERTER_H */ diff --git a/lib/AST/ConcreteDeclRef.cpp b/lib/AST/ConcreteDeclRef.cpp index f828dbf17ff68..248d57cec00d1 100644 --- a/lib/AST/ConcreteDeclRef.cpp +++ b/lib/AST/ConcreteDeclRef.cpp @@ -45,7 +45,7 @@ ConcreteDeclRef ConcreteDeclRef::getOverriddenDecl() const { return ConcreteDeclRef(baseDecl, subs); } -void ConcreteDeclRef::dump(raw_ostream &os) { +void ConcreteDeclRef::dump(raw_ostream &os) const { if (!getDecl()) { os << "**NULL**"; return; @@ -61,6 +61,6 @@ void ConcreteDeclRef::dump(raw_ostream &os) { } } -void ConcreteDeclRef::dump() { +void ConcreteDeclRef::dump() const { dump(llvm::errs()); } diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index dbe519d83907a..414d3d8803984 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -89,7 +89,7 @@ void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os, if (Loc.isValid()) { os << " loc="; - Loc.dump(getProtocol()->getASTContext().SourceMgr); + Loc.print(os, getProtocol()->getASTContext().SourceMgr); } switch (getKind()) { @@ -140,7 +140,7 @@ void ConformanceLookupTable::destroy() { } namespace { - using ConformanceConstructionInfo = std::pair; + using ConformanceConstructionInfo = Located; } template @@ -194,14 +194,14 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { - protocols.push_back({SourceLoc(), conf->getProtocol()}); + protocols.push_back({conf->getProtocol(), SourceLoc()}); } } else if (next->getParentSourceFile()) { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { - if (auto proto = dyn_cast(found.second)) - protocols.push_back({found.first, proto}); + if (auto proto = dyn_cast(found.Item)) + protocols.push_back({proto, found.Loc}); } } @@ -281,7 +281,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) - addProtocol(locAndProto.second, locAndProto.first, source); + addProtocol(locAndProto.Item, locAndProto.Loc, source); }); break; @@ -470,8 +470,8 @@ void ConformanceLookupTable::addInheritedProtocols( bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.second)) - addProtocol(proto, found.first, source); + if (auto proto = dyn_cast(found.Item)) + addProtocol(proto, found.Loc, source); } } @@ -819,7 +819,7 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, // Form the inherited conformance. entry->Conformance = - ctx.getInheritedConformance(type, inheritedConformance->getConcrete()); + ctx.getInheritedConformance(type, inheritedConformance.getConcrete()); } else { // Create or find the normal conformance. Type conformingType = conformingDC->getDeclaredInterfaceType(); diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index bcd3f5cd961b1..a27c89f03e063 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -22,6 +22,7 @@ #include "swift/AST/DeclContext.h" #include "swift/AST/TypeLoc.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/SourceLoc.h" #include "llvm/ADT/MapVector.h" @@ -289,9 +290,7 @@ class ConformanceLookupTable { void *operator new(size_t Bytes, ASTContext &C, unsigned Alignment = alignof(ConformanceEntry)); - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os, unsigned indent = 0) const; }; @@ -478,9 +477,7 @@ class ConformanceLookupTable { return Mem; } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; void dump(raw_ostream &os) const; /// Compare two protocol conformances to place them in some canonical order. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 3b5acd6a4e2d2..8bdb94abff1c9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -331,6 +331,18 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { llvm_unreachable("bad DescriptiveDeclKind"); } +Optional +Decl::getIntroducedOSVersion(PlatformKind Kind) const { + for (auto *attr: getAttrs()) { + if (auto *ava = dyn_cast(attr)) { + if (ava->Platform == Kind && ava->Introduced) { + return ava->Introduced; + } + } + } + return None; +} + llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, StaticSpellingKind SSK) { switch (SSK) { @@ -376,6 +388,77 @@ DeclContext *Decl::getInnermostDeclContext() const { return getDeclContext(); } +bool Decl::isInvalid() const { + switch (getKind()) { +#define VALUE_DECL(ID, PARENT) +#define DECL(ID, PARENT) \ + case DeclKind::ID: +#include "swift/AST/DeclNodes.def" + return Bits.Decl.Invalid; + case DeclKind::Param: { + // Parameters are special because closure parameters may not have type + // annotations. In which case, the interface type request returns + // ErrorType. Therefore, consider parameters with implicit types to always + // be valid. + auto *PD = cast(this); + if (!PD->getTypeRepr() && !PD->hasInterfaceType()) + return false; + } + LLVM_FALLTHROUGH; + case DeclKind::Enum: + case DeclKind::Struct: + case DeclKind::Class: + case DeclKind::Protocol: + case DeclKind::OpaqueType: + case DeclKind::TypeAlias: + case DeclKind::GenericTypeParam: + case DeclKind::AssociatedType: + case DeclKind::Module: + case DeclKind::Var: + case DeclKind::Subscript: + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Func: + case DeclKind::Accessor: + case DeclKind::EnumElement: + return cast(this)->getInterfaceType()->hasError(); + } + + llvm_unreachable("Unknown decl kind"); +} + +void Decl::setInvalid() { + switch (getKind()) { +#define VALUE_DECL(ID, PARENT) +#define DECL(ID, PARENT) \ + case DeclKind::ID: +#include "swift/AST/DeclNodes.def" + Bits.Decl.Invalid = true; + return; + case DeclKind::Enum: + case DeclKind::Struct: + case DeclKind::Class: + case DeclKind::Protocol: + case DeclKind::OpaqueType: + case DeclKind::TypeAlias: + case DeclKind::GenericTypeParam: + case DeclKind::AssociatedType: + case DeclKind::Module: + case DeclKind::Var: + case DeclKind::Param: + case DeclKind::Subscript: + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Func: + case DeclKind::Accessor: + case DeclKind::EnumElement: + cast(this)->setInterfaceType(ErrorType::get(getASTContext())); + return; + } + + llvm_unreachable("Unknown decl kind"); +} + void Decl::setDeclContext(DeclContext *DC) { Context = DC; } @@ -410,6 +493,10 @@ template inline char checkSourceLocType(SourceLoc (Class::*)() const); inline TwoChars checkSourceLocType(SourceLoc (Decl::*)() const); +template +inline char checkSourceLocType(SourceLoc (Class::*)(bool) const); +inline TwoChars checkSourceLocType(SourceLoc (Decl::*)(bool) const); + template inline char checkSourceRangeType(SourceRange (Class::*)() const); inline TwoChars checkSourceRangeType(SourceRange (Decl::*)() const); @@ -455,8 +542,8 @@ SourceRange Decl::getSourceRangeIncludingAttrs() const { // Attributes on PatternBindingDecls are attached to VarDecls in AST. if (auto *PBD = dyn_cast(this)) { - for (auto Entry : PBD->getPatternList()) - Entry.getPattern()->forEachVariable([&](VarDecl *VD) { + for (auto i : range(PBD->getNumPatternEntries())) + PBD->getPattern(i)->forEachVariable([&](VarDecl *VD) { for (auto Attr : VD->getAttrs()) if (Attr->getRange().isValid()) Range.widen(Attr->getRangeWithAt()); @@ -482,19 +569,79 @@ case DeclKind::ID: return cast(this)->getLocFromSource(); llvm_unreachable("Unknown decl kind"); } -SourceLoc Decl::getLoc() const { +const Decl::CachedExternalSourceLocs *Decl::calculateSerializedLocs() const { + auto *File = cast(getDeclContext()->getModuleScopeContext()); + auto Locs = File->getBasicLocsForDecl(this); + if (!Locs.hasValue()) { + static const Decl::CachedExternalSourceLocs NullLocs{}; + return &NullLocs; + } + auto *Result = getASTContext().Allocate(); + auto &SM = getASTContext().SourceMgr; +#define CASE(X) \ +Result->X = SM.getLocFromExternalSource(Locs->SourceFilePath, Locs->X.Line, \ + Locs->X.Column); + CASE(Loc) + CASE(StartLoc) + CASE(EndLoc) +#undef CASE + return Result; +} + +StringRef Decl::getAlternateModuleName() const { + for (auto *Att: Attrs) { + if (auto *OD = dyn_cast(Att)) { + if (OD->isActivePlatform(getASTContext())) { + return OD->OriginalModuleName; + } + } + } + for (auto *DC = getDeclContext(); DC; DC = DC->getParent()) { + if (auto decl = DC->getAsDecl()) { + if (decl == this) + continue; + auto AM = decl->getAlternateModuleName(); + if (!AM.empty()) + return AM; + } + } + return StringRef(); +} + +SourceLoc Decl::getLoc(bool SerializedOK) const { #define DECL(ID, X) \ static_assert(sizeof(checkSourceLocType(&ID##Decl::getLoc)) == 2, \ #ID "Decl is re-defining getLoc()"); #include "swift/AST/DeclNodes.def" - return getLocFromSource(); + if (isa(this)) + return SourceLoc(); + // When the decl is context-free, we should get loc from source buffer. + if (!getDeclContext()) + return getLocFromSource(); + auto *File = cast(getDeclContext()->getModuleScopeContext()); + switch(File->getKind()) { + case FileUnitKind::Source: + return getLocFromSource(); + case FileUnitKind::SerializedAST: { + if (!SerializedOK) + return SourceLoc(); + if (!CachedLocs) { + CachedLocs = calculateSerializedLocs(); + } + return CachedLocs->Loc; + } + case FileUnitKind::Builtin: + case FileUnitKind::ClangModule: + case FileUnitKind::DWARFModule: + return SourceLoc(); + } } Expr *AbstractFunctionDecl::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); auto braceStmt = getBody(); assert(braceStmt != nullptr && "No body currently available."); - auto body = getBody()->getElement(0); + auto body = getBody()->getFirstElement(); if (auto *stmt = body.dyn_cast()) { if (auto *returnStmt = dyn_cast(stmt)) { return returnStmt->getResult(); @@ -511,7 +658,7 @@ Expr *AbstractFunctionDecl::getSingleExpressionBody() const { void AbstractFunctionDecl::setSingleExpressionBody(Expr *NewBody) { assert(hasSingleExpressionBody() && "Not a single-expression body"); - auto body = getBody()->getElement(0); + auto body = getBody()->getFirstElement(); if (auto *stmt = body.dyn_cast()) { if (auto *returnStmt = dyn_cast(stmt)) { returnStmt->setResult(NewBody); @@ -527,7 +674,7 @@ void AbstractFunctionDecl::setSingleExpressionBody(Expr *NewBody) { return; } } - getBody()->setElement(0, NewBody); + getBody()->setFirstElement(NewBody); } bool AbstractStorageDecl::isTransparent() const { @@ -562,6 +709,56 @@ bool ParameterList::hasInternalParameter(StringRef Prefix) const { return false; } +bool Decl::hasUnderscoredNaming() const { + const Decl *D = this; + if (const auto AFD = dyn_cast(D)) { + // If it's a function with a parameter with leading underscore, it's a + // private function. + if (AFD->getParameters()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto SubscriptD = dyn_cast(D)) { + if (SubscriptD->getIndices()->hasInternalParameter("_")) { + return true; + } + } + + if (const auto PD = dyn_cast(D)) { + if (PD->getAttrs().hasAttribute()) { + return false; + } + StringRef NameStr = PD->getNameStr(); + if (NameStr.startswith("_Builtin")) { + return true; + } + if (NameStr.startswith("_ExpressibleBy")) { + return true; + } + } + + if (const auto ImportD = dyn_cast(D)) { + if (const auto *Mod = ImportD->getModule()) { + if (Mod->isSwiftShimsModule()) { + return true; + } + } + } + + const auto VD = dyn_cast(D); + if (!VD || !VD->hasName()) { + return false; + } + + if (!VD->getBaseName().isSpecial() && + VD->getBaseName().getIdentifier().str().startswith("_")) { + return true; + } + + return false; +} + bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { const Decl *D = this; if (auto ExtD = dyn_cast(D)) { @@ -583,47 +780,12 @@ bool Decl::isPrivateStdlibDecl(bool treatNonBuiltinProtocolsAsPublic) const { FU->getKind() != FileUnitKind::SerializedAST) return false; - if (auto AFD = dyn_cast(D)) { - // If it's a function with a parameter with leading underscore, it's a - // private function. - if (AFD->getParameters()->hasInternalParameter("_")) - return true; - } - - if (auto SubscriptD = dyn_cast(D)) { - if (SubscriptD->getIndices()->hasInternalParameter("_")) - return true; - } - if (auto PD = dyn_cast(D)) { - if (PD->getAttrs().hasAttribute()) - return false; - StringRef NameStr = PD->getNameStr(); - if (NameStr.startswith("_Builtin")) - return true; - if (NameStr.startswith("_ExpressibleBy")) - return true; if (treatNonBuiltinProtocolsAsPublic) return false; } - if (auto ImportD = dyn_cast(D)) { - if (auto *Mod = ImportD->getModule()) { - if (Mod->isSwiftShimsModule()) - return true; - } - } - - auto VD = dyn_cast(D); - if (!VD || !VD->hasName()) - return false; - - // If the name has leading underscore then it's a private symbol. - if (!VD->getBaseName().isSpecial() && - VD->getBaseName().getIdentifier().str().startswith("_")) - return true; - - return false; + return hasUnderscoredNaming(); } AvailabilityContext Decl::getAvailabilityForLinkage() const { @@ -635,6 +797,10 @@ AvailabilityContext Decl::getAvailabilityForLinkage() const { if (auto *accessor = dyn_cast(this)) return accessor->getStorage()->getAvailabilityForLinkage(); + if (auto *ext = dyn_cast(this)) + if (auto *nominal = ext->getExtendedNominal()) + return nominal->getAvailabilityForLinkage(); + auto *dc = getDeclContext(); if (auto *ext = dyn_cast(dc)) return ext->getAvailabilityForLinkage(); @@ -656,6 +822,10 @@ bool Decl::isAlwaysWeakImported() const { if (auto *accessor = dyn_cast(this)) return accessor->getStorage()->isAlwaysWeakImported(); + if (auto *ext = dyn_cast(this)) + if (auto *nominal = ext->getExtendedNominal()) + return nominal->isAlwaysWeakImported(); + auto *dc = getDeclContext(); if (auto *ext = dyn_cast(dc)) return ext->isAlwaysWeakImported(); @@ -1106,16 +1276,21 @@ bool ExtensionDecl::hasValidParent() const { } bool ExtensionDecl::isConstrainedExtension() const { - // Non-generic extension. - if (!getGenericSignature()) + auto nominal = getExtendedNominal(); + if (!nominal) return false; - auto nominal = getExtendedNominal(); - assert(nominal); + auto typeSig = nominal->getGenericSignature(); + if (!typeSig) + return false; + + auto extSig = getGenericSignature(); + if (!extSig) + return false; // If the generic signature differs from that of the nominal type, it's a // constrained extension. - return !getGenericSignature()->isEqual(nominal->getGenericSignature()); + return !typeSig->isEqual(extSig); } bool ExtensionDecl::isEquivalentToExtendedContext() const { @@ -1141,9 +1316,11 @@ AccessLevel ExtensionDecl::getMaxAccessLevel() const { Type ExtensionDecl::getExtendedType() const { ASTContext &ctx = getASTContext(); - return evaluateOrDefault(ctx.evaluator, - ExtendedTypeRequest{const_cast(this)}, - ErrorType::get(ctx)); + if (auto type = evaluateOrDefault(ctx.evaluator, + ExtendedTypeRequest{const_cast(this)}, + Type())) + return type; + return ErrorType::get(ctx); } /// Clone the given generic parameters in the given list. We don't need any @@ -1400,7 +1577,9 @@ unsigned PatternBindingDecl::getPatternEntryIndexForVarDecl(const VarDecl *VD) c } Expr *PatternBindingEntry::getOriginalInit() const { - return InitContextAndIsText.getInt() ? nullptr : InitExpr.originalInit; + return InitContextAndFlags.getInt().contains(PatternFlags::IsText) + ? nullptr + : InitExpr.originalInit; } SourceRange PatternBindingEntry::getOriginalInitRange() const { @@ -1411,12 +1590,13 @@ SourceRange PatternBindingEntry::getOriginalInitRange() const { void PatternBindingEntry::setOriginalInit(Expr *E) { InitExpr.originalInit = E; - InitContextAndIsText.setInt(false); + InitContextAndFlags.setInt(InitContextAndFlags.getInt() - + PatternFlags::IsText); } -bool PatternBindingEntry::isInitialized() const { +bool PatternBindingEntry::isInitialized(bool onlyExplicit) const { // Directly initialized. - if (getInit()) + if (getInit() && (!onlyExplicit || getEqualLoc().isValid())) return true; // Initialized via a property wrapper. @@ -1437,7 +1617,8 @@ void PatternBindingEntry::setInit(Expr *E) { PatternAndFlags.setInt(F | Flags::Removed); } InitExpr.initAfterSynthesis = E; - InitContextAndIsText.setInt(false); + InitContextAndFlags.setInt(InitContextAndFlags.getInt() - + PatternFlags::IsText); } VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { @@ -1447,6 +1628,12 @@ VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { return variables[0]; } +unsigned PatternBindingEntry::getNumBoundVariables() const { + unsigned varCount = 0; + getPattern()->forEachVariable([&](VarDecl *) { ++varCount; }); + return varCount; +} + SourceLoc PatternBindingEntry::getLastAccessorEndLoc() const { SourceLoc lastAccessorEnd; getPattern()->forEachVariable([&](VarDecl *var) { @@ -1486,7 +1673,7 @@ SourceRange PatternBindingEntry::getSourceRange(bool omitAccessors) const { } bool PatternBindingEntry::hasInitStringRepresentation() const { - if (InitContextAndIsText.getInt()) + if (InitContextAndFlags.getInt().contains(PatternFlags::IsText)) return !InitStringRepresentation.empty(); return getInit() && getInit()->getSourceRange().isValid(); } @@ -1497,7 +1684,8 @@ StringRef PatternBindingEntry::getInitStringRepresentation( assert(hasInitStringRepresentation() && "must check if pattern has string representation"); - if (InitContextAndIsText.getInt() && !InitStringRepresentation.empty()) + if (InitContextAndFlags.getInt().contains(PatternFlags::IsText) && + !InitStringRepresentation.empty()) return InitStringRepresentation; auto &sourceMgr = getAnchoringVarDecl()->getASTContext().SourceMgr; auto init = getOriginalInit(); @@ -1558,6 +1746,10 @@ VarDecl *PatternBindingDecl::getSingleVar() const { return nullptr; } +VarDecl *PatternBindingDecl::getAnchoringVarDecl(unsigned i) const { + return getPatternList()[i].getAnchoringVarDecl(); +} + bool VarDecl::isInitExposedToClients() const { auto parent = dyn_cast(getDeclContext()); if (!parent) return false; @@ -1588,12 +1780,12 @@ static bool isDefaultInitializable(const TypeRepr *typeRepr, ASTContext &ctx) { // Also support the desugared 'Optional' spelling. if (!ctx.isSwiftVersionAtLeast(5)) { if (auto *identRepr = dyn_cast(typeRepr)) { - if (identRepr->getIdentifier() == ctx.Id_Void) + if (identRepr->getNameRef().getBaseIdentifier() == ctx.Id_Void) return true; } if (auto *identRepr = dyn_cast(typeRepr)) { - if (identRepr->getIdentifier() == ctx.Id_Optional && + if (identRepr->getNameRef().getBaseIdentifier() == ctx.Id_Optional && identRepr->getNumGenericArgs() == 1) return true; } @@ -1686,6 +1878,24 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const { return false; } + +bool PatternBindingDecl::isComputingPatternBindingEntry( + const VarDecl *vd) const { + unsigned i = getPatternEntryIndexForVarDecl(vd); + return getASTContext().evaluator.hasActiveRequest( + PatternBindingEntryRequest{const_cast(this), i}); +} + +bool PatternBindingDecl::isExplicitlyInitialized(unsigned i) const { + const auto &entry = getPatternList()[i]; + return entry.isInitialized(/*onlyExplicit=*/true); +} + +SourceLoc PatternBindingDecl::getEqualLoc(unsigned i) const { + const auto &entry = getPatternList()[i]; + return entry.getEqualLoc(); +} + SourceLoc TopLevelCodeDecl::getStartLoc() const { return Body->getStartLoc(); } @@ -2458,7 +2668,9 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type, SmallVector newParams; for (const auto ¶m : funcTy->getParams()) { auto newParamType = mapSignatureParamType(ctx, param.getPlainType()); - ParameterTypeFlags newFlags = param.getParameterFlags(); + + // Don't allow overloading by @_nonEphemeral. + auto newFlags = param.getParameterFlags().withNonEphemeral(false); // For the 'self' of a method, strip off 'inout'. if (isMethod) { @@ -2595,7 +2807,7 @@ OpaqueReturnTypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { } } } else { - returnRepr = VD->getTypeRepr(); + returnRepr = VD->getTypeReprOrParentPatternTypeRepr(); } } else if (auto *FD = dyn_cast(this)) { returnRepr = FD->getBodyResultTypeLoc().getTypeRepr(); @@ -2660,6 +2872,13 @@ void ValueDecl::setIsDynamic(bool value) { LazySemanticInfo.isDynamic = value; } +ValueDecl *ValueDecl::getDynamicallyReplacedDecl() const { + return evaluateOrDefault(getASTContext().evaluator, + DynamicallyReplacedDeclRequest{ + const_cast(this)}, + nullptr); +} + bool ValueDecl::canBeAccessedByDynamicLookup() const { if (!hasName()) return false; @@ -2682,6 +2901,16 @@ bool ValueDecl::isImplicitlyUnwrappedOptional() const { false); } +bool ValueDecl::isLocalCapture() const { + auto *dc = getDeclContext(); + + if (auto *fd = dyn_cast(this)) + if (isa(dc)) + return fd->hasTopLevelLocalContextCaptures(); + + return dc->isLocalContext(); +} + ArrayRef ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const { // Dig out the nominal type. @@ -2706,13 +2935,18 @@ bool ValueDecl::hasInterfaceType() const { return !TypeAndAccess.getPointer().isNull(); } +static bool isComputingInterfaceType(const ValueDecl *VD) { + return VD->getASTContext().evaluator.hasActiveRequest( + InterfaceTypeRequest{const_cast(VD)}); +} + bool ValueDecl::isRecursiveValidation() const { - if (hasValidationStarted() && !hasInterfaceType()) + if (isComputingInterfaceType(this) && !hasInterfaceType()) return true; if (auto *vd = dyn_cast(this)) if (auto *pbd = vd->getParentPatternBinding()) - if (pbd->isBeingValidated()) + if (pbd->isComputingPatternBindingEntry(vd)) return true; auto *dc = getDeclContext(); @@ -2728,30 +2962,22 @@ bool ValueDecl::isRecursiveValidation() const { } Type ValueDecl::getInterfaceType() const { - if (!hasInterfaceType()) { - // Our clients that don't register the lazy resolver are relying on the - // fact that they can't pull an interface type out to avoid doing work. - // This is a necessary evil until we can wean them off. - if (auto resolver = getASTContext().getLazyResolver()) { - resolver->resolveDeclSignature(const_cast(this)); - if (!hasInterfaceType()) - return ErrorType::get(getASTContext()); - } - } - return TypeAndAccess.getPointer(); -} + auto &ctx = getASTContext(); -void ValueDecl::setInterfaceType(Type type) { - if (type) { - assert(!type->hasTypeVariable() && "Type variable in interface type"); - assert(!type->is() && "Interface type must be materializable"); - assert(!type->hasArchetype() && "Archetype in interface type"); + assert(ctx.areSemanticQueriesEnabled()); - if (type->hasError()) - setInvalid(); - } + if (auto type = + evaluateOrDefault(ctx.evaluator, + InterfaceTypeRequest{const_cast(this)}, + Type())) + return type; + return ErrorType::get(ctx); +} - TypeAndAccess.setPointer(type); +void ValueDecl::setInterfaceType(Type type) { + assert(!type.isNull() && "Resetting the interface type to null is forbidden"); + getASTContext().evaluator.cacheOutput(InterfaceTypeRequest{this}, + std::move(type)); } Optional ValueDecl::getObjCRuntimeName( @@ -2842,7 +3068,7 @@ SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const { /// Returns true if \p VD needs to be treated as publicly-accessible /// at the SIL, LLVM, and machine levels due to being @usableFromInline. bool ValueDecl::isUsableFromInline() const { - assert(getFormalAccess() == AccessLevel::Internal); + assert(getFormalAccess() <= AccessLevel::Internal); if (getAttrs().hasAttribute() || getAttrs().hasAttribute() || @@ -2939,7 +3165,7 @@ static AccessLevel getAdjustedFormalAccess(const ValueDecl *VD, return getMaximallyOpenAccessFor(VD); if (treatUsableFromInlineAsPublic && - access == AccessLevel::Internal && + access <= AccessLevel::Internal && VD->isUsableFromInline()) { return AccessLevel::Public; } @@ -3275,11 +3501,7 @@ Type TypeDecl::getDeclaredInterfaceType() const { selfTy, const_cast(ATD)); } - Type interfaceType = getInterfaceType(); - if (!interfaceType) - return ErrorType::get(getASTContext()); - - return interfaceType->getMetatypeInstanceType(); + return getInterfaceType()->getMetatypeInstanceType(); } int TypeDecl::compare(const TypeDecl *type1, const TypeDecl *type2) { @@ -3357,13 +3579,25 @@ bool NominalTypeDecl::isResilient() const { return getModuleContext()->isResilient(); } +static bool isOriginallyDefinedIn(const Decl *D, const ModuleDecl* MD) { + if (!MD) + return false; + if (D->getAlternateModuleName().empty()) + return false; + return D->getAlternateModuleName() == MD->getName().str(); +} + bool NominalTypeDecl::isResilient(ModuleDecl *M, ResilienceExpansion expansion) const { switch (expansion) { case ResilienceExpansion::Minimal: return isResilient(); case ResilienceExpansion::Maximal: - return M != getModuleContext() && isResilient(); + // We consider this decl belongs to the module either it's currently + // defined in this module or it's originally defined in this module, which + // is specified by @_originallyDefinedIn + return M != getModuleContext() && !isOriginallyDefinedIn(this, M) && + isResilient(); } llvm_unreachable("bad resilience expansion"); } @@ -3379,7 +3613,7 @@ static Type computeNominalType(NominalTypeDecl *decl, DeclTypeKind kind) { // Get the parent type. Type Ty; DeclContext *dc = decl->getDeclContext(); - if (dc->isTypeContext()) { + if (!isa(decl) && dc->isTypeContext()) { switch (kind) { case DeclTypeKind::DeclaredType: { auto *nominal = dc->getSelfNominalTypeDecl(); @@ -3466,6 +3700,8 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) { if (!FirstExtension) { FirstExtension = extension; LastExtension = extension; + + addedExtension(extension); return; } @@ -3548,9 +3784,11 @@ SourceRange TypeAliasDecl::getSourceRange() const { Type TypeAliasDecl::getUnderlyingType() const { auto &ctx = getASTContext(); - return evaluateOrDefault(ctx.evaluator, + if (auto type = evaluateOrDefault(ctx.evaluator, UnderlyingTypeRequest{const_cast(this)}, - ErrorType::get(ctx)); + Type())) + return type; + return ErrorType::get(ctx); } void TypeAliasDecl::setUnderlyingType(Type underlying) { @@ -3581,10 +3819,12 @@ UnboundGenericType *TypeAliasDecl::getUnboundGenericType() const { Type TypeAliasDecl::getStructuralType() const { auto &ctx = getASTContext(); - return evaluateOrDefault( + if (auto type = evaluateOrDefault( ctx.evaluator, StructuralTypeRequest{const_cast(this)}, - ErrorType::get(ctx)); + Type())) + return type; + return ErrorType::get(ctx); } Type AbstractTypeParamDecl::getSuperclass() const { @@ -3733,8 +3973,6 @@ EnumDecl::EnumDecl(SourceLoc EnumLoc, GenericParams), EnumLoc(EnumLoc) { - Bits.EnumDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.EnumDecl.HasAssociatedValues = static_cast(AssociatedValueCheck::Unchecked); Bits.EnumDecl.HasAnyUnavailableValues @@ -3758,15 +3996,105 @@ StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc, Bits.StructDecl.HasUnreferenceableStorage = false; } +bool NominalTypeDecl::hasMemberwiseInitializer() const { + // Currently only structs can have memberwise initializers. + auto *sd = dyn_cast(this); + if (!sd) + return false; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(sd); + return evaluateOrDefault(ctx.evaluator, HasMemberwiseInitRequest{mutableThis}, + false); +} + +ConstructorDecl *NominalTypeDecl::getMemberwiseInitializer() const { + if (!hasMemberwiseInitializer()) + return nullptr; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, SynthesizeMemberwiseInitRequest{mutableThis}, nullptr); +} + +bool NominalTypeDecl::hasDefaultInitializer() const { + // Currently only structs and classes can have default initializers. + if (!isa(this) && !isa(this)) + return false; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, HasDefaultInitRequest{mutableThis}, + false); +} + +ConstructorDecl *NominalTypeDecl::getDefaultInitializer() const { + if (!hasDefaultInitializer()) + return nullptr; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + SynthesizeDefaultInitRequest{mutableThis}, nullptr); +} + +void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) { + // Silently break cycles here because we can't be sure when and where a + // request to synthesize will come from yet. + // FIXME: rdar://56844567 + if (Bits.NominalTypeDecl.IsComputingSemanticMembers) + return; + + Bits.NominalTypeDecl.IsComputingSemanticMembers = true; + SWIFT_DEFER { Bits.NominalTypeDecl.IsComputingSemanticMembers = false; }; + + auto baseName = member.getBaseName(); + auto &Context = getASTContext(); + Optional action = None; + if (baseName == DeclBaseName::createConstructor()) + action.emplace(ImplicitMemberAction::ResolveImplicitInit); + + if (member.isSimpleName() && !baseName.isSpecial()) { + if (baseName.getIdentifier() == getASTContext().Id_CodingKeys) { + action.emplace(ImplicitMemberAction::ResolveCodingKeys); + } + } else { + auto argumentNames = member.getArgumentNames(); + if (!member.isCompoundName() || argumentNames.size() == 1) { + if (baseName == DeclBaseName::createConstructor() && + (member.isSimpleName() || argumentNames.front() == Context.Id_from)) { + action.emplace(ImplicitMemberAction::ResolveDecodable); + } else if (!baseName.isSpecial() && + baseName.getIdentifier() == Context.Id_encode && + (member.isSimpleName() || + argumentNames.front() == Context.Id_to)) { + action.emplace(ImplicitMemberAction::ResolveEncodable); + } + } + } + + if (auto actionToTake = action) { + (void)evaluateOrDefault(Context.evaluator, + ResolveImplicitMemberRequest{this, actionToTake.getValue()}, false); + } +} + +bool ClassDecl::hasCircularInheritance() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + HasCircularInheritanceRequest{mutableThis}, true); +} + ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, MutableArrayRef Inherited, GenericParamList *GenericParams, DeclContext *Parent) : NominalTypeDecl(DeclKind::Class, Parent, Name, NameLoc, Inherited, GenericParams), ClassLoc(ClassLoc) { - Bits.ClassDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.ClassDecl.InheritsSuperclassInits = 0; + Bits.ClassDecl.ComputedInheritsSuperclassInits = 0; Bits.ClassDecl.RawForeignKind = 0; Bits.ClassDecl.HasMissingDesignatedInitializers = 0; Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 0; @@ -3844,21 +4172,14 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const { if (ctx.LangOpts.EnableObjCInterop) CD->recordObjCMethod(DD, DD->getObjCSelector()); - // Assign DD the interface type (Self) -> () -> () - DD->computeType(); - return DD; } - bool ClassDecl::hasMissingDesignatedInitializers() const { - if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) { - auto *mutableThis = const_cast(this); - mutableThis->Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1; - (void)mutableThis->lookupDirect(DeclBaseName::createConstructor()); - } - - return Bits.ClassDecl.HasMissingDesignatedInitializers; + return evaluateOrDefault( + getASTContext().evaluator, + HasMissingDesignatedInitializersRequest{const_cast(this)}, + false); } bool ClassDecl::hasMissingVTableEntries() const { @@ -3881,32 +4202,17 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const { return false; } -bool ClassDecl::inheritsSuperclassInitializers() { - // Check whether we already have a cached answer. - if (addedImplicitInitializers()) - return Bits.ClassDecl.InheritsSuperclassInits; - +bool ClassDecl::inheritsSuperclassInitializers() const { // If there's no superclass, there's nothing to inherit. - ClassDecl *superclassDecl; - if (!(superclassDecl = getSuperclassDecl())) { - setAddedImplicitInitializers(); - return false; - } - - // If the superclass has known-missing designated initializers, inheriting - // is unsafe. - if (superclassDecl->hasMissingDesignatedInitializers()) + if (!getSuperclass()) return false; - // Otherwise, do all the work of resolving constructors, which will also - // calculate the right answer. - if (auto *resolver = getASTContext().getLazyResolver()) - resolver->resolveImplicitConstructors(this); - - return Bits.ClassDecl.InheritsSuperclassInits; + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, InheritsSuperclassInitializersRequest{mutableThis}, false); } - AncestryOptions ClassDecl::checkAncestry() const { return AncestryOptions(evaluateOrDefault(getASTContext().evaluator, ClassAncestryFlagsRequest{const_cast(this)}, @@ -3924,7 +4230,7 @@ ClassAncestryFlagsRequest::evaluate(Evaluator &evaluator, ClassDecl *value) cons do { // If we hit circularity, we will diagnose at some point in typeCheckDecl(). // However we have to explicitly guard against that here because we get - // called as part of validateDecl(). + // called as part of the interface type request. if (!visited.insert(CD).second) break; @@ -4255,6 +4561,13 @@ void EnumDecl::setHasFixedRawValues() { LazySemanticInfo.RawTypeAndFlags.setInt(flags); } +bool EnumDecl::hasCircularRawValue() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + HasCircularRawValueRequest{mutableThis}, true); +} + ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc, Identifier Name, MutableArrayRef Inherited, @@ -4266,8 +4579,6 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, Bits.ProtocolDecl.RequiresClass = false; Bits.ProtocolDecl.ExistentialConformsToSelfValid = false; Bits.ProtocolDecl.ExistentialConformsToSelf = false; - Bits.ProtocolDecl.Circularity - = static_cast(CircularityCheck::Unchecked); Bits.ProtocolDecl.InheritedProtocolsValid = 0; Bits.ProtocolDecl.NumRequirementsInSignature = 0; Bits.ProtocolDecl.HasMissingRequirements = false; @@ -4286,7 +4597,7 @@ ProtocolDecl::getInheritedProtocolsSlow() { for (const auto found : getDirectlyInheritedNominalTypeDecls( const_cast(this), anyObject)) { - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.Item)) { if (known.insert(proto).second) result.push_back(proto); } @@ -4337,7 +4648,8 @@ ValueDecl *ProtocolDecl::getSingleRequirement(DeclName name) const { } AssociatedTypeDecl *ProtocolDecl::getAssociatedType(Identifier name) const { - auto results = const_cast(this)->lookupDirect(name); + const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; + auto results = const_cast(this)->lookupDirect(name, flags); for (auto candidate : results) { if (candidate->getDeclContext() == this && isa(candidate)) { @@ -4541,10 +4853,6 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value, auto type = value->getInterfaceType(); - // FIXME: Deal with broken recursion. - if (!type) - return SelfReferenceKind::None(); - // Skip invalid declarations. if (type->hasError()) return SelfReferenceKind::None(); @@ -4694,6 +5002,12 @@ void ProtocolDecl::computeKnownProtocolKind() const { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = value; } +bool ProtocolDecl::hasCircularInheritedProtocols() const { + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true); +} StorageImplInfo AbstractStorageDecl::getImplInfo() const { ASTContext &ctx = getASTContext(); @@ -4712,9 +5026,9 @@ bool AbstractStorageDecl::hasPrivateAccessor() const { bool AbstractStorageDecl::hasDidSetOrWillSetDynamicReplacement() const { if (auto *func = getParsedAccessor(AccessorKind::DidSet)) - return func->getAttrs().hasAttribute(); + return (bool)func->getDynamicallyReplacedDecl(); if (auto *func = getParsedAccessor(AccessorKind::WillSet)) - return func->getAttrs().hasAttribute(); + return (bool)func->getDynamicallyReplacedDecl(); return false; } @@ -4726,13 +5040,6 @@ bool AbstractStorageDecl::hasAnyNativeDynamicAccessors() const { return false; } -bool AbstractStorageDecl::hasAnyDynamicReplacementAccessors() const { - for (auto accessor : getAllAccessors()) { - if (accessor->getAttrs().hasAttribute()) - return true; - } - return false; -} void AbstractStorageDecl::setAccessors(SourceLoc lbraceLoc, ArrayRef accessors, SourceLoc rbraceLoc) { @@ -5000,31 +5307,16 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, { Bits.VarDecl.Introducer = unsigned(introducer); Bits.VarDecl.IsCaptureList = isCaptureList; + Bits.VarDecl.IsSelfParamCapture = false; Bits.VarDecl.IsDebuggerVar = false; Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; Bits.VarDecl.IsPropertyWrapperBackingProperty = false; + Bits.VarDecl.IsTopLevelGlobal = false; } Type VarDecl::getType() const { - if (!typeInContext) { - const_cast(this)->typeInContext = - getDeclContext()->mapTypeIntoContext( - getInterfaceType()); - } - - return typeInContext; -} - -void VarDecl::setType(Type t) { - assert(t.isNull() || !t->is()); - typeInContext = t; -} - -void VarDecl::markInvalid() { - auto &Ctx = getASTContext(); - setType(ErrorType::get(Ctx)); - setInterfaceType(ErrorType::get(Ctx)); + return getDeclContext()->mapTypeIntoContext(getInterfaceType()); } /// Returns whether the var is settable in the specified context: this @@ -5119,11 +5411,7 @@ bool VarDecl::isLazilyInitializedGlobal() const { // Top-level global variables in the main source file and in the REPL are not // lazily initialized. - auto sourceFileContext = dyn_cast(getDeclContext()); - if (!sourceFileContext) - return true; - - return !sourceFileContext->isScriptMode(); + return !isTopLevelGlobal(); } SourceRange VarDecl::getSourceRange() const { @@ -5243,9 +5531,11 @@ Stmt *VarDecl::getRecursiveParentPatternStmt() const { /// Pattern *VarDecl::getParentPattern() const { // If this has a PatternBindingDecl parent, use its pattern. - if (auto *PBD = getParentPatternBinding()) - return PBD->getPatternEntryForVarDecl(this).getPattern(); - + if (auto *PBD = getParentPatternBinding()) { + const auto i = PBD->getPatternEntryIndexForVarDecl(this); + return PBD->getPattern(i); + } + // If this is a statement parent, dig the pattern out of it. if (auto *stmt = getParentPatternStmt()) { if (auto *FES = dyn_cast(stmt)) @@ -5269,9 +5559,6 @@ Pattern *VarDecl::getParentPattern() const { if (pat->containsVarDecl(this)) return pat; } - - //stmt->dump(); - assert(0 && "Unknown parent pattern statement?"); } // Otherwise, check if we have to walk our case stmt's var decl list to find @@ -5285,6 +5572,27 @@ Pattern *VarDecl::getParentPattern() const { return nullptr; } +NamedPattern *VarDecl::getNamingPattern() const { + return evaluateOrDefault(getASTContext().evaluator, + NamingPatternRequest{const_cast(this)}, + nullptr); +} + +void VarDecl::setNamingPattern(NamedPattern *Pat) { + getASTContext().evaluator.cacheOutput(NamingPatternRequest{this}, + std::move(Pat)); +} + +TypeRepr *VarDecl::getTypeReprOrParentPatternTypeRepr() const { + if (auto *param = dyn_cast(this)) + return param->getTypeRepr(); + + if (auto *parentPattern = dyn_cast_or_null(getParentPattern())) + return parentPattern->getTypeRepr(); + + return nullptr; +} + NullablePtr VarDecl::getCorrespondingFirstCaseLabelItemVarDecl() const { if (!hasName()) @@ -5461,8 +5769,9 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const { llvm::TinyPtrVector VarDecl::getAttachedPropertyWrappers() const { auto &ctx = getASTContext(); - if (!ctx.getLazyResolver()) + if (!ctx.areSemanticQueriesEnabled()) { return { }; + } auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, @@ -5557,19 +5866,18 @@ VarDecl *VarDecl::getLazyStorageProperty() const { {}); } -static bool propertyWrapperInitializedViaInitialValue( - const VarDecl *var, bool checkDefaultInit) { - auto customAttrs = var->getAttachedPropertyWrappers(); +bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const { + auto customAttrs = getAttachedPropertyWrappers(); if (customAttrs.empty()) return false; - auto *PBD = var->getParentPatternBinding(); + auto *PBD = getParentPatternBinding(); if (!PBD) return false; // If there was an initializer on the original property, initialize // via the initial value. - if (PBD->getPatternList()[0].getEqualLoc().isValid()) + if (PBD->getEqualLoc(0).isValid()) return true; // If there was an initializer on the outermost wrapper, initialize @@ -5578,23 +5886,12 @@ static bool propertyWrapperInitializedViaInitialValue( return false; // Default initialization does not use a value. - if (checkDefaultInit && - var->getAttachedPropertyWrapperTypeInfo(0).defaultInit) + if (getAttachedPropertyWrapperTypeInfo(0).defaultInit) return false; - // If all property wrappers have an initialValue initializer, the property + // If all property wrappers have a wrappedValue initializer, the property // wrapper will be initialized that way. - return var->allAttachedPropertyWrappersHaveInitialValueInit(); -} - -bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const { - return propertyWrapperInitializedViaInitialValue( - this, /*checkDefaultInit=*/true); -} - -bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const { - return propertyWrapperInitializedViaInitialValue( - this, /*checkDefaultInit=*/false); + return allAttachedPropertyWrappersHaveInitialValueInit(); } Identifier VarDecl::getObjCPropertyName() const { @@ -5638,16 +5935,21 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const { if (FD && !FD->isMutating() && !FD->isImplicit() && FD->isInstanceMember()&& !FD->getDeclContext()->getDeclaredInterfaceType() ->hasReferenceSemantics()) { - // Do not suggest the fix it in implicit getters + // Do not suggest the fix-it in implicit getters if (auto AD = dyn_cast(FD)) { if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid()) return; } - + auto &d = getASTContext().Diags; - d.diagnose(FD->getFuncLoc(), diag::change_to_mutating, - isa(FD)) - .fixItInsert(FD->getFuncLoc(), "mutating "); + auto diags = d.diagnose(FD->getFuncLoc(), diag::change_to_mutating, + isa(FD)); + if (auto nonmutatingAttr = + FD->getAttrs().getAttribute()) { + diags.fixItReplace(nonmutatingAttr->getLocation(), "mutating"); + } else { + diags.fixItInsert(FD->getFuncLoc(), "mutating "); + } return; } } @@ -5683,7 +5985,8 @@ ParamDecl::ParamDecl(SourceLoc specifierLoc, VarDecl::Introducer::Let, /*IsCaptureList*/ false, parameterNameLoc, parameterName, dc, StorageIsNotMutable), - ArgumentName(argumentName), ParameterNameLoc(parameterNameLoc), + ArgumentNameAndDestructured(argumentName, false), + ParameterNameLoc(parameterNameLoc), ArgumentNameLoc(argumentNameLoc), SpecifierLoc(specifierLoc) { Bits.ParamDecl.SpecifierComputed = false; Bits.ParamDecl.defaultArgumentKind = @@ -5753,7 +6056,7 @@ SourceRange ParamDecl::getSourceRange() const { // It would be nice to extend the front of the range to show where inout is, // but we don't have that location info. Extend the back of the range to the // location of the default argument, or the typeloc if they are valid. - if (auto expr = getDefaultValue()) { + if (auto expr = getStructuralDefaultExpr()) { auto endLoc = expr->getEndLoc(); if (endLoc.isValid()) return SourceRange(startLoc, endLoc); @@ -5792,14 +6095,91 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const { type = ParamDecl::getVarargBaseTy(type); auto label = getArgumentName(); - auto flags = ParameterTypeFlags::fromParameterType(type, - isVariadic(), - isAutoClosure(), - getValueOwnership()); + auto flags = ParameterTypeFlags::fromParameterType( + type, isVariadic(), isAutoClosure(), isNonEphemeral(), + getValueOwnership(), + /*isNoDerivative*/ false); return AnyFunctionType::Param(type, label, flags); } -void ParamDecl::setDefaultValue(Expr *E) { +Optional ParamDecl::getCachedDefaultArgumentInitContext() const { + if (auto *defaultInfo = DefaultValueAndFlags.getPointer()) + if (auto *init = defaultInfo->InitContextAndIsTypeChecked.getPointer()) + return init; + + return None; +} + +Initializer *ParamDecl::getDefaultArgumentInitContext() const { + // If this param doesn't need a context, don't bother kicking off a request. + if (!hasDefaultExpr() && !getStoredProperty()) + return nullptr; + + auto &ctx = getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, DefaultArgumentInitContextRequest{mutableThis}, nullptr); +} + +bool ParamDecl::hasDefaultExpr() const { + switch (getDefaultArgumentKind()) { + case DefaultArgumentKind::None: + case DefaultArgumentKind::Inherited: + case DefaultArgumentKind::StoredProperty: + return false; + case DefaultArgumentKind::Normal: + case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: + case DefaultArgumentKind::Line: + case DefaultArgumentKind::Column: + case DefaultArgumentKind::Function: + case DefaultArgumentKind::DSOHandle: + case DefaultArgumentKind::NilLiteral: + case DefaultArgumentKind::EmptyArray: + case DefaultArgumentKind::EmptyDictionary: + // Check if we have a structural default expr. This ensures we return false + // for deserialized decls. + return getStructuralDefaultExpr(); + } + llvm_unreachable("Unhandled case in switch"); +} + +bool ParamDecl::hasCallerSideDefaultExpr() const { + switch (getDefaultArgumentKind()) { + case DefaultArgumentKind::None: + case DefaultArgumentKind::Inherited: + case DefaultArgumentKind::StoredProperty: + case DefaultArgumentKind::Normal: + return false; + case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: + case DefaultArgumentKind::Line: + case DefaultArgumentKind::Column: + case DefaultArgumentKind::Function: + case DefaultArgumentKind::DSOHandle: + case DefaultArgumentKind::NilLiteral: + case DefaultArgumentKind::EmptyArray: + case DefaultArgumentKind::EmptyDictionary: + return true; + } +} + +Expr *ParamDecl::getTypeCheckedDefaultExpr() const { + // Don't kick off a request if we know there's no default expr. The only + // exception is for inherited default args which we need to perform a couple + // of semantic checks for. + if (!hasDefaultExpr() && + getDefaultArgumentKind() != DefaultArgumentKind::Inherited) { + return nullptr; + } + + auto &ctx = getASTContext(); + return evaluateOrDefault( + ctx.evaluator, DefaultArgumentExprRequest{const_cast(this)}, + new (ctx) ErrorExpr(getSourceRange(), ErrorType::get(ctx))); +} + +void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) { if (!DefaultValueAndFlags.getPointer()) { if (!E) return; @@ -5807,7 +6187,16 @@ void ParamDecl::setDefaultValue(Expr *E) { getASTContext().Allocate()); } - DefaultValueAndFlags.getPointer()->DefaultArg = E; + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo->DefaultArg.isNull() || + defaultInfo->DefaultArg.is()); + + if (!isTypeChecked) { + assert(!defaultInfo->InitContextAndIsTypeChecked.getInt() && + "Can't overwrite type-checked default with un-type-checked default"); + } + defaultInfo->DefaultArg = E; + defaultInfo->InitContextAndIsTypeChecked.setInt(isTypeChecked); } void ParamDecl::setStoredProperty(VarDecl *var) { @@ -5818,7 +6207,10 @@ void ParamDecl::setStoredProperty(VarDecl *var) { getASTContext().Allocate()); } - DefaultValueAndFlags.getPointer()->DefaultArg = var; + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo->DefaultArg.isNull() || + defaultInfo->DefaultArg.is()); + defaultInfo->DefaultArg = var; } Type ValueDecl::getFunctionBuilderType() const { @@ -5846,11 +6238,16 @@ CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { } void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) { - assert(DefaultValueAndFlags.getPointer()); - DefaultValueAndFlags.getPointer()->InitContext = initContext; + auto oldContext = getCachedDefaultArgumentInitContext(); + assert((!oldContext || oldContext == initContext) && + "Cannot change init context after setting"); + + auto *defaultInfo = DefaultValueAndFlags.getPointer(); + assert(defaultInfo); + defaultInfo->InitContextAndIsTypeChecked.setPointer(initContext); } -void ParamDecl::setDefaultArgumentCaptureInfo(const CaptureInfo &captures) { +void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) { assert(DefaultValueAndFlags.getPointer()); DefaultValueAndFlags.getPointer()->Captures = captures; } @@ -5863,8 +6260,7 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var, return nullptr; // If there is no '=' on the pattern, there was no initial value. - if (PBD->getPatternList()[0].getEqualLoc().isInvalid() - && !PBD->isDefaultInitializable()) + if (PBD->getEqualLoc(0).isInvalid() && !PBD->isDefaultInitializable()) return nullptr; ASTContext &ctx = var->getASTContext(); @@ -5896,13 +6292,25 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var, if (!call->isImplicit()) return { true, E }; - // ... producing a value of the same nominal type as the innermost - // property wrapper. + // ... which may call the constructor of another property + // wrapper if there are multiple wrappers attached to the + // property. + if (auto tuple = dyn_cast(call->getArg())) { + if (tuple->getNumElements() > 0) { + auto elem = tuple->getElement(0); + if (elem->isImplicit() && isa(elem)) { + return { true, E }; + } + } + } + + // ... producing a value of the same nominal type as the + // innermost property wrapper. if (!call->getType() || call->getType()->getAnyNominal() != innermostNominal) - return { true, E }; + return { false, E }; - // Find the implicit initialValue argument. + // Find the implicit initialValue/wrappedValue argument. if (auto tuple = dyn_cast(call->getArg())) { ASTContext &ctx = innermostNominal->getASTContext(); for (unsigned i : range(tuple->getNumElements())) { @@ -5984,10 +6392,10 @@ ParamDecl::getDefaultValueStringRepresentation( if (!existing.empty()) return existing; - assert(getDefaultValue() + assert(hasDefaultExpr() && "Normal default argument with no default expression?!"); - return extractInlinableText(getASTContext().SourceMgr, getDefaultValue(), - scratch); + return extractInlinableText(getASTContext().SourceMgr, + getStructuralDefaultExpr(), scratch); } case DefaultArgumentKind::StoredProperty: { assert(DefaultValueAndFlags.getPointer() && @@ -6054,6 +6462,7 @@ ParamDecl::getDefaultValueStringRepresentation( } case DefaultArgumentKind::Inherited: return "super"; case DefaultArgumentKind::File: return "#file"; + case DefaultArgumentKind::FilePath: return "#filePath"; case DefaultArgumentKind::Line: return "#line"; case DefaultArgumentKind::Column: return "#column"; case DefaultArgumentKind::Function: return "#function"; @@ -6087,7 +6496,7 @@ void DefaultArgumentInitializer::changeFunction( } auto param = paramList->get(getIndex()); - if (param->getDefaultValue() || param->getStoredProperty()) + if (param->hasDefaultExpr() || param->getStoredProperty()) param->setDefaultArgumentInitContext(this); } @@ -6130,9 +6539,11 @@ void SubscriptDecl::setIndices(ParameterList *p) { Type SubscriptDecl::getElementInterfaceType() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); - return evaluateOrDefault(ctx.evaluator, + if (auto type = evaluateOrDefault(ctx.evaluator, ResultTypeRequest{mutableThis}, - ErrorType::get(ctx)); + Type())) + return type; + return ErrorType::get(ctx); } ObjCSubscriptKind SubscriptDecl::getObjCSubscriptKind() const { @@ -6174,7 +6585,7 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { auto &ctx = getASTContext(); auto storage = accessor->getStorage(); auto subscript = dyn_cast(storage); - switch (auto accessorKind = accessor->getAccessorKind()) { + switch (accessor->getAccessorKind()) { // These don't have any extra implicit parameters. case AccessorKind::Address: case AccessorKind::MutableAddress: @@ -6205,17 +6616,24 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { return DeclName(); } -const ParamDecl *swift::getParameterAt(const ValueDecl *source, unsigned index) { - const ParameterList *paramList; +ParameterList *swift::getParameterList(ValueDecl *source) { if (auto *AFD = dyn_cast(source)) { - paramList = AFD->getParameters(); + return AFD->getParameters(); } else if (auto *EED = dyn_cast(source)) { - paramList = EED->getParameterList(); - } else { - paramList = cast(source)->getIndices(); + return EED->getParameterList(); + } else if (auto *SD = dyn_cast(source)) { + return SD->getIndices(); } - return paramList->get(index); + return nullptr; +} + +const ParamDecl *swift::getParameterAt(const ValueDecl *source, + unsigned index) { + if (auto *params = getParameterList(const_cast(source))) { + return params->get(index); + } + return nullptr; } Type AbstractFunctionDecl::getMethodInterfaceType() const { @@ -6271,6 +6689,20 @@ BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { nullptr); } +void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) { + assert(getBodyKind() != BodyKind::Skipped && + "cannot set a body if it was skipped"); + + Body = S; + setBodyKind(NewBodyKind); + + // Need to recompute init body kind. + if (NewBodyKind < BodyKind::TypeChecked) { + if (auto *ctor = dyn_cast(this)) + ctor->clearCachedDelegatingOrChainedInitKind(); + } +} + SourceRange AbstractFunctionDecl::getBodySourceRange() const { switch (getBodyKind()) { case BodyKind::None: @@ -6310,7 +6742,7 @@ ObjCSelector AbstractFunctionDecl::getObjCSelector(DeclName preferredName, bool skipIsObjCResolution) const { // FIXME: Forces computation of the Objective-C selector. - if (getASTContext().getLazyResolver() && !skipIsObjCResolution) + if (getASTContext().areSemanticQueriesEnabled() && !skipIsObjCResolution) (void)isObjC(); // If there is an @objc attribute with a name, use that name. @@ -6365,8 +6797,7 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName, scratch += "init"; // If the first argument name doesn't start with a preposition, add "with". - if (getPrepositionKind(camel_case::getFirstWord(firstName.str())) - == PK_None) { + if (!isPreposition(camel_case::getFirstWord(firstName.str()))) { camel_case::appendSentenceCase(scratch, "With"); } @@ -6425,10 +6856,8 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName, // If the first argument name doesn't start with a preposition, and the // method name doesn't end with a preposition, add "with". auto firstName = argNames[argIndex++]; - if (getPrepositionKind(camel_case::getFirstWord(firstName.str())) - == PK_None && - getPrepositionKind(camel_case::getLastWord(firstPiece.str())) - == PK_None) { + if (!isPreposition(camel_case::getFirstWord(firstName.str())) && + !isPreposition(camel_case::getLastWord(firstPiece.str()))) { camel_case::appendSentenceCase(scratch, "With"); } @@ -6548,55 +6977,6 @@ Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const { return OpaqueReturnTypeIdentifier; } -void AbstractFunctionDecl::computeType(AnyFunctionType::ExtInfo info) { - auto sig = getGenericSignature(); - bool hasSelf = hasImplicitSelfDecl(); - - // Result - Type resultTy; - if (auto fn = dyn_cast(this)) { - resultTy = fn->getResultInterfaceType(); - } else if (auto ctor = dyn_cast(this)) { - resultTy = ctor->getResultInterfaceType(); - } else { - assert(isa(this)); - resultTy = TupleType::getEmpty(getASTContext()); - } - - // (Args...) -> Result - Type funcTy; - - { - SmallVector argTy; - getParameters()->getParams(argTy); - - // 'throws' only applies to the innermost function. - info = info.withThrows(hasThrows()); - // Defer bodies must not escape. - if (auto fd = dyn_cast(this)) - info = info.withNoEscape(fd->isDeferBody()); - - if (sig && !hasSelf) { - funcTy = GenericFunctionType::get(sig, argTy, resultTy, info); - } else { - funcTy = FunctionType::get(argTy, resultTy, info); - } - } - - // (Self) -> (Args...) -> Result - if (hasSelf) { - // Substitute in our own 'self' parameter. - auto selfParam = computeSelfParam(this); - if (sig) - funcTy = GenericFunctionType::get(sig, {selfParam}, funcTy); - else - funcTy = FunctionType::get({selfParam}, funcTy); - } - - // Record the interface type. - setInterfaceType(funcTy); -} - bool AbstractFunctionDecl::hasInlinableBodyText() const { switch (getBodyKind()) { case BodyKind::Deserialized: @@ -6630,6 +7010,45 @@ StringRef AbstractFunctionDecl::getInlinableBodyText( return extractInlinableText(getASTContext().SourceMgr, body, scratch); } +/// A uniqued list of derivative function configurations. +struct AbstractFunctionDecl::DerivativeFunctionConfigurationList + : public llvm::SetVector { + // Necessary for `ASTContext` allocation. + void *operator new( + size_t bytes, ASTContext &ctx, + unsigned alignment = alignof(DerivativeFunctionConfigurationList)) { + return ctx.Allocate(bytes, alignment); + } +}; + +void AbstractFunctionDecl::prepareDerivativeFunctionConfigurations() { + if (DerivativeFunctionConfigs) + return; + auto &ctx = getASTContext(); + DerivativeFunctionConfigs = new (ctx) DerivativeFunctionConfigurationList(); + // Register an `ASTContext` cleanup calling the list destructor. + ctx.addCleanup([this]() { + this->DerivativeFunctionConfigs->~DerivativeFunctionConfigurationList(); + }); +} + +ArrayRef +AbstractFunctionDecl::getDerivativeFunctionConfigurations() { + prepareDerivativeFunctionConfigurations(); + auto &ctx = getASTContext(); + if (ctx.getCurrentGeneration() > DerivativeFunctionConfigGeneration) { + // TODO(TF-1100): Upstream derivative function configuration serialization + // logic. + } + return DerivativeFunctionConfigs->getArrayRef(); +} + +void AbstractFunctionDecl::addDerivativeFunctionConfiguration( + AutoDiffConfig config) { + prepareDerivativeFunctionConfigurations(); + DerivativeFunctionConfigs->insert(config); +} + FuncDecl *FuncDecl::createImpl(ASTContext &Context, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, @@ -6812,9 +7231,11 @@ StaticSpellingKind FuncDecl::getCorrectStaticSpelling() const { Type FuncDecl::getResultInterfaceType() const { auto &ctx = getASTContext(); auto mutableThis = const_cast(this); - return evaluateOrDefault(ctx.evaluator, + if (auto type = evaluateOrDefault(ctx.evaluator, ResultTypeRequest{mutableThis}, - ErrorType::get(ctx)); + Type())) + return type; + return ErrorType::get(ctx); } bool FuncDecl::isUnaryOperator() const { @@ -7097,8 +7518,11 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, // If we already computed the result, return it. if (Bits.ConstructorDecl.ComputedBodyInitKind) { - return static_cast( - Bits.ConstructorDecl.ComputedBodyInitKind - 1); + auto Kind = static_cast( + Bits.ConstructorDecl.ComputedBodyInitKind - 1); + assert((Kind == BodyInitKind::None || !init) && + "can't return cached result with the init expr"); + return Kind; } @@ -7329,21 +7753,6 @@ PrecedenceGroupDecl::PrecedenceGroupDecl(DeclContext *dc, lowerThan.size() * sizeof(Relation)); } -llvm::Expected LookupPrecedenceGroupRequest::evaluate( - Evaluator &eval, PrecedenceGroupDescriptor descriptor) const { - auto *dc = descriptor.dc; - PrecedenceGroupDecl *group = nullptr; - if (auto sf = dc->getParentSourceFile()) { - bool cascading = dc->isCascadingContextForLookup(false); - group = sf->lookupPrecedenceGroup(descriptor.ident, cascading, - descriptor.nameLoc); - } else { - group = dc->getParentModule()->lookupPrecedenceGroup(descriptor.ident, - descriptor.nameLoc); - } - return group; -} - PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const { return evaluateOrDefault( getASTContext().evaluator, @@ -7361,6 +7770,12 @@ bool FuncDecl::isPotentialIBActionTarget() const { !isa(this); } +void FuncDecl::setHasTopLevelLocalContextCaptures(bool hasCaptures) { + assert(!hasCaptures || isa(getDeclContext())); + + Bits.FuncDecl.HasTopLevelLocalContextCaptures = hasCaptures; +} + Type TypeBase::getSwiftNewtypeUnderlyingType() { auto structDecl = getStructOrBoundGenericStruct(); if (!structDecl) @@ -7500,6 +7915,15 @@ void swift::simple_display(llvm::raw_ostream &out, const Decl *decl) { } } +void swift::simple_display(llvm::raw_ostream &out, + OptionSet opts) { + out << "{ "; + using LookupFlags = NominalTypeDecl::LookupDirectFlags; + if (opts.contains(LookupFlags::IncludeAttrImplements)) + out << "IncludeAttrImplements"; + out << " }"; +} + void swift::simple_display(llvm::raw_ostream &out, const ValueDecl *decl) { if (decl) decl->dumpRef(out); else out << "(null)"; @@ -7578,3 +8002,10 @@ void ParseAbstractFunctionBodyRequest::cacheResult(BraceStmt *value) const { } } + +void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) { + if (auto func = fn.getAbstractFunctionDecl()) + simple_display(out, func); + else + out << "closure"; +} diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index f8860e540a47d..62f08d4b418f7 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -798,8 +798,7 @@ void IterableDeclContext::setMemberLoader(LazyMemberLoader *loader, ASTContext &ctx = getASTContext(); auto contextInfo = ctx.getOrCreateLazyIterableContextData(this, loader); - auto lazyMembers = FirstDeclAndLazyMembers.getInt() | LazyMembers::Present; - FirstDeclAndLazyMembers.setInt(LazyMembers(lazyMembers)); + FirstDeclAndLazyMembers.setInt(true); contextInfo->memberData = contextData; ++NumLazyIterableDeclContexts; @@ -839,8 +838,10 @@ void IterableDeclContext::loadAllMembers() const { // members to this context, this call is important for recording the // dependency edge. auto mutableThis = const_cast(this); - auto members = evaluateOrDefault( - ctx.evaluator, ParseMembersRequest{mutableThis}, ArrayRef()); + auto members = + evaluateOrDefault(ctx.evaluator, ParseMembersRequest{mutableThis}, + FingerprintAndMembers()) + .members; // If we haven't already done so, add these members to this context. if (!AddedParsedMembers) { @@ -855,12 +856,11 @@ void IterableDeclContext::loadAllMembers() const { return; // Don't try to load all members re-entrant-ly. - auto contextInfo = ctx.getOrCreateLazyIterableContextData(this, - /*lazyLoader=*/nullptr); - auto lazyMembers = FirstDeclAndLazyMembers.getInt() & ~LazyMembers::Present; - FirstDeclAndLazyMembers.setInt(LazyMembers(lazyMembers)); + FirstDeclAndLazyMembers.setInt(false); const Decl *container = getDecl(); + auto contextInfo = ctx.getOrCreateLazyIterableContextData(this, + /*lazyLoader=*/nullptr); contextInfo->loader->loadAllMembers(const_cast(container), contextInfo->memberData); @@ -901,6 +901,21 @@ IterableDeclContext::castDeclToIterableDeclContext(const Decl *D) { } } +Optional IterableDeclContext::getBodyFingerprint() const { + // Only makes sense for contexts in a source file + if (!getDecl()->getDeclContext()->getParentSourceFile()) + return None; + auto mutableThis = const_cast(this); + return evaluateOrDefault(getASTContext().evaluator, + ParseMembersRequest{mutableThis}, + FingerprintAndMembers()) + .fingerprint; +} + +bool IterableDeclContext::areDependenciesUsingTokenHashesForTypeBodies() const { + return getASTContext().LangOpts.EnableTypeFingerprints; +} + /// Return the DeclContext to compare when checking private access in /// Swift 4 mode. The context returned is the type declaration if the context /// and the type declaration are in the same file, otherwise it is the types @@ -1035,6 +1050,20 @@ DeclContextKind DeclContext::getContextKind() const { llvm_unreachable("Unhandled DeclContext ASTHierarchy"); } +bool DeclContext::hasValueSemantics() const { + if (getExtendedProtocolDecl()) + return !isClassConstrainedProtocolExtension(); + return !getDeclaredInterfaceType()->hasReferenceSemantics(); +} + +bool DeclContext::isClassConstrainedProtocolExtension() const { + if (getExtendedProtocolDecl()) { + auto ED = cast(this); + return ED->getGenericSignature()->requiresClass(ED->getSelfInterfaceType()); + } + return false; +} + SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) { switch (dc->getContextKind()) { case DeclContextKind::AbstractFunctionDecl: diff --git a/lib/AST/DiagnosticConsumer.cpp b/lib/AST/DiagnosticConsumer.cpp index 122e322c3fa0d..fe647177745ef 100644 --- a/lib/AST/DiagnosticConsumer.cpp +++ b/lib/AST/DiagnosticConsumer.cpp @@ -179,38 +179,29 @@ FileSpecificDiagnosticConsumer::subconsumerForLocation(SourceManager &SM, } void FileSpecificDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { + SourceManager &SM, const DiagnosticInfo &Info) { - HasAnErrorBeenConsumed |= Kind == DiagnosticKind::Error; + HasAnErrorBeenConsumed |= Info.Kind == DiagnosticKind::Error; - auto subconsumer = - findSubconsumer(SM, Loc, Kind, bufferIndirectlyCausingDiagnostic); + auto subconsumer = findSubconsumer(SM, Info); if (subconsumer) { - subconsumer.getValue()->handleDiagnostic(SM, Loc, Kind, FormatString, - FormatArgs, Info, - bufferIndirectlyCausingDiagnostic); + subconsumer.getValue()->handleDiagnostic(SM, Info); return; } // Last resort: spray it everywhere for (auto &subconsumer : Subconsumers) - subconsumer.handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info, - bufferIndirectlyCausingDiagnostic); + subconsumer.handleDiagnostic(SM, Info); } Optional -FileSpecificDiagnosticConsumer::findSubconsumer( - SourceManager &SM, SourceLoc loc, DiagnosticKind Kind, - SourceLoc bufferIndirectlyCausingDiagnostic) { +FileSpecificDiagnosticConsumer::findSubconsumer(SourceManager &SM, + const DiagnosticInfo &Info) { // Ensure that a note goes to the same place as the preceeding non-note. - switch (Kind) { + switch (Info.Kind) { case DiagnosticKind::Error: case DiagnosticKind::Warning: case DiagnosticKind::Remark: { - auto subconsumer = - findSubconsumerForNonNote(SM, loc, bufferIndirectlyCausingDiagnostic); + auto subconsumer = findSubconsumerForNonNote(SM, Info); SubconsumerForSubsequentNotes = subconsumer; return subconsumer; } @@ -222,18 +213,17 @@ FileSpecificDiagnosticConsumer::findSubconsumer( Optional FileSpecificDiagnosticConsumer::findSubconsumerForNonNote( - SourceManager &SM, const SourceLoc loc, - const SourceLoc bufferIndirectlyCausingDiagnostic) { - const auto subconsumer = subconsumerForLocation(SM, loc); + SourceManager &SM, const DiagnosticInfo &Info) { + const auto subconsumer = subconsumerForLocation(SM, Info.Loc); if (!subconsumer) return None; // No place to put it; might be in an imported module if ((*subconsumer)->getConsumer()) return subconsumer; // A primary file with a .dia file // Try to put it in the responsible primary input - if (bufferIndirectlyCausingDiagnostic.isInvalid()) + if (Info.BufferIndirectlyCausingDiagnostic.isInvalid()) return None; const auto currentPrimarySubconsumer = - subconsumerForLocation(SM, bufferIndirectlyCausingDiagnostic); + subconsumerForLocation(SM, Info.BufferIndirectlyCausingDiagnostic); assert(!currentPrimarySubconsumer || (*currentPrimarySubconsumer)->getConsumer() && "current primary must have a .dia file"); @@ -261,14 +251,12 @@ void FileSpecificDiagnosticConsumer:: (*this)[info].informDriverOfIncompleteBatchModeCompilation(); } -void NullDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, const SourceLoc) { +void NullDiagnosticConsumer::handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) { LLVM_DEBUG({ llvm::dbgs() << "NullDiagnosticConsumer received diagnostic: "; - DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), FormatString, - FormatArgs); + DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), Info.FormatString, + Info.FormatArgs); llvm::dbgs() << "\n"; }); } @@ -277,18 +265,14 @@ ForwardingDiagnosticConsumer::ForwardingDiagnosticConsumer(DiagnosticEngine &Tar : TargetEngine(Target) {} void ForwardingDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { + SourceManager &SM, const DiagnosticInfo &Info) { LLVM_DEBUG({ llvm::dbgs() << "ForwardingDiagnosticConsumer received diagnostic: "; - DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), FormatString, - FormatArgs); + DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), Info.FormatString, + Info.FormatArgs); llvm::dbgs() << "\n"; }); for (auto *C : TargetEngine.getConsumers()) { - C->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info, - bufferIndirectlyCausingDiagnostic); + C->handleDiagnostic(SM, Info); } } diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 08d87b482c228..56aa08790966a 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -116,6 +116,30 @@ static constexpr const char *const fixItStrings[] = { "", }; +#define EDUCATIONAL_NOTES(DIAG, ...) \ + static constexpr const char *const DIAG##_educationalNotes[] = {__VA_ARGS__, \ + nullptr}; +#include "swift/AST/EducationalNotes.def" + +// NOTE: sadly, while GCC and Clang support array designators in C++, they are +// not part of the standard at the moment, so Visual C++ doesn't support them. +// This construct allows us to provide a constexpr array initialized to empty +// values except in the cases that EducationalNotes.def are provided, similar to +// what the C array would have looked like. +template +struct EducationalNotes { + constexpr EducationalNotes() : value() { + for (auto i = 0; i < N; ++i) value[i] = {}; +#define EDUCATIONAL_NOTES(DIAG, ...) \ + value[LocalDiagID::DIAG] = DIAG##_educationalNotes; +#include "swift/AST/EducationalNotes.def" + } + const char *const *value[N]; +}; + +static constexpr EducationalNotes _EducationalNotes = EducationalNotes(); +static constexpr auto educationalNotes = _EducationalNotes.value; + DiagnosticState::DiagnosticState() { // Initialize our per-diagnostic state to default perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified); @@ -669,9 +693,8 @@ void DiagnosticEngine::formatDiagnosticText( } if (Modifier == "error") { - assert(false && "encountered %error in diagnostic text"); - Out << StringRef("<>"); - break; + Out << StringRef("<>"); + continue; } // Parse the optional argument list for a modifier, which is brace-enclosed. @@ -684,12 +707,20 @@ void DiagnosticEngine::formatDiagnosticText( // Find the digit sequence, and parse it into an argument index. size_t Length = InText.find_if_not(isdigit); unsigned ArgIndex; - bool Result = InText.substr(0, Length).getAsInteger(10, ArgIndex); - assert(!Result && "Unparseable argument index value?"); - (void)Result; - assert(ArgIndex < Args.size() && "Out-of-range argument index"); + bool IndexParseFailed = InText.substr(0, Length).getAsInteger(10, ArgIndex); + + if (IndexParseFailed) { + Out << StringRef("<>"); + continue; + } + InText = InText.substr(Length); + if (ArgIndex >= Args.size()) { + Out << StringRef("<>"); + continue; + } + // Convert the argument to a string. formatDiagnosticArgument(Modifier, ModifierArguments, Args, ArgIndex, FormatOpts, Out); @@ -951,10 +982,21 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) { } } info->ChildDiagnosticInfo = childInfoPtrs; + + SmallVector educationalNotePaths; + if (useDescriptiveDiagnostics) { + auto associatedNotes = educationalNotes[(uint32_t)diagnostic.getID()]; + while (associatedNotes && *associatedNotes) { + SmallString<128> notePath(getDiagnosticDocumentationPath()); + llvm::sys::path::append(notePath, *associatedNotes); + educationalNotePaths.push_back(notePath.str()); + associatedNotes++; + } + info->EducationalNotePaths = educationalNotePaths; + } + for (auto &consumer : Consumers) { - consumer->handleDiagnostic(SourceMgr, info->Loc, info->Kind, - info->FormatString, info->FormatArgs, *info, - info->BufferIndirectlyCausingDiagnostic); + consumer->handleDiagnostic(SourceMgr, *info); } } @@ -1028,7 +1070,7 @@ void DiagnosticEngine::onTentativeDiagnosticFlush(Diagnostic &diagnostic) { if (content.empty()) continue; - auto I = TransactionStrings.insert(std::make_pair(content, char())).first; + auto I = TransactionStrings.insert(content).first; argument = DiagnosticArgument(StringRef(I->getKeyData())); } } diff --git a/lib/AST/DocComment.cpp b/lib/AST/DocComment.cpp index af637f16dca93..993b4909e4fc6 100644 --- a/lib/AST/DocComment.cpp +++ b/lib/AST/DocComment.cpp @@ -414,7 +414,8 @@ const ValueDecl *findDefaultProvidedDeclWithDocComment(const ValueDecl *VD) { SmallVector members; protocol->lookupQualified(const_cast(protocol), - VD->getFullName(), NLOptions::NL_ProtocolMembers, + DeclNameRef(VD->getFullName()), + NLOptions::NL_ProtocolMembers, members); for (auto *member : members) { diff --git a/lib/AST/Evaluator.cpp b/lib/AST/Evaluator.cpp index c73ed3e97c357..3d831f24d1971 100644 --- a/lib/AST/Evaluator.cpp +++ b/lib/AST/Evaluator.cpp @@ -24,8 +24,6 @@ using namespace swift; -AnyRequest::HolderBase::~HolderBase() { } - std::string AnyRequest::getAsString() const { std::string result; { @@ -62,8 +60,12 @@ void Evaluator::registerRequestFunctions( requestFunctionsByZone.push_back({zoneID, functions}); } -Evaluator::Evaluator(DiagnosticEngine &diags, bool debugDumpCycles) - : diags(diags), debugDumpCycles(debugDumpCycles) { } +Evaluator::Evaluator(DiagnosticEngine &diags, + bool debugDumpCycles, + bool buildDependencyGraph) + : diags(diags), + debugDumpCycles(debugDumpCycles), + buildDependencyGraph(buildDependencyGraph) { } void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { std::error_code error; @@ -71,33 +73,43 @@ void Evaluator::emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath) { printDependenciesGraphviz(out); } -bool Evaluator::checkDependency(const AnyRequest &request) { - // If there is an active request, record it's dependency on this request. - if (!activeRequests.empty()) - dependencies[activeRequests.back()].push_back(request); +bool Evaluator::checkDependency(const ActiveRequest &request) { + if (buildDependencyGraph) { + // Insert the request into the dependency graph if we haven't already. + auto req = AnyRequest(request); + dependencies.insert({req, {}}); + + // If there is an active request, record it's dependency on this request. + if (!activeRequests.empty()) { + auto activeDeps = dependencies.find_as(activeRequests.back()); + assert(activeDeps != dependencies.end()); + activeDeps->second.push_back(req); + } + } // Record this as an active request. - if (activeRequests.insert(request)) { + if (activeRequests.insert(request)) return false; - } // Diagnose cycle. diagnoseCycle(request); + return true; +} +void Evaluator::diagnoseCycle(const ActiveRequest &request) { if (debugDumpCycles) { llvm::errs() << "===CYCLE DETECTED===\n"; llvm::DenseSet visitedAnywhere; llvm::SmallVector visitedAlongPath; std::string prefixStr; - printDependencies(activeRequests.front(), llvm::errs(), visitedAnywhere, - visitedAlongPath, activeRequests.getArrayRef(), + SmallVector highlightPath; + for (auto &req : activeRequests) + highlightPath.push_back(AnyRequest(req)); + printDependencies(AnyRequest(activeRequests.front()), llvm::errs(), + visitedAnywhere, visitedAlongPath, highlightPath, prefixStr, /*lastChild=*/true); } - return true; -} - -void Evaluator::diagnoseCycle(const AnyRequest &request) { request.diagnoseCycle(diags); for (const auto &step : llvm::reverse(activeRequests)) { if (step == request) return; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 584d1c54a8eb8..4b2406099ebfb 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -25,6 +25,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/AvailabilitySpec.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeLoc.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/PointerUnion.h" @@ -227,7 +228,7 @@ DeclRefExpr *Expr::getMemberOperatorRef() { return operatorRef; } -ConcreteDeclRef Expr::getReferencedDecl() const { +ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const { switch (getKind()) { // No declaration reference. #define NO_REFERENCE(Id) case ExprKind::Id: return ConcreteDeclRef() @@ -236,7 +237,8 @@ ConcreteDeclRef Expr::getReferencedDecl() const { return cast(this)->Getter() #define PASS_THROUGH_REFERENCE(Id, GetSubExpr) \ case ExprKind::Id: \ - return cast(this)->GetSubExpr()->getReferencedDecl() + return cast(this) \ + ->GetSubExpr()->getReferencedDecl(stopAtParenExpr) NO_REFERENCE(Error); NO_REFERENCE(NilLiteral); @@ -253,13 +255,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const { SIMPLE_REFERENCE(DeclRef, getDeclRef); SIMPLE_REFERENCE(SuperRef, getSelf); - case ExprKind::Type: { - auto typeRepr = cast(this)->getTypeRepr(); - if (!typeRepr) return ConcreteDeclRef(); - auto ident = dyn_cast(typeRepr); - if (!ident) return ConcreteDeclRef(); - return ident->getComponentRange().back()->getBoundDecl(); - } + NO_REFERENCE(Type); SIMPLE_REFERENCE(OtherConstructorDeclRef, getDeclRef); @@ -280,7 +276,12 @@ ConcreteDeclRef Expr::getReferencedDecl() const { NO_REFERENCE(UnresolvedMember); NO_REFERENCE(UnresolvedDot); NO_REFERENCE(Sequence); - PASS_THROUGH_REFERENCE(Paren, getSubExpr); + + case ExprKind::Paren: + if (stopAtParenExpr) return ConcreteDeclRef(); + return cast(this) + ->getSubExpr()->getReferencedDecl(stopAtParenExpr); + PASS_THROUGH_REFERENCE(DotSelf, getSubExpr); PASS_THROUGH_REFERENCE(Try, getSubExpr); PASS_THROUGH_REFERENCE(ForceTry, getSubExpr); @@ -311,7 +312,6 @@ ConcreteDeclRef Expr::getReferencedDecl() const { NO_REFERENCE(OpaqueValue); NO_REFERENCE(DefaultArgument); - NO_REFERENCE(CallerDefaultArgument); PASS_THROUGH_REFERENCE(BindOptional, getSubExpr); PASS_THROUGH_REFERENCE(OptionalEvaluation, getSubExpr); @@ -324,8 +324,8 @@ ConcreteDeclRef Expr::getReferencedDecl() const { NO_REFERENCE(Binary); NO_REFERENCE(DotSyntaxCall); NO_REFERENCE(MakeTemporarilyEscapable); + NO_REFERENCE(ConstructorRefCall); - PASS_THROUGH_REFERENCE(ConstructorRefCall, getFn); PASS_THROUGH_REFERENCE(Load, getSubExpr); NO_REFERENCE(DestructureTuple); NO_REFERENCE(UnresolvedTypeConversion); @@ -620,7 +620,6 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const { case ExprKind::RebindSelfInConstructor: case ExprKind::OpaqueValue: case ExprKind::DefaultArgument: - case ExprKind::CallerDefaultArgument: case ExprKind::BindOptional: case ExprKind::OptionalEvaluation: return false; @@ -721,56 +720,6 @@ llvm::DenseMap Expr::getParentMap() { return parentMap; } -llvm::DenseMap> Expr::getDepthMap() { - class RecordingTraversal : public ASTWalker { - public: - llvm::DenseMap> &DepthMap; - unsigned Depth = 0; - - explicit RecordingTraversal( - llvm::DenseMap> &depthMap) - : DepthMap(depthMap) {} - - std::pair walkToExprPre(Expr *E) override { - DepthMap[E] = {Depth, Parent.getAsExpr()}; - Depth++; - return { true, E }; - } - - Expr *walkToExprPost(Expr *E) override { - Depth--; - return E; - } - }; - - llvm::DenseMap> depthMap; - RecordingTraversal traversal(depthMap); - walk(traversal); - return depthMap; -} - -llvm::DenseMap Expr::getPreorderIndexMap() { - class RecordingTraversal : public ASTWalker { - public: - llvm::DenseMap &IndexMap; - unsigned Index = 0; - - explicit RecordingTraversal(llvm::DenseMap &indexMap) - : IndexMap(indexMap) { } - - std::pair walkToExprPre(Expr *E) override { - IndexMap[E] = Index; - Index++; - return { true, E }; - } - }; - - llvm::DenseMap indexMap; - RecordingTraversal traversal(indexMap); - walk(traversal); - return indexMap; -} - //===----------------------------------------------------------------------===// // Support methods for Exprs. //===----------------------------------------------------------------------===// @@ -1230,6 +1179,17 @@ UnresolvedSpecializeExpr *UnresolvedSpecializeExpr::create(ASTContext &ctx, UnresolvedParams, RAngleLoc); } +bool CaptureListEntry::isSimpleSelfCapture() const { + if (Init->getPatternList().size() != 1) + return false; + if (auto *DRE = dyn_cast(Init->getInit(0))) + if (auto *VD = dyn_cast(DRE->getDecl())) { + return (VD->isSelfParameter() || VD->isSelfParamCapture()) + && VD->getName() == Var->getName(); + } + return false; +} + CaptureListExpr *CaptureListExpr::create(ASTContext &ctx, ArrayRef captureList, ClosureExpr *closureBody) { @@ -1432,6 +1392,23 @@ static ValueDecl *getCalledValue(Expr *E) { return nullptr; } +const ParamDecl *DefaultArgumentExpr::getParamDecl() const { + return getParameterAt(DefaultArgsOwner.getDecl(), ParamIndex); +} + +bool DefaultArgumentExpr::isCallerSide() const { + return getParamDecl()->hasCallerSideDefaultExpr(); +} + +Expr *DefaultArgumentExpr::getCallerSideDefaultExpr() const { + assert(isCallerSide()); + auto &ctx = DefaultArgsOwner.getDecl()->getASTContext(); + auto *mutableThis = const_cast(this); + return evaluateOrDefault(ctx.evaluator, + CallerSideDefaultArgExprRequest{mutableThis}, + new (ctx) ErrorExpr(getSourceRange(), getType())); +} + ValueDecl *ApplyExpr::getCalledValue() const { return ::getCalledValue(Fn); } @@ -1538,36 +1515,9 @@ DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, hasTrailingClosure, decl, implicit); } -DynamicSubscriptExpr * -DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, SourceLoc lSquareLoc, - ArrayRef indexArgs, - ArrayRef indexArgLabels, - ArrayRef indexArgLabelLocs, - SourceLoc rSquareLoc, - Expr *trailingClosure, - ConcreteDeclRef decl, - bool implicit) { - SmallVector indexArgLabelsScratch; - SmallVector indexArgLabelLocsScratch; - Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, - indexArgLabelLocs, rSquareLoc, - trailingClosure, implicit, - indexArgLabelsScratch, - indexArgLabelLocsScratch); - - size_t size = totalSizeToAlloc(indexArgLabels, indexArgLabelLocs, - trailingClosure != nullptr); - - void *memory = ctx.Allocate(size, alignof(DynamicSubscriptExpr)); - return new (memory) DynamicSubscriptExpr(base, index, indexArgLabels, - indexArgLabelLocs, - trailingClosure != nullptr, - decl, implicit); -} - UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, - DeclName name, Expr *argument, + DeclNameRef name, Expr *argument, ArrayRef argLabels, ArrayRef argLabelLocs, bool hasTrailingClosure, @@ -1584,7 +1534,7 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, UnresolvedMemberExpr *UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, DeclNameLoc nameLoc, - DeclName name, + DeclNameRef name, bool implicit) { size_t size = totalSizeToAlloc({ }, { }, /*hasTrailingClosure=*/false); @@ -1597,7 +1547,7 @@ UnresolvedMemberExpr *UnresolvedMemberExpr::create(ASTContext &ctx, UnresolvedMemberExpr * UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclName name, + DeclNameLoc nameLoc, DeclNameRef name, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, @@ -1854,7 +1804,7 @@ FORWARD_SOURCE_LOCS_TO(ClosureExpr, Body.getPointer()) Expr *ClosureExpr::getSingleExpressionBody() const { assert(hasSingleExpressionBody() && "Not a single-expression body"); - auto body = getBody()->getElement(0); + auto body = getBody()->getFirstElement(); if (body.is()) return cast(body.get())->getResult(); return body.get(); @@ -1862,16 +1812,22 @@ Expr *ClosureExpr::getSingleExpressionBody() const { void ClosureExpr::setSingleExpressionBody(Expr *NewBody) { assert(hasSingleExpressionBody() && "Not a single-expression body"); - auto body = getBody()->getElement(0); + auto body = getBody()->getFirstElement(); if (body.is()) { cast(body.get())->setResult(NewBody); return; } - getBody()->setElement(0, NewBody); + getBody()->setFirstElement(NewBody); } bool ClosureExpr::hasEmptyBody() const { - return getBody()->getNumElements() == 0; + return getBody()->empty(); +} + +bool ClosureExpr::capturesSelfEnablingImplictSelf() const { + if (auto *VD = getCapturedSelfDecl()) + return VD->isSelfParamCapture() && !VD->getType()->is(); + return false; } FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body) @@ -1883,7 +1839,7 @@ void AutoClosureExpr::setBody(Expr *E) { } Expr *AutoClosureExpr::getSingleExpressionBody() const { - return cast(Body->getElement(0).get())->getResult(); + return cast(Body->getFirstElement().get())->getResult(); } FORWARD_SOURCE_LOCS_TO(UnresolvedPatternExpr, subPattern) @@ -1916,12 +1872,12 @@ Type TypeExpr::getInstanceType( } -TypeExpr *TypeExpr::createForDecl(SourceLoc Loc, TypeDecl *Decl, +TypeExpr *TypeExpr::createForDecl(DeclNameLoc Loc, TypeDecl *Decl, DeclContext *DC, bool isImplicit) { ASTContext &C = Decl->getASTContext(); assert(Loc.isValid() || isImplicit); - auto *Repr = new (C) SimpleIdentTypeRepr(Loc, Decl->getName()); + auto *Repr = new (C) SimpleIdentTypeRepr(Loc, Decl->createNameRef()); Repr->setValue(Decl, DC); auto result = new (C) TypeExpr(TypeLoc(Repr, Type())); if (isImplicit) @@ -1929,9 +1885,9 @@ TypeExpr *TypeExpr::createForDecl(SourceLoc Loc, TypeDecl *Decl, return result; } -TypeExpr *TypeExpr::createForMemberDecl(SourceLoc ParentNameLoc, +TypeExpr *TypeExpr::createForMemberDecl(DeclNameLoc ParentNameLoc, TypeDecl *Parent, - SourceLoc NameLoc, + DeclNameLoc NameLoc, TypeDecl *Decl) { ASTContext &C = Decl->getASTContext(); assert(ParentNameLoc.isValid()); @@ -1942,13 +1898,13 @@ TypeExpr *TypeExpr::createForMemberDecl(SourceLoc ParentNameLoc, // The first component is the parent type. auto *ParentComp = new (C) SimpleIdentTypeRepr(ParentNameLoc, - Parent->getName()); + Parent->createNameRef()); ParentComp->setValue(Parent, nullptr); Components.push_back(ParentComp); // The second component is the member we just found. auto *NewComp = new (C) SimpleIdentTypeRepr(NameLoc, - Decl->getName()); + Decl->createNameRef()); NewComp->setValue(Decl, nullptr); Components.push_back(NewComp); @@ -1957,7 +1913,7 @@ TypeExpr *TypeExpr::createForMemberDecl(SourceLoc ParentNameLoc, } TypeExpr *TypeExpr::createForMemberDecl(IdentTypeRepr *ParentTR, - SourceLoc NameLoc, + DeclNameLoc NameLoc, TypeDecl *Decl) { ASTContext &C = Decl->getASTContext(); @@ -1969,7 +1925,7 @@ TypeExpr *TypeExpr::createForMemberDecl(IdentTypeRepr *ParentTR, assert(!Components.empty()); // Add a new component for the member we just found. - auto *NewComp = new (C) SimpleIdentTypeRepr(NameLoc, Decl->getName()); + auto *NewComp = new (C) SimpleIdentTypeRepr(NameLoc, Decl->createNameRef()); NewComp->setValue(Decl, nullptr); Components.push_back(NewComp); @@ -2017,7 +1973,7 @@ TypeExpr *TypeExpr::createForSpecializedDecl(IdentTypeRepr *ParentTR, } auto *genericComp = GenericIdentTypeRepr::create(C, - last->getIdLoc(), last->getIdentifier(), + last->getNameLoc(), last->getNameRef(), Args, AngleLocs); genericComp->setValue(last->getBoundDecl(), last->getDeclContext()); components.push_back(genericComp); @@ -2206,7 +2162,7 @@ void InterpolatedStringLiteralExpr::forEachSegment(ASTContext &Ctx, name = fn->getFullName(); } else if (auto unresolvedDot = dyn_cast(call->getFn())) { - name = unresolvedDot->getName(); + name = unresolvedDot->getName().getFullName(); } bool isInterpolation = (name.getBaseName() == @@ -2222,14 +2178,14 @@ TapExpr::TapExpr(Expr * SubExpr, BraceStmt *Body) : Expr(ExprKind::Tap, /*Implicit=*/true), SubExpr(SubExpr), Body(Body) { if (Body) { - assert(Body->getNumElements() > 0 && - Body->getElement(0).isDecl(DeclKind::Var) && + assert(!Body->empty() && + Body->getFirstElement().isDecl(DeclKind::Var) && "First element of Body should be a variable to init with the subExpr"); } } VarDecl * TapExpr::getVar() const { - return dyn_cast(Body->getElement(0).dyn_cast()); + return dyn_cast(Body->getFirstElement().dyn_cast()); } SourceLoc TapExpr::getEndLoc() const { @@ -2246,6 +2202,36 @@ SourceLoc TapExpr::getEndLoc() const { return SourceLoc(); } +void swift::simple_display(llvm::raw_ostream &out, const ClosureExpr *CE) { + if (!CE) { + out << "(null)"; + return; + } + + if (CE->hasSingleExpressionBody()) { + out << "single expression closure"; + } else { + out << "closure"; + } +} + +void swift::simple_display(llvm::raw_ostream &out, + const DefaultArgumentExpr *expr) { + if (!expr) { + out << "(null)"; + return; + } + + out << "default arg for param "; + out << "#" << expr->getParamIndex() + 1 << " "; + out << "of "; + simple_display(out, expr->getDefaultArgsOwner().getDecl()); +} + +SourceLoc swift::extractNearestSourceLoc(const DefaultArgumentExpr *expr) { + return expr->getLoc(); +} + // See swift/Basic/Statistic.h for declaration: this enables tracing Exprs, is // defined here to avoid too much layering violation / circular linkage // dependency. diff --git a/lib/AST/ExperimentalDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp similarity index 72% rename from lib/AST/ExperimentalDependencies.cpp rename to lib/AST/FineGrainedDependencies.cpp index bfdea7748b74f..69256f1cffc93 100644 --- a/lib/AST/ExperimentalDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -1,4 +1,4 @@ -//===--- ExperimentalDependencies.cpp - Generates swiftdeps files ---------===// +//===---- FineGrainedDependencies.cpp - Generates swiftdeps files ---------===// // // This source file is part of the Swift.org open source project // @@ -12,7 +12,7 @@ #include -#include "swift/AST/ExperimentalDependencies.h" +#include "swift/AST/FineGrainedDependencies.h" // may not all be needed #include "swift/Basic/FileSystem.h" @@ -26,13 +26,13 @@ #include "llvm/Support/Path.h" #include "llvm/Support/YAMLParser.h" -// This file holds the definitions for the experimental dependency system +// This file holds the definitions for the fine-grained dependency system // that are likely to be stable as it moves away from the status quo. // These include the graph structures common to both programs and also // the frontend graph, which must be read by the driver. using namespace swift; -using namespace experimental_dependencies; +using namespace fine_grained_dependencies; //============================================================================== // MARK: SourceFileDepGraph access @@ -49,19 +49,18 @@ SourceFileDepGraph::getNode(size_t sequenceNumber) const { InterfaceAndImplementationPair SourceFileDepGraph::getSourceFileNodePair() const { - assert(getNode(0)->getKey().getKind() == NodeKind::sourceFileProvide && - "First node must be sourceFileProvide."); - assert(getNode(1)->getKey().getKind() == NodeKind::sourceFileProvide && - "Second node must be sourceFileProvide."); - return InterfaceAndImplementationPair(getNode(0), - getNode(1)); + return InterfaceAndImplementationPair( + getNode( + SourceFileDepGraphNode::sourceFileProvidesInterfaceSequenceNumber), + getNode(SourceFileDepGraphNode:: + sourceFileProvidesImplementationSequenceNumber)); } -StringRef SourceFileDepGraph::getSwiftDepsFromSourceFileProvide() const { +StringRef SourceFileDepGraph::getSwiftDepsOfJobThatProducedThisGraph() const { return getSourceFileNodePair() .getInterface() ->getKey() - .getSwiftDepsFromSourceFileProvide(); + .getSwiftDepsFromASourceFileProvideNodeKey(); } void SourceFileDepGraph::forEachArc( @@ -77,22 +76,40 @@ void SourceFileDepGraph::forEachArc( InterfaceAndImplementationPair SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew( - NodeKind k, StringRef context, StringRef name, - Optional fingerprint) { + NodeKind k, const ContextNameFingerprint &contextNameFingerprint) { + const std::string &context = std::get<0>(contextNameFingerprint); + const std::string &name = std::get<1>(contextNameFingerprint); + const Optional &fingerprint = + std::get<2>(contextNameFingerprint); + auto *interfaceNode = findExistingNodeOrCreateIfNew( + DependencyKey(k, DeclAspect::interface, context, name), fingerprint, + true /* = isProvides */); + auto *implementationNode = findExistingNodeOrCreateIfNew( + DependencyKey(k, DeclAspect::implementation, context, name), fingerprint, + true /* = isProvides */); + InterfaceAndImplementationPair nodePair{ - findExistingNodeOrCreateIfNew( - DependencyKey(k, DeclAspect::interface, context, name), fingerprint, - true /* = isProvides */), - findExistingNodeOrCreateIfNew( - DependencyKey(k, DeclAspect::implementation, context, name), - fingerprint, true /* = isProvides */)}; - // if interface changes, have to rebuild implementation - addArc(nodePair.getInterface(), nodePair.getImplementation()); + interfaceNode, implementationNode}; + + // if interface changes, have to rebuild implementation. + // This dependency used to be represented by + // addArc(nodePair.getInterface(), nodePair.getImplementation()); + // However, recall that the dependency scheme as of 1/2020 chunks + // declarations together by base name. + // So if the arc were added, a dirtying of a same-based-named interface + // in a different file would dirty the implementation in this file, + // causing the needless recompilation of this file. + // But, if an arc is added for this, then *any* change that causes + // a same-named interface to be dirty will dirty this implementation, + // even if that interface is in another file. + // Therefor no such arc is added here, and any dirtying of either + // the interface or implementation of this declaration will cause + // the driver to recompile this source file. return nodePair; } SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( - DependencyKey key, Optional fingerprint, + DependencyKey key, const Optional &fingerprint, const bool isProvides) { SourceFileDepGraphNode *result = memoizedNodes.findExistingOrCreateIfNew( key, [&](DependencyKey key) -> SourceFileDepGraphNode * { @@ -101,11 +118,22 @@ SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew( addNode(n); return n; }); + assert(result->getKey() == key && "Keys must match."); + if (!isProvides) + return result; // If have provides and depends with same key, result is one node that // isProvides - if (isProvides) + if (!result->getIsProvides() && fingerprint) { result->setIsProvides(); - assert(result->getKey() == key && "Keys must match."); + assert(!result->getFingerprint() && "Depends should not have fingerprints"); + result->setFingerprint(fingerprint); + return result; + } + // If there are two Decls with same base name but differ only in fingerprint, + // since we won't be able to tell which Decl is depended-upon (is this right?) + // just use the one node, but erase its print: + if (fingerprint != result->getFingerprint()) + result->setFingerprint(None); return result; } @@ -113,6 +141,18 @@ std::string DependencyKey::demangleTypeAsContext(StringRef s) { return swift::Demangle::demangleTypeAsString(s.str()); } +DependencyKey +DependencyKey::createKeyForWholeSourceFile(const StringRef swiftDeps) { + assert(!swiftDeps.empty()); + const auto context = DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps); + const auto name = + DependencyKey::computeNameForProvidedEntity( + swiftDeps); + return DependencyKey(NodeKind::sourceFileProvide, DeclAspect::interface, + context, name); +} + //============================================================================== // MARK: Debugging //============================================================================== @@ -133,9 +173,9 @@ bool SourceFileDepGraph::verify() const { auto iterInserted = nodesSeen.insert(std::make_pair(n->getKey(), n)); if (!iterInserted.second) { llvm::errs() << "Duplicate frontend keys: "; - iterInserted.first->second->dump(); + iterInserted.first->second->dump(llvm::errs()); llvm::errs() << " and "; - n->dump(); + n->dump(llvm::errs()); llvm::errs() << "\n"; llvm_unreachable("duplicate frontend keys"); } @@ -175,13 +215,12 @@ std::string DependencyKey::humanReadableName() const { } std::string DependencyKey::asString() const { - return NodeKindNames[size_t(kind)] + " " + - "aspect: " + DeclAspectNames[size_t(aspect)] + ", " + - humanReadableName(); + return NodeKindNames[size_t(kind)] + " " + "aspect: " + aspectName().str() + + ", " + humanReadableName(); } /// Needed for TwoStageMap::verify: -raw_ostream &experimental_dependencies::operator<<(raw_ostream &out, +raw_ostream &fine_grained_dependencies::operator<<(raw_ostream &out, const DependencyKey &key) { out << key.asString(); return out; @@ -231,7 +270,11 @@ void DependencyKey::verifyDeclAspectNames() { } void DepGraphNode::dump() const { - key.dump(); + dump(llvm::errs()); +} + +void DepGraphNode::dump(raw_ostream &os) const { + key.dump(os); if (fingerprint.hasValue()) llvm::errs() << "fingerprint: " << fingerprint.getValue() << ""; else @@ -275,9 +318,9 @@ StringRef ScalarTraits::input(StringRef scalar, void *ctxt, } #endif -void ScalarEnumerationTraits:: - enumeration(IO &io, swift::experimental_dependencies::NodeKind &value) { - using NodeKind = swift::experimental_dependencies::NodeKind; +void ScalarEnumerationTraits:: + enumeration(IO &io, swift::fine_grained_dependencies::NodeKind &value) { + using NodeKind = swift::fine_grained_dependencies::NodeKind; io.enumCase(value, "topLevel", NodeKind::topLevel); io.enumCase(value, "nominal", NodeKind::nominal); io.enumCase(value, "potentialMember", NodeKind::potentialMember); @@ -288,14 +331,14 @@ void ScalarEnumerationTraits:: } void ScalarEnumerationTraits::enumeration( - IO &io, swift::experimental_dependencies::DeclAspect &value) { - using DeclAspect = swift::experimental_dependencies::DeclAspect; + IO &io, swift::fine_grained_dependencies::DeclAspect &value) { + using DeclAspect = swift::fine_grained_dependencies::DeclAspect; io.enumCase(value, "interface", DeclAspect::interface); io.enumCase(value, "implementation", DeclAspect::implementation); } void MappingTraits::mapping( - IO &io, swift::experimental_dependencies::DependencyKey &key) { + IO &io, swift::fine_grained_dependencies::DependencyKey &key) { io.mapRequired("kind", key.kind); io.mapRequired("aspect", key.aspect); io.mapRequired("context", key.context); @@ -303,7 +346,7 @@ void MappingTraits::mapping( } void MappingTraits::mapping( - IO &io, swift::experimental_dependencies::DepGraphNode &node) { + IO &io, swift::fine_grained_dependencies::DepGraphNode &node) { io.mapRequired("key", node.key); io.mapOptional("fingerprint", node.fingerprint); } diff --git a/lib/AST/ExperimentalDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp similarity index 56% rename from lib/AST/ExperimentalDependenciesSourceFileDepGraphConstructor.cpp rename to lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 4b9be295c3263..eba5fe33192bb 100644 --- a/lib/AST/ExperimentalDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -1,4 +1,4 @@ -//===--- ExperimentalDependenciesSourceFileDepGraphConstructor.cpp --------===// +//===--- FineGrainedDependenciesSourceFileDepGraphConstructor.cpp ---------===// // // This source file is part of the Swift.org open source project // @@ -19,8 +19,8 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/ExistentialLayout.h" -#include "swift/AST/ExperimentalDependencies.h" #include "swift/AST/FileSystem.h" +#include "swift/AST/FineGrainedDependencies.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" @@ -28,6 +28,7 @@ #include "swift/AST/Types.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Demangling/Demangle.h" #include "swift/Frontend/FrontendOptions.h" #include "llvm/ADT/MapVector.h" @@ -43,7 +44,7 @@ // source files require recompilation. using namespace swift; -using namespace experimental_dependencies; +using namespace fine_grained_dependencies; //============================================================================== // MARK: Emitting and reading SourceFileDepGraph @@ -191,7 +192,7 @@ struct SourceFileDeclFinder { // clang-format off SourceFileDeclFinder(const SourceFile *const SF, const bool includePrivateDecls) : includePrivateDecls(includePrivateDecls) { - for (const Decl *const D : SF->Decls) { + for (const Decl *const D : SF->getTopLevelDecls()) { select(D, extensions, false) || select(D, operators, false) || @@ -214,8 +215,11 @@ struct SourceFileDeclFinder { private: /// Extensions may contain nominals and operators. void findNominalsFromExtensions() { - for (auto *ED : extensions) - findNominalsAndOperatorsIn(ED->getExtendedNominal(), ED); + for (auto *ED : extensions) { + const auto *const NTD = ED->getExtendedNominal(); + if (NTD) + findNominalsAndOperatorsIn(NTD, ED); + } } /// Top-level nominals may contain nominals and operators. void findNominalsInTopNominals() { @@ -260,16 +264,19 @@ struct SourceFileDeclFinder { /// Extensions may contain ValueDecls. void findValuesInExtensions() { for (const auto *ED : extensions) { - if (excludeIfPrivate(ED->getExtendedNominal())) + const auto *const NTD = ED->getExtendedNominal(); + if (!NTD || excludeIfPrivate(NTD)) continue; if (!includePrivateDecls && (!allInheritedProtocolsArePrivate(ED) || allMembersArePrivate(ED))) continue; for (const auto *member : ED->getMembers()) if (const auto *VD = dyn_cast(member)) - if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) - valuesInExtensions.push_back( - std::make_pair(ED->getExtendedNominal(), VD)); + if (VD->hasName() && (includePrivateDecls || !declIsPrivate(VD))) { + const auto *const NTD = ED->getExtendedNominal(); + if (NTD) + valuesInExtensions.push_back(std::make_pair(NTD, VD)); + } } } @@ -354,6 +361,12 @@ std::string DependencyKey::computeContextForProvidedEntity< return mangleTypeAsContext(holderAndMember.first); } +// Linux compiler requires the following: +template +std::string +DependencyKey::computeContextForProvidedEntity(StringRef); + //============================================================================== // MARK: computeNameForProvidedEntity //============================================================================== @@ -424,46 +437,38 @@ std::string DependencyKey::computeNameForProvidedEntity< template <> DependencyKey -DependencyKey::createDependedUponKey( - const DeclBaseName &dbn) { - return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", - dbn.userFacingName()); +DependencyKey::createDependedUponKey(StringRef name) { + return DependencyKey(NodeKind::topLevel, DeclAspect::interface, "", name); } template <> DependencyKey -DependencyKey::createDependedUponKey( - const DeclBaseName &dbn) { +DependencyKey::createDependedUponKey(StringRef name) { return DependencyKey(NodeKind::dynamicLookup, DeclAspect::interface, "", - dbn.userFacingName()); + name); } template <> -DependencyKey DependencyKey::createDependedUponKey< - NodeKind::nominal, std::pair>( - const std::pair &p) { - return DependencyKey(NodeKind::nominal, DeclAspect::interface, - mangleTypeAsContext(p.first), ""); +DependencyKey +DependencyKey::createDependedUponKey(StringRef name) { + return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", + name); } template <> -DependencyKey DependencyKey::createDependedUponKey< - NodeKind::member, std::pair>( - const std::pair &p) { - const bool isMemberBlank = p.second.empty(); - const auto kind = - isMemberBlank ? NodeKind::potentialMember : NodeKind::member; - return DependencyKey(kind, DeclAspect::interface, - mangleTypeAsContext(p.first), - isMemberBlank ? "" : p.second.userFacingName()); +DependencyKey +DependencyKey::createDependedUponKey(StringRef mangledName) { + return DependencyKey(NodeKind::nominal, DeclAspect::interface, mangledName, + ""); } -template <> -DependencyKey -DependencyKey::createDependedUponKey( - const std::string &file) { - return DependencyKey(NodeKind::externalDepend, DeclAspect::interface, "", - file); +DependencyKey DependencyKey::createDependedUponKey(StringRef mangledHolderName, + StringRef memberBaseName) { + const bool isMemberBlank = memberBaseName.empty(); + const auto kind = + isMemberBlank ? NodeKind::potentialMember : NodeKind::member; + return DependencyKey(kind, DeclAspect::interface, mangledHolderName, + isMemberBlank ? "" : memberBaseName); } //============================================================================== @@ -475,12 +480,6 @@ namespace { /// Reads the information provided by the frontend and builds the /// SourceFileDepGraph class SourceFileDepGraphConstructor { - /// The SourceFile containing the Decls. - SourceFile *SF; - - /// Furnishes depended-upon names resulting from lookups. - const DependencyTracker &depTracker; - /// Name of the swiftDeps file, for inclusion in the constructed graph. StringRef swiftDeps; // TODO rm? @@ -491,18 +490,136 @@ class SourceFileDepGraphConstructor { /// If there was an error, cannot get accurate info. const bool hadCompilationError; + /// Functions as the fingerprint of the entire file + const std::string interfaceHash; + + /// Top-level base names of decls that are depended-upon and a flag indicating + /// if the dependency "cascades" + const std::vector> topLevelDepends; + + /// A mangled nominal name and the member base name that are depended-upon, + /// a flag indicating if the member is private to its enclosing file, and + /// a flag indicating if the dependency cascades. + const std::vector, bool>> + memberDepends; + + /// The base name of a class member depended-upon for dynamic lookup, and a + /// cascades flag. + const std::vector> dynamicLookupDepends; + + /// The paths of swiftdeps files of other modules that are depended-upon. + const std::vector externalDependencies; + + /// Provided names + std::vector precedenceGroups; + std::vector memberOperatorDecls; + std::vector operators; + std::vector topNominals; + std::vector topValues; + std::vector allNominals; + std::vector potentialMemberHolders; + std::vector valuesInExtensions; + std::vector classMembers; + /// Graph under construction SourceFileDepGraph g; public: - SourceFileDepGraphConstructor(SourceFile *SF, + /// Expose this layer to enable faking up a constructor for testing. + /// See the instance variable comments for explanation. + // clang-format off + SourceFileDepGraphConstructor( + StringRef swiftDeps, + bool includePrivateDeps, + bool hadCompilationError, + const std::string &interfaceHash, + ArrayRef> topLevelDepends, + ArrayRef, bool>> memberDepends, + ArrayRef> dynamicLookupDepends, + ArrayRef externalDependencies, + + ArrayRef precedenceGroups, + ArrayRef memberOperatorDecls, + ArrayRef operators, + ArrayRef topNominals, + ArrayRef topValues, + ArrayRef allNominals, + ArrayRef potentialMemberHolders, + ArrayRef valuesInExtensions, + ArrayRef classMembers + ) : + swiftDeps(swiftDeps), + includePrivateDeps(includePrivateDeps), + hadCompilationError(hadCompilationError), + + interfaceHash(interfaceHash), + topLevelDepends(topLevelDepends), + memberDepends(memberDepends), + dynamicLookupDepends(dynamicLookupDepends), + externalDependencies(externalDependencies), + + precedenceGroups(precedenceGroups), + memberOperatorDecls(memberOperatorDecls), + operators(operators), + topNominals(topNominals), + topValues(topValues), + allNominals(allNominals), + potentialMemberHolders(potentialMemberHolders), + valuesInExtensions(valuesInExtensions), + classMembers(classMembers) + {} + + SourceFileDepGraphConstructor static forSourceFile(SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, const bool includePrivateDeps, - const bool hadCompilationError) - : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), - includePrivateDeps(includePrivateDeps), - hadCompilationError(hadCompilationError) {} + const bool hadCompilationError) { + + SourceFileDeclFinder declFinder(SF, includePrivateDeps); + std::vector> topLevelDepends; + for (const auto &p: SF->getReferencedNameTracker()->getTopLevelNames()) + topLevelDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); + + std::vector> dynamicLookupDepends; + for (const auto &p: SF->getReferencedNameTracker()->getDynamicLookupNames()) + dynamicLookupDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); + + std::vector, bool>> memberDepends; + for (const auto &p: SF->getReferencedNameTracker()->getUsedMembers()) { + const auto &member = p.getFirst().second; + StringRef emptyOrUserFacingName = member.empty() ? "" : member.userFacingName(); + memberDepends.push_back( + std::make_pair( + std::make_tuple( + mangleTypeAsContext(p.getFirst().first), + emptyOrUserFacingName, + declIsPrivate(p.getFirst().first)), + p.getSecond())); + } + + return SourceFileDepGraphConstructor( + swiftDeps, + includePrivateDeps, + hadCompilationError, + + getInterfaceHash(SF), + topLevelDepends, + memberDepends, + dynamicLookupDepends, + depTracker.getDependencies(), + + namesForProvidersOfAGivenType(declFinder.precedenceGroups), + namesForProvidersOfAGivenType(declFinder.memberOperatorDecls), + namesForProvidersOfAGivenType(declFinder.operators), + namesForProvidersOfAGivenType(declFinder.topNominals), + namesForProvidersOfAGivenType(declFinder.topValues), + namesForProvidersOfAGivenType(declFinder.allNominals), + namesForProvidersOfAGivenType(declFinder.potentialMemberHolders), + namesForProvidersOfAGivenType(declFinder.valuesInExtensions), + namesForProvidersOfAGivenType(declFinder.classMembers) + ); + } + // clang-format on /// Construct the graph and return it. SourceFileDepGraph construct() { @@ -517,7 +634,7 @@ class SourceFileDepGraphConstructor { } private: - std::string getSourceFileFingerprint() const { return getInterfaceHash(SF); } + std::string getSourceFileFingerprint() const { return interfaceHash; } static std::string getInterfaceHash(SourceFile *SF) { llvm::SmallString<32> interfaceHash; @@ -533,32 +650,48 @@ class SourceFileDepGraphConstructor { void addDependencyArcsToGraph(); /// Given an array of Decls or pairs of them in \p declsOrPairs - /// create nodes if needed and add the new nodes to the graph. + /// create string pairs for context and name template - void addAllProviderNodesOfAGivenType(std::vector &contentsVec) { - for (const auto declOrPair : contentsVec) { - // No fingerprints for providers (Decls) yet. - // Someday ... - const Optional fingerprint = None; - auto p = g.findExistingNodePairOrCreateAndAddIfNew( - kind, + static std::vector + namesForProvidersOfAGivenType(std::vector &contentsVec) { + std::vector result; + for (const auto declOrPair : contentsVec) + result.push_back( + std::make_tuple( DependencyKey::computeContextForProvidedEntity(declOrPair), DependencyKey::computeNameForProvidedEntity(declOrPair), - fingerprint); - // Since we don't have fingerprints yet, must rebuild every provider when - // interfaceHash changes. So when interface (i.e. interface hash) of - // sourceFile changes, every provides is dirty. And since we don't know - // what happened, dirtyness might affect the interface. - if (!p.getInterface()->getFingerprint().hasValue()) - g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); + getFingerprintIfAny(declOrPair))); + return result; + } + + static Optional + getFingerprintIfAny(std::pair) { + return None; + } + static Optional getFingerprintIfAny(const Decl *d) { + if (const auto *idc = dyn_cast(d)) + return idc->getBodyFingerprint(); + return None; + } + + template + void addAllProviderNodesOfAGivenType( + ArrayRef contextNameFingerprints) { + for (const auto &contextNameFingerprint : contextNameFingerprints) { + auto p = g.findExistingNodePairOrCreateAndAddIfNew( + kind, contextNameFingerprint); + // Since the current type fingerprints only include tokens in the body, + // when the interface hash changes, it is possible that the type in the + // file has changed. + g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); } } /// Given a map of names and isCascades, add the resulting dependencies to the /// graph. template - void addAllDependenciesFrom(const llvm::DenseMap &map) { - for (const auto &p : map) + void addAllDependenciesFrom(ArrayRef> names) { + for (const auto &p : names) recordThatThisWholeFileDependsOn( DependencyKey::createDependedUponKey(p.first), p.second); } @@ -566,8 +699,7 @@ class SourceFileDepGraphConstructor { /// Given a map of holder-and-member-names and isCascades, add the resulting /// dependencies to the graph. void addAllDependenciesFrom( - const llvm::DenseMap, - bool> &); + ArrayRef, bool>>); /// Given an array of external swiftDeps files, add the resulting external /// dependencies to the graph. @@ -587,28 +719,27 @@ class SourceFileDepGraphConstructor { }; } // namespace -using UsedMembersMap = - llvm::DenseMap, bool>; void SourceFileDepGraphConstructor::addAllDependenciesFrom( - const UsedMembersMap &map) { - - UsedMembersMap filteredMap; - for (const auto &entry : map) - if (includePrivateDeps || !declIsPrivate(entry.first.first)) - filteredMap[entry.getFirst()] = entry.getSecond(); + ArrayRef, bool>> + members) { - std::unordered_set holdersOfCascadingMembers; - for (auto &entry : filteredMap) + llvm::StringSet<> holdersOfCascadingMembers; + for (const auto &entry : members) { + if (!includePrivateDeps && std::get<2>(entry.first)) + continue; if (entry.second) - holdersOfCascadingMembers.insert(entry.first.first); - - for (auto &entry : filteredMap) { - // mangles twice in the name of symmetry + holdersOfCascadingMembers.insert(std::get<0>(entry.first)); + } + for (const auto &entry : members) { + if (!includePrivateDeps && std::get<2>(entry.first)) + continue; recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey(entry.first), - holdersOfCascadingMembers.count(entry.first.first) != 0); + DependencyKey::createDependedUponKey( + std::get<0>(entry.first)), + holdersOfCascadingMembers.count(std::get<0>(entry.first)) != 0); recordThatThisWholeFileDependsOn( - DependencyKey::createDependedUponKey(entry.first), + DependencyKey::createDependedUponKey(std::get<0>(entry.first), + std::get<1>(entry.first)), entry.second); } } @@ -620,47 +751,40 @@ void SourceFileDepGraphConstructor::addAllDependenciesFrom( void SourceFileDepGraphConstructor::addSourceFileNodesToGraph() { g.findExistingNodePairOrCreateAndAddIfNew( NodeKind::sourceFileProvide, - DependencyKey::computeContextForProvidedEntity< - NodeKind::sourceFileProvide>(swiftDeps), - DependencyKey::computeNameForProvidedEntity( - swiftDeps), - getSourceFileFingerprint()); + ContextNameFingerprint(DependencyKey::computeContextForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + DependencyKey::computeNameForProvidedEntity< + NodeKind::sourceFileProvide>(swiftDeps), + getSourceFileFingerprint())); } void SourceFileDepGraphConstructor::addProviderNodesToGraph() { - SourceFileDeclFinder declFinder(SF, includePrivateDeps); // TODO: express the multiple provides and depends streams with variadic // templates // Many kinds of Decls become top-level depends. - addAllProviderNodesOfAGivenType( - declFinder.precedenceGroups); - addAllProviderNodesOfAGivenType( - declFinder.memberOperatorDecls); - addAllProviderNodesOfAGivenType(declFinder.operators); - addAllProviderNodesOfAGivenType(declFinder.topNominals); - addAllProviderNodesOfAGivenType(declFinder.topValues); + addAllProviderNodesOfAGivenType(precedenceGroups); + addAllProviderNodesOfAGivenType(memberOperatorDecls); + addAllProviderNodesOfAGivenType(operators); + addAllProviderNodesOfAGivenType(topNominals); + addAllProviderNodesOfAGivenType(topValues); - addAllProviderNodesOfAGivenType(declFinder.allNominals); + addAllProviderNodesOfAGivenType(allNominals); addAllProviderNodesOfAGivenType( - declFinder.potentialMemberHolders); - addAllProviderNodesOfAGivenType( - declFinder.valuesInExtensions); + potentialMemberHolders); + addAllProviderNodesOfAGivenType(valuesInExtensions); - addAllProviderNodesOfAGivenType( - declFinder.classMembers); + addAllProviderNodesOfAGivenType(classMembers); } void SourceFileDepGraphConstructor::addDependencyArcsToGraph() { // TODO: express the multiple provides and depends streams with variadic // templates - addAllDependenciesFrom( - SF->getReferencedNameTracker()->getTopLevelNames()); - addAllDependenciesFrom(SF->getReferencedNameTracker()->getUsedMembers()); - addAllDependenciesFrom( - SF->getReferencedNameTracker()->getDynamicLookupNames()); - addAllDependenciesFrom(depTracker.getDependencies()); + addAllDependenciesFrom(topLevelDepends); + addAllDependenciesFrom(memberDepends); + addAllDependenciesFrom(dynamicLookupDepends); + addAllDependenciesFrom(externalDependencies); } void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( @@ -674,19 +798,27 @@ void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( // Entry point from the Frontend to this whole system //============================================================================== -bool swift::experimental_dependencies::emitReferenceDependencies( +bool swift::fine_grained_dependencies::emitReferenceDependencies( DiagnosticEngine &diags, SourceFile *const SF, - const DependencyTracker &depTracker, StringRef outputPath) { + const DependencyTracker &depTracker, StringRef outputPath, + const bool alsoEmitDotFile) { // Before writing to the dependencies file path, preserve any previous file // that may have been there. No error handling -- this is just a nicety, it // doesn't matter if it fails. llvm::sys::fs::rename(outputPath, outputPath + "~"); + // Since, when fingerprints are enabled, + // the parser diverts token hashing into per-body fingerprints + // before it can know if a difference is in a private type, + // in order to be able to test the changed fingerprints + // we force the inclusion of private declarations when fingerprints + // are enabled. const bool includeIntrafileDeps = - SF->getASTContext().LangOpts.ExperimentalDependenciesIncludeIntrafileOnes; + SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || + SF->getASTContext().LangOpts.EnableTypeFingerprints; const bool hadCompilationError = SF->getASTContext().hadError(); - SourceFileDepGraphConstructor gc(SF, depTracker, outputPath, - includeIntrafileDeps, hadCompilationError); + auto gc = SourceFileDepGraphConstructor::forSourceFile( + SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); SourceFileDepGraph g = gc.construct(); const bool hadError = @@ -697,12 +829,145 @@ bool swift::experimental_dependencies::emitReferenceDependencies( return false; }); - assert(g.verifyReadsWhatIsWritten(outputPath)); + // If path is stdout, cannot read it back, so check for "-" + assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); - std::string dotFileName = outputPath.str() + ".dot"; - withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { - DotFileEmitter(out, g, false, false).emit(); - return false; - }); + if (alsoEmitDotFile) { + std::string dotFileName = outputPath.str() + ".dot"; + withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { + DotFileEmitter(out, g, false, false).emit(); + return false; + }); + } return hadError; } + +//============================================================================== +// Entry point from the unit tests +//============================================================================== +static StringRef stripPrefix(const StringRef name) { + return name.ltrim(SourceFileDepGraph::noncascadingOrPrivatePrefix); +} +static StringRef stripFingerprint(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .first; +} +static StringRef stripName(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .second; +} +static std::string extractName(const StringRef prefixNameFingerprint) { + return stripFingerprint(stripPrefix(prefixNameFingerprint)).str(); +} +static Optional extractFingerprint( + const StringRef prefixNameFingerprint) { + const auto fp = stripName(stripPrefix(prefixNameFingerprint)); + return fp.empty() ? None : Optional(fp.str()); +} + +static std::vector +getBaseNameProvides(ArrayRef simpleNames) { + std::vector result; + for (StringRef n : simpleNames) + result.push_back(ContextNameFingerprint("", extractName(n), + extractFingerprint(n))); + return result; +} + +static std::vector +getMangledHolderProvides(ArrayRef simpleNames) { + std::vector result; + for (StringRef n : simpleNames) + result.push_back(ContextNameFingerprint(extractName(n), "", + extractFingerprint(n))); + return result; +} + +static std::vector getCompoundProvides( + ArrayRef> compoundNames) { + std::vector result; + for (const auto &p : compoundNames) + result.push_back(ContextNameFingerprint(extractName(p.first), + extractName(p.second), + extractFingerprint(p.second))); + return result; +} + +static bool cascades(const std::string &s) { + return s.empty() || s[0] != SourceFileDepGraph::noncascadingOrPrivatePrefix; +} + +// Use '_' as a prefix for a file-private member +static bool isPrivate(const std::string &s) { + return !s.empty() && s[0] == SourceFileDepGraph::noncascadingOrPrivatePrefix; +} + +static std::vector> +getSimpleDepends(ArrayRef simpleNames) { + std::vector> result; + for (std::string n : simpleNames) + result.push_back({stripPrefix(n), cascades((n))}); + return result; +} + +static std::vector +getExternalDepends(ArrayRef simpleNames) { + return simpleNames; +} + +static std::vector, bool>> +getCompoundDepends( + ArrayRef simpleNames, + ArrayRef> compoundNames) { + std::vector, bool>> + result; + for (std::string n : simpleNames) { + // (On Linux, the compiler needs more verbosity than: + // result.push_back({{n, "", false}, cascades(n)}); + result.push_back( + std::make_pair(std::make_tuple(stripPrefix(n), std::string(), false), + cascades(n))); + } + for (auto &p : compoundNames) { + // Likewise, for Linux expand the following out: + // result.push_back( + // {{p.first, p.second, isPrivate(p.second)}, cascades(p.first)}); + result.push_back( + std::make_pair(std::make_tuple(stripPrefix(p.first), + stripPrefix(p.second), + isPrivate(p.second)), + cascades(p.first))); + } + return result; +} + +SourceFileDepGraph SourceFileDepGraph::simulateLoad( + std::string swiftDepsFilename, const bool includePrivateDeps, + const bool hadCompilationError, std::string interfaceHash, + llvm::StringMap> simpleNamesByRDK, + llvm::StringMap>> + compoundNamesByRDK) { + + using namespace reference_dependency_keys; + + // clang-format off + SourceFileDepGraphConstructor c( + swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, + getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), + getCompoundDepends(simpleNamesByRDK[dependsNominal], + compoundNamesByRDK[dependsMember]), + getSimpleDepends(simpleNamesByRDK[dependsDynamicLookup]), + getExternalDepends(simpleNamesByRDK[dependsExternal]), + {}, // precedence groups + {}, // memberOperatorDecls + {}, // operators + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // topNominals + getBaseNameProvides(simpleNamesByRDK[providesTopLevel]), // topValues + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // allNominals + getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // potentialMemberHolders + getCompoundProvides(compoundNamesByRDK[providesMember]), // valuesInExtensions + getBaseNameProvides(simpleNamesByRDK[providesDynamicLookup]) // classMembers + ); + // clang-format on + return c.construct(); +} diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index 845b177cd1497..bbd46afacd7a1 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -167,12 +167,12 @@ GenericSignatureBuilder *GenericSignatureImpl::getGenericSignatureBuilder() { } bool GenericSignatureImpl::isEqual(GenericSignature Other) { - return getCanonicalSignature() == Other.getPointer()->getCanonicalSignature(); + return getCanonicalSignature() == Other.getCanonicalSignature(); } bool GenericSignatureImpl::isCanonical() const { - if (CanonicalSignatureOrASTContext.is()) return true; - + if (CanonicalSignatureOrASTContext.is()) + return true; return getCanonicalSignature().getPointer() == this; } @@ -310,6 +310,14 @@ CanGenericSignature::getCanonical(TypeArrayView params, return CanGenericSignature(canSig); } +CanGenericSignature GenericSignature::getCanonicalSignature() const { + // If the underlying pointer is null, return `CanGenericSignature()`. + if (isNull()) + return CanGenericSignature(); + // Otherwise, return the canonical signature of the underlying pointer. + return getPointer()->getCanonicalSignature(); +} + CanGenericSignature GenericSignatureImpl::getCanonicalSignature() const { // If we haven't computed the canonical signature yet, do so now. if (CanonicalSignatureOrASTContext.isNull()) { @@ -358,8 +366,9 @@ ASTContext &GenericSignatureImpl::getASTContext() const { return GenericSignature::getASTContext(getGenericParams(), getRequirements()); } -Optional -GenericSignatureImpl::lookupConformance(CanType type, ProtocolDecl *proto) const { +ProtocolConformanceRef +GenericSignatureImpl::lookupConformance(CanType type, + ProtocolDecl *proto) const { // FIXME: Actually implement this properly. auto *M = proto->getParentModule(); @@ -813,6 +822,7 @@ getBestRequirementSource(GenericSignatureBuilder &builder, } } + assert(bestSource && "All sources were self-recursive?"); return bestSource; } diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 6c35dd0a6c434..bb58412049496 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -33,6 +33,7 @@ #include "swift/AST/TypeMatcher.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/TypeWalker.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/GraphTraits.h" @@ -198,8 +199,7 @@ class RewritePath { /// Print this path. void print(llvm::raw_ostream &out) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger") { + SWIFT_DEBUG_DUMP { print(llvm::errs()); } @@ -401,8 +401,7 @@ class RewriteTreeNode { return enumerateRulesRec(fn, temporarilyDisableVisitedRule, lhs); } - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Dump the tree. void dump(llvm::raw_ostream &out, bool lastChild = true) const; @@ -1442,6 +1441,10 @@ void RequirementSource::print(llvm::raw_ostream &out, static Type formProtocolRelativeType(ProtocolDecl *proto, Type baseType, Type type) { + // Error case: hand back the erroneous type. + if (type->hasError()) + return type; + // Basis case: we've hit the base potential archetype. if (baseType->isEqual(type)) return proto->getSelfInterfaceType(); @@ -1709,22 +1712,6 @@ bool EquivalenceClass::recordConformanceConstraint( return inserted; } -template -bool Constraint::isSubjectEqualTo(Type type) const { - return getSubjectDependentType({ })->isEqual(type); -} - -template -bool Constraint::isSubjectEqualTo(const PotentialArchetype *pa) const { - return getSubjectDependentType({ })->isEqual(pa->getDependentType({ })); -} - -template -bool Constraint::hasSameSubjectAs(const Constraint &other) const { - return getSubjectDependentType({ }) - ->isEqual(other.getSubjectDependentType({ })); -} - Optional EquivalenceClass::findAnyConcreteConstraintAsWritten(Type preferredType) const { // If we don't have a concrete type, there's no source. @@ -1883,7 +1870,7 @@ TypeDecl *EquivalenceClass::lookupNestedType( if (decl) { SmallVector foundMembers; decl->getParentModule()->lookupQualified( - decl, name, + decl, DeclNameRef(name), NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, foundMembers); for (auto member : foundMembers) { @@ -2312,7 +2299,7 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type, auto conformance = lookupConformance(type.getDependentType(*this)->getCanonicalType(), concrete, proto); - if (!conformance) { + if (conformance.isInvalid()) { if (!concrete->hasError() && concreteSource->getLoc().isValid()) { Impl->HadAnyError = true; @@ -2326,9 +2313,9 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type, return nullptr; } - concreteSource = concreteSource->viaConcrete(*this, *conformance); + concreteSource = concreteSource->viaConcrete(*this, conformance); equivClass->recordConformanceConstraint(*this, type, proto, concreteSource); - if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr, + if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr, concreteSource->getLoc())) return nullptr; @@ -2346,7 +2333,8 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( auto conformance = lookupConformance(type.getDependentType(*this)->getCanonicalType(), superclass, proto); - if (!conformance) return nullptr; + if (conformance.isInvalid()) + return nullptr; // Conformance to this protocol is redundant; update the requirement source // appropriately. @@ -2357,10 +2345,9 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( else superclassSource = equivClass->superclassConstraints.front().source; - superclassSource = - superclassSource->viaSuperclass(*this, *conformance); + superclassSource = superclassSource->viaSuperclass(*this, conformance); equivClass->recordConformanceConstraint(*this, type, proto, superclassSource); - if (addConditionalRequirements(*conformance, /*inferForModule=*/nullptr, + if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr, superclassSource->getLoc())) return nullptr; @@ -3497,7 +3484,7 @@ GenericSignatureBuilder::getLookupConformanceFn() return LookUpConformanceInBuilder(this); } -Optional +ProtocolConformanceRef GenericSignatureBuilder::lookupConformance(CanType dependentType, Type conformingReplacementType, ProtocolDecl *conformedProtocol) { @@ -3512,10 +3499,6 @@ GenericSignatureBuilder::lookupConformance(CanType dependentType, conformedProtocol); } -LazyResolver *GenericSignatureBuilder::getLazyResolver() const { - return Context.getLazyResolver(); -} - /// Resolve any unresolved dependent member types using the given builder. static Type resolveDependentMemberTypes(GenericSignatureBuilder &builder, Type type) { @@ -3940,13 +3923,13 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( // Local function to find the insertion point for the protocol's "where" // clause, as well as the string to start the insertion ("where" or ","); - auto getProtocolWhereLoc = [&]() -> std::pair { + auto getProtocolWhereLoc = [&]() -> Located { // Already has a trailing where clause. if (auto trailing = proto->getTrailingWhereClause()) - return { trailing->getRequirements().back().getSourceRange().End, ", " }; + return { ", ", trailing->getRequirements().back().getSourceRange().End }; // Inheritance clause. - return { proto->getInherited().back().getSourceRange().End, " where " }; + return { " where ", proto->getInherited().back().getSourceRange().End }; }; // Retrieve the set of requirements that a given associated type declaration @@ -4061,8 +4044,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( assocTypeDecl->getFullName(), inheritedFromProto->getDeclaredInterfaceType()) .fixItInsertAfter( - fixItWhere.first, - getAssociatedTypeReqs(assocTypeDecl, fixItWhere.second)) + fixItWhere.Loc, + getAssociatedTypeReqs(assocTypeDecl, fixItWhere.Item)) .fixItRemove(assocTypeDecl->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, @@ -4103,6 +4086,10 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( auto type = dyn_cast(found); if (!type || isa(type)) continue; + // Ignore nominal types. They're always invalid declarations. + if (isa(type)) + continue; + // ... from the same module as the protocol. if (type->getModuleContext() != proto->getModuleContext()) continue; @@ -4133,8 +4120,8 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement( diag::typealias_override_associated_type, name, inheritedFromProto->getDeclaredInterfaceType()) - .fixItInsertAfter(fixItWhere.first, - getConcreteTypeReq(type, fixItWhere.second)) + .fixItInsertAfter(fixItWhere.Loc, + getConcreteTypeReq(type, fixItWhere.Item)) .fixItRemove(type->getSourceRange()); Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here, inheritedAssocTypeDecl->getFullName()); @@ -4374,6 +4361,8 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( if (subjectType->is()) { subjectType = resolveDependentMemberTypes(*this, subjectType); + } else { + subjectType = ErrorType::get(subjectType); } auto invalidConstraint = Constraint( @@ -4414,7 +4403,7 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement( // FIXME: diagnose if there's no conformance. if (conformance) { - if (addConditionalRequirements(*conformance, inferForModule, + if (addConditionalRequirements(conformance, inferForModule, source.getLoc())) return ConstraintResult::Conflicting; } @@ -6156,8 +6145,7 @@ namespace { return lhs.constraint < rhs.constraint; } - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; } @@ -7290,7 +7278,7 @@ void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context, } // Canonicalize the signature to check that it is canonical. - (void)newSig->getCanonicalSignature(); + (void)newSig.getCanonicalSignature(); } } @@ -7312,7 +7300,7 @@ void GenericSignatureBuilder::verifyGenericSignaturesInModule( for (auto genericSig : allGenericSignatures) { // Check whether this is the first time we've checked this (canonical) // signature. - auto canGenericSig = genericSig->getCanonicalSignature(); + auto canGenericSig = genericSig.getCanonicalSignature(); if (!knownGenericSignatures.insert(canGenericSig).second) continue; verifyGenericSignature(context, canGenericSig); @@ -7381,7 +7369,7 @@ AbstractGenericSignatureRequest::evaluate( // generic signature builder. if (!isCanonicalRequest(baseSignature, addedParameters, addedRequirements)) { // Canonicalize the inputs so we can form the canonical request. - GenericSignature canBaseSignature = GenericSignature(); + GenericSignature canBaseSignature; if (baseSignature) canBaseSignature = baseSignature->getCanonicalSignature(); diff --git a/lib/AST/Identifier.cpp b/lib/AST/Identifier.cpp index 8fbf7e20416ea..7c2d461e955a1 100644 --- a/lib/AST/Identifier.cpp +++ b/lib/AST/Identifier.cpp @@ -47,6 +47,19 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, DeclName I) { return OS; } +void swift::simple_display(llvm::raw_ostream &out, DeclName name) { + out << "'" << name << "'"; +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, DeclNameRef I) { + OS << I.getFullName(); + return OS; +} + +void swift::simple_display(llvm::raw_ostream &out, DeclNameRef name) { + out << "'" << name << "'"; +} + raw_ostream &llvm::operator<<(raw_ostream &OS, swift::ObjCSelector S) { unsigned n = S.getNumArgs(); if (n == 0) { @@ -185,6 +198,24 @@ llvm::raw_ostream &DeclName::printPretty(llvm::raw_ostream &os) const { return print(os, /*skipEmptyArgumentNames=*/!isSpecial()); } +void DeclNameRef::dump() const { + llvm::errs() << *this << "\n"; +} + +StringRef DeclNameRef::getString(llvm::SmallVectorImpl &scratch, + bool skipEmptyArgumentNames) const { + return FullName.getString(scratch, skipEmptyArgumentNames); +} + +llvm::raw_ostream &DeclNameRef::print(llvm::raw_ostream &os, + bool skipEmptyArgumentNames) const { + return FullName.print(os, skipEmptyArgumentNames); +} + +llvm::raw_ostream &DeclNameRef::printPretty(llvm::raw_ostream &os) const { + return FullName.printPretty(os); +} + ObjCSelector::ObjCSelector(ASTContext &ctx, unsigned numArgs, ArrayRef pieces) { if (numArgs == 0) { diff --git a/lib/AST/ImportCache.cpp b/lib/AST/ImportCache.cpp index 2d8ff08890c96..1b667a75d7373 100644 --- a/lib/AST/ImportCache.cpp +++ b/lib/AST/ImportCache.cpp @@ -57,7 +57,7 @@ void ImportSet::Profile( for (auto import : topLevelImports) { ID.AddInteger(import.first.size()); for (auto accessPathElt : import.first) { - ID.AddPointer(accessPathElt.first.getAsOpaquePointer()); + ID.AddPointer(accessPathElt.Item.getAsOpaquePointer()); } ID.AddPointer(import.second); } diff --git a/lib/AST/IncrementalRanges.cpp b/lib/AST/IncrementalRanges.cpp new file mode 100644 index 0000000000000..e28e6089c45cf --- /dev/null +++ b/lib/AST/IncrementalRanges.cpp @@ -0,0 +1,309 @@ +//===------------- IncrementalRanges.cpp - Generates swiftdeps files +//---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// These are the definitions for managing serializable source locations so that +// the frontend and the driver can implement incremental compilation based on +// source ranges. + +#include "swift/AST/IncrementalRanges.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsCommon.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/FileSystem.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Parse/Lexer.h" +#include "llvm/Support/YAMLParser.h" + +using namespace swift; +using namespace incremental_ranges; + +//============================================================================== +// MARK: SerializableSourceLocation +//============================================================================== + +SerializableSourceLocation::SerializableSourceLocation( + const SourceLoc loc, const SourceManager &SM) { + auto lc = SM.getLineAndColumn(loc); + line = lc.first; + column = lc.second; +} + +const SerializableSourceLocation SerializableSourceLocation::endOfAnyFile = { + ~decltype(line)(0), 0}; + +void SerializableSourceLocation::print(raw_ostream &out) const { + out << line << ":" << column; +} +void SerializableSourceLocation::dump() const { print(llvm::errs()); } + +//============================================================================== +// MARK: SerializableSourceRange +//============================================================================== + +SerializableSourceRange::SerializableSourceRange( + SerializableSourceLocation start, SerializableSourceLocation end) + : start(start), end(end) { +#ifndef NDEBUG + if (!(start <= end)) { + llvm::errs() << "*** Error: creating backwards SerializableSourceRange: ["; + start.print(llvm::errs()); + llvm::errs() << " -- "; + end.print(llvm::errs()); + llvm::errs() << ")\n"; + assert(false && "Detected backwards SerializableSourceRange"); + } +#endif +} + +SerializableSourceRange::SerializableSourceRange(const CharSourceRange r, + const SourceManager &SM) + : start(SerializableSourceLocation(r.getStart(), SM)), + end(SerializableSourceLocation(r.getEnd(), SM)) {} + +const SerializableSourceRange SerializableSourceRange::wholeFile = { + {0, 0}, SerializableSourceLocation::endOfAnyFile}; + +Ranges SerializableSourceRange::RangesForWholeFile() { + assert(wholeFile.end.line && "Ensure initialization happens"); + return {wholeFile}; +} + +bool SerializableSourceRange::properlyPreceeds( + const SerializableSourceRange &other) const { + return end <= other.start; +} + +bool SerializableSourceRange::isProperlySorted( + ArrayRef ranges) { + Optional lastRange; + for (const auto &thisRange : ranges) { + if (!lastRange) + lastRange = thisRange; + else if (!lastRange->properlyPreceeds(thisRange)) + return false; + } + return true; +} + +bool SerializableSourceRange::isImproperSubsetOf( + const SerializableSourceRange &superset) const { + return superset.start <= start && end <= superset.end; +} + +Optional SerializableSourceRange::findOutlierIfAny( + ArrayRef subset, + ArrayRef superset) { + // TODO: could optimize this by exploiting sortedness of the arrays: + // i.e. could skip searching the start of the superset + for (const auto &subsetRange : subset) { + const bool isSubsetRangeContained = subsetRange.isImproperSubsetOfAny(superset); + if (!isSubsetRangeContained) + return subsetRange; + } + return None; +} + +Ranges SerializableSourceRange::findAllOutliers( + ArrayRef subset, + ArrayRef superset) { + // TODO: optimize with slice of superset + Ranges outliers; + std::copy_if(subset.begin(), subset.end(), std::back_inserter(outliers), + [&](const SerializableSourceRange &r) { + return !r.isImproperSubsetOfAny(superset); + }); + return outliers; +} + +bool SerializableSourceRange::isImproperSubsetOfAny( + ArrayRef supersetRanges) const { + assert(isProperlySorted(supersetRanges) && "required for binary search"); + const auto firstRangeInSupersetNotBeforeSub = + std::lower_bound(supersetRanges.begin(), supersetRanges.end(), *this, + [](const SerializableSourceRange &superRange, + const SerializableSourceRange &subsetRange) { + return superRange.properlyPreceeds(subsetRange); + }); + const bool result = + firstRangeInSupersetNotBeforeSub != supersetRanges.end() && + isImproperSubsetOf(*firstRangeInSupersetNotBeforeSub); + + // slow if input is too big + assert((supersetRanges.size() >= 5 || + result == isImproperSubsetOfAnySlowlyAndSimply(supersetRanges)) && + "Check against reference"); + + return result; +} + +bool SerializableSourceRange::isImproperSubsetOfAnySlowlyAndSimply( + ArrayRef supersetRanges) const { + return llvm::any_of(supersetRanges, + [&](const SerializableSourceRange &superset) { + return isImproperSubsetOf(superset); + }); +} + +std::string SerializableSourceRange::printString() const { + std::string result; + llvm::raw_string_ostream out(result); + print(out); + return out.str(); +} + +void SerializableSourceRange::print(raw_ostream &out) const { + out << "["; + start.print(out); + out << "--"; + end.print(out); + out << ")"; +} +void SerializableSourceRange::dump() const { print(llvm::errs()); } + +//============================================================================== +// MARK: SwiftRangesEmitter +//============================================================================== + +bool SwiftRangesEmitter::emit() const { + const bool hadError = + withOutputFile(diags, outputPath, [&](llvm::raw_pwrite_stream &out) { + out << SwiftRangesFileContents::header; + emitRanges(out); + return false; + }); + if (!hadError) + return false; + diags.diagnose(SourceLoc(), diag::error_unable_to_write_swift_ranges_file, + outputPath, "Output error"); + return true; +} + +void SwiftRangesEmitter::emitRanges(llvm::raw_ostream &out) const { + SwiftRangesFileContents wholeFileContents( + collectSortedSerializedNoninlinableFunctionBodies()); + llvm::yaml::Output yamlWriter(out); + yamlWriter << wholeFileContents; +} + +Ranges +SwiftRangesEmitter::collectSortedSerializedNoninlinableFunctionBodies() const { + return serializeRanges( + coalesceSortedRanges(sortRanges(collectNoninlinableFunctionBodies()))); +} + +std::vector +SwiftRangesEmitter::collectNoninlinableFunctionBodies() const { + struct FnBodyCollector : ASTWalker { + const SourceManager &SM; + FnBodyCollector(const SourceManager &SM) : SM(SM) {} + + std::vector ranges; + bool walkToDeclPre(Decl *D) override { + if (const auto *AFD = dyn_cast(D)) { + // If you change an accessor body you might change the inferred + // type, and the change might propagate, so exclude them from local + // change ranges. + // Also rule out implicit constructors a fortiori. + if (!isa(AFD) && !AFD->isImplicit() && + !AFD->getAttrs().hasAttribute()) { + auto sr = AFD->getBodySourceRange(); + if (sr.isValid()) + ranges.push_back(Lexer::getCharSourceRangeFromSourceRange(SM, sr)); + } + return false; + } + return true; + } + }; + FnBodyCollector collector(sourceMgr); + primaryFile->walk(collector); + return collector.ranges; +} + +std::vector +SwiftRangesEmitter::sortRanges(std::vector ranges) const { + std::sort(ranges.begin(), ranges.end(), + [&](const CharSourceRange &lhs, const CharSourceRange &rhs) { + return sourceMgr.isBeforeInBuffer(lhs.getStart(), rhs.getStart()); + }); + return ranges; +} + +std::vector SwiftRangesEmitter::coalesceSortedRanges( + std::vector ranges) const { + if (ranges.empty()) + return ranges; + auto toBeWidened = ranges.begin(); + auto candidate = toBeWidened + 1; + while (candidate < ranges.end()) { + if (isImmediatelyBeforeOrOverlapping(*toBeWidened, *candidate)) + toBeWidened->widen(*candidate++); + else + *++toBeWidened = *candidate++; + } + ranges.erase(toBeWidened + 1, ranges.end()); + return ranges; +} + +std::vector +SwiftRangesEmitter::serializeRanges(std::vector ranges) const { + std::vector result; + for (const auto r : ranges) + result.push_back(SerializableSourceRange(r, sourceMgr)); + return result; +} + +bool SwiftRangesEmitter::isImmediatelyBeforeOrOverlapping( + CharSourceRange prev, CharSourceRange next) const { + // TODO: investigate returning true if only white space intervenes. + // Would be more work here, but less work downstream. + return !sourceMgr.isBeforeInBuffer(prev.getEnd(), next.getStart()); +} + +//============================================================================== +// MARK: CompiledSource +//============================================================================== + +bool CompiledSourceEmitter::emit() { + auto const bufID = primaryFile->getBufferID(); + if (!bufID.hasValue()) { + diags.diagnose(SourceLoc(), + diag::error_unable_to_write_compiled_source_file, outputPath, + "No buffer"); + return true; + } + const bool hadError = + withOutputFile(diags, outputPath, [&](llvm::raw_pwrite_stream &out) { + out << sourceMgr.getEntireTextForBuffer(bufID.getValue()); + return false; + }); + if (!hadError) + return false; + diags.diagnose(SourceLoc(), diag::error_unable_to_write_compiled_source_file, + outputPath, "Output error"); + return true; +} + +//============================================================================== +// MARK: SwiftRangesFileContents +//============================================================================== + +void SwiftRangesFileContents::dump(const StringRef primaryInputFilename) const { + llvm::errs() << "\n*** Swift range file contents for '" + << primaryInputFilename << "': ***\n"; + llvm::yaml::Output dumper(llvm::errs()); + dumper << *const_cast(this); +} diff --git a/lib/AST/IndexSubset.cpp b/lib/AST/IndexSubset.cpp index f2c5c1c478b78..fe64ee07d58ca 100644 --- a/lib/AST/IndexSubset.cpp +++ b/lib/AST/IndexSubset.cpp @@ -1,8 +1,8 @@ -//===-------- IndexSubset.cpp - Swift Differentiable Programming ----------===// +//===--- IndexSubset.cpp - Fixed-size subset of indices -------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -79,6 +79,20 @@ IndexSubset *IndexSubset::extendingCapacity( return IndexSubset::get(ctx, indices); } +void IndexSubset::print(llvm::raw_ostream &s) const { + s << '{'; + interleave(range(capacity), [this, &s](unsigned i) { s << contains(i); }, + [&s] { s << ", "; }); + s << '}'; +} + +void IndexSubset::dump(llvm::raw_ostream &s) const { + s << "(index_subset capacity=" << capacity << " indices=("; + interleave(getIndices(), [&s](unsigned i) { s << i; }, + [&s] { s << ", "; }); + s << "))\n"; +} + int IndexSubset::findNext(int startIndex) const { assert(startIndex < (int)capacity && "Start index cannot be past the end"); unsigned bitWordIndex = 0, offset = 0; diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 85291e744737f..a478cb81a4514 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -212,14 +212,15 @@ void SourceLookupCache::addToUnqualifiedLookupCache(Range decls, if (!NTD->hasUnparsedMembers() || NTD->maybeHasOperatorDeclarations()) addToUnqualifiedLookupCache(NTD->getMembers(), true); - // Avoid populating the cache with the members of invalid extension - // declarations. These members can be used to point validation inside of - // a malformed context. - if (D->isInvalid()) continue; + if (auto *ED = dyn_cast(D)) { + // Avoid populating the cache with the members of invalid extension + // declarations. These members can be used to point validation inside of + // a malformed context. + if (ED->isInvalid()) continue; - if (auto *ED = dyn_cast(D)) if (!ED->hasUnparsedMembers() || ED->maybeHasOperatorDeclarations()) addToUnqualifiedLookupCache(ED->getMembers(), true); + } } } @@ -229,7 +230,7 @@ void SourceLookupCache::populateMemberCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "populate-source-file-class-member-cache"); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); MemberCachePopulated = true; } @@ -242,7 +243,7 @@ void SourceLookupCache::populateMemberCache(const ModuleDecl &Mod) { for (const FileUnit *file : Mod.getFiles()) { auto &SF = *cast(file); - addToMemberCache(SF.Decls); + addToMemberCache(SF.getTopLevelDecls()); } MemberCachePopulated = true; @@ -274,7 +275,7 @@ void SourceLookupCache::addToMemberCache(Range decls) { SourceLookupCache::SourceLookupCache(const SourceFile &SF) { FrontendStatsTracer tracer(SF.getASTContext().Stats, "source-file-populate-cache"); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { @@ -282,7 +283,7 @@ SourceLookupCache::SourceLookupCache(const ModuleDecl &M) { "module-populate-cache"); for (const FileUnit *file : M.getFiles()) { auto &SF = *cast(file); - addToUnqualifiedLookupCache(SF.Decls, false); + addToUnqualifiedLookupCache(SF.getTopLevelDecls(), false); } } @@ -302,7 +303,7 @@ void SourceLookupCache::lookupVisibleDecls(AccessPathTy AccessPath, assert(AccessPath.size() <= 1 && "can only refer to top-level decls"); if (!AccessPath.empty()) { - auto I = TopLevelValues.find(AccessPath.front().first); + auto I = TopLevelValues.find(AccessPath.front().Item); if (I == TopLevelValues.end()) return; for (auto vd : I->second) @@ -334,7 +335,7 @@ void SourceLookupCache::lookupClassMembers(AccessPathTy accessPath, for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } @@ -366,7 +367,7 @@ void SourceLookupCache::lookupClassMember(AccessPathTy accessPath, if (!accessPath.empty()) { for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); - if (nominal && nominal->getName() == accessPath.front().first) + if (nominal && nominal->getName() == accessPath.front().Item) results.push_back(vd); } return; @@ -643,6 +644,12 @@ void ModuleDecl::getTopLevelDecls(SmallVectorImpl &Results) const { FORWARD(getTopLevelDecls, (Results)); } +void ModuleDecl::getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes) const { + FORWARD(getTopLevelDeclsWhereAttributesMatch, (Results, matchAttributes)); +} + void SourceFile::getTopLevelDecls(SmallVectorImpl &Results) const { Results.append(Decls.begin(), Decls.end()); } @@ -716,7 +723,7 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl &Results) const { FORWARD(getDisplayDecls, (Results)); } -Optional +ProtocolConformanceRef ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { ASTContext &ctx = getASTContext(); @@ -725,7 +732,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { // If the existential type cannot be represented or the protocol does not // conform to itself, there's no point in looking further. if (!protocol->existentialConformsToSelf()) - return None; + return ProtocolConformanceRef::forInvalid(); auto layout = type->getExistentialLayout(); @@ -742,7 +749,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { type->castTo()->getDecl() == protocol) return ProtocolConformanceRef(ctx.getSelfConformance(protocol)); - return None; + return ProtocolConformanceRef::forInvalid(); } // If the existential is class-constrained, the class might conform @@ -775,11 +782,11 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) { // We didn't find our protocol in the existential's list; it doesn't // conform. - return None; + return ProtocolConformanceRef::forInvalid(); } -Optional -ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { +ProtocolConformanceRef ModuleDecl::lookupConformance(Type type, + ProtocolDecl *protocol) { ASTContext &ctx = getASTContext(); // A dynamic Self type conforms to whatever its underlying type @@ -799,10 +806,8 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { // concrete. if (auto super = archetype->getSuperclass()) { if (auto inheritedConformance = lookupConformance(super, protocol)) { - return ProtocolConformanceRef( - ctx.getInheritedConformance( - type, - inheritedConformance->getConcrete())); + return ProtocolConformanceRef(ctx.getInheritedConformance( + type, inheritedConformance.getConcrete())); } } @@ -811,7 +816,7 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { return ProtocolConformanceRef(protocol); } - return None; + return ProtocolConformanceRef::forInvalid(); } // An existential conforms to a protocol if the protocol is listed in the @@ -833,12 +838,13 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { auto nominal = type->getAnyNominal(); // If we don't have a nominal type, there are no conformances. - if (!nominal || isa(nominal)) return None; + if (!nominal || isa(nominal)) + return ProtocolConformanceRef::forInvalid(); // Find the (unspecialized) conformance. SmallVector conformances; if (!nominal->lookupConformance(this, protocol, conformances)) - return None; + return ProtocolConformanceRef::forInvalid(); // FIXME: Ambiguity resolution. auto conformance = conformances.front(); @@ -862,8 +868,8 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) { "We already found the inherited conformance"); // Create the inherited conformance entry. - conformance - = ctx.getInheritedConformance(type, inheritedConformance->getConcrete()); + conformance = + ctx.getInheritedConformance(type, inheritedConformance.getConcrete()); return ProtocolConformanceRef(conformance); } @@ -1175,13 +1181,13 @@ void ModuleDecl::getImportedModulesForLookup( } bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) { - using AccessPathElem = std::pair; + using AccessPathElem = Located; if (lhs.size() != rhs.size()) return false; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first == rElem.first; + return lElem.Item == rElem.Item; }); } @@ -1245,12 +1251,12 @@ ModuleDecl::removeDuplicateImports(SmallVectorImpl &imports) { lhs.second->getReverseFullModuleName(), {}, rhs.second->getReverseFullModuleName(), {}); } - using AccessPathElem = std::pair; + using AccessPathElem = Located; return std::lexicographical_compare(lhs.first.begin(), lhs.first.end(), rhs.first.begin(), rhs.first.end(), [](const AccessPathElem &lElem, const AccessPathElem &rElem) { - return lElem.first.str() < rElem.first.str(); + return lElem.Item.str() < rElem.Item.str(); }); }); auto last = std::unique(imports.begin(), imports.end(), @@ -1873,6 +1879,22 @@ void *FileUnit::operator new(size_t Bytes, ASTContext &C, unsigned Alignment) { return C.Allocate(Bytes, Alignment); } +void FileUnit::getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes) const { + auto prevSize = Results.size(); + getTopLevelDecls(Results); + + // Filter out unwanted decls that were just added to Results. + // Note: We could apply this check in all implementations of + // getTopLevelDecls instead or in everything that creates a Decl. + auto newEnd = std::remove_if(Results.begin() + prevSize, Results.end(), + [&matchAttributes](const Decl *D) -> bool { + return !matchAttributes(D->getAttrs()); + }); + Results.erase(newEnd, Results.end()); +} + StringRef LoadedFile::getFilename() const { return ""; } diff --git a/lib/AST/ModuleLoader.cpp b/lib/AST/ModuleLoader.cpp index fbcf3f9621134..60fe67355c9fa 100644 --- a/lib/AST/ModuleLoader.cpp +++ b/lib/AST/ModuleLoader.cpp @@ -39,7 +39,6 @@ DependencyTracker::addDependency(StringRef File, bool IsSystem) { // DependencyTracker exposes an interface that (intentionally) does not talk // about clang at all, nor about missing deps. It does expose an IsSystem // dimension, which we accept and pass along to the clang DependencyCollector. - // along to the clang DependencyCollector. clangCollector->maybeAddDependency(File, /*FromModule=*/false, IsSystem, /*IsModuleFile=*/false, /*IsMissing=*/false); diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 500e8f84c52a3..9a701a07382fd 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -15,6 +15,7 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ImportCache.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "llvm/Support/raw_ostream.h" using namespace swift; @@ -228,23 +229,32 @@ void ModuleNameLookup::lookupInModule( decls.end()); } +llvm::Expected LookupInModuleRequest::evaluate( + Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, + NLKind lookupKind, ResolutionKind resolutionKind, + const DeclContext *moduleScopeContext) const { + assert(moduleScopeContext->isModuleScopeContext()); + + auto &ctx = moduleOrFile->getASTContext(); + FrontendStatsTracer tracer(ctx.Stats, "lookup-in-module"); + + QualifiedLookupResult decls; + LookupByName lookup(ctx, resolutionKind, name, lookupKind); + lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext); + return decls; +} + void namelookup::lookupInModule(const DeclContext *moduleOrFile, DeclName name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, const DeclContext *moduleScopeContext) { - assert(moduleScopeContext->isModuleScopeContext()); - auto &ctx = moduleOrFile->getASTContext(); - auto *stats = ctx.Stats; - if (stats) - stats->getFrontendCounters().NumLookupInModule++; - - FrontendStatsTracer tracer(stats, "lookup-in-module"); - - LookupByName lookup(ctx, resolutionKind, name, lookupKind); - lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext); + LookupInModuleRequest req(moduleOrFile, name, lookupKind, resolutionKind, + moduleScopeContext); + auto results = evaluateOrDefault(ctx.evaluator, req, {}); + decls.append(results.begin(), results.end()); } void namelookup::lookupVisibleDeclsInModule( @@ -260,3 +270,14 @@ void namelookup::lookupVisibleDeclsInModule( lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext); } +void namelookup::simple_display(llvm::raw_ostream &out, ResolutionKind kind) { + switch (kind) { + case ResolutionKind::Overloadable: + out << "Overloadable"; + return; + case ResolutionKind::TypesOnly: + out << "TypesOnly"; + return; + } + llvm_unreachable("Unhandled case in switch"); +} diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index d49f281c375fb..3e9797008ff24 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -29,6 +29,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/ReferencedNameTracker.h" #include "swift/AST/SourceFile.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/STLExtras.h" @@ -59,17 +60,86 @@ ValueDecl *LookupResultEntry::getBaseDecl() const { return selfDecl; } + if (auto *CE = dyn_cast(BaseDC)) { + auto *selfDecl = CE->getCapturedSelfDecl(); + assert(selfDecl); + assert(selfDecl->isSelfParamCapture()); + return selfDecl; + } + auto *nominalDecl = BaseDC->getSelfNominalTypeDecl(); assert(nominalDecl); return nominalDecl; } +void LookupResult::filter( + llvm::function_ref pred) { + size_t index = 0; + size_t originalFirstOuter = IndexOfFirstOuterResult; + Results.erase(std::remove_if(Results.begin(), Results.end(), + [&](LookupResultEntry result) -> bool { + auto isInner = index < originalFirstOuter; + index++; + if (pred(result, !isInner)) + return false; + + // Need to remove this, which means, if it is + // an inner result, the outer results need to + // shift down. + if (isInner) + IndexOfFirstOuterResult--; + return true; + }), + Results.end()); +} + +void LookupResult::shiftDownResults() { + // Remove inner results. + Results.erase(Results.begin(), Results.begin() + IndexOfFirstOuterResult); + IndexOfFirstOuterResult = 0; + + if (Results.empty()) + return; + + // Compute IndexOfFirstOuterResult. + const DeclContext *dcInner = Results.front().getValueDecl()->getDeclContext(); + for (auto &&result : Results) { + const DeclContext *dc = result.getValueDecl()->getDeclContext(); + if (dc == dcInner || + (dc->isModuleScopeContext() && dcInner->isModuleScopeContext())) + ++IndexOfFirstOuterResult; + else + break; + } +} + +void swift::simple_display(llvm::raw_ostream &out, + UnqualifiedLookupOptions options) { + using Flag = std::pair; + Flag possibleFlags[] = { + {UnqualifiedLookupFlags::AllowProtocolMembers, "AllowProtocolMembers"}, + {UnqualifiedLookupFlags::IgnoreAccessControl, "IgnoreAccessControl"}, + {UnqualifiedLookupFlags::IncludeOuterResults, "IncludeOuterResults"}, + {UnqualifiedLookupFlags::KnownPrivate, "KnownPrivate"}, + {UnqualifiedLookupFlags::TypeLookup, "TypeLookup"}, + }; + + auto flagsToPrint = llvm::make_filter_range( + possibleFlags, [&](Flag flag) { return options.contains(flag.first); }); + + out << "{ "; + interleave( + flagsToPrint, [&](Flag flag) { out << flag.second; }, + [&] { out << ", "; }); + out << " }"; +} + void DebuggerClient::anchor() {} void AccessFilteringDeclConsumer::foundDecl( ValueDecl *D, DeclVisibilityKind reason, DynamicLookupInfo dynamicLookupInfo) { - if (D->isInvalid()) + if (D->hasInterfaceType() && D->isInvalid()) return; if (!D->isAccessibleFrom(DC)) return; @@ -712,7 +782,8 @@ SelfBoundsFromWhereClauseRequest::evaluate( bool isSelfLHS = false; if (auto typeRepr = req.getSubjectRepr()) { if (auto identTypeRepr = dyn_cast(typeRepr)) - isSelfLHS = (identTypeRepr->getIdentifier() == ctx.Id_Self); + isSelfLHS = (identTypeRepr->getNameRef().getBaseIdentifier() == + ctx.Id_Self); } else if (Type type = req.getSubject()) { isSelfLHS = type->isEqual(dc->getSelfInterfaceType()); } @@ -809,6 +880,11 @@ class swift::MemberLookupTable { /// Lookup table mapping names to the set of declarations with that name. LookupTable Lookup; + /// The set of names of lazily-loaded members that the lookup table has a + /// complete accounting of with respect to all known extensions of its + /// parent nominal type. + llvm::DenseSet LazilyCompleteNames; + public: /// Create a new member lookup table. explicit MemberLookupTable(ASTContext &ctx); @@ -822,6 +898,24 @@ class swift::MemberLookupTable { /// Add the given members to the lookup table. void addMembers(DeclRange members); + /// Returns \c true if the lookup table has a complete accounting of the + /// given name. + bool isLazilyComplete(DeclBaseName name) const { + return LazilyCompleteNames.find(name) != LazilyCompleteNames.end(); + } + + /// Mark a given lazily-loaded name as being complete. + void markLazilyComplete(DeclBaseName name) { + LazilyCompleteNames.insert(name); + } + + /// Clears the cache of lazily-complete names. This _must_ be called when + /// new extensions with lazy members are added to the type, or direct lookup + /// will return inconsistent or stale results. + void clearLazilyCompleteCache() { + LazilyCompleteNames.clear(); + } + /// Iterator into the lookup table. typedef LookupTable::iterator iterator; @@ -841,7 +935,11 @@ class swift::MemberLookupTable { os << "Lookup:\n "; for (auto &pair : Lookup) { - pair.getFirst().print(os) << ":\n "; + pair.getFirst().print(os); + if (isLazilyComplete(pair.getFirst().getBaseName())) { + os << " (lazily complete)"; + } + os << ":\n "; for (auto &decl : pair.getSecond()) { os << "- "; decl->dumpRef(os); @@ -851,26 +949,10 @@ class swift::MemberLookupTable { os << "\n"; } - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger") { + SWIFT_DEBUG_DUMP { dump(llvm::errs()); } - // Mark all Decls in this table as not-resident in a table, drop - // references to them. Should only be called when this was not fully-populated - // from an IterableDeclContext. - void clear() { - // LastExtensionIncluded would only be non-null if this was populated from - // an IterableDeclContext (though it might still be null in that case). - assert(LastExtensionIncluded == nullptr); - for (auto const &i : Lookup) { - for (auto d : i.getSecond()) { - d->setAlreadyInLookupTable(false); - } - } - Lookup.clear(); - } - // Only allow allocation of member lookup tables using the allocator in // ASTContext or by doing a placement new. void *operator new(size_t Bytes, ASTContext &C, @@ -973,30 +1055,26 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) { } } -void NominalTypeDecl::addedMember(Decl *member) { - auto *vd = dyn_cast(member); - if (!vd) - return; - - // If we have a lookup table, add the new member to it. - auto *lookup = LookupTable.getPointer(); - if (lookup && isLookupTablePopulated()) { - if (hasLazyMembers()) { - // If we have lazy members, only add the new member to the lookup - // table if we already have another member with the same name. - // The presence of a lookup table entry indicates that the - // nominal as well as all extensions have already been searched. - if (lookup->find(vd->getBaseName()) == lookup->end()) - return; - } +void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { + if (!LookupTable) return; - lookup->addMember(vd); + if (ext->hasLazyMembers()) { + LookupTable->addMembers(ext->getCurrentMembersWithoutLoading()); + LookupTable->clearLazilyCompleteCache(); + } else { + LookupTable->addMembers(ext->getMembers()); } } -void NominalTypeDecl::addedExtension(ExtensionDecl *ext) { - if (hasLazyMembers()) - setLookupTablePopulated(false); +void NominalTypeDecl::addedMember(Decl *member) { + // If we have a lookup table, add the new member to it. If not, we'll pick up + // this member when we first create the table. + auto *vd = dyn_cast(member); + auto *lookup = LookupTable; + if (!vd || !lookup) + return; + + lookup->addMember(vd); } void ExtensionDecl::addedMember(Decl *member) { @@ -1029,8 +1107,8 @@ void ExtensionDecl::addedMember(Decl *member) { // │ExtensionDecl *LastExtension ─┼───────┐│ │ └───┐ // │ │ ││ └──────────────────────┐│ // │MemberLookupTable *LookupTable├─┐ ││ ││ -// │bool LookupTableComplete │ │ ││ ┌─────────────────┐ ││ -// └──────────────────────────────┘ │ ││ │ExtensionDecl │ ││ +// └──────────────────────────────┘ │ ││ ┌─────────────────┐ ││ +// │ ││ │ExtensionDecl │ ││ // │ ││ │------------- │ ││ // ┌─────────────┘ │└────▶│ExtensionDecl │ ││ // │ │ │ *NextExtension ├──┐ ││ @@ -1063,22 +1141,16 @@ void ExtensionDecl::addedMember(Decl *member) { // // If the IDC list is later populated and/or an extension is added _after_ // MemberLookupTable is constructed (and possibly has entries in it), -// MemberLookupTable is purged and reconstructed from IDC's list. +// MemberLookupTable is incrementally reconstituted with new members. static bool populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, MemberLookupTable &LookupTable, - DeclName name, + DeclBaseName name, IterableDeclContext *IDC) { - if (IDC->isLoadingLazyMembers()) { - return false; - } - IDC->setLoadingLazyMembers(true); auto ci = ctx.getOrCreateLazyIterableContextData(IDC, /*lazyLoader=*/nullptr); - if (auto res = ci->loader->loadNamedMembers(IDC, name.getBaseName(), - ci->memberData)) { - IDC->setLoadingLazyMembers(false); + if (auto res = ci->loader->loadNamedMembers(IDC, name, ci->memberData)) { if (auto s = ctx.Stats) { ++s->getFrontendCounters().NamedLazyMemberLoadSuccessCount; } @@ -1087,7 +1159,6 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, } return false; } else { - IDC->setLoadingLazyMembers(false); if (auto s = ctx.Stats) { ++s->getFrontendCounters().NamedLazyMemberLoadFailureCount; } @@ -1095,82 +1166,62 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx, } } -static void populateLookupTableEntryFromCurrentMembers( - ASTContext &ctx, MemberLookupTable &LookupTable, DeclName name, - IterableDeclContext *IDC) { - for (auto m : IDC->getMembers()) { - if (auto v = dyn_cast(m)) { - if (v->getFullName().matchesRef(name.getBaseName())) { - LookupTable.addMember(m); - } - } - } -} - static void populateLookupTableEntryFromExtensions(ASTContext &ctx, MemberLookupTable &table, NominalTypeDecl *nominal, - DeclName name) { + DeclBaseName name) { + assert(!table.isLazilyComplete(name) && + "Should not be searching extensions for complete name!"); + for (auto e : nominal->getExtensions()) { - if (e->wasDeserialized() || e->hasClangNode()) { - assert(!e->hasUnparsedMembers()); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, - name, e)) { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); - } - } else { - populateLookupTableEntryFromCurrentMembers(ctx, table, name, e); + // If there's no lazy members to look at, all the members of this extension + // are present in the lookup table. + if (!e->hasLazyMembers()) { + continue; } - } -} -bool NominalTypeDecl::isLookupTablePopulated() const { - return LookupTable.getInt(); -} + assert(e->wasDeserialized() || e->hasClangNode() && + "Extension without deserializable content has lazy members!"); + assert(!e->hasUnparsedMembers()); -void NominalTypeDecl::setLookupTablePopulated(bool value) { - LookupTable.setInt(value); + // Try lazy loading. If that fails, then we fall back by loading the + // entire extension. FIXME: It's rather unfortunate that we fall off the + // happy path because the Clang Importer can't handle lazy import-as-member. + if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, name, e)) { + e->loadAllMembers(); + } + } } void NominalTypeDecl::prepareLookupTable() { - // If we haven't allocated the lookup table yet, do so now. - if (!LookupTable.getPointer()) { - auto &ctx = getASTContext(); - LookupTable.setPointer(new (ctx) MemberLookupTable(ctx)); + // If we have already allocated the lookup table, then there's nothing further + // to do. + if (LookupTable) { + return; } + // Otherwise start the first fill. + auto &ctx = getASTContext(); + LookupTable = new (ctx) MemberLookupTable(ctx); + if (hasLazyMembers()) { assert(!hasUnparsedMembers()); + LookupTable->addMembers(getCurrentMembersWithoutLoading()); + } else { + LookupTable->addMembers(getMembers()); + } - // Lazy members: if the table needs population, populate the table _only - // from those members already in the IDC member list_ such as implicits or - // globals-as-members, then update table entries from the extensions that - // have the same names as any such initial-population members. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getCurrentMembersWithoutLoading()); - - llvm::SetVector baseNamesPresent; - for (auto entry : *LookupTable.getPointer()) { - baseNamesPresent.insert(entry.getFirst().getBaseName()); - } - - for (auto baseName : baseNamesPresent) { - populateLookupTableEntryFromExtensions(getASTContext(), - *LookupTable.getPointer(), - this, baseName); - } + for (auto e : getExtensions()) { + // If we can lazy-load this extension, only take the members we've loaded + // so far. + if (e->wasDeserialized() || e->hasClangNode()) { + LookupTable->addMembers(e->getCurrentMembersWithoutLoading()); + continue; } - } else { - // No lazy members: if the table needs population, populate the table - // en-masse; and in either case update the extensions. - if (!isLookupTablePopulated()) { - setLookupTablePopulated(true); - LookupTable.getPointer()->addMembers(getMembers()); - } - LookupTable.getPointer()->updateLookupTable(this); + // Else, load all the members into the table. + LookupTable->addMembers(e->getMembers()); } } @@ -1196,102 +1247,88 @@ maybeFilterOutAttrImplements(TinyPtrVector decls, return result; } -TinyPtrVector NominalTypeDecl::lookupDirect( - DeclName name, - OptionSet flags) { - ASTContext &ctx = getASTContext(); - if (auto s = ctx.Stats) { - ++s->getFrontendCounters().NominalTypeLookupDirectCount; - } +TinyPtrVector +NominalTypeDecl::lookupDirect(DeclName name, + OptionSet flags) { + return evaluateOrDefault(getASTContext().evaluator, + DirectLookupRequest({this, name, flags}), {}); +} + +llvm::Expected> +DirectLookupRequest::evaluate(Evaluator &evaluator, + DirectLookupDescriptor desc) const { + const auto &name = desc.Name; + const auto flags = desc.Options; + auto *decl = desc.DC; // We only use NamedLazyMemberLoading when a user opts-in and we have // not yet loaded all the members into the IDC list in the first place. - bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && - hasLazyMembers()); - - bool includeAttrImplements = - flags.contains(LookupDirectFlags::IncludeAttrImplements); - - // FIXME: At present, lazy member is not able to find inherited constructors - // in imported classes, because SwiftDeclConverter::importInheritedConstructors() - // is only called via ClangImporter::Implementation::loadAllMembers(). - if (hasClangNode() && - name.getBaseName() == DeclBaseName::createConstructor()) - useNamedLazyMemberLoading = false; - - LLVM_DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" - << name << ")" - << ", isLookupTablePopulated()=" << isLookupTablePopulated() - << ", hasLazyMembers()=" << hasLazyMembers() - << ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading - << "\n"); - - // We check the LookupTable at most twice, possibly treating a miss in the - // first try as a cache-miss that we then do a cache-fill on, and retry. - for (int i = 0; i < 2; ++i) { - - // First, if we're _not_ doing NamedLazyMemberLoading, we make sure we've - // populated the IDC and brought it up to date with any extensions. This - // will flip the hasLazyMembers() flag to false as well. - if (!useNamedLazyMemberLoading) { - // It's possible that the lookup table exists but has information in it - // that is either currently out of date or soon to be out of date. - // This can happen two ways: - // - // - We've not yet indexed the members we have (isLookupTablePopulated() - // is zero). - // - // - We've still got more lazy members left to load; this can happen - // even if we _did_ index some members. - // - // In either of these cases, we want to reset the table to empty and - // mark it as needing reconstruction. - if (LookupTable.getPointer() && - (hasLazyMembers() || !isLookupTablePopulated())) { - LookupTable.getPointer()->clear(); - setLookupTablePopulated(false); - } - - (void)getMembers(); - - // Make sure we have the complete list of members (in this nominal and in - // all extensions). - for (auto E : getExtensions()) - (void)E->getMembers(); + ASTContext &ctx = decl->getASTContext(); + const bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading && + decl->hasLazyMembers()); + const bool disableAdditionalExtensionLoading = + flags.contains(NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions); + const bool includeAttrImplements = + flags.contains(NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements); + + LLVM_DEBUG(llvm::dbgs() << decl->getNameStr() << ".lookupDirect(" + << name << ")" + << ", hasLazyMembers()=" << decl->hasLazyMembers() + << ", useNamedLazyMemberLoading=" + << useNamedLazyMemberLoading + << "\n"); + + decl->prepareLookupTable(); + + auto tryCacheLookup = + [=](MemberLookupTable &table, + DeclName name) -> Optional> { + // Look for a declaration with this name. + auto known = table.find(name); + if (known == table.end()) { + return None; } - // Next, in all cases, prepare the lookup table for use, possibly - // repopulating it from the IDC if the IDC member list has just grown. - prepareLookupTable(); + // We found something; return it. + return maybeFilterOutAttrImplements(known->second, name, + includeAttrImplements); + }; - // Look for a declaration with this name. - auto known = LookupTable.getPointer()->find(name); + auto updateLookupTable = [&decl](MemberLookupTable &table, + bool noExtensions) { + // Make sure we have the complete list of members (in this nominal and in + // all extensions). + (void)decl->getMembers(); - // We found something; return it. - if (known != LookupTable.getPointer()->end()) - return maybeFilterOutAttrImplements(known->second, name, - includeAttrImplements); + if (noExtensions) + return; - // If we have no more second chances, stop now. - if (!useNamedLazyMemberLoading || i > 0) - break; + for (auto E : decl->getExtensions()) + (void)E->getMembers(); - // If we get here, we had a cache-miss and _are_ using - // NamedLazyMemberLoading. Try to populate a _single_ entry in the - // MemberLookupTable from both this nominal and all of its extensions, and - // retry. Any failure to load here flips the useNamedLazyMemberLoading to - // false, and we fall back to loading all members during the retry. - auto &Table = *LookupTable.getPointer(); - if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, - name, this)) { - useNamedLazyMemberLoading = false; - } else { - populateLookupTableEntryFromExtensions(ctx, Table, this, name); + table.updateLookupTable(decl); + }; + + auto &Table = *decl->LookupTable; + if (!useNamedLazyMemberLoading) { + updateLookupTable(Table, disableAdditionalExtensionLoading); + } else if (!Table.isLazilyComplete(name.getBaseName())) { + // The lookup table believes it doesn't have a complete accounting of this + // name - either because we're never seen it before, or another extension + // was registered since the last time we searched. Ask the loaders to give + // us a hand. + DeclBaseName baseName(name.getBaseName()); + if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, baseName, decl)) { + updateLookupTable(Table, disableAdditionalExtensionLoading); + } else if (!disableAdditionalExtensionLoading) { + populateLookupTableEntryFromExtensions(ctx, Table, decl, baseName); } + Table.markLazilyComplete(baseName); } - // None of our attempts found anything. - return { }; + // Look for a declaration with this name. + return tryCacheLookup(Table, name) + .getValueOr(TinyPtrVector()); } void ClassDecl::createObjCMethodLookup() { @@ -1496,15 +1533,18 @@ static void extractDirectlyReferencedNominalTypes( } bool DeclContext::lookupQualified(Type type, - DeclName member, + DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); // Handle AnyObject lookup. - if (type->isAnyObject()) - return lookupAnyObject(member, options, decls); + if (type->isAnyObject()) { + AnyObjectLookupRequest req(this, member, options); + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); + } // Handle lookup in a module. if (auto moduleTy = type->getAs()) @@ -1518,20 +1558,27 @@ bool DeclContext::lookupQualified(Type type, } bool DeclContext::lookupQualified(ArrayRef typeDecls, - DeclName member, + DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); + QualifiedLookupRequest req{this, {typeDecls.begin(), typeDecls.end()}, + member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto *stats = getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInNominal++; +llvm::Expected +QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + SmallVector typeDecls, + DeclNameRef member, NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); // Tracking for the nominal types we'll visit. SmallVector stack; @@ -1560,8 +1607,6 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, // Visit all of the nominal types we know about, discovering any others // we need along the way. - auto &ctx = getASTContext(); - auto typeResolver = ctx.getLazyResolver(); bool wantProtocolMembers = (options & NL_ProtocolMembers); while (!stack.empty()) { auto current = stack.back(); @@ -1570,27 +1615,18 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, if (tracker) tracker->addUsedMember({current, member.getBaseName()},isLookupCascading); - // Make sure we've resolved implicit members, if we need them. - if (typeResolver) { - if (member.getBaseName() == DeclBaseName::createConstructor()) - typeResolver->resolveImplicitConstructors(current); - - typeResolver->resolveImplicitMember(current, member); - } - // Look for results within the current nominal type and its extensions. bool currentIsProtocol = isa(current); auto flags = OptionSet(); if (options & NL_IncludeAttributeImplements) flags |= NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements; - for (auto decl : current->lookupDirect(member, flags)) { + for (auto decl : current->lookupDirect(member.getFullName(), flags)) { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa(decl)) continue; - if (isAcceptableLookupResult(this, options, decl, - onlyCompleteObjectInits)) + if (isAcceptableLookupResult(DC, options, decl, onlyCompleteObjectInits)) decls.push_back(decl); } @@ -1650,39 +1686,46 @@ bool DeclContext::lookupQualified(ArrayRef typeDecls, } } - pruneLookupResultSet(this, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInNominals(this, typeDecls, member, options, - decls); + pruneLookupResultSet(DC, options, decls); + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInNominals(DC, typeDecls, member.getFullName(), + options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } -bool DeclContext::lookupQualified(ModuleDecl *module, DeclName member, +bool DeclContext::lookupQualified(ModuleDecl *module, DeclNameRef member, NLOptions options, SmallVectorImpl &decls) const { - using namespace namelookup; + assert(decls.empty() && "additive lookup not supported"); + ModuleQualifiedLookupRequest req{this, module, member, options}; + decls = evaluateOrDefault(getASTContext().evaluator, req, {}); + return !decls.empty(); +} - auto &ctx = getASTContext(); - auto *stats = ctx.Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInModule++; +llvm::Expected +ModuleQualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, + ModuleDecl *module, DeclNameRef member, + NLOptions options) const { + using namespace namelookup; + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(DC, options, tracker, isLookupCascading); auto kind = (options & NL_OnlyTypes ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable); - auto topLevelScope = getModuleScopeContext(); + auto topLevelScope = DC->getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { if (tracker) { - recordLookupOfTopLevelName(topLevelScope, member, isLookupCascading); + recordLookupOfTopLevelName(topLevelScope, member.getFullName(), + isLookupCascading); } - lookupInModule(module, member, decls, + lookupInModule(module, member.getFullName(), decls, NLKind::QualifiedLookup, kind, topLevelScope); } else { // Note: This is a lookup into another module. Unless we're compiling @@ -1691,35 +1734,39 @@ bool DeclContext::lookupQualified(ModuleDecl *module, DeclName member, // anything in this one. // Perform the lookup in all imports of this module. + auto &ctx = DC->getASTContext(); auto accessPaths = ctx.getImportCache().getAllVisibleAccessPaths( module, topLevelScope); if (llvm::any_of(accessPaths, [&](ModuleDecl::AccessPathTy accessPath) { - return ModuleDecl::matchesAccessPath(accessPath, member); + return ModuleDecl::matchesAccessPath(accessPath, + member.getFullName()); })) { - lookupInModule(module, member, decls, + lookupInModule(module, member.getFullName(), decls, NLKind::QualifiedLookup, kind, topLevelScope); } } - pruneLookupResultSet(this, options, decls); + pruneLookupResultSet(DC, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInModule(this, module, member, options, decls); + if (auto *debugClient = DC->getParentModule()->getDebugClient()) { + debugClient->finishLookupInModule(DC, module, member.getFullName(), + options, decls); } - // We're done. Report success/failure. - return !decls.empty(); + + return decls; } -bool DeclContext::lookupAnyObject(DeclName member, NLOptions options, - SmallVectorImpl &decls) const { +llvm::Expected +AnyObjectLookupRequest::evaluate(Evaluator &evaluator, const DeclContext *dc, + DeclNameRef member, NLOptions options) const { using namespace namelookup; - assert(decls.empty() && "additive lookup not supported"); + QualifiedLookupResult decls; // Configure lookup and dig out the tracker. ReferencedNameTracker *tracker = nullptr; bool isLookupCascading; - configureLookup(this, options, tracker, isLookupCascading); + configureLookup(dc, options, tracker, isLookupCascading); // Record this lookup. if (tracker) @@ -1727,16 +1774,13 @@ bool DeclContext::lookupAnyObject(DeclName member, NLOptions options, // Type-only lookup won't find anything on AnyObject. if (options & NL_OnlyTypes) - return false; - - auto *stats = getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumLookupQualifiedInAnyObject++; + return decls; // Collect all of the visible declarations. SmallVector allDecls; - for (auto import : namelookup::getAllImports(this)) { - import.second->lookupClassMember(import.first, member, allDecls); + for (auto import : namelookup::getAllImports(dc)) { + import.second->lookupClassMember(import.first, member.getFullName(), + allDecls); } // For each declaration whose context is not something we've @@ -1753,26 +1797,24 @@ bool DeclContext::lookupAnyObject(DeclName member, NLOptions options, if (decl->getOverriddenDecl()) continue; - auto dc = decl->getDeclContext(); - auto nominal = dc->getSelfNominalTypeDecl(); - assert(nominal && "Couldn't find nominal type?"); - (void)nominal; + assert(decl->getDeclContext()->isTypeContext() && + "Couldn't find nominal type?"); // If we didn't see this declaration before, and it's an acceptable // result, add it to the list. // declaration to the list. if (knownDecls.insert(decl).second && - isAcceptableLookupResult(this, options, decl, + isAcceptableLookupResult(dc, options, decl, /*onlyCompleteObjectInits=*/false)) decls.push_back(decl); } - pruneLookupResultSet(this, options, decls); - if (auto *debugClient = this->getParentModule()->getDebugClient()) { - debugClient->finishLookupInAnyObject(this, member, options, decls); + pruneLookupResultSet(dc, options, decls); + if (auto *debugClient = dc->getParentModule()->getDebugClient()) { + debugClient->finishLookupInAnyObject(dc, member.getFullName(), options, + decls); } - // We're done. Report success/failure. - return !decls.empty(); + return decls; } void DeclContext::lookupAllObjCMethods( @@ -1840,8 +1882,8 @@ resolveTypeDeclsToNominal(Evaluator &evaluator, if (auto compound = dyn_cast(typeRepr)) { auto components = compound->getComponents(); if (components.size() == 2 && - components[0]->getIdentifier().is("Builtin") && - components[1]->getIdentifier().is("AnyObject")) { + components[0]->getNameRef().isSimpleName("Builtin") && + components[1]->getNameRef().isSimpleName("AnyObject")) { anyObject = true; } } @@ -1884,18 +1926,21 @@ resolveTypeDeclsToNominal(Evaluator &evaluator, /// Perform unqualified name lookup for types at the given location. static DirectlyReferencedTypeDecls -directReferencesForUnqualifiedTypeLookup(DeclName name, +directReferencesForUnqualifiedTypeLookup(DeclNameRef name, SourceLoc loc, DeclContext *dc, LookupOuterResults lookupOuter) { DirectlyReferencedTypeDecls results; - UnqualifiedLookup::Options options = - UnqualifiedLookup::Flags::TypeLookup | - UnqualifiedLookup::Flags::AllowProtocolMembers; + UnqualifiedLookupOptions options = + UnqualifiedLookupFlags::TypeLookup | + UnqualifiedLookupFlags::AllowProtocolMembers; if (lookupOuter == LookupOuterResults::Included) - options |= UnqualifiedLookup::Flags::IncludeOuterResults; + options |= UnqualifiedLookupFlags::IncludeOuterResults; - UnqualifiedLookup lookup(name, dc, loc, options); - for (const auto &result : lookup.Results) { + auto &ctx = dc->getASTContext(); + auto descriptor = UnqualifiedLookupDescriptor(name, dc, loc, options); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); + for (const auto &result : lookup.allResults()) { if (auto typeDecl = dyn_cast(result.getValueDecl())) results.push_back(typeDecl); } @@ -1908,7 +1953,7 @@ static DirectlyReferencedTypeDecls directReferencesForQualifiedTypeLookup(Evaluator &evaluator, ASTContext &ctx, ArrayRef baseTypes, - DeclName name, + DeclNameRef name, DeclContext *dc) { DirectlyReferencedTypeDecls result; auto addResults = [&result](ArrayRef found){ @@ -1939,7 +1984,9 @@ directReferencesForQualifiedTypeLookup(Evaluator &evaluator, auto innerOptions = options; innerOptions &= ~NL_RemoveOverridden; innerOptions &= ~NL_RemoveNonVisible; - dc->lookupQualified(module, name, innerOptions, members); + SmallVector moduleMembers; + dc->lookupQualified(module, name, innerOptions, moduleMembers); + members.append(moduleMembers.begin(), moduleMembers.end()); } addResults(members); @@ -1966,8 +2013,8 @@ directReferencesForIdentTypeRepr(Evaluator &evaluator, // For the first component, perform unqualified name lookup. if (current.empty()) { current = - directReferencesForUnqualifiedTypeLookup(component->getIdentifier(), - component->getIdLoc(), + directReferencesForUnqualifiedTypeLookup(component->getNameRef(), + component->getLoc(), dc, LookupOuterResults::Excluded); @@ -1982,7 +2029,7 @@ directReferencesForIdentTypeRepr(Evaluator &evaluator, // For subsequent components, perform qualified name lookup. current = directReferencesForQualifiedTypeLookup(evaluator, ctx, current, - component->getIdentifier(), dc); + component->getNameRef(), dc); if (current.empty()) return current; } @@ -2252,7 +2299,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, modulesFound.clear(); anyObject = false; decls = directReferencesForUnqualifiedTypeLookup( - identTypeRepr->getIdentifier(), identTypeRepr->getIdLoc(), dc, + identTypeRepr->getNameRef(), identTypeRepr->getLoc(), dc, LookupOuterResults::Included); nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls, modulesFound, anyObject); @@ -2263,7 +2310,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, auto moduleName = nominal->getParentModule()->getName(); ctx.Diags.diagnose(typeRepr->getLoc(), diag::warn_property_wrapper_module_scope, - identTypeRepr->getIdentifier(), + identTypeRepr->getNameRef(), moduleName) .fixItInsert(typeRepr->getLoc(), moduleName.str().str() + "."); @@ -2272,7 +2319,8 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, assocType->getFullName()); ComponentIdentTypeRepr *components[2] = { - new (ctx) SimpleIdentTypeRepr(typeRepr->getLoc(), moduleName), + new (ctx) SimpleIdentTypeRepr(identTypeRepr->getNameLoc(), + DeclNameRef(moduleName)), identTypeRepr }; @@ -2290,7 +2338,7 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, void swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, unsigned i, - llvm::SmallVectorImpl> &result, + llvm::SmallVectorImpl> &result, bool &anyObject) { auto typeDecl = decl.dyn_cast(); auto extDecl = decl.dyn_cast(); @@ -2319,11 +2367,11 @@ void swift::getDirectlyInheritedNominalTypeDecls( // Form the result. for (auto nominal : nominalTypes) { - result.push_back({loc, nominal}); + result.push_back({nominal, loc}); } } -SmallVector, 4> +SmallVector, 4> swift::getDirectlyInheritedNominalTypeDecls( llvm::PointerUnion decl, bool &anyObject) { @@ -2333,7 +2381,7 @@ swift::getDirectlyInheritedNominalTypeDecls( // Gather results from all of the inherited types. unsigned numInherited = typeDecl ? typeDecl->getInherited().size() : extDecl->getInherited().size(); - SmallVector, 4> result; + SmallVector, 4> result; for (unsigned i : range(numInherited)) { getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject); } @@ -2345,11 +2393,32 @@ swift::getDirectlyInheritedNominalTypeDecls( // FIXME: Refactor SelfBoundsFromWhereClauseRequest to dig out // the source location. SourceLoc loc = SourceLoc(); + + // For a deserialized protocol, the where clause isn't going to tell us + // anything. Ask the requirement signature instead. + if (protoDecl->wasDeserialized()) { + auto protoSelfTy = protoDecl->getSelfInterfaceType(); + for (auto &req : protoDecl->getRequirementSignature()) { + // Dig out a conformance requirement... + if (req.getKind() != RequirementKind::Conformance) + continue; + + // constraining Self. + if (!req.getFirstType()->isEqual(protoSelfTy)) + continue; + + result.emplace_back(req.getSecondType()->castTo()->getDecl(), + loc); + } + return result; + } + + // Else we have access to this information on the where clause. auto selfBounds = getSelfBoundsFromWhereClause(decl); anyObject |= selfBounds.anyObject; for (auto inheritedNominal : selfBounds.decls) - result.emplace_back(loc, inheritedNominal); + result.emplace_back(inheritedNominal, loc); return result; } @@ -2406,7 +2475,7 @@ void FindLocalVal::checkGenericParams(GenericParamList *Params) { } void FindLocalVal::checkSourceFile(const SourceFile &SF) { - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) if (auto *TLCD = dyn_cast(D)) visitBraceStmt(TLCD->getBody(), /*isTopLevel=*/true); } @@ -2443,7 +2512,8 @@ void FindLocalVal::visitGuardStmt(GuardStmt *S) { return; // Names in the guard aren't visible until after the body. - if (!isReferencePointInRange(S->getBody()->getSourceRange())) + if (S->getBody()->isImplicit() || + !isReferencePointInRange(S->getBody()->getSourceRange())) checkStmtCondition(S->getCond()); visit(S->getBody()); @@ -2545,3 +2615,40 @@ void FindLocalVal::visitCatchStmt(CatchStmt *S) { checkPattern(S->getErrorPattern(), DeclVisibilityKind::LocalVariable); visit(S->getBody()); } + +void swift::simple_display(llvm::raw_ostream &out, NLKind kind) { + switch (kind) { + case NLKind::QualifiedLookup: + out << "QualifiedLookup"; + return; + case NLKind::UnqualifiedLookup: + out << "UnqualifiedLookup"; + return; + } + llvm_unreachable("Unhandled case in switch"); +} + +void swift::simple_display(llvm::raw_ostream &out, NLOptions options) { + using Flag = std::pair; + Flag possibleFlags[] = { +#define FLAG(Name) {Name, #Name}, + FLAG(NL_ProtocolMembers) + FLAG(NL_RemoveNonVisible) + FLAG(NL_RemoveOverridden) + FLAG(NL_IgnoreAccessControl) + FLAG(NL_KnownNonCascadingDependency) + FLAG(NL_KnownCascadingDependency) + FLAG(NL_OnlyTypes) + FLAG(NL_IncludeAttributeImplements) +#undef FLAG + }; + + auto flagsToPrint = llvm::make_filter_range( + possibleFlags, [&](Flag flag) { return options & flag.first; }); + + out << "{ "; + interleave( + flagsToPrint, [&](Flag flag) { out << flag.second; }, + [&] { out << ", "; }); + out << " }"; +} diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index b073e42ddfcef..0a48749b5e394 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -67,6 +67,47 @@ void SuperclassDeclRequest::cacheResult(ClassDecl *value) const { protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true); } +//----------------------------------------------------------------------------// +// Missing designated initializers computation +//----------------------------------------------------------------------------// + +Optional HasMissingDesignatedInitializersRequest::getCachedResult() const { + auto classDecl = std::get<0>(getStorage()); + return classDecl->getCachedHasMissingDesignatedInitializers(); +} + +void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const { + auto classDecl = std::get<0>(getStorage()); + classDecl->setHasMissingDesignatedInitializers(result); +} + +llvm::Expected +HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator, + ClassDecl *subject) const { + // Short-circuit and check for the attribute here. + if (subject->getAttrs().hasAttribute()) + return true; + + AccessScope scope = + subject->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + // This flag only makes sense for public types that will be written in the + // module. + if (!scope.isPublic()) + return false; + + auto constructors = subject->lookupDirect(DeclBaseName::createConstructor()); + return llvm::any_of(constructors, [&](ValueDecl *decl) { + auto init = cast(decl); + if (!init->isDesignatedInit()) + return false; + AccessScope scope = + init->getFormalAccessScope(/*useDC*/nullptr, + /*treatUsableFromInlineAsPublic*/true); + return !scope.isPublic(); + }); +} + //----------------------------------------------------------------------------// // Extended nominal computation. //----------------------------------------------------------------------------// @@ -128,24 +169,41 @@ void GenericParamListRequest::cacheResult(GenericParamList *params) const { context->GenericParamsAndBit.setPointerAndInt(params, true); } - //----------------------------------------------------------------------------// -// LookupPrecedenceGroupRequest computation. +// UnqualifiedLookupRequest computation. //----------------------------------------------------------------------------// -SourceLoc LookupPrecedenceGroupRequest::getNearestLoc() const { - auto &desc = std::get<0>(getStorage()); - return desc.getLoc(); +void swift::simple_display(llvm::raw_ostream &out, + const UnqualifiedLookupDescriptor &desc) { + out << "looking up "; + simple_display(out, desc.Name); + out << " from "; + simple_display(out, desc.DC); + out << " with options "; + simple_display(out, desc.Options); } -SourceLoc PrecedenceGroupDescriptor::getLoc() const { - return nameLoc; +SourceLoc +swift::extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc) { + return extractNearestSourceLoc(desc.DC); } +//----------------------------------------------------------------------------// +// DirectLookupRequest computation. +//----------------------------------------------------------------------------// + void swift::simple_display(llvm::raw_ostream &out, - const PrecedenceGroupDescriptor &desc) { - out << "precedence group " << desc.ident << " at "; - desc.nameLoc.print(out, desc.dc->getASTContext().SourceMgr); + const DirectLookupDescriptor &desc) { + out << "directly looking up "; + simple_display(out, desc.Name); + out << " on "; + simple_display(out, desc.DC); + out << " with options "; + simple_display(out, desc.Options); +} + +SourceLoc swift::extractNearestSourceLoc(const DirectLookupDescriptor &desc) { + return extractNearestSourceLoc(desc.DC); } // Define request evaluation functions for each of the name lookup requests. diff --git a/lib/AST/Parameter.cpp b/lib/AST/Parameter.cpp index f645d0cceba8f..8f3ace9139036 100644 --- a/lib/AST/Parameter.cpp +++ b/lib/AST/Parameter.cpp @@ -67,10 +67,9 @@ ParameterList *ParameterList::clone(const ASTContext &C, if (options & Implicit) decl->setImplicit(); - // If the argument isn't named, and we're cloning for an inherited - // constructor, give the parameter a name so that silgen will produce a - // value for it. - if (decl->getName().empty() && (options & Inherited)) + // If the argument isn't named, give the parameter a name so that + // silgen will produce a value for it. + if (decl->getName().empty() && (options & NamedArguments)) decl->setName(C.getIdentifier("argument")); // If we're inheriting a default argument, mark it as such. diff --git a/lib/AST/Pattern.cpp b/lib/AST/Pattern.cpp index 447ba7fe0abbd..9f2924ac2945c 100644 --- a/lib/AST/Pattern.cpp +++ b/lib/AST/Pattern.cpp @@ -496,3 +496,33 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + + +ContextualPattern ContextualPattern::forPatternBindingDecl( + PatternBindingDecl *pbd, unsigned index) { + return ContextualPattern( + pbd->getPattern(index), /*isTopLevel=*/true, pbd, index); +} + +DeclContext *ContextualPattern::getDeclContext() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->getDeclContext(); + + return declOrContext.get(); +} + +PatternBindingDecl *ContextualPattern::getPatternBindingDecl() const { + return declOrContext.dyn_cast(); +} + +bool ContextualPattern::allowsInference() const { + if (auto pbd = getPatternBindingDecl()) + return pbd->isInitialized(index); + + return true; +} + +void swift::simple_display(llvm::raw_ostream &out, + const ContextualPattern &pattern) { + out << "(pattern @ " << pattern.getPattern() << ")"; +} diff --git a/lib/AST/PlatformKind.cpp b/lib/AST/PlatformKind.cpp index c62460b05b97a..1119601d458c1 100644 --- a/lib/AST/PlatformKind.cpp +++ b/lib/AST/PlatformKind.cpp @@ -16,6 +16,7 @@ #include "swift/AST/PlatformKind.h" #include "swift/Basic/LangOptions.h" +#include "swift/Basic/Platform.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" @@ -63,7 +64,8 @@ static bool isPlatformActiveForTarget(PlatformKind Platform, return true; if (Platform == PlatformKind::OSXApplicationExtension || - Platform == PlatformKind::iOSApplicationExtension) + Platform == PlatformKind::iOSApplicationExtension || + Platform == PlatformKind::macCatalystApplicationExtension) if (!EnableAppExtensionRestrictions) return false; @@ -75,6 +77,9 @@ static bool isPlatformActiveForTarget(PlatformKind Platform, case PlatformKind::iOS: case PlatformKind::iOSApplicationExtension: return Target.isiOS() && !Target.isTvOS(); + case PlatformKind::macCatalyst: + case PlatformKind::macCatalystApplicationExtension: + return tripleIsMacCatalystEnvironment(Target); case PlatformKind::tvOS: case PlatformKind::tvOSApplicationExtension: return Target.isTvOS(); @@ -87,8 +92,15 @@ static bool isPlatformActiveForTarget(PlatformKind Platform, llvm_unreachable("bad PlatformKind"); } -bool swift::isPlatformActive(PlatformKind Platform, LangOptions &LangOpts) { +bool swift::isPlatformActive(PlatformKind Platform, LangOptions &LangOpts, + bool ForTargetVariant) { llvm::Triple TT = LangOpts.Target; + + if (ForTargetVariant) { + assert(LangOpts.TargetVariant && "Must have target variant triple"); + TT = *LangOpts.TargetVariant; + } + return isPlatformActiveForTarget(Platform, TT, LangOpts.EnableAppExtensionRestrictions); } @@ -113,6 +125,10 @@ PlatformKind swift::targetPlatform(LangOptions &LangOpts) { } if (LangOpts.Target.isiOS()) { + if (tripleIsMacCatalystEnvironment(LangOpts.Target)) + return (LangOpts.EnableAppExtensionRestrictions + ? PlatformKind::macCatalystApplicationExtension + : PlatformKind::macCatalyst); return (LangOpts.EnableAppExtensionRestrictions ? PlatformKind::iOSApplicationExtension : PlatformKind::iOS); @@ -120,3 +136,22 @@ PlatformKind swift::targetPlatform(LangOptions &LangOpts) { return PlatformKind::none; } + +bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child, + PlatformKind Parent) { + if (Child == PlatformKind::macCatalyst && Parent == PlatformKind::iOS) + return true; + + if (Child == PlatformKind::macCatalystApplicationExtension) { + if (Parent == PlatformKind::iOS || + Parent == PlatformKind::iOSApplicationExtension || + Parent == PlatformKind::macCatalyst) { + return true; + } + } + + // Ideally we would have all ApplicationExtension platforms + // inherit from their non-extension platform. + + return false; +} diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index c1516f4373c6a..84626c26178cc 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -14,17 +14,18 @@ // //===----------------------------------------------------------------------===// +#include "swift/AST/ProtocolConformance.h" #include "ConformanceLookupTable.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Availability.h" #include "swift/AST/Decl.h" #include "swift/AST/FileUnit.h" -#include "swift/AST/LazyResolver.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" -#include "swift/AST/ProtocolConformance.h" -#include "swift/AST/Types.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeWalker.h" +#include "swift/AST/Types.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/Statistic.h" @@ -128,21 +129,17 @@ ProtocolConformanceRef::subst(Type origType, // If the type is an existential, it must be self-conforming. if (substType->isExistentialType()) { - if (auto optConformance = - proto->getModuleContext()->lookupExistentialConformance( - substType, proto)) - return *optConformance; + auto optConformance = + proto->getModuleContext()->lookupExistentialConformance(substType, + proto); + if (optConformance) + return optConformance; return ProtocolConformanceRef::forInvalid(); } // Check the conformance map. - if (auto result = conformances(origType->getCanonicalType(), - substType, proto)) { - return *result; - } - - return ProtocolConformanceRef::forInvalid(); + return conformances(origType->getCanonicalType(), substType, proto); } ProtocolConformanceRef ProtocolConformanceRef::mapConformanceOutOfContext() const { @@ -203,24 +200,12 @@ void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, #define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&ProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&ProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ - static_assert(&ProtocolConformance::Method != \ - &SpecializedProtocolConformance::Method, \ - "Must override SpecializedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Inherited: \ - static_assert(&ProtocolConformance::Method != \ - &InheritedProtocolConformance::Method, \ - "Must override InheritedProtocolConformance::" #Method); \ return cast(this)->Method Args; \ } \ llvm_unreachable("bad ProtocolConformanceKind"); @@ -228,14 +213,8 @@ llvm_unreachable("bad ProtocolConformanceKind"); #define ROOT_CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ switch (getKind()) { \ case ProtocolConformanceKind::Normal: \ - static_assert(&RootProtocolConformance::Method != \ - &NormalProtocolConformance::Method, \ - "Must override NormalProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Self: \ - static_assert(&RootProtocolConformance::Method != \ - &SelfProtocolConformance::Method, \ - "Must override SelfProtocolConformance::" #Method); \ return cast(this)->Method Args; \ case ProtocolConformanceKind::Specialized: \ case ProtocolConformanceKind::Inherited: \ @@ -269,7 +248,7 @@ ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType) const { CONFORMANCE_SUBCLASS_DISPATCH(hasTypeWitness, (assocType)); } -std::pair +TypeWitnessAndDecl ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options) const { CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl, @@ -278,7 +257,7 @@ ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType, SubstOptions options) const { - return getTypeWitnessAndDecl(assocType, options).first; + return getTypeWitnessAndDecl(assocType, options).getWitnessType(); } ConcreteDeclRef @@ -568,8 +547,8 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements() } auto extensionSig = ext->getGenericSignature(); - auto canExtensionSig = extensionSig->getCanonicalSignature(); - auto canTypeSig = typeSig->getCanonicalSignature(); + auto canExtensionSig = extensionSig.getCanonicalSignature(); + auto canTypeSig = typeSig.getCanonicalSignature(); if (canTypeSig == canExtensionSig) { success({}); return; @@ -659,13 +638,12 @@ bool NormalProtocolConformance::hasTypeWitness( auto found = TypeWitnesses.find(assocType); if (found != TypeWitnesses.end()) { - return !found->getSecond().first.isNull(); + return !found->getSecond().getWitnessType().isNull(); } return false; } -using TypeWitnessAndDecl = std::pair; TypeWitnessAndDecl NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, SubstOptions options) const { @@ -693,21 +671,23 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, // If the conditional requirements aren't known, we can't properly run // inference. if (!getConditionalRequirementsIfAvailable()) { - return {Type(), nullptr}; + return TypeWitnessAndDecl(); } - // Otherwise, resolve the type witness. - PrettyStackTraceRequirement trace("resolving", this, assocType); - auto *resolver = assocType->getASTContext().getLazyResolver(); - assert(resolver && "Unable to resolve type witness"); - - // Block recursive resolution of this type witness. - TypeWitnesses[assocType] = { Type(), nullptr }; - resolver->resolveTypeWitness(this, assocType); + return evaluateOrDefault( + assocType->getASTContext().evaluator, + TypeWitnessRequest{const_cast(this), + assocType}, + TypeWitnessAndDecl()); +} - known = TypeWitnesses.find(assocType); - assert(known != TypeWitnesses.end() && "Didn't resolve witness?"); - return known->second; +TypeWitnessAndDecl NormalProtocolConformance::getTypeWitnessUncached( + AssociatedTypeDecl *requirement) const { + auto entry = TypeWitnesses.find(requirement); + if (entry == TypeWitnesses.end()) { + return TypeWitnessAndDecl(); + } + return entry->second; } void NormalProtocolConformance::setTypeWitness(AssociatedTypeDecl *assocType, @@ -716,11 +696,11 @@ void NormalProtocolConformance::setTypeWitness(AssociatedTypeDecl *assocType, assert(getProtocol() == cast(assocType->getDeclContext()) && "associated type in wrong protocol"); assert((TypeWitnesses.count(assocType) == 0 || - TypeWitnesses[assocType].first.isNull()) && + TypeWitnesses[assocType].getWitnessType().isNull()) && "Type witness already known"); assert((!isComplete() || isInvalid()) && "Conformance already complete?"); assert(!type->hasArchetype() && "type witnesses must be interface types"); - TypeWitnesses[assocType] = std::make_pair(type, typeDecl); + TypeWitnesses[assocType] = {type, typeDecl}; } Type ProtocolConformance::getAssociatedType(Type assocType) const { @@ -841,8 +821,8 @@ recursivelySubstituteBaseType(ModuleDecl *module, // If we have an inherited protocol just look up the conformance. if (reqProto != conformance->getProtocol()) { - reqConformance = module->lookupConformance( - conformance->getType(), reqProto)->getConcrete(); + reqConformance = module->lookupConformance(conformance->getType(), reqProto) + .getConcrete(); } return reqConformance->getTypeWitness(depMemTy->getAssocType()); @@ -894,12 +874,8 @@ void NormalProtocolConformance::finishSignatureConformances() { if (substTy->hasTypeParameter()) substTy = getDeclContext()->mapTypeIntoContext(substTy); - auto reqConformance = module->lookupConformance(substTy, reqProto); - if (!reqConformance) - reqConformance = ProtocolConformanceRef::forInvalid(); - - reqConformance = reqConformance->mapConformanceOutOfContext(); - reqConformances.push_back(*reqConformance); + reqConformances.push_back(module->lookupConformance(substTy, reqProto) + .mapConformanceOutOfContext()); } setSignatureConformances(reqConformances); } @@ -916,20 +892,20 @@ Witness NormalProtocolConformance::getWitness(ValueDecl *requirement) const { if (Loader) resolveLazyInfo(); - auto known = Mapping.find(requirement); - if (known == Mapping.end()) { - auto *resolver = requirement->getASTContext().getLazyResolver(); - assert(resolver && "Unable to resolve witness without resolver"); - resolver->resolveWitness(this, requirement); - known = Mapping.find(requirement); - } - if (known != Mapping.end()) { - return known->second; - } else { - assert((!isComplete() || isInvalid()) && - "Resolver did not resolve requirement"); + return evaluateOrDefault( + requirement->getASTContext().evaluator, + ValueWitnessRequest{const_cast(this), + requirement}, + Witness()); +} + +Witness +NormalProtocolConformance::getWitnessUncached(ValueDecl *requirement) const { + auto entry = Mapping.find(requirement); + if (entry == Mapping.end()) { return Witness(); } + return entry->second; } Witness SelfProtocolConformance::getWitness(ValueDecl *requirement) const { @@ -1020,7 +996,7 @@ bool SpecializedProtocolConformance::hasTypeWitness( GenericConformance->hasTypeWitness(assocType); } -std::pair +TypeWitnessAndDecl SpecializedProtocolConformance::getTypeWitnessAndDecl( AssociatedTypeDecl *assocType, SubstOptions options) const { @@ -1048,16 +1024,16 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl( auto genericWitnessAndDecl = GenericConformance->getTypeWitnessAndDecl(assocType, options); - auto genericWitness = genericWitnessAndDecl.first; + auto genericWitness = genericWitnessAndDecl.getWitnessType(); if (!genericWitness) return { Type(), nullptr }; - auto *typeDecl = genericWitnessAndDecl.second; + auto *typeDecl = genericWitnessAndDecl.getWitnessDecl(); // Form the substitution. auto substitutionMap = getSubstitutionMap(); if (substitutionMap.empty()) - return {Type(), nullptr}; + return TypeWitnessAndDecl(); // Apply the substitution we computed above auto specializedType = genericWitness.subst(substitutionMap, options); @@ -1070,7 +1046,7 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl( // If we aren't in a case where we used the tentative type witness // information, cache the result. - auto specializedWitnessAndDecl = std::make_pair(specializedType, typeDecl); + auto specializedWitnessAndDecl = TypeWitnessAndDecl{specializedType, typeDecl}; if (!isTentativeWitness() && !specializedType->hasError()) TypeWitnesses[assocType] = specializedWitnessAndDecl; @@ -1524,3 +1500,8 @@ const UnifiedStatsReporter::TraceFormatter* FrontendStatsTracer::getTraceFormatter() { return &TF; } + +void swift::simple_display(llvm::raw_ostream &out, + const ProtocolConformance *conf) { + conf->printName(out); +} diff --git a/lib/AST/RequirementEnvironment.cpp b/lib/AST/RequirementEnvironment.cpp index e84797f3f78da..765eeb67d2527 100644 --- a/lib/AST/RequirementEnvironment.cpp +++ b/lib/AST/RequirementEnvironment.cpp @@ -111,7 +111,7 @@ RequirementEnvironment::RequirementEnvironment( }, [selfType, substConcreteType, conformance, conformanceDC, &ctx]( CanType type, Type replacement, ProtocolDecl *proto) - -> Optional { + -> ProtocolConformanceRef { // The protocol 'Self' conforms concretely to the conforming type. if (type->isEqual(selfType)) { ProtocolConformance *specialized = conformance; @@ -140,7 +140,7 @@ RequirementEnvironment::RequirementEnvironment( reqSig->getRequirements().size() == 1) { syntheticSignature = conformanceDC->getGenericSignatureOfContext(); if (syntheticSignature) { - syntheticSignature = syntheticSignature->getCanonicalSignature(); + syntheticSignature = syntheticSignature.getCanonicalSignature(); syntheticEnvironment = syntheticSignature->getGenericEnvironment(); } diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index e60cee1395e11..dc90a47f7714a 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -152,7 +152,7 @@ bool SubstitutionMap::isCanonical() const { SubstitutionMap SubstitutionMap::getCanonical() const { if (empty()) return *this; - auto canonicalSig = getGenericSignature()->getCanonicalSignature(); + auto canonicalSig = getGenericSignature().getCanonicalSignature(); SmallVector replacementTypes; for (Type replacementType : getReplacementTypesBuffer()) { if (replacementType) @@ -222,8 +222,7 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, auto replacement = depTy.subst(subs, lookupConformance); auto protoType = req.getSecondType()->castTo(); auto proto = protoType->getDecl(); - auto conformance = lookupConformance(depTy, replacement, proto) - .getValueOr(ProtocolConformanceRef::forInvalid()); + auto conformance = lookupConformance(depTy, replacement, proto); conformances.push_back(conformance); } @@ -301,9 +300,10 @@ Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const { return replacementType; } -Optional +ProtocolConformanceRef SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { - if (empty()) return None; + if (empty()) + return ProtocolConformanceRef::forInvalid(); // If we have an archetype, map out of the context so we can compute a // conformance access path. @@ -316,7 +316,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // Error path: if we don't have a type parameter, there is no conformance. // FIXME: Query concrete conformances in the generic signature? if (!type->isTypeParameter()) - return None; + return ProtocolConformanceRef::forInvalid(); auto genericSig = getGenericSignature(); @@ -334,23 +334,23 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // Retrieve the starting conformance from the conformance map. auto getInitialConformance = - [&](Type type, ProtocolDecl *proto) -> Optional { - unsigned conformanceIndex = 0; - for (const auto &req : getGenericSignature()->getRequirements()) { - if (req.getKind() != RequirementKind::Conformance) - continue; - - // Is this the conformance we're looking for? - if (req.getFirstType()->isEqual(type) && - req.getSecondType()->castTo()->getDecl() == proto) { - return getConformances()[conformanceIndex]; - } - - ++conformanceIndex; + [&](Type type, ProtocolDecl *proto) -> ProtocolConformanceRef { + unsigned conformanceIndex = 0; + for (const auto &req : getGenericSignature()->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) + continue; + + // Is this the conformance we're looking for? + if (req.getFirstType()->isEqual(type) && + req.getSecondType()->castTo()->getDecl() == proto) { + return getConformances()[conformanceIndex]; } - return None; - }; + ++conformanceIndex; + } + + return ProtocolConformanceRef::forInvalid(); + }; // Check whether the superclass conforms. if (auto superclass = genericSig->getSuperclassBound(type)) { @@ -362,30 +362,30 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // If the type doesn't conform to this protocol, the result isn't formed // from these requirements. if (!genericSig->conformsToProtocol(type, proto)) - return None; + return ProtocolConformanceRef::forInvalid(); auto accessPath = genericSig->getConformanceAccessPath(type, proto); // Fall through because we cannot yet evaluate an access path. - Optional conformance; + ProtocolConformanceRef conformance; for (const auto &step : accessPath) { // For the first step, grab the initial conformance. - if (!conformance) { + if (conformance.isInvalid()) { conformance = getInitialConformance(step.first, step.second); - if (!conformance) - return None; + if (conformance.isInvalid()) + return ProtocolConformanceRef::forInvalid(); continue; } - if (conformance->isInvalid()) + if (conformance.isInvalid()) return conformance; // If we've hit an abstract conformance, everything from here on out is // abstract. // FIXME: This may not always be true, but it holds for now. - if (conformance->isAbstract()) { + if (conformance.isAbstract()) { // FIXME: Rip this out once we can get a concrete conformance from // an archetype. auto substType = type.subst(*this); @@ -405,7 +405,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // For the second step, we're looking into the requirement signature for // this protocol. - auto concrete = conformance->getConcrete(); + auto concrete = conformance.getConcrete(); auto normal = concrete->getRootNormalConformance(); // If we haven't set the signature conformances yet, force the issue now. @@ -415,7 +415,7 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // FIXME: Seems like we should be able to get at the intermediate state // to use that. if (normal->getState() == ProtocolConformanceState::CheckingTypeWitnesses) - return None; + return ProtocolConformanceRef::forInvalid(); } // Get the associated conformance. @@ -555,12 +555,8 @@ SubstitutionMap::getOverrideSubstitutions(const ClassDecl *baseClass, SubstitutionMap origSubMap; if (derivedSubs) origSubMap = *derivedSubs; - else if (derivedSig) { - origSubMap = get( - derivedSig, - [](SubstitutableType *type) -> Type { return type; }, - MakeAbstractConformanceForGenericType()); - } + else if (derivedSig) + origSubMap = derivedSig->getIdentitySubstitutionMap(); return combineSubstitutionMaps(baseSubMap, origSubMap, CombineSubstitutionMaps::AtDepth, @@ -603,17 +599,50 @@ SubstitutionMap::combineSubstitutionMaps(SubstitutionMap firstSubMap, return get( genericSig, [&](SubstitutableType *type) { - auto replacement = replaceGenericParameter(type); - if (replacement) + if (auto replacement = replaceGenericParameter(type)) return Type(replacement).subst(secondSubMap); return Type(type).subst(firstSubMap); }, - [&](CanType type, Type substType, ProtocolDecl *conformedProtocol) { - auto replacement = type.transform(replaceGenericParameter); - if (replacement) + [&](CanType type, Type substType, ProtocolDecl *proto) { + if (auto replacement = type.transform(replaceGenericParameter)) return secondSubMap.lookupConformance(replacement->getCanonicalType(), - conformedProtocol); - return firstSubMap.lookupConformance(type, conformedProtocol); + proto); + if (auto conformance = firstSubMap.lookupConformance(type, proto)) + return conformance; + + // We might not have enough information in the substitution maps alone. + // + // Eg, + // + // class Base { + // func foo(_: U1) where T1 : P {} + // } + // + // class Derived : Base> { + // override func foo(_: U2) where T2 : Q {} + // } + // + // Suppose we're devirtualizing a call to Base.foo() on a value whose + // type is known to be Derived. We start with substitutions written + // in terms of Base.foo()'s generic signature: + // + // + // T1 := Foo + // T1 : P := Foo : P + // + // We want to build substitutions in terms of Derived.foo()'s + // generic signature: + // + // + // T2 := Bar + // T2 : Q := Bar : Q + // + // The conformance Bar : Q is difficult to recover in the general case. + // + // Some combination of storing substitution maps in BoundGenericTypes + // as well as for method overrides would solve this, but for now, just + // punt to module lookup. + return proto->getParentModule()->lookupConformance(substType, proto); }); } @@ -686,3 +715,11 @@ bool SubstitutionMap::isIdentity() const { return !hasNonIdentityReplacement; } + +SubstitutionMap SubstitutionMap::mapIntoTypeExpansionContext( + TypeExpansionContext context) const { + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + return this->subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); +} diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f009ec01361fc..679962777c587 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Types.h" #include "ForeignRepresentationInfo.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ReferenceCounting.h" #include "swift/AST/TypeCheckRequests.h" @@ -31,6 +32,7 @@ #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/TypeRepr.h" +#include "clang/AST/Type.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" @@ -157,6 +159,10 @@ bool TypeBase::isAny() { return isEqual(getASTContext().TheAnyType); } +bool TypeBase::isHole() { + return isEqual(getASTContext().TheUnresolvedType); +} + bool TypeBase::isAnyClassReferenceType() { return getCanonicalType().isAnyClassReferenceType(); } @@ -645,6 +651,37 @@ Type TypeBase::wrapInPointer(PointerTypeKind kind) { return BoundGenericType::get(pointerDecl, /*parent*/nullptr, Type(this)); } +Type TypeBase::getAnyBufferPointerElementType(BufferPointerTypeKind &BPTK) { + auto &C = getASTContext(); + if (auto nominalTy = getAs()) { + if (nominalTy->getDecl() == C.getUnsafeMutableRawBufferPointerDecl()) { + BPTK = BPTK_UnsafeMutableRawBufferPointer; + } else if (nominalTy->getDecl() == C.getUnsafeRawBufferPointerDecl()) { + BPTK = BPTK_UnsafeRawBufferPointer; + } else { + return Type(); + } + return C.TheEmptyTupleType; + } + if (auto boundTy = getAs()) { + if (boundTy->getDecl() == C.getUnsafeMutableBufferPointerDecl()) { + BPTK = BPTK_UnsafeMutableBufferPointer; + } else if (boundTy->getDecl() == C.getUnsafeBufferPointerDecl()) { + BPTK = BPTK_UnsafeBufferPointer; + } else { + return Type(); + } + return boundTy->getGenericArgs()[0]; + } + return Type(); +} + +Type TypeBase::lookThroughSingleOptionalType() { + Type type(this); + if (auto objType = type->getOptionalObjectType()) + type = objType; + return type; +} Type TypeBase::lookThroughAllOptionalTypes() { Type type(this); @@ -781,7 +818,6 @@ ParameterListInfo::ParameterListInfo( const ValueDecl *paramOwner, bool skipCurriedSelf) { defaultArguments.resize(params.size()); - functionBuilderTypes.resize(params.size()); // No parameter owner means no parameter list means no default arguments // - hand back the zeroed bitvector. @@ -811,19 +847,14 @@ ParameterListInfo::ParameterListInfo( return; } - switch (params.size()) { - case 0: + if (params.empty()) return; - default: - // Arguments and parameters are not guaranteed to always line-up - // perfectly, e.g. failure diagnostics tries to match argument type - // to different "candidate" parameters. - if (params.size() != paramList->size()) - return; - - break; - } + // Arguments and parameters are not guaranteed to always line-up + // perfectly, e.g. failure diagnostics tries to match argument type + // to different "candidate" parameters. + if (params.size() != paramList->size()) + return; // Note which parameters have default arguments and/or function builders. for (auto i : range(0, params.size())) { @@ -831,10 +862,6 @@ ParameterListInfo::ParameterListInfo( if (param->isDefaultArgument()) { defaultArguments.set(i); } - - if (Type functionBuilderType = param->getFunctionBuilderType()) { - functionBuilderTypes[i] = functionBuilderType; - } } } @@ -843,12 +870,6 @@ bool ParameterListInfo::hasDefaultArgument(unsigned paramIdx) const { : false; } -Type ParameterListInfo::getFunctionBuilderType(unsigned paramIdx) const { - return paramIdx < functionBuilderTypes.size() - ? functionBuilderTypes[paramIdx] - : Type(); -} - /// Turn a param list into a symbolic and printable representation that does not /// include the types, something like (_:, b:, c:) std::string swift::getParamListAsString(ArrayRef params) { @@ -1115,8 +1136,6 @@ CanType TypeBase::computeCanonicalType() { // If we haven't set a depth for this generic parameter, try to do so. // FIXME: This is a dreadful hack. if (gpDecl->getDepth() == GenericTypeParamDecl::InvalidDepth) { - auto resolver = gpDecl->getASTContext().getLazyResolver(); - assert(resolver && "Need to resolve generic parameter depth"); if (auto decl = gpDecl->getDeclContext()->getInnermostDeclarationDeclContext()) if (auto valueDecl = decl->getAsGenericContext()) @@ -1163,19 +1182,21 @@ CanType TypeBase::computeCanonicalType() { CanGenericSignature genericSig; if (auto *genericFnTy = dyn_cast(this)) - genericSig = genericFnTy->getGenericSignature()->getCanonicalSignature(); + genericSig = genericFnTy->getGenericSignature().getCanonicalSignature(); // Transform the parameter and result types. SmallVector canParams; getCanonicalParams(funcTy, genericSig, canParams); auto resultTy = funcTy->getResult()->getCanonicalType(genericSig); + bool useClangFunctionType = + resultTy->getASTContext().LangOpts.UseClangFunctionTypes; + auto extInfo = funcTy->getCanonicalExtInfo(useClangFunctionType); if (genericSig) { Result = GenericFunctionType::get(genericSig, canParams, resultTy, - funcTy->getExtInfo()); + extInfo); } else { - Result = FunctionType::get(canParams, resultTy, - funcTy->getExtInfo()); + Result = FunctionType::get(canParams, resultTy, extInfo); } assert(Result->isCanonical()); break; @@ -1447,6 +1468,28 @@ bool TypeBase::satisfiesClassConstraint() { return mayHaveSuperclass() || isObjCExistentialType(); } +bool TypeBase::isCallableNominalType(DeclContext *dc) { + // Don't allow callAsFunction to be used with dynamic lookup. + if (isAnyObject()) + return false; + + // If the type cannot have members, we're done. + if (!mayHaveMembers()) + return false; + + auto canTy = getCanonicalType(); + auto &ctx = canTy->getASTContext(); + return evaluateOrDefault(ctx.evaluator, + IsCallableNominalTypeRequest{canTy, dc}, false); +} + +bool TypeBase::hasDynamicMemberLookupAttribute() { + auto canTy = getCanonicalType(); + auto &ctx = canTy->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, HasDynamicMemberLookupAttributeRequest{canTy}, false); +} + Type TypeBase::getSuperclass(bool useArchetypes) { auto *nominalDecl = getAnyNominal(); auto *classDecl = dyn_cast_or_null(nominalDecl); @@ -1511,33 +1554,41 @@ bool TypeBase::isExactSuperclassOf(Type ty) { return false; } -/// Returns true if type `a` has archetypes that can be bound to form `b`. -bool TypeBase::isBindableTo(Type b) { - class IsBindableVisitor : public TypeVisitor - { - llvm::DenseMap Bindings; - - public: - IsBindableVisitor() {} +namespace { +class IsBindableVisitor + : public TypeVisitor +{ +public: + llvm::function_ref VisitBinding; + llvm::DenseMap Bindings; - bool visitArchetypeType(ArchetypeType *orig, CanType subst) { - // If we already bound this archetype, make sure the new binding candidate - // is the same type. - auto bound = Bindings.find(orig); - if (bound != Bindings.end()) { - return bound->second->isEqual(subst); - } + IsBindableVisitor( + llvm::function_ref VisitBinding) + : VisitBinding(VisitBinding) + {} + + CanType visitArchetypeType(ArchetypeType *orig, CanType subst) { + // If we already bound this archetype, make sure the new binding candidate + // is the same type. + auto bound = Bindings.find(orig); + if (bound != Bindings.end()) { + if (!bound->second->isEqual(subst)) + return CanType(); + + return VisitBinding(orig, subst); + } + if (!subst->isTypeParameter()) { // Check that the archetype isn't constrained in a way that makes the // binding impossible. // For instance, if the archetype is class-constrained, and the binding // is not a class, it can never be bound. if (orig->requiresClass() && !subst->satisfiesClassConstraint()) - return false; + return CanType(); if (auto superclass = orig->getSuperclass()) if (!superclass->isBindableToSuperclassOf(subst)) - return false; + return CanType(); // TODO: If the archetype has a superclass constraint, check that the // substitution is a subclass. @@ -1547,187 +1598,302 @@ bool TypeBase::isBindableTo(Type b) { // Otherwise, there may be an external retroactive conformance that // allows the binding. - - // Remember the binding, and succeed. - Bindings.insert({orig, subst}); - return true; } + // Remember the binding, and succeed. + Bindings.insert({orig, subst}); + return VisitBinding(orig, subst); + } + + CanType visitType(TypeBase *orig, CanType subst) { + if (CanType(orig) == subst) + return subst; - bool visitType(TypeBase *orig, CanType subst) { - if (CanType(orig) == subst) - return true; + return CanType(); + } + + CanType visitNominalType(NominalType *nom, CanType subst) { + if (auto substNom = dyn_cast(subst)) { + if (nom->getDecl() != substNom->getDecl()) + return CanType(); - return false; - } - - bool visitNominalType(NominalType *nom, CanType subst) { - if (auto substNom = dyn_cast(subst)) { - if (nom->getDecl() != substNom->getDecl()) - return false; - - if (nom->getDecl()->isInvalid()) - return false; - - // Same decl should always either have or not have a parent. - assert((bool)nom->getParent() == (bool)substNom->getParent()); - - if (nom->getParent()) - return visit(nom->getParent()->getCanonicalType(), - substNom->getParent()->getCanonicalType()); - return true; + if (nom->getDecl()->isInvalid()) + return CanType(); + + // Same decl should always either have or not have a parent. + assert((bool)nom->getParent() == (bool)substNom->getParent()); + + if (nom->getParent()) { + auto substParent = visit(nom->getParent()->getCanonicalType(), + substNom->getParent()->getCanonicalType()); + if (substParent == substNom.getParent()) + return subst; + return NominalType::get(nom->getDecl(), substParent, + nom->getASTContext()) + ->getCanonicalType(); } - return false; + return subst; } - - bool visitAnyMetatypeType(AnyMetatypeType *meta, CanType subst) { - if (auto substMeta = dyn_cast(subst)) { - if (substMeta->getKind() != meta->getKind()) - return false; - return visit(meta->getInstanceType()->getCanonicalType(), - substMeta->getInstanceType()->getCanonicalType()); - } - return false; + return CanType(); + } + + CanType visitAnyMetatypeType(AnyMetatypeType *meta, CanType subst) { + if (auto substMeta = dyn_cast(subst)) { + if (substMeta->getKind() != meta->getKind()) + return CanType(); + + auto substInstance = visit(meta->getInstanceType()->getCanonicalType(), + substMeta->getInstanceType()->getCanonicalType()); + if (!substInstance) + return CanType(); + + if (substInstance == substMeta.getInstanceType()) + return subst; + + return isa(substMeta) + ? CanType(CanExistentialMetatypeType::get(substInstance)) + : CanType(CanMetatypeType::get(substInstance)); } - - bool visitTupleType(TupleType *tuple, CanType subst) { - if (auto substTuple = dyn_cast(subst)) { - // Tuple elements must match. - if (tuple->getNumElements() != substTuple->getNumElements()) - return false; - // TODO: Label reordering? - for (unsigned i : indices(tuple->getElements())) { - auto elt = tuple->getElements()[i], - substElt = substTuple->getElements()[i]; - if (elt.getName() != substElt.getName()) - return false; - if (!visit(elt.getType(), substElt.getType()->getCanonicalType())) - return false; - } - return true; + return CanType(); + } + + CanType visitTupleType(TupleType *tuple, CanType subst) { + if (auto substTuple = dyn_cast(subst)) { + // Tuple elements must match. + if (tuple->getNumElements() != substTuple->getNumElements()) + return CanType(); + // TODO: Label reordering? + SmallVector newElements; + bool didChange = false; + for (unsigned i : indices(tuple->getElements())) { + auto elt = tuple->getElements()[i], + substElt = substTuple->getElements()[i]; + if (elt.getName() != substElt.getName()) + return CanType(); + auto newElt = visit(elt.getType(), + substElt.getType()->getCanonicalType()); + if (!newElt) + return CanType(); + newElements.push_back(substElt.getWithType(newElt)); + didChange = didChange | (newElt != CanType(substElt.getType())); } - return false; - } - - bool visitDependentMemberType(DependentMemberType *dt, CanType subst) { - return true; + if (!didChange) + return subst; + return TupleType::get(newElements, subst->getASTContext()) + ->getCanonicalType(); } - bool visitGenericTypeParamType(GenericTypeParamType *dt, CanType subst) { - return true; - } - - bool visitFunctionType(FunctionType *func, CanType subst) { - if (auto substFunc = dyn_cast(subst)) { - if (func->getExtInfo() != substFunc->getExtInfo()) - return false; + return CanType(); + } + + CanType visitDependentMemberType(DependentMemberType *dt, CanType subst) { + return subst; + } + CanType visitGenericTypeParamType(GenericTypeParamType *dt, CanType subst) { + return subst; + } + + CanType visitFunctionType(FunctionType *func, CanType subst) { + if (auto substFunc = dyn_cast(subst)) { + if (func->getExtInfo() != substFunc->getExtInfo()) + return CanType(); + + if (func->getParams().size() != substFunc->getParams().size()) + return CanType(); + + SmallVector newParams; + bool didChange = false; + for (unsigned i : indices(func->getParams())) { + auto param = func->getParams()[i]; + auto substParam = substFunc.getParams()[i]; + if (param.getParameterFlags() != substParam.getParameterFlags()) + return CanType(); - if (func->getParams().size() != substFunc->getParams().size()) - return false; - - for (unsigned i : indices(func->getParams())) { - if (!visit(func->getParams()[i].getOldType(), - substFunc.getParams()[i].getOldType())) - return false; - } + auto newParamTy = + visit(param.getPlainType(), substParam.getPlainType()); + if (!newParamTy) + return CanType(); - return visit(func->getResult()->getCanonicalType(), - substFunc->getResult()->getCanonicalType()); + newParams.push_back(substParam.withType(newParamTy)); + didChange = didChange | (newParamTy != substParam.getPlainType()); } - return false; + + auto newReturn = visit(func->getResult()->getCanonicalType(), + substFunc->getResult()->getCanonicalType()); + if (!newReturn) + return CanType(); + if (!didChange && newReturn == substFunc.getResult()) + return subst; + return FunctionType::get(newParams, newReturn) + ->getCanonicalType(); } - - bool visitSILFunctionType(SILFunctionType *func, - CanType subst) { - if (auto substFunc = dyn_cast(subst)) { - if (func->getExtInfo() != substFunc->getExtInfo()) - return false; + return CanType(); + } + + CanType visitSILFunctionType(SILFunctionType *func, + CanType subst) { + if (auto substFunc = dyn_cast(subst)) { + if (func->getExtInfo() != substFunc->getExtInfo()) + return CanType(); + + // Compare substituted function types. + if (func->getSubstGenericSignature() + || substFunc->getSubstGenericSignature()) { + if (func->getSubstGenericSignature() + != substFunc->getSubstGenericSignature()) + return CanType(); - // TODO: Generic signatures - if (func->getGenericSignature() || substFunc->getGenericSignature()) - return false; + auto sig = func->getSubstGenericSignature(); - if (func->getParameters().size() != substFunc->getParameters().size()) - return false; - if (func->getResults().size() != substFunc->getResults().size()) - return false; + auto origSubs = func->getSubstitutions(); + auto substSubs = substFunc->getSubstitutions(); - for (unsigned i : indices(func->getParameters())) { - if (func->getParameters()[i].getConvention() - != substFunc->getParameters()[i].getConvention()) - return false; - if (!visit(func->getParameters()[i].getType(), - substFunc->getParameters()[i].getType())) - return false; - } - - for (unsigned i : indices(func->getResults())) { - if (func->getResults()[i].getConvention() - != substFunc->getResults()[i].getConvention()) - return false; - - if (!visit(func->getResults()[i].getType(), - substFunc->getResults()[i].getType())) - return false; + if (!origSubs || !substSubs) + return CanType(); + + for (unsigned i : indices(origSubs.getReplacementTypes())) { + auto origType = + origSubs.getReplacementTypes()[i]->getCanonicalType(sig); + auto substType = + substSubs.getReplacementTypes()[i]->getCanonicalType(sig); + + auto newType = visit(origType, substType); + + if (!newType) + return CanType(); + + // We can test SILFunctionTypes for bindability, but we can't + // transform them. + assert(newType == substType + && "cannot transform SILFunctionTypes"); } - return true; + return subst; } - return false; + if (func->getParameters().size() != substFunc->getParameters().size()) + return CanType(); + if (func->getResults().size() != substFunc->getResults().size()) + return CanType(); + + for (unsigned i : indices(func->getParameters())) { + if (func->getParameters()[i].getConvention() + != substFunc->getParameters()[i].getConvention()) + return CanType(); + + auto origParam = func->getParameters()[i].getInterfaceType(); + auto substParam = substFunc->getParameters()[i].getInterfaceType(); + auto newParam = visit(origParam, substParam); + if (!newParam) + return CanType(); + + // We can test SILFunctionTypes for bindability, but we can't + // transform them. + assert(newParam == substParam + && "cannot transform SILFunctionTypes"); + } + + for (unsigned i : indices(func->getResults())) { + if (func->getResults()[i].getConvention() + != substFunc->getResults()[i].getConvention()) + return CanType(); + + auto origResult = func->getResults()[i].getInterfaceType(); + auto substResult = substFunc->getResults()[i].getInterfaceType(); + auto newResult = visit(origResult, substResult); + if (!newResult) + return CanType(); + + // We can test SILFunctionTypes for bindability, but we can't + // transform them. + assert(newResult == substResult + && "cannot transform SILFunctionTypes"); + } + + return subst; } - bool visitBoundGenericType(BoundGenericType *bgt, CanType subst) { - if (auto substBGT = dyn_cast(subst)) { - if (bgt->getDecl() != substBGT->getDecl()) - return false; - - auto *decl = bgt->getDecl(); - if (decl->isInvalid()) - return false; - - auto *moduleDecl = decl->getParentModule(); - auto origSubMap = bgt->getContextSubstitutionMap( - moduleDecl, decl, decl->getGenericEnvironment()); - auto substSubMap = substBGT->getContextSubstitutionMap( - moduleDecl, decl, decl->getGenericEnvironment()); - - auto genericSig = decl->getGenericSignature(); - for (auto gp : genericSig->getGenericParams()) { - auto orig = Type(gp).subst(origSubMap)->getCanonicalType(); - auto subst = Type(gp).subst(substSubMap)->getCanonicalType(); - if (!visit(orig, subst)) - return false; - } + return CanType(); + } + + CanType visitBoundGenericType(BoundGenericType *bgt, CanType subst) { + if (auto substBGT = dyn_cast(subst)) { + if (bgt->getDecl() != substBGT->getDecl()) + return CanType(); + + auto *decl = bgt->getDecl(); + if (decl->isInvalid()) + return CanType(); + + auto *moduleDecl = decl->getParentModule(); + auto origSubMap = bgt->getContextSubstitutionMap( + moduleDecl, decl, decl->getGenericEnvironment()); + auto substSubMap = substBGT->getContextSubstitutionMap( + moduleDecl, decl, decl->getGenericEnvironment()); + + auto genericSig = decl->getGenericSignature(); + + SmallVector newParams; + bool didChange = false; + for (auto gp : genericSig->getGenericParams()) { + auto orig = Type(gp).subst(origSubMap)->getCanonicalType(); + auto subst = Type(gp).subst(substSubMap)->getCanonicalType(); + + auto newParam = visit(orig, subst); + if (!newParam) + return CanType(); + + newParams.push_back(newParam); + didChange = didChange | (newParam != subst); + } - for (const auto &req : genericSig->getRequirements()) { - if (req.getKind() != RequirementKind::Conformance) continue; + for (const auto &req : genericSig->getRequirements()) { + if (req.getKind() != RequirementKind::Conformance) continue; - auto canTy = req.getFirstType()->getCanonicalType(); - auto *proto = req.getSecondType()->castTo()->getDecl(); - auto origConf = *origSubMap.lookupConformance(canTy, proto); - auto substConf = *substSubMap.lookupConformance(canTy, proto); + auto canTy = req.getFirstType()->getCanonicalType(); + auto *proto = req.getSecondType()->castTo()->getDecl(); + auto origConf = origSubMap.lookupConformance(canTy, proto); + auto substConf = substSubMap.lookupConformance(canTy, proto); - if (origConf.isConcrete()) { - if (!substConf.isConcrete()) - return false; - if (origConf.getConcrete()->getRootConformance() - != substConf.getConcrete()->getRootConformance()) - return false; - } + if (origConf.isConcrete()) { + if (!substConf.isConcrete()) + return CanType(); + if (origConf.getConcrete()->getRootConformance() + != substConf.getConcrete()->getRootConformance()) + return CanType(); } + } - // Same decl should always either have or not have a parent. - assert((bool)bgt->getParent() == (bool)substBGT->getParent()); - if (bgt->getParent()) - return visit(bgt->getParent()->getCanonicalType(), - substBGT->getParent()->getCanonicalType()); - return true; + // Same decl should always either have or not have a parent. + assert((bool)bgt->getParent() == (bool)substBGT->getParent()); + CanType newParent; + if (bgt->getParent()) { + newParent = visit(bgt->getParent()->getCanonicalType(), + substBGT->getParent()->getCanonicalType()); } - return false; + + if (!didChange && newParent == substBGT.getParent()) + return subst; + + return BoundGenericType::get(substBGT->getDecl(), + newParent, newParams) + ->getCanonicalType(); } - }; - - return IsBindableVisitor().visit(getCanonicalType(), - b->getCanonicalType()); + return CanType(); + } +}; +} + +CanType TypeBase::substituteBindingsTo(Type ty, + llvm::function_ref substFn) { + return IsBindableVisitor(substFn) + .visit(getCanonicalType(), ty->getCanonicalType()); +} + +bool TypeBase::isBindableTo(Type ty) { + return !substituteBindingsTo(ty, + [&](ArchetypeType *archetype, CanType binding) -> CanType { + return binding; + }) + .isNull(); } bool TypeBase::isBindableToSuperclassOf(Type ty) { @@ -2393,8 +2559,8 @@ static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode, if (matchMode.contains(TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes)) if (auto opaque1 = t1->getAs()) if (auto opaque2 = t2->getAs()) - return opaque1->getBoundSignature()->getCanonicalSignature() == - opaque2->getBoundSignature()->getCanonicalSignature() && + return opaque1->getBoundSignature().getCanonicalSignature() == + opaque2->getBoundSignature().getCanonicalSignature() && opaque1->getInterfaceType()->getCanonicalType()->matches( opaque2->getInterfaceType()->getCanonicalType(), matchMode); @@ -2570,31 +2736,33 @@ getArchetypeAndRootOpaqueArchetype(Type maybeOpaqueType) { return std::make_pair(archetype, opaqueRoot); } -bool ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( +OpaqueSubstitutionKind +ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( OpaqueTypeDecl *opaque) const { - return shouldPerformSubstitution(opaque, contextModule, contextExpansion); + return shouldPerformSubstitution(opaque, inContext->getParentModule(), + contextExpansion); } - -bool ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( +OpaqueSubstitutionKind +ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( OpaqueTypeDecl *opaque, ModuleDecl *contextModule, ResilienceExpansion contextExpansion) { auto namingDecl = opaque->getNamingDecl(); // Don't allow replacement if the naming decl is dynamically replaceable. if (namingDecl && namingDecl->isDynamic()) - return false; + return OpaqueSubstitutionKind::DontSubstitute; // Allow replacement of opaque result types of inlineable function regardless // of resilience and in which context. if (auto *afd = dyn_cast(namingDecl)) { if (afd->getResilienceExpansion() == ResilienceExpansion::Minimal) { - return true; + return OpaqueSubstitutionKind::AlwaysSubstitute; } } else if (auto *asd = dyn_cast(namingDecl)) { auto *getter = asd->getOpaqueAccessor(AccessorKind::Get); if (getter && getter->getResilienceExpansion() == ResilienceExpansion::Minimal) { - return true; + return OpaqueSubstitutionKind::AlwaysSubstitute; } } @@ -2604,20 +2772,62 @@ bool ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( auto module = namingDecl->getModuleContext(); if (contextExpansion == ResilienceExpansion::Maximal && module == contextModule) - return true; + return OpaqueSubstitutionKind::SubstituteSameModuleMaximalResilience; // Allow general replacement from non resilient modules. Otherwise, disallow. - return !module->isResilient(); + if (module->isResilient()) + return OpaqueSubstitutionKind::DontSubstitute; + + return OpaqueSubstitutionKind::SubstituteNonResilientModule; } static Type -substOpaqueTypesWithUnderlyingTypes(Type ty, ModuleDecl *contextModule, - ResilienceExpansion contextExpansion) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(contextModule, - contextExpansion); +substOpaqueTypesWithUnderlyingTypes(Type ty, const DeclContext *inContext, + ResilienceExpansion contextExpansion, + bool isWholeModuleContext) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, + isWholeModuleContext); return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); } +/// Checks that \p dc has access to \p ty for the purposes of an opaque +/// substitution described by \p kind. +/// +/// This is purely an implementation detail check about whether type metadata +/// will be accessible. It's not intended to enforce any rules about what +/// opaque substitutions are or are not allowed. +static bool canSubstituteTypeInto(Type ty, const DeclContext *dc, + OpaqueSubstitutionKind kind, + bool isContextWholeModule) { + auto nominal = ty->getAnyNominal(); + if (!nominal) + return true; + + switch (kind) { + case OpaqueSubstitutionKind::DontSubstitute: + return false; + + case OpaqueSubstitutionKind::AlwaysSubstitute: + return true; + + case OpaqueSubstitutionKind::SubstituteSameModuleMaximalResilience: + // In whole module compilation private types are okay. + if (isContextWholeModule) + return true; + + // In the same file any visibility is okay. + if (!dc->isModuleContext() && + nominal->getDeclContext()->getParentSourceFile() == + dc->getParentSourceFile()) + return true; + return nominal->getEffectiveAccess() > AccessLevel::FilePrivate; + + case OpaqueSubstitutionKind::SubstituteNonResilientModule: + // Can't access types that are not public from a different module. + return nominal->getEffectiveAccess() > AccessLevel::Internal; + } +} + Type ReplaceOpaqueTypesWithUnderlyingTypes:: operator()(SubstitutableType *maybeOpaqueType) const { auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType); @@ -2627,7 +2837,8 @@ operator()(SubstitutableType *maybeOpaqueType) const { auto archetype = archetypeAndRoot->first; auto opaqueRoot = archetypeAndRoot->second; - if (!shouldPerformSubstitution(opaqueRoot->getDecl())) { + auto substitutionKind = shouldPerformSubstitution(opaqueRoot->getDecl()); + if (substitutionKind == OpaqueSubstitutionKind::DontSubstitute) { return maybeOpaqueType; } @@ -2645,25 +2856,47 @@ operator()(SubstitutableType *maybeOpaqueType) const { // for its type arguments. auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions()); + // Check that we are allowed to substitute the underlying type into the + // context. + auto inContext = this->inContext; + auto isContextWholeModule = this->isContextWholeModule; + if (substTy.findIf( + [inContext, substitutionKind, isContextWholeModule](Type t) -> bool { + if (!canSubstituteTypeInto(t, inContext, substitutionKind, + isContextWholeModule)) + return true; + return false; + })) + return maybeOpaqueType; + // If the type still contains opaque types, recur. if (substTy->hasOpaqueArchetype()) { - return substOpaqueTypesWithUnderlyingTypes(substTy, contextModule, - contextExpansion); + return ::substOpaqueTypesWithUnderlyingTypes( + substTy, inContext, contextExpansion, isContextWholeModule); } + return substTy; } -static ProtocolConformanceRef -substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType, - ModuleDecl *contextModule, - ResilienceExpansion contextExpansion) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(contextModule, - contextExpansion); +static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef ref, Type origType, const DeclContext *inContext, + ResilienceExpansion contextExpansion, bool isWholeModuleContext) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion, + isWholeModuleContext); + return ref.subst(origType, replacer, replacer, + SubstFlags::SubstituteOpaqueArchetypes); +} + +ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef ref, Type origType, TypeExpansionContext context) { + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); return ref.subst(origType, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); } -Optional ReplaceOpaqueTypesWithUnderlyingTypes:: +ProtocolConformanceRef ReplaceOpaqueTypesWithUnderlyingTypes:: operator()(CanType maybeOpaqueType, Type replacementType, ProtocolDecl *protocol) const { auto abstractRef = ProtocolConformanceRef(protocol); @@ -2678,7 +2911,8 @@ operator()(CanType maybeOpaqueType, Type replacementType, auto archetype = archetypeAndRoot->first; auto opaqueRoot = archetypeAndRoot->second; - if (!shouldPerformSubstitution(opaqueRoot->getDecl())) { + auto substitutionKind = shouldPerformSubstitution(opaqueRoot->getDecl()); + if (substitutionKind == OpaqueSubstitutionKind::DontSubstitute) { return abstractRef; } @@ -2698,13 +2932,27 @@ operator()(CanType maybeOpaqueType, Type replacementType, // Then apply the substitutions from the root opaque archetype, to specialize // for its type arguments. auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions()); + + // Check that we are allowed to substitute the underlying type into the + // context. + auto inContext = this->inContext; + auto isContextWholeModule = this->isContextWholeModule; + if (substTy.findIf( + [inContext, substitutionKind, isContextWholeModule](Type t) -> bool { + if (!canSubstituteTypeInto(t, inContext, substitutionKind, + isContextWholeModule)) + return true; + return false; + })) + return abstractRef; + auto substRef = partialSubstRef.subst(partialSubstTy, opaqueRoot->getSubstitutions()); // If the type still contains opaque types, recur. if (substTy->hasOpaqueArchetype()) { - return substOpaqueTypesWithUnderlyingTypes(substRef, substTy, contextModule, - contextExpansion); + return ::substOpaqueTypesWithUnderlyingTypes( + substRef, substTy, inContext, contextExpansion, isContextWholeModule); } return substRef; } @@ -2976,6 +3224,48 @@ Type ProtocolCompositionType::get(const ASTContext &C, return build(C, CanTypes, HasExplicitAnyObject); } +void AnyFunctionType::ExtInfo::Uncommon::printClangFunctionType( + ClangModuleLoader *cml, llvm::raw_ostream &os) { + cml->printClangType(ClangFunctionType, os); +} + +void +AnyFunctionType::ExtInfo::assertIsFunctionType(const clang::Type *type) { +#ifndef NDEBUG + if (!(type->isFunctionPointerType() || type->isBlockPointerType())) { + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Expected a Clang function type wrapped in a pointer type or " + << "a block pointer type but found:\n"; + type->dump(os); + llvm_unreachable(os.str().data()); + } +#endif + return; +} + +const clang::Type *AnyFunctionType::getClangFunctionType() const { + switch (getKind()) { + case TypeKind::Function: + return cast(this)->getClangFunctionType(); + case TypeKind::GenericFunction: + // Generic functions do not have C types. + return nullptr; + default: + llvm_unreachable("Illegal type kind for AnyFunctionType."); + } +} + +const clang::Type *AnyFunctionType::getCanonicalClangFunctionType() const { + auto *ty = getClangFunctionType(); + return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; +} + +// TODO: [store-sil-clang-function-type] +const clang::FunctionType *SILFunctionType::getClangFunctionType() const { + return nullptr; +} + FunctionType * GenericFunctionType::substGenericArgs(SubstitutionMap subs) { return substGenericArgs( @@ -3067,16 +3357,17 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, // If we know the associated type, look in the witness table. if (assocType) { auto proto = assocType->getProtocol(); - Optional conformance - = lookupConformances(origBase->getCanonicalType(), - substBase, proto); + ProtocolConformanceRef conformance = + lookupConformances(origBase->getCanonicalType(), substBase, proto); - if (!conformance) return failed(); - if (!conformance->isConcrete()) return failed(); + if (conformance.isInvalid()) + return failed(); + if (!conformance.isConcrete()) + return failed(); // Retrieve the type witness. auto witness = - conformance->getConcrete()->getTypeWitness(assocType, options); + conformance.getConcrete()->getTypeWitness(assocType, options); if (!witness || witness->hasError()) return failed(); @@ -3099,10 +3390,9 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, return failed(); } -Optional -LookUpConformanceInModule::operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { +ProtocolConformanceRef LookUpConformanceInModule:: +operator()(CanType dependentType, Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { if (conformingReplacementType->isTypeParameter()) return ProtocolConformanceRef(conformedProtocol); @@ -3110,17 +3400,15 @@ LookUpConformanceInModule::operator()(CanType dependentType, conformedProtocol); } -Optional -LookUpConformanceInSubstitutionMap::operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { +ProtocolConformanceRef LookUpConformanceInSubstitutionMap:: +operator()(CanType dependentType, Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { return Subs.lookupConformance(dependentType, conformedProtocol); } -Optional -MakeAbstractConformanceForGenericType::operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { +ProtocolConformanceRef MakeAbstractConformanceForGenericType:: +operator()(CanType dependentType, Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { assert((conformingReplacementType->is() || conformingReplacementType->is() || conformingReplacementType->is()) @@ -3128,10 +3416,9 @@ MakeAbstractConformanceForGenericType::operator()(CanType dependentType, return ProtocolConformanceRef(conformedProtocol); } -Optional -LookUpConformanceInSignature::operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { +ProtocolConformanceRef LookUpConformanceInSignature:: +operator()(CanType dependentType, Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { // FIXME: Should pass dependentType instead, once // GenericSignature::lookupConformance() does the right thing return Sig->lookupConformance(conformingReplacementType->getCanonicalType(), @@ -3228,7 +3515,7 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, requirements.push_back(*substReqt); } - GenericSignature genericSig = GenericSignature(); + GenericSignature genericSig; if (anySemanticChanges) { // If there were semantic changes, we need to build a new generic // signature. @@ -3272,8 +3559,8 @@ static Type substType(Type derivedType, "should not be doing AST type-substitution on a lowered SIL type;" "use SILType::subst"); - // Special-case handle SILBoxTypes; we want to structurally substitute the - // substitutions. + // Special-case handle SILBoxTypes and substituted SILFunctionTypes; + // we want to structurally substitute the substitutions. if (auto boxTy = dyn_cast(type)) { auto subMap = boxTy->getSubstitutions(); auto newSubMap = subMap.subst(substitutions, lookupConformances); @@ -3282,6 +3569,14 @@ static Type substType(Type derivedType, boxTy->getLayout(), newSubMap); } + + if (auto silFnTy = dyn_cast(type)) { + if (auto subs = silFnTy->getSubstitutions()) { + auto newSubs = subs.subst(substitutions, lookupConformances); + return silFnTy->withSubstitutions(newSubs); + } + } + // Special-case TypeAliasType; we need to substitute conformances. if (auto aliasTy = dyn_cast(type)) { @@ -3326,6 +3621,11 @@ static Type substType(Type derivedType, // If we have a substitution for this type, use it. if (auto known = substitutions(substOrig)) { + if (options.contains(SubstFlags::SubstituteOpaqueArchetypes) && + isa(substOrig) && + known->getCanonicalType() == substOrig->getCanonicalType()) + return None; // Recursively process the substitutions of the opaque type + // archetype. return known; } @@ -3487,8 +3787,6 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, if (auto *ownerClass = dyn_cast(ownerNominal)) baseTy = baseTy->getSuperclassForDecl(ownerClass); - assert(ownerNominal == baseTy->getAnyNominal()); - // Gather all of the substitutions for all levels of generic arguments. auto genericSig = dc->getGenericSignatureOfContext(); if (!genericSig) @@ -3498,6 +3796,9 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, unsigned n = params.size(); while (baseTy && n > 0) { + if (baseTy->is()) + break; + // For a bound generic type, gather the generic parameter -> generic // argument substitutions. if (auto boundGeneric = baseTy->getAs()) { @@ -3668,13 +3969,13 @@ Identifier DependentMemberType::getName() const { static bool transformSILResult( SILResultInfo &result, bool &changed, llvm::function_ref(TypeBase *)> fn) { - Type transType = result.getType().transformRec(fn); + Type transType = result.getInterfaceType().transformRec(fn); if (!transType) return true; CanType canTransType = transType->getCanonicalType(); - if (canTransType != result.getType()) { + if (canTransType != result.getInterfaceType()) { changed = true; - result = result.getWithType(canTransType); + result = result.getWithInterfaceType(canTransType); } return false; } @@ -3682,13 +3983,13 @@ static bool transformSILResult( static bool transformSILYield( SILYieldInfo &yield, bool &changed, llvm::function_ref(TypeBase *)> fn) { - Type transType = yield.getType().transformRec(fn); + Type transType = yield.getInterfaceType().transformRec(fn); if (!transType) return true; CanType canTransType = transType->getCanonicalType(); - if (canTransType != yield.getType()) { + if (canTransType != yield.getInterfaceType()) { changed = true; - yield = yield.getWithType(canTransType); + yield = yield.getWithInterfaceType(canTransType); } return false; } @@ -3696,13 +3997,13 @@ static bool transformSILYield( static bool transformSILParameter( SILParameterInfo ¶m, bool &changed, llvm::function_ref(TypeBase *)> fn) { - Type transType = param.getType().transformRec(fn); + Type transType = param.getInterfaceType().transformRec(fn); if (!transType) return true; CanType canTransType = transType->getCanonicalType(); - if (canTransType != param.getType()) { + if (canTransType != param.getInterfaceType()) { changed = true; - param = param.getWithType(canTransType); + param = param.getWithInterfaceType(canTransType); } return false; } @@ -3796,6 +4097,19 @@ case TypeKind::Id: case TypeKind::SILFunction: { auto fnTy = cast(base); bool changed = false; + + if (auto subs = fnTy->getSubstitutions()) { +#ifndef NDEBUG + // This interface isn't suitable for updating the substitution map in a + // substituted SILFunctionType. + // TODO(SILFunctionType): Is it suitable for any SILFunctionType?? + for (Type type : fnTy->getSubstitutions().getReplacementTypes()) { + assert(type->isEqual(type.transformRec(fn)) + && "Substituted SILFunctionType can't be transformed"); + } +#endif + return fnTy; + } SmallVector transInterfaceParams; for (SILParameterInfo param : fnTy->getParameters()) { @@ -3824,16 +4138,19 @@ case TypeKind::Id: if (!changed) return *this; - return SILFunctionType::get(fnTy->getGenericSignature(), - fnTy->getExtInfo(), - fnTy->getCoroutineKind(), - fnTy->getCalleeConvention(), - transInterfaceParams, - transInterfaceYields, - transInterfaceResults, - transErrorResult, - Ptr->getASTContext(), - fnTy->getWitnessMethodConformanceOrNone()); + return SILFunctionType::get( + fnTy->getSubstGenericSignature(), + fnTy->getExtInfo(), + fnTy->getCoroutineKind(), + fnTy->getCalleeConvention(), + transInterfaceParams, + transInterfaceYields, + transInterfaceResults, + transErrorResult, + SubstitutionMap(), + /*genericSigIsImplied*/ false, + Ptr->getASTContext(), + fnTy->getWitnessMethodConformanceOrInvalid()); } #define REF_STORAGE(Name, ...) \ @@ -4352,9 +4669,12 @@ ReferenceCounting TypeBase::getReferenceCounting() { ASTContext &ctx = type->getASTContext(); // In the absence of Objective-C interoperability, everything uses native - // reference counting. - if (!ctx.LangOpts.EnableObjCInterop) - return ReferenceCounting::Native; + // reference counting or is the builtin BridgeObject. + if (!ctx.LangOpts.EnableObjCInterop) { + return type->getKind() == TypeKind::BuiltinBridgeObject + ? ReferenceCounting::Bridge + : ReferenceCounting::Native; + } switch (type->getKind()) { #define SUGARED_TYPE(id, parent) case TypeKind::id: @@ -4485,3 +4805,242 @@ Type TypeBase::openAnyExistentialType(OpenedArchetypeType *&opened) { opened = OpenedArchetypeType::get(this); return opened; } + +bool TypeBase::hasOpaqueArchetypePropertiesOrCases() { + if (auto *structDecl = getStructOrBoundGenericStruct()) { + for (auto *field : structDecl->getStoredProperties()) { + auto fieldTy = field->getInterfaceType()->getCanonicalType(); + if (fieldTy->hasOpaqueArchetype() || + fieldTy->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + + if (auto *enumDecl = getEnumOrBoundGenericEnum()) { + for (auto *elt : enumDecl->getAllElements()) { + auto eltType = elt->getInterfaceType(); + if (eltType->hasOpaqueArchetype() || + eltType->getCanonicalType()->hasOpaqueArchetypePropertiesOrCases()) + return true; + } + } + return false; +} + +CanType swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, + TypeExpansionContext context, + bool allowLoweredTypes) { + if (!context.shouldLookThroughOpaqueTypeArchetypes() || + !ty->hasOpaqueArchetype()) + return ty; + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + SubstOptions flags = SubstFlags::SubstituteOpaqueArchetypes; + if (allowLoweredTypes) + flags = + SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes; + return ty.subst(replacer, replacer, flags)->getCanonicalType(); +} + +Optional +TypeBase::getAutoDiffTangentSpace(LookupConformanceFn lookupConformance) { + assert(lookupConformance); + auto &ctx = getASTContext(); + + Type cacheKey = this; + auto lookup = ctx.AutoDiffTangentSpaces.find(cacheKey); + if (lookup != ctx.AutoDiffTangentSpaces.end()) + return lookup->getSecond(); + auto cache = [&](Optional tangentSpace) { + ctx.AutoDiffTangentSpaces.insert({cacheKey, tangentSpace}); + return tangentSpace; + }; + + // For tuple types: the tangent space is a tuple of the elements' tangent + // space types, for the elements that have a tangent space. + if (auto *tupleTy = getAs()) { + SmallVector newElts; + for (auto elt : tupleTy->getElements()) { + auto eltSpace = elt.getType()->getAutoDiffTangentSpace(lookupConformance); + if (!eltSpace) + continue; + newElts.push_back(elt.getWithType(eltSpace->getType())); + } + if (newElts.empty()) + return cache( + TangentSpace::getTuple(ctx.TheEmptyTupleType->castTo())); + if (newElts.size() == 1) + return cache(TangentSpace::getTangentVector(newElts.front().getType())); + auto *tupleType = TupleType::get(newElts, ctx)->castTo(); + return cache(TangentSpace::getTuple(tupleType)); + } + + // For `Differentiable`-conforming types: the tangent space is the + // `TangentVector` associated type. + auto *differentiableProtocol = + ctx.getProtocol(KnownProtocolKind::Differentiable); + assert(differentiableProtocol && "`Differentiable` protocol not found"); + auto associatedTypeLookup = + differentiableProtocol->lookupDirect(ctx.Id_TangentVector); + assert(associatedTypeLookup.size() == 1); + auto *dependentType = DependentMemberType::get( + differentiableProtocol->getDeclaredInterfaceType(), + cast(associatedTypeLookup[0])); + + // Try to get the `TangentVector` associated type of `base`. + // Return the associated type if it is valid. + auto assocTy = dependentType->substBaseType(this, lookupConformance); + if (!assocTy->hasError()) + return cache(TangentSpace::getTangentVector(assocTy)); + + // Otherwise, there is no associated tangent space. Return `None`. + return cache(None); +} + +// Creates an `AnyFunctionType` from the given parameters, result type, +// generic signature, and `ExtInfo`. +static AnyFunctionType * +makeFunctionType(ArrayRef parameters, Type resultType, + GenericSignature genericSignature, + AnyFunctionType::ExtInfo extInfo) { + if (genericSignature) + return GenericFunctionType::get(genericSignature, parameters, resultType, + extInfo); + return FunctionType::get(parameters, resultType, extInfo); +} + +AnyFunctionType *AnyFunctionType::getAutoDiffDerivativeFunctionType( + IndexSubset *parameterIndices, unsigned resultIndex, + AutoDiffDerivativeFunctionKind kind, LookupConformanceFn lookupConformance, + GenericSignature derivativeGenSig, bool makeSelfParamFirst) { + assert(!parameterIndices->isEmpty() && + "Expected at least one differentiability parameter"); + auto &ctx = getASTContext(); + + // If `derivativeGenSig` is not defined, use the current function's type + // generic signature. + if (!derivativeGenSig) + derivativeGenSig = getOptGenericSignature(); + + // Get differentiability parameter types. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(parameterIndices, this, diffParamTypes, + /*reverseCurryLevels*/ !makeSelfParamFirst); + + // Unwrap curry levels. At most, two parameter lists are necessary, for + // curried method types with a `(Self)` parameter list. + // TODO(TF-874): Simplify curry level logic. + SmallVector curryLevels; + auto *currentLevel = castTo(); + for (unsigned i : range(2)) { + (void)i; + if (currentLevel == nullptr) + break; + curryLevels.push_back(currentLevel); + currentLevel = currentLevel->getResult()->getAs(); + } + + Type originalResult = curryLevels.back()->getResult(); + + // Build the result linear map function type. + Type linearMapType; + switch (kind) { + case AutoDiffDerivativeFunctionKind::JVP: { + // Differential function type, a result of the JVP: + // `LinearMapType = (T.TangentVector, ...) -> (R.TangentVector)` + SmallVector differentialParams; + for (auto diffParamType : diffParamTypes) + differentialParams.push_back(AnyFunctionType::Param( + diffParamType->getAutoDiffTangentSpace(lookupConformance) + ->getType())); + SmallVector differentialResults; + if (auto *resultTuple = originalResult->getAs()) { + auto resultTupleEltType = resultTuple->getElementType(resultIndex); + differentialResults.push_back( + resultTupleEltType->getAutoDiffTangentSpace(lookupConformance) + ->getType()); + } else { + assert(resultIndex == 0 && "resultIndex out of bounds"); + differentialResults.push_back( + originalResult->getAutoDiffTangentSpace(lookupConformance) + ->getType()); + } + Type differentialResult = differentialResults.size() > 1 + ? TupleType::get(differentialResults, ctx) + : differentialResults[0].getType(); + linearMapType = FunctionType::get(differentialParams, differentialResult); + break; + } + case AutoDiffDerivativeFunctionKind::VJP: { + // Pullback function type, a result of the VJP: + // `LinearMapType = (R.TangentVector) -> (T.TangentVector, ...)` + SmallVector pullbackParams; + if (auto *resultTuple = originalResult->getAs()) { + auto resultTupleEltType = resultTuple->getElementType(resultIndex); + pullbackParams.push_back(AnyFunctionType::Param( + resultTupleEltType->getAutoDiffTangentSpace(lookupConformance) + ->getType())); + } else { + assert(resultIndex == 0 && + "Expected result index 0 for non-tuple result"); + pullbackParams.push_back(AnyFunctionType::Param( + originalResult->getAutoDiffTangentSpace(lookupConformance) + ->getType())); + } + SmallVector pullbackResults; + for (auto diffParamType : diffParamTypes) + pullbackResults.push_back( + diffParamType->getAutoDiffTangentSpace(lookupConformance)->getType()); + Type pullbackResult = pullbackResults.size() > 1 + ? TupleType::get(pullbackResults, ctx) + : pullbackResults[0].getType(); + linearMapType = FunctionType::get(pullbackParams, pullbackResult); + break; + } + } + assert(linearMapType && "Expected linear map type"); + + // Build the full derivative function type: `(T...) -> (R, LinearMapType)`. + SmallVector retElts; + retElts.push_back(originalResult); + retElts.push_back(linearMapType); + auto retTy = TupleType::get(retElts, ctx); + auto *derivativeFunctionType = + makeFunctionType(curryLevels.back()->getParams(), retTy, + curryLevels.size() == 1 ? derivativeGenSig : nullptr, + curryLevels.back()->getExtInfo()); + + // Wrap the derivative function type in additional curry levels. + auto curryLevelsWithoutLast = + ArrayRef(curryLevels).drop_back(1); + for (auto pair : enumerate(llvm::reverse(curryLevelsWithoutLast))) { + unsigned i = pair.index(); + auto *curryLevel = pair.value(); + derivativeFunctionType = makeFunctionType( + curryLevel->getParams(), derivativeFunctionType, + i == curryLevelsWithoutLast.size() - 1 ? derivativeGenSig : nullptr, + curryLevel->getExtInfo()); + } + + return derivativeFunctionType; +} + +CanSILFunctionType +SILFunctionType::withSubstitutions(SubstitutionMap subs) const { + return SILFunctionType::get(getSubstGenericSignature(), + getExtInfo(), getCoroutineKind(), + getCalleeConvention(), + getParameters(), getYields(), getResults(), + getOptionalErrorResult(), + subs, isGenericSignatureImplied(), + const_cast(this)->getASTContext()); +} + +SourceLoc swift::extractNearestSourceLoc(Type ty) { + if (auto nominal = ty->getAnyNominal()) + return extractNearestSourceLoc(nominal); + + return SourceLoc(); +} diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index e7e3d1e3c6cb2..267980c92d5fe 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -13,13 +13,17 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/Initializer.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/Types.h" +#include "swift/Subsystems.h" using namespace swift; @@ -336,12 +340,20 @@ void RequirementSignatureRequest::cacheResult(ArrayRef value) const // Requirement computation. //----------------------------------------------------------------------------// -WhereClauseOwner::WhereClauseOwner(Decl *decl) - : dc(decl->getInnermostDeclContext()), source(decl) { } +WhereClauseOwner::WhereClauseOwner(GenericContext *genCtx): dc(genCtx) { + if (const auto whereClause = genCtx->getTrailingWhereClause()) + source = whereClause; + else + source = genCtx->getGenericParams(); +} + +WhereClauseOwner::WhereClauseOwner(AssociatedTypeDecl *atd) + : dc(atd->getInnermostDeclContext()), + source(atd->getTrailingWhereClause()) {} SourceLoc WhereClauseOwner::getLoc() const { - if (auto decl = source.dyn_cast()) - return decl->getLoc(); + if (auto where = source.dyn_cast()) + return where->getWhereLoc(); if (auto attr = source.dyn_cast()) return attr->getLocation(); @@ -351,8 +363,8 @@ SourceLoc WhereClauseOwner::getLoc() const { void swift::simple_display(llvm::raw_ostream &out, const WhereClauseOwner &owner) { - if (auto decl = owner.source.dyn_cast()) { - simple_display(out, decl); + if (auto where = owner.source.dyn_cast()) { + simple_display(out, owner.dc->getAsDecl()); } else if (owner.source.is()) { out << "@_specialize"; } else { @@ -374,36 +386,16 @@ void RequirementRequest::noteCycleStep(DiagnosticEngine &diags) const { } MutableArrayRef WhereClauseOwner::getRequirements() const { - if (auto genericParams = source.dyn_cast()) { + if (const auto genericParams = source.dyn_cast()) { return genericParams->getRequirements(); - } - - if (auto attr = source.dyn_cast()) { + } else if (const auto attr = source.dyn_cast()) { if (auto whereClause = attr->getTrailingWhereClause()) return whereClause->getRequirements(); - - return { }; - } - - auto decl = source.dyn_cast(); - if (!decl) - return { }; - - if (auto proto = dyn_cast(decl)) { - if (auto whereClause = proto->getTrailingWhereClause()) - return whereClause->getRequirements(); - - return { }; - } - - if (auto assocType = dyn_cast(decl)) { - if (auto whereClause = assocType->getTrailingWhereClause()) + } else if (const auto attr = source.dyn_cast()) { + if (auto whereClause = attr->getWhereClause()) return whereClause->getRequirements(); - } - - if (auto genericContext = decl->getAsGenericContext()) { - if (auto genericParams = genericContext->getGenericParams()) - return genericParams->getRequirements(); + } else if (const auto whereClause = source.get()) { + return whereClause->getRequirements(); } return { }; @@ -962,4 +954,355 @@ Optional ResultTypeRequest::getCachedResult() const { void ResultTypeRequest::cacheResult(Type type) const { getResultTypeLoc().setType(type); -} \ No newline at end of file +} + +//----------------------------------------------------------------------------// +// PatternBindingEntryRequest computation. +//----------------------------------------------------------------------------// + +Optional +PatternBindingEntryRequest::getCachedResult() const { + auto *PBD = std::get<0>(getStorage()); + auto idx = std::get<1>(getStorage()); + if (!PBD->getPatternList()[idx].isFullyValidated()) { + return None; + } + return &PBD->getPatternList()[idx]; +} + +void PatternBindingEntryRequest::cacheResult( + const PatternBindingEntry *value) const { + auto *PBD = std::get<0>(getStorage()); + auto idx = std::get<1>(getStorage()); + PBD->getMutablePatternList()[idx].setFullyValidated(); +} + +//----------------------------------------------------------------------------// +// NamingPatternRequest computation. +//----------------------------------------------------------------------------// + +Optional NamingPatternRequest::getCachedResult() const { + auto *VD = std::get<0>(getStorage()); + if (auto *Pat = VD->NamingPattern) { + return Pat; + } + return None; +} + +void NamingPatternRequest::cacheResult(NamedPattern *value) const { + auto *VD = std::get<0>(getStorage()); + VD->NamingPattern = value; +} + +//----------------------------------------------------------------------------// +// InterfaceTypeRequest computation. +//----------------------------------------------------------------------------// + +Optional InterfaceTypeRequest::getCachedResult() const { + auto *decl = std::get<0>(getStorage()); + if (auto Ty = decl->TypeAndAccess.getPointer()) { + return Ty; + } + return None; +} + +void InterfaceTypeRequest::cacheResult(Type type) const { + auto *decl = std::get<0>(getStorage()); + if (type) { + assert(!type->hasTypeVariable() && "Type variable in interface type"); + assert(!type->is() && "Interface type must be materializable"); + assert(!type->hasArchetype() && "Archetype in interface type"); + } + decl->TypeAndAccess.setPointer(type); +} + +//----------------------------------------------------------------------------// +// LookupPrecedenceGroupRequest computation. +//----------------------------------------------------------------------------// + +SourceLoc LookupPrecedenceGroupRequest::getNearestLoc() const { + auto &desc = std::get<0>(getStorage()); + return desc.getLoc(); +} + +void LookupPrecedenceGroupRequest::diagnoseCycle(DiagnosticEngine &diags) const { + auto &desc = std::get<0>(getStorage()); + if (auto pathDir = desc.pathDirection) { + diags.diagnose(desc.nameLoc, diag::precedence_group_cycle, (bool)*pathDir); + } else { + diags.diagnose(desc.nameLoc, diag::circular_reference); + } +} + +void LookupPrecedenceGroupRequest::noteCycleStep(DiagnosticEngine &diag) const { + auto &desc = std::get<0>(getStorage()); + diag.diagnose(desc.nameLoc, + diag::circular_reference_through_precedence_group, desc.ident); +} + +SourceLoc PrecedenceGroupDescriptor::getLoc() const { + return nameLoc; +} + +void swift::simple_display(llvm::raw_ostream &out, + const PrecedenceGroupDescriptor &desc) { + out << "precedence group " << desc.ident << " at "; + desc.nameLoc.print(out, desc.dc->getASTContext().SourceMgr); +} + +//----------------------------------------------------------------------------// +// InheritsSuperclassInitializersRequest computation. +//----------------------------------------------------------------------------// + +Optional InheritsSuperclassInitializersRequest::getCachedResult() const { + auto *decl = std::get<0>(getStorage()); + return decl->getCachedInheritsSuperclassInitializers(); +} + +void InheritsSuperclassInitializersRequest::cacheResult(bool value) const { + auto *decl = std::get<0>(getStorage()); + decl->setInheritsSuperclassInitializers(value); +} + +//----------------------------------------------------------------------------// +// ResolveImplicitMemberRequest computation. +//----------------------------------------------------------------------------// + +void swift::simple_display(llvm::raw_ostream &out, + ImplicitMemberAction action) { + switch (action) { + case ImplicitMemberAction::ResolveImplicitInit: + out << "resolve implicit initializer"; + break; + case ImplicitMemberAction::ResolveCodingKeys: + out << "resolve CodingKeys"; + break; + case ImplicitMemberAction::ResolveEncodable: + out << "resolve Encodable.encode(to:)"; + break; + case ImplicitMemberAction::ResolveDecodable: + out << "resolve Decodable.init(from:)"; + break; + } +} + +//----------------------------------------------------------------------------// +// TypeWitnessRequest computation. +//----------------------------------------------------------------------------// + +Optional TypeWitnessRequest::getCachedResult() const { + auto *conformance = std::get<0>(getStorage()); + auto *requirement = std::get<1>(getStorage()); + if (conformance->TypeWitnesses.count(requirement) == 0) { + return None; + } + return conformance->TypeWitnesses[requirement]; +} + +void TypeWitnessRequest::cacheResult(TypeWitnessAndDecl typeWitAndDecl) const { + // FIXME: Refactor this to be the thing that warms the cache. +} + +//----------------------------------------------------------------------------// +// WitnessRequest computation. +//----------------------------------------------------------------------------// + +Optional ValueWitnessRequest::getCachedResult() const { + auto *conformance = std::get<0>(getStorage()); + auto *requirement = std::get<1>(getStorage()); + if (conformance->Mapping.count(requirement) == 0) { + return None; + } + return conformance->Mapping[requirement]; +} + +void ValueWitnessRequest::cacheResult(Witness type) const { + // FIXME: Refactor this to be the thing that warms the cache. +} + +//----------------------------------------------------------------------------// +// PreCheckFunctionBuilderRequest computation. +//----------------------------------------------------------------------------// + +void swift::simple_display(llvm::raw_ostream &out, + FunctionBuilderBodyPreCheck value) { + switch (value) { + case FunctionBuilderBodyPreCheck::Okay: + out << "okay"; + break; + case FunctionBuilderBodyPreCheck::HasReturnStmt: + out << "has return statement"; + break; + case FunctionBuilderBodyPreCheck::Error: + out << "error"; + break; + } +} + +//----------------------------------------------------------------------------// +// HasCircularInheritanceRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularInheritanceRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_class_inheritance, decl->getName()); +} + +void HasCircularInheritanceRequest::noteCycleStep( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + +//----------------------------------------------------------------------------// +// HasCircularInheritedProtocolsRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularInheritedProtocolsRequest::diagnoseCycle( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_protocol_def, decl->getName()); +} + +void HasCircularInheritedProtocolsRequest::noteCycleStep( + DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + +//----------------------------------------------------------------------------// +// HasCircularRawValueRequest computation. +//----------------------------------------------------------------------------// + +void HasCircularRawValueRequest::diagnoseCycle(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::circular_enum_inheritance, decl->getName()); +} + +void HasCircularRawValueRequest::noteCycleStep(DiagnosticEngine &diags) const { + auto *decl = std::get<0>(getStorage()); + diags.diagnose(decl, diag::kind_declname_declared_here, + decl->getDescriptiveKind(), decl->getName()); +} + +//----------------------------------------------------------------------------// +// DefaultArgumentInitContextRequest computation. +//----------------------------------------------------------------------------// + +Optional +DefaultArgumentInitContextRequest::getCachedResult() const { + auto *param = std::get<0>(getStorage()); + return param->getCachedDefaultArgumentInitContext(); +} + +void DefaultArgumentInitContextRequest::cacheResult(Initializer *init) const { + auto *param = std::get<0>(getStorage()); + param->setDefaultArgumentInitContext(init); +} + +//----------------------------------------------------------------------------// +// DefaultArgumentExprRequest computation. +//----------------------------------------------------------------------------// + +Optional DefaultArgumentExprRequest::getCachedResult() const { + auto *param = std::get<0>(getStorage()); + auto *defaultInfo = param->DefaultValueAndFlags.getPointer(); + if (!defaultInfo) + return None; + + if (!defaultInfo->InitContextAndIsTypeChecked.getInt()) + return None; + + return defaultInfo->DefaultArg.get(); +} + +void DefaultArgumentExprRequest::cacheResult(Expr *expr) const { + auto *param = std::get<0>(getStorage()); + param->setDefaultExpr(expr, /*isTypeChecked*/ true); +} + +//----------------------------------------------------------------------------// +// CallerSideDefaultArgExprRequest computation. +//----------------------------------------------------------------------------// + +Optional CallerSideDefaultArgExprRequest::getCachedResult() const { + auto *defaultExpr = std::get<0>(getStorage()); + auto storage = defaultExpr->ContextOrCallerSideExpr; + assert(!storage.isNull()); + + if (auto *expr = storage.dyn_cast()) + return expr; + + return None; +} + +void CallerSideDefaultArgExprRequest::cacheResult(Expr *expr) const { + auto *defaultExpr = std::get<0>(getStorage()); + defaultExpr->ContextOrCallerSideExpr = expr; +} + +//----------------------------------------------------------------------------// +// DifferentiableAttributeTypeCheckRequest computation. +//----------------------------------------------------------------------------// + +Optional +DifferentiableAttributeTypeCheckRequest::getCachedResult() const { + auto *attr = std::get<0>(getStorage()); + if (attr->hasBeenTypeChecked()) + return attr->ParameterIndicesAndBit.getPointer(); + return None; +} + +void DifferentiableAttributeTypeCheckRequest::cacheResult( + IndexSubset *parameterIndices) const { + auto *attr = std::get<0>(getStorage()); + attr->ParameterIndicesAndBit.setPointerAndInt(parameterIndices, true); +} + +//----------------------------------------------------------------------------// +// TypeCheckSourceFileRequest computation. +//----------------------------------------------------------------------------// + +Optional TypeCheckSourceFileRequest::getCachedResult() const { + auto *SF = std::get<0>(getStorage()); + if (SF->ASTStage == SourceFile::TypeChecked) + return true; + + return None; +} + +void TypeCheckSourceFileRequest::cacheResult(bool result) const { + auto *SF = std::get<0>(getStorage()); + + // Verify that we've checked types correctly. + SF->ASTStage = SourceFile::TypeChecked; + + { + auto &Ctx = SF->getASTContext(); + FrontendStatsTracer tracer(Ctx.Stats, "AST verification"); + // Verify the SourceFile. + swift::verify(*SF); + + // Verify imported modules. + // + // Skip per-file verification in whole-module mode. Verifying imports + // between files could cause the importer to cache declarations without + // adding them to the ASTContext. This happens when the importer registers a + // declaration without a valid TypeChecker instance, as is the case during + // verification. A subsequent file may require that declaration to be fully + // imported (e.g. to synthesized a function body), but since it has already + // been cached, it will never be added to the ASTContext. The solution is to + // skip verification and avoid caching it. +#ifndef NDEBUG + if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking && + SF->Kind != SourceFileKind::REPL && + SF->Kind != SourceFileKind::SIL && + !Ctx.LangOpts.DebuggerSupport) { + Ctx.verifyAllLoadedModules(); + } +#endif + } +} diff --git a/lib/AST/TypeJoinMeet.cpp b/lib/AST/TypeJoinMeet.cpp index ae0bc61c0251c..a1af594424502 100644 --- a/lib/AST/TypeJoinMeet.cpp +++ b/lib/AST/TypeJoinMeet.cpp @@ -314,7 +314,7 @@ CanType TypeJoin::visitFunctionType(CanType second) { auto secondExtInfo = secondFnTy->getExtInfo(); // FIXME: Properly handle these attributes. - if (firstExtInfo.withNoEscape(false) != secondExtInfo.withNoEscape(false)) + if (firstExtInfo != secondExtInfo) return Unimplemented; if (!AnyFunctionType::equalParams(firstFnTy->getParams(), diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 90a203ab7180a..a1fd18d5af19b 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -79,11 +79,11 @@ void *TypeRepr::operator new(size_t Bytes, const ASTContext &C, return C.Allocate(Bytes, Alignment); } -Identifier ComponentIdentTypeRepr::getIdentifier() const { - if (IdOrDecl.is()) - return IdOrDecl.get(); +DeclNameRef ComponentIdentTypeRepr::getNameRef() const { + if (IdOrDecl.is()) + return IdOrDecl.get(); - return IdOrDecl.get()->getName(); + return IdOrDecl.get()->createNameRef(); } static void printTypeRepr(const TypeRepr *TyR, ASTPrinter &Printer, @@ -138,7 +138,7 @@ TypeRepr *CloneVisitor::visitAttributedTypeRepr(AttributedTypeRepr *T) { } TypeRepr *CloneVisitor::visitSimpleIdentTypeRepr(SimpleIdentTypeRepr *T) { - return new (Ctx) SimpleIdentTypeRepr(T->getIdLoc(), T->getIdentifier()); + return new (Ctx) SimpleIdentTypeRepr(T->getNameLoc(), T->getNameRef()); } TypeRepr *CloneVisitor::visitGenericIdentTypeRepr(GenericIdentTypeRepr *T) { @@ -148,7 +148,7 @@ TypeRepr *CloneVisitor::visitGenericIdentTypeRepr(GenericIdentTypeRepr *T) { for (auto &arg : T->getGenericArgs()) { genericArgs.push_back(visit(arg)); } - return GenericIdentTypeRepr::create(Ctx, T->getIdLoc(), T->getIdentifier(), + return GenericIdentTypeRepr::create(Ctx, T->getNameLoc(), T->getNameRef(), genericArgs, T->getAngleBrackets()); } @@ -298,16 +298,28 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printSimpleAttr("@autoclosure") << " "; if (hasAttr(TAK_escaping)) Printer.printSimpleAttr("@escaping") << " "; + if (hasAttr(TAK_noDerivative)) + Printer.printSimpleAttr("@noDerivative") << " "; + + if (hasAttr(TAK_differentiable)) { + if (Attrs.isLinear()) { + Printer.printSimpleAttr("@differentiable(linear)") << " "; + } else { + Printer.printSimpleAttr("@differentiable") << " "; + } + } if (hasAttr(TAK_thin)) Printer.printSimpleAttr("@thin") << " "; if (hasAttr(TAK_thick)) Printer.printSimpleAttr("@thick") << " "; - if (hasAttr(TAK_convention) && Attrs.convention.hasValue()) { + if (hasAttr(TAK_convention) && Attrs.hasConvention()) { Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); - Printer << "(" << Attrs.convention.getValue() << ")"; + SmallString<32> convention; + Attrs.getConventionArguments(convention); + Printer << "(" << convention << ")"; Printer.printStructurePost(PrintStructureKind::BuiltinAttribute); Printer << " "; } @@ -337,11 +349,11 @@ void ComponentIdentTypeRepr::printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const { if (auto *TD = dyn_cast_or_null(getBoundDecl())) { if (auto MD = dyn_cast(TD)) - Printer.printModuleRef(MD, getIdentifier()); + Printer.printModuleRef(MD, getNameRef().getBaseIdentifier()); else - Printer.printTypeRef(Type(), TD, getIdentifier()); + Printer.printTypeRef(Type(), TD, getNameRef().getBaseIdentifier()); } else { - Printer.printName(getIdentifier()); + Printer.printName(getNameRef().getBaseIdentifier()); } if (auto GenIdT = dyn_cast(this)) @@ -413,7 +425,7 @@ TupleTypeRepr::TupleTypeRepr(ArrayRef Elements, // Set ellipsis location and index. if (Ellipsis.isValid()) { - getTrailingObjects()[0] = {Ellipsis, EllipsisIdx}; + getTrailingObjects()[0] = {EllipsisIdx, Ellipsis}; } } @@ -439,8 +451,8 @@ TupleTypeRepr *TupleTypeRepr::createEmpty(const ASTContext &C, } GenericIdentTypeRepr *GenericIdentTypeRepr::create(const ASTContext &C, - SourceLoc Loc, - Identifier Id, + DeclNameLoc Loc, + DeclNameRef Id, ArrayRef GenericArgs, SourceRange AngleBrackets) { auto size = totalSizeToAlloc(GenericArgs.size()); diff --git a/lib/AST/TypeWalker.cpp b/lib/AST/TypeWalker.cpp index ba70105096670..3491055221da7 100644 --- a/lib/AST/TypeWalker.cpp +++ b/lib/AST/TypeWalker.cpp @@ -117,14 +117,24 @@ class Traversal : public TypeVisitor } bool visitSILFunctionType(SILFunctionType *ty) { + // TODO: Should this be the only kind of walk we allow? + if (auto subs = ty->getSubstitutions()) { + for (auto paramTy : subs.getReplacementTypes()) { + if (doIt(paramTy)) + return true; + } + + return false; + } + for (auto param : ty->getParameters()) - if (doIt(param.getType())) + if (doIt(param.getInterfaceType())) return true; for (auto result : ty->getResults()) - if (doIt(result.getType())) + if (doIt(result.getInterfaceType())) return true; if (ty->hasErrorResult()) - if (doIt(ty->getErrorResult().getType())) + if (doIt(ty->getErrorResult().getInterfaceType())) return true; return false; } diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index c9baacc7d0f86..7f30e930391cb 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -242,8 +242,6 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, } auto declIFaceTy = D->getInterfaceType(); - if (!declIFaceTy) - return std::string(); // Invalid code. if (declIFaceTy.findIf([](Type t) -> bool { @@ -258,9 +256,6 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, llvm::Expected swift::MangleLocalTypeDeclRequest::evaluate(Evaluator &evaluator, const TypeDecl *D) const { - if (!D->getInterfaceType()) - return std::string(); - if (isa(D)) return std::string(); // Ignore. @@ -310,7 +305,7 @@ bool ide::printAccessorUSR(const AbstractStorageDecl *D, AccessorKind AccKind, Mangle::ASTMangler NewMangler; std::string Mangled = NewMangler.mangleAccessorEntityAsUSR(AccKind, - SD, getUSRSpacePrefix()); + SD, getUSRSpacePrefix(), SD->isStatic()); OS << Mangled; @@ -342,8 +337,6 @@ bool ide::printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS) { } bool ide::printDeclUSR(const Decl *D, raw_ostream &OS) { - if (D->isImplicit()) - return true; if (auto *VD = dyn_cast(D)) { if (ide::printValueDeclUSR(VD, OS)) { return true; diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 17ddfd585898a..3c7a152b92d59 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// /// -/// This file implements the construction of an UnqualifiedLookup, which entails -/// performing the lookup. +/// This file implements unqualified lookup, which searches for an identifier +/// from a given context. /// //===----------------------------------------------------------------------===// @@ -29,6 +29,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/ReferencedNameTracker.h" #include "swift/AST/SourceFile.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" @@ -80,18 +81,14 @@ namespace { } // end anonymous namespace namespace { - /// Because UnqualifiedLookup does all of its work in the constructor, - /// a factory class is needed to hold all of the inputs and outputs so - /// that the construction code can be decomposed into bite-sized pieces. - class UnqualifiedLookupFactory { friend class ASTScopeDeclConsumerForUnqualifiedLookup; public: - using Flags = UnqualifiedLookup::Flags; - using Options = UnqualifiedLookup::Options; - using ResultsVector = UnqualifiedLookup::ResultsVector; + using Flags = UnqualifiedLookupFlags; + using Options = UnqualifiedLookupOptions; + using ResultsVector = SmallVector; private: struct ContextAndResolvedIsCascadingUse { @@ -121,7 +118,7 @@ namespace { DeclContext *dynamicContext, DeclContext *staticContext); - void dump() const; + SWIFT_DEBUG_DUMP; private: SelfBounds findSelfBounds(DeclContext *dc); @@ -134,7 +131,7 @@ namespace { public: /// Do the lookups and add matches to results. - void findResults(const DeclName &Name, bool isCascadingUse, + void findResults(const DeclNameRef &Name, bool isCascadingUse, NLOptions baseNLOptions, DeclContext *contextForLookup, SmallVectorImpl &results) const; }; @@ -150,7 +147,7 @@ namespace { public: InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory, - DeclName name, + DeclNameRef name, SmallVectorImpl &results, bool isTypeLookup) : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {} @@ -166,7 +163,7 @@ namespace { }; #endif // Inputs - const DeclName Name; + const DeclNameRef Name; DeclContext *const DC; ModuleDecl &M; const ASTContext &Ctx; @@ -199,16 +196,10 @@ namespace { SourceFile const *recordedSF = nullptr; bool recordedIsCascadingUse = false; unsigned resultsSizeBeforeLocalsPass = ~0; - + public: // clang-format off - UnqualifiedLookupFactory(DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options, - UnqualifiedLookup &lookupToBeCreated); - - UnqualifiedLookupFactory(DeclName Name, + UnqualifiedLookupFactory(DeclNameRef Name, DeclContext *const DC, SourceLoc Loc, Options options, @@ -231,7 +222,7 @@ namespace { bool useASTScopesForLookup() const; /// For testing, assume this lookup is enabled: - bool useASTScopesForLookupIfEnabled() const; + bool wouldUseASTScopesForLookupIfItWereEnabled() const; void lookUpTopLevelNamesInModuleScopeContext(DeclContext *); @@ -261,52 +252,70 @@ namespace { void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse); - void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse); + /// When performing a lookup, we may come across a capture of 'self'. We + /// will need to remember the DeclContext of the innermost captured self so + /// that it can be used as the base DeclContext if we find a lookup result + /// in the enclosing type. \c capturedSelfContext tracks this. + void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse, + DeclContext *capturedSelfContext); void finishLookingInContext( AddGenericParameters addGenericParameters, DeclContext *lookupContextForThisContext, Optional &&resultFinderForTypeContext, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupInModuleScopeContext(DeclContext *, Optional isCascadingUse); void lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PatternBindingInitializer *PBI, Optional isCascadingUse); /// An initializer of a global name, or a function-likelocal name. void lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse); + PatternBindingInitializer *PBI, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD, - bool isCascadingUse); + bool isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); template void lookupNamesIntroducedByNominalTypeOrExtension( NominalTypeDeclOrExtensionDecl *D, Optional isCascadingUse); void lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse); + DefaultArgumentInitializer *I, + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookupNamesIntroducedByMiscContext(DeclContext *dc, - Optional isCascadingUse); + Optional isCascadingUse, + DeclContext *capturedSelfContext); void lookForLocalVariablesIn(AbstractFunctionDecl *AFD, Optional isCascadingUse); @@ -329,7 +338,7 @@ namespace { void setAsideUnavailableResults(size_t firstPossiblyUnavailableResult); void recordDependencyOnTopLevelName(DeclContext *topLevelContext, - DeclName name, bool isCascadingUse); + DeclNameRef name, bool isCascadingUse); void addImportedResults(DeclContext *const dc); @@ -341,7 +350,7 @@ namespace { #pragma mark common helper declarations static NLOptions - computeBaseNLOptions(const UnqualifiedLookup::Options options, + computeBaseNLOptions(const UnqualifiedLookupOptions options, const bool isOriginallyTypeLookup); Optional getInitialIsCascadingUse() const { @@ -368,15 +377,17 @@ namespace { bool isCascadingUse, NLOptions baseNLOptions); public: - void dump() const; - void dumpScopes() const; + SWIFT_DEBUG_DUMP; + SWIFT_DEBUG_DUMPER(dumpResults()); + SWIFT_DEBUG_DUMPER(dumpScopes()); + + void printScopes(raw_ostream &OS) const; void print(raw_ostream &OS) const; - - void dumpResults() const; - - bool verifyEqualTo(const UnqualifiedLookupFactory &&, const char *thisLabel, - const char *otherLabel) const; - + void printResults(raw_ostream &OS) const; + + bool verifyEqualTo(const UnqualifiedLookupFactory &&, StringRef thisLabel, + StringRef otherLabel) const; + /// Legacy lookup is wrong here; we should NOT find this symbol. bool shouldDiffer() const; StringRef getSourceFileName() const; @@ -431,19 +442,7 @@ class ASTScopeDeclConsumerForUnqualifiedLookup // clang-format off UnqualifiedLookupFactory::UnqualifiedLookupFactory( - DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options, - UnqualifiedLookup &lookupToBeCreated) -: UnqualifiedLookupFactory(Name, DC, Loc, options, - lookupToBeCreated.Results, - lookupToBeCreated.IndexOfFirstOuterResult) - -{} - -UnqualifiedLookupFactory::UnqualifiedLookupFactory( - DeclName Name, + DeclNameRef Name, DeclContext *const DC, SourceLoc Loc, Options options, @@ -500,10 +499,11 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { if (Name.isOperator()) lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - lookupNamesIntroducedBy(contextAndIsCascadingUse); + lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); } - if (crosscheckUnqualifiedLookup && useASTScopesForLookupIfEnabled()) { + if (crosscheckUnqualifiedLookup && + wouldUseASTScopesForLookupIfItWereEnabled()) { ResultsVector results; size_t indexOfFirstOuterResult = 0; UnqualifiedLookupFactory altLookup(Name, DC, Loc, options, results, @@ -513,10 +513,15 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() { else if (Name.isOperator()) altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse); else - altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse); - - assert( - verifyEqualTo(std::move(altLookup), "main lookup", "alternate lookup")); + altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); + + const auto *ASTScopeLabel = "ASTScope lookup"; + const auto *contextLabel = "context-bsed lookup"; + const auto *mainLabel = + useASTScopesForLookup() ? ASTScopeLabel : contextLabel; + const auto *alternateLabel = + useASTScopesForLookup() ? contextLabel : ASTScopeLabel; + assert(verifyEqualTo(std::move(altLookup), mainLabel, alternateLabel)); } } @@ -542,10 +547,12 @@ void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext( } bool UnqualifiedLookupFactory::useASTScopesForLookup() const { - return Ctx.LangOpts.EnableASTScopeLookup && useASTScopesForLookupIfEnabled(); + return Ctx.LangOpts.EnableASTScopeLookup && + wouldUseASTScopesForLookupIfItWereEnabled(); } -bool UnqualifiedLookupFactory::useASTScopesForLookupIfEnabled() const { +bool UnqualifiedLookupFactory::wouldUseASTScopesForLookupIfItWereEnabled() + const { if (!Loc.isValid()) return false; const auto *const SF = DC->getParentSourceFile(); @@ -567,28 +574,43 @@ void UnqualifiedLookupFactory::lookupOperatorInDeclContexts( // TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl void UnqualifiedLookupFactory::lookupNamesIntroducedBy( - const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg) { + const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook; const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse; - if (dc->isModuleScopeContext()) + if (dc->isModuleScopeContext()) { + assert(capturedSelfContext == NULL && "By the time we reach module scope," + " there should be no 'self'."); lookupInModuleScopeContext(dc, isCascadingUseSoFar); + } else if (auto *PBI = dyn_cast(dc)) - lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar); + lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar, + capturedSelfContext); else if (auto *AFD = dyn_cast(dc)) - lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar); + lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar, + capturedSelfContext); else if (auto *ACE = dyn_cast(dc)) - lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar); - else if (auto *ED = dyn_cast(dc)) + lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar, + capturedSelfContext); + else if (auto *ED = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar); - else if (auto *ND = dyn_cast(dc)) + } + else if (auto *ND = dyn_cast(dc)) { + assert(capturedSelfContext == NULL && "When we recurse into type context," + " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar); + } else if (auto I = dyn_cast(dc)) - lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar); + lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar, + capturedSelfContext); else - lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar); + lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupInModuleScopeContext( @@ -606,22 +628,29 @@ void UnqualifiedLookupFactory::lookupInModuleScopeContext( } void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // Lazy variable initializer contexts have a 'self' parameter for // instance member lookup. if (auto *selfParam = PBI->getImplicitSelfDecl()) lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam, - isCascadingUse); - else if (PBI->getParent()->isTypeContext()) - lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(PBI, + isCascadingUse, + capturedSelfContext); + else if (PBI->getParent()->isTypeContext()) { + assert(capturedSelfContext == NULL && "If we were in a type's property" + " initializer, there should be no 'self' to have been captured."); + lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( + PBI, isCascadingUse); + } else - lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse); + lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer( PatternBindingInitializer *PBI, ParamDecl *selfParam, - Optional isCascadingUse) { + Optional isCascadingUse, DeclContext *capturedSelfContext) { Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); ifNotDoneYet([&] { DeclContext *const patternContainer = PBI->getParent(); @@ -631,7 +660,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( patternContainer, ResultFinderForTypeContext(this, PBI, patternContainer), resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -649,13 +679,15 @@ void UnqualifiedLookupFactory:: ResultFinderForTypeContext( this, storedPropertyContainer, storedPropertyContainer), resolveIsCascadingUse(storedPropertyContainer, None, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByInitializerOfGlobalOrLocal( - PatternBindingInitializer *PBI, Optional isCascadingUse) { + PatternBindingInitializer *PBI, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // There's not much to find here, we'll keep going up to a parent // context. // clang-format off @@ -664,12 +696,14 @@ void UnqualifiedLookupFactory:: PBI, None, // not looking in the partic type resolveIsCascadingUse(PBI, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( - AbstractFunctionDecl *AFD, Optional isCascadingUseArg) { + AbstractFunctionDecl *AFD, Optional isCascadingUseArg, + DeclContext *capturedSelfContext) { // DOUG: how does this differ from isOutsideBodyOfFunction below? const bool isCascadingUse = @@ -679,13 +713,16 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc))); if (AFD->getDeclContext()->isTypeContext()) - lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse); + lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse, + capturedSelfContext); else - lookupNamesIntroducedByPureFunction(AFD, isCascadingUse); + lookupNamesIntroducedByPureFunction(AFD, isCascadingUse, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet( [&] { @@ -701,9 +738,13 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( // If we're not in the body of the function (for example, we // might be type checking a default argument expression and // performing name lookup from there), the base declaration - // is the nominal type, not 'self'. + // is the nominal type, not 'self'. If we've captured self + // somewhere down the tree, we should use that as the context + // for lookup. DeclContext *const BaseDC = - isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD; + isOutsideBodyOfFunction(AFD) ? fnDeclContext + : capturedSelfContext ? capturedSelfContext + : AFD; // If we are inside of a method, check to see if there are any ivars in // scope, and if so, whether this is a reference to one of them. // FIXME: We should persist this information between lookups. @@ -712,13 +753,15 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( AddGenericParameters::Yes, AFD->getParent(), ResultFinderForTypeContext(this, BaseDC, fnDeclContext), - isCascadingUse); + isCascadingUse, + NULL); // clang-format on }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( - AbstractFunctionDecl *AFD, bool isCascadingUse) { + AbstractFunctionDecl *AFD, bool isCascadingUse, + DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet([&] { // clang-format off @@ -726,15 +769,24 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( AddGenericParameters::Yes, AFD, None, - isCascadingUse); + isCascadingUse, + capturedSelfContext); }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( - AbstractClosureExpr *ACE, Optional isCascadingUse) { - if (auto *CE = dyn_cast(ACE)) + AbstractClosureExpr *ACE, Optional isCascadingUse, + DeclContext *capturedSelfContext) { + if (auto *CE = dyn_cast(ACE)) { lookForLocalVariablesIn(CE); + // If we don't already have a captured self context, and this closure + // captures the self param (not weakly, so that implicit self is available), + // remember that. + if (capturedSelfContext == nullptr) + if (CE->capturesSelfEnablingImplictSelf()) + capturedSelfContext = CE; + } ifNotDoneYet([&] { // clang-format off finishLookingInContext( @@ -742,7 +794,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( ACE, None, resolveIsCascadingUse(ACE, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on }); } @@ -759,21 +812,25 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension( ResultFinderForTypeContext(this, D, D)) : None, resolveIsCascadingUse(D, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByDefaultArgumentInitializer( - DefaultArgumentInitializer *I, Optional isCascadingUse) { + DefaultArgumentInitializer *I, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // In a default argument, skip immediately out of both the // initializer and the function. - finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false); + finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false, + capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( - DeclContext *dc, Optional isCascadingUse) { + DeclContext *dc, Optional isCascadingUse, + DeclContext *capturedSelfContext) { // clang-format off assert(isa(dc) || isa(dc) || @@ -785,7 +842,8 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( dc, None, resolveIsCascadingUse(DC, isCascadingUse, - /*onlyCareAboutFunctionBody=*/false)); + /*onlyCareAboutFunctionBody=*/false), + capturedSelfContext); // clang-format on } @@ -794,7 +852,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( const AddGenericParameters addGenericParameters, DeclContext *const lookupContextForThisContext, Optional &&resultFinderForTypeContext, - const Optional isCascadingUse) { + const Optional isCascadingUse, + DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif @@ -815,7 +874,8 @@ void UnqualifiedLookupFactory::finishLookingInContext( // Recurse into the next context. [&] { lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{ - lookupContextForThisContext->getParentForLookup(), isCascadingUse}); + lookupContextForThisContext->getParentForLookup(), isCascadingUse}, + capturedSelfContext); }); } @@ -917,7 +977,7 @@ void UnqualifiedLookupFactory::addGenericParametersForFunction( } void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( - const DeclName &Name, bool isCascadingUse, NLOptions baseNLOptions, + const DeclNameRef &Name, bool isCascadingUse, NLOptions baseNLOptions, DeclContext *contextForLookup, SmallVectorImpl &results) const { // An optimization: @@ -971,8 +1031,8 @@ void UnqualifiedLookupFactory::setAsideUnavailableResults( void UnqualifiedLookupFactory::recordDependencyOnTopLevelName( - DeclContext *topLevelContext, DeclName name, bool isCascadingUse) { - recordLookupOfTopLevelName(topLevelContext, Name, isCascadingUse); + DeclContext *topLevelContext, DeclNameRef name, bool isCascadingUse) { + recordLookupOfTopLevelName(topLevelContext, Name.getFullName(), isCascadingUse); recordedSF = dyn_cast(topLevelContext); recordedIsCascadingUse = isCascadingUse; } @@ -982,7 +1042,7 @@ void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) { SmallVector CurModuleResults; auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; - lookupInModule(dc, Name, CurModuleResults, NLKind::UnqualifiedLookup, + lookupInModule(dc, Name.getFullName(), CurModuleResults, NLKind::UnqualifiedLookup, resolutionKind, dc); // Always perform name shadowing for type lookup. @@ -1025,7 +1085,7 @@ void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName( return; } ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier()); - if (!desiredModule && Name == Ctx.TheBuiltinModule->getName()) + if (!desiredModule && Name.getFullName() == Ctx.TheBuiltinModule->getName()) desiredModule = Ctx.TheBuiltinModule; if (desiredModule) { // Make sure the desired module is actually visible from the current @@ -1054,7 +1114,7 @@ void UnqualifiedLookupFactory::findResultsAndSaveUnavailables( NLOptions UnqualifiedLookupFactory::computeBaseNLOptions( - const UnqualifiedLookup::Options options, + const UnqualifiedLookupOptions options, const bool isOriginallyTypeLookup) { NLOptions baseNLOptions = NL_UnqualifiedDefault; if (options.contains(Flags::AllowProtocolMembers)) @@ -1146,7 +1206,7 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( for (auto *value: values) { if (factory.isOriginallyTypeLookup && !isa(value)) continue; - if (!value->getFullName().matchesRef(factory.Name)) + if (!value->getFullName().matchesRef(factory.Name.getFullName())) continue; // In order to preserve the behavior of the existing context-based lookup, @@ -1198,29 +1258,15 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( return factory.isFirstResultEnough(); } - -#pragma mark UnqualifiedLookup functions - -// clang-format off -UnqualifiedLookup::UnqualifiedLookup(DeclName Name, - DeclContext *const DC, - SourceLoc Loc, - Options options) - // clang-format on - : IndexOfFirstOuterResult(0) { - - auto *stats = DC->getASTContext().Stats; - if (stats) - stats->getFrontendCounters().NumUnqualifiedLookup++; - - UnqualifiedLookupFactory factory(Name, DC, Loc, options, *this); +llvm::Expected +UnqualifiedLookupRequest::evaluate(Evaluator &evaluator, + UnqualifiedLookupDescriptor desc) const { + SmallVector results; + size_t indexOfFirstOuterResult = 0; + UnqualifiedLookupFactory factory(desc.Name, desc.DC, desc.Loc, desc.Options, + results, indexOfFirstOuterResult); factory.performUnqualifiedLookup(); -} - -TypeDecl *UnqualifiedLookup::getSingleTypeResult() const { - if (Results.size() != 1) - return nullptr; - return dyn_cast(Results.back().getValueDecl()); + return LookupResult(results, indexOfFirstOuterResult); } #pragma mark debugging @@ -1240,16 +1286,17 @@ void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const { llvm::errs() << "\n"; } -void UnqualifiedLookupFactory::dumpScopes() const { - llvm::errs() << "\n\nScopes:\n"; - DC->getParentSourceFile()->getScope().print(llvm::errs()); - llvm::errs() << "\n"; -} - void UnqualifiedLookupFactory::dump() const { print(llvm::errs()); } +void UnqualifiedLookupFactory::dumpScopes() const { printScopes(llvm::errs()); } +void UnqualifiedLookupFactory::dumpResults() const { printResults(llvm::errs()); } -void UnqualifiedLookupFactory::dumpResults() const { - auto &out = llvm::errs(); +void UnqualifiedLookupFactory::printScopes(raw_ostream &out) const { + out << "\n\nScopes:\n"; + DC->getParentSourceFile()->getScope().print(out); + out << "\n"; +} + +void UnqualifiedLookupFactory::printResults(raw_ostream &out) const { for (auto i : indices(Results)) { if (i == resultsSizeBeforeLocalsPass) out << "============== next pass ============\n"; @@ -1285,45 +1332,40 @@ StringRef UnqualifiedLookupFactory::getSourceFileName() const { return DC->getParentSourceFile()->getFilename(); } -static void writeFirstLine(const UnqualifiedLookupFactory &ul, StringRef s) { +static void writeFirstLine(const UnqualifiedLookupFactory &ul, llvm::Twine s) { std::string line = std::string("In file: ") + ul.getSourceFileName().str() + ", " + s.str(); writeLine(line); } static void writeInconsistent(const UnqualifiedLookupFactory &me, - const char *thisLabel, + StringRef thisLabel, const UnqualifiedLookupFactory &other, - const char *otherLabel, StringRef s) { + StringRef otherLabel, llvm::Twine s) { writeFirstLine(me, s); - other.dump(); + other.print(llvm::errs()); llvm::errs() << "\n" << thisLabel << " Results:\n"; - me.dumpResults(); + me.printResults(llvm::errs()); llvm::errs() << "\n" << otherLabel << " Results:\n"; - other.dumpResults(); - me.dumpScopes(); + other.printResults(llvm::errs()); + me.printScopes(llvm::errs()); } #pragma mark comparing results bool UnqualifiedLookupFactory::verifyEqualTo( - const UnqualifiedLookupFactory &&other, const char *thisLabel, - const char *otherLabel) const { + const UnqualifiedLookupFactory &&other, const StringRef thisLabel, + StringRef otherLabel) const { if (shouldDiffer()) { return true; } - auto writeErr = [&](StringRef s) { + auto writeErr = [&](llvm::Twine s) { writeInconsistent(*this, thisLabel, other, otherLabel, s); }; if (Results.size() != other.Results.size()) { - const bool tooMany = Results.size() < other.Results.size(); - writeErr(std::string(tooMany ? "Found too many: " : "Found too few: ") + - std::to_string(Results.size()) + " vs " + - std::to_string(other.Results.size())); - if (tooMany) - assert(false && "ASTScopeImpl found too many"); - else - assert(false && "ASTScopeImpl found too few"); + writeErr(thisLabel + " found " + std::to_string(Results.size()) + " but " + + otherLabel + " found " + std::to_string(other.Results.size())); + assert(false && "mismatch in number of results"); } for (size_t i : indices(Results)) { const auto &e = Results[i]; @@ -1338,7 +1380,7 @@ bool UnqualifiedLookupFactory::verifyEqualTo( llvm::errs() << "ValueDecls differ but print same\n"; else { writeErr(std::string( "ValueDecls differ at ") + std::to_string(i)); - assert(false && "ASTScopeImpl found different Decl"); + assert(false && "other lookup found different Decl"); } } if (e.getDeclContext() != oe.getDeclContext()) { @@ -1353,7 +1395,7 @@ bool UnqualifiedLookupFactory::verifyEqualTo( + std::to_string(IndexOfFirstOuterResult) + std::string( ", is: ") + std::to_string(other.IndexOfFirstOuterResult)); - assert(false && "ASTScopeImpl IndexOfFirstOuterResult differs"); + assert(false && "other lookup IndexOfFirstOuterResult differs"); } if (recordedSF != other.recordedSF) { writeErr(std::string("recordedSF differs: shouldBe: ") + @@ -1362,13 +1404,13 @@ bool UnqualifiedLookupFactory::verifyEqualTo( std::string(" is: ") + (other.recordedSF ? other.recordedSF->getFilename().str() : std::string(""))); - assert(false && "ASTScopeImpl recordedSF differs"); + assert(false && "other lookup recordedSF differs"); } if (recordedSF && recordedIsCascadingUse != other.recordedIsCascadingUse) { writeErr(std::string("recordedIsCascadingUse differs: shouldBe: ") + std::to_string(recordedIsCascadingUse) + std::string(" is: ") + std::to_string(other.recordedIsCascadingUse)); - assert(false && "ASTScopeImpl recordedIsCascadingUse differs"); + assert(false && "other lookup recordedIsCascadingUse differs"); } return true; } diff --git a/lib/ASTSectionImporter/ASTSectionImporter.cpp b/lib/ASTSectionImporter/ASTSectionImporter.cpp index c35254cb26fe8..d3797d3020b23 100644 --- a/lib/ASTSectionImporter/ASTSectionImporter.cpp +++ b/lib/ASTSectionImporter/ASTSectionImporter.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "swift/ASTSectionImporter/ASTSectionImporter.h" +#include "../Serialization/ModuleFormat.h" #include "swift/Basic/Dwarf.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Serialization/Validation.h" @@ -63,7 +64,8 @@ bool swift::parseASTSection(MemoryBufferSerializedModuleLoader &Loader, return false; } - buf = buf.substr(info.bytes); + buf = buf.substr( + llvm::alignTo(info.bytes, swift::serialization::SWIFTMODULE_ALIGNMENT)); } return true; diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 8504b672afc70..491de85f96bb9 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,59 +11,32 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -# Figure out if we can track VC revisions. -# FIXME: Copied from Clang. -function(find_first_existing_file out_var) - foreach(file ${ARGN}) - if(EXISTS "${file}") - set(${out_var} "${file}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file("${dir}" ${name}_vc) -macro(find_first_existing_vc_file out_var path) - find_first_existing_file(${out_var} - "${path}/.git/logs/HEAD" # Git - "${path}/.svn/wc.db" # SVN 1.7 - "${path}/.svn/entries" # SVN 1.6 - ) -endmacro() + # Create custom target to generate the VC revision include. + set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") -set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") + set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file(dep_file "${dir}") - # Create custom target to generate the VC revision include. - set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") - string(TOUPPER ${name} upper_name) - if(DEFINED dep_file) - add_custom_command(OUTPUT "${revision_inc}" - DEPENDS "${dep_file}" "${generate_vcs_version_script}" - COMMAND - ${CMAKE_COMMAND} "-DNAMES=${upper_name}" - "-D${upper_name}_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${revision_inc}" - -P "${generate_vcs_version_script}") - else() - # Generate an empty Revision.inc file if we are not using git or SVN. - file(WRITE "${revision_inc}" "") - endif() + add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=$" + "-D$_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") # Mark the generated header as being generated. - set_source_files_properties("${revision_inc}" + set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) + set(${revision_inc_var} ${version_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") -set(version_inc_files - ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) - add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -77,6 +50,7 @@ add_swift_host_library(swiftBasic STATIC JSONSerialization.cpp LangOptions.cpp LLVMContext.cpp + Located.cpp Mangler.cpp OutputFileMap.cpp Platform.cpp @@ -94,7 +68,10 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - ${version_inc_files} + + ${llvm_revision_inc} + ${clang_revision_inc} + ${swift_revision_inc} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc diff --git a/lib/Basic/FileTypes.cpp b/lib/Basic/FileTypes.cpp index a2657603e6f43..bae29a9f0daa0 100644 --- a/lib/Basic/FileTypes.cpp +++ b/lib/Basic/FileTypes.cpp @@ -94,6 +94,8 @@ bool file_types::isTextual(ID Id) { case file_types::TY_SerializedDiagnostics: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_Nothing: case file_types::TY_Remapping: case file_types::TY_IndexData: @@ -133,6 +135,8 @@ bool file_types::isAfterLLVM(ID Id) { case file_types::TY_SerializedDiagnostics: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_Nothing: case file_types::TY_Remapping: case file_types::TY_IndexData: @@ -175,6 +179,8 @@ bool file_types::isPartOfSwiftCompilation(ID Id) { case file_types::TY_SerializedDiagnostics: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_Nothing: case file_types::TY_ASTDump: case file_types::TY_Remapping: diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 9a967497b122d..def6143af764f 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -26,7 +26,18 @@ using namespace swift; -static const StringRef SupportedConditionalCompilationOSs[] = { +struct SupportedConditionalValue { + StringRef value; + + /// If the value has been deprecated, the new value to replace it with. + StringRef replacement = ""; + + SupportedConditionalValue(const char *value) : value(value) {} + SupportedConditionalValue(const char *value, const char *replacement) + : value(value), replacement(replacement) {} +}; + +static const SupportedConditionalValue SupportedConditionalCompilationOSs[] = { "OSX", "macOS", "tvOS", @@ -39,30 +50,34 @@ static const StringRef SupportedConditionalCompilationOSs[] = { "PS4", "Cygwin", "Haiku", + "WASI", }; -static const StringRef SupportedConditionalCompilationArches[] = { +static const SupportedConditionalValue SupportedConditionalCompilationArches[] = { "arm", "arm64", "i386", "x86_64", "powerpc64", "powerpc64le", - "s390x" + "s390x", + "wasm32", }; -static const StringRef SupportedConditionalCompilationEndianness[] = { +static const SupportedConditionalValue SupportedConditionalCompilationEndianness[] = { "little", "big" }; -static const StringRef SupportedConditionalCompilationRuntimes[] = { +static const SupportedConditionalValue SupportedConditionalCompilationRuntimes[] = { "_ObjC", "_Native", }; -static const StringRef SupportedConditionalCompilationTargetEnvironments[] = { +static const SupportedConditionalValue SupportedConditionalCompilationTargetEnvironments[] = { "simulator", + { "macabi", "macCatalyst" }, + "macCatalyst", // A synonym for "macabi" when compiling for iOS }; static const PlatformConditionKind AllPublicPlatformConditionKinds[] = { @@ -71,7 +86,7 @@ static const PlatformConditionKind AllPublicPlatformConditionKinds[] = { #include "swift/AST/PlatformConditionKinds.def" }; -ArrayRef getSupportedConditionalCompilationValues(const PlatformConditionKind &Kind) { +ArrayRef getSupportedConditionalCompilationValues(const PlatformConditionKind &Kind) { switch (Kind) { case PlatformConditionKind::OS: return SupportedConditionalCompilationOSs; @@ -95,11 +110,11 @@ PlatformConditionKind suggestedPlatformConditionKind(PlatformConditionKind Kind, for (const PlatformConditionKind& candidateKind : AllPublicPlatformConditionKinds) { if (candidateKind != Kind) { auto supportedValues = getSupportedConditionalCompilationValues(candidateKind); - for (const StringRef& candidateValue : supportedValues) { - if (candidateValue.lower() == lower) { + for (const SupportedConditionalValue& candidateValue : supportedValues) { + if (candidateValue.value.lower() == lower) { suggestedValues.clear(); - if (candidateValue != V) { - suggestedValues.emplace_back(candidateValue); + if (candidateValue.value != V) { + suggestedValues.emplace_back(candidateValue.value); } return candidateKind; } @@ -116,19 +131,21 @@ bool isMatching(PlatformConditionKind Kind, const StringRef &V, unsigned minDistance = std::numeric_limits::max(); std::string lower = V.lower(); auto supportedValues = getSupportedConditionalCompilationValues(Kind); - for (const StringRef& candidate : supportedValues) { - if (candidate == V) { + for (const SupportedConditionalValue& candidate : supportedValues) { + if (candidate.value == V) { suggestedKind = Kind; suggestions.clear(); + if (!candidate.replacement.empty()) + suggestions.push_back(candidate.replacement); return true; } - unsigned distance = StringRef(lower).edit_distance(candidate.lower()); + unsigned distance = StringRef(lower).edit_distance(candidate.value.lower()); if (distance < minDistance) { suggestions.clear(); minDistance = distance; } if (distance == minDistance) - suggestions.emplace_back(candidate); + suggestions.emplace_back(candidate.value); } suggestedKind = suggestedPlatformConditionKind(Kind, V, suggestions); return false; @@ -169,6 +186,16 @@ checkPlatformCondition(PlatformConditionKind Kind, StringRef Value) const { if (Kind == PlatformConditionKind::OS && Value == "macOS") return checkPlatformCondition(Kind, "OSX"); + // When compiling for iOS we consider "macCatalyst" to be a + // synonym of "macabi". This enables the use of + // #if targetEnvironment(macCatalyst) as a compilation + // condition for macCatalyst. + + if (Kind == PlatformConditionKind::TargetEnvironment && + Value == "macCatalyst" && Target.isiOS()) { + return checkPlatformCondition(Kind, "macabi"); + } + for (auto &Opt : llvm::reverse(PlatformConditionValues)) { if (Opt.first == Kind) if (Opt.second == Value) @@ -208,30 +235,51 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { bool UnsupportedOS = false; // Set the "os" platform condition. - if (Target.isMacOSX()) + switch (Target.getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: addPlatformConditionValue(PlatformConditionKind::OS, "OSX"); - else if (Target.isTvOS()) + break; + case llvm::Triple::TvOS: addPlatformConditionValue(PlatformConditionKind::OS, "tvOS"); - else if (Target.isWatchOS()) + break; + case llvm::Triple::WatchOS: addPlatformConditionValue(PlatformConditionKind::OS, "watchOS"); - else if (Target.isiOS()) + break; + case llvm::Triple::IOS: addPlatformConditionValue(PlatformConditionKind::OS, "iOS"); - else if (Target.isAndroid()) - addPlatformConditionValue(PlatformConditionKind::OS, "Android"); - else if (Target.isOSLinux()) - addPlatformConditionValue(PlatformConditionKind::OS, "Linux"); - else if (Target.isOSFreeBSD()) + break; + case llvm::Triple::Linux: + if (Target.getEnvironment() == llvm::Triple::Android) + addPlatformConditionValue(PlatformConditionKind::OS, "Android"); + else + addPlatformConditionValue(PlatformConditionKind::OS, "Linux"); + break; + case llvm::Triple::FreeBSD: addPlatformConditionValue(PlatformConditionKind::OS, "FreeBSD"); - else if (Target.isWindowsCygwinEnvironment()) - addPlatformConditionValue(PlatformConditionKind::OS, "Cygwin"); - else if (Target.isOSWindows()) - addPlatformConditionValue(PlatformConditionKind::OS, "Windows"); - else if (Target.isPS4()) - addPlatformConditionValue(PlatformConditionKind::OS, "PS4"); - else if (Target.isOSHaiku()) + break; + case llvm::Triple::Win32: + if (Target.getEnvironment() == llvm::Triple::Cygnus) + addPlatformConditionValue(PlatformConditionKind::OS, "Cygwin"); + else + addPlatformConditionValue(PlatformConditionKind::OS, "Windows"); + break; + case llvm::Triple::PS4: + if (Target.getVendor() == llvm::Triple::SCEI) + addPlatformConditionValue(PlatformConditionKind::OS, "PS4"); + else + UnsupportedOS = false; + break; + case llvm::Triple::Haiku: addPlatformConditionValue(PlatformConditionKind::OS, "Haiku"); - else + break; + case llvm::Triple::WASI: + addPlatformConditionValue(PlatformConditionKind::OS, "WASI"); + break; + default: UnsupportedOS = true; + break; + } bool UnsupportedArch = false; @@ -259,6 +307,9 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { case llvm::Triple::ArchType::systemz: addPlatformConditionValue(PlatformConditionKind::Arch, "s390x"); break; + case llvm::Triple::ArchType::wasm32: + addPlatformConditionValue(PlatformConditionKind::Arch, "wasm32"); + break; default: UnsupportedArch = true; } @@ -268,37 +319,25 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { // Set the "_endian" platform condition. switch (Target.getArch()) { + default: llvm_unreachable("undefined architecture endianness"); case llvm::Triple::ArchType::arm: case llvm::Triple::ArchType::thumb: - addPlatformConditionValue(PlatformConditionKind::Endianness, "little"); - break; case llvm::Triple::ArchType::aarch64: - addPlatformConditionValue(PlatformConditionKind::Endianness, "little"); - break; - case llvm::Triple::ArchType::ppc64: - addPlatformConditionValue(PlatformConditionKind::Endianness, "big"); - break; case llvm::Triple::ArchType::ppc64le: - addPlatformConditionValue(PlatformConditionKind::Endianness, "little"); - break; + case llvm::Triple::ArchType::wasm32: case llvm::Triple::ArchType::x86: - addPlatformConditionValue(PlatformConditionKind::Endianness, "little"); - break; case llvm::Triple::ArchType::x86_64: addPlatformConditionValue(PlatformConditionKind::Endianness, "little"); break; + case llvm::Triple::ArchType::ppc64: case llvm::Triple::ArchType::systemz: addPlatformConditionValue(PlatformConditionKind::Endianness, "big"); break; - default: - llvm_unreachable("undefined architecture endianness"); } // Set the "runtime" platform condition. - if (EnableObjCInterop) - addPlatformConditionValue(PlatformConditionKind::Runtime, "_ObjC"); - else - addPlatformConditionValue(PlatformConditionKind::Runtime, "_Native"); + addPlatformConditionValue(PlatformConditionKind::Runtime, + EnableObjCInterop ? "_ObjC" : "_Native"); // Set the "targetEnvironment" platform condition if targeting a simulator // environment. Otherwise _no_ value is present for targetEnvironment; it's @@ -307,6 +346,10 @@ std::pair LangOptions::setTarget(llvm::Triple triple) { addPlatformConditionValue(PlatformConditionKind::TargetEnvironment, "simulator"); + if (tripleIsMacCatalystEnvironment(Target)) + addPlatformConditionValue(PlatformConditionKind::TargetEnvironment, + "macabi"); + // If you add anything to this list, change the default size of // PlatformConditionValues to not require an extra allocation // in the common case. diff --git a/lib/Basic/Located.cpp b/lib/Basic/Located.cpp new file mode 100644 index 0000000000000..0c33f1a14e1e0 --- /dev/null +++ b/lib/Basic/Located.cpp @@ -0,0 +1,28 @@ +//===--- Located.cpp - Source Location and Associated Value ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +#include "llvm/Support/raw_ostream.h" +#include "swift/Basic/Located.h" + +using namespace swift; + +template +void Located::dump() const { + dump(llvm::errs()); +} + +template +void Located::dump(raw_ostream &os) const { + // FIXME: The following does not compile on newer clangs because operator<< + // does not exist for SourceLoc. More so, the operator does not exist because + // one needs a SourceManager reference and buffer ID to convert any given + // SourceLoc into line and column information. + //os << Loc << " " << Item; +} diff --git a/lib/Basic/Mangler.cpp b/lib/Basic/Mangler.cpp index 5b242cf44278c..fcf7d26037f33 100644 --- a/lib/Basic/Mangler.cpp +++ b/lib/Basic/Mangler.cpp @@ -200,8 +200,10 @@ void Mangler::appendIdentifier(StringRef ident) { recordOpStat("", OldPos); } -void Mangler::dump() { - llvm::errs() << Buffer.str() << '\n'; +void Mangler::dump() const { + // FIXME: const_casting because llvm::raw_svector_ostream::str() is + // incorrectly not marked const. + llvm::errs() << const_cast(this)->Buffer.str() << '\n'; } bool Mangler::tryMangleSubstitution(const void *ptr) { diff --git a/lib/Basic/OutputFileMap.cpp b/lib/Basic/OutputFileMap.cpp index e17208d21548a..44c03c836f7fe 100644 --- a/lib/Basic/OutputFileMap.cpp +++ b/lib/Basic/OutputFileMap.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/OutputFileMap.h" +#include "swift/Basic/FileTypes.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" @@ -20,26 +21,32 @@ using namespace swift; llvm::Expected -OutputFileMap::loadFromPath(StringRef Path, StringRef workingDirectory) { +OutputFileMap::loadFromPath(StringRef Path, StringRef workingDirectory, + const bool addEntriesForSourceRangeDependencies) { llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(Path); if (!FileBufOrErr) { return llvm::errorCodeToError(FileBufOrErr.getError()); } - return loadFromBuffer(std::move(FileBufOrErr.get()), workingDirectory); + return loadFromBuffer(std::move(FileBufOrErr.get()), workingDirectory, + addEntriesForSourceRangeDependencies); } llvm::Expected -OutputFileMap::loadFromBuffer(StringRef Data, StringRef workingDirectory) { +OutputFileMap::loadFromBuffer(StringRef Data, StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies) { std::unique_ptr Buffer{ llvm::MemoryBuffer::getMemBuffer(Data)}; - return loadFromBuffer(std::move(Buffer), workingDirectory); + return loadFromBuffer(std::move(Buffer), workingDirectory, + addEntriesForSourceRangeDependencies); } llvm::Expected OutputFileMap::loadFromBuffer(std::unique_ptr Buffer, - StringRef workingDirectory) { - return parse(std::move(Buffer), workingDirectory); + StringRef workingDirectory, + bool addEntriesForSourceRangeDependencies) { + return parse(std::move(Buffer), workingDirectory, + addEntriesForSourceRangeDependencies); } const TypeToPathMap *OutputFileMap::getOutputMapForInput(StringRef Input) const{ @@ -136,7 +143,8 @@ void OutputFileMap::write(llvm::raw_ostream &os, llvm::Expected OutputFileMap::parse(std::unique_ptr Buffer, - StringRef workingDirectory) { + StringRef workingDirectory, + const bool addEntriesForSourceRangeDependencies) { auto constructError = [](const char *errorString) -> llvm::Expected { return llvm::make_error(errorString, @@ -225,6 +233,25 @@ OutputFileMap::parse(std::unique_ptr Buffer, llvm::SmallString<128> PathStorage; OutputMap.insert(std::pair( Kind, resolvePath(Path, PathStorage))); + + // HACK: fake up an SwiftRanges & CompiledSource output filenames + if (addEntriesForSourceRangeDependencies && + Kind == file_types::TY_SwiftDeps) { + // Not for the master-swiftdeps + llvm::SmallString<128> Storage; + if (!InputPath->getValue(Storage).empty()) { + std::string baseName = OutputMap[Kind]; + baseName.resize(baseName.size() - + getExtension(file_types::TY_SwiftDeps).size()); + auto insertFilename = [&](file_types::ID type) { + std::string s = baseName; + s += getExtension(type); + OutputMap.insert({type, s}); + }; + insertFilename(file_types::TY_SwiftRanges); + insertFilename(file_types::TY_CompiledSource); + } + } } llvm::SmallString<128> InputStorage; diff --git a/lib/Basic/PartsOfSpeech.def b/lib/Basic/PartsOfSpeech.def index a134ab98183c9..4c5fede616b74 100644 --- a/lib/Basic/PartsOfSpeech.def +++ b/lib/Basic/PartsOfSpeech.def @@ -20,44 +20,40 @@ # define PREPOSITION(Word) #endif -#ifndef DIRECTIONAL_PREPOSITION -# define DIRECTIONAL_PREPOSITION(Word) PREPOSITION(Word) -#endif - #ifndef VERB # define VERB(Word) #endif -DIRECTIONAL_PREPOSITION(above) -DIRECTIONAL_PREPOSITION(after) -DIRECTIONAL_PREPOSITION(along) -DIRECTIONAL_PREPOSITION(alongside) -DIRECTIONAL_PREPOSITION(as) -DIRECTIONAL_PREPOSITION(at) -DIRECTIONAL_PREPOSITION(before) -DIRECTIONAL_PREPOSITION(below) -DIRECTIONAL_PREPOSITION(by) -DIRECTIONAL_PREPOSITION(following) -DIRECTIONAL_PREPOSITION(for) -DIRECTIONAL_PREPOSITION(from) -DIRECTIONAL_PREPOSITION(given) -DIRECTIONAL_PREPOSITION(in) -DIRECTIONAL_PREPOSITION(including) -DIRECTIONAL_PREPOSITION(inside) -DIRECTIONAL_PREPOSITION(into) -DIRECTIONAL_PREPOSITION(matching) -DIRECTIONAL_PREPOSITION(of) -DIRECTIONAL_PREPOSITION(on) -DIRECTIONAL_PREPOSITION(passing) -DIRECTIONAL_PREPOSITION(preceding) -DIRECTIONAL_PREPOSITION(since) -DIRECTIONAL_PREPOSITION(to) -DIRECTIONAL_PREPOSITION(until) -DIRECTIONAL_PREPOSITION(using) -DIRECTIONAL_PREPOSITION(via) -DIRECTIONAL_PREPOSITION(when) +PREPOSITION(above) +PREPOSITION(after) +PREPOSITION(along) +PREPOSITION(alongside) +PREPOSITION(as) +PREPOSITION(at) +PREPOSITION(before) +PREPOSITION(below) +PREPOSITION(by) +PREPOSITION(following) +PREPOSITION(for) +PREPOSITION(from) +PREPOSITION(given) +PREPOSITION(in) +PREPOSITION(including) +PREPOSITION(inside) +PREPOSITION(into) +PREPOSITION(matching) +PREPOSITION(of) +PREPOSITION(on) +PREPOSITION(passing) +PREPOSITION(preceding) +PREPOSITION(since) +PREPOSITION(to) +PREPOSITION(until) +PREPOSITION(using) +PREPOSITION(via) +PREPOSITION(when) PREPOSITION(with) -DIRECTIONAL_PREPOSITION(within) +PREPOSITION(within) VERB(abbreviate) VERB(accept) diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 356a7d4186139..9581df610fab2 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -21,6 +21,7 @@ using namespace swift; bool swift::tripleIsiOSSimulator(const llvm::Triple &triple) { llvm::Triple::ArchType arch = triple.getArch(); return (triple.isiOS() && + !tripleIsMacCatalystEnvironment(triple) && // FIXME: transitional, this should eventually stop testing arch, and // switch to only checking the -environment field. (triple.isSimulatorEnvironment() || @@ -54,17 +55,50 @@ bool swift::tripleIsAnySimulator(const llvm::Triple &triple) { tripleIsAppleTVSimulator(triple); } +bool swift::tripleIsMacCatalystEnvironment(const llvm::Triple &triple) { + return triple.isiOS() && !triple.isTvOS() && + triple.getEnvironment() == llvm::Triple::MacABI; +} + +bool swift::triplesAreValidForZippering(const llvm::Triple &target, + const llvm::Triple &targetVariant) { + // The arch and vendor must match. + if (target.getArchName() != targetVariant.getArchName() || + target.getArch() != targetVariant.getArch() || + target.getSubArch() != targetVariant.getSubArch() || + target.getVendor() != targetVariant.getVendor()) { + return false; + } + + // Allow a macOS target and an iOS-macabi target variant + // This is typically the case when zippering a library originally + // developed for macOS. + if (target.isMacOSX() && tripleIsMacCatalystEnvironment(targetVariant)) { + return true; + } + + // Allow an iOS-macabi target and a macOS target variant. This would + // be the case when zippering a library originally developed for + // iOS. + if (targetVariant.isMacOSX() && tripleIsMacCatalystEnvironment(target)) { + return true; + } + + return false; +} bool swift::tripleRequiresRPathForSwiftInOS(const llvm::Triple &triple) { if (triple.isMacOSX()) { // macOS 10.14.4 contains a copy of Swift, but the linker will still use an // rpath-based install name until 10.15. return triple.isMacOSXVersionLT(10, 15); + } - } else if (triple.isiOS()) { + if (triple.isiOS()) { return triple.isOSVersionLT(12, 2); + } - } else if (triple.isWatchOS()) { + if (triple.isWatchOS()) { return triple.isOSVersionLT(5, 2); } @@ -162,7 +196,6 @@ StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) { case llvm::Triple::AMDPAL: case llvm::Triple::HermitCore: case llvm::Triple::Hurd: - case llvm::Triple::WASI: return ""; case llvm::Triple::Darwin: case llvm::Triple::MacOSX: @@ -190,26 +223,24 @@ StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) { return "ps4"; case llvm::Triple::Haiku: return "haiku"; + case llvm::Triple::WASI: + return "wasi"; } llvm_unreachable("unsupported OS"); } StringRef swift::getMajorArchitectureName(const llvm::Triple &Triple) { if (Triple.isOSLinux()) { - switch(Triple.getSubArch()) { - default: - return Triple.getArchName(); - break; + switch (Triple.getSubArch()) { case llvm::Triple::SubArchType::ARMSubArch_v7: return "armv7"; - break; case llvm::Triple::SubArchType::ARMSubArch_v6: return "armv6"; + default: break; } - } else { - return Triple.getArchName(); } + return Triple.getArchName(); } // The code below is responsible for normalizing target triples into the form @@ -306,6 +337,7 @@ getEnvironmentForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) { .Cases("unknown", "", None) // These values are also supported, but are handled by the default case below: // .Case ("simulator", StringRef("simulator")) + // .Case ("macabi", StringRef("macabi")) .Default(tripleEnvironment); } @@ -344,10 +376,25 @@ llvm::Triple swift::getTargetSpecificModuleTriple(const llvm::Triple &triple) { return triple; } +llvm::Triple swift::getUnversionedTriple(const llvm::Triple &triple) { + StringRef unversionedOSName = triple.getOSName().take_until(llvm::isDigit); + if (triple.getEnvironment()) { + StringRef environment = + llvm::Triple::getEnvironmentTypeName(triple.getEnvironment()); + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName, environment); + } + + return llvm::Triple(triple.getArchName(), triple.getVendorName(), + unversionedOSName); +} + Optional -swift::getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple){ +swift::getSwiftRuntimeCompatibilityVersionForTarget( + const llvm::Triple &Triple) { unsigned Major, Minor, Micro; - + if (Triple.isMacOSX()) { Triple.getMacOSXVersion(Major, Minor, Micro); if (Major == 10) { @@ -355,11 +402,7 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple){ return llvm::VersionTuple(5, 0); } else if (Minor <= 15) { return llvm::VersionTuple(5, 1); - } else { - return None; } - } else { - return None; } } else if (Triple.isiOS()) { // includes tvOS Triple.getiOSVersion(Major, Minor, Micro); @@ -367,8 +410,6 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple){ return llvm::VersionTuple(5, 0); } else if (Major <= 13) { return llvm::VersionTuple(5, 1); - } else { - return None; } } else if (Triple.isWatchOS()) { Triple.getWatchOSVersion(Major, Minor, Micro); @@ -376,11 +417,8 @@ swift::getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple){ return llvm::VersionTuple(5, 0); } else if (Major <= 6) { return llvm::VersionTuple(5, 1); - } else { - return None; } - } else { - return None; } -} + return None; +} diff --git a/lib/Basic/PrefixMap.cpp b/lib/Basic/PrefixMap.cpp index 3fe43ad6cf7cd..9f7390d698cce 100644 --- a/lib/Basic/PrefixMap.cpp +++ b/lib/Basic/PrefixMap.cpp @@ -34,8 +34,8 @@ enum class ChildKind { Left, Right, Further, Root }; // that's technically instantiation-specific. Redefining the struct here // is technically an aliasing violation, but we can just tell the compilers // that actually use TBAA that this is okay. -typedef struct _Node Node LLVM_MAY_ALIAS; -struct _Node { +typedef struct _Node Node; +struct LLVM_MAY_ALIAS _Node { // If you change the layout in the header, you'll need to change it here. // (This comment is repeated there.) Node *Left, *Right, *Further; diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 0a0f0f7db4f79..76547ff2e91f6 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -213,7 +213,8 @@ StringRef SourceManager::extractText(CharSourceRange Range, Range.getByteLength()); } -unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const { +Optional +SourceManager::findBufferContainingLocInternal(SourceLoc Loc) const { assert(Loc.isValid()); // Search the buffers back-to front, so later alias buffers are // visited first. @@ -226,9 +227,20 @@ unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const { less_equal(Loc.Value.getPointer(), Buf->getBufferEnd())) return i; } + return None; +} + +unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const { + auto Id = findBufferContainingLocInternal(Loc); + if (Id.hasValue()) + return *Id; llvm_unreachable("no buffer containing location found"); } +bool SourceManager::isOwning(SourceLoc Loc) const { + return findBufferContainingLocInternal(Loc).hasValue(); +} + void SourceRange::widen(SourceRange Other) { if (Other.Start.Value.getPointer() < Start.Value.getPointer()) Start = Other.Start; @@ -319,10 +331,20 @@ SourceManager::resolveOffsetForEndOfLine(unsigned BufferId, return resolveFromLineCol(BufferId, Line, ~0u); } +llvm::Optional +SourceManager::getLineLength(unsigned BufferId, unsigned Line) const { + auto BegOffset = resolveFromLineCol(BufferId, Line, 0); + auto EndOffset = resolveFromLineCol(BufferId, Line, ~0u); + if (BegOffset && EndOffset) { + return EndOffset.getValue() - BegOffset.getValue(); + } + return None; +} + llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { - if (Line == 0 || Col == 0) { + if (Line == 0) { return None; } const bool LineEnd = Col == ~0u; @@ -341,6 +363,9 @@ llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, return None; } Ptr = LineStart; + if (Col == 0) { + return Ptr - InputBuf->getBufferStart(); + } // The <= here is to allow for non-inclusive range end positions at EOF for (; ; ++Ptr) { --Col; @@ -357,3 +382,30 @@ llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, return None; } +unsigned SourceManager::getExternalSourceBufferId(StringRef Path) { + auto It = BufIdentIDMap.find(Path); + if (It != BufIdentIDMap.end()) { + return It->getSecond(); + } + unsigned Id = 0u; + auto InputFileOrErr = swift::vfs::getFileOrSTDIN(*getFileSystem(), Path); + if (InputFileOrErr) { + // This assertion ensures we can look up from the map in the future when + // using the same Path. + assert(InputFileOrErr.get()->getBufferIdentifier() == Path); + Id = addNewSourceBuffer(std::move(InputFileOrErr.get())); + } + return Id; +} + +SourceLoc +SourceManager::getLocFromExternalSource(StringRef Path, unsigned Line, + unsigned Col) { + auto BufferId = getExternalSourceBufferId(Path); + if (BufferId == 0u) + return SourceLoc(); + auto Offset = resolveFromLineCol(BufferId, Line, Col); + if (!Offset.hasValue()) + return SourceLoc(); + return getLocForOffset(BufferId, *Offset); +} diff --git a/lib/Basic/Statistic.cpp b/lib/Basic/Statistic.cpp index 90cacfc70a7a2..18ee0d31c9d5e 100644 --- a/lib/Basic/Statistic.cpp +++ b/lib/Basic/Statistic.cpp @@ -18,13 +18,14 @@ #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/SIL/SILFunction.h" -#include "swift/Driver/DependencyGraph.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Config/config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SaveAndRestore.h" #include #include @@ -348,7 +349,8 @@ UnifiedStatsReporter::UnifiedStatsReporter(StringRef ProgramName, ProgramName, "Running Program")), SourceMgr(SM), ClangSourceMgr(CSM), - RecursiveTimers(llvm::make_unique()) + RecursiveTimers(llvm::make_unique()), + IsFlushingTracesAndProfiles(false) { path::append(StatsFilename, makeStatsFileName(ProgramName, AuxName)); path::append(TraceFilename, makeTraceFileName(ProgramName, AuxName)); @@ -557,6 +559,13 @@ UnifiedStatsReporter::saveAnyFrontendStatsEvents( bool IsEntry) { assert(MainThreadID == std::this_thread::get_id()); + + // Don't record any new stats if we're currently flushing the ones we've + // already recorded. This can happen when requests get kicked off when + // computing source ranges. + if (IsFlushingTracesAndProfiles) + return; + // First make a note in the recursion-safe timers; these // are active anytime UnifiedStatsReporter is active. if (IsEntry) { @@ -711,6 +720,10 @@ UnifiedStatsReporter::~UnifiedStatsReporter() void UnifiedStatsReporter::flushTracesAndProfiles() { + // Note that we're currently flushing statistics and shouldn't record any + // more until we've finished. + llvm::SaveAndRestore flushing(IsFlushingTracesAndProfiles, true); + if (FrontendStatsEvents && SourceMgr) { std::error_code EC; raw_fd_ostream tstream(TraceFilename, EC, fs::F_Append | fs::F_Text); diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index cc8b173f3314e..a33dd51e8caac 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -44,16 +44,13 @@ bool swift::canBeMemberName(StringRef identifier) { .Default(true); } -PrepositionKind swift::getPrepositionKind(StringRef word) { -#define DIRECTIONAL_PREPOSITION(Word) \ - if (word.equals_lower(#Word)) \ - return PK_Directional; +bool swift::isPreposition(StringRef word) { #define PREPOSITION(Word) \ if (word.equals_lower(#Word)) \ - return PK_Nondirectional; + return true; #include "PartsOfSpeech.def" - return PK_None; + return false; } PartOfSpeech swift::getPartOfSpeech(StringRef word) { @@ -68,7 +65,7 @@ PartOfSpeech swift::getPartOfSpeech(StringRef word) { // Identify gerunds, which always end in "ing". if (word.endswith("ing") && word.size() > 4) { - StringRef possibleVerb = word.substr(0, word.size()-3); + StringRef possibleVerb = word.drop_back(3); // If what remains is a verb, we have a gerund. if (getPartOfSpeech(possibleVerb) == PartOfSpeech::Verb) @@ -87,7 +84,7 @@ PartOfSpeech swift::getPartOfSpeech(StringRef word) { // instance of that letter and try again. unsigned count = possibleVerb.size(); if (possibleVerb[count-1] == possibleVerb[count-2] && - getPartOfSpeech(possibleVerb.substr(0, count-1)) == PartOfSpeech::Verb) + getPartOfSpeech(possibleVerb.drop_back()) == PartOfSpeech::Verb) return PartOfSpeech::Gerund; } @@ -501,8 +498,7 @@ StringRef camel_case::toLowercaseWord(StringRef string, /// Omit needless words from the beginning of a name. static StringRef omitNeedlessWordsFromPrefix(StringRef name, - OmissionTypeName type, - StringScratchSpace &scratch){ + OmissionTypeName type) { if (type.empty()) return name; @@ -523,11 +519,11 @@ static StringRef omitNeedlessWordsFromPrefix(StringRef name, StringRef nextWord = camel_case::getFirstWord( newName.substr(firstWord.size())); if (nextWord.endswith("ing")) { - return toLowercaseWord(newName.substr(firstWord.size()), scratch); + return newName.substr(firstWord.size()); } } - return toLowercaseWord(newName, scratch); + return newName; } return name; @@ -609,7 +605,7 @@ omitTrailingTypeNameWithSpecialCases(StringRef name, /// If there is no match, returns the end WordIterator for \p name. static Words::iterator matchTypeNameFromBackWithSpecialCases( StringRef name, OmissionTypeName typeName, - const InheritedNameSet *allPropertyNames, bool canSkipTypeSuffix = true) { + const InheritedNameSet *allPropertyNames) { // Get the camel-case words in the name and type name. auto nameWords = camel_case::getWords(name); auto typeWords = camel_case::getWords(typeName.Name); @@ -697,7 +693,7 @@ static Words::iterator matchTypeNameFromBackWithSpecialCases( } // If this is a skippable suffix, skip it and keep looking. - if (canSkipTypeSuffix && nameWordRevIter == nameWordRevIterBegin) { + if (nameWordRevIter == nameWordRevIterBegin) { if (auto withoutSuffix = skipTypeSuffix(typeName.Name)) { typeName.Name = *withoutSuffix; typeWords = camel_case::getWords(typeName.Name); @@ -724,8 +720,6 @@ omitSelfTypeFromBaseName(StringRef name, OmissionTypeName typeName, typeName.CollectionElement = StringRef(); auto nameWords = camel_case::getWords(name); - - bool canSkipTypeSuffix = true; Optional> matchingRange; // Search backwards for the type name, whether anchored at the end or not. @@ -734,8 +728,7 @@ omitSelfTypeFromBaseName(StringRef name, OmissionTypeName typeName, ++nameReverseIter) { StringRef matchName = nameReverseIter.base().getPriorStr(); auto matchIter = matchTypeNameFromBackWithSpecialCases(matchName, typeName, - allPropertyNames, - canSkipTypeSuffix); + allPropertyNames); auto matchIterInFullName = WordIterator(name, matchIter.getPosition()); if (matchIterInFullName != nameReverseIter.base()) { matchingRange = llvm::make_range(matchIterInFullName, @@ -746,7 +739,8 @@ omitSelfTypeFromBaseName(StringRef name, OmissionTypeName typeName, // Note: This behavior fell out of a previous implementation of // omit-needless-words, even though it probably wasn't intentional. At this // point, though, it could be source-breaking to change it. - canSkipTypeSuffix = false; + while (auto withoutSuffix = skipTypeSuffix(typeName.Name)) + typeName.Name = *withoutSuffix; } // If we matched nothing, or if the type name was all the way at the start @@ -815,8 +809,6 @@ omitTrailingTypeNameWithSpecialCases(StringRef name, OmissionTypeName typeName, if (matchIter == nameWords.end()) return name; - StringRef origName = name; - // Handle complete name matches. if (matchIter == nameWords.begin()) { // If we're doing a partial match or we have an initial @@ -835,59 +827,62 @@ omitTrailingTypeNameWithSpecialCases(StringRef name, OmissionTypeName typeName, switch (role) { case NameRole::Property: // Always strip off type information. - name = matchIter.getPriorStr(); break; - case NameRole::BaseName: case NameRole::FirstParameter: case NameRole::Partial: - case NameRole::SubsequentParameter: + case NameRole::SubsequentParameter: { + // Classify the part of speech of the word before the type information we + // would strip off. We only want to strip it if the previous word is a + // preposition, verb, or gerund. + auto previousWordIter = std::prev(matchIter); + if (getPartOfSpeech(*previousWordIter) == PartOfSpeech::Unknown) + return name; + break; + } + + case NameRole::BaseName: { // Classify the part of speech of the word before the type // information we would strip off. auto previousWordIter = std::prev(matchIter); switch (getPartOfSpeech(*previousWordIter)) { case PartOfSpeech::Preposition: - if (role == NameRole::BaseName) { - // Strip off the part of the name that is redundant with - // type information, so long as there's something preceding the - // preposition. - if (previousWordIter != nameWords.begin()) - name = matchIter.getPriorStr(); - break; - } - - LLVM_FALLTHROUGH; + // If there's nothing preceding the preposition, don't strip anything. + if (previousWordIter == nameWords.begin()) + return name; + break; case PartOfSpeech::Verb: case PartOfSpeech::Gerund: // Don't prune redundant type information from the base name if // there is a corresponding property (either singular or plural). - if (role == NameRole::BaseName && - textMatchesPropertyName(matchIter.getRestOfStr(), allPropertyNames)) + if (textMatchesPropertyName(matchIter.getRestOfStr(), allPropertyNames)) return name; - - // Strip off the part of the name that is redundant with - // type information. - name = matchIter.getPriorStr(); break; case PartOfSpeech::Unknown: // Assume it's a noun or adjective; don't strip anything. - break; + return name; } + break; } + } + + // Strip off the part of the name that is redundant with + // type information. + StringRef newName = matchIter.getPriorStr(); switch (role) { case NameRole::BaseName: case NameRole::Property: // If we ended up with something that can't be a member name, do nothing. - if (!canBeMemberName(name)) - return origName; + if (!canBeMemberName(newName)) + return name; // If we ended up with a vacuous name like "get" or "set", do nothing. - if (isVacuousName(name)) - return origName; + if (isVacuousName(newName)) + return name; break; @@ -898,7 +893,7 @@ omitTrailingTypeNameWithSpecialCases(StringRef name, OmissionTypeName typeName, } // We're done. - return name; + return newName; } StringRef camel_case::toLowercaseInitialisms(StringRef string, @@ -923,7 +918,8 @@ camel_case::toLowercaseInitialisms(StringRef string, // Lowercase until we hit the an uppercase letter followed by a // non-uppercase letter. - llvm::SmallString<32> scratchStr; + scratch.clear(); + scratch.reserve(string.size()); for (unsigned i = 0, n = string.size(); i != n; ++i) { // If the next character is not uppercase, stop. if (i < n - 1 && !clang::isUppercase(string[i+1])) { @@ -932,19 +928,18 @@ camel_case::toLowercaseInitialisms(StringRef string, // lowercase the character we're on. if (i == 0 || !clang::isLetter(string[i+1]) || isPluralSuffix(camel_case::getFirstWord(string.substr(i+1)))) { - scratchStr.push_back(clang::toLowercase(string[i])); + scratch.push_back(clang::toLowercase(string[i])); ++i; } - scratchStr.append(string.substr(i)); + scratch.append(string.substr(i).begin(), string.substr(i).end()); break; } - scratchStr.push_back(clang::toLowercase(string[i])); + scratch.push_back(clang::toLowercase(string[i])); } - scratch = scratchStr; - return {scratch.begin(), scratch.size()}; + return {scratch.data(), scratch.size()}; } /// Determine whether the given word occurring before the given @@ -981,10 +976,6 @@ static bool wordConflictsAfterPreposition(StringRef word, return true; } - if (camel_case::sameWordIgnoreFirstCase(preposition, "and") && - camel_case::sameWordIgnoreFirstCase(word, "return")) - return true; - return false; } @@ -1198,7 +1189,7 @@ static bool splitBaseName(StringRef &baseName, StringRef &argName, // Try splitting a Boolean "Animated". if (paramType.isBoolean() && camel_case::getLastWord(baseName) == "Animated") { - baseName = baseName.substr(0, baseName.size() - strlen("Animated")); + baseName = baseName.drop_back(strlen("Animated")); argName = "animated"; return true; } @@ -1257,8 +1248,7 @@ bool swift::omitNeedlessWords(StringRef &baseName, // prefix of the name. bool resultTypeMatchesContext = (resultType == contextType); if (resultTypeMatchesContext) { - StringRef newBaseName = omitNeedlessWordsFromPrefix(baseName, contextType, - scratch); + StringRef newBaseName = omitNeedlessWordsFromPrefix(baseName, contextType); if (newBaseName != baseName) { baseName = newBaseName; anyChanges = true; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a56d16dcfcb7b..e3a21225fdb66 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -37,9 +37,12 @@ add_subdirectory(RemoteAST) add_subdirectory(Sema) add_subdirectory(Serialization) add_subdirectory(SwiftDemangle) +add_subdirectory(SwiftReflection) +add_subdirectory(SwiftRemoteMirror) add_subdirectory(SIL) add_subdirectory(SILGen) add_subdirectory(SILOptimizer) +add_subdirectory(SymbolGraphGen) add_subdirectory(Syntax) add_subdirectory(SyntaxParse) add_subdirectory(TBDGen) diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index b46ee39ce0be4..6417184633427 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -9,6 +9,7 @@ add_swift_host_library(swiftClangImporter STATIC ClangAdapter.cpp ClangDiagnosticConsumer.cpp ClangImporter.cpp + ClangSourceBufferImporter.cpp DWARFImporter.cpp IAMInference.cpp ImportDecl.cpp @@ -20,7 +21,8 @@ add_swift_host_library(swiftClangImporter STATIC ) target_link_libraries(swiftClangImporter PRIVATE swiftAST - swiftParse) + swiftParse + LLVMBitstreamReader) # This property is only set by calls to clang_tablegen. It will not be set on # standalone builds, so it can always be safely passed. diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index 6260ccdb911a8..eddac5d46b3bf 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -77,6 +77,17 @@ importer::getDefinitionForClangTypeDecl(const clang::Decl *D) { return None; } +const clang::Decl * +importer::getFirstNonLocalDecl(const clang::Decl *D) { + D = D->getCanonicalDecl(); + auto iter = llvm::find_if(D->redecls(), [](const clang::Decl *next) -> bool { + return !next->isLexicallyWithinFunctionOrMethod(); + }); + if (iter == D->redecls_end()) + return nullptr; + return *iter; +} + Optional importer::getClangSubmoduleForDecl(const clang::Decl *D, bool allowForwardDeclaration) { @@ -91,7 +102,7 @@ importer::getClangSubmoduleForDecl(const clang::Decl *D, } if (!actual) - actual = D->getCanonicalDecl(); + actual = getFirstNonLocalDecl(D); return actual->getImportedOwningModule(); } @@ -601,29 +612,6 @@ OptionalTypeKind importer::translateNullability(clang::NullabilityKind kind) { llvm_unreachable("Invalid NullabilityKind."); } -bool importer::hasDesignatedInitializers( - const clang::ObjCInterfaceDecl *classDecl) { - if (classDecl->hasDesignatedInitializers()) - return true; - - return false; -} - -bool importer::isDesignatedInitializer( - const clang::ObjCInterfaceDecl *classDecl, - const clang::ObjCMethodDecl *method) { - // If the information is on the AST, use it. - if (classDecl->hasDesignatedInitializers()) { - auto *methodParent = method->getClassInterface(); - if (!methodParent || - methodParent->getCanonicalDecl() == classDecl->getCanonicalDecl()) { - return method->hasAttr(); - } - } - - return false; -} - bool importer::isRequiredInitializer(const clang::ObjCMethodDecl *method) { // FIXME: No way to express this in Objective-C. return false; diff --git a/lib/ClangImporter/ClangAdapter.h b/lib/ClangImporter/ClangAdapter.h index 84c35ad118961..0b066364bd622 100644 --- a/lib/ClangImporter/ClangAdapter.h +++ b/lib/ClangImporter/ClangAdapter.h @@ -60,6 +60,34 @@ struct PlatformAvailability; Optional getDefinitionForClangTypeDecl(const clang::Decl *D); +/// Returns the first redeclaration of \p D outside of a function. +/// +/// C allows redeclaring most declarations in function bodies, as so: +/// +/// void usefulPublicFunction(void) { +/// extern void importantInternalFunction(int code); +/// importantInternalFunction(42); +/// } +/// +/// This should allow clients to call \c usefulPublicFunction without exposing +/// \c importantInternalFunction . However, if there is another declaration of +/// \c importantInternalFunction later, Clang still needs to treat them as the +/// same function. This is normally fine...except that if the local declaration +/// is the \e first declaration, it'll also get used as the "canonical" +/// declaration that Clang (and Swift) use for uniquing purposes. +/// +/// Every imported declaration gets assigned to a module in Swift, and for +/// declarations without definitions that choice is somewhat arbitrary. But it +/// would be better not to pick a local declaration like the one above, and +/// therefore this method should be used instead of +/// clang::Decl::getCanonicalDecl when the containing module is important. +/// +/// If there are no non-local redeclarations, returns null. +/// If \p D is not a kind of declaration that supports being redeclared, just +/// returns \p D itself. +const clang::Decl * +getFirstNonLocalDecl(const clang::Decl *D); + /// Returns the module \p D comes from, or \c None if \p D does not have /// a valid associated module. /// @@ -111,15 +139,6 @@ bool hasNativeSwiftDecl(const clang::Decl *decl); /// Translation API nullability from an API note into an optional kind. OptionalTypeKind translateNullability(clang::NullabilityKind kind); -/// Determine whether the given class has designated initializers, -/// consulting -bool hasDesignatedInitializers(const clang::ObjCInterfaceDecl *classDecl); - -/// Determine whether the given method is a designated initializer -/// of the given class. -bool isDesignatedInitializer(const clang::ObjCInterfaceDecl *classDecl, - const clang::ObjCMethodDecl *method); - /// Determine whether the given method is a required initializer /// of the given class. bool isRequiredInitializer(const clang::ObjCMethodDecl *method); diff --git a/lib/ClangImporter/ClangDiagnosticConsumer.cpp b/lib/ClangImporter/ClangDiagnosticConsumer.cpp index d882b0dad11ad..b394212f1c9b6 100644 --- a/lib/ClangImporter/ClangDiagnosticConsumer.cpp +++ b/lib/ClangImporter/ClangDiagnosticConsumer.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "ClangDiagnosticConsumer.h" +#include "ClangSourceBufferImporter.h" #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" @@ -22,6 +23,7 @@ #include "llvm/ADT/STLExtras.h" using namespace swift; +using namespace swift::importer; namespace { class ClangDiagRenderer final : public clang::DiagnosticNoteRenderer { @@ -109,81 +111,6 @@ ClangDiagnosticConsumer::ClangDiagnosticConsumer( : TextDiagnosticPrinter(llvm::errs(), &clangDiagOptions), ImporterImpl(impl), DumpToStderr(dumpToStderr) {} -static SourceLoc findEndOfLine(SourceManager &SM, SourceLoc loc, - unsigned bufferID) { - CharSourceRange entireBuffer = SM.getRangeForBuffer(bufferID); - CharSourceRange rangeFromLoc{SM, loc, entireBuffer.getEnd()}; - StringRef textFromLoc = SM.extractText(rangeFromLoc); - size_t newlineOffset = textFromLoc.find_first_of({"\r\n\0", 3}); - if (newlineOffset == StringRef::npos) - return entireBuffer.getEnd(); - return loc.getAdvancedLoc(newlineOffset); -} - -SourceLoc ClangDiagnosticConsumer::resolveSourceLocation( - const clang::SourceManager &clangSrcMgr, - clang::SourceLocation clangLoc) { - SourceManager &swiftSrcMgr = ImporterImpl.SwiftContext.SourceMgr; - SourceLoc loc; - - clangLoc = clangSrcMgr.getFileLoc(clangLoc); - auto decomposedLoc = clangSrcMgr.getDecomposedLoc(clangLoc); - if (decomposedLoc.first.isInvalid()) - return loc; - - auto buffer = clangSrcMgr.getBuffer(decomposedLoc.first); - unsigned mirrorID; - - auto mirrorIter = mirroredBuffers.find(buffer); - if (mirrorIter != mirroredBuffers.end()) { - mirrorID = mirrorIter->second; - } else { - std::unique_ptr mirrorBuffer{ - llvm::MemoryBuffer::getMemBuffer(buffer->getBuffer(), - buffer->getBufferIdentifier(), - /*RequiresNullTerminator=*/true) - }; - mirrorID = swiftSrcMgr.addNewSourceBuffer(std::move(mirrorBuffer)); - mirroredBuffers[buffer] = mirrorID; - } - loc = swiftSrcMgr.getLocForOffset(mirrorID, decomposedLoc.second); - - auto presumedLoc = clangSrcMgr.getPresumedLoc(clangLoc); - if (!presumedLoc.getFilename()) - return loc; - if (presumedLoc.getLine() == 0) - return SourceLoc(); - - unsigned bufferLineNumber = - clangSrcMgr.getLineNumber(decomposedLoc.first, decomposedLoc.second); - - StringRef presumedFile = presumedLoc.getFilename(); - SourceLoc startOfLine = loc.getAdvancedLoc(-presumedLoc.getColumn() + 1); - bool isNewVirtualFile = - swiftSrcMgr.openVirtualFile(startOfLine, presumedFile, - presumedLoc.getLine() - bufferLineNumber); - if (isNewVirtualFile) { - SourceLoc endOfLine = findEndOfLine(swiftSrcMgr, loc, mirrorID); - swiftSrcMgr.closeVirtualFile(endOfLine); - } - - using SourceManagerRef = llvm::IntrusiveRefCntPtr; - auto iter = std::lower_bound(sourceManagersWithDiagnostics.begin(), - sourceManagersWithDiagnostics.end(), - &clangSrcMgr, - [](const SourceManagerRef &inArray, - const clang::SourceManager *toInsert) { - return std::less()(inArray.get(), toInsert); - }); - if (iter == sourceManagersWithDiagnostics.end() || - iter->get() != &clangSrcMgr) { - sourceManagersWithDiagnostics.insert(iter, &clangSrcMgr); - } - - return loc; -} - - void ClangDiagnosticConsumer::HandleDiagnostic( clang::DiagnosticsEngine::Level clangDiagLevel, const clang::Diagnostic &clangDiag) { @@ -195,13 +122,16 @@ void ClangDiagnosticConsumer::HandleDiagnostic( } const ASTContext &ctx = ImporterImpl.SwiftContext; + ClangSourceBufferImporter &bufferImporter = + ImporterImpl.getBufferImporterForDiagnostics(); if (clangDiag.getID() == clang::diag::err_module_not_built && CurrentImport && clangDiag.getArgStdStr(0) == CurrentImport->getName()) { SourceLoc loc = DiagLoc; - if (clangDiag.getLocation().isValid()) - loc = resolveSourceLocation(clangDiag.getSourceManager(), - clangDiag.getLocation()); + if (clangDiag.getLocation().isValid()) { + loc = bufferImporter.resolveSourceLocation(clangDiag.getSourceManager(), + clangDiag.getLocation()); + } ctx.Diags.diagnose(loc, diag::clang_cannot_build_module, ctx.LangOpts.EnableObjCInterop, @@ -216,9 +146,10 @@ void ClangDiagnosticConsumer::HandleDiagnostic( DiagnosticConsumer::HandleDiagnostic(clangDiagLevel, clangDiag); // FIXME: Map over source ranges in the diagnostic. - auto emitDiag = [&ctx, this](clang::FullSourceLoc clangNoteLoc, - clang::DiagnosticsEngine::Level clangDiagLevel, - StringRef message) { + auto emitDiag = + [&ctx, &bufferImporter](clang::FullSourceLoc clangNoteLoc, + clang::DiagnosticsEngine::Level clangDiagLevel, + StringRef message) { decltype(diag::error_from_clang) diagKind; switch (clangDiagLevel) { case clang::DiagnosticsEngine::Ignored: @@ -241,8 +172,8 @@ void ClangDiagnosticConsumer::HandleDiagnostic( SourceLoc noteLoc; if (clangNoteLoc.isValid()) - noteLoc = resolveSourceLocation(clangNoteLoc.getManager(), - clangNoteLoc); + noteLoc = bufferImporter.resolveSourceLocation(clangNoteLoc.getManager(), + clangNoteLoc); ctx.Diags.diagnose(noteLoc, diagKind, message); }; diff --git a/lib/ClangImporter/ClangDiagnosticConsumer.h b/lib/ClangImporter/ClangDiagnosticConsumer.h index a323294a1edf7..dd7244b4480d2 100644 --- a/lib/ClangImporter/ClangDiagnosticConsumer.h +++ b/lib/ClangImporter/ClangDiagnosticConsumer.h @@ -55,18 +55,6 @@ class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter { ClangImporter::Implementation &ImporterImpl; - /// Keeps alive the Clang source managers where diagnostics have been - /// reported. - /// - /// This is a bit of a hack, but LLVM's source manager (and by extension - /// Swift's) does not support buffers going away. - // - // This is not using SmallPtrSet or similar because we need the - // IntrusiveRefCntPtr to stay a ref-counting pointer. - SmallVector, 4> - sourceManagersWithDiagnostics; - llvm::DenseMap mirroredBuffers; - const clang::IdentifierInfo *CurrentImport = nullptr; SourceLoc DiagLoc; const bool DumpToStderr; @@ -82,12 +70,6 @@ class ClangDiagnosticConsumer : public clang::TextDiagnosticPrinter { return LoadModuleRAII(*this, name); } - /// Returns a Swift source location that points into a Clang buffer. - /// - /// This will keep the Clang buffer alive as long as this diagnostic consumer. - SourceLoc resolveSourceLocation(const clang::SourceManager &clangSrcMgr, - clang::SourceLocation clangLoc); - void HandleDiagnostic(clang::DiagnosticsEngine::Level diagLevel, const clang::Diagnostic &info) override; }; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 885ed98d34398..3bc3c4b5d1e3d 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -768,73 +768,58 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) { return false; // FIXME: The following attempts to do an initial ReadAST invocation to verify - // the PCH, without affecting the existing CompilerInstance. + // the PCH, without causing trouble for the existing CompilerInstance. // Look into combining creating the ASTReader along with verification + update // if necessary, so that we can create and use one ASTReader in the common case // when there is no need for update. + clang::CompilerInstance CI(Impl.Instance->getPCHContainerOperations(), + &Impl.Instance->getModuleCache()); + auto invocation = + std::make_shared(*Impl.Invocation); + invocation->getPreprocessorOpts().DisablePCHValidation = false; + invocation->getPreprocessorOpts().AllowPCHWithCompilerErrors = false; + invocation->getHeaderSearchOpts().ModulesValidateSystemHeaders = true; + invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; + invocation->getLangOpts()->CacheGeneratedPCH = true; + + // ClangImporter::create adds a remapped MemoryBuffer that we don't need + // here. Moreover, it's a raw pointer owned by the preprocessor options; if + // we don't clear the range then both the original and new CompilerInvocation + // will try to free it. + invocation->getPreprocessorOpts().RemappedFileBuffers.clear(); - CompilerInstance &CI = *Impl.Instance; - auto clangDiags = CompilerInstance::createDiagnostics( - new clang::DiagnosticOptions()); + CI.setInvocation(std::move(invocation)); + CI.setTarget(&Impl.Instance->getTarget()); + CI.setDiagnostics( + &*CompilerInstance::createDiagnostics(new clang::DiagnosticOptions())); // Note: Reusing the file manager is safe; this is a component that's already // reused when building PCM files for the module cache. - clang::SourceManager clangSrcMgr(*clangDiags, CI.getFileManager()); + CI.createSourceManager(Impl.Instance->getFileManager()); + auto &clangSrcMgr = CI.getSourceManager(); auto FID = clangSrcMgr.createFileID( llvm::make_unique(1, "
")); clangSrcMgr.setMainFileID(FID); - - // Note: Reusing the real HeaderSearch is dangerous, but it's not easy to - // copy. We put in some effort to reset it to the way it was below. - clang::HeaderSearch &headerSearchInfo = - CI.getPreprocessor().getHeaderSearchInfo(); - assert(headerSearchInfo.getExternalLookup() == nullptr && - "already configured an ASTReader"); - - // Note: Reusing the PCM cache is safe because that's already shared when - // building PCM files for the module cache. Using the top-level compiler - // instance as a module loader does seem a little dangerous but does not - // appear to cause problems at the moment. - clang::Preprocessor PP(CI.getInvocation().getPreprocessorOptsPtr(), - *clangDiags, - CI.getLangOpts(), - clangSrcMgr, - headerSearchInfo, - (clang::ModuleLoader &)CI, - /*IILookup=*/nullptr, - /*OwnsHeaderSearch=*/false); - PP.Initialize(CI.getTarget()); - clang::ASTContext ctx(CI.getLangOpts(), clangSrcMgr, - PP.getIdentifierTable(), PP.getSelectorTable(), - PP.getBuiltinInfo()); - - // Note: Reusing the PCHContainerReader or ModuleFileExtensions could be - // dangerous. - std::unique_ptr Reader(new clang::ASTReader( - PP, CI.getModuleCache(), &ctx, CI.getPCHContainerReader(), - CI.getFrontendOpts().ModuleFileExtensions, - CI.getHeaderSearchOpts().Sysroot, - /*DisableValidation*/ false, - /*AllowPCHWithCompilerErrors*/ false, - /*AllowConfigurationMismatch*/ false, - /*ValidateSystemInputs*/ true)); + auto &diagConsumer = CI.getDiagnosticClient(); + diagConsumer.BeginSourceFile(CI.getLangOpts()); SWIFT_DEFER { - assert(headerSearchInfo.getExternalLookup() == Reader.get() || - headerSearchInfo.getExternalLookup() == nullptr); - headerSearchInfo.SetExternalLookup(nullptr); - headerSearchInfo.SetExternalSource(nullptr); + diagConsumer.EndSourceFile(); }; - ctx.InitBuiltinTypes(CI.getTarget()); + + // Pass in TU_Complete, which is the default mode for the Preprocessor + // constructor and the right one for reading a PCH. + CI.createPreprocessor(clang::TU_Complete); + CI.createASTContext(); + CI.createModuleManager(); + clang::ASTReader &Reader = *CI.getModuleManager(); auto failureCapabilities = clang::ASTReader::ARR_Missing | clang::ASTReader::ARR_OutOfDate | clang::ASTReader::ARR_VersionMismatch; - auto result = Reader->ReadAST(PCHFilename, - clang::serialization::MK_PCH, - clang::SourceLocation(), - failureCapabilities); + auto result = Reader.ReadAST(PCHFilename, clang::serialization::MK_PCH, + clang::SourceLocation(), failureCapabilities); switch (result) { case clang::ASTReader::Success: return true; @@ -922,6 +907,7 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, invocationArgStrs.push_back("clang"); switch (importerOpts.Mode) { case ClangImporterOptions::Modes::Normal: + case ClangImporterOptions::Modes::PrecompiledModule: getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts); break; case ClangImporterOptions::Modes::EmbedBitcode: @@ -1000,6 +986,7 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, std::make_shared( importer->Impl.BridgingHeaderLookupTable, importer->Impl.LookupTables, importer->Impl.SwiftContext, + importer->Impl.getBufferImporterForDiagnostics(), importer->Impl.platformAvailability, importer->Impl.InferImportAsMember)); @@ -1081,7 +1068,17 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts, if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode) return importer; + // ClangImporter always sets this in Normal mode, so we need to make sure to + // set it before bailing out early when configuring ClangImporter for + // precompiled modules. This is not a benign langopt, so forgetting this (for + // example, if we combined the early exit below with the one above) would make + // the compiler instance used to emit PCMs incompatible with the one used to + // read them later. instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; + + if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule) + return importer; + bool canBegin = action->BeginSourceFile(instance, instance.getFrontendOpts().Inputs[0]); if (!canBegin) @@ -1330,7 +1327,8 @@ bool ClangImporter::Implementation::importHeader( } // Finalize the lookup table, which may fail. - finalizeLookupTable(*BridgingHeaderLookupTable, getNameImporter()); + finalizeLookupTable(*BridgingHeaderLookupTable, getNameImporter(), + getBufferImporterForDiagnostics()); // FIXME: What do we do if there was already an error? if (!hadError && clangDiags.hasErrorOccurred()) { @@ -1354,7 +1352,7 @@ bool ClangImporter::importHeader(StringRef header, ModuleDecl *adapter, // If we've made it to here, this is some header other than the bridging // header, which means we can no longer rely on one file's modification time - // to invalid code completion caches. :-( + // to invalidate code completion caches. :-( Impl.setSinglePCHImport(None); if (!cachedContents.empty() && cachedContents.back() == '\0') @@ -1464,43 +1462,82 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, return result; } -bool -ClangImporter::emitBridgingPCH(StringRef headerPath, - StringRef outputPCHPath) { - auto invocation = std::make_shared - (clang::CompilerInvocation(*Impl.Invocation)); - invocation->getFrontendOpts().DisableFree = false; - invocation->getFrontendOpts().Inputs.clear(); - invocation->getFrontendOpts().Inputs.push_back( - clang::FrontendInputFile(headerPath, clang::Language::ObjC)); - invocation->getFrontendOpts().OutputFile = outputPCHPath; - invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; - invocation->getPreprocessorOpts().resetNonModularOptions(); - invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; - invocation->getLangOpts()->CacheGeneratedPCH = true; +/// Returns the appropriate source input language based on language options. +static clang::Language getLanguageFromOptions( + const clang::LangOptions *LangOpts) { + if (LangOpts->OpenCL) + return clang::Language::OpenCL; + if (LangOpts->CUDA) + return clang::Language::CUDA; + if (LangOpts->ObjC) + return LangOpts->CPlusPlus ? + clang::Language::ObjCXX : clang::Language::ObjC; + return LangOpts->CPlusPlus ? clang::Language::CXX : clang::Language::C; +} + +/// Wraps the given frontend action in an index data recording action if the +/// frontend options have an index store path specified. +static +std::unique_ptr wrapActionForIndexingIfEnabled( + const clang::FrontendOptions &FrontendOpts, + std::unique_ptr action) { + if (!FrontendOpts.IndexStorePath.empty()) { + return clang::index::createIndexDataRecordingAction( + FrontendOpts, std::move(action)); + } + return action; +} + +std::unique_ptr +ClangImporter::cloneCompilerInstanceForPrecompiling() { + auto invocation = + std::make_shared(*Impl.Invocation); + + auto &PPOpts = invocation->getPreprocessorOpts(); + PPOpts.resetNonModularOptions(); + + auto &FrontendOpts = invocation->getFrontendOpts(); + FrontendOpts.DisableFree = false; + FrontendOpts.Inputs.clear(); - clang::CompilerInstance emitInstance( + auto clonedInstance = llvm::make_unique( Impl.Instance->getPCHContainerOperations(), &Impl.Instance->getModuleCache()); - emitInstance.setInvocation(std::move(invocation)); - emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(), - /*ShouldOwnClient=*/false); + clonedInstance->setInvocation(std::move(invocation)); + clonedInstance->createDiagnostics(&Impl.Instance->getDiagnosticClient(), + /*ShouldOwnClient=*/false); clang::FileManager &fileManager = Impl.Instance->getFileManager(); - emitInstance.setFileManager(&fileManager); - emitInstance.createSourceManager(fileManager); - emitInstance.setTarget(&Impl.Instance->getTarget()); + clonedInstance->setFileManager(&fileManager); + clonedInstance->createSourceManager(fileManager); + clonedInstance->setTarget(&Impl.Instance->getTarget()); - std::unique_ptr action; - action.reset(new clang::GeneratePCHAction()); - if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) { - action = clang::index:: - createIndexDataRecordingAction(emitInstance.getFrontendOpts(), - std::move(action)); - } - emitInstance.ExecuteAction(*action); + return clonedInstance; +} - if (emitInstance.getDiagnostics().hasErrorOccurred()) { +bool +ClangImporter::emitBridgingPCH(StringRef headerPath, + StringRef outputPCHPath) { + auto emitInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = emitInstance->getInvocation(); + + auto LangOpts = invocation.getLangOpts(); + LangOpts->NeededByPCHOrCompilationUsesPCH = true; + LangOpts->CacheGeneratedPCH = true; + + auto language = getLanguageFromOptions(LangOpts); + auto inputFile = clang::FrontendInputFile(headerPath, language); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OutputFile = outputPCHPath; + FrontendOpts.ProgramAction = clang::frontend::GeneratePCH; + + auto action = wrapActionForIndexingIfEnabled( + FrontendOpts, llvm::make_unique()); + emitInstance->ExecuteAction(*action); + + if (emitInstance->getDiagnostics().hasErrorOccurred()) { Impl.SwiftContext.Diags.diagnose({}, diag::bridging_header_pch_error, outputPCHPath, headerPath); @@ -1509,6 +1546,65 @@ ClangImporter::emitBridgingPCH(StringRef headerPath, return false; } +bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath, + StringRef moduleName, + StringRef outputPath) { + auto emitInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = emitInstance->getInvocation(); + + auto LangOpts = invocation.getLangOpts(); + LangOpts->setCompilingModule(clang::LangOptions::CMK_ModuleMap); + LangOpts->ModuleName = moduleName; + LangOpts->CurrentModule = LangOpts->ModuleName; + + auto language = getLanguageFromOptions(LangOpts); + auto inputFile = clang::FrontendInputFile( + moduleMapPath, clang::InputKind( + language, clang::InputKind::ModuleMap, false)); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OriginalModuleMap = moduleMapPath; + FrontendOpts.OutputFile = outputPath; + FrontendOpts.ProgramAction = clang::frontend::GenerateModule; + + auto action = wrapActionForIndexingIfEnabled( + FrontendOpts, + llvm::make_unique()); + emitInstance->ExecuteAction(*action); + + if (emitInstance->getDiagnostics().hasErrorOccurred()) { + Impl.SwiftContext.Diags.diagnose({}, + diag::emit_pcm_error, + outputPath, moduleMapPath); + return true; + } + return false; +} + +bool ClangImporter::dumpPrecompiledModule(StringRef modulePath, + StringRef outputPath) { + auto dumpInstance = cloneCompilerInstanceForPrecompiling(); + auto &invocation = dumpInstance->getInvocation(); + + auto inputFile = clang::FrontendInputFile( + modulePath, clang::InputKind( + clang::Language::Unknown, clang::InputKind::Precompiled, false)); + + auto &FrontendOpts = invocation.getFrontendOpts(); + FrontendOpts.Inputs = {inputFile}; + FrontendOpts.OutputFile = outputPath; + + auto action = llvm::make_unique(); + dumpInstance->ExecuteAction(*action); + + if (dumpInstance->getDiagnostics().hasErrorOccurred()) { + Impl.SwiftContext.Diags.diagnose({}, diag::dump_pcm_error, modulePath); + return true; + } + return false; +} + void ClangImporter::collectVisibleTopLevelModuleNames( SmallVectorImpl &names) const { SmallVector Modules; @@ -1523,19 +1619,19 @@ void ClangImporter::collectVisibleTopLevelModuleNames( } void ClangImporter::collectSubModuleNames( - ArrayRef> path, + ArrayRef> path, std::vector &names) const { auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return; clang::Module *submodule = clangModule; for (auto component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.Item.str()); if (!submodule) return; } @@ -1547,12 +1643,12 @@ bool ClangImporter::isModuleImported(const clang::Module *M) { return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; } -bool ClangImporter::canImportModule(std::pair moduleID) { +bool ClangImporter::canImportModule(Located moduleID) { // Look up the top-level module to see if it exists. // FIXME: This only works with top-level modules. auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo(); clang::Module *clangModule = - clangHeaderSearch.lookupModule(moduleID.first.str(), /*AllowSearch=*/true, + clangHeaderSearch.lookupModule(moduleID.Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) { return false; @@ -1566,13 +1662,13 @@ bool ClangImporter::canImportModule(std::pair moduleID) { } ModuleDecl *ClangImporter::Implementation::loadModuleClang( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { auto &clangContext = getClangASTContext(); auto &clangHeaderSearch = getClangPreprocessor().getHeaderSearchInfo(); // Look up the top-level module first, to see if it exists at all. clang::Module *clangModule = clangHeaderSearch.lookupModule( - path.front().first.str(), /*AllowSearch=*/true, + path.front().Item.str(), /*AllowSearch=*/true, /*AllowExtraModuleMapSearch=*/true); if (!clangModule) return nullptr; @@ -1581,8 +1677,8 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( SmallVector, 4> clangPath; for (auto component : path) { - clangPath.push_back({&clangContext.Idents.get(component.first.str()), - exportSourceLoc(component.second)}); + clangPath.push_back({&clangContext.Idents.get(component.Item.str()), + exportSourceLoc(component.Loc)}); } auto &rawDiagClient = Instance->getDiagnosticClient(); @@ -1632,13 +1728,13 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( // Verify that the submodule exists. clang::Module *submodule = clangModule; for (auto &component : path.slice(1)) { - submodule = submodule->findSubmodule(component.first.str()); + submodule = submodule->findSubmodule(component.Item.str()); // Special case: a submodule named "Foo.Private" can be moved to a top-level // module named "Foo_Private". Clang has special support for this. // We're limiting this to just submodules named "Private" because this will // put the Clang AST in a fatal error state if it /doesn't/ exist. - if (!submodule && component.first.str() == "Private" && + if (!submodule && component.Item.str() == "Private" && (&component) == (&path[1])) { submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false); } @@ -1657,18 +1753,24 @@ ModuleDecl *ClangImporter::Implementation::loadModuleClang( return finishLoadingClangModule(clangModule, /*preferOverlay=*/false); } -ModuleDecl *ClangImporter::loadModule( - SourceLoc importLoc, - ArrayRef> path) { - ModuleDecl *MD = Impl.loadModuleClang(importLoc, path); +ModuleDecl * +ClangImporter::loadModule(SourceLoc importLoc, + ArrayRef> path) { + return Impl.loadModule(importLoc, path); +} + +ModuleDecl *ClangImporter::Implementation::loadModule( + SourceLoc importLoc, ArrayRef> path) { + ModuleDecl *MD = nullptr; + if (!DisableSourceImport) + MD = loadModuleClang(importLoc, path); if (!MD) - MD = Impl.loadModuleDWARF(importLoc, path); + MD = loadModuleDWARF(importLoc, path); return MD; } ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule( - const clang::Module *clangModule, - bool findOverlay) { + const clang::Module *clangModule, bool findOverlay) { assert(clangModule); // Bump the generation count. @@ -1757,6 +1859,8 @@ PlatformAvailability::PlatformAvailability(LangOptions &langOpts) switch (platformKind) { case PlatformKind::iOS: case PlatformKind::iOSApplicationExtension: + case PlatformKind::macCatalyst: + case PlatformKind::macCatalystApplicationExtension: case PlatformKind::tvOS: case PlatformKind::tvOSApplicationExtension: deprecatedAsUnavailableMessage = @@ -1791,6 +1895,11 @@ bool PlatformAvailability::isPlatformRelevant(StringRef name) const { case PlatformKind::iOSApplicationExtension: return name == "ios" || name == "ios_app_extension"; + case PlatformKind::macCatalyst: + case PlatformKind::macCatalystApplicationExtension: + // ClangImporter does not yet support macCatalyst. + return false; + case PlatformKind::tvOS: return name == "tvos"; case PlatformKind::tvOSApplicationExtension: @@ -1831,6 +1940,11 @@ bool PlatformAvailability::treatDeprecatedAsUnavailable( // Anything deprecated in iOS 7.x and earlier is unavailable in Swift. return major <= 7; + case PlatformKind::macCatalyst: + case PlatformKind::macCatalystApplicationExtension: + // ClangImporter does not yet support macCatalyst. + return false; + case PlatformKind::watchOS: case PlatformKind::watchOSApplicationExtension: // No deprecation filter on watchOS @@ -1852,7 +1966,9 @@ ClangImporter::Implementation::Implementation( IsReadingBridgingPCH(false), CurrentVersion(ImportNameVersion::fromOptions(ctx.LangOpts)), BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)), + BuffersForDiagnostics(ctx.SourceMgr), platformAvailability(ctx.LangOpts), nameImporter(), + DisableSourceImport(opts.DisableSourceImport), DWARFImporter(dwarfImporterDelegate) {} ClangImporter::Implementation::~Implementation() { @@ -2512,7 +2628,8 @@ void ClangImporter::lookupTypeDecl( clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(), lookupKind); bool foundViaClang = false; - if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { + if (!Impl.DisableSourceImport && + sema.LookupName(lookupResult, /*Scope=*/nullptr)) { for (auto clangDecl : lookupResult) { if (!isa(clangDecl) && !isa(clangDecl) && @@ -2681,7 +2798,7 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx, ArrayRef Exported) { auto *ImportedMod = ClangN.getClangModule(); assert(ImportedMod); - SmallVector, 4> AccessPath; + SmallVector, 4> AccessPath; auto *TmpMod = ImportedMod; while (TmpMod) { AccessPath.push_back({ Ctx.getIdentifier(TmpMod->Name), SourceLoc() }); @@ -2750,22 +2867,23 @@ void ClangModuleUnit::lookupValue(DeclName name, NLKind lookupKind, } } -/// Determine whether the given Clang entry is visible. -/// -/// FIXME: this is an elaborate hack to badly reflect Clang's -/// submodule visibility into Swift. -static bool isVisibleClangEntry(clang::ASTContext &ctx, - SwiftLookupTable::SingleEntry entry) { - if (auto clangDecl = entry.dyn_cast()) { - // For a declaration, check whether the declaration is hidden. - if (!clangDecl->isHidden()) return true; +bool ClangImporter::Implementation::isVisibleClangEntry( + const clang::NamedDecl *clangDecl) { + // For a declaration, check whether the declaration is hidden. + if (!clangDecl->isHidden()) return true; - // Is any redeclaration visible? - for (auto redecl : clangDecl->redecls()) { - if (!cast(redecl)->isHidden()) return true; - } + // Is any redeclaration visible? + for (auto redecl : clangDecl->redecls()) { + if (!cast(redecl)->isHidden()) return true; + } - return false; + return false; +} + +bool ClangImporter::Implementation::isVisibleClangEntry( + SwiftLookupTable::SingleEntry entry) { + if (auto clangDecl = entry.dyn_cast()) { + return isVisibleClangEntry(clangDecl); } // If it's a macro from a module, check whether the module has been imported. @@ -2800,22 +2918,19 @@ ClangModuleUnit::lookupNestedType(Identifier name, if (!baseTypeContext) return nullptr; - auto &clangCtx = owner.getClangASTContext(); - // FIXME: This is very similar to what's in Implementation::lookupValue and // Implementation::loadAllMembers. SmallVector results; for (auto entry : lookupTable->lookup(SerializedSwiftName(name.str()), baseTypeContext)) { // If the entry is not visible, skip it. - if (!isVisibleClangEntry(clangCtx, entry)) continue; + if (!owner.isVisibleClangEntry(entry)) continue; - auto clangDecl = entry.dyn_cast(); - auto clangTypeDecl = dyn_cast_or_null(clangDecl); - if (!clangTypeDecl) + auto *clangDecl = entry.dyn_cast(); + if (!clangDecl) continue; - clangTypeDecl = cast(clangTypeDecl->getMostRecentDecl()); + const auto *clangTypeDecl = clangDecl->getMostRecentDecl(); bool anyMatching = false; TypeDecl *originalDecl = nullptr; @@ -2867,24 +2982,23 @@ void ClangImporter::loadExtensions(NominalTypeDecl *nominal, // For an Objective-C class, import all of the visible categories. if (auto objcClass = dyn_cast_or_null( effectiveClangContext.getAsDeclContext())) { + SmallVector DelayedCategories; + // Simply importing the categories adds them to the list of extensions. - for (auto I = objcClass->visible_categories_begin(), - E = objcClass->visible_categories_end(); - I != E; ++I) { - Impl.importDeclReal(*I, Impl.CurrentVersion); + for (const auto *Cat : objcClass->visible_categories()) { + Impl.importDeclReal(Cat, Impl.CurrentVersion); } } // Dig through each of the Swift lookup tables, creating extensions // where needed. - auto &clangCtx = Impl.getClangASTContext(); (void)Impl.forEachLookupTable([&](SwiftLookupTable &table) -> bool { // FIXME: If we already looked at this for this generation, // skip. - for (auto entry : table.lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table.allGlobalsAsMembersInContext(effectiveClangContext)) { // If the entry is not visible, skip it. - if (!isVisibleClangEntry(clangCtx, entry)) continue; + if (!Impl.isVisibleClangEntry(entry)) continue; if (auto decl = entry.dyn_cast()) { // Import the context of this declaration, which has the @@ -3147,6 +3261,29 @@ void ClangImporter::verifyAllModules() { #endif } +const clang::Type * +ClangImporter::parseClangFunctionType(StringRef typeStr, + SourceLoc loc) const { + auto &sema = Impl.getClangSema(); + StringRef filename = Impl.SwiftContext.SourceMgr.getDisplayNameForLoc(loc); + // TODO: Obtain a clang::SourceLocation from the swift::SourceLoc we have + auto parsedType = sema.ParseTypeFromStringCallback(typeStr, filename, {}); + if (!parsedType.isUsable()) + return nullptr; + clang::QualType resultType = clang::Sema::GetTypeFromParser(parsedType.get()); + auto *typePtr = resultType.getTypePtrOrNull(); + if (typePtr && (typePtr->isFunctionPointerType() + || typePtr->isBlockPointerType())) + return typePtr; + return nullptr; +} + +void ClangImporter::printClangType(const clang::Type *type, + llvm::raw_ostream &os) const { + auto policy = clang::PrintingPolicy(getClangASTContext().getLangOpts()); + clang::QualType(type, 0).print(os, policy); +} + //===----------------------------------------------------------------------===// // ClangModule Implementation //===----------------------------------------------------------------------===// @@ -3442,7 +3579,7 @@ void ClangImporter::Implementation::lookupValue( for (auto entry : table.lookup(name.getBaseName(), clangTU)) { // If the entry is not visible, skip it. - if (!isVisibleClangEntry(clangCtx, entry)) continue; + if (!isVisibleClangEntry(entry)) continue; ValueDecl *decl; // If it's a Clang declaration, try to import it. @@ -3546,11 +3683,9 @@ void ClangImporter::Implementation::lookupObjCMembers( SwiftLookupTable &table, DeclName name, VisibleDeclConsumer &consumer) { - auto &clangCtx = getClangASTContext(); - for (auto clangDecl : table.lookupObjCMembers(name.getBaseName())) { // If the entry is not visible, skip it. - if (!isVisibleClangEntry(clangCtx, clangDecl)) continue; + if (!isVisibleClangEntry(clangDecl)) continue; forEachDistinctName(clangDecl, [&](ImportedName importedName, @@ -3595,6 +3730,27 @@ void ClangImporter::Implementation::lookupAllObjCMembers( } } +// Force the members of the entire inheritance hierarchy to be loaded and +// deserialized before loading the named member of this class. This allows the +// decl members table to be warmed up and enables the correct identification of +// overrides. +// +// FIXME: Very low hanging fruit: Loading everything is extremely wasteful. We +// should be able to just load the name lazy member loading is asking for. +static void ensureSuperclassMembersAreLoaded(const ClassDecl *CD) { + if (!CD) + return; + + CD = CD->getSuperclassDecl(); + if (!CD || !CD->hasClangNode()) + return; + + CD->loadAllMembers(); + + for (auto *ED : const_cast(CD)->getExtensions()) + ED->loadAllMembers(); +} + Optional> ClangImporter::Implementation::loadNamedMembers( const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) { @@ -3620,17 +3776,6 @@ ClangImporter::Implementation::loadNamedMembers( return None; } - // Also bail out if there are any global-as-member mappings for this context; - // we can support some of them lazily but the full set of idioms seems - // prohibitively complex (also they're not stored in by-name lookup, for - // reasons unclear). - if (isa(D) && !checkedGlobalsAsMembers.insert(IDC).second) { - if (forEachLookupTable([&](SwiftLookupTable &table) -> bool { - return (!table.lookupGlobalsAsMembers(effectiveClangContext).empty()); - })) - return None; - } - // There are 3 cases: // // - The decl is from a bridging header, CMO is Some(nullptr) @@ -3652,16 +3797,16 @@ ClangImporter::Implementation::loadNamedMembers( auto table = findLookupTable(*CMO); assert(table && "clang module without lookup table"); - clang::ASTContext &clangCtx = getClangASTContext(); - assert(isa(CD) || isa(CD)); + ensureSuperclassMembersAreLoaded(dyn_cast(D)); + TinyPtrVector Members; for (auto entry : table->lookup(SerializedSwiftName(N), effectiveClangContext)) { if (!entry.is()) continue; auto member = entry.get(); - if (!isVisibleClangEntry(clangCtx, member)) continue; + if (!isVisibleClangEntry(member)) continue; // Skip Decls from different clang::DeclContexts if (member->getDeclContext() != CDC) continue; @@ -3677,6 +3822,37 @@ ClangImporter::Implementation::loadNamedMembers( } } } + + for (auto entry : table->lookupGlobalsAsMembers(SerializedSwiftName(N), + effectiveClangContext)) { + if (!entry.is()) continue; + auto member = entry.get(); + if (!isVisibleClangEntry(member)) continue; + + // Skip Decls from different clang::DeclContexts + if (member->getDeclContext() != CDC) continue; + + SmallVector tmp; + insertMembersAndAlternates(member, tmp); + for (auto *TD : tmp) { + if (auto *V = dyn_cast(TD)) { + // Skip ValueDecls if they import under different names. + if (V->getBaseName() == N) { + Members.push_back(V); + } + } + } + } + + if (N == DeclBaseName::createConstructor()) { + if (auto *classDecl = dyn_cast(D)) { + SmallVector ctors; + importInheritedConstructors(cast(CD), + classDecl, ctors); + for (auto ctor : ctors) + Members.push_back(cast(ctor)); + } + } return Members; } @@ -3702,8 +3878,16 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext( (nominal->getAttrs().hasAttribute() || (!nominal->getParentSourceFile() && nominal->isObjC()))) { // Map the name. If we can't represent the Swift name in Clang. - // FIXME: We should be using the Objective-C name here! - auto clangName = exportName(nominal->getName()); + Identifier name = nominal->getName(); + if (auto objcAttr = nominal->getAttrs().getAttribute()) { + if (auto objcName = objcAttr->getName()) { + if (objcName->getNumArgs() == 0) { + // This is an error if not 0, but it should be caught later. + name = objcName->getSimpleName(); + } + } + } + auto clangName = exportName(name); if (!clangName) return EffectiveClangContext(); @@ -3721,6 +3905,28 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext( /// FIXME: Other type declarations should also be okay? } } + + // For source compatibility reasons, fall back to the Swift name. + // + // This is how people worked around not being able to import-as-member onto + // Swift types by their ObjC name before the above code to handle ObjCAttr + // was added. + if (name != nominal->getName()) + clangName = exportName(nominal->getName()); + + lookupResult.clear(); + lookupResult.setLookupName(clangName); + // FIXME: This loop is duplicated from above, but doesn't obviously factor + // out in a nice way. + if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) { + // FIXME: Filter based on access path? C++ access control? + for (auto clangDecl : lookupResult) { + if (auto objcClass = dyn_cast(clangDecl)) + return EffectiveClangContext(objcClass); + + /// FIXME: Other type declarations should also be okay? + } + } } return EffectiveClangContext(); @@ -3742,11 +3948,11 @@ void ClangImporter::Implementation::dumpSwiftLookupTables() { for (auto moduleName : moduleNames) { llvm::errs() << "<<" << moduleName << " lookup table>>\n"; LookupTables[moduleName]->deserializeAll(); - LookupTables[moduleName]->dump(); + LookupTables[moduleName]->dump(llvm::errs()); } llvm::errs() << "<>\n"; - BridgingHeaderLookupTable->dump(); + BridgingHeaderLookupTable->dump(llvm::errs()); } DeclName ClangImporter:: diff --git a/lib/ClangImporter/ClangSourceBufferImporter.cpp b/lib/ClangImporter/ClangSourceBufferImporter.cpp new file mode 100644 index 0000000000000..fa4df253d7a0f --- /dev/null +++ b/lib/ClangImporter/ClangSourceBufferImporter.cpp @@ -0,0 +1,91 @@ +//===--- ClangSourceBufferImporter.cpp - Map Clang buffers to Swift -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ClangSourceBufferImporter.h" +#include "swift/Basic/SourceManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace swift; +using namespace swift::importer; + +static SourceLoc findEndOfLine(SourceManager &SM, SourceLoc loc, + unsigned bufferID) { + CharSourceRange entireBuffer = SM.getRangeForBuffer(bufferID); + CharSourceRange rangeFromLoc{SM, loc, entireBuffer.getEnd()}; + StringRef textFromLoc = SM.extractText(rangeFromLoc); + size_t newlineOffset = textFromLoc.find_first_of({"\r\n\0", 3}); + if (newlineOffset == StringRef::npos) + return entireBuffer.getEnd(); + return loc.getAdvancedLoc(newlineOffset); +} + +SourceLoc ClangSourceBufferImporter::resolveSourceLocation( + const clang::SourceManager &clangSrcMgr, + clang::SourceLocation clangLoc) { + SourceLoc loc; + + clangLoc = clangSrcMgr.getFileLoc(clangLoc); + auto decomposedLoc = clangSrcMgr.getDecomposedLoc(clangLoc); + if (decomposedLoc.first.isInvalid()) + return loc; + + auto buffer = clangSrcMgr.getBuffer(decomposedLoc.first); + unsigned mirrorID; + + auto mirrorIter = mirroredBuffers.find(buffer); + if (mirrorIter != mirroredBuffers.end()) { + mirrorID = mirrorIter->second; + } else { + std::unique_ptr mirrorBuffer{ + llvm::MemoryBuffer::getMemBuffer(buffer->getBuffer(), + buffer->getBufferIdentifier(), + /*RequiresNullTerminator=*/true) + }; + mirrorID = swiftSourceManager.addNewSourceBuffer(std::move(mirrorBuffer)); + mirroredBuffers[buffer] = mirrorID; + } + loc = swiftSourceManager.getLocForOffset(mirrorID, decomposedLoc.second); + + auto presumedLoc = clangSrcMgr.getPresumedLoc(clangLoc); + if (!presumedLoc.getFilename()) + return loc; + if (presumedLoc.getLine() == 0) + return SourceLoc(); + + unsigned bufferLineNumber = + clangSrcMgr.getLineNumber(decomposedLoc.first, decomposedLoc.second); + + StringRef presumedFile = presumedLoc.getFilename(); + SourceLoc startOfLine = loc.getAdvancedLoc(-presumedLoc.getColumn() + 1); + bool isNewVirtualFile = swiftSourceManager.openVirtualFile( + startOfLine, presumedFile, presumedLoc.getLine() - bufferLineNumber); + if (isNewVirtualFile) { + SourceLoc endOfLine = findEndOfLine(swiftSourceManager, loc, mirrorID); + swiftSourceManager.closeVirtualFile(endOfLine); + } + + using SourceManagerRef = llvm::IntrusiveRefCntPtr; + auto iter = std::lower_bound(sourceManagersWithDiagnostics.begin(), + sourceManagersWithDiagnostics.end(), + &clangSrcMgr, + [](const SourceManagerRef &inArray, + const clang::SourceManager *toInsert) { + return std::less()(inArray.get(), toInsert); + }); + if (iter == sourceManagersWithDiagnostics.end() || + iter->get() != &clangSrcMgr) { + sourceManagersWithDiagnostics.insert(iter, &clangSrcMgr); + } + + return loc; +} diff --git a/lib/ClangImporter/ClangSourceBufferImporter.h b/lib/ClangImporter/ClangSourceBufferImporter.h new file mode 100644 index 0000000000000..7feb0216e6855 --- /dev/null +++ b/lib/ClangImporter/ClangSourceBufferImporter.h @@ -0,0 +1,64 @@ +//===--- ClangSourceBufferImporter.h - Map Clang buffers over ---*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANGIMPORTER_CLANGSOURCEBUFFERIMPORTER_H +#define SWIFT_CLANGIMPORTER_CLANGSOURCEBUFFERIMPORTER_H + +#include "swift/Basic/LLVM.h" +#include "swift/Basic/SourceLoc.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class MemoryBuffer; +} + +namespace clang { +class SourceManager; +} + +namespace swift { +class SourceManager; + +namespace importer { + +/// A helper class used to keep alive the Clang source managers where +/// diagnostics have been reported. +/// +/// This is a bit of a hack, but LLVM's source manager (and by extension +/// Swift's) does not support buffers going away, so if we want to report +/// diagnostics in them we have to do it this way. +class ClangSourceBufferImporter { + // This is not using SmallPtrSet or similar because we need the + // IntrusiveRefCntPtr to stay a ref-counting pointer. + SmallVector, 4> + sourceManagersWithDiagnostics; + llvm::DenseMap mirroredBuffers; + SourceManager &swiftSourceManager; + +public: + explicit ClangSourceBufferImporter(SourceManager &sourceMgr) + : swiftSourceManager(sourceMgr) {} + + /// Returns a Swift source location that points into a Clang buffer. + /// + /// This will keep the Clang buffer alive as long as this object. + SourceLoc resolveSourceLocation(const clang::SourceManager &clangSrcMgr, + clang::SourceLocation clangLoc); +}; + +} // end namespace importer +} // end namespace swift + +#endif diff --git a/lib/ClangImporter/DWARFImporter.cpp b/lib/ClangImporter/DWARFImporter.cpp index 654fd287d9ce4..d044355cb0d10 100644 --- a/lib/ClangImporter/DWARFImporter.cpp +++ b/lib/ClangImporter/DWARFImporter.cpp @@ -99,13 +99,13 @@ static_assert(IsTriviallyDestructible::value, "DWARFModuleUnits are BumpPtrAllocated; the d'tor is not called"); ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( - SourceLoc importLoc, ArrayRef> path) { + SourceLoc importLoc, ArrayRef> path) { // There's no importing from debug info if no importer is installed. if (!DWARFImporter) return nullptr; // FIXME: Implement submodule support! - Identifier name = path[0].first; + Identifier name = path[0].Item; auto it = DWARFModuleUnits.find(name); if (it != DWARFModuleUnits.end()) return it->second->getParentModule(); @@ -128,6 +128,17 @@ ModuleDecl *ClangImporter::Implementation::loadModuleDWARF( return decl; } +// This function exists to defeat the lazy member importing mechanism. The +// DWARFImporter is not capable of loading individual members, so it cannot +// benefit from this optimization yet anyhow. Besides, if you're importing a +// type here, you more than likely want to dump it and its fields. Loading all +// members populates lookup tables in the Clang Importer and ensures the +// absence of cache-fill-related side effects. +static void forceLoadAllMembers(IterableDeclContext *IDC) { + if (!IDC) return; + IDC->loadAllMembers(); +} + void ClangImporter::Implementation::lookupValueDWARF( DeclName name, NLKind lookupKind, Identifier inModule, SmallVectorImpl &results) { @@ -150,8 +161,10 @@ void ClangImporter::Implementation::lookupValueDWARF( continue; if (swiftDecl->getFullName().matchesRef(name) && - swiftDecl->getDeclContext()->isModuleScopeContext()) + swiftDecl->getDeclContext()->isModuleScopeContext()) { + forceLoadAllMembers(dyn_cast(swiftDecl)); results.push_back(swiftDecl); + } } } @@ -174,8 +187,10 @@ void ClangImporter::Implementation::lookupTypeDeclDWARF( Decl *importedDecl = cast_or_null( importDeclReal(namedDecl->getMostRecentDecl(), CurrentVersion)); - if (auto *importedType = dyn_cast_or_null(importedDecl)) + if (auto *importedType = dyn_cast_or_null(importedDecl)) { + forceLoadAllMembers(dyn_cast(importedType)); receiver(importedType); + } } } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index ef2271dbca11b..505eeb8e29c8c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -104,7 +104,7 @@ getDefaultMakeStructRawValuedOptions() { return opts; } -static bool isInSystemModule(DeclContext *D) { +static bool isInSystemModule(const DeclContext *D) { return cast(D->getModuleScopeContext())->isSystemModule(); } @@ -540,8 +540,6 @@ makeEnumRawValueConstructor(ClangImporter::Implementation &Impl, /*GenericParams=*/nullptr, enumDecl); ctorDecl->setImplicit(); ctorDecl->setAccess(AccessLevel::Public); - - ctorDecl->computeType(); ctorDecl->setBodySynthesizer(synthesizeEnumRawValueConstructorBody, enumDecl); return ctorDecl; } @@ -617,8 +615,6 @@ static void makeEnumRawValueGetter(ClangImporter::Implementation &Impl, getterDecl->setIsDynamic(false); getterDecl->setIsTransparent(false); - getterDecl->computeType(); - getterDecl->setAccess(AccessLevel::Public); getterDecl->setBodySynthesizer(synthesizeEnumRawValueGetterBody, enumDecl); makeComputed(rawValueDecl, getterDecl, nullptr); @@ -698,8 +694,6 @@ static AccessorDecl *makeStructRawValueGetter( getterDecl->setIsDynamic(false); getterDecl->setIsTransparent(false); - getterDecl->computeType(); - getterDecl->setAccess(AccessLevel::Public); getterDecl->setBodySynthesizer(synthesizeStructRawValueGetterBody, storedVar); return getterDecl; @@ -729,8 +723,6 @@ static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl, getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); - getterDecl->computeType(); - return getterDecl; } @@ -765,8 +757,6 @@ static AccessorDecl *makeFieldSetterDecl(ClangImporter::Implementation &Impl, setterDecl->setSelfAccessKind(SelfAccessKind::Mutating); setterDecl->setAccess(AccessLevel::Public); - setterDecl->computeType(); - return setterDecl; } @@ -777,8 +767,9 @@ static VarDecl *findAnonymousInnerFieldDecl(VarDecl *importedFieldDecl, auto anonymousFieldTypeDecl = anonymousFieldType->getStructOrBoundGenericStruct(); + const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; for (auto decl : anonymousFieldTypeDecl->lookupDirect( - importedFieldDecl->getName())) { + importedFieldDecl->getName(), flags)) { if (isa(decl)) { return cast(decl); } @@ -1308,9 +1299,6 @@ createDefaultConstructor(ClangImporter::Implementation &Impl, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), emptyPL, /*GenericParams=*/nullptr, structDecl); - // Set the constructor's type. - constructor->computeType(); - constructor->setAccess(AccessLevel::Public); // Mark the constructor transparent so that we inline it away completely. @@ -1424,6 +1412,10 @@ createValueConstructor(ClangImporter::Implementation &Impl, param->setSpecifier(ParamSpecifier::Default); param->setInterfaceType(var->getInterfaceType()); Impl.recordImplicitUnwrapForDecl(param, var->isImplicitlyUnwrappedOptional()); + + // Don't allow the parameter to accept temporary pointer conversions. + param->setNonEphemeralIfPossible(); + valueParameters.push_back(param); } @@ -1437,9 +1429,6 @@ createValueConstructor(ClangImporter::Implementation &Impl, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), paramList, /*GenericParams=*/nullptr, structDecl); - // Set the constructor's type. - constructor->computeType(); - constructor->setAccess(AccessLevel::Public); // Make the constructor transparent so we inline it away completely. @@ -1714,8 +1703,6 @@ buildSubscriptGetterDecl(ClangImporter::Implementation &Impl, TypeLoc::withoutLoc(elementTy), dc, getter->getClangNode()); - thunk->computeType(); - thunk->setAccess(getOverridableAccessLevel(dc)); if (auto objcAttr = getter->getAttrs().getAttribute()) @@ -1767,7 +1754,6 @@ buildSubscriptSetterDecl(ClangImporter::Implementation &Impl, valueIndicesPL, TypeLoc::withoutLoc(TupleType::getEmpty(C)), dc, setter->getClangNode()); - thunk->computeType(); thunk->setAccess(getOverridableAccessLevel(dc)); @@ -1952,7 +1938,6 @@ static bool addErrorDomain(NominalTypeDecl *swiftDecl, /*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(stringTy), swiftDecl); - getterDecl->computeType(); getterDecl->setIsObjC(false); getterDecl->setIsDynamic(false); getterDecl->setIsTransparent(false); @@ -2110,7 +2095,6 @@ applyPropertyOwnership(VarDecl *prop, if (attrs & clang::ObjCPropertyDecl::OBJC_PR_weak) { prop->getAttrs().add(new (ctx) ReferenceOwnershipAttr(ReferenceOwnership::Weak)); - prop->setType(WeakStorageType::get(prop->getType(), ctx)); prop->setInterfaceType(WeakStorageType::get( prop->getInterfaceType(), ctx)); return; @@ -2119,7 +2103,6 @@ applyPropertyOwnership(VarDecl *prop, (attrs & clang::ObjCPropertyDecl::OBJC_PR_unsafe_unretained)) { prop->getAttrs().add( new (ctx) ReferenceOwnershipAttr(ReferenceOwnership::Unmanaged)); - prop->setType(UnmanagedStorageType::get(prop->getType(), ctx)); prop->setInterfaceType(UnmanagedStorageType::get( prop->getInterfaceType(), ctx)); return; @@ -2167,6 +2150,64 @@ namespace { } }; + /// Search the member tables for this class and its superclasses and try to + /// identify the nearest VarDecl that serves as a base for an override. We + /// have to do this ourselves because Objective-C has no semantic notion of + /// overrides, and freely allows users to refine the type of any member + /// property in a derived class. + /// + /// The override must be the nearest possible one so there are not breaks + /// in the override chain. That is, suppose C refines B refines A and each + /// successively redeclares a member with a different type. It should be + /// the case that the nearest override from C is B and from B is A. If the + /// override point from C were A, then B would record an override on A as + /// well and we would introduce a semantic ambiguity. + /// + /// There is also a special case for finding a method that stomps over a + /// getter. If this is the case and no override point is identified, we will + /// not import the property to force users to explicitly call the method. + static std::pair + identifyNearestOverriddenDecl(ClangImporter::Implementation &Impl, + DeclContext *dc, + const clang::ObjCPropertyDecl *decl, + Identifier name, + ClassDecl *subject) { + bool foundMethod = false; + for (; subject; (subject = subject->getSuperclassDecl())) { + llvm::SmallVector lookup; + auto foundNames = Impl.MembersForNominal.find(subject); + if (foundNames != Impl.MembersForNominal.end()) { + auto foundDecls = foundNames->second.find(name); + if (foundDecls != foundNames->second.end()) { + lookup.append(foundDecls->second.begin(), foundDecls->second.end()); + } + } + + for (auto *&result : lookup) { + if (auto *fd = dyn_cast(result)) { + if (fd->isInstanceMember() != decl->isInstanceProperty()) + continue; + + assert(fd->getFullName().getArgumentNames().empty()); + foundMethod = true; + } else { + auto *var = cast(result); + if (var->isInstanceMember() != decl->isInstanceProperty()) + continue; + + // If the selectors of the getter match in Objective-C, we have an + // override. + if (var->getObjCGetterSelector() == + Impl.importSelector(decl->getGetterName())) { + return {var, foundMethod}; + } + } + } + } + + return {nullptr, foundMethod}; + } + /// Convert Clang declarations into the corresponding Swift /// declarations. class SwiftDeclConverter @@ -2192,6 +2233,13 @@ namespace { return getVersion() == getActiveSwiftVersion(); } + void recordMemberInContext(DeclContext *dc, ValueDecl *member) { + assert(member && "Attempted to record null member!"); + auto *nominal = dc->getSelfNominalTypeDecl(); + auto name = member->getBaseName(); + Impl.MembersForNominal[nominal][name].push_back(member); + } + /// Import the name of the given entity. /// /// This version of importFullName introduces any context-specific @@ -2728,7 +2776,6 @@ namespace { auto Loc = Impl.importSourceLoc(decl->getLocation()); auto structDecl = Impl.createDeclWithClangNode(decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); - structDecl->setAddedImplicitInitializers(); auto options = getDefaultMakeStructRawValuedOptions(); options |= MakeStructRawValuedFlags::MakeUnlabeledValueInit; @@ -2777,7 +2824,6 @@ namespace { C.getProtocol(KnownProtocolKind::ErrorCodeProtocol))) { // Create the wrapper struct. errorWrapper = new (C) StructDecl(loc, name, loc, None, nullptr, dc); - errorWrapper->setAddedImplicitInitializers(); errorWrapper->setAccess(AccessLevel::Public); errorWrapper->getAttrs().add( new (Impl.SwiftContext) FrozenAttr(/*IsImplicit*/true)); @@ -3205,7 +3251,6 @@ namespace { name, Impl.importSourceLoc(decl->getLocation()), None, nullptr, dc); - result->setAddedImplicitInitializers(); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; // FIXME: Figure out what to do with superclasses in C++. One possible @@ -3738,7 +3783,6 @@ namespace { result->setIsObjC(false); result->setIsDynamic(false); - result->computeType(); Impl.recordImplicitUnwrapForDecl(result, importedType.isImplicitlyUnwrapped()); @@ -4048,8 +4092,8 @@ namespace { /// Check whether we have already imported a method with the given /// selector in the given context. - bool isMethodAlreadyImported(ObjCSelector selector, bool isInstance, - DeclContext *dc, + bool isMethodAlreadyImported(ObjCSelector selector, ImportedName importedName, + bool isInstance, const DeclContext *dc, llvm::function_ref filter) { // We only need to perform this check for classes. auto classDecl @@ -4065,6 +4109,7 @@ namespace { for (auto decl : classDecl->lookupDirect(selector, isInstance)) { if ((decl->getClangDecl() || !decl->getDeclContext()->getParentSourceFile()) + && importedName.getDeclName() == decl->getFullName() && filter(decl)) { result = true; break; @@ -4132,12 +4177,18 @@ namespace { } } + ImportedName importedName; + Optional correctSwiftName; + importedName = importFullName(decl, correctSwiftName); + if (!importedName) + return nullptr; + // Check whether another method with the same selector has already been // imported into this context. ObjCSelector selector = Impl.importSelector(decl->getSelector()); bool isInstance = decl->isInstanceMethod() && !forceClassMethod; if (isActiveSwiftVersion()) { - if (isMethodAlreadyImported(selector, isInstance, dc, + if (isMethodAlreadyImported(selector, importedName, isInstance, dc, [&](AbstractFunctionDecl *fn) { return isAcceptableResult(fn, accessorInfo); })) { @@ -4145,12 +4196,6 @@ namespace { } } - ImportedName importedName; - Optional correctSwiftName; - importedName = importFullName(decl, correctSwiftName); - if (!importedName) - return nullptr; - // Normal case applies when we're importing an older name, or when we're // not an init if (!isFactoryInit(importedName)) { @@ -4321,9 +4366,6 @@ namespace { if (forceClassMethod) result->setImplicit(); - // Compute the interface type. - result->computeType(); - Impl.recordImplicitUnwrapForDecl(result, isIUO); // Mark this method @objc. @@ -4369,6 +4411,13 @@ namespace { } } + // We only care about recording methods with no arguments here, because + // they can shadow imported properties. + if (!isa(result) && + result->getFullName().getArgumentNames().empty()) { + recordMemberInContext(dc, result); + } + return result; } @@ -4386,7 +4435,7 @@ namespace { /// NSArray(capacity: 1024) /// \endcode ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, Optional kind, bool required); @@ -4415,9 +4464,9 @@ namespace { /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod, - DeclContext *dc, + const DeclContext *dc, bool implicit, - Optional kindIn, + CtorInitializerKind kind, bool required, ObjCSelector selector, ImportedName importedName, @@ -4453,11 +4502,6 @@ namespace { const clang::ObjCProtocolList &clangProtocols, SmallVectorImpl &inheritedTypes); - /// Add conformances to the given Objective-C protocols to the - /// given declaration. - void addObjCProtocolConformances(Decl *decl, - ArrayRef protocols); - // Returns None on error. Returns nullptr if there is no type param list to // import or we suppress its import, as in the case of NSArray, NSSet, and // NSDictionary. @@ -4478,9 +4522,7 @@ namespace { /// methods become class methods on NSObject). void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, - SmallVectorImpl &members, - ASTContext &Ctx); + SmallVectorImpl &members); void importNonOverriddenMirroredMethods(DeclContext *dc, MutableArrayRef entries, @@ -4488,7 +4530,7 @@ namespace { /// Import constructors from our superclasses (and their /// categories/extensions), effectively "inheriting" constructors. - void importInheritedConstructors(ClassDecl *classDecl, + void importInheritedConstructors(const ClassDecl *classDecl, SmallVectorImpl &newMembers); Decl *VisitObjCCategoryDecl(const clang::ObjCCategoryDecl *decl) { @@ -4537,24 +4579,95 @@ namespace { } template - T *resolveSwiftDeclImpl(const U *decl, Identifier name, ModuleDecl *overlay) { + T *resolveSwiftDeclImpl(const U *decl, Identifier name, + bool hasKnownSwiftName, ModuleDecl *overlay) { const auto &languageVersion = Impl.SwiftContext.LangOpts.EffectiveLanguageVersion; - SmallVector results; - overlay->lookupValue(name, NLKind::QualifiedLookup, results); + auto isMatch = [&](const T *singleResult, bool baseNameMatches, + bool allowObjCMismatch) -> bool { + const DeclAttributes &attrs = singleResult->getAttrs(); + + // Skip versioned variants. + if (attrs.isUnavailableInSwiftVersion(languageVersion)) + return false; + + // If Clang decl has a custom Swift name, then we know that the name we + // did direct lookup for is correct. + // 'allowObjCMismatch' shouldn't exist, but we need it for source + // compatibility where a previous version of the compiler didn't check + // @objc-ness at all. + if (hasKnownSwiftName || allowObjCMismatch) { + assert(baseNameMatches); + return allowObjCMismatch || singleResult->isObjC(); + } + + // Skip if a different name is used for Objective-C. + if (auto objcAttr = attrs.getAttribute()) + if (auto objcName = objcAttr->getName()) + return objcName->getSimpleName() == name; + + return baseNameMatches && singleResult->isObjC(); + }; + + // First look at Swift types with the same name. + SmallVector swiftDeclsByName; + overlay->lookupValue(name, NLKind::QualifiedLookup, swiftDeclsByName); T *found = nullptr; - for (auto result : results) { + for (auto result : swiftDeclsByName) { if (auto singleResult = dyn_cast(result)) { - // Skip versioned variants. - const DeclAttributes &attrs = singleResult->getAttrs(); - if (attrs.isUnavailableInSwiftVersion(languageVersion)) - continue; + if (isMatch(singleResult, /*baseNameMatches=*/true, + /*allowObjCMismatch=*/false)) { + if (found) + return nullptr; + found = singleResult; + } + } + } - if (found) - return nullptr; + if (!found && hasKnownSwiftName) + return nullptr; + + if (!found) { + // Try harder to find a match looking at just custom Objective-C names. + // Limit what we deserialize to decls with an @objc attribute. + SmallVector matchingTopLevelDecls; + + // Get decls with a matching @objc attribute + overlay->getTopLevelDeclsWhereAttributesMatch( + matchingTopLevelDecls, + [&name](const DeclAttributes attrs) -> bool { + if (auto objcAttr = attrs.getAttribute()) + if (auto objcName = objcAttr->getName()) + return objcName->getSimpleName() == name; + return false; + }); + + // Filter by decl kind + for (auto result : matchingTopLevelDecls) { + if (auto singleResult = dyn_cast(result)) { + if (found) + return nullptr; + found = singleResult; + } + } + } - found = singleResult; + if (!found) { + // Go back to the first list and find classes with matching Swift names + // *even if the ObjC name doesn't match.* + // This shouldn't be allowed but we need it for source compatibility; + // people used `@class SwiftNameOfClass` as a workaround for not + // having the previous loop, and it "worked". + for (auto result : swiftDeclsByName) { + if (auto singleResult = dyn_cast(result)) { + if (isMatch(singleResult, /*baseNameMatches=*/true, + /*allowObjCMismatch=*/true)) { + if (found) + return nullptr; + found = singleResult; + } + } } } @@ -4567,15 +4680,16 @@ namespace { template T *resolveSwiftDecl(const U *decl, Identifier name, - ClangModuleUnit *clangModule) { + bool hasKnownSwiftName, ClangModuleUnit *clangModule) { if (auto overlay = clangModule->getOverlayModule()) - return resolveSwiftDeclImpl(decl, name, overlay); + return resolveSwiftDeclImpl(decl, name, hasKnownSwiftName, overlay); if (clangModule == Impl.ImportedHeaderUnit) { // Use an index-based loop because new owners can come in as we're // iterating. for (size_t i = 0; i < Impl.ImportedHeaderOwners.size(); ++i) { ModuleDecl *owner = Impl.ImportedHeaderOwners[i]; - if (T *result = resolveSwiftDeclImpl(decl, name, owner)) + if (T *result = resolveSwiftDeclImpl(decl, name, + hasKnownSwiftName, owner)) return result; } } @@ -4588,7 +4702,8 @@ namespace { if (!importer::hasNativeSwiftDecl(decl)) return false; auto wrapperUnit = cast(dc->getModuleScopeContext()); - swiftDecl = resolveSwiftDecl(decl, name, wrapperUnit); + swiftDecl = resolveSwiftDecl(decl, name, /*hasCustomSwiftName=*/true, + wrapperUnit); return true; } @@ -4617,6 +4732,7 @@ namespace { *correctSwiftName); Identifier name = importedName.getDeclName().getBaseIdentifier(); + bool hasKnownSwiftName = importedName.hasCustomName(); // FIXME: Figure out how to deal with incomplete protocols, since that // notion doesn't exist in Swift. @@ -4624,6 +4740,7 @@ namespace { // Check if this protocol is implemented in its overlay. if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) if (auto native = resolveSwiftDecl(decl, name, + hasKnownSwiftName, clangModule)) return native; @@ -4657,8 +4774,6 @@ namespace { Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); - // Import protocols this protocol conforms to. SmallVector inheritedTypes; importObjCProtocols(result, decl->getReferencedProtocols(), @@ -4694,7 +4809,6 @@ namespace { SourceLoc(), None, nullptr, dc); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); result->setSuperclass(Type()); result->setAddedImplicitInitializers(); // suppress all initializers result->setHasMissingVTableEntries(false); @@ -4735,11 +4849,13 @@ namespace { *correctSwiftName); auto name = importedName.getDeclName().getBaseIdentifier(); + bool hasKnownSwiftName = importedName.hasCustomName(); if (!decl->hasDefinition()) { // Check if this class is implemented in its overlay. if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) { if (auto native = resolveSwiftDecl(decl, name, + hasKnownSwiftName, clangModule)) { return native; } @@ -4798,7 +4914,6 @@ namespace { } Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - result->setCircularityCheck(CircularityCheck::Checked); addObjCAttribute(result, Impl.importIdentifier(decl->getIdentifier())); if (declaredNative) @@ -4954,43 +5069,21 @@ namespace { return nullptr; VarDecl *overridden = nullptr; - if (dc->getSelfClassDecl()) { - // Check whether there is a function with the same name as this - // property. If so, suppress the property; the user will have to use - // the methods directly, to avoid ambiguities. - NominalTypeDecl *lookupContext = dc->getSelfNominalTypeDecl(); - + // Check whether there is a function with the same name as this + // property. If so, suppress the property; the user will have to use + // the methods directly, to avoid ambiguities. + if (auto *subject = dc->getSelfClassDecl()) { if (auto *classDecl = dyn_cast(dc)) { - // If we're importing into the primary @interface for something, as - // opposed to an extension, make sure we don't try to load any - // categories...by just looking into the super type. - lookupContext = classDecl->getSuperclassDecl(); + // Start looking into the superclass. + subject = classDecl->getSuperclassDecl(); } - if (lookupContext) { - SmallVector lookup; - dc->lookupQualified(lookupContext, name, - NL_QualifiedDefault | NL_KnownNoDependency, - lookup); - bool foundMethod = false; - for (auto result : lookup) { - if (isa(result) && - result->isInstanceMember() == decl->isInstanceProperty() && - result->getFullName().getArgumentNames().empty()) - foundMethod = true; - - if (auto var = dyn_cast(result)) { - // If the selectors of the getter match in Objective-C, we have an - // override. - if (var->isInstanceMember() == decl->isInstanceProperty() && - var->getObjCGetterSelector() == - Impl.importSelector(decl->getGetterName())) - overridden = var; - } - } - if (foundMethod && !overridden) - return nullptr; - } + bool foundMethod = false; + std::tie(overridden, foundMethod) + = identifyNearestOverriddenDecl(Impl, dc, decl, name, subject); + + if (foundMethod && !overridden) + return nullptr; if (overridden) { const DeclContext *overrideContext = overridden->getDeclContext(); @@ -5082,6 +5175,7 @@ namespace { if (correctSwiftName) markAsVariant(result, *correctSwiftName); + recordMemberInContext(dc, result); return result; } @@ -5275,7 +5369,6 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl, auto theClass = Impl.createDeclWithClangNode( decl, AccessLevel::Public, SourceLoc(), className, SourceLoc(), None, nullptr, dc); - theClass->setCircularityCheck(CircularityCheck::Checked); theClass->setSuperclass(superclass); theClass->setAddedImplicitInitializers(); // suppress all initializers theClass->setHasMissingVTableEntries(false); @@ -5369,7 +5462,7 @@ namespace { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto protoDecl = dyn_cast(found.second)) + if (auto protoDecl = dyn_cast(found.Item)) if (protoDecl == proto || protoDecl->inheritsFrom(proto)) return true; } @@ -5440,7 +5533,6 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl, auto structDecl = Impl.createDeclWithClangNode( decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); - structDecl->setAddedImplicitInitializers(); // Import the type of the underlying storage auto storedUnderlyingType = Impl.importTypeIgnoreIUO( @@ -5716,7 +5808,6 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, // Create a struct with the underlying type as a field. auto structDecl = Impl.createDeclWithClangNode( decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); - structDecl->setAddedImplicitInitializers(); makeStructRawValued(Impl, structDecl, underlyingType, {KnownProtocolKind::OptionSet}); @@ -5793,8 +5884,6 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( std::move(initKind)); result->setImportAsStaticMember(); - // Set the constructor's type. - result->computeType(); Impl.recordImplicitUnwrapForDecl(result, importedType.isImplicitlyUnwrapped()); result->setOverriddenDecls({ }); @@ -5998,7 +6087,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName, } ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, Optional kind, bool required) { // Only methods in the 'init' family can become constructors. assert(isInitMethod(objcMethod) && "Not a real init method"); @@ -6009,10 +6098,15 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( if (known != Impl.Constructors.end()) return known->second; + Optional correctSwiftName; + auto importedName = importFullName(objcMethod, correctSwiftName); + if (!importedName) + return nullptr; + // Check whether there is already a method with this selector. auto selector = Impl.importSelector(objcMethod->getSelector()); if (isActiveSwiftVersion() && - isMethodAlreadyImported(selector, /*isInstance=*/true, dc, + isMethodAlreadyImported(selector, importedName, /*isInstance=*/true, dc, [](AbstractFunctionDecl *fn) { return true; })) @@ -6023,10 +6117,6 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( objcMethod->param_end()}; bool variadic = objcMethod->isVariadic(); - Optional correctSwiftName; - auto importedName = importFullName(objcMethod, correctSwiftName); - if (!importedName) - return nullptr; // If we dropped the variadic, handle it now. if (importedName.droppedVariadic()) { @@ -6037,9 +6127,10 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( } bool redundant; - auto result = - importConstructor(objcMethod, dc, implicit, kind, required, selector, - importedName, params, variadic, redundant); + auto result = importConstructor(objcMethod, dc, implicit, + kind.getValueOr(importedName.getInitKind()), + required, selector, importedName, params, + variadic, redundant); // If this is a compatibility stub, mark it as such. if (result && correctSwiftName) @@ -6148,8 +6239,8 @@ bool SwiftDeclConverter::existingConstructorIsWorse( /// This variant of the function is responsible for actually binding the /// constructor declaration appropriately. ConstructorDecl *SwiftDeclConverter::importConstructor( - const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit, - Optional kindIn, bool required, ObjCSelector selector, + const clang::ObjCMethodDecl *objcMethod, const DeclContext *dc, bool implicit, + CtorInitializerKind kind, bool required, ObjCSelector selector, ImportedName importedName, ArrayRef args, bool variadic, bool &redundant) { redundant = false; @@ -6158,35 +6249,6 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( auto ownerNominal = dc->getSelfNominalTypeDecl(); assert(ownerNominal && "Method in non-type context?"); - // Find the interface, if we can. - const clang::ObjCInterfaceDecl *interface = nullptr; - if (auto classDecl = dyn_cast(ownerNominal)) { - interface = - dyn_cast_or_null(classDecl->getClangDecl()); - } - - // If we weren't told what kind of initializer this should be, - // figure it out now. - CtorInitializerKind kind; - - if (kindIn) { - kind = *kindIn; - - // If we know this is a designated initializer, mark it as such. - if (interface && hasDesignatedInitializers(interface) && - isDesignatedInitializer(interface, objcMethod)) - kind = CtorInitializerKind::Designated; - } else { - // If the owning Objective-C class has designated initializers and this - // is not one of them, treat it as a convenience initializer. - if (interface && hasDesignatedInitializers(interface) && - !isDesignatedInitializer(interface, objcMethod)) { - kind = CtorInitializerKind::Convenience; - } else { - kind = CtorInitializerKind::Designated; - } - } - // Import the type that this method will have. Optional errorConvention; ParameterList *bodyParams; @@ -6297,13 +6359,10 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), /*Throws=*/importedName.getErrorInfo().hasValue(), /*ThrowsLoc=*/SourceLoc(), bodyParams, - /*GenericParams=*/nullptr, dc); + /*GenericParams=*/nullptr, const_cast(dc)); addObjCAttribute(result, selector); - // Calculate the function type of the result. - result->computeType(); - Impl.recordImplicitUnwrapForDecl(result, importedType.isImplicitlyUnwrapped()); @@ -6384,7 +6443,7 @@ void SwiftDeclConverter::recordObjCOverride(AbstractFunctionDecl *decl) { return; // Dig out the Objective-C superclass. SmallVector results; - superDecl->lookupQualified(superDecl, decl->getFullName(), + superDecl->lookupQualified(superDecl, DeclNameRef(decl->getFullName()), NL_QualifiedDefault | NL_KnownNoDependency, results); for (auto member : results) { @@ -6457,7 +6516,7 @@ void SwiftDeclConverter::recordObjCOverride(SubscriptDecl *subscript) { // operation. SmallVector lookup; subscript->getModuleContext()->lookupQualified( - superDecl, subscript->getFullName(), + superDecl, DeclNameRef(subscript->getFullName()), NL_QualifiedDefault | NL_KnownNoDependency, lookup); for (auto result : lookup) { @@ -6807,22 +6866,7 @@ void SwiftDeclConverter::importObjCProtocols( } } - addObjCProtocolConformances(decl, protocols); -} - -void SwiftDeclConverter::addObjCProtocolConformances( - Decl *decl, ArrayRef protocols) { - // Nothing to do for protocols. - if (isa(decl)) return; - Impl.recordImportedProtocols(decl, protocols); - - if (auto nominal = dyn_cast(decl)) { - nominal->setConformanceLoader(&Impl, 0); - } else { - auto ext = cast(decl); - ext->setConformanceLoader(&Impl, 0); - } } Optional SwiftDeclConverter::importObjCGenericParams( @@ -6887,23 +6931,18 @@ Optional SwiftDeclConverter::importObjCGenericParams( void SwiftDeclConverter::importMirroredProtocolMembers( const clang::ObjCContainerDecl *decl, DeclContext *dc, - ArrayRef protocols, SmallVectorImpl &members, - ASTContext &Ctx) { + SmallVectorImpl &members) { assert(dc); const clang::ObjCInterfaceDecl *interfaceDecl = nullptr; const ClangModuleUnit *declModule; const ClangModuleUnit *interfaceModule; - // 'protocols' is, for some reason, the full recursive expansion of - // the protocol hierarchy, so there's no need to recursively descend - // into inherited protocols. - // Try to import only the most specific methods with a particular name. // We use a MapVector to get deterministic iteration order later. llvm::MapVector> methodsByName; - for (auto proto : protocols) { + for (auto proto : Impl.getImportedProtocols(dc->getAsDecl())) { auto clangProto = cast_or_null(proto->getClangDecl()); if (!clangProto) @@ -7135,7 +7174,7 @@ void SwiftDeclConverter::importNonOverriddenMirroredMethods(DeclContext *dc, } void SwiftDeclConverter::importInheritedConstructors( - ClassDecl *classDecl, SmallVectorImpl &newMembers) { + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { if (!classDecl->hasSuperclass()) return; @@ -7230,9 +7269,9 @@ void SwiftDeclConverter::importInheritedConstructors( }; // The kind of initializer to import. If this class has designated - // initializers, everything it imports is a convenience initializer. + // initializers, everything it inherits is a convenience initializer. Optional kind; - if (hasDesignatedInitializers(curObjCClass)) + if (curObjCClass->hasDesignatedInitializers()) kind = CtorInitializerKind::Convenience; @@ -7503,6 +7542,15 @@ void ClangImporter::Implementation::importAttributes( } } + if (auto method = dyn_cast(ClangDecl)) { + if (method->isDirectMethod() && !AnyUnavailable) { + auto attr = AvailableAttr::createPlatformAgnostic( + C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift); + MappedDecl->getAttrs().add(attr); + AnyUnavailable = true; + } + } + // If the declaration is unavailable, we're done. if (AnyUnavailable) return; @@ -7634,7 +7682,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl, if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) { if (auto swiftClass = castIgnoringCompatibilityAlias( importDecl(theClass, CurrentVersion))) { - swiftClass->setHasMissingDesignatedInitializers(); + SwiftContext.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{swiftClass}, true); } } } @@ -7740,7 +7789,7 @@ static void finishTypeWitnesses( NL_OnlyTypes | NL_ProtocolMembers); - dc->lookupQualified(nominal, assocType->getFullName(), options, + dc->lookupQualified(nominal, DeclNameRef(assocType->getFullName()), options, lookupResults); for (auto member : lookupResults) { auto typeDecl = cast(member); @@ -8255,7 +8304,8 @@ synthesizeConstantGetterBody(AbstractFunctionDecl *afd, void *voidContext) { DeclName initName = DeclName(ctx, DeclBaseName::createConstructor(), { ctx.Id_rawValue }); auto nominal = type->getAnyNominal(); - for (auto found : nominal->lookupDirect(initName)) { + const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; + for (auto found : nominal->lookupDirect(initName, flags)) { init = dyn_cast(found); if (init && init->getDeclContext() == nominal) break; @@ -8344,7 +8394,6 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc, params, TypeLoc::withoutLoc(type), dc); func->setStatic(isStatic); - func->computeType(); func->setAccess(getOverridableAccessLevel(dc)); func->setIsObjC(false); func->setIsDynamic(false); @@ -8394,6 +8443,23 @@ createUnavailableDecl(Identifier name, DeclContext *dc, Type type, return var; } +// Force the members of the entire inheritance hierarchy to be loaded and +// deserialized before loading the members of this class. This allows the +// decl members table to be warmed up and enables the correct identification of +// overrides. +static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) { + if (!CD) + return; + + CD = CD->getSuperclassDecl(); + if (!CD || !CD->hasClangNode()) + return; + + CD->loadAllMembers(); + + for (auto E : CD->getExtensions()) + E->loadAllMembers(); +} void ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) { @@ -8407,6 +8473,7 @@ ClangImporter::Implementation::loadAllMembers(Decl *D, uint64_t extra) { // If not, we're importing globals-as-members into an extension. if (objcContainer) { + loadAllMembersOfSuperclassIfNeeded(dyn_cast(D)); loadAllMembersOfObjcContainer(D, objcContainer); return; } @@ -8468,7 +8535,7 @@ void ClangImporter::Implementation::loadAllMembersIntoExtension( startedImportingEntity(); // Load the members. - for (auto entry : table->lookupGlobalsAsMembers(effectiveClangContext)) { + for (auto entry : table->allGlobalsAsMembersInContext(effectiveClangContext)) { auto decl = entry.get(); // Only include members in the same submodule as this extension. @@ -8524,20 +8591,6 @@ bool ClangImporter::Implementation::addMemberAndAlternatesToExtension( return true; } -static ExtensionDecl * -figureOutTheDeclarationContextToImportInto(Decl *D, DeclContext *&DC, - IterableDeclContext *&IDC) { - if (auto *nominal = dyn_cast(D)) { - DC = nominal; - IDC = nominal; - return nullptr; - } - ExtensionDecl *ext = cast(D); - DC = ext; - IDC = ext; - return ext; -} - static void loadMembersOfBaseImportedFromClang(ExtensionDecl *ext) { const NominalTypeDecl *base = ext->getExtendedNominal(); auto *clangBase = base->getClangDecl(); @@ -8559,19 +8612,18 @@ void ClangImporter::Implementation::loadAllMembersOfObjcContainer( Instance->getSourceManager(), "loading members for"); - DeclContext *DC; - IterableDeclContext *IDC; - if (ExtensionDecl *ext = - figureOutTheDeclarationContextToImportInto(D, DC, IDC)) { - // If the base is also imported from Clang, load its members first. + assert(isa(D) || isa(D)); + if (auto *ext = dyn_cast(D)) { + // If the extended type is also imported from Clang, load its members first. loadMembersOfBaseImportedFromClang(ext); } startedImportingEntity(); SmallVector members; - collectMembersToAdd(objcContainer, D, DC, members); + collectMembersToAdd(objcContainer, D, cast(D), members); + auto *IDC = cast(D); for (auto member : members) { if (!isa(member)) IDC->addMember(member); @@ -8605,29 +8657,30 @@ void ClangImporter::Implementation::insertMembersAndAlternates( }); } +void ClangImporter::Implementation::importInheritedConstructors( + const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, SmallVectorImpl &newMembers) { + if (curObjCClass->getName() != "Protocol") { + SwiftDeclConverter converter(*this, CurrentVersion); + converter.importInheritedConstructors(classDecl, newMembers); + } + } + void ClangImporter::Implementation::collectMembersToAdd( const clang::ObjCContainerDecl *objcContainer, Decl *D, DeclContext *DC, SmallVectorImpl &members) { for (const clang::Decl *m : objcContainer->decls()) { auto nd = dyn_cast(m); if (nd && nd == nd->getCanonicalDecl() && - nd->getDeclContext() == objcContainer) + nd->getDeclContext() == objcContainer && + isVisibleClangEntry(nd)) insertMembersAndAlternates(nd, members); } SwiftDeclConverter converter(*this, CurrentVersion); - - auto protos = getImportedProtocols(D); if (auto clangClass = dyn_cast(objcContainer)) { - auto swiftClass = cast(D); objcContainer = clangClass = clangClass->getDefinition(); - - // Imported inherited initializers. - if (clangClass->getName() != "Protocol") { - converter.importInheritedConstructors(const_cast(swiftClass), - members); - } - + importInheritedConstructors(clangClass, cast(D), members); } else if (auto clangProto = dyn_cast(objcContainer)) { objcContainer = clangProto->getDefinition(); @@ -8635,8 +8688,7 @@ void ClangImporter::Implementation::collectMembersToAdd( // Import mirrored declarations for protocols to which this category // or extension conforms. // FIXME: This is supposed to be a short-term hack. - converter.importMirroredProtocolMembers(objcContainer, DC, - protos, members, SwiftContext); + converter.importMirroredProtocolMembers(objcContainer, DC, members); } void ClangImporter::Implementation::loadAllConformances( diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index dc1b4d9a9af74..2200bd6d78509 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -21,6 +21,7 @@ #include "ClangDiagnosticConsumer.h" #include "swift/Subsystems.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ClangSwiftTypeCorrespondence.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/Module.h" @@ -170,19 +171,6 @@ static bool isIntegerType(clang::QualType clangType) { return false; } -/// Whether the given Objective-C type can be imported as an optional type. -static bool canImportAsOptional(clang::ASTContext &ctx, clang::QualType type) { - // Note: this mimics ImportHint::canImportAsOptional. - - // Objective-C object pointers. - if (type->getAs()) return true; - - // Block and C pointers, including CF types. - if (type->isBlockPointerType() || type->isPointerType()) return true; - - return false; -} - static Optional classifyMethodErrorHandling(const clang::ObjCMethodDecl *clangDecl, OptionalTypeKind resultOptionality) { @@ -202,7 +190,8 @@ classifyMethodErrorHandling(const clang::ObjCMethodDecl *clangDecl, // non-optional type. case clang::SwiftErrorAttr::NullResult: if (resultOptionality != OTK_None && - canImportAsOptional(clangCtx, clangDecl->getReturnType())) + swift::canImportAsOptional( + clangDecl->getReturnType().getTypePtrOrNull())) return ForeignErrorConvention::NilResult; return None; @@ -235,7 +224,8 @@ classifyMethodErrorHandling(const clang::ObjCMethodDecl *clangDecl, // For optional reference results, a nil value is normally an error. if (resultOptionality != OTK_None && - canImportAsOptional(clangCtx, clangDecl->getReturnType())) { + swift::canImportAsOptional( + clangDecl->getReturnType().getTypePtrOrNull())) { return ForeignErrorConvention::NilResult; } @@ -518,7 +508,7 @@ matchFactoryAsInitName(const clang::ObjCMethodDecl *method) { /// Determine the kind of initializer the given factory method could be mapped /// to, or produce \c None. static Optional -determineCtorInitializerKind(const clang::ObjCMethodDecl *method) { +determineFactoryInitializerKind(const clang::ObjCMethodDecl *method) { // Determine whether we have a suitable return type. if (method->hasRelatedResultType()) { // When the factory method has an "instancetype" result type, we @@ -708,7 +698,7 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) { } // Special case: preventing a mapping to an initializer. - if (matchFactoryAsInitName(method) && determineCtorInitializerKind(method)) + if (matchFactoryAsInitName(method) && determineFactoryInitializerKind(method)) return attr; return nullptr; @@ -733,24 +723,39 @@ getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl, return FactoryAsInitKind::Infer; } +Optional determineCtorInitializerKind( + const clang::ObjCMethodDecl *method) { + const clang::ObjCInterfaceDecl *interface = method->getClassInterface(); + + if (isInitMethod(method)) { + // If the owning Objective-C class has designated initializers and this + // is not one of them, treat it as a convenience initializer. + if (interface && interface->hasDesignatedInitializers() && + !method->hasAttr()) { + return CtorInitializerKind::Convenience; + } + + return CtorInitializerKind::Designated; + } + + if (method->isClassMethod()) + return determineFactoryInitializerKind(method); + + return None; +} + /// Determine whether this Objective-C method should be imported as /// an initializer. /// /// \param prefixLength Will be set to the length of the prefix that /// should be stripped from the first selector piece, e.g., "init" /// or the restated name of the class in a factory method. -/// -/// \param kind Will be set to the kind of initializer being -/// imported. Note that this does not distinguish designated -/// vs. convenience; both will be classified as "designated". static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method, ImportNameVersion version, - unsigned &prefixLength, - CtorInitializerKind &kind) { + unsigned &prefixLength) { /// Is this an initializer? if (isInitMethod(method)) { prefixLength = 4; - kind = CtorInitializerKind::Designated; return true; } @@ -783,11 +788,8 @@ static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method, return false; } - // Determine what kind of initializer we're creating. - if (auto initKind = determineCtorInitializerKind(method)) { - kind = *initKind; + if (determineFactoryInitializerKind(method)) return true; - } // Not imported as an initializer. return false; @@ -833,8 +835,8 @@ static bool omitNeedlessWordsInFunctionName( param->getType(), getParamOptionality(swiftLanguageVersion, param, !nonNullArgs.empty() && nonNullArgs[i]), - nameImporter.getIdentifier(baseName), numParams, argumentName, - i == 0, isLastParameter, nameImporter) != DefaultArgumentKind::None; + nameImporter.getIdentifier(baseName), argumentName, i == 0, + isLastParameter, nameImporter) != DefaultArgumentKind::None; paramTypes.push_back(getClangTypeNameForOmission(clangCtx, param->getOriginalType()) @@ -1238,6 +1240,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (overriddenNames.size() > 1) mergeOverriddenNames(swiftCtx, method, overriddenNames); overriddenNames[0].second.effectiveContext = result.effectiveContext; + + // Compute the initializer kind from the derived method, though. + if (auto kind = determineCtorInitializerKind(method)) + overriddenNames[0].second.info.initKind = *kind; + return overriddenNames[0].second; } } else if (auto property = dyn_cast(D)) { @@ -1294,12 +1301,14 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, if (method) { unsigned initPrefixLength; if (parsedName.BaseName == "init" && parsedName.IsFunctionName) { - if (!shouldImportAsInitializer(method, version, initPrefixLength, - result.info.initKind)) { + if (!shouldImportAsInitializer(method, version, initPrefixLength)) { // We cannot import this as an initializer anyway. return ImportedName(); } + if (auto kind = determineCtorInitializerKind(method)) + result.info.initKind = *kind; + // If this swift_name attribute maps a factory method to an // initializer and we were asked not to do so, ignore the // custom name. @@ -1471,14 +1480,18 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, return ImportedName(); isInitializer = shouldImportAsInitializer(objcMethod, version, - initializerPrefixLen, - result.info.initKind); - - // If we would import a factory method as an initializer but were - // asked not to, don't consider this as an initializer. - if (isInitializer && suppressFactoryMethodAsInit(objcMethod, version, - result.getInitKind())) { - isInitializer = false; + initializerPrefixLen); + + if (isInitializer) { + if (auto kind = determineCtorInitializerKind(objcMethod)) + result.info.initKind = *kind; + + // If we would import a factory method as an initializer but were + // asked not to, don't consider this as an initializer. + if (suppressFactoryMethodAsInit(objcMethod, version, + result.getInitKind())) { + isInitializer = false; + } } if (isInitializer) @@ -1781,9 +1794,11 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl, ImportNameVersion version, clang::DeclarationName givenName) { CacheKeyType key(decl, version); - if (importNameCache.count(key) && !givenName) { - ++ImportNameNumCacheHits; - return importNameCache[key]; + if (!givenName) { + if (auto cachedRes = importNameCache[key]) { + ++ImportNameNumCacheHits; + return cachedRes; + } } ++ImportNameNumCacheMisses; auto res = importNameImpl(decl, version, givenName); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 92df022f172da..854a4f29d78fa 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -185,7 +185,7 @@ namespace { llvm_unreachable("Dependent types cannot be converted"); \ } #define TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Given a loaded type like CInt, look through the type alias sugar that the // stdlib uses to show the underlying type. We want to import the signature @@ -418,8 +418,10 @@ namespace { auto funcTy = pointeeType->castTo(); return { FunctionType::get(funcTy->getParams(), funcTy->getResult(), - funcTy->getExtInfo().withRepresentation( - AnyFunctionType::Representation::CFunctionPointer)), + funcTy->getExtInfo() + .withRepresentation( + AnyFunctionType::Representation::CFunctionPointer) + .withClangFunctionType(type)), ImportHint::CFunctionPointer }; } @@ -573,7 +575,7 @@ namespace { } // Form the function type. - return FunctionType::get(params, resultTy); + return FunctionType::get(params, resultTy, FunctionType::ExtInfo()); } ImportResult @@ -606,7 +608,7 @@ namespace { importObjCTypeParamDecl(const clang::ObjCTypeParamDecl *objcTypeParamDecl) { // Pull the corresponding generic type parameter from the imported class. const auto *typeParamContext = objcTypeParamDecl->getDeclContext(); - GenericSignature genericSig = GenericSignature(); + GenericSignature genericSig; if (auto *category = dyn_cast(typeParamContext)) { auto ext = cast_or_null( @@ -762,8 +764,8 @@ namespace { LLVM_FALLTHROUGH; default: if (!underlyingResult.AbstractType->isEqual(mappedType)) { - underlyingResult.AbstractType->dump(); - mappedType->dump(); + underlyingResult.AbstractType->dump(llvm::errs()); + mappedType->dump(llvm::errs()); } assert(underlyingResult.AbstractType->isEqual(mappedType) && "typedef without special typedef kind was mapped " @@ -1762,13 +1764,12 @@ static bool isObjCMethodResultAudited(const clang::Decl *decl) { DefaultArgumentKind ClangImporter::Implementation::inferDefaultArgument( clang::QualType type, OptionalTypeKind clangOptionality, - DeclBaseName baseName, unsigned numParams, StringRef argumentLabel, - bool isFirstParameter, bool isLastParameter, NameImporter &nameImporter) { + DeclBaseName baseName, StringRef argumentLabel, bool isFirstParameter, + bool isLastParameter, NameImporter &nameImporter) { auto baseNameStr = baseName.userFacingName(); - // Don't introduce a default argument for setters with only a single - // parameter. - if (numParams == 1 && camel_case::getFirstWord(baseNameStr) == "set") + // Don't introduce a default argument for the first parameter of setters. + if (isFirstParameter && camel_case::getFirstWord(baseNameStr) == "set") return DefaultArgumentKind::None; // Some nullable parameters default to 'nil'. @@ -1789,10 +1790,6 @@ DefaultArgumentKind ClangImporter::Implementation::inferDefaultArgument( } } - // Don't introduce an empty options default arguments for setters. - if (isFirstParameter && camel_case::getFirstWord(baseNameStr) == "set") - return DefaultArgumentKind::None; - // Option sets default to "[]" if they have "Options" in their name. if (const clang::EnumType *enumTy = type->getAs()) { const clang::EnumDecl *enumDef = enumTy->getDecl()->getDefinition(); @@ -2087,7 +2084,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( bool paramIsIUO; if (kind == SpecialMethodKind::NSDictionarySubscriptGetter && paramTy->isObjCIdType()) { - swiftParamTy = SwiftContext.getNSCopyingDecl()->getDeclaredType(); + swiftParamTy = SwiftContext.getNSCopyingType(); if (optionalityOfParam != OTK_None) swiftParamTy = OptionalType::get(swiftParamTy); @@ -2175,8 +2172,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( auto defaultArg = inferDefaultArgument( param->getType(), optionalityOfParam, - importedName.getDeclName().getBaseName(), numEffectiveParams, - name.empty() ? StringRef() : name.str(), paramIndex == 0, + importedName.getDeclName().getBaseName(), name.str(), paramIndex == 0, isLastParameter, getNameImporter()); if (defaultArg != DefaultArgumentKind::None) paramInfo->setDefaultArgumentKind(defaultArg); @@ -2194,14 +2190,20 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( if (importedName.hasCustomName() && argNames.size() != swiftParams.size()) { // Note carefully: we're emitting a warning in the /Clang/ buffer. auto &srcMgr = getClangASTContext().getSourceManager(); - auto &rawDiagClient = Instance->getDiagnosticClient(); - auto &diagClient = static_cast(rawDiagClient); + ClangSourceBufferImporter &bufferImporter = + getBufferImporterForDiagnostics(); SourceLoc methodLoc = - diagClient.resolveSourceLocation(srcMgr, clangDecl->getLocation()); + bufferImporter.resolveSourceLocation(srcMgr, clangDecl->getLocation()); if (methodLoc.isValid()) { SwiftContext.Diags.diagnose(methodLoc, diag::invalid_swift_name_method, swiftParams.size() < argNames.size(), swiftParams.size(), argNames.size()); + ModuleDecl *parentModule = dc->getParentModule(); + if (parentModule != ImportedHeaderUnit->getParentModule()) { + SwiftContext.Diags.diagnose( + methodLoc, diag::unresolvable_clang_decl_is_a_framework_bug, + parentModule->getName().str()); + } } return {Type(), false}; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 4e93ef0172d1f..6c851d5468d8d 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -18,6 +18,7 @@ #define SWIFT_CLANG_IMPORTER_IMPL_H #include "ClangAdapter.h" +#include "ClangSourceBufferImporter.h" #include "ImportEnumInfo.h" #include "ImportName.h" #include "SwiftLookupTable.h" @@ -345,6 +346,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// (through the Swift name lookup module file extension). LookupTableMap LookupTables; + /// A helper class used to bring Clang buffers into Swift's SourceManager + /// for the purpose of emitting diagnostics. + /// + /// Listed early so that it gets torn down after the underlying Clang + /// instances that also use these buffers. + importer::ClangSourceBufferImporter BuffersForDiagnostics; + /// The fake buffer used to import modules. /// /// \see getNextIncludeLoc @@ -480,7 +488,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Keep track of initializer declarations that correspond to /// imported methods. llvm::DenseMap< - std::tuple, + std::tuple, ConstructorDecl *> Constructors; /// Keep track of all initializers that have been imported into a @@ -488,6 +496,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm::DenseMap> ConstructorsForNominal; + /// Keep track of all member declarations that have been imported into + /// a nominal type. + llvm::DenseMap>> + MembersForNominal; + /// Keep track of the nested 'Code' enum for imported error wrapper /// structs. llvm::DenseMap ErrorCodeEnums; @@ -593,6 +608,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool shouldIgnoreBridgeHeaderTopLevelDecl(clang::Decl *D); private: + /// When set, ClangImporter is disabled, and all requests go to the + /// DWARFImporter delegate. + bool DisableSourceImport; + /// The DWARF importer delegate, if installed. DWARFImporterDelegate *DWARFImporter = nullptr; public: @@ -602,17 +621,21 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation private: /// The list of Clang modules found in the debug info. llvm::DenseMap DWARFModuleUnits; -public: + /// Load a module using the clang::CompilerInstance. ModuleDecl *loadModuleClang(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); /// "Load" a module from debug info. Because debug info types are read on /// demand, this doesn't really do any work. ModuleDecl *loadModuleDWARF(SourceLoc importLoc, - ArrayRef> path); + ArrayRef> path); + +public: + /// Load a module using either method. + ModuleDecl *loadModule(SourceLoc importLoc, + ArrayRef> path); - void recordImplicitUnwrapForDecl(ValueDecl *decl, bool isIUO) { if (!isIUO) return; @@ -651,6 +674,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation return Instance->getCodeGenOpts(); } + importer::ClangSourceBufferImporter &getBufferImporterForDiagnostics() { + return BuffersForDiagnostics; + } + /// Imports the given header contents into the Clang context. bool importHeader(ModuleDecl *adapter, StringRef headerName, SourceLoc diagLoc, bool trackParsedSymbols, @@ -788,6 +815,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation Decl *importMirroredDecl(const clang::NamedDecl *decl, DeclContext *dc, Version version, ProtocolDecl *proto); + void importInheritedConstructors(const clang::ObjCInterfaceDecl *curObjCClass, + const ClassDecl *classDecl, + SmallVectorImpl &newMembers); + /// Utility function for building simple generic signatures. GenericSignature buildGenericSignature(GenericParamList *genericParams, DeclContext *dc); @@ -1070,9 +1101,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// given Clang \c type, \c baseName, and optionality. static DefaultArgumentKind inferDefaultArgument(clang::QualType type, OptionalTypeKind clangOptionality, - DeclBaseName baseName, unsigned numParams, - StringRef argumentLabel, bool isFirstParameter, - bool isLastParameter, importer::NameImporter &); + DeclBaseName baseName, StringRef argumentLabel, + bool isFirstParameter, bool isLastParameter, + importer::NameImporter &nameImporter); /// Import the parameter and return types of an Objective-C method. /// @@ -1152,12 +1183,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// FIXME: This is all a hack; we should have lazier deserialization /// of protocols separate from their conformances. - void recordImportedProtocols(const Decl *decl, + void recordImportedProtocols(Decl *decl, ArrayRef protocols) { + // Nothing to do for protocols. + if (isa(decl)) return; + if (protocols.empty()) return; ImportedProtocols[decl] = SwiftContext.AllocateCopy(protocols); + + if (auto nominal = dyn_cast(decl)) { + nominal->setConformanceLoader(this, 0); + } else { + auto ext = cast(decl); + ext->setConformanceLoader(this, 0); + } } /// Retrieve the imported protocols for the given declaration. @@ -1218,6 +1259,12 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + void loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) override { llvm_unreachable("unimplemented for ClangImporter"); @@ -1252,6 +1299,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// false otherwise. bool forEachLookupTable(llvm::function_ref fn); + /// Determine whether the given Clang entry is visible. + /// + /// FIXME: this is an elaborate hack to badly reflect Clang's + /// submodule visibility into Swift. + bool isVisibleClangEntry(const clang::NamedDecl *clangDecl); + bool isVisibleClangEntry(SwiftLookupTable::SingleEntry entry); + /// Look for namespace-scope values with the given name in the given /// Swift lookup table. void lookupValue(SwiftLookupTable &table, DeclName name, @@ -1368,15 +1422,18 @@ class SwiftNameLookupExtension : public clang::ModuleFileExtension { std::unique_ptr &pchLookupTable; LookupTableMap &lookupTables; ASTContext &swiftCtx; + ClangSourceBufferImporter &buffersForDiagnostics; const PlatformAvailability &availability; const bool inferImportAsMember; public: SwiftNameLookupExtension(std::unique_ptr &pchLookupTable, LookupTableMap &tables, ASTContext &ctx, + ClangSourceBufferImporter &buffersForDiagnostics, const PlatformAvailability &avail, bool inferIAM) : pchLookupTable(pchLookupTable), lookupTables(tables), swiftCtx(ctx), - availability(avail), inferImportAsMember(inferIAM) {} + buffersForDiagnostics(buffersForDiagnostics), availability(avail), + inferImportAsMember(inferIAM) {} clang::ModuleFileExtensionMetadata getExtensionMetadata() const override; llvm::hash_code hashExtension(llvm::hash_code code) const override; diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 770b95cc59ed2..0aa7476df5d55 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -30,9 +30,9 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Bitcode/BitstreamReader.h" -#include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/DJB.h" #include "llvm/Support/OnDiskHashTable.h" @@ -58,6 +58,9 @@ namespace { llvm::OnDiskIterableChainedHashTable; using SerializedGlobalsAsMembersTable = + llvm::OnDiskIterableChainedHashTable; + + using SerializedGlobalsAsMembersIndex = llvm::OnDiskIterableChainedHashTable; } // end anonymous namespace @@ -67,15 +70,19 @@ class SwiftLookupTableWriter : public clang::ModuleFileExtensionWriter { clang::ASTWriter &Writer; ASTContext &swiftCtx; + importer::ClangSourceBufferImporter &buffersForDiagnostics; const PlatformAvailability &availability; const bool inferImportAsMember; public: - SwiftLookupTableWriter(clang::ModuleFileExtension *extension, - clang::ASTWriter &writer, ASTContext &ctx, - const PlatformAvailability &avail, bool inferIAM) - : ModuleFileExtensionWriter(extension), Writer(writer), swiftCtx(ctx), - availability(avail), inferImportAsMember(inferIAM) {} + SwiftLookupTableWriter( + clang::ModuleFileExtension *extension, clang::ASTWriter &writer, + ASTContext &ctx, + importer::ClangSourceBufferImporter &buffersForDiagnostics, + const PlatformAvailability &avail, bool inferIAM) + : ModuleFileExtensionWriter(extension), Writer(writer), swiftCtx(ctx), + buffersForDiagnostics(buffersForDiagnostics), availability(avail), + inferImportAsMember(inferIAM) {} void writeExtensionContents(clang::Sema &sema, llvm::BitstreamWriter &stream) override; @@ -95,6 +102,7 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { std::unique_ptr SerializedTable; ArrayRef Categories; std::unique_ptr GlobalsAsMembersTable; + std::unique_ptr GlobalsAsMembersIndex; SwiftLookupTableReader(clang::ModuleFileExtension *extension, clang::ASTReader &reader, @@ -104,11 +112,14 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { serializedTable, ArrayRef categories, std::unique_ptr - globalsAsMembersTable) + globalsAsMembersTable, + std::unique_ptr + globalsAsMembersIndex) : ModuleFileExtensionReader(extension), Reader(reader), ModuleFile(moduleFile), OnRemove(onRemove), SerializedTable(std::move(serializedTable)), Categories(categories), - GlobalsAsMembersTable(std::move(globalsAsMembersTable)) {} + GlobalsAsMembersTable(std::move(globalsAsMembersTable)), + GlobalsAsMembersIndex(std::move(globalsAsMembersIndex)) {} public: /// Create a new lookup table reader for the given AST reader and stream @@ -144,12 +155,22 @@ class SwiftLookupTableReader : public clang::ModuleFileExtensionReader { /// injected into them. SmallVector getGlobalsAsMembersContexts(); + SmallVector getGlobalsAsMembersBaseNames(); + /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. /// /// \returns true if we found anything, false otherwise. - bool lookupGlobalsAsMembers(SwiftLookupTable::StoredContext context, - SmallVectorImpl &entries); + bool lookupGlobalsAsMembersInContext(SwiftLookupTable::StoredContext context, + SmallVectorImpl &entries); + + /// Retrieve the set of global declarations that are going to be imported as members under the given + /// Swift base name. + /// + /// \returns true if we found anything, false otherwise. + bool lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries); }; } // namespace swift @@ -487,63 +508,81 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, return; } - // Populate cache from reader if necessary. - findOrCreate(name.getBaseName()); + auto updateTableWithEntry = [this](SingleEntry newEntry, StoredContext context, + TableType::value_type::second_type &entries){ + for (auto &entry : entries) { + if (entry.Context == context) { + // We have entries for this context. + (void)addLocalEntry(newEntry, entry.DeclsOrMacros); + return; + } else { + (void)newEntry; + } + } - auto context = *contextOpt; + // This is a new context for this name. Add it. + auto decl = newEntry.dyn_cast(); + auto macro = newEntry.dyn_cast(); + auto moduleMacro = newEntry.dyn_cast(); + + FullTableEntry entry; + entry.Context = context; + if (decl) + entry.DeclsOrMacros.push_back(encodeEntry(decl)); + else if (macro) + entry.DeclsOrMacros.push_back(encodeEntry(macro)); + else + entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); + + entries.push_back(entry); + }; // If this is a global imported as a member, record is as such. + auto context = *contextOpt; if (isGlobalAsMember(newEntry, context)) { - auto &entries = GlobalsAsMembers[context]; + // Populate cache from reader if necessary. + findOrCreate(GlobalsAsMembers, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + updateTableWithEntry(newEntry, context, + GlobalsAsMembers[name.getBaseName()]); + + // Populate the index as well. + auto &entries = GlobalsAsMembersIndex[context]; (void)addLocalEntry(newEntry, entries); } - // Find the list of entries for this base name. - auto &entries = LookupTable[name.getBaseName()]; - auto decl = newEntry.dyn_cast(); - auto macro = newEntry.dyn_cast(); - auto moduleMacro = newEntry.dyn_cast(); - for (auto &entry : entries) { - if (entry.Context == context) { - // We have entries for this context. - (void)addLocalEntry(newEntry, entry.DeclsOrMacros); - return; - } - } - - // This is a new context for this name. Add it. - FullTableEntry entry; - entry.Context = context; - if (decl) - entry.DeclsOrMacros.push_back(encodeEntry(decl)); - else if (macro) - entry.DeclsOrMacros.push_back(encodeEntry(macro)); - else - entry.DeclsOrMacros.push_back(encodeEntry(moduleMacro)); - entries.push_back(entry); + // Populate cache from reader if necessary. + findOrCreate(LookupTable, name.getBaseName(), + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); + updateTableWithEntry(newEntry, context, LookupTable[name.getBaseName()]); } -auto SwiftLookupTable::findOrCreate(SerializedSwiftName baseName) - -> llvm::DenseMap>::iterator { +SwiftLookupTable::TableType::iterator +SwiftLookupTable::findOrCreate(TableType &Table, + SerializedSwiftName baseName, + llvm::function_ref create) { // If there is no base name, there is nothing to find. - if (baseName.empty()) return LookupTable.end(); + if (baseName.empty()) return Table.end(); // Find entries for this base name. - auto known = LookupTable.find(baseName); + auto known = Table.find(baseName); // If we found something, we're done. - if (known != LookupTable.end()) return known; + if (known != Table.end()) return known; // If there's no reader, we've found all there is to find. if (!Reader) return known; // Lookup this base name in the module file. SmallVector results; - (void)Reader->lookup(baseName, results); + create(results, *Reader, baseName); // Add an entry to the table so we don't look again. - known = LookupTable.insert({ std::move(baseName), std::move(results) }).first; + known = Table.insert({ std::move(baseName), std::move(results) }).first; return known; } @@ -554,7 +593,10 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -574,24 +616,53 @@ SwiftLookupTable::lookup(SerializedSwiftName baseName, } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { +SwiftLookupTable::lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext) { + SmallVector result; + + // Find entries for this base name. + auto known = findOrCreate(GlobalsAsMembers, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookupGlobalsAsMembers(Name, results); + }); + if (known == GlobalsAsMembers.end()) return result; + + + // Walk each of the entries. + for (auto &entry : known->second) { + // If we're looking in a particular context and it doesn't match the + // entry context, we're done. + if (searchContext && entry.Context != *searchContext) + continue; + + // Map each of the declarations. + for (auto &stored : entry.DeclsOrMacros) + if (auto entry = mapStored(stored)) + result.push_back(entry); + } + + return result; +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(StoredContext context) { SmallVector result; // Find entries for this base name. - auto known = GlobalsAsMembers.find(context); + auto known = GlobalsAsMembersIndex.find(context); // If we didn't find anything... - if (known == GlobalsAsMembers.end()) { + if (known == GlobalsAsMembersIndex.end()) { // If there's no reader, we've found all there is to find. if (!Reader) return result; // Lookup this base name in the module extension file. SmallVector results; - (void)Reader->lookupGlobalsAsMembers(context, results); + (void)Reader->lookupGlobalsAsMembersInContext(context, results); // Add an entry to the table so we don't look again. - known = GlobalsAsMembers.insert({ std::move(context), - std::move(results) }).first; + known = GlobalsAsMembersIndex.insert({ std::move(context), + std::move(results) }).first; } // Map each of the results. @@ -603,14 +674,26 @@ SwiftLookupTable::lookupGlobalsAsMembers(StoredContext context) { } SmallVector -SwiftLookupTable::lookupGlobalsAsMembers(EffectiveClangContext context) { - // Translate context. +SwiftLookupTable::lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext) { + // Propagate the null search context. + if (!searchContext) + return lookupGlobalsAsMembersImpl(baseName, None); + + Optional storedContext = translateContext(*searchContext); + if (!storedContext) return { }; + + return lookupGlobalsAsMembersImpl(baseName, *storedContext); +} + +SmallVector +SwiftLookupTable::allGlobalsAsMembersInContext(EffectiveClangContext context) { if (!context) return { }; Optional storedContext = translateContext(context); if (!storedContext) return { }; - return lookupGlobalsAsMembers(*storedContext); + return allGlobalsAsMembersInContext(*storedContext); } SmallVector @@ -618,13 +701,13 @@ SwiftLookupTable::allGlobalsAsMembers() { // If we have a reader, deserialize all of the globals-as-members data. if (Reader) { for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } // Collect all of the keys and sort them. SmallVector contexts; - for (const auto &globalAsMember : GlobalsAsMembers) { + for (const auto &globalAsMember : GlobalsAsMembersIndex) { contexts.push_back(globalAsMember.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -632,12 +715,25 @@ SwiftLookupTable::allGlobalsAsMembers() { // Collect all of the results in order. SmallVector results; for (const auto &context : contexts) { - for (auto &entry : GlobalsAsMembers[context]) + for (auto &entry : GlobalsAsMembersIndex[context]) results.push_back(mapStored(entry)); } return results; } +SmallVector +SwiftLookupTable::allGlobalsAsMembersBaseNames() { + // If we have a reader, enumerate its base names. + if (Reader) return Reader->getGlobalsAsMembersBaseNames(); + + // Otherwise, walk the lookup table. + SmallVector result; + for (const auto &entry : GlobalsAsMembers) { + result.push_back(entry.first); + } + return result; +} + SmallVector SwiftLookupTable::lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext) { @@ -668,7 +764,10 @@ SwiftLookupTable::lookupObjCMembers(SerializedSwiftName baseName) { SmallVector result; // Find the lookup table entry for this base name. - auto known = findOrCreate(baseName); + auto known = findOrCreate(LookupTable, baseName, + [](auto &results, auto &Reader, auto Name) { + return (void)Reader.lookup(Name, results); + }); if (known == LookupTable.end()) return result; // Walk each of the entries. @@ -770,10 +869,14 @@ void SwiftLookupTable::deserializeAll() { (void)lookup(baseName, None); } + for (auto baseName : Reader->getGlobalsAsMembersBaseNames()) { + (void)lookupGlobalsAsMembersImpl(baseName, None); + } + (void)categories(); for (auto context : Reader->getGlobalsAsMembersContexts()) { - (void)lookupGlobalsAsMembers(context); + (void)allGlobalsAsMembersInContext(context); } } @@ -838,89 +941,93 @@ static void printStoredEntry(const SwiftLookupTable *table, uint64_t entry, } void SwiftLookupTable::dump() const { + dump(llvm::errs()); +} + +void SwiftLookupTable::dump(raw_ostream &os) const { // Dump the base name -> full table entry mappings. SmallVector baseNames; for (const auto &entry : LookupTable) { baseNames.push_back(entry.first); } llvm::array_pod_sort(baseNames.begin(), baseNames.end()); - llvm::errs() << "Base name -> entry mappings:\n"; + os << "Base name -> entry mappings:\n"; for (auto baseName : baseNames) { switch (baseName.Kind) { case DeclBaseName::Kind::Normal: - llvm::errs() << " " << baseName.Name << ":\n"; + os << " " << baseName.Name << ":\n"; break; case DeclBaseName::Kind::Subscript: - llvm::errs() << " subscript:\n"; + os << " subscript:\n"; break; case DeclBaseName::Kind::Constructor: - llvm::errs() << " init:\n"; + os << " init:\n"; break; case DeclBaseName::Kind::Destructor: - llvm::errs() << " deinit:\n"; + os << " deinit:\n"; break; } const auto &entries = LookupTable.find(baseName)->second; for (const auto &entry : entries) { - llvm::errs() << " "; - printStoredContext(entry.Context, llvm::errs()); - llvm::errs() << ": "; + os << " "; + printStoredContext(entry.Context, os); + os << ": "; interleave(entry.DeclsOrMacros.begin(), entry.DeclsOrMacros.end(), - [this](uint64_t entry) { - printStoredEntry(this, entry, llvm::errs()); + [this, &os](uint64_t entry) { + printStoredEntry(this, entry, os); }, - [] { - llvm::errs() << ", "; + [&os] { + os << ", "; }); - llvm::errs() << "\n"; + os << "\n"; } } if (!Categories.empty()) { - llvm::errs() << "Categories: "; + os << "Categories: "; interleave(Categories.begin(), Categories.end(), - [](clang::ObjCCategoryDecl *category) { - llvm::errs() << category->getClassInterface()->getName() - << "(" << category->getName() << ")"; + [&os](clang::ObjCCategoryDecl *category) { + os << category->getClassInterface()->getName() + << "(" << category->getName() << ")"; }, - [] { - llvm::errs() << ", "; + [&os] { + os << ", "; }); - llvm::errs() << "\n"; + os << "\n"; } else if (Reader && !Reader->categories().empty()) { - llvm::errs() << "Categories: "; + os << "Categories: "; interleave(Reader->categories().begin(), Reader->categories().end(), - [](clang::serialization::DeclID declID) { - llvm::errs() << "decl ID #" << declID; + [&os](clang::serialization::DeclID declID) { + os << "decl ID #" << declID; }, - [] { - llvm::errs() << ", "; + [&os] { + os << ", "; }); - llvm::errs() << "\n"; + os << "\n"; } - if (!GlobalsAsMembers.empty()) { - llvm::errs() << "Globals-as-members mapping:\n"; + if (!GlobalsAsMembersIndex.empty()) { + os << "Globals-as-members mapping:\n"; SmallVector contexts; - for (const auto &entry : GlobalsAsMembers) { + for (const auto &entry : GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); for (auto context : contexts) { - llvm::errs() << " "; - printStoredContext(context, llvm::errs()); - llvm::errs() << ": "; + os << " "; + printStoredContext(context, os); + os << ": "; - const auto &entries = GlobalsAsMembers.find(context)->second; + const auto &entries = GlobalsAsMembersIndex.find(context)->second; interleave(entries.begin(), entries.end(), - [this](uint64_t entry) { - printStoredEntry(this, entry, llvm::errs()); + [this, &os](uint64_t entry) { + printStoredEntry(this, entry, os); }, - [] { - llvm::errs() << ", "; + [&os] { + os << ", "; }); - llvm::errs() << "\n"; + os << "\n"; } } } @@ -947,7 +1054,11 @@ namespace { /// Record that contains the mapping from contexts to the list of /// globals that will be injected as members into those contexts. - GLOBALS_AS_MEMBERS_RECORD_ID + GLOBALS_AS_MEMBERS_RECORD_ID, + + /// Record that contains the mapping from contexts to the list of + /// globals that will be injected as members into those contexts. + GLOBALS_AS_MEMBERS_INDEX_RECORD_ID, }; using BaseNameToEntitiesTableRecordLayout @@ -959,6 +1070,9 @@ namespace { using GlobalsAsMembersTableRecordLayout = BCRecordLayout, BCBlob>; + using GlobalsAsMembersIndexRecordLayout + = BCRecordLayout, BCBlob>; + /// Trait used to write the on-disk hash table for the base name -> entities /// mapping. class BaseNameToEntitiesTableWriterInfo { @@ -1204,9 +1318,39 @@ void SwiftLookupTableWriter::writeExtensionContents( // Write the globals-as-members table, if non-empty. if (!table.GlobalsAsMembers.empty()) { + // First, gather the sorted list of base names. + SmallVector baseNames; + for (const auto &entry : table.GlobalsAsMembers) + baseNames.push_back(entry.first); + llvm::array_pod_sort(baseNames.begin(), baseNames.end()); + + // Form the mapping from base names to entities with their context. + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + generator; + BaseNameToEntitiesTableWriterInfo info(table, Writer); + for (auto baseName : baseNames) + generator.insert(baseName, table.GlobalsAsMembers[baseName], info); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream, info); + } + + GlobalsAsMembersTableRecordLayout layout(stream); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + } + + // Write the globals-as-members index, if non-empty. + if (!table.GlobalsAsMembersIndex.empty()) { // Sort the keys. SmallVector contexts; - for (const auto &entry : table.GlobalsAsMembers) { + for (const auto &entry : table.GlobalsAsMembersIndex) { contexts.push_back(entry.first); } llvm::array_pod_sort(contexts.begin(), contexts.end()); @@ -1219,7 +1363,7 @@ void SwiftLookupTableWriter::writeExtensionContents( generator; GlobalsAsMembersTableWriterInfo info(table, Writer); for (auto context : contexts) - generator.insert(context, table.GlobalsAsMembers[context], info); + generator.insert(context, table.GlobalsAsMembersIndex[context], info); llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 @@ -1227,7 +1371,7 @@ void SwiftLookupTableWriter::writeExtensionContents( tableOffset = generator.Emit(blobStream, info); } - GlobalsAsMembersTableRecordLayout layout(stream); + GlobalsAsMembersIndexRecordLayout layout(stream); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } } @@ -1474,8 +1618,15 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, // Look for the base name -> entities table record. SmallVector scratch; auto cursor = stream; - auto next = cursor.advance(); + llvm::Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + llvm::BitstreamEntry next = maybeNext.get(); std::unique_ptr serializedTable; + std::unique_ptr globalsAsMembersIndex; std::unique_ptr globalsAsMembersTable; ArrayRef categories; while (next.Kind != llvm::BitstreamEntry::EndBlock) { @@ -1487,14 +1638,27 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, // API notes format. if (cursor.SkipBlock()) return nullptr; - - next = cursor.advance(); + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + unsigned kind = maybeKind.get(); switch (kind) { case BASE_NAME_TO_ENTITIES_RECORD_ID: { // Already saw base name -> entities table. @@ -1512,6 +1676,22 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, break; } + case GLOBALS_AS_MEMBERS_INDEX_RECORD_ID: { + // Already saw globals as members index. + if (globalsAsMembersIndex) + return nullptr; + + uint32_t tableOffset; + GlobalsAsMembersIndexRecordLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + globalsAsMembersIndex.reset( + SerializedGlobalsAsMembersIndex::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + case CATEGORIES_RECORD_ID: { // Already saw categories; input is malformed. if (!categories.empty()) return nullptr; @@ -1546,7 +1726,13 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return nullptr; + } + next = maybeNext.get(); } if (!serializedTable) return nullptr; @@ -1557,7 +1743,8 @@ SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, return std::unique_ptr( new SwiftLookupTableReader(extension, reader, moduleFile, onRemove, std::move(serializedTable), categories, - std::move(globalsAsMembersTable))); + std::move(globalsAsMembersTable), + std::move(globalsAsMembersIndex))); } @@ -1584,21 +1771,46 @@ bool SwiftLookupTableReader::lookup( SmallVector SwiftLookupTableReader::getGlobalsAsMembersContexts() { SmallVector results; - if (!GlobalsAsMembersTable) return results; + if (!GlobalsAsMembersIndex) return results; - for (auto key : GlobalsAsMembersTable->keys()) { + for (auto key : GlobalsAsMembersIndex->keys()) { results.push_back(key); } return results; } -bool SwiftLookupTableReader::lookupGlobalsAsMembers( +bool SwiftLookupTableReader::lookupGlobalsAsMembersInContext( SwiftLookupTable::StoredContext context, SmallVectorImpl &entries) { + if (!GlobalsAsMembersIndex) return false; + + // Look for an entry with this context name. + auto known = GlobalsAsMembersIndex->find(context); + if (known == GlobalsAsMembersIndex->end()) return false; + + // Grab the results. + entries = std::move(*known); + return true; +} + +SmallVector +SwiftLookupTableReader::getGlobalsAsMembersBaseNames() { + SmallVector results; + if (!GlobalsAsMembersTable) return {}; + + for (auto key : GlobalsAsMembersTable->keys()) { + results.push_back(key); + } + return results; +} + +bool SwiftLookupTableReader::lookupGlobalsAsMembers( + SerializedSwiftName baseName, + SmallVectorImpl &entries) { if (!GlobalsAsMembersTable) return false; // Look for an entry with this context name. - auto known = GlobalsAsMembersTable->find(context); + auto known = GlobalsAsMembersTable->find(baseName); if (known == GlobalsAsMembersTable->end()) return false; // Grab the results. @@ -1788,8 +2000,9 @@ void importer::addMacrosToLookupTable(SwiftLookupTable &table, } } -void importer::finalizeLookupTable(SwiftLookupTable &table, - NameImporter &nameImporter) { +void importer::finalizeLookupTable( + SwiftLookupTable &table, NameImporter &nameImporter, + ClangSourceBufferImporter &buffersForDiagnostics) { // Resolve any unresolved entries. SmallVector unresolved; if (table.resolveUnresolvedEntries(unresolved)) { @@ -1799,9 +2012,22 @@ void importer::finalizeLookupTable(SwiftLookupTable &table, auto swiftName = decl->getAttr(); if (swiftName) { - nameImporter.getContext().Diags.diagnose( - SourceLoc(), diag::unresolvable_clang_decl, decl->getNameAsString(), - swiftName->getName()); + clang::SourceLocation diagLoc = swiftName->getLocation(); + if (!diagLoc.isValid()) + diagLoc = decl->getLocation(); + SourceLoc swiftSourceLoc = buffersForDiagnostics.resolveSourceLocation( + nameImporter.getClangContext().getSourceManager(), diagLoc); + + DiagnosticEngine &swiftDiags = nameImporter.getContext().Diags; + swiftDiags.diagnose(swiftSourceLoc, diag::unresolvable_clang_decl, + decl->getNameAsString(), swiftName->getName()); + StringRef moduleName = + nameImporter.getClangContext().getLangOpts().CurrentModule; + if (!moduleName.empty()) { + swiftDiags.diagnose(swiftSourceLoc, + diag::unresolvable_clang_decl_is_a_framework_bug, + moduleName); + } } } } @@ -1842,14 +2068,15 @@ void SwiftLookupTableWriter::populateTable(SwiftLookupTable &table, addMacrosToLookupTable(table, nameImporter); // Finalize the lookup table, which may fail. - finalizeLookupTable(table, nameImporter); + finalizeLookupTable(table, nameImporter, buffersForDiagnostics); }; std::unique_ptr SwiftNameLookupExtension::createExtensionWriter(clang::ASTWriter &writer) { - return std::unique_ptr( - new SwiftLookupTableWriter(this, writer, swiftCtx, availability, - inferImportAsMember)); + return llvm::make_unique(this, writer, swiftCtx, + buffersForDiagnostics, + availability, + inferImportAsMember); } std::unique_ptr diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 168428bde0038..fe980625c0ff8 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -17,6 +17,7 @@ #ifndef SWIFT_CLANGIMPORTER_SWIFTLOOKUPTABLE_H #define SWIFT_CLANGIMPORTER_SWIFTLOOKUPTABLE_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/Identifier.h" #include "clang/AST/Decl.h" @@ -275,7 +276,7 @@ const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MAJOR = 1; /// Lookup table minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 15; // Special names +const uint16_t SWIFT_LOOKUP_TABLE_VERSION_MINOR = 16; // Lazy Member Loading Index /// A lookup table that maps Swift names to the set of Clang /// declarations with that particular name. @@ -384,10 +385,19 @@ class SwiftLookupTable { } private: + using TableType = + llvm::DenseMap>; + using CacheCallback = void(SmallVectorImpl &, + SwiftLookupTableReader &, + SerializedSwiftName); + /// A table mapping from the base name of Swift entities to all of /// the C entities that have that name, in all contexts. - llvm::DenseMap> - LookupTable; + TableType LookupTable; + + /// A table mapping the base names of Swift entities to all of the C entities + /// that are remapped to that name by the globals-as-members utility, in all contexts. + TableType GlobalsAsMembers; /// The list of Objective-C categories and extensions. llvm::SmallVector Categories; @@ -397,7 +407,7 @@ class SwiftLookupTable { /// /// The values use the same representation as /// FullTableEntry::DeclsOrMacros. - llvm::DenseMap> GlobalsAsMembers; + llvm::DenseMap> GlobalsAsMembersIndex; /// The reader responsible for lazily loading the contents of this table. SwiftLookupTableReader *Reader; @@ -412,7 +422,9 @@ class SwiftLookupTable { /// Find or create the table entry for the given base name. llvm::DenseMap>::iterator - findOrCreate(SerializedSwiftName baseName); + findOrCreate(TableType &table, + SerializedSwiftName baseName, + llvm::function_ref create); /// Add the given entry to the list of entries, if it's not already /// present. @@ -473,8 +485,22 @@ class SwiftLookupTable { llvm::Optional searchContext); /// Retrieve the set of global declarations that are going to be - /// imported as members into the given context. - SmallVector lookupGlobalsAsMembers(StoredContext context); + /// imported as the given Swift name into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. This may be None to indicate that + /// all results from all contexts should be produced. + SmallVector + lookupGlobalsAsMembersImpl(SerializedSwiftName baseName, + llvm::Optional searchContext); + + /// Retrieve the set of global declarations that are going to be imported as + /// members in the given context. + SmallVector + allGlobalsAsMembersInContext(StoredContext context); public: /// Lookup an unresolved context name and resolve it to a Clang @@ -487,14 +513,16 @@ class SwiftLookupTable { /// have this base name. /// /// \param searchContext The context in which the resulting set of - /// entities should reside. This may be None to indicate that - /// all results from all contexts should be produced. + /// entities should reside. SmallVector lookup(SerializedSwiftName baseName, EffectiveClangContext searchContext); /// Retrieve the set of base names that are stored in the lookup table. SmallVector allBaseNames(); + /// Retrieve the set of base names that are stored in the globals-as-members lookup table. + SmallVector allGlobalsAsMembersBaseNames(); + /// Lookup Objective-C members with the given base name, regardless /// of context. SmallVector @@ -505,21 +533,39 @@ class SwiftLookupTable { /// Retrieve the set of global declarations that are going to be /// imported as members into the given context. + /// + /// \param baseName The base name to search for. All results will + /// have this base name. + /// + /// \param searchContext The context in which the resulting set of + /// entities should reside. + SmallVector + lookupGlobalsAsMembers(SerializedSwiftName baseName, + Optional searchContext); + SmallVector - lookupGlobalsAsMembers(EffectiveClangContext context); + allGlobalsAsMembersInContext(EffectiveClangContext context); /// Retrieve the set of global declarations that are going to be /// imported as members. SmallVector allGlobalsAsMembers(); + /// Retrieve the set of base names that are going to be imported as members in the + /// given context. + SmallVector + globalsAsMembersBaseNames(EffectiveClangContext context); + /// Deserialize all entries. void deserializeAll(); /// Dump the internal representation of this lookup table. - void dump() const; + SWIFT_DEBUG_DUMP; + + void dump(llvm::raw_ostream &os) const; }; namespace importer { +class ClangSourceBufferImporter; class NameImporter; /// Add the given named declaration as an entry to the given Swift name @@ -533,7 +579,8 @@ void addMacrosToLookupTable(SwiftLookupTable &table, NameImporter &); /// Finalize a lookup table, handling any as-yet-unresolved entries /// and emitting diagnostics if necessary. -void finalizeLookupTable(SwiftLookupTable &table, NameImporter &); +void finalizeLookupTable(SwiftLookupTable &table, NameImporter &, + ClangSourceBufferImporter &buffersForDiagnostics); } } diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index f6b5c3d4a8f07..90a2304df0064 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -412,7 +412,7 @@ void NodeFactory::clear() { assert(!isBorrowed); if (CurrentSlab) { #ifdef NODE_FACTORY_DEBUGGING - std::cerr << indent() << "## clear: allocated memory = " << allocatedMemory << "\n"; + fprintf(stderr, "%s## clear: allocated memory = %zu\n", indent().c_str(), allocatedMemory); #endif freeSlabs(CurrentSlab->Previous); @@ -529,7 +529,7 @@ NodePointer Demangler::demangleSymbol(StringRef MangledName, // Demangle old-style class and protocol names, which are still used in the // ObjC metadata. if (nextIf("_Tt")) - return demangleObjCTypeName(); + return demangleOldSymbolAsNode(Text, *this); unsigned PrefixLength = getManglingPrefixLength(MangledName); if (PrefixLength == 0) @@ -1733,6 +1733,24 @@ NodePointer Demangler::demangleImplResultConvention(Node::Kind ConvKind) { NodePointer Demangler::demangleImplFunctionType() { NodePointer type = createNode(Node::Kind::ImplFunctionType); + if (nextIf('s')) { + Vector Substitutions; + NodePointer SubstitutionRetroConformances; + if (!demangleBoundGenerics(Substitutions, SubstitutionRetroConformances)) + return nullptr; + + bool isImplied = !nextIf('i'); + + auto subsNode = createNode(isImplied + ? Node::Kind::ImplImpliedSubstitutions + : Node::Kind::ImplSubstitutions); + assert(Substitutions.size() == 1); + subsNode->addChild(Substitutions[0], *this); + if (SubstitutionRetroConformances) + subsNode->addChild(SubstitutionRetroConformances, *this); + type->addChild(subsNode, *this); + } + NodePointer GenSig = popNode(Node::Kind::DependentGenericSignature); if (GenSig && nextIf('P')) GenSig = changeKind(GenSig, Node::Kind::DependentPseudogenericSignature); @@ -1794,6 +1812,7 @@ NodePointer Demangler::demangleImplFunctionType() { return nullptr; type->getChild(type->getNumChildren() - Idx - 1)->addChild(ConvTy, *this); } + return createType(type); } @@ -2339,7 +2358,7 @@ std::string Demangler::demangleBridgedMethodParams() { while (!nextIf('_')) { auto c = nextChar(); - if (!c && c != 'n' && c != 'b') + if (c != 'n' && c != 'b' && c != 'g') return std::string(); Str.push_back(c); } @@ -3193,45 +3212,3 @@ NodePointer Demangler::demangleValueWitness() { addChild(VW, createNode(Node::Kind::Index, unsigned(Kind))); return addChild(VW, popNode(Node::Kind::Type)); } - -NodePointer Demangler::demangleObjCTypeName() { - NodePointer Ty = createNode(Node::Kind::Type); - NodePointer Global = addChild(createNode(Node::Kind::Global), - addChild(createNode(Node::Kind::TypeMangling), Ty)); - NodePointer Nominal = nullptr; - bool isProto = false; - if (nextIf('C')) { - Nominal = createNode(Node::Kind::Class); - addChild(Ty, Nominal); - } else if (nextIf('P')) { - isProto = true; - Nominal = createNode(Node::Kind::Protocol); - addChild(Ty, addChild(createNode(Node::Kind::ProtocolList), - addChild(createNode(Node::Kind::TypeList), - addChild(createNode(Node::Kind::Type), Nominal)))); - } else { - return nullptr; - } - - if (nextIf('s')) { - Nominal->addChild(createNode(Node::Kind::Module, "Swift"), *this); - } else { - NodePointer Module = demangleIdentifier(); - if (!Module) - return nullptr; - Nominal->addChild(changeKind(Module, Node::Kind::Module), *this); - } - - NodePointer Ident = demangleIdentifier(); - if (!Ident) - return nullptr; - Nominal->addChild(Ident, *this); - - if (isProto && !nextIf('_')) - return nullptr; - - if (Pos < Text.size()) - return nullptr; - - return Global; -} diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 162b7ecc46132..94a5247a7063c 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -390,6 +390,8 @@ class NodePrinter { case Node::Kind::ImplConvention: case Node::Kind::ImplFunctionAttribute: case Node::Kind::ImplFunctionType: + case Node::Kind::ImplImpliedSubstitutions: + case Node::Kind::ImplSubstitutions: case Node::Kind::ImplicitClosure: case Node::Kind::ImplParameter: case Node::Kind::ImplResult: @@ -979,7 +981,7 @@ static bool needSpaceBeforeType(NodePointer Type) { } NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { - switch (Node->getKind()) { + switch (auto kind = Node->getKind()) { case Node::Kind::Static: Printer << "static "; print(Node->getChild(0)); @@ -1487,7 +1489,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->hasChildren()) { Printer << " for "; - print(Node->getFirstChild()); + printChildren(Node); } return nullptr; case Node::Kind::PartialApplyObjCForwarder: @@ -1498,7 +1500,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { if (Node->hasChildren()) { Printer << " for "; - print(Node->getFirstChild()); + printChildren(Node); } return nullptr; case Node::Kind::KeyPathGetterThunkHelper: @@ -2016,6 +2018,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::ImplFunctionType: printImplFunctionType(Node); return nullptr; + case Node::Kind::ImplSubstitutions: + case Node::Kind::ImplImpliedSubstitutions: + Printer << "for <"; + printChildren(Node->getChild(0), ", "); + Printer << '>'; + if (kind == Node::Kind::ImplImpliedSubstitutions) + Printer << " in"; + return nullptr; case Node::Kind::ErrorType: Printer << ""; return nullptr; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 4a6a669683524..26ed34e4b6abd 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1252,6 +1252,14 @@ void Remangler::mangleImplEscaping(Node *node) { // The old mangler does not encode escaping. } +void Remangler::mangleImplSubstitutions(Node *node) { + // The old mangler does not encode substituted function types. +} + +void Remangler::mangleImplImpliedSubstitutions(Node *node) { + // The old mangler does not encode substituted function types. +} + void Remangler::mangleImplConvention(Node *node) { assert(node->getKind() == Node::Kind::ImplConvention); StringRef text = node->getText(); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index ecb972dcc0006..18c880eb43e50 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -487,6 +487,12 @@ void Remangler::mangleAnyNominalType(Node *node) { void Remangler::mangleGenericArgs(Node *node, char &Separator, bool fullSubstitutionMap) { switch (node->getKind()) { + case Node::Kind::Protocol: + // A protocol cannot be the parent of a nominal type, so this case should + // never be hit by valid swift code. But the indexer might generate a URL + // from invalid swift code, which has a bound generic inside a protocol. + // The ASTMangler treats a protocol like any other nominal type in this + // case, so we also support it in the remangler. case Node::Kind::Structure: case Node::Kind::Enum: case Node::Kind::Class: @@ -1390,30 +1396,59 @@ void Remangler::mangleImplFunctionAttribute(Node *node) { unreachable("handled inline"); } +void Remangler::mangleImplSubstitutions(Node *node) { + unreachable("handled inline"); +} + +void Remangler::mangleImplImpliedSubstitutions(Node *node) { + unreachable("handled inline"); +} + void Remangler::mangleImplFunctionType(Node *node) { const char *PseudoGeneric = ""; Node *GenSig = nullptr; + Node *GenSubs = nullptr; + bool isImplied; for (NodePointer Child : *node) { - switch (Child->getKind()) { - case Node::Kind::ImplParameter: - case Node::Kind::ImplResult: - case Node::Kind::ImplErrorResult: - mangleChildNode(Child, 1); - break; - case Node::Kind::DependentPseudogenericSignature: - PseudoGeneric = "P"; - LLVM_FALLTHROUGH; - case Node::Kind::DependentGenericSignature: - GenSig = Child; - break; - default: - break; + switch (auto kind = Child->getKind()) { + case Node::Kind::ImplParameter: + case Node::Kind::ImplResult: + case Node::Kind::ImplErrorResult: + mangleChildNode(Child, 1); + break; + case Node::Kind::DependentPseudogenericSignature: + PseudoGeneric = "P"; + LLVM_FALLTHROUGH; + case Node::Kind::DependentGenericSignature: + GenSig = Child; + break; + case Node::Kind::ImplSubstitutions: + case Node::Kind::ImplImpliedSubstitutions: + GenSubs = Child; + isImplied = kind == Node::Kind::ImplImpliedSubstitutions; + break; + default: + break; } } if (GenSig) mangle(GenSig); + if (GenSubs) { + Buffer << 'y'; + mangleChildNodes(GenSubs->getChild(0)); + if (GenSubs->getNumChildren() >= 2) + mangleRetroactiveConformance(GenSubs->getChild(1)); + } + + Buffer << 'I'; + + if (GenSubs) { + Buffer << 's'; + if (!isImplied) + Buffer << 'i'; + } - Buffer << 'I' << PseudoGeneric; + Buffer << PseudoGeneric; for (NodePointer Child : *node) { switch (Child->getKind()) { case Node::Kind::ImplEscaping: diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index ed21de8722ad2..448f5044b7064 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,10 +1,12 @@ set(swiftDriver_sources Action.cpp + CoarseGrainedDependencyGraph.cpp Compilation.cpp DarwinToolChains.cpp - DependencyGraph.cpp + SourceComparator.cpp Driver.cpp - ExperimentalDependencyDriverGraph.cpp + DriverIncrementalRanges.cpp + FineGrainedDependencyDriverGraph.cpp FrontendUtil.cpp Job.cpp ParseableOutput.cpp diff --git a/lib/Driver/DependencyGraph.cpp b/lib/Driver/CoarseGrainedDependencyGraph.cpp similarity index 87% rename from lib/Driver/DependencyGraph.cpp rename to lib/Driver/CoarseGrainedDependencyGraph.cpp index 09f382fcb5c83..476006f922213 100644 --- a/lib/Driver/DependencyGraph.cpp +++ b/lib/Driver/CoarseGrainedDependencyGraph.cpp @@ -1,4 +1,4 @@ -//===--- DependencyGraph.cpp - Track intra-module dependencies ------------===// +//===- CoarseGrainedDependencyGraph.cpp - Track intra-module dependencies -===// // // This source file is part of the Swift.org open source project // @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// +#include "swift/Driver/CoarseGrainedDependencyGraph.h" #include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Basic/Statistic.h" -#include "swift/Driver/DependencyGraph.h" #include "swift/Demangling/Demangle.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -25,30 +25,31 @@ using namespace swift; -enum class DependencyGraphImpl::DependencyKind : uint8_t { +enum class CoarseGrainedDependencyGraphImpl::DependencyKind : uint8_t { TopLevelName = 1 << 0, DynamicLookupName = 1 << 1, NominalType = 1 << 2, NominalTypeMember = 1 << 3, ExternalFile = 1 << 4, }; -enum class DependencyGraphImpl::DependencyFlags : uint8_t { +enum class CoarseGrainedDependencyGraphImpl::DependencyFlags : uint8_t { IsCascading = 1 << 0 }; -class DependencyGraphImpl::MarkTracerImpl::Entry { +class CoarseGrainedDependencyGraphImpl::MarkTracerImpl::Entry { public: const void *Node; StringRef Name; DependencyMaskTy KindMask; }; -DependencyGraphImpl::MarkTracerImpl::MarkTracerImpl(UnifiedStatsReporter *Stats) - : Stats(Stats) {} -DependencyGraphImpl::MarkTracerImpl::~MarkTracerImpl() = default; +CoarseGrainedDependencyGraphImpl::MarkTracerImpl::MarkTracerImpl( + UnifiedStatsReporter *Stats) + : Stats(Stats) {} +CoarseGrainedDependencyGraphImpl::MarkTracerImpl::~MarkTracerImpl() = default; -using LoadResult = DependencyGraphImpl::LoadResult; -using DependencyKind = DependencyGraphImpl::DependencyKind; +using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; +using DependencyKind = CoarseGrainedDependencyGraphImpl::DependencyKind; using DependencyCallbackTy = LoadResult(StringRef, DependencyKind, bool); using InterfaceHashCallbackTy = LoadResult(StringRef); @@ -211,21 +212,23 @@ parseDependencyFile(llvm::MemoryBuffer &buffer, return result; } -LoadResult DependencyGraphImpl::loadFromPath(const void *node, StringRef path) { +LoadResult CoarseGrainedDependencyGraphImpl::loadFromPath(const void *node, + StringRef path) { auto buffer = llvm::MemoryBuffer::getFile(path); if (!buffer) return LoadResult::HadError; return loadFromBuffer(node, *buffer.get()); } -LoadResult -DependencyGraphImpl::loadFromString(const void *node, StringRef data) { +LoadResult CoarseGrainedDependencyGraphImpl::loadFromString(const void *node, + StringRef data) { auto buffer = llvm::MemoryBuffer::getMemBuffer(data); return loadFromBuffer(node, *buffer); } -LoadResult DependencyGraphImpl::loadFromBuffer(const void *node, - llvm::MemoryBuffer &buffer) { +LoadResult +CoarseGrainedDependencyGraphImpl::loadFromBuffer(const void *node, + llvm::MemoryBuffer &buffer) { auto &provides = Provides[node]; auto dependsCallback = [this, node](StringRef name, DependencyKind kind, @@ -294,26 +297,38 @@ LoadResult DependencyGraphImpl::loadFromBuffer(const void *node, interfaceHashCallback); } -void DependencyGraphImpl::markExternal(SmallVectorImpl &visited, - StringRef externalDependency) { - auto allDependents = Dependencies.find(externalDependency); +std::vector CoarseGrainedDependencyGraphImpl::markExternal( + StringRef externalDependency) { + std::vector visited; + forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( + externalDependency, [&](const void *node) { + visited.push_back(node); + for (const void* markedNode: markTransitive(node)) + visited.push_back(markedNode); + }); + return visited; +} + +void CoarseGrainedDependencyGraphImpl:: + forEachUnmarkedJobDirectlyDependentOnExternalSwiftdeps( + StringRef externalSwiftDeps, function_ref fn) { + auto allDependents = Dependencies.find(externalSwiftDeps); assert(allDependents != Dependencies.end() && "not a dependency!"); allDependents->second.second |= DependencyKind::ExternalFile; - for (const auto &dependent : allDependents->second.first) { if (!dependent.kindMask.contains(DependencyKind::ExternalFile)) continue; if (isMarked(dependent.node)) continue; assert(dependent.flags & DependencyFlags::IsCascading); - visited.push_back(dependent.node); - markTransitive(visited, dependent.node); + fn(dependent.node); } } -void -DependencyGraphImpl::markTransitive(SmallVectorImpl &visited, - const void *node, MarkTracerImpl *tracer) { +std::vector CoarseGrainedDependencyGraphImpl::markTransitive( + const void *node, + MarkTracerImpl *tracer) { + std::vector visited; assert(Provides.count(node) && "node is not in the graph"); llvm::SpecificBumpPtrAllocator scratchAlloc; @@ -397,9 +412,10 @@ DependencyGraphImpl::markTransitive(SmallVectorImpl &visited, continue; record(next); } + return visited; } -void DependencyGraphImpl::MarkTracerImpl::countStatsForNodeMarking( +void CoarseGrainedDependencyGraphImpl::MarkTracerImpl::countStatsForNodeMarking( const OptionSet &Kind, bool IsCascading) const { if (!Stats) @@ -431,7 +447,7 @@ void DependencyGraphImpl::MarkTracerImpl::countStatsForNodeMarking( } } -void DependencyGraphImpl::MarkTracerImpl::printPath( +void CoarseGrainedDependencyGraphImpl::MarkTracerImpl::printPath( raw_ostream &out, const void *item, llvm::function_ref printItem) const { diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 4b60fa5727434..8e6dd57c24c5e 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -14,7 +14,7 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsDriver.h" -#include "swift/AST/ExperimentalDependencies.h" +#include "swift/AST/FineGrainedDependencies.h" #include "swift/Basic/OutputFileMap.h" #include "swift/Basic/Program.h" #include "swift/Basic/STLExtras.h" @@ -23,9 +23,10 @@ #include "swift/Basic/Version.h" #include "swift/Basic/type_traits.h" #include "swift/Driver/Action.h" -#include "swift/Driver/DependencyGraph.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" #include "swift/Driver/Driver.h" -#include "swift/Driver/ExperimentalDependencyDriverGraph.h" +#include "swift/Driver/DriverIncrementalRanges.h" +#include "swift/Driver/FineGrainedDependencyDriverGraph.h" #include "swift/Driver/Job.h" #include "swift/Driver/ParseableOutput.h" #include "swift/Driver/ToolChain.h" @@ -47,6 +48,7 @@ #include "CompilationRecord.h" +#include #include #define DEBUG_TYPE "batch-mode" @@ -120,10 +122,15 @@ Compilation::Compilation(DiagnosticEngine &Diags, bool SaveTemps, bool ShowDriverTimeCompilation, std::unique_ptr StatsReporter, - bool EnableExperimentalDependencies, - bool VerifyExperimentalDependencyGraphAfterEveryImport, - bool EmitExperimentalDependencyDotFileAfterEveryImport, - bool ExperimentalDependenciesIncludeIntrafileOnes) + bool OnlyOneDependencyFile, + bool EnableFineGrainedDependencies, + bool EnableTypeFingerprints, + bool VerifyFineGrainedDependencyGraphAfterEveryImport, + bool EmitFineGrainedDependencyDotFileAfterEveryImport, + bool FineGrainedDependenciesIncludeIntrafileOnes, + bool EnableSourceRangeDependencies, + bool CompareIncrementalSchemes, + StringRef CompareIncrementalSchemesPath) : Diags(Diags), TheToolChain(TC), TheOutputInfo(OI), Level(Level), @@ -145,21 +152,29 @@ Compilation::Compilation(DiagnosticEngine &Diags, ShowDriverTimeCompilation(ShowDriverTimeCompilation), Stats(std::move(StatsReporter)), FilelistThreshold(FilelistThreshold), - EnableExperimentalDependencies(EnableExperimentalDependencies), - VerifyExperimentalDependencyGraphAfterEveryImport( - VerifyExperimentalDependencyGraphAfterEveryImport), - EmitExperimentalDependencyDotFileAfterEveryImport( - EmitExperimentalDependencyDotFileAfterEveryImport), - ExperimentalDependenciesIncludeIntrafileOnes( - ExperimentalDependenciesIncludeIntrafileOnes) { - + OnlyOneDependencyFile(OnlyOneDependencyFile), + EnableFineGrainedDependencies(EnableFineGrainedDependencies), + EnableTypeFingerprints(EnableTypeFingerprints), + VerifyFineGrainedDependencyGraphAfterEveryImport( + VerifyFineGrainedDependencyGraphAfterEveryImport), + EmitFineGrainedDependencyDotFileAfterEveryImport( + EmitFineGrainedDependencyDotFileAfterEveryImport), + FineGrainedDependenciesIncludeIntrafileOnes( + FineGrainedDependenciesIncludeIntrafileOnes), + EnableSourceRangeDependencies(EnableSourceRangeDependencies) + { + if (CompareIncrementalSchemes) + IncrementalComparator.emplace( + // Ensure the references are to inst vars, NOT arguments + this->EnableIncrementalBuild, + EnableSourceRangeDependencies, + CompareIncrementalSchemesPath, countSwiftInputs(), getDiags()); }; // clang-format on static bool writeFilelistIfNecessary(const Job *job, const ArgList &args, DiagnosticEngine &diags); -using CommandSet = llvm::SmallPtrSet; using CommandSetVector = llvm::SetVector; using BatchPartition = std::vector>; @@ -217,25 +232,31 @@ namespace driver { /// Jobs that incremental-mode has decided it can skip. CommandSet DeferredCommands; - - /// Jobs in the initial set with Condition::Always, and having an existing - /// .swiftdeps files. - /// Set by scheduleInitialJobs and used only by scheduleAdditionalJobs. - SmallVector InitialCascadingCommands; - public: - /// Dependency graph for deciding which jobs are dirty (need running) + /// Why are we keeping four dependency graphs? + /// One dimension for standard vs fine-grained dependencies. + /// The other dimension because we want to compare what dependency-based + /// incrementalism would do vs range-based incrementalism. Unfortuneatly, + /// the dependency graph includes marks that record if a node (Job) has ever + /// been traversed (i.e. marked for cascading). So, in order to find + /// externally-dependent jobs for range based incrementality, the + /// range-based computation needs its own graph when both strategies are + /// used for comparison purposes. Sigh. + /// + /// Dependency graphs for deciding which jobs are dirty (need running) /// or clean (can be skipped). - using DependencyGraph = DependencyGraph; - DependencyGraph StandardDepGraph; + using CoarseGrainedDependencyGraph = + swift::CoarseGrainedDependencyGraph; + CoarseGrainedDependencyGraph CoarseGrainedDepGraph; + CoarseGrainedDependencyGraph CoarseGrainedDepGraphForRanges; - /// Experimental Dependency graph for finer-grained dependencies - Optional ExpDepGraph; + fine_grained_dependencies::ModuleDepGraph FineGrainedDepGraph; + fine_grained_dependencies::ModuleDepGraph FineGrainedDepGraphForRanges; private: /// Helper for tracing the propagation of marks in the graph. - DependencyGraph::MarkTracer ActualIncrementalTracer; - DependencyGraph::MarkTracer *IncrementalTracer = nullptr; + CoarseGrainedDependencyGraph::MarkTracer ActualIncrementalTracer; + CoarseGrainedDependencyGraph::MarkTracer *IncrementalTracer = nullptr; /// TaskQueue for execution. std::unique_ptr TQ; @@ -251,15 +272,29 @@ namespace driver { llvm::SmallDenseMap, 16> DriverTimers; - void noteBuilding(const Job *cmd, StringRef reason) { + void noteBuilding(const Job *cmd, const bool willBeBuilding, + const bool isTentative, const bool forRanges, + StringRef reason) const { if (!Comp.getShowIncrementalBuildDecisions()) return; if (ScheduledCommands.count(cmd)) return; - llvm::outs() << "Queuing " << reason << ": " << LogJob(cmd) << "\n"; - - if (Comp.getEnableExperimentalDependencies()) - ExpDepGraph.getValue().printPath(llvm::outs(), cmd); + if (!Comp.getEnableSourceRangeDependencies() && + !Comp.IncrementalComparator && !willBeBuilding) + return; // preserve legacy behavior + const bool isHypothetical = + Comp.getEnableSourceRangeDependencies() != forRanges; + llvm::outs() << (isHypothetical ? "Hypothetically: " : "") + << (isTentative ? "(tentatively) " : "") + << (willBeBuilding ? "Queuing " : "Skipping ") + << (forRanges ? " " + : Comp.getEnableSourceRangeDependencies() + ? " " + : "") + << reason << ": " << LogJob(cmd) << "\n"; + + if (Comp.getEnableFineGrainedDependencies()) + getFineGrainedDepGraph(forRanges).printPath(llvm::outs(), cmd); else IncrementalTracer->printPath( llvm::outs(), cmd, [](raw_ostream &out, const Job *base) { @@ -267,6 +302,23 @@ namespace driver { }); } + template + void noteBuildingJobs(const JobsCollection &unsortedJobsArg, + const bool forRanges, const StringRef reason) const { + if (!Comp.getShowIncrementalBuildDecisions() && + !Comp.getShowJobLifecycle()) + return; + // Sigh, must manually convert SmallPtrSet to ArrayRef-able container + llvm::SmallVector unsortedJobs; + for (const Job *j : unsortedJobsArg) + unsortedJobs.push_back(j); + llvm::SmallVector sortedJobs; + Comp.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); + for (const Job *j : sortedJobs) + noteBuilding(j, /*willBeBuilding=*/true, /*isTentative=*/false, + forRanges, reason); + } + const Job *findUnfinishedJob(ArrayRef JL) { for (const Job *Cmd : JL) { if (!FinishedCommands.count(Cmd)) @@ -305,6 +357,15 @@ namespace driver { PendingExecution.insert(Cmd); } + // Sort for ease of testing + template + void scheduleCommandsInSortedOrder(const Jobs &jobs) { + llvm::SmallVector sortedJobs; + Comp.sortJobsToMatchCompilationInputs(jobs, sortedJobs); + for (const Job *Cmd : sortedJobs) + scheduleCommandIfNecessaryAndPossible(Cmd); + } + void addPendingJobToTaskQueue(const Job *Cmd) { // FIXME: Failing here should not take down the whole process. bool success = @@ -343,8 +404,7 @@ namespace driver { << LogJobArray(AllBlocked) << "\n"; } BlockingCommands.erase(BlockedIter); - for (auto *Blocked : AllBlocked) - scheduleCommandIfNecessaryAndPossible(Blocked); + scheduleCommandsInSortedOrder(AllBlocked); } } @@ -398,22 +458,19 @@ namespace driver { Comp.getDiags().diagnose(SourceLoc(), diag::warn_unable_to_load_dependencies, DependenciesFile); - Comp.disableIncrementalBuild(); - for (const Job *Cmd : DeferredCommands) - scheduleCommandIfNecessaryAndPossible(Cmd); - DeferredCommands.clear(); + Comp.disableIncrementalBuild( + Twine("malformed swift dependencies file '") + DependenciesFile + + "'"); } /// Helper that attempts to reload a job's .swiftdeps file after the job /// exits, and re-run transitive marking to ensure everything is properly /// invalidated by any new dependency edges introduced by it. If reloading /// fails, this can cause deferred jobs to be immediately scheduled. - template - void reloadAndRemarkDeps(const Job *FinishedCmd, - int ReturnCode, - SmallVector &Dependents, - DependencyGraphT &DepGraph) { + std::vector + reloadAndRemarkDeps(const Job *FinishedCmd, int ReturnCode, + const bool forRanges) { const CommandOutput &Output = FinishedCmd->getOutput(); StringRef DependenciesFile = Output.getAdditionalOutputForType(file_types::TY_SwiftDeps); @@ -426,73 +483,125 @@ namespace driver { // not using either of those right now, and this logic should probably // be revisited when we are. assert(FinishedCmd->getCondition() == Job::Condition::Always); - } else { - // If we have a dependency file /and/ the frontend task exited normally, - // we can be discerning about what downstream files to rebuild. - if (ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE) { - // "Marked" means that everything provided by this node (i.e. Job) is - // dirty. Thus any file using any of these provides must be - // recompiled. (Only non-private entities are output as provides.) In - // other words, this Job "cascades"; the need to recompile it causes - // other recompilations. It is possible that the current code marks - // things that do not need to be marked. Unecessary compilation would - // result if that were the case. - bool wasCascading = DepGraph.isMarked(FinishedCmd); - - switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile, - Comp.getDiags())) { - case DependencyGraphImpl::LoadResult::HadError: - if (ReturnCode == EXIT_SUCCESS) { - dependencyLoadFailed(DependenciesFile); - Dependents.clear(); - } // else, let the next build handle it. - break; - case DependencyGraphImpl::LoadResult::UpToDate: - if (!wasCascading) - break; - LLVM_FALLTHROUGH; - case DependencyGraphImpl::LoadResult::AffectsDownstream: - DepGraph.markTransitive(Dependents, FinishedCmd, - IncrementalTracer); - break; - } - } else { - // If there's an abnormal exit (a crash), assume the worst. - switch (FinishedCmd->getCondition()) { - case Job::Condition::NewlyAdded: - // The job won't be treated as newly added next time. Conservatively - // mark it as affecting other jobs, because some of them may have - // completed already. - DepGraph.markTransitive(Dependents, FinishedCmd, - IncrementalTracer); - break; - case Job::Condition::Always: - // Any incremental task that shows up here has already been marked; - // we didn't need to wait for it to finish to start downstream - // tasks. - assert(DepGraph.isMarked(FinishedCmd)); - break; - case Job::Condition::RunWithoutCascading: - // If this file changed, it might have been a non-cascading change - // and it might not. Unfortunately, the interface hash has been - // updated or compromised, so we don't actually know anymore; we - // have to conservatively assume the changes could affect other - // files. - DepGraph.markTransitive(Dependents, FinishedCmd, - IncrementalTracer); - break; - case Job::Condition::CheckDependencies: - // If the only reason we're running this is because something else - // changed, then we can trust the dependency graph as to whether - // it's a cascading or non-cascading change. That is, if whatever - // /caused/ the error isn't supposed to affect other files, and - // whatever /fixes/ the error isn't supposed to affect other files, - // then there's no need to recompile any other inputs. If either of - // those are false, we /do/ need to recompile other inputs. - break; - } - } + return {}; + } + const bool compileExitedNormally = + ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE; + return !compileExitedNormally + ? reloadAndRemarkDepsOnAbnormalExit(FinishedCmd, forRanges) + : reloadAndRemarkDepsOnNormalExit(FinishedCmd, /*cmdFailed=*/ + ReturnCode != EXIT_SUCCESS, + forRanges, DependenciesFile); + } + + // If we have a dependency file /and/ the frontend task exited normally, + // we can be discerning about what downstream files to rebuild. + std::vector + reloadAndRemarkDepsOnNormalExit(const Job *FinishedCmd, + const bool cmdFailed, const bool forRanges, + StringRef DependenciesFile) { + return Comp.getEnableFineGrainedDependencies() + ? reloadAndRemarkFineGrainedDepsOnNormalExit( + FinishedCmd, cmdFailed, forRanges, DependenciesFile) + : reloadAndRemarkCoarseGrainedDepsOnNormalExit( + FinishedCmd, cmdFailed, forRanges, DependenciesFile); + } + + std::vector reloadAndRemarkCoarseGrainedDepsOnNormalExit( + const Job *FinishedCmd, const bool cmdFailed, const bool forRanges, + StringRef DependenciesFile) { + assert(!Comp.getEnableFineGrainedDependencies() && + "Only for coarse-grained"); + // "Marked" means that everything provided by this node (i.e. Job) is + // dirty. Thus any file using any of these provides must be + // recompiled. (Only non-private entities are output as provides.) In + // other words, this Job "cascades"; the need to recompile it causes + // other recompilations. It is possible that the current code marks + // things that do not need to be marked. Unecessary compilation would + // result if that were the case. + bool wasMarkedBeforeReload = getDepGraph(forRanges).isMarked(FinishedCmd); + + const auto loadResult = getDepGraph(forRanges).loadFromPath( + FinishedCmd, DependenciesFile, Comp.getDiags()); + using LoadResult = CoarseGrainedDependencyGraph::LoadResult; + const bool loadFailed = loadResult == LoadResult::HadError; + if (loadFailed) { + handleDependenciesReloadFailure(cmdFailed, DependenciesFile); + return {}; + } + if (loadResult == LoadResult::UpToDate && !wasMarkedBeforeReload) + return {}; + return getDepGraph(forRanges).markTransitive(FinishedCmd, + IncrementalTracer); + } + + std::vector reloadAndRemarkFineGrainedDepsOnNormalExit( + const Job *FinishedCmd, const bool cmdFailed, const bool forRanges, + StringRef DependenciesFile) { + assert(Comp.getEnableFineGrainedDependencies() && + "Only for fine-grained"); + const auto changedNodes = getFineGrainedDepGraph(forRanges).loadFromPath( + FinishedCmd, DependenciesFile, Comp.getDiags()); + const bool loadFailed = !changedNodes; + if (loadFailed) { + handleDependenciesReloadFailure(cmdFailed, DependenciesFile); + return {}; } + return getFineGrainedDepGraph(forRanges) + .findJobsToRecompileWhenNodesChange(changedNodes.getValue()); + } + + void handleDependenciesReloadFailure(const bool cmdFailed, + const StringRef DependenciesFile) { + if (cmdFailed) { + // let the next build handle it. + return; + } + dependencyLoadFailed(DependenciesFile); + // Better try compiling whatever was waiting on more info. + for (const Job *Cmd : DeferredCommands) + scheduleCommandIfNecessaryAndPossible(Cmd); + DeferredCommands.clear(); + }; + + std::vector + reloadAndRemarkDepsOnAbnormalExit(const Job *FinishedCmd, + const bool forRanges) { + // If there's an abnormal exit (a crash), assume the worst. + switch (FinishedCmd->getCondition()) { + case Job::Condition::NewlyAdded: + // The job won't be treated as newly added next time. Conservatively + // mark it as affecting other jobs, because some of them may have + // completed already. + return findJobsToRecompileWhenWholeJobChanges(FinishedCmd, forRanges, + IncrementalTracer); + case Job::Condition::Always: + // Any incremental task that shows up here has already been marked; + // we didn't need to wait for it to finish to start downstream + // tasks. + if (!Comp.getEnableFineGrainedDependencies()) + assert(getDepGraph(forRanges).isMarked(FinishedCmd)); + break; + case Job::Condition::RunWithoutCascading: + // If this file changed, it might have been a non-cascading change + // and it might not. Unfortunately, the interface hash has been + // updated or compromised, so we don't actually know anymore; we + // have to conservatively assume the changes could affect other + // files. + return findJobsToRecompileWhenWholeJobChanges(FinishedCmd, forRanges, + IncrementalTracer); + + case Job::Condition::CheckDependencies: + // If the only reason we're running this is because something else + // changed, then we can trust the dependency graph as to whether + // it's a cascading or non-cascading change. That is, if whatever + // /caused/ the error isn't supposed to affect other files, and + // whatever /fixes/ the error isn't supposed to affect other files, + // then there's no need to recompile any other inputs. If either of + // those are false, we /do/ need to recompile other inputs. + break; + } + return {}; } /// Check to see if a job produced a zero-length serialized diagnostics @@ -577,30 +686,15 @@ namespace driver { StringRef Output, StringRef Errors, TaskProcessInformation ProcInfo, void *Context) { - const Job *FinishedCmd = (const Job *)Context; + const Job *const FinishedCmd = (const Job *)Context; if (Pid != llvm::sys::ProcessInfo::InvalidPid) { if (Comp.getShowDriverTimeCompilation()) { DriverTimers[FinishedCmd]->stopTimer(); } - - switch (Comp.getOutputLevel()) { - case OutputLevel::PrintJobs: - // Only print the jobs, not the outputs - break; - case OutputLevel::Normal: - case OutputLevel::Verbose: - // Send the buffered output to stderr, though only if we - // support getting buffered output. - if (TaskQueue::supportsBufferingOutput()) - llvm::errs() << Output; - break; - case OutputLevel::Parseable: - emitParseableOutputForEachFinishedJob(Pid, ReturnCode, Output, - FinishedCmd, ProcInfo); - break; - } + processOutputOfFinishedProcess(Pid, ReturnCode, FinishedCmd, Output, + ProcInfo); } if (Comp.getStatsReporter() && ProcInfo.getResourceUsage().hasValue()) @@ -611,58 +705,127 @@ namespace driver { return unpackAndFinishBatch(ReturnCode, Output, Errors, static_cast(FinishedCmd)); } - - // In order to handle both old dependencies that have disappeared and new - // dependencies that have arisen, we need to reload the dependency file. - // Do this whether or not the build succeeded. - SmallVector Dependents; - if (Comp.getIncrementalBuildEnabled()) { - if (Comp.getEnableExperimentalDependencies()) - reloadAndRemarkDeps(FinishedCmd, ReturnCode, Dependents, - ExpDepGraph.getValue()); - else - reloadAndRemarkDeps(FinishedCmd, ReturnCode, Dependents, - StandardDepGraph); + const bool useRangesForScheduling = + Comp.getEnableSourceRangeDependencies(); + const bool isComparing = Comp.IncrementalComparator.hasValue(); + + CommandSet DependentsWithoutRanges, DependentsWithRanges; + if (useRangesForScheduling || isComparing) + DependentsWithRanges = + subsequentJobsNeeded(FinishedCmd, ReturnCode, /*forRanges=*/true); + if (!useRangesForScheduling || isComparing) + DependentsWithoutRanges = + subsequentJobsNeeded(FinishedCmd, ReturnCode, /*forRanges=*/false); + + if (isComparing) + Comp.IncrementalComparator->update(DependentsWithoutRanges, + DependentsWithRanges); + + if (Comp.getShowIncrementalBuildDecisions() && isComparing && + useRangesForScheduling && + (!DependentsWithoutRanges.empty() || !DependentsWithRanges.empty())) { + llvm::outs() << "\nAfter completion of " << LogJob(FinishedCmd) + << ": \n"; + for (auto const *Cmd : DependentsWithoutRanges) + llvm::outs() << "- Dependencies would now schedule: " << LogJob(Cmd) + << "\n"; + for (auto const *Cmd : DependentsWithRanges) + llvm::outs() << "- Source ranges will now schedule: " << LogJob(Cmd) + << "\n"; + if (DependentsWithoutRanges.size() > 1 || + DependentsWithRanges.size() > 1) + llvm::outs() << "For an additional " << DependentsWithoutRanges.size() + << " (deps) vs " << DependentsWithRanges.size() + << " (ranges)\n"; } - if (ReturnCode != EXIT_SUCCESS) { - // The task failed, so return true without performing any further - // dependency analysis. + if (ReturnCode != EXIT_SUCCESS) + return taskFailed(FinishedCmd, ReturnCode); - // Store this task's ReturnCode as our Result if we haven't stored - // anything yet. - if (Result == EXIT_SUCCESS) - Result = ReturnCode; + // When a task finishes, we need to reevaluate the other commands that + // might have been blocked. + markFinished(FinishedCmd); - if (!isa(FinishedCmd->getSource()) || - ReturnCode != EXIT_FAILURE) { - Comp.getDiags().diagnose(SourceLoc(), diag::error_command_failed, - FinishedCmd->getSource().getClassName(), - ReturnCode); - } + const CommandSet &DependentsInEffect = useRangesForScheduling + ? DependentsWithRanges + : DependentsWithoutRanges; + + noteBuildingJobs(DependentsInEffect, useRangesForScheduling, + "because of dependencies discovered later"); + + scheduleCommandsInSortedOrder(DependentsInEffect); + for (const Job *Cmd : DependentsInEffect) + DeferredCommands.erase(Cmd); + return TaskFinishedResponse::ContinueExecution; + } - // See how ContinueBuildingAfterErrors gets set up in Driver.cpp for - // more info. - assert((Comp.getContinueBuildingAfterErrors() || - !Comp.getBatchModeEnabled()) && - "batch mode diagnostics require ContinueBuildingAfterErrors"); + TaskFinishedResponse taskFailed(const Job *FinishedCmd, + const int ReturnCode) { + // The task failed, so return true without performing any further + // dependency analysis. - return Comp.getContinueBuildingAfterErrors() ? - TaskFinishedResponse::ContinueExecution : - TaskFinishedResponse::StopExecution; + // Store this task's ReturnCode as our Result if we haven't stored + // anything yet. + + if (Result == EXIT_SUCCESS) + Result = ReturnCode; + + if (!isa(FinishedCmd->getSource()) || + ReturnCode != EXIT_FAILURE) { + Comp.getDiags().diagnose(SourceLoc(), diag::error_command_failed, + FinishedCmd->getSource().getClassName(), + ReturnCode); } - // When a task finishes, we need to reevaluate the other commands that - // might have been blocked. - markFinished(FinishedCmd); + // See how ContinueBuildingAfterErrors gets set up in Driver.cpp for + // more info. + assert((Comp.getContinueBuildingAfterErrors() || + !Comp.getBatchModeEnabled()) && + "batch mode diagnostics require ContinueBuildingAfterErrors"); - for (const Job *Cmd : Dependents) { - DeferredCommands.erase(Cmd); - noteBuilding(Cmd, "because of dependencies discovered later"); - scheduleCommandIfNecessaryAndPossible(Cmd); + return Comp.getContinueBuildingAfterErrors() + ? TaskFinishedResponse::ContinueExecution + : TaskFinishedResponse::StopExecution; + } + + void processOutputOfFinishedProcess(ProcessId Pid, int ReturnCode, + const Job *const FinishedCmd, + StringRef Output, + TaskProcessInformation ProcInfo) { + switch (Comp.getOutputLevel()) { + case OutputLevel::PrintJobs: + // Only print the jobs, not the outputs + break; + case OutputLevel::Normal: + case OutputLevel::Verbose: + // Send the buffered output to stderr, though only if we + // support getting buffered output. + if (TaskQueue::supportsBufferingOutput()) + llvm::errs() << Output; + break; + case OutputLevel::Parseable: + emitParseableOutputForEachFinishedJob(Pid, ReturnCode, Output, + FinishedCmd, ProcInfo); + break; } + } - return TaskFinishedResponse::ContinueExecution; + /// In order to handle both old dependencies that have disappeared and new + /// dependencies that have arisen, we need to reload the dependency file. + /// Do this whether or not the build succeeded. + /// + /// FIXME: too much global state floating around, e.g. + /// getIncrementalBuildEnabled + CommandSet subsequentJobsNeeded(const Job *FinishedCmd, + const int ReturnCode, + const bool forRanges) { + if (!Comp.getIncrementalBuildEnabled()) + return {}; + auto Dependents = reloadAndRemarkDeps(FinishedCmd, ReturnCode, forRanges); + CommandSet DepSet; + for (const Job *Cmd : Dependents) + DepSet.insert(Cmd); + return DepSet; } TaskFinishedResponse taskSignalled(ProcessId Pid, StringRef ErrorMsg, @@ -718,145 +881,339 @@ namespace driver { public: PerformJobsState(Compilation &Comp, std::unique_ptr &&TaskQueue) - : Comp(Comp), ActualIncrementalTracer(Comp.getStatsReporter()), - TQ(std::move(TaskQueue)) { - if (Comp.getEnableExperimentalDependencies()) - ExpDepGraph.emplace( - Comp.getVerifyExperimentalDependencyGraphAfterEveryImport(), - Comp.getEmitExperimentalDependencyDotFileAfterEveryImport(), - Comp.getTraceDependencies(), - Comp.getStatsReporter() - ); - else if (Comp.getTraceDependencies()) + : Comp(Comp), + FineGrainedDepGraph( + Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), + Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), + Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getStatsReporter()), + FineGrainedDepGraphForRanges( + Comp.getVerifyFineGrainedDependencyGraphAfterEveryImport(), + Comp.getEmitFineGrainedDependencyDotFileAfterEveryImport(), + Comp.getEnableTypeFingerprints(), Comp.getTraceDependencies(), + Comp.getStatsReporter()), + ActualIncrementalTracer(Comp.getStatsReporter()), + TQ(std::move(TaskQueue)) { + if (!Comp.getEnableFineGrainedDependencies() && + Comp.getTraceDependencies()) IncrementalTracer = &ActualIncrementalTracer; } /// Schedule and run initial, additional, and batch jobs. - template - void runJobs(DependencyGraphT &DepGraph) { - scheduleInitialJobs(DepGraph); - scheduleAdditionalJobs(DepGraph); + void runJobs() { + scheduleJobsBeforeBatching(); formBatchJobsAndAddPendingJobsToTaskQueue(); runTaskQueueToCompletion(); - checkUnfinishedJobs(DepGraph); + checkUnfinishedJobs(); } private: - /// Schedule all jobs we can from the initial list provided by Compilation. - template - void scheduleInitialJobs(DependencyGraphT &DepGraph) { + void scheduleJobsBeforeBatching() { + if (Comp.getIncrementalBuildEnabled()) + scheduleFirstRoundJobsForIncrementalCompilation(); + else + scheduleJobsForNonIncrementalCompilation(); + } + + void scheduleJobsForNonIncrementalCompilation() { + for (const Job *Cmd : Comp.getJobs()) + scheduleCommandIfNecessaryAndPossible(Cmd); + } + + void scheduleFirstRoundJobsForIncrementalCompilation() { + + CommandSet compileJobsToSchedule = + computeFirstRoundCompileJobsForIncrementalCompilation(); + for (const Job *Cmd : Comp.getJobs()) { - if (!Comp.getIncrementalBuildEnabled()) { + if (Cmd->getFirstSwiftPrimaryInput().empty() || + compileJobsToSchedule.count(Cmd)) { scheduleCommandIfNecessaryAndPossible(Cmd); - continue; + noteBuilding(Cmd, /*willBeBuilding*/ true, /*isTentative=*/false, + Comp.getEnableSourceRangeDependencies(), ""); + } else { + DeferredCommands.insert(Cmd); + noteBuilding(Cmd, /*willBeBuilding*/ false, /*isTentative=*/false, + Comp.getEnableSourceRangeDependencies(), ""); } + } + } - // Try to load the dependencies file for this job. If there isn't one, we - // always have to run the job, but it doesn't affect any other jobs. If - // there should be one but it's not present or can't be loaded, we have to - // run all the jobs. - // FIXME: We can probably do better here! - Job::Condition Condition = Job::Condition::Always; - StringRef DependenciesFile = - Cmd->getOutput().getAdditionalOutputForType( - file_types::TY_SwiftDeps); - if (!DependenciesFile.empty()) { - if (Cmd->getCondition() == Job::Condition::NewlyAdded) { - DepGraph.addIndependentNode(Cmd); - } else { - switch ( - DepGraph.loadFromPath(Cmd, DependenciesFile, Comp.getDiags())) { - case DependencyGraphImpl::LoadResult::HadError: - dependencyLoadFailed(DependenciesFile, /*Warn=*/false); - break; - case DependencyGraphImpl::LoadResult::UpToDate: - Condition = Cmd->getCondition(); - break; - case DependencyGraphImpl::LoadResult::AffectsDownstream: - if (Comp.getEnableExperimentalDependencies()) { - // The experimental graph reports a change, since it lumps new - // files together with new "Provides". - Condition = Cmd->getCondition(); - break; - } - llvm_unreachable("we haven't marked anything in this graph yet"); - } - } + /// Figure out the best strategy and return those jobs. May return + /// duplicates. + CommandSet computeFirstRoundCompileJobsForIncrementalCompilation() { + const bool useRangesForScheduling = + Comp.getEnableSourceRangeDependencies(); + const bool isComparing = Comp.IncrementalComparator.hasValue(); + + CommandSet jobsWithRanges, jobsWithoutRanges; + if (useRangesForScheduling || isComparing) + jobsWithRanges = + computeDependenciesAndGetNeededCompileJobs(/*useRanges=*/true); + if (!useRangesForScheduling || isComparing) + jobsWithoutRanges = + computeDependenciesAndGetNeededCompileJobs(/*useRanges=*/false); + + if (isComparing) + Comp.IncrementalComparator->update(jobsWithoutRanges, jobsWithRanges); + + return useRangesForScheduling ? jobsWithRanges : jobsWithoutRanges; + } + + /// Return jobs to run if using dependencies, may include duplicates. + /// If optional argument is present, optimize with source range info + CommandSet + computeDependenciesAndGetNeededCompileJobs(const bool forRanges) { + auto getEveryCompileJob = [&] { + CommandSet everyCompileJob; + for (const Job *Cmd : Comp.getJobs()) { + if (!Cmd->getFirstSwiftPrimaryInput().empty()) + everyCompileJob.insert(Cmd); } + return everyCompileJob; + }; + + CommandSet jobsToSchedule; + CommandSet initialCascadingCommands; + for (const Job *cmd : Comp.getJobs()) { + const StringRef primary = cmd->getFirstSwiftPrimaryInput(); + if (primary.empty()) + continue; // not Compile + const Optional> shouldSchedAndIsCascading = + computeShouldInitiallyScheduleJobAndDependendents(cmd, forRanges); + if (!shouldSchedAndIsCascading) + return getEveryCompileJob(); // Load error, just run them all + const bool &shouldSchedule = shouldSchedAndIsCascading->first; + const bool &isCascading = shouldSchedAndIsCascading->second; + if (shouldSchedule) + jobsToSchedule.insert(cmd); + if (isCascading) + initialCascadingCommands.insert(cmd); + } + for (const auto *cmd : collectCascadedJobsFromDependencyGraph( + initialCascadingCommands, forRanges)) + jobsToSchedule.insert(cmd); + for (const auto cmd : + collectExternallyDependentJobsFromDependencyGraph(forRanges)) + jobsToSchedule.insert(cmd); + return jobsToSchedule; + } + + /// If error return None, else return if this (compile) job should be + /// scheduled, and if its dependents should be. + Optional> + computeShouldInitiallyScheduleJobAndDependendents(const Job *cmd, + const bool forRanges) { + const Optional> shouldSchedAndIsCascading = + isCompileJobInitiallyNeededForDependencyBasedIncrementalCompilation( + cmd, forRanges); + if (!shouldSchedAndIsCascading) + return None; + + if (!forRanges) + return shouldSchedAndIsCascading; + + using namespace incremental_ranges; + const Optional info = + SourceRangeBasedInfo::loadInfoForOneJob( + cmd, Comp.getShowIncrementalBuildDecisions(), Comp.getDiags()); + + // Need to run this if only to create the supplementary outputs. + if (!info) + return std::make_pair(true, shouldSchedAndIsCascading->second); + + dumpSourceRangeInfo(info.getValue()); + + auto noteBuildingThisOne = [&](const bool willBeBuilding, + StringRef reason) { + noteBuilding(cmd, willBeBuilding, + /*isTentative=*/false, forRanges, reason); + }; + + const bool shouldScheduleThisJob = + shouldSchedAndIsCascading->first && + info->didInputChangeAtAll(Comp.getDiags(), noteBuildingThisOne); + + auto noteInitiallyCascading = [&](const bool isInitiallyCascading, + StringRef reason) { + if (!Comp.getShowIncrementalBuildDecisions()) + return; + const bool isHypothetical = + Comp.getEnableSourceRangeDependencies() != forRanges; + llvm::outs() << " - " << (isHypothetical ? "Hypothetically: " : "") + << (isInitiallyCascading ? "Will " : "Will not ") + << "immediately schedule dependents of " << LogJob(cmd) + << " because " << reason << "\n"; + }; + const bool shouldScheduleCascadingJobs = + shouldScheduleThisJob && + (shouldSchedAndIsCascading->second || + info->didInputChangeNonlocally(Comp.getDiags(), + noteInitiallyCascading)); + + return std::make_pair(shouldScheduleThisJob, shouldScheduleCascadingJobs); + } + + void + dumpSourceRangeInfo(const incremental_ranges::SourceRangeBasedInfo &info) { + const bool dumpCompiledSourceDiffs = + Comp.getArgs().hasArg(options::OPT_driver_dump_compiled_source_diffs); + const bool dumpSwiftRanges = + Comp.getArgs().hasArg(options::OPT_driver_dump_swift_ranges); + info.dump(dumpCompiledSourceDiffs, dumpSwiftRanges); + } + + /// Return whether job should be scheduled when using dependencies, and if + /// the job is cascading. Or if there was a dependency-read error, return + /// None to indicate don't-know. + Optional> + isCompileJobInitiallyNeededForDependencyBasedIncrementalCompilation( + const Job *Cmd, const bool forRanges) { + auto CondAndHasDepsIfNoError = + loadDependenciesAndComputeCondition(Cmd, forRanges); + if (!CondAndHasDepsIfNoError) + return None; // swiftdeps read error, abandon dependencies + + Job::Condition Cond; + bool HasDependenciesFileName; + std::tie(Cond, HasDependenciesFileName) = + CondAndHasDepsIfNoError.getValue(); + + const bool shouldSched = shouldScheduleCompileJobAccordingToCondition( + Cmd, Cond, HasDependenciesFileName, forRanges); + + const bool isCascading = isCascadingJobAccordingToCondition( + Cmd, Cond, HasDependenciesFileName); + return std::make_pair(shouldSched, isCascading); + } + + /// Returns job condition, and whether a dependency file was specified. + /// But returns None if there was a dependency read error. + Optional> + loadDependenciesAndComputeCondition(const Job *const Cmd, bool forRanges) { + // Try to load the dependencies file for this job. If there isn't one, we + // always have to run the job, but it doesn't affect any other jobs. If + // there should be one but it's not present or can't be loaded, we have to + // run all the jobs. + // FIXME: We can probably do better here! + + const StringRef DependenciesFile = + Cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); + if (DependenciesFile.empty()) + return std::make_pair(Job::Condition::Always, false); + if (Cmd->getCondition() == Job::Condition::NewlyAdded) { + registerJobToDepGraph(Cmd, forRanges); + return std::make_pair(Job::Condition::NewlyAdded, true); + } + const bool depGraphLoadError = + loadDepGraphFromPath(Cmd, DependenciesFile, forRanges); + if (depGraphLoadError) { + dependencyLoadFailed(DependenciesFile, /*Warn=*/true); + return None; + } + return std::make_pair(Cmd->getCondition(), true); + } - switch (Condition) { - case Job::Condition::Always: - if (Comp.getIncrementalBuildEnabled() && !DependenciesFile.empty()) { - // Ensure dependents will get recompiled. - InitialCascadingCommands.push_back(Cmd); + bool shouldScheduleCompileJobAccordingToCondition( + const Job *const Cmd, const Job::Condition Condition, + const bool hasDependenciesFileName, const bool forRanges) { + + // When using ranges may still decide not to schedule the job. + const bool isTentative = + Comp.getEnableSourceRangeDependencies() || forRanges; + + switch (Condition) { + case Job::Condition::Always: + case Job::Condition::NewlyAdded: + if (Comp.getIncrementalBuildEnabled() && hasDependenciesFileName) { + if (Comp.getEnableFineGrainedDependencies()) { + // No need to do anything since after this jos is run and its + // dependencies reloaded, they will show up as changed nodes + } else { // Mark this job as cascading. // // It would probably be safe and simpler to markTransitive on the // start nodes in the "Always" condition from the start instead of // using markIntransitive and having later functions call // markTransitive. That way markIntransitive would be an - // implementation detail of DependencyGraph. - DepGraph.markIntransitive(Cmd); + // implementation detail of CoarseGrainedDependencyGraph. + // + // As it stands, after this job finishes, this mark will tell the + // code that this job was known to be "cascading". That knowledge + // will cause any dependent jobs to be run if it hasn't already + // been. + getDepGraph(forRanges).markIntransitive(Cmd); } - LLVM_FALLTHROUGH; - case Job::Condition::RunWithoutCascading: - noteBuilding(Cmd, "(initial)"); - scheduleCommandIfNecessaryAndPossible(Cmd); - break; - case Job::Condition::CheckDependencies: - DeferredCommands.insert(Cmd); - break; - case Job::Condition::NewlyAdded: - llvm_unreachable("handled above"); } + LLVM_FALLTHROUGH; + case Job::Condition::RunWithoutCascading: + noteBuilding(Cmd, /*willBeBuilding=*/true, /*isTentative=*/isTentative, + forRanges, "(initial)"); + return true; + case Job::Condition::CheckDependencies: + noteBuilding(Cmd, /*willBeBuilding=*/false, /*isTentative=*/isTentative, + forRanges, "file is up-to-date and output exists"); + return false; } - if (ExpDepGraph.hasValue()) - assert(ExpDepGraph.getValue().emitDotFileAndVerify(Comp.getDiags())); - } - - /// Schedule transitive closure of initial jobs, and external jobs. - template - void scheduleAdditionalJobs(DependencyGraphT &DepGraph) { - if (Comp.getIncrementalBuildEnabled()) { - SmallVector AdditionalOutOfDateCommands; - - // We scheduled all of the files that have actually changed. Now add the - // files that haven't changed, so that they'll get built in parallel if - // possible and after the first set of files if it's not. - for (auto *Cmd : InitialCascadingCommands) { - DepGraph.markTransitive(AdditionalOutOfDateCommands, Cmd, - IncrementalTracer); - } + } - for (auto *transitiveCmd : AdditionalOutOfDateCommands) - noteBuilding(transitiveCmd, "because of the initial set"); - size_t firstSize = AdditionalOutOfDateCommands.size(); - - // Check all cross-module dependencies as well. - for (StringRef dependency : DepGraph.getExternalDependencies()) { - llvm::sys::fs::file_status depStatus; - if (!llvm::sys::fs::status(dependency, depStatus)) - if (depStatus.getLastModificationTime() < Comp.getLastBuildTime()) - continue; - - // If the dependency has been modified since the oldest built file, - // or if we can't stat it for some reason (perhaps it's been deleted?), - // trigger rebuilds through the dependency graph. - DepGraph.markExternal(AdditionalOutOfDateCommands, dependency); - } + bool isCascadingJobAccordingToCondition( + const Job *const Cmd, const Job::Condition Condition, + const bool hasDependenciesFileName) const { + switch (Condition) { + case Job::Condition::Always: + case Job::Condition::NewlyAdded: + return Comp.getIncrementalBuildEnabled() && hasDependenciesFileName; + case Job::Condition::RunWithoutCascading: + case Job::Condition::CheckDependencies: + return false; + } + } - for (auto *externalCmd : - llvm::makeArrayRef(AdditionalOutOfDateCommands).slice(firstSize)) { - noteBuilding(externalCmd, "because of external dependencies"); - } + void forEachOutOfDateExternalDependency( + const bool forRanges, + function_ref consumeExternalSwiftDeps) { + for (StringRef dependency : getExternalDependencies(forRanges)) { + // If the dependency has been modified since the oldest built file, + // or if we can't stat it for some reason (perhaps it's been + // deleted?), trigger rebuilds through the dependency graph. + llvm::sys::fs::file_status depStatus; + if (llvm::sys::fs::status(dependency, depStatus) || + Comp.getLastBuildTime() < depStatus.getLastModificationTime()) + consumeExternalSwiftDeps(dependency); + } + } - for (auto *AdditionalCmd : AdditionalOutOfDateCommands) { - if (!DeferredCommands.count(AdditionalCmd)) - continue; - scheduleCommandIfNecessaryAndPossible(AdditionalCmd); - DeferredCommands.erase(AdditionalCmd); - } + CommandSet collectCascadedJobsFromDependencyGraph( + const CommandSet &InitialCascadingCommands, const bool forRanges) { + CommandSet CascadedJobs; + // We scheduled all of the files that have actually changed. Now add the + // files that haven't changed, so that they'll get built in parallel if + // possible and after the first set of files if it's not. + for (auto *Cmd : InitialCascadingCommands) { + for (const auto *transitiveCmd : findJobsToRecompileWhenWholeJobChanges( + Cmd, forRanges, IncrementalTracer)) + CascadedJobs.insert(transitiveCmd); } + noteBuildingJobs(CascadedJobs, forRanges, "because of the initial set"); + return CascadedJobs; + } + + /// Return jobs dependent on other modules, and jobs dependent on those jobs + SmallVector + collectExternallyDependentJobsFromDependencyGraph(const bool forRanges) { + SmallVector ExternallyDependentJobs; + // Check all cross-module dependencies as well. + forEachOutOfDateExternalDependency(forRanges, [&](StringRef dependency) { + // If the dependency has been modified since the oldest built file, + // or if we can't stat it for some reason (perhaps it's been + // deleted?), trigger rebuilds through the dependency graph. + for (const Job * marked: markExternalInDepGraph(dependency, forRanges)) + ExternallyDependentJobs.push_back(marked); + }); + noteBuildingJobs(ExternallyDependentJobs, forRanges, + "because of external dependencies"); + return ExternallyDependentJobs; } /// Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c @@ -1074,11 +1431,20 @@ namespace driver { // subprocesses than before. And significantly: it's doing so while // not exceeding the RAM of a typical 2-core laptop. + // An explanation of why the partition calculation isn't integer division. + // Using an example, a module of 26 files exceeds the limit of 25 and must + // be compiled in 2 batches. Integer division yields 26/25 = 1 batch, but + // a single batch of 26 exceeds the limit. The calculation must round up, + // which can be calculated using: `(x + y - 1) / y` + auto DivideRoundingUp = [](size_t Num, size_t Div) -> size_t { + return (Num + Div - 1) / Div; + }; + size_t DefaultSizeLimit = 25; size_t NumTasks = TQ->getNumberOfParallelTasks(); size_t NumFiles = PendingExecution.size(); size_t SizeLimit = Comp.getBatchSizeLimit().getValueOr(DefaultSizeLimit); - return std::max(NumTasks, NumFiles / SizeLimit); + return std::max(NumTasks, DivideRoundingUp(NumFiles, SizeLimit)); } /// Select jobs that are batch-combinable from \c PendingExecution, combine @@ -1182,8 +1548,7 @@ namespace driver { } while (Result == 0 && TQ->hasRemainingTasks()); } - template - void checkUnfinishedJobs(DependencyGraphT &DepGraph) { + void checkUnfinishedJobs() { if (Result == 0) { assert(BlockingCommands.empty() && "some blocking commands never finished properly"); @@ -1191,10 +1556,13 @@ namespace driver { // Make sure we record any files that still need to be rebuilt. for (const Job *Cmd : Comp.getJobs()) { // Skip files that don't use dependency analysis. - StringRef DependenciesFile = - Cmd->getOutput().getAdditionalOutputForType( - file_types::TY_SwiftDeps); - if (DependenciesFile.empty()) + bool shouldHaveOutput = false; + file_types::forEachIncrementalOutputType( + [&](const file_types::ID type) { + shouldHaveOutput |= + !Cmd->getOutput().getAdditionalOutputForType(type).empty(); + }); + if (!shouldHaveOutput) continue; // Don't worry about commands that finished or weren't going to run. @@ -1203,14 +1571,46 @@ namespace driver { if (!ScheduledCommands.count(Cmd)) continue; - bool isCascading = true; - if (Comp.getIncrementalBuildEnabled()) - isCascading = DepGraph.isMarked(Cmd); - UnfinishedCommands.insert({Cmd, isCascading}); + const bool needsCascadingBuild = + computeNeedsCascadingBuildForUnfinishedCommand(Cmd); + UnfinishedCommands.insert({Cmd, needsCascadingBuild}); } } } + /// When the driver next runs, it will read the build record, and the + /// unfinished job status will be set to either \c NeedsCascading... or + /// \c NeedsNonCascading... + /// Decide which it will be. + /// As far as I can tell, the only difference the result of this function + /// makes is how soon + /// required dependents are recompiled. Here's my reasoning: + /// + /// When the driver next runs, the condition will be filtered through + /// \c loadDependenciesAndComputeCondition . + /// Then, the cascading predicate is returned from + /// \c isCompileJobInitiallyNeededForDependencyBasedIncrementalCompilation + /// and \c computeShouldInitiallyScheduleJobAndDependendents Then, in \c + /// computeDependenciesAndGetNeededCompileJobs if the job needs a cascading + /// build, it's dependents will be scheduled immediately. + /// After the job finishes, it's dependencies will be processed again. + /// If a non-cascading job failed, the driver will schedule all of its + /// dependents. (All of its dependents are assumed to have already been + /// scheduled.) If the job succeeds, the revised dependencies are consulted + /// to schedule any needed jobs. + + bool computeNeedsCascadingBuildForUnfinishedCommand(const Job *Cmd) { + if (!Comp.getIncrementalBuildEnabled()) + return true; + const bool forRanges = Comp.getEnableSourceRangeDependencies(); + if (!Comp.getEnableFineGrainedDependencies()) { + // Mysterious legacy code + return getDepGraph(forRanges).isMarked(Cmd); + } + // See the comment on the whole function above + return false; + } + public: void populateInputInfoMap(InputInfoMap &inputs) const { for (auto &entry : UnfinishedCommands) { @@ -1267,6 +1667,73 @@ namespace driver { bool hadAnyAbnormalExit() { return AnyAbnormalExit; } + + // MARK: dependency graph interface + + std::vector getExternalDependencies(const bool forRanges) const { + if (Comp.getEnableFineGrainedDependencies()) + return getFineGrainedDepGraph(forRanges).getExternalDependencies(); + const auto deps = getDepGraph(forRanges).getExternalDependencies(); + std::vector Dependencies; + std::copy(std::begin(deps), std::end(deps), + std::back_inserter(Dependencies)); + return Dependencies; + } + + std::vector + markExternalInDepGraph(StringRef externalDependency, + const bool forRanges) { + return Comp.getEnableFineGrainedDependencies() + ? getFineGrainedDepGraph(forRanges) + .findExternallyDependentUntracedJobs(externalDependency) + : getDepGraph(forRanges).markExternal(externalDependency); + } + + std::vector findJobsToRecompileWhenWholeJobChanges( + const Job *Cmd, const bool forRanges, + CoarseGrainedDependencyGraph::MarkTracer *tracer = nullptr) { + return Comp.getEnableFineGrainedDependencies() + ? getFineGrainedDepGraph(forRanges) + .findJobsToRecompileWhenWholeJobChanges(Cmd) + : getDepGraph(forRanges).markTransitive(Cmd, tracer); + } + + void registerJobToDepGraph(const Job *Cmd, const bool forRanges) { + if (Comp.getEnableFineGrainedDependencies()) + getFineGrainedDepGraph(forRanges).registerJob(Cmd); + else + getDepGraph(forRanges).registerJob(Cmd); + } + + /// Return hadError + bool loadDepGraphFromPath(const Job *Cmd, const StringRef DependenciesFile, + const bool forRanges) { + if (Comp.getEnableFineGrainedDependencies()) { + const auto changes = getFineGrainedDepGraph(forRanges).loadFromPath( + Cmd, DependenciesFile, Comp.getDiags()); + const bool didDependencyLoadSucceed = changes.hasValue(); + return !didDependencyLoadSucceed; + } + auto loadResult = getDepGraph(forRanges).loadFromPath( + Cmd, DependenciesFile, Comp.getDiags()); + return loadResult == CoarseGrainedDependencyGraph::LoadResult::HadError; + } + + fine_grained_dependencies::ModuleDepGraph & + getFineGrainedDepGraph(const bool forRanges) { + return forRanges ? FineGrainedDepGraphForRanges : FineGrainedDepGraph; + } + CoarseGrainedDependencyGraph &getDepGraph(const bool forRanges) { + return forRanges ? CoarseGrainedDepGraphForRanges : CoarseGrainedDepGraph; + } + const fine_grained_dependencies::ModuleDepGraph & + getFineGrainedDepGraph(const bool forRanges) const { + return forRanges ? FineGrainedDepGraphForRanges : FineGrainedDepGraph; + } + const CoarseGrainedDependencyGraph & + getDepGraph(const bool forRanges) const { + return forRanges ? CoarseGrainedDepGraphForRanges : CoarseGrainedDepGraph; + } }; } // namespace driver } // namespace swift @@ -1356,6 +1823,51 @@ static void writeCompilationRecord(StringRef path, StringRef argsHash, } } +static void writeInputJobsToFilelist(llvm::raw_fd_ostream &out, const Job *job, + const file_types::ID infoType) { + // FIXME: Duplicated from ToolChains.cpp. + for (const Job *input : job->getInputs()) { + const CommandOutput &outputInfo = input->getOutput(); + if (outputInfo.getPrimaryOutputType() == infoType) { + for (auto &output : outputInfo.getPrimaryOutputFilenames()) + out << output << "\n"; + } else { + auto output = outputInfo.getAnyOutputForType(infoType); + if (!output.empty()) + out << output << "\n"; + } + } +} +static void writeSourceInputActionsToFilelist(llvm::raw_fd_ostream &out, + const Job *job, + const ArgList &args) { + // Ensure that -index-file-path works in conjunction with + // -driver-use-filelists. It needs to be the only primary. + if (Arg *A = args.getLastArg(options::OPT_index_file_path)) + out << A->getValue() << "\n"; + else { + // The normal case for non-single-compile jobs. + for (const Action *A : job->getSource().getInputs()) { + // A could be a GeneratePCHJobAction + if (!isa(A)) + continue; + const auto *IA = cast(A); + out << IA->getInputArg().getValue() << "\n"; + } + } +} +static void writeOutputToFilelist(llvm::raw_fd_ostream &out, const Job *job, + const file_types::ID infoType) { + const CommandOutput &outputInfo = job->getOutput(); + assert(outputInfo.getPrimaryOutputType() == infoType); + for (auto &output : outputInfo.getPrimaryOutputFilenames()) + out << output << "\n"; +} +static void writeSupplementarOutputToFilelist(llvm::raw_fd_ostream &out, + const Job *job) { + job->getOutput().writeOutputFileMap(out); +} + static bool writeFilelistIfNecessary(const Job *job, const ArgList &args, DiagnosticEngine &diags) { bool ok = true; @@ -1374,46 +1886,23 @@ static bool writeFilelistIfNecessary(const Job *job, const ArgList &args, } switch (filelistInfo.whichFiles) { - case FilelistInfo::WhichFiles::Input: - // FIXME: Duplicated from ToolChains.cpp. - for (const Job *input : job->getInputs()) { - const CommandOutput &outputInfo = input->getOutput(); - if (outputInfo.getPrimaryOutputType() == filelistInfo.type) { - for (auto &output : outputInfo.getPrimaryOutputFilenames()) - out << output << "\n"; - } else { - auto output = outputInfo.getAnyOutputForType(filelistInfo.type); - if (!output.empty()) - out << output << "\n"; - } - } + case FilelistInfo::WhichFiles::InputJobs: + writeInputJobsToFilelist(out, job, filelistInfo.type); break; - case FilelistInfo::WhichFiles::PrimaryInputs: - // Ensure that -index-file-path works in conjunction with - // -driver-use-filelists. It needs to be the only primary. - if (Arg *A = args.getLastArg(options::OPT_index_file_path)) - out << A->getValue() << "\n"; - else { - // The normal case for non-single-compile jobs. - for (const Action *A : job->getSource().getInputs()) { - // A could be a GeneratePCHJobAction - if (!isa(A)) - continue; - const auto *IA = cast(A); - out << IA->getInputArg().getValue() << "\n"; - } - } + case FilelistInfo::WhichFiles::SourceInputActions: + writeSourceInputActionsToFilelist(out, job, args); break; - case FilelistInfo::WhichFiles::Output: { - const CommandOutput &outputInfo = job->getOutput(); - assert(outputInfo.getPrimaryOutputType() == filelistInfo.type); - for (auto &output : outputInfo.getPrimaryOutputFilenames()) - out << output << "\n"; + case FilelistInfo::WhichFiles::InputJobsAndSourceInputActions: + writeInputJobsToFilelist(out, job, filelistInfo.type); + writeSourceInputActionsToFilelist(out, job, args); break; - } - case FilelistInfo::WhichFiles::SupplementaryOutput: - job->getOutput().writeOutputFileMap(out); + case FilelistInfo::WhichFiles::Output: { + writeOutputToFilelist(out, job, filelistInfo.type); break; + } + case FilelistInfo::WhichFiles::SupplementaryOutput: + writeSupplementarOutputToFilelist(out, job); + break; } } return ok; @@ -1423,10 +1912,7 @@ int Compilation::performJobsImpl(bool &abnormalExit, std::unique_ptr &&TQ) { PerformJobsState State(*this, std::move(TQ)); - if (getEnableExperimentalDependencies()) - State.runJobs(State.ExpDepGraph.getValue()); - else - State.runJobs(State.StandardDepGraph); + State.runJobs(); if (!CompilationRecordPath.empty()) { InputInfoMap InputInfo; @@ -1441,8 +1927,6 @@ int Compilation::performJobsImpl(bool &abnormalExit, CompilationRecordPath + "~moduleonly"); } } - if (State.ExpDepGraph.hasValue()) - assert(State.ExpDepGraph.getValue().emitDotFileAndVerify(getDiags())); abnormalExit = State.hadAnyAbnormalExit(); return State.getResult(); } @@ -1541,6 +2025,9 @@ int Compilation::performJobs(std::unique_ptr &&TQ) { bool abnormalExit; int result = performJobsImpl(abnormalExit, std::move(TQ)); + if (IncrementalComparator) + IncrementalComparator->outputComparison(); + if (!SaveTemps) { for (const auto &pathPair : TempFilePaths) { if (!abnormalExit || pathPair.getValue() == PreserveOnSignal::No) @@ -1572,3 +2059,128 @@ const char *Compilation::getAllSourcesPath() const { } return AllSourceFilesPath; } + +void Compilation::disableIncrementalBuild(Twine why) { + if (getShowIncrementalBuildDecisions()) + llvm::outs() << "Disabling incremental build: " << why << "\n"; + + EnableIncrementalBuild = false; + if (IncrementalComparator) + IncrementalComparator->WhyIncrementalWasDisabled = why.str(); +} + +void Compilation::IncrementalSchemeComparator::update( + const CommandSet &jobsWithoutRanges, const CommandSet &jobsWithRanges) { + for (const auto *cmd : jobsWithoutRanges) + JobsWithoutRanges.insert(cmd); + for (const auto *cmd : jobsWithRanges) + JobsWithRanges.insert(cmd); + + if (!jobsWithoutRanges.empty()) + ++CompileStagesWithoutRanges; + if (!jobsWithRanges.empty()) + ++CompileStagesWithRanges; +} + +void Compilation::IncrementalSchemeComparator::outputComparison() const { + if (CompareIncrementalSchemesPath.empty()) { + outputComparison(llvm::outs()); + return; + } + + std::error_code EC; + using namespace llvm::sys::fs; + llvm::raw_fd_ostream OS(CompareIncrementalSchemesPath, EC, CD_OpenAlways, + FA_Write, OF_Append | OF_Text); + + if (EC) { + Diags.diagnose(SourceLoc(), diag::unable_to_open_incremental_comparison_log, + CompareIncrementalSchemesPath); + return; + } + outputComparison(OS); +} + +void Compilation::IncrementalSchemeComparator::outputComparison( + llvm::raw_ostream &out) const { + if (!EnableIncrementalBuildWhenConstructed) { + out << "*** Incremental build was not enabled in the command line ***\n"; + return; + } + if (!EnableIncrementalBuild) { + // No stats will have been gathered + assert(!WhyIncrementalWasDisabled.empty() && "Must be a reason"); + out << "*** Incremental build disabled because " + << WhyIncrementalWasDisabled << ", cannot compare ***\n"; + return; + } + unsigned countWithoutRanges = JobsWithoutRanges.size(); + unsigned countWithRanges = JobsWithRanges.size(); + + const int rangeBenefit = countWithoutRanges - countWithRanges; + const int rangeStageBenefit = + CompileStagesWithoutRanges - CompileStagesWithRanges; + + out << "*** " + << "Range benefit: " << rangeBenefit << " compilations, " + << rangeStageBenefit << " stages, " + << "without ranges: " << countWithoutRanges << ", " + << "with ranges: " << countWithRanges << ", " + << (EnableSourceRangeDependencies ? "used" : "did not use") << " ranges, " + << "total: " << SwiftInputCount << " ***\n"; +} + +unsigned Compilation::countSwiftInputs() const { + unsigned inputCount = 0; + for (const auto &p : InputFilesWithTypes) + if (p.first == file_types::TY_Swift) + ++inputCount; + return inputCount; +} + +void Compilation::addDependencyPathOrCreateDummy( + StringRef depPath, function_ref addDependencyPath) { + + if (!OnlyOneDependencyFile) { + addDependencyPath(); + return; + } + if (!HaveAlreadyAddedDependencyPath) { + addDependencyPath(); + HaveAlreadyAddedDependencyPath = true; + } else if (!depPath.empty()) { + // Create dummy empty file + std::error_code EC; + llvm::raw_fd_ostream(depPath, EC, llvm::sys::fs::F_None); + } +} + +template +void Compilation::sortJobsToMatchCompilationInputs( + const JobCollection &unsortedJobs, + SmallVectorImpl &sortedJobs) const { + llvm::DenseMap jobsByInput; + for (const Job *J : unsortedJobs) { + // Only worry about sorting compilation jobs + if (const CompileJobAction *CJA = + dyn_cast(&J->getSource())) { + const InputAction *IA = CJA->findSingleSwiftInput(); + auto R = + jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); + assert(R.second); + (void)R; + } else + sortedJobs.push_back(J); + } + for (const InputPair &P : getInputFiles()) { + auto I = jobsByInput.find(P.second->getValue()); + if (I != jobsByInput.end()) { + sortedJobs.push_back(I->second); + } + } +} + +template void +Compilation::sortJobsToMatchCompilationInputs>( + const ArrayRef &, + SmallVectorImpl &sortedJobs) const; diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index abb8a389c0a06..71656e2fa8073 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -96,6 +96,11 @@ getDarwinLibraryNameSuffixForTriple(const llvm::Triple &triple, case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: + // Here we return "osx" under the assumption that all the + // darwin runtime libraries are zippered and so the "osx" variants + // should be used for macCatalyst targets. + if (tripleIsMacCatalystEnvironment(triple)) + return "osx"; return "ios"; case DarwinPlatformKind::IPhoneOSSimulator: return "iossim"; @@ -231,14 +236,15 @@ toolchains::Darwin::addLinkerInputArgs(InvocationInfo &II, if (context.shouldUseInputFileList()) { Arguments.push_back("-filelist"); Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList")); - II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object, - FilelistInfo::WhichFiles::Input}); + II.FilelistInfos.push_back( + {Arguments.back(), file_types::TY_Object, + FilelistInfo::WhichFiles::InputJobsAndSourceInputActions}); } else { addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); } - addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) addInputsOfType(Arguments, context.Inputs, context.Args, @@ -430,6 +436,9 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments, // package isn't installed. Arguments.push_back("-rpath"); Arguments.push_back(context.Args.MakeArgString("/usr/lib/swift")); + // We don't need an rpath for /System/iOSSupport/usr/lib/swift because... + assert(!tripleIsMacCatalystEnvironment(getTriple()) + && "macCatalyst not supported without Swift-in-the-OS"); } } @@ -488,12 +497,23 @@ toolchains::Darwin::addDeploymentTargetArgs(ArgStringList &Arguments, } else { if (isiOSSimulator) Arguments.push_back("-ios_simulator_version_min"); + else if (tripleIsMacCatalystEnvironment(Triple)) + Arguments.push_back("-maccatalyst_version_min"); else Arguments.push_back("-iphoneos_version_min"); } unsigned major, minor, micro; Triple.getiOSVersion(major, minor, micro); addVersionString(context.Args, Arguments, major, minor, micro); + + if (TargetVariant) { + assert(triplesAreValidForZippering(Triple, *TargetVariant)); + assert(TargetVariant->isMacOSX()); + Arguments.push_back("-macosx_version_min"); + unsigned major, minor, micro; + TargetVariant->getMacOSXVersion(major, minor, micro); + addVersionString(context.Args, Arguments, major, minor, micro); + } } else if (Triple.isWatchOS()) { if (tripleIsWatchSimulator(Triple)) Arguments.push_back("-watchos_simulator_version_min"); @@ -507,6 +527,15 @@ toolchains::Darwin::addDeploymentTargetArgs(ArgStringList &Arguments, unsigned major, minor, micro; Triple.getMacOSXVersion(major, minor, micro); addVersionString(context.Args, Arguments, major, minor, micro); + + if (TargetVariant) { + assert(triplesAreValidForZippering(Triple, *TargetVariant)); + assert(tripleIsMacCatalystEnvironment(*TargetVariant)); + Arguments.push_back("-maccatalyst_version_min"); + unsigned major, minor, micro; + TargetVariant->getiOSVersion(major, minor, micro); + addVersionString(context.Args, Arguments, major, minor, micro); + } } } @@ -643,7 +672,7 @@ toolchains::Darwin::constructInvocation(const StaticLinkJobAction &job, Arguments.push_back("-filelist"); Arguments.push_back(context.getTemporaryFilePath("inputs", "LinkFileList")); II.FilelistInfos.push_back({Arguments.back(), file_types::TY_Object, - FilelistInfo::WhichFiles::Input}); + FilelistInfo::WhichFiles::InputJobs}); } else { addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_Object); @@ -717,15 +746,33 @@ static void validateDeploymentTarget(const toolchains::Darwin &TC, } } +static void validateTargetVariant(const toolchains::Darwin &TC, + DiagnosticEngine &diags, + const llvm::opt::ArgList &args, + StringRef defaultTarget) { + if (TC.getTargetVariant().hasValue()) { + auto target = TC.getTriple(); + auto variant = *TC.getTargetVariant(); + + if (!triplesAreValidForZippering(target, variant)) { + diags.diagnose(SourceLoc(), diag::error_unsupported_target_variant, + variant.str(), + variant.isiOS()); + } + } +} + void toolchains::Darwin::validateArguments(DiagnosticEngine &diags, - const llvm::opt::ArgList &args) const { + const llvm::opt::ArgList &args, + StringRef defaultTarget) const { // Validating arclite library path when link-objc-runtime. validateLinkObjcRuntimeARCLiteLib(*this, diags, args); // Validating apple platforms deployment targets. validateDeploymentTarget(*this, diags, args); - + validateTargetVariant(*this, diags, args, defaultTarget); + // Validating darwin unsupported -static-stdlib argument. if (args.hasArg(options::OPT_static_stdlib)) { diags.diagnose(SourceLoc(), diag::error_darwin_static_stdlib_not_supported); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index c696ac204bf93..cac9af11f2be9 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -21,7 +21,9 @@ #include "swift/AST/DiagnosticsDriver.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/LLVM.h" +#include "swift/Basic/LangOptions.h" #include "swift/Basic/OutputFileMap.h" +#include "swift/Basic/Platform.h" #include "swift/Basic/Range.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/TaskQueue.h" @@ -98,6 +100,7 @@ void Driver::parseDriverKind(ArrayRef Args) { .Case("swiftc", DriverKind::Batch) .Case("swift-autolink-extract", DriverKind::AutolinkExtract) .Case("swift-indent", DriverKind::SwiftIndent) + .Case("swift-symbolgraph-extract", DriverKind::SymbolGraph) .Default(None); if (Kind.hasValue()) @@ -251,8 +254,13 @@ Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) { case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: - case llvm::Triple::WatchOS: - return llvm::make_unique(*this, target); + case llvm::Triple::WatchOS: { + Optional targetVariant; + if (const Arg *A = ArgList.getLastArg(options::OPT_target_variant)) + targetVariant = llvm::Triple(llvm::Triple::normalize(A->getValue())); + + return llvm::make_unique(*this, target, targetVariant); + } case llvm::Triple::Linux: if (target.isAndroid()) return llvm::make_unique(*this, target); @@ -265,6 +273,8 @@ Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) { return llvm::make_unique(*this, target); case llvm::Triple::Haiku: return llvm::make_unique(*this, target); + case llvm::Triple::WASI: + return llvm::make_unique(*this, target); default: Diags.diagnose(SourceLoc(), diag::error_unknown_target, ArgList.getLastArg(options::OPT_target)->getValue()); @@ -375,33 +385,41 @@ static bool getCompilationRecordPath(std::string &buildRecordPath, return false; } -static bool failedToReadOutOfDateMap(bool ShowIncrementalBuildDecisions, - StringRef buildRecordPath, - StringRef reason = "") { +static std::string failedToReadOutOfDateMap(bool ShowIncrementalBuildDecisions, + StringRef buildRecordPath, + StringRef reason = "") { + std::string why = "malformed build record file"; + if (!reason.empty()) { + why += " "; + why += reason; + } if (ShowIncrementalBuildDecisions) { - llvm::outs() << "Incremental compilation has been disabled due to " - << "malformed build record file '" << buildRecordPath << "'."; - if (!reason.empty()) { - llvm::outs() << " " << reason; - } - llvm::outs() << "\n"; + llvm::outs() << "Incremental compilation has been disabled due to " << why + << " '" << buildRecordPath << "'.\n"; } - return true; + return why; } -/// Returns true on error. -static bool populateOutOfDateMap(InputInfoMap &map, - llvm::sys::TimePoint<> &LastBuildTime, - StringRef argsHashStr, - const InputFileList &inputs, - StringRef buildRecordPath, - bool ShowIncrementalBuildDecisions) { +static SmallVector findRemovedInputs( + const InputFileList &inputs, + const llvm::StringMap &previousInputs); + +static void dealWithRemovedInputs(ArrayRef removedInputs, + bool ShowIncrementalBuildDecisions); + +/// Returns why ignore incrementality +static std::string +populateOutOfDateMap(InputInfoMap &map, llvm::sys::TimePoint<> &LastBuildTime, + StringRef argsHashStr, const InputFileList &inputs, + StringRef buildRecordPath, + const bool EnableSourceRangeDependencies, + const bool ShowIncrementalBuildDecisions) { // Treat a missing file as "no previous build". auto buffer = llvm::MemoryBuffer::getFile(buildRecordPath); if (!buffer) { if (ShowIncrementalBuildDecisions) llvm::outs() << "Incremental compilation could not read build record.\n"; - return false; + return "could not read build record"; } namespace yaml = llvm::yaml; @@ -491,7 +509,7 @@ static bool populateOutOfDateMap(InputInfoMap &map, } else if (keyStr == compilation_record::getName(TopLevelKey::Options)) { auto *value = dyn_cast(i->getValue()); if (!value) - return true; + return "no name node in build record"; optionsMatch = (argsHashStr == value->getValue(scratch)); } else if (keyStr == compilation_record::getName(TopLevelKey::BuildTime)) { @@ -504,7 +522,7 @@ static bool populateOutOfDateMap(InputInfoMap &map, } llvm::sys::TimePoint<> timeVal; if (readTimeValue(i->getValue(), timeVal)) - return true; + return "could not read time value in build record"; LastBuildTime = timeVal; } else if (keyStr == compilation_record::getName(TopLevelKey::Inputs)) { @@ -521,21 +539,21 @@ static bool populateOutOfDateMap(InputInfoMap &map, for (auto i = inputMap->begin(), e = inputMap->end(); i != e; ++i) { auto *key = dyn_cast(i->getKey()); if (!key) - return true; + return "no input entry in build record"; auto *value = dyn_cast(i->getValue()); if (!value) - return true; + return "no sequence node for input entry in build record"; using compilation_record::getInfoStatusForIdentifier; auto previousBuildState = getInfoStatusForIdentifier(value->getRawTag()); if (!previousBuildState) - return true; + return "no previous build state in build record"; llvm::sys::TimePoint<> timeValue; if (readTimeValue(value, timeValue)) - return true; + return "could not read time value in build record"; auto inputName = key->getValue(scratch); previousInputs[inputName] = { *previousBuildState, timeValue }; @@ -553,7 +571,7 @@ static bool populateOutOfDateMap(InputInfoMap &map, << "\tPreviously compiled with: " << CompilationRecordSwiftVersion << "\n"; } - return true; + return "compiler version mismatch"; } if (!optionsMatch) { @@ -561,48 +579,70 @@ static bool populateOutOfDateMap(InputInfoMap &map, llvm::outs() << "Incremental compilation has been disabled, because " << "different arguments were passed to the compiler.\n"; } - return true; + return "different arguments passed to compiler"; } - size_t numInputsFromPrevious = 0; + unsigned numMatchingPreviouslyCompiledInputs = 0; for (auto &inputPair : inputs) { auto iter = previousInputs.find(inputPair.second->getValue()); - if (iter == previousInputs.end()) { - map[inputPair.second] = InputInfo::makeNewlyAdded(); - continue; + if (iter == previousInputs.end()) + map[inputPair.second] = CompileJobAction::InputInfo::makeNewlyAdded(); + else { + map[inputPair.second] = iter->getValue(); + ++numMatchingPreviouslyCompiledInputs; } - ++numInputsFromPrevious; - map[inputPair.second] = iter->getValue(); } + assert(numMatchingPreviouslyCompiledInputs <= previousInputs.size()); + auto const wereAnyInputsRemoved = + numMatchingPreviouslyCompiledInputs < previousInputs.size(); + if (!wereAnyInputsRemoved) + return ""; - if (numInputsFromPrevious == previousInputs.size()) { - return false; - } else { - // If a file was removed, we've lost its dependency info. Rebuild everything. - // FIXME: Can we do better? - if (ShowIncrementalBuildDecisions) { - llvm::DenseSet inputArgs; - for (auto &inputPair : inputs) { - inputArgs.insert(inputPair.second->getValue()); - } + const auto removedInputs = findRemovedInputs(inputs, previousInputs); + assert(!removedInputs.empty()); - SmallVector missingInputs; - for (auto &previousInput : previousInputs) { - auto previousInputArg = previousInput.getKey(); - if (inputArgs.find(previousInputArg) == inputArgs.end()) { - missingInputs.push_back(previousInputArg); - } - } + dealWithRemovedInputs(removedInputs, ShowIncrementalBuildDecisions); + return "an input was removed"; // recompile everything; could do better + // someday +} - llvm::outs() << "Incremental compilation has been disabled, because " - << "the following inputs were used in the previous " - << "compilation, but not in the current compilation:\n"; - for (auto &missing : missingInputs) { - llvm::outs() << "\t" << missing << "\n"; - } +static SmallVector findRemovedInputs( + const InputFileList &inputs, + const llvm::StringMap &previousInputs) { + llvm::DenseSet inputArgs; + for (auto &inputPair : inputs) { + inputArgs.insert(inputPair.second->getValue()); + } + SmallVector missingInputs; + for (auto &previousInput : previousInputs) { + auto previousInputArg = previousInput.getKey(); + if (inputArgs.find(previousInputArg) == inputArgs.end()) { + missingInputs.push_back(previousInputArg); } - return true; } + return missingInputs; +} + +static void showRemovedInputs(ArrayRef removedInputs); + +/// Return true if hadError +static void dealWithRemovedInputs(ArrayRef removedInputs, + const bool ShowIncrementalBuildDecisions) { + // If a file was removed, we've lost its dependency info. Rebuild everything. + // FIXME: Can we do better? + // Yes, for range-based recompilation. + if (ShowIncrementalBuildDecisions) + showRemovedInputs(removedInputs); +} + +static void showRemovedInputs(ArrayRef removedInputs) { + + llvm::outs() << "Incremental compilation has been disabled, because " + << "the following inputs were used in the previous " + << "compilation, but not in the current compilation:\n"; + + for (auto &missing : removedInputs) + llvm::outs() << "\t" << missing << "\n"; } // warn if -embed-bitcode is set and the output type is not an object @@ -681,7 +721,9 @@ static bool computeIncremental(const llvm::opt::InputArgList *ArgList, return false; const char *ReasonToDisable = - ArgList->hasArg(options::OPT_whole_module_optimization) + ArgList->hasFlag(options::OPT_whole_module_optimization, + options::OPT_no_whole_module_optimization, + false) ? "is not compatible with whole module optimization." : ArgList->hasArg(options::OPT_embed_bitcode) ? "is not currently compatible with embedding LLVM IR bitcode." @@ -791,7 +833,7 @@ Driver::buildCompilation(const ToolChain &TC, validateArgs(Diags, *TranslatedArgList, TC.getTriple()); // Perform toolchain specific args validation. - TC.validateArguments(Diags, *TranslatedArgList); + TC.validateArguments(Diags, *TranslatedArgList, DefaultTargetTriple); if (Diags.hadAnyError()) return nullptr; @@ -825,8 +867,18 @@ Driver::buildCompilation(const ToolChain &TC, // REPL mode expects no input files, so suppress the error. SuppressNoInputFilesError = true; - Optional OFM = - buildOutputFileMap(*TranslatedArgList, workingDirectory); + const bool EnableSourceRangeDependencies = + ArgList->hasArg(options::OPT_enable_source_range_dependencies); + const bool CompareIncrementalSchemes = + ArgList->hasArg(options::OPT_driver_compare_incremental_schemes) || + ArgList->hasArg(options::OPT_driver_compare_incremental_schemes_path); + const StringRef CompareIncrementalSchemesPath = ArgList->getLastArgValue( + options::OPT_driver_compare_incremental_schemes_path); + + Optional OFM = buildOutputFileMap( + *TranslatedArgList, workingDirectory, + /*addEntriesForSourceFileDependencies=*/EnableSourceRangeDependencies || + CompareIncrementalSchemes); if (Diags.hadAnyError()) return nullptr; @@ -853,15 +905,16 @@ Driver::buildCompilation(const ToolChain &TC, computeArgsHash(ArgsHash, *TranslatedArgList); llvm::sys::TimePoint<> LastBuildTime = llvm::sys::TimePoint<>::min(); InputInfoMap outOfDateMap; - bool rebuildEverything = true; - if (Incremental && !buildRecordPath.empty()) { - if (populateOutOfDateMap(outOfDateMap, LastBuildTime, ArgsHash, Inputs, - buildRecordPath, ShowIncrementalBuildDecisions)) { - // FIXME: Distinguish errors from "file removed", which is benign. - } else { - rebuildEverything = false; - } - } + std::string whyIgnoreIncrementallity = + !Incremental + ? "" + : buildRecordPath.empty() + ? "no build record path" + : populateOutOfDateMap(outOfDateMap, LastBuildTime, ArgsHash, + Inputs, buildRecordPath, + EnableSourceRangeDependencies, + ShowIncrementalBuildDecisions); + // FIXME: Distinguish errors from "file removed", which is benign. size_t DriverFilelistThreshold; if (getFilelistThreshold(*TranslatedArgList, DriverFilelistThreshold, Diags)) @@ -908,16 +961,31 @@ Driver::buildCompilation(const ToolChain &TC, ArgList->hasArg(options::OPT_driver_time_compilation); std::unique_ptr StatsReporter = createStatsReporter(ArgList.get(), Inputs, OI, DefaultTargetTriple); - const bool EnableExperimentalDependencies = - ArgList->hasArg(options::OPT_enable_experimental_dependencies); - const bool VerifyExperimentalDependencyGraphAfterEveryImport = ArgList->hasArg( + + const bool OnlyOneDependencyFile = + ArgList->hasFlag(options::OPT_enable_only_one_dependency_file, + options::OPT_disable_only_one_dependency_file, true); + + // relies on the new dependency graph + // Get the default from the initializer in LangOptions. + const bool EnableFineGrainedDependencies = + ArgList->hasFlag(options::OPT_enable_fine_grained_dependencies, + options::OPT_disable_fine_grained_dependencies, + LangOptions().EnableFineGrainedDependencies); + + const bool EnableTypeFingerprints = + ArgList->hasFlag(options::OPT_enable_type_fingerprints, + options::OPT_disable_type_fingerprints, + LangOptions().EnableTypeFingerprints); + + const bool VerifyFineGrainedDependencyGraphAfterEveryImport = ArgList->hasArg( options:: - OPT_driver_verify_experimental_dependency_graph_after_every_import); - const bool EmitExperimentalDependencyDotFileAfterEveryImport = ArgList->hasArg( + OPT_driver_verify_fine_grained_dependency_graph_after_every_import); + const bool EmitFineGrainedDependencyDotFileAfterEveryImport = ArgList->hasArg( options:: - OPT_driver_emit_experimental_dependency_dot_file_after_every_import); - const bool ExperimentalDependenciesIncludeIntrafileOnes = ArgList->hasArg( - options::OPT_experimental_dependency_include_intrafile); + OPT_driver_emit_fine_grained_dependency_dot_file_after_every_import); + const bool FineGrainedDependenciesIncludeIntrafileOnes = + ArgList->hasArg(options::OPT_fine_grained_dependency_include_intrafile); // clang-format off C = llvm::make_unique( @@ -939,17 +1007,22 @@ Driver::buildCompilation(const ToolChain &TC, SaveTemps, ShowDriverTimeCompilation, std::move(StatsReporter), - EnableExperimentalDependencies, - VerifyExperimentalDependencyGraphAfterEveryImport, - EmitExperimentalDependencyDotFileAfterEveryImport, - ExperimentalDependenciesIncludeIntrafileOnes); + OnlyOneDependencyFile, + EnableFineGrainedDependencies, + EnableTypeFingerprints, + VerifyFineGrainedDependencyGraphAfterEveryImport, + EmitFineGrainedDependencyDotFileAfterEveryImport, + FineGrainedDependenciesIncludeIntrafileOnes, + EnableSourceRangeDependencies, + CompareIncrementalSchemes, + CompareIncrementalSchemesPath); // clang-format on } // Construct the graph of Actions. SmallVector TopLevelActions; buildActions(TopLevelActions, TC, OI, - rebuildEverything ? nullptr : &outOfDateMap, *C); + whyIgnoreIncrementallity.empty() ? &outOfDateMap : nullptr, *C); if (Diags.hadAnyError()) return nullptr; @@ -980,8 +1053,8 @@ Driver::buildCompilation(const ToolChain &TC, // This has to happen after building jobs, because otherwise we won't even // emit .swiftdeps files for the next build. - if (rebuildEverything) - C->disableIncrementalBuild(); + if (!whyIgnoreIncrementallity.empty()) + C->disableIncrementalBuild(whyIgnoreIncrementallity); if (Diags.hadAnyError()) return nullptr; @@ -1410,6 +1483,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OI.CompilerOutputType = file_types::TY_PCH; break; + case options::OPT_emit_pcm: + OI.CompilerMode = OutputInfo::Mode::SingleCompile; + OI.CompilerOutputType = file_types::TY_ClangModuleFile; + break; + case options::OPT_emit_imported_modules: OI.CompilerOutputType = file_types::TY_ImportedModules; // We want the imported modules from the module as a whole, not individual @@ -1442,6 +1520,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OI.CompilerOutputType = file_types::TY_Nothing; break; + case options::OPT_dump_pcm: + OI.CompilerMode = OutputInfo::Mode::SingleCompile; + OI.CompilerOutputType = file_types::TY_Nothing; + break; + case options::OPT_i: // Keep the default output/mode; this flag was removed and should already // have been diagnosed above. @@ -1636,6 +1719,14 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared); }); + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) { + // Just validate the args. The frontend will parse these again and actually + // use them. To avoid emitting warnings multiple times we surpress warnings + // here but not in the frontend. + (void)parseSanitizerRecoverArgValues(A, OI.SelectedSanitizers, Diags, + /*emitWarnings=*/false); + } + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) { // Check that the sanitizer coverage flags are supported if supplied. @@ -1681,25 +1772,48 @@ Driver::computeCompilerMode(const DerivedArgList &Args, return Inputs.empty() ? OutputInfo::Mode::REPL : OutputInfo::Mode::Immediate; + bool UseWMO = Args.hasFlag(options::OPT_whole_module_optimization, + options::OPT_no_whole_module_optimization, + false); + const Arg *ArgRequiringSingleCompile = Args.getLastArg( - options::OPT_whole_module_optimization, options::OPT_index_file); + options::OPT_index_file, + UseWMO ? options::OPT_whole_module_optimization : llvm::opt::OptSpecifier()); BatchModeOut = Args.hasFlag(options::OPT_enable_batch_mode, options::OPT_disable_batch_mode, false); - if (!ArgRequiringSingleCompile) + // For best unparsed ranges, want non-batch mode, standard compile + const Arg *ArgRequiringSinglePrimaryCompile = + Args.getLastArg(options::OPT_enable_source_range_dependencies); + + // AST dump doesn't work with `-wmo`. Since it's not common to want to dump + // the AST, we assume that's the priority and ignore `-wmo`, but we warn the + // user about this decision. + // FIXME: AST dump also doesn't work with `-index-file`, but that fix is a bit + // more complicated than this. + if (UseWMO && Args.hasArg(options::OPT_dump_ast)) { + Diags.diagnose(SourceLoc(), diag::warn_ignoring_wmo); return OutputInfo::Mode::StandardCompile; + } // Override batch mode if given -wmo or -index-file. - if (BatchModeOut) { - BatchModeOut = false; - // Emit a warning about such overriding (FIXME: we might conditionalize - // this based on the user or xcode passing -disable-batch-mode). - Diags.diagnose(SourceLoc(), diag::warn_ignoring_batch_mode, - ArgRequiringSingleCompile->getOption().getPrefixedName()); + if (ArgRequiringSingleCompile) { + if (BatchModeOut) { + BatchModeOut = false; + // Emit a warning about such overriding (FIXME: we might conditionalize + // this based on the user or xcode passing -disable-batch-mode). + Diags.diagnose(SourceLoc(), diag::warn_ignoring_batch_mode, + ArgRequiringSingleCompile->getOption().getPrefixedName()); + } + if (ArgRequiringSinglePrimaryCompile) + Diags.diagnose(SourceLoc(), diag::warn_ignoring_source_range_dependencies, + ArgRequiringSingleCompile->getOption().getPrefixedName()); + return OutputInfo::Mode::SingleCompile; } - return OutputInfo::Mode::SingleCompile; + + return OutputInfo::Mode::StandardCompile; } void Driver::buildActions(SmallVectorImpl &TopLevelActions, @@ -1818,6 +1932,8 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_ObjCHeader: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_Remapping: case file_types::TY_IndexData: case file_types::TY_PCH: @@ -1842,6 +1958,11 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, } case OutputInfo::Mode::SingleCompile: { if (Inputs.empty()) break; + if (Args.hasArg(options::OPT_emit_pcm) && Inputs.size() != 1) { + // -emit-pcm mode requires exactly one input (the module map). + Diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file); + return; + } if (Args.hasArg(options::OPT_embed_bitcode)) { // Make sure we can handle the inputs. bool HandledHere = true; @@ -1962,9 +2083,12 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, for (const Action *A : AllLinkerInputs) if (A->getType() == file_types::TY_Object) AutolinkExtractInputs.push_back(A); - if (!AutolinkExtractInputs.empty() && - (TC.getTriple().getObjectFormat() == llvm::Triple::ELF || - TC.getTriple().isOSCygMing())) { + const auto &Triple = TC.getTriple(); + const bool AutolinkExtractRequired = + (Triple.getObjectFormat() == llvm::Triple::ELF && !Triple.isPS4()) || + Triple.getObjectFormat() == llvm::Triple::Wasm || + Triple.isOSCygMing(); + if (!AutolinkExtractInputs.empty() && AutolinkExtractRequired) { auto *AutolinkExtractAction = C.createAction(AutolinkExtractInputs); // Takes the same inputs as the linker... @@ -1974,8 +2098,9 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (MergeModuleAction) { if (OI.DebugInfoLevel == IRGenDebugInfoLevel::Normal) { - if (TC.getTriple().getObjectFormat() == llvm::Triple::ELF || - TC.getTriple().getObjectFormat() == llvm::Triple::COFF) { + const bool ModuleWrapRequired = + Triple.getObjectFormat() != llvm::Triple::MachO; + if (ModuleWrapRequired) { auto *ModuleWrapAction = C.createAction(MergeModuleAction); LinkAction->addInput(ModuleWrapAction); @@ -2043,19 +2168,54 @@ bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) { std::end(commandArgs)); } + if (Args.hasArg(options::OPT_print_target_info)) { + SmallVector commandLine; + commandLine.push_back("-frontend"); + commandLine.push_back("-print-target-info"); + if (const Arg *targetArg = Args.getLastArg(options::OPT_target)) { + commandLine.push_back("-target"); + commandLine.push_back(targetArg->getValue()); + } + if (const Arg *sdkArg = Args.getLastArg(options::OPT_sdk)) { + commandLine.push_back("-sdk"); + commandLine.push_back(sdkArg->getValue()); + } + + if (const Arg *resourceDirArg = Args.getLastArg(options::OPT_resource_dir)) { + commandLine.push_back("-resource-dir"); + commandLine.push_back(resourceDirArg->getValue()); + } + + std::string executable = getSwiftProgramPath(); + + sys::TaskQueue queue; + queue.addTask(executable.c_str(), commandLine); + queue.execute(nullptr, + [](sys::ProcessId PID, int returnCode, + StringRef output, StringRef errors, + sys::TaskProcessInformation ProcInfo, + void *unused) -> sys::TaskFinishedResponse { + llvm::outs() << output; + llvm::errs() << errors; + return sys::TaskFinishedResponse::ContinueExecution; + }); + return false; + } + return true; } Optional Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args, - StringRef workingDirectory) const { + StringRef workingDirectory, + bool addEntriesForSourceFileDependencies) const { const Arg *A = Args.getLastArg(options::OPT_output_file_map); if (!A) return None; // TODO: perform some preflight checks to ensure the file exists. - llvm::Expected OFM = - OutputFileMap::loadFromPath(A->getValue(), workingDirectory); + llvm::Expected OFM = OutputFileMap::loadFromPath( + A->getValue(), workingDirectory, addEntriesForSourceFileDependencies); if (auto Err = OFM.takeError()) { Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map, llvm::toString(std::move(Err)), A->getValue()); @@ -2305,60 +2465,74 @@ static bool hasExistingAdditionalOutput(CommandOutput &output, return false; } -static void addAuxiliaryOutput( +static llvm::SmallString<128> computeAuxiliaryOutputPath( Compilation &C, CommandOutput &output, file_types::ID outputType, const TypeToPathMap *outputMap, StringRef workingDirectory, StringRef outputPath = StringRef(), llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { if (hasExistingAdditionalOutput(output, outputType, outputPath)) - return; + return {}; - StringRef outputMapPath; if (outputMap) { auto iter = outputMap->find(outputType); - if (iter != outputMap->end()) - outputMapPath = iter->second; + if (iter != outputMap->end()) { + StringRef outputMapPath = iter->second; + // Prefer a path from the OutputMap. + if (!outputMapPath.empty()) + return outputMapPath; + } } + if (!outputPath.empty()) + return outputPath; - if (!outputMapPath.empty()) { - // Prefer a path from the OutputMap. - output.setAdditionalOutputForType(outputType, outputMapPath); - } else if (!outputPath.empty()) { - output.setAdditionalOutputForType(outputType, outputPath); - } else if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { + if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) { // This auxiliary output only exists if requireArg is passed, but it // wasn't this time. - return; - } else { - // Put the auxiliary output file next to "the" primary output file. - // - // FIXME: when we're in WMO and have multiple primary outputs, we derive the - // additional filename here from the _first_ primary output name, which - // means that in the derived OFM (in Job.cpp) the additional output will - // have a possibly-surprising name. But that's only half the problem: it - // also get associated with the first primary _input_, even when there are - // multiple primary inputs; really it should be associated with the build as - // a whole -- derived OFM input "" -- but that's a more general thing to - // fix. - llvm::SmallString<128> path; - if (output.getPrimaryOutputType() != file_types::TY_Nothing) - path = output.getPrimaryOutputFilenames()[0]; - else if (!output.getBaseInput(0).empty()) - path = llvm::sys::path::filename(output.getBaseInput(0)); - else { - formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", - workingDirectory, path); - } - assert(!path.empty()); - - bool isTempFile = C.isTemporaryFile(path); - llvm::sys::path::replace_extension( - path, file_types::getExtension(outputType)); - output.setAdditionalOutputForType(outputType, path); - if (isTempFile) - C.addTemporaryFile(path); + return {}; } + + // Put the auxiliary output file next to "the" primary output file. + // + // FIXME: when we're in WMO and have multiple primary outputs, we derive the + // additional filename here from the _first_ primary output name, which + // means that in the derived OFM (in Job.cpp) the additional output will + // have a possibly-surprising name. But that's only half the problem: it + // also get associated with the first primary _input_, even when there are + // multiple primary inputs; really it should be associated with the build as + // a whole -- derived OFM input "" -- but that's a more general thing to + // fix. + llvm::SmallString<128> path; + if (output.getPrimaryOutputType() != file_types::TY_Nothing) + path = output.getPrimaryOutputFilenames()[0]; + else if (!output.getBaseInput(0).empty()) + path = llvm::sys::path::filename(output.getBaseInput(0)); + else { + formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"", + workingDirectory, path); + } + assert(!path.empty()); + + const bool isTempFile = C.isTemporaryFile(path); + llvm::sys::path::replace_extension(path, + file_types::getExtension(outputType)); + if (isTempFile) + C.addTemporaryFile(path); + return path; +} + +static void addAuxiliaryOutput( + Compilation &C, CommandOutput &output, file_types::ID outputType, + const TypeToPathMap *outputMap, StringRef workingDirectory, + StringRef outputPath = StringRef(), + llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) { + + const auto path = + computeAuxiliaryOutputPath(C, output, outputType, outputMap, + workingDirectory, outputPath, requireArg); + if (path.empty()) + return; + output.setAdditionalOutputForType(outputType, path); } static void addDiagFileOutputForPersistentPCHAction( @@ -2937,12 +3111,19 @@ void Driver::chooseDependenciesOutputPaths(Compilation &C, llvm::SmallString<128> &Buf, CommandOutput *Output) const { if (C.getArgs().hasArg(options::OPT_emit_dependencies)) { - addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, - workingDirectory); + auto depPath = computeAuxiliaryOutputPath( + C, *Output, file_types::TY_Dependencies, OutputMap, workingDirectory); + C.addDependencyPathOrCreateDummy(depPath, [&] { + addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap, + workingDirectory); + }); } if (C.getIncrementalBuildEnabled()) { - addAuxiliaryOutput(C, *Output, file_types::TY_SwiftDeps, OutputMap, - workingDirectory); + file_types::forEachIncrementalOutputType([&](file_types::ID type) { + if (C.getEnableSourceRangeDependencies() || C.IncrementalComparator || + type == file_types::TY_SwiftDeps) + addAuxiliaryOutput(C, *Output, type, OutputMap, workingDirectory); + }); } chooseLoadedModuleTracePath(C, workingDirectory, Buf, Output); } @@ -3094,6 +3275,7 @@ void Driver::printHelp(bool ShowHidden) const { case DriverKind::Batch: case DriverKind::AutolinkExtract: case DriverKind::SwiftIndent: + case DriverKind::SymbolGraph: ExcludedFlagsBitmask |= options::NoBatchOption; break; } diff --git a/lib/Driver/DriverIncrementalRanges.cpp b/lib/Driver/DriverIncrementalRanges.cpp new file mode 100644 index 0000000000000..b7ff68b91d462 --- /dev/null +++ b/lib/Driver/DriverIncrementalRanges.cpp @@ -0,0 +1,290 @@ +//===------ DriverIncrementalRanges.cpp ------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Driver/DriverIncrementalRanges.h" + +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsDriver.h" +#include "swift/Driver/Compilation.h" +#include "swift/Driver/Job.h" +#include "swift/Driver/SourceComparator.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +// These are the definitions for managing serializable source locations so that +// the driver can implement incremental compilation based on +// source ranges. + +using namespace swift; +using namespace incremental_ranges; +using namespace driver; + + +//============================================================================== +// MARK: SourceRangeBasedInfo - constructing +//============================================================================== + +SourceRangeBasedInfo::SourceRangeBasedInfo( + StringRef primaryInputPath, + SwiftRangesFileContents &&swiftRangesFileContents, + SourceComparator::LRRanges &&changedRanges, Ranges &&nonlocalChangedRanges) + : primaryInputPath(primaryInputPath), + swiftRangesFileContents(std::move(swiftRangesFileContents)), + changedRanges(std::move(changedRanges)), + nonlocalChangedRanges(std::move(nonlocalChangedRanges)) { + assert(!primaryInputPath.empty() && "Must be a compile job"); +} + +SourceRangeBasedInfo::SourceRangeBasedInfo(SourceRangeBasedInfo &&x) + : primaryInputPath(x.primaryInputPath), + swiftRangesFileContents(std::move(x.swiftRangesFileContents)), + changedRanges(std::move(x.changedRanges)), + nonlocalChangedRanges(std::move(x.nonlocalChangedRanges)) {} + +//============================================================================== +// MARK: loading +//============================================================================== + +Optional SourceRangeBasedInfo::loadInfoForOneJob( + const Job *cmd, const bool showIncrementalBuildDecisions, + DiagnosticEngine &diags) { + StringRef primaryInputPath = cmd->getFirstSwiftPrimaryInput(); + if (primaryInputPath.empty()) + return None; + + const StringRef compiledSourcePath = + cmd->getOutput().getAdditionalOutputForType( + file_types::TY_CompiledSource); + const StringRef swiftRangesPath = + cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftRanges); + + return loadInfoForOnePrimary(primaryInputPath, compiledSourcePath, + swiftRangesPath, showIncrementalBuildDecisions, + diags); +} + +Optional SourceRangeBasedInfo::loadInfoForOnePrimary( + StringRef primaryInputPath, StringRef compiledSourcePath, + StringRef swiftRangesPath, bool showIncrementalBuildDecisions, + DiagnosticEngine &diags) { + + auto removeSupplementaryPaths = [&] { + if (auto ec = llvm::sys::fs::remove(compiledSourcePath)) + llvm::errs() << "WARNING could not remove: " << compiledSourcePath; + if (auto ec = llvm::sys::fs::remove(swiftRangesPath)) + llvm::errs() << "WARNING could not remove: " << swiftRangesPath; + }; + + assert(!primaryInputPath.empty() && "Must have a primary to load info."); + + // nonexistant primary -> it was removed since invoking swift?! + if (!llvm::sys::fs::exists(primaryInputPath)) { + if (showIncrementalBuildDecisions) + llvm::outs() << primaryInputPath << " was removed."; + // so they won't be used if primary gets re-added + removeSupplementaryPaths(); + return None; + } + + auto swiftRangesFileContents = loadSwiftRangesFileContents( + swiftRangesPath, primaryInputPath, showIncrementalBuildDecisions, diags); + + auto changedRanges = loadChangedRanges(compiledSourcePath, primaryInputPath, + showIncrementalBuildDecisions, diags); + + if (!swiftRangesFileContents || !changedRanges) { + removeSupplementaryPaths(); + return None; + } + + Ranges nonlocalChangedRanges = computeNonlocalChangedRanges( + swiftRangesFileContents.getValue(), changedRanges.getValue()); + return SourceRangeBasedInfo( + primaryInputPath, std::move(swiftRangesFileContents.getValue()), + std::move(changedRanges.getValue()), std::move(nonlocalChangedRanges)); +} + +Optional +SourceRangeBasedInfo::loadSwiftRangesFileContents( + const StringRef swiftRangesPath, const StringRef primaryInputPath, + const bool showIncrementalBuildDecisions, DiagnosticEngine &diags) { + auto bufferOrError = llvm::MemoryBuffer::getFile(swiftRangesPath); + if (auto ec = bufferOrError.getError()) { + diags.diagnose(SourceLoc(), diag::warn_unable_to_load_swift_ranges, + swiftRangesPath, ec.message()); + return None; + } + return SwiftRangesFileContents::load(primaryInputPath, *bufferOrError->get(), + showIncrementalBuildDecisions, diags); +} + +Optional SourceRangeBasedInfo::loadChangedRanges( + const StringRef compiledSourcePath, const StringRef primaryInputPath, + const bool showIncrementalBuildDecisions, DiagnosticEngine &diags) { + + // Shortcut the diff if the saved source is newer than the actual source. + auto isPreviouslyCompiledNewer = + isFileNewerThan(compiledSourcePath, primaryInputPath, diags); + if (!isPreviouslyCompiledNewer) + return None; + if (isPreviouslyCompiledNewer.getValue()) + return SourceComparator::LRRanges(); // no changes + + auto whatWasPreviouslyCompiled = + llvm::MemoryBuffer::getFile(compiledSourcePath); + if (auto ec = whatWasPreviouslyCompiled.getError()) { + diags.diagnose(SourceLoc(), diag::warn_unable_to_load_compiled_swift, + compiledSourcePath, ec.message()); + return None; + } + + auto whatIsAboutToBeCompiled = llvm::MemoryBuffer::getFile(primaryInputPath); + if (auto ec = whatIsAboutToBeCompiled.getError()) { + diags.diagnose(SourceLoc(), diag::warn_unable_to_load_primary, + primaryInputPath, ec.message()); + return None; + } + // SourceComparator::test(); + auto comp = SourceComparator(whatWasPreviouslyCompiled->get()->getBuffer(), + whatIsAboutToBeCompiled->get()->getBuffer()); + comp.compare(); + // lhs in terms of old version + return comp.convertAllMismatches(); +} + +/// Return true if lhs is newer than rhs, or None for error. +Optional SourceRangeBasedInfo::isFileNewerThan(StringRef lhs, + StringRef rhs, + DiagnosticEngine &diags) { + auto getModTime = [&](StringRef path) -> Optional> { + llvm::sys::fs::file_status status; + if (auto statError = llvm::sys::fs::status(path, status)) { + diags.diagnose(SourceLoc(), diag::warn_cannot_stat_input, + llvm::sys::path::filename(path), statError.message()); + return None; + } + return status.getLastModificationTime(); + }; + const auto lhsModTime = getModTime(lhs); + const auto rhsModTime = getModTime(rhs); + return !lhsModTime || !rhsModTime + ? None + : Optional(lhsModTime.getValue() > rhsModTime.getValue()); +} + +Optional SwiftRangesFileContents::load( + const StringRef primaryPath, const llvm::MemoryBuffer &swiftRangesBuffer, + const bool showIncrementalBuildDecisions, DiagnosticEngine &diags) { + + if (!swiftRangesBuffer.getBuffer().startswith(header)) { + diags.diagnose(SourceLoc(), diag::warn_bad_swift_ranges_header, + swiftRangesBuffer.getBufferIdentifier()); + return None; + } + + llvm::yaml::Input yamlReader(llvm::MemoryBufferRef(swiftRangesBuffer), + nullptr); + + SwiftRangesFileContents contents; + yamlReader >> contents; + if (yamlReader.error()) { + diags.diagnose(SourceLoc(), diag::warn_bad_swift_ranges_format, + swiftRangesBuffer.getBufferIdentifier(), + yamlReader.error().message()); + return None; + } + return contents; +} + +Ranges SourceRangeBasedInfo::computeNonlocalChangedRanges( + const SwiftRangesFileContents &swiftRangesFileContents, + const SourceComparator::LRRanges &changedRanges) { + return SerializableSourceRange::findAllOutliers( + changedRanges.lhs(), swiftRangesFileContents.noninlinableFunctionBodies); +} +//============================================================================== +// MARK: scheduling +//============================================================================== + +static std::string rangeStrings(std::string prefix, const Ranges &ranges) { + std::string s = prefix; + interleave(ranges.begin(), ranges.end(), + [&](const SerializableSourceRange &r) { s += r.printString(); }, + [&] { s += ", "; }); + return s; +} + +bool SourceRangeBasedInfo::didInputChangeAtAll( + DiagnosticEngine &, + function_ref noteBuilding) const { + const auto &changesToOldSource = changedRanges.lhs(); + if (changesToOldSource.empty()) + noteBuilding(/*willBeBuilding=*/false, "Did not change at all"); + else + noteBuilding(/*willBeBuilding=*/true, + rangeStrings("changed at ", changesToOldSource)); + return !changesToOldSource.empty(); +} + +bool SourceRangeBasedInfo::didInputChangeNonlocally( + DiagnosticEngine &, + function_ref noteInitiallyCascading) const { + if (nonlocalChangedRanges.empty()) + noteInitiallyCascading(false, "did not change outside any function bodies"); + else + noteInitiallyCascading(true, + rangeStrings("changed outside a function body at: ", + nonlocalChangedRanges)); + return !nonlocalChangedRanges.empty(); +} + + +//============================================================================== +// MARK: SourceRangeBasedInfo - printing +//============================================================================== + +void SourceRangeBasedInfo::dump(const bool dumpCompiledSourceDiffs, + const bool dumpSwiftRanges) const { + if (!dumpSwiftRanges && !dumpCompiledSourceDiffs) + return; + if (dumpSwiftRanges) + swiftRangesFileContents.dump(primaryInputPath); + if (dumpCompiledSourceDiffs) + dumpChangedRanges(); +} + +void SourceRangeBasedInfo::dumpChangedRanges() const { + const auto primaryFilename = llvm::sys::path::filename(primaryInputPath); + + auto dumpRangeSet = [&](StringRef which, StringRef wrt, + const Ranges &ranges) { + llvm::errs() << "*** " << which << " changed ranges in '" << primaryFilename + << "' (w.r.t " << wrt << ") ***\n"; + for (const auto &r : ranges) + llvm::errs() << "- " << r.printString() << "\n"; + }; + if (changedRanges.empty()) { + assert(nonlocalChangedRanges.empty() && "A fortiori."); + dumpRangeSet("no", "previously- or about-to-be-compiled", {}); + return; + } + dumpRangeSet("all", "previously-compiled", changedRanges.lhs()); + dumpRangeSet("all", "to-be-compiled", changedRanges.rhs()); + dumpRangeSet("nonlocal", "previously-compiled", nonlocalChangedRanges); + llvm::errs() << "\n"; +} diff --git a/lib/Driver/ExperimentalDependencyDriverGraph.cpp b/lib/Driver/ExperimentalDependencyDriverGraph.cpp deleted file mode 100644 index 08c69f900e2d0..0000000000000 --- a/lib/Driver/ExperimentalDependencyDriverGraph.cpp +++ /dev/null @@ -1,531 +0,0 @@ -//===-- ExperimentalDependencyGraph.cpp ------------------------------------==// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "swift/Driver/ExperimentalDependencyDriverGraph.h" -// Next two includes needed for reporting errors opening dot file for writing. -#include "swift/AST/DiagnosticsFrontend.h" -#include "swift/AST/FileSystem.h" -#include "swift/Basic/ReferenceDependencyKeys.h" -#include "swift/Basic/Statistic.h" -#include "swift/Demangling/Demangle.h" -#include "swift/Driver/Job.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringSet.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/YAMLParser.h" -#include "llvm/Support/raw_ostream.h" - -// Definitions for the portion experimental dependency system used by the -// driver. - -using namespace swift; - -using namespace swift::experimental_dependencies; -using namespace swift::driver; - -//============================================================================== -// MARK: Interfacing to Compilation -//============================================================================== - -using LoadResult = experimental_dependencies::DependencyGraphImpl::LoadResult; - -LoadResult ModuleDepGraph::loadFromPath(const Job *Cmd, StringRef path, - DiagnosticEngine &diags) { - FrontendStatsTracer tracer(stats, "experimental-dependencies-loadFromPath"); - - if (driverDotFileBasePath.empty()) { - driverDotFileBasePath = path; - llvm::sys::path::remove_filename(driverDotFileBasePath); - llvm::sys::path::append(driverDotFileBasePath, "driver"); - } - - auto buffer = llvm::MemoryBuffer::getFile(path); - if (!buffer) - return LoadResult::HadError; - auto r = loadFromBuffer(Cmd, *buffer.get()); - if (emitExperimentalDependencyDotFileAfterEveryImport) - emitDotFileForJob(diags, Cmd); - if (verifyExperimentalDependencyGraphAfterEveryImport) - verify(); - return r; -} - -LoadResult ModuleDepGraph::loadFromBuffer(const Job *job, - llvm::MemoryBuffer &buffer) { - - Optional sourceFileDepGraph = - SourceFileDepGraph::loadFromBuffer(buffer); - if (!sourceFileDepGraph) - return DependencyGraphImpl::LoadResult::HadError; - addIndependentNode(job); - return integrate(sourceFileDepGraph.getValue()); -} - -bool ModuleDepGraph::isMarked(const Job *cmd) const { - return cascadingJobs.count(getSwiftDeps(cmd)); -} - -void ModuleDepGraph::markTransitive( - SmallVectorImpl &consequentJobsToRecompile, - const Job *jobToBeRecompiled, const void *ignored) { - FrontendStatsTracer tracer(stats, "experimental-dependencies-markTransitive"); - - std::unordered_set dependentNodes; - const StringRef swiftDepsToBeRecompiled = getSwiftDeps(jobToBeRecompiled); - // Do the traversal. - for (auto &fileAndNode : nodeMap[swiftDepsToBeRecompiled]) { - assert(isCurrentPathForTracingEmpty()); - findDependentNodesAndRecordCascadingOnes(dependentNodes, - fileAndNode.second); - } - computeUniqueJobsFromNodes(consequentJobsToRecompile, dependentNodes); -} - -void ModuleDepGraph::computeUniqueJobsFromNodes( - SmallVectorImpl &jobs, - const std::unordered_set &nodes) { - std::unordered_set swiftDepsOfNodes; - for (const ModuleDepGraphNode *n : nodes) { - if (!n->getSwiftDeps().hasValue()) - continue; - const std::string &swiftDeps = n->getSwiftDeps().getValue(); - if (swiftDepsOfNodes.insert(swiftDeps).second) { - assert(n->assertImplementationMustBeInAFile()); - ensureJobIsTracked(swiftDeps); - jobs.push_back(getJob(swiftDeps)); - } - } -} - -bool ModuleDepGraph::markIntransitive(const Job *node) { - return rememberThatJobCascades(getSwiftDeps(node)); -} - -void ModuleDepGraph::addIndependentNode(const Job *job) { - // No need to create any nodes; that will happen when the swiftdeps file is - // read. Just record the correspondence. - jobsBySwiftDeps.insert(std::make_pair(getSwiftDeps(job), job)); -} - -std::vector ModuleDepGraph::getExternalDependencies() const { - return std::vector(externalDependencies.begin(), - externalDependencies.end()); -} - -// Add every (swiftdeps) use of the external dependency to uses. -void ModuleDepGraph::markExternal(SmallVectorImpl &uses, - StringRef externalDependency) { - FrontendStatsTracer tracer(stats, "experimental-dependencies-markExternal"); - // TODO move nameForDep into key - // These nodes will depend on the *interface* of the external Decl. - DependencyKey key = - DependencyKey::createDependedUponKey( - externalDependency.str()); - // collect answers into useSet - std::unordered_set visitedSet; - for (const ModuleDepGraphNode *useNode : usesByDef[key]) { - const Job *job = getJob(useNode->getSwiftDeps()); - if (!isMarked(job)) - continue; - uses.push_back(job); - markTransitive(uses, job); - } -} - -//============================================================================== -// MARK: Integrating SourceFileDepGraph into ModuleDepGraph -//============================================================================== - -LoadResult ModuleDepGraph::integrate(const SourceFileDepGraph &g) { - FrontendStatsTracer tracer(stats, "experimental-dependencies-integrate"); - - StringRef swiftDeps = g.getSwiftDepsFromSourceFileProvide(); - // When done, disappearedNodes contains the nodes which no longer exist. - auto disappearedNodes = nodeMap[swiftDeps]; - // When done, changeDependencyKeys contains a list of keys that changed - // as a result of this integration. - auto changedNodes = std::unordered_set(); - - g.forEachNode([&](const SourceFileDepGraphNode *integrand) { - const auto &key = integrand->getKey(); - auto preexistingMatch = findPreexistingMatch(swiftDeps, integrand); - if (preexistingMatch.hasValue() && - preexistingMatch.getValue().first == LocationOfPreexistingNode::here) - disappearedNodes.erase(key); // Node was and still is. Do not erase it. - const bool changed = - integrateSourceFileDepGraphNode(g, integrand, preexistingMatch); - if (changed) - changedNodes.insert(key); - }); - - for (auto &p : disappearedNodes) { - changedNodes.insert(p.second->getKey()); - removeNode(p.second); - } - - // TODO: use changedKeys sometime, for instance by returning them - // as part of return value so that the driver can only mark from them. - return changedNodes.empty() ? LoadResult::UpToDate - : LoadResult::AffectsDownstream; -} - -ModuleDepGraph::PreexistingNodeIfAny ModuleDepGraph::findPreexistingMatch( - StringRef swiftDepsOfCompilationToBeIntegrated, - const SourceFileDepGraphNode *integrand) { - const auto &matches = nodeMap[integrand->getKey()]; - const auto &expatsIter = matches.find(""); - if (expatsIter != matches.end()) { - assert(matches.size() == 1 && - "If an expat exists, then must not be any matches in other files"); - return std::make_pair(LocationOfPreexistingNode::nowhere, - expatsIter->second); - } - if (integrand->getIsProvides()) { - const auto &preexistingNodeInPlaceIter = - matches.find(swiftDepsOfCompilationToBeIntegrated); - if (preexistingNodeInPlaceIter != matches.end()) - return std::make_pair(LocationOfPreexistingNode::here, - preexistingNodeInPlaceIter->second); - } - if (!matches.empty()) - return std::make_pair(LocationOfPreexistingNode::elsewhere, - matches.begin()->second); - return None; -} - -bool ModuleDepGraph::integrateSourceFileDepGraphNode( - const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, - const PreexistingNodeIfAny preexistingMatch) { - - // Track externalDependencies so Compilation can check them. - if (integrand->getKey().getKind() == NodeKind::externalDepend) - return externalDependencies.insert(integrand->getKey().getName()).second; - - if (integrand->isDepends()) - return false; // dependency will be handled by the use node - - StringRef swiftDepsOfSourceFileGraph = g.getSwiftDepsFromSourceFileProvide(); - auto changedAndUseNode = integrateSourceFileDeclNode( - integrand, swiftDepsOfSourceFileGraph, preexistingMatch); - recordWhatUseDependsUpon(g, integrand, changedAndUseNode.second); - return changedAndUseNode.first; -} - -std::pair -ModuleDepGraph::integrateSourceFileDeclNode( - const SourceFileDepGraphNode *integrand, - StringRef swiftDepsOfSourceFileGraph, - const PreexistingNodeIfAny preexistingMatch) { - - if (!preexistingMatch.hasValue()) { - auto *newNode = integrateByCreatingANewNode( - integrand, swiftDepsOfSourceFileGraph.str()); - return std::make_pair(true, newNode); // New node - } - const auto where = preexistingMatch.getValue().first; - auto *match = preexistingMatch.getValue().second; - switch (where) { - case LocationOfPreexistingNode::here: - return std::make_pair(match->integrateFingerprintFrom(integrand), match); - - case LocationOfPreexistingNode::nowhere: - // Some other file depended on this, but didn't know where it was. - moveNodeToDifferentFile(match, swiftDepsOfSourceFileGraph.str()); - match->integrateFingerprintFrom(integrand); - return std::make_pair(true, match); // New Decl, assume changed - - case LocationOfPreexistingNode::elsewhere: - auto *newNode = integrateByCreatingANewNode( - integrand, swiftDepsOfSourceFileGraph.str()); - return std::make_pair(true, newNode); // New node; - } - llvm_unreachable("impossible"); -} - -ModuleDepGraphNode *ModuleDepGraph::integrateByCreatingANewNode( - const SourceFileDepGraphNode *integrand, - const Optional swiftDepsForNewNode) { - const auto &key = integrand->getKey(); - ModuleDepGraphNode *newNode = new ModuleDepGraphNode( - key, integrand->getFingerprint(), swiftDepsForNewNode); - addToMap(newNode); - return newNode; -} - -void ModuleDepGraph::recordWhatUseDependsUpon( - const SourceFileDepGraph &g, - const SourceFileDepGraphNode *sourceFileUseNode, - ModuleDepGraphNode *moduleUseNode) { - g.forEachDefDependedUponBy(sourceFileUseNode, - [&](const SourceFileDepGraphNode *def) { - usesByDef[def->getKey()].insert(moduleUseNode); - }); -} - -void ModuleDepGraph::removeNode(ModuleDepGraphNode *n) { - eraseNodeFromMap(n); - delete n; -} - -//============================================================================== -// MARK: ModuleDepGraph access -//============================================================================== - -void ModuleDepGraph::forEachUseOf( - const ModuleDepGraphNode *def, - function_ref fn) { - auto iter = usesByDef.find(def->getKey()); - if (iter == usesByDef.end()) - return; - for (const ModuleDepGraphNode *useNode : iter->second) - fn(useNode); -} - -void ModuleDepGraph::forEachNode( - function_ref fn) const { - nodeMap.forEachEntry([&](const std::string &, const DependencyKey &, - ModuleDepGraphNode *n) { fn(n); }); -} - -void ModuleDepGraph::forEachMatchingNode( - const DependencyKey &key, - function_ref fn) const { - nodeMap.forEachValueMatching( - key, [&](const std::string &, ModuleDepGraphNode *n) { fn(n); }); -} - -void ModuleDepGraph::forEachArc( - function_ref - fn) const { - /// Use find instead of [] because this is const - for (const auto &defUse : usesByDef) - forEachMatchingNode(defUse.first, [&](const ModuleDepGraphNode *defNode) { - for (const auto &useNode : defUse.second) - fn(defNode, useNode); - }); -} - -//============================================================================== -// MARK: ModuleDepGraph traversal -//============================================================================== - -// Could be faster by passing in a file, not a node, but we are trying for -// generality. - -void ModuleDepGraph::findDependentNodesAndRecordCascadingOnes( - std::unordered_set &foundDependents, - const ModuleDepGraphNode *definition) { - - size_t pathLengthAfterArrival = traceArrival(definition); - - // Moved this out of the following loop for effieciency. - assert(definition->getSwiftDeps().hasValue() && - "Should only call me for Decl nodes."); - - forEachUseOf(definition, [&](const ModuleDepGraphNode *u) { - // Cycle recording and check. - if (!foundDependents.insert(u).second) - return; - if (u->getKey().isInterface() && u->getSwiftDeps().hasValue()) { - // An interface depends on something. Thus, if that something changes - // the interface must be recompiled. But if an interface changes, then - // anything using that interface must also be recompiled. - // So, the job containing the interface "cascades", in other words - // whenever that job gets recompiled, anything depending on it - // (since we don't have interface-specific dependency info as of Dec. - // 2018) must be recompiled. - rememberThatJobCascades(u->getSwiftDeps().getValue()); - findDependentNodesAndRecordCascadingOnes(foundDependents, u); - } - }); - traceDeparture(pathLengthAfterArrival); -} - -size_t ModuleDepGraph::traceArrival(const ModuleDepGraphNode *visitedNode) { - if (!currentPathIfTracing.hasValue()) - return 0; - auto ¤tPath = currentPathIfTracing.getValue(); - recordDependencyPathToJob(currentPath, getJob(visitedNode->getSwiftDeps())); - - currentPath.push_back(visitedNode); - return currentPath.size(); -} - -void ModuleDepGraph::recordDependencyPathToJob( - const std::vector &pathToJob, - const driver::Job *dependentJob) { - dependencyPathsToJobs.insert(std::make_pair(dependentJob, pathToJob)); -} - -void ModuleDepGraph::traceDeparture(size_t pathLengthAfterArrival) { - if (!currentPathIfTracing) - return; - auto ¤tPath = currentPathIfTracing.getValue(); - assert(pathLengthAfterArrival == currentPath.size() && - "Path must be maintained throughout recursive visits."); - currentPath.pop_back(); -} - -// Emitting Dot file for ModuleDepGraph -// =========================================== - -void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, - const Job *job) { - emitDotFile(diags, getSwiftDeps(job)); -} - -void ModuleDepGraph::emitDotFile(DiagnosticEngine &diags, StringRef baseName) { - unsigned seqNo = dotFileSequenceNumber[baseName]++; - std::string fullName = baseName.str() + "." + std::to_string(seqNo) + ".dot"; - withOutputFile(diags, fullName, [&](llvm::raw_ostream &out) { - emitDotFile(out); - return false; - }); -} - -void ModuleDepGraph::emitDotFile(llvm::raw_ostream &out) { - FrontendStatsTracer tracer(stats, "experimental-dependencies-emitDotFile"); - DotFileEmitter(out, *this, true, false).emit(); -} - -//============================================================================== -// MARK: ModuleDepGraph debugging -//============================================================================== - -void ModuleDepGraphNode::dump() const { - DepGraphNode::dump(); - if (getSwiftDeps().hasValue()) - llvm::errs() << " swiftDeps: <" << getSwiftDeps().getValue() << ">\n"; - else - llvm::errs() << " no swiftDeps\n"; -} - -bool ModuleDepGraph::verify() const { - FrontendStatsTracer tracer(stats, "experimental-dependencies-verify"); - verifyNodeMapEntries(); - verifyCanFindEachJob(); - verifyEachJobInGraphIsTracked(); - - return true; -} - -void ModuleDepGraph::verifyNodeMapEntries() const { - FrontendStatsTracer tracer(stats, - "experimental-dependencies-verifyNodeMapEntries"); - // TODO: disable when not debugging - std::array< - std::unordered_map>, - 2> - nodesSeenInNodeMap; - nodeMap.verify([&](const std::string &swiftDepsString, - const DependencyKey &key, ModuleDepGraphNode *n, - unsigned submapIndex) { - verifyNodeMapEntry(nodesSeenInNodeMap, swiftDepsString, key, n, - submapIndex); - }); -} - -void ModuleDepGraph::verifyNodeMapEntry( - std::array>, - 2> &nodesSeenInNodeMap, - const std::string &swiftDepsString, const DependencyKey &key, - ModuleDepGraphNode *n, const unsigned submapIndex) const { - verifyNodeIsUniqueWithinSubgraph(nodesSeenInNodeMap, swiftDepsString, key, n, - submapIndex); - verifyNodeIsInRightEntryInNodeMap(swiftDepsString, key, n); - key.verify(); - verifyExternalDependencyUniqueness(key); -} - -void ModuleDepGraph::verifyNodeIsUniqueWithinSubgraph( - std::array>, - 2> &nodesSeenInNodeMap, - const std::string &swiftDepsString, const DependencyKey &key, - ModuleDepGraphNode *const n, const unsigned submapIndex) const { - assert(submapIndex < nodesSeenInNodeMap.size() && - "submapIndex is out of bounds."); - auto iterInserted = nodesSeenInNodeMap[submapIndex][n->getKey()].insert( - std::make_pair(n->getSwiftDeps().hasValue() ? n->getSwiftDeps().getValue() - : std::string(), - n)); - if (!iterInserted.second) { - llvm_unreachable("duplicate driver keys"); - } -} - -void ModuleDepGraph::verifyNodeIsInRightEntryInNodeMap( - const std::string &swiftDepsString, const DependencyKey &key, - const ModuleDepGraphNode *const n) const { - const DependencyKey &nodeKey = n->getKey(); - const Optional swiftDeps = - swiftDepsString.empty() ? None : Optional(swiftDepsString); - (void)nodeKey; - (void)swiftDeps; - assert(n->getSwiftDeps() == swiftDeps || - mapCorruption("Node misplaced for swiftDeps")); - assert(nodeKey == key || mapCorruption("Node misplaced for key")); -} - -void ModuleDepGraph::verifyExternalDependencyUniqueness( - const DependencyKey &key) const { - assert((key.getKind() != NodeKind::externalDepend || - externalDependencies.count(key.getName()) == 1) && - "Ensure each external dependency is tracked exactly once"); -} - -void ModuleDepGraph::verifyCanFindEachJob() const { - FrontendStatsTracer tracer(stats, - "experimental-dependencies-verifyCanFindEachJob"); - for (const auto p : jobsBySwiftDeps) { - getJob(p.first); - } -} - -void ModuleDepGraph::verifyEachJobInGraphIsTracked() const { - FrontendStatsTracer tracer( - stats, "experimental-dependencies-verifyEachJobIsTracked"); - nodeMap.forEachKey1( - [&](const std::string &swiftDeps, const typename NodeMap::Key2Map &) { - ensureJobIsTracked(swiftDeps); - }); -} - -bool ModuleDepGraph::emitDotFileAndVerify(DiagnosticEngine &diags) { - if (!driverDotFileBasePath.empty()) - emitDotFile(diags, driverDotFileBasePath); - return verify(); -} - -/// Dump the path that led to \p node. -/// TODO: make output more like existing system's -void ModuleDepGraph::printPath(raw_ostream &out, - const driver::Job *jobToBeBuilt) const { - assert(currentPathIfTracing.hasValue() && - "Cannot print paths of paths weren't tracked."); - auto const allPaths = dependencyPathsToJobs.find(jobToBeBuilt); - if (allPaths == dependencyPathsToJobs.cend()) - return; - for (const auto *n : allPaths->second) { - out << n->humanReadableName() << "\n"; - } - out << "\n"; -} diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp new file mode 100644 index 0000000000000..5bda1956f9b4c --- /dev/null +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -0,0 +1,725 @@ +//===--- FineGrainedDependencyGraph.cpp ------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Driver/FineGrainedDependencyDriverGraph.h" +// Next two includes needed for reporting errors opening dot file for writing. +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/FileSystem.h" +#include "swift/Basic/ReferenceDependencyKeys.h" +#include "swift/Basic/Statistic.h" +#include "swift/Demangling/Demangle.h" +#include "swift/Driver/Job.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/raw_ostream.h" +#include + +// Definitions for the portion fine-grained dependency system used by the +// driver. + +using namespace swift; + +using namespace swift::fine_grained_dependencies; +using namespace swift::driver; + +//============================================================================== +// MARK: Affordances to unit tests +//============================================================================== +/// Initial underscore makes non-cascading, on member means private. + ModuleDepGraph::Changes +ModuleDepGraph::simulateLoad(const Job *cmd, + llvm::StringMap> simpleNames, + llvm::StringMap>> + compoundNames, + const bool includePrivateDeps, + const bool hadCompilationError) { + StringRef swiftDeps = + cmd->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); + assert(!swiftDeps.empty()); + StringRef interfaceHash = swiftDeps; + auto sfdg = SourceFileDepGraph::simulateLoad( + swiftDeps, includePrivateDeps, hadCompilationError, interfaceHash, + simpleNames, compoundNames); + + return loadFromSourceFileDepGraph(cmd, sfdg); +} + +std::string SourceFileDepGraph::noncascading(std::string name) { + std::string s{SourceFileDepGraph::noncascadingOrPrivatePrefix}; + s += name; + return s; +} + +LLVM_ATTRIBUTE_UNUSED +std::string SourceFileDepGraph::privatize(std::string name) { + std::string s{SourceFileDepGraph::noncascadingOrPrivatePrefix}; + s += name; + return s; +} + +LLVM_ATTRIBUTE_UNUSED +std::vector +ModuleDepGraph::printJobsForDebugging(const std::vector &jobs) { + llvm::errs() << "\nprintForDebugging: "; + for (auto *j : jobs) { + const auto swiftDeps = + j->getOutput().getAdditionalOutputForType(file_types::TY_SwiftDeps); + assert(!swiftDeps.empty()); + llvm::errs() << "job" << swiftDeps << ", "; + } + llvm::errs() << "\n"; + return jobs; +} +//============================================================================== +// MARK: Interfacing to Compilation +//============================================================================== + +ModuleDepGraph::Changes ModuleDepGraph::loadFromPath(const Job *Cmd, + StringRef path, + DiagnosticEngine &diags) { + FrontendStatsTracer tracer(stats, "fine-grained-dependencies-loadFromPath"); + + if (driverDotFileBasePath.empty()) { + driverDotFileBasePath = path; + llvm::sys::path::remove_filename(driverDotFileBasePath); + llvm::sys::path::append(driverDotFileBasePath, "driver"); + } + + auto buffer = llvm::MemoryBuffer::getFile(path); + if (!buffer) + return None; + auto r = loadFromBuffer(Cmd, *buffer.get()); + assert(path == getSwiftDeps(Cmd) && "Should be reading the job's swiftdeps"); + assert(!r || !nodeMap[path].empty() && + "Must have a node for the whole file"); + if (emitFineGrainedDependencyDotFileAfterEveryImport) + emitDotFileForJob(diags, Cmd); + if (verifyFineGrainedDependencyGraphAfterEveryImport) + verify(); + return r; +} + +/// Returns None for error or a set of changed keys +ModuleDepGraph::Changes +ModuleDepGraph::loadFromBuffer(const Job *job, llvm::MemoryBuffer &buffer) { + Optional sourceFileDepGraph = + SourceFileDepGraph::loadFromBuffer(buffer); + if (!sourceFileDepGraph) + return None; + return loadFromSourceFileDepGraph(job, sourceFileDepGraph.getValue()); +} + +ModuleDepGraph::Changes ModuleDepGraph::loadFromSourceFileDepGraph( + const Job *job, const SourceFileDepGraph &sourceFileDepGraph) { + registerJob(job); + return integrate(sourceFileDepGraph, getSwiftDeps(job)); +} + +bool ModuleDepGraph::haveAnyNodesBeenTraversedIn(const Job *cmd) const { + const StringRef swiftDeps = getSwiftDeps(cmd); + + // optimization + const auto fileKey = DependencyKey::createKeyForWholeSourceFile(swiftDeps); + if (const auto fileNode = nodeMap.find(swiftDeps, fileKey)) { + if (fileNode && fileNode.getValue()->getHasBeenTraced()) + return true; + } + + bool result = false; + forEachNodeInJob(swiftDeps, [&](const ModuleDepGraphNode *n) { + if (n->getHasBeenTraced()) + result = true; + }); + return result; +} + +std::vector ModuleDepGraph::findJobsToRecompileWhenWholeJobChanges( + const Job *jobToBeRecompiled) { + std::vector allNodesInJob; + forEachNodeInJob(getSwiftDeps(jobToBeRecompiled), + [&](ModuleDepGraphNode *n) { allNodesInJob.push_back(n); }); + return findJobsToRecompileWhenNodesChange(allNodesInJob); +} + +template +std::vector +ModuleDepGraph::findJobsToRecompileWhenNodesChange(const Nodes &nodes) { + std::vector foundDependents; + for (ModuleDepGraphNode *n : nodes) + findPreviouslyUntracedDependents(foundDependents, n); + return jobsContaining(foundDependents); +} + +template std::vector +ModuleDepGraph::findJobsToRecompileWhenNodesChange< + std::unordered_set>( + const std::unordered_set &); + +template std::vector +ModuleDepGraph::findJobsToRecompileWhenNodesChange< + std::vector>( + const std::vector &); + +std::vector ModuleDepGraph::computeSwiftDepsFromNodes( + ArrayRef nodes) const { + llvm::StringSet<> swiftDepsOfNodes; + for (const ModuleDepGraphNode *n : nodes) { + if (!n->getIsProvides()) + continue; + const std::string &swiftDeps = n->getSwiftDepsOfProvides(); + swiftDepsOfNodes.insert(swiftDeps); + } + std::vector swiftDepsVec; + for (const auto &entry : swiftDepsOfNodes) + swiftDepsVec.push_back(entry.getKey().str()); + return swiftDepsVec; +} + +std::vector ModuleDepGraph::jobsContaining( + ArrayRef nodes) const { + std::vector jobs; + for (StringRef swiftDeps : computeSwiftDepsFromNodes(nodes)) + jobs.push_back(getJob(swiftDeps.str())); + return jobs; +} + +void ModuleDepGraph::registerJob(const Job *job) { + // No need to create any nodes; that will happen when the swiftdeps file is + // read. Just record the correspondence. + jobsBySwiftDeps.insert(std::make_pair(getSwiftDeps(job), job)); +} + +std::vector ModuleDepGraph::getExternalDependencies() const { + return std::vector(externalDependencies.begin(), + externalDependencies.end()); +} + +// Add every (swiftdeps) use of the external dependency to foundJobs. +std::vector ModuleDepGraph::findExternallyDependentUntracedJobs( + StringRef externalDependency) { + FrontendStatsTracer tracer( + stats, "fine-grained-dependencies-findExternallyDependentUntracedJobs"); + std::vector foundJobs; + forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( + externalDependency, [&](const Job *job) { + foundJobs.push_back(job); + for (const Job *marked : findJobsToRecompileWhenWholeJobChanges(job)) { + // findJobsToRecompileWhenWholeJobChanges is reflexive + // Don't return job twice. + if (marked != job) + foundJobs.push_back(marked); + } + }); + return foundJobs; +} + +void ModuleDepGraph::forEachUntracedJobDirectlyDependentOnExternalSwiftDeps( + StringRef externalSwiftDeps, function_ref fn) { + // TODO move nameForDep into key + // These nodes will depend on the *interface* of the external Decl. + DependencyKey key = + DependencyKey::createDependedUponKey( + externalSwiftDeps.str()); + for (const ModuleDepGraphNode *useNode : usesByDef[key]) { + if (!useNode->getHasBeenTraced()) + fn(getJob(useNode->getSwiftDepsOfProvides())); + } +} + +//============================================================================== +// MARK: Integrating SourceFileDepGraph into ModuleDepGraph +//============================================================================== + +ModuleDepGraph::Changes ModuleDepGraph::integrate(const SourceFileDepGraph &g, + StringRef swiftDepsOfJob) { + FrontendStatsTracer tracer(stats, "fine-grained-dependencies-integrate"); + + // When done, disappearedNodes contains the nodes which no longer exist. + auto disappearedNodes = nodeMap[swiftDepsOfJob]; + // When done, changeDependencyKeys contains a list of keys that changed + // as a result of this integration. + auto changedNodes = std::unordered_set(); + + g.forEachNode([&](const SourceFileDepGraphNode *integrand) { + const auto &key = integrand->getKey(); + + auto preexistingMatch = findPreexistingMatch(swiftDepsOfJob, integrand); + if (preexistingMatch.hasValue() && + preexistingMatch.getValue().first == LocationOfPreexistingNode::here) + disappearedNodes.erase(key); // Node was and still is. Do not erase it. + + NullablePtr newNodeOrChangedNode = + integrateSourceFileDepGraphNode(g, integrand, preexistingMatch, + swiftDepsOfJob); + + if (auto *n = newNodeOrChangedNode.getPtrOrNull()) + changedNodes.insert(n); + }); + + for (auto &p : disappearedNodes) { + changedNodes.insert(p.second); + eraseNodeFromJob(p.second); + } + + // Make sure the changes can be retraced: + for (auto *n : changedNodes) + n->clearHasBeenTraced(); + + return changedNodes; +} + +ModuleDepGraph::PreexistingNodeIfAny ModuleDepGraph::findPreexistingMatch( + StringRef swiftDepsOfCompilationToBeIntegrated, + const SourceFileDepGraphNode *integrand) const { + const auto *matches = nodeMap.find(integrand->getKey()).getPtrOrNull(); + if (!matches) + return None; + const auto &expatsIter = matches->find(""); + if (expatsIter != matches->end()) { + assert(matches->size() == 1 && + "If an expat exists, then must not be any matches in other files"); + return std::make_pair(LocationOfPreexistingNode::nowhere, + expatsIter->second); + } + if (integrand->getIsProvides()) { + const auto &preexistingNodeInPlaceIter = + matches->find(swiftDepsOfCompilationToBeIntegrated); + if (preexistingNodeInPlaceIter != matches->end()) + return std::make_pair(LocationOfPreexistingNode::here, + preexistingNodeInPlaceIter->second); + } + if (!matches->empty()) + return std::make_pair(LocationOfPreexistingNode::elsewhere, + matches->begin()->second); + return None; +} + +NullablePtr ModuleDepGraph::integrateSourceFileDepGraphNode( + const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, + const PreexistingNodeIfAny preexistingMatch, + const StringRef swiftDepsOfJob) { + + if (!integrand->getIsProvides()) + return nullptr; // depends are captured by recordWhatUseDependsUpon below + + auto changedAndIntegrationResultNode = + integrateSourceFileDeclNode(integrand, swiftDepsOfJob, preexistingMatch); + + ModuleDepGraphNode *const integratedNode = + changedAndIntegrationResultNode.second; + const bool hasNewExternalDependency = + recordWhatUseDependsUpon(g, integrand, integratedNode); + + NullablePtr changedNode = + changedAndIntegrationResultNode.first || hasNewExternalDependency + ? integratedNode + : nullptr; + return changedNode; +} + +std::pair +ModuleDepGraph::integrateSourceFileDeclNode( + const SourceFileDepGraphNode *integrand, StringRef swiftDepsOfJob, + const PreexistingNodeIfAny preexistingMatch) { + + if (!preexistingMatch.hasValue()) { + // The driver will be accessing nodes by the swiftDeps of the job, + // so pass that in. + auto *newNode = + integrateByCreatingANewNode(integrand, swiftDepsOfJob.str()); + return std::make_pair(true, newNode); // New node + } + const auto where = preexistingMatch.getValue().first; + auto *match = preexistingMatch.getValue().second; + switch (where) { + case LocationOfPreexistingNode::here: + return std::make_pair(match->integrateFingerprintFrom(integrand), match); + + case LocationOfPreexistingNode::nowhere: + // Some other file depended on this, but didn't know where it was. + moveNodeToDifferentFile(match, swiftDepsOfJob.str()); + match->integrateFingerprintFrom(integrand); + return std::make_pair(true, match); // New Decl, assume changed + + case LocationOfPreexistingNode::elsewhere: + auto *newNode = + integrateByCreatingANewNode(integrand, swiftDepsOfJob.str()); + return std::make_pair(true, newNode); // New node; + } + llvm_unreachable("impossible"); +} + +ModuleDepGraphNode *ModuleDepGraph::integrateByCreatingANewNode( + const SourceFileDepGraphNode *integrand, + const Optional swiftDepsForNewNode) { + assert(integrand->getIsProvides() && + "Dependencies are arcs in the module graph"); + const auto &key = integrand->getKey(); + ModuleDepGraphNode *newNode = new ModuleDepGraphNode( + key, integrand->getFingerprint(), swiftDepsForNewNode); + addToMap(newNode); + return newNode; +} + +bool ModuleDepGraph::recordWhatUseDependsUpon( + const SourceFileDepGraph &g, + const SourceFileDepGraphNode *sourceFileUseNode, + ModuleDepGraphNode *moduleUseNode) { + bool useHasNewExternalDependency = false; + g.forEachDefDependedUponBy( + sourceFileUseNode, [&](const SourceFileDepGraphNode *def) { + const bool isNewUse = + usesByDef[def->getKey()].insert(moduleUseNode).second; + if (isNewUse && def->getKey().getKind() == NodeKind::externalDepend) { + StringRef externalSwiftDeps = def->getKey().getName(); + externalDependencies.insert(externalSwiftDeps); + useHasNewExternalDependency = true; + } + }); + return useHasNewExternalDependency; +} + +void ModuleDepGraph::eraseNodeFromGraphAndFreeIt(ModuleDepGraphNode *n) { + eraseNodeFromJob(n); + eraseNodeFromCurrentPathIfTracing(n); + eraseNodeFromDependencyPathToJobs(n); + + delete n; +} + +void ModuleDepGraph::eraseNodeFromJob(ModuleDepGraphNode *n) { + eraseNodeFromMap(n); + eraseUsesOfNode(n); +} + +//============================================================================== +// MARK: ModuleDepGraph access +//============================================================================== +void ModuleDepGraph::forEachUseOf( + const ModuleDepGraphNode *def, + function_ref fn) const { + auto iter = usesByDef.find(def->getKey()); + if (iter == usesByDef.end()) + return; + for (ModuleDepGraphNode *useNode : iter->second) + fn(useNode); + // Add in implicit interface->implementation dependency + forCorrespondingImplementationOfProvidedInterface(def, fn); +} + +void ModuleDepGraph::forCorrespondingImplementationOfProvidedInterface( + const ModuleDepGraphNode *interfaceNode, + function_ref fn) const { + if (!interfaceNode->getKey().isInterface() || !interfaceNode->getIsProvides()) + return; + const auto swiftDeps = interfaceNode->getSwiftDeps().getValue(); + const auto &interfaceKey = interfaceNode->getKey(); + const DependencyKey implementationKey( + interfaceKey.getKind(), DeclAspect::implementation, + interfaceKey.getContext(), interfaceKey.getName()); + if (const auto implementationNode = + nodeMap.find(swiftDeps, implementationKey)) + fn(implementationNode.getValue()); +} + +void ModuleDepGraph::forEachNode( + function_ref fn) const { + nodeMap.forEachEntry([&](const std::string &, const DependencyKey &, + ModuleDepGraphNode *n) { fn(n); }); +} + +void ModuleDepGraph::forEachMatchingNode( + const DependencyKey &key, + function_ref fn) const { + nodeMap.forEachValueMatching( + key, [&](const std::string &, ModuleDepGraphNode *n) { fn(n); }); +} + +void ModuleDepGraph::forEachArc( + function_ref + fn) const { + forEachNode([&](const ModuleDepGraphNode *defNode) { + forEachUseOf(defNode, [&](const ModuleDepGraphNode *const useNode) { + fn(defNode, useNode); + }); + }); +} + +void ModuleDepGraph::forEachNodeInJob( + StringRef swiftDeps, function_ref fn) const { + if (const auto *nodesByKeys = nodeMap.find(swiftDeps).getPtrOrNull()) { + for (const auto &keyAndNode : *nodesByKeys) + fn(keyAndNode.second); + } +} + +//============================================================================== +// MARK: ModuleDepGraph traversal +//============================================================================== + +void ModuleDepGraph::findPreviouslyUntracedDependents( + std::vector &foundDependents, + ModuleDepGraphNode *definition) { + + if (definition->getHasBeenTraced()) + return; + definition->setHasBeenTraced(); + + foundDependents.push_back(definition); + + // If this use also provides something, follow it + if (!definition->getIsProvides()) + return; // No need to look for uses; provides nothing + + size_t pathLengthAfterArrival = traceArrival(definition); + + forEachUseOf(definition, [&](ModuleDepGraphNode *u) { + // If this use also provides something, follow it + findPreviouslyUntracedDependents(foundDependents, u); + }); + + traceDeparture(pathLengthAfterArrival); +} + +size_t ModuleDepGraph::traceArrival(const ModuleDepGraphNode *visitedNode) { + if (!currentPathIfTracing.hasValue()) + return 0; + auto ¤tPath = currentPathIfTracing.getValue(); + currentPath.push_back(visitedNode); + const auto visitedSwiftDepsIfAny = visitedNode->getSwiftDeps(); + recordDependencyPathToJob(currentPath, getJob(visitedSwiftDepsIfAny)); + return currentPath.size(); +} + +void ModuleDepGraph::recordDependencyPathToJob( + const std::vector &pathToJob, + const driver::Job *dependentJob) { + dependencyPathsToJobs.insert(std::make_pair(dependentJob, pathToJob)); +} + +void ModuleDepGraph::traceDeparture(size_t pathLengthAfterArrival) { + if (!currentPathIfTracing) + return; + auto ¤tPath = currentPathIfTracing.getValue(); + assert(pathLengthAfterArrival == currentPath.size() && + "Path must be maintained throughout recursive visits."); + currentPath.pop_back(); +} + +// ============================================================================= +// MARK: Emitting Dot file for ModuleDepGraph +// ============================================================================= + +void ModuleDepGraph::emitDotFileForJob(DiagnosticEngine &diags, + const Job *job) { + emitDotFile(diags, getSwiftDeps(job)); +} + +void ModuleDepGraph::emitDotFile(DiagnosticEngine &diags, StringRef baseName) { + unsigned seqNo = dotFileSequenceNumber[baseName]++; + std::string fullName = + baseName.str() + "-post-integration." + std::to_string(seqNo) + ".dot"; + withOutputFile(diags, fullName, [&](llvm::raw_ostream &out) { + emitDotFile(out); + return false; + }); +} + +void ModuleDepGraph::emitDotFile(llvm::raw_ostream &out) { + FrontendStatsTracer tracer(stats, "fine-grained-dependencies-emitDotFile"); + DotFileEmitter(out, *this, true, false).emit(); +} + +//============================================================================== +// MARK: ModuleDepGraph debugging +//============================================================================== + +void ModuleDepGraphNode::dump() const { + DepGraphNode::dump(); + if (getIsProvides()) + llvm::errs() << " swiftDeps: <" << getSwiftDepsOfProvides() << ">\n"; + else + llvm::errs() << " no swiftDeps\n"; +} + +bool ModuleDepGraph::verify() const { + FrontendStatsTracer tracer(stats, "fine-grained-dependencies-verify"); + verifyNodeMapEntries(); + verifyCanFindEachJob(); + verifyEachJobInGraphIsTracked(); + + return true; +} + +void ModuleDepGraph::verifyNodeMapEntries() const { + FrontendStatsTracer tracer(stats, + "fine-grained-dependencies-verifyNodeMapEntries"); + // TODO: disable when not debugging + std::array< + std::unordered_map>, + 2> + nodesSeenInNodeMap; + nodeMap.verify([&](const std::string &swiftDepsString, + const DependencyKey &key, ModuleDepGraphNode *n, + unsigned submapIndex) { + verifyNodeMapEntry(nodesSeenInNodeMap, swiftDepsString, key, n, + submapIndex); + }); +} + +void ModuleDepGraph::verifyNodeMapEntry( + std::array>, + 2> &nodesSeenInNodeMap, + const std::string &swiftDepsString, const DependencyKey &key, + ModuleDepGraphNode *n, const unsigned submapIndex) const { + verifyNodeIsUniqueWithinSubgraph(nodesSeenInNodeMap, swiftDepsString, key, n, + submapIndex); + verifyNodeIsInRightEntryInNodeMap(swiftDepsString, key, n); + key.verify(); + verifyExternalDependencyUniqueness(key); +} + +void ModuleDepGraph::verifyNodeIsUniqueWithinSubgraph( + std::array>, + 2> &nodesSeenInNodeMap, + const std::string &swiftDepsString, const DependencyKey &key, + ModuleDepGraphNode *const n, const unsigned submapIndex) const { + assert(submapIndex < nodesSeenInNodeMap.size() && + "submapIndex is out of bounds."); + auto iterInserted = nodesSeenInNodeMap[submapIndex][n->getKey()].insert( + std::make_pair(n->getSwiftDepsForMapKey(), n)); + if (!iterInserted.second) { + llvm_unreachable("duplicate driver keys"); + } +} + +void ModuleDepGraph::verifyNodeIsInRightEntryInNodeMap( + const std::string &swiftDepsString, const DependencyKey &key, + const ModuleDepGraphNode *const n) const { + const DependencyKey &nodeKey = n->getKey(); + const Optional swiftDeps = + swiftDepsString.empty() ? None : Optional(swiftDepsString); + (void)nodeKey; + (void)swiftDeps; + assert(n->getSwiftDeps() == swiftDeps || + mapCorruption("Node misplaced for swiftDeps")); + assert(nodeKey == key || mapCorruption("Node misplaced for key")); +} + +void ModuleDepGraph::verifyExternalDependencyUniqueness( + const DependencyKey &key) const { + assert((key.getKind() != NodeKind::externalDepend || + externalDependencies.count(key.getName()) == 1) && + "Ensure each external dependency is tracked exactly once"); +} + +void ModuleDepGraph::verifyCanFindEachJob() const { + FrontendStatsTracer tracer(stats, + "fine-grained-dependencies-verifyCanFindEachJob"); + for (const auto &p : jobsBySwiftDeps) { + getJob(p.first); + } +} + +void ModuleDepGraph::verifyEachJobInGraphIsTracked() const { + FrontendStatsTracer tracer( + stats, "fine-grained-dependencies-verifyEachJobIsTracked"); + nodeMap.forEachKey1( + [&](const std::string &swiftDeps, const typename NodeMap::Key2Map &) { + ensureJobIsTracked(swiftDeps); + }); +} + +/// Dump the path(s) that led to \p node. +/// TODO: break up +void ModuleDepGraph::printPath(raw_ostream &out, + const driver::Job *jobToBeBuilt) const { + assert(currentPathIfTracing.hasValue() && + "Cannot print paths of paths weren't tracked."); + + for (auto paths = dependencyPathsToJobs.find(jobToBeBuilt); + paths != dependencyPathsToJobs.end() && paths->first == jobToBeBuilt; + ++paths) { + const auto &path = paths->second; + bool first = true; + out << "\t"; + for (const ModuleDepGraphNode *n : path) { + if (first) + first = false; + else + out << " -> "; + + const StringRef providerName = getProvidingFilename(n->getSwiftDeps()); + printOneNodeOfPath(out, n->getKey(), providerName); + } + out << "\n"; + } +} + +StringRef ModuleDepGraph::getProvidingFilename( + const Optional swiftDeps) const { + if (!swiftDeps) + return "getFirstSwiftPrimaryInput()); + // FineGrainedDependencyGraphTests work with simulated jobs with empty + // input names. + return !inputName.empty() ? inputName : StringRef(swiftDeps.getValue()); +} + +void ModuleDepGraph::printOneNodeOfPath(raw_ostream &out, + const DependencyKey &key, + const StringRef filename) { + switch (key.getKind()) { + case NodeKind::topLevel: + out << key.aspectName() << " of top-level name '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::nominal: + out << key.aspectName() << " of type '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::potentialMember: + out << key.aspectName() << " of non-private members '" + << key.humanReadableName() << "' in " << filename; + break; + case NodeKind::member: + out << key.aspectName() << " of member '" << key.humanReadableName() + << "' in " << filename; + break; + case NodeKind::dynamicLookup: + out << key.aspectName() << " of AnyObject member '" + << key.humanReadableName() << "' in " << filename; + break; + case NodeKind::externalDepend: + out << filename << " depends on " << key.aspectName() << " of module '" + << key.humanReadableName() << "'"; + break; + case NodeKind::sourceFileProvide: + out << key.aspectName() << " of source file " << key.humanReadableName(); + break; + default: + llvm_unreachable("unknown NodeKind"); + } +} diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index 64372b71cb111..c6362335de7d8 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/STLExtras.h" +#include "swift/Driver/DriverIncrementalRanges.h" #include "swift/Driver/Job.h" #include "swift/Driver/PrettyStackTrace.h" #include "llvm/ADT/STLExtras.h" @@ -24,6 +25,11 @@ using namespace swift; using namespace swift::driver; +CommandOutput::CommandOutput(StringRef dummyBase, OutputFileMap &dummyOFM) + : Inputs({CommandInputPair(dummyBase, "")}), DerivedOutputMap(dummyOFM) { + setAdditionalOutputForType(file_types::TY_SwiftDeps, dummyBase); +} + StringRef CommandOutput::getOutputForInputAndType(StringRef PrimaryInputFile, file_types::ID Type) const { if (Type == file_types::TY_Nothing) @@ -235,6 +241,10 @@ CommandOutput::getAdditionalOutputsForType(file_types::ID Type) const { return V; } +bool CommandOutput::hasAdditionalOutputForType(file_types::ID type) const { + return AdditionalOutputTypes.count(type); +} + StringRef CommandOutput::getAnyOutputForType(file_types::ID Type) const { if (PrimaryOutputType == Type) return getPrimaryOutputFilename(); @@ -418,6 +428,7 @@ void Job::printSummary(raw_ostream &os) const { os << "}"; } + bool Job::writeArgsToResponseFile() const { assert(hasResponseFile()); std::error_code EC; @@ -433,6 +444,16 @@ bool Job::writeArgsToResponseFile() const { return false; } +StringRef Job::getFirstSwiftPrimaryInput() const { + const JobAction &source = getSource(); + if (!isa(source)) + return StringRef(); + const auto *firstInput = source.getInputs().front(); + if (auto *inputInput = dyn_cast(firstInput)) + return inputInput->getInputArg().getValue(); + return StringRef(); +} + BatchJob::BatchJob(const JobAction &Source, SmallVectorImpl &&Inputs, std::unique_ptr Output, diff --git a/lib/Driver/ParseableOutput.cpp b/lib/Driver/ParseableOutput.cpp index 7c6b4fb9ca627..0b110daad12ec 100644 --- a/lib/Driver/ParseableOutput.cpp +++ b/lib/Driver/ParseableOutput.cpp @@ -125,7 +125,7 @@ class DetailedCommandBasedMessage : public CommandBasedMessage { if (const auto *BJAction = dyn_cast(&Cmd.getSource())) { Inputs.push_back(CommandInput(OutFiles[BJAction->getInputIndex()])); } else { - for (const std::string &FileName : OutFiles) { + for (const std::string FileName : OutFiles) { Inputs.push_back(CommandInput(FileName)); } } @@ -134,7 +134,7 @@ class DetailedCommandBasedMessage : public CommandBasedMessage { // TODO: set up Outputs appropriately. file_types::ID PrimaryOutputType = Cmd.getOutput().getPrimaryOutputType(); if (PrimaryOutputType != file_types::TY_Nothing) { - for (const std::string &OutputFileName : Cmd.getOutput(). + for (const std::string OutputFileName : Cmd.getOutput(). getPrimaryOutputFilenames()) { Outputs.push_back(OutputPair(PrimaryOutputType, OutputFileName)); } diff --git a/lib/Driver/SourceComparator.cpp b/lib/Driver/SourceComparator.cpp new file mode 100644 index 0000000000000..2fccb91e779b6 --- /dev/null +++ b/lib/Driver/SourceComparator.cpp @@ -0,0 +1,470 @@ +//===------------------------- SourceComparator.cpp ------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Driver/SourceComparator.h" + +#include +#include + +using namespace swift; +using namespace incremental_ranges; + +//============================================================================== +// MARK: SourceComparator +//============================================================================== + +//============================================================================== +// MARK: initialization +//============================================================================== + +SourceComparator::SourceComparator(StringRef s1, StringRef s2) + : linesToCompare(splitIntoLines(s1), splitIntoLines(s2)), + regionsToCompare(LineSpan({0, 0}, linesToCompare.size())), + matches(linesToCompare.lhs().size(), None) {} + +std::vector SourceComparator::splitIntoLines(const StringRef s) { + std::vector result; + for (auto toSplit = s; !toSplit.empty();) { + StringRef line; + std::tie(line, toSplit) = toSplit.split('\n'); + result.push_back(line); + } + return result; +} + +//============================================================================== +// MARK: Comparing +//============================================================================== + +void SourceComparator::compare() { + // Trim ends of common elements + trimStart(), trimEnd(); + // Do the comparison + scanMatchedLines(buildDAGOfSubsequences(buildEquivalenceClasses())); +} + +void SourceComparator::trimStart() { + for (; regionsToCompare.bothNonEmpty() && + linesToCompare.matchAt(regionsToCompare.start); + ++regionsToCompare.start) + matches[regionsToCompare.start.lhs()] = regionsToCompare.start.rhs(); +} +void SourceComparator::trimEnd() { + for (; regionsToCompare.bothNonEmpty() && + linesToCompare.matchAt(--regionsToCompare.end);) + matches[regionsToCompare.end.lhs()] = regionsToCompare.end.rhs(); +} + +std::unordered_map> +SourceComparator::buildEquivalenceClasses() { + // Map each element of rhs to the sequence of indices it occupies. + std::unordered_map> rhsMap; + for (auto index = regionsToCompare.start.rhs(); + index < regionsToCompare.end.rhs(); ++index) + rhsMap[linesToCompare.rhs()[index]].push_back(index); + return rhsMap; +} + +std::pair, + SourceComparator::SortedSequence> +SourceComparator::buildDAGOfSubsequences( + std::unordered_map> rhsMap) { + SortedSequence thresh; + const size_t linksSize = regionsToCompare.size().min(); + std::vector links; + links.resize(linksSize); + + for (auto i = regionsToCompare.start.lhs(); i < regionsToCompare.end.lhs(); + ++i) { + // What lines in rhs does the ith line in lhs match? + auto iter = rhsMap.find(linesToCompare.lhs()[i]); + if (iter == rhsMap.end()) + continue; // no match in rhs + auto &res = iter->second; + size_t lastJ = ~0; + // For each match in rhs in reverse order + for (auto j : llvm::reverse(res)) { + assert(j < lastJ); + lastJ = j; + // replace the index of the match with the index of the earlier match + // or add it if last match + // (thresh gets populated for each lhs match with rhs indices) + if (auto optK = thresh.replaceNextLargerWith(j)) { + // There was a later match at thresh[k] + auto k = optK.getValue(); + // prev match (of what?) was at links[k-1]? + // chain to that match + Optional newNext = k == 0 ? None : Optional(k - 1); + links.emplace(links.begin() + k, LineIndices(i, j), newNext); + } + } + } + return {std::move(links), thresh}; +} + +void SourceComparator::scanMatchedLines( + std::pair, SortedSequence> &&linksAndThres) { + const auto &links = linksAndThres.first; + const auto &thresh = linksAndThres.second; + + if (thresh.empty()) + return; + // For every match put rhs index in matches[col2 index] + for (const auto *p = &links[thresh.size() - 1]; p; + p = p->next ? &links[p->next.getValue()] : nullptr) + matches[p->lines.lhs()] = p->lines.rhs(); +} + +//============================================================================== +// MARK: summarizing +//============================================================================== + +std::vector +SourceComparator::getMatchingRegions() const { + std::vector matches; + forEachMatch([&](const LineSpan r) { matches.push_back(r); }); + return matches; +} + +std::vector +SourceComparator::getMismatchingRegions() const { + std::vector mismatches; + forEachMismatch([&](const LineSpan r) { mismatches.push_back(r); }); + return mismatches; +} + +void SourceComparator::forEachMatch( + function_ref consumeMatch) const { + for (LineIndices nextMatch = getFirstMatch(), + nextMismatch = getNextMismatchAfter(nextMatch); + areEitherInRange(nextMatch); nextMatch = getNextMatchAfter(nextMismatch), + nextMismatch = getNextMismatchAfter(nextMatch)) { + consumeMatch(LineSpan{nextMatch, nextMismatch}); + } +} + +void SourceComparator::forEachMismatch( + function_ref consumeMismatch) const { + for (LineIndices nextMismatch = getFirstMismatch(), + nextMatch = getNextMatchAfter(nextMismatch); + areEitherInRange(nextMismatch); + nextMismatch = getNextMismatchAfter(nextMatch), + nextMatch = getNextMatchAfter(nextMismatch)) { + consumeMismatch(LineSpan{nextMismatch, nextMatch}); + } +} + +bool SourceComparator::areEitherInRange(const LineIndices lines) const { + return lines.lhs() < linesToCompare.lhs().size() || + lines.rhs() < linesToCompare.rhs().size(); +} + +SourceComparator::LineIndices SourceComparator::getFirstMatch() const { + for (size_t i = 0; i < linesToCompare.lhs().size(); ++i) { + if (auto m = matches[i]) + return {i, m.getValue()}; + } + return linesToCompare.size(); +} + +SourceComparator::LineIndices SourceComparator::getFirstMismatch() const { + size_t i = 0; + for (; i < linesToCompare.lhs().size() && matches[i].getValueOr(~0) == i; + ++i) { + } + return {i, i}; +} + +SourceComparator::LineIndices +SourceComparator::getNextMismatchAfter(const LineIndices nextMatch) const { + LineIndices possibleNextMatch = nextMatch + 1; + for (; possibleNextMatch.lhs() < matches.size() && + possibleNextMatch.rhs() == + matches[possibleNextMatch.lhs()].getValueOr(~0); + ++possibleNextMatch) { + } + return possibleNextMatch; +} + +SourceComparator::LineIndices +SourceComparator::getNextMatchAfter(const LineIndices nextMismatch) const { + // matches[nextMismatch.lhs] could be present if the gap was in 2 + // so start right at nextMismatch + for (auto maybeMatch1 = nextMismatch.lhs(); maybeMatch1 < matches.size(); + ++maybeMatch1) { + if (auto rhsMatch = matches[maybeMatch1]) + return {maybeMatch1, rhsMatch.getValue()}; + } + return linesToCompare.size(); +} + +SourceComparator::LRSerializableRange +SourceComparator::convertAMismatch(const LineSpan mismatchLines) const { + CharIndices firstMismatchInLine = computeFirstMismatchInLine(mismatchLines); + CharIndices endMismatchInLine = + computeEndMismatchInLine(mismatchLines, firstMismatchInLine); + + const LineIndices endLinesForCoords = { + computeEndLinesToGoWithChars(mismatchLines, endMismatchInLine, Side::L), + computeEndLinesToGoWithChars(mismatchLines, endMismatchInLine, Side::R), + }; + + // 1-origin for diff format + // Since we convert from a semi-closed line range to a semi-closed + // line, char range, we don't add 1 to the end lines. + const auto lhsMismatchStart = SerializableSourceLocation{ + mismatchLines.start.lhs() + 1, firstMismatchInLine.lhs() + 1}; + const auto rhsMismatchStart = SerializableSourceLocation{ + mismatchLines.start.rhs() + 1, firstMismatchInLine.rhs() + 1}; + const auto lhsMismatchEnd = SerializableSourceLocation{ + endLinesForCoords.lhs() + 1, endMismatchInLine.lhs() + 1}; + const auto rhsMismatchEnd = SerializableSourceLocation{ + endLinesForCoords.rhs() + 1, endMismatchInLine.rhs() + 1}; + + if (lhsMismatchEnd < lhsMismatchStart) + llvm::errs() << "LEFT: "; + if (rhsMismatchEnd < rhsMismatchStart) + llvm::errs() << "RIGHT: "; + + const auto lhsMismatchRange = + SerializableSourceRange(lhsMismatchStart, lhsMismatchEnd); + const auto rhsMismatchRange = + SerializableSourceRange(rhsMismatchStart, rhsMismatchEnd); + + return {lhsMismatchRange, rhsMismatchRange}; +} + +SourceComparator::CharIndices SourceComparator::computeFirstMismatchInLine( + const LineSpan mismatchLines) const { + return mismatchLines.bothNonEmpty() + ? computeFirstMismatchInLinesOnBothSides(mismatchLines) + : computeFirstMismatchInLineOnOneSide( + mismatchLines, + mismatchLines.start.lhs() < mismatchLines.end.lhs() + ? Side::L + : Side::R); +} + +SourceComparator::CharIndices +SourceComparator::computeFirstMismatchInLinesOnBothSides( + const LineSpan mismatchLines) const { + CharIndices firstMismatchInLine = {0, 0}; // next char after last char + if (mismatchLines.start < linesToCompare.size()) { + const auto firstLines = linesToCompare[mismatchLines.start]; + const auto ends = firstLines.size(); + for (; + firstMismatchInLine < ends && firstLines.matchAt(firstMismatchInLine); + ++firstMismatchInLine) { + } + } + return firstMismatchInLine; +} + +SourceComparator::CharIndices +SourceComparator::computeFirstMismatchInLineOnOneSide( + const LineSpan mismatchLines, const Side nonemptySide) const { + return {0, 0}; +} + +SourceComparator::CharIndices SourceComparator::computeEndMismatchInLine( + const LineSpan mismatchLines, const CharIndices firstMismatchInLine) const { + return mismatchLines.bothNonEmpty() + ? computeEndMismatchInLineOnBothSides(mismatchLines, + firstMismatchInLine) + : computeEndMismatchInLineOnOneSide( + mismatchLines, firstMismatchInLine, + mismatchLines.start.lhs() < mismatchLines.end.lhs() + ? Side::L + : Side::R); +} + +SourceComparator::CharIndices +SourceComparator::computeEndMismatchInLineOnBothSides( + LineSpan mismatchLines, const CharIndices firstMismatchInLine) const { + CharIndices endMismatchInLine(0, 0); + const auto lastLines = linesToCompare[mismatchLines.end - 1]; + for (endMismatchInLine = lastLines.size(); + firstMismatchInLine < endMismatchInLine && + lastLines.matchAt(endMismatchInLine - 1); + --endMismatchInLine) { + } + return endMismatchInLine; +} + +SourceComparator::CharIndices +SourceComparator::computeEndMismatchInLineOnOneSide( + LineSpan mismatchLines, const CharIndices firstMismatchInLine, + const Side side) const { + const size_t lineSize = + linesToCompare.side(side)[mismatchLines.end[side] - 1].size(); + return side == Side::L ? CharIndices(lineSize, 0) : CharIndices(0, lineSize); +} + +size_t SourceComparator::computeEndLinesToGoWithChars( + const LineSpan mismatchLines, const CharIndices endMismatchInLine, + const Side side) const { + const bool noMismatch = mismatchLines.start[side] == mismatchLines.end[side]; + if (noMismatch) + return mismatchLines.start[side]; + const bool mismatchEndsWithinALine = endMismatchInLine[side] != 0; + // If mismatch ends within a line, then the line coordinate should be the + // line *before* the line after the mismatch. + return mismatchEndsWithinALine ? mismatchLines.end[side] - 1 + : mismatchLines.end[side]; +} + +SourceComparator::LRRanges SourceComparator::convertAllMismatches() const { + LRRanges mismatches; + + forEachMismatch([&](const SourceComparator::LineSpan mismatch) { + mismatches.push_back(convertAMismatch(mismatch)); + }); + return mismatches; +} + +//============================================================================== +// MARK: printing +//============================================================================== + +void SourceComparator::print(raw_ostream &out) const { + forEachMismatch( + [&](const LineSpan &mismatch) { reportMismatch(mismatch, out); }); +} + +void SourceComparator::reportMismatch(const LineSpan mismatch, + raw_ostream &out) const { + + auto printRange = [&](size_t rangeStart, size_t rangeEnd) { + // 1-origin for llvm compat + ++rangeStart, ++rangeEnd; + const auto rangeLast = rangeEnd - 1; + out << std::min(rangeStart, rangeLast); + if (rangeStart < rangeLast) + out << ", " << rangeLast; + }; + + if (mismatch.bothEmpty()) + return; + + const auto lhsStart = mismatch.start.lhs(); + const auto lhsEnd = mismatch.end.lhs(); + const auto rhsStart = mismatch.start.rhs(); + const auto rhsEnd = mismatch.end.rhs(); + + printRange(lhsStart, lhsEnd); + { + const auto added = "a", deleted = "d", changed = "c"; + out << (lhsStart >= lhsEnd ? added + : rhsStart >= rhsEnd ? deleted : changed); + } + printRange(rhsStart, rhsEnd); + out << "\n"; + + for (auto i = lhsStart; i < lhsEnd; ++i) + out << "< " << linesToCompare.lhs()[i] << "\n"; + if (lhsStart < lhsEnd && rhsStart < rhsEnd) + out << "---\n"; + for (auto i = rhsStart; i < rhsEnd; ++i) + out << "> " << linesToCompare.rhs()[i] << "\n"; +} + +//============================================================================== +// MARK: SortedSequence +//============================================================================== + +Optional +SourceComparator::SortedSequence::replaceNextLargerWith(const Elem x) { + auto loc = locationOfFirstGreaterOrEqualElement(x); + if (loc >= contents.size()) { + contents.push_back(x); + return loc; + } + if (contents[loc] == x) + return None; + contents[loc] = x; + return loc; +} + +size_t SourceComparator::SortedSequence::locationOfFirstGreaterOrEqualElement( + const Elem x) const { + return std::lower_bound(contents.begin(), contents.end(), x) - + contents.begin(); +} + +//============================================================================== +// MARK: testing +//============================================================================== + +bool SourceComparator::test() { + auto doCompare = [&](const char *lhs, + const char *rhs) -> LRSerializableRange { + SourceComparator sc(lhs, rhs); + sc.compare(); + sc.dump(); + auto mismatches = sc.convertAllMismatches(); + assert(mismatches.lhs().size() == 1); + assert(mismatches.rhs().size() == 1); + return LRSerializableRange(mismatches.lhs()[0], mismatches.rhs()[0]); + }; + { + auto a = "}\nfunc nine() {}\nfunc ten() {}"; + auto b = "}\nfunc ten() {}"; + auto mismatches = doCompare(a, b); + assert(mismatches.lhs() == SerializableSourceRange({2, 1}, {2, 15})); + assert(mismatches.rhs() == SerializableSourceRange({2, 1}, {2, 1})); + } + { + auto a = "abcde"; + auto b = "abde"; + auto mismatches = doCompare(a, b); + assert(mismatches.lhs() == SerializableSourceRange({1, 3}, {1, 4})); + assert(mismatches.rhs() == SerializableSourceRange({1, 3}, {1, 3})); + } + { + auto a = "line0\n"; + auto b = "line0\nline1\n"; + auto mismatches = doCompare(a, b); + assert(mismatches.lhs() == SerializableSourceRange({2, 1}, {2, 1})); + assert(mismatches.rhs() == SerializableSourceRange({2, 1}, {2, 6})); + } + { + auto a = "var snort = 333\n"; + auto b = "var fred = 123456\n"; + auto mismatches = doCompare(a, b); + assert(mismatches.lhs() == SerializableSourceRange({1, 5}, {1, 16})); + assert(mismatches.rhs() == SerializableSourceRange({1, 5}, {1, 18})); + } + { + auto a = "line1\nline2a\nline3\nline4a\nline5\nline6\n"; + auto b = "line1\nline2b\nline3\nline4b\nline6\n"; + + SourceComparator sc(a, b); + sc.compare(); + + std::vector> matchesVec{0, None, 2, None, None, 4}; + assert(sc.matches == matchesVec); + + auto matches = sc.getMatchingRegions(); + assert(matches.size() == 3); + assert(matches[0] == LineSpan({0, 0}, {1, 1})); + assert(matches[1] == LineSpan({2, 2}, {3, 3})); + assert(matches[2] == LineSpan({5, 4}, {6, 5})); + + auto mismatches = sc.getMismatchingRegions(); + assert(mismatches.size() == 2); + assert(mismatches[0] == LineSpan({1, 1}, {2, 2})); + assert(mismatches[1] == LineSpan({3, 3}, {5, 4})); + sc.dump(); + sc.convertAllMismatches(); + } + return true; +} diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index bf42c9dd02c7e..87398eecadb9b 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -178,26 +178,6 @@ file_types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const { return file_types::lookupTypeForExtension(Ext); } -/// Return a _single_ TY_Swift InputAction, if one exists; -/// if 0 or >1 such inputs exist, return nullptr. -static const InputAction *findSingleSwiftInput(const CompileJobAction *CJA) { - auto Inputs = CJA->getInputs(); - const InputAction *IA = nullptr; - for (auto const *I : Inputs) { - if (auto const *S = dyn_cast(I)) { - if (S->getType() == file_types::TY_Swift) { - if (IA == nullptr) { - IA = S; - } else { - // Already found one, two is too many. - return nullptr; - } - } - } - } - return IA; -} - static bool jobsHaveSameExecutableNames(const Job *A, const Job *B) { // Jobs that get here (that are derived from CompileJobActions) should always // have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME @@ -238,7 +218,12 @@ bool ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const { auto const *CJActA = dyn_cast(&A->getSource()); if (!CJActA) return false; - return findSingleSwiftInput(CJActA) != nullptr; + // When having only one job output a dependency file, that job is not + // batchable since it has an oddball set of additional output types. + if (C.OnlyOneDependencyFile && + A->getOutput().hasAdditionalOutputForType(file_types::TY_Dependencies)) + return false; + return CJActA->findSingleSwiftInput() != nullptr; } bool ToolChain::jobsAreBatchCombinable(const Compilation &C, const Job *A, @@ -300,33 +285,6 @@ mergeBatchInputs(ArrayRef jobs, return false; } -/// Unfortunately the success or failure of a Swift compilation is currently -/// sensitive to the order in which files are processed, at least in terms of -/// the order of processing extensions (and likely other ways we haven't -/// discovered yet). So long as this is true, we need to make sure any batch job -/// we build names its inputs in an order that's a subsequence of the sequence -/// of inputs the driver was initially invoked with. -static void -sortJobsToMatchCompilationInputs(ArrayRef unsortedJobs, - SmallVectorImpl &sortedJobs, - Compilation &C) { - llvm::DenseMap jobsByInput; - for (const Job *J : unsortedJobs) { - const CompileJobAction *CJA = cast(&J->getSource()); - const InputAction *IA = findSingleSwiftInput(CJA); - auto R = - jobsByInput.insert(std::make_pair(IA->getInputArg().getValue(), J)); - assert(R.second); - (void)R; - } - for (const InputPair &P : C.getInputFiles()) { - auto I = jobsByInput.find(P.second->getValue()); - if (I != jobsByInput.end()) { - sortedJobs.push_back(I->second); - } - } -} - /// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput, /// input \c Job and \c Action members. Call through to \c constructInvocation /// on \p BatchJob, to build the \c InvocationInfo. @@ -338,7 +296,7 @@ ToolChain::constructBatchJob(ArrayRef unsortedJobs, return nullptr; llvm::SmallVector sortedJobs; - sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs, C); + C.sortJobsToMatchCompilationInputs(unsortedJobs, sortedJobs); // Synthetic OutputInfo is a slightly-modified version of the initial // compilation's OI. diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 1dd32b67bd156..7ce44bb05a9c7 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -151,6 +151,12 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, break; } + if (const Arg *variant = inputArgs.getLastArg(options::OPT_target_variant)) { + arguments.push_back("-target-variant"); + std::string normalized = llvm::Triple::normalize(variant->getValue()); + arguments.push_back(inputArgs.MakeArgString(normalized)); + } + // Enable address top-byte ignored in the ARM64 backend. if (Triple.getArch() == llvm::Triple::aarch64) { arguments.push_back("-Xllvm"); @@ -192,6 +198,8 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit); inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension); inputArgs.AddLastArg(arguments, options::OPT_enable_library_evolution); + inputArgs.AddLastArg(arguments, options::OPT_require_explicit_availability); + inputArgs.AddLastArg(arguments, options::OPT_require_explicit_availability_target); inputArgs.AddLastArg(arguments, options::OPT_enable_testing); inputArgs.AddLastArg(arguments, options::OPT_enable_private_imports); inputArgs.AddLastArg(arguments, options::OPT_enable_cxx_interop); @@ -214,6 +222,7 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors); inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ); + inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ); inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ); inputArgs.AddLastArg(arguments, options::OPT_static); inputArgs.AddLastArg(arguments, options::OPT_swift_version); @@ -228,15 +237,23 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts); inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded); inputArgs.AddLastArg(arguments, - options::OPT_enable_experimental_dependencies); + options::OPT_enable_fine_grained_dependencies); + inputArgs.AddLastArg(arguments, + options::OPT_disable_fine_grained_dependencies); + inputArgs.AddLastArg(arguments, options::OPT_enable_type_fingerprints); + inputArgs.AddLastArg(arguments, options::OPT_disable_type_fingerprints); inputArgs.AddLastArg(arguments, - options::OPT_experimental_dependency_include_intrafile); + options::OPT_fine_grained_dependency_include_intrafile); + inputArgs.AddLastArg(arguments, + options::OPT_emit_fine_grained_dependency_sourcefile_dot_files); inputArgs.AddLastArg(arguments, options::OPT_package_description_version); inputArgs.AddLastArg(arguments, options::OPT_serialize_diagnostics_path); inputArgs.AddLastArg(arguments, options::OPT_debug_diagnostic_names); inputArgs.AddLastArg(arguments, options::OPT_enable_astscope_lookup); inputArgs.AddLastArg(arguments, options::OPT_disable_astscope_lookup); inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup); + inputArgs.AddLastArg(arguments, + options::OPT_enable_experimental_concise_pound_file); // Pass on any build config options inputArgs.AddAllArgs(arguments, options::OPT_D); @@ -399,6 +416,10 @@ ToolChain::constructInvocation(const CompileJobAction &job, Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); + if (context.Args.hasArg(options::OPT_CrossModuleOptimization)) { + Arguments.push_back("-cross-module-optimization"); + } + addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_OptRecord, "-save-optimization-record-path"); @@ -461,6 +482,9 @@ ToolChain::constructInvocation(const CompileJobAction &job, Arguments.push_back("-runtime-compatibility-version"); Arguments.push_back(arg->getValue()); } + if (context.Args.hasArg(options::OPT_track_system_dependencies)) { + Arguments.push_back("-track-system-dependencies"); + } context.Args.AddLastArg( Arguments, @@ -499,6 +523,8 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { return "-emit-ir"; case file_types::TY_LLVM_BC: return "-emit-bc"; + case file_types::TY_ClangModuleFile: + return "-emit-pcm"; case file_types::TY_Assembly: return "-S"; case file_types::TY_SwiftModuleFile: @@ -522,11 +548,12 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_AutolinkFile: case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: - case file_types::TY_ClangModuleFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_ModuleTrace: case file_types::TY_TBD: case file_types::TY_OptRecord: @@ -577,7 +604,7 @@ void ToolChain::JobContext::addFrontendInputAndOutputArguments( Arguments.push_back("-primary-filelist"); Arguments.push_back(getTemporaryFilePath("primaryInputs", "")); FilelistInfos.push_back({Arguments.back(), file_types::TY_Swift, - FilelistInfo::WhichFiles::PrimaryInputs}); + FilelistInfo::WhichFiles::SourceInputActions}); } if (!UseFileList || !UsePrimaryFileList) { addFrontendCommandLineInputArguments(MayHavePrimaryInputs, UseFileList, @@ -662,6 +689,10 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( "-emit-dependencies-path"); addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftDeps, "-emit-reference-dependencies-path"); + addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftRanges, + "-emit-swift-ranges-path"); + addOutputsOfType(arguments, Output, Args, file_types::TY_CompiledSource, + "-emit-compiled-source-path"); addOutputsOfType(arguments, Output, Args, file_types::TY_ModuleTrace, "-emit-loaded-module-trace-path"); addOutputsOfType(arguments, Output, Args, file_types::TY_TBD, @@ -758,6 +789,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_SIL: case file_types::TY_SIB: case file_types::TY_PCH: + case file_types::TY_ClangModuleFile: case file_types::TY_IndexData: llvm_unreachable("Cannot be output from backend job"); case file_types::TY_Swift: @@ -765,11 +797,12 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_AutolinkFile: case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: - case file_types::TY_ClangModuleFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: + case file_types::TY_SwiftRanges: + case file_types::TY_CompiledSource: case file_types::TY_Remapping: case file_types::TY_ModuleTrace: case file_types::TY_OptRecord: @@ -874,7 +907,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, Arguments.push_back(context.getTemporaryFilePath("inputs", "")); II.FilelistInfos.push_back({Arguments.back(), file_types::TY_SwiftModuleFile, - FilelistInfo::WhichFiles::Input}); + FilelistInfo::WhichFiles::InputJobs}); addInputsOfType(Arguments, context.InputActions, file_types::TY_SwiftModuleFile); @@ -1194,8 +1227,28 @@ void ToolChain::getResourceDirPath(SmallVectorImpl &resourceDirPath, llvm::sys::path::append(resourceDirPath, "lib", shared ? "swift" : "swift_static"); } - llvm::sys::path::append(resourceDirPath, - getPlatformNameForTriple(getTriple())); + + StringRef libSubDir = getPlatformNameForTriple(getTriple()); + if (tripleIsMacCatalystEnvironment(getTriple())) + libSubDir = "maccatalyst"; + llvm::sys::path::append(resourceDirPath, libSubDir); +} + +// Get the secondary runtime library link path given the primary path. +// The compiler will look for runtime libraries in the secondary path if they +// can't be found in the primary path. +void ToolChain::getSecondaryResourceDirPath( + SmallVectorImpl &secondaryResourceDirPath, + StringRef primaryPath) const { + if (!tripleIsMacCatalystEnvironment(getTriple())) + return; + + // For macCatalyst, the secondary runtime library path is the macOS library + // path. The compiler will find zippered libraries here. + secondaryResourceDirPath.append(primaryPath.begin(), primaryPath.end()); + // Remove '/maccatalyst' and replace with 'macosx'. + llvm::sys::path::remove_filename(secondaryResourceDirPath); + llvm::sys::path::append(secondaryResourceDirPath, "macosx"); } void ToolChain::getRuntimeLibraryPaths(SmallVectorImpl &runtimeLibPaths, @@ -1205,7 +1258,22 @@ void ToolChain::getRuntimeLibraryPaths(SmallVectorImpl &runtimeLibP getResourceDirPath(scratchPath, args, shared); runtimeLibPaths.push_back(scratchPath.str()); + // If there's a secondary resource dir, add it too. + scratchPath.clear(); + getSecondaryResourceDirPath(scratchPath, runtimeLibPaths[0]); + if (!scratchPath.empty()) + runtimeLibPaths.push_back(scratchPath.str()); + if (!SDKPath.empty()) { + if (!scratchPath.empty()) { + // If we added the secondary resource dir, we also need the iOSSupport + // directory. + scratchPath = SDKPath; + llvm::sys::path::append(scratchPath, "System", "iOSSupport"); + llvm::sys::path::append(scratchPath, "usr", "lib", "swift"); + runtimeLibPaths.push_back(scratchPath.str()); + } + scratchPath = SDKPath; llvm::sys::path::append(scratchPath, "usr", "lib", "swift"); runtimeLibPaths.push_back(scratchPath.str()); diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 936599c4cf36d..addeeba50f52c 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -55,17 +55,27 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain { const JobContext &context) const override; void validateArguments(DiagnosticEngine &diags, - const llvm::opt::ArgList &args) const override; + const llvm::opt::ArgList &args, + StringRef defaultTarget) const override; std::string findProgramRelativeToSwiftImpl(StringRef name) const override; bool shouldStoreInvocationInDebugInfo() const override; + const Optional TargetVariant; + public: - Darwin(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {} + Darwin(const Driver &D, const llvm::Triple &Triple, + const Optional &TargetVariant) : + ToolChain(D, Triple), TargetVariant(TargetVariant) {} + ~Darwin() = default; std::string sanitizerRuntimeLibName(StringRef Sanitizer, bool shared = true) const override; + + Optional getTargetVariant() const { + return TargetVariant; + } }; class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain { diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 307c552048627..07a19d9e548ac 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -42,7 +42,8 @@ std::string toolchains::GenericUnix::sanitizerRuntimeLibName(StringRef Sanitizer, bool shared) const { return (Twine("libclang_rt.") + Sanitizer + "-" + - this->getTriple().getArchName() + ".a") + this->getTriple().getArchName() + + (this->getTriple().isAndroid() ? "-android" : "") + ".a") .str(); } diff --git a/lib/Driver/WindowsToolChains.cpp b/lib/Driver/WindowsToolChains.cpp index 7554a1f18dbad..0fe623675eb1a 100644 --- a/lib/Driver/WindowsToolChains.cpp +++ b/lib/Driver/WindowsToolChains.cpp @@ -52,6 +52,12 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, ArgStringList Arguments; + std::string Target = getTriple().str(); + if (!Target.empty()) { + Arguments.push_back("-target"); + Arguments.push_back(context.Args.MakeArgString(Target)); + } + switch (job.getKind()) { case LinkKind::None: llvm_unreachable("invalid link kind"); @@ -104,12 +110,6 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, Clang = context.Args.MakeArgString(tool.get()); } - std::string Target = getTriple().str(); - if (!Target.empty()) { - Arguments.push_back("-target"); - Arguments.push_back(context.Args.MakeArgString(Target)); - } - // Rely on `-libc` to correctly identify the MSVC Runtime Library. We use // `-nostartfiles` as that limits the difference to just the // `-defaultlib:libcmt` which is passed unconditionally with the `clang++` diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 6bbd2d8f9d4a7..0958d6df7b64d 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -90,15 +90,6 @@ bool ArgsToFrontendOptionsConverter::convert( computeDebugTimeOptions(); computeTBDOptions(); - setUnsignedIntegerArgument(OPT_warn_long_function_bodies, 10, - Opts.WarnLongFunctionBodies); - setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, 10, - Opts.WarnLongExpressionTypeChecking); - setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, 10, - Opts.SolverExpressionTimeThreshold); - setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, 10, - Opts.SwitchCheckingInvocationThreshold); - Opts.CheckOnoneSupportCompleteness = Args.hasArg(OPT_check_onone_completeness); Opts.DebuggerTestingTransform = Args.hasArg(OPT_debugger_testing_transform); @@ -110,8 +101,13 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.ParseStdlib |= Args.hasArg(OPT_parse_stdlib); + Opts.IgnoreSwiftSourceInfo |= Args.hasArg(OPT_ignore_module_source_info); computeHelpOptions(); + if (Args.hasArg(OPT_print_target_info)) { + Opts.PrintTargetInfo = true; + } + if (const Arg *A = Args.getLastArg(OPT_verify_generic_signatures)) { Opts.VerifyGenericSignaturesInModule = A->getValue(); } @@ -162,7 +158,7 @@ bool ArgsToFrontendOptionsConverter::convert( return true; if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction) - && Opts.SkipNonInlinableFunctionBodies) { + && Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies)) { Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies); return true; } @@ -221,12 +217,8 @@ void ArgsToFrontendOptionsConverter::computePrintStatsOptions() { void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() { using namespace options; - Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies); - Opts.DebugTimeExpressionTypeChecking |= - Args.hasArg(OPT_debug_time_expression_type_checking); Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation); - Opts.SkipNonInlinableFunctionBodies |= - Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies); + if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) { Opts.StatsOutputDir = A->getValue(); if (Args.getLastArg(OPT_trace_stats_events)) { @@ -259,19 +251,6 @@ void ArgsToFrontendOptionsConverter::computeTBDOptions() { } } -void ArgsToFrontendOptionsConverter::setUnsignedIntegerArgument( - options::ID optionID, unsigned radix, unsigned &valueToSet) { - if (const Arg *A = Args.getLastArg(optionID)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(radix, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - } else { - valueToSet = attempt; - } - } -} - void ArgsToFrontendOptionsConverter::computePlaygroundOptions() { using namespace options; Opts.PlaygroundTransform |= Args.hasArg(OPT_playground); @@ -386,6 +365,10 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) { return FrontendOptions::ActionType::DumpTypeInfo; if (Opt.matches(OPT_print_ast)) return FrontendOptions::ActionType::PrintAST; + if (Opt.matches(OPT_emit_pcm)) + return FrontendOptions::ActionType::EmitPCM; + if (Opt.matches(OPT_dump_pcm)) + return FrontendOptions::ActionType::DumpPCM; if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl)) return FrontendOptions::ActionType::REPL; @@ -522,6 +505,16 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() diag::error_mode_cannot_emit_reference_dependencies); return true; } + if (!FrontendOptions::canActionEmitSwiftRanges(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasSwiftRangesPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_swift_ranges); + return true; + } + if (!FrontendOptions::canActionEmitCompiledSource(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasCompiledSourcePath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_compiled_source); + return true; + } if (!FrontendOptions::canActionEmitObjCHeader(Opts.RequestedAction) && Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.h b/lib/Frontend/ArgsToFrontendOptionsConverter.h index ebf09b6d3153e..8431ac4c0090b 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.h +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.h @@ -47,9 +47,6 @@ class ArgsToFrontendOptionsConverter { void computePrintStatsOptions(); void computeTBDOptions(); - void setUnsignedIntegerArgument(options::ID optionID, unsigned radix, - unsigned &valueToSet); - bool setUpInputKindAndImmediateArgs(); bool checkUnusedSupplementaryOutputPaths() const; diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index cec2a86ec45d1..fac378995366f 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -287,6 +287,10 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_dependencies_path); auto referenceDependenciesFile = getSupplementaryFilenamesFromArguments( options::OPT_emit_reference_dependencies_path); + auto swiftRangesFile = getSupplementaryFilenamesFromArguments( + options::OPT_emit_swift_ranges_path); + auto compiledSourceFile = getSupplementaryFilenamesFromArguments( + options::OPT_emit_compiled_source_path); auto serializedDiagnostics = getSupplementaryFilenamesFromArguments( options::OPT_serialize_diagnostics_path); auto fixItsOutput = getSupplementaryFilenamesFromArguments( @@ -298,11 +302,12 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_interface_path); auto moduleSourceInfoOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_source_info_path); - + auto ldAddCFileOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_ldadd_cfile_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || - !moduleInterfaceOutput || !moduleSourceInfoOutput) { + !moduleInterfaceOutput || !moduleSourceInfoOutput || !ldAddCFileOutput) { return None; } std::vector result; @@ -316,12 +321,15 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.ModuleDocOutputPath = (*moduleDocOutput)[i]; sop.DependenciesFilePath = (*dependenciesFile)[i]; sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[i]; + sop.SwiftRangesFilePath = (*swiftRangesFile)[i]; + sop.CompiledSourceFilePath = (*compiledSourceFile)[i]; sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[i]; sop.FixItsOutputPath = (*fixItsOutput)[i]; sop.LoadedModuleTracePath = (*loadedModuleTrace)[i]; sop.TBDPath = (*TBD)[i]; sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i]; sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; + sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; result.push_back(sop); } return result; @@ -369,6 +377,16 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( file_types::TY_SwiftDeps, "", defaultSupplementaryOutputPathExcludingExtension); + auto swiftRangesFilePath = determineSupplementaryOutputFilename( + OPT_emit_swift_ranges, pathsFromArguments.SwiftRangesFilePath, + file_types::TY_SwiftRanges, "", + defaultSupplementaryOutputPathExcludingExtension); + + auto compiledSourceFilePath = determineSupplementaryOutputFilename( + OPT_emit_compiled_source, pathsFromArguments.CompiledSourceFilePath, + file_types::TY_CompiledSource, "", + defaultSupplementaryOutputPathExcludingExtension); + auto serializedDiagnosticsPath = determineSupplementaryOutputFilename( OPT_serialize_diagnostics, pathsFromArguments.SerializedDiagnosticsPath, file_types::TY_SerializedDiagnostics, "", @@ -422,12 +440,15 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.ModuleDocOutputPath = moduleDocOutputPath; sop.DependenciesFilePath = dependenciesFilePath; sop.ReferenceDependenciesFilePath = referenceDependenciesFilePath; + sop.SwiftRangesFilePath = swiftRangesFilePath; + sop.CompiledSourceFilePath = compiledSourceFilePath; sop.SerializedDiagnosticsPath = serializedDiagnosticsPath; sop.FixItsOutputPath = fixItsOutputPath; sop.LoadedModuleTracePath = loadedModuleTracePath; sop.TBDPath = tbdPath; sop.ModuleInterfaceOutputPath = ModuleInterfaceOutputPath; sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; + sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; return sop; } @@ -500,12 +521,13 @@ createFromTypeToPathMap(const TypeToPathMap *map) { {file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath}, {file_types::TY_Dependencies, paths.DependenciesFilePath}, {file_types::TY_SwiftDeps, paths.ReferenceDependenciesFilePath}, + {file_types::TY_SwiftRanges, paths.SwiftRangesFilePath}, + {file_types::TY_CompiledSource, paths.CompiledSourceFilePath}, {file_types::TY_SerializedDiagnostics, paths.SerializedDiagnosticsPath}, {file_types::TY_ModuleTrace, paths.LoadedModuleTracePath}, {file_types::TY_TBD, paths.TBDPath}, {file_types::TY_SwiftModuleInterfaceFile, - paths.ModuleInterfaceOutputPath} - }; + paths.ModuleInterfaceOutputPath}}; for (const std::pair &typeAndString : typesAndStrings) { auto const out = map->find(typeAndString.first); @@ -522,11 +544,13 @@ SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { options::OPT_emit_module_doc_path, options::OPT_emit_dependencies_path, options::OPT_emit_reference_dependencies_path, + options::OPT_emit_swift_ranges_path, options::OPT_serialize_diagnostics_path, options::OPT_emit_loaded_module_trace_path, options::OPT_emit_module_interface_path, options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + options::OPT_emit_tbd_path, + options::OPT_emit_ldadd_cfile_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); @@ -542,7 +566,7 @@ SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { return None; } llvm::Expected OFM = - OutputFileMap::loadFromBuffer(std::move(buffer.get()), ""); + OutputFileMap::loadFromBuffer(std::move(buffer.get()), "", false); if (auto Err = OFM.takeError()) { Diags.diagnose(SourceLoc(), diag::error_unable_to_load_supplementary_output_file_map, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index fa6565237f0d6..40762133ccb58 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -38,12 +38,25 @@ swift::CompilerInvocation::CompilerInvocation() { setTargetTriple(llvm::sys::getDefaultTargetTriple()); } +void CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( + StringRef mainExecutablePath, llvm::SmallString<128> &runtimeResourcePath) { + runtimeResourcePath.assign(mainExecutablePath); + llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /swift + llvm::sys::path::remove_filename(runtimeResourcePath); // Remove /bin + llvm::sys::path::append(runtimeResourcePath, "lib", "swift"); +} + void CompilerInvocation::setMainExecutablePath(StringRef Path) { - llvm::SmallString<128> LibPath(Path); - llvm::sys::path::remove_filename(LibPath); // Remove /swift - llvm::sys::path::remove_filename(LibPath); // Remove /bin - llvm::sys::path::append(LibPath, "lib", "swift"); + llvm::SmallString<128> LibPath; + computeRuntimeResourcePathFromExecutablePath(Path, LibPath); setRuntimeResourcePath(LibPath.str()); + + llvm::SmallString<128> DiagnosticDocsPath(Path); + llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /swift + llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /bin + llvm::sys::path::append(DiagnosticDocsPath, "share", "doc", "swift", + "diagnostics"); + DiagnosticOpts.DiagnosticDocumentationPath = DiagnosticDocsPath.str(); } /// If we haven't explicitly passed -prebuilt-module-cache-path, set it to @@ -60,7 +73,14 @@ static void setDefaultPrebuiltCacheIfNecessary( return; SmallString<64> defaultPrebuiltPath{searchPathOpts.RuntimeResourcePath}; - StringRef platform = getPlatformNameForTriple(triple); + StringRef platform; + if (tripleIsMacCatalystEnvironment(triple)) { + // The prebuilt cache for macCatalyst is the same as the one for macOS, not iOS + // or a separate location of its own. + platform = "macosx"; + } else { + platform = getPlatformNameForTriple(triple); + } llvm::sys::path::append(defaultPrebuiltPath, platform, "prebuilt-modules"); frontendOpts.PrebuiltModuleCachePath = defaultPrebuiltPath.str(); } @@ -69,7 +89,11 @@ static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts, llvm::Triple &Triple) { llvm::SmallString<128> LibPath(SearchPathOpts.RuntimeResourcePath); - llvm::sys::path::append(LibPath, getPlatformNameForTriple(Triple)); + StringRef LibSubDir = getPlatformNameForTriple(Triple); + if (tripleIsMacCatalystEnvironment(Triple)) + LibSubDir = "maccatalyst"; + + llvm::sys::path::append(LibPath, LibSubDir); SearchPathOpts.RuntimeLibraryPaths.clear(); SearchPathOpts.RuntimeLibraryPaths.push_back(LibPath.str()); if (Triple.isOSDarwin()) @@ -88,6 +112,13 @@ static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts, SearchPathOpts.RuntimeLibraryImportPaths.push_back(LibPath.str()); if (!SearchPathOpts.SDKPath.empty()) { + if (tripleIsMacCatalystEnvironment(Triple)) { + LibPath = SearchPathOpts.SDKPath; + llvm::sys::path::append(LibPath, "System", "iOSSupport"); + llvm::sys::path::append(LibPath, "usr", "lib", "swift"); + SearchPathOpts.RuntimeLibraryImportPaths.push_back(LibPath.str()); + } + LibPath = SearchPathOpts.SDKPath; llvm::sys::path::append(LibPath, "usr", "lib", "swift"); if (!Triple.isOSDarwin()) { @@ -98,6 +129,54 @@ static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts, } } +static void +setIRGenOutputOptsFromFrontendOptions(IRGenOptions &IRGenOpts, + const FrontendOptions &FrontendOpts) { + // Set the OutputKind for the given Action. + IRGenOpts.OutputKind = [](FrontendOptions::ActionType Action) { + switch (Action) { + case FrontendOptions::ActionType::EmitIR: + return IRGenOutputKind::LLVMAssembly; + case FrontendOptions::ActionType::EmitBC: + return IRGenOutputKind::LLVMBitcode; + case FrontendOptions::ActionType::EmitAssembly: + return IRGenOutputKind::NativeAssembly; + case FrontendOptions::ActionType::Immediate: + return IRGenOutputKind::Module; + case FrontendOptions::ActionType::EmitObject: + default: + // Just fall back to emitting an object file. If we aren't going to run + // IRGen, it doesn't really matter what we put here anyways. + return IRGenOutputKind::ObjectFile; + } + }(FrontendOpts.RequestedAction); + + // If we're in JIT mode, set the requisite flags. + if (FrontendOpts.RequestedAction == FrontendOptions::ActionType::Immediate) { + IRGenOpts.UseJIT = true; + IRGenOpts.DebugInfoLevel = IRGenDebugInfoLevel::Normal; + IRGenOpts.DebugInfoFormat = IRGenDebugInfoFormat::DWARF; + } +} + +static void +setBridgingHeaderFromFrontendOptions(ClangImporterOptions &ImporterOpts, + const FrontendOptions &FrontendOpts) { + if (FrontendOpts.RequestedAction != FrontendOptions::ActionType::EmitPCH) + return; + + // If there aren't any inputs, there's nothing to do. + if (!FrontendOpts.InputsAndOutputs.hasInputs()) + return; + + // If we aren't asked to output a bridging header, we don't need to set this. + if (ImporterOpts.PrecompiledHeaderOutputDir.empty()) + return; + + ImporterOpts.BridgingHeader = + FrontendOpts.InputsAndOutputs.getFilenameOfFirstInput(); +} + void CompilerInvocation::setRuntimeResourcePath(StringRef Path) { SearchPathOpts.RuntimeResourcePath = Path; updateRuntimeLibraryPaths(SearchPathOpts, LangOpts.Target); @@ -214,6 +293,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts, Opts.PreserveTypesAsWritten |= Args.hasArg(OPT_module_interface_preserve_types_as_written); + Opts.PrintFullConvention |= + Args.hasArg(OPT_experimental_print_full_convention); } /// Save a copy of any flags marked as ModuleInterfaceOption, if running @@ -274,14 +355,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableExperimentalStaticAssert |= Args.hasArg(OPT_enable_experimental_static_assert); - Opts.EnableOperatorDesignatedTypes |= - Args.hasArg(OPT_enable_operator_designated_types); - - // Always enable operator designated types for the standard library. - Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; + Opts.EnableSubstSILFunctionTypesForFunctionValues |= + Args.hasArg(OPT_enable_subst_sil_function_types_for_function_values); - Opts.SolverEnableOperatorDesignatedTypes |= - Args.hasArg(OPT_solver_enable_operator_designated_types); + Opts.DiagnoseInvalidEphemeralnessAsError |= + Args.hasArg(OPT_enable_invalid_ephemeralness_as_error); if (auto A = Args.getLastArg(OPT_enable_deserialization_recovery, OPT_disable_deserialization_recovery)) { @@ -292,9 +370,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DisableAvailabilityChecking |= Args.hasArg(OPT_disable_availability_checking); - Opts.DisableTsanInoutInstrumentation |= - Args.hasArg(OPT_disable_tsan_inout_instrumentation); - if (FrontendOpts.InputKind == InputFileKind::SIL) Opts.DisableAvailabilityChecking = true; @@ -342,21 +417,39 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.StressASTScopeLookup |= Args.hasArg(OPT_stress_astscope_lookup); Opts.WarnIfASTScopeLookup |= Args.hasArg(OPT_warn_if_astscope_lookup); Opts.LazyASTScopes |= Args.hasArg(OPT_lazy_astscopes); + Opts.UseClangFunctionTypes |= Args.hasArg(OPT_use_clang_function_types); - Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); Opts.NamedLazyMemberLoading &= !Args.hasArg(OPT_disable_named_lazy_member_loading); - Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); if (Args.hasArg(OPT_verify_syntax_tree)) { Opts.BuildSyntaxTree = true; Opts.VerifySyntaxTree = true; } - - if (Args.hasArg(OPT_enable_experimental_dependencies)) - Opts.EnableExperimentalDependencies = true; - if (Args.hasArg(OPT_experimental_dependency_include_intrafile)) - Opts.ExperimentalDependenciesIncludeIntrafileOnes = true; + Opts.EnableFineGrainedDependencies = + Args.hasFlag(options::OPT_enable_fine_grained_dependencies, + options::OPT_disable_fine_grained_dependencies, + Opts.EnableFineGrainedDependencies); + Opts.EnableTypeFingerprints = + Args.hasFlag(options::OPT_enable_type_fingerprints, + options::OPT_disable_type_fingerprints, + LangOptions().EnableTypeFingerprints); + + if (!Opts.EnableFineGrainedDependencies && Opts.EnableTypeFingerprints) { + Diags.diagnose( + SourceLoc(), + diag::warning_type_fingerprints_require_fine_grained_dependencies); + Opts.EnableTypeFingerprints = false; + } + + if (Args.hasArg(OPT_emit_fine_grained_dependency_sourcefile_dot_files)) + Opts.EmitFineGrainedDependencySourcefileDotFiles = true; + + if (Args.hasArg(OPT_fine_grained_dependency_include_intrafile)) + Opts.FineGrainedDependenciesIncludeIntrafileOnes = true; + + if (Args.hasArg(OPT_enable_experimental_differentiable_programming)) + Opts.EnableExperimentalDifferentiableProgramming = true; Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support); if (Opts.DebuggerSupport) @@ -377,37 +470,13 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableTestableAttrRequiresTestableModule = A->getOption().matches(OPT_enable_testable_attr_requires_testable_module); } - - if (const Arg *A = Args.getLastArg(OPT_debug_constraints_attempt)) { - unsigned attempt; - if (StringRef(A->getValue()).getAsInteger(10, attempt)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.DebugConstraintSolverAttempt = attempt; - } - } - - for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { - unsigned line; - if (StringRef(A->getValue()).getAsInteger(10, line)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.DebugConstraintSolverOnLines.push_back(line); - } - } - llvm::sort(Opts.DebugConstraintSolverOnLines); - if (const Arg *A = Args.getLastArg(OPT_debug_forbid_typecheck_prefix)) { - Opts.DebugForbidTypecheckPrefix = A->getValue(); - } - if (Args.getLastArg(OPT_debug_cycles)) Opts.DebugDumpCycles = true; + if (Args.getLastArg(OPT_build_request_dependency_graph)) + Opts.BuildRequestDependencyGraph = true; + if (const Arg *A = Args.getLastArg(OPT_output_request_graphviz)) { Opts.RequestEvaluatorGraphVizPath = A->getValue(); } @@ -419,35 +488,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } - if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverMemoryThreshold = threshold; - } - } - - if (const Arg *A = Args.getLastArg(OPT_solver_shrink_unsolved_threshold)) { - unsigned threshold; - if (StringRef(A->getValue()).getAsInteger(10, threshold)) { - Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, - A->getAsString(Args), A->getValue()); - HadError = true; - } else { - Opts.SolverShrinkUnsolvedThreshold = threshold; - } - } - - if (Args.getLastArg(OPT_solver_disable_shrink)) - Opts.SolverDisableShrink = true; - Opts.FunctionBuilderOneWayConstraints = - Args.hasFlag(OPT_enable_function_builder_one_way_constraints, - OPT_disable_function_builder_one_way_constraints, - /*Default=*/true); - if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) { unsigned threshold; if (StringRef(A->getValue()).getAsInteger(10, threshold)) { @@ -500,6 +540,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.OptimizationRemarkMissedPattern = generateOptimizationRemarkRegex(Diags, Args, A); + Opts.EnableConcisePoundFile = + Args.hasArg(OPT_enable_experimental_concise_pound_file); + llvm::Triple Target = Opts.Target; StringRef TargetArg; if (const Arg *A = Args.getLastArg(OPT_target)) { @@ -507,6 +550,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, TargetArg = A->getValue(); } + if (const Arg *A = Args.getLastArg(OPT_target_variant)) { + Opts.TargetVariant = llvm::Triple(A->getValue()); + } + Opts.EnableCXXInterop |= Args.hasArg(OPT_enable_cxx_interop); Opts.EnableObjCInterop = Args.hasFlag(OPT_enable_objc_interop, OPT_disable_objc_interop, @@ -519,9 +566,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, (Target.isTvOS() && Target.isOSVersionLT(12, 2)) || (Target.isWatchOS() && Target.isOSVersionLT(5, 2)); - Opts.DisableConstraintSolverPerformanceHacks |= - Args.hasArg(OPT_disable_constraint_solver_performance_hacks); - // Must be processed after any other language options that could affect // platform conditions. bool UnsupportedOS, UnsupportedArch; @@ -543,6 +587,88 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, return HadError || UnsupportedOS || UnsupportedArch; } +static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, + DiagnosticEngine &Diags, + const FrontendOptions &FrontendOpts) { + using namespace options; + + bool HadError = false; + auto setUnsignedIntegerArgument = + [&Args, &Diags, &HadError](options::ID optionID, unsigned &valueToSet) { + if (const Arg *A = Args.getLastArg(optionID)) { + unsigned attempt; + if (StringRef(A->getValue()).getAsInteger(/*radix*/ 10, attempt)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + valueToSet = attempt; + } + } + }; + + setUnsignedIntegerArgument(OPT_warn_long_function_bodies, + Opts.WarnLongFunctionBodies); + setUnsignedIntegerArgument(OPT_warn_long_expression_type_checking, + Opts.WarnLongExpressionTypeChecking); + setUnsignedIntegerArgument(OPT_solver_expression_time_threshold_EQ, + Opts.ExpressionTimeoutThreshold); + setUnsignedIntegerArgument(OPT_switch_checking_invocation_threshold_EQ, + Opts.SwitchCheckingInvocationThreshold); + setUnsignedIntegerArgument(OPT_debug_constraints_attempt, + Opts.DebugConstraintSolverAttempt); + setUnsignedIntegerArgument(OPT_solver_memory_threshold, + Opts.SolverMemoryThreshold); + setUnsignedIntegerArgument(OPT_solver_shrink_unsolved_threshold, + Opts.SolverShrinkUnsolvedThreshold); + + Opts.DebugTimeFunctionBodies |= Args.hasArg(OPT_debug_time_function_bodies); + Opts.DebugTimeExpressions |= + Args.hasArg(OPT_debug_time_expression_type_checking); + Opts.SkipNonInlinableFunctionBodies |= + Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies); + + // If asked to perform InstallAPI, go ahead and enable non-inlinable function + // body skipping. + Opts.SkipNonInlinableFunctionBodies |= Args.hasArg(OPT_tbd_is_installapi); + + Opts.DisableConstraintSolverPerformanceHacks |= + Args.hasArg(OPT_disable_constraint_solver_performance_hacks); + + Opts.EnableOperatorDesignatedTypes |= + Args.hasArg(OPT_enable_operator_designated_types); + + // Always enable operator designated types for the standard library. + Opts.EnableOperatorDesignatedTypes |= FrontendOpts.ParseStdlib; + + Opts.SolverEnableOperatorDesignatedTypes |= + Args.hasArg(OPT_solver_enable_operator_designated_types); + + Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); + Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); + + for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) { + unsigned line; + if (StringRef(A->getValue()).getAsInteger(/*radix*/ 10, line)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + HadError = true; + } else { + Opts.DebugConstraintSolverOnLines.push_back(line); + } + } + llvm::sort(Opts.DebugConstraintSolverOnLines); + + if (const Arg *A = Args.getLastArg(OPT_debug_forbid_typecheck_prefix)) { + Opts.DebugForbidTypecheckPrefix = A->getValue(); + } + + if (Args.getLastArg(OPT_solver_disable_shrink)) + Opts.SolverDisableShrink = true; + + return HadError; +} + static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args, DiagnosticEngine &Diags, @@ -587,6 +713,9 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, if (Args.hasArg(OPT_embed_bitcode)) Opts.Mode = ClangImporterOptions::Modes::EmbedBitcode; + else if (Args.hasArg(OPT_emit_pcm) || Args.hasArg(OPT_dump_pcm)) + Opts.Mode = ClangImporterOptions::Modes::PrecompiledModule; + if (auto *A = Args.getLastArg(OPT_import_objc_header)) Opts.BridgingHeader = A->getValue(); Opts.DisableSwiftBridgeAttr |= Args.hasArg(OPT_disable_swift_bridge_attr); @@ -598,7 +727,14 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, Opts.PCHDisableValidation |= Args.hasArg(OPT_pch_disable_validation); } + if (Args.hasArg(OPT_warnings_as_errors)) + Opts.ExtraArgs.push_back("-Werror"); + Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support); + + Opts.DisableSourceImport |= + Args.hasArg(OPT_disable_clangimporter_source_import); + return false; } @@ -678,7 +814,11 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.SuppressWarnings |= Args.hasArg(OPT_suppress_warnings); Opts.WarningsAsErrors |= Args.hasArg(OPT_warnings_as_errors); Opts.PrintDiagnosticNames |= Args.hasArg(OPT_debug_diagnostic_names); - + Opts.EnableDescriptiveDiagnostics |= + Args.hasArg(OPT_enable_descriptive_diagnostics); + if (Arg *A = Args.getLastArg(OPT_diagnostic_documentation_path)) { + Opts.DiagnosticDocumentationPath = A->getValue(); + } assert(!(Opts.WarningsAsErrors && Opts.SuppressWarnings) && "conflicting arguments; should have been caught by driver"); @@ -828,11 +968,15 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, // -Ounchecked might also set removal of runtime asserts (cond_fail). Opts.RemoveRuntimeAsserts |= Args.hasArg(OPT_RemoveRuntimeAsserts); - Opts.EnableARCOptimizations |= !Args.hasArg(OPT_disable_arc_opts); + Opts.EnableARCOptimizations &= !Args.hasArg(OPT_disable_arc_opts); + Opts.EnableOSSAOptimizations &= !Args.hasArg(OPT_disable_ossa_opts); + Opts.EnableSpeculativeDevirtualization |= Args.hasArg(OPT_enable_spec_devirt); Opts.DisableSILPerfOptimizations |= Args.hasArg(OPT_disable_sil_perf_optzns); + Opts.CrossModuleOptimization |= Args.hasArg(OPT_CrossModuleOptimization); Opts.VerifyAll |= Args.hasArg(OPT_sil_verify_all); Opts.DebugSerialization |= Args.hasArg(OPT_sil_debug_serialization); Opts.EmitVerboseSIL |= Args.hasArg(OPT_emit_verbose_sil); + Opts.EmitSortedSIL |= Args.hasArg(OPT_emit_sorted_sil); Opts.PrintInstCounts |= Args.hasArg(OPT_print_inst_counts); if (const Arg *A = Args.getLastArg(OPT_external_pass_pipeline_filename)) Opts.ExternalPassPipelineFilename = A->getValue(); @@ -876,6 +1020,12 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, IRGenOpts.Sanitizers = Opts.Sanitizers; } + if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) { + IRGenOpts.SanitizersWithRecoveryInstrumentation = + parseSanitizerRecoverArgValues(A, Opts.Sanitizers, Diags, + /*emitWarnings=*/true); + } + if (auto A = Args.getLastArg(OPT_enable_verify_exclusivity, OPT_disable_verify_exclusivity)) { Opts.VerifyExclusivity @@ -946,16 +1096,14 @@ static bool ParseTBDGenArgs(TBDGenOptions &Opts, ArgList &Args, Opts.IsInstallAPI = Args.hasArg(OPT_tbd_is_installapi); if (const Arg *A = Args.getLastArg(OPT_tbd_compatibility_version)) { - if (auto vers = version::Version::parseVersionString( - A->getValue(), SourceLoc(), &Diags)) { - Opts.CompatibilityVersion = *vers; - } + Opts.CompatibilityVersion = A->getValue(); } + if (const Arg *A = Args.getLastArg(OPT_tbd_current_version)) { - if (auto vers = version::Version::parseVersionString( - A->getValue(), SourceLoc(), &Diags)) { - Opts.CurrentVersion = *vers; - } + Opts.CurrentVersion = A->getValue(); + } + if (const Arg *A = Args.getLastArg(OPT_previous_module_installname_map_file)) { + Opts.ModuleInstallNameMapPath = A->getValue(); } return false; } @@ -1075,6 +1223,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.StackPromotionSizeLimit = limit; } + Opts.FunctionSections = Args.hasArg(OPT_function_sections); + if (Args.hasArg(OPT_autolink_force_load)) Opts.ForceLoadSymbolName = Args.getLastArgValue(OPT_module_link_name); @@ -1083,6 +1233,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_no_clang_module_breadcrumbs)) Opts.DisableClangModuleSkeletonCUs = true; + if (Args.hasArg(OPT_disable_debugger_shadow_copies)) + Opts.DisableDebuggerShadowCopies = true; + if (Args.hasArg(OPT_use_jit)) Opts.UseJIT = true; @@ -1185,6 +1338,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.DisableLegacyTypeInfo = true; } + if (Args.hasArg(OPT_prespecialize_generic_metadata)) { + Opts.PrespecializeGenericMetadata = true; + } + if (const Arg *A = Args.getLastArg(OPT_read_legacy_type_info_path_EQ)) { Opts.ReadLegacyTypeInfoPath = A->getValue(); } @@ -1205,11 +1362,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, A->getAsString(Args), A->getValue()); } } - - // Autolink runtime compatibility libraries, if asked to. - if (!Args.hasArg(options::OPT_disable_autolinking_runtime_compatibility)) { + + auto getRuntimeCompatVersion = [&] () -> Optional { Optional runtimeCompatibilityVersion; - if (auto versionArg = Args.getLastArg( options::OPT_runtime_compatibility_version)) { auto version = StringRef(versionArg->getValue()); @@ -1225,15 +1380,18 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, runtimeCompatibilityVersion = getSwiftRuntimeCompatibilityVersionForTarget(Triple); } - - Opts.AutolinkRuntimeCompatibilityLibraryVersion = - runtimeCompatibilityVersion; + return runtimeCompatibilityVersion; + }; + + // Autolink runtime compatibility libraries, if asked to. + if (!Args.hasArg(options::OPT_disable_autolinking_runtime_compatibility)) { + Opts.AutolinkRuntimeCompatibilityLibraryVersion = getRuntimeCompatVersion(); } if (!Args.hasArg(options:: OPT_disable_autolinking_runtime_compatibility_dynamic_replacements)) { Opts.AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion = - getSwiftRuntimeCompatibilityVersionForTarget(Triple); + getRuntimeCompatVersion(); } return false; } @@ -1370,6 +1528,10 @@ bool CompilerInvocation::parseArgs( return true; } + if (ParseTypeCheckerArgs(TypeCheckerOpts, ParsedArgs, Diags, FrontendOpts)) { + return true; + } + if (ParseClangImporterArgs(ClangImporterOpts, ParsedArgs, Diags, workingDirectory)) { return true; @@ -1408,6 +1570,10 @@ bool CompilerInvocation::parseArgs( setDefaultPrebuiltCacheIfNecessary(FrontendOpts, SearchPathOpts, LangOpts.Target); + // Now that we've parsed everything, setup some inter-option-dependent state. + setIRGenOutputOptsFromFrontendOptions(IRGenOpts, FrontendOpts); + setBridgingHeaderFromFrontendOptions(ClangImporterOpts, FrontendOpts); + return false; } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 6d8bb6478e95b..a2cf2504c0665 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -19,7 +19,10 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/FileSystem.h" +#include "swift/AST/IncrementalRanges.h" #include "swift/AST/Module.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" @@ -100,6 +103,16 @@ std::string CompilerInvocation::getReferenceDependenciesFilePathForPrimary( .SupplementaryOutputs.ReferenceDependenciesFilePath; } std::string +CompilerInvocation::getSwiftRangesFilePathForPrimary(StringRef filename) const { + return getPrimarySpecificPathsForPrimary(filename) + .SupplementaryOutputs.SwiftRangesFilePath; +} +std::string CompilerInvocation::getCompiledSourceFilePathForPrimary( + StringRef filename) const { + return getPrimarySpecificPathsForPrimary(filename) + .SupplementaryOutputs.CompiledSourceFilePath; +} +std::string CompilerInvocation::getSerializedDiagnosticsPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() .SupplementaryOutputs.SerializedDiagnosticsPath; @@ -111,6 +124,14 @@ std::string CompilerInvocation::getTBDPathForWholeModule() const { .SupplementaryOutputs.TBDPath; } +std::string +CompilerInvocation::getLdAddCFileOutputPathForWholeModule() const { + assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && + "LdAdd cfile only makes sense when the whole module can be seen"); + return getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.LdAddCFilePath; +} + std::string CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const { assert(getFrontendOptions().InputsAndOutputs.isWholeModule() && @@ -121,7 +142,7 @@ CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const { } SerializationOptions CompilerInvocation::computeSerializationOptions( - const SupplementaryOutputPaths &outs, bool moduleIsPublic) { + const SupplementaryOutputPaths &outs, bool moduleIsPublic) const { const FrontendOptions &opts = getFrontendOptions(); SerializationOptions serializationOpts; @@ -164,10 +185,6 @@ void CompilerInstance::createSILModule() { Invocation.getFrontendOptions().InputsAndOutputs.isWholeModule()); } -void CompilerInstance::setSILModule(std::unique_ptr M) { - TheSILModule = std::move(M); -} - void CompilerInstance::recordPrimaryInputBuffer(unsigned BufID) { PrimaryBufferIDs.insert(BufID); } @@ -190,11 +207,12 @@ bool CompilerInstance::setUpASTContextIfNeeded() { return false; } - Context.reset(ASTContext::get(Invocation.getLangOptions(), - Invocation.getSearchPathOptions(), SourceMgr, - Diagnostics)); + Context.reset(ASTContext::get( + Invocation.getLangOptions(), Invocation.getTypeCheckerOptions(), + Invocation.getSearchPathOptions(), SourceMgr, Diagnostics)); registerParseRequestFunctions(Context->evaluator); registerTypeCheckerRequestFunctions(Context->evaluator); + registerSILGenRequestFunctions(Context->evaluator); // Migrator, indexing and typo correction need some IDE requests. // The integrated REPL needs IDE requests for completion. @@ -222,17 +240,28 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) { setUpLLVMArguments(); setUpDiagnosticOptions(); + const auto &frontendOpts = Invocation.getFrontendOptions(); + // If we are asked to emit a module documentation file, configure lexing and // parsing to remember comments. - if (Invocation.getFrontendOptions().InputsAndOutputs.hasModuleDocOutputPath()) + if (frontendOpts.InputsAndOutputs.hasModuleDocOutputPath()) Invocation.getLangOptions().AttachCommentsToDecls = true; // If we are doing index-while-building, configure lexing and parsing to // remember comments. - if (!Invocation.getFrontendOptions().IndexStorePath.empty()) { + if (!frontendOpts.IndexStorePath.empty()) { Invocation.getLangOptions().AttachCommentsToDecls = true; } + // Set up the type checker options. + auto &typeCkOpts = Invocation.getTypeCheckerOptions(); + if (isWholeModuleCompilation()) { + typeCkOpts.DelayWholeModuleChecking = true; + } + if (FrontendOptions::isActionImmediate(frontendOpts.RequestedAction)) { + typeCkOpts.InImmediateMode = true; + } + assert(Lexer::isIdentifier(Invocation.getModuleName())); if (isInSILMode()) @@ -316,8 +345,30 @@ void CompilerInstance::setUpDiagnosticOptions() { if (Invocation.getDiagnosticOptions().PrintDiagnosticNames) { Diagnostics.setPrintDiagnosticNames(true); } + if (Invocation.getDiagnosticOptions().EnableDescriptiveDiagnostics) { + Diagnostics.setUseDescriptiveDiagnostics(true); + } + Diagnostics.setDiagnosticDocumentationPath( + Invocation.getDiagnosticOptions().DiagnosticDocumentationPath); } +// The ordering of ModuleLoaders is important! +// +// 1. SourceLoader: This is a hack and only the compiler's tests are using it, +// to avoid writing repetitive code involving generating modules/interfaces. +// Ideally, we'd get rid of it. +// 2. MemoryBufferSerializedModuleLoader: This is used by LLDB, because it might +// already have the module available in memory. +// 3. ModuleInterfaceLoader: Tries to find an up-to-date swiftmodule. If it +// succeeds, it issues a particular "error" (see +// [Note: ModuleInterfaceLoader-defer-to-SerializedModuleLoader]), which +// is interpreted by the overarching loader as a command to use the +// SerializedModuleLoader. If we failed to find a .swiftmodule, this falls +// back to using an interface. Actual errors lead to diagnostics. +// 4. SerializedModuleLoader: Loads a serialized module if it can. +// 5. ClangImporter: This must come after all the Swift module loaders because +// in the presence of overlays and mixed-source frameworks, we want to prefer +// the overlay or framework module over the underlying Clang module. bool CompilerInstance::setUpModuleLoaders() { if (hasSourceImport()) { bool enableLibraryEvolution = @@ -346,18 +397,15 @@ bool CompilerInstance::setUpModuleLoaders() { return true; } } - + auto IgnoreSourceInfoFile = + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo; if (Invocation.getLangOptions().EnableMemoryBufferImporter) { auto MemoryBufferLoader = MemoryBufferSerializedModuleLoader::create( - *Context, getDependencyTracker()); + *Context, getDependencyTracker(), MLM, IgnoreSourceInfoFile); this->MemoryBufferLoader = MemoryBufferLoader.get(); Context->addModuleLoader(std::move(MemoryBufferLoader)); } - std::unique_ptr SML = - SerializedModuleLoader::create(*Context, getDependencyTracker(), MLM); - this->SML = SML.get(); - // Wire up the Clang importer. If the user has specified an SDK, use it. // Otherwise, we just keep it around as our interface to Clang's ABI // knowledge. @@ -377,10 +425,16 @@ bool CompilerInstance::setUpModuleLoaders() { auto PIML = ModuleInterfaceLoader::create( *Context, ModuleCachePath, PrebuiltModuleCachePath, getDependencyTracker(), MLM, FEOpts.PreferInterfaceForModules, - FEOpts.RemarkOnRebuildFromModuleInterface); + FEOpts.RemarkOnRebuildFromModuleInterface, IgnoreSourceInfoFile); Context->addModuleLoader(std::move(PIML)); } + + std::unique_ptr SML = + SerializedModuleLoader::create(*Context, getDependencyTracker(), MLM, + IgnoreSourceInfoFile); + this->SML = SML.get(); Context->addModuleLoader(std::move(SML)); + Context->addModuleLoader(std::move(clangImporter), /*isClang*/ true); return false; @@ -479,9 +533,7 @@ Optional CompilerInstance::getRecordedBufferID(const InputFile &input, // FIXME: The fact that this test happens twice, for some cases, // suggests that setupInputs could use another round of refactoring. if (serialization::isSerializedAST(buffers->ModuleBuffer->getBuffer())) { - PartialModules.push_back( - {std::move(buffers->ModuleBuffer), std::move(buffers->ModuleDocBuffer), - std::move(buffers->ModuleSourceInfoBuffer)}); + PartialModules.push_back(std::move(*buffers)); return None; } assert(buffers->ModuleDocBuffer.get() == nullptr); @@ -493,7 +545,7 @@ Optional CompilerInstance::getRecordedBufferID(const InputFile &input, return bufferID; } -Optional CompilerInstance::getInputBuffersIfPresent( +Optional CompilerInstance::getInputBuffersIfPresent( const InputFile &input) { if (auto b = input.buffer()) { return ModuleBuffers(llvm::MemoryBuffer::getMemBufferCopy(b->getBuffer(), @@ -563,7 +615,7 @@ std::unique_ptr CompilerInstance::takeSILModule() { return std::move(TheSILModule); } -ModuleDecl *CompilerInstance::getMainModule() { +ModuleDecl *CompilerInstance::getMainModule() const { if (!MainModule) { Identifier ID = Context->getIdentifier(Invocation.getModuleName()); MainModule = ModuleDecl::create(ID, *Context); @@ -723,7 +775,7 @@ ModuleDecl *CompilerInstance::importUnderlyingModule() { ModuleDecl *objCModuleUnderlyingMixedFramework = static_cast(Context->getClangModuleLoader()) ->loadModule(SourceLoc(), - std::make_pair(MainModule->getName(), SourceLoc())); + { Located(MainModule->getName(), SourceLoc()) }); if (objCModuleUnderlyingMixedFramework) return objCModuleUnderlyingMixedFramework; Diagnostics.diagnose(SourceLoc(), diag::error_underlying_module_not_found, @@ -753,7 +805,7 @@ void CompilerInstance::getImplicitlyImportedModules( if (Lexer::isIdentifier(ImplicitImportModuleName)) { auto moduleID = Context->getIdentifier(ImplicitImportModuleName); ModuleDecl *importModule = - Context->getModule(std::make_pair(moduleID, SourceLoc())); + Context->getModule({ Located(moduleID, SourceLoc()) }); if (importModule) { importModules.push_back(importModule); } else { @@ -802,14 +854,12 @@ void CompilerInstance::parseAndCheckTypesUpTo( if (hadLoadError) return; - OptionSet TypeCheckOptions = computeTypeCheckingOptions(); - // Type-check main file after parsing all other files so that // it can use declarations from other files. - // In addition, the main file has parsing and type-checking - // interwined. + // In addition, in SIL mode the main file has parsing and + // type-checking interwined. if (MainBufferID != NO_SUCH_BUFFER) { - parseAndTypeCheckMainFileUpTo(limitStage, TypeCheckOptions); + parseAndTypeCheckMainFileUpTo(limitStage); } assert(llvm::all_of(MainModule->getFiles(), [](const FileUnit *File) -> bool { @@ -820,22 +870,16 @@ void CompilerInstance::parseAndCheckTypesUpTo( }) && "some files have not yet had their imports resolved"); MainModule->setHasResolvedImports(); - // If the limiting AST stage is name binding, we're done. - if (limitStage <= SourceFile::NameBound) { - return; - } - - const auto &options = Invocation.getFrontendOptions(); forEachFileToTypeCheck([&](SourceFile &SF) { - performTypeChecking(SF, PersistentState->getTopLevelContext(), - TypeCheckOptions, /*curElem*/ 0, - options.WarnLongFunctionBodies, - options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold, - options.SwitchCheckingInvocationThreshold); + if (limitStage == SourceFile::NameBound) { + bindExtensions(SF); + return; + } + + performTypeChecking(SF); if (!Context->hadError() && Invocation.getFrontendOptions().PCMacro) { - performPCMacro(SF, PersistentState->getTopLevelContext()); + performPCMacro(SF); } // Playground transform knows to look out for PCMacro's changes and not @@ -847,11 +891,12 @@ void CompilerInstance::parseAndCheckTypesUpTo( } }); - if (Invocation.isCodeCompletion()) { - performDelayedParsing(MainModule, *PersistentState.get(), - Invocation.getCodeCompletionFactory()); + // If the limiting AST stage is name binding, we're done. + if (limitStage <= SourceFile::NameBound) { + return; } - finishTypeChecking(TypeCheckOptions); + + finishTypeChecking(); } void CompilerInstance::parseLibraryFile( @@ -882,27 +927,6 @@ void CompilerInstance::parseLibraryFile( performNameBinding(*NextInput); } -OptionSet CompilerInstance::computeTypeCheckingOptions() { - OptionSet TypeCheckOptions; - if (isWholeModuleCompilation()) { - TypeCheckOptions |= TypeCheckingFlags::DelayWholeModuleChecking; - } - const auto &options = Invocation.getFrontendOptions(); - if (options.DebugTimeFunctionBodies) { - TypeCheckOptions |= TypeCheckingFlags::DebugTimeFunctionBodies; - } - if (FrontendOptions::isActionImmediate(options.RequestedAction)) { - TypeCheckOptions |= TypeCheckingFlags::ForImmediateMode; - } - if (options.DebugTimeExpressionTypeChecking) { - TypeCheckOptions |= TypeCheckingFlags::DebugTimeExpressions; - } - if (options.SkipNonInlinableFunctionBodies) { - TypeCheckOptions |= TypeCheckingFlags::SkipNonInlinableFunctionBodies; - } - return TypeCheckOptions; -} - bool CompilerInstance::parsePartialModulesAndLibraryFiles( const ImplicitImports &implicitImports) { FrontendStatsTracer tracer(Context->Stats, @@ -911,8 +935,8 @@ bool CompilerInstance::parsePartialModulesAndLibraryFiles( // Parse all the partial modules first. for (auto &PM : PartialModules) { assert(PM.ModuleBuffer); - if (!SML->loadAST(*MainModule, SourceLoc(), std::move(PM.ModuleBuffer), - std::move(PM.ModuleDocBuffer), + if (!SML->loadAST(*MainModule, SourceLoc(), /*moduleInterfacePath*/"", + std::move(PM.ModuleBuffer), std::move(PM.ModuleDocBuffer), std::move(PM.ModuleSourceInfoBuffer), /*isFramework*/false, /*treatAsPartialModule*/true)) hadLoadError = true; @@ -928,8 +952,7 @@ bool CompilerInstance::parsePartialModulesAndLibraryFiles( } void CompilerInstance::parseAndTypeCheckMainFileUpTo( - SourceFile::ASTStage_t LimitStage, - OptionSet TypeCheckOptions) { + SourceFile::ASTStage_t LimitStage) { FrontendStatsTracer tracer(Context->Stats, "parse-and-typecheck-main-file"); bool mainIsPrimary = @@ -953,30 +976,35 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( parseIntoSourceFile(MainFile, MainFile.getBufferID().getValue(), &Done, TheSILModule ? &SILContext : nullptr, PersistentState.get(), - /*DelayedBodyParsing=*/false); + !mainIsPrimary); + + // For SIL we actually have to interleave parsing and type checking + // because the SIL parser expects to see fully type checked declarations. + if (TheSILModule) { + if (Done || CurTUElem < MainFile.getTopLevelDecls().size()) { + assert(mainIsPrimary); + performTypeChecking(MainFile, CurTUElem); + } + } + + CurTUElem = MainFile.getTopLevelDecls().size(); + } while (!Done); - if (mainIsPrimary && (Done || CurTUElem < MainFile.Decls.size())) { + if (!TheSILModule) { + if (mainIsPrimary) { switch (LimitStage) { case SourceFile::Parsing: case SourceFile::Parsed: llvm_unreachable("invalid limit stage"); case SourceFile::NameBound: - performNameBinding(MainFile, CurTUElem); + performNameBinding(MainFile); break; case SourceFile::TypeChecked: - const auto &options = Invocation.getFrontendOptions(); - performTypeChecking(MainFile, PersistentState->getTopLevelContext(), - TypeCheckOptions, CurTUElem, - options.WarnLongFunctionBodies, - options.WarnLongExpressionTypeChecking, - options.SolverExpressionTimeThreshold, - options.SwitchCheckingInvocationThreshold); + performTypeChecking(MainFile); break; } } - - CurTUElem = MainFile.Decls.size(); - } while (!Done); + } Diags.setSuppressWarnings(DidSuppressWarnings); @@ -985,8 +1013,10 @@ void CompilerInstance::parseAndTypeCheckMainFileUpTo( performDebuggerTestingTransform(MainFile); } - if (!mainIsPrimary) { - performNameBinding(MainFile); + if (!TheSILModule) { + if (!mainIsPrimary) { + performNameBinding(MainFile); + } } } @@ -1010,9 +1040,8 @@ void CompilerInstance::forEachFileToTypeCheck( } } -void CompilerInstance::finishTypeChecking( - OptionSet TypeCheckOptions) { - if (TypeCheckOptions & TypeCheckingFlags::DelayWholeModuleChecking) { +void CompilerInstance::finishTypeChecking() { + if (getASTContext().TypeCheckerOpts.DelayWholeModuleChecking) { forEachSourceFileIn(MainModule, [&](SourceFile &SF) { performWholeModuleTypeChecking(SF); }); @@ -1039,7 +1068,7 @@ SourceFile *CompilerInstance::createSourceFileForMainModule( } void CompilerInstance::performParseOnly(bool EvaluateConditionals, - bool ParseDelayedBodyOnEnd) { + bool CanDelayBodies) { const InputFileKind Kind = Invocation.getInputKind(); ModuleDecl *const MainModule = getMainModule(); Context->LoadedModules[MainModule->getName()] = MainModule; @@ -1061,25 +1090,27 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, } PersistentState = llvm::make_unique(); + PersistentState->PerformConditionEvaluation = EvaluateConditionals; + + auto shouldDelayBodies = [&](unsigned bufferID) -> bool { + if (!CanDelayBodies) + return false; - SWIFT_DEFER { - if (ParseDelayedBodyOnEnd) - PersistentState->parseAllDelayedDeclLists(); + // Don't delay bodies in whole module mode or for primary files. + return !(isWholeModuleCompilation() || isPrimaryInput(bufferID)); }; - PersistentState->PerformConditionEvaluation = EvaluateConditionals; + // Parse all the library files. for (auto BufferID : InputSourceCodeBufferIDs) { if (BufferID == MainBufferID) continue; - auto IsPrimary = isWholeModuleCompilation() || isPrimaryInput(BufferID); - SourceFile *NextInput = createSourceFileForMainModule( SourceFileKind::Library, SourceFile::ImplicitModuleImportKind::None, BufferID); parseIntoSourceFileFull(*NextInput, BufferID, PersistentState.get(), - /*DelayBodyParsing=*/!IsPrimary); + shouldDelayBodies(BufferID)); } // Now parse the main file. @@ -1087,10 +1118,10 @@ void CompilerInstance::performParseOnly(bool EvaluateConditionals, SourceFile &MainFile = MainModule->getMainSourceFile(Invocation.getSourceFileKind()); MainFile.SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache(); + assert(MainBufferID == MainFile.getBufferID()); - parseIntoSourceFileFull(MainFile, MainFile.getBufferID().getValue(), - PersistentState.get(), - /*DelayBodyParsing=*/false); + parseIntoSourceFileFull(MainFile, MainBufferID, PersistentState.get(), + shouldDelayBodies(MainBufferID)); } assert(Context->LoadedModules.size() == 1 && @@ -1135,7 +1166,8 @@ static bool performMandatorySILPasses(CompilerInvocation &Invocation, /// These may change across compiler versions. static void performSILOptimizations(CompilerInvocation &Invocation, SILModule *SM) { - SharedTimer timer("SIL optimization"); + FrontendStatsTracer tracer(SM->getASTContext().Stats, + "SIL optimization"); if (Invocation.getFrontendOptions().RequestedAction == FrontendOptions::ActionType::MergeModules || !Invocation.getSILOptions().shouldOptimize()) { @@ -1178,7 +1210,8 @@ bool CompilerInstance::performSILProcessing(SILModule *silModule, return true; { - SharedTimer timer("SIL verification, pre-optimization"); + FrontendStatsTracer tracer(silModule->getASTContext().Stats, + "SIL verification, pre-optimization"); silModule->verify(); } @@ -1188,7 +1221,8 @@ bool CompilerInstance::performSILProcessing(SILModule *silModule, countStatsPostSILOpt(*stats, *silModule); { - SharedTimer timer("SIL verification, post-optimization"); + FrontendStatsTracer tracer(silModule->getASTContext().Stats, + "SIL verification, post-optimization"); silModule->verify(); } @@ -1215,3 +1249,20 @@ CompilerInstance::getPrimarySpecificPathsForSourceFile( const SourceFile &SF) const { return Invocation.getPrimarySpecificPathsForSourceFile(SF); } + +bool CompilerInstance::emitSwiftRanges(DiagnosticEngine &diags, + SourceFile *primaryFile, + StringRef outputPath) const { + return incremental_ranges::SwiftRangesEmitter(outputPath, primaryFile, + SourceMgr, diags) + .emit(); + return false; +} + +bool CompilerInstance::emitCompiledSource(DiagnosticEngine &diags, + const SourceFile *primaryFile, + StringRef outputPath) const { + return incremental_ranges::CompiledSourceEmitter(outputPath, primaryFile, + SourceMgr, diags) + .emit(); +} diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index e5e94ec621de9..ee0b67fc0b64e 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -412,6 +412,18 @@ bool FrontendInputsAndOutputs::hasReferenceDependenciesPath() const { return outs.ReferenceDependenciesFilePath; }); } +bool FrontendInputsAndOutputs::hasSwiftRangesPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.SwiftRangesFilePath; + }); +} +bool FrontendInputsAndOutputs::hasCompiledSourcePath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.CompiledSourceFilePath; + }); +} bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 46e60edfb27c6..8e3a467237348 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -40,6 +40,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::PrintAST: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpPCM: return false; case ActionType::EmitPCH: case ActionType::EmitSILGen: @@ -59,6 +60,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::EmitObject: case ActionType::EmitImportedModules: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: return true; } llvm_unreachable("Unknown ActionType"); @@ -95,6 +97,8 @@ bool FrontendOptions::isActionImmediate(ActionType action) { case ActionType::EmitObject: case ActionType::EmitImportedModules: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; } llvm_unreachable("Unknown ActionType"); @@ -154,6 +158,7 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: + case ActionType::DumpPCM: return TY_Nothing; case ActionType::EmitPCH: @@ -195,6 +200,9 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { case ActionType::EmitImportedModules: return TY_ImportedModules; + + case ActionType::EmitPCM: + return TY_ClangModuleFile; } llvm_unreachable("unhandled action"); } @@ -214,6 +222,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::DumpPCM: return false; case ActionType::ResolveImports: case ActionType::Typecheck: @@ -229,6 +238,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::EmitAssembly: case ActionType::EmitObject: case ActionType::EmitImportedModules: + case ActionType::EmitPCM: return true; } llvm_unreachable("unhandled action"); @@ -250,6 +260,8 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -269,6 +281,14 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) { llvm_unreachable("unhandled action"); } +bool FrontendOptions::canActionEmitSwiftRanges(ActionType action) { + return canActionEmitReferenceDependencies(action); +} + +bool FrontendOptions::canActionEmitCompiledSource(ActionType action) { + return canActionEmitReferenceDependencies(action); +} + bool FrontendOptions::canActionEmitObjCHeader(ActionType action) { switch (action) { case ActionType::NoneAction: @@ -286,6 +306,8 @@ bool FrontendOptions::canActionEmitObjCHeader(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -319,6 +341,8 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::ResolveImports: case ActionType::Typecheck: @@ -358,6 +382,8 @@ bool FrontendOptions::canActionEmitModule(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::MergeModules: case ActionType::EmitModuleOnly: @@ -398,6 +424,8 @@ bool FrontendOptions::canActionEmitInterface(ActionType action) { case ActionType::CompileModuleFromInterface: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Typecheck: case ActionType::MergeModules: @@ -439,6 +467,8 @@ bool FrontendOptions::doesActionProduceOutput(ActionType action) { case ActionType::MergeModules: case ActionType::CompileModuleFromInterface: case ActionType::DumpTypeInfo: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return true; case ActionType::NoneAction: @@ -462,6 +492,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::EmitObject: case ActionType::Immediate: case ActionType::REPL: + case ActionType::EmitPCM: return false; case ActionType::Parse: @@ -480,6 +511,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::EmitAssembly: case ActionType::EmitIR: case ActionType::DumpTypeInfo: + case ActionType::DumpPCM: return true; } llvm_unreachable("unhandled action"); @@ -501,6 +533,8 @@ bool FrontendOptions::doesActionGenerateSIL(ActionType action) { case ActionType::EmitImportedModules: case ActionType::EmitPCH: case ActionType::CompileModuleFromInterface: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::EmitSILGen: case ActionType::EmitSIBGen: @@ -543,6 +577,8 @@ bool FrontendOptions::doesActionGenerateIR(ActionType action) { case ActionType::EmitSIBGen: case ActionType::EmitSIB: case ActionType::EmitImportedModules: + case ActionType::EmitPCM: + case ActionType::DumpPCM: return false; case ActionType::Immediate: case ActionType::REPL: diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index f1bfcea821bc6..2f7a40f255c21 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Regex.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/LockFileManager.h" using namespace swift; using FileDependency = SerializationOptions::FileDependency; @@ -240,7 +241,7 @@ bool ModuleInterfaceBuilder::collectDepsForSerialization( return false; } -bool ModuleInterfaceBuilder::buildSwiftModule( +bool ModuleInterfaceBuilder::buildSwiftModuleInternal( StringRef OutPath, bool ShouldSerializeDeps, std::unique_ptr *ModuleBuffer) { bool SubError = false; @@ -348,6 +349,8 @@ bool ModuleInterfaceBuilder::buildSwiftModule( std::string OutPathStr = OutPath; SerializationOpts.OutputPath = OutPathStr.c_str(); SerializationOpts.ModuleLinkName = FEOpts.ModuleLinkName; + SerializationOpts.AutolinkForceLoad = + !subInvocation.getIRGenOptions().ForceLoadSymbolName.empty(); // Record any non-SDK module interface files for the debug info. StringRef SDKPath = SubInstance.getASTContext().SearchPathOpts.SDKPath; @@ -382,3 +385,71 @@ bool ModuleInterfaceBuilder::buildSwiftModule( }); return !RunSuccess || SubError; } + +bool ModuleInterfaceBuilder::buildSwiftModule(StringRef OutPath, + bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild) { + + while (1) { + // Attempt to lock the interface file. Only one process is allowed to build + // module from the interface so we don't consume too much memory when multiple + // processes are doing the same. + // FIXME: We should surface the module building step to the build system so + // we don't need to synchronize here. + llvm::LockFileManager Locked(interfacePath); + switch (Locked) { + case llvm::LockFileManager::LFS_Error:{ + // ModuleInterfaceBuilder takes care of correctness and locks are only + // necessary for performance. Fallback to building the module in case of any lock + // related errors. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_failure, + interfacePath); + } + // Clear out any potential leftover. + Locked.unsafeRemoveLockFile(); + LLVM_FALLTHROUGH; + } + case llvm::LockFileManager::LFS_Owned: { + if (RemarkRebuild) { + RemarkRebuild(); + } + return buildSwiftModuleInternal(OutPath, ShouldSerializeDeps, ModuleBuffer); + } + case llvm::LockFileManager::LFS_Shared: { + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: { + // This process may have a different module output path. If the other + // process doesn't build the interface to this output path, we should try + // building ourselves. + auto bufferOrError = llvm::MemoryBuffer::getFile(OutPath); + if (!bufferOrError) + continue; + if (ModuleBuffer) + *ModuleBuffer = std::move(bufferOrError.get()); + return false; + } + case llvm::LockFileManager::Res_OwnerDied: { + continue; // try again to get the lock. + } + case llvm::LockFileManager::Res_Timeout: { + // Since ModuleInterfaceBuilder takes care of correctness, we try waiting for + // another process to complete the build so swift does not do it done + // twice. If case of timeout, build it ourselves. + if (RemarkRebuild) { + diags.diagnose(SourceLoc(), diag::interface_file_lock_timed_out, + interfacePath); + } + // Clear the lock file so that future invocations can make progress. + Locked.unsafeRemoveLockFile(); + continue; + } + } + break; + } + } + } +} diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index f8f096a39538a..b5de3c64b8a8e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -67,6 +67,8 @@ class ModuleInterfaceBuilder { version::Version &Vers, llvm::StringSaver &SubArgSaver, SmallVectorImpl &SubArgs); + bool buildSwiftModuleInternal(StringRef OutPath, bool ShouldSerializeDeps, + std::unique_ptr *ModuleBuffer); public: ModuleInterfaceBuilder(SourceManager &sourceMgr, DiagnosticEngine &diags, const SearchPathOptions &searchPathOpts, @@ -102,7 +104,8 @@ class ModuleInterfaceBuilder { } bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps, - std::unique_ptr *ModuleBuffer); + std::unique_ptr *ModuleBuffer, + llvm::function_ref RemarkRebuild = nullptr); }; } // end namespace swift diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index e734a514d8195..9c925a58cda04 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -340,7 +340,7 @@ struct ModuleRebuildInfo { /// normal cache, the prebuilt cache, a module adjacent to the interface, or /// a module that we'll build from a module interface. class ModuleInterfaceLoaderImpl { - using AccessPathElem = std::pair; + using AccessPathElem = Located; friend class swift::ModuleInterfaceLoader; ASTContext &ctx; llvm::vfs::FileSystem &fs; @@ -764,6 +764,7 @@ class ModuleInterfaceLoaderImpl { } } + // [Note: ModuleInterfaceLoader-defer-to-SerializedModuleLoader] // Finally, if there's a module adjacent to the .swiftinterface that we can // _likely_ load (it validates OK and is up to date), bail early with // errc::not_supported, so the next (serialized) loader in the chain will @@ -949,13 +950,11 @@ class ModuleInterfaceLoaderImpl { std::unique_ptr moduleBuffer; // We didn't discover a module corresponding to this interface. - // Diagnose that we didn't find a loadable module, if we were asked to. - if (remarkOnRebuildFromInterface) { + auto remarkRebuild = [&]() { rebuildInfo.diagnose(ctx, diagnosticLoc, moduleName, interfacePath); - } - + }; // If we found an out-of-date .swiftmodule, we still want to add it as // a dependency of the .swiftinterface. That way if it's updated, but // the .swiftinterface remains the same, we invalidate the cache and @@ -965,7 +964,9 @@ class ModuleInterfaceLoaderImpl { builder.addExtraDependency(modulePath); if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true, - &moduleBuffer)) + &moduleBuffer, + remarkOnRebuildFromInterface ? remarkRebuild: + llvm::function_ref())) return std::make_error_code(std::errc::invalid_argument); assert(moduleBuffer && @@ -986,9 +987,9 @@ bool ModuleInterfaceLoader::isCached(StringRef DepPath) { /// cache or by converting it in a subordinate \c CompilerInstance, caching /// the results. std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, - StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) { @@ -997,16 +998,12 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( // should not have been constructed at all. assert(LoadMode != ModuleLoadingMode::OnlySerialized); - auto &fs = *Ctx.SourceMgr.getFileSystem(); - llvm::SmallString<256> ModPath, InPath; + llvm::SmallString<256> + ModPath{ BaseName.getName(file_types::TY_SwiftModuleFile) }, + InPath{ BaseName.getName(file_types::TY_SwiftModuleInterfaceFile) }; // First check to see if the .swiftinterface exists at all. Bail if not. - ModPath = DirPath; - path::append(ModPath, ModuleFilename); - - auto Ext = file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile); - InPath = ModPath; - path::replace_extension(InPath, Ext); + auto &fs = *Ctx.SourceMgr.getFileSystem(); if (!fs.exists(InPath)) { if (fs.exists(ModPath)) { LLVM_DEBUG(llvm::dbgs() @@ -1018,10 +1015,10 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( } // Create an instance of the Impl to do the heavy lifting. - auto ModuleName = ModuleID.first.str(); + auto ModuleName = ModuleID.Item.str(); ModuleInterfaceLoaderImpl Impl( Ctx, ModPath, InPath, ModuleName, - CacheDir, PrebuiltCacheDir, ModuleID.second, + CacheDir, PrebuiltCacheDir, ModuleID.Loc, RemarkOnRebuildFromInterface, dependencyTracker, llvm::is_contained(PreferInterfaceForModules, ModuleName) ? @@ -1035,19 +1032,19 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory( if (ModuleBuffer) { *ModuleBuffer = std::move(*ModuleBufferOrErr); + if (ModuleInterfacePath) + *ModuleInterfacePath = InPath; } + // Open .swiftsourceinfo file if it's present. - SerializedModuleLoaderBase::openModuleSourceInfoFileIfPresent(ModuleID, - ModPath, - ModuleSourceInfoFilename, - ModuleSourceInfoBuffer); + if (auto SourceInfoError = openModuleSourceInfoFileIfPresent(ModuleID, + BaseName, + ModuleSourceInfoBuffer)) + return SourceInfoError; + // Delegate back to the serialized module loader to load the module doc. - llvm::SmallString<256> DocPath{DirPath}; - path::append(DocPath, ModuleDocFilename); - auto DocLoadErr = - SerializedModuleLoaderBase::openModuleDocFile(ModuleID, DocPath, - ModuleDocBuffer); - if (DocLoadErr) + if (auto DocLoadErr = openModuleDocFileIfPresent(ModuleID, BaseName, + ModuleDocBuffer)) return DocLoadErr; return std::error_code(); diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 7fa40c3a94b5f..3369917df62b9 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -49,7 +49,7 @@ static void diagnoseScopedImports(DiagnosticEngine &diags, for (const ModuleDecl::ImportedModule &importPair : imports) { if (importPair.first.empty()) continue; - diags.diagnose(importPair.first.front().second, + diags.diagnose(importPair.first.front().Loc, diag::module_interface_scoped_import_unsupported); } } @@ -119,7 +119,7 @@ static void printImports(raw_ostream &out, ModuleDecl *M) { if (!import.first.empty()) { out << "/*"; for (const auto &accessPathElem : import.first) - out << "." << accessPathElem.first; + out << "." << accessPathElem.Item; out << "*/"; } @@ -440,7 +440,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten); + Opts.PreserveTypesAsWritten, Opts.PrintFullConvention); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 0cba82aab26c1..87736c550b22e 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -63,53 +63,48 @@ namespace { }; } // end anonymous namespace -void PrintingDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { +void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) { if (Info.IsChildNote) return; - printDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info, - bufferIndirectlyCausingDiagnostic); + printDiagnostic(SM, Info); + for (auto path : Info.EducationalNotePaths) { + if (auto buffer = SM.getFileSystem()->getBufferForFile(path)) + Stream << buffer->get()->getBuffer() << "\n"; + } for (auto ChildInfo : Info.ChildDiagnosticInfo) { - printDiagnostic(SM, ChildInfo->Loc, ChildInfo->Kind, - ChildInfo->FormatString, ChildInfo->FormatArgs, *ChildInfo, - ChildInfo->BufferIndirectlyCausingDiagnostic); + printDiagnostic(SM, *ChildInfo); } } -void PrintingDiagnosticConsumer::printDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { +void PrintingDiagnosticConsumer::printDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) { // Determine what kind of diagnostic we're emitting. llvm::SourceMgr::DiagKind SMKind; - switch (Kind) { - case DiagnosticKind::Error: - SMKind = llvm::SourceMgr::DK_Error; - break; - case DiagnosticKind::Warning: - SMKind = llvm::SourceMgr::DK_Warning; - break; - - case DiagnosticKind::Note: - SMKind = llvm::SourceMgr::DK_Note; - break; - - case DiagnosticKind::Remark: - SMKind = llvm::SourceMgr::DK_Remark; - break; + switch (Info.Kind) { + case DiagnosticKind::Error: + SMKind = llvm::SourceMgr::DK_Error; + break; + case DiagnosticKind::Warning: + SMKind = llvm::SourceMgr::DK_Warning; + break; + + case DiagnosticKind::Note: + SMKind = llvm::SourceMgr::DK_Note; + break; + + case DiagnosticKind::Remark: + SMKind = llvm::SourceMgr::DK_Remark; + break; } - if (Kind == DiagnosticKind::Error) { + if (Info.Kind == DiagnosticKind::Error) { DidErrorOccur = true; } - + // Translate ranges. SmallVector Ranges; for (auto R : Info.Ranges) @@ -129,10 +124,11 @@ void PrintingDiagnosticConsumer::printDiagnostic( llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); - DiagnosticEngine::formatDiagnosticText(Out, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString, + Info.FormatArgs); } - auto Msg = SM.GetMessage(Loc, SMKind, Text, Ranges, FixIts); + auto Msg = SM.GetMessage(Info.Loc, SMKind, Text, Ranges, FixIts); rawSM.PrintMessage(out, Msg, ForceColors); } diff --git a/lib/Frontend/SerializedDiagnosticConsumer.cpp b/lib/Frontend/SerializedDiagnosticConsumer.cpp index e46badadfa550..001063e817f90 100644 --- a/lib/Frontend/SerializedDiagnosticConsumer.cpp +++ b/lib/Frontend/SerializedDiagnosticConsumer.cpp @@ -28,7 +28,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitstreamWriter.h" // For constant values only. #include "clang/Frontend/SerializedDiagnosticPrinter.h" @@ -191,11 +191,7 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer { CompilationWasComplete = false; } - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; /// The version of the diagnostics file. enum { Version = 1 }; @@ -543,15 +539,12 @@ emitDiagnosticMessage(SourceManager &SM, } void SerializedDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { + SourceManager &SM, const DiagnosticInfo &Info) { // Enter the block for a non-note diagnostic immediately, rather // than waiting for beginDiagnostic, in case associated notes // are emitted before we get there. - if (Kind != DiagnosticKind::Note) { + if (Info.Kind != DiagnosticKind::Note) { if (State->EmittedAnyDiagBlocks) exitDiagBlock(); @@ -561,7 +554,7 @@ void SerializedDiagnosticConsumer::handleDiagnostic( // Special-case diagnostics with no location. // Make sure we bracket all notes as "sub-diagnostics". - bool bracketDiagnostic = (Kind == DiagnosticKind::Note); + bool bracketDiagnostic = (Info.Kind == DiagnosticKind::Note); if (bracketDiagnostic) enterDiagBlock(); @@ -570,10 +563,11 @@ void SerializedDiagnosticConsumer::handleDiagnostic( llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); - DiagnosticEngine::formatDiagnosticText(Out, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString, + Info.FormatArgs); } - - emitDiagnosticMessage(SM, Loc, Kind, Text, Info); + + emitDiagnosticMessage(SM, Info.Loc, Info.Kind, Text, Info); if (bracketDiagnostic) exitDiagBlock(); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 7596ee59aa885..02386f37e7102 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -28,8 +28,8 @@ #include "swift/Subsystems.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/DiagnosticsSema.h" -#include "swift/AST/ExperimentalDependencies.h" #include "swift/AST/FileSystem.h" +#include "swift/AST/FineGrainedDependencies.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/NameLookup.h" @@ -42,6 +42,7 @@ #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/LLVMContext.h" #include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/Platform.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" @@ -237,12 +238,156 @@ template <> struct ObjectTraits { } } +/// Compute the per-module information to be recorded in the trace file. +// +// The most interesting/tricky thing here is _which_ paths get recorded in +// the trace file as dependencies. It depends on how the module was synthesized. +// The key points are: +// +// 1. Paths to swiftmodules in the module cache or in the prebuilt cache are not +// recorded - Precondition: the corresponding path to the swiftinterface must +// already be present as a key in pathToModuleDecl. +// 2. swiftmodules next to a swiftinterface are saved if they are up-to-date. +// +// FIXME: Use the VFS instead of handling paths directly. We are particularly +// sloppy about handling relative paths in the dependency tracker. +static void computeSwiftModuleTraceInfo( + const SmallPtrSetImpl &importedModules, + const llvm::DenseMap &pathToModuleDecl, + const DependencyTracker &depTracker, + StringRef prebuiltCachePath, + std::vector &traceInfo) { + + SmallString<256> buffer; + + std::string errMsg; + llvm::raw_string_ostream err(errMsg); + + // FIXME: Use PrettyStackTrace instead. + auto errorUnexpectedPath = + [&pathToModuleDecl](llvm::raw_string_ostream &errStream) { + errStream << "The module <-> path mapping we have is:\n"; + for (auto &m: pathToModuleDecl) + errStream << m.second->getName() << " <-> " << m.first << '\n'; + llvm::report_fatal_error(errStream.str()); + }; + + using namespace llvm::sys; + + auto computeAdjacentInterfacePath = [](SmallVectorImpl &modPath) { + auto swiftInterfaceExt = + file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile); + path::replace_extension(modPath, swiftInterfaceExt); + }; + + for (auto &depPath : depTracker.getDependencies()) { + + // Decide if this is a swiftmodule based on the extension of the raw + // dependency path, as the true file may have a different one. + // For example, this might happen when the canonicalized path points to + // a Content Addressed Storage (CAS) location. + auto moduleFileType = + file_types::lookupTypeForExtension(path::extension(depPath)); + auto isSwiftmodule = + moduleFileType == file_types::TY_SwiftModuleFile; + auto isSwiftinterface = + moduleFileType == file_types::TY_SwiftModuleInterfaceFile; + + if (!(isSwiftmodule || isSwiftinterface)) + continue; + + auto dep = pathToModuleDecl.find(depPath); + if (dep != pathToModuleDecl.end()) { + // Great, we recognize the path! Check if the file is still around. + + ModuleDecl *depMod = dep->second; + if(depMod->isResilient() && !isSwiftinterface) { + // FIXME: Ideally, we would check that the swiftmodule has a + // swiftinterface next to it. Tracked by rdar://problem/56351399. + } + + // FIXME: Better error handling + StringRef realDepPath + = fs::real_path(depPath, buffer, /*expand_tile*/true) + ? StringRef(depPath) // Couldn't find the canonical path, assume + // this is good enough. + : buffer.str(); + + traceInfo.push_back({ + /*Name=*/ + depMod->getName(), + /*Path=*/ + realDepPath, + // TODO: There is an edge case which is not handled here. + // When we build a framework using -import-underlying-module, or an + // app/test using -import-objc-header, we should look at the direct + // imports of the bridging modules, and mark those as our direct + // imports. + /*IsImportedDirectly=*/ + importedModules.find(depMod) != importedModules.end(), + /*SupportsLibraryEvolution=*/ + depMod->isResilient() + }); + buffer.clear(); + + continue; + } + + // If the depTracker had an interface, that means that we must've + // built a swiftmodule from that interface, so we should have that + // filename available. + if (isSwiftinterface) { + err << "Unexpected path for swiftinterface file:\n" << depPath << "\n"; + errorUnexpectedPath(err); + } + + // Skip cached modules in the prebuilt cache. We will add the corresponding + // swiftinterface from the SDK directly, but this isn't checked. :-/ + // + // FIXME: This is incorrect if both paths are not relative w.r.t. to the + // same root. + if (StringRef(depPath).startswith(prebuiltCachePath)) + continue; + + // If we have a swiftmodule next to an interface, that interface path will + // be saved (not checked), so don't save the path to this swiftmodule. + SmallString<256> moduleAdjacentInterfacePath(depPath); + computeAdjacentInterfacePath(moduleAdjacentInterfacePath); + if (pathToModuleDecl.find(moduleAdjacentInterfacePath) + != pathToModuleDecl.end()) + continue; + + // FIXME: The behavior of fs::exists for relative paths is undocumented. + // Use something else instead? + if (fs::exists(moduleAdjacentInterfacePath)) { + // This should be an error but it is not because of funkiness around + // compatible modules such as us having both armv7s.swiftinterface + // and armv7.swiftinterface in the dependency tracker. + continue; + } + buffer.clear(); + + // We might land here when we have a arm.swiftmodule in the cache path + // which added a dependency on a arm.swiftinterface (which was not loaded). + } + + // Almost a re-implementation of reversePathSortedFilenames :(. + std::sort( + traceInfo.begin(), traceInfo.end(), + [](const SwiftModuleTraceInfo &m1, const SwiftModuleTraceInfo &m2) -> bool { + return std::lexicographical_compare( + m1.Path.rbegin(), m1.Path.rend(), + m2.Path.rbegin(), m2.Path.rend()); + }); +} + static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, DependencyTracker *depTracker, + StringRef prebuiltCachePath, StringRef loadedModuleTracePath) { ASTContext &ctxt = mainModule->getASTContext(); assert(!ctxt.hadError() - && "We may not be able to emit a proper trace if there was an error."); + && "We should've already exited earlier if there was an error."); if (loadedModuleTracePath.empty()) return false; @@ -269,67 +414,22 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, llvm::DenseMap pathToModuleDecl; for (auto &module : ctxt.LoadedModules) { ModuleDecl *loadedDecl = module.second; - assert(loadedDecl && "Expected loaded module to be non-null."); + if (!loadedDecl) + llvm::report_fatal_error("Expected loaded modules to be non-null."); if (loadedDecl == mainModule) continue; - assert(!loadedDecl->getModuleFilename().empty() - && ("Don't know how to handle modules with empty names." - " One potential reason for getting an empty module name might" - " be that the module could not be deserialized correctly.")); + if (loadedDecl->getModuleFilename().empty()) + llvm::report_fatal_error( + "Don't know how to handle modules with empty names." + " One potential reason for getting an empty module name might" + " be that the module could not be deserialized correctly."); pathToModuleDecl.insert( std::make_pair(loadedDecl->getModuleFilename(), loadedDecl)); } std::vector swiftModules; - SmallString<256> buffer; - for (auto &depPath : depTracker->getDependencies()) { - StringRef realDepPath; - // FIXME: appropriate error handling - if (llvm::sys::fs::real_path(depPath, buffer,/*expand_tilde=*/true)) - // Couldn't find the canonical path, so let's just assume the old one was - // canonical (enough). - realDepPath = depPath; - else - realDepPath = buffer.str(); - - // Decide if this is a swiftmodule based on the extension of the raw - // dependency path, as the true file may have a different one. - // For example, this might happen when the canonicalized path points to - // a Content Addressed Storage (CAS) location. - auto moduleFileType = - file_types::lookupTypeForExtension(llvm::sys::path::extension(depPath)); - if (moduleFileType == file_types::TY_SwiftModuleFile - || moduleFileType == file_types::TY_SwiftModuleInterfaceFile) { - auto dep = pathToModuleDecl.find(depPath); - assert(dep != pathToModuleDecl.end() - && "Dependency must've been loaded."); - ModuleDecl *depMod = dep->second; - swiftModules.push_back({ - /*Name=*/ - depMod->getName(), - /*Path=*/ - realDepPath, - // TODO: There is an edge case which is not handled here. - // When we build a framework using -import-underlying-module, or an - // app/test using -import-objc-header, we should look at the direct - // imports of the bridging modules, and mark those as our direct - // imports. - /*IsImportedDirectly=*/ - importedModules.find(depMod) != importedModules.end(), - /*SupportsLibraryEvolution=*/ - depMod->isResilient() - }); - } - } - - // Almost a re-implementation of reversePathSortedFilenames :(. - std::sort( - swiftModules.begin(), swiftModules.end(), - [](const SwiftModuleTraceInfo &m1, const SwiftModuleTraceInfo &m2) -> bool { - return std::lexicographical_compare( - m1.Path.rbegin(), m1.Path.rend(), - m2.Path.rbegin(), m2.Path.rend()); - }); + computeSwiftModuleTraceInfo(importedModules, pathToModuleDecl, *depTracker, + prebuiltCachePath, swiftModules); LoadedModuleTraceFormat trace = { /*version=*/LoadedModuleTraceFormat::CurrentVersion, @@ -339,8 +439,7 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, }; // raw_fd_ostream is unbuffered, and we may have multiple processes writing, - // so first write the whole thing into memory and dump out that buffer to the - // file. + // so first write to memory and then dump the buffer to the trace file. std::string stringBuffer; { llvm::raw_string_ostream memoryBuffer(stringBuffer); @@ -349,7 +448,6 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, json::jsonize(jsonOutput, trace, /*Required=*/true); } stringBuffer += "\n"; - out << stringBuffer; return true; @@ -362,7 +460,8 @@ emitLoadedModuleTraceForAllPrimariesIfNeeded(ModuleDecl *mainModule, return opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput( [&](const InputFile &input) -> bool { return emitLoadedModuleTraceIfNeeded( - mainModule, depTracker, input.loadedModuleTracePath()); + mainModule, depTracker, opts.PrebuiltModuleCachePath, + input.loadedModuleTracePath()); }); } @@ -383,8 +482,7 @@ getFileOutputStream(StringRef OutputFilename, ASTContext &Ctx) { } /// Writes the Syntax tree to the given file -static bool emitSyntax(SourceFile *SF, LangOptions &LangOpts, - SourceManager &SM, StringRef OutputFilename) { +static bool emitSyntax(SourceFile *SF, StringRef OutputFilename) { auto bufferID = SF->getBufferID(); assert(bufferID && "frontend should have a buffer ID " "for the main source file"); @@ -401,21 +499,20 @@ static bool emitSyntax(SourceFile *SF, LangOptions &LangOpts, } /// Writes SIL out to the given file. -static bool writeSIL(SILModule &SM, ModuleDecl *M, bool EmitVerboseSIL, - StringRef OutputFilename, bool SortSIL) { +static bool writeSIL(SILModule &SM, ModuleDecl *M, const SILOptions &Opts, + StringRef OutputFilename) { auto OS = getFileOutputStream(OutputFilename, M->getASTContext()); if (!OS) return true; - SM.print(*OS, EmitVerboseSIL, M, SortSIL); + SM.print(*OS, M, Opts); return M->getASTContext().hadError(); } static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, - CompilerInstance &Instance, - CompilerInvocation &Invocation) { - const FrontendOptions &opts = Invocation.getFrontendOptions(); - return writeSIL(SM, Instance.getMainModule(), opts.EmitVerboseSIL, - PSPs.OutputFilename, opts.EmitSortedSIL); + const CompilerInstance &Instance, + const SILOptions &Opts) { + return writeSIL(SM, Instance.getMainModule(), Opts, + PSPs.OutputFilename); } /// Prints the Objective-C "generated header" interface for \p M to \p @@ -470,25 +567,6 @@ printModuleInterfaceIfNeeded(StringRef outputPath, }); } -/// Returns the OutputKind for the given Action. -static IRGenOutputKind getOutputKind(FrontendOptions::ActionType Action) { - switch (Action) { - case FrontendOptions::ActionType::EmitIR: - return IRGenOutputKind::LLVMAssembly; - case FrontendOptions::ActionType::EmitBC: - return IRGenOutputKind::LLVMBitcode; - case FrontendOptions::ActionType::EmitAssembly: - return IRGenOutputKind::NativeAssembly; - case FrontendOptions::ActionType::EmitObject: - return IRGenOutputKind::ObjectFile; - case FrontendOptions::ActionType::Immediate: - return IRGenOutputKind::Module; - default: - llvm_unreachable("Unknown ActionType which requires IRGen"); - return IRGenOutputKind::ObjectFile; - } -} - namespace { /// If there is an error with fixits it writes the fixits as edits in json @@ -507,13 +585,9 @@ class JSONFixitWriter FixitAll(DiagOpts.FixitCodeForAllDiagnostics) {} private: - void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { - if (!(FixitAll || shouldTakeFixit(Kind, Info))) + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { + if (!(FixitAll || shouldTakeFixit(Info))) return; for (const auto &Fix : Info.FixIts) { AllEdits.push_back({SM, Fix.getRange(), Fix.getText()}); @@ -565,14 +639,14 @@ static void debugFailWithCrash() { /// \return true on error. static bool emitIndexDataIfNeeded(SourceFile *PrimarySourceFile, const CompilerInvocation &Invocation, - CompilerInstance &Instance); + const CompilerInstance &Instance); static void countStatsOfSourceFile(UnifiedStatsReporter &Stats, - CompilerInstance &Instance, + const CompilerInstance &Instance, SourceFile *SF) { auto &C = Stats.getFrontendCounters(); auto &SM = Instance.getSourceMgr(); - C.NumDecls += SF->Decls.size(); + C.NumDecls += SF->getTopLevelDecls().size(); C.NumLocalTypeDecls += SF->LocalTypeDecls.size(); C.NumObjCMethods += SF->ObjCMethods.size(); C.NumInfixOperators += SF->InfixOperators.size(); @@ -648,16 +722,13 @@ createOptRecordFile(StringRef Filename, DiagnosticEngine &DE) { return File; } -static bool precompileBridgingHeader(CompilerInvocation &Invocation, - CompilerInstance &Instance) { +static bool precompileBridgingHeader(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { auto clangImporter = static_cast( Instance.getASTContext().getClangModuleLoader()); auto &ImporterOpts = Invocation.getClangImporterOptions(); auto &PCHOutDir = ImporterOpts.PrecompiledHeaderOutputDir; if (!PCHOutDir.empty()) { - ImporterOpts.BridgingHeader = - Invocation.getFrontendOptions() - .InputsAndOutputs.getFilenameOfFirstInput(); // Create or validate a persistent PCH. auto SwiftPCHHash = Invocation.getPCHHash(); auto PCH = clangImporter->getOrCreatePCH(ImporterOpts, SwiftPCHHash); @@ -670,7 +741,30 @@ static bool precompileBridgingHeader(CompilerInvocation &Invocation, .InputsAndOutputs.getSingleOutputFilename()); } -static bool buildModuleFromInterface(CompilerInvocation &Invocation, +static bool precompileClangModule(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { + auto clangImporter = static_cast( + Instance.getASTContext().getClangModuleLoader()); + return clangImporter->emitPrecompiledModule( + Invocation.getFrontendOptions() + .InputsAndOutputs.getFilenameOfFirstInput(), + Invocation.getFrontendOptions().ModuleName, + Invocation.getFrontendOptions() + .InputsAndOutputs.getSingleOutputFilename()); +} + +static bool dumpPrecompiledClangModule(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { + auto clangImporter = static_cast( + Instance.getASTContext().getClangModuleLoader()); + return clangImporter->dumpPrecompiledModule( + Invocation.getFrontendOptions() + .InputsAndOutputs.getFilenameOfFirstInput(), + Invocation.getFrontendOptions() + .InputsAndOutputs.getSingleOutputFilename()); +} + +static bool buildModuleFromInterface(const CompilerInvocation &Invocation, CompilerInstance &Instance) { const FrontendOptions &FEOpts = Invocation.getFrontendOptions(); assert(FEOpts.InputsAndOutputs.hasSingleInput()); @@ -686,7 +780,7 @@ static bool buildModuleFromInterface(CompilerInvocation &Invocation, FEOpts.TrackSystemDeps, FEOpts.RemarkOnRebuildFromModuleInterface); } -static bool compileLLVMIR(CompilerInvocation &Invocation, +static bool compileLLVMIR(const CompilerInvocation &Invocation, CompilerInstance &Instance, UnifiedStatsReporter *Stats) { auto &LLVMContext = getGlobalLLVMContext(); @@ -722,18 +816,14 @@ static bool compileLLVMIR(CompilerInvocation &Invocation, Err.getMessage()); return true; } - IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); - // TODO: remove once the frontend understands what action it should perform - IRGenOpts.OutputKind = - getOutputKind(Invocation.getFrontendOptions().RequestedAction); - - return performLLVM(IRGenOpts, Instance.getASTContext(), Module.get(), + return performLLVM(Invocation.getIRGenOptions(), + Instance.getASTContext(), Module.get(), Invocation.getFrontendOptions() .InputsAndOutputs.getSingleOutputFilename(), Stats); } -static void verifyGenericSignaturesIfNeeded(CompilerInvocation &Invocation, +static void verifyGenericSignaturesIfNeeded(const CompilerInvocation &Invocation, ASTContext &Context) { auto verifyGenericSignaturesInModule = Invocation.getFrontendOptions().VerifyGenericSignaturesInModule; @@ -743,8 +833,8 @@ static void verifyGenericSignaturesIfNeeded(CompilerInvocation &Invocation, GenericSignatureBuilder::verifyGenericSignaturesInModule(module); } -static void dumpAndPrintScopeMap(CompilerInvocation &Invocation, - CompilerInstance &Instance, SourceFile *SF) { +static void dumpAndPrintScopeMap(const CompilerInvocation &Invocation, + const CompilerInstance &Instance, SourceFile *SF) { // Not const because may require reexpansion ASTScope &scope = SF->getScope(); @@ -762,8 +852,8 @@ static void dumpAndPrintScopeMap(CompilerInvocation &Invocation, } } -static SourceFile *getPrimaryOrMainSourceFile(CompilerInvocation &Invocation, - CompilerInstance &Instance) { +static SourceFile *getPrimaryOrMainSourceFile(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { SourceFile *SF = Instance.getPrimarySourceFile(); if (!SF) { SourceFileKind Kind = Invocation.getSourceFileKind(); @@ -774,12 +864,8 @@ static SourceFile *getPrimaryOrMainSourceFile(CompilerInvocation &Invocation, /// Dumps the AST of all available primary source files. If corresponding output /// files were specified, use them; otherwise, dump the AST to stdout. -static void dumpAST(CompilerInvocation &Invocation, +static void dumpAST(const CompilerInvocation &Invocation, CompilerInstance &Instance) { - // FIXME: WMO doesn't use the notion of primary files, so this doesn't do the - // right thing. Perhaps it'd be best to ignore WMO when dumping the AST, just - // like WMO ignores `-incremental`. - auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -800,10 +886,10 @@ static void dumpAST(CompilerInvocation &Invocation, /// CompilerInstance::performSema()), so dump or print the main source file and /// return. -static Optional dumpASTIfNeeded(CompilerInvocation &Invocation, +static Optional dumpASTIfNeeded(const CompilerInvocation &Invocation, CompilerInstance &Instance) { - FrontendOptions &opts = Invocation.getFrontendOptions(); - FrontendOptions::ActionType Action = opts.RequestedAction; + const FrontendOptions &opts = Invocation.getFrontendOptions(); + const FrontendOptions::ActionType Action = opts.RequestedAction; ASTContext &Context = Instance.getASTContext(); switch (Action) { default: @@ -832,7 +918,6 @@ static Optional dumpASTIfNeeded(CompilerInvocation &Invocation, case FrontendOptions::ActionType::EmitSyntax: emitSyntax(getPrimaryOrMainSourceFile(Invocation, Instance), - Invocation.getLangOptions(), Instance.getSourceMgr(), opts.InputsAndOutputs.getSingleOutputFilename()); break; @@ -849,7 +934,7 @@ static Optional dumpASTIfNeeded(CompilerInvocation &Invocation, } static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( - CompilerInvocation &Invocation, CompilerInstance &Instance) { + const CompilerInvocation &Invocation, CompilerInstance &Instance) { if (Invocation.getFrontendOptions() .InputsAndOutputs.hasReferenceDependenciesPath() && Instance.getPrimarySourceFiles().empty()) { @@ -862,10 +947,12 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( Invocation.getReferenceDependenciesFilePathForPrimary( SF->getFilename()); if (!referenceDependenciesFilePath.empty()) { - if (Invocation.getLangOptions().EnableExperimentalDependencies) - (void)experimental_dependencies::emitReferenceDependencies( + if (Invocation.getLangOptions().EnableFineGrainedDependencies) + (void)fine_grained_dependencies::emitReferenceDependencies( Instance.getASTContext().Diags, SF, - *Instance.getDependencyTracker(), referenceDependenciesFilePath); + *Instance.getDependencyTracker(), referenceDependenciesFilePath, + Invocation.getLangOptions() + .EmitFineGrainedDependencySourcefileDotFiles); else (void)emitReferenceDependencies(Instance.getASTContext().Diags, SF, *Instance.getDependencyTracker(), @@ -873,8 +960,45 @@ static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( } } } +static void +emitSwiftRangesForAllPrimaryInputsIfNeeded(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { + if (Invocation.getFrontendOptions().InputsAndOutputs.hasSwiftRangesPath() && + Instance.getPrimarySourceFiles().empty()) { + Instance.getASTContext().Diags.diagnose( + SourceLoc(), diag::emit_swift_ranges_without_primary_file); + return; + } + for (auto *SF : Instance.getPrimarySourceFiles()) { + const std::string &swiftRangesFilePath = + Invocation.getSwiftRangesFilePathForPrimary(SF->getFilename()); + if (!swiftRangesFilePath.empty()) { + (void)Instance.emitSwiftRanges(Instance.getASTContext().Diags, SF, + swiftRangesFilePath); + } + } +} +static void +emitCompiledSourceForAllPrimaryInputsIfNeeded(const CompilerInvocation &Invocation, + const CompilerInstance &Instance) { + if (Invocation.getFrontendOptions() + .InputsAndOutputs.hasCompiledSourcePath() && + Instance.getPrimarySourceFiles().empty()) { + Instance.getASTContext().Diags.diagnose( + SourceLoc(), diag::emit_compiled_source_without_primary_file); + return; + } + for (auto *SF : Instance.getPrimarySourceFiles()) { + const std::string &compiledSourceFilePath = + Invocation.getCompiledSourceFilePathForPrimary(SF->getFilename()); + if (!compiledSourceFilePath.empty()) { + (void)Instance.emitCompiledSource(Instance.getASTContext().Diags, SF, + compiledSourceFilePath); + } + } +} -static bool writeTBDIfNeeded(CompilerInvocation &Invocation, +static bool writeTBDIfNeeded(const CompilerInvocation &Invocation, CompilerInstance &Instance) { const auto &frontendOpts = Invocation.getFrontendOptions(); const auto &tbdOpts = Invocation.getTBDGenOptions(); @@ -892,15 +1016,73 @@ static bool writeTBDIfNeeded(CompilerInvocation &Invocation, return writeTBD(Instance.getMainModule(), TBDPath, tbdOpts); } +static std::string changeToLdAdd(StringRef ldHide) { + SmallString<64> SymbolBuffer; + llvm::raw_svector_ostream OS(SymbolBuffer); + auto Parts = ldHide.split("$hide$"); + assert(!Parts.first.empty()); + assert(!Parts.second.empty()); + OS << Parts.first << "$add$" << Parts.second; + return OS.str().str(); +} + +static bool writeLdAddCFileIfNeeded(const CompilerInvocation &Invocation, + CompilerInstance &Instance) { + auto frontendOpts = Invocation.getFrontendOptions(); + if (!frontendOpts.InputsAndOutputs.isWholeModule()) + return false; + auto Path = Invocation.getLdAddCFileOutputPathForWholeModule(); + if (Path.empty()) + return false; + if (!frontendOpts.InputsAndOutputs.isWholeModule()) { + Instance.getDiags().diagnose(SourceLoc(), + diag::tbd_only_supported_in_whole_module); + return true; + } + if (!Invocation.getTBDGenOptions().ModuleInstallNameMapPath.empty()) { + Instance.getDiags().diagnose(SourceLoc(), + diag::linker_directives_choice_confusion); + return true; + } + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + llvm::StringSet<> ldSymbols; + auto *module = Instance.getMainModule(); + enumeratePublicSymbols(module, ldSymbols, tbdOpts); + std::error_code EC; + llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); + if (EC) { + module->getASTContext().Diags.diagnose(SourceLoc(), + diag::error_opening_output, + Path, EC.message()); + return true; + } + OS << "// Automatically generated C source file from the Swift compiler \n" + << "// to add removed symbols back to the high-level framework for deployment\n" + << "// targets prior to the OS version when these symbols were moved to\n" + << "// a low-level framework " << module->getName().str() << ".\n\n"; + unsigned Idx = 0; + for (auto &S: ldSymbols) { + SmallString<32> NameBuffer; + llvm::raw_svector_ostream NameOS(NameBuffer); + NameOS << "ldAdd_" << Idx; + OS << "extern const char " << NameOS.str() << " __asm(\"" << + changeToLdAdd(S.getKey()) << "\");\n"; + OS << "const char " << NameOS.str() << " = 0;\n"; + ++ Idx; + } + return false; +} + static bool performCompileStepsPostSILGen( - CompilerInstance &Instance, CompilerInvocation &Invocation, + CompilerInstance &Instance, const CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, ModuleOrSourceFile MSF, const PrimarySpecificPaths &PSPs, bool moduleIsPublic, int &ReturnValue, FrontendObserver *observer, UnifiedStatsReporter *Stats); static bool -performCompileStepsPostSema(CompilerInvocation &Invocation, +performCompileStepsPostSema(const CompilerInvocation &Invocation, CompilerInstance &Instance, bool moduleIsPublic, int &ReturnValue, FrontendObserver *observer, @@ -915,8 +1097,8 @@ performCompileStepsPostSema(CompilerInvocation &Invocation, ReturnValue, observer, Stats); } - SILOptions &SILOpts = Invocation.getSILOptions(); - FrontendOptions &opts = Invocation.getFrontendOptions(); + const SILOptions &SILOpts = Invocation.getSILOptions(); + const FrontendOptions &opts = Invocation.getFrontendOptions(); auto fileIsSIB = [](const FileUnit *File) -> bool { auto SASTF = dyn_cast(File); return SASTF && SASTF->isSIB(); @@ -975,7 +1157,7 @@ performCompileStepsPostSema(CompilerInvocation &Invocation, /// Emits index data for all primary inputs, or the main module. static bool -emitIndexData(CompilerInvocation &Invocation, CompilerInstance &Instance) { +emitIndexData(const CompilerInvocation &Invocation, const CompilerInstance &Instance) { bool hadEmitIndexDataError = false; if (Instance.getPrimarySourceFiles().empty()) return emitIndexDataIfNeeded(nullptr, Invocation, Instance); @@ -992,7 +1174,7 @@ emitIndexData(CompilerInvocation &Invocation, CompilerInstance &Instance) { /// `-typecheck`, but skipped for any mode that runs SIL diagnostics if there's /// an error found there (to get those diagnostics back to the user faster). static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( - CompilerInstance &Instance, CompilerInvocation &Invocation, + CompilerInstance &Instance, const CompilerInvocation &Invocation, bool moduleIsPublic) { const FrontendOptions &opts = Invocation.getFrontendOptions(); @@ -1017,6 +1199,9 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( { hadAnyError |= writeTBDIfNeeded(Invocation, Instance); } + { + hadAnyError |= writeLdAddCFileIfNeeded(Invocation, Instance); + } return hadAnyError; } @@ -1026,7 +1211,7 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( /// mode is NoVerify and there were no errors. /// \returns true on error static bool performCompile(CompilerInstance &Instance, - CompilerInvocation &Invocation, + const CompilerInvocation &Invocation, ArrayRef Args, int &ReturnValue, FrontendObserver *observer, @@ -1039,10 +1224,14 @@ static bool performCompile(CompilerInstance &Instance, Instance.getASTContext().LangOpts.VerifySyntaxTree = true; } - // We've been asked to precompile a bridging header; we want to + // We've been asked to precompile a bridging header or module; we want to // avoid touching any other inputs and just parse, emit and exit. if (Action == FrontendOptions::ActionType::EmitPCH) return precompileBridgingHeader(Invocation, Instance); + if (Action == FrontendOptions::ActionType::EmitPCM) + return precompileClangModule(Invocation, Instance); + if (Action == FrontendOptions::ActionType::DumpPCM) + return dumpPrecompiledClangModule(Invocation, Instance); if (Action == FrontendOptions::ActionType::CompileModuleFromInterface) return buildModuleFromInterface(Invocation, Instance); @@ -1051,12 +1240,12 @@ static bool performCompile(CompilerInstance &Instance, return compileLLVMIR(Invocation, Instance, Stats); if (FrontendOptions::shouldActionOnlyParse(Action)) { - bool ParseDelayedDeclListsOnEnd = - Action == FrontendOptions::ActionType::DumpParse || - Invocation.getDiagnosticOptions().VerifyMode != DiagnosticOptions::NoVerify; + // Disable delayed parsing of type and function bodies when we've been + // asked to dump the resulting AST. + bool CanDelayBodies = Action != FrontendOptions::ActionType::DumpParse; Instance.performParseOnly(/*EvaluateConditionals*/ Action == FrontendOptions::ActionType::EmitImportedModules, - ParseDelayedDeclListsOnEnd); + CanDelayBodies); } else if (Action == FrontendOptions::ActionType::ResolveImports) { Instance.performParseAndResolveImportsOnly(); } else { @@ -1105,6 +1294,8 @@ static bool performCompile(CompilerInstance &Instance, Context.getClangModuleLoader()->printStatistics(); emitReferenceDependenciesForAllPrimaryInputsIfNeeded(Invocation, Instance); + emitSwiftRangesForAllPrimaryInputsIfNeeded(Invocation, Instance); + emitCompiledSourceForAllPrimaryInputsIfNeeded(Invocation, Instance); if (Context.hadError()) { // Emit the index store data even if there were compiler errors. @@ -1151,23 +1342,8 @@ static bool performCompile(CompilerInstance &Instance, ReturnValue, observer, Stats); } -/// Get the main source file's private discriminator and attach it to -/// the compile unit's flags. -static void setPrivateDiscriminatorIfNeeded(IRGenOptions &IRGenOpts, - ModuleOrSourceFile MSF) { - if (IRGenOpts.DebugInfoLevel == IRGenDebugInfoLevel::None || - !MSF.is()) - return; - Identifier PD = MSF.get()->getPrivateDiscriminator(); - if (!PD.empty()) { - if (!IRGenOpts.DebugFlags.empty()) - IRGenOpts.DebugFlags += " "; - IRGenOpts.DebugFlags += ("-private-discriminator " + PD.str()).str(); - } -} - static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, - ASTContext &Context, ModuleOrSourceFile MSF) { + const ASTContext &Context, ModuleOrSourceFile MSF) { const std::string &moduleOutputPath = PSPs.SupplementaryOutputs.ModuleOutputPath; assert(!moduleOutputPath.empty() && "must have an output path"); @@ -1181,53 +1357,62 @@ static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, return Context.hadError(); } -static void generateIR(IRGenOptions &IRGenOpts, std::unique_ptr SM, +static void generateIR(const IRGenOptions &IRGenOpts, + std::unique_ptr SM, const PrimarySpecificPaths &PSPs, StringRef OutputFilename, ModuleOrSourceFile MSF, std::unique_ptr &IRModule, llvm::GlobalVariable *&HashGlobal, - ArrayRef parallelOutputFilenames) { + ArrayRef parallelOutputFilenames, + llvm::StringSet<> &LinkerDirectives) { // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto &LLVMContext = getGlobalLLVMContext(); IRModule = MSF.is() ? performIRGeneration(IRGenOpts, *MSF.get(), std::move(SM), OutputFilename, PSPs, - LLVMContext, &HashGlobal) + MSF.get()->getPrivateDiscriminator().str(), + LLVMContext, &HashGlobal, + &LinkerDirectives) : performIRGeneration(IRGenOpts, MSF.get(), std::move(SM), OutputFilename, PSPs, LLVMContext, parallelOutputFilenames, - &HashGlobal); + &HashGlobal, &LinkerDirectives); } -static bool processCommandLineAndRunImmediately(CompilerInvocation &Invocation, +static bool processCommandLineAndRunImmediately(const CompilerInvocation &Invocation, CompilerInstance &Instance, - std::unique_ptr SM, + std::unique_ptr &&SM, ModuleOrSourceFile MSF, FrontendObserver *observer, int &ReturnValue) { - FrontendOptions &opts = Invocation.getFrontendOptions(); + const FrontendOptions &opts = Invocation.getFrontendOptions(); assert(!MSF.is() && "-i doesn't work in -primary-file mode"); - IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); - IRGenOpts.UseJIT = true; - IRGenOpts.DebugInfoLevel = IRGenDebugInfoLevel::Normal; - IRGenOpts.DebugInfoFormat = IRGenDebugInfoFormat::DWARF; + const IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); const ProcessCmdLine &CmdLine = ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end()); - Instance.setSILModule(std::move(SM)); + + PrettyStackTraceStringAction trace( + "running user code", + MSF.is() ? MSF.get()->getFilename() + : MSF.get()->getModuleFilename()); ReturnValue = - RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions()); + RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions(), + std::move(SM)); return Instance.getASTContext().hadError(); } -static bool validateTBDIfNeeded(CompilerInvocation &Invocation, +static bool validateTBDIfNeeded(const CompilerInvocation &Invocation, ModuleOrSourceFile MSF, bool astGuaranteedToCorrespondToSIL, llvm::Module &IRModule) { if (!astGuaranteedToCorrespondToSIL || !inputFileKindCanHaveTBDValidated(Invocation.getInputKind())) return false; + + if (Invocation.getSILOptions().CrossModuleOptimization) + return false; const auto &frontendOpts = Invocation.getFrontendOptions(); auto mode = frontendOpts.ValidateTBDAgainstIR; @@ -1235,15 +1420,13 @@ static bool validateTBDIfNeeded(CompilerInvocation &Invocation, switch (mode) { case FrontendOptions::TBDValidationMode::Default: #ifndef NDEBUG - // When a debug compiler is targeting an apple platform, we do some - // validation by default. - if (Invocation.getLangOptions().Target.getVendor() == llvm::Triple::Apple) { - mode = FrontendOptions::TBDValidationMode::MissingFromTBD; - break; - } -#endif + // With a debug compiler, we do some validation by default. + mode = FrontendOptions::TBDValidationMode::MissingFromTBD; + break; +#else // Otherwise, the default is to do nothing. LLVM_FALLTHROUGH; +#endif case FrontendOptions::TBDValidationMode::None: return false; case FrontendOptions::TBDValidationMode::All: @@ -1259,7 +1442,7 @@ static bool validateTBDIfNeeded(CompilerInvocation &Invocation, Invocation.getTBDGenOptions(), allSymbols); } -static bool generateCode(CompilerInvocation &Invocation, +static bool generateCode(const CompilerInvocation &Invocation, CompilerInstance &Instance, StringRef OutputFilename, llvm::Module *IRModule, llvm::GlobalVariable *HashGlobal, @@ -1289,8 +1472,19 @@ static bool generateCode(CompilerInvocation &Invocation, EffectiveLanguageVersion, OutputFilename, Stats); } +static void collectLinkerDirectives(const CompilerInvocation &Invocation, + ModuleOrSourceFile MSF, + llvm::StringSet<> &Symbols) { + auto tbdOpts = Invocation.getTBDGenOptions(); + tbdOpts.LinkerDirectivesOnly = true; + if (MSF.is()) + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); + else + enumeratePublicSymbols(MSF.get(), Symbols, tbdOpts); +} + static bool performCompileStepsPostSILGen( - CompilerInstance &Instance, CompilerInvocation &Invocation, + CompilerInstance &Instance, const CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, ModuleOrSourceFile MSF, const PrimarySpecificPaths &PSPs, bool moduleIsPublic, int &ReturnValue, FrontendObserver *observer, @@ -1298,9 +1492,9 @@ static bool performCompileStepsPostSILGen( FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions::ActionType Action = opts.RequestedAction; - ASTContext &Context = Instance.getASTContext(); - SILOptions &SILOpts = Invocation.getSILOptions(); - IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); + const ASTContext &Context = Instance.getASTContext(); + const SILOptions &SILOpts = Invocation.getSILOptions(); + const IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); Optional ricd; if (auto *SF = MSF.dyn_cast()) @@ -1314,11 +1508,11 @@ static bool performCompileStepsPostSILGen( // We've been told to emit SIL after SILGen, so write it now. if (Action == FrontendOptions::ActionType::EmitSILGen) { - return writeSIL(*SM, PSPs, Instance, Invocation); + return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions()); } if (Action == FrontendOptions::ActionType::EmitSIBGen) { - serializeSIB(SM.get(), PSPs, Instance.getASTContext(), MSF); + serializeSIB(SM.get(), PSPs, Context, MSF); return Context.hadError(); } @@ -1361,10 +1555,8 @@ static bool performCompileStepsPostSILGen( emitAnyWholeModulePostTypeCheckSupplementaryOutputs(Instance, Invocation, moduleIsPublic); - setPrivateDiscriminatorIfNeeded(IRGenOpts, MSF); - if (Action == FrontendOptions::ActionType::EmitSIB) - return serializeSIB(SM.get(), PSPs, Instance.getASTContext(), MSF); + return serializeSIB(SM.get(), PSPs, Context, MSF); { if (PSPs.haveModuleOrModuleDocOutputPaths()) { @@ -1385,7 +1577,7 @@ static bool performCompileStepsPostSILGen( // We've been told to write canonical SIL, so write it now. if (Action == FrontendOptions::ActionType::EmitSIL) - return writeSIL(*SM, PSPs, Instance, Invocation); + return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions()); assert(Action >= FrontendOptions::ActionType::Immediate && "All actions not requiring IRGen must have been handled!"); @@ -1411,12 +1603,15 @@ static bool performCompileStepsPostSILGen( if (Action == FrontendOptions::ActionType::DumpTypeInfo) return performDumpTypeInfo(IRGenOpts, *SM, getGlobalLLVMContext()); - // TODO: remove once the frontend understands what action it should perform - IRGenOpts.OutputKind = getOutputKind(Action); if (Action == FrontendOptions::ActionType::Immediate) return processCommandLineAndRunImmediately( Invocation, Instance, std::move(SM), MSF, observer, ReturnValue); + llvm::StringSet<> LinkerDirectives; + collectLinkerDirectives(Invocation, MSF, LinkerDirectives); + // Don't proceed to IRGen if collecting linker directives failed. + if (Context.hadError()) + return true; StringRef OutputFilename = PSPs.OutputFilename; std::vector ParallelOutputFilenames = Invocation.getFrontendOptions().InputsAndOutputs.copyOutputFilenames(); @@ -1424,7 +1619,7 @@ static bool performCompileStepsPostSILGen( llvm::GlobalVariable *HashGlobal; generateIR( IRGenOpts, std::move(SM), PSPs, OutputFilename, MSF, IRModule, HashGlobal, - ParallelOutputFilenames); + ParallelOutputFilenames, LinkerDirectives); // Walk the AST for indexing after IR generation. Walking it before seems // to cause miscompilation issues. @@ -1451,7 +1646,7 @@ static bool performCompileStepsPostSILGen( static bool emitIndexDataIfNeeded(SourceFile *PrimarySourceFile, const CompilerInvocation &Invocation, - CompilerInstance &Instance) { + const CompilerInstance &Instance) { const FrontendOptions &opts = Invocation.getFrontendOptions(); if (opts.IndexStorePath.empty()) @@ -1699,6 +1894,74 @@ createJSONFixItDiagnosticConsumerIfNeeded( }); } +/// Print information about the selected target in JSON. +static void printTargetInfo(const CompilerInvocation &invocation, + llvm::raw_ostream &out) { + out << "{\n"; + + // Target information. + auto &langOpts = invocation.getLangOptions(); + out << " \"target\": {\n"; + + out << " \"triple\": \""; + out.write_escaped(langOpts.Target.getTriple()); + out << "\",\n"; + + out << " \"unversionedTriple\": \""; + out.write_escaped(getUnversionedTriple(langOpts.Target).getTriple()); + out << "\",\n"; + + out << " \"moduleTriple\": \""; + out.write_escaped(getTargetSpecificModuleTriple(langOpts.Target).getTriple()); + out << "\",\n"; + + if (auto runtimeVersion = getSwiftRuntimeCompatibilityVersionForTarget( + langOpts.Target)) { + out << " \"swiftRuntimeCompatibilityVersion\": \""; + out.write_escaped(runtimeVersion->getAsString()); + out << "\",\n"; + } + + out << " \"librariesRequireRPath\": " + << (tripleRequiresRPathForSwiftInOS(langOpts.Target) ? "true" : "false") + << "\n"; + + out << " },\n"; + + // Various paths. + auto &searchOpts = invocation.getSearchPathOptions(); + out << " \"paths\": {\n"; + + if (!searchOpts.SDKPath.empty()) { + out << " \"sdkPath\": \""; + out.write_escaped(searchOpts.SDKPath); + out << "\",\n"; + } + + auto outputPaths = [&](StringRef name, const std::vector &paths){ + out << " \"" << name << "\": [\n"; + interleave(paths, [&out](const std::string &path) { + out << " \""; + out.write_escaped(path); + out << "\""; + }, [&out] { + out << ",\n"; + }); + out << "\n ],\n"; + }; + + outputPaths("runtimeLibraryPaths", searchOpts.RuntimeLibraryPaths); + outputPaths("runtimeLibraryImportPaths", + searchOpts.RuntimeLibraryImportPaths); + + out << " \"runtimeResourcePath\": \""; + out.write_escaped(searchOpts.RuntimeResourcePath); + out << "\"\n"; + + out << " }\n"; + + out << "}\n"; +} int swift::performFrontend(ArrayRef Args, const char *Argv0, void *MainAddr, @@ -1724,13 +1987,15 @@ int swift::performFrontend(ArrayRef Args, SourceManager dummyMgr; - PDC.handleDiagnostic(dummyMgr, SourceLoc(), DiagnosticKind::Error, - "fatal error encountered during compilation; please " - "file a bug report with your project and the crash " - "log", - {}, DiagnosticInfo(), SourceLoc()); - PDC.handleDiagnostic(dummyMgr, SourceLoc(), DiagnosticKind::Note, reason, - {}, DiagnosticInfo(), SourceLoc()); + DiagnosticInfo errorInfo( + DiagID(0), SourceLoc(), DiagnosticKind::Error, + "fatal error encountered during compilation; please file a bug report " + "with your project and the crash log", + {}, SourceLoc(), {}, {}, {}, false); + DiagnosticInfo noteInfo(DiagID(0), SourceLoc(), DiagnosticKind::Note, + reason, {}, SourceLoc(), {}, {}, {}, false); + PDC.handleDiagnostic(dummyMgr, errorInfo); + PDC.handleDiagnostic(dummyMgr, noteInfo); if (shouldCrash) abort(); }; @@ -1821,6 +2086,11 @@ int swift::performFrontend(ArrayRef Args, return finishDiagProcessing(0); } + if (Invocation.getFrontendOptions().PrintTargetInfo) { + printTargetInfo(Invocation, llvm::outs()); + return finishDiagProcessing(0); + } + if (Invocation.getFrontendOptions().RequestedAction == FrontendOptions::ActionType::NoneAction) { Instance->getDiags().diagnose(SourceLoc(), diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 5b9837f424f99..82a683236668c 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -68,7 +68,7 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, auto accessPath = ID->getModulePath(); // only the top-level name is needed (i.e. A in A.B.C) - Modules.insert(accessPath[0].first.str()); + Modules.insert(accessPath[0].Item.str()); } // And now look in the C code we're possibly using. @@ -98,8 +98,8 @@ bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, } if (opts.ImportUnderlyingModule) { - auto underlyingModule = clangImporter->loadModule( - SourceLoc(), std::make_pair(mainModule->getName(), SourceLoc())); + auto underlyingModule = clangImporter->loadModule(SourceLoc(), + { Located(mainModule->getName(), SourceLoc()) }); if (!underlyingModule) { Context.Diags.diagnose(SourceLoc(), diag::error_underlying_module_not_found, diff --git a/lib/FrontendTool/ReferenceDependencies.cpp b/lib/FrontendTool/ReferenceDependencies.cpp index ef198c095afd1..c9cb5117a0a57 100644 --- a/lib/FrontendTool/ReferenceDependencies.cpp +++ b/lib/FrontendTool/ReferenceDependencies.cpp @@ -252,7 +252,7 @@ ProvidesEmitter::emitTopLevelNames() const { out << providesTopLevel << ":\n"; CollectedDeclarations cpd; - for (const Decl *D : SF->Decls) + for (const Decl *D : SF->getTopLevelDecls()) emitTopLevelDecl(D, cpd); for (auto *operatorFunction : cpd.memberOperatorDecls) out << "- \"" << escape(operatorFunction->getName()) << "\"\n"; diff --git a/lib/FrontendTool/TBD.cpp b/lib/FrontendTool/TBD.cpp index b80537a24e95b..2391632f26c78 100644 --- a/lib/FrontendTool/TBD.cpp +++ b/lib/FrontendTool/TBD.cpp @@ -25,6 +25,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Mangler.h" #include "llvm/IR/ValueSymbolTable.h" #include "llvm/Support/FileSystem.h" #include @@ -85,7 +86,14 @@ static bool validateSymbolSet(DiagnosticEngine &diags, std::vector irNotTBD; for (auto &nameValue : IRModule.getValueSymbolTable()) { - auto name = nameValue.getKey(); + // TBDGen inserts mangled names (usually with a leading '_') into its + // symbol table, so make sure to mangle IRGen names before comparing them + // with what TBDGen created. + auto unmangledName = nameValue.getKey(); + SmallString<128> name; + llvm::Mangler::getNameWithPrefix(name, unmangledName, + IRModule.getDataLayout()); + auto value = nameValue.getValue(); if (auto GV = dyn_cast(value)) { // Is this a symbol that should be listed? @@ -94,7 +102,9 @@ static bool validateSymbolSet(DiagnosticEngine &diags, if (!GV->isDeclaration() && externallyVisible) { // Is it listed? if (!symbols.erase(name)) - irNotTBD.push_back(name); + // Note: Add the unmangled name to the irNotTBD list, which is owned + // by the IRModule, instead of the mangled name. + irNotTBD.push_back(unmangledName); } } else { assert(symbols.find(name) == symbols.end() && diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 042571855a62a..40bf8dbb52dee 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC CodeCompletion.cpp CodeCompletionCache.cpp CommentConversion.cpp + CompletionInstance.cpp ConformingMethodList.cpp ExprContextAnalysis.cpp Formatting.cpp diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 70d18a4949fd4..f8e20c52b1e38 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1217,6 +1217,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { CompletionKind Kind = CompletionKind::None; Expr *ParsedExpr = nullptr; SourceLoc DotLoc; + TypeLoc ParsedTypeLoc; DeclContext *CurDeclContext = nullptr; DeclAttrKind AttrKind; @@ -1259,11 +1260,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { Builder.addTypeAnnotation(ST.getString()); } - /// Set to true when we have delivered code completion results - /// to the \c Consumer. - bool DeliveredResults = false; - - Optional> typeCheckParsedExpr() { assert(ParsedExpr && "should have an expression"); @@ -1278,24 +1274,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { // typecheck again. rdar://21466394 if (CheckKind == CompletionTypeCheckKind::Normal && ParsedExpr->getType() && !ParsedExpr->getType()->is()) { - auto refDecl = ParsedExpr->getReferencedDecl(); - auto exprTy = ParsedExpr->getType(); - if (!refDecl) { - // FIXME: do this in the not-already-type-checked branch too? - if (auto *apply = dyn_cast(ParsedExpr)) { - refDecl = apply->getFn()->getReferencedDecl(); - } else if (auto *apply = dyn_cast(ParsedExpr)) { - // If this is an IUO, use the underlying non-optional type instead - auto fnDecl = apply->getFn()->getReferencedDecl(); - if (auto FD = fnDecl.getDecl()) { - if (FD->isImplicitlyUnwrappedOptional()) { - if (auto OT = exprTy->getOptionalObjectType()) - exprTy = OT; - } - } - } - } - return std::make_pair(exprTy, refDecl); + return getReferencedDecl(ParsedExpr); } ConcreteDeclRef ReferencedDecl = nullptr; @@ -1316,8 +1295,21 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { /// \returns true on success, false on failure. bool typecheckParsedType() { assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr"); - return !performTypeLocChecking(P.Context, ParsedTypeLoc, - CurDeclContext, false); + if (!performTypeLocChecking(P.Context, ParsedTypeLoc, + CurDeclContext, false)) + return true; + + // It doesn't type check as a type, so see if it's a qualifying module name. + if (auto *ITR = dyn_cast(ParsedTypeLoc.getTypeRepr())) { + SmallVector AccessPath; + for (auto Component : ITR->getComponentRange()) + AccessPath.push_back({ Component->getNameRef().getBaseIdentifier(), + Component->getLoc() }); + if (auto Module = Context.getLoadedModule(AccessPath)) + ParsedTypeLoc.setType(ModuleType::get(Module)); + return true; + } + return false; } public: @@ -1339,7 +1331,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { AttTargetDK = DK; } - void completeExpr() override; void completeDotExpr(Expr *E, SourceLoc DotLoc) override; void completeStmtOrExpr(CodeCompletionExpr *E) override; void completePostfixExprBeginning(CodeCompletionExpr *E) override; @@ -1350,8 +1341,8 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeTypeDeclResultBeginning() override; void completeTypeSimpleBeginning() override; - void completeTypeIdentifierWithDot() override; - void completeTypeIdentifierWithoutDot() override; + void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override; + void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override; void completeCaseStmtKeyword() override; void completeCaseStmtBeginning(CodeCompletionExpr *E) override; @@ -1363,7 +1354,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeAccessorBeginning(CodeCompletionExpr *E) override; void completePoundAvailablePlatform() override; - void completeImportDecl(std::vector> &Path) override; + void completeImportDecl(std::vector> &Path) override; void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; @@ -1385,18 +1376,6 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { }; } // end anonymous namespace -void CodeCompletionCallbacksImpl::completeExpr() { - if (DeliveredResults) - return; - - Parser::ParserPositionRAII RestorePosition(P); - P.restoreParserPosition(ExprBeginPosition); - - // FIXME: implement fallback code completion. - - deliverCompletionResults(); -} - namespace { static bool isTopLevelContext(const DeclContext *DC) { for (; DC && DC->isLocalContext(); DC = DC->getParent()) { @@ -1582,26 +1561,37 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { bool OnlyTypes; bool OnlyPrecedenceGroups; bool NeedLeadingDot; + bool IncludeModuleQualifier; static RequestedResultsTy fromModule(const ModuleDecl *TheModule) { - return { TheModule, false, false, false }; + return { TheModule, false, false, false, true }; } RequestedResultsTy onlyTypes() const { - return { TheModule, true, false, NeedLeadingDot }; + return { TheModule, true, false, NeedLeadingDot, IncludeModuleQualifier }; } RequestedResultsTy onlyPrecedenceGroups() const { assert(!OnlyTypes && "onlyTypes() already includes precedence groups"); - return { TheModule, false, true, false }; + return { TheModule, false, true, false, true }; } RequestedResultsTy needLeadingDot(bool NeedDot) const { - return { TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot }; + return { + TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot, + IncludeModuleQualifier + }; + } + + RequestedResultsTy withModuleQualifier(bool IncludeModule) const { + return { + TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedLeadingDot, + IncludeModule + }; } static RequestedResultsTy toplevelResults() { - return { nullptr, false, false, false }; + return { nullptr, false, false, false, true }; } }; @@ -1618,7 +1608,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Importer(static_cast(CurrDeclContext->getASTContext(). getClangModuleLoader())), CompletionContext(CompletionContext) { - (void)createTypeChecker(Ctx); + (void)swift::createTypeChecker(Ctx); // Determine if we are doing code completion inside a static method. if (CurrDeclContext) { @@ -1703,7 +1693,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResult::ResultKind:: Declaration, - SemanticContextKind::OtherModule, + SemanticContextKind::None, expectedTypeContext); auto MD = ModuleDecl::create(Ctx.getIdentifier(Pair.first), Ctx); Builder.setAssociatedDecl(MD); @@ -1740,6 +1730,21 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } } + void addModuleName( + const ModuleDecl *MD, + Optional R = None) { + CodeCompletionResultBuilder Builder( + Sink, + CodeCompletionResult::ResultKind::Declaration, + SemanticContextKind::None, + expectedTypeContext); + Builder.setAssociatedDecl(MD); + Builder.addTextChunk(MD->getNameStr()); + Builder.addTypeAnnotation("Module"); + if (R) + Builder.setNotRecommended(*R); + } + void addImportModuleNames() { SmallVector ModuleNames; Ctx.getVisibleTopLevelModuleNames(ModuleNames); @@ -1756,19 +1761,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { continue; auto MD = ModuleDecl::create(ModuleName, Ctx); - CodeCompletionResultBuilder Builder( - Sink, - CodeCompletionResult::ResultKind::Declaration, - SemanticContextKind::OtherModule, - expectedTypeContext); - Builder.setAssociatedDecl(MD); - Builder.addTextChunk(MD->getNameStr()); - Builder.addTypeAnnotation("Module"); + Optional Reason = None; // Imported modules are not recommended. if (ImportedModules.count(MD->getNameStr()) != 0) - Builder.setNotRecommended( - CodeCompletionResult::NotRecommendedReason::Redundant); + Reason = CodeCompletionResult::NotRecommendedReason::Redundant; + + addModuleName(MD, Reason); } } @@ -1889,6 +1888,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PrintOptions PO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO)); } } @@ -1910,6 +1911,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.PrintOptionalAsImplicitlyUnwrapped = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); Builder.addTypeAnnotation(T.getString(PO) + suffix); } @@ -2106,9 +2109,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { auto *Module = NTD->getParentModule(); auto Conformance = Module->lookupConformance( BaseTy, ATD->getProtocol()); - if (Conformance && Conformance->isConcrete()) { - return Conformance->getConcrete() - ->getTypeWitness(const_cast(ATD)); + if (Conformance.isConcrete()) { + return Conformance.getConcrete()->getTypeWitness( + const_cast(ATD)); } } } @@ -2134,6 +2137,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addValueBaseName(Builder, Name); setClangDeclKeywords(VD, Pairs, Builder); + // "not recommended" in its own getter. + if (Kind == LookupKind::ValueInDeclContext) { + if (auto accessor = dyn_cast(CurrDeclContext)) { + if (accessor->getStorage() == VD && accessor->isGetter()) + Builder.setNotRecommended(CodeCompletionResult::NoReason); + } + } + if (!VD->hasInterfaceType()) return; @@ -2202,6 +2213,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return !includeDefaultArgs; case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Column: case DefaultArgumentKind::Function: @@ -2245,9 +2257,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (NeedComma) Builder.addComma(); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); - Builder.addCallParameter(argName, bodyName, paramTy, isVariadic, isInOut, - isIUO, isAutoclosure); + Builder.addCallParameter(argName, bodyName, paramTy, contextTy, + isVariadic, isInOut, isIUO, isAutoclosure); modifiedBuilder = true; NeedComma = true; @@ -2430,8 +2445,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addTypeAnnotation(Builder, AFT->getResult()); }; - if (!AFD || !AFD->getInterfaceType() || - !AFD->getInterfaceType()->is()) { + if (!AFD || !AFD->getInterfaceType()->is()) { // Probably, calling closure type expression. foundFunction(AFT); addPattern(); @@ -2594,6 +2608,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + PO.setBaseType(typeContext->getDeclaredTypeInContext()); ResultType.print(OS, PO); } } @@ -2697,22 +2713,21 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return; assert(CurrDeclContext); - SmallVector initializers; - if (CurrDeclContext->lookupQualified(type, DeclBaseName::createConstructor(), - NL_QualifiedDefault, - initializers)) { - for (auto *init : initializers) { - if (init->shouldHideFromEditor()) - continue; - if (IsUnresolvedMember && - cast(init)->isFailable() && - !cast(init)->isImplicitlyUnwrappedOptional()) { - continue; - } - addConstructorCall(cast(init), Reason, - dynamicLookupInfo, type, None, - /*IsOnType=*/true, name); + + auto results = + swift::lookupSemanticMember(const_cast(CurrDeclContext), + type, DeclBaseName::createConstructor()); + for (const auto &entry : results.allResults()) { + auto *init = cast(entry.getValueDecl()); + if (init->shouldHideFromEditor()) + continue; + if (IsUnresolvedMember && init->isFailable() && + !init->isImplicitlyUnwrappedOptional()) { + continue; } + addConstructorCall(cast(init), Reason, + dynamicLookupInfo, type, None, + /*IsOnType=*/true, name); } } @@ -3025,10 +3040,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { auto CDC = dyn_cast(CurrDeclContext); if (!CDC) return; - // We do not want 'init' completions for 'self' in non-convenience - // initializers and for 'super' in convenience initializers. - if ((IsSelfRefExpr && CDC->isConvenienceInit()) || - ((IsSuperRefExpr && !CDC->isConvenienceInit()))) + + // For classes, we do not want 'init' completions for 'self' in + // non-convenience initializers and for 'super' in convenience initializers. + if (ExprType->is()) { + if ((IsSelfRefExpr && !CDC->isConvenienceInit()) || + (IsSuperRefExpr && CDC->isConvenienceInit())) + return; + } + if (IsSelfRefExpr || IsSuperRefExpr) addConstructorCall(CD, Reason, dynamicLookupInfo, None, None, /*IsOnType=*/false); } @@ -3198,13 +3218,17 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return false; } - bool tryModuleCompletions(Type ExprType) { + bool tryModuleCompletions(Type ExprType, bool TypesOnly = false) { if (auto MT = ExprType->getAs()) { ModuleDecl *M = MT->getModule(); if (CurrModule != M) { // Only use the cache if it is not the current module. - RequestedCachedResults.push_back( - RequestedResultsTy::fromModule(M).needLeadingDot(needDot())); + RequestedResultsTy Request = RequestedResultsTy::fromModule(M) + .needLeadingDot(needDot()) + .withModuleQualifier(false); + if (TypesOnly) + Request = Request.onlyTypes(); + RequestedCachedResults.push_back(Request); return true; } } @@ -3422,9 +3446,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addEqual(); builder.addWhitespace(" "); assert(RHSType && resultType); - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); addTypeAnnotation(builder, resultType); } @@ -3445,10 +3470,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addWhitespace(" "); builder.addTextChunk(op->getName().str()); builder.addWhitespace(" "); - if (RHSType) - builder.addCallParameter(Identifier(), Identifier(), RHSType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + if (RHSType) { + Type contextTy; + if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) + contextTy = typeContext->getDeclaredTypeInContext(); + builder.addCallParameter(Identifier(), RHSType, contextTy); + } if (resultType) addTypeAnnotation(builder, resultType); } @@ -3603,6 +3630,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { CodeCompletionLiteralKind::StringLiteral, "String"); addFromProto("#file", CodeCompletionKeywordKind::pound_file, CodeCompletionLiteralKind::StringLiteral, "String"); + if (Ctx.LangOpts.EnableConcisePoundFile) { + addFromProto("#filePath", CodeCompletionKeywordKind::pound_file, + CodeCompletionLiteralKind::StringLiteral, "String"); + } addFromProto("#line", CodeCompletionKeywordKind::pound_line, CodeCompletionLiteralKind::IntegerLiteral, "Int"); addFromProto("#column", CodeCompletionKeywordKind::pound_column, @@ -3668,21 +3699,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { addFromProto(LK::ColorLiteral, "", [&](Builder &builder) { builder.addTextChunk("#colorLiteral"); builder.addLeftParen(); - builder.addCallParameter(context.getIdentifier("red"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("red"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("green"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("green"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("blue"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("blue"), floatType); builder.addComma(); - builder.addCallParameter(context.getIdentifier("alpha"), floatType, - /*IsVarArg*/ false, /*IsInOut*/ false, - /*isIUO*/ false, /*isAutoClosure*/ false); + builder.addCallParameter(context.getIdentifier("alpha"), floatType); builder.addRightParen(); }); @@ -3691,9 +3714,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { builder.addTextChunk("#imageLiteral"); builder.addLeftParen(); builder.addCallParameter(context.getIdentifier("resourceName"), - stringType, /*IsVarArg*/ false, - /*IsInOut*/ false, /*isIUO*/ false, - /*isAutoClosure*/ false); + stringType); builder.addRightParen(); }); @@ -3762,17 +3783,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void getValueCompletionsInDeclContext(SourceLoc Loc, DeclFilter Filter = DefaultFilter, - bool IncludeTopLevel = false, - bool RequestCache = true, - bool LiteralCompletions = true) { + bool LiteralCompletions = true, + bool ModuleQualifier = true) { ExprType = Type(); Kind = LookupKind::ValueInDeclContext; NeedLeadingDot = false; FilteredDeclConsumer Consumer(*this, Filter); lookupVisibleDecls(Consumer, CurrDeclContext, - /*IncludeTopLevel=*/IncludeTopLevel, Loc); - if (RequestCache) - RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults()); + /*IncludeTopLevel=*/false, Loc); + RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults() + .withModuleQualifier(ModuleQualifier)); // Manually add any expected nominal types from imported modules so that // they get their expected type relation. Don't include protocols, since @@ -3870,6 +3890,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void getTypeCompletions(Type BaseType) { + if (tryModuleCompletions(BaseType, /*OnlyTypes=*/true)) + return; Kind = LookupKind::Type; this->BaseType = BaseType; NeedLeadingDot = !HaveDot; @@ -3879,7 +3901,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (BaseType->isAnyExistentialType()) { addKeyword("Protocol", MetatypeType::get(BaseType)); addKeyword("Type", ExistentialMetatypeType::get(BaseType)); - } else { + } else if (!BaseType->is()) { addKeyword("Type", MetatypeType::get(BaseType)); } } @@ -3957,8 +3979,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (Module == CurrModule) continue; - RequestedCachedResults.push_back( - RequestedResultsTy::fromModule(Module).onlyPrecedenceGroups()); + RequestedCachedResults.push_back(RequestedResultsTy::fromModule(Module) + .onlyPrecedenceGroups() + .withModuleQualifier(false)); } } @@ -3995,13 +4018,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { getAttributeDeclParamCompletions(DAK_Available, 0); } - void getTypeCompletionsInDeclContext(SourceLoc Loc) { + void getTypeCompletionsInDeclContext(SourceLoc Loc, + bool ModuleQualifier = true) { Kind = LookupKind::TypeInDeclContext; lookupVisibleDecls(*this, CurrDeclContext, /*IncludeTopLevel=*/false, Loc); RequestedCachedResults.push_back( - RequestedResultsTy::toplevelResults().onlyTypes()); + RequestedResultsTy::toplevelResults() + .onlyTypes() + .withModuleQualifier(ModuleQualifier)); } void getToplevelCompletions(bool OnlyTypes) { @@ -4019,10 +4045,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Kind = LookupKind::ImportFromModule; NeedLeadingDot = ResultsHaveLeadingDot; - llvm::SmallVector, 1> LookupAccessPath; + llvm::SmallVector, 1> LookupAccessPath; for (auto Piece : AccessPath) { - LookupAccessPath.push_back( - std::make_pair(Ctx.getIdentifier(Piece), SourceLoc())); + LookupAccessPath.push_back({Ctx.getIdentifier(Piece), SourceLoc()}); } AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); TheModule->lookupVisibleDecls(LookupAccessPath, FilteringConsumer, @@ -4091,10 +4116,42 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { void addAccessControl(const ValueDecl *VD, CodeCompletionResultBuilder &Builder) { - assert(CurrDeclContext->getSelfNominalTypeDecl()); - auto AccessOfContext = - CurrDeclContext->getSelfNominalTypeDecl()->getFormalAccess(); - auto Access = std::min(VD->getFormalAccess(), AccessOfContext); + auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl(); + assert(CurrentNominal); + + auto AccessOfContext = CurrentNominal->getFormalAccess(); + if (AccessOfContext < AccessLevel::Public) + return; + + auto Access = VD->getFormalAccess(); + // Use the greater access between the protocol requirement and the witness. + // In case of: + // + // public protocol P { func foo() } + // public class B { func foo() {} } + // public class C: B, P { + // + // } + // + // 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding + // declaration, the user needs to write both 'public' and 'override': + // + // public class C: B { + // public override func foo() {} + // } + if (Access < AccessLevel::Public && + !isa(VD->getDeclContext())) { + for (auto Conformance : CurrentNominal->getAllConformances()) { + Conformance->getRootConformance()->forEachValueWitness( + [&](ValueDecl *req, Witness witness) { + if (witness.getDecl() == VD) + Access = std::max( + Access, Conformance->getProtocol()->getFormalAccess()); + }); + } + } + + Access = std::min(Access, AccessOfContext); // Only emit 'public', not needed otherwise. if (Access >= AccessLevel::Public) Builder.addAccessControlKeyword(Access); @@ -4102,8 +4159,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { /// Return type if the result type if \p VD should be represented as opaque /// result type. - TypeLoc getOpaqueResultTypeLoc(const ValueDecl *VD, DeclVisibilityKind Reason, - DynamicLookupInfo dynamicLookupInfo) { + Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason, + DynamicLookupInfo dynamicLookupInfo) { if (Reason != DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal) return nullptr; @@ -4113,35 +4170,61 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { return nullptr; Type ResultT; - if (auto *FD = dyn_cast(VD)) + if (auto *FD = dyn_cast(VD)) { + if (FD->getGenericParams()) { + // Generic function cannot have opaque result type. + return nullptr; + } ResultT = FD->getResultInterfaceType(); - else if (auto *SD = dyn_cast(VD)) + } else if (auto *SD = dyn_cast(VD)) { + if (SD->getGenericParams()) { + // Generic subscript cannot have opaque result type. + return nullptr; + } ResultT = SD->getElementInterfaceType(); - else if (auto *VarD = dyn_cast(VD)) + } else if (auto *VarD = dyn_cast(VD)) { ResultT = VarD->getInterfaceType(); - else - return nullptr; - - if (!ResultT->is()) - // The result is not associatedtype. + } else { return nullptr; + } - // If associatedtype doesn't have conformance/superclass constraint, we - // can't use opaque type. - auto assocTyD = ResultT->castTo()->getAssocType(); - if (!assocTyD->getInherited().size()) + if (!ResultT->is() || + !ResultT->castTo()->getAssocType()) + // The result is not a valid associatedtype. return nullptr; // Try substitution to see if the associated type is resolved to concrete // type. auto substMap = currTy->getMemberSubstitutionMap( CurrDeclContext->getParentModule(), VD); - ResultT = ResultT.subst(substMap); - if (!ResultT || !ResultT->is()) + if (!ResultT.subst(substMap)->is()) // If resolved print it. return nullptr; - return assocTyD->getInherited()[0]; + auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext(); + + if (genericSig->isConcreteType(ResultT)) + // If it has same type requrement, we will emit the concrete type. + return nullptr; + + // Collect requirements on the associatedtype. + SmallVector opaqueTypes; + bool hasExplicitAnyObject = false; + if (auto superTy = genericSig->getSuperclassBound(ResultT)) + opaqueTypes.push_back(superTy); + for (auto proto : genericSig->getConformsTo(ResultT)) + opaqueTypes.push_back(proto->getDeclaredInterfaceType()); + if (auto layout = genericSig->getLayoutConstraint(ResultT)) + hasExplicitAnyObject = layout->isClass(); + + if (!hasExplicitAnyObject) { + if (opaqueTypes.empty()) + return nullptr; + if (opaqueTypes.size() == 1) + return opaqueTypes.front(); + } + return ProtocolCompositionType::get( + VD->getASTContext(), opaqueTypes, hasExplicitAnyObject); } void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, @@ -4149,14 +4232,14 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { CodeCompletionResultBuilder &Builder, bool hasDeclIntroducer) { class DeclPrinter : public StreamPrinter { - TypeLoc OpaqueBaseTy; + Type OpaqueBaseTy; public: using StreamPrinter::StreamPrinter; Optional NameOffset; - DeclPrinter(raw_ostream &OS, TypeLoc OpaqueBaseTy) + DeclPrinter(raw_ostream &OS, Type OpaqueBaseTy) : StreamPrinter(OS), OpaqueBaseTy(OpaqueBaseTy) {} void printDeclLoc(const Decl *D) override { @@ -4168,7 +4251,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override { if (!OpaqueBaseTy.isNull()) { OS << "some "; - TL = OpaqueBaseTy; + TL = TypeLoc::withoutLoc(OpaqueBaseTy); } } }; @@ -4178,10 +4261,11 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); DeclPrinter Printer( - OS, getOpaqueResultTypeLoc(VD, Reason, dynamicLookupInfo)); + OS, getOpaqueResultType(VD, Reason, dynamicLookupInfo)); PrintOptions Options; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) Options.setBaseType(transformType); + Options.SkipUnderscoredKeywords = true; Options.PrintImplicitAttrs = false; Options.ExclusiveAttrList.push_back(TAK_escaping); Options.ExclusiveAttrList.push_back(TAK_autoclosure); @@ -4300,6 +4384,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { { llvm::raw_svector_ostream OS(DeclStr); PrintOptions Options; + if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) + Options.setBaseType(transformType); Options.PrintImplicitAttrs = false; Options.SkipAttributes = true; CD->print(OS, Options); @@ -4317,9 +4403,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer { if (D->shouldHideFromEditor()) return; - if (D->isFinal() || - // A 'class' member with an initial value cannot be overriden either. - (D->isStatic() && D->getAttrs().hasAttribute())) + if (D->isFinal()) return; bool hasIntroducer = hasFuncIntroducer || @@ -4599,13 +4683,22 @@ void CodeCompletionCallbacksImpl::completeInPrecedenceGroup(SyntaxKind SK) { CurDeclContext = P.CurDeclContext; } -void CodeCompletionCallbacksImpl::completeTypeIdentifierWithDot() { +void CodeCompletionCallbacksImpl::completeTypeIdentifierWithDot( + IdentTypeRepr *ITR) { + if (!ITR) { + completeTypeSimpleBeginning(); + return; + } Kind = CompletionKind::TypeIdentifierWithDot; + ParsedTypeLoc = TypeLoc(ITR); CurDeclContext = P.CurDeclContext; } -void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot() { +void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot( + IdentTypeRepr *ITR) { + assert(ITR); Kind = CompletionKind::TypeIdentifierWithoutDot; + ParsedTypeLoc = TypeLoc(ITR); CurDeclContext = P.CurDeclContext; } @@ -4623,10 +4716,10 @@ void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr * } void CodeCompletionCallbacksImpl::completeImportDecl( - std::vector> &Path) { + std::vector> &Path) { Kind = CompletionKind::Import; CurDeclContext = P.CurDeclContext; - DotLoc = Path.empty() ? SourceLoc() : Path.back().second; + DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc; if (DotLoc.isInvalid()) return; auto Importer = static_cast(CurDeclContext->getASTContext(). @@ -4635,7 +4728,7 @@ void CodeCompletionCallbacksImpl::completeImportDecl( Importer->collectSubModuleNames(Path, SubNames); ASTContext &Ctx = CurDeclContext->getASTContext(); for (StringRef Sub : SubNames) { - Path.push_back(std::make_pair(Ctx.getIdentifier(Sub), SourceLoc())); + Path.push_back({ Ctx.getIdentifier(Sub), SourceLoc() }); SubModuleNameVisibilityPairs.push_back( std::make_pair(Sub.str(), Ctx.getLoadedModule(Path))); Path.pop_back(); @@ -4899,13 +4992,16 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::TypeIdentifierWithoutDot: break; - case CompletionKind::TypeDeclResultBeginning: - if (!isa(CurDeclContext)) - if (CurDeclContext->isTypeContext() || - (ParsedDecl && isa(ParsedDecl))) + case CompletionKind::TypeDeclResultBeginning: { + auto DC = CurDeclContext; + if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl()) + DC = ParsedDecl->getDeclContext(); + if (!isa(DC)) + if (DC->isTypeContext() || (ParsedDecl && isa(ParsedDecl))) addOpaqueTypeKeyword(Sink); LLVM_FALLTHROUGH; + } case CompletionKind::TypeSimpleBeginning: addAnyTypeKeyword(Sink); break; @@ -5074,8 +5170,6 @@ void CodeCompletionCallbacksImpl::doneParsing() { CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) MaybeFuncBody = false; } - // Add keywords even if type checking fails completely. - addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); if (auto *DC = dyn_cast_or_null(ParsedDecl)) { if (DC->isChildContextOf(CurDeclContext)) @@ -5086,6 +5180,9 @@ void CodeCompletionCallbacksImpl::doneParsing() { CurDeclContext, CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); + // Add keywords even if type checking fails completely. + addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); + Optional ExprType; ConcreteDeclRef ReferencedDecl = nullptr; if (ParsedExpr) { @@ -5259,7 +5356,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { } else { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter, - false, true, false); + /*LiteralCompletions=*/false); } break; } @@ -5468,7 +5565,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { std::vector AccessPath; for (auto Piece : Path) { - AccessPath.push_back(Piece.first.str()); + AccessPath.push_back(Piece.Item.str()); } StringRef ModuleFilename = TheModule->getModuleFilename(); @@ -5494,6 +5591,8 @@ void CodeCompletionCallbacksImpl::doneParsing() { return; // already handled. RequestedModules.push_back({std::move(K), TheModule, Request.OnlyTypes, Request.OnlyPrecedenceGroups}); + if (Request.IncludeModuleQualifier) + Lookup.addModuleName(TheModule); } }; @@ -5506,6 +5605,10 @@ void CodeCompletionCallbacksImpl::doneParsing() { // Add results from current module. Lookup.getToplevelCompletions(Request.OnlyTypes); + // Add the qualifying module name + if (Request.IncludeModuleQualifier) + Lookup.addModuleName(CurDeclContext->getParentModule()); + // Add results for all imported modules. ModuleDecl::ImportFilter ImportFilter; ImportFilter |= ModuleDecl::ImportFilterKind::Public; @@ -5537,7 +5640,6 @@ void CodeCompletionCallbacksImpl::deliverCompletionResults() { Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DCForModules); RequestedModules.clear(); - DeliveredResults = true; } void PrintingCodeCompletionConsumer::handleResults( diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 6498cc80ad6b2..cc4793d9eb1ba 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -344,7 +344,7 @@ class CodeCompletionResultBuilder { } void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, - bool IsVarArg, bool IsInOut, bool IsIUO, + Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, bool isAutoClosure) { CurrentNestingLevel++; @@ -388,6 +388,8 @@ class CodeCompletionResultBuilder { PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); std::string TypeName = Ty->getString(PO); addChunkWithText(CodeCompletionString::Chunk::ChunkKind::CallParameterType, TypeName); @@ -398,10 +400,13 @@ class CodeCompletionResultBuilder { if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallParameterClosureType. PrintOptions PO; - PO.PrintFunctionRepresentationAttrs = false; + PO.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; + if (ContextTy) + PO.setBaseType(ContextTy); addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, AFT->getString(PO)); @@ -412,10 +417,10 @@ class CodeCompletionResultBuilder { CurrentNestingLevel--; } - void addCallParameter(Identifier Name, Type Ty, bool IsVarArg, bool IsInOut, - bool IsIUO, bool isAutoClosure) { - addCallParameter(Name, Identifier(), Ty, IsVarArg, IsInOut, IsIUO, - isAutoClosure); + void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { + addCallParameter(Name, Identifier(), Ty, ContextTy, + /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, + /*isAutoClosure=*/false); } void addGenericParameter(StringRef Name) { diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp new file mode 100644 index 0000000000000..446d80601a9c3 --- /dev/null +++ b/lib/IDE/CompletionInstance.cpp @@ -0,0 +1,398 @@ +//===--- CompletionInstance.cpp -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/CompletionInstance.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/Module.h" +#include "swift/AST/SourceFile.h" +#include "swift/Basic/LangOptions.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Parse/Lexer.h" +#include "swift/Parse/PersistentParserState.h" +#include "swift/Subsystems.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace swift; +using namespace ide; + +std::unique_ptr +swift::ide::makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, + unsigned &Offset, + StringRef bufferIdentifier) { + + auto origBuffSize = origBuf->getBufferSize(); + if (Offset > origBuffSize) + Offset = origBuffSize; + + auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( + origBuffSize + 1, bufferIdentifier); + auto *pos = origBuf->getBufferStart() + Offset; + auto *newPos = + std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); + *newPos = '\0'; + std::copy(pos, origBuf->getBufferEnd(), newPos + 1); + + return std::unique_ptr(newBuffer.release()); +} + +namespace { +/// Returns index number of \p D in \p Decls . If it's not found, returns ~0. +template +unsigned findIndexInRange(Decl *D, const Range &Decls) { + unsigned N = 0; + for (auto I = Decls.begin(), E = Decls.end(); I != E; ++I) { + if ((*I)->isImplicit()) + continue; + if (*I == D) + return N; + ++N; + } + return ~0U; +} + +/// Return the element at \p N in \p Decls . +template Decl *getElementAt(const Range &Decls, unsigned N) { + for (auto I = Decls.begin(), E = Decls.end(); I != E; ++I) { + if ((*I)->isImplicit()) + continue; + if (N == 0) + return *I; + --N; + } + return nullptr; +} + +/// Find the equivalent \c DeclContext with \p DC from \p SF AST. +/// This assumes the AST which contains \p DC has exact the same structure with +/// \p SF. +/// FIXME: This doesn't support IfConfigDecl blocks. If \p DC is in an inactive +/// config block, this function returns \c false. +static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, + SourceFile *SF) { + auto *newDC = DC; + // NOTE: Shortcut for DC->getParentSourceFile() == SF case is not needed + // because they should be always different. + + // Get the index path in the current AST. + SmallVector IndexStack; + do { + auto *D = newDC->getAsDecl(); + if (!D) + return nullptr; + auto *parentDC = newDC->getParent(); + unsigned N; + + if (auto accessor = dyn_cast(D)) { + // The AST for accessors is like: + // DeclContext -> AbstractStorageDecl -> AccessorDecl + // We need to push the index of the accessor within the accessor list + // of the storage. + auto *storage = accessor->getStorage(); + if (!storage) + return nullptr; + auto accessorN = findIndexInRange(accessor, storage->getAllAccessors()); + IndexStack.push_back(accessorN); + D = storage; + } + + if (auto parentSF = dyn_cast(parentDC)) + N = findIndexInRange(D, parentSF->getTopLevelDecls()); + else if (auto parentIDC = + dyn_cast(parentDC->getAsDecl())) + N = findIndexInRange(D, parentIDC->getMembers()); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (indexpath)"); + + // Not found in the decl context tree. + // FIXME: Probably DC is in an inactive #if block. + if (N == ~0U) { + return nullptr; + } + + IndexStack.push_back(N); + newDC = parentDC; + } while (!newDC->isModuleScopeContext()); + + assert(isa(newDC) && "DC should be in a SourceFile"); + + // Query the equivalent decl context from the base SourceFile using the index + // path. + newDC = SF; + do { + auto N = IndexStack.pop_back_val(); + Decl *D; + if (auto parentSF = dyn_cast(newDC)) + D = getElementAt(parentSF->getTopLevelDecls(), N); + else if (auto parentIDC = dyn_cast(newDC->getAsDecl())) + D = getElementAt(parentIDC->getMembers(), N); + else + llvm_unreachable("invalid DC kind for finding equivalent DC (query)"); + + if (auto storage = dyn_cast(D)) { + if (IndexStack.empty()) + return nullptr; + auto accessorN = IndexStack.pop_back_val(); + D = getElementAt(storage->getAllAccessors(), accessorN); + } + + newDC = dyn_cast(D); + if (!newDC) + return nullptr; + } while (!IndexStack.empty()); + + assert(newDC->getContextKind() == DC->getContextKind()); + + return newDC; +} + +} // namespace + +bool CompletionInstance::performCachedOperaitonIfPossible( + const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + if (!CachedCI) + return false; + if (CachedReuseCount >= MaxASTReuseCount) + return false; + if (CachedArgHash != ArgsHash) + return false; + + auto &CI = *CachedCI; + + if (!CI.hasPersistentParserState()) + return false; + auto &oldState = CI.getPersistentParserState(); + if (!oldState.hasCodeCompletionDelayedDeclState()) + return false; + + auto &SM = CI.getSourceMgr(); + if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != + completionBuffer->getBufferIdentifier()) + return false; + + auto &oldInfo = oldState.getCodeCompletionDelayedDeclState(); + + // Currently, only completions within a function body is supported. + if (oldInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return false; + + // Parse the new buffer into temporary SourceFile. + SourceManager tmpSM; + auto tmpBufferID = tmpSM.addMemBufferCopy(completionBuffer); + tmpSM.setCodeCompletionPoint(tmpBufferID, Offset); + + LangOptions langOpts; + langOpts.DisableParserLookup = true; + TypeCheckerOptions typeckOpts; + SearchPathOptions searchPathOpts; + DiagnosticEngine tmpDiags(tmpSM); + std::unique_ptr Ctx( + ASTContext::get(langOpts, typeckOpts, searchPathOpts, tmpSM, tmpDiags)); + registerIDERequestFunctions(Ctx->evaluator); + registerTypeCheckerRequestFunctions(Ctx->evaluator); + registerSILGenRequestFunctions(Ctx->evaluator); + ModuleDecl *M = ModuleDecl::create(Identifier(), *Ctx); + PersistentParserState newState; + SourceFile *newSF = + new (*Ctx) SourceFile(*M, SourceFileKind::Library, tmpBufferID, + SourceFile::ImplicitModuleImportKind::None); + newSF->enableInterfaceHash(); + // Ensure all non-function-body tokens are hashed into the interface hash + Ctx->LangOpts.EnableTypeFingerprints = false; + parseIntoSourceFileFull(*newSF, tmpBufferID, &newState); + // Couldn't find any completion token? + if (!newState.hasCodeCompletionDelayedDeclState()) + return false; + + auto &newInfo = newState.getCodeCompletionDelayedDeclState(); + + // The new completion must happens in function body too. + if (newInfo.Kind != CodeCompletionDelayedDeclKind::FunctionBody) + return false; + + auto *oldSF = oldInfo.ParentContext->getParentSourceFile(); + + // If the interface has changed, AST must be refreshed. + llvm::SmallString<32> oldInterfaceHash{}; + llvm::SmallString<32> newInterfaceHash{}; + oldSF->getInterfaceHash(oldInterfaceHash); + newSF->getInterfaceHash(newInterfaceHash); + if (oldInterfaceHash != newInterfaceHash) + return false; + + DeclContext *DC = + getEquivalentDeclContextFromSourceFile(newInfo.ParentContext, oldSF); + if (!DC) + return false; + + // OK, we can perform fast completion for this. Update the orignal delayed + // decl state. + + // Fast completion keeps the buffer in memory for multiple completions. + // To reduce the consumption, slice the source buffer so it only holds + // the portion that is needed for the second pass. + auto startOffset = newInfo.StartOffset; + if (newInfo.PrevOffset != ~0u) + startOffset = newInfo.PrevOffset; + auto startLoc = tmpSM.getLocForOffset(tmpBufferID, startOffset); + startLoc = Lexer::getLocForStartOfLine(tmpSM, startLoc); + startOffset = tmpSM.getLocOffsetInBuffer(startLoc, tmpBufferID); + + auto endOffset = newInfo.EndOffset; + auto endLoc = tmpSM.getLocForOffset(tmpBufferID, endOffset); + endLoc = Lexer::getLocForEndOfToken(tmpSM, endLoc); + endOffset = tmpSM.getLocOffsetInBuffer(endLoc, tmpBufferID); + + newInfo.StartOffset -= startOffset; + newInfo.EndOffset -= startOffset; + if (newInfo.PrevOffset != ~0u) + newInfo.PrevOffset -= startOffset; + + auto sourceText = completionBuffer->getBuffer().slice(startOffset, endOffset); + auto newOffset = Offset - startOffset; + + auto newBufferID = + SM.addMemBufferCopy(sourceText, completionBuffer->getBufferIdentifier()); + SM.openVirtualFile(SM.getLocForBufferStart(newBufferID), + tmpSM.getDisplayNameForLoc(startLoc), + tmpSM.getLineAndColumn(startLoc).first - 1); + SM.setCodeCompletionPoint(newBufferID, newOffset); + + // Construct dummy scopes. We don't need to restore the original scope + // because they are probably not 'isResolvable()' anyway. + auto &SI = oldState.getScopeInfo(); + assert(SI.getCurrentScope() == nullptr); + Scope Top(SI, ScopeKind::TopLevel); + Scope Body(SI, ScopeKind::FunctionBody); + + oldInfo.ParentContext = DC; + oldInfo.StartOffset = newInfo.StartOffset; + oldInfo.EndOffset = newInfo.EndOffset; + oldInfo.PrevOffset = newInfo.PrevOffset; + oldState.restoreCodeCompletionDelayedDeclState(oldInfo); + + auto *AFD = cast(DC); + if (AFD->isBodySkipped()) + AFD->setBodyDelayed(AFD->getBodySourceRange()); + if (DiagC) + CI.addDiagnosticConsumer(DiagC); + + CI.getDiags().diagnose(SM.getLocForOffset(newBufferID, newInfo.StartOffset), + diag::completion_reusing_astcontext); + + Callback(CI); + + if (DiagC) + CI.removeDiagnosticConsumer(DiagC); + + CachedReuseCount += 1; + + return true; +} + +bool CompletionInstance::performNewOperation( + Optional ArgsHash, swift::CompilerInvocation &Invocation, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + auto TheInstance = std::make_unique(); + auto &CI = *TheInstance; + if (DiagC) + CI.addDiagnosticConsumer(DiagC); + + if (FileSystem != llvm::vfs::getRealFileSystem()) + CI.getSourceMgr().setFileSystem(FileSystem); + + Invocation.setCodeCompletionPoint(completionBuffer, Offset); + + if (CI.setup(Invocation)) { + Error = "failed to setup compiler instance"; + return false; + } + registerIDERequestFunctions(CI.getASTContext().evaluator); + + CI.performParseAndResolveImportsOnly(); + if (CI.hasPersistentParserState()) + Callback(CI); + + if (DiagC) + CI.removeDiagnosticConsumer(DiagC); + + if (ArgsHash.hasValue()) { + CachedCI = std::move(TheInstance); + CachedArgHash = *ArgsHash; + CachedReuseCount = 0; + } + + return true; +} + +bool swift::ide::CompletionInstance::performOperation( + swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + llvm::MemoryBuffer *completionBuffer, unsigned int Offset, + bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC, + llvm::function_ref Callback) { + + // Always disable source location resolutions from .swiftsourceinfo file + // because they're somewhat heavy operations and aren't needed for completion. + Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true; + + // Disable to build syntax tree because code-completion skips some portion of + // source text. That breaks an invariant of syntax tree building. + Invocation.getLangOptions().BuildSyntaxTree = false; + + // Since caching uses the interface hash, and since per type fingerprints + // weaken that hash, disable them here: + Invocation.getLangOptions().EnableTypeFingerprints = false; + + // FIXME: ASTScopeLookup doesn't support code completion yet. + Invocation.disableASTScopeLookup(); + + if (EnableASTCaching) { + // Compute the signature of the invocation. + llvm::hash_code ArgsHash(0); + for (auto arg : Args) + ArgsHash = llvm::hash_combine(ArgsHash, StringRef(arg)); + + // Concurrent completions will block so that they have higher chance to use + // the cached completion instance. + std::lock_guard lock(mtx); + + if (performCachedOperaitonIfPossible(Invocation, ArgsHash, completionBuffer, + Offset, DiagC, Callback)) + return true; + + if (performNewOperation(ArgsHash, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } else { + // Concurrent completions may happen in parallel when caching is disabled. + if (performNewOperation(None, Invocation, FileSystem, completionBuffer, + Offset, Error, DiagC, Callback)) + return true; + } + + assert(!Error.empty()); + return false; +} diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index fb4430291862a..b2c2ee4f39609 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" @@ -61,10 +62,9 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { if (auto *patternInit = dyn_cast(DC)) { if (auto *PBD = patternInit->getBinding()) { auto i = patternInit->getBindingIndex(); + PBD->getPattern(i)->forEachVariable( + [](VarDecl *VD) { (void)VD->getInterfaceType(); }); if (PBD->getInit(i)) { - PBD->getPattern(i)->forEachVariable([](VarDecl *VD) { - (void) VD->getInterfaceType(); - }); if (!PBD->isInitializerChecked(i)) typeCheckPatternBinding(PBD, i); } @@ -74,7 +74,14 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { case DeclContextKind::AbstractFunctionDecl: { auto *AFD = cast(DC); - typeCheckAbstractFunctionBodyUntil(AFD, Loc); + auto &SM = DC->getASTContext().SourceMgr; + auto bodyRange = AFD->getBodySourceRange(); + if (SM.rangeContainsTokenLoc(bodyRange, Loc)) { + swift::typeCheckAbstractFunctionBodyUntil(AFD, Loc); + } else { + assert(bodyRange.isInvalid() && "The body should not be parsed if the " + "completion happens in the signature"); + } break; } @@ -91,15 +98,29 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) { } // anonymous namespace void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) { - // The only time we have to explicitly check a TopLevelCodeDecl - // is when we're directly inside of one. In this case, - // performTypeChecking() did not type check it for us. while (isa(DC)) DC = DC->getParent(); - if (auto *TLCD = dyn_cast(DC)) - typeCheckTopLevelCodeDecl(TLCD); - else + + if (auto *TLCD = dyn_cast(DC)) { + // Typecheck all 'TopLevelCodeDecl's up to the target one. + // In theory, this is not needed, but it fails to resolve the type of + // 'guard'ed variable. e.g. + // + // guard value = something() else { fatalError() } + // + // Here, 'value' is '' unless we explicitly typecheck the + // 'guard' statement. + SourceFile *SF = DC->getParentSourceFile(); + for (auto *D : SF->getTopLevelDecls()) { + if (auto Code = dyn_cast(D)) { + typeCheckTopLevelCodeDecl(Code); + if (Code == TLCD) + break; + } + } + } else { typeCheckContextImpl(DC, Loc); + } } //===----------------------------------------------------------------------===// @@ -140,8 +161,6 @@ class ExprFinder : public ASTWalker { return {isInterstingRange(S), S}; } - bool walkToDeclPre(Decl *D) override { return isInterstingRange(D); } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } bool walkToTypeReprPre(TypeRepr *T) override { return false; } }; @@ -160,13 +179,11 @@ Expr *swift::ide::findParsedExpr(const DeclContext *DC, Type swift::ide::getReturnTypeFromContext(const DeclContext *DC) { if (auto FD = dyn_cast(DC)) { - if (FD->hasInterfaceType()) { - auto Ty = FD->getInterfaceType(); - if (FD->getDeclContext()->isTypeContext()) - Ty = FD->getMethodInterfaceType(); - if (auto FT = Ty->getAs()) - return DC->mapTypeIntoContext(FT->getResult()); - } + auto Ty = FD->getInterfaceType(); + if (FD->getDeclContext()->isTypeContext()) + Ty = FD->getMethodInterfaceType(); + if (auto FT = Ty->getAs()) + return DC->mapTypeIntoContext(FT->getResult()); } else if (auto ACE = dyn_cast(DC)) { if (ACE->getType() && !ACE->getType()->hasError()) return ACE->getResultType(); @@ -259,12 +276,13 @@ class ExprParentFinder : public ASTWalker { /// Collect function (or subscript) members with the given \p name on \p baseTy. static void collectPossibleCalleesByQualifiedLookup( - DeclContext &DC, Type baseTy, DeclBaseName name, + DeclContext &DC, Type baseTy, DeclNameRef name, SmallVectorImpl &candidates) { bool isOnMetaType = baseTy->is(); SmallVector decls; - if (!DC.lookupQualified(baseTy->getMetatypeInstanceType(), name, + if (!DC.lookupQualified(baseTy->getMetatypeInstanceType(), + name.withoutArgumentLabels(), NL_QualifiedDefault | NL_ProtocolMembers, decls)) return; @@ -276,9 +294,6 @@ static void collectPossibleCalleesByQualifiedLookup( if (!isMemberDeclApplied(&DC, baseTy->getMetatypeInstanceType(), VD)) continue; Type declaredMemberType = VD->getInterfaceType(); - if (!declaredMemberType) { - continue; - } if (!declaredMemberType->is()) continue; if (VD->getDeclContext()->isTypeContext()) { @@ -325,7 +340,7 @@ static void collectPossibleCalleesByQualifiedLookup( /// Collect function (or subscript) members with the given \p name on /// \p baseExpr expression. static void collectPossibleCalleesByQualifiedLookup( - DeclContext &DC, Expr *baseExpr, DeclBaseName name, + DeclContext &DC, Expr *baseExpr, DeclNameRef name, SmallVectorImpl &candidates) { ConcreteDeclRef ref = nullptr; auto baseTyOpt = getTypeOfCompletionContextExpr( @@ -372,11 +387,12 @@ static bool collectPossibleCalleesForApply( } } else if (auto *UDE = dyn_cast(fnExpr)) { collectPossibleCalleesByQualifiedLookup( - DC, UDE->getBase(), UDE->getName().getBaseName(), candidates); + DC, UDE->getBase(), UDE->getName(), candidates); } else if (auto *DSCE = dyn_cast(fnExpr)) { if (auto *DRE = dyn_cast(DSCE->getFn())) { collectPossibleCalleesByQualifiedLookup( - DC, DSCE->getArg(), DRE->getDecl()->getBaseName(), candidates); + DC, DSCE->getArg(), DeclNameRef(DRE->getDecl()->getFullName()), + candidates); } } @@ -393,7 +409,7 @@ static bool collectPossibleCalleesForApply( auto baseTy = AMT->getInstanceType(); if (baseTy->mayHaveMembers()) collectPossibleCalleesByQualifiedLookup( - DC, AMT, DeclBaseName::createConstructor(), candidates); + DC, AMT, DeclNameRef::createConstructor(), candidates); } } @@ -414,7 +430,7 @@ static bool collectPossibleCalleesForSubscript( } } else { collectPossibleCalleesByQualifiedLookup(DC, subscriptExpr->getBase(), - DeclBaseName::createSubscript(), + DeclNameRef::createSubscript(), candidates); } return !candidates.empty(); @@ -426,7 +442,6 @@ static bool collectPossibleCalleesForUnresolvedMember( DeclContext &DC, UnresolvedMemberExpr *unresolvedMemberExpr, SmallVectorImpl &candidates) { auto currModule = DC.getParentModule(); - auto baseName = unresolvedMemberExpr->getName().getBaseName(); // Get the context of the expression itself. ExprContextInfo contextInfo(&DC, unresolvedMemberExpr); @@ -435,7 +450,8 @@ static bool collectPossibleCalleesForUnresolvedMember( continue; SmallVector members; collectPossibleCalleesByQualifiedLookup(DC, MetatypeType::get(expectedTy), - baseName, members); + unresolvedMemberExpr->getName(), + members); for (auto member : members) { if (isReferenceableByImplicitMemberExpr(currModule, &DC, expectedTy, member.second)) @@ -586,12 +602,61 @@ class ExprContextAnalyzer { // Check context types of the array literal expression. ExprContextInfo arrayCtxtInfo(DC, Parent); for (auto arrayT : arrayCtxtInfo.getPossibleTypes()) { - if (auto boundGenericT = arrayT->getAs()) + if (auto boundGenericT = arrayT->getAs()) { + // let _: [Element] = [#HERE#] + // In this case, 'Element' is the expected type. if (boundGenericT->getDecl() == Context.getArrayDecl()) recordPossibleType(boundGenericT->getGenericArgs()[0]); + + // let _: [Key : Value] = [#HERE#] + // In this case, 'Key' is the expected type. + if (boundGenericT->getDecl() == Context.getDictionaryDecl()) + recordPossibleType(boundGenericT->getGenericArgs()[0]); + } } break; } + case ExprKind::Dictionary: { + // Check context types of the dictionary literal expression. + ExprContextInfo dictCtxtInfo(DC, Parent); + + for (auto dictT : dictCtxtInfo.getPossibleTypes()) { + if (auto boundGenericT = dictT->getAs()) { + if (boundGenericT->getDecl() == Context.getDictionaryDecl()) { + if (ParsedExpr->isImplicit() && isa(ParsedExpr)) { + // let _: [Key : Value] = [#HERE#:] + // let _: [Key : Value] = [#HERE#:val] + // let _: [Key : Value] = [key:#HERE#] + // In this case, this is called by 'ExprKind::Tuple' case. Return + // '(Key,Value)' here, 'ExprKind::Tuple' branch can decide which + // type in the tuple type is the exprected type. + SmallVector elts; + for (auto genericArg : boundGenericT->getGenericArgs()) + elts.emplace_back(genericArg); + recordPossibleType(TupleType::get(elts, DC->getASTContext())); + } else { + // let _: [Key : Value] = [key: val, #HERE#] + // In this case, assume 'Key' is the expected type. + if (boundGenericT->getDecl() == Context.getDictionaryDecl()) + recordPossibleType(boundGenericT->getGenericArgs()[0]); + } + } + } + } + break; + } + case ExprKind::If: { + auto *IE = cast(Parent); + if (SM.rangeContains(IE->getCondExpr()->getSourceRange(), + ParsedExpr->getSourceRange())) { + recordPossibleType(Context.getBoolDecl()->getDeclaredInterfaceType()); + break; + } + ExprContextInfo ternaryCtxtInfo(DC, Parent); + for (auto ternaryT : ternaryCtxtInfo.getPossibleTypes()) + recordPossibleType(ternaryT); + break; + } case ExprKind::Assign: { auto *AE = cast(Parent); @@ -613,13 +678,25 @@ class ExprContextAnalyzer { break; } case ExprKind::Tuple: { - if (!Parent->getType() || !Parent->getType()->is()) - return; + TupleType *tupleT = nullptr; + if (Parent->getType() && Parent->getType()->is()) { + tupleT = Parent->getType()->castTo(); + } else { + ExprContextInfo tupleCtxtInfo(DC, Parent); + for (auto possibleT : tupleCtxtInfo.getPossibleTypes()) { + if (auto possibleTupleT = possibleT->getAs()) { + tupleT = possibleTupleT; + break; + } + } + if (!tupleT) + return; + } + unsigned Position = 0; bool HasName; if (getPositionInArgs(*DC, Parent, ParsedExpr, Position, HasName)) { - recordPossibleType( - Parent->getType()->castTo()->getElementType(Position)); + recordPossibleType(tupleT->getElementType(Position)); } break; } @@ -686,7 +763,7 @@ class ExprContextAnalyzer { switch (D->getKind()) { case DeclKind::PatternBinding: { auto PBD = cast(D); - for (unsigned I = 0; I < PBD->getNumPatternEntries(); ++I) { + for (unsigned I : range(PBD->getNumPatternEntries())) { if (auto Init = PBD->getInit(I)) { if (containsTarget(Init)) { if (PBD->getPattern(I)->hasType()) { @@ -743,8 +820,7 @@ class ExprContextAnalyzer { if (!AFD) return; auto param = AFD->getParameters()->get(initDC->getIndex()); - if (param->hasInterfaceType()) - recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType())); + recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType())); break; } } @@ -759,7 +835,7 @@ class ExprContextAnalyzer { /// in order to avoid a base expression affecting the type. However, now that /// we've typechecked, we will take the context type into account. static bool isSingleExpressionBodyForCodeCompletion(BraceStmt *body) { - return body->getNumElements() == 1 && body->getElements()[0].is(); + return body->getNumElements() == 1 && body->getFirstElement().is(); } public: @@ -796,7 +872,8 @@ class ExprContextAnalyzer { case ExprKind::PrefixUnary: case ExprKind::Assign: case ExprKind::Array: - return true; + case ExprKind::Dictionary: + case ExprKind::If: case ExprKind::UnresolvedMember: return true; case ExprKind::Tuple: { diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 89a27d52979ec..714116fce3ee1 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -564,6 +564,10 @@ class FormatWalker : public SourceEntityWalker { public: SourceLocIterator(TokenIt It) :It(It) {} SourceLocIterator(const SourceLocIterator& mit) : It(mit.It) {} + const SourceLocIterator &operator=(const SourceLocIterator &mit) { + It = mit.It; + return *this; + } SourceLocIterator& operator++() {++It; return *this;} SourceLocIterator operator++(int) { SourceLocIterator tmp(*this); diff --git a/lib/IDE/IDERequests.cpp b/lib/IDE/IDERequests.cpp index 69df1b361ac71..339d6398ae571 100644 --- a/lib/IDE/IDERequests.cpp +++ b/lib/IDE/IDERequests.cpp @@ -267,6 +267,12 @@ bool CursorInfoResolver::visitCallArgName(Identifier Name, ValueDecl *D) { if (isDone()) return false; + + // Handle invalid code where the called decl isn't actually callable, so this + // argument label doesn't really refer to it. + if (isa(D)) + return true; + bool Found = tryResolve(D, nullptr, nullptr, Range.getStart(), /*IsRef=*/true); if (Found) CursorInfo.IsKeywordArgument = true; @@ -486,7 +492,7 @@ struct RangeResolver::Implementation { // Unbox the brace statement to find its type. if (auto BS = dyn_cast(N.get())) { if (!BS->getElements().empty()) { - return resolveNodeType(BS->getElements().back(), + return resolveNodeType(BS->getLastElement(), RangeKind::SingleStatement); } } diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index bb79fb94b2f07..6d4050743df8f 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -85,7 +85,8 @@ PrintOptions PrintOptions::printDocInterface() { PrintOptions::ArgAndParamPrintingMode::BothAlways; result.PrintDocumentationComments = false; result.PrintRegularClangComments = false; - result.PrintFunctionRepresentationAttrs = false; + result.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; return result; } @@ -307,11 +308,10 @@ struct SynthesizedExtensionAnalyzer::Implementation { case RequirementKind::Conformance: { auto *M = DC->getParentModule(); auto *Proto = Second->castTo()->getDecl(); - if (!First->isTypeParameter() && - !First->is() && - !M->conformsToProtocol(First, Proto)) + if (!First->isTypeParameter() && !First->is() && + M->conformsToProtocol(First, Proto).isInvalid()) return true; - if (!M->conformsToProtocol(First, Proto)) + if (M->conformsToProtocol(First, Proto).isInvalid()) MergeInfo.addRequirement(GenericSig, First, Second, Kind); break; } @@ -760,8 +760,3 @@ Type swift::getResultTypeOfKeypathDynamicMember(SubscriptDecl *SD) { RootAndResultTypeOfKeypathDynamicMemberRequest{SD}, TypePair()). SecondTy; } - -bool swift::hasDynamicMemberLookupAttribute(Type ty) { - return evaluateOrDefault(ty->getASTContext().evaluator, - HasDynamicMemberLookupAttributeRequest{ty.getPointer()}, false); -} diff --git a/lib/IDE/ModuleInterfacePrinting.cpp b/lib/IDE/ModuleInterfacePrinting.cpp index e87deadb53b05..b061bebbfa362 100644 --- a/lib/IDE/ModuleInterfacePrinting.cpp +++ b/lib/IDE/ModuleInterfacePrinting.cpp @@ -448,7 +448,7 @@ void swift::ide::printSubmoduleInterface( auto RHSPath = RHS->getFullAccessPath(); for (unsigned i = 0, e = std::min(LHSPath.size(), RHSPath.size()); i != e; i++) { - if (int Ret = LHSPath[i].first.str().compare(RHSPath[i].first.str())) + if (int Ret = LHSPath[i].Item.str().compare(RHSPath[i].Item.str())) return Ret < 0; } return false; @@ -667,7 +667,7 @@ static SourceLoc getDeclStartPosition(SourceFile &File) { return false; }; - for (auto D : File.Decls) { + for (auto D : File.getTopLevelDecls()) { if (tryUpdateStart(D->getStartLoc())) { tryUpdateStart(D->getAttrs().getStartLoc()); auto RawComment = D->getRawComment(); diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 0b7db14712a67..247ddb4d0e473 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -205,21 +205,20 @@ doCodeCompletion(SourceFile &SF, StringRef EnteredCode, unsigned *BufferID, Ctx.SourceMgr.setCodeCompletionPoint(*BufferID, CodeCompletionOffset); // Parse, typecheck and temporarily insert the incomplete code into the AST. - const unsigned OriginalDeclCount = SF.Decls.size(); + const unsigned OriginalDeclCount = SF.getTopLevelDecls().size(); PersistentParserState PersistentState; bool Done; do { parseIntoSourceFile(SF, *BufferID, &Done, nullptr, &PersistentState); } while (!Done); - performTypeChecking(SF, PersistentState.getTopLevelContext(), None, - OriginalDeclCount); + performTypeChecking(SF, OriginalDeclCount); - performDelayedParsing(&SF, PersistentState, CompletionCallbacksFactory); + performCodeCompletionSecondPass(PersistentState, *CompletionCallbacksFactory); // Now we are done with code completion. Remove the declarations we // temporarily inserted. - SF.Decls.resize(OriginalDeclCount); + SF.truncateTopLevelDecls(OriginalDeclCount); // Reset the error state because it's only relevant to the code that we just // processed, which now gets thrown away. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 978a405cd8b75..9ce2bc7739024 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -1692,7 +1692,7 @@ findCollapseNestedIfTarget(ResolvedCursorInfo CursorInfo) { return {}; IfStmt *InnerIf = - dyn_cast_or_null(Body->getElement(0).dyn_cast()); + dyn_cast_or_null(Body->getFirstElement().dyn_cast()); if (!InnerIf) return {}; @@ -2119,8 +2119,8 @@ bool RefactoringActionConvertIfLetExprToGuardExpr::performChange() { auto Body = dyn_cast_or_null(If->getThenStmt()); // Get if-let then body. - auto firstElement = Body->getElements()[0]; - auto lastElement = Body->getElements().back(); + auto firstElement = Body->getFirstElement(); + auto lastElement = Body->getLastElement(); SourceRange bodyRange = firstElement.getSourceRange(); bodyRange.widen(lastElement.getSourceRange()); auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange); @@ -2138,8 +2138,8 @@ bool RefactoringActionConvertIfLetExprToGuardExpr::performChange() { // Get if-let else body. if (auto *ElseBody = dyn_cast_or_null(If->getElseStmt())) { - auto firstElseElement = ElseBody->getElements()[0]; - auto lastElseElement = ElseBody->getElements().back(); + auto firstElseElement = ElseBody->getFirstElement(); + auto lastElseElement = ElseBody->getLastElement(); SourceRange elseBodyRange = firstElseElement.getSourceRange(); elseBodyRange.widen(lastElseElement.getSourceRange()); auto ElseBodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, elseBodyRange); @@ -2225,9 +2225,9 @@ bool RefactoringActionConvertGuardExprToIfLetExpr::performChange() { // Get guard body auto Body = dyn_cast_or_null(Guard->getBody()); - if (Body && Body->getElements().size() > 1) { - auto firstElement = Body->getElements()[0]; - auto lastElement = Body->getElements().back(); + if (Body && Body->getNumElements() > 1) { + auto firstElement = Body->getFirstElement(); + auto lastElement = Body->getLastElement(); SourceRange bodyRange = firstElement.getSourceRange(); bodyRange.widen(lastElement.getSourceRange()); auto BodyCharRange = Lexer::getCharSourceRangeFromSourceRange(SM, bodyRange); @@ -2841,11 +2841,10 @@ collectMembersForInit(ResolvedCursorInfo CursorInfo, continue; } - auto &entry = patternBinding->getPatternEntryForVarDecl(varDecl); - bool isExplicitlyInitialized = - entry.isInitialized() && entry.getEqualLoc().isValid(); + const auto i = patternBinding->getPatternEntryIndexForVarDecl(varDecl); Expr *defaultInit = nullptr; - if (isExplicitlyInitialized || patternBinding->isDefaultInitializable()) { + if (patternBinding->isExplicitlyInitialized(i) || + patternBinding->isDefaultInitializable()) { defaultInit = varDecl->getParentInitializer(); } @@ -2940,30 +2939,36 @@ static NumberLiteralExpr *getTrailingNumberLiteral(ResolvedCursorInfo Tok) { // This cursor must point to the start of an expression. if (Tok.Kind != CursorInfoKind::ExprStart) return nullptr; - Expr *Parent = Tok.TrailingExpr; - assert(Parent); - - // Check if an expression is a number literal. - auto IsLiteralNumber = [&](Expr *E) -> NumberLiteralExpr* { - if (auto *NL = dyn_cast(E)) { - - // The sub-expression must have the same start loc with the outermost - // expression, i.e. the cursor position. - if (Parent->getStartLoc().getOpaquePointerValue() == - E->getStartLoc().getOpaquePointerValue()) { - return NL; - } - } - return nullptr; - }; + // For every sub-expression, try to find the literal expression that matches // our criteria. - for (auto Pair: Parent->getDepthMap()) { - if (auto Result = IsLiteralNumber(Pair.getFirst())) { - return Result; + class FindLiteralNumber : public ASTWalker { + Expr * const parent; + + public: + NumberLiteralExpr *found = nullptr; + + explicit FindLiteralNumber(Expr *parent) : parent(parent) { } + + std::pair walkToExprPre(Expr *expr) override { + if (auto *literal = dyn_cast(expr)) { + // The sub-expression must have the same start loc with the outermost + // expression, i.e. the cursor position. + if (!found && + parent->getStartLoc().getOpaquePointerValue() == + expr->getStartLoc().getOpaquePointerValue()) { + found = literal; + } + } + + return { found == nullptr, expr }; } - } - return nullptr; + }; + + auto parent = Tok.TrailingExpr; + FindLiteralNumber finder(parent); + parent->walk(finder); + return finder.found; } static std::string insertUnderscore(StringRef Text) { @@ -3161,6 +3166,97 @@ static bool rangeStartMayNeedRename(ResolvedRangeInfo Info) { } llvm_unreachable("unhandled kind"); } + +bool RefactoringActionConvertToComputedProperty:: +isApplicable(ResolvedRangeInfo Info, DiagnosticEngine &Diag) { + if (Info.Kind != RangeKind::SingleDecl) { + return false; + } + + if (Info.ContainedNodes.size() != 1) { + return false; + } + + auto D = Info.ContainedNodes[0].dyn_cast(); + if (!D) { + return false; + } + + auto Binding = dyn_cast(D); + if (!Binding) { + return false; + } + + auto SV = Binding->getSingleVar(); + if (!SV) { + return false; + } + + // willSet, didSet cannot be provided together with a getter + for (auto AD : SV->getAllAccessors()) { + if (AD->isObservingAccessor()) { + return false; + } + } + + // 'lazy' must not be used on a computed property + // NSCopying and IBOutlet attribute requires property to be mutable + auto Attributies = SV->getAttrs(); + if (Attributies.hasAttribute() || + Attributies.hasAttribute() || + Attributies.hasAttribute()) { + return false; + } + + // Property wrapper cannot be applied to a computed property + if (SV->hasAttachedPropertyWrapper()) { + return false; + } + + // has an initializer + return Binding->hasInitStringRepresentation(0); +} + +bool RefactoringActionConvertToComputedProperty::performChange() { + // Get an initialization + auto D = RangeInfo.ContainedNodes[0].dyn_cast(); + auto Binding = dyn_cast(D); + SmallString<128> scratch; + auto Init = Binding->getInitStringRepresentation(0, scratch); + + // Get type + auto SV = Binding->getSingleVar(); + auto SVType = SV->getType(); + auto TR = SV->getTypeReprOrParentPatternTypeRepr(); + + llvm::SmallString<64> DeclBuffer; + llvm::raw_svector_ostream OS(DeclBuffer); + llvm::StringRef Space = " "; + llvm::StringRef NewLine = "\n"; + + OS << tok::kw_var << Space; + // Add var name + OS << SV->getNameStr().str() << ":" << Space; + // For computed property must write a type of var + if (TR) { + OS << Lexer::getCharSourceRangeFromSourceRange(SM, TR->getSourceRange()).str(); + } else { + SVType.print(OS); + } + + OS << Space << tok::l_brace << NewLine; + // Add an initialization + OS << tok::kw_return << Space << Init.str() << NewLine; + OS << tok::r_brace; + + // Replace initializer to computed property + auto ReplaceStartLoc = Binding->getLoc(); + auto ReplaceEndLoc = Binding->getSourceRange().End; + auto ReplaceRange = SourceRange(ReplaceStartLoc, ReplaceEndLoc); + auto ReplaceCharSourceRange = Lexer::getCharSourceRangeFromSourceRange(SM, ReplaceRange); + EditConsumer.accept(SM, ReplaceCharSourceRange, DeclBuffer.str()); + return false; // success +} }// end of anonymous namespace StringRef swift::ide:: diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 364b3be704d20..e551ced63b7aa 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -74,7 +74,7 @@ class SemaAnnotator : public ASTWalker { bool passReference(ValueDecl *D, Type Ty, SourceLoc Loc, SourceRange Range, ReferenceMetaData Data); bool passReference(ValueDecl *D, Type Ty, DeclNameLoc Loc, ReferenceMetaData Data); - bool passReference(ModuleEntity Mod, std::pair IdLoc); + bool passReference(ModuleEntity Mod, Located IdLoc); bool passSubscriptReference(ValueDecl *D, SourceLoc Loc, ReferenceMetaData Data, bool IsOpenBracket); @@ -270,7 +270,7 @@ std::pair SemaAnnotator::walkToExprPre(Expr *E) { if (auto *DRE = dyn_cast(E)) { if (auto *module = dyn_cast(DRE->getDecl())) { if (!passReference(ModuleEntity(module), - std::make_pair(module->getName(), E->getLoc()))) + {module->getName(), E->getLoc()})) return { false, nullptr }; } else if (!passReference(DRE->getDecl(), DRE->getType(), DRE->getNameLoc(), @@ -489,11 +489,12 @@ bool SemaAnnotator::walkToTypeReprPre(TypeRepr *T) { if (auto IdT = dyn_cast(T)) { if (ValueDecl *VD = IdT->getBoundDecl()) { - if (auto *ModD = dyn_cast(VD)) - return passReference(ModD, std::make_pair(IdT->getIdentifier(), - IdT->getIdLoc())); + if (auto *ModD = dyn_cast(VD)) { + auto ident = IdT->getNameRef().getBaseIdentifier(); + return passReference(ModD, { ident, IdT->getLoc() }); + } - return passReference(VD, Type(), DeclNameLoc(IdT->getIdLoc()), + return passReference(VD, Type(), IdT->getNameLoc(), ReferenceMetaData(SemaReferenceKind::TypeRef, None)); } } @@ -668,11 +669,11 @@ passReference(ValueDecl *D, Type Ty, SourceLoc BaseNameLoc, SourceRange Range, } bool SemaAnnotator::passReference(ModuleEntity Mod, - std::pair IdLoc) { - if (IdLoc.second.isInvalid()) + Located IdLoc) { + if (IdLoc.Loc.isInvalid()) return true; - unsigned NameLen = IdLoc.first.getLength(); - CharSourceRange Range{ IdLoc.second, NameLen }; + unsigned NameLen = IdLoc.Item.getLength(); + CharSourceRange Range{ IdLoc.Loc, NameLen }; bool Continue = SEWalker.visitModuleReference(Mod, Range); if (!Continue) Cancelled = true; diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index e86f3782bb091..a60e6ab70e276 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -177,7 +177,7 @@ bool NameMatcher::handleCustomAttrs(Decl *D) { // CustomAttr's type, e.g. on `Wrapper` in `@Wrapper(wrappedValue: 10)`. SWIFT_DEFER { CustomAttrArg = None; }; if (Arg && !Arg->isImplicit()) - CustomAttrArg = {Repr->getLoc(), Arg}; + CustomAttrArg = Located(Arg, Repr->getLoc()); if (!Repr->walk(*this)) return false; } @@ -246,7 +246,7 @@ bool NameMatcher::walkToDeclPre(Decl *D) { } } else if (ImportDecl *ID = dyn_cast(D)) { for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) { - tryResolve(ASTWalker::ParentTy(D), Element.second); + tryResolve(ASTWalker::ParentTy(D), Element.Loc); if (isDone()) break; } @@ -416,9 +416,9 @@ bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { if (isa(T)) { // If we're walking a CustomAttr's type we may have an associated call // argument to resolve with from its semantic initializer. - if (CustomAttrArg.hasValue() && CustomAttrArg->first == T->getLoc()) { + if (CustomAttrArg.hasValue() && CustomAttrArg->Loc == T->getLoc()) { tryResolve(ASTWalker::ParentTy(T), T->getLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->second, LabelRangeEndAt::BeforeElemStart)); + getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->Item, LabelRangeEndAt::BeforeElemStart)); } else { tryResolve(ASTWalker::ParentTy(T), T->getLoc()); } @@ -764,18 +764,21 @@ void swift::ide::getLocationInfo(const ValueDecl *VD, auto ClangNode = VD->getClangNode(); if (VD->getLoc().isValid()) { + auto getSignatureRange = [&](const ValueDecl *VD) -> Optional { + if (auto FD = dyn_cast(VD)) { + SourceRange R = FD->getSignatureSourceRange(); + if (R.isValid()) + return getCharLength(SM, R); + } + return None; + }; unsigned NameLen; - if (auto FD = dyn_cast(VD)) { - SourceRange R = FD->getSignatureSourceRange(); - if (R.isInvalid()) - return; - NameLen = getCharLength(SM, R); + if (auto SigLen = getSignatureRange(VD)) { + NameLen = SigLen.getValue(); + } else if (VD->hasName()) { + NameLen = VD->getBaseName().userFacingName().size(); } else { - if (VD->hasName()) { - NameLen = VD->getBaseName().userFacingName().size(); - } else { - NameLen = getCharLength(SM, VD->getLoc()); - } + NameLen = getCharLength(SM, VD->getLoc()); } unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc()); diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index d1640d80ef029..faa7da133ab17 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -15,6 +15,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Module.h" @@ -56,7 +57,7 @@ struct SyntaxModelContext::Implementation { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and /// number of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchImageOrFileLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 5; if (Tokens.size() < NUM_TOKENS) @@ -75,8 +76,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { if (Tokens[1].getText() != "resourceName") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } /// Matches the tokens in the argument of an image literal expression if its @@ -85,7 +85,7 @@ matchImageOrFileLiteralArg(ArrayRef Tokens) { /// If the given tokens start with the expected tokens and they all appear on /// the same line, the source location beyond the final matched token and number /// of matched tokens are returned. Otherwise None is returned. -static Optional> +static Optional> matchColorLiteralArg(ArrayRef Tokens) { const unsigned NUM_TOKENS = 17; if (Tokens.size() < NUM_TOKENS) @@ -111,8 +111,7 @@ matchColorLiteralArg(ArrayRef Tokens) { Tokens[9].getText() != "blue" || Tokens[13].getText() != "alpha") return None; auto EndToken = Tokens[NUM_TOKENS-1]; - return std::make_pair(EndToken.getLoc().getAdvancedLoc(EndToken.getLength()), - NUM_TOKENS); + return Located(NUM_TOKENS, EndToken.getLoc().getAdvancedLoc(EndToken.getLength())); } SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) @@ -171,9 +170,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_imageLiteral: if (auto Match = matchImageOrFileLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the extra matched tokens - I += Match->second - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } @@ -181,9 +180,9 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile) case tok::pound_colorLiteral: if (auto Match = matchColorLiteralArg(Tokens.slice(I+1))) { Kind = SyntaxNodeKind::ObjectLiteral; - Length = SM.getByteDistance(Loc, Match->first); + Length = SM.getByteDistance(Loc, Match->Loc); // skip over the matches tokens - I += Match->second - 1; + I += Match->Item - 1; } else { Kind = SyntaxNodeKind::Keyword; } @@ -346,6 +345,21 @@ class ModelASTWalker : public ASTWalker { /// is considered as one, e.g. object literal expression. uint8_t AvoidPassingSyntaxToken = 0; + class InactiveClauseRAII { + const bool wasInInactiveClause; + bool &isInInactiveClause; + + public: + InactiveClauseRAII(bool &isInInactiveClauseArg, bool enteringInactiveClause) + : wasInInactiveClause(isInInactiveClauseArg), + isInInactiveClause(isInInactiveClauseArg) { + isInInactiveClause |= enteringInactiveClause; + } + ~InactiveClauseRAII() { isInInactiveClause = wasInInactiveClause; } + }; + friend class InactiveClauseRAII; + bool inInactiveClause = false; + public: SyntaxModelWalker &Walker; ArrayRef TokenNodes; @@ -888,9 +902,6 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) { pushStructureNode(SN, NTD); } else if (auto *ED = dyn_cast(D)) { - // Normally bindExtension() would take care of computing the extended - // nominal. It must be done before asking for generic parameters. - ED->computeExtendedNominal(); SyntaxStructureNode SN; setDecl(SN, D); SN.Kind = SyntaxStructureKind::Extension; @@ -967,6 +978,7 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) { if (Clause.Cond && !annotateIfConfigConditionIdentifiers(Clause.Cond)) return false; + InactiveClauseRAII inactiveClauseRAII(inInactiveClause, !Clause.isActive); for (auto &Element : Clause.Elements) { if (auto *E = Element.dyn_cast()) { E->walk(*this); @@ -1094,11 +1106,11 @@ bool ModelASTWalker::walkToTypeReprPre(TypeRepr *T) { return false; } else if (auto IdT = dyn_cast(T)) { - if (!passTokenNodesUntil(IdT->getIdLoc(), + if (!passTokenNodesUntil(IdT->getStartLoc(), ExcludeNodeAtLocation).shouldContinue) return false; if (TokenNodes.empty() || - TokenNodes.front().Range.getStart() != IdT->getIdLoc()) + TokenNodes.front().Range.getStart() != IdT->getStartLoc()) return false; if (!passNode({SyntaxNodeKind::TypeId, TokenNodes.front().Range})) return false; diff --git a/lib/IDE/TypeContextInfo.cpp b/lib/IDE/TypeContextInfo.cpp index 5d5e0eb3350a4..6da9445b3ff2b 100644 --- a/lib/IDE/TypeContextInfo.cpp +++ b/lib/IDE/TypeContextInfo.cpp @@ -130,7 +130,6 @@ void ContextInfoCallbacks::getImplicitMembers( class LocalConsumer : public VisibleDeclConsumer { DeclContext *DC; - LazyResolver *TypeResolver; ModuleDecl *CurModule; Type T; SmallVectorImpl &Result; @@ -157,8 +156,7 @@ void ContextInfoCallbacks::getImplicitMembers( public: LocalConsumer(DeclContext *DC, Type T, SmallVectorImpl &Result) - : DC(DC), TypeResolver(DC->getASTContext().getLazyResolver()), - CurModule(DC->getParentModule()), T(T), Result(Result) {} + : DC(DC), CurModule(DC->getParentModule()), T(T), Result(Result) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, DynamicLookupInfo) { diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index ec7393c37633a..a52c6570e178f 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -976,3 +976,55 @@ ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) { return ClangNode(); } + +std::pair swift::ide::getReferencedDecl(Expr *expr) { + auto exprTy = expr->getType(); + + // Look through unbound instance member accesses. + if (auto *dotSyntaxExpr = dyn_cast(expr)) + expr = dotSyntaxExpr->getRHS(); + + // Look through the 'self' application. + if (auto *selfApplyExpr = dyn_cast(expr)) + expr = selfApplyExpr->getFn(); + + // Look through curry thunks. + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + if (isa(body) && + closure->getParameters()->size() == 1) + expr = closure->getSingleExpressionBody(); + } + } + + if (auto *closure = dyn_cast(expr)) { + if (closure->isThunk()) { + auto *body = closure->getSingleExpressionBody(); + body = body->getSemanticsProvidingExpr(); + if (auto *outerCall = dyn_cast(body)) { + if (auto *innerCall = dyn_cast(outerCall->getFn())) { + if (auto *declRef = dyn_cast(innerCall->getFn())) { + expr = declRef; + } + } + } + } + } + + // If this is an IUO result, unwrap the optional type. + auto refDecl = expr->getReferencedDecl(); + if (!refDecl) { + if (auto *applyExpr = dyn_cast(expr)) { + auto fnDecl = applyExpr->getFn()->getReferencedDecl(); + if (auto *func = fnDecl.getDecl()) { + if (func->isImplicitlyUnwrappedOptional()) { + if (auto objectTy = exprTy->getOptionalObjectType()) + exprTy = objectTy; + } + } + } + } + + return std::make_pair(exprTy, refDecl); +} diff --git a/lib/IRGen/AllocStackHoisting.cpp b/lib/IRGen/AllocStackHoisting.cpp index 1551b32f10f99..e4c125fdbe1cd 100644 --- a/lib/IRGen/AllocStackHoisting.cpp +++ b/lib/IRGen/AllocStackHoisting.cpp @@ -21,6 +21,7 @@ #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILArgument.h" +#include "swift/AST/SemanticAttrs.h" #include "IRGenModule.h" #include "NonFixedTypeInfo.h" @@ -349,7 +350,7 @@ bool indicatesDynamicAvailabilityCheckUse(SILInstruction *I) { auto *Apply = dyn_cast(I); if (!Apply) return false; - if (Apply->hasSemantics("availability.osversion")) + if (Apply->hasSemantics(semantics::AVAILABILITY_OSVERSION)) return true; auto *FunRef = Apply->getReferencedFunctionOrNull(); if (!FunRef) diff --git a/lib/IRGen/ClassLayout.cpp b/lib/IRGen/ClassLayout.cpp index 450f90af3982d..65adf4e9f1483 100644 --- a/lib/IRGen/ClassLayout.cpp +++ b/lib/IRGen/ClassLayout.cpp @@ -29,12 +29,14 @@ ClassLayout::ClassLayout(const StructLayoutBuilder &builder, llvm::Type *classTy, ArrayRef allStoredProps, ArrayRef allFieldAccesses, - ArrayRef allElements) + ArrayRef allElements, + Size headerSize) : MinimumAlign(builder.getAlignment()), MinimumSize(builder.getSize()), IsFixedLayout(builder.isFixedLayout()), Options(options), Ty(classTy), + HeaderSize(headerSize), AllStoredProperties(allStoredProps), AllFieldAccesses(allFieldAccesses), AllElements(allElements) { } @@ -52,6 +54,26 @@ Size ClassLayout::getInstanceStart() const { // FIXME: assumes layout is always sequential! return element.getByteOffset(); } else { + // We used to crash for classes that have an empty and a resilient field + // during intialization. + // class CrashInInit { + // var empty = EmptyStruct() + // var resilient = ResilientThing() + // } + // What happened was that for such a class we we would compute a + // instanceStart of 0. The shared cache builder would then slide the value + // of the constant ivar offset for the empty field from 0 to 16. However + // the field offset for empty fields is assume to be zero and the runtime + // does not compute a different value for the empty field and so the field + // offset for the empty field stays 0. The runtime then trys to reconcile + // the field offset and the ivar offset trying to write to the ivar + // offset. However, the ivar offset is marked as constant and so we + // crashed. + // This can be avoided by correctly computing the instanceStart for such a + // class to be 16 such that the shared cache builder does not update the + // value of the empty field. + if (!Options.contains(ClassMetadataFlags::ClassHasObjCAncestry)) + return HeaderSize; return Size(0); } } diff --git a/lib/IRGen/ClassLayout.h b/lib/IRGen/ClassLayout.h index 084dcea421b77..014e518a07433 100644 --- a/lib/IRGen/ClassLayout.h +++ b/lib/IRGen/ClassLayout.h @@ -114,6 +114,9 @@ class ClassLayout { /// The LLVM type for instances of this class. llvm::Type *Ty; + /// The header size of this class. + Size HeaderSize; + /// Lazily-initialized array of all fragile stored properties directly defined /// in the class itself. ArrayRef AllStoredProperties; @@ -131,7 +134,8 @@ class ClassLayout { llvm::Type *classTy, ArrayRef allStoredProps, ArrayRef allFieldAccesses, - ArrayRef allElements); + ArrayRef allElements, + Size headerSize); Size getInstanceStart() const; diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 65492f9625ec1..3c82701e78448 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -204,8 +204,13 @@ class ClassMetadataScanner : public ClassMetadataVisitor { addPointer(); } } - void addGenericArgument(ClassDecl *forClass) { addPointer(); } - void addGenericWitnessTable(ClassDecl *forClass) { addPointer(); } + void addGenericArgument(GenericRequirement requirement, ClassDecl *forClass) { + addPointer(); + } + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { + addPointer(); + } void addPlaceholder(MissingMemberDecl *MMD) { for (auto i : range(MMD->getNumberOfVTableEntries())) { (void)i; diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index 393b4a4eadc01..34fa19bd537f0 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -154,7 +154,7 @@ LLVM_DUMP_METHOD void DebugTypeInfo::dump() const { llvm::errs() << "[Size " << size.getValue() << " Alignment " << align.getValue() << "] "; - getType()->dump(); + getType()->dump(llvm::errs()); if (StorageType) { llvm::errs() << "StorageType="; StorageType->dump(); diff --git a/lib/IRGen/EnumMetadataVisitor.h b/lib/IRGen/EnumMetadataVisitor.h index 30bd8da890121..ab6690c42dac8 100644 --- a/lib/IRGen/EnumMetadataVisitor.h +++ b/lib/IRGen/EnumMetadataVisitor.h @@ -82,8 +82,8 @@ class EnumMetadataScanner : public EnumMetadataVisitor { void addMetadataFlags() { addPointer(); } void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void addPayloadSize() { addPointer(); } void noteStartOfTypeSpecificMembers() {} diff --git a/lib/IRGen/Fulfillment.cpp b/lib/IRGen/Fulfillment.cpp index 52fcc5b4f50eb..065aa8cd7e931 100644 --- a/lib/IRGen/Fulfillment.cpp +++ b/lib/IRGen/Fulfillment.cpp @@ -267,36 +267,35 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM, GenericTypeRequirements requirements(IGM, nominal); requirements.enumerateFulfillments( IGM, type->getContextSubstitutionMap(IGM.getSwiftModule(), nominal), - [&](unsigned reqtIndex, CanType arg, - Optional conf) { - // Skip uninteresting type arguments. - if (!keys.hasInterestingType(arg)) - return; - - // If the fulfilled value is type metadata, refine the path. - if (!conf) { - auto argState = getPresumedMetadataStateForTypeArgument(metadataState); - MetadataPath argPath = path; - argPath.addNominalTypeArgumentComponent(reqtIndex); - hadFulfillment |= - searchTypeMetadata(IGM, arg, IsExact, argState, source, - std::move(argPath), keys); - return; - } - - // Otherwise, it's a conformance. - - // Ignore it unless the type itself is interesting. - if (!keys.isInterestingType(arg)) - return; - - // Refine the path. - MetadataPath argPath = path; - argPath.addNominalTypeArgumentConformanceComponent(reqtIndex); - - hadFulfillment |= searchWitnessTable(IGM, arg, conf->getRequirement(), - source, std::move(argPath), keys); - }); + [&](unsigned reqtIndex, CanType arg, ProtocolConformanceRef conf) { + // Skip uninteresting type arguments. + if (!keys.hasInterestingType(arg)) + return; + + // If the fulfilled value is type metadata, refine the path. + if (conf.isInvalid()) { + auto argState = + getPresumedMetadataStateForTypeArgument(metadataState); + MetadataPath argPath = path; + argPath.addNominalTypeArgumentComponent(reqtIndex); + hadFulfillment |= searchTypeMetadata( + IGM, arg, IsExact, argState, source, std::move(argPath), keys); + return; + } + + // Otherwise, it's a conformance. + + // Ignore it unless the type itself is interesting. + if (!keys.isInterestingType(arg)) + return; + + // Refine the path. + MetadataPath argPath = path; + argPath.addNominalTypeArgumentConformanceComponent(reqtIndex); + + hadFulfillment |= searchWitnessTable(IGM, arg, conf.getRequirement(), + source, std::move(argPath), keys); + }); return hadFulfillment; } diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp index a9afa0d8a5313..5601d015a1a30 100644 --- a/lib/IRGen/GenArchetype.cpp +++ b/lib/IRGen/GenArchetype.cpp @@ -189,7 +189,7 @@ llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF, // concretely available; we really ought to be comparing the full paths // to this conformance from concrete sources. - auto signature = environment->getGenericSignature()->getCanonicalSignature(); + auto signature = environment->getGenericSignature().getCanonicalSignature(); auto archetypeDepType = archetype->getInterfaceType(); auto astPath = signature->getConformanceAccessPath(archetypeDepType, @@ -414,21 +414,22 @@ withOpaqueTypeGenericArgs(IRGenFunction &IGF, } else { SmallVector args; SmallVector types; - + enumerateGenericSignatureRequirements( - opaqueDecl->getGenericSignature()->getCanonicalSignature(), - [&](GenericRequirement reqt) { - auto ty = reqt.TypeParameter.subst(archetype->getSubstitutions()) - ->getCanonicalType(opaqueDecl->getGenericSignature()); - if (reqt.Protocol) { - auto ref = ProtocolConformanceRef(reqt.Protocol) - .subst(reqt.TypeParameter, archetype->getSubstitutions()); - args.push_back(emitWitnessTableRef(IGF, ty, ref)); - } else { - args.push_back(IGF.emitAbstractTypeMetadataRef(ty)); - } - types.push_back(args.back()->getType()); - }); + opaqueDecl->getGenericSignature().getCanonicalSignature(), + [&](GenericRequirement reqt) { + auto ty = reqt.TypeParameter.subst(archetype->getSubstitutions()) + ->getCanonicalType(opaqueDecl->getGenericSignature()); + if (reqt.Protocol) { + auto ref = + ProtocolConformanceRef(reqt.Protocol) + .subst(reqt.TypeParameter, archetype->getSubstitutions()); + args.push_back(emitWitnessTableRef(IGF, ty, ref)); + } else { + args.push_back(IGF.emitAbstractTypeMetadataRef(ty)); + } + types.push_back(args.back()->getType()); + }); auto bufTy = llvm::StructType::get(IGF.IGM.LLVMContext, types); alloca = IGF.createAlloca(bufTy, IGF.IGM.getPointerAlignment()); allocaSize = IGF.IGM.getPointerSize() * args.size(); @@ -452,21 +453,18 @@ withOpaqueTypeGenericArgs(IRGenFunction &IGF, bool shouldUseOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { auto *namingDecl = opaque->getNamingDecl(); - auto *abstractStorage = dyn_cast(namingDecl); // Don't emit accessors for abstract storage that is not dynamic or a dynamic // replacement. - if (abstractStorage) { + if (auto *abstractStorage = dyn_cast(namingDecl)) { return abstractStorage->hasAnyNativeDynamicAccessors() || - abstractStorage->hasAnyDynamicReplacementAccessors(); + abstractStorage->getDynamicallyReplacedDecl(); } // Don't emit accessors for functions that are not dynamic or dynamic // replacements. - return opaque->getNamingDecl()->isNativeDynamic() || - opaque->getNamingDecl() - ->getAttrs() - .hasAttribute(); + return namingDecl->isNativeDynamic() || + (bool)namingDecl->getDynamicallyReplacedDecl(); } static llvm::Value * diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 89ce5bf08e45b..8dbfb7847f052 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -692,7 +692,7 @@ namespace { case clang::Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) \ case clang::Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("canonical or dependent type in ABI lowering"); // These shouldn't occur in expandable struct types. @@ -1066,7 +1066,8 @@ void SignatureExpansion::expandExternalSignatureTypes() { assert(FnType->getLanguage() == SILFunctionLanguage::C); // Convert the SIL result type to a Clang type. - auto clangResultTy = IGM.getClangType(FnType->getFormalCSemanticResult()); + auto clangResultTy = + IGM.getClangType(FnType->getFormalCSemanticResult(IGM.getSILModule())); // Now convert the parameters to Clang types. auto params = FnType->getParameters(); @@ -1079,7 +1080,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { // ObjC methods take their 'self' argument first, followed by an // implicit _cmd argument. auto &self = params.back(); - auto clangTy = IGM.getClangType(self); + auto clangTy = IGM.getClangType(self, FnType); paramTys.push_back(clangTy); paramTys.push_back(clangCtx.VoidPtrTy); params = params.drop_back(); @@ -1110,7 +1111,7 @@ void SignatureExpansion::expandExternalSignatureTypes() { // Convert each parameter to a Clang type. for (auto param : params) { - auto clangTy = IGM.getClangType(param); + auto clangTy = IGM.getClangType(param, FnType); paramTys.push_back(clangTy); } @@ -1310,7 +1311,7 @@ bool irgen::hasSelfContextParameter(CanSILFunctionType fnType) { } // Direct conventions depend on the type. - CanType type = param.getType(); + CanType type = param.getInterfaceType(); // Thick or @objc metatypes (but not existential metatypes). if (auto metatype = dyn_cast(type)) { @@ -1471,7 +1472,7 @@ Signature SignatureExpansion::getSignature() { Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType) { - GenericContextScope scope(IGM, formalType->getGenericSignature()); + GenericContextScope scope(IGM, formalType->getInvocationGenericSignature()); SignatureExpansion expansion(IGM, formalType); expansion.expandFunctionType(); return expansion.getSignature(); @@ -1534,7 +1535,7 @@ void CallEmission::emitToUnmappedExplosion(Explosion &out) { // Specially handle noreturn c function which would return a 'Never' SIL result // type. if (origFnType->getLanguage() == SILFunctionLanguage::C && - origFnType->isNoReturnFunction()) { + origFnType->isNoReturnFunction(IGF.getSILModule())) { auto clangResultTy = result->getType(); extractScalarResults(IGF, clangResultTy, result, out); return; @@ -1680,8 +1681,10 @@ void CallEmission::emitToMemory(Address addr, // result that's actually being passed indirectly. // // TODO: SIL address lowering should be able to handle such cases earlier. - auto origResultType = origFnType->getDirectFormalResultsType().getASTType(); - auto substResultType = substFnType->getDirectFormalResultsType().getASTType(); + auto origResultType = origFnType->getDirectFormalResultsType(IGF.IGM.getSILModule()) + .getASTType(); + auto substResultType = substFnType->getDirectFormalResultsType(IGF.IGM.getSILModule()) + .getASTType(); if (origResultType->hasTypeParameter()) origResultType = IGF.IGM.getGenericEnvironment() @@ -1809,7 +1812,7 @@ void CallEmission::emitToExplosion(Explosion &out, bool isOutlined) { auto origFnType = getCallee().getOrigFunctionType(); auto isNoReturnCFunction = origFnType->getLanguage() == SILFunctionLanguage::C && - origFnType->isNoReturnFunction(); + origFnType->isNoReturnFunction(IGF.getSILModule()); // If the call is naturally to memory, emit it that way and then // explode that temporary. @@ -1884,7 +1887,8 @@ Callee::Callee(CalleeInfo &&info, const FunctionPointer &fn, break; case SILFunctionTypeRepresentation::Method: case SILFunctionTypeRepresentation::WitnessMethod: - assert((FirstData != nullptr) == hasSelfContextParameter(Info.OrigFnType)); + assert((FirstData != nullptr) == + hasSelfContextParameter(Info.OrigFnType)); assert(!SecondData); break; case SILFunctionTypeRepresentation::Thick: @@ -2275,8 +2279,8 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, Explosion &in, Explosion &out, TemporarySet &temporaries, bool isOutlined) { - auto silConv = IGF.IGM.silConv; auto fnType = callee.getOrigFunctionType(); + auto silConv = SILFunctionConventions(fnType, IGF.IGM.silConv); auto params = fnType->getParameters(); assert(callee.getForeignInfo().ClangInfo); @@ -2384,7 +2388,9 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, } /// Returns whether allocas are needed. -bool irgen::addNativeArgument(IRGenFunction &IGF, Explosion &in, +bool irgen::addNativeArgument(IRGenFunction &IGF, + Explosion &in, + CanSILFunctionType fnTy, SILParameterInfo origParamInfo, Explosion &out, bool isOutlined) { // Addresses consist of a single pointer argument. @@ -2392,7 +2398,7 @@ bool irgen::addNativeArgument(IRGenFunction &IGF, Explosion &in, out.add(in.claimNext()); return false; } - auto paramType = IGF.IGM.silConv.getSILType(origParamInfo); + auto paramType = IGF.IGM.silConv.getSILType(origParamInfo, fnTy); auto &ti = cast(IGF.getTypeInfo(paramType)); auto schema = ti.getSchema(); auto &nativeSchema = ti.nativeParameterValueSchema(IGF.IGM); @@ -2646,7 +2652,8 @@ llvm::Value *irgen::emitYield(IRGenFunction &IGF, // Translate the arguments to an unsubstituted form. Explosion allComponents; for (auto yield : coroutineType->getYields()) - addNativeArgument(IGF, substValues, yield, allComponents, false); + addNativeArgument(IGF, substValues, coroutineType, + yield, allComponents, false); // Figure out which arguments need to be yielded directly. SmallVector yieldArgs; @@ -2779,7 +2786,8 @@ void CallEmission::setArgs(Explosion &original, bool isOutlined, params = params.drop_back(); } for (auto param : params) { - addNativeArgument(IGF, original, param, adjusted, isOutlined); + addNativeArgument(IGF, original, + origCalleeType, param, adjusted, isOutlined); } // Anything else, just pass along. This will include things like @@ -3339,10 +3347,43 @@ Explosion NativeConventionSchema::mapIntoNative(IRGenModule &IGM, return nativeExplosion; } -void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, +Explosion IRGenFunction::coerceValueTo(SILType fromTy, Explosion &from, + SILType toTy) { + if (fromTy == toTy) + return std::move(from); + + auto &fromTI = cast(IGM.getTypeInfo(fromTy)); + auto &toTI = cast(IGM.getTypeInfo(toTy)); + + Explosion result; + if (fromTI.getStorageType()->isPointerTy() && + toTI.getStorageType()->isPointerTy()) { + auto ptr = from.claimNext(); + ptr = Builder.CreateBitCast(ptr, toTI.getStorageType()); + result.add(ptr); + return result; + } + + auto temporary = toTI.allocateStack(*this, toTy, "coerce.temp"); + + auto addr = + Address(Builder.CreateBitCast(temporary.getAddressPointer(), + fromTI.getStorageType()->getPointerTo()), + temporary.getAlignment()); + fromTI.initialize(*this, from, addr, false); + + toTI.loadAsTake(*this, temporary.getAddress(), result); + toTI.deallocateStack(*this, temporary, toTy); + return result; +} + +void IRGenFunction::emitScalarReturn(SILType returnResultType, + SILType funcResultType, Explosion &result, bool isSwiftCCReturn, bool isOutlined) { if (result.empty()) { - assert(IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGM).empty() && + assert(IGM.getTypeInfo(returnResultType) + .nativeReturnValueSchema(IGM) + .empty() && "Empty explosion must match the native calling convention"); Builder.CreateRetVoid(); @@ -3351,12 +3392,13 @@ void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, // In the native case no coercion is needed. if (isSwiftCCReturn) { + result = coerceValueTo(returnResultType, result, funcResultType); auto &nativeSchema = - IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGM); + IGM.getTypeInfo(funcResultType).nativeReturnValueSchema(IGM); assert(!nativeSchema.requiresIndirect()); - Explosion native = - nativeSchema.mapIntoNative(IGM, *this, result, resultType, isOutlined); + Explosion native = nativeSchema.mapIntoNative(IGM, *this, result, + funcResultType, isOutlined); if (native.size() == 1) { Builder.CreateRet(native.claimNext()); return; @@ -3383,7 +3425,7 @@ void IRGenFunction::emitScalarReturn(SILType resultType, Explosion &result, return; } - auto &resultTI = IGM.getTypeInfo(resultType); + auto &resultTI = IGM.getTypeInfo(returnResultType); auto schema = resultTI.getSchema(); auto *bodyType = schema.getScalarResultType(IGM); diff --git a/lib/IRGen/GenCall.h b/lib/IRGen/GenCall.h index c1c50f3fa0883..c8e3a2c54dd19 100644 --- a/lib/IRGen/GenCall.h +++ b/lib/IRGen/GenCall.h @@ -96,7 +96,9 @@ namespace irgen { SILType swiftType, const LoadableTypeInfo &swiftTI); - bool addNativeArgument(IRGenFunction &IGF, Explosion &in, + bool addNativeArgument(IRGenFunction &IGF, + Explosion &in, + CanSILFunctionType fnTy, SILParameterInfo origParamInfo, Explosion &args, bool isOutlined); diff --git a/lib/IRGen/GenCast.cpp b/lib/IRGen/GenCast.cpp index 5ab80e382fbfd..5054b5344303f 100644 --- a/lib/IRGen/GenCast.cpp +++ b/lib/IRGen/GenCast.cpp @@ -164,7 +164,7 @@ getDynamicCastArguments(IRGenFunction &IGF, /// Emit a checked unconditional downcast of a class value. llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, - SILType toType, CheckedCastMode mode) { + CanType toType, CheckedCastMode mode) { // Emit the value we're casting from. if (from->getType() != IGF.IGM.Int8PtrTy) from = IGF.Builder.CreateBitOrPointerCast(from, IGF.IGM.Int8PtrTy); @@ -174,11 +174,19 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, llvm::Value *metadataRef; llvm::Constant *castFn; + // If true, the target class is not known at compile time because it is a + // class-bounded archetype or the dynamic Self type. + bool nonSpecificClass = false; + // Get the best known type information about the destination type. ClassDecl *destClass = nullptr; - if (auto archetypeTy = toType.getAs()) { + if (auto archetypeTy = dyn_cast(toType)) { + nonSpecificClass = true; if (auto superclassTy = archetypeTy->getSuperclass()) destClass = superclassTy->getClassOrBoundGenericClass(); + } else if (auto selfTy = dyn_cast(toType)) { + nonSpecificClass = true; + destClass = selfTy->getSelfType()->getClassOrBoundGenericClass(); } else { destClass = toType.getClassOrBoundGenericClass(); assert(destClass != nullptr); @@ -187,7 +195,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, // If the destination type is known to have a Swift-compatible // implementation, use the most specific entrypoint. if (destClass && destClass->hasKnownSwiftImplementation()) { - metadataRef = IGF.emitTypeMetadataRef(toType.getASTType()); + metadataRef = IGF.emitTypeMetadataRef(toType); switch (mode) { case CheckedCastMode::Unconditional: @@ -200,9 +208,9 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, // If the destination type is a CF type or a non-specific // class-bounded archetype, use the most general cast entrypoint. - } else if (toType.is() || + } else if (nonSpecificClass || destClass->getForeignClassKind()==ClassDecl::ForeignKind::CFType) { - metadataRef = IGF.emitTypeMetadataRef(toType.getASTType()); + metadataRef = IGF.emitTypeMetadataRef(toType); switch (mode) { case CheckedCastMode::Unconditional: @@ -249,7 +257,7 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, call->setCallingConv(cc); call->setDoesNotThrow(); - llvm::Type *subTy = IGF.getTypeInfo(toType).getStorageType(); + llvm::Type *subTy = IGF.getTypeInfoForUnlowered(toType).getStorageType(); return IGF.Builder.CreateBitCast(call, subTy); } @@ -638,8 +646,12 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, bool checkSuperclassConstraint = false; if (hasSuperclassConstraint) { Type srcSuperclassType = srcInstanceType; - if (srcSuperclassType->isExistentialType()) + if (srcSuperclassType->isExistentialType()) { srcSuperclassType = srcSuperclassType->getSuperclass(); + // Look for an AnyObject superclass (getSuperclass() returns nil). + if (!srcSuperclassType && srcInstanceType->isClassExistentialType()) + checkSuperclassConstraint = true; + } if (srcSuperclassType) { checkSuperclassConstraint = !destInstanceType->getSuperclass()->isExactSuperclassOf( @@ -867,12 +879,14 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, /// isn't exposed. void irgen::emitScalarCheckedCast(IRGenFunction &IGF, Explosion &value, - SILType sourceType, - SILType targetType, + SILType sourceLoweredType, + CanType sourceFormalType, + SILType targetLoweredType, + CanType targetFormalType, CheckedCastMode mode, Explosion &out) { - assert(sourceType.isObject()); - assert(targetType.isObject()); + assert(sourceLoweredType.isObject()); + assert(targetLoweredType.isObject()); llvm::BasicBlock *nilCheckBB = nullptr; llvm::BasicBlock *nilMergeBB = nullptr; @@ -902,22 +916,23 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, } }; - if (auto sourceOptObjectType = sourceType.getOptionalObjectType()) { + if (auto sourceOptObjectType = sourceLoweredType.getOptionalObjectType()) { // Translate the value from an enum representation to a possibly-null // representation. Note that we assume that this projection is safe // for the particular case of an optional class-reference or metatype // value. Explosion optValue; auto someDecl = IGF.IGM.Context.getOptionalSomeDecl(); - emitProjectLoadableEnum(IGF, sourceType, value, someDecl, optValue); + emitProjectLoadableEnum(IGF, sourceLoweredType, value, someDecl, optValue); assert(value.empty()); value = std::move(optValue); - sourceType = sourceOptObjectType; + sourceLoweredType = sourceOptObjectType; + sourceFormalType = sourceFormalType.getOptionalObjectType(); // We need a null-check because the runtime function can't handle null in // some of the cases. - if (targetType.isExistentialType()) { + if (targetLoweredType.isExistentialType()) { auto &Builder = IGF.Builder; auto val = value.getAll()[0]; auto isNotNil = Builder.CreateICmpNE( @@ -934,7 +949,7 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, // If the source value is a metatype, either do a metatype-to-metatype // cast or cast it to an object instance and continue. - if (auto sourceMetatype = sourceType.getAs()) { + if (auto sourceMetatype = sourceLoweredType.getAs()) { llvm::Value *metatypeVal = nullptr; if (sourceMetatype->getRepresentation() != MetatypeRepresentation::Thin) metatypeVal = value.claimNext(); @@ -948,37 +963,36 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, SmallVector protocols; // Casts to existential metatypes. - if (auto existential = targetType.getAs()) { - emitScalarExistentialDowncast(IGF, metatypeVal, sourceType, targetType, - mode, existential->getRepresentation(), + if (auto existential = targetLoweredType.getAs()) { + emitScalarExistentialDowncast(IGF, metatypeVal, sourceLoweredType, + targetLoweredType, mode, + existential->getRepresentation(), out); return; // Casts to concrete metatypes. - } else if (auto destMetaType = targetType.getAs()) { + } else if (auto destMetaType = targetLoweredType.getAs()) { emitMetatypeDowncast(IGF, metatypeVal, destMetaType, mode, out); return; } // Otherwise, this is a metatype-to-object cast. - assert(targetType.isAnyClassReferenceType()); + assert(targetLoweredType.isAnyClassReferenceType()); // Convert the metatype value to AnyObject. llvm::Value *object = emitMetatypeToAnyObjectDowncast(IGF, metatypeVal, sourceMetatype, mode); - SILType anyObjectType = - SILType::getPrimitiveObjectType( - IGF.IGM.Context.getAnyObjectType()); + sourceFormalType = IGF.IGM.Context.getAnyObjectType(); + sourceLoweredType = SILType::getPrimitiveObjectType(sourceFormalType); // Continue, pretending that the source value was an (optional) value. Explosion newValue; newValue.add(object); value = std::move(newValue); - sourceType = anyObjectType; } - assert(!targetType.is() && + assert(!targetLoweredType.is() && "scalar cast of class reference to metatype is unimplemented"); // If the source type is existential, project out the class pointer. @@ -986,22 +1000,24 @@ void irgen::emitScalarCheckedCast(IRGenFunction &IGF, // TODO: if we're casting to an existential type, don't throw away the // protocol conformance information we already have. llvm::Value *instance; - if (sourceType.isExistentialType()) { - instance = emitClassExistentialProjection(IGF, value, sourceType, + if (sourceLoweredType.isExistentialType()) { + instance = emitClassExistentialProjection(IGF, value, sourceLoweredType, CanArchetypeType()); } else { instance = value.claimNext(); } - if (targetType.isExistentialType()) { + if (targetFormalType.isExistentialType()) { Explosion outRes; - emitScalarExistentialDowncast(IGF, instance, sourceType, targetType, - mode, /*not a metatype*/ None, outRes); + emitScalarExistentialDowncast(IGF, instance, sourceLoweredType, + targetLoweredType, mode, + /*not a metatype*/ None, outRes); returnNilCheckedResult(IGF.Builder, outRes); return; } Explosion outRes; - llvm::Value *result = emitClassDowncast(IGF, instance, targetType, mode); + llvm::Value *result = emitClassDowncast(IGF, instance, targetFormalType, + mode); out.add(result); } diff --git a/lib/IRGen/GenCast.h b/lib/IRGen/GenCast.h index db2909929efe7..9352ed8a6ea56 100644 --- a/lib/IRGen/GenCast.h +++ b/lib/IRGen/GenCast.h @@ -48,16 +48,17 @@ namespace irgen { CheckedCastMode mode); void emitScalarCheckedCast(IRGenFunction &IGF, Explosion &value, - SILType valueType, SILType loweredTargetType, + SILType sourceLoweredType, + CanType sourceFormalType, + SILType targetLoweredType, + CanType targetFormalType, CheckedCastMode mode, Explosion &out); /// Convert a class object to the given destination type, /// using a runtime-checked cast. - /// - /// FIXME: toType should be an AST CanType. llvm::Value *emitClassDowncast(IRGenFunction &IGF, llvm::Value *from, - SILType toType, + CanType toType, CheckedCastMode mode); /// A result of a cast generation function. diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index 780ef09a59a9a..16b4a9b6ac638 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -12,6 +12,8 @@ // // This file implements generation of Clang AST types from Swift AST types // for types that are representable in Objective-C interfaces. +// AST/ClangTypeConverter.cpp duplicates a bunch of code from here, so make +// sure to keep the two in sync. // //===----------------------------------------------------------------------===// @@ -191,7 +193,7 @@ static clang::CanQualType getClangVectorType(const clang::ASTContext &ctx, clang::VectorType::VectorKind vecKind, StringRef numEltsString) { unsigned numElts; - bool failedParse = numEltsString.getAsInteger(10, numElts); + bool failedParse = numEltsString.getAsInteger(10, numElts); assert(!failedParse && "vector type name didn't end in count?"); (void) failedParse; @@ -324,7 +326,9 @@ ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, // On 64-bit Windows, no C type is imported as an Int or UInt; CLong is // imported as an Int32 and CLongLong as an Int64. Therefore, manually // add mappings to C for Int and UInt. - if (IGM.Triple.isOSWindows() && IGM.Triple.isArch64Bit()) { + // On 64-bit Cygwin, no manual mapping is required. + if (IGM.Triple.isOSWindows() && !IGM.Triple.isWindowsCygwinEnvironment() && + IGM.Triple.isArch64Bit()) { // Map UInt to uintptr_t auto swiftUIntType = getNamedSwiftType(stdlib, "UInt"); auto clangUIntPtrType = ctx.getCanonicalType(ctx.getUIntPtrType()); @@ -364,36 +368,38 @@ clang::CanQualType GenClangType::visitTupleType(CanTupleType type) { return ctx.getCanonicalType( ctx.getConstantArrayType(clangEltTy, size, clang::ArrayType::Normal, 0)); - - llvm_unreachable("Unexpected tuple type in Clang type generation!"); } clang::CanQualType GenClangType::visitProtocolType(CanProtocolType type) { auto proto = type->getDecl(); + auto &clangCtx = getClangASTContext(); - // Single protocol -> id - if (proto->isObjC()) { - auto &clangCtx = getClangASTContext(); - clang::IdentifierInfo *name = &clangCtx.Idents.get(proto->getName().get()); - auto *PDecl = clang::ObjCProtocolDecl::Create( - const_cast(clangCtx), - clangCtx.getTranslationUnitDecl(), name, - clang::SourceLocation(), clang::SourceLocation(), nullptr); - - // Attach an objc_runtime_name attribute with the Objective-C name to use - // for this protocol. - SmallString<64> runtimeNameBuffer; - PDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( - PDecl->getASTContext(), - proto->getObjCRuntimeName(runtimeNameBuffer))); - - auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, - &PDecl, 1); - auto ptrTy = clangCtx.getObjCObjectPointerType(clangType); - return clangCtx.getCanonicalType(ptrTy); + if (!proto->isObjC()) { + std::string s; + llvm::raw_string_ostream err(s); + err << "Trying to compute the clang type for a non-ObjC protocol type\n"; + proto->dump(err); + llvm::report_fatal_error(err.str()); } - return getClangIdType(getClangASTContext()); + // Single protocol -> id + clang::IdentifierInfo *name = &clangCtx.Idents.get(proto->getName().get()); + auto *PDecl = clang::ObjCProtocolDecl::Create( + const_cast(clangCtx), + clangCtx.getTranslationUnitDecl(), name, + clang::SourceLocation(), clang::SourceLocation(), nullptr); + + // Attach an objc_runtime_name attribute with the Objective-C name to use + // for this protocol. + SmallString<64> runtimeNameBuffer; + PDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( + PDecl->getASTContext(), + proto->getObjCRuntimeName(runtimeNameBuffer))); + + auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, + &PDecl, 1); + auto ptrTy = clangCtx.getObjCObjectPointerType(clangType); + return clangCtx.getCanonicalType(ptrTy); } clang::CanQualType GenClangType::visitMetatypeType(CanMetatypeType type) { @@ -407,29 +413,33 @@ GenClangType::visitExistentialMetatypeType(CanExistentialMetatypeType type) { clang::CanQualType GenClangType::visitClassType(CanClassType type) { auto &clangCtx = getClangASTContext(); - // produce the clang type INTF * if it is imported ObjC object. auto swiftDecl = type->getDecl(); - if (swiftDecl->isObjC()) { - clang::IdentifierInfo *ForwardClassId = - &clangCtx.Idents.get(swiftDecl->getName().get()); - auto *CDecl = clang::ObjCInterfaceDecl::Create( - clangCtx, clangCtx.getTranslationUnitDecl(), - clang::SourceLocation(), ForwardClassId, - /*typeParamList*/nullptr, /*PrevDecl=*/nullptr, - clang::SourceLocation()); - - // Attach an objc_runtime_name attribute with the Objective-C name to use - // for this class. - SmallString<64> runtimeNameBuffer; - CDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( - CDecl->getASTContext(), - swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); - - auto clangType = clangCtx.getObjCInterfaceType(CDecl); - auto ptrTy = clangCtx.getObjCObjectPointerType(clangType); - return clangCtx.getCanonicalType(ptrTy); - } - return getClangIdType(clangCtx); + + // TODO: [non-objc-class-clang-type-conversion] + // Crashing here instead of returning a bogus 'id' leads to test failures, + // which is surprising. + if (!swiftDecl->isObjC()) + return getClangIdType(clangCtx); + + // produce the clang type INTF * if it is imported ObjC object. + clang::IdentifierInfo *ForwardClassId = + &clangCtx.Idents.get(swiftDecl->getName().get()); + auto *CDecl = clang::ObjCInterfaceDecl::Create( + clangCtx, clangCtx.getTranslationUnitDecl(), + clang::SourceLocation(), ForwardClassId, + /*typeParamList*/nullptr, /*PrevDecl=*/nullptr, + clang::SourceLocation()); + + // Attach an objc_runtime_name attribute with the Objective-C name to use + // for this class. + SmallString<64> runtimeNameBuffer; + CDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( + CDecl->getASTContext(), + swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); + + auto clangType = clangCtx.getObjCInterfaceType(CDecl); + auto ptrTy = clangCtx.getObjCObjectPointerType(clangType); + return clangCtx.getCanonicalType(ptrTy); } clang::CanQualType GenClangType::visitBoundGenericClassType( @@ -441,8 +451,7 @@ clang::CanQualType GenClangType::visitBoundGenericClassType( clang::CanQualType GenClangType::visitBoundGenericType(CanBoundGenericType type) { - // We only expect *Pointer, ImplicitlyUnwrappedOptional, and Optional. - // The first two are structs; the last is an enum. + // We only expect *Pointer, SIMD* and Optional. if (auto underlyingTy = SILType::getPrimitiveObjectType(type).getOptionalObjectType()) { // The underlying type could be a bridged type, which makes any @@ -576,13 +585,17 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { if (allResults.empty()) { resultType = clangCtx.VoidTy; } else { - resultType = Converter.convert(IGM, allResults[0].getType()); + resultType = Converter.convert(IGM, + allResults[0].getReturnValueType(IGM.getSILModule(), type)); if (resultType.isNull()) return clang::CanQualType(); } SmallVector paramTypes; + SmallVector extParamInfos; for (auto paramTy : type->getParameters()) { + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + // Blocks should only take direct +0 parameters. switch (paramTy.getConvention()) { case ParameterConvention::Direct_Guaranteed: @@ -591,7 +604,9 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { break; case ParameterConvention::Direct_Owned: - llvm_unreachable("block takes owned parameter"); + extParamInfo = extParamInfo.withIsConsumed(true); + break; + case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_Inout: @@ -599,15 +614,20 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { case ParameterConvention::Indirect_In_Guaranteed: llvm_unreachable("block takes indirect parameter"); } - auto param = Converter.convert(IGM, paramTy.getType()); + auto param = Converter.convert(IGM, + paramTy.getArgumentType(IGM.getSILModule(), type)); if (param.isNull()) return clang::CanQualType(); + paramTypes.push_back(param); + extParamInfos.push_back(extParamInfo); } // Build the Clang function type. - clang::FunctionProtoType::ExtProtoInfo defaultEPI; - auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, defaultEPI); + clang::FunctionProtoType::ExtProtoInfo extProtoInfo; + extProtoInfo.ExtParameterInfos = extParamInfos.begin(); + + auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, extProtoInfo); clang::QualType ptrTy; switch (kind) { @@ -681,15 +701,13 @@ clang::CanQualType GenClangType::visitBuiltinRawPointerType( clang::CanQualType GenClangType::visitBuiltinIntegerType( CanBuiltinIntegerType type) { auto &ctx = getClangASTContext(); - if (type->getWidth().isPointerWidth()) { + if (type->getWidth().isPointerWidth()) return ctx.getCanonicalType(ctx.getUIntPtrType()); - } - if (type->getWidth().isFixedWidth()) { - auto width = type->getWidth().getFixedWidth(); - if (width == 1) return ctx.BoolTy; - return ctx.getCanonicalType(ctx.getIntTypeForBitwidth(width, /*signed*/ 0)); - } - llvm_unreachable(""); + assert(type->getWidth().isFixedWidth()); + auto width = type->getWidth().getFixedWidth(); + if (width == 1) + return ctx.BoolTy; + return ctx.getCanonicalType(ctx.getIntTypeForBitwidth(width, /*signed*/ 0)); } clang::CanQualType GenClangType::visitBuiltinFloatType( @@ -728,21 +746,25 @@ clang::CanQualType GenClangType::visitType(CanType type) { } clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { + // Look in the cache. + auto it = Cache.find(type); + if (it != Cache.end()) { + return it->second; + } + // Try to do this without making cache entries for obvious cases. if (auto nominal = dyn_cast(type)) { auto decl = nominal->getDecl(); if (auto clangDecl = decl->getClangDecl()) { + auto &ctx = IGM.getClangASTContext(); if (auto clangTypeDecl = dyn_cast(clangDecl)) { - auto &ctx = IGM.getClangASTContext(); return ctx.getCanonicalType(ctx.getTypeDeclType(clangTypeDecl)) .getUnqualifiedType(); } else if (auto ifaceDecl = dyn_cast(clangDecl)) { - auto &ctx = IGM.getClangASTContext(); auto clangType = ctx.getObjCInterfaceType(ifaceDecl); auto ptrTy = ctx.getObjCObjectPointerType(clangType); return ctx.getCanonicalType(ptrTy); } else if (auto protoDecl = dyn_cast(clangDecl)){ - auto &ctx = IGM.getClangASTContext(); auto clangType = ctx.getObjCObjectType( ctx.ObjCBuiltinIdTy, const_cast(&protoDecl), @@ -753,12 +775,6 @@ clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { } } - // Look in the cache. - auto it = Cache.find(type); - if (it != Cache.end()) { - return it->second; - } - // If that failed, convert the type, cache, and return. clang::CanQualType result = GenClangType(IGM, *this).visit(type); Cache.insert({type, result}); @@ -773,11 +789,13 @@ clang::CanQualType IRGenModule::getClangType(SILType type) { return getClangType(type.getASTType()); } -clang::CanQualType IRGenModule::getClangType(SILParameterInfo params) { - auto clangType = getClangType(params.getSILStorageType()); +clang::CanQualType IRGenModule::getClangType(SILParameterInfo params, + CanSILFunctionType funcTy) { + auto paramTy = params.getSILStorageType(getSILModule(), funcTy); + auto clangType = getClangType(paramTy); // @block_storage types must be @inout_aliasable and have // special lowering - if (!params.getSILStorageType().is()) { + if (!paramTy.is()) { if (params.isIndirectMutating()) { return getClangASTContext().getPointerType(clangType); } diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 4d4998e175c88..a4d5f652cbba3 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -165,6 +165,8 @@ namespace { ClassMetadataOptions Options; + Size HeaderSize; + public: ClassLayoutBuilder(IRGenModule &IGM, SILType classType, ReferenceCounting refcounting, @@ -178,11 +180,13 @@ namespace { case ReferenceCounting::Native: // For native classes, place a full object header. addHeapHeader(); + HeaderSize = CurSize; break; case ReferenceCounting::ObjC: // For ObjC-inheriting classes, we don't reliably know the size of the // base class, but NSObject only has an `isa` pointer at most. addNSObjectHeader(); + HeaderSize = CurSize; break; case ReferenceCounting::Block: case ReferenceCounting::Unknown: @@ -222,7 +226,7 @@ namespace { auto allElements = IGM.Context.AllocateCopy(Elements); return ClassLayout(*this, Options, classTy, - allStoredProps, allFieldAccesses, allElements); + allStoredProps, allFieldAccesses, allElements, HeaderSize); } private: @@ -297,7 +301,8 @@ namespace { SILType classType, bool superclass) { for (VarDecl *var : theClass->getStoredProperties()) { - SILType type = classType.getFieldType(var, IGM.getSILModule()); + SILType type = classType.getFieldType(var, IGM.getSILModule(), + TypeExpansionContext::minimal()); // Lower the field type. auto *eltType = &IGM.getTypeInfo(type); @@ -500,23 +505,20 @@ Address IRGenFunction::emitByteOffsetGEP(llvm::Value *base, } /// Emit a field l-value by applying the given offset to the given base. -static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF, - SILType baseType, - llvm::Value *base, - llvm::Value *offset, +static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF, SILType baseType, + llvm::Value *base, llvm::Value *offset, VarDecl *field) { - auto &fieldTI = - IGF.getTypeInfo(baseType.getFieldType(field, IGF.getSILModule())); + auto &fieldTI = IGF.getTypeInfo(baseType.getFieldType( + field, IGF.getSILModule(), IGF.IGM.getMaximalTypeExpansionContext())); auto addr = IGF.emitByteOffsetGEP(base, offset, fieldTI, base->getName() + "." + field->getName().str()); return OwnedAddress(addr, base); } -llvm::Constant * -irgen::tryEmitConstantClassFragilePhysicalMemberOffset(IRGenModule &IGM, - SILType baseType, - VarDecl *field) { - auto fieldType = baseType.getFieldType(field, IGM.getSILModule()); +llvm::Constant *irgen::tryEmitConstantClassFragilePhysicalMemberOffset( + IRGenModule &IGM, SILType baseType, VarDecl *field) { + auto fieldType = baseType.getFieldType(field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); // If the field is empty, its address doesn't matter. auto &fieldTI = IGM.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { @@ -1231,9 +1233,9 @@ namespace { // const class_t *theClass; fields.add(getClassMetadataRef()); // const method_list_t *instanceMethods; - fields.add(buildInstanceMethodList()); + emitAndAddMethodList(fields, MethodListKind::InstanceMethods); // const method_list_t *classMethods; - fields.add(buildClassMethodList()); + emitAndAddMethodList(fields, MethodListKind::ClassMethods); // const protocol_list_t *baseProtocols; fields.add(buildProtocolList()); // const property_list_t *properties; @@ -1266,13 +1268,13 @@ namespace { // const protocol_list_t *baseProtocols; fields.add(buildProtocolList()); // const method_list_t *requiredInstanceMethods; - fields.add(buildInstanceMethodList()); + emitAndAddMethodList(fields, MethodListKind::InstanceMethods); // const method_list_t *requiredClassMethods; - fields.add(buildClassMethodList()); + emitAndAddMethodList(fields, MethodListKind::ClassMethods); // const method_list_t *optionalInstanceMethods; - fields.add(buildOptInstanceMethodList()); + emitAndAddMethodList(fields, MethodListKind::OptionalInstanceMethods); // const method_list_t *optionalClassMethods; - fields.add(buildOptClassMethodList()); + emitAndAddMethodList(fields, MethodListKind::OptionalClassMethods); // const property_list_t *properties; fields.add(buildPropertyList(ForClass)); @@ -1350,7 +1352,8 @@ namespace { b.add(buildName()); // const method_list_t *baseMethods; - b.add(forMeta ? buildClassMethodList() : buildInstanceMethodList()); + emitAndAddMethodList(b, forMeta ? MethodListKind::ClassMethods + : MethodListKind::InstanceMethods); // const protocol_list_t *baseProtocols; // Apparently, this list is the same in the class and the metaclass. @@ -1466,7 +1469,7 @@ namespace { // If we have the destructor body, we know whether SILGen // generated a -dealloc body. if (auto braceStmt = destructor->getBody()) - return braceStmt->getNumElements() != 0; + return !braceStmt->empty(); // We don't have a destructor body, so hunt for the SIL function // for it. @@ -1568,28 +1571,43 @@ namespace { llvm_unreachable("not a class, category, or protocol?!"); } - llvm::Constant *buildClassMethodList() { - return buildMethodList(ClassMethods, - chooseNamePrefix("_CLASS_METHODS_", - "_CATEGORY_CLASS_METHODS_", - "_PROTOCOL_CLASS_METHODS_")); - } - - llvm::Constant *buildInstanceMethodList() { - return buildMethodList(InstanceMethods, - chooseNamePrefix("_INSTANCE_METHODS_", - "_CATEGORY_INSTANCE_METHODS_", - "_PROTOCOL_INSTANCE_METHODS_")); - } - llvm::Constant *buildOptClassMethodList() { - return buildMethodList(OptClassMethods, - "_PROTOCOL_CLASS_METHODS_OPT_"); - } + enum class MethodListKind : uint8_t { + ClassMethods, + InstanceMethods, + OptionalClassMethods, + OptionalInstanceMethods + }; - llvm::Constant *buildOptInstanceMethodList() { - return buildMethodList(OptInstanceMethods, - "_PROTOCOL_INSTANCE_METHODS_OPT_"); + /// Emit the method list and add the pointer to the `builder`. + void emitAndAddMethodList(ConstantInitBuilder::StructBuilder &builder, + MethodListKind kind) { + ArrayRef methods; + StringRef namePrefix; + switch (kind) { + case MethodListKind::ClassMethods: + methods = ClassMethods; + namePrefix = chooseNamePrefix("_CLASS_METHODS_", + "_CATEGORY_CLASS_METHODS_", + "_PROTOCOL_CLASS_METHODS_"); + break; + case MethodListKind::InstanceMethods: + methods = InstanceMethods; + namePrefix = chooseNamePrefix("_INSTANCE_METHODS_", + "_CATEGORY_INSTANCE_METHODS_", + "_PROTOCOL_INSTANCE_METHODS_"); + break; + case MethodListKind::OptionalClassMethods: + methods = OptClassMethods; + namePrefix = "_PROTOCOL_CLASS_METHODS_OPT_"; + break; + case MethodListKind::OptionalInstanceMethods: + methods = OptInstanceMethods; + namePrefix = "_PROTOCOL_INSTANCE_METHODS_OPT_"; + break; + } + llvm::Constant *methodListPtr = buildMethodList(methods, namePrefix); + builder.add(methodListPtr); } llvm::Constant *buildOptExtendedMethodTypes() { @@ -2423,12 +2441,13 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, return FunctionPointer(fnPtr, signature); } -FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILDeclRef method, - CanSILFunctionType methodType, - bool useSuperVTable) { +FunctionPointer +irgen::emitVirtualMethodValue(IRGenFunction &IGF, + llvm::Value *base, + SILType baseType, + SILDeclRef method, + CanSILFunctionType methodType, + bool useSuperVTable) { // Find the metadata. llvm::Value *metadata; if (useSuperVTable) { diff --git a/lib/IRGen/GenClass.h b/lib/IRGen/GenClass.h index b7fdf7f758d87..66018fbfb7b8d 100644 --- a/lib/IRGen/GenClass.h +++ b/lib/IRGen/GenClass.h @@ -50,12 +50,10 @@ namespace irgen { enum class ClassDeallocationKind : unsigned char; enum class FieldAccess : uint8_t; - - OwnedAddress projectPhysicalClassMemberAddress(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILType fieldType, - VarDecl *field); + + OwnedAddress projectPhysicalClassMemberAddress( + IRGenFunction &IGF, llvm::Value *base, + SILType baseType, SILType fieldType, VarDecl *field); /// Return a strategy for accessing the given stored class property. /// @@ -180,11 +178,9 @@ namespace irgen { /// Emit the constant fragile offset of the given property inside an instance /// of the class. - llvm::Constant * - tryEmitConstantClassFragilePhysicalMemberOffset(IRGenModule &IGM, - SILType baseType, - VarDecl *field); - + llvm::Constant *tryEmitConstantClassFragilePhysicalMemberOffset( + IRGenModule &IGM, SILType baseType, VarDecl *field); + FieldAccess getClassFieldAccess(IRGenModule &IGM, SILType baseType, VarDecl *field); @@ -208,10 +204,8 @@ namespace irgen { /// Given an instance pointer (or, for a static method, a class /// pointer), emit the callee for the given method. - FunctionPointer emitVirtualMethodValue(IRGenFunction &IGF, - llvm::Value *base, - SILType baseType, - SILDeclRef method, + FunctionPointer emitVirtualMethodValue(IRGenFunction &IGF, llvm::Value *base, + SILType baseType, SILDeclRef method, CanSILFunctionType methodType, bool useSuperVTable); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 8f36545b5027d..440963c22d129 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -148,20 +148,19 @@ class CategoryInitializerVisitor if (method->getAttrs().hasAttribute()) return; - llvm::Constant *name, *imp, *types; - emitObjCMethodDescriptorParts(IGM, method, /*concrete*/true, - name, types, imp); + auto descriptor = emitObjCMethodDescriptorParts(IGM, method, + /*concrete*/true); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); + descriptor.selectorRef); llvm::Value *args[] = { method->isStatic() ? metaclassMetadata : classMetadata, sel, - imp, - types + descriptor.impl, + descriptor.typeEncoding }; Builder.CreateCall(class_replaceMethod, args); @@ -172,20 +171,19 @@ class CategoryInitializerVisitor void visitConstructorDecl(ConstructorDecl *constructor) { if (!requiresObjCMethodDescriptor(constructor)) return; - llvm::Constant *name, *imp, *types; - emitObjCMethodDescriptorParts(IGM, constructor, /*concrete*/true, - name, types, imp); + auto descriptor = emitObjCMethodDescriptorParts(IGM, constructor, + /*concrete*/true); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); + descriptor.selectorRef); llvm::Value *args[] = { classMetadata, sel, - imp, - types + descriptor.impl, + descriptor.typeEncoding }; Builder.CreateCall(class_replaceMethod, args); @@ -206,23 +204,22 @@ class CategoryInitializerVisitor if (prop->getAttrs().hasAttribute()) return; - llvm::Constant *name, *imp, *types; - emitObjCGetterDescriptorParts(IGM, prop, - name, types, imp); + auto descriptor = emitObjCGetterDescriptorParts(IGM, prop); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); + descriptor.selectorRef); auto theClass = prop->isStatic() ? metaclassMetadata : classMetadata; - llvm::Value *getterArgs[] = {theClass, sel, imp, types}; + llvm::Value *getterArgs[] = + {theClass, sel, descriptor.impl, descriptor.typeEncoding}; Builder.CreateCall(class_replaceMethod, getterArgs); if (prop->isSettable(prop->getDeclContext())) { - emitObjCSetterDescriptorParts(IGM, prop, - name, types, imp); + auto descriptor = emitObjCSetterDescriptorParts(IGM, prop); sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); - llvm::Value *setterArgs[] = {theClass, sel, imp, types}; + descriptor.selectorRef); + llvm::Value *setterArgs[] = + {theClass, sel, descriptor.impl, descriptor.typeEncoding}; Builder.CreateCall(class_replaceMethod, setterArgs); } @@ -232,22 +229,21 @@ class CategoryInitializerVisitor assert(!subscript->isStatic() && "objc doesn't support class subscripts"); if (!requiresObjCSubscriptDescriptor(IGM, subscript)) return; - llvm::Constant *name, *imp, *types; - emitObjCGetterDescriptorParts(IGM, subscript, - name, types, imp); + auto descriptor = emitObjCGetterDescriptorParts(IGM, subscript); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); - llvm::Value *getterArgs[] = {classMetadata, sel, imp, types}; + descriptor.selectorRef); + llvm::Value *getterArgs[] = + {classMetadata, sel, descriptor.impl, descriptor.typeEncoding}; Builder.CreateCall(class_replaceMethod, getterArgs); if (subscript->supportsMutation()) { - emitObjCSetterDescriptorParts(IGM, subscript, - name, types, imp); + auto descriptor = emitObjCSetterDescriptorParts(IGM, subscript); sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), - name); - llvm::Value *setterArgs[] = {classMetadata, sel, imp, types}; + descriptor.selectorRef); + llvm::Value *setterArgs[] = + {classMetadata, sel, descriptor.impl, descriptor.typeEncoding}; Builder.CreateCall(class_replaceMethod, setterArgs); } @@ -353,16 +349,16 @@ class ObjCProtocolInitializerVisitor return; } - llvm::Constant *name, *imp, *types; - emitObjCMethodDescriptorParts(IGM, method, /*concrete*/false, - name, types, imp); + auto descriptor = emitObjCMethodDescriptorParts(IGM, method, + /*concrete*/false); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. - llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), name); + llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), + descriptor.selectorRef); llvm::Value *args[] = { - NewProto, sel, types, + NewProto, sel, descriptor.typeEncoding, // required? llvm::ConstantInt::get(IGM.ObjCBoolTy, !method->getAttrs().hasAttribute()), @@ -381,14 +377,13 @@ class ObjCProtocolInitializerVisitor void visitAbstractStorageDecl(AbstractStorageDecl *prop) { // TODO: Add properties to protocol. - llvm::Constant *name, *imp, *types; - emitObjCGetterDescriptorParts(IGM, prop, - name, types, imp); + auto descriptor = emitObjCGetterDescriptorParts(IGM, prop); // When generating JIT'd code, we need to call sel_registerName() to force // the runtime to unique the selector. - llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), name); + llvm::Value *sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), + descriptor.selectorRef); llvm::Value *getterArgs[] = { - NewProto, sel, types, + NewProto, sel, descriptor.typeEncoding, // required? llvm::ConstantInt::get(IGM.ObjCBoolTy, !prop->getAttrs().hasAttribute()), @@ -399,11 +394,11 @@ class ObjCProtocolInitializerVisitor Builder.CreateCall(protocol_addMethodDescription, getterArgs); if (prop->isSettable(nullptr)) { - emitObjCSetterDescriptorParts(IGM, prop, - name, types, imp); - sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), name); + auto descriptor = emitObjCSetterDescriptorParts(IGM, prop); + sel = Builder.CreateCall(IGM.getObjCSelRegisterNameFn(), + descriptor.selectorRef); llvm::Value *setterArgs[] = { - NewProto, sel, types, + NewProto, sel, descriptor.typeEncoding, // required? llvm::ConstantInt::get(IGM.ObjCBoolTy, !prop->getAttrs().hasAttribute()), @@ -436,7 +431,7 @@ void IRGenModule::emitSourceFile(SourceFile &SF) { llvm::SaveAndRestore SetCurSourceFile(CurSourceFile, &SF); // Emit types and other global decls. - for (auto *decl : SF.Decls) + for (auto *decl : SF.getTopLevelDecls()) emitGlobalDecl(decl); for (auto *localDecl : SF.LocalTypeDecls) emitGlobalDecl(localDecl); @@ -793,6 +788,17 @@ IRGenModule::getAddrOfContextDescriptorForParent(DeclContext *parent, LLVM_FALLTHROUGH; case DeclContextKind::Module: + if (auto *D = ofChild->getAsDecl()) { + // If the top-level decl has been marked as moved from another module, + // using @_originallyDefinedIn, we should emit the original module as + // the context because all run-time names of this decl are based on the + // original module name. + auto OriginalModule = D->getAlternateModuleName(); + if (!OriginalModule.empty()) { + return {getAddrOfOriginalModuleContextDescriptor(OriginalModule), + ConstantReference::Direct}; + } + } return {getAddrOfModuleContextDescriptor(cast(parent)), ConstantReference::Direct}; } @@ -831,7 +837,8 @@ IRGenModule::getAddrOfParentContextDescriptor(DeclContext *from, // Wrap up private types in an anonymous context for the containing file // unit so that the runtime knows they have unstable identity. - if (!fromAnonymousContext && Type->isOutermostPrivateOrFilePrivateScope()) + if (!fromAnonymousContext && Type->isOutermostPrivateOrFilePrivateScope() + && !Type->isUsableFromInline()) return {getAddrOfAnonymousContextDescriptor(Type), ConstantReference::Direct}; } @@ -909,12 +916,11 @@ std::string IRGenModule::GetObjCSectionName(StringRef Section, ? ("__DATA," + Section).str() : ("__DATA," + Section + "," + MachOAttributes).str(); case llvm::Triple::ELF: + case llvm::Triple::Wasm: return Section.substr(2).str(); case llvm::Triple::XCOFF: case llvm::Triple::COFF: return ("." + Section.substr(2) + "$B").str(); - case llvm::Triple::Wasm: - return Section.substr(2).str(); } llvm_unreachable("unexpected object file format"); @@ -941,12 +947,11 @@ void IRGenModule::SetCStringLiteralSection(llvm::GlobalVariable *GV, return; } case llvm::Triple::ELF: + case llvm::Triple::Wasm: return; case llvm::Triple::XCOFF: case llvm::Triple::COFF: return; - case llvm::Triple::Wasm: - return; } llvm_unreachable("unexpected object file format"); @@ -1004,7 +1009,7 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) { return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping; } -void IRGenerator::emitGlobalTopLevel() { +void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { // Generate order numbers for the functions in the SIL module that // correspond to definitions in the LLVM module. unsigned nextOrderNumber = 0; @@ -1024,7 +1029,11 @@ void IRGenerator::emitGlobalTopLevel() { CurrentIGMPtr IGM = getGenModule(wt.getProtocol()->getDeclContext()); ensureRelativeSymbolCollocation(wt); } - + if (linkerDirectives) { + for (auto &entry: *linkerDirectives) { + createLinkerDirectiveVariable(*PrimaryIGM, entry.getKey()); + } + } for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) { Decl *decl = v.getDecl(); CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr); @@ -1082,7 +1091,7 @@ void IRGenModule::finishEmitAfterTopLevel() { if (DebugInfo) { if (ModuleDecl *TheStdlib = Context.getStdlibModule()) { if (TheStdlib != getSwiftModule()) { - std::pair AccessPath[] = { + Located AccessPath[] = { { Context.StdlibModuleName, swift::SourceLoc() } }; @@ -1120,6 +1129,7 @@ void IRGenerator::emitTypeMetadataRecords() { /// else) that we require. void IRGenerator::emitLazyDefinitions() { while (!LazyTypeMetadata.empty() || + !LazySpecializedTypeMetadataRecords.empty() || !LazyTypeContextDescriptors.empty() || !LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() || @@ -1136,6 +1146,12 @@ void IRGenerator::emitLazyDefinitions() { CurrentIGMPtr IGM = getGenModule(type->getDeclContext()); emitLazyTypeMetadata(*IGM.get(), type); } + while (!LazySpecializedTypeMetadataRecords.empty()) { + CanType type = LazySpecializedTypeMetadataRecords.pop_back_val(); + auto *nominal = type->getNominalOrBoundGenericNominal(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazySpecializedGenericTypeMetadata(*IGM.get(), type); + } while (!LazyTypeContextDescriptors.empty()) { NominalTypeDecl *type = LazyTypeContextDescriptors.pop_back_val(); auto &entry = LazyTypeGlobals.find(type)->second; @@ -1176,6 +1192,12 @@ void IRGenerator::emitLazyDefinitions() { } } + while (!LazyMetadataAccessors.empty()) { + NominalTypeDecl *nominal = LazyMetadataAccessors.pop_back_val(); + CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext()); + emitLazyMetadataAccessor(*IGM.get(), nominal); + } + FinishedEmittingLazyDefinitions = true; } @@ -1187,6 +1209,16 @@ void IRGenerator::addLazyFunction(SILFunction *f) { assert(!FinishedEmittingLazyDefinitions); LazyFunctionDefinitions.push_back(f); + if (const SILFunction *orig = f->getOriginOfSpecialization()) { + // f is a specialization. Try to emit all specializations of the same + // original function into the same IGM. This increases the chances that + // specializations are merged by LLVM's function merging. + auto iter = + IGMForSpecializations.insert(std::make_pair(orig, CurrentIGM)).first; + DefaultIGMForFunction.insert(std::make_pair(f, iter->second)); + return; + } + if (auto *dc = f->getDeclContext()) if (dc->getParentSourceFile()) return; @@ -1262,6 +1294,17 @@ void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type, if (!hasLazyMetadata(type)) return; + // If the type can be generated in several TU with weak linkage we don't know + // which one will be picked up so we have to require the metadata. Otherwise, + // the situation can arise where one TU contains a type descriptor with a null + // metadata access function and the other TU which requires metadata has a + // type descriptor with a valid metadata access function but the linker picks + // the first one. + if (isAccessorLazilyGenerated(getTypeMetadataAccessStrategy( + type->getDeclaredType()->getCanonicalType()))) { + requireMetadata = RequireMetadata; + } + // Try to create a new record of the fact that we used this type. auto insertResult = LazyTypeGlobals.try_emplace(type); auto &entry = insertResult.first->second; @@ -1321,6 +1364,18 @@ void IRGenerator::noteUseOfFieldDescriptor(NominalTypeDecl *type) { LazyFieldDescriptors.push_back(type); } +void IRGenerator::noteUseOfSpecializedGenericTypeMetadata(CanType type) { + auto key = type->getAnyNominal(); + assert(key); + auto &enqueuedSpecializedTypes = this->SpecializationsForGenericTypes[key]; + if (llvm::all_of(enqueuedSpecializedTypes, + [&](CanType enqueued) { return enqueued != type; })) { + assert(!FinishedEmittingLazyDefinitions); + this->LazySpecializedTypeMetadataRecords.push_back(type); + enqueuedSpecializedTypes.push_back(type); + } +} + void IRGenerator::noteUseOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaque) { if (!opaque) return; @@ -1903,6 +1958,49 @@ llvm::GlobalVariable *swift::irgen::createVariable( return var; } +llvm::GlobalVariable * +swift::irgen::createLinkerDirectiveVariable(IRGenModule &IGM, StringRef name) { + + // A prefix of \1 can avoid further mangling of the symbol (prefixing _). + llvm::SmallString<32> NameWithFlag; + NameWithFlag.push_back('\1'); + NameWithFlag.append(name); + name = NameWithFlag.str(); + static const uint8_t Size = 8; + static const uint8_t Alignment = 8; + + // Use a char type as the type for this linker directive. + auto ProperlySizedIntTy = SILType::getBuiltinIntegerType( + Size, IGM.getSwiftModule()->getASTContext()); + auto storageType = IGM.getStorageType(ProperlySizedIntTy); + + llvm::GlobalValue *existingValue = IGM.Module.getNamedGlobal(name); + if (existingValue) { + auto existingVar = dyn_cast(existingValue); + if (existingVar && isPointerTo(existingVar->getType(), storageType)) + return existingVar; + + IGM.error(SourceLoc(), + "program too clever: variable collides with existing symbol " + + name); + + // Note that this will implicitly unique if the .unique name is also taken. + existingValue->setName(name + ".unique"); + } + + llvm::GlobalValue::LinkageTypes Linkage = + llvm::GlobalValue::LinkageTypes::ExternalLinkage; + auto var = new llvm::GlobalVariable(IGM.Module, storageType, /*constant*/true, + Linkage, /*Init to zero*/llvm::Constant::getNullValue(storageType), name); + ApplyIRLinkage({Linkage, + llvm::GlobalValue::VisibilityTypes::DefaultVisibility, + llvm::GlobalValue::DLLStorageClassTypes::DefaultStorageClass}).to(var); + var->setAlignment(Alignment); + disableAddressSanitizer(IGM, var); + IGM.addUsedGlobal(var); + return var; +} + void swift::irgen::disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var) { // Add an operand to llvm.asan.globals blacklisting this global variable. llvm::Metadata *metadata[] = { @@ -2338,13 +2436,12 @@ void IRGenModule::emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { auto *abstractStorage = dyn_cast(namingDecl); bool isNativeDynamic = false; - bool isDynamicReplacement = false; + const bool isDynamicReplacement = namingDecl->getDynamicallyReplacedDecl(); // Don't emit accessors for abstract storage that is not dynamic or a dynamic // replacement. if (abstractStorage) { isNativeDynamic = abstractStorage->hasAnyNativeDynamicAccessors(); - isDynamicReplacement = abstractStorage->hasAnyDynamicReplacementAccessors(); if (!isNativeDynamic && !isDynamicReplacement) return; } @@ -2352,10 +2449,7 @@ void IRGenModule::emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { // Don't emit accessors for functions that are not dynamic or dynamic // replacements. if (!abstractStorage) { - isNativeDynamic = opaque->getNamingDecl()->isNativeDynamic(); - isDynamicReplacement = opaque->getNamingDecl() - ->getAttrs() - .hasAttribute(); + isNativeDynamic = namingDecl->isNativeDynamic(); if (!isNativeDynamic && !isDynamicReplacement) return; } @@ -2568,20 +2662,15 @@ static llvm::GlobalVariable *createGOTEquivalent(IRGenModule &IGM, global, llvm::Twine("got.") + globalName); - // rdar://problem/50968433: Unnamed_addr constants appear to get emitted - // with incorrect alignment by the LLVM JIT in some cases. Don't use - // unnamed_addr as a workaround. // rdar://problem/53836960: i386 ld64 also mis-links relative references // to GOT entries. - if (!IGM.getOptions().UseJIT - && (!IGM.Triple.isOSDarwin() - || IGM.Triple.getArch() != llvm::Triple::x86)) { + if (!IGM.Triple.isOSDarwin() || IGM.Triple.getArch() != llvm::Triple::x86) { gotEquivalent->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); } else { ApplyIRLinkage(IRLinkage::InternalLinkOnceODR) .to(gotEquivalent); } - + return gotEquivalent; } @@ -3571,8 +3660,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, if (nominal) addRuntimeResolvableType(nominal); - // Don't define the alias for foreign type metadata, since it's not ABI. - if (nominal && requiresForeignTypeMetadata(nominal)) + // Don't define the alias for foreign type metadata or prespecialized generic + // metadata, since neither is ABI. + if ((nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext())) return var; // For concrete metadata, declare the alias to its address point. @@ -3607,9 +3699,13 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, llvm::Type *defaultVarTy; unsigned adjustmentIndex; - + + bool fullMetadata = (nominal && requiresForeignTypeMetadata(nominal)) || + (concreteType->getAnyGeneric() && + concreteType->getAnyGeneric()->isGenericContext()); + // Foreign classes reference the full metadata with a GEP. - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { defaultVarTy = FullTypeMetadataStructTy; adjustmentIndex = MetadataAdjustmentIndex::ValueType; // The symbol for other nominal type metadata is generated at the address @@ -3634,10 +3730,18 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType, IRGen.noteUseOfTypeMetadata(nominal); } + if (shouldPrespecializeGenericMetadata()) { + if (auto nominal = concreteType->getAnyNominal()) { + if (nominal->isGenericContext()) { + IRGen.noteUseOfSpecializedGenericTypeMetadata(concreteType); + } + } + } + Optional entity; DebugTypeInfo DbgTy; - if (nominal && requiresForeignTypeMetadata(nominal)) { + if (fullMetadata) { entity = LinkEntity::forTypeMetadata(concreteType, TypeMetadataAddress::FullMetadata); DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), @@ -3954,10 +4058,6 @@ Optional IRGenModule::getAddrOfIVarInitDestroy( llvm::Function *IRGenModule::getAddrOfValueWitness(CanType abstractType, ValueWitness index, ForDefinition_t forDefinition) { - // We shouldn't emit value witness symbols for generic type instances. - assert(!isa(abstractType) && - "emitting value witness for generic type instance?!"); - LinkEntity entity = LinkEntity::forValueWitness(abstractType, index); llvm::Function *&entry = GlobalFuncs[entity]; diff --git a/lib/IRGen/GenDecl.h b/lib/IRGen/GenDecl.h index aabd2448096fd..93656316c6a0c 100644 --- a/lib/IRGen/GenDecl.h +++ b/lib/IRGen/GenDecl.h @@ -52,6 +52,9 @@ namespace irgen { Optional DebugLoc = None, StringRef DebugName = StringRef(), bool heapAllocated = false); + llvm::GlobalVariable * + createLinkerDirectiveVariable(IRGenModule &IGM, StringRef Name); + void disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var); } } diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index 7108fe82f08bd..c6e07d16b6c57 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -315,9 +315,10 @@ namespace { SILType getSingletonType(IRGenModule &IGM, SILType T) const { assert(!ElementsWithPayload.empty()); - + return T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } public: @@ -614,8 +615,9 @@ namespace { assert(ElementsWithPayload.size() == 1 && "empty singleton enum should not be dynamic!"); - auto payloadTy = T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + auto payloadTy = T.getEnumElementType( + ElementsWithPayload[0].decl, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); auto payloadLayout = emitTypeLayoutRef(IGF, payloadTy, collector); auto flags = emitEnumLayoutFlags(IGF.IGM, isVWTMutable); IGF.Builder.CreateCall( @@ -1127,10 +1129,12 @@ namespace { protected: int64_t getDiscriminatorIndex(EnumElementDecl *target) const override { // The elements are assigned discriminators ABI-compatible with their - // raw values from C. - assert(target->getRawValueExpr() - && "c-compatible enum elt has no raw value?!"); - auto intExpr = cast(target->getRawValueExpr()); + // raw values from C. An invalid raw value is assigned the error index -1. + auto intExpr = + dyn_cast_or_null(target->getRawValueExpr()); + if (!intExpr) { + return -1; + } auto intType = getDiscriminatorType(); APInt intValue = @@ -1565,7 +1569,8 @@ namespace { SILType getPayloadType(IRGenModule &IGM, SILType T) const { return T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } const TypeInfo &getPayloadTypeInfo() const { @@ -2955,8 +2960,9 @@ namespace { // Ask the runtime to do our layout using the payload metadata and number // of empty cases. - auto payloadTy = T.getEnumElementType(ElementsWithPayload[0].decl, - IGM.getSILModule()); + auto payloadTy = + T.getEnumElementType(ElementsWithPayload[0].decl, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); auto payloadLayout = emitTypeLayoutRef(IGF, payloadTy, collector); auto emptyCasesVal = llvm::ConstantInt::get(IGM.Int32Ty, ElementsWithNoPayload.size()); @@ -4573,8 +4579,9 @@ namespace { unsigned tagIndex = 0; for (auto &payloadCasePair : ElementsWithPayload) { - SILType PayloadT = T.getEnumElementType(payloadCasePair.decl, - IGF.getSILModule()); + SILType PayloadT = + T.getEnumElementType(payloadCasePair.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); auto &payloadTI = *payloadCasePair.ti; // Trivial and, in the case of a take, bitwise-takable payloads, // can all share the default path. @@ -4690,9 +4697,9 @@ namespace { } for (auto &payloadCasePair : ElementsWithPayload) { - SILType payloadT = - T.getEnumElementType(payloadCasePair.decl, - collector.IGF.getSILModule()); + SILType payloadT = T.getEnumElementType( + payloadCasePair.decl, collector.IGF.getSILModule(), + collector.IGF.IGM.getMaximalTypeExpansionContext()); auto &payloadTI = *payloadCasePair.ti; payloadTI.collectMetadataForOutlining(collector, payloadT); } @@ -4735,8 +4742,9 @@ namespace { // Destroy the data. Address dataAddr = IGF.Builder.CreateBitCast( addr, elt.ti->getStorageType()->getPointerTo()); - SILType payloadT = - T.getEnumElementType(elt.decl, IGF.getSILModule()); + SILType payloadT = T.getEnumElementType( + elt.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); elt.ti->destroy(IGF, dataAddr, payloadT, true /*isOutlined*/); }); return; @@ -4975,9 +4983,11 @@ namespace { Address eltAddr = IGF.Builder.CreateStructGEP(metadataBuffer, i, IGM.getPointerSize() * i); if (i == 0) firstAddr = eltAddr.getAddress(); - - auto payloadTy = T.getEnumElementType(elt.decl, IGF.getSILModule()); - + + auto payloadTy = + T.getEnumElementType(elt.decl, IGF.getSILModule(), + IGF.IGM.getMaximalTypeExpansionContext()); + auto metadata = emitTypeLayoutRef(IGF, payloadTy, collector); IGF.Builder.CreateStore(metadata, eltAddr); @@ -5807,7 +5817,8 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) { // *Now* apply the substitutions and get the type info for the instance's // payload type, since we know this case carries an apparent payload in // the generic case. - SILType fieldTy = type.getEnumElementType(elt, TC.IGM.getSILModule()); + SILType fieldTy = type.getEnumElementType( + elt, TC.IGM.getSILModule(), TC.IGM.getMaximalTypeExpansionContext()); auto *substArgTI = &TC.IGM.getTypeInfo(fieldTy); elementsWithPayload.push_back({elt, substArgTI, origArgTI}); diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index f8bd70dd1a17a..c2b9f730bddf0 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -580,14 +580,17 @@ IRGenModule::getForeignFunctionInfo(CanSILFunctionType type) { } static void emitApplyArgument(IRGenFunction &IGF, + CanSILFunctionType origFnTy, SILParameterInfo origParam, + CanSILFunctionType substFnTy, SILParameterInfo substParam, Explosion &in, Explosion &out) { auto silConv = IGF.IGM.silConv; bool isSubstituted = - (silConv.getSILType(substParam) != silConv.getSILType(origParam)); + (silConv.getSILType(substParam, substFnTy) + != silConv.getSILType(origParam, origFnTy)); // For indirect arguments, we just need to pass a pointer. if (silConv.isSILIndirect(origParam)) { @@ -597,7 +600,7 @@ static void emitApplyArgument(IRGenFunction &IGF, // If a substitution is in play, just bitcast the address. if (isSubstituted) { auto origType = - IGF.IGM.getStoragePointerType(silConv.getSILType(origParam)); + IGF.IGM.getStoragePointerType(silConv.getSILType(origParam, origFnTy)); addr = IGF.Builder.CreateBitCast(addr, origType); } @@ -612,14 +615,14 @@ static void emitApplyArgument(IRGenFunction &IGF, // Handle the last unsubstituted case. if (!isSubstituted) { - auto &substArgTI = - cast(IGF.getTypeInfo(silConv.getSILType(substParam))); + auto &substArgTI = cast( + IGF.getTypeInfo(silConv.getSILType(substParam, substFnTy))); substArgTI.reexplode(IGF, in, out); return; } - reemitAsUnsubstituted(IGF, silConv.getSILType(origParam), - silConv.getSILType(substParam), in, out); + reemitAsUnsubstituted(IGF, silConv.getSILType(origParam, origFnTy), + silConv.getSILType(substParam, substFnTy), in, out); } static CanType getArgumentLoweringType(CanType type, @@ -650,7 +653,7 @@ static bool isABIIgnoredParameterWithoutStorage(IRGenModule &IGM, if (param.isFormalIndirect()) return false; - SILType argType = IGM.silConv.getSILType(param); + SILType argType = IGM.silConv.getSILType(param, substType); auto &ti = IGF.getTypeInfoForLowered(argType.getASTType()); // Empty values don't matter. return ti.getSchema().empty(); @@ -730,7 +733,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, { // Lower the forwarded arguments in the original function's generic context. - GenericContextScope scope(IGM, origType->getGenericSignature()); + GenericContextScope scope(IGM, origType->getInvocationGenericSignature()); SILFunctionConventions origConv(origType, IGM.getSILModule()); auto &outResultTI = IGM.getTypeInfo(outConv.getSILResultType()); @@ -767,10 +770,11 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Reemit the parameters as unsubstituted. for (unsigned i = 0; i < outType->getParameters().size(); ++i) { auto origParamInfo = origType->getParameters()[i]; - auto &ti = IGM.getTypeInfoForLowered(origParamInfo.getType()); + auto &ti = IGM.getTypeInfoForLowered( + origParamInfo.getArgumentType(IGM.getSILModule(), origType)); auto schema = ti.getSchema(); - auto origParamSILType = IGM.silConv.getSILType(origParamInfo); + auto origParamSILType = IGM.silConv.getSILType(origParamInfo, origType); // Forward the address of indirect value params. auto &nativeSchemaOrigParam = ti.nativeParameterValueSchema(IGM); bool isIndirectParam = origConv.isSILIndirect(origParamInfo); @@ -787,13 +791,17 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Indirect parameters need no mapping through the native calling // convention. if (isIndirectParam) { - emitApplyArgument(subIGF, origParamInfo, outTypeParamInfo, origParams, - args); + emitApplyArgument(subIGF, + origType, + origParamInfo, + outType, + outTypeParamInfo, + origParams, args); continue; } // Map from the native calling convention into the explosion schema. - auto outTypeParamSILType = IGM.silConv.getSILType(origParamInfo); + auto outTypeParamSILType = IGM.silConv.getSILType(origParamInfo, origType); auto &nativeSchemaOutTypeParam = IGM.getTypeInfo(outTypeParamSILType).nativeParameterValueSchema(IGM); Explosion nativeParam; @@ -807,7 +815,10 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, // Emit unsubstituted argument for call. Explosion nonNativeApplyArg; - emitApplyArgument(subIGF, origParamInfo, outTypeParamInfo, nonNativeParam, + emitApplyArgument(subIGF, + origType, origParamInfo, + outType, outTypeParamInfo, + nonNativeParam, nonNativeApplyArg); assert(nonNativeParam.empty()); // Map back from the explosion scheme to the native calling convention for @@ -847,7 +858,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, } // Lower the captured arguments in the original function's generic context. - GenericContextScope scope(IGM, origType->getGenericSignature()); + GenericContextScope scope(IGM, origType->getInvocationGenericSignature()); // This is where the context parameter appears. llvm::Value *rawData = nullptr; @@ -903,7 +914,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, findSinglePartiallyAppliedParameterIndexIgnoringEmptyTypes( subIGF, substType, outType); auto paramInfo = substType->getParameters()[paramI]; - auto &ti = IGM.getTypeInfoForLowered(paramInfo.getType()); + auto &ti = IGM.getTypeInfoForLowered( + paramInfo.getArgumentType(IGM.getSILModule(), substType)); Explosion param; auto ref = rawData; // We can get a '{ swift.refcounted* }' type for AnyObject on linux. @@ -1089,13 +1101,14 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, if (hasPolymorphicParams) bindPolymorphicParameter(subIGF, origType, substType, param, origParamI); - emitApplyArgument(subIGF, origParamInfo, - substType->getParameters()[origParamI], + emitApplyArgument(subIGF, + origType, origParamInfo, + substType, substType->getParameters()[origParamI], param, origParam); bool isWitnessMethodCalleeSelf = (isWitnessMethodCallee && origParamI + 1 == origType->getParameters().size()); needsAllocas |= addNativeArgument( - subIGF, origParam, origParamInfo, + subIGF, origParam, origType, origParamInfo, isWitnessMethodCalleeSelf ? witnessMethodSelfValue : args, false); ++origParamI; } else { @@ -1125,8 +1138,11 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, FunctionPointer fnPtr = [&]() -> FunctionPointer { // If we found a function pointer statically, great. if (staticFnPtr) { - assert(staticFnPtr->getPointer()->getType() == fnTy && - "static function type mismatch?!"); + if (staticFnPtr->getPointer()->getType() != fnTy) { + auto fnPtr = staticFnPtr->getPointer(); + fnPtr = subIGF.Builder.CreateBitCast(fnPtr, fnTy); + return FunctionPointer(fnPtr, origSig); + } return *staticFnPtr; } @@ -1290,7 +1306,7 @@ Optional irgen::emitFunctionPartialApplication( // Collect the type infos for the context parameters. for (auto param : params) { - SILType argType = IGF.IGM.silConv.getSILType(param); + SILType argType = IGF.IGM.silConv.getSILType(param, origType); auto argLoweringTy = getArgumentLoweringType(argType.getASTType(), param); diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 0c2c82b812b41..e02dce766058a 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -317,6 +317,7 @@ HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF, elt.getType().getAlignmentMask(IGF, eltTy)); LLVM_FALLTHROUGH; case ElementLayout::Kind::Empty: + case ElementLayout::Kind::EmptyTailAllocatedCType: case ElementLayout::Kind::Fixed: // Don't need to dynamically calculate this offset. Offsets.push_back(nullptr); @@ -1459,7 +1460,7 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo { auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor( boxedInterfaceType, - env ? env->getGenericSignature()->getCanonicalSignature() + env ? env->getGenericSignature().getCanonicalSignature() : CanGenericSignature()); llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name, boxDescriptor); @@ -1527,8 +1528,8 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) { // TODO: Multi-field boxes assert(T->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - auto &eltTI = IGM.getTypeInfoForLowered( - getSILBoxFieldLoweredType(T, IGM.getSILModule().Types, 0)); + auto &eltTI = IGM.getTypeInfoForLowered(getSILBoxFieldLoweredType( + IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0)); if (!eltTI.isFixedSize()) { if (!NonFixedBoxTI) NonFixedBoxTI = new NonFixedBoxTypeInfo(IGM); @@ -1576,8 +1577,9 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) { // Produce a tailored box metadata for the type. assert(T->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return new FixedBoxTypeInfo(IGM, - getSILBoxFieldType(T, IGM.getSILModule().Types, 0)); + return new FixedBoxTypeInfo( + IGM, getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(), + T, IGM.getSILModule().Types, 0)); } OwnedAddress @@ -1587,9 +1589,12 @@ irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.allocate(IGF, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0), - env, name); + return boxTI.allocate( + IGF, + getSILBoxFieldType( + IGF.IGM.getMaximalTypeExpansionContext(), + boxType, IGF.IGM.getSILModule().Types, 0), + env, name); } void irgen::emitDeallocateBox(IRGenFunction &IGF, @@ -1598,8 +1603,10 @@ void irgen::emitDeallocateBox(IRGenFunction &IGF, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.deallocate(IGF, box, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0)); + return boxTI.deallocate( + IGF, box, + getSILBoxFieldType(IGF.IGM.getMaximalTypeExpansionContext(), boxType, + IGF.IGM.getSILModule().Types, 0)); } Address irgen::emitProjectBox(IRGenFunction &IGF, @@ -1608,8 +1615,10 @@ Address irgen::emitProjectBox(IRGenFunction &IGF, auto &boxTI = IGF.getTypeInfoForLowered(boxType).as(); assert(boxType->getLayout()->getFields().size() == 1 && "multi-field boxes not implemented yet"); - return boxTI.project(IGF, box, - getSILBoxFieldType(boxType, IGF.IGM.getSILModule().Types, 0)); + return boxTI.project( + IGF, box, + getSILBoxFieldType(IGF.IGM.getMaximalTypeExpansionContext(), boxType, + IGF.IGM.getSILModule().Types, 0)); } Address irgen::emitAllocateExistentialBoxInBuffer( diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index e014f91a0137a..545fff4017486 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -695,11 +695,13 @@ emitKeyPathComponent(IRGenModule &IGM, && "must be 32-bit-aligned here"); SILType loweredBaseTy; - GenericContextScope scope(IGM, - genericEnv ? genericEnv->getGenericSignature()->getCanonicalSignature() - : nullptr); loweredBaseTy = IGM.getLoweredType(AbstractionPattern::getOpaque(), baseTy->getWithoutSpecifierType()); + // TODO: Eliminate GenericContextScope entirely + GenericContextScope scope( + IGM, genericEnv + ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr); switch (auto kind = component.getKind()) { case KeyPathPatternComponent::Kind::StoredProperty: { auto property = cast(component.getStoredPropertyDecl()); @@ -790,9 +792,8 @@ emitKeyPathComponent(IRGenModule &IGM, switch (getClassFieldAccess(IGM, loweredBaseContextTy, property)) { case FieldAccess::ConstantDirect: { // Known constant fixed offset. - auto offset = tryEmitConstantClassFragilePhysicalMemberOffset(IGM, - loweredClassTy, - property); + auto offset = tryEmitConstantClassFragilePhysicalMemberOffset( + IGM, loweredClassTy, property); assert(offset && "no constant offset for ConstantDirect field?!"); addFixedOffset(/*struct*/ false, property->isLet(), offset); break; @@ -838,31 +839,28 @@ emitKeyPathComponent(IRGenModule &IGM, SmallVector externalSubArgs; auto componentSig = externalDecl->getInnermostDeclContext() ->getGenericSignatureOfContext(); - - auto componentCanSig = componentSig - ? componentSig->getCanonicalSignature() - : CanGenericSignature(); + + auto componentCanSig = componentSig.getCanonicalSignature(); auto subs = component.getExternalSubstitutions(); if (!subs.empty()) { enumerateGenericSignatureRequirements( - componentSig->getCanonicalSignature(), - [&](GenericRequirement reqt) { - auto substType = reqt.TypeParameter.subst(subs) - ->getCanonicalType(); - if (!reqt.Protocol) { - // Type requirement. - externalSubArgs.push_back( - emitMetadataTypeRefForKeyPath(IGM, substType, componentCanSig)); - } else { - // Protocol requirement. - auto conformance = subs.lookupConformance( - reqt.TypeParameter->getCanonicalType(), reqt.Protocol); - externalSubArgs.push_back( - IGM.emitWitnessTableRefString(substType, *conformance, - genericEnv ? genericEnv->getGenericSignature() : nullptr, - /*shouldSetLowBit*/ true)); - } - }); + componentCanSig, [&](GenericRequirement reqt) { + auto substType = + reqt.TypeParameter.subst(subs)->getCanonicalType(); + if (!reqt.Protocol) { + // Type requirement. + externalSubArgs.push_back(emitMetadataTypeRefForKeyPath( + IGM, substType, componentCanSig)); + } else { + // Protocol requirement. + auto conformance = subs.lookupConformance( + reqt.TypeParameter->getCanonicalType(), reqt.Protocol); + externalSubArgs.push_back(IGM.emitWitnessTableRefString( + substType, conformance, + genericEnv ? genericEnv->getGenericSignature() : nullptr, + /*shouldSetLowBit*/ true)); + } + }); } fields.addInt32( KeyPathComponentHeader::forExternalComponent(externalSubArgs.size()) @@ -1104,7 +1102,8 @@ emitKeyPathComponent(IRGenModule &IGM, case KeyPathPatternComponent::Kind::TupleElement: assert(baseTy->is() && "not a tuple"); - SILType loweredTy = IGM.getLoweredType(baseTy); + SILType loweredTy = IGM.getLoweredType(AbstractionPattern::getOpaque(), + baseTy); // Tuple with fixed layout // @@ -1113,7 +1112,8 @@ emitKeyPathComponent(IRGenModule &IGM, // the compiler knows that the tuple element is always at offset 0. // TODO: If this is behavior is not desired we should find a way to skip to // the next section of code e.g. check if baseTy has archetypes? - if (auto offset = getFixedTupleElementOffset(IGM, loweredTy, component.getTupleIndex())) { + if (auto offset = getFixedTupleElementOffset(IGM, loweredTy, + component.getTupleIndex())) { auto header = KeyPathComponentHeader ::forStructComponentWithInlineOffset(/*isLet*/ false, offset->getValue()); @@ -1331,9 +1331,10 @@ void IRGenModule::emitSILProperty(SILProperty *prop) { SmallVector requirements; CanGenericSignature genericSig; if (genericEnv) { - genericSig = prop->getDecl()->getInnermostDeclContext() - ->getGenericSignatureOfContext() - ->getCanonicalSignature(); + genericSig = prop->getDecl() + ->getInnermostDeclContext() + ->getGenericSignatureOfContext() + .getCanonicalSignature(); enumerateGenericSignatureRequirements(genericSig, [&](GenericRequirement reqt) { requirements.push_back(reqt); }); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index aa570cf02c3b9..392b6c4159f10 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -18,9 +18,10 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" +#include "swift/AST/Attr.h" #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Decl.h" -#include "swift/AST/Attr.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/SubstitutionMap.h" @@ -31,13 +32,13 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/TypeLowering.h" #include "swift/Strings.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclObjC.h" #include "Address.h" #include "Callee.h" @@ -53,6 +54,7 @@ #include "GenPoly.h" #include "GenStruct.h" #include "GenValueWitness.h" +#include "GenericArguments.h" #include "HeapTypeInfo.h" #include "IRGenDebugInfo.h" #include "IRGenMangler.h" @@ -113,15 +115,13 @@ void IRGenModule::setTrueConstGlobal(llvm::GlobalVariable *var) { var->setSection("__TEXT,__const"); break; case llvm::Triple::ELF: + case llvm::Triple::Wasm: var->setSection(".rodata"); break; case llvm::Triple::XCOFF: case llvm::Triple::COFF: var->setSection(".rdata"); break; - case llvm::Triple::Wasm: - var->setSection(".rodata"); - break; } } @@ -327,7 +327,7 @@ namespace { void addGenericParameters() { GenericSignature sig = asImpl().getGenericSignature(); assert(sig); - auto canSig = sig->getCanonicalSignature(); + auto canSig = sig.getCanonicalSignature(); canSig->forEachParam([&](GenericTypeParamType *param, bool canonical) { // Currently, there are only type parameters. The parameter is a key @@ -826,6 +826,7 @@ namespace { auto witness = entry.getAssociatedTypeWitness().Witness->mapTypeOutOfContext(); return IGM.getAssociatedTypeWitness(witness, + Proto->getGenericSignature(), /*inProtocolContext=*/true); } @@ -1595,8 +1596,7 @@ namespace { // TargetRelativeDirectPointer SuperclassType; if (auto superclassType = getType()->getSuperclass()) { GenericSignature genericSig = getType()->getGenericSignature(); - B.addRelativeAddress(IGM.getTypeRef(superclassType->getCanonicalType(), - genericSig, + B.addRelativeAddress(IGM.getTypeRef(superclassType, genericSig, MangledTypeRefRole::Metadata) .first); } else { @@ -1688,10 +1688,8 @@ namespace { auto underlyingType = Type(O->getUnderlyingInterfaceType()) .subst(*O->getUnderlyingTypeSubstitutions()) ->getCanonicalType(sig); - - auto contextSig = O->getGenericSignature() - ? O->getGenericSignature()->getCanonicalSignature() - : CanGenericSignature(); + + auto contextSig = O->getGenericSignature().getCanonicalSignature(); B.addRelativeAddress(IGM.getTypeRef(underlyingType, contextSig, MangledTypeRefRole::Metadata).first); @@ -1836,6 +1834,34 @@ void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) { } } +void irgen::emitLazyMetadataAccessor(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericArguments genericArgs; + genericArgs.collectTypes(IGM, nominal); + + llvm::Function *accessor = IGM.getAddrOfGenericTypeMetadataAccessFunction( + nominal, genericArgs.Types, ForDefinition); + + if (IGM.getOptions().optimizeForSize()) + accessor->addFnAttr(llvm::Attribute::NoInline); + + bool isReadNone = (genericArgs.Types.size() <= + NumDirectGenericTypeMetadataAccessFunctionArgs); + + emitCacheAccessFunction( + IGM, accessor, /*cache*/ nullptr, CacheStrategy::None, + [&](IRGenFunction &IGF, Explosion ¶ms) { + return emitGenericTypeMetadataAccessFunction(IGF, params, nominal, + genericArgs); + }, + isReadNone); +} + +void irgen::emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, + CanType type) { + emitSpecializedGenericStructMetadata(IGM, type); +} + llvm::Constant * IRGenModule::getAddrOfSharedContextDescriptor(LinkEntity entity, ConstantInit definition, @@ -1910,6 +1936,13 @@ IRGenModule::getAddrOfAnonymousContextDescriptor( [&]{ AnonymousContextDescriptorBuilder(*this, DC).emit(); }); } +llvm::Constant * +IRGenModule::getAddrOfOriginalModuleContextDescriptor(StringRef Name) { + return getAddrOfModuleContextDescriptor(OriginalModules.insert({Name, + ModuleDecl::create(Context.getIdentifier(Name), Context)}) + .first->getValue()); +} + static void emitInitializeFieldOffsetVector(IRGenFunction &IGF, SILType T, llvm::Value *metadata, @@ -1939,7 +1972,8 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF, unsigned index = 0; for (auto prop : storedProperties) { - auto propTy = T.getFieldType(prop, IGF.getSILModule()); + auto propTy = T.getFieldType(prop, IGF.getSILModule(), + TypeExpansionContext::minimal()); llvm::Value *metadata = emitTypeLayoutRef(IGF, propTy, collector); Address field = IGF.Builder.CreateConstArrayGEP(fields, index, IGM.getPointerSize()); @@ -2224,6 +2258,7 @@ namespace { void layout() { asImpl().layoutHeader(); + // See also: [pre-5.2-extra-data-zeroing] if (asImpl().hasExtraDataPattern()) { asImpl().addExtraDataPattern(); } @@ -2862,11 +2897,13 @@ namespace { llvm_unreachable("Fixed class metadata cannot have missing members"); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic parameters"); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { llvm_unreachable("Fixed class metadata cannot have generic requirements"); } }; @@ -2901,12 +2938,14 @@ namespace { } } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.TypeMetadataPtrTy); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { // Filled in at runtime. B.addNullPointer(IGM.WitnessTablePtrTy); } @@ -3458,8 +3497,12 @@ namespace { return descriptor; } + llvm::Constant *getNominalTypeDescriptor() { + return emitNominalTypeDescriptor(); + } + void addNominalTypeDescriptor() { - B.add(emitNominalTypeDescriptor()); + B.add(asImpl().getNominalTypeDescriptor()); } ConstantReference emitValueWitnessTable(bool relativeReference) { @@ -3467,14 +3510,18 @@ namespace { return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); } + ConstantReference getValueWitnessTable(bool relativeReference) { + return emitValueWitnessTable(relativeReference); + } + void addValueWitnessTable() { - B.add(emitValueWitnessTable(false).getValue()); + B.add(asImpl().getValueWitnessTable(false).getValue()); } void addFieldOffset(VarDecl *var) { assert(var->hasStorage() && "storing field offset for computed property?!"); - SILType structType = getLoweredType(); + SILType structType = asImpl().getLoweredType(); llvm::Constant *offset = emitPhysicalStructMemberFixedOffset(IGM, structType, var); @@ -3492,13 +3539,29 @@ namespace { B.addAlignmentPadding(super::IGM.getPointerAlignment()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } + + bool hasTrailingFlags() { + return IGM.shouldPrespecializeGenericMetadata(); + } + + void addTrailingFlags() { + auto flags = asImpl().getTrailingFlags(); + + B.addInt(IGM.Int64Ty, flags.getOpaqueValue()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags; + + return flags; + } }; class StructMetadataBuilder : @@ -3571,6 +3634,16 @@ namespace { return StructContextDescriptorBuilder(IGM, Target, RequireMetadata).emit(); } + GenericMetadataPatternFlags getPatternFlags() { + auto flags = super::getPatternFlags(); + + if (IGM.shouldPrespecializeGenericMetadata()) { + flags.setHasTrailingFlags(true); + } + + return flags; + } + ConstantReference emitValueWitnessTable(bool relativeReference) { assert(relativeReference && "should only relative reference"); return getValueWitnessTableForGenericValueType(IGM, Target, @@ -3606,8 +3679,10 @@ namespace { B.add(offset); return; } - assert(IGM.getTypeInfo(Type.getFieldType(field, IGM.getSILModule())) - .isKnownEmpty(ResilienceExpansion::Maximal)); + assert(IGM.getTypeInfo( + Type.getFieldType(field, IGM.getSILModule(), + TypeExpansionContext::minimal())) + .isKnownEmpty(ResilienceExpansion::Maximal)); B.addInt32(0); } @@ -3628,10 +3703,98 @@ namespace { vectorSize }; } + void addTrailingFlags() { this->B.addInt(IGM.Int64Ty, 0); } + bool hasCompletionFunction() { return !isa(IGM.getTypeInfo(getLoweredType())); } }; + + class SpecializedGenericStructMetadataBuilder + : public StructMetadataBuilderBase< + SpecializedGenericStructMetadataBuilder> { + using super = + StructMetadataBuilderBase; + CanType type; + bool HasUnfilledFieldOffset = false; + + protected: + using super::asImpl; + using super::getLoweredType; + using super::IGM; + using super::Target; + + public: + SpecializedGenericStructMetadataBuilder(IRGenModule &IGM, CanType type, + StructDecl &decl, + ConstantStructBuilder &B) + : super(IGM, &decl, B), type(type) {} + + void noteStartOfTypeSpecificMembers() {} + + llvm::Constant *getNominalTypeDescriptor() { + return IGM.getAddrOfTypeContextDescriptor(Target, RequireMetadata); + } + + SILType getLoweredType() { return SILType::getPrimitiveObjectType(type); } + + ConstantReference emitValueWitnessTable(bool relativeReference) { + return irgen::emitValueWitnessTable(IGM, type, false, relativeReference); + } + + ConstantReference getValueWitnessTable(bool relativeReference) { + return emitValueWitnessTable(relativeReference); + } + + void addGenericArgument(GenericRequirement requirement) { + auto t = requirement.TypeParameter.subst(genericSubstitutions()); + ConstantReference ref = IGM.getAddrOfTypeMetadata( + CanType(t), SymbolReferenceKind::Relative_Direct); + B.add(ref.getDirectValue()); + } + + void addGenericWitnessTable(GenericRequirement requirement) { + auto conformance = genericSubstitutions().lookupConformance( + requirement.TypeParameter->getCanonicalType(), requirement.Protocol); + ProtocolConformance *concreteConformance = conformance.getConcrete(); + + llvm::Constant *addr; + + Type argument = requirement.TypeParameter.subst(genericSubstitutions()); + auto argumentNominal = argument->getAnyNominal(); + if (argumentNominal && argumentNominal->isGenericContext()) { + // TODO: Statically specialize the witness table pattern for t's + // conformance. + llvm_unreachable("Statically specializing metadata at generic types is " + "not supported."); + } else { + RootProtocolConformance *rootConformance = + concreteConformance->getRootConformance(); + addr = IGM.getAddrOfWitnessTable(rootConformance); + } + + B.add(addr); + } + + SubstitutionMap genericSubstitutions() { + return type->getContextSubstitutionMap(IGM.getSwiftModule(), + type->getAnyNominal()); + } + + MetadataTrailingFlags getTrailingFlags() { + MetadataTrailingFlags flags = super::getTrailingFlags(); + + flags.setIsStaticSpecialization(true); + flags.setIsCanonicalStaticSpecialization(true); + + return flags; + } + + void flagUnfilledFieldOffset() { HasUnfilledFieldOffset = true; } + + bool canBeConstant() { return !HasUnfilledFieldOffset; } + }; + } // end anonymous namespace /// Emit the type metadata or metadata template for a struct. @@ -3665,6 +3828,27 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { init.finishAndCreateFuture()); } +void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, + CanType type) { + Type ty = type.getPointer(); + auto &context = type->getNominalOrBoundGenericNominal()->getASTContext(); + PrettyStackTraceType stackTraceRAII( + context, "emitting prespecialized metadata for", ty); + ConstantInitBuilder initBuilder(IGM); + auto init = initBuilder.beginStruct(); + init.setPacked(true); + + bool isPattern = false; + + auto &decl = *type.getStructOrBoundGenericStruct(); + SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init); + builder.layout(); + + bool canBeConstant = builder.canBeConstant(); + IGM.defineTypeMetadata(type, isPattern, canBeConstant, + init.finishAndCreateFuture()); +} + // Enums static Optional getConstantPayloadSize(IRGenModule &IGM, @@ -3724,11 +3908,11 @@ namespace { B.add(emitNominalTypeDescriptor()); } - void addGenericArgument() { + void addGenericArgument(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic parameters"); } - void addGenericWitnessTable() { + void addGenericWitnessTable(GenericRequirement requirement) { llvm_unreachable("Concrete type metadata cannot have generic requirements"); } }; @@ -4217,6 +4401,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::Encodable: case KnownProtocolKind::Decodable: case KnownProtocolKind::StringInterpolationProtocol: + case KnownProtocolKind::Differentiable: return SpecialProtocol::None; } diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 4d56cc52f7a80..f62e2995946f1 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -71,6 +71,10 @@ namespace irgen { /// generated definitions. void emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type); + /// Emit the type metadata accessor for a type for which it might be used. + void emitLazyMetadataAccessor(IRGenModule &IGM, NominalTypeDecl *type); + + void emitLazySpecializedGenericTypeMetadata(IRGenModule &IGM, CanType type); /// Emit metadata for a foreign struct, enum or class. void emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl); @@ -81,6 +85,8 @@ namespace irgen { /// Emit the metadata associated with the given enum declaration. void emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum); + void emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type); + /// Get what will be the index into the generic type argument array at the end /// of a nominal type's metadata. int32_t getIndexOfGenericArgument(IRGenModule &IGM, diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index cd8feb4bf8dd3..77ce2bedaedf6 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -773,7 +773,7 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, assert(origMethodType->getNumIndirectFormalResults() == 1); formalIndirectResult = params.claimNext(); } else { - SILType appliedResultTy = origMethodType->getDirectFormalResultsType(); + SILType appliedResultTy = origMethodType->getDirectFormalResultsType(IGM.getSILModule()); indirectedResultTI = &cast(IGM.getTypeInfo(appliedResultTy)); auto &nativeSchema = indirectedResultTI->nativeReturnValueSchema(IGM); @@ -802,8 +802,8 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, } // Otherwise, we have a loadable type that can either be passed directly or // indirectly. - assert(info.getSILStorageType().isObject()); - auto curSILType = info.getSILStorageType(); + assert(info.getSILStorageType(IGM.getSILModule(), origMethodType).isObject()); + auto curSILType = info.getSILStorageType(IGM.getSILModule(), origMethodType); auto &ti = cast(IGM.getTypeInfo(curSILType)); // Load the indirectly passed parameter. @@ -857,9 +857,9 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, cleanup(); auto &callee = emission.getCallee(); auto resultType = - callee.getOrigFunctionType()->getDirectFormalResultsType(); - subIGF.emitScalarReturn(resultType, result, true /*isSwiftCCReturn*/, - false); + callee.getOrigFunctionType()->getDirectFormalResultsType(IGM.getSILModule()); + subIGF.emitScalarReturn(resultType, resultType, result, + true /*isSwiftCCReturn*/, false); } return fwd; @@ -919,8 +919,11 @@ static llvm::Constant *findSwiftAsObjCThunk(IRGenModule &IGM, SILDeclRef ref, assert(SILFn && "no IR function for swift-as-objc thunk"); auto fn = IGM.getAddrOfSILFunction(SILFn, NotForDefinition); ApplyIRLinkage(IRLinkage::Internal).to(fn); - fn->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - + // Don't add the unnamed_addr attribute: in some places Foundation is + // comparing ObjC method pointers. Therefore LLVM's function merging pass must + // not create aliases for identical functions, but create thunks. + // This can be ensured if ObjC methods are not created with the unnamed_addr + // attribute. return llvm::ConstantExpr::getBitCast(fn, IGM.Int8PtrTy); } @@ -1018,8 +1021,9 @@ static SILDeclRef getObjCMethodRef(AbstractFunctionDecl *method) { } static CanSILFunctionType getObjCMethodType(IRGenModule &IGM, - AbstractFunctionDecl *method) { - return IGM.getSILTypes().getConstantFunctionType(getObjCMethodRef(method)); + AbstractFunctionDecl *method) { + return IGM.getSILTypes().getConstantFunctionType( + TypeExpansionContext::minimal(), getObjCMethodRef(method)); } static clang::CanQualType getObjCPropertyType(IRGenModule &IGM, @@ -1028,7 +1032,7 @@ static clang::CanQualType getObjCPropertyType(IRGenModule &IGM, auto getter = property->getOpaqueAccessor(AccessorKind::Get); CanSILFunctionType methodTy = getObjCMethodType(IGM, getter); return IGM.getClangType( - methodTy->getFormalCSemanticResult().getASTType()); + methodTy->getFormalCSemanticResult(IGM.getSILModule()).getASTType()); } void irgen::getObjCEncodingForPropertyType(IRGenModule &IGM, @@ -1049,11 +1053,12 @@ HelperGetObjCEncodingForType(const clang::ASTContext &Context, } static llvm::Constant *getObjCEncodingForTypes(IRGenModule &IGM, - SILType resultType, + CanSILFunctionType fnType, ArrayRef params, StringRef fixedParamsString, Size::int_type parmOffset, bool useExtendedEncoding) { + auto resultType = fnType->getFormalCSemanticResult(IGM.getSILModule()); auto &clangASTContext = IGM.getClangASTContext(); std::string encodingString; @@ -1071,7 +1076,8 @@ static llvm::Constant *getObjCEncodingForTypes(IRGenModule &IGM, // TODO. Encode type qualifier, 'in', 'inout', etc. for the parameter. std::string paramsString; for (auto param : params) { - auto clangType = IGM.getClangType(param.getType()); + auto clangType = IGM.getClangType( + param.getArgumentType(IGM.getSILModule(), fnType)); if (clangType.isNull()) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); @@ -1093,8 +1099,6 @@ static llvm::Constant *getObjCEncodingForTypes(IRGenModule &IGM, static llvm::Constant *getObjCEncodingForMethodType(IRGenModule &IGM, CanSILFunctionType fnType, bool useExtendedEncoding) { - SILType resultType = fnType->getFormalCSemanticResult(); - // Get the inputs without 'self'. auto inputs = fnType->getParameters().drop_back(); @@ -1103,59 +1107,60 @@ static llvm::Constant *getObjCEncodingForMethodType(IRGenModule &IGM, specialParams += "@0:"; auto ptrSize = IGM.getPointerSize().getValue(); specialParams += llvm::itostr(ptrSize); - GenericContextScope scope(IGM, fnType->getGenericSignature()); - return getObjCEncodingForTypes(IGM, resultType, inputs, specialParams, + GenericContextScope scope(IGM, fnType->getInvocationGenericSignature()); + return getObjCEncodingForTypes(IGM, fnType, inputs, specialParams, ptrSize * 2, useExtendedEncoding); } /// Emit the components of an Objective-C method descriptor: its selector, /// type encoding, and IMP pointer. -SILFunction *irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM, - AbstractFunctionDecl *method, - bool concrete, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM, + AbstractFunctionDecl *method, + bool concrete) { + ObjCMethodDescriptor descriptor{}; Selector selector(method); /// The first element is the selector. - selectorRef = IGM.getAddrOfObjCMethodName(selector.str()); + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(selector.str()); /// The second element is the method signature. A method signature is made of /// the return type @encoding and every parameter type @encoding, glued with /// numbers that used to represent stack offsets for each of these elements. CanSILFunctionType methodType = getObjCMethodType(IGM, method); - atEncoding = getObjCEncodingForMethodType(IGM, methodType, /*extended*/false); + descriptor.typeEncoding = getObjCEncodingForMethodType(IGM, methodType, /*extended*/false); /// The third element is the method implementation pointer. if (!concrete) { - impl = nullptr; - return nullptr; + descriptor.impl = nullptr; + descriptor.silFunction = nullptr; + return descriptor; } - SILFunction *silFn = nullptr; + descriptor.silFunction = nullptr; + if (auto func = dyn_cast(method)) - impl = getObjCMethodPointer(IGM, func, silFn); + descriptor.impl = getObjCMethodPointer(IGM, func, descriptor.silFunction); else if (auto ctor = dyn_cast(method)) - impl = getObjCMethodPointer(IGM, ctor, silFn); + descriptor.impl = getObjCMethodPointer(IGM, ctor, descriptor.silFunction); else - impl = getObjCMethodPointer(IGM, cast(method), silFn); - return silFn; + descriptor.impl = getObjCMethodPointer(IGM, cast(method), + descriptor.silFunction); + return descriptor; } /// Emit the components of an Objective-C method descriptor for a /// property getter method. -SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, VarDecl *property) { Selector getterSel(property, Selector::ForGetter); - selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); + ObjCMethodDescriptor descriptor{}; + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); auto clangType = getObjCPropertyType(IGM, property); if (clangType.isNull()) { - atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - return nullptr; + descriptor.typeEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); + descriptor.silFunction = nullptr; + return descriptor; } auto &clangASTContext = IGM.getClangASTContext(); @@ -1168,58 +1173,53 @@ SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, TypeStr += llvm::itostr(ParmOffset); TypeStr += "@0:"; TypeStr += llvm::itostr(PtrSize.getValue()); - atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); - SILFunction *silFn = nullptr; - impl = getObjCGetterPointer(IGM, property, silFn); - return silFn; + descriptor.typeEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); + descriptor.silFunction = nullptr; + descriptor.impl = getObjCGetterPointer(IGM, property, descriptor.silFunction); + return descriptor; } /// Emit the components of an Objective-C method descriptor for a /// subscript getter method. -SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript) { Selector getterSel(subscript, Selector::ForGetter); - selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); + ObjCMethodDescriptor descriptor{}; + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); auto methodTy = getObjCMethodType(IGM, subscript->getOpaqueAccessor(AccessorKind::Get)); - atEncoding = getObjCEncodingForMethodType(IGM, methodTy, /*extended*/false); - SILFunction *silFn = nullptr; - impl = getObjCGetterPointer(IGM, subscript, silFn); - return silFn; + descriptor.typeEncoding = getObjCEncodingForMethodType(IGM, methodTy, + /*extended*/false); + descriptor.silFunction = nullptr; + descriptor.impl = getObjCGetterPointer(IGM, subscript, + descriptor.silFunction); + return descriptor; } -SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *decl, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *decl) { if (auto sub = dyn_cast(decl)) { - return emitObjCGetterDescriptorParts(IGM, sub, - selectorRef, atEncoding, impl); + return emitObjCGetterDescriptorParts(IGM, sub); } if (auto var = dyn_cast(decl)) { - return emitObjCGetterDescriptorParts(IGM, var, - selectorRef, atEncoding, impl); + return emitObjCGetterDescriptorParts(IGM, var); } llvm_unreachable("unknown storage!"); - return nullptr; } /// Emit the components of an Objective-C method descriptor for a /// property getter method. -SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + VarDecl *property) { assert(property->isSettable(property->getDeclContext()) && "not a settable property?!"); Selector setterSel(property, Selector::ForSetter); - selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str()); + ObjCMethodDescriptor descriptor{}; + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str()); auto &clangASTContext = IGM.getClangASTContext(); std::string TypeStr; @@ -1231,8 +1231,9 @@ SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, clangType = getObjCPropertyType(IGM, property); if (clangType.isNull()) { - atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - return nullptr; + descriptor.typeEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); + descriptor.silFunction = nullptr; + return descriptor; } clang::CharUnits sz = clangASTContext.getObjCEncodingTypeSize(clangType); if (!sz.isZero()) @@ -1243,59 +1244,66 @@ SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, ParmOffset = 2 * PtrSize.getValue(); clangASTContext.getObjCEncodingForType(clangType, TypeStr); TypeStr += llvm::itostr(ParmOffset); - atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); - SILFunction *silFn = nullptr; - impl = getObjCSetterPointer(IGM, property, silFn); - return silFn; + descriptor.typeEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); + descriptor.silFunction = nullptr; + descriptor.impl = getObjCSetterPointer(IGM, property, descriptor.silFunction); + return descriptor; } /// Emit the components of an Objective-C method descriptor for a /// subscript getter method. -SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript) { assert(subscript->supportsMutation() && "not a settable subscript?!"); Selector setterSel(subscript, Selector::ForSetter); - selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str()); + ObjCMethodDescriptor descriptor{}; + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str()); auto methodTy = getObjCMethodType(IGM, subscript->getOpaqueAccessor(AccessorKind::Set)); - atEncoding = getObjCEncodingForMethodType(IGM, methodTy, /*extended*/false); - SILFunction *silFn = nullptr; - impl = getObjCSetterPointer(IGM, subscript, silFn); - return silFn; + descriptor.typeEncoding = getObjCEncodingForMethodType(IGM, methodTy, + /*extended*/false); + descriptor.silFunction = nullptr; + descriptor.impl = getObjCSetterPointer(IGM, subscript, + descriptor.silFunction); + return descriptor; } -SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *decl, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +ObjCMethodDescriptor +irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *decl) { if (auto sub = dyn_cast(decl)) { - return emitObjCSetterDescriptorParts(IGM, sub, - selectorRef, atEncoding, impl); + return emitObjCSetterDescriptorParts(IGM, sub); } if (auto var = dyn_cast(decl)) { - return emitObjCSetterDescriptorParts(IGM, var, - selectorRef, atEncoding, impl); + return emitObjCSetterDescriptorParts(IGM, var); } llvm_unreachable("unknown storage!"); - return nullptr; } static void buildMethodDescriptor(ConstantArrayBuilder &descriptors, - llvm::Constant *selectorRef, - llvm::Constant *atEncoding, - llvm::Constant *impl) { + ObjCMethodDescriptor &parts) { auto descriptor = descriptors.beginStruct(); - descriptor.add(selectorRef); - descriptor.add(atEncoding); - descriptor.add(impl); + descriptor.add(parts.selectorRef); + descriptor.add(parts.typeEncoding); + descriptor.add(parts.impl); descriptor.finishAndAddTo(descriptors); } +static void emitObjCDescriptor(IRGenModule &IGM, + ConstantArrayBuilder &descriptors, + ObjCMethodDescriptor &descriptor) { + buildMethodDescriptor(descriptors, descriptor); + auto *silFn = descriptor.silFunction; + if (silFn && silFn->hasObjCReplacement()) { + auto replacedSelector = + IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); + descriptor.selectorRef = replacedSelector; + buildMethodDescriptor(descriptors, descriptor); + } +} + /// Emit an Objective-C method descriptor for the given method. /// struct method_t { /// SEL name; @@ -1305,16 +1313,9 @@ static void buildMethodDescriptor(ConstantArrayBuilder &descriptors, void irgen::emitObjCMethodDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractFunctionDecl *method) { - llvm::Constant *selectorRef, *atEncoding, *impl; - auto silFn = emitObjCMethodDescriptorParts(IGM, method, /*concrete*/ true, - selectorRef, atEncoding, impl); - buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); - - if (silFn && silFn->hasObjCReplacement()) { - auto replacedSelector = - IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); - buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); - } + ObjCMethodDescriptor descriptor( + emitObjCMethodDescriptorParts(IGM, method, /*concrete*/ true)); + emitObjCDescriptor(IGM, descriptors, descriptor); } void irgen::emitObjCIVarInitDestroyDescriptor(IRGenModule &IGM, @@ -1329,7 +1330,8 @@ void irgen::emitObjCIVarInitDestroyDescriptor(IRGenModule &IGM, 1, /*foreign*/ true); Selector selector(declRef); - auto selectorRef = IGM.getAddrOfObjCMethodName(selector.str()); + ObjCMethodDescriptor descriptor{}; + descriptor.selectorRef = IGM.getAddrOfObjCMethodName(selector.str()); /// The second element is the method signature. A method signature is made of /// the return type @encoding and every parameter type @encoding, glued with @@ -1337,13 +1339,13 @@ void irgen::emitObjCIVarInitDestroyDescriptor(IRGenModule &IGM, auto ptrSize = IGM.getPointerSize().getValue(); llvm::SmallString<8> signature; signature = "v" + llvm::itostr(ptrSize * 2) + "@0:" + llvm::itostr(ptrSize); - auto atEncoding = IGM.getAddrOfGlobalString(signature); + descriptor.typeEncoding = IGM.getAddrOfGlobalString(signature); /// The third element is the method implementation pointer. - auto impl = llvm::ConstantExpr::getBitCast(objcImpl, IGM.Int8PtrTy); + descriptor.impl = llvm::ConstantExpr::getBitCast(objcImpl, IGM.Int8PtrTy); // Form the method_t instance. - buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); + buildMethodDescriptor(descriptors, descriptor); } llvm::Constant * @@ -1356,13 +1358,11 @@ irgen::getMethodTypeExtendedEncoding(IRGenModule &IGM, llvm::Constant * irgen::getBlockTypeExtendedEncoding(IRGenModule &IGM, CanSILFunctionType invokeTy) { - SILType resultType = invokeTy->getFormalCSemanticResult(); - // Skip the storage pointer, which is encoded as '@?' to avoid the infinite // recursion of the usual '@?<...>' rule for blocks. auto paramTypes = invokeTy->getParameters().slice(1); - return getObjCEncodingForTypes(IGM, resultType, paramTypes, + return getObjCEncodingForTypes(IGM, invokeTy, paramTypes, "@?0", IGM.getPointerSize().getValue(), /*extended*/ true); } @@ -1370,29 +1370,15 @@ irgen::getBlockTypeExtendedEncoding(IRGenModule &IGM, void irgen::emitObjCGetterDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractStorageDecl *storage) { - llvm::Constant *selectorRef, *atEncoding, *impl; - auto *silFn = emitObjCGetterDescriptorParts(IGM, storage, - selectorRef, atEncoding, impl); - buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); - if (silFn && silFn->hasObjCReplacement()) { - auto replacedSelector = - IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); - buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); - } + ObjCMethodDescriptor descriptor(emitObjCGetterDescriptorParts(IGM, storage)); + emitObjCDescriptor(IGM, descriptors, descriptor); } void irgen::emitObjCSetterDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractStorageDecl *storage) { - llvm::Constant *selectorRef, *atEncoding, *impl; - auto *silFn = emitObjCSetterDescriptorParts(IGM, storage, - selectorRef, atEncoding, impl); - buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); - if (silFn && silFn->hasObjCReplacement()) { - auto replacedSelector = - IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); - buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); - } + ObjCMethodDescriptor descriptor(emitObjCSetterDescriptorParts(IGM, storage)); + emitObjCDescriptor(IGM, descriptors, descriptor); } bool irgen::requiresObjCMethodDescriptor(FuncDecl *method) { diff --git a/lib/IRGen/GenObjC.h b/lib/IRGen/GenObjC.h index aa396df7c7e70..64ec654afc3b2 100644 --- a/lib/IRGen/GenObjC.h +++ b/lib/IRGen/GenObjC.h @@ -105,58 +105,51 @@ namespace irgen { llvm::Value *emitObjCAutoreleaseReturnValue(IRGenFunction &IGF, llvm::Value *value); + struct ObjCMethodDescriptor { + llvm::Constant *selectorRef = nullptr; + llvm::Constant *typeEncoding = nullptr; + llvm::Constant *impl = nullptr; + SILFunction *silFunction = nullptr; + }; + /// Build the components of an Objective-C method descriptor for the given /// method or constructor implementation. - SILFunction *emitObjCMethodDescriptorParts(IRGenModule &IGM, - AbstractFunctionDecl *method, - bool concrete, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + ObjCMethodDescriptor + emitObjCMethodDescriptorParts(IRGenModule &IGM, + AbstractFunctionDecl *method, + bool concrete); /// Build the components of an Objective-C method descriptor for the given /// property's method implementations. - SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + ObjCMethodDescriptor emitObjCGetterDescriptorParts(IRGenModule &IGM, + VarDecl *property); /// Build the components of an Objective-C method descriptor for the given /// subscript's method implementations. - SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); - - SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + ObjCMethodDescriptor emitObjCGetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *property); + + /// Build the components of an Objective-C method descriptor if the abstract + /// storage refers to a property or a subscript. + ObjCMethodDescriptor + emitObjCGetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *subscript); /// Build the components of an Objective-C method descriptor for the given /// property's method implementations. - SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + ObjCMethodDescriptor emitObjCSetterDescriptorParts(IRGenModule &IGM, + VarDecl *property); /// Build the components of an Objective-C method descriptor for the given /// subscript's method implementations. - SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); - - SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + ObjCMethodDescriptor emitObjCSetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *property); + + /// Build the components of an Objective-C method descriptor if the abstract + /// storage refers to a property or a subscript. + ObjCMethodDescriptor + emitObjCSetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *subscript); /// Build an Objective-C method descriptor for the given method, /// constructor, or destructor implementation. diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 71d4908248292..aa41956afa039 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -293,7 +293,7 @@ enumerateUnfulfilledRequirements(const RequirementCallback &callback) { } void PolymorphicConvention::initGenerics() { - Generics = FnType->getGenericSignature(); + Generics = FnType->getInvocationGenericSignature(); } void PolymorphicConvention::considerNewTypeSource(MetadataSource::Kind kind, @@ -322,15 +322,15 @@ bool PolymorphicConvention::considerType(CanType type, IsExact_t isExact, } void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) { - CanType selfTy = fnType->getSelfInstanceType(); - auto conformance = fnType->getWitnessMethodConformance(); + CanType selfTy = fnType->getSelfInstanceType(IGM.getSILModule()); + auto conformance = fnType->getWitnessMethodConformanceOrInvalid(); // First, bind type metadata for Self. Sources.emplace_back(MetadataSource::Kind::SelfMetadata, MetadataSource::InvalidSourceIndex, selfTy); - if (fnType->getSelfInstanceType()->is()) { + if (selfTy->is()) { // The Self type is abstract, so we can fulfill its metadata from // the Self metadata parameter. addSelfMetadataFulfillment(selfTy); @@ -347,7 +347,7 @@ void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) { void PolymorphicConvention::considerObjCGenericSelf(CanSILFunctionType fnType) { // If this is a static method, get the instance type. - CanType selfTy = fnType->getSelfInstanceType(); + CanType selfTy = fnType->getSelfInstanceType(IGM.getSILModule()); unsigned paramIndex = fnType->getParameters().size() - 1; // Bind type metadata for Self. @@ -364,7 +364,7 @@ void PolymorphicConvention::considerObjCGenericSelf(CanSILFunctionType fnType) { void PolymorphicConvention::considerParameter(SILParameterInfo param, unsigned paramIndex, bool isSelfParameter) { - auto type = param.getType(); + auto type = param.getArgumentType(IGM.getSILModule(), FnType); switch (param.getConvention()) { // Indirect parameters do give us a value we can use, but right now // we don't bother, for no good reason. But if this is 'self', @@ -454,7 +454,7 @@ void irgen::enumerateGenericParamFulfillments(IRGenModule &IGM, // Check if any requirements were fulfilled by metadata stored inside a // captured value. - auto generics = fnType->getGenericSignature(); + auto generics = fnType->getInvocationGenericSignature(); for (auto genericParam : generics->getGenericParams()) { auto genericParamType = genericParam->getCanonicalType(); @@ -514,7 +514,8 @@ CanType EmitPolymorphicParameters::getTypeInContext(CanType type) const { } CanType EmitPolymorphicParameters::getArgTypeInContext(unsigned paramIndex) const { - return getTypeInContext(FnType->getParameters()[paramIndex].getType()); + return getTypeInContext(FnType->getParameters()[paramIndex] + .getArgumentType(IGM.getSILModule(), FnType)); } void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, @@ -543,7 +544,7 @@ void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, assert(metadata && "no Self metadata for witness method"); // Mark this as the cached metatype for Self. - auto selfTy = FnType->getSelfInstanceType(); + auto selfTy = FnType->getSelfInstanceType(IGM.getSILModule()); CanType argTy = getTypeInContext(selfTy); setTypeMetadataName(IGF.IGM, metadata, argTy); auto *CD = selfTy.getClassOrBoundGenericClass(); @@ -563,10 +564,10 @@ void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source, assert(selfTable && "no Self witness table for witness method"); // Mark this as the cached witness table for Self. - auto conformance = FnType->getWitnessMethodConformance(); + auto conformance = FnType->getWitnessMethodConformanceOrInvalid(); auto selfProto = conformance.getRequirement(); - auto selfTy = FnType->getSelfInstanceType(); + auto selfTy = FnType->getSelfInstanceType(IGM.getSILModule()); CanType argTy = getTypeInContext(selfTy); setProtocolWitnessTableName(IGF.IGM, selfTable, argTy, selfProto); @@ -697,7 +698,8 @@ void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex) return; assert(nativeParam.size() == 1); - auto paramType = SubstFnType->getParameters()[paramIndex].getType(); + auto paramType = SubstFnType->getParameters()[paramIndex] + .getArgumentType(IGM.getSILModule(), SubstFnType); llvm::Value *instanceRef = nativeParam.getAll()[0]; SILType instanceType = SILType::getPrimitiveObjectType(paramType); llvm::Value *metadata = @@ -826,6 +828,15 @@ bool IRGenModule::isResilientConformance( conformanceModule == conformance->getProtocol()->getParentModule()) return false; + // If the protocol WAS from the current module (@_originallyDefinedIn), we + // consider the conformance non-resilient, because we used to consider it + // non-resilient before the symbol moved. This is to ensure ABI stability + // across module boundaries. + if (conformanceModule == getSwiftModule() && + conformanceModule->getName().str() == + conformance->getProtocol()->getAlternateModuleName()) + return false; + // If the protocol and the conformance are in the same module and the // conforming type is not generic, they're not resilient. // @@ -954,9 +965,9 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF, rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) { auto substType = type.subst(subMap)->getCanonicalType(); auto reqConformance = subMap.lookupConformance(type, proto); - assert(reqConformance && "conditional conformance must exist"); + assert(reqConformance && "conditional conformance must be valid"); - tables.push_back(emitWitnessTableRef(IGF, substType, *reqConformance)); + tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance)); return /*finished?*/ false; }); @@ -1292,7 +1303,10 @@ class AccessorConformanceInfo : public ConformanceInfo { auto associate = Conformance.getTypeWitness(requirement.getAssociation()); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + Conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); Table.addBitCast(witness, IGM.Int8PtrTy); } @@ -1425,6 +1439,7 @@ void WitnessTableBuilder::build() { } llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, + GenericSignature sig, bool inProtocolContext) { // FIXME: If we can directly reference constant type metadata, do so. @@ -1433,7 +1448,7 @@ llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type, auto role = inProtocolContext ? MangledTypeRefRole::DefaultAssociatedTypeWitness : MangledTypeRefRole::Metadata; - auto typeRef = getTypeRef(type, /*generic signature*/nullptr, role).first; + auto typeRef = getTypeRef(type, sig, role).first; // Set the low bit to indicate that this is a mangled name. auto witness = llvm::ConstantExpr::getPtrToInt(typeRef, IntPtrTy); @@ -1602,7 +1617,10 @@ void WitnessTableBuilder::collectResilientWitnesses( auto associate = conformance.getTypeWitness(assocType); llvm::Constant *witness = - IGM.getAssociatedTypeWitness(associate, /*inProtocolContext=*/false); + IGM.getAssociatedTypeWitness( + associate, + conformance.getDeclContext()->getGenericSignatureOfContext(), + /*inProtocolContext=*/false); resilientWitnesses.push_back(witness); continue; } @@ -2415,8 +2433,8 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF, // conformance kind. } else { assert(requirement.Protocol && "index mismatch!"); - auto conformance = *subs.lookupConformance(requirement.TypeParameter, - requirement.Protocol); + auto conformance = subs.lookupConformance(requirement.TypeParameter, + requirement.Protocol); assert(conformance.getRequirement() == requirement.Protocol); sourceKey.Kind = LocalTypeDataKind::forProtocolWitnessTable(conformance); @@ -2699,7 +2717,7 @@ void NecessaryBindings::addTypeMetadata(CanType type) { return; } if (auto fn = dyn_cast(type)) { - for (const auto &elt : fn.getParams()) + for (const auto elt : fn.getParams()) addTypeMetadata(elt.getPlainType()); addTypeMetadata(fn.getResult()); return; @@ -2777,14 +2795,15 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, return wtable; } -static CanType getSubstSelfType(CanSILFunctionType origFnType, +static CanType getSubstSelfType(IRGenModule &IGM, + CanSILFunctionType origFnType, SubstitutionMap subs) { // Grab the apparent 'self' type. If there isn't a 'self' type, // we're not going to try to access this anyway. assert(!origFnType->getParameters().empty()); auto selfParam = origFnType->getParameters().back(); - CanType inputType = selfParam.getType(); + CanType inputType = selfParam.getArgumentType(IGM.getSILModule(), origFnType); // If the parameter is a direct metatype parameter, this is a static method // of the instance type. We can assume this because: // - metatypes cannot directly conform to protocols @@ -2833,7 +2852,8 @@ namespace { // Needs a special argument. case MetadataSource::Kind::GenericLValueMetadata: { - out.add(IGF.emitTypeMetadataRef(getSubstSelfType(FnType, subs))); + out.add( + IGF.emitTypeMetadataRef(getSubstSelfType(IGF.IGM, FnType, subs))); continue; } @@ -2886,7 +2906,8 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs, case MetadataSource::Kind::SelfMetadata: { assert(witnessMetadata && "no metadata structure for witness method"); - auto self = IGF.emitTypeMetadataRef(getSubstSelfType(FnType, subs)); + auto self = IGF.emitTypeMetadataRef( + getSubstSelfType(IGF.IGM, FnType, subs)); witnessMetadata->SelfMetadata = self; continue; } @@ -2919,8 +2940,8 @@ NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, CanType type = requirement.TypeParameter.subst(subs)->getCanonicalType(); if (requirement.Protocol) { - auto conf = *subs.lookupConformance(requirement.TypeParameter, - requirement.Protocol); + auto conf = subs.lookupConformance(requirement.TypeParameter, + requirement.Protocol); bindings.addProtocolConformance(type, conf); } else { bindings.addTypeMetadata(type); @@ -2935,11 +2956,11 @@ NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, continue; case MetadataSource::Kind::GenericLValueMetadata: - bindings.addTypeMetadata(getSubstSelfType(origType, subs)); + bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); continue; case MetadataSource::Kind::SelfMetadata: - bindings.addTypeMetadata(getSubstSelfType(origType, subs)); + bindings.addTypeMetadata(getSubstSelfType(IGM, origType, subs)); continue; case MetadataSource::Kind::SelfWitnessTable: @@ -2965,12 +2986,13 @@ GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM, if (!ncGenerics || ncGenerics->areAllParamsConcrete()) return; // Construct a representative function type. - auto generics = ncGenerics->getCanonicalSignature(); + auto generics = ncGenerics.getCanonicalSignature(); auto fnType = SILFunctionType::get(generics, SILFunctionType::ExtInfo(), SILCoroutineKind::None, /*callee*/ ParameterConvention::Direct_Unowned, /*params*/ {}, /*yields*/ {}, /*results*/ {}, /*error*/ None, + /*subs*/ SubstitutionMap(), /*implied*/ false, IGM.Context); // Figure out what we're actually still required to pass @@ -2993,11 +3015,11 @@ GenericTypeRequirements::enumerateFulfillments(IRGenModule &IGM, auto &reqt = getRequirements()[reqtIndex]; CanType type = reqt.TypeParameter.subst(subs)->getCanonicalType(); if (reqt.Protocol) { - auto conformance = *subs.lookupConformance(reqt.TypeParameter, - reqt.Protocol); + auto conformance = + subs.lookupConformance(reqt.TypeParameter, reqt.Protocol); callback(reqtIndex, type, conformance); } else { - callback(reqtIndex, type, None); + callback(reqtIndex, type, ProtocolConformanceRef::forInvalid()); } } } @@ -3008,7 +3030,7 @@ void GenericTypeRequirements::emitInitOfBuffer(IRGenFunction &IGF, if (Requirements.empty()) return; auto generics = - TheDecl->getGenericSignatureOfContext()->getCanonicalSignature(); + TheDecl->getGenericSignatureOfContext().getCanonicalSignature(); auto &module = *TheDecl->getParentModule(); emitInitOfGenericRequirementsBuffer(IGF, Requirements, buffer, [&](GenericRequirement requirement) { @@ -3057,7 +3079,7 @@ irgen::emitGenericRequirementFromSubstitutions(IRGenFunction &IGF, } auto proto = requirement.Protocol; - auto conformance = *subs.lookupConformance(depTy, proto); + auto conformance = subs.lookupConformance(depTy, proto); assert(conformance.getRequirement() == proto); llvm::Value *metadata = nullptr; auto wtable = emitWitnessTableRef(IGF, argType, &metadata, conformance); @@ -3179,10 +3201,9 @@ void irgen::expandTrailingWitnessSignature(IRGenModule &IGM, out.push_back(IGM.WitnessTablePtrTy); } -FunctionPointer -irgen::emitWitnessMethodValue(IRGenFunction &IGF, - llvm::Value *wtable, - SILDeclRef member) { +FunctionPointer irgen::emitWitnessMethodValue(IRGenFunction &IGF, + llvm::Value *wtable, + SILDeclRef member) { auto *fn = cast(member.getDecl()); auto proto = cast(fn->getDeclContext()); @@ -3195,7 +3216,8 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF, emitInvariantLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable()); - auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType(member); + auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType( + IGF.IGM.getMaximalTypeExpansionContext(), member); Signature signature = IGF.IGM.getSignature(fnType); witnessFnPtr = IGF.Builder.CreateBitCast(witnessFnPtr, signature.getType()->getPointerTo()); @@ -3203,12 +3225,9 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF, return FunctionPointer(witnessFnPtr, signature); } -FunctionPointer -irgen::emitWitnessMethodValue(IRGenFunction &IGF, - CanType baseTy, - llvm::Value **baseMetadataCache, - SILDeclRef member, - ProtocolConformanceRef conformance) { +FunctionPointer irgen::emitWitnessMethodValue( + IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache, + SILDeclRef member, ProtocolConformanceRef conformance) { llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache, conformance); diff --git a/lib/IRGen/GenProto.h b/lib/IRGen/GenProto.h index 7d602cc80b291..f91b802841c85 100644 --- a/lib/IRGen/GenProto.h +++ b/lib/IRGen/GenProto.h @@ -64,8 +64,7 @@ namespace irgen { /// Extract the method pointer from an archetype's witness table /// as a function value. - FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, - CanType baseTy, + FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache, SILDeclRef member, ProtocolConformanceRef conformance); diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 50eeb1126a6cb..04b269a990675 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -322,8 +322,7 @@ std::pair IRGenModule::getTypeRef(Type type, GenericSignature genericSig, MangledTypeRefRole role) { return getTypeRef(type->getCanonicalType(genericSig), - genericSig ? genericSig->getCanonicalSignature() : CanGenericSignature(), - role); + genericSig.getCanonicalSignature(), role); } std::pair @@ -359,7 +358,7 @@ IRGenModule::emitWitnessTableRefString(CanType type, GenericEnvironment *genericEnv = nullptr; if (origGenericSig) { - genericSig = origGenericSig->getCanonicalSignature(); + genericSig = origGenericSig.getCanonicalSignature(); enumerateGenericSignatureRequirements(genericSig, [&](GenericRequirement reqt) { requirements.push_back(reqt); }); genericEnv = genericSig->getGenericEnvironment(); @@ -523,9 +522,7 @@ class ReflectionMetadataBuilder { MangledTypeRefRole role = MangledTypeRefRole::Reflection) { addTypeRef(type->getCanonicalType(genericSig), - genericSig ? genericSig->getCanonicalSignature() - : CanGenericSignature(), - role); + genericSig.getCanonicalSignature(), role); } /// Add a 32-bit relative offset to a mangled typeref string @@ -647,9 +644,7 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { auto NameGlobal = IGM.getAddrOfFieldName(AssocTy.first); B.addRelativeAddress(NameGlobal); addTypeRef(AssocTy.second, - Nominal->getGenericSignature() - ? Nominal->getGenericSignature()->getCanonicalSignature() - : CanGenericSignature()); + Nominal->getGenericSignature().getCanonicalSignature()); } } @@ -671,7 +666,10 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { }; class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { - const uint32_t fieldRecordSize = 12; +public: + static const uint32_t FieldRecordSize = 12; + +private: const NominalTypeDecl *NTD; void addFieldDecl(const ValueDecl *value, Type type, @@ -714,7 +712,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); auto properties = NTD->getStoredProperties(); B.addInt32(properties.size()); @@ -737,7 +735,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(strategy.getElementsWithPayload().size() + strategy.getElementsWithNoPayload().size()); @@ -764,7 +762,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { else Kind = FieldDescriptorKind::Protocol; B.addInt16(uint16_t(Kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(0); } @@ -825,6 +823,57 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } }; +static bool +deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(IRGenModule &IGM) { + auto target = IGM.Context.LangOpts.Target; + + if (target.isMacOSX() && target.isMacOSXVersionLT(10, 16, 0)) { + return true; + } + if (target.isiOS() && target.isOSVersionLT(14)) { // includes tvOS + return true; + } + if (target.isWatchOS() && target.isOSVersionLT(7)) { + return true; + } + + return false; +} + +/// Metadata builder that emits a fixed-layout empty type as an empty struct, as +/// a workaround for a RemoteMirror crash in older OSes. +class EmptyStructMetadataBuilder : public ReflectionMetadataBuilder { + const NominalTypeDecl *NTD; + + void layout() override { + addNominalRef(NTD); + B.addInt32(0); + B.addInt16(uint16_t(FieldDescriptorKind::Struct)); + B.addInt16(FieldTypeMetadataBuilder::FieldRecordSize); + B.addInt32(0); + } + +public: + EmptyStructMetadataBuilder(IRGenModule &IGM, + const NominalTypeDecl *NTD) + : ReflectionMetadataBuilder(IGM), NTD(NTD) { + assert(IGM.getTypeInfoForUnlowered( + NTD->getDeclaredTypeInContext()->getCanonicalType()) + .isKnownEmpty(ResilienceExpansion::Maximal) + && "should only be used for known empty types"); + } + + llvm::GlobalVariable *emit() { + auto section = IGM.getFieldTypeMetadataSectionName(); + return ReflectionMetadataBuilder::emit( + [&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* { + return IGM.getAddrOfReflectionFieldDescriptor( + NTD->getDeclaredType()->getCanonicalType(), definition); + }, + section); + } +}; + class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder { ModuleDecl *module; CanType type; @@ -1095,10 +1144,9 @@ class CaptureDescriptorBuilder : public ReflectionMetadataBuilder { B.addInt32(MetadataSources.size()); B.addInt32(Layout.getBindings().size()); - auto sig = OrigCalleeType->getGenericSignature() - ? OrigCalleeType->getGenericSignature()->getCanonicalSignature() - : CanGenericSignature(); - + auto sig = + OrigCalleeType->getSubstGenericSignature().getCanonicalSignature(); + // Now add typerefs of all of the captures. for (auto CaptureType : CaptureTypes) { addLoweredTypeRef(CaptureType, sig); @@ -1337,6 +1385,19 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) { } if (needsOpaqueDescriptor) { + // Work around an issue in the RemoteMirror library that ships in + // macOS 10.15/iOS 13 and earlier that causes it to crash on a + // BuiltinTypeDescriptor with zero size. If the type has zero size, emit it + // as an empty struct instead, which will have the same impact on the + // encoded type layout. + auto &TI = getTypeInfoForUnlowered(T); + if (deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(*this) + && TI.isKnownEmpty(ResilienceExpansion::Maximal)) { + EmptyStructMetadataBuilder builder(*this, D); + builder.emit(); + return; + } + FixedTypeMetadataBuilder builder(*this, D); builder.emit(); } diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index 29404f721a521..022cc0496abbe 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -74,7 +74,8 @@ namespace { } SILType getType(IRGenModule &IGM, SILType T) const { - return T.getFieldType(Field, IGM.getSILModule()); + return T.getFieldType(Field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } }; @@ -95,7 +96,8 @@ namespace { SILType getType(IRGenModule &IGM, SILType T) const { if (Field) - return T.getFieldType(Field, IGM.getSILModule()); + return T.getFieldType(Field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); // The Swift-field-less cases use opaque storage, which is // guaranteed to ignore the type passed to it. @@ -185,6 +187,7 @@ namespace { switch (fieldInfo.getKind()) { case ElementLayout::Kind::Fixed: case ElementLayout::Kind::Empty: + case ElementLayout::Kind::EmptyTailAllocatedCType: return MemberAccessStrategy::getDirectFixed( fieldInfo.getFixedByteOffset()); case ElementLayout::Kind::InitialNonFixedSize: @@ -278,6 +281,7 @@ namespace { break; } case ElementLayout::Kind::Empty: + case ElementLayout::Kind::EmptyTailAllocatedCType: case ElementLayout::Kind::InitialNonFixedSize: case ElementLayout::Kind::NonFixed: continue; @@ -409,17 +413,14 @@ namespace { } llvm::Value *getOffsetForIndex(IRGenFunction &IGF, unsigned index) override { - // TODO: do this with StructMetadataLayout::getFieldOffset - - // Get the field offset vector from the struct metadata. + auto &layout = + IGF.IGM.getMetadataLayout(TheStruct.getStructOrBoundGenericStruct()); + auto offset = layout.getFieldOffset( + IGF, layout.getDecl()->getStoredProperties()[index]); llvm::Value *metadata = IGF.emitTypeMetadataRefForLayout(TheStruct); - Address fieldVector = emitAddressOfFieldOffsetVector(IGF, metadata, - TheStruct.getStructOrBoundGenericStruct()); - - // Grab the indexed offset. - fieldVector = IGF.Builder.CreateConstArrayGEP(fieldVector, index, - IGF.IGM.getPointerSize()); - return IGF.Builder.CreateLoad(fieldVector); + auto field = IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.Int32Ty, + IGF.IGM.getPointerAlignment()); + return IGF.Builder.CreateLoad(field); } MemberAccessStrategy getFieldAccessStrategy(IRGenModule &IGM, @@ -590,7 +591,9 @@ namespace { SILType getType(VarDecl *field) { assert(field->getDeclContext() == TheStruct->getAnyNominal()); auto silType = SILType::getPrimitiveAddressType(TheStruct); - return silType.getFieldType(field, IGM.getSILModule()); + return silType.getFieldType( + field, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); } StructLayout performLayout(ArrayRef fieldTypes) { @@ -731,8 +734,9 @@ class ClangRecordLowering { // If we have a Swift import of this type, use our lowered information. if (swiftField) { - auto &fieldTI = cast( - IGM.getTypeInfo(SwiftType.getFieldType(swiftField, IGM.getSILModule()))); + auto &fieldTI = cast(IGM.getTypeInfo( + SwiftType.getFieldType(swiftField, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()))); addField(swiftField, offset, fieldTI); return; } @@ -789,7 +793,8 @@ class ClangRecordLowering { ElementLayout layout = ElementLayout::getIncomplete(fieldType); auto isEmpty = fieldType.isKnownEmpty(ResilienceExpansion::Maximal); if (isEmpty) - layout.completeEmpty(fieldType.isPOD(ResilienceExpansion::Maximal)); + layout.completeEmptyTailAllocatedCType( + fieldType.isPOD(ResilienceExpansion::Maximal), NextOffset); else layout.completeFixed(fieldType.isPOD(ResilienceExpansion::Maximal), NextOffset, LLVMFields.size()); diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index e9f08725defcd..db4d67356ac81 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -46,7 +46,8 @@ IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, return entry; } - auto fnType = getSILModule().Types.getConstantFunctionType(declRef); + auto fnType = getSILModule().Types.getConstantFunctionType( + getMaximalTypeExpansionContext(), declRef); Signature signature = getSignature(fnType); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); @@ -54,8 +55,8 @@ IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef, return entry; } -static FunctionPointer lookupMethod(IRGenFunction &IGF, - SILDeclRef declRef) { +static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) { + auto expansionContext = IGF.IGM.getMaximalTypeExpansionContext(); auto *decl = cast(declRef.getDecl()); // Protocol case. @@ -68,7 +69,8 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, } // Class case. - auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType(declRef); + auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType( + expansionContext, declRef); // Load the metadata, or use the 'self' value if we have a static method. llvm::Value *self; @@ -82,7 +84,8 @@ static FunctionPointer lookupMethod(IRGenFunction &IGF, else self = (IGF.CurFn->arg_end() - 1); - auto selfTy = funcTy->getSelfParameter().getSILStorageType(); + auto selfTy = funcTy->getSelfParameter() + .getSILStorageType(IGF.IGM.getSILModule(), funcTy); llvm::Value *metadata; if (selfTy.is()) { diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp index 520e9a0c1ec8a..fb1f42b6b247e 100644 --- a/lib/IRGen/GenTuple.cpp +++ b/lib/IRGen/GenTuple.cpp @@ -149,6 +149,7 @@ namespace { const TupleFieldInfo &field = asImpl().getFields()[fieldNo]; switch (field.getKind()) { case ElementLayout::Kind::Empty: + case ElementLayout::Kind::EmptyTailAllocatedCType: case ElementLayout::Kind::Fixed: return field.getFixedByteOffset(); case ElementLayout::Kind::InitialNonFixedSize: @@ -194,6 +195,7 @@ namespace { } case ElementLayout::Kind::Empty: + case ElementLayout::Kind::EmptyTailAllocatedCType: case ElementLayout::Kind::InitialNonFixedSize: case ElementLayout::Kind::NonFixed: continue; diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 92a492daa5905..54798e8093483 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -198,15 +198,11 @@ void LoadableTypeInfo::addScalarToAggLowering(IRGenModule &IGM, offset.asCharUnits() + storageSize.asCharUnits()); } -static llvm::Constant *asSizeConstant(IRGenModule &IGM, Size size) { - return llvm::ConstantInt::get(IGM.SizeTy, size.getValue()); -} - llvm::Value *FixedTypeInfo::getSize(IRGenFunction &IGF, SILType T) const { return FixedTypeInfo::getStaticSize(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticSize(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedSize()); + return IGM.getSize(getFixedSize()); } llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, @@ -214,7 +210,7 @@ llvm::Value *FixedTypeInfo::getAlignmentMask(IRGenFunction &IGF, return FixedTypeInfo::getStaticAlignmentMask(IGF.IGM); } llvm::Constant *FixedTypeInfo::getStaticAlignmentMask(IRGenModule &IGM) const { - return asSizeConstant(IGM, Size(getFixedAlignment().getValue() - 1)); + return IGM.getSize(Size(getFixedAlignment().getValue() - 1)); } llvm::Value *FixedTypeInfo::getStride(IRGenFunction &IGF, SILType T) const { @@ -229,7 +225,7 @@ llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) c isBitwiseTakable(ResilienceExpansion::Maximal) == IsBitwiseTakable); } llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const { - return asSizeConstant(IGM, getFixedStride()); + return IGM.getSize(getFixedStride()); } llvm::Value *FixedTypeInfo::isDynamicallyPackedInline(IRGenFunction &IGF, @@ -399,7 +395,7 @@ static llvm::Value *computeExtraTagBytes(IRGenFunction &IGF, IRBuilder &Builder, } auto *entryBB = Builder.GetInsertBlock(); - llvm::Value *size = asSizeConstant(IGM, fixedSize); + llvm::Value *size = IGM.getSize(fixedSize); auto *returnBB = llvm::BasicBlock::Create(Ctx); size = Builder.CreateZExtOrTrunc(size, int32Ty); // We know size < 4. @@ -1105,8 +1101,9 @@ TypeConverter::createImmovable(llvm::Type *type, Size size, Alignment align) { static TypeInfo *invalidTypeInfo() { return (TypeInfo*) 1; } -bool TypeConverter::readLegacyTypeInfo(StringRef path) { - auto fileOrErr = llvm::MemoryBuffer::getFile(path); +bool TypeConverter::readLegacyTypeInfo(llvm::vfs::FileSystem &fs, + StringRef path) { + auto fileOrErr = fs.getBufferForFile(path); if (!fileOrErr) return true; @@ -1209,6 +1206,8 @@ TypeConverter::TypeConverter(IRGenModule &IGM) llvm::SmallString<128> defaultPath; StringRef path = IGM.IRGen.Opts.ReadLegacyTypeInfoPath; + auto fs = + IGM.getSwiftModule()->getASTContext().SourceMgr.getFileSystem(); if (path.empty()) { const auto &Triple = IGM.Context.LangOpts.Target; @@ -1225,7 +1224,7 @@ TypeConverter::TypeConverter(IRGenModule &IGM) bool found = false; for (auto &RuntimeLibraryPath : IGM.Context.SearchPathOpts.RuntimeLibraryPaths) { - if (llvm::sys::fs::exists(RuntimeLibraryPath)) { + if (fs->exists(RuntimeLibraryPath)) { defaultPath.append(RuntimeLibraryPath); found = true; break; @@ -1245,7 +1244,7 @@ TypeConverter::TypeConverter(IRGenModule &IGM) path = defaultPath; } - bool error = readLegacyTypeInfo(path); + bool error = readLegacyTypeInfo(*fs, path); if (error) llvm::report_fatal_error("Cannot read '" + path + "'"); } @@ -1259,13 +1258,11 @@ TypeConverter::~TypeConverter() { } } -void TypeConverter::pushGenericContext(CanGenericSignature signature) { +void TypeConverter::setGenericContext(CanGenericSignature signature) { if (!signature) return; - // Push the generic context down to the SIL TypeConverter, so we can share - // archetypes with SIL. - IGM.getSILTypes().pushGenericContext(signature); + CurGenericSignature = signature; // Clear the dependent type info cache since we have a new active signature // now. @@ -1274,21 +1271,12 @@ void TypeConverter::pushGenericContext(CanGenericSignature signature) { Types.getCacheFor(/*isDependent*/ true, Mode::CompletelyFragile).clear(); } -void TypeConverter::popGenericContext(CanGenericSignature signature) { - if (!signature) - return; - - // Pop the SIL TypeConverter's generic context too. - IGM.getSILTypes().popGenericContext(signature); - - Types.getCacheFor(/*isDependent*/ true, Mode::Normal).clear(); - Types.getCacheFor(/*isDependent*/ true, Mode::Legacy).clear(); - Types.getCacheFor(/*isDependent*/ true, Mode::CompletelyFragile).clear(); +CanGenericSignature IRGenModule::getCurGenericContext() { + return Types.getCurGenericContext(); } GenericEnvironment *TypeConverter::getGenericEnvironment() { - auto genericSig = IGM.getSILTypes().getCurGenericContext(); - return genericSig->getCanonicalSignature()->getGenericEnvironment(); + return CurGenericSignature->getGenericEnvironment(); } GenericEnvironment *IRGenModule::getGenericEnvironment() { @@ -1463,25 +1451,25 @@ const TypeInfo &IRGenFunction::getTypeInfo(SILType T) { /// Return the SIL-lowering of the given type. SILType IRGenModule::getLoweredType(AbstractionPattern orig, Type subst) const { - return getSILTypes().getLoweredType(orig, subst, - ResilienceExpansion::Maximal); + return getSILTypes().getLoweredType( + orig, subst, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Return the SIL-lowering of the given type. SILType IRGenModule::getLoweredType(Type subst) const { - return getSILTypes().getLoweredType(subst, - ResilienceExpansion::Maximal); + return getSILTypes().getLoweredType( + subst, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Return the SIL-lowering of the given type. const Lowering::TypeLowering &IRGenModule::getTypeLowering(SILType type) const { - return getSILTypes().getTypeLowering(type, - ResilienceExpansion::Maximal); + return getSILTypes().getTypeLowering( + type, TypeExpansionContext::maximalResilienceExpansionOnly()); } bool IRGenModule::isTypeABIAccessible(SILType type) const { - return getSILModule().isTypeABIAccessible(type, - ResilienceExpansion::Maximal); + return getSILModule().isTypeABIAccessible( + type, TypeExpansionContext::maximalResilienceExpansionOnly()); } /// Get a pointer to the storage type for the given type. Note that, @@ -1561,7 +1549,7 @@ ArchetypeType *TypeConverter::getExemplarArchetype(ArchetypeType *t) { // Dig out the canonical generic environment. auto genericSig = genericEnv->getGenericSignature(); - auto canGenericSig = genericSig->getCanonicalSignature(); + auto canGenericSig = genericSig.getCanonicalSignature(); auto canGenericEnv = canGenericSig->getGenericEnvironment(); if (canGenericEnv == genericEnv) return t; @@ -2268,7 +2256,7 @@ bool TypeConverter::isExemplarArchetype(ArchetypeType *arch) const { // Dig out the canonical generic environment. auto genericSig = genericEnv->getGenericSignature(); - auto canGenericSig = genericSig->getCanonicalSignature(); + auto canGenericSig = genericSig.getCanonicalSignature(); auto canGenericEnv = canGenericSig->getGenericEnvironment(); // If this archetype is in the canonical generic environment, it's an @@ -2304,7 +2292,10 @@ SILType irgen::getSingletonAggregateFieldType(IRGenModule &IGM, SILType t, auto allFields = structDecl->getStoredProperties(); if (allFields.size() == 1) { - auto fieldTy = t.getFieldType(allFields[0], IGM.getSILModule()); + auto fieldTy = t.getFieldType( + allFields[0], IGM.getSILModule(), + TypeExpansionContext(expansion, IGM.getSwiftModule(), + IGM.getSILModule().isWholeModule())); if (!IGM.isTypeABIAccessible(fieldTy)) return SILType(); return fieldTy; @@ -2324,7 +2315,10 @@ SILType irgen::getSingletonAggregateFieldType(IRGenModule &IGM, SILType t, auto theCase = allCases.begin(); if (!allCases.empty() && std::next(theCase) == allCases.end() && (*theCase)->hasAssociatedValues()) { - auto enumEltTy = t.getEnumElementType(*theCase, IGM.getSILModule()); + auto enumEltTy = t.getEnumElementType( + *theCase, IGM.getSILModule(), + TypeExpansionContext(expansion, IGM.getSwiftModule(), + IGM.getSILModule().isWholeModule())); if (!IGM.isTypeABIAccessible(enumEltTy)) return SILType(); return enumEltTy; diff --git a/lib/IRGen/GenType.h b/lib/IRGen/GenType.h index d02805009ed46..e2dd0a7b629fe 100644 --- a/lib/IRGen/GenType.h +++ b/lib/IRGen/GenType.h @@ -53,6 +53,7 @@ namespace swift { namespace irgen { class Alignment; + class GenericContextScope; class ProtocolInfo; class Size; class FixedTypeInfo; @@ -86,6 +87,13 @@ class TypeConverter { IRGenModule &IGM; private: + // Set using the GenericContextScope RAII object. + friend GenericContextScope; + CanGenericSignature CurGenericSignature; + // Enter a generic context for lowering the parameters of a generic function + // type. + void setGenericContext(CanGenericSignature signature); + Mode LoweringMode = Mode::Normal; llvm::DenseMap> Protocols; @@ -187,13 +195,10 @@ class TypeConverter { llvm::Type *getExistentialType(unsigned numWitnessTables); - /// Enter a generic context for lowering the parameters of a generic function - /// type. - void pushGenericContext(CanGenericSignature signature); + /// Retrieve the generic signature for the current generic context, or null if no + /// generic environment is active. + CanGenericSignature getCurGenericContext() { return CurGenericSignature; } - /// Exit a generic context. - void popGenericContext(CanGenericSignature signature); - /// Retrieve the generic environment for the current generic context. /// /// Fails if there is no generic context. @@ -208,7 +213,7 @@ class TypeConverter { /// Read a YAML legacy type layout dump. Returns false on success, true on /// error. - bool readLegacyTypeInfo(StringRef path); + bool readLegacyTypeInfo(llvm::vfs::FileSystem &fs, StringRef path); Optional getLegacyTypeInfo(NominalTypeDecl *decl) const; @@ -235,12 +240,12 @@ class TypeConverter { /// a scope. class GenericContextScope { TypeConverter &TC; - CanGenericSignature sig; + CanGenericSignature newSig, oldSig; public: GenericContextScope(TypeConverter &TC, CanGenericSignature sig) - : TC(TC), sig(sig) + : TC(TC), newSig(sig), oldSig(TC.CurGenericSignature) { - TC.pushGenericContext(sig); + TC.setGenericContext(newSig); } GenericContextScope(IRGenModule &IGM, CanGenericSignature sig) @@ -248,7 +253,10 @@ class GenericContextScope { {} ~GenericContextScope() { - TC.popGenericContext(sig); + if (!newSig) + return; + assert(TC.CurGenericSignature == newSig); + TC.setGenericContext(oldSig); } }; diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 05024f6abd84e..d3b77b9625e73 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1023,15 +1023,21 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, return {}; } +llvm::Constant * +IRGenModule::getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init) { + if (auto known = + getAddrOfKnownValueWitnessTable(*this, concreteType, false)) { + return known.getValue(); + } + return getAddrOfValueWitnessTable(concreteType); +} + /// Emit a value-witness table for the given type. ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM, CanType abstractType, bool isPattern, bool relativeReference) { - // We shouldn't emit global value witness tables for generic type instances. - assert(!isa(abstractType) && - "emitting VWT for generic instance"); - // See if we can use a prefab witness table from the runtime. if (!isPattern) { if (auto known = getAddrOfKnownValueWitnessTable(IGM, abstractType, diff --git a/lib/IRGen/GenericArguments.h b/lib/IRGen/GenericArguments.h new file mode 100644 index 0000000000000..5365888e7cedd --- /dev/null +++ b/lib/IRGen/GenericArguments.h @@ -0,0 +1,98 @@ +//===--- MetadataRequest.cpp - IR generation for metadata requests --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements IR generation for accessing metadata. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IRGEN_GENERICARGUMENTS_H +#define SWIFT_IRGEN_GENERICARGUMENTS_H + +#include "Explosion.h" +#include "FixedTypeInfo.h" +#include "GenArchetype.h" +#include "GenClass.h" +#include "GenMeta.h" +#include "GenProto.h" +#include "GenType.h" +#include "GenericRequirement.h" +#include "IRGenDebugInfo.h" +#include "IRGenFunction.h" +#include "IRGenMangler.h" +#include "IRGenModule.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IRGenOptions.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/ClangImporter/ClangModule.h" +#include "swift/IRGen/Linking.h" +#include "llvm/ADT/STLExtras.h" + +namespace swift { +namespace irgen { + +/// A structure for collecting generic arguments for emitting a +/// nominal metadata reference. The structure produced here is +/// consumed by swift_getGenericMetadata() and must correspond to +/// the fill operations that the compiler emits for the bound decl. +struct GenericArguments { + /// The values to use to initialize the arguments structure. + SmallVector Values; + SmallVector Types; + + static unsigned getNumGenericArguments(IRGenModule &IGM, + NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + return requirements.getNumTypeRequirements(); + } + + void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { + GenericTypeRequirements requirements(IGM, nominal); + collectTypes(IGM, requirements); + } + + void collectTypes(IRGenModule &IGM, + const GenericTypeRequirements &requirements) { + for (auto &requirement : requirements.getRequirements()) { + if (requirement.Protocol) { + Types.push_back(IGM.WitnessTablePtrTy); + } else { + Types.push_back(IGM.TypeMetadataPtrTy); + } + } + } + + void collect(IRGenFunction &IGF, CanType type) { + auto *decl = type.getNominalOrBoundGenericNominal(); + GenericTypeRequirements requirements(IGF.IGM, decl); + + auto subs = type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); + requirements.enumerateFulfillments( + IGF.IGM, subs, + [&](unsigned reqtIndex, CanType type, ProtocolConformanceRef conf) { + if (conf) { + Values.push_back(emitWitnessTableRef(IGF, type, conf)); + } else { + Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); + } + }); + + collectTypes(IGF.IGM, decl); + assert(Types.size() == Values.size()); + } +}; + +} // namespace irgen +} // namespace swift + +#endif diff --git a/lib/IRGen/GenericRequirement.h b/lib/IRGen/GenericRequirement.h index 68057942bd449..5076da261c9a4 100644 --- a/lib/IRGen/GenericRequirement.h +++ b/lib/IRGen/GenericRequirement.h @@ -137,10 +137,8 @@ class GenericTypeRequirements { bool empty() const { return Requirements.empty(); } - using FulfillmentCallback = - llvm::function_ref conf)>; + using FulfillmentCallback = llvm::function_ref; void enumerateFulfillments(IRGenModule &IGM, SubstitutionMap subs, FulfillmentCallback callback); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index a7f3fc0643fbe..a39ff7e63e4cd 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -37,6 +37,7 @@ #include "swift/SILOptimizer/PassManager/PassPipeline.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Subsystems.h" +#include "../Serialization/ModuleFormat.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringSet.h" #include "llvm/Analysis/AliasAnalysis.h" @@ -126,8 +127,14 @@ static void addSwiftMergeFunctionsPass(const PassManagerBuilder &Builder, static void addAddressSanitizerPasses(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { - PM.add(createAddressSanitizerFunctionPass()); - PM.add(createModuleAddressSanitizerLegacyPassPass()); + auto &BuilderWrapper = + static_cast(Builder); + auto recover = + bool(BuilderWrapper.IRGOpts.SanitizersWithRecoveryInstrumentation & + SanitizerKind::Address); + PM.add(createAddressSanitizerFunctionPass(/*CompileKernel=*/false, recover)); + PM.add(createModuleAddressSanitizerLegacyPassPass(/*CompileKernel=*/false, + recover)); } static void addThreadSanitizerPass(const PassManagerBuilder &Builder, @@ -145,7 +152,7 @@ static void addSanitizerCoveragePass(const PassManagerBuilder &Builder, std::tuple, std::string> -swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { +swift::getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx) { // Things that maybe we should collect from the command line: // - relocation model // - code model @@ -155,6 +162,7 @@ swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) { // Explicitly request debugger tuning for LLDB which is the default // on Darwin platforms but not on others. TargetOpts.DebuggerTuning = llvm::DebuggerKind::LLDB; + TargetOpts.FunctionSections = Opts.FunctionSections; auto *Clang = static_cast(Ctx.getClangModuleLoader()); clang::TargetOptions &ClangOpts = Clang->getTargetInfo().getTargetOpts(); @@ -171,7 +179,8 @@ void setModuleFlags(IRGenModule &IGM) { IRGenModule::swiftVersion); } -void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module, +void swift::performLLVMOptimizations(const IRGenOptions &Opts, + llvm::Module *Module, llvm::TargetMachine *TargetMachine) { // Set up a pipeline. PassManagerBuilderWrapper PMBuilder(Opts); @@ -337,7 +346,7 @@ class MD5Stream : public llvm::raw_ostream { /// Computes the MD5 hash of the llvm \p Module including the compiler version /// and options which influence the compilation. -static void getHashOfModule(MD5::MD5Result &Result, IRGenOptions &Opts, +static void getHashOfModule(MD5::MD5Result &Result, const IRGenOptions &Opts, llvm::Module *Module, llvm::TargetMachine *TargetMachine, version::Version const& effectiveLanguageVersion) { @@ -449,7 +458,7 @@ diagnoseSync(DiagnosticEngine *Diags, llvm::sys::Mutex *DiagMutex, /// Run the LLVM passes. In multi-threaded compilation this will be done for /// multiple LLVM modules in parallel. -bool swift::performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, +bool swift::performLLVM(const IRGenOptions &Opts, DiagnosticEngine *Diags, llvm::sys::Mutex *DiagMutex, llvm::GlobalVariable *HashGlobal, llvm::Module *Module, @@ -458,6 +467,11 @@ bool swift::performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, StringRef OutputFilename, UnifiedStatsReporter *Stats) { #ifndef NDEBUG +// FIXME: Some bots are failing. See: rdar://54708850 +//#define DEBUG_VERIFY_GENERATED_CODE +#endif + +#ifdef DEBUG_VERIFY_GENERATED_CODE // To check that we only skip generating code when it would have no effect, in // assertion builds we still generate the code, but write it into a temporary // file that we compare to the original file. @@ -490,7 +504,7 @@ bool swift::performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, !Opts.PrintInlineTree && !needsRecompile(OutputFilename, HashData, HashGlobal, DiagMutex)) { // The llvm IR did not change. We don't need to re-create the object file. -#ifdef NDEBUG +#ifndef DEBUG_VERIFY_GENERATED_CODE return false; #else // ...but we're in an asserts build, so we want to check that assumption. @@ -594,8 +608,7 @@ bool swift::performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, if (DiagMutex) DiagMutex->unlock(); } -#if 0 -#ifndef NDEBUG +#ifdef DEBUG_VERIFY_GENERATED_CODE if (!OriginalOutputFilename.empty()) { // We're done changing the file; make sure it's saved before we compare. RawOS->close(); @@ -630,14 +643,13 @@ bool swift::performLLVM(IRGenOptions &Opts, DiagnosticEngine *Diags, llvm_unreachable("one of these should be a temporary file"); } } -#endif #endif return false; } std::unique_ptr -swift::createTargetMachine(IRGenOptions &Opts, ASTContext &Ctx) { +swift::createTargetMachine(const IRGenOptions &Opts, ASTContext &Ctx) { CodeGenOpt::Level OptLevel = Opts.shouldOptimize() ? CodeGenOpt::Default // -Os : CodeGenOpt::None; @@ -670,10 +682,17 @@ swift::createTargetMachine(IRGenOptions &Opts, ASTContext &Ctx) { } + // On Cygwin 64 bit, dlls are loaded above the max address for 32 bits. + // This means that the default CodeModel causes generated code to segfault + // when run. + Optional cmodel = None; + if (EffectiveTriple.isArch64Bit() && EffectiveTriple.isWindowsCygwinEnvironment()) + cmodel = CodeModel::Large; + // Create a target machine. llvm::TargetMachine *TargetMachine = Target->createTargetMachine( EffectiveTriple.str(), CPU, targetFeatures, TargetOpts, Reloc::PIC_, - None, OptLevel); + cmodel, OptLevel); if (!TargetMachine) { Ctx.Diags.diagnose(SourceLoc(), diag::no_llvm_target, EffectiveTriple.str(), "no LLVM target machine"); @@ -682,7 +701,7 @@ swift::createTargetMachine(IRGenOptions &Opts, ASTContext &Ctx) { return std::unique_ptr(TargetMachine); } -IRGenerator::IRGenerator(IRGenOptions &options, SILModule &module) +IRGenerator::IRGenerator(const IRGenOptions &options, SILModule &module) : Opts(options), SIL(module), QueueIndex(0) { } @@ -793,6 +812,7 @@ static void initLLVMModule(const IRGenModule &IGM, ModuleDecl &M) { std::pair swift::irgen::createIRGenModule(SILModule *SILMod, StringRef OutputFilename, StringRef MainInputFilenameForDebugInfo, + StringRef PrivateDiscriminator, llvm::LLVMContext &LLVMContext) { IRGenOptions Opts; @@ -804,7 +824,8 @@ swift::irgen::createIRGenModule(SILModule *SILMod, StringRef OutputFilename, // Create the IR emitter. IRGenModule *IGM = new IRGenModule(*irgen, std::move(targetMachine), nullptr, LLVMContext, - "", OutputFilename, MainInputFilenameForDebugInfo); + "", OutputFilename, MainInputFilenameForDebugInfo, + PrivateDiscriminator); initLLVMModule(*IGM, *SILMod->getSwiftModule()); @@ -840,11 +861,13 @@ static void runIRGenPreparePasses(SILModule &Module, /// Generates LLVM IR, runs the LLVM passes and produces the output file. /// All this is done in a single thread. static std::unique_ptr -performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, +performIRGeneration(const IRGenOptions &Opts, ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, + StringRef PrivateDiscriminator, llvm::LLVMContext &LLVMContext, SourceFile *SF = nullptr, - llvm::GlobalVariable **outModuleHash = nullptr) { + llvm::GlobalVariable **outModuleHash = nullptr, + llvm::StringSet<> *linkerDirectives = nullptr) { auto &Ctx = M->getASTContext(); assert(!Ctx.hadError()); @@ -856,7 +879,8 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, // Create the IR emitter. IRGenModule IGM(irgen, std::move(targetMachine), nullptr, LLVMContext, ModuleName, PSPs.OutputFilename, - PSPs.MainInputFilenameForDebugInfo); + PSPs.MainInputFilenameForDebugInfo, + PrivateDiscriminator); initLLVMModule(IGM, *SILMod->getSwiftModule()); @@ -864,9 +888,10 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, runIRGenPreparePasses(*SILMod, IGM); { - SharedTimer timer("IRGen"); + FrontendStatsTracer tracer(Ctx.Stats, "IRGen"); + // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); if (SF) { IGM.emitSourceFile(*SF); @@ -935,7 +960,7 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, if (outModuleHash) { *outModuleHash = IGM.ModuleHash; } else { - SharedTimer timer("LLVM pipeline"); + FrontendStatsTracer tracer(Ctx.Stats, "LLVM pipeline"); // Since no out module hash was set, we need to performLLVM. if (performLLVM(Opts, &IGM.Context.Diags, nullptr, IGM.ModuleHash, @@ -1052,9 +1077,10 @@ struct LLVMCodeGenThreads { /// Generates LLVM IR, runs the LLVM passes and produces the output files. /// All this is done in multiple threads. static void performParallelIRGeneration( - IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, + const IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, int numThreads, - ArrayRef outputFilenames) { + ArrayRef outputFilenames, + llvm::StringSet<> *linkerDirectives) { IRGenerator irgen(Opts, *SILMod); @@ -1101,7 +1127,8 @@ static void performParallelIRGeneration( // Create the IR emitter. IRGenModule *IGM = new IRGenModule(irgen, std::move(targetMachine), nextSF, *Context, - ModuleName, *OutputIter++, nextSF->getFilename()); + ModuleName, *OutputIter++, nextSF->getFilename(), + nextSF->getPrivateDiscriminator().str()); IGMcreated = true; initLLVMModule(*IGM, *SILMod->getSwiftModule()); @@ -1120,7 +1147,7 @@ static void performParallelIRGeneration( } // Emit the module contents. - irgen.emitGlobalTopLevel(); + irgen.emitGlobalTopLevel(linkerDirectives); for (auto *File : M->getFiles()) { if (auto *SF = dyn_cast(File)) { @@ -1223,7 +1250,7 @@ static void performParallelIRGeneration( // Bail out if there are any errors. if (Ctx.hadError()) return; - SharedTimer timer("LLVM pipeline"); + FrontendStatsTracer tracer(Ctx.Stats, "LLVM pipeline"); llvm::sys::Mutex DiagMutex; @@ -1244,33 +1271,39 @@ static void performParallelIRGeneration( } std::unique_ptr swift::performIRGeneration( - IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, + const IRGenOptions &Opts, swift::ModuleDecl *M, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, llvm::LLVMContext &LLVMContext, ArrayRef parallelOutputFilenames, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { if (SILMod->getOptions().shouldPerformIRGenerationInParallel() && !parallelOutputFilenames.empty()) { auto NumThreads = SILMod->getOptions().NumThreads; ::performParallelIRGeneration(Opts, M, std::move(SILMod), ModuleName, - NumThreads, parallelOutputFilenames); + NumThreads, parallelOutputFilenames, + LinkerDirectives); // TODO: Parallel LLVM compilation cannot be used if a (single) module is // needed as return value. return nullptr; } return ::performIRGeneration(Opts, M, std::move(SILMod), ModuleName, PSPs, - LLVMContext, nullptr, outModuleHash); + "", LLVMContext, nullptr, + outModuleHash, LinkerDirectives); } std::unique_ptr swift:: -performIRGeneration(IRGenOptions &Opts, SourceFile &SF, +performIRGeneration(const IRGenOptions &Opts, SourceFile &SF, std::unique_ptr SILMod, StringRef ModuleName, const PrimarySpecificPaths &PSPs, + StringRef PrivateDiscriminator, llvm::LLVMContext &LLVMContext, - llvm::GlobalVariable **outModuleHash) { + llvm::GlobalVariable **outModuleHash, + llvm::StringSet<> *LinkerDirectives) { return ::performIRGeneration(Opts, SF.getParentModule(), std::move(SILMod), - ModuleName, PSPs, LLVMContext, &SF, - outModuleHash); + ModuleName, PSPs, PrivateDiscriminator, + LLVMContext, &SF, + outModuleHash, LinkerDirectives); } void @@ -1289,7 +1322,7 @@ swift::createSwiftModuleObjectFile(SILModule &SILMod, StringRef Buffer, if (!targetMachine) return; IRGenModule IGM(irgen, std::move(targetMachine), nullptr, VMContext, - OutputPath, OutputPath, ""); + OutputPath, OutputPath, "", ""); initLLVMModule(IGM, *SILMod.getSwiftModule()); auto *Ty = llvm::ArrayType::get(IGM.Int8Ty, Buffer.size()); auto *Data = @@ -1317,14 +1350,14 @@ swift::createSwiftModuleObjectFile(SILModule &SILMod, StringRef Buffer, break; } ASTSym->setSection(Section); - ASTSym->setAlignment(8); + ASTSym->setAlignment(serialization::SWIFTMODULE_ALIGNMENT); ::performLLVM(Opts, &Ctx.Diags, nullptr, nullptr, IGM.getModule(), IGM.TargetMachine.get(), Ctx.LangOpts.EffectiveLanguageVersion, OutputPath); } -bool swift::performLLVM(IRGenOptions &Opts, ASTContext &Ctx, +bool swift::performLLVM(const IRGenOptions &Opts, ASTContext &Ctx, llvm::Module *Module, StringRef OutputFilename, UnifiedStatsReporter *Stats) { // Build TargetMachine. diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 8419da7fd7607..8cbdd8095e06d 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -143,7 +143,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { public: IRGenDebugInfoImpl(const IRGenOptions &Opts, ClangImporter &CI, IRGenModule &IGM, llvm::Module &M, - StringRef MainOutputFilenameForDebugInfo); + StringRef MainOutputFilenameForDebugInfo, + StringRef PrivateDiscriminator); void finalize(); void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, @@ -444,7 +445,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { Dir = CurDir; } llvm::DIFile *F = DBuilder.createFile(File, Dir, CSInfo, Source); - DIFileCache[FileName.data()].reset(F); + DIFileCache[FileName].reset(F); return F; } @@ -515,7 +516,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto FnTy = SILTy.getAs(); if (!FnTy) { - LLVM_DEBUG(llvm::dbgs() << "Unexpected function type: "; SILTy.dump(); + LLVM_DEBUG(llvm::dbgs() << "Unexpected function type: "; + SILTy.print(llvm::dbgs()); llvm::dbgs() << "\n"); return CanSILFunctionType(); } @@ -584,15 +586,16 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // This is different from SILFunctionType::getAllResultsType() in some subtle // ways. - static SILType getResultTypeForDebugInfo(CanSILFunctionType fnTy) { + static SILType getResultTypeForDebugInfo(IRGenModule &IGM, + CanSILFunctionType fnTy) { if (fnTy->getNumResults() == 1) { - return fnTy->getResults()[0].getSILStorageType(); + return fnTy->getResults()[0].getSILStorageType(IGM.getSILModule(), fnTy); } else if (!fnTy->getNumIndirectFormalResults()) { - return fnTy->getDirectFormalResultsType(); + return fnTy->getDirectFormalResultsType(IGM.getSILModule()); } else { SmallVector eltTys; for (auto &result : fnTy->getResults()) { - eltTys.push_back(result.getType()); + eltTys.push_back(result.getReturnValueType(IGM.getSILModule(), fnTy)); } return SILType::getPrimitiveAddressType( CanType(TupleType::get(eltTys, fnTy->getASTContext()))); @@ -608,16 +611,16 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { llvm::DITypeRefArray createParameterTypes(CanSILFunctionType FnTy) { SmallVector Parameters; - GenericContextScope scope(IGM, FnTy->getGenericSignature()); + GenericContextScope scope(IGM, FnTy->getInvocationGenericSignature()); // The function return type is the first element in the list. - createParameterType(Parameters, getResultTypeForDebugInfo(FnTy)); + createParameterType(Parameters, getResultTypeForDebugInfo(IGM, FnTy)); // Actually, the input type is either a single type or a tuple // type. We currently represent a function with one n-tuple argument // as an n-ary function. for (auto Param : FnTy->getParameters()) - createParameterType(Parameters, IGM.silConv.getSILType(Param)); + createParameterType(Parameters, IGM.silConv.getSILType(Param, FnTy)); return DBuilder.getOrCreateTypeArray(Parameters); } @@ -802,14 +805,14 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { if (!Reconstructed) { llvm::errs() << "Failed to reconstruct type for " << Result << "\n"; llvm::errs() << "Original type:\n"; - Ty->dump(); + Ty->dump(llvm::errs()); abort(); } else if (!Reconstructed->isEqual(Ty)) { llvm::errs() << "Incorrect reconstructed type for " << Result << "\n"; llvm::errs() << "Original type:\n"; - Ty->dump(); + Ty->dump(llvm::errs()); llvm::errs() << "Reconstructed type:\n"; - Reconstructed->dump(); + Reconstructed->dump(llvm::errs()); abort(); } #endif @@ -839,7 +842,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { llvm::DINode::DIFlags Flags, unsigned &SizeInBits) { SmallVector Elements; unsigned OffsetInBits = 0; - auto genericSig = IGM.getSILTypes().getCurGenericContext(); + auto genericSig = IGM.getCurGenericContext(); for (auto ElemTy : TupleTy->getElementTypes()) { auto &elemTI = IGM.getTypeInfoForUnlowered( AbstractionPattern(genericSig, ElemTy->getCanonicalType()), ElemTy); @@ -999,6 +1002,29 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return BitWidth; } + /// Create a sized container for a sizeless type. Used to represent + /// BoundGenericEnums that may have different sizes depending on what they are + /// bound to, but still share a mangled name. + llvm::DIType *createOpaqueStructWithSizedContainer( + llvm::DIScope *Scope, StringRef Name, llvm::DIFile *File, unsigned Line, + unsigned SizeInBits, unsigned AlignInBits, llvm::DINode::DIFlags Flags, + StringRef MangledName) { + // Let the MDNode folding set do the work of uniquing the inner type. This + // should be cheap. + llvm::DICompositeType *UniqueType = DBuilder.createStructType( + Scope, Name, File, Line, 0, 0, Flags, nullptr, + DBuilder.getOrCreateArray(ArrayRef()), + llvm::dwarf::DW_LANG_Swift, nullptr, MangledName); + llvm::Metadata *Elements[] = { + DBuilder.createMemberType(Scope, "", File, 0, SizeInBits, + AlignInBits, 0, Flags, UniqueType)}; + + return DBuilder.createStructType( + Scope, "", File, Line, SizeInBits, AlignInBits, Flags, + /* DerivedFrom */ nullptr, DBuilder.getOrCreateArray(Elements), + llvm::dwarf::DW_LANG_Swift); + } + llvm::DIType *createPointerSizedStruct(llvm::DIScope *Scope, StringRef Name, llvm::DIFile *File, unsigned Line, llvm::DINode::DIFlags Flags, @@ -1171,7 +1197,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { if (!BaseTy) { LLVM_DEBUG(llvm::dbgs() << "Type without TypeBase: "; - DbgTy.getType()->dump(); + DbgTy.getType()->dump(llvm::dbgs()); llvm::dbgs() << "\n"); if (!InternalType) { StringRef Name = ""; @@ -1303,9 +1329,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto *StructTy = BaseTy->castTo(); auto *Decl = StructTy->getDecl(); auto L = getDebugLoc(*this, Decl); - return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName, - File, L.Line, SizeInBits, AlignInBits, Flags, - MangledName); + return createOpaqueStructWithSizedContainer( + Scope, Decl ? Decl->getNameStr() : "", File, L.Line, SizeInBits, + AlignInBits, Flags, MangledName); } case TypeKind::BoundGenericClass: { @@ -1412,12 +1438,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto *Decl = EnumTy->getDecl(); auto L = getDebugLoc(*this, Decl); auto *File = getOrCreateFile(L.Filename); - if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) - return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line, - Flags); - else - return createOpaqueStruct(Scope, Decl->getName().str(), File, L.Line, - SizeInBits, AlignInBits, Flags, MangledName); + return createOpaqueStructWithSizedContainer( + Scope, Decl->getName().str(), File, L.Line, SizeInBits, AlignInBits, + Flags, MangledName); } case TypeKind::BuiltinVector: { @@ -1498,8 +1521,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { case TypeKind::SILToken: case TypeKind::BuiltinUnsafeValueBuffer: - LLVM_DEBUG(llvm::errs() << "Unhandled type: "; DbgTy.getType()->dump(); - llvm::errs() << "\n"); + LLVM_DEBUG(llvm::dbgs() << "Unhandled type: "; + DbgTy.getType()->dump(llvm::dbgs()); + llvm::dbgs() << "\n"); MangledName = ""; } return DBuilder.createBasicType(MangledName, SizeInBits, Encoding); @@ -1641,7 +1665,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { IRGenDebugInfoImpl::IRGenDebugInfoImpl(const IRGenOptions &Opts, ClangImporter &CI, IRGenModule &IGM, llvm::Module &M, - StringRef MainOutputFilenameForDebugInfo) + StringRef MainOutputFilenameForDebugInfo, + StringRef PD) : Opts(Opts), CI(CI), SM(IGM.Context.SourceMgr), M(M), DBuilder(M), IGM(IGM), DebugPrefixMap(Opts.DebugPrefixMap) { assert(Opts.DebugInfoLevel > IRGenDebugInfoLevel::None && @@ -1655,7 +1680,6 @@ IRGenDebugInfoImpl::IRGenDebugInfoImpl(const IRGenOptions &Opts, unsigned Lang = llvm::dwarf::DW_LANG_Swift; std::string Producer = version::getSwiftFullVersion( IGM.Context.LangOpts.EffectiveLanguageVersion); - StringRef Flags = Opts.DebugFlags; unsigned Major, Minor; std::tie(Major, Minor) = version::getSwiftNumericVersion(); unsigned MajorRuntimeVersion = Major; @@ -1670,7 +1694,8 @@ IRGenDebugInfoImpl::IRGenDebugInfoImpl(const IRGenOptions &Opts, TheCU = DBuilder.createCompileUnit( Lang, MainFile, - Producer, Opts.shouldOptimize(), Flags, MajorRuntimeVersion, SplitName, + Producer, Opts.shouldOptimize(), Opts.getDebugFlags(PD), + MajorRuntimeVersion, SplitName, Opts.DebugInfoLevel > IRGenDebugInfoLevel::LineTables ? llvm::DICompileUnit::FullDebug : llvm::DICompileUnit::LineTablesOnly); @@ -1822,7 +1847,7 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder, auto DL = llvm::DebugLoc::get(L.Line, L.Column, Scope, InlinedAt); Builder.SetCurrentDebugLocation(DL); } - + void IRGenDebugInfoImpl::addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg) { auto TrapLoc = Builder.getCurrentDebugLocation(); @@ -1839,7 +1864,7 @@ void IRGenDebugInfoImpl::addFailureMessageToCurrentLoc(IRBuilder &Builder, FuncName += failureMsg; llvm::DISubprogram *TrapSP = DBuilder.createFunction( - MainModule, StringRef(), FuncName, TrapLoc->getFile(), 0, DIFnTy, 0, + MainModule, FuncName, StringRef(), TrapLoc->getFile(), 0, DIFnTy, 0, llvm::DINode::FlagArtificial, llvm::DISubprogram::SPFlagDefinition, nullptr, nullptr, nullptr); @@ -2082,8 +2107,8 @@ IRGenDebugInfoImpl::emitFunction(const SILDebugScope *DS, llvm::Function *Fn, if (FnTy) if (auto ErrorInfo = FnTy->getOptionalErrorResult()) { auto DTI = DebugTypeInfo::getFromTypeInfo( - ErrorInfo->getType(), - IGM.getTypeInfo(IGM.silConv.getSILType(*ErrorInfo))); + ErrorInfo->getReturnValueType(IGM.getSILModule(), FnTy), + IGM.getTypeInfo(IGM.silConv.getSILType(*ErrorInfo, FnTy))); Error = DBuilder.getOrCreateArray({getOrCreateType(DTI)}).get(); } @@ -2332,9 +2357,11 @@ SILLocation::DebugLoc IRGenDebugInfoImpl::decodeSourceLoc(SourceLoc SL) { std::unique_ptr IRGenDebugInfo::createIRGenDebugInfo( const IRGenOptions &Opts, ClangImporter &CI, IRGenModule &IGM, - llvm::Module &M, StringRef MainOutputFilenameForDebugInfo) { + llvm::Module &M, StringRef MainOutputFilenameForDebugInfo, + StringRef PrivateDiscriminator) { return llvm::make_unique(Opts, CI, IGM, M, - MainOutputFilenameForDebugInfo); + MainOutputFilenameForDebugInfo, + PrivateDiscriminator); } diff --git a/lib/IRGen/IRGenDebugInfo.h b/lib/IRGen/IRGenDebugInfo.h index 6b59daeaaf733..229a544b7ce1e 100644 --- a/lib/IRGen/IRGenDebugInfo.h +++ b/lib/IRGen/IRGenDebugInfo.h @@ -49,7 +49,8 @@ class IRGenDebugInfo { static std::unique_ptr createIRGenDebugInfo(const IRGenOptions &Opts, ClangImporter &CI, IRGenModule &IGM, llvm::Module &M, - StringRef MainOutputFilenameForDebugInfo); + StringRef MainOutputFilenameForDebugInfo, + StringRef PrivateDiscriminator); virtual ~IRGenDebugInfo(); /// Finalize the llvm::DIBuilder owned by this object. diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 3ef32d7c19674..0a1affaf92acb 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -92,8 +92,9 @@ class IRGenFunction { //--- Function prologue and epilogue ------------------------------------------- public: Explosion collectParameters(); - void emitScalarReturn(SILType resultTy, Explosion &scalars, - bool isSwiftCCReturn, bool isOutlined); + void emitScalarReturn(SILType returnResultType, SILType funcResultType, + Explosion &scalars, bool isSwiftCCReturn, + bool isOutlined); void emitScalarReturn(llvm::Type *resultTy, Explosion &scalars); void emitBBForReturn(); @@ -280,6 +281,7 @@ class IRGenFunction { const SILDebugScope *getDebugScope() const { return DbgScope; } llvm::Value *coerceValue(llvm::Value *value, llvm::Type *toTy, const llvm::DataLayout &); + Explosion coerceValueTo(SILType fromTy, Explosion &from, SILType toTy); /// Mark a load as invariant. void setInvariantLoad(llvm::LoadInst *load); diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index a368458702c8e..06f85871b7b36 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -91,7 +91,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, CanSymbolicReferenceLocally(CanSymbolicReference); AllowSymbolicReferences = true; - CanSymbolicReference = [&IGM](SymbolicReferent s) -> bool { + CanSymbolicReference = [](SymbolicReferent s) -> bool { if (auto type = s.dyn_cast()) { // The short-substitution types in the standard library have compact // manglings already, and the runtime ought to have a lookup table for @@ -119,19 +119,6 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, } } - // TODO: ObjectMemoryReader for PE platforms still does not - // implement symbol relocations. For now, on non-Mach-O platforms, - // only symbolic reference things in the same module. - if (IGM.TargetInfo.OutputObjectFormat != llvm::Triple::MachO - && IGM.TargetInfo.OutputObjectFormat != llvm::Triple::ELF) { - auto formalAccessScope = type->getFormalAccessScope(nullptr, true); - if ((formalAccessScope.isPublic() || formalAccessScope.isInternal()) && - (!IGM.CurSourceFile || - IGM.CurSourceFile != type->getParentSourceFile())) { - return false; - } - } - return true; } else if (auto opaque = s.dyn_cast()) { // Always symbolically reference opaque types. @@ -267,7 +254,7 @@ mangleSymbolNameForSymbolicMangling(const SymbolicMangling &mangling, = '_'; Buffer << ' '; if (auto ty = referent.dyn_cast()) - appendContext(ty); + appendContext(ty, ty->getAlternateModuleName()); else if (auto opaque = referent.dyn_cast()) appendOpaqueDeclName(opaque); else diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index 57125fc7950db..aa4d644d0095b 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -191,21 +191,21 @@ class IRGenMangler : public Mangle::ASTMangler { std::string mangleModuleDescriptor(const ModuleDecl *Decl) { beginMangling(); - appendContext(Decl); + appendContext(Decl, Decl->getAlternateModuleName()); appendOperator("MXM"); return finalize(); } std::string mangleExtensionDescriptor(const ExtensionDecl *Decl) { beginMangling(); - appendContext(Decl); + appendContext(Decl, Decl->getAlternateModuleName()); appendOperator("MXE"); return finalize(); } void appendAnonymousDescriptorName(PointerUnion Name){ if (auto DC = Name.dyn_cast()) { - return appendContext(DC); + return appendContext(DC, StringRef()); } if (auto VD = Name.dyn_cast()) { return appendEntity(VD); @@ -228,12 +228,6 @@ class IRGenMangler : public Mangle::ASTMangler { return finalize(); } - std::string mangleContext(const DeclContext *DC) { - beginMangling(); - appendContext(DC); - return finalize(); - } - std::string mangleBareProtocol(const ProtocolDecl *Decl) { beginMangling(); appendAnyGenericType(Decl); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 80af85aff9456..3ed07808b4168 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -50,6 +50,7 @@ #include "llvm/Support/MD5.h" #include "ConformanceDescription.h" +#include "GenDecl.h" #include "GenEnum.h" #include "GenIntegerLiteral.h" #include "GenType.h" @@ -86,8 +87,9 @@ static llvm::PointerType *createStructPointerType(IRGenModule &IGM, static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context, llvm::LLVMContext &LLVMContext, - IRGenOptions &Opts, - StringRef ModuleName) { + const IRGenOptions &Opts, + StringRef ModuleName, + StringRef PD) { auto Loader = Context.getClangModuleLoader(); auto *Importer = static_cast(&*Loader); assert(Importer && "No clang module loader!"); @@ -118,13 +120,13 @@ static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context, case IRGenDebugInfoFormat::DWARF: CGO.DebugCompilationDir = Opts.DebugCompilationDir; CGO.DwarfVersion = Opts.DWARFVersion; - CGO.DwarfDebugFlags = Opts.DebugFlags; + CGO.DwarfDebugFlags = Opts.getDebugFlags(PD); break; case IRGenDebugInfoFormat::CodeView: CGO.EmitCodeView = true; CGO.DebugCompilationDir = Opts.DebugCompilationDir; // This actually contains the debug flags for codeview. - CGO.DwarfDebugFlags = Opts.DebugFlags; + CGO.DwarfDebugFlags = Opts.getDebugFlags(PD); break; } @@ -143,10 +145,11 @@ IRGenModule::IRGenModule(IRGenerator &irgen, std::unique_ptr &&target, SourceFile *SF, llvm::LLVMContext &LLVMContext, StringRef ModuleName, StringRef OutputFilename, - StringRef MainInputFilenameForDebugInfo) + StringRef MainInputFilenameForDebugInfo, + StringRef PrivateDiscriminator) : IRGen(irgen), Context(irgen.SIL.getASTContext()), ClangCodeGen(createClangCodeGenerator(Context, LLVMContext, irgen.Opts, - ModuleName)), + ModuleName, PrivateDiscriminator)), Module(*ClangCodeGen->GetModule()), LLVMContext(Module.getContext()), DataLayout(irgen.getClangDataLayout()), Triple(irgen.getEffectiveClangTriple()), TargetMachine(std::move(target)), @@ -490,7 +493,8 @@ IRGenModule::IRGenModule(IRGenerator &irgen, if (opts.DebugInfoLevel > IRGenDebugInfoLevel::None) DebugInfo = IRGenDebugInfo::createIRGenDebugInfo(IRGen.Opts, *CI, *this, Module, - MainInputFilenameForDebugInfo); + MainInputFilenameForDebugInfo, + PrivateDiscriminator); initClangTypeConverter(); @@ -564,6 +568,16 @@ namespace RuntimeConstants { return RuntimeAvailability::AlwaysAvailable; } + RuntimeAvailability + GetTypesInAbstractMetadataStateAvailability(ASTContext &context) { + auto featureAvailability = + context.getTypesInAbstractMetadataStateAvailability(); + if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } + RuntimeAvailability DynamicReplacementAvailability(ASTContext &Context) { auto featureAvailability = Context.getSwift51Availability(); if (!isDeploymentAvailabilityContainedIn(Context, featureAvailability)) { @@ -1143,21 +1157,17 @@ void IRGenModule::emitAutolinkInfo() { }), AutolinkEntries.end()); - if ((TargetInfo.OutputObjectFormat == llvm::Triple::COFF && - !Triple.isOSCygMing()) || - TargetInfo.OutputObjectFormat == llvm::Triple::MachO || Triple.isPS4()) { + const bool AutolinkExtractRequired = + (TargetInfo.OutputObjectFormat == llvm::Triple::ELF && !Triple.isPS4()) || + TargetInfo.OutputObjectFormat == llvm::Triple::Wasm || + Triple.isOSCygMing(); + if (!AutolinkExtractRequired) { // On platforms that support autolinking, continue to use the metadata. Metadata->clearOperands(); for (auto *Entry : AutolinkEntries) Metadata->addOperand(Entry); - } else { - assert((TargetInfo.OutputObjectFormat == llvm::Triple::ELF || - TargetInfo.OutputObjectFormat == llvm::Triple::Wasm || - Triple.isOSCygMing()) && - "expected ELF output format or COFF format for Cygwin/MinGW"); - // Merge the entries into null-separated string. llvm::SmallString<64> EntriesString; for (auto &EntryNode : AutolinkEntries) { @@ -1184,6 +1194,7 @@ void IRGenModule::emitAutolinkInfo() { var->setSection(".swift1_autolink_entries"); var->setAlignment(getPointerAlignment().getValue()); + disableAddressSanitizer(*this, var); addUsedGlobal(var); } @@ -1259,6 +1270,7 @@ bool IRGenModule::finalize() { ModuleHash->setSection("__LLVM,__swift_modhash"); break; case llvm::Triple::ELF: + case llvm::Triple::Wasm: ModuleHash->setSection(".swift_modhash"); break; case llvm::Triple::COFF: @@ -1322,6 +1334,15 @@ void IRGenModule::error(SourceLoc loc, const Twine &message) { bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); } +bool IRGenModule::shouldPrespecializeGenericMetadata() { + auto &context = getSwiftModule()->getASTContext(); + auto deploymentAvailability = + AvailabilityContext::forDeploymentTarget(context); + return IRGen.Opts.PrespecializeGenericMetadata && + deploymentAvailability.isContainedIn( + context.getPrespecializedGenericMetadataAvailability()); +} + void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) { assert(GenModules.count(SF) == 0); GenModules[SF] = IGM; @@ -1376,3 +1397,8 @@ const llvm::DataLayout &IRGenerator::getClangDataLayout() { ->getTargetInfo() .getDataLayout(); } + +TypeExpansionContext IRGenModule::getMaximalTypeExpansionContext() const { + return TypeExpansionContext::maximal(getSwiftModule(), + getSILModule().isWholeModule()); +} diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 9f6d47e3303aa..87e948c7aa176 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -194,7 +194,7 @@ enum RequireMetadata_t : bool { /// IRGenModules - one for each LLVM module (= one for each input/output file). class IRGenerator { public: - IRGenOptions &Opts; + const IRGenOptions &Opts; SILModule &SIL; @@ -204,7 +204,13 @@ class IRGenerator { // Stores the IGM from which a function is referenced the first time. // It is used if a function has no source-file association. llvm::DenseMap DefaultIGMForFunction; - + + // The IGMs where sepecializations of functions are emitted. The key is the + // non-specialized function. + // Storing all specializations of a function in the same IGM increases the + // chances of function merging. + llvm::DenseMap IGMForSpecializations; + // The IGM of the first source file. IRGenModule *PrimaryIGM = nullptr; @@ -244,6 +250,21 @@ class IRGenerator { /// queued up. llvm::SmallPtrSet LazilyEmittedFieldMetadata; + /// Maps every generic type that is specialized within the module to its + /// specializations. + llvm::DenseMap> + SpecializationsForGenericTypes; + + /// The queue of specialized generic types whose prespecialized metadata to + /// emit. + llvm::SmallVector LazySpecializedTypeMetadataRecords; + + /// The queue of metadata accessors to emit. + /// + /// The accessors must be emitted after everything else which might result in + /// a statically-known-canonical prespecialization. + llvm::SmallSetVector LazyMetadataAccessors; + struct LazyOpaqueInfo { bool IsDescriptorUsed = false; bool IsDescriptorEmitted = false; @@ -283,7 +304,7 @@ class IRGenerator { friend class CurrentIGMPtr; public: - explicit IRGenerator(IRGenOptions &opts, SILModule &module); + explicit IRGenerator(const IRGenOptions &opts, SILModule &module); /// Attempt to create an llvm::TargetMachine for the current target. std::unique_ptr createTargetMachine(); @@ -331,7 +352,7 @@ class IRGenerator { /// Emit functions, variables and tables which are needed anyway, e.g. because /// they are externally visible. - void emitGlobalTopLevel(); + void emitGlobalTopLevel(llvm::StringSet<> *LinkerDirectives); /// Emit references to each of the protocol descriptors defined in this /// IR module. @@ -375,10 +396,22 @@ class IRGenerator { void ensureRelativeSymbolCollocation(SILDefaultWitnessTable &wt); + llvm::SmallVector specializationsForType(NominalTypeDecl *type) { + return SpecializationsForGenericTypes.lookup(type); + } + + void noteUseOfMetadataAccessor(NominalTypeDecl *decl) { + if (LazyMetadataAccessors.count(decl) == 0) { + LazyMetadataAccessors.insert(decl); + } + } + void noteUseOfTypeMetadata(NominalTypeDecl *type) { noteUseOfTypeGlobals(type, true, RequireMetadata); } + void noteUseOfSpecializedGenericTypeMetadata(CanType type); + void noteUseOfTypeMetadata(CanType type) { type.visit([&](Type t) { if (auto *nominal = t->getAnyNominal()) @@ -510,6 +543,7 @@ class IRGenModule { ModuleDecl *ClangImporterModule = nullptr; SourceFile *CurSourceFile = nullptr; + llvm::StringMap OriginalModules; llvm::SmallString<128> OutputFilename; llvm::SmallString<128> MainInputFilenameForDebugInfo; @@ -743,6 +777,8 @@ class IRGenModule { void error(SourceLoc loc, const Twine &message); bool useDllStorage(); + + bool shouldPrespecializeGenericMetadata(); Size getAtomicBoolSize() const { return AtomicBoolSize; } Alignment getAtomicBoolAlignment() const { return AtomicBoolAlign; } @@ -813,7 +849,8 @@ class IRGenModule { llvm::StructType *createNominalType(ProtocolCompositionType *T); clang::CanQual getClangType(CanType type); clang::CanQual getClangType(SILType type); - clang::CanQual getClangType(SILParameterInfo param); + clang::CanQual getClangType(SILParameterInfo param, + CanSILFunctionType funcTy); const clang::ASTContext &getClangASTContext() { assert(ClangASTContext && @@ -836,6 +873,8 @@ class IRGenModule { ResilienceExpansion getResilienceExpansionForLayout(NominalTypeDecl *decl); ResilienceExpansion getResilienceExpansionForLayout(SILGlobalVariable *var); + TypeExpansionContext getMaximalTypeExpansionContext() const; + bool isResilientConformance(const NormalProtocolConformance *conformance); bool isResilientConformance(const RootProtocolConformance *root); bool isDependentConformance(const RootProtocolConformance *conformance); @@ -855,7 +894,7 @@ class IRGenModule { private: TypeConverter &Types; - friend class TypeConverter; + friend TypeConverter; const clang::ASTContext *ClangASTContext; ClangTypeConverter *ClangTypes; @@ -1119,7 +1158,8 @@ class IRGenModule { CanGenericSignature genericSig); /// Produce an associated type witness that refers to the given type. - llvm::Constant *getAssociatedTypeWitness(Type type, bool inProtocolContext); + llvm::Constant *getAssociatedTypeWitness(Type type, GenericSignature sig, + bool inProtocolContext); void emitAssociatedTypeMetadataRecord(const RootProtocolConformance *C); void emitFieldDescriptor(const NominalTypeDecl *Decl); @@ -1191,14 +1231,15 @@ private: \ IRGenModule(IRGenerator &irgen, std::unique_ptr &&target, SourceFile *SF, llvm::LLVMContext &LLVMContext, StringRef ModuleName, StringRef OutputFilename, - StringRef MainInputFilenameForDebugInfo); + StringRef MainInputFilenameForDebugInfo, + StringRef PrivateDiscriminator); /// The constructor used when we just need an IRGenModule for type lowering. IRGenModule(IRGenerator &irgen, std::unique_ptr &&target, llvm::LLVMContext &LLVMContext) : IRGenModule(irgen, std::move(target), /*SF=*/nullptr, LLVMContext, "", "", - "") {} + "", "") {} ~IRGenModule(); @@ -1278,6 +1319,9 @@ private: \ ForDefinition_t forDefinition); llvm::Constant *getAddrOfValueWitnessTable(CanType concreteType, ConstantInit init = ConstantInit()); + llvm::Constant * + getAddrOfEffectiveValueWitnessTable(CanType concreteType, + ConstantInit init = ConstantInit()); Optional getAddrOfIVarInitDestroy(ClassDecl *cd, bool isDestroyer, bool isForeign, @@ -1337,6 +1381,7 @@ private: \ ConstantInit definition = ConstantInit()); llvm::Constant *getAddrOfObjCModuleContextDescriptor(); llvm::Constant *getAddrOfClangImporterModuleContextDescriptor(); + llvm::Constant *getAddrOfOriginalModuleContextDescriptor(StringRef Name); ConstantReference getAddrOfParentContextDescriptor(DeclContext *from, bool fromAnonymousContext); ConstantReference getAddrOfContextDescriptorForParent(DeclContext *parent, @@ -1420,6 +1465,10 @@ private: \ Address getAddrOfObjCISAMask(); + /// Retrieve the generic signature for the current generic context, or null if no + /// generic environment is active. + CanGenericSignature getCurGenericContext(); + /// Retrieve the generic environment for the current generic context. /// /// Fails if there is no generic context. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 46f0183dfd858..7292f9e0160ca 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -389,10 +389,6 @@ class IRGenSILFunction : /// Keeps track of the mapping of source variables to -O0 shadow copy allocas. llvm::SmallDenseMap ShadowStackSlots; llvm::SmallDenseMap, 8> AnonymousVariables; - /// To avoid inserting elements into ValueDomPoints twice. - llvm::SmallDenseSet ValueVariables; - /// Holds the DominancePoint of values that are storage for a source variable. - SmallVector, 8> ValueDomPoints; unsigned NumAnonVars = 0; /// Accumulative amount of allocated bytes on the stack. Used to limit the @@ -622,6 +618,16 @@ class IRGenSILFunction : return foundBB->second; } + TypeExpansionContext getExpansionContext() { + return TypeExpansionContext(*CurSILFn); + } + + SILType getLoweredTypeInContext(SILType ty) { + return CurSILFn->getModule() + .Types.getLoweredType(ty.getASTType(), getExpansionContext()) + .getCategoryType(ty.getCategory()); + } + StringRef getOrCreateAnonymousVarName(VarDecl *Decl) { llvm::SmallString<4> &Name = AnonymousVariables[Decl]; if (Name.empty()) { @@ -651,78 +657,6 @@ class IRGenSILFunction : return Name; } - /// Try to emit an inline assembly gadget which extends the lifetime of - /// \p Var. Returns whether or not this was successful. - bool emitLifetimeExtendingUse(llvm::Value *Var) { - llvm::Type *ArgTys; - auto *Ty = Var->getType(); - // Vectors, Pointers and Floats are expected to fit into a register. - if (Ty->isPointerTy() || Ty->isFloatingPointTy() || Ty->isVectorTy()) - ArgTys = {Ty}; - else { - // If this is not a scalar or vector type, we can't handle it. - if (isa(Ty)) - return false; - // The storage is guaranteed to be no larger than the register width. - // Extend the storage so it would fit into a register. - llvm::Type *IntTy; - switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) { - case 64: - IntTy = IGM.Int64Ty; - break; - case 32: - IntTy = IGM.Int32Ty; - break; - default: - llvm_unreachable("unsupported register width"); - } - ArgTys = {IntTy}; - Var = Builder.CreateZExtOrBitCast(Var, IntTy); - } - // Emit an empty inline assembler expression depending on the register. - auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false); - auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true); - Builder.CreateAsmCall(InlineAsm, Var); - return true; - } - - /// At -Onone, forcibly keep all LLVM values that are tracked by - /// debug variables alive by inserting an empty inline assembler - /// expression depending on the value in the blocks dominated by the - /// value. - void emitDebugVariableRangeExtension(const SILBasicBlock *CurBB) { - if (IGM.IRGen.Opts.shouldOptimize()) - return; - for (auto &Variable : ValueDomPoints) { - llvm::Instruction *Var = Variable.first; - DominancePoint VarDominancePoint = Variable.second; - if (getActiveDominancePoint() == VarDominancePoint || - isActiveDominancePointDominatedBy(VarDominancePoint)) { - bool ExtendedLifetime = emitLifetimeExtendingUse(Var); - if (!ExtendedLifetime) - continue; - - // Propagate dbg.values for Var into the current basic block. Note - // that this shouldn't be necessary. LiveDebugValues should be doing - // this but can't in general because it currently only tracks register - // locations. - llvm::BasicBlock *BB = Var->getParent(); - llvm::BasicBlock *CurBB = Builder.GetInsertBlock(); - if (BB == CurBB) - // The current basic block must be a successor of the dbg.value(). - continue; - - llvm::SmallVector DbgValues; - llvm::findDbgValues(DbgValues, Var); - for (auto *DVI : DbgValues) - if (DVI->getParent() == BB) - IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic( - DVI->getValue(), DVI->getVariable(), DVI->getExpression(), - DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt()); - } - } - } - /// To make it unambiguous whether a `var` binding has been initialized, /// zero-initialize the shadow copy alloca. LLDB uses the first pointer-sized /// field to recognize to detect uninitizialized variables. This can be @@ -747,6 +681,9 @@ class IRGenSILFunction : /// Account for bugs in LLVM. /// + /// - When a variable is spilled into a stack slot, LiveDebugValues fails to + /// recognize a restore of that slot for a different variable. + /// /// - The LLVM type legalizer currently doesn't update debug /// intrinsics when a large value is split up into smaller /// pieces. Note that this heuristic as a bit too conservative @@ -754,9 +691,7 @@ class IRGenSILFunction : /// /// - CodeGen Prepare may drop dbg.values pointing to PHI instruction. bool needsShadowCopy(llvm::Value *Storage) { - return (IGM.DataLayout.getTypeSizeInBits(Storage->getType()) > - IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) || - isa(Storage); + return !isa(Storage); } /// Unconditionally emit a stack shadow copy of an \c llvm::Value. @@ -787,29 +722,13 @@ class IRGenSILFunction : Alignment Align = Alignment(0)) { // Never emit shadow copies when optimizing, or if already on the stack. // No debug info is emitted for refcounts either. - if (IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || + if (IGM.IRGen.Opts.DisableDebuggerShadowCopies || + IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || isa(Storage) || isa(Storage) || - Storage->getType() == IGM.RefCountedPtrTy) + Storage->getType() == IGM.RefCountedPtrTy || !needsShadowCopy(Storage)) return Storage; - // Always emit shadow copies for function arguments. - if (VarInfo.ArgNo == 0) - // Otherwise only if debug value range extension is not feasible. - if (!needsShadowCopy(Storage)) { - // Mark for debug value range extension unless this is a constant, or - // unless it's not possible to emit lifetime-extending uses for this. - if (auto *Value = dyn_cast(Storage)) { - // Emit a use at the start of the storage lifetime to force early - // materialization. This makes variables available for inspection as - // soon as they are defined. - bool ExtendedLifetime = emitLifetimeExtendingUse(Value); - if (ExtendedLifetime) - if (ValueVariables.insert(Value).second) - ValueDomPoints.push_back({Value, getActiveDominancePoint()}); - } - - return Storage; - } + // Emit a shadow copy. return emitShadowCopy(Storage, Scope, VarInfo, Align); } @@ -830,7 +749,8 @@ class IRGenSILFunction : Explosion e = getLoweredExplosion(SILVal); // Only do this at -O0. - if (IGM.IRGen.Opts.shouldOptimize() || IsAnonymous) { + if (IGM.IRGen.Opts.DisableDebuggerShadowCopies || + IGM.IRGen.Opts.shouldOptimize() || IsAnonymous) { auto vals = e.claimAll(); copy.append(vals.begin(), vals.end()); return; @@ -898,7 +818,9 @@ class IRGenSILFunction : void visitSILBasicBlock(SILBasicBlock *BB); - void emitErrorResultVar(SILResultInfo ErrorInfo, DebugValueInst *DbgValue); + void emitErrorResultVar(CanSILFunctionType FnTy, + SILResultInfo ErrorInfo, + DebugValueInst *DbgValue); void emitDebugInfoForAllocStack(AllocStackInst *i, const TypeInfo &type, llvm::Value *addr); void visitAllocStackInst(AllocStackInst *i); @@ -1123,7 +1045,7 @@ class IRGenSILFunction : #define LOADABLE_REF_STORAGE_HELPER(Name) \ void visitRefTo##Name##Inst(RefTo##Name##Inst *i); \ void visit##Name##ToRefInst(Name##ToRefInst *i); \ - void visitCopy##Name##ValueInst(Copy##Name##ValueInst *i); + void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *i); #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ void visitLoad##Name##Inst(Load##Name##Inst *i); \ void visitStore##Name##Inst(Store##Name##Inst *i); @@ -1339,41 +1261,72 @@ static ArrayRef emitEntryPointIndirectReturn( SILType directResultType = IGF.CurSILFn->mapTypeIntoContext(fnConv.getSILResultType()); if (requiresIndirectResult(directResultType)) { - auto &retTI = IGF.IGM.getTypeInfo(directResultType); - IGF.IndirectReturn = retTI.getAddressForPointer(params.claimNext()); + auto ¶mTI = IGF.IGM.getTypeInfo(directResultType); + auto &retTI = + IGF.IGM.getTypeInfo(IGF.getLoweredTypeInContext(directResultType)); + auto ptr = params.claimNext(); + if (paramTI.getStorageType() != retTI.getStorageType()) { + assert(directResultType.getASTType()->hasOpaqueArchetype()); + ptr = IGF.Builder.CreateBitCast(ptr, + retTI.getStorageType()->getPointerTo()); + } + IGF.IndirectReturn = retTI.getAddressForPointer(ptr); } auto bbargs = entry->getArguments(); // Map the indirect returns if present. unsigned numIndirectResults = fnConv.getNumIndirectSILResults(); - for (unsigned i = 0; i != numIndirectResults; ++i) { - SILArgument *ret = bbargs[i]; - auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); - IGF.setLoweredAddress(ret, retTI.getAddressForPointer(params.claimNext())); - } + unsigned idx = 0; + for (auto indirectResultType : fnConv.getIndirectSILResultTypes()) { + SILArgument *ret = bbargs[idx]; + auto inContextResultType = + IGF.CurSILFn->mapTypeIntoContext(indirectResultType); + auto &retTI = IGF.IGM.getTypeInfo(ret->getType()); + // The parameter's type might be different due to looking through opaque + // archetypes. + auto ptr = params.claimNext(); + auto ¶mTI = IGF.IGM.getTypeInfo(inContextResultType); + if (paramTI.getStorageType() != retTI.getStorageType()) { + assert(inContextResultType.getASTType()->hasOpaqueArchetype()); + ptr = IGF.Builder.CreateBitCast(ptr, + retTI.getStorageType()->getPointerTo()); + } + auto addr = retTI.getAddressForPointer(ptr); + IGF.setLoweredAddress(ret, addr); + ++idx; + } + assert(numIndirectResults == idx); return bbargs.slice(numIndirectResults); } static void bindParameter(IRGenSILFunction &IGF, SILArgument *param, + SILType paramTy, Explosion &allParamValues) { // Pull out the parameter value and its formal type. - auto ¶mTI = IGF.getTypeInfo(param->getType()); + auto ¶mTI = IGF.getTypeInfo(IGF.CurSILFn->mapTypeIntoContext(paramTy)); + auto &argTI = IGF.getTypeInfo(param->getType()); // If the SIL parameter isn't passed indirectly, we need to map it // to an explosion. if (param->getType().isObject()) { Explosion paramValues; - auto &loadableTI = cast(paramTI); + auto &loadableParamTI = cast(paramTI); + auto &loadableArgTI = cast(paramTI); // If the explosion must be passed indirectly, load the value from the // indirect address. - auto &nativeSchema = paramTI.nativeParameterValueSchema(IGF.IGM); + auto &nativeSchema = argTI.nativeParameterValueSchema(IGF.IGM); if (nativeSchema.requiresIndirect()) { - Address paramAddr - = loadableTI.getAddressForPointer(allParamValues.claimNext()); - loadableTI.loadAsTake(IGF, paramAddr, paramValues); + Address paramAddr = + loadableParamTI.getAddressForPointer(allParamValues.claimNext()); + if (paramTI.getStorageType() != argTI.getStorageType()) + paramAddr = + loadableArgTI.getAddressForPointer(IGF.Builder.CreateBitCast( + paramAddr.getAddress(), + loadableArgTI.getStorageType()->getPointerTo())); + loadableArgTI.loadAsTake(IGF, paramAddr, paramValues); } else { if (!nativeSchema.empty()) { // Otherwise, we map from the native convention to the type's explosion @@ -1395,8 +1348,12 @@ static void bindParameter(IRGenSILFunction &IGF, // FIXME: that doesn't mean we should physically pass it // indirectly at this resilience expansion. An @in or @in_guaranteed parameter // could be passed by value in the right resilience domain. - Address paramAddr - = paramTI.getAddressForPointer(allParamValues.claimNext()); + auto ptr = allParamValues.claimNext(); + if (paramTI.getStorageType() != argTI.getStorageType()) { + ptr = + IGF.Builder.CreateBitCast(ptr, argTI.getStorageType()->getPointerTo()); + } + Address paramAddr = argTI.getAddressForPointer(ptr); IGF.setLoweredAddress(param, paramAddr); } @@ -1426,7 +1383,6 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, if (funcTy->hasErrorResult()) { IGF.setErrorResultSlot(allParamValues.takeLast()); } - // The coroutine context should be the first parameter. switch (funcTy->getCoroutineKind()) { case SILCoroutineKind::None: @@ -1439,6 +1395,8 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, break; } + SILFunctionConventions conv(funcTy, IGF.getSILModule()); + // The 'self' argument might be in the context position, which is // now the end of the parameter list. Bind it now. if (hasSelfContextParameter(funcTy)) { @@ -1447,10 +1405,12 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, Explosion selfTemp; selfTemp.add(allParamValues.takeLast()); - bindParameter(IGF, selfParam, selfTemp); + bindParameter(IGF, selfParam, + conv.getSILArgumentType(conv.getNumSILArguments() - 1), + selfTemp); - // Even if we don't have a 'self', if we have an error result, we - // should have a placeholder argument here. + // Even if we don't have a 'self', if we have an error result, we + // should have a placeholder argument here. } else if (funcTy->hasErrorResult() || funcTy->getRepresentation() == SILFunctionTypeRepresentation::Thick) { @@ -1459,8 +1419,11 @@ static void emitEntryPointArgumentsNativeCC(IRGenSILFunction &IGF, } // Map the remaining SIL parameters to LLVM parameters. + unsigned i = 0; for (SILArgument *param : params) { - bindParameter(IGF, param, allParamValues); + auto argIdx = conv.getSILArgIndexOfFirstParam() + i; + bindParameter(IGF, param, conv.getSILArgumentType(argIdx), allParamValues); + ++i; } // Bind polymorphic arguments. This can only be done after binding @@ -1845,9 +1808,6 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { IGM.DebugInfo->setCurrentLoc( Builder, DS, RegularLocation::getAutoGeneratedLocation()); } - - if (isa(&I)) - emitDebugVariableRangeExtension(BB); } visit(&I); } @@ -1913,7 +1873,7 @@ void IRGenSILFunction::visitAllocGlobalInst(AllocGlobalInst *i) { void IRGenSILFunction::visitGlobalAddrInst(GlobalAddrInst *i) { SILGlobalVariable *var = i->getReferencedGlobal(); - SILType loweredTy = var->getLoweredType(); + SILType loweredTy = var->getLoweredTypeInContext(getExpansionContext()); assert(loweredTy == i->getType().getObjectType()); auto &ti = getTypeInfo(loweredTy); @@ -2120,8 +2080,9 @@ emitWitnessTableForLoweredCallee(IRGenSILFunction &IGF, CanSILFunctionType substCalleeType) { // This use of getSelfInstanceType() assumes that the instance type is // always a meaningful formal type. - auto substSelfType = substCalleeType->getSelfInstanceType(); - auto substConformance = substCalleeType->getWitnessMethodConformance(); + auto substSelfType = substCalleeType->getSelfInstanceType(IGF.IGM.getSILModule()); + auto substConformance = + substCalleeType->getWitnessMethodConformanceOrInvalid(); llvm::Value *argMetadata = IGF.emitTypeMetadataRef(substSelfType); llvm::Value *wtable = @@ -2146,7 +2107,8 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF, // Convert a metatype 'self' argument to the ObjC class pointer. // FIXME: why on earth is this not correctly represented in SIL? if (auto metatype = dyn_cast( - calleeInfo.OrigFnType->getSelfParameter().getType())) { + calleeInfo.OrigFnType->getSelfParameter() + .getArgumentType(IGF.IGM.getSILModule(), calleeInfo.OrigFnType))) { selfValue = getObjCClassForValue(IGF, selfValue, metatype); } @@ -2305,7 +2267,7 @@ void IRGenSILFunction::visitFullApplySite(FullApplySite site) { &witnessMetadata, llArgs); // Lower the arguments and return value in the callee's generic context. - GenericContextScope scope(IGM, origCalleeType->getGenericSignature()); + GenericContextScope scope(IGM, origCalleeType->getInvocationGenericSignature()); // Allocate space for the coroutine buffer. Optional
coroutineBuffer; @@ -2485,18 +2447,20 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) { // NB: We collect the arguments under the substituted type. auto args = i->getArguments(); - auto params = i->getSubstCalleeType()->getParameters(); + auto calleeTy = i->getSubstCalleeType(); + auto params = calleeTy->getParameters(); params = params.slice(params.size() - args.size(), args.size()); Explosion llArgs; // Lower the parameters in the callee's generic context. { - GenericContextScope scope(IGM, i->getOrigCalleeType()->getGenericSignature()); + GenericContextScope scope(IGM, + i->getOrigCalleeType()->getSubstGenericSignature()); for (auto index : indices(args)) { - assert(args[index]->getType() == IGM.silConv.getSILType(params[index])); - emitApplyArgument(*this, args[index], - IGM.silConv.getSILType(params[index]), llArgs); + auto paramTy = IGM.silConv.getSILType(params[index], calleeTy); + assert(args[index]->getType() == paramTy); + emitApplyArgument(*this, args[index], paramTy, llArgs); } } @@ -2635,7 +2599,12 @@ static void emitReturnInst(IRGenSILFunction &IGF, auto swiftCCReturn = funcLang == SILFunctionLanguage::Swift; assert(swiftCCReturn || funcLang == SILFunctionLanguage::C && "Need to handle all cases"); - IGF.emitScalarReturn(resultTy, result, swiftCCReturn, false); + SILFunctionConventions conv(IGF.CurSILFn->getLoweredFunctionType(), + IGF.getSILModule()); + auto funcResultType = + IGF.CurSILFn->mapTypeIntoContext(conv.getSILResultType()); + IGF.emitScalarReturn(resultTy, funcResultType, result, swiftCCReturn, + false); } } @@ -2682,7 +2651,7 @@ void IRGenSILFunction::visitYieldInst(swift::YieldInst *i) { auto coroutineType = CurSILFn->getLoweredFunctionType(); SILFunctionConventions coroConv(coroutineType, getSILModule()); - GenericContextScope scope(IGM, coroutineType->getGenericSignature()); + GenericContextScope scope(IGM, coroutineType->getInvocationGenericSignature()); // Collect all the yielded values. Explosion values; @@ -3559,12 +3528,9 @@ void IRGenSILFunction::visitRefElementAddrInst(swift::RefElementAddrInst *i) { llvm::Value *value = base.claimNext(); SILType baseTy = i->getOperand()->getType(); - Address field = projectPhysicalClassMemberAddress(*this, - value, - baseTy, - i->getType(), - i->getField()) - .getAddress(); + Address field = projectPhysicalClassMemberAddress(*this, value, baseTy, + i->getType(), i->getField()) + .getAddress(); setLoweredAddress(i, field); } @@ -3634,13 +3600,15 @@ void IRGenSILFunction::visitStoreInst(swift::StoreInst *i) { } /// Emit the artificial error result argument. -void IRGenSILFunction::emitErrorResultVar(SILResultInfo ErrorInfo, +void IRGenSILFunction::emitErrorResultVar(CanSILFunctionType FnTy, + SILResultInfo ErrorInfo, DebugValueInst *DbgValue) { // We don't need a shadow error variable for debugging on ABI's that return // swifterror in a register. if (IGM.IsSwiftErrorInRegister) return; - auto ErrorResultSlot = getErrorResultSlot(IGM.silConv.getSILType(ErrorInfo)); + auto ErrorResultSlot = getErrorResultSlot( + IGM.silConv.getSILType(ErrorInfo, FnTy)); auto Var = DbgValue->getVarInfo(); assert(Var && "error result without debug info"); auto Storage = emitShadowCopyIfNeeded(ErrorResultSlot.getAddress(), @@ -3648,8 +3616,9 @@ void IRGenSILFunction::emitErrorResultVar(SILResultInfo ErrorInfo, if (!IGM.DebugInfo) return; auto DbgTy = DebugTypeInfo::getErrorResult( - ErrorInfo.getType(), ErrorResultSlot->getType(), IGM.getPointerSize(), - IGM.getPointerAlignment()); + ErrorInfo.getReturnValueType(IGM.getSILModule(), FnTy), + ErrorResultSlot->getType(), + IGM.getPointerSize(), IGM.getPointerAlignment()); IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, DbgTy, getDebugScope(), nullptr, *Var, IndirectValue, ArtificialValue); @@ -3667,7 +3636,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { // representation in SIL. if (!i->getDebugScope()->InlinedCallSite && VarInfo->Name == "$error") { auto funcTy = CurSILFn->getLoweredFunctionType(); - emitErrorResultVar(funcTy->getErrorResult(), i); + emitErrorResultVar(funcTy, funcTy->getErrorResult(), i); } return; } @@ -3821,57 +3790,58 @@ static const ReferenceTypeInfo &getReferentTypeInfo(IRGenFunction &IGF, ti.name##Assign(*this, source, dest, isOptional); \ } \ } -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ - void IRGenSILFunction:: \ - visitStrongRetain##Name##Inst(swift::StrongRetain##Name##Inst *i) { \ - Explosion lowered = getLoweredExplosion(i->getOperand()); \ - auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ - ti.strongRetain##Name(*this, lowered, \ - i->isAtomic() ? irgen::Atomicity::Atomic \ - : irgen::Atomicity::NonAtomic); \ - } \ +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ + void IRGenSILFunction::visitStrongRetain##Name##Inst( \ + swift::StrongRetain##Name##Inst *i) { \ + Explosion lowered = getLoweredExplosion(i->getOperand()); \ + auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ + ti.strongRetain##Name(*this, lowered, \ + i->isAtomic() ? irgen::Atomicity::Atomic \ + : irgen::Atomicity::NonAtomic); \ + } \ void IRGenSILFunction::visit##Name##RetainInst(swift::Name##RetainInst *i) { \ - Explosion lowered = getLoweredExplosion(i->getOperand()); \ - auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ - ti.name##Retain(*this, lowered, \ - i->isAtomic() ? irgen::Atomicity::Atomic \ - : irgen::Atomicity::NonAtomic); \ - } \ - void \ - IRGenSILFunction::visit##Name##ReleaseInst(swift::Name##ReleaseInst *i) { \ - Explosion lowered = getLoweredExplosion(i->getOperand()); \ - auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ - ti.name##Release(*this, lowered, \ - i->isAtomic() ? irgen::Atomicity::Atomic \ - : irgen::Atomicity::NonAtomic); \ - } \ - void IRGenSILFunction::visitCopy##Name##ValueInst( \ - swift::Copy##Name##ValueInst *i) { \ - Explosion in = getLoweredExplosion(i->getOperand()); \ - auto silTy = i->getOperand()->getType(); \ - auto ty = cast(silTy.getASTType()); \ - auto isOptional = bool(ty.getReferentType()->getOptionalObjectType()); \ - auto &ti = getReferentTypeInfo(*this, silTy); \ - ti.strongRetain##Name(*this, in, irgen::Atomicity::Atomic); \ - /* Semantically we are just passing through the input parameter but as a */\ - /* strong reference... at LLVM IR level these type differences don't */ \ - /* matter. So just set the lowered explosion appropriately. */ \ - Explosion output = getLoweredExplosion(i->getOperand()); \ - if (isOptional) { \ - auto values = output.claimAll(); \ - output.reset(); \ - for (auto value : values) { \ - output.add(Builder.CreatePtrToInt(value, IGM.IntPtrTy)); \ - } \ - } \ - setLoweredExplosion(i, output); \ + Explosion lowered = getLoweredExplosion(i->getOperand()); \ + auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ + ti.name##Retain(*this, lowered, \ + i->isAtomic() ? irgen::Atomicity::Atomic \ + : irgen::Atomicity::NonAtomic); \ + } \ + void IRGenSILFunction::visit##Name##ReleaseInst( \ + swift::Name##ReleaseInst *i) { \ + Explosion lowered = getLoweredExplosion(i->getOperand()); \ + auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \ + ti.name##Release(*this, lowered, \ + i->isAtomic() ? irgen::Atomicity::Atomic \ + : irgen::Atomicity::NonAtomic); \ + } \ + void IRGenSILFunction::visitStrongCopy##Name##ValueInst( \ + swift::StrongCopy##Name##ValueInst *i) { \ + Explosion in = getLoweredExplosion(i->getOperand()); \ + auto silTy = i->getOperand()->getType(); \ + auto ty = cast(silTy.getASTType()); \ + auto isOptional = bool(ty.getReferentType()->getOptionalObjectType()); \ + auto &ti = getReferentTypeInfo(*this, silTy); \ + ti.strongRetain##Name(*this, in, irgen::Atomicity::Atomic); \ + /* Semantically we are just passing through the input parameter but as a \ + */ \ + /* strong reference... at LLVM IR level these type differences don't */ \ + /* matter. So just set the lowered explosion appropriately. */ \ + Explosion output = getLoweredExplosion(i->getOperand()); \ + if (isOptional) { \ + auto values = output.claimAll(); \ + output.reset(); \ + for (auto value : values) { \ + output.add(Builder.CreatePtrToInt(value, IGM.IntPtrTy)); \ + } \ + } \ + setLoweredExplosion(i, output); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, "...") #define UNCHECKED_REF_STORAGE(Name, name, ...) \ - void IRGenSILFunction::visitCopy##Name##ValueInst( \ - swift::Copy##Name##ValueInst *i) { \ + void IRGenSILFunction::visitStrongCopy##Name##ValueInst( \ + swift::StrongCopy##Name##ValueInst *i) { \ Explosion in = getLoweredExplosion(i->getOperand()); \ auto silTy = i->getOperand()->getType(); \ auto ty = cast(silTy.getASTType()); \ @@ -3999,7 +3969,8 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, isa(addr)); auto Indirection = DirectValue; - if (!IGM.IRGen.Opts.shouldOptimize()) + if (!IGM.IRGen.Opts.DisableDebuggerShadowCopies && + !IGM.IRGen.Opts.shouldOptimize()) if (auto *Alloca = dyn_cast(addr)) if (!Alloca->isStaticAlloca()) { // Store the address of the dynamic alloca on the stack. @@ -4181,7 +4152,8 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { assert(i->getBoxType()->getLayout()->getFields().size() == 1 && "multi field boxes not implemented yet"); const TypeInfo &type = getTypeInfo( - getSILBoxFieldType(i->getBoxType(), IGM.getSILModule().Types, 0)); + getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(), i->getBoxType(), + IGM.getSILModule().Types, 0)); // Derive name from SIL location. bool IsAnonymous = false; @@ -4219,7 +4191,9 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { assert(i->getBoxType()->getLayout()->getFields().size() == 1 && "box for a local variable should only have one field"); - auto SILTy = getSILBoxFieldType(i->getBoxType(), IGM.getSILModule().Types, 0); + auto SILTy = getSILBoxFieldType( + IGM.getMaximalTypeExpansionContext(), + i->getBoxType(), IGM.getSILModule().Types, 0); auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); @@ -4537,7 +4511,9 @@ void IRGenSILFunction:: visitUncheckedRefCastAddrInst(swift::UncheckedRefCastAddrInst *i) { Address dest = getLoweredAddress(i->getDest()); Address src = getLoweredAddress(i->getSrc()); - emitCheckedCast(*this, src, i->getSourceType(), dest, i->getTargetType(), + emitCheckedCast(*this, + src, i->getSourceFormalType(), + dest, i->getTargetFormalType(), CastConsumptionKind::TakeAlways, CheckedCastMode::Unconditional); } @@ -4763,7 +4739,11 @@ void IRGenSILFunction::visitUnconditionalCheckedCastInst( swift::UnconditionalCheckedCastInst *i) { Explosion value = getLoweredExplosion(i->getOperand()); Explosion ex; - emitScalarCheckedCast(*this, value, i->getOperand()->getType(), i->getType(), + emitScalarCheckedCast(*this, value, + i->getSourceLoweredType(), + i->getSourceFormalType(), + i->getTargetLoweredType(), + i->getTargetFormalType(), CheckedCastMode::Unconditional, ex); setLoweredExplosion(i, ex); } @@ -4948,7 +4928,9 @@ void IRGenSILFunction::visitUnconditionalCheckedCastAddrInst( swift::UnconditionalCheckedCastAddrInst *i) { Address dest = getLoweredAddress(i->getDest()); Address src = getLoweredAddress(i->getSrc()); - emitCheckedCast(*this, src, i->getSourceType(), dest, i->getTargetType(), + emitCheckedCast(*this, + src, i->getSourceFormalType(), + dest, i->getTargetFormalType(), CastConsumptionKind::TakeAlways, CheckedCastMode::Unconditional); } @@ -4965,18 +4947,22 @@ void IRGenSILFunction::visitCheckedCastValueBranchInst( void IRGenSILFunction::visitCheckedCastBranchInst( swift::CheckedCastBranchInst *i) { - SILType destTy = i->getCastType(); FailableCastResult castResult; Explosion ex; if (i->isExact()) { auto operand = i->getOperand(); Explosion source = getLoweredExplosion(operand); castResult = emitClassIdenticalCast(*this, source.claimNext(), - operand->getType(), destTy); + i->getSourceLoweredType(), + i->getTargetLoweredType()); } else { Explosion value = getLoweredExplosion(i->getOperand()); - emitScalarCheckedCast(*this, value, i->getOperand()->getType(), - i->getCastType(), CheckedCastMode::Conditional, ex); + emitScalarCheckedCast(*this, value, + i->getSourceLoweredType(), + i->getSourceFormalType(), + i->getTargetLoweredType(), + i->getTargetFormalType(), + CheckedCastMode::Conditional, ex); auto val = ex.claimNext(); castResult.casted = val; llvm::Value *nil = @@ -4989,7 +4975,7 @@ void IRGenSILFunction::visitCheckedCastBranchInst( auto &successBB = getLoweredBB(i->getSuccessBB()); - llvm::Type *toTy = IGM.getTypeInfo(destTy).getStorageType(); + llvm::Type *toTy = IGM.getTypeInfo(i->getTargetLoweredType()).getStorageType(); if (toTy->isPointerTy()) castResult.casted = Builder.CreateBitCast(castResult.casted, toTy); @@ -5010,7 +4996,9 @@ void IRGenSILFunction::visitCheckedCastAddrBranchInst( Address dest = getLoweredAddress(i->getDest()); Address src = getLoweredAddress(i->getSrc()); llvm::Value *castSucceeded = - emitCheckedCast(*this, src, i->getSourceType(), dest, i->getTargetType(), + emitCheckedCast(*this, + src, i->getSourceFormalType(), + dest, i->getTargetFormalType(), i->getConsumptionKind(), CheckedCastMode::Conditional); Builder.CreateCondBr(castSucceeded, getLoweredBB(i->getSuccessBB()).bb, @@ -5399,7 +5387,8 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { if (IGM.isResilient(conformance.getRequirement(), ResilienceExpansion::Maximal)) { auto *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition); - auto fnType = IGM.getSILTypes().getConstantFunctionType(member); + auto fnType = IGM.getSILTypes().getConstantFunctionType( + IGM.getMaximalTypeExpansionContext(), member); auto sig = IGM.getSignature(fnType); auto fn = FunctionPointer::forDirect(fnPtr, sig); @@ -5410,9 +5399,9 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) { // It would be nice if this weren't discarded. llvm::Value *baseMetadataCache = nullptr; - auto fn = emitWitnessMethodValue(*this, baseTy, &baseMetadataCache, - member, conformance); - + auto fn = emitWitnessMethodValue(*this, baseTy, &baseMetadataCache, member, + conformance); + setLoweredFunctionPointer(i, fn); } @@ -5466,11 +5455,19 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) { Explosion e = getLoweredExplosion(i->getOperand()); llvm::Value *cond = e.claimNext(); + // The condition should be false, or we die. + auto expectedCond = Builder.CreateExpect(cond, + llvm::ConstantInt::get(IGM.Int1Ty, 0)); + // Emit individual fail blocks so that we can map the failure back to a source // line. + auto origInsertionPoint = Builder.GetInsertBlock(); + llvm::BasicBlock *failBB = llvm::BasicBlock::Create(IGM.getLLVMContext()); llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGM.getLLVMContext()); - Builder.CreateCondBr(cond, failBB, contBB); + Builder.CreateCondBr(expectedCond, failBB, contBB); + + Builder.SetInsertPoint(&CurFn->back()); Builder.emitBlock(failBB); if (IGM.DebugInfo) // If we are emitting DWARF, this does nothing. Otherwise the ``llvm.trap`` @@ -5479,6 +5476,8 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) { // in CodeView. IGM.DebugInfo->setInlinedTrapLocation(Builder, i->getDebugScope()); emitTrap(i->getMessage(), /*EmitUnreachable=*/true); + + Builder.SetInsertPoint(origInsertionPoint); Builder.emitBlock(contBB); FailBBs.push_back(failBB); } @@ -5559,9 +5558,9 @@ void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) { // Non-resilient case. - auto fn = emitVirtualMethodValue(*this, baseValue, baseType, - method, methodType, - /*useSuperVTable*/ true); + auto fn = + emitVirtualMethodValue(*this, baseValue, baseType, method, methodType, + /*useSuperVTable*/ true); setLoweredFunctionPointer(i, fn); } @@ -5595,10 +5594,9 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { // For Swift classes, get the method implementation from the vtable. // FIXME: better explosion kind, map as static. - FunctionPointer fn = emitVirtualMethodValue(*this, baseValue, - i->getOperand()->getType(), - method, methodType, - /*useSuperVTable*/ false); + FunctionPointer fn = emitVirtualMethodValue( + *this, baseValue, i->getOperand()->getType(), method, methodType, + /*useSuperVTable*/ false); setLoweredFunctionPointer(i, fn); } @@ -5617,6 +5615,15 @@ void IRGenModule::emitSILStaticInitializers() { if (!InitValue) continue; +#ifndef NDEBUG + SILType loweredTy = Global.getLoweredType(); + auto &ti = getTypeInfo(loweredTy); + + auto expansion = getResilienceExpansionForLayout(&Global); + assert(ti.isFixedSize(expansion) && + "cannot emit a static initializer for dynamically-sized global"); +#endif + auto *IRGlobal = Module.getGlobalVariable(Global.getName(), true /* = AllowLocal */); diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index f344db5c9057d..85698e126a4dd 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -37,7 +37,7 @@ using namespace swift; using namespace swift::irgen; static GenericEnvironment *getGenericEnvironment(CanSILFunctionType loweredTy) { - return loweredTy->getGenericSignature()->getGenericEnvironment(); + return loweredTy->getSubstGenericSignature()->getGenericEnvironment(); } class LargeSILTypeMapper { @@ -207,7 +207,7 @@ bool LargeSILTypeMapper::newResultsDiffer(GenericEnvironment *GenericEnv, irgen::IRGenModule &Mod) { SmallVector newResults; for (auto result : origResults) { - SILType currResultTy = result.getSILStorageType(); + SILType currResultTy = result.getSILStorageInterfaceType(); SILType newSILType = getNewSILType(GenericEnv, currResultTy, Mod); // We (currently) only care about function signatures if (containsDifferentFunctionSignature(GenericEnv, Mod, currResultTy, @@ -228,7 +228,7 @@ static bool modNonFuncTypeResultType(GenericEnvironment *genEnv, return false; } auto singleResult = loweredTy->getSingleResult(); - auto resultStorageType = singleResult.getSILStorageType(); + auto resultStorageType = singleResult.getSILStorageInterfaceType(); if (isLargeLoadableType(genEnv, resultStorageType, Mod)) { return true; } @@ -245,7 +245,7 @@ LargeSILTypeMapper::getNewResults(GenericEnvironment *GenericEnv, auto origResults = fnType->getResults(); SmallVector newResults; for (auto result : origResults) { - SILType currResultTy = result.getSILStorageType(); + SILType currResultTy = result.getSILStorageInterfaceType(); SILType newSILType = getNewSILType(GenericEnv, currResultTy, Mod); if (modNonFuncTypeResultType(GenericEnv, fnType, Mod)) { // Case (2) Above @@ -275,7 +275,7 @@ LargeSILTypeMapper::getNewSILFunctionType(GenericEnvironment *env, auto newYields = getNewYields(env, fnType, IGM); auto newResults = getNewResults(env, fnType, IGM); auto newFnType = SILFunctionType::get( - fnType->getGenericSignature(), + fnType->getSubstGenericSignature(), fnType->getExtInfo(), fnType->getCoroutineKind(), fnType->getCalleeConvention(), @@ -283,8 +283,10 @@ LargeSILTypeMapper::getNewSILFunctionType(GenericEnvironment *env, newYields, newResults, fnType->getOptionalErrorResult(), + fnType->getSubstitutions(), + fnType->isGenericSignatureImplied(), fnType->getASTContext(), - fnType->getWitnessMethodConformanceOrNone()); + fnType->getWitnessMethodConformanceOrInvalid()); return newFnType; } @@ -314,13 +316,13 @@ bool LargeSILTypeMapper::shouldTransformResults(GenericEnvironment *genEnv, } if (loweredTy->getNumResults() != 1) { - auto resultType = loweredTy->getAllResultsType(); + auto resultType = loweredTy->getAllResultsInterfaceType(); auto newResultType = getNewSILType(genEnv, resultType, Mod); return resultType != newResultType; } auto singleResult = loweredTy->getSingleResult(); - auto resultStorageType = singleResult.getSILStorageType(); + auto resultStorageType = singleResult.getSILStorageInterfaceType(); auto newResultStorageType = getNewSILType(genEnv, resultStorageType, Mod); if (resultStorageType != newResultStorageType) { return true; @@ -344,7 +346,7 @@ static bool shouldTransformYields(GenericEnvironment *genEnv, return false; } for (auto &yield : loweredTy->getYields()) { - auto yieldStorageType = yield.getSILStorageType(); + auto yieldStorageType = yield.getSILStorageInterfaceType(); auto newYieldStorageType = Mapper.getNewSILType(genEnv, yieldStorageType, Mod); if (yieldStorageType != newYieldStorageType) @@ -364,17 +366,17 @@ static bool modYieldType(SILFunction *F, irgen::IRGenModule &Mod, SILParameterInfo LargeSILTypeMapper::getNewParameter(GenericEnvironment *env, SILParameterInfo param, irgen::IRGenModule &IGM) { - SILType storageType = param.getSILStorageType(); + SILType storageType = param.getSILStorageInterfaceType(); SILType newOptFuncType = getNewOptionalFunctionType(env, storageType, IGM); if (newOptFuncType != storageType) { - return param.getWithType(newOptFuncType.getASTType()); + return param.getWithInterfaceType(newOptFuncType.getASTType()); } if (auto paramFnType = storageType.getAs()) { if (shouldTransformFunctionType(env, paramFnType, IGM)) { auto newFnType = getNewSILFunctionType(env, paramFnType, IGM); - return param.getWithType(newFnType); + return param.getWithInterfaceType(newFnType); } else { return param; } @@ -411,7 +413,7 @@ LargeSILTypeMapper::getNewYields(GenericEnvironment *env, SmallVector newYields; for (auto oldYield : fnType->getYields()) { auto newYieldAsParam = getNewParameter(env, oldYield, IGM); - newYields.push_back(SILYieldInfo(newYieldAsParam.getType(), + newYields.push_back(SILYieldInfo(newYieldAsParam.getInterfaceType(), newYieldAsParam.getConvention())); } return newYields; @@ -1353,7 +1355,7 @@ SILArgument *LoadableStorageAllocation::replaceArgType(SILBuilder &argBuilder, arg) == pass.largeLoadableArgs.end()); arg = arg->getParent()->replaceFunctionArgument( - arg->getIndex(), newSILType, ValueOwnershipKind::Any, arg->getDecl()); + arg->getIndex(), newSILType, ValueOwnershipKind::None, arg->getDecl()); for (auto *use : useList) { use->set(arg); @@ -1365,7 +1367,7 @@ SILArgument *LoadableStorageAllocation::replaceArgType(SILBuilder &argBuilder, void LoadableStorageAllocation::insertIndirectReturnArgs() { GenericEnvironment *genEnv = pass.F->getGenericEnvironment(); auto loweredTy = pass.F->getLoweredFunctionType(); - SILType resultStorageType = loweredTy->getAllResultsType(); + SILType resultStorageType = loweredTy->getAllResultsInterfaceType(); auto canType = resultStorageType.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); @@ -1382,7 +1384,7 @@ void LoadableStorageAllocation::insertIndirectReturnArgs() { pass.F->getDeclContext()); var->setSpecifier(ParamSpecifier::InOut); pass.F->begin()->insertFunctionArgument( - 0, newResultStorageType.getAddressType(), ValueOwnershipKind::Any, var); + 0, newResultStorageType.getAddressType(), ValueOwnershipKind::None, var); } void LoadableStorageAllocation::convertIndirectFunctionArgs() { @@ -1459,12 +1461,12 @@ void LoadableStorageAllocation::convertApplyResults() { pass.Mod)) { continue; } - auto resultStorageType = origSILFunctionType->getAllResultsType(); + auto resultStorageType = origSILFunctionType->getAllResultsInterfaceType(); if (!pass.isLargeLoadableType(resultStorageType)) { // Make sure it contains a function type auto numFuncTy = llvm::count_if(origSILFunctionType->getResults(), [](const SILResultInfo &origResult) { - auto resultStorageTy = origResult.getSILStorageType(); + auto resultStorageTy = origResult.getSILStorageInterfaceType(); return containsFunctionType(resultStorageTy.getASTType()); }); assert(numFuncTy != 0 && @@ -1848,7 +1850,7 @@ static void castTupleInstr(SingleValueInstruction *instr, IRGenModule &Mod, auto funcType = getInnerFunctionType(currSILType); assert(funcType && "Expected a function Type"); GenericEnvironment *genEnv = instr->getFunction()->getGenericEnvironment(); - if (!genEnv && funcType->isPolymorphic()) { + if (!genEnv && funcType->getSubstGenericSignature()) { genEnv = getGenericEnvironment(funcType); } SILType newSILType = Mapper.getNewSILType(genEnv, currSILType, Mod); @@ -2282,7 +2284,7 @@ static void rewriteFunction(StructLoweringState &pass, static bool rewriteFunctionReturn(StructLoweringState &pass) { auto loweredTy = pass.F->getLoweredFunctionType(); SILFunction *F = pass.F; - SILType resultTy = loweredTy->getAllResultsType(); + SILType resultTy = loweredTy->getAllResultsInterfaceType(); SILType newSILType = pass.getNewSILType(resultTy); // We (currently) only care about function signatures if (pass.isLargeLoadableType(resultTy)) { @@ -2306,13 +2308,16 @@ static bool rewriteFunctionReturn(StructLoweringState &pass) { } auto NewTy = SILFunctionType::get( - loweredTy->getGenericSignature(), loweredTy->getExtInfo(), + loweredTy->getSubstGenericSignature(), + loweredTy->getExtInfo(), loweredTy->getCoroutineKind(), - loweredTy->getCalleeConvention(), loweredTy->getParameters(), + loweredTy->getCalleeConvention(), + loweredTy->getParameters(), loweredTy->getYields(), newSILResultInfo, loweredTy->getOptionalErrorResult(), + loweredTy->getSubstitutions(), loweredTy->isGenericSignatureImplied(), F->getModule().getASTContext(), - loweredTy->getWitnessMethodConformanceOrNone()); + loweredTy->getWitnessMethodConformanceOrInvalid()); F->rewriteLoweredTypeUnsafe(NewTy); return true; } @@ -2330,7 +2335,7 @@ void LoadableByAddress::runOnFunction(SILFunction *F) { // External function - re-write external declaration - this is ABI! GenericEnvironment *genEnv = F->getGenericEnvironment(); auto loweredTy = F->getLoweredFunctionType(); - if (!genEnv && loweredTy->isPolymorphic()) { + if (!genEnv && loweredTy->getSubstGenericSignature()) { genEnv = getGenericEnvironment(loweredTy); } if (MapperCache.shouldTransformFunctionType(genEnv, loweredTy, @@ -2351,7 +2356,8 @@ void LoadableByAddress::runOnFunction(SILFunction *F) { rewrittenReturn = rewriteFunctionReturn(pass); } - LLVM_DEBUG(llvm::dbgs() << "\nREWRITING: " << F->getName(); F->dump()); + LLVM_DEBUG(llvm::dbgs() << "\nREWRITING: " << F->getName(); + F->print(llvm::dbgs())); // Rewrite instructions relating to the loadable struct. rewriteFunction(pass, allocator); @@ -2586,7 +2592,7 @@ bool LoadableByAddress::recreateUncheckedEnumDataInstr( GenericEnvironment *genEnv = F->getGenericEnvironment(); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( - enumInstr->getElement(), F->getModule()); + enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (newType.isAddress()) { newType = newType.getObjectType(); @@ -2619,7 +2625,7 @@ bool LoadableByAddress::recreateUncheckedTakeEnumDataAddrInst( GenericEnvironment *genEnv = F->getGenericEnvironment(); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( - enumInstr->getElement(), F->getModule()); + enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (caseTy != origType.getObjectType()) { auto *takeEnum = enumBuilder.createUncheckedTakeEnumDataAddr( @@ -2777,7 +2783,7 @@ void LoadableByAddress::updateLoweredTypes(SILFunction *F) { IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); CanSILFunctionType funcType = F->getLoweredFunctionType(); GenericEnvironment *genEnv = F->getGenericEnvironment(); - if (!genEnv && funcType->isPolymorphic()) { + if (!genEnv && funcType->getSubstGenericSignature()) { genEnv = getGenericEnvironment(funcType); } auto newFuncTy = diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index 795529c5f8a41..8b54e77503875 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -314,18 +314,20 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl) super::noteStartOfGenericRequirements(forClass); } - void addGenericWitnessTable(ClassDecl *forClass) { + void addGenericWitnessTable(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericWitnessTable(forClass); + super::addGenericWitnessTable(requirement, forClass); } - void addGenericArgument(ClassDecl *forClass) { + void addGenericArgument(GenericRequirement requirement, + ClassDecl *forClass) { if (forClass == Target) { Layout.NumImmediateMembers++; } - super::addGenericArgument(forClass); + super::addGenericArgument(requirement, forClass); } void addMethod(SILDeclRef fn) { diff --git a/lib/IRGen/MetadataPath.h b/lib/IRGen/MetadataPath.h index 77c59162f0a92..d8456aac0005a 100644 --- a/lib/IRGen/MetadataPath.h +++ b/lib/IRGen/MetadataPath.h @@ -183,7 +183,7 @@ class MetadataPath { /// Return an abstract measurement of the cost of this path. OperationCost cost() const { auto cost = OperationCost::Free; - for (const Component &component : Path) + for (const Component component : Path) cost += component.cost(); return cost; } diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 62adc6a22557c..3d8385d7db6e6 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -19,24 +19,31 @@ #include "ConstantBuilder.h" #include "Explosion.h" #include "FixedTypeInfo.h" -#include "GenericRequirement.h" #include "GenArchetype.h" #include "GenClass.h" #include "GenMeta.h" #include "GenProto.h" #include "GenType.h" +#include "GenericArguments.h" +#include "GenericRequirement.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenMangler.h" #include "IRGenModule.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/SubstitutionMap.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/TypeLowering.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Constant.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" +#include using namespace swift; using namespace irgen; @@ -432,60 +439,6 @@ static llvm::Value *emitObjCMetadataRef(IRGenFunction &IGF, return emitObjCMetadataRefForMetadata(IGF, classPtr); } -namespace { - /// A structure for collecting generic arguments for emitting a - /// nominal metadata reference. The structure produced here is - /// consumed by swift_getGenericMetadata() and must correspond to - /// the fill operations that the compiler emits for the bound decl. - struct GenericArguments { - /// The values to use to initialize the arguments structure. - SmallVector Values; - SmallVector Types; - - static unsigned getNumGenericArguments(IRGenModule &IGM, - NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - return requirements.getNumTypeRequirements(); - } - - void collectTypes(IRGenModule &IGM, NominalTypeDecl *nominal) { - GenericTypeRequirements requirements(IGM, nominal); - collectTypes(IGM, requirements); - } - - void collectTypes(IRGenModule &IGM, - const GenericTypeRequirements &requirements) { - for (auto &requirement : requirements.getRequirements()) { - if (requirement.Protocol) { - Types.push_back(IGM.WitnessTablePtrTy); - } else { - Types.push_back(IGM.TypeMetadataPtrTy); - } - } - } - - void collect(IRGenFunction &IGF, CanType type) { - auto *decl = type.getNominalOrBoundGenericNominal(); - GenericTypeRequirements requirements(IGF.IGM, decl); - - auto subs = - type->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), decl); - requirements.enumerateFulfillments(IGF.IGM, subs, - [&](unsigned reqtIndex, CanType type, - Optional conf) { - if (conf) { - Values.push_back(emitWitnessTableRef(IGF, type, *conf)); - } else { - Values.push_back(IGF.emitAbstractTypeMetadataRef(type)); - } - }); - - collectTypes(IGF.IGM, decl); - assert(Types.size() == Values.size()); - } - }; -} // end anonymous namespace - static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) { // ObjC classes are type erased. // TODO: Unless they have magic methods... @@ -516,11 +469,13 @@ CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); - type = type.subst(replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes) - ->getCanonicalType(); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); + auto underlyingTy = + type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) + ->getCanonicalType(); + return underlyingTy; } return type; @@ -531,10 +486,13 @@ SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes( // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type.getASTType()->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); - type = type.subst(getSILModule(), replacer, replacer, genericSig, - /*substitute opaque*/ true); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); + auto underlyingTy = + type.subst(getSILModule(), replacer, replacer, genericSig, + /*substitute opaque*/ true); + return underlyingTy; } return type; @@ -546,13 +504,15 @@ IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type, // Substitute away opaque types whose underlying types we're allowed to // assume are constant. if (type->hasOpaqueArchetype()) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer(getSwiftModule(), - ResilienceExpansion::Maximal); - conformance = conformance.subst(type, replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes); - type = type.subst(replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes) - ->getCanonicalType(); + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + getSwiftModule(), ResilienceExpansion::Maximal, + getSILModule().isWholeModule()); + auto substConformance = conformance.subst( + type, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); + auto underlyingTy = + type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes) + ->getCanonicalType(); + return std::make_pair(underlyingTy, substConformance); } return std::make_pair(type, conformance); @@ -631,6 +591,22 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF, classObject); } +static MetadataResponse emitNominalPrespecializedGenericMetadataRef( + IRGenFunction &IGF, NominalTypeDecl *theDecl, CanType theType, + DynamicMetadataRequest request) { + assert(isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)); + // We are applying generic parameters to a generic type. + assert(theType->getAnyNominal() == theDecl); + + // Check to see if we've maybe got a local reference already. + if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request)) + return cache; + + auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType); + return MetadataResponse::forComplete(metadata); +} + /// Returns a metadata reference for a nominal type. /// /// This is only valid in a couple of special cases: @@ -671,6 +647,12 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, theDecl->getGenericSignature()->areAllParamsConcrete()) && "no generic args?!"); + if (isNominalGenericContextTypeMetadataAccessTrivial(IGF.IGM, *theDecl, + theType)) { + return emitNominalPrespecializedGenericMetadataRef(IGF, theDecl, theType, + request); + } + // Call the generic metadata accessor function. llvm::Function *accessor = IGF.IGM.getAddrOfGenericTypeMetadataAccessFunction(theDecl, @@ -685,6 +667,72 @@ static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF, return response; } +bool irgen::isNominalGenericContextTypeMetadataAccessTrivial( + IRGenModule &IGM, NominalTypeDecl &nominal, CanType type) { + assert(nominal.isGenericContext()); + + if (!IGM.shouldPrespecializeGenericMetadata()) { + return false; + } + + if (type->hasArchetype()) { + return false; + } + + if (nominal.getModuleContext() != IGM.getSwiftModule() || + nominal.isResilient(IGM.getSwiftModule(), ResilienceExpansion::Minimal)) { + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support enums. + return false; + } + + if (isa(type) || isa(type)) { + // TODO: Support classes. + return false; + } + + auto *generic = type.getAnyGeneric(); + assert(generic); + auto *environment = generic->getGenericEnvironment(); + assert(environment); + auto substitutions = + type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal); + + return llvm::all_of(environment->getGenericParams(), [&](auto parameter) { + auto conformances = + environment->getGenericSignature()->getConformsTo(parameter); + auto witnessTablesAreReferenceable = + llvm::all_of(conformances, [&](ProtocolDecl *conformance) { + return conformance->getModuleContext() == IGM.getSwiftModule() && + !conformance->isResilient(IGM.getSwiftModule(), + ResilienceExpansion::Minimal); + }); + auto argument = ((Type *)parameter)->subst(substitutions); + auto genericArgument = argument->getAnyGeneric(); + // For now, to avoid statically specializing generic protocol witness + // tables, don't statically specialize metadata for types any of whose + // arguments are generic. + // + // TODO: This is more pessimistic than necessary. Specialize even in + // the face of generic arguments so long as those arguments + // aren't required to conform to any protocols. + // + // TODO: Once witness tables are statically specialized, check whether the + // ConformanceInfo returns nullptr from tryGetConstantTable. + // early return. + auto isGeneric = genericArgument && genericArgument->isGenericContext(); + auto isNominal = argument->getNominalOrBoundGenericNominal(); + auto isExistential = argument->isExistentialType(); + return isNominal && !isGeneric && !isExistential && + witnessTablesAreReferenceable && + irgen::isTypeMetadataAccessTrivial(IGM, + argument->getCanonicalType()); + }) && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); +} + /// Is it basically trivial to access the given metadata? If so, we don't /// need a cache variable in its accessor. bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { @@ -700,14 +748,14 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (isa(nominalDecl->getModuleScopeContext())) return false; - // Generic type metadata always requires an accessor. if (nominalDecl->isGenericContext()) - return false; + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); auto expansion = ResilienceExpansion::Maximal; // Resiliently-sized metadata access always requires an accessor. - return (IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion)); + return IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion); } // The empty tuple type has a singleton metadata. @@ -732,6 +780,18 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { if (type->hasDynamicSelfType()) return true; + if (isa(type) || isa(type)) { + auto nominalType = cast(type); + auto *nominalDecl = nominalType->getDecl(); + + // Imported type metadata always requires an accessor. + if (isa(nominalDecl->getModuleScopeContext())) + return false; + + return isNominalGenericContextTypeMetadataAccessTrivial(IGM, *nominalDecl, + type); + } + return false; } @@ -1676,11 +1736,77 @@ IRGenFunction::emitGenericTypeMetadataAccessFunctionCall( return MetadataResponse::handle(*this, request, call); } -static MetadataResponse -emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, - Explosion ¶ms, - NominalTypeDecl *nominal, - GenericArguments &genericArgs) { +static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs, + std::function valueAtIndex) { + auto &IGM = IGF.IGM; + auto specializations = IGF.IGM.IRGen.specializationsForType(nominal); + if (specializations.size() > 0) { + SmallVector conditionBlocks; + for (size_t index = 0; index < specializations.size(); ++index) { + conditionBlocks.push_back(llvm::BasicBlock::Create(IGM.getLLVMContext())); + } + + IGF.Builder.CreateBr(conditionBlocks[0]); + + SmallVector, 4> + specializationBlocks; + auto switchDestination = llvm::BasicBlock::Create(IGM.getLLVMContext()); + unsigned long index = 0; + for (auto specialization : specializations) { + auto conditionBlock = conditionBlocks[index]; + IGF.Builder.emitBlock(conditionBlock); + auto successorBlock = index < conditionBlocks.size() - 1 + ? conditionBlocks[index + 1] + : switchDestination; + auto specializationBlock = llvm::BasicBlock::Create(IGM.getLLVMContext()); + auto substitutions = specialization->getContextSubstitutionMap( + IGM.getSwiftModule(), nominal); + + llvm::Value *condition = llvm::ConstantInt::get(IGM.Int1Ty, 1); + auto generic = specialization->getAnyGeneric(); + auto parameters = generic->getGenericEnvironment()->getGenericParams(); + for (size_t index = 0; index < parameters.size(); ++index) { + auto parameter = parameters[index]; + auto argument = ((Type *)parameter)->subst(substitutions); + llvm::Constant *addr = + IGM.getAddrOfTypeMetadata(argument->getCanonicalType()); + auto addrInt = IGF.Builder.CreateBitCast(addr, IGM.Int8PtrTy); + condition = IGF.Builder.CreateAnd( + condition, IGF.Builder.CreateICmpEQ(addrInt, valueAtIndex(index))); + } + IGF.Builder.CreateCondBr(condition, specializationBlock, successorBlock); + + auto specializedMetadataAddress = + IGM.getAddrOfTypeMetadata(specialization); + // Construct a MetadataResponse. It has three fields in the following + // order: + // - const Metadata *Metadata; + // - MetadataState (i32) StaticState; + llvm::Value *response = llvm::UndefValue::get(IGM.TypeMetadataResponseTy); + response = IGF.Builder.CreateInsertValue( + response, specializedMetadataAddress, 0, + "insert metadata address into response"); + auto state = + llvm::ConstantInt::get(IGM.SizeTy, (uint32_t)MetadataState::Complete); + response = IGF.Builder.CreateInsertValue( + response, state, 1, "insert metadata state into response"); + specializationBlocks.push_back({specializationBlock, response}); + ++index; + } + + for (auto pair : specializationBlocks) { + IGF.Builder.emitBlock(pair.first); + IGF.Builder.CreateRet(pair.second); + } + IGF.Builder.emitBlock(switchDestination); + } +} + +MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( + IRGenFunction &IGF, Explosion ¶ms, NominalTypeDecl *nominal, + GenericArguments &genericArgs) { auto &IGM = IGF.IGM; llvm::Constant *descriptor = @@ -1699,6 +1825,21 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, llvm::Value *arguments = IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrTy); + llvm::Value *argumentsBuffer = + IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrPtrTy); + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, [&](int index) { + llvm::Value *indexValue = llvm::ConstantInt::get(IGM.Int64Ty, index); + llvm::SmallVector indices{indexValue}; + llvm::Value *elementPointer = + IGF.Builder.CreateGEP(argumentsBuffer, indexValue); + llvm::LoadInst *retval = IGF.Builder.CreateLoad( + elementPointer, Alignment(), + llvm::formatv("load argument at index {0} from buffer", index)); + return retval; + }); + // Make the call. auto call = IGF.Builder.CreateCall(IGM.getGetGenericMetadataFn(), {request, arguments, descriptor}); @@ -1784,7 +1925,13 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, auto arg2 = numArguments >= 3 ? IGF.Builder.CreateBitCast(params.claimNext(), IGM.Int8PtrTy) : llvm::UndefValue::get(IGM.Int8PtrTy); - + + std::array argValues{arg0, arg1, arg2}; + + emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( + IGF, params, nominal, genericArgs, + [&](int index) { return argValues[index]; }); + auto call = IGF.Builder.CreateCall(thunkFn, {request, arg0, arg1, arg2, descriptor}); call->setDoesNotAccessMemory(); @@ -1979,18 +2126,7 @@ irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM, if (!shouldDefine || !accessor->empty()) return accessor; - if (IGM.getOptions().optimizeForSize()) - accessor->addFnAttr(llvm::Attribute::NoInline); - - bool isReadNone = - (genericArgs.Types.size() <= NumDirectGenericTypeMetadataAccessFunctionArgs); - - emitCacheAccessFunction(IGM, accessor, /*cache*/nullptr, CacheStrategy::None, - [&](IRGenFunction &IGF, Explosion ¶ms) { - return emitGenericTypeMetadataAccessFunction( - IGF, params, nominal, genericArgs); - }, - isReadNone); + IGM.IRGen.noteUseOfMetadataAccessor(nominal); return accessor; } @@ -2001,10 +2137,7 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) { if (auto nom = dyn_cast(type)) { if (!isa(nom->getDecl()) && (!nom->getDecl()->isGenericContext() - || nom->getDecl()->getGenericSignature()->areAllParamsConcrete()) - && (!nom->getClassOrBoundGenericClass() - || !nom->getClassOrBoundGenericClass()->hasClangNode() - || nom->getClassOrBoundGenericClass()->isForeign())) { + || nom->getDecl()->getGenericSignature()->areAllParamsConcrete())) { return false; } } @@ -2149,16 +2282,29 @@ static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) { } +static bool canIssueIncompleteMetadataRequests(IRGenModule &IGM) { + // We can only answer blocking complete metadata requests with the <=5.1 + // runtime ABI entry points. + auto &context = IGM.getSwiftModule()->getASTContext(); + auto deploymentAvailability = + AvailabilityContext::forDeploymentTarget(context); + return deploymentAvailability.isContainedIn( + context.getTypesInAbstractMetadataStateAvailability()); +} + /// Emit a call to a type metadata accessor using a mangled name. static MetadataResponse emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, DynamicMetadataRequest request) { - // TODO: We can only answer blocking complete metadata requests with the - // <=5.1 runtime ABI entry points. - assert(request.isStaticallyBlockingComplete() - && "can only form complete metadata by mangled name"); - auto &IGM = IGF.IGM; + + // We can only answer blocking complete metadata requests with the <=5.1 + // runtime ABI entry points. + assert((request.isStaticallyBlockingComplete() || + (request.isStaticallyAbstract() && + canIssueIncompleteMetadataRequests(IGM))) && + "can only form complete metadata by mangled name"); + llvm::Constant *mangledString; unsigned mangledStringSize; std::tie(mangledString, mangledStringSize) = @@ -2191,11 +2337,15 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, } // Get or create a shared helper function to do the instantiation. + auto instantiationFnName = + request.isStaticallyAbstract() + ? "__swift_instantiateConcreteTypeFromMangledNameAbstract" + : "__swift_instantiateConcreteTypeFromMangledName"; auto instantiationFn = cast( - IGM.getModule() - ->getOrInsertFunction("__swift_instantiateConcreteTypeFromMangledName", - IGF.IGM.TypeMetadataPtrTy, cache->getType()) - .getCallee()); + IGM.getModule() + ->getOrInsertFunction(instantiationFnName, IGF.IGM.TypeMetadataPtrTy, + cache->getType()) + .getCallee()); if (instantiationFn->empty()) { ApplyIRLinkage(IRLinkage::InternalLinkOnceODR) .to(instantiationFn); @@ -2205,7 +2355,7 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, llvm::Attribute::NoInline); IGM.setHasFramePointer(instantiationFn, false); - [&IGM, instantiationFn]{ + [&IGM, instantiationFn, request]{ IRGenFunction subIGF(IGM, instantiationFn); auto params = subIGF.collectParameters(); @@ -2272,15 +2422,26 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, stringAddrOffset); stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); - - auto call = - subIGF.Builder.CreateCall(IGM.getGetTypeByMangledNameInContextFn(), - {stringAddr, - size, - // TODO: Use mangled name lookup in generic - // contexts? - llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), - llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); + + llvm::CallInst *call; + if (request.isStaticallyAbstract()) { + call = subIGF.Builder.CreateCall( + IGM.getGetTypeByMangledNameInContextInMetadataStateFn(), + {llvm::ConstantInt::get(IGM.SizeTy, (size_t)MetadataState::Abstract), + stringAddr, size, + // TODO: Use mangled name lookup in generic + // contexts? + llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), + llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); + } else { + call = subIGF.Builder.CreateCall( + IGM.getGetTypeByMangledNameInContextFn(), + {stringAddr, size, + // TODO: Use mangled name lookup in generic + // contexts? + llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), + llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); + } call->setDoesNotThrow(); call->setDoesNotAccessMemory(); call->setCallingConv(IGM.SwiftCC); @@ -2332,14 +2493,13 @@ emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF, CanType type, // single access by mangled name instead, if we're asking for complete // metadata. // - // TODO: The getTypeByMangledNameInContext entry point in Swift <=5.1 can - // only answer requests for complete metadata. We could introduce new - // entry points that could answer all metadata requests. - if (request.isStaticallyBlockingComplete() - && shouldAccessByMangledName(IGF.IGM, type)) { + if ((request.isStaticallyBlockingComplete() || + (request.isStaticallyAbstract() && + canIssueIncompleteMetadataRequests(IGF.IGM))) && + shouldAccessByMangledName(IGF.IGM, type)) { return emitMetadataAccessByMangledName(IGF, type, request); } - + llvm::Constant *accessor = getOrCreateTypeMetadataAccessFunction(IGF.IGM, type); llvm::CallInst *call = IGF.Builder.CreateCall(accessor, { request.get(IGF) }); diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index 681a62f80fb75..4c39a03e8199d 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -34,6 +34,7 @@ enum ForDefinition_t : bool; namespace irgen { class ConstantReference; class Explosion; +struct GenericArguments; class IRGenFunction; class IRGenModule; class MetadataDependencyCollector; @@ -372,6 +373,18 @@ class MetadataResponse { static llvm::Constant *getCompletedState(IRGenModule &IGM); }; +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const MetadataResponse &MR) { + if (!MR.isValid()) + return OS; + OS << MR.getMetadata(); + if (MR.hasDynamicState()) + OS << MR.getDynamicState(); + // FIXME + // OS << MR.getStaticLowerBoundOnState(); + return OS; +} + inline bool DynamicMetadataRequest::isSatisfiedBy(MetadataResponse response) const { return isSatisfiedBy(response.getStaticLowerBoundOnState()); @@ -491,6 +504,10 @@ static inline bool isAccessorLazilyGenerated(MetadataAccessStrategy strategy) { /// need a cache variable in its accessor. bool isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type); +bool isNominalGenericContextTypeMetadataAccessTrivial(IRGenModule &IGM, + NominalTypeDecl &nominal, + CanType type); + /// Determine how the given type metadata should be accessed. MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type); @@ -571,6 +588,10 @@ void emitCacheAccessFunction(IRGenModule &IGM, CacheStrategy cacheStrategy, CacheEmitter getValue, bool isReadNone = true); +MetadataResponse +emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, Explosion ¶ms, + NominalTypeDecl *nominal, + GenericArguments &genericArgs); /// Emit a declaration reference to a metatype object. void emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type, diff --git a/lib/IRGen/NominalMetadataVisitor.h b/lib/IRGen/NominalMetadataVisitor.h index 6ac633abf6d31..212d7c65640c9 100644 --- a/lib/IRGen/NominalMetadataVisitor.h +++ b/lib/IRGen/NominalMetadataVisitor.h @@ -53,9 +53,9 @@ template class NominalMetadataVisitor GenericTypeRequirements requirements(super::IGM, typeDecl); for (auto reqt : requirements.getRequirements()) { if (reqt.Protocol) { - asImpl().addGenericWitnessTable(args...); + asImpl().addGenericWitnessTable(reqt, args...); } else { - asImpl().addGenericArgument(args...); + asImpl().addGenericArgument(reqt, args...); } } diff --git a/lib/IRGen/Outlining.cpp b/lib/IRGen/Outlining.cpp index ecf67c6179643..b16dc87817c24 100644 --- a/lib/IRGen/Outlining.cpp +++ b/lib/IRGen/Outlining.cpp @@ -127,7 +127,7 @@ irgen::getTypeAndGenericSignatureForManglingOutlineFunction(SILType type) { }); assert(env && "has archetype but no archetype?!"); return {loweredType->mapTypeOutOfContext()->getCanonicalType(), - env->getGenericSignature()->getCanonicalSignature()}; + env->getGenericSignature().getCanonicalSignature()}; } return {loweredType, nullptr}; } diff --git a/lib/IRGen/StructLayout.cpp b/lib/IRGen/StructLayout.cpp index f19c7b268cf6b..d5de8bc1f1f3b 100644 --- a/lib/IRGen/StructLayout.cpp +++ b/lib/IRGen/StructLayout.cpp @@ -156,6 +156,7 @@ Address ElementLayout::project(IRGenFunction &IGF, Address baseAddr, const llvm::Twine &suffix) const { switch (getKind()) { case Kind::Empty: + case Kind::EmptyTailAllocatedCType: return getType().getUndefAddress(); case Kind::Fixed: diff --git a/lib/IRGen/StructLayout.h b/lib/IRGen/StructLayout.h index 1d3fc715d1610..d5abf45576be8 100644 --- a/lib/IRGen/StructLayout.h +++ b/lib/IRGen/StructLayout.h @@ -84,6 +84,11 @@ class ElementLayout { /// Its offset in the aggregate is always statically zero. Empty, + /// The element is known to require no storage in the aggregate. + /// But it has an offset in the aggregate. This is to support getting the + /// offset of tail allocated storage using MemoryLayout<>.offset(of:). + EmptyTailAllocatedCType, + /// The element can be positioned at a fixed offset within the /// aggregate. Fixed, @@ -163,6 +168,15 @@ class ElementLayout { assert(getByteOffset() == byteOffset); } + void completeEmptyTailAllocatedCType(IsPOD_t isPOD, Size byteOffset) { + TheKind = unsigned(Kind::EmptyTailAllocatedCType); + IsPOD = unsigned(isPOD); + ByteOffset = byteOffset.getValue(); + Index = 0; + + assert(getByteOffset() == byteOffset); + } + /// Complete this element layout with a non-fixed offset. /// /// \param nonFixedElementIndex - the index into the elements array @@ -181,7 +195,8 @@ class ElementLayout { /// Is this element known to be empty? bool isEmpty() const { - return getKind() == Kind::Empty; + return getKind() == Kind::Empty || + getKind() == Kind::EmptyTailAllocatedCType; } /// Is this element known to be POD? @@ -194,6 +209,7 @@ class ElementLayout { bool hasByteOffset() const { switch (getKind()) { case Kind::Empty: + case Kind::EmptyTailAllocatedCType: case Kind::Fixed: return true; diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 837ea00e5087e..147d2672bb162 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -18,6 +18,7 @@ #define SWIFT_IRGEN_STRUCTMETADATALAYOUT_H #include "NominalMetadataVisitor.h" +#include "swift/AST/IRGenOptions.h" namespace swift { namespace irgen { @@ -61,6 +62,9 @@ template class StructMetadataVisitor asImpl().addFieldOffset(prop); asImpl().noteEndOfFieldOffsets(); + + if (asImpl().hasTrailingFlags()) + asImpl().addTrailingFlags(); } // Note the start of the field offset vector. @@ -68,6 +72,11 @@ template class StructMetadataVisitor // Note the end of the field offset vector. void noteEndOfFieldOffsets() {} + + bool hasTrailingFlags() { + return Target->isGenericContext() && + IGM.shouldPrespecializeGenericMetadata(); + } }; /// An "implementation" of StructMetadataVisitor that just scans through @@ -87,14 +96,16 @@ class StructMetadataScanner : public StructMetadataVisitor { void addValueWitnessTable() { addPointer(); } void addNominalTypeDescriptor() { addPointer(); } void addFieldOffset(VarDecl *) { addInt32(); } - void addGenericArgument() { addPointer(); } - void addGenericWitnessTable() { addPointer(); } + void addGenericArgument(GenericRequirement requirement) { addPointer(); } + void addGenericWitnessTable(GenericRequirement requirement) { addPointer(); } void noteStartOfTypeSpecificMembers() {} void noteEndOfFieldOffsets() { NextOffset = NextOffset.roundUpToAlignment(super::IGM.getPointerAlignment()); } + void addTrailingFlags() { addPointer(); } + private: void addPointer() { NextOffset += super::IGM.getPointerSize(); diff --git a/lib/IRGen/SwiftTargetInfo.cpp b/lib/IRGen/SwiftTargetInfo.cpp index 972ff34f5eab1..a7b6f84bf06c0 100644 --- a/lib/IRGen/SwiftTargetInfo.cpp +++ b/lib/IRGen/SwiftTargetInfo.cpp @@ -134,6 +134,10 @@ static void configureSystemZ(IRGenModule &IGM, const llvm::Triple &triple, SwiftTargetInfo &target) { setToMask(target.PointerSpareBits, 64, SWIFT_ABI_S390X_SWIFT_SPARE_BITS_MASK); + setToMask(target.ObjCPointerReservedBits, 64, + SWIFT_ABI_S390X_OBJC_RESERVED_BITS_MASK); + setToMask(target.IsObjCPointerBit, 64, SWIFT_ABI_S390X_IS_OBJC_BIT); + target.SwiftRetainIgnoresNegativeValues = true; } /// Configure a default target. diff --git a/lib/IRGen/SwitchBuilder.h b/lib/IRGen/SwitchBuilder.h index 550098f1d6a19..2a90fd013adf7 100644 --- a/lib/IRGen/SwitchBuilder.h +++ b/lib/IRGen/SwitchBuilder.h @@ -165,10 +165,9 @@ class SwitchSwitchBuilder final : public SwitchBuilder { } }; -std::unique_ptr SwitchBuilder::create(IRGenFunction &IGF, - llvm::Value *Subject, - SwitchDefaultDest Default, - unsigned NumCases) { +inline std::unique_ptr +SwitchBuilder::create(IRGenFunction &IGF, llvm::Value *Subject, + SwitchDefaultDest Default, unsigned NumCases) { // Pick a builder based on how many total reachable destinations we intend // to have. switch (NumCases + (Default.getInt() == IsNotUnreachable)) { diff --git a/lib/IRGen/TypeLayoutDumper.cpp b/lib/IRGen/TypeLayoutDumper.cpp index 49b8e52f4aaed..614416956eb04 100644 --- a/lib/IRGen/TypeLayoutDumper.cpp +++ b/lib/IRGen/TypeLayoutDumper.cpp @@ -152,7 +152,7 @@ void TypeLayoutDumper::write(ArrayRef AllModules, } } -bool swift::performDumpTypeInfo(IRGenOptions &Opts, +bool swift::performDumpTypeInfo(const IRGenOptions &Opts, SILModule &SILMod, llvm::LLVMContext &LLVMContext) { auto &Ctx = SILMod.getASTContext(); diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index c9e27358976d7..195ef7e7cc00f 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -12,7 +12,9 @@ target_link_libraries(swiftImmediate PRIVATE swiftIRGen swiftSILGen swiftSILOptimizer) - -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swiftImmediate PRIVATE edit) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) + target_compile_definitions(swiftImmediate PRIVATE + HAVE_LIBEDIT) + target_link_libraries(swiftImmediate PRIVATE + libedit) endif() diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index b6554d9ea45a7..4037c9efabf8e 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -220,7 +220,7 @@ bool swift::immediate::linkLLVMModules(llvm::Module *Module, } bool swift::immediate::autolinkImportedModules(ModuleDecl *M, - IRGenOptions &IRGenOpts) { + const IRGenOptions &IRGenOpts) { // Perform autolinking. SmallVector AllLinkLibraries(IRGenOpts.LinkLibraries); auto addLinkLibrary = [&](LinkLibrary linkLib) { @@ -234,8 +234,11 @@ bool swift::immediate::autolinkImportedModules(ModuleDecl *M, return false; } -int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, - IRGenOptions &IRGenOpts, const SILOptions &SILOpts) { +int swift::RunImmediately(CompilerInstance &CI, + const ProcessCmdLine &CmdLine, + const IRGenOptions &IRGenOpts, + const SILOptions &SILOpts, + std::unique_ptr &&SM) { ASTContext &Context = CI.getASTContext(); // IRGen the main module. @@ -244,7 +247,7 @@ int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto ModuleOwner = performIRGeneration( - IRGenOpts, swiftModule, CI.takeSILModule(), swiftModule->getName().str(), + IRGenOpts, swiftModule, std::move(SM), swiftModule->getName().str(), PSPs, getGlobalLLVMContext(), ArrayRef()); auto *Module = ModuleOwner.get(); diff --git a/lib/Immediate/ImmediateImpl.h b/lib/Immediate/ImmediateImpl.h index a309385910bff..904e09691a4b2 100644 --- a/lib/Immediate/ImmediateImpl.h +++ b/lib/Immediate/ImmediateImpl.h @@ -44,7 +44,7 @@ bool tryLoadLibraries(ArrayRef LinkLibraries, DiagnosticEngine &Diags); bool linkLLVMModules(llvm::Module *Module, std::unique_ptr SubModule); -bool autolinkImportedModules(ModuleDecl *M, IRGenOptions &IRGenOpts); +bool autolinkImportedModules(ModuleDecl *M, const IRGenOptions &IRGenOpts); } // end namespace immediate } // end namespace swift diff --git a/lib/Immediate/REPL.cpp b/lib/Immediate/REPL.cpp index 5d55c9f633c25..67c26f1c7d2d3 100644 --- a/lib/Immediate/REPL.cpp +++ b/lib/Immediate/REPL.cpp @@ -20,6 +20,7 @@ #include "swift/AST/IRGenOptions.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/Basic/LLVMContext.h" #include "swift/Frontend/Frontend.h" #include "swift/IDE/REPLCodeCompletion.h" @@ -36,7 +37,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT #include #include #endif @@ -119,8 +120,8 @@ class ConvertForWcharSize<4> { }; using Convert = ConvertForWcharSize; - -#if HAVE_UNICODE_LIBEDIT + +#if HAVE_LIBEDIT static void convertFromUTF8(llvm::StringRef utf8, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + utf8.size(); @@ -134,7 +135,7 @@ static void convertFromUTF8(llvm::StringRef utf8, (void)res; out.set_size(wide_begin - out.begin()); } - + static void convertToUTF8(llvm::ArrayRef wide, llvm::SmallVectorImpl &out) { size_t reserve = out.size() + wide.size()*4; @@ -152,7 +153,7 @@ static void convertToUTF8(llvm::ArrayRef wide, } // end anonymous namespace -#if HAVE_UNICODE_LIBEDIT +#if HAVE_LIBEDIT static ModuleDecl * typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, @@ -186,15 +187,12 @@ typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name, REPLInputFile.addImports(ImportsWithOptions); } - bool FoundAnySideEffects = false; bool Done; do { - FoundAnySideEffects |= - parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, - &PersistentState); + parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr, + &PersistentState); } while (!Done); - performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext(), - /*Options*/None); + performTypeChecking(REPLInputFile); return REPLModule; } @@ -1092,8 +1090,11 @@ class REPLEnvironment { ASTContext &ctx = CI.getASTContext(); SourceFile &SF = MostRecentModule->getMainSourceFile(SourceFileKind::REPL); - UnqualifiedLookup lookup(ctx.getIdentifier(Tok.getText()), &SF); - for (auto result : lookup.Results) { + DeclNameRef name(ctx.getIdentifier(Tok.getText())); + auto descriptor = UnqualifiedLookupDescriptor(name, &SF); + auto lookup = evaluateOrDefault( + ctx.evaluator, UnqualifiedLookupRequest{descriptor}, {}); + for (auto result : lookup) { printOrDumpDecl(result.getValueDecl(), doPrint); if (auto typeDecl = dyn_cast(result.getValueDecl())) { @@ -1163,9 +1164,9 @@ class REPLEnvironment { if (Tok.getText() == "debug") { L.lex(Tok); if (Tok.getText() == "on") { - CI.getASTContext().LangOpts.DebugConstraintSolver = true; + CI.getASTContext().TypeCheckerOpts.DebugConstraintSolver = true; } else if (Tok.getText() == "off") { - CI.getASTContext().LangOpts.DebugConstraintSolver = false; + CI.getASTContext().TypeCheckerOpts.DebugConstraintSolver = false; } else { llvm::outs() << "Unknown :constraints debug command; try :help\n"; } diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 5d43692c2c748..ad7b9db5f296f 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -362,10 +362,10 @@ class IndexSwiftASTWalker : public SourceEntityWalker { } void handleMemberwiseInitRefs(Expr *E) { - if (!isa(E)) + if (!isa(E)) return; - auto *DeclRef = dyn_cast(cast(E)->getFn()); + auto *DeclRef = dyn_cast(cast(E)->getFn()); if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl())) return; @@ -837,7 +837,7 @@ bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) { if (!shouldIndex(D, /*IsRef=*/false)) return false; - SourceLoc Loc = D->getLoc(); + SourceLoc Loc = D->getLoc(/*SerializedOK*/false); if (Loc.isInvalid() && !IsModuleFile) return false; @@ -919,7 +919,7 @@ bool IndexSwiftASTWalker::reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet if (auto *T = dyn_cast_or_null(Ty.getTypeRepr())) { auto Comps = T->getComponentRange(); - SourceLoc IdLoc = Comps.back()->getIdLoc(); + SourceLoc IdLoc = Comps.back()->getLoc(); NominalTypeDecl *NTD = nullptr; bool isImplicit = false; if (auto *VD = Comps.back()->getBoundDecl()) { @@ -1186,7 +1186,7 @@ bool IndexSwiftASTWalker::reportImplicitConformance(ValueDecl *witness, ValueDec if (auto *extD = dyn_cast(container)) loc = getLocForExtension(extD); else - loc = container->getLoc(); + loc = container->getLoc(/*SerializedOK*/false); IndexSymbol info; if (initIndexSymbol(witness, loc, /*IsRef=*/true, info)) @@ -1208,6 +1208,8 @@ bool IndexSwiftASTWalker::reportImplicitConformance(ValueDecl *witness, ValueDec bool IndexSwiftASTWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc, bool IsRef, IndexSymbol &Info) { assert(D); + if (Loc.isValid() && SrcMgr.findBufferContainingLoc(Loc) != BufferID) + return true; if (auto *VD = dyn_cast(D)) { // Always base the symbol information on the canonical VarDecl D = VD->getCanonicalVarDecl(); @@ -1266,7 +1268,7 @@ static NominalTypeDecl *getNominalParent(ValueDecl *D) { bool IndexSwiftASTWalker::initFuncDeclIndexSymbol(FuncDecl *D, IndexSymbol &Info) { - if (initIndexSymbol(D, D->getLoc(), /*IsRef=*/false, Info)) + if (initIndexSymbol(D, D->getLoc(/*SerializedOK*/false), /*IsRef=*/false, Info)) return true; if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) { diff --git a/lib/Markup/LineList.cpp b/lib/Markup/LineList.cpp index 0afeb5eec995d..a89488b8c4213 100644 --- a/lib/Markup/LineList.cpp +++ b/lib/Markup/LineList.cpp @@ -115,8 +115,6 @@ LineList MarkupContext::getLineList(swift::RawComment RC) { // Determine if we have leading decorations in this block comment. bool HasASCIIArt = false; if (swift::startsWithNewline(Cleaned)) { - Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(), - C.Range.getStart() }); unsigned NewlineBytes = swift::measureNewline(Cleaned); Cleaned = Cleaned.drop_front(NewlineBytes); CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes); diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 38535f82610fc..0cc1870cbd9ff 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -231,21 +231,6 @@ class ChildIndexFinder : public TypeReprVisitor { } }; -static ValueDecl* getReferencedDecl(Expr *E) { - // Get the syntactic expression out of an implicit expression. - if (auto *ICE = dyn_cast(E)) - E = ICE->getSyntacticSubExpr(); - if (auto *DRE = dyn_cast(E)) { - return DRE->getDecl(); - } else if (auto *MRE = dyn_cast(E)) { - return MRE->getMember().getDecl(); - } else if (auto OtherCtorE = dyn_cast(E)) { - return OtherCtorE->getDecl(); - } else { - return nullptr; - } -} - struct ConversionFunctionInfo { Expr *ExpressionToWrap; SmallString<256> Buffer; @@ -582,15 +567,10 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } return false; }; - if (auto *DSC = dyn_cast(Call)) { - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - if (handleDecl(FD, Call->getSourceRange())) - return true; - } - } else if (auto MRE = dyn_cast(Call)) { - if (handleDecl(MRE->getReferencedDecl().getDecl(), MRE->getSourceRange())) + if (auto *VD = getReferencedDecl(Call).second.getDecl()) + if (handleDecl(VD, Call->getSourceRange())) return true; - } + return false; } @@ -858,11 +838,12 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Lexer::getLocForEndOfToken(SM, E->getEndLoc())), Text); } - bool wrapAttributeReference(Expr* Reference, Expr* WrapperTarget, + bool wrapAttributeReference(Expr *Reference, Expr *WrapperTarget, bool FromString) { - auto *RD = getReferencedDecl(Reference); + auto *RD = Reference->getReferencedDecl().getDecl(); if (!RD) return false; + std::string Rename; Optional Kind; StringRef LeftComment; @@ -1110,7 +1091,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { // reference of the property. bool handlePropertyTypeChange(Expr *E) { if (auto MRE = dyn_cast(E)) { - if (auto *VD = MRE->getReferencedDecl().getDecl()) { + if (auto *VD = MRE->getMember().getDecl()) { for (auto *I: getRelatedDiffItems(VD)) { if (auto *Item = dyn_cast(I)) { if (Item->DiffKind == NodeAnnotation::WrapOptional && @@ -1143,46 +1124,36 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { if (auto *CE = dyn_cast(E)) { auto Fn = CE->getFn(); auto Args = CE->getArg(); - switch (Fn->getKind()) { - case ExprKind::DeclRef: { - if (auto FD = Fn->getReferencedDecl().getDecl()) { - handleFuncRename(FD, Fn, Args); - handleTypeHoist(FD, CE, Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); - } - break; - } - case ExprKind::DotSyntaxCall: { - auto DSC = cast(Fn); - if (auto FD = DSC->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, DSC->getFn(), Args); - handleFunctionCallToPropertyChange(FD, DSC->getFn(), Args); - handleSpecialCases(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *DRE = dyn_cast(Fn)) { + if (auto *VD = DRE->getDecl()) { + if (VD->getNumCurryLevels() == 1) { + handleFuncRename(VD, Fn, Args); + handleTypeHoist(VD, CE, Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; } - case ExprKind::ConstructorRefCall: { - auto CCE = cast(Fn); - if (auto FD = CCE->getFn()->getReferencedDecl().getDecl()) { - handleFuncRename(FD, CE, Args); - handleStringRepresentableArg(FD, Args, CE); - handleResultTypeChange(FD, CE); + + if (auto *SelfApply = dyn_cast(Fn)) { + if (auto VD = SelfApply->getFn()->getReferencedDecl().getDecl()) { + if (VD->getNumCurryLevels() == 2) { + handleFuncRename(VD, SelfApply->getFn(), Args); + handleFunctionCallToPropertyChange(VD, SelfApply->getFn(), Args); + handleSpecialCases(VD, CE, Args); + handleStringRepresentableArg(VD, Args, CE); + handleResultTypeChange(VD, CE); + } } - break; - } - default: - break; } } return true; } - static void collectParamters(AbstractFunctionDecl *AFD, - SmallVectorImpl &Results) { + static void collectParameters(AbstractFunctionDecl *AFD, + SmallVectorImpl &Results) { for (auto PD : *AFD->getParameters()) { Results.push_back(PD); } @@ -1197,7 +1168,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor.replace(NameRange, View.base()); unsigned Index = 0; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); for (auto *PD: Params) { if (Index == View.argSize()) break; @@ -1322,7 +1293,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { return; Idx --; SmallVector Params; - collectParamters(AFD, Params); + collectParameters(AFD, Params); if (Params.size() <= Idx) return; @@ -1397,11 +1368,11 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { Editor(Editor), USRs(USRs) {} bool isSuperExpr(Expr *E) { if (E->isImplicit()) - return false; + return false; // Check if the expression is super.foo(). if (auto *CE = dyn_cast(E)) { if (auto *DSC = dyn_cast(CE->getFn())) { - if (DSC->getBase()->getKind() != ExprKind::SuperRef) + if (!isa(DSC->getBase())) return false; llvm::SmallString<64> Buffer; llvm::raw_svector_ostream OS(Buffer); @@ -1419,9 +1390,9 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { } std::pair walkToStmtPre(Stmt *S) override { if (auto *BS = dyn_cast(S)) { - for(auto Ele: BS->getElements()) { - if (Ele.is() && isSuperExpr(Ele.get())) { - Editor.remove(Ele.getSourceRange()); + for(auto Ele: BS->getElements()) { + if (Ele.is() && isSuperExpr(Ele.get())) { + Editor.remove(Ele.getSourceRange()); } } } diff --git a/include/swift/Basic/Diff.h b/lib/Migrator/Diff.h similarity index 99% rename from include/swift/Basic/Diff.h rename to lib/Migrator/Diff.h index 0e11601ec3e3f..ef933b04d999a 100644 --- a/include/swift/Basic/Diff.h +++ b/lib/Migrator/Diff.h @@ -1242,19 +1242,19 @@ class diff_match_patch { int count_insert = 0; string_t text_delete; string_t text_insert; - Diff *prevEqual = NULL; + Diff *prevEqual = nullptr; int commonlength; for (cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { switch ((*cur_diff).operation) { case INSERT: count_insert++; text_insert += (*cur_diff).text; - prevEqual = NULL; + prevEqual = nullptr; break; case DELETE: count_delete++; text_delete += (*cur_diff).text; - prevEqual = NULL; + prevEqual = nullptr; break; case EQUAL: if (count_delete + count_insert > 1) { @@ -1296,7 +1296,7 @@ class diff_match_patch { if (!text_insert.empty()) { diffs.insert(cur_diff, Diff(INSERT, text_insert)); } - } else if (prevEqual != NULL) { + } else if (prevEqual != nullptr) { // Merge this equality with the previous one. prevEqual->text += (*cur_diff).text; diffs.erase(cur_diff--); @@ -1672,7 +1672,7 @@ class diff_match_patch { int bin_min, bin_mid; int bin_max = pattern.length() + text.length(); int *rd; - int *last_rd = NULL; + int *last_rd = nullptr; for (int d = 0; d < (int)pattern.length(); d++) { // Scan for the best match; each iteration allows for one more error. // Run a binary search to determine how far from 'loc' we can stray at @@ -2571,7 +2571,7 @@ template <> struct diff_match_patch_traits : diff_match_patch_utf32_fro static bool is_alnum(wchar_t c) { return std::iswalnum(c)? true : false; } static bool is_digit(wchar_t c) { return std::iswdigit(c)? true : false; } static bool is_space(wchar_t c) { return std::iswspace(c)? true : false; } - static int to_int(const wchar_t* s) { return static_cast(std::wcstol(s, NULL, 10)); } + static int to_int(const wchar_t* s) { return static_cast(std::wcstol(s, nullptr, 10)); } static wchar_t from_wchar(wchar_t c) { return c; } static wchar_t to_wchar(wchar_t c) { return c; } static const wchar_t* cs(const wchar_t* s) { return s; } diff --git a/lib/Migrator/FixitApplyDiagnosticConsumer.cpp b/lib/Migrator/FixitApplyDiagnosticConsumer.cpp index 5e040ec4a5bcf..896ee28fa452c 100644 --- a/lib/Migrator/FixitApplyDiagnosticConsumer.cpp +++ b/lib/Migrator/FixitApplyDiagnosticConsumer.cpp @@ -32,20 +32,17 @@ void FixitApplyDiagnosticConsumer::printResult(llvm::raw_ostream &OS) const { } void FixitApplyDiagnosticConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { - if (Loc.isInvalid()) { + SourceManager &SM, const DiagnosticInfo &Info) { + if (Info.Loc.isInvalid()) { return; } - auto ThisBufferID = SM.findBufferContainingLoc(Loc); + auto ThisBufferID = SM.findBufferContainingLoc(Info.Loc); auto ThisBufferName = SM.getIdentifierForBuffer(ThisBufferID); if (ThisBufferName != BufferName) { return; } - if (!shouldTakeFixit(Kind, Info)) { + if (!shouldTakeFixit(Info)) { return; } diff --git a/lib/Migrator/Migrator.cpp b/lib/Migrator/Migrator.cpp index d3242896354ba..d2a7dfd10c34d 100644 --- a/lib/Migrator/Migrator.cpp +++ b/lib/Migrator/Migrator.cpp @@ -9,7 +9,7 @@ // //===----------------------------------------------------------------------===// -#include "swift/Basic/Diff.h" +#include "Diff.h" #include "swift/Frontend/Frontend.h" #include "swift/Migrator/ASTMigratorPass.h" #include "swift/Migrator/EditorAdapter.h" diff --git a/lib/Migrator/README.md b/lib/Migrator/README.md index baf7827dc5360..6ea9b9e29bb75 100644 --- a/lib/Migrator/README.md +++ b/lib/Migrator/README.md @@ -227,7 +227,7 @@ There are two main other pieces of the Migrator's implementation, *diffing* and For diffing, we pulled in an STL port of Google's *diff-match-patch* library to perform the final diff of the start and end `MigrationState`'s text. This is a fairly standard implementation of the Myers Difference Algorithm (see *An O(ND) Difference Algorithm and Its Variations* by Eugene W. Myers). -> See include/swift/Basic/Diff.h +> See Diff.h ### Editing diff --git a/lib/Option/SanitizerOptions.cpp b/lib/Option/SanitizerOptions.cpp index 781427f004677..ed32373d63b1d 100644 --- a/lib/Option/SanitizerOptions.cpp +++ b/lib/Option/SanitizerOptions.cpp @@ -182,9 +182,82 @@ OptionSet swift::parseSanitizerArgValues( + toStringRef(SanitizerKind::Thread)).toStringRef(b2)); } + // Scudo can only be run with ubsan. + if (sanitizerSet & SanitizerKind::Scudo) { + OptionSet allowedSet; + allowedSet |= SanitizerKind::Scudo; + allowedSet |= SanitizerKind::Undefined; + + auto forbiddenOptions = sanitizerSet - allowedSet; + + if (forbiddenOptions) { + SanitizerKind forbidden; + + if (forbiddenOptions & SanitizerKind::Address) { + forbidden = SanitizerKind::Address; + } else if (forbiddenOptions & SanitizerKind::Thread) { + forbidden = SanitizerKind::Thread; + } else { + assert(forbiddenOptions & SanitizerKind::Fuzzer); + forbidden = SanitizerKind::Fuzzer; + } + + SmallString<128> b1; + SmallString<128> b2; + Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with, + (A->getOption().getPrefixedName() + + toStringRef(SanitizerKind::Scudo)).toStringRef(b1), + (A->getOption().getPrefixedName() + + toStringRef(forbidden)).toStringRef(b2)); + } + } + return sanitizerSet; } +OptionSet swift::parseSanitizerRecoverArgValues( + const llvm::opt::Arg *A, const OptionSet &enabledSanitizers, + DiagnosticEngine &Diags, bool emitWarnings) { + OptionSet sanitizerRecoverSet; + + // Find the sanitizer kind. + for (const char *arg : A->getValues()) { + Optional optKind = parse(arg); + + // Unrecognized sanitizer option + if (!optKind.hasValue()) { + Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument, + A->getOption().getPrefixedName(), arg); + continue; + } + SanitizerKind kind = optKind.getValue(); + + // Only support ASan for now. + if (kind != SanitizerKind::Address) { + Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument, + A->getOption().getPrefixedName(), arg); + continue; + } + + // Check that the sanitizer is enabled. + if (!(enabledSanitizers & kind)) { + SmallString<128> b; + if (emitWarnings) { + Diags.diagnose(SourceLoc(), + diag::warning_option_requires_specific_sanitizer, + (A->getOption().getPrefixedName() + toStringRef(kind)) + .toStringRef(b), + toStringRef(kind)); + } + continue; + } + + sanitizerRecoverSet |= kind; + } + + return sanitizerRecoverSet; +} + std::string swift::getSanitizerList(const OptionSet &Set) { std::string list; #define SANITIZER(_, kind, name, file) \ diff --git a/lib/Parse/ASTGen.cpp b/lib/Parse/ASTGen.cpp deleted file mode 100644 index 79b07e2368cdb..0000000000000 --- a/lib/Parse/ASTGen.cpp +++ /dev/null @@ -1,1728 +0,0 @@ -//===--- ASTGen.cpp -------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "swift/Parse/ASTGen.h" -#include "swift/AST/TypeRepr.h" -#include "swift/Basic/SourceManager.h" - -#include "DebuggerContextChange.h" -#include "swift/Basic/SourceManager.h" -#include "swift/Parse/CodeCompletionCallbacks.h" -#include "swift/Parse/Parser.h" -#include "swift/Parse/Scope.h" - -using namespace swift; -using namespace swift::syntax; - -SourceLoc ASTGen::generate(const TokenSyntax &Tok, const SourceLoc Loc) { - return advanceLocBegin(Loc, Tok); -} - -SourceLoc ASTGen::generateIdentifierDeclName(const syntax::TokenSyntax &Tok, - const SourceLoc Loc, - Identifier &Id) { - StringRef text; - if (Tok.getText() == "Any") - // Special handle 'Any' because we don't want to accidantaly declare 'Any' - // type in any way. - text = "#Any"; - else - text = Tok.getIdentifierText(); - - Id = Context.getIdentifier(text); - return advanceLocBegin(Loc, Tok); -} - -Decl *ASTGen::generate(const DeclSyntax &D, const SourceLoc Loc) { - Decl *DeclAST = nullptr; - - if (auto associatedTypeDecl = D.getAs()) { - DeclAST = generate(*associatedTypeDecl, Loc); - } else if (auto typealiasDecl = D.getAs()) { - DeclAST = generate(*typealiasDecl, Loc); - } else { - llvm_unreachable("unsupported decl kind"); - } - - return DeclAST; -} - -DeclAttributes -ASTGen::generateDeclAttributes(const Syntax &D, SourceLoc Loc, - bool includeComments) { - // Find the AST attribute-list from the lookup table. - if (auto firstTok = D.getFirstToken()) { - auto declLoc = advanceLocBegin(Loc, *firstTok); - if (hasDeclAttributes(declLoc)) - return takeDeclAttributes(declLoc); - } - return DeclAttributes(); -} - -MutableArrayRef -ASTGen::generate(const TypeInheritanceClauseSyntax &clause, SourceLoc Loc, - bool allowClassRequirement) { - SmallVector inherited; - - bool hasClass = false; - for (const auto elem : clause.getInheritedTypeCollection()) { - const auto &tySyntax = elem.getTypeName(); - if (tySyntax.is()) { - // Accept 'class' only if it's allowed and it's the first one. - if (!allowClassRequirement || hasClass) - continue; - hasClass = true; - } - if (auto ty = generate(tySyntax, Loc)) - inherited.emplace_back(ty); - } - - return Context.AllocateCopy(inherited); -} - -TypeDecl *ASTGen::generate(const AssociatedtypeDeclSyntax &D, - const SourceLoc Loc) { - if (!isa(P.CurDeclContext)) { - // This is already diagnosed in Parser. - return nullptr; - } - - auto idToken = D.getIdentifier(); - if (idToken.isMissing()) - return nullptr; - - auto keywordLoc = advanceLocBegin(Loc, D.getAssociatedtypeKeyword()); - Identifier name; - SourceLoc nameLoc = generateIdentifierDeclName(idToken, Loc, name); - - DeclAttributes attrs = generateDeclAttributes(D, Loc, true); - - DebuggerContextChange DCC(P, name, DeclKind::AssociatedType); - - ArrayRef inherited; - if (const auto inheritanceClause = D.getInheritanceClause()) - inherited = - generate(*inheritanceClause, Loc, /*allowClassRequirement=*/true); - - TypeRepr *defaultTy = nullptr; - if (const auto init = D.getInitializer()) - defaultTy = generate(init->getValue(), Loc); - - TrailingWhereClause *trailingWhere = nullptr; - if (auto whereClause = D.getGenericWhereClause()) - trailingWhere = generate(*whereClause, Loc); - - auto assocType = new (Context) - AssociatedTypeDecl(P.CurDeclContext, keywordLoc, name, nameLoc, defaultTy, - trailingWhere); - assocType->getAttrs() = attrs; - if (!inherited.empty()) - assocType->setInherited(Context.AllocateCopy(inherited)); - addToScope(assocType); - return assocType; -} - -TypeDecl *ASTGen::generate(const TypealiasDeclSyntax &D, const SourceLoc Loc) { - auto idToken = D.getIdentifier(); - if (idToken.isMissing()) - return nullptr; - - auto keywordLoc = advanceLocBegin(Loc, D.getTypealiasKeyword()); - Identifier name; - SourceLoc nameLoc = generateIdentifierDeclName(idToken, Loc, name); - auto attrs = generateDeclAttributes(D, Loc, true); - SourceLoc equalLoc; - - DebuggerContextChange DCC(P, name, DeclKind::TypeAlias); - - Optional GenericScope; - GenericParamList *genericParams = nullptr; - GenericScope.emplace(&P, ScopeKind::Generics); - if (auto clause = D.getGenericParameterClause()) - genericParams = generate(*clause, Loc); - - auto *TAD = new (Context) TypeAliasDecl(keywordLoc, equalLoc, name, nameLoc, - genericParams, P.CurDeclContext); - P.setLocalDiscriminator(TAD); - TAD->getAttrs() = attrs; - - TypeRepr *underlyingType = nullptr; - SourceLoc typeEndLoc; - if (auto init = D.getInitializer()) { - Parser::ContextChange CC(P, TAD); - equalLoc = generate(init->getEqual(), Loc); - underlyingType = generate(init->getValue(), Loc); - if (auto lastToken = init->getLastToken()) - typeEndLoc = generate(*lastToken, Loc); - } - TAD->setUnderlyingTypeRepr(underlyingType); - - SourceLoc whereLoc; - if (auto clause = D.getGenericWhereClause()) { - whereLoc = advanceLocBegin(Loc, clause->getWhereKeyword()); - Parser::ContextChange CC(P, TAD); - generateFreeStandingGenericWhereClause(*clause, Loc, genericParams); - } - P.diagnoseWhereClauseInGenericParamList(genericParams, whereLoc); - - if (equalLoc.isInvalid()) - return nullptr; - - GenericScope.reset(); - - addToScope(TAD); - return DCC.fixupParserResult(TAD).getPtrOrNull(); -} - -void ASTGen::generateFreeStandingGenericWhereClause( - const syntax::GenericWhereClauseSyntax &syntax, const SourceLoc Loc, - GenericParamList *genericParams) { - - SourceLoc whereLoc = generate(syntax.getWhereKeyword(), Loc); - - if (!genericParams) { - P.diagnose(whereLoc, diag::where_without_generic_params, - unsigned(Parser::WhereClauseKind::Declaration)); - return; - } - - // Push the generic parameters back into a local scope so that references - // will find them. - Scope S(&P, ScopeKind::Generics); - for (auto pd : genericParams->getParams()) - addToScope(pd); - - SmallVector requirements; - requirements.reserve(syntax.getRequirementList().size()); - for (auto elem : syntax.getRequirementList()) { - if (auto req = generate(elem, Loc)) - requirements.push_back(*req); - } - if (requirements.empty()) - return; - - genericParams->addTrailingWhereClause(Context, whereLoc, requirements); -} - -TrailingWhereClause *ASTGen::generate(const GenericWhereClauseSyntax &syntax, - const SourceLoc Loc) { - SourceLoc whereLoc = advanceLocBegin(Loc, syntax.getWhereKeyword()); - - SmallVector requirements; - requirements.reserve(syntax.getRequirementList().size()); - for (auto elem : syntax.getRequirementList()) { - if (auto req = generate(elem, Loc)) - requirements.push_back(*req); - } - - if (requirements.empty()) - return nullptr; - return TrailingWhereClause::create(Context, whereLoc, requirements); -} - -Expr *ASTGen::generate(const ExprSyntax &E, const SourceLoc Loc) { - Expr *result = nullptr; - - auto exprLoc = advanceLocBegin(Loc, E); - if (hasExpr(exprLoc)) - return takeExpr(exprLoc); - - if (auto identifierExpr = E.getAs()) - result = generate(*identifierExpr, Loc); - else if (auto superRefExpr = E.getAs()) - result = generate(*superRefExpr, Loc); - else if (auto specializeExpr = E.getAs()) - result = generate(*specializeExpr, Loc); - else if (auto editorPlaceHolderExpr = E.getAs()) - result = generate(*editorPlaceHolderExpr, Loc); - else if (auto arrayExpr = E.getAs()) - result = generate(*arrayExpr, Loc); - else if (auto dictionaryExpr = E.getAs()) - result = generate(*dictionaryExpr, Loc); - else if (auto tupleExpr = E.getAs()) - result = generate(*tupleExpr, Loc); - else if (auto callExpr = E.getAs()) - result = generate(*callExpr, Loc); - else if (auto memberExpr = E.getAs()) - result = generate(*memberExpr, Loc); - else if (auto integerLiteralExpr = E.getAs()) - result = generate(*integerLiteralExpr, Loc); - else if (auto floatLiteralExpr = E.getAs()) - result = generate(*floatLiteralExpr, Loc); - else if (auto nilLiteral = E.getAs()) - result = generate(*nilLiteral, Loc); - else if (auto boolLiteral = E.getAs()) - result = generate(*boolLiteral, Loc); - else if (auto poundFileExpr = E.getAs()) - result = generate(*poundFileExpr, Loc); - else if (auto poundLineExpr = E.getAs()) - result = generate(*poundLineExpr, Loc); - else if (auto poundColumnExpr = E.getAs()) - result = generate(*poundColumnExpr, Loc); - else if (auto poundFunctionExpr = E.getAs()) - result = generate(*poundFunctionExpr, Loc); - else if (auto poundDsohandleExpr = E.getAs()) - result = generate(*poundDsohandleExpr, Loc); - else if (auto objcKeyPathExpr = E.getAs()) - result = generate(*objcKeyPathExpr, Loc); - else if (auto objectLiteralExpr = E.getAs()) - result = generate(*objectLiteralExpr, Loc); - else if (auto completionExpr = E.getAs()) - result = generate(*completionExpr, Loc); - else if (auto unknownExpr = E.getAs()) - result = generate(*unknownExpr, Loc); - else { -#ifndef NDEBUG - E.dump(); - llvm_unreachable("unsupported expression"); -#endif - } - - return result; -} - -std::pair ASTGen::generateUnqualifiedDeclName( - const TokenSyntax &idTok, const Optional &args, - const SourceLoc Loc) { - SourceLoc baseNameLoc = advanceLocBegin(Loc, idTok); - - DeclBaseName baseName; - if (idTok.getTokenKind() == tok::kw_init) - baseName = DeclBaseName::createConstructor(); - else if (idTok.getTokenKind() == tok::kw_deinit) - baseName = DeclBaseName::createDestructor(); - else if (idTok.getTokenKind() == tok::kw_subscript) - baseName = DeclBaseName::createSubscript(); - else - baseName = Context.getIdentifier(idTok.getIdentifierText()); - - if (!args) - return {DeclName(baseName), DeclNameLoc(baseNameLoc)}; - - // FIXME: Remove this block and use 'Loc'. - // This is needed for the case 'idTok' and 'args' are not in the same tree. - // i.e. Call from parseUnqualifiedDeclName(). - SourceLoc argsLeadingLoc = Loc; - if (!args->getParent()) { - argsLeadingLoc = Loc.getAdvancedLoc(idTok.getTextLength()); - } else { - assert(idTok.getData().getParent() == args->getData().getParent() && - idTok.getIndexInParent() + 1 == args->getIndexInParent() && - "'idTok' must be immediately followed by 'args'"); - } - - SmallVector argumentLabels; - SmallVector argumentLabelLocs; - for (auto arg : args->getArguments()) { - Identifier label; - if (!arg.getName().isMissing() && - arg.getName().getTokenKind() != tok::kw__) { - label = Context.getIdentifier(arg.getName().getIdentifierText()); - } - argumentLabels.push_back(label); - argumentLabelLocs.push_back(advanceLocBegin(argsLeadingLoc, - *arg.getFirstToken())); - } - SourceLoc lParenLoc = advanceLocBegin(argsLeadingLoc, args->getLeftParen()); - SourceLoc rParenLoc = advanceLocBegin(argsLeadingLoc, args->getRightParen()); - - DeclName name(Context, baseName, argumentLabels); - DeclNameLoc nameLoc; - if (argumentLabelLocs.empty()) - nameLoc = DeclNameLoc(baseNameLoc); - else - nameLoc = DeclNameLoc(Context, baseNameLoc, lParenLoc, argumentLabelLocs, - rParenLoc); - return {name, nameLoc}; -} - -Expr *ASTGen::generate(const IdentifierExprSyntax &E, const SourceLoc Loc) { - auto idTok = E.getIdentifier(); - DeclName name; - DeclNameLoc nameLoc; - std::tie(name, nameLoc) = generateUnqualifiedDeclName( - E.getIdentifier(), E.getDeclNameArguments(), Loc); - - ValueDecl *D = nullptr; - if (!P.InPoundIfEnvironment) { - D = lookupInScope(name); - // FIXME: We want this to work: "var x = { x() }", but for now it's better - // to disallow it than to crash. - if (D) { - for (auto activeVar : P.DisabledVars) { - if (activeVar != D) - continue; - P.diagnose(nameLoc.getBaseNameLoc(), P.DisabledVarReason); - return new (Context) ErrorExpr(nameLoc.getSourceRange()); - } - } else { - for (auto activeVar : P.DisabledVars) { - if (activeVar->getFullName() != name) - continue; - P.diagnose(nameLoc.getBaseNameLoc(), P.DisabledVarReason); - return new (Context) ErrorExpr(nameLoc.getSourceRange()); - } - } - } - - if (!D) { - return new (Context) - UnresolvedDeclRefExpr(name, DeclRefKind::Ordinary, nameLoc); - } - - if (auto TD = dyn_cast(D)) { - // When parsing default argument expressions for generic functions, - // we haven't built a FuncDecl or re-parented the GenericTypeParamDecls - // to the FuncDecl yet. Other than that, we should only ever find - // global or local declarations here. - assert(!TD->getDeclContext()->isTypeContext() || - isa(TD)); - return TypeExpr::createForDecl(nameLoc.getBaseNameLoc(), TD, - /*DeclContext=*/nullptr, - /*inplicit=*/false); - } - - return new (Context) DeclRefExpr(D, nameLoc, /*implicit=*/false); -} - -static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P, - DeclContext *DC, - SourceLoc Loc) { - auto *methodContext = DC->getInnermostMethodContext(); - if (!methodContext) { - P.diagnose(Loc, diag::super_not_in_class_method); - return nullptr; - } - - // Do an actual lookup for 'self' in case it shows up in a capture list. - auto *methodSelf = methodContext->getImplicitSelfDecl(); - auto *lookupSelf = P.lookupInScope(P.Context.Id_self); - if (lookupSelf && lookupSelf != methodSelf) { - // FIXME: This is the wrong diagnostic for if someone manually declares a - // variable named 'self' using backticks. - P.diagnose(Loc, diag::super_in_closure_with_capture); - P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here); - return nullptr; - } - - return methodSelf; -} - -Expr *ASTGen::generate(const SuperRefExprSyntax &E, const SourceLoc Loc) { - auto superLoc = advanceLocBegin(Loc, E.getSuperKeyword()); - VarDecl *selfDecl = - getImplicitSelfDeclForSuperContext(P, P.CurDeclContext, superLoc); - if (!selfDecl) - return new (Context) ErrorExpr(superLoc); - - return new (Context) SuperRefExpr(selfDecl, superLoc, /*Implicit=*/false); -} - -Expr *ASTGen::generate(const EditorPlaceholderExprSyntax &E, const SourceLoc Loc) { - assert(!E.getIdentifier().isMissing()); - - auto text = E.getIdentifier().getText(); - auto tokLoc = advanceLocBegin(Loc, E.getIdentifier()); - return P.parseExprEditorPlaceholder(tokLoc, text); -} - -Expr *ASTGen::generate(const SpecializeExprSyntax &E, const SourceLoc Loc) { - auto base = generate(E.getExpression(), Loc); - - SourceLoc lAngleLoc, rAngleLoc; - SmallVector argTyRs; - generate(E.getGenericArgumentClause(), Loc, lAngleLoc, rAngleLoc, argTyRs); - if (argTyRs.empty()) - return base; - - SmallVector args; - args.assign(argTyRs.begin(), argTyRs.end()); - return UnresolvedSpecializeExpr::create(Context, base, lAngleLoc, args, - rAngleLoc); -} - -/// validateCollectionElement - Check if a given collection element is valid. -/// -/// At the moment, this checks whether a given collection element is a subscript -/// expression and whether we're subscripting into an array. If we are, then it -/// we emit a diagnostic in case it was not something that the user was -/// expecting. -/// -/// For example: `let array [ [0, 1] [42] ]` -void ASTGen::validateCollectionElement(Expr *elementExpr) { - if (!elementExpr) - return; - - if (!isa(elementExpr)) - return; - - auto subscriptExpr = cast(elementExpr); - if (!isa(subscriptExpr->getBase())) - return; - - auto arrayExpr = cast(subscriptExpr->getBase()); - - auto startLocOfSubscript = subscriptExpr->getIndex()->getStartLoc(); - auto endLocOfArray = arrayExpr->getEndLoc(); - - auto locForEndOfTokenArray = - Lexer::getLocForEndOfToken(Context.SourceMgr, endLocOfArray); - - if (locForEndOfTokenArray != startLocOfSubscript) { - auto subscriptLoc = subscriptExpr->getLoc(); - P.diagnose(subscriptLoc, diag::subscript_array_element) - .highlight(subscriptExpr->getSourceRange()); - P.diagnose(subscriptLoc, diag::subscript_array_element_fix_it_add_comma) - .fixItInsertAfter(endLocOfArray, ","); - P.diagnose(subscriptLoc, diag::subscript_array_element_fix_it_remove_space) - .fixItRemoveChars(locForEndOfTokenArray, startLocOfSubscript); - } -} - -Expr *ASTGen::generate(const ArrayExprSyntax &E, const SourceLoc Loc) { - SmallVector elements; - SmallVector commaLocs; - elements.reserve(E.getElements().size()); - for (auto elemSyntax : E.getElements()) { - if (auto elemAST = generate(elemSyntax.getExpression(), Loc)) { - validateCollectionElement(elemAST); - elements.push_back(elemAST); - } - if (auto comma = elemSyntax.getTrailingComma()) - commaLocs.push_back(advanceLocBegin(Loc, *comma)); - } - - // Don't bother to create expression if any expressions aren't parsed. - if (elements.empty() && !E.getElements().empty()) - return nullptr; - - auto LSquareLoc = advanceLocBegin(Loc, E); - auto RSquareLoc = advanceLocEnd(Loc, E); - return ArrayExpr::create(Context, LSquareLoc, elements, commaLocs, - RSquareLoc); -} - -Expr *ASTGen::generate(const DictionaryExprSyntax &E, const SourceLoc Loc) { - SmallVector elements; - SmallVector commaLocs; - if (auto contents = E.getContent().getAs()) { - elements.reserve(contents->size()); - for (auto elemSyntax : *contents) { - if (auto key = generate(elemSyntax.getKeyExpression(), Loc)) { - auto val = generate(elemSyntax.getValueExpression(), Loc); - if (!val) - val = new (Context) ErrorExpr(advanceLocEnd(Loc, elemSyntax)); - auto elemAST = TupleExpr::createImplicit(Context, {key, val}, {}); - elements.push_back(elemAST); - } - if (auto comma = elemSyntax.getTrailingComma()) - commaLocs.push_back(advanceLocBegin(Loc, *comma)); - } - // Don't bother to create expression if any expressions aren't parsed. - if (elements.empty() && !contents->empty()) - return nullptr; - } - - auto LSquareLoc = advanceLocBegin(Loc, E); - auto RSquareLoc = advanceLocEnd(Loc, E); - return DictionaryExpr::create(Context, LSquareLoc, elements, commaLocs, - RSquareLoc); -} - -Expr *ASTGen::generate(const TupleExprSyntax &E, const SourceLoc Loc) { - SmallVector exprs; - SmallVector exprLabels; - SmallVector exprLabelLocs; - generateExprTupleElementList(E.getElementList(), Loc, - /*isForCallArguments=*/false, exprs, exprLabels, - exprLabelLocs); - - SourceLoc leftLoc = advanceLocBegin(Loc, E.getLeftParen()); - SourceLoc rightLoc = advanceLocEnd(Loc, E); - - // A tuple with a single, unlabeled element is just parentheses. - if (exprs.size() == 1 && exprLabels.empty()) { - return new (Context) ParenExpr(leftLoc, exprs[0], rightLoc, - /*hasTrailingClosure=*/false); - } - - return TupleExpr::create(Context, leftLoc, exprs, exprLabels, exprLabelLocs, - rightLoc, /*HasTrailingClosure=*/false, - /*Implicit=*/false); -} - -void ASTGen::generateExprTupleElementList(const TupleExprElementListSyntax &elements, - const SourceLoc Loc, bool isForCallArguments, - SmallVectorImpl &exprs, - SmallVectorImpl &exprLabels, - SmallVectorImpl &exprLabelLocs) { - auto isFirst = true; - for (auto elem : elements) { - auto *subExpr = generate(elem.getExpression(), Loc); - if (!subExpr) - continue; - - // Handle call arguments specially because it may need argument labels. - if (P.CodeCompletion && isForCallArguments && !elem.getLabel()) - if (auto CCExpr = elem.getExpression().getAs()) - if (!CCExpr->getBase() && !CCExpr->getPeriodOrParen()) - P.CodeCompletion->completeCallArg(cast(subExpr), - isFirst); - isFirst = false; - - Identifier fieldName; - SourceLoc fieldNameLoc; - if (auto label = elem.getLabel()) { - fieldNameLoc = advanceLocBegin(Loc, *label); - if (label->getTokenKind() == tok::identifier) - fieldName = Context.getIdentifier(label->getIdentifierText()); - } - - // Don't populate label vectors unless we see at least one label. - if (!exprLabels.empty()) { - exprLabels.push_back(fieldName); - exprLabelLocs.push_back(fieldNameLoc); - } else if (fieldNameLoc.isValid()) { - exprLabels.resize(exprs.size()); - exprLabelLocs.resize(exprs.size()); - exprLabels.push_back(fieldName); - exprLabelLocs.push_back(fieldNameLoc); - } - exprs.push_back(subExpr); - } - assert((exprLabels.size() == 0 || exprs.size() == exprLabels.size()) && - exprLabels.size() == exprLabelLocs.size()); -} - -Expr *ASTGen::generate(const FunctionCallExprSyntax &E, const SourceLoc Loc) { - auto callee = E.getCalledExpression(); - - SourceLoc LParenLoc, RParenLoc; - SmallVector args; - SmallVector argLabels; - SmallVector argLabelLocs; - generateExprTupleElementList(E.getArgumentList(), Loc, - /*isForCallArguments=*/true, args, argLabels, - argLabelLocs); - Expr *trailingClosure = nullptr; - if (auto CE = E.getTrailingClosure()) - trailingClosure = generate(*CE, Loc); - if (auto LParen = E.getLeftParen()) { - LParenLoc = advanceLocBegin(Loc, *LParen); - if (auto RParen = E.getRightParen()) - RParenLoc = advanceLocBegin(Loc, *RParen); - else - RParenLoc = advanceLocEnd(Loc, E.getArgumentList()); - } - - if (auto memberAccess = callee.getAs()) { - if (!memberAccess->getBase()) { - // This is UnresolvedMemberExpr with call arguments. - if (memberAccess->getName().isMissing()) - return nullptr; - - SourceLoc dotLoc = advanceLocBegin(Loc, memberAccess->getDot()); - DeclName name; - DeclNameLoc nameLoc; - std::tie(name, nameLoc) = generateUnqualifiedDeclName( - memberAccess->getName(), memberAccess->getDeclNameArguments(), Loc); - - return UnresolvedMemberExpr::create( - Context, dotLoc, nameLoc, name, LParenLoc, args, argLabels, - argLabelLocs, RParenLoc, trailingClosure, - /*implicit=*/false); - } - } - llvm_unreachable("call expression not implemented"); - return nullptr; -} - -Expr *ASTGen::generate(const MemberAccessExprSyntax &E, const SourceLoc Loc) { - if (!E.getBase()) { - // This is an UnresolvedMemberExpr. - if (E.getName().isMissing()) - return nullptr; - - DeclName name; - DeclNameLoc nameLoc; - std::tie(name, nameLoc) = - generateUnqualifiedDeclName(E.getName(), E.getDeclNameArguments(), Loc); - SourceLoc dotLoc = advanceLocBegin(Loc, E.getDot()); - - return UnresolvedMemberExpr::create(Context, dotLoc, nameLoc, name, - /*implicit=*/false); - } - llvm_unreachable("member access expression not implemented"); - return nullptr; -} - -Expr *ASTGen::generate(const IntegerLiteralExprSyntax &Expr, - const SourceLoc Loc) { - auto Digits = Expr.getDigits(); - auto Text = copyAndStripUnderscores(Digits.getText()); - auto DigitsLoc = advanceLocBegin(Loc, Digits); - return new (Context) IntegerLiteralExpr(Text, DigitsLoc); -} - -Expr *ASTGen::generate(const FloatLiteralExprSyntax &Expr, - const SourceLoc Loc) { - auto Digits = Expr.getFloatingDigits(); - auto Text = copyAndStripUnderscores(Digits.getText()); - auto DigitsLoc = advanceLocBegin(Loc, Digits); - return new (Context) FloatLiteralExpr(Text, DigitsLoc); -} - -Expr *ASTGen::generate(const NilLiteralExprSyntax &Expr, const SourceLoc Loc) { - auto Nil = Expr.getNilKeyword(); - auto NilLoc = advanceLocBegin(Loc, Nil); - return new (Context) NilLiteralExpr(NilLoc); -} - -Expr *ASTGen::generate(const BooleanLiteralExprSyntax &Expr, - const SourceLoc Loc) { - auto Boolean = Expr.getBooleanLiteral(); - auto Value = Boolean.getTokenKind() == tok::kw_true; - auto BooleanLoc = advanceLocBegin(Loc, Boolean); - return new (Context) BooleanLiteralExpr(Value, BooleanLoc); -} - -Expr *ASTGen::generate(const PoundFileExprSyntax &Expr, const SourceLoc Loc) { - return generateMagicIdentifierLiteralExpression(Expr.getPoundFile(), Loc); -} - -Expr *ASTGen::generate(const PoundLineExprSyntax &Expr, const SourceLoc Loc) { - return generateMagicIdentifierLiteralExpression(Expr.getPoundLine(), Loc); -} - -Expr *ASTGen::generate(const PoundColumnExprSyntax &Expr, const SourceLoc Loc) { - return generateMagicIdentifierLiteralExpression(Expr.getPoundColumn(), Loc); -} - -Expr *ASTGen::generate(const PoundFunctionExprSyntax &Expr, - const SourceLoc Loc) { - return generateMagicIdentifierLiteralExpression(Expr.getPoundFunction(), Loc); -} - -Expr *ASTGen::generate(const PoundDsohandleExprSyntax &Expr, - const SourceLoc Loc) { - return generateMagicIdentifierLiteralExpression(Expr.getPoundDsohandle(), - Loc); -} - -Expr *ASTGen::generate(const ObjcKeyPathExprSyntax &E, const SourceLoc Loc) { - SmallVector components; - if (E.getLeftParen().isMissing()) - return nullptr; - - for (auto piece : E.getName()) { - DeclName name; - DeclNameLoc nameLoc; - std::tie(name, nameLoc) = - generateUnqualifiedDeclName(piece.getName(), - piece.getDeclNameArguments(), Loc); - auto component = KeyPathExpr::Component::forUnresolvedProperty(name, - nameLoc.getBaseNameLoc()); - components.push_back(component); - } - auto keywordLoc = advanceLocBegin(Loc, E.getKeyPath()); - auto LParenLoc = advanceLocBegin(Loc, E.getLeftParen()); - auto RParenLoc = advanceLocEnd(Loc, E); - - if (components.empty()) - return new (Context) ErrorExpr(SourceRange(keywordLoc, RParenLoc)); - - return new (Context) KeyPathExpr( - Context, keywordLoc, LParenLoc, components, RParenLoc); -} - -Expr *ASTGen::generate(const ObjectLiteralExprSyntax &E, const SourceLoc Loc) { - ObjectLiteralExpr::LiteralKind kind; - switch (E.getIdentifier().getTokenKind()) { -#define POUND_OBJECT_LITERAL(Name, Desc, Proto) \ - case tok::pound_##Name: \ - kind = ObjectLiteralExpr::Name; \ - break; -#include "swift/Syntax/TokenKinds.def" - default: - llvm_unreachable("unknown token kind for object literal expression"); - } - - SmallVector args; - SmallVector argLabels; - SmallVector argLabelLocs; - generateExprTupleElementList(E.getArguments(), Loc, true, args, argLabels, - argLabelLocs); - - ClosureExpr *trailingClosure = nullptr; - if (auto CE = E.getTrailingClosure()) - trailingClosure = dyn_cast_or_null(generate(*CE, Loc)); - - if (E.getLeftParen().isMissing() || E.getRightParen().isMissing()) - return nullptr; - - SourceLoc poundLoc = advanceLocBegin(Loc, E.getIdentifier()); - SourceLoc LParenLoc = advanceLocBegin(Loc, E.getLeftParen()); - SourceLoc RParenLoc = advanceLocBegin(Loc, E.getRightParen()); - - return ObjectLiteralExpr::create(Context, poundLoc, kind, LParenLoc, args, - argLabels, argLabelLocs, RParenLoc, - trailingClosure, /*implicit=*/false); -} - -Expr *ASTGen::generate(const CodeCompletionExprSyntax &E, const SourceLoc Loc) { - if (!E.getBase()) { - if (auto punctuator = E.getPeriodOrParen()) { - // '.' - if (punctuator->getTokenKind() == tok::period || - punctuator->getTokenKind() == tok::period_prefix) { - auto ccLoc = advanceLocBegin(Loc, E.getCodeCompletionToken()); - auto dotLoc = advanceLocBegin(Loc, *punctuator); - - auto CCE = new (Context) CodeCompletionExpr(ccLoc); - if (P.CodeCompletion) - P.CodeCompletion->completeUnresolvedMember(CCE, dotLoc); - return CCE; - } - } else { - llvm_unreachable("'(' is not suppported"); - } - } else { - if (auto objcKeyPathExpr = E.getBase()->getAs()) { - // #keyPath( - // #keyPath(some - // #keyPath(some. - auto expr = generate(*objcKeyPathExpr, Loc); - if (P.CodeCompletion) { - SourceLoc dotLoc; - if (!expr || isa(expr)) { - P.CodeCompletion->completeExprKeyPath(nullptr, SourceLoc()); - } else { - auto namePieces = objcKeyPathExpr->getName(); - if (!namePieces.empty()) - if (auto dot = namePieces[namePieces.getNumChildren() - 1].getDot()) - dotLoc = advanceLocBegin(Loc, *dot); - P.CodeCompletion->completeExprKeyPath(cast(expr), dotLoc); - } - } - return expr; - } - // TODO: implement - } - llvm_unreachable("code completion expression not implemented"); - return nullptr; -} - -Expr *ASTGen::generate(const UnknownExprSyntax &Expr, const SourceLoc Loc) { - if (Expr.getNumChildren() == 1 && Expr.getChild(0)->isToken()) { - Syntax Token = *Expr.getChild(0); - tok Kind = Token.getRaw()->getTokenKind(); - switch (Kind) { - case tok::kw___FILE__: - case tok::kw___LINE__: - case tok::kw___COLUMN__: - case tok::kw___FUNCTION__: - case tok::kw___DSO_HANDLE__: { - auto MagicKind = getMagicIdentifierLiteralKind(Kind); - auto KindLoc = advanceLocBegin(Loc, Token); - return new (Context) MagicIdentifierLiteralExpr(MagicKind, KindLoc); - } - default: - return nullptr; - } - } - return nullptr; -} - -TypeRepr *ASTGen::generate(const TypeSyntax &Type, const SourceLoc Loc, - bool IsSILFuncDecl) { - TypeRepr *TypeAST = nullptr; - - if (auto SimpleIdentifier = Type.getAs()) - TypeAST = generate(*SimpleIdentifier, Loc); - else if (auto MemberIdentifier = Type.getAs()) - TypeAST = generate(*MemberIdentifier, Loc); - else if (auto Composition = Type.getAs()) - TypeAST = generate(*Composition, Loc); - else if (auto Function = Type.getAs()) - TypeAST = generate(*Function, Loc); - else if (auto Metatype = Type.getAs()) - TypeAST = generate(*Metatype, Loc); - else if (auto Array = Type.getAs()) - TypeAST = generate(*Array, Loc); - else if (auto Dictionary = Type.getAs()) - TypeAST = generate(*Dictionary, Loc); - else if (auto Tuple = Type.getAs()) - TypeAST = generate(*Tuple, Loc); - else if (auto Some = Type.getAs()) - TypeAST = generate(*Some, Loc); - else if (auto Optional = Type.getAs()) - TypeAST = generate(*Optional, Loc); - else if (auto Unwrapped = Type.getAs()) - TypeAST = generate(*Unwrapped, Loc); - else if (auto Attributed = Type.getAs()) - TypeAST = generate(*Attributed, Loc); - else if (auto ClassRestriction = Type.getAs()) - TypeAST = generate(*ClassRestriction, Loc); - else if (auto SILBoxType = Type.getAs()) - TypeAST = generate(*SILBoxType, Loc, IsSILFuncDecl); - else if (auto SILFunctionType = Type.getAs()) - TypeAST = generate(*SILFunctionType, Loc, IsSILFuncDecl); - else if (auto CompletionTy = Type.getAs()) - TypeAST = generate(*CompletionTy, Loc); - else if (auto Unknown = Type.getAs()) - TypeAST = generate(*Unknown, Loc); - - return cacheType(Type, TypeAST); -} - -TypeRepr *ASTGen::generate(const FunctionTypeSyntax &Type, - const SourceLoc Loc) { - TupleTypeRepr *ArgumentTypes = nullptr; - - SourceLoc VoidLoc; - if (Type.getLeftParen().isMissing() && Type.getArguments().size() == 1) { - if (auto ident = Type.getArguments()[0] - .getType() - .getAs()) { - if (!ident->getGenericArgumentClause().hasValue() && - ident->getName().getText() == "Void") - VoidLoc = advanceLocBegin(Loc, ident->getName()); - } - } - - if (VoidLoc.isValid()) - ArgumentTypes = TupleTypeRepr::createEmpty(Context, VoidLoc); - else { - ArgumentTypes = generateTuple(Type.getLeftParen(), Type.getArguments(), - Type.getRightParen(), Loc, - /*IsFunction=*/true); - } - if (!ArgumentTypes) - return nullptr; - - auto ThrowsLoc = Type.getThrowsOrRethrowsKeyword() - ? generate(*Type.getThrowsOrRethrowsKeyword(), Loc) - : SourceLoc(); - - auto ArrowLoc = generate(Type.getArrow(), Loc); - auto ReturnType = generate(Type.getReturnType(), Loc); - if (!ReturnType) - return nullptr; - - return new (Context) - FunctionTypeRepr(nullptr, ArgumentTypes, ThrowsLoc, ArrowLoc, ReturnType); -} - -TupleTypeRepr *ASTGen::generateTuple(const TokenSyntax &LParen, - const TupleTypeElementListSyntax &Elements, - const TokenSyntax &RParen, - const SourceLoc Loc, bool IsFunction) { - auto LPLoc = advanceLocBegin(Loc, LParen); - auto RPLoc = advanceLocEnd(Loc, RParen); - - SmallVector TupleElements; - - SourceLoc EllipsisLoc; - unsigned EllipsisIdx; - - for (unsigned i = 0; i < Elements.size(); i++) { - auto Element = Elements[i]; - TupleTypeReprElement ElementAST; - ElementAST.Type = generate(Element.getType(), Loc); - if (!ElementAST.Type) - continue; - - if (auto Name = Element.getName()) { - ElementAST.NameLoc = generate(*Name, Loc); - ElementAST.Name = Name->getText() == "_" - ? Identifier() - : Context.getIdentifier(Name->getIdentifierText()); - } - if (auto Colon = Element.getColon()) - ElementAST.ColonLoc = generate(*Colon, Loc); - if (auto SecondName = Element.getSecondName()) { - ElementAST.SecondNameLoc = generate(*SecondName, Loc); - ElementAST.SecondName = - SecondName->getText() == "_" - ? Identifier() - : Context.getIdentifier(SecondName->getIdentifierText()); - if (IsFunction) { - // Form the named parameter type representation. - ElementAST.UnderscoreLoc = ElementAST.NameLoc; - ElementAST.Name = ElementAST.SecondName; - ElementAST.NameLoc = ElementAST.SecondNameLoc; - } - } - - if (auto InOut = Element.getInOut()) { - // don't apply multiple inout specifiers to a type: that's invalid and was - // already reported in the parser, handle gracefully - if (!isa(ElementAST.Type)) { - auto InOutLoc = generate(*InOut, Loc); - ElementAST.Type = - new (Context) InOutTypeRepr(ElementAST.Type, InOutLoc); - } - } - if (auto Comma = Element.getTrailingComma()) - ElementAST.TrailingCommaLoc = generate(*Comma, Loc); - - if (auto Ellipsis = Element.getEllipsis()) { - if (EllipsisLoc.isInvalid()) { - EllipsisLoc = generate(*Ellipsis, Loc); - EllipsisIdx = i; - } - } - TupleElements.push_back(ElementAST); - } - if (EllipsisLoc.isInvalid()) - EllipsisIdx = TupleElements.size(); - - return TupleTypeRepr::create(Context, TupleElements, {LPLoc, RPLoc}, - EllipsisLoc, EllipsisIdx); -} - -TypeAttributes ASTGen::generateTypeAttributes(const AttributeListSyntax &syntax, - const SourceLoc Loc) { - TypeAttributes attrs; - - for (auto elem : syntax) { - auto attrSyntax = elem.castTo(); - if (attrSyntax.getAttributeName().isMissing()) - continue; - - auto attrName = attrSyntax.getAttributeName().getText(); - - auto atLoc = advanceLocBegin(Loc, attrSyntax.getAtSignToken()); - if (attrs.AtLoc.isInvalid()) - attrs.AtLoc = atLoc; - - auto attr = TypeAttributes::getAttrKindFromString(attrName); - if (attr == TAK_Count) - continue; - - if (attrs.has(attr)) { - P.diagnose(atLoc, diag::duplicate_attribute, /*isModifier=*/false); - continue; - } - - auto arg = attrSyntax.getArgument(); - - if (attr == TAK_sil_weak || attr == TAK_sil_unowned) { - if (attrs.hasOwnership()) { - P.diagnose(atLoc, diag::duplicate_attribute, /*isModifier*/false); - } - } else if (attr == TAK_convention) { - // @convention(block) - // @convention(witness_method: ProtocolName) - if (!arg) - continue; - - if (auto conventionNameTok = arg->getAs()) { - assert(conventionNameTok->getTokenKind() == tok::identifier); - auto convention = - Context.getIdentifier(conventionNameTok->getIdentifierText()); - attrs.convention = convention.str(); - } else if (auto witness = - arg->getAs()) { - assert(witness->getNameTok().getIdentifierText() == "witness_method"); - if (witness->getStringOrDeclname().isMissing()) - continue; - auto protocolName = - witness->getStringOrDeclname().castTo(); - auto protocol = Context.getIdentifier( - protocolName.getDeclBaseName().getIdentifierText()); - attrs.convention = "witness_method"; - attrs.conventionWitnessMethodProtocol = protocol.str(); - } else { - continue; - } - } else if (attr == TAK_opened) { - // @opened("01234567-89ab-cdef-0123-111111111111") - if (!arg) - continue; - - assert(arg->castTo().getTokenKind() == - tok::string_literal); - auto tokText = arg->castTo().getText(); - auto literalText = tokText.slice(1, tokText.size() - 1); - attrs.OpenedID = UUID::fromString(literalText.str().c_str()); - } else if (attr == TAK__opaqueReturnTypeOf) { - // @_opaqueReturnTypeOf("$sMangledName", 0) - if (!arg) - continue; - - auto opaqueArg = - arg->castTo(); - - auto manglingTok = opaqueArg.getMangledName(); - auto indexTok = opaqueArg.getIndex(); - if (manglingTok.isMissing() || indexTok.isMissing()) - continue; - - auto tokText = manglingTok.getText(); - auto mangling = - Context.getIdentifier(tokText.slice(1, tokText.size() - 1)); - unsigned index; - if (indexTok.getText().getAsInteger(10, index)) - continue; - attrs.setOpaqueReturnTypeOf(mangling.str(), index); - } - - attrs.setAttr(attr, atLoc); - } - - return attrs; -} - -TypeRepr *ASTGen::generate(const AttributedTypeSyntax &Type, - const SourceLoc Loc) { - auto TypeAST = generate(Type.getBaseType(), Loc); - if (!TypeAST) - return nullptr; - - if (auto Attributes = Type.getAttributes()) { - TypeAttributes attrs = generateTypeAttributes(*Attributes, Loc); - if (!attrs.empty()) - TypeAST = new (Context) AttributedTypeRepr(attrs, TypeAST); - } - - if (auto Specifier = Type.getSpecifier()) { - auto SpecifierLoc = generate(*Specifier, Loc); - auto SpecifierText = Specifier->getText(); - - // don't apply multiple specifiers to a type: that's invalid and was already - // reported in the parser, handle gracefully - if (!isa(TypeAST)) { - if (SpecifierText == "inout") - TypeAST = new (Context) InOutTypeRepr(TypeAST, SpecifierLoc); - else if (SpecifierText == "__owned") - TypeAST = new (Context) OwnedTypeRepr(TypeAST, SpecifierLoc); - else if (SpecifierText == "__shared") - TypeAST = new (Context) SharedTypeRepr(TypeAST, SpecifierLoc); - } - } - - return TypeAST; -} - -TypeRepr *ASTGen::generate(const TupleTypeSyntax &Type, const SourceLoc Loc) { - return generateTuple(Type.getLeftParen(), Type.getElements(), - Type.getRightParen(), Loc); -} - -TypeRepr *ASTGen::generate(const SomeTypeSyntax &Type, const SourceLoc Loc) { - auto Some = Type.getSomeSpecifier(); - auto SomeLoc = generate(Some, Loc); - auto BaseType = generate(Type.getBaseType(), Loc); - return new (Context) OpaqueReturnTypeRepr(SomeLoc, BaseType); -} - -TypeRepr *ASTGen::generate(const CompositionTypeSyntax &Type, - const SourceLoc Loc) { - auto Elements = Type.getElements(); - auto FirstElem = Elements[0]; - auto LastElem = Elements[Elements.size() - 1]; - - SmallVector ElemTypes; - for (unsigned i = 0; i < Elements.size(); i++) { - auto ElemType = Elements[i].getType(); - - TypeRepr *ElemTypeR = nullptr; - if (auto Some = ElemType.getAs()) { - // the invalid `some` after an ampersand was already diagnosed by the - // parser, handle it gracefully - ElemTypeR = generate(Some->getBaseType(), Loc); - } else { - ElemTypeR = generate(ElemType, Loc); - } - - if (ElemTypeR) { - if (auto Comp = dyn_cast(ElemTypeR)) { - // Accept protocol & P3; explode it. - auto TyRs = Comp->getTypes(); - ElemTypes.append(TyRs.begin(), TyRs.end()); - } else { - ElemTypes.push_back(ElemTypeR); - } - } - } - - auto FirstTypeLoc = advanceLocBegin(Loc, FirstElem); - auto FirstAmpersandLoc = advanceLocBegin(Loc, *FirstElem.getAmpersand()); - auto LastTypeLoc = advanceLocBegin(Loc, *LastElem.getLastToken()); - return CompositionTypeRepr::create(Context, ElemTypes, FirstTypeLoc, - {FirstAmpersandLoc, LastTypeLoc}); -} - -void ASTGen::gatherTypeIdentifierComponents( - const TypeSyntax &Component, const SourceLoc Loc, - SmallVectorImpl &Components) { - if (auto SimpleIdentifier = Component.getAs()) { - auto ComponentType = generateIdentifier(*SimpleIdentifier, Loc); - Components.push_back(ComponentType); - return; - } - - if (auto MemberIdentifier = Component.getAs()) { - auto ComponentType = generateIdentifier(*MemberIdentifier, Loc); - Components.push_back(ComponentType); - gatherTypeIdentifierComponents(MemberIdentifier->getBaseType(), Loc, - Components); - return; - } - - llvm_unreachable("unexpected type identifier component"); -} - -template -TypeRepr *ASTGen::generateSimpleOrMemberIdentifier(const T &Type, - const SourceLoc Loc) { - SmallVector Components; - gatherTypeIdentifierComponents(Type, Loc, Components); - std::reverse(Components.begin(), Components.end()); - - auto IdentType = IdentTypeRepr::create(Context, Components); - auto FirstComponent = IdentType->getComponentRange().front(); - // Lookup element #0 through our current scope chains in case it is some - // thing local (this returns null if nothing is found). - if (auto Entry = lookupInScope(FirstComponent->getIdentifier())) { - if (auto *TD = dyn_cast(Entry)) - FirstComponent->setValue(TD, nullptr); - } - - return IdentType; -} - -template -ComponentIdentTypeRepr *ASTGen::generateIdentifier(const T &Type, - const SourceLoc Loc) { - auto IdentifierLoc = advanceLocBegin(Loc, Type.getName()); - auto Identifier = Context.getIdentifier(Type.getName().getIdentifierText()); - if (auto Clause = Type.getGenericArgumentClause()) { - SourceLoc lAngleLoc, rAngleLoc; - SmallVector args; - generate(*Clause, Loc, lAngleLoc, rAngleLoc, args); - if (!args.empty()) - return GenericIdentTypeRepr::create(Context, IdentifierLoc, Identifier, - args, {lAngleLoc, rAngleLoc}); - } - return new (Context) SimpleIdentTypeRepr(IdentifierLoc, Identifier); -} - -TypeRepr *ASTGen::generate(const SimpleTypeIdentifierSyntax &Type, - const SourceLoc Loc) { - if (Type.getName().getTokenKind() == tok::kw_Any) { - auto AnyLoc = advanceLocBegin(Loc, Type.getName()); - return CompositionTypeRepr::createEmptyComposition(Context, AnyLoc); - } - - return generateSimpleOrMemberIdentifier(Type, Loc); -} - -TypeRepr *ASTGen::generate(const MemberTypeIdentifierSyntax &Type, - SourceLoc Loc) { - return generateSimpleOrMemberIdentifier(Type, Loc); -} - -TypeRepr *ASTGen::generate(const DictionaryTypeSyntax &Type, - const SourceLoc Loc) { - TypeRepr *ValueType = generate(Type.getValueType(), Loc); - TypeRepr *KeyType = generate(Type.getKeyType(), Loc); - if (!ValueType || !KeyType) - return nullptr; - auto ColonLoc = advanceLocBegin(Loc, Type.getColon()); - - SourceLoc LBracketLoc, RBracketLoc; - LBracketLoc = advanceLocBegin(Loc, Type); - RBracketLoc = advanceLocEnd(Loc, Type); - SourceRange Range{LBracketLoc, RBracketLoc}; - return new (Context) DictionaryTypeRepr(KeyType, ValueType, ColonLoc, Range); -} - -TypeRepr *ASTGen::generate(const ArrayTypeSyntax &Type, SourceLoc Loc) { - TypeRepr *ElementType = generate(Type.getElementType(), Loc); - if (!ElementType) - return nullptr; - SourceLoc LBracketLoc, RBracketLoc; - LBracketLoc = advanceLocBegin(Loc, Type); - RBracketLoc = advanceLocEnd(Loc, Type); - return new (Context) ArrayTypeRepr(ElementType, {LBracketLoc, RBracketLoc}); -} - -TypeRepr *ASTGen::generate(const MetatypeTypeSyntax &Type, - const SourceLoc Loc) { - TypeRepr *BaseType = generate(Type.getBaseType(), Loc); - auto TypeOrProtocol = Type.getTypeOrProtocol(); - auto TypeOrProtocolLoc = advanceLocBegin(Loc, TypeOrProtocol); - if (TypeOrProtocol.getText() == "Type") - return new (Context) MetatypeTypeRepr(BaseType, TypeOrProtocolLoc); - return new (Context) ProtocolTypeRepr(BaseType, TypeOrProtocolLoc); -} - -TypeRepr *ASTGen::generate(const OptionalTypeSyntax &Type, - const SourceLoc Loc) { - TypeRepr *WrappedType = generate(Type.getWrappedType(), Loc); - auto QuestionLoc = advanceLocBegin(Loc, Type.getQuestionMark()); - return new (Context) OptionalTypeRepr(WrappedType, QuestionLoc); -} - -TypeRepr *ASTGen::generate(const ImplicitlyUnwrappedOptionalTypeSyntax &Type, - const SourceLoc Loc) { - TypeRepr *WrappedType = generate(Type.getWrappedType(), Loc); - auto ExclamationLoc = advanceLocBegin(Loc, Type.getExclamationMark()); - return new (Context) - ImplicitlyUnwrappedOptionalTypeRepr(WrappedType, ExclamationLoc); -} - -TypeRepr * -ASTGen::generate(const ClassRestrictionTypeSyntax &Type, const SourceLoc Loc) { - auto classLoc = advanceLocBegin(Loc, Type); - return new (Context) - SimpleIdentTypeRepr(classLoc, Context.getIdentifier("AnyObject")); -} - -TypeRepr *ASTGen::generate(const SILBoxTypeSyntax &Type, const SourceLoc Loc, - bool IsSILFuncDecl) { - if (Type.getRightBrace().isMissing()) - return nullptr; - - // If this is part of a sil function decl, generic parameters are visible in - // the function body; otherwise, they are visible when parsing the type. - Optional GenericsScope; - if (!IsSILFuncDecl) - GenericsScope.emplace(&P, ScopeKind::Generics); - - GenericParamList *generics = nullptr; - if (auto genericParamsSyntax = Type.getGenericParameterClauses()) - generics = generate(*genericParamsSyntax, Loc); - - SmallVector Fields; - for (auto field : Type.getFields()) { - auto specifier = field.getSpecifier(); - if (specifier.isMissing()) - return nullptr; - bool Mutable; - if (specifier.getTokenKind() == tok::kw_let) - Mutable = false; - else if (specifier.getTokenKind() == tok::kw_var) - Mutable = true; - else - return nullptr; - SourceLoc VarOrLetLoc = advanceLocBegin(Loc, specifier);; - auto fieldTy = generate(field.getType(), Loc); - if (!fieldTy) - return nullptr; - - Fields.emplace_back(VarOrLetLoc, Mutable, fieldTy); - } - GenericsScope.reset(); - - auto LBraceLoc = advanceLocBegin(Loc, Type.getLeftBrace()); - auto RBraceLoc = advanceLocBegin(Loc, Type.getRightBrace()); - - SourceLoc LAngleLoc, RAngleLoc; - SmallVector Args; - if (auto genericArgs = Type.getGenericArgumentClause()) { - if (genericArgs->getRightAngleBracket().isMissing()) - return nullptr; - generate(*genericArgs, Loc, LAngleLoc, RAngleLoc, Args); - } - - auto SILType = SILBoxTypeRepr::create(Context, generics, LBraceLoc, Fields, - RBraceLoc, LAngleLoc, Args, RAngleLoc); - return SILType; -} - -TypeRepr *ASTGen::generate(const SILFunctionTypeSyntax &Type, - const SourceLoc Loc, bool IsSILFuncDecl) { - // If this is part of a sil function decl, generic parameters are visible in - // the function body; otherwise, they are visible when parsing the type. - Optional GenericsScope; - if (!IsSILFuncDecl) - GenericsScope.emplace(&P, ScopeKind::Generics); - - GenericParamList *generics = nullptr; - if (auto genericParamsSyntax = Type.getGenericParameterClauses()) - generics = generate(*genericParamsSyntax, Loc); - - auto tyR = cast(generate(Type.getFunction(), Loc)); - return new (Context) - FunctionTypeRepr(generics, tyR->getArgsTypeRepr(), tyR->getThrowsLoc(), - tyR->getArrowLoc(), tyR->getResultTypeRepr()); -} - -TypeRepr *ASTGen::generate(const CodeCompletionTypeSyntax &Type, - const SourceLoc Loc) { - auto base = Type.getBase(); - if (!base) { - if (P.CodeCompletion) - P.CodeCompletion->completeTypeSimpleBeginning(); - return nullptr; - } - - if (P.CodeCompletion) { - if (auto *parsedTyR = generate(*base, Loc)) { - P.CodeCompletion->setParsedTypeLoc(parsedTyR); - if (Type.getPeriod()) - P.CodeCompletion->completeTypeIdentifierWithDot(); - else - P.CodeCompletion->completeTypeIdentifierWithoutDot(); - } - } - - // Return nullptr to typecheck this TypeRepr independently in code completion. - return nullptr; -} - -TypeRepr *ASTGen::generate(const UnknownTypeSyntax &Type, const SourceLoc Loc) { - auto ChildrenCount = Type.getNumChildren(); - - // Recover from old-style protocol composition: - // `protocol` `<` protocols `>` - if (ChildrenCount >= 2) { - auto keyword = Type.getChild(0)->getAs(); - - if (keyword && keyword->getText() == "protocol") { - auto keywordLoc = advanceLocBegin(Loc, *keyword); - auto LAngle = Type.getChild(1); - auto RAngle = Type.getChild(ChildrenCount - 1); - - auto LAngleLoc = advanceLocBegin(Loc, *LAngle); - auto RAngleLoc = advanceLocBegin(Loc, *RAngle); - - SmallVector protocols; - for (unsigned i = 2; i < Type.getNumChildren(); i++) { - if (auto elem = Type.getChild(i)->getAs()) - if (auto proto = generate(*elem, Loc)) - protocols.push_back(proto); - } - - return CompositionTypeRepr::create(Context, protocols, keywordLoc, - {LAngleLoc, RAngleLoc}); - } - } - - // Create ErrorTypeRepr for keywords. - if (ChildrenCount == 1) { - auto Keyword = Type.getChild(0)->getAs(); - if (Keyword && isTokenKeyword(Keyword->getTokenKind())) { - auto ErrorLoc = generate(*Keyword, Loc); - return new (Context) ErrorTypeRepr(ErrorLoc); - } - } - - // generate child 'TypeSyntax' anyway to trigger the side effects e.g. - // code-completion. - for (size_t i = 0; i != ChildrenCount; ++i) { - auto elem = *Type.getChild(i); - if (auto ty = elem.getAs()) - (void)generate(*ty, Loc); - } - - // let's hope the main `generate` method can find this node in the type map - return nullptr; -} - -void -ASTGen::generate(const GenericArgumentClauseSyntax &clause, const SourceLoc Loc, - SourceLoc &lAngleLoc, SourceLoc &rAngleLoc, - SmallVectorImpl &args) { - lAngleLoc = advanceLocBegin(Loc, clause); - rAngleLoc = advanceLocEnd(Loc, clause); - - assert(args.empty()); - for (auto Arg : clause.getArguments()) { - auto tyR = generate(Arg.getArgumentType(), Loc); - if (!tyR) - tyR = new (Context) ErrorTypeRepr(advanceLocBegin(Loc, Arg)); - args.push_back(tyR); - } -} - -StringRef ASTGen::copyAndStripUnderscores(StringRef Orig, ASTContext &Context) { - char *start = static_cast(Context.Allocate(Orig.size(), 1)); - char *p = start; - - if (p) { - for (char c : Orig) { - if (c != '_') { - *p++ = c; - } - } - } - - return StringRef(start, p - start); -} - -GenericParamList * -ASTGen::generate(const GenericParameterClauseListSyntax &clauses, - const SourceLoc Loc) { - GenericParamList *curr = nullptr; - - // The first one is the outmost generic parameter list. - for (const auto &clause : clauses) { - auto params = generate(clause, Loc); - if (!params) - continue; - params->setOuterParameters(curr); - curr = params; - } - - return curr; -} - -GenericParamList *ASTGen::generate(const GenericParameterClauseSyntax &clause, - const SourceLoc Loc) { - SmallVector params; - params.reserve(clause.getGenericParameterList().getNumChildren()); - - for (auto elem : clause.getGenericParameterList()) { - auto nameTok = elem.getName(); - if (nameTok.isMissing()) - break; - - DeclAttributes attrs = generateDeclAttributes(elem, Loc, false); - Identifier name = Context.getIdentifier(elem.getName().getIdentifierText()); - SourceLoc nameLoc = advanceLocBegin(Loc, elem.getName()); - - // We always create generic type parameters with an invalid depth. - // Semantic analysis fills in the depth when it processes the generic - // parameter list. - auto param = new (Context) - GenericTypeParamDecl(P.CurDeclContext, name, nameLoc, - GenericTypeParamDecl::InvalidDepth, params.size()); - - if (auto inherited = elem.getInheritedType()) { - if (auto ty = generate(*inherited, Loc)) { - SmallVector constraints = {ty}; - param->setInherited(Context.AllocateCopy(constraints)); - } - } - - // Attach attributes. - param->getAttrs() = attrs; - - // Add this parameter to the scope. - addToScope(param); - - params.push_back(param); - } - if (params.empty()) - return nullptr; - - SourceLoc whereLoc; - SmallVector requirements; - if (auto whereClause = clause.getObsoletedWhereClause()) { - requirements.reserve(whereClause->getRequirementList().size()); - for (auto elem : whereClause->getRequirementList()) { - if (auto req = generate(elem, Loc)) - requirements.push_back(*req); - } - // There's an invariant that valid 'where' loc means that there's at - // at least one valid requirement. - if (!requirements.empty()) - whereLoc = advanceLocBegin(Loc, whereClause->getWhereKeyword()); - } - - auto lAngleLoc = advanceLocBegin(Loc, clause); - auto rAngleLoc = advanceLocEnd(Loc, clause); - return GenericParamList::create(Context, lAngleLoc, params, whereLoc, - requirements, rAngleLoc); -} - -Optional -ASTGen::generate(const syntax::GenericRequirementSyntax &req, - const SourceLoc Loc) { - if (auto sameTypeReq = req.getBody().getAs()) { - auto firstType = generate(sameTypeReq->getLeftTypeIdentifier(), Loc); - auto secondType = generate(sameTypeReq->getRightTypeIdentifier(), Loc); - if (!firstType || !secondType) - return None; - return RequirementRepr::getSameType( - firstType, advanceLocBegin(Loc, sameTypeReq->getEqualityToken()), - secondType); - } else if (auto conformanceReq = - req.getBody().getAs()) { - auto firstType = generate(conformanceReq->getLeftTypeIdentifier(), Loc); - auto secondType = generate(conformanceReq->getRightTypeIdentifier(), Loc); - if (!firstType || !secondType) - return None; - return RequirementRepr::getTypeConstraint( - firstType, advanceLocBegin(Loc, conformanceReq->getColon()), - secondType); - } else if (auto layoutReq = req.getBody().getAs()) { - auto firstType = generate(layoutReq->getLeftTypeIdentifier(), Loc); - auto layout = generate(layoutReq->getLayoutConstraint(), Loc); - if (!firstType || layout.isNull()) - return None; - auto colonLoc = advanceLocBegin(Loc, layoutReq->getColon()); - auto layoutLoc = advanceLocBegin(Loc, layoutReq->getLayoutConstraint()); - return RequirementRepr::getLayoutConstraint( - firstType, colonLoc, LayoutConstraintLoc(layout, layoutLoc)); - } else { - llvm_unreachable("invalid syntax kind for requirement body"); - } -} - -static LayoutConstraintKind getLayoutConstraintKind(Identifier &id, - ASTContext &Ctx) { - if (id == Ctx.Id_TrivialLayout) - return LayoutConstraintKind::TrivialOfExactSize; - if (id == Ctx.Id_TrivialAtMostLayout) - return LayoutConstraintKind::TrivialOfAtMostSize; - if (id == Ctx.Id_RefCountedObjectLayout) - return LayoutConstraintKind::RefCountedObject; - if (id == Ctx.Id_NativeRefCountedObjectLayout) - return LayoutConstraintKind::NativeRefCountedObject; - if (id == Ctx.Id_ClassLayout) - return LayoutConstraintKind::Class; - if (id == Ctx.Id_NativeClassLayout) - return LayoutConstraintKind::NativeClass; - return LayoutConstraintKind::UnknownLayout; -} - -LayoutConstraint ASTGen::generate(const LayoutConstraintSyntax &constraint, - const SourceLoc Loc) { - auto name = Context.getIdentifier(constraint.getName().getIdentifierText()); - auto constraintKind = getLayoutConstraintKind(name, Context); - assert(constraintKind != LayoutConstraintKind::UnknownLayout); - - // Non-trivial constraint kinds don't have size/alignment. - // TODO: Diagnose if it's supplied? - if (!LayoutConstraintInfo::isTrivial(constraintKind)) - return LayoutConstraint::getLayoutConstraint(constraintKind, Context); - - // '_Trivial' without explicit size/alignment. - if (!constraint.getSize()) - return LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Trivial, - Context); - - int size = 0; - if (auto sizeSyntax = constraint.getSize()) - sizeSyntax->getText().getAsInteger(10, size); - assert(size >= 0); - - int alignment = 0; - if (auto alignmentSyntax = constraint.getAlignment()) - alignmentSyntax->getText().getAsInteger(10, alignment); - assert(alignment >= 0); - - return LayoutConstraint::getLayoutConstraint(constraintKind, size, alignment, - Context); -} - -SourceLoc ASTGen::advanceLocBegin(const SourceLoc &Loc, const Syntax &Node) { - return Loc.getAdvancedLoc(Node.getAbsolutePosition().getOffset()); -} - -SourceLoc ASTGen::advanceLocEnd(const SourceLoc &Loc, const Syntax &Node) { - if (!Node.isMissing()) { - // NOTE: We cannot use 'getLastToken()' because it doesn't take string - // literal expressions into account. - if (Node.isToken() || Node.is()) - return advanceLocBegin(Loc, Node); - for (size_t I = Node.getNumChildren(); I != 0; --I) - if (auto Child = Node.getChild(I - 1)) - return advanceLocEnd(Loc, *Child); - } - if (auto Prev = Node.getPreviousNode()) - return advanceLocEnd(Loc, *Prev); - assert(false && "No tokens in tree?"); - return Loc; -} - -StringRef ASTGen::copyAndStripUnderscores(StringRef Orig) { - return copyAndStripUnderscores(Orig, Context); -} - -Expr * -ASTGen::generateMagicIdentifierLiteralExpression(const TokenSyntax &PoundToken, - const SourceLoc Loc) { - auto Kind = getMagicIdentifierLiteralKind(PoundToken.getTokenKind()); - auto KindLoc = advanceLocBegin(Loc, PoundToken); - return new (Context) MagicIdentifierLiteralExpr(Kind, KindLoc); -} - -MagicIdentifierLiteralExpr::Kind -ASTGen::getMagicIdentifierLiteralKind(tok Kind) { - switch (Kind) { - case tok::kw___COLUMN__: - case tok::pound_column: - return MagicIdentifierLiteralExpr::Kind::Column; - case tok::kw___FILE__: - case tok::pound_file: - return MagicIdentifierLiteralExpr::Kind::File; - case tok::kw___FUNCTION__: - case tok::pound_function: - return MagicIdentifierLiteralExpr::Kind::Function; - case tok::kw___LINE__: - case tok::pound_line: - return MagicIdentifierLiteralExpr::Kind::Line; - case tok::kw___DSO_HANDLE__: - case tok::pound_dsohandle: - return MagicIdentifierLiteralExpr::Kind::DSOHandle; - default: - llvm_unreachable("not a magic literal"); - } -} - -ValueDecl *ASTGen::lookupInScope(DeclName Name) { - return P.lookupInScope(Name); -} - -void ASTGen::addToScope(ValueDecl *D, bool diagnoseRedefinitions) { - P.addToScope(D, diagnoseRedefinitions); -} - -TypeRepr *ASTGen::cacheType(TypeSyntax Type, TypeRepr *TypeAST) { - TypeCache[Type.getId()] = TypeAST; - return TypeAST; -} - -TypeRepr *ASTGen::lookupType(TypeSyntax Type) { - auto Found = TypeCache.find(Type.getId()); - return Found != TypeCache.end() ? Found->second : nullptr; -} - -void ASTGen::addExpr(Expr *E, const SourceLoc Loc) { - assert(!hasExpr(Loc)); - Exprs[Loc] = E; -} - -bool ASTGen::hasExpr(const SourceLoc Loc) const { - return Exprs.find(Loc) != Exprs.end(); -} - -Expr *ASTGen::takeExpr(const SourceLoc Loc) { - auto I = Exprs.find(Loc); - assert(I != Exprs.end()); - auto expr = I->second; - Exprs.erase(I); - return expr; -} - -void ASTGen::addDeclAttributes(DeclAttributes attrs, SourceLoc Loc) { - assert(!hasDeclAttributes(Loc)); - ParsedDeclAttrs[Loc] = attrs; -} - -bool ASTGen::hasDeclAttributes(SourceLoc Loc) const { - return ParsedDeclAttrs.find(Loc) != ParsedDeclAttrs.end(); -} - -DeclAttributes ASTGen::takeDeclAttributes(SourceLoc Loc) { - auto I = ParsedDeclAttrs.find(Loc); - assert(I != ParsedDeclAttrs.end()); - auto attrs = I->second; - ParsedDeclAttrs.erase(I); - return attrs; -} diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 84abea7dce9b4..c9ae5862e7888 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -5,9 +5,7 @@ else() endif() add_swift_host_library(swiftParse STATIC - ASTGen.cpp Confusables.cpp - HiddenLibSyntaxAction.cpp Lexer.cpp ParseDecl.cpp ParsedRawSyntaxNode.cpp @@ -32,7 +30,6 @@ add_swift_host_library(swiftParse STATIC ParsedSyntaxRecorder.cpp.gyb) target_link_libraries(swiftParse PRIVATE swiftAST - swiftSyntax - swiftSyntaxParse) + swiftSyntax) add_dependencies(swiftParse swift-parse-syntax-generated-headers) diff --git a/lib/Parse/DebuggerContextChange.h b/lib/Parse/DebuggerContextChange.h deleted file mode 100644 index b6ab3fe0f8ed0..0000000000000 --- a/lib/Parse/DebuggerContextChange.h +++ /dev/null @@ -1,151 +0,0 @@ -//===--- DebuggerContextChange.h --------------------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_PARSE_DEBUGGERCONTEXTCHANGE_H -#define SWIFT_PARSE_DEBUGGERCONTEXTCHANGE_H - -#include "swift/AST/DebuggerClient.h" -#include "swift/AST/Decl.h" -#include "swift/AST/Identifier.h" -#include "swift/AST/SourceFile.h" -#include "swift/Parse/Parser.h" - -namespace swift { - -/// A RAII object for deciding whether this DeclKind needs special -/// treatment when parsing in the "debugger context", and implementing -/// that treatment. The problem arises because, when lldb -/// uses swift to parse expressions, it needs to emulate the current -/// frame's scope. We do that, for instance, by making a class extension -/// and running the code in a function in that extension. -/// -/// This causes two kinds of issues: -/// 1) Some DeclKinds require to be parsed in TopLevel contexts only. -/// 2) Sometimes the debugger wants a Decl to live beyond the current -/// function invocation, in which case it should be parsed at the -/// file scope level so it will be set up correctly for this purpose. -/// -/// Creating an instance of this object will cause it to figure out -/// whether we are in the debugger function, whether it needs to swap -/// the Decl that is currently being parsed. -/// If you have created the object, instead of returning the result -/// with makeParserResult, use the object's fixupParserResult. If -/// no swap has occurred, these methods will work the same. -/// If the decl has been moved, then Parser::markWasHandled will be -/// called on the Decl, and you should call declWasHandledAlready -/// before you consume the Decl to see if you actually need to -/// consume it. -/// If you are making one of these objects to address issue 1, call -/// the constructor that only takes a DeclKind, and it will be moved -/// unconditionally. Otherwise pass in the Name and DeclKind and the -/// DebuggerClient will be asked whether to move it or not. -class DebuggerContextChange { -protected: - Parser &P; - Identifier Name; - SourceFile *SF; - Optional CC; - -public: - DebuggerContextChange(Parser &P) : P(P), SF(nullptr) { - if (!inDebuggerContext()) - return; - else - switchContext(); - } - - DebuggerContextChange(Parser &P, Identifier &Name, DeclKind Kind) - : P(P), Name(Name), SF(nullptr) { - if (!inDebuggerContext()) - return; - bool globalize = false; - - DebuggerClient *debug_client = getDebuggerClient(); - if (!debug_client) - return; - - globalize = debug_client->shouldGlobalize(Name, Kind); - - if (globalize) - switchContext(); - } - - bool movedToTopLevel() { return CC.hasValue(); } - - template - ParserResult fixupParserResult(ParserResult &Result) { - ParserStatus Status = Result; - return fixupParserResult(Status, Result.getPtrOrNull()); - } - - template ParserResult fixupParserResult(T *D) { - if (CC.hasValue()) { - swapDecl(D); - } - return ParserResult(D); - } - - template - ParserResult fixupParserResult(ParserStatus Status, T *D) { - if (CC.hasValue() && !Status.isError()) { - // If there is an error, don't do our splicing trick, - // just return the Decl and the status for reporting. - swapDecl(D); - } - return makeParserResult(Status, D); - } - - // The destructor doesn't need to do anything, the CC's destructor will - // pop the context if we set it. - ~DebuggerContextChange() {} - -protected: - DebuggerClient *getDebuggerClient() { - ModuleDecl *PM = P.CurDeclContext->getParentModule(); - if (!PM) - return nullptr; - else - return PM->getDebugClient(); - } - - bool inDebuggerContext() { - if (!P.Context.LangOpts.DebuggerSupport) - return false; - if (!P.CurDeclContext) - return false; - auto *func_decl = dyn_cast(P.CurDeclContext); - if (!func_decl) - return false; - - if (!func_decl->getAttrs().hasAttribute()) - return false; - - return true; - } - - void switchContext() { - SF = P.CurDeclContext->getParentSourceFile(); - CC.emplace(P, SF); - } - - void swapDecl(Decl *D) { - assert(SF); - DebuggerClient *debug_client = getDebuggerClient(); - assert(debug_client); - debug_client->didGlobalize(D); - SF->Decls.push_back(D); - P.markWasHandled(D); - } -}; -} // namespace swift - -#endif diff --git a/lib/Parse/HiddenLibSyntaxAction.cpp b/lib/Parse/HiddenLibSyntaxAction.cpp deleted file mode 100644 index 023d01158bae2..0000000000000 --- a/lib/Parse/HiddenLibSyntaxAction.cpp +++ /dev/null @@ -1,136 +0,0 @@ -//===--- HiddenLibSyntaxAction.cpp ----------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "swift/Parse/HiddenLibSyntaxAction.h" - -#include "swift/AST/ASTContext.h" -#include "swift/AST/DiagnosticsParse.h" -#include "swift/AST/Module.h" -#include "swift/Basic/OwnedString.h" -#include "swift/Parse/ParsedTrivia.h" -#include "swift/Parse/SyntaxParsingCache.h" -#include "swift/Parse/Token.h" -#include "swift/Syntax/RawSyntax.h" -#include "swift/Syntax/SyntaxVisitor.h" -#include "swift/Syntax/Trivia.h" -#include "llvm/ADT/SmallVector.h" - -using namespace swift; -using namespace swift::syntax; -using namespace llvm; - -OpaqueSyntaxNode -HiddenLibSyntaxAction::makeHiddenNode(OpaqueSyntaxNode explicitActionNode, - OpaqueSyntaxNode libSyntaxNode) { - auto dat = NodeAllocator.Allocate(); - return new (dat) Node(explicitActionNode, libSyntaxNode); -} - -OpaqueSyntaxNode HiddenLibSyntaxAction::recordToken( - tok tokenKind, ArrayRef leadingTrivia, - ArrayRef trailingTrivia, CharSourceRange range) { - OpaqueSyntaxNode primaryNode = ExplicitAction->recordToken( - tokenKind, leadingTrivia, trailingTrivia, range); - OpaqueSyntaxNode secondaryNode = nullptr; - - if (areBothLibSyntax()) { - secondaryNode = primaryNode; - } else { - secondaryNode = LibSyntaxAction->recordToken(tokenKind, leadingTrivia, - trailingTrivia, range); - } - - return makeHiddenNode(primaryNode, secondaryNode); -} - -OpaqueSyntaxNode HiddenLibSyntaxAction::recordMissingToken(tok tokenKind, - SourceLoc loc) { - OpaqueSyntaxNode primaryNode = - ExplicitAction->recordMissingToken(tokenKind, loc); - OpaqueSyntaxNode secondaryNode = nullptr; - - if (areBothLibSyntax()) { - secondaryNode = primaryNode; - } else { - secondaryNode = LibSyntaxAction->recordMissingToken(tokenKind, loc); - } - - return makeHiddenNode(primaryNode, secondaryNode); -} - -OpaqueSyntaxNode -HiddenLibSyntaxAction::recordRawSyntax(syntax::SyntaxKind kind, - ArrayRef elements, - CharSourceRange range) { - OpaqueSyntaxNode primaryNode = nullptr; - OpaqueSyntaxNode secondaryNode = nullptr; - - { - SmallVector primaryElements; - primaryElements.reserve(elements.size()); - for (auto element : elements) { - OpaqueSyntaxNode primaryElement = nullptr; - if (element) - primaryElement = ((Node *)element)->ExplicitActionNode; - primaryElements.push_back(primaryElement); - } - - primaryNode = ExplicitAction->recordRawSyntax(kind, primaryElements, range); - } - - if (areBothLibSyntax()) { - secondaryNode = primaryNode; - } else { - SmallVector secondaryElements; - secondaryElements.reserve(elements.size()); - for (auto element : elements) { - OpaqueSyntaxNode secondaryElement = nullptr; - if (element) - secondaryElement = ((Node *)element)->LibSyntaxNode; - secondaryElements.push_back(secondaryElement); - } - secondaryNode = - LibSyntaxAction->recordRawSyntax(kind, secondaryElements, range); - } - - return makeHiddenNode(primaryNode, secondaryNode); -} - -std::pair -HiddenLibSyntaxAction::lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) { - size_t length; - OpaqueSyntaxNode n; - std::tie(length, n) = ExplicitAction->lookupNode(lexerOffset, kind); - if (length == 0) - return {0, nullptr}; - return {length, makeHiddenNode(n, nullptr)}; -} - -RawSyntax *HiddenLibSyntaxAction::getLibSyntaxNodeFor(OpaqueSyntaxNode node) { - auto hiddenNode = (Node *)node; - return (RawSyntax *)hiddenNode->LibSyntaxNode; -} - -OpaqueSyntaxNode -HiddenLibSyntaxAction::getExplicitNodeFor(OpaqueSyntaxNode node) { - auto hiddenNode = (Node *)node; - return hiddenNode->ExplicitActionNode; -} - -void HiddenLibSyntaxAction::discardRecordedNode(OpaqueSyntaxNode opaqueN) { - if (!opaqueN) - return; - auto node = static_cast(opaqueN); - if (!areBothLibSyntax()) - LibSyntaxAction->discardRecordedNode(node->LibSyntaxNode); - ExplicitAction->discardRecordedNode(node->ExplicitActionNode); -} diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 0c7a51f7e7ff4..7fd7e5915725d 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -2482,7 +2482,8 @@ void Lexer::lexImpl() { } } -Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { +Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc, + CommentRetentionMode CRM) { // Don't try to do anything with an invalid location. if (!Loc.isValid()) return Token(); @@ -2501,7 +2502,7 @@ Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc) { // (making this option irrelevant), or the caller lexed comments and // we need to lex just the comment token. Lexer L(FakeLangOpts, SM, BufferID, nullptr, LexerMode::Swift, - HashbangMode::Allowed, CommentRetentionMode::ReturnAsTokens); + HashbangMode::Allowed, CRM); L.restoreState(State(Loc)); return L.peekNextToken(); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b75b3857cfcfc..6edbf335b8d78 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -14,10 +14,8 @@ // //===----------------------------------------------------------------------===// -#include "DebuggerContextChange.h" #include "swift/Parse/Parser.h" #include "swift/Parse/CodeCompletionCallbacks.h" -#include "swift/Parse/ParsedSyntaxBuilders.h" #include "swift/Parse/ParsedSyntaxRecorder.h" #include "swift/Parse/ParseSILSupport.h" #include "swift/Parse/SyntaxParsingContext.h" @@ -47,6 +45,142 @@ using namespace swift; using namespace syntax; +namespace { + /// A RAII object for deciding whether this DeclKind needs special + /// treatment when parsing in the "debugger context", and implementing + /// that treatment. The problem arises because, when lldb + /// uses swift to parse expressions, it needs to emulate the current + /// frame's scope. We do that, for instance, by making a class extension + /// and running the code in a function in that extension. + /// + /// This causes two kinds of issues: + /// 1) Some DeclKinds require to be parsed in TopLevel contexts only. + /// 2) Sometimes the debugger wants a Decl to live beyond the current + /// function invocation, in which case it should be parsed at the + /// file scope level so it will be set up correctly for this purpose. + /// + /// Creating an instance of this object will cause it to figure out + /// whether we are in the debugger function, whether it needs to swap + /// the Decl that is currently being parsed. + /// If you have created the object, instead of returning the result + /// with makeParserResult, use the object's fixupParserResult. If + /// no swap has occurred, these methods will work the same. + /// If the decl has been moved, then Parser::markWasHandled will be + /// called on the Decl, and you should call declWasHandledAlready + /// before you consume the Decl to see if you actually need to + /// consume it. + /// If you are making one of these objects to address issue 1, call + /// the constructor that only takes a DeclKind, and it will be moved + /// unconditionally. Otherwise pass in the Name and DeclKind and the + /// DebuggerClient will be asked whether to move it or not. + class DebuggerContextChange { + protected: + Parser &P; + Identifier Name; + SourceFile *SF; + Optional CC; + public: + DebuggerContextChange (Parser &P) + : P(P), SF(nullptr) { + if (!inDebuggerContext()) + return; + else + switchContext(); + } + + DebuggerContextChange (Parser &P, Identifier &Name, DeclKind Kind) + : P(P), Name(Name), SF(nullptr) { + if (!inDebuggerContext()) + return; + bool globalize = false; + + DebuggerClient *debug_client = getDebuggerClient(); + if (!debug_client) + return; + + globalize = debug_client->shouldGlobalize(Name, Kind); + + if (globalize) + switchContext(); + } + + bool movedToTopLevel() { + return CC.hasValue(); + } + + template + ParserResult + fixupParserResult(ParserResult &Result) { + ParserStatus Status = Result; + return fixupParserResult(Status, Result.getPtrOrNull()); + } + + template + ParserResult + fixupParserResult(T *D) { + if (CC.hasValue()) { + swapDecl(D); + } + return ParserResult(D); + } + + template + ParserResult + fixupParserResult(ParserStatus Status, T *D) { + if (CC.hasValue() && !Status.isError()) { + // If there is an error, don't do our splicing trick, + // just return the Decl and the status for reporting. + swapDecl(D); + } + return makeParserResult(Status, D); + } + + // The destructor doesn't need to do anything, the CC's destructor will + // pop the context if we set it. + ~DebuggerContextChange () {} + protected: + + DebuggerClient *getDebuggerClient() + { + ModuleDecl *PM = P.CurDeclContext->getParentModule(); + if (!PM) + return nullptr; + else + return PM->getDebugClient(); + } + + bool inDebuggerContext() { + if (!P.Context.LangOpts.DebuggerSupport) + return false; + if (!P.CurDeclContext) + return false; + auto *func_decl = dyn_cast(P.CurDeclContext); + if (!func_decl) + return false; + + if (!func_decl->getAttrs().hasAttribute()) + return false; + + return true; + } + + void switchContext () { + SF = P.CurDeclContext->getParentSourceFile(); + CC.emplace (P, SF); + } + + void swapDecl (Decl *D) + { + assert (SF); + DebuggerClient *debug_client = getDebuggerClient(); + assert (debug_client); + debug_client->didGlobalize(D); + SF->addTopLevelDecl(D); + P.markWasHandled(D); + } + }; +} // end anonymous namespace + /// Main entrypoint for the parser. /// /// \verbatim @@ -55,7 +189,7 @@ using namespace syntax; /// decl-sil [[only in SIL mode] /// decl-sil-stage [[only in SIL mode] /// \endverbatim -bool Parser::parseTopLevel() { +void Parser::parseTopLevel() { SF.ASTStage = SourceFile::Parsing; // Prime the lexer. @@ -112,22 +246,11 @@ bool Parser::parseTopLevel() { consumeToken(); } - // If this is a Main source file, determine if we found code that needs to be - // executed (this is used by the repl to know whether to compile and run the - // newly parsed stuff). - bool FoundTopLevelCodeToExecute = false; - if (allowTopLevelCode()) { - for (auto V : Items) { - if (isa(V.get())) - FoundTopLevelCodeToExecute = true; - } - } - // Add newly parsed decls to the module. for (auto Item : Items) { if (auto *D = Item.dyn_cast()) { assert(!isa(D) && "accessors should not be added here"); - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); } } @@ -145,8 +268,6 @@ bool Parser::parseTopLevel() { SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia); TokReceiver->finalize(); } - - return FoundTopLevelCodeToExecute; } ParserResult Parser::parseExtendedAvailabilitySpecList( @@ -619,7 +740,7 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { SourceLoc lParenLoc = consumeToken(); DeclNameLoc MemberNameLoc; - DeclName MemberName; + DeclNameRef MemberName; ParserResult ProtocolType; { SyntaxParsingContext ContentContext( @@ -634,11 +755,10 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { } if (!Status.shouldStopParsing()) { - MemberName = - parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc, - diag::attr_implements_expected_member_name, - /*allowOperators=*/true, - /*allowZeroArgCompoundNames=*/true); + MemberName = parseDeclNameRef(MemberNameLoc, + diag::attr_implements_expected_member_name, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowOperators); if (!MemberName) { Status.setIsParseError(); } @@ -660,9 +780,503 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) { return Status; } + // FIXME(ModQual): Reject module qualification on MemberName. + return ParserResult( ImplementsAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), - ProtocolType.get(), MemberName, MemberNameLoc)); + ProtocolType.get(), MemberName.getFullName(), + MemberNameLoc)); +} + +/// Parse a `@differentiable` attribute, returning true on error. +/// +/// \verbatim +/// differentiable-attribute-arguments: +/// '(' (differentiability-params-clause ',')? +/// (differentiable-attr-func-specifier ',')? +/// differentiable-attr-func-specifier? +/// where-clause? +/// ')' +/// differentiable-attr-func-specifier: +/// ('jvp' | 'vjp') ':' decl-name +/// \endverbatim +ParserResult +Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) { + StringRef AttrName = "differentiable"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + bool linear = false; + SmallVector parameters; + Optional jvpSpec; + Optional vjpSpec; + TrailingWhereClause *whereClause = nullptr; + + // Parse '('. + if (consumeIf(tok::l_paren, lParenLoc)) { + // Parse @differentiable attribute arguments. + if (parseDifferentiableAttributeArguments(linear, parameters, jvpSpec, + vjpSpec, whereClause)) + return makeParserError(); + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier=*/false); + return makeParserError(); + } + } + + return ParserResult(DifferentiableAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), linear, + parameters, jvpSpec, vjpSpec, whereClause)); +} + +// Attribute parsing error helper. +// For the given parentheses depth, skip until ')' and consume it if possible. +// If no ')' is found, produce error. +// Always returns true to indicate a parsing error has occurred. +static bool errorAndSkipUntilConsumeRightParen(Parser &P, StringRef attrName, + int parenDepth = 1) { + for (int i = 0; i < parenDepth; ++i) { + P.skipUntil(tok::r_paren); + if (!P.consumeIf(tok::r_paren)) { + P.diagnose(P.Tok, diag::attr_expected_rparen, attrName, + /*DeclModifier=*/false); + return true; + } + } + return true; +}; + +/// Parse a differentiability parameters 'wrt:' clause, returning true on error. +/// If `allowNamedParameters` is false, allow only index parameters and 'self'. +/// +/// \verbatim +/// differentiability-params-clause: +/// 'wrt' ':' (differentiability-param | differentiability-params) +/// differentiability-params: +/// '(' differentiability-param (',' differentiability-param)* ')' +/// differentiability-param: +/// 'self' | identifier | [0-9]+ +/// \endverbatim +bool Parser::parseDifferentiabilityParametersClause( + SmallVectorImpl ¶meters, StringRef attrName, + bool allowNamedParameters) { + SyntaxParsingContext DiffParamsClauseContext( + SyntaxContext, SyntaxKind::DifferentiationParamsClause); + consumeToken(tok::identifier); + if (!consumeIf(tok::colon)) { + diagnose(Tok, diag::expected_colon_after_label, "wrt"); + return errorAndSkipUntilConsumeRightParen(*this, attrName); + } + + // Function that parses a parameter into `parameters`. Returns true if error + // occurred. + auto parseParam = [&](bool parseTrailingComma = true) -> bool { + SyntaxParsingContext DiffParamContext( + SyntaxContext, SyntaxKind::DifferentiationParam); + SourceLoc paramLoc; + switch (Tok.getKind()) { + case tok::identifier: { + // If named parameters are not allowed, diagnose. + if (!allowNamedParameters) { + diagnose(Tok, diag::diff_params_clause_expected_parameter_unnamed); + return true; + } + Identifier paramName; + if (parseIdentifier(paramName, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + parameters.push_back( + ParsedAutoDiffParameter::getNamedParameter(paramLoc, paramName)); + break; + } + case tok::integer_literal: { + unsigned paramNum; + if (parseUnsignedInteger( + paramNum, paramLoc, + diag::diff_params_clause_expected_parameter)) + return true; + parameters.push_back( + ParsedAutoDiffParameter::getOrderedParameter(paramLoc, paramNum)); + break; + } + case tok::kw_self: { + paramLoc = consumeToken(tok::kw_self); + parameters.push_back(ParsedAutoDiffParameter::getSelfParameter(paramLoc)); + break; + } + default: + diagnose(Tok, diag::diff_params_clause_expected_parameter); + return true; + } + if (parseTrailingComma && Tok.isNot(tok::r_paren)) + return parseToken(tok::comma, diag::attr_expected_comma, attrName, + /*isDeclModifier=*/false); + return false; + }; + + // Parse opening '(' of the parameter list. + if (Tok.is(tok::l_paren)) { + SyntaxParsingContext DiffParamsContext( + SyntaxContext, SyntaxKind::DifferentiationParams); + consumeToken(tok::l_paren); + // Parse first parameter. At least one is required. + if (parseParam()) + return errorAndSkipUntilConsumeRightParen(*this, attrName, 2); + // Parse remaining parameters until ')'. + while (Tok.isNot(tok::r_paren)) + if (parseParam()) + return errorAndSkipUntilConsumeRightParen(*this, attrName, 2); + SyntaxContext->collectNodesInPlace(SyntaxKind::DifferentiationParamList); + // Parse closing ')' of the parameter list. + consumeToken(tok::r_paren); + } + // If no opening '(' for parameter list, parse a single parameter. + else { + if (parseParam(/*parseTrailingComma*/ false)) + return errorAndSkipUntilConsumeRightParen(*this, attrName); + } + return false; +} + +bool Parser::parseDifferentiableAttributeArguments( + bool &linear, SmallVectorImpl ¶meters, + Optional &jvpSpec, + Optional &vjpSpec, TrailingWhereClause *&whereClause) { + StringRef AttrName = "differentiable"; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before 'where' or ')'. + if (Tok.is(tok::kw_where) || Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return true; + } + // Check that token after comma is 'wrt' or a function specifier label. + if (isIdentifier(Tok, "wrt") || isIdentifier(Tok, "jvp") || + isIdentifier(Tok, "vjp")) { + return false; + } + diagnose(Tok, diag::attr_differentiable_expected_label); + return true; + }; + + // Store starting parser position. + auto startingLoc = Tok.getLoc(); + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeArguments); + + // Parse optional differentiability parameters. + // Parse 'linear' label (optional). + linear = false; + if (isIdentifier(Tok, "linear")) { + linear = true; + consumeToken(tok::identifier); + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + + // If 'withRespectTo' is used, make the user change it to 'wrt'. + if (isIdentifier(Tok, "withRespectTo")) { + SourceRange withRespectToRange(Tok.getLoc(), peekToken().getLoc()); + diagnose(Tok, diag::attr_differentiable_use_wrt_not_withrespectto) + .highlight(withRespectToRange) + .fixItReplace(withRespectToRange, "wrt:"); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + // Parse the optional 'wrt' differentiability parameters clause. + if (isIdentifier(Tok, "wrt")) { + if (parseDifferentiabilityParametersClause(parameters, AttrName)) + return true; + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + + // Function that parses a label and a function specifier, e.g. 'vjp: foo(_:)'. + // Return true on error. + auto parseFuncSpec = [&](StringRef label, DeclNameRefWithLoc &result, + bool &terminateParsingArgs) -> bool { + // Parse label. + if (parseSpecificIdentifier(label, diag::attr_missing_label, label, + AttrName) || + parseToken(tok::colon, diag::expected_colon_after_label, label)) + return true; + // Parse the name of the function. + SyntaxParsingContext FuncDeclNameContext( + SyntaxContext, SyntaxKind::FunctionDeclName); + Diagnostic funcDiag(diag::attr_differentiable_expected_function_name.ID, + { label }); + result.Name = parseDeclNameRef(result.Loc, funcDiag, + DeclNameFlag::AllowZeroArgCompoundNames | DeclNameFlag::AllowOperators); + // Emit warning for deprecated `jvp:` and `vjp:` arguments. + // TODO(TF-1001): Remove deprecated `jvp:` and `vjp:` arguments. + if (result.Loc.isValid()) { + diagnose(result.Loc.getStartLoc(), + diag::attr_differentiable_jvp_vjp_deprecated_warning) + .highlight(result.Loc.getSourceRange()); + } + // If no trailing comma or 'where' clause, terminate parsing arguments. + if (Tok.isNot(tok::comma, tok::kw_where)) + terminateParsingArgs = true; + return !result.Name; + }; + + // Store whether to terminate parsing arguments. + bool terminateParsingArgs = false; + + // Parse 'jvp: ' (optional). + if (isIdentifier(Tok, "jvp")) { + SyntaxParsingContext JvpContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); + jvpSpec = DeclNameRefWithLoc(); + if (parseFuncSpec("jvp", *jvpSpec, terminateParsingArgs)) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + if (terminateParsingArgs) + return false; + if (consumeIfTrailingComma()) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + + // Parse 'vjp: ' (optional). + if (isIdentifier(Tok, "vjp")) { + SyntaxParsingContext VjpContext( + SyntaxContext, SyntaxKind::DifferentiableAttributeFuncSpecifier); + vjpSpec = DeclNameRefWithLoc(); + if (parseFuncSpec("vjp", *vjpSpec, terminateParsingArgs)) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + if (terminateParsingArgs) + return false; + // Note: intentionally parse trailing comma here, even though it's the last + // function specifier. `consumeIfTrailingComma` will emit an error. + if (consumeIfTrailingComma()) + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + + // If parser has not advanced and token is not 'where' or ')', emit error. + if (Tok.getLoc() == startingLoc && Tok.isNot(tok::kw_where, tok::r_paren)) { + diagnose(Tok, diag::attr_differentiable_expected_label); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + + // Parse a trailing 'where' clause if any. + if (Tok.is(tok::kw_where)) { + SourceLoc whereLoc; + SmallVector requirements; + bool firstTypeInComplete; + parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete, + /*AllowLayoutConstraints*/ true); + whereClause = TrailingWhereClause::create(Context, whereLoc, requirements); + } + return false; +} + +/// Helper function that parses 'type-identifier' for `parseQualifiedDeclName`. +/// Returns true on error. Sets `baseType` to the parsed base type if present, +/// or to `nullptr` if not. A missing base type is not considered an error. +static bool parseBaseTypeForQualifiedDeclName(Parser &P, TypeRepr *&baseType) { + baseType = nullptr; + + // If base type cannot be parsed, return false (no error). + if (!P.canParseBaseTypeForQualifiedDeclName()) + return false; + + auto result = P.parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true); + // If base type should be parseable but the actual base type result is null, + // return true (error). + if (result.isNull()) + return true; + + // Consume the leading period before the final declaration name component. + // `parseTypeIdentifier(/*isParsingQualifiedDeclName*/ true)` leaves the + // leading period unparsed to avoid syntax verification errors. + assert(P.startsWithSymbol(P.Tok, '.') && "false"); + P.consumeStartingCharacterOfCurrentToken(tok::period); + + // Set base type and return false (no error). + baseType = result.getPtrOrNull(); + return false; +} + +/// Parses an optional base type, followed by a declaration name. +/// Returns true on error (if declaration name could not be parsed). +/// +/// \verbatim +/// qualified-decl-name: +/// type-identifier? unqualified-decl-name +/// type-identifier: +/// identifier generic-args? ('.' identifier generic-args?)* +/// \endverbatim +/// +// TODO(TF-1066): Use module qualified name syntax/parsing instead of custom +// qualified name syntax/parsing. +static bool parseQualifiedDeclName(Parser &P, Diag<> nameParseError, + TypeRepr *&baseType, + DeclNameRefWithLoc &original) { + SyntaxParsingContext DeclNameContext(P.SyntaxContext, + SyntaxKind::QualifiedDeclName); + // Parse base type. + if (parseBaseTypeForQualifiedDeclName(P, baseType)) + return true; + // Parse final declaration name. + original.Name = P.parseDeclNameRef( + original.Loc, nameParseError, + Parser::DeclNameFlag::AllowZeroArgCompoundNames | + Parser::DeclNameFlag::AllowKeywordsUsingSpecialNames | + Parser::DeclNameFlag::AllowOperators); + // The base type is optional, but the final unqualified declaration name is + // not. If name could not be parsed, return true for error. + return !original.Name; +} + +/// Parse a `@derivative(of:)` attribute, returning true on error. +/// +/// \verbatim +/// derivative-attribute-arguments: +/// '(' 'of' ':' qualified-decl-name (',' differentiability-params-clause)? +/// ')' +/// \endverbatim +ParserResult Parser::parseDerivativeAttribute(SourceLoc atLoc, + SourceLoc loc) { + StringRef AttrName = "derivative"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; + DeclNameRefWithLoc original; + SmallVector parameters; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before ')'. + if (Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + // Check that token after comma is 'wrt:'. + if (isIdentifier(Tok, "wrt")) + return false; + diagnose(Tok, diag::attr_expected_label, "wrt", AttrName); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + }; + // Parse '('. + if (!consumeIf(tok::l_paren, lParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DerivativeRegistrationAttributeArguments); + // Parse the 'of:' label and colon. + if (parseSpecificIdentifier("of", diag::attr_missing_label, "of", + AttrName) || + parseToken(tok::colon, diag::expected_colon_after_label, "of")) { + return makeParserError(); + } + { + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); + } + if (consumeIfTrailingComma()) + return makeParserError(); + // Parse the optional 'wrt' differentiability parameters clause. + if (isIdentifier(Tok, "wrt") && + parseDifferentiabilityParametersClause(parameters, AttrName)) + return makeParserError(); + } + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + return ParserResult(DerivativeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, parameters)); +} + +/// Parse a `@transpose(of:)` attribute, returning true on error. +/// +/// \verbatim +/// transpose-attribute-arguments: +/// '(' 'of' ':' qualified-decl-name (',' linearity-params-clause)? ')' +/// linearity-params-clause: +/// 'wrt' ':' (linearity-param | linearity-params) +/// linearity-params: +/// '(' linearity-param (',' linearity-param)* ')' +/// linearity-param: +/// 'self' | [0-9]+ +/// \endverbatim +ParserResult Parser::parseTransposeAttribute(SourceLoc atLoc, + SourceLoc loc) { + StringRef AttrName = "transpose"; + SourceLoc lParenLoc = loc, rParenLoc = loc; + TypeRepr *baseType = nullptr; + DeclNameRefWithLoc original; + SmallVector parameters; + + // Parse trailing comma, if it exists, and check for errors. + auto consumeIfTrailingComma = [&]() -> bool { + if (!consumeIf(tok::comma)) return false; + // Diagnose trailing comma before ')'. + if (Tok.is(tok::r_paren)) { + diagnose(Tok, diag::unexpected_separator, ","); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + } + // Check that token after comma is 'wrt:'. + if (isIdentifier(Tok, "wrt")) + return false; + diagnose(Tok, diag::attr_expected_label, "wrt", AttrName); + return errorAndSkipUntilConsumeRightParen(*this, AttrName); + }; + + // Parse '('. + if (!consumeIf(tok::l_paren, lParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_lparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::DerivativeRegistrationAttributeArguments); + // Parse the 'of:' label and colon. + if (parseSpecificIdentifier("of", diag::attr_missing_label, "of", + AttrName) || + parseToken(tok::colon, diag::expected_colon_after_label, "of")) { + return makeParserError(); + } + { + // Parse the optionally qualified function name. + if (parseQualifiedDeclName( + *this, diag::autodiff_attr_expected_original_decl_name, + baseType, original)) + return makeParserError(); + } + if (consumeIfTrailingComma()) + return makeParserError(); + // Parse the optional 'wrt' linearity parameters clause. + if (Tok.is(tok::identifier) && Tok.getText() == "wrt" && + parseDifferentiabilityParametersClause(parameters, AttrName, + /*allowNamedParameters*/ false)) + return makeParserError(); + } + // Parse ')'. + if (!consumeIf(tok::r_paren, rParenLoc)) { + diagnose(getEndOfPreviousLoc(), diag::attr_expected_rparen, AttrName, + /*DeclModifier*/ false); + return makeParserError(); + } + return ParserResult(TransposeAttr::create( + Context, /*implicit*/ false, atLoc, SourceRange(loc, rParenLoc), baseType, + original, parameters)); } void Parser::parseObjCSelector(SmallVector &Names, @@ -1129,21 +1743,130 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, return false; } - // Diagnose using @_semantics in a local scope. These don't - // actually work. - if (CurDeclContext->isLocalContext()) { - // Emit an error, but do not discard the attribute. This enables - // better recovery in the parser. - diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); - } - if (!DiscardAttribute) Attributes.add(new (Context) SemanticsAttr(Value.getValue(), AtLoc, AttrRange, /*Implicit=*/false)); break; } + case DAK_OriginallyDefinedIn: { + auto LeftLoc = Tok.getLoc(); + if (!consumeIf(tok::l_paren)) { + diagnose(Loc, diag::attr_expected_lparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return false; + } + SourceLoc RightLoc; + enum class NextSegmentKind: uint8_t { + ModuleName = 0, + PlatformVersion, + }; + NextSegmentKind NK = NextSegmentKind::ModuleName; + StringRef OriginalModuleName; + llvm::SmallVector, 4> + PlatformAndVersions; + + StringRef AttrName = "@_originalDefinedIn"; + if (parseList(tok::r_paren, LeftLoc, RightLoc, false, + diag::originally_defined_in_missing_rparen, + SyntaxKind::Unknown, [&]() -> ParserStatus { + SWIFT_DEFER { + if (NK != NextSegmentKind::PlatformVersion) { + NK = (NextSegmentKind)((uint8_t)NK + (uint8_t)1); + } + }; + switch (NK) { + // Parse 'module: "original_module_name"'. + case NextSegmentKind::ModuleName: { + // Parse 'module' ':'. + if (!Tok.is(tok::identifier) || Tok.getText() != "module" || + !peekToken().is(tok::colon)) { + diagnose(Tok, diag::originally_defined_in_need_original_module_name); + return makeParserError(); + } + consumeToken(tok::identifier); + consumeToken(tok::colon); + // Parse the next string literal as the original module name. + auto ModuleNameLoc = Tok.getLoc(); + if (Tok.is(tok::string_literal)) { + auto NameOp = getStringLiteralIfNotInterpolated(Tok.getLoc(), + "original module name"); + if (NameOp.hasValue()) + OriginalModuleName = *NameOp; + consumeToken(); + } + if (OriginalModuleName.empty()) { + diagnose(ModuleNameLoc, + diag::originally_defined_in_need_nonempty_module_name); + return makeParserError(); + } + return makeParserSuccess(); + } + // Parse 'OSX 13.13'. + case NextSegmentKind::PlatformVersion: { + if ((Tok.is(tok::identifier) || Tok.is(tok::oper_binary_spaced)) && + (peekToken().is(tok::floating_literal) || + peekToken().is(tok::integer_literal))) { + PlatformKind Platform; + // Parse platform name. + auto Plat = platformFromString(Tok.getText()); + if (!Plat.hasValue()) { + diagnose(Tok.getLoc(), + diag::originally_defined_in_unrecognized_platform); + return makeParserError(); + } else { + consumeToken(); + Platform = *Plat; + } + // Parse version number + llvm::VersionTuple VerTuple; + SourceRange VersionRange; + if (parseVersionTuple(VerTuple, VersionRange, + Diagnostic(diag::attr_availability_expected_version, AttrName))) { + return makeParserError(); + } else { + if (VerTuple.getSubminor().hasValue() || + VerTuple.getBuild().hasValue()) { + diagnose(Tok.getLoc(), diag::originally_defined_in_major_minor_only); + } + // * as platform name isn't supported. + if (Platform == PlatformKind::none) { + diagnose(AtLoc, diag::originally_defined_in_missing_platform_name); + } else { + PlatformAndVersions.emplace_back(Platform, VerTuple); + } + return makeParserSuccess(); + } + } + diagnose(AtLoc, diag::originally_defined_in_need_platform_version); + return makeParserError(); + } + } + }).isError()) { + return false; + } + if (OriginalModuleName.empty()) { + diagnose(AtLoc, diag::originally_defined_in_need_nonempty_module_name); + return false; + } + if (PlatformAndVersions.empty()) { + diagnose(AtLoc, diag::originally_defined_in_need_platform_version); + return false; + } + assert(!OriginalModuleName.empty()); + assert(!PlatformAndVersions.empty()); + assert(NK == NextSegmentKind::PlatformVersion); + AttrRange = SourceRange(Loc, Tok.getLoc()); + for (auto &Item: PlatformAndVersions) { + Attributes.add(new (Context) OriginallyDefinedInAttr(AtLoc, AttrRange, + OriginalModuleName, + Item.first, + Item.second, + /*IsImplicit*/false)); + } + break; + } case DAK_Available: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -1404,7 +2127,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, } SourceLoc LParenLoc = consumeToken(tok::l_paren); - DeclName replacedFunction; + DeclNameRef replacedFunction; { SyntaxParsingContext ContentContext( SyntaxContext, SyntaxKind::NamedAttributeStringArgument); @@ -1427,10 +2150,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, SyntaxKind::DeclName); DeclNameLoc loc; - replacedFunction = parseUnqualifiedDeclName( - true, loc, diag::attr_dynamic_replacement_expected_function, - /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true, - /*allowDeinitAndSubscript*/ true); + replacedFunction = parseDeclNameRef(loc, + diag::attr_dynamic_replacement_expected_function, + DeclNameFlag::AllowZeroArgCompoundNames | + DeclNameFlag::AllowKeywordsUsingSpecialNames | + DeclNameFlag::AllowOperators); } } @@ -1472,6 +2196,35 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_Differentiable: { + auto Attr = parseDifferentiableAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + + case DAK_Derivative: { + // `@derivative` in a local scope is not allowed. + if (CurDeclContext->isLocalContext()) + diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str()); + + auto Attr = parseDerivativeAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + + case DAK_Transpose: { + // `@transpose` in a local scope is not allowed. + if (CurDeclContext->isLocalContext()) + diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str()); + + auto Attr = parseTransposeAttribute(AtLoc, Loc); + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } + case DAK_ProjectedValueProperty: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -1632,9 +2385,6 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At } consumeToken(tok::code_complete); return makeParserCodeCompletionStatus(); - } else { - // Synthesize an r_brace syntax node if the token is absent - SyntaxContext->synthesize(tok::identifier, AtLoc.getAdvancedLoc(1)); } diagnose(Tok, diag::expected_attribute_name); @@ -1784,7 +2534,8 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At status |= parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/false, /*isExprBasic=*/true, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosure); + rParenLoc, trailingClosure, + SyntaxKind::TupleExprElementList); assert(!trailingClosure && "Cannot parse a trailing closure here"); hasInitializer = true; } @@ -1806,340 +2557,370 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At return makeParserError(); } -/// Parses specifier and attributes for types. -/// -/// specifier: -/// 'inout' -/// '__shared' -/// '__owned' +bool Parser::canParseTypeAttribute() { + TypeAttributes attrs; // ignored + return !parseTypeAttribute(attrs, /*atLoc=*/SourceLoc(), + /*justChecking*/ true); +} + +/// Parses the '@differentiable' argument (no argument list, or '(linear)'), +/// and sets the appropriate fields on `Attributes`. /// -/// attribute-list: -/// attribute-type (',' attribute-type)? -ParserStatus Parser::parseTypeAttributeListSyntax( - Optional &specifier, - Optional &attrs) { - // Parser a specifier. - while (Tok.is(tok::kw_inout) || - (Tok.is(tok::identifier) && - (Tok.getRawText().equals("__shared") || - Tok.getRawText().equals("__owned")))) { - if (specifier) { - diagnose(Tok, diag::parameter_specifier_repeated) - .fixItRemove(Tok.getLoc()); - ignoreToken(); - } else { - specifier = consumeTokenSyntax(); +/// \param emitDiagnostics - if false, doesn't emit diagnostics +/// \returns true on error, false on success +static bool parseDifferentiableAttributeArgument(Parser &P, + TypeAttributes &Attributes, + bool emitDiagnostics) { + Parser::BacktrackingScope backtrack(P); + + // Match '( )', and store the identifier token to `argument`. + if (!P.consumeIf(tok::l_paren)) + return false; + auto argument = P.Tok; + if (!P.consumeIf(tok::identifier)) + return false; + if (!P.consumeIf(tok::r_paren)) { + // Special case handling for '( (' so that we don't produce the + // misleading diagnostic "expected ',' separator" when the real issue is + // that the user forgot the ')' closing the '@differentiable' argument list. + if (P.Tok.is(tok::l_paren)) { + backtrack.cancelBacktrack(); + if (emitDiagnostics) + P.diagnose(P.Tok, diag::attr_expected_rparen, "@differentiable", + /*DeclModifier*/ false); + return true; } + return false; } - ParserStatus status; - SmallVector attrsList; - while (Tok.is(tok::at_sign) && status.isSuccess()) { - auto attr = parseTypeAttributeSyntax(); - status |= attr.getStatus(); - if (!attr.isNull()) - attrsList.emplace_back(attr.get()); - } - if (!attrsList.empty()) - attrs = ParsedSyntaxRecorder::makeAttributeList(attrsList, *SyntaxContext); + // If the next token is not a `(`, `@`, or an identifier, then the + // matched '( )' is actually the parameter type list, + // not an argument to '@differentiable'. + if (P.Tok.isNot(tok::l_paren, tok::at_sign, tok::identifier)) + return false; - return status; -} + backtrack.cancelBacktrack(); + + if (argument.getText() != "linear") { + if (emitDiagnostics) + P.diagnose(argument, diag::attr_differentiable_unexpected_argument, + argument.getText()); + return true; + } -bool Parser::parseTypeAttributeList(ParamDecl::Specifier &Specifier, - SourceLoc &SpecifierLoc, - TypeAttributes &Attributes) { - SourceLoc leadingLoc = leadingTriviaLoc(); - Optional parsedSpecifier; - Optional parsedAttrs; - auto status = parseTypeAttributeListSyntax(parsedSpecifier, parsedAttrs); - - if (parsedSpecifier) { - SyntaxContext->addSyntax(std::move(*parsedSpecifier)); - auto tok = SyntaxContext->topNode(); - Specifier = llvm::StringSwitch(Tok.getText()) - .Case("inout", ParamDecl::Specifier::InOut) - .Case("__shared", ParamDecl::Specifier::Shared) - .Case("__owned", ParamDecl::Specifier::Owned) - .Default(ParamDecl::Specifier::Default); - SpecifierLoc = Generator.generate(tok, leadingLoc); - - leadingLoc = leadingLoc.getAdvancedLoc(tok.getTextLength()); - } - - if (parsedAttrs) { - SyntaxContext->addSyntax(std::move(*parsedAttrs)); - auto syntax = SyntaxContext->topNode(); - Attributes = Generator.generateTypeAttributes(syntax, leadingLoc); - } - return status.isError(); + Attributes.linear = true; + return false; } -/// Parses an attribute for types. +/// Parse the inside of a convention attribute '(...)'. /// -/// attribute-type: -/// '@' identifier -/// '@' 'convention' '(' identifier ')' -/// '@' 'convention' '(' 'witness_method' ':' identifier ')' -/// '@' 'opened' '(' string-literal ')' -/// '@' '_opaqureResultTypeOf' '(' string-literal ',' integer-literal ')' -ParsedSyntaxResult Parser::parseTypeAttributeSyntax() { - ParsedAttributeSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; +/// The '@convention' prefix should've been parsed by the caller. +/// See `Parser::parseTypeAttribute` for the justChecking argument. +/// +/// Returns true if there was an error. +bool Parser::parseConventionAttributeInternal( + bool justChecking, TypeAttributes::Convention &convention) { + SourceLoc LPLoc; + if (!consumeIfNotAtStartOfLine(tok::l_paren)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_expected_lparen); + return true; + } + + if (Tok.isNot(tok::identifier)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_expected_name); + return true; + } - // Parse '@'. - auto atLoc = Tok.getLoc(); - builder.useAtSignToken(consumeTokenSyntax(tok::at_sign)); + convention.Name = Tok.getText(); + consumeToken(tok::identifier); - // Parse attribute name. - if (Tok.isNot(tok::identifier, tok::kw_in, tok::kw_inout)) { - diagnose(Tok, diag::expected_attribute_name); - if (Tok.is(tok::code_complete)) { - // TODO: Implement type attribute completion. + // Consume extra (optional) ', cType: " blah blah "' + if (consumeIf(tok::comma)) { + if (Tok.isNot(tok::identifier)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_label); + return true; + } + auto cTypeLabel = Tok.getText(); + consumeToken(tok::identifier); + if (cTypeLabel != "cType") { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_label); + return true; + } + if (!consumeIf(tok::colon)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_colon); + return true; + } + if (Tok.isNot(tok::string_literal)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_string); + return true; } - return makeParsedError(builder.build()); + if (auto ty = getStringLiteralIfNotInterpolated(Tok.getLoc(), "(C type)")) { + convention.ClangType = { ty.getValue(), Tok.getLoc() }; + } + consumeToken(tok::string_literal); } - StringRef attrName = Tok.getText(); - builder.useAttributeName(consumeTokenSyntax()); - TypeAttrKind attr = TypeAttributes::getAttrKindFromString(attrName); - switch (attr) { - case TAK_out: - case TAK_in: - case TAK_owned: - case TAK_unowned_inner_pointer: - case TAK_guaranteed: - case TAK_autoreleased: - case TAK_callee_owned: - case TAK_callee_guaranteed: - case TAK_objc_metatype: - case TAK_sil_weak: - case TAK_sil_unowned: - if (!isInSILMode()) { - diagnose(atLoc, diag::only_allowed_in_sil, attrName); - status.setIsParseError(); + if (convention.Name == "witness_method") { + if (!consumeIf(tok::colon)) { + if (!justChecking) + diagnose(Tok, + diag::convention_attribute_witness_method_expected_colon); + return true; } - break; - case TAK_Count: { - auto declAttrID = DeclAttribute::getAttrKindFromString(attrName); + DeclNameLoc unusedLoc; + convention.WitnessMethodProtocol = parseDeclNameRef(unusedLoc, + diag::convention_attribute_witness_method_expected_protocol, {}); + } + + // Parse the ')'. We can't use parseMatchingToken if we're in + // just-checking mode. + if (justChecking && Tok.isNot(tok::r_paren)) + return true; + + SourceLoc RPLoc; + parseMatchingToken(tok::r_paren, RPLoc, + diag::convention_attribute_expected_rparen, + LPLoc); + return false; +} + +/// \verbatim +/// attribute-type: +/// 'noreturn' +/// \endverbatim +/// +/// \param justChecking - if true, we're just checking whether we +/// canParseTypeAttribute; don't emit any diagnostics, and there's +/// no need to actually record the attribute +bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, + bool justChecking) { + // If this not an identifier, the attribute is malformed. + if (Tok.isNot(tok::identifier) && + // These are keywords that we accept as attribute names. + Tok.isNot(tok::kw_in) && Tok.isNot(tok::kw_inout)) { + if (!justChecking) + diagnose(Tok, diag::expected_attribute_name); + return true; + } + + // Determine which attribute it is, and diagnose it if unknown. + TypeAttrKind attr = TypeAttributes::getAttrKindFromString(Tok.getText()); + + if (attr == TAK_Count) { + if (justChecking) return true; + + auto declAttrID = DeclAttribute::getAttrKindFromString(Tok.getText()); if (declAttrID == DAK_Count) { // Not a decl or type attribute. - diagnose(Tok, diag::unknown_attribute, attrName); + diagnose(Tok, diag::unknown_attribute, Tok.getText()); } else { // Otherwise this is a valid decl attribute so they should have put it on // the decl instead of the type. - diagnose(Tok, diag::decl_attribute_applied_to_type); + + // If this is the first attribute, and if we are on a simple decl, emit a + // fixit to move the attribute. Otherwise, we don't have the location of + // the @ sign, or we don't have confidence that the fixit will be right. + if (!Attributes.empty() || StructureMarkers.empty() || + StructureMarkers.back().Kind != StructureMarkerKind::Declaration || + StructureMarkers.back().Loc.isInvalid() || + peekToken().is(tok::equal)) { + diagnose(Tok, diag::decl_attribute_applied_to_type); + } else { + // Otherwise, this is the first type attribute and we know where the + // declaration is. Emit the same diagnostic, but include a fixit to + // move the attribute. Unfortunately, we don't have enough info to add + // the attribute to DeclAttributes. + diagnose(Tok, diag::decl_attribute_applied_to_type) + .fixItRemove(SourceRange(Attributes.AtLoc, Tok.getLoc())) + .fixItInsert(StructureMarkers.back().Loc, + "@" + Tok.getText().str()+" "); + } } + // Recover by eating @foo(...) when foo is not known. + consumeToken(); + SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList); + if (Tok.is(tok::l_paren) && getEndOfPreviousLoc() == Tok.getLoc()) { BacktrackingScope backtrack(*this); - auto lParen = consumeTokenSyntax(tok::l_paren); - ignoreUntil(tok::r_paren); - auto rParen = consumeTokenSyntaxIf(tok::r_paren); - + skipSingle(); // If we found '->', or 'throws' after paren, it's likely a parameter // of function type. if (Tok.isNot(tok::arrow, tok::kw_throws, tok::kw_rethrows, - tok::kw_throw)) { + tok::kw_throw)) backtrack.cancelBacktrack(); - builder.useLeftParen(std::move(lParen)); - if (rParen) - builder.useRightParen(std::move(*rParen)); - } } - status.setIsParseError(); - break; + return true; + } + + // Ok, it is a valid attribute, eat it, and then process it. + StringRef Text = Tok.getText(); + consumeToken(); + + TypeAttributes::Convention convention; + if (attr == TAK_convention) { + bool failedToParse = + parseConventionAttributeInternal(justChecking, convention); + if (failedToParse) { + if (Tok.is(tok::r_paren)) + consumeToken(); + return true; + } } - case TAK_convention: - status |= [&]() -> ParserStatus { - // Parse '('. - if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { - diagnose(Tok, diag::convention_attribute_expected_lparen); - return makeParserError(); - } - SourceLoc LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - // Parse convention name. - if (Tok.isNot(tok::identifier)) { - diagnose(Tok, diag::convention_attribute_expected_name); - return makeParserError(); - } - auto conventionName = Tok.getText(); - auto convention = consumeTokenSyntax(tok::identifier); - - // Parse convention name. - if (conventionName == "witness_method") { - ParsedNamedAttributeStringArgumentSyntaxBuilder argBuilder( - *SyntaxContext); - argBuilder.useNameTok(std::move(convention)); - - // Parse ':'. - if (Tok.isNot(tok::colon)) { - diagnose(Tok, - diag::convention_attribute_witness_method_expected_colon); - builder.useArgument(argBuilder.build()); - return makeParserError(); - } - argBuilder.useColon(consumeTokenSyntax(tok::colon)); - - // Parse protocol name. - if (Tok.isNot(tok::identifier)) { - diagnose(Tok, - diag::convention_attribute_witness_method_expected_protocol); - builder.useArgument(argBuilder.build()); - return makeParserError(); - } - auto name = ParsedSyntaxRecorder::makeDeclName( - consumeTokenSyntax(tok::identifier), None, *SyntaxContext); - argBuilder.useStringOrDeclname(std::move(name)); - builder.useArgument(argBuilder.build()); - } else { - builder.useArgument(std::move(convention)); - } + // In just-checking mode, we only need to consume the tokens, and we don't + // want to do any other analysis. + if (justChecking) + return false; - // Parse ')'. - auto RParen = parseMatchingTokenSyntax( - tok::r_paren, diag::convention_attribute_expected_rparen, LParenLoc); - if (RParen.isError()) - return makeParserError(); - builder.useRightParen(RParen.get()); + // Diagnose duplicated attributes. + if (Attributes.has(attr)) { + diagnose(AtLoc, diag::duplicate_attribute, /*isModifier=*/false); + return false; + } - return makeParserSuccess(); - }(); + // Handle any attribute-specific processing logic. + switch (attr) { + default: break; + case TAK_autoclosure: + case TAK_escaping: + case TAK_noescape: break; - case TAK_opened: - status |= [&]() -> ParserStatus { - if (!isInSILMode()) { - diagnose(atLoc, diag::only_allowed_in_sil, "opened"); - return makeParserError(); - } - - // Parse '('. - if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { - diagnose(Tok, diag::opened_attribute_expected_lparen); - return makeParserError(); - } - SourceLoc LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - if (!Tok.is(tok::string_literal)) { - diagnose(Tok, diag::opened_attribute_id_value); - return makeParserError(); - } - builder.useArgument(consumeTokenSyntax(tok::string_literal)); - - // Parse ')'. - auto RParen = parseMatchingTokenSyntax( - tok::r_paren, diag::opened_attribute_expected_rparen, LParenLoc); - if (RParen.isError()) - return makeParserError(); - builder.useRightParen(RParen.get()); - - return makeParserSuccess(); - }(); + case TAK_out: + case TAK_in: + case TAK_owned: + case TAK_unowned_inner_pointer: + case TAK_guaranteed: + case TAK_autoreleased: + case TAK_callee_owned: + case TAK_callee_guaranteed: + case TAK_objc_metatype: + if (!isInSILMode()) { + diagnose(AtLoc, diag::only_allowed_in_sil, Text); + return false; + } + break; + + // Ownership attributes. + case TAK_sil_weak: + case TAK_sil_unowned: + if (!isInSILMode()) { + diagnose(AtLoc, diag::only_allowed_in_sil, Text); + return false; + } + + if (Attributes.hasOwnership()) { + diagnose(AtLoc, diag::duplicate_attribute, /*isModifier*/false); + return false; + } break; - case TAK__opaqueReturnTypeOf: - status |= [&]() -> ParserStatus { - // Parse '('. - if (!Tok.is(tok::l_paren) || Tok.isAtStartOfLine()) { - diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false); - return makeParserError(); - } - SourceLoc LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - ParsedOpaqueReturnTypeOfAttributeArgumentsSyntaxBuilder argBuilder( - *SyntaxContext); + // 'inout' attribute. + case TAK_inout: + if (!isInSILMode()) { + diagnose(AtLoc, diag::inout_not_attribute); + return false; + } + break; + + case TAK_opened: { + if (!isInSILMode()) { + diagnose(AtLoc, diag::only_allowed_in_sil, "opened"); + return false; + } - // Parse the mangled decl name and index. - if (!Tok.is(tok::string_literal)) { + // Parse the opened existential ID string in parens + SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc; + if (consumeIfNotAtStartOfLine(tok::l_paren)) { + if (Tok.is(tok::string_literal)) { + UUID openedID; + idLoc = Tok.getLoc(); + auto literalText = Tok.getText().slice(1, Tok.getText().size() - 1); + llvm::SmallString text(literalText); + if (auto openedID = UUID::fromString(text.c_str())) { + Attributes.OpenedID = openedID; + } else { + diagnose(Tok, diag::opened_attribute_id_value); + } + consumeToken(); + } else { diagnose(Tok, diag::opened_attribute_id_value); - return makeParserError(); } - argBuilder.useMangledName(consumeTokenSyntax(tok::string_literal)); - - // Parse ','. - if (!Tok.is(tok::comma)) { - diagnose(Tok, diag::attr_expected_comma, "_opaqueReturnTypeOf", false); - builder.useArgument(builder.build()); - return makeParserError(); - } - argBuilder.useComma(consumeTokenSyntax(tok::comma)); - - // Parse index number. - if (!Tok.is(tok::integer_literal)) { - diagnose(Tok, diag::attr_expected_string_literal, - "_opaqueReturnTypeOf"); - builder.useArgument(builder.build()); - return makeParserError(); - } - argBuilder.useIndex(consumeTokenSyntax(tok::integer_literal)); - - builder.useArgument(argBuilder.build()); - - // Parse ')'. - auto RParen = parseMatchingTokenSyntax( - tok::r_paren, diag::expected_rparen_expr_list, LParenLoc); - if (RParen.isError()) - return makeParserError(); - builder.useRightParen(RParen.get()); + parseMatchingToken(tok::r_paren, endLoc, + diag::opened_attribute_expected_rparen, + beginLoc); + } else { + diagnose(Tok, diag::opened_attribute_expected_lparen); + } - return makeParserSuccess(); - }(); break; + } - default: + case TAK_differentiable: { + if (parseDifferentiableAttributeArgument(*this, Attributes, + /*emitDiagnostics=*/!justChecking)) + return true; break; } - return makeParsedResult(builder.build(), status); -} -bool Parser::canParseTypeAttribute() { - if (!Tok.isAny(tok::identifier, tok::kw_in, tok::kw_inout)) - return false; - - TypeAttrKind attr = TypeAttributes::getAttrKindFromString(Tok.getText()); - consumeToken(); - - switch (attr) { - case TAK_Count: - return false; - case TAK_convention: { - if (!consumeIf(tok::l_paren)) - return false; - if (!Tok.is(tok::identifier)) - return false; - auto name = Tok.getText(); - consumeToken(tok::identifier); - if (name == "witness_method") { - consumeToken(); - if (!consumeIf(tok::colon)) - return false; - if (!consumeIf(tok::identifier)) - return false; + // Convention attribute. + case TAK_convention: + Attributes.ConventionArguments = convention; + break; + + case TAK__opaqueReturnTypeOf: { + // Parse the mangled decl name and index. + auto beginLoc = Tok.getLoc(); + if (!consumeIfNotAtStartOfLine(tok::l_paren)) { + diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false); + return true; } - if (!consumeIf(tok::r_paren)) - return false; - return true; + + if (!Tok.is(tok::string_literal)) { + diagnose(Tok, diag::opened_attribute_id_value); + return true; + } + auto mangling = Tok.getText().slice(1, Tok.getText().size() - 1); + consumeToken(tok::string_literal); + + if (!Tok.is(tok::comma)) { + diagnose(Tok, diag::attr_expected_comma, "_opaqueReturnTypeOf", false); + return true; + } + consumeToken(tok::comma); + + if (!Tok.is(tok::integer_literal)) { + diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf"); + return true; + } + + unsigned index; + if (Tok.getText().getAsInteger(10, index)) { + diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf"); + return true; + } + consumeToken(tok::integer_literal); + + SourceLoc endLoc; + parseMatchingToken(tok::r_paren, endLoc, + diag::expected_rparen_expr_list, + beginLoc); + + Attributes.setOpaqueReturnTypeOf(mangling, index); + break; } - case TAK_opened: - return (consumeIf(tok::l_paren) && // '(' - consumeIf(tok::string_literal) && // UUID - consumeIf(tok::r_paren)); // ')' - case TAK__opaqueReturnTypeOf: - return (consumeIf(tok::l_paren) && // '(' - consumeIf(tok::string_literal) && // Mangled name - consumeIf(tok::comma) && // ',' - consumeIf(tok::integer_literal) && // Index - consumeIf(tok::r_paren)); // ')' - default: - return true; } + + Attributes.setAttr(attr, AtLoc); + return false; } /// \verbatim @@ -2300,13 +3081,67 @@ bool Parser::parseDeclModifierList(DeclAttributes &Attributes, break; } - // If we don't have any modifiers, don't bother to construct an empty list. - if (!hasModifier) - ListContext.setTransparent(); - - // If we 'break' out of the switch, modifier list has ended. - return isError; + // If we don't have any modifiers, don't bother to construct an empty list. + if (!hasModifier) + ListContext.setTransparent(); + + // If we 'break' out of the switch, modifier list has ended. + return isError; + } +} + +/// This is the internal implementation of \c parseTypeAttributeList, +/// which we expect to be inlined to handle the common case of an absent +/// attribute list. +/// +/// \verbatim +/// attribute-list: +/// /*empty*/ +/// attribute-list-clause attribute-list +/// 'inout' attribute-list-clause attribute-list +/// '__shared' attribute-list-clause attribute-list +/// '__owned' attribute-list-clause attribute-list +/// 'some' attribute-list-clause attribute-list +/// attribute-list-clause: +/// '@' attribute +/// '@' attribute attribute-list-clause +/// \endverbatim +bool Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, + SourceLoc &SpecifierLoc, + TypeAttributes &Attributes) { + Specifier = ParamDecl::Specifier::Default; + while (Tok.is(tok::kw_inout) || + (Tok.is(tok::identifier) && + (Tok.getRawText().equals("__shared") || + Tok.getRawText().equals("__owned")))) { + if (SpecifierLoc.isValid()) { + diagnose(Tok, diag::parameter_specifier_repeated) + .fixItRemove(SpecifierLoc); + } else { + if (Tok.is(tok::kw_inout)) { + Specifier = ParamDecl::Specifier::InOut; + } else if (Tok.is(tok::identifier)) { + if (Tok.getRawText().equals("__shared")) { + Specifier = ParamDecl::Specifier::Shared; + } else if (Tok.getRawText().equals("__owned")) { + Specifier = ParamDecl::Specifier::Owned; + } + } + } + SpecifierLoc = consumeToken(); + } + + SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList); + while (Tok.is(tok::at_sign)) { + if (Attributes.AtLoc.isInvalid()) + Attributes.AtLoc = Tok.getLoc(); + SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute); + SourceLoc AtLoc = consumeToken(); + if (parseTypeAttribute(Attributes, AtLoc)) + return true; } + + return false; } static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) { @@ -2364,21 +3199,11 @@ static void diagnoseOperatorFixityAttributes(Parser &P, static unsigned skipUntilMatchingRBrace(Parser &P, bool &HasPoundDirective, bool &HasOperatorDeclarations, - bool &HasNestedClassDeclarations, - SyntaxParsingContext *&SyntaxContext) { + bool &HasNestedClassDeclarations) { HasPoundDirective = false; HasOperatorDeclarations = false; HasNestedClassDeclarations = false; - bool isRootCtx = SyntaxContext->isRoot(); - SyntaxParsingContext BlockItemListContext(SyntaxContext, - SyntaxKind::CodeBlockItemList); - if (isRootCtx) { - BlockItemListContext.setTransparent(); - } - SyntaxParsingContext BlockItemContext(SyntaxContext, - SyntaxKind::CodeBlockItem); - SyntaxParsingContext BodyContext(SyntaxContext, SyntaxKind::TokenList); unsigned OpenBraces = 1; bool LastTokenWasFunc = false; @@ -2565,16 +3390,18 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, ParseDeclOptions Flags, bool IsTopLevel) { SyntaxParsingContext Discarding(SyntaxContext); - Discarding.setDiscard(); + Discarding.disable(); SourceLoc CurrentLoc = Tok.getLoc(); SourceLoc EndLoc = PreviousLoc; backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); - State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, Flags.toRaw(), - CurDeclContext, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); + State->setCodeCompletionDelayedDeclState( + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::Decl, + Flags.toRaw(), CurDeclContext, {BeginLoc, EndLoc}, + BeginParserPosition.PreviousLoc); while (SourceMgr.isBeforeInBuffer(Tok.getLoc(), CurrentLoc)) consumeToken(); @@ -2609,19 +3436,16 @@ void Parser::setLocalDiscriminatorToParamList(ParameterList *PL) { } } -void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition, - ParseDeclOptions Flags) { - auto CurLoc = Tok.getLoc(); - backtrackToPosition(BeginParserPosition); - SourceLoc BeginLoc = Tok.getLoc(); - SourceLoc EndLoc = CurLoc; - State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, - Flags.toRaw(), - CurDeclContext, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); - - while (Tok.isNot(tok::eof)) - consumeToken(); +/// Set the original declaration in `@differentiable` attributes. +/// +/// Necessary because `Parser::parseNewDeclAttribute` (which calls +/// `Parser::parseDifferentiableAttribute`) does not have access to the +/// parent declaration of parsed attributes. +static void +setOriginalDeclarationForDifferentiableAttributes(DeclAttributes attrs, + Decl *D) { + for (auto *attr : attrs.getAttributes()) + const_cast(attr)->setOriginalDeclaration(D); } /// Parse a single syntactic declaration and return a list of decl @@ -2704,14 +3528,12 @@ Parser::parseDecl(ParseDeclOptions Flags, SyntaxParsingContext DeclParsingContext(SyntaxContext, SyntaxContextKind::Decl); - SourceLoc leadingLoc = leadingTriviaLoc(); // Note that we're parsing a declaration. StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), StructureMarkerKind::Declaration); // Parse attributes. - SourceLoc AttrsLoc = Tok.getLoc(); DeclAttributes Attributes; if (Tok.hasComment()) Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange())); @@ -2723,9 +3545,6 @@ Parser::parseDecl(ParseDeclOptions Flags, StaticSpellingKind StaticSpelling = StaticSpellingKind::None; parseDeclModifierList(Attributes, StaticLoc, StaticSpelling); - if (!Attributes.isEmpty()) - Generator.addDeclAttributes(Attributes, AttrsLoc); - // We emit diagnostics for 'try let ...' in parseDeclVar(). SourceLoc tryLoc; if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var)) @@ -2776,11 +3595,13 @@ Parser::parseDecl(ParseDeclOptions Flags, break; } case tok::kw_typealias: - DeclResult = parseDeclTypeAlias(Flags, Attributes, leadingLoc); + DeclParsingContext.setCreateSyntax(SyntaxKind::TypealiasDecl); + DeclResult = parseDeclTypeAlias(Flags, Attributes); MayNeedOverrideCompletion = true; break; case tok::kw_associatedtype: - DeclResult = parseDeclAssociatedType(Flags, Attributes, leadingLoc); + DeclParsingContext.setCreateSyntax(SyntaxKind::AssociatedtypeDecl); + DeclResult = parseDeclAssociatedType(Flags, Attributes); break; case tok::kw_enum: DeclParsingContext.setCreateSyntax(SyntaxKind::EnumDecl); @@ -2948,12 +3769,12 @@ Parser::parseDecl(ParseDeclOptions Flags, if (CurDeclContext) { if (auto nominal = dyn_cast(CurDeclContext)) { diagnose(nominal->getLoc(), diag::note_in_decl_extension, false, - nominal->getName()); + nominal->createNameRef()); } else if (auto extension = dyn_cast(CurDeclContext)) { if (auto repr = extension->getExtendedTypeRepr()) { if (auto idRepr = dyn_cast(repr)) { diagnose(extension->getLoc(), diag::note_in_decl_extension, true, - idRepr->getComponentRange().front()->getIdentifier()); + idRepr->getComponentRange().front()->getNameRef()); } } } @@ -2996,26 +3817,23 @@ Parser::parseDecl(ParseDeclOptions Flags, consumeToken(tok::code_complete); } - if (AttrStatus.hasCodeCompletion()) { - if (CodeCompletion) { + if (AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion()) { + if (isCodeCompletionFirstPass() && + !CurDeclContext->isModuleScopeContext() && + !isa(CurDeclContext) && + !isa(CurDeclContext)) { + // Only consume non-toplevel decls. + consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false); + + return makeParserError(); + } + if (AttrStatus.hasCodeCompletion() && CodeCompletion) { Optional DK; if (DeclResult.isNonNull()) DK = DeclResult.get()->getKind(); CodeCompletion->setAttrTargetDeclKind(DK); - } else { - delayParseFromBeginningToHere(BeginParserPosition, Flags); - return makeParserError(); } - } - - if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass() && - !CurDeclContext->isModuleScopeContext() && - !isa(CurDeclContext) && - !isa(CurDeclContext)) { - // Only consume non-toplevel decls. - consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false); - - return makeParserError(); + DeclResult.setHasCodeCompletion(); } if (auto SF = CurDeclContext->getParentSourceFile()) { @@ -3030,7 +3848,8 @@ Parser::parseDecl(ParseDeclOptions Flags, if (DeclResult.isNonNull()) { Decl *D = DeclResult.get(); if (!declWasHandledAlready(D)) - Handler(DeclResult.get()); + Handler(D); + setOriginalDeclarationForDifferentiableAttributes(D->getAttrs(), D); } if (!DeclResult.isParseError()) { @@ -3093,7 +3912,8 @@ static ScopeKind getMemberParseScopeKind(IterableDeclContext *idc) { } } -std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { +std::pair, Optional> +Parser::parseDeclListDelayed(IterableDeclContext *IDC) { Decl *D = const_cast(IDC->getDecl()); DeclContext *DC = cast(D); SourceRange BodyRange; @@ -3106,10 +3926,10 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { if (BodyRange.isInvalid()) { assert(D->isImplicit()); - return { }; + return {std::vector(), None}; } - auto BeginParserPosition = getParserPosition({BodyRange.Start,BodyRange.End}); + auto BeginParserPosition = getParserPosition({BodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(BodyRange.End); // ParserPositionRAII needs a primed parser to restore to. @@ -3132,7 +3952,7 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { // If there is no left brace, then return an empty list of declarations; // we will have already diagnosed this. if (!Tok.is(tok::l_brace)) - return { }; + return {std::vector(), None}; // Re-enter the lexical scope. The top-level scope is needed because // delayed parsing of members happens with a fresh parser, where there is @@ -3160,48 +3980,6 @@ std::vector Parser::parseDeclListDelayed(IterableDeclContext *IDC) { return parseDeclList(LBLoc, RBLoc, Id, Options, IDC, hadError); } -void Parser::parseDeclDelayed() { - auto DelayedState = State->takeDelayedDeclState(); - assert(DelayedState.get() && "should have delayed state"); - - auto BeginParserPosition = getParserPosition(DelayedState->BodyPos); - auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd); - - // ParserPositionRAII needs a primed parser to restore to. - if (Tok.is(tok::NUM_TOKENS)) - consumeTokenWithoutFeedingReceiver(); - - // Ensure that we restore the parser state at exit. - ParserPositionRAII PPR(*this); - - // Create a lexer that cannot go past the end state. - Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); - - // Temporarily swap out the parser's current lexer with our new one. - llvm::SaveAndRestore T(L, &LocalLex); - - // Rewind to the beginning of the decl. - restoreParserPosition(BeginParserPosition); - - // Re-enter the lexical scope. - Scope S(this, DelayedState->takeScope()); - ContextChange CC(*this, DelayedState->ParentContext); - - parseDecl(ParseDeclOptions(DelayedState->Flags), - /*IsAtStartOfLineOrPreviousHadSemi=*/true, - [&](Decl *D) { - if (auto *parent = DelayedState->ParentContext) { - if (auto *NTD = dyn_cast(parent)) { - NTD->addMember(D); - } else if (auto *ED = dyn_cast(parent)) { - ED->addMember(D); - } else if (auto *SF = dyn_cast(parent)) { - SF->Decls.push_back(D); - } - } - }); -} - /// Parse an 'import' declaration, doing no token skipping on error. /// /// \verbatim @@ -3263,7 +4041,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, KindLoc = consumeToken(); } - std::vector> ImportPath; + std::vector> ImportPath; bool HasNext; do { SyntaxParsingContext AccessCompCtx(SyntaxContext, @@ -3275,8 +4053,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } return makeParserCodeCompletionStatus(); } - ImportPath.push_back(std::make_pair(Identifier(), Tok.getLoc())); - if (parseAnyIdentifier(ImportPath.back().first, + ImportPath.push_back({Identifier(), Tok.getLoc()}); + if (parseAnyIdentifier(ImportPath.back().Item, diag::expected_identifier_in_decl, "import")) return nullptr; HasNext = consumeIf(tok::period); @@ -3289,8 +4067,8 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, // We omit the code completion token if it immediately follows the module // identifiers. auto BufferId = SourceMgr.getCodeCompletionBufferID(); - auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().second, - BufferId) + ImportPath.back().first.str().size(); + auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().Loc, + BufferId) + ImportPath.back().Item.str().size(); auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr. getCodeCompletionLoc(), BufferId); if (IdEndOffset == CCTokenOffset) { @@ -3299,7 +4077,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, } if (Kind != ImportKind::Module && ImportPath.size() == 1) { - diagnose(ImportPath.front().second, diag::decl_expected_module_name); + diagnose(ImportPath.front().Loc, diag::decl_expected_module_name); return nullptr; } @@ -3319,107 +4097,100 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, /// 'class' /// type-identifier /// \endverbatim +ParserStatus Parser::parseInheritance(SmallVectorImpl &Inherited, + bool allowClassRequirement, + bool allowAnyObject) { + SyntaxParsingContext InheritanceContext(SyntaxContext, + SyntaxKind::TypeInheritanceClause); -ParsedSyntaxResult -Parser::parseTypeInheritanceClauseSyntax(bool allowClassRequirement, - bool allowAnyObject) { - ParsedTypeInheritanceClauseSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; + Scope S(this, ScopeKind::InheritanceClause); + consumeToken(tok::colon); - builder.useColon(consumeTokenSyntax(tok::colon)); + SyntaxParsingContext TypeListContext(SyntaxContext, + SyntaxKind::InheritedTypeList); + SourceLoc classRequirementLoc; - SourceLoc startLoc = Tok.getLoc(); - SourceLoc classRequirementLoc, prevCommaLoc; - bool hasNext = true; + ParserStatus Status; + SourceLoc prevComma; + bool HasNextType; do { - ParsedInheritedTypeSyntaxBuilder elemBuilder(*SyntaxContext); - + SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::InheritedType); + SWIFT_DEFER { + // Check for a ',', which indicates that there are more protocols coming. + HasNextType = consumeIf(tok::comma, prevComma); + }; // Parse the 'class' keyword for a class requirement. if (Tok.is(tok::kw_class)) { - auto classLoc = Tok.getLoc(); - auto classTok = consumeTokenSyntax(tok::kw_class); - auto restriction = ParsedSyntaxRecorder::makeClassRestrictionType( - std::move(classTok), *SyntaxContext); - elemBuilder.useTypeName(std::move(restriction)); - + SyntaxParsingContext ClassTypeContext(SyntaxContext, + SyntaxKind::ClassRestrictionType); + // If we aren't allowed to have a class requirement here, complain. + auto classLoc = consumeToken(); if (!allowClassRequirement) { - // If we aren't allowed to have a class requirement here, complain. diagnose(classLoc, diag::unexpected_class_constraint); - // Note that it makes no sense to suggest fixing 'struct S : class' to - // 'struct S : AnyObject' for example; in that case we just complain - // about 'class' being invalid here. + // Note that it makes no sense to suggest fixing + // 'struct S : class' to 'struct S : AnyObject' for + // example; in that case we just complain about + // 'class' being invalid here. if (allowAnyObject) { diagnose(classLoc, diag::suggest_anyobject) - .fixItReplace(classLoc, "AnyObject"); + .fixItReplace(classLoc, "AnyObject"); } + continue; + } - } else if (classRequirementLoc.isValid()) { - // If we already saw a class requirement, complain. - diagnose(Tok.getLoc(), diag::redundant_class_requirement) + // If we already saw a class requirement, complain. + if (classRequirementLoc.isValid()) { + diagnose(classLoc, diag::redundant_class_requirement) .highlight(classRequirementLoc) - .fixItRemove(SourceRange(prevCommaLoc, classLoc)); + .fixItRemove(SourceRange(prevComma, classLoc)); + continue; + } - } else if (prevCommaLoc.isValid()) { - // If the class requirement was not the first requirement, complain. + // If the class requirement was not the first requirement, complain. + if (!Inherited.empty()) { + SourceLoc properLoc = Inherited[0].getSourceRange().Start; diagnose(classLoc, diag::late_class_requirement) - .fixItInsert(startLoc, "class, ") - .fixItRemove(SourceRange(prevCommaLoc, classLoc)); + .fixItInsert(properLoc, "class, ") + .fixItRemove(SourceRange(prevComma, classLoc)); } // Record the location of the 'class' keyword. - if (!classRequirementLoc.isValid()) - classRequirementLoc = classLoc; - } else { - // Parse inherited type. - auto inheritedType = parseTypeSyntax(); - status |= inheritedType.getStatus(); - if (!inheritedType.isNull()) - elemBuilder.useTypeName(inheritedType.get()); - else - elemBuilder.useTypeName( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - } + classRequirementLoc = classLoc; - // Parse ','. - if (Tok.is(tok::comma)) { - prevCommaLoc = Tok.getLoc(); - elemBuilder.useTrailingComma(consumeTokenSyntax(tok::comma)); - } else { - hasNext = false; + // Add 'AnyObject' to the inherited list. + Inherited.push_back( + new (Context) SimpleIdentTypeRepr(DeclNameLoc(classLoc), DeclNameRef( + Context.getIdentifier("AnyObject")))); + continue; } - builder.addInheritedTypeCollectionMember(elemBuilder.build()); - } while (hasNext); + auto ParsedTypeResult = parseType(); + Status |= ParsedTypeResult; - return makeParsedResult(builder.build(), status); -} + // Record the type if its a single type. + if (ParsedTypeResult.isNonNull()) + Inherited.push_back(ParsedTypeResult.get()); + } while (HasNextType); -ParserStatus Parser::parseInheritance(MutableArrayRef &Inherited, - bool allowClassRequirement, - bool allowAnyObject) { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseTypeInheritanceClauseSyntax(allowClassRequirement, - allowAnyObject); - SyntaxContext->addSyntax(parsed.get()); - auto clause = SyntaxContext->topNode(); - Inherited = Generator.generate(clause, leadingLoc, allowClassRequirement); - return parsed.getStatus(); + return Status; } -static ParsedSyntaxResult -parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, - llvm::function_ref canRecover) { +static ParserStatus +parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, + StringRef DeclKindName, + llvm::function_ref canRecover) { if (P.Tok.is(tok::identifier)) { - auto text = P.Tok.getText(); - auto loc = P.Tok.getLoc(); + Loc = P.consumeIdentifier(&Result); - auto tok = P.consumeIdentifierSyntax(); + // We parsed an identifier for the declaration. If we see another + // identifier, it might've been a single identifier that got broken by a + // space or newline accidentally. if (P.Tok.isIdentifierOrUnderscore() && !P.Tok.isContextualDeclKeyword()) - P.diagnoseConsecutiveIDs(text, loc, DeclKindName); + P.diagnoseConsecutiveIDs(Result.str(), Loc, DeclKindName); // Return success anyway - return makeParsedResult(std::move(tok)); + return makeParserSuccess(); } P.checkForInputIncomplete(); @@ -3433,8 +4204,12 @@ parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, // Pretend this works as an identifier, which shouldn't be observable since // actual uses of it will hit random other errors, e.g. `1()` won't be // callable. - P.Tok.setKind(tok::identifier); - return makeParsedResult(P.consumeTokenSyntax()); + Result = P.Context.getIdentifier(P.Tok.getText()); + Loc = P.Tok.getLoc(); + P.consumeToken(); + + // We recovered, so this is a success. + return makeParserSuccess(); } if (P.Tok.isKeyword()) { @@ -3444,8 +4219,14 @@ parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, // Recover if the next token is one of the expected tokens. if (canRecover(P.peekToken())) { - P.Tok.setKind(tok::identifier); - return makeParsedResult(P.consumeTokenSyntax()); + llvm::SmallString<32> Name(P.Tok.getText()); + // Append an invalid character so that nothing can resolve to this name. + Name += "#"; + Result = P.Context.getIdentifier(Name.str()); + Loc = P.Tok.getLoc(); + P.consumeToken(); + // Return success because we recovered. + return makeParserSuccess(); } return makeParserError(); } @@ -3454,20 +4235,6 @@ parseIdentifierDeclNameSyntax(Parser &P, StringRef DeclKindName, return makeParserError(); } -static ParserStatus -parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc, - StringRef DeclKindName, - llvm::function_ref canRecover) { - auto leadingLoc = P.leadingTriviaLoc(); - auto parsed = parseIdentifierDeclNameSyntax(P, DeclKindName, canRecover); - if (!parsed.isNull()) { - P.SyntaxContext->addSyntax(parsed.get()); - auto syntax = P.SyntaxContext->topNode(); - Loc = P.Generator.generateIdentifierDeclName(syntax, leadingLoc, Result); - } - return parsed.getStatus(); -} - /// Add a fix-it to remove the space in consecutive identifiers. /// Add a camel-cased option if it is different than the first option. void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, @@ -3476,8 +4243,7 @@ void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, diagnose(Tok, diag::repeated_identifier, DeclKindName); auto Second = Tok.getText(); - auto SecondLoc = Tok.getLoc(); - ignoreToken(); + auto SecondLoc = consumeToken(); SourceRange FixRange(FirstLoc, SecondLoc); // Provide two fix-its: a direct concatenation of the two identifiers @@ -3564,13 +4330,15 @@ bool Parser::parseMemberDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, // evaluator. bool hadError = false; ParseDeclOptions Options = getMemberParseDeclOptions(IDC); - auto members = parseDeclList( - LBLoc, RBLoc, ErrorDiag, Options, IDC, hadError); + auto membersAndHash = + parseDeclList(LBLoc, RBLoc, ErrorDiag, Options, IDC, hadError); IDC->setMaybeHasOperatorDeclarations(); IDC->setMaybeHasNestedClassDeclarations(); Context.evaluator.cacheOutput( ParseMembersRequest{IDC}, - Context.AllocateCopy(llvm::makeArrayRef(members))); + FingerprintAndMembers{ + membersAndHash.second, + Context.AllocateCopy(llvm::makeArrayRef(membersAndHash.first))}); if (hadError) return true; @@ -3584,10 +4352,22 @@ bool Parser::parseMemberDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, /// \verbatim /// decl* '}' /// \endverbatim -std::vector Parser::parseDeclList( - SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, - ParseDeclOptions Options, IterableDeclContext *IDC, - bool &hadError) { +std::pair, Optional> +Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, + ParseDeclOptions Options, IterableDeclContext *IDC, + bool &hadError) { + + // Record the curly braces but nothing inside. + if (IDC->areDependenciesUsingTokenHashesForTypeBodies()) { + recordTokenHash("{"); + recordTokenHash("}"); + } + llvm::MD5 tokenHashForThisDeclList; + llvm::SaveAndRestore> T( + CurrentTokenHash, IDC->areDependenciesUsingTokenHashesForTypeBodies() + ? &tokenHashForThisDeclList + : CurrentTokenHash); + std::vector decls; ParserStatus Status; bool PreviousHadSemi = true; @@ -3616,7 +4396,15 @@ std::vector Parser::parseDeclList( // were errors while parsing inner decls, because we recovered. if (RBLoc.isInvalid()) hadError = true; - return decls; + + if (!Context.LangOpts.EnableTypeFingerprints) + return std::make_pair(decls, None); + + llvm::MD5::MD5Result result; + tokenHashForThisDeclList.final(result); + llvm::SmallString<32> tokenHashString; + llvm::MD5::stringifyResult(result, tokenHashString); + return std::make_pair(decls, tokenHashString.str().str()); } bool Parser::canDelayMemberDeclParsing(bool &HasOperatorDeclarations, @@ -3636,8 +4424,7 @@ bool Parser::canDelayMemberDeclParsing(bool &HasOperatorDeclarations, skipUntilMatchingRBrace(*this, HasPoundDirective, HasOperatorDeclarations, - HasNestedClassDeclarations, - SyntaxContext); + HasNestedClassDeclarations); if (!HasPoundDirective) BackTrack.cancelBacktrack(); return !BackTrack.willBacktrack(); @@ -3653,8 +4440,6 @@ bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, RBLoc = Tok.getLoc(); error = true; } - - State->delayDeclList(IDC); return error; } @@ -3677,7 +4462,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { status |= extendedType; // Parse optional inheritance clause. - MutableArrayRef Inherited; + SmallVector Inherited; if (Tok.is(tok::colon)) status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, @@ -3704,7 +4489,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc, extendedType.getPtrOrNull(), - Inherited, + Context.AllocateCopy(Inherited), CurDeclContext, trailingWhereClause); ext->getAttrs() = Attributes; @@ -3970,240 +4755,207 @@ ParserStatus Parser::parseLineDirective(bool isLine) { /// Parse a typealias decl. /// +/// \verbatim /// decl-typealias: -/// 'typealias' identifier generic-params? '=' type -/// generic-where-clause? -ParsedSyntaxResult -Parser::parseDeclTypeAliasSyntax(Parser::ParseDeclOptions Flags, - Optional attrs, - Optional modifiers) { +/// 'typealias' identifier generic-params? '=' type requirement-clause? +/// \endverbatim +ParserResult Parser:: +parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { ParserPosition startPosition = getParserPosition(); llvm::Optional TmpCtxt; TmpCtxt.emplace(SyntaxContext); TmpCtxt->setBackTracking(); - auto typealiasKeyword = consumeTokenSyntax(tok::kw_typealias); - - ParserStatus status; - - auto applyIntroducer = [&](ParsedTypealiasDeclSyntaxBuilder &builder) { - if (attrs) - builder.useAttributes(std::move(*attrs)); - if (modifiers) - builder.useModifiers(std::move(*modifiers)); - builder.useTypealiasKeyword(std::move(typealiasKeyword)); - }; + SourceLoc TypeAliasLoc = consumeToken(tok::kw_typealias); + SourceLoc EqualLoc; + Identifier Id; + SourceLoc IdLoc; + ParserStatus Status; - // Parse the name. - auto name = - parseIdentifierDeclNameSyntax(*this, "typealias", [](const Token &next) { - return next.isAny(tok::colon, tok::equal); - }); - if (name.isNull()) { + Status |= parseIdentifierDeclName( + *this, Id, IdLoc, "typealias", + [](const Token &next) { return next.isAny(tok::colon, tok::equal); }); + if (Status.isError()) { TmpCtxt->setTransparent(); - TmpCtxt.reset(); - ParsedTypealiasDeclSyntaxBuilder builder(*SyntaxContext); - applyIntroducer(builder); - return makeParsedError(builder.build()); + return nullptr; } + + DebuggerContextChange DCC(*this, Id, DeclKind::TypeAlias); + + Optional GenericsScope; + GenericsScope.emplace(this, ScopeKind::Generics); - // Parse optional generic parameters. - Optional genericParams; + // Parse a generic parameter list if it is present. + GenericParamList *genericParams = nullptr; if (startsWithLess(Tok)) { - auto result = parseGenericParameterClauseSyntax(); - status |= result.getStatus(); - if (!result.isNull()) - genericParams = result.get(); + auto Result = parseGenericParameters(); + if (Result.hasCodeCompletion() && !CodeCompletion) + return makeParserCodeCompletionStatus(); + genericParams = Result.getPtrOrNull(); + + if (!genericParams) { + // If the parser returned null, it is an already diagnosed parse error. + } else if (!genericParams->getRequirements().empty()) { + // Reject a where clause. + diagnose(genericParams->getWhereLoc(), + diag::associated_type_generic_parameter_list) + .highlight(genericParams->getWhereClauseSourceRange()); + } } if (Flags.contains(PD_InProtocol) && !genericParams && !Tok.is(tok::equal)) { - // If we're in a protocol and don't see an '=' this looks like leftover - // Swift 2 code intending to be an associatedtype. TmpCtxt.reset(); + // If we're in a protocol and don't see an '=' this looks like leftover Swift 2 + // code intending to be an associatedtype. backtrackToPosition(startPosition); - return parseDeclAssociatedTypeSyntax(Flags, std::move(attrs), - std::move(modifiers)); + return parseDeclAssociatedType(Flags, Attributes); } - TmpCtxt->setTransparent(); TmpCtxt.reset(); - ParsedTypealiasDeclSyntaxBuilder builder(*SyntaxContext); - applyIntroducer(builder); - builder.useIdentifier(name.get()); - if (genericParams) - builder.useGenericParameterClause(std::move(*genericParams)); + auto *TAD = new (Context) TypeAliasDecl(TypeAliasLoc, EqualLoc, Id, IdLoc, + genericParams, CurDeclContext); + setLocalDiscriminator(TAD); + ParserResult UnderlyingTy; - // Parse underlying type clause. - if (Tok.isAny(tok::equal, tok::colon)) { - ParsedTypeInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); + if (Tok.is(tok::colon) || Tok.is(tok::equal)) { + ContextChange CC(*this, TAD); - // Parse '='. + SyntaxParsingContext InitCtx(SyntaxContext, + SyntaxKind::TypeInitializerClause); if (Tok.is(tok::colon)) { // It is a common mistake to write "typealias A : Int" instead of = Int. // Recognize this and produce a fixit. diagnose(Tok, diag::expected_equal_in_typealias) .fixItReplace(Tok.getLoc(), " = "); - ignoreToken(tok::colon); + EqualLoc = consumeToken(tok::colon); } else { - initBuilder.useEqual(consumeTokenSyntax()); + EqualLoc = consumeToken(tok::equal); } - // Parse the underlying type. - auto underlyingType = parseTypeSyntax(diag::expected_type_in_typealias); - status |= underlyingType.getStatus(); - if (!underlyingType.isNull()) { - initBuilder.useValue(underlyingType.get()); - } else { - initBuilder.useValue( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - } - builder.useInitializer(initBuilder.build()); - } else { - diagnose(Tok, diag::expected_equal_in_typealias); - status.setIsParseError(); + UnderlyingTy = parseType(diag::expected_type_in_typealias); + TAD->setTypeEndLoc(PreviousLoc); + Status |= UnderlyingTy; } - // Parse optional where clause. + TAD->setUnderlyingTypeRepr(UnderlyingTy.getPtrOrNull()); + TAD->getAttrs() = Attributes; + + // Parse a 'where' clause if present, adding it to our GenericParamList. if (Tok.is(tok::kw_where)) { - bool FirstTypeInComplete = false; - auto whereClause = parseGenericWhereClauseSyntax(FirstTypeInComplete); - status |= whereClause.getStatus(); - builder.useGenericWhereClause(whereClause.get()); + ContextChange CC(*this, TAD); + Status |= parseFreestandingGenericWhereClause(genericParams); } - return makeParsedResult(builder.build(), status); -} + if (UnderlyingTy.isNull()) { + // If there is an attempt to do code completion + // inside of typealias type, let's just return + // because we've seen required '=' token. + if (EqualLoc.isInvalid()) { + diagnose(Tok, diag::expected_equal_in_typealias); + Status.setIsParseError(); + return Status; + } + } + + // Exit the scope introduced for the generic parameters. + GenericsScope.reset(); -ParserResult -Parser::parseDeclTypeAlias(Parser::ParseDeclOptions Flags, - DeclAttributes &Attributes, SourceLoc leadingLoc) { - auto modifiers = SyntaxContext->popIf(); - auto attrs = SyntaxContext->popIf(); - - auto parsed = - parseDeclTypeAliasSyntax(Flags, std::move(attrs), std::move(modifiers)); - assert(!parsed.isNull()); - - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - TypeDecl *result = - cast_or_null(Generator.generate(syntax, leadingLoc)); - return makeParserResult(parsed.getStatus(), result); + addToScope(TAD); + return DCC.fixupParserResult(Status, TAD); } /// Parse an associatedtype decl. /// +/// \verbatim /// decl-associatedtype: -/// 'associatedtype' identifier type-inheritance-clause? -/// ('=' type)? where-clause? -ParsedSyntaxResult -Parser::parseDeclAssociatedTypeSyntax(ParseDeclOptions flags, - Optional attrs, - Optional modifiers) { - ParsedAssociatedtypeDeclSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - if (attrs) - builder.useAttributes(std::move(*attrs)); - if (modifiers) - builder.useModifiers(std::move(*modifiers)); +/// 'associatedtype' identifier inheritance? ('=' type)? where-clause? +/// \endverbatim - // Parse 'associatedtype' keyword. - // Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias - // can ask us to fix up leftover Swift 2 code intending to be an - // associatedtype. - auto keywordLoc = Tok.getLoc(); +ParserResult Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags, + DeclAttributes &Attributes) { + SourceLoc AssociatedTypeLoc; + ParserStatus Status; + Identifier Id; + SourceLoc IdLoc; + + // Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias can + // ask us to fix up leftover Swift 2 code intending to be an associatedtype. if (Tok.is(tok::kw_typealias)) { - diagnose(Tok.getLoc(), diag::typealias_inside_protocol_without_type) - .fixItReplace(Tok.getLoc(), "associatedtype"); - ignoreToken(tok::kw_typealias); + AssociatedTypeLoc = consumeToken(tok::kw_typealias); + diagnose(AssociatedTypeLoc, diag::typealias_inside_protocol_without_type) + .fixItReplace(AssociatedTypeLoc, "associatedtype"); } else { - builder.useAssociatedtypeKeyword( - consumeTokenSyntax(tok::kw_associatedtype)); + AssociatedTypeLoc = consumeToken(tok::kw_associatedtype); } - // Parse the name. - auto name = parseIdentifierDeclNameSyntax( - *this, "associatedtype", - [&](const Token &next) { return next.isAny(tok::colon, tok::equal); }); - if (name.isNull()) - return makeParsedResult(builder.build(), name.getStatus()); - assert(name.isSuccess()); - builder.useIdentifier(name.get()); - - // Diagnose generic parameters. + Status = parseIdentifierDeclName( + *this, Id, IdLoc, "associatedtype", + [](const Token &next) { return next.isAny(tok::colon, tok::equal); }); + if (Status.isError()) + return nullptr; + + DebuggerContextChange DCC(*this, Id, DeclKind::AssociatedType); + + // Reject generic parameters with a specific error. if (startsWithLess(Tok)) { - auto loc = Tok.getLoc(); - ignoreToken(); - if (ignoreUntilGreaterInTypeList()) - ignoreToken(); + // Introduce a throwaway scope to capture the generic parameters. We + // don't want them visible anywhere! + Scope S(this, ScopeKind::Generics); - diagnose(loc, diag::associated_type_generic_parameter_list) - .fixItRemove({loc, PreviousLoc}); + if (auto genericParams = parseGenericParameters().getPtrOrNull()) { + diagnose(genericParams->getLAngleLoc(), + diag::associated_type_generic_parameter_list) + .fixItRemove(genericParams->getSourceRange()); + } } - + // Parse optional inheritance clause. - if (Tok.is(tok::colon)) { - auto inheritance = parseTypeInheritanceClauseSyntax( - /*allowClassRequirement=*/false, /*allowAnyObject=*/true); - status |= inheritance.getStatus(); - if (!inheritance.isNull()) - builder.useInheritanceClause(inheritance.get()); - } - - // Parse optional default type. + // FIXME: Allow class requirements here. + SmallVector Inherited; + if (Tok.is(tok::colon)) + Status |= parseInheritance(Inherited, + /*allowClassRequirement=*/false, + /*allowAnyObject=*/true); + + ParserResult UnderlyingTy; if (Tok.is(tok::equal)) { - ParsedTypeInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); - initBuilder.useEqual(consumeTokenSyntax(tok::equal)); - - // Parse type. - auto type = parseTypeSyntax(diag::expected_type_in_associatedtype); - status |= type.getStatus(); - if (!type.isNull()) - initBuilder.useValue(type.get()); - else - initBuilder.useValue( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - - builder.useInitializer(initBuilder.build()); + SyntaxParsingContext InitContext(SyntaxContext, + SyntaxKind::TypeInitializerClause); + consumeToken(tok::equal); + UnderlyingTy = parseType(diag::expected_type_in_associatedtype); + Status |= UnderlyingTy; + if (UnderlyingTy.isNull()) + return Status; } - // Parse optional 'where' clause. + TrailingWhereClause *TrailingWhere = nullptr; + // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { - bool firstTypeInComplete = false; - auto where = parseGenericWhereClauseSyntax(firstTypeInComplete); - status |= where.getStatus(); - if (!where.isNull()) - builder.useGenericWhereClause(where.get()); - } - - // Diagnose if it's not in protocol decl. - // TODO: Move this to ASTGen. - if (!flags.contains(PD_InProtocol)) { - diagnose(keywordLoc, diag::associatedtype_outside_protocol) - .fixItReplace(keywordLoc, "typealias"); - status.setIsParseError(); + auto whereStatus = parseProtocolOrAssociatedTypeWhereClause( + TrailingWhere, /*isProtocol=*/false); + Status |= whereStatus; + if (whereStatus.hasCodeCompletion() && !CodeCompletion) { + // Trigger delayed parsing, no need to continue. + return whereStatus; + } } - return makeParsedResult(builder.build(), status); -} + if (!Flags.contains(PD_InProtocol)) { + diagnose(AssociatedTypeLoc, diag::associatedtype_outside_protocol) + .fixItReplace(AssociatedTypeLoc, "typealias"); + Status.setIsParseError(); + return Status; + } -ParserResult -Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags, - DeclAttributes &Attributes, - SourceLoc leadingLoc) { - auto modifiers = SyntaxContext->popIf(); - auto attrs = SyntaxContext->popIf(); - - auto parsed = parseDeclAssociatedTypeSyntax(Flags, std::move(attrs), - std::move(modifiers)); - assert(!parsed.isNull()); - - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - auto result = Generator.generate(syntax, leadingLoc); - return makeParserResult(parsed.getStatus(), result); + auto assocType = new (Context) + AssociatedTypeDecl(CurDeclContext, AssociatedTypeLoc, Id, IdLoc, + UnderlyingTy.getPtrOrNull(), TrailingWhere); + assocType->getAttrs() = Attributes; + if (!Inherited.empty()) + assocType->setInherited(Context.AllocateCopy(Inherited)); + addToScope(assocType); + return makeParserResult(Status, assocType); } /// This function creates an accessor function (with no body) for a computed @@ -4361,10 +5113,10 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, return ParameterList::create(P.Context, StartLoc, param, EndLoc); } -static unsigned skipBracedBlock(Parser &P, - SyntaxParsingContext *&SyntaxContext) { - SyntaxParsingContext CodeBlockContext(SyntaxContext, SyntaxKind::CodeBlock); - P.consumeToken(tok::l_brace); +bool Parser::skipBracedBlock() { + SyntaxParsingContext disabled(SyntaxContext); + SyntaxContext->disable(); + consumeToken(tok::l_brace); // We don't care if a skipped function body contained any of these, so // just ignore them. @@ -4372,14 +5124,13 @@ static unsigned skipBracedBlock(Parser &P, bool HasOperatorDeclarations; bool HasNestedClassDeclarations; - unsigned OpenBraces = skipUntilMatchingRBrace(P, + unsigned OpenBraces = skipUntilMatchingRBrace(*this, HasPoundDirectives, HasOperatorDeclarations, - HasNestedClassDeclarations, - SyntaxContext); - if (P.consumeIf(tok::r_brace)) + HasNestedClassDeclarations); + if (consumeIf(tok::r_brace)) OpenBraces--; - return OpenBraces; + return OpenBraces != 0; } /// Returns a descriptive name for the given accessor/addressor kind. @@ -4540,9 +5291,11 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, if (peekToken().is(tok::r_brace)) { accessors.LBLoc = consumeToken(tok::l_brace); // Give syntax node an empty accessor list. - SourceLoc listLoc = accessors.LBLoc.getAdvancedLoc(1); - SyntaxContext->addSyntax( - ParsedSyntaxRecorder::makeBlankAccessorList(listLoc, *SyntaxContext)); + if (SyntaxContext->isEnabled()) { + SourceLoc listLoc = leadingTriviaLoc(); + SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankAccessorList(listLoc, *SyntaxContext)); + } accessors.RBLoc = consumeToken(tok::r_brace); // In the limited syntax, fall out and let the caller handle it. @@ -4764,7 +5517,7 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, VarLoc, Identifier(), CurDeclContext); storage->setImplicit(true); - storage->setInvalid(true); + storage->setInvalid(); Pattern *pattern = TypedPattern::createImplicit(Context, new (Context) NamedPattern(storage), @@ -4774,7 +5527,7 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, auto binding = PatternBindingDecl::create(Context, StaticLoc, StaticSpelling, VarLoc, entry, CurDeclContext); - binding->setInvalid(true); + binding->setInvalid(); storage->setParentPatternBinding(binding); Decls.push_back(binding); @@ -4827,6 +5580,11 @@ Parser::parseDeclVarGetSet(Pattern *pattern, ParseDeclOptions Flags, accessors.record(*this, PrimaryVar, Invalid); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + return makeParserResult(PrimaryVar); } @@ -4857,12 +5615,6 @@ void Parser::ParsedAccessors::record(Parser &P, AbstractStorageDecl *storage, storage->setAccessors(LBLoc, Accessors, RBLoc); } -static void flagInvalidAccessor(AccessorDecl *func) { - if (func) { - func->setInvalid(); - } -} - static void diagnoseConflictingAccessors(Parser &P, AccessorDecl *first, AccessorDecl *&second) { if (!second) return; @@ -4873,7 +5625,7 @@ static void diagnoseConflictingAccessors(Parser &P, AccessorDecl *first, P.diagnose(first->getLoc(), diag::previous_accessor, getAccessorNameForDiagnostic(first, /*article*/ false), /*already*/ false); - flagInvalidAccessor(second); + second->setInvalid(); } template @@ -4883,11 +5635,11 @@ static void diagnoseAndIgnoreObservers(Parser &P, typename std::enable_if::type... args) { if (auto &accessor = accessors.WillSet) { P.diagnose(accessor->getLoc(), diagnostic, /*willSet*/ 0, args...); - flagInvalidAccessor(accessor); + accessor->setInvalid(); } if (auto &accessor = accessors.DidSet) { P.diagnose(accessor->getLoc(), diagnostic, /*didSet*/ 1, args...); - flagInvalidAccessor(accessor); + accessor->setInvalid(); } } @@ -4899,7 +5651,7 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage, // was invalid. if (invalid) { for (auto accessor : Accessors) { - flagInvalidAccessor(accessor); + accessor->setInvalid(); } } @@ -5027,12 +5779,12 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // Now that we've parsed all of our patterns, initializers and accessors, we // can finally create our PatternBindingDecl to represent the // pattern/initializer pairs. - auto PBD = PatternBindingDecl::create(Context, StaticLoc, StaticSpelling, - VarLoc, PBDEntries, BaseContext); + auto *PBD = PatternBindingDecl::create(Context, StaticLoc, StaticSpelling, + VarLoc, PBDEntries, BaseContext); // Wire up any initializer contexts we needed. for (unsigned i : indices(PBDEntries)) { - if (auto initContext = PBDEntries[i].getInitContext()) + if (auto initContext = PBD->getInitContext(i)) cast(initContext)->setBinding(PBD, i); } @@ -5088,8 +5840,13 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + VD->setTopLevelGlobal(topLevelDecl); + + // Set original declaration in `@differentiable` attributes. + setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); + Decls.push_back(VD); - if (hasOpaqueReturnTy && sf) { + if (hasOpaqueReturnTy && sf && !InInactiveClauseEnvironment) { sf->addUnvalidatedDeclWithOpaqueResultType(VD); } }); @@ -5253,28 +6010,22 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, SourceRange BodyRange; BodyRange.Start = Tok.getLoc(); - // Consume the '{', and find the matching '}'. - unsigned OpenBraces = skipBracedBlock(*this, SyntaxContext); - if (OpenBraces != 0 && Tok.isNot(tok::code_complete)) { - assert(Tok.is(tok::eof)); - // We hit EOF, and not every brace has a pair. Recover by searching - // for the next decl except variable decls and cutting off before - // that point. - backtrackToPosition(BeginParserPosition); - consumeToken(tok::l_brace); - while (Tok.is(tok::kw_var) || Tok.is(tok::kw_let) || - (Tok.isNot(tok::eof) && !isStartOfDecl())) { - consumeToken(); - } - } + // Advance the parser to the end of the block; '{' ... '}'. + skipBracedBlock(); BodyRange.End = PreviousLoc; - if (SourceMgr.getCodeCompletionLoc().isInvalid() || - SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { - AFD->setBodyDelayed(BodyRange); - } else { - AFD->setBodySkipped(BodyRange); + AFD->setBodyDelayed(BodyRange); + + if (isCodeCompletionFirstPass()) { + if (SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) { + State->setCodeCompletionDelayedDeclState( + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::FunctionBody, + PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc); + } else { + AFD->setBodySkipped(BodyRange); + } } } @@ -5315,6 +6066,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, } } + ParserStatus Status; SourceLoc FuncLoc = HasFuncKeyword ? consumeToken(tok::kw_func) : Tok.getLoc(); @@ -5371,12 +6123,13 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, Optional GenericsScope; GenericsScope.emplace(this, ScopeKind::Generics); GenericParamList *GenericParams; - bool SignatureHasCodeCompletion = false; auto GenericParamResult = maybeParseGenericParams(); GenericParams = GenericParamResult.getPtrOrNull(); - SignatureHasCodeCompletion |= GenericParamResult.hasCodeCompletion(); - if (SignatureHasCodeCompletion && !CodeCompletion) - return makeParserCodeCompletionStatus(); + if (GenericParamResult.hasCodeCompletion()) { + Status.setHasCodeCompletion(); + if (!CodeCompletion) + return Status; + } DefaultArgumentInfo DefaultArgs; TypeRepr *FuncRetTy = nullptr; @@ -5384,14 +6137,11 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, ParameterList *BodyParams; SourceLoc throwsLoc; bool rethrows; - ParserStatus SignatureStatus = - parseFunctionSignature(SimpleName, FullName, BodyParams, DefaultArgs, - throwsLoc, rethrows, FuncRetTy); - - SignatureHasCodeCompletion |= SignatureStatus.hasCodeCompletion(); - if (SignatureStatus.hasCodeCompletion() && !CodeCompletion) { + Status |= parseFunctionSignature(SimpleName, FullName, BodyParams, + DefaultArgs, throwsLoc, rethrows, FuncRetTy); + if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. - return SignatureStatus; + return Status; } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -5405,7 +6155,8 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, CurDeclContext); // Let the source file track the opaque return type mapping, if any. - if (FuncRetTy && isa(FuncRetTy)) { + if (FuncRetTy && isa(FuncRetTy) && + !InInactiveClauseEnvironment) { if (auto sf = CurDeclContext->getParentSourceFile()) { sf->addUnvalidatedDeclWithOpaqueResultType(FD); } @@ -5415,11 +6166,10 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, if (Tok.is(tok::kw_where)) { ContextChange CC(*this, FD); - auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); - SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); - if (whereStatus.hasCodeCompletion() && !CodeCompletion) { + Status |= parseFreestandingGenericWhereClause(GenericParams); + if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. - return whereStatus; + return Status; } } @@ -5440,8 +6190,10 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, FD->getAttrs() = Attributes; // Pass the function signature to code completion. - if (SignatureHasCodeCompletion) + if (Status.hasCodeCompletion()) { + assert(CodeCompletion && "must be code completion second pass"); CodeCompletion->setParsedDecl(FD); + } DefaultArgs.setFunctionContext(FD, FD->getParameters()); setLocalDiscriminator(FD); @@ -5451,7 +6203,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, diagnose(Tok, diag::protocol_method_with_body); skipSingle(); } - } else { + } else if (!Status.hasCodeCompletion()) { parseAbstractFunctionBody(FD); } @@ -5464,6 +6216,22 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, /// Parse function body into \p AFD. void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { + if (!Tok.is(tok::l_brace)) { + checkForInputIncomplete(); + return; + } + + // Record the curly braces but nothing inside. + recordTokenHash("{"); + recordTokenHash("}"); + + llvm::SaveAndRestore> T(CurrentTokenHash, nullptr); + + if (isDelayedParsingEnabled()) { + consumeAbstractFunctionBody(AFD, AFD->getAttrs()); + return; + } + Scope S(this, ScopeKind::FunctionBody); // Enter the arguments for the function into a new function-body scope. We @@ -5477,23 +6245,6 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); - if (!Tok.is(tok::l_brace)) { - checkForInputIncomplete(); - return; - } - - if (IsParsingInterfaceTokens) { - // Record the curly braces but nothing inside. - SF.recordInterfaceToken("{"); - SF.recordInterfaceToken("}"); - } - llvm::SaveAndRestore T(IsParsingInterfaceTokens, false); - - if (isDelayedParsingEnabled()) { - consumeAbstractFunctionBody(AFD, AFD->getAttrs()); - return; - } - if (Context.Stats) Context.Stats->getFrontendCounters().NumFunctionsParsed++; @@ -5509,7 +6260,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { // may be incomplete and the type mismatch in return statement will just // confuse the type checker. if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) { - auto Element = BS->getElement(0); + auto Element = BS->getFirstElement(); if (auto *stmt = Element.dyn_cast()) { if (isa(AFD)) { if (auto *returnStmt = dyn_cast(stmt)) { @@ -5534,7 +6285,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { } if (auto F = dyn_cast(AFD)) { auto RS = new (Context) ReturnStmt(SourceLoc(), E); - BS->setElement(0, RS); + BS->setFirstElement(RS); AFD->setHasSingleExpressionBody(); AFD->setSingleExpressionBody(E); } else if (auto *F = dyn_cast(AFD)) { @@ -5542,7 +6293,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { // If it's a nil literal, just insert return. This is the only // legal thing to return. auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); - BS->setElement(0, RS); + BS->setFirstElement(RS); AFD->setHasSingleExpressionBody(); AFD->setSingleExpressionBody(E); } @@ -5557,7 +6308,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { "function body should be delayed"); auto bodyRange = AFD->getBodySourceRange(); - auto BeginParserPosition = getParserPosition({bodyRange.Start,bodyRange.End}); + auto BeginParserPosition = getParserPosition({bodyRange.Start, SourceLoc()}); auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc()); // ParserPositionRAII needs a primed parser to restore to. @@ -5579,6 +6330,9 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { // Re-enter the lexical scope. Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); ParseFunctionBody CC(*this, AFD); setLocalDiscriminatorToParamList(AFD->getParameters()); @@ -5631,11 +6385,11 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the enum. if (Tok.is(tok::colon)) { - MutableArrayRef Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - ED->setInherited(Inherited); + ED->setInherited(Context.AllocateCopy(Inherited)); } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -5917,11 +6671,11 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the struct. if (Tok.is(tok::colon)) { - MutableArrayRef Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - SD->setInherited(Inherited); + SD->setInherited(Context.AllocateCopy(Inherited)); } diagnoseWhereClauseInGenericParamList(GenericParams); @@ -6010,11 +6764,11 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, // Parse optional inheritance clause within the context of the class. if (Tok.is(tok::colon)) { - MutableArrayRef Inherited; + SmallVector Inherited; Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false, /*allowAnyObject=*/false); - CD->setInherited(Inherited); + CD->setInherited(Context.AllocateCopy(Inherited)); // Parse python style inheritance clause and replace parentheses with a colon } else if (Tok.is(tok::l_paren)) { @@ -6116,7 +6870,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { DebuggerContextChange DCC (*this); // Parse optional inheritance clause. - MutableArrayRef InheritedProtocols; + SmallVector InheritedProtocols; SourceLoc colonLoc; if (Tok.is(tok::colon)) { colonLoc = Tok.getLoc(); @@ -6136,7 +6890,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { ProtocolDecl *Proto = new (Context) ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName, - InheritedProtocols, TrailingWhere); + Context.AllocateCopy(InheritedProtocols), TrailingWhere); // No need to setLocalDiscriminator: protocols can't appear in local contexts. Proto->getAttrs() = Attributes; @@ -6213,14 +6967,14 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Optional GenericsScope; GenericsScope.emplace(this, ScopeKind::Generics); GenericParamList *GenericParams; - bool SignatureHasCodeCompletion = false; auto Result = maybeParseGenericParams(); GenericParams = Result.getPtrOrNull(); - SignatureHasCodeCompletion |= Result.hasCodeCompletion(); - - if (SignatureHasCodeCompletion && !CodeCompletion) - return makeParserCodeCompletionStatus(); + if (Result.hasCodeCompletion()) { + Status.setHasCodeCompletion(); + if (!CodeCompletion) + return Status; + } // Parse the parameter list. DefaultArgumentInfo DefaultArgs; @@ -6229,10 +6983,8 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, = parseSingleParameterClause(ParameterContextKind::Subscript, &argumentNames, &DefaultArgs); Status |= Indices; - - SignatureHasCodeCompletion |= Indices.hasCodeCompletion(); - if (SignatureHasCodeCompletion && !CodeCompletion) - return makeParserCodeCompletionStatus(); + if (Status.hasCodeCompletion() && !CodeCompletion) + return Status; SourceLoc ArrowLoc; ParserResult ElementTy; @@ -6256,10 +7008,9 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, // type ElementTy = parseDeclResultType(diag::expected_type_subscript); Status |= ElementTy; - SignatureHasCodeCompletion |= ElementTy.hasCodeCompletion(); - if (SignatureHasCodeCompletion && !CodeCompletion) { - return makeParserCodeCompletionStatus(); - } + if (Status.hasCodeCompletion() && !CodeCompletion) + return Status; + if (ElementTy.isNull()) { // Always set an element type. ElementTy = makeParserResult(ElementTy, new (Context) ErrorTypeRepr()); @@ -6280,7 +7031,8 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Subscript->getAttrs() = Attributes; // Let the source file track the opaque return type mapping, if any. - if (ElementTy.get() && isa(ElementTy.get())) { + if (ElementTy.get() && isa(ElementTy.get()) && + !InInactiveClauseEnvironment) { if (auto sf = CurDeclContext->getParentSourceFile()) { sf->addUnvalidatedDeclWithOpaqueResultType(Subscript); } @@ -6292,16 +7044,16 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, if (Tok.is(tok::kw_where)) { ContextChange CC(*this, Subscript); - auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); - SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); - if (whereStatus.hasCodeCompletion() && !CodeCompletion) { + Status |= parseFreestandingGenericWhereClause(GenericParams); + if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. - return whereStatus; + return Status; } } // Pass the function signature to code completion. - if (SignatureHasCodeCompletion && CodeCompletion) { + if (Status.hasCodeCompletion()) { + assert(CodeCompletion && "must be code completion second pass"); CodeCompletion->setParsedDecl(Subscript); } @@ -6322,7 +7074,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, } Status.setIsParseError(); } - } else { + } else if (!Status.hasCodeCompletion()) { Status |= parseGetSet(Flags, GenericParams, Indices.get(), accessors, Subscript, StaticLoc); } @@ -6339,6 +7091,11 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, accessors.record(*this, Subscript, (Invalid || !Status.isSuccess())); + // Set original declaration in `@differentiable` attributes. + for (auto *accessor : accessors.Accessors) + setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), + accessor); + // No need to setLocalDiscriminator because subscripts cannot // validly appear outside of type decls. return makeParserResult(Status, Subscript); @@ -6347,6 +7104,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, ParserResult Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { assert(Tok.is(tok::kw_init)); + ParserStatus Status; SourceLoc ConstructorLoc = consumeToken(); bool Failable = false, IUO = false; SourceLoc FailabilityLoc; @@ -6381,21 +7139,22 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { Scope S(this, ScopeKind::Generics); auto GPResult = maybeParseGenericParams(); GenericParamList *GenericParams = GPResult.getPtrOrNull(); - if (GPResult.hasCodeCompletion()) - return makeParserCodeCompletionStatus(); + if (GPResult.hasCodeCompletion()) { + Status.setHasCodeCompletion(); + if (!CodeCompletion) + return Status; + } // Parse the parameters. DefaultArgumentInfo DefaultArgs; llvm::SmallVector namePieces; - bool SignatureHasCodeCompletion = false; ParserResult Params = parseSingleParameterClause(ParameterContextKind::Initializer, &namePieces, &DefaultArgs); - - SignatureHasCodeCompletion |= Params.hasCodeCompletion(); - if (Params.hasCodeCompletion() && !CodeCompletion) { + Status |= Params; + if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. - return makeParserCodeCompletionStatus(); + return Status; } // Protocol initializer arguments may not have default values. @@ -6427,11 +7186,10 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { if (Tok.is(tok::kw_where)) { ContextChange(*this, CD); - auto whereStatus = parseFreestandingGenericWhereClause(GenericParams); - SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion(); - if (whereStatus.hasCodeCompletion() && !CodeCompletion) { + Status |= parseFreestandingGenericWhereClause(GenericParams); + if (Status.hasCodeCompletion() && !CodeCompletion) { // Trigger delayed parsing, no need to continue. - return whereStatus; + return Status; } } @@ -6440,8 +7198,10 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { DefaultArgs.setFunctionContext(CD, CD->getParameters()); // Pass the function signature to code completion. - if (SignatureHasCodeCompletion) + if (Status.hasCodeCompletion()) { + assert(CodeCompletion && "must be code completion second pass"); CodeCompletion->setParsedDecl(CD); + } if (ConstructorsNotAllowed || Params.isParseError()) { // Tell the type checker not to touch this constructor. @@ -6453,11 +7213,11 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { diagnose(Tok, diag::protocol_init_with_body); skipSingle(); } - } else { + } else if(!Status.hasCodeCompletion()) { parseAbstractFunctionBody(CD); } - return makeParserResult(CD); + return makeParserResult(Status, CD); } ParserResult Parser:: @@ -6595,7 +7355,7 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, return makeParserCodeCompletionResult(); } - if (Context.LangOpts.EnableOperatorDesignatedTypes) { + if (Context.TypeCheckerOpts.EnableOperatorDesignatedTypes) { if (Tok.is(tok::identifier)) { SyntaxParsingContext GroupCtxt(SyntaxContext, SyntaxKind::IdentifierList); @@ -6701,12 +7461,13 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, if (parseIdentifier(name, nameLoc, diag::expected_precedencegroup_name)) { // If the identifier is missing or a keyword or something, try to // skip the entire body. - if (consumeIf(tok::l_brace)) { + if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof) && + peekToken().is(tok::l_brace)) + consumeToken(); + if (Tok.is(tok::l_brace)) { + consumeToken(tok::l_brace); skipUntilDeclRBrace(); (void) consumeIf(tok::r_brace); - } else if (Tok.isNot(tok::eof) && peekToken().is(tok::l_brace)) { - consumeToken(); - skipBracedBlock(*this, SyntaxContext); } return nullptr; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 700f0b345d92f..eac6df3a4d0f8 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -14,13 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "ParseList.h" #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/TypeRepr.h" #include "swift/Basic/EditorPlaceholder.h" #include "swift/Parse/CodeCompletionCallbacks.h" -#include "swift/Parse/ParsedSyntaxBuilders.h" #include "swift/Parse/ParsedSyntaxRecorder.h" #include "swift/Parse/SyntaxParsingContext.h" #include "swift/Syntax/SyntaxKind.h" @@ -36,19 +34,6 @@ using namespace swift; using namespace swift::syntax; -ParsedSyntaxResult Parser::parseExpressionSyntax(Diag<> ID) { - SourceLoc ExprLoc = Tok.getLoc(); - SyntaxParsingContext ExprParsingContext(SyntaxContext, - SyntaxContextKind::Expr); - ExprParsingContext.setTransparent(); - ParserResult Result = parseExpr(ID); - if (auto ParsedExpr = ExprParsingContext.popIf()) { - Generator.addExpr(Result.getPtrOrNull(), ExprLoc); - return makeParsedResult(std::move(*ParsedExpr), Result.getStatus()); - } - return Result.getStatus(); -} - /// parseExpr /// /// expr: @@ -204,8 +189,13 @@ ParserResult Parser::parseExprSequence(Diag<> Message, if (CodeCompletion) CodeCompletion->setLeadingSequenceExprs(SequencedExprs); } - if (Primary.isNull()) + if (Primary.isNull()) { + if (HasCodeCompletion) { + SequencedExprs.push_back(new (Context) CodeCompletionExpr(PreviousLoc)); + break; + } return Primary; + } SequencedExprs.push_back(Primary.get()); @@ -257,16 +247,23 @@ ParserResult Parser::parseExprSequence(Diag<> Message, HasCodeCompletion = true; if (middle.isNull()) return nullptr; - + // Make sure there's a matching ':' after the middle expr. if (!Tok.is(tok::colon)) { + if (middle.hasCodeCompletion()) { + SequencedExprs.push_back(new (Context) IfExpr(questionLoc, + middle.get(), + PreviousLoc)); + SequencedExprs.push_back(new (Context) CodeCompletionExpr(PreviousLoc)); + goto done; + } + diagnose(questionLoc, diag::expected_colon_after_if_question); - - Status.setIsParseError(); - return makeParserResult(Status, new (Context) ErrorExpr( - {startLoc, middle.get()->getSourceRange().End})); + Status.setIsParseError(); + return makeParserResult(Status, new (Context) ErrorExpr( + {startLoc, middle.get()->getSourceRange().End})); } - + SourceLoc colonLoc = consumeToken(); auto *unresolvedIf @@ -627,79 +624,99 @@ ParserResult Parser::parseExprKeyPath() { /// expr-keypath-objc: /// '#keyPath' '(' unqualified-name ('.' unqualified-name) * ')' /// -ParsedSyntaxResult Parser::parseExprObjcKeyPathSyntax() { - ParsedObjcKeyPathExprSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - +ParserResult Parser::parseExprKeyPathObjC() { + SyntaxParsingContext ObjcKPCtx(SyntaxContext, SyntaxKind::ObjcKeyPathExpr); // Consume '#keyPath'. - builder.useKeyPath(consumeTokenSyntax(tok::pound_keyPath)); + SourceLoc keywordLoc = consumeToken(tok::pound_keyPath); // Parse the leading '('. if (!Tok.is(tok::l_paren)) { diagnose(Tok, diag::expr_keypath_expected_lparen); - return makeParsedError(builder.build()); + return makeParserError(); } - auto LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); + SourceLoc lParenLoc = consumeToken(tok::l_paren); + + SmallVector components; + /// Handler for code completion. + auto handleCodeCompletion = [&](SourceLoc DotLoc) -> ParserResult { + KeyPathExpr *expr = nullptr; + if (!components.empty()) { + expr = new (Context) + KeyPathExpr(Context, keywordLoc, lParenLoc, components, Tok.getLoc()); + } + + if (CodeCompletion) + CodeCompletion->completeExprKeyPath(expr, DotLoc); + + // Eat the code completion token because we handled it. + consumeToken(tok::code_complete); + return makeParserCodeCompletionResult(expr); + }; // Parse the sequence of unqualified-names. - bool isFirst = true; - bool hasNext = true; - do { - // Parse the next name - Optional identTok; - Optional declNameArgs; - status |= parseUnqualifiedDeclNameSyntax( - identTok, declNameArgs, /*afterDot=*/!isFirst, - diag::expr_keypath_expected_property_or_type); - isFirst = false; - if (status.isError()) + ParserStatus status; + SourceLoc LastDotLoc; + DeclNameOptions flags = DeclNameFlag::AllowCompoundNames; + while (true) { + SyntaxParsingContext NamePieceCtx(SyntaxContext, SyntaxKind::ObjcNamePiece); + // Handle code completion. + if (Tok.is(tok::code_complete)) + return handleCodeCompletion(LastDotLoc); + + // Parse the next name. + DeclNameLoc nameLoc; + DeclNameRef name = parseDeclNameRef(nameLoc, + diag::expr_keypath_expected_property_or_type, flags); + if (!name) { + status.setIsParseError(); break; + } - ParsedObjcNamePieceSyntaxBuilder elemBuilder(*SyntaxContext); + // Record the name we parsed. + auto component = KeyPathExpr::Component::forUnresolvedProperty(name, + nameLoc.getBaseNameLoc()); + components.push_back(component); - elemBuilder.useName(std::move(*identTok)); - if (declNameArgs) - elemBuilder.useDeclNameArguments(std::move(*declNameArgs)); + // After the first component, we can start parsing keywords. + flags |= DeclNameFlag::AllowKeywords; - hasNext = Tok.is(tok::period); - if (hasNext) - elemBuilder.useDot(consumeTokenSyntax(tok::period)); - builder.addNameMember(elemBuilder.build()); - } while (hasNext); + // Handle code completion. + if (Tok.is(tok::code_complete)) + return handleCodeCompletion(SourceLoc()); - if (Tok.is(tok::code_complete)) { - return makeParsedCodeCompletion( - ParsedSyntaxRecorder::makeCodeCompletionExpr( - builder.build(), None, consumeTokenSyntax(tok::code_complete), - *SyntaxContext)); + // Parse the next period to continue the path. + if (consumeIf(tok::period, LastDotLoc)) + continue; + + break; } + // Collect all name pieces to an objc name. + SyntaxContext->collectNodesInPlace(SyntaxKind::ObjcName); + + // Parse the closing ')'. + SourceLoc rParenLoc; if (status.isError()) { - while (!Tok.isAny(tok::r_paren, tok::eof, tok::r_brace, tok::pound_endif, - tok::pound_else, tok::pound_elseif) && - !isStartOfDecl() && !isStartOfStmt()) - ignoreSingle(); + skipUntilDeclStmtRBrace(tok::r_paren); + if (Tok.is(tok::r_paren)) + rParenLoc = consumeToken(); + else + rParenLoc = PreviousLoc; + } else { + parseMatchingToken(tok::r_paren, rParenLoc, + diag::expr_keypath_expected_rparen, lParenLoc); } - // Parse the closing ')'. - auto RParen = - parseMatchingTokenSyntax(tok::r_paren, diag::expr_keypath_expected_rparen, - LParenLoc, /*silenceDiag=*/status.isError()); - status |= RParen.getStatus(); - if (!RParen.isNull()) - builder.useRightParen(RParen.get()); - - return makeParsedResult(builder.build(), status); -} + // If we cannot build a useful expression, just return an error + // expression. + if (components.empty() || status.isError()) { + return makeParserResult( + new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); + } -ParserResult Parser::parseExprKeyPathObjC() { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprObjcKeyPathSyntax(); - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - auto expr = Generator.generate(syntax, leadingLoc); - return makeParserResult(parsed.getStatus(), expr); + // We're done: create the key-path expression. + return makeParserResult(new (Context) KeyPathExpr( + Context, keywordLoc, lParenLoc, components, rParenLoc)); } /// parseExprSelector @@ -803,12 +820,35 @@ UnresolvedDeclRefExpr *Parser::parseExprOperator() { assert(Tok.isAnyOperator()); DeclRefKind refKind = getDeclRefKindForOperator(Tok.getKind()); SourceLoc loc = Tok.getLoc(); - Identifier name = Context.getIdentifier(Tok.getText()); + DeclNameRef name(Context.getIdentifier(Tok.getText())); consumeToken(); // Bypass local lookup. return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc)); } +static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P, + DeclContext *DC, + SourceLoc Loc) { + auto *methodContext = DC->getInnermostMethodContext(); + if (!methodContext) { + P.diagnose(Loc, diag::super_not_in_class_method); + return nullptr; + } + + // Do an actual lookup for 'self' in case it shows up in a capture list. + auto *methodSelf = methodContext->getImplicitSelfDecl(); + auto *lookupSelf = P.lookupInScope(DeclNameRef(P.Context.Id_self)); + if (lookupSelf && lookupSelf != methodSelf) { + // FIXME: This is the wrong diagnostic for if someone manually declares a + // variable named 'self' using backticks. + P.diagnose(Loc, diag::super_in_closure_with_capture); + P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here); + return nullptr; + } + + return methodSelf; +} + /// parseExprSuper /// /// expr-super: @@ -821,40 +861,45 @@ UnresolvedDeclRefExpr *Parser::parseExprOperator() { /// 'super' '.' 'init' /// expr-super-subscript: /// 'super' '[' expr ']' -ParsedSyntaxResult Parser::parseExprSuperSyntax() { - auto superTok = consumeTokenSyntax(tok::kw_super); +ParserResult Parser::parseExprSuper() { + SyntaxParsingContext SuperCtxt(SyntaxContext, SyntaxContextKind::Expr); + // Parse the 'super' reference. + SourceLoc superLoc = consumeToken(tok::kw_super); + SyntaxContext->createNodeInPlace(SyntaxKind::SuperRefExpr); // 'super.' must be followed by a member ref, explicit initializer ref, or // subscript call. if (!Tok.isAny(tok::period, tok::period_prefix, tok::code_complete) && !Tok.isFollowingLSquare()) { - SmallVector junk; - junk.emplace_back(std::move(superTok)); - if (auto unknown = consumeTokenSyntaxIf(tok::unknown)) { - junk.emplace_back(std::move(*unknown)); - } else { + if (!consumeIf(tok::unknown)) diagnose(Tok, diag::expected_dot_or_subscript_after_super); - } - - return makeParsedError( - ParsedSyntaxRecorder::makeUnknownExpr(junk, *SyntaxContext)); + return nullptr; } - return makeParsedResult(ParsedSyntaxRecorder::makeSuperRefExpr( - std::move(superTok), *SyntaxContext)); -} + VarDecl *selfDecl = + getImplicitSelfDeclForSuperContext(*this, CurDeclContext, superLoc); + if (!selfDecl) + return makeParserResult(new (Context) ErrorExpr(superLoc)); -ParserResult Parser::parseExprSuper() { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprSuperSyntax(); - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - auto expr = Generator.generate(syntax, leadingLoc); - return makeParserResult(parsed.getStatus(), expr); + return makeParserResult(new (Context) SuperRefExpr(selfDecl, superLoc, + /*Implicit=*/false)); } +/// Copy a numeric literal value into AST-owned memory, stripping underscores +/// so the semantic part of the value can be parsed by APInt/APFloat parsers. StringRef Parser::copyAndStripUnderscores(StringRef orig) { - return ASTGen::copyAndStripUnderscores(orig, Context); + char *start = static_cast(Context.Allocate(orig.size(), 1)); + char *p = start; + + if (p) { + for (char c : orig) { + if (c != '_') { + *p++ = c; + } + } + } + + return StringRef(start, p - start); } /// Disambiguate the parse after '{' token that is in a place that might be @@ -934,113 +979,70 @@ static bool isValidTrailingClosure(bool isExprBasic, Parser &P){ // the token after the { is on the same line as the {. if (P.peekToken().isAtStartOfLine()) return false; - - + // Determine if the {} goes with the expression by eating it, and looking - // to see if it is immediately followed by '{', 'where', or comma. If so, - // we consider it to be part of the proceeding expression. + // to see if it is immediately followed by a token which indicates we should + // consider it part of the preceding expression Parser::BacktrackingScope backtrack(P); P.consumeToken(tok::l_brace); P.skipUntil(tok::r_brace); SourceLoc endLoc; - if (!P.consumeIf(tok::r_brace, endLoc) || - P.Tok.isNot(tok::l_brace, tok::kw_where, tok::comma)) { + if (!P.consumeIf(tok::r_brace, endLoc)) return false; - } - // Recoverable case. Just return true here and Sema will emit a diagnostic - // later. see: Sema/MiscDiagnostics.cpp#checkStmtConditionTrailingClosure - return true; + switch (P.Tok.getKind()) { + case tok::l_brace: + case tok::kw_where: + case tok::comma: + return true; + case tok::l_square: + case tok::l_paren: + case tok::period: + case tok::period_prefix: + case tok::kw_is: + case tok::kw_as: + case tok::question_postfix: + case tok::question_infix: + case tok::exclaim_postfix: + case tok::colon: + case tok::equal: + case tok::oper_postfix: + case tok::oper_binary_spaced: + case tok::oper_binary_unspaced: + return !P.Tok.isAtStartOfLine(); + default: + return false; + } } -ParsedSyntaxResult -Parser::parseExprUnresolvedMemberSyntax(bool isExprBasic) { - assert(Tok.isAny(tok::period, tok::period_prefix)); - // Parse '.' - Tok.setKind(tok::period_prefix); - auto dotTok = consumeTokenSyntax(tok::period_prefix); - // Handle code completion; '.' - if (Tok.is(tok::code_complete)) { - ParsedCodeCompletionExprSyntaxBuilder ccBuilder(*SyntaxContext); - ccBuilder.usePeriodOrParen(std::move(dotTok)); - ccBuilder.useCodeCompletionToken(consumeTokenSyntax(tok::code_complete)); - return makeParsedCodeCompletion(ccBuilder.build()); - } - - ParserStatus status; +/// Map magic literal tokens such as #file to their +/// MagicIdentifierLiteralExpr kind. +static MagicIdentifierLiteralExpr::Kind +getMagicIdentifierLiteralKind(tok Kind) { + switch (Kind) { + case tok::kw___COLUMN__: + case tok::pound_column: + return MagicIdentifierLiteralExpr::Kind::Column; + case tok::kw___FILE__: + case tok::pound_file: + return MagicIdentifierLiteralExpr::Kind::File; + case tok::pound_filePath: + return MagicIdentifierLiteralExpr::Kind::FilePath; + case tok::kw___FUNCTION__: + case tok::pound_function: + return MagicIdentifierLiteralExpr::Kind::Function; + case tok::kw___LINE__: + case tok::pound_line: + return MagicIdentifierLiteralExpr::Kind::Line; + case tok::kw___DSO_HANDLE__: + case tok::pound_dsohandle: + return MagicIdentifierLiteralExpr::Kind::DSOHandle; - // Parse the name. - Optional identTok; - Optional declNameArgs; - status |= - parseUnqualifiedDeclNameSyntax(identTok, declNameArgs, /*afterDot=*/true, - diag::expected_identifier_after_dot_expr); - if (status.isError()) { - // If the name is missing. It makes no sense to construct a member access - // expression. - assert(!identTok && !declNameArgs); - return makeParsedError( - ParsedSyntaxRecorder::makeUnknownExpr({&dotTok, 1}, *SyntaxContext)); + default: + llvm_unreachable("not a magic literal"); } - - ParsedMemberAccessExprSyntaxBuilder builder(*SyntaxContext); - builder.useDot(std::move(dotTok)); - builder.useName(std::move(*identTok)); - if (declNameArgs) - builder.useDeclNameArguments(std::move(*declNameArgs)); - - // FIXME: These calling suffix parsings are not necessary for Syntax parsing. - // Remove this block after full expression parsing migration. - - // Check for a () suffix, which indicates a call when constructing - // this member. Note that this cannot be the start of a new line. - if (Tok.isFollowingLParen()) { - ParsedFunctionCallExprSyntaxBuilder callBuilder(*SyntaxContext); - callBuilder.useCalledExpression(builder.build()); - - status |= parseExprListSyntax( - tok::l_paren, tok::r_paren, - /*isPostfix=*/true, isExprBasic, - [&](ParsedTokenSyntax &&leftTok, - ParsedTupleExprElementListSyntax &&args, - Optional &&rightTok, - Optional &&closure) { - callBuilder.useLeftParen(std::move(leftTok)); - callBuilder.useArgumentList(std::move(args)); - if (rightTok) - callBuilder.useRightParen(std::move(*rightTok)); - if (closure) - callBuilder.useTrailingClosure(std::move(*closure)); - }); - return makeParsedResult(callBuilder.build(), status); - } - - // Check for a trailing closure, if allowed. - if (Tok.is(tok::l_brace) && isValidTrailingClosure(isExprBasic, *this)) { - ParsedFunctionCallExprSyntaxBuilder callBuilder(*SyntaxContext); - callBuilder.useCalledExpression(builder.build()); - - auto closure = parseTrailingClosureSyntax({PreviousLoc, PreviousLoc}); - status |= closure.getStatus(); - assert(!closure.isNull()); - callBuilder.useTrailingClosure(closure.get()); - - return makeParsedResult(callBuilder.build(), status); - } - - return makeParsedResult(builder.build(), status); -} - -ParserResult -Parser::parseExprUnresolvedMember(bool isExprBasic) { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprUnresolvedMemberSyntax(isExprBasic); - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - auto expr = Generator.generate(syntax, leadingLoc); - return makeParserResult(parsed.getStatus(), expr); } ParserResult @@ -1085,7 +1087,7 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, // Handle "x.42" - a tuple index. if (Tok.is(tok::integer_literal)) { - DeclName name = Context.getIdentifier(Tok.getText()); + DeclNameRef name(Context.getIdentifier(Tok.getText())); SourceLoc nameLoc = consumeToken(tok::integer_literal); SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -1142,7 +1144,8 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Diag<> D = isa(Result.get()) ? diag::expected_identifier_after_super_dot_expr : diag::expected_member_name; - DeclName Name = parseUnqualifiedDeclName(/*afterDot=*/true, NameLoc, D); + auto Name = parseDeclNameRef(NameLoc, D, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); if (!Name) return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); @@ -1188,7 +1191,8 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, ParserStatus status = parseExprList( tok::l_square, tok::r_square, /*isPostfix=*/true, isExprBasic, lSquareLoc, indexArgs, - indexArgLabels, indexArgLabelLocs, rSquareLoc, trailingClosure); + indexArgLabels, indexArgLabelLocs, rSquareLoc, trailingClosure, + SyntaxKind::TupleExprElementList); Result = makeParserResult( status | Result, SubscriptExpr::create(Context, Result.get(), lSquareLoc, indexArgs, @@ -1209,10 +1213,12 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, isa(callee)) break; - // Add dummy blank argument list to the call expression syntax. - SyntaxContext->addSyntax( - ParsedSyntaxRecorder::makeBlankTupleExprElementList( - Tok.getLoc(), *SyntaxContext)); + if (SyntaxContext->isEnabled()) { + // Add dummy blank argument list to the call expression syntax. + SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankTupleExprElementList( + leadingTriviaLoc(), *SyntaxContext)); + } ParserResult closure = parseTrailingClosure(callee->getSourceRange()); @@ -1359,122 +1365,6 @@ ParserResult Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) { return Result; } -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - auto Token = consumeTokenSyntax(tok::integer_literal); - return ParsedSyntaxRecorder::makeIntegerLiteralExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - auto Token = consumeTokenSyntax(tok::floating_literal); - return ParsedSyntaxRecorder::makeFloatLiteralExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - auto Token = consumeTokenSyntax(tok::kw_nil); - return ParsedSyntaxRecorder::makeNilLiteralExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - auto Token = consumeTokenSyntax(); - return ParsedSyntaxRecorder::makeBooleanLiteralExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - if (Tok.getKind() == tok::kw___FILE__) { - StringRef fixit = "#file"; - diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), fixit) - .fixItReplace(Tok.getLoc(), fixit); - - auto Token = consumeTokenSyntax(tok::kw___FILE__); - return ParsedSyntaxRecorder::makeUnknownExpr({&Token, 1}, *SyntaxContext); - } - - auto Token = consumeTokenSyntax(tok::pound_file); - return ParsedSyntaxRecorder::makePoundFileExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - if (Tok.getKind() == tok::kw___LINE__) { - StringRef fixit = "#line"; - diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), fixit) - .fixItReplace(Tok.getLoc(), fixit); - - auto Token = consumeTokenSyntax(tok::kw___LINE__); - return ParsedSyntaxRecorder::makeUnknownExpr({&Token, 1}, *SyntaxContext); - } - - // FIXME: #line was renamed to #sourceLocation - auto Token = consumeTokenSyntax(tok::pound_line); - return ParsedSyntaxRecorder::makePoundLineExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - if (Tok.getKind() == tok::kw___COLUMN__) { - StringRef fixit = "#column"; - diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), fixit) - .fixItReplace(Tok.getLoc(), fixit); - - auto Token = consumeTokenSyntax(tok::kw___COLUMN__); - return ParsedSyntaxRecorder::makeUnknownExpr({&Token, 1}, *SyntaxContext); - } - - auto Token = consumeTokenSyntax(tok::pound_column); - return ParsedSyntaxRecorder::makePoundColumnExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - if (Tok.getKind() == tok::kw___FUNCTION__) { - StringRef fixit = "#function"; - diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), fixit) - .fixItReplace(Tok.getLoc(), fixit); - - auto Token = consumeTokenSyntax(tok::kw___FUNCTION__); - return ParsedSyntaxRecorder::makeUnknownExpr({&Token, 1}, *SyntaxContext); - } - - auto Token = consumeTokenSyntax(tok::pound_function); - return ParsedSyntaxRecorder::makePoundFunctionExpr(std::move(Token), *SyntaxContext); -} - -template <> -ParsedExprSyntax Parser::parseExprSyntax() { - if (Tok.getKind() == tok::kw___DSO_HANDLE__) { - StringRef fixit = "#dsohandle"; - diagnose(Tok.getLoc(), diag::snake_case_deprecated, Tok.getText(), fixit) - .fixItReplace(Tok.getLoc(), fixit); - - auto Token = consumeTokenSyntax(tok::kw___DSO_HANDLE__); - return ParsedSyntaxRecorder::makeUnknownExpr({&Token, 1}, *SyntaxContext); - } - - auto Token = consumeTokenSyntax(tok::pound_dsohandle); - return ParsedSyntaxRecorder::makePoundDsohandleExpr(std::move(Token), *SyntaxContext); -} - -template -ParserResult Parser::parseExprAST() { - auto Loc = leadingTriviaLoc(); - auto ParsedExpr = parseExprSyntax(); - SyntaxContext->addSyntax(std::move(ParsedExpr)); - // todo [gsoc]: improve this somehow - if (SyntaxContext->isTopNode()) { - auto Expr = SyntaxContext->topNode(); - auto ExprAST = Generator.generate(Expr, Loc); - return makeParserResult(ExprAST); - } - auto Expr = SyntaxContext->topNode(); - auto ExprAST = Generator.generate(Expr, Loc); - return makeParserResult(ExprAST); -} - /// parseExprPrimary /// /// expr-literal: @@ -1508,27 +1398,23 @@ ParserResult Parser::parseExprAST() { /// expr-selector /// ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { - switch (Tok.getKind()) { - case tok::integer_literal: return parseExprAST(); - case tok::floating_literal: return parseExprAST(); - case tok::kw_nil: return parseExprAST(); - case tok::kw_true: - case tok::kw_false: return parseExprAST(); - case tok::kw___FILE__: - case tok::pound_file: return parseExprAST(); - case tok::kw___LINE__: - case tok::pound_line: return parseExprAST(); - case tok::kw___COLUMN__: - case tok::pound_column: return parseExprAST(); - case tok::kw___FUNCTION__: - case tok::pound_function: return parseExprAST(); - case tok::kw___DSO_HANDLE__: - case tok::pound_dsohandle: return parseExprAST(); - default: break; - } - SyntaxParsingContext ExprContext(SyntaxContext, SyntaxContextKind::Expr); switch (Tok.getKind()) { + case tok::integer_literal: { + StringRef Text = copyAndStripUnderscores(Tok.getText()); + SourceLoc Loc = consumeToken(tok::integer_literal); + ExprContext.setCreateSyntax(SyntaxKind::IntegerLiteralExpr); + return makeParserResult(new (Context) + IntegerLiteralExpr(Text, Loc, + /*Implicit=*/false)); + } + case tok::floating_literal: { + StringRef Text = copyAndStripUnderscores(Tok.getText()); + SourceLoc Loc = consumeToken(tok::floating_literal); + ExprContext.setCreateSyntax(SyntaxKind::FloatLiteralExpr); + return makeParserResult(new (Context) FloatLiteralExpr(Text, Loc, + /*Implicit=*/false)); + } case tok::at_sign: // Objective-C programmers habitually type @"foo", so recover gracefully // with a fixit. If this isn't @"foo", just handle it like an unknown @@ -1543,7 +1429,72 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { case tok::string_literal: // "foo" return parseExprStringLiteral(); + + case tok::kw_nil: + ExprContext.setCreateSyntax(SyntaxKind::NilLiteralExpr); + return makeParserResult(new (Context) + NilLiteralExpr(consumeToken(tok::kw_nil))); + + case tok::kw_true: + case tok::kw_false: { + ExprContext.setCreateSyntax(SyntaxKind::BooleanLiteralExpr); + bool isTrue = Tok.is(tok::kw_true); + return makeParserResult(new (Context) + BooleanLiteralExpr(isTrue, consumeToken())); + } + + case tok::kw___FILE__: + case tok::kw___LINE__: + case tok::kw___COLUMN__: + case tok::kw___FUNCTION__: + case tok::kw___DSO_HANDLE__: { + StringRef replacement = ""; + switch (Tok.getKind()) { + default: llvm_unreachable("can't get here"); + case tok::kw___FILE__: replacement = "#file"; break; + case tok::kw___LINE__: replacement = "#line"; break; + case tok::kw___COLUMN__: replacement = "#column"; break; + case tok::kw___FUNCTION__: replacement = "#function"; break; + case tok::kw___DSO_HANDLE__: replacement = "#dsohandle"; break; + } + + diagnose(Tok.getLoc(), diag::snake_case_deprecated, + Tok.getText(), replacement) + .fixItReplace(Tok.getLoc(), replacement); + LLVM_FALLTHROUGH; + } + case tok::pound_filePath: + // Check twice because of fallthrough--this is ugly but temporary. + if (Tok.is(tok::pound_filePath) && !Context.LangOpts.EnableConcisePoundFile) + diagnose(Tok.getLoc(), diag::unknown_pound_expr, "filePath"); + // Continue since we actually do know how to handle it. This avoids extra + // diagnostics. + LLVM_FALLTHROUGH; + + case tok::pound_column: + case tok::pound_file: + case tok::pound_function: + case tok::pound_line: + case tok::pound_dsohandle: { + SyntaxKind SKind = SyntaxKind::UnknownExpr; + switch (Tok.getKind()) { + case tok::pound_column: SKind = SyntaxKind::PoundColumnExpr; break; + case tok::pound_file: SKind = SyntaxKind::PoundFileExpr; break; + case tok::pound_filePath: SKind = SyntaxKind::PoundFilePathExpr; break; + case tok::pound_function: SKind = SyntaxKind::PoundFunctionExpr; break; + // FIXME: #line was renamed to #sourceLocation + case tok::pound_line: SKind = SyntaxKind::PoundLineExpr; break; + case tok::pound_dsohandle: SKind = SyntaxKind::PoundDsohandleExpr; break; + default: break; + } + ExprContext.setCreateSyntax(SKind); + auto Kind = getMagicIdentifierLiteralKind(Tok.getKind()); + SourceLoc Loc = consumeToken(); + return makeParserResult(new (Context) MagicIdentifierLiteralExpr( + Kind, Loc, /*implicit=*/false)); + } + case tok::identifier: // foo case tok::kw_self: // self @@ -1559,19 +1510,22 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { // name, not a binding, because it is the start of an enum pattern or // call pattern. peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) { + DeferringContextRAII Deferring(*SyntaxContext); Identifier name; SourceLoc loc = consumeIdentifier(&name, /*allowDollarIdentifier=*/true); auto introducer = (InVarOrLetPattern != IVOLP_InVar ? VarDecl::Introducer::Let : VarDecl::Introducer::Var); auto pattern = createBindingFromPattern(loc, name, introducer); - ParsedPatternSyntax PatternNode = - ParsedSyntaxRecorder::makeIdentifierPattern( - SyntaxContext->popToken(), *SyntaxContext); - ParsedExprSyntax ExprNode = - ParsedSyntaxRecorder::makeUnresolvedPatternExpr(std::move(PatternNode), - *SyntaxContext); - SyntaxContext->addSyntax(std::move(ExprNode)); + if (SyntaxContext->isEnabled()) { + ParsedPatternSyntax PatternNode = + ParsedSyntaxRecorder::makeIdentifierPattern( + SyntaxContext->popToken(), *SyntaxContext); + ParsedExprSyntax ExprNode = + ParsedSyntaxRecorder::makeUnresolvedPatternExpr(std::move(PatternNode), + *SyntaxContext); + SyntaxContext->addSyntax(std::move(ExprNode)); + } return makeParserResult(new (Context) UnresolvedPatternExpr(pattern)); } @@ -1581,7 +1535,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { case tok::kw_Any: { // Any ExprContext.setCreateSyntax(SyntaxKind::TypeExpr); - auto TyR = parseAnyTypeAST(); + auto TyR = parseAnyType(); return makeParserResult(new (Context) TypeExpr(TypeLoc(TyR.get()))); } @@ -1604,12 +1558,13 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { case tok::period: //=.foo case tok::period_prefix: { // .foo + Tok.setKind(tok::period_prefix); + SourceLoc DotLoc = consumeToken(); + // Special case "." like ".4". This isn't valid, but the // developer almost certainly meant to use "0.4". Diagnose this, and // recover as if they wrote that. - if (peekToken().is(tok::integer_literal) && - !peekToken().isAtStartOfLine()) { - SourceLoc DotLoc = consumeToken(); + if (Tok.is(tok::integer_literal) && !Tok.isAtStartOfLine()) { diagnose(DotLoc, diag::invalid_float_literal_missing_leading_zero, Tok.getText()) .fixItInsert(DotLoc, "0") @@ -1625,8 +1580,79 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { FloatLiteralExpr(FltText, DotLoc, /*Implicit=*/false)); } + + DeclNameRef Name; + DeclNameLoc NameLoc; + + if (Tok.is(tok::code_complete)) { + auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); + auto Result = makeParserResult(CCE); + Result.setHasCodeCompletion(); + if (CodeCompletion) { + CodeCompletion->completeUnresolvedMember(CCE, DotLoc); + } + consumeToken(); + return Result; + } + + Name = parseDeclNameRef(NameLoc, diag::expected_identifier_after_dot_expr, + DeclNameFlag::AllowKeywords | DeclNameFlag::AllowCompoundNames); + if (!Name) return nullptr; + SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); + + // Check for a () suffix, which indicates a call when constructing + // this member. Note that this cannot be the start of a new line. + if (Tok.isFollowingLParen()) { + SourceLoc lParenLoc, rParenLoc; + SmallVector args; + SmallVector argLabels; + SmallVector argLabelLocs; + Expr *trailingClosure; + + ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, + /*isPostfix=*/true, isExprBasic, + lParenLoc, args, argLabels, + argLabelLocs, + rParenLoc, + trailingClosure, + SyntaxKind::TupleExprElementList); + SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); + return makeParserResult( + status, + UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, + lParenLoc, args, argLabels, + argLabelLocs, rParenLoc, + trailingClosure, + /*implicit=*/false)); + } - return parseExprUnresolvedMember(isExprBasic); + // Check for a trailing closure, if allowed. + if (Tok.is(tok::l_brace) && isValidTrailingClosure(isExprBasic, *this)) { + if (SyntaxContext->isEnabled()) { + // Add dummy blank argument list to the call expression syntax. + SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankTupleExprElementList( + leadingTriviaLoc(), *SyntaxContext)); + } + + ParserResult closure = + parseTrailingClosure(NameLoc.getSourceRange()); + if (closure.isNull()) return nullptr; + + SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); + // Handle .foo by just making an AST node. + return makeParserResult( + ParserStatus(closure), + UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, + SourceLoc(), { }, { }, { }, + SourceLoc(), closure.get(), + /*implicit=*/false)); + } + + // Handle .foo by just making an AST node. + return makeParserResult( + UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, + /*implicit=*/false)); } case tok::kw_super: // 'super' @@ -1638,7 +1664,9 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { // only one element without label. However, libSyntax tree doesn't have this // differentiation. A tuple expression node in libSyntax can have a single // element without label. - return parseExprParenOrTuple(); + ExprContext.setCreateSyntax(SyntaxKind::TupleExpr); + return parseExprList(tok::l_paren, tok::r_paren, + SyntaxKind::TupleExprElementList); case tok::l_square: return parseExprCollection(); @@ -1657,9 +1685,9 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { } #define POUND_OBJECT_LITERAL(Name, Desc, Proto) \ - case tok::pound_##Name: + case tok::pound_##Name: \ + return parseExprObjectLiteral(ObjectLiteralExpr::Name, isExprBasic); #include "swift/Syntax/TokenKinds.def" - return parseExprObjectLiteral(isExprBasic); case tok::code_complete: { auto Result = @@ -1675,7 +1703,6 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { cast(Result.get())); } } - ExprContext.setCreateSyntax(SyntaxKind::CodeCompletionExpr); consumeToken(tok::code_complete); return Result; } @@ -1740,8 +1767,9 @@ parseStringSegments(SmallVectorImpl &Segments, ParsedTrivia EmptyTrivia; bool First = true; - DeclName appendLiteral(Context, Context.Id_appendLiteral, { Identifier() }); - DeclName appendInterpolation(Context.Id_appendInterpolation); + DeclNameRef appendLiteral( + { Context, Context.Id_appendLiteral, { Identifier() } }); + DeclNameRef appendInterpolation(Context.Id_appendInterpolation); for (auto Segment : Segments) { auto InterpolationVarRef = @@ -2041,32 +2069,6 @@ ParserResult Parser::parseExprStringLiteral() { AppendingExpr)); } -/// Parse an optional argument label and ':'. Returns \c true if it's parsed. -/// Returns \c false it it's not found. -bool Parser::parseOptionalArgumentLabelSyntax( - Optional &name, Optional &colon) { - if (!Tok.canBeArgumentLabel() || !peekToken().is(tok::colon)) - return false; - - // If this was an escaped identifier that need not have been escaped, say - // so. Only _ needs escaping, because we take foo(_: 3) to be equivalent - // to foo(3), to be more uniform with _ in function declaration as well as - // the syntax for referring to the function pointer (foo(_:)), - auto text = Tok.getText(); - auto escaped = Tok.isEscapedIdentifier(); - auto underscore = Tok.is(tok::kw__) || (escaped && text == "_"); - if (escaped && !underscore && canBeArgumentLabel(text)) { - SourceLoc start = Tok.getLoc(); - SourceLoc end = start.getAdvancedLoc(Tok.getLength()); - diagnose(Tok, diag::escaped_parameter_name, text) - .fixItRemoveChars(start, start.getAdvancedLoc(1)) - .fixItRemoveChars(end.getAdvancedLoc(-1), end); - } - name = consumeArgumentLabelSyntax(); - colon = consumeTokenSyntax(tok::colon); - return true; -} - void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { // Check to see if there is an argument label. if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) { @@ -2091,197 +2093,240 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) { } } -/// Parse unqualified decl name. -/// -/// decl-name: -/// identifier decl-name-arguments? -/// decl-name-arguments: -/// '(' decl-name-argument* ')' -/// decl-name-argument: -/// (identifier | '_') ':' -ParserStatus Parser::parseUnqualifiedDeclNameSyntax( - Optional &identTok, - Optional &declNameArg, bool afterDot, - const Diagnostic &diag, bool allowOperators, bool allowZeroArgCompoundNames, - bool allowDeinitAndSubscript) { +static bool tryParseArgLabelList(Parser &P, Parser::DeclNameOptions flags, + SourceLoc &lparenLoc, + SmallVectorImpl &argumentLabels, + SmallVectorImpl &argumentLabelLocs, + SourceLoc &rparenLoc) { + if (!flags.contains(Parser::DeclNameFlag::AllowCompoundNames)) + return false; - if (Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_self)) { - identTok = consumeTokenSyntax(); - } else if (allowOperators && Tok.isAnyOperator()) { - identTok = consumeTokenSyntax(); - } else if (afterDot && Tok.isKeyword()) { - if (Tok.is(tok::kw_init) || - (allowDeinitAndSubscript && - Tok.isAny(tok::kw_deinit, tok::kw_subscript))) { - // Parse 'init', 'deinit' and 'subscript' as a keyword. - } else { - Tok.setKind(tok::identifier); - } - identTok = consumeTokenSyntax(); - } else { - checkForInputIncomplete(); - diagnose(Tok, diag); - return makeParserError(); - } + // Is the current token a left paren? + if (!P.Tok.isFollowingLParen()) + return false; - // If the next token isn't a following '(', we don't have a compound name. - if (!Tok.isFollowingLParen()) - return makeParserSuccess(); + // Okay, let's look ahead and see if the next token is something that could + // be in an arg label list... + const Token &next = P.peekToken(); - // If the next token is a ')' then we have a 0-arg compound name. This is - // explicitly differentiated from "simple" (non-compound) name in DeclName. - // Unfortunately only some places in the grammar are ok with accepting this - // kind of name; in other places it's ambiguous with trailing calls. - if (allowZeroArgCompoundNames && peekToken().is(tok::r_paren)) { - ParsedDeclNameArgumentsSyntaxBuilder builder(*SyntaxContext); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - builder.useRightParen(consumeTokenSyntax(tok::r_paren)); - declNameArg = builder.build(); - return makeParserSuccess(); - } + // A close parenthesis, if empty lists are allowed. + bool nextIsRParen = + flags.contains(Parser::DeclNameFlag::AllowZeroArgCompoundNames) && + next.is(tok::r_paren); + // An argument label. + bool nextIsArgLabel = next.canBeArgumentLabel() || next.is(tok::colon); + // An editor placeholder. + bool nextIsPlaceholder = Identifier::isEditorPlaceholder(next.getText()); - // If the token after that isn't an argument label or ':', we don't have a - // compound name. - if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) || - Identifier::isEditorPlaceholder(peekToken().getText())) - return makeParserSuccess(); - - ParsedDeclNameArgumentsSyntaxBuilder builder(*SyntaxContext); + if (!(nextIsRParen || nextIsArgLabel || nextIsPlaceholder)) + return false; // Try to parse a compound name. - BacktrackingScope backtrack(*this); + SyntaxParsingContext ArgsCtxt(P.SyntaxContext, SyntaxKind::DeclNameArguments); + Parser::BacktrackingScope backtrack(P); - // Parse '('. - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - while (Tok.isNot(tok::r_paren)) { - ParsedDeclNameArgumentSyntaxBuilder argBuilder(*SyntaxContext); + lparenLoc = P.consumeToken(tok::l_paren); + while (P.Tok.isNot(tok::r_paren)) { + SyntaxParsingContext ArgCtxt(P.SyntaxContext, SyntaxKind::DeclNameArgument); // If we see a ':', the user forgot the '_'; - if (Tok.is(tok::colon)) { - diagnose(Tok, diag::empty_arg_label_underscore) - .fixItInsert(Tok.getLoc(), "_"); - argBuilder.useColon(consumeTokenSyntax(tok::colon)); - builder.addArgumentsMember(argBuilder.build()); - continue; + if (P.Tok.is(tok::colon)) { + P.diagnose(P.Tok, diag::empty_arg_label_underscore) + .fixItInsert(P.Tok.getLoc(), "_"); + argumentLabels.push_back(Identifier()); + argumentLabelLocs.push_back(P.consumeToken(tok::colon)); } - Optional name; - Optional colon; - if (parseOptionalArgumentLabelSyntax(name, colon)) { - argBuilder.useName(std::move(*name)); - argBuilder.useColon(std::move(*colon)); - builder.addArgumentsMember(argBuilder.build()); + Identifier argName; + SourceLoc argLoc; + P.parseOptionalArgumentLabel(argName, argLoc); + if (argLoc.isValid()) { + argumentLabels.push_back(argName); + argumentLabelLocs.push_back(argLoc); continue; } // This is not a compound name. // FIXME: Could recover better if we "know" it's a compound name. - return makeParserSuccess(); + ArgCtxt.setBackTracking(); + ArgsCtxt.setBackTracking(); + + return false; } // We have a compound name. Cancel backtracking and build that name. backtrack.cancelBacktrack(); - // Parse ')'. - builder.useRightParen(consumeTokenSyntax(tok::r_paren)); - declNameArg = builder.build(); - - return makeParserSuccess(); -} - -DeclName Parser::parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, - const Diagnostic &diag, - bool allowOperators, - bool allowZeroArgCompoundNames, - bool allowDeinitAndSubscript) { - auto leadingLoc = leadingTriviaLoc(); - Optional parsedIdentTok; - Optional parsedDeclNameArgs; - auto status = parseUnqualifiedDeclNameSyntax( - parsedIdentTok, parsedDeclNameArgs, afterDot, diag, allowOperators, - allowZeroArgCompoundNames, allowDeinitAndSubscript); - if (status.isError()) - return DeclName(); + if (argumentLabels.empty() && P.SyntaxContext->isEnabled()) + P.SyntaxContext->addSyntax( + ParsedSyntaxRecorder::makeBlankDeclNameArgumentList( + P.leadingTriviaLoc(), *P.SyntaxContext)); + else + ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList); - SyntaxContext->addSyntax(std::move(*parsedIdentTok)); - auto identTok = SyntaxContext->topNode(); + rparenLoc = P.consumeToken(tok::r_paren); - Optional declNameArgs; - if (parsedDeclNameArgs) { - SyntaxContext->addSyntax(std::move(*parsedDeclNameArgs)); - declNameArgs.emplace(SyntaxContext->topNode()); - } + assert(argumentLabels.size() == argumentLabelLocs.size()); - DeclName name; - std::tie(name, loc) = - Generator.generateUnqualifiedDeclName(identTok, declNameArgs, leadingLoc); - return name; + return true; } -/// expr-identifier: -/// unqualified-decl-name generic-argument-clause? -ParsedSyntaxResult Parser::parseExprIdentifierSyntax() { - assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); - - if (swift::isEditorPlaceholder(Tok.getRawText())) { - return makeParsedResult(ParsedSyntaxRecorder::makeEditorPlaceholderExpr( - consumeTokenSyntax(), *SyntaxContext)); +DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc, + const Diagnostic &diag, + DeclNameOptions flags) { + // Consume the base name. + DeclBaseName baseName; + SourceLoc baseNameLoc; + if (Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_self)) { + Identifier baseNameId; + baseNameLoc = consumeIdentifier( + &baseNameId, /*allowDollarIdentifier=*/true); + baseName = baseNameId; + } else if (flags.contains(DeclNameFlag::AllowOperators) && + Tok.isAnyOperator()) { + baseName = Context.getIdentifier(Tok.getText()); + baseNameLoc = consumeToken(); + } else if (flags.contains(DeclNameFlag::AllowKeywords) && Tok.isKeyword()) { + bool specialDeinitAndSubscript = + flags.contains(DeclNameFlag::AllowKeywordsUsingSpecialNames); + + // Syntax highlighting should treat this token as an identifier and + // not as a keyword. + if (Tok.is(tok::kw_init)) + baseName = DeclBaseName::createConstructor(); + else if (specialDeinitAndSubscript && Tok.is(tok::kw_deinit)) + baseName = DeclBaseName::createDestructor(); + else if (specialDeinitAndSubscript && Tok.is(tok::kw_subscript)) + baseName = DeclBaseName::createSubscript(); + else + baseName = Context.getIdentifier(Tok.getText()); + Tok.setKind(tok::identifier); + baseNameLoc = consumeToken(); + } else { + baseName = Context.getIdentifier(Tok.getText()); + checkForInputIncomplete(); + diagnose(Tok, diag); + return DeclNameRef(); } - Optional idTok; - Optional declNameArgs; - auto status = parseUnqualifiedDeclNameSyntax(idTok, declNameArgs, - /*afterDot=*/false, - diag::expected_expr); - assert(status.isSuccess() && idTok.hasValue()); - (void)status; + // Parse an argument list, if the flags allow it and it's present. + SmallVector argumentLabels; + SmallVector argumentLabelLocs; + SourceLoc lparenLoc; + SourceLoc rparenLoc; - ParsedIdentifierExprSyntaxBuilder builder(*SyntaxContext); - builder.useIdentifier(std::move(*idTok)); - if (declNameArgs) - builder.useDeclNameArguments(std::move(*declNameArgs)); + bool hadArgList = tryParseArgLabelList(*this, flags, lparenLoc, + argumentLabels, argumentLabelLocs, + rparenLoc); - if (canParseAsGenericArgumentList()) - return parseExprSpecializeSyntax(builder.build()); + if (argumentLabelLocs.empty() || !hadArgList) + loc = DeclNameLoc(baseNameLoc); + else + loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs, + rparenLoc); - return makeParsedResult(builder.build()); -} + if (!hadArgList) + return DeclNameRef(baseName); -/// Parse generic argument suffix for the base expression. -/// -/// expr-specialize: -/// expr generic-argument-clause -ParsedSyntaxResult -Parser::parseExprSpecializeSyntax(ParsedExprSyntax &&base) { - assert(startsWithLess(Tok)); - - auto LAngleLoc = Tok.getLoc(); - auto genericArgs = parseGenericArgumentClauseSyntax(); - auto status = genericArgs.getStatus(); - - if (status.isError()) - diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket); - assert(!genericArgs.isNull()); - - auto specializeExpr = ParsedSyntaxRecorder::makeSpecializeExpr( - std::move(base), genericArgs.get(), *SyntaxContext); - return makeParsedResult(std::move(specializeExpr), status); + return DeclNameRef({ Context, baseName, argumentLabels }); } +/// expr-identifier: +/// unqualified-decl-name generic-args? Expr *Parser::parseExprIdentifier() { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprIdentifierSyntax(); - assert(!parsed.isNull()); - SyntaxContext->addSyntax(parsed.get()); + assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); + SyntaxParsingContext IDSyntaxContext(SyntaxContext, + SyntaxKind::IdentifierExpr); + Token IdentTok = Tok; + + // Parse the unqualified-decl-name. + DeclNameLoc loc; + DeclNameRef name = parseDeclNameRef(loc, diag::expected_expr, + DeclNameFlag::AllowCompoundNames); + + SmallVector args; + SourceLoc LAngleLoc, RAngleLoc; + bool hasGenericArgumentList = false; + + /// The generic-args case is ambiguous with an expression involving '<' + /// and '>' operators. The operator expression is favored unless a generic + /// argument list can be successfully parsed, and the closing bracket is + /// followed by one of these tokens: + /// lparen_following rparen lsquare_following rsquare lbrace rbrace + /// period_following comma semicolon + /// + if (canParseAsGenericArgumentList()) { + SyntaxContext->createNodeInPlace(SyntaxKind::IdentifierExpr); + SyntaxContext->setCreateSyntax(SyntaxKind::SpecializeExpr); + auto argStat = parseGenericArguments(args, LAngleLoc, RAngleLoc); + if (argStat.isError()) + diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket); + + // The result can be empty in error cases. + hasGenericArgumentList = !args.empty(); + } + + ValueDecl *D = nullptr; + if (!InPoundIfEnvironment) { + D = lookupInScope(name); + // FIXME: We want this to work: "var x = { x() }", but for now it's better + // to disallow it than to crash. + if (D) { + for (auto activeVar : DisabledVars) { + if (activeVar == D) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } + } + } else { + for (auto activeVar : DisabledVars) { + if (activeVar->getFullName() == name.getFullName()) { + diagnose(loc.getBaseNameLoc(), DisabledVarReason); + return new (Context) ErrorExpr(loc.getSourceRange()); + } + } + } + } + + Expr *E; + if (D == nullptr) { + if (name.getBaseName().isEditorPlaceholder()) { + IDSyntaxContext.setCreateSyntax(SyntaxKind::EditorPlaceholderExpr); + return parseExprEditorPlaceholder(IdentTok, name.getBaseIdentifier()); + } - auto syntax = SyntaxContext->topNode(); - return Generator.generate(syntax, leadingLoc); + auto refKind = DeclRefKind::Ordinary; + E = new (Context) UnresolvedDeclRefExpr(name, refKind, loc); + } else if (auto TD = dyn_cast(D)) { + // When parsing default argument expressions for generic functions, + // we haven't built a FuncDecl or re-parented the GenericTypeParamDecls + // to the FuncDecl yet. Other than that, we should only ever find + // global or local declarations here. + assert(!TD->getDeclContext()->isTypeContext() || + isa(TD)); + E = TypeExpr::createForDecl(loc, TD, /*DC*/nullptr, /*implicit*/false); + } else { + E = new (Context) DeclRefExpr(D, loc, /*Implicit=*/false); + } + + if (hasGenericArgumentList) { + SmallVector locArgs; + for (auto ty : args) + locArgs.push_back(ty); + E = UnresolvedSpecializeExpr::create(Context, E, LAngleLoc, locArgs, + RAngleLoc); + } + return E; } -Expr *Parser::parseExprEditorPlaceholder(SourceLoc loc, StringRef text) { +Expr *Parser::parseExprEditorPlaceholder(Token PlaceholderTok, + Identifier PlaceholderId) { + assert(PlaceholderTok.is(tok::identifier)); + assert(PlaceholderId.isEditorPlaceholder()); + auto parseTypeForPlaceholder = [&](TypeLoc &TyLoc, TypeRepr *&ExpansionTyR) { Optional DataOpt = - swift::parseEditorPlaceholder(text); + swift::parseEditorPlaceholder(PlaceholderTok.getText()); if (!DataOpt) return; StringRef TypeStr = DataOpt->Type; @@ -2292,8 +2337,8 @@ Expr *Parser::parseExprEditorPlaceholder(SourceLoc loc, StringRef text) { ParserPositionRAII PPR(*this); auto parseTypeString = [&](StringRef TyStr) -> TypeRepr* { - unsigned Offset = TyStr.data() - text.data(); - SourceLoc TypeStartLoc = loc.getAdvancedLoc(Offset); + unsigned Offset = TyStr.data() - PlaceholderTok.getText().data(); + SourceLoc TypeStartLoc = PlaceholderTok.getLoc().getAdvancedLoc(Offset); SourceLoc TypeEndLoc = TypeStartLoc.getAdvancedLoc(TyStr.size()); LexerState StartState = L->getStateForBeginningOfTokenLoc(TypeStartLoc); @@ -2309,7 +2354,7 @@ Expr *Parser::parseExprEditorPlaceholder(SourceLoc loc, StringRef text) { ConsumeTokenReceiver DisabledRec; llvm::SaveAndRestore R(TokReceiver, &DisabledRec); SyntaxParsingContext SContext(SyntaxContext); - SContext.setDiscard(); + SContext.disable(); Tok.setKind(tok::unknown); // we might be at tok::eof now. consumeTokenWithoutFeedingReceiver(); @@ -2328,8 +2373,9 @@ Expr *Parser::parseExprEditorPlaceholder(SourceLoc loc, StringRef text) { TypeLoc TyLoc; TypeRepr *ExpansionTyR = nullptr; parseTypeForPlaceholder(TyLoc, ExpansionTyR); - return new (Context) EditorPlaceholderExpr(Context.getIdentifier(text), - loc, TyLoc, ExpansionTyR); + return new (Context) EditorPlaceholderExpr(PlaceholderId, + PlaceholderTok.getLoc(), + TyLoc, ExpansionTyR); } // Extract names of the tuple elements and preserve the structure @@ -2369,11 +2415,15 @@ static void printTupleNames(const TypeRepr *typeRepr, llvm::raw_ostream &OS) { } bool Parser:: -parseClosureSignatureIfPresent(SmallVectorImpl &captureList, +parseClosureSignatureIfPresent(SourceRange &bracketRange, + SmallVectorImpl &captureList, + VarDecl *&capturedSelfDecl, ParameterList *¶ms, SourceLoc &throwsLoc, SourceLoc &arrowLoc, TypeRepr *&explicitResultType, SourceLoc &inLoc){ // Clear out result parameters. + bracketRange = SourceRange(); + capturedSelfDecl = nullptr; params = nullptr; throwsLoc = SourceLoc(); arrowLoc = SourceLoc(); @@ -2442,14 +2492,16 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, } SyntaxParsingContext ClosureSigCtx(SyntaxContext, SyntaxKind::ClosureSignature); if (Tok.is(tok::l_square) && peekToken().is(tok::r_square)) { + SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); - consumeToken(tok::r_square); + SourceLoc lBracketLoc = consumeToken(tok::l_square); + SourceLoc rBracketLoc = consumeToken(tok::r_square); + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } else if (Tok.is(tok::l_square) && !peekToken().is(tok::r_square)) { SyntaxParsingContext CaptureCtx(SyntaxContext, SyntaxKind::ClosureCaptureSignature); - consumeToken(tok::l_square); + SourceLoc lBracketLoc = consumeToken(tok::l_square); // At this point, we know we have a closure signature. Parse the capture list // and parameters. bool HasNext; @@ -2539,6 +2591,10 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, auto *VD = new (Context) VarDecl(/*isStatic*/false, introducer, /*isCaptureList*/true, nameLoc, name, CurDeclContext); + + // If we captured something under the name "self", remember that. + if (name == Context.Id_self) + capturedSelfDecl = VD; // Attributes. if (ownershipKind != ReferenceOwnership::Strong) @@ -2552,17 +2608,22 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, /*VarLoc*/ nameLoc, pattern, /*EqualLoc*/ equalLoc, initializer, CurDeclContext); - captureList.push_back(CaptureListEntry(VD, PBD)); + auto CLE = CaptureListEntry(VD, PBD); + if (CLE.isSimpleSelfCapture()) + VD->setIsSelfParamCapture(); + captureList.push_back(CLE); } while (HasNext); SyntaxContext->collectNodesInPlace(SyntaxKind::ClosureCaptureItemList); // The capture list needs to be closed off with a ']'. + SourceLoc rBracketLoc = Tok.getLoc(); if (!consumeIf(tok::r_square)) { diagnose(Tok, diag::expected_capture_list_end_rsquare); skipUntil(tok::r_square); if (Tok.is(tok::r_square)) - consumeToken(tok::r_square); + rBracketLoc = consumeToken(tok::r_square); } + bracketRange = SourceRange(lBracketLoc, rBracketLoc); } bool invalid = false; @@ -2674,11 +2735,10 @@ parseClosureSignatureIfPresent(SmallVectorImpl &captureList, // to detect any tuple splat or destructuring as early as // possible and give a proper fix-it. See SE-0110 for more details. auto isTupleDestructuring = [](ParamDecl *param) -> bool { - if (!param->isInvalid()) + auto *typeRepr = param->getTypeRepr(); + if (!(typeRepr && param->isDestructured())) return false; - if (auto *typeRepr = param->getTypeRepr()) - return !param->hasName() && isa(typeRepr); - return false; + return !param->hasName() && isa(typeRepr); }; for (unsigned i = 0, e = params->size(); i != e; ++i) { @@ -2721,14 +2781,17 @@ ParserResult Parser::parseExprClosure() { SourceLoc leftBrace = consumeToken(); // Parse the closure-signature, if present. + SourceRange bracketRange; + SmallVector captureList; + VarDecl *capturedSelfDecl; ParameterList *params = nullptr; SourceLoc throwsLoc; SourceLoc arrowLoc; TypeRepr *explicitResultType; SourceLoc inLoc; - SmallVector captureList; - parseClosureSignatureIfPresent(captureList, params, throwsLoc, arrowLoc, - explicitResultType, inLoc); + parseClosureSignatureIfPresent(bracketRange, captureList, + capturedSelfDecl, params, throwsLoc, + arrowLoc, explicitResultType, inLoc); // If the closure was created in the context of an array type signature's // size expression, there will not be a local context. A parse error will @@ -2743,9 +2806,10 @@ ParserResult Parser::parseExprClosure() { unsigned discriminator = CurLocalContext->claimNextClosureDiscriminator(); // Create the closure expression and enter its context. - auto *closure = new (Context) ClosureExpr(params, throwsLoc, arrowLoc, inLoc, - explicitResultType, - discriminator, CurDeclContext); + auto *closure = new (Context) ClosureExpr(bracketRange, capturedSelfDecl, + params, throwsLoc, arrowLoc, inLoc, + explicitResultType, discriminator, + CurDeclContext); // The arguments to the func are defined in their own scope. Scope S(this, ScopeKind::ClosureParams); ParseFunctionBody cc(*this, closure); @@ -2760,7 +2824,7 @@ ParserResult Parser::parseExprClosure() { // FIXME: We could do this all the time, and then provide Fix-Its // to map $i -> the appropriately-named argument. This might help // users who are refactoring code by adding names. - AnonClosureVars.push_back({ leftBrace, {}}); + AnonClosureVars.push_back({{}, leftBrace}); } // Add capture list variables to scope. @@ -2774,17 +2838,17 @@ ParserResult Parser::parseExprClosure() { // Parse the closing '}'. SourceLoc rightBrace; - if (parseMatchingToken(tok::r_brace, rightBrace, - diag::expected_closure_rbrace, leftBrace)) { - // Synthesize an r_brace syntax node if the token is absent - SyntaxContext->synthesize(tok::r_brace, rightBrace); - } + bool missingRBrace = parseMatchingToken(tok::r_brace, rightBrace, + diag::expected_closure_rbrace, + leftBrace); + if (missingRBrace) + Status.setIsParseError(); // If we didn't have any parameters, create a parameter list from the // anonymous closure arguments. if (!params) { // Create a parameter pattern containing the anonymous variables. - auto &anonVars = AnonClosureVars.back().second; + auto &anonVars = AnonClosureVars.back().Item; SmallVector elements; for (auto anonVar : anonVars) elements.push_back(anonVar); @@ -2806,7 +2870,8 @@ ParserResult Parser::parseExprClosure() { // may be incomplete and the type mismatch in return statement will just // confuse the type checker. bool hasSingleExpressionBody = false; - if (!Status.hasCodeCompletion() && bodyElements.size() == 1) { + if (!missingRBrace && !Status.hasCodeCompletion() && + bodyElements.size() == 1) { // If the closure's only body element is a single return statement, // use that instead of creating a new wrapping return expression. Expr *returnExpr = nullptr; @@ -2875,8 +2940,8 @@ Expr *Parser::parseExprAnonClosureArg() { if (Context.LangOpts.DebuggerSupport) { auto refKind = DeclRefKind::Ordinary; auto identifier = Context.getIdentifier(Name); - return new (Context) UnresolvedDeclRefExpr(DeclName(identifier), refKind, - DeclNameLoc(Loc)); + return new (Context) UnresolvedDeclRefExpr(DeclNameRef(identifier), + refKind, DeclNameLoc(Loc)); } diagnose(Loc, diag::anon_closure_arg_not_in_closure); return new (Context) ErrorExpr(Loc); @@ -2896,8 +2961,8 @@ Expr *Parser::parseExprAnonClosureArg() { } } - auto leftBraceLoc = AnonClosureVars.back().first; - auto &decls = AnonClosureVars.back().second; + auto leftBraceLoc = AnonClosureVars.back().Loc; + auto &decls = AnonClosureVars.back().Item; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); SmallVector StrBuf; @@ -2917,157 +2982,59 @@ Expr *Parser::parseExprAnonClosureArg() { } -/// Parse a tuple expression or a paren expression. -/// -/// expr-tuple: -/// '(' ')' -/// '(' expr-tuple-element-list ')' -ParsedSyntaxResult Parser::parseExprTupleSyntax() { - ParsedTupleExprSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - StructureMarkerRAII ParsingExprList(*this, Tok); - if (ParsingExprList.isFailed()) - return makeParserError(); - - // Parse '('. - auto LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - // Parse the elements. - SmallVector elements; - status |= parseExprTupleElementListSyntax( - elements, [&]() { return Tok.is(tok::r_paren); }); - for (auto &elem : elements) - builder.addElementListMember(std::move(elem)); - - // Parse ')'. - auto RParen = - parseMatchingTokenSyntax(tok::r_paren, diag::expected_rparen_expr_list, - LParenLoc, /*silenceDiag=*/status.isError()); - status |= RParen.getStatus(); - if (!RParen.isNull()) - builder.useRightParen(RParen.get()); - - return makeParsedResult(builder.build(), status); -} - -ParserResult Parser::parseExprParenOrTuple() { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprTupleSyntax(); - // NOTE: 'parsed' can be null if the nesting structure is too deep. - if (!parsed.isNull()) { - SyntaxContext->addSyntax(parsed.get()); - auto syntax = SyntaxContext->topNode(); - return makeParserResult(parsed.getStatus(), - Generator.generate(syntax, leadingLoc)); - } - return parsed.getStatus(); -} - /// parseExprList - Parse a list of expressions. /// -/// expr-tuple-element-list: -/// expr-tuple-element (',' expr-tuple-element)* -/// expr-tuple-element: -/// (identifier ':')? (expr | binary-operator) +/// expr-paren: +/// lparen-any ')' +/// lparen-any binary-op ')' +/// lparen-any expr-paren-element (',' expr-paren-element)* ')' /// -ParserStatus Parser::parseExprTupleElementListSyntax( - SmallVectorImpl &elements, - llvm::function_ref isAtCloseTok) { - return parseListSyntax( - elements, /*AllowEmpty=*/true, /*AllowSepAfterLast=*/false, isAtCloseTok, - [&](ParsedTupleExprElementSyntaxBuilder &elemBuilder) { - Optional label; - Optional colon; - if (parseOptionalArgumentLabelSyntax(label, colon)) { - elemBuilder.useLabel(std::move(*label)); - elemBuilder.useColon(std::move(*colon)); - } - - // See if we have an operator decl ref '()'. The operator token in - // this case lexes as a binary operator because it neither leads nor - // follows a proper subexpression. - if (Tok.isBinaryOperator() && - peekToken().isAny(tok::r_paren, tok::r_square, tok::eof, - tok::comma)) { - elemBuilder.useExpression(ParsedSyntaxRecorder::makeIdentifierExpr( - consumeTokenSyntax(), None, *SyntaxContext)); - return makeParserSuccess(); - } - - auto subExpr = parseExpressionSyntax(diag::expected_expr_in_expr_list); - if (!subExpr.isNull()) - elemBuilder.useExpression(subExpr.get()); - else - elemBuilder.useExpression( - ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - return subExpr.getStatus(); - }); -} - -/// Parse a parenthesized expression list for a call-like expressions including -/// subscripts. +/// expr-paren-element: +/// (identifier ':')? expr /// -/// expr-list(left, right): -/// left right expr-closure? -/// left expr-tuple-element-list right expr-closure? -ParserStatus Parser::parseExprListSyntax( - tok leftK, tok rightK, bool isPostfix, bool isExprBasic, - llvm::function_ref &&, Optional &&)> - callback) { - StructureMarkerRAII ParsingExprList(*this, Tok); - if (ParsingExprList.isFailed()) - return makeParserError(); - - ParserStatus status; - - auto TokIsStringInterpolationEOF = [&]() -> bool { - return Tok.is(tok::eof) && Tok.getText() == ")" && rightK == tok::r_paren; - }; - - // Parse '(' or '['. - auto leftLoc = Tok.getLoc(); - auto leftTok = consumeTokenSyntax(tok::l_paren); - - // Parse the elements. - SmallVector elements; - status |= parseExprTupleElementListSyntax(elements, [&] { - return Tok.is(rightK) || TokIsStringInterpolationEOF(); - }); - auto list = - ParsedSyntaxRecorder::makeTupleExprElementList(elements, *SyntaxContext); - - if (TokIsStringInterpolationEOF()) { - callback(std::move(leftTok), std::move(list), None, None); - return status; - } - - // Parse ')' or ']'. - auto rightTok = - parseMatchingTokenSyntax(rightK, rightK == tok::r_paren - ? diag::expected_rparen_expr_list - : diag::expected_rsquare_expr_list, - leftLoc, /*silenceDiag=*/status.isError()); - status |= rightTok.getStatus(); - auto rightLoc = PreviousLoc; - - // If we aren't interested in trailing closures, or there isn't a valid one, - // we're done. - if (rightTok.isNull() || !isPostfix || Tok.isNot(tok::l_brace) || - !isValidTrailingClosure(isExprBasic, *this)) { - callback(std::move(leftTok), std::move(list), rightTok.getOrNull(), None); - return status; +ParserResult +Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { + SmallVector subExprs; + SmallVector subExprNames; + SmallVector subExprNameLocs; + Expr *trailingClosure = nullptr; + + SourceLoc leftLoc, rightLoc; + ParserStatus status = parseExprList(leftTok, rightTok, /*isPostfix=*/false, + /*isExprBasic=*/true, + leftLoc, + subExprs, + subExprNames, + subExprNameLocs, + rightLoc, + trailingClosure, + Kind); + + // A tuple with a single, unlabeled element is just parentheses. + if (subExprs.size() == 1 && + (subExprNames.empty() || subExprNames[0].empty())) { + return makeParserResult( + status, new (Context) ParenExpr(leftLoc, subExprs[0], rightLoc, + /*hasTrailingClosure=*/false)); } - auto closure = parseTrailingClosureSyntax({leftLoc, rightLoc}); - status |= closure.getStatus(); - callback(std::move(leftTok), std::move(list), rightTok.getOrNull(), closure.getOrNull()); - return status; + return makeParserResult( + status, + TupleExpr::create(Context, leftLoc, subExprs, subExprNames, + subExprNameLocs, rightLoc, /*HasTrailingClosure=*/false, + /*Implicit=*/false)); } +/// parseExprList - Parse a list of expressions. +/// +/// expr-paren: +/// lparen-any ')' +/// lparen-any binary-op ')' +/// lparen-any expr-paren-element (',' expr-paren-element)* ')' +/// +/// expr-paren-element: +/// (identifier ':')? expr +/// ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, bool isPostfix, bool isExprBasic, @@ -3076,53 +3043,82 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - Expr *&trailingClosure) { + Expr *&trailingClosure, + SyntaxKind Kind) { trailingClosure = nullptr; StructureMarkerRAII ParsingExprList(*this, Tok); - if (ParsingExprList.isFailed()) + + if (ParsingExprList.isFailed()) { return makeParserError(); - - auto TokIsStringInterpolationEOF = [&]() -> bool { - return Tok.is(tok::eof) && Tok.getText() == ")" && rightTok == tok::r_paren; - }; + } leftLoc = consumeToken(leftTok); + ParserStatus status = parseList(rightTok, leftLoc, rightLoc, + /*AllowSepAfterLast=*/false, + rightTok == tok::r_paren + ? diag::expected_rparen_expr_list + : diag::expected_rsquare_expr_list, + Kind, + [&] () -> ParserStatus { + Identifier FieldName; + SourceLoc FieldNameLoc; + if (Kind != SyntaxKind::YieldStmt) + parseOptionalArgumentLabel(FieldName, FieldNameLoc); + + // See if we have an operator decl ref '()'. The operator token in + // this case lexes as a binary operator because it neither leads nor + // follows a proper subexpression. + ParserStatus Status; + Expr *SubExpr = nullptr; + if (Tok.isBinaryOperator() && peekToken().isAny(rightTok, tok::comma)) { + SyntaxParsingContext operatorContext(SyntaxContext, + SyntaxKind::IdentifierExpr); + DeclNameLoc Loc; + auto OperName = parseDeclNameRef(Loc, diag::expected_operator_ref, + DeclNameFlag::AllowOperators); + if (!OperName) { + return makeParserError(); + } + // Bypass local lookup. Use an 'Ordinary' reference kind so that the + // reference may resolve to any unary or binary operator based on + // context. + SubExpr = new(Context) UnresolvedDeclRefExpr(OperName, + DeclRefKind::Ordinary, Loc); + } else if (isPostfix && Tok.is(tok::code_complete)) { + // Handle call arguments specially because it may need argument labels. + auto CCExpr = new (Context) CodeCompletionExpr(Tok.getLoc()); + if (CodeCompletion) + CodeCompletion->completeCallArg(CCExpr, PreviousLoc == leftLoc); + consumeIf(tok::code_complete); + SubExpr = CCExpr; + Status.setHasCodeCompletion(); + } else { + auto ParsedSubExpr = parseExpr(diag::expected_expr_in_expr_list); + SubExpr = ParsedSubExpr.getPtrOrNull(); + Status = ParsedSubExpr; + } - ParserStatus status; - // Parse the parenthesized expression list. - { - auto leadingLoc = leadingTriviaLoc(); - SmallVector parsedElements; - status |= parseExprTupleElementListSyntax(parsedElements, [&] { - return Tok.is(rightTok) || TokIsStringInterpolationEOF(); - }); - - // Elements. - SyntaxContext->addSyntax(ParsedSyntaxRecorder::makeTupleExprElementList( - parsedElements, *SyntaxContext)); - auto elements = SyntaxContext->topNode(); - Generator.generateExprTupleElementList(elements, leadingLoc, - /*isForCallArguments=*/true, exprs, - exprLabels, exprLabelLocs); - } + // If we got a subexpression, add it. + if (SubExpr) { + // Update names and locations. + if (!exprLabels.empty()) { + exprLabels.push_back(FieldName); + exprLabelLocs.push_back(FieldNameLoc); + } else if (FieldNameLoc.isValid()) { + exprLabels.resize(exprs.size()); + exprLabels.push_back(FieldName); + + exprLabelLocs.resize(exprs.size()); + exprLabelLocs.push_back(FieldNameLoc); + } - if (TokIsStringInterpolationEOF()) { - rightLoc = Tok.getLoc(); - return status; - } + // Add the subexpression. + exprs.push_back(SubExpr); + } - if (status.isError()) { - // If we've already got errors, don't emit missing RightK diagnostics. - rightLoc = - Tok.is(rightTok) ? consumeToken() : getLocForMissingMatchingToken(); - } else if (parseMatchingToken(rightTok, rightLoc, - rightTok == tok::r_paren - ? diag::expected_rparen_expr_list - : diag::expected_rsquare_expr_list, - leftLoc)) { - status.setIsParseError(); - } + return Status; + }); // If we aren't interested in trailing closures, or there isn't a valid one, // we're done. @@ -3174,63 +3170,46 @@ ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { return closure; } - -ParsedSyntaxResult -Parser::parseTrailingClosureSyntax(SourceRange calleeRange) { - SyntaxParsingContext TmpContext(SyntaxContext); - TmpContext.setTransparent(); - - SourceLoc ExprLoc = Tok.getLoc(); - ParserResult Result = parseTrailingClosure(calleeRange); - if (auto ParsedExpr = TmpContext.popIf()) { - Generator.addExpr(Result.getPtrOrNull(), ExprLoc); - return makeParsedResult(std::move(*ParsedExpr), Result.getStatus()); - } - return Result.getStatus(); -} /// Parse an object literal expression. /// /// expr-literal: /// '#' identifier expr-paren -ParsedSyntaxResult -Parser::parseExprObjectLiteralSyntax(bool isExprBasic) { - ParsedObjectLiteralExprSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - builder.useIdentifier(consumeTokenSyntax()); - +ParserResult +Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, + bool isExprBasic) { + SyntaxParsingContext ObjectLiteralContext(SyntaxContext, + SyntaxKind::ObjectLiteralExpr); + SourceLoc PoundLoc = consumeToken(); + // Parse a tuple of args if (!Tok.is(tok::l_paren)) { diagnose(Tok, diag::expected_arg_list_in_object_literal); - return makeParsedError(builder.build()); - } - - status |= parseExprListSyntax( - tok::l_paren, tok::r_paren, - /*isPostfix=*/true, isExprBasic, - [&](ParsedTokenSyntax &&leftTok, ParsedTupleExprElementListSyntax &&args, - Optional &&rightTok, - Optional &&closure) { - builder.useLeftParen(std::move(leftTok)); - builder.useArguments(std::move(args)); - if (rightTok) - builder.useRightParen(std::move(*rightTok)); - if (closure) - builder.useTrailingClosure(std::move(*closure)); - }); - return makeParsedResult(builder.build(), status); -} + return makeParserError(); + } -ParserResult -Parser::parseExprObjectLiteral(bool isExprBasic) { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprObjectLiteralSyntax(isExprBasic); - assert(!parsed.isNull()); - SyntaxContext->addSyntax(parsed.get()); - - auto syntax = SyntaxContext->topNode(); - return makeParserResult(parsed.getStatus(), - Generator.generate(syntax, leadingLoc)); + // Parse the argument list. + SourceLoc lParenLoc, rParenLoc; + SmallVector args; + SmallVector argLabels; + SmallVector argLabelLocs; + Expr *trailingClosure; + + ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, + /*isPostfix=*/true, isExprBasic, + lParenLoc, args, argLabels, + argLabelLocs, + rParenLoc, + trailingClosure, + SyntaxKind::TupleExprElementList); + if (status.hasCodeCompletion()) + return makeParserCodeCompletionResult(); + if (status.isError()) + return makeParserError(); + + return makeParserResult( + ObjectLiteralExpr::create(Context, PoundLoc, LitKind, lParenLoc, args, + argLabels, argLabelLocs, rParenLoc, + trailingClosure, /*implicit=*/false)); } /// Parse and diagnose unknown pound expression @@ -3261,7 +3240,8 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic*/ true, LParenLoc, - args, argLabels, argLabelLocs, RParenLoc, trailingClosure); + args, argLabels, argLabelLocs, RParenLoc, trailingClosure, + SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (status.isError()) @@ -3314,26 +3294,6 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { return makeParserError(); } -ParsedSyntaxResult -Parser::parseExprPoundUnknownSyntax(Optional &&LSquare, - SourceLoc LSquareLoc) { - SourceLoc ExprLoc = Tok.getLoc(); - if (LSquareLoc.isValid()) - ExprLoc = LSquareLoc; - ParserStatus status; - { - SyntaxParsingContext ExprParsingContext(SyntaxContext, - SyntaxContextKind::Expr); - if (LSquare) - ExprParsingContext.addSyntax(std::move(*LSquare)); - ParserResult result = parseExprPoundUnknown(LSquareLoc); - status = result; - Generator.addExpr(result.getPtrOrNull(), ExprLoc); - } - auto parsed = SyntaxContext->popIf(); - return makeParsedResult(std::move(*parsed), status); -} - /// Handle code completion after pound in expression position. /// /// In case it's in a stmt condition position, specify \p ParentKind to @@ -3396,7 +3356,8 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure); + trailingClosure, + SyntaxKind::TupleExprElementList); // Form the call. return makeParserResult( @@ -3416,163 +3377,216 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { /// expr-dictionary: /// '[' expr ':' expr (',' expr ':' expr)* ','? ']' /// '[' ':' ']' -ParsedSyntaxResult Parser::parseExprCollectionSyntax() { - auto LSquareLoc = Tok.getLoc(); - auto LSquare = consumeTokenSyntax(tok::l_square); +ParserResult Parser::parseExprCollection() { + SyntaxParsingContext ArrayOrDictContext(SyntaxContext, + SyntaxContextKind::Expr); + SourceLoc LSquareLoc = consumeToken(tok::l_square); + SourceLoc RSquareLoc; Parser::StructureMarkerRAII ParsingCollection( - *this, LSquareLoc, StructureMarkerKind::OpenSquare); + *this, LSquareLoc, + StructureMarkerKind::OpenSquare); // [] is always an array. if (Tok.is(tok::r_square)) { - ParsedArrayExprSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquare(std::move(LSquare)); - builder.useRightSquare(consumeTokenSyntax(tok::r_square)); - return makeParsedResult(builder.build()); + if (SyntaxContext->isEnabled()) + SyntaxContext->addSyntax(ParsedSyntaxRecorder::makeBlankArrayElementList( + leadingTriviaLoc(), *SyntaxContext)); + RSquareLoc = consumeToken(tok::r_square); + ArrayOrDictContext.setCreateSyntax(SyntaxKind::ArrayExpr); + return makeParserResult( + ArrayExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc)); } // [:] is always an empty dictionary. if (Tok.is(tok::colon) && peekToken().is(tok::r_square)) { - ParsedDictionaryExprSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquare(std::move(LSquare)); - builder.useContent(consumeTokenSyntax(tok::colon)); - builder.useRightSquare(consumeTokenSyntax(tok::r_square)); - return makeParsedResult(builder.build()); + consumeToken(tok::colon); + RSquareLoc = consumeToken(tok::r_square); + ArrayOrDictContext.setCreateSyntax(SyntaxKind::DictionaryExpr); + return makeParserResult( + DictionaryExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc)); } - // '[#identifier' is likely to be a legacy object literal. + // [#identifier is likely to be a legacy object literal. if (Tok.is(tok::pound) && peekToken().is(tok::identifier) && !peekToken().isEscapedIdentifier() && LSquareLoc.getAdvancedLoc(1) == Tok.getLoc() && Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { - return parseExprPoundUnknownSyntax(std::move(LSquare), LSquareLoc); + ArrayOrDictContext.setCoerceKind(SyntaxContextKind::Expr); + return parseExprPoundUnknown(LSquareLoc); } - auto firstExpr = - parseExpressionSyntax(diag::expected_expr_in_collection_literal); + ParserStatus Status; + Optional isDictionary; + SmallVector ElementExprs; + SmallVector CommaLocs; - if (!Tok.is(tok::colon)) { - // Array. - return parseExprArraySyntax(std::move(LSquare), LSquareLoc, - std::move(firstExpr)); - } else { - // Dictionary. - return parseExprDictionarySyntax(std::move(LSquare), LSquareLoc, - std::move(firstExpr)); + { + SyntaxParsingContext ListCtx(SyntaxContext, SyntaxContextKind::Expr); + + while (true) { + SyntaxParsingContext ElementCtx(SyntaxContext); + + auto Element = parseExprCollectionElement(isDictionary); + Status |= Element; + ElementCtx.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryElement + : SyntaxKind::ArrayElement); + if (Element.isNonNull()) + ElementExprs.push_back(Element.get()); + + // Skip to ']' or ',' in case of error. + // NOTE: This checks 'Status' instead of 'Element' to silence excessive + // diagnostics. + if (Status.isError()) { + skipUntilDeclRBrace(tok::r_square, tok::comma); + if (Tok.isNot(tok::comma)) + break; + } + + // Parse the ',' if exists. + if (Tok.is(tok::comma)) { + CommaLocs.push_back(consumeToken()); + if (!Tok.is(tok::r_square)) + continue; + } + + // Close square. + if (Tok.is(tok::r_square)) + break; + + // If we found EOF or such, bailout. + if (Tok.is(tok::eof)) { + IsInputIncomplete = true; + break; + } + + // If The next token is at the beginning of a new line and can never start + // an element, break. + if (Tok.isAtStartOfLine() && (Tok.isAny(tok::r_brace, tok::pound_endif) || + isStartOfDecl() || isStartOfStmt())) + break; + + diagnose(Tok, diag::expected_separator, ",") + .fixItInsertAfter(PreviousLoc, ","); + Status.setIsParseError(); + } + + ListCtx.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryElementList + : SyntaxKind::ArrayElementList); } -} + ArrayOrDictContext.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryExpr + : SyntaxKind::ArrayExpr); -ParsedSyntaxResult -Parser::parseExprArraySyntax(ParsedTokenSyntax &&LSquare, SourceLoc LSquareLoc, - ParsedSyntaxResult &&firstExpr) { - ParserStatus status; + if (Status.isError()) { + // If we've already got errors, don't emit missing RightK diagnostics. + RSquareLoc = Tok.is(tok::r_square) ? consumeToken() + : getLocForMissingMatchingToken(); + } else if (parseMatchingToken(tok::r_square, RSquareLoc, + diag::expected_rsquare_array_expr, + LSquareLoc)) { + Status.setIsParseError(); + } + + // Don't bother to create expression if any expressions aren't parsed. + if (ElementExprs.empty()) + return Status; - ParsedArrayExprSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquare(std::move(LSquare)); - - SmallVector elements; - - // Parse elements. - // NOTE: 'AllowEmpty=false' because we already have 'firstExpr'. - bool isFirst = true; - status |= parseListSyntax( - elements, /*AllowEmpty=*/false, /*AllowSepAfterLast=*/true, - [&]() { return Tok.is(tok::r_square); }, - [&](ParsedArrayElementSyntaxBuilder &elemBuilder) { - auto expr = isFirst ? std::move(firstExpr) - : parseExpressionSyntax( - diag::expected_expr_in_collection_literal); - isFirst = false; - elemBuilder.useExpression( - !expr.isNull() - ? expr.get() - : ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - return expr.getStatus(); - }); - for (auto &elem : elements) - builder.addElementsMember(std::move(elem)); - - // Parse ']'. - auto RSquare = - parseMatchingTokenSyntax(tok::r_square, diag::expected_rsquare_array_expr, - LSquareLoc, status.isError()); - status |= RSquare.getStatus(); - if (!RSquare.isNull()) - builder.useRightSquare(RSquare.get()); - - return makeParsedResult(builder.build(), status); + Expr *expr; + if (*isDictionary) + expr = DictionaryExpr::create(Context, LSquareLoc, ElementExprs, CommaLocs, + RSquareLoc); + else + expr = ArrayExpr::create(Context, LSquareLoc, ElementExprs, CommaLocs, + RSquareLoc); + + return makeParserResult(Status, expr); } -ParsedSyntaxResult Parser::parseExprDictionarySyntax( - ParsedTokenSyntax &&LSquare, SourceLoc LSquareLoc, - ParsedSyntaxResult &&firstExpr) { - ParserStatus status = firstExpr.getStatus(); - - ParsedDictionaryExprSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquare(std::move(LSquare)); - - SmallVector elements; - - // Parse other elements. - bool isFirst = true; - status |= parseListSyntax( - elements, /*AllowEmpty=*/false, /*AllowSepAfterLast=*/true, - [&]() { return Tok.is(tok::r_square); }, - [&](ParsedDictionaryElementSyntaxBuilder &elemBuilder) { - // Parse the key expression. - auto key = isFirst ? std::move(firstExpr) - : parseExpressionSyntax( - diag::expected_key_in_dictionary_literal); - isFirst = false; - elemBuilder.useKeyExpression( - !key.isNull() - ? key.get() - : ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - - // Parse ':'. - if (Tok.is(tok::colon)) { - elemBuilder.useColon(consumeTokenSyntax(tok::colon)); - } else { - if (key.getStatus().isSuccess()) - diagnose(Tok, diag::expected_colon_in_dictionary_literal); - elemBuilder.useValueExpression( - ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - return key.getStatus() | makeParserError(); - } +/// parseExprCollectionElement - Parse an element for collection expr. +/// +/// If \p isDictionary is \c None, it's set to \c true if the element is for +/// dictionary literal, or \c false otherwise. +ParserResult +Parser::parseExprCollectionElement(Optional &isDictionary) { + auto Element = parseExpr(isDictionary.hasValue() && *isDictionary + ? diag::expected_key_in_dictionary_literal + : diag::expected_expr_in_collection_literal); + + if (!isDictionary.hasValue()) + isDictionary = Tok.is(tok::colon); + + if (!*isDictionary) { + validateCollectionElement(Element); + return Element; + } + + if (Element.isNull()) + return Element; + + // Parse the ':'. + if (!consumeIf(tok::colon)) { + if (Element.hasCodeCompletion()) { + // Return the completion expression itself so we can analyze the type + // later. + return Element; + } + diagnose(Tok, diag::expected_colon_in_dictionary_literal); + return ParserStatus(Element) | makeParserError(); + } + + // Parse the value. + auto Value = parseExpr(diag::expected_value_in_dictionary_literal); + + if (Value.isNull()) { + if (!Element.hasCodeCompletion()) { + Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc)); + } else { + Value = makeParserResult(Value, + new (Context) CodeCompletionExpr(PreviousLoc)); + } + } - // Parse the value expression. - auto value = - parseExpressionSyntax(diag::expected_value_in_dictionary_literal); - elemBuilder.useValueExpression( - !value.isNull() - ? value.get() - : ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - - return key.getStatus() | value.getStatus(); - }); - builder.useContent(ParsedSyntaxRecorder::makeDictionaryElementList( - elements, *SyntaxContext)); - - // Parse ']'. - auto RSquare = - parseMatchingTokenSyntax(tok::r_square, diag::expected_rsquare_array_expr, - LSquareLoc, status.isError()); - status |= RSquare.getStatus(); - if (!RSquare.isNull()) - builder.useRightSquare(RSquare.get()); - - return makeParsedResult(builder.build(), status); + // Make a tuple of Key Value pair. + return makeParserResult( + ParserStatus(Element) | ParserStatus(Value), + TupleExpr::createImplicit(Context, {Element.get(), Value.get()}, {})); } -ParserResult Parser::parseExprCollection() { - auto leadingLoc = leadingTriviaLoc(); - auto parsed = parseExprCollectionSyntax(); - assert(!parsed.isNull()); - SyntaxContext->addSyntax(parsed.get()); - - auto syntax = SyntaxContext->topNode(); - return makeParserResult(parsed.getStatus(), - Generator.generate(syntax, leadingLoc)); +/// validateCollectionElement - Check if a given collection element is valid. +/// +/// At the moment, this checks whether a given collection element is a subscript +/// expression and whether we're subscripting into an array. If we are, then it +/// we emit a diagnostic in case it was not something that the user was +/// expecting. +/// +/// For example: `let array [ [0, 1] [42] ]` +void Parser::validateCollectionElement(ParserResult element) { + if (element.isNull()) + return; + + auto elementExpr = element.get(); + if (!isa(elementExpr)) + return; + + auto subscriptExpr = cast(elementExpr); + if (!isa(subscriptExpr->getBase())) + return; + + auto arrayExpr = cast(subscriptExpr->getBase()); + + auto startLocOfSubscript = subscriptExpr->getIndex()->getStartLoc(); + auto endLocOfArray = arrayExpr->getEndLoc(); + auto locForEndOfTokenArray = L->getLocForEndOfToken(SourceMgr, endLocOfArray); + + if (locForEndOfTokenArray != startLocOfSubscript) { + auto subscriptLoc = subscriptExpr->getLoc(); + diagnose(subscriptLoc, diag::subscript_array_element) + .highlight(subscriptExpr->getSourceRange()); + diagnose(subscriptLoc, diag::subscript_array_element_fix_it_add_comma) + .fixItInsertAfter(endLocOfArray, ","); + diagnose(subscriptLoc, diag::subscript_array_element_fix_it_remove_space) + .fixItRemoveChars(locForEndOfTokenArray, startLocOfSubscript); + } } void Parser::addPatternVariablesToScope(ArrayRef Patterns) { diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index 750b5f7a33458..b65fc4f4eb951 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,201 +10,191 @@ // //===----------------------------------------------------------------------===// // -// Generic Parsing +// Generic Parsing and AST Building // //===----------------------------------------------------------------------===// #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/TypeRepr.h" -#include "swift/Parse/ParsedSyntaxBuilders.h" -#include "swift/Parse/ParsedSyntaxRecorder.h" +#include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/SyntaxParsingContext.h" - +#include "swift/Parse/Lexer.h" +#include "swift/Syntax/SyntaxBuilders.h" +#include "swift/Syntax/SyntaxNodes.h" using namespace swift; using namespace swift::syntax; -/// Parse a list of generic parameters. -/// -/// generic-parameter-clause-list: -/// generic-parameter-clause generic-parameter-clause* -ParserStatus Parser::parseSILGenericParamsSyntax( - Optional &result) { - assert(isInSILMode()); - ParserStatus status; - if (!startsWithLess(Tok)) - return status; - - SmallVector clauses; - do { - auto result = parseGenericParameterClauseSyntax(); - status |= result.getStatus(); - if (!result.isNull()) - clauses.push_back(result.get()); - } while (startsWithLess(Tok)); - - result = ParsedSyntaxRecorder::makeGenericParameterClauseList(clauses, - *SyntaxContext); - return status; -} - -/// Parse a sequence of generic parameters, e.g. '' -/// along with an optional requires clause. +/// parseGenericParameters - Parse a sequence of generic parameters, e.g., +/// < T : Comparable, U : Container> along with an optional requires clause. /// -/// generic-parameter-clause: -/// '<' generic-paramter (',' generic-parameter)* where-clause? '>' +/// generic-params: +/// '<' generic-param (',' generic-param)* where-clause? '>' /// -/// generic-parameter: +/// generic-param: /// identifier -/// identifier ':' type -ParsedSyntaxResult -Parser::parseGenericParameterClauseSyntax() { +/// identifier ':' type-identifier +/// identifier ':' type-composition +/// +/// When parsing the generic parameters, this routine establishes a new scope +/// and adds those parameters to the scope. +ParserResult Parser::parseGenericParameters() { + SyntaxParsingContext GPSContext(SyntaxContext, SyntaxKind::GenericParameterClause); + // Parse the opening '<'. assert(startsWithLess(Tok) && "Generic parameter list must start with '<'"); - ParsedGenericParameterClauseSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - // Parse '<'. - SourceLoc LAngleLoc = Tok.getLoc(); - builder.useLeftAngleBracket(consumeStartingLessSyntax()); + return parseGenericParameters(consumeStartingLess()); +} - // Parse parameters. - bool hasNext = true; +ParserStatus +Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, + SmallVectorImpl &GenericParams) { + ParserStatus Result; + SyntaxParsingContext GPSContext(SyntaxContext, SyntaxKind::GenericParameterList); + bool HasNextParam; do { - ParsedGenericParameterSyntaxBuilder paramBuilder(*SyntaxContext); + SyntaxParsingContext GParamContext(SyntaxContext, SyntaxKind::GenericParameter); + // Note that we're parsing a declaration. + StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(), + StructureMarkerKind::Declaration); + + if (ParsingDecl.isFailed()) { + return makeParserError(); + } // Parse attributes. - // TODO: Implement syntax attribute parsing. - Optional attrs; - if (Tok.is(tok::at_sign)) { - SyntaxParsingContext TmpCtxt(SyntaxContext); - TmpCtxt.setTransparent(); - - auto AttrsLoc = Tok.getLoc(); - DeclAttributes attrsAST; - parseDeclAttributeList(attrsAST); - if (!attrsAST.isEmpty()) - Generator.addDeclAttributes(attrsAST, AttrsLoc); - attrs = SyntaxContext->popIf(); - } + DeclAttributes attributes; + if (Tok.hasComment()) + attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange())); + parseDeclAttributeList(attributes); // Parse the name of the parameter. - auto ident = Context.getIdentifier(Tok.getText()); - auto name = parseIdentifierSyntax(diag::expected_generics_parameter_name); - if (!name) { - if (attrs) { - paramBuilder.useAttributes(std::move(*attrs)); - builder.addGenericParameterListMember(paramBuilder.build()); - } - status.setIsParseError(); + Identifier Name; + SourceLoc NameLoc; + if (parseIdentifier(Name, NameLoc, + diag::expected_generics_parameter_name)) { + Result.setIsParseError(); break; } - if (attrs) - paramBuilder.useAttributes(std::move(*attrs)); - paramBuilder.useName(std::move(*name)); - // Parse the ':' followed by a type. + SmallVector Inherited; if (Tok.is(tok::colon)) { - paramBuilder.useColon(consumeTokenSyntax(tok::colon)); + (void)consumeToken(); + ParserResult Ty; + if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol, tok::kw_Any)) { - auto tyResult = parseTypeSyntax(); - status |= tyResult.getStatus(); - if (auto ty = tyResult.getOrNull()) - paramBuilder.useInheritedType(std::move(*ty)); + Ty = parseType(); + } else if (Tok.is(tok::kw_class)) { + diagnose(Tok, diag::unexpected_class_constraint); + diagnose(Tok, diag::suggest_anyobject) + .fixItReplace(Tok.getLoc(), "AnyObject"); + consumeToken(); + Result.setIsParseError(); } else { - if (Tok.is(tok::kw_class)) { - diagnose(Tok, diag::unexpected_class_constraint); - diagnose(Tok, diag::suggest_anyobject) - .fixItReplace(Tok.getLoc(), "AnyObject"); - auto ty = ParsedSyntaxRecorder::makeClassRestrictionType( - consumeTokenSyntax(tok::kw_class), *SyntaxContext); - paramBuilder.useInheritedType(std::move(ty)); - } else { - diagnose(Tok, diag::expected_generics_type_restriction, ident); - - paramBuilder.useInheritedType( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - } - status.setIsParseError(); + diagnose(Tok, diag::expected_generics_type_restriction, Name); + Result.setIsParseError(); } + + if (Ty.hasCodeCompletion()) + return makeParserCodeCompletionStatus(); + + if (Ty.isNonNull()) + Inherited.push_back(Ty.get()); } - // Parse ',' - hasNext = Tok.is(tok::comma); - if (hasNext) - paramBuilder.useTrailingComma(consumeTokenSyntax(tok::comma)); + // We always create generic type parameters with an invalid depth. + // Semantic analysis fills in the depth when it processes the generic + // parameter list. + auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name, NameLoc, + GenericTypeParamDecl::InvalidDepth, + GenericParams.size()); + if (!Inherited.empty()) + Param->setInherited(Context.AllocateCopy(Inherited)); + GenericParams.push_back(Param); - builder.addGenericParameterListMember(paramBuilder.build()); - } while (hasNext); + // Attach attributes. + Param->getAttrs() = attributes; - // Parse optional where clause. - SourceLoc whereLoc; - if (Tok.is(tok::kw_where)) { - SmallVector requirementAST; - bool FirstTypeInComplete = false; - auto where = parseGenericWhereClauseSyntax(FirstTypeInComplete); - builder.useObsoletedWhereClause(where.get()); - } + // Add this parameter to the scope. + addToScope(Param); + + // Parse the comma, if the list continues. + HasNextParam = consumeIf(tok::comma); + } while (HasNextParam); + return Result; +} + +ParserResult +Parser::parseGenericParameters(SourceLoc LAngleLoc) { + // Parse the generic parameter list. + SmallVector GenericParams; + auto Result = parseGenericParametersBeforeWhere(LAngleLoc, GenericParams); + + // Return early if there was code completion token. + if (Result.hasCodeCompletion()) + return Result; + auto Invalid = Result.isError(); + + // Parse the optional where-clause. + SourceLoc WhereLoc; + SmallVector Requirements; + bool FirstTypeInComplete; + if (Tok.is(tok::kw_where) && + parseGenericWhereClause(WhereLoc, Requirements, + FirstTypeInComplete).isError()) { + Invalid = true; + } + // Parse the closing '>'. + SourceLoc RAngleLoc; if (startsWithGreater(Tok)) { - builder.useRightAngleBracket(consumeStartingGreaterSyntax()); + RAngleLoc = consumeStartingGreater(); } else { - if (!status.isError()) { + if (!Invalid) { diagnose(Tok, diag::expected_rangle_generics_param); diagnose(LAngleLoc, diag::opening_angle); + Invalid = true; } // Skip until we hit the '>'. - if (ignoreUntilGreaterInTypeList()) - builder.useRightAngleBracket(consumeStartingGreaterSyntax()); - status.setIsParseError(); + RAngleLoc = skipUntilGreaterInTypeList(); } - return makeParsedResult(builder.build(), status); -} - -ParserResult Parser::parseGenericParameters() { - auto loc = leadingTriviaLoc(); - auto syntaxResult = parseGenericParameterClauseSyntax(); - if (syntaxResult.isNull()) - return syntaxResult.getStatus(); - SyntaxContext->addSyntax(syntaxResult.get()); - - auto clause = SyntaxContext->topNode(); - if (clause.getGenericParameterList().empty()) + if (GenericParams.empty()) return nullptr; - return makeParserResult(syntaxResult.getStatus(), - Generator.generate(clause, loc)); + + return makeParserResult(GenericParamList::create(Context, LAngleLoc, + GenericParams, WhereLoc, + Requirements, RAngleLoc)); } ParserResult Parser::maybeParseGenericParams() { if (!startsWithLess(Tok)) return nullptr; - return parseGenericParameters(); -} -ParserResult Parser::parseSILGenericParams() { - assert(isInSILMode()); - auto loc = leadingTriviaLoc(); - Optional result; - auto status = parseSILGenericParamsSyntax(result); - if (!result.hasValue()) { - status.setIsParseError(); - return status; - } + if (!isInSILMode()) + return parseGenericParameters(); - SyntaxContext->addSyntax(std::move(*result)); - auto list = SyntaxContext->topNode(); - auto ret = Generator.generate(list, loc); - if (!ret) - return nullptr; - return makeParserResult(status, ret); + // In SIL mode, we can have multiple generic parameter lists, with the + // first one being the outmost generic parameter list. + GenericParamList *gpl = nullptr, *outer_gpl = nullptr; + do { + gpl = parseGenericParameters().getPtrOrNull(); + if (!gpl) + return nullptr; + + if (outer_gpl) + gpl->setOuterParameters(outer_gpl); + outer_gpl = gpl; + } while (startsWithLess(Tok)); + return makeParserResult(gpl); } -void Parser::diagnoseWhereClauseInGenericParamList( - const GenericParamList *GenericParams, SourceLoc whereLoc) { +void +Parser::diagnoseWhereClauseInGenericParamList(const GenericParamList * + GenericParams) { if (GenericParams == nullptr || GenericParams->getWhereLoc().isInvalid()) return; @@ -229,7 +219,7 @@ void Parser::diagnoseWhereClauseInGenericParamList( SmallString<64> Buffer; llvm::raw_svector_ostream WhereClauseText(Buffer); - WhereClauseText << SourceMgr.extractText(whereLoc.isValid() + WhereClauseText << SourceMgr.extractText(Tok.is(tok::kw_where) ? WhereCharRange : RemoveWhereRange); @@ -245,161 +235,154 @@ void Parser::diagnoseWhereClauseInGenericParamList( Diag.fixItRemoveChars(RemoveWhereRange.getStart(), RemoveWhereRange.getEnd()); - if (whereLoc.isValid()) { - Diag.fixItReplace(whereLoc, WhereClauseText.str()); + if (Tok.is(tok::kw_where)) { + Diag.fixItReplace(Tok.getLoc(), WhereClauseText.str()); } else { Diag.fixItInsert(Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc), WhereClauseText.str()); } } -void Parser::diagnoseWhereClauseInGenericParamList( - const GenericParamList *GenericParams) { - SourceLoc whereLoc; - if (Tok.is(tok::kw_where)) - whereLoc = Tok.getLoc(); - diagnoseWhereClauseInGenericParamList(GenericParams, whereLoc); -} - -/// Parse a 'where' clause, which places additional constraints on generic -/// parameters or types based on them. +/// parseGenericWhereClause - Parse a 'where' clause, which places additional +/// constraints on generic parameters or types based on them. /// /// where-clause: -/// 'where' generic-requirement (',' generic-requirement) * +/// 'where' requirement (',' requirement) * /// -/// generic-requirement: +/// requirement: /// conformance-requirement /// same-type-requirement -/// layout-requirement /// /// conformance-requirement: -/// type ':' type +/// type-identifier ':' type-identifier +/// type-identifier ':' type-composition /// /// same-type-requirement: -/// type '==' type -/// -/// layout-requirement: -/// type ':' layout-constraint -ParsedSyntaxResult -Parser::parseGenericWhereClauseSyntax(bool &FirstTypeInComplete, - bool allowLayoutConstraints) { - ParsedGenericWhereClauseSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - // Parse 'where'. - builder.useWhereKeyword(consumeTokenSyntax(tok::kw_where)); - - bool hasNext = true; +/// type-identifier '==' type +ParserStatus Parser::parseGenericWhereClause( + SourceLoc &WhereLoc, + SmallVectorImpl &Requirements, + bool &FirstTypeInComplete, + bool AllowLayoutConstraints) { + SyntaxParsingContext ClauseContext(SyntaxContext, + SyntaxKind::GenericWhereClause); + ParserStatus Status; + // Parse the 'where'. + WhereLoc = consumeToken(tok::kw_where); + FirstTypeInComplete = false; + SyntaxParsingContext ReqListContext(SyntaxContext, + SyntaxKind::GenericRequirementList); + bool HasNextReq; do { - auto firstType = parseTypeSyntax(); - status |= firstType.getStatus(); - FirstTypeInComplete = firstType.hasCodeCompletion(); - if (firstType.isNull()) - break; + SyntaxParsingContext ReqContext(SyntaxContext, + SyntaxKind::GenericRequirement); + Optional BodyContext; + BodyContext.emplace(SyntaxContext); + + // Parse the leading type. It doesn't necessarily have to be just a type + // identifier if we're dealing with a same-type constraint. + ParserResult FirstType = parseType(); + + if (FirstType.hasCodeCompletion()) { + BodyContext->setTransparent(); + Status.setHasCodeCompletion(); + FirstTypeInComplete = true; + } - ParsedGenericRequirementSyntaxBuilder elementBuilder(*SyntaxContext); + if (FirstType.isNull()) { + BodyContext->setTransparent(); + Status.setIsParseError(); + break; + } if (Tok.is(tok::colon)) { - auto colon = consumeTokenSyntax(tok::colon); - + // A conformance-requirement. + SourceLoc ColonLoc = consumeToken(); + BodyContext->setCreateSyntax(SyntaxKind::ConformanceRequirement); if (Tok.is(tok::identifier) && getLayoutConstraint(Context.getIdentifier(Tok.getText()), Context) ->isKnownLayout()) { - // Layout constraint. - ParsedLayoutRequirementSyntaxBuilder layoutReqBuilder(*SyntaxContext); - layoutReqBuilder.useLeftTypeIdentifier(firstType.get()); - layoutReqBuilder.useColon(std::move(colon)); - SourceLoc layoutLoc = Tok.getLoc(); - auto layout = parseLayoutConstraintSyntax(); - status |= layout.getStatus(); - - if (!allowLayoutConstraints && !isInSILMode()) - diagnose(layoutLoc, + // Parse a layout constraint. + Identifier LayoutName; + auto LayoutLoc = consumeIdentifier(&LayoutName); + auto LayoutInfo = parseLayoutConstraint(LayoutName); + if (!LayoutInfo->isKnownLayout()) { + // There was a bug in the layout constraint. + Status.setIsParseError(); + } + auto Layout = LayoutInfo; + // Types in SIL mode may contain layout constraints. + if (!AllowLayoutConstraints && !isInSILMode()) { + diagnose(LayoutLoc, diag::layout_constraints_only_inside_specialize_attr); - assert(!layout.isNull()); - layoutReqBuilder.useLayoutConstraint(layout.get()); - elementBuilder.useBody(layoutReqBuilder.build()); + } else { + // Add the layout requirement. + Requirements.push_back(RequirementRepr::getLayoutConstraint( + FirstType.get(), ColonLoc, + LayoutConstraintLoc(Layout, LayoutLoc))); + } } else { - // Conformance requirement. - ParsedConformanceRequirementSyntaxBuilder conformanceReqBuilder( - *SyntaxContext); - conformanceReqBuilder.useLeftTypeIdentifier(firstType.get()); - conformanceReqBuilder.useColon(std::move(colon)); - auto secondType = parseTypeSyntax(); - status |= secondType.getStatus(); - if (!secondType.isNull()) - conformanceReqBuilder.useRightTypeIdentifier(secondType.get()); - else - conformanceReqBuilder.useRightTypeIdentifier( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - elementBuilder.useBody(conformanceReqBuilder.build()); + // Parse the protocol or composition. + ParserResult Protocol = parseType(); + + if (Protocol.isNull()) { + Status.setIsParseError(); + if (Protocol.hasCodeCompletion()) + Status.setHasCodeCompletion(); + break; + } + + // Add the requirement. + Requirements.push_back(RequirementRepr::getTypeConstraint( + FirstType.get(), ColonLoc, Protocol.get())); } } else if ((Tok.isAnyOperator() && Tok.getText() == "==") || Tok.is(tok::equal)) { - // Same type requirement. - ParsedSameTypeRequirementSyntaxBuilder sametypeReqBuilder(*SyntaxContext); - sametypeReqBuilder.useLeftTypeIdentifier(firstType.get()); + BodyContext->setCreateSyntax(SyntaxKind::SameTypeRequirement); + // A same-type-requirement if (Tok.is(tok::equal)) { diagnose(Tok, diag::requires_single_equal) - .fixItReplace(SourceRange(Tok.getLoc()), "=="); - ignoreToken(); - } else { - sametypeReqBuilder.useEqualityToken(consumeTokenSyntax()); + .fixItReplace(SourceRange(Tok.getLoc()), "=="); + } + SourceLoc EqualLoc = consumeToken(); + + // Parse the second type. + ParserResult SecondType = parseType(); + if (SecondType.isNull()) { + Status.setIsParseError(); + if (SecondType.hasCodeCompletion()) + Status.setHasCodeCompletion(); + break; } - auto secondType = parseTypeSyntax(); - status |= secondType.getStatus(); - if (!secondType.isNull()) - sametypeReqBuilder.useRightTypeIdentifier(secondType.get()); - else - sametypeReqBuilder.useRightTypeIdentifier( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - elementBuilder.useBody(sametypeReqBuilder.build()); + // Add the requirement + Requirements.push_back(RequirementRepr::getSameType(FirstType.get(), + EqualLoc, + SecondType.get())); } else { + BodyContext->setTransparent(); diagnose(Tok, diag::expected_requirement_delim); - status.setIsParseError(); - - // Fallback to conformance requirement with missing right type. - ParsedConformanceRequirementSyntaxBuilder conformanceReqBuilder( - *SyntaxContext); - conformanceReqBuilder.useLeftTypeIdentifier(firstType.get()); - conformanceReqBuilder.useRightTypeIdentifier( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - elementBuilder.useBody(conformanceReqBuilder.build()); + Status.setIsParseError(); + break; } + BodyContext.reset(); + HasNextReq = consumeIf(tok::comma); + // If there's a comma, keep parsing the list. + // If there's a "&&", diagnose replace with a comma and keep parsing + if (Tok.isBinaryOperator() && Tok.getText() == "&&" && !HasNextReq) { + diagnose(Tok, diag::requires_comma) + .fixItReplace(SourceRange(Tok.getLoc()), ","); + consumeToken(); + HasNextReq = true; + } + } while (HasNextReq); - // Parse ','. - hasNext = (status.isSuccess() && Tok.is(tok::comma)); - if (hasNext) - elementBuilder.useTrailingComma(consumeTokenSyntax()); - - builder.addRequirementListMember(elementBuilder.build()); - } while (hasNext && status.isSuccess()); + if (Requirements.empty()) + WhereLoc = SourceLoc(); - return makeParsedResult(builder.build(), status); + return Status; } -ParserStatus Parser::parseGenericWhereClause( - SourceLoc &whereLoc, SmallVectorImpl &requirements, - bool &FirstTypeInComplete, bool AllowLayoutConstraints) { - auto loc = leadingTriviaLoc(); - auto syntaxResult = parseGenericWhereClauseSyntax(FirstTypeInComplete, - AllowLayoutConstraints); - if (syntaxResult.isNull()) - return syntaxResult.getStatus(); - - SyntaxContext->addSyntax(syntaxResult.get()); - auto clause = SyntaxContext->topNode(); - - whereLoc = Generator.generate(clause.getWhereKeyword(), loc); - requirements.reserve(clause.getRequirementList().size()); - for (auto elem : clause.getRequirementList()) { - if (auto req = Generator.generate(elem, loc)) - requirements.push_back(*req); - } - - return syntaxResult.getStatus(); -} /// Parse a free-standing where clause attached to a declaration, adding it to /// a generic parameter list that may (or may not) already exist. diff --git a/lib/Parse/ParseIfConfig.cpp b/lib/Parse/ParseIfConfig.cpp index 6ae5cea370c3a..aa7acdbfbadea 100644 --- a/lib/Parse/ParseIfConfig.cpp +++ b/lib/Parse/ParseIfConfig.cpp @@ -311,6 +311,16 @@ class ValidateIfConfigCondition : D.diagnose(Loc, diag::note_typo_candidate, suggestion) .fixItReplace(Arg->getSourceRange(), suggestion); } + else if (!suggestedValues.empty()) { + // The value the user gave has been replaced by something newer. + assert(suggestedValues.size() == 1 && "only support one replacement"); + auto replacement = suggestedValues.front(); + + auto Loc = Arg->getLoc(); + D.diagnose(Loc, diag::renamed_platform_condition_argument, + *ArgStr, replacement) + .fixItReplace(Arg->getSourceRange(), replacement); + } return E; } @@ -662,13 +672,20 @@ ParserResult Parser::parseIfConfig( // Parse elements SmallVector Elements; + llvm::SaveAndRestore S(InInactiveClauseEnvironment, + InInactiveClauseEnvironment || !isActive); if (isActive || !isVersionCondition) { parseElements(Elements, isActive); - } else { + } else if (SyntaxContext->isEnabled()) { + // We shouldn't skip code if we are building syntax tree. // The parser will keep running and we just discard the AST part. DiagnosticSuppression suppression(Context.Diags); SmallVector dropedElements; parseElements(dropedElements, false); + } else { + DiagnosticTransaction DT(Diags); + skipUntilConditionalBlockClose(); + DT.abort(); } Clauses.emplace_back(ClauseLoc, Condition, diff --git a/lib/Parse/ParseList.h b/lib/Parse/ParseList.h deleted file mode 100644 index 7ed23eb38c08a..0000000000000 --- a/lib/Parse/ParseList.h +++ /dev/null @@ -1,87 +0,0 @@ -//===--- ParseList.h ------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_PARSE_PARSELIST_H -#define SWIFT_PARSE_PARSELIST_H - -#include "swift/Parse/Parser.h" -#include "swift/Parse/ParserResult.h" - -namespace swift { - -/// Parse a comma-separated list of something. -template -inline ParserStatus Parser::parseListSyntax( - SmallVectorImpl &elements, bool AllowEmpty, - bool AllowSepAfterLast, llvm::function_ref isAtCloseTok, - llvm::function_ref callback) { - ParserStatus status; - - auto isAtTerminator = [&]() -> bool { - return Tok.isAny(tok::eof, tok::pound_endif, tok::pound_else, - tok::pound_elseif) || - (Tok.isAtStartOfLine() && - (Tok.is(tok::r_brace) || isStartOfDecl() || isStartOfStmt())); - }; - - bool isTerminated = AllowEmpty && isAtCloseTok(); - - while (!isTerminated) { - typename ParsedNode::Builder elemBuilder(*SyntaxContext); - - // Parse the element. - status |= callback(elemBuilder); - - if (status.isError()) { - // Recover by skipping to ',' or close bracket. - while (!Tok.is(tok::comma) && !isAtCloseTok() && !isAtTerminator()) - ignoreSingle(); - } - - // Parse ','. - auto hasComma = Tok.is(tok::comma); - if (hasComma) - elemBuilder.useTrailingComma(consumeTokenSyntax(tok::comma)); - - // Store the element to the list. - elements.emplace_back(elemBuilder.build()); - - // Check to see if there's close bracket. - isTerminated = isAtCloseTok(); - - // If we found EOF or such without seeing ',' or close bracket, terminate. - if (!isTerminated && !hasComma) { - if (isAtTerminator()) { - checkForInputIncomplete(); - isTerminated = true; - } - } - - if (isTerminated) { - // Complain about the trailing comma at the last element. - if (hasComma && !AllowSepAfterLast) - diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(PreviousLoc)); - } else { - // Complain about the missing ','. - if (!hasComma) { - diagnose(Tok, diag::expected_separator, ",") - .fixItInsertAfter(PreviousLoc, ","); - status.setIsParseError(); - } - } - } - return status; -} - -} // namespace swift -#endif diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index fa4641294819c..610daf08d2ece 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -48,6 +48,8 @@ static DefaultArgumentKind getDefaultArgKind(Expr *init) { return DefaultArgumentKind::Column; case MagicIdentifierLiteralExpr::File: return DefaultArgumentKind::File; + case MagicIdentifierLiteralExpr::FilePath: + return DefaultArgumentKind::FilePath; case MagicIdentifierLiteralExpr::Line: return DefaultArgumentKind::Line; case MagicIdentifierLiteralExpr::Function: @@ -163,6 +165,10 @@ static bool startsParameterName(Parser &parser, bool isClosure) { if (nextTok.is(tok::colon) || nextTok.canBeArgumentLabel()) return true; + if (parser.isOptionalToken(nextTok) + || parser.isImplicitlyUnwrappedOptionalToken(nextTok)) + return false; + // The identifier could be a name or it could be a type. In a closure, we // assume it's a name, because the type can be inferred. Elsewhere, we // assume it's a type. @@ -202,9 +208,10 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, diagnose(leftParenLoc, diagnostic) .highlight({leftParenLoc, rightParenLoc}); diagnose(leftParenLoc, diag::enum_element_empty_arglist_delete) - .fixItRemoveChars(leftParenLoc, rightParenLoc); + .fixItRemoveChars(leftParenLoc, + Lexer::getLocForEndOfToken(SourceMgr, rightParenLoc)); diagnose(leftParenLoc, diag::enum_element_empty_arglist_add_void) - .fixItInsert(leftParenLoc, "Void"); + .fixItInsertAfter(leftParenLoc, "Void"); } return ParserStatus(); } @@ -327,6 +334,9 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, // was invalid. Remember that. if (type.isParseError() && !type.hasCodeCompletion()) param.isInvalid = true; + } else if (paramContext != Parser::ParameterContextKind::Closure) { + diagnose(Tok, diag::expected_parameter_colon); + param.isInvalid = true; } } else { // Otherwise, we have invalid code. Check to see if this looks like a @@ -358,13 +368,28 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc, // on is most likely argument destructuring, we are going // to diagnose that after all of the parameters are parsed. if (param.Type) { - // Mark current parameter as invalid so it is possible + // Mark current parameter type as invalid so it is possible // to diagnose it as destructuring of the closure parameter list. - param.isInvalid = true; + param.isPotentiallyDestructured = true; if (!isClosure) { // Unnamed parameters must be written as "_: Type". diagnose(typeStartLoc, diag::parameter_unnamed) .fixItInsert(typeStartLoc, "_: "); + } else { + // Unnamed parameters were accidentally possibly accepted after + // SE-110 depending on the kind of declaration. We now need to + // warn about the misuse of this syntax and offer to + // fix it. + // An exception to this rule is when the type is declared with type sugar + // Reference: SR-11724 + if (isa(param.Type) + || isa(param.Type)) { + diagnose(typeStartLoc, diag::parameter_unnamed) + .fixItInsert(typeStartLoc, "_: "); + } else { + diagnose(typeStartLoc, diag::parameter_unnamed_warn) + .fixItInsert(typeStartLoc, "_: "); + } } } } else { @@ -478,12 +503,6 @@ mapParsedParameters(Parser &parser, parser.CurDeclContext); param->getAttrs() = paramInfo.Attrs; - auto setInvalid = [&]{ - if (param->isInvalid()) - return; - param->setInvalid(); - }; - bool parsingEnumElt = (paramContext == Parser::ParameterContextKind::EnumElement); // If we're not parsing an enum case, lack of a SourceLoc for both @@ -493,8 +512,14 @@ mapParsedParameters(Parser &parser, // If we diagnosed this parameter as a parse error, propagate to the decl. if (paramInfo.isInvalid) - setInvalid(); - + param->setInvalid(); + + // If we need to diagnose this parameter as a destructuring, propagate that + // to the decl. + // FIXME: This is a terrible way to catch this. + if (paramInfo.isPotentiallyDestructured) + param->setDestructured(true); + // If a type was provided, create the type for the parameter. if (auto type = paramInfo.Type) { // If 'inout' was specified, turn the type into an in-out type. @@ -524,13 +549,6 @@ mapParsedParameters(Parser &parser, // or typealias with underlying function type. param->setAutoClosure(attrs.has(TypeAttrKind::TAK_autoclosure)); } - } else if (paramContext != Parser::ParameterContextKind::Closure) { - // Non-closure parameters require a type. - if (!param->isInvalid()) - parser.diagnose(param->getLoc(), diag::missing_parameter_type); - - param->setSpecifier(ParamSpecifier::Default); - setInvalid(); } else if (paramInfo.SpecifierLoc.isValid()) { StringRef specifier; switch (paramInfo.SpecifierKind) { @@ -651,7 +669,7 @@ mapParsedParameters(Parser &parser, if (param.DefaultArg) { DefaultArgumentKind kind = getDefaultArgKind(param.DefaultArg); result->setDefaultArgumentKind(kind); - result->setDefaultValue(param.DefaultArg); + result->setDefaultExpr(param.DefaultArg, /*isTypeChecked*/ false); } else if (param.hasInheritedDefaultArg) { result->setDefaultArgumentKind(DefaultArgumentKind::Inherited); } @@ -708,7 +726,7 @@ Parser::parseSingleParameterClause(ParameterContextKind paramContext, // Parse the parameter clause. status |= parseParameterClause(leftParenLoc, params, rightParenLoc, defaultArgs, paramContext); - + // Turn the parameter clause into argument and body patterns. auto paramList = mapParsedParameters(*this, leftParenLoc, params, rightParenLoc, namePieces, paramContext); @@ -782,7 +800,7 @@ Parser::parseFunctionSignature(Identifier SimpleName, } else if (Tok.is(tok::kw_rethrows)) { throwsLoc = consumeToken(); rethrows = true; - } else if (Tok.is(tok::kw_throw)) { + } else if (Tok.isAny(tok::kw_throw, tok::kw_try)) { throwsLoc = consumeToken(); diagnose(throwsLoc, diag::throw_in_function_type) .fixItReplace(throwsLoc, "throws"); @@ -900,7 +918,8 @@ ParserResult Parser::parseTypedPattern() { /*isExprBasic=*/false, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure); + trailingClosure, + SyntaxKind::Unknown); if (status.isSuccess()) { backtrack.cancelBacktrack(); @@ -1174,17 +1193,18 @@ ParserResult Parser::parseMatchingPattern(bool isExprBasic) { // matching-pattern ::= expr // Fall back to expression parsing for ambiguous forms. Name lookup will // disambiguate. - DeferringContextRAII Deferring(*SyntaxContext); ParserResult subExpr = parseExprImpl(diag::expected_pattern, isExprBasic); ParserStatus status = subExpr; if (subExpr.isNull()) return status; - if (auto UPES = PatternCtx.popIf()) { - PatternCtx.addSyntax(UPES->getDeferredPattern()); - } else { - PatternCtx.setCreateSyntax(SyntaxKind::ExpressionPattern); + if (SyntaxContext->isEnabled()) { + if (auto UPES = PatternCtx.popIf()) { + PatternCtx.addSyntax(UPES->getDeferredPattern()); + } else { + PatternCtx.setCreateSyntax(SyntaxKind::ExpressionPattern); + } } // The most common case here is to parse something that was a lexically // obvious pattern, which will come back wrapped in an immediate diff --git a/lib/Parse/ParseRequests.cpp b/lib/Parse/ParseRequests.cpp index dbc96e6e04889..8c03940bff2f9 100644 --- a/lib/Parse/ParseRequests.cpp +++ b/lib/Parse/ParseRequests.cpp @@ -33,7 +33,17 @@ namespace swift { #undef SWIFT_TYPEID_HEADER } -ArrayRef +void swift::simple_display(llvm::raw_ostream &out, + const FingerprintAndMembers &value) { + if (value.fingerprint) + simple_display(out, value.fingerprint.getValue()); + else + out << ""; + out << ", "; + simple_display(out, value.members); +} + +FingerprintAndMembers ParseMembersRequest::evaluate(Evaluator &evaluator, IterableDeclContext *idc) const { SourceFile &sf = *idc->getDecl()->getDeclContext()->getParentSourceFile(); @@ -43,10 +53,14 @@ ParseMembersRequest::evaluate(Evaluator &evaluator, // diagnostic engine here. Parser parser(bufferID, sf, /*No Lexer Diags*/nullptr, nullptr, nullptr); // Disable libSyntax creation in the delayed parsing. - parser.SyntaxContext->setDiscard(); + parser.SyntaxContext->disable(); ASTContext &ctx = idc->getDecl()->getASTContext(); - return ctx.AllocateCopy( - llvm::makeArrayRef(parser.parseDeclListDelayed(idc))); + auto declsAndHash = parser.parseDeclListDelayed(idc); + FingerprintAndMembers fingerprintAndMembers = {declsAndHash.second, + declsAndHash.first}; + return FingerprintAndMembers{ + fingerprintAndMembers.fingerprint, + ctx.AllocateCopy(llvm::makeArrayRef(fingerprintAndMembers.members))}; } BraceStmt *ParseAbstractFunctionBodyRequest::evaluate( @@ -82,7 +96,7 @@ BraceStmt *ParseAbstractFunctionBodyRequest::evaluate( unsigned bufferID = sourceMgr.findBufferContainingLoc(afd->getLoc()); Parser parser(bufferID, sf, static_cast(nullptr), nullptr, nullptr); - parser.SyntaxContext->setDiscard(); + parser.SyntaxContext->disable(); auto body = parser.parseAbstractFunctionBodyDelayed(afd); afd->setBodyKind(BodyKind::Parsed); return body; diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index fe52ad6afc079..3805594342060 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -93,6 +93,12 @@ bool Parser::isStartOfStmt() { Parser::BacktrackingScope backtrack(*this); consumeToken(tok::identifier); consumeToken(tok::colon); + + // We treating IDENTIIFIER: { as start of statement to provide missed 'do' + // diagnostics. This case will be handled in parseStmt(). + if (Tok.is(tok::l_brace)) { + return true; + } // For better recovery, we just accept a label on any statement. We reject // putting a label on something inappropriate in parseStmt(). return isStartOfStmt(); @@ -167,10 +173,6 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) { } } - if (ResultExpr.hasCodeCompletion() && CodeCompletion) { - CodeCompletion->completeExpr(); - } - return ResultExpr; } @@ -202,6 +204,8 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, ArrayRef ParsedDecls) { switch (Kind) { case BraceItemListKind::Brace: + case BraceItemListKind::TopLevelCode: + case BraceItemListKind::TopLevelLibrary: return false; case BraceItemListKind::Case: { if (Tok.is(tok::pound_if)) { @@ -220,26 +224,6 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, } return isAtStartOfSwitchCase(*this); } - case BraceItemListKind::TopLevelCode: - // When parsing the top level executable code for a module, if we parsed - // some executable code, then we're done. We want to process (name bind, - // type check, etc) decls one at a time to make sure that there are not - // forward type references, etc. There is an outer loop around the parser - // that will reinvoke the parser at the top level on each statement until - // EOF. In contrast, it is ok to have forward references between classes, - // functions, etc. - for (auto I : ParsedDecls) { - if (isa(I.get())) - // Only bail out if the next token is at the start of a line. If we - // don't, then we may accidentally allow things like "a = 1 b = 4". - // FIXME: This is really dubious. This will reject some things, but - // allow other things we don't want. - if (Tok.isAtStartOfLine()) - return true; - } - return false; - case BraceItemListKind::TopLevelLibrary: - return false; case BraceItemListKind::ActiveConditionalBlock: case BraceItemListKind::InactiveConditionalBlock: return Tok.isNot(tok::pound_else) && Tok.isNot(tok::pound_endif) && @@ -252,12 +236,14 @@ bool Parser::isTerminatorForBraceItemListKind(BraceItemListKind Kind, void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD) { SyntaxParsingContext Discarding(SyntaxContext); - Discarding.setDiscard(); + Discarding.disable(); SourceLoc EndLoc = PreviousLoc; backtrackToPosition(BeginParserPosition); SourceLoc BeginLoc = Tok.getLoc(); - State->delayTopLevel(TLCD, {BeginLoc, EndLoc}, - BeginParserPosition.PreviousLoc); + State->setCodeCompletionDelayedDeclState( + SourceMgr, L->getBufferID(), + CodeCompletionDelayedDeclKind::TopLevelCodeDecl, + PD_Default, TLCD, {BeginLoc, EndLoc}, BeginParserPosition.PreviousLoc); // Skip the rest of the file to prevent the parser from constructing the AST // for it. Forward references are not allowed at the top level. @@ -287,7 +273,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition, /// expr '=' expr ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, BraceItemListKind Kind, - BraceItemListKind ConditionalBlockKind) { + BraceItemListKind ConditionalBlockKind, + bool &IsFollowingGuard) { bool isRootCtx = SyntaxContext->isRoot(); SyntaxParsingContext ItemListContext(SyntaxContext, SyntaxKind::CodeBlockItemList); @@ -380,7 +367,8 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, [&](SmallVectorImpl &Elements, bool IsActive) { parseBraceItems(Elements, Kind, IsActive ? BraceItemListKind::ActiveConditionalBlock - : BraceItemListKind::InactiveConditionalBlock); + : BraceItemListKind::InactiveConditionalBlock, + IsFollowingGuard); }); if (IfConfigResult.hasCodeCompletion() && isCodeCompletionFirstPass()) { consumeDecl(BeginParserPosition, None, IsTopLevel); @@ -417,7 +405,18 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, ParserResult DeclResult = parseDecl(IsTopLevel ? PD_AllowTopLevel : PD_Default, IsAtStartOfLineOrPreviousHadSemi, - [&](Decl *D) {TmpDecls.push_back(D);}); + [&](Decl *D) { + TmpDecls.push_back(D); + + // Any function after a 'guard' statement is marked as + // possibly having local captures. This allows SILGen + // to correctly determine its capture list, since + // otherwise it would be skipped because it is not + // defined inside a local context. + if (IsFollowingGuard) + if (auto *FD = dyn_cast(D)) + FD->setHasTopLevelLocalContextCaptures(); + }); BraceItemsStatus |= DeclResult; if (DeclResult.isParseError()) { NeedParseErrorRecovery = true; @@ -467,6 +466,14 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, Result, Result.getEndLoc()); TLCD->setBody(Brace); Entries.push_back(TLCD); + + // A top-level 'guard' statement can introduce local bindings, so we + // must mark all functions following one. This makes them behave + // as if they were in local context for the purposes of capture + // emission in SILGen. + if (auto *stmt = Result.dyn_cast()) + if (isa(stmt)) + IsFollowingGuard = true; } } else if (Tok.is(tok::kw_init) && isa(CurDeclContext)) { SourceLoc StartLoc = Tok.getLoc(); @@ -519,53 +526,6 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, return BraceItemsStatus; } -void Parser::parseTopLevelCodeDeclDelayed() { - auto DelayedState = State->takeDelayedDeclState(); - assert(DelayedState.get() && "should have delayed state"); - - auto BeginParserPosition = getParserPosition(DelayedState->BodyPos); - auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd); - - // ParserPositionRAII needs a primed parser to restore to. - if (Tok.is(tok::NUM_TOKENS)) - consumeTokenWithoutFeedingReceiver(); - - // Ensure that we restore the parser state at exit. - ParserPositionRAII PPR(*this); - - // Create a lexer that cannot go past the end state. - Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState); - - // Temporarily swap out the parser's current lexer with our new one. - llvm::SaveAndRestore T(L, &LocalLex); - - // Rewind to the beginning of the top-level code. - restoreParserPosition(BeginParserPosition); - - // Re-enter the lexical scope. - Scope S(this, DelayedState->takeScope()); - - // Re-enter the top-level decl context. - // FIXME: this can issue discriminators out-of-order? - auto *TLCD = cast(DelayedState->ParentContext); - ContextChange CC(*this, TLCD, &State->getTopLevelContext()); - - SourceLoc StartLoc = Tok.getLoc(); - ASTNode Result; - - // Expressions can't begin with a closure literal at statement position. This - // prevents potential ambiguities with trailing closure syntax. - if (Tok.is(tok::l_brace)) { - diagnose(Tok, diag::statement_begins_with_closure); - } - - parseExprOrStmt(Result); - if (!Result.isNull()) { - auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc()); - TLCD->setBody(Brace); - } -} - /// Recover from a 'case' or 'default' outside of a 'switch' by consuming up to /// the next ':'. static ParserResult recoverFromInvalidCase(Parser &P) { @@ -683,6 +643,12 @@ ParserResult Parser::parseStmt() { if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt); if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText()); return parseStmtPoundAssert(); + case tok::l_brace: + if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText()); + SourceLoc colonLoc = Tok.getLoc(); + diagnose(colonLoc, diag::labeled_block_needs_do) + .fixItInsert(colonLoc, "do "); + return parseStmtDo(LabelInfo, /*shouldSkipDoTokenConsume*/ true); } } @@ -879,16 +845,11 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { lpLoc, yields, yieldLabels, yieldLabelLocs, rpLoc, - trailingClosure); + trailingClosure, + SyntaxKind::ExprList); assert(trailingClosure == nullptr); - if (!yieldLabelLocs.empty()) { - for (auto labelLoc : yieldLabelLocs) { - if (labelLoc.isValid()) { - diagnose(labelLoc, diag::unexpected_arg_label_yield); - break; - } - } - } + assert(yieldLabels.empty()); + assert(yieldLabelLocs.empty()); } else { SourceLoc beginLoc = Tok.getLoc(); @@ -1926,9 +1887,16 @@ ParserResult Parser::parseStmtRepeat(LabeledStmtInfo labelInfo) { /// stmt-do: /// (identifier ':')? 'do' stmt-brace /// (identifier ':')? 'do' stmt-brace stmt-catch+ -ParserResult Parser::parseStmtDo(LabeledStmtInfo labelInfo) { +ParserResult Parser::parseStmtDo(LabeledStmtInfo labelInfo, + bool shouldSkipDoTokenConsume) { SyntaxContext->setCreateSyntax(SyntaxKind::DoStmt); - SourceLoc doLoc = consumeToken(tok::kw_do); + SourceLoc doLoc; + + if (shouldSkipDoTokenConsume) { + doLoc = Tok.getLoc(); + } else { + doLoc = consumeToken(tok::kw_do); + } ParserStatus status; @@ -1968,18 +1936,21 @@ ParserResult Parser::parseStmtDo(LabeledStmtInfo labelInfo) { DoCatchStmt::create(Context, labelInfo, doLoc, body.get(), allClauses)); } - SourceLoc whileLoc; - - // If we don't see a 'while', this is just the bare 'do' scoping - // statement. - if (!consumeIf(tok::kw_while, whileLoc)) { + // If we dont see a 'while' or see a 'while' that starts + // from new line. This is just the bare `do` scoping statement. + if (Tok.getKind() != tok::kw_while || Tok.isAtStartOfLine()) { return makeParserResult(status, - new (Context) DoStmt(labelInfo, doLoc, body.get())); + new (Context) DoStmt(labelInfo, doLoc, body.get())); } - + SourceLoc whileLoc = Tok.getLoc(); // But if we do, advise the programmer that it's 'repeat' now. - diagnose(doLoc, diag::do_while_now_repeat_while) + diagnose(doLoc, diag::do_while_now_repeat_while); + diagnose(doLoc, diag::do_while_expected_repeat_while) .fixItReplace(doLoc, "repeat"); + diagnose(doLoc, diag::do_while_expected_separate_stmt) + .fixItInsert(whileLoc, "\n"); + + consumeToken(tok::kw_while); status.setIsParseError(); ParserResult condition; if (Tok.is(tok::l_brace)) { diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 4a860bfeba4a0..43eb3d157d2cb 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -14,7 +14,6 @@ // //===----------------------------------------------------------------------===// -#include "ParseList.h" #include "swift/Parse/Parser.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" @@ -63,84 +62,81 @@ TypeRepr *Parser::applyAttributeToType(TypeRepr *ty, return ty; } -/// Apply specifier and attributes to the parsed type. -ParsedSyntaxResult -Parser::applyAttributeToTypeSyntax(ParsedSyntaxResult &&ty, - Optional specifier, - Optional attrs) { - if (!attrs && !specifier) - return std::move(ty); - - if (ty.isNull()) { - SmallVector junk; - if (specifier) - junk.emplace_back(std::move(*specifier)); - if (attrs) - junk.emplace_back(std::move(*attrs)); - return makeParsedResult( - ParsedSyntaxRecorder::makeUnknownType(junk, *SyntaxContext), - ty.getStatus()); - } - - return makeParsedResult( - ParsedSyntaxRecorder::makeAttributedType( - std::move(specifier), std::move(attrs), ty.get(), *SyntaxContext), - ty.getStatus()); -} - -/// Parse layout constraint for 'where' clause in '@_specialize' attribute -/// and in SIL. -/// -/// layout-constraint: -/// identifier -/// identifier '(' integer-literal ')' -/// identifier '(' integer-literal ',' integer-literal ')' -ParsedSyntaxResult -Parser::parseLayoutConstraintSyntax() { - assert(Tok.is(tok::identifier)); - ParsedLayoutConstraintSyntaxBuilder builder(*SyntaxContext); +LayoutConstraint Parser::parseLayoutConstraint(Identifier LayoutConstraintID) { + LayoutConstraint layoutConstraint = + getLayoutConstraint(LayoutConstraintID, Context); + assert(layoutConstraint->isKnownLayout() && + "Expected layout constraint definition"); - builder.useName(consumeTokenSyntax(tok::identifier)); + if (!layoutConstraint->isTrivial()) + return layoutConstraint; - if (!Tok.isFollowingLParen()) - return makeParsedResult(builder.build()); - - auto lParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - auto parseTrivialConstraintBody = [&]() -> bool { - int value; - - if (!Tok.is(tok::integer_literal) || - Tok.getText().getAsInteger(10, value) || value < 0) { - diagnose(Tok, diag::layout_size_should_be_positive); - return true; - } - builder.useSize(consumeTokenSyntax(tok::integer_literal)); + SourceLoc LParenLoc; + if (!consumeIf(tok::l_paren, LParenLoc)) { + // It is a trivial without any size constraints. + return LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Trivial, + Context); + } - if (Tok.is(tok::comma)) { - builder.useComma(consumeTokenSyntax(tok::comma)); + int size = 0; + int alignment = 0; - if (!Tok.is(tok::integer_literal) || - Tok.getText().getAsInteger(10, value) || value < 0) { - diagnose(Tok, diag::layout_alignment_should_be_positive); + auto ParseTrivialLayoutConstraintBody = [&] () -> bool { + // Parse the size and alignment. + if (Tok.is(tok::integer_literal)) { + if (Tok.getText().getAsInteger(10, size)) { + diagnose(Tok.getLoc(), diag::layout_size_should_be_positive); return true; } - builder.useAlignment(consumeTokenSyntax(tok::integer_literal)); + consumeToken(); + if (consumeIf(tok::comma)) { + // parse alignment. + if (Tok.is(tok::integer_literal)) { + if (Tok.getText().getAsInteger(10, alignment)) { + diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive); + return true; + } + consumeToken(); + } else { + diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive); + return true; + } + } + } else { + diagnose(Tok.getLoc(), diag::layout_size_should_be_positive); + return true; } return false; }; - auto hasError = parseTrivialConstraintBody(); - if (hasError) - ignoreUntil(tok::r_paren); - auto rParen = parseMatchingTokenSyntax( - tok::r_paren, diag::expected_rparen_layout_constraint, lParenLoc, - /*silenceDiag=*/hasError); - if (!rParen.isNull()) - builder.useRightParen(rParen.get()); + if (ParseTrivialLayoutConstraintBody()) { + // There was an error during parsing. + skipUntil(tok::r_paren); + consumeIf(tok::r_paren); + return LayoutConstraint::getUnknownLayout(); + } + + if (!consumeIf(tok::r_paren)) { + // Expected a closing r_paren. + diagnose(Tok.getLoc(), diag::expected_rparen_layout_constraint); + consumeToken(); + return LayoutConstraint::getUnknownLayout(); + } + + if (size < 0) { + diagnose(Tok.getLoc(), diag::layout_size_should_be_positive); + return LayoutConstraint::getUnknownLayout(); + } + + if (alignment < 0) { + diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive); + return LayoutConstraint::getUnknownLayout(); + } - return makeParsedResult(builder.build()); + // Otherwise it is a trivial layout constraint with + // provided size and alignment. + return LayoutConstraint::getLayoutConstraint(layoutConstraint->getKind(), size, + alignment, Context); } /// parseTypeSimple @@ -155,42 +151,47 @@ Parser::parseLayoutConstraintSyntax() { /// type-simple '!' /// type-collection /// type-array -ParsedSyntaxResult -Parser::parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion) { +ParserResult Parser::parseTypeSimple(Diag<> MessageID, + bool HandleCodeCompletion) { + ParserResult ty; + if (Tok.is(tok::kw_inout) || (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || Tok.getRawText().equals("__owned")))) { // Type specifier should already be parsed before here. This only happens // for construct like 'P1 & inout P2'. diagnose(Tok.getLoc(), diag::attr_only_on_parameters, Tok.getRawText()); - ignoreToken(); + consumeToken(); } - auto TypeLoc = leadingTriviaLoc(); - - ParsedSyntaxResult Result; switch (Tok.getKind()) { case tok::kw_Self: case tok::kw_Any: - case tok::identifier: - Result = parseTypeIdentifier(); + case tok::identifier: { + ty = parseTypeIdentifier(); break; + } case tok::l_paren: - Result = parseTypeTupleBody(); + ty = parseTypeTupleBody(); break; - case tok::code_complete: { + case tok::code_complete: if (!HandleCodeCompletion) break; - ParsedTypeSyntax ty = ParsedSyntaxRecorder::makeCodeCompletionType( - None, None, consumeTokenSyntax(), *SyntaxContext); - return makeParsedCodeCompletion(std::move(ty)); - } - case tok::l_square: - Result = parseTypeCollection(); + if (CodeCompletion) + CodeCompletion->completeTypeSimpleBeginning(); + // Eat the code completion token because we handled it. + consumeToken(tok::code_complete); + return makeParserCodeCompletionResult(); + case tok::l_square: { + auto Result = parseTypeCollection(); + if (Result.hasSyntax()) + SyntaxContext->addSyntax(Result.getSyntax()); + ty = Result.getASTResult(); break; + } case tok::kw_protocol: if (startsWithLess(peekToken())) { - Result = parseOldStyleProtocolComposition(); + ty = parseOldStyleProtocolComposition(); break; } LLVM_FALLTHROUGH; @@ -202,287 +203,349 @@ Parser::parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion) { tok::equal, tok::comma, tok::semi)) diag.fixItInsert(getEndOfPreviousLoc(), " <#type#>"); } - if (Tok.isKeyword() && !Tok.isAtStartOfLine()) { - auto token = consumeTokenSyntax(); - ParsedTypeSyntax ty = ParsedSyntaxRecorder::makeUnknownType( - {&token, 1}, *SyntaxContext); - // Return success result because we recovered. - return makeParsedResult(std::move(ty)); + ty = makeParserErrorResult(new (Context) ErrorTypeRepr(Tok.getLoc())); + consumeToken(); + return ty; } - checkForInputIncomplete(); - return makeParsedError(); + return nullptr; } + auto makeMetatypeTypeSyntax = [&]() { + if (!SyntaxContext->isEnabled()) + return; + ParsedMetatypeTypeSyntaxBuilder Builder(*SyntaxContext); + auto TypeOrProtocol = SyntaxContext->popToken(); + auto Period = SyntaxContext->popToken(); + auto BaseType(std::move(*SyntaxContext->popIf())); + Builder + .useTypeOrProtocol(std::move(TypeOrProtocol)) + .usePeriod(std::move(Period)) + .useBaseType(std::move(BaseType)); + SyntaxContext->addSyntax(Builder.build()); + }; + // '.Type', '.Protocol', '?', '!', and '[]' still leave us with type-simple. - while (Result.isSuccess()) { - if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && - (peekToken().isContextualKeyword("Type") || - peekToken().isContextualKeyword("Protocol"))) { - Result = parseMetatypeType(Result.get()); - continue; + while (ty.isNonNull()) { + if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) { + if (peekToken().isContextualKeyword("Type")) { + consumeToken(); + SourceLoc metatypeLoc = consumeToken(tok::identifier); + ty = makeParserResult(ty, + new (Context) MetatypeTypeRepr(ty.get(), metatypeLoc)); + makeMetatypeTypeSyntax(); + continue; + } + if (peekToken().isContextualKeyword("Protocol")) { + consumeToken(); + SourceLoc protocolLoc = consumeToken(tok::identifier); + ty = makeParserResult(ty, + new (Context) ProtocolTypeRepr(ty.get(), protocolLoc)); + makeMetatypeTypeSyntax(); + continue; + } } if (!Tok.isAtStartOfLine()) { if (isOptionalToken(Tok)) { - Result = parseOptionalType(Result.get()); + auto Result = parseTypeOptional(ty.get()); + if (Result.hasSyntax()) + SyntaxContext->addSyntax(Result.getSyntax()); + ty = Result.getASTResult(); continue; } if (isImplicitlyUnwrappedOptionalToken(Tok)) { - Result = parseImplicitlyUnwrappedOptionalType(Result.get()); + auto Result = parseTypeImplicitlyUnwrappedOptional(ty.get()); + if (Result.hasSyntax()) + SyntaxContext->addSyntax(Result.getSyntax()); + ty = Result.getASTResult(); continue; } // Parse legacy array types for migration. if (Tok.is(tok::l_square)) { - Result = parseTypeArray(Result.get(), TypeLoc); + ty = parseTypeArray(ty.get()); continue; } } break; } - return Result; + return ty; } -ParsedSyntaxResult Parser::parseSILBoxTypeSyntax( - Optional generics) { - ParsedSILBoxTypeSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - if (generics) - builder.useGenericParameterClauses(std::move(*generics)); - - // Parse '{'. - builder.useLeftBrace(consumeTokenSyntax(tok::l_brace)); +ParserResult Parser::parseType() { + return parseType(diag::expected_type); +} - // Parse comma separated field list. +ParserResult Parser::parseSILBoxType(GenericParamList *generics, + const TypeAttributes &attrs, + Optional &GenericsScope) { + auto LBraceLoc = consumeToken(tok::l_brace); + + SmallVector Fields; if (!Tok.is(tok::r_brace)) { - bool hasNext = true; - do { - ParsedSILBoxTypeFieldSyntaxBuilder fieldBuilder(*SyntaxContext); - - // Parse 'let' or 'var'. - if (!Tok.isAny(tok::kw_var, tok::kw_let)) { + for (;;) { + bool Mutable; + if (Tok.is(tok::kw_var)) { + Mutable = true; + } else if (Tok.is(tok::kw_let)) { + Mutable = false; + } else { diagnose(Tok, diag::sil_box_expected_var_or_let); - break; + return makeParserError(); } - fieldBuilder.useSpecifier(consumeTokenSyntax()); - - // Parse the type. - auto ty = parseTypeSyntax(); - status |= ty.getStatus(); - if (!ty.isNull()) - fieldBuilder.useType(ty.get()); - else - fieldBuilder.useType( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - - // Parse ','. - hasNext = (status.isSuccess() && Tok.is(tok::comma)); - if (hasNext) - fieldBuilder.useTrailingComma(consumeTokenSyntax()); - - builder.addFieldsMember(fieldBuilder.build()); - } while (hasNext); + SourceLoc VarOrLetLoc = consumeToken(); + + auto fieldTy = parseType(); + if (!fieldTy.getPtrOrNull()) + return makeParserError(); + Fields.push_back({VarOrLetLoc, Mutable, fieldTy.get()}); + + if (consumeIf(tok::comma)) + continue; + + break; + } } - - // Parse '}'. + if (!Tok.is(tok::r_brace)) { diagnose(Tok, diag::sil_box_expected_r_brace); - return makeParsedError(builder.build()); + return makeParserError(); } - builder.useRightBrace(consumeTokenSyntax(tok::r_brace)); - - // Parse the generic argument. - if (startsWithLess(Tok)) { - auto genericArgs = parseGenericArgumentClauseSyntax(); - status |= genericArgs.getStatus(); - if (!genericArgs.isNull()) - builder.useGenericArgumentClause(genericArgs.get()); + + auto RBraceLoc = consumeToken(tok::r_brace); + + // The generic arguments are taken from the enclosing scope. Pop the + // box layout's scope now. + GenericsScope.reset(); + + SourceLoc LAngleLoc, RAngleLoc; + SmallVector Args; + if (Tok.isContextualPunctuator("<")) { + LAngleLoc = consumeToken(); + for (;;) { + auto argTy = parseType(); + if (!argTy.getPtrOrNull()) + return makeParserError(); + Args.push_back(argTy.get()); + if (consumeIf(tok::comma)) + continue; + break; + } + if (!Tok.isContextualPunctuator(">")) { + diagnose(Tok, diag::sil_box_expected_r_angle); + return makeParserError(); + } + + RAngleLoc = consumeToken(); } - - return makeParsedResult(builder.build()); + + auto repr = SILBoxTypeRepr::create(Context, generics, + LBraceLoc, Fields, RBraceLoc, + LAngleLoc, Args, RAngleLoc); + return makeParserResult(applyAttributeToType(repr, attrs, + ParamDecl::Specifier::Owned, + SourceLoc())); } + /// parseType /// type: /// attribute-list type-composition /// attribute-list type-function -/// attribute-list sil-generic-function-type -/// sil-box-type /// /// type-function: -/// '(' tuple-type-element-list ')' 'throws'? '->' type +/// type-composition 'throws'? '->' type /// -/// sil-generic-function-type: -/// generic-parameter-clause-list type-function -ParsedSyntaxResult -Parser::parseTypeSyntax(Diag<> MessageID, bool HandleCodeCompletion, - bool IsSILFuncDecl) { - ParserStatus status; - +ParserResult Parser::parseType(Diag<> MessageID, + bool HandleCodeCompletion, + bool IsSILFuncDecl) { + // Start a context for creating type syntax. + SyntaxParsingContext TypeParsingContext(SyntaxContext, + SyntaxContextKind::Type); // Parse attributes. - Optional specifier; - Optional attrs; - if (Tok.isAny(tok::at_sign, tok::kw_inout) || - (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || - Tok.getRawText().equals("__owned")))) - status |= parseTypeAttributeListSyntax(specifier, attrs); + ParamDecl::Specifier specifier; + SourceLoc specifierLoc; + TypeAttributes attrs; + parseTypeAttributeList(specifier, specifierLoc, attrs); - // Parse generic parameters in SIL mode. - Optional genericParams; - SourceLoc genericParamsLoc = Tok.getLoc(); - if (isInSILMode()) - (void)parseSILGenericParamsSyntax(genericParams); + Optional GenericsScope; + // Parse generic parameters in SIL mode. + GenericParamList *generics = nullptr; + bool isImplied = false; + if (isInSILMode()) { + // If this is part of a sil function decl, generic parameters are visible in + // the function body; otherwise, they are visible when parsing the type. + if (!IsSILFuncDecl) + GenericsScope.emplace(this, ScopeKind::Generics); + generics = maybeParseGenericParams().getPtrOrNull(); + + isImplied = consumeIf(tok::kw_in); + } + // In SIL mode, parse box types { ... }. if (isInSILMode() && Tok.is(tok::l_brace)) { - auto ty = parseSILBoxTypeSyntax(std::move(genericParams)); - return applyAttributeToTypeSyntax(std::move(ty), std::move(specifier), - std::move(attrs)); + return parseSILBoxType(generics, attrs, GenericsScope); } - auto startLoc = Tok.getLoc(); - // Parse the type. - auto ty = parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion); - status |= ty.getStatus(); - auto endLoc = PreviousLoc; + ParserResult ty = + parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion); + if (ty.hasCodeCompletion()) + return makeParserCodeCompletionResult(); + if (ty.isNull()) + return nullptr; + auto tyR = ty.get(); + // Parse a throws specifier. // Don't consume 'throws', if the next token is not '->', so we can emit a // more useful diagnostic when parsing a function decl. - bool canBeFunctionTy = - Tok.is(tok::arrow) || - (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) && - peekToken().is(tok::arrow)); - - // If the first type has an error, or this is not a function type, return the - // first result. - if (ty.isNull() || !canBeFunctionTy) { - // Diagnose generic parameter for non-function types. - if (!genericParams && !specifier && !attrs) - return ty; - - if (genericParams) { - SmallVector junk; - diagnose(genericParamsLoc, diag::generic_non_function); - junk.push_back(std::move(*genericParams)); - if (!ty.isNull()) - junk.emplace_back(ty.get()); - ty = makeParsedResult( - ParsedSyntaxRecorder::makeUnknownType(junk, *SyntaxContext), - status); - } - - return applyAttributeToTypeSyntax(std::move(ty), std::move(specifier), - std::move(attrs)); - } - - // Parse a throws specifier. - Optional throws; - if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) && + SourceLoc throwsLoc; + if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw, tok::kw_try) && peekToken().is(tok::arrow)) { if (Tok.isNot(tok::kw_throws)) { // 'rethrows' is only allowed on function declarations for now. - // 'throw' is probably a typo for 'throws'. - Diag<> DiagID = Tok.is(tok::kw_rethrows) ? diag::rethrowing_function_type - : diag::throw_in_function_type; - diagnose(Tok.getLoc(), DiagID).fixItReplace(Tok.getLoc(), "throws"); - ignoreToken(); - } else { - throws = consumeTokenSyntax(); + // 'throw' or 'try' are probably typos for 'throws'. + Diag<> DiagID = Tok.is(tok::kw_rethrows) ? + diag::rethrowing_function_type : diag::throw_in_function_type; + diagnose(Tok.getLoc(), DiagID) + .fixItReplace(Tok.getLoc(), "throws"); } + throwsLoc = consumeToken(); } - auto arrowLoc = Tok.getLoc(); - auto arrow = consumeTokenSyntax(tok::arrow); - if (Tok.is(tok::kw_throws)) { - Diag<> DiagID = diag::throws_in_wrong_position; - diagnose(Tok.getLoc(), DiagID) - .fixItInsert(arrowLoc, "throws ") - .fixItRemove(Tok.getLoc()); - ignoreToken(); - } + if (Tok.is(tok::arrow)) { + // Handle type-function if we have an arrow. + SourceLoc arrowLoc = consumeToken(); + if (Tok.is(tok::kw_throws)) { + Diag<> DiagID = diag::throws_in_wrong_position; + diagnose(Tok.getLoc(), DiagID) + .fixItInsert(arrowLoc, "throws ") + .fixItRemove(Tok.getLoc()); + throwsLoc = consumeToken(); + } + ParserResult SecondHalf = + parseType(diag::expected_type_function_result); + if (SecondHalf.hasCodeCompletion()) + return makeParserCodeCompletionResult(); + if (SecondHalf.isNull()) + return nullptr; + + if (SyntaxContext->isEnabled()) { + ParsedFunctionTypeSyntaxBuilder Builder(*SyntaxContext); + Builder.useReturnType(std::move(*SyntaxContext->popIf())); + Builder.useArrow(SyntaxContext->popToken()); + if (throwsLoc.isValid()) + Builder.useThrowsOrRethrowsKeyword(SyntaxContext->popToken()); + + auto InputNode(std::move(*SyntaxContext->popIf())); + if (auto TupleTypeNode = InputNode.getAs()) { + // Decompose TupleTypeSyntax and repack into FunctionType. + auto LeftParen = TupleTypeNode->getDeferredLeftParen(); + auto Arguments = TupleTypeNode->getDeferredElements(); + auto RightParen = TupleTypeNode->getDeferredRightParen(); + Builder + .useLeftParen(std::move(LeftParen)) + .useArguments(std::move(Arguments)) + .useRightParen(std::move(RightParen)); + } else { + Builder.addArgumentsMember(ParsedSyntaxRecorder::makeTupleTypeElement( + std::move(InputNode), /*TrailingComma=*/None, *SyntaxContext)); + } + SyntaxContext->addSyntax(Builder.build()); + } - auto input = ty.get(); - auto result = parseTypeSyntax(diag::expected_type_function_result); - status |= result.getStatus(); + TupleTypeRepr *argsTyR = nullptr; + if (auto *TTArgs = dyn_cast(tyR)) { + argsTyR = TTArgs; + } else { + bool isVoid = false; + if (const auto Void = dyn_cast(tyR)) { + if (Void->getNameRef().isSimpleName(Context.Id_Void)) { + isVoid = true; + } + } - ParsedFunctionTypeSyntaxBuilder builder(*SyntaxContext); - if (auto tuple = input.getAs()) { - assert(tuple->getRaw().isDeferredLayout()); - builder.useLeftParen(tuple->getDeferredLeftParen()); - builder.useArguments(tuple->getDeferredElements()); - builder.useRightParen(tuple->getDeferredRightParen()); - } else { - builder.addArgumentsMember(ParsedSyntaxRecorder::makeTupleTypeElement( - std::move(input), /*TrailingComma=*/None, *SyntaxContext)); - - // Diagnose only if the result type is successfully parsed, to reduce the - // noisy diagnostics. - if (result.isSuccess()) { - auto charRange = Lexer::getCharSourceRangeFromSourceRange( - SourceMgr, {startLoc, endLoc}); - auto diag = diagnose(startLoc, diag::function_type_no_parens); - if (SourceMgr.extractText(charRange) == "Void") { - diag.fixItReplace(startLoc, "()"); + if (isVoid) { + diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .fixItReplace(tyR->getStartLoc(), "()"); + argsTyR = TupleTypeRepr::createEmpty(Context, tyR->getSourceRange()); } else { - diag.highlight(SourceRange(startLoc, endLoc)); - diag.fixItInsert(startLoc, "("); - diag.fixItInsertAfter(endLoc, ")"); + diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + .highlight(tyR->getSourceRange()) + .fixItInsert(tyR->getStartLoc(), "(") + .fixItInsertAfter(tyR->getEndLoc(), ")"); + argsTyR = TupleTypeRepr::create(Context, {tyR}, + tyR->getSourceRange()); } } - } + + // Parse substitutions for substituted SIL types. + SourceLoc SubsLAngleLoc, SubsRAngleLoc; + MutableArrayRef SubsTypes; + if (isInSILMode() && consumeIf(tok::kw_for)) { + if (!Tok.isContextualPunctuator("<")) { + diagnose(Tok, diag::sil_function_subst_expected_l_angle); + return makeParserError(); + } + + SubsLAngleLoc = consumeToken(); - if (throws) - builder.useThrowsOrRethrowsKeyword(std::move(*throws)); - builder.useArrow(std::move(arrow)); - if (!result.isNull()) - builder.useReturnType(result.get()); - else - builder.useReturnType( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); - - ParsedFunctionTypeSyntax funcTy = builder.build(); - - // Apply generic parameters if exists in SIL mode. - if (genericParams) { - auto silTy = ParsedSyntaxRecorder::makeSILFunctionType( - std::move(*genericParams), std::move(funcTy), *SyntaxContext); - return applyAttributeToTypeSyntax( - makeParsedResult(std::move(silTy), status), std::move(specifier), - std::move(attrs)); - } + SmallVector SubsTypesVec; + for (;;) { + auto argTy = parseType(); + if (!argTy.getPtrOrNull()) + return makeParserError(); + SubsTypesVec.push_back(argTy.get()); + if (consumeIf(tok::comma)) + continue; + break; + } + if (!Tok.isContextualPunctuator(">")) { + diagnose(Tok, diag::sil_function_subst_expected_r_angle); + return makeParserError(); + } + + SubsRAngleLoc = consumeToken(); - return applyAttributeToTypeSyntax(makeParsedResult(std::move(funcTy), status), - std::move(specifier), std::move(attrs)); -} + SubsTypes = Context.AllocateCopy(SubsTypesVec); + } -ParsedSyntaxResult Parser::parseTypeSyntax() { - return parseTypeSyntax(diag::expected_type); -} + tyR = new (Context) FunctionTypeRepr(generics, argsTyR, throwsLoc, arrowLoc, + SecondHalf.get(), + isImplied, + SubsTypes); + } else if (generics) { + // Only function types may be generic. + auto brackets = generics->getSourceRange(); + diagnose(brackets.Start, diag::generic_non_function); + GenericsScope.reset(); + + // Forget any generic parameters we saw in the type. + class EraseTypeParamWalker : public ASTWalker { + public: + bool walkToTypeReprPre(TypeRepr *T) override { + if (auto ident = dyn_cast(T)) { + if (auto decl = ident->getBoundDecl()) { + if (auto genericParam = dyn_cast(decl)) + ident->overwriteNameRef(genericParam->createNameRef()); + } + } + return true; + } -ParserResult Parser::parseType(Diag<> MessageID, - bool HandleCodeCompletion, - bool IsSILFuncDecl) { - auto leadingLoc = leadingTriviaLoc(); - auto result = parseTypeSyntax(MessageID, HandleCodeCompletion, IsSILFuncDecl); - auto status = result.getStatus(); - if (result.isNull()) - return status; - - SyntaxContext->addSyntax(result.get()); - auto syntax = SyntaxContext->topNode(); - auto tyR = Generator.generate(syntax, leadingLoc, IsSILFuncDecl); - if (!tyR) - status.setIsParseError(); - return makeParserResult(status, tyR); -} + } walker; -ParserResult Parser::parseType() { - return parseType(diag::expected_type); -} + if (tyR) + tyR->walk(walker); + } + if (specifierLoc.isValid() || !attrs.empty()) + SyntaxContext->setCreateSyntax(SyntaxKind::AttributedType); + return makeParserResult(applyAttributeToType(tyR, attrs, specifier, + specifierLoc)); +} ParserResult Parser::parseDeclResultType(Diag<> MessageID) { if (Tok.is(tok::code_complete)) { @@ -524,199 +587,177 @@ SourceLoc Parser::getTypeErrorLoc() const { return getErrorOrMissingLoc(); } -ParsedSyntaxResult -Parser::parseGenericArgumentClauseSyntax() { - assert(startsWithLess(Tok) && "Generic parameter list must start with '<'"); - auto LAngleLoc = Tok.getLoc(); - ParsedGenericArgumentClauseSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - // Parse '<'. - builder.useLeftAngleBracket(consumeStartingLessSyntax()); - - bool hasNext = true; - do { - // Parse argument type. - auto ty = parseTypeSyntax(diag::expected_type); - status |= ty.getStatus(); - if (ty.isNull()) - break; - ParsedGenericArgumentSyntaxBuilder argBuilder(*SyntaxContext); - argBuilder.useArgumentType(ty.get()); +ParserStatus Parser::parseGenericArguments(SmallVectorImpl &Args, + SourceLoc &LAngleLoc, + SourceLoc &RAngleLoc) { + SyntaxParsingContext GenericArgumentsContext( + SyntaxContext, SyntaxKind::GenericArgumentClause); - // Parse trailing comma: ','. - if (Tok.is(tok::comma)) { - argBuilder.useTrailingComma(consumeTokenSyntax()); - } else { - hasNext = false; - } - builder.addArgumentsMember(argBuilder.build()); - } while (hasNext); + // Parse the opening '<'. + assert(startsWithLess(Tok) && "Generic parameter list must start with '<'"); + LAngleLoc = consumeStartingLess(); + + { + SyntaxParsingContext ListContext(SyntaxContext, + SyntaxKind::GenericArgumentList); + + while (true) { + SyntaxParsingContext ElementContext(SyntaxContext, + SyntaxKind::GenericArgument); + ParserResult Ty = parseType(diag::expected_type); + if (Ty.isNull() || Ty.hasCodeCompletion()) { + // Skip until we hit the '>'. + RAngleLoc = skipUntilGreaterInTypeList(); + return ParserStatus(Ty); + } - // Parse '>'. - if (startsWithGreater(Tok)) { - builder.useRightAngleBracket(consumeStartingGreaterSyntax()); - } else { - if (status.isSuccess()) { - diagnose(Tok, diag::expected_rangle_generic_arg_list); - diagnose(LAngleLoc, diag::opening_angle); + Args.push_back(Ty.get()); + // Parse the comma, if the list continues. + if (!consumeIf(tok::comma)) + break; } - checkForInputIncomplete(); - status.setIsParseError(); - if (ignoreUntilGreaterInTypeList()) - builder.useRightAngleBracket(consumeStartingGreaterSyntax()); } - return makeParsedResult(builder.build(), status); -} - -ParserStatus Parser::parseGenericArguments(SmallVectorImpl &ArgsAST, - SourceLoc &LAngleLoc, - SourceLoc &RAngleLoc) { - auto leadingLoc = leadingTriviaLoc(); - auto ParsedClauseResult = parseGenericArgumentClauseSyntax(); - if (ParsedClauseResult.isNull()) - return ParsedClauseResult.getStatus(); + if (!startsWithGreater(Tok)) { + checkForInputIncomplete(); + diagnose(Tok, diag::expected_rangle_generic_arg_list); + diagnose(LAngleLoc, diag::opening_angle); - SyntaxContext->addSyntax(ParsedClauseResult.get()); - if (ParsedClauseResult.isError()) - return ParsedClauseResult.getStatus(); + // Skip until we hit the '>'. + RAngleLoc = skipUntilGreaterInTypeList(); + return makeParserError(); + } else { + RAngleLoc = consumeStartingGreater(); + } - auto Clause = SyntaxContext->topNode(); - Generator.generate(Clause, leadingLoc, LAngleLoc, RAngleLoc, ArgsAST); return makeParserSuccess(); } /// parseTypeIdentifier -/// +/// /// type-identifier: /// identifier generic-args? ('.' identifier generic-args?)* /// -ParsedSyntaxResult Parser::parseTypeIdentifier() { +ParserResult +Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { + // If parsing a qualified declaration name, return error if base type cannot + // be parsed. + if (isParsingQualifiedDeclBaseType && !canParseBaseTypeForQualifiedDeclName()) + return makeParserError(); + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { // is this the 'Any' type - if (Tok.is(tok::kw_Any)) + if (Tok.is(tok::kw_Any)) { return parseAnyType(); - - if (Tok.is(tok::code_complete)) { - auto CCTok = consumeTokenSyntax(tok::code_complete); - auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( - None, None, std::move(CCTok), *SyntaxContext); - return makeParsedCodeCompletion(std::move(ty)); + } else if (Tok.is(tok::code_complete)) { + if (CodeCompletion) + CodeCompletion->completeTypeSimpleBeginning(); + // Eat the code completion token because we handled it. + consumeToken(tok::code_complete); + return makeParserCodeCompletionResult(); } diagnose(Tok, diag::expected_identifier_for_type); // If there is a keyword at the start of a new line, we won't want to // skip it as a recovery but rather keep it. - if (Tok.isKeyword() && !Tok.isAtStartOfLine()) { - auto kwTok = consumeTokenSyntax(); - ParsedTypeSyntax ty = - ParsedSyntaxRecorder::makeUnknownType({&kwTok, 1}, *SyntaxContext); - return makeParsedError(std::move(ty)); - } + if (Tok.isKeyword() && !Tok.isAtStartOfLine()) + consumeToken(); - return makeParsedError(); + return nullptr; } + SyntaxParsingContext IdentTypeCtxt(SyntaxContext, SyntaxContextKind::Type); - SmallVector Junk; - - auto parseComponent = - [&](Optional &Identifier, - Optional &GenericArgs) { - if (Tok.is(tok::kw_Self)) { - Identifier = consumeIdentifierSyntax(); - } else { - // FIXME: specialize diagnostic for 'Type': type cannot start with - // 'metatype' - // FIXME: offer a fixit: 'self' -> 'Self' - Identifier = - parseIdentifierSyntax(diag::expected_identifier_in_dotted_type); - } - - if (!Identifier) - return makeParserError(); - - if (!startsWithLess(Tok)) - return makeParserSuccess(); - - SmallVector GenericArgsAST; - SourceLoc LAngleLoc, RAngleLoc; - auto GenericArgsResult = parseGenericArgumentClauseSyntax(); - if (!GenericArgsResult.isNull()) - GenericArgs = GenericArgsResult.get(); - return GenericArgsResult.getStatus(); - }; + ParserStatus Status; + SmallVector ComponentsR; + SourceLoc EndLoc; + while (true) { + DeclNameLoc Loc; + DeclNameRef Name = + parseDeclNameRef(Loc, diag::expected_identifier_in_dotted_type, {}); + if (!Name) + Status.setIsParseError(); - ParsedSyntaxResult result; - - // Parse the base identifier. - result = [&]() { - Optional identifier; - Optional genericArgs; - auto status = parseComponent(identifier, genericArgs); - assert(identifier); - return makeParsedResult( - ParsedSyntaxRecorder::makeSimpleTypeIdentifier( - std::move(*identifier), std::move(genericArgs), *SyntaxContext), - status); - }(); - - // Parse member identifiers. - while (result.isSuccess() && Tok.isAny(tok::period, tok::period_prefix)) { - if (peekToken().isContextualKeyword("Type") || - peekToken().isContextualKeyword("Protocol")) - break; + if (Loc.isValid()) { + SourceLoc LAngle, RAngle; + SmallVector GenericArgs; + if (startsWithLess(Tok)) { + auto genericArgsStatus = parseGenericArguments(GenericArgs, LAngle, RAngle); + if (genericArgsStatus.isError()) + return genericArgsStatus; + } + EndLoc = Loc.getEndLoc(); - // Parse '.'. - auto period = consumeTokenSyntax(); - - // Parse component; - Optional identifier; - Optional genericArgs; - auto status = parseComponent(identifier, genericArgs); - if (identifier) { - ParsedMemberTypeIdentifierSyntaxBuilder builder(*SyntaxContext); - builder.useBaseType(result.get()); - builder.usePeriod(std::move(period)); - builder.useName(std::move(*identifier)); - if (genericArgs) - builder.useGenericArgumentClause(std::move(*genericArgs)); - result = makeParsedResult(builder.build(), status); - continue; + ComponentIdentTypeRepr *CompT; + if (!GenericArgs.empty()) + CompT = GenericIdentTypeRepr::create(Context, Loc, Name, GenericArgs, + SourceRange(LAngle, RAngle)); + else + CompT = new (Context) SimpleIdentTypeRepr(Loc, Name); + ComponentsR.push_back(CompT); } + SyntaxContext->createNodeInPlace(ComponentsR.size() == 1 + ? SyntaxKind::SimpleTypeIdentifier + : SyntaxKind::MemberTypeIdentifier); - assert(!genericArgs); - - if (Tok.is(tok::code_complete)) { - auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( - result.get(), std::move(period), consumeTokenSyntax(), - *SyntaxContext); - return makeParsedCodeCompletion(std::move(ty)); + // Treat 'Foo.' as an attempt to write a dotted type + // unless is 'Type'. + if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) { + if (peekToken().is(tok::code_complete)) { + Status.setHasCodeCompletion(); + break; + } + if (!peekToken().isContextualKeyword("Type") + && !peekToken().isContextualKeyword("Protocol")) { + // If parsing a qualified declaration name, break before parsing the + // period before the final declaration name component. + if (isParsingQualifiedDeclBaseType) { + // If qualified name base type cannot be parsed from the current + // point (i.e. the next type identifier is not followed by a '.'), + // then the next identifier is the final declaration name component. + BacktrackingScope backtrack(*this); + consumeStartingCharacterOfCurrentToken(tok::period); + if (!canParseBaseTypeForQualifiedDeclName()) + break; + } + // Consume the period. + consumeToken(); + continue; + } + } else if (Tok.is(tok::code_complete)) { + if (!Tok.isAtStartOfLine()) + Status.setHasCodeCompletion(); + break; } - - ParsedSyntax parts[] = {result.get(), std::move(period)}; - return makeParsedResult( - ParsedSyntaxRecorder::makeUnknownType({parts, 2}, *SyntaxContext), - status); + break; } - if (result.isSuccess() && Tok.is(tok::code_complete) && - !Tok.isAtStartOfLine()) { - auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( - result.get(), None, consumeTokenSyntax(), *SyntaxContext); - return makeParsedCodeCompletion(std::move(ty)); + IdentTypeRepr *ITR = nullptr; + if (!ComponentsR.empty()) { + // Lookup element #0 through our current scope chains in case it is some + // thing local (this returns null if nothing is found). + if (auto Entry = lookupInScope(ComponentsR[0]->getNameRef())) + if (auto *TD = dyn_cast(Entry)) + ComponentsR[0]->setValue(TD, nullptr); + + ITR = IdentTypeRepr::create(Context, ComponentsR); } - // Don't propagate malformed type as valid type. - if (!result.isSuccess()) { - auto ty = result.get(); - return makeParsedResult( - ParsedSyntaxRecorder::makeUnknownType({&ty, 1}, *SyntaxContext), - result.getStatus()); + if (Status.hasCodeCompletion()) { + if (Tok.isNot(tok::code_complete)) { + // We have a dot. + consumeToken(); + if (CodeCompletion) + CodeCompletion->completeTypeIdentifierWithDot(ITR); + } else { + if (CodeCompletion) + CodeCompletion->completeTypeIdentifierWithoutDot(ITR); + } + // Eat the code completion token because we handled it. + consumeToken(tok::code_complete); } - return result; + return makeParserResult(Status, ITR); } /// parseTypeSimpleOrComposition @@ -724,107 +765,119 @@ ParsedSyntaxResult Parser::parseTypeIdentifier() { /// type-composition: /// 'some'? type-simple /// type-composition '&' type-simple -ParsedSyntaxResult +ParserResult Parser::parseTypeSimpleOrComposition(Diag<> MessageID, bool HandleCodeCompletion) { + SyntaxParsingContext SomeTypeContext(SyntaxContext, SyntaxKind::SomeType); // Check for the opaque modifier. // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. - Optional FirstSome; + SourceLoc opaqueLoc; if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { // Treat some as a keyword. TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); - FirstSome = consumeTokenSyntax(); + opaqueLoc = consumeToken(); + } else { + // This isn't a some type. + SomeTypeContext.setTransparent(); } - - auto ApplySome = [this](ParsedTypeSyntax Type, - Optional Some) { - return Some ? ParsedSyntaxRecorder::makeSomeType( - std::move(*Some), std::move(Type), *SyntaxContext) - : std::move(Type); + + auto applyOpaque = [&](TypeRepr *type) -> TypeRepr* { + if (opaqueLoc.isValid()) { + type = new (Context) OpaqueReturnTypeRepr(opaqueLoc, type); + } + return type; }; - + + SyntaxParsingContext CompositionContext(SyntaxContext, SyntaxContextKind::Type); // Parse the first type - auto FirstTypeResult = parseTypeSimple(MessageID, HandleCodeCompletion); - - if (FirstTypeResult.isError()) - return FirstTypeResult; - - auto FirstType = FirstTypeResult.get(); - - if (!Tok.isContextualPunctuator("&")) - return makeParsedResult( - ApplySome(std::move(FirstType), std::move(FirstSome))); - - SmallVector Elements; - - Optional Ampersand = consumeTokenSyntax(); - auto FirstElement = ParsedSyntaxRecorder::makeCompositionTypeElement( - std::move(FirstType), std::move(*Ampersand), *SyntaxContext); - Elements.push_back(std::move(FirstElement)); + ParserResult FirstType = parseTypeSimple(MessageID, + HandleCodeCompletion); + if (FirstType.hasCodeCompletion()) + return makeParserCodeCompletionResult(); + if (FirstType.isNull()) + return FirstType; + if (!Tok.isContextualPunctuator("&")) { + return makeParserResult(ParserStatus(FirstType), + applyOpaque(FirstType.get())); + } - ParserStatus Status; + SmallVector Types; + ParserStatus Status(FirstType); + SourceLoc FirstTypeLoc = FirstType.get()->getStartLoc(); + SourceLoc FirstAmpersandLoc = Tok.getLoc(); + + auto addType = [&](TypeRepr *T) { + if (!T) return; + if (auto Comp = dyn_cast(T)) { + // Accept protocol & P3; explode it. + auto TyRs = Comp->getTypes(); + if (!TyRs.empty()) // If empty, is 'Any'; ignore. + Types.append(TyRs.begin(), TyRs.end()); + return; + } + Types.push_back(T); + }; + addType(FirstType.get()); + SyntaxContext->setCreateSyntax(SyntaxKind::CompositionType); + assert(Tok.isContextualPunctuator("&")); do { + if (SyntaxContext->isEnabled()) { + auto Type = SyntaxContext->popIf(); + consumeToken(); // consume '&' + if (Type) { + ParsedCompositionTypeElementSyntaxBuilder Builder(*SyntaxContext); + auto Ampersand = SyntaxContext->popToken(); + Builder + .useAmpersand(std::move(Ampersand)) + .useType(std::move(*Type)); + SyntaxContext->addSyntax(Builder.build()); + } + } else { + consumeToken(); // consume '&' + } + // Diagnose invalid `some` after an ampersand. - Optional NextSome; if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { - auto NextSomeLoc = Tok.getLoc(); - NextSome = consumeTokenSyntax(); - // TODO: Fixit to move to beginning of composition. - diagnose(NextSomeLoc, diag::opaque_mid_composition); - } - - auto NextTypeResult = parseTypeSimple(diag::expected_identifier_for_type, - HandleCodeCompletion); + auto badLoc = consumeToken(); - if (!NextTypeResult.isSuccess()) { - Status |= NextTypeResult.getStatus(); - if (NextTypeResult.isNull()) - break; + diagnose(badLoc, diag::opaque_mid_composition) + .fixItRemove(badLoc) + .fixItInsert(FirstTypeLoc, "some "); - SmallVector nodes; - if (FirstSome) - nodes.push_back(std::move(*FirstSome)); - std::move(Elements.begin(), Elements.end(), std::back_inserter(nodes)); - if (NextSome) - nodes.push_back(std::move(*NextSome)); - nodes.push_back(NextTypeResult.get()); - - auto ty = ParsedSyntaxRecorder::makeUnknownType(nodes, *SyntaxContext); - return makeParsedResult(std::move(ty), Status); + if (opaqueLoc.isInvalid()) + opaqueLoc = badLoc; } - auto NextType = ApplySome(NextTypeResult.get(), std::move(NextSome)); - Ampersand = Tok.isContextualPunctuator("&") - ? consumeTokenSyntax() - : llvm::Optional(); - auto NextElement = ParsedSyntaxRecorder::makeCompositionTypeElement( - std::move(NextType), std::move(Ampersand), *SyntaxContext); - Elements.push_back(std::move(NextElement)); - } while (Ampersand); - - auto ElementList = ParsedSyntaxRecorder::makeCompositionTypeElementList( - Elements, *SyntaxContext); - auto Composition = ParsedSyntaxRecorder::makeCompositionType( - std::move(ElementList), *SyntaxContext); - return makeParsedResult( - ApplySome(std::move(Composition), std::move(FirstSome)), Status); -} - -ParserResult Parser::parseAnyTypeAST() { - auto AnyLoc = leadingTriviaLoc(); - auto ParsedAny = parseAnyType().get(); - SyntaxContext->addSyntax(std::move(ParsedAny)); - auto Any = SyntaxContext->topNode(); - return makeParserResult(Generator.generate(Any, AnyLoc)); + // Parse next type. + ParserResult ty = + parseTypeSimple(diag::expected_identifier_for_type, HandleCodeCompletion); + if (ty.hasCodeCompletion()) + return makeParserCodeCompletionResult(); + Status |= ty; + addType(ty.getPtrOrNull()); + } while (Tok.isContextualPunctuator("&")); + + if (SyntaxContext->isEnabled()) { + if (auto synType = SyntaxContext->popIf()) { + auto LastNode = ParsedSyntaxRecorder::makeCompositionTypeElement( + std::move(*synType), None, *SyntaxContext); + SyntaxContext->addSyntax(std::move(LastNode)); + } + } + SyntaxContext->collectNodesInPlace(SyntaxKind::CompositionTypeElementList); + + return makeParserResult(Status, applyOpaque(CompositionTypeRepr::create( + Context, Types, FirstTypeLoc, {FirstAmpersandLoc, PreviousLoc}))); } -ParsedSyntaxResult Parser::parseAnyType() { - auto Any = consumeTokenSyntax(tok::kw_Any); - auto Type = ParsedSyntaxRecorder::makeSimpleTypeIdentifier( - std::move(Any), llvm::None, *SyntaxContext); - return makeParsedResult(std::move(Type)); +ParserResult Parser::parseAnyType() { + SyntaxParsingContext IdentTypeCtxt(SyntaxContext, + SyntaxKind::SimpleTypeIdentifier); + auto Loc = consumeToken(tok::kw_Any); + auto TyR = CompositionTypeRepr::createEmptyComposition(Context, Loc); + return makeParserResult(TyR); } /// parseOldStyleProtocolComposition @@ -835,72 +888,59 @@ ParsedSyntaxResult Parser::parseAnyType() { /// type-composition-list-deprecated: /// type-identifier /// type-composition-list-deprecated ',' type-identifier -ParsedSyntaxResult -Parser::parseOldStyleProtocolComposition() { - // Defer all nodes so that we can de-structure the composed types in case we - // need to emit a diagnostic (below). - DeferringContextRAII Deferring(*SyntaxContext); +ParserResult Parser::parseOldStyleProtocolComposition() { + assert(Tok.is(tok::kw_protocol) && startsWithLess(peekToken())); - SmallVector Junk; + // Start a context for creating type syntax. + SyntaxParsingContext TypeParsingContext(SyntaxContext, + SyntaxContextKind::Type); - auto ProtocolLoc = Tok.getLoc(); - auto Protocol = consumeTokenSyntax(); - auto LAngleLoc = Tok.getLoc(); - auto LAngle = consumeStartingLessSyntax(); - - Junk.push_back(Protocol.copyDeferred()); - Junk.push_back(LAngle.copyDeferred()); + SourceLoc ProtocolLoc = consumeToken(); + SourceLoc LAngleLoc = consumeStartingLess(); // Parse the type-composition-list. ParserStatus Status; - SmallVector Protocols; - Optional Comma; + SmallVector Protocols; bool IsEmpty = startsWithGreater(Tok); if (!IsEmpty) { do { - bool IsAny = Tok.getKind() == tok::kw_Any; - auto TypeResult = parseTypeIdentifier(); - Status |= TypeResult.getStatus(); - if (!TypeResult.isNull()) { - auto Type = TypeResult.get(); - Junk.push_back(Type.copyDeferred()); - if (!IsAny) - Protocols.push_back(std::move(Type)); - } - Comma = consumeTokenSyntaxIf(tok::comma); - if (Comma) - Junk.push_back(Comma->copyDeferred()); - } while (Comma); + // Parse the type-identifier. + ParserResult Protocol = parseTypeIdentifier(); + Status |= Protocol; + if (auto *ident = + dyn_cast_or_null(Protocol.getPtrOrNull())) + Protocols.push_back(ident); + } while (consumeIf(tok::comma)); } // Check for the terminating '>'. - Optional RAngleLoc; + SourceLoc RAngleLoc = PreviousLoc; if (startsWithGreater(Tok)) { - RAngleLoc = Tok.getLoc(); - auto RAngle = consumeStartingGreaterSyntax(); - Junk.push_back(RAngle.copyDeferred()); + RAngleLoc = consumeStartingGreater(); } else { if (Status.isSuccess()) { diagnose(Tok, diag::expected_rangle_protocol); diagnose(LAngleLoc, diag::opening_angle); Status.setIsParseError(); } - - SmallVector RAngleJunk; + // Skip until we hit the '>'. - skipUntilGreaterInTypeListSyntax(RAngleJunk, /*protocolComposition=*/true); - for (auto &&Piece : RAngleJunk) - Junk.push_back(Piece.copyDeferred()); + RAngleLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true); } + auto composition = CompositionTypeRepr::create( + Context, Protocols, ProtocolLoc, {LAngleLoc, RAngleLoc}); + if (Status.isSuccess()) { + // Only if we have complete protocol<...> construct, diagnose deprecated. SmallString<32> replacement; if (Protocols.empty()) { replacement = "Any"; } else { - auto extractText = [&](ParsedTypeSyntax &Type) -> StringRef { - auto SourceRange = Type.getRaw().getDeferredRange(); - return SourceMgr.extractText(SourceRange).trim(); + auto extractText = [&](TypeRepr *Ty) -> StringRef { + auto SourceRange = Ty->getSourceRange(); + return SourceMgr.extractText( + Lexer::getCharSourceRangeFromSourceRange(SourceMgr, SourceRange)); }; auto Begin = Protocols.begin(); replacement += extractText(*Begin); @@ -925,22 +965,22 @@ Parser::parseOldStyleProtocolComposition() { // Copy split token after '>' to the replacement string. // FIXME: lexer should smartly separate '>' and trailing contents like '?'. - StringRef TrailingContent = L->getTokenAt(*RAngleLoc).getRange().str(). + StringRef TrailingContent = L->getTokenAt(RAngleLoc).getRange().str(). substr(1); - if (!TrailingContent.empty()) + if (!TrailingContent.empty()) { replacement += TrailingContent; + } // Replace 'protocol' with 'T1 & T2' diagnose(ProtocolLoc, IsEmpty ? diag::deprecated_any_composition : Protocols.size() > 1 ? diag::deprecated_protocol_composition : diag::deprecated_protocol_composition_single) - .highlight({ProtocolLoc, *RAngleLoc}) - .fixItReplace({ProtocolLoc, *RAngleLoc}, replacement); + .highlight(composition->getSourceRange()) + .fixItReplace(composition->getSourceRange(), replacement); } - auto Unknown = ParsedSyntaxRecorder::makeUnknownType(Junk, *SyntaxContext); - return makeParsedResult(std::move(Unknown)); + return makeParserResult(Status, composition); } /// parseTypeTupleBody @@ -951,257 +991,196 @@ Parser::parseOldStyleProtocolComposition() { /// type-tuple-element: /// identifier? identifier ':' type /// type -ParsedSyntaxResult Parser::parseTypeTupleBody() { +ParserResult Parser::parseTypeTupleBody() { // Force the context to create deferred nodes, as we might need to // de-structure the tuple type to create a function type. DeferringContextRAII Deferring(*SyntaxContext); + SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::TupleType); + TypeContext.setCreateSyntax(SyntaxKind::TupleType); Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); - if (ParsingTypeTuple.isFailed()) - return makeParsedError(); - - ParsedTupleTypeSyntaxBuilder builder(*SyntaxContext); - - // Parse '('. - auto LParenLoc = Tok.getLoc(); - builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); - - // Parse the elements. - SmallVector Elements; - SmallVector, 4> ElementsLoc; - SourceLoc FirstEllipsisLoc; - auto Status = parseListSyntax( - Elements, /*AllowEmpty=*/true, /*AllowSepAfterLast=*/false, - [&] { return Tok.is(tok::r_paren); }, - [&](ParsedTupleTypeElementSyntaxBuilder &elemBuilder) { - Optional Backtracking; - - // 'inout' here can be a obsoleted use of the marker in an argument - // list, consume it in backtracking context so we can determine it's - // really a deprecated use of it. - SourceLoc InOutLoc; - Optional InOut; - bool IsInOutObsoleted = false; - if (Tok.is(tok::kw_inout)) { - InOutLoc = Tok.getLoc(); - InOut = consumeTokenSyntax(tok::kw_inout); - IsInOutObsoleted = true; - } + if (ParsingTypeTuple.isFailed()) { + return makeParserError(); + } - // If the label is "some", this could end up being an opaque type - // description if there's `some ` without a following colon, - // so we may need to backtrack as well. - if (Tok.getText().equals("some")) - Backtracking.emplace(*this); - - // If the tuple element starts with a potential argument label followed - // by a ':' or another potential argument label, then the identifier is - // an element tag, and it is followed by a type annotation. - Optional Name; - Optional SecondName; - Optional Colon; - SourceLoc NameLoc; - SourceLoc SecondNameLoc; - if (Tok.canBeArgumentLabel() && - (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { - // Consume a name. - NameLoc = Tok.getLoc(); - Name = consumeArgumentLabelSyntax(); - - // If there is a second name, consume it as well. - if (Tok.canBeArgumentLabel()) { - SecondNameLoc = Tok.getLoc(); - SecondName = consumeArgumentLabelSyntax(); - } + SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren); + SourceLoc EllipsisLoc; + unsigned EllipsisIdx; + SmallVector ElementsR; + + ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc, + /*AllowSepAfterLast=*/false, + diag::expected_rparen_tuple_type_list, + SyntaxKind::TupleTypeElementList, + [&] () -> ParserStatus { + TupleTypeReprElement element; + + // 'inout' here can be a obsoleted use of the marker in an argument list, + // consume it in backtracking context so we can determine it's really a + // deprecated use of it. + llvm::Optional Backtracking; + SourceLoc ObsoletedInOutLoc; + if (Tok.is(tok::kw_inout)) { + Backtracking.emplace(*this); + ObsoletedInOutLoc = consumeToken(tok::kw_inout); + } + + // If the label is "some", this could end up being an opaque type + // description if there's `some ` without a following colon, + // so we may need to backtrack as well. + if (Tok.getText().equals("some")) { + Backtracking.emplace(*this); + } - // Consume the ':'. - if ((Colon = consumeTokenSyntaxIf(tok::colon))) { - // If we succeed, then we successfully parsed a label. - if (Backtracking) - Backtracking->cancelBacktrack(); - // Otherwise, if we can't backtrack to parse this as a type, - // this is a syntax error. - } else { - if (!Backtracking) - diagnose(Tok, diag::expected_parameter_colon); - NameLoc = SourceLoc(); - SecondNameLoc = SourceLoc(); - } - } else if (InOut) { - // If we don't have labels, 'inout' is not a obsoleted use. - IsInOutObsoleted = false; + // If the tuple element starts with a potential argument label followed by a + // ':' or another potential argument label, then the identifier is an + // element tag, and it is followed by a type annotation. + if (Tok.canBeArgumentLabel() + && (peekToken().is(tok::colon) + || peekToken().canBeArgumentLabel())) { + // Consume a name. + element.NameLoc = consumeArgumentLabel(element.Name); + + // If there is a second name, consume it as well. + if (Tok.canBeArgumentLabel()) + element.SecondNameLoc = consumeArgumentLabel(element.SecondName); + + // Consume the ':'. + if (consumeIf(tok::colon, element.ColonLoc)) { + // If we succeed, then we successfully parsed a label. + if (Backtracking) + Backtracking->cancelBacktrack(); + // Otherwise, if we can't backtrack to parse this as a type, + // this is a syntax error. + } else { + if (!Backtracking) { + diagnose(Tok, diag::expected_parameter_colon); } + element.NameLoc = SourceLoc(); + element.SecondNameLoc = SourceLoc(); + } - if (!Backtracking || !Backtracking->willBacktrack()) { - if (Name) - elemBuilder.useName(std::move(*Name)); - if (SecondName) - elemBuilder.useSecondName(std::move(*SecondName)); - if (Colon) - elemBuilder.useColon(std::move(*Colon)); - } else if (Backtracking && Backtracking->willBacktrack()) { - NameLoc = SourceLoc(); - SecondNameLoc = SourceLoc(); - Name.reset(); - SecondName.reset(); - assert(!Colon.hasValue()); - } - Backtracking.reset(); - - // Parse the type. - auto TypeLoc = Tok.getLoc(); - auto ty = parseTypeSyntax(diag::expected_type); - if (ty.isNull()) { - ty = makeParsedResult( - ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext), - ty.getStatus()); - } + } else if (Backtracking) { + // If we don't have labels, 'inout' is not a obsoleted use. + ObsoletedInOutLoc = SourceLoc(); + } + Backtracking.reset(); + + // Parse the type annotation. + auto type = parseType(diag::expected_type); + if (type.hasCodeCompletion()) + return makeParserCodeCompletionStatus(); + if (type.isNull()) + return makeParserError(); + element.Type = type.get(); + + // Complain obsoleted 'inout' etc. position; (inout name: Ty) + if (ObsoletedInOutLoc.isValid()) { + if (isa(element.Type)) { + // If the parsed type is already a inout type et al, just remove it. + diagnose(Tok, diag::parameter_specifier_repeated) + .fixItRemove(ObsoletedInOutLoc); + } else { + diagnose(ObsoletedInOutLoc, + diag::parameter_specifier_as_attr_disallowed, "inout") + .fixItRemove(ObsoletedInOutLoc) + .fixItInsert(element.Type->getStartLoc(), "inout "); + // Build inout type. Note that we bury the inout locator within the + // named locator. This is weird but required by Sema apparently. + element.Type = + new (Context) InOutTypeRepr(element.Type, ObsoletedInOutLoc); + } + } - // Handle pre-parsed 'inout'. - if (InOut) { - if (IsInOutObsoleted) { - elemBuilder.useInOut(std::move(*InOut)); - bool IsTypeAlreadyAttributed = false; - if (!ty.isNull()) { - if (auto AttributedType = - ty.getAs()) { - IsTypeAlreadyAttributed = - AttributedType->getDeferredSpecifier().hasValue(); - ty = makeParsedResult(std::move(*AttributedType), - ty.getStatus()); - } - } - if (IsTypeAlreadyAttributed) { - // If the parsed type is already attributed, suggest removing - // `inout`. - diagnose(Tok, diag::parameter_specifier_repeated) - .fixItRemove(InOutLoc); - } else { - diagnose(InOutLoc, diag::parameter_specifier_as_attr_disallowed, - "inout") - .fixItRemove(InOutLoc) - .fixItInsert(TypeLoc, "inout "); - } - } else { - // Apply 'inout' to the parsed type. - ParsedAttributedTypeSyntaxBuilder builder(*SyntaxContext); - ty = applyAttributeToTypeSyntax(std::move(ty), std::move(InOut), - None); - TypeLoc = InOutLoc; - InOutLoc = SourceLoc(); - InOut.reset(); - } - } - if (!ty.isNull()) - elemBuilder.useType(ty.get()); - ElementsLoc.emplace_back(NameLoc, SecondNameLoc, TypeLoc); - if (ty.isError()) - return ty.getStatus(); - - // Parse '...'. - if (Tok.isEllipsis()) { - auto ElementEllipsisLoc = Tok.getLoc(); - Tok.setKind(tok::ellipsis); - elemBuilder.useEllipsis(consumeTokenSyntax(tok::ellipsis)); - if (!FirstEllipsisLoc.isValid()) { - FirstEllipsisLoc = ElementEllipsisLoc; - } else { - diagnose(ElementEllipsisLoc, diag::multiple_ellipsis_in_tuple) - .highlight(FirstEllipsisLoc) - .fixItRemove(ElementEllipsisLoc); - } - } + // Parse optional '...'. + if (Tok.isEllipsis()) { + Tok.setKind(tok::ellipsis); + auto ElementEllipsisLoc = consumeToken(); + if (EllipsisLoc.isInvalid()) { + EllipsisLoc = ElementEllipsisLoc; + EllipsisIdx = ElementsR.size(); + } else { + diagnose(ElementEllipsisLoc, diag::multiple_ellipsis_in_tuple) + .highlight(EllipsisLoc) + .fixItRemove(ElementEllipsisLoc); + } + } - // Parse the initializer ('=' expr). - if (Tok.is(tok::equal)) { - ParsedInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); - auto EqualLoc = Tok.getLoc(); - initBuilder.useEqual(consumeTokenSyntax(tok::equal)); - SyntaxParsingContext tmpCtxt(SyntaxContext); - tmpCtxt.setTransparent(); - auto Init = parseExpr(diag::expected_init_value); - auto InFlight = diagnose(EqualLoc, diag::tuple_type_init); - if (Init.isNonNull()) - InFlight.fixItRemove(SourceRange(EqualLoc, PreviousLoc)); - if (auto expr = SyntaxContext->popIf()) - initBuilder.useValue(std::move(*expr)); - else - initBuilder.useValue( - ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); - elemBuilder.useInitializer(initBuilder.build()); - } + // Parse '= expr' here so we can complain about it directly, rather + // than dying when we see it. + if (Tok.is(tok::equal)) { + SyntaxParsingContext InitContext(SyntaxContext, + SyntaxKind::InitializerClause); + SourceLoc equalLoc = consumeToken(tok::equal); + auto init = parseExpr(diag::expected_init_value); + auto inFlight = diagnose(equalLoc, diag::tuple_type_init); + if (init.isNonNull()) + inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc())); + } - return makeParserSuccess(); - }); + // Record the ',' location. + if (Tok.is(tok::comma)) + element.TrailingCommaLoc = Tok.getLoc(); - // Parse ')'. - auto RParen = parseMatchingTokenSyntax( - tok::r_paren, diag::expected_rparen_tuple_type_list, LParenLoc, - /*silenceDiag=*/Status.isError()); - Status |= RParen.getStatus(); + ElementsR.push_back(element); + return makeParserSuccess(); + }); - bool IsFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows); + if (EllipsisLoc.isInvalid()) + EllipsisIdx = ElementsR.size(); - auto GetNameText = [this](Optional Name) { - return !Name ? StringRef() - : SourceMgr.extractText( - Name->getRaw().getDeferredTokenRange(), - L->getBufferID()); - }; + bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, + tok::kw_rethrows); - if (!IsFunctionType) { - for (unsigned i = 0; i < Elements.size(); i++) { - // true tuples have labels - auto &Element = Elements[i]; - SourceLoc NameLoc, SecondNameLoc, TypeLoc; - std::tie(NameLoc, SecondNameLoc, TypeLoc) = ElementsLoc[i]; + // If there were any labels, figure out which labels should go into the type + // representation. + for (auto &element : ElementsR) { + // True tuples have labels. + if (!isFunctionType) { // If there were two names, complain. - if (NameLoc.isValid() && SecondNameLoc.isValid()) { - auto Diag = diagnose(NameLoc, diag::tuple_type_multiple_labels); - auto Name = Element.getDeferredName(); - auto NameText = SourceMgr.extractText( - Name->getRaw().getDeferredTokenRange(), - L->getBufferID()); - if (NameText == "_") { - Diag.fixItRemoveChars(NameLoc, TypeLoc); + if (element.NameLoc.isValid() && element.SecondNameLoc.isValid()) { + auto diag = diagnose(element.NameLoc, diag::tuple_type_multiple_labels); + if (element.Name.empty()) { + diag.fixItRemoveChars(element.NameLoc, + element.Type->getStartLoc()); } else { - Diag.fixItRemove(SourceRange( - Lexer::getLocForEndOfToken(SourceMgr, NameLoc), SecondNameLoc)); + diag.fixItRemove( + SourceRange(Lexer::getLocForEndOfToken(SourceMgr, element.NameLoc), + element.SecondNameLoc)); } } + continue; } - } else { - for (unsigned i = 0; i < Elements.size(); i++) { - // If there was a first name, complain; arguments in function types are - // always unlabeled. - auto &Element = Elements[i]; - SourceLoc NameLoc, SecondNameLoc, TypeLoc; - std::tie(NameLoc, SecondNameLoc, TypeLoc) = ElementsLoc[i]; - if (NameLoc.isValid()) { - auto NameText = GetNameText(Element.getDeferredName()); - if (NameText != "_") { - auto NameIdentifier = Context.getIdentifier(NameText); - auto Diag = diagnose(NameLoc, diag::function_type_argument_label, - NameIdentifier); - auto SecondNameText = GetNameText(Element.getDeferredSecondName()); - if (SecondNameLoc.isInvalid()) - Diag.fixItInsert(NameLoc, "_ "); - else if (SecondNameText == "_") - Diag.fixItRemoveChars(NameLoc, TypeLoc); - else - Diag.fixItReplace(SourceRange(NameLoc), "_"); - } - } + + // If there was a first name, complain; arguments in function types are + // always unlabeled. + if (element.NameLoc.isValid() && !element.Name.empty()) { + auto diag = diagnose(element.NameLoc, diag::function_type_argument_label, + element.Name); + if (element.SecondNameLoc.isInvalid()) + diag.fixItInsert(element.NameLoc, "_ "); + else if (element.SecondName.empty()) + diag.fixItRemoveChars(element.NameLoc, + element.Type->getStartLoc()); + else + diag.fixItReplace(SourceRange(element.NameLoc), "_"); + } + + if (element.SecondNameLoc.isValid()) { + // Form the named parameter type representation. + element.UnderscoreLoc = element.NameLoc; + element.Name = element.SecondName; + element.NameLoc = element.SecondNameLoc; } } - for (auto &elem : Elements) - builder.addElementsMember(std::move(elem)); - if (!RParen.isNull()) - builder.useRightParen(RParen.get()); - return makeParsedResult(builder.build(), Status); + return makeParserResult(Status, + TupleTypeRepr::create(Context, ElementsR, + SourceRange(LPLoc, RPLoc), + EllipsisLoc, EllipsisIdx)); } + /// parseTypeArray - Parse the type-array production, given that we /// are looking at the initial l_square. Note that this index /// clause is actually the outermost (first-indexed) clause. @@ -1211,100 +1190,119 @@ ParsedSyntaxResult Parser::parseTypeTupleBody() { /// type-array '[' ']' /// type-array '[' expr ']' /// -ParsedSyntaxResult -Parser::parseTypeArray(ParsedTypeSyntax Base, SourceLoc BaseLoc) { +ParserResult Parser::parseTypeArray(TypeRepr *Base) { assert(Tok.isFollowingLSquare()); - auto LSquareLoc = Tok.getLoc(); - ignoreToken(tok::l_square); - - // Ignore integer literal between '[' and ']' - ignoreIf(tok::integer_literal); - - auto RSquareLoc = Tok.getLoc(); - auto RSquare = parseMatchingTokenSyntax( - tok::r_square, diag::expected_rbracket_array_type, LSquareLoc); - - if (!RSquare.isNull()) { - // If we parsed something valid, diagnose it with a fixit to rewrite it to - // Swift syntax. - diagnose(LSquareLoc, diag::new_array_syntax) - .fixItInsert(BaseLoc, "[") - .fixItRemoveChars(LSquareLoc, RSquareLoc); + Parser::StructureMarkerRAII ParsingArrayBound(*this, Tok); + SourceLoc lsquareLoc = consumeToken(); + ArrayTypeRepr *ATR = nullptr; + + // Handle a postfix [] production, a common typo for a C-like array. + + // If we have something that might be an array size expression, parse it as + // such, for better error recovery. + if (Tok.isNot(tok::r_square)) { + auto sizeEx = parseExprBasic(diag::expected_expr); + if (sizeEx.hasCodeCompletion()) + return makeParserCodeCompletionStatus(); + if (sizeEx.isNull()) + return makeParserErrorResult(Base); } - - ParsedArrayTypeSyntaxBuilder builder(*SyntaxContext); - ParserStatus status; - - builder.useElementType(std::move(Base)); - if (!RSquare.isNull()) - builder.useRightSquareBracket(RSquare.get()); - status |= RSquare.getStatus(); - - return makeParsedResult(builder.build(), status); + + SourceLoc rsquareLoc; + if (parseMatchingToken(tok::r_square, rsquareLoc, + diag::expected_rbracket_array_type, lsquareLoc)) + return makeParserErrorResult(Base); + + // If we parsed something valid, diagnose it with a fixit to rewrite it to + // Swift syntax. + diagnose(lsquareLoc, diag::new_array_syntax) + .fixItInsert(Base->getStartLoc(), "[") + .fixItRemove(lsquareLoc); + + // Build a normal array slice type for recovery. + ATR = new (Context) ArrayTypeRepr(Base, + SourceRange(Base->getStartLoc(), rsquareLoc)); + return makeParserResult(ATR); } -/// Parse a collection type. -/// type-simple: -/// '[' type ']' -/// '[' type ':' type ']' -ParsedSyntaxResult Parser::parseTypeCollection() { +SyntaxParserResult Parser::parseTypeCollection() { ParserStatus Status; + // Parse the leading '['. assert(Tok.is(tok::l_square)); Parser::StructureMarkerRAII parsingCollection(*this, Tok); - auto LSquareLoc = Tok.getLoc(); - auto LSquare = consumeTokenSyntax(tok::l_square); + SourceLoc lsquareLoc = consumeToken(); - auto ElementTypeResult = parseTypeSyntax(diag::expected_element_type); - Status |= ElementTypeResult.getStatus(); - auto ElementType = ElementTypeResult.getOrNull(); - if (!ElementType) - ElementType = ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext); - - Optional Colon; - Optional ValueType; + // Parse the element type. + ParserResult firstTy = parseType(diag::expected_element_type); + Status |= firstTy; + // If there is a ':', this is a dictionary type. + SourceLoc colonLoc; + ParserResult secondTy; if (Tok.is(tok::colon)) { - Colon = consumeTokenSyntax(tok::colon); - auto ValueTypeResult = - parseTypeSyntax(diag::expected_dictionary_value_type); - ValueType = ValueTypeResult.getOrNull(); - if (!ValueType) - ValueType = ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext); - Status |= ValueTypeResult.getStatus(); + colonLoc = consumeToken(); + + // Parse the second type. + secondTy = parseType(diag::expected_dictionary_value_type); + Status |= secondTy; } - auto Diag = Colon ? diag::expected_rbracket_dictionary_type - : diag::expected_rbracket_array_type; - - auto RSquare = parseMatchingTokenSyntax(tok::r_square, Diag, LSquareLoc); - Status |= RSquare.getStatus(); - - if (Colon) { - ParsedDictionaryTypeSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquareBracket(std::move(LSquare)); - builder.useKeyType(std::move(*ElementType)); - builder.useColon(std::move(*Colon)); - builder.useValueType(std::move(*ValueType)); - if (!RSquare.isNull()) - builder.useRightSquareBracket(RSquare.get()); - return makeParsedResult(builder.build(), Status); + // Parse the closing ']'. + SourceLoc rsquareLoc; + if (parseMatchingToken(tok::r_square, rsquareLoc, + colonLoc.isValid() + ? diag::expected_rbracket_dictionary_type + : diag::expected_rbracket_array_type, + lsquareLoc)) + Status.setIsParseError(); + + if (Status.hasCodeCompletion()) + return Status; + + // If we couldn't parse anything for one of the types, propagate the error. + if (Status.isError()) + return makeParserError(); + + TypeRepr *TyR; + llvm::Optional SyntaxNode; + + SourceRange brackets(lsquareLoc, rsquareLoc); + if (colonLoc.isValid()) { + // Form the dictionary type. + TyR = new (Context) + DictionaryTypeRepr(firstTy.get(), secondTy.get(), colonLoc, brackets); + if (SyntaxContext->isEnabled()) { + ParsedDictionaryTypeSyntaxBuilder Builder(*SyntaxContext); + auto RightSquareBracket = SyntaxContext->popToken(); + auto ValueType(std::move(*SyntaxContext->popIf())); + auto Colon = SyntaxContext->popToken(); + auto KeyType(std::move(*SyntaxContext->popIf())); + auto LeftSquareBracket = SyntaxContext->popToken(); + Builder + .useRightSquareBracket(std::move(RightSquareBracket)) + .useValueType(std::move(ValueType)) + .useColon(std::move(Colon)) + .useKeyType(std::move(KeyType)) + .useLeftSquareBracket(std::move(LeftSquareBracket)); + SyntaxNode.emplace(Builder.build()); + } } else { - ParsedArrayTypeSyntaxBuilder builder(*SyntaxContext); - builder.useLeftSquareBracket(std::move(LSquare)); - builder.useElementType(std::move(*ElementType)); - if (!RSquare.isNull()) - builder.useRightSquareBracket(RSquare.get()); - return makeParsedResult(builder.build(), Status); + // Form the array type. + TyR = new (Context) ArrayTypeRepr(firstTy.get(), brackets); + if (SyntaxContext->isEnabled()) { + ParsedArrayTypeSyntaxBuilder Builder(*SyntaxContext); + auto RightSquareBracket = SyntaxContext->popToken(); + auto ElementType(std::move(*SyntaxContext->popIf())); + auto LeftSquareBracket = SyntaxContext->popToken(); + Builder + .useRightSquareBracket(std::move(RightSquareBracket)) + .useElementType(std::move(ElementType)) + .useLeftSquareBracket(std::move(LeftSquareBracket)); + SyntaxNode.emplace(Builder.build()); + } } -} - -ParsedSyntaxResult -Parser::parseMetatypeType(ParsedTypeSyntax Base) { - auto Period = consumeTokenSyntax(); // tok::period or tok::period_prefix - auto Keyword = consumeTokenSyntax(tok::identifier); // "Type" or "Protocol" - auto MetatypeType = ParsedSyntaxRecorder::makeMetatypeType( - std::move(Base), std::move(Period), std::move(Keyword), *SyntaxContext); - return makeParsedResult(std::move(MetatypeType)); + + return makeSyntaxResult(Status, std::move(SyntaxNode), TyR); } bool Parser::isOptionalToken(const Token &T) const { @@ -1336,42 +1334,58 @@ bool Parser::isImplicitlyUnwrappedOptionalToken(const Token &T) const { return false; } -ParsedTokenSyntax Parser::consumeOptionalTokenSyntax() { - assert(isOptionalToken(Tok) && "not a '?' token?!"); - return consumeStartingCharacterOfCurrentTokenSyntax(tok::question_postfix, 1); -} - SourceLoc Parser::consumeOptionalToken() { assert(isOptionalToken(Tok) && "not a '?' token?!"); return consumeStartingCharacterOfCurrentToken(tok::question_postfix); } -ParsedTokenSyntax Parser::consumeImplicitlyUnwrappedOptionalTokenSyntax() { - assert(isImplicitlyUnwrappedOptionalToken(Tok) && "not a '!' token?!"); - // If the text of the token is just '!', grab the next token. - return consumeStartingCharacterOfCurrentTokenSyntax(tok::exclaim_postfix, 1); -} - SourceLoc Parser::consumeImplicitlyUnwrappedOptionalToken() { assert(isImplicitlyUnwrappedOptionalToken(Tok) && "not a '!' token?!"); // If the text of the token is just '!', grab the next token. return consumeStartingCharacterOfCurrentToken(tok::exclaim_postfix); } -ParsedSyntaxResult -Parser::parseOptionalType(ParsedTypeSyntax Base) { - auto Question = consumeOptionalTokenSyntax(); - auto Optional = ParsedSyntaxRecorder::makeOptionalType( - std::move(Base), std::move(Question), *SyntaxContext); - return makeParsedResult(std::move(Optional)); +/// Parse a single optional suffix, given that we are looking at the +/// question mark. +SyntaxParserResult +Parser::parseTypeOptional(TypeRepr *base) { + SourceLoc questionLoc = consumeOptionalToken(); + auto TyR = new (Context) OptionalTypeRepr(base, questionLoc); + llvm::Optional SyntaxNode; + if (SyntaxContext->isEnabled()) { + auto QuestionMark = SyntaxContext->popToken(); + if (auto WrappedType = SyntaxContext->popIf()) { + ParsedOptionalTypeSyntaxBuilder Builder(*SyntaxContext); + Builder + .useQuestionMark(std::move(QuestionMark)) + .useWrappedType(std::move(*WrappedType)); + SyntaxNode.emplace(Builder.build()); + } else { + // Undo the popping of the question mark + SyntaxContext->addSyntax(std::move(QuestionMark)); + } + } + return makeSyntaxResult(std::move(SyntaxNode), TyR); } -ParsedSyntaxResult -Parser::parseImplicitlyUnwrappedOptionalType(ParsedTypeSyntax Base) { - auto Exclamation = consumeImplicitlyUnwrappedOptionalTokenSyntax(); - auto Unwrapped = ParsedSyntaxRecorder::makeImplicitlyUnwrappedOptionalType( - std::move(Base), std::move(Exclamation), *SyntaxContext); - return makeParsedResult(std::move(Unwrapped)); +/// Parse a single implicitly unwrapped optional suffix, given that we +/// are looking at the exclamation mark. +SyntaxParserResult +Parser::parseTypeImplicitlyUnwrappedOptional(TypeRepr *base) { + SourceLoc exclamationLoc = consumeImplicitlyUnwrappedOptionalToken(); + auto TyR = + new (Context) ImplicitlyUnwrappedOptionalTypeRepr(base, exclamationLoc); + llvm::Optional SyntaxNode; + if (SyntaxContext->isEnabled()) { + ParsedImplicitlyUnwrappedOptionalTypeSyntaxBuilder Builder(*SyntaxContext); + auto ExclamationMark = SyntaxContext->popToken(); + auto WrappedType(std::move(*SyntaxContext->popIf())); + Builder + .useExclamationMark(std::move(ExclamationMark)) + .useWrappedType(std::move(WrappedType)); + SyntaxNode.emplace(Builder.build()); + } + return makeSyntaxResult(std::move(SyntaxNode), TyR); } //===----------------------------------------------------------------------===// @@ -1422,10 +1436,6 @@ bool Parser::canParseAsGenericArgumentList() { BacktrackingScope backtrack(*this); if (canParseGenericArguments()) - // The generic-args case is ambiguous with an expression involving '<' - // and '>' operators. The operator expression is favored unless a generic - // argument list can be successfully parsed, and the closing bracket is - // followed by one of dis-ambiguating tokens. return isGenericTypeDisambiguatingToken(*this); return false; @@ -1550,19 +1560,27 @@ bool Parser::canParseTypeIdentifierOrTypeComposition() { } } +bool Parser::canParseSimpleTypeIdentifier() { + // Parse an identifier. + if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + return false; + consumeToken(); + + // Parse an optional generic argument list. + if (startsWithLess(Tok)) + if (!canParseGenericArguments()) + return false; + + return true; +} + bool Parser::canParseTypeIdentifier() { while (true) { - if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) + if (!canParseSimpleTypeIdentifier()) return false; - consumeToken(); - - if (startsWithLess(Tok)) { - if (!canParseGenericArguments()) - return false; - } // Treat 'Foo.' as an attempt to write a dotted type - // unless is 'Type'. + // unless is 'Type' or 'Protocol'. if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && !peekToken().isContextualKeyword("Type") && !peekToken().isContextualKeyword("Protocol")) { @@ -1573,6 +1591,18 @@ bool Parser::canParseTypeIdentifier() { } } +bool Parser::canParseBaseTypeForQualifiedDeclName() { + BacktrackingScope backtrack(*this); + + // Parse a simple type identifier. + if (!canParseSimpleTypeIdentifier()) + return false; + + // Qualified name base types must be followed by a period. + // If the next token starts with a period, return true. + return startsWithSymbol(Tok, '.'); +} + bool Parser::canParseOldStyleProtocolComposition() { consumeToken(tok::kw_protocol); diff --git a/lib/Parse/ParsedRawSyntaxNode.cpp b/lib/Parse/ParsedRawSyntaxNode.cpp index 778e3316943c0..104057efbc4ca 100644 --- a/lib/Parse/ParsedRawSyntaxNode.cpp +++ b/lib/Parse/ParsedRawSyntaxNode.cpp @@ -28,6 +28,9 @@ ParsedRawSyntaxNode::makeDeferred(SyntaxKind k, ParsedRawSyntaxNode *newPtr = ctx.getScratchAlloc().Allocate(deferredNodes.size()); +#ifndef NDEBUG + ParsedRawSyntaxRecorder::verifyElementRanges(deferredNodes); +#endif auto ptr = newPtr; for (auto &node : deferredNodes) { // Cached range. diff --git a/lib/Parse/ParsedRawSyntaxRecorder.cpp b/lib/Parse/ParsedRawSyntaxRecorder.cpp index 51b6677c9044f..2689c4621eaac 100644 --- a/lib/Parse/ParsedRawSyntaxRecorder.cpp +++ b/lib/Parse/ParsedRawSyntaxRecorder.cpp @@ -75,6 +75,9 @@ getRecordedNode(ParsedRawSyntaxNode node, ParsedRawSyntaxRecorder &rec) { ParsedRawSyntaxNode ParsedRawSyntaxRecorder::recordRawSyntax(SyntaxKind kind, MutableArrayRef elements) { +#ifndef NDEBUG + ParsedRawSyntaxRecorder::verifyElementRanges(elements); +#endif CharSourceRange range; SmallVector subnodes; if (!elements.empty()) { diff --git a/lib/Parse/ParsedSyntaxBuilders.cpp.gyb b/lib/Parse/ParsedSyntaxBuilders.cpp.gyb index da17313b2c7ec..9ab3964f2b2cb 100644 --- a/lib/Parse/ParsedSyntaxBuilders.cpp.gyb +++ b/lib/Parse/ParsedSyntaxBuilders.cpp.gyb @@ -26,7 +26,7 @@ using namespace swift; using namespace swift::syntax; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_buildable(): % for child in node.children: % child_elt = None @@ -88,8 +88,6 @@ void Parsed${node.name}Builder::finishLayout(bool deferred) { % child_node = NODE_MAP.get(child.syntax_kind) % if child_node and child_node.is_syntax_collection(): % child_elt = child_node.collection_element_name -% child_elt_type = child_node.collection_element_type -% child_elt_name = child.name + 'Member' % if child_elt: if (!${child_elt_name}s.empty()) { if (deferred) { @@ -122,10 +120,6 @@ void Parsed${node.name}Builder::finishLayout(bool deferred) { } % end % end - -#ifndef NDEBUG - ParsedRawSyntaxRecorder::verifyElementRanges(Layout); -#endif % end } diff --git a/lib/Parse/ParsedSyntaxNodes.cpp.gyb b/lib/Parse/ParsedSyntaxNodes.cpp.gyb index f45d87d62ba39..e3bb56c91812e 100644 --- a/lib/Parse/ParsedSyntaxNodes.cpp.gyb +++ b/lib/Parse/ParsedSyntaxNodes.cpp.gyb @@ -23,7 +23,7 @@ using namespace swift; using namespace swift::syntax; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % for child in node.children: % if child.is_optional: Optional diff --git a/lib/Parse/ParsedSyntaxRecorder.cpp.gyb b/lib/Parse/ParsedSyntaxRecorder.cpp.gyb index 3ffcb36e1bdf9..f33aeb68e1291 100644 --- a/lib/Parse/ParsedSyntaxRecorder.cpp.gyb +++ b/lib/Parse/ParsedSyntaxRecorder.cpp.gyb @@ -29,7 +29,7 @@ bool ParsedSyntaxRecorder::formExactLayoutFor(syntax::SyntaxKind Kind, MutableArrayRef Elements, function_ref)> receiver) { switch (Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: case SyntaxKind::${node.syntax_kind}: { % if node.children: % child_count = len(node.children) @@ -77,7 +77,7 @@ bool ParsedSyntaxRecorder::formExactLayoutFor(syntax::SyntaxKind Kind, } } -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.children: % child_params = [] % child_move_args = [] @@ -114,9 +114,6 @@ ParsedSyntaxRecorder::make${node.syntax_kind}(${child_params}, % end % end }; - #ifndef NDEBUG - ParsedRawSyntaxRecorder::verifyElementRanges(layout); - #endif if (SPCtx.shouldDefer()) return defer${node.syntax_kind}(layout, SPCtx); return record${node.syntax_kind}(layout, SPCtx.getRecorder()); @@ -149,9 +146,6 @@ ParsedSyntaxRecorder::make${node.syntax_kind}( for (auto &element : elements) { layout.push_back(element.takeRaw()); } - #ifndef NDEBUG - ParsedRawSyntaxRecorder::verifyElementRanges(layout); - #endif if (SPCtx.shouldDefer()) return defer${node.syntax_kind}(layout, SPCtx); return record${node.syntax_kind}(layout, SPCtx.getRecorder()); @@ -169,56 +163,9 @@ ParsedSyntaxRecorder::makeBlank${node.syntax_kind}(SourceLoc loc, } return Parsed${node.name}(std::move(raw)); } -% elif node.is_unknown(): -Parsed${node.name} -ParsedSyntaxRecorder::record${node.syntax_kind}( - MutableArrayRef layout, - ParsedRawSyntaxRecorder &rec) { - auto raw = rec.recordRawSyntax(SyntaxKind::${node.syntax_kind}, layout); - return Parsed${node.name}(std::move(raw)); -} - -Parsed${node.name} -ParsedSyntaxRecorder::defer${node.syntax_kind}( - MutableArrayRef layout, - SyntaxParsingContext &SPCtx) { - auto raw = ParsedRawSyntaxNode::makeDeferred(SyntaxKind::${node.syntax_kind}, layout, SPCtx); - return Parsed${node.name}(std::move(raw)); -} - -Parsed${node.name} -ParsedSyntaxRecorder::make${node.syntax_kind}( - MutableArrayRef elements, - SyntaxParsingContext &SPCtx) { - SmallVector layout; - layout.reserve(elements.size()); - for (auto &element : elements) { - layout.push_back(element.takeRaw()); - } - #ifndef NDEBUG - ParsedRawSyntaxRecorder::verifyElementRanges(layout); - #endif - if (SPCtx.shouldDefer()) - return defer${node.syntax_kind}(layout, SPCtx); - return record${node.syntax_kind}(layout, SPCtx.getRecorder()); -} % end % end -ParsedTokenSyntax -ParsedSyntaxRecorder::makeToken(const Token &Tok, - const ParsedTrivia &LeadingTrivia, - const ParsedTrivia &TrailingTrivia, - SyntaxParsingContext &SPCtx) { - ParsedRawSyntaxNode raw; - if (SPCtx.shouldDefer()) { - raw = ParsedRawSyntaxNode::makeDeferred(Tok, LeadingTrivia, TrailingTrivia, SPCtx); - } else { - raw = SPCtx.getRecorder().recordToken(Tok, LeadingTrivia, TrailingTrivia); - } - return ParsedTokenSyntax(std::move(raw)); -} - ParsedTupleTypeElementSyntax ParsedSyntaxRecorder::makeTupleTypeElement(ParsedTypeSyntax Type, llvm::Optional TrailingComma, diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index d744bb645f907..8ac9ec0c6ffa0 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -26,17 +26,14 @@ #include "swift/Basic/Timer.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/CodeCompletionCallbacks.h" -#include "swift/Parse/ParsedSyntaxNodes.h" -#include "swift/Parse/ParsedSyntaxRecorder.h" #include "swift/Parse/ParseSILSupport.h" #include "swift/Parse/SyntaxParseActions.h" #include "swift/Parse/SyntaxParsingContext.h" -#include "swift/Parse/HiddenLibSyntaxAction.h" #include "swift/Syntax/RawSyntax.h" #include "swift/Syntax/TokenSyntax.h" -#include "swift/SyntaxParse/SyntaxTreeCreator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/ADT/PointerUnion.h" @@ -114,88 +111,113 @@ using namespace swift::syntax; void SILParserTUStateBase::anchor() { } -namespace { -/// A visitor that does delayed parsing of function bodies. -class ParseDelayedFunctionBodies : public ASTWalker { - PersistentParserState &ParserState; - CodeCompletionCallbacksFactory *CodeCompletionFactory; +void swift::performCodeCompletionSecondPass( + PersistentParserState &ParserState, + CodeCompletionCallbacksFactory &Factory) { + if (!ParserState.hasCodeCompletionDelayedDeclState()) + return; -public: - ParseDelayedFunctionBodies(PersistentParserState &ParserState, - CodeCompletionCallbacksFactory *Factory) - : ParserState(ParserState), CodeCompletionFactory(Factory) {} - - bool walkToDeclPre(Decl *D) override { - if (auto AFD = dyn_cast(D)) { - if (AFD->getBodyKind() != FuncDecl::BodyKind::Unparsed) - return false; - parseFunctionBody(AFD); - return true; - } - return true; - } + auto state = ParserState.takeCodeCompletionDelayedDeclState(); + auto &SF = *state->ParentContext->getParentSourceFile(); + auto &Ctx = SF.getASTContext(); -private: - void parseFunctionBody(AbstractFunctionDecl *AFD) { - // FIXME: This duplicates the evaluation of - // ParseAbstractFunctionBodyRequest, but installs a code completion - // factory. - assert(AFD->getBodyKind() == FuncDecl::BodyKind::Unparsed); - - SourceFile &SF = *AFD->getDeclContext()->getParentSourceFile(); - SourceManager &SourceMgr = SF.getASTContext().SourceMgr; - unsigned BufferID = SourceMgr.findBufferContainingLoc(AFD->getLoc()); - Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr, - /*DelayBodyParsing=*/false); - TheParser.SyntaxContext->setDiscard(); - std::unique_ptr CodeCompletion; - if (CodeCompletionFactory) { - CodeCompletion.reset( - CodeCompletionFactory->createCodeCompletionCallbacks(TheParser)); - TheParser.setCodeCompletionCallbacks(CodeCompletion.get()); - } - auto body = TheParser.parseAbstractFunctionBodyDelayed(AFD); - AFD->setBodyParsed(body); + FrontendStatsTracer tracer(Ctx.Stats, "CodeCompletionSecondPass"); - if (CodeCompletion) - CodeCompletion->doneParsing(); - } -}; + auto BufferID = Ctx.SourceMgr.getCodeCompletionBufferID(); + Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); -static void parseDelayedDecl( - PersistentParserState &ParserState, - CodeCompletionCallbacksFactory *CodeCompletionFactory) { - if (!ParserState.hasDelayedDecl()) - return; + std::unique_ptr CodeCompletion( + Factory.createCodeCompletionCallbacks(TheParser)); + TheParser.setCodeCompletionCallbacks(CodeCompletion.get()); - SourceFile &SF = *ParserState.getDelayedDeclContext()->getParentSourceFile(); - SourceManager &SourceMgr = SF.getASTContext().SourceMgr; - unsigned BufferID = - SourceMgr.findBufferContainingLoc(ParserState.getDelayedDeclLoc()); - Parser TheParser(BufferID, SF, nullptr, &ParserState, nullptr); - TheParser.SyntaxContext->setDiscard(); - std::unique_ptr CodeCompletion; - if (CodeCompletionFactory) { - CodeCompletion.reset( - CodeCompletionFactory->createCodeCompletionCallbacks(TheParser)); - TheParser.setCodeCompletionCallbacks(CodeCompletion.get()); + TheParser.performCodeCompletionSecondPassImpl(*state); +} + +void Parser::performCodeCompletionSecondPassImpl( + CodeCompletionDelayedDeclState &info) { + // Disable libSyntax creation in the delayed parsing. + SyntaxContext->disable(); + + // Disable updating the interface hash + llvm::SaveAndRestore> CurrentTokenHashSaver( + CurrentTokenHash, nullptr); + + auto BufferID = L->getBufferID(); + auto startLoc = SourceMgr.getLocForOffset(BufferID, info.StartOffset); + SourceLoc prevLoc; + if (info.PrevOffset != ~0U) + prevLoc = SourceMgr.getLocForOffset(BufferID, info.PrevOffset); + // Set the parser position to the start of the delayed decl or the body. + restoreParserPosition(getParserPosition({startLoc, prevLoc})); + + // Do not delay parsing in the second pass. + llvm::SaveAndRestore DisableDelayedBody(DelayBodyParsing, false); + + // Re-enter the lexical scope. + Scope S(this, info.takeScope()); + + DeclContext *DC = info.ParentContext; + + switch (info.Kind) { + case CodeCompletionDelayedDeclKind::TopLevelCodeDecl: { + // Re-enter the top-level code decl context. + // FIXME: this can issue discriminators out-of-order? + auto *TLCD = cast(DC); + ContextChange CC(*this, TLCD, &State->getTopLevelContext()); + + SourceLoc StartLoc = Tok.getLoc(); + ASTNode Result; + parseExprOrStmt(Result); + if (!Result.isNull()) { + auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc()); + TLCD->setBody(Brace); + } + break; } - switch (ParserState.getDelayedDeclKind()) { - case PersistentParserState::DelayedDeclKind::TopLevelCodeDecl: - TheParser.parseTopLevelCodeDeclDelayed(); + case CodeCompletionDelayedDeclKind::Decl: { + assert((DC->isTypeContext() || DC->isModuleScopeContext()) && + "Delayed decl must be a type member or a top-level decl"); + ContextChange CC(*this, DC); + + parseDecl(ParseDeclOptions(info.Flags), + /*IsAtStartOfLineOrPreviousHadSemi=*/true, [&](Decl *D) { + if (auto *NTD = dyn_cast(DC)) { + NTD->addMember(D); + } else if (auto *ED = dyn_cast(DC)) { + ED->addMember(D); + } else if (auto *SF = dyn_cast(DC)) { + SF->addTopLevelDecl(D); + } else { + llvm_unreachable("invalid decl context kind"); + } + }); break; + } + + case CodeCompletionDelayedDeclKind::FunctionBody: { + auto *AFD = cast(DC); + + if (auto *P = AFD->getImplicitSelfDecl()) + addToScope(P); + addParametersToScope(AFD->getParameters()); + + ParseFunctionBody CC(*this, AFD); + setLocalDiscriminatorToParamList(AFD->getParameters()); - case PersistentParserState::DelayedDeclKind::Decl: - TheParser.parseDeclDelayed(); + auto result = parseBraceItemList(diag::func_decl_without_brace); + AFD->setBody(result.getPtrOrNull()); break; } + } - if (CodeCompletion) - CodeCompletion->doneParsing(); -} -} // unnamed namespace + assert(!State->hasCodeCompletionDelayedDeclState() && + "Second pass should not set any code completion info"); + CodeCompletion->doneParsing(); + + State->restoreCodeCompletionDelayedDeclState(info); +} swift::Parser::BacktrackingScope::~BacktrackingScope() { if (Backtrack) { @@ -204,18 +226,6 @@ swift::Parser::BacktrackingScope::~BacktrackingScope() { } } -void swift::performDelayedParsing( - DeclContext *DC, PersistentParserState &PersistentState, - CodeCompletionCallbacksFactory *CodeCompletionFactory) { - SharedTimer timer("Parsing"); - ParseDelayedFunctionBodies Walker(PersistentState, - CodeCompletionFactory); - DC->walkContext(Walker); - - if (CodeCompletionFactory) - parseDelayedDecl(PersistentState, CodeCompletionFactory); -} - /// Tokenizes a string literal, taking into account string interpolation. static void getStringPartTokens(const Token &Tok, const LangOptions &LangOpts, const SourceManager &SM, @@ -384,7 +394,9 @@ Parser::Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags, SF.getASTContext().LangOpts.AttachCommentsToDecls ? CommentRetentionMode::AttachToNextToken : CommentRetentionMode::None, - TriviaRetentionMode::WithTrivia)), + SF.shouldBuildSyntaxTree() + ? TriviaRetentionMode::WithTrivia + : TriviaRetentionMode::WithoutTrivia)), SF, SIL, PersistentState, std::move(SPActions), DelayBodyParsing) {} namespace { @@ -430,11 +442,11 @@ class TokenRecorder: public ConsumeTokenReceiver { } public: - TokenRecorder(SourceFile &SF): + TokenRecorder(SourceFile &SF, unsigned BufferID): Ctx(SF.getASTContext()), SM(SF.getASTContext().SourceMgr), Bag(SF.getTokenVector()), - BufferID(SF.getBufferID().getValue()) {}; + BufferID(BufferID) {}; void finalize() override { @@ -514,20 +526,14 @@ Parser::Parser(std::unique_ptr Lex, SourceFile &SF, SIL(SIL), CurDeclContext(&SF), Context(SF.getASTContext()), + CurrentTokenHash(SF.getInterfaceHashPtr()), DelayBodyParsing(DelayBodyParsing), TokReceiver(SF.shouldCollectToken() ? - new TokenRecorder(SF) : + new TokenRecorder(SF, L->getBufferID()) : new ConsumeTokenReceiver()), - SyntaxContext(new SyntaxParsingContext( - SyntaxContext, SF, L->getBufferID(), - std::make_shared( - SPActions, - std::make_shared( - SF.getASTContext().SourceMgr, - L->getBufferID(), - SF.SyntaxParsingCache, - SF.getASTContext().getSyntaxArena())))), - Generator(SF.getASTContext(), *this) { + SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, + L->getBufferID(), + std::move(SPActions))) { State = PersistentState; if (!State) { OwnedState.reset(new PersistentParserState()); @@ -564,14 +570,23 @@ SourceLoc Parser::consumeTokenWithoutFeedingReceiver() { SourceLoc Loc = Tok.getLoc(); assert(Tok.isNot(tok::eof) && "Lexing past eof!"); - if (IsParsingInterfaceTokens && !Tok.getText().empty()) { - SF.recordInterfaceToken(Tok.getText()); - } + recordTokenHash(Tok); + L->lex(Tok, LeadingTrivia, TrailingTrivia); PreviousLoc = Loc; return Loc; } +void Parser::recordTokenHash(StringRef token) { + assert(!token.empty()); + if (llvm::MD5 *cth = CurrentTokenHash.getPtrOrNull()) { + cth->update(token); + // Add null byte to separate tokens. + uint8_t a[1] = {0}; + cth->update(a); + } +} + void Parser::consumeExtraToken(Token Extra) { TokReceiver->receive(Extra); } @@ -582,43 +597,10 @@ SourceLoc Parser::consumeToken() { return consumeTokenWithoutFeedingReceiver(); } -ParsedTokenSyntax Parser::consumeTokenSyntax() { - TokReceiver->receive(Tok); - ParsedTokenSyntax ParsedToken = ParsedSyntaxRecorder::makeToken( - Tok, LeadingTrivia, TrailingTrivia, *SyntaxContext); - consumeTokenWithoutFeedingReceiver(); - return ParsedToken; -} - SourceLoc Parser::getEndOfPreviousLoc() const { return Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc); } -ParsedTokenSyntax -Parser::consumeStartingCharacterOfCurrentTokenSyntax(tok Kind, size_t Len) { - // Consumes prefix of token and returns its location. - // (like '?', '<', '>' or '!' immediately followed by '<') - assert(Len >= 1); - - // Current token can be either one-character token we want to consume... - if (Tok.getLength() == Len) { - Tok.setKind(Kind); - return consumeTokenSyntax(); - } - - auto Loc = Tok.getLoc(); - - // ... or a multi-character token with the first N characters being the one - // that we want to consume as a separate token. - assert(Tok.getLength() > Len); - auto Token = markSplitTokenSyntax(Kind, Tok.getText().substr(0, Len)); - - auto NewState = L->getStateForBeginningOfTokenLoc(Loc.getAdvancedLoc(Len)); - restoreParserPosition(ParserPosition(NewState, Loc), - /*enableDiagnostics=*/true); - return Token; -} - SourceLoc Parser::consumeStartingCharacterOfCurrentToken(tok Kind, size_t Len) { // Consumes prefix of token and returns its location. // (like '?', '<', '>' or '!' immediately followed by '<') @@ -643,14 +625,6 @@ SourceLoc Parser::consumeStartingCharacterOfCurrentToken(tok Kind, size_t Len) { return PreviousLoc; } -ParsedTokenSyntax Parser::markSplitTokenSyntax(tok Kind, StringRef Txt) { - SplitTokens.emplace_back(); - SplitTokens.back().setToken(Kind, Txt); - TokReceiver->receive(SplitTokens.back()); - return ParsedSyntaxRecorder::makeToken(SplitTokens.back(), LeadingTrivia, - ParsedTrivia(), *SyntaxContext); -} - void Parser::markSplitToken(tok Kind, StringRef Txt) { SplitTokens.emplace_back(); SplitTokens.back().setToken(Kind, Txt); @@ -659,65 +633,16 @@ void Parser::markSplitToken(tok Kind, StringRef Txt) { TokReceiver->receive(SplitTokens.back()); } -ParsedTokenSyntax Parser::consumeStartingLessSyntax() { - assert(startsWithLess(Tok) && "Token does not start with '<'"); - return consumeStartingCharacterOfCurrentTokenSyntax(tok::l_angle); -} - SourceLoc Parser::consumeStartingLess() { assert(startsWithLess(Tok) && "Token does not start with '<'"); return consumeStartingCharacterOfCurrentToken(tok::l_angle); } -ParsedTokenSyntax Parser::consumeStartingGreaterSyntax() { - assert(startsWithGreater(Tok) && "Token does not start with '>'"); - return consumeStartingCharacterOfCurrentTokenSyntax(tok::r_angle); -} - SourceLoc Parser::consumeStartingGreater() { assert(startsWithGreater(Tok) && "Token does not start with '>'"); return consumeStartingCharacterOfCurrentToken(tok::r_angle); } -void Parser::skipSingleSyntax(SmallVectorImpl &Skipped) { - switch (Tok.getKind()) { - case tok::l_paren: - Skipped.push_back(consumeTokenSyntax()); - skipUntilSyntax(Skipped, tok::r_paren); - if (auto RParen = consumeTokenSyntaxIf(tok::r_paren)) - Skipped.push_back(std::move(*RParen)); - break; - case tok::l_brace: - Skipped.push_back(consumeTokenSyntax()); - skipUntilSyntax(Skipped, tok::r_brace); - if (auto RBrace = consumeTokenSyntaxIf(tok::r_brace)) - Skipped.push_back(std::move(*RBrace)); - break; - case tok::l_square: - Skipped.push_back(consumeTokenSyntax()); - skipUntilSyntax(Skipped, tok::r_square); - if (auto RSquare = consumeTokenSyntaxIf(tok::r_square)) - Skipped.push_back(std::move(*RSquare)); - break; - case tok::pound_if: - case tok::pound_else: - case tok::pound_elseif: - Skipped.push_back(consumeTokenSyntax()); - // skipUntil also implicitly stops at tok::pound_endif. - skipUntilSyntax(Skipped, tok::pound_else, tok::pound_elseif); - - if (Tok.isAny(tok::pound_else, tok::pound_elseif)) - skipSingleSyntax(Skipped); - else if (auto Endif = consumeTokenSyntaxIf(tok::pound_endif)) - Skipped.push_back(std::move(*Endif)); - break; - - default: - Skipped.push_back(consumeTokenSyntax()); - break; - } -} - void Parser::skipSingle() { switch (Tok.getKind()) { case tok::l_paren: @@ -754,68 +679,6 @@ void Parser::skipSingle() { } } -void Parser::ignoreToken() { - assert(!Tok.is(tok::eof) && "Lexing eof token"); - ParsedTriviaList Skipped; - Skipped.reserve(LeadingTrivia.size() + TrailingTrivia.size() + 1 + 2); - std::move(LeadingTrivia.begin(), LeadingTrivia.end(), - std::back_inserter(Skipped)); - Skipped.emplace_back(TriviaKind::GarbageText, Tok.getText().size()); - std::move(TrailingTrivia.begin(), TrailingTrivia.end(), - std::back_inserter(Skipped)); - - TokReceiver->receive(Tok); - L->lex(Tok, LeadingTrivia, TrailingTrivia); - - std::move(LeadingTrivia.begin(), LeadingTrivia.end(), - std::back_inserter(Skipped)); - LeadingTrivia = {std::move(Skipped)}; -} - -void Parser::ignoreSingle() { - switch (Tok.getKind()) { - case tok::l_paren: - ignoreToken(); - ignoreUntil(tok::r_paren); - ignoreIf(tok::r_paren); - break; - case tok::l_brace: - ignoreToken(); - ignoreUntil(tok::r_brace); - ignoreIf(tok::r_brace); - break; - case tok::l_square: - ignoreToken(); - ignoreUntil(tok::r_square); - ignoreIf(tok::r_square); - break; - case tok::pound_if: - case tok::pound_else: - case tok::pound_elseif: - ignoreToken(); - ignoreUntil(tok::pound_endif); - ignoreIf(tok::pound_endif); - break; - default: - ignoreToken(); - break; - } -} - -void Parser::ignoreUntil(tok Kind) { - while (Tok.isNot(Kind, tok::eof, tok::pound_endif, tok::code_complete)) - ignoreSingle(); -} - -void Parser::skipUntilSyntax(llvm::SmallVectorImpl &Skipped, - tok T1, tok T2) { - // tok::NUM_TOKENS is a sentinel that means "don't skip". - if (T1 == tok::NUM_TOKENS && T2 == tok::NUM_TOKENS) return; - - while (Tok.isNot(T1, T2, tok::eof, tok::pound_endif, tok::code_complete)) - skipSingleSyntax(Skipped); -} - void Parser::skipUntil(tok T1, tok T2) { // tok::NUM_TOKENS is a sentinel that means "don't skip". if (T1 == tok::NUM_TOKENS && T2 == tok::NUM_TOKENS) return; @@ -830,72 +693,6 @@ void Parser::skipUntilAnyOperator() { skipSingle(); } -bool Parser::ignoreUntilGreaterInTypeList() { - while (true) { - switch (Tok.getKind()) { - case tok::eof: - case tok::l_brace: - case tok::r_brace: - case tok::code_complete: - return false; - -#define KEYWORD(X) case tok::kw_##X: -#define POUND_KEYWORD(X) case tok::pound_##X: -#include "swift/Syntax/TokenKinds.def" - if (isStartOfStmt() || isStartOfDecl() || Tok.is(tok::pound_endif)) - return false; - break; - default: - if (startsWithGreater(Tok)) - return true; - break; - } - ignoreSingle(); - } -} - -void Parser::skipUntilGreaterInTypeListSyntax( - SmallVectorImpl &Skipped, bool protocolComposition) { - while (true) { - switch (Tok.getKind()) { - case tok::eof: - case tok::l_brace: - case tok::r_brace: - case tok::code_complete: - return; - -#define KEYWORD(X) case tok::kw_##X: -#define POUND_KEYWORD(X) case tok::pound_##X: -#include "swift/Syntax/TokenKinds.def" - // 'Self' can appear in types, skip it. - if (Tok.is(tok::kw_Self)) - break; - if (isStartOfStmt() || isStartOfDecl() || Tok.is(tok::pound_endif)) - return; - break; - - case tok::l_paren: - case tok::r_paren: - case tok::l_square: - case tok::r_square: - // In generic type parameter list, skip '[' ']' '(' ')', because they - // can appear in types. - if (protocolComposition) - return; - break; - - default: - if (Tok.isAnyOperator() && startsWithGreater(Tok)) { - Skipped.push_back(consumeStartingGreaterSyntax()); - return; - } - - break; - } - skipSingleSyntax(Skipped); - } -} - /// Skip until a token that starts with '>', and consume it if found. /// Applies heuristics that are suitable when trying to find the end of a list /// of generic parameters, generic arguments, or list of types in a protocol @@ -1000,45 +797,6 @@ void Parser::skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2) { } } -void Parser::skipListUntilDeclRBraceSyntax( - SmallVectorImpl &Skipped, SourceLoc startLoc, tok T1, tok T2) { - while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif, - tok::pound_else, tok::pound_elseif)) { - auto Comma = consumeTokenSyntaxIf(tok::comma); - - bool hasDelimiter = Tok.getLoc() == startLoc || Comma; - bool possibleDeclStartsLine = Tok.isAtStartOfLine(); - - if (Comma) - Skipped.push_back(std::move(*Comma)); - - if (isStartOfDecl()) { - // Could have encountered something like `_ var:` - // or `let foo:` or `var:` - if (Tok.isAny(tok::kw_var, tok::kw_let)) { - if (possibleDeclStartsLine && !hasDelimiter) { - break; - } - - Parser::BacktrackingScope backtrack(*this); - // Consume the let or var - auto LetOrVar = consumeTokenSyntax(); - Skipped.push_back(std::move(LetOrVar)); - - // If the following token is either or :, it means that - // this `var` or `let` should be interpreted as a label - if ((Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) || - peekToken().is(tok::colon)) { - backtrack.cancelBacktrack(); - continue; - } - } - break; - } - skipSingleSyntax(Skipped); - } -} - void Parser::skipUntilDeclRBrace(tok T1, tok T2) { while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif, tok::pound_else, tok::pound_elseif) && @@ -1133,19 +891,6 @@ bool Parser::StructureMarkerRAII::pushStructureMarker( // Primitive Parsing //===----------------------------------------------------------------------===// -Optional Parser::parseIdentifierSyntax(const Diagnostic &D) { - switch (Tok.getKind()) { - case tok::kw_self: - case tok::kw_Self: - case tok::identifier: - return consumeIdentifierSyntax(); - default: - checkForInputIncomplete(); - diagnose(Tok, D); - return None; - } -} - bool Parser::parseIdentifier(Identifier &Result, SourceLoc &Loc, const Diagnostic &D) { switch (Tok.getKind()) { @@ -1243,38 +988,16 @@ bool Parser::parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag, return false; } -Optional Parser::parseTokenSyntax(tok K, SourceLoc &TokLoc, - const Diagnostic &D) { - if (Tok.is(K)) { - TokLoc = Tok.getLoc(); - return consumeTokenSyntax(); - } - - checkForInputIncomplete(); - diagnose(Tok, D); - return None; -} - -ParsedSyntaxResult -Parser::parseMatchingTokenSyntax(tok K, Diag<> ErrorDiag, SourceLoc OtherLoc, - bool silenceDiag) { - if (Tok.is(K)) - return makeParsedResult(consumeTokenSyntax(K)); - checkForInputIncomplete(); - - if (!silenceDiag) { - diagnose(Tok, ErrorDiag); - - Diag<> OtherNote; - switch (K) { - case tok::r_paren: OtherNote = diag::opening_paren; break; - case tok::r_square: OtherNote = diag::opening_bracket; break; - case tok::r_brace: OtherNote = diag::opening_brace; break; - default: llvm_unreachable("unknown matching token!"); - } - diagnose(OtherLoc, OtherNote); +bool Parser::parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, + const Diagnostic &D) { + auto IntTok = Tok; + if (parseToken(tok::integer_literal, Loc, D)) + return true; + if (IntTok.getText().getAsInteger(0, Result)) { + diagnose(IntTok.getLoc(), D); + return true; } - return makeParserError(); + return false; } SourceLoc Parser::getLocForMissingMatchingToken() const { @@ -1346,7 +1069,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, while (true) { while (Tok.is(tok::comma)) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(Tok.getLoc())); + .fixItRemove(Tok.getLoc()); consumeToken(); } SourceLoc StartLoc = Tok.getLoc(); @@ -1376,7 +1099,7 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, continue; if (!AllowSepAfterLast) { diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(SourceRange(PreviousLoc)); + .fixItRemove(PreviousLoc); } break; } @@ -1445,6 +1168,7 @@ Parser::getStringLiteralIfNotInterpolated(SourceLoc Loc, struct ParserUnit::Implementation { std::shared_ptr SPActions; LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; DiagnosticEngine Diags; ASTContext &Ctx; @@ -1452,19 +1176,16 @@ struct ParserUnit::Implementation { std::unique_ptr TheParser; Implementation(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &Opts, StringRef ModuleName, + const LangOptions &Opts, const TypeCheckerOptions &TyOpts, + StringRef ModuleName, std::shared_ptr spActions) - : SPActions(std::move(spActions)), - LangOpts(Opts), - Diags(SM), - Ctx(*ASTContext::get(LangOpts, SearchPathOpts, SM, Diags)), - SF(new (Ctx) SourceFile( - *ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx), - SFKind, BufferID, - SourceFile::ImplicitModuleImportKind::None, - Opts.CollectParsedToken, - Opts.BuildSyntaxTree)) { - } + : SPActions(std::move(spActions)), + LangOpts(Opts), TypeCheckerOpts(TyOpts), Diags(SM), + Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, SM, Diags)), + SF(new (Ctx) SourceFile( + *ModuleDecl::create(Ctx.getIdentifier(ModuleName), Ctx), SFKind, + BufferID, SourceFile::ImplicitModuleImportKind::None, + Opts.CollectParsedToken, Opts.BuildSyntaxTree)) {} ~Implementation() { // We need to delete the parser before the context so that it can finalize @@ -1475,15 +1196,18 @@ struct ParserUnit::Implementation { }; ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID) - : ParserUnit(SM, SFKind, BufferID, LangOptions(), "input") { + : ParserUnit(SM, SFKind, BufferID, + LangOptions(), TypeCheckerOptions(), "input") { } ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, - const LangOptions &LangOpts, StringRef ModuleName, + const LangOptions &LangOpts, + const TypeCheckerOptions &TypeCheckOpts, + StringRef ModuleName, std::shared_ptr spActions, SyntaxParsingCache *SyntaxCache) - : Impl(*new Implementation(SM, SFKind, BufferID, LangOpts, ModuleName, - std::move(spActions))) { + : Impl(*new Implementation(SM, SFKind, BufferID, LangOpts, TypeCheckOpts, + ModuleName, std::move(spActions))) { Impl.SF->SyntaxParsingCache = SyntaxCache; Impl.TheParser.reset(new Parser(BufferID, *Impl.SF, /*SIL=*/nullptr, @@ -1493,8 +1217,8 @@ ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned Buffer ParserUnit::ParserUnit(SourceManager &SM, SourceFileKind SFKind, unsigned BufferID, unsigned Offset, unsigned EndOffset) - : Impl(*new Implementation(SM, SFKind, BufferID, LangOptions(), "input", - nullptr)) { + : Impl(*new Implementation(SM, SFKind, BufferID, LangOptions(), + TypeCheckerOptions(), "input", nullptr)) { std::unique_ptr Lex; Lex.reset(new Lexer(Impl.LangOpts, SM, @@ -1649,8 +1373,12 @@ ParsedDeclName swift::parseDeclName(StringRef name) { } DeclName ParsedDeclName::formDeclName(ASTContext &ctx) const { - return swift::formDeclName(ctx, BaseName, ArgumentLabels, IsFunctionName, - /*IsInitializer=*/true); + return formDeclNameRef(ctx).getFullName(); +} + +DeclNameRef ParsedDeclName::formDeclNameRef(ASTContext &ctx) const { + return swift::formDeclNameRef(ctx, BaseName, ArgumentLabels, IsFunctionName, + /*IsInitializer=*/true); } DeclName swift::formDeclName(ASTContext &ctx, @@ -1658,11 +1386,20 @@ DeclName swift::formDeclName(ASTContext &ctx, ArrayRef argumentLabels, bool isFunctionName, bool isInitializer) { + return formDeclNameRef(ctx, baseName, argumentLabels, isFunctionName, + isInitializer).getFullName(); +} + +DeclNameRef swift::formDeclNameRef(ASTContext &ctx, + StringRef baseName, + ArrayRef argumentLabels, + bool isFunctionName, + bool isInitializer) { // We cannot import when the base name is not an identifier. if (baseName.empty()) - return DeclName(); + return DeclNameRef(); if (!Lexer::isIdentifier(baseName) && !Lexer::isOperator(baseName)) - return DeclName(); + return DeclNameRef(); // Get the identifier for the base name. Special-case `init`. DeclBaseName baseNameId = ((isInitializer && baseName == "init") @@ -1670,7 +1407,7 @@ DeclName swift::formDeclName(ASTContext &ctx, : ctx.getIdentifier(baseName)); // For non-functions, just use the base name. - if (!isFunctionName) return baseNameId; + if (!isFunctionName) return DeclNameRef(baseNameId); // For functions, we need to form a complete name. @@ -1686,7 +1423,7 @@ DeclName swift::formDeclName(ASTContext &ctx, } // Build the result. - return DeclName(ctx, baseNameId, argumentLabelIds); + return DeclNameRef({ ctx, baseNameId, argumentLabelIds }); } DeclName swift::parseDeclName(ASTContext &ctx, StringRef name) { diff --git a/lib/Parse/PersistentParserState.cpp b/lib/Parse/PersistentParserState.cpp index 89d700847d2b6..4e4d15e4dd072 100644 --- a/lib/Parse/PersistentParserState.cpp +++ b/lib/Parse/PersistentParserState.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/Basic/SourceManager.h" #include "swift/Parse/PersistentParserState.h" using namespace swift; @@ -25,30 +26,27 @@ PersistentParserState::PersistentParserState() { } PersistentParserState::~PersistentParserState() { } -void PersistentParserState::delayDecl(DelayedDeclKind Kind, - unsigned Flags, - DeclContext *ParentContext, - SourceRange BodyRange, - SourceLoc PreviousLoc) { - assert(!CodeCompletionDelayedDeclState.get() && +void PersistentParserState::setCodeCompletionDelayedDeclState( + SourceManager &SM, unsigned BufferID, CodeCompletionDelayedDeclKind Kind, + unsigned Flags, DeclContext *ParentContext, SourceRange BodyRange, + SourceLoc PreviousLoc) { + assert(!CodeCompletionDelayedDeclStat.get() && "only one decl can be delayed for code completion"); - CodeCompletionDelayedDeclState.reset(new DelayedDeclState( - Kind, Flags, ParentContext, BodyRange, PreviousLoc, - ScopeInfo.saveCurrentScope())); + unsigned startOffset = SM.getLocOffsetInBuffer(BodyRange.Start, BufferID); + unsigned endOffset = SM.getLocOffsetInBuffer(BodyRange.End, BufferID); + unsigned prevOffset = ~0U; + if (PreviousLoc.isValid()) + prevOffset = SM.getLocOffsetInBuffer(PreviousLoc, BufferID); + + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( + Kind, Flags, ParentContext, ScopeInfo.saveCurrentScope(), startOffset, + endOffset, prevOffset)); } -void PersistentParserState::delayDeclList(IterableDeclContext *D) { - DelayedDeclLists.push_back(D); -} - -void PersistentParserState::parseAllDelayedDeclLists() { - for (auto IDC : DelayedDeclLists) - IDC->loadAllMembers(); -} - -void PersistentParserState::delayTopLevel(TopLevelCodeDecl *TLCD, - SourceRange BodyRange, - SourceLoc PreviousLoc) { - delayDecl(DelayedDeclKind::TopLevelCodeDecl, 0U, TLCD, BodyRange, - PreviousLoc); +void PersistentParserState::restoreCodeCompletionDelayedDeclState( + const CodeCompletionDelayedDeclState &other) { + CodeCompletionDelayedDeclStat.reset(new CodeCompletionDelayedDeclState( + other.Kind, other.Flags, other.ParentContext, + ScopeInfo.saveCurrentScope(), other.StartOffset, other.EndOffset, + other.PrevOffset)); } diff --git a/lib/Parse/Scope.cpp b/lib/Parse/Scope.cpp index d8ebb202d4b91..0ae1ff976c196 100644 --- a/lib/Parse/Scope.cpp +++ b/lib/Parse/Scope.cpp @@ -50,14 +50,14 @@ static bool isResolvableScope(ScopeKind SK) { } Scope::Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock) - : SI(P->getScopeInfo()), - HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), - PrevScope(SI.CurScope), - PrevResolvableDepth(SI.ResolvableDepth), - Kind(SC), - IsInactiveConfigBlock(isInactiveConfigBlock) { + : Scope(P->getScopeInfo(), SC, isInactiveConfigBlock) {} + +Scope::Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock) + : SI(SI), HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr), + PrevScope(SI.CurScope), PrevResolvableDepth(SI.ResolvableDepth), Kind(SC), + IsInactiveConfigBlock(isInactiveConfigBlock) { assert(PrevScope || Kind == ScopeKind::TopLevel); - + if (SI.CurScope) { Depth = SI.CurScope->Depth + 1; IsInactiveConfigBlock |= SI.CurScope->IsInactiveConfigBlock; diff --git a/lib/Parse/SyntaxParsingContext.cpp b/lib/Parse/SyntaxParsingContext.cpp index 9899ee57737e8..807cbbb14b23b 100644 --- a/lib/Parse/SyntaxParsingContext.cpp +++ b/lib/Parse/SyntaxParsingContext.cpp @@ -19,6 +19,7 @@ #include "swift/AST/SourceFile.h" #include "swift/Basic/Defer.h" #include "swift/Parse/ParsedSyntax.h" +#include "swift/Parse/ParsedRawSyntaxRecorder.h" #include "swift/Parse/ParsedSyntaxRecorder.h" #include "swift/Parse/SyntaxParseActions.h" #include "swift/Parse/SyntaxParsingCache.h" @@ -34,18 +35,21 @@ using RootContextData = SyntaxParsingContext::RootContextData; SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF, unsigned BufferID, - std::shared_ptr SPActions) + std::shared_ptr SPActions) : RootDataOrParent(new RootContextData( SF, SF.getASTContext().Diags, SF.getASTContext().SourceMgr, BufferID, std::move(SPActions))), CtxtHolder(CtxtHolder), RootData(RootDataOrParent.get()), Offset(0), - Mode(AccumulationMode::Root) { + Mode(AccumulationMode::Root), Enabled(SF.shouldBuildSyntaxTree()) { CtxtHolder = this; getStorage().reserve(128); } size_t SyntaxParsingContext::lookupNode(size_t LexerOffset, SourceLoc Loc) { + if (!Enabled) + return 0; + assert(getStorage().size() == Offset && "Cannot do lookup if nodes have already been gathered"); assert(Mode == AccumulationMode::CreateSyntax && @@ -173,6 +177,9 @@ ParsedTokenSyntax SyntaxParsingContext::popToken() { void SyntaxParsingContext::addToken(Token &Tok, const ParsedTrivia &LeadingTrivia, const ParsedTrivia &TrailingTrivia) { + if (!Enabled) + return; + ParsedRawSyntaxNode raw; if (shouldDefer()) raw = ParsedRawSyntaxNode::makeDeferred(Tok, LeadingTrivia, TrailingTrivia, @@ -184,6 +191,8 @@ void SyntaxParsingContext::addToken(Token &Tok, /// Add Syntax to the parts. void SyntaxParsingContext::addSyntax(ParsedSyntax Node) { + if (!Enabled) + return; addRawSyntax(Node.takeRaw()); } @@ -204,6 +213,8 @@ void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind, size_t N, void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind, SyntaxNodeCreationKind nodeCreateK) { assert(isTopOfContextStack()); + if (!Enabled) + return; switch (Kind) { case SyntaxKind::SuperRefExpr: @@ -238,6 +249,8 @@ void SyntaxParsingContext::collectNodesInPlace(SyntaxKind ColletionKind, SyntaxNodeCreationKind nodeCreateK) { assert(isCollectionKind(ColletionKind)); assert(isTopOfContextStack()); + if (!Enabled) + return; auto Parts = getParts(); auto Count = 0; for (auto I = Parts.rbegin(), End = Parts.rend(); I != End; ++I) { @@ -249,15 +262,16 @@ void SyntaxParsingContext::collectNodesInPlace(SyntaxKind ColletionKind, createNodeInPlace(ColletionKind, Count, nodeCreateK); } -ParsedRawSyntaxNode SyntaxParsingContext::finalizeSourceFile() { - ParsedRawSyntaxRecorder &Recorder = getRecorder(); - auto Parts = getParts(); +static ParsedRawSyntaxNode finalizeSourceFile(RootContextData &RootData, + MutableArrayRef Parts) { + ParsedRawSyntaxRecorder &Recorder = RootData.Recorder; ParsedRawSyntaxNode Layout[2]; assert(!Parts.empty() && Parts.back().isToken(tok::eof)); Layout[1] = std::move(Parts.back()); Parts = Parts.drop_back(); + assert(llvm::all_of(Parts, [](const ParsedRawSyntaxNode& node) { return node.getKind() == SyntaxKind::CodeBlockItem; }) && "all top level element must be 'CodeBlockItem'"); @@ -269,23 +283,28 @@ ParsedRawSyntaxNode SyntaxParsingContext::finalizeSourceFile() { } OpaqueSyntaxNode SyntaxParsingContext::finalizeRoot() { + if (!Enabled) + return nullptr; assert(isTopOfContextStack() && "some sub-contexts are not destructed"); assert(isRoot() && "only root context can finalize the tree"); assert(Mode == AccumulationMode::Root); - if (getStorage().empty()) { + auto parts = getParts(); + if (parts.empty()) { return nullptr; // already finalized. } - ParsedRawSyntaxNode root = finalizeSourceFile(); - auto opaqueRoot = getSyntaxCreator().finalizeNode(root.takeOpaqueNode()); + ParsedRawSyntaxNode root = finalizeSourceFile(*getRootData(), parts); // Clear the parts because we will call this function again when destroying // the root context. getStorage().clear(); - return opaqueRoot; + return root.takeOpaqueNode(); } void SyntaxParsingContext::synthesize(tok Kind, SourceLoc Loc) { + if (!Enabled) + return; + ParsedRawSyntaxNode raw; if (shouldDefer()) raw = ParsedRawSyntaxNode::makeDeferredMissing(Kind, Loc); @@ -316,6 +335,9 @@ SyntaxParsingContext::~SyntaxParsingContext() { delete RootDataOrParent.get(); }; + if (!Enabled) + return; + auto &Storage = getStorage(); switch (Mode) { @@ -371,6 +393,7 @@ SyntaxParsingContext::~SyntaxParsingContext() { // Never. case AccumulationMode::NotSet: - llvm_unreachable("Accumulation mode not set."); + assert(!Enabled && "Cleanup mode must be specified before destruction"); + break; } } diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index bd52c9501cc78..35aa6f9a2be75 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -15,6 +15,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" @@ -59,7 +60,7 @@ class SILParserTUState : public SILParserTUStateBase { /// This is all of the forward referenced functions with /// the location for where the reference is. llvm::DenseMap> ForwardRefFns; + Located> ForwardRefFns; /// A list of all functions forward-declared by a sil_scope. llvm::DenseSet PotentialZombieFns; @@ -84,8 +85,8 @@ class SILParserTUState : public SILParserTUStateBase { SILParserTUState::~SILParserTUState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { - if (Entry.second.second.isValid()) { - M.getASTContext().Diags.diagnose(Entry.second.second, + if (Entry.second.Loc.isValid()) { + M.getASTContext().Diags.diagnose(Entry.second.Loc, diag::sil_use_of_undefined_value, Entry.first.str()); } @@ -111,13 +112,13 @@ void PrettyStackTraceParser::print(llvm::raw_ostream &out) const { out << '\n'; } -static bool parseIntoSourceFileImpl(SourceFile &SF, - unsigned BufferID, - bool *Done, - SILParserState *SIL, - PersistentParserState *PersistentState, - bool FullParse, - bool DelayBodyParsing) { +static void parseIntoSourceFileImpl(SourceFile &SF, + unsigned BufferID, + bool *Done, + SILParserState *SIL, + PersistentParserState *PersistentState, + bool FullParse, + bool DelayBodyParsing) { assert((!FullParse || (SF.canBeParsedInFull() && !SIL)) && "cannot parse in full with the given parameters!"); @@ -140,55 +141,44 @@ static bool parseIntoSourceFileImpl(SourceFile &SF, if (SIL) DelayBodyParsing = false; - SharedTimer timer("Parsing"); + FrontendStatsTracer tracer(SF.getASTContext().Stats, "Parsing"); Parser P(BufferID, SF, SIL ? SIL->Impl.get() : nullptr, PersistentState, STreeCreator, DelayBodyParsing); PrettyStackTraceParser StackTrace(P); - llvm::SaveAndRestore S(P.IsParsingInterfaceTokens, - SF.hasInterfaceHash()); + llvm::SaveAndRestore> S(P.CurrentTokenHash, + SF.getInterfaceHashPtr()); - bool FoundSideEffects = false; do { - bool hasSideEffects = P.parseTopLevel(); - FoundSideEffects = FoundSideEffects || hasSideEffects; + P.parseTopLevel(); *Done = P.Tok.is(tok::eof); } while (FullParse && !*Done); - if (!FullParse && !*Done) { - // Only parsed a part of the source file. - // Add artificial EOF to be able to finalize the tree. - P.SyntaxContext->addRawSyntax( - ParsedRawSyntaxNode::makeDeferredMissing(tok::eof, P.Tok.getLoc())); - } - if (STreeCreator) { auto rawNode = P.finalizeSyntaxTree(); STreeCreator->acceptSyntaxRoot(rawNode, SF); } - - return FoundSideEffects; } -bool swift::parseIntoSourceFile(SourceFile &SF, +void swift::parseIntoSourceFile(SourceFile &SF, unsigned BufferID, bool *Done, SILParserState *SIL, PersistentParserState *PersistentState, bool DelayBodyParsing) { - return parseIntoSourceFileImpl(SF, BufferID, Done, SIL, - PersistentState, - /*FullParse=*/SF.shouldBuildSyntaxTree(), - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, Done, SIL, + PersistentState, + /*FullParse=*/SF.shouldBuildSyntaxTree(), + DelayBodyParsing); } -bool swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, +void swift::parseIntoSourceFileFull(SourceFile &SF, unsigned BufferID, PersistentParserState *PersistentState, bool DelayBodyParsing) { bool Done = false; - return parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, - PersistentState, /*FullParse=*/true, - DelayBodyParsing); + parseIntoSourceFileImpl(SF, BufferID, &Done, /*SIL=*/nullptr, + PersistentState, /*FullParse=*/true, + DelayBodyParsing); } @@ -234,7 +224,7 @@ namespace { /// Data structures used to perform name lookup of basic blocks. llvm::DenseMap BlocksByName; llvm::DenseMap> UndefinedBlocks; + Located> UndefinedBlocks; /// Data structures used to perform name lookup for local values. llvm::StringMap LocalValues; @@ -250,11 +240,10 @@ namespace { void convertRequirements(SILFunction *F, ArrayRef From, SmallVectorImpl &To); - Optional - parseProtocolConformanceHelper(ProtocolDecl *&proto, - GenericEnvironment *GenericEnv, - ConformanceContext context, - ProtocolDecl *defaultForProto); + ProtocolConformanceRef parseProtocolConformanceHelper( + ProtocolDecl *&proto, GenericEnvironment *GenericEnv, + ConformanceContext context, ProtocolDecl *defaultForProto); + public: SILParser(Parser &P) : P(P), SILMod(static_cast(P.SIL)->M), @@ -393,7 +382,7 @@ namespace { if (!P.consumeIf(tok::at_sign)) { // If we fail, we must have @any ownership. We check elsewhere in the // parser that this matches what the function signature wants. - OwnershipKind = ValueOwnershipKind::Any; + OwnershipKind = ValueOwnershipKind::None; return false; } @@ -465,6 +454,10 @@ namespace { bool parseScopeRef(SILDebugScope *&DS); bool parseSILDebugLocation(SILLocation &L, SILBuilder &B, bool parsedComma = false); + bool parseSpecificSILInstruction(SILBuilder &B, SILInstructionKind Opcode, + SourceLoc OpcodeLoc, StringRef OpcodeName, + SILInstruction *&ResultVal); + bool parseSILInstruction(SILBuilder &B); bool parseCallInstruction(SILLocation InstLoc, SILInstructionKind Opcode, SILBuilder &B, @@ -484,12 +477,10 @@ namespace { GenericEnvironment *GenericEnv=nullptr, ProtocolDecl *defaultForProto = nullptr); - Optional - parseProtocolConformance(ProtocolDecl *&proto, - GenericEnvironment *&genericEnv, - ConformanceContext context, - ProtocolDecl *defaultForProto); - Optional + ProtocolConformanceRef parseProtocolConformance( + ProtocolDecl *&proto, GenericEnvironment *&genericEnv, + ConformanceContext context, ProtocolDecl *defaultForProto); + ProtocolConformanceRef parseProtocolConformance(ProtocolDecl *defaultForProto, ConformanceContext context) { ProtocolDecl *dummy; @@ -593,8 +584,8 @@ bool SILParser::diagnoseProblems() { if (!UndefinedBlocks.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto Entry : UndefinedBlocks) - P.diagnose(Entry.second.first, diag::sil_undefined_basicblock_use, - Entry.second.second); + P.diagnose(Entry.second.Loc, diag::sil_undefined_basicblock_use, + Entry.second.Item); HadError = true; } @@ -622,13 +613,13 @@ SILFunction *SILParser::getGlobalNameForDefinition(Identifier name, // complete the forward reference. auto iter = TUState.ForwardRefFns.find(name); if (iter != TUState.ForwardRefFns.end()) { - SILFunction *fn = iter->second.first; + SILFunction *fn = iter->second.Item; // Verify that the types match up. if (fn->getLoweredFunctionType() != ty) { P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), ty); - P.diagnose(iter->second.second, diag::sil_prior_reference); + P.diagnose(iter->second.Loc, diag::sil_prior_reference); fn = builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } @@ -726,7 +717,7 @@ SILBasicBlock *SILParser::getBBForReference(Identifier Name, SourceLoc Loc) { // Otherwise, create it and remember that this is a forward reference so // that we can diagnose use without definition problems. BB = F->createBasicBlock(); - UndefinedBlocks[BB] = {Loc, Name}; + UndefinedBlocks[BB] = {Name, Loc}; return BB; } @@ -911,7 +902,7 @@ namespace { bool walkToTypeReprPre(TypeRepr *Ty) override { auto *T = dyn_cast_or_null(Ty); auto Comp = T->getComponentRange().front(); - if (auto Entry = P.lookupInScope(Comp->getIdentifier())) + if (auto Entry = P.lookupInScope(Comp->getNameRef())) if (auto *TD = dyn_cast(Entry)) { Comp->setValue(TD, nullptr); return false; @@ -1191,14 +1182,16 @@ lookupTopDecl(Parser &P, DeclBaseName Name, bool typeLookup) { llvm::SaveAndRestore ASTStage(P.SF.ASTStage, SourceFile::Parsed); - UnqualifiedLookup::Options options; + UnqualifiedLookupOptions options; if (typeLookup) - options |= UnqualifiedLookup::Flags::TypeLookup; - - UnqualifiedLookup DeclLookup(Name, &P.SF, SourceLoc(), options); - assert(DeclLookup.isSuccess() && DeclLookup.Results.size() == 1); - ValueDecl *VD = DeclLookup.Results.back().getValueDecl(); - return VD; + options |= UnqualifiedLookupFlags::TypeLookup; + + auto &ctx = P.SF.getASTContext(); + auto descriptor = UnqualifiedLookupDescriptor(DeclNameRef(Name), &P.SF); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); + assert(lookup.size() == 1); + return lookup.back().getValueDecl(); } /// Find the ValueDecl given an interface type and a member name. @@ -1266,7 +1259,7 @@ bool SILParser::parseSILType(SILType &Result, SILValueCategory category = SILValueCategory::Object; if (P.Tok.isAnyOperator() && P.Tok.getText().startswith("*")) { category = SILValueCategory::Address; - P.consumeStartingCharacterOfCurrentToken(tok::oper_binary_unspaced); + P.consumeStartingCharacterOfCurrentToken(); } // Parse attributes. @@ -1279,7 +1272,8 @@ bool SILParser::parseSILType(SILType &Result, if (IsFuncDecl && !attrs.has(TAK_convention)) { // Use a random location. attrs.setAttr(TAK_convention, P.PreviousLoc); - attrs.convention = "thin"; + attrs.ConventionArguments = + TypeAttributes::Convention::makeSwiftConvention("thin"); } ParserResult TyR = P.parseType(diag::expected_sil_type, @@ -1291,24 +1285,20 @@ bool SILParser::parseSILType(SILType &Result, // Resolve the generic environments for parsed generic function and box types. class HandleSILGenericParamsWalker : public ASTWalker { - ASTContext &C; SourceFile *SF; public: - HandleSILGenericParamsWalker(ASTContext &C, - SourceFile *SF) - : C(C), SF(SF) - {} - + HandleSILGenericParamsWalker(SourceFile *SF) : SF(SF) {} + bool walkToTypeReprPre(TypeRepr *T) override { if (auto fnType = dyn_cast(T)) { if (auto generics = fnType->getGenericParams()) { - auto env = handleSILGenericParams(C, generics, SF); + auto env = handleSILGenericParams(generics, SF); fnType->setGenericEnvironment(env); } } if (auto boxType = dyn_cast(T)) { if (auto generics = boxType->getGenericParams()) { - auto env = handleSILGenericParams(C, generics, SF); + auto env = handleSILGenericParams(generics, SF); boxType->setGenericEnvironment(env); } } @@ -1316,9 +1306,8 @@ bool SILParser::parseSILType(SILType &Result, } }; - TyR.get() - ->walk(HandleSILGenericParamsWalker(P.Context, &P.SF)); - + TyR.get()->walk(HandleSILGenericParamsWalker(&P.SF)); + // Save the top-level function generic environment if there was one. if (auto fnType = dyn_cast(TyR.get())) if (auto env = fnType->getGenericEnvironment()) @@ -1714,7 +1703,7 @@ static void bindProtocolSelfInTypeRepr(TypeLoc &TL, ProtocolDecl *proto) { virtual bool walkToTypeReprPre(TypeRepr *T) override { if (auto ident = dyn_cast(T)) { auto firstComponent = ident->getComponentRange().front(); - if (firstComponent->getIdentifier() == selfId) + if (firstComponent->getNameRef().isSimpleName(selfId)) firstComponent->setValue(selfParam, proto); } @@ -1776,14 +1765,11 @@ static bool getConformancesForSubstitution(Parser &P, for (auto protoTy : protocols) { auto conformance = M->lookupConformance(subReplacement, protoTy->getDecl()); - if (conformance) { - conformances.push_back(*conformance); - continue; + if (conformance.isInvalid()) { + P.diagnose(loc, diag::sil_substitution_mismatch, subReplacement, protoTy); + return true; } - - P.diagnose(loc, diag::sil_substitution_mismatch, subReplacement, - protoTy); - return true; + conformances.push_back(conformance); } return false; @@ -1815,30 +1801,31 @@ SubstitutionMap getApplySubstitutionsFromParsed( bool failed = false; auto subMap = SubstitutionMap::get( - genericSig, - [&](SubstitutableType *type) -> Type { - auto genericParam = dyn_cast(type); - if (!genericParam) return nullptr; - - auto index = genericSig->getGenericParamOrdinal(genericParam); - assert(index < genericSig->getGenericParams().size()); - assert(index < parses.size()); - - // Provide the replacement type. - return parses[index].replacement; - }, - [&](CanType dependentType, Type replacementType, - ProtocolDecl *proto) ->Optional { - auto M = SP.P.SF.getParentModule(); - auto conformance = M->lookupConformance(replacementType, proto); - if (conformance) return conformance; - - SP.P.diagnose(loc, diag::sil_substitution_mismatch, replacementType, - proto->getDeclaredType()); - failed = true; - - return ProtocolConformanceRef(proto); - }); + genericSig, + [&](SubstitutableType *type) -> Type { + auto genericParam = dyn_cast(type); + if (!genericParam) + return nullptr; + + auto index = genericSig->getGenericParamOrdinal(genericParam); + assert(index < genericSig->getGenericParams().size()); + assert(index < parses.size()); + + // Provide the replacement type. + return parses[index].replacement; + }, + [&](CanType dependentType, Type replacementType, + ProtocolDecl *proto) -> ProtocolConformanceRef { + auto M = SP.P.SF.getParentModule(); + if (auto conformance = M->lookupConformance(replacementType, proto)) + return conformance; + + SP.P.diagnose(loc, diag::sil_substitution_mismatch, replacementType, + proto->getDeclaredType()); + failed = true; + + return ProtocolConformanceRef(proto); + }); return failed ? SubstitutionMap() : subMap; } @@ -2051,7 +2038,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Member, bool FnTypeRequired) { if (auto generics = fnType->getGenericParams()) { assert(!Ty.wasValidated() && Ty.getType().isNull()); - genericEnv = handleSILGenericParams(P.Context, generics, &P.SF); + genericEnv = handleSILGenericParams(generics, &P.SF); fnType->setGenericEnvironment(genericEnv); } } @@ -2140,14 +2127,14 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, contextFormalTy = patternEnv->mapTypeIntoContext(formalTy); auto lookup = P.SF.getParentModule()->lookupConformance( contextFormalTy, proto); - if (!lookup) { + if (lookup.isInvalid()) { P.diagnose(formalTyLoc, diag::sil_keypath_index_not_hashable, formalTy); return true; } - auto conformance = ProtocolConformanceRef(*lookup); - + auto conformance = ProtocolConformanceRef(lookup); + indexes.push_back({index, formalTy, loweredTy, conformance}); if (operandTypes.size() <= index) @@ -2374,67 +2361,11 @@ SILParser::parseKeyPathPatternComponent(KeyPathPatternComponent &component, } } -/// sil-instruction-result ::= sil-value-name '=' -/// sil-instruction-result ::= '(' sil-value-name? ')' -/// sil-instruction-result ::= '(' sil-value-name (',' sil-value-name)* ')' -/// sil-instruction-source-info ::= (',' sil-scope-ref)? (',' sil-loc)? -/// sil-instruction-def ::= -/// (sil-instruction-result '=')? sil-instruction sil-instruction-source-info -bool SILParser::parseSILInstruction(SILBuilder &B) { - // We require SIL instructions to be at the start of a line to assist - // recovery. - if (!P.Tok.isAtStartOfLine()) { - P.diagnose(P.Tok, diag::expected_sil_instr_start_of_line); - return true; - } - - SmallVector, 4> resultNames; - SourceLoc resultClauseBegin; - - // If the instruction has a name '%foo =', parse it. - if (P.Tok.is(tok::sil_local_name)) { - resultClauseBegin = P.Tok.getLoc(); - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); - P.consumeToken(tok::sil_local_name); - - // If the instruction has a '(%foo, %bar) = ', parse it. - } else if (P.consumeIf(tok::l_paren)) { - resultClauseBegin = P.PreviousLoc; - - if (!P.consumeIf(tok::r_paren)) { - while (true) { - if (!P.Tok.is(tok::sil_local_name)) { - P.diagnose(P.Tok, diag::expected_sil_value_name); - return true; - } - - resultNames.push_back(std::make_pair(P.Tok.getText(), P.Tok.getLoc())); - P.consumeToken(tok::sil_local_name); - - if (P.consumeIf(tok::comma)) - continue; - if (P.consumeIf(tok::r_paren)) - break; - - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, ","); - return true; - } - } - } - - if (resultClauseBegin.isValid()) { - if (P.parseToken(tok::equal, diag::expected_equal_in_sil_instr)) - return true; - } - - SILInstructionKind Opcode; - SourceLoc OpcodeLoc; - StringRef OpcodeName; - - // Parse the opcode name. - if (parseSILOpcode(Opcode, OpcodeLoc, OpcodeName)) - return true; - +bool SILParser::parseSpecificSILInstruction(SILBuilder &B, + SILInstructionKind Opcode, + SourceLoc OpcodeLoc, + StringRef OpcodeName, + SILInstruction *&ResultVal) { SmallVector OpList; SILValue Val; SILType Ty; @@ -2474,26 +2405,25 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { CanType SourceType, TargetType; SILValue SourceAddr, DestAddr; auto parseSourceAndDestAddress = [&] { - return parseFormalTypeAndValue(SourceType, SourceAddr) - || parseVerbatim("to") - || parseFormalTypeAndValue(TargetType, DestAddr); + return parseFormalTypeAndValue(SourceType, SourceAddr) || + parseVerbatim("to") || parseFormalTypeAndValue(TargetType, DestAddr); }; Identifier SuccessBBName, FailureBBName; SourceLoc SuccessBBLoc, FailureBBLoc; auto parseConditionalBranchDestinations = [&] { - return P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") - || parseSILIdentifier(SuccessBBName, SuccessBBLoc, - diag::expected_sil_block_name) - || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") - || parseSILIdentifier(FailureBBName, FailureBBLoc, - diag::expected_sil_block_name) - || parseSILDebugLocation(InstLoc, B); + return P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(SuccessBBName, SuccessBBLoc, + diag::expected_sil_block_name) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(FailureBBName, FailureBBLoc, + diag::expected_sil_block_name) || + parseSILDebugLocation(InstLoc, B); }; // Validate the opcode name, and do opcode-specific parsing logic based on the // opcode we find. - SILInstruction *ResultVal; + switch (Opcode) { case SILInstructionKind::AllocBoxInst: { bool hasDynamicLifetime = false; @@ -2501,7 +2431,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; SILType Ty; - if (parseSILType(Ty)) return true; + if (parseSILType(Ty)) + return true; SILDebugVariable VarInfo; if (parseSILDebugVar(VarInfo)) return true; @@ -2521,7 +2452,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::AbortApplyInst: case SILInstructionKind::EndApplyInst: { UnresolvedValueName argName; - if (parseValueName(argName)) return true; + if (parseValueName(argName)) + return true; if (parseSILDebugLocation(InstLoc, B)) return true; @@ -2541,7 +2473,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (parseSILType(Ty) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) return true; - + bool Negative = false; if (P.Tok.isAnyOperator() && P.Tok.getText() == "-") { Negative = true; @@ -2551,7 +2483,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "integer"); return true; } - + auto intTy = Ty.getAs(); if (!intTy) { P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); @@ -2578,13 +2510,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (parseSILType(Ty) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) return true; - + // The value is expressed as bits. if (P.Tok.getKind() != tok::integer_literal) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "integer"); return true; } - + auto floatTy = Ty.getAs(); if (!floatTy) { P.diagnose(P.Tok, diag::sil_float_literal_not_float_type); @@ -2592,15 +2524,15 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } StringRef text = prepareIntegerLiteralForParsing(P.Tok.getText()); - + APInt bits(floatTy->getBitWidth(), 0); bool error = text.getAsInteger(0, bits); assert(!error && "float_literal token did not parse as APInt?!"); (void)error; - + if (bits.getBitWidth() != floatTy->getBitWidth()) bits = bits.zextOrTrunc(floatTy->getBitWidth()); - + APFloat value(floatTy->getAPFloatSemantics(), bits); if (parseSILDebugLocation(InstLoc, B)) return true; @@ -2671,8 +2603,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } - StringRef string = P.L->getEncodedStringSegment(segments.front(), - stringBuffer); + StringRef string = + P.L->getEncodedStringSegment(segments.front(), stringBuffer); ResultVal = B.createStringLiteral(InstLoc, string, encoding); break; } @@ -2706,9 +2638,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::AllocValueBufferInst: { SILType Ty; - if (parseSILType(Ty) || - parseVerbatim("in") || - parseTypedValueRef(Val, B) || + if (parseSILType(Ty) || parseVerbatim("in") || parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createAllocValueBuffer(InstLoc, Ty, Val); @@ -2716,9 +2646,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } case SILInstructionKind::ProjectValueBufferInst: { SILType Ty; - if (parseSILType(Ty) || - parseVerbatim("in") || - parseTypedValueRef(Val, B) || + if (parseSILType(Ty) || parseVerbatim("in") || parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createProjectValueBuffer(InstLoc, Ty, Val); @@ -2726,9 +2654,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } case SILInstructionKind::DeallocValueBufferInst: { SILType Ty; - if (parseSILType(Ty) || - parseVerbatim("in") || - parseTypedValueRef(Val, B) || + if (parseSILType(Ty) || parseVerbatim("in") || parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createDeallocValueBuffer(InstLoc, Ty, Val); @@ -2739,12 +2665,12 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (parseTypedValueRef(Val, B) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) return true; - + if (!P.Tok.is(tok::integer_literal)) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "integer"); return true; } - + unsigned Index; bool error = parseIntegerLiteral(P.Tok.getText(), 0, Index); assert(!error && "project_box index did not parse as integer?!"); @@ -2753,34 +2679,30 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.consumeToken(tok::integer_literal); if (parseSILDebugLocation(InstLoc, B)) return true; - + ResultVal = B.createProjectBox(InstLoc, Val, Index); break; } - + case SILInstructionKind::ProjectExistentialBoxInst: { SILType Ty; - if (parseSILType(Ty) || - parseVerbatim("in") || - parseTypedValueRef(Val, B) || + if (parseSILType(Ty) || parseVerbatim("in") || parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createProjectExistentialBox(InstLoc, Ty, Val); break; } - + case SILInstructionKind::FunctionRefInst: { SILFunction *Fn; - if (parseSILFunctionRef(InstLoc, Fn) || - parseSILDebugLocation(InstLoc, B)) + if (parseSILFunctionRef(InstLoc, Fn) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createFunctionRef(InstLoc, Fn); break; } case SILInstructionKind::DynamicFunctionRefInst: { SILFunction *Fn; - if (parseSILFunctionRef(InstLoc, Fn) || - parseSILDebugLocation(InstLoc, B)) + if (parseSILFunctionRef(InstLoc, Fn) || parseSILDebugLocation(InstLoc, B)) return true; // Set a forward reference's dynamic property for the first time. if (!Fn->isDynamicallyReplaceable()) { @@ -2795,40 +2717,38 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } case SILInstructionKind::PreviousDynamicFunctionRefInst: { SILFunction *Fn; - if (parseSILFunctionRef(InstLoc, Fn) || - parseSILDebugLocation(InstLoc, B)) + if (parseSILFunctionRef(InstLoc, Fn) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createPreviousDynamicFunctionRef(InstLoc, Fn); break; } case SILInstructionKind::BuiltinInst: { if (P.Tok.getKind() != tok::string_literal) { - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr,"builtin name"); + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "builtin name"); return true; } StringRef Str = P.Tok.getText(); - Identifier Id = P.Context.getIdentifier(Str.substr(1, Str.size()-2)); + Identifier Id = P.Context.getIdentifier(Str.substr(1, Str.size() - 2)); P.consumeToken(tok::string_literal); - + // Find the builtin in the Builtin module - SmallVector foundBuiltins; - P.Context.TheBuiltinModule->lookupMember(foundBuiltins, - P.Context.TheBuiltinModule, Id, - Identifier()); + SmallVector foundBuiltins; + P.Context.TheBuiltinModule->lookupMember( + foundBuiltins, P.Context.TheBuiltinModule, Id, Identifier()); if (foundBuiltins.empty()) { - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr,"builtin name"); + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "builtin name"); return true; } assert(foundBuiltins.size() == 1 && "ambiguous builtin name?!"); auto *builtinFunc = cast(foundBuiltins[0]); GenericEnvironment *genericEnv = builtinFunc->getGenericEnvironment(); - + SmallVector parsedSubs; SubstitutionMap subMap; if (parseSubstitutions(parsedSubs)) return true; - + if (!parsedSubs.empty()) { if (!genericEnv) { P.diagnose(P.Tok, diag::sil_substitutions_on_non_polymorphic_type); @@ -2838,7 +2758,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (!subMap) return true; } - + if (P.Tok.getKind() != tok::l_paren) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "("); return true; @@ -2861,76 +2781,78 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, ")' or ',"); return true; } - + if (P.Tok.getKind() != tok::colon) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, ":"); return true; } P.consumeToken(tok::colon); - + SILType ResultTy; if (parseSILType(ResultTy)) return true; - + if (parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createBuiltin(InstLoc, Id, ResultTy, subMap, Args); break; } case SILInstructionKind::OpenExistentialAddrInst: - if (parseOpenExistAddrKind() || parseTypedValueRef(Val, B) - || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseOpenExistAddrKind() || parseTypedValueRef(Val, B) || + parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialAddr(InstLoc, Val, Ty, AccessKind); break; case SILInstructionKind::OpenExistentialBoxInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialBox(InstLoc, Val, Ty); break; case SILInstructionKind::OpenExistentialBoxValueInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialBoxValue(InstLoc, Val, Ty); break; case SILInstructionKind::OpenExistentialMetatypeInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialMetatype(InstLoc, Val, Ty); break; case SILInstructionKind::OpenExistentialRefInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialRef(InstLoc, Val, Ty); break; case SILInstructionKind::OpenExistentialValueInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createOpenExistentialValue(InstLoc, Val, Ty); break; -#define UNARY_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: \ - if (parseTypedValueRef(Val, B)) return true; \ - if (parseSILDebugLocation(InstLoc, B)) return true; \ - ResultVal = B.create##ID(InstLoc, Val); \ +#define UNARY_INSTRUCTION(ID) \ + case SILInstructionKind::ID##Inst: \ + if (parseTypedValueRef(Val, B)) \ + return true; \ + if (parseSILDebugLocation(InstLoc, B)) \ + return true; \ + ResultVal = B.create##ID(InstLoc, Val); \ break; #define REFCOUNTING_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: { \ + case SILInstructionKind::ID##Inst: { \ Atomicity atomicity = Atomicity::Atomic; \ StringRef Optional; \ if (parseSILOptional(Optional, *this)) { \ @@ -2970,12 +2892,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { REFCOUNTING_INSTRUCTION(RetainValue) REFCOUNTING_INSTRUCTION(ReleaseValueAddr) REFCOUNTING_INSTRUCTION(RetainValueAddr) -#define UNCHECKED_REF_STORAGE(Name, ...) UNARY_INSTRUCTION(Copy##Name##Value) -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - REFCOUNTING_INSTRUCTION(StrongRetain##Name) \ - REFCOUNTING_INSTRUCTION(Name##Retain) \ - REFCOUNTING_INSTRUCTION(Name##Release) \ - UNARY_INSTRUCTION(Copy##Name##Value) +#define UNCHECKED_REF_STORAGE(Name, ...) \ + UNARY_INSTRUCTION(StrongCopy##Name##Value) +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + REFCOUNTING_INSTRUCTION(StrongRetain##Name) \ + REFCOUNTING_INSTRUCTION(Name##Retain) \ + REFCOUNTING_INSTRUCTION(Name##Release) \ + UNARY_INSTRUCTION(StrongCopy##Name##Value) #include "swift/AST/ReferenceStorage.def" #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION @@ -2984,8 +2907,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { bool IsObjcVerifcationType = false; if (parseSILOptional(IsObjcVerifcationType, *this, "objc")) return true; - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createIsEscapingClosure( InstLoc, Val, @@ -2994,98 +2916,98 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } - case SILInstructionKind::DebugValueInst: - case SILInstructionKind::DebugValueAddrInst: { - SILDebugVariable VarInfo; - if (parseTypedValueRef(Val, B) || - parseSILDebugVar(VarInfo) || - parseSILDebugLocation(InstLoc, B)) - return true; - if (Opcode == SILInstructionKind::DebugValueInst) - ResultVal = B.createDebugValue(InstLoc, Val, VarInfo); - else - ResultVal = B.createDebugValueAddr(InstLoc, Val, VarInfo); - break; - } - - // unchecked_ownership_conversion : , to - case SILInstructionKind::UncheckedOwnershipConversionInst: { - ValueOwnershipKind LHSKind = ValueOwnershipKind::Any; - ValueOwnershipKind RHSKind = ValueOwnershipKind::Any; - SourceLoc Loc; - - if (parseTypedValueRef(Val, Loc, B) || - P.parseToken(tok::comma, diag::expected_sil_colon, - "unchecked_ownership_conversion value ownership kind " - "conversion specification") || - parseSILOwnership(LHSKind) || parseVerbatim("to") || - parseSILOwnership(RHSKind) || parseSILDebugLocation(InstLoc, B)) { - return true; - } - - if (Val.getOwnershipKind() != LHSKind) { - return true; - } - - ResultVal = B.createUncheckedOwnershipConversion(InstLoc, Val, RHSKind); - break; - } - - case SILInstructionKind::LoadInst: { - LoadOwnershipQualifier Qualifier; - SourceLoc AddrLoc; - - if (parseLoadOwnershipQualifier(Qualifier, *this) || - parseTypedValueRef(Val, AddrLoc, B) || parseSILDebugLocation(InstLoc, B)) - return true; - - ResultVal = B.createLoad(InstLoc, Val, Qualifier); - break; - } - - case SILInstructionKind::LoadBorrowInst: { - SourceLoc AddrLoc; - - if (parseTypedValueRef(Val, AddrLoc, B) || parseSILDebugLocation(InstLoc, B)) - return true; - - ResultVal = B.createLoadBorrow(InstLoc, Val); - break; - } - - case SILInstructionKind::BeginBorrowInst: { - SourceLoc AddrLoc; - - if (parseTypedValueRef(Val, AddrLoc, B) || parseSILDebugLocation(InstLoc, B)) - return true; - - ResultVal = B.createBeginBorrow(InstLoc, Val); - break; - } - -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Load##Name##Inst: { \ - bool isTake = false; \ - SourceLoc addrLoc; \ - if (parseSILOptional(isTake, *this, "take") || \ - parseTypedValueRef(Val, addrLoc, B) || \ - parseSILDebugLocation(InstLoc, B)) \ - return true; \ - if (!Val->getType().is()) { \ - P.diagnose(addrLoc, diag::sil_operand_not_ref_storage_address, \ - "source", OpcodeName, ReferenceOwnership::Name); \ - } \ - ResultVal = B.createLoad##Name(InstLoc, Val, IsTake_t(isTake)); \ - break; \ + case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugValueAddrInst: { + SILDebugVariable VarInfo; + if (parseTypedValueRef(Val, B) || parseSILDebugVar(VarInfo) || + parseSILDebugLocation(InstLoc, B)) + return true; + if (Opcode == SILInstructionKind::DebugValueInst) + ResultVal = B.createDebugValue(InstLoc, Val, VarInfo); + else + ResultVal = B.createDebugValueAddr(InstLoc, Val, VarInfo); + break; + } + + // unchecked_ownership_conversion : , to + case SILInstructionKind::UncheckedOwnershipConversionInst: { + ValueOwnershipKind LHSKind = ValueOwnershipKind::None; + ValueOwnershipKind RHSKind = ValueOwnershipKind::None; + SourceLoc Loc; + + if (parseTypedValueRef(Val, Loc, B) || + P.parseToken(tok::comma, diag::expected_sil_colon, + "unchecked_ownership_conversion value ownership kind " + "conversion specification") || + parseSILOwnership(LHSKind) || parseVerbatim("to") || + parseSILOwnership(RHSKind) || parseSILDebugLocation(InstLoc, B)) { + return true; + } + + if (Val.getOwnershipKind() != LHSKind) { + return true; + } + + ResultVal = B.createUncheckedOwnershipConversion(InstLoc, Val, RHSKind); + break; + } + + case SILInstructionKind::LoadInst: { + LoadOwnershipQualifier Qualifier; + SourceLoc AddrLoc; + + if (parseLoadOwnershipQualifier(Qualifier, *this) || + parseTypedValueRef(Val, AddrLoc, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createLoad(InstLoc, Val, Qualifier); + break; + } + + case SILInstructionKind::LoadBorrowInst: { + SourceLoc AddrLoc; + + if (parseTypedValueRef(Val, AddrLoc, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createLoadBorrow(InstLoc, Val); + break; + } + + case SILInstructionKind::BeginBorrowInst: { + SourceLoc AddrLoc; + + if (parseTypedValueRef(Val, AddrLoc, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createBeginBorrow(InstLoc, Val); + break; + } + +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Load##Name##Inst: { \ + bool isTake = false; \ + SourceLoc addrLoc; \ + if (parseSILOptional(isTake, *this, "take") || \ + parseTypedValueRef(Val, addrLoc, B) || \ + parseSILDebugLocation(InstLoc, B)) \ + return true; \ + if (!Val->getType().is()) { \ + P.diagnose(addrLoc, diag::sil_operand_not_ref_storage_address, "source", \ + OpcodeName, ReferenceOwnership::Name); \ + } \ + ResultVal = B.createLoad##Name(InstLoc, Val, IsTake_t(isTake)); \ + break; \ } #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::CopyBlockWithoutEscapingInst: { SILValue Closure; - if (parseTypedValueRef(Val, B) || - parseVerbatim("withoutEscaping") || - parseTypedValueRef(Closure, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("withoutEscaping") || + parseTypedValueRef(Closure, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createCopyBlockWithoutEscaping(InstLoc, Val, Closure); @@ -3094,10 +3016,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::MarkDependenceInst: { SILValue Base; - if (parseTypedValueRef(Val, B) || - parseVerbatim("on") || - parseTypedValueRef(Base, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseVerbatim("on") || + parseTypedValueRef(Base, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createMarkDependence(InstLoc, Val, Base); @@ -3107,8 +3027,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::KeyPathInst: { SmallVector components; SILType Ty; - if (parseSILType(Ty) - || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) + if (parseSILType(Ty) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) return true; GenericParamList *generics = nullptr; @@ -3118,12 +3038,12 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SmallVector operandTypes; { Scope genericsScope(&P, ScopeKind::Generics); - generics = P.parseSILGenericParams().getPtrOrNull(); - patternEnv = handleSILGenericParams(P.Context, generics, &P.SF); - + generics = P.maybeParseGenericParams().getPtrOrNull(); + patternEnv = handleSILGenericParams(generics, &P.SF); + if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) return true; - + while (true) { Identifier componentKind; SourceLoc componentLoc; @@ -3131,47 +3051,47 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { diag::sil_keypath_expected_component_kind)) return true; - if (componentKind.str() == "root") { - if (P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") - || parseASTType(rootType, patternEnv)) + if (P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, + "$") || + parseASTType(rootType, patternEnv)) return true; } else if (componentKind.str() == "objc") { auto tok = P.Tok; if (P.parseToken(tok::string_literal, diag::expected_tok_in_sil_instr, "string literal")) return true; - + auto objcStringValue = tok.getText().drop_front().drop_back(); - objcString = StringRef( - P.Context.AllocateCopy(objcStringValue.begin(), - objcStringValue.end()), - objcStringValue.size()); + objcString = + StringRef(P.Context.AllocateCopy(objcStringValue.begin(), + objcStringValue.end()), + objcStringValue.size()); } else { KeyPathPatternComponent component; if (parseKeyPathPatternComponent(component, operandTypes, - componentLoc, componentKind, - InstLoc, patternEnv)) + componentLoc, componentKind, InstLoc, + patternEnv)) return true; components.push_back(component); } - + if (!P.consumeIf(tok::semi)) break; } - + if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")") || parseSILDebugLocation(InstLoc, B)) return true; } - + if (rootType.isNull()) P.diagnose(InstLoc.getSourceLoc(), diag::sil_keypath_no_root); - + SmallVector parsedSubs; if (parseSubstitutions(parsedSubs, ContextGenericEnv)) return true; - + SubstitutionMap subMap; if (!parsedSubs.empty()) { if (!patternEnv) { @@ -3184,29 +3104,26 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (!subMap) return true; } - + SmallVector operands; - + if (P.consumeIf(tok::l_paren)) { - Lowering::GenericContextScope scope(SILMod.Types, - patternEnv ? patternEnv->getGenericSignature()->getCanonicalSignature() - : nullptr); while (true) { SILValue v; - - if (operands.size() >= operandTypes.size() - || !operandTypes[operands.size()]) { + + if (operands.size() >= operandTypes.size() || + !operandTypes[operands.size()]) { P.diagnose(P.Tok, diag::sil_keypath_no_use_of_operand_in_pattern, operands.size()); return true; } - + auto ty = operandTypes[operands.size()].subst(SILMod, subMap); - + if (parseValueRef(v, ty, RegularLocation(P.Tok.getLoc()), B)) return true; operands.push_back(v); - + if (P.consumeIf(tok::comma)) continue; if (P.consumeIf(tok::r_paren)) @@ -3214,28 +3131,27 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } } - + if (parseSILDebugLocation(InstLoc, B)) return true; - CanGenericSignature canSig = CanGenericSignature(); + CanGenericSignature canSig; if (patternEnv && patternEnv->getGenericSignature()) { - canSig = patternEnv->getGenericSignature()->getCanonicalSignature(); + canSig = patternEnv->getGenericSignature().getCanonicalSignature(); } CanType leafType; if (!components.empty()) leafType = components.back().getComponentType(); else leafType = rootType; - auto pattern = KeyPathPattern::get(B.getModule(), canSig, - rootType, leafType, - components, objcString); + auto pattern = KeyPathPattern::get(B.getModule(), canSig, rootType, + leafType, components, objcString); ResultVal = B.createKeyPath(InstLoc, pattern, subMap, operands, Ty); break; } - // Conversion instructions. + // Conversion instructions. case SILInstructionKind::UncheckedRefCastInst: case SILInstructionKind::UncheckedAddrCastInst: case SILInstructionKind::UncheckedTrivialBitCastInst: @@ -3246,8 +3162,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::BridgeObjectToWordInst: case SILInstructionKind::RefToRawPointerInst: case SILInstructionKind::RawPointerToRefInst: -#define LOADABLE_REF_STORAGE(Name, ...) \ - case SILInstructionKind::RefTo##Name##Inst: \ +#define LOADABLE_REF_STORAGE(Name, ...) \ + case SILInstructionKind::RefTo##Name##Inst: \ case SILInstructionKind::Name##ToRefInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::ThinFunctionToPointerInst: @@ -3273,9 +3189,9 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } } - if (parseTypedValueRef(Val, B) - || parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, - "to")) + if (parseTypedValueRef(Val, B) || + parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, + "to")) return true; if (ToToken.str() != "to") { @@ -3295,7 +3211,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; switch (Opcode) { - default: llvm_unreachable("Out of sync with parent switch"); + default: + llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::UncheckedRefCastInst: ResultVal = B.createUncheckedRefCast(InstLoc, Val, Ty); break; @@ -3334,13 +3251,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { case SILInstructionKind::RawPointerToRefInst: ResultVal = B.createRawPointerToRef(InstLoc, Val, Ty); break; -#define LOADABLE_REF_STORAGE(Name, ...) \ - case SILInstructionKind::RefTo##Name##Inst: \ - ResultVal = B.createRefTo##Name(InstLoc, Val, Ty); \ - break; \ - case SILInstructionKind::Name##ToRefInst: \ - ResultVal = B.create##Name##ToRef(InstLoc, Val, Ty); \ - break; +#define LOADABLE_REF_STORAGE(Name, ...) \ + case SILInstructionKind::RefTo##Name##Inst: \ + ResultVal = B.createRefTo##Name(InstLoc, Val, Ty); \ + break; \ + case SILInstructionKind::Name##ToRefInst: \ + ResultVal = B.create##Name##ToRef(InstLoc, Val, Ty); \ + break; #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::ThinFunctionToPointerInst: ResultVal = B.createThinFunctionToPointer(InstLoc, Val, Ty); @@ -3372,13 +3289,12 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SourceLoc ToLoc; StringRef attr; if (parseTypedValueRef(Val, B) || - parseSILIdentifier(ToToken, ToLoc, - diag::expected_tok_in_sil_instr, "to")) + parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, + "to")) return true; if (parseSILOptional(attr, *this) && attr.empty()) return true; - if (parseSILType(Ty) || - parseSILDebugLocation(InstLoc, B)) + if (parseSILType(Ty) || parseSILDebugLocation(InstLoc, B)) return true; bool isStrict = attr.equals("strict"); @@ -3389,16 +3305,15 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - ResultVal = B.createPointerToAddress(InstLoc, Val, Ty, - isStrict, isInvariant); + ResultVal = + B.createPointerToAddress(InstLoc, Val, Ty, isStrict, isInvariant); break; } case SILInstructionKind::RefToBridgeObjectInst: { SILValue BitsVal; if (parseTypedValueRef(Val, B) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(BitsVal, B) || - parseSILDebugLocation(InstLoc, B)) + parseTypedValueRef(BitsVal, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createRefToBridgeObject(InstLoc, Val, BitsVal); break; @@ -3428,8 +3343,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } auto consumptionKind = kind.getValue(); - if (parseSourceAndDestAddress() || parseConditionalBranchDestinations() - || parseSILDebugLocation(InstLoc, B)) + if (parseSourceAndDestAddress() || parseConditionalBranchDestinations() || + parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createCheckedCastAddrBranch( @@ -3454,23 +3369,32 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { InstLoc, SourceAddr, SourceType, DestAddr, TargetType); break; - case SILInstructionKind::UnconditionalCheckedCastValueInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseSILDebugLocation(InstLoc, B)) + case SILInstructionKind::UnconditionalCheckedCastValueInst: { + if (parseASTType(SourceType) || parseVerbatim("in") || + parseTypedValueRef(Val, B) || parseVerbatim("to") || + parseASTType(TargetType) || parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createUnconditionalCheckedCastValue(InstLoc, Val, Ty); + auto opaque = Lowering::AbstractionPattern::getOpaque(); + ResultVal = B.createUnconditionalCheckedCastValue( + InstLoc, Val, SourceType, F->getLoweredType(opaque, TargetType), + TargetType); break; + } - case SILInstructionKind::UnconditionalCheckedCastInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty)) + case SILInstructionKind::UnconditionalCheckedCastInst: { + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || + parseASTType(TargetType)) return true; if (parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createUnconditionalCheckedCast(InstLoc, Val, Ty); + auto opaque = Lowering::AbstractionPattern::getOpaque(); + ResultVal = B.createUnconditionalCheckedCast( + InstLoc, Val, F->getLoweredType(opaque, TargetType), TargetType); break; + } case SILInstructionKind::CheckedCastBranchInst: { bool isExact = false; @@ -3478,38 +3402,43 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { parseSILOptional(isExact, *this, "exact")) return true; - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseConditionalBranchDestinations()) + if (parseTypedValueRef(Val, B) || parseVerbatim("to") || + parseASTType(TargetType) || parseConditionalBranchDestinations()) return true; + auto opaque = Lowering::AbstractionPattern::getOpaque(); ResultVal = B.createCheckedCastBranch( - InstLoc, isExact, Val, Ty, - getBBForReference(SuccessBBName, SuccessBBLoc), + InstLoc, isExact, Val, F->getLoweredType(opaque, TargetType), + TargetType, getBBForReference(SuccessBBName, SuccessBBLoc), getBBForReference(FailureBBName, FailureBBLoc)); break; } - case SILInstructionKind::CheckedCastValueBranchInst: - if (parseTypedValueRef(Val, B) || parseVerbatim("to") || parseSILType(Ty) - || parseConditionalBranchDestinations()) + case SILInstructionKind::CheckedCastValueBranchInst: { + if (parseASTType(SourceType) || parseVerbatim("in") || + parseTypedValueRef(Val, B) || parseVerbatim("to") || + parseASTType(TargetType) || parseConditionalBranchDestinations()) return true; + auto opaque = Lowering::AbstractionPattern::getOpaque(); ResultVal = B.createCheckedCastValueBranch( - InstLoc, Val, Ty, getBBForReference(SuccessBBName, SuccessBBLoc), + InstLoc, Val, SourceType, F->getLoweredType(opaque, TargetType), + TargetType, getBBForReference(SuccessBBName, SuccessBBLoc), getBBForReference(FailureBBName, FailureBBLoc)); break; + } case SILInstructionKind::MarkUninitializedInst: { if (P.parseToken(tok::l_square, diag::expected_tok_in_sil_instr, "[")) return true; - + Identifier KindId; SourceLoc KindLoc = P.Tok.getLoc(); if (P.consumeIf(tok::kw_var)) KindId = P.Context.getIdentifier("var"); - else if (P.parseIdentifier(KindId, KindLoc, - diag::expected_tok_in_sil_instr, "kind")) + else if (P.parseIdentifier(KindId, KindLoc, diag::expected_tok_in_sil_instr, + "kind")) return true; - + if (P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]")) return true; @@ -3535,17 +3464,17 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createMarkUninitialized(InstLoc, Val, Kind); break; } - + case SILInstructionKind::MarkFunctionEscapeInst: { SmallVector OpList; do { - if (parseTypedValueRef(Val, B)) return true; + if (parseTypedValueRef(Val, B)) + return true; OpList.push_back(Val); } while (!peekSILDebugLocation(P) && P.consumeIf(tok::comma)); @@ -3594,15 +3523,15 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILType ValType = AddrVal->getType().getObjectType(); if (IsStore) { - ResultVal = B.createStore(InstLoc, - getLocalValue(From, ValType, InstLoc, B), - AddrVal, StoreQualifier); + ResultVal = + B.createStore(InstLoc, getLocalValue(From, ValType, InstLoc, B), + AddrVal, StoreQualifier); } else { assert(IsAssign); - ResultVal = B.createAssign(InstLoc, - getLocalValue(From, ValType, InstLoc, B), - AddrVal, AssignQualifier); + ResultVal = + B.createAssign(InstLoc, getLocalValue(From, ValType, InstLoc, B), + AddrVal, AssignQualifier); } break; @@ -3612,16 +3541,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILValue Src, DestAddr, InitFn, SetFn; SourceLoc DestLoc; AssignOwnershipQualifier AssignQualifier; - if (parseTypedValueRef(Src, B) || - parseVerbatim("to") || + if (parseTypedValueRef(Src, B) || parseVerbatim("to") || parseAssignOwnershipQualifier(AssignQualifier, *this) || parseTypedValueRef(DestAddr, DestLoc, B) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseVerbatim("init") || - parseTypedValueRef(InitFn, B) || + parseVerbatim("init") || parseTypedValueRef(InitFn, B) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseVerbatim("set") || - parseTypedValueRef(SetFn, B) || + parseVerbatim("set") || parseTypedValueRef(SetFn, B) || parseSILDebugLocation(InstLoc, B)) return true; @@ -3632,7 +3558,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } ResultVal = B.createAssignByWrapper(InstLoc, Src, DestAddr, InitFn, SetFn, - AssignQualifier); + AssignQualifier); break; } @@ -3646,10 +3572,11 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ParsedEnum noNestedConflict; ParsedEnum fromBuiltin; - bool isBeginAccess = (Opcode == SILInstructionKind::BeginAccessInst || - Opcode == SILInstructionKind::BeginUnpairedAccessInst); - bool wantsEnforcement = (isBeginAccess || - Opcode == SILInstructionKind::EndUnpairedAccessInst); + bool isBeginAccess = + (Opcode == SILInstructionKind::BeginAccessInst || + Opcode == SILInstructionKind::BeginUnpairedAccessInst); + bool wantsEnforcement = + (isBeginAccess || Opcode == SILInstructionKind::EndUnpairedAccessInst); while (P.consumeIf(tok::l_square)) { Identifier ident; @@ -3746,15 +3673,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; if (!addrVal->getType().isAddress()) { - P.diagnose(addrLoc, diag::sil_operand_not_address, "operand", - OpcodeName); + P.diagnose(addrLoc, diag::sil_operand_not_address, "operand", OpcodeName); return true; } if (Opcode == SILInstructionKind::BeginAccessInst) { - ResultVal = - B.createBeginAccess(InstLoc, addrVal, *kind, *enforcement, - *noNestedConflict, *fromBuiltin); + ResultVal = B.createBeginAccess(InstLoc, addrVal, *kind, *enforcement, + *noNestedConflict, *fromBuiltin); } else if (Opcode == SILInstructionKind::EndAccessInst) { ResultVal = B.createEndAccess(InstLoc, addrVal, *aborting); } else if (Opcode == SILInstructionKind::BeginUnpairedAccessInst) { @@ -3768,14 +3693,14 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StoreBorrowInst: { UnresolvedValueName from; bool isRefStorage = false; -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - isRefStorage |= Opcode == SILInstructionKind::Store##Name##Inst; +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + isRefStorage |= Opcode == SILInstructionKind::Store##Name##Inst; #include "swift/AST/ReferenceStorage.def" SourceLoc toLoc, addrLoc; @@ -3783,8 +3708,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILValue addrVal; bool isInit = false; if (parseValueName(from) || - parseSILIdentifier(toToken, toLoc, - diag::expected_tok_in_sil_instr, "to") || + parseSILIdentifier(toToken, toLoc, diag::expected_tok_in_sil_instr, + "to") || (isRefStorage && parseSILOptional(isInit, *this, "initialization")) || parseTypedValueRef(addrVal, addrLoc, B) || parseSILDebugLocation(InstLoc, B)) @@ -3796,8 +3721,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } if (!addrVal->getType().isAddress()) { - P.diagnose(addrLoc, diag::sil_operand_not_address, - "destination", OpcodeName); + P.diagnose(addrLoc, diag::sil_operand_not_address, "destination", + OpcodeName); return true; } @@ -3808,20 +3733,20 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - if (Opcode == SILInstructionKind::Store##Name##Inst) { \ - auto refType = addrVal->getType().getAs(); \ - if (!refType) { \ - P.diagnose(addrLoc, diag::sil_operand_not_ref_storage_address, \ - "destination", OpcodeName, ReferenceOwnership::Name); \ - return true; \ - } \ - auto valueTy =SILType::getPrimitiveObjectType(refType.getReferentType());\ - ResultVal = B.createStore##Name(InstLoc, \ - getLocalValue(from, valueTy, InstLoc, B),\ - addrVal, IsInitialization_t(isInit)); \ - break; \ - } +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + if (Opcode == SILInstructionKind::Store##Name##Inst) { \ + auto refType = addrVal->getType().getAs(); \ + if (!refType) { \ + P.diagnose(addrLoc, diag::sil_operand_not_ref_storage_address, \ + "destination", OpcodeName, ReferenceOwnership::Name); \ + return true; \ + } \ + auto valueTy = SILType::getPrimitiveObjectType(refType.getReferentType()); \ + ResultVal = \ + B.createStore##Name(InstLoc, getLocalValue(from, valueTy, InstLoc, B), \ + addrVal, IsInitialization_t(isInit)); \ + break; \ + } #include "swift/AST/ReferenceStorage.def" break; @@ -3840,8 +3765,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (Opcode == SILInstructionKind::AllocStackInst) { SILDebugVariable VarInfo; - if (parseSILDebugVar(VarInfo) || - parseSILDebugLocation(InstLoc, B)) + if (parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime); } else { @@ -3868,8 +3792,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { OnStack = true; } else if (Optional == "tail_elems") { SILType ElemTy; - if (parseSILType(ElemTy) || - !P.Tok.isAnyOperator() || + if (parseSILType(ElemTy) || !P.Tok.isAnyOperator() || P.Tok.getText() != "*") return true; P.consumeToken(); @@ -3907,8 +3830,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (OnStack) return true; - ResultVal = B.createAllocRefDynamic(InstLoc, Metadata, ObjectType, - IsObjC, ElementTypes, ElementCounts); + ResultVal = B.createAllocRefDynamic(InstLoc, Metadata, ObjectType, IsObjC, + ElementTypes, ElementCounts); } else { ResultVal = B.createAllocRef(InstLoc, ObjectType, IsObjC, OnStack, ElementTypes, ElementCounts); @@ -3917,8 +3840,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { } case SILInstructionKind::DeallocStackInst: - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createDeallocStack(InstLoc, Val); break; @@ -3927,8 +3849,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (parseSILOptional(OnStack, *this, "stack")) return true; - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createDeallocRef(InstLoc, Val, OnStack); break; @@ -3937,71 +3858,110 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { SILValue Metatype, Instance; if (parseTypedValueRef(Instance, B) || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(Metatype, B) || - parseSILDebugLocation(InstLoc, B)) + parseTypedValueRef(Metatype, B) || parseSILDebugLocation(InstLoc, B)) return true; ResultVal = B.createDeallocPartialRef(InstLoc, Instance, Metatype); break; } - case SILInstructionKind::DeallocBoxInst: - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; + case SILInstructionKind::DeallocBoxInst: + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; - ResultVal = B.createDeallocBox(InstLoc, Val); - break; - case SILInstructionKind::ValueMetatypeInst: - case SILInstructionKind::ExistentialMetatypeInst: { - SILType Ty; - if (parseSILType(Ty) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - switch (Opcode) { - default: llvm_unreachable("Out of sync with parent switch"); - case SILInstructionKind::ValueMetatypeInst: - ResultVal = B.createValueMetatype(InstLoc, Ty, Val); + ResultVal = B.createDeallocBox(InstLoc, Val); break; - case SILInstructionKind::ExistentialMetatypeInst: - ResultVal = B.createExistentialMetatype(InstLoc, Ty, Val); + case SILInstructionKind::ValueMetatypeInst: + case SILInstructionKind::ExistentialMetatypeInst: { + SILType Ty; + if (parseSILType(Ty) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + switch (Opcode) { + default: + llvm_unreachable("Out of sync with parent switch"); + case SILInstructionKind::ValueMetatypeInst: + ResultVal = B.createValueMetatype(InstLoc, Ty, Val); + break; + case SILInstructionKind::ExistentialMetatypeInst: + ResultVal = B.createExistentialMetatype(InstLoc, Ty, Val); + break; + case SILInstructionKind::DeallocBoxInst: + ResultVal = B.createDeallocBox(InstLoc, Val); + break; + } break; - case SILInstructionKind::DeallocBoxInst: - ResultVal = B.createDeallocBox(InstLoc, Val); + } + case SILInstructionKind::DeallocExistentialBoxInst: { + CanType ConcreteTy; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(ConcreteTy) || parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createDeallocExistentialBox(InstLoc, ConcreteTy, Val); break; } - break; - } - case SILInstructionKind::DeallocExistentialBoxInst: { - CanType ConcreteTy; - if (parseTypedValueRef(Val, B) - || P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") - || P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") - || parseASTType(ConcreteTy) - || parseSILDebugLocation(InstLoc, B)) - return true; - - ResultVal = B.createDeallocExistentialBox(InstLoc, ConcreteTy, Val); - break; - } - case SILInstructionKind::TupleInst: { - // Tuple instructions have two different syntaxes, one for simple tuple - // types, one for complicated ones. - if (P.Tok.isNot(tok::sil_dollar)) { - // If there is no type, parse the simple form. - if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) + case SILInstructionKind::TupleInst: { + // Tuple instructions have two different syntaxes, one for simple tuple + // types, one for complicated ones. + if (P.Tok.isNot(tok::sil_dollar)) { + // If there is no type, parse the simple form. + if (P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) + return true; + + // TODO: Check for a type here. This is how tuples with "interesting" + // types are described. + + // This form is used with tuples that have elements with no names or + // default values. + SmallVector TypeElts; + if (P.Tok.isNot(tok::r_paren)) { + do { + if (parseTypedValueRef(Val, B)) + return true; + OpList.push_back(Val); + TypeElts.push_back(Val->getType().getASTType()); + } while (P.consumeIf(tok::comma)); + } + HadError |= + P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")"); + + auto Ty = TupleType::get(TypeElts, P.Context); + auto Ty2 = SILType::getPrimitiveObjectType(Ty->getCanonicalType()); + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createTuple(InstLoc, Ty2, OpList); + break; + } + + // Otherwise, parse the fully general form. + SILType Ty; + if (parseSILType(Ty) || + P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) return true; - - // TODO: Check for a type here. This is how tuples with "interesting" - // types are described. - - // This form is used with tuples that have elements with no names or - // default values. + + TupleType *TT = Ty.getAs(); + if (TT == nullptr) { + P.diagnose(OpcodeLoc, diag::expected_tuple_type_in_tuple); + return true; + } + SmallVector TypeElts; if (P.Tok.isNot(tok::r_paren)) { do { - if (parseTypedValueRef(Val, B)) return true; + if (TypeElts.size() > TT->getNumElements()) { + P.diagnose(P.Tok, diag::sil_tuple_inst_wrong_value_count, + TT->getNumElements()); + return true; + } + Type EltTy = TT->getElement(TypeElts.size()).getType(); + if (parseValueRef( + Val, + SILType::getPrimitiveObjectType(EltTy->getCanonicalType()), + RegularLocation(P.Tok.getLoc()), B)) + return true; OpList.push_back(Val); TypeElts.push_back(Val->getType().getASTType()); } while (P.consumeIf(tok::comma)); @@ -4009,1058 +3969,1060 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { HadError |= P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr,")"); - auto Ty = TupleType::get(TypeElts, P.Context); - auto Ty2 = SILType::getPrimitiveObjectType(Ty->getCanonicalType()); + if (TypeElts.size() != TT->getNumElements()) { + P.diagnose(OpcodeLoc, diag::sil_tuple_inst_wrong_value_count, + TT->getNumElements()); + return true; + } + if (parseSILDebugLocation(InstLoc, B)) return true; - ResultVal = B.createTuple(InstLoc, Ty2, OpList); + ResultVal = B.createTuple(InstLoc, Ty, OpList); break; } - - // Otherwise, parse the fully general form. - SILType Ty; - if (parseSILType(Ty) || - P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) - return true; - - TupleType *TT = Ty.getAs(); - if (TT == nullptr) { - P.diagnose(OpcodeLoc, diag::expected_tuple_type_in_tuple); - return true; - } - - SmallVector TypeElts; - if (P.Tok.isNot(tok::r_paren)) { - do { - if (TypeElts.size() > TT->getNumElements()) { - P.diagnose(P.Tok, diag::sil_tuple_inst_wrong_value_count, - TT->getNumElements()); - return true; - } - Type EltTy = TT->getElement(TypeElts.size()).getType(); - if (parseValueRef(Val, - SILType::getPrimitiveObjectType(EltTy->getCanonicalType()), - RegularLocation(P.Tok.getLoc()), B)) + case SILInstructionKind::EnumInst: { + SILType Ty; + SILDeclRef Elt; + SILValue Operand; + if (parseSILType(Ty) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDeclRef(Elt)) + return true; + + if (P.Tok.is(tok::comma) && !peekSILDebugLocation(P)) { + P.consumeToken(tok::comma); + if (parseTypedValueRef(Operand, B)) return true; - OpList.push_back(Val); - TypeElts.push_back(Val->getType().getASTType()); - } while (P.consumeIf(tok::comma)); + } + + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createEnum(InstLoc, Operand, + cast(Elt.getDecl()), Ty); + break; } - HadError |= P.parseToken(tok::r_paren, - diag::expected_tok_in_sil_instr,")"); + case SILInstructionKind::InitEnumDataAddrInst: + case SILInstructionKind::UncheckedEnumDataInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { + SILValue Operand; + SILDeclRef EltRef; + if (parseTypedValueRef(Operand, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDeclRef(EltRef) || parseSILDebugLocation(InstLoc, B)) + return true; - if (TypeElts.size() != TT->getNumElements()) { - P.diagnose(OpcodeLoc, diag::sil_tuple_inst_wrong_value_count, - TT->getNumElements()); - return true; + EnumElementDecl *Elt = cast(EltRef.getDecl()); + auto ResultTy = Operand->getType().getEnumElementType( + Elt, SILMod, B.getTypeExpansionContext()); + + switch (Opcode) { + case swift::SILInstructionKind::InitEnumDataAddrInst: + ResultVal = B.createInitEnumDataAddr(InstLoc, Operand, Elt, ResultTy); + break; + case swift::SILInstructionKind::UncheckedTakeEnumDataAddrInst: + ResultVal = + B.createUncheckedTakeEnumDataAddr(InstLoc, Operand, Elt, ResultTy); + break; + case swift::SILInstructionKind::UncheckedEnumDataInst: + ResultVal = B.createUncheckedEnumData(InstLoc, Operand, Elt, ResultTy); + break; + default: + llvm_unreachable("switch out of sync"); + } + break; } + case SILInstructionKind::InjectEnumAddrInst: { + SILValue Operand; + SILDeclRef EltRef; + if (parseTypedValueRef(Operand, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDeclRef(EltRef) || parseSILDebugLocation(InstLoc, B)) + return true; - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createTuple(InstLoc, Ty, OpList); - break; - } - case SILInstructionKind::EnumInst: { - SILType Ty; - SILDeclRef Elt; - SILValue Operand; - if (parseSILType(Ty) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDeclRef(Elt)) - return true; - - if (P.Tok.is(tok::comma) && !peekSILDebugLocation(P)) { - P.consumeToken(tok::comma); - if (parseTypedValueRef(Operand, B)) + EnumElementDecl *Elt = cast(EltRef.getDecl()); + ResultVal = B.createInjectEnumAddr(InstLoc, Operand, Elt); + break; + } + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::TupleExtractInst: { + SourceLoc NameLoc; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) return true; + + unsigned Field = 0; + TupleType *TT = Val->getType().getAs(); + if (P.Tok.isNot(tok::integer_literal) || + parseIntegerLiteral(P.Tok.getText(), 10, Field) || + Field >= TT->getNumElements()) { + P.diagnose(P.Tok, diag::sil_tuple_inst_wrong_field); + return true; + } + P.consumeToken(tok::integer_literal); + if (parseSILDebugLocation(InstLoc, B)) + return true; + auto ResultTy = TT->getElement(Field).getType()->getCanonicalType(); + if (Opcode == SILInstructionKind::TupleElementAddrInst) + ResultVal = B.createTupleElementAddr( + InstLoc, Val, Field, SILType::getPrimitiveAddressType(ResultTy)); + else + ResultVal = B.createTupleExtract( + InstLoc, Val, Field, SILType::getPrimitiveObjectType(ResultTy)); + break; } - - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createEnum(InstLoc, Operand, - cast(Elt.getDecl()), Ty); - break; - } - case SILInstructionKind::InitEnumDataAddrInst: - case SILInstructionKind::UncheckedEnumDataInst: - case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { - SILValue Operand; - SILDeclRef EltRef; - if (parseTypedValueRef(Operand, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDeclRef(EltRef) || - parseSILDebugLocation(InstLoc, B)) - return true; - - EnumElementDecl *Elt = cast(EltRef.getDecl()); - auto ResultTy = Operand->getType().getEnumElementType(Elt, SILMod); - - switch (Opcode) { - case swift::SILInstructionKind::InitEnumDataAddrInst: - ResultVal = B.createInitEnumDataAddr(InstLoc, Operand, Elt, ResultTy); + case SILInstructionKind::ReturnInst: { + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createReturn(InstLoc, Val); break; - case swift::SILInstructionKind::UncheckedTakeEnumDataAddrInst: - ResultVal = B.createUncheckedTakeEnumDataAddr(InstLoc, Operand, Elt, - ResultTy); + } + case SILInstructionKind::ThrowInst: { + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createThrow(InstLoc, Val); break; - case swift::SILInstructionKind::UncheckedEnumDataInst: - ResultVal = B.createUncheckedEnumData(InstLoc, Operand, Elt, ResultTy); + } + case SILInstructionKind::UnwindInst: { + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createUnwind(InstLoc); break; - default: - llvm_unreachable("switch out of sync"); } - break; - } - case SILInstructionKind::InjectEnumAddrInst: { - SILValue Operand; - SILDeclRef EltRef; - if (parseTypedValueRef(Operand, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDeclRef(EltRef) || - parseSILDebugLocation(InstLoc, B)) - return true; - - EnumElementDecl *Elt = cast(EltRef.getDecl()); - ResultVal = B.createInjectEnumAddr(InstLoc, Operand, Elt); - break; - } - case SILInstructionKind::TupleElementAddrInst: - case SILInstructionKind::TupleExtractInst: { - SourceLoc NameLoc; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) - return true; + case SILInstructionKind::YieldInst: { + SmallVector values; - unsigned Field = 0; - TupleType *TT = Val->getType().getAs(); - if (P.Tok.isNot(tok::integer_literal) || - parseIntegerLiteral(P.Tok.getText(), 10, Field) || - Field >= TT->getNumElements()) { - P.diagnose(P.Tok, diag::sil_tuple_inst_wrong_field); - return true; - } - P.consumeToken(tok::integer_literal); - if (parseSILDebugLocation(InstLoc, B)) - return true; - auto ResultTy = TT->getElement(Field).getType()->getCanonicalType(); - if (Opcode == SILInstructionKind::TupleElementAddrInst) - ResultVal = B.createTupleElementAddr(InstLoc, Val, Field, - SILType::getPrimitiveAddressType(ResultTy)); - else - ResultVal = B.createTupleExtract(InstLoc, Val, Field, - SILType::getPrimitiveObjectType(ResultTy)); - break; - } - case SILInstructionKind::ReturnInst: { - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createReturn(InstLoc, Val); - break; - } - case SILInstructionKind::ThrowInst: { - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createThrow(InstLoc, Val); - break; - } - case SILInstructionKind::UnwindInst: { - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createUnwind(InstLoc); - break; - } - case SILInstructionKind::YieldInst: { - SmallVector values; + // Parse a parenthesized (unless length-1), comma-separated list + // of yielded values. + if (P.consumeIf(tok::l_paren)) { + if (!P.Tok.is(tok::r_paren)) { + do { + if (parseTypedValueRef(Val, B)) + return true; + values.push_back(Val); + } while (P.consumeIf(tok::comma)); + } - // Parse a parenthesized (unless length-1), comma-separated list - // of yielded values. - if (P.consumeIf(tok::l_paren)) { - if (!P.Tok.is(tok::r_paren)) { - do { - if (parseTypedValueRef(Val, B)) - return true; - values.push_back(Val); - } while (P.consumeIf(tok::comma)); + if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")")) + return true; + + } else { + if (parseTypedValueRef(Val, B)) + return true; + values.push_back(Val); } - if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")")) + Identifier resumeName, unwindName; + SourceLoc resumeLoc, unwindLoc; + if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("resume") || + parseSILIdentifier(resumeName, resumeLoc, + diag::expected_sil_block_name) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("unwind") || + parseSILIdentifier(unwindName, unwindLoc, + diag::expected_sil_block_name) || + parseSILDebugLocation(InstLoc, B)) return true; - } else { - if (parseTypedValueRef(Val, B)) - return true; - values.push_back(Val); + auto resumeBB = getBBForReference(resumeName, resumeLoc); + auto unwindBB = getBBForReference(unwindName, unwindLoc); + ResultVal = B.createYield(InstLoc, values, resumeBB, unwindBB); + break; } + case SILInstructionKind::BranchInst: { + Identifier BBName; + SourceLoc NameLoc; + if (parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name)) + return true; - Identifier resumeName, unwindName; - SourceLoc resumeLoc, unwindLoc; - if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseVerbatim("resume") || - parseSILIdentifier(resumeName, resumeLoc, - diag::expected_sil_block_name) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseVerbatim("unwind") || - parseSILIdentifier(unwindName, unwindLoc, - diag::expected_sil_block_name) || - parseSILDebugLocation(InstLoc, B)) - return true; - - auto resumeBB = getBBForReference(resumeName, resumeLoc); - auto unwindBB = getBBForReference(unwindName, unwindLoc); - ResultVal = B.createYield(InstLoc, values, resumeBB, unwindBB); - break; - } - case SILInstructionKind::BranchInst: { - Identifier BBName; - SourceLoc NameLoc; - if (parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name)) - return true; - - SmallVector Args; - if (parseSILBBArgsAtBranch(Args, B)) - return true; - - if (parseSILDebugLocation(InstLoc, B)) - return true; - - // Note, the basic block here could be a reference to an undefined - // basic block, which will be parsed later on. - ResultVal = B.createBranch(InstLoc, getBBForReference(BBName, NameLoc), - Args); - break; - } - case SILInstructionKind::CondBranchInst: { - UnresolvedValueName Cond; - Identifier BBName, BBName2; - SourceLoc NameLoc, NameLoc2; - SmallVector Args, Args2; - if (parseValueName(Cond) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name) || - parseSILBBArgsAtBranch(Args, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(BBName2, NameLoc2, - diag::expected_sil_block_name) || - parseSILBBArgsAtBranch(Args2, B) || - parseSILDebugLocation(InstLoc, B)) - return true; + SmallVector Args; + if (parseSILBBArgsAtBranch(Args, B)) + return true; - auto I1Ty = - SILType::getBuiltinIntegerType(1, SILMod.getASTContext()); - SILValue CondVal = getLocalValue(Cond, I1Ty, InstLoc, B); - ResultVal = B.createCondBranch(InstLoc, CondVal, - getBBForReference(BBName, NameLoc), - Args, - getBBForReference(BBName2, NameLoc2), - Args2); - break; - } - case SILInstructionKind::UnreachableInst: - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createUnreachable(InstLoc); - break; - - case SILInstructionKind::ClassMethodInst: - case SILInstructionKind::SuperMethodInst: - case SILInstructionKind::ObjCMethodInst: - case SILInstructionKind::ObjCSuperMethodInst: { - SILDeclRef Member; - SILType MethodTy; - SourceLoc TyLoc; - SmallVector values; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) - return true; + if (parseSILDebugLocation(InstLoc, B)) + return true; - if (parseSILDeclRef(Member, true)) - return true; + // Note, the basic block here could be a reference to an undefined + // basic block, which will be parsed later on. + ResultVal = + B.createBranch(InstLoc, getBBForReference(BBName, NameLoc), Args); + break; + } + case SILInstructionKind::CondBranchInst: { + UnresolvedValueName Cond; + Identifier BBName, BBName2; + SourceLoc NameLoc, NameLoc2; + SmallVector Args, Args2; + if (parseValueName(Cond) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name) || + parseSILBBArgsAtBranch(Args, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(BBName2, NameLoc2, + diag::expected_sil_block_name) || + parseSILBBArgsAtBranch(Args2, B) || parseSILDebugLocation(InstLoc, B)) + return true; - if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(MethodTy, TyLoc) || - parseSILDebugLocation(InstLoc, B)) - return true; + auto I1Ty = SILType::getBuiltinIntegerType(1, SILMod.getASTContext()); + SILValue CondVal = getLocalValue(Cond, I1Ty, InstLoc, B); + ResultVal = B.createCondBranch( + InstLoc, CondVal, getBBForReference(BBName, NameLoc), Args, + getBBForReference(BBName2, NameLoc2), Args2); + break; + } + case SILInstructionKind::UnreachableInst: + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createUnreachable(InstLoc); + break; - switch (Opcode) { - default: llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::ClassMethodInst: - ResultVal = B.createClassMethod(InstLoc, Val, Member, MethodTy); - break; case SILInstructionKind::SuperMethodInst: - ResultVal = B.createSuperMethod(InstLoc, Val, Member, MethodTy); - break; case SILInstructionKind::ObjCMethodInst: - ResultVal = B.createObjCMethod(InstLoc, Val, Member, MethodTy); - break; - case SILInstructionKind::ObjCSuperMethodInst: - ResultVal = B.createObjCSuperMethod(InstLoc, Val, Member, MethodTy); + case SILInstructionKind::ObjCSuperMethodInst: { + SILDeclRef Member; + SILType MethodTy; + SourceLoc TyLoc; + SmallVector values; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) + return true; + + if (parseSILDeclRef(Member, true)) + return true; + + if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(MethodTy, TyLoc) || parseSILDebugLocation(InstLoc, B)) + return true; + + switch (Opcode) { + default: + llvm_unreachable("Out of sync with parent switch"); + case SILInstructionKind::ClassMethodInst: + ResultVal = B.createClassMethod(InstLoc, Val, Member, MethodTy); + break; + case SILInstructionKind::SuperMethodInst: + ResultVal = B.createSuperMethod(InstLoc, Val, Member, MethodTy); + break; + case SILInstructionKind::ObjCMethodInst: + ResultVal = B.createObjCMethod(InstLoc, Val, Member, MethodTy); + break; + case SILInstructionKind::ObjCSuperMethodInst: + ResultVal = B.createObjCSuperMethod(InstLoc, Val, Member, MethodTy); + break; + } break; } - break; - } - case SILInstructionKind::WitnessMethodInst: { - CanType LookupTy; - SILDeclRef Member; - SILType MethodTy; - SourceLoc TyLoc; - if (P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || - parseASTType(LookupTy) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) - return true; - if (parseSILDeclRef(Member, true)) - return true; - // Optional operand. - SILValue Operand; - if (P.Tok.is(tok::comma)) { - P.consumeToken(tok::comma); - if (parseTypedValueRef(Operand, B)) + case SILInstructionKind::WitnessMethodInst: { + CanType LookupTy; + SILDeclRef Member; + SILType MethodTy; + SourceLoc TyLoc; + if (P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(LookupTy) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) + return true; + if (parseSILDeclRef(Member, true)) + return true; + // Optional operand. + SILValue Operand; + if (P.Tok.is(tok::comma)) { + P.consumeToken(tok::comma); + if (parseTypedValueRef(Operand, B)) + return true; + } + if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(MethodTy, TyLoc) || parseSILDebugLocation(InstLoc, B)) return true; - } - if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - parseSILType(MethodTy, TyLoc) || - parseSILDebugLocation(InstLoc, B)) - return true; - // If LookupTy is a non-archetype, look up its conformance. - ProtocolDecl *proto - = dyn_cast(Member.getDecl()->getDeclContext()); - if (!proto) { - P.diagnose(TyLoc, diag::sil_witness_method_not_protocol); - return true; - } - auto conformance = P.SF.getParentModule()->lookupConformance(LookupTy, proto); - if (!conformance) { - P.diagnose(TyLoc, diag::sil_witness_method_type_does_not_conform); - return true; + // If LookupTy is a non-archetype, look up its conformance. + ProtocolDecl *proto = + dyn_cast(Member.getDecl()->getDeclContext()); + if (!proto) { + P.diagnose(TyLoc, diag::sil_witness_method_not_protocol); + return true; + } + auto conformance = + P.SF.getParentModule()->lookupConformance(LookupTy, proto); + if (conformance.isInvalid()) { + P.diagnose(TyLoc, diag::sil_witness_method_type_does_not_conform); + return true; + } + + ResultVal = B.createWitnessMethod(InstLoc, LookupTy, conformance, Member, + MethodTy); + break; } + case SILInstructionKind::CopyAddrInst: { + bool IsTake = false, IsInit = false; + UnresolvedValueName SrcLName; + SILValue DestLVal; + SourceLoc ToLoc, DestLoc; + Identifier ToToken; + if (parseSILOptional(IsTake, *this, "take") || parseValueName(SrcLName) || + parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, + "to") || + parseSILOptional(IsInit, *this, "initialization") || + parseTypedValueRef(DestLVal, DestLoc, B) || + parseSILDebugLocation(InstLoc, B)) + return true; - ResultVal = B.createWitnessMethod(InstLoc, LookupTy, *conformance, Member, - MethodTy); - break; - } - case SILInstructionKind::CopyAddrInst: { - bool IsTake = false, IsInit = false; - UnresolvedValueName SrcLName; - SILValue DestLVal; - SourceLoc ToLoc, DestLoc; - Identifier ToToken; - if (parseSILOptional(IsTake, *this, "take") || parseValueName(SrcLName) || - parseSILIdentifier(ToToken, ToLoc, - diag::expected_tok_in_sil_instr, "to") || - parseSILOptional(IsInit, *this, "initialization") || - parseTypedValueRef(DestLVal, DestLoc, B) || - parseSILDebugLocation(InstLoc, B)) - return true; + if (ToToken.str() != "to") { + P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to"); + return true; + } - if (ToToken.str() != "to") { - P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to"); - return true; - } + if (!DestLVal->getType().isAddress()) { + P.diagnose(DestLoc, diag::sil_invalid_instr_operands); + return true; + } - if (!DestLVal->getType().isAddress()) { - P.diagnose(DestLoc, diag::sil_invalid_instr_operands); - return true; + SILValue SrcLVal = + getLocalValue(SrcLName, DestLVal->getType(), InstLoc, B); + ResultVal = B.createCopyAddr(InstLoc, SrcLVal, DestLVal, IsTake_t(IsTake), + IsInitialization_t(IsInit)); + break; } + case SILInstructionKind::BindMemoryInst: { + SILValue IndexVal; + Identifier ToToken; + SourceLoc ToLoc; + SILType EltTy; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(IndexVal, B) || + parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr, + "to") || + parseSILType(EltTy) || parseSILDebugLocation(InstLoc, B)) + return true; - SILValue SrcLVal = getLocalValue(SrcLName, DestLVal->getType(), InstLoc, B); - ResultVal = B.createCopyAddr(InstLoc, SrcLVal, DestLVal, - IsTake_t(IsTake), - IsInitialization_t(IsInit)); - break; - } - case SILInstructionKind::BindMemoryInst: { - SILValue IndexVal; - Identifier ToToken; - SourceLoc ToLoc; - SILType EltTy; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(IndexVal, B) || - parseSILIdentifier(ToToken, ToLoc, - diag::expected_tok_in_sil_instr, "to") || - parseSILType(EltTy) || - parseSILDebugLocation(InstLoc, B)) - return true; - - if (ToToken.str() != "to") { - P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to"); - return true; + if (ToToken.str() != "to") { + P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to"); + return true; + } + ResultVal = B.createBindMemory(InstLoc, Val, IndexVal, EltTy); + break; } - ResultVal = B.createBindMemory(InstLoc, Val, IndexVal, EltTy); - break; - } - case SILInstructionKind::ObjectInst: - case SILInstructionKind::StructInst: { - SILType Ty; - if (parseSILType(Ty) || - P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) - return true; + case SILInstructionKind::ObjectInst: + case SILInstructionKind::StructInst: { + SILType Ty; + if (parseSILType(Ty) || + P.parseToken(tok::l_paren, diag::expected_tok_in_sil_instr, "(")) + return true; - // Parse a list of SILValue. - bool OpsAreTailElems = false; - unsigned NumBaseElems = 0; - if (P.Tok.isNot(tok::r_paren)) { - do { - if (Opcode == SILInstructionKind::ObjectInst) { - if (parseSILOptional(OpsAreTailElems, *this, "tail_elems")) + // Parse a list of SILValue. + bool OpsAreTailElems = false; + unsigned NumBaseElems = 0; + if (P.Tok.isNot(tok::r_paren)) { + do { + if (Opcode == SILInstructionKind::ObjectInst) { + if (parseSILOptional(OpsAreTailElems, *this, "tail_elems")) + return true; + } + if (parseTypedValueRef(Val, B)) return true; - } - if (parseTypedValueRef(Val, B)) return true; - OpList.push_back(Val); - if (!OpsAreTailElems) - NumBaseElems = OpList.size(); - } while (P.consumeIf(tok::comma)); - } - if (P.parseToken(tok::r_paren, - diag::expected_tok_in_sil_instr,")") || - parseSILDebugLocation(InstLoc, B)) - return true; + OpList.push_back(Val); + if (!OpsAreTailElems) + NumBaseElems = OpList.size(); + } while (P.consumeIf(tok::comma)); + } + if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")") || + parseSILDebugLocation(InstLoc, B)) + return true; - if (Opcode == SILInstructionKind::StructInst) { - ResultVal = B.createStruct(InstLoc, Ty, OpList); - } else { - ResultVal = B.createObject(InstLoc, Ty, OpList, NumBaseElems); + if (Opcode == SILInstructionKind::StructInst) { + ResultVal = B.createStruct(InstLoc, Ty, OpList); + } else { + ResultVal = B.createObject(InstLoc, Ty, OpList, NumBaseElems); + } + break; } - break; - } - case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::StructExtractInst: { - ValueDecl *FieldV; - SourceLoc NameLoc = P.Tok.getLoc(); - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDottedPath(FieldV) || - parseSILDebugLocation(InstLoc, B)) - return true; - if (!FieldV || !isa(FieldV)) { - P.diagnose(NameLoc, diag::sil_struct_inst_wrong_field); - return true; + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::StructExtractInst: { + ValueDecl *FieldV; + SourceLoc NameLoc = P.Tok.getLoc(); + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDottedPath(FieldV) || parseSILDebugLocation(InstLoc, B)) + return true; + if (!FieldV || !isa(FieldV)) { + P.diagnose(NameLoc, diag::sil_struct_inst_wrong_field); + return true; + } + VarDecl *Field = cast(FieldV); + + // FIXME: substitution means this type should be explicit to improve + // performance. + auto ResultTy = Val->getType().getFieldType(Field, SILMod, + B.getTypeExpansionContext()); + if (Opcode == SILInstructionKind::StructElementAddrInst) + ResultVal = B.createStructElementAddr(InstLoc, Val, Field, + ResultTy.getAddressType()); + else + ResultVal = B.createStructExtract(InstLoc, Val, Field, + ResultTy.getObjectType()); + break; } - VarDecl *Field = cast(FieldV); - - // FIXME: substitution means this type should be explicit to improve - // performance. - auto ResultTy = Val->getType().getFieldType(Field, SILMod); - if (Opcode == SILInstructionKind::StructElementAddrInst) - ResultVal = B.createStructElementAddr(InstLoc, Val, Field, - ResultTy.getAddressType()); - else - ResultVal = B.createStructExtract(InstLoc, Val, Field, - ResultTy.getObjectType()); - break; - } - case SILInstructionKind::RefElementAddrInst: { - ValueDecl *FieldV; - SourceLoc NameLoc; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDottedPath(FieldV) || - parseSILDebugLocation(InstLoc, B)) - return true; - if (!FieldV || !isa(FieldV)) { - P.diagnose(NameLoc, diag::sil_ref_inst_wrong_field); - return true; + case SILInstructionKind::RefElementAddrInst: { + ValueDecl *FieldV; + SourceLoc NameLoc; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDottedPath(FieldV) || parseSILDebugLocation(InstLoc, B)) + return true; + if (!FieldV || !isa(FieldV)) { + P.diagnose(NameLoc, diag::sil_ref_inst_wrong_field); + return true; + } + VarDecl *Field = cast(FieldV); + auto ResultTy = Val->getType().getFieldType(Field, SILMod, + B.getTypeExpansionContext()); + ResultVal = B.createRefElementAddr(InstLoc, Val, Field, ResultTy); + break; } - VarDecl *Field = cast(FieldV); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); - ResultVal = B.createRefElementAddr(InstLoc, Val, Field, ResultTy); - break; - } - case SILInstructionKind::RefTailAddrInst: { - SourceLoc NameLoc; - SILType ResultObjTy; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(ResultObjTy) || - parseSILDebugLocation(InstLoc, B)) - return true; - SILType ResultTy = ResultObjTy.getAddressType(); - ResultVal = B.createRefTailAddr(InstLoc, Val, ResultTy); - break; - } - case SILInstructionKind::IndexAddrInst: { - SILValue IndexVal; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(IndexVal, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createIndexAddr(InstLoc, Val, IndexVal); - break; - } - case SILInstructionKind::TailAddrInst: { - SILValue IndexVal; - SILType ResultObjTy; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(IndexVal, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(ResultObjTy) || - parseSILDebugLocation(InstLoc, B)) - return true; - SILType ResultTy = ResultObjTy.getAddressType(); - ResultVal = B.createTailAddr(InstLoc, Val, IndexVal, ResultTy); - break; - } - case SILInstructionKind::IndexRawPointerInst: { - SILValue IndexVal; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseTypedValueRef(IndexVal, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createIndexRawPointer(InstLoc, Val, IndexVal); - break; - } - case SILInstructionKind::ObjCProtocolInst: { - Identifier ProtocolName; - SILType Ty; - if (P.parseToken(tok::pound, diag::expected_sil_constant) || - parseSILIdentifier(ProtocolName, diag::expected_sil_constant) || - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - parseSILType(Ty) || - parseSILDebugLocation(InstLoc, B)) - return true; - // Find the decl for the protocol name. - ValueDecl *VD; - SmallVector CurModuleResults; - // Perform a module level lookup on the first component of the - // fully-qualified name. - P.SF.getParentModule()->lookupValue(ProtocolName, - NLKind::UnqualifiedLookup, - CurModuleResults); - assert(CurModuleResults.size() == 1); - VD = CurModuleResults[0]; - ResultVal = B.createObjCProtocol(InstLoc, cast(VD), Ty); - break; - } - case SILInstructionKind::AllocGlobalInst: { - Identifier GlobalName; - SourceLoc IdLoc; - if (P.parseToken(tok::at_sign, diag::expected_sil_value_name) || - parseSILIdentifier(GlobalName, IdLoc, diag::expected_sil_value_name) || - parseSILDebugLocation(InstLoc, B)) - return true; - - // Go through list of global variables in the SILModule. - SILGlobalVariable *global = SILMod.lookUpGlobalVariable(GlobalName.str()); - if (!global) { - P.diagnose(IdLoc, diag::sil_global_variable_not_found, GlobalName); - return true; + case SILInstructionKind::RefTailAddrInst: { + SourceLoc NameLoc; + SILType ResultObjTy; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(ResultObjTy) || parseSILDebugLocation(InstLoc, B)) + return true; + SILType ResultTy = ResultObjTy.getAddressType(); + ResultVal = B.createRefTailAddr(InstLoc, Val, ResultTy); + break; } - - ResultVal = B.createAllocGlobal(InstLoc, global); - break; - } - case SILInstructionKind::GlobalAddrInst: - case SILInstructionKind::GlobalValueInst: { - Identifier GlobalName; - SourceLoc IdLoc; - SILType Ty; - if (P.parseToken(tok::at_sign, diag::expected_sil_value_name) || - parseSILIdentifier(GlobalName, IdLoc, diag::expected_sil_value_name) || - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - parseSILType(Ty) || - parseSILDebugLocation(InstLoc, B)) - return true; - - // Go through list of global variables in the SILModule. - SILGlobalVariable *global = SILMod.lookUpGlobalVariable(GlobalName.str()); - if (!global) { - P.diagnose(IdLoc, diag::sil_global_variable_not_found, GlobalName); - return true; + case SILInstructionKind::IndexAddrInst: { + SILValue IndexVal; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(IndexVal, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createIndexAddr(InstLoc, Val, IndexVal); + break; } - - SILType expectedType = (Opcode == SILInstructionKind::GlobalAddrInst ? - global->getLoweredType().getAddressType() : - global->getLoweredType()); - if (expectedType != Ty) { - P.diagnose(IdLoc, diag::sil_value_use_type_mismatch, GlobalName.str(), - global->getLoweredType().getASTType(), - Ty.getASTType()); - return true; + case SILInstructionKind::TailAddrInst: { + SILValue IndexVal; + SILType ResultObjTy; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(IndexVal, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(ResultObjTy) || parseSILDebugLocation(InstLoc, B)) + return true; + SILType ResultTy = ResultObjTy.getAddressType(); + ResultVal = B.createTailAddr(InstLoc, Val, IndexVal, ResultTy); + break; + } + case SILInstructionKind::IndexRawPointerInst: { + SILValue IndexVal; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(IndexVal, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createIndexRawPointer(InstLoc, Val, IndexVal); + break; } + case SILInstructionKind::ObjCProtocolInst: { + Identifier ProtocolName; + SILType Ty; + if (P.parseToken(tok::pound, diag::expected_sil_constant) || + parseSILIdentifier(ProtocolName, diag::expected_sil_constant) || + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(Ty) || parseSILDebugLocation(InstLoc, B)) + return true; + // Find the decl for the protocol name. + ValueDecl *VD; + SmallVector CurModuleResults; + // Perform a module level lookup on the first component of the + // fully-qualified name. + P.SF.getParentModule()->lookupValue( + ProtocolName, NLKind::UnqualifiedLookup, CurModuleResults); + assert(CurModuleResults.size() == 1); + VD = CurModuleResults[0]; + ResultVal = B.createObjCProtocol(InstLoc, cast(VD), Ty); + break; + } + case SILInstructionKind::AllocGlobalInst: { + Identifier GlobalName; + SourceLoc IdLoc; + if (P.parseToken(tok::at_sign, diag::expected_sil_value_name) || + parseSILIdentifier(GlobalName, IdLoc, + diag::expected_sil_value_name) || + parseSILDebugLocation(InstLoc, B)) + return true; - if (Opcode == SILInstructionKind::GlobalAddrInst) { - ResultVal = B.createGlobalAddr(InstLoc, global); - } else { - ResultVal = B.createGlobalValue(InstLoc, global); + // Go through list of global variables in the SILModule. + SILGlobalVariable *global = SILMod.lookUpGlobalVariable(GlobalName.str()); + if (!global) { + P.diagnose(IdLoc, diag::sil_global_variable_not_found, GlobalName); + return true; + } + + ResultVal = B.createAllocGlobal(InstLoc, global); + break; } - break; - } - case SILInstructionKind::SelectEnumInst: - case SILInstructionKind::SelectEnumAddrInst: { - if (parseTypedValueRef(Val, B)) - return true; + case SILInstructionKind::GlobalAddrInst: + case SILInstructionKind::GlobalValueInst: { + Identifier GlobalName; + SourceLoc IdLoc; + SILType Ty; + if (P.parseToken(tok::at_sign, diag::expected_sil_value_name) || + parseSILIdentifier(GlobalName, IdLoc, + diag::expected_sil_value_name) || + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(Ty) || parseSILDebugLocation(InstLoc, B)) + return true; - SmallVector, 4> - CaseValueNames; - Optional DefaultValueName; - while (P.consumeIf(tok::comma)) { - Identifier BBName; - SourceLoc BBLoc; - // Parse 'default' sil-value. - UnresolvedValueName tmp; - if (P.consumeIf(tok::kw_default)) { - if (parseValueName(tmp)) - return true; - DefaultValueName = tmp; - break; + // Go through list of global variables in the SILModule. + SILGlobalVariable *global = SILMod.lookUpGlobalVariable(GlobalName.str()); + if (!global) { + P.diagnose(IdLoc, diag::sil_global_variable_not_found, GlobalName); + return true; } - // Parse 'case' sil-decl-ref ':' sil-value. - if (P.consumeIf(tok::kw_case)) { - SILDeclRef ElemRef; - if (parseSILDeclRef(ElemRef)) - return true; - assert(ElemRef.hasDecl() && isa(ElemRef.getDecl())); - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); - parseValueName(tmp); - CaseValueNames.push_back(std::make_pair( - cast(ElemRef.getDecl()), - tmp)); - continue; + SILType expectedType = (Opcode == SILInstructionKind::GlobalAddrInst + ? global->getLoweredType().getAddressType() + : global->getLoweredType()); + if (expectedType != Ty) { + P.diagnose(IdLoc, diag::sil_value_use_type_mismatch, GlobalName.str(), + global->getLoweredType().getASTType(), Ty.getASTType()); + return true; } - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); - return true; + if (Opcode == SILInstructionKind::GlobalAddrInst) { + ResultVal = B.createGlobalAddr(InstLoc, global); + } else { + ResultVal = B.createGlobalValue(InstLoc, global); + } + break; } - - // Parse the type of the result operands. - SILType ResultType; - if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") - || parseSILType(ResultType) || - parseSILDebugLocation(InstLoc, B)) - return true; - - // Resolve the results. - SmallVector, 4> CaseValues; - SILValue DefaultValue; - if (DefaultValueName) - DefaultValue = getLocalValue(*DefaultValueName, ResultType, InstLoc, B); - for (auto &caseName : CaseValueNames) - CaseValues.push_back(std::make_pair( - caseName.first, - getLocalValue(caseName.second, ResultType, InstLoc, B))); - - if (Opcode == SILInstructionKind::SelectEnumInst) - ResultVal = B.createSelectEnum(InstLoc, Val, ResultType, - DefaultValue, CaseValues); - else - ResultVal = B.createSelectEnumAddr(InstLoc, Val, ResultType, - DefaultValue, CaseValues); - break; - } - - case SILInstructionKind::SwitchEnumInst: - case SILInstructionKind::SwitchEnumAddrInst: { - if (parseTypedValueRef(Val, B)) - return true; + case SILInstructionKind::SelectEnumInst: + case SILInstructionKind::SelectEnumAddrInst: { + if (parseTypedValueRef(Val, B)) + return true; - SmallVector, 4> CaseBBs; - SILBasicBlock *DefaultBB = nullptr; - while (!peekSILDebugLocation(P) && P.consumeIf(tok::comma)) { - Identifier BBName; - SourceLoc BBLoc; - // Parse 'default' sil-identifier. - if (P.consumeIf(tok::kw_default)) { - parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); - DefaultBB = getBBForReference(BBName, BBLoc); - break; - } + SmallVector, 4> + CaseValueNames; + Optional DefaultValueName; + while (P.consumeIf(tok::comma)) { + Identifier BBName; + SourceLoc BBLoc; + // Parse 'default' sil-value. + UnresolvedValueName tmp; + if (P.consumeIf(tok::kw_default)) { + if (parseValueName(tmp)) + return true; + DefaultValueName = tmp; + break; + } - // Parse 'case' sil-decl-ref ':' sil-identifier. - if (P.consumeIf(tok::kw_case)) { - SILDeclRef ElemRef; - if (parseSILDeclRef(ElemRef)) - return true; - assert(ElemRef.hasDecl() && isa(ElemRef.getDecl())); - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); - parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); - CaseBBs.push_back( {cast(ElemRef.getDecl()), - getBBForReference(BBName, BBLoc)} ); - continue; + // Parse 'case' sil-decl-ref ':' sil-value. + if (P.consumeIf(tok::kw_case)) { + SILDeclRef ElemRef; + if (parseSILDeclRef(ElemRef)) + return true; + assert(ElemRef.hasDecl() && isa(ElemRef.getDecl())); + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); + parseValueName(tmp); + CaseValueNames.push_back( + std::make_pair(cast(ElemRef.getDecl()), tmp)); + continue; + } + + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); + return true; } - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); - return true; + // Parse the type of the result operands. + SILType ResultType; + if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(ResultType) || parseSILDebugLocation(InstLoc, B)) + return true; + + // Resolve the results. + SmallVector, 4> CaseValues; + SILValue DefaultValue; + if (DefaultValueName) + DefaultValue = getLocalValue(*DefaultValueName, ResultType, InstLoc, B); + for (auto &caseName : CaseValueNames) + CaseValues.push_back(std::make_pair( + caseName.first, + getLocalValue(caseName.second, ResultType, InstLoc, B))); + + if (Opcode == SILInstructionKind::SelectEnumInst) + ResultVal = B.createSelectEnum(InstLoc, Val, ResultType, DefaultValue, + CaseValues); + else + ResultVal = B.createSelectEnumAddr(InstLoc, Val, ResultType, + DefaultValue, CaseValues); + break; } - if (parseSILDebugLocation(InstLoc, B)) - return true; - if (Opcode == SILInstructionKind::SwitchEnumInst) - ResultVal = B.createSwitchEnum(InstLoc, Val, DefaultBB, CaseBBs); - else - ResultVal = B.createSwitchEnumAddr(InstLoc, Val, DefaultBB, CaseBBs); - break; - } - case SILInstructionKind::SwitchValueInst: { - if (parseTypedValueRef(Val, B)) - return true; - SmallVector, 4> CaseBBs; - SILBasicBlock *DefaultBB = nullptr; - while (!peekSILDebugLocation(P) && P.consumeIf(tok::comma)) { - Identifier BBName; - SourceLoc BBLoc; - SILValue CaseVal; - - // Parse 'default' sil-identifier. - if (P.consumeIf(tok::kw_default)) { - parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); - DefaultBB = getBBForReference(BBName, BBLoc); - break; - } + case SILInstructionKind::SwitchEnumInst: + case SILInstructionKind::SwitchEnumAddrInst: { + if (parseTypedValueRef(Val, B)) + return true; - // Parse 'case' value-ref ':' sil-identifier. - if (P.consumeIf(tok::kw_case)) { - if (parseValueRef(CaseVal, Val->getType(), - RegularLocation(P.Tok.getLoc()), B)) { - // TODO: Issue a proper error message here - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "reference to a value"); - return true; + SmallVector, 4> CaseBBs; + SILBasicBlock *DefaultBB = nullptr; + while (!peekSILDebugLocation(P) && P.consumeIf(tok::comma)) { + Identifier BBName; + SourceLoc BBLoc; + // Parse 'default' sil-identifier. + if (P.consumeIf(tok::kw_default)) { + parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); + DefaultBB = getBBForReference(BBName, BBLoc); + break; } - auto intTy = Val->getType().getAs(); - auto functionTy = Val->getType().getAs(); - if (!intTy && !functionTy) { - P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); - return true; + // Parse 'case' sil-decl-ref ':' sil-identifier. + if (P.consumeIf(tok::kw_case)) { + SILDeclRef ElemRef; + if (parseSILDeclRef(ElemRef)) + return true; + assert(ElemRef.hasDecl() && isa(ElemRef.getDecl())); + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); + parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); + CaseBBs.push_back({cast(ElemRef.getDecl()), + getBBForReference(BBName, BBLoc)}); + continue; + } + + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); + return true; + } + if (parseSILDebugLocation(InstLoc, B)) + return true; + if (Opcode == SILInstructionKind::SwitchEnumInst) + ResultVal = B.createSwitchEnum(InstLoc, Val, DefaultBB, CaseBBs); + else + ResultVal = B.createSwitchEnumAddr(InstLoc, Val, DefaultBB, CaseBBs); + break; + } + case SILInstructionKind::SwitchValueInst: { + if (parseTypedValueRef(Val, B)) + return true; + + SmallVector, 4> CaseBBs; + SILBasicBlock *DefaultBB = nullptr; + while (!peekSILDebugLocation(P) && P.consumeIf(tok::comma)) { + Identifier BBName; + SourceLoc BBLoc; + SILValue CaseVal; + + // Parse 'default' sil-identifier. + if (P.consumeIf(tok::kw_default)) { + parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); + DefaultBB = getBBForReference(BBName, BBLoc); + break; } - if (intTy) { - // If it is a switch on an integer type, check that all case values - // are integer literals or undef. - if (!isa(CaseVal)) { - auto *IL = dyn_cast(CaseVal); - if (!IL) { - P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); - return true; - } - APInt CaseValue = IL->getValue(); + // Parse 'case' value-ref ':' sil-identifier. + if (P.consumeIf(tok::kw_case)) { + if (parseValueRef(CaseVal, Val->getType(), + RegularLocation(P.Tok.getLoc()), B)) { + // TODO: Issue a proper error message here + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, + "reference to a value"); + return true; + } - if (CaseValue.getBitWidth() != intTy->getGreatestWidth()) - CaseVal = B.createIntegerLiteral( - IL->getLoc(), Val->getType(), - CaseValue.zextOrTrunc(intTy->getGreatestWidth())); + auto intTy = Val->getType().getAs(); + auto functionTy = Val->getType().getAs(); + if (!intTy && !functionTy) { + P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); + return true; } - } - if (functionTy) { - // If it is a switch on a function type, check that all case values - // are function references or undef. - if (!isa(CaseVal)) { - auto *FR = dyn_cast(CaseVal); - if (!FR) { - if (auto *CF = dyn_cast(CaseVal)) { - FR = dyn_cast(CF->getOperand()); + if (intTy) { + // If it is a switch on an integer type, check that all case values + // are integer literals or undef. + if (!isa(CaseVal)) { + auto *IL = dyn_cast(CaseVal); + if (!IL) { + P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); + return true; } + APInt CaseValue = IL->getValue(); + + if (CaseValue.getBitWidth() != intTy->getGreatestWidth()) + CaseVal = B.createIntegerLiteral( + IL->getLoc(), Val->getType(), + CaseValue.zextOrTrunc(intTy->getGreatestWidth())); } - if (!FR) { - P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); - return true; + } + + if (functionTy) { + // If it is a switch on a function type, check that all case values + // are function references or undef. + if (!isa(CaseVal)) { + auto *FR = dyn_cast(CaseVal); + if (!FR) { + if (auto *CF = dyn_cast(CaseVal)) { + FR = dyn_cast(CF->getOperand()); + } + } + if (!FR) { + P.diagnose(P.Tok, diag::sil_integer_literal_not_integer_type); + return true; + } } } + + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); + parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); + CaseBBs.push_back({CaseVal, getBBForReference(BBName, BBLoc)}); + continue; } - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); - parseSILIdentifier(BBName, BBLoc, diag::expected_sil_block_name); - CaseBBs.push_back({CaseVal, getBBForReference(BBName, BBLoc)}); - continue; + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); + return true; } - - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); - return true; + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createSwitchValue(InstLoc, Val, DefaultBB, CaseBBs); + break; } - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createSwitchValue(InstLoc, Val, DefaultBB, CaseBBs); - break; - } - case SILInstructionKind::SelectValueInst: { - if (parseTypedValueRef(Val, B)) - return true; + case SILInstructionKind::SelectValueInst: { + if (parseTypedValueRef(Val, B)) + return true; - SmallVector, 4> - CaseValueAndResultNames; - Optional DefaultResultName; - while (P.consumeIf(tok::comma)) { - Identifier BBName; - SourceLoc BBLoc; - // Parse 'default' sil-value. - UnresolvedValueName tmp; - if (P.consumeIf(tok::kw_default)) { - if (parseValueName(tmp)) - return true; - DefaultResultName = tmp; - break; + SmallVector, 4> + CaseValueAndResultNames; + Optional DefaultResultName; + while (P.consumeIf(tok::comma)) { + Identifier BBName; + SourceLoc BBLoc; + // Parse 'default' sil-value. + UnresolvedValueName tmp; + if (P.consumeIf(tok::kw_default)) { + if (parseValueName(tmp)) + return true; + DefaultResultName = tmp; + break; + } + + // Parse 'case' sil-decl-ref ':' sil-value. + if (P.consumeIf(tok::kw_case)) { + UnresolvedValueName casevalue; + parseValueName(casevalue); + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); + parseValueName(tmp); + CaseValueAndResultNames.push_back(std::make_pair(casevalue, tmp)); + continue; + } + + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); + return true; } - // Parse 'case' sil-decl-ref ':' sil-value. - if (P.consumeIf(tok::kw_case)) { - UnresolvedValueName casevalue; - parseValueName(casevalue); - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":"); - parseValueName(tmp); - CaseValueAndResultNames.push_back(std::make_pair( - casevalue, - tmp)); - continue; + if (!DefaultResultName) { + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "default"); + return true; } - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "case or default"); - return true; + // Parse the type of the result operands. + SILType ResultType; + if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(ResultType) || parseSILDebugLocation(InstLoc, B)) + return true; + + // Resolve the results. + SmallVector, 4> CaseValues; + SILValue DefaultValue; + if (DefaultResultName) + DefaultValue = + getLocalValue(*DefaultResultName, ResultType, InstLoc, B); + SILType ValType = Val->getType(); + for (auto &caseName : CaseValueAndResultNames) + CaseValues.push_back(std::make_pair( + getLocalValue(caseName.first, ValType, InstLoc, B), + getLocalValue(caseName.second, ResultType, InstLoc, B))); + + ResultVal = B.createSelectValue(InstLoc, Val, ResultType, DefaultValue, + CaseValues); + break; + } + case SILInstructionKind::DeinitExistentialAddrInst: { + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createDeinitExistentialAddr(InstLoc, Val); + break; } + case SILInstructionKind::DeinitExistentialValueInst: { + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createDeinitExistentialValue(InstLoc, Val); + break; + } + case SILInstructionKind::InitExistentialAddrInst: { + CanType Ty; + SourceLoc TyLoc; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(Ty, TyLoc) || parseSILDebugLocation(InstLoc, B)) + return true; - if (!DefaultResultName) { - P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "default"); - return true; + // Lower the type at the abstraction level of the existential. + auto archetype = OpenedArchetypeType::get(Val->getType().getASTType()) + ->getCanonicalType(); + + auto &F = B.getFunction(); + SILType LoweredTy = + F.getLoweredType(Lowering::AbstractionPattern(archetype), Ty) + .getAddressType(); + + // Collect conformances for the type. + ArrayRef conformances = + collectExistentialConformances(P, Ty, TyLoc, + Val->getType().getASTType()); + + ResultVal = B.createInitExistentialAddr(InstLoc, Val, Ty, LoweredTy, + conformances); + break; } + case SILInstructionKind::InitExistentialValueInst: { + CanType FormalConcreteTy; + SILType ExistentialTy; + SourceLoc TyLoc; + + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(FormalConcreteTy, TyLoc) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(ExistentialTy) || parseSILDebugLocation(InstLoc, B)) + return true; - // Parse the type of the result operands. - SILType ResultType; - if (P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - parseSILType(ResultType) || - parseSILDebugLocation(InstLoc, B)) - return true; + ArrayRef conformances = + collectExistentialConformances(P, FormalConcreteTy, TyLoc, + ExistentialTy.getASTType()); - // Resolve the results. - SmallVector, 4> CaseValues; - SILValue DefaultValue; - if (DefaultResultName) - DefaultValue = getLocalValue(*DefaultResultName, ResultType, InstLoc, B); - SILType ValType = Val->getType(); - for (auto &caseName : CaseValueAndResultNames) - CaseValues.push_back(std::make_pair( - getLocalValue(caseName.first, ValType, InstLoc, B), - getLocalValue(caseName.second, ResultType, InstLoc, B))); + ResultVal = B.createInitExistentialValue( + InstLoc, ExistentialTy, FormalConcreteTy, Val, conformances); + break; + } + case SILInstructionKind::AllocExistentialBoxInst: { + SILType ExistentialTy; + CanType ConcreteFormalTy; + SourceLoc TyLoc; - ResultVal = B.createSelectValue(InstLoc, Val, ResultType, - DefaultValue, CaseValues); - break; - } - case SILInstructionKind::DeinitExistentialAddrInst: { - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createDeinitExistentialAddr(InstLoc, Val); - break; - } - case SILInstructionKind::DeinitExistentialValueInst: { - if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createDeinitExistentialValue(InstLoc, Val); - break; - } - case SILInstructionKind::InitExistentialAddrInst: { - CanType Ty; - SourceLoc TyLoc; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || - parseASTType(Ty, TyLoc) || - parseSILDebugLocation(InstLoc, B)) - return true; - - // Lower the type at the abstraction level of the existential. - auto archetype - = OpenedArchetypeType::get(Val->getType().getASTType()) - ->getCanonicalType(); - - auto &F = B.getFunction(); - SILType LoweredTy = F.getLoweredType( - Lowering::AbstractionPattern(archetype), Ty) - .getAddressType(); - - // Collect conformances for the type. - ArrayRef conformances - = collectExistentialConformances(P, Ty, TyLoc, - Val->getType().getASTType()); - - ResultVal = B.createInitExistentialAddr(InstLoc, Val, Ty, LoweredTy, - conformances); - break; - } - case SILInstructionKind::InitExistentialValueInst: { - CanType FormalConcreteTy; - SILType ExistentialTy; - SourceLoc TyLoc; + if (parseSILType(ExistentialTy) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(ConcreteFormalTy, TyLoc) || + parseSILDebugLocation(InstLoc, B)) + return true; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || - parseASTType(FormalConcreteTy, TyLoc) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(ExistentialTy) || parseSILDebugLocation(InstLoc, B)) - return true; + // Collect conformances for the type. + ArrayRef conformances = + collectExistentialConformances(P, ConcreteFormalTy, TyLoc, + ExistentialTy.getASTType()); - ArrayRef conformances = - collectExistentialConformances(P, FormalConcreteTy, TyLoc, - ExistentialTy.getASTType()); + ResultVal = B.createAllocExistentialBox(InstLoc, ExistentialTy, + ConcreteFormalTy, conformances); - ResultVal = B.createInitExistentialValue( - InstLoc, ExistentialTy, FormalConcreteTy, Val, conformances); - break; - } - case SILInstructionKind::AllocExistentialBoxInst: { - SILType ExistentialTy; - CanType ConcreteFormalTy; - SourceLoc TyLoc; - - if (parseSILType(ExistentialTy) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || - parseASTType(ConcreteFormalTy, TyLoc) || - parseSILDebugLocation(InstLoc, B)) - return true; - - // Collect conformances for the type. - ArrayRef conformances - = collectExistentialConformances(P, ConcreteFormalTy, TyLoc, - ExistentialTy.getASTType()); - - ResultVal = B.createAllocExistentialBox(InstLoc, ExistentialTy, - ConcreteFormalTy, conformances); - - break; - } - case SILInstructionKind::InitExistentialRefInst: { - CanType FormalConcreteTy; - SILType ExistentialTy; - SourceLoc TyLoc; + break; + } + case SILInstructionKind::InitExistentialRefInst: { + CanType FormalConcreteTy; + SILType ExistentialTy; + SourceLoc TyLoc; + + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || + parseASTType(FormalConcreteTy, TyLoc) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(ExistentialTy) || parseSILDebugLocation(InstLoc, B)) + return true; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - P.parseToken(tok::sil_dollar, diag::expected_tok_in_sil_instr, "$") || - parseASTType(FormalConcreteTy, TyLoc) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(ExistentialTy) || - parseSILDebugLocation(InstLoc, B)) - return true; - - ArrayRef conformances - = collectExistentialConformances(P, FormalConcreteTy, TyLoc, - ExistentialTy.getASTType()); - - // FIXME: Conformances in InitExistentialRefInst is currently not included - // in SIL.rst. - ResultVal = B.createInitExistentialRef(InstLoc, ExistentialTy, - FormalConcreteTy, Val, - conformances); - break; - } - case SILInstructionKind::InitExistentialMetatypeInst: { - SourceLoc TyLoc; - SILType ExistentialTy; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILType(ExistentialTy, TyLoc) || - parseSILDebugLocation(InstLoc, B)) - return true; + ArrayRef conformances = + collectExistentialConformances(P, FormalConcreteTy, TyLoc, + ExistentialTy.getASTType()); - auto baseExType = ExistentialTy.getASTType(); - auto formalConcreteType = Val->getType().getASTType(); - while (auto instExType = dyn_cast(baseExType)) { - baseExType = instExType.getInstanceType(); - formalConcreteType = - cast(formalConcreteType).getInstanceType(); + // FIXME: Conformances in InitExistentialRefInst is currently not included + // in SIL.rst. + ResultVal = B.createInitExistentialRef( + InstLoc, ExistentialTy, FormalConcreteTy, Val, conformances); + break; } + case SILInstructionKind::InitExistentialMetatypeInst: { + SourceLoc TyLoc; + SILType ExistentialTy; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILType(ExistentialTy, TyLoc) || + parseSILDebugLocation(InstLoc, B)) + return true; - ArrayRef conformances - = collectExistentialConformances(P, formalConcreteType, TyLoc, - baseExType); - - ResultVal = B.createInitExistentialMetatype(InstLoc, Val, ExistentialTy, - conformances); - break; - } - case SILInstructionKind::DynamicMethodBranchInst: { - SILDeclRef Member; - Identifier BBName, BBName2; - SourceLoc NameLoc, NameLoc2; - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILDeclRef(Member) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(BBName2, NameLoc2, - diag::expected_sil_block_name) || - parseSILDebugLocation(InstLoc, B)) - return true; + auto baseExType = ExistentialTy.getASTType(); + auto formalConcreteType = Val->getType().getASTType(); + while (auto instExType = dyn_cast(baseExType)) { + baseExType = instExType.getInstanceType(); + formalConcreteType = + cast(formalConcreteType).getInstanceType(); + } - ResultVal = B.createDynamicMethodBranch(InstLoc, Val, Member, - getBBForReference(BBName, NameLoc), - getBBForReference(BBName2, - NameLoc2)); - break; - } - case SILInstructionKind::ProjectBlockStorageInst: { - if (parseTypedValueRef(Val, B) || - parseSILDebugLocation(InstLoc, B)) - return true; - - ResultVal = B.createProjectBlockStorage(InstLoc, Val); - break; - } - case SILInstructionKind::InitBlockStorageHeaderInst: { - Identifier invoke, type; - SourceLoc invokeLoc, typeLoc; - - UnresolvedValueName invokeName; - SILType invokeTy; - GenericEnvironment *invokeGenericEnv; - - SILType blockType; - SmallVector parsedSubs; + ArrayRef conformances = + collectExistentialConformances(P, formalConcreteType, TyLoc, + baseExType); - - if (parseTypedValueRef(Val, B) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(invoke, invokeLoc, - diag::expected_tok_in_sil_instr, "invoke") || - parseValueName(invokeName) || - parseSubstitutions(parsedSubs) || - P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || - parseSILType(invokeTy, invokeGenericEnv) || - P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseSILIdentifier(type, typeLoc, - diag::expected_tok_in_sil_instr, "type") || - parseSILType(blockType) || - parseSILDebugLocation(InstLoc, B)) - return true; - - if (invoke.str() != "invoke") { - P.diagnose(invokeLoc, diag::expected_tok_in_sil_instr, "invoke"); - return true; + ResultVal = B.createInitExistentialMetatype(InstLoc, Val, ExistentialTy, + conformances); + break; } - if (type.str() != "type") { - P.diagnose(invokeLoc, diag::expected_tok_in_sil_instr, "type"); - return true; + case SILInstructionKind::DynamicMethodBranchInst: { + SILDeclRef Member; + Identifier BBName, BBName2; + SourceLoc NameLoc, NameLoc2; + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILDeclRef(Member) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(BBName, NameLoc, diag::expected_sil_block_name) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(BBName2, NameLoc2, + diag::expected_sil_block_name) || + parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createDynamicMethodBranch( + InstLoc, Val, Member, getBBForReference(BBName, NameLoc), + getBBForReference(BBName2, NameLoc2)); + break; } - - auto invokeVal = getLocalValue(invokeName, invokeTy, InstLoc, B); + case SILInstructionKind::ProjectBlockStorageInst: { + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; - SubstitutionMap subMap; - if (!parsedSubs.empty()) { - if (!invokeGenericEnv) { - P.diagnose(typeLoc, diag::sil_substitutions_on_non_polymorphic_type); + ResultVal = B.createProjectBlockStorage(InstLoc, Val); + break; + } + case SILInstructionKind::InitBlockStorageHeaderInst: { + Identifier invoke, type; + SourceLoc invokeLoc, typeLoc; + + UnresolvedValueName invokeName; + SILType invokeTy; + GenericEnvironment *invokeGenericEnv; + + SILType blockType; + SmallVector parsedSubs; + + if (parseTypedValueRef(Val, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(invoke, invokeLoc, diag::expected_tok_in_sil_instr, + "invoke") || + parseValueName(invokeName) || parseSubstitutions(parsedSubs) || + P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || + parseSILType(invokeTy, invokeGenericEnv) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseSILIdentifier(type, typeLoc, diag::expected_tok_in_sil_instr, + "type") || + parseSILType(blockType) || parseSILDebugLocation(InstLoc, B)) + return true; + + if (invoke.str() != "invoke") { + P.diagnose(invokeLoc, diag::expected_tok_in_sil_instr, "invoke"); + return true; + } + if (type.str() != "type") { + P.diagnose(invokeLoc, diag::expected_tok_in_sil_instr, "type"); return true; } - subMap = getApplySubstitutionsFromParsed(*this, invokeGenericEnv, - parsedSubs); - if (!subMap) + auto invokeVal = getLocalValue(invokeName, invokeTy, InstLoc, B); + + SubstitutionMap subMap; + if (!parsedSubs.empty()) { + if (!invokeGenericEnv) { + P.diagnose(typeLoc, diag::sil_substitutions_on_non_polymorphic_type); + return true; + } + + subMap = getApplySubstitutionsFromParsed(*this, invokeGenericEnv, + parsedSubs); + if (!subMap) + return true; + } + + ResultVal = B.createInitBlockStorageHeader(InstLoc, Val, invokeVal, + blockType, subMap); + break; + } + } + + return false; +} + +/// sil-instruction-result ::= sil-value-name '=' +/// sil-instruction-result ::= '(' sil-value-name? ')' +/// sil-instruction-result ::= '(' sil-value-name (',' sil-value-name)* ')' +/// sil-instruction-source-info ::= (',' sil-scope-ref)? (',' sil-loc)? +/// sil-instruction-def ::= +/// (sil-instruction-result '=')? sil-instruction sil-instruction-source-info +bool SILParser::parseSILInstruction(SILBuilder &B) { + // We require SIL instructions to be at the start of a line to assist + // recovery. + if (!P.Tok.isAtStartOfLine()) { + P.diagnose(P.Tok, diag::expected_sil_instr_start_of_line); + return true; + } + + SmallVector, 4> resultNames; + SourceLoc resultClauseBegin; + + // If the instruction has a name '%foo =', parse it. + if (P.Tok.is(tok::sil_local_name)) { + resultClauseBegin = P.Tok.getLoc(); + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); + P.consumeToken(tok::sil_local_name); + + // If the instruction has a '(%foo, %bar) = ', parse it. + } else if (P.consumeIf(tok::l_paren)) { + resultClauseBegin = P.PreviousLoc; + + if (!P.consumeIf(tok::r_paren)) { + while (true) { + if (!P.Tok.is(tok::sil_local_name)) { + P.diagnose(P.Tok, diag::expected_sil_value_name); + return true; + } + + resultNames.push_back({P.Tok.getText(), P.Tok.getLoc()}); + P.consumeToken(tok::sil_local_name); + + if (P.consumeIf(tok::comma)) + continue; + if (P.consumeIf(tok::r_paren)) + break; + + P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, ","); return true; + } } - - ResultVal = B.createInitBlockStorageHeader(InstLoc, Val, invokeVal, - blockType, subMap); - break; } + + if (resultClauseBegin.isValid()) { + if (P.parseToken(tok::equal, diag::expected_equal_in_sil_instr)) + return true; } + SILInstructionKind Opcode; + SourceLoc OpcodeLoc; + StringRef OpcodeName; + + // Parse the opcode name. + if (parseSILOpcode(Opcode, OpcodeLoc, OpcodeName)) + return true; + + // Perform opcode specific parsing. + SILInstruction *ResultVal; + if (parseSpecificSILInstruction(B, Opcode, OpcodeLoc, OpcodeName, ResultVal)) + return true; + // Match the results clause if we had one. if (resultClauseBegin.isValid()) { auto results = ResultVal->getResults(); @@ -5069,7 +5031,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { results.size()); } else { for (size_t i : indices(results)) { - setLocalValue(results[i], resultNames[i].first, resultNames[i].second); + setLocalValue(results[i], resultNames[i].Item, resultNames[i].Loc); } } } @@ -5147,8 +5109,8 @@ bool SILParser::parseCallInstruction(SILLocation InstLoc, CanSILFunctionType substFTI = FTI; if (!subs.empty()) { auto silFnTy = FnTy.castTo(); - substFTI - = silFnTy->substGenericArgs(SILMod, subs); + substFTI = + silFnTy->substGenericArgs(SILMod, subs, B.getTypeExpansionContext()); FnTy = SILType::getPrimitiveObjectType(substFTI); } SILFunctionConventions substConv(substFTI, B.getModule()); @@ -5325,7 +5287,7 @@ bool SILParser::parseSILBasicBlock(SILBuilder &B) { BB = getBBForDefinition(BBName, NameLoc); // For now, since we always assume that PhiArguments have - // ValueOwnershipKind::Any, do not parse or do anything special. Eventually + // ValueOwnershipKind::None, do not parse or do anything special. Eventually // we will parse the convention. bool IsEntry = BB->isEntry(); @@ -5333,7 +5295,7 @@ bool SILParser::parseSILBasicBlock(SILBuilder &B) { if (P.consumeIf(tok::l_paren)) { do { SILType Ty; - ValueOwnershipKind OwnershipKind = ValueOwnershipKind::Any; + ValueOwnershipKind OwnershipKind = ValueOwnershipKind::None; SourceLoc NameLoc; StringRef Name = P.Tok.getText(); if (P.parseToken(tok::sil_local_name, NameLoc, @@ -5724,13 +5686,14 @@ bool SILParserTUState::parseSILProperty(Parser &P) { GenericEnvironment *patternEnv; Scope toplevelScope(&P, ScopeKind::TopLevel); Scope genericsScope(&P, ScopeKind::Generics); - generics = P.parseSILGenericParams().getPtrOrNull(); - patternEnv = handleSILGenericParams(P.Context, generics, &P.SF); - + generics = P.maybeParseGenericParams().getPtrOrNull(); + patternEnv = handleSILGenericParams(generics, &P.SF); + if (patternEnv) { - if (patternEnv->getGenericSignature()->getCanonicalSignature() - != VD->getInnermostDeclContext()->getGenericSignatureOfContext() - ->getCanonicalSignature()) { + if (patternEnv->getGenericSignature().getCanonicalSignature() != + VD->getInnermostDeclContext() + ->getGenericSignatureOfContext() + .getCanonicalSignature()) { P.diagnose(loc, diag::sil_property_generic_signature_mismatch); return true; } @@ -5973,24 +5936,24 @@ static bool isSelfConformance(Type conformingType, ProtocolDecl *protocol) { return false; } -static Optional parseRootProtocolConformance(Parser &P, - SILParser &SP, Type ConformingTy, ProtocolDecl *&proto, - ConformanceContext context) { +static ProtocolConformanceRef +parseRootProtocolConformance(Parser &P, SILParser &SP, Type ConformingTy, + ProtocolDecl *&proto, ConformanceContext context) { Identifier ModuleKeyword, ModuleName; SourceLoc Loc, KeywordLoc; proto = parseProtocolDecl(P, SP); if (!proto) - return None; - + return ProtocolConformanceRef(); + if (P.parseIdentifier(ModuleKeyword, KeywordLoc, diag::expected_tok_in_sil_instr, "module") || SP.parseSILIdentifier(ModuleName, Loc, diag::expected_sil_value_name)) - return None; + return ProtocolConformanceRef(); if (ModuleKeyword.str() != "module") { P.diagnose(KeywordLoc, diag::expected_tok_in_sil_instr, "module"); - return None; + return ProtocolConformanceRef(); } // Calling lookupConformance on a BoundGenericType will return a specialized @@ -5999,14 +5962,13 @@ static Optional parseRootProtocolConformance(Parser &P, if (auto bound = lookupTy->getAs()) lookupTy = bound->getDecl()->getDeclaredType(); auto lookup = P.SF.getParentModule()->lookupConformance(lookupTy, proto); - if (!lookup) { + if (lookup.isInvalid()) { P.diagnose(KeywordLoc, diag::sil_witness_protocol_conformance_not_found); - return None; + return ProtocolConformanceRef(); } // Use a concrete self-conformance if we're parsing this for a witness table. - if (context == ConformanceContext::WitnessTable && - !lookup->isConcrete() && + if (context == ConformanceContext::WitnessTable && !lookup.isConcrete() && isSelfConformance(ConformingTy, proto)) { lookup = ProtocolConformanceRef(P.Context.getSelfConformance(proto)); } @@ -6023,11 +5985,9 @@ static Optional parseRootProtocolConformance(Parser &P, /// normal-protocol-conformance ::= /// generic-parameter-list? type: protocolName module ModuleName /// Note that generic-parameter-list is already parsed before calling this. -Optional SILParser::parseProtocolConformance( - ProtocolDecl *&proto, - GenericEnvironment *&genericEnv, - ConformanceContext context, - ProtocolDecl *defaultForProto) { +ProtocolConformanceRef SILParser::parseProtocolConformance( + ProtocolDecl *&proto, GenericEnvironment *&genericEnv, + ConformanceContext context, ProtocolDecl *defaultForProto) { // Parse generic params for the protocol conformance. We need to make sure // they have the right scope. Optional GenericsScope; @@ -6037,9 +5997,9 @@ Optional SILParser::parseProtocolConformance( // Make sure we don't leave it uninitialized in the caller genericEnv = nullptr; - auto *genericParams = P.parseSILGenericParams().getPtrOrNull(); + auto *genericParams = P.maybeParseGenericParams().getPtrOrNull(); if (genericParams) { - genericEnv = handleSILGenericParams(P.Context, genericParams, &P.SF); + genericEnv = handleSILGenericParams(genericParams, &P.SF); } auto retVal = parseProtocolConformanceHelper(proto, genericEnv, context, @@ -6051,15 +6011,13 @@ Optional SILParser::parseProtocolConformance( return retVal; } -Optional SILParser::parseProtocolConformanceHelper( - ProtocolDecl *&proto, - GenericEnvironment *witnessEnv, - ConformanceContext context, - ProtocolDecl *defaultForProto) { +ProtocolConformanceRef SILParser::parseProtocolConformanceHelper( + ProtocolDecl *&proto, GenericEnvironment *witnessEnv, + ConformanceContext context, ProtocolDecl *defaultForProto) { // Parse AST type. ParserResult TyR = P.parseType(); if (TyR.isNull()) - return None; + return ProtocolConformanceRef(); TypeLoc Ty = TyR.get(); if (defaultForProto) { bindProtocolSelfInTypeRepr(Ty, defaultForProto); @@ -6067,11 +6025,11 @@ Optional SILParser::parseProtocolConformanceHelper( if (performTypeLocChecking(Ty, /*IsSILType=*/ false, witnessEnv, defaultForProto)) - return None; + return ProtocolConformanceRef(); auto ConformingTy = Ty.getType(); if (P.parseToken(tok::colon, diag::expected_sil_witness_colon)) - return None; + return ProtocolConformanceRef(); if (P.Tok.is(tok::identifier) && P.Tok.getText() == "specialize") { P.consumeToken(); @@ -6079,28 +6037,28 @@ Optional SILParser::parseProtocolConformanceHelper( // Parse substitutions for specialized conformance. SmallVector parsedSubs; if (parseSubstitutions(parsedSubs, witnessEnv, defaultForProto)) - return None; + return ProtocolConformanceRef(); if (P.parseToken(tok::l_paren, diag::expected_sil_witness_lparen)) - return None; + return ProtocolConformanceRef(); ProtocolDecl *dummy; GenericEnvironment *specializedEnv; auto genericConform = parseProtocolConformance(dummy, specializedEnv, ConformanceContext::Ordinary, defaultForProto); - if (!genericConform || !genericConform->isConcrete()) - return None; + if (genericConform.isInvalid() || !genericConform.isConcrete()) + return ProtocolConformanceRef(); if (P.parseToken(tok::r_paren, diag::expected_sil_witness_rparen)) - return None; + return ProtocolConformanceRef(); SubstitutionMap subMap = getApplySubstitutionsFromParsed(*this, specializedEnv, parsedSubs); if (!subMap) - return None; + return ProtocolConformanceRef(); auto result = P.Context.getSpecializedConformance( - ConformingTy, genericConform->getConcrete(), subMap); + ConformingTy, genericConform.getConcrete(), subMap); return ProtocolConformanceRef(result); } @@ -6108,16 +6066,16 @@ Optional SILParser::parseProtocolConformanceHelper( P.consumeToken(); if (P.parseToken(tok::l_paren, diag::expected_sil_witness_lparen)) - return None; + return ProtocolConformanceRef(); auto baseConform = parseProtocolConformance(defaultForProto, ConformanceContext::Ordinary); - if (!baseConform || !baseConform->isConcrete()) - return None; + if (baseConform.isInvalid() || !baseConform.isConcrete()) + return ProtocolConformanceRef(); if (P.parseToken(tok::r_paren, diag::expected_sil_witness_rparen)) - return None; + return ProtocolConformanceRef(); auto result = P.Context.getInheritedConformance(ConformingTy, - baseConform->getConcrete()); + baseConform.getConcrete()); return ProtocolConformanceRef(result); } @@ -6161,12 +6119,12 @@ static bool parseSILVTableEntry( auto conform = witnessState.parseProtocolConformance(defaultForProto, ConformanceContext::Ordinary); - if (!conform || !conform->isConcrete()) // Ignore this witness entry for now. + // Ignore invalid and abstract witness entries. + if (conform.isInvalid() || !conform.isConcrete()) return false; - witnessEntries.push_back(SILWitnessTable::BaseProtocolWitness{ - proto, conform->getConcrete() - }); + witnessEntries.push_back( + SILWitnessTable::BaseProtocolWitness{proto, conform.getConcrete()}); return false; } @@ -6210,9 +6168,10 @@ static bool parseSILVTableEntry( auto concrete = witnessState.parseProtocolConformance(defaultForProto, ConformanceContext::Ordinary); - if (!concrete && !concrete->isConcrete()) // Ignore this for now. + // Ignore invalid and abstract witness entries. + if (concrete.isInvalid() || !concrete.isConcrete()) return false; - conformance = *concrete; + conformance = concrete; } else { P.consumeToken(); } @@ -6336,10 +6295,9 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) { WitnessState.ContextGenericEnv = witnessEnv; // FIXME: should we really allow a specialized or inherited conformance here? - auto theConformance = - (conf && conf->isConcrete()) - ? conf->getConcrete()->getRootConformance() - : nullptr; + RootProtocolConformance *theConformance = nullptr; + if (conf.isConcrete()) + theConformance = conf.getConcrete()->getRootConformance(); SILWitnessTable *wt = nullptr; if (theConformance) { diff --git a/lib/PrintAsObjC/DeclAndTypePrinter.cpp b/lib/PrintAsObjC/DeclAndTypePrinter.cpp index c9a05d3b78801..0b609510f66c7 100644 --- a/lib/PrintAsObjC/DeclAndTypePrinter.cpp +++ b/lib/PrintAsObjC/DeclAndTypePrinter.cpp @@ -14,6 +14,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ClangSwiftTypeCorrespondence.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" @@ -278,6 +279,10 @@ class DeclAndTypePrinter::Implementation if (CD->getAttrs().hasAttribute()) os << "SWIFT_WEAK_IMPORT\n"; + if (CD->getAttrs().hasAttribute()) { + os << "IB_DESIGNABLE\n"; + } + bool hasResilientAncestry = CD->checkAncestry().contains(AncestryFlags::ResilientOther); if (hasResilientAncestry) { @@ -822,6 +827,9 @@ class DeclAndTypePrinter::Implementation case PlatformKind::iOS: plat = "ios"; break; + case PlatformKind::macCatalyst: + plat = "maccatalyst"; + break; case PlatformKind::tvOS: plat = "tvos"; break; @@ -834,6 +842,9 @@ class DeclAndTypePrinter::Implementation case PlatformKind::iOSApplicationExtension: plat = "ios_app_extension"; break; + case PlatformKind::macCatalystApplicationExtension: + plat = "maccatalyst_app_extension"; + break; case PlatformKind::tvOSApplicationExtension: plat = "tvos_app_extension"; break; @@ -890,7 +901,7 @@ class DeclAndTypePrinter::Implementation const ParsedDeclName renamedParsedDeclName) { auto declContext = D->getDeclContext(); ASTContext &astContext = D->getASTContext(); - auto renamedDeclName = renamedParsedDeclName.formDeclName(astContext); + auto renamedDeclName = renamedParsedDeclName.formDeclNameRef(astContext); if (isa(D) || isa(D)) { if (!renamedParsedDeclName.ContextName.empty()) { @@ -898,7 +909,7 @@ class DeclAndTypePrinter::Implementation } SmallVector decls; declContext->lookupQualified(declContext->getParentModule(), - renamedDeclName.getBaseIdentifier(), + renamedDeclName.withoutArgumentLabels(), NL_OnlyTypes, decls); if (decls.size() == 1) @@ -1189,6 +1200,10 @@ class DeclAndTypePrinter::Implementation } os << ") "; + if (VD->getAttrs().hasAttribute()) { + os << "IBInspectable "; + } + if (VD->getAttrs().hasAttribute()) { if (!maybePrintIBOutletCollection(ty)) os << "IBOutlet "; @@ -1388,8 +1403,8 @@ class DeclAndTypePrinter::Implementation // upper-bounded keys. else if (swiftNominal == ctx.getDictionaryDecl() && isNSObjectOrAnyHashable(ctx, typeArgs[0])) { - if (auto proto = ctx.getNSCopyingDecl()) { - rewrittenArgsBuf[0] = proto->getDeclaredInterfaceType(); + if (auto protoTy = ctx.getNSCopyingType()) { + rewrittenArgsBuf[0] = protoTy; rewrittenArgsBuf[1] = typeArgs[1]; typeArgs = rewrittenArgsBuf; } @@ -1551,8 +1566,7 @@ class DeclAndTypePrinter::Implementation ASTContext &ctx = getASTContext(); auto &clangASTContext = ctx.getClangModuleLoader()->getClangASTContext(); clang::QualType clangTy = clangASTContext.getTypeDeclType(clangTypeDecl); - return clangTy->isPointerType() || clangTy->isBlockPointerType() || - clangTy->isObjCObjectPointerType(); + return swift::canImportAsOptional(clangTy.getTypePtr()); } bool printImportedAlias(const TypeAliasDecl *alias, @@ -1873,8 +1887,8 @@ class DeclAndTypePrinter::Implementation assert(extension->getGenericParams()->size() == extendedClass->getGenericParams()->size() && "extensions with custom generic parameters?"); - assert(extension->getGenericSignature()->getCanonicalSignature() == - extendedClass->getGenericSignature()->getCanonicalSignature() && + assert(extension->getGenericSignature().getCanonicalSignature() == + extendedClass->getGenericSignature().getCanonicalSignature() && "constrained extensions or custom generic parameters?"); type = extendedClass->getGenericEnvironment()->getSugaredType(type); decl = type->getDecl(); @@ -1926,7 +1940,18 @@ class DeclAndTypePrinter::Implementation if (!FT->getParams().empty()) { interleave(FT->getParams(), [this](const AnyFunctionType::Param ¶m) { - print(param.getOldType(), OTK_None, param.getLabel(), + switch (param.getValueOwnership()) { + case ValueOwnership::Default: + case ValueOwnership::Shared: + break; + case ValueOwnership::Owned: + os << "SWIFT_RELEASES_ARGUMENT "; + break; + case ValueOwnership::InOut: + llvm_unreachable("bad specifier"); + } + + print(param.getParameterType(), OTK_None, param.getLabel(), IsFunctionParam); }, [this] { os << ", "; }); diff --git a/lib/PrintAsObjC/ModuleContentsWriter.h b/lib/PrintAsObjC/ModuleContentsWriter.h index f9852029b2d7c..0336b6e7da95b 100644 --- a/lib/PrintAsObjC/ModuleContentsWriter.h +++ b/lib/PrintAsObjC/ModuleContentsWriter.h @@ -35,4 +35,5 @@ void printModuleContentsAsObjC(raw_ostream &os, } // end namespace swift -#endif \ No newline at end of file +#endif + diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index b9e441ee16375..23187675fa1a6 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -112,6 +112,11 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx) { "#else\n" "# define SWIFT_NOESCAPE\n" "#endif\n" + "#if __has_attribute(ns_consumed)\n" + "# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))\n" + "#else\n" + "# define SWIFT_RELEASES_ARGUMENT\n" + "#endif\n" "#if __has_attribute(warn_unused_result)\n" "# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n" "#else\n" diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 9b264e8665e0c..943a1dad7babe 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -59,7 +59,7 @@ namespace { /// A "minimal" class for querying IRGen. struct IRGenContext { - IRGenOptions IROpts; + const IRGenOptions IROpts; SILOptions SILOpts; Lowering::TypeConverter TC; std::unique_ptr SILMod; diff --git a/lib/SIL/AbstractionPattern.cpp b/lib/SIL/AbstractionPattern.cpp index ff1b499c68120..6099ad9bb9ac8 100644 --- a/lib/SIL/AbstractionPattern.cpp +++ b/lib/SIL/AbstractionPattern.cpp @@ -47,7 +47,7 @@ AbstractionPattern TypeConverter::getAbstractionPattern(SubscriptDecl *decl, bool isNonObjC) { CanGenericSignature genericSig; if (auto sig = decl->getGenericSignatureOfContext()) - genericSig = sig->getCanonicalSignature(); + genericSig = sig.getCanonicalSignature(); return AbstractionPattern(genericSig, decl->getElementInterfaceType() ->getCanonicalType()); @@ -77,7 +77,7 @@ AbstractionPattern TypeConverter::getAbstractionPattern(VarDecl *var, bool isNonObjC) { CanGenericSignature genericSig; if (auto sig = var->getDeclContext()->getGenericSignatureOfContext()) - genericSig = sig->getCanonicalSignature(); + genericSig = sig.getCanonicalSignature(); CanType swiftType = var->getInterfaceType() ->getCanonicalType(); @@ -110,7 +110,7 @@ AbstractionPattern TypeConverter::getAbstractionPattern(EnumElementDecl *decl) { CanGenericSignature genericSig; if (auto sig = decl->getParentEnum()->getGenericSignatureOfContext()) - genericSig = sig->getCanonicalSignature(); + genericSig = sig.getCanonicalSignature(); return AbstractionPattern(genericSig, decl->getArgumentInterfaceType() ->getCanonicalType()); @@ -203,28 +203,57 @@ bool AbstractionPattern::isConcreteType() const { GenericSig->isConcreteType(getType())); } -bool AbstractionPattern::requiresClass() { +bool AbstractionPattern::requiresClass() const { switch (getKind()) { case Kind::Opaque: return false; case Kind::Type: - case Kind::Discard: { + case Kind::Discard: + case Kind::ClangType: { auto type = getType(); if (auto archetype = dyn_cast(type)) return archetype->requiresClass(); - else if (isa(type) || - isa(type)) { + if (isa(type) || + isa(type)) { + if (getKind() == Kind::ClangType) { + // ObjC generics are always class constrained. + return true; + } + assert(GenericSig && "Dependent type in pattern without generic signature?"); return GenericSig->requiresClass(type); } return false; } + default: return false; } } +LayoutConstraint AbstractionPattern::getLayoutConstraint() const { + switch (getKind()) { + case Kind::Opaque: + return LayoutConstraint(); + case Kind::Type: + case Kind::Discard: { + auto type = getType(); + if (auto archetype = dyn_cast(type)) + return archetype->getLayoutConstraint(); + else if (isa(type) || + isa(type)) { + assert(GenericSig && + "Dependent type in pattern without generic signature?"); + return GenericSig->getLayoutConstraint(type); + } + return LayoutConstraint(); + } + default: + return LayoutConstraint(); + } +} + bool AbstractionPattern::matchesTuple(CanTupleType substType) { switch (getKind()) { case Kind::Invalid: @@ -391,7 +420,7 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const { case Kind::Opaque: return *this; case Kind::Type: - if (isTypeParameter()) + if (isTypeParameterOrOpaqueArchetype()) return AbstractionPattern::getOpaque(); return AbstractionPattern(getGenericSignatureForFunctionComponent(), getResultType(getType())); @@ -440,7 +469,7 @@ AbstractionPattern::getFunctionParamType(unsigned index) const { case Kind::Opaque: return *this; case Kind::Type: { - if (isTypeParameter()) + if (isTypeParameterOrOpaqueArchetype()) return AbstractionPattern::getOpaque(); auto params = cast(getType()).getParams(); return AbstractionPattern(getGenericSignatureForFunctionComponent(), @@ -596,6 +625,8 @@ AbstractionPattern AbstractionPattern::getOptionalObjectType() const { case Kind::Type: if (isTypeParameter()) return AbstractionPattern::getOpaque(); + if (isa(getType())) + return AbstractionPattern::getOpaque(); return AbstractionPattern(getGenericSignature(), ::getOptionalObjectType(getType())); @@ -697,6 +728,9 @@ void AbstractionPattern::print(raw_ostream &out) const { getKind() == Kind::PartialCurriedCXXMethodType ? "AP::PartialCurriedCXXMethodType(" : "AP::PartialCurriedCFunctionAsMethodType("); + if (auto sig = getGenericSignature()) { + sig->print(out); + } getType().dump(out); out << ", "; // It would be better to use print, but we need a PrintingPolicy @@ -794,3 +828,44 @@ bool AbstractionPattern::hasSameBasicTypeStructure(CanType l, CanType r) { // Otherwise, the structure is similar enough. return true; } + +AbstractionPattern +AbstractionPattern::unsafeGetSubstFieldType(ValueDecl *member, + CanType origMemberInterfaceType) +const { + if (isTypeParameterOrOpaqueArchetype()) { + // Fall back to the generic abstraction pattern for the member. + auto sig = member->getDeclContext()->getGenericSignatureOfContext(); + CanType memberTy = origMemberInterfaceType + ? origMemberInterfaceType + : member->getInterfaceType()->getCanonicalType(sig); + return AbstractionPattern(sig.getCanonicalSignature(), memberTy); + } + + switch (getKind()) { + case Kind::Opaque: + llvm_unreachable("should be handled by isTypeParameter"); + case Kind::Invalid: + llvm_unreachable("called on invalid abstraction pattern"); + case Kind::Tuple: + llvm_unreachable("should not have a tuple pattern matching a struct/enum " + "type"); + case Kind::PartialCurriedObjCMethodType: + case Kind::CurriedObjCMethodType: + case Kind::PartialCurriedCFunctionAsMethodType: + case Kind::CurriedCFunctionAsMethodType: + case Kind::CFunctionAsMethodType: + case Kind::ObjCMethodType: + case Kind::CXXMethodType: + case Kind::CurriedCXXMethodType: + case Kind::PartialCurriedCXXMethodType: + case Kind::ClangType: + case Kind::Type: + case Kind::Discard: + auto memberTy = getType()->getTypeOfMember(member->getModuleContext(), + member, origMemberInterfaceType) + ->getCanonicalType(getGenericSignature()); + + return AbstractionPattern(getGenericSignature(), memberTy); + } +} diff --git a/lib/SIL/Bridging.cpp b/lib/SIL/Bridging.cpp index c00aff9cd1e23..c5760f1805eda 100644 --- a/lib/SIL/Bridging.cpp +++ b/lib/SIL/Bridging.cpp @@ -33,7 +33,8 @@ using namespace swift::Lowering; CanType TypeConverter::getLoweredTypeOfGlobal(VarDecl *var) { AbstractionPattern origType = getAbstractionPattern(var); assert(!origType.isTypeParameter()); - return getLoweredRValueType(origType, origType.getType()); + return getLoweredRValueType(TypeExpansionContext::minimal(), origType, + origType.getType()); } AnyFunctionType::Param @@ -232,9 +233,9 @@ Type TypeConverter::getLoweredCBridgedType(AbstractionPattern pattern, } case ForeignRepresentableKind::BridgedError: { - auto nsErrorDecl = M.getASTContext().getNSErrorDecl(); - assert(nsErrorDecl && "Cannot bridge when NSError isn't available"); - return nsErrorDecl->getDeclaredInterfaceType(); + auto nsErrorTy = M.getASTContext().getNSErrorType(); + assert(nsErrorTy && "Cannot bridge when NSError isn't available"); + return nsErrorTy; } } diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp index 6ebe7611d16ca..fe5b9a8bf8c68 100644 --- a/lib/SIL/DynamicCasts.cpp +++ b/lib/SIL/DynamicCasts.cpp @@ -135,7 +135,7 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target, // the implementation, including checkGenericArguments, needs to be taught to // recognize that types with archetypes may potentially succeed. if (auto conformance = M->lookupConformance(source, TargetProtocol)) { - assert(!conformance->getConditionalRequirements().empty()); + assert(!conformance.getConditionalRequirements().empty()); return DynamicCastFeasibility::MaySucceed; } @@ -217,8 +217,7 @@ bool swift::isObjectiveCBridgeable(ModuleDecl *M, CanType Ty) { if (bridgedProto) { // Find the conformance of the value type to _BridgedToObjectiveC. // Check whether the type conforms to _BridgedToObjectiveC. - auto conformance = M->lookupConformance(Ty, bridgedProto); - return conformance.hasValue(); + return (bool)M->lookupConformance(Ty, bridgedProto); } return false; } @@ -232,8 +231,7 @@ bool swift::isError(ModuleDecl *M, CanType Ty) { if (errorTypeProto) { // Find the conformance of the value type to Error. // Check whether the type conforms to Error. - auto conformance = M->lookupConformance(Ty, errorTypeProto); - return conformance.hasValue(); + return (bool)M->lookupConformance(Ty, errorTypeProto); } return false; } @@ -290,15 +288,16 @@ CanType swift::getNSBridgedClassOfCFClass(ModuleDecl *M, CanType type) { return CanType(); } -static bool isCFBridgingConversion(ModuleDecl *M, SILType sourceType, - SILType targetType) { +static bool isCFBridgingConversion(ModuleDecl *M, + CanType sourceFormalType, + CanType targetFormalType) { if (auto bridgedTarget = - getNSBridgedClassOfCFClass(M, targetType.getASTType())) { - return bridgedTarget->isExactSuperclassOf(sourceType.getASTType()); + getNSBridgedClassOfCFClass(M, targetFormalType)) { + return bridgedTarget->isExactSuperclassOf(sourceFormalType); } if (auto bridgedSource = - getNSBridgedClassOfCFClass(M, sourceType.getASTType())) { - return targetType.getASTType()->isExactSuperclassOf(bridgedSource); + getNSBridgedClassOfCFClass(M, sourceFormalType)) { + return targetFormalType->isExactSuperclassOf(bridgedSource); } return false; @@ -873,11 +872,15 @@ namespace { } else { value = getOwnedScalar(source, srcTL); } - auto targetTy = target.LoweredType; - if (isCFBridgingConversion(SwiftModule, value->getType(), targetTy)) { - value = B.createUncheckedRefCast(Loc, value, targetTy.getObjectType()); + auto targetFormalTy = target.FormalType; + auto targetLoweredTy = + SILType::getPrimitiveObjectType(target.LoweredType.getASTType()); + if (isCFBridgingConversion(SwiftModule, + source.FormalType, + targetFormalTy)) { + value = B.createUncheckedRefCast(Loc, value, targetLoweredTy); } else { - value = B.createUpcast(Loc, value, targetTy.getObjectType()); + value = B.createUpcast(Loc, value, targetLoweredTy); } return putOwnedScalar(value, target); } @@ -920,7 +923,8 @@ namespace { auto sourceSomeDecl = Ctx.getOptionalSomeDecl(); SILType loweredSourceObjectType = - source.Value->getType().getEnumElementType(sourceSomeDecl, M); + source.Value->getType().getEnumElementType( + sourceSomeDecl, M, B.getTypeExpansionContext()); // Form the target for the optional object. EmitSomeState state; @@ -993,8 +997,8 @@ namespace { auto someDecl = Ctx.getOptionalSomeDecl(); state.SomeDecl = someDecl; - SILType loweredObjectType = - target.LoweredType.getEnumElementType(someDecl, M); + SILType loweredObjectType = target.LoweredType.getEnumElementType( + someDecl, M, B.getTypeExpansionContext()); if (target.isAddress()) { SILValue objectAddr = @@ -1040,24 +1044,24 @@ swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, SILLocation loc, SILDynamicCastInst dynamicCast) { return emitSuccessfulScalarUnconditionalCast( B, B.getModule().getSwiftModule(), loc, dynamicCast.getSource(), - dynamicCast.getLoweredTargetType(), dynamicCast.getSourceType(), - dynamicCast.getTargetType(), dynamicCast.getInstruction()); + dynamicCast.getTargetLoweredType(), dynamicCast.getSourceFormalType(), + dynamicCast.getTargetFormalType(), dynamicCast.getInstruction()); } /// Emit an unconditional scalar cast that's known to succeed. SILValue swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, ModuleDecl *M, SILLocation loc, SILValue value, - SILType loweredTargetType, - CanType sourceType, - CanType targetType, + SILType targetLoweredType, + CanType sourceFormalType, + CanType targetFormalType, SILInstruction *existingCast) { - assert(classifyDynamicCast(M, sourceType, targetType) + assert(classifyDynamicCast(M, sourceFormalType, targetFormalType) == DynamicCastFeasibility::WillSucceed); // Casts to/from existential types cannot be further improved. - if (sourceType.isAnyExistentialType() || - targetType.isAnyExistentialType()) { + if (sourceFormalType.isAnyExistentialType() || + targetFormalType.isAnyExistentialType()) { if (existingCast) // Indicate that the existing cast cannot be further improved. return SILValue(); @@ -1066,14 +1070,14 @@ swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, ModuleDecl *M, } // Fast path changes that don't change the type. - if (sourceType == targetType) + if (sourceFormalType == targetFormalType) return value; - Source source(value, sourceType); - Target target(loweredTargetType, targetType); + Source source(value, sourceFormalType); + Target target(targetLoweredType, targetFormalType); Source result = CastEmitter(B, M, loc).emitTopLevel(source, target); assert(!result.isAddress()); - assert(result.Value->getType() == loweredTargetType); + assert(result.Value->getType() == targetLoweredType); return result.Value; } @@ -1081,15 +1085,15 @@ bool swift::emitSuccessfulIndirectUnconditionalCast( SILBuilder &B, SILLocation loc, SILDynamicCastInst dynamicCast) { return emitSuccessfulIndirectUnconditionalCast( B, B.getModule().getSwiftModule(), loc, dynamicCast.getSource(), - dynamicCast.getSourceType(), dynamicCast.getDest(), - dynamicCast.getTargetType(), dynamicCast.getInstruction()); + dynamicCast.getSourceFormalType(), dynamicCast.getDest(), + dynamicCast.getTargetFormalType(), dynamicCast.getInstruction()); } bool swift::emitSuccessfulIndirectUnconditionalCast( SILBuilder &B, ModuleDecl *M, SILLocation loc, SILValue src, - CanType sourceType, SILValue dest, CanType targetType, + CanType sourceFormalType, SILValue dest, CanType targetFormalType, SILInstruction *existingCast) { - assert(classifyDynamicCast(M, sourceType, targetType) + assert(classifyDynamicCast(M, sourceFormalType, targetFormalType) == DynamicCastFeasibility::WillSucceed); assert(src->getType().isAddress()); @@ -1112,20 +1116,21 @@ bool swift::emitSuccessfulIndirectUnconditionalCast( if (existingCast) { auto *UCCAI = dyn_cast(existingCast); if (UCCAI && UCCAI->getSrc() == src && UCCAI->getDest() == dest - && UCCAI->getSourceType() == sourceType - && UCCAI->getTargetType() == targetType) { + && UCCAI->getSourceFormalType() == sourceFormalType + && UCCAI->getTargetFormalType() == targetFormalType) { // Indicate that the existing cast cannot be further improved. return false; } } - B.createUnconditionalCheckedCastAddr(loc, src, sourceType, dest, - targetType); + B.createUnconditionalCheckedCastAddr(loc, + src, sourceFormalType, + dest, targetFormalType); return true; } - Source source(src, sourceType); - Target target(dest, targetType); + Source source(src, sourceFormalType); + Target target(dest, targetFormalType); Source result = CastEmitter(B, M, loc).emitTopLevel(source, target); assert(result.isAddress()); assert(result.Value == dest); @@ -1136,10 +1141,10 @@ bool swift::emitSuccessfulIndirectUnconditionalCast( /// Can the given cast be performed by the scalar checked-cast /// instructions? bool swift::canUseScalarCheckedCastInstructions(SILModule &M, - CanType sourceType, - CanType targetType) { + CanType sourceFormalType, + CanType targetFormalType) { // Look through one level of optionality on the source. - auto objectType = sourceType; + auto objectType = sourceFormalType; if (auto type = objectType.getOptionalObjectType()) objectType = type; @@ -1151,14 +1156,14 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M, // A class-constrained archetype may be bound to NSError, unless it has a // non-NSError superclass constraint. Casts to archetypes thus must always be // indirect. - if (auto archetype = targetType->getAs()) { + if (auto archetype = targetFormalType->getAs()) { // Only ever permit this if the source type is a reference type. if (!objectType.isAnyClassReferenceType()) return false; - auto super = archetype->getSuperclass(); - if (super.isNull()) - return false; + auto super = archetype->getSuperclass(); + if (super.isNull()) + return false; // A base class constraint that isn't NSError rules out the archetype being // bound to NSError. @@ -1172,11 +1177,11 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M, } if (M.getASTContext().LangOpts.EnableObjCInterop - && targetType == M.Types.getNSErrorType()) { + && targetFormalType == M.Types.getNSErrorType()) { // If we statically know the source is an NSError subclass, then the cast // can go through the scalar path (and it's trivially true so can be // killed). - return targetType->isExactSuperclassOf(objectType); + return targetFormalType->isExactSuperclassOf(objectType); } // Three supported cases: @@ -1184,10 +1189,10 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M, // - metatype to object // - object to object if ((objectType.isAnyClassReferenceType() || isa(objectType)) - && targetType.isAnyClassReferenceType()) + && targetFormalType.isAnyClassReferenceType()) return true; - if (isa(objectType) && isa(targetType)) + if (isa(objectType) && isa(targetFormalType)) return true; // Otherwise, we need to use the general indirect-cast functions. @@ -1198,12 +1203,13 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M, /// using a scalar cast operation. void swift::emitIndirectConditionalCastWithScalar( SILBuilder &B, ModuleDecl *M, SILLocation loc, - CastConsumptionKind consumption, SILValue srcAddr, CanType sourceType, - SILValue destAddr, CanType targetType, SILBasicBlock *indirectSuccBB, - SILBasicBlock *indirectFailBB, ProfileCounter TrueCount, - ProfileCounter FalseCount) { + CastConsumptionKind consumption, + SILValue srcAddr, CanType sourceFormalType, + SILValue destAddr, CanType targetFormalType, + SILBasicBlock *indirectSuccBB, SILBasicBlock *indirectFailBB, + ProfileCounter TrueCount, ProfileCounter FalseCount) { assert(canUseScalarCheckedCastInstructions(B.getModule(), - sourceType, targetType)); + sourceFormalType, targetFormalType)); // Create our successor and fail blocks. SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough(); @@ -1225,7 +1231,7 @@ void swift::emitIndirectConditionalCastWithScalar( // 3. If the original cast was take_on_success, then on success we place the // casted value into dest and on failure, store the original value back into // src. - SILType targetValueType = destAddr->getType().getObjectType(); + SILType targetLoweredType = destAddr->getType().getObjectType(); // Inline constructor auto srcValue = ([&]() -> SILValue { if (consumption == CastConsumptionKind::CopyOnSuccess) @@ -1233,13 +1239,14 @@ void swift::emitIndirectConditionalCastWithScalar( return B.emitLoadValueOperation(loc, srcAddr, LoadOwnershipQualifier::Take); })(); - B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType, - scalarSuccBB, scalarFailBB, TrueCount, FalseCount); + B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetLoweredType, + targetFormalType, scalarSuccBB, scalarFailBB, + TrueCount, FalseCount); // Emit the success block. B.setInsertionPoint(scalarSuccBB); { SILValue succValue = scalarSuccBB->createPhiArgument( - targetValueType, srcValue.getOwnershipKind()); + targetLoweredType, srcValue.getOwnershipKind()); switch (consumption) { // On success, we take with both take_always and take_on_success. diff --git a/lib/SIL/InstructionUtils.cpp b/lib/SIL/InstructionUtils.cpp index 7e98cc3ee8a33..4b8f70aa5db8f 100644 --- a/lib/SIL/InstructionUtils.cpp +++ b/lib/SIL/InstructionUtils.cpp @@ -38,6 +38,7 @@ SILValue swift::stripOwnershipInsts(SILValue v) { /// Strip off casts/indexing insts/address projections from V until there is /// nothing left to strip. +/// /// FIXME: Why don't we strip projections after stripping indexes? SILValue swift::getUnderlyingObject(SILValue v) { while (true) { @@ -54,25 +55,25 @@ SILValue swift::getUnderlyingObject(SILValue v) { /// Strip off casts and address projections into the interior of a value. Unlike /// getUnderlyingObject, this does not find the root of a heap object--a class /// property is itself an address root. -SILValue swift::getUnderlyingAddressRoot(SILValue V) { +SILValue swift::getUnderlyingAddressRoot(SILValue v) { while (true) { - SILValue V2 = stripIndexingInsts(stripCasts(V)); - switch (V2->getKind()) { - case ValueKind::StructElementAddrInst: - case ValueKind::TupleElementAddrInst: - case ValueKind::UncheckedTakeEnumDataAddrInst: - V2 = cast(V2)->getOperand(0); - break; - default: - break; + SILValue v2 = stripIndexingInsts(stripCasts(v)); + v2 = stripOwnershipInsts(v2); + switch (v2->getKind()) { + case ValueKind::StructElementAddrInst: + case ValueKind::TupleElementAddrInst: + case ValueKind::UncheckedTakeEnumDataAddrInst: + v2 = cast(v2)->getOperand(0); + break; + default: + break; } - if (V2 == V) - return V2; - V = V2; + if (v2 == v) + return v2; + v = v2; } } - SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) { while (true) { SILValue v2 = stripCastsWithoutMarkDependence(v); @@ -329,12 +330,12 @@ bool swift::onlyAffectsRefCount(SILInstruction *user) { #define UNCHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Name##RetainValueInst: \ case SILInstructionKind::Name##ReleaseValueInst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Name##RetainInst: \ case SILInstructionKind::Name##ReleaseInst: \ case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" return true; } diff --git a/lib/SIL/Linker.cpp b/lib/SIL/Linker.cpp index e20d69edd4d81..245d7548cb205 100644 --- a/lib/SIL/Linker.cpp +++ b/lib/SIL/Linker.cpp @@ -114,6 +114,11 @@ void SILLinkerVisitor::maybeAddFunctionToWorklist(SILFunction *F) { // So try deserializing HiddenExternal functions too. if (F->getLinkage() == SILLinkage::HiddenExternal) return addFunctionToWorklist(F); + + // Update the linkage of the function in case it's different in the serialized + // SIL than derived from the AST. This can be the case with cross-module- + // optimizations. + Mod.updateFunctionLinkage(F); } /// Process F, recursively deserializing any thing F may reference. diff --git a/lib/SIL/Linker.h b/lib/SIL/Linker.h index 12ad57ae57fff..2901959ddfbc6 100644 --- a/lib/SIL/Linker.h +++ b/lib/SIL/Linker.h @@ -33,10 +33,6 @@ class SILLinkerVisitor : public SILInstructionVisitor { /// Worklist of SILFunctions we are processing. llvm::SmallVector Worklist; - /// A list of callees of the current instruction being visited. cleared after - /// every instruction is visited. - llvm::SmallVector FunctionDeserializationWorklist; - /// The current linking mode. LinkingMode Mode; @@ -45,8 +41,7 @@ class SILLinkerVisitor : public SILInstructionVisitor { public: SILLinkerVisitor(SILModule &M, SILModule::LinkingMode LinkingMode) - : Mod(M), Worklist(), FunctionDeserializationWorklist(), - Mode(LinkingMode), Changed(false) {} + : Mod(M), Worklist(), Mode(LinkingMode), Changed(false) {} /// Process F, recursively deserializing any thing F may reference. /// Returns true if any deserialization was performed. diff --git a/lib/SIL/LoopInfo.cpp b/lib/SIL/LoopInfo.cpp index 84f55ef1d4116..72defd29eb7bc 100644 --- a/lib/SIL/LoopInfo.cpp +++ b/lib/SIL/LoopInfo.cpp @@ -47,6 +47,18 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { } return true; } + if (I->isDeallocatingStack()) { + SILInstruction *alloc = nullptr; + if (auto *dealloc = dyn_cast(I)) { + SILValue address = dealloc->getOperand(); + if (isa(address) || isa(address)) + alloc = cast(address); + } + if (auto *dealloc = dyn_cast(I)) + alloc = dyn_cast(dealloc->getOperand()); + + return alloc && contains(alloc); + } // CodeGen can't build ssa for objc methods. if (auto *Method = dyn_cast(I)) { @@ -71,13 +83,17 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { return true; } - if (auto *Dealloc = dyn_cast(I)) { - // The matching alloc_stack must be in the loop. - if (auto *Alloc = dyn_cast(Dealloc->getOperand())) - return contains(Alloc->getParent()); + if (isa(I)) return false; - } + // The entire access must be within the loop. + if (auto BAI = dyn_cast(I)) { + for (auto *UI : BAI->getUses()) { + if (!contains(UI->getUser())) + return false; + } + return true; + } // The entire coroutine execution must be within the loop. // Note that we don't have to worry about the reverse --- a loop which // contains an end_apply or abort_apply of an external begin_apply --- @@ -92,18 +108,11 @@ bool SILLoop::canDuplicate(SILInstruction *I) const { return true; } - if (isa(I)) - return false; - - if (isa(I)) - return false; - if (isa(I)) return false; - if (auto *PA = dyn_cast(I)) - return !PA->isOnStack(); - + // Some special cases above that aren't considered isTriviallyDuplicatable + // return true early. assert(I->isTriviallyDuplicatable() && "Code here must match isTriviallyDuplicatable in SILInstruction"); return true; diff --git a/lib/SIL/MemAccessUtils.cpp b/lib/SIL/MemAccessUtils.cpp index 3580c541d0dd1..387ab1b2ab0d1 100644 --- a/lib/SIL/MemAccessUtils.cpp +++ b/lib/SIL/MemAccessUtils.cpp @@ -14,6 +14,7 @@ #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/ApplySite.h" +#include "swift/SIL/Projection.h" #include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" @@ -596,9 +597,9 @@ void swift::visitAccessedAddress(SILInstruction *I, // Non-access cases: these are marked with memory side effects, but, by // themselves, do not access formal memory. #define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::AbortApplyInst: case SILInstructionKind::AllocBoxInst: @@ -660,3 +661,64 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { } return beginAccess->getParent()->erase(beginAccess); } + +bool swift::isSingleInitAllocStack( + AllocStackInst *asi, SmallVectorImpl &destroyingUsers) { + // For now, we just look through projections and rely on memInstMustInitialize + // to classify all other uses as init or not. + SmallVector worklist(asi->getUses()); + bool foundInit = false; + + while (!worklist.empty()) { + auto *use = worklist.pop_back_val(); + auto *user = use->getUser(); + + if (Projection::isAddressProjection(user) || + isa(user)) { + // Look through address projections. + for (SILValue r : user->getResults()) { + llvm::copy(r->getUses(), std::back_inserter(worklist)); + } + continue; + } + + if (auto *li = dyn_cast(user)) { + // If we are not taking, + if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Take) { + continue; + } + // Treat load [take] as a write. + return false; + } + + switch (user->getKind()) { + default: + break; + case SILInstructionKind::DestroyAddrInst: + destroyingUsers.push_back(user); + continue; + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::DebugValueAddrInst: + continue; + } + + // See if we have an initializer and that such initializer is in the same + // block. + if (memInstMustInitialize(use)) { + if (user->getParent() != asi->getParent() || foundInit) { + return false; + } + + foundInit = true; + continue; + } + + // Otherwise, if we have found something not in our whitelist, return false. + return false; + } + + // We did not find any users that we did not understand. So we can + // conservatively return true here. + return true; +} diff --git a/lib/SIL/MemoryLifetime.cpp b/lib/SIL/MemoryLifetime.cpp index a80b13eef44b2..e7cb283cb5eed 100644 --- a/lib/SIL/MemoryLifetime.cpp +++ b/lib/SIL/MemoryLifetime.cpp @@ -28,16 +28,8 @@ llvm::cl::opt DontAbortOnMemoryLifetimeErrors( llvm::cl::desc("Don't abort compliation if the memory lifetime checker " "detects an error.")); -namespace swift { -namespace { - -//===----------------------------------------------------------------------===// -// Utility functions -//===----------------------------------------------------------------------===// - /// Debug dump a location bit vector. -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const SmallBitVector &bits) { +void swift::printBitsAsArray(llvm::raw_ostream &OS, const SmallBitVector &bits) { const char *separator = ""; OS << '['; for (int idx = bits.find_first(); idx >= 0; idx = bits.find_next(idx)) { @@ -45,9 +37,19 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, separator = ","; } OS << ']'; - return OS; } +void swift::dumpBits(const SmallBitVector &bits) { + llvm::dbgs() << bits << '\n'; +} + +namespace swift { +namespace { + +//===----------------------------------------------------------------------===// +// Utility functions +//===----------------------------------------------------------------------===// + /// Enlarge the bitset if needed to set the bit with \p idx. static void setBitAndResize(SmallBitVector &bits, unsigned idx) { if (bits.size() <= idx) @@ -240,10 +242,6 @@ void MemoryLocations::dump() const { } } -void MemoryLocations::dumpBits(const Bits &bits) { - llvm::errs() << bits << '\n'; -} - void MemoryLocations::clear() { locations.clear(); addr2LocIdx.clear(); @@ -299,6 +297,7 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: + case SILInstructionKind::BeginApplyInst: case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::CopyAddrInst: case SILInstructionKind::YieldInst: @@ -375,7 +374,8 @@ void MemoryLocations::initFieldsCounter(Location &loc) { } SILModule &module = function->getModule(); for (VarDecl *field : decl->getStoredProperties()) { - loc.updateFieldCounters(ty.getFieldType(field, module), +1); + loc.updateFieldCounters( + ty.getFieldType(field, module, TypeExpansionContext(*function)), +1); } return; } @@ -435,7 +435,10 @@ void MemoryDataflow::exitReachableAnalysis() { llvm::SmallVector workList; for (BlockState &state : blockStates) { if (state.block->getTerminator()->isFunctionExiting()) { - state.exitReachable = true; + state.exitReachability = ExitReachability::ReachesExit; + workList.push_back(&state); + } else if (isa(state.block->getTerminator())) { + state.exitReachability = ExitReachability::ReachesUnreachable; workList.push_back(&state); } } @@ -443,8 +446,10 @@ void MemoryDataflow::exitReachableAnalysis() { BlockState *state = workList.pop_back_val(); for (SILBasicBlock *pred : state->block->getPredecessorBlocks()) { BlockState *predState = block2State[pred]; - if (!predState->exitReachable) { - predState->exitReachable = true; + if (predState->exitReachability < state->exitReachability) { + // As there are 3 states, each block can be put into the workList 2 + // times maximum. + predState->exitReachability = state->exitReachability; workList.push_back(predState); } } @@ -806,7 +811,7 @@ void MemoryLifetimeVerifier::checkFunction(MemoryDataflow &dataFlow) { const Bits &nonTrivialLocations = locations.getNonTrivialLocations(); Bits bits(locations.getNumLocations()); for (BlockState &st : dataFlow) { - if (!st.reachableFromEntry || !st.exitReachable) + if (!st.reachableFromEntry || !st.exitReachable()) continue; // Check all instructions in the block. diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index face71e5f2177..120199e4fde5c 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -78,7 +78,7 @@ class OperandOwnershipKindClassifier bool isAddressOrTrivialType() const { if (getType().isAddress()) return true; - return getOwnershipKind() == ValueOwnershipKind::Any; + return getOwnershipKind() == ValueOwnershipKind::None; } OperandOwnershipKindMap visitForwardingInst(SILInstruction *i, @@ -142,6 +142,18 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) #include "swift/AST/ReferenceStorage.def" #undef SHOULD_NEVER_VISIT_INST +/// Instructions that are interior pointers into a guaranteed value. +#define INTERIOR_POINTER_PROJECTION(INST) \ + OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ + INST##Inst *i) { \ + assert(i->getNumOperands() && "Expected to have non-zero operands"); \ + return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, \ + UseLifetimeConstraint::MustBeLive); \ + } +INTERIOR_POINTER_PROJECTION(RefElementAddr) +INTERIOR_POINTER_PROJECTION(RefTailAddr) +#undef INTERIOR_POINTER_PROJECTION + /// Instructions whose arguments are always compatible with one convention. #define CONSTANT_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ @@ -151,7 +163,6 @@ SHOULD_NEVER_VISIT_INST(StrongRelease) ValueOwnershipKind::OWNERSHIP, \ UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ } -CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue) CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, AutoreleaseValue) @@ -161,73 +172,73 @@ CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocRef) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DestroyValue) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndLifetime) CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, InitExistentialRef) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, AbortApply) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, AddressToPointer) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, BeginAccess) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, BeginUnpairedAccess) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, BindMemory) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, CheckedCastAddrBranch) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, CondFail) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, CopyAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DeallocStack) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DebugValueAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DeinitExistentialAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DestroyAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, EndAccess) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, EndApply) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, EndUnpairedAccess) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, IndexAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, IndexRawPointer) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, InitBlockStorageHeader) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, InitEnumDataAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, InitExistentialAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, InitExistentialMetatype) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, InjectEnumAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, IsUnique) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, Load) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, LoadBorrow) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, MarkFunctionEscape) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ObjCExistentialMetatypeToObject) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ObjCMetatypeToObject) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ObjCToThickMetatype) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, OpenExistentialAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, OpenExistentialMetatype) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, PointerToAddress) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, PointerToThinFunction) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ProjectBlockStorage) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ProjectValueBuffer) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, RawPointerToRef) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, SelectEnumAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, SelectValue) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, StructElementAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, SwitchEnumAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, SwitchValue) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, TailAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ThickToObjCMetatype) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ThinFunctionToPointer) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, ThinToThickFunction) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, TupleElementAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, UncheckedAddrCast) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, UncheckedRefCastAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, UncheckedTakeEnumDataAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, UnconditionalCheckedCastAddr) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, AllocValueBuffer) -CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DeallocValueBuffer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, AbortApply) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, AddressToPointer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, BeginAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, BeginUnpairedAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, BindMemory) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, CheckedCastAddrBranch) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, CondFail) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, CopyAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, DeallocStack) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, DebugValueAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, DeinitExistentialAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, DestroyAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndApply) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndUnpairedAccess) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexRawPointer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitBlockStorageHeader) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitEnumDataAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitExistentialAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitExistentialMetatype) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, InjectEnumAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, IsUnique) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, Load) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, LoadBorrow) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, MarkFunctionEscape) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ObjCExistentialMetatypeToObject) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ObjCMetatypeToObject) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ObjCToThickMetatype) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, OpenExistentialAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, OpenExistentialMetatype) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, PointerToAddress) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, PointerToThinFunction) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ProjectBlockStorage) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ProjectValueBuffer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, RawPointerToRef) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, SelectEnumAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, SelectValue) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, StructElementAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, SwitchEnumAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, SwitchValue) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, TailAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ThickToObjCMetatype) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ThinFunctionToPointer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, ThinToThickFunction) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, TupleElementAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, UncheckedAddrCast) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, UncheckedRefCastAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, UncheckedTakeEnumDataAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, UnconditionalCheckedCastAddr) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, AllocValueBuffer) +CONSTANT_OWNERSHIP_INST(None, MustBeLive, DeallocValueBuffer) #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - CONSTANT_OWNERSHIP_INST(Any, MustBeLive, Load##Name) + CONSTANT_OWNERSHIP_INST(None, MustBeLive, Load##Name) #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, Name##Release) #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") #define UNCHECKED_REF_STORAGE(Name, ...) \ - CONSTANT_OWNERSHIP_INST(Any, MustBeLive, Name##ToRef) + CONSTANT_OWNERSHIP_INST(None, MustBeLive, Name##ToRef) #include "swift/AST/ReferenceStorage.def" #undef CONSTANT_OWNERSHIP_INST /// Instructions whose arguments are always compatible with one convention. -#define CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, \ - INST) \ +#define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, \ + INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ INST##Inst *i) { \ assert(i->getNumOperands() && "Expected to have non-zero operands"); \ @@ -235,15 +246,14 @@ CONSTANT_OWNERSHIP_INST(Any, MustBeLive, DeallocValueBuffer) ValueOwnershipKind::OWNERSHIP, \ UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ } -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, - CheckedCastValueBranch) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, - UnconditionalCheckedCastValue) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, - InitExistentialValue) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, - DeinitExistentialValue) -#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + CheckedCastValueBranch) +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + UnconditionalCheckedCastValue) +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, InitExistentialValue) +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + DeinitExistentialValue) +#undef CONSTANT_OR_NONE_OWNERSHIP_INST #define ACCEPTS_ANY_OWNERSHIP_INST(INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ @@ -272,7 +282,6 @@ ACCEPTS_ANY_OWNERSHIP_INST(BridgeObjectToWord) ACCEPTS_ANY_OWNERSHIP_INST(ClassifyBridgeObject) ACCEPTS_ANY_OWNERSHIP_INST(CopyBlock) ACCEPTS_ANY_OWNERSHIP_INST(OpenExistentialBox) -ACCEPTS_ANY_OWNERSHIP_INST(RefTailAddr) ACCEPTS_ANY_OWNERSHIP_INST(RefToRawPointer) ACCEPTS_ANY_OWNERSHIP_INST(SetDeallocating) ACCEPTS_ANY_OWNERSHIP_INST(ProjectExistentialBox) @@ -283,10 +292,10 @@ ACCEPTS_ANY_OWNERSHIP_INST(ConvertEscapeToNoEscape) #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ ACCEPTS_ANY_OWNERSHIP_INST(RefTo##Name) \ ACCEPTS_ANY_OWNERSHIP_INST(Name##ToRef) \ - ACCEPTS_ANY_OWNERSHIP_INST(Copy##Name##Value) + ACCEPTS_ANY_OWNERSHIP_INST(StrongCopy##Name##Value) #define UNCHECKED_REF_STORAGE(Name, ...) \ ACCEPTS_ANY_OWNERSHIP_INST(RefTo##Name) \ - ACCEPTS_ANY_OWNERSHIP_INST(Copy##Name##Value) + ACCEPTS_ANY_OWNERSHIP_INST(StrongCopy##Name##Value) #include "swift/AST/ReferenceStorage.def" #undef ACCEPTS_ANY_OWNERSHIP_INST @@ -312,7 +321,7 @@ OperandOwnershipKindClassifier::visitForwardingInst(SILInstruction *i, return Map(); auto kind = optionalKind.getValue(); - if (kind == ValueOwnershipKind::Any) + if (kind == ValueOwnershipKind::None) return Map::allLive(); auto lifetimeConstraint = kind.getForwardingLifetimeConstraint(); return Map::compatibilityMap(kind, lifetimeConstraint); @@ -334,15 +343,14 @@ FORWARD_ANY_OWNERSHIP_INST(ConvertFunction) FORWARD_ANY_OWNERSHIP_INST(RefToBridgeObject) FORWARD_ANY_OWNERSHIP_INST(BridgeObjectToRef) FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast) -FORWARD_ANY_OWNERSHIP_INST(MarkUninitialized) FORWARD_ANY_OWNERSHIP_INST(UncheckedEnumData) FORWARD_ANY_OWNERSHIP_INST(DestructureStruct) FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) #undef FORWARD_ANY_OWNERSHIP_INST // An instruction that forwards a constant ownership or trivial ownership. -#define FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST( \ - OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ +#define FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, \ + USE_LIFETIME_CONSTRAINT, INST) \ OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ INST##Inst *i) { \ assert(i->getNumOperands() && "Expected to have non-zero operands"); \ @@ -354,10 +362,11 @@ FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ return map; \ } -FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract) -FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive, - StructExtract) -#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST +FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract) +FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, MustBeLive, StructExtract) +FORWARD_CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MustBeInvalidated, + MarkUninitialized) +#undef CONSTANT_OR_NONE_OWNERSHIP_INST OperandOwnershipKindMap OperandOwnershipKindClassifier::visitDeallocPartialRefInst( @@ -456,17 +465,16 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { // Otherwise, go through the ownership constraints of our successor arguments // and merge them. auto mergedKind = ValueOwnershipKind::merge(makeTransformRange( - sei->getSuccessorBlockArguments(), - [&](PhiArgumentArrayRef array) -> ValueOwnershipKind { + sei->getSuccessorBlockArgumentLists(), + [&](ArrayRef array) -> ValueOwnershipKind { // If the array is empty, we have a non-payloaded case. Return any. if (array.empty()) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; // Otherwise, we should have a single element since a payload is // a tuple. assert(std::distance(array.begin(), array.end()) == 1); - SILPhiArgument *arg = array.front(); - return arg->getOwnershipKind(); + return array.front()->getOwnershipKind(); })); // If we failed to merge, return an empty map so we will fail to pattern match @@ -475,7 +483,7 @@ OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { if (!mergedKind) return Map(); auto kind = mergedKind.getValue(); - if (kind == ValueOwnershipKind::Any) + if (kind == ValueOwnershipKind::None) return Map::allLive(); auto lifetimeConstraint = kind.getForwardingLifetimeConstraint(); return Map::compatibilityMap(kind, lifetimeConstraint); @@ -486,7 +494,7 @@ OperandOwnershipKindClassifier::visitCheckedCastBranchInst( CheckedCastBranchInst *ccbi) { // TODO: Simplify this using ValueOwnershipKind::merge. Optional map; - for (auto argArray : ccbi->getSuccessorBlockArguments()) { + for (auto argArray : ccbi->getSuccessorBlockArgumentLists()) { assert(!argArray.empty()); auto argOwnershipKind = argArray[getOperandIndex()]->getOwnershipKind(); @@ -603,12 +611,12 @@ OperandOwnershipKindMap OperandOwnershipKindClassifier::visitCallee( case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: assert(!SILModuleConventions(mod).isSILIndirect( - SILParameterInfo(substCalleeType, conv))); + SILParameterInfo(substCalleeType, conv))); return Map::compatibilityMap(ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated); case ParameterConvention::Indirect_In_Guaranteed: assert(!SILModuleConventions(mod).isSILIndirect( - SILParameterInfo(substCalleeType, conv))); + SILParameterInfo(substCalleeType, conv))); return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, UseLifetimeConstraint::MustBeLive); case ParameterConvention::Indirect_Inout: @@ -856,8 +864,8 @@ OperandOwnershipKindMap OperandOwnershipKindClassifier::visitMarkDependenceInst( MarkDependenceInst *mdi) { // If we are analyzing "the value", we forward ownership. if (getValue() == mdi->getValue()) { - auto kind = getValue().getOwnershipKind(); - if (kind == ValueOwnershipKind::Any) + auto kind = mdi->getOwnershipKind(); + if (kind == ValueOwnershipKind::None) return Map::allLive(); auto lifetimeConstraint = kind.getForwardingLifetimeConstraint(); return Map::compatibilityMap(kind, lifetimeConstraint); diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index fc4ff51aca925..8047342ad08e6 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -19,7 +19,7 @@ using namespace swift; bool swift::isValueAddressOrTrivial(SILValue v) { return v->getType().isAddress() || - v.getOwnershipKind() == ValueOwnershipKind::Any; + v.getOwnershipKind() == ValueOwnershipKind::None; } // These operations forward both owned and guaranteed ownership. @@ -44,6 +44,7 @@ bool swift::isOwnershipForwardingValueKind(SILNodeKind kind) { case SILNodeKind::CondBranchInst: case SILNodeKind::DestructureStructInst: case SILNodeKind::DestructureTupleInst: + case SILNodeKind::MarkDependenceInst: return true; default: return false; @@ -162,6 +163,13 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } + // If v produces .none ownership, then we can ignore it. It is important + // that we put this before checking for guaranteed forwarding instructions, + // since we want to ignore guaranteed forwarding instructions that in this + // specific case produce a .none value. + if (v.getOwnershipKind() == ValueOwnershipKind::None) + continue; + // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { @@ -172,10 +180,9 @@ bool swift::getUnderlyingBorrowIntroducingValues( continue; } - // If v produces any ownership, then we can ignore it. Otherwise, we need to - // return false since this is an introducer we do not understand. - if (v.getOwnershipKind() != ValueOwnershipKind::Any) - return false; + // Otherwise, this is an introducer we do not understand. Bail and return + // false. + return false; } return true; @@ -188,8 +195,8 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, } bool BorrowScopeIntroducingValue::areInstructionsWithinScope( - ArrayRef instructions, - SmallVectorImpl &scratchSpace, + ArrayRef instructions, + SmallVectorImpl &scratchSpace, SmallPtrSetImpl &visitedBlocks, DeadEndBlocks &deadEndBlocks) const { // Make sure that we clear our scratch space/utilities before we exit. @@ -207,8 +214,9 @@ bool BorrowScopeIntroducingValue::areInstructionsWithinScope( return true; // Otherwise, gather up our local scope ending instructions. - visitLocalScopeEndingUses( - [&scratchSpace](Operand *op) { scratchSpace.emplace_back(op); }); + visitLocalScopeEndingUses([&scratchSpace](Operand *op) { + scratchSpace.emplace_back(op->getUser()); + }); LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); return checker.validateLifetime(value, scratchSpace, instructions); diff --git a/lib/SIL/Projection.cpp b/lib/SIL/Projection.cpp index 3e090189a21bf..2881469102ea1 100644 --- a/lib/SIL/Projection.cpp +++ b/lib/SIL/Projection.cpp @@ -177,17 +177,18 @@ Projection::Projection(SingleValueInstruction *I) : Value() { /// /// WARNING: This is not a constant time operation because it is implemented /// in terms of getVarDecl, which requests all BaseType's stored properties. -SILType Projection::getType(SILType BaseType, SILModule &M) const { +SILType Projection::getType(SILType BaseType, SILModule &M, + TypeExpansionContext context) const { assert(isValid()); switch (getKind()) { case ProjectionKind::Struct: case ProjectionKind::Class: - return BaseType.getFieldType(getVarDecl(BaseType), M); + return BaseType.getFieldType(getVarDecl(BaseType), M, context); case ProjectionKind::Enum: - return BaseType.getEnumElementType(getEnumElementDecl(BaseType), M); + return BaseType.getEnumElementType(getEnumElementDecl(BaseType), M, context); case ProjectionKind::Box: - return getSILBoxFieldType(BaseType.castTo(), - M.Types, getIndex()); + return getSILBoxFieldType(context, BaseType.castTo(), M.Types, + getIndex()); case ProjectionKind::Tuple: return BaseType.getTupleElementType(getIndex()); case ProjectionKind::Upcast: @@ -285,18 +286,20 @@ Projection::createAddressProjection(SILBuilder &B, SILLocation Loc, llvm_unreachable("Unhandled ProjectionKind in switch."); } -void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, - llvm::SmallVectorImpl &Out) { +void Projection::getFirstLevelProjections( + SILType Ty, SILModule &Mod, TypeExpansionContext context, + llvm::SmallVectorImpl &Out) { if (auto *S = Ty.getStructOrBoundGenericStruct()) { unsigned Count = 0; for (auto *VDecl : S->getStoredProperties()) { (void) VDecl; Projection P(ProjectionKind::Struct, Count++); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod)==Ty.getFieldType(VDecl, Mod)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getFieldType(VDecl, Mod, context)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -306,10 +309,11 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { Projection P(ProjectionKind::Tuple, i); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod) == Ty.getTupleElementType(i)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getTupleElementType(i)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -321,10 +325,11 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, (void) VDecl; Projection P(ProjectionKind::Class, Count++); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); - assert(X.getMostDerivedType(Mod)==Ty.getFieldType(VDecl, Mod)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == + Ty.getFieldType(VDecl, Mod, context)); + X.verify(Mod, context);); Out.push_back(P); } return; @@ -334,11 +339,10 @@ void Projection::getFirstLevelProjections(SILType Ty, SILModule &Mod, for (unsigned field : indices(Box->getLayout()->getFields())) { Projection P(ProjectionKind::Box, field); LLVM_DEBUG(ProjectionPath X(Ty); - assert(X.getMostDerivedType(Mod) == Ty); - X.append(P); - assert(X.getMostDerivedType(Mod) - == getSILBoxFieldType(Box, Mod.Types, field)); - X.verify(Mod);); + assert(X.getMostDerivedType(Mod, context) == Ty); X.append(P); + assert(X.getMostDerivedType(Mod, context) == + getSILBoxFieldType(context, Box, Mod.Types, field)); + X.verify(Mod, context);); (void)Box; Out.push_back(P); } @@ -580,12 +584,13 @@ void Projection::print(raw_ostream &os, SILType baseType) const { os << ""; } -raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { +raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M, + TypeExpansionContext context) const { os << "Projection Path ["; SILType IterType = getBaseType(); for (const Projection &IterProj : Path) { SILType BaseType = IterType; - IterType = IterProj.getType(IterType, M); + IterType = IterProj.getType(IterType, M, context); os << BaseType.getAddressType() << "\n "; @@ -596,16 +601,16 @@ raw_ostream &ProjectionPath::print(raw_ostream &os, SILModule &M) const { return os; } -void ProjectionPath::dump(SILModule &M) const { - print(llvm::dbgs(), M); +void ProjectionPath::dump(SILModule &M, TypeExpansionContext context) const { + print(llvm::dbgs(), M, context); } -void ProjectionPath::verify(SILModule &M) { +void ProjectionPath::verify(SILModule &M, TypeExpansionContext context) { #ifndef NDEBUG SILType IterTy = getBaseType(); assert(IterTy); for (auto &Proj : Path) { - IterTy = Proj.getType(IterTy, M); + IterTy = Proj.getType(IterTy, M, context); assert(IterTy); } #endif @@ -613,6 +618,7 @@ void ProjectionPath::verify(SILModule &M) { void ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, + TypeExpansionContext context, ProjectionPathList &Paths) { // Perform a BFS to expand the given type into projectionpath each of // which contains 1 field from the type. @@ -626,7 +632,7 @@ ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, // Get the next level projections based on current projection's type. ProjectionPath PP = Worklist.pop_back_val(); // Get the current type to process. - SILType Ty = PP.getMostDerivedType(*Mod); + SILType Ty = PP.getMostDerivedType(*Mod, context); LLVM_DEBUG(llvm::dbgs() << "Visiting type: " << Ty << "\n"); @@ -655,7 +661,7 @@ ProjectionPath::expandTypeIntoLeafProjectionPaths(SILType B, SILModule *Mod, // Get the first level projection of the current type. Projections.clear(); - Projection::getFirstLevelProjections(Ty, *Mod, Projections); + Projection::getFirstLevelProjections(Ty, *Mod, context, Projections); // Reached the end of the projection tree, this field can not be expanded // anymore. @@ -695,11 +701,12 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, continue; // Get the current type to process. - SILType Ty = PP.getMostDerivedType(Mod); + SILType Ty = PP.getMostDerivedType(Mod, F.getTypeExpansionContext()); // Get the first level projection of the current type. llvm::SmallVector Projections; - Projection::getFirstLevelProjections(Ty, Mod, Projections); + Projection::getFirstLevelProjections(Ty, Mod, F.getTypeExpansionContext(), + Projections); // Reached the end of the projection tree, this field can not be expanded // anymore. @@ -719,7 +726,8 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, for (auto &P : Projections) { ProjectionPath X(B); X.append(PP); - assert(PP.getMostDerivedType(Mod) == X.getMostDerivedType(Mod)); + assert(PP.getMostDerivedType(Mod, F.getTypeExpansionContext()) == + X.getMostDerivedType(Mod, F.getTypeExpansionContext())); X.append(P); Worklist.push_back(X); } @@ -728,8 +736,8 @@ bool ProjectionPath::hasUncoveredNonTrivials(SILType B, const SILFunction &F, // Check whether any path leads to a non-trivial type. for (auto &X : Paths) { - if (!X.getMostDerivedType(Mod).isTrivial(F)) - return true; + if (!X.getMostDerivedType(Mod, F.getTypeExpansionContext()).isTrivial(F)) + return true; } return false; } @@ -937,7 +945,8 @@ processUsersOfValue(ProjectionTree &Tree, // we have a projection to the next level children, create the next // level children nodes lazily. if (!Initialized) - createNextLevelChildren(Tree); + createNextLevelChildren( + Tree, TypeExpansionContext(*projectionInst->getFunction())); // Look up the Node for this projection add {User, ChildNode} to the // worklist. @@ -962,15 +971,14 @@ processUsersOfValue(ProjectionTree &Tree, } } -void -ProjectionTreeNode:: -createNextLevelChildrenForStruct(ProjectionTree &Tree, StructDecl *SD) { +void ProjectionTreeNode::createNextLevelChildrenForStruct( + ProjectionTree &Tree, TypeExpansionContext context, StructDecl *SD) { SILModule &Mod = Tree.getModule(); unsigned ChildIndex = 0; SILType Ty = getType(); for (VarDecl *VD : SD->getStoredProperties()) { assert(Tree.getNode(Index) == this && "Node is not mapped to itself?"); - SILType NodeTy = Ty.getFieldType(VD, Mod); + SILType NodeTy = Ty.getFieldType(VD, Mod, context); auto *Node = Tree.createChildForStruct(this, NodeTy, VD, ChildIndex++); LLVM_DEBUG(llvm::dbgs() << " Creating child for: " <(OwnershipKind); - ParentBB->insertArgument(ParentBB->args_end(), this); +SILArgument::SILArgument(ValueKind subClassKind, + SILBasicBlock *inputParentBlock, SILType type, + ValueOwnershipKind ownershipKind, + const ValueDecl *inputDecl) + : ValueBase(subClassKind, type, IsRepresentative::Yes), + parentBlock(inputParentBlock), decl(inputDecl) { + Bits.SILArgument.VOKind = static_cast(ownershipKind); + inputParentBlock->insertArgument(inputParentBlock->args_end(), this); } -SILArgument::SILArgument(ValueKind ChildKind, SILBasicBlock *ParentBB, - SILBasicBlock::arg_iterator Pos, SILType Ty, - ValueOwnershipKind OwnershipKind, const ValueDecl *D) - : ValueBase(ChildKind, Ty, IsRepresentative::Yes), ParentBB(ParentBB), - Decl(D) { - Bits.SILArgument.VOKind = static_cast(OwnershipKind); +SILArgument::SILArgument(ValueKind subClassKind, + SILBasicBlock *inputParentBlock, + SILBasicBlock::arg_iterator argArrayInsertPt, + SILType type, ValueOwnershipKind ownershipKind, + const ValueDecl *inputDecl) + : ValueBase(subClassKind, type, IsRepresentative::Yes), + parentBlock(inputParentBlock), decl(inputDecl) { + Bits.SILArgument.VOKind = static_cast(ownershipKind); // Function arguments need to have a decl. - assert( - !ParentBB->getParent()->isBare() && - ParentBB->getParent()->size() == 1 - ? D != nullptr - : true); - ParentBB->insertArgument(Pos, this); + assert(!inputParentBlock->getParent()->isBare() && + inputParentBlock->getParent()->size() == 1 + ? decl != nullptr + : true); + inputParentBlock->insertArgument(argArrayInsertPt, this); } @@ -70,90 +72,95 @@ SILModule &SILArgument::getModule() const { // expensive to call this helper instead of simply specifying phis with an // opcode. It results in repeated CFG traversals and repeated, unnecessary // switching over terminator opcodes. -bool SILPhiArgument::isPhiArgument() { +bool SILPhiArgument::isPhiArgument() const { // No predecessors indicates an unreachable block. if (getParent()->pred_empty()) return false; // Multiple predecessors require phis. - auto *predBB = getParent()->getSinglePredecessorBlock(); - if (!predBB) + auto *predBlock = getParent()->getSinglePredecessorBlock(); + if (!predBlock) return true; - auto *TI = predBB->getTerminator(); - return isa(TI) || isa(TI); + auto *termInst = predBlock->getTerminator(); + return isa(termInst) || isa(termInst); } -static SILValue getIncomingPhiValueForPred(const SILBasicBlock *BB, - const SILBasicBlock *Pred, - unsigned Index) { - const TermInst *TI = Pred->getTerminator(); - if (auto *BI = dyn_cast(TI)) - return BI->getArg(Index); +static SILValue getIncomingPhiValueForPred(const SILBasicBlock *parentBlock, + const SILBasicBlock *predBlock, + unsigned argIndex) { + const auto *predBlockTermInst = predBlock->getTerminator(); + if (auto *bi = dyn_cast(predBlockTermInst)) + return bi->getArg(argIndex); // FIXME: Disallowing critical edges in SIL would enormously simplify phi and // branch handling and reduce expensive analysis invalidation. If that is // done, then only BranchInst will participate in phi operands, eliminating // the need to search for the appropriate CondBranchInst operand. - return cast(TI)->getArgForDestBB(BB, Index); + return cast(predBlockTermInst) + ->getArgForDestBB(parentBlock, argIndex); } -SILValue SILPhiArgument::getIncomingPhiValue(SILBasicBlock *predBB) { +SILValue SILPhiArgument::getIncomingPhiValue(SILBasicBlock *predBlock) const { if (!isPhiArgument()) return SILValue(); - SILBasicBlock *Parent = getParent(); - assert(!Parent->pred_empty()); + const auto *parentBlock = getParent(); + assert(!parentBlock->pred_empty()); - unsigned Index = getIndex(); + unsigned argIndex = getIndex(); - assert(Parent->pred_end() - != std::find(Parent->pred_begin(), Parent->pred_end(), predBB)); + assert(parentBlock->pred_end() != std::find(parentBlock->pred_begin(), + parentBlock->pred_end(), + predBlock)); - return getIncomingPhiValueForPred(Parent, predBB, Index); + return getIncomingPhiValueForPred(parentBlock, predBlock, argIndex); } bool SILPhiArgument::getIncomingPhiValues( - llvm::SmallVectorImpl &ReturnedPhiValues) { + SmallVectorImpl &returnedPhiValues) const { if (!isPhiArgument()) return false; - SILBasicBlock *Parent = getParent(); - assert(!Parent->pred_empty()); + const auto *parentBlock = getParent(); + assert(!parentBlock->pred_empty()); - unsigned Index = getIndex(); - for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) { - SILValue Value = getIncomingPhiValueForPred(Parent, Pred, Index); - assert(Value); - ReturnedPhiValues.push_back(Value); + unsigned argIndex = getIndex(); + for (auto *predBlock : getParent()->getPredecessorBlocks()) { + SILValue incomingValue = + getIncomingPhiValueForPred(parentBlock, predBlock, argIndex); + assert(incomingValue); + returnedPhiValues.push_back(incomingValue); } return true; } bool SILPhiArgument::getIncomingPhiValues( - llvm::SmallVectorImpl> - &ReturnedPredBBAndPhiValuePairs) { + SmallVectorImpl> + &returnedPredBBAndPhiValuePairs) const { if (!isPhiArgument()) return false; - SILBasicBlock *Parent = getParent(); - assert(!Parent->pred_empty()); + const auto *parentBlock = getParent(); + assert(!parentBlock->pred_empty()); - unsigned Index = getIndex(); - for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) { - SILValue Value = getIncomingPhiValueForPred(Parent, Pred, Index); - assert(Value); - ReturnedPredBBAndPhiValuePairs.push_back({Pred, Value}); + unsigned argIndex = getIndex(); + for (auto *predBlock : getParent()->getPredecessorBlocks()) { + SILValue incomingValue = + getIncomingPhiValueForPred(parentBlock, predBlock, argIndex); + assert(incomingValue); + returnedPredBBAndPhiValuePairs.push_back({predBlock, incomingValue}); } return true; } -static SILValue getSingleTerminatorOperandForPred(const SILBasicBlock *BB, - const SILBasicBlock *Pred, - unsigned Index) { - const TermInst *TI = Pred->getTerminator(); +static SILValue +getSingleTerminatorOperandForPred(const SILBasicBlock *parentBlock, + const SILBasicBlock *predBlock, + unsigned argIndex) { + const auto *predTermInst = predBlock->getTerminator(); - switch (TI->getTermKind()) { + switch (predTermInst->getTermKind()) { case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::ThrowInst: @@ -167,61 +174,65 @@ static SILValue getSingleTerminatorOperandForPred(const SILBasicBlock *BB, case TermKind::YieldInst: return SILValue(); case TermKind::BranchInst: - return cast(TI)->getArg(Index); + return cast(predTermInst)->getArg(argIndex); case TermKind::CondBranchInst: - return cast(TI)->getArgForDestBB(BB, Index); + return cast(predTermInst) + ->getArgForDestBB(parentBlock, argIndex); case TermKind::CheckedCastBranchInst: - return cast(TI)->getOperand(); + return cast(predTermInst)->getOperand(); case TermKind::CheckedCastValueBranchInst: - return cast(TI)->getOperand(); + return cast(predTermInst)->getOperand(); case TermKind::SwitchEnumInst: - return cast(TI)->getOperand(); + return cast(predTermInst)->getOperand(); } llvm_unreachable("Unhandled TermKind?!"); } bool SILPhiArgument::getSingleTerminatorOperands( - llvm::SmallVectorImpl &OutArray) { - SILBasicBlock *Parent = getParent(); + SmallVectorImpl &returnedSingleTermOperands) const { + const auto *parentBlock = getParent(); - if (Parent->pred_empty()) + if (parentBlock->pred_empty()) return false; - unsigned Index = getIndex(); - for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) { - SILValue Value = getSingleTerminatorOperandForPred(Parent, Pred, Index); - if (!Value) + unsigned argIndex = getIndex(); + for (auto *predBlock : getParent()->getPredecessorBlocks()) { + SILValue incomingValue = + getSingleTerminatorOperandForPred(parentBlock, predBlock, argIndex); + if (!incomingValue) return false; - OutArray.push_back(Value); + returnedSingleTermOperands.push_back(incomingValue); } return true; } bool SILPhiArgument::getSingleTerminatorOperands( - llvm::SmallVectorImpl> &OutArray) { - SILBasicBlock *Parent = getParent(); + SmallVectorImpl> + &returnedSingleTermOperands) const { + const auto *parentBlock = getParent(); - if (Parent->pred_empty()) + if (parentBlock->pred_empty()) return false; - unsigned Index = getIndex(); - for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) { - SILValue Value = getSingleTerminatorOperandForPred(Parent, Pred, Index); - if (!Value) + unsigned argIndex = getIndex(); + for (auto *predBlock : getParent()->getPredecessorBlocks()) { + SILValue incomingValue = + getSingleTerminatorOperandForPred(parentBlock, predBlock, argIndex); + if (!incomingValue) return false; - OutArray.push_back({Pred, Value}); + returnedSingleTermOperands.push_back({predBlock, incomingValue}); } return true; } SILValue SILPhiArgument::getSingleTerminatorOperand() const { - const SILBasicBlock *Parent = getParent(); - const SILBasicBlock *PredBB = Parent->getSinglePredecessorBlock(); - if (!PredBB) + const auto *parentBlock = getParent(); + const auto *predBlock = parentBlock->getSinglePredecessorBlock(); + if (!predBlock) return SILValue(); - return getSingleTerminatorOperandForPred(Parent, PredBB, getIndex()); + return getSingleTerminatorOperandForPred(parentBlock, predBlock, getIndex()); } const SILPhiArgument *BranchInst::getArgForOperand(const Operand *oper) const { diff --git a/lib/SIL/SILBasicBlock.cpp b/lib/SIL/SILBasicBlock.cpp index 8cef1e8ed5ca2..027df3133a039 100644 --- a/lib/SIL/SILBasicBlock.cpp +++ b/lib/SIL/SILBasicBlock.cpp @@ -129,22 +129,24 @@ void SILBasicBlock::cloneArgumentList(SILBasicBlock *Other) { "Expected to both blocks to be entries or not"); if (isEntry()) { assert(args_empty() && "Expected to have no arguments"); - for (auto *FuncArg : Other->getFunctionArguments()) { + for (auto *FuncArg : Other->getSILFunctionArguments()) { createFunctionArgument(FuncArg->getType(), FuncArg->getDecl()); } return; } - for (auto *PHIArg : Other->getPhiArguments()) { + for (auto *PHIArg : Other->getSILPhiArguments()) { createPhiArgument(PHIArg->getType(), PHIArg->getOwnershipKind(), PHIArg->getDecl()); } } -SILFunctionArgument *SILBasicBlock::createFunctionArgument(SILType Ty, - const ValueDecl *D) { - assert(isEntry() && "Function Arguments can only be in the entry block"); +SILFunctionArgument * +SILBasicBlock::createFunctionArgument(SILType Ty, const ValueDecl *D, + bool disableEntryBlockVerification) { + assert((disableEntryBlockVerification || isEntry()) && + "Function Arguments can only be in the entry block"); const SILFunction *Parent = getParent(); auto OwnershipKind = ValueOwnershipKind( *Parent, Ty, @@ -167,7 +169,7 @@ SILFunctionArgument *SILBasicBlock::replaceFunctionArgument( SILFunction *F = getParent(); SILModule &M = F->getModule(); if (Ty.isTrivial(*F)) - Kind = ValueOwnershipKind::Any; + Kind = ValueOwnershipKind::None; assert(ArgumentList[i]->use_empty() && "Expected no uses of the old arg!"); @@ -193,7 +195,7 @@ SILPhiArgument *SILBasicBlock::replacePhiArgument(unsigned i, SILType Ty, SILFunction *F = getParent(); SILModule &M = F->getModule(); if (Ty.isTrivial(*F)) - Kind = ValueOwnershipKind::Any; + Kind = ValueOwnershipKind::None; assert(ArgumentList[i]->use_empty() && "Expected no uses of the old BB arg!"); @@ -238,7 +240,7 @@ SILPhiArgument *SILBasicBlock::createPhiArgument(SILType Ty, const ValueDecl *D) { assert(!isEntry() && "PHI Arguments can not be in the entry block"); if (Ty.isTrivial(*getParent())) - Kind = ValueOwnershipKind::Any; + Kind = ValueOwnershipKind::None; return new (getModule()) SILPhiArgument(this, Ty, Kind, D); } @@ -247,7 +249,7 @@ SILPhiArgument *SILBasicBlock::insertPhiArgument(arg_iterator Iter, SILType Ty, const ValueDecl *D) { assert(!isEntry() && "PHI Arguments can not be in the entry block"); if (Ty.isTrivial(*getParent())) - Kind = ValueOwnershipKind::Any; + Kind = ValueOwnershipKind::None; return new (getModule()) SILPhiArgument(this, Iter, Ty, Kind, D); } @@ -357,18 +359,12 @@ bool SILBasicBlock::isEntry() const { } /// Declared out of line so we can have a declaration of SILArgument. -PhiArgumentArrayRef SILBasicBlock::getPhiArguments() const { - return PhiArgumentArrayRef(getArguments(), [](SILArgument *arg) { - return cast(arg); - }); -} - -/// Declared out of line so we can have a declaration of SILArgument. -FunctionArgumentArrayRef SILBasicBlock::getFunctionArguments() const { - return FunctionArgumentArrayRef(getArguments(), [](SILArgument *arg) { - return cast(arg); - }); -} +#define ARGUMENT(NAME, PARENT) \ + NAME##ArrayRef SILBasicBlock::get##NAME##s() const { \ + return NAME##ArrayRef(getArguments(), \ + [](SILArgument *arg) { return cast(arg); }); \ + } +#include "swift/SIL/SILNodes.def" /// Returns true if this block ends in an unreachable or an apply of a /// no-return apply or builtin. diff --git a/lib/SIL/SILBuilder.cpp b/lib/SIL/SILBuilder.cpp index 8ab647a662fb2..a6a9bed4a3173 100644 --- a/lib/SIL/SILBuilder.cpp +++ b/lib/SIL/SILBuilder.cpp @@ -49,15 +49,14 @@ TupleInst *SILBuilder::createTuple(SILLocation loc, ArrayRef elts) { return createTuple(loc, tupleType, elts); } -SILType SILBuilder::getPartialApplyResultType(SILType origTy, unsigned argCount, - SILModule &M, - SubstitutionMap subs, - ParameterConvention calleeConvention, - PartialApplyInst::OnStackKind onStack) { +SILType SILBuilder::getPartialApplyResultType( + TypeExpansionContext context, SILType origTy, unsigned argCount, + SILModule &M, SubstitutionMap subs, ParameterConvention calleeConvention, + PartialApplyInst::OnStackKind onStack) { CanSILFunctionType FTI = origTy.castTo(); if (!subs.empty()) - FTI = FTI->substGenericArgs(M, subs); - + FTI = FTI->substGenericArgs(M, subs, context); + assert(!FTI->isPolymorphic() && "must provide substitutions for generic partial_apply"); auto params = FTI->getParameters(); @@ -79,9 +78,11 @@ SILType SILBuilder::getPartialApplyResultType(SILType origTy, unsigned argCount, results.append(FTI->getResults().begin(), FTI->getResults().end()); for (auto &result : results) { if (result.getConvention() == ResultConvention::UnownedInnerPointer) - result = SILResultInfo(result.getType(), ResultConvention::Unowned); + result = SILResultInfo(result.getReturnValueType(M, FTI), + ResultConvention::Unowned); else if (result.getConvention() == ResultConvention::Autoreleased) - result = SILResultInfo(result.getType(), ResultConvention::Owned); + result = SILResultInfo(result.getReturnValueType(M, FTI), + ResultConvention::Owned); } auto appliedFnType = SILFunctionType::get(nullptr, extInfo, @@ -91,6 +92,8 @@ SILType SILBuilder::getPartialApplyResultType(SILType origTy, unsigned argCount, FTI->getYields(), results, FTI->getOptionalErrorResult(), + SubstitutionMap(), + false, M.getASTContext()); return SILType::getPrimitiveObjectType(appliedFnType); @@ -100,25 +103,13 @@ ProjectBoxInst *SILBuilder::createProjectBox(SILLocation Loc, SILValue boxOperand, unsigned index) { auto boxTy = boxOperand->getType().castTo(); - auto fieldTy = getSILBoxFieldType(boxTy, getModule().Types, index); + auto fieldTy = getSILBoxFieldType(getTypeExpansionContext(), boxTy, + getModule().Types, index); return insert(new (getModule()) ProjectBoxInst( getSILDebugLocation(Loc), boxOperand, index, fieldTy)); } -// If legal, create an unchecked_ref_cast from the given operand and result -// type, otherwise return null. -SingleValueInstruction * -SILBuilder::tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op, - SILType ResultTy) { - if (!SILType::canRefCast(Op->getType(), ResultTy, getModule())) - return nullptr; - - return insert(UncheckedRefCastInst::create(getSILDebugLocation(Loc), Op, - ResultTy, getFunction(), - C.OpenedArchetypes)); -} - ClassifyBridgeObjectInst * SILBuilder::createClassifyBridgeObject(SILLocation Loc, SILValue value) { auto &ctx = getASTContext(); @@ -138,8 +129,8 @@ SILBuilder::createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty) { return insert(UncheckedTrivialBitCastInst::create( getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); - if (auto refCast = tryCreateUncheckedRefCast(Loc, Op, Ty)) - return refCast; + if (SILType::canRefCast(Op->getType(), Ty, getModule())) + return createUncheckedRefCast(Loc, Op, Ty); // The destination type is nontrivial, and may be smaller than the source // type, so RC identity cannot be assumed. @@ -291,7 +282,7 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) { case SILInstructionKind::Store##Name##Inst: \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") #define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: \ + case SILInstructionKind::StrongCopy##Name##ValueInst: \ return false; #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::LoadInst: @@ -300,7 +291,6 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) { case SILInstructionKind::StrongRetainInst: case SILInstructionKind::AllocStackInst: case SILInstructionKind::DeallocStackInst: - case SILInstructionKind::CopyUnownedValueInst: return false; default: break; @@ -508,11 +498,11 @@ void SILBuilder::addOpenedArchetypeOperands(SILInstruction *I) { ValueMetatypeInst *SILBuilder::createValueMetatype(SILLocation Loc, SILType MetatypeTy, SILValue Base) { - assert( - Base->getType().isLoweringOf( - getModule(), MetatypeTy.castTo().getInstanceType()) && - "value_metatype result must be formal metatype of the lowered operand " - "type"); + assert(Base->getType().isLoweringOf( + getTypeExpansionContext(), getModule(), + MetatypeTy.castTo().getInstanceType()) && + "value_metatype result must be formal metatype of the lowered operand " + "type"); return insert(new (getModule()) ValueMetatypeInst(getSILDebugLocation(Loc), MetatypeTy, Base)); } @@ -538,7 +528,8 @@ void SILBuilder::emitDestructureValueOperation( // In non qualified ownership SIL, drop back to using projection code. SmallVector projections; - Projection::getFirstLevelProjections(v->getType(), getModule(), projections); + Projection::getFirstLevelProjections(v->getType(), getModule(), + getTypeExpansionContext(), projections); llvm::transform(projections, std::back_inserter(results), [&](const Projection &p) -> SILValue { return p.createObjectProjection(*this, loc, v).get(); @@ -559,7 +550,8 @@ void SILBuilder::emitDestructureAddressOperation( } SmallVector projections; - Projection::getFirstLevelProjections(v->getType(), getModule(), projections); + Projection::getFirstLevelProjections(v->getType(), getModule(), + getTypeExpansionContext(), projections); llvm::transform(projections, std::back_inserter(results), [&](const Projection &p) -> SILValue { return p.createAddressProjection(*this, loc, v).get(); @@ -615,13 +607,15 @@ void SILBuilder::emitScopedBorrowOperation(SILLocation loc, SILValue original, } CheckedCastBranchInst *SILBuilder::createCheckedCastBranch( - SILLocation Loc, bool isExact, SILValue op, SILType destTy, + SILLocation Loc, bool isExact, SILValue op, + SILType destLoweredTy, CanType destFormalTy, SILBasicBlock *successBB, SILBasicBlock *failureBB, ProfileCounter target1Count, ProfileCounter target2Count) { assert((!hasOwnership() || !failureBB->getNumArguments() || failureBB->getArgument(0)->getType() == op->getType()) && "failureBB's argument doesn't match incoming argument type"); return insertTerminator(CheckedCastBranchInst::create( - getSILDebugLocation(Loc), isExact, op, destTy, successBB, failureBB, + getSILDebugLocation(Loc), isExact, op, + destLoweredTy, destFormalTy, successBB, failureBB, getFunction(), C.OpenedArchetypes, target1Count, target2Count)); } diff --git a/lib/SIL/SILConstants.cpp b/lib/SIL/SILConstants.cpp index 9442912092cb8..093b55ed0af97 100644 --- a/lib/SIL/SILConstants.cpp +++ b/lib/SIL/SILConstants.cpp @@ -20,7 +20,7 @@ using namespace swift; namespace swift { llvm::cl::opt - ConstExprLimit("constexpr-limit", llvm::cl::init(1024), + ConstExprLimit("constexpr-limit", llvm::cl::init(2048), llvm::cl::desc("Number of instructions interpreted in a" " constexpr function")); } @@ -66,7 +66,7 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const { os << "string: \"" << getStringValue() << "\"\n"; return; case RK_Aggregate: { - ArrayRef elements = getAggregateValue(); + ArrayRef elements = getAggregateMembers(); switch (elements.size()) { case 0: os << "agg: 0 elements []\n"; @@ -126,6 +126,29 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const { case RK_Array: { os << getArrayType() << ": \n"; getStorageOfArray().print(os, indent); + return; + } + case RK_Closure: { + SymbolicClosure *clo = getClosure(); + SILFunction *target = clo->getTarget(); + std::string targetName = target->getName(); + os << "closure: target: " << targetName; + ArrayRef args = clo->getCaptures(); + os << " captures [\n"; + for (SymbolicClosureArgument closureArg : args) { + os.indent(indent + 2) << closureArg.first << "\n"; + } + os.indent(indent) << "] values: [\n"; + for (SymbolicClosureArgument closureArg : args) { + Optional value = closureArg.second; + if (!value.hasValue()) { + os.indent(indent + 2) << "nil\n"; + continue; + } + value->print(os, indent + 2); + } + os.indent(indent) << "]\n"; + return; } } } @@ -162,6 +185,8 @@ SymbolicValue::Kind SymbolicValue::getKind() const { return ArrayStorage; case RK_Array: return Array; + case RK_Closure: + return Closure; } llvm_unreachable("covered switch"); } @@ -186,12 +211,12 @@ SymbolicValue::cloneInto(SymbolicValueAllocator &allocator) const { case RK_String: return SymbolicValue::getString(getStringValue(), allocator); case RK_Aggregate: { - auto elts = getAggregateValue(); + auto elts = getAggregateMembers(); SmallVector results; results.reserve(elts.size()); for (auto elt : elts) results.push_back(elt.cloneInto(allocator)); - return getAggregate(results, allocator); + return getAggregate(results, getAggregateType(), allocator); } case RK_EnumWithPayload: { return getEnumWithPayload( @@ -219,6 +244,13 @@ SymbolicValue::cloneInto(SymbolicValueAllocator &allocator) const { SymbolicValue clonedStorage = getStorageOfArray().cloneInto(allocator); return getArray(getArrayType(), clonedStorage, allocator); } + case RK_Closure: { + SymbolicClosure *clo = getClosure(); + ArrayRef closureArgs = clo->getCaptures(); + return SymbolicValue::makeClosure(clo->getTarget(), closureArgs, + clo->getCallSubstitutionMap(), + clo->getClosureInst(), allocator); + } } llvm_unreachable("covered switch"); } @@ -320,24 +352,71 @@ StringRef SymbolicValue::getStringValue() const { // Aggregates //===----------------------------------------------------------------------===// -/// This returns a constant Symbolic value with the specified elements in it. -/// This assumes that the elements lifetime has been managed for this. -SymbolicValue SymbolicValue::getAggregate(ArrayRef elements, - SymbolicValueAllocator &allocator) { - // Copy the elements into the bump pointer. - auto *resultElts = allocator.allocate(elements.size()); - std::uninitialized_copy(elements.begin(), elements.end(), resultElts); +namespace swift { + +/// Representation of a constant aggregate namely a struct or a tuple. +struct AggregateSymbolicValue final + : private llvm::TrailingObjects { + friend class llvm::TrailingObjects; + + const Type aggregateType; + + const unsigned numElements; + + static AggregateSymbolicValue *create(ArrayRef members, + Type aggregateType, + SymbolicValueAllocator &allocator) { + auto byteSize = + AggregateSymbolicValue::totalSizeToAlloc(members.size()); + auto rawMem = allocator.allocate(byteSize, alignof(AggregateSymbolicValue)); + + // Placement initialize the object. + auto *aggregate = + ::new (rawMem) AggregateSymbolicValue(aggregateType, members.size()); + std::uninitialized_copy(members.begin(), members.end(), + aggregate->getTrailingObjects()); + return aggregate; + } + + /// Return the type of the aggregate. + Type getAggregateType() const { return aggregateType; } + + /// Return the symbolic values of members. + ArrayRef getMemberValues() const { + return {getTrailingObjects(), numElements}; + } + + // This is used by the llvm::TrailingObjects base class. + size_t numTrailingObjects(OverloadToken) const { + return numElements; + } + +private: + AggregateSymbolicValue() = delete; + AggregateSymbolicValue(const AggregateSymbolicValue &) = delete; + AggregateSymbolicValue(Type aggregateType, unsigned numElements) + : aggregateType(aggregateType), numElements(numElements) {} +}; +} // namespace swift +SymbolicValue SymbolicValue::getAggregate(ArrayRef members, + Type aggregateType, + SymbolicValueAllocator &allocator) { SymbolicValue result; result.representationKind = RK_Aggregate; - result.value.aggregate = resultElts; - result.auxInfo.aggregateNumElements = elements.size(); + result.value.aggregate = + AggregateSymbolicValue::create(members, aggregateType, allocator); return result; } -ArrayRef SymbolicValue::getAggregateValue() const { +ArrayRef SymbolicValue::getAggregateMembers() const { + assert(getKind() == Aggregate); + return value.aggregate->getMemberValues(); +} + +Type SymbolicValue::getAggregateType() const { assert(getKind() == Aggregate); - return ArrayRef(value.aggregate, auxInfo.aggregateNumElements); + return value.aggregate->getAggregateType(); } //===----------------------------------------------------------------------===// @@ -661,6 +740,49 @@ Type SymbolicValue::getArrayType() const { return value.array->getType(); } +//===----------------------------------------------------------------------===// +// Symbolic Closure +//===----------------------------------------------------------------------===// + +SymbolicValue SymbolicValue::makeClosure(SILFunction *target, + ArrayRef args, + SubstitutionMap substMap, + SingleValueInstruction *closureInst, + SymbolicValueAllocator &allocator) { + auto clo = + SymbolicClosure::create(target, args, substMap, closureInst, allocator); + SymbolicValue result; + result.representationKind = RK_Closure; + result.value.closure = clo; + return result; +} + +SymbolicClosure *SymbolicClosure::create(SILFunction *target, + ArrayRef args, + SubstitutionMap substMap, + SingleValueInstruction *closureInst, + SymbolicValueAllocator &allocator) { + // Determine whether there are captured arguments without a symbolic value. + bool hasNonConstantCapture = false; + for (SymbolicClosureArgument closureArg : args) { + if (!closureArg.second) { + hasNonConstantCapture = true; + break; + } + } + + auto byteSizeOfArgs = + SymbolicClosure::totalSizeToAlloc(args.size()); + auto rawMem = allocator.allocate(byteSizeOfArgs, alignof(SymbolicClosure)); + // Placement initialize the object. + auto closure = ::new (rawMem) SymbolicClosure( + target, args.size(), substMap, closureInst, hasNonConstantCapture); + std::uninitialized_copy( + args.begin(), args.end(), + closure->getTrailingObjects()); + return closure; +} + //===----------------------------------------------------------------------===// // Higher level code //===----------------------------------------------------------------------===// @@ -702,7 +824,7 @@ SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const { while (1) { if (result.getKind() != Aggregate) return result; - auto elts = result.getAggregateValue(); + auto elts = result.getAggregateMembers(); if (elts.size() != 1) return result; result = elts[0]; @@ -724,6 +846,12 @@ static void getWitnessMethodName(WitnessMethodInst *witnessMethodInst, } } +/// A helper function to pretty print function names in diagnostics. +static std::string demangleSymbolNameForDiagnostics(StringRef name) { + return Demangle::demangleSymbolAsString( + name, Demangle::DemangleOptions::SimplifiedUIDemangleOptions()); +} + /// Given that this is an 'Unknown' value, emit diagnostic notes providing /// context about what the problem is. Specifically, point to interesting /// source locations and function calls in the call stack. @@ -842,7 +970,7 @@ void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) { case UnknownReason::CalleeImplementationUnknown: { SILFunction *callee = unknownReason.getCalleeWithoutImplmentation(); std::string demangledCalleeName = - Demangle::demangleSymbolAsString(callee->getName()); + demangleSymbolNameForDiagnostics(callee->getName()); diagnose(ctx, diagLoc, diag::constexpr_found_callee_with_no_body, StringRef(demangledCalleeName)); if (emitTriggerLocInDiag) @@ -850,12 +978,35 @@ void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) { triggerLocSkipsInternalLocs); return; } + case UnknownReason::CallArgumentUnknown: { + unsigned argNumber = unknownReason.getArgumentIndex() + 1; + ApplyInst *call = dyn_cast(unknownNode); + assert(call); + SILFunction *callee = call->getCalleeFunction(); + assert(callee); + std::string demangledCalleeName = + demangleSymbolNameForDiagnostics(callee->getName()); + diagnose(ctx, diagLoc, diag::constexpr_found_call_with_unknown_arg, + demangledCalleeName, + (Twine(argNumber) + llvm::getOrdinalSuffix(argNumber)).str()); + if (emitTriggerLocInDiag) + diagnose(ctx, triggerLoc, diag::constexpr_call_with_unknown_arg, + triggerLocSkipsInternalLocs); + return; + } case UnknownReason::UntrackedSILValue: diagnose(ctx, diagLoc, diag::constexpr_untracked_sil_value_use_found); if (emitTriggerLocInDiag) diagnose(ctx, triggerLoc, diag::constexpr_untracked_sil_value_used_here, triggerLocSkipsInternalLocs); return; + case UnknownReason::UnknownCastResult: { + diagnose(ctx, diagLoc, diag::constexpr_unevaluable_cast_found); + if (emitTriggerLocInDiag) + diagnose(ctx, triggerLoc, diag::constexpr_unevaluable_cast_used_here, + triggerLocSkipsInternalLocs); + return; + } case UnknownReason::UnknownWitnessMethodConformance: { SmallString<8> witnessMethodName; getWitnessMethodName(dyn_cast(unknownNode), @@ -919,7 +1070,7 @@ static SymbolicValue getIndexedElement(SymbolicValue aggregate, elt = aggregate.getStoredElements(arrayEltTy)[elementNo]; eltType = arrayEltTy; } else { - elt = aggregate.getAggregateValue()[elementNo]; + elt = aggregate.getAggregateMembers()[elementNo]; if (auto *decl = type->getStructOrBoundGenericStruct()) { eltType = decl->getStoredProperties()[elementNo]->getType(); } else if (auto tuple = type->getAs()) { @@ -973,7 +1124,7 @@ static SymbolicValue setIndexedElement(SymbolicValue aggregate, SmallVector newElts(numMembers, SymbolicValue::getUninitMemory()); - aggregate = SymbolicValue::getAggregate(newElts, allocator); + aggregate = SymbolicValue::getAggregate(newElts, type, allocator); } assert((aggregate.getKind() == SymbolicValue::Aggregate || @@ -990,7 +1141,7 @@ static SymbolicValue setIndexedElement(SymbolicValue aggregate, oldElts = aggregate.getStoredElements(arrayEltTy); eltType = arrayEltTy; } else { - oldElts = aggregate.getAggregateValue(); + oldElts = aggregate.getAggregateMembers(); if (auto *decl = type->getStructOrBoundGenericStruct()) { eltType = decl->getStoredProperties()[elementNo]->getType(); } else if (auto tuple = type->getAs()) { @@ -1007,8 +1158,12 @@ static SymbolicValue setIndexedElement(SymbolicValue aggregate, setIndexedElement(newElts[elementNo], accessPath.drop_front(), newElement, eltType, allocator); - if (aggregate.getKind() == SymbolicValue::Aggregate) - return SymbolicValue::getAggregate(newElts, allocator); + if (aggregate.getKind() == SymbolicValue::Aggregate) { + Type aggregateType = aggregate.getAggregateType(); + assert(aggregateType->isEqual(type) && + "input type does not match the type of the aggregate"); + return SymbolicValue::getAggregate(newElts, aggregateType, allocator); + } return aggregate = SymbolicValue::getSymbolicArrayStorage( newElts, eltType->getCanonicalType(), allocator); diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 8d4cf1de29e51..b524432785176 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -958,31 +958,52 @@ SubclassScope SILDeclRef::getSubclassScope() const { if (!isa(decl)) return SubclassScope::NotApplicable; - // If this declaration is a function which goes into a vtable, then it's - // symbol must be as visible as its class, because derived classes have to put - // all less visible methods of the base class into their vtables. + DeclContext *context = decl->getDeclContext(); + + // Only methods in non-final classes go in the vtable. + auto *classType = dyn_cast(context); + if (!classType || classType->isFinal()) + return SubclassScope::NotApplicable; + + // If a method appears in the vtable of a class, we must give it's symbol + // special consideration when computing visibility because the SIL-level + // linkage does not map to the symbol's visibility in a straightforward + // way. + // + // In particular, the rules are: + // - If the class metadata is not resilient, then all method symbols must + // be visible from any translation unit where a subclass might be defined, + // because the subclass metadata will re-emit all vtable entries. + // + // - For resilient classes, we do the opposite: generally, a method's symbol + // can be hidden from other translation units, because we want to enforce + // that resilient access patterns are used for method calls and overrides. + // + // Constructors and final methods are the exception here, because they can + // be called directly. + + // FIXME: This is too narrow. Any class with resilient metadata should + // probably have this, at least for method overrides that don't add new + // vtable entries. + bool isResilientClass = classType->isResilient(); if (auto *CD = dyn_cast(decl)) { + if (isResilientClass) + return SubclassScope::NotApplicable; // Initializing entry points do not appear in the vtable. if (kind == SILDeclRef::Kind::Initializer) return SubclassScope::NotApplicable; - // Non-required convenience inits do not apper in the vtable. + // Non-required convenience inits do not appear in the vtable. if (!CD->isRequired() && !CD->isDesignatedInit()) return SubclassScope::NotApplicable; } else if (isa(decl)) { - // Detructors do not appear in the vtable. + // Destructors do not appear in the vtable. return SubclassScope::NotApplicable; } else { assert(isa(decl)); } - DeclContext *context = decl->getDeclContext(); - - // Methods from extensions don't go in the vtable. - if (isa(context)) - return SubclassScope::NotApplicable; - - // Various forms of thunks don't either. + // Various forms of thunks don't go in the vtable. if (isThunk() || isForeign) return SubclassScope::NotApplicable; @@ -990,34 +1011,41 @@ SubclassScope SILDeclRef::getSubclassScope() const { if (isDefaultArgGenerator()) return SubclassScope::NotApplicable; - // Only non-final methods in non-final classes go in the vtable. - auto *classType = context->getSelfClassDecl(); - if (!classType || classType->isFinal()) - return SubclassScope::NotApplicable; + if (decl->isFinal()) { + // Final methods only go in the vtable if they override something. + if (!decl->getOverriddenDecl()) + return SubclassScope::NotApplicable; - if (decl->isFinal()) - return SubclassScope::NotApplicable; + // In the resilient case, we're going to be making symbols _less_ + // visible, so make sure we stop now; final methods can always be + // called directly. + if (isResilientClass) + return SubclassScope::Internal; + } assert(decl->getEffectiveAccess() <= classType->getEffectiveAccess() && "class must be as visible as its members"); - // FIXME: This is too narrow. Any class with resilient metadata should - // probably have this, at least for method overrides that don't add new - // vtable entries. - if (classType->isResilient()) { - if (isa(decl)) - return SubclassScope::NotApplicable; + if (isResilientClass) { + // The symbol should _only_ be reached via the vtable, so we're + // going to make it hidden. return SubclassScope::Resilient; } switch (classType->getEffectiveAccess()) { case AccessLevel::Private: case AccessLevel::FilePrivate: + // If the class is private, it can only be subclassed from the same + // SILModule, so we don't need to do anything. return SubclassScope::NotApplicable; case AccessLevel::Internal: case AccessLevel::Public: + // If the class is internal or public, it can only be subclassed from + // the same AST Module, but possibly a different SILModule. return SubclassScope::Internal; case AccessLevel::Open: + // If the class is open, it can be subclassed from a different + // AST Module. All method symbols are public. return SubclassScope::External; } @@ -1064,7 +1092,8 @@ bool SILDeclRef::canBeDynamicReplacement() const { bool SILDeclRef::isDynamicallyReplaceable() const { if (kind == SILDeclRef::Kind::DefaultArgGenerator) return false; - if (isStoredPropertyInitializer()) + if (isStoredPropertyInitializer() || + kind == SILDeclRef::Kind::PropertyWrapperBackingInitializer) return false; // Class allocators are not dynamic replaceable. diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index c6a616fb213c8..352cb1acb5335 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -44,7 +44,7 @@ SILSpecializeAttr *SILSpecializeAttr::create(SILModule &M, } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { - if (getLoweredFunctionType()->getGenericSignature()) { + if (getLoweredFunctionType()->getInvocationGenericSignature()) { Attr->F = this; SpecializeAttrSet.push_back(Attr); } @@ -167,6 +167,17 @@ bool SILFunction::hasForeignBody() const { return SILDeclRef::isClangGenerated(getClangNode()); } +const SILFunction *SILFunction::getOriginOfSpecialization() const { + if (!isSpecialization()) + return nullptr; + + const SILFunction *p = getSpecializationInfo()->getParent(); + while (p->isSpecialization()) { + p = p->getSpecializationInfo()->getParent(); + } + return p; +} + void SILFunction::numberValues(llvm::DenseMap & ValueToNumberMap) const { unsigned idx = 0; @@ -220,7 +231,7 @@ SILType GenericEnvironment::mapTypeIntoContext(SILModule &M, SILType type) const { assert(!type.hasArchetype()); - auto genericSig = getGenericSignature()->getCanonicalSignature(); + auto genericSig = getGenericSignature().getCanonicalSignature(); return type.subst(M, QueryInterfaceTypeSubstitutions(this), LookUpConformanceInSignature(genericSig.getPointer()), @@ -229,40 +240,43 @@ SILType GenericEnvironment::mapTypeIntoContext(SILModule &M, bool SILFunction::isNoReturnFunction() const { return SILType::getPrimitiveObjectType(getLoweredFunctionType()) - .isNoReturnFunction(); + .isNoReturnFunction(getModule()); } const TypeLowering & SILFunction::getTypeLowering(AbstractionPattern orig, Type subst) { return getModule().Types.getTypeLowering(orig, subst, - getResilienceExpansion()); + TypeExpansionContext(*this)); } const TypeLowering &SILFunction::getTypeLowering(Type t) const { - return getModule().Types.getTypeLowering(t, getResilienceExpansion()); + return getModule().Types.getTypeLowering(t, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(AbstractionPattern orig, Type subst) const { return getModule().Types.getLoweredType(orig, subst, - getResilienceExpansion()); + TypeExpansionContext(*this)); } SILType SILFunction::getLoweredType(Type t) const { - return getModule().Types.getLoweredType(t, getResilienceExpansion()); + return getModule().Types.getLoweredType(t, TypeExpansionContext(*this)); } SILType SILFunction::getLoweredLoadableType(Type t) const { auto &M = getModule(); - return M.Types.getLoweredLoadableType(t, getResilienceExpansion(), M); + return M.Types.getLoweredLoadableType(t, TypeExpansionContext(*this), M); } const TypeLowering &SILFunction::getTypeLowering(SILType type) const { - return getModule().Types.getTypeLowering(type, getResilienceExpansion()); + return getModule().Types.getTypeLowering(type, *this); } +SILType SILFunction::getLoweredType(SILType t) const { + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); +} bool SILFunction::isTypeABIAccessible(SILType type) const { - return getModule().isTypeABIAccessible(type, getResilienceExpansion()); + return getModule().isTypeABIAccessible(type, TypeExpansionContext(*this)); } bool SILFunction::isWeakImported() const { diff --git a/lib/SIL/SILFunctionBuilder.cpp b/lib/SIL/SILFunctionBuilder.cpp index e2abe75068a65..0330b3d93cca0 100644 --- a/lib/SIL/SILFunctionBuilder.cpp +++ b/lib/SIL/SILFunctionBuilder.cpp @@ -76,13 +76,13 @@ void SILFunctionBuilder::addFunctionAttributes(SILFunction *F, SILFunctionTypeRepresentation::ObjCMethod) return; - auto *replacedFuncAttr = Attrs.getAttribute(); - if (!replacedFuncAttr) + // Only assign replacements when the thing being replaced is function-like and + // explicitly declared. + auto *origDecl = decl->getDynamicallyReplacedDecl(); + auto *replacedDecl = dyn_cast_or_null(origDecl); + if (!replacedDecl) return; - auto *replacedDecl = replacedFuncAttr->getReplacedFunction(); - assert(replacedDecl); - if (decl->isObjC()) { F->setObjCReplacement(replacedDecl); return; @@ -107,7 +107,8 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant, ForDefinition_t forDefinition, ProfileCounter entryCount) { auto nameTmp = constant.mangle(); - auto constantType = mod.Types.getConstantFunctionType(constant); + auto constantType = mod.Types.getConstantFunctionType( + TypeExpansionContext::minimal(), constant); SILLinkage linkage = constant.getLinkage(forDefinition); if (auto fn = mod.lookUpFunction(nameTmp)) { diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index a69f7522d96ce..8b667f595bdb2 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -22,6 +22,7 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/ForeignInfo.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/ProtocolConformance.h" @@ -40,12 +41,83 @@ using namespace swift; using namespace swift::Lowering; -SILType SILFunctionType::getDirectFormalResultsType() { +SILType SILFunctionType::substInterfaceType(SILModule &M, + SILType interfaceType) const { + if (getSubstitutions().empty()) + return interfaceType; + + return interfaceType.subst(M, getSubstitutions()); +} + +CanSILFunctionType SILFunctionType::getUnsubstitutedType(SILModule &M) const { + auto mutableThis = const_cast(this); + if (!getSubstitutions()) + return CanSILFunctionType(mutableThis); + + if (!isGenericSignatureImplied()) + return withSubstitutions(SubstitutionMap()); + + SmallVector params; + SmallVector yields; + SmallVector results; + Optional errorResult; + + for (auto param : getParameters()) { + params.push_back( + param.getWithInterfaceType(param.getArgumentType(M, this))); + } + + for (auto yield : getYields()) { + yields.push_back( + yield.getWithInterfaceType(yield.getArgumentType(M, this))); + } + + for (auto result : getResults()) { + results.push_back( + result.getWithInterfaceType(result.getReturnValueType(M, this))); + } + + if (auto error = getOptionalErrorResult()) { + errorResult = + error->getWithInterfaceType(error->getReturnValueType(M, this)); + } + + return SILFunctionType::get(GenericSignature(), getExtInfo(), + getCoroutineKind(), + getCalleeConvention(), + params, yields, results, errorResult, + SubstitutionMap(), false, + mutableThis->getASTContext()); +} + +CanType SILParameterInfo::getArgumentType(SILModule &M, + const SILFunctionType *t) const { + // TODO: We should always require a function type. + if (t) + return t->substInterfaceType(M, + SILType::getPrimitiveAddressType(getInterfaceType())) + .getASTType(); + + return getInterfaceType(); +} + +CanType SILResultInfo::getReturnValueType(SILModule &M, + const SILFunctionType *t) const { + // TODO: We should always require a function type. + if (t) + return t->substInterfaceType(M, + SILType::getPrimitiveAddressType(getInterfaceType())) + .getASTType(); + + return getInterfaceType(); +} + +SILType SILFunctionType::getDirectFormalResultsType(SILModule &M) { CanType type; if (getNumDirectFormalResults() == 0) { type = getASTContext().TheEmptyTupleType; } else if (getNumDirectFormalResults() == 1) { - type = getSingleDirectFormalResult().getType(); + type = getSingleDirectFormalResult().getReturnValueType(M, this); } else { auto &cache = getMutableFormalResultsCache(); if (cache) { @@ -54,7 +126,7 @@ SILType SILFunctionType::getDirectFormalResultsType() { SmallVector elts; for (auto result : getResults()) if (!result.isFormalIndirect()) - elts.push_back(result.getType()); + elts.push_back(result.getReturnValueType(M, this)); type = CanType(TupleType::get(elts, getASTContext())); cache = type; } @@ -62,12 +134,12 @@ SILType SILFunctionType::getDirectFormalResultsType() { return SILType::getPrimitiveObjectType(type); } -SILType SILFunctionType::getAllResultsType() { +SILType SILFunctionType::getAllResultsInterfaceType() { CanType type; if (getNumResults() == 0) { type = getASTContext().TheEmptyTupleType; } else if (getNumResults() == 1) { - type = getResults()[0].getType(); + type = getResults()[0].getInterfaceType(); } else { auto &cache = getMutableAllResultsCache(); if (cache) { @@ -75,7 +147,7 @@ SILType SILFunctionType::getAllResultsType() { } else { SmallVector elts; for (auto result : getResults()) - elts.push_back(result.getType()); + elts.push_back(result.getInterfaceType()); type = CanType(TupleType::get(elts, getASTContext())); cache = type; } @@ -83,14 +155,18 @@ SILType SILFunctionType::getAllResultsType() { return SILType::getPrimitiveObjectType(type); } -SILType SILFunctionType::getFormalCSemanticResult() { +SILType SILFunctionType::getAllResultsSubstType(SILModule &M) { + return substInterfaceType(M, getAllResultsInterfaceType()); +} + +SILType SILFunctionType::getFormalCSemanticResult(SILModule &M) { assert(getLanguage() == SILFunctionLanguage::C); assert(getNumResults() <= 1); - return getDirectFormalResultsType(); + return getDirectFormalResultsType(M); } -CanType SILFunctionType::getSelfInstanceType() const { - auto selfTy = getSelfParameter().getType(); +CanType SILFunctionType::getSelfInstanceType(SILModule &M) const { + auto selfTy = getSelfParameter().getArgumentType(M, this); // If this is a static method, get the instance type. if (auto metaTy = dyn_cast(selfTy)) @@ -100,9 +176,11 @@ CanType SILFunctionType::getSelfInstanceType() const { } ClassDecl * -SILFunctionType::getWitnessMethodClass() const { - auto selfTy = getSelfInstanceType(); - auto genericSig = getGenericSignature(); +SILFunctionType::getWitnessMethodClass(SILModule &M) const { + // TODO: When witnesses use substituted types, we'd get this from the + // substitution map. + auto selfTy = getSelfInstanceType(M); + auto genericSig = getSubstGenericSignature(); if (auto paramTy = dyn_cast(selfTy)) { assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0); auto superclass = genericSig->getSuperclassBound(paramTy); @@ -113,6 +191,196 @@ SILFunctionType::getWitnessMethodClass() const { return nullptr; } +// Returns the canonical generic signature for an autodiff derivative function +// given an existing derivative function generic signature. All +// differentiability parameters are required to conform to `Differentiable`. +static CanGenericSignature getAutoDiffDerivativeFunctionGenericSignature( + CanGenericSignature derivativeFnGenSig, + ArrayRef originalParameters, + IndexSubset *parameterIndices, ModuleDecl *module) { + if (!derivativeFnGenSig) + return nullptr; + auto &ctx = module->getASTContext(); + GenericSignatureBuilder builder(ctx); + // Add derivative function generic signature. + builder.addGenericSignature(derivativeFnGenSig); + // All differentiability parameters are required to conform to + // `Differentiable`. + auto source = + GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); + auto *differentiableProtocol = + ctx.getProtocol(KnownProtocolKind::Differentiable); + for (unsigned paramIdx : parameterIndices->getIndices()) { + auto paramType = originalParameters[paramIdx].getInterfaceType(); + Requirement req(RequirementKind::Conformance, paramType, + differentiableProtocol->getDeclaredType()); + builder.addRequirement(req, source, module); + } + return std::move(builder) + .computeGenericSignature(SourceLoc(), /*allowConcreteGenericParams*/ true) + ->getCanonicalSignature(); +} + +CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( + IndexSubset *parameterIndices, unsigned resultIndex, + AutoDiffDerivativeFunctionKind kind, TypeConverter &TC, + LookupConformanceFn lookupConformance, + CanGenericSignature derivativeFnGenSig, bool isReabstractionThunk) { + auto &ctx = getASTContext(); + + // Returns true if `index` is a differentiability parameter index. + auto isDiffParamIndex = [&](unsigned index) -> bool { + return index < parameterIndices->getCapacity() && + parameterIndices->contains(index); + }; + + // Calculate differentiability parameter infos. + SmallVector diffParams; + for (auto valueAndIndex : enumerate(getParameters())) + if (isDiffParamIndex(valueAndIndex.index())) + diffParams.push_back(valueAndIndex.value()); + + // Get the canonical derivative function generic signature. + if (!derivativeFnGenSig) + derivativeFnGenSig = getSubstGenericSignature(); + derivativeFnGenSig = getAutoDiffDerivativeFunctionGenericSignature( + derivativeFnGenSig, getParameters(), parameterIndices, &TC.M); + + // Given a type, returns its formal SIL parameter info. + auto getTangentParameterInfoForOriginalResult = + [&](CanType tanType, ResultConvention origResConv) -> SILParameterInfo { + AbstractionPattern pattern(derivativeFnGenSig, tanType); + auto &tl = + TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); + ParameterConvention conv; + switch (origResConv) { + case ResultConvention::Owned: + case ResultConvention::Autoreleased: + conv = tl.isTrivial() ? ParameterConvention::Direct_Unowned + : ParameterConvention::Direct_Guaranteed; + break; + case ResultConvention::Unowned: + case ResultConvention::UnownedInnerPointer: + conv = ParameterConvention::Direct_Unowned; + break; + case ResultConvention::Indirect: + conv = ParameterConvention::Indirect_In_Guaranteed; + break; + } + return {tanType, conv}; + }; + + // Given a type, returns its formal SIL result info. + auto getTangentResultInfoForOriginalParameter = + [&](CanType tanType, ParameterConvention origParamConv) -> SILResultInfo { + AbstractionPattern pattern(derivativeFnGenSig, tanType); + auto &tl = + TC.getTypeLowering(pattern, tanType, TypeExpansionContext::minimal()); + ResultConvention conv; + switch (origParamConv) { + case ParameterConvention::Direct_Owned: + case ParameterConvention::Direct_Guaranteed: + case ParameterConvention::Direct_Unowned: + conv = + tl.isTrivial() ? ResultConvention::Unowned : ResultConvention::Owned; + break; + case ParameterConvention::Indirect_In: + case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_In_Constant: + case ParameterConvention::Indirect_In_Guaranteed: + case ParameterConvention::Indirect_InoutAliasable: + conv = ResultConvention::Indirect; + break; + } + return {tanType, conv}; + }; + + CanSILFunctionType closureType; + switch (kind) { + case AutoDiffDerivativeFunctionKind::JVP: { + SmallVector differentialParams; + for (auto ¶m : diffParams) { + auto paramTan = + param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + differentialParams.push_back( + {paramTan->getCanonicalType(), param.getConvention()}); + } + SmallVector differentialResults; + auto &result = getResults()[resultIndex]; + auto resultTan = + result.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(resultTan && "Result type does not have a tangent space?"); + differentialResults.push_back( + {resultTan->getCanonicalType(), result.getConvention()}); + closureType = SILFunctionType::get( + /*genericSignature*/ nullptr, ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, differentialParams, {}, + differentialResults, None, getSubstitutions(), + isGenericSignatureImplied(), ctx); + break; + } + case AutoDiffDerivativeFunctionKind::VJP: { + SmallVector pullbackParams; + auto &origRes = getResults()[resultIndex]; + auto resultTan = + origRes.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(resultTan && "Result type does not have a tangent space?"); + pullbackParams.push_back(getTangentParameterInfoForOriginalResult( + resultTan->getCanonicalType(), origRes.getConvention())); + SmallVector pullbackResults; + for (auto ¶m : diffParams) { + auto paramTan = + param.getInterfaceType()->getAutoDiffTangentSpace(lookupConformance); + assert(paramTan && "Parameter type does not have a tangent space?"); + pullbackResults.push_back(getTangentResultInfoForOriginalParameter( + paramTan->getCanonicalType(), param.getConvention())); + } + closureType = SILFunctionType::get( + /*genericSignature*/ nullptr, ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, pullbackParams, {}, + pullbackResults, {}, getSubstitutions(), isGenericSignatureImplied(), + ctx); + break; + } + } + + SmallVector newParameters; + newParameters.reserve(getNumParameters()); + for (auto ¶m : getParameters()) { + newParameters.push_back(param.getWithInterfaceType( + param.getInterfaceType()->getCanonicalType(derivativeFnGenSig))); + } + // TODO(TF-1124): Upstream reabstraction thunk derivative typing rules. + // Blocked by TF-1125: `SILFunctionType::getWithDifferentiability`. + SmallVector newResults; + newResults.reserve(getNumResults() + 1); + for (auto &result : getResults()) { + newResults.push_back(result.getWithInterfaceType( + result.getInterfaceType()->getCanonicalType(derivativeFnGenSig))); + } + newResults.push_back({closureType->getCanonicalType(derivativeFnGenSig), + ResultConvention::Owned}); + // Derivative function type has a generic signature only if the original + // function type does, and if `derivativeFnGenSig` does not have all concrete + // generic parameters. + CanGenericSignature canGenSig; + if (getSubstGenericSignature() && derivativeFnGenSig && + !derivativeFnGenSig->areAllParamsConcrete()) + canGenSig = derivativeFnGenSig; + // If original function is `@convention(c)`, the derivative function should + // have `@convention(thin)`. IRGen does not support `@convention(c)` functions + // with multiple results. + auto extInfo = getExtInfo(); + if (getRepresentation() == SILFunctionTypeRepresentation::CFunctionPointer) + extInfo = extInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); + return SILFunctionType::get(canGenSig, extInfo, getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), + newResults, getOptionalErrorResult(), + getSubstitutions(), isGenericSignatureImplied(), + ctx, getWitnessMethodConformanceOrInvalid()); +} + static CanType getKnownType(Optional &cacheSlot, ASTContext &C, StringRef moduleName, StringRef typeName) { if (!cacheSlot) { @@ -126,7 +394,7 @@ static CanType getKnownType(Optional &cacheSlot, ASTContext &C, // lookupValue would only give us types actually declared in the overlays // themselves. SmallVector decls; - mod->lookupQualified(mod, C.getIdentifier(typeName), + mod->lookupQualified(mod, DeclNameRef(C.getIdentifier(typeName)), NL_QualifiedDefault | NL_KnownNonCascadingDependency, decls); if (decls.size() != 1) @@ -172,19 +440,22 @@ Lowering::adjustFunctionType(CanAnyFunctionType t, } /// Adjust a function type to have a slightly different type. -CanSILFunctionType Lowering::adjustFunctionType( - CanSILFunctionType type, SILFunctionType::ExtInfo extInfo, - ParameterConvention callee, - Optional witnessMethodConformance) { +CanSILFunctionType +Lowering::adjustFunctionType(CanSILFunctionType type, + SILFunctionType::ExtInfo extInfo, + ParameterConvention callee, + ProtocolConformanceRef witnessMethodConformance) { if (type->getExtInfo() == extInfo && type->getCalleeConvention() == callee && - type->getWitnessMethodConformanceOrNone() == witnessMethodConformance) + type->getWitnessMethodConformanceOrInvalid() == witnessMethodConformance) return type; - return SILFunctionType::get(type->getGenericSignature(), + return SILFunctionType::get(type->getSubstGenericSignature(), extInfo, type->getCoroutineKind(), callee, type->getParameters(), type->getYields(), type->getResults(), type->getOptionalErrorResult(), + type->getSubstitutions(), + type->isGenericSignatureImplied(), type->getASTContext(), witnessMethodConformance); } @@ -206,10 +477,11 @@ CanSILFunctionType SILFunctionType::getWithExtInfo(ExtInfo newExt) { : Lowering::DefaultThickCalleeConvention) : ParameterConvention::Direct_Unowned); - return get(getGenericSignature(), newExt, getCoroutineKind(), - calleeConvention, getParameters(), getYields(), - getResults(), getOptionalErrorResult(), getASTContext(), - getWitnessMethodConformanceOrNone()); + return get(getSubstGenericSignature(), newExt, getCoroutineKind(), + calleeConvention, getParameters(), getYields(), getResults(), + getOptionalErrorResult(), getSubstitutions(), + isGenericSignatureImplied(), getASTContext(), + getWitnessMethodConformanceOrInvalid()); } namespace { @@ -291,6 +563,128 @@ class Conventions { } }; +/// A structure for building the substituted generic signature of a lowered type. +/// +/// Where the abstraction pattern for a lowered type involves substitutable types, we extract those positions +/// out into generic arguments. This signature only needs to consider the general calling convention, +/// so it can reduce away protocol and base class constraints aside from +/// `AnyObject`. We want similar-shaped generic function types to remain +/// canonically equivalent, like `(T, U) -> ()`, `(T, T) -> ()`, +/// `(U, T) -> ()` or `(T, T.A) -> ()` when given substitutions that produce +/// the same function types, so we also introduce a new generic argument for +/// each position where we see a dependent type, and canonicalize the order in +/// which we see independent generic arguments. +class SubstFunctionTypeCollector { +public: + TypeConverter &TC; + bool Enabled; + + SmallVector substGenericParams; + SmallVector substRequirements; + SmallVector substReplacements; + + SubstFunctionTypeCollector(TypeConverter &TC, bool enabled) + : TC(TC), Enabled(enabled) { + } + SubstFunctionTypeCollector(const SubstFunctionTypeCollector &) = delete; + + // Add a substitution for a fresh type variable, with the given replacement + // type and layout constraint. + CanType addSubstitution(LayoutConstraint layout, + CanType substType) { + auto paramIndex = substGenericParams.size(); + auto param = CanGenericTypeParamType::get(0, paramIndex, TC.Context); + + substGenericParams.push_back(param); + substReplacements.push_back(substType); + + // Preserve the layout constraint, if any, on the archetype in the + // generic signature, generalizing away some constraints that + // shouldn't affect ABI substitutability. + if (layout) { + switch (layout->getKind()) { + // Keep these layout constraints as is. + case LayoutConstraintKind::RefCountedObject: + case LayoutConstraintKind::TrivialOfAtMostSize: + break; + + case LayoutConstraintKind::UnknownLayout: + case LayoutConstraintKind::Trivial: + // These constraints don't really constrain the ABI, so we can + // eliminate them. + layout = LayoutConstraint(); + break; + + // Replace these specific constraints with one of the more general + // constraints above. + case LayoutConstraintKind::NativeClass: + case LayoutConstraintKind::Class: + case LayoutConstraintKind::NativeRefCountedObject: + // These can all be generalized to RefCountedObject. + layout = LayoutConstraint::getLayoutConstraint( + LayoutConstraintKind::RefCountedObject); + break; + + case LayoutConstraintKind::TrivialOfExactSize: + // Generalize to TrivialOfAtMostSize. + layout = LayoutConstraint::getLayoutConstraint( + LayoutConstraintKind::TrivialOfAtMostSize, + layout->getTrivialSizeInBits(), + layout->getAlignmentInBits(), + TC.Context); + break; + } + + if (layout) + substRequirements.push_back( + Requirement(RequirementKind::Layout, param, layout)); + } + + return param; + } + + /// Given the destructured original abstraction pattern and substituted type for a destructured + /// parameter or result, introduce substituted generic parameters and requirements as needed for + /// the lowered type, and return the substituted type in terms of the substituted generic signature. + CanType getSubstitutedInterfaceType(AbstractionPattern origType, + CanType substType) { + if (!Enabled) + return substType; + + // Replace every dependent type we see with a fresh type variable in + // the substituted signature, substituted by the corresponding concrete + // type. + + // The entire original context could be a generic parameter. + if (origType.isTypeParameter()) { + return addSubstitution(origType.getLayoutConstraint(), substType); + } + + auto origContextType = origType.getType(); + + if (!origContextType->hasTypeParameter() + && !origContextType->hasArchetype()) { + // If the abstraction pattern doesn't have substitutable positions, nor + // should the concrete type. + assert(!substType->hasTypeParameter() + && !substType->hasArchetype()); + return substType; + } + + // Extract structural substitutions. + if (origContextType->hasTypeParameter()) + origContextType = origType.getGenericSignature()->getGenericEnvironment() + ->mapTypeIntoContext(origContextType) + ->getCanonicalType(origType.getGenericSignature()); + return origContextType + ->substituteBindingsTo(substType, + [&](ArchetypeType *archetype, CanType binding) -> CanType { + return addSubstitution(archetype->getLayoutConstraint(), + binding); + }); + } +}; + /// A visitor for breaking down formal result types into a SILResultInfo /// and possibly some number of indirect-out SILParameterInfos, /// matching the abstraction patterns of the original type. @@ -298,13 +692,19 @@ class DestructureResults { TypeConverter &TC; const Conventions &Convs; SmallVectorImpl &Results; + TypeExpansionContext context; + SubstFunctionTypeCollector &Subst; + public: - DestructureResults(TypeConverter &TC, const Conventions &conventions, - SmallVectorImpl &results) - : TC(TC), Convs(conventions), Results(results) {} + DestructureResults(TypeExpansionContext context, TypeConverter &TC, + const Conventions &conventions, + SmallVectorImpl &results, + SubstFunctionTypeCollector &subst) + : TC(TC), Convs(conventions), Results(results), context(context), + Subst(subst) {} void destructure(AbstractionPattern origType, CanType substType) { - // Recurse into tuples. + // Recur into tuples. if (origType.isTuple()) { auto substTupleType = cast(substType); for (auto eltIndex : indices(substTupleType.getElementTypes())) { @@ -315,16 +715,23 @@ class DestructureResults { } return; } + + auto substInterfaceType = Subst.getSubstitutedInterfaceType(origType, + substType); + + auto &substResultTLForConvention = TC.getTypeLowering( + origType, substInterfaceType, TypeExpansionContext::minimal()); + auto &substResultTL = TC.getTypeLowering(origType, substInterfaceType, + context); - auto &substResultTL = TC.getTypeLowering(origType, substType, - ResilienceExpansion::Minimal); // Determine the result convention. ResultConvention convention; - if (isFormallyReturnedIndirectly(origType, substType, substResultTL)) { + if (isFormallyReturnedIndirectly(origType, substType, + substResultTLForConvention)) { convention = ResultConvention::Indirect; } else { - convention = Convs.getResult(substResultTL); + convention = Convs.getResult(substResultTLForConvention); // Reduce conventions for trivial types to an unowned convention. if (substResultTL.isTrivial()) { @@ -343,7 +750,7 @@ class DestructureResults { } } } - + SILResultInfo result(substResultTL.getLoweredType().getASTType(), convention); Results.push_back(result); @@ -467,17 +874,21 @@ static bool isFormallyPassedIndirectly(TypeConverter &TC, /// /// See the comment in AbstractionPattern.h for details. class DestructureInputs { + TypeExpansionContext expansion; TypeConverter &TC; const Conventions &Convs; const ForeignInfo &Foreign; Optional> HandleForeignSelf; SmallVectorImpl &Inputs; + SubstFunctionTypeCollector &Subst; unsigned NextOrigParamIndex = 0; public: - DestructureInputs(TypeConverter &TC, const Conventions &conventions, - const ForeignInfo &foreign, - SmallVectorImpl &inputs) - : TC(TC), Convs(conventions), Foreign(foreign), Inputs(inputs) {} + DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC, + const Conventions &conventions, const ForeignInfo &foreign, + SmallVectorImpl &inputs, + SubstFunctionTypeCollector &subst) + : expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign), + Inputs(inputs), Subst(subst) {} void destructure(AbstractionPattern origType, CanAnyFunctionType::CanParamArrayRef params, @@ -534,8 +945,8 @@ class DestructureInputs { auto eltPattern = origType.getFunctionParamType(i); auto flags = params[i].getParameterFlags(); - visit(flags.getValueOwnership(), /*forSelf=*/false, - eltPattern, ty, silRepresentation); + visit(flags.getValueOwnership(), /*forSelf=*/false, eltPattern, ty, + silRepresentation, flags.isNoDerivative()); } // Process the self parameter. Note that we implicitly drop self @@ -556,7 +967,8 @@ class DestructureInputs { void visit(ValueOwnership ownership, bool forSelf, AbstractionPattern origType, CanType substType, - SILFunctionTypeRepresentation rep) { + SILFunctionTypeRepresentation rep, + bool isNonDifferentiable = false) { assert(!isa(substType)); // Tuples get handled specially, in some cases: @@ -571,10 +983,8 @@ class DestructureInputs { for (auto i : indices(substTupleTy.getElementTypes())) { auto &elt = substTupleTy->getElement(i); auto ownership = elt.getParameterFlags().getValueOwnership(); - // FIXME(swift3): Once the entire parameter list is no longer a - // target for substitution, re-enable this. - // assert(ownership == ValueOwnership::Default); - // assert(!elt.isVararg()); + assert(ownership == ValueOwnership::Default); + assert(!elt.isVararg()); visit(ownership, forSelf, origType.getTupleElementType(i), CanType(elt.getRawType()), rep); @@ -587,26 +997,34 @@ class DestructureInputs { } unsigned origParamIndex = NextOrigParamIndex++; + + auto substInterfaceType = + Subst.getSubstitutedInterfaceType(origType, substType); + + auto &substTLConv = TC.getTypeLowering(origType, substInterfaceType, + TypeExpansionContext::minimal()); + auto &substTL = TC.getTypeLowering(origType, substInterfaceType, expansion); - auto &substTL = TC.getTypeLowering(origType, substType, - ResilienceExpansion::Minimal); ParameterConvention convention; if (ownership == ValueOwnership::InOut) { convention = ParameterConvention::Indirect_Inout; - } else if (isFormallyPassedIndirectly(origType, substType, substTL)) { + } else if (isFormallyPassedIndirectly(origType, substType, substTLConv)) { convention = Convs.getIndirect(ownership, forSelf, origParamIndex, - origType, substTL); + origType, substTLConv); assert(isIndirectFormalParameter(convention)); } else if (substTL.isTrivial()) { convention = ParameterConvention::Direct_Unowned; } else { convention = Convs.getDirect(ownership, forSelf, origParamIndex, origType, - substTL); + substTLConv); assert(!isIndirectFormalParameter(convention)); } - auto loweredType = substTL.getLoweredType().getASTType(); - Inputs.push_back(SILParameterInfo(loweredType, convention)); + SILParameterInfo param(substTL.getLoweredType().getASTType(), convention); + if (isNonDifferentiable) + param = param.getWithDifferentiability( + SILParameterDifferentiability::NotDifferentiable); + Inputs.push_back(param); maybeAddForeignParameters(); } @@ -625,8 +1043,8 @@ class DestructureInputs { NextOrigParamIndex != Foreign.Error->getErrorParameterIndex()) return false; - auto foreignErrorTy = - TC.getLoweredRValueType(Foreign.Error->getErrorParameterType()); + auto foreignErrorTy = TC.getLoweredRValueType( + expansion, Foreign.Error->getErrorParameterType()); // Assume the error parameter doesn't have interesting lowering. Inputs.push_back(SILParameterInfo(foreignErrorTy, @@ -715,7 +1133,7 @@ static std::pair updateResultTypeForForeignError( static void lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, CanGenericSignature genericSig, - ResilienceExpansion expansion, + TypeExpansionContext expansion, SmallVectorImpl &inputs) { // NB: The generic signature may be elided from the lowered function type @@ -787,9 +1205,14 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, break; } case CaptureKind::Box: { + // The type in the box is lowered in the minimal context. + auto minimalLoweredTy = + TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType, + TypeExpansionContext::minimal()) + .getLoweredType(); // Lvalues are captured as a box that owns the captured value. auto boxTy = TC.getInterfaceBoxTypeForCapture( - VD, loweredTy.getASTType(), + VD, minimalLoweredTy.getASTType(), /*mutable*/ true); auto convention = ParameterConvention::Direct_Guaranteed; auto param = SILParameterInfo(boxTy, convention); @@ -810,39 +1233,51 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function, } static void destructureYieldsForReadAccessor(TypeConverter &TC, + TypeExpansionContext expansion, AbstractionPattern origType, CanType valueType, - SmallVectorImpl &yields) { + SmallVectorImpl &yields, + SubstFunctionTypeCollector &subst) { // Recursively destructure tuples. if (origType.isTuple()) { auto valueTupleType = cast(valueType); for (auto i : indices(valueTupleType.getElementTypes())) { auto origEltType = origType.getTupleElementType(i); auto valueEltType = valueTupleType.getElementType(i); - destructureYieldsForReadAccessor(TC, origEltType, valueEltType, yields); + destructureYieldsForReadAccessor(TC, expansion, origEltType, valueEltType, + yields, subst); } return; } - auto &tl = TC.getTypeLowering(origType, valueType, - ResilienceExpansion::Minimal); + auto valueInterfaceType = + subst.getSubstitutedInterfaceType(origType, valueType); + + auto &tlConv = + TC.getTypeLowering(origType, valueInterfaceType, + TypeExpansionContext::minimal()); + auto &tl = + TC.getTypeLowering(origType, valueInterfaceType, expansion); auto convention = [&] { - if (isFormallyPassedIndirectly(TC, origType, valueType, tl)) + if (isFormallyPassedIndirectly(TC, origType, valueInterfaceType, tlConv)) return ParameterConvention::Indirect_In_Guaranteed; - if (tl.isTrivial()) + if (tlConv.isTrivial()) return ParameterConvention::Direct_Unowned; return ParameterConvention::Direct_Guaranteed; }(); + yields.push_back(SILYieldInfo(tl.getLoweredType().getASTType(), convention)); } static void destructureYieldsForCoroutine(TypeConverter &TC, + TypeExpansionContext expansion, Optional origConstant, Optional constant, Optional reqtSubs, SmallVectorImpl &yields, - SILCoroutineKind &coroutineKind) { + SILCoroutineKind &coroutineKind, + SubstFunctionTypeCollector &subst) { assert(coroutineKind == SILCoroutineKind::None); assert(yields.empty()); @@ -876,8 +1311,11 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, // 'modify' yields an inout of the target type. if (accessor->getAccessorKind() == AccessorKind::Modify) { - auto loweredValueTy = TC.getLoweredRValueType(origType, canValueType); - yields.push_back(SILYieldInfo(loweredValueTy, + auto valueInterfaceType = subst.getSubstitutedInterfaceType(origType, + canValueType); + auto loweredValueTy = + TC.getLoweredType(origType, valueInterfaceType, expansion); + yields.push_back(SILYieldInfo(loweredValueTy.getASTType(), ParameterConvention::Indirect_Inout)); return; } @@ -885,7 +1323,8 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, // 'read' yields a borrowed value of the target type, destructuring // tuples as necessary. assert(accessor->getAccessorKind() == AccessorKind::Read); - destructureYieldsForReadAccessor(TC, origType, canValueType, yields); + destructureYieldsForReadAccessor(TC, expansion, origType, canValueType, + yields, subst); } /// Create the appropriate SIL function type for the given formal type @@ -924,12 +1363,16 @@ static void destructureYieldsForCoroutine(TypeConverter &TC, /// /// \param conventions - conventions as expressed for the original type static CanSILFunctionType getSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, + TypeConverter &TC, TypeExpansionContext expansionContext, AbstractionPattern origType, CanAnyFunctionType substFnInterfaceType, AnyFunctionType::ExtInfo extInfo, const Conventions &conventions, const ForeignInfo &foreignInfo, Optional origConstant, Optional constant, Optional reqtSubs, - Optional witnessMethodConformance) { + ProtocolConformanceRef witnessMethodConformance) { + // Find the generic parameters. + CanGenericSignature genericSig = + substFnInterfaceType.getOptGenericSignature(); + // Per above, only fully honor opaqueness in the abstraction pattern // for thick or polymorphic functions. We don't need to worry about // non-opaque patterns because the type-checker forbids non-thick @@ -938,17 +1381,10 @@ static CanSILFunctionType getSILFunctionType( substFnInterfaceType->getExtInfo().getSILRepresentation() != SILFunctionType::Representation::Thick && isa(substFnInterfaceType)) { - origType = AbstractionPattern(TC.getCurGenericContext(), + origType = AbstractionPattern(genericSig, substFnInterfaceType); } - // Find the generic parameters. - CanGenericSignature genericSig = - substFnInterfaceType.getOptGenericSignature(); - - // Lower the interface type in a generic context. - GenericContextScope scope(TC, genericSig); - // Map 'throws' to the appropriate error convention. Optional errorResult; assert((!foreignInfo.Error || substFnInterfaceType->getExtInfo().throws()) && @@ -973,17 +1409,18 @@ static CanSILFunctionType getSILFunctionType( substFormalResultType); } - // Destructure the result tuple type. - SmallVector results; - { - DestructureResults destructurer(TC, conventions, results); - destructurer.destructure(origResultType, substFormalResultType); - } + SubstFunctionTypeCollector subst(TC, + TC.Context.LangOpts.EnableSubstSILFunctionTypesForFunctionValues + // We don't currently use substituted function types for generic function + // type lowering, though we should for generic methods on classes and + // protocols. + && !genericSig); // Destructure the input tuple type. SmallVector inputs; { - DestructureInputs destructurer(TC, conventions, foreignInfo, inputs); + DestructureInputs destructurer(expansionContext, TC, conventions, + foreignInfo, inputs, subst); destructurer.destructure(origType, substFnInterfaceType.getParams(), extInfo); @@ -992,16 +1429,24 @@ static CanSILFunctionType getSILFunctionType( // Destructure the coroutine yields. SILCoroutineKind coroutineKind = SILCoroutineKind::None; SmallVector yields; - destructureYieldsForCoroutine(TC, origConstant, constant, reqtSubs, - yields, coroutineKind); + destructureYieldsForCoroutine(TC, expansionContext, origConstant, constant, + reqtSubs, yields, coroutineKind, subst); + // Destructure the result tuple type. + SmallVector results; + { + DestructureResults destructurer(expansionContext, TC, conventions, + results, subst); + destructurer.destructure(origResultType, substFormalResultType); + } + // Lower the capture context parameters, if any. if (constant && constant->getAnyFunctionRef()) { - auto expansion = ResilienceExpansion::Maximal; + auto expansion = TypeExpansionContext::maximal( + expansionContext.getContext(), expansionContext.isWholeModuleContext()); if (constant->isSerialized()) - expansion = ResilienceExpansion::Minimal; - lowerCaptureContextParameters(TC, *constant, genericSig, expansion, - inputs); + expansion = TypeExpansionContext::minimal(); + lowerCaptureContextParameters(TC, *constant, genericSig, expansion, inputs); } auto calleeConvention = ParameterConvention::Direct_Unowned; @@ -1017,12 +1462,29 @@ static CanSILFunctionType getSILFunctionType( auto silExtInfo = SILFunctionType::ExtInfo() .withRepresentation(extInfo.getSILRepresentation()) .withIsPseudogeneric(pseudogeneric) - .withNoEscape(extInfo.isNoEscape()); + .withNoEscape(extInfo.isNoEscape()) + .withDifferentiabilityKind(extInfo.getDifferentiabilityKind()); + + // Build the substituted generic signature we extracted. + bool impliedSignature = false; + SubstitutionMap substitutions; + if (subst.Enabled) { + if (!subst.substGenericParams.empty()) { + genericSig = GenericSignature::get(subst.substGenericParams, + subst.substRequirements) + .getCanonicalSignature(); + substitutions = SubstitutionMap::get(genericSig, + subst.substReplacements, + ArrayRef()); + impliedSignature = true; + } + } return SILFunctionType::get(genericSig, silExtInfo, coroutineKind, calleeConvention, inputs, yields, - results, errorResult, TC.Context, - witnessMethodConformance); + results, errorResult, + substitutions, impliedSignature, + TC.Context, witnessMethodConformance); } //===----------------------------------------------------------------------===// @@ -1240,12 +1702,11 @@ getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, Optional constant); static CanSILFunctionType getNativeSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, + TypeConverter &TC, TypeExpansionContext context, AbstractionPattern origType, CanAnyFunctionType substInterfaceType, AnyFunctionType::ExtInfo extInfo, - Optional origConstant, - Optional constant, + Optional origConstant, Optional constant, Optional reqtSubs, - Optional witnessMethodConformance) { + ProtocolConformanceRef witnessMethodConformance) { assert(bool(origConstant) == bool(constant)); switch (extInfo.getSILRepresentation()) { case SILFunctionType::Representation::Block: @@ -1263,23 +1724,23 @@ static CanSILFunctionType getNativeSILFunctionType( switch (constant ? constant->kind : SILDeclRef::Kind::Func) { case SILDeclRef::Kind::Initializer: case SILDeclRef::Kind::EnumElement: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultInitializerConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultInitializerConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); case SILDeclRef::Kind::Allocator: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultAllocatorConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultAllocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); case SILDeclRef::Kind::Func: // If we have a setter, use the special setter convention. This ensures // that we take normal parameters at +1. if (constant && constant->isSetter()) { - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DefaultSetterConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, - witnessMethodConformance); + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DefaultSetterConventions(), + ForeignInfo(), origConstant, constant, + reqtSubs, witnessMethodConformance); } LLVM_FALLTHROUGH; case SILDeclRef::Kind::Destroyer: @@ -1290,14 +1751,14 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: { auto conv = DefaultConventions(NormalParameterConvention::Guaranteed); - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, conv, - ForeignInfo(), origConstant, constant, reqtSubs, - witnessMethodConformance); + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, conv, ForeignInfo(), origConstant, + constant, reqtSubs, witnessMethodConformance); } case SILDeclRef::Kind::Deallocator: - return getSILFunctionType(TC, origType, substInterfaceType, extInfo, - DeallocatorConventions(), ForeignInfo(), - origConstant, constant, reqtSubs, + return getSILFunctionType(TC, context, origType, substInterfaceType, + extInfo, DeallocatorConventions(), + ForeignInfo(), origConstant, constant, reqtSubs, witnessMethodConformance); } } @@ -1307,11 +1768,11 @@ static CanSILFunctionType getNativeSILFunctionType( } CanSILFunctionType swift::getNativeSILFunctionType( - TypeConverter &TC, AbstractionPattern origType, CanAnyFunctionType substType, - Optional origConstant, - Optional substConstant, + TypeConverter &TC, TypeExpansionContext context, + AbstractionPattern origType, CanAnyFunctionType substType, + Optional origConstant, Optional substConstant, Optional reqtSubs, - Optional witnessMethodConformance) { + ProtocolConformanceRef witnessMethodConformance) { AnyFunctionType::ExtInfo extInfo; // Preserve type information from the original type if possible. @@ -1323,7 +1784,7 @@ CanSILFunctionType swift::getNativeSILFunctionType( extInfo = substType->getExtInfo(); } - return ::getNativeSILFunctionType(TC, origType, substType, extInfo, + return ::getNativeSILFunctionType(TC, context, origType, substType, extInfo, origConstant, substConstant, reqtSubs, witnessMethodConformance); } @@ -1670,20 +2131,21 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, if (auto method = dyn_cast(clangDecl)) { auto origPattern = AbstractionPattern::getObjCMethod(origType, method, foreignInfo.Error); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, ObjCMethodConventions(method), foreignInfo, constant, constant, None, - /*witnessMethodConformance=*/None); + ProtocolConformanceRef()); } if (auto method = dyn_cast(clangDecl)) { AbstractionPattern origPattern = AbstractionPattern::getCXXMethod(origType, method); auto conventions = CXXMethodConventions(method); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, - conventions, foreignInfo, constant, constant, - None, - /*witnessMethodConformance=*/None); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, conventions, + foreignInfo, constant, constant, None, + ProtocolConformanceRef()); } if (auto func = dyn_cast(clangDecl)) { @@ -1693,10 +2155,10 @@ getSILFunctionTypeForClangDecl(TypeConverter &TC, const clang::Decl *clangDecl, ? AbstractionPattern::getCFunctionAsMethod(origType, clangType, foreignInfo.Self) : AbstractionPattern(origType, clangType); - return getSILFunctionType(TC, origPattern, substInterfaceType, extInfo, - CFunctionConventions(func), foreignInfo, - constant, constant, None, - /*witnessMethodConformance=*/None); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern, + substInterfaceType, extInfo, + CFunctionConventions(func), foreignInfo, constant, + constant, None, ProtocolConformanceRef()); } llvm_unreachable("call to unknown kind of C function"); @@ -1723,18 +2185,18 @@ getSILFunctionTypeForAbstractCFunction(TypeConverter &TC, llvm_unreachable("unexpected type imported as a function type"); } if (fnType) { - return getSILFunctionType(TC, origType, substType, extInfo, - CFunctionTypeConventions(fnType), ForeignInfo(), - constant, constant, None, - /*witnessMethodConformance=*/None); + return getSILFunctionType( + TC, TypeExpansionContext::minimal(), origType, substType, extInfo, + CFunctionTypeConventions(fnType), ForeignInfo(), constant, constant, + None, ProtocolConformanceRef()); } } // TODO: Ought to support captures in block funcs. - return getSILFunctionType(TC, origType, substType, extInfo, - DefaultBlockConventions(), ForeignInfo(), - constant, constant, None, - /*witnessMethodConformance=*/None); + return getSILFunctionType(TC, TypeExpansionContext::minimal(), origType, + substType, extInfo, DefaultBlockConventions(), + ForeignInfo(), constant, constant, None, + ProtocolConformanceRef()); } /// Try to find a clang method declaration for the given function. @@ -1898,11 +2360,11 @@ getSILFunctionTypeForObjCSelectorFamily(TypeConverter &TC, ObjCSelectorFamily fa AnyFunctionType::ExtInfo extInfo, const ForeignInfo &foreignInfo, Optional constant) { - return getSILFunctionType(TC, AbstractionPattern(origType), substInterfaceType, - extInfo, ObjCSelectorFamilyConventions(family), - foreignInfo, constant, constant, - /*requirement subs*/None, - /*witnessMethodConformance=*/None); + return getSILFunctionType( + TC, TypeExpansionContext::minimal(), AbstractionPattern(origType), + substInterfaceType, extInfo, ObjCSelectorFamilyConventions(family), + foreignInfo, constant, constant, + /*requirement subs*/ None, ProtocolConformanceRef()); } static bool isImporterGeneratedAccessor(const clang::Decl *clangDecl, @@ -1923,10 +2385,9 @@ static bool isImporterGeneratedAccessor(const clang::Decl *clangDecl, return true; } -static CanSILFunctionType -getUncachedSILFunctionTypeForConstant(TypeConverter &TC, - SILDeclRef constant, - CanAnyFunctionType origLoweredInterfaceType) { +static CanSILFunctionType getUncachedSILFunctionTypeForConstant( + TypeConverter &TC, TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origLoweredInterfaceType) { assert(origLoweredInterfaceType->getExtInfo().getSILRepresentation() != SILFunctionTypeRepresentation::Thick && origLoweredInterfaceType->getExtInfo().getSILRepresentation() @@ -1935,7 +2396,7 @@ getUncachedSILFunctionTypeForConstant(TypeConverter &TC, auto extInfo = origLoweredInterfaceType->getExtInfo(); if (!constant.isForeign) { - Optional witnessMethodConformance; + ProtocolConformanceRef witnessMethodConformance; if (extInfo.getSILRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { @@ -1944,9 +2405,9 @@ getUncachedSILFunctionTypeForConstant(TypeConverter &TC, } return ::getNativeSILFunctionType( - TC, AbstractionPattern(origLoweredInterfaceType), - origLoweredInterfaceType, extInfo, - constant, constant, None, witnessMethodConformance); + TC, context, AbstractionPattern(origLoweredInterfaceType), + origLoweredInterfaceType, extInfo, constant, constant, None, + witnessMethodConformance); } ForeignInfo foreignInfo; @@ -1986,12 +2447,12 @@ getUncachedSILFunctionTypeForConstant(TypeConverter &TC, extInfo, foreignInfo, constant); } -CanSILFunctionType TypeConverter:: -getUncachedSILFunctionTypeForConstant(SILDeclRef constant, - CanAnyFunctionType origInterfaceType) { +CanSILFunctionType TypeConverter::getUncachedSILFunctionTypeForConstant( + TypeExpansionContext context, SILDeclRef constant, + CanAnyFunctionType origInterfaceType) { auto origLoweredInterfaceType = getLoweredFormalTypes(constant, origInterfaceType).Uncurried; - return ::getUncachedSILFunctionTypeForConstant(*this, constant, + return ::getUncachedSILFunctionTypeForConstant(*this, context, constant, origLoweredInterfaceType); } @@ -2071,9 +2532,11 @@ static llvm::cl::opt DisableConstantInfoCache("sil-disable-typelowering-constantinfo-cache", llvm::cl::init(false)); -const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { +const SILConstantInfo & +TypeConverter::getConstantInfo(TypeExpansionContext expansion, + SILDeclRef constant) { if (!DisableConstantInfoCache) { - auto found = ConstantTypes.find(constant); + auto found = ConstantTypes.find(std::make_pair(expansion, constant)); if (found != ConstantTypes.end()) return *found->second; } @@ -2094,7 +2557,7 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { // The SIL type encodes conventions according to the original type. CanSILFunctionType silFnType = - ::getUncachedSILFunctionTypeForConstant(*this, constant, + ::getUncachedSILFunctionTypeForConstant(*this, expansion, constant, loweredInterfaceType); LLVM_DEBUG(llvm::dbgs() << "lowering type for constant "; @@ -2117,7 +2580,8 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { if (DisableConstantInfoCache) return *result; - auto inserted = ConstantTypes.insert({constant, result}); + auto inserted = + ConstantTypes.insert({std::make_pair(expansion, constant), result}); assert(inserted.second); (void)inserted; return *result; @@ -2125,8 +2589,10 @@ const SILConstantInfo &TypeConverter::getConstantInfo(SILDeclRef constant) { /// Returns the SILParameterInfo for the given declaration's `self` parameter. /// `constant` must refer to a method. -SILParameterInfo TypeConverter::getConstantSelfParameter(SILDeclRef constant) { - auto ty = getConstantFunctionType(constant); +SILParameterInfo +TypeConverter::getConstantSelfParameter(TypeExpansionContext context, + SILDeclRef constant) { + auto ty = getConstantFunctionType(context, constant); // In most cases the "self" parameter is lowered as the back parameter. // The exception is C functions imported as methods. @@ -2230,10 +2696,11 @@ static CanType copyOptionalityFromDerivedToBase(TypeConverter &tc, /// Returns the ConstantInfo corresponding to the VTable thunk for overriding. /// Will be the same as getConstantInfo if the declaration does not override. const SILConstantInfo & -TypeConverter::getConstantOverrideInfo(SILDeclRef derived, SILDeclRef base) { +TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, + SILDeclRef derived, SILDeclRef base) { // Foreign overrides currently don't need reabstraction. if (derived.isForeign) - return getConstantInfo(derived); + return getConstantInfo(context, derived); auto found = ConstantOverrideTypes.find({derived, base}); if (found != ConstantOverrideTypes.end()) @@ -2241,64 +2708,77 @@ TypeConverter::getConstantOverrideInfo(SILDeclRef derived, SILDeclRef base) { assert(base.requiresNewVTableEntry() && "base must not be an override"); - auto baseInfo = getConstantInfo(base); - auto derivedInfo = getConstantInfo(derived); + // Figure out the generic signature for the class method call. This is the + // signature of the derived class, with requirements transplanted from + // the base method. The derived method is allowed to have fewer + // requirements, in which case the thunk will translate the calling + // convention appropriately before calling the derived method. + bool hasGenericRequirementDifference = false; - // If the derived method is ABI-compatible with the base method, give the - // vtable thunk the same signature as the derived method. - auto basePattern = AbstractionPattern(baseInfo.LoweredType); + auto derivedSig = derived.getDecl()->getAsGenericContext() + ->getGenericSignature(); + auto genericSig = Context.getOverrideGenericSignature(base.getDecl(), + derived.getDecl()); + if (genericSig) { + hasGenericRequirementDifference = + !genericSig->requirementsNotSatisfiedBy(derivedSig).empty(); + } - auto baseInterfaceTy = baseInfo.FormalType; - auto derivedInterfaceTy = derivedInfo.FormalType; + auto baseInfo = getConstantInfo(context, base); + auto derivedInfo = getConstantInfo(context, derived); - auto params = derivedInterfaceTy.getParams(); + auto params = derivedInfo.FormalType.getParams(); assert(params.size() == 1); auto selfInterfaceTy = params[0].getPlainType()->getMetatypeInstanceType(); auto overrideInterfaceTy = + cast( selfInterfaceTy->adjustSuperclassMemberDeclType( - base.getDecl(), derived.getDecl(), baseInterfaceTy); - - // Copy generic signature from derived to the override type, to handle - // the case where the base member is not generic (because the base class - // is concrete) but the derived member is generic (because the derived - // class is generic). - if (auto derivedInterfaceFnTy = derivedInterfaceTy->getAs()) { - auto overrideInterfaceFnTy = overrideInterfaceTy->castTo(); - overrideInterfaceTy = - GenericFunctionType::get(derivedInterfaceFnTy->getGenericSignature(), - overrideInterfaceFnTy->getParams(), - overrideInterfaceFnTy->getResult(), - overrideInterfaceFnTy->getExtInfo()); - } + base.getDecl(), derived.getDecl(), baseInfo.FormalType) + ->getCanonicalType()); - // Lower the formal AST type. - auto bridgedTypes = getLoweredFormalTypes(derived, - cast(overrideInterfaceTy->getCanonicalType())); - auto overrideLoweredInterfaceTy = bridgedTypes.Uncurried; + // Build the formal AST function type for the class method call. + auto basePattern = AbstractionPattern(baseInfo.LoweredType); + + if (!hasGenericRequirementDifference && + !checkASTTypeForABIDifferences(derivedInfo.FormalType, + overrideInterfaceTy)) { - if (!checkASTTypeForABIDifferences(derivedInfo.LoweredType, - overrideLoweredInterfaceTy)) { + // The derived method is ABI-compatible with the base method. Let's + // just use the derived method's formal type. basePattern = AbstractionPattern( copyOptionalityFromDerivedToBase( *this, derivedInfo.LoweredType, baseInfo.LoweredType)); - overrideLoweredInterfaceTy = derivedInfo.LoweredType; + overrideInterfaceTy = derivedInfo.FormalType; + } + + if (genericSig && !genericSig->areAllParamsConcrete()) { + overrideInterfaceTy = + cast( + GenericFunctionType::get(genericSig, + overrideInterfaceTy->getParams(), + overrideInterfaceTy->getResult(), + overrideInterfaceTy->getExtInfo()) + ->getCanonicalType()); } - // Build the SILFunctionType for the vtable thunk. + // Build the lowered AST function type for the class method call. + auto bridgedTypes = getLoweredFormalTypes(derived, overrideInterfaceTy); + + // Build the SILFunctionType for the class method call. CanSILFunctionType fnTy = getNativeSILFunctionType( - *this, basePattern, overrideLoweredInterfaceTy, base, derived, - /*reqt subs*/None, /*witnessMethodConformance=*/None); + *this, context, basePattern, bridgedTypes.Uncurried, base, derived, + /*reqt subs*/ None, ProtocolConformanceRef()); // Build the SILConstantInfo and cache it. auto resultBuf = Context.Allocate(sizeof(SILConstantInfo), alignof(SILConstantInfo)); auto result = ::new (resultBuf) SILConstantInfo{ - derivedInterfaceTy, - bridgedTypes.Pattern, - overrideLoweredInterfaceTy, + overrideInterfaceTy, + basePattern, + bridgedTypes.Uncurried, fnTy}; auto inserted = ConstantOverrideTypes.insert({{derived, base}, result}); @@ -2322,10 +2802,13 @@ class SILTypeSubstituter : // context signature. CanGenericSignature Sig; + TypeExpansionContext typeExpansionContext; + bool shouldSubstituteOpaqueArchetypes; public: SILTypeSubstituter(TypeConverter &TC, + TypeExpansionContext context, TypeSubstitutionFn Subst, LookupConformanceFn Conformances, CanGenericSignature Sig, @@ -2334,6 +2817,7 @@ class SILTypeSubstituter : Subst(Subst), Conformances(Conformances), Sig(Sig), + typeExpansionContext(context), shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) {} @@ -2342,41 +2826,52 @@ class SILTypeSubstituter : // When a function appears inside of another type, we only perform // substitutions if it does not have a generic signature. CanSILFunctionType visitSILFunctionType(CanSILFunctionType origType) { - if (origType->getGenericSignature()) + if (origType->getSubstGenericSignature()) { + if (auto subs = origType->getSubstitutions()) { + // Substitute the substitutions. + auto newSubs = subs.subst(Subst, Conformances); + return origType->withSubstitutions(newSubs); + } + return origType; + } return substSILFunctionType(origType); } // Entry point for use by SILType::substGenericArgs(). CanSILFunctionType substSILFunctionType(CanSILFunctionType origType) { + // TODO: Maybe this can be retired once substituted function types are + // used pervasively. + assert(!origType->getSubstitutions()); + SmallVector substResults; substResults.reserve(origType->getNumResults()); for (auto origResult : origType->getResults()) { - substResults.push_back(subst(origResult)); + substResults.push_back(substInterface(origResult)); } auto substErrorResult = origType->getOptionalErrorResult(); assert(!substErrorResult || - (!substErrorResult->getType()->hasTypeParameter() && - !substErrorResult->getType()->hasArchetype())); + (!substErrorResult->getInterfaceType()->hasTypeParameter() && + !substErrorResult->getInterfaceType()->hasArchetype())); SmallVector substParams; substParams.reserve(origType->getParameters().size()); for (auto &origParam : origType->getParameters()) { - substParams.push_back(subst(origParam)); + substParams.push_back(substInterface(origParam)); } SmallVector substYields; substYields.reserve(origType->getYields().size()); for (auto &origYield : origType->getYields()) { - substYields.push_back(subst(origYield)); + substYields.push_back(substInterface(origYield)); } - Optional witnessMethodConformance; - if (auto conformance = origType->getWitnessMethodConformanceOrNone()) { + ProtocolConformanceRef witnessMethodConformance; + if (auto conformance = origType->getWitnessMethodConformanceOrInvalid()) { assert(origType->getExtInfo().hasSelfParam()); - auto selfType = origType->getSelfParameter().getType(); + auto selfType = origType->getSelfParameter().getInterfaceType(); // The Self type can be nested in a few layers of metatypes (etc.). while (auto metatypeType = dyn_cast(selfType)) { auto next = metatypeType.getInstanceType(); @@ -2385,17 +2880,36 @@ class SILTypeSubstituter : selfType = next; } witnessMethodConformance = - conformance->subst(selfType, Subst, Conformances); + conformance.subst(selfType, Subst, Conformances); + + // Substitute the underlying conformance of opaque type archetypes if we + // should look through opaque archetypes. + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + SubstOptions substOptions(None); + auto substType = selfType.subst(Subst, Conformances, substOptions) + ->getCanonicalType(); + if (substType->hasOpaqueArchetype()) { + witnessMethodConformance = substOpaqueTypesWithUnderlyingTypes( + witnessMethodConformance, substType, typeExpansionContext); + } + } } - + // The substituted type is no longer generic, so it'd never be // pseudogeneric. - auto extInfo = origType->getExtInfo().withIsPseudogeneric(false); + auto extInfo = origType->getExtInfo(); + if (!shouldSubstituteOpaqueArchetypes) + extInfo = extInfo.withIsPseudogeneric(false); - return SILFunctionType::get(nullptr, extInfo, + auto genericSig = shouldSubstituteOpaqueArchetypes + ? origType->getSubstGenericSignature() + : nullptr; + + return SILFunctionType::get(genericSig, extInfo, origType->getCoroutineKind(), origType->getCalleeConvention(), substParams, substYields, substResults, substErrorResult, + SubstitutionMap(), false, TC.Context, witnessMethodConformance); } @@ -2404,16 +2918,17 @@ class SILTypeSubstituter : type.getCategory()); } - SILResultInfo subst(SILResultInfo orig) { - return SILResultInfo(visit(orig.getType()), orig.getConvention()); + SILResultInfo substInterface(SILResultInfo orig) { + return SILResultInfo(visit(orig.getInterfaceType()), orig.getConvention()); } - SILYieldInfo subst(SILYieldInfo orig) { - return SILYieldInfo(visit(orig.getType()), orig.getConvention()); + SILYieldInfo substInterface(SILYieldInfo orig) { + return SILYieldInfo(visit(orig.getInterfaceType()), orig.getConvention()); } - SILParameterInfo subst(SILParameterInfo orig) { - return SILParameterInfo(visit(orig.getType()), orig.getConvention()); + SILParameterInfo substInterface(SILParameterInfo orig) { + return SILParameterInfo(visit(orig.getInterfaceType()), + orig.getConvention(), orig.getDifferentiability()); } /// Tuples need to have their component types substituted by these @@ -2470,7 +2985,14 @@ class SILTypeSubstituter : } AbstractionPattern abstraction(Sig, origType); - return TC.getLoweredRValueType(abstraction, substType); + // If we looked through an opaque archetype to a function type we need to + // use the function type's abstraction. + if (isa(origType) && + isa(substType)) + abstraction = AbstractionPattern(Sig, substType); + + return TC.getLoweredRValueType(typeExpansionContext, abstraction, + substType); } }; @@ -2485,10 +3007,9 @@ SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, !getASTType()->hasOpaqueArchetype())) return *this; - if (!genericSig) - genericSig = tc.getCurGenericContext(); - SILTypeSubstituter STST(tc, subs, conformances, - genericSig, shouldSubstituteOpaqueArchetypes); + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, + conformances, genericSig, + shouldSubstituteOpaqueArchetypes); return STST.subst(*this); } @@ -2500,9 +3021,11 @@ SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, shouldSubstituteOpaqueArchetypes); } -SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const{ +SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { + auto sig = subs.getGenericSignature(); return subst(tc, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs)); + LookUpConformanceInSubstitutionMap(subs), + sig.getCanonicalSignature()); } SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ return subst(M.Types, subs); @@ -2512,8 +3035,8 @@ SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ /// it has the form of the normal SILFunctionType for the substituted /// type, except using the original conventions. CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, - SubstitutionMap subs) { +SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, + TypeExpansionContext context) { if (!isPolymorphic()) { return CanSILFunctionType(this); } @@ -2524,20 +3047,42 @@ SILFunctionType::substGenericArgs(SILModule &silModule, return substGenericArgs(silModule, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs)); + LookUpConformanceInSubstitutionMap(subs), + context); } CanSILFunctionType SILFunctionType::substGenericArgs(SILModule &silModule, TypeSubstitutionFn subs, - LookupConformanceFn conformances) { + LookupConformanceFn conformances, + TypeExpansionContext context) { if (!isPolymorphic()) return CanSILFunctionType(this); - SILTypeSubstituter substituter(silModule.Types, subs, conformances, - getGenericSignature(), + SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, + getSubstGenericSignature(), /*shouldSubstituteOpaqueTypes*/ false); return substituter.substSILFunctionType(CanSILFunctionType(this)); } +CanSILFunctionType +SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, + TypeExpansionContext context) { + if (!hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return CanSILFunctionType(this); + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + + SILTypeSubstituter substituter(TC, context, replacer, replacer, + getSubstGenericSignature(), + /*shouldSubstituteOpaqueTypes*/ true); + auto resTy = + substituter.substSILFunctionType(CanSILFunctionType(this)); + + return resTy; +} + /// Fast path for bridging types in a function type without uncurrying. CanAnyFunctionType TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, @@ -2804,9 +3349,13 @@ static bool areABICompatibleParamsOrReturns(SILType a, SILType b, // Opaque types are compatible with their substitution. if (inFunction) { auto opaqueTypesSubsituted = aa; + auto *dc = inFunction->getDeclContext(); + auto *currentModule = inFunction->getModule().getSwiftModule(); + if (!dc || !dc->isChildContextOf(currentModule)) + dc = currentModule; ReplaceOpaqueTypesWithUnderlyingTypes replacer( - inFunction->getModule().getSwiftModule(), - inFunction->getResilienceExpansion()); + dc, inFunction->getResilienceExpansion(), + inFunction->getModule().isWholeModule()); if (aa.getASTType()->hasOpaqueArchetype()) opaqueTypesSubsituted = aa.subst(inFunction->getModule(), replacer, replacer, CanGenericSignature(), true); @@ -2867,7 +3416,7 @@ static bool areABICompatibleParamsOrReturns(SILType a, SILType b, // *NOTE* We swallow the specific error here for now. We will still get // that the function types are incompatible though, just not more // specific information. - return aFunc->isABICompatibleWith(bFunc, inFunction).isCompatible(); + return aFunc->isABICompatibleWith(bFunc, *inFunction).isCompatible(); } } @@ -2893,7 +3442,7 @@ using ABICompatibilityCheckResult = ABICompatibilityCheckResult SILFunctionType::isABICompatibleWith(CanSILFunctionType other, - SILFunction *context) const { + SILFunction &context) const { // The calling convention and function representation can't be changed. if (getRepresentation() != other->getRepresentation()) return ABICompatibilityCheckResult::DifferentFunctionRepresentations; @@ -2909,9 +3458,10 @@ SILFunctionType::isABICompatibleWith(CanSILFunctionType other, if (result1.getConvention() != result2.getConvention()) return ABICompatibilityCheckResult::DifferentReturnValueConventions; - if (!areABICompatibleParamsOrReturns(result1.getSILStorageType(), - result2.getSILStorageType(), - context)) { + if (!areABICompatibleParamsOrReturns( + result1.getSILStorageType(context.getModule(), this), + result2.getSILStorageType(context.getModule(), other), + &context)) { return ABICompatibilityCheckResult::ABIIncompatibleReturnValues; } } @@ -2925,8 +3475,10 @@ SILFunctionType::isABICompatibleWith(CanSILFunctionType other, if (error1.getConvention() != error2.getConvention()) return ABICompatibilityCheckResult::DifferentErrorResultConventions; - if (!areABICompatibleParamsOrReturns(error1.getSILStorageType(), - error2.getSILStorageType(), context)) + if (!areABICompatibleParamsOrReturns( + error1.getSILStorageType(context.getModule(), this), + error2.getSILStorageType(context.getModule(), other), + &context)) return ABICompatibilityCheckResult::ABIIncompatibleErrorResults; } @@ -2942,8 +3494,10 @@ SILFunctionType::isABICompatibleWith(CanSILFunctionType other, if (param1.getConvention() != param2.getConvention()) return {ABICompatibilityCheckResult::DifferingParameterConvention, i}; - if (!areABICompatibleParamsOrReturns(param1.getSILStorageType(), - param2.getSILStorageType(), context)) + if (!areABICompatibleParamsOrReturns( + param1.getSILStorageType(context.getModule(), this), + param2.getSILStorageType(context.getModule(), other), + &context)) return {ABICompatibilityCheckResult::ABIIncompatibleParameterType, i}; } @@ -2986,3 +3540,26 @@ StringRef SILFunctionType::ABICompatibilityCheckResult::getMessage() const { } llvm_unreachable("Covered switch isn't completely covered?!"); } + +static DeclContext *getDeclContextForExpansion(const SILFunction &f) { + auto *dc = f.getDeclContext(); + if (!dc) + dc = f.getModule().getSwiftModule(); + auto *currentModule = f.getModule().getSwiftModule(); + if (!dc || !dc->isChildContextOf(currentModule)) + dc = currentModule; + return dc; +} + +TypeExpansionContext::TypeExpansionContext(const SILFunction &f) + : expansion(f.getResilienceExpansion()), + inContext(getDeclContextForExpansion(f)), + isContextWholeModule(f.getModule().isWholeModule()) {} + +CanSILFunctionType SILFunction::getLoweredFunctionTypeInContext( + TypeExpansionContext context) const { + auto origFunTy = getLoweredFunctionType(); + auto &M = getModule(); + auto funTy = M.Types.getLoweredType(origFunTy , context); + return cast(funTy.getASTType()); +} diff --git a/lib/SIL/SILGlobalVariable.cpp b/lib/SIL/SILGlobalVariable.cpp index 48d66dd76e3de..733ad528dcd52 100644 --- a/lib/SIL/SILGlobalVariable.cpp +++ b/lib/SIL/SILGlobalVariable.cpp @@ -310,3 +310,14 @@ swift::getVariableOfStaticInitializer(SILFunction *InitFunc, return nullptr; return GVar; } + +SILType +SILGlobalVariable::getLoweredTypeInContext(TypeExpansionContext context) const { + auto ty = getLoweredType(); + if (!ty.getASTType()->hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return ty; + auto resultTy = + getModule().Types.getTypeLowering(ty, context).getLoweredType(); + return resultTy.getCategoryType(ty.getCategory()); +} diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp index d81393f5a2cfb..b1e14d28d4521 100644 --- a/lib/SIL/SILInstruction.cpp +++ b/lib/SIL/SILInstruction.cpp @@ -737,7 +737,9 @@ namespace { #define LOADABLE_REF_STORAGE_HELPER(Name) \ bool visit##Name##ToRefInst(Name##ToRefInst *RHS) { return true; } \ bool visitRefTo##Name##Inst(RefTo##Name##Inst *RHS) { return true; } \ - bool visitCopy##Name##ValueInst(Copy##Name##ValueInst *RHS) { return true; } + bool visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *RHS) { \ + return true; \ + } #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ LOADABLE_REF_STORAGE_HELPER(Name) \ bool visitStrongRetain##Name##Inst(const StrongRetain##Name##Inst *RHS) { \ @@ -1170,9 +1172,9 @@ SILInstruction *SILInstruction::clone(SILInstruction *InsertPt) { /// additional handling. It is important to know this information when /// you perform such optimizations like e.g. jump-threading. bool SILInstruction::isTriviallyDuplicatable() const { - if (isa(this) || isa(this)) { + if (isAllocatingStack()) return false; - } + if (auto *ARI = dyn_cast(this)) { if (ARI->canAllocOnStack()) return false; @@ -1211,9 +1213,6 @@ bool SILInstruction::isTriviallyDuplicatable() const { if (isa(this)) return false; - if (auto *PA = dyn_cast(this)) - return !PA->isOnStack(); - // If you add more cases here, you should also update SILLoop:canDuplicate. return true; diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8dbfdeb75ca92..27439f486b245 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -180,7 +180,7 @@ AllocStackInst::create(SILDebugLocation Loc, hasDynamicLifetime); } -VarDecl *AllocStackInst::getDecl() const { +VarDecl *AllocationInst::getDecl() const { return getLoc().getAsASTNode(); } @@ -285,11 +285,9 @@ AllocBoxInst *AllocBoxInst::create(SILDebugLocation Loc, } SILType AllocBoxInst::getAddressType() const { - return getSILBoxFieldType(getBoxType(), getModule().Types, 0).getAddressType(); -} - -VarDecl *AllocBoxInst::getDecl() const { - return getLoc().getAsASTNode(); + return getSILBoxFieldType(TypeExpansionContext(*this->getFunction()), + getBoxType(), getModule().Types, 0) + .getAddressType(); } DebugValueInst::DebugValueInst(SILDebugLocation DebugLoc, SILValue Operand, @@ -416,8 +414,8 @@ ApplyInst::create(SILDebugLocation Loc, SILValue Callee, SubstitutionMap Subs, Optional ModuleConventions, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo) { - SILType SubstCalleeSILTy = - Callee->getType().substGenericArgs(F.getModule(), Subs); + SILType SubstCalleeSILTy = Callee->getType().substGenericArgs( + F.getModule(), Subs, F.getTypeExpansionContext()); auto SubstCalleeTy = SubstCalleeSILTy.getAs(); SILFunctionConventions Conv(SubstCalleeTy, ModuleConventions.hasValue() @@ -462,8 +460,8 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo) { - SILType substCalleeSILType = - callee->getType().substGenericArgs(F.getModule(), subs); + SILType substCalleeSILType = callee->getType().substGenericArgs( + F.getModule(), subs, F.getTypeExpansionContext()); auto substCalleeType = substCalleeSILType.castTo(); SILFunctionConventions conv(substCalleeType, @@ -483,7 +481,7 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, } resultTypes.push_back(SILType::getSILTokenType(F.getASTContext())); - resultOwnerships.push_back(ValueOwnershipKind::Any); + resultOwnerships.push_back(ValueOwnershipKind::None); SmallVector typeDependentOperands; collectTypeDependentOperands(typeDependentOperands, openedArchetypes, F, @@ -499,6 +497,35 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, isNonThrowing, specializationInfo); } +void BeginApplyInst::getCoroutineEndPoints( + SmallVectorImpl &endApplyInsts, + SmallVectorImpl &abortApplyInsts) const { + for (auto *tokenUse : getTokenResult()->getUses()) { + auto *user = tokenUse->getUser(); + if (auto *end = dyn_cast(user)) { + endApplyInsts.push_back(end); + continue; + } + + abortApplyInsts.push_back(cast(user)); + } +} + +void BeginApplyInst::getCoroutineEndPoints( + SmallVectorImpl &endApplyInsts, + SmallVectorImpl &abortApplyInsts) const { + for (auto *tokenUse : getTokenResult()->getUses()) { + auto *user = tokenUse->getUser(); + if (isa(user)) { + endApplyInsts.push_back(tokenUse); + continue; + } + + assert(isa(user)); + abortApplyInsts.push_back(tokenUse); + } +} + bool swift::doesApplyCalleeHaveSemantics(SILValue callee, StringRef semantics) { if (auto *FRI = dyn_cast(callee)) if (auto *F = FRI->getReferencedFunctionOrNull()) @@ -524,10 +551,11 @@ PartialApplyInst *PartialApplyInst::create( SILOpenedArchetypesState &OpenedArchetypes, const GenericSpecializationInformation *SpecializationInfo, OnStackKind onStack) { - SILType SubstCalleeTy = - Callee->getType().substGenericArgs(F.getModule(), Subs); + SILType SubstCalleeTy = Callee->getType().substGenericArgs( + F.getModule(), Subs, F.getTypeExpansionContext()); SILType ClosureType = SILBuilder::getPartialApplyResultType( - SubstCalleeTy, Args.size(), F.getModule(), {}, CalleeConvention, onStack); + F.getTypeExpansionContext(), SubstCalleeTy, Args.size(), F.getModule(), {}, + CalleeConvention, onStack); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, @@ -545,7 +573,7 @@ TryApplyInstBase::TryApplyInstBase(SILInstructionKind kind, SILDebugLocation loc, SILBasicBlock *normalBB, SILBasicBlock *errorBB) - : TermInst(kind, loc), DestBBs{{this, normalBB}, {this, errorBB}} {} + : TermInst(kind, loc), DestBBs{{{this, normalBB}, {this, errorBB}}} {} TryApplyInst::TryApplyInst( SILDebugLocation Loc, SILValue callee, SILType substCalleeTy, @@ -562,8 +590,8 @@ TryApplyInst *TryApplyInst::create( ArrayRef args, SILBasicBlock *normalBB, SILBasicBlock *errorBB, SILFunction &F, SILOpenedArchetypesState &openedArchetypes, const GenericSpecializationInformation *specializationInfo) { - SILType substCalleeTy = - callee->getType().substGenericArgs(F.getModule(), subs); + SILType substCalleeTy = callee->getType().substGenericArgs( + F.getModule(), subs, F.getTypeExpansionContext()); SmallVector typeDependentOperands; collectTypeDependentOperands(typeDependentOperands, openedArchetypes, F, @@ -579,8 +607,9 @@ TryApplyInst *TryApplyInst::create( FunctionRefBaseInst::FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, - SILFunction *F) - : LiteralInst(Kind, DebugLoc, F->getLoweredType()), f(F) { + SILFunction *F, + TypeExpansionContext context) + : LiteralInst(Kind, DebugLoc, F->getLoweredTypeInContext(context)), f(F) { F->incrementRefCount(); } @@ -595,21 +624,25 @@ FunctionRefBaseInst::~FunctionRefBaseInst() { getInitiallyReferencedFunction()->decrementRefCount(); } -FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F) - : FunctionRefBaseInst(SILInstructionKind::FunctionRefInst, Loc, F) { +FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F, + TypeExpansionContext context) + : FunctionRefBaseInst(SILInstructionKind::FunctionRefInst, Loc, F, + context) { assert(!F->isDynamicallyReplaceable()); } DynamicFunctionRefInst::DynamicFunctionRefInst(SILDebugLocation Loc, - SILFunction *F) - : FunctionRefBaseInst(SILInstructionKind::DynamicFunctionRefInst, Loc, F) { + SILFunction *F, + TypeExpansionContext context) + : FunctionRefBaseInst(SILInstructionKind::DynamicFunctionRefInst, Loc, F, + context) { assert(F->isDynamicallyReplaceable()); } PreviousDynamicFunctionRefInst::PreviousDynamicFunctionRefInst( - SILDebugLocation Loc, SILFunction *F) + SILDebugLocation Loc, SILFunction *F, TypeExpansionContext context) : FunctionRefBaseInst(SILInstructionKind::PreviousDynamicFunctionRefInst, - Loc, F) { + Loc, F, context) { assert(!F->isDynamicallyReplaceable()); } @@ -619,16 +652,19 @@ AllocGlobalInst::AllocGlobalInst(SILDebugLocation Loc, Global(Global) {} GlobalAddrInst::GlobalAddrInst(SILDebugLocation DebugLoc, - SILGlobalVariable *Global) - : InstructionBase(DebugLoc, Global->getLoweredType().getAddressType(), + SILGlobalVariable *Global, + TypeExpansionContext context) + : InstructionBase(DebugLoc, + Global->getLoweredTypeInContext(context).getAddressType(), Global) {} GlobalValueInst::GlobalValueInst(SILDebugLocation DebugLoc, - SILGlobalVariable *Global) - : InstructionBase(DebugLoc, Global->getLoweredType().getObjectType(), + SILGlobalVariable *Global, + TypeExpansionContext context) + : InstructionBase(DebugLoc, + Global->getLoweredTypeInContext(context).getObjectType(), Global) {} - const IntrinsicInfo &BuiltinInst::getIntrinsicInfo() const { return getModule().getIntrinsicInfo(getName()); } @@ -914,7 +950,7 @@ StructInst::StructInst(SILDebugLocation Loc, SILType Ty, : InstructionBaseWithTrailingOperands( Elems, Loc, Ty, HasOwnership ? *mergeSILValueOwnership(Elems) - : ValueOwnershipKind(ValueOwnershipKind::Any)) { + : ValueOwnershipKind(ValueOwnershipKind::None)) { assert(!Ty.getStructOrBoundGenericStruct()->hasUnreferenceableStorage()); } @@ -1058,7 +1094,8 @@ bool StructExtractInst::isTrivialFieldOfOneRCIDStruct() const { // Otherwise check if we have a non-trivial type. If we don't have one, // continue. - if (StructTy.getFieldType(D, F->getModule()).isTrivial(*F)) + if (StructTy.getFieldType(D, F->getModule(), TypeExpansionContext(*F)) + .isTrivial(*F)) continue; // Ok, this type is non-trivial. If we have not seen a non-trivial field @@ -1103,7 +1140,8 @@ bool StructExtractInst::isFieldOnlyNonTrivialField() const { // Ok, we have a field that is not equal to the field we are // extracting. If that field is trivial, we do not care about // it... continue. - if (StructTy.getFieldType(D, F->getModule()).isTrivial(*F)) + if (StructTy.getFieldType(D, F->getModule(), TypeExpansionContext(*F)) + .isTrivial(*F)) continue; // We have found a non trivial member that is not the member we are @@ -1179,13 +1217,13 @@ bool TermInst::isProgramTerminating() const { llvm_unreachable("Unhandled TermKind in switch."); } -TermInst::SuccessorBlockArgumentsListTy -TermInst::getSuccessorBlockArguments() const { - function_ref op; - op = [](const SILSuccessor &succ) -> PhiArgumentArrayRef { - return succ.getBB()->getPhiArguments(); +TermInst::SuccessorBlockArgumentListTy +TermInst::getSuccessorBlockArgumentLists() const { + function_ref(const SILSuccessor &)> op; + op = [](const SILSuccessor &succ) -> ArrayRef { + return succ.getBB()->getArguments(); }; - return SuccessorBlockArgumentsListTy(getSuccessors(), op); + return SuccessorBlockArgumentListTy(getSuccessors(), op); } YieldInst *YieldInst::create(SILDebugLocation loc, @@ -1229,8 +1267,7 @@ CondBranchInst::CondBranchInst(SILDebugLocation Loc, SILValue Condition, unsigned NumFalse, ProfileCounter TrueBBCount, ProfileCounter FalseBBCount) : InstructionBaseWithTrailingOperands(Condition, Args, Loc), - DestBBs{{this, TrueBB, TrueBBCount}, - {this, FalseBB, FalseBBCount}} { + DestBBs{{{this, TrueBB, TrueBBCount}, {this, FalseBB, FalseBBCount}}} { assert(Args.size() == (NumTrue + NumFalse) && "Invalid number of args"); SILInstruction::Bits.CondBranchInst.NumTrueArgs = NumTrue; assert(SILInstruction::Bits.CondBranchInst.NumTrueArgs == NumTrue && @@ -1395,7 +1432,7 @@ SelectValueInst::SelectValueInst(SILDebugLocation DebugLoc, SILValue Operand, : InstructionBaseWithTrailingOperands( Operand, CaseValuesAndResults, DebugLoc, Type, HasOwnership ? *mergeSILValueOwnership(CaseValuesAndResults) - : ValueOwnershipKind(ValueOwnershipKind::Any)) {} + : ValueOwnershipKind(ValueOwnershipKind::None)) {} SelectValueInst * SelectValueInst::create(SILDebugLocation Loc, SILValue Operand, SILType Type, @@ -1668,7 +1705,7 @@ DynamicMethodBranchInst::DynamicMethodBranchInst(SILDebugLocation Loc, SILBasicBlock *NoMethodBB) : InstructionBase(Loc), Member(Member), - DestBBs{{this, HasMethodBB}, {this, NoMethodBB}}, + DestBBs{{{this, HasMethodBB}, {this, NoMethodBB}}}, Operands(this, Operand) { } @@ -1834,7 +1871,7 @@ OpenExistentialRefInst::OpenExistentialRefInst(SILDebugLocation DebugLoc, : UnaryInstructionBase(DebugLoc, Operand, Ty, HasOwnership ? Operand.getOwnershipKind() - : ValueOwnershipKind(ValueOwnershipKind::Any)) { + : ValueOwnershipKind(ValueOwnershipKind::None)) { assert(Operand->getType().isObject() && "Operand must be an object."); assert(Ty.isObject() && "Result type must be an object type."); } @@ -1921,65 +1958,77 @@ UncheckedBitwiseCastInst::create(SILDebugLocation DebugLoc, SILValue Operand, } UnconditionalCheckedCastInst *UnconditionalCheckedCastInst::create( - SILDebugLocation DebugLoc, SILValue Operand, SILType DestTy, SILFunction &F, - SILOpenedArchetypesState &OpenedArchetypes) { + SILDebugLocation DebugLoc, SILValue Operand, + SILType DestLoweredTy, CanType DestFormalTy, + SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes) { SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getASTType()); + DestFormalTy); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UnconditionalCheckedCastInst)); return ::new (Buffer) UnconditionalCheckedCastInst(DebugLoc, Operand, - TypeDependentOperands, DestTy); + TypeDependentOperands, + DestLoweredTy, + DestFormalTy); } UnconditionalCheckedCastValueInst *UnconditionalCheckedCastValueInst::create( - SILDebugLocation DebugLoc, SILValue Operand, SILType DestTy, SILFunction &F, - SILOpenedArchetypesState &OpenedArchetypes) { + SILDebugLocation DebugLoc, + SILValue Operand, CanType SrcFormalTy, + SILType DestLoweredTy, CanType DestFormalTy, + SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes) { SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getASTType()); + DestFormalTy); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(UnconditionalCheckedCastValueInst)); return ::new (Buffer) UnconditionalCheckedCastValueInst( - DebugLoc, Operand, TypeDependentOperands, DestTy); + DebugLoc, Operand, SrcFormalTy, TypeDependentOperands, + DestLoweredTy, DestFormalTy); } CheckedCastBranchInst *CheckedCastBranchInst::create( - SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, SILType DestTy, + SILDebugLocation DebugLoc, bool IsExact, SILValue Operand, + SILType DestLoweredTy, CanType DestFormalTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes, ProfileCounter Target1Count, ProfileCounter Target2Count) { SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getASTType()); + DestFormalTy); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(CheckedCastBranchInst)); return ::new (Buffer) CheckedCastBranchInst( - DebugLoc, IsExact, Operand, TypeDependentOperands, DestTy, SuccessBB, - FailureBB, Target1Count, Target2Count); + DebugLoc, IsExact, Operand, TypeDependentOperands, + DestLoweredTy, DestFormalTy, SuccessBB, FailureBB, + Target1Count, Target2Count); } CheckedCastValueBranchInst * -CheckedCastValueBranchInst::create(SILDebugLocation DebugLoc, SILValue Operand, - SILType DestTy, SILBasicBlock *SuccessBB, - SILBasicBlock *FailureBB, SILFunction &F, +CheckedCastValueBranchInst::create(SILDebugLocation DebugLoc, + SILValue Operand, CanType SrcFormalTy, + SILType DestLoweredTy, CanType DestFormalTy, + SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB, + SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes) { SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - DestTy.getASTType()); + DestFormalTy); unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(CheckedCastValueBranchInst)); return ::new (Buffer) CheckedCastValueBranchInst( - DebugLoc, Operand, TypeDependentOperands, DestTy, SuccessBB, FailureBB); + DebugLoc, Operand, SrcFormalTy, TypeDependentOperands, + DestLoweredTy, DestFormalTy, + SuccessBB, FailureBB); } MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty, @@ -2062,7 +2111,7 @@ ConvertFunctionInst *ConvertFunctionInst::create( (void)opTI; CanSILFunctionType resTI = CFI->getType().castTo(); (void)resTI; - assert(opTI->isABICompatibleWith(resTI, &F).isCompatible() && + assert(opTI->isABICompatibleWith(resTI, F).isCompatible() && "Can not convert in between ABI incompatible function types"); } return CFI; @@ -2092,7 +2141,7 @@ ConvertEscapeToNoEscapeInst *ConvertEscapeToNoEscapeInst::create( (void)opTI; CanSILFunctionType resTI = CFI->getType().castTo(); (void)resTI; - assert(opTI->isABICompatibleWith(resTI, &F) + assert(opTI->isABICompatibleWith(resTI, F) .isCompatibleUpToNoEscapeConversion() && "Can not convert in between ABI incompatible function types"); } @@ -2369,6 +2418,46 @@ void KeyPathInst::dropReferencedPattern() { Pattern = nullptr; } +void KeyPathPatternComponent:: +visitReferencedFunctionsAndMethods( + std::function functionCallBack, + std::function methodCallBack) const { + switch (getKind()) { + case KeyPathPatternComponent::Kind::SettableProperty: + functionCallBack(getComputedPropertySetter()); + LLVM_FALLTHROUGH; + case KeyPathPatternComponent::Kind::GettableProperty: { + functionCallBack(getComputedPropertyGetter()); + auto id = getComputedPropertyId(); + switch (id.getKind()) { + case KeyPathPatternComponent::ComputedPropertyId::DeclRef: { + methodCallBack(id.getDeclRef()); + break; + } + case KeyPathPatternComponent::ComputedPropertyId::Function: + functionCallBack(id.getFunction()); + break; + case KeyPathPatternComponent::ComputedPropertyId::Property: + break; + } + + if (auto equals = getSubscriptIndexEquals()) + functionCallBack(equals); + if (auto hash = getSubscriptIndexHash()) + functionCallBack(hash); + + break; + } + case KeyPathPatternComponent::Kind::StoredProperty: + case KeyPathPatternComponent::Kind::OptionalChain: + case KeyPathPatternComponent::Kind::OptionalForce: + case KeyPathPatternComponent::Kind::OptionalWrap: + case KeyPathPatternComponent::Kind::TupleElement: + break; + } +} + + GenericSpecializationInformation::GenericSpecializationInformation( SILFunction *Caller, SILFunction *Parent, SubstitutionMap Subs) : Caller(Caller), Parent(Parent), Subs(Subs) {} @@ -2421,11 +2510,12 @@ static void computeAggregateFirstLevelSubtypeInfo( // TODO: Create an iterator for accessing first level projections to eliminate // this SmallVector. llvm::SmallVector Projections; - Projection::getFirstLevelProjections(OpType, M, Projections); + Projection::getFirstLevelProjections(OpType, M, F.getTypeExpansionContext(), + Projections); auto OpOwnershipKind = Operand.getOwnershipKind(); for (auto &P : Projections) { - SILType ProjType = P.getType(OpType, M); + SILType ProjType = P.getType(OpType, M, F.getTypeExpansionContext()); Types.emplace_back(ProjType); OwnershipKinds.emplace_back( OpOwnershipKind.getProjectedOwnershipKind(F, ProjType)); diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index c6723e0b206f6..6b022676ca2be 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -90,7 +90,7 @@ class SILModule::SerializationCallback final }; SILModule::SILModule(ModuleDecl *SwiftModule, TypeConverter &TC, - SILOptions &Options, const DeclContext *associatedDC, + const SILOptions &Options, const DeclContext *associatedDC, bool wholeModule) : TheSwiftModule(SwiftModule), AssociatedDeclContext(associatedDC), @@ -120,7 +120,7 @@ SILModule::~SILModule() { } std::unique_ptr -SILModule::createEmptyModule(ModuleDecl *M, TypeConverter &TC, SILOptions &Options, +SILModule::createEmptyModule(ModuleDecl *M, TypeConverter &TC, const SILOptions &Options, bool WholeModule) { return std::unique_ptr( new SILModule(M, TC, Options, M, WholeModule)); @@ -321,7 +321,8 @@ SILFunction *SILModule::lookUpFunction(SILDeclRef fnRef) { } bool SILModule::loadFunction(SILFunction *F) { - SILFunction *NewF = getSILLoader()->lookupSILFunction(F); + SILFunction *NewF = + getSILLoader()->lookupSILFunction(F, /*onlyUpdateLinkage*/ false); if (!NewF) return false; @@ -329,6 +330,10 @@ bool SILModule::loadFunction(SILFunction *F) { return true; } +void SILModule::updateFunctionLinkage(SILFunction *F) { + getSILLoader()->lookupSILFunction(F, /*onlyUpdateLinkage*/ true); +} + bool SILModule::linkFunction(SILFunction *F, SILModule::LinkingMode Mode) { return SILLinkerVisitor(*this, Mode).processFunction(F); } @@ -491,7 +496,7 @@ SILModule::lookUpFunctionInWitnessTable(ProtocolConformanceRef C, if (!Ret) { LLVM_DEBUG(llvm::dbgs() << " Failed speculative lookup of " "witness for: "; - C.dump(); Requirement.dump()); + C.dump(llvm::dbgs()); Requirement.dump()); return std::make_pair(nullptr, nullptr); } diff --git a/lib/SIL/SILOpenedArchetypesTracker.cpp b/lib/SIL/SILOpenedArchetypesTracker.cpp index 36daa46a56982..3b4a9633aade1 100644 --- a/lib/SIL/SILOpenedArchetypesTracker.cpp +++ b/lib/SIL/SILOpenedArchetypesTracker.cpp @@ -210,7 +210,7 @@ void SILOpenedArchetypesTracker::dump() const { auto Archetype = KV.first; auto Def = KV.second; llvm::dbgs() << "open archetype:\n"; - Type(Archetype)->dump(); + Type(Archetype)->dump(llvm::dbgs()); llvm::dbgs() << "defined at: " << *Def << "\n"; } llvm::dbgs() << "}\n"; diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index 17040c08299de..f05a70f435a82 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -287,10 +287,22 @@ bool SILValueOwnershipChecker::gatherUsers( continue; } - // If we are guaranteed, but are not a guaranteed forwarding inst, - // just continue. This user is just treated as a normal use. - if (!isGuaranteedForwardingInst(user)) + // If we are guaranteed, but are not a guaranteed forwarding inst, we add + // the end scope instructions of any new sub-scopes. This ensures that the + // parent scope completely encloses the child borrow scope. + // + // Example: A guaranteed parameter of a co-routine. + if (!isGuaranteedForwardingInst(user)) { + // First check if we are visiting an operand that introduces a new + // sub-scope. If we do, we need to preserve + if (auto scopedOperand = BorrowScopeOperand::get(op)) { + scopedOperand->visitEndScopeInstructions( + [&](Operand *op) { implicitRegularUsers.push_back(op); }); + } + + // Then continue. continue; + } // At this point, we know that we must have a forwarded subobject. Since the // base type is guaranteed, we know that the subobject is either guaranteed @@ -299,7 +311,7 @@ bool SILValueOwnershipChecker::gatherUsers( // User's results to the worklist. if (user->getResults().size()) { for (SILValue result : user->getResults()) { - if (result.getOwnershipKind() == ValueOwnershipKind::Any) { + if (result.getOwnershipKind() == ValueOwnershipKind::None) { continue; } @@ -337,7 +349,7 @@ bool SILValueOwnershipChecker::gatherUsers( // // TODO: We could ignore this error and emit a more specific error on the // actual terminator. - for (auto *succArg : succBlock->getPhiArguments()) { + for (auto *succArg : succBlock->getSILPhiArguments()) { // *NOTE* We do not emit an error here since we want to allow for more // specific errors to be found during use_verification. // @@ -351,7 +363,7 @@ bool SILValueOwnershipChecker::gatherUsers( } // If we have an any value, just continue. - if (succArgOwnershipKind == ValueOwnershipKind::Any) + if (succArgOwnershipKind == ValueOwnershipKind::None) continue; // Otherwise add all end_borrow users for this BBArg to the @@ -380,7 +392,7 @@ bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses( switch (arg->getOwnershipKind()) { case ValueOwnershipKind::Guaranteed: case ValueOwnershipKind::Unowned: - case ValueOwnershipKind::Any: + case ValueOwnershipKind::None: return true; case ValueOwnershipKind::Owned: break; @@ -401,7 +413,7 @@ bool SILValueOwnershipChecker::checkYieldWithoutLifetimeEndingUses( switch (yield->getOwnershipKind()) { case ValueOwnershipKind::Guaranteed: case ValueOwnershipKind::Unowned: - case ValueOwnershipKind::Any: + case ValueOwnershipKind::None: return true; case ValueOwnershipKind::Owned: break; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 6cd3b7ad50803..976b9c790185c 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -64,6 +64,10 @@ llvm::cl::opt SILPrintDebugInfo("sil-print-debuginfo", llvm::cl::init(false), llvm::cl::desc("Include debug info in SIL output")); +llvm::cl::opt +SILPrintSourceInfo("sil-print-sourceinfo", llvm::cl::init(false), + llvm::cl::desc("Include source annotation in SIL output")); + llvm::cl::opt SILPrintGenericSpecializationInfo( "sil-print-generic-specialization-info", llvm::cl::init(false), llvm::cl::desc("Include generic specialization" @@ -222,9 +226,10 @@ static void printFullContext(const DeclContext *Context, raw_ostream &Buffer) { static void printValueDecl(ValueDecl *Decl, raw_ostream &OS) { printFullContext(Decl->getDeclContext(), OS); - assert(Decl->hasName()); - if (Decl->isOperator()) { + if (!Decl->hasName()) { + OS << "anonname=" << (const void*)Decl; + } else if (Decl->isOperator()) { OS << '"' << Decl->getBaseName() << '"'; } else { bool shouldEscape = !Decl->getBaseName().isSpecial() && @@ -469,7 +474,7 @@ class SILPrinter : public SILInstructionVisitor { if (!i.Type) return *this; *this << " : "; - if (i.OwnershipKind && *i.OwnershipKind != ValueOwnershipKind::Any) { + if (i.OwnershipKind && *i.OwnershipKind != ValueOwnershipKind::None) { *this << "@" << i.OwnershipKind.getValue() << " "; } return *this << i.Type; @@ -617,7 +622,22 @@ class SILPrinter : public SILInstructionVisitor { } *this << '\n'; + const auto &SM = BB->getModule().getASTContext().SourceMgr; + Optional PrevLoc; for (const SILInstruction &I : *BB) { + if (SILPrintSourceInfo) { + auto CurSourceLoc = I.getLoc().getSourceLoc(); + if (CurSourceLoc.isValid()) { + if (!PrevLoc || SM.getLineNumber(CurSourceLoc) > SM.getLineNumber(PrevLoc->getSourceLoc())) { + auto Buffer = SM.findBufferContainingLoc(CurSourceLoc); + auto Line = SM.getLineNumber(CurSourceLoc); + auto LineLength = SM.getLineLength(Buffer, Line); + PrintState.OS << " // " << SM.extractText({SM.getLocForLineCol(Buffer, Line, 0), LineLength.getValueOr(0)}) << + "\tSourceLoc: " << SM.getDisplayNameForLoc(CurSourceLoc) << ":" << Line << "\n"; + PrevLoc = I.getLoc(); + } + } + } Ctx.printInstructionCallBack(&I); if (SILPrintGenericSpecializationInfo) { if (auto AI = ApplySite::isa(const_cast(&I))) @@ -1083,7 +1103,7 @@ class SILPrinter : public SILInstructionVisitor { void visitApplyInstBase(Inst *AI) { *this << Ctx.getID(AI->getCallee()); printSubstitutions(AI->getSubstitutionMap(), - AI->getOrigCalleeType()->getGenericSignature()); + AI->getOrigCalleeType()->getInvocationGenericSignature()); *this << '('; interleave(AI->getArguments(), [&](const SILValue &arg) { *this << Ctx.getID(arg); }, @@ -1387,13 +1407,13 @@ class SILPrinter : public SILInstructionVisitor { } void visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *CI) { - *this << getIDAndType(CI->getOperand()) << " to " << CI->getType(); + *this << getIDAndType(CI->getOperand()) << " to " << CI->getTargetFormalType(); } void visitCheckedCastBranchInst(CheckedCastBranchInst *CI) { if (CI->isExact()) *this << "[exact] "; - *this << getIDAndType(CI->getOperand()) << " to " << CI->getCastType() + *this << getIDAndType(CI->getOperand()) << " to " << CI->getTargetFormalType() << ", " << Ctx.getID(CI->getSuccessBB()) << ", " << Ctx.getID(CI->getFailureBB()); if (CI->getTrueBBCount()) @@ -1403,26 +1423,28 @@ class SILPrinter : public SILInstructionVisitor { } void visitCheckedCastValueBranchInst(CheckedCastValueBranchInst *CI) { - *this << getIDAndType(CI->getOperand()) << " to " << CI->getCastType() + *this << CI->getSourceFormalType() << " in " + << getIDAndType(CI->getOperand()) << " to " << CI->getTargetFormalType() << ", " << Ctx.getID(CI->getSuccessBB()) << ", " << Ctx.getID(CI->getFailureBB()); } void visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *CI) { - *this << CI->getSourceType() << " in " << getIDAndType(CI->getSrc()) - << " to " << CI->getTargetType() << " in " + *this << CI->getSourceFormalType() << " in " << getIDAndType(CI->getSrc()) + << " to " << CI->getTargetFormalType() << " in " << getIDAndType(CI->getDest()); } void visitUnconditionalCheckedCastValueInst( UnconditionalCheckedCastValueInst *CI) { - *this << getIDAndType(CI->getOperand()) << " to " << CI->getType(); + *this << CI->getSourceFormalType() << " in " << getIDAndType(CI->getOperand()) + << " to " << CI->getTargetFormalType(); } void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CI) { *this << getCastConsumptionKindName(CI->getConsumptionKind()) << ' ' - << CI->getSourceType() << " in " << getIDAndType(CI->getSrc()) - << " to " << CI->getTargetType() << " in " + << CI->getSourceFormalType() << " in " << getIDAndType(CI->getSrc()) + << " to " << CI->getTargetFormalType() << " in " << getIDAndType(CI->getDest()) << ", " << Ctx.getID(CI->getSuccessBB()) << ", " << Ctx.getID(CI->getFailureBB()); @@ -1477,8 +1499,8 @@ class SILPrinter : public SILInstructionVisitor { printUncheckedConversionInst(CI, CI->getOperand()); } void visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *CI) { - *this << ' ' << CI->getSourceType() << " in " << getIDAndType(CI->getSrc()) - << " to " << CI->getTargetType() << " in " + *this << ' ' << CI->getSourceFormalType() << " in " << getIDAndType(CI->getSrc()) + << " to " << CI->getTargetFormalType() << " in " << getIDAndType(CI->getDest()); } void visitUncheckedAddrCastInst(UncheckedAddrCastInst *CI) { @@ -1542,13 +1564,13 @@ class SILPrinter : public SILInstructionVisitor { } #define UNCHECKED_REF_STORAGE(Name, ...) \ - void visitCopy##Name##ValueInst(Copy##Name##ValueInst *I) { \ + void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ *this << getIDAndType(I->getOperand()); \ } -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - void visitCopy##Name##ValueInst(Copy##Name##ValueInst *I) { \ - *this << getIDAndType(I->getOperand()); \ +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ + *this << getIDAndType(I->getOperand()); \ } #include "swift/AST/ReferenceStorage.def" @@ -2417,7 +2439,7 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { llvm::DenseMap Aliases; llvm::DenseSet UsedNames; - auto sig = getLoweredFunctionType()->getGenericSignature(); + auto sig = getLoweredFunctionType()->getSubstGenericSignature(); auto *env = getGenericEnvironment(); if (sig && env) { llvm::SmallString<16> disambiguatedNameBuf; @@ -2673,7 +2695,7 @@ void SILProperty::print(SILPrintContext &Ctx) const { printValueDecl(getDecl(), OS); if (auto sig = getDecl()->getInnermostDeclContext() ->getGenericSignatureOfContext()) { - sig->getCanonicalSignature()->print(OS, Options); + sig.getCanonicalSignature()->print(OS, Options); } OS << " ("; if (auto component = getComponent()) @@ -2693,6 +2715,18 @@ static void printSILProperties(SILPrintContext &Ctx, } } +static void printExternallyVisibleDecls(SILPrintContext &Ctx, + ArrayRef decls) { + if (decls.empty()) + return; + Ctx.OS() << "/* externally visible decls: \n"; + for (ValueDecl *decl : decls) { + printValueDecl(decl, Ctx.OS()); + Ctx.OS() << '\n'; + } + Ctx.OS() << "*/\n"; +} + /// Pretty-print the SILModule to the designated stream. void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, bool PrintASTDecls) const { @@ -2759,7 +2793,8 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printSILDefaultWitnessTables(PrintCtx, getDefaultWitnessTableList()); printSILCoverageMaps(PrintCtx, getCoverageMaps()); printSILProperties(PrintCtx, getPropertyList()); - + printExternallyVisibleDecls(PrintCtx, externallyVisible.getArrayRef()); + OS << "\n\n"; } @@ -3100,6 +3135,11 @@ SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, bool Verbose, OutStream(OS), Verbose(Verbose), SortedSIL(SortedSIL), DebugInfo(SILPrintDebugInfo) { } +SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, + const SILOptions &Opts) : + OutStream(OS), Verbose(Opts.EmitVerboseSIL), SortedSIL(Opts.EmitSortedSIL), + DebugInfo(SILPrintDebugInfo) {} + SILPrintContext::SILPrintContext(llvm::raw_ostream &OS, bool Verbose, bool SortedSIL, bool DebugInfo) : OutStream(OS), Verbose(Verbose), SortedSIL(SortedSIL), diff --git a/lib/SIL/SILProfiler.cpp b/lib/SIL/SILProfiler.cpp index 010ef576c9059..a361a4fdb5de4 100644 --- a/lib/SIL/SILProfiler.cpp +++ b/lib/SIL/SILProfiler.cpp @@ -133,8 +133,9 @@ static bool canCreateProfilerForAST(ASTNode N, SILDeclRef forDecl) { if (isa(D)) return true; - } else if (auto *E = N.get()) { + } else if (N.get()) { if (forDecl.isStoredPropertyInitializer() || + forDecl.isPropertyWrapperBackingInitializer() || forDecl.getAbstractClosureExpr()) return true; } @@ -151,8 +152,10 @@ SILProfiler *SILProfiler::create(SILModule &M, ForDefinition_t forDefinition, if (!doesASTRequireProfiling(M, N) && Opts.UseProfile.empty()) return nullptr; - if (!canCreateProfilerForAST(N, forDecl)) + if (!canCreateProfilerForAST(N, forDecl)) { + N.dump(llvm::errs()); llvm_unreachable("Invalid AST node for profiling"); + } auto *Buf = M.allocate(1); auto *SP = diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp index b64663c70f6df..104e24d600b54 100644 --- a/lib/SIL/SILType.cpp +++ b/lib/SIL/SILType.cpp @@ -81,18 +81,20 @@ SILType SILType::getSILTokenType(const ASTContext &C) { } bool SILType::isTrivial(const SILFunction &F) const { - return F.getTypeLowering(*this).isTrivial(); + auto contextType = hasTypeParameter() ? F.mapTypeIntoContext(*this) : *this; + + return F.getTypeLowering(contextType).isTrivial(); } bool SILType::isReferenceCounted(SILModule &M) const { return M.Types.getTypeLowering(*this, - ResilienceExpansion::Minimal) + TypeExpansionContext::minimal()) .isReferenceCounted(); } -bool SILType::isNoReturnFunction() const { +bool SILType::isNoReturnFunction(SILModule &M) const { if (auto funcTy = dyn_cast(getASTType())) - return funcTy->isNoReturnFunction(); + return funcTy->isNoReturnFunction(M); return false; } @@ -133,8 +135,8 @@ bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) { && toTy.isHeapObjectReferenceType(); } -SILType SILType::getFieldType(VarDecl *field, - TypeConverter &TC) const { +SILType SILType::getFieldType(VarDecl *field, TypeConverter &TC, + TypeExpansionContext context) const { AbstractionPattern origFieldTy = TC.getAbstractionPattern(field); CanType substFieldTy; if (field->hasClangNode()) { @@ -144,7 +146,8 @@ SILType SILType::getFieldType(VarDecl *field, getASTType()->getTypeOfMember(&TC.M, field, nullptr)->getCanonicalType(); } - auto loweredTy = TC.getLoweredRValueType(origFieldTy, substFieldTy); + auto loweredTy = + TC.getLoweredRValueType(context, origFieldTy, substFieldTy); if (isAddress() || getClassOrBoundGenericClass() != nullptr) { return SILType::getPrimitiveAddressType(loweredTy); } else { @@ -152,12 +155,13 @@ SILType SILType::getFieldType(VarDecl *field, } } -SILType SILType::getFieldType(VarDecl *field, SILModule &M) const { - return getFieldType(field, M.Types); +SILType SILType::getFieldType(VarDecl *field, SILModule &M, + TypeExpansionContext context) const { + return getFieldType(field, M.Types, context); } -SILType SILType::getEnumElementType(EnumElementDecl *elt, - TypeConverter &TC) const { +SILType SILType::getEnumElementType(EnumElementDecl *elt, TypeConverter &TC, + TypeExpansionContext context) const { assert(elt->getDeclContext() == getEnumOrBoundGenericEnum()); assert(elt->hasAssociatedValues()); @@ -168,7 +172,7 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, // If the case is indirect, then the payload is boxed. if (elt->isIndirect() || elt->getParentEnum()->isIndirect()) { - auto box = TC.getBoxTypeForEnumElement(*this, elt); + auto box = TC.getBoxTypeForEnumElement(context, *this, elt); return SILType(SILType::getPrimitiveObjectType(box).getASTType(), getCategory()); } @@ -176,15 +180,15 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, auto substEltTy = getASTType()->getTypeOfMember(&TC.M, elt, elt->getArgumentInterfaceType()); - auto loweredTy = - TC.getLoweredRValueType(TC.getAbstractionPattern(elt), - substEltTy); + auto loweredTy = TC.getLoweredRValueType( + context, TC.getAbstractionPattern(elt), substEltTy); return SILType(loweredTy, getCategory()); } -SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M) const { - return getEnumElementType(elt, M.Types); +SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M, + TypeExpansionContext context) const { + return getEnumElementType(elt, M.Types, context); } bool SILType::isLoadableOrOpaque(const SILFunction &F) const { @@ -193,13 +197,15 @@ bool SILType::isLoadableOrOpaque(const SILFunction &F) const { } bool SILType::isAddressOnly(const SILFunction &F) const { - return F.getTypeLowering(*this).isAddressOnly(); + auto contextType = hasTypeParameter() ? F.mapTypeIntoContext(*this) : *this; + + return F.getTypeLowering(contextType).isAddressOnly(); } -SILType SILType::substGenericArgs(SILModule &M, - SubstitutionMap SubMap) const { +SILType SILType::substGenericArgs(SILModule &M, SubstitutionMap SubMap, + TypeExpansionContext context) const { auto fnTy = castTo(); - auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, SubMap)); + auto canFnTy = CanSILFunctionType(fnTy->substGenericArgs(M, SubMap, context)); return SILType::getPrimitiveObjectType(canFnTy); } @@ -217,7 +223,8 @@ bool SILType::isHeapObjectReferenceType() const { return false; } -bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod) const { +bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod, + TypeExpansionContext context) const { assert(!hasArchetype() && "Agg should be proven to not be generic " "before passed to this function."); assert(!Record.hasArchetype() && "Record should be proven to not be generic " @@ -246,14 +253,14 @@ bool SILType::aggregateContainsRecord(SILType Record, SILModule &Mod) const { if (EnumDecl *E = Ty.getEnumOrBoundGenericEnum()) { for (auto Elt : E->getAllElements()) if (Elt->hasAssociatedValues()) - Worklist.push_back(Ty.getEnumElementType(Elt, Mod)); + Worklist.push_back(Ty.getEnumElementType(Elt, Mod, context)); continue; } // Then if we have a struct address... if (StructDecl *S = Ty.getStructOrBoundGenericStruct()) for (VarDecl *Var : S->getStoredProperties()) - Worklist.push_back(Ty.getFieldType(Var, Mod)); + Worklist.push_back(Ty.getFieldType(Var, Mod, context)); // If we have a class address, it is a pointer so it cannot contain other // types. @@ -304,12 +311,10 @@ static bool isBridgedErrorClass(ASTContext &ctx, Type t) { t = archetypeType->getSuperclass(); // NSError (TODO: and CFError) can be bridged. - auto nsErrorType = ctx.getNSErrorDecl(); - if (t && nsErrorType && - nsErrorType->getDeclaredType()->isExactSuperclassOf(t)) { + auto nsErrorType = ctx.getNSErrorType(); + if (t && nsErrorType && nsErrorType->isExactSuperclassOf(t)) return true; - } - + return false; } @@ -389,35 +394,39 @@ SILType SILType::mapTypeOutOfContext() const { getCategory()); } -CanType -swift::getSILBoxFieldLoweredType(SILBoxType *type, TypeConverter &TC, - unsigned index) { - auto fieldTy = type->getLayout()->getFields()[index].getLoweredType(); +CanType swift::getSILBoxFieldLoweredType(TypeExpansionContext context, + SILBoxType *type, TypeConverter &TC, + unsigned index) { + auto fieldTy = SILType::getPrimitiveObjectType( + type->getLayout()->getFields()[index].getLoweredType()); + + // Map the type into the new expansion context, which might substitute opaque + // types. + auto sig = type->getLayout()->getGenericSignature(); + fieldTy = TC.getTypeLowering(fieldTy, context, sig) + .getLoweredType(); // Apply generic arguments if the layout is generic. if (auto subMap = type->getSubstitutions()) { - auto sig = type->getLayout()->getGenericSignature(); - return SILType::getPrimitiveObjectType(fieldTy) - .subst(TC, - QuerySubstitutionMap{subMap}, - LookUpConformanceInSubstitutionMap(subMap), - sig) - .getASTType(); + fieldTy = fieldTy.subst(TC, + QuerySubstitutionMap{subMap}, + LookUpConformanceInSubstitutionMap(subMap), + sig); } - return fieldTy; + + return fieldTy.getASTType(); } ValueOwnershipKind SILResultInfo::getOwnershipKind(SILFunction &F) const { auto &M = F.getModule(); - auto sig = F.getLoweredFunctionType()->getGenericSignature(); - GenericContextScope GCS(M.Types, sig); + auto FTy = F.getLoweredFunctionType(); - bool IsTrivial = getSILStorageType().isTrivial(F); + bool IsTrivial = getSILStorageType(M, FTy).isTrivial(F); switch (getConvention()) { case ResultConvention::Indirect: return SILModuleConventions(M).isSILIndirect(*this) - ? ValueOwnershipKind::Any + ? ValueOwnershipKind::None : ValueOwnershipKind::Owned; case ResultConvention::Autoreleased: case ResultConvention::Owned: @@ -425,23 +434,24 @@ SILResultInfo::getOwnershipKind(SILFunction &F) const { case ResultConvention::Unowned: case ResultConvention::UnownedInnerPointer: if (IsTrivial) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; return ValueOwnershipKind::Unowned; } llvm_unreachable("Unhandled ResultConvention in switch."); } -SILModuleConventions::SILModuleConventions(const SILModule &M) - : loweredAddresses(!M.getASTContext().LangOpts.EnableSILOpaqueValues - || M.getStage() == SILStage::Lowered) {} +SILModuleConventions::SILModuleConventions(SILModule &M) + : M(&M), + loweredAddresses(!M.getASTContext().LangOpts.EnableSILOpaqueValues + || M.getStage() == SILStage::Lowered) +{} bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type, SILModule &M) { if (SILModuleConventions(M).loweredAddresses) { - return M.Types.getTypeLowering(type, - ResilienceExpansion::Minimal) - .isAddressOnly(); + return M.Types.getTypeLowering(type, TypeExpansionContext::minimal()) + .isAddressOnly(); } return false; @@ -449,18 +459,17 @@ bool SILModuleConventions::isReturnedIndirectlyInSIL(SILType type, bool SILModuleConventions::isPassedIndirectlyInSIL(SILType type, SILModule &M) { if (SILModuleConventions(M).loweredAddresses) { - return M.Types.getTypeLowering(type, - ResilienceExpansion::Minimal) - .isAddressOnly(); + return M.Types.getTypeLowering(type, TypeExpansionContext::minimal()) + .isAddressOnly(); } return false; } -bool SILFunctionType::isNoReturnFunction() const { +bool SILFunctionType::isNoReturnFunction(SILModule &M) const { for (unsigned i = 0, e = getNumResults(); i < e; ++i) { - if (getResults()[i].getType()->isUninhabited()) + if (getResults()[i].getReturnValueType(M, this)->isUninhabited()) return true; } @@ -545,8 +554,14 @@ bool SILType::hasAbstractionDifference(SILFunctionTypeRepresentation rep, return (*this != type2); } -bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { +bool SILType::isLoweringOf(TypeExpansionContext context, SILModule &Mod, + CanType formalType) { SILType loweredType = *this; + if (formalType->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes() && + loweredType.getASTType() == + Mod.Types.getLoweredRValueType(context, formalType)) + return true; // Optional lowers its contained type. SILType loweredObjectType = loweredType.getOptionalObjectType(); @@ -554,7 +569,7 @@ bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { if (loweredObjectType) { return formalObjectType && - loweredObjectType.isLoweringOf(Mod, formalObjectType); + loweredObjectType.isLoweringOf(context, Mod, formalObjectType); } // Metatypes preserve their instance type through lowering. @@ -585,7 +600,8 @@ bool SILType::isLoweringOf(SILModule &Mod, CanType formalType) { for (unsigned i = 0, e = loweredTT->getNumElements(); i < e; ++i) { auto loweredTTEltType = SILType::getPrimitiveAddressType(loweredTT.getElementType(i)); - if (!loweredTTEltType.isLoweringOf(Mod, formalTT.getElementType(i))) + if (!loweredTTEltType.isLoweringOf(context, Mod, + formalTT.getElementType(i))) return false; } return true; diff --git a/lib/SIL/SILUndef.cpp b/lib/SIL/SILUndef.cpp index bf990e3b0581f..a234bbd6a9756 100644 --- a/lib/SIL/SILUndef.cpp +++ b/lib/SIL/SILUndef.cpp @@ -17,7 +17,7 @@ using namespace swift; static ValueOwnershipKind getOwnershipKindForUndef(SILType type, const SILFunction &f) { if (type.isAddress() || type.isTrivial(f)) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; return ValueOwnershipKind::Owned; } diff --git a/lib/SIL/SILValue.cpp b/lib/SIL/SILValue.cpp index 3d985f2cbc7f8..e296fa55558af 100644 --- a/lib/SIL/SILValue.cpp +++ b/lib/SIL/SILValue.cpp @@ -153,7 +153,7 @@ ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type, // Trivial types can be passed using a variety of conventions. They always // have trivial ownership. if (Type.isTrivial(F)) { - Value = ValueOwnershipKind::Any; + Value = ValueOwnershipKind::None; return; } @@ -161,18 +161,18 @@ ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type, case SILArgumentConvention::Indirect_In: case SILArgumentConvention::Indirect_In_Constant: Value = SILModuleConventions(M).useLoweredAddresses() - ? ValueOwnershipKind::Any - : ValueOwnershipKind::Owned; + ? ValueOwnershipKind::None + : ValueOwnershipKind::Owned; break; case SILArgumentConvention::Indirect_In_Guaranteed: Value = SILModuleConventions(M).useLoweredAddresses() - ? ValueOwnershipKind::Any - : ValueOwnershipKind::Guaranteed; + ? ValueOwnershipKind::None + : ValueOwnershipKind::Guaranteed; break; case SILArgumentConvention::Indirect_Inout: case SILArgumentConvention::Indirect_InoutAliasable: case SILArgumentConvention::Indirect_Out: - Value = ValueOwnershipKind::Any; + Value = ValueOwnershipKind::None; return; case SILArgumentConvention::Direct_Owned: Value = ValueOwnershipKind::Owned; @@ -196,7 +196,7 @@ StringRef ValueOwnershipKind::asString() const { return "owned"; case ValueOwnershipKind::Guaranteed: return "guaranteed"; - case ValueOwnershipKind::Any: + case ValueOwnershipKind::None: return "any"; } llvm_unreachable("Unhandled ValueOwnershipKind in switch."); @@ -213,15 +213,15 @@ ValueOwnershipKind::merge(ValueOwnershipKind RHS) const { auto RHSVal = RHS.Value; // Any merges with anything. - if (LHSVal == ValueOwnershipKind::Any) { + if (LHSVal == ValueOwnershipKind::None) { return ValueOwnershipKind(RHSVal); } // Any merges with anything. - if (RHSVal == ValueOwnershipKind::Any) { + if (RHSVal == ValueOwnershipKind::None) { return ValueOwnershipKind(LHSVal); } - return (LHSVal == RHSVal) ? Optional(*this) : None; + return (LHSVal == RHSVal) ? Optional(*this) : llvm::None; } ValueOwnershipKind::ValueOwnershipKind(StringRef S) { @@ -229,7 +229,7 @@ ValueOwnershipKind::ValueOwnershipKind(StringRef S) { .Case("unowned", ValueOwnershipKind::Unowned) .Case("owned", ValueOwnershipKind::Owned) .Case("guaranteed", ValueOwnershipKind::Guaranteed) - .Case("any", ValueOwnershipKind::Any) + .Case("any", ValueOwnershipKind::None) .Default(None); if (!Result.hasValue()) llvm_unreachable("Invalid string representation of ValueOwnershipKind"); @@ -240,7 +240,7 @@ ValueOwnershipKind ValueOwnershipKind::getProjectedOwnershipKind(const SILFunction &F, SILType Proj) const { if (Proj.isTrivial(F)) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; return *this; } diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 03d11540f459c..2cfc03caf564b 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -134,14 +134,15 @@ void verifyKeyPathComponent(SILModule &M, bool forPropertyDescriptor, bool hasIndices) { auto &C = M.getASTContext(); - + auto typeExpansionContext = + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion); auto opaque = AbstractionPattern::getOpaque(); auto loweredBaseTy = - M.Types.getLoweredType(opaque, baseTy, expansion); + M.Types.getLoweredType(opaque, baseTy, typeExpansionContext); auto componentTy = component.getComponentType().subst(patternSubs) ->getCanonicalType(); auto loweredComponentTy = - M.Types.getLoweredType(opaque, componentTy, expansion); + M.Types.getLoweredType(opaque, componentTy, typeExpansionContext); auto checkIndexEqualsAndHash = [&]{ if (!component.getSubscriptIndices().empty()) { @@ -153,7 +154,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substEqualsType = equals->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substEqualsType->getParameters().size() == 2, "must have two arguments"); @@ -162,7 +163,7 @@ void verifyKeyPathComponent(SILModule &M, require(param.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); - require(param.getType()->getAnyNominal() + require(param.getInterfaceType()->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } @@ -173,7 +174,8 @@ void verifyKeyPathComponent(SILModule &M, require(substEqualsType->getResults()[0].getConvention() == ResultConvention::Unowned, "result should be unowned"); - require(substEqualsType->getResults()[0].getType()->getAnyNominal() + require(substEqualsType->getResults()[0].getInterfaceType() + ->getAnyNominal() == C.getBoolDecl(), "result should be Bool"); } @@ -185,7 +187,7 @@ void verifyKeyPathComponent(SILModule &M, "operator"); auto substHashType = hash->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substHashType->getParameters().size() == 1, "must have two arguments"); @@ -193,7 +195,7 @@ void verifyKeyPathComponent(SILModule &M, require(param.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); - require(param.getType()->getAnyNominal() + require(param.getInterfaceType()->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); @@ -203,7 +205,8 @@ void verifyKeyPathComponent(SILModule &M, require(substHashType->getResults()[0].getConvention() == ResultConvention::Unowned, "result should be unowned"); - require(substHashType->getResults()[0].getType()->getAnyNominal() + require(substHashType->getResults()[0].getInterfaceType() + ->getAnyNominal() == C.getIntDecl(), "result should be Int"); } @@ -232,7 +235,8 @@ void verifyKeyPathComponent(SILModule &M, require(property->hasStorage(), "property must be stored"); require(!property->isResilient(M.getSwiftModule(), expansion), "cannot access storage of resilient property"); - auto propertyTy = loweredBaseTy.getFieldType(property, M); + auto propertyTy = + loweredBaseTy.getFieldType(property, M, typeExpansionContext); require(propertyTy.getObjectType() == loweredComponentTy.getObjectType(), "component type should match the maximal abstraction of the " @@ -267,8 +271,8 @@ void verifyKeyPathComponent(SILModule &M, "less visible getters"); } - auto substGetterType = getter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + auto substGetterType = getter->getLoweredFunctionType()->substGenericArgs( + M, patternSubs, TypeExpansionContext::minimal()); require(substGetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, "getter should be a thin function"); @@ -278,7 +282,8 @@ void verifyKeyPathComponent(SILModule &M, auto baseParam = substGetterType->getParameters()[0]; require(baseParam.getConvention() == normalArgConvention, "getter base parameter should have normal arg convention"); - require(baseParam.getType() == loweredBaseTy.getASTType(), + require(baseParam.getArgumentType(M, substGetterType) + == loweredBaseTy.getASTType(), "getter base parameter should match base of component"); if (hasIndices) { @@ -286,7 +291,7 @@ void verifyKeyPathComponent(SILModule &M, require(indicesParam.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); - require(indicesParam.getType()->getAnyNominal() + require(indicesParam.getArgumentType(M, substGetterType)->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } @@ -296,7 +301,8 @@ void verifyKeyPathComponent(SILModule &M, auto result = substGetterType->getResults()[0]; require(result.getConvention() == ResultConvention::Indirect, "getter result should be @out"); - require(result.getType() == loweredComponentTy.getASTType(), + require(result.getReturnValueType(M, substGetterType) + == loweredComponentTy.getASTType(), "getter result should match the maximal abstraction of the " "formal component type"); } @@ -313,7 +319,7 @@ void verifyKeyPathComponent(SILModule &M, } auto substSetterType = setter->getLoweredFunctionType() - ->substGenericArgs(M, patternSubs); + ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substSetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, @@ -340,12 +346,12 @@ void verifyKeyPathComponent(SILModule &M, require(indicesParam.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); - require(indicesParam.getType()->getAnyNominal() + require(indicesParam.getArgumentType(M, substSetterType)->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } - require(newValueParam.getType() == + require(newValueParam.getArgumentType(M, substSetterType) == loweredComponentTy.getASTType(), "setter value should match the maximal abstraction of the " "formal component type"); @@ -362,8 +368,9 @@ void verifyKeyPathComponent(SILModule &M, require(contextType == operands[opIndex].get()->getType(), "operand must match type required by pattern"); SILType loweredType = index.LoweredType; - require(loweredType.isLoweringOf(M, index.FormalType), - "pattern index formal type doesn't match lowered type"); + require( + loweredType.isLoweringOf(typeExpansionContext, M, index.FormalType), + "pattern index formal type doesn't match lowered type"); } checkIndexEqualsAndHash(); @@ -764,7 +771,7 @@ class SILVerifier : public SILVerifierBase { void requireABICompatibleFunctionTypes(CanSILFunctionType type1, CanSILFunctionType type2, const Twine &what, - SILFunction *inFunction = nullptr) { + SILFunction &inFunction) { auto complain = [=](const char *msg) -> std::function { return [=]{ llvm::dbgs() << " " << msg << '\n' @@ -1297,7 +1304,7 @@ class SILVerifier : public SILVerifierBase { } // Apply the substitutions. - return fnTy->substGenericArgs(F.getModule(), subs); + return fnTy->substGenericArgs(F.getModule(), subs, F.getTypeExpansionContext()); } /// Check that for each opened archetype or dynamic self type in substitutions @@ -1571,8 +1578,9 @@ class SILVerifier : public SILVerifierBase { // lifetime-extending 'self'. if (expectedResult.getConvention() == ResultConvention::UnownedInnerPointer) { - expectedResult = SILResultInfo(expectedResult.getType(), - ResultConvention::Unowned); + expectedResult = SILResultInfo( + expectedResult.getReturnValueType(F.getModule(), substTy), + ResultConvention::Unowned); require(originalResult == expectedResult, "result type of result function type for partially applied " "@unowned_inner_pointer function should have @unowned" @@ -1583,8 +1591,9 @@ class SILVerifier : public SILVerifierBase { // retaining the return value. } else if (expectedResult.getConvention() == ResultConvention::Autoreleased) { - expectedResult = SILResultInfo(expectedResult.getType(), - ResultConvention::Owned); + expectedResult = SILResultInfo( + expectedResult.getReturnValueType(F.getModule(), substTy), + ResultConvention::Owned); require(originalResult == expectedResult, "result type of result function type for partially applied " "@autoreleased function should have @owned convention"); @@ -1737,7 +1746,8 @@ class SILVerifier : public SILVerifierBase { void checkGlobalAccessInst(GlobalAccessInst *GAI) { SILGlobalVariable *RefG = GAI->getReferencedGlobal(); - require(GAI->getType().getObjectType() == RefG->getLoweredType(), + require(GAI->getType().getObjectType() == + RefG->getLoweredTypeInContext(F.getTypeExpansionContext()), "global_addr/value must be the type of the variable it references"); if (auto *VD = RefG->getDecl()) { require(!VD->isResilient(F.getModule().getSwiftModule(), @@ -1875,9 +1885,17 @@ class SILVerifier : public SILVerifierBase { // Unsafe enforcement is used for some unrecognizable access patterns, // like debugger variables. The compiler never cares about the source of // those accesses. + findAccessedStorage(BAI->getSource()); + // FIXME: rdar://57291811 - the following check for valid storage will be + // reenabled shortly. A fix is planned. In the meantime, the possiblity that + // a real miscompilation could be caused by this failure is insignificant. + // I will probably enable a much broader SILVerification of address-type + // block arguments first to ensure we never hit this check again. + /* AccessedStorage storage = findAccessedStorage(BAI->getSource()); if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe) require(storage, "Unknown formal access pattern"); + */ } void checkEndAccessInst(EndAccessInst *EAI) { @@ -1966,7 +1984,7 @@ class SILVerifier : public SILVerifierBase { "Inst with qualified ownership in a function that is not qualified"); SILValue Src = SI->getSrc(); require(Src->getType().isTrivial(*SI->getFunction()) || - Src.getOwnershipKind() == ValueOwnershipKind::Any, + Src.getOwnershipKind() == ValueOwnershipKind::None, "A store with trivial ownership must store a type with trivial " "ownership"); break; @@ -2132,7 +2150,7 @@ class SILVerifier : public SILVerifierBase { require(!F.hasOwnership(), \ #name "_release is only in functions with unqualified ownership"); \ } \ - void checkCopy##Name##ValueInst(Copy##Name##ValueInst *I) { \ + void StrongcheckCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ auto ty = requireObjectType(Name##StorageType, I->getOperand(), \ "Operand of " #name "_retain"); \ closure(); \ @@ -2153,7 +2171,7 @@ class SILVerifier : public SILVerifierBase { }) #define UNCHECKED_REF_STORAGE(Name, name, ...) \ LOADABLE_REF_STORAGE_HELPER(Name, name) \ - void checkCopy##Name##ValueInst(Copy##Name##ValueInst *I) { \ + void checkStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ auto ty = requireObjectType(Name##StorageType, I->getOperand(), \ "Operand of " #name "_retain"); \ (void)ty; \ @@ -2188,10 +2206,6 @@ class SILVerifier : public SILVerifierBase { // outside by the allocating initializer and we pass in the to be // initialized value as a SILArgument. || isa(Src) - // FIXME: Once the MarkUninitializedFixup pass is eliminated, - // mark_uninitialized should never be applied to a project_box. So - // at that point, this should be eliminated. - || isa(Src) // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new @@ -2318,7 +2332,8 @@ class SILVerifier : public SILVerifierBase { "project_box operand should be a value"); auto boxTy = I->getOperand()->getType().getAs(); require(boxTy, "project_box operand should be a @box type"); - require(I->getType() == getSILBoxFieldType(boxTy, F.getModule().Types, + require(I->getType() == getSILBoxFieldType(F.getTypeExpansionContext(), boxTy, + F.getModule().Types, I->getFieldIndex()), "project_box result should be address of boxed type"); @@ -2387,7 +2402,8 @@ class SILVerifier : public SILVerifierBase { "number of struct operands does not match number of stored " "member variables of struct"); - SILType loweredType = structTy.getFieldType(field, F.getModule()); + SILType loweredType = + structTy.getFieldType(field, F.getModule(), F.getTypeExpansionContext()); if (SI->getModule().getStage() != SILStage::Lowered) { require((*opi)->getType() == loweredType, "struct operand type does not match field type"); @@ -2409,8 +2425,8 @@ class SILVerifier : public SILVerifierBase { if (UI->getElement()->hasAssociatedValues()) { require(UI->getOperand()->getType().isObject(), "EnumInst operand must be an object"); - SILType caseTy = UI->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getOperand()->getType(), "EnumInst operand type does not match type of case"); @@ -2430,9 +2446,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isAddress(), "InitEnumDataAddrInst must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType( @@ -2453,9 +2468,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isObject(), "UncheckedEnumData must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getType(), @@ -2475,9 +2489,8 @@ class SILVerifier : public SILVerifierBase { require(UI->getType().isAddress(), "UncheckedTakeEnumDataAddrInst must produce an address"); - SILType caseTy = - UI->getOperand()->getType().getEnumElementType(UI->getElement(), - F.getModule()); + SILType caseTy = UI->getOperand()->getType().getEnumElementType( + UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { require(caseTy == UI->getType(), "UncheckedTakeEnumDataAddrInst result " @@ -2513,7 +2526,8 @@ class SILVerifier : public SILVerifierBase { // Is a SIL type a potential lowering of a formal type? bool isLoweringOf(SILType loweredType, CanType formalType) { - return loweredType.isLoweringOf(F.getModule(), formalType); + return loweredType.isLoweringOf(F.getTypeExpansionContext(), F.getModule(), + formalType); } void checkMetatypeInst(MetatypeInst *MI) { @@ -2603,9 +2617,9 @@ class SILVerifier : public SILVerifierBase { require(AI->getType().isObject(), "result of alloc_box must be an object"); for (unsigned field : indices(AI->getBoxType()->getLayout()->getFields())) { - verifyOpenedArchetype(AI, - getSILBoxFieldLoweredType(AI->getBoxType(), F.getModule().Types, - field)); + verifyOpenedArchetype(AI, getSILBoxFieldLoweredType( + F.getTypeExpansionContext(), AI->getBoxType(), + F.getModule().Types, field)); } // An alloc_box with a mark_uninitialized user can not have any other users. @@ -2699,8 +2713,8 @@ class SILVerifier : public SILVerifierBase { "struct_extract field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of struct_extract does not match type of field"); } @@ -2745,8 +2759,8 @@ class SILVerifier : public SILVerifierBase { "struct_element_addr field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of struct_element_addr does not match type of field"); } @@ -2771,8 +2785,8 @@ class SILVerifier : public SILVerifierBase { "ref_element_addr field must be a member of the class"); if (EI->getModule().getStage() != SILStage::Lowered) { - SILType loweredFieldTy = - operandTy.getFieldType(EI->getField(), F.getModule()); + SILType loweredFieldTy = operandTy.getFieldType( + EI->getField(), F.getModule(), F.getTypeExpansionContext()); require(loweredFieldTy == EI->getType(), "result of ref_element_addr does not match type of field"); } @@ -2821,7 +2835,7 @@ class SILVerifier : public SILVerifierBase { require(methodType->isPolymorphic(), "result of witness_method must be polymorphic"); - auto genericSig = methodType->getGenericSignature(); + auto genericSig = methodType->getInvocationGenericSignature(); auto selfGenericParam = genericSig->getGenericParams()[0]; require(selfGenericParam->getDepth() == 0 @@ -2870,7 +2884,8 @@ class SILVerifier : public SILVerifierBase { // The type of the dynamic method must match the usual type of the method, // but with the more opaque Self type. - auto constantInfo = F.getModule().Types.getConstantInfo(method); + auto constantInfo = + F.getModule().Types.getConstantInfo(F.getTypeExpansionContext(), method); auto methodTy = constantInfo.SILFnType; assert(!methodTy->isCoroutine()); @@ -2878,7 +2893,8 @@ class SILVerifier : public SILVerifierBase { // Map interface types to archetypes. if (auto *env = F.getModule().Types.getConstantGenericEnvironment(method)) { auto subs = env->getForwardingSubstitutionMap(); - methodTy = methodTy->substGenericArgs(F.getModule(), subs); + methodTy = methodTy->substGenericArgs(F.getModule(), subs, + F.getTypeExpansionContext()); } assert(!methodTy->isPolymorphic()); @@ -2898,7 +2914,8 @@ class SILVerifier : public SILVerifierBase { auto anyObjectTy = C.getAnyObjectType(); for (auto &dynResult : dynResults) { auto newResultTy - = dynResult.getType()->replaceCovariantResultType(anyObjectTy, 0); + = dynResult.getReturnValueType(F.getModule(), methodTy) + ->replaceCovariantResultType(anyObjectTy, 0); dynResult = SILResultInfo(newResultTy->getCanonicalType(), dynResult.getConvention()); } @@ -2913,6 +2930,7 @@ class SILVerifier : public SILVerifierBase { methodTy->getYields(), dynResults, methodTy->getOptionalErrorResult(), + SubstitutionMap(), false, F.getASTContext()); return SILType::getPrimitiveObjectType(fnTy); } @@ -2946,7 +2964,8 @@ class SILVerifier : public SILVerifierBase { void checkClassMethodInst(ClassMethodInst *CMI) { auto member = CMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -2974,7 +2993,8 @@ class SILVerifier : public SILVerifierBase { void checkSuperMethodInst(SuperMethodInst *CMI) { auto member = CMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -3025,7 +3045,8 @@ class SILVerifier : public SILVerifierBase { operandInstanceType = metatypeType.getInstanceType(); if (operandInstanceType.getClassOrBoundGenericClass()) { - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of objc_method must match abstracted type of method"); @@ -3050,7 +3071,8 @@ class SILVerifier : public SILVerifierBase { void checkObjCSuperMethodInst(ObjCSuperMethodInst *OMI) { auto member = OMI->getMember(); - auto overrideTy = TC.getConstantOverrideType(member); + auto overrideTy = + TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (OMI->getModule().getStage() != SILStage::Lowered) { requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), @@ -3535,14 +3557,14 @@ class SILVerifier : public SILVerifierBase { void checkCheckedCastBranchInst(CheckedCastBranchInst *CBI) { verifyCheckedCast(CBI->isExact(), - CBI->getOperand()->getType(), - CBI->getCastType()); - verifyOpenedArchetype(CBI, CBI->getCastType().getASTType()); + CBI->getSourceLoweredType(), + CBI->getTargetLoweredType()); + verifyOpenedArchetype(CBI, CBI->getTargetFormalType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_br must take one argument"); require(CBI->getSuccessBB()->args_begin()[0]->getType() == - CBI->getCastType(), + CBI->getTargetLoweredType(), "success dest block argument of checked_cast_br must match type of " "cast"); require(!F.hasOwnership() || CBI->getFailureBB()->args_size() == 1, @@ -3559,14 +3581,16 @@ class SILVerifier : public SILVerifierBase { } void checkCheckedCastValueBranchInst(CheckedCastValueBranchInst *CBI) { - verifyCheckedCast(false, CBI->getOperand()->getType(), CBI->getCastType(), + verifyCheckedCast(false, + CBI->getSourceLoweredType(), + CBI->getTargetLoweredType(), true); - verifyOpenedArchetype(CBI, CBI->getCastType().getASTType()); + verifyOpenedArchetype(CBI, CBI->getTargetFormalType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_value_br must take one argument"); require(CBI->getSuccessBB()->args_begin()[0]->getType() == - CBI->getCastType(), + CBI->getTargetLoweredType(), "success dest block argument of checked_cast_value_br must match " "type of cast"); require(F.hasOwnership() || CBI->getFailureBB()->args_empty(), @@ -3826,7 +3850,7 @@ class SILVerifier : public SILVerifierBase { // convert_function is required to be an ABI-compatible conversion. requireABICompatibleFunctionTypes( opTI, resTI, "convert_function cannot change function ABI", - ICI->getFunction()); + *ICI->getFunction()); } void checkConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *ICI) { @@ -3843,7 +3867,7 @@ class SILVerifier : public SILVerifierBase { requireABICompatibleFunctionTypes( opTI, resTI->getWithExtInfo(resTI->getExtInfo().withNoEscape(false)), "convert_escape_to_noescape cannot change function ABI", - ICI->getFunction()); + *ICI->getFunction()); // After mandatory passes convert_escape_to_noescape should not have the // '[not_guaranteed]' or '[escaped]' attributes. @@ -3893,7 +3917,9 @@ class SILVerifier : public SILVerifierBase { LLVM_DEBUG(RI->print(llvm::dbgs())); SILType functionResultType = - F.mapTypeIntoContext(fnConv.getSILResultType()); + F.getLoweredType( + F.mapTypeIntoContext(fnConv.getSILResultType()).getASTType()) + .getCategoryType(fnConv.getSILResultType().getCategory()); SILType instResultType = RI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function return type: "; functionResultType.dump(); @@ -3906,11 +3932,15 @@ class SILVerifier : public SILVerifierBase { void checkThrowInst(ThrowInst *TI) { LLVM_DEBUG(TI->print(llvm::dbgs())); - CanSILFunctionType fnType = F.getLoweredFunctionType(); + CanSILFunctionType fnType = + F.getLoweredFunctionTypeInContext(F.getTypeExpansionContext()); require(fnType->hasErrorResult(), "throw in function that doesn't have an error result"); - SILType functionResultType = F.mapTypeIntoContext(fnConv.getSILErrorType()); + SILType functionResultType = + F.getLoweredType( + F.mapTypeIntoContext(fnConv.getSILErrorType()).getASTType()) + .getCategoryType(fnConv.getSILErrorType().getCategory()); SILType instResultType = TI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function error result type: "; functionResultType.dump(); @@ -3926,7 +3956,8 @@ class SILVerifier : public SILVerifierBase { } void checkYieldInst(YieldInst *YI) { - CanSILFunctionType fnType = F.getLoweredFunctionType(); + CanSILFunctionType fnType = + F.getLoweredFunctionTypeInContext(F.getTypeExpansionContext()); require(fnType->isCoroutine(), "yield in non-coroutine function"); @@ -4126,7 +4157,8 @@ class SILVerifier : public SILVerifierBase { } if (dest->getArguments().size() == 1) { - SILType eltArgTy = uTy.getEnumElementType(elt, F.getModule()); + SILType eltArgTy = uTy.getEnumElementType(elt, F.getModule(), + F.getTypeExpansionContext()); SILType bbArgTy = dest->getArguments()[0]->getType(); if (F.getModule().getStage() != SILStage::Lowered) { // During the lowered stage, a function type might have different @@ -4351,7 +4383,7 @@ class SILVerifier : public SILVerifierBase { "invoke function operand must be a c function"); require(invokeTy->getParameters().size() >= 1, "invoke function must take at least one parameter"); - require(!invokeTy->getGenericSignature() || + require(!invokeTy->getInvocationGenericSignature() || invokeTy->getExtInfo().isPseudogeneric(), "invoke function must not take reified generic parameters"); @@ -4363,7 +4395,7 @@ class SILVerifier : public SILVerifierBase { ParameterConvention::Indirect_InoutAliasable, "invoke function must take block storage as @inout_aliasable " "parameter"); - require(storageParam.getType() == storageTy, + require(storageParam.getArgumentType(F.getModule(), invokeTy) == storageTy, "invoke function must take block storage type as first parameter"); require(IBSHI->getType().isObject(), "result must be a value"); @@ -4450,9 +4482,6 @@ class SILVerifier : public SILVerifierBase { "keypath value type should match value type of keypath pattern"); { - Lowering::GenericContextScope scope(F.getModule().Types, - pattern->getGenericSignature()); - for (auto &component : pattern->getComponents()) { bool hasIndices; switch (component.getKind()) { @@ -4532,7 +4561,9 @@ class SILVerifier : public SILVerifierBase { auto mappedTy = F.mapTypeIntoContext(ty); SILArgument *bbarg = *argI; ++argI; - if (bbarg->getType() != mappedTy) { + if (bbarg->getType() != mappedTy && + bbarg->getType() != F.getLoweredType(mappedTy.getASTType()) + .getCategoryType(mappedTy.getCategory())) { llvm::errs() << what << " type mismatch!\n"; llvm::errs() << " argument: "; bbarg->dump(); llvm::errs() << " expected: "; mappedTy.dump(); @@ -4839,7 +4870,7 @@ class SILVerifier : public SILVerifierBase { llvm::all_of(CBI->getTrueArgs(), [](SILValue V) -> bool { return V.getOwnershipKind() == - ValueOwnershipKind::Any; + ValueOwnershipKind::None; }), "cond_br with critical edges must not have a non-trivial value"); } @@ -4848,7 +4879,7 @@ class SILVerifier : public SILVerifierBase { llvm::all_of(CBI->getFalseArgs(), [](SILValue V) -> bool { return V.getOwnershipKind() == - ValueOwnershipKind::Any; + ValueOwnershipKind::None; }), "cond_br with critical edges must not have a non-trivial value"); } @@ -5099,8 +5130,7 @@ void SILProperty::verify(const SILModule &M) const { hasIndices = subscript->getIndices()->size() != 0; } - auto canSig = sig ? sig->getCanonicalSignature() : nullptr; - Lowering::GenericContextScope scope(M.Types, canSig); + auto canSig = sig.getCanonicalSignature(); auto require = [&](bool reqt, StringRef message) { if (!reqt) { @@ -5137,7 +5167,8 @@ void SILVTable::verify(const SILModule &M) const { for (auto &entry : getEntries()) { // All vtable entries must be decls in a class context. assert(entry.Method.hasDecl() && "vtable entry is not a decl"); - auto baseInfo = M.Types.getConstantInfo(entry.Method); + auto baseInfo = + M.Types.getConstantInfo(TypeExpansionContext::minimal(), entry.Method); ValueDecl *decl = entry.Method.getDecl(); assert((!isa(decl) @@ -5175,7 +5206,8 @@ void SILVTable::verify(const SILModule &M) const { .requireABICompatibleFunctionTypes( baseInfo.getSILType().castTo(), entry.Implementation->getLoweredFunctionType(), - "vtable entry for " + baseName + " must be ABI-compatible"); + "vtable entry for " + baseName + " must be ABI-compatible", + *entry.Implementation); } } } diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index ce42ab8dac01a..5566950fb6acd 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -24,9 +24,11 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/PrettyStackTrace.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILModule.h" @@ -90,7 +92,7 @@ static bool hasSingletonMetatype(CanType instanceType) { } CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture, - ResilienceExpansion expansion) { + TypeExpansionContext expansion) { auto decl = capture.getDecl(); auto *var = cast(decl); assert(var->hasStorage() && @@ -101,7 +103,11 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture, // by its address (like a var) instead. if (!var->supportsMutation() && (Context.LangOpts.EnableSILOpaqueValues || - !getTypeLowering(var->getType(), expansion).isAddressOnly())) + !getTypeLowering( + var->getType(), + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( + expansion.getResilienceExpansion())) + .isAddressOnly())) return CaptureKind::Constant; // In-out parameters are captured by address. @@ -129,22 +135,22 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture, using RecursiveProperties = TypeLowering::RecursiveProperties; static RecursiveProperties -classifyType(CanType type, TypeConverter &TC, CanGenericSignature sig, - ResilienceExpansion expansion); +classifyType(AbstractionPattern origType, CanType type, + TypeConverter &TC, TypeExpansionContext expansion); namespace { /// A CRTP helper class for doing things that depends on type /// classification. template - class TypeClassifierBase : public CanTypeVisitor { + class TypeClassifierBase + : public CanTypeVisitor + { Impl &asImpl() { return *static_cast(this); } protected: TypeConverter &TC; - CanGenericSignature Sig; - ResilienceExpansion Expansion; - TypeClassifierBase(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion) - : TC(TC), Sig(Sig), Expansion(Expansion) {} + TypeExpansionContext Expansion; + TypeClassifierBase(TypeConverter &TC, TypeExpansionContext Expansion) + : TC(TC), Expansion(Expansion) {} public: // The subclass should implement: @@ -183,9 +189,9 @@ namespace { return asImpl().handle(type, RecursiveProperties::forReference()); } -#define IMPL(TYPE, LOWERING) \ - RetTy visit##TYPE##Type(Can##TYPE##Type type) { \ - return asImpl().handle##LOWERING(type); \ +#define IMPL(TYPE, LOWERING) \ + RetTy visit##TYPE##Type(Can##TYPE##Type type, AbstractionPattern orig) { \ + return asImpl().handle##LOWERING(type); \ } IMPL(BuiltinInteger, Trivial) @@ -204,12 +210,14 @@ namespace { #undef IMPL RetTy visitBuiltinUnsafeValueBufferType( - CanBuiltinUnsafeValueBufferType type) { + CanBuiltinUnsafeValueBufferType type, + AbstractionPattern origType) { return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, IsNotResilient}); } - RetTy visitAnyFunctionType(CanAnyFunctionType type) { + RetTy visitAnyFunctionType(CanAnyFunctionType type, + AbstractionPattern origType) { switch (type->getRepresentation()) { case AnyFunctionType::Representation::Swift: case AnyFunctionType::Representation::Block: @@ -221,7 +229,8 @@ namespace { llvm_unreachable("bad function representation"); } - RetTy visitSILFunctionType(CanSILFunctionType type) { + RetTy visitSILFunctionType(CanSILFunctionType type, + AbstractionPattern origType) { // Only escaping closures are references. bool isSwiftEscaping = type->getExtInfo().isNoEscape() && type->getExtInfo().getRepresentation() == @@ -232,67 +241,52 @@ namespace { return asImpl().handleTrivial(type); } - RetTy visitLValueType(CanLValueType type) { + RetTy visitLValueType(CanLValueType type, + AbstractionPattern origType) { llvm_unreachable("shouldn't get an l-value type here"); } - RetTy visitInOutType(CanInOutType type) { + RetTy visitInOutType(CanInOutType type, + AbstractionPattern origType) { llvm_unreachable("shouldn't get an inout type here"); } - RetTy visitErrorType(CanErrorType type) { + RetTy visitErrorType(CanErrorType type, + AbstractionPattern origType) { return asImpl().handleTrivial(type); } - // Dependent types should be contextualized before visiting. - - CanGenericSignature getGenericSignature() { - if (Sig) - return Sig; - return TC.getCurGenericContext(); - } + // Dependent types can be lowered according to their corresponding + // abstraction pattern. - RetTy visitAbstractTypeParamType(CanType type) { - if (auto genericSig = getGenericSignature()) { - if (genericSig->requiresClass(type)) { + RetTy visitAbstractTypeParamType(CanType type, + AbstractionPattern origType) { + if (origType.isTypeParameterOrOpaqueArchetype()) { + if (origType.requiresClass()) { return asImpl().handleReference(type); - } else if (genericSig->isConcreteType(type)) { - return asImpl().visit(genericSig->getConcreteType(type) - ->getCanonicalType()); } else { return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque()); } + } else { + // If the abstraction pattern provides a concrete type, lower as that + // type. This can occur if the abstraction pattern provides a more + // constrained generic signature with more same-type constraints than + // the original declaration whose type we're lowering. + return asImpl().visit(origType.getType(), origType); } - llvm_unreachable("should have substituted dependent type into context"); - } - RetTy visitGenericTypeParamType(CanGenericTypeParamType type) { - return visitAbstractTypeParamType(type); + RetTy visitGenericTypeParamType(CanGenericTypeParamType type, + AbstractionPattern origType) { + return visitAbstractTypeParamType(type, origType); } - RetTy visitDependentMemberType(CanDependentMemberType type) { - return visitAbstractTypeParamType(type); + RetTy visitDependentMemberType(CanDependentMemberType type, + AbstractionPattern origType) { + return visitAbstractTypeParamType(type, origType); } Type getConcreteReferenceStorageReferent(Type type) { if (type->isTypeParameter()) { - auto signature = getGenericSignature(); - assert(signature && "dependent type without generic signature?!"); - - if (auto concreteType = signature->getConcreteType(type)) - return concreteType->getCanonicalType(); - - assert(signature->requiresClass(type)); - - // If we have a superclass bound, recurse on that. This should - // always terminate: even if we allow - // - // at some point the type-checker should prove acyclic-ness. - auto bound = signature->getSuperclassBound(type); - if (bound) { - return getConcreteReferenceStorageReferent(bound->getCanonicalType()); - } - return TC.Context.getAnyObjectType(); } @@ -300,43 +294,58 @@ namespace { } #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - RetTy visit##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visit##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ IsNotResilient}); \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - RetTy visit##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visit##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ return asImpl().handleReference(type); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - RetTy visitLoadable##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visitLoadable##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ return asImpl().handleReference(type); \ } \ - RetTy visitAddressOnly##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visitAddressOnly##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ return asImpl().handleAddressOnly(type, {IsNotTrivial, \ IsFixedABI, \ IsAddressOnly, \ IsNotResilient}); \ } \ - RetTy visit##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visit##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ auto referentType = type->getReferentType(); \ auto concreteType = getConcreteReferenceStorageReferent(referentType); \ if (Name##StorageType::get(concreteType, TC.Context) \ - ->isLoadable(Expansion)) { \ - return asImpl().visitLoadable##Name##StorageType(type); \ + ->isLoadable(Expansion.getResilienceExpansion())) { \ + return asImpl().visitLoadable##Name##StorageType(type, origType); \ } else { \ - return asImpl().visitAddressOnly##Name##StorageType(type); \ + return asImpl().visitAddressOnly##Name##StorageType(type, origType); \ } \ } #define UNCHECKED_REF_STORAGE(Name, ...) \ - RetTy visit##Name##StorageType(Can##Name##StorageType type) { \ + RetTy visit##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ return asImpl().handleTrivial(type); \ } #include "swift/AST/ReferenceStorage.def" - RetTy visitArchetypeType(CanArchetypeType type) { + RetTy visitOpaqueTypeArchetypeType(CanOpaqueTypeArchetypeType ty, + AbstractionPattern origType) { + auto replacedTy = substOpaqueTypesWithUnderlyingTypes(ty, Expansion); + if (replacedTy == ty) + return visitArchetypeType(ty, origType); + return this->visit(replacedTy, origType); + } + + RetTy visitArchetypeType(CanArchetypeType type, + AbstractionPattern origType) { if (type->requiresClass()) { return asImpl().handleReference(type); } @@ -359,7 +368,8 @@ namespace { return asImpl().handleAddressOnly(type, RecursiveProperties::forOpaque()); } - RetTy visitExistentialType(CanType type) { + RetTy visitExistentialType(CanType type, + AbstractionPattern origType) { switch (SILType::getPrimitiveObjectType(type) .getPreferredExistentialRepresentation()) { case ExistentialRepresentation::None: @@ -381,43 +391,54 @@ namespace { llvm_unreachable("Unhandled ExistentialRepresentation in switch."); } - RetTy visitProtocolType(CanProtocolType type) { - return visitExistentialType(type); + RetTy visitProtocolType(CanProtocolType type, + AbstractionPattern origType) { + return visitExistentialType(type, origType); } - RetTy visitProtocolCompositionType(CanProtocolCompositionType type) { - return visitExistentialType(type); + RetTy visitProtocolCompositionType(CanProtocolCompositionType type, + AbstractionPattern origType) { + return visitExistentialType(type, origType); } // Enums depend on their enumerators. - RetTy visitEnumType(CanEnumType type) { - return asImpl().visitAnyEnumType(type, type->getDecl()); + RetTy visitEnumType(CanEnumType type, + AbstractionPattern origType) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl()); } - RetTy visitBoundGenericEnumType(CanBoundGenericEnumType type) { - return asImpl().visitAnyEnumType(type, type->getDecl()); + RetTy visitBoundGenericEnumType(CanBoundGenericEnumType type, + AbstractionPattern origType) { + return asImpl().visitAnyEnumType(type, origType, type->getDecl()); } // Structs depend on their physical fields. - RetTy visitStructType(CanStructType type) { - return asImpl().visitAnyStructType(type, type->getDecl()); + RetTy visitStructType(CanStructType type, + AbstractionPattern origType) { + return asImpl().visitAnyStructType(type, origType, type->getDecl()); } - RetTy visitBoundGenericStructType(CanBoundGenericStructType type) { - return asImpl().visitAnyStructType(type, type->getDecl()); + RetTy visitBoundGenericStructType(CanBoundGenericStructType type, + AbstractionPattern origType) { + return asImpl().visitAnyStructType(type, origType, type->getDecl()); } // Tuples depend on their elements. - RetTy visitTupleType(CanTupleType type) { + RetTy visitTupleType(CanTupleType type, + AbstractionPattern origType) { RecursiveProperties props; - for (auto eltType : type.getElementTypes()) { - props.addSubobject(classifyType(eltType, TC, Sig, Expansion)); + for (unsigned i = 0, e = type->getNumElements(); i < e; ++i) { + props.addSubobject(classifyType(origType.getTupleElementType(i), + type.getElementType(i), + TC, Expansion)); } return asImpl().handleAggregateByProperties(type, props); } - RetTy visitDynamicSelfType(CanDynamicSelfType type) { - return this->visit(type.getSelfType()); + RetTy visitDynamicSelfType(CanDynamicSelfType type, + AbstractionPattern origType) { + return this->visit(type.getSelfType(), origType); } - RetTy visitSILBlockStorageType(CanSILBlockStorageType type) { + RetTy visitSILBlockStorageType(CanSILBlockStorageType type, + AbstractionPattern origType) { // Should not be loaded. return asImpl().handleAddressOnly(type, {IsNotTrivial, IsFixedABI, @@ -425,7 +446,8 @@ namespace { IsNotResilient}); } - RetTy visitSILBoxType(CanSILBoxType type) { + RetTy visitSILBoxType(CanSILBoxType type, + AbstractionPattern origType) { // Should not be loaded. return asImpl().handleReference(type); } @@ -445,52 +467,39 @@ namespace { class TypeClassifier : public TypeClassifierBase { public: - TypeClassifier(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion) - : TypeClassifierBase(TC, Sig, Expansion) {} + TypeClassifier(TypeConverter &TC, + TypeExpansionContext Expansion) + : TypeClassifierBase(TC, Expansion) {} RecursiveProperties handle(CanType type, RecursiveProperties properties) { return properties; } - RecursiveProperties visitAnyEnumType(CanType type, EnumDecl *D) { + RecursiveProperties visitAnyEnumType(CanType type, + AbstractionPattern origType, + EnumDecl *D) { // We have to look through optionals here without grabbing the // type lowering because the way that optionals are reabstracted // can trip recursion checks if we try to build a lowered type. if (D->isOptionalDecl()) { - return visit(type.getOptionalObjectType()); + return visit(type.getOptionalObjectType(), + origType.getOptionalObjectType()); } // Consult the type lowering. - type = getSubstitutedTypeForTypeLowering(type); - auto &lowering = TC.getTypeLowering(type, Expansion); + auto &lowering = TC.getTypeLowering(origType, type, Expansion); return handleClassificationFromLowering(type, lowering); } - RecursiveProperties visitAnyStructType(CanType type, StructDecl *D) { + RecursiveProperties visitAnyStructType(CanType type, + AbstractionPattern origType, + StructDecl *D) { // Consult the type lowering. - type = getSubstitutedTypeForTypeLowering(type); - auto &lowering = TC.getTypeLowering(type, Expansion); + auto &lowering = TC.getTypeLowering(origType, type, Expansion); return handleClassificationFromLowering(type, lowering); } private: - CanType getSubstitutedTypeForTypeLowering(CanType type) { - // If we're using a generic signature different from - // TC.getCurGenericContext(), we have to map the - // type into context before asking for a type lowering - // because the rest of type lowering doesn't have a generic - // signature plumbed through. - if (Sig && type->hasTypeParameter()) { - type = Sig->getCanonicalSignature() - ->getGenericEnvironment() - ->mapTypeIntoContext(type) - ->getCanonicalType(); - } - - return type; - } - RecursiveProperties handleClassificationFromLowering(CanType type, const TypeLowering &lowering) { return handle(type, lowering.getRecursiveProperties()); @@ -498,19 +507,22 @@ namespace { }; } // end anonymous namespace -static RecursiveProperties classifyType(CanType type, TypeConverter &tc, - CanGenericSignature sig, - ResilienceExpansion expansion) { - return TypeClassifier(tc, sig, expansion).visit(type); +static RecursiveProperties classifyType(AbstractionPattern origType, + CanType type, + TypeConverter &tc, + TypeExpansionContext expansion) { + return TypeClassifier(tc, expansion).visit(type, origType); } /// True if the type, or the referenced type of an address /// type, is address-only. For example, it could be a resilient struct or /// something of unknown size. -bool SILType::isAddressOnly(CanType type, TypeConverter &tc, +bool SILType::isAddressOnly(CanType type, + TypeConverter &tc, CanGenericSignature sig, - ResilienceExpansion expansion) { - return classifyType(type, tc, sig, expansion).isAddressOnly(); + TypeExpansionContext expansion) { + return classifyType(AbstractionPattern(sig, type), + type, tc, expansion).isAddressOnly(); } namespace { @@ -521,7 +533,7 @@ namespace { protected: LoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : TypeLowering(type, properties, isRefCounted, forExpansion) {} public: @@ -548,7 +560,7 @@ namespace { class TrivialTypeLowering final : public LoadableTypeLowering { public: TrivialTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableTypeLowering(type, properties, IsNotReferenceCounted, forExpansion) { assert(properties.isFixedABI()); @@ -618,7 +630,7 @@ namespace { NonTrivialLoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableTypeLowering(type, properties, isRefCounted, forExpansion) { assert(!properties.isTrivial()); } @@ -711,7 +723,7 @@ namespace { public: LoadableAggTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), properties, IsNotReferenceCounted, forExpansion) { @@ -724,9 +736,7 @@ namespace { if (Children.data() == nullptr) { SmallVector children; lowerChildren(TC, children); - auto isDependent = IsDependent_t(getLoweredType().hasTypeParameter()); - auto buf = operator new(sizeof(Child) * children.size(), TC, - isDependent); + auto buf = operator new(sizeof(Child) * children.size(), TC); memcpy(buf, children.data(), sizeof(Child) * children.size()); Children = {reinterpret_cast(buf), children.size()}; } @@ -830,7 +840,7 @@ namespace { : public LoadableAggTypeLowering { public: LoadableTupleTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableAggTypeLowering(type, properties, forExpansion) {} SILValue emitRValueProject(SILBuilder &B, SILLocation loc, @@ -855,7 +865,7 @@ namespace { unsigned index = 0; for (auto elt : tupleTy.getElementTypes()) { auto silElt = SILType::getPrimitiveType(elt, silTy.getCategory()); - auto &eltTL = TC.getTypeLowering(silElt, getResilienceExpansion()); + auto &eltTL = TC.getTypeLowering(silElt, getExpansionContext()); children.push_back(Child{index, eltTL}); ++index; } @@ -867,7 +877,7 @@ namespace { : public LoadableAggTypeLowering { public: LoadableStructTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LoadableAggTypeLowering(type, properties, forExpansion) {} SILValue emitRValueProject(SILBuilder &B, SILLocation loc, @@ -890,8 +900,8 @@ namespace { assert(structDecl); for (auto prop : structDecl->getStoredProperties()) { - SILType propTy = silTy.getFieldType(prop, TC); - auto &propTL = TC.getTypeLowering(propTy, getResilienceExpansion()); + SILType propTy = silTy.getFieldType(prop, TC, getExpansionContext()); + auto &propTL = TC.getTypeLowering(propTy, getExpansionContext()); children.push_back(Child{prop, propTL}); } } @@ -901,7 +911,7 @@ namespace { class LoadableEnumTypeLowering final : public NonTrivialLoadableTypeLowering { public: LoadableEnumTypeLowering(CanType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(SILType::getPrimitiveObjectType(type), properties, IsNotReferenceCounted, @@ -944,7 +954,7 @@ namespace { public: LeafLoadableTypeLowering(SILType type, RecursiveProperties properties, IsReferenceCounted_t isRefCounted, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : NonTrivialLoadableTypeLowering(type, properties, isRefCounted, forExpansion) {} @@ -964,7 +974,7 @@ namespace { /// loadable. class ReferenceTypeLowering : public LeafLoadableTypeLowering { public: - ReferenceTypeLowering(SILType type, ResilienceExpansion forExpansion) + ReferenceTypeLowering(SILType type, TypeExpansionContext forExpansion) : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), IsReferenceCounted, forExpansion) {} @@ -996,7 +1006,7 @@ namespace { class Loadable##Name##TypeLowering final : public LeafLoadableTypeLowering { \ public: \ Loadable##Name##TypeLowering(SILType type, \ - ResilienceExpansion forExpansion) \ + TypeExpansionContext forExpansion) \ : LeafLoadableTypeLowering(type, RecursiveProperties::forReference(), \ IsReferenceCounted, \ forExpansion) {} \ @@ -1022,7 +1032,7 @@ namespace { class AddressOnlyTypeLowering : public TypeLowering { public: AddressOnlyTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : TypeLowering(type, properties, IsNotReferenceCounted, forExpansion) { assert(properties.isAddressOnly()); @@ -1094,7 +1104,7 @@ namespace { class UnsafeValueBufferTypeLowering : public AddressOnlyTypeLowering { public: UnsafeValueBufferTypeLowering(SILType type, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : AddressOnlyTypeLowering(type, {IsNotTrivial, IsFixedABI, IsAddressOnly, IsNotResilient}, @@ -1125,7 +1135,7 @@ namespace { class OpaqueValueTypeLowering : public LeafLoadableTypeLowering { public: OpaqueValueTypeLowering(SILType type, RecursiveProperties properties, - ResilienceExpansion forExpansion) + TypeExpansionContext forExpansion) : LeafLoadableTypeLowering(type, properties, IsNotReferenceCounted, forExpansion) {} @@ -1164,11 +1174,9 @@ namespace { class LowerType : public TypeClassifierBase { - IsDependent_t Dependent; public: - LowerType(TypeConverter &TC, CanGenericSignature Sig, - ResilienceExpansion Expansion, IsDependent_t Dependent) - : TypeClassifierBase(TC, Sig, Expansion), Dependent(Dependent) {} + LowerType(TypeConverter &TC, TypeExpansionContext Expansion) + : TypeClassifierBase(TC, Expansion) {} TypeLowering *handleTrivial(CanType type) { return handleTrivial(type, RecursiveProperties::forTrivial()); @@ -1177,54 +1185,57 @@ namespace { TypeLowering *handleTrivial(CanType type, RecursiveProperties properties) { auto silType = SILType::getPrimitiveObjectType(type); - return new (TC, Dependent) TrivialTypeLowering(silType, properties, - Expansion); + return new (TC) TrivialTypeLowering(silType, properties, Expansion); } TypeLowering *handleReference(CanType type) { auto silType = SILType::getPrimitiveObjectType(type); - return new (TC, Dependent) ReferenceTypeLowering(silType, Expansion); + return new (TC) ReferenceTypeLowering(silType, Expansion); } TypeLowering *handleAddressOnly(CanType type, RecursiveProperties properties) { if (!TC.Context.LangOpts.EnableSILOpaqueValues) { auto silType = SILType::getPrimitiveAddressType(type); - return new (TC, Dependent) AddressOnlyTypeLowering(silType, properties, + return new (TC) AddressOnlyTypeLowering(silType, properties, Expansion); } auto silType = SILType::getPrimitiveObjectType(type); - return new (TC, Dependent) OpaqueValueTypeLowering(silType, properties, - Expansion); + return new (TC) OpaqueValueTypeLowering(silType, properties, Expansion); } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ - visit##Name##StorageType(Can##Name##StorageType type) { \ - return new (TC, Dependent) Loadable##Name##TypeLowering( \ + visit##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ + return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ Expansion); \ } #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ TypeLowering * \ - visitLoadable##Name##StorageType(Can##Name##StorageType type) { \ - return new (TC, Dependent) Loadable##Name##TypeLowering( \ + visitLoadable##Name##StorageType(Can##Name##StorageType type, \ + AbstractionPattern origType) { \ + return new (TC) Loadable##Name##TypeLowering( \ SILType::getPrimitiveObjectType(type), \ Expansion); \ } #include "swift/AST/ReferenceStorage.def" TypeLowering * - visitBuiltinUnsafeValueBufferType(CanBuiltinUnsafeValueBufferType type) { + visitBuiltinUnsafeValueBufferType(CanBuiltinUnsafeValueBufferType type, + AbstractionPattern origType) { auto silType = SILType::getPrimitiveAddressType(type); - return new (TC, Dependent) UnsafeValueBufferTypeLowering(silType, - Expansion); + return new (TC) UnsafeValueBufferTypeLowering(silType, Expansion); } - TypeLowering *visitTupleType(CanTupleType tupleType) { + TypeLowering *visitTupleType(CanTupleType tupleType, + AbstractionPattern origType) { RecursiveProperties properties; - for (auto eltType : tupleType.getElementTypes()) { - auto &lowering = TC.getTypeLowering(eltType, Expansion); + for (unsigned i = 0, e = tupleType->getNumElements(); i < e; ++i) { + auto eltType = tupleType.getElementType(i); + auto origEltType = origType.getTupleElementType(i); + auto &lowering = TC.getTypeLowering(origEltType, eltType, Expansion); properties.addSubobject(lowering.getRecursiveProperties()); } @@ -1247,7 +1258,8 @@ namespace { // Note: if the type is in a different module, the lowering does // not depend on the resilience expansion, so we do not need to set // the isResilent() flag above. - if (!sameModule || Expansion == ResilienceExpansion::Minimal) { + if (!sameModule || Expansion.getResilienceExpansion() == + ResilienceExpansion::Minimal) { properties.addSubobject(RecursiveProperties::forOpaque()); return true; } @@ -1256,7 +1268,9 @@ namespace { return false; } - TypeLowering *visitAnyStructType(CanType structType, StructDecl *D) { + TypeLowering *visitAnyStructType(CanType structType, + AbstractionPattern origType, + StructDecl *D) { RecursiveProperties properties; if (handleResilience(structType, D, properties)) @@ -1267,17 +1281,27 @@ namespace { // Classify the type according to its stored properties. for (auto field : D->getStoredProperties()) { auto substFieldType = - field->getInterfaceType().subst(subMap)->getCanonicalType(); - - properties.addSubobject(classifyType(substFieldType->getCanonicalType(), - TC, Sig, Expansion)); + field->getInterfaceType().subst(subMap) + ->getCanonicalType(D->getGenericSignature()); + + // We are determining the recursive properties of the struct here, + // not the lowered types of the fields, so instead of lowering the + // field type against the declaration's interface type as we normally + // would, we use the substituted field type in order to accurately + // preserve the properties of the aggregate. + auto origFieldType = origType.unsafeGetSubstFieldType(field); + + properties.addSubobject(classifyType(origFieldType, substFieldType, + TC, Expansion)); } return handleAggregateByProperties(structType, properties); } - TypeLowering *visitAnyEnumType(CanType enumType, EnumDecl *D) { + TypeLowering *visitAnyEnumType(CanType enumType, + AbstractionPattern origType, + EnumDecl *D) { RecursiveProperties properties; if (handleResilience(enumType, D, properties)) @@ -1291,8 +1315,8 @@ namespace { // may be added resiliently later. if (D->isIndirect()) { properties.setNonTrivial(); - return new (TC, Dependent) LoadableEnumTypeLowering(enumType, properties, - Expansion); + return new (TC) LoadableEnumTypeLowering(enumType, properties, + Expansion); } auto subMap = enumType->getContextSubstitutionMap(&TC.M, D); @@ -1310,9 +1334,14 @@ namespace { } auto substEltType = - elt->getArgumentInterfaceType().subst(subMap)->getCanonicalType(); + elt->getArgumentInterfaceType().subst(subMap) + ->getCanonicalType(D->getGenericSignature()); - properties.addSubobject(classifyType(substEltType, TC, Sig, Expansion)); + auto origEltType = origType.unsafeGetSubstFieldType(elt, + elt->getArgumentInterfaceType() + ->getCanonicalType(D->getGenericSignature())); + properties.addSubobject(classifyType(origEltType, substEltType, + TC, Expansion)); } return handleAggregateByProperties(enumType, @@ -1329,7 +1358,7 @@ namespace { if (props.isTrivial()) { return handleTrivial(type, props); } - return new (TC, Dependent) LoadableLoweringClass(type, props, Expansion); + return new (TC) LoadableLoweringClass(type, props, Expansion); } }; } // end anonymous namespace @@ -1341,7 +1370,7 @@ TypeConverter::TypeConverter(ModuleDecl &m) TypeConverter::~TypeConverter() { // The bump pointer allocator destructor will deallocate but not destroy all // our independent TypeLowerings. - for (auto &ti : IndependentTypes) { + for (auto &ti : LoweredTypes) { // Destroy only the unique entries. CanType srcType = ti.first.OrigType; if (!srcType) continue; @@ -1351,53 +1380,48 @@ TypeConverter::~TypeConverter() { } } -void *TypeLowering::operator new(size_t size, TypeConverter &tc, - IsDependent_t dependent) { - if (dependent) { - auto &state = tc.DependentTypes.back(); - return state.BPA.Allocate(size, alignof(TypeLowering&)); - } - return tc.IndependentBPA.Allocate(size, alignof(TypeLowering&)); +void *TypeLowering::operator new(size_t size, TypeConverter &tc) { + return tc.TypeLoweringBPA.Allocate(size, alignof(TypeLowering&)); } const TypeLowering *TypeConverter::find(TypeKey k) { if (!k.isCacheable()) return nullptr; auto ck = k.getCachingKey(); - - llvm::DenseMap *types; - if (k.isDependent()) { - auto &state = DependentTypes.back(); - types = &state.Map; - } else { - types = &IndependentTypes; - } - - auto found = types->find(ck); - if (found == types->end()) + auto found = LoweredTypes.find(ck); + if (found == LoweredTypes.end()) return nullptr; - assert(found->second && "type recursion not caught in Sema"); + assert((found->second || k.expansionContext.isMinimal()) && + "type recursion not caught in Sema"); return found->second; } +#ifndef NDEBUG +void TypeConverter::removeNullEntry(TypeKey k) { + if (!k.isCacheable()) + return; + + auto ck = k.getCachingKey(); + + auto found = LoweredTypes.find(ck); + if (found == LoweredTypes.end() || found->second != nullptr) + return; + + LoweredTypes.erase(ck); +} +#endif + void TypeConverter::insert(TypeKey k, const TypeLowering *tl) { if (!k.isCacheable()) return; - llvm::DenseMap *types; - if (k.isDependent()) { - auto &state = DependentTypes.back(); - types = &state.Map; - } else { - types = &IndependentTypes; - } - - (*types)[k.getCachingKey()] = tl; + LoweredTypes[k.getCachingKey()] = tl; } /// Lower each of the elements of the substituted type according to /// the abstraction pattern of the given original type. static CanTupleType computeLoweredTupleType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanTupleType substType) { assert(origType.matchesTuple(substType)); @@ -1420,7 +1444,7 @@ static CanTupleType computeLoweredTupleType(TypeConverter &tc, assert(!Flags.isVariadic()); CanType loweredSubstEltType = - tc.getLoweredRValueType(origEltType, substEltType); + tc.getLoweredRValueType(context, origEltType, substEltType); changed = (changed || substEltType != loweredSubstEltType || !Flags.isNone()); @@ -1442,14 +1466,14 @@ static CanTupleType computeLoweredTupleType(TypeConverter &tc, } static CanType computeLoweredOptionalType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanType substType, CanType substObjectType) { assert(substType.getOptionalObjectType() == substObjectType); - CanType loweredObjectType = - tc.getLoweredRValueType(origType.getOptionalObjectType(), - substObjectType); + CanType loweredObjectType = tc.getLoweredRValueType( + context, origType.getOptionalObjectType(), substObjectType); // If the object type didn't change, we don't have to rebuild anything. if (loweredObjectType == substObjectType) { @@ -1462,11 +1486,12 @@ static CanType computeLoweredOptionalType(TypeConverter &tc, static CanType computeLoweredReferenceStorageType(TypeConverter &tc, + TypeExpansionContext context, AbstractionPattern origType, CanReferenceStorageType substType) { - CanType loweredReferentType = - tc.getLoweredRValueType(origType.getReferenceStorageReferentType(), - substType.getReferentType()); + CanType loweredReferentType = tc.getLoweredRValueType( + context, origType.getReferenceStorageReferentType(), + substType.getReferentType()); if (loweredReferentType == substType.getReferentType()) return substType; @@ -1476,75 +1501,91 @@ computeLoweredReferenceStorageType(TypeConverter &tc, } CanSILFunctionType -TypeConverter::getSILFunctionType(AbstractionPattern origType, +TypeConverter::getSILFunctionType(TypeExpansionContext context, + AbstractionPattern origType, CanFunctionType substType) { return cast( - getLoweredRValueType(origType, substType)); + getLoweredRValueType(context, origType, substType)); +} + +bool TypeConverter::hasOpaqueArchetypeOrPropertiesOrCases(CanType ty) { + if (ty->hasOpaqueArchetype()) + return true; + + auto it = opaqueArchetypeFields.find(ty); + if (it == opaqueArchetypeFields.end()) { + bool res = ty->hasOpaqueArchetypePropertiesOrCases(); + opaqueArchetypeFields[ty] = res; + return res; + } + return it->second; } const TypeLowering & TypeConverter::getTypeLowering(AbstractionPattern origType, Type origSubstType, - ResilienceExpansion forExpansion) { + TypeExpansionContext forExpansion) { CanType substType = origSubstType->getCanonicalType(); - auto key = getTypeKey(origType, substType); - - assert((!key.isDependent() || getCurGenericContext()) - && "dependent type outside of generic context?!"); + auto origHadOpaqueTypeArchetype = + hasOpaqueArchetypeOrPropertiesOrCases(origSubstType->getCanonicalType()); + auto key = getTypeKey(origType, substType, forExpansion); assert(!substType->is()); - auto *prev = find(key); - auto *lowering = getTypeLoweringForExpansion(key, forExpansion, prev); + auto *candidateLowering = find(key.getKeyForMinimalExpansion()); + auto *lowering = getTypeLoweringForExpansion( + key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); if (lowering != nullptr) return *lowering; #ifndef NDEBUG // Catch reentrancy bugs. - if (prev == nullptr) - insert(key, nullptr); + if (candidateLowering == nullptr) + insert(key.getKeyForMinimalExpansion(), nullptr); #endif // Lower the type. - auto loweredSubstType = computeLoweredRValueType(origType, substType); + auto loweredSubstType = + computeLoweredRValueType(forExpansion, origType, substType); // If that didn't change the type and the key is cachable, there's no // point in re-checking the table, so just construct a type lowering // and cache it. if (loweredSubstType == substType && key.isCacheable()) { - lowering = LowerType(*this, - CanGenericSignature(), - forExpansion, - key.isDependent()).visit(key.SubstType); + lowering = LowerType(*this, forExpansion) + .visit(key.SubstType, key.OrigType); // Otherwise, check the table at a key that would be used by the // SILType-based lookup path for the type we just lowered to, then cache // that same result at this key if possible. } else { - AbstractionPattern origTypeForCaching = - AbstractionPattern(getCurGenericContext(), loweredSubstType); - auto loweredKey = getTypeKey(origTypeForCaching, loweredSubstType); - - lowering = &getTypeLoweringForLoweredType(loweredKey, - forExpansion); + lowering = &getTypeLoweringForLoweredType(origType, + loweredSubstType, + forExpansion, + origHadOpaqueTypeArchetype); } - if (prev == nullptr) + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { + insert(key.getKeyForMinimalExpansion(), lowering); + } else { insert(key, lowering); - else { - prev->NextExpansion = lowering; - assert(prev->isResilient() == lowering->isResilient()); +#ifndef NDEBUG + removeNullEntry(key.getKeyForMinimalExpansion()); +#endif } - return *lowering; } -CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, - CanType substType) { +CanType +TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion, + AbstractionPattern origType, + CanType substType) { // AST function types are turned into SIL function types: // - the type is uncurried as desired // - types are turned into their unbridged equivalents, depending // on the abstract CC // - ownership conventions are deduced + // - a minimal substituted generic signature is extracted to represent + // possible ABI-compatible substitutions if (auto substFnType = dyn_cast(substType)) { // If the formal type uses a C convention, it is not formally // abstractable, and it may be subject to implicit bridging. @@ -1564,7 +1605,7 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, substFnType = bridgedFnType; // Also rewrite the type of the abstraction pattern. - auto signature = getCurGenericContext(); + auto signature = origType.getGenericSignatureOrNull(); if (origType.isTypeParameter()) { origType = AbstractionPattern(signature, bridgedFnType); } else { @@ -1572,12 +1613,12 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, } } - return getNativeSILFunctionType(*this, origType, substFnType); + return getNativeSILFunctionType(*this, forExpansion, origType, substFnType); } // Ignore dynamic self types. if (auto selfType = dyn_cast(substType)) { - return selfType.getSelfType(); + return getLoweredRValueType(forExpansion, origType, selfType.getSelfType()); } // Static metatypes are unitary and can optimized to a "thin" empty @@ -1588,7 +1629,7 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, // representation. if (substMeta->hasRepresentation()) { assert(substMeta->isLegalSILType()); - return substMeta; + return substOpaqueTypesWithUnderlyingTypes(substMeta, forExpansion); } MetatypeRepresentation repr; @@ -1607,8 +1648,9 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, else repr = MetatypeRepresentation::Thick; } - - CanType instanceType = substMeta.getInstanceType(); + + CanType instanceType = substOpaqueTypesWithUnderlyingTypes( + substMeta.getInstanceType(), forExpansion); // Regardless of thinness, metatypes are always trivial. return CanMetatypeType::get(instanceType, repr); @@ -1627,61 +1669,113 @@ CanType TypeConverter::computeLoweredRValueType(AbstractionPattern origType, // Lower tuple element types. if (auto substTupleType = dyn_cast(substType)) { - return computeLoweredTupleType(*this, origType, substTupleType); + return computeLoweredTupleType(*this, forExpansion, origType, + substTupleType); } // Lower the referent type of reference storage types. if (auto substRefType = dyn_cast(substType)) { - return computeLoweredReferenceStorageType(*this, origType, substRefType); + return computeLoweredReferenceStorageType(*this, forExpansion, origType, + substRefType); } // Lower the object type of optional types. if (auto substObjectType = substType.getOptionalObjectType()) { - return computeLoweredOptionalType(*this, origType, + return computeLoweredOptionalType(*this, forExpansion, origType, substType, substObjectType); } + if (auto silFnTy = dyn_cast(substType)) { + if (!substType->hasOpaqueArchetype() || + !forExpansion.shouldLookThroughOpaqueTypeArchetypes()) + return substType; + return silFnTy->substituteOpaqueArchetypes(*this, forExpansion); + } + // The Swift type directly corresponds to the lowered type. - return substType; + auto underlyingTy = + substOpaqueTypesWithUnderlyingTypes(substType, forExpansion, + /*allowLoweredTypes*/ true); + if (underlyingTy != substType) { + underlyingTy = computeLoweredRValueType( + forExpansion, + origType, + underlyingTy); + } + + return underlyingTy; } const TypeLowering & -TypeConverter::getTypeLowering(SILType type, ResilienceExpansion forExpansion) { +TypeConverter::getTypeLowering(SILType type, + TypeExpansionContext forExpansion, + CanGenericSignature sig) { + // The type lowering for a type parameter relies on its context. + assert(sig || !type.getASTType()->hasTypeParameter()); auto loweredType = type.getASTType(); - auto key = getTypeKey(AbstractionPattern(getCurGenericContext(), loweredType), - loweredType); + auto origHadOpaqueTypeArchetype = + hasOpaqueArchetypeOrPropertiesOrCases(loweredType); - return getTypeLoweringForLoweredType(key, forExpansion); + return getTypeLoweringForLoweredType( + AbstractionPattern(sig, loweredType), + loweredType, forExpansion, + origHadOpaqueTypeArchetype); } const TypeLowering & -TypeConverter::getTypeLoweringForLoweredType(TypeKey key, - ResilienceExpansion forExpansion) { - auto type = key.SubstType; - assert(type->isLegalSILType() && "type is not lowered!"); - (void)type; - - auto *prev = find(key); - auto *lowering = getTypeLoweringForExpansion(key, forExpansion, prev); +TypeConverter::getTypeLowering(SILType t, SILFunction &F) { + return getTypeLowering(t, TypeExpansionContext(F), + F.getLoweredFunctionType()->getSubstGenericSignature()); +} + +const TypeLowering & +TypeConverter::getTypeLoweringForLoweredType(AbstractionPattern origType, + CanType loweredType, + TypeExpansionContext forExpansion, + bool origHadOpaqueTypeArchetype) { + assert(loweredType->isLegalSILType() && "type is not lowered!"); + (void)loweredType; + + // Cache the lowered type record for a contextualized type independent of the + // abstraction pattern. Lowered type parameters can't be cached or looked up + // without context. (TODO: We could if they match the out-of-context + // abstraction pattern.) + AbstractionPattern origTypeForCaching = loweredType->hasTypeParameter() + ? AbstractionPattern::getInvalid() + : AbstractionPattern(loweredType); + auto key = getTypeKey(origTypeForCaching, loweredType, forExpansion); + + auto *candidateLowering = find(key.getKeyForMinimalExpansion()); + auto *lowering = getTypeLoweringForExpansion( + key, forExpansion, candidateLowering, origHadOpaqueTypeArchetype); if (lowering != nullptr) return *lowering; #ifndef NDEBUG // Catch reentrancy bugs. - if (prev == nullptr) - insert(key, nullptr); + if (candidateLowering == nullptr) + insert(key.getKeyForMinimalExpansion(), nullptr); #endif - lowering = LowerType(*this, - CanGenericSignature(), - forExpansion, - key.isDependent()).visit(key.SubstType); + if (forExpansion.shouldLookThroughOpaqueTypeArchetypes() && + loweredType->hasOpaqueArchetype()) { + loweredType = computeLoweredRValueType( + forExpansion, origType, loweredType); + } + + lowering = + LowerType(*this, forExpansion) + .visit(loweredType, origType); - if (prev) { - prev->NextExpansion = lowering; - assert(prev->isResilient() == lowering->isResilient()); - } else + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) + insert(key.getKeyForMinimalExpansion(), lowering); + else { insert(key, lowering); +#ifndef NDEBUG + removeNullEntry(key.getKeyForMinimalExpansion()); +#endif + } + return *lowering; } @@ -1691,28 +1785,25 @@ TypeConverter::getTypeLoweringForLoweredType(TypeKey key, /// go ahead and lower the type with the correct expansion. const TypeLowering *TypeConverter:: getTypeLoweringForExpansion(TypeKey key, - ResilienceExpansion forExpansion, - const TypeLowering *lowering) { + TypeExpansionContext forExpansion, + const TypeLowering *lowering, + bool origHadOpaqueTypeArchetype) { if (lowering == nullptr) return nullptr; - if (!lowering->isResilient()) { + if (!lowering->isResilient() && !origHadOpaqueTypeArchetype) { // Don't try to refine the lowering for other resilience expansions if - // we don't expect to get a different lowering anyway. + // we don't expect to get a different lowering anyway. Similar if the + // original type did not have opaque type archetypes. // // See LowerType::handleResilience() for the gory details; we only // set this flag if the type is resilient *and* inside our module. return lowering; } - // Search for a matching lowering in the linked list of lowerings. - while (lowering) { - if (lowering->getResilienceExpansion() == forExpansion) - return lowering; - - // Continue searching. - lowering = lowering->NextExpansion; - } + auto *exactLowering = find(key); + if (exactLowering) + return exactLowering; // We have to create a new one. return nullptr; @@ -1737,9 +1828,8 @@ getEffectiveGenericSignature(AnyFunctionRef fn, static CanGenericSignature getCanonicalSignatureOrNull(GenericSignature sig) { if (!sig || sig->areAllParamsConcrete()) - return nullptr; - - return sig->getCanonicalSignature(); + return nullptr; + return sig.getCanonicalSignature(); } /// Get the type of a global variable accessor function, () -> RawPointer. @@ -1774,8 +1864,8 @@ static CanAnyFunctionType getDefaultArgGeneratorInterfaceType( auto sig = vd->getInnermostDeclContext()->getGenericSignatureOfContext(); if (auto *afd = dyn_cast(vd)) { auto *param = getParameterAt(afd, c.defaultArgIndex); - if (param->getDefaultValue()) { - auto &captureInfo = param->getDefaultArgumentCaptureInfo(); + if (param->hasDefaultExpr()) { + auto captureInfo = param->getDefaultArgumentCaptureInfo(); sig = getEffectiveGenericSignature(afd, captureInfo); } } @@ -1796,7 +1886,7 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType( // wrapper that was initialized with '=', the stored property initializer // will be in terms of the original property's type. if (auto originalProperty = VD->getOriginalWrappedProperty()) { - if (originalProperty->isPropertyWrapperInitializedWithInitialValue()) + if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) resultTy = originalProperty->getValueInterfaceType()->getCanonicalType(); } @@ -2016,7 +2106,8 @@ TypeConverter::getConstantGenericEnvironment(SILDeclRef c) { return nullptr; } -SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, +SILType TypeConverter::getSubstitutedStorageType(TypeExpansionContext context, + AbstractStorageDecl *value, Type lvalueType) { // The l-value type is the result of applying substitutions to // the type-of-reference. Essentially, we want to apply those @@ -2035,7 +2126,7 @@ SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, substType = substType.getReferenceStorageReferent(); } - CanType substLoweredType = getLoweredRValueType(origType, substType); + CanType substLoweredType = getLoweredRValueType(context, origType, substType); // Type substitution preserves structural type structure, and the // type-of-reference is only different in the outermost structural @@ -2052,39 +2143,6 @@ SILType TypeConverter::getSubstitutedStorageType(AbstractStorageDecl *value, return SILType::getPrimitiveAddressType(substLoweredType); } -void TypeConverter::pushGenericContext(CanGenericSignature sig) { - // If the generic signature is empty, this is a no-op. - if (!sig) - return; - - DependentTypeState state(sig); - DependentTypes.push_back(std::move(state)); -} - -void TypeConverter::popGenericContext(CanGenericSignature sig) { - // If the generic signature is empty, this is a no-op. - if (!sig) - return; - - DependentTypeState &state = DependentTypes.back(); - assert(state.Sig == sig && "unpaired push/pop"); - - // Erase our cached TypeLowering objects and associated mappings for dependent - // types. - // Resetting the DependentBPA will deallocate but not run the destructor of - // the dependent TypeLowerings. - for (auto &ti : state.Map) { - // Destroy only the unique entries. - CanType srcType = ti.first.OrigType; - if (!srcType) continue; - CanType mappedType = ti.second->getLoweredType().getASTType(); - if (srcType == mappedType) - ti.second->~TypeLowering(); - } - - DependentTypes.pop_back(); -} - ProtocolDispatchStrategy TypeConverter::getProtocolDispatchStrategy(ProtocolDecl *P) { // ObjC protocols use ObjC method dispatch, and Swift protocols @@ -2111,6 +2169,23 @@ TypeConverter::hasLoweredLocalCaptures(SILDeclRef fn) { CaptureInfo TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { + PrettyStackTraceSILLocation stack("getting lowered local captures", + fn.getAsRegularLocation(), Context); + // If we're guaranteed to never have local captures, bail out now. + switch (fn.kind) { + case SILDeclRef::Kind::StoredPropertyInitializer: + case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + return CaptureInfo::empty(); + + default: + if (fn.hasDecl()) { + if (!fn.getDecl()->isLocalCapture()) + return CaptureInfo::empty(); + } + + break; + } + fn.isForeign = 0; fn.isCurried = 0; fn.isDirectReference = 0; @@ -2132,11 +2207,13 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { DynamicSelfType *capturesDynamicSelf = nullptr; OpaqueValueExpr *capturesOpaqueValue = nullptr; - std::function collectCaptures; + std::function collectCaptures; std::function collectFunctionCaptures; std::function collectConstantCaptures; - collectCaptures = [&](const CaptureInfo &captureInfo) { + collectCaptures = [&](CaptureInfo captureInfo, DeclContext *dc) { + assert(captureInfo.hasBeenComputed()); + if (captureInfo.hasGenericParamCaptures()) capturesGenericParams = true; if (captureInfo.hasDynamicSelfCapture()) @@ -2223,9 +2300,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { continue; // We can always capture the storage in these cases. - Type captureType = capturedVar->getType(); - if (auto *metatypeType = captureType->getAs()) - captureType = metatypeType->getInstanceType(); + Type captureType = capturedVar->getType()->getMetatypeInstanceType(); if (auto *selfType = captureType->getAs()) { captureType = selfType->getSelfType(); @@ -2237,11 +2312,22 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { // mutable, we're going to be capturing a box or an address. if (captureType->getClassOrBoundGenericClass() && capturedVar->isLet()) { - if (selfCapture) + // If we've already captured the same value already, just merge + // flags. + if (selfCapture && selfCapture->getDecl() == capture.getDecl()) { selfCapture = selfCapture->mergeFlags(capture); - else + continue; + + // Otherwise, record the canonical self capture. It will appear + // at the end of the capture list. + } else if (!selfCapture) { selfCapture = capture; - continue; + continue; + } + + // If we end up here, we have multiple different captured values + // with a dynamic 'Self' type. Handle this and any subsequent + // captures via the normal code path below. } } @@ -2260,10 +2346,15 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { }; collectFunctionCaptures = [&](AnyFunctionRef curFn) { + if (!curFn.getBody()) + return; + if (!visitedFunctions.insert(curFn).second) return; - collectCaptures(curFn.getCaptureInfo()); + PrettyStackTraceAnyFunctionRef("lowering local captures", curFn); + auto dc = curFn.getAsDeclContext(); + collectCaptures(curFn.getCaptureInfo(), dc); // A function's captures also include its default arguments, because // when we reference a function we don't track which default arguments @@ -2273,18 +2364,23 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { // captures for default arguments that are actually referenced. if (auto *AFD = curFn.getAbstractFunctionDecl()) { for (auto *P : *AFD->getParameters()) { - if (P->getDefaultValue()) - collectCaptures(P->getDefaultArgumentCaptureInfo()); + if (P->hasDefaultExpr()) + collectCaptures(P->getDefaultArgumentCaptureInfo(), dc); } } }; collectConstantCaptures = [&](SILDeclRef curFn) { if (curFn.isDefaultArgGenerator()) { + PrettyStackTraceSILLocation stack("lowering local captures", + fn.getAsRegularLocation(), Context); + if (auto *afd = dyn_cast(curFn.getDecl())) { auto *param = getParameterAt(afd, curFn.defaultArgIndex); - if (param->getDefaultValue()) - collectCaptures(param->getDefaultArgumentCaptureInfo()); + if (param->hasDefaultExpr()) { + auto dc = afd->getInnermostDeclContext(); + collectCaptures(param->getDefaultArgumentCaptureInfo(), dc); + } return; } @@ -2332,7 +2428,8 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { /// Given that type1 is known to be a subtype of type2, check if the two /// types have the same calling convention representation. TypeConverter::ABIDifference -TypeConverter::checkForABIDifferences(SILType type1, SILType type2, +TypeConverter::checkForABIDifferences(SILModule &M, + SILType type1, SILType type2, bool thunkOptionals) { // Unwrap optionals, but remember that we did. bool type1WasOptional = false; @@ -2367,14 +2464,14 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, // If the types are identical and there was no optionality change, // we're done. if (type1 == type2 && !optionalityChange) - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; // Classes, class-constrained archetypes, and pure-ObjC existential types // all have single retainable pointer representation; optionality change // is allowed. if (type1.getASTType()->satisfiesClassConstraint() && type2.getASTType()->satisfiesClassConstraint()) - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; // Function parameters are ABI compatible if their differences are // trivial. @@ -2387,7 +2484,7 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, fnTy1->getRepresentation() != SILFunctionTypeRepresentation::Block) return ABIDifference::NeedsThunk; - return checkFunctionForABIDifferences(fnTy1, fnTy2); + return checkFunctionForABIDifferences(M, fnTy1, fnTy2); } } @@ -2397,7 +2494,7 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, if (meta1->getRepresentation() == meta2->getRepresentation() && (!optionalityChange || meta1->getRepresentation() == MetatypeRepresentation::Thick)) - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; } } @@ -2410,7 +2507,7 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, if (auto meta2 = type2.getAs()) { if (meta1->getRepresentation() == meta2->getRepresentation() && meta1->getRepresentation() == MetatypeRepresentation::ObjC) - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; } } @@ -2422,14 +2519,15 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, return ABIDifference::NeedsThunk; for (unsigned i = 0, e = tuple1->getNumElements(); i < e; i++) { - if (checkForABIDifferences(type1.getTupleElementType(i), + if (checkForABIDifferences(M, + type1.getTupleElementType(i), type2.getTupleElementType(i)) - != ABIDifference::Trivial) + != ABIDifference::CompatibleRepresentation) return ABIDifference::NeedsThunk; } // Tuple lengths and elements match - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; } } } @@ -2440,12 +2538,22 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2, } TypeConverter::ABIDifference -TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1, +TypeConverter::checkFunctionForABIDifferences(SILModule &M, + SILFunctionType *fnTy1, SILFunctionType *fnTy2) { + // For now, only differentiate representation from calling convention when + // staging in substituted function types. + // + // We might still want to conditionalize this behavior even after we commit + // substituted function types, to avoid bloating + // IR for platforms that don't differentiate function type representations. + bool DifferentFunctionTypesHaveDifferentRepresentation + = Context.LangOpts.EnableSubstSILFunctionTypesForFunctionValues; + // Fast path -- if both functions were unwrapped from a CanSILFunctionType, // we might have pointer equality here. if (fnTy1 == fnTy2) - return ABIDifference::Trivial; + return ABIDifference::CompatibleRepresentation; if (fnTy1->getParameters().size() != fnTy2->getParameters().size()) return ABIDifference::NeedsThunk; @@ -2469,10 +2577,11 @@ TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1, if (result1.getConvention() != result2.getConvention()) return ABIDifference::NeedsThunk; - if (checkForABIDifferences(result1.getSILStorageType(), - result2.getSILStorageType(), + if (checkForABIDifferences(M, + result1.getSILStorageType(M, fnTy1), + result2.getSILStorageType(M, fnTy2), /*thunk iuos*/ fnTy1->getLanguage() == SILFunctionLanguage::Swift) - != ABIDifference::Trivial) + != ABIDifference::CompatibleRepresentation) return ABIDifference::NeedsThunk; } @@ -2483,10 +2592,11 @@ TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1, if (yield1.getConvention() != yield2.getConvention()) return ABIDifference::NeedsThunk; - if (checkForABIDifferences(yield1.getSILStorageType(), - yield2.getSILStorageType(), + if (checkForABIDifferences(M, + yield1.getSILStorageType(M, fnTy1), + yield2.getSILStorageType(M, fnTy2), /*thunk iuos*/ fnTy1->getLanguage() == SILFunctionLanguage::Swift) - != ABIDifference::Trivial) + != ABIDifference::CompatibleRepresentation) return ABIDifference::NeedsThunk; } @@ -2499,10 +2609,11 @@ TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1, if (error1.getConvention() != error2.getConvention()) return ABIDifference::NeedsThunk; - if (checkForABIDifferences(error1.getSILStorageType(), - error2.getSILStorageType(), + if (checkForABIDifferences(M, + error1.getSILStorageType(M, fnTy1), + error2.getSILStorageType(M, fnTy2), /*thunk iuos*/ fnTy1->getLanguage() == SILFunctionLanguage::Swift) - != ABIDifference::Trivial) + != ABIDifference::CompatibleRepresentation) return ABIDifference::NeedsThunk; } @@ -2514,25 +2625,34 @@ TypeConverter::checkFunctionForABIDifferences(SILFunctionType *fnTy1, // Parameters are contravariant and our relation is not symmetric, so // make sure to flip the relation around. - std::swap(param1, param2); - - if (checkForABIDifferences(param1.getSILStorageType(), - param2.getSILStorageType(), + if (checkForABIDifferences(M, + param2.getSILStorageType(M, fnTy2), + param1.getSILStorageType(M, fnTy1), /*thunk iuos*/ fnTy1->getLanguage() == SILFunctionLanguage::Swift) - != ABIDifference::Trivial) + != ABIDifference::CompatibleRepresentation) return ABIDifference::NeedsThunk; } auto rep1 = fnTy1->getRepresentation(), rep2 = fnTy2->getRepresentation(); if (rep1 != rep2) { if (rep1 == SILFunctionTypeRepresentation::Thin && - rep2 == SILFunctionTypeRepresentation::Thick) - return ABIDifference::ThinToThick; + rep2 == SILFunctionTypeRepresentation::Thick) { + if (DifferentFunctionTypesHaveDifferentRepresentation) { + // FIXME: check whether the representations are compatible modulo + // context + return ABIDifference::CompatibleCallingConvention_ThinToThick; + } else { + return ABIDifference::CompatibleRepresentation_ThinToThick; + } + } return ABIDifference::NeedsThunk; } - return ABIDifference::Trivial; + if (DifferentFunctionTypesHaveDifferentRepresentation) + return ABIDifference::CompatibleCallingConvention; + else + return ABIDifference::CompatibleRepresentation; } CanSILBoxType @@ -2569,8 +2689,6 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, auto boxTy = SILBoxType::get(C, layout, subMap); #ifndef NDEBUG - // FIXME: Map the box type out of context when asserting the field so - // we don't need to push a GenericContextScope (which really ought to die). auto loweredContextType = loweredInterfaceType; auto contextBoxTy = boxTy; if (signature) { @@ -2581,10 +2699,11 @@ TypeConverter::getInterfaceBoxTypeForCapture(ValueDecl *captured, env->mapTypeIntoContext(contextBoxTy) ->getCanonicalType()); } - assert(contextBoxTy->getLayout()->getFields().size() == 1 - && getSILBoxFieldType(contextBoxTy, *this, 0).getASTType() - == loweredContextType - && "box field type doesn't match capture!"); + assert(contextBoxTy->getLayout()->getFields().size() == 1 && + getSILBoxFieldType(TypeExpansionContext::minimal(), contextBoxTy, + *this, 0) + .getASTType() == loweredContextType && + "box field type doesn't match capture!"); #endif return boxTy; } @@ -2614,8 +2733,8 @@ TypeConverter::getContextBoxTypeForCapture(ValueDecl *captured, return boxType; } -CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, - EnumElementDecl *elt) { +CanSILBoxType TypeConverter::getBoxTypeForEnumElement( + TypeExpansionContext context, SILType enumType, EnumElementDecl *elt) { auto *enumDecl = enumType.getEnumOrBoundGenericEnum(); @@ -2628,8 +2747,7 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, if (boxSignature == CanGenericSignature()) { auto eltIntfTy = elt->getArgumentInterfaceType(); - - auto boxVarTy = getLoweredRValueType(eltIntfTy); + auto boxVarTy = getLoweredRValueType(context, eltIntfTy); auto layout = SILLayout::get(C, nullptr, SILField(boxVarTy, true)); return SILBoxType::get(C, layout, {}); } @@ -2639,10 +2757,9 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, // Lower the enum element's argument in the box's context. auto eltIntfTy = elt->getArgumentInterfaceType(); - GenericContextScope scope(*this, boxSignature); - auto boxVarTy = getLoweredRValueType(getAbstractionPattern(elt), - eltIntfTy); + auto boxVarTy = getLoweredRValueType(context, + getAbstractionPattern(elt), eltIntfTy); auto layout = SILLayout::get(C, boxSignature, SILField(boxVarTy, true)); // Instantiate the layout with enum's substitution list. @@ -2654,13 +2771,15 @@ CanSILBoxType TypeConverter::getBoxTypeForEnumElement(SILType enumType, } static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, - SILType Ty, ResilienceExpansion expansion) { + SILType Ty, + TypeExpansionContext expansion) { if (auto *structDecl = Ty.getStructOrBoundGenericStruct()) { - assert(!structDecl->isResilient(&TC.M, expansion) && - " FSO should not be trying to explode resilient (ie address-only) " - "types at all"); + assert( + !structDecl->isResilient(&TC.M, expansion.getResilienceExpansion()) && + " FSO should not be trying to explode resilient (ie address-only) " + "types at all"); for (auto *prop : structDecl->getStoredProperties()) { - SILType propTy = Ty.getFieldType(prop, TC); + SILType propTy = Ty.getFieldType(prop, TC, expansion); unsigned fieldsCountBefore = fieldsCount; countNumberOfInnerFields(fieldsCount, TC, propTy, expansion); if (fieldsCount == fieldsCountBefore) { @@ -2682,7 +2801,7 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, if (enumDecl->isIndirect()) { return; } - assert(!enumDecl->isResilient(&TC.M, expansion) && + assert(!enumDecl->isResilient(&TC.M, expansion.getResilienceExpansion()) && " FSO should not be trying to explode resilient (ie address-only) " "types at all"); unsigned fieldsCountBefore = fieldsCount; @@ -2699,7 +2818,7 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, // (we shouldn't expand enums) // Number of fields > 1 as "future proof" for this heuristic: // In case it is used by a pass that tries to explode enums. - auto payloadTy = Ty.getEnumElementType(elt, TC); + auto payloadTy = Ty.getEnumElementType(elt, TC, expansion); fieldsCount = 0; countNumberOfInnerFields(fieldsCount, TC, payloadTy, expansion); if (fieldsCount > maxEnumCount) { @@ -2712,8 +2831,8 @@ static void countNumberOfInnerFields(unsigned &fieldsCount, TypeConverter &TC, } unsigned TypeConverter::countNumberOfFields(SILType Ty, - ResilienceExpansion expansion) { - auto key = std::make_pair(Ty, unsigned(expansion)); + TypeExpansionContext expansion) { + auto key = std::make_pair(Ty, unsigned(expansion.getResilienceExpansion())); auto Iter = TypeFields.find(key); if (Iter != TypeFields.end()) { return std::max(Iter->second, 1U); @@ -2731,7 +2850,7 @@ void TypeLowering::print(llvm::raw_ostream &os) const { return "false"; }; os << "Type Lowering for lowered type: " << LoweredType << ".\n" - << "Expansion: " << ResilienceExpansion(ForExpansion) << "\n" + << "Expansion: " << getResilienceExpansion() << "\n" << "isTrivial: " << BOOL(Properties.isTrivial()) << ".\n" << "isFixedABI: " << BOOL(Properties.isFixedABI()) << ".\n" << "isAddressOnly: " << BOOL(Properties.isAddressOnly()) << ".\n" diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 9fd00e0df9d8f..94ebb2555fdd6 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -51,17 +51,17 @@ class ValueOwnershipKindClassifier #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ CONSTANT_OWNERSHIP_INST(Owned, Load##Name) -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - CONSTANT_OWNERSHIP_INST(Unowned, RefTo##Name) \ - CONSTANT_OWNERSHIP_INST(Unowned, Name##ToRef) \ - CONSTANT_OWNERSHIP_INST(Owned, Copy##Name##Value) +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + CONSTANT_OWNERSHIP_INST(Unowned, RefTo##Name) \ + CONSTANT_OWNERSHIP_INST(Unowned, Name##ToRef) \ + CONSTANT_OWNERSHIP_INST(Owned, StrongCopy##Name##Value) #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") #define UNCHECKED_REF_STORAGE(Name, ...) \ - CONSTANT_OWNERSHIP_INST(Any, RefTo##Name) \ + CONSTANT_OWNERSHIP_INST(None, RefTo##Name) \ CONSTANT_OWNERSHIP_INST(Unowned, Name##ToRef) \ - CONSTANT_OWNERSHIP_INST(Owned, Copy##Name##Value) + CONSTANT_OWNERSHIP_INST(Owned, StrongCopy##Name##Value) #include "swift/AST/ReferenceStorage.def" CONSTANT_OWNERSHIP_INST(Guaranteed, BeginBorrow) @@ -70,7 +70,7 @@ CONSTANT_OWNERSHIP_INST(Owned, AllocBox) CONSTANT_OWNERSHIP_INST(Owned, AllocExistentialBox) CONSTANT_OWNERSHIP_INST(Owned, AllocRef) CONSTANT_OWNERSHIP_INST(Owned, AllocRefDynamic) -CONSTANT_OWNERSHIP_INST(Any, AllocValueBuffer) +CONSTANT_OWNERSHIP_INST(None, AllocValueBuffer) CONSTANT_OWNERSHIP_INST(Owned, CopyBlock) CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping) CONSTANT_OWNERSHIP_INST(Owned, CopyValue) @@ -96,57 +96,57 @@ CONSTANT_OWNERSHIP_INST(Owned, ObjCMetatypeToObject) // All addresses have trivial ownership. The values stored at the address may // not though. -CONSTANT_OWNERSHIP_INST(Any, AddressToPointer) -CONSTANT_OWNERSHIP_INST(Any, AllocStack) -CONSTANT_OWNERSHIP_INST(Any, BeginAccess) -CONSTANT_OWNERSHIP_INST(Any, BridgeObjectToWord) -CONSTANT_OWNERSHIP_INST(Any, ClassMethod) -CONSTANT_OWNERSHIP_INST(Any, ClassifyBridgeObject) -CONSTANT_OWNERSHIP_INST(Any, ObjCMethod) -CONSTANT_OWNERSHIP_INST(Any, ExistentialMetatype) -CONSTANT_OWNERSHIP_INST(Any, FloatLiteral) -CONSTANT_OWNERSHIP_INST(Any, FunctionRef) -CONSTANT_OWNERSHIP_INST(Any, DynamicFunctionRef) -CONSTANT_OWNERSHIP_INST(Any, PreviousDynamicFunctionRef) -CONSTANT_OWNERSHIP_INST(Any, GlobalAddr) -CONSTANT_OWNERSHIP_INST(Any, IndexAddr) -CONSTANT_OWNERSHIP_INST(Any, IndexRawPointer) -CONSTANT_OWNERSHIP_INST(Any, InitEnumDataAddr) -CONSTANT_OWNERSHIP_INST(Any, InitExistentialAddr) -CONSTANT_OWNERSHIP_INST(Any, InitExistentialMetatype) -CONSTANT_OWNERSHIP_INST(Any, IntegerLiteral) -CONSTANT_OWNERSHIP_INST(Any, IsUnique) -CONSTANT_OWNERSHIP_INST(Any, IsEscapingClosure) -CONSTANT_OWNERSHIP_INST(Any, Metatype) -CONSTANT_OWNERSHIP_INST(Any, ObjCToThickMetatype) -CONSTANT_OWNERSHIP_INST(Any, OpenExistentialAddr) -CONSTANT_OWNERSHIP_INST(Any, OpenExistentialBox) -CONSTANT_OWNERSHIP_INST(Any, OpenExistentialMetatype) -CONSTANT_OWNERSHIP_INST(Any, PointerToAddress) -CONSTANT_OWNERSHIP_INST(Any, PointerToThinFunction) -CONSTANT_OWNERSHIP_INST(Any, ProjectBlockStorage) -CONSTANT_OWNERSHIP_INST(Any, ProjectBox) -CONSTANT_OWNERSHIP_INST(Any, ProjectExistentialBox) -CONSTANT_OWNERSHIP_INST(Any, ProjectValueBuffer) -CONSTANT_OWNERSHIP_INST(Any, RefElementAddr) -CONSTANT_OWNERSHIP_INST(Any, RefTailAddr) -CONSTANT_OWNERSHIP_INST(Any, RefToRawPointer) -CONSTANT_OWNERSHIP_INST(Any, SelectEnumAddr) -CONSTANT_OWNERSHIP_INST(Any, StringLiteral) -CONSTANT_OWNERSHIP_INST(Any, StructElementAddr) -CONSTANT_OWNERSHIP_INST(Any, SuperMethod) -CONSTANT_OWNERSHIP_INST(Any, ObjCSuperMethod) -CONSTANT_OWNERSHIP_INST(Any, TailAddr) -CONSTANT_OWNERSHIP_INST(Any, ThickToObjCMetatype) -CONSTANT_OWNERSHIP_INST(Any, ThinFunctionToPointer) -CONSTANT_OWNERSHIP_INST(Any, TupleElementAddr) -CONSTANT_OWNERSHIP_INST(Any, UncheckedAddrCast) -CONSTANT_OWNERSHIP_INST(Any, UncheckedTakeEnumDataAddr) -CONSTANT_OWNERSHIP_INST(Any, UncheckedTrivialBitCast) -CONSTANT_OWNERSHIP_INST(Any, ValueMetatype) -CONSTANT_OWNERSHIP_INST(Any, WitnessMethod) -CONSTANT_OWNERSHIP_INST(Any, StoreBorrow) -CONSTANT_OWNERSHIP_INST(Any, ConvertEscapeToNoEscape) +CONSTANT_OWNERSHIP_INST(None, AddressToPointer) +CONSTANT_OWNERSHIP_INST(None, AllocStack) +CONSTANT_OWNERSHIP_INST(None, BeginAccess) +CONSTANT_OWNERSHIP_INST(None, BridgeObjectToWord) +CONSTANT_OWNERSHIP_INST(None, ClassMethod) +CONSTANT_OWNERSHIP_INST(None, ClassifyBridgeObject) +CONSTANT_OWNERSHIP_INST(None, ObjCMethod) +CONSTANT_OWNERSHIP_INST(None, ExistentialMetatype) +CONSTANT_OWNERSHIP_INST(None, FloatLiteral) +CONSTANT_OWNERSHIP_INST(None, FunctionRef) +CONSTANT_OWNERSHIP_INST(None, DynamicFunctionRef) +CONSTANT_OWNERSHIP_INST(None, PreviousDynamicFunctionRef) +CONSTANT_OWNERSHIP_INST(None, GlobalAddr) +CONSTANT_OWNERSHIP_INST(None, IndexAddr) +CONSTANT_OWNERSHIP_INST(None, IndexRawPointer) +CONSTANT_OWNERSHIP_INST(None, InitEnumDataAddr) +CONSTANT_OWNERSHIP_INST(None, InitExistentialAddr) +CONSTANT_OWNERSHIP_INST(None, InitExistentialMetatype) +CONSTANT_OWNERSHIP_INST(None, IntegerLiteral) +CONSTANT_OWNERSHIP_INST(None, IsUnique) +CONSTANT_OWNERSHIP_INST(None, IsEscapingClosure) +CONSTANT_OWNERSHIP_INST(None, Metatype) +CONSTANT_OWNERSHIP_INST(None, ObjCToThickMetatype) +CONSTANT_OWNERSHIP_INST(None, OpenExistentialAddr) +CONSTANT_OWNERSHIP_INST(None, OpenExistentialBox) +CONSTANT_OWNERSHIP_INST(None, OpenExistentialMetatype) +CONSTANT_OWNERSHIP_INST(None, PointerToAddress) +CONSTANT_OWNERSHIP_INST(None, PointerToThinFunction) +CONSTANT_OWNERSHIP_INST(None, ProjectBlockStorage) +CONSTANT_OWNERSHIP_INST(None, ProjectBox) +CONSTANT_OWNERSHIP_INST(None, ProjectExistentialBox) +CONSTANT_OWNERSHIP_INST(None, ProjectValueBuffer) +CONSTANT_OWNERSHIP_INST(None, RefElementAddr) +CONSTANT_OWNERSHIP_INST(None, RefTailAddr) +CONSTANT_OWNERSHIP_INST(None, RefToRawPointer) +CONSTANT_OWNERSHIP_INST(None, SelectEnumAddr) +CONSTANT_OWNERSHIP_INST(None, StringLiteral) +CONSTANT_OWNERSHIP_INST(None, StructElementAddr) +CONSTANT_OWNERSHIP_INST(None, SuperMethod) +CONSTANT_OWNERSHIP_INST(None, ObjCSuperMethod) +CONSTANT_OWNERSHIP_INST(None, TailAddr) +CONSTANT_OWNERSHIP_INST(None, ThickToObjCMetatype) +CONSTANT_OWNERSHIP_INST(None, ThinFunctionToPointer) +CONSTANT_OWNERSHIP_INST(None, TupleElementAddr) +CONSTANT_OWNERSHIP_INST(None, UncheckedAddrCast) +CONSTANT_OWNERSHIP_INST(None, UncheckedTakeEnumDataAddr) +CONSTANT_OWNERSHIP_INST(None, UncheckedTrivialBitCast) +CONSTANT_OWNERSHIP_INST(None, ValueMetatype) +CONSTANT_OWNERSHIP_INST(None, WitnessMethod) +CONSTANT_OWNERSHIP_INST(None, StoreBorrow) +CONSTANT_OWNERSHIP_INST(None, ConvertEscapeToNoEscape) CONSTANT_OWNERSHIP_INST(Unowned, InitBlockStorageHeader) // TODO: It would be great to get rid of these. CONSTANT_OWNERSHIP_INST(Unowned, RawPointerToRef) @@ -154,25 +154,32 @@ CONSTANT_OWNERSHIP_INST(Unowned, ObjCProtocol) CONSTANT_OWNERSHIP_INST(Unowned, ValueToBridgeObject) #undef CONSTANT_OWNERSHIP_INST -#define CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(OWNERSHIP, INST) \ +#define CONSTANT_OR_NONE_OWNERSHIP_INST(OWNERSHIP, INST) \ ValueOwnershipKind ValueOwnershipKindClassifier::visit##INST##Inst( \ INST##Inst *I) { \ - if (I->getType().isTrivial(*I->getFunction())) { \ - return ValueOwnershipKind::Any; \ + if (I->getType().isTrivial(*I->getFunction()) || \ + I->getType().isAddress()) { \ + return ValueOwnershipKind::None; \ } \ return ValueOwnershipKind::OWNERSHIP; \ } -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, StructExtract) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, TupleExtract) +CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, StructExtract) +CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, TupleExtract) // OpenExistentialValue opens the boxed value inside an existential // CoW box. The semantics of an existential CoW box implies that we // can only consume the projected value inside the box if the box is // unique. Since we do not know in general if the box is unique // without additional work, in SIL we require opened archetypes to // be borrowed sub-objects of the parent CoW box. -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, OpenExistentialValue) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, OpenExistentialBoxValue) -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, UnconditionalCheckedCastValue) +CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialValue) +CONSTANT_OR_NONE_OWNERSHIP_INST(Guaranteed, OpenExistentialBoxValue) +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, UnconditionalCheckedCastValue) + +// Given an owned value, mark_uninitialized always forwards an owned value since +// we want to make sure that all destroys of that value must come through the +// mark_uninitialized (which will happen due to mark_uninitialized consuming the +// value). +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, MarkUninitialized) // unchecked_bitwise_cast is a bitwise copy. It produces a trivial or unowned // result. @@ -197,12 +204,12 @@ CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, UnconditionalCheckedCastValue) // then eliminate the copy. That being said, we should investigate // this since this is used in reinterpret_cast which is important from // a performance perspective. -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Unowned, UncheckedBitwiseCast) +CONSTANT_OR_NONE_OWNERSHIP_INST(Unowned, UncheckedBitwiseCast) // A thin_to_thick instruction can return a trivial (@noescape) type. -CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, ThinToThickFunction) +CONSTANT_OR_NONE_OWNERSHIP_INST(Owned, ThinToThickFunction) -#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST +#undef CONSTANT_OR_NONE_OWNERSHIP_INST // For a forwarding instruction, we loop over all operands and make sure that // all non-trivial values have the same ownership. @@ -211,7 +218,7 @@ ValueOwnershipKindClassifier::visitForwardingInst(SILInstruction *i, ArrayRef ops) { // A forwarding inst without operands must be trivial. if (ops.empty()) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; auto mergedValue = ValueOwnershipKind::merge(makeOptionalTransformRange( ops, [&i](const Operand &op) -> Optional { @@ -222,10 +229,10 @@ ValueOwnershipKindClassifier::visitForwardingInst(SILInstruction *i, if (!mergedValue.hasValue()) { // If we have mismatched SILOwnership and sil ownership is not enabled, - // just return Any for staging purposes. If SILOwnership is enabled, then + // just return None for staging purposes. If SILOwnership is enabled, then // we must assert! if (!i->getModule().getOptions().VerifySILOwnership) { - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; } llvm_unreachable("Forwarding inst with mismatching ownership kinds?!"); } @@ -249,7 +256,6 @@ FORWARDING_OWNERSHIP_INST(Tuple) FORWARDING_OWNERSHIP_INST(UncheckedRefCast) FORWARDING_OWNERSHIP_INST(UnconditionalCheckedCast) FORWARDING_OWNERSHIP_INST(Upcast) -FORWARDING_OWNERSHIP_INST(MarkUninitialized) FORWARDING_OWNERSHIP_INST(UncheckedEnumData) FORWARDING_OWNERSHIP_INST(SelectEnum) FORWARDING_OWNERSHIP_INST(Enum) @@ -293,7 +299,7 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitSILFunctionArgument( // This is a forwarding instruction through only one of its arguments. ValueOwnershipKind ValueOwnershipKindClassifier::visitMarkDependenceInst(MarkDependenceInst *MDI) { - return MDI->getValue().getOwnershipKind(); + return MDI->getOwnershipKind(); } ValueOwnershipKind ValueOwnershipKindClassifier::visitApplyInst(ApplyInst *ai) { @@ -301,13 +307,13 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitApplyInst(ApplyInst *ai) { bool isTrivial = ai->getType().isTrivial(*f); // Quick is trivial check. if (isTrivial) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; SILFunctionConventions fnConv(ai->getSubstCalleeType(), f->getModule()); auto results = fnConv.getDirectSILResults(); - // No results => Any. + // No results => None. if (results.empty()) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; // Otherwise, map our results to their ownership kinds and then merge them! auto resultOwnershipKinds = @@ -329,7 +335,7 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitLoadInst(LoadInst *LI) { return ValueOwnershipKind::Owned; case LoadOwnershipQualifier::Unqualified: case LoadOwnershipQualifier::Trivial: - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; } llvm_unreachable("Unhandled LoadOwnershipQualifier in switch."); @@ -338,7 +344,7 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitLoadInst(LoadInst *LI) { ValueOwnershipKind ValueOwnershipKindClassifier::visitPartialApplyInst(PartialApplyInst *PA) { if (PA->isOnStack()) - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; return ValueOwnershipKind::Owned; } @@ -355,7 +361,7 @@ struct ValueOwnershipKindBuiltinVisitor llvm::Intrinsic::ID ID) { // LLVM intrinsics do not traffic in ownership, so if we have a result, it // must be any. - return ValueOwnershipKind::Any; + return ValueOwnershipKind::None; } #define BUILTIN(ID, NAME, ATTRS) \ @@ -373,167 +379,167 @@ struct ValueOwnershipKindBuiltinVisitor // This returns a value at +1 that is destroyed strictly /after/ the // UnsafeGuaranteedEnd. This provides the guarantee that we want. CONSTANT_OWNERSHIP_BUILTIN(Owned, UnsafeGuaranteed) -CONSTANT_OWNERSHIP_BUILTIN(Any, AShr) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericAShr) -CONSTANT_OWNERSHIP_BUILTIN(Any, Add) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericAdd) -CONSTANT_OWNERSHIP_BUILTIN(Any, And) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericAnd) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeNonNegative) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeTrue) -CONSTANT_OWNERSHIP_BUILTIN(Any, BitCast) -CONSTANT_OWNERSHIP_BUILTIN(Any, CondFailMessage) -CONSTANT_OWNERSHIP_BUILTIN(Any, ExactSDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericExactSDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, ExactUDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericExactUDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, FAdd) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericFAdd) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_OEQ) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_OGE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_OGT) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_OLE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_OLT) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_ONE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_UEQ) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_UGE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_UGT) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_ULE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_ULT) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_UNE) -CONSTANT_OWNERSHIP_BUILTIN(Any, FDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericFDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, FMul) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericFMul) -CONSTANT_OWNERSHIP_BUILTIN(Any, FNeg) -CONSTANT_OWNERSHIP_BUILTIN(Any, FPExt) -CONSTANT_OWNERSHIP_BUILTIN(Any, FPToSI) -CONSTANT_OWNERSHIP_BUILTIN(Any, FPToUI) -CONSTANT_OWNERSHIP_BUILTIN(Any, FPTrunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, FRem) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericFRem) -CONSTANT_OWNERSHIP_BUILTIN(Any, FSub) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericFSub) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_EQ) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_NE) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_SGE) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_SGT) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_SLE) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_SLT) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_UGE) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_UGT) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_ULE) -CONSTANT_OWNERSHIP_BUILTIN(Any, ICMP_ULT) -CONSTANT_OWNERSHIP_BUILTIN(Any, IntToPtr) -CONSTANT_OWNERSHIP_BUILTIN(Any, LShr) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericLShr) -CONSTANT_OWNERSHIP_BUILTIN(Any, Mul) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericMul) -CONSTANT_OWNERSHIP_BUILTIN(Any, Or) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericOr) -CONSTANT_OWNERSHIP_BUILTIN(Any, PtrToInt) -CONSTANT_OWNERSHIP_BUILTIN(Any, SAddOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, SDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericSDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, SExt) -CONSTANT_OWNERSHIP_BUILTIN(Any, SExtOrBitCast) -CONSTANT_OWNERSHIP_BUILTIN(Any, SIToFP) -CONSTANT_OWNERSHIP_BUILTIN(Any, SMulOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, SRem) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericSRem) -CONSTANT_OWNERSHIP_BUILTIN(Any, SSubOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, Expect) -CONSTANT_OWNERSHIP_BUILTIN(Any, Shl) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericShl) -CONSTANT_OWNERSHIP_BUILTIN(Any, Sub) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericSub) -CONSTANT_OWNERSHIP_BUILTIN(Any, Trunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, TruncOrBitCast) -CONSTANT_OWNERSHIP_BUILTIN(Any, UAddOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, UDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericUDiv) -CONSTANT_OWNERSHIP_BUILTIN(Any, UIToFP) -CONSTANT_OWNERSHIP_BUILTIN(Any, UMulOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, URem) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericURem) -CONSTANT_OWNERSHIP_BUILTIN(Any, USubOver) -CONSTANT_OWNERSHIP_BUILTIN(Any, Xor) -CONSTANT_OWNERSHIP_BUILTIN(Any, GenericXor) -CONSTANT_OWNERSHIP_BUILTIN(Any, ZExt) -CONSTANT_OWNERSHIP_BUILTIN(Any, ZExtOrBitCast) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_ORD) -CONSTANT_OWNERSHIP_BUILTIN(Any, FCMP_UNO) -CONSTANT_OWNERSHIP_BUILTIN(Any, OnFastPath) -CONSTANT_OWNERSHIP_BUILTIN(Any, IsOptionalType) -CONSTANT_OWNERSHIP_BUILTIN(Any, Sizeof) -CONSTANT_OWNERSHIP_BUILTIN(Any, Strideof) -CONSTANT_OWNERSHIP_BUILTIN(Any, StringObjectOr) -CONSTANT_OWNERSHIP_BUILTIN(Any, IsPOD) -CONSTANT_OWNERSHIP_BUILTIN(Any, IsConcrete) -CONSTANT_OWNERSHIP_BUILTIN(Any, IsBitwiseTakable) -CONSTANT_OWNERSHIP_BUILTIN(Any, IsSameMetatype) -CONSTANT_OWNERSHIP_BUILTIN(Any, Alignof) -CONSTANT_OWNERSHIP_BUILTIN(Any, AllocRaw) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssertConf) -CONSTANT_OWNERSHIP_BUILTIN(Any, UToSCheckedTrunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, SToSCheckedTrunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, SToUCheckedTrunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, UToUCheckedTrunc) -CONSTANT_OWNERSHIP_BUILTIN(Any, IntToFPWithOverflow) +CONSTANT_OWNERSHIP_BUILTIN(None, AShr) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericAShr) +CONSTANT_OWNERSHIP_BUILTIN(None, Add) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericAdd) +CONSTANT_OWNERSHIP_BUILTIN(None, And) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericAnd) +CONSTANT_OWNERSHIP_BUILTIN(None, AssumeNonNegative) +CONSTANT_OWNERSHIP_BUILTIN(None, AssumeTrue) +CONSTANT_OWNERSHIP_BUILTIN(None, BitCast) +CONSTANT_OWNERSHIP_BUILTIN(None, CondFailMessage) +CONSTANT_OWNERSHIP_BUILTIN(None, ExactSDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericExactSDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, ExactUDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericExactUDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, FAdd) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericFAdd) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_OEQ) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_OGE) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_OGT) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_OLE) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_OLT) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_ONE) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_UEQ) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_UGE) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_UGT) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_ULE) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_ULT) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_UNE) +CONSTANT_OWNERSHIP_BUILTIN(None, FDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericFDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, FMul) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericFMul) +CONSTANT_OWNERSHIP_BUILTIN(None, FNeg) +CONSTANT_OWNERSHIP_BUILTIN(None, FPExt) +CONSTANT_OWNERSHIP_BUILTIN(None, FPToSI) +CONSTANT_OWNERSHIP_BUILTIN(None, FPToUI) +CONSTANT_OWNERSHIP_BUILTIN(None, FPTrunc) +CONSTANT_OWNERSHIP_BUILTIN(None, FRem) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericFRem) +CONSTANT_OWNERSHIP_BUILTIN(None, FSub) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericFSub) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_EQ) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_NE) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_SGE) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_SGT) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_SLE) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_SLT) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_UGE) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_UGT) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_ULE) +CONSTANT_OWNERSHIP_BUILTIN(None, ICMP_ULT) +CONSTANT_OWNERSHIP_BUILTIN(None, IntToPtr) +CONSTANT_OWNERSHIP_BUILTIN(None, LShr) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericLShr) +CONSTANT_OWNERSHIP_BUILTIN(None, Mul) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericMul) +CONSTANT_OWNERSHIP_BUILTIN(None, Or) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericOr) +CONSTANT_OWNERSHIP_BUILTIN(None, PtrToInt) +CONSTANT_OWNERSHIP_BUILTIN(None, SAddOver) +CONSTANT_OWNERSHIP_BUILTIN(None, SDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericSDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, SExt) +CONSTANT_OWNERSHIP_BUILTIN(None, SExtOrBitCast) +CONSTANT_OWNERSHIP_BUILTIN(None, SIToFP) +CONSTANT_OWNERSHIP_BUILTIN(None, SMulOver) +CONSTANT_OWNERSHIP_BUILTIN(None, SRem) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericSRem) +CONSTANT_OWNERSHIP_BUILTIN(None, SSubOver) +CONSTANT_OWNERSHIP_BUILTIN(None, Expect) +CONSTANT_OWNERSHIP_BUILTIN(None, Shl) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericShl) +CONSTANT_OWNERSHIP_BUILTIN(None, Sub) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericSub) +CONSTANT_OWNERSHIP_BUILTIN(None, Trunc) +CONSTANT_OWNERSHIP_BUILTIN(None, TruncOrBitCast) +CONSTANT_OWNERSHIP_BUILTIN(None, UAddOver) +CONSTANT_OWNERSHIP_BUILTIN(None, UDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericUDiv) +CONSTANT_OWNERSHIP_BUILTIN(None, UIToFP) +CONSTANT_OWNERSHIP_BUILTIN(None, UMulOver) +CONSTANT_OWNERSHIP_BUILTIN(None, URem) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericURem) +CONSTANT_OWNERSHIP_BUILTIN(None, USubOver) +CONSTANT_OWNERSHIP_BUILTIN(None, Xor) +CONSTANT_OWNERSHIP_BUILTIN(None, GenericXor) +CONSTANT_OWNERSHIP_BUILTIN(None, ZExt) +CONSTANT_OWNERSHIP_BUILTIN(None, ZExtOrBitCast) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_ORD) +CONSTANT_OWNERSHIP_BUILTIN(None, FCMP_UNO) +CONSTANT_OWNERSHIP_BUILTIN(None, OnFastPath) +CONSTANT_OWNERSHIP_BUILTIN(None, IsOptionalType) +CONSTANT_OWNERSHIP_BUILTIN(None, Sizeof) +CONSTANT_OWNERSHIP_BUILTIN(None, Strideof) +CONSTANT_OWNERSHIP_BUILTIN(None, StringObjectOr) +CONSTANT_OWNERSHIP_BUILTIN(None, IsPOD) +CONSTANT_OWNERSHIP_BUILTIN(None, IsConcrete) +CONSTANT_OWNERSHIP_BUILTIN(None, IsBitwiseTakable) +CONSTANT_OWNERSHIP_BUILTIN(None, IsSameMetatype) +CONSTANT_OWNERSHIP_BUILTIN(None, Alignof) +CONSTANT_OWNERSHIP_BUILTIN(None, AllocRaw) +CONSTANT_OWNERSHIP_BUILTIN(None, AssertConf) +CONSTANT_OWNERSHIP_BUILTIN(None, UToSCheckedTrunc) +CONSTANT_OWNERSHIP_BUILTIN(None, SToSCheckedTrunc) +CONSTANT_OWNERSHIP_BUILTIN(None, SToUCheckedTrunc) +CONSTANT_OWNERSHIP_BUILTIN(None, UToUCheckedTrunc) +CONSTANT_OWNERSHIP_BUILTIN(None, IntToFPWithOverflow) // This is surprising, Builtin.unreachable returns a "Never" value which is // trivially typed. -CONSTANT_OWNERSHIP_BUILTIN(Any, Unreachable) +CONSTANT_OWNERSHIP_BUILTIN(None, Unreachable) /// AtomicRMW has type (Builtin.RawPointer, T) -> T. But it provides overloads /// for integer or rawpointer, so it should be trivial. -CONSTANT_OWNERSHIP_BUILTIN(Any, AtomicRMW) - -CONSTANT_OWNERSHIP_BUILTIN(Any, CondUnreachable) -CONSTANT_OWNERSHIP_BUILTIN(Any, UnsafeGuaranteedEnd) -CONSTANT_OWNERSHIP_BUILTIN(Any, GetObjCTypeEncoding) -CONSTANT_OWNERSHIP_BUILTIN(Any, CanBeObjCClass) -CONSTANT_OWNERSHIP_BUILTIN(Any, WillThrow) -CONSTANT_OWNERSHIP_BUILTIN(Any, StaticReport) - -CONSTANT_OWNERSHIP_BUILTIN(Any, DestroyArray) -CONSTANT_OWNERSHIP_BUILTIN(Any, CopyArray) -CONSTANT_OWNERSHIP_BUILTIN(Any, TakeArrayNoAlias) -CONSTANT_OWNERSHIP_BUILTIN(Any, TakeArrayFrontToBack) -CONSTANT_OWNERSHIP_BUILTIN(Any, TakeArrayBackToFront) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssignCopyArrayNoAlias) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssignCopyArrayFrontToBack) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssignCopyArrayBackToFront) -CONSTANT_OWNERSHIP_BUILTIN(Any, AssignTakeArray) -CONSTANT_OWNERSHIP_BUILTIN(Any, UnexpectedError) -CONSTANT_OWNERSHIP_BUILTIN(Any, ErrorInMain) -CONSTANT_OWNERSHIP_BUILTIN(Any, DeallocRaw) -CONSTANT_OWNERSHIP_BUILTIN(Any, Fence) -CONSTANT_OWNERSHIP_BUILTIN(Any, AtomicStore) -CONSTANT_OWNERSHIP_BUILTIN(Any, Once) -CONSTANT_OWNERSHIP_BUILTIN(Any, OnceWithContext) -CONSTANT_OWNERSHIP_BUILTIN(Any, TSanInoutAccess) -CONSTANT_OWNERSHIP_BUILTIN(Any, Swift3ImplicitObjCEntrypoint) -CONSTANT_OWNERSHIP_BUILTIN(Any, PoundAssert) -CONSTANT_OWNERSHIP_BUILTIN(Any, GlobalStringTablePointer) +CONSTANT_OWNERSHIP_BUILTIN(None, AtomicRMW) + +CONSTANT_OWNERSHIP_BUILTIN(None, CondUnreachable) +CONSTANT_OWNERSHIP_BUILTIN(None, UnsafeGuaranteedEnd) +CONSTANT_OWNERSHIP_BUILTIN(None, GetObjCTypeEncoding) +CONSTANT_OWNERSHIP_BUILTIN(None, CanBeObjCClass) +CONSTANT_OWNERSHIP_BUILTIN(None, WillThrow) +CONSTANT_OWNERSHIP_BUILTIN(None, StaticReport) + +CONSTANT_OWNERSHIP_BUILTIN(None, DestroyArray) +CONSTANT_OWNERSHIP_BUILTIN(None, CopyArray) +CONSTANT_OWNERSHIP_BUILTIN(None, TakeArrayNoAlias) +CONSTANT_OWNERSHIP_BUILTIN(None, TakeArrayFrontToBack) +CONSTANT_OWNERSHIP_BUILTIN(None, TakeArrayBackToFront) +CONSTANT_OWNERSHIP_BUILTIN(None, AssignCopyArrayNoAlias) +CONSTANT_OWNERSHIP_BUILTIN(None, AssignCopyArrayFrontToBack) +CONSTANT_OWNERSHIP_BUILTIN(None, AssignCopyArrayBackToFront) +CONSTANT_OWNERSHIP_BUILTIN(None, AssignTakeArray) +CONSTANT_OWNERSHIP_BUILTIN(None, UnexpectedError) +CONSTANT_OWNERSHIP_BUILTIN(None, ErrorInMain) +CONSTANT_OWNERSHIP_BUILTIN(None, DeallocRaw) +CONSTANT_OWNERSHIP_BUILTIN(None, Fence) +CONSTANT_OWNERSHIP_BUILTIN(None, AtomicStore) +CONSTANT_OWNERSHIP_BUILTIN(None, Once) +CONSTANT_OWNERSHIP_BUILTIN(None, OnceWithContext) +CONSTANT_OWNERSHIP_BUILTIN(None, TSanInoutAccess) +CONSTANT_OWNERSHIP_BUILTIN(None, Swift3ImplicitObjCEntrypoint) +CONSTANT_OWNERSHIP_BUILTIN(None, PoundAssert) +CONSTANT_OWNERSHIP_BUILTIN(None, GlobalStringTablePointer) #undef CONSTANT_OWNERSHIP_BUILTIN // Check all of these... -#define UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ID) \ +#define UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ID) \ ValueOwnershipKind ValueOwnershipKindBuiltinVisitor::visit##ID( \ BuiltinInst *BI, StringRef Attr) { \ if (BI->getType().isTrivial(*BI->getFunction())) { \ - return ValueOwnershipKind::Any; \ + return ValueOwnershipKind::None; \ } \ return ValueOwnershipKind::Unowned; \ } -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(CmpXChg) -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(AtomicLoad) -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ExtractElement) -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(InsertElement) -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ZeroInitializer) -#undef UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT +UNOWNED_OR_NONE_DEPENDING_ON_RESULT(CmpXChg) +UNOWNED_OR_NONE_DEPENDING_ON_RESULT(AtomicLoad) +UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ExtractElement) +UNOWNED_OR_NONE_DEPENDING_ON_RESULT(InsertElement) +UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ZeroInitializer) +#undef UNOWNED_OR_NONE_DEPENDING_ON_RESULT #define BUILTIN(X,Y,Z) #define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \ diff --git a/lib/SILGen/ASTVisitor.h b/lib/SILGen/ASTVisitor.h index e98f9b7bfa1a6..919bf08904ce7 100644 --- a/lib/SILGen/ASTVisitor.h +++ b/lib/SILGen/ASTVisitor.h @@ -60,11 +60,6 @@ class ASTVisitor : public swift::ASTVisitor(this)->visit(E->getSubExpr(), - std::forward(AA)...); - } - ExprRetTy visitVarargExpansionExpr(VarargExpansionExpr *E, Args... AA) { return static_cast(this)->visit(E->getSubExpr(), std::forward(AA)...); diff --git a/lib/SILGen/ArgumentSource.h b/lib/SILGen/ArgumentSource.h index f9e7399acee0d..016e47c4ea411 100644 --- a/lib/SILGen/ArgumentSource.h +++ b/lib/SILGen/ArgumentSource.h @@ -172,15 +172,22 @@ class ArgumentSource { bool isRValue() const & { return StoredKind == Kind::RValue; } bool isLValue() const & { return StoredKind == Kind::LValue; } - bool isDefaultArg() const { + /// Whether this argument is for a default argument that should be delayed. + /// Note that this will return false for caller-side default arguments which + /// are emitted directly. + bool isDelayedDefaultArg() const { switch (StoredKind) { case Kind::Invalid: llvm_unreachable("argument source is invalid"); case Kind::RValue: case Kind::LValue: return false; - case Kind::Expr: - return isa(asKnownExpr()); + case Kind::Expr: { + auto *defaultArg = dyn_cast(asKnownExpr()); + if (!defaultArg) + return false; + return !defaultArg->isCallerSide(); + } } llvm_unreachable("bad kind"); } diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index 8c89d430e6fdf..c144b14bf69ee 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -28,6 +28,7 @@ add_swift_host_library(swiftSILGen STATIC SILGenPattern.cpp SILGenPoly.cpp SILGenProlog.cpp + SILGenRequests.cpp SILGenStmt.cpp SILGenThunk.cpp SILGenType.cpp) diff --git a/lib/SILGen/ManagedValue.cpp b/lib/SILGen/ManagedValue.cpp index 74ec67a99065d..04cadbc5fe391 100644 --- a/lib/SILGen/ManagedValue.cpp +++ b/lib/SILGen/ManagedValue.cpp @@ -228,7 +228,7 @@ bool ManagedValue::isPlusOne(SILGenFunction &SGF) const { // If we have an object and the object has any ownership, the same // property applies. - if (getType().isObject() && getOwnershipKind() == ValueOwnershipKind::Any) + if (getType().isObject() && getOwnershipKind() == ValueOwnershipKind::None) return true; return hasCleanup(); diff --git a/lib/SILGen/ManagedValue.h b/lib/SILGen/ManagedValue.h index cf864cd9568a5..f96525d8a223e 100644 --- a/lib/SILGen/ManagedValue.h +++ b/lib/SILGen/ManagedValue.h @@ -78,7 +78,7 @@ class ManagedValue { : valueAndFlag(value, false), cleanup(cleanup) { assert(value && "No value specified?!"); assert((!getType().isObject() || - value.getOwnershipKind() != ValueOwnershipKind::Any || + value.getOwnershipKind() != ValueOwnershipKind::None || !hasCleanup()) && "Objects with trivial ownership should never have a cleanup"); } @@ -98,7 +98,7 @@ class ManagedValue { assert(value && "No value specified"); assert(value->getType().isObject() && "Expected borrowed rvalues to be objects"); - assert(value.getOwnershipKind() != ValueOwnershipKind::Any); + assert(value.getOwnershipKind() != ValueOwnershipKind::None); return ManagedValue(value, false, cleanup); } @@ -109,7 +109,7 @@ class ManagedValue { CleanupHandle cleanup) { assert(value && "No value specified"); assert(value->getType().isAddress() && "Expected value to be an address"); - assert(value.getOwnershipKind() == ValueOwnershipKind::Any && + assert(value.getOwnershipKind() == ValueOwnershipKind::None && "Addresses always have any ownership"); return ManagedValue(value, false, cleanup); } @@ -127,7 +127,7 @@ class ManagedValue { assert(value && "No value specified"); assert(value->getType().isObject() && "Expected borrowed rvalues to be objects"); - assert(value.getOwnershipKind() != ValueOwnershipKind::Any); + assert(value.getOwnershipKind() != ValueOwnershipKind::None); return ManagedValue(value, false, CleanupHandle::invalid()); } @@ -136,7 +136,7 @@ class ManagedValue { forBorrowedAddressRValue(SILValue value) { assert(value && "No value specified"); assert(value->getType().isAddress() && "Expected value to be an address"); - assert(value.getOwnershipKind() == ValueOwnershipKind::Any && + assert(value.getOwnershipKind() == ValueOwnershipKind::None && "Addresses always have trivial ownership"); return ManagedValue(value, false, CleanupHandle::invalid()); } @@ -152,14 +152,14 @@ class ManagedValue { /// Create a managed value for a +0 trivial object rvalue. static ManagedValue forTrivialObjectRValue(SILValue value) { assert(value->getType().isObject() && "Expected an object"); - assert(value.getOwnershipKind() == ValueOwnershipKind::Any); + assert(value.getOwnershipKind() == ValueOwnershipKind::None); return ManagedValue(value, false, CleanupHandle::invalid()); } /// Create a managed value for a +0 trivial address rvalue. static ManagedValue forTrivialAddressRValue(SILValue value) { assert(value->getType().isAddress() && "Expected an address"); - assert(value.getOwnershipKind() == ValueOwnershipKind::Any); + assert(value.getOwnershipKind() == ValueOwnershipKind::None); return ManagedValue(value, false, CleanupHandle::invalid()); } diff --git a/lib/SILGen/RValue.cpp b/lib/SILGen/RValue.cpp index 7a84b9bd68c58..9eaf85abb2df3 100644 --- a/lib/SILGen/RValue.cpp +++ b/lib/SILGen/RValue.cpp @@ -388,7 +388,7 @@ static void verifyHelper(ArrayRef values, NullablePtr SGF = nullptr) { // This is a no-op in non-assert builds. #ifndef NDEBUG - auto result = Optional(ValueOwnershipKind::Any); + auto result = Optional(ValueOwnershipKind::None); Optional sameHaveCleanups; for (ManagedValue v : values) { assert((!SGF || !v.getType().isLoadable(SGF.get()->F) || @@ -396,7 +396,7 @@ static void verifyHelper(ArrayRef values, "All loadable values in an RValue must be an object"); ValueOwnershipKind kind = v.getOwnershipKind(); - if (kind == ValueOwnershipKind::Any) + if (kind == ValueOwnershipKind::None) continue; // Merge together whether or not the RValue has cleanups. diff --git a/lib/SILGen/ResultPlan.cpp b/lib/SILGen/ResultPlan.cpp index 3cc27ef9be79f..4bf82704cbd1a 100644 --- a/lib/SILGen/ResultPlan.cpp +++ b/lib/SILGen/ResultPlan.cpp @@ -71,7 +71,7 @@ class IndirectOpenedSelfCleanup final : public Cleanup { void dump(SILGenFunction &SGF) const override { llvm::errs() << "IndirectOpenedSelfCleanup\n"; if (box) - box->dump(); + box->print(llvm::errs()); } }; @@ -105,8 +105,7 @@ mapTypeOutOfOpenedExistentialContext(CanType t) { MakeAbstractConformanceForGenericType()); return std::make_tuple(mappedTy->getCanonicalType(mappedSig), - mappedSig->getCanonicalSignature(), - mappedSubs); + mappedSig.getCanonicalSignature(), mappedSubs); } /// A result plan for an indirectly-returned opened existential value. @@ -149,11 +148,11 @@ class IndirectOpenedSelfResultPlan final : public ResultPlan { SubstitutionMap layoutSubs; std::tie(layoutTy, layoutSig, layoutSubs) = mapTypeOutOfOpenedExistentialContext(resultTy); - - auto boxLayout = SILLayout::get(SGF.getASTContext(), - layoutSig->getCanonicalSignature(), - SILField(layoutTy->getCanonicalType(layoutSig), true)); - + + auto boxLayout = + SILLayout::get(SGF.getASTContext(), layoutSig.getCanonicalSignature(), + SILField(layoutTy->getCanonicalType(layoutSig), true)); + resultBox = SGF.B.createAllocBox(loc, SILBoxType::get(SGF.getASTContext(), boxLayout, @@ -468,11 +467,12 @@ class ForeignErrorInitializationPlan final : public ResultPlan { : loc(loc), subPlan(std::move(subPlan)) { unsigned errorParamIndex = calleeTypeInfo.foreignError->getErrorParameterIndex(); + auto substFnType = calleeTypeInfo.substFnType; SILParameterInfo errorParameter = - calleeTypeInfo.substFnType->getParameters()[errorParamIndex]; + substFnType->getParameters()[errorParamIndex]; // We assume that there's no interesting reabstraction here beyond a layer // of optional. - errorPtrType = errorParameter.getType(); + errorPtrType = errorParameter.getArgumentType(SGF.SGM.M, substFnType); unwrappedPtrType = errorPtrType; Type unwrapped = errorPtrType->getOptionalObjectType(); isOptional = (bool) unwrapped; @@ -570,8 +570,10 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init, // need to make our own make SILResultInfo array. case ForeignErrorConvention::NilResult: { assert(allResults.size() == 1); - CanType objectType = allResults[0].getType().getOptionalObjectType(); - SILResultInfo optResult = allResults[0].getWithType(objectType); + auto substFnTy = calleeTypeInfo.substFnType; + CanType objectType = allResults[0].getReturnValueType(SGF.SGM.M, substFnTy) + .getOptionalObjectType(); + SILResultInfo optResult = allResults[0].getWithInterfaceType(objectType); allResults.clear(); allResults.push_back(optResult); break; @@ -598,12 +600,15 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init, // Otherwise, grab the next result. auto result = allResults.pop_back_val(); + auto calleeTy = calleeTypeInfo.substFnType; + // If the result is indirect, and we have an address to emit into, and // there are no abstraction differences, then just do it. if (init && init->canPerformInPlaceInitialization() && SGF.silConv.isSILIndirect(result) && !SGF.getLoweredType(substType).getAddressType().hasAbstractionDifference( - calleeTypeInfo.getOverrideRep(), result.getSILStorageType())) { + calleeTypeInfo.getOverrideRep(), + result.getSILStorageType(SGF.SGM.M, calleeTy))) { return ResultPlanPtr(new InPlaceInitializationResultPlan(init)); } @@ -618,7 +623,7 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init, // then we need to evaluate the arguments first in order to have access to // the opened Self type. A special result plan defers allocating the stack // slot to the point the call is emitted. - if (result.getType()->hasOpenedExistential() + if (result.getReturnValueType(SGF.SGM.M, calleeTy)->hasOpenedExistential() && SGF.silConv.isSILIndirect(result)) { return ResultPlanPtr( new IndirectOpenedSelfResultPlan(SGF, origType, substType)); @@ -627,7 +632,8 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init, // Create a temporary if the result is indirect. std::unique_ptr temporary; if (SGF.silConv.isSILIndirect(result)) { - auto &resultTL = SGF.getTypeLowering(result.getType()); + auto &resultTL = SGF.getTypeLowering( + result.getReturnValueType(SGF.SGM.M, calleeTy)); temporary = SGF.emitTemporary(loc, resultTL); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index c4be5e08136fc..4060f7c25c5a2 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -17,6 +17,7 @@ #include "SILGenFunctionBuilder.h" #include "Scope.h" #include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/Evaluator.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" @@ -34,6 +35,7 @@ #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILProfiler.h" +#include "swift/AST/SILGenRequests.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Serialization/SerializedSILLoader.h" #include "swift/Strings.h" @@ -49,15 +51,15 @@ using namespace Lowering; SILGenModule::SILGenModule(SILModule &M, ModuleDecl *SM) : M(M), Types(M.Types), SwiftModule(SM), TopLevelSGF(nullptr) { - SILOptions &Opts = M.getOptions(); + const SILOptions &Opts = M.getOptions(); if (!Opts.UseProfile.empty()) { auto ReaderOrErr = llvm::IndexedInstrProfReader::create(Opts.UseProfile); if (auto E = ReaderOrErr.takeError()) { diagnose(SourceLoc(), diag::profile_read_error, Opts.UseProfile, llvm::toString(std::move(E))); - Opts.UseProfile.erase(); + } else { + M.setPGOReader(std::move(ReaderOrErr.get())); } - M.setPGOReader(std::move(ReaderOrErr.get())); } } @@ -110,11 +112,12 @@ getBridgingFn(Optional &cacheSlot, // Check that the function takes the expected arguments and returns the // expected result type. SILDeclRef c(fd); - auto funcTy = SGM.Types.getConstantFunctionType(c); + auto funcTy = + SGM.Types.getConstantFunctionType(TypeExpansionContext::minimal(), c); SILFunctionConventions fnConv(funcTy, SGM.M); auto toSILType = [&SGM](Type ty) { - return SGM.Types.getLoweredType(ty, ResilienceExpansion::Minimal); + return SGM.Types.getLoweredType(ty, TypeExpansionContext::minimal()); }; if (inputTypes) { @@ -284,8 +287,10 @@ SILGenModule::getConformanceToObjectiveCBridgeable(SILLocation loc, Type type) { // Find the conformance to _ObjectiveCBridgeable. auto result = SwiftModule->lookupConformance(type, proto); - if (result) return result->getConcrete(); - return nullptr; + if (result.isInvalid()) + return nullptr; + + return result.getConcrete(); } ProtocolDecl *SILGenModule::getBridgedStoredNSError(SILLocation loc) { @@ -319,10 +324,11 @@ VarDecl *SILGenModule::getNSErrorRequirement(SILLocation loc) { return found; } -Optional +ProtocolConformanceRef SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) { auto proto = getBridgedStoredNSError(loc); - if (!proto) return None; + if (!proto) + return ProtocolConformanceRef::forInvalid(); // Find the conformance to _BridgedStoredNSError. return SwiftModule->lookupConformance(type, proto); @@ -333,8 +339,8 @@ ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { return *NSErrorConformanceToError; auto &ctx = getASTContext(); - auto nsError = ctx.getNSErrorDecl(); - if (!nsError) { + auto nsErrorTy = ctx.getNSErrorType(); + if (!nsErrorTy) { NSErrorConformanceToError = nullptr; return nullptr; } @@ -346,11 +352,10 @@ ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { } auto conformance = - SwiftModule->lookupConformance(nsError->getDeclaredInterfaceType(), - cast(error)); + SwiftModule->lookupConformance(nsErrorTy, cast(error)); - if (conformance && conformance->isConcrete()) - NSErrorConformanceToError = conformance->getConcrete(); + if (conformance.isConcrete()) + NSErrorConformanceToError = conformance.getConcrete(); else NSErrorConformanceToError = nullptr; return *NSErrorConformanceToError; @@ -413,10 +418,12 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, : ParameterConvention::Indirect_In_Guaranteed }, }; - auto extInfo = - SILFunctionType::ExtInfo(SILFunctionTypeRepresentation::Thin, - /*pseudogeneric*/false, - /*non-escaping*/false); + auto extInfo = SILFunctionType::ExtInfo( + SILFunctionTypeRepresentation::Thin, + /*pseudogeneric*/ false, + /*non-escaping*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr); auto functionTy = SILFunctionType::get(sig, extInfo, SILCoroutineKind::YieldOnce, @@ -425,6 +432,7 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, yields, /*results*/ {}, /*error result*/ {}, + SubstitutionMap(), false, getASTContext()); auto env = sig->getGenericEnvironment(); @@ -485,6 +493,7 @@ SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) { SILResultInfo(Int32Ty, ResultConvention::Unowned), None, + SubstitutionMap(), false, C); SILGenFunctionBuilder builder(*this); @@ -544,7 +553,7 @@ static bool haveProfiledAssociatedFunction(SILDeclRef constant) { /// Set up the function for profiling instrumentation. static void setUpForProfiling(SILDeclRef constant, SILFunction *F, ForDefinition_t forDefinition) { - if (!forDefinition) + if (!forDefinition || F->getProfiler()) return; ASTNode profiledNode; @@ -751,7 +760,7 @@ void SILGenModule::postEmitFunction(SILDeclRef constant, void SILGenModule:: emitMarkFunctionEscapeForTopLevelCodeGlobals(SILLocation loc, - const CaptureInfo &captureInfo) { + CaptureInfo captureInfo) { assert(TopLevelSGF && TopLevelSGF->B.hasValidInsertionPoint() && "no valid code generator for top-level function?!"); @@ -912,7 +921,6 @@ SILFunction *SILGenModule::emitClosure(AbstractClosureExpr *ce) { // initializer of the containing type. if (!f->isExternalDeclaration()) return f; - preEmitFunction(constant, ce, f, ce); PrettyStackTraceSILFunction X("silgen closureexpr", f); SILGenFunction(*this, *f, ce).emitClosure(ce); @@ -930,8 +938,8 @@ static bool requiresIVarInitialization(SILGenModule &SGM, ClassDecl *cd) { auto pbd = dyn_cast(member); if (!pbd) continue; - for (auto entry : pbd->getPatternList()) - if (entry.getExecutableInit()) + for (auto i : range(pbd->getNumPatternEntries())) + if (pbd->getExecutableInit(i)) return true; } @@ -943,8 +951,8 @@ bool SILGenModule::hasNonTrivialIVars(ClassDecl *cd) { auto *vd = dyn_cast(member); if (!vd || !vd->hasStorage()) continue; - auto &ti = Types.getTypeLowering(vd->getType(), - ResilienceExpansion::Maximal); + auto &ti = Types.getTypeLowering( + vd->getType(), TypeExpansionContext::maximalResilienceExpansionOnly()); if (!ti.isTrivial()) return true; } @@ -977,7 +985,7 @@ void SILGenModule::emitObjCAllocatorDestructor(ClassDecl *cd, // Emit the Objective-C -dealloc entry point if it has // something to do beyond messaging the superclass's -dealloc. - if (dd->hasBody() && dd->getBody()->getNumElements() != 0) + if (dd->hasBody() && !dd->getBody()->empty()) emitObjCDestructorThunk(dd); // Emit the ivar initializer, if needed. @@ -1056,7 +1064,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, llvm_unreachable("No default argument here?"); case DefaultArgumentKind::Normal: { - auto arg = param->getDefaultValue(); + auto arg = param->getTypeCheckedDefaultExpr(); emitOrDelayFunction(*this, constant, [this,constant,arg,initDC](SILFunction *f) { preEmitFunction(constant, arg, f, arg); @@ -1084,6 +1092,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, case DefaultArgumentKind::Inherited: case DefaultArgumentKind::Column: case DefaultArgumentKind::File: + case DefaultArgumentKind::FilePath: case DefaultArgumentKind::Line: case DefaultArgumentKind::Function: case DefaultArgumentKind::DSOHandle: @@ -1096,12 +1105,11 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, void SILGenModule:: emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { - const PatternBindingEntry &pbdEntry = pbd->getPatternList()[i]; - auto *var = pbdEntry.getAnchoringVarDecl(); - auto *init = pbdEntry.getInit(); - auto *initDC = pbdEntry.getInitContext(); - auto &captureInfo = pbdEntry.getCaptureInfo(); - assert(!pbdEntry.isInitializerSubsumed()); + auto *var = pbd->getAnchoringVarDecl(i); + auto *init = pbd->getInit(i); + auto *initDC = pbd->getInitContext(i); + auto captureInfo = pbd->getCaptureInfo(i); + assert(!pbd->isInitializerSubsumed(i)); // If this is the backing storage for a property with an attached wrapper // that was initialized with `=`, use that expression as the initializer. @@ -1110,8 +1118,8 @@ emitStoredPropertyInitialization(PatternBindingDecl *pbd, unsigned i) { ->isPropertyMemberwiseInitializedWithWrappedType()) { auto wrapperInfo = originalProperty->getPropertyWrapperBackingPropertyInfo(); - if (wrapperInfo.originalInitialValue) - init = wrapperInfo.originalInitialValue; + assert(wrapperInfo.originalInitialValue); + init = wrapperInfo.originalInitialValue; } } @@ -1145,10 +1153,11 @@ emitPropertyWrapperBackingInitializer(VarDecl *var) { preEmitFunction(constant, var, f, var); PrettyStackTraceSILFunction X( "silgen emitPropertyWrapperBackingInitializer", f); - f->createProfiler(var, constant, ForDefinition); - auto varDC = var->getInnermostDeclContext(); auto wrapperInfo = var->getPropertyWrapperBackingPropertyInfo(); assert(wrapperInfo.initializeFromOriginal); + f->createProfiler(wrapperInfo.initializeFromOriginal, constant, + ForDefinition); + auto varDC = var->getInnermostDeclContext(); SILGenFunction SGF(*this, *f, varDC); SGF.emitGeneratorFunction(constant, wrapperInfo.initializeFromOriginal); postEmitFunction(constant, f); @@ -1166,7 +1175,7 @@ SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, Type initType = FunctionType::get({}, TupleType::getEmpty(C), type->getExtInfo()); auto initSILType = cast( - Types.getLoweredRValueType(initType)); + Types.getLoweredRValueType(TypeExpansionContext::minimal(), initType)); SILGenFunctionBuilder builder(*this); auto *f = builder.createFunction( @@ -1304,7 +1313,7 @@ void SILGenModule::emitObjCDestructorThunk(DestructorDecl *destructor) { void SILGenModule::visitPatternBindingDecl(PatternBindingDecl *pd) { assert(!TopLevelSGF && "script mode PBDs should be in TopLevelCodeDecls"); - for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) + for (auto i : range(pd->getNumPatternEntries())) if (pd->getExecutableInit(i)) emitGlobalInitialization(pd, i); } @@ -1350,11 +1359,12 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl, if (auto genericEnv = decl->getInnermostDeclContext()->getGenericEnvironmentOfContext()) componentObjTy = genericEnv->mapTypeIntoContext(componentObjTy); - auto storageTy = M.Types.getSubstitutedStorageType(decl, componentObjTy); - auto opaqueTy = - M.Types.getLoweredRValueType(AbstractionPattern::getOpaque(), - componentObjTy); - + auto storageTy = M.Types.getSubstitutedStorageType( + TypeExpansionContext::minimal(), decl, componentObjTy); + auto opaqueTy = M.Types.getLoweredRValueType( + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion), + AbstractionPattern::getOpaque(), componentObjTy); + return storageTy.getASTType() == opaqueTy; } case AccessStrategy::DirectToAccessor: @@ -1662,7 +1672,7 @@ class SourceFileScope { void SILGenModule::emitSourceFile(SourceFile *sf) { SourceFileScope scope(*this, sf); FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-file", sf); - for (Decl *D : sf->Decls) { + for (Decl *D : sf->getTopLevelDecls()) { FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-decl", D); visit(D); } @@ -1683,8 +1693,8 @@ void SILGenModule::emitSourceFile(SourceFile *sf) { std::unique_ptr SILModule::constructSIL(ModuleDecl *mod, TypeConverter &tc, - SILOptions &options, FileUnit *SF) { - SharedTimer timer("SILGen"); + const SILOptions &options, FileUnit *SF) { + FrontendStatsTracer tracer(mod->getASTContext().Stats, "SILGen"); const DeclContext *DC; if (SF) { DC = SF; @@ -1743,12 +1753,25 @@ SILModule::constructSIL(ModuleDecl *mod, TypeConverter &tc, std::unique_ptr swift::performSILGeneration(ModuleDecl *mod, Lowering::TypeConverter &tc, - SILOptions &options) { - return SILModule::constructSIL(mod, tc, options, nullptr); + const SILOptions &options) { + auto desc = SILGenDescriptor::forWholeModule(mod, tc, options); + return llvm::cantFail(mod->getASTContext().evaluator(GenerateSILRequest{desc})); } std::unique_ptr swift::performSILGeneration(FileUnit &sf, Lowering::TypeConverter &tc, - SILOptions &options) { - return SILModule::constructSIL(sf.getParentModule(), tc, options, &sf); + const SILOptions &options) { + auto desc = SILGenDescriptor::forFile(sf, tc, options); + return llvm::cantFail(sf.getASTContext().evaluator(GenerateSILRequest{desc})); +} + +llvm::Expected> +GenerateSILRequest::evaluate(Evaluator &evaluator, SILGenDescriptor sgd) const { + if (auto *MD = sgd.context.dyn_cast()) { + return SILModule::constructSIL(MD, sgd.conv, sgd.opts, nullptr); + } else { + auto *SF = sgd.context.get(); + return SILModule::constructSIL(SF->getParentModule(), + sgd.conv, sgd.opts, SF); + } } diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 0acad1587419a..021a16e5de469 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -393,8 +393,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Find the conformance of the given Swift type to the /// _BridgedStoredNSError protocol. - Optional - getConformanceToBridgedStoredNSError(SILLocation loc, Type type); + ProtocolConformanceRef getConformanceToBridgedStoredNSError(SILLocation loc, + Type type); /// Retrieve the conformance of NSError to the Error protocol. ProtocolConformance *getNSErrorConformanceToError(); @@ -444,7 +444,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emit a `mark_function_escape` instruction for top-level code when a /// function or closure at top level refers to script globals. void emitMarkFunctionEscapeForTopLevelCodeGlobals(SILLocation loc, - const CaptureInfo &captureInfo); + CaptureInfo captureInfo); /// Map the substitutions for the original declaration to substitutions for /// the overridden declaration. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 748f2d8bad3c3..fcdbcffd06c9f 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -55,10 +55,8 @@ SubstitutionMap SILGenModule::mapSubstitutionsForWitnessOverride( Type origProtoSelfType = origProto->getSelfInterfaceType(); auto baseProto = cast(overridden->getDeclContext()); return SubstitutionMap::getProtocolSubstitutions( - baseProto, - origProtoSelfType.subst(subs), - *subs.lookupConformance(origProtoSelfType->getCanonicalType(), - baseProto)); + baseProto, origProtoSelfType.subst(subs), + subs.lookupConformance(origProtoSelfType->getCanonicalType(), baseProto)); } /// Return the abstraction pattern to use when calling a function value. @@ -92,7 +90,8 @@ getIndirectApplyAbstractionPattern(SILGenFunction &SGF, static CanFunctionType getPartialApplyOfDynamicMethodFormalType(SILGenModule &SGM, SILDeclRef member, ConcreteDeclRef memberRef) { - auto memberCI = SGM.Types.getConstantInfo(member); + auto memberCI = + SGM.Types.getConstantInfo(TypeExpansionContext::minimal(), member); // Construct a non-generic version of the formal type. // This works because we're only using foreign members, where presumably @@ -135,7 +134,8 @@ getDynamicMethodLoweredType(SILModule &M, auto objcFormalTy = substMemberTy.withExtInfo(substMemberTy->getExtInfo() .withSILRepresentation(SILFunctionTypeRepresentation::ObjCMethod)); return SILType::getPrimitiveObjectType( - M.Types.getUncachedSILFunctionTypeForConstant(constant, objcFormalTy)); + M.Types.getUncachedSILFunctionTypeForConstant( + TypeExpansionContext::minimal(), constant, objcFormalTy)); } /// Check if we can perform a dynamic dispatch on a super method call. @@ -384,34 +384,39 @@ class Callee { SubstitutionMap subs, SILLocation l, bool callPreviousDynamicReplaceableImpl = false) { - auto &ci = SGF.getConstantInfo(c); - return Callee(SGF, c, ci.FormalPattern, ci.FormalType, subs, l, - callPreviousDynamicReplaceableImpl); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l, + callPreviousDynamicReplaceableImpl); } static Callee forEnumElement(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { assert(isa(c.getDecl())); - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::EnumElement, SGF, c, ci.FormalPattern, - ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::EnumElement, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forClassMethod(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { auto base = c.getOverriddenVTableEntry(); - auto &baseCI = SGF.getConstantInfo(base); - auto &derivedCI = SGF.getConstantInfo(c); - return Callee(Kind::ClassMethod, SGF, c, - baseCI.FormalPattern, derivedCI.FormalType, subs, l); + auto &baseCI = SGF.getConstantInfo(SGF.getTypeExpansionContext(), base); + auto &derivedCI = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::ClassMethod, SGF, c, baseCI.FormalPattern, derivedCI.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forSuperMethod(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::SuperMethod, SGF, c, - ci.FormalPattern, ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::SuperMethod, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forWitnessMethod(SILGenFunction &SGF, CanType protocolSelfType, @@ -432,15 +437,16 @@ class Callee { subs); } - auto &ci = SGF.getConstantInfo(c); - return Callee(Kind::WitnessMethod, SGF, c, ci.FormalPattern, - ci.FormalType, subs, l); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); + return Callee( + Kind::WitnessMethod, SGF, c, ci.FormalPattern, ci.FormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } static Callee forDynamic(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap constantSubs, CanAnyFunctionType substFormalType, SubstitutionMap subs, SILLocation l) { - auto &ci = SGF.getConstantInfo(c); + auto &ci = SGF.getConstantInfo(SGF.getTypeExpansionContext(), c); AbstractionPattern origFormalType = ci.FormalPattern; // Replace the original self type with the partially-applied subst type. @@ -458,8 +464,9 @@ class Callee { } origFormalType.rewriteType(CanGenericSignature(), origFormalFnType); - return Callee(Kind::DynamicMethod, SGF, c, origFormalType, - substFormalType, subs, l); + return Callee( + Kind::DynamicMethod, SGF, c, origFormalType, substFormalType, + subs.mapIntoTypeExpansionContext(SGF.getTypeExpansionContext()), l); } Callee(Callee &&) = default; @@ -536,8 +543,8 @@ class Callee { CalleeTypeInfo result; result.substFnType = - formalFnType.castTo()->substGenericArgs(SGF.SGM.M, - Substitutions); + formalFnType.castTo()->substGenericArgs( + SGF.SGM.M, Substitutions, SGF.getTypeExpansionContext()); if (!constant || !constant->isForeign) return result; @@ -580,7 +587,8 @@ class Callee { // If the call is curried, emit a direct call to the curry thunk. if (constant->isCurried) { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo); return ManagedValue::forUnmanaged(ref); } @@ -592,18 +600,21 @@ class Callee { return IndirectValue; case Kind::EnumElement: case Kind::StandaloneFunction: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo); return ManagedValue::forUnmanaged(ref); } case Kind::StandaloneFunctionDynamicallyReplaceableImpl: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo, true); return ManagedValue::forUnmanaged(ref); } case Kind::ClassMethod: { - auto methodTy = SGF.SGM.Types.getConstantOverrideType(*constant); + auto methodTy = SGF.SGM.Types.getConstantOverrideType( + SGF.getTypeExpansionContext(), *constant); // Otherwise, do the dynamic dispatch inline. ArgumentScope S(SGF, Loc); @@ -628,8 +639,8 @@ class Callee { SGF, Loc, *borrowedSelf); auto base = constant->getOverriddenVTableEntry(); - auto constantInfo = - SGF.SGM.Types.getConstantOverrideInfo(*constant, base); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant, base); ManagedValue fn; if (!constant->isForeign) { @@ -643,12 +654,14 @@ class Callee { return fn; } case Kind::WitnessMethod: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); + // TODO: substOpaqueTypesWithUnderlyingTypes ... auto proto = cast(Constant.getDecl()->getDeclContext()); auto selfType = proto->getSelfInterfaceType()->getCanonicalType(); auto lookupType = selfType.subst(Substitutions)->getCanonicalType(); - auto conformance = *Substitutions.lookupConformance(selfType, proto); + auto conformance = Substitutions.lookupConformance(selfType, proto); ArgumentScope S(SGF, Loc); @@ -689,7 +702,8 @@ class Callee { // If the call is curried, emit a direct call to the curry thunk. if (constant->isCurried) { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } } @@ -701,26 +715,30 @@ class Callee { case Kind::StandaloneFunctionDynamicallyReplaceableImpl: case Kind::StandaloneFunction: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::EnumElement: { // Emit a direct call to the element constructor thunk. - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::ClassMethod: { - auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo(*constant); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::SuperMethod: { auto base = constant->getOverriddenVTableEntry(); - auto constantInfo = - SGF.SGM.Types.getConstantOverrideInfo(*constant, base); + auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo( + SGF.getTypeExpansionContext(), *constant, base); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::WitnessMethod: { - auto constantInfo = SGF.getConstantInfo(*constant); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), *constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); } case Kind::DynamicMethod: { @@ -771,8 +789,7 @@ bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, bool &isObjCReplacementSelfCall) { if (auto *func = dyn_cast_or_null(SGF.FunctionDC->getAsDecl())) { - auto *repl = func->getAttrs().getAttribute(); - if (repl && repl->getReplacedFunction() == afd) { + if (func->getDynamicallyReplacedDecl() == afd) { isObjCReplacementSelfCall = afd->isObjC(); return true; } @@ -1787,7 +1804,8 @@ static void emitRawApply(SILGenFunction &SGF, rawResults.push_back(result); SILBasicBlock *errorBB = - SGF.getTryApplyErrorDest(loc, substFnType->getErrorResult(), + SGF.getTryApplyErrorDest(loc, substFnType, + substFnType->getErrorResult(), options & ApplyOptions::DoesNotThrow); SGF.B.createTryApply(loc, fnValue, subs, argValues, @@ -1868,15 +1886,17 @@ class ClaimedParamsRef { public: static constexpr const unsigned NoSkip = (unsigned)-1; private: + CanSILFunctionType FnTy; ArrayRef Params; // The index of the param excluded from this range, if any, or ~0. unsigned SkipParamIndex; friend struct ParamLowering; - explicit ClaimedParamsRef(ArrayRef params, + explicit ClaimedParamsRef(CanSILFunctionType fnTy, + ArrayRef params, unsigned skip) - : Params(params), SkipParamIndex(skip) + : FnTy(fnTy), Params(params), SkipParamIndex(skip) { // Eagerly chop a skipped parameter off either end. if (SkipParamIndex == 0) { @@ -1890,10 +1910,13 @@ class ClaimedParamsRef { return SkipParamIndex != (unsigned)NoSkip; } public: - ClaimedParamsRef() : Params({}), SkipParamIndex(-1) {} - explicit ClaimedParamsRef(ArrayRef params) - : Params(params), SkipParamIndex(NoSkip) + ClaimedParamsRef() : FnTy(), Params({}), SkipParamIndex(-1) {} + explicit ClaimedParamsRef(CanSILFunctionType fnTy, + ArrayRef params) + : FnTy(fnTy), Params(params), SkipParamIndex(NoSkip) {} + + CanSILFunctionType getFunctionType() const { return FnTy; } struct iterator : public std::iterator @@ -2003,20 +2026,21 @@ class ClaimedParamsRef { ClaimedParamsRef slice(unsigned start) const { if (start >= SkipParamIndex) - return ClaimedParamsRef(Params.slice(start + 1), NoSkip); - return ClaimedParamsRef(Params.slice(start), + return ClaimedParamsRef(FnTy, Params.slice(start + 1), NoSkip); + return ClaimedParamsRef(FnTy, + Params.slice(start), hasSkip() ? SkipParamIndex - start : NoSkip); } ClaimedParamsRef slice(unsigned start, unsigned count) const { if (start >= SkipParamIndex) - return ClaimedParamsRef(Params.slice(start + 1, count), NoSkip); + return ClaimedParamsRef(FnTy, Params.slice(start + 1, count), NoSkip); unsigned newSkip = SkipParamIndex; if (hasSkip()) newSkip -= start; if (newSkip < count) - return ClaimedParamsRef(Params.slice(start, count+1), newSkip); - return ClaimedParamsRef(Params.slice(start, count), NoSkip); + return ClaimedParamsRef(FnTy, Params.slice(start, count+1), newSkip); + return ClaimedParamsRef(FnTy, Params.slice(start, count), NoSkip); } }; @@ -2609,9 +2633,9 @@ class ArgEmitter { // origParamType is a parameter type. void emitSingleArg(ArgumentSource &&arg, AbstractionPattern origParamType) { - // If this is default argument, prepare to emit the default argument + // If this is delayed default argument, prepare to emit the default argument // generator later. - if (arg.isDefaultArg()) { + if (arg.isDelayedDefaultArg()) { auto substParamType = arg.getSubstRValueType(); auto defArg = std::move(arg).asKnownDefaultArg(); @@ -2686,9 +2710,9 @@ class ArgEmitter { loweredSubstArgType = SILType::getPrimitiveAddressType(loweredSubstArgType.getASTType()); } - SILType loweredSubstParamType = - SILType::getPrimitiveType(param.getType(), - loweredSubstArgType.getCategory()); + SILType loweredSubstParamType = SILType::getPrimitiveType( + param.getArgumentType(SGF.SGM.M, ParamInfos.getFunctionType()), + loweredSubstArgType.getCategory()); // If the caller takes the argument indirectly, the argument has an // inout type. @@ -2705,7 +2729,7 @@ class ArgEmitter { return; } - if (SGF.silConv.isSILIndirect(param)) { + if (SGF.silConv.isSILIndirect(param)) { emitIndirect(std::move(arg), loweredSubstArgType, origParamType, param); return; } @@ -2812,7 +2836,7 @@ class ArgEmitter { // Otherwise, simultaneously emit and reabstract. } else { result = std::move(arg).materialize(SGF, origParamType, - SGF.getSILType(param)); + SGF.getSILType(param, ParamInfos.getFunctionType())); } Args.push_back(result); @@ -2920,9 +2944,9 @@ class ArgEmitter { arg.getSubstRValueType()); case SILFunctionLanguage::C: return Conversion::getBridging(Conversion::BridgeToObjC, - arg.getSubstRValueType(), - origParamType.getType(), - param.getSILStorageType()); + arg.getSubstRValueType(), + origParamType.getType(), + param.getSILStorageType(SGF.SGM.M, ParamInfos.getFunctionType())); } llvm_unreachable("bad language"); }(); @@ -3144,10 +3168,10 @@ class ArgEmitter { /// If the context requires reabstraction bool RequiresReabstraction; }; - static EmissionContexts getRValueEmissionContexts(SILType loweredArgType, - SILParameterInfo param) { - bool requiresReabstraction = - loweredArgType.getASTType() != param.getType(); + EmissionContexts getRValueEmissionContexts(SILType loweredArgType, + SILParameterInfo param) { + bool requiresReabstraction = loweredArgType.getASTType() + != param.getArgumentType(SGF.SGM.M, ParamInfos.getFunctionType()); // If the parameter is consumed, we have to emit at +1. if (param.isConsumed()) { return {SGFContext(), requiresReabstraction}; @@ -3235,7 +3259,8 @@ static void emitBorrowedLValueRecursive(SILGenFunction &SGF, value = SGF.B.createFormalAccessLoadBorrow(loc, value); } - assert(param.getType() == value.getType().getASTType()); + assert(param.getArgumentType(SGF.SGM.M, params.getFunctionType()) + == value.getType().getASTType()); args[argIndex++] = value; } @@ -3372,7 +3397,8 @@ struct ParamLowering { return {}; } ClaimedForeignSelf = foreignSelf.getSelfIndex(); - return ClaimedParamsRef(Params[ClaimedForeignSelf], + return ClaimedParamsRef(fnConv.funcTy, + Params[ClaimedForeignSelf], ClaimedParamsRef::NoSkip); } @@ -3381,13 +3407,13 @@ struct ParamLowering { "not claiming all params after foreign self?!"); auto result = Params; Params = {}; - return ClaimedParamsRef(result, ClaimedForeignSelf); + return ClaimedParamsRef(fnConv.funcTy, result, ClaimedForeignSelf); } assert(count <= Params.size()); auto result = Params.slice(Params.size() - count, count); Params = Params.slice(0, Params.size() - count); - return ClaimedParamsRef(result, (unsigned)-1); + return ClaimedParamsRef(fnConv.funcTy, result, (unsigned)-1); } ArrayRef @@ -3944,8 +3970,8 @@ CallEmission::applyPartiallyAppliedSuperMethod(SGFContext C) { // because that's what the partially applied super method expects; firstLevelResult.formalType = callee.getSubstFormalType(); auto origFormalType = AbstractionPattern(firstLevelResult.formalType); - auto substFnType = - SGF.getSILFunctionType(origFormalType, firstLevelResult.formalType); + auto substFnType = SGF.getSILFunctionType( + SGF.getTypeExpansionContext(), origFormalType, firstLevelResult.formalType); // Emit the arguments. SmallVector uncurriedArgs; @@ -3971,7 +3997,8 @@ CallEmission::applyPartiallyAppliedSuperMethod(SGFContext C) { // partial_apply. upcastedSelf = upcastedSelf.ensurePlusOne(SGF, loc); - auto constantInfo = SGF.getConstantInfo(callee.getMethodName()); + auto constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), callee.getMethodName()); auto functionTy = constantInfo.getSILType(); ManagedValue superMethod; { @@ -4012,8 +4039,8 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // expect. firstLevelResult.formalType = callee.getSubstFormalType(); auto origFormalType = AbstractionPattern(firstLevelResult.formalType); - auto substFnType = - SGF.getSILFunctionType(origFormalType, firstLevelResult.formalType); + auto substFnType = SGF.getSILFunctionType( + SGF.getTypeExpansionContext(), origFormalType, firstLevelResult.formalType); // If we have an early emitter, just let it take over for the // uncurried call site. @@ -4345,7 +4372,8 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) { if (method->isStatic()) return false; - auto fnType = M.Types.getConstantFunctionType(methodRef); + auto fnType = M.Types.getConstantFunctionType(TypeExpansionContext::minimal(), + methodRef); auto importAsMember = method->getImportAsMemberStatus(); SILParameterInfo self; @@ -4431,7 +4459,7 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, // Emit the raw application. GenericSignature genericSig = - fn.getType().castTo()->getGenericSignature(); + fn.getType().castTo()->getInvocationGenericSignature(); // When calling a closure that's defined in a generic context but does not // capture any generic parameters, we will have substitutions, but the @@ -4442,8 +4470,8 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, // Otherwise, the substitutions should match the generic signature. } else { - assert(genericSig->getCanonicalSignature() == - subs.getGenericSignature()->getCanonicalSignature()); + assert(genericSig.getCanonicalSignature() == + subs.getGenericSignature().getCanonicalSignature()); } auto rawDirectResult = [&] { @@ -4457,7 +4485,7 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, // Pop the argument scope. argScope.pop(); - if (substFnType->isNoReturnFunction()) + if (substFnType->isNoReturnFunction(SGM.M)) loc.markAutoGenerated(); // Explode the direct results. @@ -4465,7 +4493,8 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan, SmallVector directResults; auto addManagedDirectResult = [&](SILValue result, const SILResultInfo &resultInfo) { - auto &resultTL = getTypeLowering(resultInfo.getType()); + auto &resultTL = + getTypeLowering(resultInfo.getReturnValueType(SGM.M, substFnType)); switch (resultInfo.getConvention()) { case ResultConvention::Indirect: @@ -4579,9 +4608,6 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn, SILValue error = errorBB->createPhiArgument(fnConv.getSILErrorType(), ValueOwnershipKind::Owned); - B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"), - SGM.Types.getEmptyTupleType(), {}, {error}); - Cleanups.emitCleanupsForReturn(CleanupLocation::get(loc), IsForUnwind); B.createThrow(loc, error); } @@ -4632,17 +4658,19 @@ void SILGenFunction::emitYield(SILLocation loc, SmallVector yieldArgs; SmallVector delayedArgs; - auto fnType = F.getLoweredFunctionType(); + auto fnType = F.getLoweredFunctionTypeInContext(getTypeExpansionContext()); SmallVector substYieldTys; for (auto origYield : fnType->getYields()) { substYieldTys.push_back({ - F.mapTypeIntoContext(origYield.getType())->getCanonicalType(), + F.mapTypeIntoContext(origYield.getArgumentType(SGM.M, fnType)) + ->getCanonicalType(), origYield.getConvention() }); } ArgEmitter emitter(*this, fnType->getRepresentation(), /*yield*/ true, - /*isForCoroutine*/ false, ClaimedParamsRef(substYieldTys), + /*isForCoroutine*/ false, + ClaimedParamsRef(fnType, substYieldTys), yieldArgs, delayedArgs, /*foreign error*/ None, ImportAsMemberStatus()); @@ -4730,7 +4758,8 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc, // careful to stage the cleanups so that if the expression // throws, we know to deallocate the uninitialized box. if (element->isIndirect() || element->getParentEnum()->isIndirect()) { - auto boxTy = SGM.M.Types.getBoxTypeForEnumElement(enumTy, element); + auto boxTy = SGM.M.Types.getBoxTypeForEnumElement(getTypeExpansionContext(), + enumTy, element); auto *box = B.createAllocBox(loc, boxTy); auto *addr = B.createProjectBox(loc, box, 0); @@ -4836,15 +4865,32 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc, finalArgs, calleeTypeInfo, ApplyOptions::None, ctx); } -static StringRef -getMagicFunctionString(SILGenFunction &SGF) { - assert(SGF.MagicFunctionName +StringRef SILGenFunction::getMagicFunctionString() { + assert(MagicFunctionName && "asking for #function but we don't have a function name?!"); - if (SGF.MagicFunctionString.empty()) { - llvm::raw_string_ostream os(SGF.MagicFunctionString); - SGF.MagicFunctionName.print(os); + if (MagicFunctionString.empty()) { + llvm::raw_string_ostream os(MagicFunctionString); + MagicFunctionName.print(os); } - return SGF.MagicFunctionString; + return MagicFunctionString; +} + +StringRef SILGenFunction::getMagicFilePathString(SourceLoc loc) { + assert(loc.isValid()); + return getSourceManager().getDisplayNameForLoc(loc); +} + +std::string SILGenFunction::getMagicFileString(SourceLoc loc) { + auto path = getMagicFilePathString(loc); + + if (!getASTContext().LangOpts.EnableConcisePoundFile) + return path; + + auto value = llvm::sys::path::filename(path).str(); + value += " ("; + value += getModule().getSwiftModule()->getNameStr(); + value += ")"; + return value; } /// Emit an application of the given allocating initializer. @@ -4858,7 +4904,7 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, // Form the reference to the allocating initializer. auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator) .asForeign(requiresForeignEntryPoint(ctor)); - auto initConstant = getConstantInfo(initRef); + auto initConstant = getConstantInfo(getTypeExpansionContext(), initRef); auto subs = init.getSubstitutions(); // Scope any further writeback just within this operation. @@ -4869,9 +4915,10 @@ RValue SILGenFunction::emitApplyAllocatingInitializer(SILLocation loc, SILType selfMetaTy; { // Determine the self metatype type. - CanSILFunctionType substFnType = - initConstant.SILFnType->substGenericArgs(SGM.M, subs); - SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter()); + CanSILFunctionType substFnType = initConstant.SILFnType->substGenericArgs( + SGM.M, subs, getTypeExpansionContext()); + SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter(), + substFnType); if (overriddenSelfType) { // If the 'self' type has been overridden, form a metatype to the @@ -4961,7 +5008,7 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, // Form the reference to the method. auto callRef = SILDeclRef(call, SILDeclRef::Kind::Func) .asForeign(requiresForeignEntryPoint(declRef.getDecl())); - auto declRefConstant = getConstantInfo(callRef); + auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); auto subs = declRef.getSubstitutions(); // Scope any further writeback just within this operation. @@ -4973,8 +5020,10 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, { // Determine the self metatype type. CanSILFunctionType substFnType = - declRefConstant.SILFnType->substGenericArgs(SGM.M, subs); - SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter()); + declRefConstant.SILFnType->substGenericArgs(SGM.M, subs, + getTypeExpansionContext()); + SILType selfParamMetaTy = getSILType(substFnType->getSelfParameter(), + substFnType); selfMetaTy = selfParamMetaTy; } @@ -5099,9 +5148,16 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { auto magicLiteral = cast(literal); switch (magicLiteral->getKind()) { case MagicIdentifierLiteralExpr::File: { - std::string value; - if (loc.isValid()) - value = ctx.SourceMgr.getDisplayNameForLoc(loc); + std::string value = loc.isValid() ? getMagicFileString(loc) : ""; + builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, + magicLiteral->getStringEncoding()); + builtinInit = magicLiteral->getBuiltinInitializer(); + init = magicLiteral->getInitializer(); + break; + } + + case MagicIdentifierLiteralExpr::FilePath: { + StringRef value = loc.isValid() ? getMagicFilePathString(loc) : ""; builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, magicLiteral->getStringEncoding()); builtinInit = magicLiteral->getBuiltinInitializer(); @@ -5110,9 +5166,7 @@ RValue SILGenFunction::emitLiteral(LiteralExpr *literal, SGFContext C) { } case MagicIdentifierLiteralExpr::Function: { - StringRef value = ""; - if (loc.isValid()) - value = getMagicFunctionString(*this); + StringRef value = loc.isValid() ? getMagicFunctionString() : ""; builtinLiteralArgs = emitStringLiteral(*this, literal, value, C, magicLiteral->getStringEncoding()); builtinInit = magicLiteral->getBuiltinInitializer(); @@ -5478,8 +5532,8 @@ AccessorBaseArgPreparer::AccessorBaseArgPreparer(SILGenFunction &SGF, CanType baseFormalType, SILDeclRef accessor) : SGF(SGF), loc(loc), base(base), baseFormalType(baseFormalType), - accessor(accessor), - selfParam(SGF.SGM.Types.getConstantSelfParameter(accessor)), + accessor(accessor), selfParam(SGF.SGM.Types.getConstantSelfParameter( + SGF.getTypeExpansionContext(), accessor)), baseLoweredType(base.getType()) { assert(!base.isInContext()); assert(!base.isLValue() || !base.hasCleanup()); @@ -5543,6 +5597,7 @@ static void emitPseudoFunctionArguments(SILGenFunction &SGF, SmallVectorImpl &outVals, PreparedArguments &&args) { auto substParams = substFnType->getParams(); + auto &tl = SGF.getTypeLowering(origFnType, substFnType); SmallVector substParamTys; for (auto substParam : substParams) { @@ -5554,10 +5609,12 @@ static void emitPseudoFunctionArguments(SILGenFunction &SGF, SmallVector delayedArgs; ArgEmitter emitter(SGF, SILFunctionTypeRepresentation::Thin, - /*yield*/ false, - /*isForCoroutine*/ false, ClaimedParamsRef(substParamTys), - argValues, delayedArgs, - /*foreign error*/ None, ImportAsMemberStatus()); + /*yield*/ false, + /*isForCoroutine*/ false, + ClaimedParamsRef(tl.getLoweredType().castTo(), + substParamTys), + argValues, delayedArgs, + /*foreign error*/ None, ImportAsMemberStatus()); emitter.emitPreparedArgs(std::move(args), origFnType); @@ -6052,6 +6109,39 @@ RValue SILGenFunction::emitDynamicSubscriptExpr(DynamicSubscriptExpr *e, return RValue(*this, e, emitManagedRValueWithCleanup(optResult, optTL)); } +SmallVector SILGenFunction::emitKeyPathSubscriptOperands( + SubscriptDecl *subscript, SubstitutionMap subs, Expr *indexExpr) { + Type interfaceType = subscript->getInterfaceType(); + CanFunctionType substFnType = + subs ? cast(interfaceType->castTo() + ->substGenericArgs(subs) + ->getCanonicalType()) + : cast(interfaceType->getCanonicalType()); + AbstractionPattern origFnType(substFnType); + auto fnType = + getLoweredType(origFnType, substFnType).castTo(); + + SmallVector argValues; + SmallVector delayedArgs; + ArgEmitter emitter(*this, fnType->getRepresentation(), + /*yield*/ false, + /*isForCoroutine*/ false, + ClaimedParamsRef(fnType, fnType->getParameters()), + argValues, delayedArgs, + /*foreign error*/ None, ImportAsMemberStatus()); + + auto prepared = + prepareSubscriptIndices(subscript, subs, + // Strategy doesn't matter + AccessStrategy::getStorage(), indexExpr); + emitter.emitPreparedArgs(std::move(prepared), origFnType); + + if (!delayedArgs.empty()) + emitDelayedArguments(*this, delayedArgs, argValues); + + return argValues; +} + ManagedValue ArgumentScope::popPreservingValue(ManagedValue mv) { formalEvalScope.pop(); return normalScope.popPreservingValue(mv); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 7439006b6a169..9ecb35559c539 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -77,8 +77,7 @@ static bool shouldBridgeThroughError(SILGenModule &SGM, CanType type, } } - auto optConf = SGM.SwiftModule->lookupConformance(type, errorProtocol); - return optConf.hasValue(); + return (bool)SGM.SwiftModule->lookupConformance(type, errorProtocol); } /// Bridge the given Swift value to its corresponding Objective-C @@ -124,7 +123,8 @@ emitBridgeNativeToObjectiveC(SILGenFunction &SGF, SGF.SGM.SwiftModule, dc); // Substitute into the witness function type. - witnessFnTy = witnessFnTy.substGenericArgs(SGF.SGM.M, typeSubMap); + witnessFnTy = witnessFnTy.substGenericArgs(SGF.SGM.M, typeSubMap, + SGF.getTypeExpansionContext()); // We might have to re-abstract the 'self' value if it is an // Optional. @@ -201,11 +201,12 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, auto witnessFnTy = witnessRef->getType().castTo(); CanType swiftValueType = conformance->getType()->getCanonicalType(); - auto genericSig = witnessFnTy->getGenericSignature(); + auto genericSig = witnessFnTy->getInvocationGenericSignature(); SubstitutionMap typeSubMap = witness.getSubstitutions(); // Substitute into the witness function type. - witnessFnTy = witnessFnTy->substGenericArgs(SGF.SGM.M, typeSubMap); + witnessFnTy = witnessFnTy->substGenericArgs(SGF.SGM.M, typeSubMap, + SGF.getTypeExpansionContext()); // The witness takes an _ObjectiveCType?, so convert to that type. CanType desiredValueType = OptionalType::get(objcType)->getCanonicalType(); @@ -214,19 +215,20 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF, // Call the witness. auto metatypeParam = witnessFnTy->getParameters()[1]; - assert(isa(metatypeParam.getType()) && - cast(metatypeParam.getType()).getInstanceType() + assert(isa(metatypeParam.getInterfaceType()) && + cast(metatypeParam.getInterfaceType()).getInstanceType() == swiftValueType); SILValue metatypeValue = - SGF.B.createMetatype(loc, metatypeParam.getSILStorageType()); + SGF.B.createMetatype(loc, + metatypeParam.getSILStorageType(SGF.SGM.M, witnessFnTy)); - auto witnessCI = SGF.getConstantInfo(witnessConstant); + auto witnessCI = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), witnessConstant); CanType formalResultTy = witnessCI.LoweredType.getResult(); auto subs = witness.getSubstitutions(); // Set up the generic signature, since formalResultTy is an interface type. - GenericContextScope genericContextScope(SGF.SGM.Types, genericSig); CalleeTypeInfo calleeTypeInfo( witnessFnTy, AbstractionPattern(genericSig, formalResultTy), @@ -367,7 +369,7 @@ static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, formalBlockType = getBridgedBlockType(SGF.SGM, formalBlockType); // Set up the indirect result. - SILType blockResultTy = blockTy->getAllResultsType(); + SILType blockResultTy = blockTy->getAllResultsSubstType(SGF.SGM.M); SILValue indirectResult; if (blockTy->getNumResults() != 0) { auto result = blockTy->getSingleResult(); @@ -402,7 +404,7 @@ static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, // If the parameter is a block, we need to copy it to ensure it lives on // the heap. The adapted closure value might outlive the block's original // scope. - if (SGF.getSILType(param).isBlockPointerCompatible()) { + if (SGF.getSILType(param, blockTy).isBlockPointerCompatible()) { // We still need to consume the original block if it was owned. switch (param.getConvention()) { case ParameterConvention::Direct_Owned: @@ -429,7 +431,8 @@ static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, CanType formalBridgedType = bridgedParamTypes[i]; CanType formalNativeType = nativeParamTypes[i]; - SILType loweredNativeTy = funcTy->getParameters()[i].getSILStorageType(); + SILType loweredNativeTy = + funcTy->getParameters()[i].getSILStorageType(SGF.SGM.M, funcTy); args.push_back(SGF.emitBridgedToNativeValue(loc, mv, formalBridgedType, formalNativeType, @@ -546,7 +549,7 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, GenericEnvironment *genericEnv = nullptr; SubstitutionMap subs; if (funcType->hasArchetype() || blockType->hasArchetype()) { - genericSig = F.getLoweredFunctionType()->getGenericSignature(); + genericSig = F.getLoweredFunctionType()->getInvocationGenericSignature(); genericEnv = F.getGenericEnvironment(); subs = F.getForwardingSubstitutionMap(); @@ -567,7 +570,8 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, genericSig, extInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, blockInterfaceTy->getResults(), - blockInterfaceTy->getOptionalErrorResult(), getASTContext()); + blockInterfaceTy->getOptionalErrorResult(), SubstitutionMap(), false, + getASTContext()); // Create the invoke function. Borrow the mangling scheme from reabstraction // thunks, which is what we are in spirit. @@ -726,7 +730,7 @@ static ManagedValue emitNativeToCBridgedNonoptionalValue(SILGenFunction &SGF, nativeType); // Put the value into memory if necessary. - assert(v.getType().isTrivial(SGF.F) || v.hasCleanup()); + assert(v.getOwnershipKind() == ValueOwnershipKind::None || v.hasCleanup()); SILModuleConventions silConv(SGF.SGM.M); // bridgeAnything always takes an indirect argument as @in. // Since we don't have the SIL type here, check the current SIL stage/mode @@ -843,7 +847,8 @@ static void buildBlockToFuncThunkBody(SILGenFunction &SGF, // First get the managed parameter for this function. auto mv = emitManagedParameter(SGF, loc, param, v); - SILType loweredBlockArgTy = blockTy->getParameters()[i].getSILStorageType(); + SILType loweredBlockArgTy = blockTy->getParameters()[i] + .getSILStorageType(SGF.SGM.M, blockTy); // Then bridge the native value to its bridged variant. mv = SGF.emitNativeToBridgedValue(loc, mv, formalFuncParamTy, @@ -924,7 +929,7 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, auto loweredFuncTyWithoutNoEscape = adjustFunctionType( loweredFuncTy, loweredFuncTy->getExtInfo().withNoEscape(false), - loweredFuncTy->getWitnessMethodConformanceOrNone()); + loweredFuncTy->getWitnessMethodConformanceOrInvalid()); CanType dynamicSelfType; auto thunkTy = buildThunkType(loweredBlockTy, loweredFuncTyWithoutNoEscape, @@ -949,9 +954,10 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, CanSILFunctionType substFnTy = thunkTy; - if (thunkTy->getGenericSignature()) { + if (thunkTy->getInvocationGenericSignature()) { substFnTy = thunkTy->substGenericArgs(F.getModule(), - interfaceSubs); + interfaceSubs, + getTypeExpansionContext()); } // Create it in the current function. @@ -1129,7 +1135,8 @@ ManagedValue SILGenFunction::emitBridgedToNativeError(SILLocation loc, auto nativeErrorTy = SILType::getExceptionType(getASTContext()); auto conformance = SGM.getNSErrorConformanceToError(); - if (!conformance) return emitUndef(nativeErrorTy); + if (!conformance) + return emitUndef(nativeErrorTy); ProtocolConformanceRef conformanceArray[] = { ProtocolConformanceRef(conformance) }; @@ -1259,12 +1266,16 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF, auto subs = SGF.F.getForwardingSubstitutionMap(); - auto objcInfo = SGF.SGM.Types.getConstantInfo(thunk); - auto objcFnTy = objcInfo.SILFnType->substGenericArgs(SGF.SGM.M, subs); + auto objcInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), thunk); + auto objcFnTy = objcInfo.SILFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); auto objcFormalFnTy = substGenericArgs(objcInfo.LoweredType, subs); - auto swiftInfo = SGF.SGM.Types.getConstantInfo(native); - auto swiftFnTy = swiftInfo.SILFnType->substGenericArgs(SGF.SGM.M, subs); + auto swiftInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), native); + auto swiftFnTy = swiftInfo.SILFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); auto swiftFormalFnTy = substGenericArgs(swiftInfo.LoweredType, subs); SILFunctionConventions swiftConv(swiftFnTy, SGF.SGM.M); @@ -1298,7 +1309,7 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF, assert(inputs.size() == nativeInputs.size() + unsigned(foreignError.hasValue())); for (unsigned i = 0, e = inputs.size(); i < e; ++i) { - SILType argTy = SGF.getSILType(inputs[i]); + SILType argTy = SGF.getSILType(inputs[i], objcFnTy); SILValue arg = SGF.F.begin()->createFunctionArgument(argTy); // If this parameter is the foreign error slot, pull it out. @@ -1349,12 +1360,12 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF, // from potentially nil-unsound ObjC callers. ManagedValue native = SGF.emitBridgedToNativeValue(loc, - bridgedArgs[i], - bridgedFormalTypes[i], - nativeFormalTypes[i], - swiftFnTy->getParameters()[i].getSILStorageType(), - SGFContext(), - /*isCallResult*/ true); + bridgedArgs[i], + bridgedFormalTypes[i], + nativeFormalTypes[i], + swiftFnTy->getParameters()[i].getSILStorageType(SGF.SGM.M, swiftFnTy), + SGFContext(), + /*isCallResult*/ true); SILValue argValue; // This can happen if the value is resilient in the calling convention @@ -1398,9 +1409,10 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { } } - auto nativeInfo = getConstantInfo(native); + auto nativeInfo = getConstantInfo(getTypeExpansionContext(), native); auto subs = F.getForwardingSubstitutionMap(); - auto substTy = nativeInfo.SILFnType->substGenericArgs(SGM.M, subs); + auto substTy = nativeInfo.SILFnType->substGenericArgs( + SGM.M, subs, getTypeExpansionContext()); SILFunctionConventions substConv(substTy, SGM.M); // Use the same generic environment as the native entry point. @@ -1601,7 +1613,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { // Wrap the function in its original form. auto fd = cast(thunk.getDecl()); - auto nativeCI = getConstantInfo(thunk); + auto nativeCI = getConstantInfo(getTypeExpansionContext(), thunk); auto nativeFnTy = F.getLoweredFunctionType(); assert(nativeFnTy == nativeCI.SILFnType); @@ -1609,7 +1621,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(thunk)); SILDeclRef foreignDeclRef = thunk.asForeign(true); - SILConstantInfo foreignCI = getConstantInfo(foreignDeclRef); + SILConstantInfo foreignCI = + getConstantInfo(getTypeExpansionContext(), foreignDeclRef); auto foreignFnTy = foreignCI.SILFnType; // Find the foreign error convention and 'self' parameter index. @@ -1746,7 +1759,8 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { auto foreignParam = foreignFnTy->getParameters()[foreignArgIndex++]; SILType foreignLoweredTy = - F.mapTypeIntoContext(foreignParam.getSILStorageType()); + F.mapTypeIntoContext( + foreignParam.getSILStorageType(F.getModule(), foreignFnTy)); auto bridged = emitNativeToBridgedValue(fd, param, nativeFormalType, foreignFormalType, @@ -1775,7 +1789,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { foreignCI); auto fnType = fn->getType().castTo(); - fnType = fnType->substGenericArgs(SGM.M, subs); + fnType = fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CanType nativeFormalResultType = fd->mapTypeIntoContext(nativeCI.LoweredType.getResult()) @@ -1784,7 +1798,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { fd->mapTypeIntoContext(foreignCI.LoweredType.getResult()) ->getCanonicalType(); CalleeTypeInfo calleeTypeInfo( - fnType, AbstractionPattern(nativeFnTy->getGenericSignature(), + fnType, AbstractionPattern(nativeFnTy->getInvocationGenericSignature(), bridgedFormalResultType), nativeFormalResultType, foreignError, ImportAsMemberStatus()); diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index ca20e416474b6..503e6d6d9b55c 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -149,7 +149,7 @@ ManagedValue SILGenBuilder::createCopyValue(SILLocation loc, "value"); if (ty.isObject() && - originalValue.getOwnershipKind() == ValueOwnershipKind::Any) { + originalValue.getOwnershipKind() == ValueOwnershipKind::None) { return originalValue; } @@ -158,30 +158,31 @@ ManagedValue SILGenBuilder::createCopyValue(SILLocation loc, return SGF.emitManagedRValueWithCleanup(result, lowering); } -#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - ManagedValue \ - SILGenBuilder::createCopy##Name##Value(SILLocation loc, \ - ManagedValue originalValue) { \ - auto ty = originalValue.getType().castTo(); \ - assert(ty->isLoadable(ResilienceExpansion::Maximal)); \ - (void)ty; \ - SILValue result = createCopy##Name##Value(loc, originalValue.getValue()); \ - return SGF.emitManagedRValueWithCleanup(result); \ +#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + ManagedValue SILGenBuilder::createStrongCopy##Name##Value( \ + SILLocation loc, ManagedValue originalValue) { \ + auto ty = originalValue.getType().castTo(); \ + assert(ty->isLoadable(ResilienceExpansion::Maximal)); \ + (void)ty; \ + SILValue result = \ + createStrongCopy##Name##Value(loc, originalValue.getValue()); \ + return SGF.emitManagedRValueWithCleanup(result); \ } -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - ManagedValue \ - SILGenBuilder::createCopy##Name##Value(SILLocation loc, \ - ManagedValue originalValue) { \ - SILValue result = createCopy##Name##Value(loc, originalValue.getValue()); \ - return SGF.emitManagedRValueWithCleanup(result); \ +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + ManagedValue SILGenBuilder::createStrongCopy##Name##Value( \ + SILLocation loc, ManagedValue originalValue) { \ + SILValue result = \ + createStrongCopy##Name##Value(loc, originalValue.getValue()); \ + return SGF.emitManagedRValueWithCleanup(result); \ } #define UNCHECKED_REF_STORAGE(Name, ...) \ - ManagedValue SILGenBuilder::createCopy##Name##Value( \ + ManagedValue SILGenBuilder::createStrongCopy##Name##Value( \ SILLocation loc, ManagedValue originalValue) { \ /* *NOTE* The reason why this is unsafe is that we are converting and */ \ /* unconditionally retaining, rather than before converting from */ \ /* type->ref checking that our value is not yet uninitialized. */ \ - SILValue result = createCopy##Name##Value(loc, originalValue.getValue()); \ + SILValue result = \ + createStrongCopy##Name##Value(loc, originalValue.getValue()); \ return SGF.emitManagedRValueWithCleanup(result); \ } #include "swift/AST/ReferenceStorage.def" @@ -283,7 +284,7 @@ SILGenBuilder::createFormalAccessCopyValue(SILLocation loc, "address only type"); if (ty.isObject() && - originalValue.getOwnershipKind() == ValueOwnershipKind::Any) { + originalValue.getOwnershipKind() == ValueOwnershipKind::None) { return originalValue; } @@ -484,37 +485,48 @@ ManagedValue SILGenBuilder::createEnum(SILLocation loc, ManagedValue payload, } ManagedValue SILGenBuilder::createUnconditionalCheckedCastValue( - SILLocation loc, ManagedValue operand, SILType type) { + SILLocation loc, ManagedValue op, CanType srcFormalTy, + SILType destLoweredTy, CanType destFormalTy) { SILValue result = - createUnconditionalCheckedCastValue(loc, operand.forward(SGF), type); + createUnconditionalCheckedCastValue(loc, op.forward(SGF), + srcFormalTy, destLoweredTy, + destFormalTy); return SGF.emitManagedRValueWithCleanup(result); } -ManagedValue SILGenBuilder::createUnconditionalCheckedCast(SILLocation loc, - ManagedValue operand, - SILType type) { +ManagedValue SILGenBuilder::createUnconditionalCheckedCast( + SILLocation loc, ManagedValue op, + SILType destLoweredTy, CanType destFormalTy) { SILValue result = - createUnconditionalCheckedCast(loc, operand.forward(SGF), type); + createUnconditionalCheckedCast(loc, op.forward(SGF), + destLoweredTy, destFormalTy); return SGF.emitManagedRValueWithCleanup(result); } void SILGenBuilder::createCheckedCastBranch(SILLocation loc, bool isExact, - ManagedValue operand, SILType type, + ManagedValue op, + SILType destLoweredTy, + CanType destFormalTy, SILBasicBlock *trueBlock, SILBasicBlock *falseBlock, ProfileCounter Target1Count, ProfileCounter Target2Count) { - createCheckedCastBranch(loc, isExact, operand.forward(SGF), type, trueBlock, - falseBlock, Target1Count, Target2Count); + createCheckedCastBranch(loc, isExact, op.forward(SGF), + destLoweredTy, destFormalTy, + trueBlock, falseBlock, + Target1Count, Target2Count); } void SILGenBuilder::createCheckedCastValueBranch(SILLocation loc, - ManagedValue operand, - SILType type, + ManagedValue op, + CanType srcFormalTy, + SILType destLoweredTy, + CanType destFormalTy, SILBasicBlock *trueBlock, SILBasicBlock *falseBlock) { - createCheckedCastValueBranch(loc, operand.forward(SGF), type, trueBlock, - falseBlock); + createCheckedCastValueBranch(loc, op.forward(SGF), srcFormalTy, + destLoweredTy, destFormalTy, + trueBlock, falseBlock); } ManagedValue SILGenBuilder::createUpcast(SILLocation loc, ManagedValue original, @@ -657,8 +669,7 @@ ManagedValue SILGenBuilder::createStore(SILLocation loc, ManagedValue value, SILValue address, StoreOwnershipQualifier qualifier) { CleanupCloner cloner(*this, value); - if (value.getType().isTrivial(SGF.F) || - value.getOwnershipKind() == ValueOwnershipKind::Any) + if (value.getOwnershipKind() == ValueOwnershipKind::None) qualifier = StoreOwnershipQualifier::Trivial; createStore(loc, value.forward(SGF), address, qualifier); return cloner.clone(address); @@ -696,7 +707,7 @@ void SILGenBuilder::createStoreBorrow(SILLocation loc, ManagedValue value, void SILGenBuilder::createStoreBorrowOrTrivial(SILLocation loc, ManagedValue value, SILValue address) { - if (value.getOwnershipKind() == ValueOwnershipKind::Any) { + if (value.getOwnershipKind() == ValueOwnershipKind::None) { createStore(loc, value, address, StoreOwnershipQualifier::Trivial); return; } @@ -755,14 +766,15 @@ ManagedValue SILGenBuilder::createTuple(SILLocation loc, SILType type, return ManagedValue::forUnmanaged(result); } - // We need to look for the first non-trivial value and use that as our cleanup - // cloner value. + // We need to look for the first value without .none ownership and use that as + // our cleanup cloner value. auto iter = find_if(elements, [&](ManagedValue mv) -> bool { - return !mv.getType().isTrivial(getFunction()); + return mv.getOwnershipKind() != ValueOwnershipKind::None; }); llvm::SmallVector forwardedValues; - // If we have all trivial values, then just create the tuple and return. No + + // If we have all .none values, then just create the tuple and return. No // cleanups need to be cloned. if (iter == elements.end()) { llvm::transform(elements, std::back_inserter(forwardedValues), @@ -791,17 +803,6 @@ ManagedValue SILGenBuilder::createUncheckedAddrCast(SILLocation loc, ManagedValu return cloner.clone(cast); } -ManagedValue SILGenBuilder::tryCreateUncheckedRefCast(SILLocation loc, - ManagedValue original, - SILType type) { - CleanupCloner cloner(*this, original); - SILValue result = tryCreateUncheckedRefCast(loc, original.getValue(), type); - if (!result) - return ManagedValue(); - original.forward(SGF); - return cloner.clone(result); -} - ManagedValue SILGenBuilder::createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, SILType type) { diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index b91969b603bbd..c4b8cd3f1b42e 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -115,13 +115,13 @@ class SILGenBuilder : public SILBuilder { ManagedValue originalValue); #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - using SILBuilder::createCopy##Name##Value; \ - ManagedValue createCopy##Name##Value(SILLocation loc, \ - ManagedValue originalValue); + using SILBuilder::createStrongCopy##Name##Value; \ + ManagedValue createStrongCopy##Name##Value(SILLocation loc, \ + ManagedValue originalValue); #define UNCHECKED_REF_STORAGE(Name, ...) \ - using SILBuilder::createCopy##Name##Value; \ - ManagedValue createCopy##Name##Value(SILLocation loc, \ - ManagedValue originalValue); + using SILBuilder::createStrongCopy##Name##Value; \ + ManagedValue createStrongCopy##Name##Value(SILLocation loc, \ + ManagedValue originalValue); #include "swift/AST/ReferenceStorage.def" ManagedValue createOwnedPhiArgument(SILType type); @@ -223,33 +223,39 @@ class SILGenBuilder : public SILBuilder { using SILBuilder::createUnconditionalCheckedCastValue; ManagedValue createUnconditionalCheckedCastValue(SILLocation loc, - ManagedValue operand, SILType type); + ManagedValue op, + CanType srcFormalTy, + SILType destLoweredTy, + CanType destFormalTy); using SILBuilder::createUnconditionalCheckedCast; ManagedValue createUnconditionalCheckedCast(SILLocation loc, - ManagedValue operand, - SILType type); + ManagedValue op, + SILType destLoweredTy, + CanType destFormalTy); using SILBuilder::createCheckedCastBranch; void createCheckedCastBranch(SILLocation loc, bool isExact, - ManagedValue operand, SILType type, + ManagedValue op, + SILType destLoweredTy, + CanType destFormalTy, SILBasicBlock *trueBlock, SILBasicBlock *falseBlock, ProfileCounter Target1Count, ProfileCounter Target2Count); using SILBuilder::createCheckedCastValueBranch; - void createCheckedCastValueBranch(SILLocation loc, ManagedValue operand, - SILType type, SILBasicBlock *trueBlock, + void createCheckedCastValueBranch(SILLocation loc, + ManagedValue op, + CanType srcFormalTy, + SILType destLoweredTy, + CanType destFormalTy, + SILBasicBlock *trueBlock, SILBasicBlock *falseBlock); using SILBuilder::createUpcast; ManagedValue createUpcast(SILLocation loc, ManagedValue original, SILType type); - using SILBuilder::tryCreateUncheckedRefCast; - ManagedValue tryCreateUncheckedRefCast(SILLocation loc, ManagedValue original, - SILType type); - using SILBuilder::createUncheckedTrivialBitCast; ManagedValue createUncheckedTrivialBitCast(SILLocation loc, ManagedValue original, diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 31390a0b9838e..f1772f0e511df 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -649,13 +649,14 @@ emitBuiltinCastReference(SILGenFunction &SGF, auto &toTL = SGF.getTypeLowering(toTy); assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type"); + auto arg = args[0]; + // TODO: Fix this API. if (!fromTL.isAddress() || !toTL.isAddress()) { - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, args[0], - toTL.getLoweredType())) { + if (SILType::canRefCast(arg.getType(), toTL.getLoweredType(), SGF.SGM.M)) { // Create a reference cast, forwarding the cleanup. // The cast takes the source reference. - return refCast; + return SGF.B.createUncheckedRefCast(loc, arg, toTL.getLoweredType()); } } @@ -670,7 +671,7 @@ emitBuiltinCastReference(SILGenFunction &SGF, // TODO: For now, we leave invalid casts in address form so that the runtime // will trap. We could emit a noreturn call here instead which would provide // more information to the optimizer. - SILValue srcVal = args[0].ensurePlusOne(SGF, loc).forward(SGF); + SILValue srcVal = arg.ensurePlusOne(SGF, loc).forward(SGF); SILValue fromAddr; if (!fromTL.isAddress()) { // Move the loadable value into a "source temp". Since the source and @@ -745,17 +746,9 @@ static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, } // Create the appropriate bitcast based on the source and dest types. ManagedValue in = args[0]; - SILType resultTy = toTL.getLoweredType(); - if (resultTy.isTrivial(SGF.F)) - return SGF.B.createUncheckedTrivialBitCast(loc, in, resultTy); - // If we can perform a ref cast, just return. - if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, in, resultTy)) - return refCast; - - // Otherwise leave the original cleanup and retain the cast value. - SILValue out = SGF.B.createUncheckedBitwiseCast(loc, in.getValue(), resultTy); - return SGF.emitManagedRetain(loc, out, toTL); + SILType resultTy = toTL.getLoweredType(); + return SGF.B.createUncheckedBitCast(loc, in, resultTy); } /// Specialized emitter for Builtin.castToBridgeObject. @@ -976,8 +969,8 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, SILType ElemType = SGF.getLoweredType(subs.getReplacementTypes()[1]-> getCanonicalType()).getObjectType(); - SILValue result = SGF.B.createRefTailAddr(loc, args[0].getValue(), - ElemType.getAddressType()); + SILValue result = SGF.B.createRefTailAddr( + loc, args[0].borrow(SGF, loc).getValue(), ElemType.getAddressType()); SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); result = SGF.B.createAddressToPointer(loc, result, rawPointerType); return ManagedValue::forUnmanaged(result); diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 69a2eb99c5962..b414808edfffd 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -49,7 +49,8 @@ static SILValue emitConstructorMetatypeArg(SILGenFunction &SGF, VD->setInterfaceType(metatype); SGF.AllocatorMetatype = SGF.F.begin()->createFunctionArgument( - SGF.getLoweredType(DC->mapTypeIntoContext(metatype)), VD); + SGF.getLoweredTypeForFunctionArgument(DC->mapTypeIntoContext(metatype)), + VD); return SGF.AllocatorMetatype; } @@ -78,8 +79,7 @@ static RValue emitImplicitValueConstructorArg(SILGenFunction &SGF, VD->setSpecifier(ParamSpecifier::Default); VD->setInterfaceType(interfaceType); - auto argType = SGF.SGM.Types.getLoweredType(type, - ResilienceExpansion::Minimal); + auto argType = SGF.getLoweredTypeForFunctionArgument(type); auto *arg = SGF.F.begin()->createFunctionArgument(argType, VD); ManagedValue mvArg; if (arg->getArgumentConvention().isOwnedConvention()) { @@ -129,7 +129,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto *paramList = ctor->getParameters(); auto *selfDecl = ctor->getImplicitSelfDecl(); auto selfIfaceTy = selfDecl->getInterfaceType(); - SILType selfTy = SGF.getLoweredType(selfDecl->getType()); + SILType selfTy = SGF.getLoweredTypeForFunctionArgument(selfDecl->getType()); // Emit the indirect return argument, if any. SILValue resultSlot; @@ -142,7 +142,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, ctor); VD->setSpecifier(ParamSpecifier::InOut); VD->setInterfaceType(selfIfaceTy); - resultSlot = SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); + resultSlot = + SGF.F.begin()->createFunctionArgument(selfTy.getAddressType(), VD); } // Emit the elementwise arguments. @@ -164,7 +165,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, if (resultSlot) { auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { - auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue slot = SGF.B.createStructElementAddr(Loc, resultSlot, field, fieldTy.getAddressType()); @@ -201,7 +203,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { - auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M); + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue v; // If it's memberwise initialized, do so now. @@ -416,8 +419,8 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) { Type enumIfaceTy = element->getParentEnum()->getDeclaredInterfaceType(); Type enumTy = F.mapTypeIntoContext(enumIfaceTy); - auto &enumTI = SGM.Types.getTypeLowering(enumTy, - ResilienceExpansion::Minimal); + auto &enumTI = + SGM.Types.getTypeLowering(enumTy, TypeExpansionContext::minimal()); RegularLocation Loc(element); CleanupLocation CleanupLoc(element); @@ -896,7 +899,7 @@ static Type getInitializationTypeInContext( // initialization type is the original property type. if (auto singleVar = pattern->getSingleVar()) { if (auto originalProperty = singleVar->getOriginalWrappedProperty()) { - if (originalProperty->isPropertyWrapperInitializedWithInitialValue()) + if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) interfaceType = originalProperty->getValueInterfaceType(); } } @@ -914,12 +917,13 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, if (auto pbd = dyn_cast(member)) { if (pbd->isStatic()) continue; - for (auto entry : pbd->getPatternList()) { - auto init = entry.getExecutableInit(); + for (auto i : range(pbd->getNumPatternEntries())) { + auto init = pbd->getExecutableInit(i); if (!init) continue; + auto *varPattern = pbd->getPattern(i); // Cleanup after this initialization. - FullExpr scope(Cleanups, entry.getPattern()); + FullExpr scope(Cleanups, varPattern); // We want a substitution list written in terms of the generic // signature of the type, with replacement archetypes from the @@ -948,13 +952,13 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, // Get the type of the initialization result, in terms // of the constructor context's archetypes. CanType resultType = getInitializationTypeInContext( - pbd->getDeclContext(), dc, entry.getPattern())->getCanonicalType(); + pbd->getDeclContext(), dc, varPattern)->getCanonicalType(); AbstractionPattern origResultType(resultType); // FIXME: Can emitMemberInit() share code with // InitializationForPattern in SILGenDecl.cpp? RValue result = emitApplyOfStoredPropertyInitializer( - init, entry, subs, + init, pbd->getAnchoringVarDecl(i), subs, resultType, origResultType, SGFContext()); @@ -964,13 +968,13 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, if (auto singleVar = pbd->getSingleVar()) { auto originalVar = singleVar->getOriginalWrappedProperty(); if (originalVar && - originalVar->isPropertyWrapperInitializedWithInitialValue()) { + originalVar->isPropertyMemberwiseInitializedWithWrappedType()) { result = maybeEmitPropertyWrapperInitFromValue( *this, init, singleVar, std::move(result)); } } - emitMemberInit(*this, selfDecl, entry.getPattern(), std::move(result)); + emitMemberInit(*this, selfDecl, varPattern, std::move(result)); } } } diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 7e45a01b5d444..f02c0b387765f 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -140,11 +140,11 @@ auto SILGenFunction::emitSourceLocationArgs(SourceLoc sourceLoc, -> SourceLocArgs { auto &ctx = getASTContext(); - StringRef filename = ""; + std::string filename = ""; unsigned line = 0; unsigned column = 0; if (sourceLoc.isValid()) { - filename = ctx.SourceMgr.getDisplayNameForLoc(sourceLoc); + filename = getMagicFileString(sourceLoc); std::tie(line, column) = ctx.SourceMgr.getLineAndColumn(sourceLoc); } @@ -160,7 +160,7 @@ auto SILGenFunction::emitSourceLocationArgs(SourceLoc sourceLoc, auto i1Ty = SILType::getBuiltinIntegerType(1, ctx); SourceLocArgs result; - SILValue literal = B.createStringLiteral(emitLoc, filename, + SILValue literal = B.createStringLiteral(emitLoc, StringRef(filename), StringLiteralInst::Encoding::UTF8); result.filenameStartPointer = ManagedValue::forUnmanaged(literal); // File length @@ -625,11 +625,9 @@ ManagedValue SILGenFunction::emitExistentialErasure( if (ctx.LangOpts.EnableObjCInterop && conformances.size() == 1 && conformances[0].getRequirement() == ctx.getErrorDecl() && ctx.getNSErrorDecl()) { - auto nsErrorDecl = ctx.getNSErrorDecl(); - // If the concrete type is NSError or a subclass thereof, just erase it // directly. - auto nsErrorType = nsErrorDecl->getDeclaredType()->getCanonicalType(); + auto nsErrorType = ctx.getNSErrorType()->getCanonicalType(); if (nsErrorType->isExactSuperclassOf(concreteFormalType)) { ManagedValue nsError = F(SGFContext()); if (nsErrorType != concreteFormalType) { @@ -641,8 +639,9 @@ ManagedValue SILGenFunction::emitExistentialErasure( // If the concrete type is known to conform to _BridgedStoredNSError, // call the _nsError witness getter to extract the NSError directly, // then just erase the NSError. - if (auto storedNSErrorConformance = - SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType)) { + auto storedNSErrorConformance = + SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType); + if (storedNSErrorConformance) { auto nsErrorVar = SGM.getNSErrorRequirement(loc); if (!nsErrorVar) return emitUndef(existentialTL.getLoweredType()); @@ -650,9 +649,9 @@ ManagedValue SILGenFunction::emitExistentialErasure( // Devirtualize. Maybe this should be done implicitly by // emitPropertyLValue? - if (storedNSErrorConformance->isConcrete()) { + if (storedNSErrorConformance.isConcrete()) { if (auto normal = dyn_cast( - storedNSErrorConformance->getConcrete())) { + storedNSErrorConformance.getConcrete())) { if (auto witnessVar = normal->getWitness(nsErrorVar)) { nsErrorVar = cast(witnessVar.getDecl()); nsErrorVarSubstitutions = witnessVar.getSubstitutions(); @@ -828,12 +827,10 @@ ManagedValue SILGenFunction::emitExistentialErasure( loc, SILType::getPrimitiveObjectType(anyObjectTy), concreteFormalType, concreteValue, {}); }; - - auto concreteTLPtr = &concreteTL; + if (this->F.getLoweredFunctionType()->isPseudogeneric()) { if (anyObjectTy && concreteFormalType->is()) { concreteFormalType = anyObjectTy; - concreteTLPtr = &getTypeLowering(anyObjectTy); F = eraseToAnyObject; } } @@ -993,7 +990,7 @@ ManagedValue SILGenFunction::manageOpaqueValue(ManagedValue value, SGFContext C) { // If the opaque value is consumable, we can just return the // value with a cleanup. There is no need to retain it separately. - if (value.hasCleanup()) + if (value.isPlusOne(*this)) return value; // If the context wants a +0 value, guaranteed or immediate, we can diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 323636a97b831..ace1e9e71c359 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -371,12 +371,13 @@ class LocalVariableInitialization : public SingleBufferInitialization { "can't emit a local var for a non-local var decl"); assert(decl->hasStorage() && "can't emit storage for a computed variable"); assert(!SGF.VarLocs.count(decl) && "Already have an entry for this decl?"); - - auto boxType = SGF.SGM.Types - .getContextBoxTypeForCapture(decl, - SGF.SGM.Types.getLoweredRValueType(decl->getType()), - SGF.F.getGenericEnvironment(), - /*mutable*/ true); + // The box type's context is lowered in the minimal resilience domain. + auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture( + decl, + SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + decl->getType()), + SGF.F.getGenericEnvironment(), + /*mutable*/ true); // The variable may have its lifetime extended by a closure, heap-allocate // it using a box. @@ -850,7 +851,8 @@ void EnumElementPatternInitialization::emitEnumMatch( } // Otherwise, the bound value for the enum case is available. - SILType eltTy = value.getType().getEnumElementType(eltDecl, SGF.SGM.M); + SILType eltTy = value.getType().getEnumElementType( + eltDecl, SGF.SGM.M, SGF.getTypeExpansionContext()); auto &eltTL = SGF.getTypeLowering(eltTy); if (mv.getType().isAddress()) { @@ -1166,14 +1168,13 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable) { } void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, - unsigned pbdEntry) { - auto &entry = PBD->getPatternList()[pbdEntry]; - auto initialization = emitPatternBindingInitialization(entry.getPattern(), + unsigned idx) { + auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx), JumpDest::invalid()); // If an initial value expression was specified by the decl, emit it into // the initialization. Otherwise, mark it uninitialized for DI to resolve. - if (auto *Init = entry.getExecutableInit()) { + if (auto *Init = PBD->getExecutableInit(idx)) { FullExpr Scope(Cleanups, CleanupLocation(Init)); emitExprInto(Init, initialization.get(), SILLocation(PBD)); } else { @@ -1185,7 +1186,7 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD) { // Allocate the variables and build up an Initialization over their // allocated storage. - for (unsigned i : indices(PBD->getPatternList())) { + for (unsigned i : range(PBD->getNumPatternEntries())) { emitPatternBinding(PBD, i); } } @@ -1238,7 +1239,7 @@ SILValue SILGenFunction::emitOSVersionRangeCheck(SILLocation loc, auto silDeclRef = SILDeclRef(versionQueryDecl); SILValue availabilityGTEFn = emitGlobalFunctionRef( - loc, silDeclRef, getConstantInfo(silDeclRef)); + loc, silDeclRef, getConstantInfo(getTypeExpansionContext(), silDeclRef)); SILValue args[] = {majorValue, minorValue, subminorValue}; return B.createApply(loc, availabilityGTEFn, SubstitutionMap(), args); diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 1c816b3314ce5..357bf94625f1f 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -186,7 +186,11 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, SILValue addr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, ti.getLoweredType().getAddressType()); + addr = B.createBeginAccess( + cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); B.createDestroyAddr(cleanupLoc, addr); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); } } } @@ -232,7 +236,8 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { auto superclassDtor = SILDeclRef(superclassDtorDecl, SILDeclRef::Kind::Deallocator) .asForeign(); - auto superclassDtorType = SGM.Types.getConstantType(superclassDtor); + auto superclassDtorType = + SGM.Types.getConstantType(getTypeExpansionContext(), superclassDtor); SILValue superclassDtorValue = B.createObjCSuperMethod( cleanupLoc, selfValue, superclassDtor, superclassDtorType); diff --git a/lib/SILGen/SILGenDynamicCast.cpp b/lib/SILGen/SILGenDynamicCast.cpp index dfede8af12b84..7b3ea94ceb66b 100644 --- a/lib/SILGen/SILGenDynamicCast.cpp +++ b/lib/SILGen/SILGenDynamicCast.cpp @@ -101,10 +101,12 @@ namespace { ManagedValue result; if (Strategy == CastStrategy::Address) { result = SGF.B.createUnconditionalCheckedCastValue( - Loc, operand, origTargetTL.getLoweredType()); + Loc, operand, SourceType, + origTargetTL.getLoweredType(), TargetType); } else { result = SGF.B.createUnconditionalCheckedCast( - Loc, operand, origTargetTL.getLoweredType()); + Loc, operand, + origTargetTL.getLoweredType(), TargetType); } return RValue(SGF, Loc, TargetType, @@ -147,7 +149,9 @@ namespace { // Opaque value mode operandValue = std::move(operand); SGF.B.createCheckedCastValueBranch( - Loc, operandValue, origTargetTL.getLoweredType(), trueBB, falseBB); + Loc, operandValue, SourceType, + origTargetTL.getLoweredType(), TargetType, + trueBB, falseBB); } else { // Tolerate being passed an address here. It comes up during switch // emission. @@ -162,8 +166,8 @@ namespace { operandValue = operandValue.borrow(SGF, Loc); } SGF.B.createCheckedCastBranch(Loc, /*exact*/ false, operandValue, - origTargetTL.getLoweredType(), trueBB, - falseBB, TrueCount, FalseCount); + origTargetTL.getLoweredType(), TargetType, + trueBB, falseBB, TrueCount, FalseCount); } // Emit the success block. @@ -588,7 +592,7 @@ SILValue Lowering::emitIsa(SILGenFunction &SGF, SILLocation loc, }); auto contBB = scope.exit(); - auto isa = contBB->createPhiArgument(i1Ty, ValueOwnershipKind::Any); + auto isa = contBB->createPhiArgument(i1Ty, ValueOwnershipKind::None); return isa; } diff --git a/lib/SILGen/SILGenEpilog.cpp b/lib/SILGen/SILGenEpilog.cpp index 414bfc6a27bd4..66f3302207a9a 100644 --- a/lib/SILGen/SILGenEpilog.cpp +++ b/lib/SILGen/SILGenEpilog.cpp @@ -30,8 +30,8 @@ void SILGenFunction::prepareEpilog(Type resultType, bool isThrowing, // emits unreachable if there is no source level return. NeedsReturn = (fnConv.funcTy->getNumResults() != 0); for (auto directResult : fnConv.getDirectSILResults()) { - SILType resultType = - F.mapTypeIntoContext(fnConv.getSILType(directResult)); + SILType resultType = F.getLoweredType( + F.mapTypeIntoContext(fnConv.getSILType(directResult))); epilogBB->createPhiArgument(resultType, ValueOwnershipKind::Owned); } } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 19e4e9215050f..879c9082c6272 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -68,7 +68,7 @@ ManagedValue SILGenFunction::emitManagedRetain(SILLocation loc, if (lowering.isTrivial()) return ManagedValue::forUnmanaged(v); if (v->getType().isObject() && - v.getOwnershipKind() == ValueOwnershipKind::Any) + v.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(v); assert((!lowering.isAddressOnly() || !silConv.useLoweredAddresses()) && "cannot retain an unloadable type"); @@ -88,7 +88,7 @@ ManagedValue SILGenFunction::emitManagedLoadCopy(SILLocation loc, SILValue v, v = lowering.emitLoadOfCopy(B, loc, v, IsNotTake); if (lowering.isTrivial()) return ManagedValue::forUnmanaged(v); - if (v.getOwnershipKind() == ValueOwnershipKind::Any) + if (v.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(v); assert((!lowering.isAddressOnly() || !silConv.useLoweredAddresses()) && "cannot retain an unloadable type"); @@ -126,7 +126,7 @@ ManagedValue SILGenFunction::emitManagedStoreBorrow( SILLocation loc, SILValue v, SILValue addr, const TypeLowering &lowering) { assert(lowering.getLoweredType().getObjectType() == v->getType()); if (lowering.isTrivial() || - v.getOwnershipKind() == ValueOwnershipKind::Any) { + v.getOwnershipKind() == ValueOwnershipKind::None) { lowering.emitStore(B, loc, v, addr, StoreOwnershipQualifier::Trivial); return ManagedValue::forUnmanaged(v); } @@ -150,7 +150,7 @@ SILGenFunction::emitManagedBeginBorrow(SILLocation loc, SILValue v, if (lowering.isTrivial()) return ManagedValue::forUnmanaged(v); - if (v.getOwnershipKind() == ValueOwnershipKind::Any) + if (v.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(v); if (v.getOwnershipKind() == ValueOwnershipKind::Guaranteed) @@ -272,7 +272,7 @@ SILGenFunction::emitFormalEvaluationManagedBorrowedRValueWithCleanup( ManagedValue SILGenFunction::emitManagedBorrowedArgumentWithCleanup(SILPhiArgument *arg) { - if (arg->getOwnershipKind() == ValueOwnershipKind::Any || + if (arg->getOwnershipKind() == ValueOwnershipKind::None || arg->getType().isTrivial(F)) { return ManagedValue::forUnmanaged(arg); } @@ -299,7 +299,7 @@ ManagedValue SILGenFunction::emitManagedBorrowedRValueWithCleanup( return ManagedValue::forUnmanaged(borrowed); if (original->getType().isObject() && - original.getOwnershipKind() == ValueOwnershipKind::Any) + original.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(borrowed); if (borrowed->getType().isObject()) { @@ -321,7 +321,7 @@ ManagedValue SILGenFunction::emitManagedRValueWithCleanup(SILValue v, if (lowering.isTrivial()) return ManagedValue::forUnmanaged(v); if (v->getType().isObject() && - v.getOwnershipKind() == ValueOwnershipKind::Any) { + v.getOwnershipKind() == ValueOwnershipKind::None) { return ManagedValue::forUnmanaged(v); } return ManagedValue(v, enterDestroyCleanup(v)); @@ -499,6 +499,8 @@ namespace { RValue visitUnevaluatedInstanceExpr(UnevaluatedInstanceExpr *E, SGFContext C); RValue visitTapExpr(TapExpr *E, SGFContext C); + RValue visitDefaultArgumentExpr(DefaultArgumentExpr *E, SGFContext C); + RValue visitErrorExpr(ErrorExpr *E, SGFContext C); }; } // end anonymous namespace @@ -1442,10 +1444,13 @@ RValueEmitter::visitBridgeToObjCExpr(BridgeToObjCExpr *E, SGFContext C) { RValue RValueEmitter::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C) { ManagedValue archetype = SGF.emitRValueAsSingleValue(E->getSubExpr()); + auto loweredTy = SGF.getLoweredLoadableType(E->getType()); + if (loweredTy == archetype.getType()) + return RValue(SGF, E, archetype); + // Replace the cleanup with a new one on the superclass value so we always use // concrete retain/release operations. - auto base = SGF.B.createUpcast(E, archetype, - SGF.getLoweredLoadableType(E->getType())); + auto base = SGF.B.createUpcast(E, archetype, loweredTy); return RValue(SGF, E, base); } @@ -1458,8 +1463,10 @@ static ManagedValue convertCFunctionSignature(SILGenFunction &SGF, // We're converting between C function pointer types. They better be // ABI-compatible, since we can't emit a thunk. - switch (SGF.SGM.Types.checkForABIDifferences(loweredResultTy, loweredDestTy)){ - case TypeConverter::ABIDifference::Trivial: + switch (SGF.SGM.Types.checkForABIDifferences(SGF.SGM.M, + loweredResultTy, loweredDestTy)){ + case TypeConverter::ABIDifference::CompatibleRepresentation: + case TypeConverter::ABIDifference::CompatibleCallingConvention: result = fnEmitter(); assert(result.getType() == loweredResultTy); @@ -1478,7 +1485,8 @@ static ManagedValue convertCFunctionSignature(SILGenFunction &SGF, result = SGF.emitUndef(loweredDestTy); break; - case TypeConverter::ABIDifference::ThinToThick: + case TypeConverter::ABIDifference::CompatibleCallingConvention_ThinToThick: + case TypeConverter::ABIDifference::CompatibleRepresentation_ThinToThick: llvm_unreachable("Cannot have thin to thick conversion here"); } @@ -1538,30 +1546,28 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF, // Produce a reference to the C-compatible entry point for the function. SILDeclRef constant(loc, /*curried*/ false, /*foreign*/ true); - SILConstantInfo constantInfo = SGF.getConstantInfo(constant); + SILConstantInfo constantInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), constant); // C function pointers cannot capture anything from their context. auto captures = SGF.SGM.Types.getLoweredLocalCaptures(constant); - if (captures.hasGenericParamCaptures() || + if (!captures.getCaptures().empty() || + captures.hasGenericParamCaptures() || captures.hasDynamicSelfCapture() || - captures.hasLocalCaptures() || captures.hasOpaqueValueCapture()) { - unsigned kind; - if (captures.hasLocalCaptures()) - kind = 0; - else if (captures.hasGenericParamCaptures()) + unsigned kind = 0; + if (captures.hasGenericParamCaptures()) kind = 1; - else if (captures.hasLocalCaptures()) + else if (captures.hasDynamicSelfCapture()) kind = 2; - else - kind = 3; SGF.SGM.diagnose(expr->getLoc(), diag::c_function_pointer_from_function_with_context, /*closure*/ constant.hasClosureExpr(), kind); - return SGF.emitUndef(constantInfo.getSILType()); + auto loweredTy = SGF.getLoweredType(conversionExpr->getType()); + return SGF.emitUndef(loweredTy); } return convertCFunctionSignature( @@ -1860,7 +1866,7 @@ ManagedValue SILGenFunction::getManagedValue(SILLocation loc, if (valueTy.isObject()) { // See if we have more accurate information from the ownership kind. This // detects trivial cases of enums. - if (value.getOwnershipKind() == ValueOwnershipKind::Any) + if (value.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(value.getValue()); // Otherwise, copy the value and return. @@ -1967,7 +1973,11 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, E->getSubExpr()->getType()); auto &underlyingSubstTL = SGF.getTypeLowering(E->getSubExpr()->getType()); - + + if (underlyingSubstTL.getLoweredType() == opaqueTL.getLoweredType()) { + return SGF.emitRValue(E->getSubExpr(), C); + } + // If the opaque type is address only, initialize in place. if (opaqueTL.getLoweredType().isAddress()) { auto opaqueAddr = SGF.getBufferForExprResult( @@ -2006,15 +2016,18 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E, // If the opaque type is loadable, emit the subexpression and bitcast it. auto value = SGF.emitRValueAsSingleValue(E->getSubExpr()); - if (underlyingSubstTL.getLoweredType() == underlyingTL.getLoweredType()) { + if (underlyingSubstTL.getLoweredType() != underlyingTL.getLoweredType()) { value = SGF.emitSubstToOrigValue(E, value, AbstractionPattern::getOpaque(), E->getSubExpr()->getType()->getCanonicalType()); } - + + if (value.getType() == opaqueTL.getLoweredType()) + return RValue(SGF, E, value); + auto cast = SGF.B.createUncheckedBitCast(E, value.forward(SGF), - opaqueTL.getLoweredType()); + opaqueTL.getLoweredType()); value = SGF.emitManagedRValueWithCleanup(cast); - + return RValue(SGF, E, value); } @@ -2197,7 +2210,8 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, if (fnType->isPolymorphic()) subs = defaultArgsOwner.getSubstitutions(); - auto substFnType = fnType->substGenericArgs(SGM.M, subs); + auto substFnType = + fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CalleeTypeInfo calleeTypeInfo(substFnType, origResultType, resultType); ResultPlanPtr resultPtr = @@ -2214,18 +2228,18 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, RValue SILGenFunction::emitApplyOfStoredPropertyInitializer( SILLocation loc, - const PatternBindingEntry &entry, + VarDecl *var, SubstitutionMap subs, CanType resultType, AbstractionPattern origResultType, SGFContext C) { - VarDecl *var = entry.getAnchoringVarDecl(); SILDeclRef constant(var, SILDeclRef::Kind::StoredPropertyInitializer); auto fnRef = ManagedValue::forUnmanaged(emitGlobalFunctionRef(loc, constant)); auto fnType = fnRef.getType().castTo(); - auto substFnType = fnType->substGenericArgs(SGM.M, subs); + auto substFnType = + fnType->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); CalleeTypeInfo calleeTypeInfo(substFnType, origResultType, resultType); ResultPlanPtr resultPlan = @@ -2636,9 +2650,9 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, } } - auto genericSig = genericEnv - ? genericEnv->getGenericSignature()->getCanonicalSignature() - : nullptr; + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; if (genericSig && genericSig->areAllParamsConcrete()) { genericSig = nullptr; genericEnv = nullptr; @@ -2646,13 +2660,12 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, // Build the signature of the thunk as expected by the keypath runtime. CanType loweredBaseTy, loweredPropTy; - { - GenericContextScope scope(SGM.Types, genericSig); - AbstractionPattern opaque = AbstractionPattern::getOpaque(); + AbstractionPattern opaque = AbstractionPattern::getOpaque(); - loweredBaseTy = SGM.Types.getLoweredRValueType(opaque, baseType); - loweredPropTy = SGM.Types.getLoweredRValueType(opaque, propertyType); - } + loweredBaseTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, baseType); + loweredPropTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, propertyType); auto paramConvention = ParameterConvention::Indirect_In_Guaranteed; @@ -2667,12 +2680,12 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, SILResultInfo result(loweredPropTy, ResultConvention::Indirect); auto signature = SILFunctionType::get(genericSig, - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false), + SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - params, {}, result, None, SGM.getASTContext()); + params, {}, result, None, + SubstitutionMap(), false, + SGM.getASTContext()); // Find the function and see if we already created it. auto name = Mangle::ASTMangler() @@ -2699,17 +2712,17 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); auto entry = thunk->begin(); - auto resultArgTy = result.getSILStorageType(); - auto baseArgTy = params[0].getSILStorageType(); + auto resultArgTy = result.getSILStorageType(SGM.M, signature); + auto baseArgTy = params[0].getSILStorageType(SGM.M, signature); if (genericEnv) { - resultArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, resultArgTy); - baseArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, baseArgTy); + resultArgTy = genericEnv->mapTypeIntoContext(SGM.M, resultArgTy); + baseArgTy = genericEnv->mapTypeIntoContext(SGM.M, baseArgTy); } auto resultArg = entry->createFunctionArgument(resultArgTy); auto baseArg = entry->createFunctionArgument(baseArgTy); SILValue indexPtrArg; if (!indexes.empty()) { - auto indexArgTy = params[1].getSILStorageType(); + auto indexArgTy = params[1].getSILStorageType(SGM.M, signature); indexPtrArg = entry->createFunctionArgument(indexArgTy); } @@ -2769,10 +2782,9 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, } } - auto genericSig = genericEnv - ? genericEnv->getGenericSignature()->getCanonicalSignature() - : nullptr; - + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; if (genericSig && genericSig->areAllParamsConcrete()) { genericSig = nullptr; genericEnv = nullptr; @@ -2781,11 +2793,12 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, // Build the signature of the thunk as expected by the keypath runtime. CanType loweredBaseTy, loweredPropTy; { - GenericContextScope scope(SGM.Types, genericSig); AbstractionPattern opaque = AbstractionPattern::getOpaque(); - loweredBaseTy = SGM.Types.getLoweredRValueType(opaque, baseType); - loweredPropTy = SGM.Types.getLoweredRValueType(opaque, propertyType); + loweredBaseTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, baseType); + loweredPropTy = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), opaque, propertyType); } auto &C = SGM.getASTContext(); @@ -2807,12 +2820,12 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, ParameterConvention::Direct_Unowned}); auto signature = SILFunctionType::get(genericSig, - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false), + SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - params, {}, {}, None, SGM.getASTContext()); + params, {}, {}, None, + SubstitutionMap(), false, + SGM.getASTContext()); // Mangle the name of the thunk to see if we already created it. auto name = Mangle::ASTMangler() @@ -2840,18 +2853,18 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, SILGenFunction subSGF(SGM, *thunk, SGM.SwiftModule); auto entry = thunk->begin(); - auto valueArgTy = params[0].getSILStorageType(); - auto baseArgTy = params[1].getSILStorageType(); + auto valueArgTy = params[0].getSILStorageType(SGM.M, signature); + auto baseArgTy = params[1].getSILStorageType(SGM.M, signature); if (genericEnv) { - valueArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, valueArgTy); - baseArgTy = genericEnv->mapTypeIntoContext(subSGF.SGM.M, baseArgTy); + valueArgTy = genericEnv->mapTypeIntoContext(SGM.M, valueArgTy); + baseArgTy = genericEnv->mapTypeIntoContext(SGM.M, baseArgTy); } auto valueArg = entry->createFunctionArgument(valueArgTy); auto baseArg = entry->createFunctionArgument(baseArgTy); SILValue indexPtrArg; if (!indexes.empty()) { - auto indexArgTy = params[2].getSILStorageType(); + auto indexArgTy = params[2].getSILStorageType(SGM.M, signature); indexPtrArg = entry->createFunctionArgument(indexArgTy); } @@ -2930,10 +2943,10 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, hash = nullptr; return; } - - auto genericSig = genericEnv - ? genericEnv->getGenericSignature()->getCanonicalSignature() - : nullptr; + + auto genericSig = + genericEnv ? genericEnv->getGenericSignature().getCanonicalSignature() + : nullptr; if (genericSig && genericSig->areAllParamsConcrete()) { genericSig = nullptr; @@ -2964,8 +2977,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, RValue indexValue(indexTupleTy); auto indexLoweredTy = - SILType::getPrimitiveAddressType( - SGM.Types.getLoweredRValueType(indexTupleTy)); + SILType::getPrimitiveAddressType(SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), indexTupleTy)); // Get or create the equals witness [unsafeRawPointerTy, boolTy, genericSig, &C, &indexTypes, &equals, loc, @@ -2981,12 +2994,12 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, results.push_back({boolTy, ResultConvention::Unowned}); auto signature = SILFunctionType::get(genericSig, - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false), + SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - params, /*yields*/ {}, results, None, C); + params, /*yields*/ {}, results, None, + SubstitutionMap(), false, + C); // Mangle the name of the thunk to see if we already created it. auto name = Mangle::ASTMangler() @@ -3005,8 +3018,10 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILGenFunction subSGF(SGM, *equals, SGM.SwiftModule); equals->setGenericEnvironment(genericEnv); auto entry = equals->begin(); - auto lhsPtr = entry->createFunctionArgument(params[0].getSILStorageType()); - auto rhsPtr = entry->createFunctionArgument(params[1].getSILStorageType()); + auto lhsPtr = + entry->createFunctionArgument(params[0].getSILStorageType(SGM.M, signature)); + auto rhsPtr = + entry->createFunctionArgument(params[1].getSILStorageType(SGM.M, signature)); Scope scope(subSGF, loc); @@ -3023,8 +3038,9 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto equalsMethod = equatableProtocol->getSingleRequirement( C.Id_EqualsOperator); auto equalsRef = SILDeclRef(equalsMethod); - auto equalsTy = subSGF.SGM.Types.getConstantType(equalsRef); - + auto equalsTy = subSGF.SGM.Types.getConstantType( + TypeExpansionContext(subSGF.F), equalsRef); + auto isFalseBB = subSGF.createBasicBlock(); auto i1Ty = SILType::getBuiltinIntegerType(1, C); for (unsigned i : indices(indexes)) { @@ -3056,8 +3072,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, = SubstitutionMap::getProtocolSubstitutions(equatableProtocol, formalCanTy, equatable); - auto equalsSubstTy = equalsTy.castTo() - ->substGenericArgs(SGM.M, equatableSub); + auto equalsSubstTy = equalsTy.castTo()->substGenericArgs( + SGM.M, equatableSub, TypeExpansionContext(subSGF.F)); auto equalsInfo = CalleeTypeInfo(equalsSubstTy, AbstractionPattern(boolTy), boolTy, None, @@ -3131,8 +3147,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, subSGF.B.emitBlock(returnBB); scope.pop(); - SILValue returnVal = returnBB->createPhiArgument(i1Ty, - ValueOwnershipKind::Any); + SILValue returnVal = + returnBB->createPhiArgument(i1Ty, ValueOwnershipKind::None); auto returnBoolVal = subSGF.B.createStruct(loc, SILType::getPrimitiveObjectType(boolTy), returnVal); subSGF.B.createReturn(loc, returnBoolVal); @@ -3152,12 +3168,11 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, results.push_back({intTy, ResultConvention::Unowned}); auto signature = SILFunctionType::get(genericSig, - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false), + SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - params, /*yields*/ {}, results, None, C); + params, /*yields*/ {}, results, None, + SubstitutionMap(), false, C); // Mangle the name of the thunk to see if we already created it. SmallString<64> nameBuf; @@ -3178,7 +3193,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILGenFunction subSGF(SGM, *hash, SGM.SwiftModule); hash->setGenericEnvironment(genericEnv); auto entry = hash->begin(); - auto indexPtr = entry->createFunctionArgument(params[0].getSILStorageType()); + auto indexPtr = entry->createFunctionArgument( + params[0].getSILStorageType(SGM.M, signature)); SILValue hashCode; @@ -3216,10 +3232,8 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SubstitutionMap hashableSubsMap = SubstitutionMap::get( hashGenericSig, [&](SubstitutableType *type) -> Type { return formalTy; }, - [&](CanType dependentType, Type replacementType, - ProtocolDecl *proto)->Optional { - return hashable; - }); + [&](CanType dependentType, Type replacementType, ProtocolDecl *proto) + -> ProtocolConformanceRef { return hashable; }); // Read the storage. ManagedValue base = ManagedValue::forBorrowedAddressRValue(indexAddr); @@ -3302,8 +3316,8 @@ lowerKeyPathSubscriptIndexTypes( } auto indexLoweredTy = SGM.Types.getLoweredType( - AbstractionPattern::getOpaque(), - indexTy, expansion); + AbstractionPattern::getOpaque(), indexTy, + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion)); indexLoweredTy = indexLoweredTy.mapTypeOutOfContext(); indexPatterns.push_back({indexTy->mapTypeOutOfContext() ->getCanonicalType(), @@ -3545,27 +3559,6 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto baseTy = rootTy; SmallVector operands; - - auto lowerSubscriptOperands = - [this, &operands, E](const KeyPathExpr::Component &component) { - if (!component.getIndexExpr()) - return; - - // Evaluate the index arguments. - SmallVector indexValues; - auto indexResult = visit(component.getIndexExpr(), SGFContext()); - if (isa(indexResult.getType())) { - std::move(indexResult).extractElements(indexValues); - } else { - indexValues.push_back(std::move(indexResult)); - } - - for (auto &rv : indexValues) { - operands.push_back( - std::move(rv).forwardAsSingleValue(SGF, E)); - } - }; - for (auto &component : E->getComponents()) { switch (auto kind = component.getKind()) { @@ -3585,11 +3578,18 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { component.getSubscriptIndexHashableConformances(), baseTy, /*for descriptor*/ false)); - lowerSubscriptOperands(component); - - assert(numOperands == operands.size() - && "operand count out of sync"); baseTy = loweredComponents.back().getComponentType(); + if (kind == KeyPathExpr::Component::Kind::Property) + break; + + auto subscript = cast(decl); + auto loweredArgs = SGF.emitKeyPathSubscriptOperands( + subscript, component.getDeclRef().getSubstitutions(), + component.getIndexExpr()); + + for (auto &arg : loweredArgs) { + operands.push_back(arg.forward(SGF)); + } break; } @@ -3653,7 +3653,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) { auto pattern = KeyPathPattern::get(SGF.SGM.M, needsGenericContext ? SGF.F.getLoweredFunctionType() - ->getGenericSignature() + ->getInvocationGenericSignature() : nullptr, rootTy, baseTy, loweredComponents, @@ -3680,6 +3680,7 @@ RValue RValueEmitter:: visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E, SGFContext C) { switch (E->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: case MagicIdentifierLiteralExpr::Line: case MagicIdentifierLiteralExpr::Column: @@ -5022,13 +5023,17 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( return visit(E->getSubExpr(), C); }; - // Handle @convention(block). No withoutActuallyEscaping verification yet. - if (silFnTy->getExtInfo().getRepresentation() != - SILFunctionTypeRepresentation::Thick) { + // Handle @convention(block) an @convention(c). No withoutActuallyEscaping + // verification yet. + auto closureRepresentation = silFnTy->getExtInfo().getRepresentation(); + if (closureRepresentation != SILFunctionTypeRepresentation::Thick) { auto escapingClosure = SGF.B.createConvertFunction(E, functionValue, escapingFnTy, /*WithoutActuallyEscaping=*/true); - return visitSubExpr(escapingClosure, true /*isClosureConsumable*/); + bool isBlockConvention = + closureRepresentation == SILFunctionTypeRepresentation::Block; + return visitSubExpr(escapingClosure, + isBlockConvention /*isClosureConsumable*/); } // Convert it to an escaping function value. @@ -5383,6 +5388,25 @@ RValue RValueEmitter::visitTapExpr(TapExpr *E, SGFContext C) { return outerScope.popPreservingValue(std::move(result)); } +RValue RValueEmitter::visitDefaultArgumentExpr(DefaultArgumentExpr *E, + SGFContext C) { + // We should only be emitting this as an rvalue for caller-side default + // arguments such as magic literals. Other default arguments get handled + // specially. + return SGF.emitRValue(E->getCallerSideDefaultExpr()); +} + +RValue RValueEmitter::visitErrorExpr(ErrorExpr *E, SGFContext C) { + // Running into an ErrorExpr here means we've failed to lazily typecheck + // something. Just emit an undef of the appropriate type and carry on. + if (SGF.getASTContext().Diags.hadAnyError()) + return SGF.emitUndefRValue(E, E->getType()); + + // Use report_fatal_error to ensure we trap in release builds instead of + // miscompiling. + llvm::report_fatal_error("Found an ErrorExpr but didn't emit an error?"); +} + RValue SILGenFunction::emitRValue(Expr *E, SGFContext C) { assert(!E->getType()->hasLValueType() && "l-values must be emitted with emitLValue"); diff --git a/lib/SILGen/SILGenForeignError.cpp b/lib/SILGen/SILGenForeignError.cpp index 41acb7b8646da..f76fd6bafff94 100644 --- a/lib/SILGen/SILGenForeignError.cpp +++ b/lib/SILGen/SILGenForeignError.cpp @@ -113,7 +113,8 @@ static SILValue emitIntValue(SILGenFunction &SGF, SILLocation loc, if (auto structDecl = type.getStructOrBoundGenericStruct()) { auto properties = structDecl->getStoredProperties(); assert(properties.size() == 1); - SILType fieldType = type.getFieldType(properties[0], SGF.SGM.M); + SILType fieldType = type.getFieldType(properties[0], SGF.SGM.M, + SGF.getTypeExpansionContext()); SILValue fieldValue = emitIntValue(SGF, loc, fieldType, value); return SGF.B.createStruct(loc, type, fieldValue); } @@ -278,7 +279,7 @@ void SILGenFunction::emitForeignErrorBlock(SILLocation loc, // Propagate. FullExpr throwScope(Cleanups, CleanupLocation::get(loc)); - emitThrow(loc, error); + emitThrow(loc, error, true); } /// Perform a foreign error check by testing whether the call result is zero. diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index cfca3cefa3f5b..96ba74a233131 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -153,10 +153,12 @@ SILGenFunction::emitSiblingMethodRef(SILLocation loc, // dispatch (viz. objc_msgSend for now). if (methodConstant.hasDecl() && methodConstant.getDecl()->isObjCDynamic()) { - methodValue = emitDynamicMethodRef( - loc, methodConstant, - SGM.Types.getConstantInfo(methodConstant).SILFnType) - .getValue(); + methodValue = + emitDynamicMethodRef( + loc, methodConstant, + SGM.Types.getConstantInfo(getTypeExpansionContext(), methodConstant) + .SILFnType) + .getValue(); } else { methodValue = emitGlobalFunctionRef(loc, methodConstant); } @@ -164,7 +166,8 @@ SILGenFunction::emitSiblingMethodRef(SILLocation loc, SILType methodTy = methodValue->getType(); // Specialize the generic method. - methodTy = methodTy.substGenericArgs(SGM.M, subMap); + methodTy = + methodTy.substGenericArgs(SGM.M, subMap, getTypeExpansionContext()); return std::make_tuple(ManagedValue::forUnmanaged(methodValue), methodTy); @@ -190,8 +193,8 @@ void SILGenFunction::emitCaptures(SILLocation loc, canGuarantee = true; break; } - - auto expansion = F.getResilienceExpansion(); + + auto expansion = getTypeExpansionContext(); for (auto capture : captureInfo.getCaptures()) { if (capture.isDynamicSelfMetadata()) { @@ -262,10 +265,12 @@ void SILGenFunction::emitCaptures(SILLocation loc, capturedArgs.push_back(emitUndef(getLoweredType(type).getAddressType())); break; case CaptureKind::Box: { - auto boxTy = SGM.Types.getContextBoxTypeForCapture(vd, - getLoweredType(type).getASTType(), - FunctionDC->getGenericEnvironmentOfContext(), - /*mutable*/ true); + auto boxTy = SGM.Types.getContextBoxTypeForCapture( + vd, + SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + type), + FunctionDC->getGenericEnvironmentOfContext(), + /*mutable*/ true); capturedArgs.push_back(emitUndef(boxTy)); break; } @@ -273,8 +278,28 @@ void SILGenFunction::emitCaptures(SILLocation loc, continue; } - auto Entry = found->second; + // Get an address value for a SILValue if it is address only in an type + // expansion context without opaque archetype substitution. + auto getAddressValue = [&](SILValue entryValue) -> SILValue { + if (SGM.Types + .getTypeLowering( + valueType, + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( + expansion.getResilienceExpansion())) + .isAddressOnly() && + !entryValue->getType().isAddress()) { + + auto addr = emitTemporaryAllocation(vd, entryValue->getType()); + auto val = B.emitCopyValueOperation(vd, entryValue); + auto &lowering = getTypeLowering(entryValue->getType()); + lowering.emitStore(B, vd, val, addr, StoreOwnershipQualifier::Init); + entryValue = addr; + enterDestroyCleanup(addr); + } + return entryValue; + }; + auto Entry = found->second; switch (SGM.Types.getDeclCaptureKind(capture, expansion)) { case CaptureKind::Constant: { // let declarations. @@ -309,20 +334,25 @@ void SILGenFunction::emitCaptures(SILLocation loc, } case CaptureKind::StorageAddress: { + auto entryValue = getAddressValue(Entry.value); // No-escaping stored declarations are captured as the // address of the value. - assert(Entry.value->getType().isAddress() && "no address for captured var!"); - capturedArgs.push_back(ManagedValue::forLValue(Entry.value)); + assert(entryValue->getType().isAddress() && "no address for captured var!"); + capturedArgs.push_back(ManagedValue::forLValue(entryValue)); break; } case CaptureKind::Box: { + auto entryValue = getAddressValue(Entry.value); // LValues are captured as both the box owning the value and the // address of the value. - assert(Entry.value->getType().isAddress() && "no address for captured var!"); - + assert(entryValue->getType().isAddress() && "no address for captured var!"); + // Boxes of opaque return values stay opaque. + auto minimalLoweredType = SGM.Types.getLoweredRValueType( + TypeExpansionContext::minimal(), type->getCanonicalType()); // If this is a boxed variable, we can use it directly. - if (Entry.box) { + if (Entry.box && + entryValue->getType().getASTType() == minimalLoweredType) { // We can guarantee our own box to the callee. if (canGuarantee) { capturedArgs.push_back( @@ -330,7 +360,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, } else { capturedArgs.push_back(emitManagedRetain(loc, Entry.box)); } - escapesToMark.push_back(Entry.value); + escapesToMark.push_back(entryValue); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each @@ -343,14 +373,13 @@ void SILGenFunction::emitCaptures(SILLocation loc, // closure context and pass it down to the partially applied function // in-place. // TODO: Use immutable box for immutable captures. - auto boxTy = SGM.Types.getContextBoxTypeForCapture(vd, - Entry.value->getType().getASTType(), - FunctionDC->getGenericEnvironmentOfContext(), - /*mutable*/ true); - + auto boxTy = SGM.Types.getContextBoxTypeForCapture( + vd, minimalLoweredType, FunctionDC->getGenericEnvironmentOfContext(), + /*mutable*/ true); + AllocBoxInst *allocBox = B.createAllocBox(loc, boxTy); ProjectBoxInst *boxAddress = B.createProjectBox(loc, allocBox, 0); - B.createCopyAddr(loc, Entry.value, boxAddress, IsNotTake, + B.createCopyAddr(loc, entryValue, boxAddress, IsNotTake, IsInitialization); if (canGuarantee) capturedArgs.push_back( @@ -377,7 +406,7 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, SubstitutionMap subs) { auto loweredCaptureInfo = SGM.Types.getLoweredLocalCaptures(constant); - auto constantInfo = getConstantInfo(constant); + auto constantInfo = getConstantInfo(getTypeExpansionContext(), constant); SILValue functionRef = emitGlobalFunctionRef(loc, constant, constantInfo); SILType functionTy = functionRef->getType(); @@ -400,7 +429,8 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant, bool wasSpecialized = false; if (!subs.empty()) { - auto specialized = pft->substGenericArgs(F.getModule(), subs); + auto specialized = + pft->substGenericArgs(F.getModule(), subs, getTypeExpansionContext()); functionTy = SILType::getPrimitiveObjectType(specialized); wasSpecialized = true; } @@ -485,8 +515,10 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { auto *autoclosure = cast(ace); // Closure expressions implicitly return the result of their body // expression. - emitReturnExpr(ImplicitReturnLocation(ace), - autoclosure->getSingleExpressionBody()); + if (B.hasValidInsertionPoint()) { + emitReturnExpr(ImplicitReturnLocation(ace), + autoclosure->getSingleExpressionBody()); + } } emitEpilog(ace); } @@ -511,7 +543,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { // be imported. ASTContext &ctx = getASTContext(); - std::pair UIKitName = + Located UIKitName = {ctx.getIdentifier("UIKit"), SourceLoc()}; ModuleDecl *UIKit = ctx @@ -520,7 +552,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { assert(UIKit && "couldn't find UIKit objc module?!"); SmallVector results; UIKit->lookupQualified(UIKit, - ctx.getIdentifier("UIApplicationMain"), + DeclNameRef(ctx.getIdentifier("UIApplicationMain")), NL_QualifiedDefault, results); assert(results.size() == 1 @@ -557,6 +589,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { SILResultInfo(OptNSStringTy, ResultConvention::Autoreleased), /*error result*/ None, + SubstitutionMap(), false, ctx); auto NSStringFromClassFn = builder.getOrCreateFunction( mainClass, "NSStringFromClass", SILLinkage::PublicExternal, @@ -643,6 +676,7 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { SILResultInfo(argc->getType().getASTType(), ResultConvention::Unowned), /*error result*/ None, + SubstitutionMap(), false, getASTContext()); SILGenFunctionBuilder builder(SGM); @@ -703,9 +737,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc()); } - CaptureInfo captureInfo; - if (function.getAnyFunctionRef()) - captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); + auto captureInfo = SGM.M.Types.getLoweredLocalCaptures(function); auto interfaceType = value->getType()->mapTypeOutOfContext(); emitProlog(captureInfo, params, /*selfParam=*/nullptr, dc, interfaceType, /*throws=*/false, SourceLoc()); @@ -750,7 +782,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { // wrapper that was initialized with '=', the stored property initializer // will be in terms of the original property's type. if (auto originalProperty = var->getOriginalWrappedProperty()) { - if (originalProperty->isPropertyWrapperInitializedWithInitialValue()) { + if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) { interfaceType = originalProperty->getValueInterfaceType(); varType = originalProperty->getType(); } @@ -761,7 +793,8 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { prepareEpilog(varType, false, CleanupLocation::get(loc)); auto pbd = var->getParentPatternBinding(); - auto entry = pbd->getPatternEntryForVarDecl(var); + const auto i = pbd->getPatternEntryIndexForVarDecl(var); + auto *anchorVar = pbd->getAnchoringVarDecl(i); auto subs = getForwardingSubstitutionMap(); auto contextualType = dc->mapTypeIntoContext(interfaceType); auto resultType = contextualType->getCanonicalType(); @@ -775,7 +808,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { SmallVector cleanups; auto init = prepareIndirectResultInit(resultType, directResults, cleanups); - emitApplyOfStoredPropertyInitializer(loc, entry, subs, resultType, + emitApplyOfStoredPropertyInitializer(loc, anchorVar, subs, resultType, origResultType, SGFContext(init.get())); @@ -786,7 +819,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) { Scope scope(Cleanups, CleanupLocation(var)); // If we have no indirect results, just return the result. - auto result = emitApplyOfStoredPropertyInitializer(loc, entry, subs, + auto result = emitApplyOfStoredPropertyInitializer(loc, anchorVar, subs, resultType, origResultType, SGFContext()) diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index bc55917e75457..f0a4c9d7646bf 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -492,7 +492,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SILFunction &getFunction() { return F; } SILModule &getModule() { return F.getModule(); } SILGenBuilder &getBuilder() { return B; } - SILOptions &getOptions() { return getModule().getOptions(); } + const SILOptions &getOptions() { return getModule().getOptions(); } + + // Returns the type expansion context for types in this function. + TypeExpansionContext getTypeExpansionContext() { + return TypeExpansionContext(getFunction()); + } const TypeLowering &getTypeLowering(AbstractionPattern orig, Type subst) { return F.getTypeLowering(orig, subst); @@ -500,16 +505,24 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction const TypeLowering &getTypeLowering(Type t) { return F.getTypeLowering(t); } - CanSILFunctionType getSILFunctionType(AbstractionPattern orig, + CanSILFunctionType getSILFunctionType(TypeExpansionContext context, + AbstractionPattern orig, CanFunctionType substFnType) { - return SGM.Types.getSILFunctionType(orig, substFnType); + return SGM.Types.getSILFunctionType(context, orig, substFnType); } - SILType getLoweredType(AbstractionPattern orig, Type subst) { + SILType getLoweredType(AbstractionPattern orig, + Type subst) { return F.getLoweredType(orig, subst); } SILType getLoweredType(Type t) { return F.getLoweredType(t); } + SILType getLoweredTypeForFunctionArgument(Type t) { + auto typeForConv = + SGM.Types.getLoweredType(t, TypeExpansionContext::minimal()); + return getLoweredType(t).getCategoryType(typeForConv.getCategory()); + } + SILType getLoweredLoadableType(Type t) { return F.getLoweredLoadableType(t); } @@ -517,15 +530,26 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction return F.getTypeLowering(type); } - SILType getSILType(SILParameterInfo param) const { - return silConv.getSILType(param); + SILType getSILType(SILParameterInfo param, CanSILFunctionType fnTy) const { + return silConv.getSILType(param, fnTy); + } + SILType getSILType(SILResultInfo result, CanSILFunctionType fnTy) const { + return silConv.getSILType(result, fnTy); + } + + SILType getSILTypeInContext(SILResultInfo result, CanSILFunctionType fnTy) { + auto t = F.mapTypeIntoContext(getSILType(result, fnTy)); + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); } - SILType getSILType(SILResultInfo result) const { - return silConv.getSILType(result); + + SILType getSILTypeInContext(SILParameterInfo param, CanSILFunctionType fnTy) { + auto t = F.mapTypeIntoContext(getSILType(param, fnTy)); + return getTypeLowering(t).getLoweredType().getCategoryType(t.getCategory()); } - const SILConstantInfo &getConstantInfo(SILDeclRef constant) { - return SGM.Types.getConstantInfo(constant); + const SILConstantInfo &getConstantInfo(TypeExpansionContext context, + SILDeclRef constant) { + return SGM.Types.getConstantInfo(context, constant); } Optional getStaticEnforcement(VarDecl *var = nullptr); @@ -533,6 +557,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction Optional getUnknownEnforcement(VarDecl *var = nullptr); SourceManager &getSourceManager() { return SGM.M.getASTContext().SourceMgr; } + std::string getMagicFileString(SourceLoc loc); + StringRef getMagicFilePathString(SourceLoc loc); + StringRef getMagicFunctionString(); /// Push a new debug scope and set its parent pointer. void enterDebugScope(SILLocation Loc) { @@ -664,7 +691,19 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree, bool isSelfConformance); - + + /// Generates subscript arguments for keypath. This function handles lowering + /// of all index expressions including default arguments. + /// + /// \returns Lowered index arguments. + /// \param subscript - The subscript decl who's arguments are being lowered. + /// \param subs - Used to get subscript function type and to substitute generic args. + /// \param indexExpr - An expression holding the indices of the + /// subscript (either a TupleExpr or a ParenExpr). + SmallVector + emitKeyPathSubscriptOperands(SubscriptDecl *subscript, SubstitutionMap subs, + Expr *indexExpr); + /// Convert a block to a native function with a thunk. ManagedValue emitBlockToFunc(SILLocation loc, ManagedValue block, @@ -770,7 +809,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// emitProlog - Generates prolog code to allocate and clean up mutable /// storage for closure captures and local arguments. - void emitProlog(const CaptureInfo &captureInfo, + void emitProlog(CaptureInfo captureInfo, ParameterList *paramList, ParamDecl *selfParam, DeclContext *DC, Type resultType, bool throws, SourceLoc throwsLoc); @@ -1162,7 +1201,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Returns a reference to a constant in global context. For local func decls /// this returns the function constant with unapplied closure context. SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant) { - return emitGlobalFunctionRef(loc, constant, getConstantInfo(constant)); + return emitGlobalFunctionRef( + loc, constant, getConstantInfo(getTypeExpansionContext(), constant)); } SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, @@ -1454,7 +1494,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue emitApplyOfStoredPropertyInitializer( SILLocation loc, - const PatternBindingEntry &entry, + VarDecl *anchoringVar, SubstitutionMap subs, CanType resultType, AbstractionPattern origResultType, @@ -1513,6 +1553,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue emitLiteral(LiteralExpr *literal, SGFContext C); SILBasicBlock *getTryApplyErrorDest(SILLocation loc, + CanSILFunctionType fnTy, SILResultInfo exnResult, bool isSuppressed); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index a601027713e81..747c6a97d1922 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -201,35 +201,37 @@ static CanType getSubstFormalRValueType(Expr *expr) { return expr->getType()->getRValueType()->getCanonicalType(); } -static LValueTypeData getAbstractedTypeData(SILGenModule &SGM, +static LValueTypeData getAbstractedTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, AbstractionPattern origFormalType, CanType substFormalType) { return { - accessKind, - origFormalType, - substFormalType, - SGM.Types.getLoweredRValueType(origFormalType, substFormalType) - }; + accessKind, origFormalType, substFormalType, + SGM.Types.getLoweredRValueType(context, origFormalType, substFormalType)}; } -static LValueTypeData getLogicalStorageTypeData(SILGenModule &SGM, +static LValueTypeData getLogicalStorageTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, CanType substFormalType) { assert(!isa(substFormalType)); AbstractionPattern origFormalType( substFormalType.getReferenceStorageReferent()); - return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); + return getAbstractedTypeData(context, SGM, accessKind, origFormalType, + substFormalType); } -static LValueTypeData getPhysicalStorageTypeData(SILGenModule &SGM, +static LValueTypeData getPhysicalStorageTypeData(TypeExpansionContext context, + SILGenModule &SGM, SGFAccessKind accessKind, AbstractStorageDecl *storage, CanType substFormalType) { assert(!isa(substFormalType)); auto origFormalType = SGM.Types.getAbstractionPattern(storage) .getReferenceStorageReferentType(); - return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); + return getAbstractedTypeData(context, SGM, accessKind, origFormalType, + substFormalType); } static bool shouldUseUnsafeEnforcement(VarDecl *var) { @@ -1208,8 +1210,10 @@ namespace { bool doesAccessorMutateSelf(SILGenFunction &SGF, SILDeclRef accessor) const { - auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); - return accessorSelf.getType() && accessorSelf.isIndirectMutating(); + auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter( + SGF.getTypeExpansionContext(), accessor); + return accessorSelf.getInterfaceType() + && accessorSelf.isIndirectMutating(); } void printBase(raw_ostream &OS, unsigned indent, StringRef name) const { @@ -1395,10 +1399,12 @@ namespace { CanType ValType = SGF.F.mapTypeIntoContext(backingVar->getInterfaceType()) ->getCanonicalType(); - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(backingVar, ValType); + // TODO: revist minimal + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + TypeExpansionContext::minimal(), backingVar, ValType); auto typeData = - getLogicalStorageTypeData(SGF.SGM, getTypeData().AccessKind, ValType); + getLogicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + getTypeData().AccessKind, ValType); // Get the address of the storage property. ManagedValue proj; @@ -1440,11 +1446,13 @@ namespace { ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI); // Create the allocating setter function. It captures the base address. - auto setterInfo = SGF.getConstantInfo(setter); + auto setterInfo = + SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter); SILValue setterFRef; if (setter.hasDecl() && setter.getDecl()->isObjCDynamic()) { auto methodTy = SILType::getPrimitiveObjectType( - SGF.SGM.Types.getConstantFunctionType(setter)); + SGF.SGM.Types.getConstantFunctionType(SGF.getTypeExpansionContext(), + setter)); setterFRef = SGF.B.createObjCMethod( loc, base.getValue(), setter, methodTy); } else @@ -2011,10 +2019,12 @@ namespace { auto projectFnType = projectFn->getLoweredFunctionType(); auto keyPathTy = keyPathValue.getType().castTo(); - auto subs = SubstitutionMap::get(projectFnType->getGenericSignature(), - keyPathTy->getGenericArgs(), {}); + auto subs = SubstitutionMap::get( + projectFnType->getInvocationGenericSignature(), + keyPathTy->getGenericArgs(), {}); - auto substFnType = projectFnType->substGenericArgs(SGF.SGM.M, subs); + auto substFnType = projectFnType->substGenericArgs( + SGF.SGM.M, subs, SGF.getTypeExpansionContext()); // Perform the begin_apply. SmallVector yields; @@ -2482,8 +2492,9 @@ namespace { void emitUsingStrategy(AccessStrategy strategy) { switch (strategy.getKind()) { case AccessStrategy::Storage: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingStorage(typeData); } @@ -2494,8 +2505,8 @@ namespace { return asImpl().emitUsingAccessor(strategy.getAccessor(), false); case AccessStrategy::MaterializeToTemporary: { - auto typeData = - getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType); return asImpl().emitUsingMaterialization(strategy.getReadStrategy(), strategy.getWriteStrategy(), typeData); @@ -2511,22 +2522,24 @@ namespace { switch (accessorKind) { case AccessorKind::Get: case AccessorKind::Set: { - auto typeData = - getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType); return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData); } case AccessorKind::Address: case AccessorKind::MutableAddress: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingAddressor(accessor, isDirect, typeData); } case AccessorKind::Read: case AccessorKind::Modify: { - auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, - FormalRValueType); + auto typeData = + getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, + AccessKind, Storage, FormalRValueType); return asImpl().emitUsingCoroutineAccessor(accessor, isDirect, typeData); } @@ -3050,8 +3063,8 @@ struct MemberStorageAccessEmitter : AccessEmitter { void emitUsingAddressor(SILDeclRef addressor, bool isDirect, LValueTypeData typeData) { - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + SGF.getTypeExpansionContext(), Storage, FormalRValueType); LV.add(Storage, addressor, IsSuper, isDirect, Subs, BaseFormalType, typeData, varStorageType, @@ -3115,8 +3128,8 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, } // Otherwise, it's a physical member. - SILType varStorageType = - SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + SGF.getTypeExpansionContext(), Storage, FormalRValueType); if (BaseFormalType->mayHaveSuperclass()) { LV.add(Storage, Options, varStorageType, typeData); @@ -3252,8 +3265,8 @@ LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, }(); if (useLogical) { - auto typeData = getLogicalStorageTypeData(SGF.SGM, accessKind, - substFormalType); + auto typeData = getLogicalStorageTypeData( + SGF.getTypeExpansionContext(), SGF.SGM, accessKind, substFormalType); Type baseFormalType = e->getBase()->getType()->getRValueType(); lv.add(typeData, keyPathKind, keyPath, @@ -3263,9 +3276,9 @@ LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, // in the opaque AbstractionPattern and push an OrigToSubstComponent here // so it can be peepholed. } else { - auto typeData = getAbstractedTypeData(SGF.SGM, accessKind, - AbstractionPattern::getOpaque(), - substFormalType); + auto typeData = getAbstractedTypeData( + TypeExpansionContext::minimal(), SGF.SGM, accessKind, + AbstractionPattern::getOpaque(), substFormalType); lv.add(typeData, keyPathKind, keyPath); @@ -3628,22 +3641,22 @@ SILValue SILGenFunction::emitConversionToSemanticRValue(SILLocation loc, case ReferenceOwnership::Name: \ /* Address-only storage types are handled with their underlying type. */ \ llvm_unreachable("address-only pointers are handled elsewhere"); -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case ReferenceOwnership::Name: \ - return B.createCopy##Name##Value(loc, src); -#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case ReferenceOwnership::Name: { \ - /* For loadable reference storage types, we need to generate a strong */ \ - /* retain and strip the box. */ \ - assert(storageType.castTo()->isLoadable( \ - ResilienceExpansion::Maximal)); \ - return B.createCopy##Name##Value(loc, src); \ +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case ReferenceOwnership::Name: \ + return B.createStrongCopy##Name##Value(loc, src); +#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case ReferenceOwnership::Name: { \ + /* For loadable reference storage types, we need to generate a strong */ \ + /* retain and strip the box. */ \ + assert(storageType.castTo()->isLoadable( \ + ResilienceExpansion::Maximal)); \ + return B.createStrongCopy##Name##Value(loc, src); \ } #define UNCHECKED_REF_STORAGE(Name, ...) \ case ReferenceOwnership::Name: { \ /* For static reference storage types, we need to strip the box and */ \ /* then do an (unsafe) retain. */ \ - return B.createCopy##Name##Value(loc, src); \ + return B.createStrongCopy##Name##Value(loc, src); \ } #include "swift/AST/ReferenceStorage.def" } @@ -3661,14 +3674,14 @@ ManagedValue SILGenFunction::emitConversionToSemanticRValue( case ReferenceOwnership::Name: \ /* Address-only storage types are handled with their underlying type. */ \ llvm_unreachable("address-only pointers are handled elsewhere"); -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case ReferenceOwnership::Name: \ - /* Generate a strong retain and strip the box. */ \ - return B.createCopy##Name##Value(loc, src); +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case ReferenceOwnership::Name: \ + /* Generate a strong retain and strip the box. */ \ + return B.createStrongCopy##Name##Value(loc, src); #define UNCHECKED_REF_STORAGE(Name, ...) \ case ReferenceOwnership::Name: \ /* Strip the box and then do an (unsafe) retain. */ \ - return B.createCopy##Name##Value(loc, src); + return B.createStrongCopy##Name##Value(loc, src); #include "swift/AST/ReferenceStorage.def" } llvm_unreachable("impossible"); @@ -3691,22 +3704,22 @@ static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF, #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case ReferenceOwnership::Name: \ return SGF.B.createLoad##Name(loc, src, isTake); -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ - { \ - /* For loadable types, we need to strip the box. */ \ - /* If we are not performing a take, use a load_borrow. */ \ - if (!isTake) { \ - SILValue value = SGF.B.createLoadBorrow(loc, src); \ - SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \ - SGF.B.createEndBorrow(loc, value, src); \ - return strongValue; \ - } \ - /* Otherwise perform a load take and destroy the stored value. */ \ - auto value = SGF.B.emitLoadValueOperation(loc, src, \ - LoadOwnershipQualifier::Take); \ - SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \ - SGF.B.createDestroyValue(loc, value); \ - return strongValue; \ +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ + { \ + /* For loadable types, we need to strip the box. */ \ + /* If we are not performing a take, use a load_borrow. */ \ + if (!isTake) { \ + SILValue value = SGF.B.createLoadBorrow(loc, src); \ + SILValue strongValue = SGF.B.createStrongCopy##Name##Value(loc, value); \ + SGF.B.createEndBorrow(loc, value, src); \ + return strongValue; \ + } \ + /* Otherwise perform a load take and destroy the stored value. */ \ + auto value = \ + SGF.B.emitLoadValueOperation(loc, src, LoadOwnershipQualifier::Take); \ + SILValue strongValue = SGF.B.createStrongCopy##Name##Value(loc, value); \ + SGF.B.createDestroyValue(loc, value); \ + return strongValue; \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case ReferenceOwnership::Name: \ @@ -3724,7 +3737,7 @@ static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF, case ReferenceOwnership::Name: { \ /* For static reference storage types, we need to strip the box. */ \ auto value = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Trivial); \ - return SGF.B.createCopy##Name##Value(loc, value); \ + return SGF.B.createStrongCopy##Name##Value(loc, value); \ } #include "swift/AST/ReferenceStorage.def" #undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER @@ -3927,8 +3940,7 @@ static ManagedValue drillIntoComponent(SILGenFunction &SGF, bool isRValue = component.isRValue(); ManagedValue addr = std::move(component).project(SGF, loc, base); - if (!SGF.getASTContext().LangOpts.DisableTsanInoutInstrumentation && - (SGF.getModule().getOptions().Sanitizers & SanitizerKind::Thread) && + if ((SGF.getModule().getOptions().Sanitizers & SanitizerKind::Thread) && tsanKind == TSanKind::InoutAccess && !isRValue) { emitTsanInoutAccess(SGF, loc, addr); } diff --git a/lib/SILGen/SILGenLazyConformance.cpp b/lib/SILGen/SILGenLazyConformance.cpp index fdf8f8892631e..791ae643c7f4c 100644 --- a/lib/SILGen/SILGenLazyConformance.cpp +++ b/lib/SILGen/SILGenLazyConformance.cpp @@ -109,17 +109,15 @@ void SILGenModule::useConformancesFromObjectiveCType(CanType type) { return; if (objectiveCBridgeable) { - auto subConformance = SwiftModule->lookupConformance( - t, objectiveCBridgeable); - if (subConformance) - useConformance(*subConformance); + if (auto subConformance = + SwiftModule->lookupConformance(t, objectiveCBridgeable)) + useConformance(subConformance); } if (bridgedStoredNSError) { - auto subConformance = SwiftModule->lookupConformance( - t, bridgedStoredNSError); - if (subConformance) - useConformance(*subConformance); + if (auto subConformance = + SwiftModule->lookupConformance(t, bridgedStoredNSError)) + useConformance(subConformance); } }); } @@ -176,24 +174,24 @@ class LazyConformanceEmitter : public SILInstructionVisitorgetSourceType()); - SGM.useConformancesFromType(CCBI->getTargetType()); - SGM.useConformancesFromObjectiveCType(CCBI->getSourceType()); - SGM.useConformancesFromObjectiveCType(CCBI->getTargetType()); + SGM.useConformancesFromType(CCBI->getSourceFormalType()); + SGM.useConformancesFromType(CCBI->getTargetFormalType()); + SGM.useConformancesFromObjectiveCType(CCBI->getSourceFormalType()); + SGM.useConformancesFromObjectiveCType(CCBI->getTargetFormalType()); } void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI) { - SGM.useConformancesFromType(CCABI->getSourceType()); - SGM.useConformancesFromType(CCABI->getTargetType()); - SGM.useConformancesFromObjectiveCType(CCABI->getSourceType()); - SGM.useConformancesFromObjectiveCType(CCABI->getTargetType()); + SGM.useConformancesFromType(CCABI->getSourceFormalType()); + SGM.useConformancesFromType(CCABI->getTargetFormalType()); + SGM.useConformancesFromObjectiveCType(CCABI->getSourceFormalType()); + SGM.useConformancesFromObjectiveCType(CCABI->getTargetFormalType()); } void visitCheckedCastValueBranchInst(CheckedCastValueBranchInst *CCVBI) { - SGM.useConformancesFromType(CCVBI->getSourceType()); - SGM.useConformancesFromType(CCVBI->getTargetType()); - SGM.useConformancesFromObjectiveCType(CCVBI->getSourceType()); - SGM.useConformancesFromObjectiveCType(CCVBI->getTargetType()); + SGM.useConformancesFromType(CCVBI->getSourceFormalType()); + SGM.useConformancesFromType(CCVBI->getTargetFormalType()); + SGM.useConformancesFromObjectiveCType(CCVBI->getSourceFormalType()); + SGM.useConformancesFromObjectiveCType(CCVBI->getTargetFormalType()); } void visitCopyAddrInst(CopyAddrInst *CAI) { @@ -291,17 +289,17 @@ class LazyConformanceEmitter : public SILInstructionVisitorgetSourceType()); - SGM.useConformancesFromType(UCCI->getTargetType()); - SGM.useConformancesFromObjectiveCType(UCCI->getSourceType()); - SGM.useConformancesFromObjectiveCType(UCCI->getTargetType()); + SGM.useConformancesFromType(UCCI->getSourceFormalType()); + SGM.useConformancesFromType(UCCI->getTargetFormalType()); + SGM.useConformancesFromObjectiveCType(UCCI->getSourceFormalType()); + SGM.useConformancesFromObjectiveCType(UCCI->getTargetFormalType()); } void visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *UCCAI) { - SGM.useConformancesFromType(UCCAI->getSourceType()); - SGM.useConformancesFromType(UCCAI->getTargetType()); - SGM.useConformancesFromObjectiveCType(UCCAI->getSourceType()); - SGM.useConformancesFromObjectiveCType(UCCAI->getTargetType()); + SGM.useConformancesFromType(UCCAI->getSourceFormalType()); + SGM.useConformancesFromType(UCCAI->getTargetFormalType()); + SGM.useConformancesFromObjectiveCType(UCCAI->getSourceFormalType()); + SGM.useConformancesFromObjectiveCType(UCCAI->getTargetFormalType()); } void visitUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *UTEDAI) { diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index f01e6070631a4..711b70d101178 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -311,7 +311,7 @@ namespace { /// A row which we intend to specialize. struct RowToSpecialize { /// The pattern from this row which we are specializing upon. - Pattern *Pattern; + swift::Pattern *Pattern; /// The index of the target row. unsigned RowIndex; @@ -1878,7 +1878,8 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( bool hasNonVoidAssocValue = false; bool hasAssocValue = elt->hasAssociatedValues(); if (hasAssocValue) { - eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); + eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M, + SGF.getTypeExpansionContext()); hasNonVoidAssocValue = !eltTy.getASTType()->isVoid(); } @@ -2060,7 +2061,8 @@ void PatternMatchEmission::emitEnumElementDispatch( SILType eltTy; bool hasElt = false; if (elt->hasAssociatedValues()) { - eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M); + eltTy = src.getType().getEnumElementType(elt, SGF.SGM.M, + SGF.getTypeExpansionContext()); hasElt = !eltTy.getASTType()->isVoid(); } @@ -2319,7 +2321,7 @@ void PatternMatchEmission::emitCaseBody(CaseStmt *caseBlock) { RegularLocation::getAutoGeneratedLocation(caseBlock->getEndLoc()); if (auto *braces = dyn_cast(caseBlock->getBody())) if (braces->getNumElements() == 1 && - dyn_cast_or_null(braces->getElement(0).dyn_cast())) + dyn_cast_or_null(braces->getFirstElement().dyn_cast())) cleanupLoc = CleanupLocation(caseBlock); SGF.emitBreakOutOf(cleanupLoc, PatternMatchStmt); } @@ -2465,7 +2467,7 @@ void PatternMatchEmission::emitSharedCaseBlocks() { } else { SILValue arg = caseBB->getArgument(argIndex++); assert(arg.getOwnershipKind() == ValueOwnershipKind::Owned || - arg.getOwnershipKind() == ValueOwnershipKind::Any); + arg.getOwnershipKind() == ValueOwnershipKind::None); mv = SGF.emitManagedRValueWithCleanup(arg); } diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index bbfc31585879c..16d45f0274957 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -103,6 +103,7 @@ using namespace swift; using namespace Lowering; + /// A helper function that pulls an element off the front of an array. template static const T &claimNext(ArrayRef &array) { @@ -176,7 +177,8 @@ collectExistentialConformances(ModuleDecl *M, CanType fromType, CanType toType) for (auto proto : protocols) { auto conformance = M->lookupConformance(fromType, proto->getDecl()); - conformances.push_back(*conformance); + assert(conformance); + conformances.push_back(conformance); } return M->getASTContext().AllocateCopy(conformances); @@ -400,8 +402,9 @@ ManagedValue Transform::transform(ManagedValue v, // Optional-to-optional conversion. if (inputIsOptional && outputIsOptional) { // If the conversion is trivial, just cast. - if (SGF.SGM.Types.checkForABIDifferences(v.getType(), loweredResultTy) - == TypeConverter::ABIDifference::Trivial) { + if (SGF.SGM.Types.checkForABIDifferences(SGF.SGM.M, + v.getType(), loweredResultTy) + == TypeConverter::ABIDifference::CompatibleRepresentation) { if (v.getType().isAddress()) return SGF.B.createUncheckedAddrCast(Loc, v, loweredResultTy); return SGF.B.createUncheckedBitCast(Loc, v, loweredResultTy); @@ -594,9 +597,8 @@ ManagedValue Transform::transform(ManagedValue v, auto conformance = SGF.SGM.M.getSwiftModule()->lookupConformance( inputSubstType, protocol); auto addr = v.getType().isAddress() ? v : v.materialize(SGF, Loc); - auto result = SGF.emitAnyHashableErasure(Loc, addr, - inputSubstType, *conformance, - ctxt); + auto result = SGF.emitAnyHashableErasure(Loc, addr, inputSubstType, + conformance, ctxt); if (result.isInContext()) return ManagedValue::forInContext(); return std::move(result).getAsSingleValue(SGF, Loc); @@ -785,7 +787,11 @@ void SILGenFunction::collectThunkParams( // Add the indirect results. for (auto resultTy : F.getConventions().getIndirectSILResultTypes()) { auto paramTy = F.mapTypeIntoContext(resultTy); - SILArgument *arg = F.begin()->createFunctionArgument(paramTy); + // Lower result parameters in the context of the function: opaque result + // types will be lowered to their underlying type if allowed by resilience. + auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) + .getCategoryType(paramTy.getCategory()); + SILArgument *arg = F.begin()->createFunctionArgument(inContextParamTy); if (indirectResults) indirectResults->push_back(arg); } @@ -794,7 +800,11 @@ void SILGenFunction::collectThunkParams( auto paramTypes = F.getLoweredFunctionType()->getParameters(); for (auto param : paramTypes) { auto paramTy = F.mapTypeIntoContext(F.getConventions().getSILType(param)); - params.push_back(B.createInputFunctionArgument(paramTy, loc)); + // Lower parameters in the context of the function: opaque result types will + // be lowered to their underlying type if allowed by resilience. + auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) + .getCategoryType(paramTy.getCategory()); + params.push_back(B.createInputFunctionArgument(inContextParamTy, loc)); } } @@ -1034,7 +1044,8 @@ namespace { assert(outputOrigType.isTypeParameter() && "Output is not a tuple and is not opaque?"); - auto outputTy = SGF.getSILType(claimNextOutputType()); + auto outputTy = SGF.getSILType(claimNextOutputType(), + CanSILFunctionType()); auto &outputTL = SGF.getTypeLowering(outputTy); if (SGF.silConv.useLoweredAddresses()) { auto temp = SGF.emitTemporary(Loc, outputTL); @@ -1154,7 +1165,8 @@ namespace { // Collect the tuple elements. auto &loweredTL = SGF.getTypeLowering(outputOrigType, outputTupleType); auto loweredTy = loweredTL.getLoweredType(); - auto optionalTy = SGF.getSILType(claimNextOutputType()); + auto optionalTy = SGF.getSILType(claimNextOutputType(), + CanSILFunctionType()); auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); if (loweredTL.isLoadable() || !SGF.silConv.useLoweredAddresses()) { auto payload = @@ -1411,12 +1423,12 @@ namespace { ManagedValue input, SILParameterInfo result) { // Easy case: we want to pass exactly this value. - if (input.getType() == SGF.getSILType(result)) { + if (input.getType() == SGF.getSILType(result, CanSILFunctionType())) { switch (result.getConvention()) { case ParameterConvention::Direct_Owned: case ParameterConvention::Indirect_In: if (!input.hasCleanup() && - input.getOwnershipKind() != ValueOwnershipKind::Any) + input.getOwnershipKind() != ValueOwnershipKind::None) input = input.copyUnmanaged(SGF, Loc); break; @@ -1434,7 +1446,8 @@ namespace { case ParameterConvention::Direct_Unowned: translateIntoOwned(inputOrigType, inputSubstType, outputOrigType, outputSubstType, input); - assert(Outputs.back().getType() == SGF.getSILType(result)); + assert(Outputs.back().getType() == SGF.getSILType(result, + CanSILFunctionType())); return; case ParameterConvention::Direct_Guaranteed: translateIntoGuaranteed(inputOrigType, inputSubstType, outputOrigType, @@ -1443,23 +1456,27 @@ namespace { case ParameterConvention::Indirect_In: { if (SGF.silConv.useLoweredAddresses()) { translateIndirect(inputOrigType, inputSubstType, outputOrigType, - outputSubstType, input, SGF.getSILType(result)); + outputSubstType, input, + SGF.getSILType(result, CanSILFunctionType())); return; } translateIntoOwned(inputOrigType, inputSubstType, outputOrigType, outputSubstType, input); - assert(Outputs.back().getType() == SGF.getSILType(result)); + assert(Outputs.back().getType() == + SGF.getSILType(result, CanSILFunctionType())); return; } case ParameterConvention::Indirect_In_Guaranteed: { if (SGF.silConv.useLoweredAddresses()) { translateIndirect(inputOrigType, inputSubstType, outputOrigType, - outputSubstType, input, SGF.getSILType(result)); + outputSubstType, input, + SGF.getSILType(result, CanSILFunctionType())); return; } translateIntoGuaranteed(inputOrigType, inputSubstType, outputOrigType, outputSubstType, input); - assert(Outputs.back().getType() == SGF.getSILType(result)); + assert(Outputs.back().getType() == + SGF.getSILType(result, CanSILFunctionType())); return; } case ParameterConvention::Indirect_Inout: @@ -1481,13 +1498,13 @@ namespace { ManagedValue input, SILParameterInfo result) { assert(input.isLValue()); - if (input.getType() == SGF.getSILType(result)) { + if (input.getType() == SGF.getSILType(result, CanSILFunctionType())) { Outputs.push_back(input); return; } // Create a temporary of the right type. - auto &temporaryTL = SGF.getTypeLowering(result.getType()); + auto &temporaryTL = SGF.getTypeLowering(result.getInterfaceType()); auto temporary = SGF.emitTemporary(Loc, temporaryTL); // Take ownership of the input value. This leaves the input l-value @@ -1633,7 +1650,7 @@ static ManagedValue manageYield(SILGenFunction &SGF, SILValue value, return SGF.emitManagedRValueWithCleanup(value); case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Direct_Unowned: - if (value.getOwnershipKind() == ValueOwnershipKind::Any) + if (value.getOwnershipKind() == ValueOwnershipKind::None) return ManagedValue::forUnmanaged(value); return ManagedValue::forBorrowedObjectRValue(value); case ParameterConvention::Indirect_In_Guaranteed: @@ -1669,7 +1686,8 @@ static void translateYields(SILGenFunction &SGF, SILLocation loc, // them into SILParameterInfos. SmallVector outerLoweredTypesAsParameters; for (auto unmappedInfo : outerInfos.getLoweredTypes()) { - auto mappedTy = SGF.F.mapTypeIntoContext(unmappedInfo.getSILStorageType()); + auto mappedTy = SGF.F.mapTypeIntoContext( + unmappedInfo.getSILStorageInterfaceType()); outerLoweredTypesAsParameters.push_back({mappedTy.getASTType(), unmappedInfo.getConvention()}); } @@ -1951,7 +1969,8 @@ class ResultPlanner { assert(SGF.silConv.isSILIndirect(innerResult) || !SGF.silConv.useLoweredAddresses()); auto temporary = - SGF.emitTemporaryAllocation(Loc, SGF.getSILType(innerResult)); + SGF.emitTemporaryAllocation(Loc, + SGF.getSILType(innerResult, CanSILFunctionType())); data.InnerIndirectResultAddrs.push_back(temporary); return temporary; } @@ -2322,7 +2341,8 @@ ResultPlanner::planTupleIntoDirectResult(AbstractionPattern innerOrigType, if (outerSubstObjectType) { auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); SILType outerObjectType = - SGF.getSILType(outerResult).getOptionalObjectType(); + SGF.getSILType(outerResult, CanSILFunctionType()) + .getOptionalObjectType(); SILResultInfo outerObjectResult(outerObjectType.getASTType(), outerResult.getConvention()); @@ -2366,7 +2386,8 @@ ResultPlanner::planTupleIntoDirectResult(AbstractionPattern innerOrigType, // Create direct outer results for each of the elements. for (auto eltIndex : indices(innerSubstType.getElementTypes())) { auto outerEltType = - SGF.getSILType(outerResult).getTupleElementType(eltIndex); + SGF.getSILType(outerResult, CanSILFunctionType()) + .getTupleElementType(eltIndex); SILResultInfo outerEltResult(outerEltType.getASTType(), outerResult.getConvention()); @@ -2406,7 +2427,8 @@ void ResultPlanner::planScalarIntoDirectResult(AbstractionPattern innerOrigType, // Otherwise, we have two direct results. // If there's no abstraction difference, it's just returned directly. - if (SGF.getSILType(innerResult) == SGF.getSILType(outerResult)) { + if (SGF.getSILType(innerResult, CanSILFunctionType()) + == SGF.getSILType(outerResult, CanSILFunctionType())) { addDirectToDirect(innerResult, outerResult); // Otherwise, we need to reabstract. @@ -2431,7 +2453,7 @@ ResultPlanner::planScalarIntoIndirectResult(AbstractionPattern innerOrigType, assert(!outerOrigType.isTuple()); bool hasAbstractionDifference = - (innerResult.getType() != outerResultAddr->getType().getASTType()); + (innerResult.getInterfaceType() != outerResultAddr->getType().getASTType()); // If the inner result is indirect, we need some memory to emit it into. if (SGF.silConv.isSILIndirect(innerResult)) { @@ -2568,7 +2590,7 @@ ResultPlanner::planScalarFromIndirectResult(AbstractionPattern innerOrigType, assert(SGF.silConv.isSILIndirect(outerResult) == bool(optOuterResultAddr)); bool hasAbstractionDifference = - (innerResultAddr->getType().getASTType() != outerResult.getType()); + (innerResultAddr->getType().getASTType() != outerResult.getInterfaceType()); // The outer result can be indirect, and it doesn't necessarily have an // abstraction difference. Note that we should only end up in this path @@ -2649,8 +2671,8 @@ void ResultPlanner::execute(ArrayRef innerDirectResults, // A helper function to claim an inner direct result. auto claimNextInnerDirectResult = [&](SILResultInfo result) -> ManagedValue { auto resultValue = claimNext(innerDirectResults); - assert(resultValue->getType() == SGF.getSILType(result)); - auto &resultTL = SGF.getTypeLowering(result.getType()); + assert(resultValue->getType() == SGF.getSILType(result, CanSILFunctionType())); + auto &resultTL = SGF.getTypeLowering(result.getInterfaceType()); switch (result.getConvention()) { case ResultConvention::Indirect: assert(!SGF.silConv.isSILIndirect(result) @@ -2675,8 +2697,8 @@ void ResultPlanner::execute(ArrayRef innerDirectResults, // A helper function to add an outer direct result. auto addOuterDirectResult = [&](ManagedValue resultValue, SILResultInfo result) { - assert(resultValue.getType() - == SGF.F.mapTypeIntoContext(SGF.getSILType(result))); + assert(resultValue.getType() == + SGF.getSILTypeInContext(result, CanSILFunctionType())); outerDirectResults.push_back(resultValue.forward(SGF)); }; @@ -2768,7 +2790,8 @@ void ResultPlanner::execute(ArrayRef innerDirectResults, case Operation::TupleDirect: { auto firstEltIndex = outerDirectResults.size() - op.NumElements; auto elts = makeArrayRef(outerDirectResults).slice(firstEltIndex); - auto tupleType = SGF.F.mapTypeIntoContext(SGF.getSILType(op.OuterResult)); + auto tupleType = SGF.F.mapTypeIntoContext( + SGF.getSILType(op.OuterResult, CanSILFunctionType())); auto tuple = SGF.B.createTuple(Loc, tupleType, elts); outerDirectResults.resize(firstEltIndex); outerDirectResults.push_back(tuple); @@ -2777,7 +2800,8 @@ void ResultPlanner::execute(ArrayRef innerDirectResults, case Operation::InjectOptionalDirect: { SILValue value = outerDirectResults.pop_back_val(); - auto tupleType = SGF.F.mapTypeIntoContext(SGF.getSILType(op.OuterResult)); + auto tupleType = SGF.F.mapTypeIntoContext( + SGF.getSILType(op.OuterResult, CanSILFunctionType())); SILValue optValue = SGF.B.createEnum(Loc, value, op.SomeDecl, tupleType); outerDirectResults.push_back(optValue); continue; @@ -2901,7 +2925,7 @@ buildThunkSignature(SILGenFunction &SGF, // If there's no opened existential, we just inherit the generic environment // from the parent function. if (openedExistential == nullptr) { - auto genericSig = SGF.F.getLoweredFunctionType()->getGenericSignature(); + auto genericSig = SGF.F.getLoweredFunctionType()->getSubstGenericSignature(); genericEnv = SGF.F.getGenericEnvironment(); interfaceSubs = SGF.F.getForwardingSubstitutionMap(); contextSubs = interfaceSubs; @@ -2910,9 +2934,9 @@ buildThunkSignature(SILGenFunction &SGF, // Add the existing generic signature. int depth = 0; - GenericSignature baseGenericSig = GenericSignature(); + GenericSignature baseGenericSig; if (inheritGenericSig) { - if (auto genericSig = SGF.F.getLoweredFunctionType()->getGenericSignature()) { + if (auto genericSig = SGF.F.getLoweredFunctionType()->getSubstGenericSignature()) { baseGenericSig = genericSig; depth = genericSig->getGenericParams().back()->getDepth() + 1; } @@ -2936,7 +2960,7 @@ buildThunkSignature(SILGenFunction &SGF, // Calculate substitutions to map the caller's archetypes to the thunk's // archetypes. if (auto calleeGenericSig = SGF.F.getLoweredFunctionType() - ->getGenericSignature()) { + ->getSubstGenericSignature()) { contextSubs = SubstitutionMap::get( calleeGenericSig, [&](SubstitutableType *type) -> Type { @@ -2955,7 +2979,7 @@ buildThunkSignature(SILGenFunction &SGF, }, MakeAbstractConformanceForGenericType()); - return genericSig->getCanonicalSignature(); + return genericSig.getCanonicalSignature(); } /// Build the type of a function transformation thunk. @@ -2968,8 +2992,10 @@ CanSILFunctionType SILGenFunction::buildThunkType( SubstitutionMap &interfaceSubs, CanType &dynamicSelfType, bool withoutActuallyEscaping) { - assert(!expectedType->isPolymorphic()); - assert(!sourceType->isPolymorphic()); + // We shouldn't be thunking generic types here, and substituted function types + // ought to have their substitutions applied before we get here. + assert(!expectedType->isPolymorphic() && !expectedType->getSubstitutions()); + assert(!sourceType->isPolymorphic() && !sourceType->getSubstitutions()); // Can't build a thunk without context, so we require ownership semantics // on the result type. @@ -3090,7 +3116,7 @@ CanSILFunctionType SILGenFunction::buildThunkType( SmallVector interfaceParams; interfaceParams.reserve(params.size()); for (auto ¶m : params) { - auto paramIfaceTy = param.getType()->mapTypeOutOfContext(); + auto paramIfaceTy = param.getInterfaceType()->mapTypeOutOfContext(); interfaceParams.push_back( SILParameterInfo(paramIfaceTy->getCanonicalType(genericSig), param.getConvention())); @@ -3098,24 +3124,24 @@ CanSILFunctionType SILGenFunction::buildThunkType( SmallVector interfaceYields; for (auto &yield : expectedType->getYields()) { - auto yieldIfaceTy = yield.getType()->mapTypeOutOfContext(); + auto yieldIfaceTy = yield.getInterfaceType()->mapTypeOutOfContext(); auto interfaceYield = - yield.getWithType(yieldIfaceTy->getCanonicalType(genericSig)); + yield.getWithInterfaceType(yieldIfaceTy->getCanonicalType(genericSig)); interfaceYields.push_back(interfaceYield); } SmallVector interfaceResults; for (auto &result : expectedType->getResults()) { - auto resultIfaceTy = result.getType()->mapTypeOutOfContext(); + auto resultIfaceTy = result.getInterfaceType()->mapTypeOutOfContext(); auto interfaceResult = - result.getWithType(resultIfaceTy->getCanonicalType(genericSig)); + result.getWithInterfaceType(resultIfaceTy->getCanonicalType(genericSig)); interfaceResults.push_back(interfaceResult); } Optional interfaceErrorResult; if (expectedType->hasErrorResult()) { auto errorResult = expectedType->getErrorResult(); - auto errorIfaceTy = errorResult.getType()->mapTypeOutOfContext(); + auto errorIfaceTy = errorResult.getInterfaceType()->mapTypeOutOfContext(); interfaceErrorResult = SILResultInfo( errorIfaceTy->getCanonicalType(genericSig), expectedType->getErrorResult().getConvention()); @@ -3127,6 +3153,8 @@ CanSILFunctionType SILGenFunction::buildThunkType( ParameterConvention::Direct_Unowned, interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, + expectedType->getSubstitutions(), + expectedType->isGenericSignatureImplied(), getASTContext()); } @@ -3161,8 +3189,22 @@ static ManagedValue createThunk(SILGenFunction &SGF, AbstractionPattern outputOrigType, CanAnyFunctionType outputSubstType, const TypeLowering &expectedTL) { - auto sourceType = fn.getType().castTo(); - auto expectedType = expectedTL.getLoweredType().castTo(); + auto substSourceType = fn.getType().castTo(); + auto substExpectedType = expectedTL.getLoweredType().castTo(); + + // Apply substitutions in the source and destination types, since the thunk + // doesn't change because of different function representations. + CanSILFunctionType sourceType; + if (substSourceType->getSubstitutions()) { + sourceType = substSourceType->getUnsubstitutedType(SGF.SGM.M); + fn = SGF.B.createConvertFunction(loc, fn, + SILType::getPrimitiveObjectType(sourceType)); + } else { + sourceType = substSourceType; + } + + auto expectedType = substExpectedType + ->getUnsubstitutedType(SGF.SGM.M); // We can't do bridging here. assert(expectedType->getLanguage() == @@ -3205,14 +3247,22 @@ static ManagedValue createThunk(SILGenFunction &SGF, createPartialApplyOfThunk(SGF, loc, thunk, interfaceSubs, dynamicSelfType, toType, fn.ensurePlusOne(SGF, loc)); - if (!expectedType->isNoEscape()) { + // Convert to the substituted result type. + if (expectedType != substExpectedType) { + auto substEscapingExpectedType = substExpectedType + ->getWithExtInfo(substExpectedType->getExtInfo().withNoEscape(false)); + thunkedFn = SGF.B.createConvertFunction(loc, thunkedFn, + SILType::getPrimitiveObjectType(substEscapingExpectedType)); + } + + if (!substExpectedType->isNoEscape()) { return thunkedFn; } // Handle the escaping to noescape conversion. - assert(expectedType->isNoEscape()); + assert(substExpectedType->isNoEscape()); return SGF.B.createConvertEscapeToNoEscape( - loc, thunkedFn, SILType::getPrimitiveObjectType(expectedType)); + loc, thunkedFn, SILType::getPrimitiveObjectType(substExpectedType)); } static CanSILFunctionType buildWithoutActuallyEscapingThunkType( @@ -3346,7 +3396,7 @@ ManagedValue Transform::transformFunction(ManagedValue fn, } // Check if we require a re-abstraction thunk. - if (SGF.SGM.Types.checkForABIDifferences( + if (SGF.SGM.Types.checkForABIDifferences(SGF.SGM.M, SILType::getPrimitiveObjectType(fnType), SILType::getPrimitiveObjectType(expectedFnType)) == TypeConverter::ABIDifference::NeedsThunk) { @@ -3367,7 +3417,7 @@ ManagedValue Transform::transformFunction(ManagedValue fn, : expectedFnType->isNoEscape()); auto newFnType = adjustFunctionType(expectedFnType, newEI, fnType->getCalleeConvention(), - fnType->getWitnessMethodConformanceOrNone()); + fnType->getWitnessMethodConformanceOrInvalid()); // Apply any ABI-compatible conversions before doing thin-to-thick or // escaping->noescape conversion. @@ -3545,16 +3595,19 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, CanSILFunctionType derivedFTy; if (baseLessVisibleThanDerived) { - derivedFTy = SGM.Types.getConstantOverrideType(derived); + derivedFTy = + SGM.Types.getConstantOverrideType(getTypeExpansionContext(), derived); } else { - derivedFTy = SGM.Types.getConstantInfo(derived).SILFnType; + derivedFTy = + SGM.Types.getConstantInfo(getTypeExpansionContext(), derived).SILFnType; } - SubstitutionMap subs; - if (auto *genericEnv = fd->getGenericEnvironment()) { - F.setGenericEnvironment(genericEnv); - subs = getForwardingSubstitutionMap(); - derivedFTy = derivedFTy->substGenericArgs(SGM.M, subs); + auto subs = getForwardingSubstitutionMap(); + if (auto genericSig = derivedFTy->getSubstGenericSignature()) { + subs = SubstitutionMap::get(genericSig, subs); + + derivedFTy = + derivedFTy->substGenericArgs(SGM.M, subs, getTypeExpansionContext()); inputSubstType = cast( cast(inputSubstType) @@ -3604,7 +3657,8 @@ SILGenFunction::emitVTableThunk(SILDeclRef base, if (baseLessVisibleThanDerived) { // See the comment in SILVTableVisitor.h under maybeAddMethod(). auto selfValue = thunkArgs.back().getValue(); - auto derivedTy = SGM.Types.getConstantOverrideType(derived); + auto derivedTy = + SGM.Types.getConstantOverrideType(getTypeExpansionContext(), derived); derivedRef = emitClassMethodRef(loc, selfValue, derived, derivedTy); } else { derivedRef = B.createFunctionRefFor(loc, implFn); @@ -3726,16 +3780,15 @@ static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness, } static CanSILFunctionType -getWitnessFunctionType(SILGenModule &SGM, - SILDeclRef witness, - WitnessDispatchKind witnessKind) { +getWitnessFunctionType(TypeExpansionContext context, SILGenModule &SGM, + SILDeclRef witness, WitnessDispatchKind witnessKind) { switch (witnessKind) { case WitnessDispatchKind::Static: case WitnessDispatchKind::Dynamic: case WitnessDispatchKind::Witness: - return SGM.Types.getConstantInfo(witness).SILFnType; + return SGM.Types.getConstantInfo(context, witness).SILFnType; case WitnessDispatchKind::Class: - return SGM.Types.getConstantOverrideType(witness); + return SGM.Types.getConstantOverrideType(context, witness); } llvm_unreachable("Unhandled WitnessDispatchKind in switch."); @@ -3746,7 +3799,7 @@ getSelfTypeAndConformanceForWitness(SILDeclRef witness, SubstitutionMap subs) { auto protocol = cast(witness.getDecl()->getDeclContext()); auto selfParam = protocol->getProtocolSelfType()->getCanonicalType(); auto type = subs.getReplacementTypes()[0]; - auto conf = *subs.lookupConformance(selfParam, protocol); + auto conf = subs.lookupConformance(selfParam, protocol); return {type->getCanonicalType(), conf}; } @@ -3785,7 +3838,7 @@ emitOpenExistentialInSelfConformance(SILGenFunction &SGF, SILLocation loc, SILDeclRef witness, SubstitutionMap subs, ManagedValue value, SILParameterInfo destParameter) { - auto openedTy = destParameter.getSILStorageType(); + auto openedTy = destParameter.getSILStorageInterfaceType(); return SGF.emitOpenExistential(loc, value, openedTy, destParameter.isIndirectMutating() ? AccessKind::ReadWrite @@ -3820,7 +3873,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, collectThunkParams(loc, origParams); // Get the type of the witness. - auto witnessInfo = getConstantInfo(witness); + auto witnessInfo = getConstantInfo(getTypeExpansionContext(), witness); CanAnyFunctionType witnessSubstTy = witnessInfo.LoweredType; if (auto genericFnType = dyn_cast(witnessSubstTy)) { witnessSubstTy = cast(genericFnType @@ -3839,10 +3892,12 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy, } // Get the lowered type of the witness. - auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind); + auto origWitnessFTy = getWitnessFunctionType(getTypeExpansionContext(), SGM, + witness, witnessKind); auto witnessFTy = origWitnessFTy; if (!witnessSubs.empty()) - witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs); + witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs, + getTypeExpansionContext()); auto reqtSubstParams = reqtSubstTy.getParams(); auto witnessSubstParams = witnessSubstTy.getParams(); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index ee839f35c0572..e4d94aae962c4 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -85,20 +85,27 @@ class EmitBBArguments : public CanTypeVisitor ¶meters; EmitBBArguments(SILGenFunction &sgf, SILBasicBlock *parent, SILLocation l, + CanSILFunctionType fnTy, ArrayRef ¶meters) - : SGF(sgf), parent(parent), loc(l), parameters(parameters) {} + : SGF(sgf), parent(parent), loc(l), fnTy(fnTy), parameters(parameters) {} ManagedValue visitType(CanType t) { return visitType(t, /*isInOut=*/false); } ManagedValue visitType(CanType t, bool isInOut) { - // The calling convention always uses minimal resilience expansion. - auto argType = - SGF.SGM.Types.getLoweredType(t, ResilienceExpansion::Minimal); + // The calling convention always uses minimal resilience expansion but + // inside the function we lower/expand types in context of the current + // function. + auto argType = SGF.SGM.Types.getLoweredType(t, SGF.getTypeExpansionContext()); + auto argTypeConv = + SGF.SGM.Types.getLoweredType(t, TypeExpansionContext::minimal()); + argType = argType.getCategoryType(argTypeConv.getCategory()); + if (isInOut) argType = SILType::getPrimitiveAddressType(argType.getASTType()); @@ -106,7 +113,8 @@ class EmitBBArguments : public CanTypeVisitor()); @@ -153,7 +161,7 @@ class EmitBBArguments : public CanTypeVisitor elements; - auto &tl = SGF.SGM.Types.getTypeLowering(t, ResilienceExpansion::Minimal); + auto &tl = SGF.SGM.Types.getTypeLowering(t, SGF.getTypeExpansionContext()); bool canBeGuaranteed = tl.isLoadable(); // Collect the exploded elements. @@ -223,9 +231,10 @@ struct ArgumentInitHelper { uint16_t ArgNo = 0; ArgumentInitHelper(SILGenFunction &SGF, SILFunction &f) - : SGF(SGF), f(f), initB(SGF.B), - parameters(f.getLoweredFunctionType()->getParameters()) { - } + : SGF(SGF), f(f), initB(SGF.B), + parameters( + f.getLoweredFunctionTypeInContext(SGF.B.getTypeExpansionContext()) + ->getParameters()) {} unsigned getNumArgs() const { return ArgNo; } @@ -235,7 +244,8 @@ struct ArgumentInitHelper { // Create an RValue by emitting destructured arguments into a basic block. CanType canTy = ty->getCanonicalType(); - EmitBBArguments argEmitter(SGF, parent, l, parameters); + EmitBBArguments argEmitter(SGF, parent, l, + f.getLoweredFunctionType(), parameters); // Note: inouts of tuples are not exploded, so we bypass visit(). if (isInOut) @@ -316,8 +326,7 @@ static void makeArgument(Type ty, ParamDecl *decl, for (auto fieldType : tupleTy->getElementTypes()) makeArgument(fieldType, decl, args, SGF); } else { - auto loweredTy = SGF.SGM.Types.getLoweredType(ty, - ResilienceExpansion::Minimal); + auto loweredTy = SGF.getLoweredTypeForFunctionArgument(ty); if (decl->isInOut()) loweredTy = SILType::getPrimitiveAddressType(loweredTy.getASTType()); auto arg = SGF.F.begin()->createFunctionArgument(loweredTy, decl); @@ -354,7 +363,7 @@ static void emitCaptureArguments(SILGenFunction &SGF, return SGF.F.mapTypeIntoContext(interfaceType); }; - auto expansion = SGF.F.getResilienceExpansion(); + auto expansion = SGF.getTypeExpansionContext(); switch (SGF.SGM.Types.getDeclCaptureKind(capture, expansion)) { case CaptureKind::Constant: { auto type = getVarTypeInCaptureContext(); @@ -396,9 +405,13 @@ static void emitCaptureArguments(SILGenFunction &SGF, // LValues are captured as a retained @box that owns // the captured value. auto type = getVarTypeInCaptureContext(); - auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(VD, - SGF.SGM.Types.getLoweredRValueType(type), - SGF.F.getGenericEnvironment(), /*mutable*/ true); + // Get the content for the box in the minimal resilience domain because we + // are declaring a type. + auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture( + VD, + SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + type), + SGF.F.getGenericEnvironment(), /*mutable*/ true); SILValue box = SGF.F.begin()->createFunctionArgument( SILType::getPrimitiveObjectType(boxTy), VD); SILValue addr = SGF.B.createProjectBox(VD, box, 0); @@ -420,7 +433,7 @@ static void emitCaptureArguments(SILGenFunction &SGF, } } -void SILGenFunction::emitProlog(const CaptureInfo &captureInfo, +void SILGenFunction::emitProlog(CaptureInfo captureInfo, ParameterList *paramList, ParamDecl *selfParam, DeclContext *DC, @@ -430,21 +443,10 @@ void SILGenFunction::emitProlog(const CaptureInfo &captureInfo, uint16_t ArgNo = emitProlog(paramList, selfParam, resultType, DC, throws, throwsLoc); - // Emit an unreachable instruction if a parameter type is - // uninhabited - if (paramList) { - for (auto *param : *paramList) { - if (param->getType()->isStructurallyUninhabited()) { - SILLocation unreachableLoc(param); - unreachableLoc.markAsPrologue(); - B.createUnreachable(unreachableLoc); - break; - } - } - } - // Emit the capture argument variables. These are placed last because they // become the first curry level of the SIL function. + assert(captureInfo.hasBeenComputed() && + "can't emit prolog of function with uncomputed captures"); for (auto capture : captureInfo.getCaptures()) { if (capture.isDynamicSelfMetadata()) { auto selfMetatype = MetatypeType::get( @@ -475,6 +477,19 @@ void SILGenFunction::emitProlog(const CaptureInfo &captureInfo, emitCaptureArguments(*this, DC->getGenericSignatureOfContext(), capture, ++ArgNo); } + + // Emit an unreachable instruction if a parameter type is + // uninhabited + if (paramList) { + for (auto *param : *paramList) { + if (param->getType()->isStructurallyUninhabited()) { + SILLocation unreachableLoc(param); + unreachableLoc.markAsPrologue(); + B.createUnreachable(unreachableLoc); + break; + } + } + } } static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType, @@ -492,9 +507,12 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType, // The calling convention always uses minimal resilience expansion. auto &resultTI = SGF.SGM.Types.getTypeLowering(DC->mapTypeIntoContext(resultType), - ResilienceExpansion::Minimal); + SGF.getTypeExpansionContext()); + auto &resultTIConv = SGF.SGM.Types.getTypeLowering( + DC->mapTypeIntoContext(resultType), TypeExpansionContext::minimal()); + if (!SILModuleConventions::isReturnedIndirectlyInSIL( - resultTI.getLoweredType(), SGF.SGM.M)) { + resultTIConv.getLoweredType(), SGF.SGM.M)) { return; } auto &ctx = SGF.getASTContext(); @@ -504,9 +522,8 @@ static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType, DC); var->setSpecifier(ParamSpecifier::InOut); var->setInterfaceType(resultType); - - auto *arg = - SGF.F.begin()->createFunctionArgument(resultTI.getLoweredType(), var); + auto *arg = SGF.F.begin()->createFunctionArgument( + resultTI.getLoweredType().getAddressType(), var); (void)arg; } diff --git a/lib/SILGen/SILGenRequests.cpp b/lib/SILGen/SILGenRequests.cpp new file mode 100644 index 0000000000000..19f9830889162 --- /dev/null +++ b/lib/SILGen/SILGenRequests.cpp @@ -0,0 +1,72 @@ +//===--- SILGenRequests.cpp - Requests for SIL Generation ----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/SILGenRequests.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Module.h" +#include "swift/AST/FileUnit.h" +#include "swift/AST/SourceFile.h" +#include "swift/SIL/SILModule.h" +#include "swift/Subsystems.h" + +using namespace swift; + +namespace swift { +// Implement the SILGen type zone (zone 12). +#define SWIFT_TYPEID_ZONE SILGen +#define SWIFT_TYPEID_HEADER "swift/AST/SILGenTypeIDZone.def" +#include "swift/Basic/ImplementTypeIDZone.h" +#undef SWIFT_TYPEID_ZONE +#undef SWIFT_TYPEID_HEADER +} // end namespace swift + +void swift::simple_display(llvm::raw_ostream &out, + const SILGenDescriptor &desc) { + auto *MD = desc.context.dyn_cast(); + auto *unit = desc.context.dyn_cast(); + if (MD) { + out << "SIL Generation for module " << MD->getName(); + } else { + assert(unit); + out << "SIL Generation for file "; + switch (unit->getKind()) { + case FileUnitKind::Source: + out << '\"' << cast(unit)->getFilename() << '\"'; + break; + case FileUnitKind::Builtin: + out << "(Builtin)"; + break; + case FileUnitKind::DWARFModule: + case FileUnitKind::ClangModule: + case FileUnitKind::SerializedAST: + out << '\"' << cast(unit)->getFilename() << '\"'; + break; + } + } +} + +SourceLoc swift::extractNearestSourceLoc(const SILGenDescriptor &desc) { + return SourceLoc(); +} + +// Define request evaluation functions for each of the SILGen requests. +static AbstractRequestFunction *silGenRequestFunctions[] = { +#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \ + reinterpret_cast(&Name::evaluateRequest), +#include "swift/AST/SILGenTypeIDZone.def" +#undef SWIFT_REQUEST +}; + +void swift::registerSILGenRequestFunctions(Evaluator &evaluator) { + evaluator.registerRequestFunctions(Zone::SILGen, + silGenRequestFunctions); +} diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 0e82053f29602..8cb86b426b4cb 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -370,7 +370,9 @@ namespace { } // end anonymous namespace static InitializationPtr -prepareIndirectResultInit(SILGenFunction &SGF, CanType resultType, +prepareIndirectResultInit(SILGenFunction &SGF, + CanSILFunctionType fnTypeForResults, + CanType resultType, ArrayRef &allResults, MutableArrayRef &directResults, ArrayRef &indirectResultAddrs, @@ -381,7 +383,8 @@ prepareIndirectResultInit(SILGenFunction &SGF, CanType resultType, tupleInit->SubInitializations.reserve(resultTupleType->getNumElements()); for (auto resultEltType : resultTupleType.getElementTypes()) { - auto eltInit = prepareIndirectResultInit(SGF, resultEltType, allResults, + auto eltInit = prepareIndirectResultInit(SGF, fnTypeForResults, + resultEltType, allResults, directResults, indirectResultAddrs, cleanups); tupleInit->SubInitializations.push_back(std::move(eltInit)); @@ -439,7 +442,9 @@ SILGenFunction::prepareIndirectResultInit(CanType formalResultType, MutableArrayRef directResults = directResultsBuffer; ArrayRef indirectResultAddrs = F.getIndirectResults(); - auto init = ::prepareIndirectResultInit(*this, formalResultType, allResults, + auto init = ::prepareIndirectResultInit(*this, + fnConv.funcTy, + formalResultType, allResults, directResults, indirectResultAddrs, cleanups); @@ -477,6 +482,7 @@ void SILGenFunction::emitReturnExpr(SILLocation branchLoc, RValue RV = emitRValue(ret).ensurePlusOne(*this, CleanupLocation(ret)); std::move(RV).forwardAll(*this, directResults); } + Cleanups.emitBranchAndCleanups(ReturnDest, branchLoc, directResults); } @@ -897,14 +903,28 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { } void StmtEmitter::visitForEachStmt(ForEachStmt *S) { + // Dig out information about the sequence conformance. + auto sequenceConformance = S->getSequenceConformance(); + Type sequenceType = S->getSequence()->getType(); + auto sequenceProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); + auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( + sequenceProto, sequenceType, sequenceConformance); + // Emit the 'iterator' variable that we'll be using for iteration. LexicalScope OuterForScope(SGF, CleanupLocation(S)); { auto initialization = SGF.emitInitializationForVarDecl(S->getIteratorVar(), false); SILLocation loc = SILLocation(S->getSequence()); + + // Compute the reference to the Sequence's makeIterator(). + FuncDecl *makeIteratorReq = SGF.getASTContext().getSequenceMakeIterator(); + ConcreteDeclRef makeIteratorRef(makeIteratorReq, sequenceSubs); + + // Call makeIterator(). RValue result = SGF.emitApplyMethod( - loc, S->getMakeIterator(), ArgumentSource(S->getSequence()), + loc, makeIteratorRef, ArgumentSource(S->getSequence()), PreparedArguments(ArrayRef({})), SGFContext(initialization.get())); if (!result.isInContext()) { @@ -926,7 +946,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { if (S->getConvertElementExpr()) { optTy = S->getConvertElementExpr()->getType()->getCanonicalType(); } else { - optTy = OptionalType::get(S->getSequenceConformance()->getTypeWitnessByName( + optTy = OptionalType::get(S->getSequenceConformance().getTypeWitnessByName( S->getSequence()->getType(), SGF.getASTContext().Id_Element)) ->getCanonicalType(); @@ -946,8 +966,26 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { JumpDest endDest = createJumpDest(S->getBody()); SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); + // Compute the reference to the the iterator's next(). + auto iteratorProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::IteratorProtocol); + ValueDecl *iteratorNextReq = iteratorProto->getSingleRequirement( + DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, + ArrayRef())); + auto iteratorAssocType = + sequenceProto->getAssociatedType(SGF.getASTContext().Id_Iterator); + auto iteratorMemberRef = DependentMemberType::get( + sequenceProto->getSelfInterfaceType(), iteratorAssocType); + auto iteratorType = sequenceConformance.getAssociatedType( + sequenceType, iteratorMemberRef); + auto iteratorConformance = sequenceConformance.getAssociatedConformance( + sequenceType, iteratorMemberRef, iteratorProto); + auto iteratorSubs = SubstitutionMap::getProtocolSubstitutions( + iteratorProto, iteratorType, iteratorConformance); + ConcreteDeclRef iteratorNextRef(iteratorNextReq, iteratorSubs); + auto buildArgumentSource = [&]() { - if (cast(S->getIteratorNext().getDecl())->getSelfAccessKind() == + if (cast(iteratorNextRef.getDecl())->getSelfAccessKind() == SelfAccessKind::Mutating) { LValue lv = SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::ReadWrite); @@ -963,7 +1001,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) { auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) { RValue result; result = SGF.emitApplyMethod( - loc, S->getIteratorNext(), buildArgumentSource(), + loc, iteratorNextRef, buildArgumentSource(), PreparedArguments(ArrayRef({})), S->getElementExpr() ? SGFContext() : ctx); if (S->getElementExpr()) { @@ -1143,6 +1181,7 @@ void StmtEmitter::visitFailStmt(FailStmt *S) { /// try_apply instruction. The block is implicitly emitted and filled in. SILBasicBlock * SILGenFunction::getTryApplyErrorDest(SILLocation loc, + CanSILFunctionType fnTy, SILResultInfo exnResult, bool suppressErrorPath) { assert(exnResult.getConvention() == ResultConvention::Owned); @@ -1150,7 +1189,7 @@ SILGenFunction::getTryApplyErrorDest(SILLocation loc, // For now, don't try to re-use destination blocks for multiple // failure sites. SILBasicBlock *destBB = createBasicBlock(FunctionSection::Postmatter); - SILValue exn = destBB->createPhiArgument(getSILType(exnResult), + SILValue exn = destBB->createPhiArgument(getSILType(exnResult, fnTy), ValueOwnershipKind::Owned); assert(B.hasValidInsertionPoint() && B.insertingAtEndOfBlock()); diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index 3b75c95f2df5c..ca420cb1a55aa 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -97,7 +97,8 @@ getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, SILDeclRef next = SILDeclRef(vd, thunk.kind); assert(!next.isCurried); - auto constantInfo = SGF.SGM.Types.getConstantInfo(next); + auto constantInfo = + SGF.SGM.Types.getConstantInfo(SGF.getTypeExpansionContext(), next); // If the function is natively foreign, reference its foreign entry point. if (requiresForeignToNativeThunk(vd)) @@ -118,7 +119,8 @@ getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, next}; } - auto methodTy = SGF.SGM.Types.getConstantOverrideType(next); + auto methodTy = SGF.SGM.Types.getConstantOverrideType( + SGF.getTypeExpansionContext(), next); SILValue result = SGF.emitClassMethodRef(loc, selfArg.getValue(), next, methodTy); return {ManagedValue::forUnmanaged(result), @@ -133,7 +135,7 @@ getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk, auto origSelfType = protocol->getSelfInterfaceType()->getCanonicalType(); auto substSelfType = origSelfType.subst(curriedSubs)->getCanonicalType(); auto conformance = curriedSubs.lookupConformance(origSelfType, protocol); - auto result = SGF.B.createWitnessMethod(loc, substSelfType, *conformance, + auto result = SGF.B.createWitnessMethod(loc, substSelfType, conformance, next, constantInfo.getSILType()); return {ManagedValue::forUnmanaged(result), next}; } @@ -158,7 +160,7 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) { SILLocation loc(vd); Scope S(*this, vd); - auto thunkInfo = SGM.Types.getConstantInfo(thunk); + auto thunkInfo = SGM.Types.getConstantInfo(getTypeExpansionContext(), thunk); auto thunkFnTy = thunkInfo.SILFnType; SILFunctionConventions fromConv(thunkFnTy, SGM.M); @@ -185,15 +187,16 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) { if (resultTy != toClosure.getType()) { CanSILFunctionType resultFnTy = resultTy.castTo(); CanSILFunctionType closureFnTy = toClosure.getType().castTo(); - if (resultFnTy->isABICompatibleWith(closureFnTy).isCompatible()) { + if (resultFnTy->isABICompatibleWith(closureFnTy, F).isCompatible()) { toClosure = B.createConvertFunction(loc, toClosure, resultTy); } else { // Compute the partially-applied abstraction pattern for the callee: // just grab the pattern for the curried fn ref and "call" it. assert(!calleeRef.isCurried); calleeRef.isCurried = true; - auto appliedFnPattern = SGM.Types.getConstantInfo(calleeRef).FormalPattern - .getFunctionResultType(); + auto appliedFnPattern = + SGM.Types.getConstantInfo(getTypeExpansionContext(), calleeRef) + .FormalPattern.getFunctionResultType(); auto appliedThunkPattern = thunkInfo.FormalPattern.getFunctionResultType(); @@ -265,7 +268,7 @@ SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, SILConstantInfo constantInfo, bool callPreviousDynamicReplaceableImpl) { - assert(constantInfo == getConstantInfo(constant)); + assert(constantInfo == getConstantInfo(getTypeExpansionContext(), constant)); // Builtins must be fully applied at the point of reference. if (constant.hasDecl() && @@ -289,7 +292,8 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, } auto f = SGM.getFunction(constant, NotForDefinition); - assert(f->getLoweredFunctionType() == constantInfo.SILFnType); + assert(f->getLoweredFunctionTypeInContext(B.getTypeExpansionContext()) == + constantInfo.SILFnType); if (callPreviousDynamicReplaceableImpl) return B.createPreviousDynamicFunctionRef(loc, f); else diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 805b6c945d74a..0486b0080fc09 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -87,7 +87,9 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, derived.kind != SILDeclRef::Kind::Allocator); if (usesObjCDynamicDispatch) { - implFn = getDynamicThunk(derived, Types.getConstantInfo(derived).SILFnType); + implFn = getDynamicThunk( + derived, Types.getConstantInfo(TypeExpansionContext::minimal(), derived) + .SILFnType); } else { implFn = getFunction(derived, NotForDefinition); } @@ -105,11 +107,13 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, // Determine the derived thunk type by lowering the derived type against the // abstraction pattern of the base. - auto baseInfo = Types.getConstantInfo(base); - auto derivedInfo = Types.getConstantInfo(derived); + auto baseInfo = Types.getConstantInfo(TypeExpansionContext::minimal(), base); + auto derivedInfo = + Types.getConstantInfo(TypeExpansionContext::minimal(), derived); auto basePattern = AbstractionPattern(baseInfo.LoweredType); - - auto overrideInfo = M.Types.getConstantOverrideInfo(derived, base); + + auto overrideInfo = M.Types.getConstantOverrideInfo( + TypeExpansionContext::minimal(), derived, base); // If base method's generic requirements are not satisfied by the derived // method then we need a thunk. @@ -121,10 +125,24 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, // The override member type is semantically a subtype of the base // member type. If the override is ABI compatible, we do not need // a thunk. - if (doesNotHaveGenericRequirementDifference && !baseLessVisibleThanDerived && - M.Types.checkFunctionForABIDifferences(derivedInfo.SILFnType, - overrideInfo.SILFnType) == - TypeConverter::ABIDifference::Trivial) + bool compatibleCallingConvention; + switch (M.Types.checkFunctionForABIDifferences(M, + derivedInfo.SILFnType, + overrideInfo.SILFnType)) { + case TypeConverter::ABIDifference::CompatibleCallingConvention: + case TypeConverter::ABIDifference::CompatibleRepresentation: + compatibleCallingConvention = true; + break; + case TypeConverter::ABIDifference::NeedsThunk: + compatibleCallingConvention = false; + break; + case TypeConverter::ABIDifference::CompatibleCallingConvention_ThinToThick: + case TypeConverter::ABIDifference::CompatibleRepresentation_ThinToThick: + llvm_unreachable("shouldn't be thick methods"); + } + if (doesNotHaveGenericRequirementDifference + && !baseLessVisibleThanDerived + && compatibleCallingConvention) return SILVTable::Entry(base, implFn, implKind); // Generate the thunk name. @@ -147,12 +165,16 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, if (auto existingThunk = M.lookUpFunction(name)) return SILVTable::Entry(base, existingThunk, implKind); + GenericEnvironment *genericEnv = nullptr; + if (auto genericSig = overrideInfo.FormalType.getOptGenericSignature()) + genericEnv = genericSig->getGenericEnvironment(); + // Emit the thunk. SILLocation loc(derivedDecl); SILGenFunctionBuilder builder(*this); auto thunk = builder.createFunction( SILLinkage::Private, name, overrideInfo.SILFnType, - cast(derivedDecl)->getGenericEnvironment(), loc, + genericEnv, loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, ProfileCounter(), IsThunk); thunk->setDebugScope(new (M) SILDebugScope(loc, thunk)); @@ -561,7 +583,7 @@ class SILGenConformance : public SILGenWitnessTable { "unable to find conformance that should be known"); ConditionalConformances.push_back( - SILWitnessTable::ConditionalConformance{type, *conformance}); + SILWitnessTable::ConditionalConformance{type, conformance}); return /*finished?*/ false; }); @@ -587,7 +609,8 @@ SILFunction *SILGenModule::emitProtocolWitness( ProtocolConformanceRef conformance, SILLinkage linkage, IsSerialized_t isSerialized, SILDeclRef requirement, SILDeclRef witnessRef, IsFreeFunctionWitness_t isFree, Witness witness) { - auto requirementInfo = Types.getConstantInfo(requirement); + auto requirementInfo = + Types.getConstantInfo(TypeExpansionContext::minimal(), requirement); // Work out the lowered function type of the SIL witness thunk. auto reqtOrigTy = cast(requirementInfo.LoweredType); @@ -600,7 +623,7 @@ SILFunction *SILGenModule::emitProtocolWitness( auto *genericEnv = witness.getSyntheticEnvironment(); CanGenericSignature genericSig; if (genericEnv) - genericSig = genericEnv->getGenericSignature()->getCanonicalSignature(); + genericSig = genericEnv->getGenericSignature().getCanonicalSignature(); // The type of the witness thunk. auto reqtSubstTy = cast( @@ -626,7 +649,7 @@ SILFunction *SILGenModule::emitProtocolWitness( auto requirement = conformance.getRequirement(); auto self = requirement->getSelfInterfaceType()->getCanonicalType(); - conformance = *reqtSubMap.lookupConformance(self, requirement); + conformance = reqtSubMap.lookupConformance(self, requirement); } reqtSubstTy = @@ -651,8 +674,9 @@ SILFunction *SILGenModule::emitProtocolWitness( // Lower the witness thunk type with the requirement's abstraction level. auto witnessSILFnType = getNativeSILFunctionType( - M.Types, AbstractionPattern(reqtOrigTy), reqtSubstTy, - requirement, witnessRef, witnessSubsForTypeLowering, conformance); + M.Types, TypeExpansionContext::minimal(), AbstractionPattern(reqtOrigTy), + reqtSubstTy, requirement, witnessRef, witnessSubsForTypeLowering, + conformance); // Mangle the name of the witness thunk. Mangle::ASTMangler NewMangler; @@ -704,7 +728,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, SelfProtocolConformance *conformance, SILLinkage linkage, SILDeclRef requirement) { - auto requirementInfo = SGM.Types.getConstantInfo(requirement); + auto requirementInfo = + SGM.Types.getConstantInfo(TypeExpansionContext::minimal(), requirement); // Work out the lowered function type of the SIL witness thunk. auto reqtOrigTy = cast(requirementInfo.LoweredType); @@ -733,8 +758,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, cast(reqtOrigTy.subst(reqtSubs)->getCanonicalType()); // Substitute into the requirement type to get the type of the thunk. - auto witnessSILFnType = - requirementInfo.SILFnType->substGenericArgs(SGM.M, reqtSubs); + auto witnessSILFnType = requirementInfo.SILFnType->substGenericArgs( + SGM.M, reqtSubs, TypeExpansionContext::minimal()); // Mangle the name of the witness thunk. std::string name = [&] { @@ -900,14 +925,11 @@ class SILGenDefaultWitnessTable Proto->getDefaultAssociatedConformanceWitness( req.getAssociation(), req.getAssociatedRequirement()); - if (!witness) + if (witness.isInvalid()) return addMissingDefault(); - auto entry = - SILWitnessTable::AssociatedTypeProtocolWitness{ - req.getAssociation(), - req.getAssociatedRequirement(), - *witness}; + auto entry = SILWitnessTable::AssociatedTypeProtocolWitness{ + req.getAssociation(), req.getAssociatedRequirement(), witness}; DefaultWitnesses.push_back(entry); } }; @@ -1019,7 +1041,7 @@ class SILGenType : public TypeMemberVisitor { void visitPatternBindingDecl(PatternBindingDecl *pd) { // Emit initializers. - for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) { + for (auto i : range(pd->getNumPatternEntries())) { if (pd->getExecutableInit(i)) { if (pd->isStatic()) SGM.emitGlobalInitialization(pd, i); @@ -1154,7 +1176,7 @@ class SILGenExtension : public TypeMemberVisitor { void visitPatternBindingDecl(PatternBindingDecl *pd) { // Emit initializers for static variables. - for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) { + for (auto i : range(pd->getNumPatternEntries())) { if (pd->getExecutableInit(i)) { assert(pd->isStatic() && "stored property in extension?!"); SGM.emitGlobalInitialization(pd, i); diff --git a/lib/SILGen/Scope.cpp b/lib/SILGen/Scope.cpp index 68d04f0d42e75..75ac5e068ff6f 100644 --- a/lib/SILGen/Scope.cpp +++ b/lib/SILGen/Scope.cpp @@ -22,7 +22,7 @@ ManagedValue Scope::popPreservingValue(ManagedValue mv) { // stack location that will be destroyed by this scope. assert(mv && mv.getType().isObject() && (mv.getType().isTrivial(cleanups.SGF.F) || - mv.getOwnershipKind() == ValueOwnershipKind::Any || + mv.getOwnershipKind() == ValueOwnershipKind::None || mv.hasCleanup())); CleanupCloner cloner(cleanups.SGF, mv); SILValue value = mv.forward(cleanups.SGF); diff --git a/lib/SILGen/SwitchEnumBuilder.cpp b/lib/SILGen/SwitchEnumBuilder.cpp index c230ec981a713..4704d95fae200 100644 --- a/lib/SILGen/SwitchEnumBuilder.cpp +++ b/lib/SILGen/SwitchEnumBuilder.cpp @@ -144,8 +144,8 @@ void SwitchEnumBuilder::emit() && { ManagedValue input; if (decl->hasAssociatedValues()) { // Pull the payload out if we have one. - SILType inputType = - optional.getType().getEnumElementType(decl, builder.getModule()); + SILType inputType = optional.getType().getEnumElementType( + decl, builder.getModule(), builder.getFunction()); input = optional; if (!isAddressOnly) { input = builder.createOwnedPhiArgument(inputType); diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index 294810014f05e..f56533063c003 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -843,7 +843,7 @@ void ConsumedArgToEpilogueReleaseMatcher::collectMatchingDestroyAddresses( SILFunction::iterator anotherEpilogueBB = (Kind == ExitKind::Return) ? F->findThrowBB() : F->findReturnBB(); - for (auto *arg : F->begin()->getFunctionArguments()) { + for (auto *arg : F->begin()->getSILFunctionArguments()) { if (arg->isIndirectResult()) continue; if (arg->getArgumentConvention() != SILArgumentConvention::Indirect_In) diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index 928106140beb0..fbd23936c2238 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -380,13 +380,13 @@ void AccessSummaryAnalysis::recompute(FunctionInfo *initial) { } while (needAnotherIteration); } -std::string -AccessSummaryAnalysis::SubAccessSummary::getDescription(SILType BaseType, - SILModule &M) const { +std::string AccessSummaryAnalysis::SubAccessSummary::getDescription( + SILType BaseType, SILModule &M, TypeExpansionContext context) const { std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M); + os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M, + context); if (!SubPath->isRoot()) os << " "; @@ -409,9 +409,8 @@ void AccessSummaryAnalysis::ArgumentSummary::getSortedSubAccesses( assert(storage.size() == SubAccesses.size()); } -std::string -AccessSummaryAnalysis::ArgumentSummary::getDescription(SILType BaseType, - SILModule &M) const { +std::string AccessSummaryAnalysis::ArgumentSummary::getDescription( + SILType BaseType, SILModule &M, TypeExpansionContext context) const { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "["; @@ -425,7 +424,7 @@ AccessSummaryAnalysis::ArgumentSummary::getDescription(SILType BaseType, if (index > 0) { os << ", "; } - os << subAccess.getDescription(BaseType, M); + os << subAccess.getDescription(BaseType, M, context); ++index; } os << "]"; @@ -551,7 +550,8 @@ AccessSummaryAnalysis::findSubPathAccessed(BeginAccessInst *BAI) { /// that stored-property relaxation supports: struct stored properties /// and tuple elements. std::string AccessSummaryAnalysis::getSubPathDescription( - SILType baseType, const IndexTrieNode *subPath, SILModule &M) { + SILType baseType, const IndexTrieNode *subPath, SILModule &M, + TypeExpansionContext context) { // Walk the trie to the root to collect the sequence (in reverse order). llvm::SmallVector reversedIndices; const IndexTrieNode *I = subPath; @@ -570,7 +570,7 @@ std::string AccessSummaryAnalysis::getSubPathDescription( if (StructDecl *D = containingType.getStructOrBoundGenericStruct()) { VarDecl *var = D->getStoredProperties()[index]; os << var->getBaseName(); - containingType = containingType.getFieldType(var, M); + containingType = containingType.getFieldType(var, M, context); continue; } @@ -635,7 +635,8 @@ void AccessSummaryAnalysis::FunctionSummary::print(raw_ostream &os, } SILArgument *arg = fn->getArgument(i); SILModule &m = fn->getModule(); - os << getAccessForArgument(i).getDescription(arg->getType(), m); + os << getAccessForArgument(i).getDescription(arg->getType(), m, + TypeExpansionContext(*fn)); } os << ")"; diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index b70518c0b815d..8dece001a856a 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -101,7 +101,10 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, AliasResult R) { llvm_unreachable("Unhandled AliasResult in switch."); } -SILValue getAccessedMemory(SILInstruction *User) { +// Return the address of the directly accessed memory. If either the address is +// unknown, or any other memory is accessed via indirection, return an invalid +// SILValue. +SILValue getDirectlyAccessedMemory(SILInstruction *User) { if (auto *LI = dyn_cast(User)) { return LI->getOperand(); } @@ -128,7 +131,7 @@ static bool isFunctionArgument(SILValue V) { static bool isIdentifiableObject(SILValue V) { if (isa(V) || isa(V)) return true; - if (isNotAliasingArgument(V)) + if (isExclusiveArgument(V)) return true; return false; } @@ -178,8 +181,7 @@ static bool isLocalLiteral(SILValue V) { /// Is this a value that can be unambiguously identified as being defined at the /// function level. static bool isIdentifiedFunctionLocal(SILValue V) { - return isa(*V) || isNotAliasingArgument(V) || - isLocalLiteral(V); + return isa(*V) || isExclusiveArgument(V) || isLocalLiteral(V); } /// Returns true if we can prove that the two input SILValues which do not equal @@ -473,8 +475,8 @@ static bool typedAccessTBAAMayAlias(SILType LTy, SILType RTy, // If one type is an aggregate and it contains the other type then the record // reference may alias the aggregate reference. - if (LTy.aggregateContainsRecord(RTy, Mod) || - RTy.aggregateContainsRecord(LTy, Mod)) + if (LTy.aggregateContainsRecord(RTy, Mod, F.getTypeExpansionContext()) || + RTy.aggregateContainsRecord(LTy, Mod, F.getTypeExpansionContext())) return true; // FIXME: All the code following could be made significantly more aggressive @@ -617,8 +619,12 @@ AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2, // non-escaping pointer with another (maybe escaping) pointer. Escape analysis // uses the connection graph to check if the pointers may point to the same // content. - // Note that escape analysis must work with the original pointers and not the - // underlying objects because it treats projections differently. + // + // canPointToSameMemory must take the original pointers used for memory + // access, not the underlying object, because objects projections can be + // modeled by escape analysis as different content, and canPointToSameMemory + // assumes that only the pointer itself may be accessed here, not any other + // address that can be derived from this pointer. if (!EA->canPointToSameMemory(V1, V2)) { LLVM_DEBUG(llvm::dbgs() << " Found not-aliased objects based on " "escape analysis\n"); @@ -672,7 +678,7 @@ bool AliasAnalysis::canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr) { if (ArgEffect.mayRelease()) { // The function may release this argument, so check if the pointer can // escape to it. - if (EA->canEscapeToValue(Ptr, FAS.getArgument(Idx))) + if (EA->mayReleaseContent(FAS.getArgument(Idx), Ptr)) return true; } } @@ -690,47 +696,51 @@ bool AliasAnalysis::canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr) { // A builtin can only release an object if it can escape to one of the // builtin's arguments. - if (EA->canEscapeToValue(Ptr, Arg)) + if (EA->mayReleaseContent(Arg, Ptr)) return true; } return false; } +// If the deinit for releasedReference can release any values used by User, then +// this is an interference. (The retains that originally forced liveness of +// those values may have already been eliminated). Note that we only care about +// avoiding a dangling pointer. The memory side affects of Release are +// unordered. +// +// \p releasedReference must be a value that directly contains the references +// being released. It cannot be an address or other kind of pointer that +// indirectly releases a reference. Otherwise, the escape analysis query is +// invalid. +bool AliasAnalysis::mayValueReleaseInterfereWithInstruction( + SILInstruction *User, SILValue releasedReference) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); -bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User, - SILValue Ptr) { - // TODO: Its important to make this as precise as possible. - // - // TODO: Eventually we can plug in some analysis on the what the release of - // the Ptr can do, i.e. be more precise about Ptr's deinit. - // - // TODO: If we know the specific release instruction, we can potentially do - // more. - // // If this instruction can not read or write any memory. Its OK. if (!User->mayReadOrWriteMemory()) return false; - // These instructions do read or write memory, get memory accessed. - SILValue V = getAccessedMemory(User); - if (!V) - return true; - - // Is this a local allocation ? - if (!pointsToLocalObject(V)) + // Get a pointer to the memory directly accessed by 'Users' (either via an + // address or heap reference operand). If additional memory may be indirectly + // accessed by 'User', such as via an inout argument, then stop here because + // mayReleaseContent can only reason about one level of memory access. + // + // TODO: Handle @inout arguments by iterating over the apply arguments. For + // each argument find out if any reachable content can be released. This is + // slightly more involved than mayReleaseContent because it needs to check all + // connection graph nodes reachable from accessedPointer that don't pass + // through another stored reference. + SILValue accessedPointer = getDirectlyAccessedMemory(User); + if (!accessedPointer) return true; - // This is a local allocation. - // The most important check: does the object escape the current function? - auto LO = getUnderlyingObject(V); - auto *ConGraph = EA->getConnectionGraph(User->getFunction()); - auto *Node = ConGraph->getNodeOrNull(LO, EA); - if (Node && !Node->escapes()) - return false; - - // This is either a non-local allocation or a local allocation that escapes. - // We failed to prove anything, it could be read or written by the deinit. - return true; + // If releasedReference can reach the first refcounted object reachable from + // accessedPointer, then releasing it early may destroy the object accessed by + // accessedPointer. Access to any objects beyond the first released refcounted + // object are irrelevant--they must already have sufficient refcount that they + // won't be released when releasing Ptr. + return EA->mayReleaseContent(releasedReference, accessedPointer); } bool swift::isLetPointer(SILValue V) { diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index 52282d2a7cf9d..f58bbb4885b22 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -744,7 +744,8 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues( ReserveFnRef->getType().castTo(); assert(ReserveFnTy->getNumParameters() == 2); StructType *IntType = - ReserveFnTy->getParameters()[0].getType()->castTo(); + ReserveFnTy->getParameters()[0].getArgumentType(F->getModule(), ReserveFnTy) + ->castTo(); StructDecl *IntDecl = IntType->getDecl(); VarDecl *field = IntDecl->getStoredProperties()[0]; SILType BuiltinIntTy =SILType::getPrimitiveObjectType( diff --git a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp index 904518a21b8df..61fe528efe0e6 100644 --- a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp @@ -25,7 +25,24 @@ using namespace swift; -bool CalleeList::allCalleesVisible() { +void CalleeList::dump() const { + print(llvm::errs()); +} + +void CalleeList::print(llvm::raw_ostream &os) const { + os << "Incomplete callee list? : " + << (isIncomplete() ? "Yes" : "No"); + if (!allCalleesVisible()) + os <<", not all callees visible"; + os << '\n'; + os << "Known callees:\n"; + for (auto *CalleeFn : *this) { + os << " " << CalleeFn->getName() << "\n"; + } + os << "\n"; +} + +bool CalleeList::allCalleesVisible() const { if (isIncomplete()) return false; @@ -81,48 +98,45 @@ CalleeCache::getOrCreateCalleesForMethod(SILDeclRef Decl) { return It->second; } -/// Update the callees for each method of a given class, along with -/// all the overridden methods from superclasses. -void CalleeCache::computeClassMethodCalleesForClass(ClassDecl *CD) { - assert(!CD->hasClangNode()); - - for (auto *Member : CD->getMembers()) { - auto *AFD = dyn_cast(Member); - if (!AFD) - continue; - - if (auto *ConstrDecl = dyn_cast(AFD)) { - computeClassMethodCallees(CD, SILDeclRef(AFD, - SILDeclRef::Kind::Initializer)); - if (ConstrDecl->isRequired()) { - computeClassMethodCallees(CD, SILDeclRef(AFD, - SILDeclRef::Kind::Allocator)); +/// Update the callees for each method of a given vtable. +void CalleeCache::computeClassMethodCallees() { + SmallPtrSet unknownCallees; + + // First mark all method declarations which might be overridden in another + // translation unit, i.e. outside the visibility of the optimizer. + // This is a little bit more complicated than to just check the VTable + // entry.Method itself, because an overridden method might be more accessible + // than the base method (e.g. a public method overrides a private method). + for (auto &VTable : M.getVTableList()) { + assert(!VTable.getClass()->hasClangNode()); + + for (Decl *member : VTable.getClass()->getMembers()) { + if (auto *afd = dyn_cast(member)) { + // If a method implementation might be overridden in another translation + // unit, also mark all the base methods as 'unknown'. + bool unknown = false; + do { + if (!calleesAreStaticallyKnowable(M, afd)) + unknown = true; + if (unknown) + unknownCallees.insert(afd); + afd = afd->getOverriddenDecl(); + } while (afd); } - } else { - computeClassMethodCallees(CD, SILDeclRef(AFD)); } } -} - -void CalleeCache::computeClassMethodCallees(ClassDecl *CD, SILDeclRef Method) { - auto *CalledFn = M.lookUpFunctionInVTable(CD, Method); - if (!CalledFn) - return; - - bool canCallUnknown = !calleesAreStaticallyKnowable(M, Method); - - // Update the callees for this method and all the methods it - // overrides by adding this function to their lists. - do { - auto &TheCallees = getOrCreateCalleesForMethod(Method); - assert(TheCallees.getPointer() && "Unexpected null callees!"); - - TheCallees.getPointer()->push_back(CalledFn); - if (canCallUnknown) - TheCallees.setInt(true); - Method = Method.getNextOverriddenVTableEntry(); - } while (Method); + // Second step: collect all implementations of a method. + for (auto &VTable : M.getVTableList()) { + for (const SILVTable::Entry &entry : VTable.getEntries()) { + if (auto *afd = entry.Method.getAbstractFunctionDecl()) { + CalleesAndCanCallUnknown &callees = getOrCreateCalleesForMethod(entry.Method); + if (unknownCallees.count(afd) != 0) + callees.setInt(1); + callees.getPointer()->push_back(entry.Implementation); + } + } + } } void CalleeCache::computeWitnessMethodCalleesForWitnessTable( @@ -183,8 +197,8 @@ void CalleeCache::computeWitnessMethodCalleesForWitnessTable( /// Witness Table. void CalleeCache::computeMethodCallees() { SWIFT_FUNC_STAT; - for (auto &VTable : M.getVTableList()) - computeClassMethodCalleesForClass(VTable.getClass()); + + computeClassMethodCallees(); for (auto &WTable : M.getWitnessTableList()) computeWitnessMethodCalleesForWitnessTable(WTable); @@ -284,3 +298,22 @@ CalleeList CalleeCache::getCalleeList(SILInstruction *I) const { SILDeclRef Destructor = SILDeclRef(Class->getDestructor()); return getCalleeList(Destructor); } + +void BasicCalleeAnalysis::dump() const { + print(llvm::errs()); +} + +void BasicCalleeAnalysis::print(llvm::raw_ostream &os) const { + if (!Cache) { + os << "\n"; + } + llvm::DenseSet printed; + for (auto &VTable : M.getVTableList()) { + for (const SILVTable::Entry &entry : VTable.getEntries()) { + if (printed.insert(entry.Method).second) { + os << "callees for " << entry.Method << ":\n"; + Cache->getCalleeList(entry.Method).print(os); + } + } + } +} diff --git a/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp b/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp index a8670c676533f..3162c361ae070 100644 --- a/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp +++ b/lib/SILOptimizer/Analysis/ColdBlockInfo.cpp @@ -13,6 +13,7 @@ #include "swift/SILOptimizer/Analysis/ColdBlockInfo.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SIL/SILArgument.h" +#include "swift/AST/SemanticAttrs.h" using namespace swift; @@ -83,9 +84,9 @@ ColdBlockInfo::BranchHint ColdBlockInfo::getBranchHint(SILValue Cond, if (F->hasSemanticsAttrs()) { // fastpath/slowpath attrs are untested because the inliner luckily // inlines them before the downstream calls. - if (F->hasSemanticsAttr("slowpath")) + if (F->hasSemanticsAttr(semantics::SLOWPATH)) return BranchHint::LikelyFalse; - else if (F->hasSemanticsAttr("fastpath")) + else if (F->hasSemanticsAttr(semantics::FASTPATH)) return BranchHint::LikelyTrue; } } diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 0b6316d9b8044..e5b1eec09a578 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -13,53 +13,182 @@ #define DEBUG_TYPE "sil-escape" #include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" #include "swift/SIL/DebugUtils.h" +#include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" -#include "swift/SILOptimizer/Analysis/ValueTracking.h" #include "swift/SILOptimizer/PassManager/PassManager.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/raw_ostream.h" using namespace swift; +using CGNode = EscapeAnalysis::CGNode; + +static llvm::cl::opt EnableInternalVerify( + "escapes-internal-verify", + llvm::cl::desc("Enable internal verification of escape analysis"), + llvm::cl::init(false)); + +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findRecursivePointerKind(SILType Ty, + const SILFunction &F) const { + // An address may be converted into a reference via something like + // raw_pointer_to_ref, but in general we don't know what kind of pointer it + // is. + if (Ty.isAddress()) + return EscapeAnalysis::AnyPointer; -static bool isExtractOfArrayUninitializedPointer(TupleExtractInst *TEI) { - if (TEI->getFieldNo() == 1) { - if (auto apply = dyn_cast(TEI->getOperand())) - if (ArraySemanticsCall(apply, "array.uninitialized", false)) - return true; + // Opaque types may contain a reference. Speculatively track them too. + // + // 1. It may be possible to optimize opaque values based on known mutation + // points. + // + // 2. A specialized function may call a generic function passing a concrete + // reference type via incomplete specialization. + // + // 3. A generic function may call a specialized function taking a concrete + // reference type via devirtualization. + if (Ty.isAddressOnly(F)) + return EscapeAnalysis::AnyPointer; + + // A raw pointer definitely does not have a reference, but could point + // anywhere. We do track these because critical stdlib data structures often + // use raw pointers under the hood. + if (Ty.getASTType() == F.getModule().getASTContext().TheRawPointerType) + return EscapeAnalysis::AnyPointer; + + if (Ty.hasReferenceSemantics()) + return EscapeAnalysis::ReferenceOnly; + + auto &M = F.getModule(); + + // Start with the most precise pointer kind + PointerKind aggregateKind = NoPointer; + auto meetAggregateKind = [&](PointerKind otherKind) { + if (otherKind > aggregateKind) + aggregateKind = otherKind; + }; + if (auto *Str = Ty.getStructOrBoundGenericStruct()) { + for (auto *Field : Str->getStoredProperties()) { + SILType fieldTy = Ty.getFieldType(Field, M, F.getTypeExpansionContext()) + .getObjectType(); + meetAggregateKind(findCachedPointerKind(fieldTy, F)); + } + return aggregateKind; } - return false; + if (auto TT = Ty.getAs()) { + for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { + meetAggregateKind(findCachedPointerKind(Ty.getTupleElementType(i), F)); + } + return aggregateKind; + } + if (auto En = Ty.getEnumOrBoundGenericEnum()) { + for (auto *ElemDecl : En->getAllElements()) { + if (!ElemDecl->hasAssociatedValues()) + continue; + SILType eltTy = + Ty.getEnumElementType(ElemDecl, M, F.getTypeExpansionContext()); + meetAggregateKind(findCachedPointerKind(eltTy, F)); + } + return aggregateKind; + } + // FIXME: without a covered switch, this is not robust in the event that new + // reference-holding AST types are invented. + return NoPointer; } -static SingleValueInstruction *isProjection(SILNode *node) { - switch (node->getKind()) { - case SILNodeKind::IndexAddrInst: - case SILNodeKind::IndexRawPointerInst: - case SILNodeKind::StructElementAddrInst: - case SILNodeKind::TupleElementAddrInst: - case SILNodeKind::UncheckedTakeEnumDataAddrInst: - case SILNodeKind::StructExtractInst: - case SILNodeKind::UncheckedEnumDataInst: - case SILNodeKind::MarkDependenceInst: - case SILNodeKind::PointerToAddressInst: - case SILNodeKind::AddressToPointerInst: - case SILNodeKind::InitEnumDataAddrInst: - return cast(node); - case SILNodeKind::TupleExtractInst: { - auto *TEI = cast(node); +// Returns the kind of pointer that \p Ty recursively contains. +EscapeAnalysis::PointerKind +EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const { + auto iter = pointerKindCache.find(Ty); + if (iter != pointerKindCache.end()) + return iter->second; + + PointerKind pointerKind = findRecursivePointerKind(Ty, F); + const_cast(this)->pointerKindCache[Ty] = pointerKind; + return pointerKind; +} + +// If EscapeAnalysis should consider the given value to be a derived address or +// pointer based on one of its address or pointer operands, then return that +// operand value. Otherwise, return an invalid value. +SILValue EscapeAnalysis::getPointerBase(SILValue value) { + switch (value->getKind()) { + case ValueKind::IndexAddrInst: + case ValueKind::IndexRawPointerInst: + case ValueKind::StructElementAddrInst: + case ValueKind::StructExtractInst: + case ValueKind::TupleElementAddrInst: + case ValueKind::BeginAccessInst: + case ValueKind::UncheckedTakeEnumDataAddrInst: + case ValueKind::UncheckedEnumDataInst: + case ValueKind::MarkDependenceInst: + case ValueKind::PointerToAddressInst: + case ValueKind::AddressToPointerInst: + case ValueKind::InitEnumDataAddrInst: + case ValueKind::UncheckedRefCastInst: + case ValueKind::ConvertFunctionInst: + case ValueKind::UpcastInst: + case ValueKind::InitExistentialRefInst: + case ValueKind::OpenExistentialRefInst: + case ValueKind::RawPointerToRefInst: + case ValueKind::RefToRawPointerInst: + case ValueKind::RefToBridgeObjectInst: + case ValueKind::BridgeObjectToRefInst: + case ValueKind::UncheckedAddrCastInst: + case ValueKind::UnconditionalCheckedCastInst: + // DO NOT use LOADABLE_REF_STORAGE because unchecked references don't have + // retain/release instructions that trigger the 'default' case. +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case ValueKind::RefTo##Name##Inst: \ + case ValueKind::Name##ToRefInst: +#include "swift/AST/ReferenceStorage.def" + return cast(value)->getOperand(0); + + case ValueKind::TupleExtractInst: { + auto *TEI = cast(value); // Special handling for extracting the pointer-result from an - // array construction. We handle this like a ref_element_addr - // rather than a projection. See the handling of tuple_extract - // in analyzeInstruction(). - if (isExtractOfArrayUninitializedPointer(TEI)) - return nullptr; - return TEI; + // array construction. See createArrayUninitializedSubgraph. + if (canOptimizeArrayUninitializedResult(TEI)) + return SILValue(); + return TEI->getOperand(); + } + case ValueKind::StructInst: + case ValueKind::TupleInst: + case ValueKind::EnumInst: { + // Allow a single-operand aggregate to share its operand's node. + auto *SVI = cast(value); + SILValue pointerOperand; + for (SILValue opV : SVI->getOperandValues()) { + if (!isPointer(opV)) + continue; + + if (pointerOperand) + return SILValue(); + + pointerOperand = opV; + } + return pointerOperand; } default: - return nullptr; + return SILValue(); + } +} + +// Recursively find the given value's pointer base. If the value cannot be +// represented in EscapeAnalysis as one of its operands, then return the same +// value. +SILValue EscapeAnalysis::getPointerRoot(SILValue value) { + while (true) { + if (SILValue v2 = getPointerBase(value)) + value = v2; + else + break; } + return value; } static bool isNonWritableMemoryAddress(SILNode *V) { @@ -84,15 +213,171 @@ static bool isNonWritableMemoryAddress(SILNode *V) { } } -static ValueBase *skipProjections(ValueBase *V) { - for (;;) { - if (auto SVI = isProjection(V)) { - V = SVI->getOperand(0); - } else { - return V; - } +// Implement an intrusive worklist of CGNode. Only one may be in use at a time. +struct EscapeAnalysis::CGNodeWorklist { + llvm::SmallVector nodeVector; + EscapeAnalysis::ConnectionGraph *conGraph; + + CGNodeWorklist(const CGNodeWorklist &) = delete; + + CGNodeWorklist(EscapeAnalysis::ConnectionGraph *conGraph) + : conGraph(conGraph) { + conGraph->activeWorklist = this; + } + ~CGNodeWorklist() { reset(); } + // Clear the intrusive isInWorkList flags, but leave the nodeVector vector in + // place for subsequent iteration. + void reset() { + ConnectionGraph::clearWorkListFlags(nodeVector); + conGraph->activeWorklist = nullptr; + } + unsigned size() const { return nodeVector.size(); } + + bool empty() const { return nodeVector.empty(); } + + bool contains(CGNode *node) const { + assert(conGraph->activeWorklist == this); + return node->isInWorkList; + } + CGNode *operator[](unsigned idx) const { + assert(idx < size()); + return nodeVector[idx]; + } + bool tryPush(CGNode *node) { + assert(conGraph->activeWorklist == this); + if (node->isInWorkList) + return false; + + node->isInWorkList = true; + nodeVector.push_back(node); + return true; + } + void push(CGNode *node) { + assert(conGraph->activeWorklist == this); + assert(!node->isInWorkList); + node->isInWorkList = true; + nodeVector.push_back(node); + } +}; + +/// Mapping from nodes in a callee-graph to nodes in a caller-graph. +class EscapeAnalysis::CGNodeMap { + /// The map itself. + llvm::DenseMap Map; + + /// The list of source nodes (= keys in Map), which is used as a work-list. + CGNodeWorklist MappedNodes; + +public: + CGNodeMap(ConnectionGraph *conGraph) : MappedNodes(conGraph) {} + CGNodeMap(const CGNodeMap &) = delete; + + /// Adds a mapping and pushes the \p From node into the work-list + /// MappedNodes. + void add(CGNode *From, CGNode *To) { + assert(From && To && !From->isMerged && !To->isMerged); + Map[From] = To; + MappedNodes.tryPush(From); + } + /// Looks up a node in the mapping. + CGNode *get(CGNode *From) const { + auto Iter = Map.find(From); + if (Iter == Map.end()) + return nullptr; + + return Iter->second->getMergeTarget(); + } + CGNodeWorklist &getMappedNodes() { return MappedNodes; } +}; + +//===----------------------------------------------------------------------===// +// ConnectionGraph Implementation +//===----------------------------------------------------------------------===// + +std::pair EscapeAnalysis::CGNode::getRepNode( + SmallPtrSetImpl &visited) const { + if (!isContent() || mappedValue) + return {this, 0}; + + for (Predecessor pred : Preds) { + if (!pred.is(EdgeType::PointsTo)) + continue; + if (!visited.insert(pred.getPredNode()).second) + continue; + auto repNodeAndDepth = pred.getPredNode()->getRepNode(visited); + if (repNodeAndDepth.first) + return {repNodeAndDepth.first, repNodeAndDepth.second + 1}; + // If a representative node was not found on this pointsTo node, recursion + // must have hit a cycle. Try the next pointsTo edge. + } + return {nullptr, 0}; +} + +EscapeAnalysis::CGNode::RepValue EscapeAnalysis::CGNode::getRepValue() const { + // We don't use CGNodeWorklist because CGNode::dump() should be callable + // anywhere, even while another worklist is active, and getRepValue() itself + // is not on any critical path. + SmallPtrSet visited({this}); + const CGNode *repNode; + unsigned depth; + std::tie(repNode, depth) = getRepNode(visited); + return {{repNode ? SILValue(repNode->mappedValue) : SILValue(), + repNode && repNode->Type == EscapeAnalysis::NodeType::Return}, + depth}; +} + +void EscapeAnalysis::CGNode::mergeFlags(bool isInterior, + bool hasReferenceOnly) { + // isInterior is conservatively preserved from either node unless two content + // nodes are being merged and one is the interior node's content. + isInteriorFlag |= isInterior; + + // hasReferenceOnly is always conservatively merged. + hasReferenceOnlyFlag &= hasReferenceOnly; +} + +void EscapeAnalysis::CGNode::mergeProperties(CGNode *fromNode) { + // isInterior is conservatively preserved from either node unless the other + // node is the interior node's content. + bool isInterior = fromNode->isInteriorFlag; + if (fromNode == pointsTo) + this->isInteriorFlag = isInterior; + else if (this == fromNode->pointsTo) + isInterior = this->isInteriorFlag; + + mergeFlags(isInterior, fromNode->hasReferenceOnlyFlag); +} + +template +bool EscapeAnalysis::CGNode::visitSuccessors(Visitor &&visitor) const { + if (CGNode *pointsToSucc = getPointsToEdge()) { + // Visit pointsTo, even if pointsTo == this. + if (!visitor(pointsToSucc)) + return false; + } + for (CGNode *def : defersTo) { + if (!visitor(def)) + return false; } - llvm_unreachable("there is no escape from an infinite loop"); + return true; +} + +template +bool EscapeAnalysis::CGNode::visitDefers(Visitor &&visitor) const { + // Save predecessors before calling `visitor` which may assign pointsTo edges + // which invalidates the predecessor iterator. + SmallVector predVector(Preds.begin(), Preds.end()); + for (Predecessor pred : predVector) { + if (!pred.is(EdgeType::Defer)) + continue; + if (!visitor(pred.getPredNode(), false)) + return false; + } + for (auto *deferred : defersTo) { + if (!visitor(deferred, true)) + return false; + } + return true; } void EscapeAnalysis::ConnectionGraph::clear() { @@ -105,70 +390,228 @@ void EscapeAnalysis::ConnectionGraph::clear() { assert(ToMerge.empty()); } -EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph:: -getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded) { +// This never returns an interior node. It should never be called directly on an +// address projection of a reference. To get the interior node for an address +// projection, always ask for the content of the projection's base instead using +// getValueContent() or getReferenceContent(). +// +// Address phis are not allowed, so merging an unknown address with a reference +// address projection is rare. If that happens, then the projection's node loses +// it's interior property. +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getNode(SILValue V) { + // Early filter obvious non-pointer opcodes. if (isa(V) || isa(V) || isa(V)) return nullptr; - - if (!EA->isPointer(V)) + + // Create the node flags based on the derived value's kind. If the pointer + // base type has non-reference pointers but they are never accessed in the + // current function, then ignore them. + PointerKind pointerKind = EA->getPointerKind(V); + if (pointerKind == EscapeAnalysis::NoPointer) return nullptr; - - V = skipProjections(V); - if (!createIfNeeded) - return lookupNode(V); - - CGNode * &Node = Values2Nodes[V]; - if (!Node) { - if (isa(V)) { - Node = allocNode(V, NodeType::Argument); - if (!isSummaryGraph) - Node->mergeEscapeState(EscapeState::Arguments); - } else { - Node = allocNode(V, NodeType::Value); - } - } - return Node->getMergeTarget(); -} + // Look past address projections, pointer casts, and the like within the same + // object. Does not look past a dereference such as ref_element_addr, or + // project_box. + SILValue ptrBase = EA->getPointerRoot(V); + // Do not create a node for undef values so we can verify that node values + // have the correct pointer kind. + if (!ptrBase->getFunction()) + return nullptr; -EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph::getContentNode( - CGNode *AddrNode) { - // Do we already have a content node (which is not necessarily an immediate - // successor of AddrNode)? - if (AddrNode->pointsTo) - return AddrNode->pointsTo; + assert(EA->isPointer(ptrBase) && + "The base for derived pointer must also be a pointer type"); - CGNode *Node = allocNode(AddrNode->V, NodeType::Content); - updatePointsTo(AddrNode, Node); - assert(ToMerge.empty() && - "Initially setting pointsTo should not require any node merges"); + bool hasReferenceOnly = canOnlyContainReferences(pointerKind); + // Update the value-to-node map. + CGNode *&Node = Values2Nodes[ptrBase]; + if (Node) { + CGNode *targetNode = Node->getMergeTarget(); + targetNode->mergeFlags(false /*isInterior*/, hasReferenceOnly); + return targetNode; + } + if (isa(ptrBase)) { + Node = allocNode(ptrBase, NodeType::Argument, false, hasReferenceOnly); + if (!isSummaryGraph) + Node->mergeEscapeState(EscapeState::Arguments); + } else + Node = allocNode(ptrBase, NodeType::Value, false, hasReferenceOnly); return Node; } -bool EscapeAnalysis::ConnectionGraph::addDeferEdge(CGNode *From, CGNode *To) { - if (!From->addDeferred(To)) - return false; +/// Adds an argument/instruction in which the node's memory is released. +int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, + SILInstruction *User) { + // Use points are never consulted for escaping nodes, but still need to + // propagate to other nodes in a defer web. Even if this node is escaping, + // some defer predecessors may not be escaping. Only checking if this node has + // defer predecessors is insufficient because a defer successor of this node + // may have defer predecessors. + if (Node->getEscapeState() >= EscapeState::Global) + return -1; + + int Idx = (int)UsePoints.size(); + assert(UsePoints.count(User) == 0 && "value is already a use-point"); + UsePoints[User] = Idx; + UsePointTable.push_back(User); + assert(UsePoints.size() == UsePointTable.size()); + Node->setUsePointBit(Idx); + return Idx; +} + +CGNode *EscapeAnalysis::ConnectionGraph::defer(CGNode *From, CGNode *To, + bool &Changed) { + if (!From->canAddDeferred(To)) + return From; CGNode *FromPointsTo = From->pointsTo; CGNode *ToPointsTo = To->pointsTo; - if (FromPointsTo != ToPointsTo) { - if (!ToPointsTo) { - updatePointsTo(To, FromPointsTo->getMergeTarget()); - assert(ToMerge.empty() && - "Initially setting pointsTo should not require any node merges"); - } else { - // We are adding an edge between two pointers which point to different - // content nodes. This will require to merge the content nodes (and maybe - // other content nodes as well), because of the graph invariance 4). - updatePointsTo(From, ToPointsTo->getMergeTarget()); - } + // If necessary, merge nodes while the graph is still in a valid state. + if (FromPointsTo && ToPointsTo && FromPointsTo != ToPointsTo) { + // We are adding an edge between two pointers which point to different + // content nodes. This will require merging the content nodes (and maybe + // other content nodes as well), because of the graph invariance 4). + // + // Once the pointee's are merged, the defer edge can be added without + // creating an inconsistency. + scheduleToMerge(FromPointsTo, ToPointsTo); + mergeAllScheduledNodes(); + Changed = true; } - return true; + // 'From' and 'To' may have been merged, so addDeferred may no longer succeed. + if (From->getMergeTarget()->addDeferred(To->getMergeTarget())) + Changed = true; + + // If pointsTo on either side of the defer was uninitialized, initialize that + // side of the defer web. Do this after adding the new edge to avoid creating + // useless pointsTo edges. + if (!FromPointsTo && ToPointsTo) + initializePointsTo(From, ToPointsTo); + else if (FromPointsTo && !ToPointsTo) + initializePointsTo(To, FromPointsTo); + + return From->getMergeTarget(); +} + +// Precondition: The pointsTo fields of all nodes in initializeNode's defer web +// are either uninitialized or already initialized to newPointsTo. +void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode, + CGNode *newPointsTo, + bool createEdge) { + // Track nodes that require pointsTo edges. + llvm::SmallVector pointsToEdgeNodes; + if (createEdge) + pointsToEdgeNodes.push_back(initialNode); + + // Step 1: Visit each node that reaches or is reachable via defer edges until + // reaching a node with the newPointsTo or with a proper pointsTo edge. + + // A worklist to gather updated nodes in the defer web. + CGNodeWorklist updatedNodes(this); + unsigned updateCount = 0; + + auto visitDeferTarget = [&](CGNode *node, bool /*isSuccessor*/) { + if (updatedNodes.contains(node)) + return true; + + if (node->pointsTo) { + assert(node->pointsTo == newPointsTo); + // Since this node already had a pointsTo, it must reach a pointsTo + // edge. Stop traversing the defer-web here--this is complete becaused + // nodes are initialized one at a time, each time a new defer edge is + // created. If this were not complete, then the backward traversal below + // in Step 2 could reach uninitialized nodes not seen here in Step 1. + pointsToEdgeNodes.push_back(node); + return true; + } + ++updateCount; + if (node->defersTo.empty()) { + // If this node is the end of a defer-edge path with no pointsTo + // edge. Create a "fake" pointsTo edge to maintain the graph invariant + // (this changes the structure of the graph but adding this edge has no + // effect on the process of merging nodes or creating new defer edges). + pointsToEdgeNodes.push_back(node); + } + updatedNodes.push(node); + return true; + }; + // Seed updatedNodes with initialNode. + visitDeferTarget(initialNode, true); + // updatedNodes may grow during this loop. + for (unsigned idx = 0; idx < updatedNodes.size(); ++idx) + updatedNodes[idx]->visitDefers(visitDeferTarget); + // Reset this worklist so others can be used, but updateNode.nodeVector still + // holds all the nodes found by step 1. + updatedNodes.reset(); + + // Step 2: Update pointsTo fields by propagating backward from nodes that + // already have a pointsTo edge. + do { + while (!pointsToEdgeNodes.empty()) { + CGNode *edgeNode = pointsToEdgeNodes.pop_back_val(); + if (!edgeNode->pointsTo) { + // This node is either (1) a leaf node in the defer web (identified in + // step 1) or (2) an arbitrary node in a defer-cycle (identified in a + // previous iteration of the outer loop). + edgeNode->setPointsToEdge(newPointsTo); + newPointsTo->mergeUsePoints(edgeNode); + assert(updateCount--); + } + // If edgeNode is already set to newPointsTo, it either was already + // up-to-date before calling initializePointsTo, or it was visited during + // a previous iteration of the backward traversal below. Rather than + // distinguish these cases, always retry backward traversal--it just won't + // revisit any edges in the later case. + backwardTraverse(edgeNode, [&](Predecessor pred) { + if (!pred.is(EdgeType::Defer)) + return Traversal::Backtrack; + + CGNode *predNode = pred.getPredNode(); + if (predNode->pointsTo) { + assert(predNode->pointsTo->getMergeTarget() + == newPointsTo->getMergeTarget()); + return Traversal::Backtrack; + } + predNode->pointsTo = newPointsTo; + newPointsTo->mergeUsePoints(predNode); + assert(updateCount--); + return Traversal::Follow; + }); + } + // For all nodes visited in step 1, pick a single node that was not + // backward-reachable from a pointsTo edge, create an edge for it and + // restart traversal. This only happens when step 1 fails to find leaves in + // the defer web because of defer edge cycles. + while (!updatedNodes.empty()) { + CGNode *node = updatedNodes.nodeVector.pop_back_val(); + if (!node->pointsTo) { + pointsToEdgeNodes.push_back(node); + break; + } + } + // This outer loop is exceedingly unlikely to execute more than twice. + } while (!pointsToEdgeNodes.empty()); + assert(updateCount == 0); } void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { + // Each merge step is self contained and verifiable, with one exception. When + // merging a node that points to itself with a node points to another node, + // multiple merge steps are necessary to make the defer web consistent. + // Example: + // NodeA pointsTo-> From + // From defersTo-> NodeA (an indirect self-cycle) + // To pointsTo-> NodeB + // Merged: + // NodeA pointsTo-> To + // To defersTo-> NodeA (To *should* pointTo itself) + // To pointsTo-> NodeB (but still has a pointsTo edge to NodeB) while (!ToMerge.empty()) { + if (EnableInternalVerify) + verifyStructure(true /*allowMerge*/); + CGNode *From = ToMerge.pop_back_val(); CGNode *To = From->getMergeTarget(); assert(To != From && "Node scheduled to merge but no merge target set"); @@ -176,204 +619,196 @@ void EscapeAnalysis::ConnectionGraph::mergeAllScheduledNodes() { assert(From->Type == NodeType::Content && "Can only merge content nodes"); assert(To->Type == NodeType::Content && "Can only merge content nodes"); - // Unlink the predecessors and redirect the incoming pointsTo edge. - // Note: we don't redirect the defer-edges because we don't want to trigger - // updatePointsTo (which is called by addDeferEdge) right now. + // Redirect the incoming pointsTo edge and unlink the defer predecessors. + // + // Don't redirect the defer-edges because it may trigger mergePointsTo() or + // initializePointsTo(). By ensuring that 'From' is unreachable first, the + // graph appears consistent during those operations. for (Predecessor Pred : From->Preds) { - CGNode *PredNode = Pred.getPointer(); - if (Pred.getInt() == EdgeType::PointsTo) { - assert(PredNode->getPointsToEdge() == From && - "Incoming pointsTo edge not set in predecessor"); + CGNode *PredNode = Pred.getPredNode(); + if (Pred.is(EdgeType::PointsTo)) { + assert(PredNode->getPointsToEdge() == From + && "Incoming pointsTo edge not set in predecessor"); if (PredNode != From) - PredNode->setPointsTo(To); + PredNode->setPointsToEdge(To); } else { assert(PredNode != From); auto Iter = PredNode->findDeferred(From); - assert(Iter != PredNode->defersTo.end() && - "Incoming defer-edge not found in predecessor's defer list"); + assert(Iter != PredNode->defersTo.end() + && "Incoming defer-edge not found in predecessor's defer list"); PredNode->defersTo.erase(Iter); } } - // Unlink and redirect the outgoing pointsTo edge. - if (CGNode *PT = From->getPointsToEdge()) { - if (PT != From) { - PT->removeFromPreds(Predecessor(From, EdgeType::PointsTo)); - } else { - PT = To; - } - if (CGNode *ExistingPT = To->getPointsToEdge()) { - // The To node already has an outgoing pointsTo edge, so the only thing - // we can do is to merge both content nodes. - scheduleToMerge(ExistingPT, PT); - } else { - To->setPointsTo(PT); - } - } // Unlink the outgoing defer edges. for (CGNode *Defers : From->defersTo) { assert(Defers != From && "defer edge may not form a self-cycle"); Defers->removeFromPreds(Predecessor(From, EdgeType::Defer)); } - // Redirect the incoming defer edges. This may trigger other node merges. - // Note that the Pred iterator may be invalidated (because we may add - // edges in the loop). So we don't do: for (Pred : From->Preds) {...} - for (unsigned PredIdx = 0; PredIdx < From->Preds.size(); ++PredIdx) { - CGNode *PredNode = From->Preds[PredIdx].getPointer(); - if (From->Preds[PredIdx].getInt() == EdgeType::Defer) { - assert(PredNode != From && "defer edge may not form a self-cycle"); - addDeferEdge(PredNode, To); - } - } - // Redirect the outgoing defer edges, which may also trigger other node - // merges. - for (CGNode *Defers : From->defersTo) { - addDeferEdge(To, Defers); - } - // There is no point in updating the pointsTo if the To node will be - // merged to another node eventually. - if (!To->mergeTo) { - // Ensure that graph invariance 4) is kept. At this point there may be still - // some violations because of the new adjacent edges of the To node. - for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { - if (To->Preds[PredIdx].getInt() == EdgeType::PointsTo) { - CGNode *PredNode = To->Preds[PredIdx].getPointer(); - for (unsigned PPIdx = 0; PPIdx < PredNode->Preds.size(); ++PPIdx) { - if (PredNode->Preds[PPIdx].getInt() == EdgeType::Defer) - updatePointsTo(PredNode->Preds[PPIdx].getPointer(), To); - } - for (CGNode *Def : PredNode->defersTo) { - updatePointsTo(Def, To); - } - } - } - if (CGNode *ToPT = To->getPointsToEdge()) { - ToPT = ToPT->getMergeTarget(); - for (CGNode *ToDef : To->defersTo) { - updatePointsTo(ToDef, ToPT); - assert(!ToPT->mergeTo); - } - for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { - if (To->Preds[PredIdx].getInt() == EdgeType::Defer) - updatePointsTo(To->Preds[PredIdx].getPointer(), ToPT); + // Handle self-cycles on From by creating a self-cycle at To. + auto redirectPointsTo = [&](CGNode *pointsTo) { + return (pointsTo == From) ? To : pointsTo; + }; + // Redirect the outgoing From -> pointsTo edge. + if (From->pointsToIsEdge) { + From->pointsTo->removeFromPreds(Predecessor(From, EdgeType::PointsTo)); + if (To->pointsToIsEdge) { + // If 'To' had a pointsTo edge to 'From', then it was redirected above. + // Otherwise FromPT and ToPT will be merged below; nothing to do here. + assert(To->pointsTo != From); + } else { + // If 'To' has no pointsTo at all, initialize its defer web. + if (!To->pointsTo) + initializePointsToEdge(To, redirectPointsTo(From->pointsTo)); + else { + // Upgrade 'To's pointsTo to an edge to preserve the fact that 'From' + // had a pointsTo edge. + To->pointsToIsEdge = true; + To->pointsTo = redirectPointsTo(To->pointsTo); + To->pointsTo->Preds.push_back(Predecessor(To, EdgeType::PointsTo)); } } - To->mergeEscapeState(From->State); } + // Merge 'From->pointsTo' and 'To->pointsTo' if needed, regardless of + // whether either is a proper edge. Merging may be needed because other + // nodes may have points-to edges to From->PointsTo that won't be visited + // when updating 'From's defer web. + // + // If To doesn't already have a points-to, it will simply be initialized + // when updating the merged defer web below. + if (CGNode *toPT = To->pointsTo) { + // If 'To' already points to 'From', then it will already point to 'From's + // pointTo after merging. An additional merge would be too conservative. + if (From->pointsTo && toPT != From) + scheduleToMerge(redirectPointsTo(From->pointsTo), toPT); + } + // Redirect adjacent defer edges, and immediately update all points-to + // fields in the defer web. + // + // Calling initializePointsTo may create new pointsTo edges from nodes in + // the defer-web. It is unsafe to mutate or query the graph in its currently + // inconsistent state. However, this particular case is safe because: + // - The graph is only locally inconsistent w.r.t. nodes still connected to + // 'From' via defer edges. + // - 'From' itself is no longer reachable via graph edges (it may only be + // referenced in points-to fields which haven't all been updated). + // - Calling initializePointsTo on one from 'From's deferred nodes implies + // that all nodes in 'From's defer web had a null pointsTo. + // - 'To's defer web remains consistent each time a new defer edge is + // added below. Any of 'To's existing deferred nodes either still need to + // be initialized or have already been initialized to the same pointsTo. + // + // Start by updating 'To's own pointsTo field. + if (To->pointsTo == From) + mergePointsTo(To, To); + + auto mergeDeferPointsTo = [&](CGNode *deferred, bool isSuccessor) { + assert(From != deferred && "defer edge may not form a self-cycle"); + if (To == deferred) + return true; + + // In case 'deferred' points to 'From', update its pointsTo before + // exposing it to 'To's defer web. + if (deferred->pointsTo == From) + mergePointsTo(deferred, To); + + if (isSuccessor) + To->addDeferred(deferred); + else + deferred->addDeferred(To); + + if (deferred->pointsTo && To->pointsTo) + mergePointsTo(deferred, To->pointsTo); + else if (deferred->pointsTo) + initializePointsTo(To, deferred->pointsTo); + else if (To->pointsTo) + initializePointsTo(deferred, To->pointsTo); + + return true; + }; + // Redirect the adjacent defer edges. + From->visitDefers(mergeDeferPointsTo); + + // Update the web of nodes that originally pointed to 'From' via 'From's old + // pointsTo predecessors (which are now attached to 'To'). + for (unsigned PredIdx = 0; PredIdx < To->Preds.size(); ++PredIdx) { + auto predEdge = To->Preds[PredIdx]; + if (!predEdge.is(EdgeType::PointsTo)) + continue; + predEdge.getPredNode()->visitDefers( + [&](CGNode *deferred, bool /*isSucc*/) { + mergePointsTo(deferred, To); + return true; + }); + } + To->mergeEscapeState(From->State); + // Cleanup the merged node. From->isMerged = true; + + if (From->mappedValue) { + // values previously mapped to 'From' but not transferred to 'To's + // mappedValue must remain mapped to 'From'. Lookups on those values will + // find 'To' via the mergeTarget. Dropping a value's mapping is illegal + // because it could cause a node to be recreated without the edges that + // have already been discovered. + if (!To->mappedValue) { + To->mappedValue = From->mappedValue; + Values2Nodes[To->mappedValue] = To; + } + From->mappedValue = nullptr; + } From->Preds.clear(); From->defersTo.clear(); From->pointsTo = nullptr; } + if (EnableInternalVerify) + verifyStructure(true /*allowMerge*/); } -void EscapeAnalysis::ConnectionGraph:: -updatePointsTo(CGNode *InitialNode, CGNode *pointsTo) { - // Visit all nodes in the defer web, which don't have the right pointsTo set. - assert(!pointsTo->mergeTo); - llvm::SmallVector WorkList; - WorkList.push_back(InitialNode); - InitialNode->isInWorkList = true; - bool isInitialSet = false; - for (unsigned Idx = 0; Idx < WorkList.size(); ++Idx) { - auto *Node = WorkList[Idx]; - if (Node->pointsTo == pointsTo) - continue; - - if (Node->pointsTo) { - // Mismatching: we need to merge! - scheduleToMerge(Node->pointsTo, pointsTo); - } else { - isInitialSet = true; - } +// As a result of a merge, update the pointsTo field of initialNode and +// everything in its defer web to newPointsTo. +// +// This may modify the graph by redirecting a pointsTo edges. +void EscapeAnalysis::ConnectionGraph::mergePointsTo(CGNode *initialNode, + CGNode *newPointsTo) { + CGNode *oldPointsTo = initialNode->pointsTo; + assert(oldPointsTo && "merging content should not initialize any pointsTo"); + + // newPointsTo may already be scheduled for a merge. Only create new edges to + // unmerged nodes. This may create a temporary pointsTo mismatch in the defer + // web, but Graph verification takes merged nodes into consideration. + newPointsTo = newPointsTo->getMergeTarget(); + if (oldPointsTo == newPointsTo) + return; - // If the node already has a pointsTo _edge_ we don't change it (we don't - // want to change the structure of the graph at this point). - if (!Node->pointsToIsEdge) { - if (Node->defersTo.empty()) { - // This node is the end of a defer-edge path with no pointsTo connected. - // We create an edge to pointsTo (agreed, this changes the structure of - // the graph but adding this edge is harmless). - Node->setPointsTo(pointsTo); - } else { - Node->pointsTo = pointsTo; - } - // Update use-points if the use-point information is already calculated. - pointsTo->mergeUsePoints(Node); - } + CGNodeWorklist updateNodes(this); + auto updatePointsTo = [&](CGNode *node) { + if (node->pointsTo == newPointsTo) + return; + // If the original graph was: 'node->From->To->newPointsTo' or + // 'node->From->From', then node is already be updated to point to + // 'To' and 'To' must be merged with newPointsTo. We must still update + // pointsTo so that all nodes in the defer web have the same pointsTo. + assert(node->pointsTo == oldPointsTo + || node->pointsTo->getMergeTarget() == newPointsTo); + if (node->pointsToIsEdge) { + node->pointsTo->removeFromPreds(Predecessor(node, EdgeType::PointsTo)); + node->setPointsToEdge(newPointsTo); + } else + node->pointsTo = newPointsTo; + updateNodes.push(node); + }; + updatePointsTo(initialNode); - // Add all adjacent nodes to the WorkList. - for (auto *Deferred : Node->defersTo) { - if (!Deferred->isInWorkList) { - WorkList.push_back(Deferred); - Deferred->isInWorkList = true; - } - } - for (Predecessor Pred : Node->Preds) { - if (Pred.getInt() == EdgeType::Defer) { - CGNode *PredNode = Pred.getPointer(); - if (!PredNode->isInWorkList) { - WorkList.push_back(PredNode); - PredNode->isInWorkList = true; - } - } - } - } - if (isInitialSet) { - // Here we handle a special case: all defer-edge paths must eventually end - // in a points-to edge to pointsTo. We ensure this by setting the edge on - // nodes which have no defer-successors (see above). But this does not cover - // the case where there is a terminating cycle in the defer-edge path, - // e.g. A -> B -> C -> B - // We find all nodes which don't reach a points-to edge and add additional - // points-to edges to fix that. - llvm::SmallVector PotentiallyInCycles; - - // Keep all nodes with a points-to edge in the WorkList and remove all other - // nodes. - unsigned InsertionPoint = 0; - for (CGNode *Node : WorkList) { - if (Node->pointsToIsEdge) { - WorkList[InsertionPoint++] = Node; - } else { - Node->isInWorkList = false; - PotentiallyInCycles.push_back(Node); - } - } - WorkList.set_size(InsertionPoint); - unsigned Idx = 0; - while (!PotentiallyInCycles.empty()) { - - // Propagate the "reaches-a-points-to-edge" backwards in the defer-edge - // sub-graph by adding those nodes to the WorkList. - while (Idx < WorkList.size()) { - auto *Node = WorkList[Idx++]; - for (Predecessor Pred : Node->Preds) { - if (Pred.getInt() == EdgeType::Defer) { - CGNode *PredNode = Pred.getPointer(); - if (!PredNode->isInWorkList) { - WorkList.push_back(PredNode); - PredNode->isInWorkList = true; - } - } - } - } - // Check if we still have some nodes which don't reach a points-to edge, - // i.e. points not yet in the WorkList. - while (!PotentiallyInCycles.empty()) { - auto *Node = PotentiallyInCycles.pop_back_val(); - if (!Node->isInWorkList) { - // We create a points-to edge for the first node which doesn't reach - // a points-to edge yet. - Node->setPointsTo(pointsTo); - WorkList.push_back(Node); - Node->isInWorkList = true; - break; - } - } - } - } - clearWorkListFlags(WorkList); + // Visit each node that reaches or is reachable via defer edges until reaching + // a node with the newPointsTo. + auto visitDeferTarget = [&](CGNode *node, bool /*isSuccessor*/) { + if (!updateNodes.contains(node)) + updatePointsTo(node); + return true; + }; + for (unsigned Idx = 0; Idx < updateNodes.size(); ++Idx) + updateNodes[Idx]->visitDefers(visitDeferTarget); } void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { @@ -382,10 +817,14 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { Changed = false; for (CGNode *Node : Nodes) { - // Propagate the state to all successor nodes. + // Propagate the state to all pointsTo nodes. It would be sufficient to + // only follow proper pointsTo edges, since this loop also follows defer + // edges, but this may converge faster. if (Node->pointsTo) { Changed |= Node->pointsTo->mergeEscapeState(Node->State); } + // Note: Propagating along defer edges may be interesting from an SSA + // standpoint, but it is entirely irrelevant alias analysis. for (CGNode *Def : Node->defersTo) { Changed |= Def->mergeEscapeState(Node->State); } @@ -394,17 +833,12 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() { } void EscapeAnalysis::ConnectionGraph::computeUsePoints() { +#ifndef NDEBUG + for (CGNode *Nd : Nodes) + assert(Nd->UsePoints.empty() && "premature use point computation"); +#endif // First scan the whole function and add relevant instructions as use-points. for (auto &BB : *F) { - for (SILArgument *BBArg : BB.getArguments()) { - /// In addition to releasing instructions (see below) we also add block - /// arguments as use points. In case of loops, block arguments can - /// "extend" the liferange of a reference in upward direction. - if (CGNode *ArgNode = lookupNode(BBArg)) { - addUsePoint(ArgNode, BBArg); - } - } - for (auto &I : BB) { switch (I.getKind()) { #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ @@ -419,14 +853,13 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { /// liferange. And that must be a releasing instruction. int ValueIdx = -1; for (const Operand &Op : I.getAllOperands()) { - ValueBase *OpV = Op.get(); - if (CGNode *OpNd = lookupNode(skipProjections(OpV))) { - if (ValueIdx < 0) { - ValueIdx = addUsePoint(OpNd, &I); - } else { - OpNd->setUsePointBit(ValueIdx); - } - } + CGNode *content = getValueContent(Op.get()); + if (!content) + continue; + if (ValueIdx < 0) + ValueIdx = addUsePoint(content, &I); + else + content->setUsePointBit(ValueIdx); } break; } @@ -440,88 +873,212 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() { bool Changed = false; do { Changed = false; - for (CGNode *Node : Nodes) { - // Propagate the bits to all successor nodes. - if (Node->pointsTo) { + // Propagate the bits to pointsTo. A release of a node may also release + // any content pointed to be the node. + if (Node->pointsTo) Changed |= Node->pointsTo->mergeUsePoints(Node); - } - for (CGNode *Def : Node->defersTo) { - Changed |= Def->mergeUsePoints(Node); - } } } while (Changed); } +CGNode *EscapeAnalysis::ConnectionGraph::createContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + CGNode *newContent = + allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly); + initializePointsToEdge(addrNode, newContent); + return newContent; +} + +CGNode *EscapeAnalysis::ConnectionGraph::getOrCreateContentNode( + CGNode *addrNode, bool isInterior, bool hasReferenceOnly) { + if (CGNode *content = addrNode->getContentNodeOrNull()) { + content->mergeFlags(isInterior, hasReferenceOnly); + return content; + } + CGNode *content = createContentNode(addrNode, isInterior, hasReferenceOnly); + // getValueContent may be called after the graph is built and escape states + // are propagated. Keep the escape state and use points consistent here. + content->mergeEscapeState(addrNode->State); + content->mergeUsePoints(addrNode); + return content; +} + +// Create a content node for merging based on an address node in the destination +// graph and a content node in the source graph. +CGNode * +EscapeAnalysis::ConnectionGraph::createMergedContent(CGNode *destAddrNode, + CGNode *srcContent) { + // destAddrNode may itself be a content node, so its value may be null. Since + // we don't have the original pointer value, build a new content node based + // on the source content. + CGNode *mergedContent = createContentNode( + destAddrNode, srcContent->isInterior(), srcContent->hasReferenceOnly()); + return mergedContent; +} + +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateAddressContent(SILValue addrVal, + CGNode *addrNode) { + assert(addrVal->getType().isAddress()); + + bool contentHasReferenceOnly = + EA->hasReferenceOnly(addrVal->getType().getObjectType(), *F); + // Address content is never an interior node (only reference content can + // be an interior node). + return getOrCreateContentNode(addrNode, false, contentHasReferenceOnly); +} + +// refVal is allowed to be invalid so we can model escaping content for +// secondary deinitializers of released objects. +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateReferenceContent(SILValue refVal, + CGNode *refNode) { + // The object node created here points to internal fields. It neither has + // indirect pointsTo nor reference-only pointsTo. + CGNode *objNode = getOrCreateContentNode(refNode, true, false); + if (!objNode->isInterior()) + return objNode; + + // Determine whether the object that refVal refers to only contains + // references. + bool contentHasReferenceOnly = false; + if (refVal) { + SILType refType = refVal->getType(); + if (auto *C = refType.getClassOrBoundGenericClass()) { + PointerKind aggregateKind = NoPointer; + for (auto *field : C->getStoredProperties()) { + SILType fieldType = refType + .getFieldType(field, F->getModule(), + F->getTypeExpansionContext()) + .getObjectType(); + PointerKind fieldKind = EA->findCachedPointerKind(fieldType, *F); + if (fieldKind > aggregateKind) + aggregateKind = fieldKind; + } + contentHasReferenceOnly = canOnlyContainReferences(aggregateKind); + } + } + getOrCreateContentNode(objNode, false, contentHasReferenceOnly); + return objNode; +} + +CGNode * +EscapeAnalysis::ConnectionGraph::getOrCreateUnknownContent(CGNode *addrNode) { + // We don't know if addrVal has been cast from a reference or raw + // pointer. More importantly, we don't know what memory contents it may + // point to. There's no need to consider it an "interior" node initially. If + // it's ever merged with another interior node (from ref_element_addr), then + // it will conservatively take on the interior flag at that time. + return getOrCreateContentNode(addrNode, false, false); +} + +// If ptrVal is itself mapped to a node, then this must return a non-null +// contentnode. Otherwise, setEscapesGlobal won't be able to represent escaping +// memory. +// +// This may be called after the graph is built and all escape states and use +// points are propagate. If a new content node is created, update its state +// on-the-fly. +EscapeAnalysis::CGNode * +EscapeAnalysis::ConnectionGraph::getValueContent(SILValue ptrVal) { + CGNode *addrNode = getNode(ptrVal); + if (!addrNode) + return nullptr; + + // Create content based on the derived pointer. If the base pointer contains + // other types of references, then the content node will be merged when those + // references are accessed. If the other references types are never accessed + // in this function, then they are ignored. + if (ptrVal->getType().isAddress()) + return getOrCreateAddressContent(ptrVal, addrNode); + + if (addrNode->hasReferenceOnly()) + return getOrCreateReferenceContent(ptrVal, addrNode); + + // The pointer value may contain raw pointers. + return getOrCreateUnknownContent(addrNode); +} + +CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() { + if (!ReturnNode) { + SILType resultTy = + F->mapTypeIntoContext(F->getConventions().getSILResultType()); + bool hasReferenceOnly = EA->hasReferenceOnly(resultTy, *F); + ReturnNode = allocNode(nullptr, NodeType::Return, false, hasReferenceOnly); + } + return ReturnNode; +} + bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, CGNodeMap &Mapping) { // The main point of the merging algorithm is to map each content node in the // source graph to a content node in this (destination) graph. This may - // require to create new nodes or to merge existing nodes in this graph. + // require creating new nodes or merging existing nodes in this graph. // First step: replicate the points-to edges and the content nodes of the // source graph in this graph. bool Changed = false; - bool NodesMerged; - do { - NodesMerged = false; - for (unsigned Idx = 0; Idx < Mapping.getMappedNodes().size(); ++Idx) { - CGNode *SourceNd = Mapping.getMappedNodes()[Idx]; - CGNode *DestNd = Mapping.get(SourceNd); - assert(DestNd); - - if (SourceNd->getEscapeState() >= EscapeState::Global) { - // We don't need to merge the source subgraph of nodes which have the - // global escaping state set. - // Just set global escaping in the caller node and that's it. - Changed |= DestNd->mergeEscapeState(EscapeState::Global); + for (unsigned Idx = 0; Idx < Mapping.getMappedNodes().size(); ++Idx) { + CGNode *SourceNd = Mapping.getMappedNodes()[Idx]; + CGNode *DestNd = Mapping.get(SourceNd); + assert(DestNd); + + if (SourceNd->getEscapeState() >= EscapeState::Global) { + // We don't need to merge the source subgraph of nodes which have the + // global escaping state set. + // Just set global escaping in the caller node and that's it. + Changed |= DestNd->mergeEscapeState(EscapeState::Global); + // If DestNd is an interior node, its content still needs to be created. + if (!DestNd->isInterior() || DestNd->pointsTo) continue; - } + } - CGNode *SourcePT = SourceNd->pointsTo; - if (!SourcePT) - continue; + CGNode *SourcePT = SourceNd->pointsTo; + if (!SourcePT) + continue; - CGNode *MappedDestPT = Mapping.get(SourcePT); - if (!DestNd->pointsTo) { - // The following getContentNode() will create a new content node. + CGNode *MappedDestPT = Mapping.get(SourcePT); + CGNode *DestPT = DestNd->pointsTo; + if (!MappedDestPT) { + if (!DestPT) { + DestPT = createMergedContent(DestNd, SourcePT); Changed = true; } - CGNode *DestPT = getContentNode(DestNd); - if (MappedDestPT) { - // We already found the destination node through another path. - if (DestPT != MappedDestPT) { - // There are two content nodes in this graph which map to the same - // content node in the source graph -> we have to merge them. - scheduleToMerge(DestPT, MappedDestPT); - mergeAllScheduledNodes(); - Changed = true; - NodesMerged = true; - } - assert(SourcePT->isInWorkList); - } else { - // It's the first time we see the destination node, so we add it to the - // mapping. - Mapping.add(SourcePT, DestPT); - } + // This is the first time the dest node is seen; just add the mapping. + Mapping.add(SourcePT, DestPT); + continue; } - } while (NodesMerged); + if (DestPT == MappedDestPT) + continue; - clearWorkListFlags(Mapping.getMappedNodes()); + // We already found the destination node through another path. + assert(Mapping.getMappedNodes().contains(SourcePT)); + Changed = true; + if (!DestPT) { + initializePointsToEdge(DestNd, MappedDestPT); + continue; + } + // There are two content nodes in this graph which map to the same + // content node in the source graph -> we have to merge them. + // Defer merging the nodes until all mapped nodes are created so that the + // graph is structurally valid before merging. + scheduleToMerge(DestPT, MappedDestPT); + } + mergeAllScheduledNodes(); + Mapping.getMappedNodes().reset(); // Make way for a different worklist. // Second step: add the source graph's defer edges to this graph. - llvm::SmallVector WorkList; - for (CGNode *SourceNd : Mapping.getMappedNodes()) { - assert(WorkList.empty()); - WorkList.push_back(SourceNd); - SourceNd->isInWorkList = true; + for (CGNode *SourceNd : Mapping.getMappedNodes().nodeVector) { + CGNodeWorklist Worklist(SourceGraph); + Worklist.push(SourceNd); CGNode *DestFrom = Mapping.get(SourceNd); assert(DestFrom && "node should have been merged to the graph"); // Collect all nodes which are reachable from the SourceNd via a path // which only contains defer-edges. - for (unsigned Idx = 0; Idx < WorkList.size(); ++Idx) { - CGNode *SourceReachable = WorkList[Idx]; + for (unsigned Idx = 0; Idx < Worklist.size(); ++Idx) { + CGNode *SourceReachable = Worklist[Idx]; CGNode *DestReachable = Mapping.get(SourceReachable); // Create the edge in this graph. Note: this may trigger merging of // content nodes. @@ -533,16 +1090,9 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, // node of the defer-edge to escaping. Changed |= DestFrom->mergeEscapeState(EscapeState::Global); } - - for (auto *Deferred : SourceReachable->defersTo) { - if (!Deferred->isInWorkList) { - WorkList.push_back(Deferred); - Deferred->isInWorkList = true; - } - } + for (auto *Deferred : SourceReachable->defersTo) + Worklist.tryPush(Deferred); } - clearWorkListFlags(WorkList); - WorkList.clear(); } return Changed; } @@ -551,11 +1101,10 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph, /// somehow refer to the Node's value. /// Use-points are only values which are relevant for lifeness computation, /// e.g. release or apply instructions. -bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, +bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint, CGNode *Node) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); - UsePoint = UsePoint->getRepresentativeSILNodeInObject(); auto Iter = UsePoints.find(UsePoint); if (Iter == UsePoints.end()) return false; @@ -565,8 +1114,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint, return Node->UsePoints.test(Idx); } -void EscapeAnalysis::ConnectionGraph:: -getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { +void EscapeAnalysis::ConnectionGraph::getUsePoints( + CGNode *Node, llvm::SmallVectorImpl &UsePoints) { assert(Node->getEscapeState() < EscapeState::Global && "Use points are only valid for non-escaping nodes"); for (int Idx = Node->UsePoints.find_first(); Idx >= 0; @@ -575,32 +1124,72 @@ getUsePoints(CGNode *Node, llvm::SmallVectorImpl &UsePoints) { } } -bool EscapeAnalysis::ConnectionGraph::isReachable(CGNode *From, CGNode *To) { - // See if we can reach the From-node by transitively visiting the - // predecessor nodes of the To-node. - // Usually nodes have few predecessor nodes and the graph depth is small. - // So this should be fast. - llvm::SmallVector WorkList; - WorkList.push_back(From); - From->isInWorkList = true; - for (unsigned Idx = 0; Idx < WorkList.size(); ++Idx) { - CGNode *Reachable = WorkList[Idx]; - if (Reachable == To) { - clearWorkListFlags(WorkList); - return true; +// Traverse backward from startNode and return true if \p visitor did not halt +// traversal.. +// +// The graph may have cycles. +template +bool EscapeAnalysis::ConnectionGraph::backwardTraverse( + CGNode *startNode, CGPredVisitor &&visitor) { + CGNodeWorklist worklist(this); + worklist.push(startNode); + + for (unsigned idx = 0; idx < worklist.size(); ++idx) { + CGNode *reachingNode = worklist[idx]; + + for (Predecessor pred : reachingNode->Preds) { + switch (visitor(pred)) { + case Traversal::Follow: { + CGNode *predNode = pred.getPredNode(); + worklist.tryPush(predNode); + break; + } + case Traversal::Backtrack: + break; + case Traversal::Halt: + return false; + } } - for (Predecessor Pred : Reachable->Preds) { - CGNode *PredNode = Pred.getPointer(); - if (!PredNode->isInWorkList) { - PredNode->isInWorkList = true; - WorkList.push_back(PredNode); + } + return true; +} + +// Traverse forward from startNode, following defer edges and return true if \p +// visitor did not halt traversal. +// +// The graph may have cycles. +template +bool EscapeAnalysis::ConnectionGraph::forwardTraverseDefer( + CGNode *startNode, CGNodeVisitor &&visitor) { + CGNodeWorklist worklist(this); + worklist.push(startNode); + + for (unsigned idx = 0; idx < worklist.size(); ++idx) { + CGNode *reachableNode = worklist[idx]; + + for (CGNode *deferNode : reachableNode->defersTo) { + switch (visitor(deferNode)) { + case Traversal::Follow: + worklist.tryPush(deferNode); + break; + case Traversal::Backtrack: + break; + case Traversal::Halt: + return false; } } } - clearWorkListFlags(WorkList); - return false; + return true; } +void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { + CGNode *node = Values2Nodes.lookup(V); + if (!node) + return; + Values2Nodes.erase(V); + if (node->mappedValue == V) + node->mappedValue = nullptr; +} //===----------------------------------------------------------------------===// // Dumping, Viewing and Verification @@ -612,10 +1201,7 @@ bool EscapeAnalysis::ConnectionGraph::isReachable(CGNode *From, CGNode *To) { /// This makes iterating over the edges easier. struct CGForDotView { - enum EdgeTypes { - PointsTo, - Deferred - }; + enum EdgeTypes { PointsTo, Reference, Deferred }; struct Node { EscapeAnalysis::CGNode *OrigNode; @@ -667,7 +1253,10 @@ CGForDotView::CGForDotView(const EscapeAnalysis::ConnectionGraph *CG) : Nd.OrigNode = OrigNode; if (auto *PT = OrigNode->getPointsToEdge()) { Nd.Children.push_back(Orig2Node[PT]); - Nd.ChildrenTypes.push_back(PointsTo); + if (OrigNode->hasReferenceOnly()) + Nd.ChildrenTypes.push_back(Reference); + else + Nd.ChildrenTypes.push_back(PointsTo); } for (auto *Def : OrigNode->defersTo) { Nd.Children.push_back(Orig2Node[Def]); @@ -676,41 +1265,41 @@ CGForDotView::CGForDotView(const EscapeAnalysis::ConnectionGraph *CG) : } } +void CGNode::RepValue::print( + llvm::raw_ostream &stream, + const llvm::DenseMap &instToIDMap) const { + if (auto v = getValue()) + stream << '%' << instToIDMap.lookup(v); + else + stream << (isReturn() ? "return" : "deleted"); + if (depth > 0) + stream << '.' << depth; +} + std::string CGForDotView::getNodeLabel(const Node *Node) const { std::string Label; llvm::raw_string_ostream O(Label); - if (ValueBase *V = Node->OrigNode->V) - O << '%' << InstToIDMap.lookup(V) << '\n'; - - switch (Node->OrigNode->Type) { - case swift::EscapeAnalysis::NodeType::Content: - O << "content"; - break; - case swift::EscapeAnalysis::NodeType::Return: - O << "return"; - break; - default: { - std::string Inst; - llvm::raw_string_ostream OI(Inst); - SILValue(Node->OrigNode->V)->print(OI); - size_t start = Inst.find(" = "); - if (start != std::string::npos) { - start += 3; - } else { - start = 2; - } - O << Inst.substr(start, 20); - break; + Node->OrigNode->getRepValue().print(O, InstToIDMap); + O << '\n'; + if (Node->OrigNode->mappedValue) { + std::string Inst; + llvm::raw_string_ostream OI(Inst); + SILValue(Node->OrigNode->mappedValue)->print(OI); + size_t start = Inst.find(" = "); + if (start != std::string::npos) { + start += 3; + } else { + start = 2; } + O << Inst.substr(start, 20); + O << '\n'; } if (!Node->OrigNode->matchPointToOfDefers()) { O << "\nPT mismatch: "; - if (Node->OrigNode->pointsTo) { - if (ValueBase *V = Node->OrigNode->pointsTo->V) - O << '%' << Node->Graph->InstToIDMap[V]; - } else { + if (Node->OrigNode->pointsTo) + Node->OrigNode->pointsTo->getRepValue().print(O, InstToIDMap); + else O << "null"; - } } O.flush(); return Label; @@ -720,32 +1309,36 @@ std::string CGForDotView::getNodeAttributes(const Node *Node) const { auto *Orig = Node->OrigNode; std::string attr; switch (Orig->Type) { - case swift::EscapeAnalysis::NodeType::Content: - attr = "style=\"rounded\""; - break; - case swift::EscapeAnalysis::NodeType::Argument: - case swift::EscapeAnalysis::NodeType::Return: - attr = "style=\"bold\""; - break; - default: - break; + case EscapeAnalysis::NodeType::Content: + attr = "style=\"rounded"; + if (Orig->isInterior()) { + attr += ",filled"; + } + attr += "\""; + break; + case EscapeAnalysis::NodeType::Argument: + case EscapeAnalysis::NodeType::Return: + attr = "style=\"bold\""; + break; + default: + break; } - if (Orig->getEscapeState() != swift::EscapeAnalysis::EscapeState::None && - !attr.empty()) + if (Orig->getEscapeState() != EscapeAnalysis::EscapeState::None + && !attr.empty()) attr += ','; switch (Orig->getEscapeState()) { - case swift::EscapeAnalysis::EscapeState::None: - break; - case swift::EscapeAnalysis::EscapeState::Return: - attr += "color=\"green\""; - break; - case swift::EscapeAnalysis::EscapeState::Arguments: - attr += "color=\"blue\""; - break; - case swift::EscapeAnalysis::EscapeState::Global: - attr += "color=\"red\""; - break; + case EscapeAnalysis::EscapeState::None: + break; + case EscapeAnalysis::EscapeState::Return: + attr += "color=\"green\""; + break; + case EscapeAnalysis::EscapeState::Arguments: + attr += "color=\"blue\""; + break; + case EscapeAnalysis::EscapeState::Global: + attr += "color=\"red\""; + break; } return attr; } @@ -811,8 +1404,12 @@ namespace llvm { const CGForDotView *Graph) { unsigned ChildIdx = I - Node->Children.begin(); switch (Node->ChildrenTypes[ChildIdx]) { - case CGForDotView::PointsTo: return ""; - case CGForDotView::Deferred: return "color=\"gray\""; + case CGForDotView::PointsTo: + return ""; + case CGForDotView::Reference: + return "color=\"green\""; + case CGForDotView::Deferred: + return "color=\"gray\""; } llvm_unreachable("Unhandled CGForDotView in switch."); @@ -830,12 +1427,29 @@ void EscapeAnalysis::ConnectionGraph::viewCG() const { #endif } +void EscapeAnalysis::ConnectionGraph::dumpCG() const { + /// When asserts are disabled, this should be a NoOp. +#ifndef NDEBUG + CGForDotView CGDot(this); + llvm::WriteGraph(&CGDot, "connection-graph"); +#endif +} + void EscapeAnalysis::CGNode::dump() const { llvm::errs() << getTypeStr(); - if (V) - llvm::errs() << ": " << *V; + if (isInterior()) + llvm::errs() << " [int]"; + if (hasReferenceOnly()) + llvm::errs() << " [ref]"; + + auto rep = getRepValue(); + if (rep.depth > 0) + llvm::errs() << " ." << rep.depth; + llvm::errs() << ": "; + if (auto v = rep.getValue()) + llvm::errs() << ": " << v; else - llvm::errs() << '\n'; + llvm::errs() << (rep.isReturn() ? "return" : "deleted") << '\n'; if (mergeTo) { llvm::errs() << " -> merged to "; @@ -867,48 +1481,40 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { InstToIDMap[nullptr] = (unsigned)-1; F->numberValues(InstToIDMap); - // Assign consecutive subindices for nodes which map to the same value. - llvm::DenseMap NumSubindicesPerValue; - llvm::DenseMap Node2Subindex; - - // Sort by SILValue ID+Subindex. To make the output somehow consistent with + // Sort by SILValue ID+depth. To make the output somehow consistent with // the output of the function's SIL. auto sortNodes = [&](llvm::SmallVectorImpl &Nodes) { std::sort(Nodes.begin(), Nodes.end(), - [&](CGNode *Nd1, CGNode *Nd2) -> bool { - unsigned VIdx1 = InstToIDMap[Nd1->V]; - unsigned VIdx2 = InstToIDMap[Nd2->V]; - if (VIdx1 != VIdx2) - return VIdx1 < VIdx2; - return Node2Subindex[Nd1] < Node2Subindex[Nd2]; - }); - }; - - auto NodeStr = [&](CGNode *Nd) -> std::string { - std::string Str; - if (Nd->V) { - llvm::raw_string_ostream OS(Str); - OS << '%' << InstToIDMap[Nd->V]; - unsigned Idx = Node2Subindex[Nd]; - if (Idx != 0) - OS << '.' << Idx; - OS.flush(); - } - return Str; + [&](CGNode *Nd1, CGNode *Nd2) -> bool { + auto rep1 = Nd1->getRepValue(); + auto rep2 = Nd2->getRepValue(); + unsigned VIdx1 = -1; + if (auto v = rep1.getValue()) + VIdx1 = InstToIDMap[v]; + unsigned VIdx2 = -1; + if (auto v = rep2.getValue()) + VIdx2 = InstToIDMap[v]; + if (VIdx1 != VIdx2) + return VIdx1 < VIdx2; + return rep1.depth < rep2.depth; + }); }; llvm::SmallVector SortedNodes; for (CGNode *Nd : Nodes) { - if (!Nd->isMerged) { - unsigned &Idx = NumSubindicesPerValue[Nd->V]; - Node2Subindex[Nd] = Idx++; + if (!Nd->isMerged) SortedNodes.push_back(Nd); - } } sortNodes(SortedNodes); for (CGNode *Nd : SortedNodes) { - OS << " " << Nd->getTypeStr() << ' ' << NodeStr(Nd) << " Esc: "; + OS << " " << Nd->getTypeStr() << ' '; + if (Nd->isInterior()) + OS << "[int] "; + if (Nd->hasReferenceOnly()) + OS << "[ref] "; + Nd->getRepValue().print(OS, InstToIDMap); + OS << " Esc: "; switch (Nd->getEscapeState()) { case EscapeState::None: { const char *Separator = ""; @@ -933,13 +1539,16 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { OS << ", Succ: "; const char *Separator = ""; if (CGNode *PT = Nd->getPointsToEdge()) { - OS << '(' << NodeStr(PT) << ')'; + OS << '('; + PT->getRepValue().print(OS, InstToIDMap); + OS << ')'; Separator = ", "; } llvm::SmallVector SortedDefers = Nd->defersTo; sortNodes(SortedDefers); for (CGNode *Def : SortedDefers) { - OS << Separator << NodeStr(Def); + OS << Separator; + Def->getRepValue().print(OS, InstToIDMap); Separator = ", "; } OS << '\n'; @@ -948,20 +1557,66 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const { #endif } +/// Checks an invariant of the connection graph: The points-to nodes of +/// the defer-successors must match with the points-to of this node. +bool CGNode::matchPointToOfDefers(bool allowMerge) const { + auto redirect = [allowMerge](CGNode *node) { + return (allowMerge && node) ? node->getMergeTarget() : node; + }; + for (CGNode *Def : defersTo) { + if (redirect(pointsTo) != redirect(Def->pointsTo)) + return false; + } + /// A defer-path in the graph must not end without the specified points-to + /// node. + if (pointsTo && !pointsToIsEdge && defersTo.empty()) + return false; + return true; +} + void EscapeAnalysis::ConnectionGraph::verify() const { #ifndef NDEBUG + // Invalidating EscapeAnalysis clears the connection graph. + if (isEmpty()) + return; + verifyStructure(); - // Check graph invariance 4) - for (CGNode *Nd : Nodes) { - assert(Nd->matchPointToOfDefers()); + // Verify that all pointer nodes are still mapped, otherwise the process of + // merging nodes may have lost information. + for (SILBasicBlock &BB : *F) { + for (auto &I : BB) { + if (isNonWritableMemoryAddress(&I)) + continue; + + if (auto ai = dyn_cast(&I)) { + if (EA->canOptimizeArrayUninitializedCall(ai, this).isValid()) + continue; + } + for (auto result : I.getResults()) { + if (EA->getPointerBase(result)) + continue; + + if (!EA->isPointer(result)) + continue; + + if (!Values2Nodes.lookup(result)) { + llvm::dbgs() << "No CG mapping for "; + result->dumpInContext(); + llvm::dbgs() << " in:\n"; + F->dump(); + llvm_unreachable("Missing escape connection graph mapping"); + } + } + } } #endif } -void EscapeAnalysis::ConnectionGraph::verifyStructure() const { +void EscapeAnalysis::ConnectionGraph::verifyStructure(bool allowMerge) const { #ifndef NDEBUG for (CGNode *Nd : Nodes) { + // Verify the graph structure... if (Nd->isMerged) { assert(Nd->mergeTo); assert(!Nd->pointsTo); @@ -972,11 +1627,11 @@ void EscapeAnalysis::ConnectionGraph::verifyStructure() const { } // Check if predecessor and successor edges are linked correctly. for (Predecessor Pred : Nd->Preds) { - CGNode *PredNode = Pred.getPointer(); - if (Pred.getInt() == EdgeType::Defer) { + CGNode *PredNode = Pred.getPredNode(); + if (Pred.is(EdgeType::Defer)) { assert(PredNode->findDeferred(Nd) != PredNode->defersTo.end()); } else { - assert(Pred.getInt() == EdgeType::PointsTo); + assert(Pred.is(EdgeType::PointsTo)); assert(PredNode->getPointsToEdge() == Nd); } } @@ -988,6 +1643,21 @@ void EscapeAnalysis::ConnectionGraph::verifyStructure() const { assert(PT->Type == NodeType::Content); assert(PT->findPred(Predecessor(Nd, EdgeType::PointsTo)) != PT->Preds.end()); } + if (Nd->isInterior()) + assert(Nd->pointsTo && "Interior content node requires a pointsTo node"); + + // ConnectionGraph invariant #4: For any node N, all paths starting at N + // which consist of only defer-edges and a single trailing points-to edge + // must lead to the same + assert(Nd->matchPointToOfDefers(allowMerge)); + + // Verify the node to value mapping... + if (Nd->mappedValue && !(allowMerge && Nd->isMerged)) { + assert(Nd == Values2Nodes.lookup(Nd->mappedValue)); + assert(EA->isPointer(Nd->mappedValue)); + // Nodes must always be mapped from the pointer root value. + assert(Nd->mappedValue == EA->getPointerRoot(Nd->mappedValue)); + } } #endif } @@ -1019,72 +1689,6 @@ static bool linkBBArgs(SILBasicBlock *BB) { return true; } -/// Returns true if the type \p Ty is a reference or may transitively contains -/// a reference, i.e. if it is a "pointer" type. -static bool mayContainReference(SILType Ty, const SILFunction &F) { - // Opaque types may contain a reference. Speculatively track them too. - // - // 1. It may be possible to optimize opaque values based on known mutation - // points. - // - // 2. A specialized function may call a generic function passing a conrete - // reference type via incomplete specialization. - // - // 3. A generic function may call a specialized function taking a concrete - // reference type via devirtualization. - if (Ty.isAddressOnly(F)) - return true; - - if (Ty.hasReferenceSemantics()) - return true; - - auto &Mod = F.getModule(); - - if (Ty.getASTType() == Mod.getASTContext().TheRawPointerType) - return true; - - if (auto *Str = Ty.getStructOrBoundGenericStruct()) { - for (auto *Field : Str->getStoredProperties()) { - if (mayContainReference(Ty.getFieldType(Field, Mod), F)) - return true; - } - return false; - } - if (auto TT = Ty.getAs()) { - for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) { - if (mayContainReference(Ty.getTupleElementType(i), F)) - return true; - } - return false; - } - if (auto En = Ty.getEnumOrBoundGenericEnum()) { - for (auto *ElemDecl : En->getAllElements()) { - if (ElemDecl->hasAssociatedValues() - && mayContainReference(Ty.getEnumElementType(ElemDecl, Mod), F)) - return true; - } - return false; - } - return false; -} - -bool EscapeAnalysis::isPointer(ValueBase *V) { - auto *F = V->getFunction(); - - // The function can be null, e.g. if V is an undef. - if (!F) - return false; - - SILType Ty = V->getType(); - auto Iter = isPointerCache.find(Ty); - if (Iter != isPointerCache.end()) - return Iter->second; - - bool IP = (Ty.isAddress() || mayContainReference(Ty, *F)); - isPointerCache[Ty] = IP; - return IP; -} - void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1126,24 +1730,23 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, // Create defer-edges from the block arguments to it's values in the // predecessor's terminator instructions. for (SILArgument *BBArg : BB.getArguments()) { - CGNode *ArgNode = ConGraph->getNode(BBArg, this); - if (!ArgNode) - continue; - llvm::SmallVector Incoming; if (!BBArg->getSingleTerminatorOperands(Incoming)) { // We don't know where the block argument comes from -> treat it // conservatively. - ConGraph->setEscapesGlobal(ArgNode); + ConGraph->setEscapesGlobal(BBArg); continue; } + CGNode *ArgNode = ConGraph->getNode(BBArg); + if (!ArgNode) + continue; for (SILValue Src : Incoming) { - CGNode *SrcArg = ConGraph->getNode(Src, this); + CGNode *SrcArg = ConGraph->getNode(Src); if (SrcArg) { ArgNode = ConGraph->defer(ArgNode, SrcArg); } else { - ConGraph->setEscapesGlobal(ArgNode); + ConGraph->setEscapesGlobal(BBArg); break; } } @@ -1153,15 +1756,6 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo, << FInfo->Graph.F->getName() << '\n'); } -/// Returns true if all uses of \p I are tuple_extract instructions. -static bool onlyUsedInTupleExtract(SILValue V) { - for (Operand *Use : getNonDebugUses(V)) { - if (!isa(Use->getUser())) - return false; - } - return true; -} - bool EscapeAnalysis::buildConnectionGraphForCallees( SILInstruction *Caller, CalleeList Callees, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { @@ -1218,6 +1812,84 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor( RecursionDepth); } +EscapeAnalysis::ArrayUninitCall +EscapeAnalysis::canOptimizeArrayUninitializedCall( + ApplyInst *ai, const ConnectionGraph *conGraph) { + ArrayUninitCall call; + // This must be an exact match so we don't accidentally optimize + // "array.uninitialized_intrinsic". + if (!ArraySemanticsCall(ai, "array.uninitialized", false)) + return call; + + // Check if the result is used in the usual way: extracting the + // array and the element pointer with tuple_extract. + // + // Do not ignore any uses, even redundant tuple_extract, because all apply + // uses must be mapped to ConnectionGraph nodes by the client of this API. + for (Operand *use : getNonDebugUses(ai)) { + if (auto *tei = dyn_cast(use->getUser())) { + if (tei->getFieldNo() == 0 && !call.arrayStruct) { + call.arrayStruct = tei; + continue; + } + if (tei->getFieldNo() == 1 && !call.arrayElementPtr) { + call.arrayElementPtr = tei; + continue; + } + } + // If there are any other uses, such as a release_value, erase the previous + // call info and bail out. + call.arrayStruct = nullptr; + call.arrayElementPtr = nullptr; + break; + } + // An "array.uninitialized" call may have a first argument which is the + // allocated array buffer. Make sure the call's argument is recognized by + // EscapeAnalysis as a pointer, otherwise createArrayUninitializedSubgraph + // won't be able to map the result nodes onto it. There is a variant of + // @_semantics("array.uninitialized") that does not take the storage as input, + // so it will effectively bail out here. + if (isPointer(ai->getArgument(0))) + call.arrayStorageRef = ai->getArgument(0); + return call; +} + +bool EscapeAnalysis::canOptimizeArrayUninitializedResult( + TupleExtractInst *tei) { + ApplyInst *ai = dyn_cast(tei->getOperand()); + if (!ai) + return false; + + auto *conGraph = getConnectionGraph(ai->getFunction()); + return canOptimizeArrayUninitializedCall(ai, conGraph).isValid(); +} + +// Handle @_semantics("array.uninitialized") +// +// This call is analagous to a 'struct(storageRef)' instruction--we want a defer +// edge from the returned Array struct to the storage Reference that it +// contains. +// +// The returned unsafe pointer is handled simply by mapping the pointer value +// onto the object node that the storage argument points to. +void EscapeAnalysis::createArrayUninitializedSubgraph( + ArrayUninitCall call, ConnectionGraph *conGraph) { + CGNode *arrayStructNode = conGraph->getNode(call.arrayStruct); + assert(arrayStructNode && "Array struct must have a node"); + + CGNode *arrayRefNode = conGraph->getNode(call.arrayStorageRef); + assert(arrayRefNode && "canOptimizeArrayUninitializedCall checks isPointer"); + // If the arrayRefNode != null then arrayObjNode must be valid. + CGNode *arrayObjNode = conGraph->getValueContent(call.arrayStorageRef); + + // The reference argument is effectively stored inside the returned + // array struct. This is like struct(arrayRefNode). + conGraph->defer(arrayStructNode, arrayRefNode); + + // Map the returned element pointer to the array object's field pointer. + conGraph->setNode(call.arrayElementPtr, arrayObjNode); +} + void EscapeAnalysis::analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, @@ -1237,73 +1909,77 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case ArrayCallKind::kMakeMutable: // These array semantics calls do not capture anything. return; - case ArrayCallKind::kArrayUninitialized: - // Check if the result is used in the usual way: extracting the - // array and the element pointer with tuple_extract. - if (onlyUsedInTupleExtract(ASC.getCallResult())) { - // array.uninitialized may have a first argument which is the - // allocated array buffer. The call is like a struct(buffer) - // instruction. - if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0), this)) { - CGNode *ArrayNode = ConGraph->getNode(ASC.getCallResult(), this); - CGNode *ArrayContent = ConGraph->getContentNode(ArrayNode); - ConGraph->defer(ArrayContent, BufferNode); - } + case ArrayCallKind::kArrayUninitialized: { + ArrayUninitCall call = canOptimizeArrayUninitializedCall( + cast(FAS.getInstruction()), ConGraph); + if (call.isValid()) { + createArrayUninitializedSubgraph(call, ConGraph); return; } break; + } case ArrayCallKind::kGetElement: - if (CGNode *AddrNode = ConGraph->getNode(ASC.getSelf(), this)) { - CGNode *DestNode = nullptr; + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + CGNode *LoadedElement = nullptr; // This is like a load from a ref_element_addr. if (ASC.hasGetElementDirectResult()) { - DestNode = ConGraph->getNode(ASC.getCallResult(), this); + LoadedElement = ConGraph->getNode(ASC.getCallResult()); } else { - CGNode *DestAddrNode = ConGraph->getNode(FAS.getArgument(0), this); - assert(DestAddrNode && "indirect result must have node"); // The content of the destination address. - DestNode = ConGraph->getContentNode(DestAddrNode); + LoadedElement = ConGraph->getValueContent(FAS.getArgument(0)); + assert(LoadedElement && "indirect result must have node"); } - if (DestNode) { - // One content node for going from the array buffer pointer to - // the element address (like ref_element_addr). - CGNode *RefElement = ConGraph->getContentNode(AddrNode); - // Another content node to actually load the element. - CGNode *ArrayContent = ConGraph->getContentNode(RefElement); - ConGraph->defer(DestNode, ArrayContent); - return; + if (LoadedElement) { + if (CGNode *arrayElementStorage = + ConGraph->getFieldContent(ArrayObjNode)) { + ConGraph->defer(arrayElementStorage, LoadedElement); + return; + } } } break; case ArrayCallKind::kGetElementAddress: - // This is like a ref_element_addr. - if (CGNode *SelfNode = ConGraph->getNode(ASC.getSelf(), this)) { - ConGraph->defer(ConGraph->getNode(ASC.getCallResult(), this), - ConGraph->getContentNode(SelfNode)); + // This is like a ref_element_addr. Both the object node and the + // returned address point to the same element storage. + if (CGNode *ArrayObjNode = ConGraph->getValueContent(ASC.getSelf())) { + CGNode *arrayElementAddress = ConGraph->getNode(ASC.getCallResult()); + ConGraph->defer(ArrayObjNode, arrayElementAddress); + return; } - return; + break; case ArrayCallKind::kWithUnsafeMutableBufferPointer: // Model this like an escape of the elements of the array and a capture // of anything captured by the closure. // Self is passed inout. - if (CGNode *AddrArrayStruct = ConGraph->getNode(ASC.getSelf(), this)) { - CGNode *ArrayStructValueNode = - ConGraph->getContentNode(AddrArrayStruct); - // One content node for going from the array buffer pointer to - // the element address (like ref_element_addr). - CGNode *RefElement = ConGraph->getContentNode(ArrayStructValueNode); - // Another content node to actually load the element. - CGNode *ArrayContent = ConGraph->getContentNode(RefElement); - ConGraph->setEscapesGlobal(ArrayContent); + if (CGNode *ArrayStructNode = + ConGraph->getValueContent(ASC.getSelf())) { // The first non indirect result is the closure. auto Args = FAS.getArgumentsWithoutIndirectResults(); - setEscapesGlobal(ConGraph, Args[0]); + ConGraph->setEscapesGlobal(Args[0]); + + // One content node for going from the array buffer pointer to + // the element address (like ref_element_addr). + CGNode *ArrayObjNode = + ConGraph->getOrCreateContentNode(ArrayStructNode, + /*isInterior*/ true, + /*hasRefOnly*/ false); + // If ArrayObjNode was already potentially merged with its pointsTo, + // then conservatively mark the whole thing as escaping. + if (!ArrayObjNode->isInterior()) { + ArrayObjNode->markEscaping(); + return; + } + // Otherwise, create the content node for the element storage. + CGNode *ArrayElementStorage = ConGraph->getOrCreateContentNode( + ArrayObjNode, /*isInterior*/ false, + /*hasRefOnly*/ true); + ArrayElementStorage->markEscaping(); return; } break; default: break; - } + } if (FAS.getReferencedFunctionOrNull() && FAS.getReferencedFunctionOrNull()->hasSemanticsAttr( @@ -1316,7 +1992,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The first not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[0]); + ConGraph->setEscapesGlobal(Args[0]); return; } @@ -1331,7 +2007,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, // from the pointer. auto Args = FAS.getArgumentsWithoutIndirectResults(); // The second not indirect result argument is the closure. - setEscapesGlobal(ConGraph, Args[1]); + ConGraph->setEscapesGlobal(Args[1]); return; } @@ -1361,8 +2037,12 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, } } - if (isProjection(I)) - return; + // If this instruction produces a single value whose pointer is represented by + // a different base pointer, then skip it. + if (auto *SVI = dyn_cast(I)) { + if (getPointerBase(SVI)) + return; + } // Instructions which return the address of non-writable memory cannot have // an effect on escaping. @@ -1373,15 +2053,12 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::AllocStackInst: case SILInstructionKind::AllocRefInst: case SILInstructionKind::AllocBoxInst: - ConGraph->getNode(cast(I), this); + ConGraph->getNode(cast(I)); return; -#define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Name##RetainInst: \ - case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::StrongRetain##Name##Inst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::DeallocStackInst: case SILInstructionKind::StrongRetainInst: @@ -1399,8 +2076,11 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::SetDeallocatingInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::ClassifyBridgeObjectInst: - case SILInstructionKind::ValueToBridgeObjectInst: - // These instructions don't have any effect on escaping. + // Early bailout: These instructions never produce a pointer value and + // have no escaping effect on their operands. + assert(!llvm::any_of(I->getResults(), [this](SILValue result) { + return isPointer(result); + })); return; #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ @@ -1408,110 +2088,159 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::ReleaseValueInst: { + // A release instruction may deallocate the pointer operand. This may + // capture anything pointed to by the released object, but not the object + // itself (because it will be a dangling pointer after deallocation). SILValue OpV = I->getOperand(0); - if (CGNode *AddrNode = ConGraph->getNode(OpV, this)) { - // A release instruction may deallocate the pointer operand. This may - // capture any content of the released object, but not the pointer to - // the object itself (because it will be a dangling pointer after - // deallocation). - CGNode *CapturedByDeinit = ConGraph->getContentNode(AddrNode); - // Get the content node for the object's properties. The object header - // itself cannot escape from the deinit. - CapturedByDeinit = ConGraph->getContentNode(CapturedByDeinit); - if (deinitIsKnownToNotCapture(OpV)) { - // Presumably this is necessary because, even though the deinit - // doesn't escape the immediate properties of this class, it may - // indirectly escape some other memory content(?) - CapturedByDeinit = ConGraph->getContentNode(CapturedByDeinit); - } - ConGraph->setEscapesGlobal(CapturedByDeinit); + CGNode *objNode = ConGraph->getValueContent(OpV); + if (!objNode) + return; + + CGNode *fieldNode = ConGraph->getFieldContent(objNode); + if (!fieldNode) { + // In the unexpected case that the object has no field content, create + // escaping unknown content. + ConGraph->getOrCreateUnknownContent(objNode)->markEscaping(); + return; + } + if (!deinitIsKnownToNotCapture(OpV)) { + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + return; + } + // This deinit is known to not directly capture it's own field content; + // however, other secondary deinitializers could still capture anything + // pointed to by references within those fields. Since secondary + // deinitializers only apply to reference-type fields, not pointer-type + // fields, the "field" content can initially be considered an indirect + // reference. Unfortunately, we can't know all possible reference types + // that may eventually be associated with 'fieldContent', so we must + // assume here that 'fieldContent2' could hold raw pointers. This is + // implied by passing in invalid SILValue. + CGNode *escapingNode = + ConGraph->getOrCreateReferenceContent(SILValue(), fieldNode); + if (CGNode *indirectFieldNode = escapingNode->getContentNodeOrNull()) + escapingNode = indirectFieldNode; + + ConGraph->getOrCreateUnknownContent(escapingNode)->markEscaping(); + return; + } + case SILInstructionKind::DestroyAddrInst: { + SILValue addressVal = I->getOperand(0); + CGNode *valueNode = ConGraph->getValueContent(addressVal); + if (!valueNode) + return; + + // The value's destructor may escape anything the value points to. + // This could be an object referenced by the value or the contents of an + // existential box. + if (CGNode *fieldNode = ConGraph->getFieldContent(valueNode)) { + ConGraph->getOrCreateUnknownContent(fieldNode)->markEscaping(); + return; } + ConGraph->getOrCreateUnknownContent(valueNode)->markEscaping(); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::LoadInst: - // We treat ref_element_addr like a load (see NodeType::Content). + case SILInstructionKind::LoadInst: { + assert(!cast(I)->getType().isAddress()); + // For loads, get the address-type operand and return the content node + // that the address directly points to. The load's address may itself come + // from a ref_element_addr, project_box or open_existential, in which + // case, the loaded content will be the field content, not the RC + // content. + auto SVI = cast(I); + if (!isPointer(SVI)) + return; + + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { + ConGraph->setNode(SVI, PointsTo); + return; + } + // A load from an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(SVI); + break; + } case SILInstructionKind::RefElementAddrInst: case SILInstructionKind::RefTailAddrInst: case SILInstructionKind::ProjectBoxInst: case SILInstructionKind::InitExistentialAddrInst: case SILInstructionKind::OpenExistentialAddrInst: { + // For projections into objects, get the non-address reference operand and + // return an interior content node that the reference points to. auto SVI = cast(I); - if (isPointer(SVI)) { - CGNode *AddrNode = ConGraph->getNode(SVI->getOperand(0), this); - if (!AddrNode) { - // A load from an address we don't handle -> be conservative. - CGNode *ValueNode = ConGraph->getNode(SVI, this); - ConGraph->setEscapesGlobal(ValueNode); - return; - } - CGNode *PointsTo = ConGraph->getContentNode(AddrNode); - // No need for a separate node for the load instruction: - // just reuse the content node. + if (CGNode *PointsTo = ConGraph->getValueContent(SVI->getOperand(0))) { ConGraph->setNode(SVI, PointsTo); + return; } + // A load or projection from an address we don't handle -> be + // conservative. + ConGraph->setEscapesGlobal(SVI); return; } case SILInstructionKind::CopyAddrInst: { // Be conservative if the dest may be the final release. if (!cast(I)->isInitializationOfDest()) { setAllEscaping(I, ConGraph); - break; + return; } // A copy_addr is like a 'store (load src) to dest'. - CGNode *SrcAddrNode = ConGraph->getNode(I->getOperand(CopyAddrInst::Src), - this); - if (!SrcAddrNode) { + SILValue srcAddr = I->getOperand(CopyAddrInst::Src); + CGNode *loadedContent = ConGraph->getValueContent(srcAddr); + if (!loadedContent) { setAllEscaping(I, ConGraph); break; } - - CGNode *LoadedValue = ConGraph->getContentNode(SrcAddrNode); - CGNode *DestAddrNode = ConGraph->getNode( - I->getOperand(CopyAddrInst::Dest), this); - if (DestAddrNode) { - // Create a defer-edge from the loaded to the stored value. - CGNode *PointsTo = ConGraph->getContentNode(DestAddrNode); - ConGraph->defer(PointsTo, LoadedValue); - } else { - // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(LoadedValue); + SILValue destAddr = I->getOperand(CopyAddrInst::Dest); + // Create a defer-edge from the store location to the loaded content. + if (CGNode *destContent = ConGraph->getValueContent(destAddr)) { + ConGraph->defer(destContent, loadedContent); + return; } + // A store to an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(srcAddr); return; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::StoreInst: - if (CGNode *ValueNode = ConGraph->getNode(I->getOperand(StoreInst::Src), - this)) { - CGNode *AddrNode = ConGraph->getNode(I->getOperand(StoreInst::Dest), - this); - if (AddrNode) { - // Create a defer-edge from the content to the stored value. - CGNode *PointsTo = ConGraph->getContentNode(AddrNode); - ConGraph->defer(PointsTo, ValueNode); - } else { - // A store to an address we don't handle -> be conservative. - ConGraph->setEscapesGlobal(ValueNode); - } + case SILInstructionKind::StoreInst: { + SILValue srcVal = I->getOperand(StoreInst::Src); + CGNode *valueNode = ConGraph->getNode(srcVal); + // If the stored value isn't tracked, ignore the store. + if (!valueNode) + return; + + // The store destination content is always one pointsTo level away from + // its address. Either the address points to a variable or argument, and + // the pointee is removed by a level of pointer indirection, or the + // address corresponds is a projection within a reference counted object + // (via ref_element_addr, project_box, or open_existential_addr) where the + // stored field content is chained one level below the RC content. + SILValue destAddr = I->getOperand(StoreInst::Dest); + if (CGNode *pointsTo = ConGraph->getValueContent(destAddr)) { + // Create a defer-edge from the content to the stored value. + ConGraph->defer(pointsTo, valueNode); + return; } + // A store to an address we don't handle -> be conservative. + ConGraph->setEscapesGlobal(srcVal); return; + } case SILInstructionKind::PartialApplyInst: { // The result of a partial_apply is a thick function which stores the // boxed partial applied arguments. We create defer-edges from the // partial_apply values to the arguments. auto PAI = cast(I); - CGNode *ResultNode = ConGraph->getNode(PAI, this); - assert(ResultNode && "thick functions must have a CG node"); - for (const Operand &Op : PAI->getAllOperands()) { - if (CGNode *ArgNode = ConGraph->getNode(Op.get(), this)) { - ResultNode = ConGraph->defer(ResultNode, ArgNode); + if (CGNode *ResultNode = ConGraph->getNode(PAI)) { + for (const Operand &Op : PAI->getAllOperands()) { + if (CGNode *ArgNode = ConGraph->getNode(Op.get())) { + ResultNode = ConGraph->defer(ResultNode, ArgNode); + } } } return; @@ -1528,73 +2257,40 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, case SILInstructionKind::EnumInst: { // Aggregate composition is like assigning the aggregate fields to the // resulting aggregate value. - auto SVI = cast(I); - CGNode *ResultNode = nullptr; - for (const Operand &Op : SVI->getAllOperands()) { - if (CGNode *FieldNode = ConGraph->getNode(Op.get(), this)) { - if (!ResultNode) { - // A small optimization to reduce the graph size: we re-use the - // first field node as result node. - ConGraph->setNode(SVI, FieldNode); - ResultNode = FieldNode; - assert(isPointer(SVI)); - } else { - ResultNode = ConGraph->defer(ResultNode, FieldNode); - } - } + auto svi = cast(I); + CGNode *resultNode = ConGraph->getNode(svi); + for (const Operand &operand : svi->getAllOperands()) { + if (CGNode *subNode = ConGraph->getNode(operand.get())) + ConGraph->defer(resultNode, subNode); } return; } case SILInstructionKind::TupleExtractInst: { // This is a tuple_extract which extracts the second result of an - // array.uninitialized call. The first result is the array itself. - // The second result (which is a pointer to the array elements) must be - // the content node of the first result. It's just like a ref_element_addr - // instruction. + // array.uninitialized call (otherwise getPointerBase should have already + // looked through it). auto *TEI = cast(I); - assert(isExtractOfArrayUninitializedPointer(TEI) + assert(canOptimizeArrayUninitializedResult(TEI) && "tuple_extract should be handled as projection"); - CGNode *ArrayNode = ConGraph->getNode(TEI->getOperand(), this); - CGNode *ArrayElements = ConGraph->getContentNode(ArrayNode); - ConGraph->setNode(TEI, ArrayElements); return; } - case SILInstructionKind::UncheckedRefCastInst: - case SILInstructionKind::ConvertFunctionInst: - case SILInstructionKind::UpcastInst: - case SILInstructionKind::InitExistentialRefInst: - case SILInstructionKind::OpenExistentialRefInst: - case SILInstructionKind::RawPointerToRefInst: - case SILInstructionKind::RefToRawPointerInst: - case SILInstructionKind::RefToBridgeObjectInst: - case SILInstructionKind::BridgeObjectToRefInst: - case SILInstructionKind::UncheckedAddrCastInst: - case SILInstructionKind::UnconditionalCheckedCastInst: - // DO NOT use LOADABLE_REF_STORAGE because unchecked references don't have - // retain/release instructions that trigger the 'default' case. -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::RefTo##Name##Inst: \ - case SILInstructionKind::Name##ToRefInst: -#include "swift/AST/ReferenceStorage.def" - // A cast is almost like a projection. - if (CGNode *OpNode = ConGraph->getNode(I->getOperand(0), this)) { - ConGraph->setNode(cast(I), OpNode); - } - break; case SILInstructionKind::UncheckedRefCastAddrInst: { auto *URCAI = cast(I); - CGNode *SrcNode = ConGraph->getNode(URCAI->getSrc(), this); - CGNode *DestNode = ConGraph->getNode(URCAI->getDest(), this); + CGNode *SrcNode = ConGraph->getNode(URCAI->getSrc()); + CGNode *DestNode = ConGraph->getNode(URCAI->getDest()); assert(SrcNode && DestNode && "must have nodes for address operands"); ConGraph->defer(DestNode, SrcNode); return; } - case SILInstructionKind::ReturnInst: - if (CGNode *ValueNd = ConGraph->getNode(cast(I)->getOperand(), - this)) { + case SILInstructionKind::ReturnInst: { + SILValue returnVal = cast(I)->getOperand(); + if (CGNode *ValueNd = ConGraph->getNode(returnVal)) { ConGraph->defer(ConGraph->getReturnNode(), ValueNd); + ConGraph->getValueContent(returnVal)->mergeEscapeState( + EscapeState::Return); } return; + } default: // We handle all other instructions conservatively. setAllEscaping(I, ConGraph); @@ -1604,18 +2300,18 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I, template void EscapeAnalysis:: analyzeSelectInst(SelectInst *SI, ConnectionGraph *ConGraph) { - if (auto *ResultNode = ConGraph->getNode(SI, this)) { + if (auto *ResultNode = ConGraph->getNode(SI)) { // Connect all case values to the result value. // Note that this does not include the first operand (the condition). for (unsigned Idx = 0, End = SI->getNumCases(); Idx < End; ++Idx) { SILValue CaseVal = SI->getCase(Idx).second; - auto *ArgNode = ConGraph->getNode(CaseVal, this); + auto *ArgNode = ConGraph->getNode(CaseVal); assert(ArgNode && "there should be an argument node if there is a result node"); ResultNode = ConGraph->defer(ResultNode, ArgNode); } // ... also including the default value. - auto *DefaultNode = ConGraph->getNode(SI->getDefaultResult(), this); + auto *DefaultNode = ConGraph->getNode(SI->getDefaultResult()); assert(DefaultNode && "there should be an argument node if there is a result node"); ConGraph->defer(ResultNode, DefaultNode); @@ -1644,8 +2340,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { } return true; } - if (auto SVI = isProjection(V)) { - V = SVI->getOperand(0); + if (auto base = getPointerBase(V)) { + V = base; continue; } return false; @@ -1655,8 +2351,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { void EscapeAnalysis::setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph) { if (auto *TAI = dyn_cast(I)) { - setEscapesGlobal(ConGraph, TAI->getNormalBB()->getArgument(0)); - setEscapesGlobal(ConGraph, TAI->getErrorBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getNormalBB()->getArgument(0)); + ConGraph->setEscapesGlobal(TAI->getErrorBB()->getArgument(0)); } // Even if the instruction does not write memory we conservatively set all // operands to escaping, because they may "escape" to the result value in @@ -1665,12 +2361,12 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I, for (const Operand &Op : I->getAllOperands()) { SILValue OpVal = Op.get(); if (!isNonWritableMemoryAddress(OpVal)) - setEscapesGlobal(ConGraph, OpVal); + ConGraph->setEscapesGlobal(OpVal); } // Even if the instruction does not write memory it could e.g. return the // address of global memory. Therefore we have to define it as escaping. for (auto result : I->getResults()) - setEscapesGlobal(ConGraph, result); + ConGraph->setEscapesGlobal(result); } void EscapeAnalysis::recompute(FunctionInfo *Initial) { @@ -1701,6 +2397,8 @@ void EscapeAnalysis::recompute(FunctionInfo *Initial) { LLVM_DEBUG(llvm::dbgs() << " create summary graph for " << FInfo->Graph.F->getName() << '\n'); + PrettyStackTraceSILFunction + callerTraceRAII("merging escape summary", FInfo->Graph.F); FInfo->Graph.propagateEscapeStates(); // Derive the summary graph of the current function. Even if the @@ -1721,11 +2419,14 @@ void EscapeAnalysis::recompute(FunctionInfo *Initial) { // Only include callers which we are actually recomputing. if (BottomUpOrder.wasRecomputedWithCurrentUpdateID(E.Caller)) { - LLVM_DEBUG(llvm::dbgs() << " merge " + PrettyStackTraceSILFunction + calleeTraceRAII("merging escape graph", FInfo->Graph.F); + PrettyStackTraceSILFunction + callerTraceRAII("...into", E.Caller->Graph.F); + LLVM_DEBUG(llvm::dbgs() << " merge " << FInfo->Graph.F->getName() << " into " << E.Caller->Graph.F->getName() << '\n'); - if (mergeCalleeGraph(E.FAS, &E.Caller->Graph, &FInfo->SummaryGraph)) { E.Caller->NeedUpdateSummaryGraph = true; @@ -1760,7 +2461,7 @@ void EscapeAnalysis::recompute(FunctionInfo *Initial) { if (BottomUpOrder.wasRecomputedWithCurrentUpdateID(FInfo)) { FInfo->Graph.computeUsePoints(); FInfo->Graph.verify(); - FInfo->SummaryGraph.verify(); + FInfo->SummaryGraph.verifyStructure(); } } } @@ -1768,7 +2469,10 @@ void EscapeAnalysis::recompute(FunctionInfo *Initial) { bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS, ConnectionGraph *CallerGraph, ConnectionGraph *CalleeGraph) { - CGNodeMap Callee2CallerMapping; + // This CGNodeMap uses an intrusive worklist to keep track of Mapped nodes + // from the CalleeGraph. Meanwhile, mergeFrom uses separate intrusive + // worklists to update nodes in the CallerGraph. + CGNodeMap Callee2CallerMapping(CalleeGraph); // First map the callee parameters to the caller arguments. SILFunction *Callee = CalleeGraph->F; @@ -1789,11 +2493,11 @@ bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS, else CallerArg = (Idx < numCallerArgs ? AS->getOperand(Idx) : SILValue()); - CGNode *CalleeNd = CalleeGraph->getNode(Callee->getArgument(Idx), this); + CGNode *CalleeNd = CalleeGraph->getNode(Callee->getArgument(Idx)); if (!CalleeNd) continue; - CGNode *CallerNd = CallerGraph->getNode(CallerArg, this); + CGNode *CallerNd = CallerGraph->getNode(CallerArg); // There can be the case that we see a callee argument as pointer but not // the caller argument. E.g. if the callee argument has a @convention(c) // function type and the caller passes a function_ref. @@ -1814,7 +2518,7 @@ bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS, } else { CallerReturnVal = cast(AS); } - CGNode *CallerRetNd = CallerGraph->getNode(CallerReturnVal, this); + CGNode *CallerRetNd = CallerGraph->getNode(CallerReturnVal); if (CallerRetNd) Callee2CallerMapping.add(RetNd, CallerRetNd); } @@ -1824,11 +2528,14 @@ bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS, bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, ConnectionGraph *Graph) { - // Make a 1-to-1 mapping of all arguments and the return value. - CGNodeMap Mapping; + // Make a 1-to-1 mapping of all arguments and the return value. This CGNodeMap + // node map uses an intrusive worklist to keep track of Mapped nodes from the + // Graph. Meanwhile, mergeFrom uses separate intrusive worklists to + // update nodes in the SummaryGraph. + CGNodeMap Mapping(Graph); for (SILArgument *Arg : Graph->F->getArguments()) { - if (CGNode *ArgNd = Graph->getNode(Arg, this)) { - Mapping.add(ArgNd, SummaryGraph->getNode(Arg, this)); + if (CGNode *ArgNd = Graph->getNode(Arg)) { + Mapping.add(ArgNd, SummaryGraph->getNode(Arg)); } } if (CGNode *RetNd = Graph->getReturnNodeOrNull()) { @@ -1838,63 +2545,70 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph, return SummaryGraph->mergeFrom(Graph, Mapping); } -bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint, - ConnectionGraph *ConGraph) { +// Return true if any content within the logical object pointed to by \p value +// escapes. +// +// Get the value's content node and check the escaping flag on all nodes within +// that object. An interior CG node points to content within the same object. +bool EscapeAnalysis::canEscapeToUsePoint(SILValue value, + SILInstruction *usePoint, + ConnectionGraph *conGraph) { - assert((FullApplySite::isa(UsePoint) || isa(UsePoint)) && - "use points are only created for calls and refcount instructions"); + assert((FullApplySite::isa(usePoint) || isa(usePoint)) + && "use points are only created for calls and refcount instructions"); - CGNode *Node = ConGraph->getNodeOrNull(V, this); - if (!Node) + CGNode *node = conGraph->getValueContent(value); + if (!node) return true; - // First check if there are escape paths which we don't explicitly see - // in the graph. - if (Node->escapesInsideFunction(isNotAliasingArgument(V))) - return true; + // Follow points-to edges and return true if the current 'node' may escape at + // 'usePoint'. + CGNodeWorklist worklist(conGraph); + while (node) { + // Merging arbitrary nodes is supported, which may lead to cycles of + // interior nodes. End the search. + if (!worklist.tryPush(node)) + break; - // No hidden escapes: check if the Node is reachable from the UsePoint. - // Check if the object itself can escape to the called function. - if (ConGraph->isUsePoint(UsePoint, Node)) - return true; + // First check if 'node' may escape in a way not represented by the + // connection graph, assuming that it may represent part of the object + // pointed to by 'value'. If 'node' happens to represent another object + // indirectly reachabe from 'value', then it cannot actually escape to this + // usePoint, so passing the original value is still conservatively correct. + if (node->valueEscapesInsideFunction(value)) + return true; - assert(isPointer(V) && "should not have a node for a non-pointer"); - - // Check if the object "content" can escape to the called function. - // This will catch cases where V is a reference and a pointer to a stored - // property escapes. - // It's also important in case of a pointer assignment, e.g. - // V = V1 - // apply(V1) - // In this case the apply is only a use-point for V1 and V1's content node. - // As V1's content node is the same as V's content node, we also make the - // check for the content node. - CGNode *ContentNode = ConGraph->getContentNode(Node); - if (ContentNode->escapesInsideFunction(false)) - return true; + // No hidden escapes; check if 'usePoint' may access memory at 'node'. + if (conGraph->isUsePoint(usePoint, node)) + return true; - if (ConGraph->isUsePoint(UsePoint, ContentNode)) - return true; + if (!node->isInterior()) + break; + // Continue to check for escaping content whenever 'content' may point to + // the same object as 'node'. + node = node->getContentNodeOrNull(); + } return false; } bool EscapeAnalysis::canEscapeTo(SILValue V, FullApplySite FAS) { // If it's not a local object we don't know anything about the value. - if (!pointsToLocalObject(V)) + if (!isUniquelyIdentified(V)) return true; auto *ConGraph = getConnectionGraph(FAS.getFunction()); return canEscapeToUsePoint(V, FAS.getInstruction(), ConGraph); } +// FIXME: remove this to avoid confusion with SILType.hasReferenceSemantics. static bool hasReferenceSemantics(SILType T) { // Exclude address types. return T.isObject() && T.hasReferenceSemantics(); } bool EscapeAnalysis::canEscapeTo(SILValue V, RefCountingInst *RI) { - // If it's not a local object we don't know anything about the value. - if (!pointsToLocalObject(V)) + // If it's not uniquely identified we don't know anything about the value. + if (!isUniquelyIdentified(V)) return true; auto *ConGraph = getConnectionGraph(RI->getFunction()); return canEscapeToUsePoint(V, RI, ConGraph); @@ -1912,29 +2626,11 @@ static SILFunction *getCommonFunction(SILValue V1, SILValue V2) { return F; } -bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) { - if (!pointsToLocalObject(V)) - return true; - - SILFunction *F = getCommonFunction(V, To); - if (!F) - return true; - auto *ConGraph = getConnectionGraph(F); - - CGNode *Node = ConGraph->getNodeOrNull(V, this); - if (!Node) - return true; - CGNode *ToNode = ConGraph->getNodeOrNull(To, this); - if (!ToNode) - return true; - return ConGraph->isReachable(Node, ToNode); -} - bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { // At least one of the values must be a non-escaping local object. - bool isLocal1 = pointsToLocalObject(V1); - bool isLocal2 = pointsToLocalObject(V2); - if (!isLocal1 && !isLocal2) + bool isUniq1 = isUniquelyIdentified(V1); + bool isUniq2 = isUniquelyIdentified(V2); + if (!isUniq1 && !isUniq2) return true; SILFunction *F = getCommonFunction(V1, V2); @@ -1942,27 +2638,26 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { return true; auto *ConGraph = getConnectionGraph(F); - CGNode *Node1 = ConGraph->getNodeOrNull(V1, this); - if (!Node1) + CGNode *Content1 = ConGraph->getValueContent(V1); + if (!Content1) return true; - CGNode *Node2 = ConGraph->getNodeOrNull(V2, this); - if (!Node2) + + CGNode *Content2 = ConGraph->getValueContent(V2); + if (!Content2) return true; // Finish the check for one value being a non-escaping local object. - if (isLocal1 && Node1->escapesInsideFunction(isNotAliasingArgument(V1))) - isLocal1 = false; + if (isUniq1 && Content1->valueEscapesInsideFunction(V1)) + isUniq1 = false; - if (isLocal2 && Node2->escapesInsideFunction(isNotAliasingArgument(V2))) - isLocal2 = false; + if (isUniq2 && Content2->valueEscapesInsideFunction(V2)) + isUniq2 = false; - if (!isLocal1 && !isLocal2) + if (!isUniq1 && !isUniq2) return true; // Check if both nodes may point to the same content. - CGNode *Content1 = ConGraph->getContentNode(Node1); - CGNode *Content2 = ConGraph->getContentNode(Node2); - + // FIXME!!!: This will be rewritten to use node flags in the next commit. SILType T1 = V1->getType(); SILType T2 = V2->getType(); if (T1.isAddress() && T2.isAddress()) { @@ -1975,43 +2670,107 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) { // have to go down one content level if just one of the values is a // ref-counted object. if (T1.isAddress() && hasReferenceSemantics(T2)) { - Content2 = ConGraph->getContentNode(Content2); + Content2 = ConGraph->getFieldContent(Content2); return Content1 == Content2; } if (T2.isAddress() && hasReferenceSemantics(T1)) { - Content1 = ConGraph->getContentNode(Content1); + Content1 = ConGraph->getFieldContent(Content1); return Content1 == Content2; } return true; } -bool EscapeAnalysis::canParameterEscape(FullApplySite FAS, int ParamIdx, - bool checkContentOfIndirectParam) { - CalleeList Callees = BCA->getCalleeList(FAS); - if (!Callees.allCalleesVisible()) +// Return true if deinitialization of \p releasedReference may release memory +// directly pointed to by \p accessAddress. +// +// Note that \p accessedAddress could be a reference itself, an address of a +// local/argument that contains a reference, or even a pointer to the middle of +// an object (even if it is an exclusive argument). +// +// This is almost the same as asking "is the content node for accessedAddress +// reachable via releasedReference", with three subtle differences: +// +// (1) A locally referenced object can only be freed when deinitializing +// releasedReference if it is the same object. Indirect references will be kept +// alive by their distinct local references--ARC can't remove those without +// inserting a mark_dependence/end_dependence scope. +// +// (2) the content of exclusive arguments may be indirectly reachable via +// releasedReference, but the exclusive argument must have it's own reference +// count, so cannot be freed via the locally released reference. +// +// (3) Objects may contain raw pointers into themselves or into other +// objects. Any access to the raw pointer is not considered a use of the object +// because that access must be "guarded" by a fix_lifetime or +// mark_dependence/end_dependence that acts as a placeholder. +// +// There are two interesting cases in which a connection graph query can +// determine that the accessed memory cannot be released: +// +// Case #1: accessedAddress points to a uniquely identified object that does not +// escape within this function. +// +// Note: A "uniquely identified object" is either a locally allocated object, +// which is obviously not reachable outside this function, or an exclusive +// address argument, which *is* reachable outside this function, but must +// have its own reference count so cannot be released locally. +// +// Case #2: The released reference points to a local object and no connection +// graph path exists from the referenced object to a global-escaping or +// argument-escaping node without traversing a non-interior edge. +// +// In both cases, the connection graph is sufficient to determine if the +// accessed content may be released. To prove that the accessed memory is +// distinct from any released memory it is now sufficient to check that no +// connection graph path exists from the released object's node to the accessed +// content node without traversing a non-interior edge. +bool EscapeAnalysis::mayReleaseContent(SILValue releasedReference, + SILValue accessedAddress) { + assert(!releasedReference->getType().isAddress() + && "an address is never a reference"); + + SILFunction *f = getCommonFunction(releasedReference, accessedAddress); + if (!f) + return true; + + auto *conGraph = getConnectionGraph(f); + + CGNode *addrContentNode = conGraph->getValueContent(accessedAddress); + if (!addrContentNode) return true; - // Derive the connection graph of the apply from the known callees. - for (SILFunction *Callee : Callees) { - FunctionInfo *FInfo = getFunctionInfo(Callee); - if (!FInfo->isValid()) - recompute(FInfo); + // Case #1: Unique accessedAddress whose content does not escape. + bool isAccessUniq = + isUniquelyIdentified(accessedAddress) + && !addrContentNode->valueEscapesInsideFunction(accessedAddress); - CGNode *Node = FInfo->SummaryGraph.getNodeOrNull( - Callee->getArgument(ParamIdx), this); - if (!Node) - return true; + // Case #2: releasedReference points to a local object. + if (!isAccessUniq && !pointsToLocalObject(releasedReference)) + return true; - if (checkContentOfIndirectParam) { - Node = Node->getContentNodeOrNull(); - if (!Node) - continue; - } + CGNode *releasedObjNode = conGraph->getValueContent(releasedReference); + // Make sure we have at least one value CGNode for releasedReference. + if (!releasedObjNode) + return true; - if (Node->escapes()) + // Check for reachability from releasedObjNode to addrContentNode. + // A pointsTo cycle is equivalent to a null pointsTo. + CGNodeWorklist worklist(conGraph); + for (CGNode *releasedNode = releasedObjNode; + releasedNode && worklist.tryPush(releasedNode); + releasedNode = releasedNode->getContentNodeOrNull()) { + // A path exists from released content to accessed content. + if (releasedNode == addrContentNode) return true; + + // A path exists to an escaping node. + if (!isAccessUniq && releasedNode->escapesInsideFunction()) + return true; + + if (!releasedNode->isInterior()) + break; } - return false; + return false; // no path to escaping memory that may be freed. } void EscapeAnalysis::invalidate() { @@ -2043,6 +2802,25 @@ void EscapeAnalysis::handleDeleteNotification(SILNode *node) { } } +void EscapeAnalysis::verify() const { +#ifndef NDEBUG + for (auto Iter : Function2Info) { + FunctionInfo *FInfo = Iter.second; + FInfo->Graph.verify(); + FInfo->SummaryGraph.verifyStructure(); + } +#endif +} + +void EscapeAnalysis::verify(SILFunction *F) const { +#ifndef NDEBUG + if (FunctionInfo *FInfo = Function2Info.lookup(F)) { + FInfo->Graph.verify(); + FInfo->SummaryGraph.verifyStructure(); + } +#endif +} + SILAnalysis *swift::createEscapeAnalysis(SILModule *M) { return new EscapeAnalysis(M); } diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 45596e922d25b..6b320a2c49b98 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -156,11 +156,11 @@ class MemoryBehaviorVisitor REFCOUNTINC_MEMBEHAVIOR_INST(RetainValueInst) #define UNCHECKED_REF_STORAGE(Name, ...) \ REFCOUNTINC_MEMBEHAVIOR_INST(Name##RetainValueInst) \ - REFCOUNTINC_MEMBEHAVIOR_INST(Copy##Name##ValueInst) -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - REFCOUNTINC_MEMBEHAVIOR_INST(Name##RetainInst) \ - REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetain##Name##Inst) \ - REFCOUNTINC_MEMBEHAVIOR_INST(Copy##Name##ValueInst) + REFCOUNTINC_MEMBEHAVIOR_INST(StrongCopy##Name##ValueInst) +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + REFCOUNTINC_MEMBEHAVIOR_INST(Name##RetainInst) \ + REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetain##Name##Inst) \ + REFCOUNTINC_MEMBEHAVIOR_INST(StrongCopy##Name##ValueInst) #include "swift/AST/ReferenceStorage.def" #undef REFCOUNTINC_MEMBEHAVIOR_INST }; diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index 91c4bdfa0a680..0712744d5a5a8 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -492,11 +492,11 @@ void FunctionSideEffects::analyzeInstruction(SILInstruction *I) { return; #define UNCHECKED_REF_CAST(Name, ...) \ case SILInstructionKind::Name##RetainValueInst: \ - case SILInstructionKind::Copy##Name##ValueInst: -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Name##RetainInst: \ - case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StrongRetainInst: case SILInstructionKind::RetainValueInst: diff --git a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp index 203e7cca65921..43fa3d930e048 100644 --- a/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/TypeExpansionAnalysis.cpp @@ -24,10 +24,13 @@ using namespace swift; // memory usage of this cache. static const int TypeExpansionAnalysisMaxCacheSize = 4096; -const ProjectionPathList& -TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod) { +const ProjectionPathList & +TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod, + TypeExpansionContext context) { // Check whether we have the type expansion. - auto Iter = ExpansionCache.find(B); + auto key = std::make_pair(B, context); + auto Iter = ExpansionCache.find(key); + // if (Iter != ExpansionCache.end()) { return Iter->second; } @@ -36,8 +39,8 @@ TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod) { if (!shouldExpand(*Mod, B)) { // Push the empty projection path. ProjectionPath P(B); - ExpansionCache[B].push_back(P); - return ExpansionCache[B]; + ExpansionCache[key].push_back(P); + return ExpansionCache[key]; } // Flush the cache if the size of the cache is too large. @@ -46,8 +49,9 @@ TypeExpansionAnalysis::getTypeExpansion(SILType B, SILModule *Mod) { } // Build the type expansion for the leaf nodes. - ProjectionPath::expandTypeIntoLeafProjectionPaths(B, Mod, ExpansionCache[B]); - return ExpansionCache[B]; + ProjectionPath::expandTypeIntoLeafProjectionPaths(B, Mod, context, + ExpansionCache[key]); + return ExpansionCache[key]; } SILAnalysis *swift::createTypeExpansionAnalysis(SILModule *M) { diff --git a/lib/SILOptimizer/Analysis/ValueTracking.cpp b/lib/SILOptimizer/Analysis/ValueTracking.cpp index 43a067b3224ae..17a8994f8f9e9 100644 --- a/lib/SILOptimizer/Analysis/ValueTracking.cpp +++ b/lib/SILOptimizer/Analysis/ValueTracking.cpp @@ -23,14 +23,13 @@ using namespace swift; using namespace swift::PatternMatch; -bool swift::isNotAliasingArgument(SILValue V, - InoutAliasingAssumption isInoutAliasing) { +bool swift::isExclusiveArgument(SILValue V) { auto *Arg = dyn_cast(V); if (!Arg) return false; SILArgumentConvention Conv = Arg->getArgumentConvention(); - return Conv.isNotAliasedIndirectParameter(isInoutAliasing); + return Conv.isExclusiveIndirectParameter(); } /// Check if the parameter \V is based on a local object, e.g. it is an @@ -81,10 +80,8 @@ static bool isLocalObject(SILValue Obj) { return true; } -bool swift::pointsToLocalObject(SILValue V, - InoutAliasingAssumption isInoutAliasing) { - V = getUnderlyingObject(V); - return isLocalObject(V) || isNotAliasingArgument(V, isInoutAliasing); +bool swift::pointsToLocalObject(SILValue V) { + return isLocalObject(getUnderlyingObject(V)); } /// Check if the value \p Value is known to be zero, non-zero or unknown. diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp index 37038e08da81e..fd7dec9c340da 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp @@ -320,7 +320,7 @@ bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() { SILFunction *F = TransformDescriptor.OriginalFunction; // Did we decide we should optimize any parameter? bool SignatureOptimize = false; - auto Args = F->begin()->getFunctionArguments(); + auto Args = F->begin()->getSILFunctionArguments(); ConsumedArgToEpilogueReleaseMatcher ArgToReturnReleaseMap( RCIA->get(F), F, {SILArgumentConvention::Direct_Owned}); diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/DeadArgumentTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/DeadArgumentTransform.cpp index aca55afb997c0..5abbbbc5ab677 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/DeadArgumentTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/DeadArgumentTransform.cpp @@ -29,7 +29,7 @@ bool FunctionSignatureTransform::DeadArgumentAnalyzeParameters() { // Did we decide we should optimize any parameter? SILFunction *F = TransformDescriptor.OriginalFunction; bool SignatureOptimize = false; - auto Args = F->begin()->getFunctionArguments(); + auto Args = F->begin()->getSILFunctionArguments(); auto OrigShouldModifySelfArgument = TransformDescriptor.shouldModifySelfArgument; // Analyze the argument information. diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp index 6a10833dc58e7..44954304de13c 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialSpecializer.cpp @@ -130,7 +130,7 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction( llvm::SmallDenseMap &ExistentialArgDescriptor) { auto *F = Apply.getReferencedFunctionOrNull(); - auto CalleeArgs = F->begin()->getFunctionArguments(); + auto CalleeArgs = F->begin()->getSILFunctionArguments(); bool returnFlag = false; /// Analyze the argument for protocol conformance. Iterator over the callee's @@ -207,7 +207,6 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction( /// Determine if this callee function can be specialized or not. bool ExistentialSpecializer::canSpecializeCalleeFunction(FullApplySite &Apply) { - /// Determine the caller of the apply. auto *Callee = Apply.getReferencedFunctionOrNull(); if (!Callee) @@ -221,6 +220,13 @@ bool ExistentialSpecializer::canSpecializeCalleeFunction(FullApplySite &Apply) { if (!Callee->isDefinition()) return false; + // If the callee has ownership enabled, bail. + // + // FIXME: We should be able to handle callees that have ownership, but the + // pass has not been updated yet. + if (Callee->hasOwnership()) + return false; + /// Ignore functions with indirect results. if (Callee->getConventions().hasIndirectSILResults()) return false; @@ -308,7 +314,7 @@ void ExistentialSpecializer::specializeExistentialArgsInAppliesWithinFunction( /// Save the arguments in a descriptor. llvm::SpecificBumpPtrAllocator Allocator; llvm::SmallVector ArgumentDescList; - auto Args = Callee->begin()->getFunctionArguments(); + auto Args = Callee->begin()->getSILFunctionArguments(); for (unsigned i : indices(Args)) { ArgumentDescList.emplace_back(Args[i], Allocator); } diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index a680bec395e1b..108a2545e1f60 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -16,6 +16,7 @@ #define DEBUG_TYPE "sil-existential-transform" #include "ExistentialTransform.h" +#include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/SIL/OptimizationRemark.h" @@ -114,11 +115,29 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() { } } +// Gather the conformances needed for an existential value based on an opened +// archetype. This adds any conformances inherited from superclass constraints. +static ArrayRef +collectExistentialConformances(ModuleDecl *M, CanType openedType, + CanType existentialType) { + assert(!openedType.isAnyExistentialType()); + + auto layout = existentialType.getExistentialLayout(); + auto protocols = layout.getProtocols(); + + SmallVector conformances; + for (auto proto : protocols) { + auto conformance = M->lookupConformance(openedType, proto->getDecl()); + assert(conformance); + conformances.push_back(conformance); + } + return M->getASTContext().AllocateCopy(conformances); +} + // Create the entry basic block with the function arguments. void ExistentialSpecializerCloner::cloneArguments( SmallVectorImpl &entryArgs) { auto &M = OrigF->getModule(); - auto &Ctx = M.getASTContext(); // Create the new entry block. SILFunction &NewF = getBuilder().getFunction(); @@ -143,7 +162,7 @@ void ExistentialSpecializerCloner::cloneArguments( auto iter = ArgToGenericTypeMap.find(ArgDesc.Index); if (iter == ArgToGenericTypeMap.end()) { // Clone arguments that are not rewritten. - auto Ty = params[ArgDesc.Index].getType(); + auto Ty = params[ArgDesc.Index].getArgumentType(M, NewFTy); auto LoweredTy = NewF.getLoweredType(NewF.mapTypeIntoContext(Ty)); auto MappedTy = LoweredTy.getCategoryType(ArgDesc.Arg->getType().getCategory()); @@ -164,14 +183,10 @@ void ExistentialSpecializerCloner::cloneArguments( NewArg->setOwnershipKind(ValueOwnershipKind( NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention())); // Determine the Conformances. - SmallVector NewConformances; - auto ContextTy = NewF.mapTypeIntoContext(GenericParam); - auto OpenedArchetype = ContextTy->castTo(); - for (auto proto : OpenedArchetype->getConformsTo()) { - NewConformances.push_back(ProtocolConformanceRef(proto)); - } - ArrayRef Conformances = - Ctx.AllocateCopy(NewConformances); + SILType ExistentialType = ArgDesc.Arg->getType().getObjectType(); + CanType OpenedType = NewArg->getType().getASTType(); + auto Conformances = collectExistentialConformances( + M.getSwiftModule(), OpenedType, ExistentialType.getASTType()); auto ExistentialRepr = ArgDesc.Arg->getType().getPreferredExistentialRepresentation(); auto &EAD = ExistentialArgDescriptor[ArgDesc.Index]; @@ -266,7 +281,7 @@ void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes( auto FTy = F->getLoweredFunctionType(); /// If the original function is generic, then maintain the same. - auto OrigGenericSig = FTy->getGenericSignature(); + auto OrigGenericSig = FTy->getInvocationGenericSignature(); /// Original list of parameters SmallVector params; @@ -285,7 +300,7 @@ void ExistentialTransform::convertExistentialArgTypesToGenericArgTypes( for (auto const &IdxIt : ExistentialArgDescriptor) { int Idx = IdxIt.first; auto ¶m = params[Idx]; - auto PType = param.getType(); + auto PType = param.getArgumentType(M, FTy); assert(PType.isExistentialType()); /// Generate new generic parameter. auto *NewGenericParam = GenericTypeParamType::get(Depth, GPIdx++, Ctx); @@ -307,10 +322,9 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { SILModule &M = F->getModule(); auto &Ctx = M.getASTContext(); GenericSignature NewGenericSig; - GenericEnvironment *NewGenericEnv; /// If the original function is generic, then maintain the same. - auto OrigGenericSig = FTy->getGenericSignature(); + auto OrigGenericSig = FTy->getInvocationGenericSignature(); SmallVector GenericParams; SmallVector Requirements; @@ -326,8 +340,6 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { std::move(Requirements)}, GenericSignature()); - NewGenericEnv = NewGenericSig->getGenericEnvironment(); - /// Create a lambda for GenericParams. auto getCanonicalType = [&](Type t) -> CanType { return t->getCanonicalType(NewGenericSig); @@ -362,13 +374,15 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { /// Finally the ExtInfo. auto ExtInfo = FTy->getExtInfo(); ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); - auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrNone(); + auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrInvalid(); /// Return the new signature. return SILFunctionType::get( NewGenericSig, ExtInfo, FTy->getCoroutineKind(), FTy->getCalleeConvention(), InterfaceParams, FTy->getYields(), - FTy->getResults(), InterfaceErrorResult, Ctx, witnessMethodConformance); + FTy->getResults(), InterfaceErrorResult, + SubstitutionMap(), false, + Ctx, witnessMethodConformance); } /// Create the Thunk Body with always_inline attribute. @@ -405,9 +419,10 @@ void ExistentialTransform::populateThunkBody() { auto *FRI = Builder.createFunctionRefFor(Loc, NewF); auto GenCalleeType = NewF->getLoweredFunctionType(); - auto CalleeGenericSig = GenCalleeType->getGenericSignature(); + auto CalleeGenericSig = GenCalleeType->getInvocationGenericSignature(); auto OrigGenCalleeType = F->getLoweredFunctionType(); - auto OrigCalleeGenericSig = OrigGenCalleeType->getGenericSignature(); + auto OrigCalleeGenericSig = + OrigGenCalleeType->getInvocationGenericSignature(); /// Determine arguments to Apply. /// Generate opened existentials for generics. @@ -514,7 +529,8 @@ void ExistentialTransform::populateThunkBody() { MakeAbstractConformanceForGenericType()); /// Perform the substitutions. - auto SubstCalleeType = GenCalleeType->substGenericArgs(M, SubMap); + auto SubstCalleeType = GenCalleeType->substGenericArgs( + M, SubMap, Builder.getTypeExpansionContext()); /// Obtain the Result Type. SILValue ReturnValue; @@ -582,7 +598,7 @@ void ExistentialTransform::createExistentialSpecializedFunction() { /// Create devirtualized function type. auto NewFTy = createExistentialSpecializedFunctionType(); - auto NewFGenericSig = NewFTy->getGenericSignature(); + auto NewFGenericSig = NewFTy->getInvocationGenericSignature(); auto NewFGenericEnv = NewFGenericSig->getGenericEnvironment(); /// Step 1: Create the new protocol constrained generic function. diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 106503643ae26..92e6036afb92d 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -204,7 +204,7 @@ static bool usesGenerics(SILFunction *F, ArrayRef InterfaceParams, ArrayRef InterfaceResults) { CanSILFunctionType FTy = F->getLoweredFunctionType(); - auto HasGenericSignature = FTy->getGenericSignature() != nullptr; + auto HasGenericSignature = FTy->getSubstGenericSignature() != nullptr; if (!HasGenericSignature) return false; @@ -218,11 +218,11 @@ static bool usesGenerics(SILFunction *F, }; for (auto Param : InterfaceParams) { - Param.getType().visit(FindArchetypesAndGenericTypes); + Param.getInterfaceType().visit(FindArchetypesAndGenericTypes); } for (auto Result : InterfaceResults) { - Result.getType().visit(FindArchetypesAndGenericTypes); + Result.getInterfaceType().visit(FindArchetypesAndGenericTypes); } if (UsesGenerics) @@ -278,26 +278,26 @@ static void mapInterfaceTypes(SILFunction *F, Optional &InterfaceErrorResult) { for (auto &Param : InterfaceParams) { - if (!Param.getType()->hasArchetype()) + if (!Param.getInterfaceType()->hasArchetype()) continue; Param = SILParameterInfo( - Param.getType()->mapTypeOutOfContext()->getCanonicalType(), + Param.getInterfaceType()->mapTypeOutOfContext()->getCanonicalType(), Param.getConvention()); } for (auto &Result : InterfaceResults) { - if (!Result.getType()->hasArchetype()) + if (!Result.getInterfaceType()->hasArchetype()) continue; - auto InterfaceResult = Result.getWithType( - Result.getType()->mapTypeOutOfContext()->getCanonicalType()); + auto InterfaceResult = Result.getWithInterfaceType( + Result.getInterfaceType()->mapTypeOutOfContext()->getCanonicalType()); Result = InterfaceResult; } if (InterfaceErrorResult.hasValue()) { - if (InterfaceErrorResult.getValue().getType()->hasArchetype()) { + if (InterfaceErrorResult.getValue().getInterfaceType()->hasArchetype()) { InterfaceErrorResult = SILResultInfo(InterfaceErrorResult.getValue() - .getType() + .getInterfaceType() ->mapTypeOutOfContext() ->getCanonicalType(), InterfaceErrorResult.getValue().getConvention()); @@ -310,7 +310,7 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { SILFunction *F = OriginalFunction; CanSILFunctionType FTy = F->getLoweredFunctionType(); auto ExpectedFTy = F->getLoweredType().castTo(); - auto HasGenericSignature = FTy->getGenericSignature() != nullptr; + auto HasGenericSignature = FTy->getSubstGenericSignature() != nullptr; // The only way that we modify the arity of function parameters is here for // dead arguments. Doing anything else is unsafe since by definition non-dead @@ -330,7 +330,7 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { auto &RV = ResultDescList[0]; if (!RV.CalleeRetain.empty()) { ++NumOwnedConvertedToNotOwnedResult; - InterfaceResults.push_back(SILResultInfo(InterfaceResult.getType(), + InterfaceResults.push_back(SILResultInfo(InterfaceResult.getInterfaceType(), ResultConvention::Unowned)); continue; } @@ -366,22 +366,22 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { << F->getName() << "\n"; llvm::dbgs() << "Interface params:\n"; for (auto Param : InterfaceParams) { - Param.getType().dump(); + Param.getInterfaceType().dump(llvm::dbgs()); } llvm::dbgs() << "Interface results:\n"; for (auto Result : InterfaceResults) { - Result.getType().dump(); + Result.getInterfaceType().dump(llvm::dbgs()); }); } } // Don't use a method representation if we modified self. auto ExtInfo = FTy->getExtInfo(); - auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrNone(); + auto witnessMethodConformance = FTy->getWitnessMethodConformanceOrInvalid(); if (shouldModifySelfArgument) { ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); - witnessMethodConformance = None; + witnessMethodConformance = ProtocolConformanceRef::forInvalid(); } Optional InterfaceErrorResult; @@ -394,11 +394,12 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { mapInterfaceTypes(F, InterfaceParams, InterfaceResults, InterfaceErrorResult); GenericSignature GenericSig = - UsesGenerics ? FTy->getGenericSignature() : nullptr; + UsesGenerics ? FTy->getSubstGenericSignature() : nullptr; return SILFunctionType::get( GenericSig, ExtInfo, FTy->getCoroutineKind(), FTy->getCalleeConvention(), InterfaceParams, InterfaceYields, InterfaceResults, InterfaceErrorResult, + FTy->getSubstitutions(), FTy->isGenericSignatureImplied(), F->getModule().getASTContext(), witnessMethodConformance); } @@ -467,7 +468,7 @@ void FunctionSignatureTransformDescriptor::computeOptimizedArgInterface( llvm_unreachable("Unknown parameter convention transformation"); } - SILParameterInfo NewInfo(AD.PInfo.getValue().getType(), + SILParameterInfo NewInfo(AD.PInfo.getValue().getInterfaceType(), ParameterConvention); Out.push_back(NewInfo); return; @@ -498,7 +499,7 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { auto NewFTy = TransformDescriptor.createOptimizedSILFunctionType(); GenericEnvironment *NewFGenericEnv; - if (NewFTy->getGenericSignature()) { + if (NewFTy->getSubstGenericSignature()) { NewFGenericEnv = F->getGenericEnvironment(); } else { NewFGenericEnv = nullptr; @@ -537,7 +538,7 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { ArgumentExplosionFinalizeOptimizedFunction(); // Update the ownership kinds of function entry BB arguments. - for (auto Arg : NewF->begin()->getFunctionArguments()) { + for (auto Arg : NewF->begin()->getSILFunctionArguments()) { SILType MappedTy = Arg->getType(); auto Ownershipkind = ValueOwnershipKind(*NewF, MappedTy, Arg->getArgumentConvention()); @@ -580,8 +581,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Produce a substitutions list and a set of substituted SIL types // required for creating a new SIL function. Subs = F->getForwardingSubstitutionMap(); - auto SubstCalleeType = - GenCalleeType->substGenericArgs(M, Subs); + auto SubstCalleeType = GenCalleeType->substGenericArgs( + M, Subs, Builder.getTypeExpansionContext()); SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); SILFunctionConventions Conv(SubstCalleeType, M); ResultType = Conv.getSILResultType(); @@ -595,7 +596,7 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { NormalBlock->createPhiArgument(ResultType, ValueOwnershipKind::Owned); SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); SILType Error = - SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getType()); + SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getInterfaceType()); auto *ErrorArg = ErrorBlock->createPhiArgument(Error, ValueOwnershipKind::Owned); Builder.createTryApply(Loc, FRI, Subs, ThunkArgs, NormalBlock, ErrorBlock); @@ -825,7 +826,7 @@ class FunctionSignatureOpts : public SILFunctionTransform { llvm::SpecificBumpPtrAllocator Allocator; llvm::SmallVector ArgumentDescList; llvm::SmallVector ResultDescList; - auto Args = F->begin()->getFunctionArguments(); + auto Args = F->begin()->getSILFunctionArguments(); for (unsigned i : indices(Args)) { ArgumentDescList.emplace_back(Args[i], Allocator); } diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h index 764510db9bd2b..019b82376b0ca 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h @@ -132,7 +132,7 @@ struct ArgumentDescriptor { if (IsEntirelyDead) return None; if (SubTy.isTrivial(*Arg->getFunction())) - return Optional(ValueOwnershipKind::Any); + return Optional(ValueOwnershipKind::None); if (OwnedToGuaranteed) return Optional(ValueOwnershipKind::Guaranteed); return Arg->getOwnershipKind(); diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/OwnedToGuaranteedTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/OwnedToGuaranteedTransform.cpp index 35c1eb1b869b3..1bd07d376c535 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/OwnedToGuaranteedTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/OwnedToGuaranteedTransform.cpp @@ -56,7 +56,7 @@ static SILInstruction *findOnlyApply(SILFunction *F) { bool FunctionSignatureTransform::OwnedToGuaranteedAnalyzeParameters() { SILFunction *F = TransformDescriptor.OriginalFunction; - auto Args = F->begin()->getFunctionArguments(); + auto Args = F->begin()->getSILFunctionArguments(); // A map from consumed SILArguments to the release associated with an // argument. // diff --git a/lib/SILOptimizer/IPO/CMakeLists.txt b/lib/SILOptimizer/IPO/CMakeLists.txt index ea6b83f231ec0..57cdb7d60904e 100644 --- a/lib/SILOptimizer/IPO/CMakeLists.txt +++ b/lib/SILOptimizer/IPO/CMakeLists.txt @@ -2,6 +2,7 @@ silopt_register_sources( CapturePromotion.cpp CapturePropagation.cpp ClosureSpecializer.cpp + CrossModuleSerializationSetup.cpp DeadFunctionElimination.cpp EagerSpecializer.cpp GlobalOpt.cpp diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 652c4485ee2c7..2bb212748025c 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -160,6 +160,8 @@ class ReachingBlockSet { return !(*this == RHS); } + ReachingBlockSet(const ReachingBlockSet &RHS) + : Bits(RHS.Bits), NumBitWords(RHS.NumBitWords) {} const ReachingBlockSet &operator=(const ReachingBlockSet &RHS) { assert(NumBitWords == RHS.NumBitWords && "mismatched sets"); for (size_t i = 0, e = NumBitWords; i != e; ++i) @@ -337,10 +339,7 @@ computeNewArgInterfaceTypes(SILFunction *F, IndicesSet &PromotableIndices, LLVM_DEBUG(llvm::dbgs() << "Preparing New Args!\n"); - auto fnTy = F->getLoweredFunctionType(); - auto &Types = F->getModule().Types; - Lowering::GenericContextScope scope(Types, fnTy->getGenericSignature()); // For each parameter in the old function... for (unsigned Index : indices(Parameters)) { @@ -353,7 +352,7 @@ computeNewArgInterfaceTypes(SILFunction *F, IndicesSet &PromotableIndices, LLVM_DEBUG(llvm::dbgs() << "Index: " << Index << "; PromotableIndices: " << (PromotableIndices.count(ArgIndex)?"yes":"no") - << " Param: "; param.dump()); + << " Param: "; param.print(llvm::dbgs())); if (!PromotableIndices.count(ArgIndex)) { OutTys.push_back(param); @@ -363,12 +362,15 @@ computeNewArgInterfaceTypes(SILFunction *F, IndicesSet &PromotableIndices, // Perform the proper conversions and then add it to the new parameter list // for the type. assert(!param.isFormalIndirect()); - auto paramTy = param.getSILStorageType(); + auto paramTy = param.getSILStorageType(fnConv.silConv.getModule(), + fnConv.funcTy); auto paramBoxTy = paramTy.castTo(); assert(paramBoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented yet"); - auto paramBoxedTy = getSILBoxFieldType(paramBoxTy, Types, 0); - auto ¶mTL = Types.getTypeLowering(paramBoxedTy, expansion); + auto paramBoxedTy = + getSILBoxFieldType(TypeExpansionContext(*F), paramBoxTy, Types, 0); + assert(expansion == F->getResilienceExpansion()); + auto ¶mTL = Types.getTypeLowering(paramBoxedTy, *F); ParameterConvention convention; if (paramTL.isAddressOnly()) { convention = ParameterConvention::Indirect_In; @@ -424,11 +426,11 @@ ClosureCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, // Create the thin function type for the cloned closure. auto ClonedTy = SILFunctionType::get( - OrigFTI->getGenericSignature(), OrigFTI->getExtInfo(), + OrigFTI->getInvocationGenericSignature(), OrigFTI->getExtInfo(), OrigFTI->getCoroutineKind(), OrigFTI->getCalleeConvention(), - ClonedInterfaceArgTys, OrigFTI->getYields(), - OrigFTI->getResults(), OrigFTI->getOptionalErrorResult(), - M.getASTContext(), OrigFTI->getWitnessMethodConformanceOrNone()); + ClonedInterfaceArgTys, OrigFTI->getYields(), OrigFTI->getResults(), + OrigFTI->getOptionalErrorResult(), SubstitutionMap(), false, + M.getASTContext(), OrigFTI->getWitnessMethodConformanceOrInvalid()); assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation()) && "SILFunction missing location"); @@ -479,8 +481,9 @@ ClosureCloner::populateCloned() { auto BoxTy = (*I)->getType().castTo(); assert(BoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented"); - auto BoxedTy = getSILBoxFieldType(BoxTy, Cloned->getModule().Types, 0) - .getObjectType(); + auto BoxedTy = getSILBoxFieldType(TypeExpansionContext(*Cloned), BoxTy, + Cloned->getModule().Types, 0) + .getObjectType(); SILValue MappedValue = ClonedEntryBB->createFunctionArgument(BoxedTy, (*I)->getDecl()); @@ -488,7 +491,7 @@ ClosureCloner::populateCloned() { // a non-trivial value. We know that our value is not written to and it does // not escape. The use of a borrow enforces this. if (Cloned->hasOwnership() && - MappedValue.getOwnershipKind() != ValueOwnershipKind::Any) { + MappedValue.getOwnershipKind() != ValueOwnershipKind::None) { SILLocation Loc(const_cast((*I)->getDecl())); MappedValue = getBuilder().emitBeginBorrowOperation(Loc, MappedValue); } @@ -579,7 +582,7 @@ void ClosureCloner::visitDestroyValueInst(DestroyValueInst *Inst) { // If ownership is enabled, then we must emit a begin_borrow for any // non-trivial value. if (F.hasOwnership() && - Value.getOwnershipKind() != ValueOwnershipKind::Any) { + Value.getOwnershipKind() != ValueOwnershipKind::None) { auto *BBI = cast(Value); Value = BBI->getOperand(); B.emitEndBorrowOperation(Inst->getLoc(), BBI); @@ -998,7 +1001,8 @@ bool isPartialApplyNonEscapingUser(Operand *CurrentOp, PartialApplyInst *PAI, auto BoxTy = BoxArg->getType().castTo(); assert(BoxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented yet"); - if (getSILBoxFieldType(BoxTy, M.Types, 0).isAddressOnly(*F)) { + if (getSILBoxFieldType(TypeExpansionContext(*Fn), BoxTy, M.Types, 0) + .isAddressOnly(*F)) { LLVM_DEBUG(llvm::dbgs() << " FAIL! Box is an address only " "argument!\n"); return false; @@ -1195,7 +1199,7 @@ constructClonedFunction(SILOptFunctionBuilder &FuncBuilder, /// 2. We only see a mark_uninitialized when paired with an (alloc_box, /// project_box). e.x.: /// -/// (mark_uninitialized (project_box (alloc_box))) +/// (project_box (mark_uninitialized (alloc_box))) /// /// The asserts are to make sure that if the initial safety condition check /// is changed, this code is changed as well. @@ -1207,15 +1211,16 @@ static SILValue getOrCreateProjectBoxHelper(SILValue PartialOperand) { } // Otherwise, handle the alloc_box case. If we have a mark_uninitialized on - // the box, we create the project value through that. + // the box, we know that we will have a project_box of that value due to SIL + // verifier invariants. SingleValueInstruction *Box = cast(PartialOperand); - if (auto *Op = Box->getSingleUse()) { - if (auto *MUI = dyn_cast(Op->getUser())) { - Box = MUI; + if (auto *MUI = Box->getSingleUserOfType()) { + if (auto *PBI = MUI->getSingleUserOfType()) { + return PBI; } } - // Just return a project_box. + // Otherwise, create a new project_box. SILBuilderWithScope B(std::next(Box->getIterator())); return B.createProjectBox(Box->getLoc(), Box, 0); } @@ -1272,8 +1277,8 @@ processPartialApplyInst(SILOptFunctionBuilder &FuncBuilder, auto CalleeFunctionTy = PAI->getCallee()->getType().castTo(); auto SubstCalleeFunctionTy = CalleeFunctionTy; if (PAI->hasSubstitutions()) - SubstCalleeFunctionTy = - CalleeFunctionTy->substGenericArgs(M, PAI->getSubstitutionMap()); + SubstCalleeFunctionTy = CalleeFunctionTy->substGenericArgs( + M, PAI->getSubstitutionMap(), TypeExpansionContext(*F)); SILFunctionConventions calleeConv(SubstCalleeFunctionTy, M); auto CalleePInfo = SubstCalleeFunctionTy->getParameters(); SILFunctionConventions paConv(PAI->getType().castTo(), M); diff --git a/lib/SILOptimizer/IPO/CapturePropagation.cpp b/lib/SILOptimizer/IPO/CapturePropagation.cpp index 614f8781b8b6d..d482e8b01ecac 100644 --- a/lib/SILOptimizer/IPO/CapturePropagation.cpp +++ b/lib/SILOptimizer/IPO/CapturePropagation.cpp @@ -256,7 +256,7 @@ SILFunction *CapturePropagation::specializeConstClosure(PartialApplyInst *PAI, NewFTy = NewFTy->getWithRepresentation(SILFunctionType::Representation::Thin); GenericEnvironment *GenericEnv = nullptr; - if (NewFTy->getGenericSignature()) + if (NewFTy->getInvocationGenericSignature()) GenericEnv = OrigF->getGenericEnvironment(); SILOptFunctionBuilder FuncBuilder(*this); SILFunction *NewF = FuncBuilder.createFunction( @@ -421,10 +421,11 @@ static SILFunction *getSpecializedWithDeadParams( return nullptr; // Perform a generic specialization of the Specialized function. - ReabstractionInfo ReInfo(ApplySite(), Specialized, - PAI->getSubstitutionMap(), - Specialized->isSerialized(), - /* ConvertIndirectToDirect */ false); + ReabstractionInfo ReInfo( + FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), ApplySite(), Specialized, + PAI->getSubstitutionMap(), Specialized->isSerialized(), + /* ConvertIndirectToDirect */ false); GenericFuncSpecializer FuncSpecializer(FuncBuilder, Specialized, ReInfo.getClonerParamSubstitutionMap(), diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index 335961d249e32..d9395b29b8f92 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -284,7 +284,7 @@ class CallSiteDescriptor { bool isTrivialNoEscapeParameter() const { auto ClosureParmFnTy = - getClosureParameterInfo().getType()->getAs(); + getClosureParameterInfo().getInterfaceType()->getAs(); return ClosureParmFnTy->isTrivialNoEscape(); } @@ -654,7 +654,7 @@ ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, : ParameterConvention::Direct_Owned; } - SILParameterInfo NewPInfo(PInfo.getType(), ParamConv); + SILParameterInfo NewPInfo(PInfo.getInterfaceType(), ParamConv); NewParameterInfoList.push_back(NewPInfo); } @@ -665,11 +665,14 @@ ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); auto ClonedTy = SILFunctionType::get( - ClosureUserFunTy->getGenericSignature(), ExtInfo, + ClosureUserFunTy->getSubstGenericSignature(), ExtInfo, ClosureUserFunTy->getCoroutineKind(), ClosureUserFunTy->getCalleeConvention(), NewParameterInfoList, ClosureUserFunTy->getYields(), ClosureUserFunTy->getResults(), - ClosureUserFunTy->getOptionalErrorResult(), M.getASTContext()); + ClosureUserFunTy->getOptionalErrorResult(), + ClosureUserFunTy->getSubstitutions(), + ClosureUserFunTy->isGenericSignatureImplied(), + M.getASTContext()); // We make this function bare so we don't have to worry about decls in the // SILArgument. @@ -797,8 +800,9 @@ void ClosureSpecCloner::populateCloned() { } // Otherwise, create a new argument which copies the original argument + auto typeInContext = Cloned->getLoweredType(Arg->getType()); SILValue MappedValue = - ClonedEntryBB->createFunctionArgument(Arg->getType(), Arg->getDecl()); + ClonedEntryBB->createFunctionArgument(typeInContext, Arg->getDecl()); entryArgs.push_back(MappedValue); } @@ -818,6 +822,8 @@ void ClosureSpecCloner::populateCloned() { unsigned idx = 0; for (auto &PInfo : ClosedOverFunConv.getParameters().slice(NumNotCaptured)) { auto paramTy = ClosedOverFunConv.getSILType(PInfo); + // Get the type in context of the new function. + paramTy = Cloned->getLoweredType(paramTy); SILValue MappedValue = ClonedEntryBB->createFunctionArgument(paramTy); NewPAIArgs.push_back(MappedValue); auto CapturedVal = @@ -1213,7 +1219,7 @@ bool SILClosureSpecializerTransform::gatherCallSites( // We currently only support copying intermediate reabastraction // closures if the closure is ultimately passed trivially. - bool IsClosurePassedTrivially = ClosureParamInfo.getType() + bool IsClosurePassedTrivially = ClosureParamInfo.getInterfaceType() ->castTo() ->isTrivialNoEscape(); if (HaveUsedReabstraction && !IsClosurePassedTrivially) diff --git a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp new file mode 100644 index 0000000000000..06460ed249257 --- /dev/null +++ b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp @@ -0,0 +1,399 @@ +//===--- UsePrespecialized.cpp - use pre-specialized functions ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// An optimization which marks functions and types as inlinable or usable +/// from inline. This lets such functions be serialized (later in the pipeline), +/// which makes them available for other modules. +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "cross-module-serialization-setup" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILCloner.h" +#include "swift/AST/Module.h" +#include "llvm/Support/Debug.h" + +using namespace swift; + +namespace { + +/// Scans a whole module and marks functions and types as inlinable or usable +/// from inline. +class CrossModuleSerializationSetup { + friend class InstructionVisitor; + + // The worklist of function which should be serialized. + llvm::SmallVector workList; + llvm::SmallPtrSet functionsHandled; + + llvm::SmallPtrSet typesHandled; + + SILModule &M; + + void addToWorklistIfNotHandled(SILFunction *F) { + if (functionsHandled.count(F) == 0) { + workList.push_back(F); + functionsHandled.insert(F); + } + } + + bool canUseFromInline(SILFunction *F, bool lookIntoThunks); + + bool canSerialize(SILFunction *F, bool lookIntoThunks); + + void setUpForSerialization(SILFunction *F); + + void prepareInstructionForSerialization(SILInstruction *inst); + + void handleReferencedFunction(SILFunction *F); + + void handleReferencedMethod(SILDeclRef method); + + void makeTypeUsableFromInline(CanType type); + + void makeSubstUsableFromInline(const SubstitutionMap &substs); + +public: + CrossModuleSerializationSetup(SILModule &M) : M(M) { } + + void scanModule(); +}; + +/// Visitor for making used types of an intruction inlinable. +/// +/// We use the SILCloner for visiting types, though it sucks that we allocate +/// instructions just to delete them immediately. But it's better than to +/// reimplement the logic. +/// TODO: separate the type visiting logic in SILCloner from the instruction +/// creation. +class InstructionVisitor : public SILCloner { + friend class SILCloner; + friend class SILInstructionVisitor; + +private: + CrossModuleSerializationSetup &CMS; + SILInstruction *result = nullptr; + +public: + InstructionVisitor(SILFunction *F, CrossModuleSerializationSetup &CMS) : + SILCloner(*F), CMS(CMS) {} + + SILType remapType(SILType Ty) { + CMS.makeTypeUsableFromInline(Ty.getASTType()); + return Ty; + } + + CanType remapASTType(CanType Ty) { + CMS.makeTypeUsableFromInline(Ty); + return Ty; + } + + SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { + CMS.makeSubstUsableFromInline(Subs); + return Subs; + } + + void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { + result = Cloned; + SILCloner::postProcess(Orig, Cloned); + } + + SILValue getMappedValue(SILValue Value) { return Value; } + + SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; } + + static void visitInst(SILInstruction *I, CrossModuleSerializationSetup &CMS) { + InstructionVisitor visitor(I->getFunction(), CMS); + visitor.visit(I); + + SILInstruction::destroy(visitor.result); + CMS.M.deallocateInst(visitor.result); + } +}; + +/// Make a nominal type, including it's context, usable from inline. +static void makeDeclUsableFromInline(ValueDecl *decl, SILModule &M) { + if (decl->getEffectiveAccess() >= AccessLevel::Public) + return; + + if (!decl->isUsableFromInline()) { + // Mark the nominal type as "usableFromInline". + // TODO: find a way to do this without modifying the AST. The AST should be + // immutable at this point. + auto &ctx = decl->getASTContext(); + auto *attr = new (ctx) UsableFromInlineAttr(/*implicit=*/true); + decl->getAttrs().add(attr); + } + if (auto *nominalCtx = dyn_cast(decl->getDeclContext())) { + makeDeclUsableFromInline(nominalCtx, M); + } else if (auto *extCtx = dyn_cast(decl->getDeclContext())) { + if (auto *extendedNominal = extCtx->getExtendedNominal()) { + makeDeclUsableFromInline(extendedNominal, M); + } + } else if (decl->getDeclContext()->isLocalContext()) { + // TODO + } +} + +/// Ensure that the \p type is usable from serialized functions. +void CrossModuleSerializationSetup::makeTypeUsableFromInline(CanType type) { + if (!typesHandled.insert(type.getPointer()).second) + return; + + if (NominalTypeDecl *NT = type->getNominalOrBoundGenericNominal()) { + makeDeclUsableFromInline(NT, M); + } + + // Also make all sub-types usable from inline. + type.visit([this](Type rawSubType) { + CanType subType = rawSubType->getCanonicalType(); + if (typesHandled.insert(subType.getPointer()).second) { + if (NominalTypeDecl *subNT = subType->getNominalOrBoundGenericNominal()) { + makeDeclUsableFromInline(subNT, M); + } + } + }); +} + +/// Ensure that all replacement types of \p substs are usable from serialized +/// functions. +void CrossModuleSerializationSetup:: +makeSubstUsableFromInline(const SubstitutionMap &substs) { + for (Type replType : substs.getReplacementTypes()) { + makeTypeUsableFromInline(replType->getCanonicalType()); + } + for (ProtocolConformanceRef pref : substs.getConformances()) { + if (pref.isConcrete()) { + ProtocolConformance *concrete = pref.getConcrete(); + makeDeclUsableFromInline(concrete->getProtocol(), M); + } + } +} + +/// Decide whether to serialize a function. +static bool shouldSerialize(SILFunction *F) { + // The basic heursitic: serialize all generic functions, because it makes a + // huge difference if generic functions can be specialized or not. + if (!F->getLoweredFunctionType()->isPolymorphic()) + return false; + + // Check if we already handled this function before. + if (F->isSerialized() == IsSerialized) + return false; + + if (F->hasSemanticsAttr("optimize.no.crossmodule")) + return false; + + return true; +} + +static void makeFunctionUsableFromInline(SILFunction *F) { + if (!isAvailableExternally(F->getLinkage())) + F->setLinkage(SILLinkage::Public); +} + +/// Prepare \p inst for serialization and in case it's a function_ref, put the +/// referenced function onto the worklist. +void CrossModuleSerializationSetup:: +prepareInstructionForSerialization(SILInstruction *inst) { + // Make all types of the instruction usable from inline. + InstructionVisitor::visitInst(inst, *this); + + // Put callees onto the worklist if they should be serialized as well. + if (auto *FRI = dyn_cast(inst)) { + SILFunction *callee = FRI->getReferencedFunctionOrNull(); + assert(callee); + handleReferencedFunction(callee); + return; + } + if (auto *MI = dyn_cast(inst)) { + handleReferencedMethod(MI->getMember()); + return; + } + if (auto *KPI = dyn_cast(inst)) { + KPI->getPattern()->visitReferencedFunctionsAndMethods( + [this](SILFunction *func) { handleReferencedFunction(func); }, + [this](SILDeclRef method) { handleReferencedMethod(method); }); + return; + } + if (auto *REAI = dyn_cast(inst)) { + makeDeclUsableFromInline(REAI->getField(), M); + } +} + +void CrossModuleSerializationSetup::handleReferencedFunction(SILFunction *func) { + if (!func->isDefinition() || func->isAvailableExternally()) + return; + if (func->getLinkage() == SILLinkage::Shared) { + assert(func->isThunk() != IsNotThunk && + "only thunks are accepted to have shared linkage"); + assert(canSerialize(func, /*lookIntoThunks*/ false) && + "we should already have checked that the thunk is serializable"); + + if (func->isSerialized() == IsSerialized) + return; + + // We cannot make shared functions "usableFromInline", i.e. make them Public + // because this could result in duplicate-symbol errors. Instead we make + // them "@alwaysEmitIntoClient" + setUpForSerialization(func); + return; + } + if (shouldSerialize(func)) { + addToWorklistIfNotHandled(func); + return; + } + makeFunctionUsableFromInline(func); + return; +} + +void CrossModuleSerializationSetup::handleReferencedMethod(SILDeclRef method) { + if (method.isForeign) + return; + // Prevent the method from dead-method elimination. + auto *methodDecl = cast(method.getDecl()); + M.addExternallyVisibleDecl(getBaseMethod(methodDecl)); +} + +/// Check if the function \p F can be serialized. +/// +/// If \p lookIntoThunks is true, function_ref instructions of shared +/// thunks are also accepted. +bool CrossModuleSerializationSetup::canSerialize(SILFunction *F, + bool lookIntoThunks) { + // First step: check if serializing F is even possible. + for (SILBasicBlock &block : *F) { + for (SILInstruction &inst : block) { + if (auto *FRI = dyn_cast(&inst)) { + SILFunction *callee = FRI->getReferencedFunctionOrNull(); + if (!canUseFromInline(callee, lookIntoThunks)) + return false; + } else if (auto *KPI = dyn_cast(&inst)) { + bool canUse = true; + KPI->getPattern()->visitReferencedFunctionsAndMethods( + [&](SILFunction *func) { + if (!canUseFromInline(func, lookIntoThunks)) + canUse = false; + }, + [](SILDeclRef method) { }); + if (!canUse) + return false; + } + } + } + return true; +} + +/// Returns true if the function \p func can be used from a serialized function. +/// +/// If \p lookIntoThunks is true, serializable shared thunks are also accepted. +bool CrossModuleSerializationSetup::canUseFromInline(SILFunction *func, + bool lookIntoThunks) { + if (!func) + return false; + + switch (func->getLinkage()) { + case SILLinkage::PublicNonABI: + return func->isSerialized() != IsNotSerialized; + case SILLinkage::Shared: + if (func->isThunk() != IsNotThunk && lookIntoThunks && + // Don't recursively lookIntoThunks to avoid infinite loops. + canSerialize(func, /*lookIntoThunks*/ false)) { + return true; + } + return false; + case SILLinkage::Public: + case SILLinkage::Hidden: + case SILLinkage::Private: + case SILLinkage::PublicExternal: + case SILLinkage::SharedExternal: + case SILLinkage::PrivateExternal: + case SILLinkage::HiddenExternal: + break; + } + return true; +} + +/// Setup the function \p param F for serialization and put callees onto the +/// worklist for further processing. +/// +/// Returns false in case this is not possible for some reason. +void CrossModuleSerializationSetup::setUpForSerialization(SILFunction *F) { + assert(F->isSerialized() != IsSerialized); + + // Second step: go through all instructions and prepare them for + // for serialization. + for (SILBasicBlock &block : *F) { + for (SILInstruction &inst : block) { + prepareInstructionForSerialization(&inst); + } + } + F->setSerialized(IsSerialized); + + // As a code size optimization, make serialized functions + // @alwaysEmitIntoClient. + // Also, for shared thunks it's required to make them @alwaysEmitIntoClient. + // SILLinkage::Public would not work for shared functions, because it could + // result in duplicate-symbol linker errors. + F->setLinkage(SILLinkage::PublicNonABI); +} + +/// Select functions in the module which should be serialized. +void CrossModuleSerializationSetup::scanModule() { + + // Start with public functions. + for (SILFunction &F : M) { + if (F.getLinkage() == SILLinkage::Public) + addToWorklistIfNotHandled(&F); + } + + // Continue with called functions. + while (!workList.empty()) { + SILFunction *F = workList.pop_back_val(); + // Decide whether we want to serialize the function. + if (shouldSerialize(F)) { + // Try to serialize. + if (canSerialize(F, /*lookIntoThunks*/ true)) { + setUpForSerialization(F); + } else { + // If for some reason the function cannot be serialized, we mark it as + // usable-from-inline. + makeFunctionUsableFromInline(F); + } + } + } +} + +class CrossModuleSerializationSetupPass: public SILModuleTransform { + void run() override { + + auto &M = *getModule(); + if (M.getSwiftModule()->isResilient()) + return; + if (!M.isWholeModule()) + return; + if (!M.getOptions().CrossModuleOptimization) + return; + + CrossModuleSerializationSetup CMSS(M); + CMSS.scanModule(); + } +}; + +} // end anonymous namespace + +SILTransform *swift::createCrossModuleSerializationSetup() { + return new CrossModuleSerializationSetupPass(); +} diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 48bba4ae28568..82fd149ddbdb5 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -90,6 +90,8 @@ class FunctionLivenessComputation { llvm::SmallPtrSet AliveFunctionsAndTables; + bool keepExternalWitnessTablesAlive; + /// Checks is a function is alive, e.g. because it is visible externally. bool isAnchorFunction(SILFunction *F) { @@ -148,6 +150,11 @@ class FunctionLivenessComputation { /// Marks all contained functions and witness tables of a witness table as /// alive. void makeAlive(SILWitnessTable *WT) { + if (isAvailableExternally(WT->getLinkage()) && + !keepExternalWitnessTablesAlive) { + return; + } + LLVM_DEBUG(llvm::dbgs() << " scan witness table " << WT->getName() << '\n'); @@ -159,7 +166,8 @@ class FunctionLivenessComputation { auto methodWitness = entry.getMethodWitness(); auto *fd = cast(methodWitness.Requirement. getDecl()); - assert(fd == getBase(fd) && "key in witness table is overridden"); + assert(fd == getBaseMethod(fd) && + "key in witness table is overridden"); SILFunction *F = methodWitness.Witness; if (F) { MethodInfo *MI = getMethodInfo(fd, /*isWitnessMethod*/ true); @@ -195,22 +203,17 @@ class FunctionLivenessComputation { /// aren't yet. void ensureKeyPathComponentIsAlive(const KeyPathPatternComponent &component) { - switch (component.getKind()) { - case KeyPathPatternComponent::Kind::SettableProperty: - ensureAlive(component.getComputedPropertySetter()); - LLVM_FALLTHROUGH; - case KeyPathPatternComponent::Kind::GettableProperty: { - ensureAlive(component.getComputedPropertyGetter()); - auto id = component.getComputedPropertyId(); - switch (id.getKind()) { - case KeyPathPatternComponent::ComputedPropertyId::DeclRef: { - auto declRef = id.getDeclRef(); - if (declRef.isForeign) { + component.visitReferencedFunctionsAndMethods( + [this](SILFunction *F) { + ensureAlive(F); + }, + [this](SILDeclRef method) { + if (method.isForeign) { // Nothing to do here: foreign functions aren't ours to be deleting. // (And even if they were, they're ObjC-dispatched and thus anchored // already: see isAnchorFunction) } else { - auto decl = cast(declRef.getDecl()); + auto decl = cast(method.getDecl()); if (auto clas = dyn_cast(decl->getDeclContext())) { ensureAliveClassMethod(getMethodInfo(decl, /*witness*/ false), dyn_cast(decl), @@ -221,29 +224,8 @@ class FunctionLivenessComputation { llvm_unreachable("key path keyed by a non-class, non-protocol method"); } } - break; } - case KeyPathPatternComponent::ComputedPropertyId::Function: - ensureAlive(id.getFunction()); - break; - case KeyPathPatternComponent::ComputedPropertyId::Property: - break; - } - - if (auto equals = component.getSubscriptIndexEquals()) - ensureAlive(equals); - if (auto hash = component.getSubscriptIndexHash()) - ensureAlive(hash); - - break; - } - case KeyPathPatternComponent::Kind::StoredProperty: - case KeyPathPatternComponent::Kind::OptionalChain: - case KeyPathPatternComponent::Kind::OptionalForce: - case KeyPathPatternComponent::Kind::OptionalWrap: - case KeyPathPatternComponent::Kind::TupleElement: - break; - } + ); } /// Marks a function as alive if it is not alive yet. @@ -324,15 +306,6 @@ class FunctionLivenessComputation { } } - /// Gets the base implementation of a method. - /// We always use the most overridden function to describe a method. - AbstractFunctionDecl *getBase(AbstractFunctionDecl *FD) { - while (FD->getOverriddenDecl()) { - FD = FD->getOverriddenDecl(); - } - return FD; - } - /// Scans all references inside a function. void scanFunction(SILFunction *F) { @@ -342,12 +315,12 @@ class FunctionLivenessComputation { for (SILBasicBlock &BB : *F) { for (SILInstruction &I : BB) { if (auto *WMI = dyn_cast(&I)) { - auto *funcDecl = getBase( + auto *funcDecl = getBaseMethod( cast(WMI->getMember().getDecl())); MethodInfo *mi = getMethodInfo(funcDecl, /*isWitnessTable*/ true); ensureAliveProtocolMethod(mi); } else if (auto *MI = dyn_cast(&I)) { - auto *funcDecl = getBase( + auto *funcDecl = getBaseMethod( cast(MI->getMember().getDecl())); assert(MI->getNumOperands() - MI->getNumTypeDependentOperands() == 1 && "method insts except witness_method must have 1 operand"); @@ -427,8 +400,10 @@ class FunctionLivenessComputation { } public: - FunctionLivenessComputation(SILModule *module) : - Module(module) {} + FunctionLivenessComputation(SILModule *module, + bool keepExternalWitnessTablesAlive) : + Module(module), + keepExternalWitnessTablesAlive(keepExternalWitnessTablesAlive) {} /// The main entry point of the optimization. bool findAliveFunctions() { @@ -474,7 +449,8 @@ class DeadFunctionElimination : FunctionLivenessComputation { continue; } SILFunction *F = entry.Implementation; - auto *fd = getBase(cast(entry.Method.getDecl())); + auto *fd = getBaseMethod(cast( + entry.Method.getDecl())); MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ false); mi->addClassMethodImpl(F, vTable.getClass()); } @@ -490,7 +466,8 @@ class DeadFunctionElimination : FunctionLivenessComputation { auto methodWitness = entry.getMethodWitness(); auto *fd = cast(methodWitness.Requirement. getDecl()); - assert(fd == getBase(fd) && "key in witness table is overridden"); + assert(fd == getBaseMethod(fd) && + "key in witness table is overridden"); SILFunction *F = methodWitness.Witness; if (!F) continue; @@ -533,12 +510,14 @@ class DeadFunctionElimination : FunctionLivenessComputation { } SILFunction *F = entry.Implementation; - auto *fd = getBase(cast(entry.Method.getDecl())); + auto *fd = getBaseMethod(cast( + entry.Method.getDecl())); if (// We also have to check the method declaration's access level. // Needed if it's a public base method declared in another // compilation unit (for this we have no SILFunction). isVisibleExternally(fd) + || Module->isExternallyVisibleDecl(fd) // Declarations are always accessible externally, so they are alive. || !F->isDefinition()) { MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ false); @@ -550,24 +529,27 @@ class DeadFunctionElimination : FunctionLivenessComputation { // Check witness table methods. for (SILWitnessTable &WT : Module->getWitnessTableList()) { ProtocolConformance *Conf = WT.getConformance(); - if (isVisibleExternally(Conf->getProtocol())) { - // The witness table is visible from "outside". Therefore all methods - // might be called and we mark all methods as alive. - for (const SILWitnessTable::Entry &entry : WT.getEntries()) { - if (entry.getKind() != SILWitnessTable::Method) - continue; + bool tableExternallyVisible = isVisibleExternally(Conf->getProtocol()); + // The witness table is visible from "outside". Therefore all methods + // might be called and we mark all methods as alive. + for (const SILWitnessTable::Entry &entry : WT.getEntries()) { + if (entry.getKind() != SILWitnessTable::Method) + continue; - auto methodWitness = entry.getMethodWitness(); - auto *fd = cast(methodWitness.Requirement. - getDecl()); - assert(fd == getBase(fd) && "key in witness table is overridden"); - SILFunction *F = methodWitness.Witness; - if (!F) - continue; + auto methodWitness = entry.getMethodWitness(); + auto *fd = cast(methodWitness.Requirement. + getDecl()); + assert(fd == getBaseMethod(fd) && + "key in witness table is overridden"); + SILFunction *F = methodWitness.Witness; + if (!F) + continue; - MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true); - ensureAliveProtocolMethod(mi); - } + if (!tableExternallyVisible && !Module->isExternallyVisibleDecl(fd)) + continue; + + MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true); + ensureAliveProtocolMethod(mi); } // We don't do dead witness table elimination right now. So we assume @@ -588,7 +570,7 @@ class DeadFunctionElimination : FunctionLivenessComputation { auto *fd = cast( entry.getMethodWitness().Requirement.getDecl()); - assert(fd == getBase(fd) && + assert(fd == getBaseMethod(fd) && "key in default witness table is overridden"); SILFunction *F = entry.getMethodWitness().Witness; if (!F) @@ -662,8 +644,8 @@ class DeadFunctionElimination : FunctionLivenessComputation { } public: - DeadFunctionElimination(SILModule *module) - : FunctionLivenessComputation(module) {} + DeadFunctionElimination(SILModule *module, bool keepExternalWitnessTablesAlive) + : FunctionLivenessComputation(module, keepExternalWitnessTablesAlive) {} /// The main entry point of the optimization. void eliminateFunctions(SILModuleTransform *DFEPass) { @@ -720,6 +702,13 @@ class DeadFunctionElimination : FunctionLivenessComputation { namespace { class SILDeadFuncElimination : public SILModuleTransform { + +private: + bool isLateDFE; + +public: + SILDeadFuncElimination(bool isLateDFE) : isLateDFE(isLateDFE) { } + void run() override { LLVM_DEBUG(llvm::dbgs() << "Running DeadFuncElimination\n"); @@ -730,7 +719,8 @@ class SILDeadFuncElimination : public SILModuleTransform { // can eliminate such functions. getModule()->invalidateSILLoaderCaches(); - DeadFunctionElimination deadFunctionElimination(getModule()); + DeadFunctionElimination deadFunctionElimination(getModule(), + /*keepExternalWitnessTablesAlive*/ !isLateDFE); deadFunctionElimination.eliminateFunctions(this); } }; @@ -738,7 +728,11 @@ class SILDeadFuncElimination : public SILModuleTransform { } // end anonymous namespace SILTransform *swift::createDeadFunctionElimination() { - return new SILDeadFuncElimination(); + return new SILDeadFuncElimination(/*isLateDFE*/ false); +} + +SILTransform *swift::createLateDeadFunctionElimination() { + return new SILDeadFuncElimination(/*isLateDFE*/ true); } void swift::performSILDeadFunctionElimination(SILModule *M) { diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/IPO/EagerSpecializer.cpp index 1d94dd9a2be85..9b0156909a7b6 100644 --- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp +++ b/lib/SILOptimizer/IPO/EagerSpecializer.cpp @@ -172,12 +172,6 @@ emitApplyWithRethrow(SILBuilder &Builder, SILValue Error = ErrorBB->createPhiArgument(fnConv.getSILErrorType(), ValueOwnershipKind::Owned); - Builder.createBuiltin(Loc, - Builder.getASTContext().getIdentifier("willThrow"), - Builder.getModule().Types.getEmptyTupleType(), - SubstitutionMap(), - {Error}); - EmitCleanup(Builder, Loc); addThrowValue(ErrorBB, Error); } @@ -231,7 +225,8 @@ emitInvocation(SILBuilder &Builder, if (ReInfo.getSpecializedType()->isPolymorphic()) { Subs = ReInfo.getCallerParamSubstitutionMap(); CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(), + Builder.getTypeExpansionContext()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -345,7 +340,7 @@ void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) { // the specialized attribute's substitution list. Visit only // SubstitutableTypes, skipping DependentTypes. auto GenericSig = - GenericFunc->getLoweredFunctionType()->getGenericSignature(); + GenericFunc->getLoweredFunctionType()->getInvocationGenericSignature(); auto SubMap = ReInfo.getClonerParamSubstitutionMap(); GenericSig->forEachParam([&](GenericTypeParamType *ParamTy, bool Canonical) { @@ -618,7 +613,7 @@ SILValue EagerDispatch::emitArgumentCast(CanSILFunctionType CalleeSubstFnTy, /// has a direct result. SILValue EagerDispatch:: emitArgumentConversion(SmallVectorImpl &CallArgs) { - auto OrigArgs = GenericFunc->begin()->getFunctionArguments(); + auto OrigArgs = GenericFunc->begin()->getSILFunctionArguments(); assert(OrigArgs.size() == substConv.getNumSILArguments() && "signature mismatch"); // Create a substituted callee type. @@ -628,7 +623,8 @@ emitArgumentConversion(SmallVectorImpl &CallArgs) { auto CalleeSubstFnTy = CanSILFuncTy; if (CanSILFuncTy->isPolymorphic()) { CalleeSubstFnTy = CanSILFuncTy->substGenericArgs( - Builder.getModule(), ReInfo.getCallerParamSubstitutionMap()); + Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(), + Builder.getTypeExpansionContext()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && @@ -713,7 +709,7 @@ static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, LLVM_DEBUG(auto FT = GenericFunc->getLoweredFunctionType(); dbgs() << " Generic Sig:"; - dbgs().indent(2); FT->getGenericSignature()->print(dbgs()); + dbgs().indent(2); FT->getInvocationGenericSignature()->print(dbgs()); dbgs() << " Generic Env:"; dbgs().indent(2); GenericFunc->getGenericEnvironment()->dump(dbgs()); @@ -752,7 +748,7 @@ void EagerSpecializerTransform::run() { if (F.isDynamicallyReplaceable()) continue; - if (!F.getLoweredFunctionType()->getGenericSignature()) + if (!F.getLoweredFunctionType()->getInvocationGenericSignature()) continue; // Create a specialized function with ReabstractionInfo for each attribute. @@ -763,7 +759,9 @@ void EagerSpecializerTransform::run() { // TODO: Use a decision-tree to reduce the amount of dynamic checks being // performed. for (auto *SA : F.getSpecializeAttrs()) { - ReInfoVec.emplace_back(&F, SA->getSpecializedSignature()); + ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), &F, + SA->getSpecializedSignature()); auto *NewFunc = eagerSpecialize(FuncBuilder, &F, *SA, ReInfoVec.back()); SpecializedFuncs.push_back(NewFunc); diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 0fcd0d7f39015..d07949a871edc 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -20,6 +20,7 @@ #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILInstructionWorklist.h" #include "swift/SILOptimizer/Analysis/ColdBlockInfo.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" @@ -59,6 +60,8 @@ class SILGlobalOpt { typedef SmallVector GlobalInitCalls; typedef SmallVector GlobalLoads; + typedef SmallVector GlobalAccesses; + typedef SmallVector GlobalAddrs; /// A map from each visited global initializer call to a list of call sites. llvm::MapVector GlobalInitCallMap; @@ -70,10 +73,20 @@ class SILGlobalOpt { /// A map from each visited global let variable to its set of loads. llvm::MapVector GlobalLoadMap; + /// A map from each visited global to its set of begin_access instructions. + llvm::MapVector GlobalAccessMap; + + /// A map from each visited global to all of its global address instructions. + llvm::MapVector GlobalAddrMap; + /// A map from each visited global let variable to the store instructions /// which initialize it. llvm::MapVector GlobalVarStore; + /// A map for each visited global variable to the alloc instruction that + /// allocated space for it. + llvm::MapVector AllocGlobalStore; + /// A set of visited global variables that for some reason we have decided is /// not able to be optimized safely or for which we do not know how to /// optimize safely. @@ -97,6 +110,10 @@ class SILGlobalOpt { /// A map from a globalinit_func to the number of times "once" has called the /// function. llvm::DenseMap InitializerCount; + + llvm::SmallVector InstToRemove; + llvm::SmallVector GlobalsToRemove; + public: SILGlobalOpt(SILOptFunctionBuilder &FunctionBuilder, SILModule *M, DominanceAnalysis *DA) : FunctionBuilder(FunctionBuilder), Module(M), DA(DA) {} @@ -104,6 +121,14 @@ class SILGlobalOpt { bool run(); protected: + /// Reset all the maps of global variables. + void reset(); + + /// Collect all global variables. + void collect(); + + void collectUsesOfInstructionForDeletion(SILInstruction *inst); + /// If this is a call to a global initializer, map it. void collectGlobalInitCall(ApplyInst *AI); @@ -118,6 +143,11 @@ class SILGlobalOpt { /// This is the main entrypoint for collecting global accesses. void collectGlobalAccess(GlobalAddrInst *GAI); + /// Simple function to collect globals and their corresponding alloc + /// instructions. + void collectAllocGlobal(SILGlobalVariable *global, + AllocGlobalInst *allocGlobal); + /// Returns true if we think that \p CurBB is inside a loop. bool isInLoop(SILBasicBlock *CurBB); @@ -139,6 +169,17 @@ class SILGlobalOpt { /// can be statically initialized. void optimizeInitializer(SILFunction *AddrF, GlobalInitCalls &Calls); + /// If possible, remove global address instructions associated with the given + /// global. + bool tryRemoveGlobalAddr(SILGlobalVariable *global); + + /// If possible, remove global alloc instructions associated with the given + /// global. + bool tryRemoveGlobalAlloc(SILGlobalVariable *global, AllocGlobalInst *alloc); + + /// If a global has no uses, remove it. + bool tryRemoveUnusedGlobal(SILGlobalVariable *global); + /// Optimize access to the global variable, which is known to have a constant /// value. Replace all loads from the global address by invocations of a /// getter that returns the value of this variable. @@ -264,7 +305,8 @@ static SILFunction *getGlobalGetterFunction(SILOptFunctionBuilder &FunctionBuild Serialized = IsSerialized; } - auto refType = M.Types.getLoweredRValueType(varDecl->getInterfaceType()); + auto refType = M.Types.getLoweredRValueType(TypeExpansionContext::minimal(), + varDecl->getInterfaceType()); // Function takes no arguments and returns refType SILResultInfo Results[] = { SILResultInfo(refType, @@ -276,6 +318,7 @@ static SILFunction *getGlobalGetterFunction(SILOptFunctionBuilder &FunctionBuild SILCoroutineKind::None, ParameterConvention::Direct_Unowned, /*params*/ {}, /*yields*/ {}, Results, None, + SubstitutionMap(), false, M.getASTContext()); auto getterName = M.allocateCopy(getterNameTmp); return FunctionBuilder.getOrCreateFunction( @@ -565,7 +608,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, // Find the store instruction auto *BB = GetterF->getEntryBlock(); SILValue Val; - SILInstruction *Store; + SILInstruction *Store = nullptr; for (auto II = BB->begin(), E = BB->end(); II != E;) { auto &I = *II++; if (isa(&I)) { @@ -584,6 +627,7 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder, B.createReturn(RI->getLoc(), Val); eraseUsesOfInstruction(RI); recursivelyDeleteTriviallyDeadInstructions(RI, true); + assert(Store && "Did not find a store?!"); recursivelyDeleteTriviallyDeadInstructions(Store, true); return GetterF; } @@ -665,6 +709,10 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF, for (int i = 0, e = Calls.size(); i < e; ++i) { auto *Call = Calls[i]; + if (Call->getFunction()->isSerialized() && + !GetterF->hasValidLinkageForFragileRef()) + continue; + // Make sure that we can go ahead and replace all uses of the // address with the value. bool isValid = true; @@ -742,6 +790,16 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF, if (!SILG) return; + auto expansion = ResilienceExpansion::Maximal; + if (hasPublicVisibility(SILG->getLinkage())) + expansion = ResilienceExpansion::Minimal; + + auto &tl = Module->Types.getTypeLowering( + SILG->getLoweredType(), + TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion)); + if (!tl.isLoadable()) + return; + LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: use static initializer for " << SILG->getName() << '\n'); @@ -792,6 +850,110 @@ static bool canBeChangedExternally(SILGlobalVariable *SILG) { return true; } +static bool canBeUsedOrChangedExternally(SILGlobalVariable *global) { + if (global->isLet()) + return isPossiblyUsedExternally(global->getLinkage(), + global->getModule().isWholeModule()); + return canBeChangedExternally(global); +} + +static bool isSafeToRemove(SILGlobalVariable *global) { + return global->getDecl() && !canBeUsedOrChangedExternally(global); +} + +bool SILGlobalOpt::tryRemoveGlobalAlloc(SILGlobalVariable *global, + AllocGlobalInst *alloc) { + if (!isSafeToRemove(global)) + return false; + + // Make sure the global's address is never taken and we shouldn't skip this + // global. + if (GlobalVarSkipProcessing.count(global) || + (GlobalAddrMap[global].size() && + std::any_of(GlobalAddrMap[global].begin(), GlobalAddrMap[global].end(), + [=](GlobalAddrInst *addr) { + return std::find(InstToRemove.begin(), InstToRemove.end(), + addr) == InstToRemove.end(); + }))) + return false; + + InstToRemove.push_back(alloc); + return true; +} + +/// If there are no loads or accesses of a given global, then remove its +/// associated global addr and all asssociated instructions. +bool SILGlobalOpt::tryRemoveGlobalAddr(SILGlobalVariable *global) { + if (!isSafeToRemove(global)) + return false; + + if (GlobalVarSkipProcessing.count(global) || GlobalLoadMap[global].size() || + GlobalAccessMap[global].size()) + return false; + + // Check if the address is used in anything but a store. If any global_addr + // instruction associated with a global is used in anything but a store, we + // can't remove ANY global_addr instruction associated with that global. + for (auto *addr : GlobalAddrMap[global]) { + for (auto *use : addr->getUses()) { + if (!isa(use->getUser())) + return false; + } + } + + // Now that it's safe, remove all global addresses associated with this global + for (auto *addr : GlobalAddrMap[global]) { + InstToRemove.push_back(addr); + } + + return true; +} + +bool SILGlobalOpt::tryRemoveUnusedGlobal(SILGlobalVariable *global) { + if (!isSafeToRemove(global)) + return false; + + if (GlobalVarSkipProcessing.count(global)) + return false; + + // If this global is used, check if the user is going to be removed. + // Make sure none of the removed instructions are the same as this global's + // alloc instruction + if (AllocGlobalStore.count(global) && + std::none_of(InstToRemove.begin(), InstToRemove.end(), + [=](SILInstruction *inst) { + return AllocGlobalStore[global] == inst; + })) + return false; + + if (GlobalVarStore.count(global) && + std::none_of( + InstToRemove.begin(), InstToRemove.end(), + [=](SILInstruction *inst) { return GlobalVarStore[global] == inst; })) + return false; + + // Check if any of the global_addr instructions associated with this global + // aren't going to be removed. In that case, we need to keep the global. + if (GlobalAddrMap[global].size() && + std::any_of(GlobalAddrMap[global].begin(), GlobalAddrMap[global].end(), + [=](GlobalAddrInst *addr) { + return std::find(InstToRemove.begin(), InstToRemove.end(), + addr) == InstToRemove.end(); + })) + return false; + + if (GlobalAccessMap[global].size() && + std::any_of(GlobalAccessMap[global].begin(), + GlobalAccessMap[global].end(), [=](BeginAccessInst *access) { + return std::find(InstToRemove.begin(), InstToRemove.end(), + access) == InstToRemove.end(); + })) + return false; + + GlobalsToRemove.push_back(global); + return true; +} + /// Check if instruction I is a load from instruction V or /// or a struct_element_addr from instruction V. /// returns instruction I if this condition holds, or nullptr otherwise. @@ -820,6 +982,11 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) { if (!SILG) return; + if (!SILG->getDecl()) + return; + + GlobalAddrMap[SILG].push_back(GAI); + if (!SILG->isLet()) { // We cannot determine the value for global variables which could be // changed externally at run-time. @@ -844,9 +1011,6 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) { if (GlobalVar == SILG) return; - if (!SILG->getDecl()) - return; - for (auto *Op : getNonDebugUses(GAI)) { if (auto *SI = dyn_cast(Op->getUser())) { if (SI->getDest() == GAI) @@ -859,6 +1023,10 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) { continue; } + if (auto *beginAccess = dyn_cast(Op->getUser())) { + GlobalAccessMap[SILG].push_back(beginAccess); + } + LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: has non-store, non-load use: " << SILG->getName() << '\n'; Op->getUser()->dump()); @@ -869,6 +1037,11 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) { } } +void SILGlobalOpt::collectAllocGlobal(SILGlobalVariable *global, + AllocGlobalInst *allocGlobal) { + AllocGlobalStore[global] = allocGlobal; +} + // Optimize access to the global variable, which is known to have a constant // value. Replace all loads from the global address by invocations of a getter // that returns the value of this variable. @@ -889,7 +1062,7 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG, return; } - if (!GlobalLoadMap.count(SILG)) { + if (GlobalLoadMap[SILG].empty()) { LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: not in load map: " << SILG->getName() << '\n'); return; @@ -906,6 +1079,10 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG, // invocation should happen at the common dominator of all // loads inside this function. for (auto *Load : GlobalLoadMap[SILG]) { + if (Load->getFunction()->isSerialized() && + !GetterF->hasValidLinkageForFragileRef()) + continue; + SILBuilderWithScope B(Load); auto *GetterRef = B.createFunctionRef(Load->getLoc(), GetterF); auto *Value = B.createApply(Load->getLoc(), GetterRef, @@ -917,13 +1094,17 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG, } -bool SILGlobalOpt::run() { - for (auto &F : *Module) { - - // Don't optimize functions that are marked with the opt.never attribute. - if (!F.shouldOptimize()) - continue; +void SILGlobalOpt::reset() { + AllocGlobalStore.clear(); + GlobalVarStore.clear(); + GlobalAddrMap.clear(); + GlobalAccessMap.clear(); + GlobalLoadMap.clear(); + GlobalInitCallMap.clear(); +} +void SILGlobalOpt::collect() { + for (auto &F : *Module) { // TODO: Add support for ownership. if (F.hasOwnership()) { continue; @@ -946,27 +1127,92 @@ bool SILGlobalOpt::run() { continue; } - auto *GAI = dyn_cast(&I); - if (!GAI) { + if (auto *GAI = dyn_cast(&I)) { + collectGlobalAccess(GAI); continue; } - collectGlobalAccess(GAI); + if (auto *allocGlobal = dyn_cast(&I)) { + collectAllocGlobal(allocGlobal->getReferencedGlobal(), allocGlobal); + continue; + } } } } +} +bool SILGlobalOpt::run() { + // Collect all the global variables and associated instructions. + collect(); + + // Optimize based on what we just collected. for (auto &InitCalls : GlobalInitCallMap) { + // Don't optimize functions that are marked with the opt.never attribute. + if (!InitCalls.first->shouldOptimize()) + continue; + // Optimize the addressors if possible. optimizeInitializer(InitCalls.first, InitCalls.second); placeInitializers(InitCalls.first, InitCalls.second); } for (auto &Init : GlobalVarStore) { + // Don't optimize functions that are marked with the opt.never attribute. + if (!Init.second->getFunction()->shouldOptimize()) + continue; + // Optimize the access to globals if possible. optimizeGlobalAccess(Init.first, Init.second); } + SmallVector addrGlobals; + for (auto &addrPair : GlobalAddrMap) { + // Don't optimize functions that are marked with the opt.never attribute. + bool shouldOptimize = true; + for (auto *addr : addrPair.second) { + if (!addr->getFunction()->shouldOptimize()) { + shouldOptimize = false; + break; + } + } + if (!shouldOptimize) + continue; + + addrGlobals.push_back(addrPair.first); + } + + for (auto *global : addrGlobals) { + HasChanged |= tryRemoveGlobalAddr(global); + } + + SmallVector, 12> + globalAllocPairs; + for (auto &alloc : AllocGlobalStore) { + if (!alloc.second->getFunction()->shouldOptimize()) + continue; + globalAllocPairs.push_back(std::make_pair(alloc.first, alloc.second)); + } + + for (auto &allocPair : globalAllocPairs) { + HasChanged |= tryRemoveGlobalAlloc(allocPair.first, allocPair.second); + } + + // Erase the instructions that we have marked for deletion. + for (auto *inst : InstToRemove) { + eraseUsesOfInstruction(inst); + inst->eraseFromParent(); + } + + for (auto &global : Module->getSILGlobals()) { + HasChanged |= tryRemoveUnusedGlobal(&global); + } + + for (auto *global : GlobalsToRemove) { + Module->eraseGlobalVariable(global); + } + + // Reset in case we re-run this function (when HasChanged is true). + reset(); return HasChanged; } diff --git a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp index 6fc43adc5fe52..5b1dab1e9ed3c 100644 --- a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp +++ b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp @@ -376,7 +376,7 @@ bool LetPropertiesOpt::isConstantLetProperty(VarDecl *Property) { // FIXME: Expansion auto &TL = Module->Types.getTypeLowering(Property->getType(), - ResilienceExpansion::Minimal); + TypeExpansionContext::minimal()); if (!TL.isTrivial()) { LLVM_DEBUG(llvm::dbgs() << "Property '" << *Property << "' is not of trivial type\n"); diff --git a/lib/SILOptimizer/IPO/UsePrespecialized.cpp b/lib/SILOptimizer/IPO/UsePrespecialized.cpp index 2b1e8f58aa42b..b2f7bfde4e40f 100644 --- a/lib/SILOptimizer/IPO/UsePrespecialized.cpp +++ b/lib/SILOptimizer/IPO/UsePrespecialized.cpp @@ -90,7 +90,8 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) { if (Subs.hasArchetypes()) continue; - ReabstractionInfo ReInfo(AI, ReferencedF, Subs, IsNotSerialized); + ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), AI, + ReferencedF, Subs, IsNotSerialized); if (!ReInfo.canBeSpecialized()) continue; diff --git a/lib/SILOptimizer/LoopTransforms/ArrayOpt.h b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h new file mode 100644 index 0000000000000..68644a33ee9bb --- /dev/null +++ b/lib/SILOptimizer/LoopTransforms/ArrayOpt.h @@ -0,0 +1,234 @@ +//===--- ArrayOpt.h ---------------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// Array optimization utilities. +/// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILInstruction.h" +#include "llvm/ADT/SmallPtrSet.h" + +namespace swift { + +/// Collect all uses of a struct given an aggregate value that contains the +/// struct and access path describing the projection of the aggregate +/// that accesses the struct. +/// +/// AggregateAddressUsers records uses of the aggregate value's address. These +/// may indirectly access the struct's elements. +/// +/// Projections over the aggregate that do not access the struct are ignored. +/// +/// StructLoads records loads of the struct value. +/// StructAddressUsers records other uses of the struct address. +/// StructValueUsers records direct uses of the loaded struct. +/// +/// Projections of the struct over its elements are all similarly recorded in +/// ElementAddressUsers, ElementLoads, and ElementValueUsers. +/// +/// bb0(%arg : $*S) +/// apply %f(%arg) // <--- Aggregate Address User +/// %struct_addr = struct_element_addr %arg : $*S, #S.element +/// apply %g(%struct_addr) // <--- Struct Address User +/// %val = load %struct_addr // <--- Struct Load +/// apply %h(%val) // <--- Struct Value User +/// %elt_addr = struct_element_addr %struct_addr : $*A, #A.element +/// apply %i(%elt_addr) // <--- Element Address User +/// %elt = load %elt_addr // <--- Element Load +/// apply %j(%elt) // <--- Element Value User +class StructUseCollector { +public: + typedef SmallPtrSet VisitedSet; + typedef SmallVector UserList; + + /// Record the users of a value or an element within that value along with the + /// operand that directly uses the value. Multiple levels of struct_extract + /// may exist between the operand and the user instruction. + typedef SmallVector, 16> UserOperList; + + /// \return a sequence of integers representing the access path of this + /// element within a Struct/Ref/Tuple. + /// + /// Do not form a path with an IndexAddrInst because we have no way to + /// distinguish between indexing and subelement access. The same index could + /// either refer to the next element (indexed) or a subelement. + static SILValue getAccessPath(SILValue V, SmallVectorImpl& Path) { + V = stripCasts(V); + if (auto *IA = dyn_cast(V)) { + // Don't include index_addr projections in the access path. We could if + // the index is constant. For simplicity we just ignore them. + V = stripCasts(IA->getBase()); + } + ProjectionIndex PI(V); + if (!PI.isValid()) + return V; + + SILValue UnderlyingObject = getAccessPath(PI.Aggregate, Path); + Path.push_back(PI.Index); + return UnderlyingObject; + } + + UserList AggregateAddressUsers; + UserList StructAddressUsers; + SmallVector StructLoads; + UserList StructValueUsers; + UserOperList ElementAddressUsers; + SmallVector, 16> ElementLoads; + UserOperList ElementValueUsers; + VisitedSet Visited; + + /// Collect all uses of the value at the given address. + void collectUses(ValueBase *V, ArrayRef AccessPath) { + // Save our old indent and increment. + // Collect all users of the address and loads. + collectAddressUses(V, AccessPath, nullptr); + + // Collect all uses of the Struct value. + for (auto *DefInst : StructLoads) { + for (auto *DefUI : DefInst->getUses()) { + if (!Visited.insert(&*DefUI).second) { + continue; + } + + StructValueUsers.push_back(DefUI->getUser()); + } + } + + // Collect all users of element values. + for (auto &Pair : ElementLoads) { + for (auto *DefUI : Pair.first->getUses()) { + if (!Visited.insert(&*DefUI).second) { + continue; + } + + ElementValueUsers.push_back( + std::make_pair(DefUI->getUser(), Pair.second)); + } + } + } + + /// Returns true if there is a single address user of the value. + bool hasSingleAddressUse(SILInstruction *SingleAddressUser) { + if (!AggregateAddressUsers.empty()) + return false; + if (!ElementAddressUsers.empty()) + return false; + if (StructAddressUsers.size() != 1) + return false; + return StructAddressUsers[0] == SingleAddressUser; + } + +protected: + + static bool definesSingleObjectType(ValueBase *V) { + return V->getType().isObject(); + } + + /// If AccessPathSuffix is non-empty, then the value is the address of an + /// aggregate containing the Struct. If AccessPathSuffix is empty and + /// StructVal is invalid, then the value is the address of the Struct. If + /// StructVal is valid, the value is the address of an element within the + /// Struct. + void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, + Operand *StructVal) { + for (auto *UI : V->getUses()) { + // Keep the operand, not the instruction in the visited set. The same + // instruction may theoretically have different types of uses. + if (!Visited.insert(&*UI).second) { + continue; + } + + SILInstruction *UseInst = UI->getUser(); + + if (UseInst->isDebugInstruction()) + continue; + + if (StructVal) { + // Found a use of an element. + assert(AccessPathSuffix.empty() && "should have accessed struct"); + if (auto *LoadI = dyn_cast(UseInst)) { + ElementLoads.push_back(std::make_pair(LoadI, StructVal)); + continue; + } + + if (auto proj = dyn_cast(UseInst)) { + collectAddressUses(proj, AccessPathSuffix, StructVal); + continue; + } + + ElementAddressUsers.push_back(std::make_pair(UseInst,StructVal)); + continue; + } + + if (isa(UseInst) || isa(UseInst)) { + // Skip over unchecked_ref_cast and index_addr. + collectAddressUses(cast(UseInst), + AccessPathSuffix, nullptr); + continue; + } + + if (AccessPathSuffix.empty()) { + // Found a use of the struct at the given access path. + if (auto *LoadI = dyn_cast(UseInst)) { + StructLoads.push_back(LoadI); + continue; + } + + if (auto proj = dyn_cast(UseInst)) { + collectAddressUses(proj, AccessPathSuffix, &*UI); + continue; + } + + // Value users - this happens if we start with a value object in V. + if (definesSingleObjectType(V)) { + StructValueUsers.push_back(UseInst); + continue; + } + + StructAddressUsers.push_back(UseInst); + continue; + } + + // Check for uses of projections. + + // These are all single-value instructions. + auto *ProjInst = dyn_cast(UseInst); + if (!ProjInst) { + AggregateAddressUsers.push_back(UseInst); + continue; + } + ProjectionIndex PI(ProjInst); + // Do not form a path from an IndexAddrInst without otherwise + // distinguishing it from subelement addressing. + if (!PI.isValid()) { + // Found a use of an aggregate containing the given element. + AggregateAddressUsers.push_back(UseInst); + continue; + } + + if (PI.Index != AccessPathSuffix[0]) { + // Ignore uses of disjoint elements. + continue; + } + + // An alloc_box returns its address as the second value. + assert(PI.Aggregate && "Expected unary element addr inst."); + + // Recursively check for users after stripping this component from the + // access path. + collectAddressUses(ProjInst, AccessPathSuffix.slice(1), nullptr); + } + } +}; +} // namespace swift diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp new file mode 100644 index 0000000000000..d00580b7c4c00 --- /dev/null +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -0,0 +1,741 @@ +//===--- ArrayPropertyOpt.cpp - Optimize Array Properties -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// Optimize array property access by specializing loop bodies. +/// +/// This optimization specializes loops with calls to +/// "array.props.isNative/needsElementTypeCheck". +/// +/// The "array.props.isNative/needsElementTypeCheck" predicate has the property +/// that if it is true/false respectively for the array struct it is true/false +/// respectively until somebody writes a new array struct over the memory +/// location. Less abstractly, a fast native swift array does not transition to +/// a slow array (be it a cocoa array, or be it an array that needs type +/// checking) except if we store a new array to the variable that holds it. +/// +/// Using this property we can hoist the predicate above a region where no such +/// store can take place. +/// +/// func f(a : A[AClass]) { +/// for i in 0..a.count { +/// let b = a.props.isNative() +/// .. += _getElement(i, b) +/// } +/// } +/// +/// ==> +/// +/// func f(a : A[AClass]) { +/// let b = a.props.isNative +/// if (b) { +/// for i in 0..a.count { +/// .. += _getElement(i, false) +/// } +/// } else { +/// for i in 0..a.count { +/// let a = a.props.isNative +/// .. += _getElement(i, a) +/// } +/// } +/// } +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "array-property-opt" + +#include "ArrayOpt.h" +#include "swift/SILOptimizer/Analysis/LoopAnalysis.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/SILSSAUpdater.h" +#include "swift/SIL/CFG.h" +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/LoopInfo.h" +#include "swift/SIL/SILCloner.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +using namespace swift; + +namespace { +/// Analysis whether it is safe to specialize this loop nest based on the +/// array.props function calls it contains. It is safe to hoist array.props +/// calls if the array does not escape such that the array container could be +/// overwritten in the hoisted region. +/// This analysis also checks if we can clone the instructions in the loop nest. +class ArrayPropertiesAnalysis { + using UserList = StructUseCollector::UserList; + using UserOperList = StructUseCollector::UserOperList; + + SILFunction *Fun; + SILLoop *Loop; + SILBasicBlock *Preheader; + DominanceInfo *DomTree; + + llvm::SmallSet HoistableArray; + + SmallPtrSet ReachingBlocks; + SmallPtrSet CachedExitingBlocks; +public: + ArrayPropertiesAnalysis(SILLoop *L, DominanceAnalysis *DA) + : Fun(L->getHeader()->getParent()), Loop(L), Preheader(nullptr), + DomTree(DA->get(Fun)) {} + + bool run() { + Preheader = Loop->getLoopPreheader(); + if (!Preheader) { + LLVM_DEBUG(llvm::dbgs() << "ArrayPropertiesAnalysis: " + "Missing preheader for " + << *Loop); + return false; + } + + // Check whether this is a 'array.props' instruction and whether we + // can hoist it. Heuristic: We only want to hoist array.props instructions + // if we can hoist all of them - only then can we get rid of all the + // control-flow if we specialize. Hoisting some but not others is not as + // beneficial. This heuristic also simplifies which regions we want to + // specialize on. We will specialize the outermost loopnest that has + // 'array.props' instructions in its preheader. + bool FoundHoistable = false; + for (auto *BB : Loop->getBlocks()) { + for (auto &Inst : *BB) { + + // Can't clone alloc_stack instructions whose dealloc_stack is outside + // the loop. + if (!Loop->canDuplicate(&Inst)) + return false; + + ArraySemanticsCall ArrayPropsInst(&Inst, "array.props", true); + if (!ArrayPropsInst) + continue; + + if (!canHoistArrayPropsInst(ArrayPropsInst)) + return false; + FoundHoistable = true; + } + } + + return FoundHoistable; + } + +private: + + /// Strip the struct load and the address projection to the location + /// holding the array struct. + SILValue stripArrayStructLoad(SILValue V) { + if (auto LI = dyn_cast(V)) { + auto Val = LI->getOperand(); + // We could have two arrays in a surrounding container so we can only + // strip off the 'array struct' project. + // struct Container { + // var a1 : [ClassA] + // var a2 : [ClassA] + // } + // 'a1' and 'a2' are different arrays. + if (auto SEAI = dyn_cast(Val)) + Val = SEAI->getOperand(); + return Val; + } + return V; + } + + SmallPtrSetImpl &getReachingBlocks() { + if (ReachingBlocks.empty()) { + SmallVector Worklist; + ReachingBlocks.insert(Preheader); + Worklist.push_back(Preheader); + while (!Worklist.empty()) { + SILBasicBlock *BB = Worklist.pop_back_val(); + for (auto PI = BB->pred_begin(), PE = BB->pred_end(); PI != PE; ++PI) { + if (ReachingBlocks.insert(*PI).second) + Worklist.push_back(*PI); + } + } + } + return ReachingBlocks; + } + + /// Array address uses are safe if they don't store to the array struct. We + /// could for example store an NSArray array struct on top of the array. For + /// example, an opaque function that uses the array's address could store a + /// new array onto it. + bool checkSafeArrayAddressUses(UserList &AddressUsers) { + for (auto *UseInst : AddressUsers) { + + if (UseInst->isDebugInstruction()) + continue; + + if (isa(UseInst)) { + // Handle destruction of a local array. + continue; + } + + if (auto *AI = dyn_cast(UseInst)) { + if (ArraySemanticsCall(AI)) + continue; + + // Check if this escape can reach the current loop. + if (!Loop->contains(UseInst->getParent()) && + !getReachingBlocks().count(UseInst->getParent())) { + continue; + } + LLVM_DEBUG(llvm::dbgs() + << " Skipping Array: may escape through call!\n" + << " " << *UseInst); + return false; + } + + if (auto *StInst = dyn_cast(UseInst)) { + // Allow a local array to be initialized outside the loop via a by-value + // argument or return value. The array value may be returned by its + // initializer or some other factory function. + if (Loop->contains(StInst->getParent())) { + LLVM_DEBUG(llvm::dbgs() << " Skipping Array: store inside loop!\n" + << " " << *StInst); + return false; + } + SILValue InitArray = StInst->getSrc(); + if (isa(InitArray) || isa(InitArray)) + continue; + + return false; + } + + LLVM_DEBUG(llvm::dbgs() << " Skipping Array: unknown Array use!\n" + << " " << *UseInst); + // Found an unsafe or unknown user. The Array may escape here. + return false; + } + + // Otherwise, all of our users are sane. The array does not escape. + return true; + } + + /// Value uses are generally safe. We can't change the state of an array + /// through a value use. + bool checkSafeArrayValueUses(UserList &ValueUsers) { + return true; + } + bool checkSafeElementValueUses(UserOperList &ElementValueUsers) { + return true; + } + + // We have a safe container if the array container is passed as a function + // argument by-value or by inout reference. In either case there can't be an + // alias of the container. Alternatively, we can have a local variable. We + // will check in checkSafeArrayAddressUses that all initialization stores to + // this variable are safe (i.e the store dominates the loop etc). + bool isSafeArrayContainer(SILValue V) { + if (auto *Arg = dyn_cast(V)) { + // Check that the argument is passed as an inout or by value type. This + // means there are no aliases accessible within this function scope. + auto Params = Fun->getLoweredFunctionType()->getParameters(); + ArrayRef FunctionArgs = Fun->begin()->getArguments(); + for (unsigned ArgIdx = 0, ArgEnd = Params.size(); ArgIdx != ArgEnd; + ++ArgIdx) { + if (FunctionArgs[ArgIdx] != Arg) + continue; + + if (!Params[ArgIdx].isIndirectInOut() + && Params[ArgIdx].isFormalIndirect()) { + LLVM_DEBUG(llvm::dbgs() << " Skipping Array: Not an inout or " + "by val argument!\n"); + return false; + } + } + return true; + } else if (isa(V)) + return true; + + LLVM_DEBUG(llvm::dbgs() + << " Skipping Array: Not a know array container type!\n"); + + return false; + } + + SmallPtrSetImpl &getLoopExitingBlocks() { + if (!CachedExitingBlocks.empty()) + return CachedExitingBlocks; + SmallVector ExitingBlocks; + Loop->getExitingBlocks(ExitingBlocks); + CachedExitingBlocks.insert(ExitingBlocks.begin(), ExitingBlocks.end()); + return CachedExitingBlocks; + } + + bool isConditionallyExecuted(ArraySemanticsCall Call) { + auto CallBB = (*Call).getParent(); + for (auto *ExitingBlk : getLoopExitingBlocks()) + if (!DomTree->dominates(CallBB, ExitingBlk)) + return true; + return false; + } + + bool isClassElementTypeArray(SILValue Arr) { + auto Ty = Arr->getType(); + if (auto BGT = Ty.getAs()) { + // Check the array element type parameter. + bool isClass = false; + for (auto EltTy : BGT->getGenericArgs()) { + if (!EltTy->hasReferenceSemantics()) + return false; + isClass = true; + } + return isClass; + } + return false; + } + + bool canHoistArrayPropsInst(ArraySemanticsCall Call) { + // TODO: This is way conservative. If there is an unconditionally + // executed call to the same array we can still hoist it. + if (isConditionallyExecuted(Call)) + return false; + + SILValue Arr = Call.getSelf(); + + // We don't attempt to hoist non-class element type arrays. + if (!isClassElementTypeArray(Arr)) + return false; + + // We can strip the load that might even occur in the loop because we make + // sure that no unsafe store to the array's address takes place. + Arr = stripArrayStructLoad(Arr); + + // Have we already seen this array and deemed it safe? + if (HoistableArray.count(Arr)) + return true; + + // Do we know how to hoist the arguments of this call. + if (!Call.canHoist(Preheader->getTerminator(), DomTree)) + return false; + + SmallVector AccessPath; + SILValue ArrayContainer = + StructUseCollector::getAccessPath(Arr, AccessPath); + + if (!isSafeArrayContainer(ArrayContainer)) + return false; + + StructUseCollector StructUses; + StructUses.collectUses(ArrayContainer, AccessPath); + + if (!checkSafeArrayAddressUses(StructUses.AggregateAddressUsers) || + !checkSafeArrayAddressUses(StructUses.StructAddressUsers) || + !checkSafeArrayValueUses(StructUses.StructValueUsers) || + !checkSafeElementValueUses(StructUses.ElementValueUsers) || + !StructUses.ElementAddressUsers.empty()) + return false; + + HoistableArray.insert(Arr); + return true; + } +}; +} // end anonymous namespace + +namespace { +/// Clone a single exit multiple exit region starting at basic block and ending +/// in a set of basic blocks. Updates the dominator tree with the cloned blocks. +/// However, the client needs to update the dominator of the exit blocks. +/// +/// FIXME: SILCloner is used to cloned CFG regions by multiple clients. All +/// functionality for generating valid SIL (including the DomTree) should be +/// handled by the common SILCloner. +class RegionCloner : public SILCloner { + DominanceInfo &DomTree; + SILBasicBlock *StartBB; + + friend class SILInstructionVisitor; + friend class SILCloner; + +public: + RegionCloner(SILBasicBlock *EntryBB, DominanceInfo &DT) + : SILCloner(*EntryBB->getParent()), DomTree(DT), + StartBB(EntryBB) {} + + SILBasicBlock *cloneRegion(ArrayRef exitBBs) { + assert (DomTree.getNode(StartBB) != nullptr && "Can't cloned dead code"); + + // We need to split any edge from a non cond_br basic block leading to a + // exit block. After cloning this edge will become critical if it came from + // inside the cloned region. The SSAUpdater can't handle critical non + // cond_br edges. + // + // FIXME: remove this in the next commit. The SILCloner will always do it. + for (auto *BB : exitBBs) { + SmallVector Preds(BB->getPredecessorBlocks()); + for (auto *Pred : Preds) + if (!isa(Pred->getTerminator()) && + !isa(Pred->getTerminator())) + splitEdgesFromTo(Pred, BB, &DomTree, nullptr); + } + + cloneReachableBlocks(StartBB, exitBBs); + + // Add dominator tree nodes for the new basic blocks. + fixDomTree(); + + // Update SSA form for values used outside of the copied region. + updateSSAForm(); + return getOpBasicBlock(StartBB); + } + +protected: + /// Clone the dominator tree from the original region to the cloned region. + void fixDomTree() { + for (auto *BB : originalPreorderBlocks()) { + auto *ClonedBB = getOpBasicBlock(BB); + auto *OrigDomBB = DomTree.getNode(BB)->getIDom()->getBlock(); + if (BB == StartBB) { + // The cloned start node shares the same dominator as the original node. + auto *ClonedNode = DomTree.addNewBlock(ClonedBB, OrigDomBB); + (void)ClonedNode; + assert(ClonedNode); + continue; + } + // Otherwise, map the dominator structure using the mapped block. + DomTree.addNewBlock(ClonedBB, getOpBasicBlock(OrigDomBB)); + } + } + + SILValue getMappedValue(SILValue V) { + if (auto *BB = V->getParentBlock()) { + if (!DomTree.dominates(StartBB, BB)) { + // Must be a value that dominates the start basic block. + assert(DomTree.dominates(BB, StartBB) && + "Must dominated the start of the cloned region"); + return V; + } + } + return SILCloner::getMappedValue(V); + } + + void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { + SILCloner::postProcess(Orig, Cloned); + } + + /// Update SSA form for values that are used outside the region. + void updateSSAForValue(SILBasicBlock *OrigBB, SILValue V, + SILSSAUpdater &SSAUp) { + // Collect outside uses. + SmallVector UseList; + for (auto Use : V->getUses()) + if (!isBlockCloned(Use->getUser()->getParent())) { + UseList.push_back(UseWrapper(Use)); + } + if (UseList.empty()) + return; + + // Update SSA form. + SSAUp.Initialize(V->getType()); + SSAUp.AddAvailableValue(OrigBB, V); + SILValue NewVal = getMappedValue(V); + SSAUp.AddAvailableValue(getOpBasicBlock(OrigBB), NewVal); + for (auto U : UseList) { + Operand *Use = U; + SSAUp.RewriteUse(*Use); + } + } + + void updateSSAForm() { + SILSSAUpdater SSAUp; + for (auto *origBB : originalPreorderBlocks()) { + // Update outside used phi values. + for (auto *arg : origBB->getArguments()) + updateSSAForValue(origBB, arg, SSAUp); + + // Update outside used instruction values. + for (auto &inst : *origBB) { + for (auto result : inst.getResults()) + updateSSAForValue(origBB, result, SSAUp); + } + } + } +}; +} // end anonymous namespace + +namespace { +/// This class transforms a hoistable loop nest into a speculatively specialized +/// loop based on array.props calls. +class ArrayPropertiesSpecializer { + DominanceInfo *DomTree; + SILLoopAnalysis *LoopAnalysis; + SILBasicBlock *HoistableLoopPreheader; + +public: + ArrayPropertiesSpecializer(DominanceInfo *DT, SILLoopAnalysis *LA, + SILBasicBlock *Hoistable) + : DomTree(DT), LoopAnalysis(LA), HoistableLoopPreheader(Hoistable) {} + + void run() { + specializeLoopNest(); + } + + SILLoop *getLoop() { + auto *LoopInfo = LoopAnalysis->get(HoistableLoopPreheader->getParent()); + return LoopInfo->getLoopFor( + HoistableLoopPreheader->getSingleSuccessorBlock()); + } + +protected: + void specializeLoopNest(); +}; +} // end anonymous namespace + +static SILValue createStructExtract(SILBuilder &B, SILLocation Loc, + SILValue Opd, unsigned FieldNo) { + SILType Ty = Opd->getType(); + auto SD = Ty.getStructOrBoundGenericStruct(); + auto Properties = SD->getStoredProperties(); + unsigned Counter = 0; + for (auto *D : Properties) + if (Counter++ == FieldNo) + return B.createStructExtract(Loc, Opd, D); + llvm_unreachable("Wrong field number"); +} + +static Identifier getBinaryFunction(StringRef Name, SILType IntSILTy, + ASTContext &C) { + auto IntTy = IntSILTy.castTo(); + unsigned NumBits = IntTy->getWidth().getFixedWidth(); + // Name is something like: add_Int64 + std::string NameStr = Name; + NameStr += "_Int" + llvm::utostr(NumBits); + return C.getIdentifier(NameStr); +} + +/// Create a binary and function. +static SILValue createAnd(SILBuilder &B, SILLocation Loc, SILValue Opd1, + SILValue Opd2) { + auto AndFn = getBinaryFunction("and", Opd1->getType(), B.getASTContext()); + SILValue Args[] = {Opd1, Opd2}; + return B.createBuiltin(Loc, AndFn, Opd1->getType(), {}, Args); +} + +/// Create a check over all array.props calls that they have the 'fast native +/// swift' array value: isNative && !needsElementTypeCheck must be true. +static SILValue +createFastNativeArraysCheck(SmallVectorImpl &ArrayProps, + SILBuilder &B) { + assert(!ArrayProps.empty() && "Must have array.pros calls"); + + SILType IntBoolTy = SILType::getBuiltinIntegerType(1, B.getASTContext()); + SILValue Result = + B.createIntegerLiteral((*ArrayProps[0]).getLoc(), IntBoolTy, 1); + + for (auto Call : ArrayProps) { + auto Loc = (*Call).getLoc(); + auto CallKind = Call.getKind(); + if (CallKind == ArrayCallKind::kArrayPropsIsNativeTypeChecked) { + auto Val = createStructExtract(B, Loc, SILValue(Call), 0); + Result = createAnd(B, Loc, Result, Val); + } + } + return Result; +} + +/// Collect all array.props calls in the cloned basic blocks stored in the map, +/// asserting that we found at least one. +static void collectArrayPropsCalls(RegionCloner &Cloner, + SmallVectorImpl &ExitBlocks, + SmallVectorImpl &Calls) { + for (auto *origBB : Cloner.originalPreorderBlocks()) { + auto clonedBB = Cloner.getOpBasicBlock(origBB); + for (auto &Inst : *clonedBB) { + ArraySemanticsCall ArrayProps(&Inst, "array.props", true); + if (!ArrayProps) + continue; + Calls.push_back(ArrayProps); + } + } + assert(!Calls.empty() && "Should have a least one array.props call"); +} + +/// Replace an array.props call by the 'fast swift array' value. +/// +/// This is true for array.props.isNative and false for +/// array.props.needsElementTypeCheck. +static void replaceArrayPropsCall(SILBuilder &B, ArraySemanticsCall C) { + assert(C.getKind() == ArrayCallKind::kArrayPropsIsNativeTypeChecked); + ApplyInst *AI = C; + + SILType IntBoolTy = SILType::getBuiltinIntegerType(1, B.getASTContext()); + + auto BoolTy = AI->getType(); + auto C0 = B.createIntegerLiteral(AI->getLoc(), IntBoolTy, 1); + auto BoolVal = B.createStruct(AI->getLoc(), BoolTy, {C0}); + + (*C).replaceAllUsesWith(BoolVal); + // Remove call to array.props.read/write. + C.removeCall(); +} + +/// Collects all loop dominated blocks outside the loop that are immediately +/// dominated by the loop. +static void +collectImmediateLoopDominatedBlocks(const SILLoop *Lp, DominanceInfoNode *Node, + SmallVectorImpl &Blocks) { + SILBasicBlock *BB = Node->getBlock(); + + // Base case: First loop dominated block outside of loop. + if (!Lp->contains(BB)) { + Blocks.push_back(BB); + return; + } + + // Loop contains the basic block. Look at immediately dominated nodes. + for (auto *Child : *Node) + collectImmediateLoopDominatedBlocks(Lp, Child, Blocks); +} + +void ArrayPropertiesSpecializer::specializeLoopNest() { + auto *Lp = getLoop(); + assert(Lp); + + // Split of a new empty preheader. We don't want to duplicate the whole + // original preheader it might contain instructions that we can't clone. + // This will be block that will contain the check whether to execute the + // 'native swift array' loop or the original loop. + SILBuilder B(HoistableLoopPreheader); + auto *CheckBlock = splitBasicBlockAndBranch(B, + HoistableLoopPreheader->getTerminator(), DomTree, nullptr); + + auto *Header = CheckBlock->getSingleSuccessorBlock(); + assert(Header); + + // Collect all loop dominated blocks (e.g exit blocks could be among them). We + // need to update their dominator. + SmallVector LoopDominatedBlocks; + collectImmediateLoopDominatedBlocks(Lp, DomTree->getNode(Header), + LoopDominatedBlocks); + + // Collect all exit blocks. + SmallVector ExitBlocks; + Lp->getExitBlocks(ExitBlocks); + + // Split the preheader before the first instruction. + SILBasicBlock *NewPreheader = + splitBasicBlockAndBranch(B, &*CheckBlock->begin(), DomTree, nullptr); + + // Clone the region from the new preheader up to (not including) the exit + // blocks. This creates a second loop nest. + RegionCloner Cloner(NewPreheader, *DomTree); + auto *ClonedPreheader = Cloner.cloneRegion(ExitBlocks); + + // Collect the array.props call that we will specialize on that we have + // cloned in the cloned loop. + SmallVector ArrayPropCalls; + collectArrayPropsCalls(Cloner, ExitBlocks, ArrayPropCalls); + + // Move them to the check block. + SmallVector HoistedArrayPropCalls; + for (auto C: ArrayPropCalls) + HoistedArrayPropCalls.push_back( + ArraySemanticsCall(C.copyTo(CheckBlock->getTerminator(), DomTree))); + + // Create a conditional branch on the fast condition being true. + B.setInsertionPoint(CheckBlock->getTerminator()); + auto IsFastNativeArray = + createFastNativeArraysCheck(HoistedArrayPropCalls, B); + B.createCondBranch(CheckBlock->getTerminator()->getLoc(), + IsFastNativeArray, ClonedPreheader, NewPreheader); + CheckBlock->getTerminator()->eraseFromParent(); + + // Fixup the loop dominated blocks. They are now dominated by the check block. + for (auto *BB : LoopDominatedBlocks) + DomTree->changeImmediateDominator(DomTree->getNode(BB), + DomTree->getNode(CheckBlock)); + + // Replace the array.props calls uses in the cloned loop by their 'fast' + // value. + SILBuilder B2(ClonedPreheader->getTerminator()); + for (auto C : ArrayPropCalls) + replaceArrayPropsCall(B2, C); + + // We have potentially cloned a loop - invalidate loop info. + LoopAnalysis->invalidate(Header->getParent(), + SILAnalysis::InvalidationKind::FunctionBody); +} + +namespace { +class SwiftArrayPropertyOptPass : public SILFunctionTransform { + + void run() override { + auto *Fn = getFunction(); + + // FIXME: Add support for ownership. + if (Fn->hasOwnership()) + return; + + // Don't hoist array property calls at Osize. + if (Fn->optimizeForSize()) + return; + + DominanceAnalysis *DA = PM->getAnalysis(); + SILLoopAnalysis *LA = PM->getAnalysis(); + SILLoopInfo *LI = LA->get(Fn); + + bool HasChanged = false; + + // Check whether we can hoist 'array.props' calls out of loops, collecting + // the preheader we can hoist to. We only hoist out of loops if 'all' + // array.props call can be hoisted for a given loop nest. + // We process the loop tree preorder (top-down) to hoist over the biggest + // possible loop-nest. + SmallVector HoistableLoopNests; + std::function processChildren = [&](SILLoop *L) { + ArrayPropertiesAnalysis Analysis(L, DA); + if (Analysis.run()) { + // Hoist in the current loop nest. + HasChanged = true; + HoistableLoopNests.push_back(L->getLoopPreheader()); + } else { + // Otherwise, try hoisting sub-loops. + for (auto *SubLoop : *L) + processChildren(SubLoop); + } + }; + for (auto *L : *LI) + processChildren(L); + + // Specialize the identified loop nest based on the 'array.props' calls. + if (HasChanged) { + LLVM_DEBUG(getFunction()->viewCFG()); + DominanceInfo *DT = DA->get(getFunction()); + + // Process specialized loop-nests in loop-tree post-order (bottom-up). + std::reverse(HoistableLoopNests.begin(), HoistableLoopNests.end()); + + // Hoist the loop nests. + for (auto &HoistableLoopNest : HoistableLoopNests) + ArrayPropertiesSpecializer(DT, LA, HoistableLoopNest).run(); + + // Verify that no illegal critical edges were created. + getFunction()->verifyCriticalEdges(); + + LLVM_DEBUG(getFunction()->viewCFG()); + + // We preserve the dominator tree. Let's invalidate everything + // else. + DA->lockInvalidation(); + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); + DA->unlockInvalidation(); + } + } + +}; +} // end anonymous namespace + +SILTransform *swift::createSwiftArrayPropertyOpt() { + return new SwiftArrayPropertyOptPass(); +} diff --git a/lib/SILOptimizer/LoopTransforms/CMakeLists.txt b/lib/SILOptimizer/LoopTransforms/CMakeLists.txt index ecd44b2a4ceac..25ebb3572ef91 100644 --- a/lib/SILOptimizer/LoopTransforms/CMakeLists.txt +++ b/lib/SILOptimizer/LoopTransforms/CMakeLists.txt @@ -1,5 +1,6 @@ silopt_register_sources( ArrayBoundsCheckOpts.cpp + ArrayPropertyOpt.cpp COWArrayOpt.cpp LoopRotate.cpp LoopUnroll.cpp diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index b7838207e27af..b4265a2690aa3 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -9,15 +9,21 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +/// +/// Optimize CoW array access by hoisting uniqueness checks. +/// +//===----------------------------------------------------------------------===// #define DEBUG_TYPE "cowarray-opts" + +#include "ArrayOpt.h" #include "swift/SIL/CFG.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/LoopInfo.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" -#include "swift/SIL/SILCloner.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" @@ -29,228 +35,13 @@ #include "swift/SILOptimizer/Analysis/ValueTracking.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" -#include "swift/SILOptimizer/Utils/SILSSAUpdater.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; -/// \return a sequence of integers representing the access path of this element -/// within a Struct/Ref/Tuple. -/// -/// Do not form a path with an IndexAddrInst because we have no way to -/// distinguish between indexing and subelement access. The same index could -/// either refer to the next element (indexed) or a subelement. -static SILValue getAccessPath(SILValue V, SmallVectorImpl& Path) { - V = stripCasts(V); - if (auto *IA = dyn_cast(V)) { - // Don't include index_addr projections in the access path. We could if - // the index is constant. For simplicity we just ignore them. - V = stripCasts(IA->getBase()); - } - ProjectionIndex PI(V); - if (!PI.isValid()) - return V; - - SILValue UnderlyingObject = getAccessPath(PI.Aggregate, Path); - Path.push_back(PI.Index); - return UnderlyingObject; -} - -namespace { -/// Collect all uses of a struct given an aggregate value that contains the -/// struct and access path describing the projection of the aggregate -/// that accesses the struct. -/// -/// AggregateAddressUsers records uses of the aggregate value's address. These -/// may indirectly access the struct's elements. -/// -/// Projections over the aggregate that do not access the struct are ignored. -/// -/// StructLoads records loads of the struct value. -/// StructAddressUsers records other uses of the struct address. -/// StructValueUsers records direct uses of the loaded struct. -/// -/// Projections of the struct over its elements are all similarly recorded in -/// ElementAddressUsers, ElementLoads, and ElementValueUsers. -/// -/// bb0(%arg : $*S) -/// apply %f(%arg) // <--- Aggregate Address User -/// %struct_addr = struct_element_addr %arg : $*S, #S.element -/// apply %g(%struct_addr) // <--- Struct Address User -/// %val = load %struct_addr // <--- Struct Load -/// apply %h(%val) // <--- Struct Value User -/// %elt_addr = struct_element_addr %struct_addr : $*A, #A.element -/// apply %i(%elt_addr) // <--- Element Address User -/// %elt = load %elt_addr // <--- Element Load -/// apply %j(%elt) // <--- Element Value User -class StructUseCollector { -public: - typedef SmallPtrSet VisitedSet; - typedef SmallVector UserList; - - /// Record the users of a value or an element within that value along with the - /// operand that directly uses the value. Multiple levels of struct_extract - /// may exist between the operand and the user instruction. - typedef SmallVector, 16> UserOperList; - - UserList AggregateAddressUsers; - UserList StructAddressUsers; - SmallVector StructLoads; - UserList StructValueUsers; - UserOperList ElementAddressUsers; - SmallVector, 16> ElementLoads; - UserOperList ElementValueUsers; - VisitedSet Visited; - - /// Collect all uses of the value at the given address. - void collectUses(ValueBase *V, ArrayRef AccessPath) { - // Save our old indent and increment. - // Collect all users of the address and loads. - collectAddressUses(V, AccessPath, nullptr); - - // Collect all uses of the Struct value. - for (auto *DefInst : StructLoads) { - for (auto *DefUI : DefInst->getUses()) { - if (!Visited.insert(&*DefUI).second) { - continue; - } - - StructValueUsers.push_back(DefUI->getUser()); - } - } - - // Collect all users of element values. - for (auto &Pair : ElementLoads) { - for (auto *DefUI : Pair.first->getUses()) { - if (!Visited.insert(&*DefUI).second) { - continue; - } - - ElementValueUsers.push_back( - std::make_pair(DefUI->getUser(), Pair.second)); - } - } - } - - /// Returns true if there is a single address user of the value. - bool hasSingleAddressUse(SILInstruction *SingleAddressUser) { - if (!AggregateAddressUsers.empty()) - return false; - if (!ElementAddressUsers.empty()) - return false; - if (StructAddressUsers.size() != 1) - return false; - return StructAddressUsers[0] == SingleAddressUser; - } - -protected: - - static bool definesSingleObjectType(ValueBase *V) { - return V->getType().isObject(); - } - - /// If AccessPathSuffix is non-empty, then the value is the address of an - /// aggregate containing the Struct. If AccessPathSuffix is empty and - /// StructVal is invalid, then the value is the address of the Struct. If - /// StructVal is valid, the value is the address of an element within the - /// Struct. - void collectAddressUses(ValueBase *V, ArrayRef AccessPathSuffix, - Operand *StructVal) { - for (auto *UI : V->getUses()) { - // Keep the operand, not the instruction in the visited set. The same - // instruction may theoretically have different types of uses. - if (!Visited.insert(&*UI).second) { - continue; - } - - SILInstruction *UseInst = UI->getUser(); - - if (UseInst->isDebugInstruction()) - continue; - - if (StructVal) { - // Found a use of an element. - assert(AccessPathSuffix.empty() && "should have accessed struct"); - if (auto *LoadI = dyn_cast(UseInst)) { - ElementLoads.push_back(std::make_pair(LoadI, StructVal)); - continue; - } - - if (auto proj = dyn_cast(UseInst)) { - collectAddressUses(proj, AccessPathSuffix, StructVal); - continue; - } - - ElementAddressUsers.push_back(std::make_pair(UseInst,StructVal)); - continue; - } - - if (isa(UseInst) || isa(UseInst)) { - // Skip over unchecked_ref_cast and index_addr. - collectAddressUses(cast(UseInst), - AccessPathSuffix, nullptr); - continue; - } - - if (AccessPathSuffix.empty()) { - // Found a use of the struct at the given access path. - if (auto *LoadI = dyn_cast(UseInst)) { - StructLoads.push_back(LoadI); - continue; - } - - if (auto proj = dyn_cast(UseInst)) { - collectAddressUses(proj, AccessPathSuffix, &*UI); - continue; - } - - // Value users - this happens if we start with a value object in V. - if (definesSingleObjectType(V)) { - StructValueUsers.push_back(UseInst); - continue; - } - - StructAddressUsers.push_back(UseInst); - continue; - } - - // Check for uses of projections. - - // These are all single-value instructions. - auto *ProjInst = dyn_cast(UseInst); - if (!ProjInst) { - AggregateAddressUsers.push_back(UseInst); - continue; - } - ProjectionIndex PI(ProjInst); - // Do not form a path from an IndexAddrInst without otherwise - // distinguishing it from subelement addressing. - if (!PI.isValid()) { - // Found a use of an aggregate containing the given element. - AggregateAddressUsers.push_back(UseInst); - continue; - } - - if (PI.Index != AccessPathSuffix[0]) { - // Ignore uses of disjoint elements. - continue; - } - - // An alloc_box returns its address as the second value. - assert(PI.Aggregate && "Expected unary element addr inst."); - - // Recursively check for users after stripping this component from the - // access path. - collectAddressUses(ProjInst, AccessPathSuffix.slice(1), nullptr); - } - } -}; -} // end anonymous namespace - // Do the two values \p A and \p B reference the same 'array' after potentially // looking through a load. To identify a common array address this functions // strips struct projections until it hits \p ArrayAddress. @@ -1050,18 +841,24 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable, } SmallVector AccessPath; - SILValue ArrayContainer = getAccessPath(CurrentArrayAddr, AccessPath); + SILValue ArrayContainer = + StructUseCollector::getAccessPath(CurrentArrayAddr, AccessPath); bool arrayContainerIsUnique = checkUniqueArrayContainer(ArrayContainer); StructUseCollector StructUses; // Check whether we can hoist make_mutable based on the operations that are // in the loop. - // Note that in this case we don't verify that the array buffer is not aliased - // and therefore we must be conservative if the make_mutable is executed - // conditionally (i.e. doesn't dominate all exit blocks). - // The test SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally - // shows the problem. + // + // Hoisting make_mutable releases the original array storage. If an alias of + // that storage is accessed on any path reachable from the loop header that + // doesn't already pass through the make_mutable, then hoisting is + // illegal. hasLoopOnlyDestructorSafeArrayOperations checks that the array + // storage is not accessed within the loop. However, this does not include + // paths exiting the loop. Rather than analyzing code outside the loop, simply + // check that the original make_mutable dominates all exits. The test + // SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally shows + // the problem. if (hasLoopOnlyDestructorSafeArrayOperations() && dominatesExits) { // Done. We can hoist the make_mutable. // We still need the array uses later to check if we can add loads to @@ -1218,718 +1015,3 @@ class COWArrayOptPass : public SILFunctionTransform { SILTransform *swift::createCOWArrayOpts() { return new COWArrayOptPass(); } - -namespace { - -/// This optimization specializes loops with calls to -/// "array.props.isNative/needsElementTypeCheck". -/// -/// The "array.props.isNative/needsElementTypeCheck" predicate has the property -/// that if it is true/false respectively for the array struct it is true/false -/// respectively until somebody writes a new array struct over the memory -/// location. Less abstractly, a fast native swift array does not transition to -/// a slow array (be it a cocoa array, or be it an array that needs type -/// checking) except if we store a new array to the variable that holds it. -/// -/// Using this property we can hoist the predicate above a region where no such -/// store can take place. -/// -/// func f(a : A[AClass]) { -/// for i in 0..a.count { -/// let b = a.props.isNative() -/// .. += _getElement(i, b) -/// } -/// } -/// -/// ==> -/// -/// func f(a : A[AClass]) { -/// let b = a.props.isNative -/// if (b) { -/// for i in 0..a.count { -/// .. += _getElement(i, false) -/// } -/// } else { -/// for i in 0..a.count { -/// let a = a.props.isNative -/// .. += _getElement(i, a) -/// } -/// } -/// } -/// -static llvm::cl::opt ShouldSpecializeArrayProps("sil-array-props", - llvm::cl::init(true)); - -/// Analysis whether it is safe to specialize this loop nest based on the -/// array.props function calls it contains. It is safe to hoist array.props -/// calls if the array does not escape such that the array container could be -/// overwritten in the hoisted region. -/// This analysis also checks if we can clone the instructions in the loop nest. -class ArrayPropertiesAnalysis { - using UserList = StructUseCollector::UserList; - using UserOperList = StructUseCollector::UserOperList; - - SILFunction *Fun; - SILLoop *Loop; - SILBasicBlock *Preheader; - DominanceInfo *DomTree; - - llvm::SmallSet HoistableArray; - - SmallPtrSet ReachingBlocks; - SmallPtrSet CachedExitingBlocks; -public: - ArrayPropertiesAnalysis(SILLoop *L, DominanceAnalysis *DA) - : Fun(L->getHeader()->getParent()), Loop(L), Preheader(nullptr), - DomTree(DA->get(Fun)) {} - - bool run() { - Preheader = Loop->getLoopPreheader(); - if (!Preheader) { - LLVM_DEBUG(llvm::dbgs() << "ArrayPropertiesAnalysis: " - "Missing preheader for " - << *Loop); - return false; - } - - // Check whether this is a 'array.props' instruction and whether we - // can hoist it. Heuristic: We only want to hoist array.props instructions - // if we can hoist all of them - only then can we get rid of all the - // control-flow if we specialize. Hoisting some but not others is not as - // beneficial. This heuristic also simplifies which regions we want to - // specialize on. We will specialize the outermost loopnest that has - // 'array.props' instructions in its preheader. - bool FoundHoistable = false; - for (auto *BB : Loop->getBlocks()) { - for (auto &Inst : *BB) { - - // Can't clone alloc_stack instructions whose dealloc_stack is outside - // the loop. - if (!Loop->canDuplicate(&Inst)) - return false; - - ArraySemanticsCall ArrayPropsInst(&Inst, "array.props", true); - if (!ArrayPropsInst) - continue; - - if (!canHoistArrayPropsInst(ArrayPropsInst)) - return false; - FoundHoistable = true; - } - } - - return FoundHoistable; - } - -private: - - /// Strip the struct load and the address projection to the location - /// holding the array struct. - SILValue stripArrayStructLoad(SILValue V) { - if (auto LI = dyn_cast(V)) { - auto Val = LI->getOperand(); - // We could have two arrays in a surrounding container so we can only - // strip off the 'array struct' project. - // struct Container { - // var a1 : [ClassA] - // var a2 : [ClassA] - // } - // 'a1' and 'a2' are different arrays. - if (auto SEAI = dyn_cast(Val)) - Val = SEAI->getOperand(); - return Val; - } - return V; - } - - SmallPtrSetImpl &getReachingBlocks() { - if (ReachingBlocks.empty()) { - SmallVector Worklist; - ReachingBlocks.insert(Preheader); - Worklist.push_back(Preheader); - while (!Worklist.empty()) { - SILBasicBlock *BB = Worklist.pop_back_val(); - for (auto PI = BB->pred_begin(), PE = BB->pred_end(); PI != PE; ++PI) { - if (ReachingBlocks.insert(*PI).second) - Worklist.push_back(*PI); - } - } - } - return ReachingBlocks; - } - - /// Array address uses are safe if they don't store to the array struct. We - /// could for example store an NSArray array struct on top of the array. For - /// example, an opaque function that uses the array's address could store a - /// new array onto it. - bool checkSafeArrayAddressUses(UserList &AddressUsers) { - for (auto *UseInst : AddressUsers) { - - if (UseInst->isDebugInstruction()) - continue; - - if (isa(UseInst)) { - // Handle destruction of a local array. - continue; - } - - if (auto *AI = dyn_cast(UseInst)) { - if (ArraySemanticsCall(AI)) - continue; - - // Check if this escape can reach the current loop. - if (!Loop->contains(UseInst->getParent()) && - !getReachingBlocks().count(UseInst->getParent())) { - continue; - } - LLVM_DEBUG(llvm::dbgs() - << " Skipping Array: may escape through call!\n" - << " " << *UseInst); - return false; - } - - if (auto *StInst = dyn_cast(UseInst)) { - // Allow a local array to be initialized outside the loop via a by-value - // argument or return value. The array value may be returned by its - // initializer or some other factory function. - if (Loop->contains(StInst->getParent())) { - LLVM_DEBUG(llvm::dbgs() << " Skipping Array: store inside loop!\n" - << " " << *StInst); - return false; - } - SILValue InitArray = StInst->getSrc(); - if (isa(InitArray) || isa(InitArray)) - continue; - - return false; - } - - LLVM_DEBUG(llvm::dbgs() << " Skipping Array: unknown Array use!\n" - << " " << *UseInst); - // Found an unsafe or unknown user. The Array may escape here. - return false; - } - - // Otherwise, all of our users are sane. The array does not escape. - return true; - } - - /// Value uses are generally safe. We can't change the state of an array - /// through a value use. - bool checkSafeArrayValueUses(UserList &ValueUsers) { - return true; - } - bool checkSafeElementValueUses(UserOperList &ElementValueUsers) { - return true; - } - - // We have a safe container if the array container is passed as a function - // argument by-value or by inout reference. In either case there can't be an - // alias of the container. Alternatively, we can have a local variable. We - // will check in checkSafeArrayAddressUses that all initialization stores to - // this variable are safe (i.e the store dominates the loop etc). - bool isSafeArrayContainer(SILValue V) { - if (auto *Arg = dyn_cast(V)) { - // Check that the argument is passed as an inout or by value type. This - // means there are no aliases accessible within this function scope. - auto Params = Fun->getLoweredFunctionType()->getParameters(); - ArrayRef FunctionArgs = Fun->begin()->getArguments(); - for (unsigned ArgIdx = 0, ArgEnd = Params.size(); ArgIdx != ArgEnd; - ++ArgIdx) { - if (FunctionArgs[ArgIdx] != Arg) - continue; - - if (!Params[ArgIdx].isIndirectInOut() - && Params[ArgIdx].isFormalIndirect()) { - LLVM_DEBUG(llvm::dbgs() << " Skipping Array: Not an inout or " - "by val argument!\n"); - return false; - } - } - return true; - } else if (isa(V)) - return true; - - LLVM_DEBUG(llvm::dbgs() - << " Skipping Array: Not a know array container type!\n"); - - return false; - } - - SmallPtrSetImpl &getLoopExitingBlocks() { - if (!CachedExitingBlocks.empty()) - return CachedExitingBlocks; - SmallVector ExitingBlocks; - Loop->getExitingBlocks(ExitingBlocks); - CachedExitingBlocks.insert(ExitingBlocks.begin(), ExitingBlocks.end()); - return CachedExitingBlocks; - } - - bool isConditionallyExecuted(ArraySemanticsCall Call) { - auto CallBB = (*Call).getParent(); - for (auto *ExitingBlk : getLoopExitingBlocks()) - if (!DomTree->dominates(CallBB, ExitingBlk)) - return true; - return false; - } - - bool isClassElementTypeArray(SILValue Arr) { - auto Ty = Arr->getType(); - if (auto BGT = Ty.getAs()) { - // Check the array element type parameter. - bool isClass = false; - for (auto EltTy : BGT->getGenericArgs()) { - if (!EltTy->hasReferenceSemantics()) - return false; - isClass = true; - } - return isClass; - } - return false; - } - - bool canHoistArrayPropsInst(ArraySemanticsCall Call) { - // TODO: This is way conservative. If there is an unconditionally - // executed call to the same array we can still hoist it. - if (isConditionallyExecuted(Call)) - return false; - - SILValue Arr = Call.getSelf(); - - // We don't attempt to hoist non-class element type arrays. - if (!isClassElementTypeArray(Arr)) - return false; - - // We can strip the load that might even occur in the loop because we make - // sure that no unsafe store to the array's address takes place. - Arr = stripArrayStructLoad(Arr); - - // Have we already seen this array and deemed it safe? - if (HoistableArray.count(Arr)) - return true; - - // Do we know how to hoist the arguments of this call. - if (!Call.canHoist(Preheader->getTerminator(), DomTree)) - return false; - - SmallVector AccessPath; - SILValue ArrayContainer = getAccessPath(Arr, AccessPath); - - if (!isSafeArrayContainer(ArrayContainer)) - return false; - - StructUseCollector StructUses; - StructUses.collectUses(ArrayContainer, AccessPath); - - if (!checkSafeArrayAddressUses(StructUses.AggregateAddressUsers) || - !checkSafeArrayAddressUses(StructUses.StructAddressUsers) || - !checkSafeArrayValueUses(StructUses.StructValueUsers) || - !checkSafeElementValueUses(StructUses.ElementValueUsers) || - !StructUses.ElementAddressUsers.empty()) - return false; - - HoistableArray.insert(Arr); - return true; - } -}; -} // end anonymous namespace - -namespace { -/// Clone a single exit multiple exit region starting at basic block and ending -/// in a set of basic blocks. Updates the dominator tree with the cloned blocks. -/// However, the client needs to update the dominator of the exit blocks. -/// -/// FIXME: SILCloner is used to cloned CFG regions by multiple clients. All -/// functionality for generating valid SIL (including the DomTree) should be -/// handled by the common SILCloner. -class RegionCloner : public SILCloner { - DominanceInfo &DomTree; - SILBasicBlock *StartBB; - - friend class SILInstructionVisitor; - friend class SILCloner; - -public: - RegionCloner(SILBasicBlock *EntryBB, DominanceInfo &DT) - : SILCloner(*EntryBB->getParent()), DomTree(DT), - StartBB(EntryBB) {} - - SILBasicBlock *cloneRegion(ArrayRef exitBBs) { - assert (DomTree.getNode(StartBB) != nullptr && "Can't cloned dead code"); - - // We need to split any edge from a non cond_br basic block leading to a - // exit block. After cloning this edge will become critical if it came from - // inside the cloned region. The SSAUpdater can't handle critical non - // cond_br edges. - // - // FIXME: remove this in the next commit. The SILCloner will always do it. - for (auto *BB : exitBBs) { - SmallVector Preds(BB->getPredecessorBlocks()); - for (auto *Pred : Preds) - if (!isa(Pred->getTerminator()) && - !isa(Pred->getTerminator())) - splitEdgesFromTo(Pred, BB, &DomTree, nullptr); - } - - cloneReachableBlocks(StartBB, exitBBs); - - // Add dominator tree nodes for the new basic blocks. - fixDomTree(); - - // Update SSA form for values used outside of the copied region. - updateSSAForm(); - return getOpBasicBlock(StartBB); - } - -protected: - /// Clone the dominator tree from the original region to the cloned region. - void fixDomTree() { - for (auto *BB : originalPreorderBlocks()) { - auto *ClonedBB = getOpBasicBlock(BB); - auto *OrigDomBB = DomTree.getNode(BB)->getIDom()->getBlock(); - if (BB == StartBB) { - // The cloned start node shares the same dominator as the original node. - auto *ClonedNode = DomTree.addNewBlock(ClonedBB, OrigDomBB); - (void)ClonedNode; - assert(ClonedNode); - continue; - } - // Otherwise, map the dominator structure using the mapped block. - DomTree.addNewBlock(ClonedBB, getOpBasicBlock(OrigDomBB)); - } - } - - SILValue getMappedValue(SILValue V) { - if (auto *BB = V->getParentBlock()) { - if (!DomTree.dominates(StartBB, BB)) { - // Must be a value that dominates the start basic block. - assert(DomTree.dominates(BB, StartBB) && - "Must dominated the start of the cloned region"); - return V; - } - } - return SILCloner::getMappedValue(V); - } - - void postProcess(SILInstruction *Orig, SILInstruction *Cloned) { - SILCloner::postProcess(Orig, Cloned); - } - - /// Update SSA form for values that are used outside the region. - void updateSSAForValue(SILBasicBlock *OrigBB, SILValue V, - SILSSAUpdater &SSAUp) { - // Collect outside uses. - SmallVector UseList; - for (auto Use : V->getUses()) - if (!isBlockCloned(Use->getUser()->getParent())) { - UseList.push_back(UseWrapper(Use)); - } - if (UseList.empty()) - return; - - // Update SSA form. - SSAUp.Initialize(V->getType()); - SSAUp.AddAvailableValue(OrigBB, V); - SILValue NewVal = getMappedValue(V); - SSAUp.AddAvailableValue(getOpBasicBlock(OrigBB), NewVal); - for (auto U : UseList) { - Operand *Use = U; - SSAUp.RewriteUse(*Use); - } - } - - void updateSSAForm() { - SILSSAUpdater SSAUp; - for (auto *origBB : originalPreorderBlocks()) { - // Update outside used phi values. - for (auto *arg : origBB->getArguments()) - updateSSAForValue(origBB, arg, SSAUp); - - // Update outside used instruction values. - for (auto &inst : *origBB) { - for (auto result : inst.getResults()) - updateSSAForValue(origBB, result, SSAUp); - } - } - } -}; -} // end anonymous namespace - -namespace { -/// This class transforms a hoistable loop nest into a speculatively specialized -/// loop based on array.props calls. -class ArrayPropertiesSpecializer { - DominanceInfo *DomTree; - SILLoopAnalysis *LoopAnalysis; - SILBasicBlock *HoistableLoopPreheader; - -public: - ArrayPropertiesSpecializer(DominanceInfo *DT, SILLoopAnalysis *LA, - SILBasicBlock *Hoistable) - : DomTree(DT), LoopAnalysis(LA), HoistableLoopPreheader(Hoistable) {} - - void run() { - specializeLoopNest(); - } - - SILLoop *getLoop() { - auto *LoopInfo = LoopAnalysis->get(HoistableLoopPreheader->getParent()); - return LoopInfo->getLoopFor( - HoistableLoopPreheader->getSingleSuccessorBlock()); - } - -protected: - void specializeLoopNest(); -}; -} // end anonymous namespace - -static SILValue createStructExtract(SILBuilder &B, SILLocation Loc, - SILValue Opd, unsigned FieldNo) { - SILType Ty = Opd->getType(); - auto SD = Ty.getStructOrBoundGenericStruct(); - auto Properties = SD->getStoredProperties(); - unsigned Counter = 0; - for (auto *D : Properties) - if (Counter++ == FieldNo) - return B.createStructExtract(Loc, Opd, D); - llvm_unreachable("Wrong field number"); -} - -static Identifier getBinaryFunction(StringRef Name, SILType IntSILTy, - ASTContext &C) { - auto IntTy = IntSILTy.castTo(); - unsigned NumBits = IntTy->getWidth().getFixedWidth(); - // Name is something like: add_Int64 - std::string NameStr = Name; - NameStr += "_Int" + llvm::utostr(NumBits); - return C.getIdentifier(NameStr); -} - -/// Create a binary and function. -static SILValue createAnd(SILBuilder &B, SILLocation Loc, SILValue Opd1, - SILValue Opd2) { - auto AndFn = getBinaryFunction("and", Opd1->getType(), B.getASTContext()); - SILValue Args[] = {Opd1, Opd2}; - return B.createBuiltin(Loc, AndFn, Opd1->getType(), {}, Args); -} - -/// Create a check over all array.props calls that they have the 'fast native -/// swift' array value: isNative && !needsElementTypeCheck must be true. -static SILValue -createFastNativeArraysCheck(SmallVectorImpl &ArrayProps, - SILBuilder &B) { - assert(!ArrayProps.empty() && "Must have array.pros calls"); - - SILType IntBoolTy = SILType::getBuiltinIntegerType(1, B.getASTContext()); - SILValue Result = - B.createIntegerLiteral((*ArrayProps[0]).getLoc(), IntBoolTy, 1); - - for (auto Call : ArrayProps) { - auto Loc = (*Call).getLoc(); - auto CallKind = Call.getKind(); - if (CallKind == ArrayCallKind::kArrayPropsIsNativeTypeChecked) { - auto Val = createStructExtract(B, Loc, SILValue(Call), 0); - Result = createAnd(B, Loc, Result, Val); - } - } - return Result; -} - -/// Collect all array.props calls in the cloned basic blocks stored in the map, -/// asserting that we found at least one. -static void collectArrayPropsCalls(RegionCloner &Cloner, - SmallVectorImpl &ExitBlocks, - SmallVectorImpl &Calls) { - for (auto *origBB : Cloner.originalPreorderBlocks()) { - auto clonedBB = Cloner.getOpBasicBlock(origBB); - for (auto &Inst : *clonedBB) { - ArraySemanticsCall ArrayProps(&Inst, "array.props", true); - if (!ArrayProps) - continue; - Calls.push_back(ArrayProps); - } - } - assert(!Calls.empty() && "Should have a least one array.props call"); -} - -/// Replace an array.props call by the 'fast swift array' value. -/// -/// This is true for array.props.isNative and false for -/// array.props.needsElementTypeCheck. -static void replaceArrayPropsCall(SILBuilder &B, ArraySemanticsCall C) { - assert(C.getKind() == ArrayCallKind::kArrayPropsIsNativeTypeChecked); - ApplyInst *AI = C; - - SILType IntBoolTy = SILType::getBuiltinIntegerType(1, B.getASTContext()); - - auto BoolTy = AI->getType(); - auto C0 = B.createIntegerLiteral(AI->getLoc(), IntBoolTy, 1); - auto BoolVal = B.createStruct(AI->getLoc(), BoolTy, {C0}); - - (*C).replaceAllUsesWith(BoolVal); - // Remove call to array.props.read/write. - C.removeCall(); -} - -/// Collects all loop dominated blocks outside the loop that are immediately -/// dominated by the loop. -static void -collectImmediateLoopDominatedBlocks(const SILLoop *Lp, DominanceInfoNode *Node, - SmallVectorImpl &Blocks) { - SILBasicBlock *BB = Node->getBlock(); - - // Base case: First loop dominated block outside of loop. - if (!Lp->contains(BB)) { - Blocks.push_back(BB); - return; - } - - // Loop contains the basic block. Look at immediately dominated nodes. - for (auto *Child : *Node) - collectImmediateLoopDominatedBlocks(Lp, Child, Blocks); -} - -void ArrayPropertiesSpecializer::specializeLoopNest() { - auto *Lp = getLoop(); - assert(Lp); - - // Split of a new empty preheader. We don't want to duplicate the whole - // original preheader it might contain instructions that we can't clone. - // This will be block that will contain the check whether to execute the - // 'native swift array' loop or the original loop. - SILBuilder B(HoistableLoopPreheader); - auto *CheckBlock = splitBasicBlockAndBranch(B, - HoistableLoopPreheader->getTerminator(), DomTree, nullptr); - - auto *Header = CheckBlock->getSingleSuccessorBlock(); - assert(Header); - - // Collect all loop dominated blocks (e.g exit blocks could be among them). We - // need to update their dominator. - SmallVector LoopDominatedBlocks; - collectImmediateLoopDominatedBlocks(Lp, DomTree->getNode(Header), - LoopDominatedBlocks); - - // Collect all exit blocks. - SmallVector ExitBlocks; - Lp->getExitBlocks(ExitBlocks); - - // Split the preheader before the first instruction. - SILBasicBlock *NewPreheader = - splitBasicBlockAndBranch(B, &*CheckBlock->begin(), DomTree, nullptr); - - // Clone the region from the new preheader up to (not including) the exit - // blocks. This creates a second loop nest. - RegionCloner Cloner(NewPreheader, *DomTree); - auto *ClonedPreheader = Cloner.cloneRegion(ExitBlocks); - - // Collect the array.props call that we will specialize on that we have - // cloned in the cloned loop. - SmallVector ArrayPropCalls; - collectArrayPropsCalls(Cloner, ExitBlocks, ArrayPropCalls); - - // Move them to the check block. - SmallVector HoistedArrayPropCalls; - for (auto C: ArrayPropCalls) - HoistedArrayPropCalls.push_back( - ArraySemanticsCall(C.copyTo(CheckBlock->getTerminator(), DomTree))); - - // Create a conditional branch on the fast condition being true. - B.setInsertionPoint(CheckBlock->getTerminator()); - auto IsFastNativeArray = - createFastNativeArraysCheck(HoistedArrayPropCalls, B); - B.createCondBranch(CheckBlock->getTerminator()->getLoc(), - IsFastNativeArray, ClonedPreheader, NewPreheader); - CheckBlock->getTerminator()->eraseFromParent(); - - // Fixup the loop dominated blocks. They are now dominated by the check block. - for (auto *BB : LoopDominatedBlocks) - DomTree->changeImmediateDominator(DomTree->getNode(BB), - DomTree->getNode(CheckBlock)); - - // Replace the array.props calls uses in the cloned loop by their 'fast' - // value. - SILBuilder B2(ClonedPreheader->getTerminator()); - for (auto C : ArrayPropCalls) - replaceArrayPropsCall(B2, C); - - // We have potentially cloned a loop - invalidate loop info. - LoopAnalysis->invalidate(Header->getParent(), - SILAnalysis::InvalidationKind::FunctionBody); -} - -namespace { -class SwiftArrayOptPass : public SILFunctionTransform { - - void run() override { - if (!ShouldSpecializeArrayProps) - return; - - auto *Fn = getFunction(); - - // FIXME: Add support for ownership. - if (Fn->hasOwnership()) - return; - - // Don't hoist array property calls at Osize. - if (Fn->optimizeForSize()) - return; - - DominanceAnalysis *DA = PM->getAnalysis(); - SILLoopAnalysis *LA = PM->getAnalysis(); - SILLoopInfo *LI = LA->get(Fn); - - bool HasChanged = false; - - // Check whether we can hoist 'array.props' calls out of loops, collecting - // the preheader we can hoist to. We only hoist out of loops if 'all' - // array.props call can be hoisted for a given loop nest. - // We process the loop tree preorder (top-down) to hoist over the biggest - // possible loop-nest. - SmallVector HoistableLoopNests; - std::function processChildren = [&](SILLoop *L) { - ArrayPropertiesAnalysis Analysis(L, DA); - if (Analysis.run()) { - // Hoist in the current loop nest. - HasChanged = true; - HoistableLoopNests.push_back(L->getLoopPreheader()); - } else { - // Otherwise, try hoisting sub-loops. - for (auto *SubLoop : *L) - processChildren(SubLoop); - } - }; - for (auto *L : *LI) - processChildren(L); - - // Specialize the identified loop nest based on the 'array.props' calls. - if (HasChanged) { - LLVM_DEBUG(getFunction()->viewCFG()); - DominanceInfo *DT = DA->get(getFunction()); - - // Process specialized loop-nests in loop-tree post-order (bottom-up). - std::reverse(HoistableLoopNests.begin(), HoistableLoopNests.end()); - - // Hoist the loop nests. - for (auto &HoistableLoopNest : HoistableLoopNests) - ArrayPropertiesSpecializer(DT, LA, HoistableLoopNest).run(); - - // Verify that no illegal critical edges were created. - getFunction()->verifyCriticalEdges(); - - LLVM_DEBUG(getFunction()->viewCFG()); - - // We preserve the dominator tree. Let's invalidate everything - // else. - DA->lockInvalidation(); - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - DA->unlockInvalidation(); - } - } - -}; -} // end anonymous namespace - -SILTransform *swift::createSwiftArrayOpts() { - return new SwiftArrayOptPass(); -} diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 10e4d6b7012de..53650a6bf37c8 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -47,29 +47,77 @@ using InstSet = llvm::SmallPtrSet; using InstVector = llvm::SmallVector; -/// A subset of instruction which may have side effects. -/// Doesn't contain ones that have special handling (e.g. fix_lifetime) -using WriteSet = SmallPtrSet; - -/// Returns true if the \p MayWrites set contains any memory writes which may -/// alias with the memory addressed by \a LI. +/// Returns true if the \p SideEffectInsts set contains any memory writes which +/// may alias with the memory addressed by \a LI. template -static bool mayWriteTo(AliasAnalysis *AA, WriteSet &MayWrites, +static bool mayWriteTo(AliasAnalysis *AA, InstSet &SideEffectInsts, UnaryInstructionBase *Inst) { - for (auto *W : MayWrites) - if (AA->mayWriteToMemory(W, Inst->getOperand())) { - LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " + for (auto *I : SideEffectInsts) + if (AA->mayWriteToMemory(I, Inst->getOperand())) { + LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *I << " to " << *Inst << "\n"); return true; } return false; } -/// Returns true if the \p MayWrites set contains any memory writes which may -/// alias with any memory which is read by \p AI. +/// Returns true if \p I is a store to \p addr. +static StoreInst *isStoreToAddr(SILInstruction *I, SILValue addr) { + auto *SI = dyn_cast(I); + if (!SI) + return nullptr; + + // TODO: handle StoreOwnershipQualifier::Init + if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Init) + return nullptr; + + if (SI->getDest() != addr) + return nullptr; + + return SI; +} + +/// Returns true if \p I is a load from \p addr or a projected address from +/// \p addr. +static LoadInst *isLoadFromAddr(SILInstruction *I, SILValue addr) { + auto *LI = dyn_cast_or_null(I); + if (!LI) + return nullptr; + + // TODO: handle StoreOwnershipQualifier::Take + if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Take) + return nullptr; + + SILValue v = LI->getOperand(); + for (;;) { + if (v == addr) { + return LI; + } else if (isa(v) || isa(v)) { + v = cast(v)->getOperand(0); + } else { + return nullptr; + } + } +} + +/// Returns true if all instructions in \p SideEffectInsts which may alias with +/// \p addr are either loads or stores from \p addr. +static bool isOnlyLoadedAndStored(AliasAnalysis *AA, InstSet &SideEffectInsts, + SILValue addr) { + for (auto *I : SideEffectInsts) { + if (AA->mayReadOrWriteMemory(I, addr) && + !isStoreToAddr(I, addr) && !isLoadFromAddr(I, addr)) { + return false; + } + } + return true; +} + +/// Returns true if the \p SideEffectInsts set contains any memory writes which +/// may alias with any memory which is read by \p AI. /// Note: This function should only be called on a read-only apply! static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, - WriteSet &MayWrites, ApplyInst *AI) { + InstSet &SideEffectInsts, ApplyInst *AI) { FunctionSideEffects E; SEA->getCalleeEffects(E, AI); assert(E.getMemBehavior(RetainObserveKind::IgnoreRetains) <= @@ -87,9 +135,9 @@ static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, SILValue Arg = AI->getArgument(Idx); // Check if the memory addressed by the argument may alias any writes. - for (auto *W : MayWrites) { - if (AA->mayWriteToMemory(W, Arg)) { - LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " + for (auto *I : SideEffectInsts) { + if (AA->mayWriteToMemory(I, Arg)) { + LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *I << " to " << *AI << "\n"); return true; } @@ -98,23 +146,6 @@ static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, return false; } -static bool hasLoopInvariantOperands(SILInstruction *I, SILLoop *L) { - auto Opds = I->getAllOperands(); - - return std::all_of(Opds.begin(), Opds.end(), [=](Operand &Op) { - - ValueBase *Def = Op.get(); - - // Operand is defined outside the loop. - if (auto *Inst = Def->getDefiningInstruction()) - return !L->contains(Inst->getParent()); - if (auto *Arg = dyn_cast(Def)) - return !L->contains(Arg->getParent()); - - return false; - }); -} - // When Hoisting / Sinking, // Don't descend into control-dependent code. // Only traverse into basic blocks that dominate all exits. @@ -146,9 +177,19 @@ static void getDominatingBlocks(SmallVectorImpl &domBlocks, } } +/// Returns true if \p v is loop invariant in \p L. +static bool isLoopInvariant(SILValue v, SILLoop *L) { + if (SILBasicBlock *parent = v->getParentBlock()) + return !L->contains(parent); + return false; +} + static bool hoistInstruction(DominanceInfo *DT, SILInstruction *Inst, SILLoop *Loop, SILBasicBlock *&Preheader) { - if (!hasLoopInvariantOperands(Inst, Loop)) { + auto Operands = Inst->getAllOperands(); + if (!std::all_of(Operands.begin(), Operands.end(), [=](Operand &Op) { + return isLoopInvariant(Op.get(), Loop); + })) { LLVM_DEBUG(llvm::dbgs() << " loop variant operands\n"); return false; } @@ -192,17 +233,17 @@ static bool hoistInstructions(SILLoop *Loop, DominanceInfo *DT, return Changed; } -/// Summary of may writes occurring in the loop tree rooted at \p +/// Summary of side effect instructions occurring in the loop tree rooted at \p /// Loop. This includes all writes of the sub loops and the loop itself. struct LoopNestSummary { SILLoop *Loop; - WriteSet MayWrites; + InstSet SideEffectInsts; LoopNestSummary(SILLoop *Curr) : Loop(Curr) {} void copySummary(LoopNestSummary &Other) { - MayWrites.insert(Other.MayWrites.begin(), Other.MayWrites.end()); + SideEffectInsts.insert(Other.SideEffectInsts.begin(), Other.SideEffectInsts.end()); } LoopNestSummary(const LoopNestSummary &) = delete; @@ -284,8 +325,8 @@ static bool sinkInstruction(DominanceInfo *DT, } if (Changed && !ExitBB) { // Created clones of instruction - // Remove it from the may write set - dangling pointer - LoopSummary->MayWrites.erase(Inst); + // Remove it from the side-effect set - dangling pointer + LoopSummary->SideEffectInsts.erase(Inst); Inst->getParent()->erase(Inst); } return Changed; @@ -381,6 +422,12 @@ class LoopTreeOptimization { /// Instructions that we may be able to sink down InstVector SinkDown; + /// Load and store instructions that we may be able to move out of the loop. + InstVector LoadsAndStores; + + /// All addresses of the \p LoadsAndStores instructions. + llvm::SetVector LoadAndStoreAddrs; + /// Hoistable Instructions that need special treatment /// e.g. begin_access InstVector SpecialHoist; @@ -413,6 +460,36 @@ class LoopTreeOptimization { /// Optimize the current loop nest. bool optimizeLoop(std::unique_ptr &CurrSummary); + + /// Move all loads and stores from/to \p addr out of the \p loop. + void hoistLoadsAndStores(SILValue addr, SILLoop *loop, InstVector &toDelete); + + /// Move all loads and stores from all addresses in LoadAndStoreAddrs out of + /// the \p loop. + /// + /// This is a combination of load hoisting and store sinking, e.g. + /// \code + /// preheader: + /// br header_block + /// header_block: + /// %x = load %not_aliased_addr + /// // use %x and define %y + /// store %y to %not_aliased_addr + /// ... + /// exit_block: + /// \endcode + /// is transformed to: + /// \code + /// preheader: + /// %x = load %not_aliased_addr + /// br header_block + /// header_block: + /// // use %x and define %y + /// ... + /// exit_block: + /// store %y to %not_aliased_addr + /// \endcode + bool hoistAllLoadsAndStores(SILLoop *loop); }; } // end anonymous namespace @@ -433,12 +510,14 @@ bool LoopTreeOptimization::optimize() { // Might allow us to sink the instruction out of the loop bool currChanged = false; do { - currChanged = false; - // Analyze the current loop for instructions that can be hoisted. analyzeCurrentLoop(CurrLoopSummary); currChanged = optimizeLoop(CurrLoopSummary); + if (currChanged) { + CurrLoopSummary->SideEffectInsts.clear(); + Changed = true; + } // Reset the data structures for next loop in the list HoistUp.clear(); @@ -476,9 +555,10 @@ static bool isSafeReadOnlyApply(SideEffectAnalysis *SEA, ApplyInst *AI) { return (MB <= SILInstruction::MemoryBehavior::MayRead); } -static void checkSideEffects(swift::SILInstruction &Inst, WriteSet &MayWrites) { +static void checkSideEffects(swift::SILInstruction &Inst, + InstSet &SideEffectInsts) { if (Inst.mayHaveSideEffects()) { - MayWrites.insert(&Inst); + SideEffectInsts.insert(&Inst); } } @@ -551,7 +631,7 @@ static bool isCoveredByScope(BeginAccessInst *BI, DominanceInfo *DT, static bool analyzeBeginAccess(BeginAccessInst *BI, SmallVector &BeginAccesses, SmallVector &fullApplies, - WriteSet &MayWrites, + InstSet &SideEffectInsts, AccessedStorageAnalysis *ASA, DominanceInfo *DT) { const AccessedStorage &storage = @@ -595,12 +675,12 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, // TODO Introduce "Pure Swift" deinitializers // We can then make use of alias information for instr's operands // If they don't alias - we might get away with not recording a conflict - for (auto mayWrite : MayWrites) { - // we actually compute all MayWrites in analyzeCurrentLoop - if (!mayWrite->mayRelease()) { + for (SILInstruction *I : SideEffectInsts) { + // we actually compute all SideEffectInsts in analyzeCurrentLoop + if (!I->mayRelease()) { continue; } - if (!isCoveredByScope(BI, DT, mayWrite)) + if (!isCoveredByScope(BI, DT, I)) return false; } @@ -611,110 +691,112 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, // Computes set of instructions we may be able to move out of the loop // Important Note: // We can't bail out of this method! we have to run it on all loops. -// We *need* to discover all MayWrites - +// We *need* to discover all SideEffectInsts - // even if the loop is otherwise skipped! // This is because outer loops will depend on the inner loop's writes. void LoopTreeOptimization::analyzeCurrentLoop( std::unique_ptr &CurrSummary) { - WriteSet &MayWrites = CurrSummary->MayWrites; + InstSet &sideEffects = CurrSummary->SideEffectInsts; SILLoop *Loop = CurrSummary->Loop; LLVM_DEBUG(llvm::dbgs() << " Analyzing accesses.\n"); - // Contains function calls in the loop, which only read from memory. + auto *Preheader = Loop->getLoopPreheader(); + if (!Preheader) { + // Can't hoist/sink instructions + return; + } + + // Interesting instructions in the loop: SmallVector ReadOnlyApplies; - // Contains Loads inside the loop. SmallVector Loads; - // Contains fix_lifetime, we might be able to sink them. + SmallVector Stores; SmallVector FixLifetimes; - // Contains begin_access, we might be able to hoist them. SmallVector BeginAccesses; - // Contains all applies - used for begin_access SmallVector fullApplies; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { switch (Inst.getKind()) { case SILInstructionKind::FixLifetimeInst: { - auto *FL = dyn_cast(&Inst); - assert(FL && "Expected a FixLifetime instruction"); - FixLifetimes.push_back(FL); + auto *FL = cast(&Inst); + if (DomTree->dominates(FL->getOperand()->getParentBlock(), Preheader)) + FixLifetimes.push_back(FL); // We can ignore the side effects of FixLifetimes break; } - case SILInstructionKind::LoadInst: { - auto *LI = dyn_cast(&Inst); - assert(LI && "Expected a Load instruction"); - Loads.push_back(LI); + case SILInstructionKind::LoadInst: + Loads.push_back(cast(&Inst)); + LoadsAndStores.push_back(&Inst); break; - } - case SILInstructionKind::BeginAccessInst: { - auto *BI = dyn_cast(&Inst); - assert(BI && "Expected a Begin Access"); - BeginAccesses.push_back(BI); - checkSideEffects(Inst, MayWrites); + case SILInstructionKind::StoreInst: { + Stores.push_back(cast(&Inst)); + LoadsAndStores.push_back(&Inst); + checkSideEffects(Inst, sideEffects); break; } - case SILInstructionKind::RefElementAddrInst: { - auto *REA = static_cast(&Inst); - SpecialHoist.push_back(REA); + case SILInstructionKind::BeginAccessInst: + BeginAccesses.push_back(cast(&Inst)); + checkSideEffects(Inst, sideEffects); break; - } - case swift::SILInstructionKind::CondFailInst: { + case SILInstructionKind::RefElementAddrInst: + SpecialHoist.push_back(cast(&Inst)); + break; + case swift::SILInstructionKind::CondFailInst: // We can (and must) hoist cond_fail instructions if the operand is // invariant. We must hoist them so that we preserve memory safety. A // cond_fail that would have protected (executed before) a memory access // must - after hoisting - also be executed before said access. HoistUp.insert(&Inst); - checkSideEffects(Inst, MayWrites); + checkSideEffects(Inst, sideEffects); break; - } case SILInstructionKind::ApplyInst: { - auto *AI = dyn_cast(&Inst); - assert(AI && "Expected an Apply Instruction"); + auto *AI = cast(&Inst); if (isSafeReadOnlyApply(SEA, AI)) { ReadOnlyApplies.push_back(AI); } // check for array semantics and side effects - same as default LLVM_FALLTHROUGH; } - default: { + default: if (auto fullApply = FullApplySite::isa(&Inst)) { fullApplies.push_back(fullApply); } - checkSideEffects(Inst, MayWrites); + checkSideEffects(Inst, sideEffects); if (canHoistUpDefault(&Inst, Loop, DomTree, RunsOnHighLevelSIL)) { HoistUp.insert(&Inst); } break; } - } } } - auto *Preheader = Loop->getLoopPreheader(); - if (!Preheader) { - // Can't hoist/sink instructions - return; - } for (auto *AI : ReadOnlyApplies) { - if (!mayWriteTo(AA, SEA, MayWrites, AI)) { + if (!mayWriteTo(AA, SEA, sideEffects, AI)) { HoistUp.insert(AI); } } for (auto *LI : Loads) { - if (!mayWriteTo(AA, MayWrites, LI)) { + if (!mayWriteTo(AA, sideEffects, LI)) { HoistUp.insert(LI); } } - bool mayWritesMayRelease = - std::any_of(MayWrites.begin(), MayWrites.end(), - [&](SILInstruction *W) { return W->mayRelease(); }); - for (auto *FL : FixLifetimes) { - if (!DomTree->dominates(FL->getOperand()->getParentBlock(), Preheader)) { - continue; + // Collect memory locations for which we can move all loads and stores out + // of the loop. + for (StoreInst *SI : Stores) { + SILValue addr = SI->getDest(); + if (isLoopInvariant(addr, Loop) && + isOnlyLoadedAndStored(AA, sideEffects, addr)) { + LoadAndStoreAddrs.insert(addr); } - if (!mayWriteTo(AA, MayWrites, FL) || !mayWritesMayRelease) { - SinkDown.push_back(FL); + } + if (!FixLifetimes.empty()) { + bool sideEffectsMayRelease = + std::any_of(sideEffects.begin(), sideEffects.end(), + [&](SILInstruction *W) { return W->mayRelease(); }); + for (auto *FL : FixLifetimes) { + if (!sideEffectsMayRelease || !mayWriteTo(AA, sideEffects, FL)) { + SinkDown.push_back(FL); + } } } for (auto *BI : BeginAccesses) { @@ -723,7 +805,7 @@ void LoopTreeOptimization::analyzeCurrentLoop( LLVM_DEBUG(llvm::dbgs() << "Some end accesses can't be handled\n"); continue; } - if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, MayWrites, ASA, + if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, sideEffects, ASA, DomTree)) { SpecialHoist.push_back(BI); } @@ -737,14 +819,195 @@ bool LoopTreeOptimization::optimizeLoop( if (!CurrentLoop->getLoopPreheader()) return false; bool currChanged = false; + if (hoistAllLoadsAndStores(CurrentLoop)) + return true; + currChanged |= hoistInstructions(CurrentLoop, DomTree, HoistUp); currChanged |= sinkInstructions(CurrSummary, DomTree, LoopInfo, SinkDown); currChanged |= hoistSpecialInstruction(CurrSummary, DomTree, LoopInfo, SpecialHoist); - Changed |= currChanged; return currChanged; } +/// Creates a value projection from \p rootVal based on the address projection +/// from \a rootAddr to \a addr. +static SILValue projectLoadValue(SILValue addr, SILValue rootAddr, + SILValue rootVal, SILInstruction *beforeInst) { + if (addr == rootAddr) + return rootVal; + + if (auto *SEI = dyn_cast(addr)) { + SILValue val = projectLoadValue(SEI->getOperand(), rootAddr, rootVal, + beforeInst); + SILBuilder B(beforeInst); + return B.createStructExtract(beforeInst->getLoc(), val, SEI->getField(), + SEI->getType().getObjectType()); + } + if (auto *TEI = dyn_cast(addr)) { + SILValue val = projectLoadValue(TEI->getOperand(), rootAddr, rootVal, + beforeInst); + SILBuilder B(beforeInst); + return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldNo(), + TEI->getType().getObjectType()); + } + llvm_unreachable("unknown projection"); +} + +/// Returns true if all stores to \p addr commonly dominate the loop exitst of +/// \p loop. +static bool storesCommonlyDominateLoopExits(SILValue addr, SILLoop *loop, + ArrayRef exitingBlocks) { + SmallPtrSet stores; + for (Operand *use : addr->getUses()) { + SILInstruction *user = use->getUser(); + if (isa(user)) + stores.insert(user->getParent()); + } + SILBasicBlock *header = loop->getHeader(); + // If a store is in the loop header, we already know that it's dominating all + // loop exits. + if (stores.count(header) != 0) + return true; + + // Propagate the store-is-not-alive flag through the control flow in the loop, + // starting at the header. + SmallPtrSet storesNotAlive; + storesNotAlive.insert(header); + bool changed = false; + do { + changed = false; + for (SILBasicBlock *block : loop->blocks()) { + bool storeAlive = (storesNotAlive.count(block) == 0); + if (storeAlive && stores.count(block) == 0 && + std::any_of(block->pred_begin(), block->pred_end(), + [&](SILBasicBlock *b) { return storesNotAlive.count(b) != 0; })) { + storesNotAlive.insert(block); + changed = true; + } + } + } while (changed); + + auto isUnreachableBlock = [](SILBasicBlock *succ) { + return isa(succ->getTerminator()); + }; + + // Check if the store-is-not-alive flag reaches any of the exits. + for (SILBasicBlock *eb : exitingBlocks) { + // Ignore loop exits to blocks which end in an unreachable. + if (!std::any_of(eb->succ_begin(), eb->succ_end(), isUnreachableBlock) && + storesNotAlive.count(eb) != 0) { + return false; + } + } + return true; +} + +void LoopTreeOptimization::hoistLoadsAndStores(SILValue addr, SILLoop *loop, InstVector &toDelete) { + + SmallVector exitingBlocks; + loop->getExitingBlocks(exitingBlocks); + + // This is not a requirement for functional correctness, but we don't want to + // _speculatively_ load and store the value (outside of the loop). + if (!storesCommonlyDominateLoopExits(addr, loop, exitingBlocks)) + return; + + // Inserting the stores requires the exit edges to be not critical. + for (SILBasicBlock *exitingBlock : exitingBlocks) { + for (unsigned idx = 0, e = exitingBlock->getSuccessors().size(); + idx != e; ++idx) { + // exitingBlock->getSuccessors() must not be moved out of this loop, + // because the successor list is invalidated by splitCriticalEdge. + if (!loop->contains(exitingBlock->getSuccessors()[idx])) { + splitCriticalEdge(exitingBlock->getTerminator(), idx, DomTree, LoopInfo); + } + } + } + + SILBasicBlock *preheader = loop->getLoopPreheader(); + assert(preheader && "Expected a preheader"); + + // Initially load the value in the loop pre header. + SILBuilder B(preheader->getTerminator()); + auto *initialLoad = B.createLoad(preheader->getTerminator()->getLoc(), addr, + LoadOwnershipQualifier::Unqualified); + + SILSSAUpdater ssaUpdater; + ssaUpdater.Initialize(initialLoad->getType()); + ssaUpdater.AddAvailableValue(preheader, initialLoad); + + // Set all stored values as available values in the ssaUpdater. + // If there are multiple stores in a block, only the last one counts. + Optional loc; + for (SILInstruction *I : LoadsAndStores) { + if (auto *SI = isStoreToAddr(I, addr)) { + loc = SI->getLoc(); + + // If a store just stores the loaded value, bail. The operand (= the load) + // will be removed later, so it cannot be used as available value. + // This corner case is suprisingly hard to handle, so we just give up. + if (isLoadFromAddr(dyn_cast(SI->getSrc()), addr)) + return; + + ssaUpdater.AddAvailableValue(SI->getParent(), SI->getSrc()); + } + } + + // Remove all stores and replace the loads with the current value. + SILBasicBlock *currentBlock = nullptr; + SILValue currentVal; + for (SILInstruction *I : LoadsAndStores) { + SILBasicBlock *block = I->getParent(); + if (block != currentBlock) { + currentBlock = block; + currentVal = SILValue(); + } + if (auto *SI = isStoreToAddr(I, addr)) { + currentVal = SI->getSrc(); + toDelete.push_back(SI); + } else if (auto *LI = isLoadFromAddr(I, addr)) { + // If we didn't see a store in this block yet, get the current value from + // the ssaUpdater. + if (!currentVal) + currentVal = ssaUpdater.GetValueInMiddleOfBlock(block); + SILValue projectedValue = projectLoadValue(LI->getOperand(), addr, + currentVal, LI); + LI->replaceAllUsesWith(projectedValue); + toDelete.push_back(LI); + } + } + + // Store back the value at all loop exits. + for (SILBasicBlock *exitingBlock : exitingBlocks) { + for (SILBasicBlock *succ : exitingBlock->getSuccessors()) { + if (!loop->contains(succ)) { + assert(succ->getSinglePredecessorBlock() && + "should have split critical edges"); + SILBuilder B(succ->begin()); + B.createStore(loc.getValue(), ssaUpdater.GetValueInMiddleOfBlock(succ), + addr, StoreOwnershipQualifier::Unqualified); + } + } + } + + // In case the value is only stored but never loaded in the loop. + eliminateDeadInstruction(initialLoad); +} + +bool LoopTreeOptimization::hoistAllLoadsAndStores(SILLoop *loop) { + InstVector toDelete; + for (SILValue addr : LoadAndStoreAddrs) { + hoistLoadsAndStores(addr, loop, toDelete); + } + LoadsAndStores.clear(); + LoadAndStoreAddrs.clear(); + + for (SILInstruction *I : toDelete) { + I->eraseFromParent(); + } + return !toDelete.empty(); +} + namespace { /// Hoist loop invariant code out of innermost loops. /// diff --git a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp index 315007020431c..4625eb2d2b5a1 100644 --- a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp +++ b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp @@ -70,6 +70,10 @@ canDuplicateOrMoveToPreheader(SILLoop *loop, SILBasicBlock *preheader, invariants.insert(inst); } else if (!inst->isTriviallyDuplicatable()) return false; + // It wouldn't make sense to rotate dealloc_stack without also rotating the + // alloc_stack, which is covered by isTriviallyDuplicatable. + else if (isa(inst)) + return false; else if (isa(inst)) { moves.push_back(inst); invariants.insert(inst); diff --git a/lib/SILOptimizer/Mandatory/AddressLowering.cpp b/lib/SILOptimizer/Mandatory/AddressLowering.cpp index 580ebe21dbe92..77a5b73b52a93 100644 --- a/lib/SILOptimizer/Mandatory/AddressLowering.cpp +++ b/lib/SILOptimizer/Mandatory/AddressLowering.cpp @@ -247,7 +247,7 @@ struct AddressLoweringState { AddressLoweringState(SILFunction *F, DominanceInfo *domInfo) : F(F), loweredFnConv(F->getLoweredFunctionType(), - SILModuleConventions::getLoweredAddressConventions()), + SILModuleConventions::getLoweredAddressConventions(F->getModule())), domInfo(domInfo) {} bool isDead(SILInstruction *inst) const { return instsToDelete.count(inst); } @@ -388,15 +388,6 @@ class OpaqueStorageAllocation { /// Top-level entry point: allocate storage for all opaque/resilient values. void OpaqueStorageAllocation::allocateOpaqueStorage() { - // TODO: I think we need a GenericContextScope for mapTypeIntoContext, but all - // tests are currently passing without it. -#if 0 - auto canFnType = pass.F->getLoweredFunctionType(); - - // Setup a generic context for argument and result types. - swift::Lowering::GenericContextScope scope(pass.F->getModule().Types, - canFnType->getGenericSignature()); -#endif // Fixup this function's argument types with temporary loads. convertIndirectFunctionArgs(); @@ -418,7 +409,7 @@ void OpaqueStorageAllocation::convertIndirectFunctionArgs() { // Insert temporary argument loads at the top of the function. SILBuilder argBuilder(pass.F->getEntryBlock()->begin()); argBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(pass.F->getModule())); auto fnConv = pass.F->getConventions(); unsigned argIdx = fnConv.getSILArgIndexOfFirstParam(); @@ -438,8 +429,7 @@ void OpaqueStorageAllocation::convertIndirectFunctionArgs() { assert(!pass.valueStorageMap.contains(arg)); arg = arg->getParent()->replaceFunctionArgument( - arg->getIndex(), addrType, ValueOwnershipKind::Any, - arg->getDecl()); + arg->getIndex(), addrType, ValueOwnershipKind::None, arg->getDecl()); loadArg->setOperand(arg); @@ -466,9 +456,8 @@ unsigned OpaqueStorageAllocation::insertIndirectReturnArgs() { pass.F->getDeclContext()); var->setSpecifier(ParamSpecifier::InOut); - pass.F->begin()->insertFunctionArgument(argIdx, - bodyResultTy.getAddressType(), - ValueOwnershipKind::Any, var); + pass.F->begin()->insertFunctionArgument( + argIdx, bodyResultTy.getAddressType(), ValueOwnershipKind::None, var); ++argIdx; } assert(argIdx == pass.loweredFnConv.getNumIndirectSILResults()); @@ -571,7 +560,7 @@ void OpaqueStorageAllocation::allocateForValue(SILValue value, SILBuilder allocBuilder(pass.F->begin()->begin()); allocBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(pass.F->getModule())); AllocStackInst *allocInstr = allocBuilder.createAllocStack(value.getLoc(), value->getType()); @@ -581,7 +570,7 @@ void OpaqueStorageAllocation::allocateForValue(SILValue value, for (TermInst *termInst : pass.returnInsts) { SILBuilder deallocBuilder(termInst); deallocBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(pass.F->getModule())); deallocBuilder.createDeallocStack(allocInstr->getLoc(), allocInstr); } } @@ -720,7 +709,7 @@ class ApplyRewriter { ApplyRewriter(ApplySite origCall, AddressLoweringState &pass) : pass(pass), apply(origCall), argBuilder(origCall.getInstruction()) { argBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(origCall.getModule())); } void rewriteParameters(); @@ -765,7 +754,7 @@ static void insertStackDeallocationAtCall(AllocStackInst *allocInst, case SILInstructionKind::ApplyInst: { SILBuilder deallocBuilder(&*std::next(lastUse->getIterator())); deallocBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(applyInst->getModule())); deallocBuilder.createDeallocStack(allocInst->getLoc(), allocInst); break; } @@ -835,13 +824,13 @@ void ApplyRewriter::canonicalizeResults( if (!result) { SILBuilder resultBuilder(std::next(SILBasicBlock::iterator(applyInst))); resultBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(applyInst->getModule())); result = resultBuilder.createTupleExtract(applyInst->getLoc(), applyInst, resultIdx); directResultValues[resultIdx] = result; } SILBuilder B(destroyInst); - B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); + B.setSILConventions(SILModuleConventions::getLoweredAddressConventions(applyInst->getModule())); auto &TL = pass.F->getTypeLowering(result->getType()); TL.emitDestroyValue(B, destroyInst->getLoc(), result); } @@ -874,7 +863,7 @@ SILValue ApplyRewriter::materializeIndirectResultAddress( // Build results outside-in to next stack allocations. SILBuilder resultBuilder(std::next(SILBasicBlock::iterator(origCallInst))); resultBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(origCallInst->getModule())); // This is a formally indirect argument, but is loadable. loadInst = resultBuilder.createLoad(loc, allocInst, LoadOwnershipQualifier::Unqualified); @@ -923,12 +912,12 @@ void ApplyRewriter::convertApplyWithIndirectResults() { SILLocation loc = origCallInst->getLoc(); SILBuilder callBuilder(origCallInst); callBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(origCallInst->getModule())); // The new call instruction's SIL calling convention. SILFunctionConventions loweredCalleeConv( apply.getSubstCalleeType(), - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(origCallInst->getModule())); // The new call instruction's SIL argument list. SmallVector newCallArgs(loweredCalleeConv.getNumSILArguments()); @@ -1000,7 +989,7 @@ void ApplyRewriter::convertApplyWithIndirectResults() { SILBuilder resultBuilder( std::next(SILBasicBlock::iterator(origCallInst))); resultBuilder.setSILConventions( - SILModuleConventions::getLoweredAddressConventions()); + SILModuleConventions::getLoweredAddressConventions(apply.getModule())); SmallVector origUses(origCallInst->getUses()); for (Operand *operand : origUses) { @@ -1075,7 +1064,8 @@ void ReturnRewriter::rewriteReturn(ReturnInst *returnInst) { break; } SILBuilder B(insertPt); - B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); + B.setSILConventions( + SILModuleConventions::getLoweredAddressConventions(returnInst->getModule())); // Gather direct function results. unsigned numOrigDirectResults = @@ -1173,7 +1163,8 @@ class AddressOnlyUseRewriter public: explicit AddressOnlyUseRewriter(AddressLoweringState &pass) : pass(pass), B(*pass.F), addrMat(pass, B) { - B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); + B.setSILConventions( + SILModuleConventions::getLoweredAddressConventions(pass.F->getModule())); } void visitOperand(Operand *operand) { @@ -1312,7 +1303,8 @@ class AddressOnlyDefRewriter public: explicit AddressOnlyDefRewriter(AddressLoweringState &pass) : pass(pass), B(*pass.F), addrMat(pass, B) { - B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); + B.setSILConventions( + SILModuleConventions::getLoweredAddressConventions(pass.F->getModule())); } void visitInst(SILInstruction *inst) { visit(inst); } diff --git a/lib/SILOptimizer/Mandatory/CMakeLists.txt b/lib/SILOptimizer/Mandatory/CMakeLists.txt index 4fdfc0cdae6d5..891c1ba40775e 100644 --- a/lib/SILOptimizer/Mandatory/CMakeLists.txt +++ b/lib/SILOptimizer/Mandatory/CMakeLists.txt @@ -13,7 +13,6 @@ silopt_register_sources( DiagnoseUnreachable.cpp GuaranteedARCOpts.cpp IRGenPrepare.cpp - MarkUninitializedFixup.cpp MandatoryInlining.cpp PredictableMemOpt.cpp PMOMemoryUseCollector.cpp @@ -23,4 +22,5 @@ silopt_register_sources( YieldOnceCheck.cpp MandatoryCombine.cpp OSLogOptimization.cpp + OwnershipModelEliminator.cpp ) diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index d0c9c67e5c49b..f3b863f32015a 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -143,8 +143,8 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, if (ReverseInitialWorklist) { std::reverse(insertedPhis.begin(), insertedPhis.end()); } - SmallVector worklist(insertedPhis.begin(), - insertedPhis.end()); + SmallVector worklist(insertedPhis.begin(), + insertedPhis.end()); sortUnique(insertedPhis); SmallVector incomingValues; @@ -187,9 +187,9 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue, continue; auto *termInst = cast(user); - for (auto succBlockArgList : termInst->getSuccessorBlockArguments()) { + for (auto succBlockArgList : termInst->getSuccessorBlockArgumentLists()) { llvm::copy_if(succBlockArgList, std::back_inserter(worklist), - [&](SILPhiArgument *succArg) -> bool { + [&](SILArgument *succArg) -> bool { auto it = lower_bound(insertedPhis, succArg); return it != insertedPhis.end() && *it == succArg; }); diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index a8138c942ddc3..1aff133106e81 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -28,34 +28,32 @@ using namespace ownership; // Utility //===----------------------------------------------------------------------===// -static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI, - DIElementUseInfo &UseInfo) { - // The MUI must be used on an alloc_box, alloc_stack, or global_addr. If we - // have an alloc_stack or a global_addr, there is nothing further to do. - if (isa(MUI->getOperand()) || - isa(MUI->getOperand()) || - isa(MUI->getOperand()) || +static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, + DIElementUseInfo &useInfo) { + auto *uninitMemory = memoryInfo.getUninitializedValue(); + + // The uninitMemory must be used on an alloc_box, alloc_stack, or global_addr. + // If we have an alloc_stack or a global_addr, there is nothing further to do. + if (isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || + isa(uninitMemory->getOperand(0)) || // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new // independent stack location for the pointer to address. - isa(MUI->getOperand())) + isa(uninitMemory->getOperand(0))) { return; + } - // Otherwise, we assume that we have a project_box. This is a hard cast to - // ensure that we catch any new patterns emitted by SILGen and assert. - auto *PBI = cast(MUI->getOperand()); - auto *ABI = cast(PBI->getOperand()); - - // Treat destroys of the container as load+destroys of the original value. + // Otherwise, we assume that we have an alloc_box. Treat destroys of the + // alloc_box as load+destroys of the value stored in the box. // // TODO: This should really be tracked separately from other destroys so that // we distinguish the lifetime of the container from the value itself. - for (auto *Op : ABI->getUses()) { - SILInstruction *User = Op->getUser(); - if (isa(User) || isa(User)) { - UseInfo.trackDestroy(User); - } + assert(isa(uninitMemory)); + auto *mui = cast(uninitMemory->getOperand(0)); + for (auto *user : mui->getUsersOfType()) { + useInfo.trackDestroy(user); } } @@ -63,7 +61,8 @@ static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI, // DIMemoryObjectInfo Implementation //===----------------------------------------------------------------------===// -static unsigned getElementCountRec(SILModule &Module, SILType T, +static unsigned getElementCountRec(TypeExpansionContext context, + SILModule &Module, SILType T, bool IsSelfOfNonDelegatingInitializer) { // If this is a tuple, it is always recursively flattened. if (CanTupleType TT = T.getAs()) { @@ -71,7 +70,7 @@ static unsigned getElementCountRec(SILModule &Module, SILType T, unsigned NumElements = 0; for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) NumElements += - getElementCountRec(Module, T.getTupleElementType(i), false); + getElementCountRec(context, Module, T.getTupleElementType(i), false); return NumElements; } @@ -83,8 +82,8 @@ static unsigned getElementCountRec(SILModule &Module, SILType T, if (auto *NTD = T.getNominalOrBoundGenericNominal()) { unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) - NumElements += - getElementCountRec(Module, T.getFieldType(VD, Module), false); + NumElements += getElementCountRec( + context, Module, T.getFieldType(VD, Module, context), false); return NumElements; } } @@ -97,7 +96,11 @@ static std::pair computeMemorySILType(MarkUninitializedInst *MemoryInst) { // Compute the type of the memory object. auto *MUI = MemoryInst; - SILType MemorySILType = MUI->getType().getObjectType(); + SILValue Address = MUI; + if (auto *PBI = Address->getSingleUserOfType()) { + Address = PBI; + } + SILType MemorySILType = Address->getType().getObjectType(); // If this is a let variable we're initializing, remember this so we don't // allow reassignment. @@ -134,7 +137,8 @@ DIMemoryObjectInfo::DIMemoryObjectInfo(MarkUninitializedInst *MI) // Otherwise, we break down the initializer. NumElements = - getElementCountRec(Module, MemorySILType, isNonDelegatingInit()); + getElementCountRec(TypeExpansionContext(*MI->getFunction()), Module, + MemorySILType, isNonDelegatingInit()); // If this is a derived class init method, track an extra element to determine // whether super.init has been called at each program point. @@ -152,16 +156,18 @@ SILInstruction *DIMemoryObjectInfo::getFunctionEntryPoint() const { } /// Given a symbolic element number, return the type of the element. -static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, +static SILType getElementTypeRec(TypeExpansionContext context, + SILModule &Module, SILType T, unsigned EltNo, bool IsSelfOfNonDelegatingInitializer) { // If this is a tuple type, walk into it. if (CanTupleType TT = T.getAs()) { assert(!IsSelfOfNonDelegatingInitializer && "self never has tuple type"); for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto FieldType = T.getTupleElementType(i); - unsigned NumFieldElements = getElementCountRec(Module, FieldType, false); + unsigned NumFieldElements = + getElementCountRec(context, Module, FieldType, false); if (EltNo < NumFieldElements) - return getElementTypeRec(Module, FieldType, EltNo, false); + return getElementTypeRec(context, Module, FieldType, EltNo, false); EltNo -= NumFieldElements; } // This can only happen if we look at a symbolic element number of an empty @@ -177,11 +183,11 @@ static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, bool HasStoredProperties = false; for (auto *VD : NTD->getStoredProperties()) { HasStoredProperties = true; - auto FieldType = T.getFieldType(VD, Module); + auto FieldType = T.getFieldType(VD, Module, context); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(context, Module, FieldType, false); if (EltNo < NumFieldElements) - return getElementTypeRec(Module, FieldType, EltNo, false); + return getElementTypeRec(context, Module, FieldType, EltNo, false); EltNo -= NumFieldElements; } @@ -202,15 +208,16 @@ static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo, /// getElementTypeRec - Return the swift type of the specified element. SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const { auto &Module = MemoryInst->getModule(); - return getElementTypeRec(Module, MemorySILType, EltNo, isNonDelegatingInit()); + return getElementTypeRec(TypeExpansionContext(*MemoryInst->getFunction()), + Module, MemorySILType, EltNo, isNonDelegatingInit()); } -/// computeTupleElementAddress - Given a tuple element number (in the flattened -/// sense) return a pointer to a leaf element of the specified number. -SILValue DIMemoryObjectInfo::emitElementAddress( +/// Given a tuple element number (in the flattened sense) return a pointer to a +/// leaf element of the specified number, so we can insert destroys for it. +SILValue DIMemoryObjectInfo::emitElementAddressForDestroy( unsigned EltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> &EndBorrowList) const { - SILValue Ptr = getAddress(); + SmallVectorImpl> &EndScopeList) const { + SILValue Ptr = getUninitializedValue(); bool IsSelf = isNonDelegatingInit(); auto &Module = MemoryInst->getModule(); @@ -225,7 +232,8 @@ SILValue DIMemoryObjectInfo::emitElementAddress( unsigned FieldNo = 0; for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto EltTy = PointeeType.getTupleElementType(i); - unsigned NumSubElt = getElementCountRec(Module, EltTy, false); + unsigned NumSubElt = getElementCountRec( + TypeExpansionContext(B.getFunction()), Module, EltTy, false); if (EltNo < NumSubElt) { Ptr = B.createTupleElementAddr(Loc, Ptr, FieldNo); PointeeType = EltTy; @@ -250,15 +258,15 @@ SILValue DIMemoryObjectInfo::emitElementAddress( // If we have a class, we can use a borrow directly and avoid ref // count traffic. if (isa(NTD) && Ptr->getType().isAddress()) { - SILValue Original = Ptr; SILValue Borrowed = Ptr = B.createLoadBorrow(Loc, Ptr); - EndBorrowList.emplace_back(Borrowed, Original); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } } - - auto FieldType = PointeeType.getFieldType(VD, Module); + auto expansionContext = TypeExpansionContext(B.getFunction()); + auto FieldType = + PointeeType.getFieldType(VD, Module, expansionContext); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(expansionContext, Module, FieldType, false); if (EltNo < NumFieldElements) { if (isa(NTD)) { Ptr = B.createStructElementAddr(Loc, Ptr, VD); @@ -268,12 +276,13 @@ SILValue DIMemoryObjectInfo::emitElementAddress( if (Ptr.getOwnershipKind() != ValueOwnershipKind::Guaranteed) { Original = Ptr; Borrowed = Ptr = B.createBeginBorrow(Loc, Ptr); + EndScopeList.emplace_back(Borrowed, EndScopeKind::Borrow); } Ptr = B.createRefElementAddr(Loc, Ptr, VD); - if (Original) { - assert(Borrowed); - EndBorrowList.emplace_back(Borrowed, Original); - } + Ptr = B.createBeginAccess( + Loc, Ptr, SILAccessKind::Deinit, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + EndScopeList.emplace_back(Ptr, EndScopeKind::Access); } PointeeType = FieldType; @@ -300,7 +309,8 @@ SILValue DIMemoryObjectInfo::emitElementAddress( /// Push the symbolic path name to the specified element number onto the /// specified std::string. -static void getPathStringToElementRec(SILModule &Module, SILType T, +static void getPathStringToElementRec(TypeExpansionContext context, + SILModule &Module, SILType T, unsigned EltNo, std::string &Result) { CanTupleType TT = T.getAs(); if (!TT) { @@ -313,7 +323,7 @@ static void getPathStringToElementRec(SILModule &Module, SILType T, for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) { auto Field = TT->getElement(i); SILType FieldTy = T.getTupleElementType(i); - unsigned NumFieldElements = getElementCountRec(Module, FieldTy, false); + unsigned NumFieldElements = getElementCountRec(context, Module, FieldTy, false); if (EltNo < NumFieldElements) { Result += '.'; @@ -321,7 +331,7 @@ static void getPathStringToElementRec(SILModule &Module, SILType T, Result += Field.getName().str(); else Result += llvm::utostr(FieldNo); - return getPathStringToElementRec(Module, FieldTy, EltNo, Result); + return getPathStringToElementRec(context, Module, FieldTy, EltNo, Result); } EltNo -= NumFieldElements; @@ -346,14 +356,16 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, Result = ""; // If this is indexing into a field of 'self', look it up. + auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction()); if (isNonDelegatingInit() && !isDerivedClassSelfOnly()) { if (auto *NTD = MemorySILType.getNominalOrBoundGenericNominal()) { bool HasStoredProperty = false; for (auto *VD : NTD->getStoredProperties()) { HasStoredProperty = true; - auto FieldType = MemorySILType.getFieldType(VD, Module); + auto FieldType = + MemorySILType.getFieldType(VD, Module, expansionContext); unsigned NumFieldElements = - getElementCountRec(Module, FieldType, false); + getElementCountRec(expansionContext, Module, FieldType, false); if (Element < NumFieldElements) { Result += '.'; auto originalProperty = VD->getOriginalWrappedProperty(); @@ -362,7 +374,8 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, } else { Result += VD->getName().str(); } - getPathStringToElementRec(Module, FieldType, Element, Result); + getPathStringToElementRec(expansionContext, Module, FieldType, + Element, Result); return VD; } Element -= NumFieldElements; @@ -375,7 +388,8 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, } // Get the path through a tuple, if relevant. - getPathStringToElementRec(Module, MemorySILType, Element, Result); + getPathStringToElementRec(expansionContext, Module, MemorySILType, Element, + Result); // If we are analyzing a variable, we can generally get the decl associated // with it. @@ -402,9 +416,11 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { return false; } + auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction()); for (auto *VD : NTD->getStoredProperties()) { - auto FieldType = MemorySILType.getFieldType(VD, Module); - unsigned NumFieldElements = getElementCountRec(Module, FieldType, false); + auto FieldType = MemorySILType.getFieldType(VD, Module, expansionContext); + unsigned NumFieldElements = + getElementCountRec(expansionContext, Module, FieldType, false); if (Element < NumFieldElements) return VD->isLet(); Element -= NumFieldElements; @@ -498,28 +514,17 @@ static SILValue scalarizeLoad(LoadInst *LI, namespace { +/// Gathers information about a specific address and its uses to determine +/// definite initialization. class ElementUseCollector { SILModule &Module; const DIMemoryObjectInfo &TheMemory; DIElementUseInfo &UseInfo; - /// This is true if definite initialization has finished processing assign - /// and other ambiguous instructions into init vs assign classes. - bool isDefiniteInitFinished; - /// IsSelfOfNonDelegatingInitializer - This is true if we're looking at the /// top level of a 'self' variable in a non-delegating init method. bool IsSelfOfNonDelegatingInitializer; - /// How should address_to_pointer be handled? - /// - /// In DefiniteInitialization it is considered as an inout parameter to get - /// diagnostics about passing a let variable to an inout mutable-pointer - /// argument. - /// In PredictableMemOpt it is considered as an escape point to be - /// conservative. - bool TreatAddressToPointerAsInout; - /// When walking the use list, if we index into a struct element, keep track /// of this, so that any indexes into tuple subelements don't affect the /// element we attribute an access to. @@ -531,11 +536,8 @@ class ElementUseCollector { public: ElementUseCollector(const DIMemoryObjectInfo &TheMemory, - DIElementUseInfo &UseInfo, bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout) - : Module(TheMemory.MemoryInst->getModule()), TheMemory(TheMemory), - UseInfo(UseInfo), isDefiniteInitFinished(isDefiniteInitFinished), - TreatAddressToPointerAsInout(TreatAddressToPointerAsInout) {} + DIElementUseInfo &UseInfo) + : Module(TheMemory.getModule()), TheMemory(TheMemory), UseInfo(UseInfo) {} /// This is the main entry point for the use walker. It collects uses from /// the address and the refcount result of the allocation. @@ -544,7 +546,7 @@ class ElementUseCollector { // If this is a delegating initializer, collect uses specially. if (IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr) { + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr) { assert(!TheMemory.isDerivedClassSelfOnly() && "Should have been handled outside of here"); // If this is a class pointer, we need to look through ref_element_addrs. @@ -552,17 +554,16 @@ class ElementUseCollector { return; } - collectUses(TheMemory.MemoryInst, 0); - gatherDestroysOfContainer(TheMemory.MemoryInst, UseInfo); + collectUses(TheMemory.getUninitializedValue(), 0); + gatherDestroysOfContainer(TheMemory, UseInfo); } void trackUse(DIMemoryUse Use) { UseInfo.trackUse(Use); } void trackDestroy(SILInstruction *Destroy) { UseInfo.trackDestroy(Destroy); } - unsigned getNumMemoryElements() const { return TheMemory.NumElements; } - - SILInstruction *getMemoryInst() const { return TheMemory.MemoryInst; } + /// Return the raw number of elements including the 'super.init' value. + unsigned getNumMemoryElements() const { return TheMemory.getNumElements(); } private: void collectUses(SILValue Pointer, unsigned BaseEltNo); @@ -573,8 +574,6 @@ class ElementUseCollector { void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, DIUseKind Kind); void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo); - void collectDestructureTupleResultUses(DestructureTupleResult *DTR, - unsigned BaseEltNo); void collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo); }; @@ -589,9 +588,11 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, // If we're in a subelement of a struct or enum, just mark the struct, not // things that come after it in a parent tuple. unsigned NumElements = 1; - if (TheMemory.NumElements != 1 && !InStructSubElement && !InEnumSubElement) + if (TheMemory.getNumElements() != 1 && !InStructSubElement && + !InEnumSubElement) NumElements = - getElementCountRec(Module, UseTy, IsSelfOfNonDelegatingInitializer); + getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, + UseTy, IsSelfOfNonDelegatingInitializer); trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements)); } @@ -617,40 +618,14 @@ void ElementUseCollector::collectTupleElementUses(TupleElementAddrInst *TEAI, if (T.is()) { for (unsigned i = 0; i != FieldNo; ++i) { SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(Module, EltTy, false); + BaseEltNo += getElementCountRec(TypeExpansionContext(*TEAI->getFunction()), + Module, EltTy, false); } } collectUses(TEAI, BaseEltNo); } -/// Given a destructure_tuple, compute the new BaseEltNo implicit in the -/// selected member, and recursively add uses of the instruction. -void ElementUseCollector::collectDestructureTupleResultUses( - DestructureTupleResult *DTR, unsigned BaseEltNo) { - - // If we're walking into a tuple within a struct or enum, don't adjust the - // BaseElt. The uses hanging off the tuple_element_addr are going to be - // counted as uses of the struct or enum itself. - if (InStructSubElement || InEnumSubElement) - return collectUses(DTR, BaseEltNo); - - assert(!IsSelfOfNonDelegatingInitializer && "self doesn't have tuple type"); - - // tuple_element_addr P, 42 indexes into the current tuple element. - // Recursively process its uses with the adjusted element number. - unsigned FieldNo = DTR->getIndex(); - auto T = DTR->getParent()->getOperand()->getType(); - if (T.is()) { - for (unsigned i = 0; i != FieldNo; ++i) { - SILType EltTy = T.getTupleElementType(i); - BaseEltNo += getElementCountRec(Module, EltTy, false); - } - } - - collectUses(DTR, BaseEltNo); -} - void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo) { // Generally, we set the "InStructSubElement" flag and recursively process @@ -670,8 +645,9 @@ void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI, if (SEAI->getField() == VD) break; - auto FieldType = SEAI->getOperand()->getType().getFieldType(VD, Module); - BaseEltNo += getElementCountRec(Module, FieldType, false); + auto expansionContext = TypeExpansionContext(*SEAI->getFunction()); + auto FieldType = SEAI->getOperand()->getType().getFieldType(VD, Module, expansionContext); + BaseEltNo += getElementCountRec(expansionContext, Module, FieldType, false); } collectUses(SEAI, BaseEltNo); @@ -715,15 +691,15 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { continue; } - // Look through begin_access and begin_borrow - if (isa(User) || isa(User)) { + // Look through begin_access. + if (isa(User)) { auto begin = cast(User); collectUses(begin, BaseEltNo); continue; } - // Ignore end_access and end_borrow. - if (isa(User) || isa(User)) { + // Ignore end_access. + if (isa(User)) { continue; } @@ -785,8 +761,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; \ else if (SWI->isInitializationOfDest()) \ Kind = DIUseKind::Initialization; \ - else if (isDefiniteInitFinished) \ - Kind = DIUseKind::Assign; \ else \ Kind = DIUseKind::InitOrAssign; \ trackUse(DIMemoryUse(User, Kind, BaseEltNo, 1)); \ @@ -813,8 +787,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { Kind = DIUseKind::PartialStore; else if (CAI->isInitializationOfDest()) Kind = DIUseKind::Initialization; - else if (isDefiniteInitFinished) - Kind = DIUseKind::Assign; else Kind = DIUseKind::InitOrAssign; @@ -872,14 +844,14 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // by the callee, and this is enforced by sema, so we can consider it // a nonmutating use. bool isLet = true; - - for (unsigned i = 0; i < TheMemory.NumElements; ++i) { + + for (unsigned i = 0; i < TheMemory.getNumElements(); ++i) { if (!TheMemory.isElementLetProperty(i)) { isLet = false; break; } } - + if (isLet) { addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn); continue; @@ -894,7 +866,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { // inout use. DIUseKind Kind; if (TheMemory.isStructInitSelf() && - getAccessedPointer(Pointer) == TheMemory.getAddress()) { + getAccessedPointer(Pointer) == TheMemory.getUninitializedValue()) { Kind = DIUseKind::Escape; } else if (Apply.hasSelfArgument() && Op == &Apply.getSelfArgumentOperand()) { @@ -910,7 +882,7 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { llvm_unreachable("bad parameter convention"); } - if (isa(User) && TreatAddressToPointerAsInout) { + if (isa(User)) { // address_to_pointer is a mutable escape, which we model as an inout use. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutArgument); @@ -1096,9 +1068,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { collectTupleElementUses(TEAI, BaseEltNo); continue; } - - auto *DTRI = cast(EltPtr); - collectDestructureTupleResultUses(DTRI, BaseEltNo); } } } @@ -1107,28 +1076,30 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { /// constructor. The memory object has class type. void ElementUseCollector::collectClassSelfUses() { assert(IsSelfOfNonDelegatingInitializer && - TheMemory.getType()->getClassOrBoundGenericClass() != nullptr); + TheMemory.getASTType()->getClassOrBoundGenericClass() != nullptr); // For efficiency of lookup below, compute a mapping of the local ivars in the // class to their element number. llvm::SmallDenseMap EltNumbering; { - SILType T = TheMemory.MemorySILType; + SILType T = TheMemory.getType(); auto *NTD = T.getNominalOrBoundGenericNominal(); unsigned NumElements = 0; for (auto *VD : NTD->getStoredProperties()) { EltNumbering[VD] = NumElements; - NumElements += - getElementCountRec(Module, T.getFieldType(VD, Module), false); + auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); + NumElements += getElementCountRec( + expansionContext, Module, + T.getFieldType(VD, Module, expansionContext), false); } } // If we are looking at the init method for a root class, just walk the // MUI use-def chain directly to find our uses. - auto *MemoryInst = TheMemory.MemoryInst; - if (MemoryInst->getKind() == MarkUninitializedInst::RootSelf) { - collectClassSelfUses(MemoryInst, TheMemory.MemorySILType, EltNumbering); + if (TheMemory.isRootSelf()) { + collectClassSelfUses(TheMemory.getUninitializedValue(), TheMemory.getType(), + EltNumbering); return; } @@ -1145,7 +1116,7 @@ void ElementUseCollector::collectClassSelfUses() { // 4) Potential escapes after super.init, if self is closed over. // // Handle each of these in turn. - SmallVector Uses(MemoryInst->getUses()); + SmallVector Uses(TheMemory.getUninitializedValue()->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1156,7 +1127,7 @@ void ElementUseCollector::collectClassSelfUses() { // The initial store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MemoryInst->getParent()) { + if (Arg->getParent() == TheMemory.getParentBlock()) { StoresOfArgumentToSelf++; continue; } @@ -1171,7 +1142,7 @@ void ElementUseCollector::collectClassSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MemoryInst) + if (LI->getOperand() == TheMemory.getUninitializedValue()) continue; // Any other store needs to be recorded. @@ -1196,7 +1167,7 @@ void ElementUseCollector::collectClassSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { auto load = cast(User); - collectClassSelfUses(load, TheMemory.MemorySILType, EltNumbering); + collectClassSelfUses(load, TheMemory.getType(), EltNumbering); continue; } @@ -1215,7 +1186,7 @@ void ElementUseCollector::collectClassSelfUses() { // We can safely handle anything else as an escape. They should all happen // after super.init is invoked. As such, all elements must be initialized // and super.init must be called. - trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, DIUseKind::Load, 0, TheMemory.getNumElements())); } assert(StoresOfArgumentToSelf == 1 && @@ -1490,7 +1461,7 @@ void ElementUseCollector::collectClassSelfUses( Kind = DIUseKind::Escape; } - trackUse(DIMemoryUse(User, Kind, 0, TheMemory.NumElements)); + trackUse(DIMemoryUse(User, Kind, 0, TheMemory.getNumElements())); } } @@ -1610,7 +1581,7 @@ class ClassInitElementUseCollector { // *NOTE* Even though this takes a SILInstruction it actually only accepts // load_borrow and load instructions. This is enforced via an assert. - void collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, + void collectClassInitSelfLoadUses(SingleValueInstruction *MUI, SingleValueInstruction *LI); }; @@ -1622,22 +1593,22 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // When we're analyzing a delegating constructor, we aren't field sensitive at // all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit"); - auto *MUI = TheMemory.MemoryInst; + assert(TheMemory.getNumElements() == 1 && "delegating inits only have 1 bit"); + auto *uninitMemory = TheMemory.getUninitializedValue(); // The number of stores of the initial 'self' argument into the self box // that we saw. unsigned StoresOfArgumentToSelf = 0; - // We walk the use chains of the self MUI to find any accesses to it. The - // possible uses are: + // We walk the use chains of the self uninitMemory to find any accesses to it. + // The possible uses are: // 1) The initialization store. // 2) Loads of the box, which have uses of self hanging off of them. // 3) An assign to the box, which happens at super.init. // 4) Potential escapes after super.init, if self is closed over. // Handle each of these in turn. // - SmallVector Uses(MUI->getUses()); + SmallVector Uses(uninitMemory->getUses()); while (!Uses.empty()) { Operand *Op = Uses.pop_back_val(); SILInstruction *User = Op->getUser(); @@ -1661,7 +1632,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // A store of 'self' into the box at the start of the // function. Ignore it. if (auto *Arg = dyn_cast(SI->getSrc())) { - if (Arg->getParent() == MUI->getParent()) { + if (Arg->getParent() == uninitMemory->getParent()) { StoresOfArgumentToSelf++; continue; } @@ -1676,7 +1647,7 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { src = conversion->getConverted(); if (auto *LI = dyn_cast(src)) - if (LI->getOperand() == MUI) + if (LI->getOperand() == uninitMemory) continue; // Any other store needs to be recorded. @@ -1726,7 +1697,8 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { // Loads of the box produce self, so collect uses from them. if (isa(User) || isa(User)) { - collectClassInitSelfLoadUses(MUI, cast(User)); + collectClassInitSelfLoadUses(uninitMemory, + cast(User)); continue; } @@ -1743,14 +1715,11 @@ void ClassInitElementUseCollector::collectClassInitSelfUses() { assert(StoresOfArgumentToSelf == 1 && "The 'self' argument should have been stored into the box exactly once"); - - // Gather the uses of the - gatherDestroysOfContainer(MUI, UseInfo); } -void ClassInitElementUseCollector:: -collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, - SingleValueInstruction *LI) { +void ClassInitElementUseCollector::collectClassInitSelfLoadUses( + SingleValueInstruction *MUI, SingleValueInstruction *LI) { + assert(isa(MUI) || isa(MUI)); assert(isa(LI) || isa(LI)); // If we have a load, then this is a use of the box. Look at the uses of @@ -1832,24 +1801,24 @@ collectClassInitSelfLoadUses(MarkUninitializedInst *MUI, //===----------------------------------------------------------------------===// static bool shouldPerformClassInitSelf(const DIMemoryObjectInfo &MemoryInfo) { - if (MemoryInfo.MemoryInst->isDelegatingSelfAllocated()) + if (MemoryInfo.isDelegatingSelfAllocated()) return true; return MemoryInfo.isNonDelegatingInit() && - MemoryInfo.getType()->getClassOrBoundGenericClass() != nullptr && + MemoryInfo.getASTType()->getClassOrBoundGenericClass() != nullptr && MemoryInfo.isDerivedClassSelfOnly(); } -/// collectDIElementUsesFrom - Analyze all uses of the specified allocation -/// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them -/// and storing the information found into the Uses and Releases lists. +/// Analyze all uses of the specified allocation instruction (alloc_box, +/// alloc_stack or mark_uninitialized), classifying them and storing the +/// information found into the Uses and Releases lists. void swift::ownership::collectDIElementUsesFrom( - const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo, - bool isDIFinished, bool TreatAddressToPointerAsInout) { + const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo) { if (shouldPerformClassInitSelf(MemoryInfo)) { ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo); UseCollector.collectClassInitSelfUses(); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } @@ -1857,12 +1826,13 @@ void swift::ownership::collectDIElementUsesFrom( // When we're analyzing a delegating constructor, we aren't field sensitive // at all. Just treat all members of self as uses of the single // non-field-sensitive value. - assert(MemoryInfo.NumElements == 1 && "delegating inits only have 1 bit"); - collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.MemoryInst); + assert(MemoryInfo.getNumElements() == 1 && + "delegating inits only have 1 bit"); + collectDelegatingInitUses(MemoryInfo, UseInfo, + MemoryInfo.getUninitializedValue()); + gatherDestroysOfContainer(MemoryInfo, UseInfo); return; } - ElementUseCollector(MemoryInfo, UseInfo, isDIFinished, - TreatAddressToPointerAsInout) - .collectFrom(); + ElementUseCollector(MemoryInfo, UseInfo).collectFrom(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index cc9b8c56add95..66a878cc5717f 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -52,7 +52,6 @@ struct DIElementUseInfo; /// Derived classes have an additional field at the end that models whether or /// not super.init() has been called or not. class DIMemoryObjectInfo { -public: /// The uninitialized memory that we are analyzing. MarkUninitializedInst *MemoryInst; @@ -61,7 +60,8 @@ class DIMemoryObjectInfo { /// This is the count of elements being analyzed. For memory objects that are /// tuples, this is the flattened element count. For 'self' members in init - /// methods, this is the local field count (+1 for derive classes). + /// methods, this is the local field count (+1 for super/self classes were + /// initialized). unsigned NumElements; /// True if the memory object being analyzed represents a 'let', which is @@ -77,27 +77,55 @@ class DIMemoryObjectInfo { SILLocation getLoc() const { return MemoryInst->getLoc(); } SILFunction &getFunction() const { return *MemoryInst->getFunction(); } + SILModule &getModule() const { return MemoryInst->getModule(); } + SILBasicBlock *getParentBlock() const { return MemoryInst->getParent(); } /// Return the first instruction of the function containing the memory object. SILInstruction *getFunctionEntryPoint() const; - CanType getType() const { return MemorySILType.getASTType(); } + CanType getASTType() const { return MemorySILType.getASTType(); } + SILType getType() const { return MemorySILType; } + + /// Returns true if this memory object is of trivial type. + bool hasTrivialType() const { return MemorySILType.isTrivial(getFunction()); } + + /// Returns true if NumElements has a dummy value in it to force a struct to + /// be non-empty. + bool hasDummyElement() const { return HasDummyElement; } - SingleValueInstruction *getAddress() const { return MemoryInst; } + /// Return the actual 'uninitialized' memory. In the case of alloc_ref, + /// alloc_stack, this always just returns the actual mark_uninitialized + /// instruction. For alloc_box though it returns the project_box associated + /// with the memory info. + SingleValueInstruction *getUninitializedValue() const { + if (auto *mui = dyn_cast(MemoryInst)) { + if (auto *pbi = mui->getSingleUserOfType()) { + return pbi; + } + } + return MemoryInst; + } - /// getNumMemoryElements - Return the number of elements, without the extra - /// "super.init" tracker in initializers of derived classes. + /// Return the number of elements, without the extra "super.init" tracker in + /// initializers of derived classes. unsigned getNumMemoryElements() const { return NumElements - (unsigned)isDerivedClassSelf(); } - /// isAnyInitSelf - Return true if this is 'self' in any kind of initializer. + /// Return the number of elements, including the extra "super.init" tracker in + /// initializers of derived classes. + /// + /// \see getNumMemoryElements() for the number of elements, excluding the + /// extra "super.init" tracker in the initializers of derived classes. + unsigned getNumElements() const { return NumElements; } + + /// Return true if this is 'self' in any kind of initializer. bool isAnyInitSelf() const { return !MemoryInst->isVar(); } /// True if the memory object is the 'self' argument of a struct initializer. bool isStructInitSelf() const { if (MemoryInst->isRootSelf() || MemoryInst->isCrossModuleRootSelf()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -123,7 +151,7 @@ class DIMemoryObjectInfo { return false; if (!MemoryInst->isVar()) { - if (auto decl = getType()->getAnyNominal()) { + if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; } @@ -176,14 +204,23 @@ class DIMemoryObjectInfo { return false; } - /// emitElementAddress - Given an element number (in the flattened sense) - /// return a pointer to a leaf element of the specified number. - SILValue - emitElementAddress(unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, - llvm::SmallVectorImpl> - &EndBorrowList) const; + bool isRootSelf() const { + return MemoryInst->getKind() == MarkUninitializedInst::RootSelf; + } + + bool isDelegatingSelfAllocated() const { + return MemoryInst->isDelegatingSelfAllocated(); + } + + enum class EndScopeKind { Borrow, Access }; + + /// Given an element number (in the flattened sense) return a pointer to a + /// leaf element of the specified number. + SILValue emitElementAddressForDestroy( + unsigned TupleEltNo, SILLocation Loc, SILBuilder &B, + SmallVectorImpl> &EndScopeList) const; - /// getElementType - Return the swift type of the specified element. + /// Return the swift type of the specified element. SILType getElementType(unsigned EltNo) const; /// Push the symbolic path name to the specified element number onto the @@ -294,9 +331,7 @@ struct DIElementUseInfo { /// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them /// and storing the information found into the Uses and Releases lists. void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, - DIElementUseInfo &UseInfo, - bool isDefiniteInitFinished, - bool TreatAddressToPointerAsInout); + DIElementUseInfo &UseInfo); } // end namespace ownership } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp b/lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp index d459043eff3d8..64d815c502254 100644 --- a/lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp +++ b/lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp @@ -60,9 +60,8 @@ static void diagnoseMissingReturn(const UnreachableInst *UI, SILLocation L = UI->getLoc(); assert(L && ResTy); - auto numElements = BS->getNumElements(); - if (numElements > 0) { - auto element = BS->getElement(numElements - 1); + if (!BS->empty()) { + auto element = BS->getLastElement(); if (auto expr = element.dyn_cast()) { if (expr->getType()->isEqual(ResTy)) { Context.Diags.diagnose( diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 531d68083f6be..1574b966d0e12 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -435,8 +435,9 @@ namespace { void emitSelfConsumedDiagnostic(SILInstruction *Inst); LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) { - return PerBlockInfo.insert({BB, - LiveOutBlockState(TheMemory.NumElements)}).first->second; + return PerBlockInfo + .insert({BB, LiveOutBlockState(TheMemory.getNumElements())}) + .first->second; } AvailabilitySet getLivenessAtInst(SILInstruction *Inst, unsigned FirstElt, @@ -476,10 +477,14 @@ namespace { void processUninitializedRelease(SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); - void processUninitializedReleaseOfBox(AllocBoxInst *ABI, + + /// Process a mark_uninitialized of an alloc_box that is uninitialized and + /// needs a dealloc_box. + void processUninitializedReleaseOfBox(MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt); + void deleteDeadRelease(unsigned ReleaseID); void processNonTrivialRelease(unsigned ReleaseID); @@ -508,12 +513,9 @@ namespace { LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, DIElementUseInfo &UseInfo) - : F(*TheMemory.MemoryInst->getFunction()), - Module(TheMemory.MemoryInst->getModule()), - TheMemory(TheMemory), - Uses(UseInfo.Uses), - StoresToSelf(UseInfo.StoresToSelf), - Destroys(UseInfo.Releases) { + : F(TheMemory.getFunction()), Module(TheMemory.getModule()), + TheMemory(TheMemory), Uses(UseInfo.Uses), + StoresToSelf(UseInfo.StoresToSelf), Destroys(UseInfo.Releases) { // The first step of processing an element is to collect information about the // element into data structures we use later. @@ -558,10 +560,10 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, getBlockInfo(bb).markStoreToSelf(); } - // If isn't really a use, but we account for the alloc_box/mark_uninitialized - // as a use so we see it in our dataflow walks. - NonLoadUses[TheMemory.MemoryInst] = ~0U; - auto &MemBBInfo = getBlockInfo(TheMemory.MemoryInst->getParent()); + // If isn't really a use, but we account for the mark_uninitialized or + // project_box as a use so we see it in our dataflow walks. + NonLoadUses[TheMemory.getUninitializedValue()] = ~0U; + auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock()); MemBBInfo.HasNonLoadUse = true; // There is no scanning required (or desired) for the block that defines the @@ -640,7 +642,7 @@ void LifetimeChecker::noteUninitializedMembers(const DIMemoryUse &Use) { if (Liveness.get(i) == DIKind::Yes) continue; // Ignore a failed super.init requirement. - if (i == TheMemory.NumElements-1 && TheMemory.isDerivedClassSelf()) + if (i == TheMemory.getNumElements() - 1 && TheMemory.isDerivedClassSelf()) continue; std::string Name; @@ -672,7 +674,7 @@ std::string LifetimeChecker::getUninitElementName(const DIMemoryUse &Use) { // Verify that it isn't the super.init marker that failed. The client should // handle this, not pass it down to diagnoseInitError. assert((!TheMemory.isDerivedClassSelf() || - firstUndefElement != TheMemory.NumElements-1) && + firstUndefElement != TheMemory.getNumElements() - 1) && "super.init failure not handled in the right place"); // If the definition is a declaration, try to reconstruct a name and @@ -792,11 +794,12 @@ void LifetimeChecker::doIt() { // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process // releases to adjust for this. - if (!TheMemory.MemorySILType.isTrivial(F)) { + if (!TheMemory.hasTrivialType()) { + // NOTE: This array may increase in size! for (unsigned i = 0, e = Destroys.size(); i != e; ++i) processNonTrivialRelease(i); } - + // If the memory object had any non-trivial stores that are init or assign // based on the control flow path reaching them, then insert dynamic control // logic and CFG diamonds to handle this. @@ -805,7 +808,7 @@ void LifetimeChecker::doIt() { HasConditionalDestroy || HasConditionalSelfInitialized) { ControlVariable = handleConditionalInitAssign(); - SILValue memAddr = TheMemory.MemoryInst->getOperand(); + SILValue memAddr = TheMemory.getUninitializedValue()->getOperand(0); if (auto *ASI = dyn_cast(memAddr)) { ASI->setDynamicLifetime(); } else if (auto *ABI = dyn_cast(memAddr)) { @@ -938,8 +941,8 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { emitSelfConsumedDiagnostic(Use.Inst); return; @@ -1006,7 +1009,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *projection = dyn_cast(addr)) addr = projection->getOperand(); - return addr == TheMemory.getAddress(); + return addr == TheMemory.getUninitializedValue(); }; if (!isFullyInitialized && WantsCrossModuleStructInitializerDiagnostic && @@ -1020,7 +1023,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { if (auto *ctor = fnLoc.getAsASTNode()) selfTy = ctor->getImplicitSelfDecl()->getType(); else - selfTy = TheMemory.getType(); + selfTy = TheMemory.getASTType(); StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1284,7 +1287,7 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { diagnose(Module, Inst->getLoc(), diag::self_before_selfinit_value_type); if (!HasSuggestedNoArgSelfInit && FullyUninitialized) { auto *maybeStruct = - TheMemory.getType().getStructOrBoundGenericStruct(); + TheMemory.getASTType().getStructOrBoundGenericStruct(); maybeSuggestNoArgSelfInit(Module, Inst->getLoc(), maybeStruct); HasSuggestedNoArgSelfInit = true; } @@ -1400,7 +1403,7 @@ findMethodForStoreInitializationOfTemporary(const DIMemoryObjectInfo &TheMemory, // argument, so the ownership verifier would trip. So we know that such a // thing can not happen. On the other hand, for store_borrow, we need to // strip the borrow, so lets use idempotence for correctness. - if (stripBorrow(SI->getSrc()) != TheMemory.MemoryInst || + if (stripBorrow(SI->getSrc()) != TheMemory.getUninitializedValue() || !isa(SI->getDest()) || !TheMemory.isClassInitSelf()) { return nullptr; } @@ -1618,9 +1621,8 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties( if (!shouldEmitError(Inst)) return true; - if (TheMemory.isCrossModuleStructInitSelf() && - TheMemory.HasDummyElement) { - Type selfTy = TheMemory.getType(); + if (TheMemory.isCrossModuleStructInitSelf() && TheMemory.hasDummyElement()) { + Type selfTy = TheMemory.getASTType(); const StructDecl *theStruct = selfTy->getStructOrBoundGenericStruct(); assert(theStruct); @@ -1653,8 +1655,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use, // Stores back to the 'self' box are OK. if (auto store = dyn_cast(Inst)) { - if (store->getDest() == TheMemory.MemoryInst - && TheMemory.isClassInitSelf()) + if (store->getDest() == TheMemory.getUninitializedValue() && + TheMemory.isClassInitSelf()) return; } @@ -1813,15 +1815,16 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { assert(TheMemory.isAnyInitSelf()); assert(!TheMemory.isClassInitSelf() || TheMemory.isNonRootClassSelf()); - assert(TheMemory.getType()->hasReferenceSemantics()); + assert(TheMemory.getASTType()->hasReferenceSemantics()); // Determine the liveness states of the memory object, including the // self/super.init state. - AvailabilitySet Liveness = getLivenessAtInst(Inst, 0, TheMemory.NumElements); + AvailabilitySet Liveness = + getLivenessAtInst(Inst, 0, TheMemory.getNumElements()); // self/super.init() calls require that self/super.init has not already // been called. If it has, reject the program. - switch (Liveness.get(TheMemory.NumElements-1)) { + switch (Liveness.get(TheMemory.getNumElements() - 1)) { case DIKind::No: // This is good! Keep going. break; case DIKind::Yes: @@ -1839,7 +1842,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } if (TheMemory.isDelegatingInit()) { - assert(TheMemory.NumElements == 1 && "delegating inits have a single elt"); + assert(TheMemory.getNumElements() == 1 && + "delegating inits have a single elt"); // Lower Assign instructions if needed. if (isa(Use.Inst) || isa(Use.Inst)) @@ -1847,7 +1851,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { } else { // super.init also requires that all ivars are initialized before the // superclass initializer runs. - for (unsigned i = 0, e = TheMemory.NumElements-1; i != e; ++i) { + for (unsigned i = 0, e = TheMemory.getNumElements() - 1; i != e; ++i) { if (Liveness.get(i) == DIKind::Yes) continue; // If the super.init call is implicit generated, produce a specific @@ -1942,12 +1946,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) { } void LifetimeChecker::processUninitializedReleaseOfBox( - AllocBoxInst *ABI, SILInstruction *Release, bool consumed, + MarkUninitializedInst *MUI, SILInstruction *Release, bool consumed, SILBasicBlock::iterator InsertPt) { - assert(ABI == Release->getOperand(0)); + assert(isa(MUI->getOperand())); + assert(MUI == Release->getOperand(0)); SILBuilderWithScope B(Release); B.setInsertionPoint(InsertPt); - Destroys.push_back(B.createDeallocBox(Release->getLoc(), ABI)); + Destroys.push_back(B.createDeallocBox(Release->getLoc(), MUI)); } void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, @@ -1957,8 +1962,10 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // dealloc_partial_ref to free the memory. If this is a derived class, we // may have to do a load of the 'self' box to get the class reference. if (!TheMemory.isClassInitSelf()) { - if (auto *ABI = dyn_cast(Release->getOperand(0))) { - return processUninitializedReleaseOfBox(ABI, Release, consumed, InsertPt); + if (auto *MUI = dyn_cast(Release->getOperand(0))) { + if (isa(MUI->getOperand())) { + return processUninitializedReleaseOfBox(MUI, Release, consumed, InsertPt); + } } return; } @@ -1973,15 +1980,20 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, // If we see an alloc_box as the pointer, then we're deallocating a 'box' for // self. Make sure that the box gets deallocated (not released) since the // pointer it contains will be manually cleaned up. - auto *ABI = dyn_cast(Release->getOperand(0)); - if (ABI) - Pointer = getOrCreateProjectBox(ABI, 0); + auto *MUI = dyn_cast(Release->getOperand(0)); + + if (MUI && isa(MUI->getOperand())) { + Pointer = MUI->getSingleUserOfType(); + assert(Pointer); + } else { + MUI = nullptr; + } if (!consumed) { if (Pointer->getType().isAddress()) Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take); - auto MetatypeTy = CanMetatypeType::get(TheMemory.MemorySILType.getASTType(), + auto MetatypeTy = CanMetatypeType::get(TheMemory.getASTType(), MetatypeRepresentation::Thick); auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy); SILValue Metatype; @@ -2000,8 +2012,8 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release, } // dealloc_box the self box if necessary. - if (ABI) { - auto DB = B.createDeallocBox(Loc, ABI); + if (MUI) { + auto DB = B.createDeallocBox(Loc, MUI); Destroys.push_back(DB); } } @@ -2010,8 +2022,13 @@ void LifetimeChecker::deleteDeadRelease(unsigned ReleaseID) { SILInstruction *Release = Destroys[ReleaseID]; if (isa(Release)) { SILValue Addr = Release->getOperand(0); - if (auto *AddrI = Addr->getDefiningInstruction()) + if (auto *AddrI = Addr->getDefiningInstruction()) { + // FIXME: AddrI will not be deleted (nor its operands) when Release is + // still using AddrI's result. Fix this, and migrate to using + // InstructionDeleter utility instead of + // recursivelyDeadTriviallyDeadInstructions. recursivelyDeleteTriviallyDeadInstructions(AddrI); + } } Release->eraseFromParent(); Destroys[ReleaseID] = nullptr; @@ -2041,7 +2058,7 @@ void LifetimeChecker::processNonTrivialRelease(unsigned ReleaseID) { assert(isa(Release) || isa(Release) || isa(Release)); - auto Availability = getLivenessAtInst(Release, 0, TheMemory.NumElements); + auto Availability = getLivenessAtInst(Release, 0, TheMemory.getNumElements()); DIKind SelfInitialized = DIKind::Yes; if (TheMemory.isNonRootClassSelf()) { @@ -2184,7 +2201,7 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { SILLocation Loc = TheMemory.getLoc(); Loc.markAutoGenerated(); - unsigned NumMemoryElements = TheMemory.NumElements; + unsigned NumMemoryElements = TheMemory.getNumElements(); // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2213,7 +2230,8 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { } // Before the memory allocation, store zero in the control variable. - auto *InsertPoint = &*std::next(TheMemory.MemoryInst->getIterator()); + auto *InsertPoint = + &*std::next(TheMemory.getUninitializedValue()->getIterator()); B.setInsertionPoint(InsertPoint); B.setCurrentDebugScope(InsertPoint->getDebugScope()); SILValue ControlVariableAddr = ControlVariableBox; @@ -2285,14 +2303,26 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { B.setInsertionPoint(TrueBB->begin()); SILValue EltPtr; { - llvm::SmallVector, 4> EndBorrowList; - EltPtr = TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; + EltPtr = + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } } B.setInsertionPoint(ContBB->begin()); @@ -2331,13 +2361,13 @@ SILValue LifetimeChecker::handleConditionalInitAssign() { /// to emit branching logic when an element may or may not be initialized. void LifetimeChecker:: handleConditionalDestroys(SILValue ControlVariableAddr) { - SILBuilderWithScope B(TheMemory.MemoryInst); + SILBuilderWithScope B(TheMemory.getUninitializedValue()); Identifier ShiftRightFn, TruncateFn; - unsigned NumMemoryElements = TheMemory.NumElements; - - unsigned SelfInitializedElt = TheMemory.NumElements; - unsigned SuperInitElt = TheMemory.NumElements - 1; + unsigned NumMemoryElements = TheMemory.getNumElements(); + + unsigned SelfInitializedElt = TheMemory.getNumElements(); + unsigned SuperInitElt = TheMemory.getNumElements() - 1; // We might need an extra bit to check if self was consumed. if (HasConditionalSelfInitialized) @@ -2346,15 +2376,27 @@ handleConditionalDestroys(SILValue ControlVariableAddr) { // Utilities. auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) { - llvm::SmallVector, 4> EndBorrowList; + using EndScopeKind = DIMemoryObjectInfo::EndScopeKind; + SmallVector, 4> EndScopeList; SILValue EltPtr = - TheMemory.emitElementAddress(Elt, Loc, B, EndBorrowList); + TheMemory.emitElementAddressForDestroy(Elt, Loc, B, EndScopeList); if (auto *DA = B.emitDestroyAddrAndFold(Loc, EltPtr)) Destroys.push_back(DA); - while (!EndBorrowList.empty()) { - SILValue Borrowed, Original; - std::tie(Borrowed, Original) = EndBorrowList.pop_back_val(); - B.createEndBorrow(Loc, Borrowed, Original); + + while (!EndScopeList.empty()) { + SILValue value; + EndScopeKind kind; + std::tie(value, kind) = EndScopeList.pop_back_val(); + + switch (kind) { + case EndScopeKind::Borrow: + B.createEndBorrow(Loc, value); + continue; + case EndScopeKind::Access: + B.createEndAccess(Loc, value, false /*can abort*/); + continue; + } + llvm_unreachable("Covered switch isn't covered!"); } }; @@ -2668,7 +2710,8 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst, // is not defined at all yet. Otherwise, we've found a definition, or // something else that will require that the memory is initialized at // this point. - Result.set(0, TheInst == TheMemory.MemoryInst ? DIKind::No : DIKind::Yes); + Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No + : DIKind::Yes); return Result; } } @@ -2694,7 +2737,7 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, LLVM_DEBUG(llvm::dbgs() << "Get liveness " << FirstElt << ", #" << NumElts << " at " << *Inst); - AvailabilitySet Result(TheMemory.NumElements); + AvailabilitySet Result(TheMemory.getNumElements()); // Empty tuple queries return a completely "unknown" vector, since they don't // care about any of the elements. @@ -2705,13 +2748,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // The vastly most common case is memory allocations that are not tuples, // so special case this with a more efficient algorithm. - if (TheMemory.NumElements == 1) { + if (TheMemory.getNumElements() == 1) { return getLivenessAtNonTupleInst(Inst, InstBB, Result); } // Check locally to see if any elements are satisfied within the block, and // keep track of which ones are still needed in the NeededElements set. - SmallBitVector NeededElements(TheMemory.NumElements); + SmallBitVector NeededElements(TheMemory.getNumElements()); NeededElements.set(FirstElt, FirstElt+NumElts); // If there is a store in the current block, scan the block to see if the @@ -2729,13 +2772,13 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst, // If we found the allocation itself, then we are loading something that // is not defined at all yet. Scan no further. - if (TheInst == TheMemory.MemoryInst) { + if (TheInst == TheMemory.getUninitializedValue()) { // The result is perfectly decided locally. for (unsigned i = FirstElt, e = i+NumElts; i != e; ++i) Result.set(i, NeededElements[i] ? DIKind::No : DIKind::Yes); return Result; } - + // Check to see which tuple elements this instruction defines. Clear them // from the set we're scanning from. auto &TheInstUse = Uses[It->second]; @@ -2838,9 +2881,9 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // If the client wants to know about super.init, check to see if we failed // it or some other element. - if (Use.FirstElement+Use.NumElements == TheMemory.NumElements && + if (Use.FirstElement + Use.NumElements == TheMemory.getNumElements() && TheMemory.isAnyDerivedClassSelf() && - Liveness.get(Liveness.size()-1) != DIKind::Yes) { + Liveness.get(Liveness.size() - 1) != DIKind::Yes) { if (SuperInitDone) *SuperInitDone = false; } @@ -2860,8 +2903,8 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use, // we caught it, self is no longer available. if (TheMemory.isNonRootClassSelf()) { if (getSelfInitializedAtInst(Use.Inst) != DIKind::Yes) { - auto SelfLiveness = getLivenessAtInst(Use.Inst, - 0, TheMemory.NumElements); + auto SelfLiveness = + getLivenessAtInst(Use.Inst, 0, TheMemory.getNumElements()); if (SelfLiveness.isAllYes()) { if (FailedSelfUse) *FailedSelfUse = true; return false; @@ -2884,8 +2927,7 @@ static void processMemoryObject(MarkUninitializedInst *I) { DIElementUseInfo UseInfo; // Walk the use list of the pointer, collecting them into the Uses array. - collectDIElementUsesFrom(MemInfo, UseInfo, false, - /*TreatAddressToPointerAsInout*/ true); + collectDIElementUsesFrom(MemInfo, UseInfo); LifetimeChecker(MemInfo, UseInfo).doIt(); } diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp index 95cdea2529a92..3003b60ff5985 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp @@ -323,19 +323,33 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC, // Otherwise, we have at least one escaping use of a partial_apply // capturing a non-escaping value. We need to emit diagnostics. + // Should match SELECT_ESCAPING_CLOSURE_KIND in DiagnosticsSIL.def. + enum { + EscapingLocalFunction, + EscapingClosure + } functionKind = EscapingClosure; + + if (auto *F = PAI->getReferencedFunctionOrNull()) { + if (auto loc = F->getLocation()) { + if (loc.isASTNode()) + functionKind = EscapingLocalFunction; + } + } // First, diagnose the inout captures, if any. for (auto inoutCapture : inoutCaptures) { if (isUseOfSelfInInitializer(inoutCapture)) { - diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture); + diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture, + functionKind); } else { auto *param = getParamDeclFromOperand(inoutCapture->get()); if (param->isSelfParameter()) - diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture); + diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture, + functionKind); else { diagnose(Context, PAI->getLoc(), diag::escaping_inout_capture, - param->getName()); + functionKind, param->getName()); diagnose(Context, param->getLoc(), diag::inout_param_defined_here, - param->getName()); + param->getName()); } } @@ -346,11 +360,12 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC, for (auto noEscapeCapture : noEscapeCaptures) { if (auto *param = getParamDeclFromOperand(noEscapeCapture->get())) { diagnose(Context, PAI->getLoc(), diag::escaping_noescape_param_capture, - param->getName()); + functionKind, param->getName()); diagnose(Context, param->getLoc(), diag::noescape_param_defined_here, param->getName()); } else { - diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture); + diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture, + functionKind); } diagnoseCaptureLoc(Context, DC, PAI, noEscapeCapture); diff --git a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp index f4f6247e4aad5..0dec95dd11056 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp @@ -499,12 +499,14 @@ static void addSwapAtFixit(InFlightDiagnostic &Diag, CallExpr *&FoundCall, /// and tuple elements. static std::string getPathDescription(DeclName BaseName, SILType BaseType, const IndexTrieNode *SubPath, - SILModule &M) { + SILModule &M, + TypeExpansionContext context) { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "'" << BaseName; - os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M); + os << AccessSummaryAnalysis::getSubPathDescription(BaseType, SubPath, M, + context); os << "'"; return os.str(); @@ -547,7 +549,8 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation, SILType BaseType = FirstAccess.getInstruction()->getType().getAddressType(); SILModule &M = FirstAccess.getInstruction()->getModule(); std::string PathDescription = getPathDescription( - VD->getBaseName(), BaseType, MainAccess.getSubPath(), M); + VD->getBaseName(), BaseType, MainAccess.getSubPath(), M, + TypeExpansionContext(*FirstAccess.getInstruction()->getFunction())); // Determine whether we can safely suggest replacing the violation with // a call to MutableCollection.swapAt(). diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index 3a60405702a3f..107829132d2a8 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -185,12 +185,9 @@ static void propagateBasicBlockArgs(SILBasicBlock &BB) { // this to CCP and trigger another round of copy propagation. SILArgument *Arg = *AI; - // If this argument is guaranteed and Args[Idx] is a SILFunctionArgument, - // delete the end_borrow. - if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed && - isa(Args[Idx])) { + // If this argument is guaranteed and Args[Idx], delete the end_borrow. + if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed) deleteEndBorrows(Arg); - } // We were able to fold, so all users should use the new folded value. Arg->replaceAllUsesWith(Args[Idx]); @@ -208,9 +205,8 @@ static void propagateBasicBlockArgs(SILBasicBlock &BB) { static bool constantFoldEnumTerminator(SILBasicBlock &BB, UnreachableUserCodeReportingState *State, SwitchEnumInstBase *SUI, - const EnumElementDecl *TheEnumElem, - SILValue value = SILValue(), - SILValue defaultValue = SILValue()) { + EnumElementDecl *TheEnumElem, + EnumInst *EnumInst) { SILBasicBlock *TheSuccessorBlock = nullptr; int ReachableBlockIdx = -1; for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { @@ -251,11 +247,9 @@ static bool constantFoldEnumTerminator(SILBasicBlock &BB, // value. SILValue branchOperand; if (TheSuccessorBlock != DB) { - assert(value); - branchOperand = value; + branchOperand = B.createUncheckedEnumData(Loc, EnumInst, TheEnumElem); } else { - assert(defaultValue); - branchOperand = defaultValue; + branchOperand = EnumInst; } B.createBranch(Loc, TheSuccessorBlock, branchOperand); } else @@ -297,6 +291,81 @@ static bool constantFoldEnumTerminator(SILBasicBlock &BB, return true; } +static bool constantFoldEnumAddrTerminator( + SILBasicBlock &BB, UnreachableUserCodeReportingState *State, + SwitchEnumInstBase *SUI, const EnumElementDecl *TheEnumElem) { + SILBasicBlock *TheSuccessorBlock = nullptr; + int ReachableBlockIdx = -1; + for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { + const EnumElementDecl *EI; + SILBasicBlock *BI; + std::tie(EI, BI) = SUI->getCase(Idx); + if (EI == TheEnumElem) { + TheSuccessorBlock = BI; + ReachableBlockIdx = Idx; + break; + } + } + + SILBasicBlock *DB = nullptr; + if (!TheSuccessorBlock) { + if (SUI->hasDefault()) { + DB = SUI->getDefaultBB(); + if (!isa(DB->getTerminator())) { + TheSuccessorBlock = DB; + ReachableBlockIdx = SUI->getNumCases(); + } + } + } + + // Not fully covered switches will be diagnosed later. SILGen represents + // them with a Default basic block with an unreachable instruction. + // We are going to produce an error on all unreachable instructions not + // eliminated by DCE. + if (!TheSuccessorBlock) + return false; + + // Replace the switch with a branch to the TheSuccessorBlock. + SILBuilderWithScope B(&BB, SUI); + SILLocation Loc = SUI->getLoc(); + B.createBranch(Loc, TheSuccessorBlock); + + // Produce diagnostic info if we are not within an inlined function or + // template instantiation. + // FIXME: Do not report if we are within a template instantiation. + assert(ReachableBlockIdx >= 0); + if (Loc.is() && State) { + // Find the first unreachable block in the switch so that we could use + // it for better diagnostics. + SILBasicBlock *UnreachableBlock = nullptr; + if (SUI->getNumCases() > 1) { + // More than one case. + UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getCase(1).second + : SUI->getCase(0).second; + } else { + if (SUI->getNumCases() == 1 && SUI->hasDefault()) { + // One case and a default. + UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getDefaultBB() + : SUI->getCase(0).second; + } + } + + // Generate diagnostic info. + if (UnreachableBlock && + !State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { + State->PossiblyUnreachableBlocks.insert(UnreachableBlock); + State->MetaMap.insert(std::pair( + UnreachableBlock, + UnreachableInfo{UnreachableKind::FoldedSwitchEnum, Loc, true})); + } + } + + LLVM_DEBUG(llvm::dbgs() << "Folding terminator: " << *SUI); + recursivelyDeleteTriviallyDeadInstructions(SUI, true); + NumTerminatorsFolded++; + return true; +} + static InjectEnumAddrInst * getAllocStackSingleInitializingInjectEnumAddr(SwitchEnumAddrInst *SEAI) { auto *stackSlot = dyn_cast(SEAI->getOperand()); @@ -506,10 +575,8 @@ static bool constantFoldTerminator(SILBasicBlock &BB, // br bb2 if (auto *SEI = dyn_cast(TI)) { if (auto *TheEnum = dyn_cast(SEI->getOperand())) { - SILValue operand = - TheEnum->hasOperand() ? TheEnum->getOperand() : SILValue(); return constantFoldEnumTerminator(BB, State, SEI, TheEnum->getElement(), - operand /*case*/, TheEnum /*default*/); + TheEnum); } } if (auto *SEAI = dyn_cast(TI)) { @@ -520,7 +587,8 @@ static bool constantFoldTerminator(SILBasicBlock &BB, // // TODO: This needs a better name. if (auto *IEAI = getAllocStackSingleInitializingInjectEnumAddr(SEAI)) { - return constantFoldEnumTerminator(BB, State, SEAI, IEAI->getElement()); + return constantFoldEnumAddrTerminator(BB, State, SEAI, + IEAI->getElement()); } } diff --git a/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp b/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp index f9160fa0eaad5..6013dc4801556 100644 --- a/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp @@ -75,13 +75,13 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) { switch (Inst->getKind()) { #define UNCHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Name##RetainValueInst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Name##RetainInst: \ - case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::StrongCopy##Name##ValueInst: #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp index 6da647c694476..c1c98ee765284 100644 --- a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -56,12 +56,17 @@ static bool cleanFunction(SILFunction &fn) { LLVM_FALLTHROUGH; } case BuiltinValueKind::PoundAssert: - case BuiltinValueKind::StaticReport: + case BuiltinValueKind::StaticReport: { // The call to the builtin should get removed before we reach // IRGen. - recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); + InstructionDeleter deleter; + deleter.forceDelete(bi); + // StaticReport only takes trivial operands, and therefore doesn't + // require fixing the lifetime of its operands. + deleter.cleanUpDeadInstructions(); madeChange = true; break; + } default: break; } diff --git a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp index 172914abe73cf..8c99b519dcf55 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp @@ -77,15 +77,17 @@ class MandatoryCombiner final SmallVector instructionsPendingDeletion; public: - MandatoryCombiner( - SmallVectorImpl &createdInstructions) + MandatoryCombiner(SmallVectorImpl &createdInstructions) : worklist("MC"), madeChange(false), iteration(0), instModCallbacks( [&](SILInstruction *instruction) { worklist.erase(instruction); instructionsPendingDeletion.push_back(instruction); }, - [&](SILInstruction *instruction) { worklist.add(instruction); }), + [&](SILInstruction *instruction) { worklist.add(instruction); }, + [this](SILValue oldValue, SILValue newValue) { + worklist.replaceValueUsesWith(oldValue, newValue); + }), createdInstructions(createdInstructions){}; void addReachableCodeToWorklist(SILFunction &function); @@ -216,6 +218,7 @@ bool MandatoryCombiner::doOneIteration(SILFunction &function, //===----------------------------------------------------------------------===// SILInstruction *MandatoryCombiner::visitApplyInst(ApplyInst *instruction) { + // Apply this pass only to partial applies all of whose arguments are // trivial. auto calledValue = instruction->getCallee(); diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 6504b6f338f5f..151726a6b817f 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -159,7 +159,7 @@ static void fixupReferenceCounts( // insert a destroy after the apply since the leak will just cover the // other path. if (!error.getFoundOverConsume()) { - applySite.insertAfter([&](SILBasicBlock::iterator iter) { + applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { if (hasOwnership) { SILBuilderWithScope(iter).createEndBorrow(loc, argument); } @@ -199,7 +199,7 @@ static void fixupReferenceCounts( } } - applySite.insertAfter([&](SILBasicBlock::iterator iter) { + applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { SILBuilderWithScope(iter).emitDestroyValueOperation(loc, v); }); break; @@ -246,15 +246,19 @@ static void fixupReferenceCounts( // Destroy the callee as the apply would have done if our function is not // callee guaranteed. if (!isCalleeGuaranteed) { - applySite.insertAfter([&](SILBasicBlock::iterator iter) { + applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) { SILBuilderWithScope(iter).emitDestroyValueOperation(loc, calleeValue); }); } } static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) { - auto *pbi = cast(li->getOperand()); - auto *abi = cast(pbi->getOperand()); + auto *pbi = dyn_cast(li->getOperand()); + if (!pbi) + return SILValue(); + auto *abi = dyn_cast(pbi->getOperand()); + if (!abi) + return SILValue(); // The load instruction must have no more uses or a single destroy left to // erase it. @@ -594,7 +598,7 @@ getCalleeFunction(SILFunction *F, FullApplySite AI, bool &IsThick, FullArgs.clear(); // First grab our basic arguments from our apply. - for (const auto &Arg : AI.getArguments()) + for (const auto Arg : AI.getArguments()) FullArgs.push_back(Arg); // Then grab a first approximation of our apply by stripping off all copy diff --git a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp b/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp deleted file mode 100644 index 4c1f330c7942b..0000000000000 --- a/lib/SILOptimizer/Mandatory/MarkUninitializedFixup.cpp +++ /dev/null @@ -1,143 +0,0 @@ -//===--- MarkUninitializedFixup.cpp ---------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-ownership-model-eliminator" -#include "swift/SIL/SILBuilder.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILVisitor.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" - -using namespace swift; - -//===----------------------------------------------------------------------===// -// Top Level Entry Point -//===----------------------------------------------------------------------===// - -static ProjectBoxInst * -getInitialProjectBox(MarkUninitializedInst *MUI, - ArrayRef Projections) { - assert(!Projections.empty()); - if (Projections.size() == 1) { - auto *PBI = Projections[0]; - assert(PBI->getParent() == MUI->getParent()); - return PBI; - } - - // Otherwise, we want to select the earliest project box. There should - // only be one. - ProjectBoxInst *PBI = Projections[0]; - - // Otherwise, we need to find the one that is closest to the - // mark_uninitialized. It should be in the same block. - for (auto *I : makeArrayRef(Projections).slice(1)) { - // If the new instruction is in a different block than the - // mark_uninitialized, it can not be a good solution, so skip it. - if (I->getParent() != MUI->getParent()) { - continue; - } - - // If PBI is not in the same block as the MUI, but I is, we picked a - // bad initial PBI, set PBI to I. - if (PBI->getParent() != MUI->getParent()) { - // Otherwise, I is a better candidate than PBI so set PBI to I. - PBI = I; - continue; - } - - // Otherwise, we have that PBI and I are both in the same block. See - // which one is first. - auto *BB = PBI->getParent(); - if (BB->end() != std::find_if(PBI->getIterator(), BB->end(), - [&I](const SILInstruction &InnerI) -> bool { - return I == &InnerI; - })) { - continue; - } - - PBI = I; - } - - assert(PBI->getParent() == MUI->getParent()); - return PBI; -} - -namespace { - -struct MarkUninitializedFixup : SILFunctionTransform { - void run() override { - // Don't rerun on deserialized functions. Nothing should have changed. - if (getFunction()->wasDeserializedCanonical()) - return; - - bool MadeChange = false; - for (auto &BB : *getFunction()) { - for (auto II = BB.begin(), IE = BB.end(); II != IE;) { - // Grab our given instruction and advance the iterator. This is - // important since we may be destroying the given instruction. - auto *MUI = dyn_cast(&*II); - ++II; - - // If we do not have a mark_uninitialized or we have a - // mark_uninitialized of an alloc_box, continue. These are not - // interesting to us. - if (!MUI) - continue; - - auto *Box = dyn_cast(MUI->getOperand()); - if (!Box) - continue; - - // We expect there to be in most cases exactly one project_box. That - // being said, it is not impossible for there to be multiple. In such - // a case, we assume that the correct project_box is the one that is - // nearest to the mark_uninitialized in the same block. This preserves - // the existing behavior. - llvm::TinyPtrVector Projections; - for (auto *Op : MUI->getUses()) { - if (auto *PBI = dyn_cast(Op->getUser())) { - Projections.push_back(PBI); - } - } - assert(!Projections.empty() && "SILGen should never emit a " - "mark_uninitialized by itself"); - - // First replace all uses of the mark_uninitialized with the box. - MUI->replaceAllUsesWith(Box); - - // That means now our project box now has the alloc_box as its - // operand. Grab that project_box. - auto *PBI = getInitialProjectBox(MUI, Projections); - - // Then create the new mark_uninitialized and force all uses of the - // project_box to go through the new mark_uninitialized. - SILBuilderWithScope B(&*std::next(PBI->getIterator())); - SILValue Undef = SILUndef::get(PBI->getType(), *PBI->getFunction()); - auto *NewMUI = - B.createMarkUninitialized(PBI->getLoc(), Undef, MUI->getKind()); - PBI->replaceAllUsesWith(NewMUI); - NewMUI->setOperand(PBI); - - // Finally, remove the old mark_uninitialized. - MUI->eraseFromParent(); - MadeChange = true; - } - } - if (MadeChange) - invalidateAnalysis( - SILAnalysis::InvalidationKind::BranchesAndInstructions); - } -}; -} // end anonymous namespace - -SILTransform *swift::createMarkUninitializedFixup() { - return new MarkUninitializedFixup(); -} diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 2bfc9a1a55705..434a5aa6a29c9 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -35,10 +35,11 @@ /// following steps on each such call. /// /// 1. Determines the range of instructions to constant evaluate. -/// The range starts from the first SIL instruction that corresponds to the +/// The range starts from the first SIL instruction that begins the /// construction of the custom string interpolation type: OSLogMessage to -/// its destruction. The log call is also inlined into the -/// caller. +/// the last transitive users of OSLogMessage. The log call which is marked +/// as @_transparent will be inlined into the caller before this pass +/// begins. /// /// 2. Constant evaluates the range of instruction identified in Step 1 and /// collects string and integer-valued instructions who values were found @@ -59,13 +60,15 @@ /// Code Overview: /// /// The function 'OSLogOptimization::run' implements the overall driver for -/// steps 1 to 4. The function 'beginOfInterpolation' implements step 1. It -/// computes the instruction from which the evaluation must start. The function -/// 'constantFold' is a driver for the steps 2 to 4. Step 2 is implemented by -/// the function 'collectConstants', step 3 by 'detectAndDiagnoseErrors', and -/// step 4 by 'substituteConstants' and 'emitCodeForSymbolicValue'. -/// The remaining functions in the file implement the subtasks and utilities -/// needed by the above functions. +/// steps 1 to 4. The function 'beginOfInterpolation' identifies the begining of +/// interpolation (step 1) and the function 'getEndPointsOfDataDependentChain' +/// identifies the last transitive users of the OSLogMessage instance (step 1). +/// The function 'constantFold' is a driver for the steps 2 to 4. Step 2 is +/// implemented by the function 'collectConstants', step 3 by +/// 'detectAndDiagnoseErrors' and 'checkOSLogMessageIsConstant', and step 4 by +/// 'substituteConstants' and 'emitCodeForSymbolicValue'. The remaining +/// functions in the file implement the subtasks and utilities needed by the +/// above functions. #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" @@ -74,9 +77,13 @@ #include "swift/AST/Module.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/OptimizationMode.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/Demangler.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/CFG.h" #include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILConstants.h" @@ -84,18 +91,20 @@ #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILLocation.h" #include "swift/SIL/SILModule.h" +#include "swift/SIL/TypeLowering.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/ConstExpr.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILInliner.h" #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" -#include "llvm/ADT/MapVector.h" -#include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/CFG.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/BreadthFirstIterator.h" +#include "llvm/ADT/MapVector.h" using namespace swift; +using namespace Lowering; template static void diagnose(ASTContext &Context, SourceLoc loc, Diag diag, @@ -114,7 +123,7 @@ static SILFunction *getStringMakeUTF8Init(SILInstruction *inst) { return nullptr; SILFunction *callee = apply->getCalleeFunction(); - if (!callee || !callee->hasSemanticsAttr("string.makeUTF8")) + if (!callee || !callee->hasSemanticsAttr(semantics::STRING_MAKE_UTF8)) return nullptr; return callee; } @@ -177,10 +186,7 @@ class FoldState { /// Instruction from where folding must begin. SILInstruction *beginInstruction; - /// Instructions that mark the end points of folding. No folded SIL value must - /// be usable beyond these instructions (in the control-flow order). These - /// instructions are also used to emit destory instructions for non-trivial, - /// SIL values emitted during folding. + /// Instructions that mark the end points of constant evaluation. SmallSetVector endInstructions; private: @@ -221,48 +227,47 @@ static bool isStdlibIntegerOrBoolDecl(NominalTypeDecl *numberDecl, numberDecl == astCtx.getBoolDecl()); } -/// Return true if and only if the given SIL type represents a String or -/// a Stdlib or builtin integer type. -static bool isIntegerOrStringType(SILType silType, ASTContext &astContext) { +/// Return true if and only if the given SIL type represents a Stdlib or builtin +/// integer type or a Bool type. +static bool isIntegerOrBoolType(SILType silType, ASTContext &astContext) { if (silType.is()) { return true; } + NominalTypeDecl *nominalDecl = silType.getNominalOrBoundGenericNominal(); + return nominalDecl && isStdlibIntegerOrBoolDecl(nominalDecl, astContext); +} +/// Return true if and only if the given SIL type represents a String type. +static bool isStringType(SILType silType, ASTContext &astContext) { NominalTypeDecl *nominalDecl = silType.getNominalOrBoundGenericNominal(); - if (!nominalDecl) { - return false; - } + return nominalDecl && nominalDecl == astContext.getStringDecl(); +} - return (nominalDecl == astContext.getStringDecl()) || - isStdlibIntegerOrBoolDecl(nominalDecl, astContext); +/// Return true if and only if the given SIL type represents an Array type. +static bool isArrayType(SILType silType, ASTContext &astContext) { + NominalTypeDecl *nominalDecl = silType.getNominalOrBoundGenericNominal(); + return nominalDecl && nominalDecl == astContext.getArrayDecl(); } /// Decide if the given instruction (which could possibly be a call) should /// be constant evaluated. /// /// \returns true iff the given instruction is not a call or if it is, it calls -/// a known string operation, such as concat/append etc., or calls an os log -/// overlay function annotated with a semantics attribute. +/// a known constant-evaluable function such as string append etc., or calls +/// a function annotate as "constant_evaluable". static bool shouldAttemptEvaluation(SILInstruction *inst) { auto *apply = dyn_cast(inst); if (!apply) return true; - SILFunction *calleeFun = apply->getCalleeFunction(); if (!calleeFun) return false; - - return calleeFun->hasSemanticsAttrThatStartsWith("string.") || - calleeFun->hasSemanticsAttr("constant_evaluable"); + return isConstantEvaluable(calleeFun); } /// Skip or evaluate the given instruction based on the evaluation policy and /// handle errors. The policy is to evaluate all non-apply instructions as well -/// as apply instructions that either invoke a known string operation or an os -/// log specific function that constructs compile-time constants -/// (like format string). Every other function call is skipped. -/// This includes calls that manipulate runtime values such as the arguments -/// (i.e, interpolated expressions) or the raw byte buffer. +/// as apply instructions that are marked as "constant_evaluable". static std::pair, Optional> evaluateOrSkip(ConstExprStepEvaluator &stepEval, SILBasicBlock::iterator instI) { @@ -276,26 +281,154 @@ evaluateOrSkip(ConstExprStepEvaluator &stepEval, return stepEval.skipByMakingEffectsNonConstant(instI); } -/// Check whether a single-valued instruction is foldable. String or integer -/// valued instructions are foldable with the exceptions: -/// - Addresses-valued instructions cannot be folded. -/// - Literal instruction need not be folded. -/// - "String.makeUTF8" instrinsic initializer need not be folded as it is -/// used only on string literals. -/// - StructInst cannot be folded. We can only fold its arguments and not the -/// instruction itself. +/// Return true iff the given value is a stdlib Int or Bool and it not a direct +/// construction of Int or Bool. +static bool isFoldableIntOrBool(SILValue value, ASTContext &astContext) { + return isIntegerOrBoolType(value->getType(), astContext) && + !isa(value); +} + +/// Return true iff the given value is a string and is not an initialization +/// of an string from a string literal. +static bool isFoldableString(SILValue value, ASTContext &astContext) { + return isStringType(value->getType(), astContext) && + (!isa(value) || + !getStringMakeUTF8Init(cast(value))); +} + +/// Return true iff the given value is an array and is not an initialization +/// of an array from an array literal. +static bool isFoldableArray(SILValue value, ASTContext &astContext) { + if (!isArrayType(value->getType(), astContext)) + return false; + // If value is an initialization of an array from a literal or an empty array + // initializer, it need not be folded. Arrays constructed from literals use a + // function with semantics: "array.uninitialized_intrinsic" that returns + // a pair, where the first element of the pair is the array. + SILInstruction *definingInst = value->getDefiningInstruction(); + if (!definingInst) + return true; + SILInstruction *constructorInst = definingInst; + if (isa(definingInst) || + isa(definingInst)) { + constructorInst = definingInst->getOperand(0)->getDefiningInstruction(); + } + if (!constructorInst || !isa(constructorInst)) + return true; + SILFunction *callee = cast(constructorInst)->getCalleeFunction(); + return !callee || + (!callee->hasSemanticsAttr("array.init.empty") && + !callee->hasSemanticsAttr("array.uninitialized_intrinsic")); +} + +/// Return true iff the given value is a closure but is not a creation of a +/// closure e.g., through partial_apply or thin_to_thick_function or +/// convert_function. +static bool isFoldableClosure(SILValue value) { + return value->getType().is() && + (!isa(value) && !isa(value) && + !isa(value) && + !isa(value)); +} + +/// Check whether a SILValue is foldable. String, integer, array and +/// function values are foldable with the following exceptions: +/// - Addresses cannot be folded. +/// - Literals need not be folded. +/// - Results of ownership instructions like load_borrow/copy_value need not +/// be folded +/// - Constructors such as \c struct Int or \c string.init() need not be folded. static bool isSILValueFoldable(SILValue value) { SILInstruction *definingInst = value->getDefiningInstruction(); if (!definingInst) return false; - ASTContext &astContext = definingInst->getFunction()->getASTContext(); SILType silType = value->getType(); - return (!silType.isAddress() && !isa(definingInst) && - !isa(definingInst) && - !getStringMakeUTF8Init(definingInst) && - isIntegerOrStringType(silType, astContext)); + !isa(definingInst) && + !isa(definingInst) && + !isa(definingInst) && + (isFoldableIntOrBool(value, astContext) || + isFoldableString(value, astContext) || + isFoldableArray(value, astContext) || isFoldableClosure(value))); +} + +/// Diagnose failure during evaluation of a call to a constant-evaluable +/// function. Note that all auto-generated 'appendInterpolation' calls are +/// constant evaluable. This function detects and specially handles such +/// functions to present better diagnostic messages. +static void diagnoseErrorInConstantEvaluableFunction(ApplyInst *call, + SymbolicValue errorInfo) { + SILNode *unknownNode = errorInfo.getUnknownNode(); + UnknownReason unknownReason = errorInfo.getUnknownReason(); + + SILFunction *callee = call->getCalleeFunction(); + assert(callee); + SILLocation loc = call->getLoc(); + SourceLoc sourceLoc = loc.getSourceLoc(); + ASTContext &astContext = callee->getASTContext(); + + std::string demangledCalleeName = Demangle::demangleSymbolAsString( + callee->getName(), + Demangle::DemangleOptions::SimplifiedUIDemangleOptions()); + + // If an 'appendInterpolation' evaluation failed, it is probably due to + // invalid privacy or format specifiers. These are the only possible errors + // that the users of the log API could make. The rest are for library authors + // or users who extend the log APIs. + if (unknownReason.getKind() == UnknownReason::CallArgumentUnknown && + dyn_cast(unknownNode) == call) { + if (StringRef(demangledCalleeName) + .contains(astContext.Id_appendInterpolation.str())) { + // TODO: extract and report the label of the parameter that is not a + // constant. + diagnose(astContext, sourceLoc, + diag::oslog_non_const_interpolation_options); + return; + } + } + diagnose(astContext, sourceLoc, diag::oslog_const_evaluable_fun_error, + demangledCalleeName); + errorInfo.emitUnknownDiagnosticNotes(loc); + return; +} + +/// Detect and emit diagnostics for errors found during evaluation. Errors +/// can happen due to incorrect implementation of the os log API in the +/// overlay or due to incorrect use of the os log API. +/// TODO: errors due to incorrect use of the API should be diagnosed by a +/// dedicated diagnostics pass that will happen before this optimization starts. +static bool detectAndDiagnoseErrors(SymbolicValue errorInfo, + SILInstruction *unevaluableInst) { + SILFunction *parentFun = unevaluableInst->getFunction(); + ASTContext &astContext = parentFun->getASTContext(); + + // If evaluation of any other constant_evaluable function call fails, point + // to that failed function along with a reason: such as that a parameter is + // non-constant parameter or that body is not constant evaluable. + ApplyInst *call = dyn_cast(unevaluableInst); + if (call) { + SILFunction *callee = call->getCalleeFunction(); + if (callee && isConstantEvaluable(callee)) { + diagnoseErrorInConstantEvaluableFunction(call, errorInfo); + return true; // abort evaluation. + } + } + + // Every other error must happen in the body of the os_log function which + // is inlined in the 'parentFun' before this pass. In this case, if we have a + // fail-stop error, point to the error and abort evaluation. Otherwise, just + // ignore the error and continue evaluation as this error might not affect the + // constant value of the OSLogMessage instance. + if (isFailStopError(errorInfo)) { + assert(errorInfo.getKind() == SymbolicValue::Unknown); + SILLocation loc = unevaluableInst->getLoc(); + SourceLoc sourceLoc = loc.getSourceLoc(); + diagnose(astContext, sourceLoc, diag::oslog_fail_stop_error); + errorInfo.emitUnknownDiagnosticNotes(loc); + return true; + } + return false; } /// Given a 'foldState', constant evaluate instructions from @@ -303,8 +436,7 @@ static bool isSILValueFoldable(SILValue value) { /// 'foldState.endInstructions' is seen. Add foldable, constant-valued /// instructions discovered during the evaluation to /// 'foldState.constantSILValues'. -/// \returns error information for emitting diagnostics if the evaluation -/// failed. +/// \returns error information if the evaluation failed. static Optional collectConstants(FoldState &foldState) { ConstExprStepEvaluator &constantEvaluator = foldState.constantEvaluator; @@ -315,7 +447,6 @@ static Optional collectConstants(FoldState &foldState) { // endInstructions. while (true) { SILInstruction *currInst = &(*currI); - if (endInstructions.count(currInst)) break; @@ -326,9 +457,22 @@ static Optional collectConstants(FoldState &foldState) { Optional nextI = None; std::tie(nextI, errorInfo) = evaluateOrSkip(constantEvaluator, currI); - if (!nextI) { + + // If the evaluation of this instruction failed, check whether it should be + // diagnosed and reported. If so, abort evaluation. Otherwise, continue + // evaluation if possible as this error could be due to an instruction that + // doesn't affect the OSLogMessage value. + if (errorInfo && detectAndDiagnoseErrors(errorInfo.getValue(), currInst)) { return errorInfo; } + + if (!nextI) { + // We cannnot find the next instruction to continue evaluation, and we + // haven't seen any reportable errors during evaluation. Therefore, + // consider this the end point of evaluation. + return None; // No error. + } + // Set the next instruction to continue evaluation from. currI = nextI.getValue(); @@ -348,6 +492,133 @@ static Optional collectConstants(FoldState &foldState) { return None; // No error. } +/// Generate SIL code to create an array of constant size from the given +/// SILValues \p elements. This function creates the same sequence of SIL +/// instructions that would be generated for initializing an array from an array +/// literal of the form [element1, element2, ..., elementn]. +/// +/// \param elements SILValues that the array should contain +/// \param arrayType the type of the array that must be created. +/// \param builder SILBuilder that provides the context for emitting the code +/// for the array. +/// \param loc SILLocation to use in the emitted instructions. +/// \return the SILValue of the array that is created with the given \c +/// elements. +static SILValue emitCodeForConstantArray(ArrayRef elements, + CanType arrayType, SILBuilder &builder, + SILLocation loc) { + ASTContext &astContext = builder.getASTContext(); + assert(astContext.getArrayDecl() == + arrayType->getNominalOrBoundGenericNominal()); + SILModule &module = builder.getModule(); + + // Create a SILValue for the number of elements. + unsigned numElements = elements.size(); + SILValue numElementsSIL = builder.createIntegerLiteral( + loc, SILType::getBuiltinWordType(astContext), numElements); + + // Find the SILFunction that corresponds to _allocateUninitializedArray. + FuncDecl *arrayAllocateDecl = astContext.getAllocateUninitializedArray(); + assert(arrayAllocateDecl); + std::string allocatorMangledName = + SILDeclRef(arrayAllocateDecl, SILDeclRef::Kind::Func).mangle(); + SILFunction *arrayAllocateFun = + module.findFunction(allocatorMangledName, SILLinkage::PublicExternal); + assert(arrayAllocateFun); + + // Call the _allocateUninitializedArray function with numElementsSIL. The + // call returns a two-element tuple, where the first element is the newly + // created array and the second element is a pointer to the internal storage + // of the array. + SubstitutionMap subMap = arrayType->getContextSubstitutionMap( + module.getSwiftModule(), astContext.getArrayDecl()); + FunctionRefInst *arrayAllocateRef = + builder.createFunctionRef(loc, arrayAllocateFun); + ApplyInst *applyInst = builder.createApply( + loc, arrayAllocateRef, subMap, ArrayRef(numElementsSIL), false); + + // Extract the elements of the tuple returned by the call to the allocator. + DestructureTupleInst *destructureInst = + builder.createDestructureTuple(loc, applyInst); + SILValue arraySIL = destructureInst->getResults()[0]; + SILValue storagePointerSIL = destructureInst->getResults()[1]; + + if (elements.empty()) { + // Nothing more to be done if we are creating an empty array. + return arraySIL; + } + + // Convert the pointer to the storage to an address. The elements will be + // stored into offsets from this address. + SILType elementSILType = elements[0]->getType(); + PointerToAddressInst *storageAddr = builder.createPointerToAddress( + loc, storagePointerSIL, elementSILType.getAddressType(), + /*isStrict*/ true, + /*isInvariant*/ false); + + // Iterate over the elements and store them into the storage address + // after offsetting it appropriately. + + // Create a TypeLowering for emitting stores. Note that TypeLowering + // provides a utility for emitting stores for storing trivial and + // non-trivial values, and also handles OSSA and non-OSSA. + const TypeLowering &elementTypeLowering = + builder.getTypeLowering(elementSILType); + + unsigned elementIndex = 0; + for (SILValue elementSIL : elements) { + // Compute the address where the element must be stored. + SILValue currentStorageAddr; + if (elementIndex != 0) { + SILValue indexSIL = builder.createIntegerLiteral( + loc, SILType::getBuiltinWordType(astContext), elementIndex); + currentStorageAddr = builder.createIndexAddr(loc, storageAddr, indexSIL); + } else { + currentStorageAddr = storageAddr; + } + // Store the generated element into the currentStorageAddr. This is an + // initializing store and therefore there is no need to free any existing + // element. + elementTypeLowering.emitStore(builder, loc, elementSIL, currentStorageAddr, + StoreOwnershipQualifier::Init); + elementIndex++; + } + return arraySIL; +} + +/// Given a SILValue \p value, return the instruction immediately following the +/// definition of the value. That is, if the value is defined by an +/// instruction, return the instruction following the definition. Otherwise, if +/// the value is a basic block parameter, return the first instruction of the +/// basic block. +SILInstruction *getInstructionFollowingValueDefinition(SILValue value) { + SILInstruction *definingInst = value->getDefiningInstruction(); + if (definingInst) { + return &*std::next(definingInst->getIterator()); + } + // Here value must be a basic block argument. + SILBasicBlock *bb = value->getParentBlock(); + return &*bb->begin(); +} + +/// Given a SILValue \p value, create a copy of the value using copy_value in +/// OSSA or retain in non-OSSA, if \p value is a non-trivial type. Otherwise, if +/// \p value is a trivial type, return the value itself. +SILValue makeOwnedCopyOfSILValue(SILValue value, SILFunction &fun) { + SILType type = value->getType(); + if (type.isTrivial(fun)) + return value; + assert(!type.isAddress() && "cannot make owned copy of addresses"); + + SILInstruction *instAfterValueDefinition = + getInstructionFollowingValueDefinition(value); + SILLocation copyLoc = instAfterValueDefinition->getLoc(); + SILBuilderWithScope builder(instAfterValueDefinition); + const TypeLowering &typeLowering = builder.getTypeLowering(type); + SILValue copy = typeLowering.emitCopyValue(builder, copyLoc, value); + return copy; +} + /// Generate SIL code that computes the constant given by the symbolic value /// `symVal`. Note that strings and struct-typed constant values will require /// multiple instructions to be emitted. @@ -362,15 +633,15 @@ static Optional collectConstants(FoldState &foldState) { /// \param stringInfo String.init and metatype information for generating code /// for string literals. static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, - SILType &expectedType, - SILBuilder &builder, SILLocation &loc, + Type expectedType, SILBuilder &builder, + SILLocation &loc, StringSILInfo &stringInfo) { - ASTContext &astContext = expectedType.getASTContext(); + ASTContext &astContext = expectedType->getASTContext(); switch (symVal.getKind()) { case SymbolicValue::String: { assert(astContext.getStringDecl() == - expectedType.getNominalOrBoundGenericNominal()); + expectedType->getNominalOrBoundGenericNominal()); StringRef stringVal = symVal.getStringValue(); StringLiteralInst *stringLitInst = builder.createStringLiteral( @@ -400,121 +671,285 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, } case SymbolicValue::Integer: { // Builtin integer types. APInt resInt = symVal.getIntegerValue(); - assert(expectedType.is()); + assert(expectedType->is()); + SILType builtinIntType = + SILType::getPrimitiveObjectType(expectedType->getCanonicalType()); IntegerLiteralInst *intLiteralInst = - builder.createIntegerLiteral(loc, expectedType, resInt); + builder.createIntegerLiteral(loc, builtinIntType, resInt); return intLiteralInst; } case SymbolicValue::Aggregate: { // Support only stdlib integer or bool structs. - StructDecl *structDecl = expectedType.getStructOrBoundGenericStruct(); + StructDecl *structDecl = expectedType->getStructOrBoundGenericStruct(); assert(structDecl); assert(isStdlibIntegerOrBoolDecl(structDecl, astContext)); + assert(symVal.getAggregateType()->isEqual(expectedType) && + "aggregate symbolic value's type and expected type do not match"); VarDecl *propertyDecl = structDecl->getStoredProperties().front(); - SILType propertyType = - expectedType.getFieldType(propertyDecl, builder.getModule()); + Type propertyType = expectedType->getTypeOfMember( + propertyDecl->getModuleContext(), propertyDecl); SymbolicValue propertyVal = symVal.lookThroughSingleElementAggregates(); SILValue newPropertySIL = emitCodeForSymbolicValue( propertyVal, propertyType, builder, loc, stringInfo); + // The lowered SIL type of an integer/bool type is just the primitive + // object type containing the Swift type. + SILType aggregateType = + SILType::getPrimitiveObjectType(expectedType->getCanonicalType()); StructInst *newStructInst = builder.createStruct( - loc, expectedType, ArrayRef(newPropertySIL)); + loc, aggregateType, ArrayRef(newPropertySIL)); return newStructInst; } + case SymbolicValue::Array: { + assert(expectedType->isEqual(symVal.getArrayType())); + CanType elementType; + ArrayRef arrayElements = + symVal.getStorageOfArray().getStoredElements(elementType); + + // Emit code for the symbolic values corresponding to the array elements. + SmallVector elementSILValues; + for (SymbolicValue elementSymVal : arrayElements) { + SILValue elementSIL = emitCodeForSymbolicValue(elementSymVal, elementType, + builder, loc, stringInfo); + elementSILValues.push_back(elementSIL); + } + SILValue arraySIL = emitCodeForConstantArray( + elementSILValues, expectedType->getCanonicalType(), builder, loc); + return arraySIL; + } + case SymbolicValue::Closure: { + assert(expectedType->is() || + expectedType->is()); + + SymbolicClosure *closure = symVal.getClosure(); + SubstitutionMap callSubstMap = closure->getCallSubstitutionMap(); + SILModule &module = builder.getModule(); + ArrayRef captures = closure->getCaptures(); + + // Recursively emit code for all captured values that are mapped to a + // symbolic value. If there is a captured value that is not mapped + // to a symbolic value, use the captured value as such (after possibly + // copying non-trivial captures). + SmallVector capturedSILVals; + for (SymbolicClosureArgument capture : captures) { + SILValue captureOperand = capture.first; + Optional captureSymVal = capture.second; + if (!captureSymVal) { + SILFunction &fun = builder.getFunction(); + assert(captureOperand->getFunction() == &fun && + "non-constant captured arugment not defined in this function"); + // If the captureOperand is a non-trivial value, it should be copied + // as it now used in a new folded closure. + SILValue captureCopy = makeOwnedCopyOfSILValue(captureOperand, fun); + capturedSILVals.push_back(captureCopy); + continue; + } + // Here, we have a symbolic value for the capture. Therefore, use it to + // create a new constant at this point. Note that the captured operand + // type may have generic parameters which has to be substituted with the + // substitution map that was inferred by the constant evaluator at the + // partial-apply site. + SILType operandType = captureOperand->getType(); + SILType captureType = operandType.subst(module, callSubstMap); + SILValue captureSILVal = emitCodeForSymbolicValue( + captureSymVal.getValue(), captureType.getASTType(), builder, loc, + stringInfo); + capturedSILVals.push_back(captureSILVal); + } + + FunctionRefInst *functionRef = + builder.createFunctionRef(loc, closure->getTarget()); + SILType closureType = closure->getClosureType(); + ParameterConvention convention = + closureType.getAs()->getCalleeConvention(); + PartialApplyInst *papply = builder.createPartialApply( + loc, functionRef, callSubstMap, capturedSILVals, convention); + // The type of the created closure must be a lowering of the expected type. + SILType resultType = papply->getType(); + CanType expectedCanType = expectedType->getCanonicalType(); + assert(expectedType->is() + ? resultType.getASTType() == expectedCanType + : resultType.is()); + return papply; + } default: { llvm_unreachable("Symbolic value kind is not supported"); } } } -/// Collect the end-of-lifetime instructions of the given SILValue. These are -/// either release_value or destroy_value instructions. -/// \param value SIL value whose end-of-lifetime instructions must be collected. -/// \param lifetimeEndInsts buffer for storing the found end-of-lifetime -/// instructions of 'value'. -static void getLifetimeEndInstructionsOfSILValue( - SILValue value, SmallVectorImpl &lifetimeEndInsts) { - - bool continueLifetimeEndInstructionSearch = true; - SILValue currValue = value; - - while (continueLifetimeEndInstructionSearch) { - continueLifetimeEndInstructionSearch = false; - - for (Operand *use : currValue->getUses()) { +/// Given a SILValue \p value, compute the set of transitive users of the value +/// (excluding value itself) by following the use-def chain starting at value. +/// Note that this function does not follow use-def chains though branches. +static void getTransitiveUsers(SILValue value, + SmallVectorImpl &users) { + // Collect the instructions that are data dependent on the value using a + // fix point iteration. + SmallPtrSet visitedUsers; + SmallVector worklist; + worklist.push_back(value); + + while (!worklist.empty()) { + SILValue currVal = worklist.pop_back_val(); + for (Operand *use : currVal->getUses()) { SILInstruction *user = use->getUser(); - - if (isa(user) || isa(user)) { - lifetimeEndInsts.push_back(user); + if (visitedUsers.count(user)) continue; - } - - if (isa(user)) { - auto *copyValueInst = cast(user); - // Continue looking for the end-of-lifetime instruction for the - // result of copy_value. - currValue = copyValueInst; - continueLifetimeEndInstructionSearch = true; - } + visitedUsers.insert(user); + llvm::copy(user->getResults(), std::back_inserter(worklist)); } } + // At this point, visitedUsers have all the transitive, data-dependent uses. + users.append(visitedUsers.begin(), visitedUsers.end()); } -/// Emit instructions to destroy the folded value at the end of its use, if -/// required. Since this pass folds only integers or strings and since the -/// former is a trivial type, we only have to destroy strings that are folded. -/// For strings, a release_value (or a destory_value instruction in ownership -/// SIL) has to be emitted if it is not already present. +/// Collect the end points of the instructions that are data dependent on \c +/// value. A instruction is data dependent on \c value if its result may +/// transitively depends on \c value. Note that data dependencies through +/// addresses are not tracked by this function. +/// +/// \param value SILValue that is not an address. +/// \param fun SILFunction that defines \c value. +/// \param endUsers buffer for storing the found end points of the data +/// dependence chain. static void -destroyFoldedValueAtEndOfUse(SILValue foldedVal, SILValue originalVal, - ArrayRef endOfUseInsts, - SILFunction *fun) { - // Folded value should have either trivial or owned ownership as it is an - // integer or string constant. - assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::Any || - foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned); - - // If the ownership kinds of folded and original values are both either - // owned or trivial, there is nothing to do. - if (foldedVal.getOwnershipKind() == originalVal.getOwnershipKind()) { +getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, + SmallVectorImpl &endUsers) { + assert(!value->getType().isAddress()); + + SmallVector transitiveUsers; + // Get transitive users of value, ignoring use-def chain going through + // branches. These transitive users define the end points of the constant + // evaluation. Igoring use-def chains through branches causes constant + // evaluation to miss some constant folding opportunities. This can be + // relaxed in the future, if necessary. + getTransitiveUsers(value, transitiveUsers); + + // Compute the lifetime frontier of all the transitive uses which are the + // instructions following the last uses. Every exit from the last uses will + // have a lifetime frontier. + SILInstruction *valueDefinition = value->getDefiningInstruction(); + SILInstruction *def = + valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); + ValueLifetimeAnalysis lifetimeAnalysis = + ValueLifetimeAnalysis(def, transitiveUsers); + ValueLifetimeAnalysis::Frontier frontier; + bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( + frontier, ValueLifetimeAnalysis::DontModifyCFG); + endUsers.append(frontier.begin(), frontier.end()); + if (!hasCriticlEdges) return; + // If there are some lifetime frontiers on the critical edges, take the + // first instruction of the target of the critical edge as the frontier. This + // will suffice as every exit from the visitedUsers must go through one of + // them. + for (auto edgeIndexPair : lifetimeAnalysis.getCriticalEdges()) { + SILBasicBlock *targetBB = + edgeIndexPair.first->getSuccessors()[edgeIndexPair.second]; + endUsers.push_back(&targetBB->front()); } - assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed); +} - // Here, the original value may be at +0 and hence may not be released. - // However, the folded value should always be released. - SmallVector lifeTimeEndInstsOfOriginal; - getLifetimeEndInstructionsOfSILValue(originalVal, lifeTimeEndInstsOfOriginal); +/// Given a guaranteed SILValue \p value, return a borrow-scope introducing +/// value, if there is exactly one such introducing value. Otherwise, return +/// None. There can be multiple borrow scopes for a SILValue iff it is derived +/// from a guaranteed basic block parameter representing a phi node. +static Optional +getUniqueBorrowScopeIntroducingValue(SILValue value) { + assert(value.getOwnershipKind() == ValueOwnershipKind::Guaranteed && + "parameter must be a guarenteed value"); + SmallVector borrowIntroducers; + getUnderlyingBorrowIntroducingValues(value, borrowIntroducers); + assert(borrowIntroducers.size() > 0 && + "folding guaranteed value with no borrow introducer"); + if (borrowIntroducers.size() > 1) + return None; + return borrowIntroducers[0]; +} - if (!lifeTimeEndInstsOfOriginal.empty()) { - // Here, the original value is released, and so would be the folded value. +/// Replace all uses of \c originalVal by \c foldedVal and adjust lifetimes of +/// original and folded values by emitting required destory/release instructions +/// at the right places. Note that this function does not remove any +/// instruction. +/// +/// \param originalVal the SIL value that is replaced. +/// \param foldedVal the SIL value that replaces the \c originalVal. +/// \param fun the SIL function containing the \c foldedVal and \c originalVal +static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, + SILValue originalVal, + SILFunction *fun) { + SILInstruction *originalInst = originalVal->getDefiningInstruction(); + SILInstruction *foldedInst = foldedVal->getDefiningInstruction(); + assert(originalInst && + "cannot constant fold function or basic block parameter"); + assert(!isa(originalInst) && + "cannot constant fold a terminator instruction"); + assert(foldedInst && "constant value does not have a defining instruction"); + + if (originalVal->getType().isTrivial(*fun)) { + assert(foldedVal->getType().isTrivial(*fun)); + // Just replace originalVal by foldedVal. + originalVal->replaceAllUsesWith(foldedVal); + return; + } + assert(!foldedVal->getType().isTrivial(*fun)); + assert(fun->hasOwnership()); + assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned && + "constant value must have owned ownership kind"); + + if (originalVal.getOwnershipKind() == ValueOwnershipKind::Owned) { + originalVal->replaceAllUsesWith(foldedVal); + // Destroy originalVal, which is now unused, immediately after its + // definition. Note that originalVal's destorys are now transferred to + // foldedVal. + SILInstruction *insertionPoint = &(*std::next(originalInst->getIterator())); + SILBuilderWithScope builder(insertionPoint); + SILLocation loc = insertionPoint->getLoc(); + builder.emitDestroyValueOperation(loc, originalVal); return; } - // Here, the original value is not released. Release the folded value at the - // 'endOfUse' instructions passed as parameter. - bool hasOwnership = fun->hasOwnership(); - for (SILInstruction *endInst : endOfUseInsts) { - SILBuilderWithScope builder(endInst); - if (hasOwnership) { - builder.createDestroyValue(endInst->getLoc(), foldedVal); - } else { - builder.createReleaseValue(endInst->getLoc(), foldedVal, - builder.getDefaultAtomicity()); - } + // Here, originalVal is guaranteed. It must belong to a borrow scope that + // begins at a scope introducing instruction e.g. begin_borrow or load_borrow. + // The foldedVal should also have been inserted at the beginning of the scope. + // Therefore, create a borrow of foldedVal at the beginning of the scope and + // use the borrow in place of the originalVal. Also, end the borrow and + // destroy foldedVal at the end of the borrow scope. + assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed); + + Optional originalScopeBegin = + getUniqueBorrowScopeIntroducingValue(originalVal); + assert(originalScopeBegin && + "value without a unique borrow scope should not have been folded"); + SILInstruction *scopeBeginInst = + originalScopeBegin->value->getDefiningInstruction(); + assert(scopeBeginInst); + + SILBuilderWithScope builder(scopeBeginInst); + SILValue borrow = + builder.emitBeginBorrowOperation(scopeBeginInst->getLoc(), foldedVal); + + originalVal->replaceAllUsesWith(borrow); + + SmallVector scopeEndingInsts; + originalScopeBegin->getLocalScopeEndingInstructions(scopeEndingInsts); + + for (SILInstruction *scopeEndingInst : scopeEndingInsts) { + SILBuilderWithScope builder(scopeEndingInst); + builder.emitEndBorrowOperation(scopeEndingInst->getLoc(), borrow); + builder.emitDestroyValueOperation(scopeEndingInst->getLoc(), foldedVal); } + return; } /// Given a fold state with constant-valued instructions, substitute the /// instructions with the constant values. The constant values could be strings /// or Stdlib integer-struct values or builtin integers. static void substituteConstants(FoldState &foldState) { - ConstExprStepEvaluator &evaluator = foldState.constantEvaluator; - SmallVector deletedInsts; - auto endOfUseInsts = ArrayRef( - foldState.endInstructions.begin(), foldState.endInstructions.end()); + // Instructions that are possibly dead since their results are folded. + SmallVector possiblyDeadInsts; for (SILValue constantSILValue : foldState.getConstantSILValues()) { SymbolicValue constantSymbolicVal = @@ -522,57 +957,58 @@ static void substituteConstants(FoldState &foldState) { SILInstruction *definingInst = constantSILValue->getDefiningInstruction(); assert(definingInst); + SILFunction *fun = definingInst->getFunction(); + + // Find an insertion point for inserting the new constant value. If we are + // folding a value like struct_extract within a borrow scope, we need to + // insert the constant value at the beginning of the borrow scope. This + // is because the borrowed value is expected to be alive during its entire + // borrow scope and could be stored into memory and accessed indirectly + // without a copy e.g. using store_borrow within the borrow scope. On the + // other hand, if we are folding an owned value, we can insert the constant + // value at the point where the owned value is defined. + SILInstruction *insertionPoint = definingInst; + if (constantSILValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + Optional borrowIntroducer = + getUniqueBorrowScopeIntroducingValue(constantSILValue); + if (!borrowIntroducer) { + // This case happens only if constantSILValue is derived from a + // guaranteed basic block parameter. This is unlikley because the values + // that have to be folded should just be a struct-extract of an owned + // instance of OSLogMessage. + continue; + } + insertionPoint = borrowIntroducer->value->getDefiningInstruction(); + assert(insertionPoint && "borrow scope begnning is a parameter"); + } - SILBuilderWithScope builder(definingInst); - SILLocation loc = definingInst->getLoc(); - SILType instType = constantSILValue->getType(); + SILBuilderWithScope builder(insertionPoint); + SILLocation loc = insertionPoint->getLoc(); + CanType instType = constantSILValue->getType().getASTType(); SILValue foldedSILVal = emitCodeForSymbolicValue( constantSymbolicVal, instType, builder, loc, foldState.stringInfo); - // Add an instruction to end the lifetime of the foldedSILVal, if necessary. - destroyFoldedValueAtEndOfUse(foldedSILVal, constantSILValue, endOfUseInsts, - definingInst->getFunction()); - - constantSILValue->replaceAllUsesWith(foldedSILVal); - - if (isa(definingInst)) { - deletedInsts.push_back(definingInst); - } // Otherwise, be conservative and do not delete the instruction as other - // results of the instruction could be used. + // Replace constantSILValue with foldedSILVal and adjust the lifetime and + // ownership of the values appropriately. + replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun); + possiblyDeadInsts.push_back(definingInst); } - - recursivelyDeleteTriviallyDeadInstructions(deletedInsts, true, - [&](SILInstruction *DeadI) {}); } -/// Detect and emit diagnostics for errors found during evaluation. Errors -/// can happen due to incorrect implementation of the os log API in the -/// overlay or due to incorrect use of the os log API. -/// TODO: some of the checks here would be made redundant by a dedicated -/// diagnostics check that will happen before the optimization starts. -static bool detectAndDiagnoseErrors(Optional errorInfo, - SingleValueInstruction *osLogMessage, - FoldState &foldState) { +/// Check whether OSLogMessage and OSLogInterpolation instances and all their +/// stored properties are constants. If not, it indicates errors that are due to +/// incorrect implementation of OSLogMessage either in the overlay or in the +/// extensions created by users. Detect and emit diagnostics for such errors. +/// The diagnostics here are for os log library authors. +static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage, + FoldState &foldState) { ConstExprStepEvaluator &constantEvaluator = foldState.constantEvaluator; SILLocation loc = osLogMessage->getLoc(); SourceLoc sourceLoc = loc.getSourceLoc(); SILFunction *fn = osLogMessage->getFunction(); SILModule &module = fn->getModule(); ASTContext &astContext = fn->getASTContext(); - bool errorDetected = false; - // If we have errorInfo that indicates a fail-stop error, diagnose it. - if (errorInfo && constantEvaluator.isFailStopError(*errorInfo)) { - assert(errorInfo->getKind() == SymbolicValue::Unknown); - diagnose(astContext, sourceLoc, diag::oslog_const_evaluation_error); - errorInfo->emitUnknownDiagnosticNotes(loc); - errorDetected = true; - } - - // Check if the OSLogMessage and OSLogInterpolation instances are correctly - // inferred as constants. If not, it implies incorrect implementation - // of the os log API in the overlay. Diagnostics here are for os log - // library authors. Optional osLogMessageValueOpt = constantEvaluator.lookupConstValue(osLogMessage); if (!osLogMessageValueOpt || @@ -581,86 +1017,241 @@ static bool detectAndDiagnoseErrors(Optional errorInfo, return true; } + // The first (and only) property of OSLogMessage is the OSLogInterpolation + // instance. SymbolicValue osLogInterpolationValue = - osLogMessageValueOpt->lookThroughSingleElementAggregates(); + osLogMessageValueOpt->getAggregateMembers()[0]; if (!osLogInterpolationValue.isConstant()) { diagnose(astContext, sourceLoc, diag::oslog_non_constant_interpolation); return true; } - // Check if every proprety of the OSLogInterpolation instance that is a - // string or integer has a constant value. If this is violated this could - // be an indication of an error in the usage of the API. Diagnostics emitted - // here are for the users of the os log APIs. + // Check if every proprety of the OSLogInterpolation instance has a constant + // value. SILType osLogMessageType = osLogMessage->getType(); StructDecl *structDecl = osLogMessageType.getStructOrBoundGenericStruct(); assert(structDecl); + auto typeExpansionContext = + TypeExpansionContext(*osLogMessage->getFunction()); VarDecl *interpolationPropDecl = structDecl->getStoredProperties().front(); - SILType osLogInterpolationType = - osLogMessageType.getFieldType(interpolationPropDecl, module); + SILType osLogInterpolationType = osLogMessageType.getFieldType( + interpolationPropDecl, module, typeExpansionContext); StructDecl *interpolationStruct = osLogInterpolationType.getStructOrBoundGenericStruct(); assert(interpolationStruct); auto propertyDecls = interpolationStruct->getStoredProperties(); ArrayRef propertyValues = - osLogInterpolationValue.getAggregateValue(); + osLogInterpolationValue.getAggregateMembers(); auto propValueI = propertyValues.begin(); + bool errorDetected = false; for (auto *propDecl : propertyDecls) { SymbolicValue propertyValue = *(propValueI++); - if (propertyValue.isConstant()) { + if (!propertyValue.isConstant()) { + diagnose(astContext, sourceLoc, diag::oslog_property_not_constant, + propDecl->getNameStr()); + errorDetected = true; + break; + } + } + return errorDetected; +} + +using CallbackTy = llvm::function_ref; + +/// Return true iff the given address-valued instruction has only stores into +/// it. This function tests for the conditions under which a call, that was +/// constant evaluated, that writes into the address-valued instruction can be +/// considered as a point store and exploits it to remove such uses. +/// TODO: eventually some of this logic can be moved to +/// PredictableDeadAllocElimination pass, but the assumption about constant +/// evaluable functions taking inout parameters is not easily generalizable to +/// arbitrary non-constant contexts where the function could be used. The logic +/// here is relying on the fact that the constant_evaluable function has been +/// evaluated and therefore doesn't have any side-effects. +static bool hasOnlyStoreUses(SingleValueInstruction *addressInst) { + for (Operand *use : addressInst->getUses()) { + SILInstruction *user = use->getUser(); + switch (user->getKind()) { + default: + return false; + case SILInstructionKind::BeginAccessInst: { + if (!hasOnlyStoreUses(cast(user))) + return false; + continue; + } + case SILInstructionKind::StoreInst: { + // For now, ignore assigns as we need to destroy_addr its dest if it + // is deleted. + if (cast(user)->getOwnershipQualifier() == + StoreOwnershipQualifier::Assign) + return false; + continue; + } + case SILInstructionKind::EndAccessInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::DeallocStackInst: + continue; + case SILInstructionKind::ApplyInst: { + ApplyInst *apply = cast(user); + SILFunction *callee = apply->getCalleeFunction(); + if (!callee || !isConstantEvaluable(callee) || !apply->use_empty()) + return false; + // Note that since we are looking at an alloc_stack used to produce the + // OSLogMessage instance, this constant_evaluable call should have been + // evaluated successfully by the evaluator. Otherwise, we would have + // reported an error earlier. Therefore, all values manipulated by such + // a call are symbolic constants and the call would not have any global + // side effects. The following logic relies on this property. + // If there are other indirect writable results for the call other than + // the alloc_stack we are checking, it may not be dead. Therefore, bail + // out. + FullApplySite applySite(apply); + unsigned numWritableArguments = + getNumInOutArguments(applySite) + applySite.getNumIndirectSILResults(); + if (numWritableArguments > 1) + return false; + SILArgumentConvention convention = applySite.getArgumentConvention(*use); + if (convention == SILArgumentConvention::Indirect_In_Guaranteed || + convention == SILArgumentConvention::Indirect_In_Constant || + convention == SILArgumentConvention::Indirect_In_Guaranteed) { + if (numWritableArguments > 0) + return false; + } + // Here, either there are no writable parameters or the alloc_stack + // is the only writable parameter. continue; } + } + } + return true; +} - if (!isIntegerOrStringType( - osLogInterpolationType.getFieldType(propDecl, module), - astContext)) { +/// Delete the given alloc_stack instruction by deleting the users of the +/// instruction. In case the user is a begin_apply, recursively delete the users +/// of begin_apply. This will also fix the lifetimes of the deleted instructions +/// whenever possible. +static void forceDeleteAllocStack(SingleValueInstruction *inst, + InstructionDeleter &deleter, + CallbackTy callback) { + SmallVector users; + for (Operand *use : inst->getUses()) + users.push_back(use->getUser()); + + for (SILInstruction *user : users) { + if (isIncidentalUse(user)) + continue; + if (isa(user)) { + deleter.forceDelete(user, callback); continue; } + if (isa(user)) { + forceDeleteAllocStack(cast(user), deleter, callback); + continue; + } + deleter.forceDeleteAndFixLifetimes(user, callback); + } + deleter.forceDelete(inst, callback); +} - diagnose(astContext, sourceLoc, diag::oslog_property_not_constant, - propDecl->getNameStr()); - errorDetected = true; - break; +/// Delete \c inst , if it is dead, along with its dead users and invoke the +/// callback whever an instruction is deleted. +static void deleteInstructionWithUsersAndFixLifetimes( + SILInstruction *inst, InstructionDeleter &deleter, CallbackTy callback) { + // If this is an alloc_stack, it can be eliminated as long as it is only + // stored into or destroyed. + if (AllocStackInst *allocStack = dyn_cast(inst)) { + if (hasOnlyStoreUses(allocStack)) + forceDeleteAllocStack(allocStack, deleter, callback); + return; + } + deleter.recursivelyDeleteUsersIfDead(inst, callback); +} + +/// Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed +/// to the os log call and clean up its dependencies. If the instance cannot be +/// eliminated, it implies that either the instance is not auto-generated or the +/// implementation of the os log overlay is incorrect. Therefore emit +/// diagnostics in such cases. +static void tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) { + InstructionDeleter deleter; + // List of instructions that are possibly dead. + SmallVector worklist = {oslogMessage}; + // Set of all deleted instructions. + SmallPtrSet deletedInstructions; + unsigned startIndex = 0; + while (startIndex < worklist.size()) { + SILInstruction *inst = worklist[startIndex++]; + if (deletedInstructions.count(inst)) + continue; + deleteInstructionWithUsersAndFixLifetimes( + inst, deleter, [&](SILInstruction *deadInst) { + // Add operands of all deleted instructions to the worklist so that + // they can be recursively deleted if possible. + for (Operand &operand : deadInst->getAllOperands()) { + if (SILInstruction *definingInstruction = + operand.get()->getDefiningInstruction()) { + if (!deletedInstructions.count(definingInstruction)) + worklist.push_back(definingInstruction); + } + } + (void)deletedInstructions.insert(deadInst); + }); + } + deleter.cleanUpDeadInstructions(); + // If the OSLogMessage instance is not deleted, the overlay implementation + // (or its extensions by users) is incorrect. + if (!deletedInstructions.count(oslogMessage)) { + SILFunction *fun = oslogMessage->getFunction(); + diagnose(fun->getASTContext(), oslogMessage->getLoc().getSourceLoc(), + diag::oslog_message_alive_after_opts); } - return errorDetected; } /// Constant evaluate instructions starting from 'start' and fold the uses /// of the value 'oslogMessage'. Stop when oslogMessageValue is released. -static void constantFold(SILInstruction *start, +static bool constantFold(SILInstruction *start, SingleValueInstruction *oslogMessage, unsigned assertConfig) { + SILFunction *fun = start->getFunction(); + assert(fun->hasOwnership() && "function not in ownership SIL"); // Initialize fold state. - SmallVector lifetimeEndInsts; - getLifetimeEndInstructionsOfSILValue(oslogMessage, lifetimeEndInsts); + SmallVector endUsersOfOSLogMessage; + getEndPointsOfDataDependentChain(oslogMessage, fun, endUsersOfOSLogMessage); + assert(!endUsersOfOSLogMessage.empty()); - FoldState state(start->getFunction(), assertConfig, start, lifetimeEndInsts); + FoldState state(fun, assertConfig, start, endUsersOfOSLogMessage); auto errorInfo = collectConstants(state); + if (errorInfo) // Evaluation failed with diagnostics. + return false; - // At this point, the `OSLogMessage` instance should be mapped to a symbolic - // value in the interpreter state. Furthermore, its format string and - // interger-valued fields (other than `OSLogArguments`) must be constants. - // If this is not the case, it means the formatting options or privacy - // qualifiers provided by the user were not inferred as compile-time - // constants. Detect and diagnose this scenario. - bool errorDetected = detectAndDiagnoseErrors(errorInfo, oslogMessage, state); + // At this point, the `OSLogMessage` instance should be mapped to a constant + // value in the interpreter state. If this is not the case, it means the + // overlay implementation of OSLogMessage (or its extensions by users) are + // incorrect. Detect and diagnose this scenario. + bool errorDetected = checkOSLogMessageIsConstant(oslogMessage, state); if (errorDetected) - return; + return false; substituteConstants(state); + + tryEliminateOSLogMessage(oslogMessage); + return true; } /// Given a call to the initializer of OSLogMessage, which conforms to -/// 'ExpressibleByStringInterpolation', find the first instruction, if any, -/// that marks the begining of the string interpolation that is used to -/// create an OSLogMessage instance. Normally, this instruction is the -/// alloc_stack of the string interpolation type: 'OSLogInterpolation'. -/// Constant evaluation and folding must begin from this instruction. +/// 'ExpressibleByStringInterpolation', find the first instruction, if any, that +/// marks the begining of the string interpolation that is used to create an +/// OSLogMessage instance. This function traverses the backward data-dependence +/// chain of the given OSLogMessage initializer: \p oslogInit. As a special case +/// it avoids chasing the data-dependencies from the captured values of +/// partial-apply instructions, as a partial apply instruction is considered as +/// a constant regardless of the constantness of its captures. static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { auto oslogInitCallSite = FullApplySite(oslogInit); SILFunction *callee = oslogInitCallSite.getCalleeFunction(); @@ -685,7 +1276,14 @@ static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { // Partial applies are used to capture the dynamic arguments passed to // the string interpolation. Their arguments are not required to be // known at compile time and they need not be constant evaluated. - // Therefore, do not follow this dependency chain. + // Therefore, follow only the dependency chain along function ref operand. + SILInstruction *definingInstruction = + inst->getOperand(0)->getDefiningInstruction(); + assert(definingInstruction && "no function-ref operand in partial-apply"); + if (seenInstructions.insert(definingInstruction).second) { + worklist.push_back(definingInstruction); + candidateStartInstructions.insert(definingInstruction); + } continue; } @@ -716,9 +1314,10 @@ static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { } // If we have an alloc_stack instruction, include stores into it into the - // backward dependency list. However, whether alloc_stack precedes its in - // control-flow order can only be determined by traversing the instrutions - // in the control-flow order. + // backward dependency list. However, whether alloc_stack precedes the + // definitions of values stored into the location in the control-flow order + // can only be determined by traversing the instrutions in the control-flow + // order. AllocStackInst *allocStackInst = cast(inst); for (StoreInst *storeInst : allocStackInst->getUsersOfType()) { worklist.push_back(storeInst); @@ -726,11 +1325,10 @@ static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) { } } - // Find the first basic block in the control-flow order. TODO: if we do not - // madatorily inline appendLiteral/Interpolation functions of - // OSLogInterpolation, we can expect all candidate instructions to be in the - // same basic block. Once @_transparent is removed from those functions, - // simplify this code. + // Find the first basic block in the control-flow order. Typically, if + // formatting and privacy options are literals, all candidate instructions + // must be in the same basic block. But, this code doesn't rely on that + // assumption. SmallPtrSet candidateBBs; for (auto *candidate: candidateStartInstructions) { SILBasicBlock *candidateBB = candidate->getParent(); @@ -823,9 +1421,6 @@ class OSLogOptimization : public SILFunctionTransform { // of OSLogMessage, which ends up invoking the OSLogMessage initializer: // "oslog.message.init_interpolation" without an interpolated string // literal that is expected by this pass. - // TODO: this check can be eliminated if there is a separate pass for - // diagnosing errors in the use of the OSLogMessage type, and this pass - // bails out when a use of the type is not optimizable. if (isMethodOfOSLogMessage(fun)) { return; } @@ -842,29 +1437,20 @@ class OSLogOptimization : public SILFunctionTransform { } } + bool madeChange = false; + // Constant fold the uses of properties of OSLogMessage instance. Note that // the function body will change due to constant folding, after each // iteration. for (auto *oslogInit : oslogMessageInits) { - - // Find the first instruction from where constant evaluation and folding - // must begin. The first instruction should precede (in the control-flow - // order) the instructions that are generated by the compiler for - // the string-interpolation literal that is used to instantiate - // OSLogMessage instance. SILInstruction *interpolationStart = beginOfInterpolation(oslogInit); - if (!interpolationStart) { - // This scenario indicates an explicit initialization of OSLogMessage - // that doesn't use a string inteprolation literal. - // However, this is not always an error as explicit initialization is - // used by thunk initializers auto-generated for protocol conformances. - // TODO: the log APIs uses must be diagnosed by a separate pass - // (possibly before mandatory inlining). The current pass should not - // emit diagnostics but only perform optimization. - continue; - } + assert(interpolationStart); + madeChange |= constantFold(interpolationStart, oslogInit, assertConfig); + } - constantFold(interpolationStart, oslogInit, assertConfig); + // TODO: Can we be more conservative here with our invalidation? + if (madeChange) { + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } }; diff --git a/lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp similarity index 98% rename from lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp rename to lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index 762ed91ccf974..40c47fd45f47d 100644 --- a/lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -258,7 +258,8 @@ static void splitDestructure(SILBuilder &B, SILInstruction *I, SILValue Op) { SILType OpType = Op->getType(); llvm::SmallVector Projections; - Projection::getFirstLevelProjections(OpType, M, Projections); + Projection::getFirstLevelProjections(OpType, M, B.getTypeExpansionContext(), + Projections); assert(Projections.size() == I->getNumResults()); auto Results = I->getResults(); @@ -318,9 +319,9 @@ static bool stripOwnership(SILFunction &F) { OwnershipModelEliminatorVisitor Visitor(B); for (auto &BB : F) { - // Change all arguments to have ValueOwnershipKind::Any. + // Change all arguments to have ValueOwnershipKind::None. for (auto *Arg : BB.getArguments()) { - Arg->setOwnershipKind(ValueOwnershipKind::Any); + Arg->setOwnershipKind(ValueOwnershipKind::None); } for (auto II = BB.begin(), IE = BB.end(); II != IE;) { diff --git a/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp index a333d41114343..5aa0d49fa6034 100644 --- a/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp @@ -34,7 +34,9 @@ PMOMemoryObjectInfo::PMOMemoryObjectInfo(AllocationInst *allocation) if (auto *abi = dyn_cast(MemoryInst)) { assert(abi->getBoxType()->getLayout()->getFields().size() == 1 && "analyzing multi-field boxes not implemented"); - MemorySILType = getSILBoxFieldType(abi->getBoxType(), module.Types, 0); + MemorySILType = + getSILBoxFieldType(TypeExpansionContext(*abi->getFunction()), + abi->getBoxType(), module.Types, 0); } else { MemorySILType = cast(MemoryInst)->getElementType(); } diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 2e814332b302b..351068091448a 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -13,14 +13,18 @@ #define DEBUG_TYPE "predictable-memopt" #include "PMOMemoryUseCollector.h" +#include "swift/Basic/BlotSetVector.h" +#include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/BranchPropagatedUser.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/SILBuilder.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" @@ -47,19 +51,22 @@ getFullyReferenceableStruct(SILType Ty) { return SD; } -static unsigned getNumSubElements(SILType T, SILModule &M) { +static unsigned getNumSubElements(SILType T, SILModule &M, + TypeExpansionContext context) { if (auto TT = T.getAs()) { unsigned NumElements = 0; for (auto index : indices(TT.getElementTypes())) - NumElements += getNumSubElements(T.getTupleElementType(index), M); + NumElements += + getNumSubElements(T.getTupleElementType(index), M, context); return NumElements; } if (auto *SD = getFullyReferenceableStruct(T)) { unsigned NumElements = 0; for (auto *D : SD->getStoredProperties()) - NumElements += getNumSubElements(T.getFieldType(D, M), M); + NumElements += + getNumSubElements(T.getFieldType(D, M, context), M, context); return NumElements; } @@ -127,7 +134,9 @@ static unsigned computeSubelement(SILValue Pointer, // Keep track of what subelement is being referenced. for (unsigned i = 0, e = TEAI->getFieldNo(); i != e; ++i) { - SubElementNumber += getNumSubElements(TT.getTupleElementType(i), M); + SubElementNumber += + getNumSubElements(TT.getTupleElementType(i), M, + TypeExpansionContext(*RootInst->getFunction())); } Pointer = TEAI->getOperand(); continue; @@ -140,7 +149,9 @@ static unsigned computeSubelement(SILValue Pointer, StructDecl *SD = SEAI->getStructDecl(); for (auto *D : SD->getStoredProperties()) { if (D == SEAI->getField()) break; - SubElementNumber += getNumSubElements(ST.getFieldType(D, M), M); + auto context = TypeExpansionContext(*RootInst->getFunction()); + SubElementNumber += + getNumSubElements(ST.getFieldType(D, M, context), M, context); } Pointer = SEAI->getOperand(); @@ -316,7 +327,8 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val, for (unsigned EltNo : indices(TT.getElementTypes())) { // Keep track of what subelement is being referenced. SILType EltTy = ValTy.getTupleElementType(EltNo); - unsigned NumSubElt = getNumSubElements(EltTy, B.getModule()); + unsigned NumSubElt = getNumSubElements( + EltTy, B.getModule(), TypeExpansionContext(B.getFunction())); if (SubElementNumber < NumSubElt) { auto BorrowedVal = Val.emitBeginBorrow(B, Loc); auto NewVal = @@ -338,9 +350,11 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val, // Extract struct elements. if (auto *SD = getFullyReferenceableStruct(ValTy)) { for (auto *D : SD->getStoredProperties()) { - auto fieldType = ValTy.getFieldType(D, B.getModule()); - unsigned NumSubElt = getNumSubElements(fieldType, B.getModule()); - + auto fieldType = ValTy.getFieldType( + D, B.getModule(), TypeExpansionContext(B.getFunction())); + unsigned NumSubElt = getNumSubElements( + fieldType, B.getModule(), TypeExpansionContext(B.getFunction())); + if (SubElementNumber < NumSubElt) { auto BorrowedVal = Val.emitBeginBorrow(B, Loc); auto NewVal = @@ -387,6 +401,12 @@ static bool anyMissing(unsigned StartSubElt, unsigned NumSubElts, namespace { +enum class AvailableValueExpectedOwnership { + Take, + Borrow, + Copy, +}; + /// A class that aggregates available values, loading them if they are not /// available. class AvailableValueAggregator { @@ -396,7 +416,7 @@ class AvailableValueAggregator { MutableArrayRef AvailableValueList; SmallVectorImpl &Uses; DeadEndBlocks &deadEndBlocks; - bool isTake; + AvailableValueExpectedOwnership expectedOwnership; /// Keep track of all instructions that we have added. Once we are done /// promoting a value, we need to make sure that if we need to balance any @@ -404,14 +424,23 @@ class AvailableValueAggregator { /// take. SmallVector insertedInsts; + /// The list of phi nodes inserted by the SSA updater. + SmallVector insertedPhiNodes; + + /// A set of copy_values whose lifetime we balanced while inserting phi + /// nodes. This means that these copy_value must be skipped in + /// addMissingDestroysForCopiedValues. + SmallPtrSet copyValueProcessedWithPhiNodes; + public: AvailableValueAggregator(SILInstruction *Inst, MutableArrayRef AvailableValueList, SmallVectorImpl &Uses, - DeadEndBlocks &deadEndBlocks, bool isTake) + DeadEndBlocks &deadEndBlocks, + AvailableValueExpectedOwnership expectedOwnership) : M(Inst->getModule()), B(Inst), Loc(Inst->getLoc()), AvailableValueList(AvailableValueList), Uses(Uses), - deadEndBlocks(deadEndBlocks), isTake(isTake) {} + deadEndBlocks(deadEndBlocks), expectedOwnership(expectedOwnership) {} // This is intended to be passed by reference only once constructed. AvailableValueAggregator(const AvailableValueAggregator &) = delete; @@ -424,15 +453,32 @@ class AvailableValueAggregator { bool isTopLevel = true); bool canTake(SILType loadTy, unsigned firstElt) const; - /// If as a result of us copying values, we may have unconsumed destroys, find - /// the appropriate location and place the values there. Only used when - /// ownership is enabled. - SingleValueInstruction * - addMissingDestroysForCopiedValues(SingleValueInstruction *li, - SILValue newVal); - void print(llvm::raw_ostream &os) const; void dump() const LLVM_ATTRIBUTE_USED; + + bool isTake() const { + return expectedOwnership == AvailableValueExpectedOwnership::Take; + } + + bool isBorrow() const { + return expectedOwnership == AvailableValueExpectedOwnership::Borrow; + } + + bool isCopy() const { + return expectedOwnership == AvailableValueExpectedOwnership::Copy; + } + + /// Given a load_borrow that we have aggregated a new value for, fixup the + /// reference counts of the intermediate copies and phis to ensure that all + /// forwarding operations in the CFG are strongly control equivalent (i.e. run + /// the same number of times). + void fixupOwnership(SILInstruction *load, SILValue newVal) { + assert(isa(load) || isa(load)); + + addHandOffCopyDestroysForPhis(load, newVal); + addMissingDestroysForCopiedValues(load, newVal); + } + private: SILValue aggregateFullyAvailableValue(SILType loadTy, unsigned firstElt); SILValue aggregateTupleSubElts(TupleType *tt, SILType loadTy, @@ -442,6 +488,18 @@ class AvailableValueAggregator { SILValue handlePrimitiveValue(SILType loadTy, SILValue address, unsigned firstElt); bool isFullyAvailable(SILType loadTy, unsigned firstElt) const; + + + /// If as a result of us copying values, we may have unconsumed destroys, find + /// the appropriate location and place the values there. Only used when + /// ownership is enabled. + void addMissingDestroysForCopiedValues(SILInstruction *load, SILValue newVal); + + /// As a result of us using the SSA updater, insert hand off copy/destroys at + /// each phi and make sure that intermediate phis do not leak by inserting + /// destroys along paths that go through the intermediate phi that do not also + /// go through the + void addHandOffCopyDestroysForPhis(SILInstruction *load, SILValue newVal); }; } // end anonymous namespace @@ -468,7 +526,8 @@ bool AvailableValueAggregator::isFullyAvailable(SILType loadTy, if (!firstVal || firstVal.getType() != loadTy) return false; - return llvm::all_of(range(getNumSubElements(loadTy, M)), + return llvm::all_of(range(getNumSubElements( + loadTy, M, TypeExpansionContext(B.getFunction()))), [&](unsigned index) -> bool { auto &val = AvailableValueList[firstElt + index]; return val.getValue() == firstVal.getValue() && @@ -494,7 +553,8 @@ bool AvailableValueAggregator::canTake(SILType loadTy, if (TupleType *tt = loadTy.getAs()) { return llvm::all_of(indices(tt->getElements()), [&](unsigned eltNo) { SILType eltTy = loadTy.getTupleElementType(eltNo); - unsigned numSubElt = getNumSubElements(eltTy, M); + unsigned numSubElt = + getNumSubElements(eltTy, M, TypeExpansionContext(B.getFunction())); bool success = canTake(eltTy, firstElt); firstElt += numSubElt; return success; @@ -503,8 +563,9 @@ bool AvailableValueAggregator::canTake(SILType loadTy, if (auto *sd = getFullyReferenceableStruct(loadTy)) { return llvm::all_of(sd->getStoredProperties(), [&](VarDecl *decl) -> bool { - SILType eltTy = loadTy.getFieldType(decl, M); - unsigned numSubElt = getNumSubElements(eltTy, M); + auto context = TypeExpansionContext(B.getFunction()); + SILType eltTy = loadTy.getFieldType(decl, M, context); + unsigned numSubElt = getNumSubElements(eltTy, M, context); bool success = canTake(eltTy, firstElt); firstElt += numSubElt; return success; @@ -524,27 +585,66 @@ SILValue AvailableValueAggregator::aggregateValues(SILType LoadTy, bool isTopLevel) { // If we are performing a take, make sure that we have available values for // /all/ of our values. Otherwise, bail. - if (isTopLevel && isTake && !canTake(LoadTy, FirstElt)) { + if (isTopLevel && isTake() && !canTake(LoadTy, FirstElt)) { return SILValue(); } // Check to see if the requested value is fully available, as an aggregate. // This is a super-common case for single-element structs, but is also a // general answer for arbitrary structs and tuples as well. - if (SILValue Result = aggregateFullyAvailableValue(LoadTy, FirstElt)) + if (SILValue Result = aggregateFullyAvailableValue(LoadTy, FirstElt)) { return Result; + } // If we have a tuple type, then aggregate the tuple's elements into a full // tuple value. - if (TupleType *TT = LoadTy.getAs()) - return aggregateTupleSubElts(TT, LoadTy, Address, FirstElt); + if (TupleType *tupleType = LoadTy.getAs()) { + SILValue result = + aggregateTupleSubElts(tupleType, LoadTy, Address, FirstElt); + if (isTopLevel && + result.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + SILValue borrowedResult = result; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + result = builder.emitCopyValueOperation(Loc, borrowedResult); + SmallVector introducers; + bool foundIntroducers = + getUnderlyingBorrowIntroducingValues(borrowedResult, introducers); + (void)foundIntroducers; + assert(foundIntroducers); + for (auto value : introducers) { + builder.emitEndBorrowOperation(Loc, value.value); + } + } + return result; + } // If we have a struct type, then aggregate the struct's elements into a full // struct value. - if (auto *SD = getFullyReferenceableStruct(LoadTy)) - return aggregateStructSubElts(SD, LoadTy, Address, FirstElt); + if (auto *structDecl = getFullyReferenceableStruct(LoadTy)) { + SILValue result = + aggregateStructSubElts(structDecl, LoadTy, Address, FirstElt); + if (isTopLevel && + result.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { + SILValue borrowedResult = result; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + result = builder.emitCopyValueOperation(Loc, borrowedResult); + SmallVector introducers; + bool foundIntroducers = + getUnderlyingBorrowIntroducingValues(borrowedResult, introducers); + (void)foundIntroducers; + assert(foundIntroducers); + for (auto value : introducers) { + builder.emitEndBorrowOperation(Loc, value.value); + } + } + return result; + } // Otherwise, we have a non-aggregate primitive. Load or extract the value. + // + // NOTE: We should never call this when taking since when taking we know that + // our underlying value is always fully available. + assert(!isTake()); return handlePrimitiveValue(LoadTy, Address, FirstElt); } @@ -574,7 +674,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, SILBuilderWithScope builder(insertPts[0], &insertedInsts); SILLocation loc = insertPts[0]->getLoc(); // If we have a take, just return the value. - if (isTake) + if (isTake()) return firstVal.getValue(); // Otherwise, return a copy of the value. return builder.emitCopyValueOperation(loc, firstVal.getValue()); @@ -584,7 +684,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // SSA updater to get a value. The reason why this is safe is that we can only // have multiple insertion points if we are storing exactly the same value // implying that we can just copy firstVal at each insertion point. - SILSSAUpdater updater; + SILSSAUpdater updater(&insertedPhiNodes); updater.Initialize(loadTy); Optional singularValue; @@ -595,7 +695,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, SILValue eltVal = firstVal.getValue(); // If we are not taking, copy the element value. - if (!isTake) { + if (!isTake()) { eltVal = builder.emitCopyValueOperation(loc, eltVal); } @@ -611,13 +711,31 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy, // If we only are tracking a singular value, we do not need to construct // SSA. Just return that value. - if (auto val = singularValue.getValueOr(SILValue())) + if (auto val = singularValue.getValueOr(SILValue())) { + // This assert documents that we are expecting that if we are in ossa, have + // a non-trivial value, and are not taking, we should never go down this + // code path. If we did, we would need to insert a copy here. The reason why + // we know we will never go down this code path is since we have been + // inserting copy_values implying that our potential singular value would be + // of the copy_values which are guaranteed to all be different. + assert((!B.hasOwnership() || isTake() || + val->getType().isTrivial(*B.getInsertionBB()->getParent())) && + "Should never reach this code path if we are in ossa and have a " + "non-trivial value"); return val; + } // Finally, grab the value from the SSA updater. SILValue result = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); assert(result.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); - return result; + if (isTake() || !B.hasOwnership()) { + return result; + } + + // Be careful with this value and insert a copy in our load block to prevent + // any weird control equivalence issues. + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, result); } SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, @@ -628,13 +746,14 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, for (unsigned EltNo : indices(TT->getElements())) { SILType EltTy = LoadTy.getTupleElementType(EltNo); - unsigned NumSubElt = getNumSubElements(EltTy, M); + unsigned NumSubElt = + getNumSubElements(EltTy, M, TypeExpansionContext(B.getFunction())); // If we are missing any of the available values in this struct element, // compute an address to load from. SILValue EltAddr; if (anyMissing(FirstElt, NumSubElt, AvailableValueList)) { - assert(!isTake && "When taking, values should never be missing?!"); + assert(!isTake() && "When taking, values should never be missing?!"); EltAddr = B.createTupleElementAddr(Loc, Address, EltNo, EltTy.getAddressType()); } @@ -644,6 +763,15 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT, FirstElt += NumSubElt; } + // If we are going to use this to promote a borrowed value, insert borrow + // operations. Eventually I am going to do this for everything, but this + // should make it easier to bring up. + if (!isTake()) { + for (unsigned i : indices(ResultElts)) { + ResultElts[i] = B.emitBeginBorrowOperation(Loc, ResultElts[i]); + } + } + return B.createTuple(Loc, LoadTy, ResultElts); } @@ -654,14 +782,15 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, SmallVector resultElts; for (auto *decl : sd->getStoredProperties()) { - SILType eltTy = loadTy.getFieldType(decl, M); - unsigned numSubElt = getNumSubElements(eltTy, M); + auto context = TypeExpansionContext(B.getFunction()); + SILType eltTy = loadTy.getFieldType(decl, M, context); + unsigned numSubElt = getNumSubElements(eltTy, M, context); // If we are missing any of the available values in this struct element, // compute an address to load from. SILValue eltAddr; if (anyMissing(firstElt, numSubElt, AvailableValueList)) { - assert(!isTake && "When taking, values should never be missing?!"); + assert(!isTake() && "When taking, values should never be missing?!"); eltAddr = B.createStructElementAddr(Loc, address, decl, eltTy.getAddressType()); } @@ -671,24 +800,32 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd, firstElt += numSubElt; } + if (!isTake()) { + for (unsigned i : indices(resultElts)) { + resultElts[i] = B.emitBeginBorrowOperation(Loc, resultElts[i]); + } + } + return B.createStruct(Loc, loadTy, resultElts); } -// We have looked through all of the aggregate values and finally found a -// "primitive value". If the value is available, use it (extracting if we need -// to), otherwise emit a load of the value with the appropriate qualifier. +// We have looked through all of the aggregate values and finally found a value +// that is not available without transforming, i.e. a "primitive value". If the +// value is available, use it (extracting if we need to), otherwise emit a load +// of the value with the appropriate qualifier. SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, SILValue address, unsigned firstElt) { - auto &val = AvailableValueList[firstElt]; + assert(!isTake() && "Should only take fully available values?!"); // If the value is not available, load the value and update our use list. + auto &val = AvailableValueList[firstElt]; if (!val) { - assert(!isTake && "Should only take fully available values?!"); LoadInst *load = ([&]() { if (B.hasOwnership()) { - return B.createTrivialLoadOr(Loc, address, - LoadOwnershipQualifier::Copy); + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.createTrivialLoadOr(Loc, address, + LoadOwnershipQualifier::Copy); } return B.createLoad(Loc, address, LoadOwnershipQualifier::Unqualified); }()); @@ -710,12 +847,21 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, !builder.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + + if (!builder.hasOwnership()) { + return eltVal; + } + + SILBuilderWithScope builder2(&*B.getInsertionPoint(), &insertedInsts); + return builder2.emitCopyValueOperation(Loc, eltVal); } // If we have an available value, then we want to extract the subelement from - // the borrowed aggregate before each insertion point. - SILSSAUpdater updater; + // the borrowed aggregate before each insertion point. Note that since we have + // inserted copies at each of these insertion points, we know that we will + // never have the same value along all paths unless we have a trivial value + // meaning the SSA updater given a non-trivial value must /always/ be used. + SILSSAUpdater updater(&insertedPhiNodes); updater.Initialize(loadTy); Optional singularValue; @@ -737,44 +883,336 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy, updater.AddAvailableValue(i->getParent(), eltVal); } - // If we only are tracking a singular value, we do not need to construct - // SSA. Just return that value. - if (auto val = singularValue.getValueOr(SILValue())) + SILBasicBlock *insertBlock = B.getInsertionBB(); + + // If we are not in ossa and have a singular value or if we are in ossa and + // have a trivial singular value, just return that value. + // + // This can never happen for non-trivial values in ossa since we never should + // visit this code path if we have a take implying that non-trivial values + // /will/ have a copy and thus are guaranteed (since each copy yields a + // different value) to not be singular values. + if (auto val = singularValue.getValueOr(SILValue())) { + assert((!B.hasOwnership() || + val->getType().isTrivial(*insertBlock->getParent())) && + "Should have inserted copies for each insertion point, so shouldn't " + "have a singular value if non-trivial?!"); return val; + } // Finally, grab the value from the SSA updater. - SILValue eltVal = updater.GetValueInMiddleOfBlock(B.getInsertionBB()); + SILValue eltVal = updater.GetValueInMiddleOfBlock(insertBlock); assert(!B.hasOwnership() || eltVal.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Owned)); assert(eltVal->getType() == loadTy && "Subelement types mismatch"); - return eltVal; + if (!B.hasOwnership()) + return eltVal; + SILBuilderWithScope builder(&*B.getInsertionPoint(), &insertedInsts); + return builder.emitCopyValueOperation(Loc, eltVal); } -SingleValueInstruction * -AvailableValueAggregator::addMissingDestroysForCopiedValues( - SingleValueInstruction *svi, SILValue newVal) { - // If ownership is not enabled... bail. We do not need to do this since we do - // not need to insert an extra copy unless we have ownership since without - // ownership stores do not consume. - if (!B.hasOwnership()) - return svi; +static SILInstruction *getNonPhiBlockIncomingValueDef(SILValue incomingValue, + CopyValueInst *phiCopy) { + auto *phiBlock = phiCopy->getParent(); + if (phiBlock == incomingValue->getParentBlock()) { + return nullptr; + } - assert((isa(svi) || isa(svi)) && - "Expected to have a /real/ load here since we assume that we have a " - "unary operand instruction"); + if (auto *cvi = dyn_cast(incomingValue)) { + return cvi; + } + + assert(isa(incomingValue)); + + // Otherwise, our copy_value may not be post-dominated by our phi. To + // work around that, we need to insert destroys along the other + // paths. So set base to the first instruction in our argument's block, + // so we can insert destroys for our base. + return &*incomingValue->getParentBlock()->begin(); +} + +static bool +terminatorHasAnyKnownPhis(TermInst *ti, + ArrayRef insertedPhiNodesSorted) { + for (auto succArgList : ti->getSuccessorBlockArgumentLists()) { + if (llvm::any_of(succArgList, [&](SILArgument *arg) { + return binary_search(insertedPhiNodesSorted, + cast(arg)); + })) { + return true; + } + } + + return false; +} + +namespace { + +class PhiNodeCopyCleanupInserter { + llvm::SmallMapVector incomingValues; + + /// Map from index -> (incomingValueIndex, copy). + /// + /// We are going to stable_sort this array using the indices of + /// incomingValueIndex. This will ensure that we always visit in + /// insertion order our incoming values (since the indices we are + /// sorting by are the count of incoming values we have seen so far + /// when we see the incoming value) and maintain the internal + /// insertion sort within our range as well. This ensures that we + /// visit our incoming values in visitation order and that within + /// their own values, also visit them in visitation order with + /// respect to each other. + SmallVector, 16> copiesToCleanup; + + /// The lifetime frontier that we use to compute lifetime endpoints + /// when emitting cleanups. + ValueLifetimeAnalysis::Frontier lifetimeFrontier; + +public: + PhiNodeCopyCleanupInserter() = default; + + void trackNewCleanup(SILValue incomingValue, CopyValueInst *copy) { + auto entry = std::make_pair(incomingValue, incomingValues.size()); + auto iter = incomingValues.insert(entry); + // If we did not succeed, then iter.first.second is the index of + // incoming value. Otherwise, it will be nextIndex. + copiesToCleanup.emplace_back(iter.first->second, copy); + } + + void emit(DeadEndBlocks &deadEndBlocks) &&; +}; + +} // end anonymous namespace + +void PhiNodeCopyCleanupInserter::emit(DeadEndBlocks &deadEndBlocks) && { + // READ THIS: We are being very careful here to avoid allowing for + // non-determinism to enter here. + // + // 1. First we create a list of indices of our phi node data. Then we use a + // stable sort those indices into the order in which our phi node cleanups + // would be in if we compared just using incomingValues. We use a stable + // sort here to ensure that within the same "cohort" of values, our order + // is insertion order. + // + // 2. We go through the list of phiNodeCleanupStates in insertion order. We + // also maintain a set of already visited base values. When we visit the + // first phiNodeCleanupState for a specific phi, we process the phi + // then. This ensures that we always process the phis in insertion order as + // well. + SmallVector copiesToCleanupIndicesSorted; + llvm::copy(indices(copiesToCleanup), + std::back_inserter(copiesToCleanupIndicesSorted)); + + stable_sort(copiesToCleanupIndicesSorted, + [&](unsigned lhsIndex, unsigned rhsIndex) { + unsigned lhs = copiesToCleanup[lhsIndex].first; + unsigned rhs = copiesToCleanup[rhsIndex].first; + return lhs < rhs; + }); + + for (auto ii = copiesToCleanupIndicesSorted.begin(), + ie = copiesToCleanupIndicesSorted.end(); + ii != ie;) { + unsigned incomingValueIndex = copiesToCleanup[*ii].first; + + // First find the end of the values for which ii does not equal baseValue. + auto rangeEnd = std::find_if_not(std::next(ii), ie, [&](unsigned index) { + return incomingValueIndex == copiesToCleanup[index].first; + }); + + SWIFT_DEFER { + // Once we have finished processing, set ii to rangeEnd. This ensures that + // the code below does not need to worry about updating the iterator. + ii = rangeEnd; + }; + + SILValue incomingValue = + std::next(incomingValues.begin(), incomingValueIndex)->first; + CopyValueInst *phiCopy = copiesToCleanup[*ii].second; + auto *insertPt = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); + auto loc = RegularLocation::getAutoGeneratedLocation(); + + // Before we do anything, see if we have a single cleanup state. In such a + // case, we could have that we have a phi node as an incoming value and a + // copy_value in that same block. In such a case, we want to just insert the + // copy and continue. This means that + // cleanupState.getNonPhiBlockIncomingValueDef() should always return a + // non-null value in the code below. + if (std::next(ii) == rangeEnd && isa(incomingValue) && + !insertPt) { + SILBasicBlock *phiBlock = phiCopy->getParent(); + SILBuilderWithScope builder(phiBlock->getTerminator()); + builder.createDestroyValue(loc, incomingValue); + continue; + } + + // Otherwise, we know that we have for this incomingValue, multiple + // potential insert pts that we need to handle at the same time with our + // lifetime query. Gather up those uses. + SmallVector users; + transform(llvm::make_range(ii, rangeEnd), std::back_inserter(users), + [&](unsigned index) { return copiesToCleanup[index].second; }); + + // Then lifetime extend our base over the copy_value. + assert(lifetimeFrontier.empty()); + auto *def = getNonPhiBlockIncomingValueDef(incomingValue, phiCopy); + assert(def && "Should never have a nullptr here since we handled all of " + "the single block cases earlier"); + ValueLifetimeAnalysis analysis(def, users); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createDestroyValue(loc, incomingValue); + } + } +} + +void AvailableValueAggregator::addHandOffCopyDestroysForPhis( + SILInstruction *load, SILValue newVal) { + assert(isa(load) || isa(load)); SmallPtrSet visitedBlocks; SmallVector leakingBlocks; - bool foundLoop = false; + SmallVector, 8> incomingValues; auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!insertedInsts.empty()) { - auto *cvi = dyn_cast(insertedInsts.pop_back_val()); - if (!cvi) + +#ifndef NDEBUG + LLVM_DEBUG(llvm::dbgs() << "Inserted Phis!\n"); + for (auto *phi : insertedPhiNodes) { + LLVM_DEBUG(llvm::dbgs() << "Phi: " << *phi); + } +#endif + + // Before we begin, identify the offset for all phis that are intermediate + // phis inserted by the SSA updater. We are taking advantage of the fact that + // the SSA updater just constructs the web without knowledge of ownership. So + // if a phi node is only used by another phi node that we inserted, then we + // have an intermediate phi node. + // + // TODO: There should be a better way of doing this than doing a copy + sort. + SmallVector insertedPhiNodesSorted; + llvm::copy(insertedPhiNodes, std::back_inserter(insertedPhiNodesSorted)); + llvm::sort(insertedPhiNodesSorted); + + SmallBitVector intermediatePhiOffsets(insertedPhiNodes.size()); + for (unsigned i : indices(insertedPhiNodes)) { + if (TermInst *termInst = + insertedPhiNodes[i]->getSingleUserOfType()) { + // Only set the value if we find termInst has a successor with a phi node + // in our insertedPhiNodes. + if (terminatorHasAnyKnownPhis(termInst, insertedPhiNodesSorted)) { + intermediatePhiOffsets.set(i); + } + } + } + + // First go through all of our phi nodes doing the following: + // + // 1. If any of the phi node have a copy_value as an operand, we know that the + // copy_value does not dominate our final definition since otherwise the + // SSA updater would not have inserted a phi node here. In such a case + // since we may not have that the copy_value is post-dominated by the phi, + // we need to insert a copy_value at the phi to allow for post-domination + // and then use the ValueLifetimeChecker to determine the rest of the + // frontier for the base value. + // + // 2. If our phi node is used by another phi node, we run into a similar + // problem where we could have that our original phi node does not dominate + // our final definition (since the SSA updater would not have inserted the + // phi) and may not be strongly control dependent on our phi. To work + // around this problem, we insert at the phi a copy_value to allow for the + // phi to post_dominate its copy and then extend the lifetime of the phied + // value over that copy. + // + // As an extra complication to this, when we insert compensating releases for + // any copy_values from (1), we need to insert the destroy_value on "base + // values" (either a copy_value or the first instruction of a phi argument's + // block) /after/ we have found all of the base_values to ensure that if the + // same base value is used by multiple phis, we do not insert too many destroy + // value. + // + // NOTE: At first glance one may think that such a problem could not occur + // with phi nodes as well. Sadly if we allow for double backedge loops, it is + // possible (there may be more cases). + PhiNodeCopyCleanupInserter cleanupInserter; + + for (unsigned i : indices(insertedPhiNodes)) { + auto *phi = insertedPhiNodes[i]; + + // If our phi is not owned, continue. No fixes are needed. + if (phi->getOwnershipKind() != ValueOwnershipKind::Owned) continue; - // Clear our state. + LLVM_DEBUG(llvm::dbgs() << "Visiting inserted phi: " << *phi); + // Otherwise, we have a copy_value that may not be strongly control + // equivalent with our phi node. In such a case, we need to use + // ValueLifetimeAnalysis to lifetime extend the copy such that we can + // produce a new copy_value at the phi. We insert destroys along the + // frontier. visitedBlocks.clear(); leakingBlocks.clear(); + incomingValues.clear(); + + phi->getIncomingPhiValues(incomingValues); + unsigned phiIndex = phi->getIndex(); + for (auto pair : incomingValues) { + SILValue value = pair.second; + + // If we had a non-trivial type with non-owned ownership, we will not see + // a copy_value, so skip them here. + if (value.getOwnershipKind() != ValueOwnershipKind::Owned) + continue; + + // Otherwise, value should be from a copy_value or a phi node. + assert(isa(value) || isa(value)); + + // If we have a copy_value, remove it from the inserted insts set so we + // skip it when we start processing insertedInstrs. + if (auto *cvi = dyn_cast(value)) { + copyValueProcessedWithPhiNodes.insert(cvi); + + // Then check if our termInst is in the same block as our copy_value. In + // such a case, we can just use the copy_value as our phi's value + // without needing to worry about any issues around control equivalence. + if (pair.first == cvi->getParent()) + continue; + } else { + assert(isa(value)); + } + + // Otherwise, insert a copy_value instruction right before the phi. We use + // that for our actual phi. + auto *termInst = pair.first->getTerminator(); + SILBuilderWithScope builder(termInst); + CopyValueInst *phiCopy = builder.createCopyValue(loc, value); + termInst->setOperand(phiIndex, phiCopy); + + // Now that we know our base, phi, phiCopy for this specific incoming + // value, append it to the phiNodeClenaupState so we can insert + // destroy_values late after we visit all insertedPhiNodes. + cleanupInserter.trackNewCleanup(value, phiCopy); + } + + // Then see if our phi is an intermediate phi. If it is an intermediate phi, + // we know that this is not the phi node that is post-dominated by the + // load_borrow and that we will lifetime extend it via the child + // phi. Instead, we need to just ensure that our phi arg does not leak onto + // its set of post-dominating paths, subtracting from that set the path + // through our terminator use. + if (intermediatePhiOffsets[i]) { + continue; + } + + // If we reach this point, then we know that we are a phi node that actually + // dominates our user so we need to lifetime extend it over the + // load_borrow. Thus insert copy_value along the incoming edges and then + // lifetime extend the phi node over the load_borrow. + // // The linear lifetime checker doesn't care if the passed in load is // actually a user of our copy_value. What we care about is that the load is // guaranteed to be in the block where we have reformed the tuple in a @@ -786,62 +1224,128 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues( auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); auto error = checker.checkValue( - cvi, {BranchPropagatedUser(&svi->getAllOperands()[0])}, {}, errorKind, + phi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, &leakingBlocks); - if (!error.getFoundError()) + + if (!error.getFoundError()) { + // If we did not find an error, then our copy_value must be strongly + // control equivalent as our load_borrow. So just insert a destroy_value + // for the copy_value. + auto next = std::next(load->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), phi); continue; + } - // Ok, we found some leaking blocks. Since we are using the linear lifetime - // checker with memory, we do not have any guarantees that the store is out - // side of a loop and a load is in a loop. In such a case, we want to - // replace the load with a copy_value. - foundLoop |= error.getFoundOverConsume(); + // Ok, we found some leaking blocks and potentially a loop. If we do not + // find a loop, insert the destroy_value after the load_borrow. We do not do + // this if we found a loop since our leaking blocks will lifetime extend the + // value over the loop. + if (!error.getFoundOverConsume()) { + auto next = std::next(load->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), phi); + } - // Ok, we found some leaking blocks. Insert destroys at the - // beginning of these blocks for our copy_value. + // Ok, we found some leaking blocks. Insert destroys at the beginning of + // these blocks for our copy_value. for (auto *bb : leakingBlocks) { SILBuilderWithScope b(bb->begin()); - b.emitDestroyValueOperation(loc, cvi); + b.emitDestroyValueOperation(loc, phi); } } - // If we didn't find a loop, we are done, just return svi to get RAUWed. - if (!foundLoop) { - // If we had a load_borrow, we have created an extra copy that we are going - // to borrow at the load point. This means we need to handle the destroying - // of the value along paths reachable from the load_borrow. Luckily that - // will exactly be after the end_borrows of the load_borrow. - if (isa(svi)) { - for (auto *use : svi->getUses()) { - if (auto *ebi = dyn_cast(use->getUser())) { - auto next = std::next(ebi->getIterator()); - SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(), - newVal); - } + // Alright! In summary, we just lifetime extended all of our phis, + // lifetime extended them to the load block, and inserted phi copies + // at all of our intermediate phi nodes. Now we need to cleanup and + // insert all of the compensating destroy_value that we need. + std::move(cleanupInserter).emit(deadEndBlocks); + + // Clear the phi node array now that we are done. + insertedPhiNodes.clear(); +} + +void AvailableValueAggregator::addMissingDestroysForCopiedValues( + SILInstruction *load, SILValue newVal) { + assert(B.hasOwnership() && + "We assume this is only called if we have ownership"); + + SmallPtrSet visitedBlocks; + SmallVector leakingBlocks; + auto loc = RegularLocation::getAutoGeneratedLocation(); + + for (auto *inst : insertedInsts) { + // Otherwise, see if this is a load [copy]. It if it a load [copy], then we + // know that the load [copy] must be in the load block meaing we can just + // put a destroy_value /after/ the load_borrow to ensure that the value + // lives long enough for us to copy_value it or a derived value for the + // begin_borrow. + if (auto *li = dyn_cast(inst)) { + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { + assert(li->getParent() == load->getParent()); + auto next = std::next(load->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), li); + continue; } } - return svi; - } - // If we found a loop, then we know that our leaking blocks are the exiting - // blocks of the loop and the value has been lifetime extended over the loop. - if (isa(svi)) { - // If we have a load, we need to put in a copy so that the destroys within - // the loop are properly balanced. - newVal = SILBuilderWithScope(svi).emitCopyValueOperation(loc, newVal); - } else { - // If we have a load_borrow, we create a begin_borrow for the end_borrows in - // the loop. - assert(isa(svi)); - newVal = SILBuilderWithScope(svi).createBeginBorrow(svi->getLoc(), newVal); - } + // Our copy_value may have been unset above if it was used by a phi + // (implying it does not dominate our final user). + auto *cvi = dyn_cast(inst); + if (!cvi) + continue; - svi->replaceAllUsesWith(newVal); - SILValue addr = svi->getOperand(0); - svi->eraseFromParent(); - if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); - return nullptr; + // If we already handled this copy_value above when handling phi nodes, just + // continue. + if (copyValueProcessedWithPhiNodes.count(cvi)) + continue; + + // Clear our state. + visitedBlocks.clear(); + leakingBlocks.clear(); + + // The linear lifetime checker doesn't care if the passed in load is + // actually a user of our copy_value. What we care about is that the load is + // guaranteed to be in the block where we have reformed the tuple in a + // consuming manner. This means if we add it as the consuming use of the + // copy, we can find the leaking places if any exist. + // + // Then perform the linear lifetime check. If we succeed, continue. We have + // no further work to do. + auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse; + LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks); + auto error = checker.checkValue( + cvi, {BranchPropagatedUser(&load->getAllOperands()[0])}, {}, errorKind, + &leakingBlocks); + + if (!error.getFoundError()) { + // If we did not find an error, then our copy_value must be strongly + // control equivalent as our load_borrow. So just insert a destroy_value + // for the copy_value. + auto next = std::next(load->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), cvi); + continue; + } + + // Ok, we found some leaking blocks and potentially a loop. If we do not + // find a loop, insert the destroy_value after the load_borrow. We do not do + // this if we found a loop since our leaking blocks will lifetime extend the + // value over the loop. + if (!error.getFoundOverConsume()) { + auto next = std::next(load->getIterator()); + SILBuilderWithScope builder(next); + builder.emitDestroyValueOperation(next->getLoc(), cvi); + } + + // Ok, we found some leaking blocks. Insert destroys at the beginning of + // these blocks for our copy_value. + for (auto *bb : leakingBlocks) { + SILBuilderWithScope b(bb->begin()); + b.emitDestroyValueOperation(loc, cvi); + } + } } //===----------------------------------------------------------------------===// @@ -1020,8 +1524,9 @@ static inline void updateAvailableValuesHelper( // TODO: Is this needed now? assert(startSubElt != ~0U && "Store within enum projection not handled"); - for (unsigned i : - range(getNumSubElements(address->getType().getObjectType(), mod))) { + for (unsigned i : range(getNumSubElements( + address->getType().getObjectType(), mod, + TypeExpansionContext(*theMemory->getFunction())))) { // If this element is not required, don't fill it in. if (!requiredElts[startSubElt + i]) continue; @@ -1147,12 +1652,13 @@ void AvailableValueDataflowContext::updateAvailableValues( SILType ValTy = CAI->getDest()->getType(); bool AnyRequired = false; - for (unsigned i : range(getNumSubElements(ValTy, getModule()))) { + for (unsigned i : range(getNumSubElements( + ValTy, getModule(), TypeExpansionContext(*CAI->getFunction())))) { // If this element is not required, don't fill it in. AnyRequired = RequiredElts[StartSubElt+i]; if (AnyRequired) break; } - + // If this is a copy addr that doesn't intersect the loaded subelements, // just continue with an unmodified load mask. if (!AnyRequired) @@ -1439,7 +1945,8 @@ static SILType getMemoryType(AllocationInst *memory) { if (auto *abi = dyn_cast(memory)) { assert(abi->getBoxType()->getLayout()->getFields().size() == 1 && "optimizing multi-field boxes not implemented"); - return getSILBoxFieldType(abi->getBoxType(), abi->getModule().Types, 0); + return getSILBoxFieldType(TypeExpansionContext(*abi->getFunction()), + abi->getBoxType(), abi->getModule().Types, 0); } assert(isa(memory)); @@ -1478,8 +1985,9 @@ class AllocOptimize { DeadEndBlocks &deadEndBlocks) : Module(memory->getModule()), TheMemory(memory), MemoryType(getMemoryType(memory)), - NumMemorySubElements(getNumSubElements(MemoryType, Module)), Uses(uses), - Releases(releases), deadEndBlocks(deadEndBlocks), + NumMemorySubElements(getNumSubElements( + MemoryType, Module, TypeExpansionContext(*memory->getFunction()))), + Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks), DataflowContext(TheMemory, NumMemorySubElements, uses) {} bool optimizeMemoryAccesses(); @@ -1489,7 +1997,12 @@ class AllocOptimize { bool tryToRemoveDeadAllocation(); private: - bool promoteLoadCopy(SILInstruction *Inst); + Optional> + computeAvailableValues(SILValue SrcAddr, SILInstruction *Inst, + SmallVectorImpl &AvailableValues); + bool promoteLoadCopy(LoadInst *li); + bool promoteLoadBorrow(LoadBorrowInst *lbi); + bool promoteCopyAddr(CopyAddrInst *cai); void promoteLoadTake(LoadInst *Inst, MutableArrayRef values); void promoteDestroyAddr(DestroyAddrInst *dai, MutableArrayRef values); @@ -1499,6 +2012,44 @@ class AllocOptimize { } // end anonymous namespace +Optional> AllocOptimize::computeAvailableValues( + SILValue SrcAddr, SILInstruction *Inst, + SmallVectorImpl &AvailableValues) { + // If the box has escaped at this instruction, we can't safely promote the + // load. + if (DataflowContext.hasEscapedAt(Inst)) + return None; + + SILType LoadTy = SrcAddr->getType().getObjectType(); + + // If this is a load/copy_addr from a struct field that we want to promote, + // compute the access path down to the field so we can determine precise + // def/use behavior. + unsigned FirstElt = computeSubelement(SrcAddr, TheMemory); + + // If this is a load from within an enum projection, we can't promote it since + // we don't track subelements in a type that could be changing. + if (FirstElt == ~0U) + return None; + + unsigned NumLoadSubElements = getNumSubElements( + LoadTy, Module, TypeExpansionContext(*TheMemory->getFunction())); + + // Set up the bitvector of elements being demanded by the load. + SmallBitVector RequiredElts(NumMemorySubElements); + RequiredElts.set(FirstElt, FirstElt + NumLoadSubElements); + + AvailableValues.resize(NumMemorySubElements); + + // Find out if we have any available values. If no bits are demanded, we + // trivially succeed. This can happen when there is a load of an empty struct. + if (NumLoadSubElements != 0 && + !DataflowContext.computeAvailableValues( + Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues)) + return None; + + return std::make_pair(LoadTy, FirstElt); +} /// If we are able to optimize \p Inst, return the source address that /// instruction is loading from. If we can not optimize \p Inst, then just @@ -1528,7 +2079,7 @@ static SILValue tryFindSrcAddrForLoad(SILInstruction *i) { /// cross element accesses have been scalarized. /// /// This returns true if the load has been removed from the program. -bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) { +bool AllocOptimize::promoteLoadCopy(LoadInst *li) { // Note that we intentionally don't support forwarding of weak pointers, // because the underlying value may drop be deallocated at any time. We would // have to prove that something in this function is holding the weak value @@ -1537,68 +2088,40 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) { // First attempt to find a source addr for our "load" instruction. If we fail // to find a valid value, just return. - SILValue SrcAddr = tryFindSrcAddrForLoad(Inst); - if (!SrcAddr) + SILValue srcAddr = tryFindSrcAddrForLoad(li); + if (!srcAddr) return false; - // If the box has escaped at this instruction, we can't safely promote the - // load. - if (DataflowContext.hasEscapedAt(Inst)) + SmallVector availableValues; + auto result = computeAvailableValues(srcAddr, li, availableValues); + if (!result.hasValue()) return false; - SILType LoadTy = SrcAddr->getType().getObjectType(); - - // If this is a load/copy_addr from a struct field that we want to promote, - // compute the access path down to the field so we can determine precise - // def/use behavior. - unsigned FirstElt = computeSubelement(SrcAddr, TheMemory); - - // If this is a load from within an enum projection, we can't promote it since - // we don't track subelements in a type that could be changing. - if (FirstElt == ~0U) - return false; - - unsigned NumLoadSubElements = getNumSubElements(LoadTy, Module); - - // Set up the bitvector of elements being demanded by the load. - SmallBitVector RequiredElts(NumMemorySubElements); - RequiredElts.set(FirstElt, FirstElt+NumLoadSubElements); - - SmallVector AvailableValues; - AvailableValues.resize(NumMemorySubElements); - - // Find out if we have any available values. If no bits are demanded, we - // trivially succeed. This can happen when there is a load of an empty struct. - if (NumLoadSubElements != 0 && - !DataflowContext.computeAvailableValues( - Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues)) - return false; - - // Ok, we have some available values. If we have a copy_addr, explode it now, - // exposing the load operation within it. Subsequent optimization passes will - // see the load and propagate the available values into it. - if (auto *CAI = dyn_cast(Inst)) { - DataflowContext.explodeCopyAddr(CAI); - - // This is removing the copy_addr, but explodeCopyAddr takes care of - // removing the instruction from Uses for us, so we return false. - return false; - } - - assert((isa(Inst) || isa(Inst)) && - "Unhandled instruction for this code path!"); + SILType loadTy = result->first; + unsigned firstElt = result->second; // Aggregate together all of the subelements into something that has the same // type as the load did, and emit smaller loads for any subelements that were // not available. We are "propagating" a +1 available value from the store // points. - auto *load = dyn_cast(Inst); - AvailableValueAggregator agg(load, AvailableValues, Uses, deadEndBlocks, - false /*isTake*/); - SILValue newVal = agg.aggregateValues(LoadTy, load->getOperand(0), FirstElt); + AvailableValueAggregator agg(li, availableValues, Uses, deadEndBlocks, + AvailableValueExpectedOwnership::Copy); + SILValue newVal = agg.aggregateValues(loadTy, li->getOperand(), firstElt); - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *load << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *li); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); + ++NumLoadPromoted; + + // If we did not have ownership, we did not insert extra copies at our stores, + // so we can just RAUW and return. + if (!li->getFunction()->hasOwnership()) { + li->replaceAllUsesWith(newVal); + SILValue addr = li->getOperand(); + li->eraseFromParent(); + if (auto *addrI = addr->getDefiningInstruction()) + eliminateDeadInstruction(addrI); + return true; + } // If we inserted any copies, we created the copies at our stores. We know // that in our load block, we will reform the aggregate as appropriate at the @@ -1607,26 +2130,120 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) { // blocks that we may have can be found by performing a linear lifetime check // over all copies that we found using the load as the "consuming uses" (just // for the purposes of identifying the consuming block). - auto *oldLoad = agg.addMissingDestroysForCopiedValues(load, newVal); + agg.fixupOwnership(li, newVal); + + // Now that we have fixed up all of our missing destroys, insert the copy + // value for our actual load and RAUW. + newVal = SILBuilderWithScope(li).emitCopyValueOperation(li->getLoc(), newVal); + + li->replaceAllUsesWith(newVal); + SILValue addr = li->getOperand(); + li->eraseFromParent(); + if (auto *addrI = addr->getDefiningInstruction()) + eliminateDeadInstruction(addrI); + return true; +} + +bool AllocOptimize::promoteCopyAddr(CopyAddrInst *cai) { + // Note that we intentionally don't support forwarding of weak pointers, + // because the underlying value may drop be deallocated at any time. We would + // have to prove that something in this function is holding the weak value + // live across the promoted region and that isn't desired for a stable + // diagnostics pass this like one. + + // First attempt to find a source addr for our "load" instruction. If we fail + // to find a valid value, just return. + SILValue srcAddr = tryFindSrcAddrForLoad(cai); + if (!srcAddr) + return false; + + SmallVector availableValues; + auto result = computeAvailableValues(srcAddr, cai, availableValues); + if (!result.hasValue()) + return false; + + // Ok, we have some available values. If we have a copy_addr, explode it now, + // exposing the load operation within it. Subsequent optimization passes will + // see the load and propagate the available values into it. + DataflowContext.explodeCopyAddr(cai); + + // This is removing the copy_addr, but explodeCopyAddr takes care of + // removing the instruction from Uses for us, so we return false. + return false; +} + +/// At this point, we know that this element satisfies the definitive init +/// requirements, so we can try to promote loads to enable SSA-based dataflow +/// analysis. We know that accesses to this element only access this element, +/// cross element accesses have been scalarized. +/// +/// This returns true if the load has been removed from the program. +bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) { + // Note that we intentionally don't support forwarding of weak pointers, + // because the underlying value may drop be deallocated at any time. We would + // have to prove that something in this function is holding the weak value + // live across the promoted region and that isn't desired for a stable + // diagnostics pass this like one. + + // First attempt to find a source addr for our "load" instruction. If we fail + // to find a valid value, just return. + SILValue srcAddr = tryFindSrcAddrForLoad(lbi); + if (!srcAddr) + return false; + + SmallVector availableValues; + auto result = computeAvailableValues(srcAddr, lbi, availableValues); + if (!result.hasValue()) + return false; ++NumLoadPromoted; - // If we are returned the load, eliminate it. Otherwise, it was already - // handled for us... so return true. - if (!oldLoad) - return true; + SILType loadTy = result->first; + unsigned firstElt = result->second; - // If our load was a +0 value, borrow the value and the RAUW. We reuse the - // end_borrows of our load_borrow. - if (isa(oldLoad)) { - newVal = SILBuilderWithScope(oldLoad).createBeginBorrow(oldLoad->getLoc(), - newVal); - } - oldLoad->replaceAllUsesWith(newVal); - SILValue addr = oldLoad->getOperand(0); - oldLoad->eraseFromParent(); + // Aggregate together all of the subelements into something that has the same + // type as the load did, and emit smaller loads for any subelements that were + // not available. We are "propagating" a +1 available value from the store + // points. + AvailableValueAggregator agg(lbi, availableValues, Uses, deadEndBlocks, + AvailableValueExpectedOwnership::Borrow); + SILValue newVal = agg.aggregateValues(loadTy, lbi->getOperand(), firstElt); + + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *lbi); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); + + // If we inserted any copies, we created the copies at our + // stores. We know that in our load block, we will reform the + // aggregate as appropriate, will borrow the value there and give us + // a whole pristine new value. Now in this routine, we go through + // all of the copies and phis that we inserted and ensure that: + // + // 1. Phis are always strongly control equivalent to the copies that + // produced their incoming values. + // + // 2. All intermediate copies are properly lifetime extended to the + // load block and all leaking blocks are filled in as appropriate + // with destroy_values. + agg.fixupOwnership(lbi, newVal); + + // Now that we have fixed up the lifetimes of all of our incoming copies so + // that they are alive over the load point, copy, borrow newVal and insert + // destroy_value after the end_borrow and then RAUW. + SILBuilderWithScope builder(lbi); + SILValue copiedVal = builder.emitCopyValueOperation(lbi->getLoc(), newVal); + newVal = builder.createBeginBorrow(lbi->getLoc(), copiedVal); + + for (auto *ebi : lbi->getUsersOfType()) { + auto next = std::next(ebi->getIterator()); + SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(), + copiedVal); + } + + lbi->replaceAllUsesWith(newVal); + SILValue addr = lbi->getOperand(); + lbi->eraseFromParent(); if (auto *addrI = addr->getDefiningInstruction()) - recursivelyDeleteTriviallyDeadInstructions(addrI); + eliminateDeadInstruction(addrI); return true; } @@ -1650,7 +2267,9 @@ bool AllocOptimize::canPromoteTake( // def/use behavior. unsigned firstElt = computeSubelement(address, TheMemory); assert(firstElt != ~0U && "destroy within enum projection is not valid"); - unsigned numLoadSubElements = getNumSubElements(loadTy, Module); + auto expansionContext = TypeExpansionContext(*inst->getFunction()); + unsigned numLoadSubElements = + getNumSubElements(loadTy, Module, expansionContext); // Find out if we have any available values. If no bits are demanded, we // trivially succeed. This can happen when there is a load of an empty struct. @@ -1674,7 +2293,7 @@ bool AllocOptimize::canPromoteTake( // available, we would need to split stores to promote this destroy_addr. We // do not support that yet. AvailableValueAggregator agg(inst, tmpList, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); if (!agg.canTake(loadTy, firstElt)) return false; @@ -1706,13 +2325,13 @@ void AllocOptimize::promoteDestroyAddr( // type as the load did, and emit smaller) loads for any subelements that were // not available. AvailableValueAggregator agg(dai, availableValues, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); SILValue newVal = agg.aggregateValues(loadTy, address, firstElt); ++NumDestroyAddrPromoted; - LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *dai << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *dai); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); SILBuilderWithScope(dai).emitDestroyValueOperation(dai->getLoc(), newVal); dai->eraseFromParent(); @@ -1734,13 +2353,13 @@ void AllocOptimize::promoteLoadTake( // type as the load did, and emit smaller) loads for any subelements that were // not available. AvailableValueAggregator agg(li, availableValues, Uses, deadEndBlocks, - true /*isTake*/); + AvailableValueExpectedOwnership::Take); SILValue newVal = agg.aggregateValues(loadTy, address, firstElt); ++NumLoadTakePromoted; - LLVM_DEBUG(llvm::dbgs() << " *** Promoting load_take: " << *li << "\n"); - LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n"); + LLVM_DEBUG(llvm::dbgs() << " *** Promoting load_take: " << *li); + LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal); // Then perform the RAUW. li->replaceAllUsesWith(newVal); @@ -1954,9 +2573,28 @@ bool AllocOptimize::optimizeMemoryAccesses() { auto &use = Uses[i]; // Ignore entries for instructions that got expanded along the way. if (use.Inst && use.Kind == PMOUseKind::Load) { - if (promoteLoadCopy(use.Inst)) { - Uses[i].Inst = nullptr; // remove entry if load got deleted. - changed = true; + if (auto *cai = dyn_cast(use.Inst)) { + if (promoteCopyAddr(cai)) { + Uses[i].Inst = nullptr; // remove entry if load got deleted. + changed = true; + } + continue; + } + + if (auto *lbi = dyn_cast(use.Inst)) { + if (promoteLoadBorrow(lbi)) { + Uses[i].Inst = nullptr; // remove entry if load got deleted. + changed = true; + } + continue; + } + + if (auto *li = dyn_cast(use.Inst)) { + if (promoteLoadCopy(li)) { + Uses[i].Inst = nullptr; // remove entry if load got deleted. + changed = true; + } + continue; } } } @@ -1998,8 +2636,8 @@ static bool optimizeMemoryAccesses(SILFunction &fn) { continue; } - LLVM_DEBUG(llvm::dbgs() << "*** PMO Optimize Memory Accesses looking at: " - << *alloc << "\n"); + LLVM_DEBUG(llvm::dbgs() + << "*** PMO Optimize Memory Accesses looking at: " << *alloc); PMOMemoryObjectInfo memInfo(alloc); // Set up the datastructure used to collect the uses of the allocation. @@ -2041,8 +2679,8 @@ static bool eliminateDeadAllocations(SILFunction &fn) { } LLVM_DEBUG(llvm::dbgs() - << "*** PMO Dead Allocation Elimination looking at: " << *alloc - << "\n"); + << "*** PMO Dead Allocation Elimination looking at: " + << *alloc); PMOMemoryObjectInfo memInfo(alloc); // Set up the datastructure used to collect the uses of the allocation. diff --git a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp index eb4c12f4d990f..e13226fd4d6c5 100644 --- a/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp +++ b/lib/SILOptimizer/Mandatory/SILGenCleanup.cpp @@ -52,14 +52,12 @@ struct SILGenCanonicalize final : CanonicalizeInstruction { SILInstruction *deadOperInst = *deadOperands.begin(); // Make sure at least the first instruction is removed from the set. deadOperands.erase(deadOperInst); - recursivelyDeleteTriviallyDeadInstructions( - deadOperInst, false, - [&](SILInstruction *deadInst) { - LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); - if (nextII == deadInst->getIterator()) - ++nextII; - deadOperands.erase(deadInst); - }); + eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) { + LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst); + if (nextII == deadInst->getIterator()) + ++nextII; + deadOperands.erase(deadInst); + }); } return nextII; } diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 72fa8368a7fba..9915714f5c575 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -14,7 +14,7 @@ #include "swift/Basic/BlotSetVector.h" #include "swift/Basic/STLExtras.h" #include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/BranchPropagatedUser.h" +#include "swift/SIL/DebugUtils.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/SILArgument.h" @@ -25,6 +25,7 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -36,19 +37,49 @@ STATISTIC(NumLoadCopyConvertedToLoadBorrow, "number of load_copy converted to load_borrow"); //===----------------------------------------------------------------------===// -// Utility +// Live Range Modeling //===----------------------------------------------------------------------===// -/// Return true if v only has invalidating uses that are destroy_value. Such an -/// owned value is said to represent a dead "live range". -/// -/// Semantically this implies that a value is never passed off as +1 to memory -/// or another function implying it can be used everywhere at +0. -static bool isDeadLiveRange( - SILValue v, SmallVectorImpl &destroys, - NullablePtr> forwardingInsts = nullptr) { - assert(v.getOwnershipKind() == ValueOwnershipKind::Owned); - SmallVector worklist(v->use_begin(), v->use_end()); +namespace { + +class LiveRange { + /// A list of destroy_values of the live range. + SmallVector destroys; + + /// A list of forwarding instructions that forward our destroys ownership, but + /// that are also able to forward guaranteed ownership. + SmallVector generalForwardingInsts; + + /// Consuming users that we were not able to understand as a forwarding + /// instruction or a destroy_value. These must be passed a strongly control + /// equivalent +1 value. + SmallVector unknownConsumingUsers; + +public: + LiveRange(SILValue value); + + LiveRange(const LiveRange &) = delete; + LiveRange &operator=(const LiveRange &) = delete; + + /// Return true if v only has invalidating uses that are destroy_value. Such + /// an owned value is said to represent a dead "live range". + /// + /// Semantically this implies that a value is never passed off as +1 to memory + /// or another function implying it can be used everywhere at +0. + bool hasConsumingUse() const { return unknownConsumingUsers.size(); } + + ArrayRef getDestroys() const { return destroys; } + ArrayRef getNonConsumingForwardingInsts() const { + return generalForwardingInsts; + } +}; + +} // end anonymous namespace + +LiveRange::LiveRange(SILValue value) + : destroys(), generalForwardingInsts(), unknownConsumingUsers() { + SmallVector worklist(value->getUses()); + while (!worklist.empty()) { auto *op = worklist.pop_back_val(); @@ -84,21 +115,21 @@ static bool isDeadLiveRange( // // NOTE: Today we do not support TermInsts for simplicity... we /could/ // support it though if we need to. - if (forwardingInsts.isNull() || isa(user) || - !isGuaranteedForwardingInst(user) || + if (isa(user) || !isGuaranteedForwardingInst(user) || 1 != count_if(user->getOperandValues( true /*ignore type dependent operands*/), [&](SILValue v) { return v.getOwnershipKind() == ValueOwnershipKind::Owned; })) { - return false; + unknownConsumingUsers.push_back(user); + continue; } // Ok, this is a forwarding instruction whose ownership we can flip from // owned -> guaranteed. Visit its users recursively to see if the the // users force the live range to be alive. - forwardingInsts.get()->push_back(user); + generalForwardingInsts.push_back(user); for (SILValue v : user->getResults()) { if (v.getOwnershipKind() != ValueOwnershipKind::Owned) continue; @@ -117,10 +148,6 @@ static bool isDeadLiveRange( continue; } } - - // We visited all of our users and were able to prove that all of them were - // benign. Return true. - return true; } //===----------------------------------------------------------------------===// @@ -148,12 +175,16 @@ struct SemanticARCOptVisitor /// Our main worklist. We use this after an initial run through. SmallBlotSetVector worklist; - /// A secondary work list that we use to store dead trivial instructions to - /// delete after we are done processing the worklist. - SmallBlotSetVector deadTrivialInsts; + /// A set of values that we have visited since the last mutation. We use this + /// to ensure that we do not visit values twice without mutating. + /// + /// This is specifically to ensure that we do not go into an infinite loop + /// when visiting phi nodes. + SmallBlotSetVector visitedSinceLastMutation; SILFunction &F; Optional TheDeadEndBlocks; + ValueLifetimeAnalysis::Frontier lifetimeFrontier; explicit SemanticARCOptVisitor(SILFunction &F) : F(F) {} @@ -167,8 +198,13 @@ struct SemanticARCOptVisitor /// the worklist, and then call eraseInstruction on i. void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, SILValue newValue) { worklist.insert(newValue); + for (auto *use : i->getUses()) { + for (SILValue result : use->getUser()->getResults()) { + worklist.insert(result); + } + } i->replaceAllUsesWith(newValue); - eraseInstruction(i); + eraseInstructionAndAddOperandsToWorklist(i); } /// Add all operands of i to the worklist and then call eraseInstruction on @@ -189,18 +225,88 @@ struct SemanticARCOptVisitor // the instruction. for (SILValue result : i->getResults()) { worklist.erase(result); + visitedSinceLastMutation.erase(result); } - deadTrivialInsts.erase(i); i->eraseFromParent(); + + // Add everything else from visitedSinceLastMutation to the worklist. + for (auto opt : visitedSinceLastMutation) { + if (!opt.hasValue()) { + continue; + } + worklist.insert(*opt); + } + visitedSinceLastMutation.clear(); } /// The default visitor. - bool visitSILInstruction(SILInstruction *i) { return false; } + bool visitSILInstruction(SILInstruction *i) { + assert(!isGuaranteedForwardingInst(i) && + "Should have forwarding visitor for all ownership forwarding " + "instructions"); + return false; + } + bool visitCopyValueInst(CopyValueInst *cvi); bool visitBeginBorrowInst(BeginBorrowInst *bbi); bool visitLoadInst(LoadInst *li); + static bool shouldVisitInst(SILInstruction *i) { + switch (i->getKind()) { + default: + return false; + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::LoadInst: + return true; + } + } - bool isWrittenTo(LoadInst *li); +#define FORWARDING_INST(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (SILValue v : cls->getResults()) { \ + worklist.insert(v); \ + } \ + return false; \ + } + FORWARDING_INST(Tuple) + FORWARDING_INST(Struct) + FORWARDING_INST(Enum) + FORWARDING_INST(OpenExistentialRef) + FORWARDING_INST(Upcast) + FORWARDING_INST(UncheckedRefCast) + FORWARDING_INST(ConvertFunction) + FORWARDING_INST(RefToBridgeObject) + FORWARDING_INST(BridgeObjectToRef) + FORWARDING_INST(UnconditionalCheckedCast) + FORWARDING_INST(UncheckedEnumData) + FORWARDING_INST(MarkUninitialized) + FORWARDING_INST(SelectEnum) + FORWARDING_INST(DestructureStruct) + FORWARDING_INST(DestructureTuple) + FORWARDING_INST(TupleExtract) + FORWARDING_INST(StructExtract) + FORWARDING_INST(OpenExistentialValue) + FORWARDING_INST(OpenExistentialBoxValue) + FORWARDING_INST(MarkDependence) +#undef FORWARDING_INST + +#define FORWARDING_TERM(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ + for (SILValue v : succValues) { \ + worklist.insert(v); \ + } \ + } \ + return false; \ + } + + FORWARDING_TERM(SwitchEnum) + FORWARDING_TERM(CheckedCastBranch) + FORWARDING_TERM(Branch) + FORWARDING_TERM(CondBranch) +#undef FORWARDING_TERM + + bool isWrittenTo(LoadInst *li, const LiveRange &lr); bool processWorklist(); @@ -225,6 +331,14 @@ bool SemanticARCOptVisitor::processWorklist() { if (!next) continue; + // First check if this is a value that we have visited since the last time + // we erased an instruction. If we have visited it, skip it. Every time we + // modify something, we should be deleting an instruction, so we have not + // found any further information. + if (!visitedSinceLastMutation.insert(next).second) { + continue; + } + // First check if this is an instruction that is trivially dead. This can // occur if we eliminate rr traffic resulting in dead projections and the // like. @@ -234,7 +348,8 @@ bool SemanticARCOptVisitor::processWorklist() { // the instruction). if (auto *defInst = next->getDefiningInstruction()) { if (isInstructionTriviallyDead(defInst)) { - deadTrivialInsts.insert(defInst); + deleteAllDebugUses(defInst); + eraseInstruction(defInst); continue; } } @@ -247,23 +362,6 @@ bool SemanticARCOptVisitor::processWorklist() { } } - // Then eliminate the rest of the dead trivial insts. - // - // NOTE: We do not need to touch the worklist here since it is guaranteed to - // be empty due to the loop above. We enforce this programatically with the - // assert. - assert(worklist.empty() && "Expected drained worklist so we don't have to " - "remove dead insts form it"); - while (!deadTrivialInsts.empty()) { - auto val = deadTrivialInsts.pop_back_val(); - if (!val) - continue; - recursivelyDeleteTriviallyDeadInstructions( - *val, true /*force*/, - [&](SILInstruction *i) { deadTrivialInsts.erase(i); }); - madeChange = true; - } - return madeChange; } @@ -299,6 +397,50 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { return true; } +static void convertForwardingInstsFromOwnedToGuaranteed( + ArrayRef guaranteedForwardingInsts) { + // Then change all of our guaranteed forwarding insts to have guaranteed + // ownership kind instead of what ever they previously had (ignoring trivial + // results); + while (!guaranteedForwardingInsts.empty()) { + auto *i = guaranteedForwardingInsts.back(); + guaranteedForwardingInsts = guaranteedForwardingInsts.drop_back(); + assert(i->hasResults()); + + for (SILValue result : i->getResults()) { + if (auto *svi = dyn_cast(result)) { + if (svi->getOwnershipKind() == ValueOwnershipKind::Owned) { + svi->setOwnershipKind(ValueOwnershipKind::Guaranteed); + } + continue; + } + + if (auto *ofci = dyn_cast(result)) { + if (ofci->getOwnershipKind() == ValueOwnershipKind::Owned) { + ofci->setOwnershipKind(ValueOwnershipKind::Guaranteed); + } + continue; + } + + if (auto *sei = dyn_cast(result)) { + if (sei->getOwnershipKind() == ValueOwnershipKind::Owned) { + sei->setOwnershipKind(ValueOwnershipKind::Guaranteed); + } + continue; + } + + if (auto *mvir = dyn_cast(result)) { + if (mvir->getOwnershipKind() == ValueOwnershipKind::Owned) { + mvir->setOwnershipKind(ValueOwnershipKind::Guaranteed); + } + continue; + } + + llvm_unreachable("unhandled forwarding instruction?!"); + } + } +} + // Eliminate a copy of a borrowed value, if: // // 1. All of the copies users do not consume the copy (and thus can accept a @@ -346,9 +488,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // must be some consuming use that we either do not understand is /actually/ // forwarding or a user that truly represents a necessary consume of the // value (e.x. storing into memory). - SmallVector destroys; - SmallVector guaranteedForwardingInsts; - if (!isDeadLiveRange(cvi, destroys, &guaranteedForwardingInsts)) + LiveRange lr(cvi); + if (lr.hasConsumingUse()) return false; // Next check if we do not have any destroys of our copy_value and are @@ -406,31 +547,44 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // dead end blocks that use the value in a non-consuming way. // // TODO: There may be some way of sinking this into the loop below. - if (destroys.empty() && - llvm::any_of(borrowScopeIntroducers, - [](BorrowScopeIntroducingValue borrowScope) { - return borrowScope.isLocalScope(); - })) { + bool haveAnyLocalScopes = llvm::any_of( + borrowScopeIntroducers, [](BorrowScopeIntroducingValue borrowScope) { + return borrowScope.isLocalScope(); + }); + + auto destroys = lr.getDestroys(); + if (destroys.empty() && haveAnyLocalScopes) { return false; } // If we reached this point, then we know that all of our users can accept a - // guaranteed value and our owned value is destroyed only by - // destroy_value. Check if all of our destroys are joint post-dominated by the - // our end borrow scope set. If they do not, then the copy_value is lifetime - // extending the guaranteed value, we can not eliminate it. + // guaranteed value and our owned value is destroyed only by a set of + // destroy_values. Check if: + // + // 1. All of our destroys are joint post-dominated by our end borrow scope + // set. If they do not, then the copy_value is lifetime extending the + // guaranteed value, we can not eliminate it. + // + // 2. If all of our destroy_values are dead end. In such a case, the linear + // lifetime checker will not perform any checks since it assumes that dead + // end destroys can be ignored. Since we are going to end the program + // anyways, we want to be conservative here and optimize only if we do not + // need to insert an end_borrow since all of our borrow introducers are + // non-local scopes. { - SmallVector destroysForLinearLifetimeCheck; + bool foundNonDeadEnd = false; for (auto *dvi : destroys) { - destroysForLinearLifetimeCheck.push_back(&dvi->getAllOperands()[0]); + foundNonDeadEnd |= !getDeadEndBlocks().isDeadEnd(dvi->getParent()); } - SmallVector scratchSpace; + if (!foundNonDeadEnd && haveAnyLocalScopes) + return false; + SmallVector scratchSpace; SmallPtrSet visitedBlocks; if (llvm::any_of(borrowScopeIntroducers, [&](BorrowScopeIntroducingValue borrowScope) { return !borrowScope.areInstructionsWithinScope( - destroysForLinearLifetimeCheck, scratchSpace, - visitedBlocks, getDeadEndBlocks()); + destroys, scratchSpace, visitedBlocks, + getDeadEndBlocks()); })) { return false; } @@ -439,53 +593,16 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // Otherwise, we know that our copy_value/destroy_values are all completely // within the guaranteed value scope. First delete the destroys/copies. while (!destroys.empty()) { - auto *dvi = destroys.pop_back_val(); + auto *dvi = destroys.back(); + destroys = destroys.drop_back(); eraseInstruction(dvi); ++NumEliminatedInsts; } eraseAndRAUWSingleValueInstruction(cvi, cvi->getOperand()); + convertForwardingInstsFromOwnedToGuaranteed( + lr.getNonConsumingForwardingInsts()); - // Then change all of our guaranteed forwarding insts to have guaranteed - // ownership kind instead of what ever they previously had (ignoring trivial - // results); - while (!guaranteedForwardingInsts.empty()) { - auto *i = guaranteedForwardingInsts.pop_back_val(); - - assert(i->hasResults()); - - for (SILValue result : i->getResults()) { - if (auto *svi = dyn_cast(result)) { - if (svi->getOwnershipKind() == ValueOwnershipKind::Owned) { - svi->setOwnershipKind(ValueOwnershipKind::Guaranteed); - } - continue; - } - - if (auto *ofci = dyn_cast(result)) { - if (ofci->getOwnershipKind() == ValueOwnershipKind::Owned) { - ofci->setOwnershipKind(ValueOwnershipKind::Guaranteed); - } - continue; - } - - if (auto *sei = dyn_cast(result)) { - if (sei->getOwnershipKind() == ValueOwnershipKind::Owned) { - sei->setOwnershipKind(ValueOwnershipKind::Guaranteed); - } - continue; - } - - if (auto *mvir = dyn_cast(result)) { - if (mvir->getOwnershipKind() == ValueOwnershipKind::Owned) { - mvir->setOwnershipKind(ValueOwnershipKind::Guaranteed); - } - continue; - } - - llvm_unreachable("unhandled forwarding instruction?!"); - } - } ++NumEliminatedInsts; return true; } @@ -532,12 +649,14 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // If our copy value inst has only destroy_value users, it is a dead live // range. Try to eliminate them. - if (eliminateDeadLiveRangeCopyValue(cvi)) + if (eliminateDeadLiveRangeCopyValue(cvi)) { return true; + } // Then try to perform the guaranteed copy value optimization. - if (performGuaranteedCopyValueOptimization(cvi)) + if (performGuaranteedCopyValueOptimization(cvi)) { return true; + } return false; } @@ -546,53 +665,32 @@ bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) { // load [copy] Optimizations //===----------------------------------------------------------------------===// -// A flow insensitive analysis that tells the load [copy] analysis if the -// storage has 0, 1, >1 writes to it. -// -// In the case of 0 writes, we return CanOptimizeLoadCopyResult::Always. -// -// In the case of 1 write, we return OnlyIfStorageIsLocal. We are taking -// advantage of definite initialization implying that an alloc_stack must be -// written to once before any loads from the memory location. Thus if we are -// local and see 1 write, we can still change to load_borrow if all other uses -// check out. -// -// If there is 2+ writes, we can not optimize = (. - -bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) { - auto *arg = cast(storage.getArgument()); - - // Then check if we have an in_guaranteed argument. In this case, we can - // always optimize load [copy] from this. - if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) - return false; - - // For now just return false. - return true; -} - -// Then find our accessed storage to determine whether it provides a guarantee -// for the loaded value. namespace { + +/// A class that computes in a flow insensitive way if we can prove that our +/// storage is either never written to, or is initialized exactly once and never +/// written to again. In both cases, we can convert load [copy] -> load_borrow +/// safely. class StorageGuaranteesLoadVisitor : public AccessUseDefChainVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; - - // The original load instruction. - LoadInst *Load; - + + // The live range of the original load. + const LiveRange &liveRange; + // The current address being visited. SILValue currentAddress; Optional isWritten; - + public: - StorageGuaranteesLoadVisitor(SemanticARCOptVisitor &arcOpt, LoadInst *load) - : ARCOpt(arcOpt), Load(load), currentAddress(load->getOperand()) - {} - + StorageGuaranteesLoadVisitor(SemanticARCOptVisitor &arcOpt, LoadInst *load, + const LiveRange &liveRange) + : ARCOpt(arcOpt), liveRange(liveRange), + currentAddress(load->getOperand()) {} + void answer(bool written) { currentAddress = nullptr; isWritten = written; @@ -608,9 +706,21 @@ class StorageGuaranteesLoadVisitor } void visitArgumentAccess(SILFunctionArgument *arg) { - return answer(mayFunctionMutateArgument( - AccessedStorage(arg, AccessedStorage::Argument), - ARCOpt.F)); + // If this load_copy is from an indirect in_guaranteed argument, then we + // know for sure that it will never be written to. + if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) { + return answer(false); + } + + // TODO: This should be extended: + // + // 1. We should be able to analyze inout arguments and see if the inout + // argument is never actually written to in a flow insensitive way. + // + // 2. We should be able to analyze in arguments and see if they are only + // ever destroyed at the end of the function. In such a case, we may be + // able to also to promote load [copy] from such args to load_borrow. + return answer(true); } void visitGlobalAccess(SILValue global) { @@ -658,26 +768,15 @@ class StorageGuaranteesLoadVisitor // Use the linear lifetime checker to check whether the copied // value is dominated by the lifetime of the borrow it's based on. - SmallVector baseEndBorrows; - for (auto *use : borrowInst->getUses()) { - if (isa(use->getUser())) { - baseEndBorrows.emplace_back(use); - } - } - - SmallVector valueDestroys; - for (auto *use : Load->getUses()) { - if (isa(use->getUser())) { - valueDestroys.emplace_back(use); - } - } - - SmallPtrSet visitedBlocks; + SmallVector baseEndBorrows; + llvm::copy(borrowInst->getUsersOfType(), + std::back_inserter(baseEndBorrows)); + SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); // Returns true on success. So we invert. - bool foundError = - !checker.validateLifetime(baseObject, baseEndBorrows, valueDestroys); + bool foundError = !checker.validateLifetime(baseObject, baseEndBorrows, + liveRange.getDestroys()); return answer(foundError); } @@ -700,6 +799,25 @@ class StorageGuaranteesLoadVisitor return answer(true); } + /// See if we have an alloc_stack that is only written to once by an + /// initializing instruction. + void visitStackAccess(AllocStackInst *stack) { + SmallVector destroyAddrs; + bool initialAnswer = isSingleInitAllocStack(stack, destroyAddrs); + if (!initialAnswer) + return answer(true); + + // Then make sure that all of our load [copy] uses are within the + // destroy_addr. + SmallPtrSet visitedBlocks; + LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); + // Returns true on success. So we invert. + bool foundError = !checker.validateLifetime( + stack, destroyAddrs /*consuming users*/, + liveRange.getDestroys() /*non consuming users*/); + return answer(foundError); + } + bool doIt() { while (currentAddress) { visit(currentAddress); @@ -707,10 +825,11 @@ class StorageGuaranteesLoadVisitor return *isWritten; } }; + } // namespace -bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load) { - StorageGuaranteesLoadVisitor visitor(*this, load); +bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load, const LiveRange &lr) { + StorageGuaranteesLoadVisitor visitor(*this, load, lr); return visitor.doIt(); } @@ -727,14 +846,14 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // FIXME: We should consider if it is worth promoting a load [copy] // -> load_borrow if we can put a copy_value on a cold path and thus // eliminate RR traffic on a hot path. - SmallVector destroyValues; - if (!isDeadLiveRange(li, destroyValues)) + LiveRange lr(li); + if (lr.hasConsumingUse()) return false; // Then check if our address is ever written to. If it is, then we cannot use // the load_borrow because the stored value may be released during the loaded // value's live range. - if (isWrittenTo(li)) + if (isWrittenTo(li, lr)) return false; // Ok, we can perform our optimization. Convert the load [copy] into a @@ -746,14 +865,37 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // parameters, we can have multiple destroy_value along the same path. We need // to find the post-dominating block set of these destroy value to ensure that // we do not insert multiple end_borrow. + assert(lifetimeFrontier.empty()); + auto destroyValues = lr.getDestroys(); + ValueLifetimeAnalysis analysis(li, destroyValues); + bool foundCriticalEdges = !analysis.computeFrontier( + lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG, + &getDeadEndBlocks()); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + auto loc = RegularLocation::getAutoGeneratedLocation(); + while (!lifetimeFrontier.empty()) { + auto *insertPoint = lifetimeFrontier.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createEndBorrow(loc, lbi); + } + + // Then delete all of our destroy_value. while (!destroyValues.empty()) { - auto *dvi = destroyValues.pop_back_val(); - SILBuilderWithScope(dvi).createEndBorrow(dvi->getLoc(), lbi); + auto *dvi = destroyValues.back(); + destroyValues = destroyValues.drop_back(); eraseInstruction(dvi); ++NumEliminatedInsts; } + // RAUW our other uses from the load to the load_borrow. eraseAndRAUWSingleValueInstruction(li, lbi); + + // And then change the ownership all of our owned forwarding users to be + // guaranteed. + convertForwardingInstsFromOwnedToGuaranteed( + lr.getNonConsumingForwardingInsts()); + ++NumEliminatedInsts; ++NumLoadCopyConvertedToLoadBorrow; return true; @@ -772,64 +914,33 @@ struct SemanticARCOpts : SILFunctionTransform { void run() override { SILFunction &f = *getFunction(); + // Return early if we are not performing OSSA optimizations. + if (!f.getModule().getOptions().EnableOSSAOptimizations) + return; + // Make sure we are running with ownership verification enabled. assert(f.getModule().getOptions().VerifySILOwnership && "Can not perform semantic arc optimization unless ownership " "verification is enabled"); - // Iterate over all of the arguments, performing small peephole ARC - // optimizations. We assume that the visitor will add any instructions we - // need to recursively to the visitor's worklist. Also, note that we assume - // that we do not look through /any/ sil block arguments here since our - // iteration here is only valid up to SSA. - bool madeChange = false; - SemanticARCOptVisitor visitor(f); - - for (auto &bb : f) { - auto ii = bb.rend(); - auto start = bb.rbegin(); - - // If the bb is empty, continue. - if (start == ii) - continue; - // Go to the first instruction to process. - --ii; - - // Then until we process the first instruction of the block... - while (ii != start) { - // Move the iterator before ii. - auto tmp = std::next(ii); - - // Then try to optimize. If we succeeded, then we deleted - // ii. Move ii from the next value back onto the instruction - // after ii's old value in the block instruction list and then - // process that. - if (visitor.visit(&*ii)) { - madeChange = true; - ii = std::prev(tmp); - continue; + // Add all the results of all instructions that we want to visit to the + // worklist. + for (auto &block : f) { + for (auto &inst : block) { + if (SemanticARCOptVisitor::shouldVisitInst(&inst)) { + for (SILValue v : inst.getResults()) { + visitor.worklist.insert(v); + } } - - // Otherwise, we didn't delete ii. Just visit the next instruction. - --ii; } - - // Finally visit the first instruction of the block. - madeChange |= visitor.visit(&*ii); } - // Finally drain the worklist on the visitor and process until we reach the - // fixpoint and thus do not have any further work to do. - // - // NOTE: At this point madeChange has already been set to true if we have - // anything in the worklist, so technically we do not need to do this. But I - // would rather represent this state to future proof the pass to be - // "visually" correct. - madeChange |= visitor.processWorklist(); - - if (madeChange) { + // Then process the worklist. We only destroy instructions, so invalidate + // that. Once we modify the ownership of block arguments, we will need to + // perhaps invalidate branches as well. + if (visitor.processWorklist()) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } } diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 714c1206a2b86..ef2e70b71957f 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -58,8 +58,8 @@ llvm::cl::opt SILBreakOnPass( "sil-break-on-pass", llvm::cl::init(""), llvm::cl::desc("Break before running a particular function pass")); -llvm::cl::opt - SILPrintOnlyFun("sil-print-only-function", llvm::cl::init(""), +llvm::cl::list + SILPrintOnlyFun("sil-print-only-function", llvm::cl::CommaSeparated, llvm::cl::desc("Only print out the sil for this function")); llvm::cl::opt @@ -144,7 +144,8 @@ static llvm::cl::optgetName() != SILPrintOnlyFun) + if (!SILPrintOnlyFun.empty() && F && SILPrintOnlyFun.end() == + std::find(SILPrintOnlyFun.begin(), SILPrintOnlyFun.end(), F->getName())) return false; if (!SILPrintOnlyFuns.empty() && F && @@ -168,7 +169,8 @@ static bool doPrintBefore(SILTransform *T, SILFunction *F) { } static bool doPrintAfter(SILTransform *T, SILFunction *F, bool Default) { - if (!SILPrintOnlyFun.empty() && F && F->getName() != SILPrintOnlyFun) + if (!SILPrintOnlyFun.empty() && F && SILPrintOnlyFun.end() == + std::find(SILPrintOnlyFun.begin(), SILPrintOnlyFun.end(), F->getName())) return false; if (!SILPrintOnlyFuns.empty() && F && @@ -207,7 +209,8 @@ static void printModule(SILModule *Mod, bool EmitVerboseSIL) { return; } for (auto &F : *Mod) { - if (!SILPrintOnlyFun.empty() && F.getName().str() == SILPrintOnlyFun) + if (!SILPrintOnlyFun.empty() && SILPrintOnlyFun.end() != + std::find(SILPrintOnlyFun.begin(), SILPrintOnlyFun.end(), F.getName())) F.dump(EmitVerboseSIL); if (!SILPrintOnlyFuns.empty() && @@ -273,8 +276,8 @@ class PassManagerDeserializationNotificationHandler final } // end anonymous namespace SILPassManager::SILPassManager(SILModule *M, llvm::StringRef Stage, - bool isMandatoryPipeline) - : Mod(M), StageName(Stage), isMandatoryPipeline(isMandatoryPipeline), + bool isMandatory) + : Mod(M), StageName(Stage), isMandatory(isMandatory), deserializationNotificationHandler(nullptr) { #define ANALYSIS(NAME) \ Analyses.push_back(create##NAME##Analysis(Mod)); @@ -292,13 +295,13 @@ SILPassManager::SILPassManager(SILModule *M, llvm::StringRef Stage, } SILPassManager::SILPassManager(SILModule *M, irgen::IRGenModule *IRMod, - llvm::StringRef Stage, bool isMandatoryPipeline) - : SILPassManager(M, Stage, isMandatoryPipeline) { + llvm::StringRef Stage, bool isMandatory) + : SILPassManager(M, Stage, isMandatory) { this->IRMod = IRMod; } bool SILPassManager::continueTransforming() { - if (isMandatoryPipeline) + if (isMandatory) return true; return NumPassesRun < SILNumOptPassesToRun; } @@ -467,7 +470,7 @@ runFunctionPasses(unsigned FromTransIdx, unsigned ToTransIdx) { // Only include functions that are definitions, and which have not // been intentionally excluded from optimization. - if (F.isDefinition() && (isMandatoryPipeline || F.shouldOptimize())) + if (F.isDefinition() && (isMandatory || F.shouldOptimize())) FunctionWorklist.push_back(*I); } @@ -675,8 +678,8 @@ void SILPassManager::notifyOfNewFunction(SILFunction *F, SILTransform *T) { void SILPassManager::addFunctionToWorklist(SILFunction *F, SILFunction *DerivedFrom) { - assert(F && F->isDefinition() && (isMandatoryPipeline || F->shouldOptimize()) - && "Expected optimizable function definition!"); + assert(F && F->isDefinition() && (isMandatory || F->shouldOptimize()) && + "Expected optimizable function definition!"); constexpr int MaxDeriveLevels = 10; diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 79974aaa9dbb2..cf9775bed4ed2 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -74,7 +74,6 @@ static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) { /// Passes for performing definite initialization. Must be run together in this /// order. static void addDefiniteInitialization(SILPassPipelinePlan &P) { - P.addMarkUninitializedFixup(); P.addDefiniteInitialization(); P.addRawSILInstLowering(); } @@ -258,7 +257,7 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) { P.addCOWArrayOpts(); // Cleanup. P.addDCE(); - P.addSwiftArrayOpts(); + P.addSwiftArrayPropertyOpt(); } // Perform classic SSA optimizations. @@ -292,10 +291,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { // Mainly for Array.append(contentsOf) optimization. P.addArrayElementPropagation(); - // Specialize opaque archetypes. - // This can expose oportunities for the generic specializer. - P.addOpaqueArchetypeSpecializer(); - // Run the devirtualizer, specializer, and inliner. If any of these // makes a change we'll end up restarting the function passes on the // current function (after optimizing any new callees). @@ -401,6 +396,8 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { // we do not spend time optimizing them. P.addDeadFunctionElimination(); + P.addSemanticARCOpts(); + // Strip ownership from non-transparent functions. if (P.getOptions().StripOwnershipAfterSerialization) P.addNonTransparentFunctionOwnershipModelEliminator(); @@ -413,6 +410,14 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { // Add the outliner pass (Osize). P.addOutliner(); + + P.addCrossModuleSerializationSetup(); + + // In case of cross-module-optimization, we need to serialize right after + // CrossModuleSerializationSetup. Eventually we want to serialize early + // anyway, but for now keep the SerializeSILPass at the later stage of the + // pipeline in case cross-module-optimization is not enabled. + P.addCMOSerializeSILPass(); } static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) { @@ -483,7 +488,9 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { P.addStackPromotion(); // Speculate virtual call targets. - P.addSpeculativeDevirtualization(); + if (P.getOptions().EnableSpeculativeDevirtualization) { + P.addSpeculativeDevirtualization(); + } // There should be at least one SILCombine+SimplifyCFG between the // ClosureSpecializer, etc. and the last inliner. Cleaning up after these @@ -515,7 +522,9 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) { P.startPipeline("LateLoopOpt"); // Delete dead code and drop the bodies of shared functions. - P.addDeadFunctionElimination(); + // Also, remove externally available witness tables. They are not needed + // anymore after the last devirtualizer run. + P.addLateDeadFunctionElimination(); // Perform the final lowering transformations. P.addCodeSinking(); @@ -575,6 +584,7 @@ SILPassPipelinePlan SILPassPipelinePlan::getLoweringPassPipeline(const SILOptions &Options) { SILPassPipelinePlan P(Options); P.startPipeline("Address Lowering"); + P.addOwnershipModelEliminator(); P.addIRGenPrepare(); P.addAddressLowering(); @@ -687,6 +697,20 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { return P; } +//===----------------------------------------------------------------------===// +// Serialize SIL Pass Pipeline +//===----------------------------------------------------------------------===// + +// Add to P a new pipeline that just serializes SIL. Meant to be used in +// situations where perf optzns are disabled, but we may need to serialize. +SILPassPipelinePlan +SILPassPipelinePlan::getSerializeSILPassPipeline(const SILOptions &Options) { + SILPassPipelinePlan P(Options); + P.startPipeline("Serialize SIL"); + P.addSerializeSILPass(); + return P; +} + //===----------------------------------------------------------------------===// // Inst Count Pass Pipeline //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/PassManager/Passes.cpp b/lib/SILOptimizer/PassManager/Passes.cpp index ce715dbd65864..948755c8a37f1 100644 --- a/lib/SILOptimizer/PassManager/Passes.cpp +++ b/lib/SILOptimizer/PassManager/Passes.cpp @@ -96,12 +96,27 @@ void swift::runSILOptimizationPasses(SILModule &Module) { if (Module.getOptions().VerifyAll) Module.verify(); - if (Module.getOptions().DisableSILPerfOptimizations) + if (Module.getOptions().DisableSILPerfOptimizations) { + // If we are not supposed to run SIL perf optzns, we may still need to + // serialize. So serialize now. + SILPassManager PM(&Module, "" /*stage*/, true /*isMandatory*/); + PM.executePassPipelinePlan( + SILPassPipelinePlan::getSerializeSILPassPipeline(Module.getOptions())); return; + } - SILPassManager PM(&Module); - PM.executePassPipelinePlan( - SILPassPipelinePlan::getPerformancePassPipeline(Module.getOptions())); + { + SILPassManager PM(&Module); + PM.executePassPipelinePlan( + SILPassPipelinePlan::getPerformancePassPipeline(Module.getOptions())); + } + + // Check if we actually serialized our module. If we did not, serialize now. + if (!Module.isSerialized()) { + SILPassManager PM(&Module, "" /*stage*/, true /*isMandatory*/); + PM.executePassPipelinePlan( + SILPassPipelinePlan::getSerializeSILPassPipeline(Module.getOptions())); + } // If we were asked to debug serialization, exit now. if (Module.getOptions().DebugSerialization) diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 609bfe79df3c0..fee896da9ad8f 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -236,8 +236,6 @@ class SILCombiner : // the result bit. SILInstruction *optimizeBuiltinCompareEq(BuiltinInst *AI, bool NegateResult); - SILInstruction *tryOptimizeApplyOfPartialApply(PartialApplyInst *PAI); - SILInstruction *optimizeApplyOfConvertFunctionInst(FullApplySite AI, ConvertFunctionInst *CFI); @@ -253,6 +251,15 @@ class SILCombiner : StringRef FInverseName, StringRef FName); private: + InstModCallbacks getInstModCallbacks() { + return InstModCallbacks( + [this](SILInstruction *DeadInst) { eraseInstFromFunction(*DeadInst); }, + [this](SILInstruction *NewInst) { Worklist.add(NewInst); }, + [this](SILValue oldValue, SILValue newValue) { + replaceValueUsesWith(oldValue, newValue); + }); + } + FullApplySite rewriteApplyCallee(FullApplySite apply, SILValue callee); // Build concrete existential information using findInitExistential. diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index 6f9af158319fe..3aac12abe25d9 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -16,6 +16,7 @@ #include "swift/AST/Module.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/Range.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/InstructionUtils.h" @@ -103,370 +104,13 @@ SILInstruction *SILCombiner::visitPartialApplyInst(PartialApplyInst *PAI) { if (foldInverseReabstractionThunks(PAI, this)) return nullptr; - tryOptimizeApplyOfPartialApply(PAI); + tryOptimizeApplyOfPartialApply(PAI, Builder, getInstModCallbacks()); // Try to delete dead closures. - tryDeleteDeadClosure( - PAI, InstModCallbacks( - [this](SILInstruction *DeadInst) { - eraseInstFromFunction(*DeadInst); - }, - [this](SILInstruction *NewInst) { Worklist.add(NewInst); })); + tryDeleteDeadClosure(PAI, getInstModCallbacks()); return nullptr; } -// Helper class performing the apply{partial_apply(x,y)}(z) -> apply(z,x,y) -// peephole. -class PartialApplyCombiner { - // True if temporaries are not created yet. - bool isFirstTime = true; - - // partial_apply which is being processed. - PartialApplyInst *PAI; - - // Temporaries created as copies of alloc_stack arguments of - // the partial_apply. - SmallVector Tmps; - - // Mapping from the original argument of partial_apply to - // the temporary containing its copy. - llvm::DenseMap ArgToTmp; - - // Set of lifetime endpoints for this partial_apply. - // - // Used to find the last uses of partial_apply, which is need to insert - // releases/destroys of temporaries as early as possible. - ValueLifetimeAnalysis::Frontier PAFrontier; - - SILBuilder &Builder; - - SILCombiner *SilCombiner; - - bool processSingleApply(FullApplySite AI); - bool allocateTemporaries(); - void deallocateTemporaries(); - void releaseTemporaries(); - -public: - PartialApplyCombiner(PartialApplyInst *PAI, SILBuilder &Builder, - SILCombiner *SilCombiner) - : isFirstTime(true), PAI(PAI), Builder(Builder), - SilCombiner(SilCombiner) {} - SILInstruction *combine(); -}; - -/// Returns true on success. -bool PartialApplyCombiner::allocateTemporaries() { - // A partial_apply [stack]'s argument are not owned by the partial_apply and - // therefore their lifetime must outlive any uses. - if (PAI->isOnStack()) { - return true; - } - - // Copy the original arguments of the partial_apply into newly created - // temporaries and use these temporaries instead of the original arguments - // afterwards. - // - // This is done to "extend" the life-time of original partial_apply arguments, - // as they may be destroyed/deallocated before the last use by one of the - // apply instructions. - // - // TODO: Copy arguments of the partial_apply into new temporaries only if the - // lifetime of arguments ends before their uses by apply instructions. - bool needsReleases = false; - CanSILFunctionType PAITy = - PAI->getCallee()->getType().getAs(); - - // Emit a destroy value for each captured closure argument. - ArrayRef Params = PAITy->getParameters(); - auto Args = PAI->getArguments(); - Params = Params.drop_front(Params.size() - Args.size()); - - llvm::SmallVector, 8> ArgsToHandle; - for (unsigned i : indices(Args)) { - SILValue Arg = Args[i]; - SILParameterInfo Param = Params[i]; - if (Param.isIndirectMutating()) - continue; - - // Create a temporary and copy the argument into it, if: - // - the argument stems from an alloc_stack - // - the argument is consumed by the callee and is indirect - // (e.g. it is an @in argument) - if (isa(Arg) - || (Param.isConsumed() - && PAI->getSubstCalleeConv().isSILIndirect(Param))) { - // If the argument has a dependent type, then we can not create a - // temporary for it at the beginning of the function, so we must bail. - // - // TODO: This is because we are inserting alloc_stack at the beginning/end - // of functions where the dependent type may not exist yet. - if (Arg->getType().hasOpenedExistential()) - return false; - - // If the temporary is non-trivial, we need to release it later. - if (!Arg->getType().isTrivial(*PAI->getFunction())) - needsReleases = true; - ArgsToHandle.push_back(std::make_pair(Arg, i)); - } - } - - if (needsReleases) { - // Compute the set of endpoints, which will be used to insert releases of - // temporaries. This may fail if the frontier is located on a critical edge - // which we may not split (no CFG changes in SILCombine). - ValueLifetimeAnalysis VLA(PAI); - if (!VLA.computeFrontier(PAFrontier, ValueLifetimeAnalysis::DontModifyCFG)) - return false; - } - - for (auto ArgWithIdx : ArgsToHandle) { - SILValue Arg = ArgWithIdx.first; - Builder.setInsertionPoint(PAI->getFunction()->begin()->begin()); - // Create a new temporary at the beginning of a function. - SILDebugVariable DbgVar(/*Constant*/ true, ArgWithIdx.second); - auto *Tmp = Builder.createAllocStack(PAI->getLoc(), Arg->getType(), DbgVar); - Builder.setInsertionPoint(PAI); - // Copy argument into this temporary. - Builder.createCopyAddr(PAI->getLoc(), Arg, Tmp, - IsTake_t::IsNotTake, - IsInitialization_t::IsInitialization); - - Tmps.push_back(Tmp); - ArgToTmp.insert(std::make_pair(Arg, Tmp)); - } - return true; -} - -/// Emit dealloc_stack for all temporaries. -void PartialApplyCombiner::deallocateTemporaries() { - // Insert dealloc_stack instructions at all function exit points. - for (SILBasicBlock &BB : *PAI->getFunction()) { - TermInst *Term = BB.getTerminator(); - if (!Term->isFunctionExiting()) - continue; - - for (auto Op : Tmps) { - Builder.setInsertionPoint(Term); - Builder.createDeallocStack(PAI->getLoc(), Op); - } - } -} - -/// Emit code to release/destroy temporaries. -void PartialApplyCombiner::releaseTemporaries() { - // Insert releases and destroy_addrs as early as possible, - // because we don't want to keep objects alive longer than - // its really needed. - for (auto Op : Tmps) { - auto TmpType = Op->getType().getObjectType(); - if (TmpType.isTrivial(*PAI->getFunction())) - continue; - for (auto *EndPoint : PAFrontier) { - Builder.setInsertionPoint(EndPoint); - if (!TmpType.isAddressOnly(*PAI->getFunction())) { - auto *Load = Builder.createLoad(PAI->getLoc(), Op, - LoadOwnershipQualifier::Unqualified); - Builder.createReleaseValue(PAI->getLoc(), Load, Builder.getDefaultAtomicity()); - } else { - Builder.createDestroyAddr(PAI->getLoc(), Op); - } - } - } -} - -/// Process an apply instruction which uses a partial_apply -/// as its callee. -/// Returns true on success. -bool PartialApplyCombiner::processSingleApply(FullApplySite AI) { - Builder.setInsertionPoint(AI.getInstruction()); - Builder.setCurrentDebugScope(AI.getDebugScope()); - - // Prepare the args. - SmallVector Args; - // First the ApplyInst args. - for (auto Op : AI.getArguments()) - Args.push_back(Op); - - SILInstruction *InsertionPoint = &*Builder.getInsertionPoint(); - // Next, the partial apply args. - - // Pre-process partial_apply arguments only once, lazily. - if (isFirstTime) { - isFirstTime = false; - if (!allocateTemporaries()) - return false; - } - - // Now, copy over the partial apply args. - for (auto Op : PAI->getArguments()) { - auto Arg = Op; - // If there is new temporary for this argument, use it instead. - if (ArgToTmp.count(Arg)) { - Op = ArgToTmp.lookup(Arg); - } - Args.push_back(Op); - } - - Builder.setInsertionPoint(InsertionPoint); - Builder.setCurrentDebugScope(AI.getDebugScope()); - - // The thunk that implements the partial apply calls the closure function - // that expects all arguments to be consumed by the function. However, the - // captured arguments are not arguments of *this* apply, so they are not - // pre-incremented. When we combine the partial_apply and this apply into - // a new apply we need to retain all of the closure non-address type - // arguments. - auto ParamInfo = PAI->getSubstCalleeType()->getParameters(); - auto PartialApplyArgs = PAI->getArguments(); - // Set of arguments that need to be released after each invocation. - SmallVector ToBeReleasedArgs; - for (unsigned i = 0, e = PartialApplyArgs.size(); i < e; ++i) { - SILValue Arg = PartialApplyArgs[i]; - if (!Arg->getType().isAddress()) { - // Retain the argument as the callee may consume it. - Arg = Builder.emitCopyValueOperation(PAI->getLoc(), Arg); - // For non consumed parameters (e.g. guaranteed), we also need to - // insert releases after each apply instruction that we create. - if (!ParamInfo[ParamInfo.size() - PartialApplyArgs.size() + i]. - isConsumed()) - ToBeReleasedArgs.push_back(Arg); - } - } - - auto Callee = PAI->getCallee(); - SubstitutionMap Subs = PAI->getSubstitutionMap(); - - // The partial_apply might be substituting in an open existential type. - Builder.addOpenedArchetypeOperands(PAI); - - FullApplySite NAI; - if (auto *TAI = dyn_cast(AI)) - NAI = Builder.createTryApply(AI.getLoc(), Callee, Subs, Args, - TAI->getNormalBB(), TAI->getErrorBB()); - else - NAI = Builder.createApply(AI.getLoc(), Callee, Subs, Args, - cast(AI)->isNonThrowing()); - - // We also need to release the partial_apply instruction itself because it - // is consumed by the apply_instruction. - if (auto *TAI = dyn_cast(AI)) { - Builder.setInsertionPoint(TAI->getNormalBB()->begin()); - for (auto Arg : ToBeReleasedArgs) { - Builder.emitDestroyValueOperation(PAI->getLoc(), Arg); - } - if (!PAI->hasCalleeGuaranteedContext()) - Builder.createStrongRelease(AI.getLoc(), PAI, - Builder.getDefaultAtomicity()); - Builder.setInsertionPoint(TAI->getErrorBB()->begin()); - // Release the non-consumed parameters. - for (auto Arg : ToBeReleasedArgs) { - Builder.emitDestroyValueOperation(PAI->getLoc(), Arg); - } - if (!PAI->hasCalleeGuaranteedContext()) - Builder.emitDestroyValueOperation(PAI->getLoc(), PAI); - Builder.setInsertionPoint(AI.getInstruction()); - } else { - // Release the non-consumed parameters. - for (auto Arg : ToBeReleasedArgs) { - Builder.emitDestroyValueOperation(PAI->getLoc(), Arg); - } - if (!PAI->hasCalleeGuaranteedContext()) - Builder.emitDestroyValueOperation(PAI->getLoc(), PAI); - } - - if (auto apply = dyn_cast(AI)) - SilCombiner->replaceInstUsesWith(*apply, - cast(NAI.getInstruction())); - SilCombiner->eraseInstFromFunction(*AI.getInstruction()); - return true; -} - -/// Perform the apply{partial_apply(x,y)}(z) -> apply(z,x,y) peephole -/// by iterating over all uses of the partial_apply and searching -/// for the pattern to transform. -SILInstruction *PartialApplyCombiner::combine() { - // We need to model @unowned_inner_pointer better before we can do the - // peephole here. - for (auto R : PAI->getSubstCalleeType()->getResults()) - if (R.getConvention() == ResultConvention::UnownedInnerPointer) - return nullptr; - - // Iterate over all uses of the partial_apply - // and look for applies that use it as a callee. - - // Worklist of operands. - SmallVector Uses(PAI->getUses()); - - // Uses may grow in this loop. - for (size_t UseIndex = 0; UseIndex < Uses.size(); ++UseIndex) { - auto *Use = Uses[UseIndex]; - auto *User = Use->getUser(); - - // Recurse through conversions. - if (auto *CFI = dyn_cast(User)) { - // TODO: Handle argument conversion. All the code in this file needs to be - // cleaned up and generalized. The argument conversion handling in - // optimizeApplyOfConvertFunctionInst should apply to any combine - // involving an apply, not just a specific pattern. - // - // For now, just handle conversion to @noescape, which is irrelevant for - // direct application of the closure. - auto ConvertCalleeTy = CFI->getType().castTo(); - auto EscapingCalleeTy = - ConvertCalleeTy->getWithExtInfo( - ConvertCalleeTy->getExtInfo().withNoEscape(false)); - assert(Use->get()->getType().castTo() == - EscapingCalleeTy); - (void)EscapingCalleeTy; - Uses.append(CFI->getUses().begin(), CFI->getUses().end()); - continue; - } - // Look through mark_dependence users of partial_apply [stack]. - if (auto *MD = dyn_cast(User)) { - if (MD->getValue() == Use->get() && - MD->getValue()->getType().is() && - MD->getValue()->getType().castTo()->isNoEscape()) { - Uses.append(MD->getUses().begin(), MD->getUses().end()); - } - continue; - } - // If this use of a partial_apply is not - // an apply which uses it as a callee, bail. - auto AI = FullApplySite::isa(User); - if (!AI) - continue; - - if (AI.getCallee() != Use->get()) - continue; - - // We cannot handle generic apply yet. Bail. - if (AI.hasSubstitutions()) - continue; - - if (!processSingleApply(AI)) - return nullptr; - } - - // release/destroy and deallocate introduced temporaries. - if (!Tmps.empty()) { - releaseTemporaries(); - deallocateTemporaries(); - } - - return nullptr; -} - -/// Iterate over all uses of a given partial_apply and check -/// if any of those uses are apply instructions. Try to -/// combine those applies with this partial_apply. -SILInstruction * -SILCombiner::tryOptimizeApplyOfPartialApply(PartialApplyInst *PAI) { - - PartialApplyCombiner PACombiner(PAI, Builder, this); - return PACombiner.combine(); -} - SILInstruction * SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, ConvertFunctionInst *CFI) { @@ -494,7 +138,8 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, // Bail if the result type of the converted callee is different from the callee's // result type of the apply instruction. - if (SubstCalleeTy->getAllResultsType() != ConvertCalleeTy->getAllResultsType()) { + if (SubstCalleeTy->getAllResultsSubstType(AI.getModule()) + != ConvertCalleeTy->getAllResultsSubstType(AI.getModule())) { return nullptr; } @@ -546,8 +191,10 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI, bool setNonThrowing = FRI->getFunctionType()->hasErrorResult(); SILInstruction *NAI = Builder.createApply(AI.getLoc(), FRI, SubstitutionMap(), Args, setNonThrowing); - assert(FullApplySite::isa(NAI).getSubstCalleeType()->getAllResultsType() == - AI.getSubstCalleeType()->getAllResultsType() && + assert(FullApplySite::isa(NAI).getSubstCalleeType() + ->getAllResultsSubstType(AI.getModule()) + == AI.getSubstCalleeType() + ->getAllResultsSubstType(AI.getModule()) && "Function types should be the same"); return NAI; } @@ -600,9 +247,23 @@ static SILValue createKeypathProjections(SILValue keyPath, SILValue root, if (addr->getType().getStructOrBoundGenericStruct()) { addr = builder.createStructElementAddr(loc, addr, storedProperty); } else if (addr->getType().getClassOrBoundGenericClass()) { - LoadInst *Ref = builder.createLoad(loc, addr, + SingleValueInstruction *Ref = builder.createLoad(loc, addr, LoadOwnershipQualifier::Unqualified); insertEndAccess(beginAccess, /*isModify*/ false, builder); + + // Handle the case where the storedProperty is in a super class. + while (Ref->getType().getClassOrBoundGenericClass() != + storedProperty->getDeclContext()) { + SILType superCl = Ref->getType().getSuperclass(); + if (!superCl) { + // This should never happen, because the property should be in the + // decl or in a superclass of it. Just handle this to be on the safe + // side. + return SILValue(); + } + Ref = builder.createUpcast(loc, Ref, superCl); + } + addr = builder.createRefElementAddr(loc, Ref, storedProperty); // Class members need access enforcement. @@ -891,7 +552,7 @@ SILCombiner::buildConcreteOpenedExistentialInfoFromSoleConformingType( PD = archetypeTy->getConformsTo()[0]; } else if (ArgType.isExistentialType() && !ArgType.isAnyObject() && !SwiftArgType->isAny()) { - PD = dyn_cast(SwiftArgType->getAnyNominal()); + PD = dyn_cast_or_null(SwiftArgType->getAnyNominal()); } } @@ -1241,7 +902,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( return type; }, [&](CanType origTy, Type substTy, - ProtocolDecl *proto) -> Optional { + ProtocolDecl *proto) -> ProtocolConformanceRef { if (origTy->isEqual(OAI.OpenedArchetype)) { assert(substTy->isEqual(CEI.ConcreteType)); // Do a conformance lookup on this witness requirement using the @@ -1258,7 +919,11 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // apply. Since the apply was never rewritten, if they aren't removed here, // they will be removed later as dead when visited by SILCombine, causing // SILCombine to loop infinitely, creating and destroying the casts. - recursivelyDeleteTriviallyDeadInstructions(*Builder.getTrackingList()); + InstructionDeleter deleter; + for (SILInstruction *inst : *Builder.getTrackingList()) { + deleter.trackIfDead(inst); + } + deleter.cleanUpDeadInstructions(); Builder.getTrackingList()->clear(); return nullptr; } @@ -1294,7 +959,7 @@ SILInstruction *SILCombiner::createApplyWithConcreteType( // deallocate the temporary copy. SILBuilder cleanupBuilder(cleanupPos, NewApply.getDebugScope(), BuilderCtx); auto cleanupLoc = RegularLocation::getAutoGeneratedLocation(); - for (ConcreteArgumentCopy &argCopy : concreteArgCopies) { + for (ConcreteArgumentCopy &argCopy : llvm::reverse(concreteArgCopies)) { cleanupBuilder.createDestroyAddr(cleanupLoc, argCopy.origArg); cleanupBuilder.createDeallocStack(cleanupLoc, argCopy.tempArgCopy->getDest()); @@ -1359,7 +1024,7 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply, // Get the conformance of the init_existential type, which is passed as the // self argument, on the witness' protocol. ProtocolConformanceRef SelfConformance = - *SelfCEI.lookupExistentialConformance(WMI->getLookupProtocol()); + SelfCEI.lookupExistentialConformance(WMI->getLookupProtocol()); // Propagate the concrete type into a callee-operand, which is a // witness_method instruction. It's ok to rewrite the witness method in terms @@ -1586,9 +1251,17 @@ FullApplySite SILCombiner::rewriteApplyCallee(FullApplySite apply, TAI->getSubstitutionMap(), arguments, TAI->getNormalBB(), TAI->getErrorBB()); } else { + auto *AI = cast(apply); + auto fTy = callee->getType().getAs(); + // The optimizer can generate a thin_to_thick_function from a throwing thin + // to a non-throwing thick function (in case it can prove that the function + // is not throwing). + // Therefore we have to check if the new callee (= the argument of the + // thin_to_thick_function) is a throwing function and set the not-throwing + // flag in this case. return Builder.createApply(apply.getLoc(), callee, apply.getSubstitutionMap(), arguments, - cast(apply)->isNonThrowing()); + AI->isNonThrowing() || fTy->hasErrorResult()); } } @@ -1623,7 +1296,7 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { return I; } } - if (SF->hasSemanticsAttr("array.uninitialized")) { + if (SF->hasSemanticsAttr(semantics::ARRAY_UNINITIALIZED)) { UserListTy Users; // If the uninitialized array is only written into then it can be removed. if (recursivelyCollectARCUsers(Users, AI)) { diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp index eee7bf3a22fe2..15810b75d66f5 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp @@ -164,11 +164,13 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) { // %addr = pointer_to_address %ptr, [strict] $T // %result = index_addr %addr, %distance // - BuiltinInst *Bytes; + BuiltinInst *Bytes = nullptr; if (match(PTAI->getOperand(), m_IndexRawPointerInst( m_ValueBase(), m_TupleExtractOperation(m_BuiltinInst(Bytes), 0)))) { + assert(Bytes != nullptr && + "Bytes should have been assigned a non-null value"); if (match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(), m_ApplyInst(BuiltinValueKind::Strideof, m_MetatypeInst(Metatype)), @@ -284,9 +286,12 @@ SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) { Builder.setCurrentDebugScope(URCI->getDebugScope()); LoadInst *load = Builder.createLoad(Loc, URCI->getSrc(), LoadOwnershipQualifier::Unqualified); - auto *cast = Builder.tryCreateUncheckedRefCast(Loc, load, - DestTy.getObjectType()); - assert(cast && "SILBuilder cannot handle reference-castable types"); + + assert(SILType::canRefCast(load->getType(), DestTy.getObjectType(), + Builder.getModule()) && + "SILBuilder cannot handle reference-castable types"); + auto *cast = Builder.createUncheckedRefCast(Loc, load, + DestTy.getObjectType()); Builder.createStore(Loc, cast, URCI->getDest(), StoreOwnershipQualifier::Unqualified); @@ -391,11 +396,12 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) { UBCI->getOperand(), UBCI->getType()); - if (auto refCast = Builder.tryCreateUncheckedRefCast( - UBCI->getLoc(), UBCI->getOperand(), UBCI->getType())) - return refCast; + if (!SILType::canRefCast(UBCI->getOperand()->getType(), UBCI->getType(), + Builder.getModule())) + return nullptr; - return nullptr; + return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(), + UBCI->getType()); } SILInstruction * diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index aa41f2c8d55b2..194c0b8181e5a 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -594,6 +594,62 @@ SILInstruction *SILCombiner::optimizeLoadFromStringLiteral(LoadInst *LI) { return Builder.createIntegerLiteral(LI->getLoc(), LI->getType(), str[index]); } +/// Returns true if \p LI loads a zero integer from the empty Array, Dictionary +/// or Set singleton. +static bool isZeroLoadFromEmptyCollection(LoadInst *LI) { + auto intTy = LI->getType().getAs(); + if (!intTy) + return false; + + SILValue addr = LI->getOperand(); + + // Find the root object of the load-address. + for (;;) { + switch (addr->getKind()) { + case ValueKind::GlobalAddrInst: { + StringRef gName = + cast(addr)->getReferencedGlobal()->getName(); + return gName == "_swiftEmptyArrayStorage" || + gName == "_swiftEmptyDictionarySingleton" || + gName == "_swiftEmptySetSingleton"; + } + case ValueKind::StructElementAddrInst: { + auto *SEA = cast(addr); + // For Array, we only support "count". The value of "capacityAndFlags" + // is not defined in the ABI and could change in another version of the + // runtime (the capacity must be 0, but the flags may be not 0). + if (SEA->getStructDecl()->getName().is("_SwiftArrayBodyStorage") && + !SEA->getField()->getName().is("count")) { + return false; + } + addr = SEA->getOperand(); + break; + } + case ValueKind::RefElementAddrInst: { + auto *REA = cast(addr); + Identifier className = REA->getClassDecl()->getName(); + // For Dictionary and Set we support "count" and "capacity". + if (className.is("__RawDictionaryStorage") || + className.is("__RawSetStorage")) { + Identifier fieldName = REA->getField()->getName(); + if (!fieldName.is("_count") && !fieldName.is("_capacity")) + return false; + } + addr = REA->getOperand(); + break; + } + case ValueKind::UncheckedRefCastInst: + case ValueKind::UpcastInst: + case ValueKind::RawPointerToRefInst: + case ValueKind::AddressToPointerInst: + addr = cast(addr)->getOperand(0); + break; + default: + return false; + } + } +} + SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) { // (load (upcast-ptr %x)) -> (upcast-ref (load %x)) Builder.setCurrentDebugScope(LI->getDebugScope()); @@ -606,6 +662,17 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) { if (SILInstruction *I = optimizeLoadFromStringLiteral(LI)) return I; + // Constant-propagate the 0 value when loading "count" or "capacity" from the + // empty Array, Set or Dictionary storage. + // On high-level SIL this optimization is also done by the + // ArrayCountPropagation pass, but only for Array. And even for Array it's + // sometimes needed to propagate the empty-array count when high-level + // semantics function are already inlined. + // Note that for non-empty arrays/sets/dictionaries, the count can be + // propagated by redundant load elimination. + if (isZeroLoadFromEmptyCollection(LI)) + return Builder.createIntegerLiteral(LI->getLoc(), LI->getType(), 0); + return nullptr; } @@ -1284,9 +1351,9 @@ SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) { X->getType() == SILType::getBuiltinIntegerType(1, CBI->getModule().getASTContext())) { SmallVector OrigTrueArgs, OrigFalseArgs; - for (const auto &Op : CBI->getTrueArgs()) + for (const auto Op : CBI->getTrueArgs()) OrigTrueArgs.push_back(Op); - for (const auto &Op : CBI->getFalseArgs()) + for (const auto Op : CBI->getFalseArgs()) OrigFalseArgs.push_back(Op); return Builder.createCondBranch(CBI->getLoc(), X, CBI->getFalseBB(), OrigFalseArgs, @@ -1519,7 +1586,7 @@ visitAllocRefDynamicInst(AllocRefDynamicInst *ARDI) { return nullptr; auto *CCBI = dyn_cast(PredBB->getTerminator()); if (CCBI && CCBI->isExact() && ARDI->getParent() == CCBI->getSuccessBB()) { - auto MetaTy = CCBI->getCastType().castTo(); + auto MetaTy = cast(CCBI->getTargetFormalType()); auto InstanceTy = MetaTy.getInstanceType(); if (auto SelfTy = dyn_cast(InstanceTy)) InstanceTy = SelfTy.getSelfType(); diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 3f52bb1c05951..d70e8c7305d79 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "allocbox-to-stack" +#include "swift/AST/DiagnosticsSIL.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/SILArgument.h" @@ -70,7 +71,7 @@ static bool useCaptured(Operand *UI) { // Is any successor of BB in the LiveIn set? static bool successorHasLiveIn(SILBasicBlock *BB, - llvm::SmallPtrSetImpl &LiveIn) { + SmallPtrSetImpl &LiveIn) { for (auto &Succ : BB->getSuccessors()) if (LiveIn.count(Succ)) return true; @@ -80,11 +81,11 @@ static bool successorHasLiveIn(SILBasicBlock *BB, // Propagate liveness backwards from an initial set of blocks in our // LiveIn set. -static void propagateLiveness(llvm::SmallPtrSetImpl &LiveIn, +static void propagateLiveness(SmallPtrSetImpl &LiveIn, SILBasicBlock *DefBB) { // First populate a worklist of predecessors. - llvm::SmallVector Worklist; + SmallVector Worklist; for (auto *BB : LiveIn) for (auto Pred : BB->getPredecessorBlocks()) Worklist.push_back(Pred); @@ -106,7 +107,7 @@ static void propagateLiveness(llvm::SmallPtrSetImpl &LiveIn, // Walk backwards in BB looking for strong_release, destroy_value, or // dealloc_box of the given value, and add it to releases. static bool addLastRelease(SILValue V, SILBasicBlock *BB, - llvm::SmallVectorImpl &Releases) { + SmallVectorImpl &Releases) { for (auto I = BB->rbegin(); I != BB->rend(); ++I) { if (isa(*I) || isa(*I) || isa(*I)) { @@ -124,11 +125,10 @@ static bool addLastRelease(SILValue V, SILBasicBlock *BB, // Find the final releases of the alloc_box along any given path. // These can include paths from a release back to the alloc_box in a // loop. -static bool -getFinalReleases(SILValue Box, - llvm::SmallVectorImpl &Releases) { - llvm::SmallPtrSet LiveIn; - llvm::SmallPtrSet UseBlocks; +static bool getFinalReleases(SILValue Box, + SmallVectorImpl &Releases) { + SmallPtrSet LiveIn; + SmallPtrSet UseBlocks; auto *DefBB = Box->getParentBlock(); @@ -138,7 +138,7 @@ getFinalReleases(SILValue Box, // We'll treat this like a liveness problem where the alloc_box is // the def. Each block that has a use of the owning pointer has the // value live-in unless it is the block with the alloc_box. - llvm::SmallVector Worklist(Box->use_begin(), Box->use_end()); + SmallVector Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); @@ -200,7 +200,7 @@ getFinalReleases(SILValue Box, /// sorting, uniquing at the appropriate time. The reason why it makes sense to /// just use a sorted vector with std::count is because generally functions do /// not have that many arguments and even fewer promoted arguments. -using ArgIndexList = llvm::SmallVector; +using ArgIndexList = SmallVector; static bool partialApplyEscapes(SILValue V, bool examineApply); @@ -220,7 +220,7 @@ static bool applyArgumentEscapes(FullApplySite Apply, Operand *O) { static bool partialApplyEscapes(SILValue V, bool examineApply) { SILModuleConventions ModConv(*V->getModule()); - llvm::SmallVector Worklist(V->use_begin(), V->use_end()); + SmallVector Worklist(V->use_begin(), V->use_end()); while (!Worklist.empty()) { Operand *Op = Worklist.pop_back_val(); @@ -277,7 +277,7 @@ static bool partialApplyEscapes(SILValue V, bool examineApply) { static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, - llvm::SmallVectorImpl &); + SmallVectorImpl &); /// checkPartialApplyBody - Check the body of a partial apply to see /// if the box pointer argument passed to it has uses that would @@ -291,7 +291,7 @@ static bool checkPartialApplyBody(Operand *O) { // We don't actually use these because we're not recursively // rewriting the partial applies we find. - llvm::SmallVector PromotedOperands; + SmallVector PromotedOperands; auto calleeArg = F->getArgument(ApplySite(O->getUser()).getCalleeArgIndex(*O)); return !findUnexpectedBoxUse(calleeArg, /* examinePartialApply = */ false, /* inAppliedFunction = */ true, @@ -305,18 +305,18 @@ static bool checkPartialApplyBody(Operand *O) { static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, - llvm::SmallVectorImpl &PromotedOperands) { + SmallVectorImpl &PromotedOperands) { assert((Box->getType().is() || Box->getType() == SILType::getNativeObjectType(Box->getType().getASTContext())) && "Expected an object pointer!"); - llvm::SmallVector LocalPromotedOperands; + SmallVector LocalPromotedOperands; // Scan all of the uses of the retain count value, collecting all // the releases and validating that we don't have an unexpected // user. - llvm::SmallVector Worklist(Box->use_begin(), Box->use_end()); + SmallVector Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); @@ -329,7 +329,7 @@ findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, (!inAppliedFunction && isa(User))) continue; - // If our user instruction is a copy_value or a marked_uninitialized, visit + // If our user instruction is a copy_value or a mark_uninitialized, visit // the users recursively. if (isa(User) || isa(User)) { llvm::copy(cast(User)->getUses(), @@ -355,9 +355,15 @@ findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, return nullptr; } +template +static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc, + Diag diag, U &&... args) { + return Context.Diags.diagnose(loc, diag, std::forward(args)...); +} + /// canPromoteAllocBox - Can we promote this alloc_box to an alloc_stack? static bool canPromoteAllocBox(AllocBoxInst *ABI, - llvm::SmallVectorImpl &PromotedOperands){ + SmallVectorImpl &PromotedOperands) { // Scan all of the uses of the address of the box to see if any // disqualifies the box from being promoted to the stack. if (auto *User = findUnexpectedBoxUse(ABI, @@ -370,6 +376,21 @@ static bool canPromoteAllocBox(AllocBoxInst *ABI, << ABI->getFunction()->getName() << ": " << *ABI << " Due to user: " << *User << "\n"); + // Check if the vardecl has a "boxtostack.mustbeonstack" attribute. If so, + // emit a diagnostic. + if (auto *decl = ABI->getDecl()) { + if (decl->hasSemanticsAttr("boxtostack.mustbeonstack")) { + auto allocDiag = + diag::box_to_stack_cannot_promote_box_to_stack_due_to_escape_alloc; + diagnose(ABI->getModule().getASTContext(), ABI->getLoc().getSourceLoc(), + allocDiag); + auto escapeNote = diag:: + box_to_stack_cannot_promote_box_to_stack_due_to_escape_location; + diagnose(ABI->getModule().getASTContext(), + User->getLoc().getSourceLoc(), escapeNote); + } + } + return false; } @@ -387,16 +408,15 @@ struct AllocBoxToStackState { SILFunctionTransform *T; bool CFGChanged = false; - llvm::SmallVector Promotable; - llvm::SmallVector PromotedOperands; + SmallVector Promotable; + SmallVector PromotedOperands; AllocBoxToStackState(SILFunctionTransform *T) : T(T) {} }; } // anonymous namespace static void replaceProjectBoxUsers(SILValue HeapBox, SILValue StackBox) { - llvm::SmallVector Worklist(HeapBox->use_begin(), - HeapBox->use_end()); + SmallVector Worklist(HeapBox->use_begin(), HeapBox->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); if (auto *PBI = dyn_cast(Op->getUser())) { @@ -427,7 +447,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { } } - llvm::SmallVector FinalReleases; + SmallVector FinalReleases; if (!getFinalReleases(HeapBox, FinalReleases)) return false; @@ -438,7 +458,8 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { && "rewriting multi-field box not implemented"); auto *ASI = Builder.createAllocStack( ABI->getLoc(), - getSILBoxFieldType(ABI->getBoxType(), ABI->getModule().Types, 0), + getSILBoxFieldType(TypeExpansionContext(*ABI->getFunction()), + ABI->getBoxType(), ABI->getModule().Types, 0), ABI->getVarInfo(), ABI->hasDynamicLifetime()); // Transfer a mark_uninitialized if we have one. @@ -454,9 +475,9 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { assert(ABI->getBoxType()->getLayout()->getFields().size() == 1 && "promoting multi-field box not implemented"); - auto &Lowering = ABI->getFunction() - ->getTypeLowering( - getSILBoxFieldType(ABI->getBoxType(), ABI->getModule().Types, 0)); + auto &Lowering = ABI->getFunction()->getTypeLowering( + getSILBoxFieldType(TypeExpansionContext(*ABI->getFunction()), + ABI->getBoxType(), ABI->getModule().Types, 0)); auto Loc = CleanupLocation::get(ABI->getLoc()); for (auto LastRelease : FinalReleases) { @@ -472,7 +493,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) { // Remove any retain and release instructions. Since all uses of project_box // are gone, this only walks through uses of the box itself (the retain count // pointer). - llvm::SmallVector Worklist; + SmallVector Worklist; std::transform(ABI->use_begin(), ABI->use_end(), std::back_inserter(Worklist), [](Operand *Op) -> SILInstruction * { return Op->getUser(); }); while (!Worklist.empty()) { @@ -514,12 +535,12 @@ class PromotedParamCloner : public SILClonerWithScopes { // The values in the original function that are promoted to stack // references. - llvm::SmallSet OrigPromotedParameters; + SmallPtrSet OrigPromotedParameters; public: - PromotedParamCloner(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, IsSerialized_t Serialized, - ArgIndexList &PromotedArgIndices, - llvm::StringRef ClonedName); + PromotedParamCloner(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, + IsSerialized_t Serialized, + ArgIndexList &PromotedArgIndices, StringRef ClonedName); void populateCloned(); @@ -529,7 +550,7 @@ class PromotedParamCloner : public SILClonerWithScopes { static SILFunction *initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, IsSerialized_t Serialized, ArgIndexList &PromotedArgIndices, - llvm::StringRef ClonedName); + StringRef ClonedName); void visitStrongReleaseInst(StrongReleaseInst *Inst); void visitDestroyValueInst(DestroyValueInst *Inst); @@ -543,10 +564,9 @@ PromotedParamCloner::PromotedParamCloner(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, IsSerialized_t Serialized, ArgIndexList &PromotedArgIndices, - llvm::StringRef ClonedName) - : SILClonerWithScopes( - *initCloned(FuncBuilder, Orig, Serialized, PromotedArgIndices, - ClonedName)), + StringRef ClonedName) + : SILClonerWithScopes(*initCloned( + FuncBuilder, Orig, Serialized, PromotedArgIndices, ClonedName)), Orig(Orig), PromotedArgIndices(PromotedArgIndices) { NewPromotedArgs.reserve(PromotedArgIndices.size()); assert(Orig->getDebugScope()->getParentFunction() != @@ -566,10 +586,11 @@ static std::string getClonedName(SILFunction *F, IsSerialized_t Serialized, /// Create the function corresponding to the clone of the /// original closure with the signature modified to reflect promoted /// parameters (which are specified by PromotedArgIndices). -SILFunction *PromotedParamCloner:: -initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, - IsSerialized_t Serialized, ArgIndexList &PromotedArgIndices, - llvm::StringRef ClonedName) { +SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, + SILFunction *Orig, + IsSerialized_t Serialized, + ArgIndexList &PromotedArgIndices, + StringRef ClonedName) { SILModule &M = Orig->getModule(); SmallVector ClonedInterfaceArgTys; @@ -579,15 +600,13 @@ initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, unsigned Index = Orig->getConventions().getSILArgIndexOfFirstParam(); for (auto ¶m : OrigFTI->getParameters()) { if (count(PromotedArgIndices, Index)) { - auto boxTy = param.getSILStorageType().castTo(); + auto boxTy = param.getSILStorageInterfaceType().castTo(); assert(boxTy->getLayout()->getFields().size() == 1 && "promoting compound box not implemented"); SILType paramTy; { auto &TC = Orig->getModule().Types; - Lowering::GenericContextScope scope(TC, - OrigFTI->getGenericSignature()); - paramTy = getSILBoxFieldType(boxTy, TC, 0); + paramTy = getSILBoxFieldType(TypeExpansionContext(*Orig), boxTy, TC, 0); } auto promotedParam = SILParameterInfo(paramTy.getASTType(), ParameterConvention::Indirect_InoutAliasable); @@ -601,11 +620,12 @@ initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, // Create the new function type for the cloned function with some of // the parameters promoted. auto ClonedTy = SILFunctionType::get( - OrigFTI->getGenericSignature(), OrigFTI->getExtInfo(), + OrigFTI->getSubstGenericSignature(), OrigFTI->getExtInfo(), OrigFTI->getCoroutineKind(), OrigFTI->getCalleeConvention(), - ClonedInterfaceArgTys, OrigFTI->getYields(), - OrigFTI->getResults(), OrigFTI->getOptionalErrorResult(), - M.getASTContext(), OrigFTI->getWitnessMethodConformanceOrNone()); + ClonedInterfaceArgTys, OrigFTI->getYields(), OrigFTI->getResults(), + OrigFTI->getOptionalErrorResult(), OrigFTI->getSubstitutions(), + OrigFTI->isGenericSignatureImplied(), M.getASTContext(), + OrigFTI->getWitnessMethodConformanceOrInvalid()); assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation()) && "SILFunction missing location"); @@ -614,7 +634,7 @@ initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned"); auto *Fn = FuncBuilder.createFunction( SILLinkage::Shared, ClonedName, ClonedTy, Orig->getGenericEnvironment(), - Orig->getLocation(), Orig->isBare(), IsNotTransparent, Serialized, + Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); @@ -651,7 +671,8 @@ PromotedParamCloner::populateCloned() { auto boxTy = (*I)->getType().castTo(); assert(boxTy->getLayout()->getFields().size() == 1 && "promoting multi-field boxes not implemented yet"); - auto promotedTy = getSILBoxFieldType(boxTy, Cloned->getModule().Types, 0); + auto promotedTy = getSILBoxFieldType(TypeExpansionContext(*Cloned), boxTy, + Cloned->getModule().Types, 0); auto *promotedArg = ClonedEntryBB->createFunctionArgument(promotedTy, (*I)->getDecl()); OrigPromotedParameters.insert(*I); @@ -776,7 +797,7 @@ specializePartialApply(SILOptFunctionBuilder &FuncBuilder, } // Now create the new partial_apply using the cloned function. - llvm::SmallVector Args; + SmallVector Args; ValueLifetimeAnalysis::Frontier PAFrontier; diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index f92c461ac3dae..b0146133a9a32 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -21,7 +21,6 @@ silopt_register_sources( MergeCondFail.cpp Outliner.cpp ObjectOutliner.cpp - OwnershipModelEliminator.cpp PerformanceInliner.cpp RedundantLoadElimination.cpp RedundantOverflowCheckRemoval.cpp @@ -32,7 +31,6 @@ silopt_register_sources( SILSROA.cpp SimplifyCFG.cpp Sink.cpp - SpecializeOpaqueArchetypes.cpp SpeculativeDevirtualizer.cpp StackPromotion.cpp UnsafeGuaranteedPeephole.cpp diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index 8021602717f38..4ea79ed617ae1 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -618,7 +618,7 @@ static void updateBasicBlockArgTypes(SILBasicBlock *BB, ArchetypeType *OldOpenedArchetype, ArchetypeType *NewOpenedArchetype) { // Check types of all BB arguments. - for (auto *Arg : BB->getPhiArguments()) { + for (auto *Arg : BB->getSILPhiArguments()) { if (!Arg->getType().hasOpenedExistential()) continue; // Type of this BB argument uses an opened existential. diff --git a/lib/SILOptimizer/Transforms/CopyForwarding.cpp b/lib/SILOptimizer/Transforms/CopyForwarding.cpp index c33bf4bddbf0b..3b8978e643222 100644 --- a/lib/SILOptimizer/Transforms/CopyForwarding.cpp +++ b/lib/SILOptimizer/Transforms/CopyForwarding.cpp @@ -69,6 +69,7 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/CommandLine.h" @@ -1569,6 +1570,8 @@ class TempRValueOptPass : public SILFunctionTransform { bool checkNoSourceModification(CopyAddrInst *copyInst, const llvm::SmallPtrSetImpl &useInsts); + bool checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst); + bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst); void run() override; @@ -1620,6 +1623,17 @@ void TempRValueOptPass::run() { /// Transitively explore all data flow uses of the given \p address until /// reaching a load or returning false. +/// +/// Any user opcode recognized by collectLoads must be replaced correctly later +/// during tryOptimizeCopyIntoTemp. If it is possible for any use to destroy the +/// value in \p address, then that use must be removed or made non-destructive +/// after the copy is removed and its operand is replaced. +/// +/// Warning: To preserve the original object lifetime, tryOptimizeCopyIntoTemp +/// must assume that there are no holes in lifetime of the temporary stack +/// location at \address. The temporary must be initialized by the original copy +/// and never written to again. Therefore, collectLoads disallows any operation +/// that may write to memory at \p address. bool TempRValueOptPass::collectLoads( Operand *userOp, SILInstruction *user, SingleValueInstruction *address, SILValue srcObject, @@ -1645,7 +1659,8 @@ bool TempRValueOptPass::collectLoads( << *user); return false; - case SILInstructionKind::ApplyInst: { + case SILInstructionKind::ApplyInst: + case SILInstructionKind::TryApplyInst: { ApplySite apply(user); // Check if the function can just read from userOp. @@ -1694,7 +1709,7 @@ bool TempRValueOptPass::collectLoads( "its source" << *user); return false; } - return true; + LLVM_FALLTHROUGH; } case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::TupleElementAddrInst: { @@ -1713,12 +1728,11 @@ bool TempRValueOptPass::collectLoads( } case SILInstructionKind::LoadInst: - case SILInstructionKind::LoadBorrowInst: { + case SILInstructionKind::LoadBorrowInst: // Loads are the end of the data flow chain. The users of the load can't // access the temporary storage. loadInsts.insert(user); return true; - } case SILInstructionKind::CopyAddrInst: { // copy_addr which read from the temporary are like loads. @@ -1768,6 +1782,73 @@ bool TempRValueOptPass::checkNoSourceModification(CopyAddrInst *copyInst, return false; } +/// Return true if the \p tempObj, which is initialized by \p copyInst, is +/// destroyed in an orthodox way. +/// +/// When tryOptimizeCopyIntoTemp replaces all of tempObj's uses, it assumes that +/// the object is initialized by the original copy and directly destroyed on all +/// paths by one of the recognized 'destroy_addr' or 'copy_addr [take]' +/// operations. This assumption must be checked. For example, in non-OSSA, +/// it is legal to destroy an in-memory object by loading the value and +/// releasing it. Rather than detecting unbalanced load releases, simply check +/// that tempObj is destroyed directly on all paths. +bool TempRValueOptPass::checkTempObjectDestroy(AllocStackInst *tempObj, + CopyAddrInst *copyInst) { + // If the original copy was a take, then replacing all uses cannot affect + // the lifetime. + if (copyInst->isTakeOfSrc()) + return true; + + // ValueLifetimeAnalysis is not normally used for address types. It does not + // reason about the lifetime of the in-memory object. However the utility can + // be abused here to check that the address is directly destroyed on all + // paths. collectLoads has already guaranteed that tempObj's lifetime has no + // holes/reinitializations. + SmallVector users; + for (auto result : tempObj->getResults()) { + for (Operand *operand : result->getUses()) { + SILInstruction *user = operand->getUser(); + if (user == copyInst) + continue; + if (isa(user)) + continue; + users.push_back(user); + } + } + // Find the boundary of tempObj's address lifetime, starting at copyInst. + ValueLifetimeAnalysis vla(copyInst, users); + ValueLifetimeAnalysis::Frontier tempAddressFrontier; + if (!vla.computeFrontier(tempAddressFrontier, + ValueLifetimeAnalysis::DontModifyCFG)) { + return false; + } + // Check that the lifetime boundary ends at direct destroy points. + for (SILInstruction *frontierInst : tempAddressFrontier) { + auto pos = frontierInst->getIterator(); + // If the frontier is at the head of a block, then either it is an + // unexpected lifetime exit, or the lifetime ended at a + // terminator. TempRValueOptPass does not handle either case. + if (pos == frontierInst->getParent()->begin()) + return false; + + // Look for a known destroy point as described in the funciton level + // comment. This whitelist can be expanded as more cases are handled in + // tryOptimizeCopyIntoTemp during copy replacement. + SILInstruction *lastUser = &*std::prev(pos); + if (isa(lastUser)) + continue; + + if (auto *cai = dyn_cast(lastUser)) { + assert(cai->getSrc() == tempObj && "collectLoads checks for writes"); + assert(!copyInst->isTakeOfSrc() && "checked above"); + if (cai->isTakeOfSrc()) + continue; + } + return false; + } + return true; +} + /// Tries to perform the temporary rvalue copy elimination for \p copyInst bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (!copyInst->isInitializationOfDest()) @@ -1803,6 +1884,9 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { if (!checkNoSourceModification(copyInst, loadInsts)) return false; + if (!checkTempObjectDestroy(tempObj, copyInst)) + return false; + LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj); // Do a "replaceAllUses" by either deleting the users or replacing them with @@ -1833,17 +1917,12 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) { use->set(copyInst->getSrc()); break; } - case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: - case SILInstructionKind::LoadInst: - case SILInstructionKind::LoadBorrowInst: - case SILInstructionKind::ApplyInst: - case SILInstructionKind::OpenExistentialAddrInst: + // ASSUMPTION: no operations that may be handled by this default clause can + // destroy tempObj. This includes operations that load the value from memory + // and release it. + default: use->set(copyInst->getSrc()); break; - - default: - llvm_unreachable("unhandled instruction"); } } tempObj->eraseFromParent(); diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index bc9dc78059b22..9ca91cc70cb9c 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -400,22 +400,18 @@ void DCE::propagateLiveness(SILInstruction *I) { case TermKind::SwitchEnumInst: case TermKind::SwitchEnumAddrInst: case TermKind::DynamicMethodBranchInst: - case TermKind::CheckedCastBranchInst: - case TermKind::CheckedCastValueBranchInst: markValueLive(I->getOperand(0)); return; + case TermKind::CheckedCastBranchInst: + case TermKind::CheckedCastValueBranchInst: + case TermKind::CheckedCastAddrBranchInst: case TermKind::TryApplyInst: case TermKind::SwitchValueInst: case TermKind::YieldInst: for (auto &O : I->getAllOperands()) markValueLive(O.get()); return; - - case TermKind::CheckedCastAddrBranchInst: - markValueLive(I->getOperand(0)); - markValueLive(I->getOperand(1)); - return; } llvm_unreachable("corrupt instruction!"); } diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 9dc817b733712..266859152516a 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -60,6 +60,7 @@ #include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" +#include "swift/SIL/MemoryLifetime.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" @@ -148,11 +149,11 @@ static inline bool isPerformingDSE(DSEKind Kind) { static bool isDeadStoreInertInstruction(SILInstruction *Inst) { switch (Inst->getKind()) { #define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Name##RetainInst: \ - case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StrongRetainInst: case SILInstructionKind::RetainValueInst: @@ -269,6 +270,8 @@ class BlockState { init(LocationNum, Optimistic); } + void dump(); + /// Initialize the bitvectors for the current basic block. void init(unsigned LocationNum, bool Optimistic); @@ -448,6 +451,8 @@ enum class ProcessKind { llvm::SpecificBumpPtrAllocator &BPA) : Mod(M), F(F), PM(PM), AA(AA), TE(TE), EAFI(EAFI), BPA(BPA) {} + void dump(); + /// Entry point for dead store elimination. bool run(); @@ -481,6 +486,12 @@ enum class ProcessKind { } // end anonymous namespace +void BlockState::dump() { + llvm::dbgs() << " block " << BB->getDebugID() << ": in=" << BBWriteSetIn + << ", out=" << BBWriteSetOut << ", mid=" << BBWriteSetMid + << ", gen=" << BBGenSet << ", kill=" << BBKillSet << '\n'; +} + void BlockState::init(unsigned LocationNum, bool Optimistic) { // For function that requires just 1 iteration of the data flow to converge // we set the initial state of BBWriteSetIn to 0. @@ -513,6 +524,21 @@ void BlockState::init(unsigned LocationNum, bool Optimistic) { BBDeallocateLocation.resize(LocationNum, false); } +#if __has_attribute(used) +__attribute((used)) +#endif +void DSEContext::dump() { + llvm::dbgs() << "Locations:\n"; + unsigned idx = 0; + for (const LSLocation &loc : LocationVault) { + llvm::dbgs() << " #" << idx << ": " << loc.getBase(); + ++idx; + } + for (SILBasicBlock &BB : *F) { + getBlockState(&BB)->dump(); + } +} + unsigned DSEContext::getLocationBit(const LSLocation &Loc) { // Return the bit position of the given Loc in the LocationVault. The bit // position is then used to set/reset the bitvector kept by each BlockState. @@ -691,6 +717,10 @@ void DSEContext::mergeSuccessorLiveIns(SILBasicBlock *BB) { // dead for block with no successor. BlockState *C = getBlockState(BB); if (BB->succ_empty()) { + if (isa(BB->getTerminator())) { + C->BBWriteSetOut.set(); + return; + } C->BBWriteSetOut |= C->BBDeallocateLocation; return; } @@ -825,7 +855,8 @@ void DSEContext::processRead(SILInstruction *I, SILValue Mem, DSEKind Kind) { // Expand the given Mem into individual fields and process them as separate // reads. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, TE); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, TE); // Are we building the genset and killset. if (isBuildingGenKillSet(Kind)) { @@ -910,7 +941,7 @@ void DSEContext::processWrite(SILInstruction *I, SILValue Val, SILValue Mem, // writes. bool Dead = true; LSLocationList Locs; - LSLocation::expand(L, Mod, Locs, TE); + LSLocation::expand(L, Mod, TypeExpansionContext(*I->getFunction()), Locs, TE); SmallBitVector V(Locs.size()); // Are we computing max store set. @@ -967,7 +998,7 @@ void DSEContext::processWrite(SILInstruction *I, SILValue Val, SILValue Mem, } // Try to create as few aggregated stores as possible out of the locations. - LSLocation::reduce(L, Mod, Alives); + LSLocation::reduce(L, Mod, TypeExpansionContext(*I->getFunction()), Alives); // Oops, we have too many smaller stores generated, bail out. if (Alives.size() > MaxPartialStoreCount) diff --git a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp index d3ab5ab15fd75..eeff8989096d1 100644 --- a/lib/SILOptimizer/Transforms/DestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/DestroyHoisting.cpp @@ -95,6 +95,8 @@ class DestroyHoisting { void getUsedLocationsOfAddr(Bits &bits, SILValue addr); + void getUsedLocationsOfOperands(Bits &bits, SILInstruction *I); + void getUsedLocationsOfInst(Bits &bits, SILInstruction *Inst); void moveDestroys(MemoryDataflow &dataFlow); @@ -206,9 +208,16 @@ void DestroyHoisting::expandStores(MemoryDataflow &dataFlow) { // Initialize the dataflow for moving destroys up the control flow. void DestroyHoisting::initDataflow(MemoryDataflow &dataFlow) { for (BlockState &st : dataFlow) { - st.entrySet.set(); st.genSet.reset(); st.killSet.reset(); + if (st.isInInfiniteLoop()) { + // Ignore blocks which are in an infinite loop and prevent any destroy + // hoisting across such block borders. + st.entrySet.reset(); + st.exitSet.reset(); + continue; + } + st.entrySet.set(); if (isa(st.block->getTerminator())) { if (canIgnoreUnreachableBlock(st.block, dataFlow)) { st.exitSet.set(); @@ -282,7 +291,7 @@ bool DestroyHoisting::canIgnoreUnreachableBlock(SILBasicBlock *block, SILBasicBlock *singlePred = block->getSinglePredecessorBlock(); if (!singlePred) return false; - if (!dataFlow.getState(singlePred)->exitReachable) + if (!dataFlow.getState(singlePred)->exitReachable()) return false; // Check if none of the locations are touched in the unreachable-block. @@ -308,6 +317,12 @@ void DestroyHoisting::getUsedLocationsOfAddr(Bits &bits, SILValue addr) { } } +void DestroyHoisting::getUsedLocationsOfOperands(Bits &bits, SILInstruction *I) { + for (Operand &op : I->getAllOperands()) { + getUsedLocationsOfAddr(bits, op.get()); + } +} + // Set all bits of locations which instruction \p I is using. It's including // parent and sub-locations (see comment in getUsedLocationsOfAddr). void DestroyHoisting::getUsedLocationsOfInst(Bits &bits, SILInstruction *I) { @@ -318,15 +333,21 @@ void DestroyHoisting::getUsedLocationsOfInst(Bits &bits, SILInstruction *I) { getUsedLocationsOfAddr(bits, LBI->getOperand()); } break; + case SILInstructionKind::EndApplyInst: + // Operands passed to begin_apply are alive throughout an end_apply ... + getUsedLocationsOfOperands(bits, cast(I)->getBeginApply()); + break; + case SILInstructionKind::AbortApplyInst: + // ... or abort_apply. + getUsedLocationsOfOperands(bits, cast(I)->getBeginApply()); + break; case SILInstructionKind::LoadInst: case SILInstructionKind::StoreInst: case SILInstructionKind::CopyAddrInst: case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::YieldInst: - for (Operand &op : I->getAllOperands()) { - getUsedLocationsOfAddr(bits, op.get()); - } + getUsedLocationsOfOperands(bits, I); break; case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DestroyAddrInst: @@ -360,6 +381,10 @@ void DestroyHoisting::moveDestroys(MemoryDataflow &dataFlow) { if (isa(block->getTerminator()) && state.exitSet.any()) continue; + // Ignore blocks which are in an infinite loop. + if (state.isInInfiniteLoop()) + continue; + // Do the inner-block processing. activeDestroys = state.exitSet; moveDestroysInBlock(block, activeDestroys, toRemove); @@ -702,6 +727,10 @@ class DestroyHoistingPass : public SILFunctionTransform { if (!F->hasOwnership()) return; + // If we are not supposed to perform ossa optimizations, bail. + if (!F->getModule().getOptions().EnableOSSAOptimizations) + return; + LLVM_DEBUG(llvm::dbgs() << "*** DestroyHoisting on function: " << F->getName() << " ***\n"); diff --git a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp index 688ca6c30db2b..1b5bef9ec9194 100644 --- a/lib/SILOptimizer/Transforms/ObjectOutliner.cpp +++ b/lib/SILOptimizer/Transforms/ObjectOutliner.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "objectoutliner" #include "swift/AST/ASTMangler.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILBuilder.h" #include "swift/SILOptimizer/PassManager/Transforms.h" @@ -148,7 +149,7 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, bool isCOWObject, // There should only be a single call to findStringSwitchCase. But even // if there are multiple calls, it's not problem - we'll just optimize the // last one we find. - if (cast(I)->hasSemantics("findStringSwitchCase")) + if (cast(I)->hasSemantics(semantics::FIND_STRING_SWITCH_CASE)) *FindStringCall = cast(I); return true; @@ -488,7 +489,8 @@ bool ObjectOutliner::optimizeObjectAllocation(AllocRefInst *ARI) { void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { // Find the replacement function in the swift stdlib. SmallVector results; - SILModule *Module = &FindStringCall->getFunction()->getModule(); + auto &F = *FindStringCall->getFunction(); + SILModule *Module = &F.getModule(); Module->getASTContext().lookupInSwiftModule("_findStringSwitchCaseWithCache", results); if (results.size() != 1) @@ -506,7 +508,8 @@ void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { if (FTy->getNumParameters() != 3) return; - SILType cacheType = FTy->getParameters()[2].getSILStorageType().getObjectType(); + SILType cacheType = FTy->getParameters()[2].getSILStorageType(*Module, FTy) + .getObjectType(); NominalTypeDecl *cacheDecl = cacheType.getNominalOrBoundGenericNominal(); if (!cacheDecl) return; @@ -516,8 +519,9 @@ void ObjectOutliner::replaceFindStringCall(ApplyInst *FindStringCall) { assert(!cacheDecl->isResilient(Module->getSwiftModule(), ResilienceExpansion::Minimal)); - SILType wordTy = cacheType.getFieldType( - cacheDecl->getStoredProperties().front(), *Module); + SILType wordTy = + cacheType.getFieldType(cacheDecl->getStoredProperties().front(), *Module, + F.getTypeExpansionContext()); GlobalVariableMangler Mangler; std::string GlobName = diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 40281ad1f2c6a..a0a827199a37c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -18,16 +18,17 @@ #include "swift/AST/Types.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingMacros.h" +#include "swift/SIL/ApplySite.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILFunction.h" -#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "llvm/ADT/BitVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" @@ -50,6 +51,7 @@ class OutlinerMangler : public Mangle::ASTMangler { }; llvm::BitVector *IsParameterBridged; + llvm::BitVector *IsParameterGuaranteed; SILDeclRef MethodDecl; MethodKind Kind; bool IsReturnBridged; @@ -57,13 +59,15 @@ class OutlinerMangler : public Mangle::ASTMangler { public: /// Create an mangler for an outlined bridged method. OutlinerMangler(SILDeclRef Method, llvm::BitVector *ParameterBridged, - bool ReturnBridged) - : IsParameterBridged(ParameterBridged), MethodDecl(Method), + llvm::BitVector *IsParameterGuaranteed, bool ReturnBridged) + : IsParameterBridged(ParameterBridged), + IsParameterGuaranteed(IsParameterGuaranteed), MethodDecl(Method), Kind(BridgedMethod), IsReturnBridged(ReturnBridged) {} /// Create an mangler for an outlined bridged property. OutlinerMangler(SILDeclRef Method, bool IsAddress) - : IsParameterBridged(nullptr), MethodDecl(Method), + : IsParameterBridged(nullptr), IsParameterGuaranteed(nullptr), + MethodDecl(Method), Kind(IsAddress ? BridgedPropertyAddress : BridgedProperty), IsReturnBridged(true) {} @@ -93,9 +97,14 @@ std::string OutlinerMangler::mangle() { llvm::raw_svector_ostream Out(Buffer); Out << getMethodKindMangling(); - if (IsParameterBridged) - for (unsigned Idx = 0, E = IsParameterBridged->size(); Idx != E; ++Idx) + if (IsParameterBridged) { + for (unsigned Idx = 0, E = IsParameterBridged->size(); Idx != E; ++Idx) { Out << (IsParameterBridged->test(Idx) ? 'b' : 'n'); + // NOTE: We must keep owned as having nothing here to preserve ABI since + // mangling is part of ABI. + Out << (IsParameterGuaranteed->test(Idx) ? "g" : ""); + } + } Out << (IsReturnBridged ? 'b' : 'n'); Out << '_'; @@ -138,10 +147,10 @@ static SILDeclRef getBridgeToObjectiveC(CanType NativeType, return SILDeclRef(); auto ConformanceRef = SwiftModule->lookupConformance(NativeType, Proto); - if (!ConformanceRef) + if (ConformanceRef.isInvalid()) return SILDeclRef(); - auto Conformance = ConformanceRef->getConcrete(); + auto Conformance = ConformanceRef.getConcrete(); // bridgeToObjectiveC DeclName Name(Ctx, Ctx.Id_bridgeToObjectiveC, llvm::ArrayRef()); auto *Requirement = dyn_cast_or_null( @@ -162,9 +171,9 @@ SILDeclRef getBridgeFromObjectiveC(CanType NativeType, return SILDeclRef(); auto ConformanceRef = SwiftModule->lookupConformance(NativeType, Proto); - if (!ConformanceRef) + if (ConformanceRef.isInvalid()) return SILDeclRef(); - auto Conformance = ConformanceRef->getConcrete(); + auto Conformance = ConformanceRef.getConcrete(); // _unconditionallyBridgeFromObjectiveC DeclName Name(Ctx, Ctx.getIdentifier("_unconditionallyBridgeFromObjectiveC"), llvm::makeArrayRef(Identifier())); @@ -288,11 +297,15 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { ResultConvention::Owned)); auto ExtInfo = SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, /*noescape*/ false); + /*pseudogeneric*/ false, /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr); auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, - Results, None, M.getASTContext()); + Results, None, + SubstitutionMap(), false, + M.getASTContext()); return FunctionType; } @@ -478,8 +491,11 @@ static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, auto NativeType = Apply->getType().getASTType(); auto *BridgeFun = FunRef->getInitiallyReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); + // Not every type conforms to the ObjectiveCBridgeable protocol in such a case + // getBridgeFromObjectiveC returns SILDeclRef(). auto bridgeWitness = getBridgeFromObjectiveC(NativeType, SwiftModule); - if (BridgeFun->getName() != bridgeWitness.mangle()) + if (bridgeWitness == SILDeclRef() || + BridgeFun->getName() != bridgeWitness.mangle()) return false; // %41 = enum $Optional, #Optional.some!enumelt.1, %40 : $String @@ -639,7 +655,10 @@ bool BridgedProperty::matchInstSequence(SILBasicBlock::iterator It) { namespace { + /// Match a bridged argument. +/// +/// /// %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF /// %16 = apply %15(%14) : /// $@convention(method) (@guaranteed String) -> @owned NSString @@ -648,11 +667,16 @@ namespace { /// /// apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional ...) -> /// release_value %17 : $Optional +/// +/// NOTE: If release_value %14 is found, our outlined function will have an +/// owned convention for self. Otherwise, if we do not find it, we will have a +/// guaranteed one. class BridgedArgument { public: FunctionRefInst *BridgeFun; ApplyInst *BridgeCall; EnumInst *OptionalResult; + SILValue BridgedValue; ReleaseValueInst *ReleaseAfterBridge; ReleaseValueInst *ReleaseArgAfterCall; unsigned Idx = 0; @@ -660,8 +684,9 @@ class BridgedArgument { // Matched bridged argument. BridgedArgument(unsigned Idx, FunctionRefInst *F, ApplyInst *A, EnumInst *E, ReleaseValueInst *R0, ReleaseValueInst *R1) - : BridgeFun(F), BridgeCall(A), OptionalResult(E), ReleaseAfterBridge(R0), - ReleaseArgAfterCall(R1), Idx(Idx) {} + : BridgeFun(F), BridgeCall(A), OptionalResult(E), + BridgedValue(FullApplySite(A).getSelfArgument()), + ReleaseAfterBridge(R0), ReleaseArgAfterCall(R1), Idx(Idx) {} /// Invalid argument constructor. BridgedArgument() @@ -671,7 +696,14 @@ class BridgedArgument { static BridgedArgument match(unsigned ArgIdx, SILValue Arg, ApplyInst *AI); operator bool() const { return BridgeFun != nullptr; } - SILValue bridgedValue() { return ReleaseAfterBridge->getOperand(); } + SILValue bridgedValue() { return BridgedValue; } + + bool isGuaranteed() const { return ReleaseAfterBridge == nullptr; } + ParameterConvention getConvention() const { + if (isGuaranteed()) + return ParameterConvention::Direct_Guaranteed; + return ParameterConvention::Direct_Owned; + } void eraseFromParent(); @@ -693,14 +725,17 @@ void BridgedArgument::transferTo(SILValue BridgedValue, DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeCall); BridgeCall->setArgument(0, BridgedValue); DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), OptionalResult); - DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), ReleaseAfterBridge); - ReleaseAfterBridge->setOperand(BridgedValue); + if (ReleaseAfterBridge) { + DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), ReleaseAfterBridge); + ReleaseAfterBridge->setOperand(BridgedValue); + } auto AfterCall = std::next(SILBasicBlock::iterator(BridgedCall)); DestBB->moveTo(SILBasicBlock::iterator(AfterCall), ReleaseArgAfterCall); } void BridgedArgument::eraseFromParent() { - ReleaseAfterBridge->eraseFromParent(); + if (ReleaseAfterBridge) + ReleaseAfterBridge->eraseFromParent(); ReleaseArgAfterCall->eraseFromParent(); OptionalResult->eraseFromParent(); BridgeCall->eraseFromParent(); @@ -729,16 +764,27 @@ BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg, auto *BridgeCall = dyn_cast(std::prev(SILBasicBlock::iterator(Enum))); if (!BridgeCall || BridgeCall->getNumArguments() != 1 || - Enum->getOperand() != BridgeCall || !BridgeCall->hasOneUse()) + Enum->getOperand() != BridgeCall || !BridgeCall->hasOneUse() || + !FullApplySite(BridgeCall).hasSelfArgument()) + return BridgedArgument(); + + auto &selfArg = FullApplySite(BridgeCall).getSelfArgumentOperand(); + auto selfConvention = + FullApplySite(BridgeCall).getArgumentConvention(selfArg); + if (selfConvention != SILArgumentConvention::Direct_Guaranteed && + selfConvention != SILArgumentConvention::Direct_Owned) return BridgedArgument(); auto BridgedValue = BridgeCall->getArgument(0); auto Next = std::next(SILBasicBlock::iterator(Enum)); if (Next == Enum->getParent()->end()) return BridgedArgument(); + + // Make sure that if we have a bridged value release that it is on the bridged + // value. auto *BridgedValueRelease = dyn_cast(std::next(SILBasicBlock::iterator(Enum))); - if (!BridgedValueRelease || BridgedValueRelease->getOperand() != BridgedValue) + if (BridgedValueRelease && BridgedValueRelease->getOperand() != BridgedValue) return BridgedArgument(); if (SILBasicBlock::iterator(BridgeCall) == BridgeCall->getParent()->begin()) @@ -766,8 +812,11 @@ BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg, auto NativeType = BridgedValue->getType().getASTType(); auto *BridgeFun = FunRef->getInitiallyReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); + // Not every type conforms to the ObjectiveCBridgeable protocol in such a case + // getBridgeToObjectiveC returns SILDeclRef(). auto bridgeWitness = getBridgeToObjectiveC(NativeType, SwiftModule); - if (BridgeFun->getName() != bridgeWitness.mangle()) + if (bridgeWitness == SILDeclRef() || + BridgeFun->getName() != bridgeWitness.mangle()) return BridgedArgument(); return BridgedArgument(ArgIdx, FunRef, BridgeCall, Enum, BridgedValueRelease, @@ -881,7 +930,8 @@ class ObjCMethodCall : public OutlinePattern { SmallVector BridgedArguments; std::string OutlinedName; llvm::BitVector IsBridgedArgument; - BridgedReturn BridgedReturn; + llvm::BitVector IsGuaranteedArgument; + ::BridgedReturn BridgedReturn; public: bool matchInstSequence(SILBasicBlock::iterator I) override; @@ -909,6 +959,7 @@ void ObjCMethodCall::clearState() { BridgedArguments.clear(); OutlinedName.clear(); IsBridgedArgument.clear(); + IsGuaranteedArgument.clear(); } std::pair @@ -1014,7 +1065,7 @@ ObjCMethodCall::outline(SILModule &M) { std::string ObjCMethodCall::getOutlinedFunctionName() { if (OutlinedName.empty()) { OutlinerMangler Mangler(ObjCMethod->getMember(), &IsBridgedArgument, - BridgedReturn); + &IsGuaranteedArgument, BridgedReturn); OutlinedName = Mangler.mangle(); } return OutlinedName; @@ -1042,6 +1093,7 @@ bool ObjCMethodCall::matchInstSequence(SILBasicBlock::iterator I) { // Collect bridged parameters. unsigned Idx = 0; IsBridgedArgument.resize(BridgedCall->getNumArguments(), false); + IsGuaranteedArgument.resize(BridgedCall->getNumArguments(), false); for (auto &Param : BridgedCall->getArgumentOperands()) { unsigned CurIdx = Idx++; @@ -1062,6 +1114,8 @@ bool ObjCMethodCall::matchInstSequence(SILBasicBlock::iterator I) { BridgedArguments.push_back(BridgedArg); IsBridgedArgument.set(CurIdx); + if (BridgedArg.isGuaranteed()) + IsGuaranteedArgument.set(CurIdx); } // Try to match a bridged return value. @@ -1093,11 +1147,12 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { // Either use the bridged type passing it @owned. if (BridgedArgIdx < BridgedArguments.size() && BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) { + auto convention = BridgedArguments[BridgedArgIdx].getConvention(); Parameters.push_back(SILParameterInfo(BridgedArguments[BridgedArgIdx] .bridgedValue() ->getType() .getASTType(), - ParameterConvention::Direct_Owned)); + convention)); ++BridgedArgIdx; } else { // Otherwise, use the original type convention. @@ -1106,10 +1161,12 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { OrigSigIdx++; } - auto ExtInfo = - SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - /*pseudogeneric*/ false, - /*noescape*/ false); + auto ExtInfo = SILFunctionType::ExtInfo( + SILFunctionType::Representation::Thin, + /*pseudogeneric*/ false, + /*noescape*/ false, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr); SmallVector Results; // If we don't have a bridged return we changed from @autoreleased to @owned @@ -1117,7 +1174,7 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { if (!BridgedReturn) { if (FunTy->getNumResults()) { auto OrigResultInfo = FunTy->getSingleResult(); - Results.push_back(SILResultInfo(OrigResultInfo.getType(), + Results.push_back(SILResultInfo(OrigResultInfo.getInterfaceType(), OrigResultInfo.getConvention() == ResultConvention::Autoreleased ? ResultConvention::Owned @@ -1131,7 +1188,9 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { auto FunctionType = SILFunctionType::get( nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, {}, - Results, None, M.getASTContext()); + Results, None, + SubstitutionMap(), false, + M.getASTContext()); return FunctionType; } diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index 961c6b574198f..da6696bde8090 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-inliner" #include "swift/AST/Module.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/OptimizationRemark.h" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" @@ -278,7 +279,8 @@ bool SILPerformanceInliner::isProfitableToInline( // Don't inline class methods. if (Callee->hasSelfParam()) { - auto SelfTy = Callee->getLoweredFunctionType()->getSelfInstanceType(); + auto SelfTy = Callee->getLoweredFunctionType() + ->getSelfInstanceType(FuncBuilder.getModule()); if (SelfTy->mayHaveSuperclass() && Callee->getRepresentation() == SILFunctionTypeRepresentation::Method) isClassMethodAtOsize = true; @@ -643,7 +645,7 @@ bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI, static void addWeightCorrection(FullApplySite FAS, llvm::DenseMap &WeightCorrections) { SILFunction *Callee = FAS.getReferencedFunctionOrNull(); - if (Callee && Callee->hasSemanticsAttr("array.uninitialized")) { + if (Callee && Callee->hasSemanticsAttr(semantics::ARRAY_UNINITIALIZED)) { // We want to inline the argument to an array.uninitialized call, because // this argument is most likely a call to a function which contains the // buffer allocation for the array. It is essential to inline it for stack diff --git a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp index 51af6042e5ad4..d34370660a95b 100644 --- a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp +++ b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp @@ -147,11 +147,11 @@ static bool inline isPerformingRLE(RLEKind Kind) { static bool isRLEInertInstruction(SILInstruction *Inst) { switch (Inst->getKind()) { #define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Name##RetainInst: \ case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::StrongRetainInst: case SILInstructionKind::RetainValueInst: @@ -677,7 +677,8 @@ SILValue BlockState::reduceValuesAtEndOfBlock(RLEContext &Ctx, LSLocation &L) { LSLocationValueMap Values; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, Ctx.getTE()); // Find the values that this basic block defines and the locations which // we do not have a concrete value in the current basic block. @@ -689,8 +690,8 @@ SILValue BlockState::reduceValuesAtEndOfBlock(RLEContext &Ctx, LSLocation &L) { // Second, reduce the available values into a single SILValue we can use to // forward. SILValue TheForwardingValue; - TheForwardingValue = LSValue::reduce(L, &BB->getModule(), Values, - BB->getTerminator()); + TheForwardingValue = + LSValue::reduce(L, &BB->getModule(), Values, BB->getTerminator()); /// Return the forwarding value. return TheForwardingValue; } @@ -856,7 +857,9 @@ void BlockState::processWrite(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Expand the given location and val into individual fields and process // them as separate writes. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, + Ctx.getTE()); if (isComputeAvailSetMax(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { @@ -875,7 +878,8 @@ void BlockState::processWrite(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Are we computing available value or performing RLE? LSValueList Vals; - LSValue::expand(Val, &I->getModule(), Vals, Ctx.getTE()); + LSValue::expand(Val, &I->getModule(), TypeExpansionContext(*I->getFunction()), + Vals, Ctx.getTE()); if (isComputeAvailValue(Kind) || isPerformingRLE(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { updateForwardSetAndValForWrite(Ctx, Ctx.getLocationBit(Locs[i]), @@ -907,7 +911,9 @@ void BlockState::processRead(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Expand the given LSLocation and Val into individual fields and process // them as separate reads. LSLocationList Locs; - LSLocation::expand(L, &I->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &I->getModule(), + TypeExpansionContext(*I->getFunction()), Locs, + Ctx.getTE()); if (isComputeAvailSetMax(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { @@ -927,7 +933,8 @@ void BlockState::processRead(RLEContext &Ctx, SILInstruction *I, SILValue Mem, // Are we computing available values ?. bool CanForward = true; LSValueList Vals; - LSValue::expand(Val, &I->getModule(), Vals, Ctx.getTE()); + LSValue::expand(Val, &I->getModule(), TypeExpansionContext(*I->getFunction()), + Vals, Ctx.getTE()); if (isComputeAvailValue(Kind) || isPerformingRLE(Kind)) { for (unsigned i = 0; i < Locs.size(); ++i) { if (isTrackingLocation(ForwardSetIn, Ctx.getLocationBit(Locs[i]))) @@ -1245,7 +1252,8 @@ BlockState::ValueState BlockState::getValueStateAtEndOfBlock(RLEContext &Ctx, // expanded from the given location. unsigned CSCount = 0, CTCount = 0; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, Ctx.getTE()); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, Ctx.getTE()); ValueTableMap &OTM = getForwardValOut(); for (auto &X : Locs) { @@ -1319,12 +1327,15 @@ SILValue RLEContext::computePredecessorLocationValue(SILBasicBlock *BB, // Reduce the available values into a single SILValue we can use to forward SILInstruction *IPt = CurBB->getTerminator(); - Values.push_back({CurBB, LSValue::reduce(L, &BB->getModule(), LSValues, IPt)}); + Values.push_back( + {CurBB, LSValue::reduce(L, &BB->getModule(), LSValues, IPt)}); } // Finally, collect all the values for the SILArgument, materialize it using // the SSAUpdater. - Updater.Initialize(L.getType(&BB->getModule()).getObjectType()); + Updater.Initialize( + L.getType(&BB->getModule(), TypeExpansionContext(*BB->getParent())) + .getObjectType()); for (auto V : Values) { Updater.AddAvailableValue(V.first, V.second); } @@ -1337,7 +1348,8 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, ValueTableMap &VM) { LSLocationList CSLocs; LSLocationList Locs; - LSLocation::expand(L, &BB->getModule(), Locs, TE); + LSLocation::expand(L, &BB->getModule(), + TypeExpansionContext(*BB->getParent()), Locs, TE); auto *Mod = &BB->getModule(); // Find the locations that this basic block defines and the locations which @@ -1352,7 +1364,7 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, // For locations which we do not have concrete values for in this basic // block, try to reduce it to the minimum # of locations possible, this // will help us to generate as few SILArguments as possible. - LSLocation::reduce(L, Mod, CSLocs); + LSLocation::reduce(L, Mod, TypeExpansionContext(*BB->getParent()), CSLocs); // To handle covering value, we need to go to the predecessors and // materialize them there. @@ -1365,8 +1377,9 @@ bool RLEContext::collectLocationValues(SILBasicBlock *BB, LSLocation &L, // collect the newly created forwardable values. LSLocationList Locs; LSValueList Vals; - LSLocation::expand(X, Mod, Locs, TE); - LSValue::expand(V, Mod, Vals, TE); + auto expansionContext = TypeExpansionContext(*BB->getParent()); + LSLocation::expand(X, Mod, expansionContext, Locs, TE); + LSValue::expand(V, Mod, expansionContext, Vals, TE); for (unsigned i = 0; i < Locs.size(); ++i) { Values[Locs[i]] = Vals[i]; @@ -1570,7 +1583,8 @@ bool RLEContext::run() { LLVM_DEBUG(for (unsigned i = 0; i < LocationVault.size(); ++i) { llvm::dbgs() << "LSLocation #" << i; - getLocation(i).print(llvm::dbgs(), &Fn->getModule()); + getLocation(i).print(llvm::dbgs(), &Fn->getModule(), + TypeExpansionContext(*Fn)); }); if (Optimistic) diff --git a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp index e52ce639db7d0..3642e337053fc 100644 --- a/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp @@ -142,13 +142,14 @@ bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType, SILFunction *Dealloc = M.lookUpFunction(DeallocRef); if (!Dealloc) return false; - - CanSILFunctionType DeallocType = Dealloc->getLoweredFunctionType(); + TypeExpansionContext context(*ReleaseInst->getFunction()); + CanSILFunctionType DeallocType = + Dealloc->getLoweredFunctionTypeInContext(context); auto *NTD = AllocType.getASTType()->getAnyNominal(); auto AllocSubMap = AllocType.getASTType() ->getContextSubstitutionMap(M.getSwiftModule(), NTD); - DeallocType = DeallocType->substGenericArgs(M, AllocSubMap); + DeallocType = DeallocType->substGenericArgs(M, AllocSubMap, context); SILBuilder B(ReleaseInst); if (object->getType() != AllocType) diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index a732dc12d8b36..f688bc6921507 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -60,7 +60,8 @@ static void createRefCountOpForPayload(SILBuilder &Builder, SILInstruction *I, // argument to the refcount instruction. SILValue EnumVal = DefOfEnum ? DefOfEnum : I->getOperand(0); - SILType ArgType = EnumVal->getType().getEnumElementType(EnumDecl, Mod); + SILType ArgType = EnumVal->getType().getEnumElementType( + EnumDecl, Mod, TypeExpansionContext(Builder.getFunction())); auto *UEDI = Builder.createUncheckedEnumData(I->getLoc(), EnumVal, EnumDecl, ArgType); @@ -130,8 +131,7 @@ class BBEnumTagDataflowState BBEnumTagDataflowState(const BBEnumTagDataflowState &Other) = default; ~BBEnumTagDataflowState() = default; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; bool init(EnumCaseDataflowContext &Context, SILBasicBlock *NewBB); @@ -1261,7 +1261,7 @@ static bool sinkArgument(EnumCaseDataflowContext &Context, SILBasicBlock *BB, un TI->setOperand(ArgNum, CloneInst->getOperand(*DifferentOperandIndex)); // Now delete the clone as we only needed it operand. if (CloneInst != FSI) - recursivelyDeleteTriviallyDeadInstructions(CloneInst); + eliminateDeadInstruction(CloneInst); ++CloneIt; } assert(CloneIt == Clones.end() && "Clone/pred mismatch"); diff --git a/lib/SILOptimizer/Transforms/SILSROA.cpp b/lib/SILOptimizer/Transforms/SILSROA.cpp index eafc9d41b120f..dcfb8d6803e78 100644 --- a/lib/SILOptimizer/Transforms/SILSROA.cpp +++ b/lib/SILOptimizer/Transforms/SILSROA.cpp @@ -228,8 +228,9 @@ createAllocas(llvm::SmallVector &NewAllocations) { "this point."); SILModule &M = AI->getModule(); for (auto *D : SD->getStoredProperties()) - NewAllocations.push_back( - B.createAllocStack(Loc, Type.getFieldType(D, M), {})); + NewAllocations.push_back(B.createAllocStack( + Loc, Type.getFieldType(D, M, TypeExpansionContext(B.getFunction())), + {})); } } diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 4a4530142072a..9fba73ceb7c3b 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -274,12 +274,15 @@ class ThreadInfo { ThreadInfo() = default; - void threadEdge() { + bool threadEdge() { LLVM_DEBUG(llvm::dbgs() << "thread edge from bb" << Src->getDebugID() << " to bb" << Dest->getDebugID() << '\n'); auto *SrcTerm = cast(Src->getTerminator()); BasicBlockCloner Cloner(SrcTerm->getDestBB()); + if (!Cloner.canCloneBlock()) + return false; + Cloner.cloneBranchTarget(SrcTerm); // We have copied the threaded block into the edge. @@ -313,7 +316,8 @@ class ThreadInfo { auto EnumVal = SEI->getOperand(); auto EnumTy = EnumVal->getType(); auto Loc = SEI->getLoc(); - auto Ty = EnumTy.getEnumElementType(EnumCase, SEI->getModule()); + auto Ty = EnumTy.getEnumElementType(EnumCase, SEI->getModule(), + Builder.getTypeExpansionContext()); SILValue UED( Builder.createUncheckedEnumData(Loc, EnumVal, EnumCase, Ty)); assert(UED->getType() == @@ -328,7 +332,8 @@ class ThreadInfo { // After rewriting the cloned branch, split the critical edge. // This does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); - updateSSAAfterCloning(Cloner, Src, Dest); + Cloner.updateSSAAfterCloning(); + return true; } }; @@ -358,7 +363,8 @@ static SILValue createEnumElement(SILBuilder &Builder, // Do we have a payload. auto EnumTy = EnumVal->getType(); if (EnumElement->hasAssociatedValues()) { - auto Ty = EnumTy.getEnumElementType(EnumElement, SEI->getModule()); + auto Ty = EnumTy.getEnumElementType(EnumElement, SEI->getModule(), + Builder.getTypeExpansionContext()); SILValue UED(Builder.createUncheckedEnumData(SEI->getLoc(), EnumVal, EnumElement, Ty)); return Builder.createEnum(SEI->getLoc(), UED, EnumElement, EnumTy); @@ -549,8 +555,8 @@ bool SimplifyCFG::dominatorBasedSimplifications(SILFunction &Fn, return Changed; for (auto &ThreadInfo : JumpThreadableEdges) { - ThreadInfo.threadEdge(); - Changed = true; + if (ThreadInfo.threadEdge()) + Changed = true; } return Changed; @@ -781,7 +787,7 @@ static NullablePtr getEnumCase(SILValue Val, } static int getThreadingCost(SILInstruction *I) { - if (!isa(I) && !I->isTriviallyDuplicatable()) + if (!I->isTriviallyDuplicatable()) return 1000; // Don't jumpthread function calls. @@ -920,16 +926,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { if (DestBB->getTerminator()->isFunctionExiting()) return false; - // We need to update SSA if a value duplicated is used outside of the - // duplicated block. - bool NeedToUpdateSSA = false; - - // Are the arguments to this block used outside of the block. - for (auto Arg : DestBB->getArguments()) - if ((NeedToUpdateSSA |= isUsedOutsideOfBlock(Arg))) { - break; - } - // We don't have a great cost model at the SIL level, so we don't want to // blissly duplicate tons of code with a goal of improved performance (we'll // leave that to LLVM). However, doing limited code duplication can lead to @@ -976,57 +972,39 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { if (ThreadingBudget <= 0) return false; + // Don't jump thread through a potential header - this can produce irreducible + // control flow. Still, we make an exception for switch_enum. + bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); + if (DestIsLoopHeader) { + if (!isa(DestBB->getTerminator())) + return false; + } + // If it looks potentially interesting, decide whether we *can* do the // operation and whether the block is small enough to be worth duplicating. int copyCosts = 0; - SinkAddressProjections sinkProj; - for (auto ii = DestBB->begin(), ie = DestBB->end(); ii != ie;) { - copyCosts += getThreadingCost(&*ii); + BasicBlockCloner Cloner(DestBB); + for (auto &inst : *DestBB) { + copyCosts += getThreadingCost(&inst); if (ThreadingBudget <= copyCosts) return false; // If this is an address projection with outside uses, sink it before // checking for SSA update. - if (!sinkProj.analyzeAddressProjections(&*ii)) - return false; - - sinkProj.cloneProjections(); - // After cloning check if any of the non-address defs in the cloned block - // (including the current instruction) now have uses outside the - // block. Do this even if nothing was cloned. - if (!sinkProj.getInBlockDefs().empty()) - NeedToUpdateSSA = true; - - auto nextII = std::next(ii); - recursivelyDeleteTriviallyDeadInstructions( - &*ii, false, [&nextII](SILInstruction *deadInst) { - if (deadInst->getIterator() == nextII) - ++nextII; - }); - ii = nextII; - } - - // Don't jump thread through a potential header - this can produce irreducible - // control flow. Still, we make an exception for switch_enum. - bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); - if (DestIsLoopHeader) { - if (!isa(DestBB->getTerminator())) + if (!Cloner.canCloneInstruction(&inst)) return false; } - LLVM_DEBUG(llvm::dbgs() << "jump thread from bb" << SrcBB->getDebugID() << " to bb" << DestBB->getDebugID() << '\n'); JumpThreadingCost[DestBB] += copyCosts; - // Okay, it looks like we want to do this and we can. Duplicate the - // destination block into this one, rewriting uses of the BBArgs to use the - // branch arguments as we go. - BasicBlockCloner Cloner(DestBB); + // Duplicate the destination block into this one, rewriting uses of the BBArgs + // to use the branch arguments as we go. Cloner.cloneBranchTarget(BI); - // Does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); + Cloner.updateSSAAfterCloning(); // Once all the instructions are copied, we can nuke BI itself. We also add // the threaded and edge block to the worklist now that they (likely) can be @@ -1034,9 +1012,6 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { addToWorklist(SrcBB); addToWorklist(Cloner.getNewBB()); - if (NeedToUpdateSSA) - updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB); - // We may be able to simplify DestBB now that it has one fewer predecessor. simplifyAfterDroppingPredecessor(DestBB); @@ -1702,7 +1677,8 @@ bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) { auto &Mod = SEI->getModule(); auto OpndTy = SEI->getOperand()->getType(); - auto Ty = OpndTy.getEnumElementType(Element, Mod); + auto Ty = OpndTy.getEnumElementType( + Element, Mod, TypeExpansionContext(*SEI->getFunction())); auto *UED = SILBuilderWithScope(SEI) .createUncheckedEnumData(SEI->getLoc(), SEI->getOperand(), Element, Ty); @@ -2388,15 +2364,17 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) { auto TargetFnTy = CalleeFnTy; if (TargetFnTy->isPolymorphic()) { - TargetFnTy = TargetFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutionMap()); + TargetFnTy = TargetFnTy->substGenericArgs( + TAI->getModule(), TAI->getSubstitutionMap(), + Builder.getTypeExpansionContext()); } SILFunctionConventions targetConv(TargetFnTy, TAI->getModule()); auto OrigFnTy = TAI->getCallee()->getType().getAs(); if (OrigFnTy->isPolymorphic()) { OrigFnTy = OrigFnTy->substGenericArgs(TAI->getModule(), - TAI->getSubstitutionMap()); + TAI->getSubstitutionMap(), + Builder.getTypeExpansionContext()); } SILFunctionConventions origConv(OrigFnTy, TAI->getModule()); @@ -2737,21 +2715,23 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() { for (auto *BB : ObjCBlocks) { auto *Branch = cast(BB->getTerminator()); auto *DestBB = Branch->getDestBB(); - Changed = true; // Okay, it looks like we want to do this and we can. Duplicate the // destination block into this one, rewriting uses of the BBArgs to use the // branch arguments as we go. BasicBlockCloner Cloner(DestBB); + if (!Cloner.canCloneBlock()) + continue; + Cloner.cloneBranchTarget(Branch); // Does not currently update DominanceInfo. Cloner.splitCriticalEdges(nullptr, nullptr); + Cloner.updateSSAAfterCloning(); - updateSSAAfterCloning(Cloner, Cloner.getNewBB(), DestBB); + Changed = true; addToWorklist(Cloner.getNewBB()); } - return Changed; } @@ -2857,7 +2837,8 @@ bool ArgumentSplitter::createNewArguments() { return false; // Get the first level projection for the struct or tuple type. - Projection::getFirstLevelProjections(Arg->getType(), Mod, Projections); + Projection::getFirstLevelProjections(Arg->getType(), Mod, + TypeExpansionContext(*F), Projections); // We do not want to split arguments with less than 2 projections. if (Projections.size() < 2) @@ -2866,7 +2847,7 @@ bool ArgumentSplitter::createNewArguments() { // We do not want to split arguments that have less than 2 non-trivial // projections. if (count_if(Projections, [&](const Projection &P) { - return !P.getType(Ty, Mod).isTrivial(*F); + return !P.getType(Ty, Mod, TypeExpansionContext(*F)).isTrivial(*F); }) < 2) return false; @@ -2878,8 +2859,9 @@ bool ArgumentSplitter::createNewArguments() { // old one. llvm::SmallVector NewArgumentValues; for (auto &P : Projections) { - auto *NewArg = ParentBB->createPhiArgument(P.getType(Ty, Mod), - ValueOwnershipKind::Owned); + auto *NewArg = ParentBB->createPhiArgument( + P.getType(Ty, Mod, TypeExpansionContext(*F)), + ValueOwnershipKind::Owned); // This is unfortunate, but it feels wrong to put in an API into SILBuilder // that only takes in arguments. // diff --git a/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp b/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp deleted file mode 100644 index b8968e0414e09..0000000000000 --- a/lib/SILOptimizer/Transforms/SpecializeOpaqueArchetypes.cpp +++ /dev/null @@ -1,545 +0,0 @@ -//===--- SpecializeOpaqueArchetypes.cpp - Specialize opaque archetypes ---===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// A pass to specialize opaque archetypes -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "opaque-archetype-specializer" - -#include "swift/AST/Types.h" -#include "swift/SIL/SILFunction.h" -#include "swift/SIL/SILInstruction.h" -#include "swift/SIL/TypeSubstCloner.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" - -#include "llvm/Support/CommandLine.h" - -llvm::cl::opt - EnableOpaqueArchetypeSpecializer("enable-opaque-archetype-specializer", - llvm::cl::init(true)); - -using namespace swift; - -static Type substOpaqueTypesWithUnderlyingTypes( - Type ty, SILFunction *context) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context->getModule().getSwiftModule(), context->getResilienceExpansion()); - return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); -} - -static SubstitutionMap -substOpaqueTypesWithUnderlyingTypes(SubstitutionMap map, SILFunction *context) { - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context->getModule().getSwiftModule(), context->getResilienceExpansion()); - return map.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes); -} - -namespace { -class OpaqueSpecializerCloner - : public SILCloner { - - using SuperTy = SILCloner; - - SILBasicBlock *entryBlock; - SILBasicBlock *cloneFromBlock; - - /// Cache for substituted types. - llvm::DenseMap TypeCache; - - SILFunction &Original; - -public: - friend class SILCloner; - friend class SILCloner; - friend class SILInstructionVisitor; - - OpaqueSpecializerCloner(SILFunction &fun) : SuperTy(fun), Original(fun) { - entryBlock = fun.getEntryBlock(); - cloneFromBlock = entryBlock->split(entryBlock->begin()); - } - - void clone(); - -protected: - void insertOpaqueToConcreteAddressCasts(SILInstruction *orig, - SILInstruction *cloned); - - void postProcess(SILInstruction *orig, SILInstruction *cloned) { - SILCloner::postProcess(orig, cloned); - insertOpaqueToConcreteAddressCasts(orig, cloned); - } - - void visitTerminator(SILBasicBlock *BB) { - visit(BB->getTerminator()); - } - - void visitReturnInst(ReturnInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto origResult = Inst->getOperand(); - auto clonedResult = getOpValue(Inst->getOperand()); - if (clonedResult->getType().getASTType() != - origResult->getType().getASTType()) { - clonedResult = createCast(RegularLocation::getAutoGeneratedLocation(), - clonedResult, origResult->getType()); - } - recordClonedInstruction( - Inst, - getBuilder().createReturn(getOpLocation(Inst->getLoc()), clonedResult)); - } - - void visitStructInst(StructInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto elements = getOpValueArray<8>(Inst->getElements()); - auto structTy = getOpType(Inst->getType()); - auto *structDecl = structTy.getStructOrBoundGenericStruct(); - unsigned idx = 0; - // Adjust field types if neccessary. - for (VarDecl *field : structDecl->getStoredProperties()) { - SILType loweredType = structTy.getFieldType( - field, getBuilder().getFunction().getModule()); - if (elements[idx]->getType() != loweredType) { - elements[idx] = createCast(getOpLocation(Inst->getLoc()), elements[idx], - loweredType); - } - idx++; - } - recordClonedInstruction( - Inst, getBuilder().createStruct(getOpLocation(Inst->getLoc()), - getOpType(Inst->getType()), elements)); - } - - void visitTupleInst(TupleInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto elements = getOpValueArray<8>(Inst->getElements()); - auto tupleTy = getOpType(Inst->getType()); - for (size_t i = 0, size = Inst->getElements().size(); i < size; ++i) { - auto elementTy = tupleTy.getTupleElementType(i); - if (Inst->getElement(i)->getType() != elementTy) { - elements[i] = - createCast(getOpLocation(Inst->getLoc()), elements[i], elementTy); - } - } - recordClonedInstruction( - Inst, getBuilder().createTuple(getOpLocation(Inst->getLoc()), - getOpType(Inst->getType()), elements)); - } - - void visitEnumInst(EnumInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - SILValue opd = SILValue(); - auto newTy = getOpType(Inst->getType()); - if (Inst->hasOperand()) { - opd = getOpValue(Inst->getOperand()); - SILType newCaseTy = newTy.getEnumElementType( - Inst->getElement(), getBuilder().getFunction().getModule()); - if (opd->getType() != newCaseTy) - opd = createCast(getOpLocation(Inst->getLoc()), opd, newCaseTy); - } - recordClonedInstruction( - Inst, getBuilder().createEnum(getOpLocation(Inst->getLoc()), opd, - Inst->getElement(), newTy)); - } - - void visitInitEnumDataAddrInst(InitEnumDataAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - auto caseTy = opd->getType().getEnumElementType( - Inst->getElement(), getBuilder().getFunction().getModule()); - auto expectedTy = getOpType(Inst->getType()); - if (expectedTy != caseTy) - expectedTy = caseTy; - recordClonedInstruction(Inst, getBuilder().createInitEnumDataAddr( - getOpLocation(Inst->getLoc()), opd, - Inst->getElement(), expectedTy)); - } - - /// Projections should not change the type if the type is not specialized. - void visitStructElementAddrInst(StructElementAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - recordClonedInstruction( - Inst, getBuilder().createStructElementAddr( - getOpLocation(Inst->getLoc()), opd, Inst->getField())); - } - - /// Projections should not change the type if the type is not specialized. - void visitStructExtractInst(StructExtractInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto opd = getOpValue(Inst->getOperand()); - recordClonedInstruction( - Inst, getBuilder().createStructExtract(getOpLocation(Inst->getLoc()), - opd, Inst->getField())); - } - /// Projections should not change the type if the type is not specialized. - void visitTupleElementAddrInst(TupleElementAddrInst *Inst) { - auto opd = getOpValue(Inst->getOperand()); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction(Inst, getBuilder().createTupleElementAddr( - getOpLocation(Inst->getLoc()), opd, - Inst->getFieldNo())); - } - /// Projections should not change the type if the type is not specialized. - void visitTupleExtractInst(TupleExtractInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createTupleExtract(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - Inst->getFieldNo())); - } - /// Projections should not change the type if the type is not specialized. - void visitRefElementAddrInst(RefElementAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createRefElementAddr( - getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()), - Inst->getField())); - } - - /// Projections should not change the type if the type is not specialized. - void visitRefTailAddrInst(RefTailAddrInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createRefTailAddr(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), - Inst->getType())); - } - - void visitYieldInst(YieldInst *Inst) { - auto OrigValues = Inst->getYieldedValues(); - auto Values = getOpValueArray<8>(Inst->getYieldedValues()); - auto ResumeBB = getOpBasicBlock(Inst->getResumeBB()); - auto UnwindBB = getOpBasicBlock(Inst->getUnwindBB()); - for (auto idx : indices(Values)) { - if (OrigValues[idx]->getType().getASTType() != - Values[idx]->getType().getASTType()) { - Values[idx] = createCast(RegularLocation::getAutoGeneratedLocation(), - Values[idx], OrigValues[idx]->getType()); - } - } - - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createYield(getOpLocation(Inst->getLoc()), Values, - ResumeBB, UnwindBB)); - } - - void visitCopyAddrInst(CopyAddrInst *Inst) { - auto src = getOpValue(Inst->getSrc()); - auto dst = getOpValue(Inst->getDest()); - auto srcType = src->getType(); - auto destType = dst->getType(); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - // If the types mismatch cast the operands to the non opaque archetype. - if (destType.getASTType() != srcType.getASTType()) { - if (srcType.getASTType()->hasOpaqueArchetype()) { - src = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), src, destType); - } else if (destType.getASTType()->hasOpaqueArchetype()) { - dst = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), dst, srcType); - } - } - recordClonedInstruction( - Inst, getBuilder().createCopyAddr(getOpLocation(Inst->getLoc()), src, - dst, Inst->isTakeOfSrc(), - Inst->isInitializationOfDest())); - } - - SILValue remapResultType(SILLocation loc, SILValue val) { - auto specializedTy = remapType(val->getType()); - if (val->getType() == specializedTy) - return val; - return createCast(loc, val, specializedTy); - } - - void visitThinToThickFunctionInst(ThinToThickFunctionInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto loc = getOpLocation(Inst->getLoc()); - auto opd = remapResultType(loc, getOpValue(Inst->getOperand())); - recordClonedInstruction(Inst, getBuilder().createThinToThickFunction( - loc, opd, getOpType(Inst->getType()))); - } - - void visitStoreInst(StoreInst *Inst) { - auto src = getOpValue(Inst->getSrc()); - auto dst = getOpValue(Inst->getDest()); - auto srcType = src->getType(); - auto destType = dst->getType(); - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - // If the types mismatch cast the operands to the non opaque archetype. - if (destType.getASTType() != srcType.getASTType()) { - if (srcType.getASTType()->hasOpaqueArchetype()) { - assert(!srcType.isAddress()); - src = createCast(getOpLocation(Inst->getLoc()), src, - destType.getObjectType()); - } else if (destType.getASTType()->hasOpaqueArchetype()) { - dst = getBuilder().createUncheckedAddrCast( - getOpLocation(Inst->getLoc()), dst, srcType.getAddressType()); - } - } - - if (!getBuilder().hasOwnership()) { - switch (Inst->getOwnershipQualifier()) { - case StoreOwnershipQualifier::Assign: { - auto *li = getBuilder().createLoad(getOpLocation(Inst->getLoc()), dst, - LoadOwnershipQualifier::Unqualified); - auto *si = getBuilder().createStore( - getOpLocation(Inst->getLoc()), src, getOpValue(Inst->getDest()), - StoreOwnershipQualifier::Unqualified); - getBuilder().emitDestroyValueOperation(getOpLocation(Inst->getLoc()), - li); - return recordClonedInstruction(Inst, si); - } - case StoreOwnershipQualifier::Init: - case StoreOwnershipQualifier::Trivial: - case StoreOwnershipQualifier::Unqualified: - break; - } - - return recordClonedInstruction( - Inst, - getBuilder().createStore(getOpLocation(Inst->getLoc()), src, dst, - StoreOwnershipQualifier::Unqualified)); - } - - recordClonedInstruction( - Inst, getBuilder().createStore(getOpLocation(Inst->getLoc()), src, dst, - Inst->getOwnershipQualifier())); - } - -protected: - - SILType remapType(SILType Ty) { - SILType &Sty = TypeCache[Ty]; - if (Sty) - return Sty; - - // Apply the opaque types substitution. - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - Original.getModule().getSwiftModule(), - Original.getResilienceExpansion()); - Sty = Ty.subst(Original.getModule(), replacer, replacer, - CanGenericSignature(), true); - return Sty; - } - - CanType remapASTType(CanType ty) { - // Apply the opaque types substitution. - return substOpaqueTypesWithUnderlyingTypes(ty, &Original) - ->getCanonicalType(); - } - - ProtocolConformanceRef remapConformance(Type type, - ProtocolConformanceRef conf) { - // Apply the opaque types substitution. - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - Original.getModule().getSwiftModule(), - Original.getResilienceExpansion()); - return conf.subst(type, replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes); - } - - SubstitutionMap remapSubstitutionMap(SubstitutionMap Subs) { - // Apply the opaque types substitution. - return substOpaqueTypesWithUnderlyingTypes(Subs, &Original); - } - - SILValue createCast(SILLocation loc, SILValue opd, SILType type) { - auto &CurFn = getBuilder().getFunction(); - if (opd->getType().isAddress()) { - return getBuilder().createUncheckedAddrCast(loc, opd, type); - } else if (opd->getType().is()) { - return getBuilder().createConvertFunction( - loc, opd, type, /*withoutActuallyEscaping*/ false); - } else if (opd->getType().isTrivial(CurFn)) { - return getBuilder().createUncheckedTrivialBitCast(loc, opd, type); - } else if (opd->getType().canRefCast(opd->getType(), type, - CurFn.getModule())) { - return getBuilder().createUncheckedRefCast(loc, opd, type); - } else { - // This could be improved upon by recursively recomposing the type. - auto *stackLoc = getBuilder().createAllocStack(loc, type); - auto *addr = getBuilder().createUncheckedAddrCast( - loc, stackLoc, opd->getType().getAddressType()); - getBuilder().createTrivialStoreOr(loc, opd, addr, - StoreOwnershipQualifier::Init, true); - SILValue res = getBuilder().createTrivialLoadOr( - loc, stackLoc, LoadOwnershipQualifier::Take, true); - getBuilder().createDeallocStack(loc, stackLoc); - return res; - } - } - - void replaceBlockArgumentType(SILLocation loc, SILBasicBlock *destBlock, - SILType withType) { - assert(destBlock->getArguments().size() == 1); - - auto origType = (*destBlock->args_begin())->getType(); - auto origPhi = destBlock->getPhiArguments()[0]; - SILValue undef = SILUndef::get(origType, getBuilder().getFunction()); - SmallVector useList(origPhi->use_begin(), origPhi->use_end()); - for (auto *use : useList) { - use->set(undef); - } - - auto *newPhi = - destBlock->replacePhiArgument(0, withType, origPhi->getOwnershipKind()); - - getBuilder().setInsertionPoint(destBlock->begin()); - auto cast = createCast(loc, newPhi, origType); - for (auto *use : useList) { - use->set(cast); - } - } - - void fixUp(SILFunction *) { - auto &clonedFunction = getBuilder().getFunction(); - for (auto &BB : clonedFunction) { - for (auto &cloned : BB) { - // Fix up the type of try_apply successor block arguments. - if (auto *tryApply = dyn_cast(&cloned)) { - auto normalBB = tryApply->getNormalBB(); - SILFunctionConventions calleeConv( - tryApply->getSubstCalleeType(), - tryApply->getFunction()->getModule()); - auto normalBBType = (*normalBB->args_begin())->getType(); - auto applyResultType = calleeConv.getSILResultType(); - if (normalBBType != calleeConv.getSILResultType()) { - replaceBlockArgumentType(tryApply->getLoc(), normalBB, applyResultType); - } - } - // Fix up the type of switch_enum successor block arguments. - if (auto *switchEnum = dyn_cast(&cloned)) { - SILType enumTy = switchEnum->getOperand()->getType(); - for (unsigned i = 0, e = switchEnum->getNumCases(); i < e; ++i) { - EnumElementDecl *elt; - SILBasicBlock *dest; - std::tie(elt, dest) = switchEnum->getCase(i); - - if (elt->hasAssociatedValues() && - dest->getArguments().size() == 1) { - SILType eltArgTy = - enumTy.getEnumElementType(elt, clonedFunction.getModule()); - SILType bbArgTy = dest->getArguments()[0]->getType(); - if (eltArgTy != bbArgTy) - replaceBlockArgumentType(switchEnum->getLoc(), dest, eltArgTy); - - } - } - } - } - } - } -}; -} // namespace - -void OpaqueSpecializerCloner::clone() { - for (auto arg: entryBlock->getArguments()) - recordFoldedValue(arg, arg); - cloneReachableBlocks(cloneFromBlock, {}, entryBlock, - true /*havePrepopulatedFunctionArgs*/); - getBuilder().setInsertionPoint(entryBlock); - getBuilder().createBranch(RegularLocation::getAutoGeneratedLocation(), - getOpBasicBlock(cloneFromBlock)); -} - -/// Update address uses of the opaque type archetype with the concrete type. -/// This is neccessary for apply instructions. -void OpaqueSpecializerCloner::insertOpaqueToConcreteAddressCasts( - SILInstruction *orig, SILInstruction *cloned) { - - // Replace apply operands. - if (auto apply = ApplySite::isa(cloned)) { - SavedInsertionPointRAII restore(getBuilder()); - getBuilder().setInsertionPoint(apply.getInstruction()); - auto substConv = apply.getSubstCalleeConv(); - unsigned idx = 0; - for (auto &opd : apply.getArgumentOperands()) { - auto argIdx = apply.getCalleeArgIndex(opd); - auto argType = substConv.getSILArgumentType(argIdx); - if (argType.getASTType() != opd.get()->getType().getASTType()) { - opd.set(createCast(apply.getLoc(), opd.get(), argType)); - } - ++idx; - } - } -} - -namespace { -class OpaqueArchetypeSpecializer : public SILFunctionTransform { - void run() override { - if (!EnableOpaqueArchetypeSpecializer) - return; - - auto *context = getFunction(); - - if (!context->shouldOptimize()) - return; - - auto opaqueArchetypeWouldChange = [=](CanType ty) -> bool { - if (!ty->hasOpaqueArchetype()) - return false; - - return ty.findIf([=](Type type) -> bool { - if (auto opaqueTy = type->getAs()) { - auto opaque = opaqueTy->getDecl(); - return ReplaceOpaqueTypesWithUnderlyingTypes:: - shouldPerformSubstitution(opaque, - context->getModule().getSwiftModule(), - context->getResilienceExpansion()); - } - return false; - }); - }; - - // Look for opaque type archetypes. - bool foundOpaqueArchetype = false; - for (auto &BB : *getFunction()) { - for (auto &inst : BB) { - auto hasOpaqueOperand = [&] (SILInstruction &inst) -> bool { - // Check the operands for opaque types. - for (auto &opd : inst.getAllOperands()) - if (opaqueArchetypeWouldChange(opd.get()->getType().getASTType())) - return true; - return false; - }; - if ((foundOpaqueArchetype = hasOpaqueOperand(inst))) - break; - auto hasOpaqueResult = [&](SILInstruction &inst) -> bool { - // Check the results for opaque types. - for (const auto &res : inst.getResults()) - if (opaqueArchetypeWouldChange(res->getType().getASTType())) - return true; - return false; - }; - if ((foundOpaqueArchetype = hasOpaqueResult(inst))) - break; - } - if (foundOpaqueArchetype) - break; - } - - if (foundOpaqueArchetype) { - OpaqueSpecializerCloner s(*getFunction()); - s.clone(); - removeUnreachableBlocks(*getFunction()); - invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); - } - } -}; -} // end anonymous namespace - -SILTransform *swift::createOpaqueArchetypeSpecializer() { - return new OpaqueArchetypeSpecializer(); -} diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index 4bcc3d7f04330..2ca01e247fd21 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -81,14 +81,20 @@ static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) { } // A utility function for cloning the apply instruction. -static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { +static FullApplySite CloneApply(FullApplySite AI, SILValue SelfArg, + SILBuilder &Builder) { // Clone the Apply. Builder.setCurrentDebugScope(AI.getDebugScope()); Builder.addOpenedArchetypeOperands(AI.getInstruction()); auto Args = AI.getArguments(); SmallVector Ret(Args.size()); - for (unsigned i = 0, e = Args.size(); i != e; ++i) - Ret[i] = Args[i]; + for (unsigned i = 0, e = Args.size(); i != e; ++i) { + if (i == e - 1 && SelfArg) { + Ret[i] = SelfArg; + } else { + Ret[i] = Args[i]; + } + } FullApplySite NAI; @@ -160,8 +166,7 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SILType::getPrimitiveObjectType(SubType), - Iden, - Virt); + SubType, Iden, Virt); It = CCBI->getIterator(); SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction()); @@ -170,8 +175,8 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. - FullApplySite IdenAI = CloneApply(AI, IdenBuilder); - FullApplySite VirtAI = CloneApply(AI, VirtBuilder); + FullApplySite IdenAI = CloneApply(AI, DownCastedClassInstance, IdenBuilder); + FullApplySite VirtAI = CloneApply(AI, SILValue(), VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. @@ -192,7 +197,7 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILArgument *Arg = Continue->createPhiArgument(AI.getType(), ValueOwnershipKind::Owned); if (!isa(AI)) { - if (AI.getSubstCalleeType()->isNoReturnFunction()) { + if (AI.getSubstCalleeType()->isNoReturnFunction(F->getModule())) { IdenBuilder.createUnreachable(AI.getLoc()); VirtBuilder.createUnreachable(AI.getLoc()); } else { @@ -563,7 +568,7 @@ static bool tryToSpeculateTarget(FullApplySite AI, ClassHierarchyAnalysis *CHA, SILBuilderWithScope B(LastCCBI); auto CastedValue = B.createUncheckedBitCast(LastCCBI->getLoc(), LastCCBI->getOperand(), - LastCCBI->getCastType()); + LastCCBI->getTargetLoweredType()); B.createBranch(LastCCBI->getLoc(), LastCCBI->getSuccessBB(), {CastedValue}); LastCCBI->eraseFromParent(); ORE.emit(RB); @@ -591,6 +596,7 @@ namespace { void run() override { auto &CurFn = *getFunction(); + // Don't perform speculative devirtualization at -Os. if (CurFn.optimizeForSize()) return; @@ -619,6 +625,8 @@ namespace { Changed |= tryToSpeculateTarget(AI, CHA, ORE); if (Changed) { + CurFn.getModule().linkFunction(&CurFn, SILModule::LinkingMode::LinkAll); + invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } diff --git a/lib/SILOptimizer/Transforms/StackPromotion.cpp b/lib/SILOptimizer/Transforms/StackPromotion.cpp index b0243dc319840..a7579edc39094 100644 --- a/lib/SILOptimizer/Transforms/StackPromotion.cpp +++ b/lib/SILOptimizer/Transforms/StackPromotion.cpp @@ -108,32 +108,20 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA, return false; auto *ConGraph = EA->getConnectionGraph(ARI->getFunction()); - auto *Node = ConGraph->getNodeOrNull(ARI, EA); - if (!Node) + auto *contentNode = ConGraph->getValueContent(ARI); + if (!contentNode) return false; // The most important check: does the object escape the current function? - if (Node->escapes()) + if (contentNode->escapes()) return false; LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI); // Collect all use-points of the allocation. These are refcount instructions // and apply instructions. - llvm::SmallVector BaseUsePoints; llvm::SmallVector UsePoints; - ConGraph->getUsePoints(Node, BaseUsePoints); - for (SILNode *UsePoint : BaseUsePoints) { - if (SILInstruction *I = dyn_cast(UsePoint)) { - UsePoints.push_back(I); - } else { - // Also block arguments can be use points. - SILBasicBlock *UseBB = cast(UsePoint)->getParent(); - // For simplicity we just add the first instruction of the block as use - // point. - UsePoints.push_back(&UseBB->front()); - } - } + ConGraph->getUsePoints(contentNode, UsePoints); ValueLifetimeAnalysis VLA(ARI, UsePoints); // Check if there is a use point before the allocation (this can happen e.g. diff --git a/lib/SILOptimizer/UtilityPasses/BasicCalleePrinter.cpp b/lib/SILOptimizer/UtilityPasses/BasicCalleePrinter.cpp index cce51c87a088b..fcd63ed00f6db 100644 --- a/lib/SILOptimizer/UtilityPasses/BasicCalleePrinter.cpp +++ b/lib/SILOptimizer/UtilityPasses/BasicCalleePrinter.cpp @@ -40,12 +40,7 @@ class BasicCalleePrinterPass : public SILModuleTransform { llvm::outs() << *FAS.getInstruction(); auto Callees = BCA->getCalleeList(FAS); - llvm::outs() << "Incomplete callee list? : " - << (Callees.isIncomplete() ? "Yes" : "No") << "\n"; - llvm::outs() << "Known callees:\n"; - for (auto *CalleeFn : Callees) - llvm::outs() << CalleeFn->getName() << "\n"; - llvm::outs() << "\n"; + Callees.print(llvm::outs()); } /// The entry point to the transformation. diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index 5edf2ff41679c..c01d47a8ff74b 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -83,12 +83,15 @@ class BugReducerTester : public SILFunctionTransform { ResultInfoArray.push_back( SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); auto FuncType = SILFunctionType::get( - nullptr, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, - false /*isPseudoGeneric*/, - false /*noescape*/), + nullptr, + SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin, + false /*isPseudoGeneric*/, false /*noescape*/, + DifferentiabilityKind::NonDifferentiable, + nullptr /*clangFunctionType*/), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, - ArrayRef(), ArrayRef(), - ResultInfoArray, None, getFunction()->getModule().getASTContext()); + ArrayRef(), ArrayRef(), ResultInfoArray, + None, SubstitutionMap(), false, + getFunction()->getModule().getASTContext()); SILOptFunctionBuilder FunctionBuilder(*this); SILFunction *F = FunctionBuilder.getOrCreateSharedFunction( diff --git a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp index d12e9b016a2e8..9d91a3b0b49bd 100644 --- a/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp +++ b/lib/SILOptimizer/UtilityPasses/ConstantEvaluableSubsetChecker.cpp @@ -29,7 +29,6 @@ using namespace swift; namespace { -static const StringRef constantEvaluableSemanticsAttr = "constant_evaluable"; static const StringRef testDriverSemanticsAttr = "test_driver"; template @@ -83,8 +82,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { Optional nextInstOpt; Optional errorVal; - if (!applyInst || !callee || - !callee->hasSemanticsAttr(constantEvaluableSemanticsAttr)) { + if (!applyInst || !callee || !isConstantEvaluable(callee)) { // Ignore these instructions if we had a fatal error already. if (previousEvaluationHadFatalError) { @@ -148,7 +146,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { SILModule &calleeModule = callee->getModule(); if (callee->isAvailableExternally() && - callee->hasSemanticsAttr(constantEvaluableSemanticsAttr) && + hasConstantEvaluableAnnotation(callee) && callee->getOptimizationMode() != OptimizationMode::NoOptimization) { diagnose(calleeModule.getASTContext(), callee->getLocation().getSourceLoc(), @@ -164,7 +162,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform { for (SILFunction &fun : *module) { // Record functions annotated as constant evaluable. - if (fun.hasSemanticsAttr(constantEvaluableSemanticsAttr)) { + if (hasConstantEvaluableAnnotation(&fun)) { constantEvaluableFunctions.insert(&fun); continue; } diff --git a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp index ba1e5ec73c943..da470ec37d705 100644 --- a/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp @@ -11,14 +11,37 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dump-ea" -#include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/Analysis/EscapeAnalysis.h" +#include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "llvm/Support/CommandLine.h" using namespace swift; +// For manual debugging, dump graphs in DOT format. +llvm::cl::opt EnableGraphWriter( + "escapes-enable-graphwriter", llvm::cl::init(false), + llvm::cl::desc("With -escapes-dump, also write .dot files.")); + namespace { +static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn, + std::vector &Values) { + for (auto &BB : Fn) { + for (auto *Arg : BB.getArguments()) { + if (EA->isPointer(Arg)) + Values.push_back(SILValue(Arg)); + } + for (auto &II : BB) { + for (auto result : II.getResults()) { + if (EA->isPointer(result)) + Values.push_back(result); + } + } + } + return Values.size() > 1; +} + /// Dumps the escape information of all functions in the module. /// Only dumps if the compiler is built with assertions. /// For details see EscapeAnalysis. @@ -35,6 +58,35 @@ class EscapeAnalysisDumper : public SILModuleTransform { if (!F.isExternalDeclaration()) { auto *ConnectionGraph = EA->getConnectionGraph(&F); ConnectionGraph->print(llvm::outs()); + if (EnableGraphWriter) + ConnectionGraph->dumpCG(); + + // Gather up all Values in Fn. + std::vector Values; + if (!gatherValues(EA, F, Values)) + continue; + + // Emit the N^2 escape analysis evaluation of the values. + for (auto &bb : F) { + for (auto &ii : bb) { + if (auto fas = FullApplySite::isa(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, fas); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + if (RefCountingInst *rci = dyn_cast(&ii)) { + for (unsigned i = 0, e = Values.size(); i != e; ++i) { + SILValue val = Values[i]; + bool escape = EA->canEscapeTo(val, rci); + llvm::outs() << (escape ? "May" : "No") << "Escape: " << val + << " to " << ii; + } + } + } + } } } #endif diff --git a/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp b/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp index 9c5ac3a6e8e42..e30cbc9cce79f 100644 --- a/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp +++ b/lib/SILOptimizer/UtilityPasses/LSLocationPrinter.cpp @@ -78,12 +78,14 @@ class LSLocationPrinter : public SILModuleTransform { SILValue V = LI->getOperand(); // This is an address type, take it object type. SILType Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else if (auto *SI = dyn_cast(&II)) { SILValue V = SI->getDest(); // This is an address type, take it object type. SILType Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else { // Not interested in these instructions yet. continue; @@ -91,7 +93,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &T : PPList) { - T.getValue().print(llvm::outs(), *M); + T.getValue().print(llvm::outs(), *M, TypeExpansionContext(Fn)); } PPList.clear(); } @@ -111,12 +113,14 @@ class LSLocationPrinter : public SILModuleTransform { V = LI->getOperand(); // This is an address type, take it object type. Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else if (auto *SI = dyn_cast(&II)) { V = SI->getDest(); // This is an address type, take it object type. Ty = V->getType().getObjectType(); - ProjectionPath::expandTypeIntoLeafProjectionPaths(Ty, M, PPList); + ProjectionPath::expandTypeIntoLeafProjectionPaths( + Ty, M, TypeExpansionContext(Fn), PPList); } else { // Not interested in these instructions yet. continue; @@ -124,7 +128,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &T : PPList) { - T.getValue().print(llvm::outs(), *M); + T.getValue().print(llvm::outs(), *M, TypeExpansionContext(Fn)); } PPList.clear(); } @@ -150,14 +154,16 @@ class LSLocationPrinter : public SILModuleTransform { L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else if (auto *SI = dyn_cast(&II)) { SILValue Mem = SI->getDest(); SILValue UO = getUnderlyingObject(Mem); L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else { // Not interested in these instructions yet. continue; @@ -165,7 +171,7 @@ class LSLocationPrinter : public SILModuleTransform { llvm::outs() << "#" << Counter++ << II; for (auto &Loc : Locs) { - Loc.print(llvm::outs(), &Fn.getModule()); + Loc.print(llvm::outs(), &Fn.getModule(), TypeExpansionContext(Fn)); } Locs.clear(); } @@ -194,14 +200,16 @@ class LSLocationPrinter : public SILModuleTransform { L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else if (auto *SI = dyn_cast(&II)) { SILValue Mem = SI->getDest(); SILValue UO = getUnderlyingObject(Mem); L.init(UO, ProjectionPath::getProjectionPath(UO, Mem)); if (!L.isValid()) continue; - LSLocation::expand(L, &Fn.getModule(), Locs, TE); + LSLocation::expand(L, &Fn.getModule(), TypeExpansionContext(Fn), Locs, + TE); } else { // Not interested in these instructions yet. continue; @@ -216,10 +224,10 @@ class LSLocationPrinter : public SILModuleTransform { } // This should get the original (unexpanded) location back. - LSLocation::reduce(L, &Fn.getModule(), SLocs); + LSLocation::reduce(L, &Fn.getModule(), TypeExpansionContext(Fn), SLocs); llvm::outs() << "#" << Counter++ << II; for (auto &Loc : SLocs) { - Loc.print(llvm::outs(), &Fn.getModule()); + Loc.print(llvm::outs(), &Fn.getModule(), TypeExpansionContext(Fn)); } L.reset(); Locs.clear(); diff --git a/lib/SILOptimizer/UtilityPasses/NonInlinableFunctionSkippingChecker.cpp b/lib/SILOptimizer/UtilityPasses/NonInlinableFunctionSkippingChecker.cpp index 402e275f39591..270dcd1fcad4e 100644 --- a/lib/SILOptimizer/UtilityPasses/NonInlinableFunctionSkippingChecker.cpp +++ b/lib/SILOptimizer/UtilityPasses/NonInlinableFunctionSkippingChecker.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/LLVM.h" +#include "swift/AST/Module.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" @@ -83,7 +84,7 @@ class NonInlinableFunctionSkippingChecker : public SILModuleTransform { return; // Skip this verification for SwiftOnoneSupport - if (getModule()->isOptimizedOnoneSupportModule()) + if (getModule()->getSwiftModule()->isOnoneSupportModule()) return; for (auto &F : *getModule()) { diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 31703885545fa..e226949cc47e1 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -12,19 +12,407 @@ #define DEBUG_TYPE "serialize-sil" #include "swift/Strings.h" +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILCloner.h" +#include "swift/SIL/SILFunction.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" using namespace swift; +namespace { +/// In place map opaque archetypes to their underlying type in a function. +/// This needs to happen when a function changes from serializable to not +/// serializable. +class MapOpaqueArchetypes : public SILCloner { + using SuperTy = SILCloner; + + SILBasicBlock *origEntryBlock; + SILBasicBlock *clonedEntryBlock; +public: + friend class SILCloner; + friend class SILCloner; + friend class SILInstructionVisitor; + + MapOpaqueArchetypes(SILFunction &fun) : SuperTy(fun) { + origEntryBlock = fun.getEntryBlock(); + clonedEntryBlock = fun.createBasicBlock(); + } + + SILType remapType(SILType Ty) { + if (!Ty.getASTType()->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return Ty; + + return getBuilder().getTypeLowering(Ty).getLoweredType().getCategoryType( + Ty.getCategory()); + } + + CanType remapASTType(CanType ty) { + if (!ty->hasOpaqueArchetype() || + !getBuilder() + .getTypeExpansionContext() + .shouldLookThroughOpaqueTypeArchetypes()) + return ty; + // Remap types containing opaque result types in the current context. + return getBuilder() + .getTypeLowering(SILType::getPrimitiveObjectType(ty)) + .getLoweredType() + .getASTType(); + } + + ProtocolConformanceRef remapConformance(Type ty, + ProtocolConformanceRef conf) { + auto context = getBuilder().getTypeExpansionContext(); + auto conformance = conf; + if (ty->hasOpaqueArchetype() && + context.shouldLookThroughOpaqueTypeArchetypes()) { + conformance = + substOpaqueTypesWithUnderlyingTypes(conformance, ty, context); + } + return conformance; + } + + void replace(); +}; +} // namespace + +void MapOpaqueArchetypes::replace() { + // Map the function arguments. + SmallVector entryArgs; + entryArgs.reserve(origEntryBlock->getArguments().size()); + for (auto &origArg : origEntryBlock->getArguments()) { + SILType mappedType = remapType(origArg->getType()); + auto *NewArg = clonedEntryBlock->createFunctionArgument( + mappedType, origArg->getDecl(), true); + entryArgs.push_back(NewArg); + } + + getBuilder().setInsertionPoint(clonedEntryBlock); + auto &fn = getBuilder().getFunction(); + cloneFunctionBody(&fn, clonedEntryBlock, entryArgs, + true /*replaceOriginalFunctionInPlace*/); + // Insert the new entry block at the beginning. + fn.getBlocks().splice(fn.getBlocks().begin(), fn.getBlocks(), + clonedEntryBlock); + removeUnreachableBlocks(fn); +} + +static bool opaqueArchetypeWouldChange(TypeExpansionContext context, + CanType ty) { + if (!ty->hasOpaqueArchetype()) + return false; + + return ty.findIf([=](Type type) -> bool { + if (auto opaqueTy = type->getAs()) { + auto opaque = opaqueTy->getDecl(); + auto module = context.getContext()->getParentModule(); + OpaqueSubstitutionKind subKind = + ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( + opaque, module, context.getResilienceExpansion()); + return subKind != OpaqueSubstitutionKind::DontSubstitute; + } + return false; + }); +} + +static bool hasOpaqueArchetypeOperand(TypeExpansionContext context, + SILInstruction &inst) { + // Check the operands for opaque types. + for (auto &opd : inst.getAllOperands()) + if (opaqueArchetypeWouldChange(context, opd.get()->getType().getASTType())) + return true; + return false; +} + +static bool hasOpaqueArchetypeResult(TypeExpansionContext context, + SILInstruction &inst) { + // Check the results for opaque types. + for (const auto res : inst.getResults()) + if (opaqueArchetypeWouldChange(context, res->getType().getASTType())) + return true; + return false; +} + +static bool hasOpaqueArchetype(TypeExpansionContext context, + SILInstruction &inst) { + // Check operands and results. + if (hasOpaqueArchetypeOperand(context, inst)) + return true; + if (hasOpaqueArchetypeResult(context, inst)) + return true; + + // Check substitution maps. + switch (inst.getKind()) { + case SILInstructionKind::AllocStackInst: + case SILInstructionKind::AllocRefInst: + case SILInstructionKind::AllocRefDynamicInst: + case SILInstructionKind::AllocValueBufferInst: + case SILInstructionKind::AllocBoxInst: + case SILInstructionKind::AllocExistentialBoxInst: + case SILInstructionKind::IndexAddrInst: + case SILInstructionKind::TailAddrInst: + case SILInstructionKind::IndexRawPointerInst: + case SILInstructionKind::FunctionRefInst: + case SILInstructionKind::DynamicFunctionRefInst: + case SILInstructionKind::PreviousDynamicFunctionRefInst: + case SILInstructionKind::GlobalAddrInst: + case SILInstructionKind::GlobalValueInst: + case SILInstructionKind::IntegerLiteralInst: + case SILInstructionKind::FloatLiteralInst: + case SILInstructionKind::StringLiteralInst: + case SILInstructionKind::ClassMethodInst: + case SILInstructionKind::SuperMethodInst: + case SILInstructionKind::ObjCMethodInst: + case SILInstructionKind::ObjCSuperMethodInst: + case SILInstructionKind::WitnessMethodInst: + case SILInstructionKind::UpcastInst: + case SILInstructionKind::AddressToPointerInst: + case SILInstructionKind::PointerToAddressInst: + case SILInstructionKind::UncheckedRefCastInst: + case SILInstructionKind::UncheckedAddrCastInst: + case SILInstructionKind::UncheckedTrivialBitCastInst: + case SILInstructionKind::UncheckedBitwiseCastInst: + case SILInstructionKind::RefToRawPointerInst: + case SILInstructionKind::RawPointerToRefInst: +#define LOADABLE_REF_STORAGE(Name, ...) \ + case SILInstructionKind::RefTo##Name##Inst: \ + case SILInstructionKind::Name##ToRefInst: +#include "swift/AST/ReferenceStorage.def" +#undef LOADABLE_REF_STORAGE_HELPER + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::ConvertEscapeToNoEscapeInst: + case SILInstructionKind::ThinFunctionToPointerInst: + case SILInstructionKind::PointerToThinFunctionInst: + case SILInstructionKind::RefToBridgeObjectInst: + case SILInstructionKind::BridgeObjectToRefInst: + case SILInstructionKind::BridgeObjectToWordInst: + case SILInstructionKind::ThinToThickFunctionInst: + case SILInstructionKind::ThickToObjCMetatypeInst: + case SILInstructionKind::ObjCToThickMetatypeInst: + case SILInstructionKind::ObjCMetatypeToObjectInst: + case SILInstructionKind::ObjCExistentialMetatypeToObjectInst: + case SILInstructionKind::UnconditionalCheckedCastValueInst: + case SILInstructionKind::UnconditionalCheckedCastInst: + case SILInstructionKind::ClassifyBridgeObjectInst: + case SILInstructionKind::ValueToBridgeObjectInst: + case SILInstructionKind::MarkDependenceInst: + case SILInstructionKind::CopyBlockInst: + case SILInstructionKind::CopyBlockWithoutEscapingInst: + case SILInstructionKind::CopyValueInst: +#define UNCHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongCopy##Name##ValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongCopy##Name##ValueInst: +#include "swift/AST/ReferenceStorage.def" +#undef UNCHECKED_REF_STORAGE +#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::UncheckedOwnershipConversionInst: + case SILInstructionKind::IsUniqueInst: + case SILInstructionKind::IsEscapingClosureInst: + case SILInstructionKind::LoadInst: + case SILInstructionKind::LoadBorrowInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::StoreBorrowInst: + case SILInstructionKind::BeginAccessInst: +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ + case SILInstructionKind::Load##Name##Inst: +#include "swift/AST/ReferenceStorage.def" +#undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::ProjectValueBufferInst: + case SILInstructionKind::ProjectBoxInst: + case SILInstructionKind::ProjectExistentialBoxInst: + case SILInstructionKind::BuiltinInst: + case SILInstructionKind::MetatypeInst: + case SILInstructionKind::ValueMetatypeInst: + case SILInstructionKind::ExistentialMetatypeInst: + case SILInstructionKind::ObjCProtocolInst: + case SILInstructionKind::ObjectInst: + case SILInstructionKind::TupleInst: + case SILInstructionKind::TupleExtractInst: + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::StructInst: + case SILInstructionKind::StructExtractInst: + case SILInstructionKind::StructElementAddrInst: + case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefTailAddrInst: + case SILInstructionKind::EnumInst: + case SILInstructionKind::UncheckedEnumDataInst: + case SILInstructionKind::InitEnumDataAddrInst: + case SILInstructionKind::UncheckedTakeEnumDataAddrInst: + case SILInstructionKind::SelectEnumInst: + case SILInstructionKind::SelectEnumAddrInst: + case SILInstructionKind::SelectValueInst: + case SILInstructionKind::InitExistentialAddrInst: + case SILInstructionKind::InitExistentialValueInst: + case SILInstructionKind::OpenExistentialAddrInst: + case SILInstructionKind::InitExistentialRefInst: + case SILInstructionKind::OpenExistentialRefInst: + case SILInstructionKind::InitExistentialMetatypeInst: + case SILInstructionKind::OpenExistentialMetatypeInst: + case SILInstructionKind::OpenExistentialBoxInst: + case SILInstructionKind::OpenExistentialValueInst: + case SILInstructionKind::OpenExistentialBoxValueInst: + case SILInstructionKind::ProjectBlockStorageInst: + case SILInstructionKind::InitBlockStorageHeaderInst: + case SILInstructionKind::KeyPathInst: + case SILInstructionKind::UnreachableInst: + case SILInstructionKind::ReturnInst: + case SILInstructionKind::ThrowInst: + case SILInstructionKind::YieldInst: + case SILInstructionKind::UnwindInst: + case SILInstructionKind::BranchInst: + case SILInstructionKind::CondBranchInst: + case SILInstructionKind::SwitchValueInst: + case SILInstructionKind::SwitchEnumInst: + case SILInstructionKind::SwitchEnumAddrInst: + case SILInstructionKind::DynamicMethodBranchInst: + case SILInstructionKind::CheckedCastBranchInst: + case SILInstructionKind::CheckedCastAddrBranchInst: + case SILInstructionKind::CheckedCastValueBranchInst: + case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::DeallocRefInst: + case SILInstructionKind::DeallocPartialRefInst: + case SILInstructionKind::DeallocValueBufferInst: + case SILInstructionKind::DeallocBoxInst: + case SILInstructionKind::DeallocExistentialBoxInst: + case SILInstructionKind::StrongRetainInst: + case SILInstructionKind::StrongReleaseInst: + case SILInstructionKind::UnmanagedRetainValueInst: + case SILInstructionKind::UnmanagedReleaseValueInst: + case SILInstructionKind::UnmanagedAutoreleaseValueInst: +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::Name##ReleaseInst: +#include "swift/AST/ReferenceStorage.def" +#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE + case SILInstructionKind::RetainValueInst: + case SILInstructionKind::RetainValueAddrInst: + case SILInstructionKind::ReleaseValueInst: + case SILInstructionKind::ReleaseValueAddrInst: + case SILInstructionKind::SetDeallocatingInst: + case SILInstructionKind::AutoreleaseValueInst: + case SILInstructionKind::BindMemoryInst: + case SILInstructionKind::FixLifetimeInst: + case SILInstructionKind::DestroyValueInst: + case SILInstructionKind::EndBorrowInst: + case SILInstructionKind::EndAccessInst: + case SILInstructionKind::BeginUnpairedAccessInst: + case SILInstructionKind::EndUnpairedAccessInst: + case SILInstructionKind::StoreInst: + case SILInstructionKind::AssignInst: + case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::MarkFunctionEscapeInst: + case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugValueAddrInst: +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Store##Name##Inst: +#include "swift/AST/ReferenceStorage.def" + case SILInstructionKind::CopyAddrInst: + case SILInstructionKind::DestroyAddrInst: + case SILInstructionKind::EndLifetimeInst: + case SILInstructionKind::InjectEnumAddrInst: + case SILInstructionKind::DeinitExistentialAddrInst: + case SILInstructionKind::DeinitExistentialValueInst: + case SILInstructionKind::UnconditionalCheckedCastAddrInst: + case SILInstructionKind::UncheckedRefCastAddrInst: + case SILInstructionKind::AllocGlobalInst: + case SILInstructionKind::EndApplyInst: + case SILInstructionKind::AbortApplyInst: + case SILInstructionKind::CondFailInst: + case SILInstructionKind::DestructureStructInst: + case SILInstructionKind::DestructureTupleInst: + // Handle by operand and result check. + break; + + case SILInstructionKind::ApplyInst: + case SILInstructionKind::PartialApplyInst: + case SILInstructionKind::TryApplyInst: + case SILInstructionKind::BeginApplyInst: + // Check substitution map. + auto apply = ApplySite(&inst); + auto subs = apply.getSubstitutionMap(); + for (auto ty: subs.getReplacementTypes()) { + if (opaqueArchetypeWouldChange(context, ty->getCanonicalType())) + return true; + } + break; + } + + return false; +} + +static bool hasOpaqueArchetypeArgument(TypeExpansionContext context, SILBasicBlock &BB) { + for (auto *arg : BB.getArguments()) { + if (opaqueArchetypeWouldChange(context, arg->getType().getASTType())) + return true; + } + return false; +} + +static bool hasAnyOpaqueArchetype(SILFunction &F) { + bool foundOpaqueArchetype = false; + auto context = F.getTypeExpansionContext(); + for (auto &BB : F) { + // Check basic block argument types. + if (hasOpaqueArchetypeArgument(context, BB)) { + foundOpaqueArchetype = true; + break; + } + + // Check instruction results and operands. + for (auto &inst : BB) { + if (hasOpaqueArchetype(context, inst)) { + foundOpaqueArchetype = true; + break; + } + } + + if (foundOpaqueArchetype) + break; + } + + return foundOpaqueArchetype; +} + +void updateOpaqueArchetypes(SILFunction &F) { + // Only map if there are opaque archetypes that could change. + if (!hasAnyOpaqueArchetype(F)) + return; + + MapOpaqueArchetypes(F).replace(); +} + /// A utility pass to serialize a SILModule at any place inside the optimization /// pipeline. class SerializeSILPass : public SILModuleTransform { + + bool onlyForCrossModuleOptimization; + /// Removes [serialized] from all functions. This allows for more /// optimizations and for a better dead function elimination. void removeSerializedFlagFromAllFunctions(SILModule &M) { for (auto &F : M) { + bool wasSerialized = F.isSerialized() != IsNotSerialized; F.setSerialized(IsNotSerialized); + + // We are removing [serialized] from the function. This will change how + // opaque archetypes are lowered in SIL - they might lower to their + // underlying type. Update the function's opaque archetypes. + if (wasSerialized && F.isDefinition()) { + updateOpaqueArchetypes(F); + invalidateAnalysis(&F, SILAnalysis::InvalidationKind::Everything); + } + + // After serialization we don't need to keep @alwaysEmitIntoClient + // functions alive, i.e. we don't need to treat them as public functions. + if (F.getLinkage() == SILLinkage::PublicNonABI && M.isWholeModule()) + F.setLinkage(SILLinkage::Shared); } for (auto &WT : M.getWitnessTables()) { @@ -37,12 +425,19 @@ class SerializeSILPass : public SILModuleTransform { } public: - SerializeSILPass() {} + SerializeSILPass(bool onlyForCrossModuleOptimization) + : onlyForCrossModuleOptimization(onlyForCrossModuleOptimization) + { } + void run() override { auto &M = *getModule(); // Nothing to do if the module was serialized already. if (M.isSerialized()) return; + + if (onlyForCrossModuleOptimization && + !M.getOptions().CrossModuleOptimization) + return; // Mark all reachable functions as "anchors" so that they are not // removed later by the dead function elimination pass. This @@ -64,5 +459,9 @@ class SerializeSILPass : public SILModuleTransform { }; SILTransform *swift::createSerializeSILPass() { - return new SerializeSILPass(); + return new SerializeSILPass(/* onlyForCrossModuleOptimization */ false); +} + +SILTransform *swift::createCMOSerializeSILPass() { + return new SerializeSILPass(/* onlyForCrossModuleOptimization */ true); } diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index 88f11083c49ee..e3b08f8c9e458 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -68,11 +68,19 @@ bool swift::removeUnreachableBlocks(SILFunction &f) { return changed; } -/// Helper function to perform SSA updates in case of jump threading. -void swift::updateSSAAfterCloning(BasicBlockCloner &cloner, - SILBasicBlock *srcBB, SILBasicBlock *destBB) { +void BasicBlockCloner::updateSSAAfterCloning() { + // All instructions should have been checked by canCloneInstruction. But we + // still need to check the arguments. + for (auto arg : origBB->getArguments()) { + if ((needsSSAUpdate |= isUsedOutsideOfBlock(arg))) { + break; + } + } + if (!needsSSAUpdate) + return; + SILSSAUpdater ssaUpdater; - for (auto availValPair : cloner.AvailVals) { + for (auto availValPair : availVals) { ValueBase *inst = availValPair.first; if (inst->use_empty()) continue; @@ -85,20 +93,20 @@ void swift::updateSSAAfterCloning(BasicBlockCloner &cloner, useList.push_back(UseWrapper(use)); ssaUpdater.Initialize(inst->getType()); - ssaUpdater.AddAvailableValue(destBB, inst); - ssaUpdater.AddAvailableValue(srcBB, newResult); + ssaUpdater.AddAvailableValue(origBB, inst); + ssaUpdater.AddAvailableValue(getNewBB(), newResult); if (useList.empty()) continue; // Update all the uses. for (auto useWrapper : useList) { - Operand *use = useWrapper; + Operand *use = useWrapper; // unwrap SILInstruction *user = use->getUser(); assert(user && "Missing user"); // Ignore uses in the same basic block. - if (user->getParent() == destBB) + if (user->getParent() == origBB) continue; ssaUpdater.RewriteUse(*use); @@ -128,6 +136,27 @@ bool BasicBlockCloner::splitCriticalEdges(DominanceInfo *domInfo, return changed; } +void BasicBlockCloner::sinkAddressProjections() { + // Because the address projections chains will be disjoint (an instruction + // in one chain cannot use the result of an instruction in another chain), + // the order they are sunk does not matter. + InstructionDeleter deleter; + for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) { + bool canSink = sinkProj.analyzeAddressProjections(&*ii); + (void)canSink; + assert(canSink && "canCloneInstruction should catch this."); + + sinkProj.cloneProjections(); + assert((sinkProj.getInBlockDefs().empty() || needsSSAUpdate) + && "canCloneInstruction should catch this."); + + auto nextII = std::next(ii); + deleter.trackIfDead(&*ii); + ii = nextII; + } + deleter.cleanUpDeadInstructions(); +} + // Populate 'projections' with the chain of address projections leading // to and including 'inst'. // @@ -149,7 +178,7 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { return true; } if (auto *addressProj = dyn_cast(def)) { - if (addressProj->isTriviallyDuplicatable()) { + if (addressProj->isPure()) { projections.push_back(addressProj); return true; } @@ -183,19 +212,40 @@ bool SinkAddressProjections::cloneProjections() { return false; SILBasicBlock *bb = projections.front()->getParent(); - SmallVector usesToReplace; // Clone projections in last-to-first order. for (unsigned idx = 0; idx < projections.size(); ++idx) { auto *oldProj = projections[idx]; assert(oldProj->getParent() == bb); + // Reset transient per-projection sets. usesToReplace.clear(); + firstBlockUse.clear(); + // Gather uses. for (Operand *use : oldProj->getUses()) { - if (use->getUser()->getParent() != bb) + auto *useBB = use->getUser()->getParent(); + if (useBB != bb) { + firstBlockUse.try_emplace(useBB, use); usesToReplace.push_back(use); + } } + // Replace uses. Uses must be handled in the same order they were discovered + // above. + // + // Avoid cloning a projection multiple times per block. This avoids extra + // projections, but also prevents the removal of DebugValue. If a + // projection's only remaining is DebugValue, then it is deleted along with + // the DebugValue. for (Operand *use : usesToReplace) { - auto *newProj = oldProj->clone(use->getUser()); - use->set(cast(newProj)); + auto *useBB = use->getUser()->getParent(); + auto *firstUse = firstBlockUse.lookup(useBB); + SingleValueInstruction *newProj; + if (use == firstUse) + newProj = cast(oldProj->clone(use->getUser())); + else { + newProj = cast(firstUse->get()); + assert(newProj->getParent() == useBB); + newProj->moveFront(useBB); + } + use->set(newProj); } } return true; diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index b88c096c5a229..3c31c4e16236c 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -86,7 +86,7 @@ deleteTriviallyDeadOperandsOfDeadArgument(MutableArrayRef termOperands, if (!i) return; op.set(SILUndef::get(op.get()->getType(), *i->getFunction())); - recursivelyDeleteTriviallyDeadInstructions(i); + eliminateDeadInstruction(i); } // Our implementation assumes that our caller is attempting to remove a dead @@ -375,7 +375,8 @@ void swift::replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest, auto failureBB = oldDest == cbi->getFailureBB() ? newDest : cbi->getFailureBB(); builder.createCheckedCastBranch( - cbi->getLoc(), cbi->isExact(), cbi->getOperand(), cbi->getCastType(), + cbi->getLoc(), cbi->isExact(), cbi->getOperand(), + cbi->getTargetLoweredType(), cbi->getTargetFormalType(), successBB, failureBB, cbi->getTrueBBCount(), cbi->getFalseBBCount()); cbi->eraseFromParent(); return; @@ -389,9 +390,10 @@ void swift::replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest, oldDest == cbi->getSuccessBB() ? newDest : cbi->getSuccessBB(); auto failureBB = oldDest == cbi->getFailureBB() ? newDest : cbi->getFailureBB(); - builder.createCheckedCastValueBranch(cbi->getLoc(), cbi->getOperand(), - cbi->getCastType(), successBB, - failureBB); + builder.createCheckedCastValueBranch( + cbi->getLoc(), cbi->getOperand(), cbi->getSourceFormalType(), + cbi->getTargetLoweredType(), cbi->getTargetFormalType(), + successBB, failureBB); cbi->eraseFromParent(); return; } @@ -407,9 +409,10 @@ void swift::replaceBranchTarget(TermInst *t, SILBasicBlock *oldDest, auto trueCount = cbi->getTrueBBCount(); auto falseCount = cbi->getFalseBBCount(); builder.createCheckedCastAddrBranch( - cbi->getLoc(), cbi->getConsumptionKind(), cbi->getSrc(), - cbi->getSourceType(), cbi->getDest(), cbi->getTargetType(), successBB, - failureBB, trueCount, falseCount); + cbi->getLoc(), cbi->getConsumptionKind(), + cbi->getSrc(), cbi->getSourceFormalType(), + cbi->getDest(), cbi->getTargetFormalType(), + successBB, failureBB, trueCount, falseCount); cbi->eraseFromParent(); return; } @@ -620,89 +623,6 @@ SILBasicBlock *swift::splitIfCriticalEdge(SILBasicBlock *from, llvm_unreachable("Destination block not found"); } -void swift::completeJointPostDominanceSet( - ArrayRef userBlocks, ArrayRef defBlocks, - llvm::SmallVectorImpl &result) { - assert(!userBlocks.empty() && "Must have at least 1 user block"); - assert(!defBlocks.empty() && "Must have at least 1 def block"); - - // If we have only one def block and one user block and they are the same - // block, then just return. - if (defBlocks.size() == 1 && userBlocks.size() == 1 - && userBlocks[0] == defBlocks[0]) { - return; - } - - // Some notes on the algorithm: - // - // 1. Our VisitedBlocks set just states that a value has been added to the - // worklist and should not be added to the worklist. - // 2. Our targets of the CFG block are DefBlockSet. - // 3. We find the missing post-domination blocks by finding successors of - // blocks on our walk that we have not visited by the end of the walk. For - // joint post-dominance to be true, no such successors should exist. - - // Our set of target blocks where we stop walking. - llvm::SmallPtrSet defBlockSet(defBlocks.begin(), - defBlocks.end()); - - // The set of successor blocks of blocks that we visit. Any blocks still in - // this set at the end of the walk act as a post-dominating closure around our - // userBlock set. - llvm::SmallSetVector mustVisitSuccessorBlocks; - - // Add our user and def blocks to the visitedBlock set. We never want to find - // these in our worklist. - llvm::SmallPtrSet visitedBlocks(userBlocks.begin(), - userBlocks.end()); - - // Finally setup our worklist by adding our user block predecessors. We only - // add the predecessors to the worklist once. - llvm::SmallVector worklist; - for (auto *block : userBlocks) { - llvm::copy_if(block->getPredecessorBlocks(), std::back_inserter(worklist), - [&](SILBasicBlock *predBlock) -> bool { - return visitedBlocks.insert(predBlock).second; - }); - } - - // Then until we reach a fix point. - while (!worklist.empty()) { - // Grab the next block from the worklist. - auto *block = worklist.pop_back_val(); - assert(visitedBlocks.count(block) - && "All blocks from worklist should be " - "in the visited blocks set."); - - // Since we are visiting this block now, we know that this block can not be - // apart of a the post-dominance closure of our UseBlocks. - mustVisitSuccessorBlocks.remove(block); - - // Then add each successor block of block that has not been visited yet to - // the mustVisitSuccessorBlocks set. - for (auto *succBlock : block->getSuccessorBlocks()) { - if (!visitedBlocks.count(succBlock)) { - mustVisitSuccessorBlocks.insert(succBlock); - } - } - - // If this is a def block, then do not add its predecessors to the - // worklist. - if (defBlockSet.count(block)) - continue; - - // Otherwise add all unvisited predecessors to the worklist. - llvm::copy_if(block->getPredecessorBlocks(), std::back_inserter(worklist), - [&](SILBasicBlock *block) -> bool { - return visitedBlocks.insert(block).second; - }); - } - - // Now that we are done, add all remaining must visit blocks to our result - // list. These are the remaining parts of our joint post-dominance closure. - llvm::copy(mustVisitSuccessorBlocks, std::back_inserter(result)); -} - bool swift::splitAllCondBrCriticalEdgesWithNonTrivialArgs( SILFunction &fn, DominanceInfo *domInfo, SILLoopInfo *loopInfo) { // Find our targets. diff --git a/lib/SILOptimizer/Utils/CMakeLists.txt b/lib/SILOptimizer/Utils/CMakeLists.txt index 36f43361f0aa0..e8d871174e021 100644 --- a/lib/SILOptimizer/Utils/CMakeLists.txt +++ b/lib/SILOptimizer/Utils/CMakeLists.txt @@ -14,6 +14,7 @@ silopt_register_sources( LoadStoreOptUtils.cpp LoopUtils.cpp OptimizerStatsUtils.cpp + PartialApplyCombiner.cpp PerformanceInlinerUtils.cpp SILInliner.cpp SILSSAUpdater.cpp diff --git a/lib/SILOptimizer/Utils/CastOptimizer.cpp b/lib/SILOptimizer/Utils/CastOptimizer.cpp index 8891d3bb64381..d318e92951016 100644 --- a/lib/SILOptimizer/Utils/CastOptimizer.cpp +++ b/lib/SILOptimizer/Utils/CastOptimizer.cpp @@ -74,7 +74,7 @@ static SubstitutionMap lookupBridgeToObjCProtocolSubs(SILModule &mod, CanType target) { auto bridgedProto = mod.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable); - auto conf = *mod.getSwiftModule()->lookupConformance(target, bridgedProto); + auto conf = mod.getSwiftModule()->lookupConformance(target, bridgedProto); return SubstitutionMap::getProtocolSubstitutions(conf.getRequirement(), target, conf); } @@ -107,7 +107,8 @@ convertObjectToLoadableBridgeableType(SILBuilderWithScope &builder, // Otherwise, just perform an unconditional checked cast to the sil bridged // ty. We return the cast as our value and as our new cast instruction. auto *cast = - builder.createUnconditionalCheckedCast(loc, load, silBridgedTy); + builder.createUnconditionalCheckedCast(loc, load, silBridgedTy, + dynamicCast.getBridgedTargetType()); return {cast, cast}; } @@ -142,6 +143,7 @@ convertObjectToLoadableBridgeableType(SILBuilderWithScope &builder, // Ok, we need to perform the full cast optimization. This means that we are // going to replace the cast terminator in inst_block with a checked_cast_br. auto *ccbi = builder.createCheckedCastBranch(loc, false, load, silBridgedTy, + dynamicCast.getBridgedTargetType(), castSuccessBB, castFailBB); splitEdge(ccbi, /* EdgeIdx to CastFailBB */ 1); @@ -253,7 +255,7 @@ CastOptimizer::optimizeBridgedObjCToSwiftCast(SILDynamicCastInst dynamicCast) { (kind == SILDynamicCastKind::UnconditionalCheckedCastAddrInst)) && "Unsupported dynamic cast kind"); - CanType target = dynamicCast.getTargetType(); + CanType target = dynamicCast.getTargetFormalType(); auto &mod = dynamicCast.getModule(); // AnyHashable is a special case that we do not handle since we only handle @@ -476,13 +478,14 @@ static bool canOptimizeCast(const swift::Type &BridgedTargetTy, static Optional> findBridgeToObjCFunc(SILOptFunctionBuilder &functionBuilder, SILDynamicCastInst dynamicCast) { - CanType sourceType = dynamicCast.getSourceType(); + CanType sourceFormalType = dynamicCast.getSourceFormalType(); auto loc = dynamicCast.getLocation(); auto &mod = dynamicCast.getModule(); auto bridgedProto = mod.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable); - auto conf = mod.getSwiftModule()->lookupConformance(sourceType, bridgedProto); + auto conf = mod.getSwiftModule()->lookupConformance( + sourceFormalType, bridgedProto); assert(conf && "_ObjectiveCBridgeable conformance should exist"); (void)conf; @@ -492,13 +495,14 @@ findBridgeToObjCFunc(SILOptFunctionBuilder &functionBuilder, if (!modDecl) return None; SmallVector results; - modDecl->lookupMember(results, sourceType.getNominalOrBoundGenericNominal(), + modDecl->lookupMember(results, + sourceFormalType.getNominalOrBoundGenericNominal(), mod.getASTContext().Id_bridgeToObjectiveC, Identifier()); ArrayRef resultsRef(results); if (resultsRef.empty()) { mod.getSwiftModule()->lookupMember( - results, sourceType.getNominalOrBoundGenericNominal(), + results, sourceFormalType.getNominalOrBoundGenericNominal(), mod.getASTContext().Id_bridgeToObjectiveC, Identifier()); resultsRef = results; } @@ -511,7 +515,7 @@ findBridgeToObjCFunc(SILOptFunctionBuilder &functionBuilder, loc, memberDeclRef, ForDefinition_t::NotForDefinition); // Get substitutions, if source is a bound generic type. - auto subMap = sourceType->getContextSubstitutionMap( + auto subMap = sourceFormalType->getContextSubstitutionMap( mod.getSwiftModule(), resultDecl->getDeclContext()); // Implementation of _bridgeToObjectiveC could not be found. @@ -532,28 +536,29 @@ findBridgeToObjCFunc(SILOptFunctionBuilder &functionBuilder, static SILValue computeFinalCastedValue(SILBuilderWithScope &builder, SILDynamicCastInst dynamicCast, ApplyInst *newAI) { - SILValue dest = dynamicCast.getDest(); auto loc = dynamicCast.getLocation(); auto convTy = newAI->getType(); bool isConditional = dynamicCast.isConditional(); - auto destTy = dest->getType().getObjectType(); - assert(destTy == dynamicCast.getLoweredBridgedTargetObjectType() && + auto destLoweredTy = dynamicCast.getTargetLoweredType().getObjectType(); + auto destFormalTy = dynamicCast.getTargetFormalType(); + assert(destLoweredTy == dynamicCast.getLoweredBridgedTargetObjectType() && "Expected Dest Type to be the same as BridgedTargetTy"); auto &m = dynamicCast.getModule(); - if (convTy == destTy) { + if (convTy == destLoweredTy) { return newAI; } - if (destTy.isExactSuperclassOf(convTy)) { - return builder.createUpcast(loc, newAI, destTy); + if (destLoweredTy.isExactSuperclassOf(convTy)) { + return builder.createUpcast(loc, newAI, destLoweredTy); } - if (convTy.isExactSuperclassOf(destTy)) { + if (convTy.isExactSuperclassOf(destLoweredTy)) { // If we are not conditional, we are ok with the downcast via checked cast // fails since we will trap. if (!isConditional) { - return builder.createUnconditionalCheckedCast(loc, newAI, destTy); + return builder.createUnconditionalCheckedCast(loc, newAI, + destLoweredTy, destFormalTy); } // Otherwise if we /are/ emitting a conditional cast, make sure that we @@ -575,19 +580,20 @@ static SILValue computeFinalCastedValue(SILBuilderWithScope &builder, auto *condBrSuccessBB = newAI->getFunction()->createBasicBlockAfter(newAI->getParent()); - condBrSuccessBB->createPhiArgument(destTy, ValueOwnershipKind::Owned); - builder.createCheckedCastBranch(loc, /* isExact*/ false, newAI, destTy, + condBrSuccessBB->createPhiArgument(destLoweredTy, ValueOwnershipKind::Owned); + builder.createCheckedCastBranch(loc, /* isExact*/ false, newAI, + destLoweredTy, destFormalTy, condBrSuccessBB, failureBB); builder.setInsertionPoint(condBrSuccessBB, condBrSuccessBB->begin()); return condBrSuccessBB->getArgument(0); } if (convTy.getASTType() == - getNSBridgedClassOfCFClass(m.getSwiftModule(), destTy.getASTType()) || - destTy.getASTType() == + getNSBridgedClassOfCFClass(m.getSwiftModule(), destLoweredTy.getASTType()) || + destLoweredTy.getASTType() == getNSBridgedClassOfCFClass(m.getSwiftModule(), convTy.getASTType())) { // Handle NS <-> CF toll-free bridging here. - return SILValue(builder.createUncheckedRefCast(loc, newAI, destTy)); + return SILValue(builder.createUncheckedRefCast(loc, newAI, destLoweredTy)); } llvm_unreachable( @@ -628,7 +634,8 @@ CastOptimizer::optimizeBridgedSwiftToObjCCast(SILDynamicCastInst dynamicCast) { std::tie(bridgedFunc, subMap) = result.getValue(); } - SILType SubstFnTy = bridgedFunc->getLoweredType().substGenericArgs(M, subMap); + SILType SubstFnTy = bridgedFunc->getLoweredType().substGenericArgs( + M, subMap, TypeExpansionContext(*F)); SILFunctionConventions substConv(SubstFnTy.castTo(), M); // Check that this is a case that the authors of this code thought it could @@ -767,8 +774,8 @@ CastOptimizer::optimizeBridgedSwiftToObjCCast(SILDynamicCastInst dynamicCast) { /// to a required _ObjectiveCType may fail. SILInstruction * CastOptimizer::optimizeBridgedCasts(SILDynamicCastInst dynamicCast) { - CanType source = dynamicCast.getSourceType(); - CanType target = dynamicCast.getTargetType(); + CanType source = dynamicCast.getSourceFormalType(); + CanType target = dynamicCast.getTargetFormalType(); auto &M = dynamicCast.getModule(); // To apply the bridged optimizations, we should ensure that types are not @@ -806,7 +813,7 @@ CastOptimizer::optimizeBridgedCasts(SILDynamicCastInst dynamicCast) { if ((CanBridgedSourceTy && CanBridgedSourceTy->getAnyNominal() == M.getASTContext().getNSErrorDecl()) || - (CanBridgedTargetTy && CanBridgedSourceTy->getAnyNominal() == + (CanBridgedTargetTy && CanBridgedTargetTy->getAnyNominal() == M.getASTContext().getNSErrorDecl())) { // FIXME: Can't optimize bridging with NSError. return nullptr; @@ -951,7 +958,7 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { SILBuilderWithScope Builder(Inst, builderContext); auto Loc = dynamicCast.getLocation(); - if (ARI->getType() == dynamicCast.getLoweredTargetType()) { + if (ARI->getType() == dynamicCast.getTargetLoweredType()) { // This exact cast will succeed. SmallVector Args; Args.push_back(ARI); @@ -980,7 +987,8 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { return nullptr; SILDynamicCastInst dynamicCast(Inst); - auto LoweredTargetType = dynamicCast.getLoweredTargetType(); + auto TargetLoweredType = dynamicCast.getTargetLoweredType(); + auto TargetFormalType = dynamicCast.getTargetFormalType(); auto Loc = dynamicCast.getLocation(); auto *SuccessBB = dynamicCast.getSuccessBlock(); auto Op = dynamicCast.getSource(); @@ -989,6 +997,9 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { // Check if we can statically predict the outcome of the cast. auto Feasibility = dynamicCast.classifyFeasibility(false /*allow whole module*/); + if (Feasibility == DynamicCastFeasibility::MaySucceed) { + return nullptr; + } SILBuilderWithScope Builder(Inst, builderContext); if (Feasibility == DynamicCastFeasibility::WillFail) { @@ -1001,9 +1012,11 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { return NewI; } + assert(Feasibility == DynamicCastFeasibility::WillSucceed); + bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty(); SILValue CastedValue; - if (Op->getType() != LoweredTargetType) { + if (Op->getType() != TargetLoweredType) { // Apply the bridged cast optimizations. // // TODO: Bridged casts cannot be expressed by checked_cast_br yet. @@ -1014,14 +1027,6 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { llvm_unreachable( "Bridged casts cannot be expressed by checked_cast_br yet"); } else { - // If the cast may succeed or fail and can't be turned into a bridging - // call, then let it be. - if (Feasibility == DynamicCastFeasibility::MaySucceed) { - return nullptr; - } - - assert(Feasibility == DynamicCastFeasibility::WillSucceed); - // Replace by unconditional_cast, followed by a branch. // The unconditional_cast can be skipped, if the result of a cast // is not used afterwards. @@ -1032,11 +1037,12 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { CastedValue = emitSuccessfulScalarUnconditionalCast(Builder, Loc, dynamicCast); } else { - CastedValue = SILUndef::get(LoweredTargetType, *F); + CastedValue = SILUndef::get(TargetLoweredType, *F); } if (!CastedValue) CastedValue = - Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType); + Builder.createUnconditionalCheckedCast( + Loc, Op, TargetLoweredType, TargetFormalType); } } else { @@ -1059,7 +1065,9 @@ SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst( return nullptr; SILDynamicCastInst dynamicCast(Inst); - auto LoweredTargetType = dynamicCast.getLoweredTargetType(); + auto SourceFormalType = dynamicCast.getSourceFormalType(); + auto TargetLoweredType = dynamicCast.getTargetLoweredType(); + auto TargetFormalType = dynamicCast.getTargetFormalType(); auto Loc = dynamicCast.getLocation(); auto *SuccessBB = dynamicCast.getSuccessBlock(); auto *FailureBB = dynamicCast.getFailureBlock(); @@ -1082,7 +1090,7 @@ SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst( bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty(); SILValue CastedValue; - if (Op->getType() != LoweredTargetType) { + if (Op->getType() != TargetLoweredType) { // Apply the bridged cast optimizations. // TODO: Bridged casts cannot be expressed by checked_cast_value_br yet. // Once the support for opaque values has landed, please review this @@ -1111,12 +1119,13 @@ SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst( CastedValue = emitSuccessfulScalarUnconditionalCast(Builder, Loc, dynamicCast); } else { - CastedValue = SILUndef::get(LoweredTargetType, *F); + CastedValue = SILUndef::get(TargetLoweredType, *F); } } if (!CastedValue) CastedValue = Builder.createUnconditionalCheckedCastValue( - Loc, Op, LoweredTargetType); + Loc, Op, SourceFormalType, + TargetLoweredType, TargetFormalType); } else { // No need to cast. CastedValue = Op; @@ -1183,10 +1192,12 @@ SILInstruction *CastOptimizer::optimizeCheckedCastAddrBranchInst( if (SuccessBB->getSinglePredecessorBlock() && canUseScalarCheckedCastInstructions( Inst->getModule(), MI->getType().getASTType(), - Inst->getTargetType())) { + Inst->getTargetFormalType())) { SILBuilderWithScope B(Inst, builderContext); auto NewI = B.createCheckedCastBranch( - Loc, false /*isExact*/, MI, Dest->getType().getObjectType(), + Loc, false /*isExact*/, MI, + Inst->getTargetLoweredType().getObjectType(), + Inst->getTargetFormalType(), SuccessBB, FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount()); SuccessBB->createPhiArgument(Dest->getType().getObjectType(), @@ -1227,11 +1238,13 @@ CastOptimizer::optimizeCheckedCastBranchInst(CheckedCastBranchInst *Inst) { auto *fBlock = dynamicCast.getFailureBlock(); if (B.hasOwnership()) { fBlock->replacePhiArgumentAndReplaceAllUses(0, mi->getType(), - ValueOwnershipKind::Any); + ValueOwnershipKind::None); } return B.createCheckedCastBranch( dynamicCast.getLocation(), false /*isExact*/, mi, - dynamicCast.getLoweredTargetType(), dynamicCast.getSuccessBlock(), + dynamicCast.getTargetLoweredType(), + dynamicCast.getTargetFormalType(), + dynamicCast.getSuccessBlock(), fBlock, *dynamicCast.getSuccessBlockCount(), *dynamicCast.getFailureBlockCount()); }; @@ -1498,8 +1511,8 @@ static bool optimizeStaticallyKnownProtocolConformance( auto Loc = Inst->getLoc(); auto Src = Inst->getSrc(); auto Dest = Inst->getDest(); - auto SourceType = Inst->getSourceType(); - auto TargetType = Inst->getTargetType(); + auto SourceType = Inst->getSourceFormalType(); + auto TargetType = Inst->getTargetFormalType(); auto &Mod = Inst->getModule(); if (TargetType->isAnyExistentialType() && @@ -1522,12 +1535,12 @@ static bool optimizeStaticallyKnownProtocolConformance( // everything is completely static (`X() as? P`), in which case a // valid conformance will be returned. auto Conformance = SM->conformsToProtocol(SourceType, Proto); - if (!Conformance) + if (Conformance.isInvalid()) return false; SILBuilderWithScope B(Inst); SmallVector NewConformances; - NewConformances.push_back(Conformance.getValue()); + NewConformances.push_back(Conformance); ArrayRef Conformances = Ctx.AllocateCopy(NewConformances); @@ -1595,10 +1608,10 @@ SILInstruction *CastOptimizer::optimizeUnconditionalCheckedCastAddrInst( SILBuilderWithScope Builder(Inst, builderContext); // mem2reg's invariants get unhappy if we don't try to // initialize a loadable result. - if (!dynamicCast.getLoweredTargetType().isAddressOnly( + if (!dynamicCast.getTargetLoweredType().isAddressOnly( Builder.getFunction())) { auto undef = SILValue( - SILUndef::get(dynamicCast.getLoweredTargetType().getObjectType(), + SILUndef::get(dynamicCast.getTargetLoweredType().getObjectType(), Builder.getFunction())); Builder.emitStoreValueOperation(Loc, undef, dynamicCast.getDest(), StoreOwnershipQualifier::Init); diff --git a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp index 390e6e826a6be..97c03c862937d 100644 --- a/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp +++ b/lib/SILOptimizer/Utils/CheckedCastBrJumpThreading.cpp @@ -87,8 +87,8 @@ class CheckedCastBrJumpThreading { hasUnknownPreds(hasUnknownPreds) { } void modifyCFGForUnknownPreds(); - void modifyCFGForFailurePreds(Optional &Cloner); - void modifyCFGForSuccessPreds(Optional &Cloner); + void modifyCFGForFailurePreds(BasicBlockCloner &Cloner); + void modifyCFGForSuccessPreds(BasicBlockCloner &Cloner); }; // Contains an entry for each checked_cast_br to be optimized. @@ -243,15 +243,14 @@ void CheckedCastBrJumpThreading::Edit::modifyCFGForUnknownPreds() { /// Create a copy of the BB as a landing BB /// for all FailurePreds. -void CheckedCastBrJumpThreading::Edit:: -modifyCFGForFailurePreds(Optional &Cloner) { +void CheckedCastBrJumpThreading::Edit::modifyCFGForFailurePreds( + BasicBlockCloner &Cloner) { if (FailurePreds.empty()) return; - assert(!Cloner.hasValue()); - Cloner.emplace(CCBBlock); - Cloner->cloneBlock(); - SILBasicBlock *TargetFailureBB = Cloner->getNewBB(); + assert(!Cloner.wasCloned()); + Cloner.cloneBlock(); + SILBasicBlock *TargetFailureBB = Cloner.getNewBB(); auto *TI = TargetFailureBB->getTerminator(); SILBuilderWithScope Builder(TI); // This BB copy branches to a FailureBB. @@ -271,8 +270,8 @@ modifyCFGForFailurePreds(Optional &Cloner) { /// Create a copy of the BB or reuse BB as /// a landing basic block for all FailurePreds. -void CheckedCastBrJumpThreading::Edit:: -modifyCFGForSuccessPreds(Optional &Cloner) { +void CheckedCastBrJumpThreading::Edit::modifyCFGForSuccessPreds( + BasicBlockCloner &Cloner) { auto *CCBI = cast(CCBBlock->getTerminator()); if (InvertSuccess) { @@ -285,10 +284,9 @@ modifyCFGForSuccessPreds(Optional &Cloner) { if (!SuccessPreds.empty()) { // Create a copy of the BB as a landing BB. // for all SuccessPreds. - assert(!Cloner.hasValue()); - Cloner.emplace(CCBBlock); - Cloner->cloneBlock(); - SILBasicBlock *TargetSuccessBB = Cloner->getNewBB(); + assert(!Cloner.wasCloned()); + Cloner.cloneBlock(); + SILBasicBlock *TargetSuccessBB = Cloner.getNewBB(); auto *TI = TargetSuccessBB->getTerminator(); SILBuilderWithScope Builder(TI); // This BB copy branches to SuccessBB. @@ -343,6 +341,11 @@ bool CheckedCastBrJumpThreading::handleArgBBIsEntryBlock(SILBasicBlock *ArgBB, // Returns false if cloning required by jump threading cannot // be performed, because some of the constraints are violated. +// +// This does not check the constraint on address projections with out-of-block +// uses. Those are rare enough that they don't need to be checked first for +// efficiency, but they need to be gathered later, just before cloning, anyway +// in order to sink the projections. bool CheckedCastBrJumpThreading::checkCloningConstraints() { // Check some cloning related constraints. @@ -527,7 +530,7 @@ bool CheckedCastBrJumpThreading::trySimplify(CheckedCastBranchInst *CCBI) { // fact that the source operand is the same for // both instructions. if (!CCBI->isExact() && !DomCCBI->isExact()) { - if (DomCCBI->getCastType() != CCBI->getCastType()) + if (DomCCBI->getTargetFormalType() != CCBI->getTargetFormalType()) continue; } @@ -599,7 +602,7 @@ bool CheckedCastBrJumpThreading::trySimplify(CheckedCastBranchInst *CCBI) { bool InvertSuccess = false; if (DomCCBI->isExact() && CCBI->isExact() && - DomCCBI->getCastType() != CCBI->getCastType()) { + DomCCBI->getTargetFormalType() != CCBI->getTargetFormalType()) { if (TotalPreds == SuccessPreds.size()) { // The dominating exact cast was successful, but it casted to a // different type. Therefore, the current cast fails for sure. @@ -673,7 +676,9 @@ void CheckedCastBrJumpThreading::optimizeFunction() { Fn->verifyCriticalEdges(); for (Edit *edit : Edits) { - Optional Cloner; + BasicBlockCloner Cloner(edit->CCBBlock); + if (!Cloner.canCloneBlock()) + continue; // Create a copy of the BB as a landing BB // for all FailurePreds. @@ -684,12 +689,11 @@ void CheckedCastBrJumpThreading::optimizeFunction() { // Handle unknown preds. edit->modifyCFGForUnknownPreds(); - if (Cloner.hasValue()) { - updateSSAAfterCloning(*Cloner.getPointer(), Cloner->getNewBB(), - edit->CCBBlock); + if (Cloner.wasCloned()) { + Cloner.updateSSAAfterCloning(); - if (!Cloner->getNewBB()->pred_empty()) - BlocksForWorklist.push_back(Cloner->getNewBB()); + if (!Cloner.getNewBB()->pred_empty()) + BlocksForWorklist.push_back(Cloner.getNewBB()); } if (!edit->CCBBlock->pred_empty()) BlocksForWorklist.push_back(edit->CCBBlock); diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index f089e25bef6d2..402c2aadf27c0 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -13,11 +13,13 @@ #define DEBUG_TYPE "ConstExpr" #include "swift/SILOptimizer/Utils/ConstExpr.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/Defer.h" #include "swift/Basic/NullablePtr.h" #include "swift/Demangling/Demangle.h" #include "swift/SIL/ApplySite.h" +#include "swift/SIL/DynamicCasts.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILConstants.h" @@ -54,32 +56,45 @@ enum class WellKnownFunction { StringEquals, // String.percentEscapedString.getter StringEscapePercent, + // BinaryInteger.description.getter + BinaryIntegerDescription, // _assertionFailure(_: StaticString, _: StaticString, file: StaticString,...) - AssertionFailure + AssertionFailure, + // A function taking one argument that prints the symbolic value of the + // argument during constant evaluation. This must only be used for debugging. + DebugPrint }; static llvm::Optional classifyFunction(SILFunction *fn) { - if (fn->hasSemanticsAttr("array.init.empty")) + if (fn->hasSemanticsAttr(semantics::ARRAY_INIT_EMPTY)) return WellKnownFunction::ArrayInitEmpty; - if (fn->hasSemanticsAttr("array.uninitialized_intrinsic")) + if (fn->hasSemanticsAttr(semantics::ARRAY_UNINITIALIZED_INTRINSIC)) return WellKnownFunction::AllocateUninitializedArray; - if (fn->hasSemanticsAttr("array.append_element")) + if (fn->hasSemanticsAttr(semantics::ARRAY_APPEND_ELEMENT)) return WellKnownFunction::ArrayAppendElement; - if (fn->hasSemanticsAttr("string.init_empty")) + if (fn->hasSemanticsAttr(semantics::STRING_INIT_EMPTY)) return WellKnownFunction::StringInitEmpty; // There are two string initializers in the standard library with the // semantics "string.makeUTF8". They are identical from the perspective of // the interpreter. One of those functions is probably redundant and not used. - if (fn->hasSemanticsAttr("string.makeUTF8")) + if (fn->hasSemanticsAttr(semantics::STRING_MAKE_UTF8)) return WellKnownFunction::StringMakeUTF8; - if (fn->hasSemanticsAttr("string.append")) + if (fn->hasSemanticsAttr(semantics::STRING_APPEND)) return WellKnownFunction::StringAppend; - if (fn->hasSemanticsAttr("string.equals")) + if (fn->hasSemanticsAttr(semantics::STRING_EQUALS)) return WellKnownFunction::StringEquals; - if (fn->hasSemanticsAttr("string.escapePercent.get")) + if (fn->hasSemanticsAttr(semantics::STRING_ESCAPE_PERCENT_GET)) return WellKnownFunction::StringEscapePercent; + if (fn->hasSemanticsAttr(semantics::BINARY_INTEGER_DESCRIPTION)) + return WellKnownFunction::BinaryIntegerDescription; if (fn->hasSemanticsAttrThatStartsWith("programtermination_point")) return WellKnownFunction::AssertionFailure; + // A call to a function with the following semantics annotation will be + // considered as a DebugPrint operation. The evaluator will print the value + // of the single argument passed to this function call to the standard error. + // This functionality must be used only for debugging the evaluator. + if (fn->hasSemanticsAttrThatStartsWith("constant_evaluator_debug_print")) + return WellKnownFunction::DebugPrint; return None; } @@ -170,9 +185,10 @@ class ConstExprFunctionState { /// as a key. SymbolicValue createMemoryObject(SILValue addr, SymbolicValue initialValue) { assert(!calculatedValues.count(addr)); - auto type = substituteGenericParamsAndSimpify(addr->getType().getASTType()); + Type valueType = + substituteGenericParamsAndSimpify(addr->getType().getASTType()); auto *memObject = SymbolicValueMemoryObject::create( - type, initialValue, evaluator.getAllocator()); + valueType, initialValue, evaluator.getAllocator()); auto result = SymbolicValue::getAddress(memObject); setValue(addr, result); return result; @@ -222,6 +238,13 @@ class ConstExprFunctionState { llvm::Optional computeWellKnownCallResult(ApplyInst *apply, WellKnownFunction callee); + /// Evaluate a closure creation instruction which is either a partial_apply + /// instruction or a thin_to_think_function instruction. On success, this + /// function will bind the \c closureInst parameter to its symbolic value. + /// On failure, it returns the unknown symbolic value that captures the error. + llvm::Optional + evaluateClosureCreation(SingleValueInstruction *closureInst); + SymbolicValue getSingleWriterAddressValue(SILValue addr); SymbolicValue getConstAddrAndLoadResult(SILValue addr); SymbolicValue loadAddrValue(SILValue addr, SymbolicValue addrVal); @@ -266,7 +289,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { auto val = getConstantValue(tei->getOperand()); if (!val.isConstant()) return val; - return val.getAggregateValue()[tei->getFieldNo()]; + return val.getAggregateMembers()[tei->getFieldNo()]; } // If this is a struct extract from a fragile type, then we can return the @@ -278,7 +301,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { return val; } assert(val.getKind() == SymbolicValue::Aggregate); - return val.getAggregateValue()[sei->getFieldNo()]; + return val.getAggregateMembers()[sei->getFieldNo()]; } // If this is an unchecked_enum_data from a fragile type, then we can return @@ -303,7 +326,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { auto val = getConstantValue(aggValue); if (val.isConstant()) { assert(val.getKind() == SymbolicValue::Aggregate); - return val.getAggregateValue()[result->getIndex()]; + return val.getAggregateMembers()[result->getIndex()]; } // Not a const. return val; @@ -325,8 +348,10 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { // trap. elts.push_back(val); } - - return SymbolicValue::getAggregate(elts, evaluator.getAllocator()); + CanType structType = value->getType().getASTType(); + return SymbolicValue::getAggregate( + elts, substituteGenericParamsAndSimpify(structType), + evaluator.getAllocator()); } // If this is a struct or tuple element addressor, compute a more derived @@ -364,12 +389,11 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { // Try to resolve a witness method against our known conformances. if (auto *wmi = dyn_cast(value)) { - auto confResult = substitutionMap.lookupConformance( + auto conf = substitutionMap.lookupConformance( wmi->getLookupType(), wmi->getConformance().getRequirement()); - if (!confResult) + if (conf.isInvalid()) return getUnknown(evaluator, value, UnknownReason::UnknownWitnessMethodConformance); - auto conf = confResult.getValue(); auto &module = wmi->getModule(); SILFunction *fn = module.lookUpFunctionInWitnessTable(conf, wmi->getMember()).first; @@ -533,7 +557,7 @@ ConstExprFunctionState::computeConstantValueBuiltin(BuiltinInst *inst) { return SymbolicValue::getAggregate( {SymbolicValue::getInteger(result, allocator), SymbolicValue::getInteger(APInt(1, false), allocator)}, - allocator); + inst->getType().getASTType(), allocator); }; switch (builtin.ID) { @@ -705,7 +729,7 @@ ConstExprFunctionState::computeConstantValueBuiltin(BuiltinInst *inst) { return SymbolicValue::getAggregate( {SymbolicValue::getInteger(result, allocator), SymbolicValue::getInteger(APInt(1, overflowed), allocator)}, - allocator); + inst->getType().getASTType(), allocator); }; switch (builtin.ID) { @@ -752,13 +776,21 @@ static Optional extractStaticStringValue(SymbolicValue staticString) { if (staticString.getKind() != SymbolicValue::Aggregate) return None; - ArrayRef staticStringProps = staticString.getAggregateValue(); + ArrayRef staticStringProps = + staticString.getAggregateMembers(); if (staticStringProps.empty() || staticStringProps[0].getKind() != SymbolicValue::String) return None; return staticStringProps[0].getStringValue(); } +static Optional +extractStringOrStaticStringValue(SymbolicValue stringValue) { + if (stringValue.getKind() == SymbolicValue::String) + return stringValue.getStringValue(); + return extractStaticStringValue(stringValue); +} + /// If the specified type is a Swift.Array of some element type, then return the /// element type. Otherwise, return a null Type. static Type getArrayElementType(Type ty) { @@ -768,6 +800,28 @@ static Type getArrayElementType(Type ty) { return Type(); } +/// Check if the given type \p ty is a stdlib integer type and if so return +/// whether the type is signed. Returns \c None if \p ty is not a stdlib integer +/// type, \c true if it is a signed integer type and \c false if it is an +/// unsigned integer type. +static Optional getSignIfStdlibIntegerType(Type ty) { + StructDecl *decl = ty->getStructOrBoundGenericStruct(); + if (!decl) + return None; + ASTContext &astCtx = ty->getASTContext(); + if (decl == astCtx.getIntDecl() || decl == astCtx.getInt8Decl() || + decl == astCtx.getInt16Decl() || decl == astCtx.getInt32Decl() || + decl == astCtx.getInt64Decl()) { + return true; + } + if (decl == astCtx.getUIntDecl() || decl == astCtx.getUInt8Decl() || + decl == astCtx.getUInt16Decl() || decl == astCtx.getUInt32Decl() || + decl == astCtx.getUInt64Decl()) { + return false; + } + return None; +} + /// Given a call to a well known function, collect its arguments as constants, /// fold it, and return None. If any of the arguments are not constants, marks /// the call's results as Unknown, and return an Unknown with information about @@ -782,8 +836,8 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, for (unsigned i = 0; i < apply->getNumArguments(); i++) { SILValue argument = apply->getArgument(i); SymbolicValue argValue = getConstantValue(argument); - Optional stringOpt = extractStaticStringValue(argValue); - + Optional stringOpt = + extractStringOrStaticStringValue(argValue); // The first argument is a prefix that specifies the kind of failure // this is. if (i == 0) { @@ -795,7 +849,6 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, } continue; } - if (stringOpt) { message += ": "; message += stringOpt.getValue(); @@ -847,7 +900,7 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, // Allocating uninitialized arrays is supported only in flow-sensitive mode. // TODO: the top-level mode in the interpreter should be phased out. - if (!fn) + if (recursivelyComputeValueIfNotInState) return getUnknown(evaluator, (SILInstruction *)apply, UnknownReason::Default); @@ -856,7 +909,10 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, // their addresses will initialize the elements. elementConstants.assign(numElements, SymbolicValue::getUninitMemory()); - Type arrayType = apply->getType().castTo()->getElementType(0); + Type resultType = + substituteGenericParamsAndSimpify(apply->getType().getASTType()); + assert(resultType->is()); + Type arrayType = resultType->castTo()->getElementType(0); Type arrayEltType = getArrayElementType(arrayType); assert(arrayEltType && "Couldn't understand Swift.Array type?"); @@ -871,8 +927,8 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, // Construct return value for this call, which is a pair consisting of the // address of the first element of the array and the array. SymbolicValue storageAddress = array.getAddressOfArrayElement(allocator, 0); - setValue(apply, - SymbolicValue::getAggregate({array, storageAddress}, allocator)); + setValue(apply, SymbolicValue::getAggregate({array, storageAddress}, + resultType, allocator)); return None; } case WellKnownFunction::ArrayAppendElement: { @@ -883,36 +939,19 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, conventions.getNumIndirectSILResults() == 0 && "unexpected Array.append(_:) signature"); // Get the element to be appended which is passed indirectly (@in). - SymbolicValue elementAddress = getConstantValue(apply->getOperand(1)); - if (!elementAddress.isConstant()) - return elementAddress; - - auto invalidOperand = [&]() { + SymbolicValue element = getConstAddrAndLoadResult(apply->getOperand(1)); + if (!element.isConstant()) + return element; + + // Get the array value. The array is passed @inout and could be a property + // of a struct. + SILValue arrayAddress = apply->getOperand(2); + SymbolicValue arrayValue = getConstAddrAndLoadResult(arrayAddress); + if (!arrayValue.isConstant()) + return arrayValue; + if (arrayValue.getKind() != SymbolicValue::Array) { return getUnknown(evaluator, (SILInstruction *)apply, UnknownReason::InvalidOperandValue); - }; - if (elementAddress.getKind() != SymbolicValue::Address) { - // TODO: store the operand number in the error message here. - return invalidOperand(); - } - - SmallVector elementAP; - SymbolicValue element = - elementAddress.getAddressValue(elementAP)->getValue(); - - // Get the array value. The array is passed @inout. - SymbolicValue arrayAddress = getConstantValue(apply->getOperand(2)); - if (!arrayAddress.isConstant()) - return arrayAddress; - if (arrayAddress.getKind() != SymbolicValue::Address) - return invalidOperand(); - - SmallVector arrayAP; - SymbolicValueMemoryObject *arrayMemoryObject = - arrayAddress.getAddressValue(arrayAP); - SymbolicValue arrayValue = arrayMemoryObject->getValue(); - if (arrayValue.getKind() != SymbolicValue::Array) { - return invalidOperand(); } // Create a new array storage by appending the \c element to the existing @@ -930,7 +969,7 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, newElements, elementType, allocator); SymbolicValue newArray = SymbolicValue::getArray(arrayValue.getArrayType(), newStorage, allocator); - arrayMemoryObject->setIndexedElement(arrayAP, newArray, allocator); + computeFSStore(newArray, arrayAddress); return None; } case WellKnownFunction::StringInitEmpty: { // String.init() @@ -1020,6 +1059,7 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, auto intVal = SymbolicValue::getInteger(APInt(1, isEqual), evaluator.getAllocator()); auto result = SymbolicValue::getAggregate(ArrayRef(intVal), + apply->getType().getASTType(), evaluator.getAllocator()); setValue(apply, result); return None; @@ -1056,6 +1096,57 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, setValue(apply, resultVal); return None; } + case WellKnownFunction::BinaryIntegerDescription: { + // BinaryInteger.description.getter + assert(conventions.getNumDirectSILResults() == 1 && + conventions.getNumIndirectSILResults() == 0 && + conventions.getNumParameters() == 1 && apply->hasSubstitutions() && + "unexpected BinaryInteger.description.getter signature"); + // Get the type of the argument and check if it is a signed or + // unsigned integer. + SILValue integerArgument = apply->getOperand(1); + CanType argumentType = substituteGenericParamsAndSimpify( + integerArgument->getType().getASTType()); + Optional isSignedIntegerType = + getSignIfStdlibIntegerType(argumentType); + if (!isSignedIntegerType.hasValue()) { + return getUnknown(evaluator, (SILInstruction *)apply, + UnknownReason::InvalidOperandValue); + } + // Load the stdlib integer's value and convert it to a string. + SymbolicValue stdlibIntegerValue = + getConstAddrAndLoadResult(integerArgument); + if (!stdlibIntegerValue.isConstant()) { + return stdlibIntegerValue; + } + SymbolicValue builtinIntegerValue = + stdlibIntegerValue.lookThroughSingleElementAggregates(); + assert(builtinIntegerValue.getKind() == SymbolicValue::Integer && + "stdlib integer type must store only a builtin integer"); + APInt integer = builtinIntegerValue.getIntegerValue(); + SmallString<8> integerString; + isSignedIntegerType.getValue() ? integer.toStringSigned(integerString) + : integer.toStringUnsigned(integerString); + SymbolicValue resultVal = + SymbolicValue::getString(integerString.str(), evaluator.getAllocator()); + setValue(apply, resultVal); + return None; + } + case WellKnownFunction::DebugPrint: { + assert(apply->getNumArguments() == 1 && + "debug_print function must take exactly one argument"); + SILValue argument = apply->getArgument(0); + SymbolicValue argValue = getConstantValue(argument); + llvm::errs() << "Debug print output "; + argValue.print(llvm::errs()); + if (argValue.getKind() != SymbolicValue::Address) + return None; + + llvm::errs() << "\n Addressed Memory Object: "; + SymbolicValueMemoryObject *memObj = argValue.getAddressValueMemoryObject(); + memObj->getValue().print(llvm::errs()); + return None; + } } llvm_unreachable("unhandled WellKnownFunction"); } @@ -1086,16 +1177,19 @@ ConstExprFunctionState::computeCallResult(ApplyInst *apply) { // call. auto op = apply->getOperand(i + 1); SymbolicValue argValue = getConstantValue(op); - if (!argValue.isConstant()) - return argValue; + if (!argValue.isConstant()) { + return evaluator.getUnknown((SILInstruction *)apply, + UnknownReason::createCallArgumentUnknown(i)); + } paramConstants.push_back(argValue); } // If we reached an external function that hasn't been deserialized yet, make - // sure to pull it in so we can see its body. If that fails, then we can't - // analyze the function. + // sure to pull it in so we can see its body. If that fails, then we can't + // analyze the function. Note: pull in everything referenced from another + // module in case some referenced functions have non-public linkage. if (callee->isExternalDeclaration()) { - callee->getModule().loadFunction(callee); + apply->getModule().linkFunction(callee, SILModule::LinkingMode::LinkAll); if (callee->isExternalDeclaration()) return computeOpaqueCallResult(apply, callee); } @@ -1107,9 +1201,10 @@ ConstExprFunctionState::computeCallResult(ApplyInst *apply) { auto calleeFnType = callee->getLoweredFunctionType(); assert( !calleeFnType->hasSelfParam() || - !calleeFnType->getSelfInstanceType()->getClassOrBoundGenericClass() && + !calleeFnType->getSelfInstanceType(callee->getModule()) + ->getClassOrBoundGenericClass() && "class methods are not supported"); - if (calleeFnType->getGenericSignature()) { + if (calleeFnType->getInvocationGenericSignature()) { // Get the substitution map of the call. This maps from the callee's space // into the caller's world. Witness methods require additional work to // compute a mapping that is valid for the callee. @@ -1118,21 +1213,23 @@ ConstExprFunctionState::computeCallResult(ApplyInst *apply) { if (calleeFnType->getRepresentation() == SILFunctionType::Representation::WitnessMethod) { auto protocol = - calleeFnType->getWitnessMethodConformance().getRequirement(); + calleeFnType->getWitnessMethodConformanceOrInvalid().getRequirement(); // Compute a mapping that maps the Self type of the protocol given by // 'requirement' to the concrete type available in the substitutionMap. - auto protoSelfToConcreteType = - apply->getSubstitutionMap().subst(substitutionMap); + SubstitutionMap applySubstMap = apply->getSubstitutionMap(); + auto protoSelfToConcreteType = substitutionMap.empty() + ? applySubstMap + : applySubstMap.subst(substitutionMap); // Get a concrete protocol conformance by using the mapping for the // Self type of the requirement. auto conf = protoSelfToConcreteType.lookupConformance( protocol->getSelfInterfaceType()->getCanonicalType(), protocol); - if (!conf.hasValue()) + if (conf.isInvalid()) return getUnknown(evaluator, (SILInstruction *)apply, UnknownReason::UnknownWitnessMethodConformance); callSubMap = getWitnessMethodSubstitutions( - apply->getModule(), ApplySite(apply), callee, conf.getValue()); + apply->getModule(), ApplySite(apply), callee, conf); /// Remark: If we ever start to care about evaluating classes, /// getSubstitutionsForCallee() is the analogous mapping function we @@ -1148,7 +1245,8 @@ ConstExprFunctionState::computeCallResult(ApplyInst *apply) { // or conformance, with the mapping introduced by the call itself. This // ensures that the callee's substitution map can map from its type // namespace back to concrete types and conformances. - calleeSubMap = callSubMap.subst(substitutionMap); + calleeSubMap = substitutionMap.empty() ? callSubMap + : callSubMap.subst(substitutionMap); } // Now that we have successfully folded all of the parameters, we can evaluate @@ -1269,7 +1367,7 @@ ConstExprFunctionState::initializeAddressFromSingleWriter(SILValue addr) { auto checkAggregateInitialized = [&]() -> bool { auto memoryValue = getMemoryValue(); return memoryValue.getKind() != SymbolicValue::UninitMemory && - llvm::all_of(memoryValue.getAggregateValue(), + llvm::all_of(memoryValue.getAggregateMembers(), [](SymbolicValue v) { return v.isConstant(); }); }; @@ -1514,7 +1612,7 @@ SymbolicValue ConstExprFunctionState::loadAddrValue(SILValue addr, // Try digging through the aggregate to get to our value. unsigned idx = 0, end = accessPath.size(); while (idx != end && objectVal.getKind() == SymbolicValue::Aggregate) { - objectVal = objectVal.getAggregateValue()[accessPath[idx]]; + objectVal = objectVal.getAggregateMembers()[accessPath[idx]]; ++idx; } @@ -1547,6 +1645,51 @@ ConstExprFunctionState::computeFSStore(SymbolicValue storedCst, SILValue dest) { return None; } +llvm::Optional ConstExprFunctionState::evaluateClosureCreation( + SingleValueInstruction *closureInst) { + assert(isa(closureInst) || + isa(closureInst)); + SILValue calleeOperand = closureInst->getOperand(0); + SymbolicValue calleeValue = getConstantValue(calleeOperand); + if (!calleeValue.isConstant()) + return calleeValue; + if (calleeValue.getKind() != SymbolicValue::Function) { + return getUnknown(evaluator, (SILInstruction *)closureInst, + UnknownReason::InvalidOperandValue); + } + + SILFunction *target = calleeValue.getFunctionValue(); + assert(target != nullptr); + + SmallVector captures; + + // Map generic parameters of the target to the generic arguments passed to the + // call. + SubstitutionMap callSubstMap; + + // If this is a partial-apply instruction, arguments to this partial-apply + // instruction are the captures of the closure. + if (PartialApplyInst *papply = dyn_cast(closureInst)) { + for (SILValue capturedSILValue : papply->getArguments()) { + SymbolicValue capturedSymbolicValue = getConstantValue(capturedSILValue); + if (!capturedSymbolicValue.isConstant()) { + captures.push_back({capturedSILValue, None}); + continue; + } + captures.push_back({capturedSILValue, capturedSymbolicValue}); + } + SubstitutionMap applySubstMap = papply->getSubstitutionMap(); + callSubstMap = substitutionMap.empty() + ? applySubstMap + : applySubstMap.subst(substitutionMap); + } + + auto closureVal = SymbolicValue::makeClosure( + target, captures, callSubstMap, closureInst, evaluator.getAllocator()); + setValue(closureInst, closureVal); + return None; +} + /// Evaluate the specified instruction in a flow sensitive way, for use by /// the constexpr function evaluator. This does not handle control flow /// statements. This returns None on success, and an Unknown SymbolicValue with @@ -1569,12 +1712,14 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { if (auto asi = dyn_cast(inst)) { // If a struct with no stored properties is created, no initialization is // needed. Hence, create a empty aggregate as the initial value. - StructDecl *structDecl = - asi->getElementType().getStructOrBoundGenericStruct(); + CanType structType = asi->getElementType().getASTType(); + StructDecl *structDecl = structType.getStructOrBoundGenericStruct(); + if (structDecl && structDecl->getStoredProperties().empty()) { - createMemoryObject(asi, - SymbolicValue::getAggregate(ArrayRef(), - evaluator.getAllocator())); + createMemoryObject(asi, SymbolicValue::getAggregate( + ArrayRef(), + substituteGenericParamsAndSimpify(structType), + evaluator.getAllocator())); return None; } createMemoryObject(asi, SymbolicValue::getUninitMemory()); @@ -1608,7 +1753,7 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { if (auto apply = dyn_cast(inst)) return computeCallResult(apply); - if (isa(inst)) { + if (isa(inst) || isa(inst)) { auto stored = getConstantValue(inst->getOperand(0)); if (!stored.isConstant()) return stored; @@ -1630,6 +1775,10 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { injectEnumInst->getOperand()); } + if (isa(inst) || isa(inst)) { + return evaluateClosureCreation(cast(inst)); + } + // If the instruction produces a result, try computing it, and fail if the // computation fails. if (auto *singleValueInst = dyn_cast(inst)) { @@ -1649,7 +1798,7 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { } assert(aggVal.getKind() == SymbolicValue::Aggregate); - ArrayRef aggElems = aggVal.getAggregateValue(); + ArrayRef aggElems = aggVal.getAggregateMembers(); assert(aggElems.size() == mvi->getNumResults()); for (unsigned i = 0; i < mvi->getNumResults(); ++i) { @@ -1757,6 +1906,50 @@ ConstExprFunctionState::evaluateInstructionAndGetNext( return {caseBB->begin(), None}; } + if (isa(inst)) { + CheckedCastBranchInst *checkedCastInst = + dyn_cast(inst); + SymbolicValue value = getConstantValue(checkedCastInst->getOperand()); + if (!value.isConstant()) + return {None, value}; + + // Determine success or failure of this cast. + CanType sourceType; + if (value.getKind() == SymbolicValue::Array) { + sourceType = value.getArrayType()->getCanonicalType(); + } else { + // Here, the source type cannot be an address-only type as this is + // not a CheckedCastBranchAddr inst. Therefore, it has to be a struct + // type or String or Metatype. Since the types of aggregates are not + // tracked, we recover it from the declared type of the source operand + // and generic parameter subsitutions in the interpreter state. + sourceType = substituteGenericParamsAndSimpify( + checkedCastInst->getSourceFormalType()); + } + CanType targetType = substituteGenericParamsAndSimpify( + checkedCastInst->getTargetFormalType()); + DynamicCastFeasibility castResult = classifyDynamicCast( + inst->getModule().getSwiftModule(), sourceType, targetType); + if (castResult == DynamicCastFeasibility::MaySucceed) { + return {None, + getUnknown(evaluator, inst, UnknownReason::UnknownCastResult)}; + } + // Determine the basic block to jump to. + SILBasicBlock *resultBB = + (castResult == DynamicCastFeasibility::WillSucceed) + ? checkedCastInst->getSuccessBB() + : checkedCastInst->getFailureBB(); + // Set up the arguments of the basic block, if any. + if (resultBB->getNumArguments() == 0) + return {resultBB->begin(), None}; + // There should be at most one argument to the basic block, which is the + // casted value with the right type, or the input value if the cast fails, + // and inst is in OSSA. + assert(resultBB->getNumArguments() == 1); + setValue(resultBB->getArgument(0), value); + return {resultBB->begin(), None}; + } + LLVM_DEBUG(llvm::dbgs() << "ConstExpr: Unknown Branch Instruction: " << *inst << "\n"); @@ -1934,6 +2127,8 @@ ConstExprStepEvaluator::skipByMakingEffectsNonConstant( constKind == SymbolicValue::Aggregate || constKind == SymbolicValue::Enum || constKind == SymbolicValue::EnumWithPayload || + constKind == SymbolicValue::Array || + constKind == SymbolicValue::Closure || constKind == SymbolicValue::UninitMemory); if (constKind != SymbolicValue::Address) { @@ -1989,7 +2184,7 @@ ConstExprStepEvaluator::skipByMakingEffectsNonConstant( return {None, None}; } -bool ConstExprStepEvaluator::isFailStopError(SymbolicValue errorVal) { +bool swift::isFailStopError(SymbolicValue errorVal) { assert(errorVal.isUnknown()); switch (errorVal.getUnknownReason().getKind()) { @@ -2041,6 +2236,18 @@ ConstExprStepEvaluator::lookupConstValue(SILValue value) { return res; } +void ConstExprStepEvaluator::dumpState() { internalState->dump(); } + bool swift::isKnownConstantEvaluableFunction(SILFunction *fun) { return classifyFunction(fun).hasValue(); } + +bool swift::hasConstantEvaluableAnnotation(SILFunction *fun) { + assert(fun && "fun should not be nullptr"); + return fun->hasSemanticsAttr("constant_evaluable"); +} + +bool swift::isConstantEvaluable(SILFunction *fun) { + return hasConstantEvaluableAnnotation(fun) || + isKnownConstantEvaluableFunction(fun); +} diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index dc276b27f4da7..af43e75cf71f2 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -14,6 +14,7 @@ #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/PatternMatch.h" #include "swift/SIL/SILBuilder.h" @@ -1414,9 +1415,22 @@ static bool constantFoldInstruction(Operand *Op, Optional &ResultsInError, [&](Operand &op) -> SILValue { SILValue operandValue = op.get(); auto ownershipKind = operandValue.getOwnershipKind(); - if (ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed)) - return operandValue; - return SILValue(); + + // First check if we are not compatible with guaranteed. This means + // we would be Owned or Unowned. If so, return SILValue(). + if (!ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed)) + return SILValue(); + + // Otherwise check if our operand is non-trivial and None. In cases + // like that, the non-trivial type could be replacing an owned value + // where we lost that our underlying value is None due to + // intermediate aggregate literal operations. In that case, we /do + // not/ want to eliminate the destructure. + if (ownershipKind == ValueOwnershipKind::None && + !operandValue->getType().isTrivial(*Struct->getFunction())) + return SILValue(); + + return operandValue; }); return true; } @@ -1435,9 +1449,22 @@ static bool constantFoldInstruction(Operand *Op, Optional &ResultsInError, [&](Operand &op) -> SILValue { SILValue operandValue = op.get(); auto ownershipKind = operandValue.getOwnershipKind(); - if (ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed)) - return operandValue; - return SILValue(); + + // First check if we are not compatible with guaranteed. This means + // we would be Owned or Unowned. If so, return SILValue(). + if (!ownershipKind.isCompatibleWith(ValueOwnershipKind::Guaranteed)) + return SILValue(); + + // Otherwise check if our operand is non-trivial and None. In cases + // like that, the non-trivial type could be replacing an owned value + // where we lost that our underlying value is None due to + // intermediate aggregate literal operations. In that case, we /do + // not/ want to eliminate the destructure. + if (ownershipKind == ValueOwnershipKind::None && + !operandValue->getType().isTrivial(*Tuple->getFunction())) + return SILValue(); + + return operandValue; }); return true; } @@ -1466,7 +1493,7 @@ static bool isApplyOfBuiltin(SILInstruction &I, BuiltinValueKind kind) { static bool isApplyOfStringConcat(SILInstruction &I) { if (auto *AI = dyn_cast(&I)) if (auto *Fn = AI->getReferencedFunctionOrNull()) - if (Fn->hasSemanticsAttr("string.concat")) + if (Fn->hasSemanticsAttr(semantics::STRING_CONCAT)) return true; return false; } @@ -1501,7 +1528,6 @@ bool ConstantFolder::constantFoldStringConcatenation(ApplyInst *AI) { assert(DeadI); recursivelyDeleteTriviallyDeadInstructions(DeadI, /*force*/ true, RemoveCallback); - WorkList.remove(DeadI); } } // Schedule users of the new instruction for constant folding. @@ -1528,16 +1554,15 @@ constantFoldGlobalStringTablePointerBuiltin(BuiltinInst *bi, bool enableDiagnostics) { // Look through string initializer to extract the string_literal instruction. // - // We allow for a single borrow to be stripped here if we are here in - // [ossa]. The begin borrow occurs b/c SILGen treats builtins as having - // arguments with a +0 convention (implying a borrow). - SILValue builtinOperand = stripBorrow(bi->getOperand(0)); + // We can look through ownership instructions to get to the string value that + // is passed to this builtin. + SILValue builtinOperand = stripOwnershipInsts(bi->getOperand(0)); SILFunction *caller = bi->getFunction(); FullApplySite stringInitSite = FullApplySite::isa(builtinOperand); if (!stringInitSite || !stringInitSite.getReferencedFunctionOrNull() || !stringInitSite.getReferencedFunctionOrNull()->hasSemanticsAttr( - "string.makeUTF8")) { + semantics::STRING_MAKE_UTF8)) { // Emit diagnostics only on non-transparent functions. if (enableDiagnostics && !caller->isTransparent()) { diagnose(caller->getASTContext(), bi->getLoc().getSourceLoc(), @@ -1671,7 +1696,6 @@ ConstantFolder::processWorkList() { // This is used to avoid duplicate error reporting in case we reach the same // instruction from different entry points in the WorkList. llvm::DenseSet ErrorSet; - llvm::SetVector FoldedUsers; CastOptimizer CastOpt(FuncBuilder, nullptr /*SILBuilderContext*/, /* replaceValueUsesAction */ [&](SILValue oldValue, SILValue newValue) { @@ -1726,7 +1750,7 @@ ConstantFolder::processWorkList() { // Schedule users for constant folding. WorkList.insert(AssertConfInt); // Delete the call. - recursivelyDeleteTriviallyDeadInstructions(BI); + eliminateDeadInstruction(BI); InvalidateInstructions = true; continue; @@ -1794,9 +1818,8 @@ ConstantFolder::processWorkList() { if (constantFoldGlobalStringTablePointerBuiltin(cast(I), EnableDiagnostics)) { // Here, the bulitin instruction got folded, so clean it up. - recursivelyDeleteTriviallyDeadInstructions( - I, /*force*/ true, - [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); + eliminateDeadInstruction( + I, [&](SILInstruction *DeadI) { WorkList.remove(DeadI); }); InvalidateInstructions = true; } continue; @@ -1848,7 +1871,7 @@ ConstantFolder::processWorkList() { } // Go through all users of the constant and try to fold them. - FoldedUsers.clear(); + InstructionDeleter deleter; for (auto Result : I->getResults()) { for (auto *Use : Result->getUses()) { SILInstruction *User = Use->getUser(); @@ -1872,7 +1895,7 @@ ConstantFolder::processWorkList() { // this as part of the constant folding logic, because there is no value // they can produce (other than empty tuple, which is wasteful). if (isa(User)) - FoldedUsers.insert(User); + deleter.trackIfDead(User); // See if we have an instruction that is read none and has a stateless // inverse. If we do, add it to the worklist so we can check its users @@ -1938,55 +1961,9 @@ ConstantFolder::processWorkList() { if (C->getDefiningInstruction() == User) continue; - // Ok, we have succeeded. Add user to the FoldedUsers list and perform - // the necessary cleanups, RAUWs, etc. - FoldedUsers.insert(User); + // Ok, we have succeeded. ++NumInstFolded; - InvalidateInstructions = true; - - // If the constant produced a tuple, be smarter than RAUW: explicitly - // nuke any tuple_extract instructions using the apply. This is a - // common case for functions returning multiple values. - if (auto *TI = dyn_cast(C)) { - for (SILValue Result : User->getResults()) { - for (auto UI = Result->use_begin(), UE = Result->use_end(); - UI != UE;) { - Operand *O = *UI++; - - // If the user is a tuple_extract, just substitute the right - // value in. - if (auto *TEI = dyn_cast(O->getUser())) { - SILValue NewVal = TI->getOperand(TEI->getFieldNo()); - TEI->replaceAllUsesWith(NewVal); - TEI->dropAllReferences(); - FoldedUsers.insert(TEI); - if (auto *Inst = NewVal->getDefiningInstruction()) - WorkList.insert(Inst); - continue; - } - - if (auto *DTI = dyn_cast(O->getUser())) { - SILValue NewVal = TI->getOperand(O->getOperandNumber()); - auto OwnershipKind = NewVal.getOwnershipKind(); - if (OwnershipKind.isCompatibleWith( - ValueOwnershipKind::Guaranteed)) { - SILValue DTIResult = DTI->getResult(O->getOperandNumber()); - DTIResult->replaceAllUsesWith(NewVal); - FoldedUsers.insert(DTI); - if (auto *Inst = NewVal->getDefiningInstruction()) - WorkList.insert(Inst); - continue; - } - } - } - } - - if (llvm::all_of(User->getResults(), - [](SILValue v) { return v->use_empty(); })) - FoldedUsers.insert(TI); - } - // We were able to fold, so all users should use the new folded // value. If we don't have any such users, continue. // @@ -2023,11 +2000,16 @@ ConstantFolder::processWorkList() { // In contrast, if we realize that RAUWing %3 does nothing and skip // it, we exit the worklist as expected. SILValue r = User->getResult(Index); - if (r->use_empty()) + if (r->use_empty()) { + deleter.trackIfDead(User); continue; + } // Otherwise, do the RAUW. User->getResult(Index)->replaceAllUsesWith(C); + // Record the user if it is dead to perform the necessary cleanups + // later. + deleter.trackIfDead(User); // The new constant could be further folded now, add it to the // worklist. @@ -2036,18 +2018,12 @@ ConstantFolder::processWorkList() { } } } - // Eagerly DCE. We do this after visiting all users to ensure we don't // invalidate the uses iterator. - ArrayRef UserArray = FoldedUsers.getArrayRef(); - if (!UserArray.empty()) { + deleter.cleanUpDeadInstructions([&](SILInstruction *DeadI) { + WorkList.remove(DeadI); InvalidateInstructions = true; - } - - recursivelyDeleteTriviallyDeadInstructions(UserArray, false, - [&](SILInstruction *DeadI) { - WorkList.remove(DeadI); - }); + }); } // TODO: refactor this code outside of the method. Passes should not merge diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp index 6957266bc7672..d743b79cf6e78 100644 --- a/lib/SILOptimizer/Utils/Devirtualize.cpp +++ b/lib/SILOptimizer/Utils/Devirtualize.cpp @@ -119,9 +119,7 @@ static bool isEffectivelyFinalMethod(FullApplySite applySite, CanType classType, if (!cha) return false; - // This is a private or a module internal class. - // - // We can analyze the class hierarchy rooted at it and + // We can analyze the class hierarchy rooted at this class and // eventually devirtualize a method call more efficiently. ClassHierarchyAnalysis::ClassList subs; @@ -412,7 +410,8 @@ getSubstitutionsForCallee(SILModule &module, CanSILFunctionType baseCalleeType, return SubstitutionMap(); // Add any generic substitutions for the base class. - Type baseSelfType = baseCalleeType->getSelfParameter().getType(); + Type baseSelfType = baseCalleeType->getSelfParameter() + .getArgumentType(module, baseCalleeType); if (auto metatypeType = baseSelfType->getAs()) baseSelfType = metatypeType->getInstanceType(); @@ -437,7 +436,8 @@ getSubstitutionsForCallee(SILModule &module, CanSILFunctionType baseCalleeType, SubstitutionMap origSubMap = applySite.getSubstitutionMap(); Type calleeSelfType = - applySite.getOrigCalleeType()->getSelfParameter().getType(); + applySite.getOrigCalleeType()->getSelfParameter() + .getArgumentType(module, applySite.getOrigCalleeType()); if (auto metatypeType = calleeSelfType->getAs()) calleeSelfType = metatypeType->getInstanceType(); auto *calleeClassDecl = calleeSelfType->getClassOrBoundGenericClass(); @@ -449,7 +449,7 @@ getSubstitutionsForCallee(SILModule &module, CanSILFunctionType baseCalleeType, if (auto calleeClassSig = calleeClassDecl->getGenericSignatureOfContext()) origDepth = calleeClassSig->getGenericParams().back()->getDepth() + 1; - auto baseCalleeSig = baseCalleeType->getGenericSignature(); + auto baseCalleeSig = baseCalleeType->getInvocationGenericSignature(); return SubstitutionMap::combineSubstitutionMaps(baseSubMap, @@ -569,10 +569,11 @@ replaceBeginApplyInst(SILBuilder &builder, SILLocation loc, SILValue token = newBAI->getTokenResult(); // The token will only be used by end_apply and abort_apply. Use that to - // insert the end_borrows we need. + // insert the end_borrows we need /after/ those uses. for (auto *use : token->getUses()) { - SILBuilderWithScope borrowBuilder(use->getUser(), - builder.getBuilderContext()); + SILBuilderWithScope borrowBuilder( + &*std::next(use->getUser()->getIterator()), + builder.getBuilderContext()); for (SILValue borrow : newArgBorrows) { borrowBuilder.createEndBorrow(loc, borrow); } @@ -740,14 +741,16 @@ FullApplySite swift::devirtualizeClassMethod(FullApplySite applySite, auto *f = getTargetClassMethod(module, cd, mi); - CanSILFunctionType genCalleeType = f->getLoweredFunctionType(); + CanSILFunctionType genCalleeType = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); SubstitutionMap subs = getSubstitutionsForCallee( module, genCalleeType, classOrMetatype->getType().getASTType(), applySite); CanSILFunctionType substCalleeType = genCalleeType; if (genCalleeType->isPolymorphic()) - substCalleeType = genCalleeType->substGenericArgs(module, subs); + substCalleeType = genCalleeType->substGenericArgs( + module, subs, TypeExpansionContext(*applySite.getFunction())); SILFunctionConventions substConv(substCalleeType, module); SILBuilderWithScope builder(applySite.getInstruction()); @@ -934,19 +937,20 @@ SubstitutionMap swift::getWitnessMethodSubstitutions(SILModule &module, ApplySite applySite, SILFunction *f, ProtocolConformanceRef cRef) { - auto witnessFnTy = f->getLoweredFunctionType(); + auto witnessFnTy = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); assert(witnessFnTy->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); - auto requirementSig = applySite.getOrigCalleeType()->getGenericSignature(); - auto witnessThunkSig = witnessFnTy->getGenericSignature(); + auto requirementSig = applySite.getOrigCalleeType()->getInvocationGenericSignature(); + auto witnessThunkSig = witnessFnTy->getInvocationGenericSignature(); SubstitutionMap origSubs = applySite.getSubstitutionMap(); auto *mod = module.getSwiftModule(); bool isSelfAbstract = - witnessFnTy->getSelfInstanceType()->is(); - auto *classWitness = witnessFnTy->getWitnessMethodClass(); + witnessFnTy->getSelfInstanceType(module)->is(); + auto *classWitness = witnessFnTy->getWitnessMethodClass(module); return ::getWitnessMethodSubstitutions(mod, cRef, requirementSig, witnessThunkSig, origSubs, @@ -972,8 +976,10 @@ static ApplySite devirtualizeWitnessMethod(ApplySite applySite, SILFunction *f, // Figure out the exact bound type of the function to be called by // applying all substitutions. - auto calleeCanType = f->getLoweredFunctionType(); - auto substCalleeCanType = calleeCanType->substGenericArgs(module, subMap); + auto calleeCanType = f->getLoweredFunctionTypeInContext( + TypeExpansionContext(*applySite.getFunction())); + auto substCalleeCanType = calleeCanType->substGenericArgs( + module, subMap, TypeExpansionContext(*applySite.getFunction())); // Collect arguments from the apply instruction. SmallVector arguments; diff --git a/lib/SILOptimizer/Utils/Existential.cpp b/lib/SILOptimizer/Utils/Existential.cpp index 07a78d2747972..ff2f500583ad4 100644 --- a/lib/SILOptimizer/Utils/Existential.cpp +++ b/lib/SILOptimizer/Utils/Existential.cpp @@ -243,11 +243,34 @@ void ConcreteExistentialInfo::initializeSubstitutionMap( // Construct a single-generic-parameter substitution map directly to the // ConcreteType with this existential's full list of conformances. + // + // NOTE: getOpenedArchetypeSignature() generates the signature for passing an + // opened existential as a generic parameter. No opened archetypes are + // actually involved here--the API is only used as a convenient way to create + // a substitution map. Since opened archetypes have different conformances + // than their corresponding existential, ExistentialConformances needs to be + // filtered when using it with this (phony) generic signature. CanGenericSignature ExistentialSig = - M->getASTContext().getExistentialSignature(ExistentialType, - M->getSwiftModule()); - ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType}, - ExistentialConformances); + M->getASTContext().getOpenedArchetypeSignature(ExistentialType, + M->getSwiftModule()); + ExistentialSubs = SubstitutionMap::get( + ExistentialSig, [&](SubstitutableType *type) { return ConcreteType; }, + [&](CanType /*depType*/, Type /*replaceType*/, + ProtocolDecl *proto) -> ProtocolConformanceRef { + // Directly providing ExistentialConformances to the SubstitionMap will + // fail because of the mismatch between opened archetype conformance and + // existential value conformance. Instead, provide a conformance lookup + // function that pulls only the necessary conformances out of + // ExistentialConformances. This assumes that existential conformances + // are a superset of opened archetype conformances. + auto iter = + llvm::find_if(ExistentialConformances, + [&](const ProtocolConformanceRef &conformance) { + return conformance.getRequirement() == proto; + }); + assert(iter != ExistentialConformances.end() && "missing conformance"); + return *iter; + }); assert(isValid()); } @@ -362,11 +385,11 @@ ConcreteExistentialInfo::ConcreteExistentialInfo(SILValue existential, // We have the open_existential; we still need the conformance. auto ConformanceRef = M->getSwiftModule()->conformsToProtocol(ConcreteTypeCandidate, Protocol); - if (!ConformanceRef) + if (ConformanceRef.isInvalid()) return; // Assert that the conformance is complete. - auto *ConcreteConformance = ConformanceRef.getValue().getConcrete(); + auto *ConcreteConformance = ConformanceRef.getConcrete(); assert(ConcreteConformance->isComplete()); ConcreteType = ConcreteTypeCandidate; diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index f41e67384b16d..438a47ca78e81 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -183,7 +183,7 @@ const SILDebugScope *GenericCloner::remapScope(const SILDebugScope *DS) { else if (ParentFunction) ParentFunction = remapParentFunction( FuncBuilder, M, ParentFunction, SubsMap, - Original.getLoweredFunctionType()->getGenericSignature()); + Original.getLoweredFunctionType()->getInvocationGenericSignature()); auto *ParentScope = DS->Parent.dyn_cast(); auto *RemappedScope = diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 32371021f7114..e91422ba4b17d 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -17,6 +17,7 @@ #include "swift/AST/TypeMatcher.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/Basic/Statistic.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Serialization/SerializedSILLoader.h" @@ -123,7 +124,8 @@ static std::pair getTypeDepthAndWidth(Type t) { for (auto Param : Params) { unsigned TypeWidth; unsigned TypeDepth; - std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Param.getType()); + std::tie(TypeDepth, TypeWidth) = + getTypeDepthAndWidth(Param.getInterfaceType()); if (TypeDepth > MaxTypeDepth) MaxTypeDepth = TypeDepth; Width += TypeWidth; @@ -133,7 +135,8 @@ static std::pair getTypeDepthAndWidth(Type t) { for (auto Result : Results) { unsigned TypeWidth; unsigned TypeDepth; - std::tie(TypeDepth, TypeWidth) = getTypeDepthAndWidth(Result.getType()); + std::tie(TypeDepth, TypeWidth) = + getTypeDepthAndWidth(Result.getInterfaceType()); if (TypeDepth > MaxTypeDepth) MaxTypeDepth = TypeDepth; Width += TypeWidth; @@ -143,7 +146,7 @@ static std::pair getTypeDepthAndWidth(Type t) { unsigned TypeWidth; unsigned TypeDepth; std::tie(TypeDepth, TypeWidth) = - getTypeDepthAndWidth(FnTy->getErrorResult().getType()); + getTypeDepthAndWidth(FnTy->getErrorResult().getInterfaceType()); if (TypeDepth > MaxTypeDepth) MaxTypeDepth = TypeDepth; Width += TypeWidth; @@ -266,9 +269,9 @@ static bool growingSubstitutions(SubstitutionMap Subs1, if (TypeCmp.isPartiallyContainedIn(Type2, Type1)) continue; if (TypeCmp.isPartiallyContainedIn(Type1, Type2)) { - LLVM_DEBUG(llvm::dbgs() << "Type:\n"; Type1.dump(); + LLVM_DEBUG(llvm::dbgs() << "Type:\n"; Type1.dump(llvm::dbgs()); llvm::dbgs() << "is (partially) contained in type:\n"; - Type2.dump(); + Type2.dump(llvm::dbgs()); llvm::dbgs() << "Replacements[" << idx << "] has got bigger since last time.\n"); return true; @@ -325,7 +328,7 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { for (auto Replacement : CurSpecializationInfo->getSubstitutions() .getReplacementTypes()) { - Replacement->dump(); + Replacement->dump(llvm::dbgs()); }); if (CurSpecializationInfo->getParent() == GenericFunc) { @@ -370,18 +373,18 @@ static bool createsInfiniteSpecializationLoop(ApplySite Apply) { static bool shouldNotSpecialize(SILFunction *Callee, SILFunction *Caller, SubstitutionMap Subs = {}) { - if (Callee->hasSemanticsAttr("optimize.sil.specialize.generic.never")) + if (Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER)) return true; if (Caller && Caller->getEffectiveOptimizationMode() == OptimizationMode::ForSize && - Callee->hasSemanticsAttr("optimize.sil.specialize.generic.size.never")) { + Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER)) { return true; } if (Subs.hasAnySubstitutableParams() && - Callee->hasSemanticsAttr("optimize.sil.specialize.generic.partial.never")) + Callee->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER)) return true; return false; @@ -401,7 +404,8 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee, SpecializedGenericEnv = nullptr; SpecializedGenericSig = nullptr; - auto CalleeGenericSig = Callee->getLoweredFunctionType()->getGenericSignature(); + auto CalleeGenericSig = Callee->getLoweredFunctionType() + ->getInvocationGenericSignature(); auto CalleeGenericEnv = Callee->getGenericEnvironment(); this->Callee = Callee; @@ -534,12 +538,12 @@ bool ReabstractionInfo::canBeSpecialized(ApplySite Apply, SILFunction *Callee, return ReInfo.prepareAndCheck(Apply, Callee, ParamSubs); } -ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, - SubstitutionMap ParamSubs, - IsSerialized_t Serialized, - bool ConvertIndirectToDirect, - OptRemark::Emitter *ORE) +ReabstractionInfo::ReabstractionInfo( + ModuleDecl *targetModule, bool isWholeModule, ApplySite Apply, + SILFunction *Callee, SubstitutionMap ParamSubs, IsSerialized_t Serialized, + bool ConvertIndirectToDirect, OptRemark::Emitter *ORE) : ConvertIndirectToDirect(ConvertIndirectToDirect), + TargetModule(targetModule), isWholeModule(isWholeModule), Serialized(Serialized) { if (!prepareAndCheck(Apply, Callee, ParamSubs, ORE)) return; @@ -573,14 +577,16 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee, auto CalleeFnTy = Callee->getLoweredFunctionType(); assert(CalleeFnTy->isPolymorphic()); auto CalleeSubstFnTy = CalleeFnTy->substGenericArgs( - Callee->getModule(), getCalleeParamSubstitutionMap()); + Callee->getModule(), getCalleeParamSubstitutionMap(), + getResilienceExpansion()); assert(!CalleeSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); assert(!CalleeSubstFnTy->hasTypeParameter() && "Substituted callee type should not have type parameters"); SpecializedSubstFnTy = SpecializedFnTy->substGenericArgs( - Callee->getModule(), getCallerParamSubstitutionMap()); + Callee->getModule(), getCallerParamSubstitutionMap(), + getResilienceExpansion()); assert(!SpecializedSubstFnTy->isPolymorphic() && "Substituted callee type should not be polymorphic"); @@ -664,13 +670,10 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { // Check which parameters and results can be converted from // indirect to direct ones. NumFormalIndirectResults = SubstitutedType->getNumIndirectFormalResults(); - Conversions.resize(NumFormalIndirectResults + - SubstitutedType->getParameters().size()); - - CanGenericSignature CanSig; - if (SpecializedGenericSig) - CanSig = SpecializedGenericSig->getCanonicalSignature(); - Lowering::GenericContextScope GenericScope(M.Types, CanSig); + unsigned NumArgs = NumFormalIndirectResults + + SubstitutedType->getParameters().size(); + Conversions.resize(NumArgs); + TrivialArgs.resize(NumArgs); SILFunctionConventions substConv(SubstitutedType, M); @@ -684,12 +687,15 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { assert(RI.isFormalIndirect()); auto ResultTy = substConv.getSILType(RI); + ResultTy = Callee->mapTypeIntoContext(ResultTy); auto &TL = M.Types.getTypeLowering(ResultTy, getResilienceExpansion()); - if (TL.isLoadable() && !RI.getType()->isVoid() && + if (TL.isLoadable() && !RI.getReturnValueType(M, SubstitutedType)->isVoid() && shouldExpand(M, ResultTy)) { Conversions.set(IdxForResult); + if (TL.isTrivial()) + TrivialArgs.set(IdxForResult); break; } ++IdxForResult; @@ -703,6 +709,7 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { ++IdxForParam; auto ParamTy = substConv.getSILType(PI); + ParamTy = Callee->mapTypeIntoContext(ParamTy); auto &TL = M.Types.getTypeLowering(ParamTy, getResilienceExpansion()); @@ -714,6 +721,8 @@ void ReabstractionInfo::createSubstitutedAndSpecializedTypes() { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: Conversions.set(IdxToInsert); + if (TL.isTrivial()) + TrivialArgs.set(IdxToInsert); break; case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_Inout: @@ -744,16 +753,13 @@ ReabstractionInfo::createSubstitutedType(SILFunction *OrigF, SpecializedGenericEnv = nullptr; } - CanGenericSignature CanSpecializedGenericSig; - if (SpecializedGenericSig) - CanSpecializedGenericSig = SpecializedGenericSig->getCanonicalSignature(); + auto CanSpecializedGenericSig = SpecializedGenericSig.getCanonicalSignature(); // First substitute concrete types into the existing function type. CanSILFunctionType FnTy; { - Lowering::GenericContextScope GenericScope(M.Types, - CanSpecializedGenericSig); - FnTy = OrigF->getLoweredFunctionType()->substGenericArgs(M, SubstMap); + FnTy = OrigF->getLoweredFunctionType()->substGenericArgs( + M, SubstMap, getResilienceExpansion()); // FIXME: Some of the added new requirements may not have been taken into // account by the substGenericArgs. So, canonicalize in the context of the // specialized signature. @@ -769,11 +775,10 @@ ReabstractionInfo::createSubstitutedType(SILFunction *OrigF, // Use the new specialized generic signature. auto NewFnTy = SILFunctionType::get( - CanSpecializedGenericSig, FnTy->getExtInfo(), - FnTy->getCoroutineKind(), FnTy->getCalleeConvention(), - FnTy->getParameters(), FnTy->getYields(), - FnTy->getResults(), FnTy->getOptionalErrorResult(), - M.getASTContext(), FnTy->getWitnessMethodConformanceOrNone()); + CanSpecializedGenericSig, FnTy->getExtInfo(), FnTy->getCoroutineKind(), + FnTy->getCalleeConvention(), FnTy->getParameters(), FnTy->getYields(), + FnTy->getResults(), FnTy->getOptionalErrorResult(), SubstitutionMap(), + false, M.getASTContext(), FnTy->getWitnessMethodConformanceOrInvalid()); // This is an interface type. It should not have any archetypes. assert(!NewFnTy->hasArchetype()); @@ -791,18 +796,15 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const { unsigned IndirectResultIdx = 0; for (SILResultInfo RI : SubstFTy->getResults()) { if (RI.isFormalIndirect()) { + bool isTrivial = TrivialArgs.test(IndirectResultIdx); if (isFormalResultConverted(IndirectResultIdx++)) { // Convert the indirect result to a direct result. - SILType SILResTy = SILType::getPrimitiveObjectType(RI.getType()); - auto &TL = M.Types.getTypeLowering(SILResTy, - getResilienceExpansion()); - // Indirect results are passed as owned, so we also need to pass the // direct result as owned (except it's a trivial type). - auto C = (TL.isTrivial() + auto C = (isTrivial ? ResultConvention::Unowned : ResultConvention::Owned); - SpecializedResults.push_back(SILResultInfo(RI.getType(), C)); + SpecializedResults.push_back(SILResultInfo(RI.getReturnValueType(M, SubstFTy), C)); continue; } } @@ -811,6 +813,7 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const { } unsigned ParamIdx = 0; for (SILParameterInfo PI : SubstFTy->getParameters()) { + bool isTrivial = TrivialArgs.test(param2ArgIndex(ParamIdx)); if (!isParamConverted(ParamIdx++)) { // No conversion: re-use the original, substituted parameter info. SpecializedParams.push_back(PI); @@ -818,34 +821,29 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const { } // Convert the indirect parameter to a direct parameter. - SILType SILParamTy = SILType::getPrimitiveObjectType(PI.getType()); - auto &TL = M.Types.getTypeLowering(SILParamTy, - getResilienceExpansion()); - // Indirect parameters are passed as owned/guaranteed, so we also // need to pass the direct/guaranteed parameter as // owned/guaranteed (except it's a trivial type). auto C = ParameterConvention::Direct_Unowned; - if (!TL.isTrivial()) { + if (!isTrivial) { if (PI.isGuaranteed()) { C = ParameterConvention::Direct_Guaranteed; } else { C = ParameterConvention::Direct_Owned; } } - SpecializedParams.push_back(SILParameterInfo(PI.getType(), C)); + SpecializedParams.push_back(SILParameterInfo(PI.getArgumentType(M, SubstFTy), C)); } for (SILYieldInfo YI : SubstFTy->getYields()) { // For now, always just use the original, substituted parameter info. SpecializedYields.push_back(YI); } return SILFunctionType::get( - SubstFTy->getGenericSignature(), - SubstFTy->getExtInfo(), SubstFTy->getCoroutineKind(), - SubstFTy->getCalleeConvention(), + SubstFTy->getInvocationGenericSignature(), SubstFTy->getExtInfo(), + SubstFTy->getCoroutineKind(), SubstFTy->getCalleeConvention(), SpecializedParams, SpecializedYields, SpecializedResults, - SubstFTy->getOptionalErrorResult(), M.getASTContext(), - SubstFTy->getWitnessMethodConformanceOrNone()); + SubstFTy->getOptionalErrorResult(), SubstitutionMap(), false, + M.getASTContext(), SubstFTy->getWitnessMethodConformanceOrInvalid()); } /// Create a new generic signature from an existing one by adding @@ -883,7 +881,7 @@ void ReabstractionInfo::performFullSpecializationPreparation( ClonerParamSubMap = ParamSubs; SubstitutedType = Callee->getLoweredFunctionType()->substGenericArgs( - M, ClonerParamSubMap); + M, ClonerParamSubMap, getResilienceExpansion()); CallerParamSubMap = {}; createSubstitutedAndSpecializedTypes(); } @@ -981,7 +979,7 @@ static void collectRequirements(ArchetypeType *Archetype, GenericSignature Sig, if (UsedGenericParams.size() != 1) { llvm::dbgs() << "Strange requirement for " << CurrentGP->getCanonicalType() << "\n"; - Req.dump(); + Req.dump(llvm::dbgs()); } assert(UsedGenericParams.size() == 1); CollectedReqs.push_back(Req); @@ -1064,7 +1062,7 @@ shouldBePartiallySpecialized(Type Replacement, LLVM_DEBUG(llvm::dbgs() << "Requirements of the archetype depend on " "other caller's generic parameters! " "It cannot be partially specialized:\n"; - UsedArchetype->dump(); + UsedArchetype->dump(llvm::dbgs()); llvm::dbgs() << "This archetype is used in the substitution: " << Replacement << "\n"); return false; @@ -1313,11 +1311,11 @@ void FunctionSignaturePartialSpecializer:: [&](SubstitutableType *type) -> Type { LLVM_DEBUG(llvm::dbgs() << "Mapping specialized interface type to " "caller archetype:\n"; - llvm::dbgs() << "Interface type: "; type->dump(); + llvm::dbgs() << "Interface type: "; type->dump(llvm::dbgs()); llvm::dbgs() << "Archetype: "; auto Archetype = SpecializedInterfaceToCallerArchetypeMapping.lookup(type); - if (Archetype) Archetype->dump(); + if (Archetype) Archetype->dump(llvm::dbgs()); else llvm::dbgs() << "Not found!\n";); return SpecializedInterfaceToCallerArchetypeMapping.lookup(type); }, @@ -1350,10 +1348,10 @@ void FunctionSignaturePartialSpecializer:: assert(CallerGenericParam->is()); LLVM_DEBUG(llvm::dbgs() << "\n\nChecking used caller archetype:\n"; - CallerArchetype->dump(); + CallerArchetype->dump(llvm::dbgs()); llvm::dbgs() << "It corresponds to the caller generic " "parameter:\n"; - CallerGenericParam->dump()); + CallerGenericParam->dump(llvm::dbgs())); // Create an equivalent generic parameter. auto SubstGenericParam = createGenericParam(); @@ -1369,7 +1367,7 @@ void FunctionSignaturePartialSpecializer:: LLVM_DEBUG(llvm::dbgs() << "\nCreated a new specialized generic " "parameter:\n"; - SubstGenericParam->dump(); + SubstGenericParam->dump(llvm::dbgs()); llvm::dbgs() << "Created a mapping " "(caller interface -> specialize interface):\n" << CallerGenericParam << " -> " @@ -1391,13 +1389,14 @@ void FunctionSignaturePartialSpecializer:: CalleeGenericSig->getCanonicalTypeInContext(CanTy); auto Replacement = CanTyInContext.subst(CalleeInterfaceToCallerArchetypeMap); LLVM_DEBUG(llvm::dbgs() << "\n\nChecking callee generic parameter:\n"; - CanTy->dump()); + CanTy->dump(llvm::dbgs())); if (!Replacement) { LLVM_DEBUG(llvm::dbgs() << "No replacement found. Skipping.\n"); continue; } - LLVM_DEBUG(llvm::dbgs() << "Replacement found:\n"; Replacement->dump()); + LLVM_DEBUG(llvm::dbgs() << "Replacement found:\n"; + Replacement->dump(llvm::dbgs())); bool ShouldSpecializeGP = shouldBePartiallySpecialized( Replacement, CallerGenericSig, CallerGenericEnv); @@ -1419,7 +1418,7 @@ void FunctionSignaturePartialSpecializer:: LLVM_DEBUG(llvm::dbgs() << "\nCreated a new specialized generic " "parameter:\n"; - SubstGenericParam->dump(); + SubstGenericParam->dump(llvm::dbgs()); llvm::dbgs() << "Created a mapping " "(callee interface -> specialized interface):\n" << CanTy << " -> " @@ -1449,7 +1448,8 @@ void FunctionSignaturePartialSpecializer:: SpecializedReplacementCallerInterfaceTy); AllRequirements.push_back(Req); - LLVM_DEBUG(llvm::dbgs() << "Added a requirement:\n"; Req.dump()); + LLVM_DEBUG(llvm::dbgs() << "Added a requirement:\n"; + Req.dump(llvm::dbgs())); if (ReplacementCallerInterfaceTy->is()) { // Remember that the new generic parameter corresponds @@ -1489,7 +1489,7 @@ void FunctionSignaturePartialSpecializer::addRequirements( ArrayRef Reqs, SubstitutionMap &SubsMap) { for (auto &reqReq : Reqs) { LLVM_DEBUG(llvm::dbgs() << "\n\nRe-mapping the requirement:\n"; - reqReq.dump()); + reqReq.dump(llvm::dbgs())); AllRequirements.push_back(*reqReq.subst(SubsMap)); } } @@ -1505,7 +1505,7 @@ void FunctionSignaturePartialSpecializer::addCallerRequirements() { if (!CollectedReqs.empty()) { LLVM_DEBUG(llvm::dbgs() << "Adding caller archetype requirements:\n"; for (auto Req : CollectedReqs) { - Req.dump(); + Req.dump(llvm::dbgs()); } CallerInterfaceToSpecializedInterfaceMap.dump(llvm::dbgs()); ); @@ -1543,7 +1543,7 @@ SubstitutionMap FunctionSignaturePartialSpecializer::computeClonerParamSubs() { LLVM_DEBUG(llvm::dbgs() << "\ngetSubstitution for ClonerParamSubs:\n" << Type(type) << "\n" << "in generic signature:\n"; - CalleeGenericSig->dump()); + CalleeGenericSig->print(llvm::dbgs())); auto SpecializedInterfaceTy = Type(type).subst(CalleeInterfaceToSpecializedInterfaceMap); return SpecializedGenericEnv->mapTypeIntoContext( @@ -1599,7 +1599,7 @@ void FunctionSignaturePartialSpecializer:: CalleeGenericSig, CalleeGenericEnv, Requirements, M); if (GenPair.second) { - SpecializedGenericSig = GenPair.second->getCanonicalSignature(); + SpecializedGenericSig = GenPair.second.getCanonicalSignature(); SpecializedGenericEnv = GenPair.first; } @@ -1640,7 +1640,7 @@ void FunctionSignaturePartialSpecializer::createSpecializedGenericSignature( auto GenPair = getSpecializedGenericEnvironmentAndSignature(); if (GenPair.second) { - SpecializedGenericSig = GenPair.second->getCanonicalSignature(); + SpecializedGenericSig = GenPair.second.getCanonicalSignature(); SpecializedGenericEnv = GenPair.first; computeSpecializedInterfaceToCallerArchetypeMap(); } @@ -1681,19 +1681,20 @@ void ReabstractionInfo::performPartialSpecializationPreparation( CanGenericSignature CallerGenericSig; GenericEnvironment *CallerGenericEnv = nullptr; if (Caller) { - CallerGenericSig = Caller->getLoweredFunctionType()->getGenericSignature(); + CallerGenericSig = Caller->getLoweredFunctionType() + ->getInvocationGenericSignature(); CallerGenericEnv = Caller->getGenericEnvironment(); } // Callee is the generic function being called by the apply instruction. auto CalleeFnTy = Callee->getLoweredFunctionType(); - auto CalleeGenericSig = CalleeFnTy->getGenericSignature(); + auto CalleeGenericSig = CalleeFnTy->getInvocationGenericSignature(); auto CalleeGenericEnv = Callee->getGenericEnvironment(); LLVM_DEBUG(llvm::dbgs() << "\n\nTrying partial specialization for: " << Callee->getName() << "\n"; llvm::dbgs() << "Callee generic signature is:\n"; - CalleeGenericSig->dump()); + CalleeGenericSig->print(llvm::dbgs())); FunctionSignaturePartialSpecializer FSPS(M, CallerGenericSig, CallerGenericEnv, @@ -1720,7 +1721,8 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( if (SpecializedGenericSig) { LLVM_DEBUG(llvm::dbgs() << "\nCreated SpecializedGenericSig:\n"; - SpecializedGenericSig->dump(); SpecializedGenericEnv->dump()); + SpecializedGenericSig->print(llvm::dbgs()); + SpecializedGenericEnv->dump(llvm::dbgs())); } // Create substitution lists for the caller and cloner. @@ -1748,8 +1750,10 @@ void ReabstractionInfo::finishPartialSpecializationPreparation( } /// This constructor is used when processing @_specialize. -ReabstractionInfo::ReabstractionInfo(SILFunction *Callee, - GenericSignature SpecializedSig) { +ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule, + bool isWholeModule, SILFunction *Callee, + GenericSignature SpecializedSig) + : TargetModule(targetModule), isWholeModule(isWholeModule) { Serialized = Callee->isSerialized(); if (shouldNotSpecialize(Callee, nullptr)) @@ -1761,7 +1765,7 @@ ReabstractionInfo::ReabstractionInfo(SILFunction *Callee, SILModule &M = Callee->getModule(); auto CalleeGenericSig = - Callee->getLoweredFunctionType()->getGenericSignature(); + Callee->getLoweredFunctionType()->getInvocationGenericSignature(); auto *CalleeGenericEnv = Callee->getGenericEnvironment(); FunctionSignaturePartialSpecializer FSPS(M, @@ -1925,10 +1929,11 @@ static void prepareCallArguments(ApplySite AI, SILBuilder &Builder, /// Return a substituted callee function type. static CanSILFunctionType -getCalleeSubstFunctionType(SILValue Callee, SubstitutionMap Subs) { +getCalleeSubstFunctionType(SILValue Callee, SubstitutionMap Subs, + TypeExpansionContext context) { // Create a substituted callee type. auto CanFnTy = Callee->getType().castTo(); - return CanFnTy->substGenericArgs(*Callee->getModule(), Subs); + return CanFnTy->substGenericArgs(*Callee->getModule(), Subs, context); } /// Create a new apply based on an old one, but with a different @@ -1949,7 +1954,8 @@ static ApplySite replaceWithSpecializedCallee(ApplySite AI, Subs = ReInfo.getCallerParamSubstitutionMap(); } - auto CalleeSubstFnTy = getCalleeSubstFunctionType(Callee, Subs); + auto CalleeSubstFnTy = + getCalleeSubstFunctionType(Callee, Subs, ReInfo.getResilienceExpansion()); auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy); SILFunctionConventions substConv(CalleeSubstFnTy, Builder.getModule()); @@ -1979,7 +1985,7 @@ static ApplySite replaceWithSpecializedCallee(ApplySite AI, A->isNonThrowing()); if (StoreResultTo) { assert(substConv.useLoweredAddresses()); - if (!CalleeSILSubstFnTy.isNoReturnFunction()) { + if (!CalleeSILSubstFnTy.isNoReturnFunction(Builder.getModule())) { // Store the direct result to the original result address. fixUsedVoidType(A, Loc, Builder); Builder.createStore(Loc, NewAI, StoreResultTo, @@ -2079,11 +2085,6 @@ SILFunction *ReabstractionThunkGenerator::createThunk() { Thunk->setGenericEnvironment(ReInfo.getSpecializedGenericEnvironment()); - // Set proper generic context scope for the type lowering. - CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType(); - Lowering::GenericContextScope GenericScope(M.Types, - SpecType->getGenericSignature()); - SILBasicBlock *EntryBB = Thunk->createBasicBlock(); SILBuilder Builder(EntryBB); @@ -2119,7 +2120,8 @@ SILFunction *ReabstractionThunkGenerator::createThunk() { Builder.createStore(Loc, ReturnValue, ReturnValueAddr, StoreOwnershipQualifier::Unqualified); SILType VoidTy = - OrigPAI->getSubstCalleeType()->getDirectFormalResultsType(); + OrigPAI->getSubstCalleeType() + ->getDirectFormalResultsType(M); assert(VoidTy.isVoid()); ReturnValue = Builder.createTuple(Loc, VoidTy, {}); } @@ -2253,9 +2255,10 @@ static bool createPrespecialized(StringRef UnspecializedName, if (!UnspecFunc || !UnspecFunc->isDefinition()) return false; - ReabstractionInfo ReInfo(ApplySite(), UnspecFunc, Apply.getSubstitutionMap(), - IsNotSerialized, /*ConvertIndirectToDirect=*/true, - nullptr); + ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), ApplySite(), + UnspecFunc, Apply.getSubstitutionMap(), + IsNotSerialized, + /*ConvertIndirectToDirect=*/true, nullptr); if (!ReInfo.canBeSpecialized()) return false; @@ -2354,9 +2357,10 @@ void swift::trySpecializeApplyOfGeneric( Serialized = IsNotSerialized; } - ReabstractionInfo ReInfo(Apply, RefF, Apply.getSubstitutionMap(), - Serialized, /*ConvertIndirectToDirect=*/true, - &ORE); + ReabstractionInfo ReInfo(FuncBuilder.getModule().getSwiftModule(), + FuncBuilder.getModule().isWholeModule(), Apply, RefF, + Apply.getSubstitutionMap(), Serialized, + /*ConvertIndirectToDirect=*/true, &ORE); if (!ReInfo.canBeSpecialized()) return; diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 76f6d6551364b..715ba5de296da 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -12,6 +12,7 @@ #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" @@ -26,6 +27,7 @@ #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/ConstExpr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSwitch.h" @@ -170,9 +172,349 @@ bool swift::isIntermediateRelease(SILInstruction *inst, return false; } +static bool hasOnlyEndOfScopeOrDestroyUses(SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + bool isDebugUser = user->isDebugInstruction(); + if (!isa(user) && !isEndOfScopeMarker(user) && + !isDebugUser) + return false; + // Include debug uses only in Onone mode. + if (isDebugUser && inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization) + return false; + } + } + return true; +} + +unsigned swift::getNumInOutArguments(FullApplySite applySite) { + assert(applySite); + auto substConv = applySite.getSubstCalleeConv(); + unsigned numIndirectResults = substConv.getNumIndirectSILResults(); + unsigned numInOutArguments = 0; + for (unsigned argIndex = 0; argIndex < applySite.getNumArguments(); + argIndex++) { + // Skip indirect results. + if (argIndex < numIndirectResults) { + continue; + } + auto paramNumber = argIndex - numIndirectResults; + auto ParamConvention = + substConv.getParameters()[paramNumber].getConvention(); + switch (ParamConvention) { + case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: { + numInOutArguments++; + break; + default: + break; + } + } + } + return numInOutArguments; +} + +/// Return true iff the \p applySite calls a constant-evaluable function and +/// it is non-generic and read/destroy only, which means that the call can do +/// only the following and nothing else: +/// (1) The call may read any memory location. +/// (2) The call may destroy owned parameters i.e., consume them. +/// (3) The call may write into memory locations newly created by the call. +/// (4) The call may use assertions, which traps at runtime on failure. +/// (5) The call may return a non-generic value. +/// Essentially, these are calls whose "effect" is visible only in their return +/// value or through the parameters that are destroyed. The return value +/// is also guaranteed to have value semantics as it is non-generic and +/// reference semantics is not constant evaluable. +static bool isNonGenericReadOnlyConstantEvaluableCall(FullApplySite applySite) { + assert(applySite); + SILFunction *callee = applySite.getCalleeFunction(); + if (!callee || !isConstantEvaluable(callee)) { + return false; + } + return !applySite.hasSubstitutions() && !getNumInOutArguments(applySite) && + !applySite.getNumIndirectSILResults(); +} + +/// A scope-affecting instruction is an instruction which may end the scope of +/// its operand or may produce scoped results that require cleaning up. E.g. +/// begin_borrow, begin_access, copy_value, a call that produces a owned value +/// are scoped instructions. The scope of the results of the first two +/// instructions end with an end_borrow/acess instruction, while those of the +/// latter two end with a consuming operation like destroy_value instruction. +/// These instruction may also end the scope of its operand e.g. a call could +/// consume owned arguments thereby ending its scope. Dead-code eliminating a +/// scope-affecting instruction requires fixing the lifetime of the non-trivial +/// operands of the instruction and requires cleaning up the end-of-scope uses +/// of non-trivial results. +/// +/// \param inst instruction that checked for liveness. +static bool isScopeAffectingInstructionDead(SILInstruction *inst) { + SILFunction *fun = inst->getFunction(); + assert(fun && "Instruction has no function."); + // Only support ownership SIL for scoped instructions. + if (!fun->hasOwnership()) { + return false; + } + // If the instruction has any use other than end of scope use or destroy_value + // use, bail out. + if (!hasOnlyEndOfScopeOrDestroyUses(inst)) { + return false; + } + // If inst is a copy or beginning of scope, inst is dead, since we know that + // it is used only in a destroy_value or end-of-scope instruction. + if (getSingleValueCopyOrCast(inst)) + return true; + + switch (inst->getKind()) { + case SILInstructionKind::LoadBorrowInst: { + // A load_borrow only used in an end_borrow is dead. + return true; + } + case SILInstructionKind::LoadInst: { + LoadOwnershipQualifier loadOwnershipQual = + cast(inst)->getOwnershipQualifier(); + // If the load creates a copy, it is dead, since we know that if at all it + // is used, it is only in a destroy_value instruction. + return (loadOwnershipQual == LoadOwnershipQualifier::Copy || + loadOwnershipQual == LoadOwnershipQualifier::Trivial); + // TODO: we can handle load [take] but we would have to know that the + // operand has been consumed. Note that OperandOwnershipKind map does not + // say this for load. + } + case SILInstructionKind::PartialApplyInst: { + // Partial applies that are only used in destroys cannot have any effect on + // the program state, provided the values they capture are explicitly + // destroyed. + return true; + } + case SILInstructionKind::StructInst: + case SILInstructionKind::EnumInst: + case SILInstructionKind::TupleInst: + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::DestructureStructInst: + case SILInstructionKind::DestructureTupleInst: { + // All these ownership forwarding instructions that are only used in + // destroys are dead provided the values they consume are destroyed + // explicitly. + return true; + } + case SILInstructionKind::ApplyInst: { + // The following property holds for constant-evaluable functions that do + // not take arguments of generic type: + // 1. they do not create objects having deinitializers with global + // side effects, as they can only create objects consisting of trivial + // values, (non-generic) arrays and strings. + // 2. they do not use global variables or call arbitrary functions with + // side effects. + // The above two properties imply that a value returned by a constant + // evaluable function does not have a deinitializer with global side + // effects. Therefore, the deinitializer can be sinked. + // + // A generic, read-only constant evaluable call only reads and/or + // destroys its (non-generic) parameters. It therefore cannot have any + // side effects (note that parameters being non-generic have value + // semantics). Therefore, the constant evaluable call can be removed + // provided the parameter lifetimes are handled correctly, which is taken + // care of by the function: \c deleteInstruction. + FullApplySite applySite(cast(inst)); + return isNonGenericReadOnlyConstantEvaluableCall(applySite); + } + default: { + return false; + } + } +} + +void InstructionDeleter::trackIfDead(SILInstruction *inst) { + if (isInstructionTriviallyDead(inst) || + isScopeAffectingInstructionDead(inst)) { + assert(!isIncidentalUse(inst) && !isa(inst) && + "Incidental uses cannot be removed in isolation. " + "They would be removed iff the operand is dead"); + deadInstructions.insert(inst); + } +} + +/// Given an \p operand that belongs to an instruction that will be removed, +/// destroy the operand just before the instruction, if the instruction consumes +/// \p operand. This function will result in a double consume, which is expected +/// to be resolved when the caller deletes the original instruction. This +/// function works only on ownership SIL. +static void destroyConsumedOperandOfDeadInst(Operand &operand) { + assert(operand.get() && operand.getUser()); + SILInstruction *deadInst = operand.getUser(); + SILFunction *fun = deadInst->getFunction(); + assert(fun->hasOwnership()); + + SILValue operandValue = operand.get(); + if (operandValue->getType().isTrivial(*fun)) + return; + // Ignore type-dependent operands which are not real operands but are just + // there to create use-def dependencies. + if (deadInst->isTypeDependentOperand(operand)) + return; + // A scope ending instruction cannot be deleted in isolation without removing + // the instruction defining its operand as well. + assert(!isEndOfScopeMarker(deadInst) && !isa(deadInst) && + !isa(deadInst) && + "lifetime ending instruction is deleted without its operand"); + ValueOwnershipKind operandOwnershipKind = operandValue.getOwnershipKind(); + UseLifetimeConstraint lifetimeConstraint = + operand.getOwnershipKindMap().getLifetimeConstraint(operandOwnershipKind); + if (lifetimeConstraint == UseLifetimeConstraint::MustBeInvalidated) { + // Since deadInst cannot be an end-of-scope instruction (asserted above), + // this must be a consuming use of an owned value. + assert(operandOwnershipKind == ValueOwnershipKind::Owned); + SILBuilderWithScope builder(deadInst); + builder.emitDestroyValueOperation(deadInst->getLoc(), operandValue); + } +} + namespace { using CallbackTy = llvm::function_ref; -} // end anonymous namespace +} // namespace + +void InstructionDeleter::deleteInstruction(SILInstruction *inst, + CallbackTy callback, + bool fixOperandLifetimes) { + // We cannot fix operand lifetimes in non-ownership SIL. + assert(!fixOperandLifetimes || inst->getFunction()->hasOwnership()); + // Collect instruction and its immediate uses and check if they are all + // incidental uses. Also, invoke the callback on the instruction and its uses. + // Note that the Callback is invoked before deleting anything to ensure that + // the SIL is valid at the time of the callback. + SmallVector toDeleteInsts; + toDeleteInsts.push_back(inst); + callback(inst); + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + assert(isIncidentalUse(user) || isa(user)); + callback(user); + toDeleteInsts.push_back(user); + } + } + // Record definitions of instruction's operands. Also, in case an operand is + // consumed by inst, emit necessary compensation code. + SmallVector operandDefinitions; + for (Operand &operand : inst->getAllOperands()) { + SILValue operandValue = operand.get(); + assert(operandValue && + "Instruction's operand are deleted before the instruction"); + SILInstruction *defInst = operandValue->getDefiningInstruction(); + // If the operand has a defining instruction, it could be potentially + // dead. Therefore, record the definition. + if (defInst) + operandDefinitions.push_back(defInst); + // The scope of the operand could be ended by inst. Therefore, emit + // any compensating code needed to end the scope of the operand value + // once inst is deleted. + if (fixOperandLifetimes) + destroyConsumedOperandOfDeadInst(operand); + } + // First drop all references from all instructions to be deleted and then + // erase the instruction. Note that this is done in this order so that when an + // instruction is deleted, its uses would have dropped their references. + // Note that the toDeleteInsts must also be removed from the tracked + // deadInstructions. + for (SILInstruction *inst : toDeleteInsts) { + deadInstructions.remove(inst); + inst->dropAllReferences(); + } + for (SILInstruction *inst : toDeleteInsts) { + inst->eraseFromParent(); + } + // Record operand definitions that become dead now. + for (SILInstruction *operandValInst : operandDefinitions) { + trackIfDead(operandValInst); + } +} + +void InstructionDeleter::cleanUpDeadInstructions(CallbackTy callback) { + SILFunction *fun = nullptr; + if (!deadInstructions.empty()) + fun = deadInstructions.front()->getFunction(); + while (!deadInstructions.empty()) { + SmallVector currentDeadInsts(deadInstructions.begin(), + deadInstructions.end()); + // Though deadInstructions is cleared here, calls to deleteInstruction may + // append to deadInstructions. So we need to iterate until this it is empty. + deadInstructions.clear(); + for (SILInstruction *deadInst : currentDeadInsts) { + // deadInst will not have been deleted in the previous iterations, + // because, by definition, deleteInstruction will only delete an earlier + // instruction and its incidental/destroy uses. The former cannot be + // deadInst as deadInstructions is a set vector, and the latter cannot be + // in deadInstructions as they are incidental uses which are never added + // to deadInstructions. + deleteInstruction(deadInst, callback, /*Fix lifetime of operands*/ + fun->hasOwnership()); + } + } +} + +static bool hasOnlyIncidentalUses(SILInstruction *inst, + bool disallowDebugUses = false) { + for (SILValue result : inst->getResults()) { + for (Operand *use : result->getUses()) { + SILInstruction *user = use->getUser(); + if (!isIncidentalUse(user)) + return false; + if (disallowDebugUses && user->isDebugInstruction()) + return false; + } + } + return true; +} + +void InstructionDeleter::deleteIfDead(SILInstruction *inst, + CallbackTy callback) { + if (isInstructionTriviallyDead(inst) || + isScopeAffectingInstructionDead(inst)) { + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ true); + } +} + +void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst, + CallbackTy callback) { + SILFunction *fun = inst->getFunction(); + assert(fun->hasOwnership()); + bool disallowDebugUses = + fun->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ true); +} + +void InstructionDeleter::forceDelete(SILInstruction *inst, + CallbackTy callback) { + bool disallowDebugUses = + inst->getFunction()->getEffectiveOptimizationMode() <= + OptimizationMode::NoOptimization; + assert(hasOnlyIncidentalUses(inst, disallowDebugUses)); + deleteInstruction(inst, callback, /*Fix lifetime of operands*/ false); +} + +void InstructionDeleter::recursivelyDeleteUsersIfDead(SILInstruction *inst, + CallbackTy callback) { + SmallVector users; + for (SILValue result : inst->getResults()) + for (Operand *use : result->getUses()) + users.push_back(use->getUser()); + + for (SILInstruction *user : users) + recursivelyDeleteUsersIfDead(user, callback); + deleteIfDead(inst, callback); +} + +void swift::eliminateDeadInstruction(SILInstruction *inst, + CallbackTy callback) { + InstructionDeleter deleter; + deleter.trackIfDead(inst); + deleter.cleanUpDeadInstructions(callback); +} void swift::recursivelyDeleteTriviallyDeadInstructions( ArrayRef ia, bool force, CallbackTy callback) { @@ -204,8 +546,8 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( // If the operand is an instruction that is only used by the instruction // being deleted, delete it. if (auto *operandValInst = operandVal->getDefiningInstruction()) - if (!deadInsts.count(operandValInst) - && isInstructionTriviallyDead(operandValInst)) + if (!deadInsts.count(operandValInst) && + isInstructionTriviallyDead(operandValInst)) nextInsts.insert(operandValInst); } @@ -725,7 +1067,7 @@ bool StringConcatenationOptimizer::extractStringConcatOperands() { if (!Fn) return false; - if (ai->getNumArguments() != 3 || !Fn->hasSemanticsAttr("string.concat")) + if (ai->getNumArguments() != 3 || !Fn->hasSemanticsAttr(semantics::STRING_CONCAT)) return false; // Left and right operands of a string concatenation operation. @@ -756,9 +1098,9 @@ bool StringConcatenationOptimizer::extractStringConcatOperands() { // makeUTF8 should have following parameters: // (start: RawPointer, utf8CodeUnitCount: Word, isASCII: Int1) - if (!((friLeftFun->hasSemanticsAttr("string.makeUTF8") + if (!((friLeftFun->hasSemanticsAttr(semantics::STRING_MAKE_UTF8) && aiLeftOperandsNum == 5) - || (friRightFun->hasSemanticsAttr("string.makeUTF8") + || (friRightFun->hasSemanticsAttr(semantics::STRING_MAKE_UTF8) && aiRightOperandsNum == 5))) return false; @@ -1019,7 +1361,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc, // possible for that value. // If we have qualified ownership, we should just emit a destroy value. - if (arg->getFunction()->hasOwnership()) { + if (builder.getFunction().hasOwnership()) { callbacks.createdNewInst(builder.createDestroyValue(loc, arg)); return; } @@ -1224,7 +1566,7 @@ bool swift::simplifyUsers(SingleValueInstruction *inst) { /// True if a type can be expanded without a significant increase to code size. bool swift::shouldExpand(SILModule &module, SILType ty) { // FIXME: Expansion - auto expansion = ResilienceExpansion::Minimal; + auto expansion = TypeExpansionContext::minimal(); if (module.Types.getTypeLowering(ty, expansion).isAddressOnly()) { return false; @@ -1433,13 +1775,19 @@ bool swift::calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl) { if (decl.isForeign) return false; + auto *afd = decl.getAbstractFunctionDecl(); + assert(afd && "Expected abstract function decl!"); + return calleesAreStaticallyKnowable(module, afd); +} + +/// Are the callees that could be called through Decl statically +/// knowable based on the Decl and the compilation mode? +bool swift::calleesAreStaticallyKnowable(SILModule &module, + AbstractFunctionDecl *afd) { const DeclContext *assocDC = module.getAssociatedContext(); if (!assocDC) return false; - auto *afd = decl.getAbstractFunctionDecl(); - assert(afd && "Expected abstract function decl!"); - // Only handle members defined within the SILModule's associated context. if (!afd->isChildContextOf(assocDC)) return false; @@ -1575,3 +1923,10 @@ void swift::insertDestroyOfCapturedArguments( releasePartialApplyCapturedArg(builder, loc, arg.get(), paramInfo); } } + +AbstractFunctionDecl *swift::getBaseMethod(AbstractFunctionDecl *FD) { + while (FD->getOverriddenDecl()) { + FD = FD->getOverriddenDecl(); + } + return FD; +} diff --git a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp index 93dcad0c477b6..80992cb15ad58 100644 --- a/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp +++ b/lib/SILOptimizer/Utils/LoadStoreOptUtils.cpp @@ -31,10 +31,9 @@ removeLSLocations(LSLocationValueMap &Values, LSLocationList &NextLevel) { //===----------------------------------------------------------------------===// // LSValue //===----------------------------------------------------------------------===// -void -LSValue::expand(SILValue Base, SILModule *M, LSValueList &Vals, - TypeExpansionAnalysis *TE) { - for (const auto &P : TE->getTypeExpansion((*Base).getType(), M)) { +void LSValue::expand(SILValue Base, SILModule *M, TypeExpansionContext context, + LSValueList &Vals, TypeExpansionAnalysis *TE) { + for (const auto &P : TE->getTypeExpansion((*Base).getType(), M, context)) { Vals.push_back(LSValue(Base, P.getValue())); } } @@ -42,18 +41,20 @@ LSValue::expand(SILValue Base, SILModule *M, LSValueList &Vals, void LSValue::reduceInner(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, SILInstruction *InsertPt) { + TypeExpansionContext context(*InsertPt->getFunction()); + // If this is a class reference type, we have reached end of the type tree. - if (Base.getType(M).getClassOrBoundGenericClass()) + if (Base.getType(M, context).getClassOrBoundGenericClass()) return; // This a don't expand node. - if (!shouldExpand(*M, Base.getType(M))) { + if (!shouldExpand(*M, Base.getType(M, context))) { return; } // This is a leaf node, we must have a value for it. LSLocationList NextLevel; - Base.getNextLevelLSLocations(NextLevel, M); + Base.getNextLevelLSLocations(NextLevel, M, context); if (NextLevel.empty()) return; @@ -117,11 +118,10 @@ LSValue::reduceInner(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, NullablePtr AI = Projection::createAggFromFirstLevelProjections( Builder, RegularLocation::getAutoGeneratedLocation(), - Base.getType(M).getObjectType(), - Vals); + Base.getType(M, context).getObjectType(), Vals); // This is the Value for the current base. - ProjectionPath P(Base.getType(M)); + ProjectionPath P(Base.getType(M, context)); Values[Base] = LSValue(SILValue(AI.get()), P); removeLSLocations(Values, NextLevel); } @@ -163,11 +163,11 @@ LSLocation::isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA) { return true; } -void -LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod) { - SILType Ty = getType(Mod); +void LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod, + TypeExpansionContext context) { + SILType Ty = getType(Mod, context); llvm::SmallVector Out; - Projection::getFirstLevelProjections(Ty, *Mod, Out); + Projection::getFirstLevelProjections(Ty, *Mod, context, Out); for (auto &X : Out) { ProjectionPath P((*Base).getType()); P.append(Path.getValue()); @@ -176,22 +176,23 @@ LSLocation::getNextLevelLSLocations(LSLocationList &Locs, SILModule *Mod) { } } -void -LSLocation::expand(LSLocation Base, SILModule *M, LSLocationList &Locs, - TypeExpansionAnalysis *TE) { +void LSLocation::expand(LSLocation Base, SILModule *M, + TypeExpansionContext context, LSLocationList &Locs, + TypeExpansionAnalysis *TE) { const ProjectionPath &BasePath = Base.getPath().getValue(); - for (const auto &P : TE->getTypeExpansion(Base.getType(M), M)) { + for (const auto &P : + TE->getTypeExpansion(Base.getType(M, context), M, context)) { Locs.push_back(LSLocation(Base.getBase(), BasePath, P.getValue())); } } /// Gets the sub-locations of \p Base in \p SubLocations. /// Returns false if this is not possible or too complex. -static bool -getSubLocations(LSLocationList &SubLocations, LSLocation Base, SILModule *M, - const LSLocationList &Locs) { +static bool getSubLocations(LSLocationList &SubLocations, LSLocation Base, + SILModule *M, TypeExpansionContext context, + const LSLocationList &Locs) { // If this is a class reference type, we have reached end of the type tree. - if (Base.getType(M).getClassOrBoundGenericClass()) + if (Base.getType(M, context).getClassOrBoundGenericClass()) return false; // Don't expand if it would be too complex. As Locs is a list (and not a set) @@ -199,27 +200,28 @@ getSubLocations(LSLocationList &SubLocations, LSLocation Base, SILModule *M, // Usually Locs is small anyway, because we limit expansion to 6 members. // But with deeply nested types we could run in a corner case where Locs is // large. - if (!shouldExpand(*M, Base.getType(M)) || Locs.size() >= 8) { + if (!shouldExpand(*M, Base.getType(M, context)) || Locs.size() >= 8) { return false; } // This is a leaf node. - Base.getNextLevelLSLocations(SubLocations, M); + Base.getNextLevelLSLocations(SubLocations, M, context); return !SubLocations.empty(); } /// Replaces \p SubLocations with \p Base in \p Locs if all sub-locations are /// alive, i.e. present in \p Locs. -static bool -replaceSubLocations(LSLocation Base, SILModule *M, LSLocationList &Locs, - const LSLocationList &SubLocations) { +static bool replaceSubLocations(LSLocation Base, SILModule *M, + TypeExpansionContext context, + LSLocationList &Locs, + const LSLocationList &SubLocations) { // Find whether all its children of Base are alive. bool Alive = true; for (auto &X : SubLocations) { // Recurse into the next level. LSLocationList NextInnerLevel; - if (getSubLocations(NextInnerLevel, X, M, Locs)) { - Alive &= replaceSubLocations(X, M, Locs, NextInnerLevel); + if (getSubLocations(NextInnerLevel, X, M, context, Locs)) { + Alive &= replaceSubLocations(X, M, context, Locs, NextInnerLevel); } else { Alive &= is_contained(Locs, X); } @@ -237,18 +239,19 @@ replaceSubLocations(LSLocation Base, SILModule *M, LSLocationList &Locs, return true; } -void LSLocation::reduce(LSLocation Base, SILModule *M, LSLocationList &Locs) { +void LSLocation::reduce(LSLocation Base, SILModule *M, + TypeExpansionContext context, LSLocationList &Locs) { LSLocationList SubLocations; - if (getSubLocations(SubLocations, Base, M, Locs)) - replaceSubLocations(Base, M, Locs, SubLocations); + if (getSubLocations(SubLocations, Base, M, context, Locs)) + replaceSubLocations(Base, M, context, Locs, SubLocations); } -void -LSLocation::enumerateLSLocation(SILModule *M, SILValue Mem, - std::vector &Locations, - LSLocationIndexMap &IndexMap, - LSLocationBaseMap &BaseMap, - TypeExpansionAnalysis *TypeCache) { +void LSLocation::enumerateLSLocation(TypeExpansionContext context, SILModule *M, + SILValue Mem, + std::vector &Locations, + LSLocationIndexMap &IndexMap, + LSLocationBaseMap &BaseMap, + TypeExpansionAnalysis *TypeCache) { // We have processed this SILValue before. if (BaseMap.find(Mem) != BaseMap.end()) return; @@ -272,7 +275,7 @@ LSLocation::enumerateLSLocation(SILModule *M, SILValue Mem, // Expand the given Mem into individual fields and add them to the // locationvault. LSLocationList Locs; - LSLocation::expand(L, M, Locs, TypeCache); + LSLocation::expand(L, M, context, Locs, TypeCache); for (auto &Loc : Locs) { if (IndexMap.find(Loc) != IndexMap.end()) continue; @@ -292,14 +295,16 @@ LSLocation::enumerateLSLocations(SILFunction &F, for (auto &B : F) { for (auto &I : B) { if (auto *LI = dyn_cast(&I)) { - enumerateLSLocation(&I.getModule(), LI->getOperand(), Locations, - IndexMap, BaseMap, TypeCache); + enumerateLSLocation(F.getTypeExpansionContext(), &I.getModule(), + LI->getOperand(), Locations, IndexMap, BaseMap, + TypeCache); ++LSCount.first; continue; } if (auto *SI = dyn_cast(&I)) { - enumerateLSLocation(&I.getModule(), SI->getDest(), Locations, - IndexMap, BaseMap, TypeCache); + enumerateLSLocation(F.getTypeExpansionContext(), &I.getModule(), + SI->getDest(), Locations, IndexMap, BaseMap, + TypeCache); ++LSCount.second; continue; } diff --git a/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp new file mode 100644 index 0000000000000..76c1ae4ef779e --- /dev/null +++ b/lib/SILOptimizer/Utils/PartialApplyCombiner.cpp @@ -0,0 +1,362 @@ +//===--- PartialApplyCombiner.cpp -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILValue.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" + +using namespace swift; + +namespace { + +// Helper class performing the apply{partial_apply(x,y)}(z) -> apply(z,x,y) +// peephole. +class PartialApplyCombiner { + // True if temporaries are not created yet. + bool isFirstTime = true; + + // partial_apply which is being processed. + PartialApplyInst *pai; + + // Temporaries created as copies of alloc_stack arguments of + // the partial_apply. + SmallVector tmpCopies; + + // Mapping from the original argument of partial_apply to + // the temporary containing its copy. + llvm::DenseMap argToTmpCopy; + + // Set of lifetime endpoints for this partial_apply. + // + // Used to find the last uses of partial_apply, which is need to insert + // releases/destroys of temporaries as early as possible. + ValueLifetimeAnalysis::Frontier partialApplyFrontier; + + SILBuilder &builder; + + InstModCallbacks &callbacks; + + bool processSingleApply(FullApplySite ai); + bool allocateTemporaries(); + void deallocateTemporaries(); + void destroyTemporaries(); + +public: + PartialApplyCombiner(PartialApplyInst *pai, SILBuilder &builder, + InstModCallbacks &callbacks) + : isFirstTime(true), pai(pai), builder(builder), callbacks(callbacks) {} + SILInstruction *combine(); +}; + +} // end anonymous namespace + +/// Returns true on success. +bool PartialApplyCombiner::allocateTemporaries() { + // A partial_apply [stack]'s argument are not owned by the partial_apply and + // therefore their lifetime must outlive any uses. + if (pai->isOnStack()) { + return true; + } + + // Copy the original arguments of the partial_apply into newly created + // temporaries and use these temporaries instead of the original arguments + // afterwards. + // + // This is done to "extend" the life-time of original partial_apply arguments, + // as they may be destroyed/deallocated before the last use by one of the + // apply instructions. + // + // TODO: Copy arguments of the partial_apply into new temporaries only if the + // lifetime of arguments ends before their uses by apply instructions. + bool needsDestroys = false; + CanSILFunctionType paiTy = + pai->getCallee()->getType().getAs(); + + // Emit a destroy value for each captured closure argument. + ArrayRef paramList = paiTy->getParameters(); + auto argList = pai->getArguments(); + paramList = paramList.drop_front(paramList.size() - argList.size()); + + llvm::SmallVector, 8> argsToHandle; + for (unsigned i : indices(argList)) { + SILValue arg = argList[i]; + SILParameterInfo param = paramList[i]; + if (param.isIndirectMutating()) + continue; + + // Create a temporary and copy the argument into it, if: + // - the argument stems from an alloc_stack + // - the argument is consumed by the callee and is indirect + // (e.g. it is an @in argument) + if (isa(arg) || + (param.isConsumed() && + pai->getSubstCalleeConv().isSILIndirect(param))) { + // If the argument has a dependent type, then we can not create a + // temporary for it at the beginning of the function, so we must bail. + // + // TODO: This is because we are inserting alloc_stack at the beginning/end + // of functions where the dependent type may not exist yet. + if (arg->getType().hasOpenedExistential()) + return false; + + // If the temporary is non-trivial, we need to destroy it later. + if (!arg->getType().isTrivial(*pai->getFunction())) + needsDestroys = true; + argsToHandle.push_back(std::make_pair(arg, i)); + } + } + + if (needsDestroys) { + // Compute the set of endpoints, which will be used to insert destroys of + // temporaries. This may fail if the frontier is located on a critical edge + // which we may not split (no CFG changes in SILCombine). + ValueLifetimeAnalysis vla(pai); + if (!vla.computeFrontier(partialApplyFrontier, + ValueLifetimeAnalysis::DontModifyCFG)) + return false; + } + + for (auto argWithIdx : argsToHandle) { + SILValue Arg = argWithIdx.first; + builder.setInsertionPoint(pai->getFunction()->begin()->begin()); + // Create a new temporary at the beginning of a function. + SILDebugVariable dbgVar(/*Constant*/ true, argWithIdx.second); + auto *tmp = builder.createAllocStack(pai->getLoc(), Arg->getType(), dbgVar); + builder.setInsertionPoint(pai); + // Copy argument into this temporary. + builder.createCopyAddr(pai->getLoc(), Arg, tmp, IsTake_t::IsNotTake, + IsInitialization_t::IsInitialization); + + tmpCopies.push_back(tmp); + argToTmpCopy.insert(std::make_pair(Arg, tmp)); + } + return true; +} + +/// Emit dealloc_stack for all temporaries. +void PartialApplyCombiner::deallocateTemporaries() { + // Insert dealloc_stack instructions at all function exit points. + for (SILBasicBlock &block : *pai->getFunction()) { + TermInst *term = block.getTerminator(); + if (!term->isFunctionExiting()) + continue; + + for (auto copy : tmpCopies) { + builder.setInsertionPoint(term); + builder.createDeallocStack(pai->getLoc(), copy); + } + } +} + +/// Emit code to release/destroy temporaries. +void PartialApplyCombiner::destroyTemporaries() { + // Insert releases and destroy_addrs as early as possible, + // because we don't want to keep objects alive longer than + // its really needed. + for (auto op : tmpCopies) { + auto tmpType = op->getType().getObjectType(); + if (tmpType.isTrivial(*pai->getFunction())) + continue; + for (auto *endPoint : partialApplyFrontier) { + builder.setInsertionPoint(endPoint); + if (!tmpType.isAddressOnly(*pai->getFunction())) { + SILValue load = builder.emitLoadValueOperation( + pai->getLoc(), op, LoadOwnershipQualifier::Take); + builder.emitDestroyValueOperation(pai->getLoc(), load); + } else { + builder.createDestroyAddr(pai->getLoc(), op); + } + } + } +} + +/// Process an apply instruction which uses a partial_apply +/// as its callee. +/// Returns true on success. +bool PartialApplyCombiner::processSingleApply(FullApplySite paiAI) { + builder.setInsertionPoint(paiAI.getInstruction()); + builder.setCurrentDebugScope(paiAI.getDebugScope()); + + // Prepare the args. + SmallVector argList; + // First the ApplyInst args. + for (auto Op : paiAI.getArguments()) + argList.push_back(Op); + + SILInstruction *insertPoint = &*builder.getInsertionPoint(); + // Next, the partial apply args. + + // Pre-process partial_apply arguments only once, lazily. + if (isFirstTime) { + isFirstTime = false; + if (!allocateTemporaries()) + return false; + } + + // Now, copy over the partial apply args. + for (auto arg : pai->getArguments()) { + // If there is new temporary for this argument, use it instead. + if (argToTmpCopy.count(arg)) { + arg = argToTmpCopy.lookup(arg); + } + argList.push_back(arg); + } + + builder.setInsertionPoint(insertPoint); + builder.setCurrentDebugScope(paiAI.getDebugScope()); + + // The thunk that implements the partial apply calls the closure function + // that expects all arguments to be consumed by the function. However, the + // captured arguments are not arguments of *this* apply, so they are not + // pre-incremented. When we combine the partial_apply and this apply into + // a new apply we need to retain all of the closure non-address type + // arguments. + auto paramInfo = pai->getSubstCalleeType()->getParameters(); + auto partialApplyArgs = pai->getArguments(); + // Set of arguments that need to be destroyed after each invocation. + SmallVector toBeDestroyedArgs; + for (unsigned i : indices(partialApplyArgs)) { + auto arg = partialApplyArgs[i]; + + if (!arg->getType().isAddress()) { + // Copy the argument as the callee may consume it. + arg = builder.emitCopyValueOperation(pai->getLoc(), arg); + // For non consumed parameters (e.g. guaranteed), we also need to + // insert destroys after each apply instruction that we create. + if (!paramInfo[paramInfo.size() - partialApplyArgs.size() + i] + .isConsumed()) + toBeDestroyedArgs.push_back(arg); + } + } + + auto callee = pai->getCallee(); + SubstitutionMap subs = pai->getSubstitutionMap(); + + // The partial_apply might be substituting in an open existential type. + builder.addOpenedArchetypeOperands(pai); + + FullApplySite nai; + if (auto *tai = dyn_cast(paiAI)) + nai = builder.createTryApply(paiAI.getLoc(), callee, subs, argList, + tai->getNormalBB(), tai->getErrorBB()); + else + nai = builder.createApply(paiAI.getLoc(), callee, subs, argList, + cast(paiAI)->isNonThrowing()); + + // We also need to destroy the partial_apply instruction itself because it is + // consumed by the apply_instruction. + auto loc = RegularLocation::getAutoGeneratedLocation(); + paiAI.insertAfterFullEvaluation([&](SILBasicBlock::iterator insertPt) { + SILBuilderWithScope builder(insertPt); + for (auto arg : toBeDestroyedArgs) { + builder.emitDestroyValueOperation(loc, arg); + } + if (!pai->hasCalleeGuaranteedContext()) { + builder.emitDestroyValueOperation(loc, pai); + } + }); + + if (auto *apply = dyn_cast(paiAI)) { + callbacks.replaceValueUsesWith(SILValue(apply), + cast(nai.getInstruction())); + } + callbacks.deleteInst(paiAI.getInstruction()); + return true; +} + +/// Perform the apply{partial_apply(x,y)}(z) -> apply(z,x,y) peephole +/// by iterating over all uses of the partial_apply and searching +/// for the pattern to transform. +SILInstruction *PartialApplyCombiner::combine() { + // We need to model @unowned_inner_pointer better before we can do the + // peephole here. + if (llvm::any_of(pai->getSubstCalleeType()->getResults(), + [](SILResultInfo resultInfo) { + return resultInfo.getConvention() == + ResultConvention::UnownedInnerPointer; + })) { + return nullptr; + } + + // Iterate over all uses of the partial_apply + // and look for applies that use it as a callee. + + // Worklist of operands. + SmallVector worklist(pai->getUses()); + + while (!worklist.empty()) { + auto *use = worklist.pop_back_val(); + auto *user = use->getUser(); + + // Recurse through conversions. + if (auto *cfi = dyn_cast(user)) { + // TODO: Handle argument conversion. All the code in this file needs to be + // cleaned up and generalized. The argument conversion handling in + // optimizeApplyOfConvertFunctionInst should apply to any combine + // involving an apply, not just a specific pattern. + // + // For now, just handle conversion to @noescape, which is irrelevant for + // direct application of the closure. + auto convertCalleeTy = cfi->getType().castTo(); + auto escapingCalleeTy = convertCalleeTy->getWithExtInfo( + convertCalleeTy->getExtInfo().withNoEscape(false)); + assert(use->get()->getType().castTo() == + escapingCalleeTy); + (void)escapingCalleeTy; + llvm::copy(cfi->getUses(), std::back_inserter(worklist)); + continue; + } + + // Look through mark_dependence users of partial_apply [stack]. + if (auto *mdi = dyn_cast(user)) { + if (mdi->getValue() == use->get() && + mdi->getValue()->getType().is() && + mdi->getValue()->getType().castTo()->isNoEscape()) { + llvm::copy(mdi->getUses(), std::back_inserter(worklist)); + } + continue; + } + // If this use of a partial_apply is not + // an apply which uses it as a callee, bail. + auto ai = FullApplySite::isa(user); + if (!ai) + continue; + + if (ai.getCallee() != use->get()) + continue; + + // We cannot handle generic apply yet. Bail. + if (ai.hasSubstitutions()) + continue; + + if (!processSingleApply(ai)) + return nullptr; + } + + // release/destroy and deallocate introduced temporaries. + if (!tmpCopies.empty()) { + destroyTemporaries(); + deallocateTemporaries(); + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Top Level Entrypoint +//===----------------------------------------------------------------------===// + +SILInstruction *swift::tryOptimizeApplyOfPartialApply( + PartialApplyInst *pai, SILBuilder &builder, InstModCallbacks callbacks) { + PartialApplyCombiner combiner(pai, builder, callbacks); + return combiner.combine(); +} diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index 0f0f78d9f7e0e..9b0d806461b80 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -303,10 +303,10 @@ SILBasicBlock *ConstantTracker::getTakenBlock(TermInst *term) { if (SILInstruction *def = getDefInCaller(CCB->getOperand())) { if (auto *UCI = dyn_cast(def)) { SILType castType = UCI->getOperand()->getType(); - if (CCB->getCastType().isExactSuperclassOf(castType)) { + if (CCB->getTargetLoweredType().isExactSuperclassOf(castType)) { return CCB->getSuccessBB(); } - if (!castType.isBindableToSuperclassOf(CCB->getCastType())) { + if (!castType.isBindableToSuperclassOf(CCB->getTargetLoweredType())) { return CCB->getFailureBB(); } } @@ -633,7 +633,8 @@ static bool isCallerAndCalleeLayoutConstraintsCompatible(FullApplySite AI) { SILFunction *Callee = AI.getReferencedFunctionOrNull(); assert(Callee && "Trying to optimize a dynamic function!?"); - auto CalleeSig = Callee->getLoweredFunctionType()->getGenericSignature(); + auto CalleeSig = Callee->getLoweredFunctionType() + ->getInvocationGenericSignature(); auto AISubs = AI.getSubstitutionMap(); SmallVector SubstParams; @@ -734,9 +735,9 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, // Check if passed Self is the same as the Self of the caller. // In this case, it is safe to inline because both functions // use the same Self. - if (AI.hasSelfArgument() && Caller->hasSelfParam()) { + if (AI.hasSelfArgument() && Caller->hasSelfMetadataParam()) { auto CalleeSelf = stripCasts(AI.getSelfArgument()); - auto CallerSelf = Caller->getSelfArgument(); + auto CallerSelf = Caller->getSelfMetadataArgument(); if (CalleeSelf != SILValue(CallerSelf)) return nullptr; } else diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index 8954dcb4f82d0..5483e099d4deb 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -72,7 +72,8 @@ bool SILInliner::canInlineApplySite(FullApplySite apply) { return true; } -namespace swift { +namespace { + /// Utility class for rewiring control-flow of inlined begin_apply functions. class BeginApplySite { SILLocation Loc; @@ -101,15 +102,20 @@ class BeginApplySite { return BeginApplySite(BeginApply, Loc, Builder); } - void preprocess(SILBasicBlock *returnToBB) { - // Get the end_apply, abort_apply instructions. - auto Token = BeginApply->getTokenResult(); - for (auto *TokenUse : Token->getUses()) { - if (auto End = dyn_cast(TokenUse->getUser())) { - collectEndApply(End); - } else { - collectAbortApply(cast(TokenUse->getUser())); - } + void preprocess(SILBasicBlock *returnToBB, + SmallVectorImpl &endBorrowInsertPts) { + SmallVector endApplyInsts; + SmallVector abortApplyInsts; + BeginApply->getCoroutineEndPoints(endApplyInsts, abortApplyInsts); + while (!endApplyInsts.empty()) { + auto *endApply = endApplyInsts.pop_back_val(); + collectEndApply(endApply); + endBorrowInsertPts.push_back(&*std::next(endApply->getIterator())); + } + while (!abortApplyInsts.empty()) { + auto *abortApply = abortApplyInsts.pop_back_val(); + collectAbortApply(abortApply); + endBorrowInsertPts.push_back(&*std::next(abortApply->getIterator())); } } @@ -228,7 +234,8 @@ class BeginApplySite { assert(!BeginApply->hasUsesOfAnyResult()); } }; -} // namespace swift + +} // end anonymous namespace namespace swift { class SILInlineCloner @@ -343,7 +350,7 @@ SILInliner::inlineFullApply(FullApplySite apply, SILOptFunctionBuilder &funcBuilder) { assert(apply.canOptimize()); SmallVector appliedArgs; - for (const auto &arg : apply.getArguments()) + for (const auto arg : apply.getArguments()) appliedArgs.push_back(arg); SILFunction *caller = apply.getFunction(); @@ -425,21 +432,29 @@ SILInlineCloner::cloneInline(ArrayRef AppliedArgs) { SmallVector entryArgs; entryArgs.reserve(AppliedArgs.size()); + SmallBitVector borrowedArgs(AppliedArgs.size()); + auto calleeConv = getCalleeFunction()->getConventions(); - for (unsigned argIdx = 0, endIdx = AppliedArgs.size(); argIdx < endIdx; - ++argIdx) { - SILValue callArg = AppliedArgs[argIdx]; + for (auto p : llvm::enumerate(AppliedArgs)) { + SILValue callArg = p.value(); + unsigned idx = p.index(); // Insert begin/end borrow for guaranteed arguments. - if (argIdx >= calleeConv.getSILArgIndexOfFirstParam() - && calleeConv.getParamInfoForSILArg(argIdx).isGuaranteed()) { - callArg = borrowFunctionArgument(callArg, Apply); + if (idx >= calleeConv.getSILArgIndexOfFirstParam() && + calleeConv.getParamInfoForSILArg(idx).isGuaranteed()) { + if (SILValue newValue = borrowFunctionArgument(callArg, Apply)) { + callArg = newValue; + borrowedArgs[idx] = true; + } } entryArgs.push_back(callArg); } // Create the return block and set ReturnToBB for use in visitTerminator // callbacks. - SILBasicBlock *callerBB = Apply.getParent(); + SILBasicBlock *callerBlock = Apply.getParent(); + SILBasicBlock *throwBlock = nullptr; + SmallVector endBorrowInsertPts; + switch (Apply.getKind()) { case FullApplySiteKind::ApplyInst: { auto *AI = dyn_cast(Apply); @@ -447,7 +462,8 @@ SILInlineCloner::cloneInline(ArrayRef AppliedArgs) { // Split the BB and do NOT create a branch between the old and new // BBs; we will create the appropriate terminator manually later. ReturnToBB = - callerBB->split(std::next(Apply.getInstruction()->getIterator())); + callerBlock->split(std::next(Apply.getInstruction()->getIterator())); + endBorrowInsertPts.push_back(&*ReturnToBB->begin()); // Create an argument on the return-to BB representing the returned value. auto *retArg = @@ -456,22 +472,49 @@ SILInlineCloner::cloneInline(ArrayRef AppliedArgs) { AI->replaceAllUsesWith(retArg); break; } - case FullApplySiteKind::BeginApplyInst: + case FullApplySiteKind::BeginApplyInst: { ReturnToBB = - callerBB->split(std::next(Apply.getInstruction()->getIterator())); - BeginApply->preprocess(ReturnToBB); + callerBlock->split(std::next(Apply.getInstruction()->getIterator())); + // For begin_apply, we insert the end_borrow in the end_apply, abort_apply + // blocks to ensure that our borrowed values live over both the body and + // resume block of our coroutine. + BeginApply->preprocess(ReturnToBB, endBorrowInsertPts); break; - - case FullApplySiteKind::TryApplyInst: - ReturnToBB = cast(Apply)->getNormalBB(); + } + case FullApplySiteKind::TryApplyInst: { + auto *tai = cast(Apply); + ReturnToBB = tai->getNormalBB(); + endBorrowInsertPts.push_back(&*ReturnToBB->begin()); + throwBlock = tai->getErrorBB(); break; } + } + + // Then insert end_borrow in our end borrow block and in the throw + // block if we have one. + if (borrowedArgs.any()) { + for (unsigned i : indices(AppliedArgs)) { + if (!borrowedArgs.test(i)) { + continue; + } + + for (auto *insertPt : endBorrowInsertPts) { + SILBuilderWithScope returnBuilder(insertPt, getBuilder()); + returnBuilder.createEndBorrow(Apply.getLoc(), entryArgs[i]); + } + + if (throwBlock) { + SILBuilderWithScope throwBuilder(throwBlock->begin(), getBuilder()); + throwBuilder.createEndBorrow(Apply.getLoc(), entryArgs[i]); + } + } + } // Visit original BBs in depth-first preorder, starting with the // entry block, cloning all instructions and terminators. // // NextIter is initialized during `fixUp`. - cloneFunctionBody(getCalleeFunction(), callerBB, entryArgs); + cloneFunctionBody(getCalleeFunction(), callerBlock, entryArgs); // For non-throwing applies, the inlined body now unconditionally branches to // the returned-to-code, which was previously part of the call site's basic @@ -557,25 +600,11 @@ SILValue SILInlineCloner::borrowFunctionArgument(SILValue callArg, FullApplySite AI) { if (!AI.getFunction()->hasOwnership() || callArg.getOwnershipKind() != ValueOwnershipKind::Owned) { - return callArg; + return SILValue(); } SILBuilderWithScope beginBuilder(AI.getInstruction(), getBuilder()); - auto *borrow = beginBuilder.createBeginBorrow(AI.getLoc(), callArg); - if (auto *tryAI = dyn_cast(AI)) { - SILBuilderWithScope returnBuilder(tryAI->getNormalBB()->begin(), - getBuilder()); - returnBuilder.createEndBorrow(AI.getLoc(), borrow, callArg); - - SILBuilderWithScope throwBuilder(tryAI->getErrorBB()->begin(), - getBuilder()); - throwBuilder.createEndBorrow(AI.getLoc(), borrow, callArg); - } else { - SILBuilderWithScope returnBuilder( - std::next(AI.getInstruction()->getIterator()), getBuilder()); - returnBuilder.createEndBorrow(AI.getLoc(), borrow, callArg); - } - return borrow; + return beginBuilder.createBeginBorrow(AI.getLoc(), callArg); } void SILInlineCloner::visitDebugValueInst(DebugValueInst *Inst) { @@ -609,7 +638,8 @@ SILInlineCloner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) { if (ParentFunction) ParentFunction = remapParentFunction( FuncBuilder, M, ParentFunction, SubsMap, - getCalleeFunction()->getLoweredFunctionType()->getGenericSignature(), + getCalleeFunction()->getLoweredFunctionType() + ->getInvocationGenericSignature(), ForInlining); auto *ParentScope = CalleeScope->Parent.dyn_cast(); @@ -848,7 +878,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { #define COMMON_ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name) \ case SILInstructionKind::Name##ToRefInst: \ case SILInstructionKind::RefTo##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: \ case SILInstructionKind::Store##Name##Inst: diff --git a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp index 9625114d8fa24..ba37d210559d3 100644 --- a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp +++ b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp @@ -221,7 +221,7 @@ SILValue SILSSAUpdater::GetValueInMiddleOfBlock(SILBasicBlock *BB) { if (!BB->getArguments().empty()) { llvm::SmallDenseMap ValueMap(PredVals.begin(), PredVals.end()); - for (auto *Arg : BB->getPhiArguments()) + for (auto *Arg : BB->getSILPhiArguments()) if (isEquivalentPHI(Arg, ValueMap)) return Arg; @@ -438,7 +438,7 @@ UseWrapper::UseWrapper(Operand *Use) { } /// Return the operand we wrap. Reconstructing branch operands. -UseWrapper::operator Operand *() { +Operand *UseWrapper::getOperand() { switch (Type) { case kRegularUse: return U; diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index bd1f11da6136d..9807b43eb5f6d 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -79,7 +79,7 @@ std::string GenericSpecializationMangler::mangle(GenericSignature Sig) { if (!Sig) { SILFunctionType *FTy = Function->getLoweredFunctionType(); - Sig = FTy->getGenericSignature(); + Sig = FTy->getInvocationGenericSignature(); } bool First = true; diff --git a/lib/SILOptimizer/Utils/ValueLifetime.cpp b/lib/SILOptimizer/Utils/ValueLifetime.cpp index 8e18a9da9ad66..eb8ae2feacf29 100644 --- a/lib/SILOptimizer/Utils/ValueLifetime.cpp +++ b/lib/SILOptimizer/Utils/ValueLifetime.cpp @@ -18,6 +18,7 @@ using namespace swift; void ValueLifetimeAnalysis::propagateLiveness() { assert(liveBlocks.empty() && "frontier computed twice"); + assert(!userSet.count(defValue) && "definition cannot be its own use"); auto defBB = defValue->getParentBlock(); llvm::SmallVector worklist; @@ -42,13 +43,16 @@ void ValueLifetimeAnalysis::propagateLiveness() { numUsersBeforeDef--; } + // Initialize the hasUsersBeforeDef field. + hasUsersBeforeDef = numUsersBeforeDef > 0; + // Now propagate liveness backwards until we hit the block that defines the // value. while (!worklist.empty()) { auto *bb = worklist.pop_back_val(); // Don't go beyond the definition. - if (bb == defBB && numUsersBeforeDef == 0) + if (bb == defBB && !hasUsersBeforeDef) continue; for (SILBasicBlock *Pred : bb->getPredecessorBlocks()) { @@ -93,13 +97,34 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode, bool liveInSucc = false; bool deadInSucc = false; + bool usedAndRedefinedInSucc = false; for (const SILSuccessor &succ : bb->getSuccessors()) { if (isAliveAtBeginOfBlock(succ)) { liveInSucc = true; + if (succ == defValue->getParent()) { + // Here, the basic block bb uses the value but also redefines the + // value inside bb. The new value could be used by the successors + // of succ and therefore could be live at the end of succ as well. + usedAndRedefinedInSucc = true; + } } else if (!deBlocks || !deBlocks->isDeadEnd(succ)) { deadInSucc = true; } } + if (usedAndRedefinedInSucc) { + // Here, the basic block bb uses the value and later redefines the value. + // Therefore, this value's lifetime ends after its last use preceding the + // re-definition of the value. + auto ii = defValue->getReverseIterator(); + for (; ii != bb->rend(); ++ii) { + if (userSet.count(&*ii)) { + frontier.push_back(&*std::next(ii)); + break; + } + } + assert(ii != bb->rend() && + "There must be a user in bb before definition"); + } if (!liveInSucc) { // The value is not live in any of the successor blocks. This means the // block contains a last use of the value. The next instruction after @@ -141,8 +166,6 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode, } } if (needSplit) { - if (mode == DontModifyCFG) - return false; // We need to split the critical edge to create a frontier instruction. unhandledFrontierBlocks.insert(frontierBB); } else { @@ -150,6 +173,10 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode, frontier.push_back(&*frontierBB->begin()); } } + if (unhandledFrontierBlocks.size() == 0) { + return true; + } + // Split critical edges from the lifetime region to not yet handled frontier // blocks. for (SILBasicBlock *frontierPred : liveOutBlocks) { @@ -163,12 +190,17 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode, for (unsigned i = 0, e = succBlocks.size(); i != e; ++i) { if (unhandledFrontierBlocks.count(succBlocks[i])) { - assert(mode == AllowToModifyCFG); assert(isCriticalEdge(term, i) && "actually not a critical edge?"); + noCriticalEdges = false; + if (mode != AllowToModifyCFG) { + // If the CFG need not be modified, just record the critical edge and + // continue. + this->criticalEdges.push_back({term, i}); + continue; + } SILBasicBlock *newBlock = splitEdge(term, i); // The single terminator instruction is part of the frontier. frontier.push_back(&*newBlock->begin()); - noCriticalEdges = false; } } } diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 2d4e03e1e271a..0dff01b97cc29 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -16,12 +16,15 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" +#include "MiscDiagnostics.h" +#include "SolutionResult.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include @@ -37,24 +40,36 @@ namespace { /// Visitor to classify the contents of the given closure. class BuilderClosureVisitor - : public StmtVisitor { + : private StmtVisitor { + + friend StmtVisitor; + ConstraintSystem *cs; + DeclContext *dc; ASTContext &ctx; - bool wantExpr; Type builderType; NominalTypeDecl *builder = nullptr; llvm::SmallDenseMap supportedOps; -public: SkipUnhandledConstructInFunctionBuilder::UnhandledNode unhandledNode; -private: - /// Produce a builder call to the given named function with the given arguments. + /// Whether an error occurred during application of the builder closure, + /// e.g., during constraint generation. + bool hadError = false; + + /// Counter used to give unique names to the variables that are + /// created implicitly. + unsigned varCounter = 0; + + /// The record of what happened when we applied the builder transform. + AppliedBuilderTransform applied; + + /// Produce a builder call to the given named function with the given + /// arguments. Expr *buildCallIfWanted(SourceLoc loc, Identifier fnName, ArrayRef args, - ArrayRef argLabels, - bool allowOneWay) { - if (!wantExpr) + ArrayRef argLabels) { + if (!cs) return nullptr; // FIXME: Setting a TypeLoc on this expression is necessary in order @@ -67,10 +82,8 @@ class BuilderClosureVisitor } auto typeExpr = new (ctx) TypeExpr(typeLoc); - if (cs) { - cs->setType(typeExpr, MetatypeType::get(builderType)); - cs->setType(&typeExpr->getTypeLoc(), builderType); - } + cs->setType(typeExpr, MetatypeType::get(builderType)); + cs->setType(&typeExpr->getTypeLoc(), builderType); SmallVector argLabelLocs; for (auto i : indices(argLabels)) { @@ -79,7 +92,9 @@ class BuilderClosureVisitor typeExpr->setImplicit(); auto memberRef = new (ctx) UnresolvedDotExpr( - typeExpr, loc, fnName, DeclNameLoc(loc), /*implicit=*/true); + typeExpr, loc, DeclNameRef(fnName), DeclNameLoc(loc), + /*implicit=*/true); + memberRef->setFunctionRefKind(FunctionRefKind::SingleApply); SourceLoc openLoc = args.empty() ? loc : args.front()->getStartLoc(); SourceLoc closeLoc = args.empty() ? loc : args.back()->getEndLoc(); Expr *result = CallExpr::create(ctx, memberRef, openLoc, args, @@ -87,11 +102,6 @@ class BuilderClosureVisitor /*trailing closure*/ nullptr, /*implicit*/true); - if (ctx.LangOpts.FunctionBuilderOneWayConstraints && allowOneWay) { - // Form a one-way constraint to prevent backward propagation. - result = new (ctx) OneWayExpr(result); - } - return result; } @@ -127,31 +137,116 @@ class BuilderClosureVisitor return supportedOps[fnName] = found; } + /// Build an implicit variable in this context. + VarDecl *buildVar(SourceLoc loc) { + // Create the implicit variable. + Identifier name = ctx.getIdentifier( + ("$__builder" + Twine(varCounter++)).str()); + auto var = new (ctx) VarDecl(/*isStatic=*/false, VarDecl::Introducer::Var, + /*isCaptureList=*/false, loc, name, dc); + var->setImplicit(); + return var; + } + + /// Capture the given expression into an implicitly-generated variable. + VarDecl *captureExpr(Expr *expr, bool oneWay, + llvm::PointerUnion forEntity = nullptr) { + if (!cs) + return nullptr; + + Expr *origExpr = expr; + + if (oneWay) { + // Form a one-way constraint to prevent backward propagation. + expr = new (ctx) OneWayExpr(expr); + } + + // Generate constraints for this expression. + expr = cs->generateConstraints(expr, dc); + if (!expr) { + hadError = true; + return nullptr; + } + + // Create the implicit variable. + auto var = buildVar(expr->getStartLoc()); + + // Record the new variable and its corresponding expression & statement. + if (auto forStmt = forEntity.dyn_cast()) { + applied.capturedStmts.insert({forStmt, { var, { expr } }}); + } else { + if (auto forExpr = forEntity.dyn_cast()) + origExpr = forExpr; + + applied.capturedExprs.insert({origExpr, {var, expr}}); + } + + cs->setType(var, cs->getType(expr)); + return var; + } + + /// Build an implicit reference to the given variable. + DeclRefExpr *buildVarRef(VarDecl *var, SourceLoc loc) { + return new (ctx) DeclRefExpr(var, DeclNameLoc(loc), /*Implicit=*/true); + } + public: BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs, - bool wantExpr, Type builderType) - : cs(cs), ctx(ctx), wantExpr(wantExpr), builderType(builderType) { + DeclContext *dc, Type builderType, + Type bodyResultType) + : cs(cs), dc(dc), ctx(ctx), builderType(builderType) { assert((cs || !builderType->hasTypeVariable()) && "cannot handle builder type with type variables without " "constraint system"); builder = builderType->getAnyNominal(); + applied.builderType = builderType; + applied.bodyResultType = bodyResultType; + } + + /// Apply the builder transform to the given statement. + Optional apply(Stmt *stmt) { + VarDecl *bodyVar = visit(stmt); + if (!bodyVar) + return None; + + applied.returnExpr = buildVarRef(bodyVar, stmt->getEndLoc()); + applied.returnExpr = cs->generateConstraints(applied.returnExpr, dc); + if (!applied.returnExpr) { + hadError = true; + return None; + } + + return std::move(applied); + } + + /// Check whether the function builder can be applied to this statement. + /// \returns the node that cannot be handled by this builder on failure. + SkipUnhandledConstructInFunctionBuilder::UnhandledNode check(Stmt *stmt) { + (void)visit(stmt); + return unhandledNode; } -#define CONTROL_FLOW_STMT(StmtClass) \ - Expr *visit##StmtClass##Stmt(StmtClass##Stmt *stmt) { \ - if (!unhandledNode) \ - unhandledNode = stmt; \ - \ - return nullptr; \ +protected: +#define CONTROL_FLOW_STMT(StmtClass) \ + VarDecl *visit##StmtClass##Stmt(StmtClass##Stmt *stmt) { \ + if (!unhandledNode) \ + unhandledNode = stmt; \ + \ + return nullptr; \ } - Expr *visitBraceStmt(BraceStmt *braceStmt) { + VarDecl *visitBraceStmt(BraceStmt *braceStmt) { SmallVector expressions; + auto addChild = [&](VarDecl *childVar) { + if (!childVar) + return; + + expressions.push_back(buildVarRef(childVar, childVar->getLoc())); + }; + for (const auto &node : braceStmt->getElements()) { if (auto stmt = node.dyn_cast()) { - auto expr = visit(stmt); - if (expr) - expressions.push_back(expr); + addChild(visit(stmt)); continue; } @@ -162,6 +257,11 @@ class BuilderClosureVisitor if (isa(decl)) continue; + // Skip #warning/#error; we'll handle them when applying the builder. + if (auto poundDiag = dyn_cast(decl)) { + continue; + } + if (!unhandledNode) unhandledNode = decl; @@ -169,20 +269,28 @@ class BuilderClosureVisitor } auto expr = node.get(); - if (wantExpr && ctx.LangOpts.FunctionBuilderOneWayConstraints) - expr = new (ctx) OneWayExpr(expr); + if (cs && builderSupports(ctx.Id_buildExpression)) { + expr = buildCallIfWanted(expr->getLoc(), ctx.Id_buildExpression, + { expr }, { Identifier() }); + } - expressions.push_back(expr); + addChild(captureExpr(expr, /*oneWay=*/true, node.get())); } + if (!cs) + return nullptr; + // Call Builder.buildBlock(... args ...) - return buildCallIfWanted(braceStmt->getStartLoc(), - ctx.Id_buildBlock, expressions, - /*argLabels=*/{ }, - /*allowOneWay=*/true); + auto call = buildCallIfWanted(braceStmt->getStartLoc(), + ctx.Id_buildBlock, expressions, + /*argLabels=*/{ }); + if (!call) + return nullptr; + + return captureExpr(call, /*oneWay=*/true, braceStmt); } - Expr *visitReturnStmt(ReturnStmt *stmt) { + VarDecl *visitReturnStmt(ReturnStmt *stmt) { // Allow implicit returns due to 'return' elision. if (!stmt->isImplicit() || !stmt->hasResult()) { if (!unhandledNode) @@ -190,39 +298,37 @@ class BuilderClosureVisitor return nullptr; } - return stmt->getResult(); + return captureExpr(stmt->getResult(), /*oneWay=*/true); } - Expr *visitDoStmt(DoStmt *doStmt) { + VarDecl *visitDoStmt(DoStmt *doStmt) { if (!builderSupports(ctx.Id_buildDo)) { if (!unhandledNode) unhandledNode = doStmt; return nullptr; } - auto arg = visit(doStmt->getBody()); - if (!arg) + auto childVar = visit(doStmt->getBody()); + if (!childVar) return nullptr; - return buildCallIfWanted(doStmt->getStartLoc(), ctx.Id_buildDo, arg, - /*argLabels=*/{ }, /*allowOneWay=*/true); + auto childRef = buildVarRef(childVar, doStmt->getEndLoc()); + auto call = buildCallIfWanted(doStmt->getStartLoc(), ctx.Id_buildDo, + childRef, /*argLabels=*/{ }); + if (!call) + return nullptr; + + return captureExpr(call, /*oneWay=*/true, doStmt); } CONTROL_FLOW_STMT(Yield) CONTROL_FLOW_STMT(Defer) - static Expr *getTrivialBooleanCondition(StmtCondition condition) { - if (condition.size() != 1) - return nullptr; - - return condition.front().getBooleanOrNull(); - } - static bool isBuildableIfChainRecursive(IfStmt *ifStmt, unsigned &numPayloads, bool &isOptional) { - // The conditional must be trivial. - if (!getTrivialBooleanCondition(ifStmt->getCond())) + // Check whether we can handle the conditional. + if (!ConstraintSystem::canGenerateConstraints(ifStmt->getCond())) return false; // The 'then' clause contributes a payload. @@ -267,7 +373,7 @@ class BuilderClosureVisitor return true; } - Expr *visitIfStmt(IfStmt *ifStmt) { + VarDecl *visitIfStmt(IfStmt *ifStmt) { // Check whether the chain is buildable and whether it terminates // without an `else`. bool isOptional = false; @@ -280,74 +386,56 @@ class BuilderClosureVisitor // Attempt to build the chain, propagating short-circuits, which // might arise either do to error or not wanting an expression. - auto chainExpr = - buildIfChainRecursive(ifStmt, 0, numPayloads, isOptional); - if (!chainExpr) - return nullptr; - assert(wantExpr); - - // The operand should have optional type if we had optional results, - // so we just need to call `buildIf` now, since we're at the top level. - if (isOptional) { - chainExpr = buildCallIfWanted(ifStmt->getStartLoc(), - ctx.Id_buildIf, chainExpr, - /*argLabels=*/{ }, - /*allowOneWay=*/true); - } else if (ctx.LangOpts.FunctionBuilderOneWayConstraints) { - // Form a one-way constraint to prevent backward propagation. - chainExpr = new (ctx) OneWayExpr(chainExpr); - } - - return chainExpr; + return buildIfChainRecursive(ifStmt, 0, numPayloads, isOptional, + /*isTopLevel=*/true); } /// Recursively build an if-chain: build an expression which will have /// a value of the chain result type before any call to `buildIf`. /// The expression will perform any necessary calls to `buildEither`, /// and the result will have optional type if `isOptional` is true. - Expr *buildIfChainRecursive(IfStmt *ifStmt, unsigned payloadIndex, - unsigned numPayloads, bool isOptional) { + VarDecl *buildIfChainRecursive(IfStmt *ifStmt, unsigned payloadIndex, + unsigned numPayloads, bool isOptional, + bool isTopLevel = false) { assert(payloadIndex < numPayloads); // Make sure we recursively visit both sides even if we're not // building expressions. // Build the then clause. This will have the corresponding payload // type (i.e. not wrapped in any way). - Expr *thenArg = visit(ifStmt->getThenStmt()); + VarDecl *thenVar = visit(ifStmt->getThenStmt()); // Build the else clause, if present. If this is from an else-if, // this will be fully wrapped; otherwise it will have the corresponding // payload type (at index `payloadIndex + 1`). assert(ifStmt->getElseStmt() || isOptional); bool isElseIf = false; - Optional elseChain; + Optional elseChainVar; if (auto elseStmt = ifStmt->getElseStmt()) { if (auto elseIfStmt = dyn_cast(elseStmt)) { isElseIf = true; - elseChain = buildIfChainRecursive(elseIfStmt, payloadIndex + 1, - numPayloads, isOptional); + elseChainVar = buildIfChainRecursive(elseIfStmt, payloadIndex + 1, + numPayloads, isOptional); } else { - elseChain = visit(elseStmt); + elseChainVar = visit(elseStmt); } } // Short-circuit if appropriate. - if (!wantExpr || !thenArg || (elseChain && !*elseChain)) + if (!cs || !thenVar || (elseChainVar && !*elseChainVar)) return nullptr; - // Okay, build the conditional expression. - // Prepare the `then` operand by wrapping it to produce a chain result. - SourceLoc thenLoc = ifStmt->getThenStmt()->getStartLoc(); - Expr *thenExpr = buildWrappedChainPayload(thenArg, payloadIndex, - numPayloads, isOptional); + Expr *thenExpr = buildWrappedChainPayload( + buildVarRef(thenVar, ifStmt->getThenStmt()->getEndLoc()), + payloadIndex, numPayloads, isOptional); // Prepare the `else operand: Expr *elseExpr; SourceLoc elseLoc; // - If there's no `else` clause, use `Optional.none`. - if (!elseChain) { + if (!elseChainVar) { assert(isOptional); elseLoc = ifStmt->getEndLoc(); elseExpr = buildNoneExpr(elseLoc); @@ -355,24 +443,60 @@ class BuilderClosureVisitor // - If there's an `else if`, the chain expression from that // should already be producing a chain result. } else if (isElseIf) { - elseExpr = *elseChain; + elseExpr = buildVarRef(*elseChainVar, ifStmt->getEndLoc()); elseLoc = ifStmt->getElseLoc(); // - Otherwise, wrap it to produce a chain result. } else { elseLoc = ifStmt->getElseLoc(); - elseExpr = buildWrappedChainPayload(*elseChain, - payloadIndex + 1, numPayloads, - isOptional); + elseExpr = buildWrappedChainPayload( + buildVarRef(*elseChainVar, ifStmt->getEndLoc()), + payloadIndex + 1, numPayloads, isOptional); + } + + // Generate constraints for the conditions. + if (cs->generateConstraints(ifStmt->getCond(), dc)) { + hadError = true; + return nullptr; } - Expr *condition = getTrivialBooleanCondition(ifStmt->getCond()); - assert(condition && "checked by isBuildableIfChain"); + // The operand should have optional type if we had optional results, + // so we just need to call `buildIf` now, since we're at the top level. + if (isOptional && isTopLevel) { + thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf, + thenExpr, /*argLabels=*/{ }); + elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf, + elseExpr, /*argLabels=*/{ }); + } - auto ifExpr = new (ctx) IfExpr(condition, thenLoc, thenExpr, - elseLoc, elseExpr); - ifExpr->setImplicit(); - return ifExpr; + thenExpr = cs->generateConstraints(thenExpr, dc); + if (!thenExpr) { + hadError = true; + return nullptr; + } + + elseExpr = cs->generateConstraints(elseExpr, dc); + if (!elseExpr) { + hadError = true; + return nullptr; + } + + // FIXME: Need a locator for the "if" statement. + Type resultType = cs->addJoinConstraint(nullptr, + { + { cs->getType(thenExpr), cs->getConstraintLocator(thenExpr) }, + { cs->getType(elseExpr), cs->getConstraintLocator(elseExpr) } + }); + if (!resultType) { + hadError = true; + return nullptr; + } + + // Create a variable to capture the result of this expression. + auto ifVar = buildVar(ifStmt->getStartLoc()); + cs->setType(ifVar, resultType); + applied.capturedStmts.insert({ifStmt, { ifVar, { thenExpr, elseExpr }}}); + return ifVar; } /// Wrap a payload value in an expression which will produce a chain @@ -415,8 +539,7 @@ class BuilderClosureVisitor bool isSecond = (path & 1); operand = buildCallIfWanted(operand->getStartLoc(), ctx.Id_buildEither, operand, - {isSecond ? ctx.Id_second : ctx.Id_first}, - /*allowOneWay=*/false); + {isSecond ? ctx.Id_second : ctx.Id_first}); } // Inject into Optional if required. We'll be adding the call to @@ -436,7 +559,7 @@ class BuilderClosureVisitor auto optionalTypeExpr = TypeExpr::createImplicitHack(loc, optionalType, ctx); auto someRef = new (ctx) UnresolvedDotExpr( - optionalTypeExpr, loc, ctx.getIdentifier("some"), + optionalTypeExpr, loc, DeclNameRef(ctx.getIdentifier("some")), DeclNameLoc(loc), /*implicit=*/true); return CallExpr::createImplicit(ctx, someRef, arg, { }); } @@ -448,7 +571,7 @@ class BuilderClosureVisitor auto optionalTypeExpr = TypeExpr::createImplicitHack(endLoc, optionalType, ctx); return new (ctx) UnresolvedDotExpr( - optionalTypeExpr, endLoc, ctx.getIdentifier("none"), + optionalTypeExpr, endLoc, DeclNameRef(ctx.getIdentifier("none")), DeclNameLoc(endLoc), /*implicit=*/true); } @@ -470,71 +593,568 @@ class BuilderClosureVisitor #undef CONTROL_FLOW_STMT }; +/// Describes the target into which the result of a particular statement in +/// a closure involving a function builder should be written. +struct FunctionBuilderTarget { + enum Kind { + /// The resulting value is returned from the closure. + ReturnValue, + /// The temporary variable into which the result should be assigned. + TemporaryVar, + } kind; + + /// Captured variable information. + std::pair> captured; + + static FunctionBuilderTarget forReturn(Expr *expr) { + return FunctionBuilderTarget{ReturnValue, {nullptr, {expr}}}; + } + + static FunctionBuilderTarget forAssign(VarDecl *temporaryVar, + llvm::TinyPtrVector exprs) { + return FunctionBuilderTarget{TemporaryVar, {temporaryVar, exprs}}; + } +}; + +/// Handles the rewrite of the body of a closure to which a function builder +/// has been applied. +class BuilderClosureRewriter + : public StmtVisitor { + ASTContext &ctx; + const Solution &solution; + DeclContext *dc; + AppliedBuilderTransform builderTransform; + std::function rewriteExprFn; + std::function coerceToType; + + /// Retrieve the temporary variable that will be used to capture the + /// value of the given expression. + AppliedBuilderTransform::RecordedExpr takeCapturedExpr(Expr *expr) { + auto found = builderTransform.capturedExprs.find(expr); + assert(found != builderTransform.capturedExprs.end()); + + // Set the type of the temporary variable. + auto recorded = found->second; + if (auto temporaryVar = recorded.temporaryVar) { + Type type = solution.simplifyType(solution.getType(temporaryVar)); + temporaryVar->setInterfaceType(type->mapTypeOutOfContext()); + } + + // Erase the captured expression, so we're sure we never do this twice. + builderTransform.capturedExprs.erase(found); + return recorded; + } + +public: + /// Retrieve information about a captured statement. + std::pair> + takeCapturedStmt(Stmt *stmt) { + auto found = builderTransform.capturedStmts.find(stmt); + assert(found != builderTransform.capturedStmts.end()); + + // Set the type of the temporary variable. + auto temporaryVar = found->second.first; + Type type = solution.simplifyType(solution.getType(temporaryVar)); + temporaryVar->setInterfaceType(type->mapTypeOutOfContext()); + + // Take the expressions. + auto exprs = std::move(found->second.second); + + // Erase the statement, so we're sure we never do this twice. + builderTransform.capturedStmts.erase(found); + return std::make_pair(temporaryVar, std::move(exprs)); + } + +private: + /// Build the statement or expression to initialize the target. + ASTNode initializeTarget(FunctionBuilderTarget target) { + assert(target.captured.second.size() == 1); + auto capturedExpr = target.captured.second.front(); + auto finalCapturedExpr = rewriteExpr(capturedExpr); + SourceLoc implicitLoc = capturedExpr->getEndLoc(); + switch (target.kind) { + case FunctionBuilderTarget::ReturnValue: { + // Return the expression. + ConstraintSystem &cs = solution.getConstraintSystem(); + Type bodyResultType = + solution.simplifyType(builderTransform.bodyResultType); + finalCapturedExpr = coerceToType( + finalCapturedExpr, + bodyResultType, + cs.getConstraintLocator(capturedExpr)); + return new (ctx) ReturnStmt(implicitLoc, finalCapturedExpr); + } + + case FunctionBuilderTarget::TemporaryVar: { + // Assign the expression into a variable. + auto temporaryVar = target.captured.first; + auto declRef = new (ctx) DeclRefExpr( + temporaryVar, DeclNameLoc(implicitLoc), /*implicit=*/true); + declRef->setType(LValueType::get(temporaryVar->getType())); + + // Load the right-hand side if needed. + if (finalCapturedExpr->getType()->is()) { + auto &cs = solution.getConstraintSystem(); + finalCapturedExpr = cs.addImplicitLoadExpr(finalCapturedExpr); + } + + auto assign = new (ctx) AssignExpr( + declRef, implicitLoc, finalCapturedExpr, /*implicit=*/true); + assign->setType(TupleType::getEmpty(ctx)); + return assign; + } + } + } + + /// Declare the given temporary variable, adding the appropriate + /// entries to the elements of a brace stmt. + void declareTemporaryVariable(VarDecl *temporaryVar, + std::vector &elements, + Expr *initExpr = nullptr) { + if (!temporaryVar) + return; + + // Form a new pattern binding to bind the temporary variable to the + // transformed expression. + auto pattern = new (ctx) NamedPattern(temporaryVar,/*implicit=*/true); + pattern->setType(temporaryVar->getType()); + + auto pbd = PatternBindingDecl::create( + ctx, SourceLoc(), StaticSpellingKind::None, temporaryVar->getLoc(), + pattern, SourceLoc(), initExpr, dc); + elements.push_back(temporaryVar); + elements.push_back(pbd); + } + + Expr *rewriteExpr(Expr *expr) { + Expr *result = rewriteExprFn(expr); + if (result) + performSyntacticExprDiagnostics(expr, dc, /*isExprStmt=*/false); + return result; + } + +public: + BuilderClosureRewriter( + const Solution &solution, + DeclContext *dc, + const AppliedBuilderTransform &builderTransform, + std::function rewriteExpr, + std::function coerceToType + ) : ctx(solution.getConstraintSystem().getASTContext()), + solution(solution), dc(dc), builderTransform(builderTransform), + rewriteExprFn(rewriteExpr), + coerceToType(coerceToType){ } + + Stmt *visitBraceStmt(BraceStmt *braceStmt, FunctionBuilderTarget target, + Optional innerTarget = None) { + std::vector newElements; + + // If there is an "inner" target corresponding to this brace, declare + // it's temporary variable if needed. + if (innerTarget) { + declareTemporaryVariable(innerTarget->captured.first, newElements); + } + + for (auto node : braceStmt->getElements()) { + // Implicit returns in single-expression function bodies are treated + // as the expression. + if (auto returnStmt = + dyn_cast_or_null(node.dyn_cast())) { + assert(returnStmt->isImplicit()); + node = returnStmt->getResult(); + } + + if (auto expr = node.dyn_cast()) { + // Skip error expressions. + if (isa(expr)) + continue; + + // Each expression turns into a 'let' that captures the value of + // the expression. + auto recorded = takeCapturedExpr(expr); + + // Rewrite the expression + Expr *finalExpr = rewriteExpr(recorded.generatedExpr); + + // Form a new pattern binding to bind the temporary variable to the + // transformed expression. + declareTemporaryVariable(recorded.temporaryVar, newElements, finalExpr); + continue; + } + + if (auto stmt = node.dyn_cast()) { + // Each statement turns into a (potential) temporary variable + // binding followed by the statement itself. + auto captured = takeCapturedStmt(stmt); + + declareTemporaryVariable(captured.first, newElements); + + Stmt *finalStmt = visit( + stmt, + FunctionBuilderTarget{FunctionBuilderTarget::TemporaryVar, + std::move(captured)}); + newElements.push_back(finalStmt); + continue; + } + + auto decl = node.get(); + + // Skip #if declarations. + if (isa(decl)) + continue; + + // Diagnose #warning / #error during application. + if (auto poundDiag = dyn_cast(decl)) { + TypeChecker::typeCheckDecl(poundDiag); + continue; + } + + llvm_unreachable("Cannot yet handle declarations"); + } + + // If there is an "inner" target corresponding to this brace, initialize + // it. + if (innerTarget) { + newElements.push_back(initializeTarget(*innerTarget)); + } + + // Capture the result of the buildBlock() call in the manner requested + // by the caller. + newElements.push_back(initializeTarget(target)); + + return BraceStmt::create(ctx, braceStmt->getLBraceLoc(), newElements, + braceStmt->getRBraceLoc()); + } + + Stmt *visitIfStmt(IfStmt *ifStmt, FunctionBuilderTarget target) { + // Rewrite the condition. + auto condition = ifStmt->getCond(); + for (auto &condElement : condition) { + switch (condElement.getKind()) { + case StmtConditionElement::CK_Availability: + continue; + + case StmtConditionElement::CK_Boolean: { + auto condExpr = condElement.getBoolean(); + auto finalCondExpr = rewriteExpr(condExpr); + + // Load the condition if needed. + if (finalCondExpr->getType()->is()) { + auto &cs = solution.getConstraintSystem(); + finalCondExpr = cs.addImplicitLoadExpr(finalCondExpr); + } + + condElement.setBoolean(finalCondExpr); + continue; + } + + case StmtConditionElement::CK_PatternBinding: + llvm_unreachable("unhandled statement condition"); + } + } + ifStmt->setCond(condition); + + assert(target.kind == FunctionBuilderTarget::TemporaryVar); + auto temporaryVar = target.captured.first; + + // Translate the "then" branch. + auto capturedThen = takeCapturedStmt(ifStmt->getThenStmt()); + auto newThen = visitBraceStmt(cast(ifStmt->getThenStmt()), + FunctionBuilderTarget::forAssign( + temporaryVar, {target.captured.second[0]}), + FunctionBuilderTarget::forAssign( + capturedThen.first, {capturedThen.second.front()})); + ifStmt->setThenStmt(newThen); + + if (auto elseBraceStmt = + dyn_cast_or_null(ifStmt->getElseStmt())) { + // Translate the "else" branch when it's a stmt-brace. + auto capturedElse = takeCapturedStmt(elseBraceStmt); + Stmt *newElse = visitBraceStmt( + elseBraceStmt, + FunctionBuilderTarget::forAssign( + temporaryVar, {target.captured.second[1]}), + FunctionBuilderTarget::forAssign( + capturedElse.first, {capturedElse.second.front()})); + ifStmt->setElseStmt(newElse); + } else if (auto elseIfStmt = cast_or_null(ifStmt->getElseStmt())){ + // Translate the "else" branch when it's an else-if. + auto capturedElse = takeCapturedStmt(elseIfStmt); + std::vector newElseElements; + declareTemporaryVariable(capturedElse.first, newElseElements); + newElseElements.push_back( + visitIfStmt( + elseIfStmt, + FunctionBuilderTarget::forAssign( + capturedElse.first, capturedElse.second))); + newElseElements.push_back( + initializeTarget( + FunctionBuilderTarget::forAssign( + temporaryVar, {target.captured.second[1]}))); + + Stmt *newElse = BraceStmt::create( + ctx, elseIfStmt->getStartLoc(), newElseElements, + elseIfStmt->getEndLoc()); + ifStmt->setElseStmt(newElse); + } else { + // Form an "else" brace containing an assignment to the temporary + // variable. + auto init = initializeTarget( + FunctionBuilderTarget::forAssign( + temporaryVar, {target.captured.second[1]})); + auto newElse = BraceStmt::create( + ctx, ifStmt->getEndLoc(), { init }, ifStmt->getEndLoc()); + ifStmt->setElseStmt(newElse); + } + + return ifStmt; + } + + Stmt *visitDoStmt(DoStmt *doStmt, FunctionBuilderTarget target) { + // Each statement turns into a (potential) temporary variable + // binding followed by the statement itself. + auto body = cast(doStmt->getBody()); + auto captured = takeCapturedStmt(body); + + auto newInnerBody = cast( + visitBraceStmt( + body, + target, + FunctionBuilderTarget::forAssign( + captured.first, {captured.second.front()}))); + doStmt->setBody(newInnerBody); + return doStmt; + } + +#define UNHANDLED_FUNCTION_BUILDER_STMT(STMT) \ + Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, FunctionBuilderTarget target) { \ + llvm_unreachable("Function builders do not allow statement of kind " \ + #STMT); \ + } + + UNHANDLED_FUNCTION_BUILDER_STMT(Return) + UNHANDLED_FUNCTION_BUILDER_STMT(Yield) + UNHANDLED_FUNCTION_BUILDER_STMT(Guard) + UNHANDLED_FUNCTION_BUILDER_STMT(While) + UNHANDLED_FUNCTION_BUILDER_STMT(Defer) + UNHANDLED_FUNCTION_BUILDER_STMT(DoCatch) + UNHANDLED_FUNCTION_BUILDER_STMT(RepeatWhile) + UNHANDLED_FUNCTION_BUILDER_STMT(ForEach) + UNHANDLED_FUNCTION_BUILDER_STMT(Switch) + UNHANDLED_FUNCTION_BUILDER_STMT(Case) + UNHANDLED_FUNCTION_BUILDER_STMT(Catch) + UNHANDLED_FUNCTION_BUILDER_STMT(Break) + UNHANDLED_FUNCTION_BUILDER_STMT(Continue) + UNHANDLED_FUNCTION_BUILDER_STMT(Fallthrough) + UNHANDLED_FUNCTION_BUILDER_STMT(Fail) + UNHANDLED_FUNCTION_BUILDER_STMT(Throw) + UNHANDLED_FUNCTION_BUILDER_STMT(PoundAssert) +#undef UNHANDLED_FUNCTION_BUILDER_STMT +}; + } // end anonymous namespace -BraceStmt * -TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType) { - // Try to build a single result expression. - BuilderClosureVisitor visitor(Context, nullptr, - /*wantExpr=*/true, builderType); - Expr *returnExpr = visitor.visit(body); - if (!returnExpr) - return nullptr; +BraceStmt *swift::applyFunctionBuilderTransform( + const Solution &solution, + AppliedBuilderTransform applied, + BraceStmt *body, + DeclContext *dc, + std::function rewriteExpr, + std::function coerceToType) { + BuilderClosureRewriter rewriter(solution, dc, applied, rewriteExpr, coerceToType); + auto captured = rewriter.takeCapturedStmt(body); + return cast( + rewriter.visitBraceStmt( + body, + FunctionBuilderTarget::forReturn(applied.returnExpr), + FunctionBuilderTarget::forAssign( + captured.first, captured.second))); +} - // Make sure we have a usable result type for the body. - Type returnType = AnyFunctionRef(FD).getBodyResultType(); - if (!returnType || returnType->hasError()) +/// Find the return statements in the given body, which block the application +/// of a function builder. +static std::vector findReturnStatements(AnyFunctionRef fn); + +Optional TypeChecker::applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType) { + // Pre-check the body: pre-check any expressions in it and look + // for return statements. + // + // If we encountered an error or there was an explicit result type, + // bail out and report that to the caller. + auto &ctx = func->getASTContext(); + auto request = PreCheckFunctionBuilderRequest{func}; + switch (evaluateOrDefault( + ctx.evaluator, request, FunctionBuilderBodyPreCheck::Error)) { + case FunctionBuilderBodyPreCheck::Okay: + // If the pre-check was okay, apply the function-builder transform. + break; + + case FunctionBuilderBodyPreCheck::Error: return nullptr; - auto loc = returnExpr->getStartLoc(); - auto returnStmt = - new (Context) ReturnStmt(loc, returnExpr, /*implicit*/ true); - return BraceStmt::create(Context, body->getLBraceLoc(), { returnStmt }, - body->getRBraceLoc()); + case FunctionBuilderBodyPreCheck::HasReturnStmt: { + // One or more explicit 'return' statements were encountered, which + // disables the function builder transform. Warn when we do this. + auto returnStmts = findReturnStatements(func); + assert(!returnStmts.empty()); + + ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_disabled_by_return, builderType); + + // Note that one can remove the function builder attribute. + auto attr = func->getAttachedFunctionBuilder(); + if (!attr) { + if (auto accessor = dyn_cast(func)) { + attr = accessor->getStorage()->getAttachedFunctionBuilder(); + } + } + + if (attr) { + ctx.Diags.diagnose( + attr->getLocation(), diag::function_builder_remove_attr) + .fixItRemove(attr->getRangeWithAt()); + attr->setInvalid(); + } + + // Note that one can remove all of the return statements. + { + auto diag = ctx.Diags.diagnose( + returnStmts.front()->getReturnLoc(), + diag::function_builder_remove_returns); + for (auto returnStmt : returnStmts) { + diag.fixItRemove(returnStmt->getReturnLoc()); + } + } + + return None; + } + } + + ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes; + auto resultInterfaceTy = func->getResultInterfaceType(); + auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy); + + // Determine whether we're inferring the underlying type for the opaque + // result type of this function. + ConstraintKind resultConstraintKind = ConstraintKind::Conversion; + if (auto opaque = resultContextType->getAs()) { + if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) { + resultConstraintKind = ConstraintKind::OpaqueUnderlyingType; + } + } + + // Build a constraint system in which we can check the body of the function. + ConstraintSystem cs(func, options); + + // Find an expression... any expression... to use for a locator. + // FIXME: This is a hack because we don't have the notion of locators that + // refer to statements. + Expr *fakeAnchor = nullptr; + { + class FindExprWalker : public ASTWalker { + Expr *&fakeAnchor; + + public: + explicit FindExprWalker(Expr *&fakeAnchor) : fakeAnchor(fakeAnchor) { } + + std::pair walkToExprPre(Expr *E) { + if (!fakeAnchor) + fakeAnchor = E; + + return { false, nullptr }; + } + } walker(fakeAnchor); + + func->getBody()->walk(walker); + } + + // FIXME: check the result + cs.matchFunctionBuilder(func, builderType, resultContextType, + resultConstraintKind, + /*calleeLocator=*/cs.getConstraintLocator(fakeAnchor), + /*FIXME:*/cs.getConstraintLocator(fakeAnchor)); + + // Solve the constraint system. + SmallVector solutions; + if (cs.solve(solutions) || solutions.size() != 1) { + // Try to fix the system or provide a decent diagnostic. + auto salvagedResult = cs.salvage(); + switch (salvagedResult.getKind()) { + case SolutionResult::Kind::Success: + solutions.clear(); + solutions.push_back(std::move(salvagedResult).takeSolution()); + break; + + case SolutionResult::Kind::Error: + case SolutionResult::Kind::Ambiguous: + return nullptr; + + case SolutionResult::Kind::UndiagnosedError: + cs.diagnoseFailureFor(SolutionApplicationTarget(func)); + salvagedResult.markAsDiagnosed(); + return nullptr; + + case SolutionResult::Kind::TooComplex: + func->diagnose(diag::expression_too_complex) + .highlight(func->getBodySourceRange()); + salvagedResult.markAsDiagnosed(); + return nullptr; + } + + // The system was salvaged; continue on as if nothing happened. + } + + // Apply the solution to the function body. + if (auto result = cs.applySolution( + solutions.front(), + SolutionApplicationTarget(func), + /*performingDiagnostics=*/false)) { + return result->getFunctionBody(); + } + + return nullptr; } -ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( - ClosureExpr *closure, Type builderType, ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator) { +ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { auto builder = builderType->getAnyNominal(); assert(builder && "Bad function builder type"); assert(builder->getAttrs().hasAttribute()); - // FIXME: Right now, single-expression closures suppress the function - // builder translation. - if (closure->hasSingleExpressionBody()) - return getTypeMatchSuccess(); - - // Pre-check the closure body: pre-check any expressions in it and look + // Pre-check the body: pre-check any expressions in it and look // for return statements. - switch (TC.preCheckFunctionBuilderClosureBody(closure)) { - case FunctionBuilderClosurePreCheck::Okay: + auto request = PreCheckFunctionBuilderRequest{fn}; + switch (evaluateOrDefault(getASTContext().evaluator, request, + FunctionBuilderBodyPreCheck::Error)) { + case FunctionBuilderBodyPreCheck::Okay: // If the pre-check was okay, apply the function-builder transform. break; - case FunctionBuilderClosurePreCheck::Error: + case FunctionBuilderBodyPreCheck::Error: // If the pre-check had an error, flag that. return getTypeMatchFailure(locator); - case FunctionBuilderClosurePreCheck::HasReturnStmt: - // If the closure has a return statement, suppress the transform but + case FunctionBuilderBodyPreCheck::HasReturnStmt: + // If the body has a return statement, suppress the transform but // continue solving the constraint system. return getTypeMatchSuccess(); } - // Check the form of this closure to see if we can apply the + // Check the form of this body to see if we can apply the // function-builder translation at all. + auto dc = fn.getAsDeclContext(); { // Check whether we can apply this specific function builder. - BuilderClosureVisitor visitor(getASTContext(), this, - /*wantExpr=*/false, builderType); - (void)visitor.visit(closure->getBody()); + BuilderClosureVisitor visitor(getASTContext(), nullptr, dc, builderType, + bodyResultType); // If we saw a control-flow statement or declaration that the builder // cannot handle, we don't have a well-formed function builder application. - if (visitor.unhandledNode) { + if (auto unhandledNode = visitor.check(fn.getBody())) { // If we aren't supposed to attempt fixes, fail. if (!shouldAttemptFixes()) { return getTypeMatchFailure(locator); @@ -543,7 +1163,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( // Record the first unhandled construct as a fix. if (recordFix( SkipUnhandledConstructInFunctionBuilder::create( - *this, visitor.unhandledNode, builder, + *this, unhandledNode, builder, getConstraintLocator(locator)))) { return getTypeMatchFailure(locator); } @@ -566,83 +1186,81 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder( assert(!builderType->hasTypeParameter()); } - BuilderClosureVisitor visitor(getASTContext(), this, - /*wantExpr=*/true, builderType); - Expr *singleExpr = visitor.visit(closure->getBody()); + BuilderClosureVisitor visitor(getASTContext(), this, dc, builderType, + bodyResultType); - // We've already pre-checked all the original expressions, but do the - // pre-check to the generated expression just to set up any preconditions - // that CSGen might have. - // - // TODO: just build the AST the way we want it in the first place. - if (TC.preCheckExpression(singleExpr, closure)) - return getTypeMatchFailure(locator); - - singleExpr = generateConstraints(singleExpr, closure); - if (!singleExpr) + auto applied = visitor.apply(fn.getBody()); + if (!applied) return getTypeMatchFailure(locator); - Type transformedType = getType(singleExpr); + Type transformedType = getType(applied->returnExpr); assert(transformedType && "Missing type"); // Record the transformation. assert(std::find_if( - builderTransformedClosures.begin(), - builderTransformedClosures.end(), - [&](const std::pair &elt) { - return elt.first == closure; - }) == builderTransformedClosures.end() && - "already transformed this closure along this path!?!"); - builderTransformedClosures.push_back( - std::make_pair(closure, - AppliedBuilderTransform{builderType, singleExpr})); - - // Bind the result type of the closure to the type of the transformed - // expression. - Type closureType = getType(closure); - auto fnType = closureType->castTo(); - addConstraint(ConstraintKind::Equal, fnType->getResult(), transformedType, + functionBuilderTransformed.begin(), + functionBuilderTransformed.end(), + [&](const std::pair &elt) { + return elt.first == fn; + }) == functionBuilderTransformed.end() && + "already transformed this body along this path!?!"); + functionBuilderTransformed.push_back( + std::make_pair(fn, std::move(*applied))); + + // If builder is applied to the closure expression then + // `closure body` to `closure result` matching should + // use special locator. + if (auto *closure = fn.getAbstractClosureExpr()) + locator = getConstraintLocator(closure, ConstraintLocator::ClosureResult); + + // Bind the body result type to the type of the transformed expression. + addConstraint(bodyResultConstraintKind, transformedType, bodyResultType, locator); return getTypeMatchSuccess(); } namespace { -/// Pre-check all the expressions in the closure body. -class PreCheckFunctionBuilderClosure : public ASTWalker { - TypeChecker &TC; - ClosureExpr *Closure; - bool HasReturnStmt = false; +/// Pre-check all the expressions in the body. +class PreCheckFunctionBuilderApplication : public ASTWalker { + AnyFunctionRef Fn; + bool SkipPrecheck = false; + std::vector ReturnStmts; bool HasError = false; + + bool hasReturnStmt() const { return !ReturnStmts.empty(); } + public: - PreCheckFunctionBuilderClosure(TypeChecker &tc, ClosureExpr *closure) - : TC(tc), Closure(closure) {} + PreCheckFunctionBuilderApplication(AnyFunctionRef fn, bool skipPrecheck) + : Fn(fn), SkipPrecheck(skipPrecheck) {} + + const std::vector getReturnStmts() const { return ReturnStmts; } - FunctionBuilderClosurePreCheck run() { - Stmt *oldBody = Closure->getBody(); + FunctionBuilderBodyPreCheck run() { + Stmt *oldBody = Fn.getBody(); Stmt *newBody = oldBody->walk(*this); // If the walk was aborted, it was because we had a problem of some kind. - assert((newBody == nullptr) == (HasError || HasReturnStmt) && - "unexpected short-circuit while walking closure body"); - if (!newBody) { - if (HasError) - return FunctionBuilderClosurePreCheck::Error; + assert((newBody == nullptr) == HasError && + "unexpected short-circuit while walking body"); + if (HasError) + return FunctionBuilderBodyPreCheck::Error; - return FunctionBuilderClosurePreCheck::HasReturnStmt; - } + if (hasReturnStmt()) + return FunctionBuilderBodyPreCheck::HasReturnStmt; assert(oldBody == newBody && "pre-check walk wasn't in-place?"); - return FunctionBuilderClosurePreCheck::Okay; + return FunctionBuilderBodyPreCheck::Okay; } std::pair walkToExprPre(Expr *E) override { // Pre-check the expression. If this fails, abort the walk immediately. // Otherwise, replace the expression with the result of pre-checking. // In either case, don't recurse into the expression. - if (TC.preCheckExpression(E, /*DC*/ Closure)) { + if (!SkipPrecheck && + ConstraintSystem::preCheckExpression(E, /*DC*/ Fn.getAsDeclContext())) { HasError = true; return std::make_pair(false, nullptr); } @@ -651,10 +1269,12 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { } std::pair walkToStmtPre(Stmt *S) override { - // If we see a return statement, abort the walk immediately. - if (isa(S)) { - HasReturnStmt = true; - return std::make_pair(false, nullptr); + // If we see a return statement, note it.. + if (auto returnStmt = dyn_cast(S)) { + if (!returnStmt->isImplicit()) { + ReturnStmts.push_back(returnStmt); + return std::make_pair(false, S); + } } // Otherwise, recurse into the statement normally. @@ -664,21 +1284,20 @@ class PreCheckFunctionBuilderClosure : public ASTWalker { } -FunctionBuilderClosurePreCheck -TypeChecker::preCheckFunctionBuilderClosureBody(ClosureExpr *closure) { +llvm::Expected +PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, + AnyFunctionRef fn) const { // Single-expression closures should already have been pre-checked. - if (closure->hasSingleExpressionBody()) - return FunctionBuilderClosurePreCheck::Okay; - - // Check whether we've already done this analysis. - auto it = precheckedFunctionBuilderClosures.find(closure); - if (it != precheckedFunctionBuilderClosures.end()) - return it->second; - - auto result = PreCheckFunctionBuilderClosure(*this, closure).run(); + if (auto closure = fn.getAbstractClosureExpr()) { + if (closure->hasSingleExpressionBody()) + return FunctionBuilderBodyPreCheck::Okay; + } - // Cache the result. - precheckedFunctionBuilderClosures.insert(std::make_pair(closure, result)); + return PreCheckFunctionBuilderApplication(fn, false).run(); +} - return result; +std::vector findReturnStatements(AnyFunctionRef fn) { + PreCheckFunctionBuilderApplication precheck(fn, true); + (void)precheck.run(); + return precheck.getReturnStmts(); } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 847f5d6150254..ac2b068d03abd 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -1,8 +1,4 @@ -if (SWIFT_FORCE_OPTIMIZED_TYPECHECKER) - set(EXTRA_TYPECHECKER_FLAGS "FORCE_BUILD_OPTIMIZED") -endif() - add_swift_host_library(swiftSema STATIC BuilderTransform.cpp CSApply.cpp @@ -46,6 +42,7 @@ add_swift_host_library(swiftSema STATIC TypeCheckDecl.cpp TypeCheckDeclObjC.cpp TypeCheckDeclOverride.cpp + TypeCheckDeclPrimary.cpp TypeCheckError.cpp TypeCheckExpr.cpp TypeCheckExprObjC.cpp @@ -62,9 +59,14 @@ add_swift_host_library(swiftSema STATIC TypeCheckSwitchStmt.cpp TypeCheckType.cpp TypeChecker.cpp - IDETypeCheckingRequests.cpp - - ${EXTRA_TYPECHECKER_FLAGS}) + IDETypeCheckingRequests.cpp) +if(SWIFT_FORCE_OPTIMIZED_TYPECHECKER) + if(CMAKE_CXX_COMPILER_ID STREQUAL MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL MSVC) + target_compile_options(swiftSema PRIVATE /O2 /Ob2) + else() + target_compile_options(swiftSema PRIVATE -O3) + endif() +endif() target_link_libraries(swiftSema PRIVATE swiftAST swiftParse diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 03428d512bdb0..443f67d35fd29 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -20,11 +20,13 @@ #include "CodeSynthesis.h" #include "CSDiagnostics.h" #include "MiscDiagnostics.h" +#include "SolutionResult.h" #include "TypeCheckProtocol.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -58,14 +60,13 @@ static bool isOpenedAnyObject(Type type) { return archetype->getOpenedExistentialType()->isAnyObject(); } -SubstitutionMap Solution::computeSubstitutions( - GenericSignature sig, - ConstraintLocatorBuilder locatorBuilder) const { +SubstitutionMap +Solution::computeSubstitutions(GenericSignature sig, + ConstraintLocator *locator) const { if (sig.isNull()) return SubstitutionMap(); // Gather the substitutions from dependent types to concrete types. - auto locator = getConstraintSystem().getConstraintLocator(locatorBuilder); auto openedTypes = OpenedTypes.find(locator); // If we have a member reference on an existential, there are no @@ -78,14 +79,15 @@ SubstitutionMap Solution::computeSubstitutions( subs[opened.first] = getFixedType(opened.second); auto lookupConformanceFn = - [&](CanType original, Type replacement, ProtocolDecl *protoType) - -> Optional { + [&](CanType original, Type replacement, + ProtocolDecl *protoType) -> ProtocolConformanceRef { if (replacement->hasError() || isOpenedAnyObject(replacement) || replacement->is()) { return ProtocolConformanceRef(protoType); } + // FIXME: Retrieve the conformance from the solution itself. return TypeChecker::conformsToProtocol(replacement, protoType, getConstraintSystem().DC, ConformanceCheckFlags::InExpression); @@ -96,6 +98,17 @@ SubstitutionMap Solution::computeSubstitutions( lookupConformanceFn); } +ConcreteDeclRef +Solution::resolveConcreteDeclRef(ValueDecl *decl, + ConstraintLocator *locator) const { + if (!decl) + return ConcreteDeclRef(); + + // Get the generic signatue of the decl and compute the substitutions. + auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); + return ConcreteDeclRef(decl, computeSubstitutions(sig, locator)); +} + static bool shouldAccessStorageDirectly(Expr *base, VarDecl *member, DeclContext *DC) { // This only matters for stored properties. @@ -139,6 +152,18 @@ static bool shouldAccessStorageDirectly(Expr *base, VarDecl *member, return true; } +ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator, + bool lookThroughApply) const { + auto &cs = getConstraintSystem(); + return cs.getCalleeLocator( + locator, lookThroughApply, + [&](const Expr *expr) -> Type { return getType(expr); }, + [&](Type type) -> Type { return simplifyType(type)->getRValueType(); }, + [&](ConstraintLocator *locator) -> Optional { + return getOverloadChoiceIfAvailable(locator); + }); +} + /// Return the implicit access kind for a MemberRefExpr with the /// specified base and member in the specified DeclContext. static AccessSemantics @@ -256,27 +281,6 @@ static bool buildObjCKeyPathString(KeyPathExpr *E, return true; } -/// Form a type checked expression for the index of a @dynamicMemberLookup -/// subscript index parameter. -/// The index expression will have a tuple type of `(dynamicMember: T)`. -static Expr *buildDynamicMemberLookupIndexExpr(StringRef name, SourceLoc loc, - DeclContext *dc, - ConstraintSystem &cs) { - auto &ctx = cs.TC.Context; - - auto *stringDecl = ctx.getStringDecl(); - auto stringType = stringDecl->getDeclaredType(); - - // Build and type check the string literal index value to the specific - // string type expected by the subscript. - auto *nameExpr = new (ctx) StringLiteralExpr(name, loc, /*implicit*/true); - nameExpr->setBuiltinInitializer(ctx.getStringBuiltinInitDecl(stringDecl)); - nameExpr->setType(stringType); - - cs.cacheExprTypes(nameExpr); - return nameExpr; -} - namespace { /// Rewrites an expression by applying the solution of a constraint @@ -313,12 +317,10 @@ namespace { /// /// \param expr The expression to be coerced. /// \param toType The type to which the expression will be coerced. - /// \param locator Locator describing where this conversion occurs. /// /// \return The coerced expression, whose type will be equivalent to /// \c toType. - Expr *coerceSuperclass(Expr *expr, Type toType, - ConstraintLocatorBuilder locator); + Expr *coerceSuperclass(Expr *expr, Type toType); /// Coerce the given value to existential type. /// @@ -330,12 +332,10 @@ namespace { /// /// \param expr The expression to be coerced. /// \param toType The type to which the expression will be coerced. - /// \param locator Locator describing where this conversion occurs. /// /// \return The coerced expression, whose type will be equivalent to /// \c toType. - Expr *coerceExistential(Expr *expr, Type toType, - ConstraintLocatorBuilder locator); + Expr *coerceExistential(Expr *expr, Type toType); /// Coerce an expression of (possibly unchecked) optional /// type to have a different (possibly unchecked) optional type. @@ -392,13 +392,75 @@ namespace { return base.getOldType(); } + // Returns None if the AST does not contain enough information to recover + // substitutions; this is different from an Optional(SubstitutionMap()), + // indicating a valid call to a non-generic operator. + Optional + getOperatorSubstitutions(ValueDecl *witness, Type refType) { + // We have to recover substitutions in this hacky way because + // the AST does not retain enough information to devirtualize + // calls like this. + auto witnessType = witness->getInterfaceType(); + + // Compute the substitutions. + auto *gft = witnessType->getAs(); + if (gft == nullptr) { + if (refType->isEqual(witnessType)) + return SubstitutionMap(); + return None; + } + + auto sig = gft->getGenericSignature(); + auto *env = sig->getGenericEnvironment(); + + witnessType = FunctionType::get(gft->getParams(), + gft->getResult(), + gft->getExtInfo()); + witnessType = env->mapTypeIntoContext(witnessType); + + TypeSubstitutionMap subs; + auto substType = witnessType->substituteBindingsTo( + refType, + [&](ArchetypeType *origType, CanType substType) -> CanType { + if (auto gpType = dyn_cast( + origType->getInterfaceType()->getCanonicalType())) + subs[gpType] = substType; + + return substType; + }); + + // If substitution failed, it means that the protocol requirement type + // and the witness type did not match up. The only time that this + // should happen is when the witness is defined in a base class and + // the actual call uses a derived class. For example, + // + // protocol P { func +(lhs: Self, rhs: Self) } + // class Base : P { func +(lhs: Base, rhs: Base) {} } + // class Derived : Base {} + // + // If we enter this code path with two operands of type Derived, + // we know we're calling the protocol requirement P.+, with a + // substituted type of (Derived, Derived) -> (). But the type of + // the witness is (Base, Base) -> (). Just bail out and make a + // witness method call in this rare case; SIL mandatory optimizations + // will likely devirtualize it anyway. + if (!substType) + return None; + + return SubstitutionMap::get(sig, + QueryTypeSubstitutionMap{subs}, + TypeChecker::LookUpConformance(cs.DC)); + } + public: /// Build a reference to the given declaration. - Expr *buildDeclRef(OverloadChoice choice, DeclNameLoc loc, Type openedType, + Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc, ConstraintLocatorBuilder locator, bool implicit, - FunctionRefKind functionRefKind, AccessSemantics semantics) { + auto choice = overload.choice; + assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic); auto *decl = choice.getDecl(); + auto fullType = simplifyType(overload.openedFullType); // Determine the declaration selected for this overloaded reference. auto &ctx = cs.getASTContext(); @@ -408,61 +470,57 @@ namespace { if (decl->getDeclContext()->isTypeContext() && isa(decl)) { assert(cast(decl)->isOperator() && "Must be an operator"); - auto openedFnType = openedType->castTo(); - auto simplifiedFnType - = simplifyType(openedFnType)->castTo(); - auto baseTy = getBaseType(simplifiedFnType); + auto baseTy = getBaseType(fullType->castTo()); // Handle operator requirements found in protocols. if (auto proto = dyn_cast(decl->getDeclContext())) { - // If we don't have an archetype or existential, we have to call the - // witness. + // If we have a concrete conformance, build a call to the witness. + // // FIXME: This is awful. We should be able to handle this as a call to // the protocol requirement with Self == the concrete type, and SILGen // (or later) can devirtualize as appropriate. - if (!baseTy->is() && !baseTy->isAnyExistentialType()) { - auto &tc = cs.getTypeChecker(); - auto conformance = - TypeChecker::conformsToProtocol( - baseTy, proto, cs.DC, - ConformanceCheckFlags::InExpression); - if (conformance && conformance->isConcrete()) { - if (auto witness = - conformance->getConcrete()->getWitnessDecl(decl)) { - // Hack up an AST that we can type-check (independently) to get - // it into the right form. - // FIXME: the hop through 'getDecl()' is because - // SpecializedProtocolConformance doesn't substitute into - // witnesses' ConcreteDeclRefs. - Type expectedFnType = simplifiedFnType->getResult(); + auto conformance = + TypeChecker::conformsToProtocol( + baseTy, proto, cs.DC, + ConformanceCheckFlags::InExpression); + if (conformance.isConcrete()) { + if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) { + // The fullType was computed by substituting the protocol + // requirement so it always has a (Self) -> ... curried + // application. Strip it off if the witness was a top-level + // function. + Type refType; + if (witness->getDeclContext()->isTypeContext()) + refType = fullType; + else + refType = fullType->castTo()->getResult(); + + // Build the AST for the call to the witness. + auto subMap = getOperatorSubstitutions(witness, refType); + if (subMap) { + ConcreteDeclRef witnessRef(witness, *subMap); + auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc, + /*Implicit=*/false); + declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); + cs.setType(declRefExpr, refType); + Expr *refExpr; if (witness->getDeclContext()->isTypeContext()) { + // If the operator is a type member, add the implicit + // (Self) -> ... call. Expr *base = TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx); - refExpr = new (ctx) MemberRefExpr(base, SourceLoc(), witness, - loc, /*Implicit=*/true); + cs.setType(base, MetatypeType::get(baseTy)); + + refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr, + SourceLoc(), base); + auto refType = fullType->castTo()->getResult(); + cs.setType(refExpr, refType); } else { - auto declRefExpr = new (ctx) DeclRefExpr(witness, loc, - /*Implicit=*/false); - declRefExpr->setFunctionRefKind(functionRefKind); refExpr = declRefExpr; } - auto resultTy = tc.typeCheckExpression( - refExpr, cs.DC, TypeLoc::withoutLoc(expectedFnType), - CTP_CannotFail); - if (!resultTy) - return nullptr; - - cs.cacheExprTypes(refExpr); - - // Remove an outer function-conversion expression. This - // happens when we end up referring to a witness for a - // superclass conformance, and 'Self' differs. - if (auto fnConv = dyn_cast(refExpr)) - refExpr = fnConv->getSubExpr(); - return forceUnwrapIfExpected(refExpr, choice, locator); } } @@ -474,31 +532,22 @@ namespace { TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx); cs.cacheExprTypes(base); - return buildMemberRef(base, openedType, SourceLoc(), choice, loc, - openedFnType->getResult(), locator, locator, - implicit, functionRefKind, semantics, - /*isDynamic=*/false); + return buildMemberRef(base, SourceLoc(), overload, loc, locator, + locator, implicit, semantics); } - auto type = solution.simplifyType(openedType); - if (isa(decl) && !isa(decl)) { auto typeExpr = TypeExpr::createImplicitHack( - loc.getBaseNameLoc(), type->getMetatypeInstanceType(), - ctx); + loc.getBaseNameLoc(), fullType->getMetatypeInstanceType(), ctx); cs.cacheType(typeExpr); return typeExpr; } - auto substitutions = - solution.computeSubstitutions( - decl->getInnermostDeclContext()->getGenericSignatureOfContext(), - locator); + auto ref = resolveConcreteDeclRef(decl, locator); auto declRefExpr = - new (ctx) DeclRefExpr(ConcreteDeclRef(decl, substitutions), - loc, implicit, semantics, type); + new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType); cs.cacheType(declRefExpr); - declRefExpr->setFunctionRefKind(functionRefKind); + declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); return forceUnwrapIfExpected(declRefExpr, choice, locator); } @@ -527,6 +576,35 @@ namespace { /// A stack of expressions being walked, used to compute existential depth. llvm::SmallVector ExprStack; + /// A map of apply exprs to their callee locators. This is necessary + /// because after rewriting an apply's function expr, its callee locator + /// will no longer be equivalent to the one stored in the solution. + llvm::DenseMap CalleeLocators; + + /// A cache of decl references with their contextual substitutions for a + /// given callee locator. + llvm::DenseMap CachedConcreteRefs; + + /// Resolves the contextual substitutions for a reference to a declaration + /// at a given locator. This should be preferred to + /// Solution::resolveConcreteDeclRef as it caches the result. + ConcreteDeclRef + resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocatorBuilder locator) { + if (!decl) + return ConcreteDeclRef(); + + // Cache the resulting concrete reference. Ideally this would be done on + // Solution, however unfortunately that would require a const_cast which + // would be undefined behaviour if we ever had a `const Solution`. + auto *loc = getConstraintSystem().getConstraintLocator(locator); + auto &ref = CachedConcreteRefs[loc]; + if (!ref) + ref = solution.resolveConcreteDeclRef(decl, loc); + + assert(ref.getDecl() == decl); + return ref; + } + /// Members which are AbstractFunctionDecls but not FuncDecls cannot /// mutate self. bool isNonMutatingMember(ValueDecl *member) { @@ -622,8 +700,6 @@ namespace { ValueDecl *member) { assert(archetype && "archetype not already opened?"); - auto &tc = cs.getTypeChecker(); - // Dig out the base type. Type baseTy = cs.getType(base); @@ -667,7 +743,7 @@ namespace { if (isLValue) opaqueType = LValueType::get(opaqueType); - ASTContext &ctx = tc.Context; + ASTContext &ctx = cs.getASTContext(); auto archetypeVal = new (ctx) OpaqueValueExpr(base->getSourceRange(), opaqueType); cs.cacheType(archetypeVal); @@ -691,8 +767,6 @@ namespace { return false; // If we had a return type of 'Self', erase it. - ConstraintSystem &innerCS = solution.getConstraintSystem(); - auto &tc = innerCS.getTypeChecker(); Type resultTy; resultTy = cs.getType(result); if (resultTy->hasOpenedExistential(record.Archetype)) { @@ -701,7 +775,7 @@ namespace { result = coerceToType(result, erasedTy, locator); // FIXME: Implement missing tuple-to-tuple conversion if (result == nullptr) { - result = new (tc.Context) ErrorExpr(range); + result = new (cs.getASTContext()) ErrorExpr(range); cs.setType(result, erasedTy); // The opaque value is no longer reachable in an AST walk as // a result of the result above being replaced with an @@ -713,7 +787,7 @@ namespace { } // Form the open-existential expression. - result = new (tc.Context) OpenExistentialExpr( + result = new (cs.getASTContext()) OpenExistentialExpr( record.ExistentialValue, record.OpaqueValue, result, cs.getType(result)); @@ -724,16 +798,18 @@ namespace { } /// Build a new member reference with the given base and member. - Expr *buildMemberRef(Expr *base, Type openedFullType, SourceLoc dotLoc, - OverloadChoice choice, DeclNameLoc memberLoc, - Type openedType, ConstraintLocatorBuilder locator, + Expr *buildMemberRef(Expr *base, SourceLoc dotLoc, + SelectedOverload overload, DeclNameLoc memberLoc, + ConstraintLocatorBuilder locator, ConstraintLocatorBuilder memberLocator, bool Implicit, - FunctionRefKind functionRefKind, - AccessSemantics semantics, bool isDynamic) { + AccessSemantics semantics) { + auto choice = overload.choice; + auto openedType = overload.openedType; + auto openedFullType = overload.openedFullType; + ValueDecl *member = choice.getDecl(); - auto &tc = cs.getTypeChecker(); - auto &context = tc.Context; + auto &context = cs.getASTContext(); bool isSuper = base->isSuperExpr(); @@ -751,12 +827,7 @@ namespace { } // Build a member reference. - SubstitutionMap substitutions = - solution.computeSubstitutions( - member->getInnermostDeclContext()->getGenericSignatureOfContext(), - memberLocator); - auto memberRef = ConcreteDeclRef(member, substitutions); - + auto memberRef = resolveConcreteDeclRef(member, memberLocator); auto refTy = solution.simplifyType(openedFullType); // If we're referring to the member of a module, it's just a simple @@ -766,7 +837,7 @@ namespace { "Direct property access doesn't make sense for this"); auto ref = new (context) DeclRefExpr(memberRef, memberLoc, Implicit); cs.setType(ref, refTy); - ref->setFunctionRefKind(functionRefKind); + ref->setFunctionRefKind(choice.getFunctionRefKind()); auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr( base, dotLoc, ref, cs.getType(ref))); return forceUnwrapIfExpected(DSBI, choice, memberLocator); @@ -835,6 +906,7 @@ namespace { semantics = getImplicitMemberReferenceAccessSemantics(base, VD, dc); } + auto isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; if (baseIsInstance) { // Convert the base to the appropriate container type, turning it // into an lvalue if required. @@ -893,19 +965,16 @@ namespace { // complain. if (auto attr = member->getAttrs().getAttribute()) { if (attr->isSwift3Inferred() && - tc.Context.LangOpts.WarnSwift3ObjCInference - == Swift3ObjCInferenceWarnings::Minimal) { - tc.diagnose(memberLoc, - diag::expr_dynamic_lookup_swift3_objc_inference, - member->getDescriptiveKind(), - member->getFullName(), - member->getDeclContext() - ->getSelfNominalTypeDecl() - ->getName()); - tc.diagnose(member, diag::make_decl_objc, - member->getDescriptiveKind()) - .fixItInsert(member->getAttributeInsertionLoc(false), - "@objc "); + context.LangOpts.WarnSwift3ObjCInference == + Swift3ObjCInferenceWarnings::Minimal) { + context.Diags.diagnose( + memberLoc, diag::expr_dynamic_lookup_swift3_objc_inference, + member->getDescriptiveKind(), member->getFullName(), + member->getDeclContext()->getSelfNominalTypeDecl()->getName()); + context.Diags + .diagnose(member, diag::make_decl_objc, + member->getDescriptiveKind()) + .fixItInsert(member->getAttributeInsertionLoc(false), "@objc "); } } @@ -967,7 +1036,7 @@ namespace { // Handle all other references. auto declRefExpr = new (context) DeclRefExpr(memberRef, memberLoc, Implicit, semantics); - declRefExpr->setFunctionRefKind(functionRefKind); + declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); cs.setType(declRefExpr, refTy); Expr *ref = declRefExpr; @@ -1001,11 +1070,8 @@ namespace { } } - return finishApply(apply, openedType, locator); + return finishApply(apply, openedType, locator, memberLocator); } - - /// Describes either a type or the name of a type to be resolved. - using TypeOrName = llvm::PointerUnion; /// Convert the given literal expression via a protocol pair. /// @@ -1065,8 +1131,11 @@ namespace { /// type-checked expression appropriately. /// /// \param locator The locator for the original expression. + /// + /// \param calleeLocator The locator that identifies the apply's callee. Expr *finishApply(ApplyExpr *apply, Type openedType, - ConstraintLocatorBuilder locator); + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator); // Resolve `@dynamicCallable` applications. Expr *finishApplyDynamicCallable(ApplyExpr *apply, @@ -1129,6 +1198,7 @@ namespace { /// /// \param arg The argument expression. /// \param funcType The function type. + /// \param callee The callee for the function being applied. /// \param apply The ApplyExpr that forms the call. /// \param argLabels The argument labels provided for the call. /// \param hasTrailingClosure Whether the last argument is a trailing @@ -1138,7 +1208,7 @@ namespace { /// \returns the coerced expression, which will have type \c ToType. Expr * coerceCallArguments(Expr *arg, AnyFunctionType *funcType, - ApplyExpr *apply, + ConcreteDeclRef callee, ApplyExpr *apply, ArrayRef argLabels, bool hasTrailingClosure, ConstraintLocatorBuilder locator); @@ -1172,45 +1242,17 @@ namespace { bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics, - Optional selected = None) { - - // Determine the declaration selected for this subscript operation. - if (!selected) - selected = solution.getOverloadChoiceIfAvailable( - cs.getConstraintLocator( - locator.withPathElement( - ConstraintLocator::SubscriptMember))); - - // Handles situation where there was a solution available but it didn't - // have a proper overload selected from subscript call, might be because - // solver was allowed to return free or unresolved types, which can - // happen while running diagnostics on one of the expressions. - if (!selected.hasValue()) { - auto &tc = cs.TC; - auto baseType = cs.getType(base); - - if (auto errorType = baseType->getAs()) { - tc.diagnose(base->getLoc(), diag::cannot_subscript_base, - errorType->getOriginalType()) - .highlight(base->getSourceRange()); - } else { - tc.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) - .highlight(base->getSourceRange()); - } - - return nullptr; - } - + const SelectedOverload &selected) { // Build the new subscript. auto newSubscript = buildSubscriptHelper(base, index, argLabels, - *selected, hasTrailingClosure, + selected, hasTrailingClosure, locator, isImplicit, semantics); - if (selected->choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { + if (selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic) { // Rewrite for implicit unwrapping if the solution requires it. auto *dynamicLocator = cs.getConstraintLocator( - locator.withPathElement(ConstraintLocator::SubscriptMember) - .withPathElement(ConstraintLocator::DynamicLookupResult)); + locator, {ConstraintLocator::SubscriptMember, + ConstraintLocator::DynamicLookupResult}); if (solution.getDisjunctionChoice(dynamicLocator)) { auto *forceValue = new (cs.getASTContext()) @@ -1221,19 +1263,19 @@ namespace { } } - if (selected->choice.isDecl()) { + if (selected.choice.isDecl()) { auto locatorKind = ConstraintLocator::SubscriptMember; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::DynamicMemberLookup) locatorKind = ConstraintLocator::Member; - if (selected->choice.getKind() == + if (selected.choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup && !isa(locator.getAnchor())) locatorKind = ConstraintLocator::Member; newSubscript = - forceUnwrapIfExpected(newSubscript, selected->choice, + forceUnwrapIfExpected(newSubscript, selected.choice, locator.withPathElement(locatorKind)); } @@ -1242,11 +1284,12 @@ namespace { Expr *buildSubscriptHelper(Expr *base, Expr *index, ArrayRef argLabels, - SelectedOverload &selected, + const SelectedOverload &selected, bool hasTrailingClosure, ConstraintLocatorBuilder locator, bool isImplicit, AccessSemantics semantics) { auto choice = selected.choice; + auto &ctx = cs.getASTContext(); // Apply a key path if we have one. if (choice.getKind() == OverloadChoiceKind::KeyPathApplication) { @@ -1276,11 +1319,11 @@ namespace { // We don't really want to attempt AnyKeyPath application // if we know a more specific key path type is being applied. if (!keyPathTy->isEqual(keyPathExprTy)) { - cs.TC.diagnose(base->getLoc(), - diag::expr_smart_keypath_application_type_mismatch, - keyPathExprTy, - baseTy) - .highlight(index->getSourceRange()); + ctx.Diags + .diagnose(base->getLoc(), + diag::expr_smart_keypath_application_type_mismatch, + keyPathExprTy, baseTy) + .highlight(index->getSourceRange()); } } else { llvm_unreachable("unknown key path class!"); @@ -1337,7 +1380,6 @@ namespace { auto subscript = cast(choice.getDecl()); - auto &tc = cs.getTypeChecker(); auto baseTy = cs.getType(base)->getRValueType(); bool baseIsInstance = true; @@ -1362,51 +1404,34 @@ namespace { // If we opened up an existential when performing the subscript, open // the base accordingly. + auto memberLoc = locator.withPathElement(locatorKind); auto knownOpened = solution.OpenedExistentialTypes.find( - getConstraintSystem().getConstraintLocator( - locator.withPathElement(locatorKind))); + cs.getConstraintLocator(memberLoc)); if (knownOpened != solution.OpenedExistentialTypes.end()) { base = openExistentialReference(base, knownOpened->second, subscript); baseTy = knownOpened->second; } - + + // Compute the concrete reference to the subscript. + auto subscriptRef = resolveConcreteDeclRef(subscript, memberLoc); + // Figure out the index and result types. - Type resultTy; - if (choice.getKind() != OverloadChoiceKind::DynamicMemberLookup && - choice.getKind() != OverloadChoiceKind::KeyPathDynamicMemberLookup) { - auto subscriptTy = simplifyType(selected.openedType); - auto *subscriptFnTy = subscriptTy->castTo(); - resultTy = subscriptFnTy->getResult(); - - // Coerce the index argument. - index = coerceCallArguments(index, subscriptFnTy, nullptr, - argLabels, hasTrailingClosure, - locator.withPathElement( - ConstraintLocator::ApplyArgument)); - if (!index) - return nullptr; + auto subscriptTy = simplifyType(selected.openedType); + auto *subscriptFnTy = subscriptTy->castTo(); + auto resultTy = subscriptFnTy->getResult(); - } else { - // If this is a @dynamicMemberLookup, then the type of the selection is - // actually the property/result type. That's fine though, and we - // already have the index type adjusted to the correct type expected by - // the subscript. - resultTy = simplifyType(selected.openedType); - } + // Coerce the index argument. + index = coerceCallArguments(index, subscriptFnTy, subscriptRef, nullptr, + argLabels, hasTrailingClosure, + locator.withPathElement( + ConstraintLocator::ApplyArgument)); + if (!index) + return nullptr; auto getType = [&](const Expr *E) -> Type { return cs.getType(E); }; - // Form the subscript expression. - - // Compute the substitutions used to reference the subscript. - SubstitutionMap substitutions = - solution.computeSubstitutions( - subscript->getInnermostDeclContext()->getGenericSignatureOfContext(), - locator.withPathElement(locatorKind)); - ConcreteDeclRef subscriptRef(subscript, substitutions); - // Handle dynamic lookup. if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic || subscript->getAttrs().hasAttribute()) { @@ -1416,9 +1441,8 @@ namespace { return nullptr; // TODO: diagnose if semantics != AccessSemantics::Ordinary? - auto subscriptExpr = DynamicSubscriptExpr::create(tc.Context, base, - index, subscriptRef, - isImplicit, getType); + auto subscriptExpr = DynamicSubscriptExpr::create( + ctx, base, index, subscriptRef, isImplicit, getType); cs.setType(subscriptExpr, resultTy); Expr *result = subscriptExpr; closeExistential(result, locator); @@ -1451,7 +1475,7 @@ namespace { // Form the subscript expression. auto subscriptExpr = SubscriptExpr::create( - tc.Context, base, index, subscriptRef, isImplicit, semantics, getType); + ctx, base, index, subscriptRef, isImplicit, semantics, getType); cs.setType(subscriptExpr, resultTy); subscriptExpr->setIsSuper(isSuper); @@ -1461,8 +1485,8 @@ namespace { if (subscript->getElementInterfaceType()->hasDynamicSelfType()) { auto dynamicSelfFnType = openedFullFnType->replaceCovariantResultType(baseTy, 2); - result = new (tc.Context) CovariantReturnConversionExpr(result, - dynamicSelfFnType); + result = + new (ctx) CovariantReturnConversionExpr(result, dynamicSelfFnType); cs.cacheType(result); cs.setType(result, simplifyType(baseTy)); } @@ -1472,18 +1496,11 @@ namespace { /// Build a new reference to another constructor. Expr *buildOtherConstructorRef(Type openedFullType, - ConstructorDecl *ctor, Expr *base, + ConcreteDeclRef ref, Expr *base, DeclNameLoc loc, ConstraintLocatorBuilder locator, bool implicit) { - auto &tc = cs.getTypeChecker(); - auto &ctx = tc.Context; - - // Compute the concrete reference. - SubstitutionMap substitutions = - solution.computeSubstitutions(ctor->getGenericSignature(), locator); - - auto ref = ConcreteDeclRef(ctor, substitutions); + auto &ctx = cs.getASTContext(); // The constructor was opened with the allocating type, not the // initializer type. Map the former into the latter. @@ -1533,7 +1550,7 @@ namespace { auto &ctx = cs.getASTContext(); auto *anchor = memberLoc->getAnchor(); - KeyPathExpr::Component component; + SmallVector components; // Let's create a KeyPath expression and fill in "parsed path" // after component is built. @@ -1555,10 +1572,11 @@ namespace { // calls necessary to resolve a member reference. if (overload.choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) { - keyPath->resolveComponents(ctx, - buildKeyPathSubscriptComponent( - overload, dotLoc, /*indexExpr=*/nullptr, - ctx.Id_dynamicMember, componentLoc)); + buildKeyPathSubscriptComponent(overload, dotLoc, /*indexExpr=*/nullptr, + ctx.Id_dynamicMember, componentLoc, + components); + keyPath->resolveComponents(ctx, components); + cs.cacheExprTypes(keyPath); return keyPath; } @@ -1608,8 +1626,8 @@ namespace { UDE->getNameLoc(), /*Implicit=*/true); - component = buildKeyPathPropertyComponent(overload, UDE->getLoc(), - componentLoc); + buildKeyPathPropertyComponent(overload, UDE->getLoc(), componentLoc, + components); } else if (auto *SE = dyn_cast(anchor)) { componentExpr = SE; // If this is not for a keypath component, we have to copy @@ -1637,9 +1655,9 @@ namespace { /*implicit=*/true, SE->getAccessSemantics()); } - component = buildKeyPathSubscriptComponent( - overload, SE->getLoc(), SE->getIndex(), SE->getArgumentLabels(), - componentLoc); + buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getIndex(), + SE->getArgumentLabels(), componentLoc, + components); } else { return nullptr; } @@ -1649,22 +1667,20 @@ namespace { componentExpr->setType(ty); cs.cacheType(componentExpr); - cs.setType(keyPath, 0, ty); - keyPath->setParsedPath(componentExpr); - keyPath->resolveComponents(ctx, {component}); + keyPath->resolveComponents(ctx, components); + cs.cacheExprTypes(keyPath); return keyPath; } /// Bridge the given value (which is an error type) to NSError. Expr *bridgeErrorToObjectiveC(Expr *value) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); - auto nsErrorDecl = tc.Context.getNSErrorDecl(); - assert(nsErrorDecl && "Missing NSError?"); - Type nsErrorType = nsErrorDecl->getDeclaredInterfaceType(); + auto nsErrorType = ctx.getNSErrorType(); + assert(nsErrorType && "Missing NSError?"); - auto result = new (tc.Context) BridgeToObjCExpr(value, nsErrorType); + auto result = new (ctx) BridgeToObjCExpr(value, nsErrorType); return cs.cacheType(result); } @@ -1675,9 +1691,7 @@ namespace { /// /// \param value The value to be bridged. Expr *bridgeToObjectiveC(Expr *value, Type objcType) { - auto &tc = cs.getTypeChecker(); - - auto result = new (tc.Context) BridgeToObjCExpr(value, objcType); + auto result = new (cs.getASTContext()) BridgeToObjCExpr(value, objcType); return cs.cacheType(result); } @@ -1697,16 +1711,16 @@ namespace { /// stores the bridged result or (when \c conditional) an empty optional if /// conditional bridging fails. Expr *bridgeFromObjectiveC(Expr *object, Type valueType, bool conditional) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); if (!conditional) { - auto result = new (tc.Context) BridgeFromObjCExpr(object, valueType); + auto result = new (ctx) BridgeFromObjCExpr(object, valueType); return cs.cacheType(result); } // Find the _BridgedToObjectiveC protocol. - auto bridgedProto - = tc.Context.getProtocol(KnownProtocolKind::ObjectiveCBridgeable); + auto bridgedProto = + ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable); // Try to find the conformance of the value type to _BridgedToObjectiveC. auto bridgedToObjectiveCConformance @@ -1718,25 +1732,24 @@ namespace { FuncDecl *fn = nullptr; if (bridgedToObjectiveCConformance) { - assert(bridgedToObjectiveCConformance->getConditionalRequirements() + assert(bridgedToObjectiveCConformance.getConditionalRequirements() .empty() && "cannot conditionally conform to _BridgedToObjectiveC"); // The conformance to _BridgedToObjectiveC is statically known. // Retrieve the bridging operation to be used if a static conformance // to _BridgedToObjectiveC can be proven. - fn = conditional - ? tc.Context.getConditionallyBridgeFromObjectiveCBridgeable() - : tc.Context.getForceBridgeFromObjectiveCBridgeable(); + fn = conditional ? ctx.getConditionallyBridgeFromObjectiveCBridgeable() + : ctx.getForceBridgeFromObjectiveCBridgeable(); } else { // Retrieve the bridging operation to be used if a static conformance // to _BridgedToObjectiveC cannot be proven. - fn = conditional ? tc.Context.getConditionallyBridgeFromObjectiveC() - : tc.Context.getForceBridgeFromObjectiveC(); + fn = conditional ? ctx.getConditionallyBridgeFromObjectiveC() + : ctx.getForceBridgeFromObjectiveC(); } if (!fn) { - tc.diagnose(object->getLoc(), diag::missing_bridging_function, - conditional); + ctx.Diags.diagnose(object->getLoc(), diag::missing_bridging_function, + conditional); return nullptr; } @@ -1745,23 +1758,23 @@ namespace { auto genericSig = fn->getGenericSignature(); auto subMap = SubstitutionMap::get( - genericSig, - [&](SubstitutableType *type) -> Type { - assert(type->isEqual(genericSig->getGenericParams()[0])); - return valueType; - }, - [&](CanType origType, Type replacementType, ProtocolDecl *protoType) - -> ProtocolConformanceRef { - assert(bridgedToObjectiveCConformance); - return *bridgedToObjectiveCConformance; - }); + genericSig, + [&](SubstitutableType *type) -> Type { + assert(type->isEqual(genericSig->getGenericParams()[0])); + return valueType; + }, + [&](CanType origType, Type replacementType, + ProtocolDecl *protoType) -> ProtocolConformanceRef { + assert(bridgedToObjectiveCConformance); + return bridgedToObjectiveCConformance; + }); ConcreteDeclRef fnSpecRef(fn, subMap); auto resultType = OptionalType::get(valueType); - auto result = new (tc.Context) ConditionalBridgeFromObjCExpr(object, - resultType, fnSpecRef); + auto result = new (ctx) + ConditionalBridgeFromObjCExpr(object, resultType, fnSpecRef); return cs.cacheType(result); } @@ -1815,37 +1828,38 @@ namespace { if (cs.getType(expr)->is()) return expr; - auto &tc = cs.getTypeChecker(); - ProtocolDecl *protocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByIntegerLiteral); - ProtocolDecl *builtinProtocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByBuiltinIntegerLiteral); + auto &ctx = cs.getASTContext(); + ProtocolDecl *protocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByIntegerLiteral); + ProtocolDecl *builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByBuiltinIntegerLiteral); // For type-sugar reasons, prefer the spelling of the default literal // type. auto type = simplifyType(cs.getType(expr)); - if (auto defaultType = tc.getDefaultType(protocol, dc)) { + if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) { if (defaultType->isEqual(type)) type = defaultType; } - if (auto floatProtocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByFloatLiteral)) { - if (auto defaultFloatType = tc.getDefaultType(floatProtocol, dc)) { + if (auto floatProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByFloatLiteral)) { + if (auto defaultFloatType = + TypeChecker::getDefaultType(floatProtocol, dc)) { if (defaultFloatType->isEqual(type)) type = defaultFloatType; } } - DeclName initName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_integerLiteral }); - DeclName builtinInitName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_builtinIntegerLiteral }); + DeclName initName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_integerLiteral}); + DeclName builtinInitName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinIntegerLiteral}); auto *result = convertLiteralInPlace( - expr, type, protocol, tc.Context.Id_IntegerLiteralType, initName, + expr, type, protocol, ctx.Id_IntegerLiteralType, initName, builtinProtocol, builtinInitName, diag::integer_literal_broken_proto, diag::builtin_integer_literal_broken_proto); if (result) { @@ -1866,19 +1880,19 @@ namespace { return expr; } - auto &tc = cs.getTypeChecker(); - auto *protocol = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByNilLiteral); + auto &ctx = cs.getASTContext(); + auto *protocol = TypeChecker::getProtocol( + ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByNilLiteral); // For type-sugar reasons, prefer the spelling of the default literal // type. - if (auto defaultType = tc.getDefaultType(protocol, dc)) { + if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) { if (defaultType->isEqual(type)) type = defaultType; } - DeclName initName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_nilLiteral }); + DeclName initName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_nilLiteral}); return convertLiteralInPlace(expr, type, protocol, Identifier(), initName, nullptr, @@ -1898,42 +1912,41 @@ namespace { if (cs.getType(expr)->is()) return expr; - auto &tc = cs.getTypeChecker(); - ProtocolDecl *protocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByFloatLiteral); - ProtocolDecl *builtinProtocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByBuiltinFloatLiteral); + auto &ctx = cs.getASTContext(); + ProtocolDecl *protocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByFloatLiteral); + ProtocolDecl *builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByBuiltinFloatLiteral); // For type-sugar reasons, prefer the spelling of the default literal // type. auto type = simplifyType(cs.getType(expr)); - if (auto defaultType = tc.getDefaultType(protocol, dc)) { + if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) { if (defaultType->isEqual(type)) type = defaultType; } // Get the _MaxBuiltinFloatType decl, or look for it if it's not cached. - auto maxFloatTypeDecl = tc.Context.get_MaxBuiltinFloatTypeDecl(); + auto maxFloatTypeDecl = ctx.get_MaxBuiltinFloatTypeDecl(); if (!maxFloatTypeDecl || - !maxFloatTypeDecl->getInterfaceType() || !maxFloatTypeDecl->getDeclaredInterfaceType()->is()) { - tc.diagnose(expr->getLoc(), diag::no_MaxBuiltinFloatType_found); + ctx.Diags.diagnose(expr->getLoc(), diag::no_MaxBuiltinFloatType_found); return nullptr; } auto maxType = maxFloatTypeDecl->getUnderlyingType(); - DeclName initName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_floatLiteral }); - DeclName builtinInitName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_builtinFloatLiteral }); + DeclName initName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_floatLiteral}); + DeclName builtinInitName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinFloatLiteral}); expr->setBuiltinType(maxType); return convertLiteralInPlace( - expr, type, protocol, tc.Context.Id_FloatLiteralType, initName, + expr, type, protocol, ctx.Id_FloatLiteralType, initName, builtinProtocol, builtinInitName, diag::float_literal_broken_proto, diag::builtin_float_literal_broken_proto); } @@ -1942,31 +1955,25 @@ namespace { if (cs.getType(expr) && cs.getType(expr)->is()) return expr; - auto &tc = cs.getTypeChecker(); - ProtocolDecl *protocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByBooleanLiteral); - ProtocolDecl *builtinProtocol - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByBuiltinBooleanLiteral); + auto &ctx = cs.getASTContext(); + ProtocolDecl *protocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByBooleanLiteral); + ProtocolDecl *builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByBuiltinBooleanLiteral); if (!protocol || !builtinProtocol) return nullptr; auto type = simplifyType(cs.getType(expr)); - DeclName initName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_booleanLiteral }); - DeclName builtinInitName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_builtinBooleanLiteral }); + DeclName initName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_booleanLiteral}); + DeclName builtinInitName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinBooleanLiteral}); return convertLiteralInPlace( - expr, - type, - protocol, - tc.Context.Id_BooleanLiteralType, - initName, - builtinProtocol, - builtinInitName, - diag::boolean_literal_broken_proto, - diag::builtin_boolean_literal_broken_proto); + expr, type, protocol, ctx.Id_BooleanLiteralType, initName, + builtinProtocol, builtinInitName, diag::boolean_literal_broken_proto, + diag::builtin_boolean_literal_broken_proto); } Expr *handleStringLiteralExpr(LiteralExpr *expr) { @@ -1976,28 +1983,28 @@ namespace { "literal must be either a string literal or a magic literal"); auto type = simplifyType(cs.getType(expr)); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); bool isStringLiteral = true; bool isGraphemeClusterLiteral = false; - ProtocolDecl *protocol = tc.getProtocol( - expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral); + ProtocolDecl *protocol = TypeChecker::getProtocol( + ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral); - if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC, - ConformanceCheckFlags::InExpression)) { + if (!TypeChecker::conformsToProtocol( + type, protocol, cs.DC, ConformanceCheckFlags::InExpression)) { // If the type does not conform to ExpressibleByStringLiteral, it should // be ExpressibleByExtendedGraphemeClusterLiteral. - protocol = tc.getProtocol( - expr->getLoc(), + protocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral); isStringLiteral = false; isGraphemeClusterLiteral = true; } - if (!TypeChecker::conformsToProtocol(type, protocol, cs.DC, - ConformanceCheckFlags::InExpression)) { + if (!TypeChecker::conformsToProtocol( + type, protocol, cs.DC, ConformanceCheckFlags::InExpression)) { // ... or it should be ExpressibleByUnicodeScalarLiteral. - protocol = tc.getProtocol( - expr->getLoc(), + protocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), KnownProtocolKind::ExpressibleByUnicodeScalarLiteral); isStringLiteral = false; isGraphemeClusterLiteral = false; @@ -2005,7 +2012,7 @@ namespace { // For type-sugar reasons, prefer the spelling of the default literal // type. - if (auto defaultType = tc.getDefaultType(protocol, dc)) { + if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) { if (defaultType->isEqual(type)) type = defaultType; } @@ -2018,19 +2025,19 @@ namespace { Diag<> brokenBuiltinProtocolDiag; if (isStringLiteral) { - literalType = tc.Context.Id_StringLiteralType; + literalType = ctx.Id_StringLiteralType; - literalFuncName = DeclName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_stringLiteral }); + literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_stringLiteral}); - builtinProtocol = tc.getProtocol( - expr->getLoc(), + builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), KnownProtocolKind::ExpressibleByBuiltinStringLiteral); - builtinLiteralFuncName - = DeclName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_builtinStringLiteral, - tc.Context.getIdentifier("utf8CodeUnitCount"), - tc.Context.getIdentifier("isASCII") }); + builtinLiteralFuncName = + DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinStringLiteral, + ctx.getIdentifier("utf8CodeUnitCount"), + ctx.getIdentifier("isASCII")}); if (stringLiteral) stringLiteral->setEncoding(StringLiteralExpr::UTF8); else @@ -2039,36 +2046,35 @@ namespace { brokenProtocolDiag = diag::string_literal_broken_proto; brokenBuiltinProtocolDiag = diag::builtin_string_literal_broken_proto; } else if (isGraphemeClusterLiteral) { - literalType = tc.Context.Id_ExtendedGraphemeClusterLiteralType; - literalFuncName - = DeclName(tc.Context, DeclBaseName::createConstructor(), - {tc.Context.Id_extendedGraphemeClusterLiteral}); - builtinLiteralFuncName - = DeclName(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_builtinExtendedGraphemeClusterLiteral, - tc.Context.getIdentifier("utf8CodeUnitCount"), - tc.Context.getIdentifier("isASCII") }); - - builtinProtocol = tc.getProtocol( - expr->getLoc(), - KnownProtocolKind::ExpressibleByBuiltinExtendedGraphemeClusterLiteral); + literalType = ctx.Id_ExtendedGraphemeClusterLiteralType; + literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_extendedGraphemeClusterLiteral}); + builtinLiteralFuncName = + DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinExtendedGraphemeClusterLiteral, + ctx.getIdentifier("utf8CodeUnitCount"), + ctx.getIdentifier("isASCII")}); + + builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind:: + ExpressibleByBuiltinExtendedGraphemeClusterLiteral); brokenProtocolDiag = diag::extended_grapheme_cluster_literal_broken_proto; brokenBuiltinProtocolDiag = diag::builtin_extended_grapheme_cluster_literal_broken_proto; } else { // Otherwise, we should have just one Unicode scalar. - literalType = tc.Context.Id_UnicodeScalarLiteralType; + literalType = ctx.Id_UnicodeScalarLiteralType; - literalFuncName - = DeclName(tc.Context, DeclBaseName::createConstructor(), - {tc.Context.Id_unicodeScalarLiteral}); - builtinLiteralFuncName - = DeclName(tc.Context, DeclBaseName::createConstructor(), - {tc.Context.Id_builtinUnicodeScalarLiteral}); + literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_unicodeScalarLiteral}); + builtinLiteralFuncName = + DeclName(ctx, DeclBaseName::createConstructor(), + {ctx.Id_builtinUnicodeScalarLiteral}); - builtinProtocol = tc.getProtocol( - expr->getLoc(), + builtinProtocol = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), KnownProtocolKind::ExpressibleByBuiltinUnicodeScalarLiteral); brokenProtocolDiag = diag::unicode_scalar_literal_broken_proto; @@ -2100,14 +2106,13 @@ namespace { auto type = simplifyType(openedType); cs.setType(expr, type); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); auto loc = expr->getStartLoc(); auto fetchProtocolInitWitness = - [&](KnownProtocolKind protocolKind, Type type, - ArrayRef argLabels) -> ConcreteDeclRef { - - auto proto = tc.getProtocol(loc, protocolKind); + [&](KnownProtocolKind protocolKind, Type type, + ArrayRef argLabels) -> ConcreteDeclRef { + auto proto = TypeChecker::getProtocol(ctx, loc, protocolKind); assert(proto && "Missing string interpolation protocol?"); auto conformance = @@ -2115,22 +2120,23 @@ namespace { ConformanceCheckFlags::InExpression); assert(conformance && "string interpolation type conforms to protocol"); - DeclName constrName(tc.Context, DeclBaseName::createConstructor(), argLabels); + DeclName constrName(ctx, DeclBaseName::createConstructor(), argLabels); ConcreteDeclRef witness = - conformance->getWitnessByName(type->getRValueType(), constrName); + conformance.getWitnessByName(type->getRValueType(), constrName); if (!witness || !isa(witness.getDecl())) return nullptr; return witness; }; - auto *interpolationProto = - tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByStringInterpolation); - auto associatedTypeDecl = interpolationProto->getAssociatedType( - tc.Context.Id_StringInterpolation); + auto *interpolationProto = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByStringInterpolation); + auto associatedTypeDecl = + interpolationProto->getAssociatedType(ctx.Id_StringInterpolation); if (associatedTypeDecl == nullptr) { - tc.diagnose(expr->getStartLoc(), diag::interpolation_broken_proto); + ctx.Diags.diagnose(expr->getStartLoc(), + diag::interpolation_broken_proto); return nullptr; } auto interpolationType = @@ -2139,21 +2145,20 @@ namespace { // Fetch needed witnesses. ConcreteDeclRef builderInit = fetchProtocolInitWitness( KnownProtocolKind::StringInterpolationProtocol, interpolationType, - { tc.Context.Id_literalCapacity, tc.Context.Id_interpolationCount }); + {ctx.Id_literalCapacity, ctx.Id_interpolationCount}); if (!builderInit) return nullptr; expr->setBuilderInit(builderInit); ConcreteDeclRef resultInit = fetchProtocolInitWitness( KnownProtocolKind::ExpressibleByStringInterpolation, type, - { tc.Context.Id_stringInterpolation }); + {ctx.Id_stringInterpolation}); if (!resultInit) return nullptr; expr->setResultInit(resultInit); // Make the integer literals for the parameters. auto buildExprFromUnsigned = [&](unsigned value) { - LiteralExpr *expr = - IntegerLiteralExpr::createFromUnsigned(tc.Context, value); - cs.setType(expr, tc.getIntType(cs.DC)); + LiteralExpr *expr = IntegerLiteralExpr::createFromUnsigned(ctx, value); + cs.setType(expr, TypeChecker::getIntType(ctx)); return handleIntegerLiteralExpr(expr); }; @@ -2164,8 +2169,8 @@ namespace { // This OpaqueValueExpr represents the result of builderInit above in // silgen. - OpaqueValueExpr *interpolationExpr = new (tc.Context) - OpaqueValueExpr(expr->getSourceRange(), interpolationType); + OpaqueValueExpr *interpolationExpr = + new (ctx) OpaqueValueExpr(expr->getSourceRange(), interpolationType); cs.setType(interpolationExpr, interpolationType); expr->setInterpolationExpr(interpolationExpr); @@ -2178,6 +2183,7 @@ namespace { Expr *visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) { switch (expr->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: return handleStringLiteralExpr(expr); @@ -2197,7 +2203,7 @@ namespace { if (cs.getType(expr) && !cs.getType(expr)->hasTypeVariable()) return expr; - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // Figure out the type we're converting to. auto openedType = cs.getType(expr); @@ -2214,18 +2220,17 @@ namespace { } // Find the appropriate object literal protocol. - auto proto = tc.getLiteralProtocol(expr); + auto proto = TypeChecker::getLiteralProtocol(cs.getASTContext(), expr); assert(proto && "Missing object literal protocol?"); auto conformance = TypeChecker::conformsToProtocol(conformingType, proto, cs.DC, ConformanceCheckFlags::InExpression); assert(conformance && "object literal type conforms to protocol"); - DeclName constrName(tc.getObjectLiteralConstructorName(expr)); + auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr); - ConcreteDeclRef witness = - conformance->getWitnessByName(conformingType->getRValueType(), - constrName); + ConcreteDeclRef witness = conformance.getWitnessByName( + conformingType->getRValueType(), constrName); if (!witness || !isa(witness.getDecl())) return nullptr; expr->setInitializer(witness); @@ -2251,7 +2256,7 @@ namespace { if (auto *fnTy = ty->getAs()) { auto underlyingType = cs.replaceFinalResultTypeWithUnderlying(fnTy); - auto &ctx = cs.getTypeChecker().Context; + auto &ctx = cs.getASTContext(); return cs.cacheType(new (ctx) ImplicitlyUnwrappedFunctionConversionExpr( expr, underlyingType)); } else { @@ -2294,10 +2299,8 @@ namespace { return expr; } - return buildDeclRef(selected->choice, expr->getNameLoc(), - selected->openedFullType, locator, expr->isImplicit(), - expr->getFunctionRefKind(), - expr->getAccessSemantics()); + return buildDeclRef(*selected, expr->getNameLoc(), locator, + expr->isImplicit(), expr->getAccessSemantics()); } Expr *visitSuperRefExpr(SuperRefExpr *expr) { @@ -2325,12 +2328,9 @@ namespace { // Determine the declaration selected for this overloaded reference. auto locator = cs.getConstraintLocator(expr); auto selected = solution.getOverloadChoice(locator); - auto choice = selected.choice; - return buildDeclRef(choice, expr->getNameLoc(), selected.openedFullType, - locator, expr->isImplicit(), - choice.getFunctionRefKind(), - AccessSemantics::Ordinary); + return buildDeclRef(selected, expr->getNameLoc(), locator, + expr->isImplicit(), AccessSemantics::Ordinary); } Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) { @@ -2348,14 +2348,10 @@ namespace { auto memberLocator = cs.getConstraintLocator(expr, ConstraintLocator::Member); auto selected = solution.getOverloadChoice(memberLocator); - bool isDynamic - = selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic; return buildMemberRef( - expr->getBase(), selected.openedFullType, expr->getDotLoc(), - selected.choice, expr->getNameLoc(), selected.openedType, + expr->getBase(), expr->getDotLoc(), selected, expr->getNameLoc(), cs.getConstraintLocator(expr), memberLocator, expr->isImplicit(), - selected.choice.getFunctionRefKind(), expr->getAccessSemantics(), - isDynamic); + expr->getAccessSemantics()); } Expr *visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) { @@ -2374,7 +2370,7 @@ namespace { } Type baseTy = resultTy->getRValueType(); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // Find the selected member. auto memberLocator = cs.getConstraintLocator( @@ -2391,19 +2387,14 @@ namespace { // The base expression is simply the metatype of the base type. // FIXME: This location info is bogus. - auto base = TypeExpr::createImplicitHack(expr->getDotLoc(), baseTy, - tc.Context); + auto base = TypeExpr::createImplicitHack(expr->getDotLoc(), baseTy, ctx); cs.cacheExprTypes(base); // Build the member reference. - bool isDynamic - = selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic; + auto *exprLoc = cs.getConstraintLocator(expr); auto result = buildMemberRef( - base, selected.openedFullType, expr->getDotLoc(), selected.choice, - expr->getNameLoc(), selected.openedType, - cs.getConstraintLocator(expr), memberLocator, expr->isImplicit(), - selected.choice.getFunctionRefKind(), AccessSemantics::Ordinary, - isDynamic); + base, expr->getDotLoc(), selected, expr->getNameLoc(), exprLoc, + memberLocator, expr->isImplicit(), AccessSemantics::Ordinary); if (!result) return nullptr; @@ -2413,11 +2404,16 @@ namespace { // If there was an argument, apply it. if (auto arg = expr->getArgument()) { + // Get the callee locator. Note this may be different to the locator for + // the member being referenced for things like callAsFunction. + auto *calleeLoc = cs.getCalleeLocator(exprLoc); + + // Build and finish the apply. ApplyExpr *apply = CallExpr::create( - tc.Context, result, arg, expr->getArgumentLabels(), + ctx, result, arg, expr->getArgumentLabels(), expr->getArgumentLabelLocs(), expr->hasTrailingClosure(), /*implicit=*/expr->isImplicit(), Type(), getType); - result = finishApply(apply, Type(), cs.getConstraintLocator(expr)); + result = finishApply(apply, Type(), exprLoc, calleeLoc); // FIXME: Application could fail, because some of the solutions // are not expressible in AST (yet?), like certain tuple-to-tuple @@ -2470,11 +2466,9 @@ namespace { // as a member auto baseTyNominalDecl = baseTyUnwrapped ->getNominalOrBoundGenericNominal(); - auto &tc = cs.getTypeChecker(); - auto results = tc.lookupMember(baseTyNominalDecl->getModuleContext(), - baseTyUnwrapped, - memberName, - defaultMemberLookupOptions); + auto results = TypeChecker::lookupMember( + baseTyNominalDecl->getModuleContext(), baseTyUnwrapped, + DeclNameRef(memberName), defaultMemberLookupOptions); // Filter out any functions, instance members, enum cases with // associated values or variables whose type does not match the @@ -2503,21 +2497,22 @@ namespace { if (results.empty()) { return; } - + + auto &de = cs.getASTContext().Diags; if (auto member = results.front().getValueDecl()) { // Emit a diagnostic with some fix-its auto baseTyName = baseTy->getCanonicalType().getString(); auto baseTyUnwrappedName = baseTyUnwrapped->getString(); auto loc = DSCE->getLoc(); auto startLoc = DSCE->getStartLoc(); - tc.diagnoseWithNotes( - tc.diagnose(loc, swift::diag::optional_ambiguous_case_ref, + de.diagnoseWithNotes( + de.diagnose(loc, swift::diag::optional_ambiguous_case_ref, baseTyName, baseTyUnwrappedName, memberName.str()), [&]() { - tc.diagnose(loc, + de.diagnose(loc, swift::diag::optional_fixit_ambiguous_case_ref) .fixItInsert(startLoc, "Optional"); - tc.diagnose( + de.diagnose( loc, swift::diag::type_fixit_optional_ambiguous_case_ref, baseTyUnwrappedName, memberName.str()) @@ -2533,34 +2528,28 @@ namespace { /// forced downcasts. SmallVector SuspiciousOptionalInjections; - public: - /// A list of optional injections that have been diagnosed. - llvm::SmallPtrSet DiagnosedOptionalInjections; - private: /// Create a member reference to the given constructor. Expr *applyCtorRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc, DeclNameLoc nameLoc, bool implicit, ConstraintLocator *ctorLocator, - OverloadChoice choice, - FunctionRefKind functionRefKind, Type openedType) { - + SelectedOverload overload) { + auto choice = overload.choice; + assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic); auto *ctor = cast(choice.getDecl()); // If the subexpression is a metatype, build a direct reference to the // constructor. if (cs.getType(base)->is()) { return buildMemberRef( - base, openedType, dotLoc, choice, nameLoc, cs.getType(expr), - ConstraintLocatorBuilder(cs.getConstraintLocator(expr)), - ctorLocator, implicit, functionRefKind, AccessSemantics::Ordinary, - /*isDynamic=*/false); + base, dotLoc, overload, nameLoc, cs.getConstraintLocator(expr), + ctorLocator, implicit, AccessSemantics::Ordinary); } // The subexpression must be either 'self' or 'super'. if (!base->isSuperExpr()) { // 'super' references have already been fully checked; handle the // 'self' case below. - auto &tc = cs.getTypeChecker(); + auto &de = cs.getASTContext().Diags; bool diagnoseBadInitRef = true; auto arg = base->getSemanticsProvidingExpr(); if (auto dre = dyn_cast(arg)) { @@ -2571,7 +2560,7 @@ namespace { if (!dyn_cast_or_null( cs.DC->getInnermostMethodContext())) { if (!SuppressDiagnostics) - tc.diagnose(dotLoc, diag::init_delegation_outside_initializer); + de.diagnose(dotLoc, diag::init_delegation_outside_initializer); return nullptr; } } @@ -2591,19 +2580,20 @@ namespace { if (SuppressDiagnostics) return nullptr; - tc.diagnose(dotLoc, diag::bad_init_ref_base, hasSuper); + de.diagnose(dotLoc, diag::bad_init_ref_base, hasSuper); } } // Build a partial application of the delegated initializer. - Expr *ctorRef = buildOtherConstructorRef(openedType, ctor, base, nameLoc, - ctorLocator, implicit); + auto callee = resolveConcreteDeclRef(ctor, ctorLocator); + Expr *ctorRef = buildOtherConstructorRef(overload.openedFullType, callee, + base, nameLoc, ctorLocator, + implicit); auto *call = new (cs.getASTContext()) DotSyntaxCallExpr(ctorRef, dotLoc, base); - return finishApply(call, cs.getType(expr), - ConstraintLocatorBuilder( - cs.getConstraintLocator(expr))); + return finishApply(call, cs.getType(expr), cs.getConstraintLocator(expr), + ctorLocator); } Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc, @@ -2613,10 +2603,8 @@ namespace { expr, ConstraintLocator::ConstructorMember); if (auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator)) { - auto choice = selected->choice; return applyCtorRefExpr( - expr, base, dotLoc, nameLoc, implicit, ctorLocator, choice, - choice.getFunctionRefKind(), selected->openedFullType); + expr, base, dotLoc, nameLoc, implicit, ctorLocator, *selected); } // Determine the declaration selected for this overloaded reference. @@ -2644,9 +2632,7 @@ namespace { cs.diagnoseDeprecatedConditionalConformanceOuterAccess( UDE, selected.choice.getDecl()); - return buildDeclRef(selected.choice, nameLoc, selected.openedFullType, - memberLocator, implicit, - selected.choice.getFunctionRefKind(), + return buildDeclRef(selected, nameLoc, memberLocator, implicit, AccessSemantics::Ordinary); } @@ -2656,16 +2642,14 @@ namespace { // Look through an implicitly unwrapped optional. auto baseTy = cs.getType(base); - auto &tc = cs.getTypeChecker(); - auto &ctx = tc.Context; + auto &ctx = cs.getASTContext(); auto baseMetaTy = baseTy->getAs(); auto baseInstTy = (baseMetaTy ? baseMetaTy->getInstanceType() : baseTy); auto classTy = ctx.getBridgedToObjC(cs.DC, baseInstTy); if (baseMetaTy) { // FIXME: We're dropping side effects in the base here! - base = TypeExpr::createImplicitHack(base->getLoc(), classTy, - tc.Context); + base = TypeExpr::createImplicitHack(base->getLoc(), classTy, ctx); cs.cacheExprTypes(base); } else { // Bridge the base to its corresponding Objective-C object. @@ -2678,15 +2662,10 @@ namespace { case OverloadChoiceKind::Decl: case OverloadChoiceKind::DeclViaUnwrappedOptional: - case OverloadChoiceKind::DeclViaDynamic: { - bool isDynamic - = selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic; - return buildMemberRef(base, selected.openedFullType, dotLoc, - selected.choice, nameLoc, selected.openedType, + case OverloadChoiceKind::DeclViaDynamic: + return buildMemberRef(base, dotLoc, selected, nameLoc, cs.getConstraintLocator(expr), memberLocator, - implicit, selected.choice.getFunctionRefKind(), - AccessSemantics::Ordinary, isDynamic); - } + implicit, AccessSemantics::Ordinary); case OverloadChoiceKind::TupleIndex: { Type toType = simplifyType(cs.getType(expr)); @@ -2732,6 +2711,18 @@ namespace { llvm_unreachable("Unhandled OverloadChoiceKind in switch."); } + /// Form a type checked expression for the index of a @dynamicMemberLookup + /// subscript index parameter. + Expr *buildDynamicMemberLookupIndexExpr(StringRef name, SourceLoc loc, + Type literalTy) { + // Build and type check the string literal index value to the specific + // string type expected by the subscript. + auto &ctx = cs.getASTContext(); + auto *nameExpr = new (ctx) StringLiteralExpr(name, loc, /*implicit*/true); + cs.setType(nameExpr, literalTy); + return handleStringLiteralExpr(nameExpr); + } + Expr *buildDynamicMemberLookupRef(Expr *expr, Expr *base, SourceLoc dotLoc, SourceLoc nameLoc, const SelectedOverload &overload, @@ -2752,7 +2743,8 @@ namespace { // Build and type check the string literal index value to the specific // string type expected by the subscript. auto fieldName = overload.choice.getName().getBaseIdentifier().str(); - argExpr = buildDynamicMemberLookupIndexExpr(fieldName, nameLoc, dc, cs); + argExpr = buildDynamicMemberLookupIndexExpr(fieldName, nameLoc, + paramTy); } else { argExpr = buildKeyPathDynamicMemberIndexExpr( paramTy->castTo(), dotLoc, memberLocator); @@ -2825,7 +2817,7 @@ namespace { // // The result is that in Swift 5, 'try?' avoids producing nested optionals. - if (!cs.getTypeChecker().getLangOpts().isSwiftVersionAtLeast(5)) { + if (!cs.getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { // Nothing to do for Swift 4 and earlier! return simplifyExprType(expr); } @@ -2854,8 +2846,29 @@ namespace { cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); auto overload = solution.getOverloadChoiceIfAvailable(memberLocator); - if (overload && overload->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup) { + // Handles situation where there was a solution available but it didn't + // have a proper overload selected from subscript call, might be because + // solver was allowed to return free or unresolved types, which can + // happen while running diagnostics on one of the expressions. + if (!overload) { + const auto *base = expr->getBase(); + auto &de = cs.getASTContext().Diags; + auto baseType = cs.getType(base); + + if (auto errorType = baseType->getAs()) { + de.diagnose(base->getLoc(), diag::cannot_subscript_base, + errorType->getOriginalType()) + .highlight(base->getSourceRange()); + } else { + de.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base) + .highlight(base->getSourceRange()); + } + + return nullptr; + } + + if (overload->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup) { return buildDynamicMemberLookupRef( expr, expr->getBase(), expr->getIndex()->getStartLoc(), SourceLoc(), *overload, memberLocator); @@ -2864,17 +2877,16 @@ namespace { return buildSubscript( expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), expr->getAccessSemantics(), overload); + expr->isImplicit(), expr->getAccessSemantics(), *overload); } /// "Finish" an array expression by filling in the semantic expression. ArrayExpr *finishArrayExpr(ArrayExpr *expr) { Type arrayTy = cs.getType(expr); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); - ProtocolDecl *arrayProto - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByArrayLiteral); + ProtocolDecl *arrayProto = TypeChecker::getProtocol( + ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByArrayLiteral); assert(arrayProto && "type-checked array literal w/o protocol?!"); auto conformance = @@ -2882,10 +2894,10 @@ namespace { ConformanceCheckFlags::InExpression); assert(conformance && "Type does not conform to protocol?"); - DeclName name(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_arrayLiteral }); + DeclName name(ctx, DeclBaseName::createConstructor(), + {ctx.Id_arrayLiteral}); ConcreteDeclRef witness = - conformance->getWitnessByName(arrayTy->getRValueType(), name); + conformance.getWitnessByName(arrayTy->getRValueType(), name); if (!witness || !isa(witness.getDecl())) return nullptr; expr->setInitializer(witness); @@ -2917,21 +2929,21 @@ namespace { DictionaryExpr *finishDictionaryExpr(DictionaryExpr *expr) { Type dictionaryTy = cs.getType(expr); - auto &tc = cs.getTypeChecker(); - ProtocolDecl *dictionaryProto - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByDictionaryLiteral); + auto &ctx = cs.getASTContext(); + ProtocolDecl *dictionaryProto = TypeChecker::getProtocol( + cs.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByDictionaryLiteral); auto conformance = TypeChecker::conformsToProtocol(dictionaryTy, dictionaryProto, cs.DC, ConformanceCheckFlags::InExpression); - if (!conformance) + if (conformance.isInvalid()) return nullptr; - DeclName name(tc.Context, DeclBaseName::createConstructor(), - { tc.Context.Id_dictionaryLiteral }); + DeclName name(ctx, DeclBaseName::createConstructor(), + {ctx.Id_dictionaryLiteral}); ConcreteDeclRef witness = - conformance->getWitnessByName(dictionaryTy->getRValueType(), name); + conformance.getWitnessByName(dictionaryTy->getRValueType(), name); if (!witness || !isa(witness.getDecl())) return nullptr; expr->setInitializer(witness); @@ -2960,11 +2972,14 @@ namespace { } Expr *visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) { + auto *memberLocator = + cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember); return buildSubscript(expr->getBase(), expr->getIndex(), expr->getArgumentLabels(), expr->hasTrailingClosure(), cs.getConstraintLocator(expr), - expr->isImplicit(), AccessSemantics::Ordinary); + expr->isImplicit(), AccessSemantics::Ordinary, + solution.getOverloadChoice(memberLocator)); } Expr *visitTupleElementExpr(TupleElementExpr *expr) { @@ -3021,14 +3036,11 @@ namespace { llvm_unreachable("Already type-checked"); } - Expr *visitCallerDefaultArgumentExpr(CallerDefaultArgumentExpr *expr) { - llvm_unreachable("Already type-checked"); - } - Expr *visitApplyExpr(ApplyExpr *expr) { - return finishApply(expr, cs.getType(expr), - ConstraintLocatorBuilder( - cs.getConstraintLocator(expr))); + auto *calleeLoc = CalleeLocators[expr]; + assert(calleeLoc); + return finishApply(expr, cs.getType(expr), cs.getConstraintLocator(expr), + calleeLoc); } Expr *visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) { @@ -3051,29 +3063,29 @@ namespace { if (isError && SuppressDiagnostics) return nullptr; - auto &tc = cs.getTypeChecker(); - auto &ctx = tc.Context; + auto &ctx = cs.getASTContext(); + auto &de = cs.getASTContext().Diags; if (isError) { if (auto *optTry = dyn_cast(unwrappedSubExpr)) { - tc.diagnose(optTry->getTryLoc(), + de.diagnose(optTry->getTryLoc(), diag::delegate_chain_nonoptional_to_optional_try, isChaining); - tc.diagnose(optTry->getTryLoc(), diag::init_delegate_force_try) - .fixItReplace({optTry->getTryLoc(), optTry->getQuestionLoc()}, - "try!"); - tc.diagnose(inCtor->getLoc(), diag::init_propagate_failure) - .fixItInsertAfter(inCtor->getLoc(), "?"); + de.diagnose(optTry->getTryLoc(), diag::init_delegate_force_try) + .fixItReplace({optTry->getTryLoc(), optTry->getQuestionLoc()}, + "try!"); + de.diagnose(inCtor->getLoc(), diag::init_propagate_failure) + .fixItInsertAfter(inCtor->getLoc(), "?"); } else { // Give the user the option of adding '!' or making the enclosing // initializer failable. - tc.diagnose(otherCtorRef->getLoc(), + de.diagnose(otherCtorRef->getLoc(), diag::delegate_chain_nonoptional_to_optional, isChaining, ctor->getFullName()); - tc.diagnose(otherCtorRef->getLoc(), diag::init_force_unwrap) - .fixItInsertAfter(expr->getEndLoc(), "!"); - tc.diagnose(inCtor->getLoc(), diag::init_propagate_failure) - .fixItInsertAfter(inCtor->getLoc(), "?"); + de.diagnose(otherCtorRef->getLoc(), diag::init_force_unwrap) + .fixItInsertAfter(expr->getEndLoc(), "!"); + de.diagnose(inCtor->getLoc(), diag::init_propagate_failure) + .fixItInsertAfter(inCtor->getLoc(), "?"); } } @@ -3110,7 +3122,7 @@ namespace { Expr *visitIsExpr(IsExpr *expr) { // Turn the subexpression into an rvalue. - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); auto toType = simplifyType(cs.getType(expr->getCastTypeLoc())); auto sub = cs.coerceToRValue(expr->getSubExpr()); @@ -3122,10 +3134,9 @@ namespace { auto castContextKind = SuppressDiagnostics ? CheckedCastContextKind::None : CheckedCastContextKind::IsExpr; - auto castKind = tc.typeCheckCheckedCast( - fromType, toType, castContextKind, cs.DC, - expr->getLoc(), sub, - expr->getCastTypeLoc().getSourceRange()); + auto castKind = TypeChecker::typeCheckCheckedCast( + fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, + expr->getCastTypeLoc().getSourceRange()); switch (castKind) { case CheckedCastKind::Unresolved: @@ -3135,7 +3146,7 @@ namespace { case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: // Check is trivially true. - tc.diagnose(expr->getLoc(), diag::isa_is_always_true, "is"); + ctx.Diags.diagnose(expr->getLoc(), diag::isa_is_always_true, "is"); expr->setCastKind(castKind); break; case CheckedCastKind::ValueCast: @@ -3143,7 +3154,8 @@ namespace { if (auto cls = toType->getAs()) { if (cls->getDecl()->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { - tc.diagnose(expr->getLoc(), diag::isa_is_foreign_check, toType); + ctx.Diags.diagnose(expr->getLoc(), diag::isa_is_foreign_check, + toType); } } expr->setCastKind(castKind); @@ -3157,8 +3169,8 @@ namespace { } // SIL-generation magically turns this into a Bool; make sure it can. - if (!tc.Context.getBoolBuiltinInitDecl()) { - tc.diagnose(expr->getLoc(), diag::broken_bool); + if (!ctx.getBoolBuiltinInitDecl()) { + ctx.Diags.diagnose(expr->getLoc(), diag::broken_bool); // Continue anyway. } @@ -3176,10 +3188,8 @@ namespace { castKind == CheckedCastKind::DictionaryDowncast || castKind == CheckedCastKind::SetDowncast) { auto toOptType = OptionalType::get(toType); - ConditionalCheckedCastExpr *cast - = new (tc.Context) ConditionalCheckedCastExpr( - sub, expr->getLoc(), SourceLoc(), - TypeLoc::withoutLoc(toType)); + ConditionalCheckedCastExpr *cast = new (ctx) ConditionalCheckedCastExpr( + sub, expr->getLoc(), SourceLoc(), expr->getCastTypeLoc()); cs.setType(cast, toOptType); cs.setType(cast->getCastTypeLoc(), toType); if (expr->isImplicit()) @@ -3191,16 +3201,15 @@ namespace { return nullptr; // Extract a Bool from the resulting expression. - tc.requireOptionalIntrinsics(expr->getLoc()); + TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc()); // Match the optional value against its `Some` case. - auto &ctx = tc.Context; - auto *someDecl = tc.Context.getOptionalSomeDecl(); - auto isSomeExpr = new (tc.Context) EnumIsCaseExpr(result, someDecl); + auto *someDecl = ctx.getOptionalSomeDecl(); + auto isSomeExpr = new (ctx) EnumIsCaseExpr(result, someDecl); auto boolDecl = ctx.getBoolDecl(); if (!boolDecl) { - tc.diagnose(SourceLoc(), diag::broken_bool); + ctx.Diags.diagnose(SourceLoc(), diag::broken_bool); } cs.setType(isSomeExpr, boolDecl ? boolDecl->getDeclaredType() : Type()); @@ -3241,12 +3250,13 @@ namespace { if (SuppressDiagnostics) return nullptr; - auto &tc = cs.getTypeChecker(); - tc.diagnose(cast->getLoc(), diag::conditional_downcast_foreign, + auto &de = cs.getASTContext().Diags; + de.diagnose(cast->getLoc(), diag::conditional_downcast_foreign, destValueType); ConcreteDeclRef refDecl = sub->getReferencedDecl(); if (refDecl) { - tc.diagnose(cast->getLoc(), diag::note_explicitly_compare_cftypeid, + de.diagnose(cast->getLoc(), + diag::note_explicitly_compare_cftypeid, refDecl.getDecl()->getBaseName(), destValueType); } } @@ -3271,7 +3281,7 @@ namespace { Expr *handleOptionalBindings(Expr *subExpr, Type finalResultType, OptionalBindingsCastKind castKind, OperationBuilderRef buildInnerOperation) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); unsigned destExtraOptionals; bool forceExtraSourceOptionals; @@ -3314,7 +3324,7 @@ namespace { // be an optional type, leave any extra optionals on the source in place. // Only apply the latter condition in Swift 5 mode to best preserve // compatibility with Swift 4.1's casting behaviour. - if (isBridgeToAnyObject || (tc.Context.isSwiftVersionAtLeast(5) && + if (isBridgeToAnyObject || (ctx.isSwiftVersionAtLeast(5) && destValueType->canDynamicallyBeOptionalType( /*includeExistential*/ false))) { auto destOptionalsCount = destOptionals.size() - destExtraOptionals; @@ -3346,8 +3356,7 @@ namespace { auto addFinalOptionalInjections = [&](Expr *result) { for (auto destType : llvm::reverse(destOptionalInjections)) { result = - cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(result, - destType)); + cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType)); } return result; @@ -3390,16 +3399,14 @@ namespace { depth -= (i - numRequiredOptionals) + 1; } else if (forceExtraSourceOptionals) { // For a forced cast, force the required optionals. - subExpr = new (tc.Context) ForceValueExpr(subExpr, fakeQuestionLoc); + subExpr = new (ctx) ForceValueExpr(subExpr, fakeQuestionLoc); cs.setType(subExpr, valueType); subExpr->setImplicit(true); continue; } - subExpr = - cs.cacheType(new (tc.Context) BindOptionalExpr(subExpr, - fakeQuestionLoc, - depth, valueType)); + subExpr = cs.cacheType(new (ctx) BindOptionalExpr( + subExpr, fakeQuestionLoc, depth, valueType)); subExpr->setImplicit(true); } @@ -3424,29 +3431,23 @@ namespace { // If the innermost cast fails, the entire expression fails. To // get this behavior, we have to bind and then re-inject the result. // (SILGen should know how to peephole this.) - result = - cs.cacheType(new (tc.Context) BindOptionalExpr(result, - result->getEndLoc(), - failureDepth, - destValueType)); + result = cs.cacheType(new (ctx) BindOptionalExpr( + result, result->getEndLoc(), failureDepth, destValueType)); result->setImplicit(true); } for (unsigned i = destOptionals.size(); i != 0; --i) { Type destType = destOptionals[i-1]; result = - cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(result, - destType)); + cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType)); result = - cs.cacheType(new (tc.Context) OptionalEvaluationExpr(result, - destType)); + cs.cacheType(new (ctx) OptionalEvaluationExpr(result, destType)); } // Otherwise, we just need to capture the failure-depth binding. } else if (!forceExtraSourceOptionals) { - result = - cs.cacheType(new (tc.Context) OptionalEvaluationExpr(result, - finalResultType)); + result = cs.cacheType( + new (ctx) OptionalEvaluationExpr(result, finalResultType)); } return addFinalOptionalInjections(result); @@ -3482,7 +3483,7 @@ namespace { auto toType = simplifyType(cs.getType(expr->getCastTypeLoc())); expr->getCastTypeLoc().setType(toType); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // If this is a literal that got converted into constructor call // lets put proper source information in place. @@ -3503,8 +3504,7 @@ namespace { if (!type->isEqual(toType)) return subExpr; - return cs.cacheType(new (tc.Context) - TypeExpr(expr->getCastTypeLoc())); + return cs.cacheType(new (ctx) TypeExpr(expr->getCastTypeLoc())); }); } @@ -3539,12 +3539,9 @@ namespace { // Convert the subexpression. Expr *sub = expr->getSubExpr(); - cs.setExprTypes(sub); - - if (tc.convertToType(sub, toType, cs.DC)) + sub = solution.coerceToType(sub, toType, cs.getConstraintLocator(sub)); + if (!sub) return nullptr; - - cs.cacheExprTypes(sub); expr->setSubExpr(sub); cs.setType(expr, toType); @@ -3577,7 +3574,7 @@ namespace { expr->getCastTypeLoc().setType(toType); // The subexpression is always an rvalue. - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); auto sub = cs.coerceToRValue(expr->getSubExpr()); expr->setSubExpr(sub); @@ -3586,39 +3583,31 @@ namespace { : CheckedCastContextKind::ForcedCast; auto fromType = cs.getType(sub); - auto castKind = tc.typeCheckCheckedCast( - fromType, toType, castContextKind, cs.DC, - expr->getLoc(), sub, - expr->getCastTypeLoc().getSourceRange()); + auto castKind = TypeChecker::typeCheckCheckedCast( + fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, + expr->getCastTypeLoc().getSourceRange()); switch (castKind) { /// Invalid cast. case CheckedCastKind::Unresolved: return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - if (cs.getType(sub)->isEqual(toType)) { - tc.diagnose(expr->getLoc(), diag::forced_downcast_noop, toType) - .fixItRemove(SourceRange(expr->getLoc(), - expr->getCastTypeLoc().getSourceRange().End)); + ctx.Diags.diagnose(expr->getLoc(), diag::forced_downcast_noop, toType) + .fixItRemove(SourceRange( + expr->getLoc(), expr->getCastTypeLoc().getSourceRange().End)); } else { - tc.diagnose(expr->getLoc(), diag::forced_downcast_coercion, - cs.getType(sub), toType) - .fixItReplace(SourceRange(expr->getLoc(), expr->getExclaimLoc()), - "as"); + ctx.Diags + .diagnose(expr->getLoc(), diag::forced_downcast_coercion, + cs.getType(sub), toType) + .fixItReplace(SourceRange(expr->getLoc(), expr->getExclaimLoc()), + "as"); } - // Transmute the checked cast into a coercion expression. - auto *result = new (tc.Context) CoerceExpr(sub, expr->getLoc(), - expr->getCastTypeLoc()); - cs.setType(result, toType); - cs.setType(result->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - return visitCoerceExpr(result, disjunctionChoice); + expr->setCastKind(castKind); + cs.setType(expr, toType); + return expr; } // Valid casts. @@ -3656,7 +3645,7 @@ namespace { expr->getCastTypeLoc().setType(toType); // The subexpression is always an rvalue. - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); auto sub = cs.coerceToRValue(expr->getSubExpr()); expr->setSubExpr(sub); @@ -3667,43 +3656,22 @@ namespace { : CheckedCastContextKind::ConditionalCast; auto fromType = cs.getType(sub); - auto castKind = tc.typeCheckCheckedCast( - fromType, toType, castContextKind, cs.DC, - expr->getLoc(), sub, - expr->getCastTypeLoc().getSourceRange()); + auto castKind = TypeChecker::typeCheckCheckedCast( + fromType, toType, castContextKind, cs.DC, expr->getLoc(), sub, + expr->getCastTypeLoc().getSourceRange()); switch (castKind) { - /// Invalid cast. + // Invalid cast. case CheckedCastKind::Unresolved: expr->setCastKind(CheckedCastKind::ValueCast); break; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: { - if (SuppressDiagnostics) - return nullptr; - - tc.diagnose(expr->getLoc(), diag::conditional_downcast_coercion, - cs.getType(sub), toType); - - // Transmute the checked cast into a coercion expression. - auto *coerce = new (tc.Context) CoerceExpr(sub, expr->getLoc(), - expr->getCastTypeLoc()); - cs.setType(coerce, toType); - cs.setType(coerce->getCastTypeLoc(), toType); - unsigned disjunctionChoice = - (castKind == CheckedCastKind::Coercion ? 0 : 1); - Expr *result = visitCoerceExpr(coerce, disjunctionChoice); - if (!result) - return nullptr; - - // Wrap the result in an optional. Mark the optional injection as - // explicit, because the user did in fact write the '?' as part of - // 'as?', even though it wasn't necessary. - result = new (tc.Context) InjectIntoOptionalExpr( - result, - OptionalType::get(toType)); - result->setImplicit(false); - return cs.cacheType(result); + ctx.Diags.diagnose(expr->getLoc(), diag::conditional_downcast_coercion, + cs.getType(sub), toType); + expr->setCastKind(castKind); + cs.setType(expr, OptionalType::get(toType)); + return expr; } // Valid casts. @@ -3734,7 +3702,7 @@ namespace { // If we're performing an assignment to a weak or unowned variable from // a constructor call, emit a warning that the instance will be // immediately deallocated. - diagnoseUnownedImmediateDeallocation(cs.getTypeChecker(), expr); + diagnoseUnownedImmediateDeallocation(cs.getASTContext(), expr); } return expr; } @@ -3749,8 +3717,9 @@ namespace { Expr *simplified = simplifyExprType(expr); if (!SuppressDiagnostics && !cs.getType(simplified)->is()) { - cs.TC.diagnose(simplified->getLoc(), diag::pattern_in_expr, - expr->getSubPattern()->getKind()); + auto &de = cs.getASTContext().Diags; + de.diagnose(simplified->getLoc(), diag::pattern_in_expr, + expr->getSubPattern()->getKind()); } return simplified; } @@ -3767,15 +3736,18 @@ namespace { // reject it. This avoids confusion of the model (where the programmer // thought it was doing something) and keeps pointless ?'s out of the // code. - if (!SuppressDiagnostics) + if (!SuppressDiagnostics) { + auto &de = cs.getASTContext().Diags; if (auto *Bind = dyn_cast( expr->getSubExpr()->getSemanticsProvidingExpr())) { - if (cs.getType(Bind->getSubExpr())->isEqual(optType)) - cs.TC.diagnose(expr->getLoc(), diag::optional_chain_noop, - optType).fixItRemove(Bind->getQuestionLoc()); - else - cs.TC.diagnose(expr->getLoc(), diag::optional_chain_isnt_chaining); + if (cs.getType(Bind->getSubExpr())->isEqual(optType)) { + de.diagnose(expr->getLoc(), diag::optional_chain_noop, optType) + .fixItRemove(Bind->getQuestionLoc()); + } else { + de.diagnose(expr->getLoc(), diag::optional_chain_isnt_chaining); + } } + } Expr *subExpr = coerceToType(expr->getSubExpr(), optType, cs.getConstraintLocator(expr)); @@ -3865,12 +3837,11 @@ namespace { if (valueType->hasUnresolvedType()) return nullptr; - auto &tc = cs.getTypeChecker(); - auto &ctx = tc.Context; + auto &ctx = cs.getASTContext(); // Synthesize a call to _undefined() of appropriate type. FuncDecl *undefinedDecl = ctx.getUndefined(); if (!undefinedDecl) { - tc.diagnose(E->getLoc(), diag::missing_undefined_runtime); + ctx.Diags.diagnose(E->getLoc(), diag::missing_undefined_runtime); return nullptr; } DeclRefExpr *fnRef = new (ctx) DeclRefExpr(undefinedDecl, DeclNameLoc(), @@ -3884,7 +3855,7 @@ namespace { Expr *callExpr = CallExpr::createImplicit(ctx, fnRef, { argExpr }, { Identifier() }); - auto resultTy = tc.typeCheckExpression( + auto resultTy = TypeChecker::typeCheckExpression( callExpr, cs.DC, TypeLoc::withoutLoc(valueType), CTP_CannotFail); assert(resultTy && "Conversion cannot fail!"); (void)resultTy; @@ -3992,10 +3963,10 @@ namespace { if (!subExpr) return nullptr; // If we didn't find any declaration at all, we're stuck. - auto &tc = cs.getTypeChecker(); + auto &de = cs.getASTContext().Diags; if (!foundDecl) { - tc.diagnose(E->getLoc(), diag::expr_selector_no_declaration) - .highlight(subExpr->getSourceRange()); + de.diagnose(E->getLoc(), diag::expr_selector_no_declaration) + .highlight(subExpr->getSourceRange()); return E; } @@ -4007,11 +3978,11 @@ namespace { // If this isn't a method, complain. if (!func->getDeclContext()->isTypeContext()) { - tc.diagnose(E->getLoc(), diag::expr_selector_not_method, + de.diagnose(E->getLoc(), diag::expr_selector_not_method, func->getDeclContext()->isModuleScopeContext(), func->getFullName()) - .highlight(subExpr->getSourceRange()); - tc.diagnose(func, diag::decl_declared_here, func->getFullName()); + .highlight(subExpr->getSourceRange()); + de.diagnose(func, diag::decl_declared_here, func->getFullName()); return E; } @@ -4024,13 +3995,12 @@ namespace { case ObjCSelectorExpr::Setter: // Complain that we cannot ask for the getter or setter of a // method. - tc.diagnose(E->getModifierLoc(), + de.diagnose(E->getModifierLoc(), diag::expr_selector_expected_property, E->getSelectorKind() == ObjCSelectorExpr::Setter, - foundDecl->getDescriptiveKind(), - foundDecl->getFullName()) - .fixItRemoveChars(E->getModifierLoc(), - E->getSubExpr()->getStartLoc()); + foundDecl->getDescriptiveKind(), foundDecl->getFullName()) + .fixItRemoveChars(E->getModifierLoc(), + E->getSubExpr()->getStartLoc()); // Update the AST to reflect the fix. E->overrideObjCSelectorKind(ObjCSelectorExpr::Method, SourceLoc()); @@ -4044,10 +4014,10 @@ namespace { // If this isn't a property on a type, complain. if (!var->getDeclContext()->isTypeContext()) { - tc.diagnose(E->getLoc(), diag::expr_selector_not_property, + de.diagnose(E->getLoc(), diag::expr_selector_not_property, isa(var), var->getFullName()) - .highlight(subExpr->getSourceRange()); - tc.diagnose(var, diag::decl_declared_here, var->getFullName()); + .highlight(subExpr->getSourceRange()); + de.diagnose(var, diag::decl_declared_here, var->getFullName()); return E; } @@ -4057,8 +4027,8 @@ namespace { bool isSettable = var->isSettable(cs.DC) && var->isSetterAccessibleFrom(cs.DC); auto primaryDiag = - tc.diagnose(E->getLoc(), diag::expr_selector_expected_method, - isSettable, var->getFullName()); + de.diagnose(E->getLoc(), diag::expr_selector_expected_method, + isSettable, var->getFullName()); primaryDiag.highlight(subExpr->getSourceRange()); // The point at which we will insert the modifier. @@ -4071,12 +4041,12 @@ namespace { primaryDiag.flush(); // Add notes for the getter and setter, respectively. - tc.diagnose(modifierLoc, diag::expr_selector_add_modifier, - false, var->getFullName()) - .fixItInsert(modifierLoc, "getter: "); - tc.diagnose(modifierLoc, diag::expr_selector_add_modifier, - true, var->getFullName()) - .fixItInsert(modifierLoc, "setter: "); + de.diagnose(modifierLoc, diag::expr_selector_add_modifier, false, + var->getFullName()) + .fixItInsert(modifierLoc, "getter: "); + de.diagnose(modifierLoc, diag::expr_selector_add_modifier, true, + var->getFullName()) + .fixItInsert(modifierLoc, "setter: "); // Bail out now. We don't know what the user wanted, so // don't fill in the details. @@ -4097,18 +4067,18 @@ namespace { case ObjCSelectorExpr::Setter: // Make sure we actually have a setter. if (!var->isSettable(cs.DC)) { - tc.diagnose(E->getLoc(), diag::expr_selector_property_not_settable, + de.diagnose(E->getLoc(), diag::expr_selector_property_not_settable, var->getDescriptiveKind(), var->getFullName()); - tc.diagnose(var, diag::decl_declared_here, var->getFullName()); + de.diagnose(var, diag::decl_declared_here, var->getFullName()); return E; } // Make sure the setter is accessible. if (!var->isSetterAccessibleFrom(cs.DC)) { - tc.diagnose(E->getLoc(), + de.diagnose(E->getLoc(), diag::expr_selector_property_setter_inaccessible, var->getDescriptiveKind(), var->getFullName()); - tc.diagnose(var, diag::decl_declared_here, var->getFullName()); + de.diagnose(var, diag::decl_declared_here, var->getFullName()); return E; } @@ -4117,9 +4087,9 @@ namespace { } } else { // Cannot reference with #selector. - tc.diagnose(E->getLoc(), diag::expr_selector_no_declaration) - .highlight(subExpr->getSourceRange()); - tc.diagnose(foundDecl, diag::decl_declared_here, + de.diagnose(E->getLoc(), diag::expr_selector_no_declaration) + .highlight(subExpr->getSourceRange()); + de.diagnose(foundDecl, diag::decl_declared_here, foundDecl->getFullName()); return E; } @@ -4133,35 +4103,34 @@ namespace { // protocol might not be the right thing to do and could lead to // problems. if (auto protocolDecl = dyn_cast(foundDecl->getDeclContext())) { - tc.diagnose(E->getLoc(), diag::expr_selector_cannot_be_used, + de.diagnose(E->getLoc(), diag::expr_selector_cannot_be_used, foundDecl->getBaseName(), protocolDecl->getFullName()); return E; } - - tc.diagnose(E->getLoc(), diag::expr_selector_not_objc, + + de.diagnose(E->getLoc(), diag::expr_selector_not_objc, foundDecl->getDescriptiveKind(), foundDecl->getFullName()) - .highlight(subExpr->getSourceRange()); - tc.diagnose(foundDecl, diag::make_decl_objc, + .highlight(subExpr->getSourceRange()); + de.diagnose(foundDecl, diag::make_decl_objc, foundDecl->getDescriptiveKind()) - .fixItInsert(foundDecl->getAttributeInsertionLoc(false), - "@objc "); + .fixItInsert(foundDecl->getAttributeInsertionLoc(false), "@objc "); return E; } else if (auto attr = foundDecl->getAttrs().getAttribute()) { // If this attribute was inferred based on deprecated Swift 3 rules, // complain. if (attr->isSwift3Inferred() && - tc.Context.LangOpts.WarnSwift3ObjCInference - == Swift3ObjCInferenceWarnings::Minimal) { - tc.diagnose(E->getLoc(), diag::expr_selector_swift3_objc_inference, + cs.getASTContext().LangOpts.WarnSwift3ObjCInference == + Swift3ObjCInferenceWarnings::Minimal) { + de.diagnose(E->getLoc(), diag::expr_selector_swift3_objc_inference, foundDecl->getDescriptiveKind(), foundDecl->getFullName(), foundDecl->getDeclContext() - ->getSelfNominalTypeDecl() - ->getName()) - .highlight(subExpr->getSourceRange()); - tc.diagnose(foundDecl, diag::make_decl_objc, + ->getSelfNominalTypeDecl() + ->getName()) + .highlight(subExpr->getSourceRange()); + de.diagnose(foundDecl, diag::make_decl_objc, foundDecl->getDescriptiveKind()) - .fixItInsert(foundDecl->getAttributeInsertionLoc(false), - "@objc "); + .fixItInsert(foundDecl->getAttributeInsertionLoc(false), + "@objc "); } } @@ -4212,13 +4181,6 @@ namespace { leafTy = keyPathTy->getGenericArgs()[1]; } - // Updates the constraint system with the type of the last resolved - // component. We do it this way because we sometimes insert new - // components. - auto updateCSWithResolvedComponent = [&]() { - cs.setType(E, resolvedComponents.size() - 1, baseTy); - }; - for (unsigned i : indices(E->getComponents())) { auto &origComponent = E->getMutableComponents()[i]; @@ -4228,28 +4190,8 @@ namespace { resolvedComponents.push_back(origComponent); continue; } - - auto getObjectType = [](Type optionalTy) -> Type { - Type objectTy; - if (auto lvalue = optionalTy->getAs()) { - objectTy = lvalue->getObjectType()->getOptionalObjectType(); - if (optionalTy->hasUnresolvedType() && !objectTy) { - objectTy = optionalTy; - } - objectTy = LValueType::get(objectTy); - } else { - objectTy = optionalTy->getOptionalObjectType(); - if (optionalTy->hasUnresolvedType() && !objectTy) { - objectTy = optionalTy; - } - } - assert(objectTy); - return objectTy; - }; auto kind = origComponent.getKind(); - Optional foundDecl; - auto locator = cs.getConstraintLocator( E, LocatorPathElt::KeyPathComponent(i)); @@ -4262,74 +4204,43 @@ namespace { // If this is an unresolved link, make sure we resolved it. if (kind == KeyPathExpr::Component::Kind::UnresolvedProperty || kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) { - foundDecl = solution.getOverloadChoiceIfAvailable(locator); - // Leave the component unresolved if the overload was not resolved. - if (foundDecl) { - isDynamicMember = - foundDecl->choice.getKind() == - OverloadChoiceKind::DynamicMemberLookup || - foundDecl->choice.getKind() == - OverloadChoiceKind::KeyPathDynamicMemberLookup; - - // If this was a @dynamicMemberLookup property, then we actually - // form a subscript reference, so switch the kind. - if (isDynamicMember) { - kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; - } - } - } - - KeyPathExpr::Component component; - switch (kind) { - case KeyPathExpr::Component::Kind::UnresolvedProperty: { - // If we couldn't resolve the component, leave it alone. + auto foundDecl = solution.getOverloadChoiceIfAvailable(locator); if (!foundDecl) { - component = origComponent; - break; + // If we couldn't resolve the component, leave it alone. + resolvedComponents.push_back(origComponent); + baseTy = origComponent.getComponentType(); + continue; } - component = buildKeyPathPropertyComponent( - *foundDecl, origComponent.getLoc(), locator); - - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); + isDynamicMember = + foundDecl->choice.getKind() == + OverloadChoiceKind::DynamicMemberLookup || + foundDecl->choice.getKind() == + OverloadChoiceKind::KeyPathDynamicMemberLookup; - if (shouldForceUnwrapResult(foundDecl->choice, locator)) { - updateCSWithResolvedComponent(); - auto objectTy = getObjectType(baseTy); - auto loc = origComponent.getLoc(); - component = KeyPathExpr::Component::forOptionalForce(objectTy, loc); - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); + // If this was a @dynamicMemberLookup property, then we actually + // form a subscript reference, so switch the kind. + if (isDynamicMember) { + kind = KeyPathExpr::Component::Kind::UnresolvedSubscript; } + } + + switch (kind) { + case KeyPathExpr::Component::Kind::UnresolvedProperty: { + buildKeyPathPropertyComponent(solution.getOverloadChoice(locator), + origComponent.getLoc(), + locator, resolvedComponents); break; } case KeyPathExpr::Component::Kind::UnresolvedSubscript: { - // Leave the component unresolved if the overload was not resolved. - if (!foundDecl) { - component = origComponent; - break; - } - ArrayRef subscriptLabels; if (!isDynamicMember) subscriptLabels = origComponent.getSubscriptLabels(); - component = buildKeyPathSubscriptComponent( - *foundDecl, origComponent.getLoc(), origComponent.getIndexExpr(), - subscriptLabels, locator); - - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); - - if (shouldForceUnwrapResult(foundDecl->choice, locator)) { - updateCSWithResolvedComponent(); - auto objectTy = getObjectType(baseTy); - auto loc = origComponent.getLoc(); - component = KeyPathExpr::Component::forOptionalForce(objectTy, loc); - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); - } + buildKeyPathSubscriptComponent( + solution.getOverloadChoice(locator), + origComponent.getLoc(), origComponent.getIndexExpr(), + subscriptLabels, locator, resolvedComponents); break; } case KeyPathExpr::Component::Kind::OptionalChain: { @@ -4343,32 +4254,25 @@ namespace { assert(objectTy); auto loc = origComponent.getLoc(); - component = KeyPathExpr::Component::forOptionalChain(objectTy, loc); - - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); + resolvedComponents.push_back( + KeyPathExpr::Component::forOptionalChain(objectTy, loc)); break; } - case KeyPathExpr::Component::Kind::OptionalForce: { - auto objectTy = getObjectType(baseTy); - auto loc = origComponent.getLoc(); - component = KeyPathExpr::Component::forOptionalForce(objectTy, loc); - baseTy = component.getComponentType(); - resolvedComponents.push_back(component); + case KeyPathExpr::Component::Kind::OptionalForce: + buildKeyPathOptionalForceComponent(resolvedComponents); break; - } - case KeyPathExpr::Component::Kind::Invalid: - component = origComponent; + case KeyPathExpr::Component::Kind::Invalid: { + auto component = origComponent; component.setComponentType(leafTy); - - baseTy = component.getComponentType(); resolvedComponents.push_back(component); break; - case KeyPathExpr::Component::Kind::Identity: - component = origComponent; + } + case KeyPathExpr::Component::Kind::Identity: { + auto component = origComponent; component.setComponentType(baseTy); resolvedComponents.push_back(component); break; + } case KeyPathExpr::Component::Kind::Property: case KeyPathExpr::Component::Kind::Subscript: case KeyPathExpr::Component::Kind::OptionalWrap: @@ -4376,8 +4280,9 @@ namespace { llvm_unreachable("already resolved"); } - // By now, "baseTy" is the result type of this component. - updateCSWithResolvedComponent(); + // Update "baseTy" with the result type of the last component. + assert(!resolvedComponents.empty()); + baseTy = resolvedComponents.back().getComponentType(); } // Wrap a non-optional result if there was chaining involved. @@ -4390,10 +4295,12 @@ namespace { auto component = KeyPathExpr::Component::forOptionalWrap(leafTy); resolvedComponents.push_back(component); baseTy = leafTy; - updateCSWithResolvedComponent(); } + + // Set the resolved components, and cache their types. E->resolveComponents(cs.getASTContext(), resolvedComponents); - + cs.cacheExprTypes(E); + // See whether there's an equivalent ObjC key path string we can produce // for interop purposes. if (cs.getASTContext().LangOpts.EnableObjCInterop) { @@ -4450,12 +4357,12 @@ namespace { auto closureTy = FunctionType::get({ FunctionType::Param(baseTy) }, leafTy); auto closure = new (ctx) - AutoClosureExpr(E, leafTy, discriminator, cs.DC); + AutoClosureExpr(/*set body later*/nullptr, leafTy, + discriminator, cs.DC); auto param = new (ctx) ParamDecl( SourceLoc(), /*argument label*/ SourceLoc(), Identifier(), /*parameter name*/ SourceLoc(), ctx.getIdentifier("$0"), closure); - param->setType(baseTy); param->setInterfaceType(baseTy->mapTypeOutOfContext()); param->setSpecifier(ParamSpecifier::Default); @@ -4465,13 +4372,13 @@ namespace { auto outerClosureTy = FunctionType::get({ FunctionType::Param(keyPathTy) }, closureTy); auto outerClosure = new (ctx) - AutoClosureExpr(closure, closureTy, discriminator, cs.DC); + AutoClosureExpr(/*set body later*/nullptr, closureTy, + discriminator, cs.DC); auto outerParam = new (ctx) ParamDecl(SourceLoc(), /*argument label*/ SourceLoc(), Identifier(), /*parameter name*/ SourceLoc(), ctx.getIdentifier("$kp$"), outerClosure); - outerParam->setType(keyPathTy); outerParam->setInterfaceType(keyPathTy->mapTypeOutOfContext()); outerParam->setSpecifier(ParamSpecifier::Default); @@ -4514,10 +4421,37 @@ namespace { return coerceToType(outerApply, exprType, cs.getConstraintLocator(E)); } - KeyPathExpr::Component - buildKeyPathPropertyComponent(const SelectedOverload &overload, - SourceLoc componentLoc, - ConstraintLocator *locator) { + void buildKeyPathOptionalForceComponent( + SmallVectorImpl &components) { + assert(!components.empty()); + + // Unwrap the last component type, preserving @lvalue-ness. + auto optionalTy = components.back().getComponentType(); + Type objectTy; + if (auto lvalue = optionalTy->getAs()) { + objectTy = lvalue->getObjectType()->getOptionalObjectType(); + if (optionalTy->hasUnresolvedType() && !objectTy) { + objectTy = optionalTy; + } + objectTy = LValueType::get(objectTy); + } else { + objectTy = optionalTy->getOptionalObjectType(); + if (optionalTy->hasUnresolvedType() && !objectTy) { + objectTy = optionalTy; + } + } + assert(objectTy); + + auto loc = components.back().getLoc(); + components.push_back( + KeyPathExpr::Component::forOptionalForce(objectTy, loc)); + } + + void buildKeyPathPropertyComponent( + const SelectedOverload &overload, SourceLoc componentLoc, + ConstraintLocator *locator, + SmallVectorImpl &components) { + auto resolvedTy = simplifyType(overload.openedType); if (auto *property = overload.choice.getDeclOrNull()) { // Key paths can only refer to properties currently. auto varDecl = cast(property); @@ -4527,48 +4461,30 @@ namespace { // There is a fix which diagnoses such situation already. assert(!varDecl->isStatic()); - auto dc = property->getInnermostDeclContext(); - - // Compute substitutions to refer to the member. - SubstitutionMap subs = solution.computeSubstitutions( - dc->getGenericSignatureOfContext(), locator); - - auto resolvedTy = overload.openedType; - resolvedTy = simplifyType(resolvedTy); - - auto ref = ConcreteDeclRef(property, subs); - - return KeyPathExpr::Component::forProperty(ref, resolvedTy, - componentLoc); + // Compute the concrete reference to the member. + auto ref = resolveConcreteDeclRef(property, locator); + components.push_back( + KeyPathExpr::Component::forProperty(ref, resolvedTy, componentLoc)); + } else { + auto fieldIndex = overload.choice.getTupleIndex(); + components.push_back(KeyPathExpr::Component::forTupleElement( + fieldIndex, resolvedTy, componentLoc)); } - auto fieldIndex = overload.choice.getTupleIndex(); - auto resolvedTy = overload.openedType; - resolvedTy = simplifyType(resolvedTy); - - return KeyPathExpr::Component::forTupleElement(fieldIndex, resolvedTy, - componentLoc); + if (shouldForceUnwrapResult(overload.choice, locator)) + buildKeyPathOptionalForceComponent(components); } - KeyPathExpr::Component buildKeyPathSubscriptComponent( - SelectedOverload &overload, SourceLoc componentLoc, Expr *indexExpr, - ArrayRef labels, ConstraintLocator *locator) { + void buildKeyPathSubscriptComponent( + const SelectedOverload &overload, + SourceLoc componentLoc, Expr *indexExpr, + ArrayRef labels, ConstraintLocator *locator, + SmallVectorImpl &components) { auto subscript = cast(overload.choice.getDecl()); assert(!subscript->isGetterMutating()); - auto dc = subscript->getInnermostDeclContext(); - - auto indexType = AnyFunctionType::composeInput( - cs.TC.Context, - subscript->getInterfaceType()->castTo()->getParams(), - /*canonicalVararg=*/false); - - SubstitutionMap subs; - if (auto sig = dc->getGenericSignatureOfContext()) { - // Compute substitutions to refer to the member. - subs = solution.computeSubstitutions(sig, locator); - indexType = indexType.subst(subs); - } + // Compute substitutions to refer to the member. + auto ref = resolveConcreteDeclRef(subscript, locator); // If this is a @dynamicMemberLookup reference to resolve a property // through the subscript(dynamicMember:) member, restore the @@ -4581,37 +4497,30 @@ namespace { OverloadChoiceKind::KeyPathDynamicMemberLookup; if (forDynamicLookup) { - overload.openedType = - overload.openedFullType->castTo()->getResult(); - labels = cs.getASTContext().Id_dynamicMember; + auto indexType = getTypeOfDynamicMemberIndex(overload); if (overload.choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) { - auto indexType = getTypeOfDynamicMemberIndex(overload); indexExpr = buildKeyPathDynamicMemberIndexExpr( indexType->castTo(), componentLoc, locator); } else { auto fieldName = overload.choice.getName().getBaseIdentifier().str(); indexExpr = buildDynamicMemberLookupIndexExpr(fieldName, componentLoc, - dc, cs); + indexType); } } auto subscriptType = simplifyType(overload.openedType)->castTo(); auto resolvedTy = subscriptType->getResult(); - auto ref = ConcreteDeclRef(subscript, subs); // Coerce the indices to the type the subscript expects. auto *newIndexExpr = - coerceCallArguments(indexExpr, subscriptType, + coerceCallArguments(indexExpr, subscriptType, ref, /*applyExpr*/ nullptr, labels, /*hasTrailingClosure*/ false, locator); - auto component = KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( - ref, newIndexExpr, labels, resolvedTy, componentLoc, {}); - // We need to be able to hash the captured index values in order for // KeyPath itself to be hashable, so check that all of the subscript // index components are hashable and collect their conformances here. @@ -4621,21 +4530,30 @@ namespace { cs.getASTContext().getProtocol(KnownProtocolKind::Hashable); auto fnType = overload.openedType->castTo(); - for (const auto ¶m : fnType->getParams()) { - auto indexType = simplifyType(param.getPlainType()); + SmallVector newLabels; + for (auto ¶m : fnType->getParams()) { + newLabels.push_back(param.getLabel()); + + auto indexType = simplifyType(param.getParameterType()); // Index type conformance to Hashable protocol has been // verified by the solver, we just need to get it again // with all of the generic parameters resolved. auto hashableConformance = TypeChecker::conformsToProtocol(indexType, hashable, cs.DC, ConformanceCheckFlags::InExpression); - assert(hashableConformance.hasValue()); + assert(hashableConformance); - conformances.push_back(*hashableConformance); + conformances.push_back(hashableConformance); } - component.setSubscriptIndexHashableConformances(conformances); - return component; + auto comp = KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr( + ref, newIndexExpr, cs.getASTContext().AllocateCopy(newLabels), + resolvedTy, componentLoc, + cs.getASTContext().AllocateCopy(conformances)); + components.push_back(comp); + + if (shouldForceUnwrapResult(overload.choice, locator)) + buildKeyPathOptionalForceComponent(components); } Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) { @@ -4650,7 +4568,6 @@ namespace { Expr *visitTapExpr(TapExpr *E) { auto type = simplifyType(cs.getType(E)); - E->getVar()->setType(type); E->getVar()->setInterfaceType(type->mapTypeOutOfContext()); cs.setType(E, type); @@ -4661,6 +4578,12 @@ namespace { /// Interface for ExprWalker void walkToExprPre(Expr *expr) { + // If we have an apply, make a note of its callee locator prior to + // rewriting. + if (auto *apply = dyn_cast(expr)) { + auto *calleeLoc = cs.getCalleeLocator(cs.getConstraintLocator(expr)); + CalleeLocators[apply] = calleeLoc; + } ExprStack.push_back(expr); } @@ -4673,47 +4596,50 @@ namespace { return result; } - void finalize(Expr *&result) { + const AppliedBuilderTransform *getAppliedBuilderTransform( + AnyFunctionRef fn) { + auto known = solution.functionBuilderTransformed.find(fn); + return known != solution.functionBuilderTransformed.end() + ? &known->second + : nullptr; + } + + void finalize() { assert(ExprStack.empty()); assert(OpenedExistentials.empty()); - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // Look at all of the suspicious optional injections for (auto injection : SuspiciousOptionalInjections) { - // If we already diagnosed this injection, we're done. - if (DiagnosedOptionalInjections.count(injection)) { - continue; - } - - auto *cast = findForcedDowncast(tc.Context, injection->getSubExpr()); + auto *cast = findForcedDowncast(ctx, injection->getSubExpr()); if (!cast) continue; if (isa(injection->getSubExpr())) continue; - tc.diagnose(injection->getLoc(), diag::inject_forced_downcast, - cs.getType(injection->getSubExpr())->getRValueType()); + ctx.Diags.diagnose( + injection->getLoc(), diag::inject_forced_downcast, + cs.getType(injection->getSubExpr())->getRValueType()); auto exclaimLoc = cast->getExclaimLoc(); - tc.diagnose(exclaimLoc, diag::forced_to_conditional_downcast, - cs.getType(injection)->getOptionalObjectType()) + ctx.Diags + .diagnose(exclaimLoc, diag::forced_to_conditional_downcast, + cs.getType(injection)->getOptionalObjectType()) .fixItReplace(exclaimLoc, "?"); - tc.diagnose(cast->getStartLoc(), diag::silence_inject_forced_downcast) - .fixItInsert(cast->getStartLoc(), "(") - .fixItInsertAfter(cast->getEndLoc(), ")"); + ctx.Diags + .diagnose(cast->getStartLoc(), diag::silence_inject_forced_downcast) + .fixItInsert(cast->getStartLoc(), "(") + .fixItInsertAfter(cast->getEndLoc(), ")"); } - - // Set the final types on the expression. - cs.setExprTypes(result); } /// Diagnose an optional injection that is probably not what the /// user wanted, because it comes from a forced downcast. void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection) { // Check whether we have a forced downcast. - auto &tc = cs.getTypeChecker(); - auto *cast = findForcedDowncast(tc.Context, injection->getSubExpr()); + auto *cast = + findForcedDowncast(cs.getASTContext(), injection->getSubExpr()); if (!cast) return; @@ -4722,284 +4648,28 @@ namespace { }; } // end anonymous namespace - -/// Resolve a locator to the specific declaration it references, if possible. -/// -/// \param cs The constraint system in which the locator will be resolved. -/// -/// \param locator The locator to resolve. -/// -/// \param findOvlChoice A function that searches for the overload choice -/// associated with the given locator, or an empty optional if there is no such -/// overload. -/// -/// \returns the decl to which the locator resolved. -/// -static ConcreteDeclRef resolveLocatorToDecl( - ConstraintSystem &cs, ConstraintLocator *locator, - llvm::function_ref(ConstraintLocator *)> - findOvlChoice, - llvm::function_ref - getConcreteDeclRef) { - assert(locator && "Null locator"); - if (!locator->getAnchor()) +ConcreteDeclRef +Solution::resolveLocatorToDecl(ConstraintLocator *locator) const { + // Get the callee locator without looking through applies, ensuring we only + // return a decl for a direct reference. + auto *calleeLoc = + constraintSystem->getCalleeLocator(locator, /*lookThroughApply*/ false); + auto overload = getOverloadChoiceIfAvailable(calleeLoc); + if (!overload) return ConcreteDeclRef(); - auto anchor = locator->getAnchor(); - // Unwrap any specializations, constructor calls, implicit conversions, and - // '.'s. - // FIXME: This is brittle. - do { - if (auto specialize = dyn_cast(anchor)) { - anchor = specialize->getSubExpr(); - continue; - } - - if (auto implicit = dyn_cast(anchor)) { - anchor = implicit->getSubExpr(); - continue; - } - - if (auto identity = dyn_cast(anchor)) { - anchor = identity->getSubExpr(); - continue; - } - - if (auto tryExpr = dyn_cast(anchor)) { - if (isa(tryExpr)) - break; - - anchor = tryExpr->getSubExpr(); - continue; - } - - if (auto selfApply = dyn_cast(anchor)) { - anchor = selfApply->getFn(); - continue; - } - - if (auto dotSyntax = dyn_cast(anchor)) { - anchor = dotSyntax->getRHS(); - continue; - } - - if (auto *OEE = dyn_cast(anchor)) { - anchor = OEE->getSubExpr(); - continue; - } - - break; - } while (true); - - // Simple case: direct reference to a declaration. - if (auto dre = dyn_cast(anchor)) - return dre->getDeclRef(); - - // Simple case: direct reference to a declaration. - if (auto mre = dyn_cast(anchor)) - return mre->getMember(); - - if (auto ctorRef = dyn_cast(anchor)) - return ctorRef->getDeclRef(); - - if (isa(anchor) || - isa(anchor)) { - // Overloaded and unresolved cases: find the resolved overload. - auto anchorLocator = cs.getConstraintLocator(anchor); - if (auto selected = findOvlChoice(anchorLocator)) { - if (auto *decl = selected->choice.getDeclOrNull()) - return getConcreteDeclRef(decl, selected->openedType, anchorLocator); - } - } - - if (isa(anchor)) { - // Unresolved member: find the resolved overload. - auto anchorLocator = cs.getConstraintLocator(anchor, - ConstraintLocator::UnresolvedMember); - if (auto selected = findOvlChoice(anchorLocator)) { - if (auto *decl = selected->choice.getDeclOrNull()) - return getConcreteDeclRef(decl, selected->openedType, anchorLocator); - } - } - - if (isa(anchor)) { - // Unresolved member: find the resolved overload. - auto anchorLocator = cs.getConstraintLocator(anchor, - ConstraintLocator::Member); - if (auto selected = findOvlChoice(anchorLocator)) { - if (auto *decl = selected->choice.getDeclOrNull()) - return getConcreteDeclRef(decl, selected->openedType, anchorLocator); - } - } - - if (auto subscript = dyn_cast(anchor)) { - // Subscript expressions may have a declaration. If so, use it. - if (subscript->hasDecl()) - return subscript->getDecl(); - - // Otherwise, find the resolved overload. - auto anchorLocator = - cs.getConstraintLocator(anchor, ConstraintLocator::SubscriptMember); - if (auto selected = findOvlChoice(anchorLocator)) { - if (auto *decl = selected->choice.getDeclOrNull()) - return getConcreteDeclRef(decl, selected->openedType, anchorLocator); - } - } - - if (auto subscript = dyn_cast(anchor)) { - // Dynamic subscripts are always resolved. - return subscript->getMember(); - } - - return ConcreteDeclRef(); + return resolveConcreteDeclRef(overload->choice.getDeclOrNull(), locator); } -ConcreteDeclRef Solution::resolveLocatorToDecl( - ConstraintLocator *locator) const { - auto &cs = getConstraintSystem(); - - // Simplify the locator. - SourceRange range; - locator = simplifyLocator(cs, locator, range); - - // If we didn't map down to a specific expression, we can't handle a default - // argument. - if (!locator->getAnchor() || !locator->getPath().empty()) - return nullptr; - - if (auto resolved - = ::resolveLocatorToDecl(cs, locator, - [&](ConstraintLocator *locator) -> Optional { - auto known = overloadChoices.find(locator); - if (known == overloadChoices.end()) { - return None; - } - - return known->second; - }, - [&](ValueDecl *decl, Type openedType, ConstraintLocator *locator) - -> ConcreteDeclRef { - SubstitutionMap subs = - computeSubstitutions( - decl->getInnermostDeclContext() - ->getGenericSignatureOfContext(), - locator); - return ConcreteDeclRef(decl, subs); - })) { - return resolved; +/// Returns the concrete callee which 'owns' the default argument at a given +/// index. This looks through inheritance for inherited default args. +static ConcreteDeclRef getDefaultArgOwner(ConcreteDeclRef owner, + unsigned index) { + auto *param = getParameterAt(owner.getDecl(), index); + if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) { + return getDefaultArgOwner(owner.getOverriddenDecl(), index); } - - return ConcreteDeclRef(); -} - -/// Given a constraint locator, find the declaration reference -/// to the callee, it is a call to a declaration. -static ConcreteDeclRef -findCalleeDeclRef(ConstraintSystem &cs, const Solution &solution, - ConstraintLocator *locator) { - if (locator->getPath().empty() || !locator->getAnchor()) - return nullptr; - - // If the locator points to a function application, find the function itself. - if (locator->getPath().back().getKind() == ConstraintLocator::ApplyArgument) { - assert(locator->getPath().back().getNewSummaryFlags() == 0 && - "ApplyArgument adds no flags"); - SmallVector newPath; - newPath.append(locator->getPath().begin(), locator->getPath().end()-1); - - unsigned newFlags = locator->getSummaryFlags(); - newPath.push_back(ConstraintLocator::ApplyFunction); - - assert(newPath.back().getNewSummaryFlags() == 0 && - "added element that changes the flags?"); - locator = cs.getConstraintLocator(locator->getAnchor(), newPath, newFlags); - } - - return solution.resolveLocatorToDecl(locator); -} - -/// Produce the caller-side default argument for this default argument, or -/// null if the default argument will be provided by the callee. -static std::pair -getCallerDefaultArg(ConstraintSystem &cs, DeclContext *dc, - SourceLoc loc, ConcreteDeclRef &owner, - unsigned index) { - auto &tc = cs.getTypeChecker(); - - const auto *param = getParameterAt(cast(owner.getDecl()), index); - Expr *init = nullptr; - switch (param->getDefaultArgumentKind()) { - case DefaultArgumentKind::None: - llvm_unreachable("No default argument here?"); - - case DefaultArgumentKind::StoredProperty: - case DefaultArgumentKind::Normal: - return {nullptr, param->getDefaultArgumentKind()}; - - case DefaultArgumentKind::Inherited: - // Update the owner to reflect inheritance here. - owner = owner.getOverriddenDecl(); - return getCallerDefaultArg(cs, dc, loc, owner, index); - - case DefaultArgumentKind::Column: - init = new (tc.Context) MagicIdentifierLiteralExpr( - MagicIdentifierLiteralExpr::Column, loc, - /*implicit=*/true); - break; - - case DefaultArgumentKind::File: - init = new (tc.Context) MagicIdentifierLiteralExpr( - MagicIdentifierLiteralExpr::File, loc, - /*implicit=*/true); - break; - - case DefaultArgumentKind::Line: - init = new (tc.Context) MagicIdentifierLiteralExpr( - MagicIdentifierLiteralExpr::Line, loc, - /*implicit=*/true); - break; - - case DefaultArgumentKind::Function: - init = new (tc.Context) MagicIdentifierLiteralExpr( - MagicIdentifierLiteralExpr::Function, loc, - /*implicit=*/true); - break; - - case DefaultArgumentKind::DSOHandle: - init = new (tc.Context) MagicIdentifierLiteralExpr( - MagicIdentifierLiteralExpr::DSOHandle, loc, - /*implicit=*/true); - break; - - case DefaultArgumentKind::NilLiteral: - init = new (tc.Context) NilLiteralExpr(loc, /*Implicit=*/true); - break; - - case DefaultArgumentKind::EmptyArray: - init = ArrayExpr::create(tc.Context, loc, {}, {}, loc); - init->setImplicit(); - break; - - case DefaultArgumentKind::EmptyDictionary: - init = DictionaryExpr::create(tc.Context, loc, {}, {}, loc); - init->setImplicit(); - break; - } - - // Convert the literal to the appropriate type. - auto defArgType = owner.getDecl()->getDeclContext()->mapTypeIntoContext( - param->getInterfaceType()); - auto resultTy = - tc.typeCheckParameterDefault(init, dc, defArgType, - /*isAutoClosure=*/param->isAutoClosure(), - /*canFail=*/false); - assert(resultTy && "Conversion cannot fail"); - (void)resultTy; - - cs.cacheExprTypes(init); - - return {init, param->getDefaultArgumentKind()}; + return owner; } static bool canPeepholeTupleConversion(Expr *expr, @@ -5020,7 +4690,7 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr, TupleType *toTuple, ConstraintLocatorBuilder locator, ArrayRef sources) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // If the input expression is a tuple expression, we can convert it in-place. if (canPeepholeTupleConversion(expr, sources)) { @@ -5057,7 +4727,7 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr, for (unsigned i = 0, e = sources.size(); i != e; ++i) { auto fromEltType = fromTuple->getElementType(i); auto *opaqueElt = - new (tc.Context) OpaqueValueExpr(expr->getSourceRange(), fromEltType); + new (ctx) OpaqueValueExpr(expr->getSourceRange(), fromEltType); cs.cacheType(opaqueElt); destructured.push_back(opaqueElt); } @@ -5089,34 +4759,29 @@ Expr *ExprRewriter::coerceTupleToTuple(Expr *expr, // Create the result tuple, written in terms of the destructured // OpaqueValueExprs. - auto *result = TupleExpr::createImplicit(tc.Context, converted, labels); - result->setType(TupleType::get(convertedElts, tc.Context)); + auto *result = TupleExpr::createImplicit(ctx, converted, labels); + result->setType(TupleType::get(convertedElts, ctx)); cs.cacheType(result); // Create the tuple conversion. - return - cs.cacheType(DestructureTupleExpr::create(tc.Context, - destructured, expr, result, - toTuple)); + return cs.cacheType( + DestructureTupleExpr::create(ctx, destructured, expr, result, toTuple)); } -static Type getMetatypeSuperclass(Type t, TypeChecker &tc) { +static Type getMetatypeSuperclass(Type t) { if (auto *metaTy = t->getAs()) return MetatypeType::get(getMetatypeSuperclass( - metaTy->getInstanceType(), - tc)); + metaTy->getInstanceType())); if (auto *metaTy = t->getAs()) return ExistentialMetatypeType::get(getMetatypeSuperclass( - metaTy->getInstanceType(), - tc)); + metaTy->getInstanceType())); return t->getSuperclass(); } -Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType, - ConstraintLocatorBuilder locator) { - auto &tc = cs.getTypeChecker(); +Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType) { + auto &ctx = cs.getASTContext(); auto fromType = cs.getType(expr); @@ -5133,14 +4798,14 @@ Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType, if (fromInstanceType->is()) { // Coercion from archetype to its (concrete) superclass. - auto superclass = getMetatypeSuperclass(fromType, tc); + auto superclass = getMetatypeSuperclass(fromType); expr = cs.cacheType( - new (tc.Context) ArchetypeToSuperExpr(expr, superclass)); + new (ctx) ArchetypeToSuperExpr(expr, superclass)); if (!superclass->isEqual(toType)) - return coerceSuperclass(expr, toType, locator); + return coerceSuperclass(expr, toType); return expr; @@ -5151,24 +4816,24 @@ Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType, // concrete superclass. auto fromArchetype = OpenedArchetypeType::getAny(fromType); - auto *archetypeVal = cs.cacheType(new (tc.Context) OpaqueValueExpr( + auto *archetypeVal = cs.cacheType(new (ctx) OpaqueValueExpr( expr->getSourceRange(), fromArchetype)); - auto *result = coerceSuperclass(archetypeVal, toType, locator); + auto *result = coerceSuperclass(archetypeVal, toType); return cs.cacheType( - new (tc.Context) OpenExistentialExpr(expr, archetypeVal, result, + new (ctx) OpenExistentialExpr(expr, archetypeVal, result, toType)); } // Coercion from subclass to superclass. if (toType->is()) { return cs.cacheType( - new (tc.Context) MetatypeConversionExpr(expr, toType)); + new (ctx) MetatypeConversionExpr(expr, toType)); } return cs.cacheType( - new (tc.Context) DerivedToBaseExpr(expr, toType)); + new (ctx) DerivedToBaseExpr(expr, toType)); } /// Collect the conformances for all the protocols of an existential type. @@ -5177,23 +4842,20 @@ Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType, /// allow the conversion here, except the ErasureExpr ends up with trivial /// conformances. static ArrayRef -collectExistentialConformances(TypeChecker &tc, Type fromType, Type toType, +collectExistentialConformances(Type fromType, Type toType, DeclContext *DC) { auto layout = toType->getExistentialLayout(); SmallVector conformances; for (auto proto : layout.getProtocols()) { - conformances.push_back( - *tc.containsProtocol(fromType, proto->getDecl(), DC, - ConformanceCheckFlags::InExpression)); + conformances.push_back(TypeChecker::containsProtocol( + fromType, proto->getDecl(), DC, ConformanceCheckFlags::InExpression)); } - return tc.Context.AllocateCopy(conformances); + return toType->getASTContext().AllocateCopy(conformances); } -Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType, - ConstraintLocatorBuilder locator) { - auto &tc = solution.getConstraintSystem().getTypeChecker(); +Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType) { Type fromType = cs.getType(expr); Type fromInstanceType = fromType; Type toInstanceType = toType; @@ -5207,10 +4869,10 @@ Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType, toInstanceType = toInstanceType->castTo()->getInstanceType(); } - ASTContext &ctx = tc.Context; + ASTContext &ctx = cs.getASTContext(); auto conformances = - collectExistentialConformances(tc, fromInstanceType, toInstanceType, cs.DC); + collectExistentialConformances(fromInstanceType, toInstanceType, cs.DC); // For existential-to-existential coercions, open the source existential. if (fromType->isAnyExistentialType()) { @@ -5274,10 +4936,10 @@ static unsigned getOptionalEvaluationDepth(Expr *expr, Expr *target) { Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType, ConstraintLocatorBuilder locator, Optional typeFromPattern) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); Type fromType = cs.getType(expr); - - tc.requireOptionalIntrinsics(expr->getLoc()); + + TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc()); SmallVector fromOptionals; (void)fromType->lookThroughAllOptionalTypes(fromOptionals); @@ -5298,7 +4960,7 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType, auto diff = toDepth - fromDepth; while (diff--) { Type type = toOptionals[diff]; - expr = cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(expr, type)); + expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, type)); diagnoseOptionalInjection(cast(expr)); } @@ -5310,8 +4972,8 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType, // The depth we use here will get patched after we apply the coercion. auto bindOptional = - new (tc.Context) BindOptionalExpr(expr, expr->getSourceRange().End, - /*depth*/ 0, fromValueType); + new (ctx) BindOptionalExpr(expr, expr->getSourceRange().End, + /*depth*/ 0, fromValueType); expr = cs.cacheType(bindOptional); expr->setImplicit(true); @@ -5320,10 +4982,10 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType, unsigned depth = getOptionalEvaluationDepth(expr, bindOptional); bindOptional->setDepth(depth); - - expr = cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(expr, toType)); - - expr = cs.cacheType(new (tc.Context) OptionalEvaluationExpr(expr, toType)); + + expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType)); + + expr = cs.cacheType(new (ctx) OptionalEvaluationExpr(expr, toType)); expr->setImplicit(true); return expr; } @@ -5334,9 +4996,8 @@ Expr *ExprRewriter::coerceImplicitlyUnwrappedOptionalToValue(Expr *expr, Type ob if (optTy->is()) objTy = LValueType::get(objTy); - expr = new (cs.getTypeChecker().Context) ForceValueExpr(expr, - expr->getEndLoc(), - /* forcedIUO=*/ true); + expr = new (cs.getASTContext()) ForceValueExpr(expr, expr->getEndLoc(), + /* forcedIUO=*/ true); cs.setType(expr, objTy); expr->setImplicit(); return expr; @@ -5385,13 +5046,13 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee, return false; } -Expr *ExprRewriter::coerceCallArguments( - Expr *arg, AnyFunctionType *funcType, - ApplyExpr *apply, - ArrayRef argLabels, - bool hasTrailingClosure, - ConstraintLocatorBuilder locator) { - auto &tc = getConstraintSystem().getTypeChecker(); +Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, + ConcreteDeclRef callee, + ApplyExpr *apply, + ArrayRef argLabels, + bool hasTrailingClosure, + ConstraintLocatorBuilder locator) { + auto &ctx = getConstraintSystem().getASTContext(); auto params = funcType->getParams(); // Local function to produce a locator to refer to the given parameter. @@ -5407,10 +5068,6 @@ Expr *ExprRewriter::coerceCallArguments( return param.getPlainType()->hasUnresolvedType(); }); - // Find the callee declaration. - ConcreteDeclRef callee = - findCalleeDeclRef(cs, solution, cs.getConstraintLocator(locator)); - // Determine whether this application has curried self. bool skipCurriedSelf = apply ? hasCurriedSelf(cs, callee, apply) : true; // Determine the parameter bindings. @@ -5528,19 +5185,15 @@ Expr *ExprRewriter::coerceCallArguments( } // Collect them into an ArrayExpr. - auto *arrayExpr = ArrayExpr::create(tc.Context, - start, - variadicArgs, - {}, end, + auto *arrayExpr = ArrayExpr::create(ctx, start, variadicArgs, {}, end, param.getParameterType()); arrayExpr->setImplicit(); cs.cacheType(arrayExpr); // Wrap the ArrayExpr in a VarargExpansionExpr. - auto *varargExpansionExpr = - new (tc.Context) VarargExpansionExpr(arrayExpr, - /*implicit=*/true, - arrayExpr->getType()); + auto *varargExpansionExpr = new (ctx) + VarargExpansionExpr(arrayExpr, + /*implicit=*/true, arrayExpr->getType()); cs.cacheType(varargExpansionExpr); newArgs.push_back(varargExpansionExpr); @@ -5550,26 +5203,10 @@ Expr *ExprRewriter::coerceCallArguments( // Handle default arguments. if (parameterBindings[paramIdx].empty()) { - Expr *defArg; - DefaultArgumentKind defArgKind; - std::tie(defArg, defArgKind) = getCallerDefaultArg(cs, dc, arg->getLoc(), - callee, paramIdx); - - // If we have a caller-side default argument, just add the magic literal - // expression to our argument list. - if (defArg) { - defArg = - new (tc.Context) CallerDefaultArgumentExpr(defArg, - arg->getStartLoc(), - param.getParameterType()); - - // Otherwise, create a call of the default argument generator. - } else { - defArg = - new (tc.Context) DefaultArgumentExpr(callee, paramIdx, - arg->getStartLoc(), - param.getParameterType()); - } + auto owner = getDefaultArgOwner(callee, paramIdx); + auto paramTy = param.getParameterType(); + auto *defArg = new (ctx) + DefaultArgumentExpr(owner, paramIdx, arg->getStartLoc(), paramTy, dc); cs.cacheType(defArg); newArgs.push_back(defArg); @@ -5630,8 +5267,7 @@ Expr *ExprRewriter::coerceCallArguments( arg, closureType->getResult(), locator.withPathElement(ConstraintLocator::AutoclosureResult)); - convertedArg = cs.TC.buildAutoClosureExpr(dc, arg, closureType); - cs.cacheExprTypes(convertedArg); + convertedArg = cs.buildAutoClosureExpr(arg, closureType); } else { convertedArg = coerceToType( arg, paramType, @@ -5666,17 +5302,15 @@ Expr *ExprRewriter::coerceCallArguments( } // Rebuild the argument list, sharing as much structure as possible. - auto paramType = AnyFunctionType::composeInput(tc.Context, newParams, + auto paramType = AnyFunctionType::composeInput(ctx, newParams, /*canonicalVararg=*/false); if (isa(paramType.getPointer())) { if (argParen) { // We already had a ParenExpr, so replace it's sub-expression. argParen->setSubExpr(newArgs[0]); } else { - arg = new (tc.Context) ParenExpr(lParenLoc, - newArgs[0], - rParenLoc, - hasTrailingClosure); + arg = new (ctx) + ParenExpr(lParenLoc, newArgs[0], rParenLoc, hasTrailingClosure); arg->setImplicit(); } } else { @@ -5690,10 +5324,8 @@ Expr *ExprRewriter::coerceCallArguments( } } else { // Build a new TupleExpr, re-using source location information. - arg = TupleExpr::create(tc.Context, lParenLoc, - newArgs, newLabels, newLabelLocs, - rParenLoc, - hasTrailingClosure, + arg = TupleExpr::create(ctx, lParenLoc, newArgs, newLabels, newLabelLocs, + rParenLoc, hasTrailingClosure, /*implicit=*/true); } } @@ -5750,24 +5382,21 @@ static bool applyTypeToClosureExpr(ConstraintSystem &cs, } ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // Re-write the single-expression closure to return '()' assert(closureExpr->hasSingleExpressionBody()); // A single-expression body contains a single return statement // prior to this transformation. - auto member = closureExpr->getBody()->getElement(0); + auto member = closureExpr->getBody()->getFirstElement(); if (member.is()) { auto returnStmt = cast(member.get()); auto singleExpr = returnStmt->getResult(); - auto voidExpr = - cs.cacheType( - TupleExpr::createEmpty(tc.Context, - singleExpr->getStartLoc(), - singleExpr->getEndLoc(), - /*implicit*/true)); + auto voidExpr = cs.cacheType(TupleExpr::createEmpty( + ctx, singleExpr->getStartLoc(), singleExpr->getEndLoc(), + /*implicit*/ true)); returnStmt->setResult(voidExpr); // For l-value types, reset to the object type. This might not be strictly @@ -5776,19 +5405,17 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) { cs.setType(singleExpr, cs.getType(singleExpr)->getWithoutSpecifierType()); - cs.setExprTypes(singleExpr); - tc.checkIgnoredExpr(singleExpr); + solution.setExprTypes(singleExpr); + TypeChecker::checkIgnoredExpr(singleExpr); SmallVector elements; elements.push_back(singleExpr); elements.push_back(returnStmt); - - auto braceStmt = BraceStmt::create(tc.Context, - closureExpr->getStartLoc(), - elements, - closureExpr->getEndLoc(), - /*implicit*/true); - + + auto braceStmt = BraceStmt::create(ctx, closureExpr->getStartLoc(), + elements, closureExpr->getEndLoc(), + /*implicit*/ true); + closureExpr->setImplicit(); closureExpr->setBody(braceStmt, /*isSingleExpression*/true); } @@ -5796,36 +5423,33 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) { // Finally, compute the proper type for the closure. auto fnType = cs.getType(closureExpr)->getAs(); auto newClosureType = FunctionType::get( - fnType->getParams(), tc.Context.TheEmptyTupleType, fnType->getExtInfo()); + fnType->getParams(), ctx.TheEmptyTupleType, fnType->getExtInfo()); cs.setType(closureExpr, newClosureType); return closureExpr; } ClosureExpr *ExprRewriter::coerceClosureExprFromNever(ClosureExpr *closureExpr) { - auto &tc = cs.getTypeChecker(); - // Re-write the single-expression closure to drop the 'return'. assert(closureExpr->hasSingleExpressionBody()); // A single-expression body contains a single return statement // prior to this transformation. - auto member = closureExpr->getBody()->getElement(0); + auto member = closureExpr->getBody()->getFirstElement(); if (member.is()) { auto returnStmt = cast(member.get()); auto singleExpr = returnStmt->getResult(); - cs.setExprTypes(singleExpr); - tc.checkIgnoredExpr(singleExpr); + solution.setExprTypes(singleExpr); + TypeChecker::checkIgnoredExpr(singleExpr); SmallVector elements; elements.push_back(singleExpr); - auto braceStmt = BraceStmt::create(tc.Context, - closureExpr->getStartLoc(), - elements, - closureExpr->getEndLoc(), - /*implicit*/true); + auto braceStmt = + BraceStmt::create(cs.getASTContext(), closureExpr->getStartLoc(), + elements, closureExpr->getEndLoc(), + /*implicit*/ true); closureExpr->setImplicit(); closureExpr->setBody(braceStmt, /*isSingleExpression*/true); @@ -5847,7 +5471,7 @@ getSemanticExprForDeclOrMemberRef(Expr *expr) { static void maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, AnyFunctionType *toType) { - auto &tc = cs.getTypeChecker(); + auto &de = cs.getASTContext().Diags; Type fromType = cs.getType(expr); auto fromFnType = fromType->getAs(); @@ -5871,10 +5495,9 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, // TODO: We could allow static (or class final) functions too by // "capturing" the metatype in a thunk. if (fn->getDeclContext()->isTypeContext()) { - tc.diagnose(expr->getLoc(), - diag::c_function_pointer_from_method); + de.diagnose(expr->getLoc(), diag::c_function_pointer_from_method); } else if (fn->getGenericParams()) { - tc.diagnose(expr->getLoc(), + de.diagnose(expr->getLoc(), diag::c_function_pointer_from_generic_function); } }; @@ -5898,8 +5521,8 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr, // Can convert a literal closure that doesn't capture context. if (auto closure = dyn_cast(semanticExpr)) return; - - tc.diagnose(expr->getLoc(), + + de.diagnose(expr->getLoc(), diag::invalid_c_function_pointer_conversion_expr); } } @@ -5911,12 +5534,10 @@ static Expr *buildElementConversion(ExprRewriter &rewriter, ConstraintLocatorBuilder locator, Expr *element) { auto &cs = rewriter.getConstraintSystem(); - - auto &tc = rewriter.getConstraintSystem().getTypeChecker(); if (bridged && - tc.typeCheckCheckedCast(srcType, destType, - CheckedCastContextKind::None, cs.DC, - SourceLoc(), nullptr, SourceRange()) + TypeChecker::typeCheckCheckedCast(srcType, destType, + CheckedCastContextKind::None, cs.DC, + SourceLoc(), nullptr, SourceRange()) != CheckedCastKind::Coercion) { if (auto conversion = rewriter.buildObjCBridgeExpr(element, destType, locator)) @@ -6111,8 +5732,6 @@ Expr *ExprRewriter::buildCollectionUpcastExpr( Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType, ConstraintLocatorBuilder locator) { - auto &tc = cs.getTypeChecker(); - Type fromType = cs.getType(expr); // Bridged collection casts always succeed, so we treat them as @@ -6144,8 +5763,8 @@ Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType, if (auto foreignClass = toType->getClassOrBoundGenericClass()) { if (foreignClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { - return cs.cacheType( - new (tc.Context) ForeignObjectConversionExpr(objcExpr, toType)); + return cs.cacheType(new (cs.getASTContext()) + ForeignObjectConversionExpr(objcExpr, toType)); } } @@ -6156,17 +5775,16 @@ Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType, return forceBridgeFromObjectiveC(expr, toType); } -static Expr *addImplicitLoadExpr(ConstraintSystem &cs, Expr *expr) { - auto &tc = cs.getTypeChecker(); - return tc.addImplicitLoadExpr( - expr, [&cs](Expr *expr) { return cs.getType(expr); }, - [&cs](Expr *expr, Type type) { cs.setType(expr, type); }); +Expr *ConstraintSystem::addImplicitLoadExpr(Expr *expr) { + return TypeChecker::addImplicitLoadExpr( + getASTContext(), expr, [this](Expr *expr) { return getType(expr); }, + [this](Expr *expr, Type type) { setType(expr, type); }); } Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, ConstraintLocatorBuilder locator, Optional typeFromPattern) { - auto &tc = cs.getTypeChecker(); + auto &ctx = cs.getASTContext(); // The type we're converting from. Type fromType = cs.getType(expr); @@ -6219,45 +5837,49 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, } } + auto &err = llvm::errs(); + err << "fromType->getCanonicalType() = "; + fromType->getCanonicalType()->dump(err); + err << "toType->getCanonicalType() = "; + toType->getCanonicalType()->dump(err); llvm_unreachable("Should be handled above"); } case ConversionRestrictionKind::Superclass: case ConversionRestrictionKind::ExistentialMetatypeToMetatype: - return coerceSuperclass(expr, toType, locator); + return coerceSuperclass(expr, toType); case ConversionRestrictionKind::Existential: case ConversionRestrictionKind::MetatypeToExistentialMetatype: - return coerceExistential(expr, toType, locator); + return coerceExistential(expr, toType); case ConversionRestrictionKind::ClassMetatypeToAnyObject: { - assert(tc.getLangOpts().EnableObjCInterop - && "metatypes can only be cast to objects w/ objc runtime!"); - return cs.cacheType( - new (tc.Context) ClassMetatypeToObjectExpr(expr, toType)); + assert(ctx.LangOpts.EnableObjCInterop && + "metatypes can only be cast to objects w/ objc runtime!"); + return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType)); } case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: { - assert(tc.getLangOpts().EnableObjCInterop - && "metatypes can only be cast to objects w/ objc runtime!"); - return cs.cacheType( - new (tc.Context) ExistentialMetatypeToObjectExpr(expr, toType)); + assert(ctx.LangOpts.EnableObjCInterop && + "metatypes can only be cast to objects w/ objc runtime!"); + return cs.cacheType(new (ctx) + ExistentialMetatypeToObjectExpr(expr, toType)); } case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: { - return cs.cacheType( - new (tc.Context) ProtocolMetatypeToObjectExpr(expr, toType)); + return cs.cacheType(new (ctx) ProtocolMetatypeToObjectExpr(expr, toType)); } case ConversionRestrictionKind::ValueToOptional: { auto toGenericType = toType->castTo(); assert(toGenericType->getDecl()->isOptionalDecl()); - tc.requireOptionalIntrinsics(expr->getLoc()); + TypeChecker::requireOptionalIntrinsics(cs.getASTContext(), + expr->getLoc()); Type valueType = toGenericType->getGenericArgs()[0]; expr = coerceToType(expr, valueType, locator); if (!expr) return nullptr; auto *result = - cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(expr, toType)); + cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType)); diagnoseOptionalInjection(result); return result; } @@ -6277,7 +5899,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, expr = cs.coerceToRValue(expr); // Find the conformance of the source type to Hashable. - auto hashable = tc.Context.getProtocol(KnownProtocolKind::Hashable); + auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable); auto conformance = TypeChecker::conformsToProtocol( cs.getType(expr), hashable, cs.DC, @@ -6285,7 +5907,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, assert(conformance && "must conform to Hashable"); return cs.cacheType( - new (tc.Context) AnyHashableErasureExpr(expr, toType, *conformance)); + new (ctx) AnyHashableErasureExpr(expr, toType, conformance)); } case ConversionRestrictionKind::DictionaryUpcast: { @@ -6310,12 +5932,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto toEltType = unwrappedTy->getAnyPointerElementType(pointerKind); assert(toEltType && "not a pointer type?"); (void) toEltType; - tc.requirePointerArgumentIntrinsics(expr->getLoc()); + TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc()); Expr *result = - cs.cacheType(new (tc.Context) InOutToPointerExpr(expr, unwrappedTy)); + cs.cacheType(new (ctx) InOutToPointerExpr(expr, unwrappedTy)); if (isOptional) - result = cs.cacheType(new (tc.Context) - InjectIntoOptionalExpr(result, toType)); + result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType)); return result; } @@ -6327,12 +5948,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, unwrappedTy = unwrapped; } - tc.requirePointerArgumentIntrinsics(expr->getLoc()); + TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc()); Expr *result = - cs.cacheType(new (tc.Context) ArrayToPointerExpr(expr, unwrappedTy)); + cs.cacheType(new (ctx) ArrayToPointerExpr(expr, unwrappedTy)); if (isOptional) - result = cs.cacheType(new (tc.Context) - InjectIntoOptionalExpr(result, toType)); + result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType)); return result; } @@ -6344,51 +5964,50 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, unwrappedTy = unwrapped; } - tc.requirePointerArgumentIntrinsics(expr->getLoc()); + TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc()); Expr *result = - cs.cacheType(new (tc.Context) StringToPointerExpr(expr, unwrappedTy)); + cs.cacheType(new (ctx) StringToPointerExpr(expr, unwrappedTy)); if (isOptional) - result = cs.cacheType(new (tc.Context) - InjectIntoOptionalExpr(result, toType)); + result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType)); return result; } case ConversionRestrictionKind::PointerToPointer: { - tc.requirePointerArgumentIntrinsics(expr->getLoc()); + TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc()); Type unwrappedToTy = toType->getOptionalObjectType(); // Optional to optional. if (Type unwrappedFromTy = cs.getType(expr)->getOptionalObjectType()) { assert(unwrappedToTy && "converting optional to non-optional"); Expr *boundOptional = cs.cacheType( - new (tc.Context) BindOptionalExpr(expr, SourceLoc(), - /*depth*/ 0, unwrappedFromTy)); - Expr *converted = cs.cacheType(new (tc.Context) PointerToPointerExpr( - boundOptional, unwrappedToTy)); - Expr *rewrapped = cs.cacheType( - new (tc.Context) InjectIntoOptionalExpr(converted, toType)); - return cs.cacheType(new (tc.Context) + new (ctx) BindOptionalExpr(expr, SourceLoc(), + /*depth*/ 0, unwrappedFromTy)); + Expr *converted = cs.cacheType( + new (ctx) PointerToPointerExpr(boundOptional, unwrappedToTy)); + Expr *rewrapped = + cs.cacheType(new (ctx) InjectIntoOptionalExpr(converted, toType)); + return cs.cacheType(new (ctx) OptionalEvaluationExpr(rewrapped, toType)); } // Non-optional to optional. if (unwrappedToTy) { - Expr *converted = cs.cacheType( - new (tc.Context) PointerToPointerExpr(expr, unwrappedToTy)); - return cs.cacheType(new (tc.Context) + Expr *converted = + cs.cacheType(new (ctx) PointerToPointerExpr(expr, unwrappedToTy)); + return cs.cacheType(new (ctx) InjectIntoOptionalExpr(converted, toType)); } // Non-optional to non-optional. - return cs.cacheType(new (tc.Context) PointerToPointerExpr(expr, toType)); + return cs.cacheType(new (ctx) PointerToPointerExpr(expr, toType)); } case ConversionRestrictionKind::CFTollFreeBridgeToObjC: { auto foreignClass = fromType->getClassOrBoundGenericClass(); auto objcType = foreignClass->getAttrs().getAttribute() ->getObjCClass()->getDeclaredInterfaceType(); - auto asObjCClass = cs.cacheType( - new (tc.Context) ForeignObjectConversionExpr(expr, objcType)); + auto asObjCClass = + cs.cacheType(new (ctx) ForeignObjectConversionExpr(expr, objcType)); return coerceToType(asObjCClass, toType, locator); } @@ -6400,7 +6019,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, if (!result) return nullptr; - return cs.cacheType(new (tc.Context) + return cs.cacheType(new (ctx) ForeignObjectConversionExpr(result, toType)); } } @@ -6416,14 +6035,14 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto fromLValue = cast(desugaredFromType); auto toIO = toType->getAs(); if (!toIO) - return coerceToType(addImplicitLoadExpr(cs, expr), toType, locator); + return coerceToType(cs.addImplicitLoadExpr(expr), toType, locator); // In an 'inout' operator like "i += 1", the operand is converted from // an implicit lvalue to an inout argument. assert(toIO->getObjectType()->isEqual(fromLValue->getObjectType())); - return cs.cacheType(new (tc.Context) InOutExpr(expr->getStartLoc(), expr, - toIO->getObjectType(), - /*isImplicit*/ true)); + return cs.cacheType(new (ctx) InOutExpr(expr->getStartLoc(), expr, + toIO->getObjectType(), + /*isImplicit*/ true)); } // Coerce from a tuple to a tuple. @@ -6465,7 +6084,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, fromSuperClass; fromSuperClass = fromSuperClass->getSuperclass()) { if (fromSuperClass->isEqual(toType)) { - return coerceSuperclass(expr, toType, locator); + return coerceSuperclass(expr, toType); } } break; @@ -6506,28 +6125,27 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto escapingToFuncTy = toFunc->withExtInfo(toEI.withNoEscape(false)); maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc); - expr = cs.cacheType(new (tc.Context) FunctionConversionExpr( - expr, escapingToFuncTy)); + expr = cs.cacheType( + new (ctx) FunctionConversionExpr(expr, escapingToFuncTy)); } // Apply an explict function conversion *only* for the escape to // noescape conversion. This conversion will be stripped by the // default argument generator. (We can't return a @noescape function) - auto newExpr = cs.cacheType(new (tc.Context) - FunctionConversionExpr(expr, toFunc)); + auto newExpr = + cs.cacheType(new (ctx) FunctionConversionExpr(expr, toFunc)); return newExpr; } } maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc); - return cs.cacheType(new (tc.Context) - FunctionConversionExpr(expr, toType)); + return cs.cacheType(new (ctx) FunctionConversionExpr(expr, toType)); } // Coercions from one metatype to another. case TypeKind::Metatype: { if (auto toMeta = toType->getAs()) - return cs.cacheType(new(tc.Context) MetatypeConversionExpr(expr, toMeta)); + return cs.cacheType(new (ctx) MetatypeConversionExpr(expr, toMeta)); LLVM_FALLTHROUGH; } // Coercions from metatype to objects. @@ -6539,16 +6157,15 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, if (fromMeta->is()) { assert(fromMeta->getInstanceType()->mayHaveSuperclass() && "metatype-to-object input should be a class metatype"); - return cs.cacheType( - new (tc.Context) ClassMetatypeToObjectExpr(expr, toType)); + return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType)); } if (fromMeta->is()) { assert(fromMeta->getInstanceType()->getCanonicalType() ->getExistentialLayout().requiresClass() && "metatype-to-object input should be a class metatype"); - return cs.cacheType( - new (tc.Context) ExistentialMetatypeToObjectExpr(expr, toType)); + return cs.cacheType(new (ctx) + ExistentialMetatypeToObjectExpr(expr, toType)); } llvm_unreachable("unhandled metatype kind"); @@ -6564,8 +6181,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, && fromMeta->getInstanceType()->is() && "protocol-metatype-to-Protocol only works for single " "protocols"); - return cs.cacheType( - new (tc.Context) ProtocolMetatypeToObjectExpr(expr, toType)); + return cs.cacheType(new (ctx) + ProtocolMetatypeToObjectExpr(expr, toType)); } } @@ -6600,14 +6217,14 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, case TypeKind::ExistentialMetatype: case TypeKind::ProtocolComposition: case TypeKind::Protocol: - return coerceExistential(expr, toType, locator); + return coerceExistential(expr, toType); // Coercion to Optional. case TypeKind::BoundGenericEnum: { auto toGenericType = cast(desugaredToType); if (!toGenericType->getDecl()->isOptionalDecl()) break; - tc.requireOptionalIntrinsics(expr->getLoc()); + TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc()); if (cs.getType(expr)->getOptionalObjectType()) return coerceOptionalToOptional(expr, toType, locator, typeFromPattern); @@ -6616,8 +6233,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, expr = coerceToType(expr, valueType, locator); if (!expr) return nullptr; - auto *result = - cs.cacheType(new (tc.Context) InjectIntoOptionalExpr(expr, toType)); + auto *result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType)); diagnoseOptionalInjection(result); return result; } @@ -6653,13 +6269,11 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, // Unresolved types come up in diagnostics for lvalue and inout types. if (fromType->hasUnresolvedType() || toType->hasUnresolvedType()) - return cs.cacheType(new (tc.Context) - UnresolvedTypeConversionExpr(expr, toType)); + return cs.cacheType(new (ctx) UnresolvedTypeConversionExpr(expr, toType)); // Use an opaque type to abstract a value of the underlying concrete type. if (toType->getAs()) { - return cs.cacheType(new (tc.Context) - UnderlyingToOpaqueExpr(expr, toType)); + return cs.cacheType(new (ctx) UnderlyingToOpaqueExpr(expr, toType)); } llvm_unreachable("Unhandled coercion"); @@ -6738,7 +6352,7 @@ ExprRewriter::coerceObjectArgumentToType(Expr *expr, assert(fromType->is() && "Can only convert lvalues to inout"); - auto &ctx = cs.getTypeChecker().Context; + auto &ctx = cs.getASTContext(); // Use InOutExpr to convert it to an explicit inout argument for the // receiver. @@ -6765,37 +6379,36 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, // Check whether this literal type conforms to the builtin protocol. If so, // initialize via the builtin protocol. - Optional builtinConformance; - if (builtinProtocol && - (builtinConformance = - TypeChecker::conformsToProtocol(type, builtinProtocol, cs.DC, - ConformanceCheckFlags::InExpression))) { - - // Find the witness that we'll use to initialize the type via a builtin - // literal. - auto witness = builtinConformance->getWitnessByName(type->getRValueType(), - builtinLiteralFuncName); - if (!witness || !isa(witness.getDecl())) - return nullptr; + if (builtinProtocol) { + auto builtinConformance = TypeChecker::conformsToProtocol( + type, builtinProtocol, cs.DC, ConformanceCheckFlags::InExpression); + if (builtinConformance) { + // Find the witness that we'll use to initialize the type via a builtin + // literal. + auto witness = builtinConformance.getWitnessByName( + type->getRValueType(), builtinLiteralFuncName); + if (!witness || !isa(witness.getDecl())) + return nullptr; - // Form a reference to the builtin conversion function. + // Form a reference to the builtin conversion function. - // Set the builtin initializer. - if (auto stringLiteral = dyn_cast(literal)) - stringLiteral->setBuiltinInitializer(witness); - else if (auto booleanLiteral = dyn_cast(literal)) - booleanLiteral->setBuiltinInitializer(witness); - else if (auto numberLiteral = dyn_cast(literal)) - numberLiteral->setBuiltinInitializer(witness); - else { - cast(literal) - ->setBuiltinInitializer(witness); - } + // Set the builtin initializer. + if (auto stringLiteral = dyn_cast(literal)) + stringLiteral->setBuiltinInitializer(witness); + else if (auto booleanLiteral = dyn_cast(literal)) + booleanLiteral->setBuiltinInitializer(witness); + else if (auto numberLiteral = dyn_cast(literal)) + numberLiteral->setBuiltinInitializer(witness); + else { + cast(literal)->setBuiltinInitializer( + witness); + } - // The literal expression has this type. - cs.setType(literal, type); + // The literal expression has this type. + cs.setType(literal, type); - return literal; + return literal; + } } // This literal type must conform to the (non-builtin) protocol. @@ -6808,7 +6421,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, if (!literalType.empty()) { // Extract the literal type. Type builtinLiteralType = - conformance->getTypeWitnessByName(type, literalType); + conformance.getTypeWitnessByName(type, literalType); if (builtinLiteralType->hasError()) return nullptr; @@ -6821,8 +6434,8 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, } // Find the witness that we'll use to initialize the literal value. - auto witness = conformance->getWitnessByName(type->getRValueType(), - literalFuncName); + auto witness = + conformance.getWitnessByName(type->getRValueType(), literalFuncName); if (!witness || !isa(witness.getDecl())) return nullptr; @@ -6860,37 +6473,22 @@ static bool isValidDynamicCallableMethod(FuncDecl *method, return true; } -// Resolve `callAsFunction` method applications. -static Expr *finishApplyCallAsFunctionMethod( +// Build a reference to a `callAsFunction` method. +static Expr *buildCallAsFunctionMethodRef( ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected, - AnyFunctionType *openedMethodType, - ConstraintLocatorBuilder applyFunctionLoc) { - auto &cs = rewriter.cs; - auto *fn = apply->getFn(); - auto choice = selected.choice; + ConstraintLocator *calleeLoc) { + assert(calleeLoc->isLastElement()); + assert(cast(selected.choice.getDecl())->isCallAsFunctionMethod()); + // Create direct reference to `callAsFunction` method. - bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; + auto *fn = apply->getFn(); auto *declRef = rewriter.buildMemberRef( - fn, selected.openedFullType, /*dotLoc*/ SourceLoc(), choice, - DeclNameLoc(fn->getEndLoc()), selected.openedType, applyFunctionLoc, - applyFunctionLoc, /*implicit*/ true, choice.getFunctionRefKind(), - AccessSemantics::Ordinary, isDynamic); + fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(fn->getEndLoc()), + calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary); if (!declRef) return nullptr; declRef->setImplicit(apply->isImplicit()); - apply->setFn(declRef); - // Coerce argument to input type of the `callAsFunction` method. - SmallVector argLabelsScratch; - auto *arg = rewriter.coerceCallArguments( - apply->getArg(), openedMethodType, apply, - apply->getArgumentLabels(argLabelsScratch), apply->hasTrailingClosure(), - applyFunctionLoc); - if (!arg) - return nullptr; - apply->setArg(arg); - cs.setType(apply, openedMethodType->getResult()); - cs.cacheExprTypes(apply); - return apply; + return declRef; } // Resolve `@dynamicCallable` applications. @@ -6918,14 +6516,9 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, bool useKwargsMethod = argumentLabel == ctx.Id_withKeywordArguments; // Construct expression referencing the `dynamicallyCall` method. - bool isDynamic = - selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic; - auto member = buildMemberRef(fn, selected.openedFullType, - SourceLoc(), selected.choice, - DeclNameLoc(method->getNameLoc()), - selected.openedType, loc, loc, /*implicit*/ true, - selected.choice.getFunctionRefKind(), - AccessSemantics::Ordinary, isDynamic); + auto member = buildMemberRef(fn, SourceLoc(), selected, + DeclNameLoc(method->getNameLoc()), loc, loc, + /*implicit*/ true, AccessSemantics::Ordinary); // Construct argument to the method (either an array or dictionary // expression). @@ -6939,11 +6532,11 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, auto dictLitProto = ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); auto conformance = - cs.TC.conformsToProtocol(argumentType, dictLitProto, cs.DC, - ConformanceCheckFlags::InExpression); - auto keyType = conformance->getTypeWitnessByName(argumentType, ctx.Id_Key); - auto valueType = conformance->getTypeWitnessByName(argumentType, - ctx.Id_Value); + TypeChecker::conformsToProtocol(argumentType, dictLitProto, cs.DC, + ConformanceCheckFlags::InExpression); + auto keyType = conformance.getTypeWitnessByName(argumentType, ctx.Id_Key); + auto valueType = + conformance.getTypeWitnessByName(argumentType, ctx.Id_Value); SmallVector names; SmallVector dictElements; for (unsigned i = 0, n = arg->getNumElements(); i < n; i++) { @@ -6980,8 +6573,9 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, } Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, - ConstraintLocatorBuilder locator) { - TypeChecker &tc = cs.getTypeChecker(); + ConstraintLocatorBuilder locator, + ConstraintLocatorBuilder calleeLocator) { + auto &ctx = cs.getASTContext(); auto fn = apply->getFn(); @@ -6990,9 +6584,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto finishApplyOfDeclWithSpecialTypeCheckingSemantics = [&](ApplyExpr *apply, - ValueDecl *decl, + ConcreteDeclRef declRef, Type openedType) -> Expr* { - switch (cs.TC.getDeclTypeCheckingSemantics(decl)) { + switch (TypeChecker::getDeclTypeCheckingSemantics(declRef.getDecl())) { case DeclTypeCheckingSemantics::TypeOf: { // Resolve into a DynamicTypeExpr. auto arg = apply->getArg(); @@ -7000,7 +6594,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, SmallVector argLabelsScratch; auto fnType = cs.getType(fn)->getAs(); - arg = coerceCallArguments(arg, fnType, + arg = coerceCallArguments(arg, fnType, declRef, apply, apply->getArgumentLabels(argLabelsScratch), hasTrailingClosure, @@ -7013,7 +6607,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, if (auto tuple = dyn_cast(arg)) arg = tuple->getElements()[0]; - auto replacement = new (tc.Context) + auto replacement = new (ctx) DynamicTypeExpr(apply->getFn()->getLoc(), apply->getArg()->getStartLoc(), arg, @@ -7040,7 +6634,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, body = coerceToType(body, bodyFnTy, locator); assert(body && "can't make nonescaping?!"); - auto escapable = new (tc.Context) + auto escapable = new (ctx) OpaqueValueExpr(apply->getFn()->getSourceRange(), Type()); cs.setType(escapable, escapableParams[0].getOldType()); @@ -7048,15 +6642,15 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, return cs.getType(E); }; - auto callSubExpr = CallExpr::createImplicit(tc.Context, body, + auto callSubExpr = CallExpr::createImplicit(ctx, body, {escapable}, {}, getType); cs.cacheSubExprTypes(callSubExpr); cs.setType(callSubExpr->getArg(), - AnyFunctionType::composeInput(tc.Context, + AnyFunctionType::composeInput(ctx, escapableParams, false)); cs.setType(callSubExpr, resultType); - auto replacement = new (tc.Context) + auto replacement = new (ctx) MakeTemporarilyEscapableExpr(apply->getFn()->getLoc(), apply->getArg()->getStartLoc(), nonescaping, @@ -7099,18 +6693,18 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, ->isEqual(existentialInstanceTy)); auto opaqueValue = - new (tc.Context) OpaqueValueExpr(apply->getSourceRange(), openedTy); + new (ctx) OpaqueValueExpr(apply->getSourceRange(), openedTy); cs.setType(opaqueValue, openedTy); auto getType = [&](const Expr *E) -> Type { return cs.getType(E); }; - auto callSubExpr = CallExpr::createImplicit(tc.Context, body, {opaqueValue}, {}, getType); + auto callSubExpr = CallExpr::createImplicit(ctx, body, {opaqueValue}, {}, getType); cs.cacheSubExprTypes(callSubExpr); cs.setType(callSubExpr, resultTy); - auto replacement = new (tc.Context) + auto replacement = new (ctx) OpenExistentialExpr(existential, opaqueValue, callSubExpr, resultTy); cs.setType(replacement, resultTy); @@ -7124,7 +6718,24 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; - // Resolve `callAsFunction` and `@dynamicCallable` applications. + // Resolve the callee for the application if we have one. + ConcreteDeclRef callee; + auto *calleeLoc = cs.getConstraintLocator(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc); + if (overload) { + auto *decl = overload->choice.getDeclOrNull(); + callee = resolveConcreteDeclRef(decl, calleeLoc); + } + + // If this is an implicit call to a `callAsFunction` method, build the + // appropriate member reference. + if (cs.getType(fn)->getRValueType()->isCallableNominalType(dc)) { + fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc); + if (!fn) + return nullptr; + } + + // Resolve a `@dynamicCallable` application. auto applyFunctionLoc = locator.withPathElement(ConstraintLocator::ApplyFunction); if (auto selected = solution.getOverloadChoiceIfAvailable( @@ -7133,10 +6744,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto methodType = simplifyType(selected->openedType)->getAs(); if (method && methodType) { - if (method->isCallAsFunctionMethod()) - return finishApplyCallAsFunctionMethod( - *this, apply, *selected, methodType, applyFunctionLoc); - if (methodType && isValidDynamicCallableMethod(method, methodType)) + // Handle a call to a @dynamicCallable method. + if (isValidDynamicCallableMethod(method, methodType)) return finishApplyDynamicCallable( apply, *selected, method, methodType, applyFunctionLoc); } @@ -7150,7 +6759,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, dyn_cast(getSemanticExprForDeclOrMemberRef(fn))) { if (auto special = finishApplyOfDeclWithSpecialTypeCheckingSemantics(apply, - declRef->getDecl(), + declRef->getDeclRef(), openedType)) { return special; } @@ -7195,7 +6804,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, SmallVector argLabelsScratch; if (auto fnType = cs.getType(fn)->getAs()) { auto origArg = apply->getArg(); - Expr *arg = coerceCallArguments(origArg, fnType, + Expr *arg = coerceCallArguments(origArg, fnType, callee, apply, apply->getArgumentLabels(argLabelsScratch), hasTrailingClosure, @@ -7209,17 +6818,17 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, cs.setType(apply, fnType->getResult()); apply->setIsSuper(isSuper); - cs.setExprTypes(apply); - Expr *result = tc.substituteInputSugarTypeForResult(apply); + solution.setExprTypes(apply); + Expr *result = TypeChecker::substituteInputSugarTypeForResult(apply); cs.cacheExprTypes(result); // If we have a covariant result type, perform the conversion now. if (covariantResultType) { if (covariantResultType->is()) - result = cs.cacheType(new (tc.Context) CovariantFunctionConversionExpr( + result = cs.cacheType(new (ctx) CovariantFunctionConversionExpr( result, covariantResultType)); else - result = cs.cacheType(new (tc.Context) CovariantReturnConversionExpr( + result = cs.cacheType(new (ctx) CovariantReturnConversionExpr( result, covariantResultType)); } @@ -7253,9 +6862,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, // We're constructing a value of nominal type. Look for the constructor or // enum element to use. - auto ctorLocator = cs.getConstraintLocator( - locator.withPathElement(ConstraintLocator::ApplyFunction) - .withPathElement(ConstraintLocator::ConstructorMember)); + auto *ctorLocator = + cs.getConstraintLocator(locator, {ConstraintLocator::ApplyFunction, + ConstraintLocator::ConstructorMember}); auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator); if (!selected) { assert(ty->hasError() || ty->hasUnresolvedType()); @@ -7266,26 +6875,19 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, assert(ty->getNominalOrBoundGenericNominal() || ty->is() || ty->isExistentialType() || ty->is()); - // We have the constructor. - auto choice = selected->choice; - // Consider the constructor decl reference expr 'implicit', but the // constructor call expr itself has the apply's 'implicitness'. - bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; - Expr *declRef = buildMemberRef(fn, selected->openedFullType, - /*dotLoc=*/SourceLoc(), choice, - DeclNameLoc(fn->getEndLoc()), - selected->openedType, locator, ctorLocator, - /*Implicit=*/true, - choice.getFunctionRefKind(), - AccessSemantics::Ordinary, isDynamic); + Expr *declRef = buildMemberRef(fn, /*dotLoc=*/SourceLoc(), *selected, + DeclNameLoc(fn->getEndLoc()), locator, + ctorLocator, /*Implicit=*/true, + AccessSemantics::Ordinary); if (!declRef) return nullptr; declRef->setImplicit(apply->isImplicit()); apply->setFn(declRef); // Tail-recur to actually call the constructor. - return finishApply(apply, openedType, locator); + return finishApply(apply, openedType, locator, ctorLocator); } // Return the precedence-yielding parent of 'expr', along with the index of @@ -7354,13 +6956,13 @@ static std::pair getPrecedenceParentAndIndex(Expr *expr, /// parentheses must be added around "" to allow the new operator /// to bind correctly. bool swift::exprNeedsParensInsideFollowingOperator( - TypeChecker &TC, DeclContext *DC, Expr *expr, + DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG) { if (expr->isInfixOperator()) { auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, expr); if (!exprPG) return true; - return TC.Context.associateInfixOperators(exprPG, followingPG) + return DC->getASTContext().associateInfixOperators(exprPG, followingPG) != Associativity::Left; } @@ -7377,7 +6979,7 @@ bool swift::exprNeedsParensInsideFollowingOperator( /// the new operator to prevent it from binding incorrectly in the /// surrounding context. bool swift::exprNeedsParensOutsideFollowingOperator( - TypeChecker &TC, DeclContext *DC, Expr *expr, Expr *rootExpr, + DeclContext *DC, Expr *expr, Expr *rootExpr, PrecedenceGroupDecl *followingPG) { Expr *parent; unsigned index; @@ -7396,11 +6998,12 @@ bool swift::exprNeedsParensOutsideFollowingOperator( if (!parentPG) return true; // If the index is 0, this is on the LHS of the parent. + auto &Context = DC->getASTContext(); if (index == 0) { - return TC.Context.associateInfixOperators(followingPG, parentPG) + return Context.associateInfixOperators(followingPG, parentPG) != Associativity::Left; } else { - return TC.Context.associateInfixOperators(parentPG, followingPG) + return Context.associateInfixOperators(parentPG, followingPG) != Associativity::Right; } } @@ -7408,24 +7011,22 @@ bool swift::exprNeedsParensOutsideFollowingOperator( return true; } -bool swift::exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC, - DeclContext *DC, +bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC, Expr *expr) { auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc()); if (!asPG) return true; - return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG); + return exprNeedsParensInsideFollowingOperator(DC, expr, asPG); } -bool swift::exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC, - DeclContext *DC, +bool swift::exprNeedsParensAfterAddingNilCoalescing(DeclContext *DC, Expr *expr, Expr *rootExpr) { auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc()); if (!asPG) return true; - return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr, asPG); + return exprNeedsParensOutsideFollowingOperator(DC, expr, rootExpr, asPG); } namespace { @@ -7450,31 +7051,36 @@ namespace { if (auto closure = dyn_cast(expr)) { Rewriter.simplifyExprType(expr); auto &cs = Rewriter.getConstraintSystem(); - auto &tc = cs.getTypeChecker(); // Coerce the pattern, in case we resolved something. auto fnType = cs.getType(closure)->castTo(); auto *params = closure->getParameters(); - tc.coerceParameterListToType(params, closure, fnType); - - // If this closure had a function builder applied, rewrite it to a - // closure with a single expression body containing the builder - // invocations. - auto builder = - Rewriter.solution.builderTransformedClosures.find(closure); - if (builder != Rewriter.solution.builderTransformedClosures.end()) { - auto singleExpr = builder->second.singleExpr; - auto returnStmt = new (tc.Context) ReturnStmt( - singleExpr->getStartLoc(), singleExpr, /*implicit=*/true); - auto braceStmt = BraceStmt::create( - tc.Context, returnStmt->getStartLoc(), ASTNode(returnStmt), - returnStmt->getEndLoc(), /*implicit=*/true); - closure->setBody(braceStmt, /*isSingleExpression=*/true); - } + TypeChecker::coerceParameterListToType(params, closure, fnType); + + if (auto transform = + Rewriter.getAppliedBuilderTransform(closure)) { + // Apply the function builder to the closure. We want to be in the + // context of the closure for subsequent transforms. + llvm::SaveAndRestore savedDC(Rewriter.dc, closure); + auto newBody = applyFunctionBuilderTransform( + Rewriter.solution, *transform, closure->getBody(), closure, + [&](Expr *expr) { + Expr *result = expr->walk(*this); + if (result) + Rewriter.solution.setExprTypes(result); + return result; + }, + [&](Expr *expr, Type toType, ConstraintLocator *locator) { + return Rewriter.coerceToType(expr, toType, locator); + }); + closure->setBody(newBody, /*isSingleExpression=*/false); + closure->setAppliedFunctionBuilder(); + + Rewriter.solution.setExprTypes(closure); + } else if (closure->hasSingleExpressionBody()) { + // If this is a single-expression closure, convert the expression + // in the body to the result type of the closure. - // If this is a single-expression closure, convert the expression - // in the body to the result type of the closure. - if (closure->hasSingleExpressionBody()) { // Enter the context of the closure when type-checking the body. llvm::SaveAndRestore savedDC(Rewriter.dc, closure); Expr *body = closure->getSingleExpressionBody()->walk(*this); @@ -7509,16 +7115,10 @@ namespace { } else { // For other closures, type-check the body once we've finished with // the expression. - cs.setExprTypes(closure); + Rewriter.solution.setExprTypes(closure); ClosuresToTypeCheck.push_back(closure); } - // Don't try to register captures if constraint system is used to - // produce diagnostics for one of the sub-expressions. - if (!cs.Options.contains( - ConstraintSystemFlags::SubExpressionDiagnostics)) - tc.ClosuresWithUncomputedCaptures.push_back(closure); - return { false, closure }; } @@ -7547,112 +7147,100 @@ namespace { } // end anonymous namespace Expr *ConstraintSystem::coerceToRValue(Expr *expr) { - auto &tc = getTypeChecker(); - return tc.coerceToRValue(expr, - [&](Expr *expr) { - return getType(expr); - }, - [&](Expr *expr, Type type) { - setType(expr, type); - }); + return TypeChecker::coerceToRValue( + getASTContext(), expr, [&](Expr *expr) { return getType(expr); }, + [&](Expr *expr, Type type) { setType(expr, type); }); } -/// Emit the fixes computed as part of the solution, returning true if we were -/// able to emit an error message, or false if none of the fixits worked out. -bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) { - // First transfer all of the deduced information back - // to the constraint system. - applySolution(solution); - - class DiagnosticWalker : public ASTWalker { - Expr *root; - const Solution &solution; - llvm::SmallDenseMap> fixesPerExpr; - - /// Determines whether any error have been diagnosed while - /// trying to apply fixes associated with a given solution. - bool DiagnosedAnyErrors = false; +namespace { + /// Function object to compare source locations, putting invalid + /// locations at the end. + class CompareExprSourceLocs { + SourceManager &sourceMgr; public: - DiagnosticWalker(Expr *expr, const Solution &solution) - : root(expr), solution(solution) { - for (auto *fix : solution.Fixes) - fixesPerExpr[fix->getAnchor()].push_back(fix); - } - - std::pair walkToExprPre(Expr *E) override { - // Diagnose root expression last. - if (E == root) - return {true, E}; - - if (auto *closure = dyn_cast(E)) { - auto result = solution.builderTransformedClosures.find(closure); - if (result != solution.builderTransformedClosures.end()) { - auto *transformedExpr = result->second.singleExpr; - // Since this closure has been transformed into something - // else let's look inside transformed expression instead. - transformedExpr->walk(*this); - return {false, E}; - } + explicit CompareExprSourceLocs(SourceManager &sourceMgr) + : sourceMgr(sourceMgr) { } + + bool operator()(Expr *lhs, Expr *rhs) const { + if (static_cast(lhs) != static_cast(rhs)) { + return static_cast(lhs); } - diagnose(E); - return {true, E}; - } + auto lhsLoc = lhs->getLoc(); + auto rhsLoc = rhs->getLoc(); + if (lhsLoc.isValid() != rhsLoc.isValid()) + return lhsLoc.isValid(); - Expr *walkToExprPost(Expr *E) override { - if (E == root) - diagnose(E); - return E; - } - - std::pair walkToStmtPre(Stmt *S) override { - return {true, S}; + return sourceMgr.isBeforeInBuffer(lhsLoc, rhsLoc); } + }; - bool hadErrors() const { return DiagnosedAnyErrors; } +} - private: - void diagnose(Expr *E) { - auto fixes = fixesPerExpr.find(E); - if (fixes == fixesPerExpr.end()) - return; +/// Emit the fixes computed as part of the solution, returning true if we were +/// able to emit an error message, or false if none of the fixits worked out. +bool ConstraintSystem::applySolutionFixes(const Solution &solution) { + /// Collect the fixes on a per-expression basis. + llvm::SmallDenseMap> fixesPerExpr; + for (auto *fix : solution.Fixes) { + fixesPerExpr[fix->getAnchor()].push_back(fix); + } - for (const auto *fix : fixes->second) { - auto diagnosed = fix->diagnose(root); - if (fix->isWarning()) { + // Collect all of the expressions that have fixes, and sort them by + // source ordering. + SmallVector exprsWithFixes; + for (const auto &fix : fixesPerExpr) { + exprsWithFixes.push_back(fix.getFirst()); + } + std::sort(exprsWithFixes.begin(), exprsWithFixes.end(), + CompareExprSourceLocs(Context.SourceMgr)); + + // Walk over each of the expressions, diagnosing fixes. + bool diagnosedAnyErrors = false; + + for (auto expr : exprsWithFixes) { + // Coalesce fixes with the same locator to avoid duplicating notes. + auto fixes = fixesPerExpr[expr]; + + using ConstraintFixVector = llvm::SmallVector; + llvm::SmallMapVector, 4> aggregatedFixes; + for (auto *fix : fixes) + aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix); + + for (auto fixesPerLocator : aggregatedFixes) { + for (auto fixesPerKind : fixesPerLocator.second) { + auto fixes = fixesPerKind.second; + auto *primaryFix = fixes[0]; + ArrayRef secondaryFixes{fixes.begin() + 1, fixes.end()}; + + auto diagnosed = primaryFix->coalesceAndDiagnose(secondaryFixes); + if (primaryFix->isWarning()) { assert(diagnosed && "warnings should always be diagnosed"); (void)diagnosed; } else { - DiagnosedAnyErrors |= diagnosed; + diagnosedAnyErrors |= diagnosed; } } } - }; + } - DiagnosticWalker diagnostics(E, solution); - E->walk(diagnostics); - return diagnostics.hadErrors(); + return diagnosedAnyErrors; } /// Apply a given solution to the expression, producing a fully /// type-checked expression. -Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool skipClosures) { - // Add the node types back. - for (auto &nodeType : solution.addedNodeTypes) { - setType(nodeType.first, nodeType.second); - } - +Optional ConstraintSystem::applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { if (shouldSuppressDiagnostics()) - return nullptr; + return None; - bool diagnosedErrorsViaFixes = applySolutionFixes(expr, solution); + bool diagnosedErrorsViaFixes = applySolutionFixes(solution); // If all of the available fixes would result in a warning, // we can go ahead and apply this solution to AST. if (!llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) { @@ -7660,74 +7248,114 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, })) { // If we already diagnosed any errors via fixes, that's it. if (diagnosedErrorsViaFixes) - return nullptr; + return None; // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. - diagnoseFailureForExpr(expr); - return nullptr; + diagnoseFailureFor(target); + return None; } } ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics()); ExprWalker walker(rewriter); - // Apply the solution to the expression. - auto result = expr->walk(walker); - if (!result) - return nullptr; + // Apply the solution to the target. + SolutionApplicationTarget result = target; + if (auto expr = target.getAsExpr()) { + Expr *rewrittenExpr = expr->walk(walker); + if (!rewrittenExpr) + return None; + + result.setExpr(rewrittenExpr); + } else { + auto fn = *target.getAsFunction(); + + // Dig out the function builder transformation we applied. + auto transform = rewriter.getAppliedBuilderTransform(fn); + assert(transform); + + auto newBody = applyFunctionBuilderTransform( + solution, *transform, fn.getBody(), fn.getAsDeclContext(), + [&](Expr *expr) { + Expr *result = expr->walk(walker); + if (result) + solution.setExprTypes(result); + return result; + }, + [&](Expr *expr, Type toType, ConstraintLocator *locator) { + return rewriter.coerceToType(expr, toType, locator); + }); + + if (!newBody) + return None; + + result.setFunctionBody(newBody); + } // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. - if (!skipClosures) { - auto &tc = getTypeChecker(); + if (!performingDiagnostics) { bool hadError = false; for (auto *closure : walker.getClosuresToTypeCheck()) - hadError |= tc.typeCheckClosureBody(closure); + hadError |= TypeChecker::typeCheckClosureBody(closure); // Tap expressions too; they should or should not be // type-checked under the same conditions as closure bodies. for (auto tuple : walker.getTapsToTypeCheck()) { auto tap = std::get<0>(tuple); auto tapDC = std::get<1>(tuple); - hadError |= tc.typeCheckTapBody(tap, tapDC); + hadError |= TypeChecker::typeCheckTapBody(tap, tapDC); } // If any of them failed to type check, bail. if (hadError) - return nullptr; + return None; } - // We are supposed to use contextual type only if it is present and - // this expression doesn't represent the implicit return of the single - // expression function which got deduced to be `Never`. - auto shouldCoerceToContextualType = [&]() { - return convertType && !(getType(result)->isUninhabited() && - getContextualTypePurpose() == CTP_ReturnSingleExpr); - }; + if (auto resultExpr = result.getAsExpr()) { + Expr *expr = target.getAsExpr(); + assert(expr && "Can't have expression result without expression target"); + + // We are supposed to use contextual type only if it is present and + // this expression doesn't represent the implicit return of the single + // expression function which got deduced to be `Never`. + Type convertType = target.getExprConversionType(); + auto shouldCoerceToContextualType = [&]() { + return convertType && + !(getType(resultExpr)->isUninhabited() && + getContextualTypePurpose(target.getAsExpr()) + == CTP_ReturnSingleExpr); + }; - // If we're supposed to convert the expression to some particular type, - // do so now. - if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(result, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; - } else if (getType(result)->hasLValueType() && !discardedExpr) { - // We referenced an lvalue. Load it. - result = rewriter.coerceToType(result, getType(result)->getRValueType(), - getConstraintLocator(expr)); + // If we're supposed to convert the expression to some particular type, + // do so now. + if (shouldCoerceToContextualType()) { + resultExpr = rewriter.coerceToType(resultExpr, + simplifyType(convertType), + getConstraintLocator(expr)); + } else if (getType(resultExpr)->hasLValueType() && + !target.isDiscardedExpr()) { + // We referenced an lvalue. Load it. + resultExpr = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); + } + + if (!resultExpr) + return None; + + solution.setExprTypes(resultExpr); + result.setExpr(resultExpr); } - if (result) - rewriter.finalize(result); + rewriter.finalize(); return result; } Expr *Solution::coerceToType(Expr *expr, Type toType, ConstraintLocator *locator, - bool ignoreTopLevelInjection, Optional typeFromPattern) const { auto &cs = getConstraintSystem(); ExprRewriter rewriter(cs, *this, /*suppressDiagnostics=*/false); @@ -7735,15 +7363,173 @@ Expr *Solution::coerceToType(Expr *expr, Type toType, if (!result) return nullptr; - // If we were asked to ignore top-level optional injections, mark - // the top-level injection (if any) as "diagnosed". - if (ignoreTopLevelInjection) { - if (auto injection = dyn_cast( - result->getSemanticsProvidingExpr())) { - rewriter.DiagnosedOptionalInjections.insert(injection); + setExprTypes(result); + rewriter.finalize(); + return result; +} + +namespace { +class SetExprTypes : public ASTWalker { + const Solution &solution; + +public: + explicit SetExprTypes(const Solution &solution) + : solution(solution) {} + + Expr *walkToExprPost(Expr *expr) override { + auto &cs = solution.getConstraintSystem(); + auto exprType = cs.getType(expr); + exprType = solution.simplifyType(exprType); + // assert((!expr->getType() || expr->getType()->isEqual(exprType)) && + // "Mismatched types!"); + assert(!exprType->hasTypeVariable() && + "Should not write type variable into expression!"); + expr->setType(exprType); + + if (auto kp = dyn_cast(expr)) { + for (auto i : indices(kp->getComponents())) { + Type componentType; + if (cs.hasType(kp, i)) { + componentType = solution.simplifyType(cs.getType(kp, i)); + assert(!componentType->hasTypeVariable() && + "Should not write type variable into key-path component"); + } + + kp->getMutableComponents()[i].setComponentType(componentType); + } } + + return expr; + } + + /// Ignore statements. + std::pair walkToStmtPre(Stmt *stmt) override { + return { false, stmt }; } - rewriter.finalize(result); + /// Ignore declarations. + bool walkToDeclPre(Decl *decl) override { return false; } +}; +} + +ProtocolConformanceRef Solution::resolveConformance( + ConstraintLocator *locator, ProtocolDecl *proto) { + for (const auto &conformance : Conformances) { + if (conformance.first != locator) + continue; + if (conformance.second.getRequirement() != proto) + continue; + + // If the conformance doesn't require substitution, return it immediately. + auto conformanceRef = conformance.second; + if (conformanceRef.isAbstract()) + return conformanceRef; + + auto concrete = conformanceRef.getConcrete(); + auto conformingType = concrete->getType(); + if (!conformingType->hasTypeVariable()) + return conformanceRef; + + // Substitute into the conformance type, then look for a conformance + // again. + // FIXME: Should be able to perform the substitution using the Solution + // itself rather than another conforms-to-protocol check. + Type substConformingType = simplifyType(conformingType); + return TypeChecker::conformsToProtocol( + substConformingType, proto, constraintSystem->DC, + ConformanceCheckFlags::InExpression); + } + + return ProtocolConformanceRef::forInvalid(); +} + +Type Solution::getType(const Expr *expr) const { + auto result = llvm::find_if( + addedNodeTypes, [&](const std::pair &node) -> bool { + if (auto *e = node.first.dyn_cast()) + return expr == e; + return false; + }); + + if (result != addedNodeTypes.end()) + return result->second; + + auto &cs = getConstraintSystem(); + return cs.getType(expr); +} + +void Solution::setExprTypes(Expr *expr) const { + if (!expr) + return; + + SetExprTypes SET(*this); + expr->walk(SET); +} + +/// MARK: SolutionResult implementation. + +SolutionResult SolutionResult::forSolved(Solution &&solution) { + SolutionResult result(Kind::Success); + result.solutions = new Solution(std::move(solution)); + result.numSolutions = 1; + return result; +} + +SolutionResult SolutionResult::forAmbiguous( + MutableArrayRef solutions) { + assert(solutions.size() > 1 && "Not actually ambiguous"); + SolutionResult result(Kind::Ambiguous); + result.solutions = + (Solution *)malloc(sizeof(Solution) * solutions.size()); + result.numSolutions = solutions.size(); + std::uninitialized_copy(std::make_move_iterator(solutions.begin()), + std::make_move_iterator(solutions.end()), + result.solutions); return result; } + +SolutionResult::~SolutionResult() { + assert((!requiresDiagnostic() || emittedDiagnostic) && + "SolutionResult was destroyed without emitting a diagnostic"); + + for (unsigned i : range(numSolutions)) { + solutions[i].~Solution(); + } + free(solutions); +} + +const Solution &SolutionResult::getSolution() const { + assert(numSolutions == 1 && "Wrong number of solutions"); + return solutions[0]; +} + +Solution &&SolutionResult::takeSolution() && { + assert(numSolutions == 1 && "Wrong number of solutions"); + return std::move(solutions[0]); +} + +ArrayRef SolutionResult::getAmbiguousSolutions() const { + assert(getKind() == Ambiguous); + return makeArrayRef(solutions, numSolutions); +} + +MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { + assert(getKind() == Ambiguous); + markAsDiagnosed(); + return MutableArrayRef(solutions, numSolutions); +} + +SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { + switch (kind) { + case Kind::expression: { + SolutionApplicationTarget result = *this; + result.setExpr(getAsExpr()->walk(walker)); + return result; + } + + case Kind::function: + return SolutionApplicationTarget( + *getAsFunction(), + cast_or_null(getFunctionBody()->walk(walker))); + } +} diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 1bfe0772bcf3c..707d57504f251 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -21,6 +21,66 @@ using namespace swift; using namespace constraints; +void ConstraintSystem::inferTransitiveSupertypeBindings( + const llvm::SmallDenseMap + &inferredBindings, + PotentialBindings &bindings) { + auto *typeVar = bindings.TypeVar; + + llvm::SmallVector subtypeOf; + // First, let's collect all of the `subtype` constraints associated + // with this type variable. + llvm::copy_if(bindings.Sources, std::back_inserter(subtypeOf), + [&](const Constraint *constraint) -> bool { + if (constraint->getKind() != ConstraintKind::Subtype) + return false; + + auto rhs = simplifyType(constraint->getSecondType()); + return rhs->getAs() == typeVar; + }); + + if (subtypeOf.empty()) + return; + + // We need to make sure that there are no duplicate bindings in the + // set, other we'll produce multiple identical solutions. + llvm::SmallPtrSet existingTypes; + for (const auto &binding : bindings.Bindings) + existingTypes.insert(binding.BindingType->getCanonicalType()); + + for (auto *constraint : subtypeOf) { + auto *tv = + simplifyType(constraint->getFirstType())->getAs(); + if (!tv) + continue; + + auto relatedBindings = inferredBindings.find(tv); + if (relatedBindings == inferredBindings.end()) + continue; + + for (auto &binding : relatedBindings->getSecond().Bindings) { + // We need the binding kind for the potential binding to + // either be Exact or Supertypes in order for it to make sense + // to add Supertype bindings based on the relationship between + // our type variables. + if (binding.Kind != AllowedBindingKind::Exact && + binding.Kind != AllowedBindingKind::Supertypes) + continue; + + auto type = binding.BindingType; + + if (!existingTypes.insert(type->getCanonicalType()).second) + continue; + + if (ConstraintSystem::typeVarOccursInType(typeVar, type)) + continue; + + bindings.addPotentialBinding( + binding.withSameSource(type, AllowedBindingKind::Supertypes)); + } + } +} + Optional ConstraintSystem::determineBestBindings() { // Look for potential type variable bindings. @@ -44,48 +104,10 @@ ConstraintSystem::determineBestBindings() { continue; auto &bindings = cachedBindings->getSecond(); - // All of the relevant relational constraints associated with - // current type variable should be recored by its potential bindings. - for (auto *constraint : bindings.Sources) { - if (constraint->getKind() != ConstraintKind::Subtype) - continue; - auto lhs = simplifyType(constraint->getFirstType()); - auto rhs = simplifyType(constraint->getSecondType()); - - // We are only interested in 'subtype' constraints which have - // type variable on the left-hand side. - if (rhs->getAs() != typeVar) - continue; - - auto *tv = lhs->getAs(); - if (!tv) - continue; - - auto relatedBindings = cache.find(tv); - if (relatedBindings == cache.end()) - continue; + inferTransitiveSupertypeBindings(cache, bindings); - for (auto &binding : relatedBindings->getSecond().Bindings) { - // We need the binding kind for the potential binding to - // either be Exact or Supertypes in order for it to make sense - // to add Supertype bindings based on the relationship between - // our type variables. - if (binding.Kind != AllowedBindingKind::Exact && - binding.Kind != AllowedBindingKind::Supertypes) - continue; - - auto type = binding.BindingType; - - if (ConstraintSystem::typeVarOccursInType(typeVar, type)) - continue; - - bindings.addPotentialBinding( - {type, AllowedBindingKind::Supertypes, binding.BindingSource}); - } - } - - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); bindings.dump(typeVar, log, solverState->depth * 2); } @@ -143,8 +165,8 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding( !binding.BindingType->hasUnresolvedType() && !binding.BindingType->hasTypeVariable() && !binding.BindingType->hasUnboundGenericType() && - !binding.DefaultedProtocol && !binding.isDefaultableBinding() && - allowJoinMeet) { + !binding.hasDefaultedLiteralProtocol() && + !binding.isDefaultableBinding() && allowJoinMeet) { if (lastSupertypeIndex) { auto &lastBinding = Bindings[*lastSupertypeIndex]; auto lastType = lastBinding.BindingType->getWithoutSpecifierType(); @@ -164,7 +186,7 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding( lastSupertypeIndex = Bindings.size(); } - if (auto *literalProtocol = binding.DefaultedProtocol) + if (auto *literalProtocol = binding.getDefaultedLiteralProtocol()) foundLiteralBinding(literalProtocol); // If the type variable can't bind to an lvalue, make sure the @@ -206,8 +228,32 @@ bool ConstraintSystem::PotentialBindings::isViable( return true; } +bool ConstraintSystem::PotentialBindings::favoredOverDisjunction( + Constraint *disjunction) const { + if (IsHole || FullyBound) + return false; + + // If this bindings are for a closure and there are no holes, + // it shouldn't matter whether it there are any type variables + // or not because e.g. parameter type can have type variables, + // but we still want to resolve closure body early (instead of + // attempting any disjunction) to gain additional contextual + // information. + if (TypeVar->getImpl().isClosureType()) { + auto boundType = disjunction->getNestedConstraints()[0]->getFirstType(); + // If disjunction is attempting to bind a type variable, let's + // favor closure because it would add additional context, otherwise + // if it's something like a collection (where it has to pick + // between a conversion and bridging conversion) or concrete + // type let's prefer the disjunction. + return boundType->is(); + } + + return !InvolvesTypeVariables; +} + static bool hasNilLiteralConstraint(TypeVariableType *typeVar, - ConstraintSystem &CS) { + const ConstraintSystem &CS) { // Look for a literal-conformance constraint on the type variable. auto constraints = CS.getConstraintGraph().gatherConstraints( @@ -230,7 +276,7 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( PotentialBindings &result, Constraint *constraint, bool &hasDependentMemberRelationalConstraints, bool &hasNonDependentMemberRelationalConstraints, - bool &addOptionalSupertypeBindings) { + bool &addOptionalSupertypeBindings) const { assert(constraint->getClassification() == ConstraintClassification::Relational && "only relational constraints handled here"); @@ -331,6 +377,11 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( result.InvolvesTypeVariables = true; + if (constraint->getKind() == ConstraintKind::Subtype && + kind == AllowedBindingKind::Subtypes) { + result.SubtypeOf.insert(bindingTypeVar); + } + // If we've already set addOptionalSupertypeBindings, or we aren't // allowing supertype bindings, we're done. if (addOptionalSupertypeBindings || kind != AllowedBindingKind::Supertypes) @@ -371,14 +422,14 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint( kind = AllowedBindingKind::Exact; } - return PotentialBinding{type, kind, constraint->getKind()}; + return PotentialBinding{type, kind, constraint}; } /// Retrieve the set of potential type bindings for the given /// representative type variable, along with flags indicating whether /// those types should be opened. ConstraintSystem::PotentialBindings -ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { +ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { assert(typeVar->getImpl().getRepresentative(nullptr) == typeVar && "not a representative"); assert(!typeVar->getImpl().getFixedType(nullptr) && "has a fixed type"); @@ -403,7 +454,6 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { SmallVector defaultableConstraints; SmallVector literalBindings; bool addOptionalSupertypeBindings = false; - auto &tc = getTypeChecker(); bool hasNonDependentMemberRelationalConstraints = false; bool hasDependentMemberRelationalConstraints = false; for (auto constraint : constraints) { @@ -467,9 +517,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { path.back().getKind() == ConstraintLocator::ClosureResult && binding->Kind == AllowedBindingKind::Supertypes && exactTypes.insert(voidType).second) { - result.addPotentialBinding( - {voidType, binding->Kind, constraint->getKind()}, - /*allowJoinMeet=*/false); + result.addPotentialBinding({voidType, binding->Kind, constraint}, + /*allowJoinMeet=*/false); } } } @@ -505,6 +554,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { } case ConstraintKind::Defaultable: + case ConstraintKind::DefaultClosureType: // Do these in a separate pass. if (getFixedTypeRecursive(constraint->getFirstType(), true) ->getAs() == typeVar) { @@ -517,13 +567,21 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { // FIXME: Recurse into these constraints to see whether this // type variable is fully bound by any of them. result.InvolvesTypeVariables = true; + + // If there is additional context available via disjunction + // associated with closure literal (e.g. coercion to some other + // type) let's delay resolving the closure until the disjunction + // is attempted. + if (typeVar->getImpl().isClosureType()) + return {typeVar}; + break; case ConstraintKind::ConformsTo: case ConstraintKind::SelfObjectOfProtocol: // Swift 3 allowed the use of default types for normal conformances // to expressible-by-literal protocols. - if (tc.Context.LangOpts.EffectiveLanguageVersion[0] >= 4) + if (getASTContext().LangOpts.EffectiveLanguageVersion[0] >= 4) continue; if (!constraint->getSecondType()->is()) @@ -541,7 +599,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { // If there is a default literal type for this protocol, it's a // potential binding. - auto defaultType = tc.getDefaultType(constraint->getProtocol(), DC); + auto defaultType = TypeChecker::getDefaultType(constraint->getProtocol(), DC); if (!defaultType) continue; @@ -552,9 +610,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { if (!exactTypes.insert(defaultType->getCanonicalType()).second) continue; - literalBindings.push_back({defaultType, AllowedBindingKind::Subtypes, - constraint->getKind(), - constraint->getProtocol()}); + literalBindings.push_back( + {defaultType, AllowedBindingKind::Subtypes, constraint}); continue; } @@ -579,9 +636,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { if (!matched) { exactTypes.insert(defaultType->getCanonicalType()); - literalBindings.push_back({defaultType, AllowedBindingKind::Subtypes, - constraint->getKind(), - constraint->getProtocol()}); + literalBindings.push_back( + {defaultType, AllowedBindingKind::Subtypes, constraint}); } break; @@ -614,6 +670,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: // If our type variable shows up in the base type, there's // nothing to do. // FIXME: Can we avoid simplification here? @@ -678,7 +735,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { // might be covered by non-defaulted bindings. bool updatedBindingType = false; for (auto &literalBinding : literalBindings) { - auto *protocol = literalBinding.DefaultedProtocol; + auto *protocol = literalBinding.getDefaultedLiteralProtocol(); assert(protocol); @@ -717,7 +774,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { } for (auto &literalBinding : literalBindings) { - auto *protocol = literalBinding.DefaultedProtocol; + auto *protocol = literalBinding.getDefaultedLiteralProtocol(); // For any literal type that has been covered, skip them. if (coveredLiteralProtocols.count(protocol) == 0) result.addPotentialBinding(std::move(literalBinding)); @@ -730,9 +787,31 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { if (!exactTypes.insert(type->getCanonicalType()).second) continue; - result.addPotentialBinding({type, AllowedBindingKind::Exact, - constraint->getKind(), nullptr, - constraint->getLocator()}); + if (constraint->getKind() == ConstraintKind::DefaultClosureType) { + // If there are no other possible bindings for this closure + // let's default it to the type inferred from its parameters/body, + // otherwise we should only attempt contextual types as a + // top-level closure type. + if (!result.Bindings.empty()) + continue; + } + + result.addPotentialBinding({type, AllowedBindingKind::Exact, constraint}); + } + + // If there are no bindings, typeVar may be a hole. + if (shouldAttemptFixes() && result.Bindings.empty() && + typeVar->getImpl().canBindToHole()) { + result.IsHole = true; + // If the base of the unresolved member reference like `.foo` + // couldn't be resolved we'd want to bind it to a hole at the + // very last moment possible, just like generic parameters. + auto *locator = typeVar->getImpl().getLocator(); + if (locator->isLastElement()) + result.PotentiallyIncomplete = true; + + result.addPotentialBinding( + PotentialBinding::forHole(getASTContext(), locator)); } // Determine if the bindings only constrain the type variable from above with @@ -796,7 +875,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) { /// /// \returns the type to bind to, if the binding is okay. Optional ConstraintSystem::checkTypeOfBinding(TypeVariableType *typeVar, - Type type) { + Type type) const { // Simplify the type. type = simplifyType(type); @@ -903,11 +982,11 @@ bool TypeVarBindingProducer::computeNext() { // If we have a protocol with a default type, look for alternative // types to the default. - if (NumTries == 0 && binding.DefaultedProtocol) { - auto knownKind = *(binding.DefaultedProtocol->getKnownProtocolKind()); + if (NumTries == 0 && binding.hasDefaultedLiteralProtocol()) { + auto knownKind = + *(binding.getDefaultedLiteralProtocol()->getKnownProtocolKind()); for (auto altType : CS.getAlternativeLiteralTypes(knownKind)) { - addNewBinding({altType, BindingKind::Subtypes, binding.BindingSource, - binding.DefaultedProtocol}); + addNewBinding(binding.withSameSource(altType, BindingKind::Subtypes)); } } @@ -924,10 +1003,10 @@ bool TypeVarBindingProducer::computeNext() { if (auto otherTypeVar = objTy->getAs()) { if (TypeVar->getImpl().canBindToLValue() == otherTypeVar->getImpl().canBindToLValue()) { - addNewBinding({objTy, binding.Kind, binding.BindingSource}); + addNewBinding(binding.withSameSource(objTy, binding.Kind)); } } else { - addNewBinding({objTy, binding.Kind, binding.BindingSource}); + addNewBinding(binding.withSameSource(objTy, binding.Kind)); } } } @@ -938,7 +1017,7 @@ bool TypeVarBindingProducer::computeNext() { for (auto supertype : enumerateDirectSupertypes(type)) { // If we're not allowed to try this binding, skip it. if (auto simplifiedSuper = CS.checkTypeOfBinding(TypeVar, supertype)) - addNewBinding({*simplifiedSuper, binding.Kind, binding.BindingSource}); + addNewBinding(binding.withType(*simplifiedSuper)); } } @@ -953,12 +1032,14 @@ bool TypeVarBindingProducer::computeNext() { bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { auto type = Binding.BindingType; - auto *locator = TypeVar->getImpl().getLocator(); + auto *srcLocator = Binding.getLocator(); + auto *dstLocator = TypeVar->getImpl().getLocator(); - if (Binding.DefaultedProtocol) { - type = cs.openUnboundGenericType(type, locator); + if (Binding.hasDefaultedLiteralProtocol()) { + type = cs.openUnboundGenericType(type, dstLocator); type = type->reconstituteSugar(/*recursive=*/false); - } else if (Binding.BindingSource == ConstraintKind::ArgumentConversion && + } else if (srcLocator && + srcLocator->isLastElement() && !type->hasTypeVariable() && cs.isCollectionType(type)) { // If the type binding comes from the argument conversion, let's // instead of binding collection types directly, try to bind @@ -969,41 +1050,39 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { auto UGT = UnboundGenericType::get(BGT->getDecl(), BGT->getParent(), BGT->getASTContext()); - type = cs.openUnboundGenericType(UGT, locator); + type = cs.openUnboundGenericType(UGT, dstLocator); type = type->reconstituteSugar(/*recursive=*/false); } - // FIXME: We want the locator that indicates where the binding came - // from. - cs.addConstraint(ConstraintKind::Bind, TypeVar, type, locator); + cs.addConstraint(ConstraintKind::Bind, TypeVar, type, srcLocator); // If this was from a defaultable binding note that. if (Binding.isDefaultableBinding()) { - auto *locator = Binding.DefaultableBinding; - // If this default binding comes from a "hole" - // in the constraint system, we have to propagate - // this information and mark this type variable - // as well as mark everything adjacent to it as - // a potential "hole". - // - // Consider this example: - // - // func foo(_: T) {} - // foo(.bar) <- Since `.bar` can't be inferred due to - // luck of information about its base type, - // it's member type is going to get defaulted - // to `Any` which has to be propaged to type - // variable associated with `T` and vice versa. - if (cs.shouldAttemptFixes() && cs.isHoleAt(locator)) { - auto &CG = cs.getConstraintGraph(); - for (auto *constraint : CG.gatherConstraints( - TypeVar, ConstraintGraph::GatheringKind::EquivalenceClass)) { - for (auto *typeVar : constraint->getTypeVariables()) - cs.recordHole(typeVar); + cs.DefaultedConstraints.push_back(srcLocator); + + if (type->isHole()) { + if (auto *GP = TypeVar->getImpl().getGenericParameter()) { + auto path = dstLocator->getPath(); + // Drop `generic parameter` locator element so that all missing + // generic parameters related to the same path can be coalesced later. + auto *fix = DefaultGenericArgument::create( + cs, GP, + cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); + if (cs.recordFix(fix)) + return true; + } else if (TypeVar->getImpl().isClosureResultType()) { + auto *fix = SpecifyClosureReturnType::create( + cs, TypeVar->getImpl().getLocator()); + if (cs.recordFix(fix)) + return true; + } else if (auto *OLE = dyn_cast_or_null( + srcLocator->getAnchor())) { + auto *fix = SpecifyObjectLiteralTypeImport::create( + cs, TypeVar->getImpl().getLocator()); + if (cs.recordFix(fix)) + return true; } } - - cs.DefaultedConstraints.push_back(locator); } return !cs.failedConstraint && !cs.simplify(); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index c4d543e59a27e..e8e865c13e43a 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -117,13 +117,9 @@ class FailureDiagnosis :public ASTVisitor{ template InFlightDiagnostic diagnose(ArgTypes &&...Args) { - return CS.TC.diagnose(std::forward(Args)...); + return CS.getASTContext().Diags.diagnose(std::forward(Args)...); } - /// Attempt to diagnose a failure without taking into account the specific - /// kind of expression that could not be type checked. - bool diagnoseConstraintFailure(); - /// Unless we've already done this, retypecheck the specified child of the /// current expression on its own, without including any contextual /// constraints or the parent expr nodes. This is more likely to succeed than @@ -167,7 +163,7 @@ class FailureDiagnosis :public ASTVisitor{ FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, ExprTypeCheckListener *listener = nullptr) { - CS.TC.getPossibleTypesOfExpressionWithoutApplying( + TypeChecker::getPossibleTypesOfExpressionWithoutApplying( expr, dc, types, allowFreeTypeVariables, listener); CS.cacheExprTypes(expr); } @@ -177,8 +173,11 @@ class FailureDiagnosis :public ASTVisitor{ FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, ExprTypeCheckListener *listener = nullptr) { - auto type = CS.TC.getTypeOfExpressionWithoutApplying(expr, dc, referencedDecl, - allowFreeTypeVariables, listener); + auto type = + TypeChecker::getTypeOfExpressionWithoutApplying(expr, dc, + referencedDecl, + allowFreeTypeVariables, + listener); CS.cacheExprTypes(expr); return type; } @@ -202,20 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - /// For an expression being type checked with a CTP_CalleeResult contextual - /// type, try to diagnose a problem. - bool diagnoseCalleeResultContextualConversionError(); - - /// Attempt to produce a diagnostic for a mismatch between a call's - /// type and its assumed contextual type. - bool diagnoseCallContextualConversionErrors(ApplyExpr *callEpxr, - Type contextualType, - ContextualTypePurpose CTP); - - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, - CalleeCandidateInfo &CCI, - ArrayRef argLabels); - private: /// Validate potential contextual type for type-checking one of the /// sub-expressions, usually correct/valid types are the ones which @@ -231,776 +216,18 @@ class FailureDiagnosis :public ASTVisitor{ std::pair validateContextualType(Type contextualType, ContextualTypePurpose CTP); - /// Check the specified closure to see if it is a multi-statement closure with - /// an uninferred type. If so, diagnose the problem with an error and return - /// true. - bool diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure); - - /// Check the associated constraint system to see if it has any opened generic - /// parameters that were not bound to a fixed type. If so, diagnose the - /// problem with an error and return true. - bool diagnoseAmbiguousGenericParameters(); - - /// Emit an error message about an unbound generic parameter, and emit notes - /// referring to the target of a diagnostic, e.g., the function or parameter - /// being used. - void diagnoseAmbiguousGenericParameter(GenericTypeParamType *paramTy, - Expr *anchor); - - /// Produce a diagnostic for a general member-lookup failure (irrespective of - /// the exact expression kind). - bool diagnoseGeneralMemberFailure(Constraint *constraint); - - /// Given a result of name lookup that had no viable results, diagnose the - /// unviable ones. - void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults, - Expr *expr, Type baseObjTy, Expr *baseExpr, - DeclName memberName, DeclNameLoc nameLoc, - SourceLoc loc); - - /// Produce a diagnostic for a general overload resolution failure - /// (irrespective of the exact expression kind). - bool diagnoseGeneralOverloadFailure(Constraint *constraint); - - /// Produce a diagnostic for a general conversion failure (irrespective of the - /// exact expression kind). - bool diagnoseGeneralConversionFailure(Constraint *constraint); - - bool diagnoseMemberFailures( - Expr *E, Expr *baseEpxr, ConstraintKind lookupKind, DeclName memberName, - FunctionRefKind funcRefKind, ConstraintLocator *locator, - Optional)>> callback = None, - bool includeInaccessibleMembers = true); - - bool diagnoseTrailingClosureErrors(ApplyExpr *expr); - - bool - diagnoseClosureExpr(ClosureExpr *closureExpr, Type contextualType, - llvm::function_ref resultTypeProcessor); - - bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet); - bool visitExpr(Expr *E); - bool visitIdentityExpr(IdentityExpr *E); - bool visitTryExpr(TryExpr *E); - bool visitTupleExpr(TupleExpr *E); - - bool visitUnresolvedMemberExpr(UnresolvedMemberExpr *E); - bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE); - bool visitArrayExpr(ArrayExpr *E); - bool visitDictionaryExpr(DictionaryExpr *E); - bool visitObjectLiteralExpr(ObjectLiteralExpr *E); - bool visitForceValueExpr(ForceValueExpr *FVE); - bool visitBindOptionalExpr(BindOptionalExpr *BOE); - - bool visitSubscriptExpr(SubscriptExpr *SE); bool visitApplyExpr(ApplyExpr *AE); - bool visitAssignExpr(AssignExpr *AE); - bool visitInOutExpr(InOutExpr *IOE); - bool visitCoerceExpr(CoerceExpr *CE); - bool visitIfExpr(IfExpr *IE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); - bool visitCaptureListExpr(CaptureListExpr *CLE); - bool visitClosureExpr(ClosureExpr *CE); - bool visitKeyPathExpr(KeyPathExpr *KPE); }; } // end anonymous namespace - - -static bool isMemberConstraint(Constraint *C) { - return C->getClassification() == ConstraintClassification::Member; -} - -static bool isOverloadConstraint(Constraint *C) { - if (C->getKind() == ConstraintKind::BindOverload) - return true; - - if (C->getKind() != ConstraintKind::Disjunction) - return false; - - return C->getNestedConstraints().front()->getKind() == - ConstraintKind::BindOverload; -} - -/// Return true if this constraint is a conversion or requirement between two -/// types. -static bool isConversionConstraint(const Constraint *C) { - return C->getClassification() == ConstraintClassification::Relational; -} - -/// Attempt to diagnose a failure without taking into account the specific -/// kind of expression that could not be type checked. -bool FailureDiagnosis::diagnoseConstraintFailure() { - // This is the priority order in which we handle constraints. Things earlier - // in the list are considered to have higher specificity (and thus, higher - // priority) than things lower in the list. - enum ConstraintRanking { - CR_MemberConstraint, - CR_ConversionConstraint, - CR_OverloadConstraint, - CR_OtherConstraint - }; - - // Start out by classifying all the constraints. - using RCElt = std::pair; - std::vector rankedConstraints; - - // This is a predicate that classifies constraints according to our - // priorities. - std::function classifyConstraint = [&](Constraint *C) { - if (isMemberConstraint(C)) - return rankedConstraints.push_back({C, CR_MemberConstraint}); - - if (isOverloadConstraint(C)) - return rankedConstraints.push_back({C, CR_OverloadConstraint}); - - if (isConversionConstraint(C)) - return rankedConstraints.push_back({C, CR_ConversionConstraint}); - - // We occasionally end up with disjunction constraints containing an - // original constraint along with one considered with a fix. If we find - // this situation, add the original one to our list for diagnosis. - if (C->getKind() == ConstraintKind::Disjunction) { - Constraint *Orig = nullptr; - bool AllOthersHaveFixes = true; - for (auto DC : C->getNestedConstraints()) { - // If this is a constraint inside of the disjunction with a fix, ignore - // it. - if (DC->getFix()) - continue; - - // If we already found a candidate without a fix, we can't do this. - if (Orig) { - AllOthersHaveFixes = false; - break; - } - - // Remember this as the exemplar to use. - Orig = DC; - } - - if (Orig && AllOthersHaveFixes) - return classifyConstraint(Orig); - - // If we got all the way down to a truly ambiguous disjunction constraint - // with a conversion in it, the problem could be that none of the options - // in the disjunction worked. - // - // We don't have a lot of great options here, so (if all else fails), - // we'll attempt to diagnose the issue as though the first option was the - // problem. - rankedConstraints.push_back({ - C->getNestedConstraints()[0], - CR_OtherConstraint - }); - return; - } - - return rankedConstraints.push_back({C, CR_OtherConstraint}); - }; - - // Look at the failed constraint and the general constraint list. Processing - // the failed constraint first slightly biases it in the ranking ahead of - // other failed constraints at the same level. - if (CS.failedConstraint) - classifyConstraint(CS.failedConstraint); - for (auto &C : CS.getConstraints()) - classifyConstraint(&C); - - // Okay, now that we've classified all the constraints, sort them by their - // priority and privilege the favored constraints. - std::stable_sort(rankedConstraints.begin(), rankedConstraints.end(), - [&] (RCElt LHS, RCElt RHS) { - // Rank things by their kind as the highest priority. - if (LHS.second < RHS.second) - return true; - if (LHS.second > RHS.second) - return false; - // Next priority is favored constraints. - if (LHS.first->isFavored() != RHS.first->isFavored()) - return LHS.first->isFavored(); - return false; - }); - - // Now that we have a sorted precedence of constraints to diagnose, charge - // through them. - for (auto elt : rankedConstraints) { - auto C = elt.first; - if (isMemberConstraint(C) && diagnoseGeneralMemberFailure(C)) - return true; - - if (isConversionConstraint(C) && diagnoseGeneralConversionFailure(C)) - return true; - - if (isOverloadConstraint(C) && diagnoseGeneralOverloadFailure(C)) - return true; - - - // TODO: There can be constraints that aren't handled here! When this - // happens, we end up diagnosing them as ambiguities that don't make sense. - // This isn't as bad as it seems though, because most of these will be - // diagnosed by expr diagnostics. - } - - // Otherwise, all the constraints look ok, diagnose this as an ambiguous - // expression. - return false; -} - - -bool FailureDiagnosis::diagnoseGeneralMemberFailure(Constraint *constraint) { - assert(isMemberConstraint(constraint)); - - // Get the referenced base expression from the failed constraint, along with - // the SourceRange for the member ref. In "x.y", this returns the expr for x - // and the source range for y. - auto anchor = expr; - SourceRange memberRange = anchor->getSourceRange(); - auto locator = constraint->getLocator(); - if (locator) { - locator = simplifyLocator(CS, locator, memberRange); - if (locator->getAnchor()) - anchor = locator->getAnchor(); - } - - // Check to see if this is a locator referring to something we cannot or do - // here: in this case, we ignore paths that end on archetypes witnesses, or - // associated types of the expression. - if (locator && !locator->getPath().empty()) { - // TODO: This should only ignore *unresolved* archetypes. For resolved - // archetypes - return false; - } - - return diagnoseMemberFailures(expr, anchor, constraint->getKind(), - constraint->getMember(), - constraint->getFunctionRefKind(), locator); -} - -/// Given a result of name lookup that had no viable results, diagnose the -/// unviable ones. -void FailureDiagnosis::diagnoseUnviableLookupResults( - MemberLookupResult &result, Expr *E, Type baseObjTy, Expr *baseExpr, - DeclName memberName, DeclNameLoc nameLoc, SourceLoc loc) { - SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange(); - - // If we found no results at all, mention that fact. - if (result.UnviableCandidates.empty()) { - MissingMemberFailure failure(nullptr, CS, baseObjTy, memberName, - CS.getConstraintLocator(E)); - auto diagnosed = failure.diagnoseAsError(); - assert(diagnosed && "Failed to produce missing member diagnostic"); - (void)diagnosed; - return; - } - - // Otherwise, we have at least one (and potentially many) viable candidates - // sort them out. If all of the candidates have the same problem (commonly - // because there is exactly one candidate!) diagnose this. - auto firstProblem = result.UnviableReasons[0]; - bool sameProblem = llvm::all_of( - result.UnviableReasons, - [&firstProblem](const MemberLookupResult::UnviableReason &problem) { - return problem == firstProblem; - }); - - auto instanceTy = baseObjTy; - if (auto *MTT = instanceTy->getAs()) - instanceTy = MTT->getInstanceType(); - - if (sameProblem) { - // If the problem is the same for all of the choices, let's - // just pick one which has a declaration. - auto choice = llvm::find_if( - result.UnviableCandidates, - [&](const OverloadChoice &choice) { return choice.isDecl(); }); - - // This code can't currently diagnose key path application - // related failures. - if (!choice) - return; - - switch (firstProblem) { - case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember: - case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember: - case MemberLookupResult::UR_KeyPathWithAnyObjectRootType: - break; - - case MemberLookupResult::UR_UnavailableInExistential: { - InvalidMemberRefOnExistential failure( - baseExpr, CS, instanceTy, memberName, CS.getConstraintLocator(E)); - failure.diagnoseAsError(); - return; - } - - case MemberLookupResult::UR_InstanceMemberOnType: - case MemberLookupResult::UR_TypeMemberOnInstance: { - auto locatorKind = isa(E) - ? ConstraintLocator::SubscriptMember - : ConstraintLocator::Member; - AllowTypeOrInstanceMemberFailure failure( - expr, CS, baseObjTy, choice->getDecl(), memberName, - CS.getConstraintLocator(E, locatorKind)); - auto diagnosed = failure.diagnoseAsError(); - assert(diagnosed && - "Failed to produce missing or extraneous metatype diagnostic"); - (void)diagnosed; - return; - } - case MemberLookupResult::UR_MutatingMemberOnRValue: - case MemberLookupResult::UR_MutatingGetterOnRValue: { - MutatingMemberRefOnImmutableBase failure(E, CS, choice->getDecl(), - CS.getConstraintLocator(E)); - (void)failure.diagnose(); - return; - } - - case MemberLookupResult::UR_Inaccessible: { - // FIXME: What if the unviable candidates have different levels of access? - // - // If we found an inaccessible member of a protocol extension, it might - // be declared 'public'. This can only happen if the protocol is not - // visible to us, but the conforming type is. In this case, we need to - // clamp the formal access for diagnostics purposes to the formal access - // of the protocol itself. - InaccessibleMemberFailure failure(expr, CS, choice->getDecl(), - CS.getConstraintLocator(E)); - auto diagnosed = failure.diagnoseAsError(); - assert(diagnosed && "failed to produce expected diagnostic"); - for (auto cand : result.UnviableCandidates) { - if (!cand.isDecl()) - continue; - - auto *candidate = cand.getDecl(); - // failure is going to highlight candidate given to it, - // we just need to handle the rest here. - if (candidate != choice->getDecl()) - diagnose(candidate, diag::decl_declared_here, - candidate->getFullName()); - } - return; - } - } - } - - // Otherwise, we don't have a specific issue to diagnose. Just say the vague - // 'cannot use' diagnostic. - if (!baseObjTy->isEqual(instanceTy)) - diagnose(loc, diag::could_not_use_type_member, - instanceTy, memberName) - .highlight(baseRange).highlight(nameLoc.getSourceRange()); - else - diagnose(loc, diag::could_not_use_value_member, - baseObjTy, memberName) - .highlight(baseRange).highlight(nameLoc.getSourceRange()); - return; -} - -// In the absence of a better conversion constraint failure, point out the -// inability to find an appropriate overload. -bool FailureDiagnosis::diagnoseGeneralOverloadFailure(Constraint *constraint) { - Constraint *bindOverload = constraint; - if (constraint->getKind() == ConstraintKind::Disjunction) - bindOverload = constraint->getNestedConstraints().front(); - - auto overloadChoice = bindOverload->getOverloadChoice(); - auto overloadName = overloadChoice.getName(); - - // Get the referenced expression from the failed constraint. - auto anchor = expr; - if (auto locator = bindOverload->getLocator()) { - anchor = simplifyLocatorToAnchor(locator); - if (!anchor) - return false; - } - - // The anchor for the constraint is almost always an OverloadedDeclRefExpr or - // UnresolvedDotExpr. Look at the parent node in the AST to find the Apply to - // give a better diagnostic. - Expr *call = expr->getParentMap()[anchor]; - // We look through some simple things that get in between the overload set - // and the apply. - while (call && - (isa(call) || - isa(call) || isa(call))) { - call = expr->getParentMap()[call]; - } - - // FIXME: This is only needed because binops don't respect contextual types. - if (call && isa(call)) - return false; - - // This happens, for example, with ambiguous OverloadedDeclRefExprs. We should - // just implement visitOverloadedDeclRefExprs and nuke this. - - // If we couldn't resolve an argument, then produce a generic "ambiguity" - // diagnostic. - diagnose(anchor->getLoc(), diag::ambiguous_member_overload_set, - overloadName) - .highlight(anchor->getSourceRange()); - - if (constraint->getKind() == ConstraintKind::Disjunction) { - for (auto elt : constraint->getNestedConstraints()) { - if (elt->getKind() != ConstraintKind::BindOverload) continue; - if (auto *candidate = elt->getOverloadChoice().getDeclOrNull()) - diagnose(candidate, diag::found_candidate); - } - } - - return true; -} - -static bool -diagnoseUnresolvedDotExprTypeRequirementFailure(ConstraintSystem &cs, - Constraint *constraint) { - auto &TC = cs.TC; - - auto *locator = constraint->getLocator(); - if (!locator) - return false; - - - auto reqElt = - locator->getLastElementAs(); - if (!reqElt) - return false; - - auto *anchor = locator->getAnchor(); - if (!anchor) - return false; - - auto *UDE = dyn_cast(anchor); - if (!UDE) - return false; - - auto ownerType = cs.getType(UDE->getBase()); - if (!ownerType) - return false; - - ownerType = cs.simplifyType(ownerType)->getWithoutSpecifierType(); - if (ownerType->hasTypeVariable() || ownerType->hasUnresolvedType()) - return false; - - // If we actually resolved the member to use, use it. - auto loc = cs.getConstraintLocator(UDE, ConstraintLocator::Member); - auto *member = cs.findResolvedMemberRef(loc); - // If the problem is contextual it's diagnosed elsewhere. - if (!member || !member->getAsGenericContext()) - return false; - - auto req = member->getAsGenericContext() - ->getGenericSignature() - ->getRequirements()[reqElt->getIndex()]; - - Diag note; - switch (req.getKind()) { - case RequirementKind::Conformance: - case RequirementKind::Layout: - return false; - - case RequirementKind::Superclass: - note = diag::candidate_types_inheritance_requirement; - break; - - case RequirementKind::SameType: - note = diag::candidate_types_equal_requirement; - break; - } - - TC.diagnose(UDE->getLoc(), diag::could_not_find_value_member, ownerType, - UDE->getName()); - - auto first = cs.simplifyType(constraint->getFirstType()); - auto second = cs.simplifyType(constraint->getSecondType()); - auto rawFirstType = req.getFirstType(); - auto rawSecondType = req.getSecondType(); - - TC.diagnose(member, note, first, second, rawFirstType, rawSecondType, ""); - - return true; -} - -/// Diagnose problems related to failures in constraints -/// generated by `openGeneric` which represent different -/// kinds of type parameter requirements. -static bool diagnoseTypeRequirementFailure(ConstraintSystem &cs, - Constraint *constraint) { - auto &TC = cs.TC; - - auto *locator = constraint->getLocator(); - if (!locator) - return false; - - auto path = locator->getPath(); - if (path.empty()) - return false; - - auto &last = path.back(); - if (last.getKind() != ConstraintLocator::TypeParameterRequirement) - return false; - - auto *anchor = locator->getAnchor(); - if (!anchor) - return false; - - auto ownerType = cs.getType(anchor); - - if (isa(anchor)) - ownerType = cs.getContextualType(); - else if (auto *UDE = dyn_cast(anchor)) - ownerType = cs.getType(UDE->getBase()); - - if (!ownerType) - return false; - - ownerType = cs.simplifyType(ownerType)->getWithoutSpecifierType(); - if (ownerType->hasTypeVariable() || ownerType->hasUnresolvedType()) - return false; - - if (diagnoseUnresolvedDotExprTypeRequirementFailure(cs, constraint)) - return true; - - auto lhs = cs.simplifyType(constraint->getFirstType()); - auto rhs = cs.simplifyType(constraint->getSecondType()); - - switch (constraint->getKind()) { - case ConstraintKind::ConformsTo: - TC.diagnose(anchor->getLoc(), diag::type_does_not_conform_owner, ownerType, - lhs, rhs); - return true; - - case ConstraintKind::Subtype: // superclass - TC.diagnose(anchor->getLoc(), diag::type_does_not_inherit, ownerType, lhs, - rhs); - return true; - - case ConstraintKind::Bind: { // same type - TC.diagnose(anchor->getLoc(), diag::types_not_equal, ownerType, lhs, rhs); - return true; - } - - default: - break; - } - - return false; -} - -bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){ - auto anchor = expr; - bool resolvedAnchorToExpr = false; - - if (auto locator = constraint->getLocator()) { - anchor = simplifyLocatorToAnchor(locator); - if (anchor) - resolvedAnchorToExpr = true; - else - anchor = locator->getAnchor(); - } - - Type fromType = CS.simplifyType(constraint->getFirstType()); - - if (fromType->hasTypeVariable() && resolvedAnchorToExpr) { - TCCOptions options; - - // If we know we're removing a contextual constraint, then we can force a - // type check of the subexpr because we know we're eliminating that - // constraint. - if (CS.getContextualTypePurpose() != CTP_Unused) - options |= TCC_ForceRecheck; - - auto sub = typeCheckChildIndependently(anchor, options); - if (!sub) return true; - fromType = CS.getType(sub); - } - - // Bail on constraints that don't relate two types. - if (constraint->getKind() == ConstraintKind::Disjunction - || constraint->getKind() == ConstraintKind::BindOverload) - return false; - - fromType = fromType->getRValueType(); - auto toType = CS.simplifyType(constraint->getSecondType()); - - // Try to simplify irrelevant details of function types. For example, if - // someone passes a "() -> Float" function to a "() throws -> Int" - // parameter, then uttering the "throws" may confuse them into thinking that - // that is the problem, even though there is a clear subtype relation. - if (auto srcFT = fromType->getAs()) - if (auto destFT = toType->getAs()) { - auto destExtInfo = destFT->getExtInfo(); - - if (!srcFT->isNoEscape()) destExtInfo = destExtInfo.withNoEscape(false); - if (!srcFT->throws()) destExtInfo = destExtInfo.withThrows(false); - if (destExtInfo != destFT->getExtInfo()) - toType = FunctionType::get(destFT->getParams(), destFT->getResult(), - destExtInfo); - - // If this is a function conversion that discards throwability or - // noescape, emit a specific diagnostic about that. - if (srcFT->throws() && !destFT->throws()) { - diagnose(expr->getLoc(), diag::throws_functiontype_mismatch, - fromType, toType) - .highlight(expr->getSourceRange()); - return true; - } - - auto destPurpose = CTP_Unused; - if (constraint->getKind() == ConstraintKind::ArgumentConversion || - constraint->getKind() == ConstraintKind::OperatorArgumentConversion) - destPurpose = CTP_CallArgument; - } - - // If this is a callee that mismatches an expected return type, we can emit a - // very nice and specific error. In this case, what we'll generally see is - // a failed conversion constraint of "A -> B" to "_ -> C", where the error is - // that B isn't convertible to C. - if (CS.getContextualTypePurpose() == CTP_CalleeResult) { - auto destFT = toType->getAs(); - auto srcFT = fromType->getAs(); - if (destFT && srcFT && !isUnresolvedOrTypeVarType(srcFT->getResult())) { - // Otherwise, the error is that the result types mismatch. - diagnose(expr->getLoc(), diag::invalid_callee_result_type, - srcFT->getResult(), destFT->getResult()) - .highlight(expr->getSourceRange()); - return true; - } - } - - - // If simplification has turned this into the same types, then this isn't the - // broken constraint that we're looking for. - if (fromType->isEqual(toType) && - constraint->getKind() != ConstraintKind::ConformsTo && - constraint->getKind() != ConstraintKind::LiteralConformsTo) - return false; - - - // If we have two tuples with mismatching types, produce a tailored - // diagnostic. - if (auto fromTT = fromType->getAs()) - if (auto toTT = toType->getAs()) { - if (fromTT->getNumElements() != toTT->getNumElements()) { - auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT, - CS.getConstraintLocator(expr)); - return failure.diagnoseAsError(); - } - - SmallVector FromElts; - auto voidTy = CS.getASTContext().TheUnresolvedType; - - for (unsigned i = 0, e = fromTT->getNumElements(); i != e; ++i) - FromElts.push_back({ voidTy, fromTT->getElement(i).getName() }); - auto TEType = TupleType::get(FromElts, CS.getASTContext()); - - SmallVector sources; - - // If the shuffle conversion is invalid (e.g. incorrect element labels), - // then we have a type error. - if (computeTupleShuffle(TEType->castTo()->getElements(), - toTT->getElements(), sources)) { - auto failure = TupleContextualFailure(anchor, CS, fromTT, toTT, - CS.getConstraintLocator(expr)); - return failure.diagnoseAsError(); - } - } - - - // If the second type is a type variable, the expression itself is - // ambiguous. Bail out so the general ambiguity diagnosing logic can handle - // it. - if (fromType->hasUnresolvedType() || fromType->hasTypeVariable() || - toType->hasUnresolvedType() || toType->hasTypeVariable() || - // FIXME: Why reject unbound generic types here? - fromType->is()) - return false; - - - // Check for various issues converting to Bool. - ContextualFailure failure(expr, CS, fromType, toType, - constraint->getLocator()); - if (failure.diagnoseConversionToBool()) - return true; - - if (auto PT = toType->getAs()) { - if (isa(expr->getValueProvidingExpr())) { - diagnose(expr->getLoc(), diag::cannot_use_nil_with_this_type, toType) - .highlight(expr->getSourceRange()); - return true; - } - - // Emit a conformance error through conformsToProtocol. - if (auto conformance = TypeChecker::conformsToProtocol( - fromType, PT->getDecl(), CS.DC, ConformanceCheckFlags::InExpression, - expr->getLoc())) { - if (conformance->isAbstract() || - !conformance->getConcrete()->isInvalid()) - return false; - } - - return true; - } - - // Due to migration reasons, types used to conform to BooleanType, which - // contain a member var 'boolValue', now does not convert to Bool. This block - // tries to add a specific diagnosis/fixit to explicitly invoke 'boolValue'. - if (toType->isBool() && - fromType->mayHaveMembers()) { - auto LookupResult = CS.TC.lookupMember( - CS.DC, fromType, DeclName(CS.TC.Context.getIdentifier("boolValue"))); - if (!LookupResult.empty()) { - if (isa(LookupResult.begin()->getValueDecl())) { - if (anchor->canAppendPostfixExpression()) - diagnose(anchor->getLoc(), diag::types_not_convertible_use_bool_value, - fromType, toType).fixItInsertAfter(anchor->getEndLoc(), - ".boolValue"); - else - diagnose(anchor->getLoc(), diag::types_not_convertible_use_bool_value, - fromType, toType).fixItInsert(anchor->getStartLoc(), "("). - fixItInsertAfter(anchor->getEndLoc(), ").boolValue"); - return true; - } - } - } - - if (diagnoseTypeRequirementFailure(CS, constraint)) - return true; - - diagnose(anchor->getLoc(), diag::types_not_convertible, - constraint->getKind() == ConstraintKind::Subtype, - fromType, toType) - .highlight(anchor->getSourceRange()); - - // Check to see if this constraint came from a cast instruction. If so, - // and if this conversion constraint is different than the types being cast, - // produce a note that talks about the overall expression. - // - // TODO: Using parentMap would be more general, rather than requiring the - // issue to be related to the root of the expr under study. - if (auto ECE = dyn_cast(expr)) - if (constraint->getLocator() && - constraint->getLocator()->getAnchor() == ECE->getSubExpr()) { - if (!toType->isEqual(ECE->getCastTypeLoc().getType())) - diagnose(expr->getLoc(), diag::in_cast_expr_types, - CS.getType(ECE->getSubExpr())->getRValueType(), - ECE->getCastTypeLoc().getType()->getRValueType()) - .highlight(ECE->getSubExpr()->getSourceRange()) - .highlight(ECE->getCastTypeLoc().getSourceRange()); - } - - return true; -} - namespace { class ExprTypeSaverAndEraser { llvm::DenseMap ExprTypes; llvm::DenseMap TypeLocTypes; llvm::DenseMap PatternTypes; - llvm::DenseMap ParamDeclTypes; - llvm::DenseMap ParamDeclInterfaceTypes; - llvm::DenseSet PossiblyInvalidDecls; ExprTypeSaverAndEraser(const ExprTypeSaverAndEraser&) = delete; void operator=(const ExprTypeSaverAndEraser&) = delete; public: @@ -1039,25 +266,6 @@ namespace { if (isa(expr) && !isa(expr) && !(expr->getType() && expr->getType()->hasError())) return { false, expr }; - - // If a ClosureExpr's parameter list has types on the decls, then - // remove them so that they'll get regenerated from the - // associated TypeLocs or resynthesized as fresh typevars. - if (auto *CE = dyn_cast(expr)) - for (auto P : *CE->getParameters()) { - if (P->hasType()) { - TS->ParamDeclTypes[P] = P->getType(); - P->setType(Type()); - } - if (P->hasInterfaceType()) { - TS->ParamDeclInterfaceTypes[P] = P->getInterfaceType(); - P->setInterfaceType(Type()); - } - TS->PossiblyInvalidDecls.insert(P); - - if (P->isInvalid()) - P->setInvalid(false); - } expr->setType(nullptr); @@ -1101,28 +309,10 @@ namespace { for (auto patternElt : PatternTypes) patternElt.first->setType(patternElt.second); - for (auto paramDeclElt : ParamDeclTypes) { - assert(!paramDeclElt.first->isImmutable() || - !paramDeclElt.second->is()); - paramDeclElt.first->setType(paramDeclElt.second->getInOutObjectType()); - } - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) { - assert(!paramDeclIfaceElt.first->isImmutable() || - !paramDeclIfaceElt.second->is()); - paramDeclIfaceElt.first->setInterfaceType(paramDeclIfaceElt.second->getInOutObjectType()); - } - - if (!PossiblyInvalidDecls.empty()) - for (auto D : PossiblyInvalidDecls) - if (D->hasInterfaceType()) - D->setInvalid(D->getInterfaceType()->hasError()); - // Done, don't do redundant work on destruction. ExprTypes.clear(); TypeLocTypes.clear(); PatternTypes.clear(); - PossiblyInvalidDecls.clear(); } // On destruction, if a type got wiped out, reset it from null to its @@ -1144,43 +334,6 @@ namespace { for (auto patternElt : PatternTypes) if (!patternElt.first->hasType()) patternElt.first->setType(patternElt.second); - - for (auto paramDeclElt : ParamDeclTypes) - if (!paramDeclElt.first->hasType()) { - paramDeclElt.first->setType(getParamBaseType(paramDeclElt)); - } - - for (auto paramDeclIfaceElt : ParamDeclInterfaceTypes) - if (!paramDeclIfaceElt.first->hasInterfaceType()) { - paramDeclIfaceElt.first->setInterfaceType( - getParamBaseType(paramDeclIfaceElt)); - } - - if (!PossiblyInvalidDecls.empty()) - for (auto D : PossiblyInvalidDecls) - if (D->hasInterfaceType()) - D->setInvalid(D->getInterfaceType()->hasError()); - } - - private: - static Type getParamBaseType(std::pair &storedParam) { - ParamDecl *param; - Type storedType; - - std::tie(param, storedType) = storedParam; - - // FIXME: We are currently in process of removing `InOutType` - // so `VarDecl::get{Interface}Type` is going to wrap base - // type into `InOutType` if its flag indicates that it's - // an `inout` parameter declaration. But such type can't - // be restored directly using `VarDecl::set{Interface}Type` - // caller needs additional logic to extract base type. - if (auto *IOT = storedType->getAs()) { - assert(param->isInOut()); - return IOT->getObjectType(); - } - - return storedType; } }; } // end anonymous namespace @@ -1201,8 +354,8 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // expression (which may lead to infinite recursion). If the client is // telling us that it knows what it is doing, then believe it. if (!options.contains(TCC_ForceRecheck)) { - if (CS.TC.isExprBeingDiagnosed(subExpr)) { - auto *savedExpr = CS.TC.getExprBeingDiagnosed(subExpr); + if (CS.isExprBeingDiagnosed(subExpr)) { + auto *savedExpr = CS.getExprBeingDiagnosed(subExpr); if (subExpr == savedExpr) return subExpr; @@ -1212,7 +365,7 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( } // Mark current expression as about to be diagnosed. - CS.TC.addExprForDiagnosis(subExpr, subExpr); + CS.addExprForDiagnosis(subExpr, subExpr); // Validate contextual type before trying to use it. std::tie(convertType, convertTypePurpose) = @@ -1237,19 +390,10 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // type check operation. Expr *preCheckedExpr = subExpr; - // Disable structural checks, because we know that the overall expression - // has type constraint problems, and we don't want to know about any - // syntactic issues in a well-typed subexpression (which might be because - // the context is missing). - TypeCheckExprOptions TCEOptions = TypeCheckExprFlags::DisableStructuralChecks; - // Make sure that typechecker knows that this is an attempt // to diagnose a problem. - TCEOptions |= TypeCheckExprFlags::SubExpressionDiagnostics; - - // Don't walk into non-single expression closure bodies, because - // ExprTypeSaver and TypeNullifier skip them too. - TCEOptions |= TypeCheckExprFlags::SkipMultiStmtClosures; + TypeCheckExprOptions TCEOptions = + TypeCheckExprFlags::SubExpressionDiagnostics; // Claim that the result is discarded to preserve the lvalue type of // the expression. @@ -1268,8 +412,10 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // if there is a closure in the subexpression, we can violate invariants. auto *DC = findDeclContext(subExpr); auto resultTy = - CS.TC.typeCheckExpression(subExpr, DC, TypeLoc::withoutLoc(convertType), - convertTypePurpose, TCEOptions, listener, &CS); + TypeChecker::typeCheckExpression(subExpr, DC, + TypeLoc::withoutLoc(convertType), + convertTypePurpose, TCEOptions, + listener, &CS); CS.cacheExprTypes(subExpr); @@ -1300,7 +446,7 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( } if (preCheckedExpr != subExpr) - CS.TC.addExprForDiagnosis(preCheckedExpr, subExpr); + CS.addExprForDiagnosis(preCheckedExpr, subExpr); return subExpr; } @@ -1354,92 +500,14 @@ DeclContext *FailureDiagnosis::findDeclContext(Expr *subExpr) const { return finder.DC; } -/// For an expression being type checked with a CTP_CalleeResult contextual -/// type, try to diagnose a problem. -bool FailureDiagnosis::diagnoseCalleeResultContextualConversionError() { - // Try to dig out the conversion constraint in question to find the contextual - // result type being specified. - Type contextualResultType; - for (auto &c : CS.getConstraints()) { - if (!isConversionConstraint(&c) || !c.getLocator() || - c.getLocator()->getAnchor() != expr) - continue; - - // If we found our contextual type, then we know we have a conversion to - // some function type, and that the result type is concrete. If not, - // ignore it. - auto toType = CS.simplifyType(c.getSecondType()); - if (auto *FT = toType->getAs()) - if (!isUnresolvedOrTypeVarType(FT->getResult())) { - contextualResultType = FT->getResult(); - break; - } - } - if (!contextualResultType) +bool FailureDiagnosis::diagnoseContextualConversionError( + Expr *expr, Type contextualType, ContextualTypePurpose CTP, + Type suggestedType) { + // If the constraint system has a contextual type, then we can test to see if + // this is the problem that prevents us from solving the system. + if (!contextualType) return false; - // Retypecheck the callee expression without a contextual type to resolve - // whatever we can in it. - auto callee = typeCheckChildIndependently(expr, TCC_ForceRecheck); - if (!callee) - return true; - - // Based on that, compute an overload set. - CalleeCandidateInfo calleeInfo(callee, /*hasTrailingClosure*/false, CS); - - switch (calleeInfo.size()) { - case 0: - // If we found no overloads, then there is something else going on here. - return false; - - case 1: - // If the callee isn't of function type, then something else has gone wrong. - if (!calleeInfo[0].getResultType()) - return false; - - diagnose(expr->getLoc(), diag::candidates_no_match_result_type, - calleeInfo.declName, calleeInfo[0].getResultType(), - contextualResultType); - return true; - default: - // Check to see if all of the viable candidates produce the same result, - // this happens for things like "==" and "&&" operators. - if (auto resultTy = calleeInfo[0].getResultType()) { - for (unsigned i = 1, e = calleeInfo.size(); i != e; ++i) - if (auto ty = calleeInfo[i].getResultType()) - if (!resultTy->isEqual(ty)) { - resultTy = Type(); - break; - } - if (resultTy) { - diagnose(expr->getLoc(), diag::candidates_no_match_result_type, - calleeInfo.declName, calleeInfo[0].getResultType(), - contextualResultType); - return true; - } - } - - // Otherwise, produce a candidate set. - diagnose(expr->getLoc(), diag::no_candidates_match_result_type, - calleeInfo.declName, contextualResultType); - calleeInfo.suggestPotentialOverloads(expr->getLoc(), /*isResult*/true); - return true; - } -} - -bool FailureDiagnosis::diagnoseContextualConversionError( - Expr *expr, Type contextualType, ContextualTypePurpose CTP, - Type suggestedType) { - // If the constraint system has a contextual type, then we can test to see if - // this is the problem that prevents us from solving the system. - if (!contextualType) { - // This contextual conversion constraint doesn't install an actual type. - if (CTP == CTP_CalleeResult) - return diagnoseCalleeResultContextualConversionError(); - - return false; - } - // Try re-type-checking the expression without the contextual type to see if // it can work without it. If so, the contextual type is the problem. We // force a recheck, because "expr" is likely in our table with the extra @@ -1457,7 +525,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError( // If it failed and diagnosed something, then we're done. if (!exprType) - return CS.TC.Diags.hadAnyError(); + return CS.getASTContext().Diags.hadAnyError(); // If we don't have a type for the expression, then we cannot use it in // conversion constraint diagnostic generation. If the types match, then it @@ -1472,7 +540,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError( return false; ContextualFailure failure( - expr, CS, CTP, exprType, contextualType, + CS, CTP, exprType, contextualType, CS.getConstraintLocator(expr, LocatorPathElt::ContextualType())); return failure.diagnoseAsError(); } @@ -1481,55 +549,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError( // Diagnose assigning variable to itself. //===----------------------------------------------------------------------===// -static Decl *findSimpleReferencedDecl(const Expr *E) { - if (auto *LE = dyn_cast(E)) - E = LE->getSubExpr(); - - if (auto *DRE = dyn_cast(E)) - return DRE->getDecl(); - - return nullptr; -} - -static std::pair findReferencedDecl(const Expr *E) { - E = E->getValueProvidingExpr(); - - if (auto *LE = dyn_cast(E)) - return findReferencedDecl(LE->getSubExpr()); - - if (auto *AE = dyn_cast(E)) - return findReferencedDecl(AE->getDest()); - - if (auto *D = findSimpleReferencedDecl(E)) - return std::make_pair(nullptr, D); - - if (auto *MRE = dyn_cast(E)) { - if (auto *BaseDecl = findSimpleReferencedDecl(MRE->getBase())) - return std::make_pair(BaseDecl, MRE->getMember().getDecl()); - } - - return std::make_pair(nullptr, nullptr); -} - -bool TypeChecker::diagnoseSelfAssignment(const Expr *E) { - auto AE = dyn_cast(E); - if (!AE) - return false; - - auto LHSDecl = findReferencedDecl(AE->getDest()); - auto RHSDecl = findReferencedDecl(AE->getSrc()); - - if (LHSDecl.second && LHSDecl == RHSDecl) { - diagnose(AE->getLoc(), LHSDecl.first ? diag::self_assignment_prop - : diag::self_assignment_var) - .highlight(AE->getDest()->getSourceRange()) - .highlight(AE->getSrc()->getSourceRange()); - return true; - } - - return false; -} - static bool isSymmetricBinaryOperator(const CalleeCandidateInfo &CCI) { // If we don't have at least one known candidate, don't trigger. if (CCI.candidates.empty()) return false; @@ -1883,20 +902,21 @@ static DeclName getBaseName(DeclContext *context) { }; static void emitFixItForExplicitlyQualifiedReference( - TypeChecker &tc, UnresolvedDotExpr *UDE, + DiagnosticEngine &de, UnresolvedDotExpr *UDE, decltype(diag::fix_unqualified_access_top_level) diag, DeclName baseName, DescriptiveDeclKind kind) { auto name = baseName.getBaseIdentifier(); SmallString<32> namePlusDot = name.str(); namePlusDot.push_back('.'); - tc.diagnose(UDE->getLoc(), diag, namePlusDot, kind, name) + de.diagnose(UDE->getLoc(), diag, namePlusDot, kind, name) .fixItInsert(UDE->getStartLoc(), namePlusDot); } void ConstraintSystem::diagnoseDeprecatedConditionalConformanceOuterAccess( UnresolvedDotExpr *UDE, ValueDecl *choice) { - auto result = TC.lookupUnqualified(DC, UDE->getName(), UDE->getLoc()); + auto result = + TypeChecker::lookupUnqualified(DC, UDE->getName(), UDE->getLoc()); assert(result && "names can't just disappear"); // These should all come from the same place. auto exampleInner = result.front(); @@ -1913,14 +933,16 @@ void ConstraintSystem::diagnoseDeprecatedConditionalConformanceOuterAccess( ? choiceParentDecl->getDescriptiveKind() : DescriptiveDeclKind::Module; - TC.diagnose(UDE->getLoc(), + auto &DE = getASTContext().Diags; + DE.diagnose(UDE->getLoc(), diag::warn_deprecated_conditional_conformance_outer_access, UDE->getName(), choiceKind, choiceParentKind, choiceBaseName, innerChoice->getDescriptiveKind(), innerParentDecl->getDescriptiveKind(), innerBaseName); emitFixItForExplicitlyQualifiedReference( - TC, UDE, diag::fix_deprecated_conditional_conformance_outer_access, + getASTContext().Diags, UDE, + diag::fix_deprecated_conditional_conformance_outer_access, choiceBaseName, choiceKind); } @@ -1932,424 +954,6 @@ decomposeArgType(Type argType, ArrayRef argLabels) { return result; } -bool FailureDiagnosis::diagnoseImplicitSelfErrors( - Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, - ArrayRef argLabels) { - // If candidate list is empty it means that problem is somewhere else, - // since we need to have candidates which might be shadowing other funcs. - if (CCI.empty() || !CCI[0].getDecl()) - return false; - - auto &TC = CS.TC; - // Call expression is formed as 'foo.bar' where 'foo' might be an - // implicit "Self" reference, such use wouldn't provide good diagnostics - // for situations where instance members have equal names to functions in - // Swift Standard Library e.g. min/max. - auto UDE = dyn_cast(fnExpr); - if (!UDE) - return false; - - auto baseExpr = dyn_cast(UDE->getBase()); - if (!baseExpr) - return false; - - auto baseDecl = baseExpr->getDecl(); - if (!baseExpr->isImplicit() || baseDecl->getFullName() != TC.Context.Id_self) - return false; - - // Our base expression is an implicit 'self.' reference e.g. - // - // extension Sequence { - // func test() -> Int { - // return max(1, 2) - // } - // } - // - // In this example the Sequence class already has two methods named 'max' - // none of which accept two arguments, but there is a function in - // Swift Standard Library called 'max' which does accept two arguments, - // so user might have called that by mistake without realizing that - // compiler would add implicit 'self.' prefix to the call of 'max'. - auto argType = CS.getType(argExpr); - // If argument wasn't properly type-checked, let's retry without changing AST. - if (!argType || argType->hasUnresolvedType() || argType->hasTypeVariable() || - argType->hasTypeParameter()) { - auto *argTuple = dyn_cast(argExpr); - if (!argTuple) { - // Bail out if we don't have a well-formed argument list. - return false; - } - - // Let's type check individual argument expressions without any - // contextual information to try to recover an argument type that - // matches what the user actually wrote instead of what the typechecker - // expects. - SmallVector elts; - for (unsigned i = 0, e = argTuple->getNumElements(); i < e; ++i) { - ConcreteDeclRef ref = nullptr; - auto *el = argTuple->getElement(i); - auto typeResult = getTypeOfExpressionWithoutApplying(el, CS.DC, ref); - if (!typeResult) - return false; - auto flags = ParameterTypeFlags().withInOut(typeResult->is()); - elts.push_back(TupleTypeElt(typeResult->getInOutObjectType(), - argTuple->getElementName(i), - flags)); - } - - argType = TupleType::get(elts, CS.getASTContext()); - } - - auto typeKind = argType->getKind(); - if (typeKind != TypeKind::Tuple && typeKind != TypeKind::Paren) - return false; - - // If argument type couldn't be properly resolved or has errors, - // we can't diagnose anything in here, it points to the different problem. - if (isUnresolvedOrTypeVarType(argType) || argType->hasError()) - return false; - - auto context = CS.DC; - using CandidateMap = - llvm::SmallDenseMap>; - - auto getBaseKind = [](ValueDecl *base) -> DescriptiveDeclKind { - DescriptiveDeclKind kind = DescriptiveDeclKind::Module; - if (!base) - return kind; - - auto context = base->getDeclContext(); - do { - if (isa(context)) - return DescriptiveDeclKind::Extension; - - if (auto nominal = dyn_cast(context)) { - kind = nominal->getDescriptiveKind(); - break; - } - - context = context->getParent(); - } while (context); - - return kind; - }; - - auto diagnoseShadowing = [&](ValueDecl *base, - ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr, - candidates, CCI.hasTrailingClosure, CS, - base); - - calleeInfo.filterListArgs(decomposeArgType(argType, argLabels)); - - auto diagnostic = diag::member_shadows_global_function_near_match; - switch (calleeInfo.closeness) { - case CC_Unavailable: - case CC_Inaccessible: - case CC_SelfMismatch: - case CC_ArgumentLabelMismatch: - case CC_ArgumentCountMismatch: - case CC_GeneralMismatch: - return false; - - case CC_NonLValueInOut: - case CC_OneArgumentNearMismatch: - case CC_OneArgumentMismatch: - case CC_OneGenericArgumentNearMismatch: - case CC_OneGenericArgumentMismatch: - case CC_ArgumentNearMismatch: - case CC_ArgumentMismatch: - case CC_GenericNonsubstitutableMismatch: - break; // Near match cases - - case CC_ExactMatch: - diagnostic = diag::member_shadows_global_function; - break; - } - - auto choice = calleeInfo.candidates[0].getDecl(); - auto baseKind = getBaseKind(base); - auto baseName = getBaseName(choice->getDeclContext()); - - auto origCandidate = CCI[0].getDecl(); - TC.diagnose(UDE->getLoc(), diagnostic, UDE->getName(), - origCandidate->getDescriptiveKind(), - origCandidate->getFullName(), choice->getDescriptiveKind(), - choice->getFullName(), baseKind, baseName); - - auto topLevelDiag = diag::fix_unqualified_access_top_level; - if (baseKind == DescriptiveDeclKind::Module) - topLevelDiag = diag::fix_unqualified_access_top_level_multi; - - emitFixItForExplicitlyQualifiedReference(TC, UDE, topLevelDiag, baseName, - choice->getDescriptiveKind()); - - for (auto &candidate : calleeInfo.candidates) { - if (auto decl = candidate.getDecl()) - TC.diagnose(decl, diag::decl_declared_here, decl->getFullName()); - } - - return true; - }; - - // For each of the parent contexts, let's try to find any candidates - // which have the same name and the same number of arguments as callee. - while (context->getParent()) { - auto result = TC.lookupUnqualified(context, UDE->getName(), UDE->getLoc()); - context = context->getParent(); - - if (!result || result.empty()) - continue; - - CandidateMap candidates; - for (const auto &candidate : result) { - auto base = candidate.getBaseDecl(); - auto decl = candidate.getValueDecl(); - if ((base && base->isInvalid()) || decl->isInvalid()) - continue; - - // If base is present but it doesn't represent a valid nominal, - // we can't use current candidate as one of the choices. - if (base && !base->getInterfaceType()->getNominalOrBoundGenericNominal()) - continue; - - auto context = decl->getDeclContext(); - // We are only interested in static or global functions, because - // there is no way to call anything else properly. - if (!decl->isStatic() && !context->isModuleScopeContext()) - continue; - - OverloadChoice choice(base ? base->getInterfaceType() : nullptr, - decl, UDE->getFunctionRefKind()); - - if (base) { // Let's group all of the candidates have a common base. - candidates[base].push_back(choice); - continue; - } - - // If there is no base, it means this is one of the global functions, - // let's try to diagnose its shadowing inline. - if (diagnoseShadowing(base, choice)) - return true; - } - - if (candidates.empty()) - continue; - - for (const auto &candidate : candidates) { - if (diagnoseShadowing(candidate.getFirst(), candidate.getSecond())) - return true; - } - } - - return false; -} - -class ArgumentMatcher : public MatchCallArgumentListener { - TypeChecker &TC; - Expr *ArgExpr; - ArrayRef &Parameters; - const ParameterListInfo &ParamInfo; - SmallVectorImpl &Arguments; - - CalleeCandidateInfo CandidateInfo; - - // Indicates if problem has been found and diagnostic was emitted. - bool Diagnosed = false; - // Indicates if functions we are trying to call is a subscript. - bool IsSubscript; - - // Stores parameter bindings determined by call to matchCallArguments. - SmallVector Bindings; - -public: - ArgumentMatcher(Expr *argExpr, - ArrayRef ¶ms, - const ParameterListInfo ¶mInfo, - SmallVectorImpl &args, - CalleeCandidateInfo &CCI, bool isSubscript) - : TC(CCI.CS.TC), ArgExpr(argExpr), Parameters(params), - ParamInfo(paramInfo), Arguments(args), CandidateInfo(CCI), - IsSubscript(isSubscript) {} - - void extraArgument(unsigned extraArgIdx) override { - auto name = Arguments[extraArgIdx].getLabel(); - Expr *arg = ArgExpr; - - auto tuple = dyn_cast(ArgExpr); - if (tuple) - arg = tuple->getElement(extraArgIdx); - - auto loc = arg->getLoc(); - if (tuple && extraArgIdx == tuple->getNumElements() - 1 && - tuple->hasTrailingClosure()) - TC.diagnose(loc, diag::extra_trailing_closure_in_call) - .highlight(arg->getSourceRange()); - else if (Parameters.empty()) { - auto Paren = dyn_cast(ArgExpr); - Expr *SubExpr = nullptr; - if (Paren) { - SubExpr = Paren->getSubExpr(); - } - - if (SubExpr && CandidateInfo.CS.getType(SubExpr) && - CandidateInfo.CS.getType(SubExpr)->isVoid()) { - TC.diagnose(loc, diag::extra_argument_to_nullary_call) - .fixItRemove(SubExpr->getSourceRange()); - } else { - TC.diagnose(loc, diag::extra_argument_to_nullary_call) - .highlight(ArgExpr->getSourceRange()); - } - } else if (name.empty()) - TC.diagnose(loc, diag::extra_argument_positional) - .highlight(arg->getSourceRange()); - else - TC.diagnose(loc, diag::extra_argument_named, name) - .highlight(arg->getSourceRange()); - - Diagnosed = true; - } - - bool missingLabel(unsigned paramIdx) override { - return false; - } - - bool extraneousLabel(unsigned paramIdx) override { - return false; - } - - bool incorrectLabel(unsigned paramIdx) override { - return false; - } - - bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override { - auto &cs = CandidateInfo.CS; - OutOfOrderArgumentFailure failure(nullptr, cs, argIdx, prevArgIdx, Bindings, - cs.getConstraintLocator(ArgExpr)); - Diagnosed = failure.diagnoseAsError(); - return true; - } - - bool relabelArguments(ArrayRef newNames) override { - assert(!newNames.empty() && "No arguments were re-labeled"); - - // Let's diagnose labeling problem but only related to corrected ones. - if (diagnoseArgumentLabelError(TC.Context, ArgExpr, newNames, IsSubscript)) - Diagnosed = true; - - return true; - } - - bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override { - Expr *arg = ArgExpr; - - auto tuple = dyn_cast(ArgExpr); - if (tuple) - arg = tuple->getElement(argIdx); - - if (argIdx >= Parameters.size()) { - TC.diagnose(arg->getLoc(), diag::extra_trailing_closure_in_call) - .highlight(arg->getSourceRange()); - } else { - auto ¶m = Parameters[paramIdx]; - TC.diagnose(arg->getLoc(), diag::trailing_closure_bad_param, - param.getPlainType()) - .highlight(arg->getSourceRange()); - - auto candidate = CandidateInfo[0]; - if (candidate.getDecl()) - TC.diagnose(candidate.getDecl(), diag::decl_declared_here, - candidate.getDecl()->getFullName()); - } - Diagnosed = true; - - return true; - } - - bool diagnose() { - // Use matchCallArguments to determine how close the argument list is (in - // shape) to the specified candidates parameters. This ignores the - // concrete types of the arguments, looking only at the argument labels. - matchCallArguments(Arguments, Parameters, ParamInfo, - CandidateInfo.hasTrailingClosure, - /*allowFixes:*/ true, *this, Bindings); - - return Diagnosed; - } -}; - -/// Emit a class of diagnostics that we only know how to generate when -/// there is exactly one candidate we know about. Return true if an error -/// is emitted. -static bool -diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI, Expr *fnExpr, - Expr *argExpr, - ArrayRef argLabels) { - // We only handle the situation where there is exactly one candidate - // here. - if (CCI.size() != 1) - return false; - - auto candidate = CCI[0]; - auto &TC = CCI.CS.TC; - - if (!candidate.hasParameters()) - return false; - - auto params = candidate.getParameters(); - auto paramInfo = candidate.getParameterListInfo(params); - auto args = decomposeArgType(CCI.CS.getType(argExpr), argLabels); - - // Check the case where a raw-representable type is constructed from an - // argument with the same type: - // - // MyEnumType(MyEnumType.foo) - // - // This is missing 'rawValue:' label, but a better fix is to just remove - // the unnecessary constructor call: - // - // MyEnumType.foo - // - if (params.size() == 1 && args.size() == 1 && candidate.getDecl() && - isa(candidate.getDecl()) && candidate.skipCurriedSelf) { - AnyFunctionType::Param &arg = args[0]; - auto resTy = - candidate.getResultType()->lookThroughAllOptionalTypes(); - auto rawTy = isRawRepresentable(CCI.CS, resTy); - if (rawTy && arg.getOldType() && resTy->isEqual(arg.getOldType())) { - auto getInnerExpr = [](Expr *E) -> Expr * { - auto *parenE = dyn_cast(E); - if (!parenE) - return nullptr; - return parenE->getSubExpr(); - }; - Expr *innerE = getInnerExpr(argExpr); - - InFlightDiagnostic diag = TC.diagnose( - fnExpr->getLoc(), - diag::invalid_initialization_parameter_same_type, resTy); - diag.highlight((innerE ? innerE : argExpr)->getSourceRange()); - if (innerE) { - // Remove the unnecessary constructor call. - diag.fixItRemoveChars(fnExpr->getLoc(), innerE->getStartLoc()) - .fixItRemove(argExpr->getEndLoc()); - } - return true; - } - } - - // We only handle structural errors here. - if (CCI.closeness != CC_ArgumentLabelMismatch && - CCI.closeness != CC_ArgumentCountMismatch) - return false; - - // If we have a single candidate that failed to match the argument list, - // attempt to use matchCallArguments to diagnose the problem. - return ArgumentMatcher(argExpr, params, paramInfo, args, CCI, - isa(fnExpr)) - .diagnose(); -} - // Extract expression for failed argument number static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { if (auto *TE = dyn_cast(argExpr)) @@ -2371,40 +975,6 @@ static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, Expr *fnExpr, Expr *argExpr, ArrayRef argLabels) { - if (auto *MTT = CS.getType(fnExpr)->getAs()) { - auto instTy = MTT->getInstanceType(); - if (instTy->getAnyNominal()) { - // If we are invoking a constructor on a nominal type and there are - // absolutely no candidates, then they must all be private. - if (CCI.empty() || (CCI.size() == 1 && CCI.candidates[0].getDecl() && - isa(CCI.candidates[0].getDecl()))) { - CS.TC.diagnose(fnExpr->getLoc(), diag::no_accessible_initializers, - instTy); - return true; - } - // continue below - } else if (!instTy->is()) { - // If we are invoking a constructor on a non-nominal type, the expression - // is malformed. - SourceRange initExprRange(fnExpr->getSourceRange().Start, - argExpr->getSourceRange().End); - CS.TC.diagnose(fnExpr->getLoc(), instTy->isExistentialType() ? - diag::construct_protocol_by_name : - diag::non_nominal_no_initializers, instTy) - .highlight(initExprRange); - return true; - } - } - - // Try to diagnose errors related to the use of implicit self reference. - if (diagnoseImplicitSelfErrors(fnExpr, argExpr, CCI, argLabels)) - return true; - - // Do all the stuff that we only have implemented when there is a single - // candidate. - if (diagnoseSingleCandidateFailures(CCI, fnExpr, argExpr, argLabels)) - return true; - // If we have a failure where the candidate set differs on exactly one // argument, and where we have a consistent mismatch across the candidate set // (often because there is only one candidate in the set), then diagnose this @@ -2441,2535 +1011,187 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, return false; } -bool FailureDiagnosis::diagnoseSubscriptErrors(SubscriptExpr *SE, - bool inAssignmentDestination) { - auto baseExpr = typeCheckChildIndependently(SE->getBase()); - if (!baseExpr) return true; - auto baseType = CS.getType(baseExpr); +bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { + auto *fnExpr = callExpr->getFn(); + auto fnType = CS.getType(fnExpr)->getRValueType(); - if (isa(baseExpr)) { - diagnose(baseExpr->getLoc(), diag::cannot_subscript_nil_literal) - .highlight(baseExpr->getSourceRange()); - return true; - } + bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); + + // Collect a full candidate list of callees based on the partially type + // checked function. + CalleeCandidateInfo calleeInfo(fnExpr, hasTrailingClosure, CS); - std::function)> callback = - [&](ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(Type(), candidates, SE->hasTrailingClosure(), - CS, /*selfAlreadyApplied*/ false); + // Filter list of the candidates based on the known function type. + if (auto fn = fnType->getAs()) { + using Closeness = CalleeCandidateInfo::ClosenessResultTy; - // We're about to typecheck the index list, which needs to be processed with - // self already applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; + calleeInfo.filterList([&](OverloadCandidate candidate) -> Closeness { + auto resultType = candidate.getResultType(); + if (!resultType) + return {CC_GeneralMismatch, {}}; - auto indexExpr = - typeCheckArgumentChildIndependently(SE->getIndex(), Type(), calleeInfo); - if (!indexExpr) - return true; + // FIXME: Handle matching of the generic types properly. + // Currently we don't filter result types containing generic parameters + // because there is no easy way to do that, and candidate set is going + // to be pruned by matching of the argument types later on anyway, so + // it's better to over report than to be too conservative. + if (resultType->isEqual(fn->getResult())) + return {CC_ExactMatch, {}}; - // Back to analyzing the candidate list with self applied. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = false; + return {CC_GeneralMismatch, {}}; + }); + } - ArrayRef argLabels = SE->getArgumentLabels(); - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; + // Filter the candidate list based on the argument we may or may not have. + calleeInfo.filterContextualMemberList(callExpr->getArg()); - auto indexType = CS.getType(indexExpr); - - auto decomposedBaseType = decomposeArgType(baseType, {Identifier()}); - auto decomposedIndexType = decomposeArgType(indexType, argLabels); - calleeInfo.filterList( - [&](OverloadCandidate cand) -> CalleeCandidateInfo::ClosenessResultTy { - // Classify how close this match is. Non-subscript decls don't match. - auto subscriptDecl = dyn_cast_or_null(cand.getDecl()); - if (!subscriptDecl || - (inAssignmentDestination && !subscriptDecl->supportsMutation())) - return {CC_GeneralMismatch, {}}; - - // Check whether the self type matches. - auto selfConstraint = CC_ExactMatch; - if (calleeInfo.evaluateCloseness(cand, decomposedBaseType).first != - CC_ExactMatch) - selfConstraint = CC_SelfMismatch; - - // Set a flag to look past the self argument to the indices. - cand.skipCurriedSelf = true; - - // Explode out multi-index subscripts to find the best match. - auto indexResult = - calleeInfo.evaluateCloseness(cand, decomposedIndexType); - if (selfConstraint > indexResult.first) - return {selfConstraint, {}}; - return indexResult; - }); - - // If the closest matches all mismatch on self, we either have something - // that cannot be subscripted, or an ambiguity. - if (calleeInfo.closeness == CC_SelfMismatch) { - diagnose(SE->getLoc(), diag::cannot_subscript_base, baseType) - .highlight(SE->getBase()->getSourceRange()); - // FIXME: Should suggest overload set, but we're not ready for that until - // it points to candidates and identifies the self type in the diagnostic. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; + SmallVector argLabelsScratch; + ArrayRef argLabels = + callExpr->getArgumentLabels(argLabelsScratch); + if (diagnoseParameterErrors(calleeInfo, callExpr->getFn(), + callExpr->getArg(), argLabels)) + return true; + + Type argType; // argument list, if known. + if (auto FTy = fnType->getAs()) { + argType = FunctionType::composeInput(CS.getASTContext(), FTy->getParams(), + false); + } else if (auto MTT = fnType->getAs()) { + // If we are constructing a tuple with initializer syntax, the expected + // argument list is the tuple type itself - and there is no initdecl. + auto instanceTy = MTT->getInstanceType(); + if (auto tupleTy = instanceTy->getAs()) { + argType = tupleTy; } + } - // Any other failures relate to the index list. - for (unsigned i = 0, e = calleeInfo.size(); i != e; ++i) - calleeInfo.candidates[i].skipCurriedSelf = true; - - // TODO: Is there any reason to check for CC_NonLValueInOut here? - - if (calleeInfo.closeness == CC_ExactMatch) { - auto message = diag::ambiguous_subscript; - - // If there is an exact match on the argument with - // a single candidate, let's type-check subscript - // as a whole to figure out if there is any structural - // problem after all. - if (calleeInfo.size() == 1) { - Expr *expr = SE; - ConcreteDeclRef decl = nullptr; - message = diag::cannot_subscript_with_index; - - if (getTypeOfExpressionWithoutApplying(expr, CS.DC, decl)) - return false; - - // If we are down to a single candidate but with an unresolved - // index type, we can substitute in the base type to get a simpler - // and more concrete expected type for this subscript decl, in order - // to diagnose a better error. - if (baseType && indexType->hasUnresolvedType()) { - auto cand = calleeInfo.candidates[0]; - auto candType = baseType->getTypeOfMember(CS.DC->getParentModule(), - cand.getDecl(), nullptr); - if (auto *candFunc = candType->getAs()) { - auto paramsType = FunctionType::composeInput(CS.getASTContext(), - candFunc->getParams(), - false); - if (!typeCheckChildIndependently( - indexExpr, paramsType, CTP_CallArgument, TCC_ForceRecheck)) - return true; - } - } - } + // Get the expression result of type checking the arguments to the call + // independently, so we have some idea of what we're working with. + // + auto argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), + argType, calleeInfo, + TCC_AllowUnresolvedTypeVariables); + if (!argExpr) + return true; // already diagnosed. - diagnose(SE->getLoc(), message, baseType, indexType) - .highlight(indexExpr->getSourceRange()) - .highlight(baseExpr->getSourceRange()); + calleeInfo.filterListArgs(decomposeArgType(CS.getType(argExpr), argLabels)); - // FIXME: suggestPotentialOverloads should do this. - // calleeInfo.suggestPotentialOverloads(SE->getLoc()); - for (auto candidate : calleeInfo.candidates) - if (auto decl = candidate.getDecl()) - diagnose(decl, diag::found_candidate); - else - diagnose(candidate.getExpr()->getLoc(), diag::found_candidate); + if (diagnoseParameterErrors(calleeInfo, callExpr->getFn(), argExpr, + argLabels)) + return true; - return true; - } + // Force recheck of the arg expression because we allowed unresolved types + // before, and that turned out not to help, and now we want any diagnoses + // from disallowing them. + argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), argType, + calleeInfo, TCC_ForceRecheck); + if (!argExpr) + return true; // already diagnosed. - if (diagnoseParameterErrors(calleeInfo, SE, indexExpr, argLabels)) - return true; + auto overloadName = calleeInfo.declName; - // Diagnose some simple and common errors. - if (calleeInfo.diagnoseSimpleErrors(SE)) + // Local function to check if the error with argument type is + // related to contextual type information of the enclosing expression + // rather than resolution of argument expression itself. + auto isContextualConversionFailure = [&](Expr *argExpr) -> bool { + // If we found an exact match, this must be a problem with a conversion from + // the result of the call to the expected type. Diagnose this as a + // conversion failure. + if (calleeInfo.closeness == CC_ExactMatch) return true; - diagnose(SE->getLoc(), diag::cannot_subscript_with_index, baseType, - indexType); + if (!CS.getContextualType(callExpr) || + (calleeInfo.closeness != CC_ArgumentMismatch && + calleeInfo.closeness != CC_OneGenericArgumentMismatch)) + return false; - calleeInfo.suggestPotentialOverloads(SE->getLoc()); - return true; - }; - - auto locator = - CS.getConstraintLocator(SE, ConstraintLocator::SubscriptMember); - - return diagnoseMemberFailures(SE, baseExpr, ConstraintKind::ValueMember, - DeclBaseName::createSubscript(), - FunctionRefKind::DoubleApply, locator, - callback); -} - -bool FailureDiagnosis::visitSubscriptExpr(SubscriptExpr *SE) { - return diagnoseSubscriptErrors(SE, /* inAssignmentDestination = */ false); -} - -namespace { - /// Type checking listener for pattern binding initializers. - class CalleeListener : public ExprTypeCheckListener { - Type contextualType; - public: - explicit CalleeListener(Type contextualType) - : contextualType(contextualType) { } - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // If we have no contextual type, there is nothing to do. - if (!contextualType) - return false; - - // If the expression is obviously something that produces a metatype, - // then don't put a constraint on it. - auto semExpr = expr->getValueProvidingExpr(); - if (isa(semExpr)) - return false; - - auto resultLocator = - cs.getConstraintLocator(expr, ConstraintLocator::FunctionResult); - auto resultType = cs.createTypeVariable(resultLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - auto locator = cs.getConstraintLocator(expr); - cs.addConstraint(ConstraintKind::FunctionResult, - cs.getType(expr), - resultType, - locator); - - cs.addConstraint(ConstraintKind::Conversion, - resultType, - contextualType, - locator); - - return false; - } - }; -} // end anonymous namespace - -/// Return true if this function name is a comparison operator. This is a -/// simple heuristic used to guide comparison related diagnostics. -static bool isNameOfStandardComparisonOperator(StringRef opName) { - return opName == "==" || opName == "!=" || - opName == "===" || opName == "!==" || - opName == "<" || opName == ">" || - opName == "<=" || opName == ">="; -} - -static bool diagnoseClosureExplicitParameterMismatch( - ConstraintSystem &CS, SourceLoc loc, - ArrayRef params, - ArrayRef args) { - // We are not trying to diagnose structural problems with top-level - // arguments here. - if (params.size() != args.size()) - return false; - - for (unsigned i = 0, n = params.size(); i != n; ++i) { - auto paramType = params[i].getOldType(); - auto argType = args[i].getOldType(); - - if (auto paramFnType = paramType->getAs()) { - if (auto argFnType = argType->getAs()) - return diagnoseClosureExplicitParameterMismatch( - CS, loc, paramFnType->getParams(), argFnType->getParams()); - } - - if (!paramType || !argType || isUnresolvedOrTypeVarType(paramType) || - isUnresolvedOrTypeVarType(argType)) - continue; - - if (!CS.TC.isConvertibleTo(argType, paramType, CS.DC)) { - CS.TC.diagnose(loc, diag::types_not_convertible, false, paramType, - argType); - return true; - } - } - - return false; -} - -bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) { - if (!callExpr->hasTrailingClosure()) - return false; - - auto *DC = CS.DC; - auto *fnExpr = callExpr->getFn(); - auto *argExpr = callExpr->getArg(); - - ClosureExpr *closureExpr = nullptr; - if (auto *PE = dyn_cast(argExpr)) { - closureExpr = dyn_cast(PE->getSubExpr()); - } else { - return false; - } - - if (!closureExpr) - return false; - - class CallResultListener : public ExprTypeCheckListener { - Type expectedResultType; - - public: - explicit CallResultListener(Type resultType) - : expectedResultType(resultType) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - if (!expectedResultType) - return false; - - auto resultType = cs.getType(expr); - auto *locator = cs.getConstraintLocator(expr); - - // Since we know that this is trailing closure, format of the - // type could be like this - ((Input) -> Result) -> ClosureResult - // which we can leverage to create specific conversion for - // result type of the call itself, this might help us gain - // some valuable contextual information. - if (auto *fnType = resultType->getAs()) { - cs.addConstraint(ConstraintKind::Conversion, fnType->getResult(), - expectedResultType, locator); - } else if (auto *typeVar = resultType->getAs()) { - auto tv = cs.createTypeVariable(cs.getConstraintLocator(expr), - TVO_CanBindToLValue | - TVO_PrefersSubtypeBinding | - TVO_CanBindToNoEscape); - - auto extInfo = FunctionType::ExtInfo().withThrows(); - - FunctionType::Param tvParam(tv); - auto fTy = FunctionType::get({tvParam}, expectedResultType, extInfo); - - // Add a conversion constraint between the types. - cs.addConstraint(ConstraintKind::Conversion, typeVar, fTy, locator, - /*isFavored*/ true); - } - - return false; - } - }; - - SmallPtrSet possibleTypes; - auto currentType = CS.simplifyType(CS.getType(fnExpr)); - - // If current type has type variables or unresolved types - // let's try to re-typecheck it to see if we can get some - // more information about what is going on. - if (currentType->hasTypeVariable() || currentType->hasUnresolvedType()) { - auto contextualType = CS.getContextualType(); - CallResultListener listener(contextualType); - getPossibleTypesOfExpressionWithoutApplying( - fnExpr, CS.DC, possibleTypes, FreeTypeVariableBinding::UnresolvedType, - &listener); - - // Looks like there is there a contextual mismatch - // related to function type, let's try to diagnose it. - if (possibleTypes.empty() && contextualType && - !contextualType->hasUnresolvedType()) - return diagnoseContextualConversionError(callExpr, contextualType, - CS.getContextualTypePurpose()); - } else { - possibleTypes.insert(currentType.getPointer()); - } - - for (Type type : possibleTypes) { - auto *fnType = type->getAs(); - if (!fnType) - continue; - - auto params = fnType->getParams(); - if (params.size() != 1) - return false; - - Type paramType = params.front().getOldType(); - if (auto paramFnType = paramType->getAs()) { - auto closureType = CS.getType(closureExpr); - if (auto *argFnType = closureType->getAs()) { - auto *params = closureExpr->getParameters(); - auto loc = params ? params->getStartLoc() : closureExpr->getStartLoc(); - if (diagnoseClosureExplicitParameterMismatch( - CS, loc, argFnType->getParams(), paramFnType->getParams())) - return true; - } - } - - auto processor = [&](Type resultType, Type expectedResultType) -> bool { - if (resultType && expectedResultType) { - if (!resultType->isEqual(expectedResultType)) { - CS.TC.diagnose(closureExpr->getEndLoc(), - diag::cannot_convert_closure_result, resultType, - expectedResultType); - return true; - } - - // Looks like both actual and expected result types match, - // there is nothing we can diagnose in this case. - return false; - } - - // If we got a result type, let's re-typecheck the function using it, - // maybe we can find a problem where contextually we expect one type - // but trailing closure produces completely different one. - auto fnType = paramType->getAs(); - if (!fnType) - return false; - - class ClosureCalleeListener : public ExprTypeCheckListener { - FunctionType *InputType; - Type ResultType; - - public: - explicit ClosureCalleeListener(FunctionType *inputType, Type resultType) - : InputType(inputType), ResultType(resultType) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - if (!ResultType) - return false; - - AnyFunctionType::Param Input(InputType); - auto expectedType = FunctionType::get({Input}, ResultType); - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - expectedType, cs.getConstraintLocator(expr), - /*isFavored*/ true); - return false; - } - }; - - auto expectedArgType = FunctionType::get(fnType->getParams(), resultType, - fnType->getExtInfo()); - - llvm::SaveAndRestore SavedDC(CS.DC, DC); - ClosureCalleeListener listener(expectedArgType, CS.getContextualType()); - return !typeCheckChildIndependently(callExpr->getFn(), Type(), - CTP_CalleeResult, TCC_ForceRecheck, - &listener); - }; - - // Let's see if there are any structural problems with closure itself. - if (diagnoseClosureExpr(closureExpr, paramType, processor)) - return true; - } - - return false; -} - -/// Check if there failure associated with expression is related -/// to given contextual type. -bool FailureDiagnosis::diagnoseCallContextualConversionErrors( - ApplyExpr *callExpr, Type contextualType, ContextualTypePurpose CTP) { - if (!contextualType || contextualType->hasUnresolvedType()) - return false; - - auto &TC = CS.TC; - auto *DC = CS.DC; - - auto typeCheckExpr = [&](TypeChecker &TC, Expr *expr, DeclContext *DC, - SmallPtrSetImpl &types) { - getPossibleTypesOfExpressionWithoutApplying( - expr, DC, types, FreeTypeVariableBinding::Disallow); - }; - - // First let's type-check expression without contextual type, and - // see if that's going to produce a type, if so, let's type-check - // again, this time using given contextual type. - SmallPtrSet withoutContextual; - typeCheckExpr(TC, callExpr, DC, withoutContextual); - - // If there are no types returned, it means that problem was - // nothing to do with contextual information, probably parameter/argument - // mismatch. - if (withoutContextual.empty()) - return false; - - Type exprType = withoutContextual.size() == 1 ? *withoutContextual.begin() : Type(); - return diagnoseContextualConversionError(callExpr, contextualType, CTP, - exprType); -} - -// Check if there is a structural problem in the function expression -// by performing type checking with the option to allow unresolved -// type variables. If that is going to produce a function type with -// unresolved result let's not re-typecheck the function expression, -// because it might produce unrelated diagnostics due to lack of -// contextual information. -static bool shouldTypeCheckFunctionExpr(FailureDiagnosis &FD, DeclContext *DC, - Expr *fnExpr) { - if (!isa(fnExpr)) - return true; - - SmallPtrSet fnTypes; - FD.getPossibleTypesOfExpressionWithoutApplying( - fnExpr, DC, fnTypes, FreeTypeVariableBinding::UnresolvedType); - - if (fnTypes.size() == 1) { - // Some member types depend on the arguments to produce a result type, - // type-checking such expressions without associated arguments is - // going to produce unrelated diagnostics. - if (auto fn = (*fnTypes.begin())->getAs()) { - auto resultType = fn->getResult(); - if (resultType->hasUnresolvedType() || resultType->hasTypeVariable()) - return false; - } - } - - // Might be a structural problem related to the member itself. - return true; -} - -// Check if any candidate of the overload set can accept a specified -// number of arguments, regardless of parameter type or label information. -static bool isViableOverloadSet(const CalleeCandidateInfo &CCI, - size_t numArgs) { - for (unsigned i = 0; i < CCI.size(); ++i) { - auto &&cand = CCI[i]; - auto funcDecl = dyn_cast_or_null(cand.getDecl()); - - // If we don't have a func decl or we haven't resolved its parameters, - // continue. The latter case can occur with `type(of:)`, which is introduced - // as a type variable. - if (!funcDecl || !cand.hasParameters()) - continue; - - auto params = cand.getParameters(); - bool hasVariadicParameter = false; - auto pairMatcher = [&](unsigned argIdx, unsigned paramIdx) { - hasVariadicParameter |= params[paramIdx].isVariadic(); - return true; - }; - - auto paramInfo = cand.getParameterListInfo(params); - InputMatcher IM(params, paramInfo); - auto result = IM.match(numArgs, pairMatcher); - if (result == InputMatcher::IM_Succeeded) - return true; - if (result == InputMatcher::IM_HasUnclaimedInput && hasVariadicParameter) - return true; - } - return false; -} - -bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { - // If this call involves trailing closure as an argument, - // let's treat it specially, because re-typecheck of the - // either function or arguments might results in diagnosing - // of the unrelated problems due to luck of context. - if (diagnoseTrailingClosureErrors(callExpr)) - return true; - - if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType(), - CS.getContextualTypePurpose())) - return true; - - auto *fnExpr = callExpr->getFn(); - auto originalFnType = CS.getType(callExpr->getFn()); - - if (shouldTypeCheckFunctionExpr(*this, CS.DC, fnExpr)) { - // Type check the function subexpression to resolve a type for it if - // possible. - fnExpr = typeCheckChildIndependently(callExpr->getFn()); - if (!fnExpr) { - return CS.TC.Diags.hadAnyError(); - } - } - - SWIFT_DEFER { - if (!fnExpr) return; - - // If it's a member operator reference, put the operator back. - if (auto operatorRef = fnExpr->getMemberOperatorRef()) - callExpr->setFn(operatorRef); - }; - - auto getFuncType = [](Type type) -> Type { return type->getRValueType(); }; - - auto fnType = getFuncType(CS.getType(fnExpr)); - - // Let's see if this has to do with member vs. property error - // because sometimes when there is a member and a property declared - // on the nominal type with the same name. Type-checking function - // expression separately from arguments might produce solution for - // the property instead of the member. - if (!fnType->is() && - isa(callExpr->getFn())) { - fnExpr = callExpr->getFn(); - - SmallPtrSet types; - getPossibleTypesOfExpressionWithoutApplying(fnExpr, CS.DC, types); - - auto isFunctionType = [getFuncType](Type type) -> bool { - return type && getFuncType(type)->is(); - }; - - auto fnTypes = std::find_if(types.begin(), types.end(), isFunctionType); - if (fnTypes != types.end()) { - auto funcType = getFuncType(*fnTypes); - // If there is only one function type, let's use it. - if (std::none_of(std::next(fnTypes), types.end(), isFunctionType)) - fnType = funcType; - } else { - fnType = getFuncType(originalFnType); - } - } - - // If we have a contextual type, and if we have an ambiguously typed function - // result from our previous check, we re-type-check it using this contextual - // type to inform the result type of the callee. - // - // We only do this as a second pass because the first pass we just did may - // return something of obviously non-function-type. If this happens, we - // produce better diagnostics below by diagnosing this here rather than trying - // to peel apart the failed conversion to function type. - if (CS.getContextualType() && - (isUnresolvedOrTypeVarType(fnType) || - (fnType->is() && fnType->hasUnresolvedType()))) { - // FIXME: Prevent typeCheckChildIndependently from transforming expressions, - // because if we try to typecheck OSR expression with contextual type, - // it'll end up converting it into DeclRefExpr based on contextual info, - // instead let's try to get a type without applying and filter callee - // candidates later on. - CalleeListener listener(CS.getContextualType()); - - if (isa(fnExpr)) { - assert(!cast(fnExpr)->getReferencedDecl() && - "unexpected declaration reference"); - - ConcreteDeclRef decl = nullptr; - Type type = getTypeOfExpressionWithoutApplying( - fnExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType, - &listener); - - if (type) - fnType = getFuncType(type); - } else { - fnExpr = typeCheckChildIndependently(callExpr->getFn(), Type(), - CTP_CalleeResult, TCC_ForceRecheck, - &listener); - if (!fnExpr) - return true; - - fnType = getFuncType(CS.getType(fnExpr)); - } - } - - // If we resolved a concrete expression for the callee, and it has - // non-function/non-metatype type, then we cannot call it! - if (!isUnresolvedOrTypeVarType(fnType) && - !fnType->is() && !fnType->is()) { - - auto arg = callExpr->getArg(); - auto isDynamicCallable = - CS.DynamicCallableCache[fnType->getCanonicalType()].isValid(); - - // Note: Consider caching `hasCallAsFunctionMethods` in `NominalTypeDecl`. - auto *nominal = fnType->getAnyNominal(); - auto hasCallAsFunctionMethods = nominal && - llvm::any_of(nominal->getMembers(), [](Decl *member) { - auto funcDecl = dyn_cast(member); - return funcDecl && funcDecl->isCallAsFunctionMethod(); - }); - - // Diagnose @dynamicCallable errors. - if (isDynamicCallable) { - auto dynamicCallableMethods = - CS.DynamicCallableCache[fnType->getCanonicalType()]; - - // Diagnose dynamic calls with keywords on @dynamicCallable types that - // don't define the `withKeywordArguments` method. - if (auto tuple = dyn_cast(arg)) { - bool hasArgLabel = llvm::any_of( - tuple->getElementNames(), [](Identifier i) { return !i.empty(); }); - if (hasArgLabel && - dynamicCallableMethods.keywordArgumentsMethods.empty()) { - diagnose(callExpr->getFn()->getStartLoc(), - diag::missing_dynamic_callable_kwargs_method, fnType); - return true; - } - } - } - - if (fnType->is()) { - auto diag = diagnose(arg->getStartLoc(), - diag::missing_init_on_metatype_initialization); - diag.highlight(fnExpr->getSourceRange()); - } - - if (!fnType->is()) { - auto diag = diagnose(arg->getStartLoc(), - diag::cannot_call_non_function_value, fnType); - diag.highlight(fnExpr->getSourceRange()); - - // If the argument is an empty tuple, then offer a - // fix-it to remove the empty tuple and use the value - // directly. - if (auto tuple = dyn_cast(arg)) { - if (tuple->getNumElements() == 0) { - diag.fixItRemove(arg->getSourceRange()); - } - } - } - - // If the argument is a trailing ClosureExpr (i.e. {....}) and it is on - // the line after the callee, then it's likely the user forgot to - // write "do" before their brace stmt. - // Note that line differences of more than 1 are diagnosed during parsing. - if (auto *PE = dyn_cast(arg)) - if (PE->hasTrailingClosure() && isa(PE->getSubExpr())) { - auto *closure = cast(PE->getSubExpr()); - auto &SM = CS.getASTContext().SourceMgr; - if (closure->hasAnonymousClosureVars() && - closure->getParameters()->size() == 0 && - 1 + SM.getLineNumber(callExpr->getFn()->getEndLoc()) == - SM.getLineNumber(closure->getStartLoc())) { - diagnose(closure->getStartLoc(), diag::brace_stmt_suggest_do) - .fixItInsert(closure->getStartLoc(), "do "); - } - } - - if (!isDynamicCallable && !hasCallAsFunctionMethods) - return true; - } - - bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); - - // Collect a full candidate list of callees based on the partially type - // checked function. - CalleeCandidateInfo calleeInfo(fnExpr, hasTrailingClosure, CS); - - // In the case that function subexpression was resolved independently in - // the first place, the resolved type may not provide the best diagnostic. - // We consider the number of arguments to decide whether we'd go with it or - // stay with the original one. - if (fnExpr != callExpr->getFn()) { - bool isInstanceMethodAsCurriedMemberOnType = false; - if (!calleeInfo.empty()) { - auto &&cand = calleeInfo[0]; - auto decl = cand.getDecl(); - if (decl && decl->isInstanceMember() && !cand.skipCurriedSelf && - cand.getParameters().size() == 1) - isInstanceMethodAsCurriedMemberOnType = true; - } - - // In terms of instance method as curried member on type, we should not - // take the number of arguments into account. - if (!isInstanceMethodAsCurriedMemberOnType) { - size_t numArgs = 1; - auto arg = callExpr->getArg(); - if (auto tuple = dyn_cast(arg)) { - numArgs = tuple->getNumElements(); - } - - if (!isViableOverloadSet(calleeInfo, numArgs)) { - CalleeCandidateInfo calleeInfoOrig(callExpr->getFn(), - hasTrailingClosure, CS); - if (isViableOverloadSet(calleeInfoOrig, numArgs)) { - fnExpr = callExpr->getFn(); - fnType = getFuncType(CS.getType(fnExpr)); - calleeInfo = calleeInfoOrig; - } - } - } - } - - // Filter list of the candidates based on the known function type. - if (auto fn = fnType->getAs()) { - using Closeness = CalleeCandidateInfo::ClosenessResultTy; - - calleeInfo.filterList([&](OverloadCandidate candidate) -> Closeness { - auto resultType = candidate.getResultType(); - if (!resultType) - return {CC_GeneralMismatch, {}}; - - // FIXME: Handle matching of the generic types properly. - // Currently we don't filter result types containing generic parameters - // because there is no easy way to do that, and candidate set is going - // to be pruned by matching of the argument types later on anyway, so - // it's better to over report than to be too conservative. - if (resultType->isEqual(fn->getResult())) - return {CC_ExactMatch, {}}; - - return {CC_GeneralMismatch, {}}; - }); - } - - // Filter the candidate list based on the argument we may or may not have. - calleeInfo.filterContextualMemberList(callExpr->getArg()); - - SmallVector argLabelsScratch; - ArrayRef argLabels = - callExpr->getArgumentLabels(argLabelsScratch); - if (diagnoseParameterErrors(calleeInfo, callExpr->getFn(), - callExpr->getArg(), argLabels)) - return true; - - Type argType; // argument list, if known. - if (auto FTy = fnType->getAs()) { - argType = FunctionType::composeInput(CS.getASTContext(), FTy->getParams(), - false); - } else if (auto MTT = fnType->getAs()) { - // If we are constructing a tuple with initializer syntax, the expected - // argument list is the tuple type itself - and there is no initdecl. - auto instanceTy = MTT->getInstanceType(); - if (auto tupleTy = instanceTy->getAs()) { - argType = tupleTy; - } - } - - // Let's check whether this is a situation when callee expects - // no arguments but N are given. Otherwise, just below - // `typeCheckArgumentChild*` is going to use `()` is a contextual type which - // is incorrect. - if (argType && argType->isVoid()) { - auto *argExpr = callExpr->getArg(); - if (isa(argExpr) || - (isa(argExpr) && - cast(argExpr)->getNumElements() > 0)) { - diagnose(callExpr->getLoc(), diag::extra_argument_to_nullary_call) - .highlight(argExpr->getSourceRange()); - return true; - } - } - - // Get the expression result of type checking the arguments to the call - // independently, so we have some idea of what we're working with. - // - auto argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), - argType, calleeInfo, - TCC_AllowUnresolvedTypeVariables); - if (!argExpr) - return true; // already diagnosed. - - calleeInfo.filterListArgs(decomposeArgType(CS.getType(argExpr), argLabels)); - - if (diagnoseParameterErrors(calleeInfo, callExpr->getFn(), argExpr, - argLabels)) - return true; - - // Diagnose some simple and common errors. - if (calleeInfo.diagnoseSimpleErrors(callExpr)) - return true; - - // Force recheck of the arg expression because we allowed unresolved types - // before, and that turned out not to help, and now we want any diagnoses - // from disallowing them. - argExpr = typeCheckArgumentChildIndependently(callExpr->getArg(), argType, - calleeInfo, TCC_ForceRecheck); - if (!argExpr) - return true; // already diagnosed. - - // Handle argument label mismatches when we have multiple candidates. - if (calleeInfo.closeness == CC_ArgumentLabelMismatch) { - auto args = decomposeArgType(CS.getType(argExpr), argLabels); - - // If we have multiple candidates that we fail to match, just say we have - // the wrong labels and list the candidates out. - diagnose(callExpr->getLoc(), diag::wrong_argument_labels_overload, - getParamListAsString(args)) - .highlight(argExpr->getSourceRange()); - - // Did the user intend on invoking a different overload? - calleeInfo.suggestPotentialOverloads(fnExpr->getLoc()); - return true; - } - - auto overloadName = calleeInfo.declName; - - // Local function to check if the error with argument type is - // related to contextual type information of the enclosing expression - // rather than resolution of argument expression itself. - auto isContextualConversionFailure = [&](Expr *argExpr) -> bool { - // If we found an exact match, this must be a problem with a conversion from - // the result of the call to the expected type. Diagnose this as a - // conversion failure. - if (calleeInfo.closeness == CC_ExactMatch) - return true; - - if (!CS.getContextualType() || - (calleeInfo.closeness != CC_ArgumentMismatch && - calleeInfo.closeness != CC_OneGenericArgumentMismatch)) - return false; - - CalleeCandidateInfo candidates(fnExpr, hasTrailingClosure, CS); - - // Filter original list of choices based on the deduced type of - // argument expression after force re-check. - candidates.filterContextualMemberList(argExpr); - - // One of the candidates matches exactly, which means that - // this is a contextual type conversion failure, we can't diagnose here. - return candidates.closeness == CC_ExactMatch; - }; - - // Otherwise, we have a generic failure. Diagnose it with a generic error - // message now. - if (isa(callExpr) && isa(argExpr)) { - auto argTuple = cast(argExpr); - auto lhsExpr = argTuple->getElement(0), rhsExpr = argTuple->getElement(1); - auto lhsType = CS.getType(lhsExpr)->getRValueType(); - auto rhsType = CS.getType(rhsExpr)->getRValueType(); - - // TODO(diagnostics): There are still cases not yet handled by new - // diagnostics framework e.g. - // - // var tuple = (1, 2, 3) - // switch tuple { - // case (let (_, _, _)) + 1: break - // } - if (callExpr->isImplicit() && overloadName == "~=") { - auto flags = ParameterTypeFlags(); - if (calleeInfo.candidates.size() == 1) - if (auto fnType = calleeInfo.candidates[0].getFunctionType()) - flags = fnType->getParams()[0].getParameterFlags(); - - auto *locator = CS.getConstraintLocator( - callExpr, - {ConstraintLocator::ApplyArgument, - LocatorPathElt::ApplyArgToParam(0, 0, flags)}, - /*summaryFlags=*/0); - - ArgumentMismatchFailure failure(expr, CS, lhsType, rhsType, locator); - return failure.diagnosePatternMatchingMismatch(); - } - - if (isContextualConversionFailure(argTuple)) - return false; - - if (!lhsType->isEqual(rhsType)) { - auto diag = diagnose(callExpr->getLoc(), diag::cannot_apply_binop_to_args, - overloadName, lhsType, rhsType); - diag.highlight(lhsExpr->getSourceRange()) - .highlight(rhsExpr->getSourceRange()); - } else { - diagnose(callExpr->getLoc(), diag::cannot_apply_binop_to_same_args, - overloadName, lhsType) - .highlight(lhsExpr->getSourceRange()) - .highlight(rhsExpr->getSourceRange()); - } - - if (lhsType->isEqual(rhsType) && - isNameOfStandardComparisonOperator(overloadName) && - lhsType->is() && - !lhsType->getAs()->getDecl() - ->hasOnlyCasesWithoutAssociatedValues()) { - diagnose(callExpr->getLoc(), - diag::no_binary_op_overload_for_enum_with_payload, - overloadName); - } else { - calleeInfo.suggestPotentialOverloads(callExpr->getLoc()); - } - - return true; - } - - // If we have a failure where closeness is an exact match, but there is - // still a failed argument, it is because one (or more) of the arguments - // types are unresolved. - if (calleeInfo.closeness == CC_ExactMatch && calleeInfo.failedArgument.isValid()) { - diagnoseAmbiguity(getFailedArgumentExpr(calleeInfo, argExpr)); - return true; - } - - if (isContextualConversionFailure(argExpr)) - return false; - - // Generate specific error messages for unary operators. - if (isa(callExpr) || isa(callExpr)) { - assert(!overloadName.empty()); - diagnose(argExpr->getLoc(), diag::cannot_apply_unop_to_arg, overloadName, - CS.getType(argExpr)); - - calleeInfo.suggestPotentialOverloads(argExpr->getLoc()); - return true; - } - - if (CS.getType(argExpr)->hasUnresolvedType()) - return false; - - SmallVector params; - AnyFunctionType::decomposeInput(CS.getType(argExpr), params); - auto argString = AnyFunctionType::getParamListAsString(params); - - // If we couldn't get the name of the callee, then it must be something of a - // more complex "value of function type". - if (overloadName.empty()) { - // If we couldn't infer the result type of the closure expr, then we have - // some sort of ambiguity, let the ambiguity diagnostic stuff handle this. - if (auto ffty = fnType->getAs()) - if (ffty->getResult()->hasTypeVariable()) { - diagnoseAmbiguity(fnExpr); - return true; - } - - // The most common unnamed value of closure type is a ClosureExpr, so - // special case it. - if (isa(fnExpr->getValueProvidingExpr())) { - if (fnType->hasTypeVariable()) - diagnose(argExpr->getStartLoc(), diag::cannot_invoke_closure, argString) - .highlight(fnExpr->getSourceRange()); - else - diagnose(argExpr->getStartLoc(), diag::cannot_invoke_closure_type, - fnType, argString) - .highlight(fnExpr->getSourceRange()); - - } else if (fnType->hasTypeVariable()) { - diagnose(argExpr->getStartLoc(), diag::cannot_call_function_value, - argString) - .highlight(fnExpr->getSourceRange()); - } else { - diagnose(argExpr->getStartLoc(), diag::cannot_call_value_of_function_type, - fnType, argString) - .highlight(fnExpr->getSourceRange()); - } - - return true; - } - - if (auto MTT = fnType->getAs()) { - if (MTT->getInstanceType()->isExistentialType()) { - diagnose(fnExpr->getLoc(), diag::construct_protocol_value, fnType); - return true; - } - } - - bool isInitializer = isa(fnExpr); - if (isa(argExpr) && - cast(argExpr)->getNumElements() == 0) { - // Emit diagnostics that say "no arguments". - diagnose(fnExpr->getLoc(), diag::cannot_call_with_no_params, - overloadName, isInitializer); - } else { - diagnose(fnExpr->getLoc(), diag::cannot_call_with_params, - overloadName, argString, isInitializer); - } - - // Did the user intend on invoking a different overload? - calleeInfo.suggestPotentialOverloads(fnExpr->getLoc()); - return true; -} - -bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) { - // Diagnose obvious assignments to literals. - if (isa(assignExpr->getDest()->getValueProvidingExpr())) { - diagnose(assignExpr->getLoc(), diag::cannot_assign_to_literal); - return true; - } - - // Situation like `var foo = &bar` didn't get diagnosed early - // because originally its parent is a `SequenceExpr` which hasn't - // been folded yet, and could represent an operator which accepts - // `inout` arguments. - if (auto *AddrOf = dyn_cast(assignExpr->getSrc())) { - diagnose(AddrOf->getLoc(), diag::extraneous_address_of); - return true; - } - - if (CS.TC.diagnoseSelfAssignment(assignExpr)) - return true; - - // Type check the destination first, so we can coerce the source to it. - auto destExpr = typeCheckChildIndependently(assignExpr->getDest(), - TCC_AllowLValue); - if (!destExpr) return true; - - auto destType = CS.getType(destExpr); - if (destType->is() || destType->hasTypeVariable()) { - // Look closer into why destination has unresolved types since such - // means that destination has diagnosable structural problems, and it's - // better to diagnose destination (if possible) before moving on to - // the source of the assignment. - destExpr = typeCheckChildIndependently( - destExpr, TCC_AllowLValue | TCC_ForceRecheck, false); - if (!destExpr) - return true; - - // If re-checking destination didn't produce diagnostic, let's just type - // check the source without contextual information. If it succeeds, then we - // win, but if it fails, we'll have to diagnose this another way. - return !typeCheckChildIndependently(assignExpr->getSrc()); - } - - // If the result type is a non-lvalue, then we are failing because it is - // immutable and that's not a great thing to assign to. - if (!destType->hasLValueType()) { - // If the destination is a subscript, the problem may actually be that we - // incorrectly decided on a get-only subscript overload, and we may be able - // to come up with a better diagnosis by looking only at subscript candidates - // that are set-able. - if (auto subscriptExpr = dyn_cast(destExpr)) { - if (diagnoseSubscriptErrors(subscriptExpr, /* inAssignmentDestination = */ true)) - return true; - } - // Member ref assignment errors detected elsewhere, so not an assignment issue if found here. - // The remaining exception involves mutable pointer conversions which aren't always caught elsewhere. - PointerTypeKind ptk; - if (!isa(destExpr) || CS.getType(destExpr) - ->lookThroughAllOptionalTypes() - ->getAnyPointerElementType(ptk)) { - AssignmentFailure failure(destExpr, CS, assignExpr->getLoc()); - if (failure.diagnoseAsError()) - return true; - } - } - - auto *srcExpr = assignExpr->getSrc(); - auto contextualType = destType->getRValueType(); - auto contextualTypePurpose = isa(destExpr) - ? CTP_SubscriptAssignSource - : CTP_AssignSource; - // Let's try to type-check assignment source expression without using - // destination as a contextual type, that allows us to diagnose - // contextual problems related to source much easier. - // - // If source expression requires contextual type to be present, - // let's avoid this step because it's always going to fail. - { - auto *srcExpr = assignExpr->getSrc(); - ExprTypeSaverAndEraser eraser(srcExpr); - - ConcreteDeclRef ref = nullptr; - auto type = getTypeOfExpressionWithoutApplying(srcExpr, CS.DC, ref); - - if (type && !type->isEqual(contextualType)) - return diagnoseContextualConversionError( - assignExpr->getSrc(), contextualType, contextualTypePurpose); - } - - srcExpr = typeCheckChildIndependently(assignExpr->getSrc(), contextualType, - contextualTypePurpose); - if (!srcExpr) - return true; - - // If we are assigning to _ and have unresolved types on the RHS, then we have - // an ambiguity problem. - if (isa(destExpr->getSemanticsProvidingExpr()) && - CS.getType(srcExpr)->hasUnresolvedType()) { - diagnoseAmbiguity(srcExpr); - return true; - } - - return false; -} - -bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) { - return false; -} - -bool FailureDiagnosis::visitCoerceExpr(CoerceExpr *CE) { - // Coerce the input to whatever type is specified by the CoerceExpr. - auto expr = typeCheckChildIndependently(CE->getSubExpr(), - CS.getType(CE->getCastTypeLoc()), - CTP_CoerceOperand); - if (!expr) - return true; - - auto ref = expr->getReferencedDecl(); - if (auto *decl = ref.getDecl()) { - // Without explicit coercion we might end up - // type-checking sub-expression as unavaible - // declaration, let's try to diagnose that here. - if (AvailableAttr::isUnavailable(decl)) - return diagnoseExplicitUnavailability( - decl, expr->getSourceRange(), CS.DC, dyn_cast(expr)); - } - - return false; -} - -bool FailureDiagnosis::visitForceValueExpr(ForceValueExpr *FVE) { - auto argExpr = typeCheckChildIndependently(FVE->getSubExpr()); - if (!argExpr) return true; - auto argType = CS.getType(argExpr); - - // If the subexpression type checks as a non-optional type, then that is the - // error. Produce a specific diagnostic about this. - if (!isUnresolvedOrTypeVarType(argType) && - argType->getOptionalObjectType().isNull()) { - diagnose(FVE->getLoc(), diag::invalid_force_unwrap, argType) - .fixItRemove(FVE->getExclaimLoc()) - .highlight(FVE->getSourceRange()); - return true; - } - - return false; -} - -bool FailureDiagnosis::visitBindOptionalExpr(BindOptionalExpr *BOE) { - auto argExpr = typeCheckChildIndependently(BOE->getSubExpr()); - if (!argExpr) return true; - auto argType = CS.getType(argExpr); - - // If the subexpression type checks as a non-optional type, then that is the - // error. Produce a specific diagnostic about this. - if (!isUnresolvedOrTypeVarType(argType) && - argType->getOptionalObjectType().isNull()) { - diagnose(BOE->getQuestionLoc(), diag::invalid_optional_chain, argType) - .highlight(BOE->getSourceRange()) - .fixItRemove(BOE->getQuestionLoc()); - return true; - } - - return false; -} - -bool FailureDiagnosis::visitIfExpr(IfExpr *IE) { - auto typeCheckClauseExpr = [&](Expr *clause, Type contextType = Type(), - ContextualTypePurpose convertPurpose = - CTP_Unused) -> Expr * { - // Provide proper contextual type when type conversion is specified. - return typeCheckChildIndependently(clause, contextType, convertPurpose, - TCCOptions(), nullptr, false); - }; - // Check all of the subexpressions independently. - auto condExpr = typeCheckClauseExpr(IE->getCondExpr()); - if (!condExpr) return true; - auto trueExpr = typeCheckClauseExpr(IE->getThenExpr(), CS.getContextualType(), - CS.getContextualTypePurpose()); - if (!trueExpr) return true; - auto falseExpr = typeCheckClauseExpr( - IE->getElseExpr(), CS.getContextualType(), CS.getContextualTypePurpose()); - if (!falseExpr) return true; - - // If the true/false values already match, it must be a contextual problem. - if (CS.getType(trueExpr)->isEqual(CS.getType(falseExpr))) - return false; - - // Otherwise, the true/false result types must not be matching. - diagnose(IE->getColonLoc(), diag::if_expr_cases_mismatch, - CS.getType(trueExpr), CS.getType(falseExpr)) - .highlight(trueExpr->getSourceRange()) - .highlight(falseExpr->getSourceRange()); - return true; -} - - -bool FailureDiagnosis:: -visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { - // Don't walk the children for this node, it leads to multiple diagnostics - // because of how sema injects this node into the type checker. - return false; -} - -bool FailureDiagnosis::visitCaptureListExpr(CaptureListExpr *CLE) { - // Always walk into the closure of a capture list expression. - return visitClosureExpr(CLE->getClosureBody()); -} - -static bool isInvalidClosureResultType(Type resultType) { - return !resultType || resultType->hasUnresolvedType() || - resultType->hasTypeVariable() || resultType->hasArchetype(); -} - -bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) { - return diagnoseClosureExpr( - CE, CS.getContextualType(), - [&](Type resultType, Type expectedResultType) -> bool { - if (isInvalidClosureResultType(expectedResultType)) - return false; - - // Following situations are possible: - // * No result type - possible structurable problem in the body; - // * Function result type - possible use of function without calling it, - // which is properly diagnosed by actual type-check call. - if (resultType && !resultType->getRValueType()->is()) { - if (!resultType->isEqual(expectedResultType)) { - diagnose(CE->getEndLoc(), diag::cannot_convert_closure_result, - resultType, expectedResultType); - return true; - } - } - return false; - }); -} - -bool FailureDiagnosis::diagnoseClosureExpr( - ClosureExpr *CE, Type contextualType, - llvm::function_ref resultTypeProcessor) { - // Look through IUO because it doesn't influence - // neither parameter nor return type diagnostics itself, - // but if we have function type inside, that might - // signficantly improve diagnostic quality. - // FIXME: We need to rework this with IUOs out of the type system. - // if (contextualType) { - // if (auto IUO = - // CS.lookThroughImplicitlyUnwrappedOptionalType(contextualType)) - // contextualType = IUO; - // } - - Type expectedResultType; - - // If we have a contextual type available for this closure, apply it to the - // ParamDecls in our parameter list. This ensures that any uses of them get - // appropriate types. - if (contextualType && contextualType->is()) { - auto fnType = contextualType->getAs(); - auto *params = CE->getParameters(); - auto inferredArgs = fnType->getParams(); - - // It is very common for a contextual type to disagree with the argument - // list built into the closure expr. This can be because the closure expr - // had an explicitly specified pattern, a la: - // { a,b in ... } - // or could be because the closure has an implicitly generated one: - // { $0 + $1 } - // in either case, we want to produce nice and clear diagnostics. - unsigned actualArgCount = params->size(); - unsigned inferredArgCount = inferredArgs.size(); - - if (actualArgCount != inferredArgCount) { - if (inferredArgCount == 1 && actualArgCount > 1) { - auto *argTupleTy = inferredArgs.front().getOldType()->getAs(); - // Let's see if inferred argument is actually a tuple inside of Paren. - if (argTupleTy) { - // Looks like the number of closure parameters matches number - // of inferred arguments, which means we can we can emit an - // error about an attempt to make use of tuple splat or tuple - // destructuring and provide a proper fix-it. - if (argTupleTy->getNumElements() == actualArgCount) { - ClosureParamDestructuringFailure failure( - expr, CS, fnType, CS.getConstraintLocator(CE)); - return failure.diagnoseAsError(); - } - } - } - - // Extraneous arguments. - if (inferredArgCount < actualArgCount) { - auto diag = diagnose( - params->getStartLoc(), diag::closure_argument_list_tuple, fnType, - inferredArgCount, actualArgCount, (actualArgCount == 1)); - - bool onlyAnonymousParams = - std::all_of(params->begin(), params->end(), - [](ParamDecl *param) { return !param->hasName(); }); - - // If closure expects no parameters but N was given, - // and all of them are anonymous let's suggest removing them. - if (inferredArgCount == 0 && onlyAnonymousParams) { - auto inLoc = CE->getInLoc(); - auto &sourceMgr = CS.getASTContext().SourceMgr; - - if (inLoc.isValid()) - diag.fixItRemoveChars(params->getStartLoc(), - Lexer::getLocForEndOfToken(sourceMgr, inLoc)); - } - return true; - } - - // Missing arguments are already diagnosed via new diagnostic framework. - return false; - } - - // Coerce parameter types here only if there are no unresolved - CS.TC.coerceParameterListToType(params, CE, fnType); - expectedResultType = fnType->getResult(); - } - - // Defend against type variables from our constraint system leaking into - // recursive constraints systems formed when checking the body of the - // closure. These typevars come into them when the body does name - // lookups against the parameter decls. - // - // Handle this by rewriting the arguments to UnresolvedType(). - for (auto VD : *CE->getParameters()) { - if (VD->hasType() && (VD->getType()->hasTypeVariable() || - VD->getType()->hasError())) { - VD->setType(CS.getASTContext().TheUnresolvedType); - VD->setInterfaceType(VD->getType()); - } - } - - // If this is a complex leaf closure, there is nothing more we can do. - if (!CE->hasSingleExpressionBody()) - return false; - - if (isInvalidClosureResultType(expectedResultType)) - expectedResultType = Type(); - - // When we're type checking a single-expression closure, we need to reset the - // DeclContext to this closure for the recursive type checking. Otherwise, - // if there is a closure in the subexpression, we can violate invariants. - { - llvm::SaveAndRestore SavedDC(CS.DC, CE); - - // Explicitly disallow to produce solutions with unresolved type variables, - // because there is no auxiliary logic which would handle that and it's - // better to allow failure diagnosis to run directly on the closure body. - // Note that presence of contextual type implicitly forbids such solutions, - // but it's not always reset. - - if (expectedResultType && !CE->hasExplicitResultType()) { - auto closure = CE->getSingleExpressionBody(); - ConcreteDeclRef decl = nullptr; - // Let's try to compute result type without mutating AST and - // using expected (contextual) result type, that's going to help - // diagnose situations where contextual type expected one result - // type but actual closure produces a different one without explicitly - // declaring it (e.g. by using anonymous parameters). - auto type = getTypeOfExpressionWithoutApplying( - closure, CS.DC, decl, FreeTypeVariableBinding::Disallow); - - if (type && resultTypeProcessor(type, expectedResultType)) - return true; - } - - // If the closure had an expected result type, use it. - if (CE->hasExplicitResultType()) - expectedResultType = CE->getExplicitResultTypeLoc().getType(); - - // If we couldn't diagnose anything related to the contextual result type - // let's run proper type-check with expected type and try to verify it. - - auto CTP = expectedResultType ? CTP_ClosureResult : CTP_Unused; - auto *bodyExpr = typeCheckChildIndependently(CE->getSingleExpressionBody(), - expectedResultType, CTP, - TCCOptions(), nullptr, false); - - if (!bodyExpr) - return true; - - if (resultTypeProcessor(CS.getType(bodyExpr), expectedResultType)) - return true; - } - - // If the body of the closure looked ok, then look for a contextual type - // error. This is necessary because FailureDiagnosis::diagnoseExprFailure - // doesn't do this for closures. - if (contextualType) { - auto fnType = contextualType->getAs(); - if (!fnType || fnType->isEqual(CS.getType(CE))) - return false; - - auto contextualResultType = fnType->getResult(); - // If the result type was unknown, it doesn't really make - // sense to diagnose from expected to unknown here. - if (isInvalidClosureResultType(contextualResultType)) - return false; - - // If the closure had an explicitly written return type incompatible with - // the contextual type, diagnose that. - if (CE->hasExplicitResultType() && - CE->getExplicitResultTypeLoc().getTypeRepr()) { - auto explicitResultTy = CE->getExplicitResultTypeLoc().getType(); - if (fnType && !explicitResultTy->isEqual(contextualResultType)) { - auto repr = CE->getExplicitResultTypeLoc().getTypeRepr(); - diagnose(repr->getStartLoc(), diag::incorrect_explicit_closure_result, - explicitResultTy, fnType->getResult()) - .fixItReplace(repr->getSourceRange(),fnType->getResult().getString()); - return true; - } - } - } - - // Otherwise, we can't produce a specific diagnostic. - return false; -} - -// Ported version of TypeChecker::checkObjCKeyPathExpr which works -// with new Smart KeyPath feature. -static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE, - Type rootType) { - auto &TC = CS.TC; - - // The constraint system may have been unable to resolve the actual root - // type. The generic interface type of the root produces better - // diagnostics in this case. - if (rootType->hasUnresolvedType() && !KPE->isObjC() && KPE->getRootType()) { - if (auto ident = dyn_cast(KPE->getRootType())) { - if (auto decl = ident->getBoundDecl()) { - if (auto metaType = decl->getInterfaceType()->castTo()) { - rootType = metaType->getInstanceType(); - } - } - } - } - - // The key path string we're forming. - SmallString<32> keyPathScratch; - llvm::raw_svector_ostream keyPathOS(keyPathScratch); - - // Captures the state of semantic resolution. - enum State { - Beginning, - ResolvingType, - ResolvingProperty, - ResolvingArray, - ResolvingSet, - ResolvingDictionary, - } state = Beginning; - - /// Determine whether we are currently resolving a property. - auto isResolvingProperty = [&] { - switch (state) { - case Beginning: - case ResolvingType: - return false; - - case ResolvingProperty: - case ResolvingArray: - case ResolvingSet: - case ResolvingDictionary: - return true; - } - - llvm_unreachable("Unhandled State in switch."); - }; - - // The type of AnyObject, which is used whenever we don't have - // sufficient type information. - Type anyObjectType = TC.Context.getAnyObjectType(); - - // Local function to update the state after we've resolved a - // component. - Type currentType = rootType; - auto updateState = [&](bool isProperty, Type newType) { - // Strip off optionals. - newType = newType->lookThroughAllOptionalTypes(); - - // If updating to a type, just set the new type; there's nothing - // more to do. - if (!isProperty) { - assert(state == Beginning || state == ResolvingType); - state = ResolvingType; - currentType = newType; - return; - } - - // We're updating to a property. Determine whether we're looking - // into a bridged Swift collection of some sort. - if (auto boundGeneric = newType->getAs()) { - auto nominal = boundGeneric->getDecl(); - - // Array - if (nominal == TC.Context.getArrayDecl()) { - // Further lookups into the element type. - state = ResolvingArray; - currentType = boundGeneric->getGenericArgs()[0]; - return; - } - - // Set - if (nominal == TC.Context.getSetDecl()) { - // Further lookups into the element type. - state = ResolvingSet; - currentType = boundGeneric->getGenericArgs()[0]; - return; - } - - // Dictionary - if (nominal == TC.Context.getDictionaryDecl()) { - // Key paths look into the keys of a dictionary; further - // lookups into the value type. - state = ResolvingDictionary; - currentType = boundGeneric->getGenericArgs()[1]; - return; - } - } - - // Determine whether we're looking into a Foundation collection. - if (auto classDecl = newType->getClassOrBoundGenericClass()) { - if (classDecl->isObjC() && classDecl->hasClangNode()) { - SmallString<32> scratch; - StringRef objcClassName = classDecl->getObjCRuntimeName(scratch); - - // NSArray - if (objcClassName == "NSArray") { - // The element type is unknown, so use AnyObject. - state = ResolvingArray; - currentType = anyObjectType; - return; - } - - // NSSet - if (objcClassName == "NSSet") { - // The element type is unknown, so use AnyObject. - state = ResolvingSet; - currentType = anyObjectType; - return; - } - - // NSDictionary - if (objcClassName == "NSDictionary") { - // Key paths look into the keys of a dictionary; there's no - // type to help us here. - state = ResolvingDictionary; - currentType = anyObjectType; - return; - } - } - } - - // It's just a property. - state = ResolvingProperty; - currentType = newType; - }; - - // Local function to perform name lookup for the current index. - auto performLookup = [&](DeclBaseName componentName, SourceLoc componentNameLoc, - Type &lookupType) -> LookupResult { - assert(currentType && "Non-beginning state must have a type"); - if (!currentType->mayHaveMembers()) - return LookupResult(); - - // Determine the type in which the lookup should occur. If we have - // a bridged value type, this will be the Objective-C class to - // which it is bridged. - if (auto bridgedClass = TC.Context.getBridgedToObjC(CS.DC, currentType)) - lookupType = bridgedClass; - else - lookupType = currentType; - - // Look for a member with the given name within this type. - return TC.lookupMember(CS.DC, lookupType, componentName); - }; - - // Local function to print a component to the string. - bool needDot = false; - auto printComponent = [&](DeclBaseName component) { - if (needDot) - keyPathOS << "."; - else - needDot = true; - - keyPathOS << component; - }; - - bool isInvalid = false; - SmallVector resolvedComponents; - - for (auto &component : KPE->getComponents()) { - auto componentNameLoc = component.getLoc(); - DeclBaseName componentName; - - switch (auto kind = component.getKind()) { - case KeyPathExpr::Component::Kind::UnresolvedProperty: { - auto componentFullName = component.getUnresolvedDeclName(); - componentName = componentFullName.getBaseIdentifier(); - break; - } - - case KeyPathExpr::Component::Kind::UnresolvedSubscript: - componentName = DeclBaseName::createSubscript(); - break; - - case KeyPathExpr::Component::Kind::Invalid: - case KeyPathExpr::Component::Kind::Identity: - case KeyPathExpr::Component::Kind::OptionalChain: - case KeyPathExpr::Component::Kind::OptionalForce: - // FIXME: Diagnose optional chaining and forcing properly. - return false; - - case KeyPathExpr::Component::Kind::OptionalWrap: - case KeyPathExpr::Component::Kind::Property: - case KeyPathExpr::Component::Kind::Subscript: - case KeyPathExpr::Component::Kind::TupleElement: - llvm_unreachable("already resolved!"); - } - - // If we are resolving into a dictionary, any component is - // well-formed because the keys are unknown dynamically. - if (state == ResolvingDictionary) { - // Just print the component unchanged; there's no checking we - // can do here. - printComponent(componentName); - - // From here, we're resolving a property. Use the current type. - updateState(/*isProperty=*/true, currentType); - - continue; - } - - // Look for this component. - Type lookupType; - LookupResult lookup = - performLookup(componentName, componentNameLoc, lookupType); - - // If we didn't find anything, try to apply typo-correction. - bool resultsAreFromTypoCorrection = false; - if (!lookup) { - TypoCorrectionResults corrections(TC, componentName, - DeclNameLoc(componentNameLoc)); - - TC.performTypoCorrection(CS.DC, DeclRefKind::Ordinary, lookupType, - (lookupType ? defaultMemberTypeLookupOptions - : defaultUnqualifiedLookupOptions), - corrections); - - if (currentType) { - TC.diagnose(componentNameLoc, diag::could_not_find_type_member, - currentType, componentName); - } else { - TC.diagnose(componentNameLoc, diag::use_unresolved_identifier, - componentName, false); - } - - // Note all the correction candidates. - corrections.noteAllCandidates(); - corrections.addAllCandidatesToLookup(lookup); - - isInvalid = true; - if (!lookup) - break; - - // Remember that these are from typo correction. - resultsAreFromTypoCorrection = true; - } - - // If we have more than one result, filter out unavailable or - // obviously unusable candidates. - if (lookup.size() > 1) { - lookup.filter([&](LookupResultEntry result, bool isOuter) -> bool { - // Drop unavailable candidates. - if (result.getValueDecl()->getAttrs().isUnavailable(TC.Context)) - return false; - - // Drop non-property, non-type candidates. - if (!isa(result.getValueDecl()) && - !isa(result.getValueDecl()) && - !isa(result.getValueDecl())) - return false; - - return true; - }); - } - - // If all results were unavailable, fail. - if (!lookup) - break; - - // If we *still* have more than one result, fail. - if (lookup.size() > 1) { - // Don't diagnose ambiguities if the results are from typo correction. - if (resultsAreFromTypoCorrection) - break; - - if (lookupType) - TC.diagnose(componentNameLoc, diag::ambiguous_member_overload_set, - componentName); - else - TC.diagnose(componentNameLoc, diag::ambiguous_decl_ref, componentName); - - for (auto result : lookup) { - TC.diagnose(result.getValueDecl(), diag::decl_declared_here, - result.getValueDecl()->getFullName()); - } - isInvalid = true; - break; - } - - auto found = lookup.front().getValueDecl(); - - // Handle property references. - if (auto var = dyn_cast(found)) { - // Resolve this component to the variable we found. - auto varRef = ConcreteDeclRef(var); - auto resolved = - KeyPathExpr::Component::forProperty(varRef, Type(), componentNameLoc); - resolvedComponents.push_back(resolved); - updateState(/*isProperty=*/true, var->getInterfaceType()); - - continue; - } - - // Handle type references. - if (auto type = dyn_cast(found)) { - // We cannot refer to a type via a property. - if (isResolvingProperty()) { - TC.diagnose(componentNameLoc, diag::expr_keypath_type_of_property, - componentName, currentType); - isInvalid = true; - break; - } - - // We cannot refer to a generic type. - if (type->getDeclaredInterfaceType()->hasTypeParameter()) { - TC.diagnose(componentNameLoc, diag::expr_keypath_generic_type, - componentName); - isInvalid = true; - break; - } - - Type newType; - if (lookupType && !lookupType->isAnyObject()) { - newType = lookupType->getTypeOfMember(CS.DC->getParentModule(), type, - type->getDeclaredInterfaceType()); - } else { - newType = type->getDeclaredInterfaceType(); - } - if (!newType) { - isInvalid = true; - break; - } - - updateState(/*isProperty=*/false, newType); - continue; - } - - continue; - } - - return isInvalid; -} - -bool FailureDiagnosis::visitKeyPathExpr(KeyPathExpr *KPE) { - auto contextualType = CS.getContextualType(); - - auto components = KPE->getComponents(); - assert(!components.empty() && "smart key path components cannot be empty."); - - auto &firstComponent = components.front(); - using ComponentKind = KeyPathExpr::Component::Kind; - - ClassDecl *klass; - Type parentType, rootType, valueType; - switch (firstComponent.getKind()) { - case ComponentKind::UnresolvedProperty: - case ComponentKind::UnresolvedSubscript: { - // If there is no contextual type we can't really do anything, - // as in case of unresolved member expression, which relies on - // contextual information. - if (!contextualType) - return false; - - if (auto *BGT = contextualType->getAs()) { - auto genericArgs = BGT->getGenericArgs(); - klass = BGT->getDecl(); - parentType = BGT->getParent(); - - // Smart Key Path can either have 1 argument - root type or - // two arguments - root and value type. - assert(genericArgs.size() == 1 || genericArgs.size() == 2); - - rootType = genericArgs.front(); - if (genericArgs.size() == 2) - valueType = genericArgs.back(); - } - break; - } - - default: - return false; - } - - // If there is no root type associated with expression we can't - // really diagnose anything here, it's most likely ambiguity. - if (!rootType) - return false; - - // If we know value type, it might be contextual mismatch between - // the actual type of the path vs. given by the caller. - if (valueType && !valueType->hasUnresolvedType()) { - struct KeyPathListener : public ExprTypeCheckListener { - ClassDecl *Decl; - Type ParentType; - Type RootType; - - KeyPathListener(ClassDecl *decl, Type parent, Type root) - : Decl(decl), ParentType(parent), RootType(root) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - auto *locator = cs.getConstraintLocator(expr); - auto valueType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape); - - auto keyPathType = - BoundGenericClassType::get(Decl, ParentType, {RootType, valueType}); - - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - keyPathType, locator, /*isFavored*/ true); - return false; - } - }; - - Expr *expr = KPE; - KeyPathListener listener(klass, parentType, rootType); - ConcreteDeclRef concreteDecl; - - auto derivedType = getTypeOfExpressionWithoutApplying( - expr, CS.DC, concreteDecl, FreeTypeVariableBinding::Disallow, - &listener); - - if (derivedType) { - if (auto *BGT = derivedType->getAs()) { - auto derivedValueType = BGT->getGenericArgs().back(); - if (!CS.TC.isConvertibleTo(valueType, derivedValueType, CS.DC)) { - diagnose(KPE->getLoc(), - diag::expr_smart_keypath_value_covert_to_contextual_type, - derivedValueType, valueType); - return true; - } - } - } - } - - // Looks like this is not a problem with contextual value type, let's see - // if there is something wrong with the path itself, maybe one of the - // components is incorrectly typed or doesn't exist... - return diagnoseKeyPathComponents(CS, KPE, rootType); -} - -bool FailureDiagnosis::visitArrayExpr(ArrayExpr *E) { - // If we had a contextual type, then it either conforms to - // ExpressibleByArrayLiteral or it is an invalid contextual type. - auto contextualType = CS.getContextualType(); - if (!contextualType) { - return false; - } - - // If our contextual type is an optional, look through them, because we're - // surely initializing whatever is inside. - contextualType = contextualType->lookThroughAllOptionalTypes(); - - // Validate that the contextual type conforms to ExpressibleByArrayLiteral and - // figure out what the contextual element type is in place. - auto ALC = CS.TC.getProtocol(E->getLoc(), - KnownProtocolKind::ExpressibleByArrayLiteral); - if (!ALC) - return visitExpr(E); - - // Check to see if the contextual type conforms. - if (auto Conformance - = TypeChecker::conformsToProtocol(contextualType, ALC, CS.DC, - ConformanceCheckFlags::InExpression)) { - Type contextualElementType = - Conformance->getTypeWitnessByName( - contextualType, CS.getASTContext().Id_ArrayLiteralElement) - ->getDesugaredType(); - - // Type check each of the subexpressions in place, passing down the contextual - // type information if we have it. - for (auto elt : E->getElements()) { - if (typeCheckChildIndependently(elt, contextualElementType, - CTP_ArrayElement) == nullptr) { - return true; - } - } - - return false; - } - - ContextualFailure failure(expr, CS, CS.getType(E), contextualType, - CS.getConstraintLocator(E)); - if (failure.diagnoseConversionToDictionary()) - return true; - - // If that didn't turn up an issue, then we don't know what to do. - // TODO: When a contextual type is missing, we could try to diagnose cases - // where the element types mismatch... but theoretically they should type - // unify to Any, so that could never happen? - return false; -} - -bool FailureDiagnosis::visitDictionaryExpr(DictionaryExpr *E) { - Type contextualKeyType, contextualValueType; - auto keyTypePurpose = CTP_Unused, valueTypePurpose = CTP_Unused; - - // If we had a contextual type, then it either conforms to - // ExpressibleByDictionaryLiteral or it is an invalid contextual type. - if (auto contextualType = CS.getContextualType()) { - // If our contextual type is an optional, look through them, because we're - // surely initializing whatever is inside. - contextualType = contextualType->lookThroughAllOptionalTypes(); - - auto DLC = CS.TC.getProtocol( - E->getLoc(), KnownProtocolKind::ExpressibleByDictionaryLiteral); - if (!DLC) return visitExpr(E); - - // Validate the contextual type conforms to ExpressibleByDictionaryLiteral - // and figure out what the contextual Key/Value types are in place. - auto Conformance = TypeChecker::conformsToProtocol( - contextualType, DLC, CS.DC, ConformanceCheckFlags::InExpression); - if (!Conformance) { - diagnose(E->getStartLoc(), diag::type_is_not_dictionary, contextualType) - .highlight(E->getSourceRange()); - return true; - } - - contextualKeyType = - Conformance->getTypeWitnessByName( - contextualType, CS.getASTContext().Id_Key) - ->getDesugaredType(); - - contextualValueType = - Conformance->getTypeWitnessByName( - contextualType, CS.getASTContext().Id_Value) - ->getDesugaredType(); - - assert(contextualKeyType && contextualValueType && - "Could not find Key/Value DictionaryLiteral associated types from" - " contextual type conformance"); - - keyTypePurpose = CTP_DictionaryKey; - valueTypePurpose = CTP_DictionaryValue; - } - - // Type check each of the subexpressions in place, passing down the contextual - // type information if we have it. - for (auto elt : E->getElements()) { - auto TE = dyn_cast(elt); - if (!TE || TE->getNumElements() != 2) continue; - - if (!typeCheckChildIndependently(TE->getElement(0), - contextualKeyType, keyTypePurpose)) - return true; - if (!typeCheckChildIndependently(TE->getElement(1), - contextualValueType, valueTypePurpose)) - return true; - } - - // If that didn't turn up an issue, then we don't know what to do. - // TODO: When a contextual type is missing, we could try to diagnose cases - // where the element types mismatch. There is no Any equivalent since they - // keys need to be hashable. - return false; -} - -/// When an object literal fails to typecheck because its protocol's -/// corresponding default type has not been set in the global namespace (e.g. -/// _ColorLiteralType), suggest that the user import the appropriate module for -/// the target. -bool FailureDiagnosis::visitObjectLiteralExpr(ObjectLiteralExpr *E) { - auto &TC = CS.getTypeChecker(); - - // Type check the argument first. - auto protocol = TC.getLiteralProtocol(E); - if (!protocol) - return false; - DeclName constrName = TC.getObjectLiteralConstructorName(E); - assert(constrName); - auto *constr = dyn_cast_or_null( - protocol->getSingleRequirement(constrName)); - if (!constr) - return false; - auto paramType = TC.getObjectLiteralParameterType(E, constr); - if (!typeCheckChildIndependently( - E->getArg(), paramType, CTP_CallArgument)) - return true; - - // Conditions for showing this diagnostic: - // * The object literal protocol's default type is unimplemented - if (TC.getDefaultType(protocol, CS.DC)) - return false; - // * The object literal has no contextual type - if (CS.getContextualType()) - return false; - - // Figure out what import to suggest. - auto &Ctx = CS.getASTContext(); - const auto &target = Ctx.LangOpts.Target; - StringRef importModule; - StringRef importDefaultTypeName; - if (protocol == Ctx.getProtocol(KnownProtocolKind::ExpressibleByColorLiteral)) { - if (target.isMacOSX()) { - importModule = "AppKit"; - importDefaultTypeName = "NSColor"; - } else if (target.isiOS() || target.isTvOS()) { - importModule = "UIKit"; - importDefaultTypeName = "UIColor"; - } - } else if (protocol == Ctx.getProtocol( - KnownProtocolKind::ExpressibleByImageLiteral)) { - if (target.isMacOSX()) { - importModule = "AppKit"; - importDefaultTypeName = "NSImage"; - } else if (target.isiOS() || target.isTvOS()) { - importModule = "UIKit"; - importDefaultTypeName = "UIImage"; - } - } else if (protocol == Ctx.getProtocol( - KnownProtocolKind::ExpressibleByFileReferenceLiteral)) { - importModule = "Foundation"; - importDefaultTypeName = "URL"; - } - - // Emit the diagnostic. - const auto plainName = E->getLiteralKindPlainName(); - TC.diagnose(E->getLoc(), diag::object_literal_default_type_missing, - plainName); - if (!importModule.empty()) { - TC.diagnose(E->getLoc(), diag::object_literal_resolve_import, - importModule, importDefaultTypeName, plainName); - } - return true; -} - -bool FailureDiagnosis::visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { - // If we have no contextual type, there is no way to resolve this. Just - // diagnose this as an ambiguity. - if (!CS.getContextualType()) - return false; - - // OTOH, if we do have a contextual type, we can provide a more specific - // error. Dig out the UnresolvedValueMember constraint for this expr node. - Constraint *memberConstraint = nullptr; - auto checkConstraint = [&](Constraint *C) { - if (C->getKind() == ConstraintKind::UnresolvedValueMember && - simplifyLocatorToAnchor(C->getLocator()) == E) - memberConstraint = C; - }; - - if (CS.failedConstraint) - checkConstraint(CS.failedConstraint); - for (auto &C : CS.getConstraints()) { - if (memberConstraint) break; - checkConstraint(&C); - } - - // If we can't find the member constraint in question, then we failed. - if (!memberConstraint) - return false; - - std::function)> callback = [&]( - ArrayRef candidates) { - bool hasTrailingClosure = callArgHasTrailingClosure(E->getArgument()); - - // Dump all of our viable candidates into a CalleeCandidateInfo & sort it - // out. - CalleeCandidateInfo candidateInfo(Type(), candidates, hasTrailingClosure, - CS); - - // Filter the candidate list based on the argument we may or may not have. - candidateInfo.filterContextualMemberList(E->getArgument()); - - // If we have multiple candidates, then we have an ambiguity. - if (candidateInfo.size() != 1) { - SourceRange argRange; - if (auto arg = E->getArgument()) - argRange = arg->getSourceRange(); - diagnose(E->getNameLoc(), diag::ambiguous_member_overload_set, - E->getName()) - .highlight(argRange); - candidateInfo.suggestPotentialOverloads(E->getNameLoc().getBaseNameLoc()); - return true; - } - - auto *argExpr = E->getArgument(); - auto candidateArgTy = candidateInfo[0].getArgumentType(CS.getASTContext()); - - // Depending on how we matched, produce tailored diagnostics. - switch (candidateInfo.closeness) { - case CC_SelfMismatch: // Self argument mismatches. - llvm_unreachable("These aren't produced by filterContextualMemberList"); - return false; - - case CC_NonLValueInOut: // First argument is inout but no lvalue present. - case CC_OneArgumentMismatch: // All arguments except one match. - case CC_OneArgumentNearMismatch: - case CC_OneGenericArgumentMismatch: - case CC_OneGenericArgumentNearMismatch: - case CC_GenericNonsubstitutableMismatch: - case CC_ArgumentNearMismatch: // Argument list mismatch. - case CC_ArgumentMismatch: // Argument list mismatch. - // Candidate filtering can produce these now, but they can't - // be properly diagnosed here at the moment. - return false; - - case CC_ExactMatch: { // This is a perfect match for the arguments. - - // If we have an exact match, then we must have an argument list, check - // it. - if (candidateArgTy) { - assert(argExpr && "Exact match without argument?"); - if (!typeCheckArgumentChildIndependently(argExpr, candidateArgTy, - candidateInfo)) - return true; - } - - // If the argument is a match, then check the result type. We might have - // looked up a contextual member whose result type disagrees with the - // expected result type. - auto resultTy = candidateInfo[0].getResultType(); - if (!resultTy) - resultTy = candidateInfo[0].getType(); - - if (resultTy && !CS.getContextualType()->is() && - !CS.TC.isConvertibleTo(resultTy, CS.getContextualType(), CS.DC)) { - diagnose(E->getNameLoc(), diag::expected_result_in_contextual_member, - E->getName(), resultTy, CS.getContextualType()); - return true; - } - - // Otherwise, this is an exact match, return false to diagnose this as an - // ambiguity. It must be some other problem, such as failing to infer a - // generic argument on the enum type. - return false; - } - - case CC_Unavailable: - case CC_Inaccessible: - // Diagnose some simple and common errors. - return candidateInfo.diagnoseSimpleErrors(E); - - case CC_ArgumentLabelMismatch: - case CC_ArgumentCountMismatch: { - // If we have no argument, the candidates must have expected one. - if (!argExpr) { - if (!candidateArgTy) - return false; // Candidate must be incorrect for some other reason. - - // Pick one of the arguments that are expected as an exemplar. - if (candidateArgTy->isVoid()) { - // If this member is () -> T, suggest adding parentheses. - diagnose(E->getNameLoc(), diag::expected_parens_in_contextual_member, - E->getName()) - .fixItInsertAfter(E->getEndLoc(), "()"); - } else { - diagnose(E->getNameLoc(), - diag::expected_argument_in_contextual_member, E->getName(), - candidateArgTy); - } - return true; - } - - assert(argExpr && candidateArgTy && "Exact match without an argument?"); - return diagnoseSingleCandidateFailures(candidateInfo, E, argExpr, - E->getArgumentLabels()); - } - - case CC_GeneralMismatch: { // Something else is wrong. - // If an argument value was specified, but this member expects no - // arguments, - // then we fail with a nice error message. - if (!candidateArgTy) { - auto kind = candidateInfo[0].getDecl()->getDescriptiveKind(); - bool isVoid = CS.getType(argExpr)->isVoid(); - auto argumentRange = E->getArgument()->getSourceRange(); - if (kind == DescriptiveDeclKind::EnumElement) { - if (isVoid) { - diagnose(E->getNameLoc(), diag::unexpected_arguments_in_enum_case, - E->getName()) - .fixItRemove(argumentRange); - } else { - diagnose(E->getNameLoc(), diag::unexpected_arguments_in_enum_case, - E->getName()) - .highlight(argumentRange); - } - } else { - if (isVoid) { - diagnose(E->getNameLoc(), - diag::unexpected_arguments_in_contextual_member, kind, - E->getName()) - .fixItRemove(argumentRange); - } else { - diagnose(E->getNameLoc(), - diag::unexpected_arguments_in_contextual_member, kind, - E->getName()) - .highlight(argumentRange); - } - } - return true; - } + CalleeCandidateInfo candidates(fnExpr, hasTrailingClosure, CS); - return false; - } - } + // Filter original list of choices based on the deduced type of + // argument expression after force re-check. + candidates.filterContextualMemberList(argExpr); - llvm_unreachable("all cases should be handled"); + // One of the candidates matches exactly, which means that + // this is a contextual type conversion failure, we can't diagnose here. + return candidates.closeness == CC_ExactMatch; }; - return diagnoseMemberFailures(E, nullptr, memberConstraint->getKind(), - memberConstraint->getMember(), - memberConstraint->getFunctionRefKind(), - memberConstraint->getLocator(), callback); -} - -bool FailureDiagnosis::diagnoseMemberFailures( - Expr *E, Expr *baseExpr, ConstraintKind lookupKind, DeclName memberName, - FunctionRefKind funcRefKind, ConstraintLocator *locator, - Optional)>> callback, - bool includeInaccessibleMembers) { - auto isInitializer = memberName.isSimpleName(DeclBaseName::createConstructor()); - - // Get the referenced base expression from the failed constraint, along with - // the SourceRange for the member ref. In "x.y", this returns the expr for x - // and the source range for y. - SourceRange memberRange; - SourceLoc BaseLoc; - DeclNameLoc NameLoc; - - Type baseTy, baseObjTy; - // UnresolvedMemberExpr doesn't have "base" expression, - // it's represented as ".foo", which means that we need - // to get base from the context. - if (auto *UME = dyn_cast(E)) { - memberRange = E->getSourceRange(); - BaseLoc = E->getLoc(); - NameLoc = UME->getNameLoc(); - baseTy = CS.getContextualType(); - if (!baseTy) - return false; - - // If we succeeded, get ready to do the member lookup. - baseObjTy = baseTy->getRValueType(); + // Otherwise, we have a generic failure. Diagnose it with a generic error + // message now. + if (isa(callExpr) && isa(argExpr)) { + auto argTuple = cast(argExpr); + auto lhsExpr = argTuple->getElement(0), rhsExpr = argTuple->getElement(1); + auto lhsType = CS.getType(lhsExpr)->getRValueType(); + auto rhsType = CS.getType(rhsExpr)->getRValueType(); - // If the base object is already a metatype type, then something weird is - // going on. For now, just generate a generic error. - if (baseObjTy->is()) + if (isContextualConversionFailure(argTuple)) return false; - baseTy = baseObjTy = MetatypeType::get(baseObjTy); - } else { - memberRange = baseExpr->getSourceRange(); - if (locator) - locator = simplifyLocator(CS, locator, memberRange); - - BaseLoc = baseExpr->getLoc(); - NameLoc = DeclNameLoc(memberRange.Start); - - // Retypecheck the anchor type, which is the base of the member expression. - baseExpr = typeCheckChildIndependently(baseExpr, TCC_AllowLValue); - if (!baseExpr) - return true; - - baseTy = CS.getType(baseExpr); - baseObjTy = baseTy->getWithoutSpecifierType(); - } - - // If the base type is an IUO, look through it. Odds are, the code is not - // trying to find a member of it. - // FIXME: We need to rework this with IUOs out of the type system. - // if (auto objTy = CS.lookThroughImplicitlyUnwrappedOptionalType(baseObjTy)) - // baseTy = baseObjTy = objTy; - - // If the base of this property access is a function that takes an empty - // argument list, then the most likely problem is that the user wanted to - // call the function, e.g. in "a.b.c" where they had to write "a.b().c". - // Produce a specific diagnostic + fixit for this situation. - if (auto baseFTy = baseObjTy->getAs()) { - if (baseExpr && baseFTy->getParams().empty()) { - auto failure = - MissingCallFailure(expr, CS, CS.getConstraintLocator(baseExpr)); - return failure.diagnoseAsError(); - } - } - - // If this is a tuple, then the index needs to be valid. - if (auto tuple = baseObjTy->getAs()) { - auto baseName = memberName.getBaseName(); - - if (!baseName.isSpecial()) { - StringRef nameStr = baseName.userFacingName(); - - int fieldIdx = -1; - // Resolve a number reference into the tuple type. - unsigned Value = 0; - if (!nameStr.getAsInteger(10, Value) && Value < tuple->getNumElements()) { - fieldIdx = Value; - } else { - fieldIdx = tuple->getNamedElementId(memberName.getBaseIdentifier()); - } - - if (fieldIdx != -1) - return false; // Lookup is valid. + if (!lhsType->isEqual(rhsType)) { + auto diag = diagnose(callExpr->getLoc(), diag::cannot_apply_binop_to_args, + overloadName, lhsType, rhsType); + diag.highlight(lhsExpr->getSourceRange()) + .highlight(rhsExpr->getSourceRange()); + } else { + diagnose(callExpr->getLoc(), diag::cannot_apply_binop_to_same_args, + overloadName, lhsType) + .highlight(lhsExpr->getSourceRange()) + .highlight(rhsExpr->getSourceRange()); } - diagnose(BaseLoc, diag::could_not_find_tuple_member, baseObjTy, memberName) - .highlight(memberRange); + calleeInfo.suggestPotentialOverloads(callExpr->getLoc()); return true; } - // If this is initializer/constructor lookup we are dealing this. - if (isInitializer) { - // Let's check what is the base type we are trying to look it up on - // because only MetatypeType is viable to find constructor on, as per - // rules in ConstraintSystem::performMemberLookup. - if (!baseTy->is()) { - baseTy = MetatypeType::get(baseTy, CS.getASTContext()); - } - } - - // If base type has unresolved generic parameters, such might mean - // that it's initializer with erroneous argument, otherwise this would - // be a simple ambiguous archetype case, neither can be diagnosed here. - if (baseTy->hasTypeParameter() && baseTy->hasUnresolvedType()) - return false; - - MemberLookupResult result = - CS.performMemberLookup(lookupKind, memberName, baseTy, funcRefKind, - locator, includeInaccessibleMembers); - - switch (result.OverallResult) { - case MemberLookupResult::Unsolved: - // If we couldn't resolve a specific type for the base expression, then we - // cannot produce a specific diagnostic. - return false; - - case MemberLookupResult::ErrorAlreadyDiagnosed: - // If an error was already emitted, then we're done, don't emit anything - // redundant. + // If we have a failure where closeness is an exact match, but there is + // still a failed argument, it is because one (or more) of the arguments + // types are unresolved. + if (calleeInfo.closeness == CC_ExactMatch && calleeInfo.failedArgument.isValid()) { + diagnoseAmbiguity(getFailedArgumentExpr(calleeInfo, argExpr)); return true; - - case MemberLookupResult::HasResults: - break; - } - - SmallVector viableCandidatesToReport; - for (auto candidate : result.ViableCandidates) - if (candidate.getKind() != OverloadChoiceKind::KeyPathApplication) - viableCandidatesToReport.push_back(candidate); - - // Since the lookup was allowing inaccessible members, let's check - // if it found anything of that sort, which is easy to diagnose. - bool allUnavailable = !CS.TC.getLangOpts().DisableAvailabilityChecking; - bool allInaccessible = true; - for (auto &member : viableCandidatesToReport) { - if (!member.isDecl()) { - // if there is no declaration, this choice is implicitly available. - allUnavailable = false; - continue; - } - - auto decl = member.getDecl(); - // Check availability of the found choice. - if (!decl->getAttrs().isUnavailable(CS.getASTContext())) - allUnavailable = false; - - if (decl->isAccessibleFrom(CS.DC)) - allInaccessible = false; - } - - // diagnoseSimpleErrors() should have diagnosed this scenario. - assert(!allInaccessible || viableCandidatesToReport.empty()); - - if (result.UnviableCandidates.empty() && isInitializer && - !baseObjTy->is()) { - if (auto ctorRef = dyn_cast(E)) { - // Diagnose 'super.init', which can only appear inside another - // initializer, specially. - if (isa(ctorRef->getBase())) { - diagnose(BaseLoc, diag::super_initializer_not_in_initializer); - return true; - } - - // Suggest inserting a call to 'type(of:)' to construct another object - // of the same dynamic type. - SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange(); - - // Surround the caller in `type(of:)`. - diagnose(BaseLoc, diag::init_not_instance_member) - .fixItInsert(fixItRng.Start, "type(of: ") - .fixItInsertAfter(fixItRng.End, ")"); - return true; - } } - if (viableCandidatesToReport.empty()) { - // If this was an optional type let's check if the base type - // has requested member, if so - generate nice error saying that - // optional was not unwrapped, otherwise say that type value has - // no such member. - if (auto *OT = dyn_cast(baseObjTy.getPointer())) { - auto optionalResult = CS.performMemberLookup( - lookupKind, memberName, OT->getBaseType(), funcRefKind, locator, - /*includeInaccessibleMembers*/ false); - - switch (optionalResult.OverallResult) { - case MemberLookupResult::ErrorAlreadyDiagnosed: - // If an error was already emitted, then we're done, don't emit anything - // redundant. - return true; - - case MemberLookupResult::Unsolved: - case MemberLookupResult::HasResults: - break; - } + if (isContextualConversionFailure(argExpr)) + return false; - if (!optionalResult.ViableCandidates.empty()) { - MemberAccessOnOptionalBaseFailure failure( - expr, CS, CS.getConstraintLocator(baseExpr), memberName, - /*resultOptional=*/false); - return failure.diagnoseAsError(); - } - } + // Generate specific error messages for unary operators. + if (isa(callExpr) || isa(callExpr)) { + assert(!overloadName.empty()); + diagnose(argExpr->getLoc(), diag::cannot_apply_unop_to_arg, overloadName, + CS.getType(argExpr)); - // FIXME: Dig out the property DeclNameLoc. - diagnoseUnviableLookupResults(result, E, baseObjTy, baseExpr, memberName, - NameLoc, BaseLoc); + calleeInfo.suggestPotentialOverloads(argExpr->getLoc()); return true; } - if (allUnavailable) { - auto firstDecl = viableCandidatesToReport[0].getDecl(); - // FIXME: We need the enclosing CallExpr to rewrite the argument labels. - if (diagnoseExplicitUnavailability(firstDecl, BaseLoc, CS.DC, - /*call*/ nullptr)) - return true; - } - - return callback.hasValue() ? (*callback)(viableCandidatesToReport) : false; -} - -bool FailureDiagnosis::visitUnresolvedDotExpr(UnresolvedDotExpr *UDE) { - auto *baseExpr = UDE->getBase(); - auto *locator = CS.getConstraintLocator(UDE, ConstraintLocator::Member); - if (!locator) + if (CS.getType(argExpr)->hasUnresolvedType()) return false; - return diagnoseMemberFailures(UDE, baseExpr, ConstraintKind::ValueMember, - UDE->getName(), UDE->getFunctionRefKind(), - locator); -} - -/// A TupleExpr propagate contextual type information down to its children and -/// can be erroneous when there is a label mismatch etc. -bool FailureDiagnosis::visitTupleExpr(TupleExpr *TE) { - // If we know the requested argType to use, use computeTupleShuffle to produce - // the shuffle of input arguments to destination values. It requires a - // TupleType to compute the mapping from argExpr. Conveniently, it doesn't - // care about the actual types though, so we can just use 'void' for them. - if (!CS.getContextualType() || !CS.getContextualType()->is()) - return visitExpr(TE); - - auto contextualTT = CS.getContextualType()->castTo(); - - SmallVector ArgElts; - auto voidTy = CS.getASTContext().TheEmptyTupleType; - - for (unsigned i = 0, e = TE->getNumElements(); i != e; ++i) - ArgElts.push_back({ voidTy, TE->getElementName(i) }); - auto TEType = TupleType::get(ArgElts, CS.getASTContext()); - - if (!TEType->is()) - return visitExpr(TE); - - SmallVector sources; - - // If the shuffle is invalid, then there is a type error. We could diagnose - // it specifically here, but the general logic does a fine job so we let it - // do it. - if (computeTupleShuffle(TEType->castTo()->getElements(), - contextualTT->getElements(), sources)) - return visitExpr(TE); - - // If we got a correct shuffle, we can perform the analysis of all of - // the input elements, with their expected types. - for (unsigned i = 0, e = sources.size(); i != e; ++i) { - // Otherwise, it must match the corresponding expected argument type. - unsigned inArgNo = sources[i]; - - TCCOptions options; - if (contextualTT->getElement(i).isInOut()) - options |= TCC_AllowLValue; + bool isInitializer = isa(fnExpr); + if (isa(argExpr) && + cast(argExpr)->getNumElements() == 0) { + // Emit diagnostics that say "no arguments". + diagnose(fnExpr->getLoc(), diag::cannot_call_with_no_params, + overloadName, isInitializer); + } else { + SmallVector params; + AnyFunctionType::decomposeInput(CS.getType(argExpr), params); + auto argString = AnyFunctionType::getParamListAsString(params); - auto actualType = contextualTT->getElementType(i); - auto exprResult = - typeCheckChildIndependently(TE->getElement(inArgNo), actualType, - CS.getContextualTypePurpose(), options); - // If there was an error type checking this argument, then we're done. - if (!exprResult) return true; + diagnose(fnExpr->getLoc(), diag::cannot_call_with_params, + overloadName, argString, isInitializer); } - - return false; -} -/// An IdentityExpr doesn't change its argument, but it *can* propagate its -/// contextual type information down. -bool FailureDiagnosis::visitIdentityExpr(IdentityExpr *E) { - auto contextualType = CS.getContextualType(); - - // If we have a paren expr and our contextual type is a ParenType, remove the - // paren expr sugar. - if (contextualType) - contextualType = contextualType->getWithoutParens(); - if (!typeCheckChildIndependently(E->getSubExpr(), contextualType, - CS.getContextualTypePurpose())) - return true; - return false; + // Did the user intend on invoking a different overload? + calleeInfo.suggestPotentialOverloads(fnExpr->getLoc()); + return true; } -/// A TryExpr doesn't change it's argument, nor does it change the contextual -/// type. -bool FailureDiagnosis::visitTryExpr(TryExpr *E) { - return visit(E->getSubExpr()); +bool FailureDiagnosis:: +visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { + // Don't walk the children for this node, it leads to multiple diagnostics + // because of how sema injects this node into the type checker. + return false; } bool FailureDiagnosis::visitExpr(Expr *E) { @@ -5018,38 +1240,38 @@ bool FailureDiagnosis::diagnoseExprFailure() { /// /// This is guaranteed to always emit an error message. /// -void ConstraintSystem::diagnoseFailureForExpr(Expr *expr) { - // Continue simplifying any active constraints left in the system. We can end - // up with them because the solver bails out as soon as it sees a Failure. We - // don't want to leave them around in the system because later diagnostics - // will assume they are unsolvable and may otherwise leave the system in an - // inconsistent state. - simplify(/*ContinueAfterFailures*/true); - - // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. - if (auto *RB = dyn_cast(expr)) - expr = RB->getSubExpr(); - - FailureDiagnosis diagnosis(expr, *this); - - // Now, attempt to diagnose the failure from the info we've collected. - if (diagnosis.diagnoseExprFailure()) - return; +void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { + setPhase(ConstraintSystemPhase::Diagnostics); - // If this is a contextual conversion problem, dig out some information. - if (diagnosis.diagnoseContextualConversionError(expr, getContextualType(), - getContextualTypePurpose())) - return; + SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); }; - // If we can diagnose a problem based on the constraints left laying around in - // the system, do so now. - if (diagnosis.diagnoseConstraintFailure()) - return; + if (auto expr = target.getAsExpr()) { + // Look through RebindSelfInConstructorExpr to avoid weird Sema issues. + if (auto *RB = dyn_cast(expr)) + expr = RB->getSubExpr(); - // If no one could find a problem with this expression or constraint system, - // then it must be well-formed... but is ambiguous. Handle this by diagnostic - // various cases that come up. - diagnosis.diagnoseAmbiguity(expr); + FailureDiagnosis diagnosis(expr, *this); + + // Now, attempt to diagnose the failure from the info we've collected. + if (diagnosis.diagnoseExprFailure()) + return; + + // If this is a contextual conversion problem, dig out some information. + if (diagnosis.diagnoseContextualConversionError( + expr, + getContextualType(expr), + getContextualTypePurpose(expr))) + return; + + // If no one could find a problem with this expression or constraint system, + // then it must be well-formed... but is ambiguous. Handle this by diagnostic + // various cases that come up. + diagnosis.diagnoseAmbiguity(expr); + } else { + // Emit a poor fallback message. + getASTContext().Diags.diagnose( + target.getAsFunction()->getLoc(), diag::failed_to_produce_diagnostic); + } } std::pair @@ -5120,319 +1342,35 @@ FailureDiagnosis::validateContextualType(Type contextualType, return {contextualType, CTP}; } -/// Check the specified closure to see if it is a multi-statement closure with -/// an uninferred type. If so, diagnose the problem with an error and return -/// true. -bool FailureDiagnosis:: -diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure) { - if (closure->hasSingleExpressionBody() || - closure->hasExplicitResultType()) - return false; - - auto closureType = CS.getType(closure)->getAs(); - if (!closureType || - !(closureType->getResult()->hasUnresolvedType() || - closureType->getResult()->hasTypeVariable())) - return false; - - // Okay, we have a multi-statement closure expr that has no inferred result, - // type, in the context of a larger expression. The user probably expected - // the compiler to infer the result type of the closure from the body of the - // closure, which Swift doesn't do for multi-statement closures. Try to be - // helpful by digging into the body of the closure, looking for a return - // statement, and inferring the result type from it. If we can figure that - // out, we can produce a fixit hint. - class ReturnStmtFinder : public ASTWalker { - SmallVectorImpl &returnStmts; - public: - ReturnStmtFinder(SmallVectorImpl &returnStmts) - : returnStmts(returnStmts) {} - - // Walk through statements, so we find returns hiding in if/else blocks etc. - std::pair walkToStmtPre(Stmt *S) override { - // Keep track of any return statements we find. - if (auto RS = dyn_cast(S)) - returnStmts.push_back(RS); - return { true, S }; - } - - // Don't walk into anything else, since they cannot contain statements - // that can return from the current closure. - std::pair walkToExprPre(Expr *E) override { - return { false, E }; - } - std::pair walkToPatternPre(Pattern *P) override { - return { false, P }; - } - bool walkToDeclPre(Decl *D) override { return false; } - bool walkToTypeLocPre(TypeLoc &TL) override { return false; } - bool walkToTypeReprPre(TypeRepr *T) override { return false; } - bool walkToParameterListPre(ParameterList *PL) override { return false; } - }; - - SmallVector Returns; - closure->getBody()->walk(ReturnStmtFinder(Returns)); - - // If we found a return statement inside of the closure expression, then go - // ahead and type check the body to see if we can determine a type. - for (auto RS : Returns) { - llvm::SaveAndRestore SavedDC(CS.DC, closure); - - // Otherwise, we're ok to type check the subexpr. - Type resultType; - if (RS->hasResult()) { - auto resultExpr = RS->getResult(); - ConcreteDeclRef decl = nullptr; - - // If return expression uses closure parameters, which have/are - // type variables, such means that we won't be able to - // type-check result correctly and, unfortunately, - // we are going to leak type variables from the parent - // constraint system through declaration types. - bool hasUnresolvedParams = false; - resultExpr->forEachChildExpr([&](Expr *childExpr) -> Expr *{ - if (auto DRE = dyn_cast(childExpr)) { - if (auto param = dyn_cast(DRE->getDecl())) { - auto paramType = - param->hasInterfaceType() ? param->getType() : Type(); - if (!paramType || paramType->hasTypeVariable()) { - hasUnresolvedParams = true; - return nullptr; - } - } - } - return childExpr; - }); - - if (hasUnresolvedParams) - continue; - - CS.TC.preCheckExpression(resultExpr, CS.DC); - - // Obtain type of the result expression without applying solutions, - // because otherwise this might result in leaking of type variables, - // since we are not resetting result statement and if expression is - // successfully type-checked its type cleanup is going to be disabled - // (we are allowing unresolved types), and as a side-effect it might - // also be transformed e.g. OverloadedDeclRefExpr -> DeclRefExpr. - auto type = getTypeOfExpressionWithoutApplying( - resultExpr, CS.DC, decl, FreeTypeVariableBinding::UnresolvedType); - if (type) - resultType = type; - } - - // If we found a type, presuppose it was the intended result and insert a - // fixit hint. - if (resultType && !isUnresolvedOrTypeVarType(resultType)) { - // If there is a location for an 'in' token, then the argument list was - // specified somehow but no return type was. Insert a "-> ReturnType " - // before the in token. - if (closure->getInLoc().isValid()) { - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsert(closure->getInLoc(), diag::insert_closure_return_type, - resultType, /*argListSpecified*/ false); - return true; - } - - // Otherwise, the closure must take zero arguments. We know this - // because the if one or more argument is specified, a multi-statement - // closure *must* name them, or explicitly ignore them with "_ in". - // - // As such, we insert " () -> ReturnType in " right after the '{' that - // starts the closure body. - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type) - .fixItInsertAfter(closure->getBody()->getLBraceLoc(), - diag::insert_closure_return_type, resultType, - /*argListSpecified*/ true); - return true; - } - } - - diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type); - return true; -} - -/// Check the associated constraint system to see if it has any archetypes -/// not properly resolved or missing. If so, diagnose the problem with -/// an error and return true. -bool FailureDiagnosis::diagnoseAmbiguousGenericParameters() { - using GenericParameter = std::tuple; - - llvm::SmallVector unboundParams; - // Check out all of the type variables lurking in the system. If any free - // type variables were created when opening generic parameters, diagnose - // that the generic parameter could not be inferred. - for (auto tv : CS.getTypeVariables()) { - auto &impl = tv->getImpl(); - - if (impl.hasRepresentativeOrFixed()) - continue; - - auto *paramTy = impl.getGenericParameter(); - if (!paramTy) - continue; - - // Number of constraints related to particular unbound parameter - // is significant indicator of the problem, because if there are - // no constraints associated with it, that means it can't ever be resolved, - // such helps to diagnose situations like: struct S { init(_ a: A) {}} - // because type B would have no constraints associated with it. - unsigned numConstraints = 0; - { - auto constraints = CS.getConstraintGraph().gatherConstraints( - tv, ConstraintGraph::GatheringKind::EquivalenceClass, - [&](Constraint *constraint) -> bool { - // We are not interested in ConformsTo constraints because - // we can't derive any concrete type information from them. - if (constraint->getKind() == ConstraintKind::ConformsTo) - return false; - - if (constraint->getKind() == ConstraintKind::Bind) { - if (auto locator = constraint->getLocator()) { - auto anchor = locator->getAnchor(); - if (anchor && isa(anchor)) - return false; - } - } - - return true; - }); - - numConstraints = constraints.size(); - } - - auto locator = impl.getLocator(); - unboundParams.emplace_back(paramTy, locator, numConstraints); - } - - // We've found unbound generic parameters, let's diagnose - // based on the number of constraints each one is related to. - if (!unboundParams.empty()) { - // Let's prioritize generic parameters that don't have any constraints - // associated. - std::stable_sort(unboundParams.begin(), unboundParams.end(), - [](GenericParameter a, GenericParameter b) { - return std::get<2>(a) < std::get<2>(b); - }); - - auto param = unboundParams.front(); - diagnoseAmbiguousGenericParameter(std::get<0>(param), - std::get<1>(param)->getAnchor()); - return true; - } - - return false; -} - -/// Emit an error message about an unbound generic parameter existing, and -/// emit notes referring to the target of a diagnostic, e.g., the function -/// or parameter being used. -void FailureDiagnosis:: -diagnoseAmbiguousGenericParameter(GenericTypeParamType *paramTy, - Expr *anchor) { - // A very common cause of this diagnostic is a situation where a closure expr - // has no inferred type, due to being a multiline closure. Check to see if - // this is the case and (if so), speculatively diagnose that as the problem. - bool didDiagnose = false; - expr->forEachChildExpr([&](Expr *subExpr) -> Expr*{ - auto closure = dyn_cast(subExpr); - if (!didDiagnose && closure) - didDiagnose = diagnoseAmbiguousMultiStatementClosure(closure); - - return subExpr; - }); - - if (didDiagnose) return; - - - // Otherwise, emit an error message on the expr we have, and emit a note - // about where the generic parameter came from. - if (!anchor) { - auto &tc = CS.getTypeChecker(); - tc.diagnose(expr->getLoc(), diag::unbound_generic_parameter, paramTy); - return; - } - - MissingGenericArgumentsFailure failure(expr, CS, {paramTy}, - CS.getConstraintLocator(anchor)); - failure.diagnoseAsError(); -} - - /// Emit an ambiguity diagnostic about the specified expression. void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { - // First, let's try to diagnose any problems related to ambiguous - // generic parameters present in the constraint system. - if (diagnoseAmbiguousGenericParameters()) - return; + if (auto *assignment = dyn_cast(E)) { + if (isa(assignment->getDest())) { + auto *srcExpr = assignment->getSrc(); + diagnoseAmbiguity(srcExpr); + return; + } + } // Unresolved/Anonymous ClosureExprs are common enough that we should give // them tailored diagnostics. if (auto CE = dyn_cast(E->getValueProvidingExpr())) { - // If this is a multi-statement closure with no explicit result type, emit - // a note to clue the developer in. - if (diagnoseAmbiguousMultiStatementClosure(CE)) - return; - diagnose(E->getLoc(), diag::cannot_infer_closure_type) .highlight(E->getSourceRange()); return; } - // A DiscardAssignmentExpr (spelled "_") needs contextual type information to - // infer its type. If we see one at top level, diagnose that it must be part - // of an assignment so we don't get a generic "expression is ambiguous" error. - if (isa(E)) { - diagnose(E->getLoc(), diag::discard_expr_outside_of_assignment) - .highlight(E->getSourceRange()); - return; - } - // Diagnose ".foo" expressions that lack context specifically. if (auto UME = dyn_cast(E->getSemanticsProvidingExpr())) { - if (!CS.getContextualType()) { + if (!CS.getContextualType(E)) { diagnose(E->getLoc(), diag::unresolved_member_no_inference,UME->getName()) .highlight(SourceRange(UME->getDotLoc(), UME->getNameLoc().getSourceRange().End)); return; } } - - // Diagnose empty collection literals that lack context specifically. - if (auto CE = dyn_cast(E->getSemanticsProvidingExpr())) { - if (CE->getNumElements() == 0) { - diagnose(E->getLoc(), diag::unresolved_collection_literal) - .highlight(E->getSourceRange()); - return; - } - } - - // Diagnose 'nil' without a contextual type. - if (isa(E->getSemanticsProvidingExpr())) { - diagnose(E->getLoc(), diag::unresolved_nil_literal) - .highlight(E->getSourceRange()); - return; - } - - // A very common cause of this diagnostic is a situation where a closure expr - // has no inferred type, due to being a multiline closure. Check to see if - // this is the case and (if so), speculatively diagnose that as the problem. - bool didDiagnose = false; - E->forEachChildExpr([&](Expr *subExpr) -> Expr*{ - auto closure = dyn_cast(subExpr); - if (!didDiagnose && closure) - didDiagnose = diagnoseAmbiguousMultiStatementClosure(closure); - - return subExpr; - }); - - if (didDiagnose) return; - - // Attempt to re-type-check the entire expression, allowing ambiguity, but // ignoring a contextual type. if (expr == E) { @@ -5459,7 +1397,7 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { { bool diagnosed = false; for (auto *fix : CS.getFixes()) - diagnosed |= fix->diagnose(expr); + diagnosed |= fix->diagnose(); if (diagnosed) return; @@ -5471,21 +1409,3 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) { diagnose(E->getLoc(), diag::type_of_expression_is_ambiguous) .highlight(E->getSourceRange()); } - -/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the -/// constraint system, return the decl that it references. -ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) { - // Search through the resolvedOverloadSets to see if we have a resolution for - // this member. This is an O(n) search, but only happens when producing an - // error diagnostic. - auto *overload = findSelectedOverloadFor(locator); - if (!overload) - return nullptr; - - // We only want to handle the simplest decl binding. - auto choice = overload->Choice; - if (choice.getKind() != OverloadChoiceKind::Decl) - return nullptr; - - return choice.getDecl(); -} diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 51c377b4e892e..0192c19079721 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -75,23 +75,25 @@ std::pair FailureDiagnostic::computeAnchor() const { return {anchor, !resolved->getPath().empty()}; } -Type FailureDiagnostic::getType(Expr *expr) const { - return resolveType(CS.getType(expr)); +Type FailureDiagnostic::getType(Expr *expr, bool wantRValue) const { + return resolveType(CS.getType(expr), /*reconstituteSugar=*/false, + wantRValue); } -Type FailureDiagnostic::getType(const TypeLoc &loc) const { - return resolveType(CS.getType(loc)); +Type FailureDiagnostic::getType(const TypeLoc &loc, bool wantRValue) const { + return resolveType(CS.getType(loc), /*reconstituteSugar=*/false, + wantRValue); } template InFlightDiagnostic FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const { auto &cs = getConstraintSystem(); - return cs.TC.diagnose(std::forward(Args)...); + return cs.getASTContext().Diags.diagnose(std::forward(Args)...); } Expr *FailureDiagnostic::findParentExpr(Expr *subExpr) const { - return E ? E->getParentMap()[subExpr] : nullptr; + return CS.getParentExpr(subExpr); } Expr * @@ -119,6 +121,12 @@ Expr *FailureDiagnostic::getBaseExprFor(Expr *anchor) const { return SE->getBase(); else if (auto *MRE = dyn_cast(anchor)) return MRE->getBase(); + else if (auto *call = dyn_cast(anchor)) { + auto fnType = getType(call->getFn()); + if (fnType->isCallableNominalType(getDC())) { + return call->getFn(); + } + } return nullptr; } @@ -129,166 +137,6 @@ FailureDiagnostic::getChoiceFor(ConstraintLocator *locator) const { return getOverloadChoiceIfAvailable(cs.getCalleeLocator(locator)); } -Type FailureDiagnostic::resolveInterfaceType(Type type, - bool reconstituteSugar) const { - auto &cs = getConstraintSystem(); - auto resolvedType = type.transform([&](Type type) -> Type { - if (auto *tvt = type->getAs()) { - // If this type variable is for a generic parameter, return that. - if (auto *gp = tvt->getImpl().getGenericParameter()) - return gp; - - // Otherwise resolve its fixed type, mapped out of context. - if (auto fixed = cs.getFixedType(tvt)) - return resolveInterfaceType(fixed->mapTypeOutOfContext()); - - return cs.getRepresentative(tvt); - } - if (auto *dmt = type->getAs()) { - // For a dependent member, first resolve the base. - auto newBase = resolveInterfaceType(dmt->getBase()); - - // Then reconstruct using its associated type. - assert(dmt->getAssocType()); - return DependentMemberType::get(newBase, dmt->getAssocType()); - } - return type; - }); - - assert(!resolvedType->hasArchetype()); - return reconstituteSugar ? resolvedType->reconstituteSugar(/*recursive*/ true) - : resolvedType; -} - -/// Given an apply expr, returns true if it is expected to have a direct callee -/// overload, resolvable using `getChoiceFor`. Otherwise, returns false. -static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) { - auto *fnExpr = callExpr->getDirectCallee(); - - // An apply of an apply/subscript doesn't have a direct callee. - if (isa(fnExpr) || isa(fnExpr)) - return false; - - // Applies of closures don't have callee overloads. - if (isa(fnExpr)) - return false; - - // No direct callee for a try!/try?. - if (isa(fnExpr) || isa(fnExpr)) - return false; - - // If we have an intermediate cast, there's no direct callee. - if (isa(fnExpr)) - return false; - - // No direct callee for an if expr. - if (isa(fnExpr)) - return false; - - // Assume that anything else would have a direct callee. - return true; -} - -Optional -FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const { - auto &cs = getConstraintSystem(); - auto *anchor = locator->getAnchor(); - auto path = locator->getPath(); - - // Look for the apply-arg-to-param element in the locator's path. We may - // have to look through other elements that are generated from an argument - // conversion such as GenericArgument for an optional-to-optional conversion, - // and OptionalPayload for a value-to-optional conversion. - auto iter = path.rbegin(); - auto applyArgElt = locator->findLast(iter); - if (!applyArgElt) - return None; - - auto nextIter = iter + 1; - assert(!locator->findLast(nextIter) && - "Multiple ApplyArgToParam components?"); - - // Form a new locator that ends at the apply-arg-to-param element, and - // simplify it to get the full argument expression. - auto argPath = path.drop_back(iter - path.rbegin()); - auto *argLocator = cs.getConstraintLocator( - anchor, argPath, ConstraintLocator::getSummaryFlagsForPath(argPath)); - - auto *argExpr = simplifyLocatorToAnchor(argLocator); - - // If we were unable to simplify down to the argument expression, we don't - // know what this is. - if (!argExpr) - return None; - - Optional choice; - Type rawFnType; - if (auto overload = getChoiceFor(argLocator)) { - // If we have resolved an overload for the callee, then use that to get the - // function type and callee. - choice = overload->choice; - rawFnType = overload->openedType; - } else { - // If we didn't resolve an overload for the callee, we should be dealing - // with a call of an arbitrary function expr. - if (auto *call = dyn_cast(anchor)) { - assert(!shouldHaveDirectCalleeOverload(call) && - "Should we have resolved a callee for this?"); - rawFnType = cs.getType(call->getFn()); - } else { - // FIXME: ArgumentMismatchFailure is currently used from CSDiag, meaning - // we can end up a BinaryExpr here with an unresolved callee. It should be - // possible to remove this once we've gotten rid of the old CSDiag logic - // and just assert that we have a CallExpr. - auto *apply = cast(anchor); - rawFnType = cs.getType(apply->getFn()); - } - } - - // Try to resolve the function type by loading lvalues and looking through - // optional types, which can occur for expressions like `fn?(5)`. - auto *fnType = resolveType(rawFnType) - ->getRValueType() - ->lookThroughAllOptionalTypes() - ->getAs(); - if (!fnType) - return None; - - // Resolve the interface type for the function. Note that this may not be a - // function type, for example it could be a generic parameter. - Type fnInterfaceType; - auto *callee = choice ? choice->getDeclOrNull() : nullptr; - if (callee && callee->hasInterfaceType()) { - // If we have a callee with an interface type, we can use it. This is - // preferable to resolveInterfaceType, as this will allow us to get a - // GenericFunctionType for generic decls. - // - // Note that it's possible to find a callee without an interface type. This - // can happen for example with closure parameters, where the interface type - // isn't set until the solution is applied. In that case, use - // resolveInterfaceType. - fnInterfaceType = callee->getInterfaceType(); - - // Strip off the curried self parameter if necessary. - if (hasAppliedSelf(cs, *choice)) - fnInterfaceType = fnInterfaceType->castTo()->getResult(); - - if (auto *fn = fnInterfaceType->getAs()) { - assert(fn->getNumParams() == fnType->getNumParams() && - "Parameter mismatch?"); - (void)fn; - } - } else { - fnInterfaceType = resolveInterfaceType(rawFnType); - } - - auto argIdx = applyArgElt->getArgIdx(); - auto paramIdx = applyArgElt->getParamIdx(); - - return FunctionArgApplyInfo(argExpr, argIdx, getType(argExpr), paramIdx, - fnInterfaceType, fnType, callee); -} - Type FailureDiagnostic::restoreGenericParameters( Type type, llvm::function_ref substitution) { @@ -384,7 +232,8 @@ ValueDecl *RequirementFailure::getDeclRef() const { }; if (isFromContextualType()) - return getAffectedDeclFromType(cs.getContextualType()); + return getAffectedDeclFromType( + cs.getContextualType(getLocator()->getAnchor())); if (auto overload = getChoiceFor(getLocator())) { // If there is a declaration associated with this @@ -478,7 +327,8 @@ bool RequirementFailure::diagnoseAsError() { auto *namingDecl = OTD->getNamingDecl(); emitDiagnostic( anchor->getLoc(), diag::type_does_not_conform_in_opaque_return, - namingDecl->getDescriptiveKind(), namingDecl->getFullName(), lhs, rhs); + namingDecl->getDescriptiveKind(), namingDecl->getFullName(), lhs, rhs, + rhs->isAnyObject()); if (auto *repr = namingDecl->getOpaqueResultTypeRepr()) { emitDiagnostic(repr->getLoc(), diag::opaque_return_type_declared_here) @@ -487,8 +337,9 @@ bool RequirementFailure::diagnoseAsError() { return true; } - if (genericCtx != reqDC && (genericCtx->isChildContextOf(reqDC) || - isStaticOrInstanceMember(AffectedDecl))) { + if (reqDC->isTypeContext() && genericCtx != reqDC && + (genericCtx->isChildContextOf(reqDC) || + isStaticOrInstanceMember(AffectedDecl))) { auto *NTD = reqDC->getSelfNominalTypeDecl(); emitDiagnostic(anchor->getLoc(), getDiagnosticInRereference(), AffectedDecl->getDescriptiveKind(), @@ -517,6 +368,18 @@ void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const { auto &req = getRequirement(); + if (req.getKind() != RequirementKind::SameType) { + if (auto wrappedType = lhs->getOptionalObjectType()) { + auto kind = (req.getKind() == RequirementKind::Superclass ? + ConstraintKind::Subtype : ConstraintKind::ConformsTo); + if (TypeChecker::typesSatisfyConstraint(wrappedType, rhs, + /*openArchetypes=*/false, + kind, getDC())) + emitDiagnostic(getAnchor()->getLoc(), + diag::wrapped_type_satisfies_requirement, wrappedType); + } + } + if (isConditional()) { emitDiagnostic(anchor, diag::requirement_implied_by_conditional_conformance, resolveType(Conformance->getType()), @@ -524,7 +387,8 @@ void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs, return; } - if (rhs->isEqual(req.getSecondType())) { + if (req.getKind() == RequirementKind::Layout || + rhs->isEqual(req.getSecondType())) { emitDiagnostic(anchor, diag::where_requirement_failure_one_subst, req.getFirstType(), lhs); return; @@ -542,26 +406,11 @@ void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs, bool MissingConformanceFailure::diagnoseAsError() { auto *anchor = getAnchor(); - auto ownerType = getOwnerType(); auto nonConformingType = getLHS(); auto protocolType = getRHS(); - auto getArgumentAt = [](const ApplyExpr *AE, unsigned index) -> Expr * { - assert(AE); - - auto *arg = AE->getArg(); - if (auto *TE = dyn_cast(arg)) - return TE->getElement(index); - - assert(index == 0); - if (auto *PE = dyn_cast(arg)) - return PE->getSubExpr(); - - return arg; - }; - // If this is a requirement of a pattern-matching operator, - // let's see whether argument is already has a fix associated + // let's see whether argument already has a fix associated // with it and if so skip conformance error, otherwise we'd // produce an unrelated ` doesn't conform to Equatable protocol` // diagnostic. @@ -588,43 +437,14 @@ bool MissingConformanceFailure::diagnoseAsError() { if (diagnoseAsAmbiguousOperatorRef()) return true; - Optional atParameterPos; - // Sometimes fix is recorded by type-checking sub-expression - // during normal diagnostics, in such case call expression - // is unavailable. - if (Apply) { - if (auto *fnType = ownerType->getAs()) { - auto parameters = fnType->getParams(); - for (auto index : indices(parameters)) { - if (parameters[index].getOldType()->isEqual(nonConformingType)) { - atParameterPos = index; - break; - } - } - } - } - if (nonConformingType->isObjCExistentialType()) { emitDiagnostic(anchor->getLoc(), diag::protocol_does_not_conform_static, nonConformingType, protocolType); return true; } - if (diagnoseTypeCannotConform((atParameterPos ? - getArgumentAt(Apply, *atParameterPos) : anchor), - nonConformingType, protocolType)) { - return true; - } - - if (atParameterPos) { - // Requirement comes from one of the parameter types, - // let's try to point diagnostic to the argument expression. - auto *argExpr = getArgumentAt(Apply, *atParameterPos); - emitDiagnostic(argExpr->getLoc(), - diag::cannot_convert_argument_value_protocol, - nonConformingType, protocolType); + if (diagnoseTypeCannotConform(anchor, nonConformingType, protocolType)) return true; - } // If none of the special cases could be diagnosed, // let's fallback to the most general diagnostic. @@ -633,7 +453,8 @@ bool MissingConformanceFailure::diagnoseAsError() { bool MissingConformanceFailure::diagnoseTypeCannotConform(Expr *anchor, Type nonConformingType, Type protocolType) const { - if (!(nonConformingType->is() || + if (getRequirement().getKind() == RequirementKind::Layout || + !(nonConformingType->is() || nonConformingType->is() || nonConformingType->isExistentialType() || nonConformingType->is())) { @@ -739,6 +560,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( case CTP_ReturnSingleExpr: return diag::cannot_convert_to_return_type; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value; case CTP_YieldByValue: return diag::cannot_convert_yield_value; @@ -762,6 +584,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( return diag::cannot_convert_condition_value; case CTP_ThrowStmt: + case CTP_ForEachStmt: case CTP_Unused: case CTP_CannotFail: case CTP_YieldByReference: @@ -801,16 +624,36 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { auto *anchor = getAnchor(); auto path = getLocator()->getPath(); + auto fromType = getFromType(); + auto toType = getToType(); + Optional> diagnostic; if (path.empty()) { - assert(isa(anchor)); - diagnostic = getDiagnosticFor(CTP_AssignSource); + if (isa(anchor)) { + diagnostic = getDiagnosticFor(CTP_AssignSource); + } else if (isa(anchor)) { + diagnostic = getDiagnosticFor(CTP_CoerceOperand); + } else { + return false; + } } else { const auto &last = path.back(); switch (last.getKind()) { case ConstraintLocator::ContextualType: { auto purpose = getContextualTypePurpose(); - assert(!(purpose == CTP_Unused && purpose == CTP_CannotFail)); + assert(!(purpose == CTP_Unused || purpose == CTP_CannotFail)); + + // If this is call to a closure e.g. `let _: A = { B() }()` + // let's point diagnostic to its result. + if (auto *call = dyn_cast(anchor)) { + auto *fnExpr = call->getFn(); + if (auto *closure = dyn_cast(fnExpr)) { + purpose = CTP_ClosureResult; + if (closure->hasSingleExpressionBody()) + anchor = closure->getSingleExpressionBody(); + } + } + diagnostic = getDiagnosticFor(purpose); break; } @@ -832,6 +675,29 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { break; } + case ConstraintLocator::GenericArgument: { + // In cases like `[[Int]]` vs. `[[String]]` + if (auto *assignExpr = dyn_cast(anchor)) { + diagnostic = getDiagnosticFor(CTP_AssignSource); + fromType = getType(assignExpr->getSrc()); + toType = getType(assignExpr->getDest()); + } + break; + } + + case ConstraintLocator::TupleElement: { + auto *anchor = getRawAnchor(); + + if (isa(anchor)) { + diagnostic = getDiagnosticFor(CTP_ArrayElement); + } else if (isa(anchor)) { + auto eltLoc = last.castTo(); + diagnostic = getDiagnosticFor( + eltLoc.getIndex() == 0 ? CTP_DictionaryKey : CTP_DictionaryValue); + } + break; + } + default: return false; } @@ -840,7 +706,7 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { if (!diagnostic) return false; - emitDiagnostic(anchor->getLoc(), *diagnostic, getFromType(), getToType()); + emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); emitNotesForMismatches(); return true; } @@ -900,27 +766,23 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() { if (diagnoseParameterUse()) return true; - if (ConvertTo) { - emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, - ConvertTo); - return true; + if (auto *typeVar = getRawFromType()->getAs()) { + if (auto *GP = typeVar->getImpl().getGenericParameter()) { + emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, GP); + return true; + } } - auto *loc = getLocator(); - if (auto gpElt = loc->getLastElementAs()) { - auto *paramTy = gpElt->getType(); - emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, - paramTy); - } else { - emitDiagnostic(anchor->getLoc(), diag::unknown_escaping_use_of_noescape); - } + emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, + getToType()); return true; } bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const { + auto convertTo = getToType(); // If the other side is not a function, we have common case diagnostics // which handle function-to-type conversion diagnostics. - if (!ConvertTo || !ConvertTo->is()) + if (!convertTo->is()) return false; auto *anchor = getAnchor(); @@ -938,7 +800,8 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const { // Let's check whether this is a function parameter passed // as an argument to another function which accepts @escaping // function at that position. - if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) { + auto &cs = getConstraintSystem(); + if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator())) { auto paramInterfaceTy = argApplyInfo->getParamInterfaceType(); if (paramInterfaceTy->isTypeParameter()) { auto diagnoseGenericParamFailure = [&](GenericTypeParamDecl *decl) { @@ -946,7 +809,9 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const { diag::converting_noespace_param_to_generic_type, PD->getName(), paramInterfaceTy); - emitDiagnostic(decl, diag::generic_parameters_always_escaping); + auto declLoc = decl->getLoc(); + if (declLoc.isValid()) + emitDiagnostic(decl, diag::generic_parameters_always_escaping); }; // If this is a situation when non-escaping parameter is passed @@ -998,45 +863,20 @@ bool MissingForcedDowncastFailure::diagnoseAsError() { if (hasComplexLocator()) return false; - auto &TC = getTypeChecker(); - auto *expr = getAnchor(); if (auto *assignExpr = dyn_cast(expr)) expr = assignExpr->getSrc(); - auto *coerceExpr = dyn_cast(expr); - if (!coerceExpr) - return false; - auto *subExpr = coerceExpr->getSubExpr(); - auto fromType = getType(subExpr)->getRValueType(); - auto toType = resolveType(coerceExpr->getCastTypeLoc().getType()); + auto *coerceExpr = cast(expr); - auto castKind = - TC.typeCheckCheckedCast(fromType, toType, CheckedCastContextKind::None, - getDC(), coerceExpr->getLoc(), subExpr, - coerceExpr->getCastTypeLoc().getSourceRange()); + auto fromType = getFromType(); + auto toType = getToType(); - switch (castKind) { - // Invalid cast. - case CheckedCastKind::Unresolved: - // Fix didn't work, let diagnoseFailureForExpr handle this. - return false; - case CheckedCastKind::Coercion: - case CheckedCastKind::BridgingCoercion: - llvm_unreachable("Coercions handled in other disjunction branch"); - - // Valid casts. - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - emitDiagnostic(coerceExpr->getLoc(), diag::missing_forced_downcast, - fromType, toType) - .highlight(coerceExpr->getSourceRange()) - .fixItReplace(coerceExpr->getLoc(), "as!"); - return true; - } - llvm_unreachable("unhandled cast kind"); + emitDiagnostic(coerceExpr->getLoc(), diag::missing_forced_downcast, fromType, + toType) + .highlight(coerceExpr->getSourceRange()) + .fixItReplace(coerceExpr->getLoc(), "as!"); + return true; } bool MissingAddressOfFailure::diagnoseAsError() { @@ -1063,8 +903,6 @@ bool MissingExplicitConversionFailure::diagnoseAsError() { return false; auto *DC = getDC(); - auto &TC = getTypeChecker(); - auto *anchor = getAnchor(); if (auto *assign = dyn_cast(anchor)) anchor = assign->getSrc(); @@ -1072,17 +910,19 @@ bool MissingExplicitConversionFailure::diagnoseAsError() { anchor = paren->getSubExpr(); auto fromType = getFromType(); - Type toType = getToType(); + auto toType = getToType(); if (!toType->hasTypeRepr()) return false; - bool useAs = TC.isExplicitlyConvertibleTo(fromType, toType, DC); - bool useAsBang = !useAs && TC.checkedCastMaySucceed(fromType, toType, DC); - if (!useAs && !useAsBang) + bool useAs = TypeChecker::isExplicitlyConvertibleTo(fromType, toType, DC); + if (!useAs && !TypeChecker::checkedCastMaySucceed(fromType, toType, DC)) return false; - auto *expr = getParentExpr(); + auto *expr = findParentExpr(getAnchor()); + if (!expr) + expr = getAnchor(); + // If we're performing pattern matching, // "as" means something completely different... if (auto binOpExpr = dyn_cast(expr)) { @@ -1126,15 +966,15 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() { return false; auto *anchor = getAnchor(); - auto baseType = getType(anchor)->getRValueType(); + auto baseType = getType(anchor); bool resultIsOptional = ResultTypeIsOptional; // If we've resolved the member overload to one that returns an optional // type, then the result of the expression is optional (and we want to offer // only a '?' fixit) even though the constraint system didn't need to add any // additional optionality. - auto overload = getResolvedOverload(getLocator()); - if (overload && overload->ImpliedType->getOptionalObjectType()) + auto overload = getOverloadChoiceIfAvailable(getLocator()); + if (overload && overload->openedType->getOptionalObjectType()) resultIsOptional = true; auto unwrappedBaseType = baseType->getOptionalObjectType(); @@ -1168,21 +1008,22 @@ void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt( // If anchor is n explicit address-of, or expression which produces // an l-value (e.g. first argument of `+=` operator), let's not // suggest default value here because that would produce r-value type. - if (isa(anchor)) + if (!anchor || isa(anchor)) return; - if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) + auto &cs = getConstraintSystem(); + if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator())) if (argApplyInfo->getParameterFlags().isInOut()) return; auto diag = emitDiagnostic(expr->getLoc(), diag::unwrap_with_default_value); - auto &TC = getTypeChecker(); // Figure out what we need to parenthesize. bool needsParensInside = - exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr); + exprNeedsParensBeforeAddingNilCoalescing(DC, expr); + auto parentExpr = findParentExpr(anchor); bool needsParensOutside = - exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, getParentExpr()); + exprNeedsParensAfterAddingNilCoalescing(DC, expr, parentExpr); llvm::SmallString<2> insertBefore; llvm::SmallString<32> insertAfter; @@ -1250,6 +1091,11 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { if (hasComplexLocator()) return false; + if (!getUnwrappedType()->isBool()) { + if (diagnoseConversionToBool()) + return true; + } + auto *anchor = getAnchor(); // If this is an unresolved member expr e.g. `.foo` its @@ -1278,7 +1124,7 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { } emitDiagnostic(tryExpr->getTryLoc(), diag::missing_unwrap_optional_try, - getType(anchor)->getRValueType()) + getType(anchor)) .fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()}, "try!"); return true; @@ -1364,10 +1210,15 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { // Assignment is not allowed inside of a condition, // so let's not diagnose immutability, because // most likely the problem is related to use of `=` itself. - if (cs.getContextualTypePurpose() == CTP_Condition) + if (cs.getContextualTypePurpose(diagExpr) == CTP_Condition) return false; if (auto assignExpr = dyn_cast(diagExpr)) { + // Let's check whether this is an attempt to assign + // variable or property to itself. + if (TypeChecker::diagnoseSelfAssignment(assignExpr)) + return true; + diagExpr = assignExpr->getDest(); } @@ -1434,21 +1285,21 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { emitDiagnostic(loc, diag::assignment_let_property_delegating_init, member->getName()); if (auto *ref = getResolvedMemberRef(member)) { - emitDiagnostic(ref, diag::decl_declared_here, member->getName()); + emitDiagnostic(ref, diag::decl_declared_here, ref->getFullName()); } return true; } } } - if (auto resolvedOverload = getResolvedOverload(getLocator())) { - if (resolvedOverload->Choice.getKind() == + if (auto resolvedOverload = getOverloadChoiceIfAvailable(getLocator())) { + if (resolvedOverload->choice.getKind() == OverloadChoiceKind::DynamicMemberLookup) subElementDiagID = diag::assignment_dynamic_property_has_immutable_base; - if (resolvedOverload->Choice.getKind() == + if (resolvedOverload->choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) { - if (!getType(member->getBase())->hasLValueType()) + if (!getType(member->getBase(), /*wantRValue=*/false)->hasLValueType()) subElementDiagID = diag::assignment_dynamic_property_has_immutable_base; } @@ -1464,9 +1315,73 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { return failure.diagnose(); } +bool RValueTreatedAsLValueFailure::diagnoseAsNote() { + auto overload = getChoiceFor(getLocator()); + if (!(overload && overload->choice.isDecl())) + return false; + + auto *decl = overload->choice.getDecl(); + emitDiagnostic(decl, diag::candidate_is_not_assignable, + decl->getDescriptiveKind(), decl->getFullName()); + return true; +} + +static Decl *findSimpleReferencedDecl(const Expr *E) { + if (auto *LE = dyn_cast(E)) + E = LE->getSubExpr(); + + if (auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + + return nullptr; +} + +static std::pair findReferencedDecl(const Expr *E) { + E = E->getValueProvidingExpr(); + + if (auto *LE = dyn_cast(E)) + return findReferencedDecl(LE->getSubExpr()); + + if (auto *AE = dyn_cast(E)) + return findReferencedDecl(AE->getDest()); + + if (auto *D = findSimpleReferencedDecl(E)) + return std::make_pair(nullptr, D); + + if (auto *MRE = dyn_cast(E)) { + if (auto *BaseDecl = findSimpleReferencedDecl(MRE->getBase())) + return std::make_pair(BaseDecl, MRE->getMember().getDecl()); + } + + return std::make_pair(nullptr, nullptr); +} + +bool TypeChecker::diagnoseSelfAssignment(const Expr *expr) { + auto *assignExpr = dyn_cast(expr); + if (!assignExpr) + return false; + + auto *dstExpr = assignExpr->getDest(); + auto *srcExpr = assignExpr->getSrc(); + + auto dstDecl = findReferencedDecl(dstExpr); + auto srcDecl = findReferencedDecl(srcExpr); + + if (dstDecl.second && dstDecl == srcDecl) { + auto &DE = dstDecl.second->getASTContext().Diags; + DE.diagnose(expr->getLoc(), dstDecl.first ? diag::self_assignment_prop + : diag::self_assignment_var) + .highlight(dstExpr->getSourceRange()) + .highlight(srcExpr->getSourceRange()); + return true; + } + + return false; +} + bool TrailingClosureAmbiguityFailure::diagnoseAsNote() { - const auto *expr = getParentExpr(); - auto *callExpr = dyn_cast(expr); + const auto *expr = findParentExpr(getAnchor()); + auto *callExpr = dyn_cast_or_null(expr); if (!callExpr) return false; if (!callExpr->hasTrailingClosure()) @@ -1510,7 +1425,7 @@ bool TrailingClosureAmbiguityFailure::diagnoseAsNote() { auto diag = emitDiagnostic( expr->getLoc(), diag::ambiguous_because_of_trailing_closure, choicePair.first.empty(), choicePair.second->getFullName()); - swift::fixItEncloseTrailingClosure(getTypeChecker(), diag, callExpr, + swift::fixItEncloseTrailingClosure(getASTContext(), diag, callExpr, choicePair.first); } @@ -1519,7 +1434,8 @@ bool TrailingClosureAmbiguityFailure::diagnoseAsNote() { AssignmentFailure::AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, SourceLoc diagnosticLoc) - : FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)), + : FailureDiagnostic(cs, cs.getConstraintLocator(destExpr)), + DestExpr(destExpr), Loc(diagnosticLoc), DeclDiagnostic(findDeclDiagonstic(cs.getASTContext(), destExpr)), TypeDiagnostic(diag::assignment_lhs_not_lvalue) {} @@ -1527,13 +1443,12 @@ AssignmentFailure::AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, bool AssignmentFailure::diagnoseAsError() { auto &cs = getConstraintSystem(); auto *DC = getDC(); - auto *destExpr = getParentExpr(); // Walk through the destination expression, resolving what the problem is. If // we find a node in the lvalue path that is problematic, this returns it. - auto immInfo = resolveImmutableBase(destExpr); - - Optional choice = immInfo.second; + Expr *immutableExpr; + Optional choice; + std::tie(immutableExpr, choice) = resolveImmutableBase(DestExpr); // Attempt diagnostics based on the overload choice. if (choice.hasValue()) { @@ -1547,9 +1462,9 @@ bool AssignmentFailure::diagnoseAsError() { if (!choice->isDecl()) { if (choice->getKind() == OverloadChoiceKind::KeyPathApplication && - !isa(immInfo.first)) { + !isa(immutableExpr)) { std::string message = "key path is read-only"; - if (auto *SE = dyn_cast(immInfo.first)) { + if (auto *SE = dyn_cast(immutableExpr)) { if (auto *DRE = dyn_cast(getKeyPathArgument(SE))) { auto identifier = DRE->getDecl()->getBaseName().getIdentifier(); message = @@ -1557,7 +1472,7 @@ bool AssignmentFailure::diagnoseAsError() { } } emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } return false; @@ -1571,7 +1486,7 @@ bool AssignmentFailure::diagnoseAsError() { message += VD->getName().str().str(); message += "'"; - auto type = getType(immInfo.first); + auto type = getType(immutableExpr); if (isKnownKeyPathType(type)) message += " is read-only"; @@ -1590,26 +1505,53 @@ bool AssignmentFailure::diagnoseAsError() { } emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); - // If there is a masked instance variable of the same type, emit a - // note to fixit prepend a 'self.'. + // If there is a masked property of the same type, emit a + // note to fixit prepend a 'self.' or 'Type.'. if (auto typeContext = DC->getInnermostTypeContext()) { - UnqualifiedLookup lookup(VD->getFullName(), typeContext); - for (auto &result : lookup.Results) { - const VarDecl *typeVar = dyn_cast(result.getValueDecl()); - if (typeVar && typeVar != VD && typeVar->isSettable(DC) && - typeVar->isSetterAccessibleFrom(DC) && - typeVar->getType()->isEqual(VD->getType())) { - // But not in its own accessor. - auto AD = - dyn_cast_or_null(DC->getInnermostMethodContext()); - if (!AD || AD->getStorage() != typeVar) { - emitDiagnostic(Loc, diag::masked_instance_variable, - typeContext->getSelfTypeInContext()) - .fixItInsert(Loc, "self."); - } + SmallVector results; + DC->lookupQualified(typeContext->getSelfNominalTypeDecl(), + VD->createNameRef(), NL_QualifiedDefault, results); + + auto foundProperty = llvm::find_if(results, [&](ValueDecl *decl) { + // We're looking for a settable property that is the same type as the + // var we found. + auto *var = dyn_cast(decl); + if (!var || var == VD) + return false; + + if (!var->isSettable(DC) || !var->isSetterAccessibleFrom(DC)) + return false; + + if (!var->getType()->isEqual(VD->getType())) + return false; + + // Don't suggest a property if we're in one of its accessors. + auto *methodDC = DC->getInnermostMethodContext(); + if (auto *AD = dyn_cast_or_null(methodDC)) + if (AD->getStorage() == var) + return false; + + return true; + }); + + if (foundProperty != results.end()) { + auto startLoc = immutableExpr->getStartLoc(); + auto *property = *foundProperty; + auto selfTy = typeContext->getSelfTypeInContext(); + + // If we found an instance property, suggest inserting "self.", + // otherwise suggest "Type." for a static property. + std::string fixItText; + if (property->isInstanceMember()) { + fixItText = "self."; + } else { + fixItText = selfTy->getString() + "."; } + emitDiagnostic(startLoc, diag::masked_mutable_property, + fixItText, property->getDescriptiveKind(), selfTy) + .fixItInsert(startLoc, fixItText); } } @@ -1630,7 +1572,7 @@ bool AssignmentFailure::diagnoseAsError() { message = "subscript is immutable"; emitDiagnostic(Loc, DeclDiagnostic, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } @@ -1652,7 +1594,7 @@ bool AssignmentFailure::diagnoseAsError() { message += " is not settable"; emitDiagnostic(Loc, diagID, message) - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); return true; } } @@ -1662,13 +1604,13 @@ bool AssignmentFailure::diagnoseAsError() { // If a keypath was the problem but wasn't resolved into a vardecl // it is ambiguous or unable to be used for setting. - if (auto *KPE = dyn_cast_or_null(immInfo.first)) { + if (auto *KPE = dyn_cast_or_null(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "immutable key path") .highlight(KPE->getSourceRange()); return true; } - if (auto LE = dyn_cast(immInfo.first)) { + if (auto LE = dyn_cast(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "literals are not mutable") .highlight(LE->getSourceRange()); return true; @@ -1676,7 +1618,7 @@ bool AssignmentFailure::diagnoseAsError() { // If the expression is the result of a call, it is an rvalue, not a mutable // lvalue. - if (auto *AE = dyn_cast(immInfo.first)) { + if (auto *AE = dyn_cast(immutableExpr)) { // Handle literals, which are a call to the conversion function. auto argsTuple = dyn_cast(AE->getArg()->getSemanticsProvidingExpr()); @@ -1709,22 +1651,22 @@ bool AssignmentFailure::diagnoseAsError() { return true; } - if (auto contextualType = cs.getContextualType(immInfo.first)) { + if (auto contextualType = cs.getContextualType(immutableExpr)) { Type neededType = contextualType->getInOutObjectType(); - Type actualType = getType(immInfo.first)->getInOutObjectType(); + Type actualType = getType(immutableExpr)->getInOutObjectType(); if (!neededType->isEqual(actualType)) { if (DeclDiagnostic.ID != diag::cannot_pass_rvalue_inout_subelement.ID) { emitDiagnostic(Loc, DeclDiagnostic, "implicit conversion from '" + actualType->getString() + "' to '" + neededType->getString() + "' requires a temporary") - .highlight(immInfo.first->getSourceRange()); + .highlight(immutableExpr->getSourceRange()); } return true; } } - if (auto IE = dyn_cast(immInfo.first)) { + if (auto IE = dyn_cast(immutableExpr)) { emitDiagnostic(Loc, DeclDiagnostic, "result of conditional operator '? :' is never mutable") .highlight(IE->getQuestionLoc()) @@ -1732,8 +1674,8 @@ bool AssignmentFailure::diagnoseAsError() { return true; } - emitDiagnostic(Loc, TypeDiagnostic, getType(destExpr)) - .highlight(immInfo.first->getSourceRange()); + emitDiagnostic(Loc, TypeDiagnostic, getType(DestExpr)) + .highlight(immutableExpr->getSourceRange()); return true; } @@ -1748,7 +1690,9 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const { return !storage->isSettable(nullptr) || !storage->isSetterAccessibleFrom(DC); - return false; + // If this is not something which could possibly be mutable, + // then it's immutable. + return true; }; // Provide specific diagnostics for assignment to subscripts whose base expr @@ -1861,15 +1805,13 @@ AssignmentFailure::getMemberRef(ConstraintLocator *locator) const { return member->choice; auto *DC = getDC(); - auto &TC = getTypeChecker(); - auto *decl = member->choice.getDecl(); if (isa(decl) && - isValidDynamicMemberLookupSubscript(cast(decl), DC, TC)) { + isValidDynamicMemberLookupSubscript(cast(decl), DC)) { auto *subscript = cast(decl); // If this is a keypath dynamic member lookup, we have to // adjust the locator to find member referred by it. - if (isValidKeyPathDynamicMemberLookup(subscript, TC)) { + if (isValidKeyPathDynamicMemberLookup(subscript)) { auto &cs = getConstraintSystem(); // Type has a following format: // `(Self) -> (dynamicMember: {Writable}KeyPath) -> U` @@ -1932,7 +1874,19 @@ bool ContextualFailure::diagnoseAsError() { if (diagnoseConversionToNil()) return true; - assert(!path.empty()); + if (path.empty()) { + if (auto *KPE = dyn_cast(anchor)) { + emitDiagnostic(KPE->getLoc(), + diag::expr_smart_keypath_value_covert_to_contextual_type, + getFromType(), getToType()); + return true; + } + + if (diagnoseCoercionToUnrelatedType()) + return true; + + return false; + } if (diagnoseMissingFunctionCall()) return true; @@ -1950,13 +1904,50 @@ bool ContextualFailure::diagnoseAsError() { return true; } + auto fromType = getFromType(); + auto toType = getToType(); + Diag diagnostic; switch (path.back().getKind()) { case ConstraintLocator::ClosureResult: { + auto *closure = cast(getRawAnchor()); + if (closure->hasExplicitResultType() && + closure->getExplicitResultTypeLoc().getTypeRepr()) { + auto resultRepr = closure->getExplicitResultTypeLoc().getTypeRepr(); + emitDiagnostic(resultRepr->getStartLoc(), + diag::incorrect_explicit_closure_result, fromType, toType) + .fixItReplace(resultRepr->getSourceRange(), toType.getString()); + return true; + } + diagnostic = diag::cannot_convert_closure_result; break; } + case ConstraintLocator::Condition: { + // Tailored diagnostics for optional or assignment use + // in condition expression. + if (diagnoseConversionToBool()) + return true; + + diagnostic = diag::cannot_convert_condition_value; + break; + } + + case ConstraintLocator::InstanceType: { + if (diagnoseCoercionToUnrelatedType()) + return true; + break; + } + + case ConstraintLocator::TernaryBranch: { + auto *ifExpr = cast(getRawAnchor()); + fromType = getType(ifExpr->getThenExpr()); + toType = getType(ifExpr->getElseExpr()); + diagnostic = diag::if_expr_cases_mismatch; + break; + } + case ConstraintLocator::ContextualType: { if (diagnoseConversionToBool()) return true; @@ -1967,25 +1958,109 @@ bool ContextualFailure::diagnoseAsError() { if (diagnoseYieldByReferenceMismatch()) return true; - auto contextualType = getToType(); - if (auto msg = getDiagnosticFor(CTP, contextualType->isExistentialType())) { + if (CTP == CTP_ForEachStmt) { + if (fromType->isAnyExistentialType()) { + emitDiagnostic(anchor->getLoc(), diag::type_cannot_conform, + /*isExistentialType=*/true, fromType, toType); + return true; + } + + emitDiagnostic( + anchor->getLoc(), + diag::foreach_sequence_does_not_conform_to_expected_protocol, + fromType, toType, bool(fromType->getOptionalObjectType())) + .highlight(anchor->getSourceRange()); + return true; + } + + if (auto *call = dyn_cast(anchor)) { + if (isa(call->getFn())) + CTP = CTP_ClosureResult; + } + + if (auto msg = getDiagnosticFor(CTP, toType)) { diagnostic = *msg; break; } return false; } + case ConstraintLocator::RValueAdjustment: { + auto &cs = getConstraintSystem(); + + auto overload = getChoiceFor( + cs.getConstraintLocator(anchor, ConstraintLocator::UnresolvedMember)); + if (!(overload && overload->choice.isDecl())) + return false; + + auto *choice = overload->choice.getDecl(); + auto fnType = fromType->getAs(); + if (!fnType) { + emitDiagnostic(anchor->getLoc(), + diag::expected_result_in_contextual_member, + choice->getFullName(), fromType, toType); + return true; + } + + // If member type is a function and contextual type matches + // its result type, most likely problem is related to a + // missing call e.g.: + // + // struct S { + // static func foo() -> S {} + // } + // + // let _: S = .foo + + auto params = fnType->getParams(); + + ParameterListInfo info(params, choice, + hasAppliedSelf(cs, overload->choice)); + auto numMissingArgs = llvm::count_if( + indices(params), [&info](const unsigned paramIdx) -> bool { + return !info.hasDefaultArgument(paramIdx); + }); + + if (numMissingArgs == 0 || numMissingArgs > 1) { + auto diagnostic = emitDiagnostic( + anchor->getLoc(), diag::expected_parens_in_contextual_member, + choice->getFullName()); + + // If there are no parameters we can suggest a fix-it + // to form an explicit call. + if (numMissingArgs == 0) + diagnostic.fixItInsertAfter(anchor->getEndLoc(), "()"); + } else { + emitDiagnostic(anchor->getLoc(), + diag::expected_argument_in_contextual_member, + choice->getFullName(), params.front().getPlainType()); + } + + return true; + } + default: return false; } - auto diag = emitDiagnostic(anchor->getLoc(), diagnostic, FromType, ToType); + auto diag = + emitDiagnostic(anchor->getLoc(), diagnostic, fromType, toType); diag.highlight(anchor->getSourceRange()); (void)tryFixIts(diag); return true; } +bool ContextualFailure::diagnoseAsNote() { + auto overload = getChoiceFor(getLocator()); + if (!(overload && overload->choice.isDecl())) + return false; + + auto *decl = overload->choice.getDecl(); + emitDiagnostic(decl, diag::found_candidate_type, getFromType()); + return true; +} + static Optional> getContextualNilDiagnostic(ContextualTypePurpose CTP) { switch (CTP) { @@ -2004,12 +2079,14 @@ getContextualNilDiagnostic(ContextualTypePurpose CTP) { return diag::cannot_convert_to_return_type_nil; case CTP_ThrowStmt: + case CTP_ForEachStmt: case CTP_YieldByReference: return None; case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value_nil; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value_nil; case CTP_YieldByValue: return diag::cannot_convert_yield_value_nil; @@ -2127,7 +2204,8 @@ bool ContextualFailure::diagnoseConversionToNil() const { emitDiagnostic(anchor->getLoc(), *diagnostic, getToType()); if (CTP == CTP_Initialization) { - auto *patternTR = cs.getContextualTypeLoc().getTypeRepr(); + auto *patternTR = + cs.getContextualTypeLoc(locator->getAnchor()).getTypeRepr(); if (!patternTR) return true; @@ -2171,14 +2249,16 @@ void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const { } bool ContextualFailure::diagnoseMissingFunctionCall() const { - auto &TC = getTypeChecker(); + if (getLocator()->isLastElement()) + return false; - auto *srcFT = FromType->getAs(); + auto *srcFT = getFromType()->getAs(); if (!srcFT || !srcFT->getParams().empty()) return false; - if (ToType->is() || - !TC.isConvertibleTo(srcFT->getResult(), ToType, getDC())) + auto toType = getToType(); + if (toType->is() || + !TypeChecker::isConvertibleTo(srcFT->getResult(), toType, getDC())) return false; auto *anchor = getAnchor(); @@ -2192,6 +2272,27 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { return true; } +bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { + auto *anchor = getAnchor(); + + if (auto *coerceExpr = dyn_cast(anchor)) { + auto fromType = getType(coerceExpr->getSubExpr()); + auto toType = getType(coerceExpr->getCastTypeLoc()); + + auto diagnostic = getDiagnosticFor(CTP_CoerceOperand, toType); + + auto diag = + emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); + diag.highlight(anchor->getSourceRange()); + + (void)tryFixIts(diag); + + return true; + } + + return false; +} + bool ContextualFailure::diagnoseConversionToBool() const { auto toType = getToType(); if (!toType->isBool()) @@ -2301,17 +2402,18 @@ bool ContextualFailure::diagnoseThrowsTypeMismatch() const { // If we tried to throw the error code of an error type, suggest object // construction. - auto &TC = getTypeChecker(); + auto &Ctx = getASTContext(); if (auto errorCodeProtocol = - TC.Context.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) { + Ctx.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) { Type errorCodeType = getFromType(); - if (auto conformance = TypeChecker::conformsToProtocol( - errorCodeType, errorCodeProtocol, getDC(), - ConformanceCheckFlags::InExpression)) { - Type errorType = conformance - ->getTypeWitnessByName(errorCodeType, - getASTContext().Id_ErrorType) - ->getCanonicalType(); + auto conformance = TypeChecker::conformsToProtocol( + errorCodeType, errorCodeProtocol, getDC(), + ConformanceCheckFlags::InExpression); + if (conformance) { + Type errorType = + conformance + .getTypeWitnessByName(errorCodeType, getASTContext().Id_ErrorType) + ->getCanonicalType(); if (errorType) { auto diagnostic = emitDiagnostic(anchor->getLoc(), diag::cannot_throw_error_code, @@ -2339,7 +2441,7 @@ bool ContextualFailure::diagnoseYieldByReferenceMismatch() const { return false; auto *anchor = getAnchor(); - auto exprType = getType(anchor); + auto exprType = getType(anchor, /*wantRValue=*/false); auto contextualType = getToType(); if (auto exprLV = exprType->getAs()) { @@ -2359,8 +2461,6 @@ bool ContextualFailure::tryRawRepresentableFixIts( InFlightDiagnostic &diagnostic, KnownProtocolKind rawRepresentableProtocol) const { auto &CS = getConstraintSystem(); - auto &TC = getTypeChecker(); - auto *expr = getAnchor(); auto fromType = getFromType(); auto toType = getToType(); @@ -2429,7 +2529,7 @@ bool ContextualFailure::tryRawRepresentableFixIts( convWrapBefore += "(rawValue: "; std::string convWrapAfter = ")"; if (!isa(expr) && - !TC.isConvertibleTo(fromType, rawTy, getDC())) { + !TypeChecker::isConvertibleTo(fromType, rawTy, getDC())) { // Only try to insert a converting construction if the protocol is a // literal protocol and not some other known protocol. switch (rawRepresentableProtocol) { @@ -2454,7 +2554,7 @@ bool ContextualFailure::tryRawRepresentableFixIts( if (conformsToKnownProtocol(CS, toType, rawRepresentableProtocol)) { std::string convWrapBefore; std::string convWrapAfter = ".rawValue"; - if (!TC.isConvertibleTo(rawTy, toType, getDC())) { + if (!TypeChecker::isConvertibleTo(rawTy, toType, getDC())) { // Only try to insert a converting construction if the protocol is a // literal protocol and not some other known protocol. switch (rawRepresentableProtocol) { @@ -2480,7 +2580,10 @@ bool ContextualFailure::tryRawRepresentableFixIts( bool ContextualFailure::tryIntegerCastFixIts( InFlightDiagnostic &diagnostic) const { - if (!isIntegerType(FromType) || !isIntegerType(ToType)) + auto fromType = getFromType(); + auto toType = getToType(); + + if (!isIntegerType(fromType) || !isIntegerType(toType)) return false; auto getInnerCastedExpr = [&](Expr *expr) -> Expr * { @@ -2501,8 +2604,7 @@ bool ContextualFailure::tryIntegerCastFixIts( auto *anchor = getAnchor(); if (Expr *innerE = getInnerCastedExpr(anchor)) { Type innerTy = getType(innerE); - auto &TC = getTypeChecker(); - if (TC.isConvertibleTo(innerTy, ToType, getDC())) { + if (TypeChecker::isConvertibleTo(innerTy, toType, getDC())) { // Remove the unnecessary cast. diagnostic.fixItRemoveChars(anchor->getLoc(), innerE->getStartLoc()) .fixItRemove(anchor->getEndLoc()); @@ -2511,7 +2613,7 @@ bool ContextualFailure::tryIntegerCastFixIts( } // Add a wrapping integer cast. - std::string convWrapBefore = ToType.getString(); + std::string convWrapBefore = toType.getString(); convWrapBefore += "("; std::string convWrapAfter = ")"; SourceRange exprRange = anchor->getSourceRange(); @@ -2525,20 +2627,29 @@ bool ContextualFailure::trySequenceSubsequenceFixIts( if (!getASTContext().getStdlibModule()) return false; - auto &TC = getTypeChecker(); - auto *DC = getDC(); - - auto String = TC.getStringType(DC); - auto Substring = TC.getSubstringType(DC); + auto String = TypeChecker::getStringType(getASTContext()); + auto Substring = TypeChecker::getSubstringType(getASTContext()); if (!String || !Substring) return false; // Substring -> String conversion // Wrap in String.init - if (FromType->isEqual(Substring)) { - if (ToType->isEqual(String)) { + if (getFromType()->isEqual(Substring)) { + if (getToType()->isEqual(String)) { auto *anchor = getAnchor()->getSemanticsProvidingExpr(); + if (auto *CE = dyn_cast(anchor)) { + anchor = CE->getSubExpr(); + } + + if (auto *call = dyn_cast(anchor)) { + auto *fnExpr = call->getFn(); + if (auto *closure = dyn_cast(fnExpr)) { + if (closure->hasSingleExpressionBody()) + anchor = closure->getSingleExpressionBody(); + } + } + auto range = anchor->getSourceRange(); diagnostic.fixItInsert(range.Start, "String("); diagnostic.fixItInsertAfter(range.End, ")"); @@ -2565,10 +2676,10 @@ bool ContextualFailure::tryTypeCoercionFixIt( if (!toType->hasTypeRepr()) return false; - auto &TC = getTypeChecker(); CheckedCastKind Kind = - TC.typeCheckCheckedCast(fromType, toType, CheckedCastContextKind::None, - getDC(), SourceLoc(), nullptr, SourceRange()); + TypeChecker::typeCheckCheckedCast(fromType, toType, + CheckedCastContextKind::None, getDC(), + SourceLoc(), nullptr, SourceRange()); if (Kind != CheckedCastKind::Unresolved) { auto *anchor = getAnchor(); @@ -2596,10 +2707,11 @@ bool ContextualFailure::tryProtocolConformanceFixIt( if (!nominal) return false; + auto fromType = getFromType(); // We need to get rid of optionals and parens as it's not relevant when // printing the diagnostic and the fix-it. auto unwrappedToType = - ToType->lookThroughAllOptionalTypes()->getWithoutParens(); + getToType()->lookThroughAllOptionalTypes()->getWithoutParens(); // If the protocol requires a class & we don't have one (maybe the context // is a struct), then bail out instead of offering a broken fix-it later on. @@ -2610,13 +2722,13 @@ bool ContextualFailure::tryProtocolConformanceFixIt( requiresClass = layout.requiresClass(); } - if (requiresClass && !FromType->is()) { + if (requiresClass && !fromType->is()) { return false; } // We can only offer a fix-it if we're assigning to a protocol type and // the type we're assigning is the same as the innermost type context. - bool shouldOfferFixIt = nominal->getSelfTypeInContext()->isEqual(FromType) && + bool shouldOfferFixIt = nominal->getSelfTypeInContext()->isEqual(fromType) && unwrappedToType->isExistentialType(); if (!shouldOfferFixIt) return false; @@ -2626,9 +2738,8 @@ bool ContextualFailure::tryProtocolConformanceFixIt( // Let's build a list of protocols that the context does not conform to. SmallVector missingProtoTypeStrings; for (auto protocol : layout.getProtocols()) { - if (!getTypeChecker().conformsToProtocol( - FromType, protocol->getDecl(), getDC(), - ConformanceCheckFlags::InExpression)) { + if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), getDC(), + ConformanceCheckFlags::InExpression)) { missingProtoTypeStrings.push_back(protocol->getString()); } } @@ -2655,7 +2766,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt( // TODO: Maybe also insert the requirement stubs? auto conformanceDiag = emitDiagnostic( getAnchor()->getLoc(), diag::assign_protocol_conformance_fix_it, - unwrappedToType, nominal->getDescriptiveKind(), FromType); + unwrappedToType, nominal->getDescriptiveKind(), fromType); if (nominal->getInherited().size() > 0) { auto lastInherited = nominal->getInherited().back().getLoc(); auto lastInheritedEndLoc = @@ -2687,7 +2798,7 @@ void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const { if (auto TLCD = dyn_cast(getDC())) { if (TLCD->getBody()->isImplicit()) { - if (auto decl = TLCD->getBody()->getElement(0).dyn_cast()) { + if (auto decl = TLCD->getBody()->getFirstElement().dyn_cast()) { if (auto binding = dyn_cast(decl)) { PBD = binding; } @@ -2699,15 +2810,15 @@ void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const { if (PBD) { if (auto VD = PBD->getSingleVar()) { - auto entry = PBD->getPatternEntryForVarDecl(VD); - + const auto i = PBD->getPatternEntryIndexForVarDecl(VD); + auto *initExpr = PBD->getInit(i); if (!VD->isStatic() && !VD->getAttrs().getAttribute() && - entry.getInit() && isa(entry.getInit())) { + initExpr && isa(initExpr)) { auto diag = emitDiagnostic(expr->getLoc(), diag::extension_stored_property_fixit, VD->getName()); - diag.fixItRemove(entry.getEqualLoc()); + diag.fixItRemove(PBD->getEqualLoc(i)); if (VD->isLet()) { diag.fixItReplace(PBD->getStartLoc(), getTokenText(tok::kw_var)); @@ -2733,26 +2844,40 @@ bool ContextualFailure::isIntegerToStringIndexConversion() const { Optional> ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, - bool forProtocol) { + Type contextualType) { + auto forProtocol = contextualType->isExistentialType(); switch (context) { - case CTP_Initialization: + case CTP_Initialization: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_initializer_value_anyobject; + return forProtocol ? diag::cannot_convert_initializer_value_protocol : diag::cannot_convert_initializer_value; + } case CTP_ReturnStmt: - case CTP_ReturnSingleExpr: + case CTP_ReturnSingleExpr: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_return_type_to_anyobject; + return forProtocol ? diag::cannot_convert_to_return_type_protocol : diag::cannot_convert_to_return_type; + } case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return forProtocol ? diag::cannot_convert_default_arg_value_protocol : diag::cannot_convert_default_arg_value; case CTP_YieldByValue: return forProtocol ? diag::cannot_convert_yield_value_protocol : diag::cannot_convert_yield_value; - case CTP_CallArgument: + case CTP_CallArgument: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_argument_value_anyobject; + return forProtocol ? diag::cannot_convert_argument_value_protocol : diag::cannot_convert_argument_value; + } case CTP_ClosureResult: return forProtocol ? diag::cannot_convert_closure_result_protocol : diag::cannot_convert_closure_result; @@ -2768,9 +2893,13 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_CoerceOperand: return forProtocol ? diag::cannot_convert_coerce_protocol : diag::cannot_convert_coerce; - case CTP_AssignSource: + case CTP_AssignSource: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_assign_anyobject; + return forProtocol ? diag::cannot_convert_assign_protocol : diag::cannot_convert_assign; + } case CTP_SubscriptAssignSource: return forProtocol ? diag::cannot_convert_subscript_assign_protocol : diag::cannot_convert_subscript_assign; @@ -2778,6 +2907,7 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, return diag::cannot_convert_condition_value; case CTP_ThrowStmt: + case CTP_ForEachStmt: case CTP_Unused: case CTP_CannotFail: case CTP_YieldByReference: @@ -2788,13 +2918,33 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, } bool TupleContextualFailure::diagnoseAsError() { - auto diagnostic = isNumElementsMismatch() - ? diag::tuple_types_not_convertible_nelts - : diag::tuple_types_not_convertible; + Diag diagnostic; + auto purpose = getContextualTypePurpose(); + auto &cs = getConstraintSystem(); + if (isNumElementsMismatch()) + diagnostic = diag::tuple_types_not_convertible_nelts; + else if ((purpose == CTP_Initialization) && + !cs.getContextualType(getAnchor())) + diagnostic = diag::tuple_types_not_convertible; + else if (auto diag = getDiagnosticFor(purpose, getToType())) + diagnostic = *diag; + else + return false; + emitDiagnostic(getAnchor()->getLoc(), diagnostic, getFromType(), getToType()); return true; } +bool FunctionTypeMismatch::diagnoseAsError() { + auto purpose = getContextualTypePurpose(); + auto diagnostic = getDiagnosticFor(purpose, getToType()); + if (!diagnostic) + return false; + + emitDiagnostic(getAnchor()->getLoc(), *diagnostic, getFromType(), getToType()); + return true; +} + bool AutoClosureForwardingFailure::diagnoseAsError() { auto *loc = getLocator(); auto last = loc->castLastElementTo(); @@ -3011,7 +3161,7 @@ bool SubscriptMisuseFailure::diagnoseAsNote() { /// meaning their lower case counterparts are identical. /// - DeclName is valid when such a correct case is found; invalid otherwise. DeclName MissingMemberFailure::findCorrectEnumCaseName( - Type Ty, TypoCorrectionResults &corrections, DeclName memberName) { + Type Ty, TypoCorrectionResults &corrections, DeclNameRef memberName) { if (memberName.isSpecial() || !memberName.isSimpleName()) return DeclName(); if (!Ty->getEnumOrBoundGenericEnum()) @@ -3026,13 +3176,15 @@ DeclName MissingMemberFailure::findCorrectEnumCaseName( } bool MissingMemberFailure::diagnoseAsError() { - auto &TC = getTypeChecker(); auto *anchor = getRawAnchor(); auto *baseExpr = getAnchor(); if (!anchor || !baseExpr) return false; + if (diagnoseForDynamicCallable()) + return true; + auto baseType = resolveType(getBaseType())->getWithoutSpecifierType(); DeclNameLoc nameLoc(anchor->getStartLoc()); @@ -3053,15 +3205,26 @@ bool MissingMemberFailure::diagnoseAsError() { if (baseType->is()) diagnostic = diag::could_not_find_tuple_member; + bool hasUnresolvedPattern = false; + anchor->forEachChildExpr([&](Expr *expr) { + hasUnresolvedPattern |= isa(expr); + return hasUnresolvedPattern ? nullptr : expr; + }); + if (hasUnresolvedPattern && !baseType->getAs()) { + emitDiagnostic(anchor->getLoc(), + diag::cannot_match_unresolved_expr_pattern_with_value, baseType); + return; + } + emitDiagnostic(anchor->getLoc(), diagnostic, baseType, getName()) .highlight(baseExpr->getSourceRange()) .highlight(nameLoc.getSourceRange()); }; - TypoCorrectionResults corrections(TC, getName(), nameLoc); + TypoCorrectionResults corrections(getName(), nameLoc); auto tryTypoCorrection = [&] (Type type) { - TC.performTypoCorrection(getDC(), DeclRefKind::Ordinary, type, - defaultMemberLookupOptions, corrections); + TypeChecker::performTypoCorrection(getDC(), DeclRefKind::Ordinary, type, + defaultMemberLookupOptions, corrections); }; if (getName().getBaseName().getKind() == DeclBaseName::Kind::Subscript) { @@ -3102,10 +3265,9 @@ bool MissingMemberFailure::diagnoseAsError() { getName().getBaseName() == DeclBaseName::createConstructor()) { auto &cs = getConstraintSystem(); - auto memberName = getName().getBaseName(); auto result = cs.performMemberLookup( - ConstraintKind::ValueMember, memberName, metatypeTy, - FunctionRefKind::DoubleApply, getLocator(), + ConstraintKind::ValueMember, getName().withoutArgumentLabels(), + metatypeTy, FunctionRefKind::DoubleApply, getLocator(), /*includeInaccessibleMembers=*/true); // If there are no `init` members at all produce a tailored @@ -3193,6 +3355,26 @@ bool MissingMemberFailure::diagnoseAsError() { return true; } +bool MissingMemberFailure::diagnoseForDynamicCallable() const { + auto *locator = getLocator(); + if (!locator->isLastElement()) + return false; + + auto memberName = getName(); + auto arguments = memberName.getArgumentNames(); + assert(arguments.size() == 1); + + auto &ctx = getASTContext(); + if (arguments.front() == ctx.Id_withKeywordArguments) { + auto anchor = getAnchor(); + emitDiagnostic(anchor->getLoc(), + diag::missing_dynamic_callable_kwargs_method, getBaseType()); + return true; + } + + return false; +} + bool InvalidMemberRefOnExistential::diagnoseAsError() { auto *anchor = getRawAnchor(); @@ -3227,7 +3409,13 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { return true; } - Expr *expr = getParentExpr(); + auto getRootExpr = [&cs](Expr *expr) { + while (auto parent = cs.getParentExpr(expr)) + expr = parent; + return expr; + }; + + Expr *expr = findParentExpr(getAnchor()); SourceRange baseRange = expr ? expr->getSourceRange() : SourceRange(); // If the base is an implicit self type reference, and we're in a @@ -3282,7 +3470,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { OverloadChoice choice = selection->choice; if (choice.isDecl() && isMutable(choice.getDecl()) && !isCallArgument(initCall) && - cs.getContextualTypePurpose() == CTP_Unused) { + cs.getContextualTypePurpose(getRootExpr(ctorRef)) == CTP_Unused) { auto fixItLoc = ctorRef->getBase()->getSourceRange().End; emitDiagnostic(loc, diag::init_not_instance_member_use_assignment) .fixItInsertAfter(fixItLoc, " = "); @@ -3406,10 +3594,10 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { // static members doesn't make a whole lot of sense if (auto TAD = dyn_cast(Member)) { Diag.emplace(emitDiagnostic(loc, diag::typealias_outside_of_protocol, - TAD->getName())); + Name)); } else if (auto ATD = dyn_cast(Member)) { Diag.emplace(emitDiagnostic(loc, diag::assoc_type_outside_of_protocol, - ATD->getName())); + Name)); } else if (isa(Member)) { Diag.emplace(emitDiagnostic(loc, diag::construct_protocol_by_name, instanceTy)); @@ -3440,7 +3628,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { // components, let's provide a tailored diagnostic and return because // that is unsupported so there is no fix-it. if (locator->isForKeyPathComponent()) { - InvalidStaticMemberRefInKeyPath failure(expr, cs, Member, locator); + InvalidStaticMemberRefInKeyPath failure(cs, Member, locator); return failure.diagnoseAsError(); } @@ -3468,7 +3656,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { Type contextualType; for (auto iterateCS = &cs; contextualType.isNull() && iterateCS; iterateCS = iterateCS->baseCS) { - contextualType = iterateCS->getContextualType(); + contextualType = iterateCS->getContextualType(getRawAnchor()); } // Try to provide a fix-it that only contains a '.' @@ -3479,11 +3667,10 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() { // Check if the expression is the matching operator ~=, most often used in // case statements. If so, try to provide a single dot fix-it - const Expr *contextualTypeNode = nullptr; + const Expr *contextualTypeNode = getRootExpr(getAnchor()); ConstraintSystem *lastCS = nullptr; for (auto iterateCS = &cs; iterateCS; iterateCS = iterateCS->baseCS) { lastCS = iterateCS; - contextualTypeNode = iterateCS->getContextualTypeNode(); } // The '~=' operator is an overloaded decl ref inside a binaryExpr @@ -3584,12 +3771,11 @@ bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() { bool MissingArgumentsFailure::diagnoseAsError() { auto &cs = getConstraintSystem(); auto *locator = getLocator(); - auto path = locator->getPath(); - if (path.empty() || - !(path.back().getKind() == ConstraintLocator::ApplyArgToParam || - path.back().getKind() == ConstraintLocator::ContextualType || - path.back().getKind() == ConstraintLocator::ApplyArgument)) + if (!(locator->isLastElement() || + locator->isLastElement() || + locator->isLastElement() || + locator->isLastElement())) return false; // If this is a misplaced `missng argument` situation, it would be @@ -3614,7 +3800,7 @@ bool MissingArgumentsFailure::diagnoseAsError() { // foo(bar) // `() -> Void` vs. `(Int) -> Void` // ``` if (locator->isLastElement()) { - auto info = *getFunctionArgApplyInfo(locator); + auto info = *(cs.getFunctionArgApplyInfo(locator)); auto *argExpr = info.getArgExpr(); emitDiagnostic(argExpr->getLoc(), diag::cannot_convert_argument_value, @@ -3632,7 +3818,8 @@ bool MissingArgumentsFailure::diagnoseAsError() { if (locator->isLastElement()) { auto &cs = getConstraintSystem(); emitDiagnostic(anchor->getLoc(), diag::cannot_convert_initializer_value, - getType(anchor), resolveType(cs.getContextualType())); + getType(anchor), + resolveType(cs.getContextualType(getAnchor()))); // TODO: It would be great so somehow point out which arguments are missing. return true; } @@ -3703,12 +3890,27 @@ bool MissingArgumentsFailure::diagnoseAsError() { return true; } +bool MissingArgumentsFailure::diagnoseAsNote() { + auto *locator = getLocator(); + if (auto overload = getChoiceFor(locator)) { + auto *fn = resolveType(overload->openedType)->getAs(); + auto loc = overload->choice.getDecl()->getLoc(); + if (loc.isInvalid()) + loc = getAnchor()->getLoc(); + emitDiagnostic(loc, diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + return true; + } + + return false; +} + bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const { auto &ctx = getASTContext(); auto *anchor = getRawAnchor(); if (!(isa(anchor) || isa(anchor) || - isa(anchor))) + isa(anchor) || isa(anchor))) return false; if (SynthesizedArgs.size() != 1) @@ -3829,9 +4031,16 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) { auto *locator = getLocator(); if (locator->isForContextualType()) { - funcType = cs.getContextualType()->getAs(); - } else if (auto info = getFunctionArgApplyInfo(locator)) { + funcType = cs.getContextualType(locator->getAnchor())->getAs(); + } else if (auto info = cs.getFunctionArgApplyInfo(locator)) { funcType = info->getParamType()->getAs(); + } else if (locator->isLastElement()) { + // Based on the locator we know this this is something like this: + // `let _: () -> ((Int) -> Void) = { return {} }`. + funcType = getType(getRawAnchor()) + ->castTo() + ->getResult() + ->castTo(); } if (!funcType) @@ -3974,7 +4183,7 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument( return false; auto *fnType = - cs.simplifyType(overloadChoice->ImpliedType)->getAs(); + cs.simplifyType(overloadChoice->openedType)->getAs(); if (!(fnType && fnType->getNumParams() == 2)) return false; @@ -4024,8 +4233,7 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument( auto argType = cs.simplifyType(cs.getType(argument)); auto paramType = fnType->getParams()[1].getPlainType(); - auto &TC = cs.getTypeChecker(); - return TC.isConvertibleTo(argType, paramType, cs.DC); + return TypeChecker::isConvertibleTo(argType, paramType, cs.DC); } std::tuple @@ -4039,6 +4247,9 @@ MissingArgumentsFailure::getCallInfo(Expr *anchor) const { } else if (auto *SE = dyn_cast(anchor)) { return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(), SE->hasTrailingClosure()); + } else if (auto *OLE = dyn_cast(anchor)) { + return std::make_tuple(OLE, OLE->getArg(), OLE->getNumArguments(), + OLE->hasTrailingClosure()); } return std::make_tuple(nullptr, nullptr, 0, false); @@ -4274,7 +4485,151 @@ bool OutOfOrderArgumentFailure::diagnoseAsError() { addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_named, first, second)); } + return true; +} + +bool ExtraneousArgumentsFailure::diagnoseAsError() { + // Simplified anchor would point directly to the + // argument in case of contextual mismatch. + auto *anchor = getAnchor(); + if (auto *closure = dyn_cast(anchor)) { + auto fnType = ContextualType; + auto params = closure->getParameters(); + + auto diag = emitDiagnostic( + params->getStartLoc(), diag::closure_argument_list_tuple, fnType, + fnType->getNumParams(), params->size(), (params->size() == 1)); + + bool onlyAnonymousParams = + std::all_of(params->begin(), params->end(), + [](ParamDecl *param) { return !param->hasName(); }); + + // If closure expects no parameters but N was given, + // and all of them are anonymous let's suggest removing them. + if (fnType->getNumParams() == 0 && onlyAnonymousParams) { + auto inLoc = closure->getInLoc(); + auto &sourceMgr = getASTContext().SourceMgr; + + if (inLoc.isValid()) + diag.fixItRemoveChars(params->getStartLoc(), + Lexer::getLocForEndOfToken(sourceMgr, inLoc)); + } + return true; + } + + if (isContextualMismatch()) { + auto *locator = getLocator(); + emitDiagnostic(anchor->getLoc(), + locator->isLastElement() + ? diag::cannot_convert_initializer_value + : diag::cannot_convert_argument_value, + getType(anchor), ContextualType); + return true; + } + + if (ExtraArgs.size() == 1) { + return diagnoseSingleExtraArgument(); + } + + if (ContextualType->getNumParams() == 0) { + if (auto argExpr = getArgumentListExprFor(getLocator())) { + emitDiagnostic(anchor->getLoc(), diag::extra_argument_to_nullary_call) + .highlight(argExpr->getSourceRange()) + .fixItRemove(argExpr->getSourceRange()); + return true; + } + } + + if (ExtraArgs.size() < 2) + return false; + + llvm::SmallString<64> positions; + llvm::raw_svector_ostream OS(positions); + + interleave( + ExtraArgs, + [&](const std::pair &arg) { + OS << "#" << (arg.first + 1); + }, + [&] { OS << ", "; }); + + emitDiagnostic(anchor->getLoc(), diag::extra_arguments_in_call, OS.str()); + if (auto overload = getChoiceFor(getLocator())) { + if (auto *decl = overload->choice.getDeclOrNull()) { + emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName()); + } + } + + return true; +} + +bool ExtraneousArgumentsFailure::diagnoseAsNote() { + auto overload = getChoiceFor(getLocator()); + if (!(overload && overload->choice.isDecl())) + return false; + + auto *decl = overload->choice.getDecl(); + auto *anchor = getAnchor(); + auto numArgs = getTotalNumArguments(); + emitDiagnostic(decl, diag::candidate_with_extraneous_args, ContextualType, + ContextualType->getNumParams(), numArgs, (numArgs == 1), + isa(anchor)); + return true; +} + +bool ExtraneousArgumentsFailure::diagnoseSingleExtraArgument() const { + auto *locator = getLocator(); + + // This specifically handles a case of `Void(...)` which generates + // constraints differently from other constructor invocations and + // wouldn't have `ApplyArgument` as a last element in the locator. + if (auto *call = dyn_cast(getRawAnchor())) { + auto *TE = dyn_cast(call->getFn()); + if (TE && getType(TE)->getMetatypeInstanceType()->isVoid()) { + emitDiagnostic(call->getLoc(), diag::extra_argument_to_nullary_call) + .highlight(call->getArg()->getSourceRange()); + return true; + } + } + + auto *arguments = getArgumentListExprFor(locator); + if (!arguments) + return false; + + const auto &e = ExtraArgs.front(); + auto index = e.first; + auto argument = e.second; + + auto tuple = dyn_cast(arguments); + auto argExpr = tuple ? tuple->getElement(index) + : cast(arguments)->getSubExpr(); + + auto loc = argExpr->getLoc(); + if (tuple && index == tuple->getNumElements() - 1 && + tuple->hasTrailingClosure()) { + emitDiagnostic(loc, diag::extra_trailing_closure_in_call) + .highlight(argExpr->getSourceRange()); + } else if (ContextualType->getNumParams() == 0) { + auto *PE = dyn_cast(arguments); + Expr *subExpr = nullptr; + if (PE) + subExpr = PE->getSubExpr(); + + if (subExpr && argument.getPlainType()->isVoid()) { + emitDiagnostic(loc, diag::extra_argument_to_nullary_call) + .fixItRemove(subExpr->getSourceRange()); + } else { + emitDiagnostic(loc, diag::extra_argument_to_nullary_call) + .highlight(argExpr->getSourceRange()); + } + } else if (argument.hasLabel()) { + emitDiagnostic(loc, diag::extra_argument_named, argument.getLabel()) + .highlight(argExpr->getSourceRange()); + } else { + emitDiagnostic(loc, diag::extra_argument_positional) + .highlight(argExpr->getSourceRange()); + } return true; } @@ -4310,9 +4665,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() { auto &cs = getConstraintSystem(); auto *locator = cs.getConstraintLocator(baseExpr, ConstraintLocator::Member); - if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) { - return fix->getLocator() == locator; - })) + if (cs.hasFixFor(locator)) return false; } @@ -4407,7 +4760,8 @@ SourceLoc InvalidUseOfAddressOf::getLoc() const { } bool InvalidUseOfAddressOf::diagnoseAsError() { - if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) { + auto &cs = getConstraintSystem(); + if (auto argApplyInfo = cs.getFunctionArgApplyInfo(getLocator())) { if (!argApplyInfo->getParameterFlags().isInOut()) { auto anchor = getAnchor(); emitDiagnostic(anchor->getLoc(), diag::extra_address_of, getToType()) @@ -4503,17 +4857,16 @@ bool MissingContextualConformanceFailure::diagnoseAsError() { if (path.empty()) { assert(isa(anchor)); if (isa(cast(anchor)->getDest())) { - diagnostic = - getDiagnosticFor(CTP_SubscriptAssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_SubscriptAssignSource, getToType()); } else { - diagnostic = getDiagnosticFor(CTP_AssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_AssignSource, getToType()); } } else { const auto &last = path.back(); switch (last.getKind()) { case ConstraintLocator::ContextualType: assert(Context != CTP_Unused); - diagnostic = getDiagnosticFor(Context, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(Context, getToType()); break; case ConstraintLocator::SequenceElementType: { @@ -4654,7 +5007,6 @@ bool MissingGenericArgumentsFailure::diagnoseParameter( void MissingGenericArgumentsFailure::emitGenericSignatureNote( Anchor anchor) const { auto &cs = getConstraintSystem(); - auto &TC = getTypeChecker(); auto *paramDC = getDeclContext(); if (!paramDC) @@ -4700,8 +5052,8 @@ void MissingGenericArgumentsFailure::emitGenericSignatureNote( SmallString<64> paramsAsString; auto baseType = anchor.get(); - if (TC.getDefaultGenericArgumentsString(paramsAsString, GTD, - getPreferredType)) { + if (TypeChecker::getDefaultGenericArgumentsString(paramsAsString, GTD, + getPreferredType)) { auto diagnostic = emitDiagnostic( baseType->getLoc(), diag::unbound_generic_parameter_explicit_fix); @@ -4913,23 +5265,9 @@ bool InvalidTupleSplatWithSingleParameterFailure::diagnoseAsError() { } } - // If the parameter is a generic parameter, it's hard to say - // whether use of a tuple is really intended here, so let's - // attach a fix-it to a note instead of the diagnostic message - // to indicate that it's not the only right solution possible. - if (auto *typeVar = ParamType->getAs()) { - if (typeVar->getImpl().getGenericParameter()) { - diagnostic.flush(); - - emitDiagnostic(argExpr->getLoc(), diag::note_maybe_forgot_to_form_tuple) - .fixItInsertAfter(newLeftParenLoc, "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } - } else { - diagnostic.highlight(argExpr->getSourceRange()) - .fixItInsertAfter(newLeftParenLoc, "(") - .fixItInsert(argExpr->getEndLoc(), ")"); - } + diagnostic.highlight(argExpr->getSourceRange()) + .fixItInsertAfter(newLeftParenLoc, "(") + .fixItInsert(argExpr->getEndLoc(), ")"); return true; } @@ -4942,20 +5280,21 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() { } bool InOutConversionFailure::diagnoseAsError() { + auto &cs = getConstraintSystem(); auto *anchor = getAnchor(); auto *locator = getLocator(); auto path = locator->getPath(); if (!path.empty() && path.back().getKind() == ConstraintLocator::FunctionArgument) { - if (auto argApplyInfo = getFunctionArgApplyInfo(locator)) { + if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) { emitDiagnostic(anchor->getLoc(), diag::cannot_convert_argument_value, argApplyInfo->getArgType(), argApplyInfo->getParamType()); } else { assert(locator->findLast()); - auto contextualType = getConstraintSystem().getContextualType(); + auto contextualType = cs.getContextualType(anchor); auto purpose = getContextualTypePurpose(); - auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false); + auto diagnostic = getDiagnosticFor(purpose, contextualType); if (!diagnostic) return false; @@ -5057,9 +5396,18 @@ bool ArgumentMismatchFailure::diagnoseAsError() { if (diagnoseUseOfReferenceEqualityOperator()) return true; + if (diagnosePropertyWrapperMismatch()) + return true; + auto argType = getFromType(); auto paramType = getToType(); + if (paramType->isAnyObject()) { + emitDiagnostic(getLoc(), diag::cannot_convert_argument_value_anyobject, + argType, paramType); + return true; + } + Diag diagnostic = diag::cannot_convert_argument_value; // If parameter type is a protocol value, let's says that @@ -5073,7 +5421,7 @@ bool ArgumentMismatchFailure::diagnoseAsError() { // let's match up its element type to the argument to see whether // it would be appropriate to suggest adding `&`. auto *argExpr = getAnchor(); - if (getType(argExpr)->is()) { + if (getType(argExpr, /*wantRValue=*/false)->is()) { auto elementTy = paramType->getAnyPointerElementType(); if (elementTy && argType->isEqual(elementTy)) { diag.fixItInsert(argExpr->getStartLoc(), "&"); @@ -5109,8 +5457,8 @@ bool ArgumentMismatchFailure::diagnoseUseOfReferenceEqualityOperator() const { auto name = *getOperatorName(binaryOp->getFn()); - auto lhsType = getType(lhs)->getRValueType(); - auto rhsType = getType(rhs)->getRValueType(); + auto lhsType = getType(lhs); + auto rhsType = getType(rhs); // If both arguments where incorrect e.g. both are function types, // let's avoid producing a diagnostic second time, because first @@ -5171,8 +5519,8 @@ bool ArgumentMismatchFailure::diagnosePatternMatchingMismatch() const { auto *lhsExpr = op->getArg()->getElement(0); auto *rhsExpr = op->getArg()->getElement(1); - auto lhsType = getType(lhsExpr)->getRValueType(); - auto rhsType = getType(rhsExpr)->getRValueType(); + auto lhsType = getType(lhsExpr); + auto rhsType = getType(rhsExpr); auto diagnostic = lhsType->is() @@ -5290,12 +5638,38 @@ bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const { auto *anchor = getRawAnchor(); MissingArgumentsFailure failure( - getParentExpr(), cs, {param.withType(argType)}, + cs, {param.withType(argType)}, cs.getConstraintLocator(anchor, ConstraintLocator::ApplyArgument)); return failure.diagnoseSingleMissingArgument(); } +bool ArgumentMismatchFailure::diagnosePropertyWrapperMismatch() const { + auto argType = getFromType(); + auto paramType = getToType(); + + // Verify that this is an implicit call to a property wrapper initializer + // in a form of `init(wrappedValue:)` or deprecated `init(initialValue:)`. + auto *call = dyn_cast(getRawAnchor()); + if (!(call && call->isImplicit() && isa(call->getFn()) && + call->getNumArguments() == 1 && + (call->getArgumentLabels().front() == getASTContext().Id_wrappedValue || + call->getArgumentLabels().front() == getASTContext().Id_initialValue))) + return false; + + auto argExpr = cast(call->getArg())->getElement(0); + // If this is an attempt to initialize property wrapper with opaque value + // of error type, let's just ignore that problem since original mismatch + // has been diagnosed already. + if (argExpr->isImplicit() && isa(argExpr) && + argType->is()) + return true; + + emitDiagnostic(getLoc(), diag::cannot_convert_initializer_value, argType, + paramType); + return true; +} + void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt( Expr *anchor) const { // If this is an array literal, offer to remove the brackets and pass the @@ -5336,3 +5710,475 @@ bool ExpandArrayIntoVarargsFailure::diagnoseAsNote() { } return false; } + +bool ExtraneousCallFailure::diagnoseAsError() { + auto &cs = getConstraintSystem(); + + auto *anchor = getAnchor(); + auto *locator = getLocator(); + + // If this is something like `foo()` where `foo` is a variable + // or a property, let's suggest dropping `()`. + auto removeParensFixIt = [&](InFlightDiagnostic &diagnostic) { + auto *argLoc = cs.getConstraintLocator(getRawAnchor(), + ConstraintLocator::ApplyArgument); + + if (auto *TE = + dyn_cast_or_null(simplifyLocatorToAnchor(argLoc))) { + if (TE->getNumElements() == 0) { + diagnostic.fixItRemove(TE->getSourceRange()); + } + } + }; + + if (auto overload = getChoiceFor(cs.getCalleeLocator(locator))) { + if (auto *decl = overload->choice.getDeclOrNull()) { + if (auto *enumCase = dyn_cast(decl)) { + auto diagnostic = emitDiagnostic( + anchor->getLoc(), diag::unexpected_arguments_in_enum_case, + enumCase->getName()); + removeParensFixIt(diagnostic); + return true; + } + } + } + + if (auto *UDE = dyn_cast(anchor)) { + auto *baseExpr = UDE->getBase(); + auto *call = cast(getRawAnchor()); + + if (getType(baseExpr)->isAnyObject()) { + emitDiagnostic(anchor->getLoc(), diag::cannot_call_with_params, + UDE->getName().getBaseName().userFacingName(), + getType(call->getArg())->getString(), + isa(baseExpr)); + return true; + } + } + + auto diagnostic = emitDiagnostic( + anchor->getLoc(), diag::cannot_call_non_function_value, getType(anchor)); + removeParensFixIt(diagnostic); + return true; +} + +bool InvalidUseOfTrailingClosure::diagnoseAsError() { + auto *anchor = getAnchor(); + auto &cs = getConstraintSystem(); + + emitDiagnostic(anchor->getLoc(), diag::trailing_closure_bad_param, + getToType()) + .highlight(anchor->getSourceRange()); + + if (auto overload = getChoiceFor(cs.getCalleeLocator(getLocator()))) { + if (auto *decl = overload->choice.getDeclOrNull()) { + emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName()); + } + } + + return true; +} + +void NonEphemeralConversionFailure::emitSuggestionNotes() const { + auto getPointerKind = [](Type ty) -> PointerTypeKind { + PointerTypeKind pointerKind; + auto pointeeType = ty->lookThroughSingleOptionalType() + ->getAnyPointerElementType(pointerKind); + assert(pointeeType && "Expected a pointer!"); + (void)pointeeType; + + return pointerKind; + }; + + // This must stay in sync with diag::ephemeral_use_array_with_unsafe_buffer + // and diag::ephemeral_use_with_unsafe_pointer. + enum AlternativeKind { + AK_Raw = 0, + AK_MutableRaw, + AK_Typed, + AK_MutableTyped, + }; + + auto getAlternativeKind = [&]() -> Optional { + switch (getPointerKind(getParamType())) { + case PTK_UnsafeRawPointer: + return AK_Raw; + case PTK_UnsafeMutableRawPointer: + return AK_MutableRaw; + case PTK_UnsafePointer: + return AK_Typed; + case PTK_UnsafeMutablePointer: + return AK_MutableTyped; + case PTK_AutoreleasingUnsafeMutablePointer: + return None; + } + }; + + // First emit a note about the implicit conversion only lasting for the + // duration of the call. + auto *argExpr = getArgExpr(); + emitDiagnostic(argExpr->getLoc(), + diag::ephemeral_pointer_argument_conversion_note, + getArgType(), getParamType(), getCallee(), getCalleeFullName()) + .highlight(argExpr->getSourceRange()); + + // Then try to find a suitable alternative. + switch (ConversionKind) { + case ConversionRestrictionKind::ArrayToPointer: { + // Don't suggest anything for optional arrays, as there's currently no + // direct alternative. + if (getArgType()->getOptionalObjectType()) + break; + + // We can suggest using withUnsafe[Mutable][Bytes/BufferPointer]. + if (auto alternative = getAlternativeKind()) + emitDiagnostic(argExpr->getLoc(), + diag::ephemeral_use_array_with_unsafe_buffer, + *alternative); + break; + } + case ConversionRestrictionKind::StringToPointer: { + // Don't suggest anything for optional strings, as there's currently no + // direct alternative. + if (getArgType()->getOptionalObjectType()) + break; + + // We can suggest withCString as long as the resulting pointer is + // immutable. + switch (getPointerKind(getParamType())) { + case PTK_UnsafePointer: + case PTK_UnsafeRawPointer: + emitDiagnostic(argExpr->getLoc(), + diag::ephemeral_use_string_with_c_string); + break; + case PTK_UnsafeMutableRawPointer: + case PTK_UnsafeMutablePointer: + case PTK_AutoreleasingUnsafeMutablePointer: + // There's nothing really sensible we can suggest for a mutable pointer. + break; + } + break; + } + case ConversionRestrictionKind::InoutToPointer: + // For an arbitrary inout-to-pointer, we can suggest + // withUnsafe[Mutable][Bytes/Pointer]. + if (auto alternative = getAlternativeKind()) + emitDiagnostic(argExpr->getLoc(), diag::ephemeral_use_with_unsafe_pointer, + *alternative); + break; + case ConversionRestrictionKind::DeepEquality: + case ConversionRestrictionKind::Superclass: + case ConversionRestrictionKind::Existential: + case ConversionRestrictionKind::MetatypeToExistentialMetatype: + case ConversionRestrictionKind::ExistentialMetatypeToMetatype: + case ConversionRestrictionKind::ValueToOptional: + case ConversionRestrictionKind::OptionalToOptional: + case ConversionRestrictionKind::ClassMetatypeToAnyObject: + case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: + case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: + case ConversionRestrictionKind::PointerToPointer: + case ConversionRestrictionKind::ArrayUpcast: + case ConversionRestrictionKind::DictionaryUpcast: + case ConversionRestrictionKind::SetUpcast: + case ConversionRestrictionKind::HashableToAnyHashable: + case ConversionRestrictionKind::CFTollFreeBridgeToObjC: + case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: + llvm_unreachable("Expected an ephemeral conversion!"); + } +} + +bool NonEphemeralConversionFailure::diagnosePointerInit() const { + auto *constructor = dyn_cast_or_null(getCallee()); + if (!constructor) + return false; + + auto constructedTy = getFnType()->getResult(); + + // Strip off a level of optionality if we have a failable initializer. + if (constructor->isFailable()) + constructedTy = constructedTy->getOptionalObjectType(); + + // This must stay in sync with diag::cannot_construct_dangling_pointer. + enum ConstructorKind { + CK_Pointer = 0, + CK_BufferPointer, + }; + + // Consider OpaquePointer as well as the other kinds of pointers. + auto isConstructingPointer = + constructedTy->getAnyPointerElementType() || + constructedTy->getAnyNominal() == getASTContext().getOpaquePointerDecl(); + + ConstructorKind constructorKind; + auto parameterCount = constructor->getParameters()->size(); + if (isConstructingPointer && parameterCount == 1) { + constructorKind = CK_Pointer; + } else if (constructedTy->getAnyBufferPointerElementType() && + parameterCount == 2) { + constructorKind = CK_BufferPointer; + } else { + return false; + } + + auto diagID = DowngradeToWarning + ? diag::cannot_construct_dangling_pointer_warning + : diag::cannot_construct_dangling_pointer; + + auto *anchor = getRawAnchor(); + emitDiagnostic(anchor->getLoc(), diagID, constructedTy, constructorKind) + .highlight(anchor->getSourceRange()); + + emitSuggestionNotes(); + return true; +} + +bool NonEphemeralConversionFailure::diagnoseAsNote() { + // We can only emit a useful note if we have a callee. + if (auto *callee = getCallee()) { + emitDiagnostic(callee, diag::candidate_performs_illegal_ephemeral_conv, + getParamPosition()); + return true; + } + return false; +} + +bool NonEphemeralConversionFailure::diagnoseAsError() { + // Emit a specialized diagnostic for + // Unsafe[Mutable][Raw]Pointer.init([mutating]:) & + // Unsafe[Mutable][Raw]BufferPointer.init(start:count:). + if (diagnosePointerInit()) + return true; + + // Otherwise, emit a more general diagnostic. + SmallString<8> scratch; + auto argDesc = getArgDescription(scratch); + + auto *argExpr = getArgExpr(); + if (isa(argExpr)) { + auto diagID = DowngradeToWarning + ? diag::cannot_use_inout_non_ephemeral_warning + : diag::cannot_use_inout_non_ephemeral; + + emitDiagnostic(argExpr->getLoc(), diagID, argDesc, getCallee(), + getCalleeFullName()) + .highlight(argExpr->getSourceRange()); + } else { + auto diagID = DowngradeToWarning + ? diag::cannot_pass_type_to_non_ephemeral_warning + : diag::cannot_pass_type_to_non_ephemeral; + + emitDiagnostic(argExpr->getLoc(), diagID, getArgType(), argDesc, + getCallee(), getCalleeFullName()) + .highlight(argExpr->getSourceRange()); + } + emitSuggestionNotes(); + return true; +} + +bool AssignmentTypeMismatchFailure::diagnoseMissingConformance() const { + auto srcType = getFromType(); + auto dstType = getToType()->lookThroughAllOptionalTypes(); + + llvm::SmallPtrSet srcMembers; + llvm::SmallPtrSet dstMembers; + + auto retrieveProtocols = [](Type type, + llvm::SmallPtrSetImpl &members) { + if (auto *protocol = type->getAs()) + members.insert(protocol->getDecl()); + + if (auto *composition = type->getAs()) { + for (auto member : composition->getMembers()) { + if (auto *protocol = member->getAs()) + members.insert(protocol->getDecl()); + } + } + }; + + retrieveProtocols(srcType, srcMembers); + retrieveProtocols(dstType, dstMembers); + + if (srcMembers.empty() || dstMembers.empty()) + return false; + + // Let's check whether there is an overlap between source and destination. + for (auto *member : srcMembers) + dstMembers.erase(member); + + if (dstMembers.size() == 1) + dstType = (*dstMembers.begin())->getDeclaredType(); + + auto *anchor = getAnchor(); + emitDiagnostic(anchor->getLoc(), diag::cannot_convert_assign_protocol, + srcType, dstType); + return true; +} + +bool AssignmentTypeMismatchFailure::diagnoseAsError() { + if (diagnoseMissingConformance()) + return true; + + return ContextualFailure::diagnoseAsError(); +} + +bool AssignmentTypeMismatchFailure::diagnoseAsNote() { + auto *anchor = getAnchor(); + auto &cs = getConstraintSystem(); + + if (auto overload = getChoiceFor(cs.getConstraintLocator(anchor))) { + if (auto *decl = overload->choice.getDeclOrNull()) { + emitDiagnostic(decl, + diag::cannot_convert_candidate_result_to_contextual_type, + decl->getFullName(), getFromType(), getToType()); + return true; + } + } + + return false; +} + +bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { + auto *anchor = getAnchor(); + auto &cs = getConstraintSystem(); + + // Member reference could be wrapped into a number of parens + // e.g. `((.foo))`. + auto *parentExpr = findParentExpr(anchor); + do { + // If we have found something which isn't a paren let's stop, + // otherwise let's keep unwrapping until there are either no + // more parens or no more parents... + if (!parentExpr || !isa(parentExpr)) + break; + } while ((parentExpr = findParentExpr(parentExpr))); + + auto diagnostic = parentExpr || cs.getContextualType(anchor) + ? diag::cannot_infer_base_of_unresolved_member + : diag::unresolved_member_no_inference; + + emitDiagnostic(anchor->getLoc(), diagnostic, MemberName) + .highlight(anchor->getSourceRange()); + return true; +} + +bool UnableToInferClosureReturnType::diagnoseAsError() { + auto *closure = cast(getRawAnchor()); + + auto diagnostic = + emitDiagnostic(closure->getLoc(), + diag::cannot_infer_closure_result_type, + closure->hasSingleExpressionBody()); + + // If there is a location for an 'in' token, then the argument list was + // specified somehow but no return type was. Insert a "-> ReturnType " + // before the in token. + if (closure->getInLoc().isValid()) { + diagnostic.fixItInsert(closure->getInLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/false); + } else if (closure->getParameters()->size() == 0) { + // Otherwise, the closure must take zero arguments. + // + // As such, we insert " () -> ReturnType in " right after the '{' that + // starts the closure body. + diagnostic.fixItInsertAfter(closure->getBody()->getLBraceLoc(), + diag::insert_closure_return_type_placeholder, + /*argListSpecified=*/true); + } + + return true; +} + +static std::pair +getImportModuleAndDefaultType(const ASTContext &ctx, ObjectLiteralExpr *expr) { + const auto &target = ctx.LangOpts.Target; + + switch (expr->getLiteralKind()) { + case ObjectLiteralExpr::colorLiteral: { + if (target.isMacOSX()) { + return std::make_pair("AppKit", "NSColor"); + } else if (target.isiOS() || target.isTvOS()) { + return std::make_pair("UIKit", "UIColor"); + } + break; + } + + case ObjectLiteralExpr::imageLiteral: { + if (target.isMacOSX()) { + return std::make_pair("AppKit", "NSImage"); + } else if (target.isiOS() || target.isTvOS()) { + return std::make_pair("UIKit", "UIImage"); + } + break; + } + + case ObjectLiteralExpr::fileLiteral: { + return std::make_pair("Foundation", "URL"); + } + } + + return std::make_pair("", ""); +} + +bool UnableToInferProtocolLiteralType::diagnoseAsError() { + auto &cs = getConstraintSystem(); + auto &ctx = cs.getASTContext(); + auto *expr = cast(getLocator()->getAnchor()); + + StringRef importModule; + StringRef importDefaultTypeName; + std::tie(importModule, importDefaultTypeName) = + getImportModuleAndDefaultType(ctx, expr); + + auto plainName = expr->getLiteralKindPlainName(); + emitDiagnostic(expr->getLoc(), diag::object_literal_default_type_missing, + plainName); + if (!importModule.empty()) { + emitDiagnostic(expr->getLoc(), diag::object_literal_resolve_import, + importModule, importDefaultTypeName, plainName); + } + + return true; +} + +bool MissingQuialifierInMemberRefFailure::diagnoseAsError() { + auto selectedOverload = getOverloadChoiceIfAvailable(getLocator()); + if (!selectedOverload) + return false; + + auto *UDE = cast(getRawAnchor()); + + auto baseType = getType(UDE->getBase()); + + auto methodKind = baseType->isAnyExistentialType() + ? DescriptiveDeclKind::StaticMethod + : DescriptiveDeclKind::Method; + + auto choice = selectedOverload->choice.getDeclOrNull(); + if (!choice) + return false; + + auto *DC = choice->getDeclContext(); + if (!(DC->isModuleContext() || DC->isModuleScopeContext())) { + emitDiagnostic(UDE->getLoc(), diag::member_shadows_function, UDE->getName(), + methodKind, choice->getDescriptiveKind(), + choice->getFullName()); + return true; + } + + auto qualifier = DC->getParentModule()->getName(); + + emitDiagnostic(UDE->getLoc(), diag::member_shadows_global_function, + UDE->getName(), methodKind, choice->getDescriptiveKind(), + choice->getFullName(), qualifier); + + SmallString<32> namePlusDot = qualifier.str(); + namePlusDot.push_back('.'); + + emitDiagnostic(UDE->getLoc(), diag::fix_unqualified_access_top_level_multi, + namePlusDot, choice->getDescriptiveKind(), qualifier) + .fixItInsert(UDE->getStartLoc(), namePlusDot); + + emitDiagnostic(choice, diag::decl_declared_here, choice->getFullName()); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index e90485b351e22..2f3c878a40777 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -38,7 +38,6 @@ class FunctionArgApplyInfo; /// provides most basic information such as location of /// the problem, parent expression and some utility methods. class FailureDiagnostic { - Expr *E; ConstraintSystem &CS; ConstraintLocator *Locator; @@ -51,9 +50,8 @@ class FailureDiagnostic { bool HasComplexLocator; public: - FailureDiagnostic(Expr *expr, ConstraintSystem &cs, - ConstraintLocator *locator) - : E(expr), CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) { + FailureDiagnostic(ConstraintSystem &cs, ConstraintLocator *locator) + : CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) { std::tie(Anchor, HasComplexLocator) = computeAnchor(); } @@ -84,35 +82,41 @@ class FailureDiagnostic { return CS; } - Expr *getParentExpr() const { return E; } - Expr *getRawAnchor() const { return RawAnchor; } Expr *getAnchor() const { return Anchor; } ConstraintLocator *getLocator() const { return Locator; } - Type getType(Expr *expr) const; - Type getType(const TypeLoc &loc) const; + Type getType(Expr *expr, bool wantRValue = true) const; + Type getType(const TypeLoc &loc, bool wantRValue = true) const; /// Resolve type variables present in the raw type, if any. - Type resolveType(Type rawType, bool reconstituteSugar = false) const { - auto resolvedType = CS.simplifyType(rawType); - return reconstituteSugar - ? resolvedType->reconstituteSugar(/*recursive*/ true) - : resolvedType; - } + Type resolveType(Type rawType, bool reconstituteSugar = false, + bool wantRValue = true) const { + if (!rawType->hasTypeVariable()) { + if (reconstituteSugar) + rawType = rawType->reconstituteSugar(/*recursive*/ true); + return wantRValue ? rawType->getRValueType() : rawType; + } - /// Resolve type variables present in the raw type, using generic parameter - /// types where possible. - Type resolveInterfaceType(Type type, bool reconstituteSugar = false) const; + auto &cs = getConstraintSystem(); + return cs.simplifyTypeImpl(rawType, + [&](TypeVariableType *typeVar) -> Type { + if (auto fixed = cs.getFixedType(typeVar)) { + auto *genericParam = typeVar->getImpl().getGenericParameter(); + if (fixed->isHole() && genericParam) + return genericParam; + return resolveType(fixed, reconstituteSugar, wantRValue); + } + return cs.getRepresentative(typeVar); + }); + } template InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const; protected: - TypeChecker &getTypeChecker() const { return CS.TC; } - DeclContext *getDC() const { return CS.DC; } ASTContext &getASTContext() const { return CS.getASTContext(); } @@ -132,18 +136,10 @@ class FailureDiagnostic { return CS.findResolvedMemberRef(locator); } + /// Retrieve overload choice resolved for a given locator + /// by the constraint solver. Optional getOverloadChoiceIfAvailable(ConstraintLocator *locator) const { - if (auto *overload = getResolvedOverload(locator)) - return Optional( - {overload->Choice, overload->OpenedFullType, overload->ImpliedType}); - return None; - } - - /// Retrieve overload choice resolved for given locator - /// by the constraint solver. - ResolvedOverloadSetListItem * - getResolvedOverload(ConstraintLocator *locator) const { return CS.findSelectedOverloadFor(locator); } @@ -176,13 +172,6 @@ class FailureDiagnostic { /// of a given locator's anchor, or \c None if no such choice can be found. Optional getChoiceFor(ConstraintLocator *) const; - /// For a given locator describing a function argument conversion, or a - /// constraint within an argument conversion, returns information about the - /// application of the argument to its parameter. If the locator is not - /// for an argument conversion, returns \c None. - Optional - getFunctionArgApplyInfo(ConstraintLocator *locator) const; - /// \returns A new type with all of the type variables associated with /// generic parameters substituted back into being generic parameter type. Type restoreGenericParameters( @@ -195,101 +184,6 @@ class FailureDiagnostic { std::pair computeAnchor() const; }; -/// Provides information about the application of a function argument to a -/// parameter. -class FunctionArgApplyInfo { - Expr *ArgExpr; - unsigned ArgIdx; - Type ArgType; - - unsigned ParamIdx; - - Type FnInterfaceType; - FunctionType *FnType; - const ValueDecl *Callee; - -public: - FunctionArgApplyInfo(Expr *argExpr, unsigned argIdx, Type argType, - unsigned paramIdx, Type fnInterfaceType, - FunctionType *fnType, const ValueDecl *callee) - : ArgExpr(argExpr), ArgIdx(argIdx), ArgType(argType), ParamIdx(paramIdx), - FnInterfaceType(fnInterfaceType), FnType(fnType), Callee(callee) {} - - /// \returns The argument being applied. - Expr *getArgExpr() const { return ArgExpr; } - - /// \returns The position of the argument, starting at 1. - unsigned getArgPosition() const { return ArgIdx + 1; } - - /// \returns The position of the parameter, starting at 1. - unsigned getParamPosition() const { return ParamIdx + 1; } - - /// \returns The type of the argument being applied, including any generic - /// substitutions. - /// - /// \param withSpecifier Whether to keep the inout or @lvalue specifier of - /// the argument, if any. - Type getArgType(bool withSpecifier = false) const { - return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType(); - } - - /// \returns The interface type for the function being applied. Note that this - /// may not a function type, for example it could be a generic parameter. - Type getFnInterfaceType() const { return FnInterfaceType; } - - /// \returns The function type being applied, including any generic - /// substitutions. - FunctionType *getFnType() const { return FnType; } - - /// \returns The callee for the application. - const ValueDecl *getCallee() const { return Callee; } - -private: - Type getParamTypeImpl(AnyFunctionType *fnTy, - bool lookThroughAutoclosure) const { - auto param = fnTy->getParams()[ParamIdx]; - auto paramTy = param.getPlainType(); - if (lookThroughAutoclosure && param.isAutoClosure()) - paramTy = paramTy->castTo()->getResult(); - return paramTy; - } - -public: - /// \returns The type of the parameter which the argument is being applied to, - /// including any generic substitutions. - /// - /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter - /// should be treated as being of type T. - Type getParamType(bool lookThroughAutoclosure = true) const { - return getParamTypeImpl(FnType, lookThroughAutoclosure); - } - - /// \returns The interface type of the parameter which the argument is being - /// applied to. - /// - /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter - /// should be treated as being of type T. - Type getParamInterfaceType(bool lookThroughAutoclosure = true) const { - auto interfaceFnTy = FnInterfaceType->getAs(); - if (!interfaceFnTy) { - // If the interface type isn't a function, then just return the resolved - // parameter type. - return getParamType(lookThroughAutoclosure)->mapTypeOutOfContext(); - } - return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure); - } - - /// \returns The flags of the parameter which the argument is being applied - /// to. - ParameterTypeFlags getParameterFlags() const { - return FnType->getParams()[ParamIdx].getParameterFlags(); - } - - ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const { - return FnType->getParams()[idx].getParameterFlags(); - } -}; - /// Base class for all of the diagnostics related to generic requirement /// failures, provides common information like failed requirement, /// declaration where such requirement comes from, etc. @@ -320,9 +214,9 @@ class RequirementFailure : public FailureDiagnostic { Type LHS, RHS; public: - RequirementFailure(ConstraintSystem &cs, Expr *expr, RequirementKind kind, - Type lhs, Type rhs, ConstraintLocator *locator) - : FailureDiagnostic(expr, cs, locator), + RequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), Conformance(getConformanceForConditionalReq(locator)), Signature(getSignature(locator)), AffectedDecl(getDeclRef()), LHS(resolveType(lhs)), RHS(resolveType(rhs)) { @@ -334,13 +228,6 @@ class RequirementFailure : public FailureDiagnostic { assert(getGenericContext() && "Affected decl not within a generic context?"); - auto reqElt = locator->castLastElementTo(); - assert(reqElt.getRequirementKind() == kind); - - // It's possible sometimes not to have no base expression. - if (!expr) - return; - if (auto *parentExpr = findParentExpr(getRawAnchor())) Apply = dyn_cast(parentExpr); } @@ -415,11 +302,15 @@ class RequirementFailure : public FailureDiagnostic { /// ``` class MissingConformanceFailure final : public RequirementFailure { public: - MissingConformanceFailure(Expr *expr, ConstraintSystem &cs, + MissingConformanceFailure(ConstraintSystem &cs, ConstraintLocator *locator, std::pair conformance) - : RequirementFailure(cs, expr, RequirementKind::Conformance, - conformance.first, conformance.second, locator) {} + : RequirementFailure(cs, conformance.first, conformance.second, + locator) { + auto reqElt = locator->castLastElementTo(); + assert(reqElt.getRequirementKind() == RequirementKind::Conformance || + reqElt.getRequirementKind() == RequirementKind::Layout); + } bool diagnoseAsError() override; @@ -433,11 +324,15 @@ class MissingConformanceFailure final : public RequirementFailure { bool diagnoseAsAmbiguousOperatorRef(); DiagOnDecl getDiagnosticOnDecl() const override { - return diag::type_does_not_conform_decl_owner; + return (getRequirement().getKind() == RequirementKind::Layout ? + diag::type_does_not_conform_anyobject_decl_owner : + diag::type_does_not_conform_decl_owner); } DiagInReference getDiagnosticInRereference() const override { - return diag::type_does_not_conform_in_decl_ref; + return (getRequirement().getKind() == RequirementKind::Layout ? + diag::type_does_not_conform_anyobject_in_decl_ref : + diag::type_does_not_conform_in_decl_ref); } DiagAsNote getDiagnosticAsNote() const override { @@ -466,10 +361,12 @@ class MissingConformanceFailure final : public RequirementFailure { /// `S.T` is not the same type as `Int`, which is required by `foo`. class SameTypeRequirementFailure final : public RequirementFailure { public: - SameTypeRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs, + SameTypeRequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : RequirementFailure(cs, expr, RequirementKind::SameType, lhs, rhs, - locator) {} + : RequirementFailure(cs, lhs, rhs, locator) { + auto reqElt = locator->castLastElementTo(); + assert(reqElt.getRequirementKind() == RequirementKind::SameType); + } protected: DiagOnDecl getDiagnosticOnDecl() const override { @@ -500,10 +397,12 @@ class SameTypeRequirementFailure final : public RequirementFailure { /// `A` is not the superclass of `B`, which is required by `foo`. class SuperclassRequirementFailure final : public RequirementFailure { public: - SuperclassRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs, + SuperclassRequirementFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : RequirementFailure(cs, expr, RequirementKind::Superclass, lhs, rhs, - locator) {} + : RequirementFailure(cs, lhs, rhs, locator) { + auto reqElt = locator->castLastElementTo(); + assert(reqElt.getRequirementKind() == RequirementKind::Superclass); + } protected: DiagOnDecl getDiagnosticOnDecl() const override { @@ -531,107 +430,50 @@ class LabelingFailure final : public FailureDiagnostic { ArrayRef CorrectLabels; public: - LabelingFailure(Expr *root, ConstraintSystem &cs, ConstraintLocator *locator, + LabelingFailure(ConstraintSystem &cs, ConstraintLocator *locator, ArrayRef labels) - : FailureDiagnostic(root, cs, locator), CorrectLabels(labels) {} + : FailureDiagnostic(cs, locator), CorrectLabels(labels) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; }; -/// Diagnose errors related to converting function type which -/// isn't explicitly '@escaping' to some other type. -class NoEscapeFuncToTypeConversionFailure final : public FailureDiagnostic { - Type ConvertTo; - -public: - NoEscapeFuncToTypeConversionFailure(Expr *expr, ConstraintSystem &cs, - ConstraintLocator *locator, - Type toType = Type()) - : FailureDiagnostic(expr, cs, locator), ConvertTo(toType) {} - - bool diagnoseAsError() override; - -private: - /// Emit tailored diagnostics for no-escape parameter conversions e.g. - /// passing such parameter as an @escaping argument, or trying to - /// assign it to a variable which expects @escaping function. - bool diagnoseParameterUse() const; -}; - -class MissingForcedDowncastFailure final : public FailureDiagnostic { -public: - MissingForcedDowncastFailure(Expr *expr, ConstraintSystem &cs, - ConstraintLocator *locator) - : FailureDiagnostic(expr, cs, locator) {} - - bool diagnoseAsError() override; -}; - /// Diagnose failures related to attempting member access on optional base /// type without optional chaining or force-unwrapping it first. class MemberAccessOnOptionalBaseFailure final : public FailureDiagnostic { - DeclName Member; + DeclNameRef Member; bool ResultTypeIsOptional; public: - MemberAccessOnOptionalBaseFailure(Expr *expr, ConstraintSystem &cs, + MemberAccessOnOptionalBaseFailure(ConstraintSystem &cs, ConstraintLocator *locator, - DeclName memberName, bool resultOptional) - : FailureDiagnostic(expr, cs, locator), Member(memberName), + DeclNameRef memberName, bool resultOptional) + : FailureDiagnostic(cs, locator), Member(memberName), ResultTypeIsOptional(resultOptional) {} bool diagnoseAsError() override; }; -/// Diagnose failures related to use of the unwrapped optional types, -/// which require some type of force-unwrap e.g. "!" or "try!". -class MissingOptionalUnwrapFailure final : public FailureDiagnostic { - Type BaseType; - Type UnwrappedType; - -public: - MissingOptionalUnwrapFailure(Expr *expr, ConstraintSystem &cs, Type baseType, - Type unwrappedType, ConstraintLocator *locator) - : FailureDiagnostic(expr, cs, locator), BaseType(baseType), - UnwrappedType(unwrappedType) {} - - bool diagnoseAsError() override; - -private: - Type getBaseType() const { - return resolveType(BaseType, /*reconstituteSugar=*/true); - } - - Type getUnwrappedType() const { - return resolveType(UnwrappedType, /*reconstituteSugar=*/true); - } - - /// Suggest a default value via `?? ` - void offerDefaultValueUnwrapFixIt(DeclContext *DC, Expr *expr) const; - /// Suggest a force optional unwrap via `!` - void offerForceUnwrapFixIt(Expr *expr) const; -}; - /// Diagnose errors associated with rvalues in positions /// where an lvalue is required, such as inout arguments. class RValueTreatedAsLValueFailure final : public FailureDiagnostic { public: RValueTreatedAsLValueFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(nullptr, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; + bool diagnoseAsNote() override; }; class TrailingClosureAmbiguityFailure final : public FailureDiagnostic { ArrayRef Choices; public: - TrailingClosureAmbiguityFailure(Expr *root, ConstraintSystem &cs, + TrailingClosureAmbiguityFailure(ConstraintSystem &cs, Expr *anchor, ArrayRef choices) - : FailureDiagnostic(root, cs, cs.getConstraintLocator(anchor)), + : FailureDiagnostic(cs, cs.getConstraintLocator(anchor)), Choices(choices) {} bool diagnoseAsError() override { return false; } @@ -643,6 +485,7 @@ class TrailingClosureAmbiguityFailure final : public FailureDiagnostic { /// trying to assign something to immutable value, or trying /// to access mutating member on immutable base. class AssignmentFailure final : public FailureDiagnostic { + Expr *DestExpr; SourceLoc Loc; Diag DeclDiagnostic; Diag TypeDiagnostic; @@ -654,8 +497,9 @@ class AssignmentFailure final : public FailureDiagnostic { AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, SourceLoc diagnosticLoc, Diag declDiag, Diag typeDiag) - : FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)), - Loc(diagnosticLoc), DeclDiagnostic(declDiag), TypeDiagnostic(typeDiag) { + : FailureDiagnostic(cs, cs.getConstraintLocator(destExpr)), + DestExpr(destExpr), Loc(diagnosticLoc), DeclDiagnostic(declDiag), + TypeDiagnostic(typeDiag) { } bool diagnoseAsError() override; @@ -683,32 +527,40 @@ class AssignmentFailure final : public FailureDiagnostic { /// e.g. argument/parameter, closure result, conversions etc. class ContextualFailure : public FailureDiagnostic { ContextualTypePurpose CTP; - Type FromType, ToType; + Type RawFromType, RawToType; public: - ContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs, + ContextualFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ContextualFailure(root, cs, cs.getContextualTypePurpose(), lhs, rhs, - locator) {} + : ContextualFailure(cs, + cs.getContextualTypePurpose(locator->getAnchor()), + lhs, rhs, locator) {} - ContextualFailure(Expr *root, ConstraintSystem &cs, - ContextualTypePurpose purpose, Type lhs, Type rhs, - ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), CTP(purpose), - FromType(resolve(lhs)->getRValueType()), - ToType(resolve(rhs)->getRValueType()) {} + ContextualFailure(ConstraintSystem &cs, ContextualTypePurpose purpose, + Type lhs, Type rhs, ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), CTP(purpose), RawFromType(lhs), + RawToType(rhs) {} - Type getFromType() const { return FromType; } + Type getFromType() const { return resolve(RawFromType); } - Type getToType() const { return ToType; } + Type getToType() const { return resolve(RawToType); } + + Type getRawFromType() const { return RawFromType; } + + Type getRawToType() const { return RawToType; } bool diagnoseAsError() override; + bool diagnoseAsNote() override; + /// If we're trying to convert something to `nil`. bool diagnoseConversionToNil() const; + + /// Diagnose failed conversion in a `CoerceExpr`. + bool diagnoseCoercionToUnrelatedType() const; - // If we're trying to convert something of type "() -> T" to T, - // then we probably meant to call the value. + /// If we're trying to convert something of type "() -> T" to T, + /// then we probably meant to call the value. bool diagnoseMissingFunctionCall() const; /// Produce a specialized diagnostic if this is an invalid conversion to Bool. @@ -779,13 +631,8 @@ class ContextualFailure : public FailureDiagnostic { Type contextualType); private: - Type resolve(Type rawType) { - auto type = resolveType(rawType)->getWithoutSpecifierType(); - if (auto *BGT = type->getAs()) { - if (BGT->hasUnresolvedType()) - return BGT->getDecl()->getDeclaredInterfaceType(); - } - return type; + Type resolve(Type rawType) const { + return resolveType(rawType)->getWithoutSpecifierType(); } /// Try to add a fix-it to convert a stored property into a computed @@ -806,7 +653,50 @@ class ContextualFailure : public FailureDiagnostic { ContextualTypePurpose getContextualTypePurpose() const { return CTP; } static Optional> - getDiagnosticFor(ContextualTypePurpose context, bool forProtocol); + getDiagnosticFor(ContextualTypePurpose context, Type contextualType); +}; + +/// Diagnose errors related to converting function type which +/// isn't explicitly '@escaping' to some other type. +class NoEscapeFuncToTypeConversionFailure final : public ContextualFailure { +public: + NoEscapeFuncToTypeConversionFailure(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) + : ContextualFailure(cs, fromType, toType, locator) {} + + bool diagnoseAsError() override; + +private: + /// Emit tailored diagnostics for no-escape parameter conversions e.g. + /// passing such parameter as an @escaping argument, or trying to + /// assign it to a variable which expects @escaping function. + bool diagnoseParameterUse() const; +}; + +/// Diagnose failures related to use of the unwrapped optional types, +/// which require some type of force-unwrap e.g. "!" or "try!". +class MissingOptionalUnwrapFailure final : public ContextualFailure { +public: + MissingOptionalUnwrapFailure(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintLocator *locator) + : ContextualFailure(cs, fromType, toType, locator) {} + + bool diagnoseAsError() override; + +private: + Type getBaseType() const { + return resolveType(getFromType(), /*reconstituteSugar=*/true); + } + + Type getUnwrappedType() const { + return resolveType(getBaseType()->getOptionalObjectType(), + /*reconstituteSugar=*/true); + } + + /// Suggest a default value via `?? ` + void offerDefaultValueUnwrapFixIt(DeclContext *DC, Expr *expr) const; + /// Suggest a force optional unwrap via `!` + void offerForceUnwrapFixIt(Expr *expr) const; }; /// Diagnostics for mismatched generic arguments e.g @@ -818,11 +708,11 @@ class GenericArgumentsMismatchFailure final : public ContextualFailure { ArrayRef Mismatches; public: - GenericArgumentsMismatchFailure(Expr *expr, ConstraintSystem &cs, + GenericArgumentsMismatchFailure(ConstraintSystem &cs, Type actualType, Type requiredType, ArrayRef mismatches, ConstraintLocator *locator) - : ContextualFailure(expr, cs, actualType, requiredType, locator), + : ContextualFailure(cs, actualType, requiredType, locator), Mismatches(mismatches) { assert(actualType->is()); assert(requiredType->is()); @@ -862,10 +752,10 @@ class GenericArgumentsMismatchFailure final : public ContextualFailure { /// ``` class ThrowingFunctionConversionFailure final : public ContextualFailure { public: - ThrowingFunctionConversionFailure(Expr *root, ConstraintSystem &cs, + ThrowingFunctionConversionFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) - : ContextualFailure(root, cs, fromType, toType, locator) { + : ContextualFailure(cs, fromType, toType, locator) { auto fnType1 = fromType->castTo(); auto fnType2 = toType->castTo(); assert(fnType1->throws() != fnType2->throws()); @@ -879,36 +769,31 @@ class ThrowingFunctionConversionFailure final : public ContextualFailure { /// "as" or "as!" has to be specified explicitly in cases like that. class MissingExplicitConversionFailure final : public ContextualFailure { public: - MissingExplicitConversionFailure(Expr *expr, ConstraintSystem &cs, + MissingExplicitConversionFailure(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator) - : ContextualFailure(expr, cs, fromType, toType, locator) {} + : ContextualFailure(cs, fromType, toType, locator) {} bool diagnoseAsError() override; private: bool exprNeedsParensBeforeAddingAs(Expr *expr) { auto *DC = getDC(); - auto &TC = getTypeChecker(); - auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; - return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG); + return exprNeedsParensInsideFollowingOperator(DC, expr, asPG); } bool exprNeedsParensAfterAddingAs(Expr *expr, Expr *rootExpr) { auto *DC = getDC(); - auto &TC = getTypeChecker(); - auto asPG = TypeChecker::lookupPrecedenceGroup( DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); if (!asPG) return true; - return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr, - asPG); + return exprNeedsParensOutsideFollowingOperator(DC, expr, rootExpr, asPG); } }; @@ -916,9 +801,9 @@ class MissingExplicitConversionFailure final : public ContextualFailure { /// to `inout` or pointer parameter, without explicitly specifying `&`. class MissingAddressOfFailure final : public ContextualFailure { public: - MissingAddressOfFailure(Expr *expr, ConstraintSystem &cs, Type argTy, + MissingAddressOfFailure(ConstraintSystem &cs, Type argTy, Type paramTy, ConstraintLocator *locator) - : ContextualFailure(expr, cs, argTy, paramTy, locator) {} + : ContextualFailure(cs, argTy, paramTy, locator) {} bool diagnoseAsError() override; }; @@ -936,9 +821,9 @@ class MissingAddressOfFailure final : public ContextualFailure { /// ``` class InvalidUseOfAddressOf final : public ContextualFailure { public: - InvalidUseOfAddressOf(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs, + InvalidUseOfAddressOf(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ContextualFailure(root, cs, lhs, rhs, locator) {} + : ContextualFailure(cs, lhs, rhs, locator) {} bool diagnoseAsError() override; @@ -949,27 +834,51 @@ class InvalidUseOfAddressOf final : public ContextualFailure { /// Diagnose mismatches relating to tuple destructuring. class TupleContextualFailure final : public ContextualFailure { + /// Indices of the tuple elements whose types do not match. + llvm::SmallVector Indices; + public: - TupleContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs, + TupleContextualFailure(ConstraintSystem &cs, ContextualTypePurpose purpose, + Type lhs, Type rhs, llvm::ArrayRef indices, ConstraintLocator *locator) - : ContextualFailure(root, cs, lhs, rhs, locator) {} + : ContextualFailure(cs, purpose, lhs, rhs, locator), + Indices(indices.begin(), indices.end()) { + std::sort(Indices.begin(), Indices.end()); + assert(getFromType()->is() && getToType()->is()); + } bool diagnoseAsError() override; bool isNumElementsMismatch() const { auto lhsTy = getFromType()->castTo(); auto rhsTy = getToType()->castTo(); - assert(lhsTy && rhsTy); return lhsTy->getNumElements() != rhsTy->getNumElements(); } }; +class FunctionTypeMismatch final : public ContextualFailure { + /// Indices of the parameters whose types do not match. + llvm::SmallVector Indices; + +public: + FunctionTypeMismatch(ConstraintSystem &cs, ContextualTypePurpose purpose, + Type lhs, Type rhs, llvm::ArrayRef indices, + ConstraintLocator *locator) + : ContextualFailure(cs, purpose, lhs, rhs, locator), + Indices(indices.begin(), indices.end()) { + std::sort(Indices.begin(), Indices.end()); + assert(getFromType()->is() && getToType()->is()); + } + + bool diagnoseAsError() override; +}; + /// Diagnose situations when @autoclosure argument is passed to @autoclosure /// parameter directly without calling it first. class AutoClosureForwardingFailure final : public FailureDiagnostic { public: AutoClosureForwardingFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(nullptr, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; @@ -984,10 +893,10 @@ class AutoClosureForwardingFailure final : public FailureDiagnostic { /// \endcode class AutoClosurePointerConversionFailure final : public ContextualFailure { public: - AutoClosurePointerConversionFailure(Expr *root, ConstraintSystem &cs, + AutoClosurePointerConversionFailure(ConstraintSystem &cs, Type pointeeType, Type pointerType, ConstraintLocator *locator) - : ContextualFailure(root, cs, pointeeType, pointerType, locator) {} + : ContextualFailure(cs, pointeeType, pointerType, locator) {} bool diagnoseAsError() override; }; @@ -1008,18 +917,18 @@ class NonOptionalUnwrapFailure final : public FailureDiagnostic { Type BaseType; public: - NonOptionalUnwrapFailure(Expr *root, ConstraintSystem &cs, Type baseType, + NonOptionalUnwrapFailure(ConstraintSystem &cs, Type baseType, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), BaseType(baseType) {} + : FailureDiagnostic(cs, locator), BaseType(baseType) {} bool diagnoseAsError() override; }; class MissingCallFailure final : public FailureDiagnostic { public: - MissingCallFailure(Expr *root, ConstraintSystem &cs, + MissingCallFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; @@ -1029,11 +938,11 @@ class PropertyWrapperReferenceFailure : public ContextualFailure { bool UsingStorageWrapper; public: - PropertyWrapperReferenceFailure(Expr *root, ConstraintSystem &cs, + PropertyWrapperReferenceFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) - : ContextualFailure(root, cs, base, wrapper, locator), Property(property), + : ContextualFailure(cs, base, wrapper, locator), Property(property), UsingStorageWrapper(usingStorageWrapper) {} VarDecl *getProperty() const { return Property; } @@ -1053,12 +962,12 @@ class PropertyWrapperReferenceFailure : public ContextualFailure { class ExtraneousPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: - ExtraneousPropertyWrapperUnwrapFailure(Expr *root, ConstraintSystem &cs, + ExtraneousPropertyWrapperUnwrapFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) - : PropertyWrapperReferenceFailure(root, cs, property, usingStorageWrapper, + : PropertyWrapperReferenceFailure(cs, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; @@ -1067,11 +976,11 @@ class ExtraneousPropertyWrapperUnwrapFailure final class MissingPropertyWrapperUnwrapFailure final : public PropertyWrapperReferenceFailure { public: - MissingPropertyWrapperUnwrapFailure(Expr *root, ConstraintSystem &cs, + MissingPropertyWrapperUnwrapFailure(ConstraintSystem &cs, VarDecl *property, bool usingStorageWrapper, Type base, Type wrapper, ConstraintLocator *locator) - : PropertyWrapperReferenceFailure(root, cs, property, usingStorageWrapper, + : PropertyWrapperReferenceFailure(cs, property, usingStorageWrapper, base, wrapper, locator) {} bool diagnoseAsError() override; @@ -1079,9 +988,9 @@ class MissingPropertyWrapperUnwrapFailure final class SubscriptMisuseFailure final : public FailureDiagnostic { public: - SubscriptMisuseFailure(Expr *root, ConstraintSystem &cs, + SubscriptMisuseFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; @@ -1089,17 +998,17 @@ class SubscriptMisuseFailure final : public FailureDiagnostic { class InvalidMemberRefFailure : public FailureDiagnostic { Type BaseType; - DeclName Name; + DeclNameRef Name; public: - InvalidMemberRefFailure(Expr *root, ConstraintSystem &cs, Type baseType, - DeclName memberName, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), BaseType(baseType->getRValueType()), + InvalidMemberRefFailure(ConstraintSystem &cs, Type baseType, + DeclNameRef memberName, ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), BaseType(baseType->getRValueType()), Name(memberName) {} protected: Type getBaseType() const { return BaseType; } - DeclName getName() const { return Name; } + DeclNameRef getName() const { return Name; } }; /// Diagnose situations when member referenced by name is missing @@ -1113,16 +1022,22 @@ class InvalidMemberRefFailure : public FailureDiagnostic { /// ``` class MissingMemberFailure final : public InvalidMemberRefFailure { public: - MissingMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType, - DeclName memberName, ConstraintLocator *locator) - : InvalidMemberRefFailure(root, cs, baseType, memberName, locator) {} + MissingMemberFailure(ConstraintSystem &cs, Type baseType, + DeclNameRef memberName, ConstraintLocator *locator) + : InvalidMemberRefFailure(cs, baseType, memberName, locator) {} bool diagnoseAsError() override; private: + /// Tailored diagnostics for missing special `@dynamicCallable` methods + /// e.g. if caller expects `dynamicallyCall(withKeywordArguments:)` + /// overload to be present, but a class marked as `@dynamicCallable` + /// defines only `dynamicallyCall(withArguments:)` variant. + bool diagnoseForDynamicCallable() const; + static DeclName findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections, - DeclName memberName); + DeclNameRef memberName); }; /// Diagnose cases where a member only accessible on generic constraints @@ -1140,9 +1055,9 @@ class MissingMemberFailure final : public InvalidMemberRefFailure { /// ``` class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure { public: - InvalidMemberRefOnExistential(Expr *root, ConstraintSystem &cs, Type baseType, - DeclName memberName, ConstraintLocator *locator) - : InvalidMemberRefFailure(root, cs, baseType, memberName, locator) {} + InvalidMemberRefOnExistential(ConstraintSystem &cs, Type baseType, + DeclNameRef memberName, ConstraintLocator *locator) + : InvalidMemberRefFailure(cs, baseType, memberName, locator) {} bool diagnoseAsError() override; }; @@ -1166,13 +1081,13 @@ class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure { class AllowTypeOrInstanceMemberFailure final : public FailureDiagnostic { Type BaseType; ValueDecl *Member; - DeclName Name; + DeclNameRef Name; public: - AllowTypeOrInstanceMemberFailure(Expr *root, ConstraintSystem &cs, + AllowTypeOrInstanceMemberFailure(ConstraintSystem &cs, Type baseType, ValueDecl *member, - DeclName name, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), + DeclNameRef name, ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), BaseType(baseType->getRValueType()), Member(member), Name(name) { assert(member); } @@ -1190,9 +1105,9 @@ class PartialApplicationFailure final : public FailureDiagnostic { bool CompatibilityWarning; public: - PartialApplicationFailure(Expr *root, bool warning, ConstraintSystem &cs, + PartialApplicationFailure(bool warning, ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), CompatibilityWarning(warning) {} + : FailureDiagnostic(cs, locator), CompatibilityWarning(warning) {} bool diagnoseAsError() override; }; @@ -1203,10 +1118,10 @@ class InvalidInitRefFailure : public FailureDiagnostic { const ConstructorDecl *Init; SourceRange BaseRange; - InvalidInitRefFailure(Expr *root, ConstraintSystem &cs, Type baseTy, + InvalidInitRefFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), BaseType(baseTy), Init(init), + : FailureDiagnostic(cs, locator), BaseType(baseTy), Init(init), BaseRange(baseRange) {} public: @@ -1227,11 +1142,11 @@ class InvalidInitRefFailure : public FailureDiagnostic { /// ``` class InvalidDynamicInitOnMetatypeFailure final : public InvalidInitRefFailure { public: - InvalidDynamicInitOnMetatypeFailure(Expr *root, ConstraintSystem &cs, + InvalidDynamicInitOnMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, SourceRange baseRange, ConstraintLocator *locator) - : InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator) {} + : InvalidInitRefFailure(cs, baseTy, init, baseRange, locator) {} bool diagnoseAsError() override; }; @@ -1251,11 +1166,11 @@ class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure { bool IsStaticallyDerived; public: - InitOnProtocolMetatypeFailure(Expr *root, ConstraintSystem &cs, Type baseTy, + InitOnProtocolMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, bool isStaticallyDerived, SourceRange baseRange, ConstraintLocator *locator) - : InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator), + : InvalidInitRefFailure(cs, baseTy, init, baseRange, locator), IsStaticallyDerived(isStaticallyDerived) {} bool diagnoseAsError() override; @@ -1271,11 +1186,11 @@ class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure { class ImplicitInitOnNonConstMetatypeFailure final : public InvalidInitRefFailure { public: - ImplicitInitOnNonConstMetatypeFailure(Expr *root, ConstraintSystem &cs, + ImplicitInitOnNonConstMetatypeFailure(ConstraintSystem &cs, Type baseTy, const ConstructorDecl *init, ConstraintLocator *locator) - : InvalidInitRefFailure(root, cs, baseTy, init, SourceRange(), locator) {} + : InvalidInitRefFailure(cs, baseTy, init, SourceRange(), locator) {} bool diagnoseAsError() override; }; @@ -1286,16 +1201,18 @@ class MissingArgumentsFailure final : public FailureDiagnostic { SmallVector SynthesizedArgs; public: - MissingArgumentsFailure(Expr *root, ConstraintSystem &cs, + MissingArgumentsFailure(ConstraintSystem &cs, ArrayRef synthesizedArgs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), + : FailureDiagnostic(cs, locator), SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) { assert(!SynthesizedArgs.empty() && "No missing arguments?!"); } bool diagnoseAsError() override; + bool diagnoseAsNote() override; + bool diagnoseSingleMissingArgument() const; private: @@ -1338,6 +1255,36 @@ class MissingArgumentsFailure final : public FailureDiagnostic { ConstraintLocator *locator); }; +class ExtraneousArgumentsFailure final : public FailureDiagnostic { + FunctionType *ContextualType; + SmallVector, 4> ExtraArgs; + +public: + ExtraneousArgumentsFailure( + ConstraintSystem &cs, FunctionType *contextualType, + ArrayRef> extraArgs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), + ContextualType(resolveType(contextualType)->castTo()), + ExtraArgs(extraArgs.begin(), extraArgs.end()) {} + + bool diagnoseAsError() override; + bool diagnoseAsNote() override; + +private: + bool diagnoseSingleExtraArgument() const; + + unsigned getTotalNumArguments() const { + return ContextualType->getNumParams() + ExtraArgs.size(); + } + + bool isContextualMismatch() const { + auto *locator = getLocator(); + return locator->isLastElement() || + locator->isLastElement(); + } +}; + class OutOfOrderArgumentFailure final : public FailureDiagnostic { using ParamBinding = SmallVector; @@ -1347,12 +1294,12 @@ class OutOfOrderArgumentFailure final : public FailureDiagnostic { SmallVector Bindings; public: - OutOfOrderArgumentFailure(Expr *root, ConstraintSystem &cs, + OutOfOrderArgumentFailure(ConstraintSystem &cs, unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), ArgIdx(argIdx), + : FailureDiagnostic(cs, locator), ArgIdx(argIdx), PrevArgIdx(prevArgIdx), Bindings(bindings.begin(), bindings.end()) {} bool diagnoseAsError() override; @@ -1368,10 +1315,10 @@ class ClosureParamDestructuringFailure final : public FailureDiagnostic { FunctionType *ContextualType; public: - ClosureParamDestructuringFailure(Expr *root, ConstraintSystem &cs, + ClosureParamDestructuringFailure(ConstraintSystem &cs, FunctionType *contextualType, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), ContextualType(contextualType) {} + : FailureDiagnostic(cs, locator), ContextualType(contextualType) {} bool diagnoseAsError() override; @@ -1398,9 +1345,9 @@ class InaccessibleMemberFailure final : public FailureDiagnostic { ValueDecl *Member; public: - InaccessibleMemberFailure(Expr *root, ConstraintSystem &cs, ValueDecl *member, + InaccessibleMemberFailure(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), Member(member) {} + : FailureDiagnostic(cs, locator), Member(member) {} bool diagnoseAsError() override; }; @@ -1422,25 +1369,25 @@ class MutatingMemberRefOnImmutableBase final : public FailureDiagnostic { ValueDecl *Member; public: - MutatingMemberRefOnImmutableBase(Expr *root, ConstraintSystem &cs, + MutatingMemberRefOnImmutableBase(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), Member(member) {} + : FailureDiagnostic(cs, locator), Member(member) {} bool diagnoseAsError() override; }; -// Diagnose an attempt to use AnyObject as the root type of a KeyPath -// -// ```swift -// let keyPath = \AnyObject.bar -// ``` +/// Diagnose an attempt to use AnyObject as the root type of a KeyPath +/// +/// ```swift +/// let keyPath = \AnyObject.bar +/// ``` class AnyObjectKeyPathRootFailure final : public FailureDiagnostic { public: - AnyObjectKeyPathRootFailure(Expr *root, ConstraintSystem &cs, + AnyObjectKeyPathRootFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; @@ -1463,9 +1410,9 @@ class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic { Type NonConformingType; public: - KeyPathSubscriptIndexHashableFailure(Expr *root, ConstraintSystem &cs, + KeyPathSubscriptIndexHashableFailure(ConstraintSystem &cs, Type type, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), NonConformingType(type) { + : FailureDiagnostic(cs, locator), NonConformingType(type) { assert(locator->isResultOfKeyPathDynamicMemberLookup() || locator->isKeyPathSubscriptComponent()); } @@ -1477,9 +1424,9 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic { ValueDecl *Member; public: - InvalidMemberRefInKeyPath(Expr *root, ConstraintSystem &cs, ValueDecl *member, + InvalidMemberRefInKeyPath(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), Member(member) { + : FailureDiagnostic(cs, locator), Member(member) { assert(member->hasName()); assert(locator->isForKeyPathComponent() || locator->isForKeyPathDynamicMemberLookup()); @@ -1512,9 +1459,9 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic { /// ``` class InvalidStaticMemberRefInKeyPath final : public InvalidMemberRefInKeyPath { public: - InvalidStaticMemberRefInKeyPath(Expr *root, ConstraintSystem &cs, + InvalidStaticMemberRefInKeyPath(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) - : InvalidMemberRefInKeyPath(root, cs, member, locator) {} + : InvalidMemberRefInKeyPath(cs, member, locator) {} bool diagnoseAsError() override; }; @@ -1539,10 +1486,10 @@ class InvalidStaticMemberRefInKeyPath final : public InvalidMemberRefInKeyPath { class InvalidMemberWithMutatingGetterInKeyPath final : public InvalidMemberRefInKeyPath { public: - InvalidMemberWithMutatingGetterInKeyPath(Expr *root, ConstraintSystem &cs, + InvalidMemberWithMutatingGetterInKeyPath(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator) - : InvalidMemberRefInKeyPath(root, cs, member, locator) {} + : InvalidMemberRefInKeyPath(cs, member, locator) {} bool diagnoseAsError() override; }; @@ -1561,9 +1508,9 @@ class InvalidMemberWithMutatingGetterInKeyPath final /// ``` class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath { public: - InvalidMethodRefInKeyPath(Expr *root, ConstraintSystem &cs, ValueDecl *method, + InvalidMethodRefInKeyPath(ConstraintSystem &cs, ValueDecl *method, ConstraintLocator *locator) - : InvalidMemberRefInKeyPath(root, cs, method, locator) { + : InvalidMemberRefInKeyPath(cs, method, locator) { assert(isa(method)); } @@ -1578,9 +1525,9 @@ class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath { /// ``` class ExtraneousReturnFailure final : public FailureDiagnostic { public: - ExtraneousReturnFailure(Expr *root, ConstraintSystem &cs, + ExtraneousReturnFailure(ConstraintSystem &cs, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator) {} + : FailureDiagnostic(cs, locator) {} bool diagnoseAsError() override; }; @@ -1594,10 +1541,10 @@ class ExtraneousReturnFailure final : public FailureDiagnostic { /// ``` class CollectionElementContextualFailure final : public ContextualFailure { public: - CollectionElementContextualFailure(Expr *root, ConstraintSystem &cs, + CollectionElementContextualFailure(ConstraintSystem &cs, Type eltType, Type contextualType, ConstraintLocator *locator) - : ContextualFailure(root, cs, eltType, contextualType, locator) {} + : ContextualFailure(cs, eltType, contextualType, locator) {} bool diagnoseAsError() override; }; @@ -1606,11 +1553,11 @@ class MissingContextualConformanceFailure final : public ContextualFailure { ContextualTypePurpose Context; public: - MissingContextualConformanceFailure(Expr *root, ConstraintSystem &cs, + MissingContextualConformanceFailure(ConstraintSystem &cs, ContextualTypePurpose context, Type type, Type protocolType, ConstraintLocator *locator) - : ContextualFailure(root, cs, type, protocolType, locator), + : ContextualFailure(cs, type, protocolType, locator), Context(context) { assert(protocolType->is() || protocolType->is()); @@ -1626,9 +1573,9 @@ class MissingContextualConformanceFailure final : public ContextualFailure { /// argument type of `inout` parameter, they have to be equal. class InOutConversionFailure final : public ContextualFailure { public: - InOutConversionFailure(Expr *root, ConstraintSystem &cs, Type argType, + InOutConversionFailure(ConstraintSystem &cs, Type argType, Type paramType, ConstraintLocator *locator) - : ContextualFailure(root, cs, argType, paramType, locator) {} + : ContextualFailure(cs, argType, paramType, locator) {} bool diagnoseAsError() override; @@ -1650,10 +1597,10 @@ class MissingGenericArgumentsFailure final : public FailureDiagnostic { SmallVector Parameters; public: - MissingGenericArgumentsFailure(Expr *root, ConstraintSystem &cs, + MissingGenericArgumentsFailure(ConstraintSystem &cs, ArrayRef missingParams, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator) { + : FailureDiagnostic(cs, locator) { assert(!missingParams.empty()); Parameters.append(missingParams.begin(), missingParams.end()); } @@ -1695,12 +1642,11 @@ class SkipUnhandledConstructInFunctionBuilderFailure final void diagnosePrimary(bool asNote); public: - SkipUnhandledConstructInFunctionBuilderFailure(Expr *root, - ConstraintSystem &cs, + SkipUnhandledConstructInFunctionBuilderFailure(ConstraintSystem &cs, UnhandledNode unhandled, NominalTypeDecl *builder, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), + : FailureDiagnostic(cs, locator), unhandled(unhandled), builder(builder) { } @@ -1719,10 +1665,10 @@ class InvalidTupleSplatWithSingleParameterFailure final Type ParamType; public: - InvalidTupleSplatWithSingleParameterFailure(Expr *root, ConstraintSystem &cs, + InvalidTupleSplatWithSingleParameterFailure(ConstraintSystem &cs, Type paramTy, ConstraintLocator *locator) - : FailureDiagnostic(root, cs, locator), ParamType(paramTy) {} + : FailureDiagnostic(cs, locator), ParamType(paramTy) {} bool diagnoseAsError() override; }; @@ -1734,9 +1680,9 @@ class InvalidTupleSplatWithSingleParameterFailure final /// ``` class ExpandArrayIntoVarargsFailure final : public ContextualFailure { public: - ExpandArrayIntoVarargsFailure(Expr *root, ConstraintSystem &cs, Type lhs, + ExpandArrayIntoVarargsFailure(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ContextualFailure(root, cs, lhs, rhs, locator) {} + : ContextualFailure(cs, lhs, rhs, locator) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; @@ -1759,10 +1705,10 @@ class ArgumentMismatchFailure : public ContextualFailure { Optional Info; public: - ArgumentMismatchFailure(Expr *root, ConstraintSystem &cs, Type argType, + ArgumentMismatchFailure(ConstraintSystem &cs, Type argType, Type paramType, ConstraintLocator *locator) - : ContextualFailure(root, cs, argType, paramType, locator), - Info(getFunctionArgApplyInfo(getLocator())) {} + : ContextualFailure(cs, argType, paramType, locator), + Info(cs.getFunctionArgApplyInfo(getLocator())) {} bool diagnoseAsError() override; bool diagnoseAsNote() override; @@ -1778,6 +1724,11 @@ class ArgumentMismatchFailure : public ContextualFailure { /// reference equality operators `===` and `!==`. bool diagnoseUseOfReferenceEqualityOperator() const; + /// Tailored diagnostics for type mismatches associated with + /// property wrapper initialization via implicit `init(wrappedValue:)` + /// or now deprecated `init(initialValue:)`. + bool diagnosePropertyWrapperMismatch() const; + protected: /// \returns The position of the argument being diagnosed, starting at 1. unsigned getArgPosition() const { return Info->getArgPosition(); } @@ -1809,6 +1760,13 @@ class ArgumentMismatchFailure : public ContextualFailure { return Info->getArgType(withSpecifier); } + /// \returns A textual description of the argument suitable for diagnostics. + /// For an argument with an unambiguous label, this will the label. Otherwise + /// it will be its position in the argument list. + StringRef getArgDescription(SmallVectorImpl &scratch) const { + return Info->getArgDescription(scratch); + } + /// \returns The interface type for the function being applied. Type getFnInterfaceType() const { return Info->getFnInterfaceType(); } @@ -1870,6 +1828,137 @@ class ArgumentMismatchFailure : public ContextualFailure { SourceLoc getLoc() const { return getAnchor()->getLoc(); } }; +/// Replace a coercion ('as') with a forced checked cast ('as!'). +class MissingForcedDowncastFailure final : public ContextualFailure { +public: + MissingForcedDowncastFailure(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) + : ContextualFailure(cs, fromType, toType, locator) {} + + bool diagnoseAsError() override; +}; + +class ExtraneousCallFailure final : public FailureDiagnostic { +public: + ExtraneousCallFailure(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError() override; +}; + +class InvalidUseOfTrailingClosure final : public ArgumentMismatchFailure { +public: + InvalidUseOfTrailingClosure(ConstraintSystem &cs, Type argType, + Type paramType, ConstraintLocator *locator) + : ArgumentMismatchFailure(cs, argType, paramType, locator) {} + + bool diagnoseAsError() override; +}; + +/// Diagnose the invalid conversion of a temporary pointer argument generated +/// from an X-to-pointer conversion to an @_nonEphemeral parameter. +/// +/// ```swift +/// func foo(@_nonEphemeral _ ptr: UnsafePointer) {} +/// +/// foo([1, 2, 3]) +/// ``` +class NonEphemeralConversionFailure final : public ArgumentMismatchFailure { + ConversionRestrictionKind ConversionKind; + bool DowngradeToWarning; + +public: + NonEphemeralConversionFailure(ConstraintSystem &cs, + ConstraintLocator *locator, + Type fromType, Type toType, + ConversionRestrictionKind conversionKind, + bool downgradeToWarning) + : ArgumentMismatchFailure(cs, fromType, toType, locator), + ConversionKind(conversionKind), DowngradeToWarning(downgradeToWarning) { + } + + bool diagnoseAsError() override; + bool diagnoseAsNote() override; + +private: + /// Attempts to emit a specialized diagnostic for + /// Unsafe[Mutable][Raw]Pointer.init([mutating]:) & + /// Unsafe[Mutable][Raw]BufferPointer.init(start:count:). + bool diagnosePointerInit() const; + + /// Emits a note explaining to the user that an ephemeral conversion is only + /// valid for the duration of the call, and suggests an alternative to use. + void emitSuggestionNotes() const; +}; + +class AssignmentTypeMismatchFailure final : public ContextualFailure { +public: + AssignmentTypeMismatchFailure(ConstraintSystem &cs, + ContextualTypePurpose context, Type srcType, + Type dstType, ConstraintLocator *locator) + : ContextualFailure(cs, context, srcType, dstType, locator) {} + + bool diagnoseAsError() override; + bool diagnoseAsNote() override; + +private: + bool diagnoseMissingConformance() const; +}; + +class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic { + DeclNameRef MemberName; + +public: + MissingContextualBaseInMemberRefFailure(ConstraintSystem &cs, + DeclNameRef member, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator), MemberName(member) {} + + bool diagnoseAsError(); +}; + +class UnableToInferClosureReturnType final : public FailureDiagnostic { +public: + UnableToInferClosureReturnType(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + +class UnableToInferProtocolLiteralType final : public FailureDiagnostic { +public: + UnableToInferProtocolLiteralType(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + +/// Diagnose an attempt to reference a top-level name shadowed by a local +/// member e.g. +/// +/// ```swift +/// extension Sequence { +/// func test() -> Int { +/// return max(1, 2) +/// } +/// } +/// ``` +/// +/// Here `max` refers to a global function `max(_: T, _: T)` in `Swift` +/// module and can only be accessed by adding `Swift.` to it, because `Sequence` +/// has a member named `max` which accepts a single argument. +class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { +public: + MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index d395538088de2..2eecf54297e86 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -56,9 +56,9 @@ std::string ForceDowncast::getName() const { return name.c_str(); } -bool ForceDowncast::diagnose(Expr *expr, bool asNote) const { +bool ForceDowncast::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - MissingExplicitConversionFailure failure(expr, cs, getFromType(), getToType(), + MissingExplicitConversionFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -68,43 +68,41 @@ ForceDowncast *ForceDowncast::create(ConstraintSystem &cs, Type fromType, return new (cs.getAllocator()) ForceDowncast(cs, fromType, toType, locator); } -bool ForceOptional::diagnose(Expr *root, bool asNote) const { - MissingOptionalUnwrapFailure failure(root, getConstraintSystem(), BaseType, - UnwrappedType, getLocator()); +bool ForceOptional::diagnose(bool asNote) const { + MissingOptionalUnwrapFailure failure(getConstraintSystem(), getFromType(), + getToType(), getLocator()); return failure.diagnose(asNote); } -ForceOptional *ForceOptional::create(ConstraintSystem &cs, Type baseType, - Type unwrappedType, - ConstraintLocator *locator) { - return new (cs.getAllocator()) - ForceOptional(cs, baseType, unwrappedType, locator); +ForceOptional *ForceOptional::create(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) { + return new (cs.getAllocator()) ForceOptional(cs, fromType, toType, locator); } -bool UnwrapOptionalBase::diagnose(Expr *root, bool asNote) const { +bool UnwrapOptionalBase::diagnose(bool asNote) const { bool resultIsOptional = getKind() == FixKind::UnwrapOptionalBaseWithOptionalResult; MemberAccessOnOptionalBaseFailure failure( - root, getConstraintSystem(), getLocator(), MemberName, resultIsOptional); + getConstraintSystem(), getLocator(), MemberName, resultIsOptional); return failure.diagnose(asNote); } UnwrapOptionalBase *UnwrapOptionalBase::create(ConstraintSystem &cs, - DeclName member, + DeclNameRef member, ConstraintLocator *locator) { return new (cs.getAllocator()) UnwrapOptionalBase(cs, FixKind::UnwrapOptionalBase, member, locator); } UnwrapOptionalBase *UnwrapOptionalBase::createWithOptionalResult( - ConstraintSystem &cs, DeclName member, ConstraintLocator *locator) { + ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator) { return new (cs.getAllocator()) UnwrapOptionalBase( cs, FixKind::UnwrapOptionalBaseWithOptionalResult, member, locator); } -bool AddAddressOf::diagnose(Expr *root, bool asNote) const { +bool AddAddressOf::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - MissingAddressOfFailure failure(root, cs, getFromType(), getToType(), + MissingAddressOfFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -114,7 +112,7 @@ AddAddressOf *AddAddressOf::create(ConstraintSystem &cs, Type argTy, return new (cs.getAllocator()) AddAddressOf(cs, argTy, paramTy, locator); } -bool TreatRValueAsLValue::diagnose(Expr *root, bool asNote) const { +bool TreatRValueAsLValue::diagnose(bool asNote) const { RValueTreatedAsLValueFailure failure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -124,32 +122,57 @@ TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs, return new (cs.getAllocator()) TreatRValueAsLValue(cs, locator); } -bool CoerceToCheckedCast::diagnose(Expr *root, bool asNote) const { - MissingForcedDowncastFailure failure(root, getConstraintSystem(), +bool CoerceToCheckedCast::diagnose(bool asNote) const { + MissingForcedDowncastFailure failure(getConstraintSystem(), + getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } -CoerceToCheckedCast *CoerceToCheckedCast::create(ConstraintSystem &cs, - ConstraintLocator *locator) { - return new (cs.getAllocator()) CoerceToCheckedCast(cs, locator); +CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs, + Type fromType, Type toType, + ConstraintLocator *locator) { + // If any of the types has a type variable, don't add the fix. + if (fromType->hasTypeVariable() || toType->hasTypeVariable()) + return nullptr; + + auto *expr = locator->getAnchor(); + if (auto *assignExpr = dyn_cast(expr)) + expr = assignExpr->getSrc(); + auto *coerceExpr = dyn_cast(expr); + if (!coerceExpr) + return nullptr; + + auto subExpr = coerceExpr->getSubExpr(); + auto castKind = + TypeChecker::typeCheckCheckedCast(fromType, toType, + CheckedCastContextKind::None, cs.DC, + coerceExpr->getLoc(), subExpr, + coerceExpr->getCastTypeLoc().getSourceRange()); + + // Invalid cast. + if (castKind == CheckedCastKind::Unresolved) + return nullptr; + + return new (cs.getAllocator()) + CoerceToCheckedCast(cs, fromType, toType, locator); } -bool MarkExplicitlyEscaping::diagnose(Expr *root, bool asNote) const { - NoEscapeFuncToTypeConversionFailure failure(root, getConstraintSystem(), - getLocator(), ConvertTo); +bool MarkExplicitlyEscaping::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + NoEscapeFuncToTypeConversionFailure failure(cs, getFromType(), getToType(), + getLocator()); return failure.diagnose(asNote); } MarkExplicitlyEscaping * -MarkExplicitlyEscaping::create(ConstraintSystem &cs, ConstraintLocator *locator, - Type convertingTo) { - return new (cs.getAllocator()) - MarkExplicitlyEscaping(cs, locator, convertingTo); +MarkExplicitlyEscaping::create(ConstraintSystem &cs, Type lhs, Type rhs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) MarkExplicitlyEscaping(cs, lhs, rhs, locator); } -bool RelabelArguments::diagnose(Expr *root, bool asNote) const { - LabelingFailure failure(root, getConstraintSystem(), getLocator(), +bool RelabelArguments::diagnose(bool asNote) const { + LabelingFailure failure(getConstraintSystem(), getLocator(), getLabels()); return failure.diagnose(asNote); } @@ -163,19 +186,19 @@ RelabelArguments::create(ConstraintSystem &cs, return new (mem) RelabelArguments(cs, correctLabels, locator); } -bool MissingConformance::diagnose(Expr *root, bool asNote) const { +bool MissingConformance::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); auto *locator = getLocator(); if (IsContextual) { - auto context = cs.getContextualTypePurpose(); + auto context = cs.getContextualTypePurpose(locator->getAnchor()); MissingContextualConformanceFailure failure( - root, cs, context, NonConformingType, ProtocolType, locator); + cs, context, NonConformingType, ProtocolType, locator); return failure.diagnose(asNote); } MissingConformanceFailure failure( - root, cs, locator, std::make_pair(NonConformingType, ProtocolType)); + cs, locator, std::make_pair(NonConformingType, ProtocolType)); return failure.diagnose(asNote); } @@ -195,8 +218,8 @@ MissingConformance::forRequirement(ConstraintSystem &cs, Type type, cs, /*isContextual=*/false, type, protocolType, locator); } -bool SkipSameTypeRequirement::diagnose(Expr *root, bool asNote) const { - SameTypeRequirementFailure failure(root, getConstraintSystem(), LHS, RHS, +bool SkipSameTypeRequirement::diagnose(bool asNote) const { + SameTypeRequirementFailure failure(getConstraintSystem(), LHS, RHS, getLocator()); return failure.diagnose(asNote); } @@ -207,8 +230,8 @@ SkipSameTypeRequirement::create(ConstraintSystem &cs, Type lhs, Type rhs, return new (cs.getAllocator()) SkipSameTypeRequirement(cs, lhs, rhs, locator); } -bool SkipSuperclassRequirement::diagnose(Expr *root, bool asNote) const { - SuperclassRequirementFailure failure(root, getConstraintSystem(), LHS, RHS, +bool SkipSuperclassRequirement::diagnose(bool asNote) const { + SuperclassRequirementFailure failure(getConstraintSystem(), LHS, RHS, getLocator()); return failure.diagnose(asNote); } @@ -220,8 +243,8 @@ SkipSuperclassRequirement::create(ConstraintSystem &cs, Type lhs, Type rhs, SkipSuperclassRequirement(cs, lhs, rhs, locator); } -bool ContextualMismatch::diagnose(Expr *root, bool asNote) const { - auto failure = ContextualFailure(root, getConstraintSystem(), getFromType(), +bool ContextualMismatch::diagnose(bool asNote) const { + auto failure = ContextualFailure(getConstraintSystem(), getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -232,23 +255,118 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs, return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator); } -bool AllowTupleTypeMismatch::diagnose(Expr *root, bool asNote) const { - auto failure = TupleContextualFailure( - root, getConstraintSystem(), getFromType(), getToType(), getLocator()); +/// Computes the contextual type information for a type mismatch of a +/// component in a structural type (tuple or function type). +/// +/// \returns A tuple containing the contextual type purpose, the source type, +/// and the contextual type. +static Optional> +getStructuralTypeContext(ConstraintSystem &cs, ConstraintLocator *locator) { + if (auto contextualType = cs.getContextualType(locator->getAnchor())) { + if (auto *anchor = simplifyLocatorToAnchor(locator)) + return std::make_tuple(cs.getContextualTypePurpose(locator->getAnchor()), + cs.getType(anchor), + contextualType); + } else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) { + return std::make_tuple(CTP_CallArgument, + argApplyInfo->getArgType(), + argApplyInfo->getParamType()); + } else if (auto *coerceExpr = dyn_cast(locator->getAnchor())) { + return std::make_tuple(CTP_CoerceOperand, + cs.getType(coerceExpr->getSubExpr()), + cs.getType(coerceExpr)); + } else if (auto *assignExpr = dyn_cast(locator->getAnchor())) { + return std::make_tuple(CTP_AssignSource, + cs.getType(assignExpr->getSrc()), + cs.getType(assignExpr->getDest())); + } + + return None; +} + +bool AllowTupleTypeMismatch::coalesceAndDiagnose( + ArrayRef fixes, bool asNote) const { + llvm::SmallVector indices; + if (isElementMismatch()) + indices.push_back(*Index); + + for (auto fix : fixes) { + auto *tupleFix = fix->getAs(); + if (!tupleFix || !tupleFix->isElementMismatch()) + continue; + indices.push_back(*tupleFix->Index); + } + + auto &cs = getConstraintSystem(); + auto *locator = getLocator(); + ContextualTypePurpose purpose; + Type fromType; + Type toType; + + if (getFromType()->is() && getToType()->is()) { + purpose = cs.getContextualTypePurpose(locator->getAnchor()); + fromType = getFromType(); + toType = getToType(); + } else if (auto contextualTypeInfo = getStructuralTypeContext(cs, locator)) { + std::tie(purpose, fromType, toType) = *contextualTypeInfo; + } else { + return false; + } + + TupleContextualFailure failure(cs, purpose, fromType, toType, indices, locator); return failure.diagnose(asNote); } +bool AllowTupleTypeMismatch::diagnose(bool asNote) const { + return coalesceAndDiagnose({}, asNote); +} + AllowTupleTypeMismatch * AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, - ConstraintLocator *locator) { - assert(lhs->is() && rhs->is() && - "lhs and rhs must be tuple types"); - return new (cs.getAllocator()) AllowTupleTypeMismatch(cs, lhs, rhs, locator); + ConstraintLocator *locator, + Optional index) { + return new (cs.getAllocator()) + AllowTupleTypeMismatch(cs, lhs, rhs, locator, index); } -bool GenericArgumentsMismatch::diagnose(Expr *root, bool asNote) const { +bool AllowFunctionTypeMismatch::coalesceAndDiagnose( + ArrayRef fixes, bool asNote) const { + llvm::SmallVector indices{ParamIndex}; + + for (auto fix : fixes) { + if (auto *fnFix = fix->getAs()) + indices.push_back(fnFix->ParamIndex); + } + auto &cs = getConstraintSystem(); - GenericArgumentsMismatchFailure failure(root, cs, getFromType(), getToType(), + auto *locator = getLocator(); + ContextualTypePurpose purpose; + Type fromType; + Type toType; + + auto contextualTypeInfo = getStructuralTypeContext(cs, locator); + if (!contextualTypeInfo) + return false; + + std::tie(purpose, fromType, toType) = *contextualTypeInfo; + FunctionTypeMismatch failure(cs, purpose, fromType, toType, indices, locator); + return failure.diagnose(asNote); +} + +bool AllowFunctionTypeMismatch::diagnose(bool asNote) const { + return coalesceAndDiagnose({}, asNote); +} + +AllowFunctionTypeMismatch * +AllowFunctionTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, + ConstraintLocator *locator, unsigned index) { + return new (cs.getAllocator()) + AllowFunctionTypeMismatch(cs, lhs, rhs, locator, index); +} + +bool GenericArgumentsMismatch::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + GenericArgumentsMismatchFailure failure(cs, getFromType(), getToType(), getMismatches(), getLocator()); return failure.diagnose(asNote); } @@ -263,7 +381,7 @@ GenericArgumentsMismatch *GenericArgumentsMismatch::create( GenericArgumentsMismatch(cs, actual, required, mismatches, locator); } -bool AutoClosureForwarding::diagnose(Expr *root, bool asNote) const { +bool AutoClosureForwarding::diagnose(bool asNote) const { auto failure = AutoClosureForwardingFailure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); @@ -274,8 +392,8 @@ AutoClosureForwarding *AutoClosureForwarding::create(ConstraintSystem &cs, return new (cs.getAllocator()) AutoClosureForwarding(cs, locator); } -bool AllowAutoClosurePointerConversion::diagnose(Expr *root, bool asNote) const { - auto failure = AutoClosurePointerConversionFailure(root, getConstraintSystem(), +bool AllowAutoClosurePointerConversion::diagnose(bool asNote) const { + auto failure = AutoClosurePointerConversionFailure(getConstraintSystem(), getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -288,8 +406,8 @@ AllowAutoClosurePointerConversion::create(ConstraintSystem &cs, Type pointeeType AllowAutoClosurePointerConversion(cs, pointeeType, pointerType, locator); } -bool RemoveUnwrap::diagnose(Expr *root, bool asNote) const { - auto failure = NonOptionalUnwrapFailure(root, getConstraintSystem(), BaseType, +bool RemoveUnwrap::diagnose(bool asNote) const { + auto failure = NonOptionalUnwrapFailure(getConstraintSystem(), BaseType, getLocator()); return failure.diagnose(asNote); } @@ -299,8 +417,8 @@ RemoveUnwrap *RemoveUnwrap::create(ConstraintSystem &cs, Type baseType, return new (cs.getAllocator()) RemoveUnwrap(cs, baseType, locator); } -bool InsertExplicitCall::diagnose(Expr *root, bool asNote) const { - auto failure = MissingCallFailure(root, getConstraintSystem(), getLocator()); +bool InsertExplicitCall::diagnose(bool asNote) const { + auto failure = MissingCallFailure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -309,10 +427,10 @@ InsertExplicitCall *InsertExplicitCall::create(ConstraintSystem &cs, return new (cs.getAllocator()) InsertExplicitCall(cs, locator); } -bool UsePropertyWrapper::diagnose(Expr *root, bool asNote) const { +bool UsePropertyWrapper::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); auto failure = ExtraneousPropertyWrapperUnwrapFailure( - root, cs, Wrapped, UsingStorageWrapper, Base, Wrapper, getLocator()); + cs, Wrapped, UsingStorageWrapper, Base, Wrapper, getLocator()); return failure.diagnose(asNote); } @@ -325,10 +443,10 @@ UsePropertyWrapper *UsePropertyWrapper::create(ConstraintSystem &cs, cs, wrapped, usingStorageWrapper, base, wrapper, locator); } -bool UseWrappedValue::diagnose(Expr *root, bool asNote) const { +bool UseWrappedValue::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); auto failure = MissingPropertyWrapperUnwrapFailure( - root, cs, PropertyWrapper, usingStorageWrapper(), Base, Wrapper, + cs, PropertyWrapper, usingStorageWrapper(), Base, Wrapper, getLocator()); return failure.diagnose(asNote); } @@ -341,8 +459,8 @@ UseWrappedValue *UseWrappedValue::create(ConstraintSystem &cs, UseWrappedValue(cs, propertyWrapper, base, wrapper, locator); } -bool UseSubscriptOperator::diagnose(Expr *root, bool asNote) const { - auto failure = SubscriptMisuseFailure(root, getConstraintSystem(), getLocator()); +bool UseSubscriptOperator::diagnose(bool asNote) const { + auto failure = SubscriptMisuseFailure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -351,51 +469,52 @@ UseSubscriptOperator *UseSubscriptOperator::create(ConstraintSystem &cs, return new (cs.getAllocator()) UseSubscriptOperator(cs, locator); } -bool DefineMemberBasedOnUse::diagnose(Expr *root, bool asNote) const { - auto failure = MissingMemberFailure(root, getConstraintSystem(), BaseType, +bool DefineMemberBasedOnUse::diagnose(bool asNote) const { + auto failure = MissingMemberFailure(getConstraintSystem(), BaseType, Name, getLocator()); - return failure.diagnose(asNote); + return AlreadyDiagnosed || failure.diagnose(asNote); } DefineMemberBasedOnUse * DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType, - DeclName member, ConstraintLocator *locator) { + DeclNameRef member, bool alreadyDiagnosed, + ConstraintLocator *locator) { return new (cs.getAllocator()) - DefineMemberBasedOnUse(cs, baseType, member, locator); + DefineMemberBasedOnUse(cs, baseType, member, alreadyDiagnosed, locator); } AllowMemberRefOnExistential * AllowMemberRefOnExistential::create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName memberName, + ValueDecl *member, DeclNameRef memberName, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowMemberRefOnExistential(cs, baseType, memberName, member, locator); } -bool AllowMemberRefOnExistential::diagnose(Expr *root, bool asNote) const { +bool AllowMemberRefOnExistential::diagnose(bool asNote) const { auto failure = - InvalidMemberRefOnExistential(root, getConstraintSystem(), getBaseType(), + InvalidMemberRefOnExistential(getConstraintSystem(), getBaseType(), getMemberName(), getLocator()); return failure.diagnose(asNote); } -bool AllowTypeOrInstanceMember::diagnose(Expr *root, bool asNote) const { +bool AllowTypeOrInstanceMember::diagnose(bool asNote) const { auto failure = AllowTypeOrInstanceMemberFailure( - root, getConstraintSystem(), getBaseType(), getMember(), getMemberName(), + getConstraintSystem(), getBaseType(), getMember(), getMemberName(), getLocator()); return failure.diagnose(asNote); } AllowTypeOrInstanceMember * AllowTypeOrInstanceMember::create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName usedName, + ValueDecl *member, DeclNameRef usedName, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowTypeOrInstanceMember(cs, baseType, member, usedName, locator); } -bool AllowInvalidPartialApplication::diagnose(Expr *root, bool asNote) const { - auto failure = PartialApplicationFailure(root, isWarning(), +bool AllowInvalidPartialApplication::diagnose(bool asNote) const { + auto failure = PartialApplicationFailure(isWarning(), getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -407,23 +526,23 @@ AllowInvalidPartialApplication::create(bool isWarning, ConstraintSystem &cs, AllowInvalidPartialApplication(isWarning, cs, locator); } -bool AllowInvalidInitRef::diagnose(Expr *root, bool asNote) const { +bool AllowInvalidInitRef::diagnose(bool asNote) const { switch (Kind) { case RefKind::DynamicOnMetatype: { InvalidDynamicInitOnMetatypeFailure failure( - root, getConstraintSystem(), BaseType, Init, BaseRange, getLocator()); + getConstraintSystem(), BaseType, Init, BaseRange, getLocator()); return failure.diagnose(asNote); } case RefKind::ProtocolMetatype: { - InitOnProtocolMetatypeFailure failure(root, getConstraintSystem(), BaseType, + InitOnProtocolMetatypeFailure failure(getConstraintSystem(), BaseType, Init, IsStaticallyDerived, BaseRange, getLocator()); return failure.diagnose(asNote); } case RefKind::NonConstMetatype: { - ImplicitInitOnNonConstMetatypeFailure failure(root, getConstraintSystem(), + ImplicitInitOnNonConstMetatypeFailure failure(getConstraintSystem(), BaseType, Init, getLocator()); return failure.diagnose(asNote); } @@ -462,8 +581,8 @@ AllowInvalidInitRef::create(RefKind kind, ConstraintSystem &cs, Type baseTy, cs, kind, baseTy, init, isStaticallyDerived, baseRange, locator); } -bool AllowClosureParamDestructuring::diagnose(Expr *root, bool asNote) const { - ClosureParamDestructuringFailure failure(root, getConstraintSystem(), +bool AllowClosureParamDestructuring::diagnose(bool asNote) const { + ClosureParamDestructuringFailure failure(getConstraintSystem(), ContextualType, getLocator()); return failure.diagnose(asNote); } @@ -476,9 +595,9 @@ AllowClosureParamDestructuring::create(ConstraintSystem &cs, AllowClosureParamDestructuring(cs, contextualType, locator); } -bool AddMissingArguments::diagnose(Expr *root, bool asNote) const { +bool AddMissingArguments::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - MissingArgumentsFailure failure(root, cs, getSynthesizedArguments(), + MissingArgumentsFailure failure(cs, getSynthesizedArguments(), getLocator()); return failure.diagnose(asNote); } @@ -492,8 +611,44 @@ AddMissingArguments::create(ConstraintSystem &cs, return new (mem) AddMissingArguments(cs, synthesizedArgs, locator); } -bool MoveOutOfOrderArgument::diagnose(Expr *root, bool asNote) const { - OutOfOrderArgumentFailure failure(root, getConstraintSystem(), ArgIdx, +bool RemoveExtraneousArguments::diagnose(bool asNote) const { + ExtraneousArgumentsFailure failure(getConstraintSystem(), + ContextualType, getExtraArguments(), + getLocator()); + return failure.diagnose(asNote); +} + +bool RemoveExtraneousArguments::isMinMaxNameShadowing( + ConstraintSystem &cs, ConstraintLocatorBuilder locator) { + auto *anchor = dyn_cast_or_null(locator.getAnchor()); + if (!anchor) + return false; + + if (auto *UDE = dyn_cast(anchor->getFn())) { + if (auto *baseExpr = dyn_cast(UDE->getBase())) { + auto *decl = baseExpr->getDecl(); + if (baseExpr->isImplicit() && decl && + decl->getFullName() == cs.getASTContext().Id_self) { + auto memberName = UDE->getName(); + return memberName.isSimpleName("min") || memberName.isSimpleName("max"); + } + } + } + + return false; +} + +RemoveExtraneousArguments *RemoveExtraneousArguments::create( + ConstraintSystem &cs, FunctionType *contextualType, + llvm::ArrayRef extraArgs, ConstraintLocator *locator) { + unsigned size = totalSizeToAlloc(extraArgs.size()); + void *mem = cs.getAllocator().Allocate(size, alignof(RemoveExtraneousArguments)); + return new (mem) + RemoveExtraneousArguments(cs, contextualType, extraArgs, locator); +} + +bool MoveOutOfOrderArgument::diagnose(bool asNote) const { + OutOfOrderArgumentFailure failure(getConstraintSystem(), ArgIdx, PrevArgIdx, Bindings, getLocator()); return failure.diagnose(asNote); } @@ -505,23 +660,22 @@ MoveOutOfOrderArgument *MoveOutOfOrderArgument::create( MoveOutOfOrderArgument(cs, argIdx, prevArgIdx, bindings, locator); } -bool AllowInaccessibleMember::diagnose(Expr *root, bool asNote) const { - InaccessibleMemberFailure failure(root, getConstraintSystem(), getMember(), +bool AllowInaccessibleMember::diagnose(bool asNote) const { + InaccessibleMemberFailure failure(getConstraintSystem(), getMember(), getLocator()); return failure.diagnose(asNote); } AllowInaccessibleMember * AllowInaccessibleMember::create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowInaccessibleMember(cs, baseType, member, name, locator); } -bool AllowAnyObjectKeyPathRoot::diagnose(Expr *root, bool asNote) const { - AnyObjectKeyPathRootFailure failure(root, getConstraintSystem(), - getLocator()); +bool AllowAnyObjectKeyPathRoot::diagnose(bool asNote) const { + AnyObjectKeyPathRootFailure failure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -531,9 +685,8 @@ AllowAnyObjectKeyPathRoot::create(ConstraintSystem &cs, return new (cs.getAllocator()) AllowAnyObjectKeyPathRoot(cs, locator); } -bool TreatKeyPathSubscriptIndexAsHashable::diagnose(Expr *root, - bool asNote) const { - KeyPathSubscriptIndexHashableFailure failure(root, getConstraintSystem(), +bool TreatKeyPathSubscriptIndexAsHashable::diagnose(bool asNote) const { + KeyPathSubscriptIndexHashableFailure failure(getConstraintSystem(), NonConformingType, getLocator()); return failure.diagnose(asNote); } @@ -545,22 +698,22 @@ TreatKeyPathSubscriptIndexAsHashable::create(ConstraintSystem &cs, Type type, TreatKeyPathSubscriptIndexAsHashable(cs, type, locator); } -bool AllowInvalidRefInKeyPath::diagnose(Expr *root, bool asNote) const { +bool AllowInvalidRefInKeyPath::diagnose(bool asNote) const { switch (Kind) { case RefKind::StaticMember: { - InvalidStaticMemberRefInKeyPath failure(root, getConstraintSystem(), Member, + InvalidStaticMemberRefInKeyPath failure(getConstraintSystem(), Member, getLocator()); return failure.diagnose(asNote); } case RefKind::MutatingGetter: { InvalidMemberWithMutatingGetterInKeyPath failure( - root, getConstraintSystem(), Member, getLocator()); + getConstraintSystem(), Member, getLocator()); return failure.diagnose(asNote); } case RefKind::Method: { - InvalidMethodRefInKeyPath failure(root, getConstraintSystem(), Member, + InvalidMethodRefInKeyPath failure(getConstraintSystem(), Member, getLocator()); return failure.diagnose(asNote); } @@ -608,8 +761,8 @@ KeyPathContextualMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs, KeyPathContextualMismatch(cs, lhs, rhs, locator); } -bool RemoveAddressOf::diagnose(Expr *root, bool asNote) const { - InvalidUseOfAddressOf failure(root, getConstraintSystem(), getFromType(), +bool RemoveAddressOf::diagnose(bool asNote) const { + InvalidUseOfAddressOf failure(getConstraintSystem(), getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -619,8 +772,8 @@ RemoveAddressOf *RemoveAddressOf::create(ConstraintSystem &cs, Type lhs, Type rh return new (cs.getAllocator()) RemoveAddressOf(cs, lhs, rhs, locator); } -bool RemoveReturn::diagnose(Expr *root, bool asNote) const { - ExtraneousReturnFailure failure(root, getConstraintSystem(), getLocator()); +bool RemoveReturn::diagnose(bool asNote) const { + ExtraneousReturnFailure failure(getConstraintSystem(), getLocator()); return failure.diagnose(asNote); } @@ -629,10 +782,9 @@ RemoveReturn *RemoveReturn::create(ConstraintSystem &cs, return new (cs.getAllocator()) RemoveReturn(cs, locator); } -bool CollectionElementContextualMismatch::diagnose(Expr *root, - bool asNote) const { +bool CollectionElementContextualMismatch::diagnose(bool asNote) const { CollectionElementContextualFailure failure( - root, getConstraintSystem(), getFromType(), getToType(), getLocator()); + getConstraintSystem(), getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -644,21 +796,28 @@ CollectionElementContextualMismatch::create(ConstraintSystem &cs, Type srcType, CollectionElementContextualMismatch(cs, srcType, dstType, locator); } -bool ExplicitlySpecifyGenericArguments::diagnose(Expr *root, - bool asNote) const { +bool DefaultGenericArgument::coalesceAndDiagnose( + ArrayRef fixes, bool asNote) const { + llvm::SmallVector missingParams{Param}; + + for (auto *otherFix : fixes) { + if (auto *fix = otherFix->getAs()) + missingParams.push_back(fix->Param); + } + auto &cs = getConstraintSystem(); - MissingGenericArgumentsFailure failure(root, cs, getParameters(), - getLocator()); + MissingGenericArgumentsFailure failure(cs, missingParams, getLocator()); return failure.diagnose(asNote); } -ExplicitlySpecifyGenericArguments *ExplicitlySpecifyGenericArguments::create( - ConstraintSystem &cs, ArrayRef params, - ConstraintLocator *locator) { - unsigned size = totalSizeToAlloc(params.size()); - void *mem = cs.getAllocator().Allocate( - size, alignof(ExplicitlySpecifyGenericArguments)); - return new (mem) ExplicitlySpecifyGenericArguments(cs, params, locator); +bool DefaultGenericArgument::diagnose(bool asNote) const { + return coalesceAndDiagnose({}, asNote); +} + +DefaultGenericArgument * +DefaultGenericArgument::create(ConstraintSystem &cs, GenericTypeParamType *param, + ConstraintLocator *locator) { + return new (cs.getAllocator()) DefaultGenericArgument(cs, param, locator); } SkipUnhandledConstructInFunctionBuilder * @@ -670,30 +829,29 @@ SkipUnhandledConstructInFunctionBuilder::create(ConstraintSystem &cs, SkipUnhandledConstructInFunctionBuilder(cs, unhandled, builder, locator); } -bool SkipUnhandledConstructInFunctionBuilder::diagnose(Expr *root, - bool asNote) const { +bool SkipUnhandledConstructInFunctionBuilder::diagnose(bool asNote) const { SkipUnhandledConstructInFunctionBuilderFailure failure( - root, getConstraintSystem(), unhandled, builder, getLocator()); + getConstraintSystem(), unhandled, builder, getLocator()); return failure.diagnose(asNote); } -bool AllowMutatingMemberOnRValueBase::diagnose(Expr *root, bool asNote) const { +bool AllowMutatingMemberOnRValueBase::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - MutatingMemberRefOnImmutableBase failure(root, cs, getMember(), getLocator()); + MutatingMemberRefOnImmutableBase failure(cs, getMember(), getLocator()); return failure.diagnose(asNote); } AllowMutatingMemberOnRValueBase * AllowMutatingMemberOnRValueBase::create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) { return new (cs.getAllocator()) AllowMutatingMemberOnRValueBase(cs, baseType, member, name, locator); } -bool AllowTupleSplatForSingleParameter::diagnose(Expr *root, bool asNote) const { +bool AllowTupleSplatForSingleParameter::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - InvalidTupleSplatWithSingleParameterFailure failure(root, cs, ParamType, + InvalidTupleSplatWithSingleParameterFailure failure(cs, ParamType, getLocator()); return failure.diagnose(asNote); } @@ -715,8 +873,7 @@ bool AllowTupleSplatForSingleParameter::attempt( // Parameter type has to be either a tuple (with the same arity as // argument list), or a type variable. if (!(paramTy->is() && - paramTy->castTo()->getNumElements() == args.size()) && - !paramTy->is()) + paramTy->castTo()->getNumElements() == args.size())) return true; SmallVector argElts; @@ -728,15 +885,22 @@ bool AllowTupleSplatForSingleParameter::attempt( auto flags = arg.getParameterFlags(); // In situations where there is a single labeled parameter - // we need to form a tuple with omits first label e.g. + // we need to form a tuple which omits the label e.g. // - // func foo(x: T) {} + // func foo(x: (T, T)) {} // foo(x: 0, 1) // // We'd want to suggest argument list to be `x: (0, 1)` instead // of `(x: 0, 1)` which would be incorrect. - if (index == 0 && param.getLabel() == label) - label = Identifier(); + if (param.hasLabel() && label == param.getLabel()) { + if (index == 0) { + label = Identifier(); + } else { + // If label match anything other than first argument, + // this can't be a tuple splat. + return true; + } + } // Tuple can't have `inout` elements. if (flags.isInOut()) @@ -759,9 +923,9 @@ bool AllowTupleSplatForSingleParameter::attempt( return cs.recordFix(fix); } -bool DropThrowsAttribute::diagnose(Expr *root, bool asNote) const { +bool DropThrowsAttribute::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - ThrowingFunctionConversionFailure failure(root, cs, getFromType(), + ThrowingFunctionConversionFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -774,9 +938,9 @@ DropThrowsAttribute *DropThrowsAttribute::create(ConstraintSystem &cs, DropThrowsAttribute(cs, fromType, toType, locator); } -bool IgnoreContextualType::diagnose(Expr *root, bool asNote) const { +bool IgnoreContextualType::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - ContextualFailure failure(root, cs, getFromType(), getToType(), getLocator()); + ContextualFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -788,14 +952,28 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs, IgnoreContextualType(cs, resultTy, specifiedTy, locator); } -bool IgnoreAssignmentDestinationType::diagnose(Expr *root, bool asNote) const { +bool IgnoreAssignmentDestinationType::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); auto *AE = cast(getAnchor()); + + // Let's check whether this is a situation of chained assignment where + // one of the steps in the chain is an assignment to self e.g. + // `let _ = { $0 = $0 = 42 }`. Assignment chaining results in + // type mismatch between result of the previous assignment and the next. + { + llvm::SaveAndRestore anchor(AE); + + do { + if (TypeChecker::diagnoseSelfAssignment(AE)) + return true; + } while ((AE = dyn_cast_or_null(cs.getParentExpr(AE)))); + } + auto CTP = isa(AE->getDest()) ? CTP_SubscriptAssignSource : CTP_AssignSource; - ContextualFailure failure( - root, cs, CTP, getFromType(), getToType(), + AssignmentTypeMismatchFailure failure( + cs, CTP, getFromType(), getToType(), cs.getConstraintLocator(AE->getSrc(), LocatorPathElt::ContextualType())); return failure.diagnose(asNote); } @@ -808,9 +986,9 @@ IgnoreAssignmentDestinationType::create(ConstraintSystem &cs, Type sourceTy, IgnoreAssignmentDestinationType(cs, sourceTy, destTy, locator); } -bool AllowInOutConversion::diagnose(Expr *root, bool asNote) const { +bool AllowInOutConversion::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - InOutConversionFailure failure(root, cs, getFromType(), getToType(), + InOutConversionFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -847,30 +1025,34 @@ static bool isValueOfRawRepresentable(ConstraintSystem &cs, ExpandArrayIntoVarargs * ExpandArrayIntoVarargs::attempt(ConstraintSystem &cs, Type argType, Type paramType, - ConstraintLocatorBuilder locator) { - auto constraintLocator = cs.getConstraintLocator(locator); + ConstraintLocatorBuilder builder) { + auto *locator = cs.getConstraintLocator(builder); + + auto argLoc = locator->getLastElementAs(); + if (!(argLoc && argLoc->getParameterFlags().isVariadic())) + return nullptr; + auto elementType = cs.isArrayType(argType); - if (elementType && - constraintLocator->getLastElementAs() - ->getParameterFlags() - .isVariadic()) { - auto options = ConstraintSystem::TypeMatchOptions( - ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix | - ConstraintSystem::TypeMatchFlags::TMF_GenerateConstraints); - auto result = - cs.matchTypes(*elementType, paramType, - ConstraintKind::ArgumentConversion, options, locator); - if (result.isSuccess()) - return new (cs.getAllocator()) - ExpandArrayIntoVarargs(cs, argType, paramType, constraintLocator); - } + if (!elementType) + return nullptr; - return nullptr; + ConstraintSystem::TypeMatchOptions options; + options |= ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix; + options |= ConstraintSystem::TypeMatchFlags::TMF_GenerateConstraints; + + auto result = cs.matchTypes(*elementType, paramType, ConstraintKind::Subtype, + options, builder); + + if (result.isFailure()) + return nullptr; + + return new (cs.getAllocator()) + ExpandArrayIntoVarargs(cs, argType, paramType, locator); } -bool ExpandArrayIntoVarargs::diagnose(Expr *root, bool asNote) const { +bool ExpandArrayIntoVarargs::diagnose(bool asNote) const { ExpandArrayIntoVarargsFailure failure( - root, getConstraintSystem(), getFromType(), getToType(), getLocator()); + getConstraintSystem(), getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -902,9 +1084,9 @@ UseValueTypeOfRawRepresentative::attempt(ConstraintSystem &cs, Type argType, return nullptr; } -bool AllowArgumentMismatch::diagnose(Expr *root, bool asNote) const { +bool AllowArgumentMismatch::diagnose(bool asNote) const { auto &cs = getConstraintSystem(); - ArgumentMismatchFailure failure(root, cs, getFromType(), getToType(), + ArgumentMismatchFailure failure(cs, getFromType(), getToType(), getLocator()); return failure.diagnose(asNote); } @@ -915,3 +1097,135 @@ AllowArgumentMismatch::create(ConstraintSystem &cs, Type argType, return new (cs.getAllocator()) AllowArgumentMismatch(cs, argType, paramType, locator); } + +bool RemoveInvalidCall::diagnose(bool asNote) const { + ExtraneousCallFailure failure(getConstraintSystem(), getLocator()); + return failure.diagnose(asNote); +} + +RemoveInvalidCall *RemoveInvalidCall::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) RemoveInvalidCall(cs, locator); +} + +bool AllowInvalidUseOfTrailingClosure::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + InvalidUseOfTrailingClosure failure(cs, getFromType(), getToType(), + getLocator()); + return failure.diagnose(asNote); +} + +AllowInvalidUseOfTrailingClosure * +AllowInvalidUseOfTrailingClosure::create(ConstraintSystem &cs, Type argType, + Type paramType, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowInvalidUseOfTrailingClosure(cs, argType, paramType, locator); +} + +bool TreatEphemeralAsNonEphemeral::diagnose(bool asNote) const { + NonEphemeralConversionFailure failure( + getConstraintSystem(), getLocator(), getFromType(), getToType(), + ConversionKind, isWarning()); + return failure.diagnose(asNote); +} + +TreatEphemeralAsNonEphemeral *TreatEphemeralAsNonEphemeral::create( + ConstraintSystem &cs, ConstraintLocator *locator, Type srcType, + Type dstType, ConversionRestrictionKind conversionKind, + bool downgradeToWarning) { + return new (cs.getAllocator()) TreatEphemeralAsNonEphemeral( + cs, locator, srcType, dstType, conversionKind, downgradeToWarning); +} + +std::string TreatEphemeralAsNonEphemeral::getName() const { + std::string name; + name += "treat ephemeral as non-ephemeral for "; + name += ::getName(ConversionKind); + return name; +} + +bool SpecifyBaseTypeForContextualMember::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + MissingContextualBaseInMemberRefFailure failure(cs, MemberName, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create( + ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator) { + return new (cs.getAllocator()) + SpecifyBaseTypeForContextualMember(cs, member, locator); +} + +bool SpecifyClosureReturnType::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + UnableToInferClosureReturnType failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyClosureReturnType * +SpecifyClosureReturnType::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyClosureReturnType(cs, locator); +} + +bool SpecifyObjectLiteralTypeImport::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + UnableToInferProtocolLiteralType failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +SpecifyObjectLiteralTypeImport * +SpecifyObjectLiteralTypeImport::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) SpecifyObjectLiteralTypeImport(cs, locator); +} + +AllowNonClassTypeToConvertToAnyObject::AllowNonClassTypeToConvertToAnyObject( + ConstraintSystem &cs, Type type, ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::AllowNonClassTypeToConvertToAnyObject, + type, cs.getASTContext().getAnyObjectType(), locator) { +} + +bool AllowNonClassTypeToConvertToAnyObject::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + + auto *locator = getLocator(); + if (locator->getPath().empty()) + return false; + + const auto &last = locator->getPath().back(); + switch (last.getKind()) { + case ConstraintLocator::ContextualType: { + ContextualFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + case ConstraintLocator::ApplyArgToParam: { + ArgumentMismatchFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + default: + return false; + } +} + +AllowNonClassTypeToConvertToAnyObject * +AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowNonClassTypeToConvertToAnyObject(cs, type, locator); +} + +bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + MissingQuialifierInMemberRefFailure failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +AddQualifierToAccessTopLevelName * +AddQualifierToAccessTopLevelName::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddQualifierToAccessTopLevelName(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 28867744468ea..d4c453070228d 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -22,6 +22,7 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/TrailingObjects.h" @@ -41,13 +42,16 @@ class OverloadChoice; class ConstraintSystem; class ConstraintLocator; class ConstraintLocatorBuilder; +enum class ConversionRestrictionKind; class Solution; /// Describes the kind of fix to apply to the given constraint before /// visiting it. +/// +/// Note: values 0 and 1 are reserved for empty and tombstone kinds. enum class FixKind : uint8_t { /// Introduce a '!' to force an optional unwrap. - ForceOptional, + ForceOptional = 2, /// Unwrap an optional base when we have a member access. UnwrapOptionalBase, @@ -141,6 +145,10 @@ enum class FixKind : uint8_t { /// types. AllowTupleTypeMismatch, + /// Allow a function type to be destructured with mismatched parameter types + /// or return type. + AllowFunctionTypeMismatch, + /// Allow an invalid member access on a value of protocol type as if /// that protocol type were a generic constraint requiring conformance /// to that protocol. @@ -150,6 +158,10 @@ enum class FixKind : uint8_t { /// by adding new arguments to the list represented as type variables. AddMissingArguments, + /// If there are more arguments than parameters, let's fix that up + /// by removing extraneous arguments. + RemoveExtraneousArguments, + /// Allow single tuple closure parameter destructuring into N arguments. AllowClosureParameterDestructuring, @@ -175,10 +187,8 @@ enum class FixKind : uint8_t { /// function to `Void` to conform to expected result type. RemoveReturn, - /// Generic parameters could not be inferred and have to be explicitly - /// specified in the source. This fix groups all of the missing arguments - /// associated with single declaration. - ExplicitlySpecifyGenericArguments, + /// Default ambiguous generic arguments to \c Any + DefaultGenericArgument, /// Skip any unhandled constructs that occur within a closure argument that /// matches up with a @@ -207,6 +217,37 @@ enum class FixKind : uint8_t { /// If an array was passed to a variadic argument, give a specific diagnostic /// and offer to drop the brackets if it's a literal. ExpandArrayIntoVarargs, + + /// Remove extraneous call to something which can't be invoked e.g. + /// a variable, a property etc. + RemoveCall, + + AllowInvalidUseOfTrailingClosure, + + /// Allow an ephemeral argument conversion for a parameter marked as being + /// non-ephemeral. + TreatEphemeralAsNonEphemeral, + + /// Base type in reference to the contextual member e.g. `.foo` couldn't be + /// inferred and has to be specified explicitly. + SpecifyBaseTypeForContextualMember, + + /// Closure return type has to be explicitly specified because it can't be + /// inferred in current context e.g. because it's a multi-statement closure. + SpecifyClosureReturnType, + + /// Object literal type coudn't be inferred because the module where + /// the default type that implements the associated literal protocol + /// is declared was not imported. + SpecifyObjectLiteralTypeImport, + + /// Allow any type (and not just class or class-constrained type) to + /// be convertible to AnyObject. + AllowNonClassTypeToConvertToAnyObject, + + /// Member shadows a top-level name, such a name could only be accessed by + /// prefixing it with a module name. + AddQualifierToAccessTopLevelName, }; class ConstraintFix { @@ -225,21 +266,32 @@ class ConstraintFix { virtual ~ConstraintFix(); + template + const Fix *getAs() const { + return Fix::classof(this) ? static_cast(this) : nullptr; + } + FixKind getKind() const { return Kind; } bool isWarning() const { return IsWarning; } virtual std::string getName() const = 0; - /// Diagnose a failure associated with this fix given - /// root expression and information from constraint system. - virtual bool diagnose(Expr *root, bool asNote = false) const = 0; + /// Coalesce this fix with the given secondary fixes and diagnose the failure. + /// + /// The default implementation ignores \c secondaryFixes and calls + /// \c diagnose. + virtual bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const { + return diagnose(asNote); + } + + /// Diagnose a failure associated with this fix. + virtual bool diagnose(bool asNote = false) const = 0; void print(llvm::raw_ostream &Out) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const - LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Retrieve anchor expression associated with this fix. /// NOTE: such anchor comes directly from locator without @@ -251,33 +303,11 @@ class ConstraintFix { ConstraintSystem &getConstraintSystem() const { return CS; } }; -/// Introduce a '!' to force an optional unwrap. -class ForceOptional final : public ConstraintFix { - Type BaseType; - Type UnwrappedType; - - ForceOptional(ConstraintSystem &cs, Type baseType, Type unwrappedType, - ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::ForceOptional, locator), BaseType(baseType), - UnwrappedType(unwrappedType) { - assert(baseType && "Base type must not be null"); - assert(unwrappedType && "Unwrapped type must not be null"); - } - -public: - std::string getName() const override { return "force optional"; } - - bool diagnose(Expr *root, bool asNote = false) const override; - - static ForceOptional *create(ConstraintSystem &cs, Type baseType, - Type unwrappedType, ConstraintLocator *locator); -}; - /// Unwrap an optional base when we have a member access. class UnwrapOptionalBase final : public ConstraintFix { - DeclName MemberName; + DeclNameRef MemberName; - UnwrapOptionalBase(ConstraintSystem &cs, FixKind kind, DeclName member, + UnwrapOptionalBase(ConstraintSystem &cs, FixKind kind, DeclNameRef member, ConstraintLocator *locator) : ConstraintFix(cs, kind, locator), MemberName(member) { assert(kind == FixKind::UnwrapOptionalBase || @@ -289,13 +319,13 @@ class UnwrapOptionalBase final : public ConstraintFix { return "unwrap optional base of member lookup"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; - static UnwrapOptionalBase *create(ConstraintSystem &cs, DeclName member, + static UnwrapOptionalBase *create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); static UnwrapOptionalBase * - createWithOptionalResult(ConstraintSystem &cs, DeclName member, + createWithOptionalResult(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); }; @@ -307,48 +337,12 @@ class TreatRValueAsLValue final : public ConstraintFix { public: std::string getName() const override { return "treat rvalue as lvalue"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static TreatRValueAsLValue *create(ConstraintSystem &cs, ConstraintLocator *locator); }; - -/// Replace a coercion ('as') with a forced checked cast ('as!'). -class CoerceToCheckedCast final : public ConstraintFix { - CoerceToCheckedCast(ConstraintSystem &cs, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::CoerceToCheckedCast, locator) {} - -public: - std::string getName() const override { return "as to as!"; } - - bool diagnose(Expr *root, bool asNote = false) const override; - - static CoerceToCheckedCast *create(ConstraintSystem &cs, - ConstraintLocator *locator); -}; - -/// Mark function type as explicitly '@escaping'. -class MarkExplicitlyEscaping final : public ConstraintFix { - /// Sometimes function type has to be marked as '@escaping' - /// to be converted to some other generic type. - Type ConvertTo; - - MarkExplicitlyEscaping(ConstraintSystem &cs, ConstraintLocator *locator, - Type convertingTo = Type()) - : ConstraintFix(cs, FixKind::ExplicitlyEscaping, locator), - ConvertTo(convertingTo) {} - -public: - std::string getName() const override { return "add @escaping"; } - - bool diagnose(Expr *root, bool asNote = false) const override; - - static MarkExplicitlyEscaping *create(ConstraintSystem &cs, - ConstraintLocator *locator, - Type convertingTo = Type()); -}; - /// Arguments have labeling failures - missing/extraneous or incorrect /// labels attached to the, fix it by suggesting proper labels. class RelabelArguments final @@ -374,7 +368,7 @@ class RelabelArguments final return {getTrailingObjects(), NumLabels}; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static RelabelArguments *create(ConstraintSystem &cs, llvm::ArrayRef correctLabels, @@ -407,7 +401,7 @@ class MissingConformance final : public ConstraintFix { return "add missing protocol conformance"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static MissingConformance *forRequirement(ConstraintSystem &cs, Type type, Type protocolType, @@ -437,7 +431,7 @@ class SkipSameTypeRequirement final : public ConstraintFix { return "skip same-type generic requirement"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; Type lhsType() { return LHS; } Type rhsType() { return RHS; } @@ -461,7 +455,7 @@ class SkipSuperclassRequirement final : public ConstraintFix { return "skip superclass generic requirement"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; Type subclassType() { return LHS; } Type superclassType() { return RHS; } @@ -487,8 +481,8 @@ class ContextualMismatch : public ConstraintFix { : ConstraintFix(cs, FixKind::ContextualMismatch, locator), LHS(lhs), RHS(rhs) {} ContextualMismatch(ConstraintSystem &cs, FixKind kind, Type lhs, Type rhs, - ConstraintLocator *locator) - : ConstraintFix(cs, kind, locator), LHS(lhs), RHS(rhs) {} + ConstraintLocator *locator, bool warning = false) + : ConstraintFix(cs, kind, locator, warning), LHS(lhs), RHS(rhs) {} public: std::string getName() const override { return "fix contextual mismatch"; } @@ -496,12 +490,48 @@ class ContextualMismatch : public ConstraintFix { Type getFromType() const { return LHS; } Type getToType() const { return RHS; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static ContextualMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator); }; +/// Mark function type as explicitly '@escaping'. +class MarkExplicitlyEscaping final : public ContextualMismatch { + MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs, + ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::ExplicitlyEscaping, lhs, rhs, locator) { + } + +public: + std::string getName() const override { return "add @escaping"; } + + bool diagnose(bool asNote = false) const override; + + static MarkExplicitlyEscaping *create(ConstraintSystem &cs, Type lhs, + Type rhs, ConstraintLocator *locator); +}; + +/// Introduce a '!' to force an optional unwrap. +class ForceOptional final : public ContextualMismatch { + ForceOptional(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::ForceOptional, fromType, toType, + locator) { + assert(fromType && "Base type must not be null"); + assert(fromType->getOptionalObjectType() && + "Unwrapped type must not be null"); + } + +public: + std::string getName() const override { return "force optional"; } + + bool diagnose(bool asNote = false) const override; + + static ForceOptional *create(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintLocator *locator); +}; + /// This is a contextual mismatch between throwing and non-throwing /// function types, repair it by dropping `throws` attribute. class DropThrowsAttribute final : public ContextualMismatch { @@ -514,7 +544,7 @@ class DropThrowsAttribute final : public ContextualMismatch { public: std::string getName() const override { return "drop 'throws' attribute"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static DropThrowsAttribute *create(ConstraintSystem &cs, FunctionType *fromType, @@ -532,7 +562,7 @@ class ForceDowncast final : public ContextualMismatch { public: std::string getName() const override; - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static ForceDowncast *create(ConstraintSystem &cs, Type fromType, Type toType, ConstraintLocator *locator); @@ -547,7 +577,7 @@ class AddAddressOf final : public ContextualMismatch { public: std::string getName() const override { return "add address-of"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AddAddressOf *create(ConstraintSystem &cs, Type argTy, Type paramTy, ConstraintLocator *locator); @@ -563,7 +593,7 @@ class RemoveAddressOf final : public ContextualMismatch { return "remove extraneous use of `&`"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static RemoveAddressOf *create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator); @@ -605,7 +635,7 @@ class GenericArgumentsMismatch final return {getTrailingObjects(), NumMismatches}; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static GenericArgumentsMismatch *create(ConstraintSystem &cs, Type actual, Type required, @@ -660,7 +690,7 @@ class AutoClosureForwarding final : public ConstraintFix { public: std::string getName() const override { return "fix @autoclosure forwarding"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AutoClosureForwarding *create(ConstraintSystem &cs, ConstraintLocator *locator); @@ -678,7 +708,7 @@ class AllowAutoClosurePointerConversion final : public ContextualMismatch { return "allow pointer conversion for autoclosure result type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowAutoClosurePointerConversion *create(ConstraintSystem &cs, Type pointeeType, @@ -697,7 +727,7 @@ class RemoveUnwrap final : public ConstraintFix { return "remove unwrap operator `!` or `?`"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static RemoveUnwrap *create(ConstraintSystem &cs, Type baseType, ConstraintLocator *locator); @@ -712,7 +742,7 @@ class InsertExplicitCall final : public ConstraintFix { return "insert explicit `()` to make a call"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static InsertExplicitCall *create(ConstraintSystem &cs, ConstraintLocator *locator); @@ -736,7 +766,7 @@ class UsePropertyWrapper final : public ConstraintFix { return "insert '$' or '_' to use property wrapper type instead of wrapped type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static UsePropertyWrapper *create(ConstraintSystem &cs, VarDecl *wrapped, bool usingStorageWrapper, Type base, @@ -763,7 +793,7 @@ class UseWrappedValue final : public ConstraintFix { return "remove '$' or _ to use wrapped type instead of wrapper type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static UseWrappedValue *create(ConstraintSystem &cs, VarDecl *propertyWrapper, Type base, Type wrapper, @@ -779,7 +809,7 @@ class UseSubscriptOperator final : public ConstraintFix { return "replace '.subscript(...)' with subscript operator"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static UseSubscriptOperator *create(ConstraintSystem &cs, ConstraintLocator *locator); @@ -787,12 +817,21 @@ class UseSubscriptOperator final : public ConstraintFix { class DefineMemberBasedOnUse final : public ConstraintFix { Type BaseType; - DeclName Name; + DeclNameRef Name; - DefineMemberBasedOnUse(ConstraintSystem &cs, Type baseType, DeclName member, - ConstraintLocator *locator) + /// Whether or not the member error is already diagnosed. This can happen + /// when referencing an erroneous member, and the error is diagnosed at the + /// member declaration. + /// + /// We still want to define erroneous members based on use in order to find + /// a solution through the new diagnostic infrastructure, but we don't + /// want to report a second error message. + bool AlreadyDiagnosed; + + DefineMemberBasedOnUse(ConstraintSystem &cs, Type baseType, DeclNameRef member, + bool alreadyDiagnosed, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator), - BaseType(baseType), Name(member) {} + BaseType(baseType), Name(member), AlreadyDiagnosed(alreadyDiagnosed) {} public: std::string getName() const override { @@ -802,21 +841,21 @@ class DefineMemberBasedOnUse final : public ConstraintFix { "' based on its use"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static DefineMemberBasedOnUse *create(ConstraintSystem &cs, Type baseType, - DeclName member, + DeclNameRef member, bool alreadyDiagnosed, ConstraintLocator *locator); }; class AllowInvalidMemberRef : public ConstraintFix { Type BaseType; ValueDecl *Member; - DeclName Name; + DeclNameRef Name; protected: AllowInvalidMemberRef(ConstraintSystem &cs, FixKind kind, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : ConstraintFix(cs, kind, locator), BaseType(baseType), Member(member), Name(name) {} @@ -826,12 +865,12 @@ class AllowInvalidMemberRef : public ConstraintFix { ValueDecl *getMember() const { return Member; } - DeclName getMemberName() const { return Name; } + DeclNameRef getMemberName() const { return Name; } }; class AllowMemberRefOnExistential final : public AllowInvalidMemberRef { AllowMemberRefOnExistential(ConstraintSystem &cs, Type baseType, - DeclName memberName, ValueDecl *member, + DeclNameRef memberName, ValueDecl *member, ConstraintLocator *locator) : AllowInvalidMemberRef(cs, FixKind::AllowMemberRefOnExistential, baseType, member, memberName, locator) {} @@ -844,17 +883,17 @@ class AllowMemberRefOnExistential final : public AllowInvalidMemberRef { "' on value of protocol type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowMemberRefOnExistential *create(ConstraintSystem &cs, Type baseType, ValueDecl *member, - DeclName memberName, + DeclNameRef memberName, ConstraintLocator *locator); }; class AllowTypeOrInstanceMember final : public AllowInvalidMemberRef { AllowTypeOrInstanceMember(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : AllowInvalidMemberRef(cs, FixKind::AllowTypeOrInstanceMember, baseType, member, name, locator) { @@ -866,10 +905,10 @@ class AllowTypeOrInstanceMember final : public AllowInvalidMemberRef { return "allow access to instance member on type or a type member on instance"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowTypeOrInstanceMember *create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName usedName, + ValueDecl *member, DeclNameRef usedName, ConstraintLocator *locator); }; @@ -884,7 +923,7 @@ class AllowInvalidPartialApplication final : public ConstraintFix { return "allow partially applied 'mutating' method"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowInvalidPartialApplication *create(bool isWarning, ConstraintSystem &cs, @@ -915,7 +954,7 @@ class AllowInvalidInitRef final : public ConstraintFix { return "allow invalid initializer reference"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowInvalidInitRef * dynamicOnMetatype(ConstraintSystem &cs, Type baseTy, ConstructorDecl *init, @@ -940,25 +979,71 @@ class AllowInvalidInitRef final : public ConstraintFix { }; class AllowTupleTypeMismatch final : public ContextualMismatch { + /// If this is an element mismatch, \c Index is the element index where the + /// type mismatch occurred. If this is an arity or label mismatch, \c Index + /// will be \c None. + Optional Index; + AllowTupleTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs, - ConstraintLocator *locator) + ConstraintLocator *locator, Optional index) : ContextualMismatch(cs, FixKind::AllowTupleTypeMismatch, lhs, rhs, - locator) {} + locator), Index(index) {} public: static AllowTupleTypeMismatch *create(ConstraintSystem &cs, Type lhs, - Type rhs, ConstraintLocator *locator); + Type rhs, ConstraintLocator *locator, + Optional index = None); + + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AllowTupleTypeMismatch; + } std::string getName() const override { return "fix tuple mismatches in type and arity"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool isElementMismatch() const { + return Index.hasValue(); + } + + bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const override; + + bool diagnose(bool asNote = false) const override; +}; + +class AllowFunctionTypeMismatch final : public ContextualMismatch { + /// The index of the parameter where the type mismatch occurred. + unsigned ParamIndex; + + AllowFunctionTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs, + ConstraintLocator *locator, unsigned index) + : ContextualMismatch(cs, FixKind::AllowFunctionTypeMismatch, lhs, rhs, + locator), ParamIndex(index) {} + +public: + static AllowFunctionTypeMismatch *create(ConstraintSystem &cs, Type lhs, + Type rhs, ConstraintLocator *locator, + unsigned index); + + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AllowFunctionTypeMismatch; + } + + std::string getName() const override { + return "allow function type mismatch"; + } + + bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const override; + + bool diagnose(bool asNote = false) const override; }; + class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef { AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : AllowInvalidMemberRef(cs, FixKind::AllowMutatingMemberOnRValueBase, baseType, member, name, locator) {} @@ -968,11 +1053,11 @@ class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef { return "allow `mutating` method on r-value base"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowMutatingMemberOnRValueBase * - create(ConstraintSystem &cs, Type baseType, ValueDecl *member, DeclName name, - ConstraintLocator *locator); + create(ConstraintSystem &cs, Type baseType, ValueDecl *member, + DeclNameRef name, ConstraintLocator *locator); }; class AllowClosureParamDestructuring final : public ConstraintFix { @@ -989,7 +1074,7 @@ class AllowClosureParamDestructuring final : public ConstraintFix { return "allow closure parameter destructuring"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowClosureParamDestructuring *create(ConstraintSystem &cs, FunctionType *contextualType, @@ -1022,7 +1107,7 @@ class AddMissingArguments final return {getTrailingObjects(), NumSynthesized}; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AddMissingArguments *create(ConstraintSystem &cs, llvm::ArrayRef synthesizedArgs, @@ -1034,6 +1119,55 @@ class AddMissingArguments final } }; +class RemoveExtraneousArguments final + : public ConstraintFix, + private llvm::TrailingObjects< + RemoveExtraneousArguments, + std::pair> { + friend TrailingObjects; + + using IndexedParam = std::pair; + + FunctionType *ContextualType; + unsigned NumExtraneous; + + RemoveExtraneousArguments(ConstraintSystem &cs, FunctionType *contextualType, + llvm::ArrayRef extraArgs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::RemoveExtraneousArguments, locator), + ContextualType(contextualType), NumExtraneous(extraArgs.size()) { + std::uninitialized_copy(extraArgs.begin(), extraArgs.end(), + getExtraArgumentsBuf().begin()); + } + +public: + std::string getName() const override { return "remove extraneous argument(s)"; } + + ArrayRef getExtraArguments() const { + return {getTrailingObjects(), NumExtraneous}; + } + + bool diagnose(bool asNote = false) const override; + + /// FIXME(diagnostics): Once `resolveDeclRefExpr` is gone this + /// logic would be obsolete. + /// + /// Determine whether presence of extraneous arguments indicates + /// potential name shadowing problem with local `min`/`max` shadowing + /// global definitions with different number of arguments. + static bool isMinMaxNameShadowing(ConstraintSystem &cs, + ConstraintLocatorBuilder locator); + + static RemoveExtraneousArguments * + create(ConstraintSystem &cs, FunctionType *contextualType, + llvm::ArrayRef extraArgs, ConstraintLocator *locator); + +private: + MutableArrayRef getExtraArgumentsBuf() { + return {getTrailingObjects(), NumExtraneous}; + } +}; + class MoveOutOfOrderArgument final : public ConstraintFix { using ParamBinding = SmallVector; @@ -1054,7 +1188,7 @@ class MoveOutOfOrderArgument final : public ConstraintFix { return "move out-of-order argument to correct position"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static MoveOutOfOrderArgument *create(ConstraintSystem &cs, unsigned argIdx, @@ -1065,7 +1199,7 @@ class MoveOutOfOrderArgument final : public ConstraintFix { class AllowInaccessibleMember final : public AllowInvalidMemberRef { AllowInaccessibleMember(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator) : AllowInvalidMemberRef(cs, FixKind::AllowInaccessibleMember, baseType, member, name, locator) {} @@ -1075,10 +1209,10 @@ class AllowInaccessibleMember final : public AllowInvalidMemberRef { return "allow inaccessible member reference"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowInaccessibleMember *create(ConstraintSystem &cs, Type baseType, - ValueDecl *member, DeclName name, + ValueDecl *member, DeclNameRef name, ConstraintLocator *locator); }; @@ -1092,7 +1226,7 @@ class AllowAnyObjectKeyPathRoot final : public ConstraintFix { return "allow anyobject as root type for a keypath"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowAnyObjectKeyPathRoot *create(ConstraintSystem &cs, ConstraintLocator *locator); @@ -1112,7 +1246,7 @@ class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix { return "treat keypath subscript index as conforming to Hashable"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static TreatKeyPathSubscriptIndexAsHashable * create(ConstraintSystem &cs, Type type, ConstraintLocator *locator); @@ -1151,7 +1285,7 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix { llvm_unreachable("covered switch"); } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; /// Determine whether give reference requires a fix and produce one. static AllowInvalidRefInKeyPath * @@ -1170,7 +1304,7 @@ class RemoveReturn final : public ConstraintFix { public: std::string getName() const override { return "remove or omit return type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static RemoveReturn *create(ConstraintSystem &cs, ConstraintLocator *locator); }; @@ -1185,50 +1319,39 @@ class CollectionElementContextualMismatch final : public ContextualMismatch { return "fix collection element contextual mismatch"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static CollectionElementContextualMismatch * create(ConstraintSystem &cs, Type srcType, Type dstType, ConstraintLocator *locator); }; -class ExplicitlySpecifyGenericArguments final - : public ConstraintFix, - private llvm::TrailingObjects { - friend TrailingObjects; - - unsigned NumMissingParams; +class DefaultGenericArgument final : public ConstraintFix { + GenericTypeParamType *Param; - ExplicitlySpecifyGenericArguments(ConstraintSystem &cs, - ArrayRef params, - ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::ExplicitlySpecifyGenericArguments, locator), - NumMissingParams(params.size()) { - assert(!params.empty()); - std::uninitialized_copy(params.begin(), params.end(), - getParametersBuf().begin()); - } + DefaultGenericArgument(ConstraintSystem &cs, GenericTypeParamType *param, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::DefaultGenericArgument, locator), + Param(param) {} public: - std::string getName() const override { - return "default missing generic arguments to `Any`"; + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::DefaultGenericArgument; } - ArrayRef getParameters() const { - return {getTrailingObjects(), NumMissingParams}; + std::string getName() const override { + auto paramName = Param->getString(); + return "default generic argument '" + paramName + "' to 'Any'"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool coalesceAndDiagnose(ArrayRef secondaryFixes, + bool asNote = false) const override; - static ExplicitlySpecifyGenericArguments * - create(ConstraintSystem &cs, ArrayRef params, - ConstraintLocator *locator); + bool diagnose(bool asNote = false) const override; -private: - MutableArrayRef getParametersBuf() { - return {getTrailingObjects(), NumMissingParams}; - } + static DefaultGenericArgument *create(ConstraintSystem &cs, + GenericTypeParamType *param, + ConstraintLocator *locator); }; class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { @@ -1252,7 +1375,7 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { return "skip unhandled constructs when applying a function builder"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static SkipUnhandledConstructInFunctionBuilder * create(ConstraintSystem &cs, UnhandledNode unhandledNode, @@ -1274,7 +1397,7 @@ class AllowTupleSplatForSingleParameter final : public ConstraintFix { return "allow single parameter tuple splat"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; /// Apply this fix to given arguments/parameters and return `true` /// this fix is not applicable and solver can't continue, `false` @@ -1295,7 +1418,7 @@ class IgnoreContextualType : public ContextualMismatch { return "ignore specified contextual type"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static IgnoreContextualType *create(ConstraintSystem &cs, Type resultTy, Type specifiedTy, @@ -1312,7 +1435,7 @@ class IgnoreAssignmentDestinationType final : public ContextualMismatch { return "ignore type of the assignment destination"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static IgnoreAssignmentDestinationType *create(ConstraintSystem &cs, Type sourceTy, Type destTy, @@ -1332,7 +1455,7 @@ class AllowInOutConversion final : public ContextualMismatch { return "allow conversions between argument/parameter marked as `inout`"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowInOutConversion *create(ConstraintSystem &cs, Type argType, Type paramType, @@ -1347,15 +1470,16 @@ class AllowArgumentMismatch : public ContextualMismatch { paramType, locator) {} AllowArgumentMismatch(ConstraintSystem &cs, FixKind kind, Type argType, - Type paramType, ConstraintLocator *locator) - : ContextualMismatch(cs, kind, argType, paramType, locator) {} + Type paramType, ConstraintLocator *locator, + bool warning = false) + : ContextualMismatch(cs, kind, argType, paramType, locator, warning) {} public: std::string getName() const override { return "allow argument to parameter type conversion mismatch"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static AllowArgumentMismatch *create(ConstraintSystem &cs, Type argType, Type paramType, @@ -1374,7 +1498,7 @@ class ExpandArrayIntoVarargs final : public AllowArgumentMismatch { return "cannot pass Array elements as variadic arguments"; } - bool diagnose(Expr *root, bool asNote = false) const override; + bool diagnose(bool asNote = false) const override; static ExpandArrayIntoVarargs *attempt(ConstraintSystem &cs, Type argType, Type paramType, @@ -1414,7 +1538,180 @@ class UseValueTypeOfRawRepresentative final : public AllowArgumentMismatch { ConstraintLocatorBuilder locator); }; +/// Replace a coercion ('as') with a forced checked cast ('as!'). +class CoerceToCheckedCast final : public ContextualMismatch { + CoerceToCheckedCast(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::CoerceToCheckedCast, fromType, toType, + locator) {} + +public: + std::string getName() const { return "as to as!"; } + + bool diagnose(bool asNote = false) const; + + static CoerceToCheckedCast *attempt(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator); +}; + +class RemoveInvalidCall final : public ConstraintFix { + RemoveInvalidCall(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::RemoveCall, locator) {} + +public: + std::string getName() const { + return "remove extraneous call from value of non-function type"; + } + + bool diagnose(bool asNote = false) const; + + static RemoveInvalidCall *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + +class AllowInvalidUseOfTrailingClosure final : public AllowArgumentMismatch { + AllowInvalidUseOfTrailingClosure(ConstraintSystem &cs, Type argType, + Type paramType, ConstraintLocator *locator) + : AllowArgumentMismatch(cs, FixKind::AllowInvalidUseOfTrailingClosure, + argType, paramType, locator) {} + +public: + std::string getName() const { + return "allow invalid use of trailing closure"; + } + + bool diagnose(bool asNote = false) const; + + static AllowInvalidUseOfTrailingClosure *create(ConstraintSystem &cs, + Type argType, Type paramType, + ConstraintLocator *locator); +}; + +class TreatEphemeralAsNonEphemeral final : public AllowArgumentMismatch { + ConversionRestrictionKind ConversionKind; + + TreatEphemeralAsNonEphemeral(ConstraintSystem &cs, ConstraintLocator *locator, + Type srcType, Type dstType, + ConversionRestrictionKind conversionKind, + bool downgradeToWarning) + : AllowArgumentMismatch(cs, FixKind::TreatEphemeralAsNonEphemeral, + srcType, dstType, locator, downgradeToWarning), + ConversionKind(conversionKind) {} + +public: + ConversionRestrictionKind getConversionKind() const { return ConversionKind; } + std::string getName() const override; + + bool diagnose(bool asNote = false) const override; + + static TreatEphemeralAsNonEphemeral * + create(ConstraintSystem &cs, ConstraintLocator *locator, Type srcType, + Type dstType, ConversionRestrictionKind conversionKind, + bool downgradeToWarning); +}; + +class SpecifyBaseTypeForContextualMember final : public ConstraintFix { + DeclNameRef MemberName; + + SpecifyBaseTypeForContextualMember(ConstraintSystem &cs, DeclNameRef member, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyBaseTypeForContextualMember, locator), + MemberName(member) {} + +public: + std::string getName() const { + const auto baseName = MemberName.getBaseName(); + return "specify base type in reference to member '" + + baseName.userFacingName().str() + "'"; + } + + bool diagnose(bool asNote = false) const; + + static SpecifyBaseTypeForContextualMember * + create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator); +}; + +class SpecifyClosureReturnType final : public ConstraintFix { + SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {} + +public: + std::string getName() const { + return "specify closure return type"; + } + + bool diagnose(bool asNote = false) const; + + static SpecifyClosureReturnType *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + +class SpecifyObjectLiteralTypeImport final : public ConstraintFix { + SpecifyObjectLiteralTypeImport(ConstraintSystem &cs, ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::SpecifyObjectLiteralTypeImport, locator) {} + +public: + std::string getName() const { + return "import required module to gain access to a default literal type"; + } + + bool diagnose(bool asNote = false) const; + + static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + +class AddQualifierToAccessTopLevelName final : public ConstraintFix { + AddQualifierToAccessTopLevelName(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddQualifierToAccessTopLevelName, locator) {} + +public: + std::string getName() const { + return "qualify reference to access top-level function"; + } + + bool diagnose(bool asNote = false) const; + + static AddQualifierToAccessTopLevelName *create(ConstraintSystem &cs, + ConstraintLocator *locator); +}; + +class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch { + AllowNonClassTypeToConvertToAnyObject(ConstraintSystem &cs, Type type, + ConstraintLocator *locator); + +public: + std::string getName() const { + return "allow non-class type to convert to 'AnyObject'"; + } + + bool diagnose(bool asNote = false) const; + + static AllowNonClassTypeToConvertToAnyObject * + create(ConstraintSystem &cs, Type type, ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift +namespace llvm { + template <> + struct DenseMapInfo { + using FixKind = swift::constraints::FixKind; + static inline FixKind getEmptyKey() { + return static_cast(0); + } + static inline FixKind getTombstoneKey() { + return static_cast(1); + } + static unsigned getHashValue(FixKind kind) { + return static_cast(kind); + } + static bool isEqual(FixKind lhs, FixKind rhs) { + return lhs == rhs; + } + }; +} + #endif // SWIFT_SEMA_CSFIX_H diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index faa0cb21ee3b6..2cb35341bde68 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -143,16 +143,15 @@ namespace { return { false, expr }; } + if (isa(expr)) + return {false, expr}; + // Store top-level binary exprs for further analysis. if (isa(expr) || // Literal exprs are contextually typed, so store them off as well. isa(expr) || - // We'd like to take a look at implicit closure params, so store - // them. - isa(expr) || - // We'd like to look at the elements of arrays and dictionaries. isa(expr) || isa(expr) || @@ -258,7 +257,7 @@ namespace { if (isa(expr)) { - return { true, expr }; + return {false, expr}; } if (auto FVE = dyn_cast(expr)) { @@ -478,10 +477,9 @@ namespace { } if (lti.haveFloatLiteral) { - if (auto floatProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByFloatLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(floatProto, CS.DC)) { + if (auto floatProto = CS.getASTContext().getProtocol( + KnownProtocolKind::ExpressibleByFloatLiteral)) { + if (auto defaultType = TypeChecker::getDefaultType(floatProto, CS.DC)) { if (!CS.getFavoredType(expr)) { CS.setFavoredType(expr, defaultType.getPointer()); } @@ -491,10 +489,9 @@ namespace { } if (lti.haveIntLiteral) { - if (auto intProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByIntegerLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(intProto, CS.DC)) { + if (auto intProto = CS.getASTContext().getProtocol( + KnownProtocolKind::ExpressibleByIntegerLiteral)) { + if (auto defaultType = TypeChecker::getDefaultType(intProto, CS.DC)) { if (!CS.getFavoredType(expr)) { CS.setFavoredType(expr, defaultType.getPointer()); } @@ -504,12 +501,11 @@ namespace { } if (lti.haveStringLiteral) { - if (auto stringProto = - CS.TC.Context.getProtocol( - KnownProtocolKind::ExpressibleByStringLiteral)) { - if (auto defaultType = CS.TC.getDefaultType(stringProto, CS.DC)) { + if (auto stringProto = CS.getASTContext().getProtocol( + KnownProtocolKind::ExpressibleByStringLiteral)) { + if (auto defTy = TypeChecker::getDefaultType(stringProto, CS.DC)) { if (!CS.getFavoredType(expr)) { - CS.setFavoredType(expr, defaultType.getPointer()); + CS.setFavoredType(expr, defTy.getPointer()); } return true; } @@ -521,9 +517,7 @@ namespace { /// Determine whether the given parameter type and argument should be /// "favored" because they match exactly. - bool isFavoredParamAndArg(ConstraintSystem &CS, - Type paramTy, - Type argTy, + bool isFavoredParamAndArg(ConstraintSystem &CS, Type paramTy, Type argTy, Type otherArgTy = Type()) { // Determine the argument type. argTy = argTy->getWithoutSpecifierType(); @@ -535,8 +529,7 @@ namespace { llvm::SmallSetVector literalProtos; if (auto argTypeVar = argTy->getAs()) { auto constraints = CS.getConstraintGraph().gatherConstraints( - argTypeVar, - ConstraintGraph::GatheringKind::EquivalenceClass, + argTypeVar, ConstraintGraph::GatheringKind::EquivalenceClass, [](Constraint *constraint) { return constraint->getKind() == ConstraintKind::LiteralConformsTo; }); @@ -550,7 +543,6 @@ namespace { if (otherArgTy) otherArgTy = otherArgTy->getWithoutSpecifierType(); - auto &tc = CS.getTypeChecker(); for (auto literalProto : literalProtos) { // If there is another, concrete argument, check whether it's type // conforms to the literal protocol and test against it directly. @@ -558,10 +550,13 @@ namespace { // the literal. if (otherArgTy && otherArgTy->getAnyNominal()) { if (otherArgTy->isEqual(paramTy) && - TypeChecker::conformsToProtocol(otherArgTy, literalProto, CS.DC, - ConformanceCheckFlags::InExpression)) + TypeChecker::conformsToProtocol( + otherArgTy, literalProto, CS.DC, + ConformanceCheckFlags::InExpression)) { return true; - } else if (Type defaultType = tc.getDefaultType(literalProto, CS.DC)) { + } + } else if (Type defaultType = + TypeChecker::getDefaultType(literalProto, CS.DC)) { // If there is a default type for the literal protocol, check whether // it is the same as the parameter type. // Check whether there is a default type to compare against. @@ -572,7 +567,7 @@ namespace { return false; } - + /// Favor certain overloads in a call based on some basic analysis /// of the overload set and call arguments. /// @@ -898,7 +893,10 @@ namespace { CS.getFavoredType(parenExpr->getSubExpr())); } } - + + if (isa(expr)) + return {false, expr}; + return { true, expr }; } @@ -921,7 +919,7 @@ namespace { class ConstraintGenerator : public ExprVisitor { ConstraintSystem &CS; DeclContext *CurDC; - SmallVector DCStack; + ConstraintSystemPhase CurrPhase; static const unsigned numEditorPlaceholderVariables = 2; @@ -933,9 +931,19 @@ namespace { = { nullptr, nullptr }; unsigned currentEditorPlaceholderVariable = 0; + /// Returns false and emits the specified diagnostic if the member reference + /// base is a nil literal. Returns true otherwise. + bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { + if (auto nilLiteral = dyn_cast(base)) { + CS.getASTContext().Diags.diagnose(nilLiteral->getLoc(), diagnostic); + return false; + } + return true; + } + /// Add constraints for a reference to a named member of the given /// base type, and return the type of such a reference. - Type addMemberRefConstraints(Expr *expr, Expr *base, DeclName name, + Type addMemberRefConstraints(Expr *expr, Expr *base, DeclNameRef name, FunctionRefKind functionRefKind, ArrayRef outerAlternatives) { // The base must have a member of the given name, such that accessing @@ -963,8 +971,6 @@ namespace { if (!decl) return nullptr; - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)decl->getInterfaceType(); if (decl->isInvalid()) return nullptr; @@ -1082,8 +1088,9 @@ namespace { CS.addBindOverloadConstraint(memberTy, choice, memberLocator, CurDC); } else { - CS.addValueMemberConstraint(baseTy, DeclBaseName::createSubscript(), - memberTy, CurDC, FunctionRefKind::DoubleApply, + CS.addValueMemberConstraint(baseTy, DeclNameRef::createSubscript(), + memberTy, CurDC, + FunctionRefKind::DoubleApply, /*outerAlternatives=*/{}, memberLocator); } @@ -1113,28 +1120,20 @@ namespace { public: ConstraintGenerator(ConstraintSystem &CS, DeclContext *DC) - : CS(CS), CurDC(DC ? DC : CS.DC) { } + : CS(CS), CurDC(DC ? DC : CS.DC), CurrPhase(CS.getPhase()) { + // Although constraint system is initialized in `constraint + // generation` phase, we have to set it here manually because e.g. + // function builders could generate constraints for its body + // in the middle of the solving. + CS.setPhase(ConstraintSystemPhase::ConstraintGeneration); + } virtual ~ConstraintGenerator() { - // We really ought to have this assertion: - // assert(DCStack.empty() && CurDC == CS.DC); - // Unfortunately, ASTWalker is really bad at letting us establish - // invariants like this because walkToExprPost isn't called if - // something early-aborts the walk. + CS.setPhase(CurrPhase); } ConstraintSystem &getConstraintSystem() const { return CS; } - void enterClosure(ClosureExpr *closure) { - DCStack.push_back(CurDC); - CurDC = closure; - } - - void exitClosure(ClosureExpr *closure) { - assert(CurDC == closure); - CurDC = DCStack.pop_back_val(); - } - virtual Type visitErrorExpr(ErrorExpr *E) { // FIXME: Can we do anything with error expressions at this point? return nullptr; @@ -1147,12 +1146,37 @@ namespace { TVO_CanBindToNoEscape); } + Type visitNilLiteralExpr(NilLiteralExpr *expr) { + auto &DE = CS.getASTContext().Diags; + // If this is a standalone `nil` literal expression e.g. + // `_ = nil`, let's diagnose it here because solver can't + // attempt any types for it. + if (!CS.isExprBeingDiagnosed(expr)) { + auto *parentExpr = CS.getParentExpr(expr); + + // `_ = nil` + if (auto *assignment = dyn_cast_or_null(parentExpr)) { + if (isa(assignment->getDest())) { + DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); + return Type(); + } + } + + if (!parentExpr && !CS.getContextualType(expr)) { + DE.diagnose(expr->getLoc(), diag::unresolved_nil_literal); + return Type(); + } + } + + return visitLiteralExpr(expr); + } + Type visitLiteralExpr(LiteralExpr *expr) { // If the expression has already been assigned a type; just use that type. if (expr->getType()) return expr->getType(); - auto protocol = CS.getTypeChecker().getLiteralProtocol(expr); + auto protocol = TypeChecker::getLiteralProtocol(CS.getASTContext(), expr); if (!protocol) return nullptr; @@ -1168,12 +1192,13 @@ namespace { Type visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) { // Dig out the ExpressibleByStringInterpolation protocol. - auto &tc = CS.getTypeChecker(); - auto interpolationProto - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByStringInterpolation); + auto &ctx = CS.getASTContext(); + auto interpolationProto = TypeChecker::getProtocol( + ctx, expr->getLoc(), + KnownProtocolKind::ExpressibleByStringInterpolation); if (!interpolationProto) { - tc.diagnose(expr->getStartLoc(), diag::interpolation_missing_proto); + ctx.Diags.diagnose(expr->getStartLoc(), + diag::interpolation_missing_proto); return nullptr; } @@ -1189,9 +1214,10 @@ namespace { if (auto appendingExpr = expr->getAppendingExpr()) { auto associatedTypeDecl = interpolationProto->getAssociatedType( - tc.Context.Id_StringInterpolation); + ctx.Id_StringInterpolation); if (associatedTypeDecl == nullptr) { - tc.diagnose(expr->getStartLoc(), diag::interpolation_broken_proto); + ctx.Diags.diagnose(expr->getStartLoc(), + diag::interpolation_broken_proto); return nullptr; } @@ -1213,18 +1239,18 @@ namespace { switch (expr->getKind()) { case MagicIdentifierLiteralExpr::Column: case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: case MagicIdentifierLiteralExpr::Line: return visitLiteralExpr(expr); case MagicIdentifierLiteralExpr::DSOHandle: { // #dsohandle has type UnsafeMutableRawPointer. - auto &tc = CS.getTypeChecker(); - if (tc.requirePointerArgumentIntrinsics(expr->getLoc())) + auto &ctx = CS.getASTContext(); + if (TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc())) return nullptr; - auto unsafeRawPointer = - CS.getASTContext().getUnsafeRawPointerDecl(); + auto unsafeRawPointer = ctx.getUnsafeRawPointerDecl(); return unsafeRawPointer->getDeclaredType(); } } @@ -1241,17 +1267,18 @@ namespace { if (expr->getType()) return expr->getType(); - auto &tc = CS.getTypeChecker(); - auto protocol = tc.getLiteralProtocol(expr); + auto &de = CS.getASTContext().Diags; + auto protocol = TypeChecker::getLiteralProtocol(CS.getASTContext(), expr); if (!protocol) { - tc.diagnose(expr->getLoc(), diag::use_unknown_object_literal_protocol, + de.diagnose(expr->getLoc(), diag::use_unknown_object_literal_protocol, expr->getLiteralKindPlainName()); return nullptr; } auto tv = CS.createTypeVariable(exprLoc, TVO_PrefersSubtypeBinding | - TVO_CanBindToNoEscape); + TVO_CanBindToNoEscape | + TVO_CanBindToHole); CS.addConstraint(ConstraintKind::LiteralConformsTo, tv, protocol->getDeclaredType(), @@ -1263,15 +1290,18 @@ namespace { // all the redundant stuff about literals (leaving e.g. "red:"). // Constraint application will quietly rewrite the type of 'args' to // use the right labels before forming the call to the initializer. - DeclName constrName = tc.getObjectLiteralConstructorName(expr); + auto constrName = + TypeChecker::getObjectLiteralConstructorName(CS.getASTContext(), + expr); assert(constrName); auto *constr = dyn_cast_or_null( protocol->getSingleRequirement(constrName)); if (!constr) { - tc.diagnose(protocol, diag::object_literal_broken_proto); + de.diagnose(protocol, diag::object_literal_broken_proto); return nullptr; } - auto constrParamType = tc.getObjectLiteralParameterType(expr, constr); + auto constrParamType = + TypeChecker::getObjectLiteralParameterType(expr, constr); // Extract the arguments. SmallVector args; @@ -1281,8 +1311,9 @@ namespace { SmallVector params; AnyFunctionType::decomposeInput(constrParamType, params); + auto funcType = constr->getMethodInterfaceType()->castTo(); ::matchCallArguments( - CS, args, params, ConstraintKind::ArgumentConversion, + CS, funcType, args, params, ConstraintKind::ArgumentConversion, CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument)); Type result = tv; @@ -1293,56 +1324,56 @@ namespace { } Type visitDeclRefExpr(DeclRefExpr *E) { - // If this is a ParamDecl for a closure argument that has an Unresolved - // type, then this is a situation where CSDiags is trying to perform - // error recovery within a ClosureExpr. Just create a new type variable - // for the decl that isn't bound to anything. This will ensure that it - // is considered ambiguous. - if (auto *VD = dyn_cast(E->getDecl())) { - if (VD->hasInterfaceType() && - VD->getInterfaceType()->is()) { - return CS.createTypeVariable(CS.getConstraintLocator(E), - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - } - } - - // If we're referring to an invalid declaration, don't type-check. - // - // FIXME: If the decl is in error, we get no information from this. - // We may, alternatively, want to use a type variable in that case, - // and possibly infer the type of the variable that way. - auto oldInterfaceTy = E->getDecl()->getInterfaceType(); - if (E->getDecl()->isInvalid()) { - CS.setType(E, oldInterfaceTy); - return nullptr; - } - auto locator = CS.getConstraintLocator(E); - // If this is a 'var' or 'let' declaration with already - // resolved type, let's favor it. + Type knownType; if (auto *VD = dyn_cast(E->getDecl())) { - Type type; - if (VD->hasInterfaceType()) { - type = VD->getInterfaceType(); - if (type->hasTypeParameter()) - type = VD->getDeclContext()->mapTypeIntoContext(type); - CS.setFavoredType(E, type.getPointer()); + knownType = CS.getTypeIfAvailable(VD); + if (!knownType && + !(isa(VD) && + isa(VD->getDeclContext()) && + CS.Options.contains( + ConstraintSystemFlags::SubExpressionDiagnostics))) + knownType = VD->getInterfaceType(); + + if (knownType) { + // If this is a ParamDecl for a closure argument that is a hole, + // then this is a situation where CSDiags is trying to perform + // error recovery within a ClosureExpr. Just create a new type + // variable for the decl that isn't bound to anything. + // This will ensure that it is considered ambiguous. + if (knownType && knownType->isHole()) { + return CS.createTypeVariable(locator, + TVO_CanBindToLValue | + TVO_CanBindToNoEscape); + } + + // If the known type has an error, bail out. + if (knownType->hasError()) { + if (!CS.hasType(E)) + CS.setType(E, knownType); + return nullptr; + } + + // Set the favored type for this expression to the known type. + if (knownType->hasTypeParameter()) + knownType = VD->getDeclContext()->mapTypeIntoContext(knownType); + CS.setFavoredType(E, knownType.getPointer()); } - // This can only happen when failure diangostics is trying + // This can only happen when failure diagnostics is trying // to type-check expressions inside of a single-statement // closure which refer to anonymous parameters, in this case // let's either use type as written or allocate a fresh type // variable, just like we do for closure type. + // FIXME: We should eliminate this case. if (auto *PD = dyn_cast(VD)) { if (!CS.hasType(PD)) { - if (type && type->hasUnboundGenericType()) - type = CS.openUnboundGenericType(type, locator); + if (knownType && knownType->hasUnboundGenericType()) + knownType = CS.openUnboundGenericType(knownType, locator); CS.setType( - PD, type ? type + PD, knownType ? knownType : CS.createTypeVariable(locator, TVO_CanBindToLValue | TVO_CanBindToNoEscape)); @@ -1350,6 +1381,16 @@ namespace { } } + // If we're referring to an invalid declaration, don't type-check. + // + // FIXME: If the decl is in error, we get no information from this. + // We may, alternatively, want to use a type variable in that case, + // and possibly infer the type of the variable that way. + if (!knownType && E->getDecl()->isInvalid()) { + CS.setType(E, E->getDecl()->getInterfaceType()); + return nullptr; + } + // Create an overload choice referencing this declaration and immediately // resolve it. This records the overload for use later. auto tv = CS.createTypeVariable(locator, @@ -1385,7 +1426,8 @@ namespace { TypeResolutionOptions options(TypeResolverContext::InExpression); options |= TypeResolutionFlags::AllowUnboundGenerics; bool hadError = TypeChecker::validateType( - CS.TC.Context, loc, TypeResolution::forContextual(CS.DC), options); + CS.getASTContext(), loc, TypeResolution::forContextual(CS.DC), + options); return hadError ? Type() : loc.getType(); } @@ -1427,8 +1469,6 @@ namespace { // If the result is invalid, skip it. // FIXME: Note this as invalid, in case we don't find a solution, // so we don't let errors cascade further. - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)decls[i]->getInterfaceType(); if (decls[i]->isInvalid()) continue; @@ -1475,10 +1515,13 @@ namespace { auto memberLocator = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); - auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape); - auto memberTy = CS.createTypeVariable(memberLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); + + // Since base type in this case is completely dependent on context it + // should be marked as a potential hole. + auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape | + TVO_CanBindToHole); + auto memberTy = CS.createTypeVariable( + memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); // An unresolved member expression '.member' is modeled as a value member // constraint @@ -1542,18 +1585,19 @@ namespace { // If this is Builtin.type_join*, just return any type and move // on since we're going to discard this, and creating any type // variables for the reference will cause problems. - auto typeOperation = getTypeOperation(expr, CS.getASTContext()); + auto &ctx = CS.getASTContext(); + auto typeOperation = getTypeOperation(expr, ctx); if (typeOperation != TypeOperation::None) - return CS.getASTContext().TheAnyType; + return ctx.TheAnyType; // If this is `Builtin.trigger_fallback_diagnostic()`, fail // without producing any diagnostics, in order to test fallback error. - if (isTriggerFallbackDiagnosticBuiltin(expr, CS.getASTContext())) + if (isTriggerFallbackDiagnosticBuiltin(expr, ctx)) return Type(); // Open a member constraint for constructor delegations on the // subexpr type. - if (CS.TC.getSelfForInitDelegationInConstructor(CS.DC, expr)) { + if (TypeChecker::getSelfForInitDelegationInConstructor(CS.DC, expr)) { auto baseTy = CS.getType(expr->getBase()) ->getWithoutSpecifierType(); @@ -1564,7 +1608,7 @@ namespace { // is really more like: // self = Self.init() // self.super = Super.init() - baseTy = MetatypeType::get(baseTy, CS.getASTContext()); + baseTy = MetatypeType::get(baseTy, ctx); auto methodTy = CS.createTypeVariable( CS.getConstraintLocator(expr, @@ -1608,11 +1652,11 @@ namespace { // We currently only support explicit specialization of generic types. // FIXME: We could support explicit function specialization. - auto &tc = CS.getTypeChecker(); + auto &de = CS.getASTContext().Diags; if (baseTy->is()) { - tc.diagnose(expr->getSubExpr()->getLoc(), + de.diagnose(expr->getSubExpr()->getLoc(), diag::cannot_explicitly_specialize_generic_function); - tc.diagnose(expr->getLAngleLoc(), + de.diagnose(expr->getLAngleLoc(), diag::while_parsing_as_left_angle_bracket); return Type(); } @@ -1621,19 +1665,21 @@ namespace { if (BoundGenericType *bgt = meta->getInstanceType()->getAs()) { ArrayRef typeVars = bgt->getGenericArgs(); - MutableArrayRef specializations = expr->getUnresolvedParams(); + MutableArrayRef specializations = + expr->getUnresolvedParams(); // If we have too many generic arguments, complain. if (specializations.size() > typeVars.size()) { - tc.diagnose(expr->getSubExpr()->getLoc(), + de.diagnose(expr->getSubExpr()->getLoc(), diag::type_parameter_count_mismatch, bgt->getDecl()->getName(), typeVars.size(), specializations.size(), false) .highlight(SourceRange(expr->getLAngleLoc(), expr->getRAngleLoc())); - tc.diagnose(bgt->getDecl(), diag::kind_declname_declared_here, - DescriptiveDeclKind::GenericType, bgt->getDecl()->getName()); + de.diagnose(bgt->getDecl(), diag::kind_declname_declared_here, + DescriptiveDeclKind::GenericType, + bgt->getDecl()->getName()); return Type(); } @@ -1643,7 +1689,7 @@ namespace { for (size_t i = 0, size = specializations.size(); i < size; ++i) { TypeResolutionOptions options(TypeResolverContext::InExpression); options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::validateType(tc.Context, + if (TypeChecker::validateType(CS.getASTContext(), specializations[i], TypeResolution::forContextual(CS.DC), options)) @@ -1656,9 +1702,9 @@ namespace { return baseTy; } else { - tc.diagnose(expr->getSubExpr()->getLoc(), diag::not_a_generic_type, + de.diagnose(expr->getSubExpr()->getLoc(), diag::not_a_generic_type, meta->getInstanceType()); - tc.diagnose(expr->getLAngleLoc(), + de.diagnose(expr->getLAngleLoc(), diag::while_parsing_as_left_angle_bracket); return Type(); } @@ -1666,9 +1712,9 @@ namespace { // FIXME: If the base type is a type variable, constrain it to a metatype // of a bound generic type. - tc.diagnose(expr->getSubExpr()->getLoc(), + de.diagnose(expr->getSubExpr()->getLoc(), diag::not_a_generic_definition); - tc.diagnose(expr->getLAngleLoc(), + de.diagnose(expr->getLAngleLoc(), diag::while_parsing_as_left_angle_bracket); return Type(); } @@ -1704,12 +1750,11 @@ namespace { // Prior to Swift 5, 'try?' always adds an additional layer of optionality, // even if the sub-expression was already optional. - if (CS.getTypeChecker().getLangOpts().isSwiftVersionAtLeast(5)) { + if (CS.getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { CS.addConstraint(ConstraintKind::Conversion, CS.getType(expr->getSubExpr()), optTy, CS.getConstraintLocator(expr)); - } - else { + } else { CS.addConstraint(ConstraintKind::OptionalObject, optTy, CS.getType(expr->getSubExpr()), CS.getConstraintLocator(expr)); @@ -1754,7 +1799,11 @@ namespace { return Type(); } - return addSubscriptConstraints(expr, CS.getType(expr->getBase()), + auto *base = expr->getBase(); + if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) + return nullptr; + + return addSubscriptConstraints(expr, CS.getType(base), expr->getIndex(), decl, expr->getArgumentLabels(), expr->hasTrailingClosure()); @@ -1763,10 +1812,9 @@ namespace { Type visitArrayExpr(ArrayExpr *expr) { // An array expression can be of a type T that conforms to the // ExpressibleByArrayLiteral protocol. - auto &tc = CS.getTypeChecker(); - ProtocolDecl *arrayProto - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByArrayLiteral); + ProtocolDecl *arrayProto = TypeChecker::getProtocol( + CS.getASTContext(), expr->getLoc(), + KnownProtocolKind::ExpressibleByArrayLiteral); if (!arrayProto) { return Type(); } @@ -1819,7 +1867,6 @@ namespace { // Introduce conversions from each element to the element type of the // array. - ConstraintLocatorBuilder builder(locator); unsigned index = 0; for (auto element : expr->getElements()) { CS.addConstraint(ConstraintKind::Conversion, @@ -1831,7 +1878,7 @@ namespace { // The array element type defaults to 'Any'. CS.addConstraint(ConstraintKind::Defaultable, arrayElementTy, - tc.Context.TheAnyType, locator); + CS.getASTContext().TheAnyType, locator); return arrayTy; } @@ -1846,10 +1893,8 @@ namespace { // A dictionary expression can be of a type T that conforms to the // ExpressibleByDictionaryLiteral protocol. // FIXME: This isn't actually used for anything at the moment. - auto &tc = CS.getTypeChecker(); - ProtocolDecl *dictionaryProto - = tc.getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByDictionaryLiteral); + ProtocolDecl *dictionaryProto = TypeChecker::getProtocol( + C, expr->getLoc(), KnownProtocolKind::ExpressibleByDictionaryLiteral); if (!dictionaryProto) { return Type(); } @@ -1983,9 +2028,10 @@ namespace { } // The dictionary key type defaults to 'AnyHashable'. + auto &ctx = CS.getASTContext(); if (dictionaryKeyTy->isTypeVariableOrMember() && - tc.Context.getAnyHashableDecl()) { - auto anyHashable = tc.Context.getAnyHashableDecl(); + ctx.getAnyHashableDecl()) { + auto anyHashable = ctx.getAnyHashableDecl(); CS.addConstraint(ConstraintKind::Defaultable, dictionaryKeyTy, anyHashable->getDeclaredInterfaceType(), locator); } @@ -1993,7 +2039,7 @@ namespace { // The dictionary value type defaults to 'Any'. if (dictionaryValueTy->isTypeVariableOrMember()) { CS.addConstraint(ConstraintKind::Defaultable, dictionaryValueTy, - tc.Context.TheAnyType, locator); + ctx.TheAnyType, locator); } return dictionaryTy; @@ -2008,45 +2054,85 @@ namespace { Type visitTupleElementExpr(TupleElementExpr *expr) { ASTContext &context = CS.getASTContext(); - Identifier name - = context.getIdentifier(llvm::utostr(expr->getFieldNumber())); + DeclNameRef name( + context.getIdentifier(llvm::utostr(expr->getFieldNumber()))); return addMemberRefConstraints(expr, expr->getBase(), name, FunctionRefKind::Unapplied, /*outerAlternatives=*/{}); } - /// Give each parameter in a ClosureExpr a fresh type variable if parameter - /// types were not specified, and return the eventual function type. - void getClosureParams(ClosureExpr *closureExpr, - SmallVectorImpl ¶ms) { - auto *paramList = closureExpr->getParameters(); - unsigned i = 0; - - for (auto *param : *paramList) { - auto *locator = CS.getConstraintLocator( - closureExpr, LocatorPathElt::TupleElement(i++)); - Type paramType, internalType; - - // If a type was explicitly specified, use its opened type. - if (param->getTypeRepr()) { - paramType = closureExpr->mapTypeIntoContext(param->getInterfaceType()); - // FIXME: Need a better locator for a pattern as a base. - paramType = CS.openUnboundGenericType(paramType, locator); - internalType = paramType; + FunctionType *inferClosureType(ClosureExpr *closure) { + SmallVector closureParams; + + if (auto *paramList = closure->getParameters()) { + for (unsigned i = 0, n = paramList->size(); i != n; ++i) { + const auto *param = paramList->get(i); + auto *paramLoc = + CS.getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); + + Type externalType; + if (param->getTypeRepr()) { + auto declaredTy = param->getType(); + externalType = CS.openUnboundGenericType(declaredTy, paramLoc); + } else { + externalType = CS.createTypeVariable( + paramLoc, TVO_CanBindToInOut | TVO_CanBindToNoEscape); + } + + closureParams.push_back(param->toFunctionParam(externalType)); + } + } + + auto extInfo = FunctionType::ExtInfo(); + if (closureCanThrow(closure)) + extInfo = extInfo.withThrows(); + + // Closure expressions always have function type. In cases where a + // parameter or return type is omitted, a fresh type variable is used to + // stand in for that parameter or return type, allowing it to be inferred + // from context. + Type resultTy; + if (closure->hasExplicitResultType() && + closure->getExplicitResultTypeLoc().getType()) { + resultTy = closure->getExplicitResultTypeLoc().getType(); + } else { + auto &ctx = CS.getASTContext(); + auto *resultLoc = + CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); + + auto getContextualResultType = [&]() -> Type { + if (auto contextualType = CS.getContextualType(closure)) { + if (auto fnType = contextualType->getAs()) + return fnType->getResult(); + } + return Type(); + }; + + if (closure->hasEmptyBody()) { + // Closures with empty bodies should be inferred to return + // (). + resultTy = ctx.TheEmptyTupleType; + } else if (auto contextualResultTy = getContextualResultType()) { + resultTy = contextualResultTy; } else { - // Otherwise, create fresh type variables. - paramType = CS.createTypeVariable(locator, - TVO_CanBindToInOut | - TVO_CanBindToNoEscape); - internalType = CS.createTypeVariable(locator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::BindParam, paramType, internalType, - locator); + // If no return type was specified, create a fresh type + // variable for it and mark it as possible hole. + // + // If this is a multi-statement closure, let's mark result + // as potential hole right away. + resultTy = CS.createTypeVariable( + resultLoc, + closure->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole); + + if (closureHasNoResult(closure)) { + // Allow it to default to () if there are no return statements. + CS.addConstraint(ConstraintKind::Defaultable, resultTy, + ctx.TheEmptyTupleType, resultLoc); + } } - CS.setType(param, internalType); - params.push_back(param->toFunctionParam(paramType)); } + + return FunctionType::get(closureParams, resultTy, extInfo); } /// Produces a type for the given pattern, filling in any missing @@ -2111,7 +2197,7 @@ namespace { if (!ty || ty->is()) ty = CS.createTypeVariable(CS.getConstraintLocator(locator), TVO_CanBindToNoEscape); - return CS.getTypeChecker().getOptionalType(var->getLoc(), ty); + return TypeChecker::getOptionalType(var->getLoc(), ty); case ReferenceOwnershipOptionality::Allowed: case ReferenceOwnershipOptionality::Disallowed: break; @@ -2123,10 +2209,11 @@ namespace { } case PatternKind::Typed: { - auto typedPattern = cast(pattern); // FIXME: Need a better locator for a pattern as a base. - Type openedType = CS.openUnboundGenericType(typedPattern->getType(), - locator); + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, CurDC); + Type type = TypeChecker::typeCheckPattern(contextualPattern); + Type openedType = CS.openUnboundGenericType(type, locator); // For a typed pattern, simply return the opened type of the pattern. // FIXME: Error recovery if the type is an error type? @@ -2223,6 +2310,7 @@ namespace { // or exhaustive catches. class FindInnerThrows : public ASTWalker { ConstraintSystem &CS; + DeclContext *DC; bool FoundThrow = false; std::pair walkToExprPre(Expr *expr) override { @@ -2273,8 +2361,8 @@ namespace { // Okay, resolve the pattern. Pattern *pattern = clause->getErrorPattern(); - pattern = CS.TC.resolvePattern(pattern, CS.DC, - /*isStmtCondition*/false); + pattern = TypeChecker::resolvePattern(pattern, CS.DC, + /*isStmtCondition*/false); if (!pattern) return false; // Save that aside while we explore the type. @@ -2284,7 +2372,7 @@ namespace { // of is-patterns applied to an irrefutable pattern. pattern = pattern->getSemanticsProvidingPattern(); while (auto isp = dyn_cast(pattern)) { - if (TypeChecker::validateType(CS.TC.Context, + if (TypeChecker::validateType(CS.getASTContext(), isp->getCastTypeLoc(), TypeResolution::forContextual(CS.DC), TypeResolverContext::InExpression)) { @@ -2305,15 +2393,15 @@ namespace { // Okay, now it should be safe to coerce the pattern. // Pull the top-level pattern back out. pattern = clause->getErrorPattern(); - Type exnType = CS.TC.getExceptionType(CS.DC, clause->getCatchLoc()); + Type exnType = CS.getASTContext().getErrorDecl()->getDeclaredType(); if (!exnType) return false; - if (CS.TC.coercePatternToType(pattern, - TypeResolution::forContextual(CS.DC), - exnType, - TypeResolverContext::InExpression)) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType( + contextualPattern, exnType, TypeResolverContext::InExpression); + if (!pattern) return false; - } clause->setErrorPattern(pattern); return clause->isSyntacticallyExhaustive(); @@ -2349,7 +2437,8 @@ namespace { } public: - FindInnerThrows(ConstraintSystem &cs) : CS(cs) {} + FindInnerThrows(ConstraintSystem &cs, DeclContext *dc) + : CS(cs), DC(dc) {} bool foundThrow() { return FoundThrow; } }; @@ -2362,71 +2451,51 @@ namespace { if (!body) return false; - auto tryFinder = FindInnerThrows(CS); + auto tryFinder = FindInnerThrows(CS, expr); body->walk(tryFinder); return tryFinder.foundThrow(); } - - Type visitClosureExpr(ClosureExpr *expr) { - - // If a contextual function type exists, we can use that to obtain the - // expected return type, rather than allocating a fresh type variable. - auto contextualType = CS.getContextualType(expr); - Type crt; - - if (contextualType) { - if (auto cft = contextualType->getAs()) { - crt = cft->getResult(); - } - } - - // Closure expressions always have function type. In cases where a - // parameter or return type is omitted, a fresh type variable is used to - // stand in for that parameter or return type, allowing it to be inferred - // from context. - Type resultTy; - if (expr->hasExplicitResultType() && - expr->getExplicitResultTypeLoc().getType()) { - resultTy = expr->getExplicitResultTypeLoc().getType(); - CS.setFavoredType(expr, resultTy.getPointer()); - } else { - auto locator = - CS.getConstraintLocator(expr, ConstraintLocator::ClosureResult); + Type visitClosureExpr(ClosureExpr *closure) { + auto *locator = CS.getConstraintLocator(closure); + auto closureType = CS.createTypeVariable(locator, TVO_CanBindToNoEscape); - if (expr->hasEmptyBody()) { - resultTy = CS.createTypeVariable(locator, 0); + // Collect any references to closure parameters whose types involve type + // variables from the closure, because there will be a dependency on + // those type variables once we have generated constraints for the + // closure body. + struct CollectParameterRefs : public ASTWalker { + ConstraintSystem &cs; + llvm::SmallVector paramRefs; - // Closures with empty bodies should be inferred to return - // (). - CS.addConstraint(ConstraintKind::Bind, resultTy, - TupleType::getEmpty(CS.getASTContext()), locator); - } else if (crt) { - // Otherwise, use the contextual type if present. - resultTy = crt; - } else { - // If no return type was specified, create a fresh type - // variable for it. - resultTy = CS.createTypeVariable(locator, 0); + CollectParameterRefs(ConstraintSystem &cs) : cs(cs) { } - if (closureHasNoResult(expr)) { - // Allow it to default to () if there are no return statements. - CS.addConstraint(ConstraintKind::Defaultable, resultTy, - TupleType::getEmpty(CS.getASTContext()), locator); + std::pair walkToExprPre(Expr *expr) override { + // Retrieve type variables from references to parameter declarations. + if (auto *declRef = dyn_cast(expr)) { + if (auto *paramDecl = dyn_cast(declRef->getDecl())) { + if (Type paramType = cs.getTypeIfAvailable(paramDecl)) { + paramType->getTypeVariables(paramRefs); + } + } } + + return { true, expr }; } - } + } collectParameterRefs(CS); + closure->walk(collectParameterRefs); - // Give each parameter in a ClosureExpr a fresh type variable if parameter - // types were not specified, and return the eventual function type. - SmallVector paramTy; - getClosureParams(expr, paramTy); + auto inferredType = inferClosureType(closure); + if (!inferredType || inferredType->hasError()) + return Type(); - auto extInfo = FunctionType::ExtInfo(); - if (closureCanThrow(expr)) - extInfo = extInfo.withThrows(); + CS.addUnsolvedConstraint( + Constraint::create(CS, ConstraintKind::DefaultClosureType, + closureType, inferredType, locator, + collectParameterRefs.paramRefs)); - return FunctionType::get(paramTy, resultTy, extInfo); + CS.setClosureType(closure, inferredType); + return closureType; } Type visitAutoClosureExpr(AutoClosureExpr *expr) { @@ -2458,7 +2527,7 @@ namespace { // Try to build the appropriate type for a variadic argument list of // the fresh element type. If that failed, just bail out. - auto array = CS.TC.getArraySliceType(expr->getLoc(), element); + auto array = TypeChecker::getArraySliceType(expr->getLoc(), element); if (!array) return element; // Require the operand to be convertible to the array type. @@ -2486,10 +2555,6 @@ namespace { return expr->getType(); } - Type visitCallerDefaultArgumentExpr(CallerDefaultArgumentExpr *expr) { - return expr->getType(); - } - Type visitApplyExpr(ApplyExpr *expr) { auto fnExpr = expr->getFn(); @@ -2545,14 +2610,14 @@ namespace { DeclContext *typeContext = selfDecl->getDeclContext()->getParent(); assert(typeContext && "constructor without parent context?!"); - auto &tc = CS.getTypeChecker(); + auto &de = CS.getASTContext().Diags; ClassDecl *classDecl = typeContext->getSelfClassDecl(); if (!classDecl) { - tc.diagnose(diagLoc, diag_not_in_class); + de.diagnose(diagLoc, diag_not_in_class); return Type(); } if (!classDecl->hasSuperclass()) { - tc.diagnose(diagLoc, diag_no_base_class); + de.diagnose(diagLoc, diag_no_base_class); return Type(); } @@ -2577,21 +2642,21 @@ namespace { if (!boolDecl) return Type(); - CS.addConstraint(ConstraintKind::Conversion, - CS.getType(expr->getCondExpr()), - boolDecl->getDeclaredType(), - CS.getConstraintLocator(expr->getCondExpr())); + CS.addConstraint( + ConstraintKind::Conversion, CS.getType(expr->getCondExpr()), + boolDecl->getDeclaredType(), + CS.getConstraintLocator(expr, ConstraintLocator::Condition)); // The branches must be convertible to a common type. - return CS.addJoinConstraint(CS.getConstraintLocator(expr), - { - { CS.getType(expr->getThenExpr()), - CS.getConstraintLocator(expr->getThenExpr()) }, - { CS.getType(expr->getElseExpr()), - CS.getConstraintLocator(expr->getElseExpr()) } - }); + return CS.addJoinConstraint( + CS.getConstraintLocator(expr), + {{CS.getType(expr->getThenExpr()), + CS.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))}, + {CS.getType(expr->getElseExpr()), + CS.getConstraintLocator(expr, + LocatorPathElt::TernaryBranch(false))}}); } - + virtual Type visitImplicitConversionExpr(ImplicitConversionExpr *expr) { llvm_unreachable("Already type-checked"); } @@ -2606,7 +2671,6 @@ namespace { } Type visitForcedCheckedCastExpr(ForcedCheckedCastExpr *expr) { - auto &tc = CS.getTypeChecker(); auto fromExpr = expr->getSubExpr(); if (!fromExpr) // Either wasn't constructed correctly or wasn't folded. return nullptr; @@ -2614,7 +2678,7 @@ namespace { // Validate the resulting type. TypeResolutionOptions options(TypeResolverContext::ExplicitCastExpr); options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::validateType(tc.Context, + if (TypeChecker::validateType(CS.getASTContext(), expr->getCastTypeLoc(), TypeResolution::forContextual(CS.DC), options)) @@ -2641,12 +2705,10 @@ namespace { } Type visitCoerceExpr(CoerceExpr *expr) { - auto &tc = CS.getTypeChecker(); - // Validate the resulting type. TypeResolutionOptions options(TypeResolverContext::ExplicitCastExpr); options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::validateType(tc.Context, + if (TypeChecker::validateType(CS.getASTContext(), expr->getCastTypeLoc(), TypeResolution::forContextual(CS.DC), options)) @@ -2675,15 +2737,35 @@ namespace { } Type visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) { - auto &tc = CS.getTypeChecker(); + auto &ctx = CS.getASTContext(); auto fromExpr = expr->getSubExpr(); if (!fromExpr) // Either wasn't constructed correctly or wasn't folded. return nullptr; + std::function nilLiteralExpr = [&](Expr *expr) -> Expr * { + expr = expr->getSemanticsProvidingExpr(); + if (expr->getKind() == ExprKind::NilLiteral) + return expr; + + if (auto *optionalEvalExpr = dyn_cast(expr)) + return nilLiteralExpr(optionalEvalExpr->getSubExpr()); + + if (auto *bindOptionalExpr = dyn_cast(expr)) + return nilLiteralExpr(bindOptionalExpr->getSubExpr()); + + return nullptr; + }; + + if (auto nilLiteral = nilLiteralExpr(fromExpr)) { + ctx.Diags.diagnose(nilLiteral->getLoc(), + diag::conditional_cast_from_nil); + return nullptr; + } + // Validate the resulting type. TypeResolutionOptions options(TypeResolverContext::ExplicitCastExpr); options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::validateType(tc.Context, + if (TypeChecker::validateType(ctx, expr->getCastTypeLoc(), TypeResolution::forContextual(CS.DC), options)) @@ -2711,10 +2793,10 @@ namespace { Type visitIsExpr(IsExpr *expr) { // Validate the type. - auto &tc = CS.getTypeChecker(); + auto &ctx = CS.getASTContext(); TypeResolutionOptions options(TypeResolverContext::ExplicitCastExpr); options |= TypeResolutionFlags::AllowUnboundGenerics; - if (TypeChecker::validateType(tc.Context, + if (TypeChecker::validateType(ctx, expr->getCastTypeLoc(), TypeResolution::forContextual(CS.DC), options)) @@ -2733,10 +2815,10 @@ namespace { CS.getConstraintLocator(expr)); // The result is Bool. - auto boolDecl = tc.Context.getBoolDecl(); + auto boolDecl = ctx.getBoolDecl(); if (!boolDecl) { - tc.diagnose(SourceLoc(), diag::broken_bool); + ctx.Diags.diagnose(SourceLoc(), diag::broken_bool); return Type(); } @@ -2745,7 +2827,8 @@ namespace { Type visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) { auto locator = CS.getConstraintLocator(expr); - auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape); + auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape | + TVO_CanBindToHole); return LValueType::get(typeVar); } @@ -2786,7 +2869,8 @@ namespace { if (!expr->getDest() || !expr->getSrc()) return Type(); Type destTy = genAssignDestType(expr->getDest(), CS); - CS.addConstraint(ConstraintKind::Conversion, CS.getType(expr->getSrc()), destTy, + CS.addConstraint(ConstraintKind::Conversion, + CS.getType(expr->getSrc()), destTy, CS.getConstraintLocator(expr)); return TupleType::getEmpty(CS.getASTContext()); } @@ -2810,8 +2894,9 @@ namespace { /// diagnosing ill-formed standard libraries, so it really isn't /// worth QoI efforts. Type getOptionalType(SourceLoc optLoc, Type valueTy) { - auto optTy = CS.getTypeChecker().getOptionalType(optLoc, valueTy); - if (!optTy || CS.getTypeChecker().requireOptionalIntrinsics(optLoc)) + auto optTy = TypeChecker::getOptionalType(optLoc, valueTy); + if (!optTy || + TypeChecker::requireOptionalIntrinsics(CS.getASTContext(), optLoc)) return Type(); return optTy; @@ -2929,18 +3014,18 @@ namespace { Type visitObjCSelectorExpr(ObjCSelectorExpr *E) { // #selector only makes sense when we have the Objective-C // runtime. - auto &tc = CS.getTypeChecker(); - if (!tc.Context.LangOpts.EnableObjCInterop) { - tc.diagnose(E->getLoc(), diag::expr_selector_no_objc_runtime); + auto &ctx = CS.getASTContext(); + if (!ctx.LangOpts.EnableObjCInterop) { + ctx.Diags.diagnose(E->getLoc(), diag::expr_selector_no_objc_runtime); return nullptr; } // Make sure we can reference ObjectiveC.Selector. // FIXME: Fix-It to add the import? - auto type = CS.getTypeChecker().getObjCSelectorType(CS.DC); + auto type = CS.getASTContext().getSelectorType(); if (!type) { - tc.diagnose(E->getLoc(), diag::expr_selector_module_missing); + ctx.Diags.diagnose(E->getLoc(), diag::expr_selector_module_missing); return nullptr; } @@ -2954,7 +3039,8 @@ namespace { auto kpDecl = CS.getASTContext().getKeyPathDecl(); if (!kpDecl) { - CS.TC.diagnose(E->getLoc(), diag::expr_keypath_no_keypath_type); + auto &de = CS.getASTContext().Diags; + de.diagnose(E->getLoc(), diag::expr_keypath_no_keypath_type); return ErrorType::get(CS.getASTContext()); } @@ -3002,8 +3088,8 @@ namespace { TVO_CanBindToNoEscape); componentTypeVars.push_back(memberTy); auto lookupName = kind == KeyPathExpr::Component::Kind::UnresolvedProperty - ? component.getUnresolvedDeclName() - : component.getDeclRef().getDecl()->getFullName(); + ? DeclNameRef(component.getUnresolvedDeclName()) // FIXME: type change needed + : component.getDeclRef().getDecl()->createNameRef(); auto refKind = lookupName.isSimpleName() ? FunctionRefKind::Unapplied @@ -3282,14 +3368,14 @@ namespace { /// diagnostics and code completion. class SanitizeExpr : public ASTWalker { ConstraintSystem &CS; - TypeChecker &TC; const bool eraseOpenExistentialsOnly; llvm::SmallDenseMap OpenExistentials; public: SanitizeExpr(ConstraintSystem &cs, bool eraseOEsOnly = false) - : CS(cs), TC(cs.getTypeChecker()), - eraseOpenExistentialsOnly(eraseOEsOnly) { } + : CS(cs), eraseOpenExistentialsOnly(eraseOEsOnly) { } + + ASTContext &getASTContext() const { return CS.getASTContext(); } std::pair walkToExprPre(Expr *expr) override { while (true) { @@ -3356,8 +3442,11 @@ namespace { // Restore '@autoclosure'd value. if (auto ACE = dyn_cast(expr)) { - expr = ACE->getSingleExpressionBody(); - continue; + // This is only valid if the closure doesn't have parameters. + if (ACE->getParameters()->size() == 0) { + expr = ACE->getSingleExpressionBody(); + continue; + } } // Remove any semantic expression injected by typechecking. @@ -3384,7 +3473,7 @@ namespace { }; if (TE->isImplicit() && TE->getNumElements() == 1 && - TE->getElementName(0) == TC.Context.Id_dynamicMember && + TE->getElementName(0) == getASTContext().Id_dynamicMember && isImplicitKeyPathExpr(TE->getElement(0))) { auto *keyPathExpr = cast(TE->getElement(0)); auto *componentExpr = keyPathExpr->getParsedPath(); @@ -3410,8 +3499,7 @@ namespace { } bool isSyntheticArgumentExpr(const Expr *expr) { - if (isa(expr) || - isa(expr)) + if (isa(expr)) return true; if (auto *varargExpr = dyn_cast(expr)) @@ -3443,15 +3531,15 @@ namespace { argList.labels[0].empty() && !isa(argList.args[0])) { auto *result = - new (TC.Context) ParenExpr(argList.lParenLoc, - argList.args[0], - argList.rParenLoc, - argList.hasTrailingClosure); + new (getASTContext()) ParenExpr(argList.lParenLoc, + argList.args[0], + argList.rParenLoc, + argList.hasTrailingClosure); result->setImplicit(); return result; } - return TupleExpr::create(TC.Context, + return TupleExpr::create(getASTContext(), argList.lParenLoc, argList.args, argList.labels, @@ -3485,7 +3573,7 @@ namespace { auto buildMemberRef = [&](Type memberType, Expr *base, SourceLoc dotLoc, ConcreteDeclRef member, DeclNameLoc memberLoc, bool implicit) -> Expr * { - auto *memberRef = new (TC.Context) + auto *memberRef = new (getASTContext()) MemberRefExpr(base, dotLoc, member, memberLoc, implicit); if (memberType) { @@ -3582,23 +3670,20 @@ namespace { if (auto keyPath = dyn_cast(expr)) { if (keyPath->isObjC()) { auto &cs = CG.getConstraintSystem(); - (void)cs.getTypeChecker().checkObjCKeyPathExpr(cs.DC, keyPath); + (void)TypeChecker::checkObjCKeyPathExpr(cs.DC, keyPath); } } - // For closures containing only a single expression, the body participates - // in type checking. + // Both multi- and single-statement closures now behave the same way + // when it comes to constraint generation. if (auto closure = dyn_cast(expr)) { auto &CS = CG.getConstraintSystem(); - if (closure->hasSingleExpressionBody()) { - CG.enterClosure(closure); - - // Visit the closure itself, which produces a function type. - auto funcTy = CG.visit(expr)->castTo(); - CS.setType(expr, funcTy); - } + auto closureType = CG.visitClosureExpr(closure); + if (!closureType) + return {false, nullptr}; - return { true, expr }; + CS.setType(expr, closureType); + return {false, expr}; } // Don't visit CoerceExpr with an empty sub expression. They may occur @@ -3624,11 +3709,11 @@ namespace { /// Once we've visited the children of the given expression, /// generate constraints from the expression. Expr *walkToExprPost(Expr *expr) override { + auto &CS = CG.getConstraintSystem(); // Translate special type-checker Builtin calls into simpler expressions. if (auto *apply = dyn_cast(expr)) { auto fnExpr = apply->getFn(); if (auto *UDE = dyn_cast(fnExpr)) { - auto &CS = CG.getConstraintSystem(); auto typeOperation = ConstraintGenerator::getTypeOperation(UDE, CS.getASTContext()); @@ -3659,36 +3744,7 @@ namespace { } } - if (auto closure = dyn_cast(expr)) { - if (closure->hasSingleExpressionBody()) { - CG.exitClosure(closure); - - auto &CS = CG.getConstraintSystem(); - Type closureTy = CS.getType(closure); - - // If the function type has an error in it, we don't want to solve the - // system. - if (closureTy && closureTy->hasError()) - return nullptr; - - // Visit the body. It's type needs to be convertible to the function's - // return type. - auto resultTy = closureTy->castTo()->getResult(); - Type bodyTy = CS.getType(closure->getSingleExpressionBody()); - CG.getConstraintSystem().setFavoredType(expr, bodyTy.getPointer()); - CG.getConstraintSystem() - .addConstraint(ConstraintKind::Conversion, bodyTy, - resultTy, - CG.getConstraintSystem() - .getConstraintLocator( - expr, - ConstraintLocator::ClosureResult)); - return expr; - } - } - if (auto type = CG.visit(expr)) { - auto &CS = CG.getConstraintSystem(); auto simplifiedType = CS.simplifyType(type); CS.setType(expr, simplifiedType); @@ -3709,30 +3765,98 @@ namespace { }; } // end anonymous namespace -Expr *ConstraintSystem::generateConstraints(Expr *expr, DeclContext *dc) { +static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr, + DeclContext *DC) { // Remove implicit conversions from the expression. - expr = expr->walk(SanitizeExpr(*this)); + expr = expr->walk(SanitizeExpr(cs)); // Walk the expression, generating constraints. - ConstraintGenerator cg(*this, dc); + ConstraintGenerator cg(cs, DC); ConstraintWalker cw(cg); - - Expr* result = expr->walk(cw); - + + Expr *result = expr->walk(cw); + if (result) - this->optimizeConstraints(result); + cs.optimizeConstraints(result); return result; } +Expr *ConstraintSystem::generateConstraints(ClosureExpr *closure) { + assert(closure->hasSingleExpressionBody()); + return generateConstraintsFor(*this, closure->getSingleExpressionBody(), + closure); +} + +Expr *ConstraintSystem::generateConstraints(Expr *expr, DeclContext *dc) { + InputExprs.insert(expr); + return generateConstraintsFor(*this, expr, dc); +} + Type ConstraintSystem::generateConstraints(Pattern *pattern, ConstraintLocatorBuilder locator) { ConstraintGenerator cg(*this, nullptr); return cg.getTypeForPattern(pattern, locator); } +bool ConstraintSystem::canGenerateConstraints(StmtCondition condition) { + for (const auto &element : condition) { + switch (element.getKind()) { + case StmtConditionElement::CK_Availability: + case StmtConditionElement::CK_Boolean: + continue; + + case StmtConditionElement::CK_PatternBinding: + return false; + } + } + + return true; +} + +bool ConstraintSystem::generateConstraints(StmtCondition condition, + DeclContext *dc) { + // FIXME: This should be folded into constraint generation for conditions. + auto boolDecl = getASTContext().getBoolDecl(); + if (!boolDecl) { + return true; + } + + Type boolTy = boolDecl->getDeclaredType(); + for (const auto &condElement : condition) { + switch (condElement.getKind()) { + case StmtConditionElement::CK_Availability: + // Nothing to do here. + continue; + + case StmtConditionElement::CK_Boolean: { + Expr *condExpr = condElement.getBoolean(); + setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition, + /*isOpaqueReturnType=*/false); + + condExpr = generateConstraints(condExpr, dc); + if (!condExpr) { + return true; + } + + addConstraint(ConstraintKind::Conversion, + getType(condExpr), + boolTy, + getConstraintLocator(condExpr, + LocatorPathElt::ContextualType())); + continue; + } + + case StmtConditionElement::CK_PatternBinding: + llvm_unreachable("unhandled statement condition"); + } + } + + return false; +} + void ConstraintSystem::optimizeConstraints(Expr *e) { - if (TC.getLangOpts().DisableConstraintSolverPerformanceHacks) + if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return; SmallVector linkedExprs; @@ -3755,9 +3879,8 @@ bool swift::areGenericRequirementsSatisfied( const DeclContext *DC, GenericSignature sig, SubstitutionMap Substitutions, bool isExtension) { - TypeChecker &TC = createTypeChecker(DC->getASTContext()); ConstraintSystemOptions Options; - ConstraintSystem CS(TC, const_cast(DC), Options); + ConstraintSystem CS(const_cast(DC), Options); auto Loc = CS.getConstraintLocator(nullptr); // For every requirement, add a constraint. @@ -3777,14 +3900,6 @@ bool swift::areGenericRequirementsSatisfied( return CS.solveSingle().hasValue(); } -bool swift::canSatisfy(Type type1, Type type2, bool openArchetypes, - ConstraintKind kind, DeclContext *dc) { - std::unique_ptr CreatedTC; - auto &TC = TypeChecker::createForContext(dc->getASTContext()); - return TC.typesSatisfyConstraint(type1, type2, openArchetypes, kind, dc, - /*unwrappedIUO=*/nullptr); -} - void swift::eraseOpenedExistentials(ConstraintSystem &CS, Expr *&expr) { expr = expr->walk(SanitizeExpr(CS, /*eraseOEsOnly=*/true)); } @@ -3826,15 +3941,13 @@ getMemberDecls(InterestedMemberKind Kind) { ResolvedMemberResult swift::resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name) { ResolvedMemberResult Result; - std::unique_ptr CreatedTC; - // If the current ast context has no type checker, create one for it. - auto &TC = TypeChecker::createForContext(DC.getASTContext()); - ConstraintSystem CS(TC, &DC, None); + assert(DC.getASTContext().areSemanticQueriesEnabled()); + ConstraintSystem CS(&DC, None); // Look up all members of BaseTy with the given Name. MemberLookupResult LookupResult = CS.performMemberLookup( - ConstraintKind::ValueMember, Name, BaseTy, FunctionRefKind::SingleApply, - nullptr, false); + ConstraintKind::ValueMember, DeclNameRef(Name), BaseTy, + FunctionRefKind::SingleApply, nullptr, false); // Keep track of all the unviable members. for (auto Can : LookupResult.UnviableCandidates) @@ -3874,8 +3987,7 @@ swift::getOriginalArgumentList(Expr *expr) { OriginalArgumentList result; auto add = [&](Expr *arg, Identifier label, SourceLoc labelLoc) { - if (isa(arg) || - isa(arg)) { + if (isa(arg)) { return; } diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 5da63bd21c75a..f138d7824ebe7 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -16,8 +16,9 @@ //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" #include "swift/AST/GenericSignature.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" @@ -34,7 +35,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { unsigned index = static_cast(kind); CurrentScore.Data[index] += value; - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver && value > 0) { auto &log = getASTContext().TypeCheckerDebug->getStream(); if (solverState) log.indent(solverState->depth * 2); @@ -90,7 +91,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { } bool ConstraintSystem::worseThanBestSolution() const { - if (TC.getLangOpts().DisableConstraintSolverPerformanceHacks) + if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; if (retainAllSolutions()) @@ -100,7 +101,7 @@ bool ConstraintSystem::worseThanBestSolution() const { CurrentScore <= *solverState->BestScore) return false; - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState->depth * 2) << "(solution is worse than the best solution)\n"; @@ -200,13 +201,13 @@ static bool isNominallySuperclassOf(Type type1, Type type2) { /// Determine the relationship between the self types of the given declaration /// contexts.. -static std::pair> -computeSelfTypeRelationship(TypeChecker &tc, DeclContext *dc, ValueDecl *decl1, +static std::pair +computeSelfTypeRelationship(DeclContext *dc, ValueDecl *decl1, ValueDecl *decl2) { // If both declarations are operators, even through they // might have Self such types are unrelated. if (decl1->isOperator() && decl2->isOperator()) - return {SelfTypeRelationship::Unrelated, None}; + return {SelfTypeRelationship::Unrelated, ProtocolConformanceRef()}; auto *dc1 = decl1->getDeclContext(); auto *dc2 = decl2->getDeclContext(); @@ -214,32 +215,32 @@ computeSelfTypeRelationship(TypeChecker &tc, DeclContext *dc, ValueDecl *decl1, // If at least one of the contexts is a non-type context, the two are // unrelated. if (!dc1->isTypeContext() || !dc2->isTypeContext()) - return {SelfTypeRelationship::Unrelated, None}; + return {SelfTypeRelationship::Unrelated, ProtocolConformanceRef()}; Type type1 = dc1->getDeclaredInterfaceType(); Type type2 = dc2->getDeclaredInterfaceType(); // If the types are equal, the answer is simple. if (type1->isEqual(type2)) - return {SelfTypeRelationship::Equivalent, None}; + return {SelfTypeRelationship::Equivalent, ProtocolConformanceRef()}; // If both types can have superclasses, which whether one is a superclass // of the other. The subclass is the common base type. if (type1->mayHaveSuperclass() && type2->mayHaveSuperclass()) { if (isNominallySuperclassOf(type1, type2)) - return {SelfTypeRelationship::Superclass, None}; + return {SelfTypeRelationship::Superclass, ProtocolConformanceRef()}; if (isNominallySuperclassOf(type2, type1)) - return {SelfTypeRelationship::Subclass, None}; + return {SelfTypeRelationship::Subclass, ProtocolConformanceRef()}; - return {SelfTypeRelationship::Unrelated, None}; + return {SelfTypeRelationship::Unrelated, ProtocolConformanceRef()}; } // If neither or both are protocol types, consider the bases unrelated. bool isProtocol1 = isa(dc1); bool isProtocol2 = isa(dc2); if (isProtocol1 == isProtocol2) - return {SelfTypeRelationship::Unrelated, None}; + return {SelfTypeRelationship::Unrelated, ProtocolConformanceRef()}; // Just one of the two is a protocol. Check whether the other conforms to // that protocol. @@ -253,8 +254,8 @@ computeSelfTypeRelationship(TypeChecker &tc, DeclContext *dc, ValueDecl *decl1, modelTy, proto, dc, (ConformanceCheckFlags::InExpression| ConformanceCheckFlags::SkipConditionalRequirements)); - if (!conformance) - return {SelfTypeRelationship::Unrelated, None}; + if (conformance.isInvalid()) + return {SelfTypeRelationship::Unrelated, conformance}; if (isProtocol1) return {SelfTypeRelationship::ConformedToBy, conformance}; @@ -312,8 +313,7 @@ static bool isDeclMoreConstrainedThan(ValueDecl *decl1, ValueDecl *decl2) { /// Determine whether one protocol extension is at least as specialized as /// another. -static bool isProtocolExtensionAsSpecializedAs(TypeChecker &tc, - DeclContext *dc1, +static bool isProtocolExtensionAsSpecializedAs(DeclContext *dc1, DeclContext *dc2) { assert(dc1->getExtendedProtocolDecl()); assert(dc2->getExtendedProtocolDecl()); @@ -334,12 +334,12 @@ static bool isProtocolExtensionAsSpecializedAs(TypeChecker &tc, // as the other. GenericSignature sig1 = dc1->getGenericSignatureOfContext(); GenericSignature sig2 = dc2->getGenericSignatureOfContext(); - if (sig1->getCanonicalSignature() == sig2->getCanonicalSignature()) + if (sig1.getCanonicalSignature() == sig2.getCanonicalSignature()) return false; // Form a constraint system where we've opened up all of the requirements of // the second protocol extension. - ConstraintSystem cs(tc, dc1, None); + ConstraintSystem cs(dc1, None); OpenedTypeMap replacements; cs.openGeneric(dc2, sig2, ConstraintLocatorBuilder(nullptr), replacements); @@ -376,12 +376,21 @@ static bool paramIsIUO(const ValueDecl *decl, int paramNum) { /// the second declaration. /// /// "Specialized" is essentially a form of subtyping, defined below. -static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc, - ValueDecl *decl1, ValueDecl *decl2, +static bool isDeclAsSpecializedAs(DeclContext *dc, ValueDecl *decl1, + ValueDecl *decl2, bool isDynamicOverloadComparison = false) { + return evaluateOrDefault(decl1->getASTContext().evaluator, + CompareDeclSpecializationRequest{ + dc, decl1, decl2, isDynamicOverloadComparison}, + false); +} - if (tc.getLangOpts().DebugConstraintSolver) { - auto &log = tc.Context.TypeCheckerDebug->getStream(); +llvm::Expected CompareDeclSpecializationRequest::evaluate( + Evaluator &eval, DeclContext *dc, ValueDecl *decl1, ValueDecl *decl2, + bool isDynamicOverloadComparison) const { + auto &C = decl1->getASTContext(); + if (C.TypeCheckerOpts.DebugConstraintSolver) { + auto &log = C.TypeCheckerDebug->getStream(); log << "Comparing declarations\n"; decl1->print(log); log << "\nand\n"; @@ -391,314 +400,297 @@ static bool isDeclAsSpecializedAs(TypeChecker &tc, DeclContext *dc, log << ")\n"; } + auto completeResult = [&C](bool result) { + if (C.TypeCheckerOpts.DebugConstraintSolver) { + auto &log = C.TypeCheckerDebug->getStream(); + log << "comparison result: " << (result ? "better" : "not better") + << "\n"; + } + return result; + }; + auto *innerDC1 = decl1->getInnermostDeclContext(); auto *innerDC2 = decl2->getInnermostDeclContext(); auto *outerDC1 = decl1->getDeclContext(); auto *outerDC2 = decl2->getDeclContext(); - auto overloadComparisonKey = - std::make_tuple(decl1, decl2, isDynamicOverloadComparison); - if (!tc.specializedOverloadComparisonCache.count(overloadComparisonKey)) { - - auto compareSpecializations = [&] () -> bool { - // If the kinds are different, there's nothing we can do. - // FIXME: This is wrong for type declarations, which we're skipping - // entirely. - if (decl1->getKind() != decl2->getKind() || isa(decl1)) - return false; - - // A non-generic declaration is more specialized than a generic declaration. - if (auto func1 = dyn_cast(decl1)) { - auto func2 = cast(decl2); - if (func1->isGeneric() != func2->isGeneric()) - return func2->isGeneric(); - } + // If the kinds are different, there's nothing we can do. + // FIXME: This is wrong for type declarations, which we're skipping + // entirely. + if (decl1->getKind() != decl2->getKind() || isa(decl1)) + return completeResult(false); - if (auto subscript1 = dyn_cast(decl1)) { - auto subscript2 = cast(decl2); - if (subscript1->isGeneric() != subscript2->isGeneric()) - return subscript2->isGeneric(); - } + // A non-generic declaration is more specialized than a generic declaration. + if (auto func1 = dyn_cast(decl1)) { + auto func2 = cast(decl2); + if (func1->isGeneric() != func2->isGeneric()) + return completeResult(func2->isGeneric()); + } - // Members of protocol extensions have special overloading rules. - ProtocolDecl *inProtocolExtension1 = outerDC1->getExtendedProtocolDecl(); - ProtocolDecl *inProtocolExtension2 = outerDC2->getExtendedProtocolDecl(); - if (inProtocolExtension1 && inProtocolExtension2) { - // Both members are in protocol extensions. - // Determine whether the 'Self' type from the first protocol extension - // satisfies all of the requirements of the second protocol extension. - bool better1 = isProtocolExtensionAsSpecializedAs(tc, outerDC1, outerDC2); - bool better2 = isProtocolExtensionAsSpecializedAs(tc, outerDC2, outerDC1); - if (better1 != better2) { - return better1; - } - } else if (inProtocolExtension1 || inProtocolExtension2) { - // One member is in a protocol extension, the other is in a concrete type. - // Prefer the member in the concrete type. - return inProtocolExtension2; - } + if (auto subscript1 = dyn_cast(decl1)) { + auto subscript2 = cast(decl2); + if (subscript1->isGeneric() != subscript2->isGeneric()) + return completeResult(subscript2->isGeneric()); + } - // A concrete type member is always more specialised than a protocol - // member (bearing in mind that we have already handled the case where - // exactly one member is in a protocol extension). Only apply this rule in - // Swift 5 mode to better maintain source compatibility under Swift 4 - // mode. - // - // Don't apply this rule when comparing two overloads found through - // dynamic lookup to ensure we keep cases like this ambiguous: - // - // @objc protocol P { - // var i: String { get } - // } - // class C { - // @objc var i: Int { return 0 } - // } - // func foo(_ x: AnyObject) { - // x.i // ensure ambiguous. - // } - // - if (tc.Context.isSwiftVersionAtLeast(5) && !isDynamicOverloadComparison) { - auto inProto1 = isa(outerDC1); - auto inProto2 = isa(outerDC2); - if (inProto1 != inProto2) - return inProto2; - } + // Members of protocol extensions have special overloading rules. + ProtocolDecl *inProtocolExtension1 = outerDC1->getExtendedProtocolDecl(); + ProtocolDecl *inProtocolExtension2 = outerDC2->getExtendedProtocolDecl(); + if (inProtocolExtension1 && inProtocolExtension2) { + // Both members are in protocol extensions. + // Determine whether the 'Self' type from the first protocol extension + // satisfies all of the requirements of the second protocol extension. + bool better1 = isProtocolExtensionAsSpecializedAs(outerDC1, outerDC2); + bool better2 = isProtocolExtensionAsSpecializedAs(outerDC2, outerDC1); + if (better1 != better2) { + return completeResult(better1); + } + } else if (inProtocolExtension1 || inProtocolExtension2) { + // One member is in a protocol extension, the other is in a concrete type. + // Prefer the member in the concrete type. + return completeResult(inProtocolExtension2); + } - Type type1 = decl1->getInterfaceType(); - Type type2 = decl2->getInterfaceType(); + // A concrete type member is always more specialised than a protocol + // member (bearing in mind that we have already handled the case where + // exactly one member is in a protocol extension). Only apply this rule in + // Swift 5 mode to better maintain source compatibility under Swift 4 + // mode. + // + // Don't apply this rule when comparing two overloads found through + // dynamic lookup to ensure we keep cases like this ambiguous: + // + // @objc protocol P { + // var i: String { get } + // } + // class C { + // @objc var i: Int { return 0 } + // } + // func foo(_ x: AnyObject) { + // x.i // ensure ambiguous. + // } + // + if (C.isSwiftVersionAtLeast(5) && !isDynamicOverloadComparison) { + auto inProto1 = isa(outerDC1); + auto inProto2 = isa(outerDC2); + if (inProto1 != inProto2) + return completeResult(inProto2); + } - // Add curried 'self' types if necessary. - if (!decl1->hasCurriedSelf()) - type1 = type1->addCurriedSelfType(outerDC1); + Type type1 = decl1->getInterfaceType(); + Type type2 = decl2->getInterfaceType(); - if (!decl2->hasCurriedSelf()) - type2 = type2->addCurriedSelfType(outerDC2); + // Add curried 'self' types if necessary. + if (!decl1->hasCurriedSelf()) + type1 = type1->addCurriedSelfType(outerDC1); - auto openType = [&](ConstraintSystem &cs, DeclContext *innerDC, - DeclContext *outerDC, Type type, - OpenedTypeMap &replacements, - ConstraintLocator *locator) -> Type { - if (auto *funcType = type->getAs()) { - return cs.openFunctionType(funcType, locator, replacements, outerDC); - } + if (!decl2->hasCurriedSelf()) + type2 = type2->addCurriedSelfType(outerDC2); - cs.openGeneric(outerDC, innerDC->getGenericSignatureOfContext(), - locator, replacements); + auto openType = [&](ConstraintSystem &cs, DeclContext *innerDC, + DeclContext *outerDC, Type type, + OpenedTypeMap &replacements, + ConstraintLocator *locator) -> Type { + if (auto *funcType = type->getAs()) { + return cs.openFunctionType(funcType, locator, replacements, outerDC); + } - return cs.openType(type, replacements); - }; + cs.openGeneric(outerDC, innerDC->getGenericSignatureOfContext(), locator, + replacements); - // Construct a constraint system to compare the two declarations. - ConstraintSystem cs(tc, dc, ConstraintSystemOptions()); - bool knownNonSubtype = false; + return cs.openType(type, replacements); + }; - auto *locator = cs.getConstraintLocator(nullptr); - // FIXME: Locator when anchored on a declaration. - // Get the type of a reference to the second declaration. + // Construct a constraint system to compare the two declarations. + ConstraintSystem cs(dc, ConstraintSystemOptions()); + bool knownNonSubtype = false; - OpenedTypeMap unused, replacements; - auto openedType2 = - openType(cs, innerDC1, outerDC2, type2, unused, locator); - auto openedType1 = - openType(cs, innerDC2, outerDC1, type1, replacements, locator); + auto *locator = cs.getConstraintLocator(nullptr); + // FIXME: Locator when anchored on a declaration. + // Get the type of a reference to the second declaration. - for (const auto &replacement : replacements) { - if (auto mapped = innerDC1->mapTypeIntoContext(replacement.first)) { - cs.addConstraint(ConstraintKind::Bind, replacement.second, mapped, - locator); - } - } + OpenedTypeMap unused, replacements; + auto openedType2 = openType(cs, innerDC1, outerDC2, type2, unused, locator); + auto openedType1 = + openType(cs, innerDC2, outerDC1, type1, replacements, locator); - // Extract the self types from the declarations, if they have them. - auto getSelfType = [](AnyFunctionType *fnType) -> Type { - auto params = fnType->getParams(); - assert(params.size() == 1); - return params.front().getPlainType()->getMetatypeInstanceType(); - }; + for (const auto &replacement : replacements) { + if (auto mapped = innerDC1->mapTypeIntoContext(replacement.first)) { + cs.addConstraint(ConstraintKind::Bind, replacement.second, mapped, + locator); + } + } - Type selfTy1; - Type selfTy2; - if (outerDC1->isTypeContext()) { - auto funcTy1 = openedType1->castTo(); - selfTy1 = getSelfType(funcTy1); - openedType1 = funcTy1->getResult(); - } - if (outerDC2->isTypeContext()) { - auto funcTy2 = openedType2->castTo(); - selfTy2 = getSelfType(funcTy2); - openedType2 = funcTy2->getResult(); - } - - // Determine the relationship between the 'self' types and add the - // appropriate constraints. The constraints themselves never fail, but - // they help deduce type variables that were opened. - auto selfTypeRelationship = - computeSelfTypeRelationship(tc, dc, decl1, decl2); - auto relationshipKind = selfTypeRelationship.first; - auto conformance = selfTypeRelationship.second; - (void)conformance; - switch (relationshipKind) { - case SelfTypeRelationship::Unrelated: - // Skip the self types parameter entirely. - break; + // Extract the self types from the declarations, if they have them. + auto getSelfType = [](AnyFunctionType *fnType) -> Type { + auto params = fnType->getParams(); + assert(params.size() == 1); + return params.front().getPlainType()->getMetatypeInstanceType(); + }; - case SelfTypeRelationship::Equivalent: - cs.addConstraint(ConstraintKind::Bind, selfTy1, selfTy2, locator); - break; + Type selfTy1; + Type selfTy2; + if (outerDC1->isTypeContext()) { + auto funcTy1 = openedType1->castTo(); + selfTy1 = getSelfType(funcTy1); + openedType1 = funcTy1->getResult(); + } + if (outerDC2->isTypeContext()) { + auto funcTy2 = openedType2->castTo(); + selfTy2 = getSelfType(funcTy2); + openedType2 = funcTy2->getResult(); + } - case SelfTypeRelationship::Subclass: - cs.addConstraint(ConstraintKind::Subtype, selfTy1, selfTy2, locator); - break; + // Determine the relationship between the 'self' types and add the + // appropriate constraints. The constraints themselves never fail, but + // they help deduce type variables that were opened. + auto selfTypeRelationship = computeSelfTypeRelationship(dc, decl1, decl2); + auto relationshipKind = selfTypeRelationship.first; + auto conformance = selfTypeRelationship.second; + (void)conformance; + switch (relationshipKind) { + case SelfTypeRelationship::Unrelated: + // Skip the self types parameter entirely. + break; + + case SelfTypeRelationship::Equivalent: + cs.addConstraint(ConstraintKind::Bind, selfTy1, selfTy2, locator); + break; + + case SelfTypeRelationship::Subclass: + cs.addConstraint(ConstraintKind::Subtype, selfTy1, selfTy2, locator); + break; + + case SelfTypeRelationship::Superclass: + cs.addConstraint(ConstraintKind::Subtype, selfTy2, selfTy1, locator); + break; + + case SelfTypeRelationship::ConformsTo: + assert(conformance); + cs.addConstraint(ConstraintKind::ConformsTo, selfTy1, + cast(outerDC2)->getDeclaredType(), locator); + break; + + case SelfTypeRelationship::ConformedToBy: + assert(conformance); + cs.addConstraint(ConstraintKind::ConformsTo, selfTy2, + cast(outerDC1)->getDeclaredType(), locator); + break; + } - case SelfTypeRelationship::Superclass: - cs.addConstraint(ConstraintKind::Subtype, selfTy2, selfTy1, locator); - break; + bool fewerEffectiveParameters = false; + if (!decl1->hasParameterList() && !decl2->hasParameterList()) { + // If neither decl has a parameter list, simply check whether the first + // type is a subtype of the second. + cs.addConstraint(ConstraintKind::Subtype, openedType1, openedType2, + locator); + } else if (decl1->hasParameterList() && decl2->hasParameterList()) { + // Otherwise, check whether the first function type's input is a subtype + // of the second type's inputs, i.e., can we forward the arguments? + auto funcTy1 = openedType1->castTo(); + auto funcTy2 = openedType2->castTo(); + auto params1 = funcTy1->getParams(); + auto params2 = funcTy2->getParams(); + + unsigned numParams1 = params1.size(); + unsigned numParams2 = params2.size(); + if (numParams1 > numParams2) + return completeResult(false); + + // If they both have trailing closures, compare those separately. + bool compareTrailingClosureParamsSeparately = false; + if (numParams1 > 0 && numParams2 > 0 && + params1.back().getOldType()->is() && + params2.back().getOldType()->is()) { + compareTrailingClosureParamsSeparately = true; + } - case SelfTypeRelationship::ConformsTo: - assert(conformance); - cs.addConstraint(ConstraintKind::ConformsTo, selfTy1, - cast(outerDC2)->getDeclaredType(), - locator); - break; + auto maybeAddSubtypeConstraint = + [&](const AnyFunctionType::Param ¶m1, + const AnyFunctionType::Param ¶m2) -> bool { + // If one parameter is variadic and the other is not... + if (param1.isVariadic() != param2.isVariadic()) { + // If the first parameter is the variadic one, it's not + // more specialized. + if (param1.isVariadic()) + return false; - case SelfTypeRelationship::ConformedToBy: - assert(conformance); - cs.addConstraint(ConstraintKind::ConformsTo, selfTy2, - cast(outerDC1)->getDeclaredType(), - locator); - break; + fewerEffectiveParameters = true; } - bool fewerEffectiveParameters = false; - if (!decl1->hasParameterList() && !decl2->hasParameterList()) { - // If neither decl has a parameter list, simply check whether the first - // type is a subtype of the second. - cs.addConstraint(ConstraintKind::Subtype, - openedType1, - openedType2, - locator); - } else if (decl1->hasParameterList() && decl2->hasParameterList()) { - // Otherwise, check whether the first function type's input is a subtype - // of the second type's inputs, i.e., can we forward the arguments? - auto funcTy1 = openedType1->castTo(); - auto funcTy2 = openedType2->castTo(); - auto params1 = funcTy1->getParams(); - auto params2 = funcTy2->getParams(); - - unsigned numParams1 = params1.size(); - unsigned numParams2 = params2.size(); - if (numParams1 > numParams2) return false; - - // If they both have trailing closures, compare those separately. - bool compareTrailingClosureParamsSeparately = false; - if (numParams1 > 0 && numParams2 > 0 && - params1.back().getOldType()->is() && - params2.back().getOldType()->is()) { - compareTrailingClosureParamsSeparately = true; - } - - auto maybeAddSubtypeConstraint = - [&](const AnyFunctionType::Param ¶m1, - const AnyFunctionType::Param ¶m2) -> bool { - // If one parameter is variadic and the other is not... - if (param1.isVariadic() != param2.isVariadic()) { - // If the first parameter is the variadic one, it's not - // more specialized. - if (param1.isVariadic()) return false; + Type paramType1 = getAdjustedParamType(param1); + Type paramType2 = getAdjustedParamType(param2); - fewerEffectiveParameters = true; - } + // Check whether the first parameter is a subtype of the second. + cs.addConstraint(ConstraintKind::Subtype, paramType1, paramType2, + locator); + return true; + }; - Type paramType1 = getAdjustedParamType(param1); - Type paramType2 = getAdjustedParamType(param2); - - // Check whether the first parameter is a subtype of the second. - cs.addConstraint(ConstraintKind::Subtype, - paramType1, paramType2, locator); - return true; - }; - - auto pairMatcher = [&](unsigned idx1, unsigned idx2) -> bool { - // Emulate behavior from when IUO was a type, where IUOs - // were considered subtypes of plain optionals, but not - // vice-versa. This wouldn't normally happen, but there are - // cases where we can rename imported APIs so that we have a - // name collision, and where the parameter type(s) are the - // same except for details of the kind of optional declared. - auto param1IsIUO = paramIsIUO(decl1, idx1); - auto param2IsIUO = paramIsIUO(decl2, idx2); - if (param2IsIUO && !param1IsIUO) - return false; - - if (!maybeAddSubtypeConstraint(params1[idx1], params2[idx2])) - return false; - - return true; - }; - - ParameterListInfo paramInfo( - params2, decl2, decl2->hasCurriedSelf()); - auto params2ForMatching = params2; - if (compareTrailingClosureParamsSeparately) { - --numParams1; - params2ForMatching = params2.drop_back(); - } + auto pairMatcher = [&](unsigned idx1, unsigned idx2) -> bool { + // Emulate behavior from when IUO was a type, where IUOs + // were considered subtypes of plain optionals, but not + // vice-versa. This wouldn't normally happen, but there are + // cases where we can rename imported APIs so that we have a + // name collision, and where the parameter type(s) are the + // same except for details of the kind of optional declared. + auto param1IsIUO = paramIsIUO(decl1, idx1); + auto param2IsIUO = paramIsIUO(decl2, idx2); + if (param2IsIUO && !param1IsIUO) + return false; - InputMatcher IM(params2ForMatching, paramInfo); - if (IM.match(numParams1, pairMatcher) != InputMatcher::IM_Succeeded) - return false; + if (!maybeAddSubtypeConstraint(params1[idx1], params2[idx2])) + return false; - fewerEffectiveParameters |= (IM.getNumSkippedParameters() != 0); + return true; + }; - if (compareTrailingClosureParamsSeparately) - if (!maybeAddSubtypeConstraint(params1.back(), params2.back())) - knownNonSubtype = true; - } + ParameterListInfo paramInfo(params2, decl2, decl2->hasCurriedSelf()); + auto params2ForMatching = params2; + if (compareTrailingClosureParamsSeparately) { + --numParams1; + params2ForMatching = params2.drop_back(); + } - if (!knownNonSubtype) { - // Solve the system. - auto solution = cs.solveSingle(FreeTypeVariableBinding::Allow); + InputMatcher IM(params2ForMatching, paramInfo); + if (IM.match(numParams1, pairMatcher) != InputMatcher::IM_Succeeded) + return completeResult(false); - // Ban value-to-optional conversions. - if (solution && solution->getFixedScore().Data[SK_ValueToOptional] == 0) - return true; - } + fewerEffectiveParameters |= (IM.getNumSkippedParameters() != 0); - // If the first function has fewer effective parameters than the - // second, it is more specialized. - if (fewerEffectiveParameters) return true; + if (compareTrailingClosureParamsSeparately) + if (!maybeAddSubtypeConstraint(params1.back(), params2.back())) + knownNonSubtype = true; + } - return false; - }; + if (!knownNonSubtype) { + // Solve the system. + auto solution = cs.solveSingle(FreeTypeVariableBinding::Allow); - tc.specializedOverloadComparisonCache[overloadComparisonKey] = - compareSpecializations(); - } else if (tc.getLangOpts().DebugConstraintSolver) { - auto &log = tc.Context.TypeCheckerDebug->getStream(); - log << "Found cached comparison: " - << tc.specializedOverloadComparisonCache[overloadComparisonKey] << "\n"; + // Ban value-to-optional conversions. + if (solution && solution->getFixedScore().Data[SK_ValueToOptional] == 0) + return completeResult(true); } - if (tc.getLangOpts().DebugConstraintSolver) { - auto &log = tc.Context.TypeCheckerDebug->getStream(); - auto result = tc.specializedOverloadComparisonCache[overloadComparisonKey]; - log << "comparison result: " << (result ? "better" : "not better") << "\n"; - } + // If the first function has fewer effective parameters than the + // second, it is more specialized. + if (fewerEffectiveParameters) + return completeResult(true); - return tc.specializedOverloadComparisonCache[overloadComparisonKey]; + return completeResult(false); } Comparison TypeChecker::compareDeclarations(DeclContext *dc, ValueDecl *decl1, ValueDecl *decl2){ - bool decl1Better = isDeclAsSpecializedAs(*this, dc, decl1, decl2); - bool decl2Better = isDeclAsSpecializedAs(*this, dc, decl2, decl1); + bool decl1Better = isDeclAsSpecializedAs(dc, decl1, decl2); + bool decl2Better = isDeclAsSpecializedAs(dc, decl2, decl1); if (decl1Better == decl2Better) return Comparison::Unordered; - return decl1Better? Comparison::Better : Comparison::Worse; + return decl1Better ? Comparison::Better : Comparison::Worse; } static Type getUnlabeledType(Type type, ASTContext &ctx) { @@ -716,11 +708,39 @@ static Type getUnlabeledType(Type type, ASTContext &ctx) { }); } +static void addKeyPathDynamicMemberOverloads( + ArrayRef solutions, unsigned idx1, unsigned idx2, + SmallVectorImpl &overloadDiff) { + const auto &overloads1 = solutions[idx1].overloadChoices; + const auto &overloads2 = solutions[idx2].overloadChoices; + + for (auto &entry : overloads1) { + auto *locator = entry.first; + if (!locator->isForKeyPathDynamicMemberLookup()) + continue; + + auto overload2 = overloads2.find(locator); + if (overload2 == overloads2.end()) + continue; + + auto &overloadChoice1 = entry.second.choice; + auto &overloadChoice2 = overload2->second.choice; + + SmallVector choices; + choices.resize(solutions.size()); + + choices[idx1] = overloadChoice1; + choices[idx2] = overloadChoice2; + + overloadDiff.push_back( + SolutionDiff::OverloadDiff{locator, std::move(choices)}); + } +} + SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef solutions, - const SolutionDiff &diff, unsigned idx1, unsigned idx2, - llvm::DenseMap> &weights) { - if (cs.TC.getLangOpts().DebugConstraintSolver) { + const SolutionDiff &diff, unsigned idx1, unsigned idx2) { + if (cs.getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log.indent(cs.solverState->depth * 2) << "comparing solutions " << idx1 << " and " << idx2 <<"\n"; @@ -751,16 +771,25 @@ SolutionCompareResult ConstraintSystem::compareSolutions( auto getWeight = [&](ConstraintLocator *locator) -> unsigned { if (auto *anchor = locator->getAnchor()) { - auto weight = weights.find(anchor); - if (weight != weights.end()) - return weight->getSecond().first + 1; + auto weight = cs.getExprDepth(anchor); + if (weight) + return *weight + 1; } return 1; }; + SmallVector overloadDiff(diff.overloads); + // Single type of keypath dynamic member lookup could refer to different + // member overlaods, we have to do a pair-wise comparison in such cases + // otherwise ranking would miss some viable information e.g. + // `_ = arr[0..<3]` could refer to subscript through writable or read-only + // key path and each of them could also pick overload which returns `Slice` + // or `ArraySlice` (assuming that `arr` is something like `Box<[Int]>`). + addKeyPathDynamicMemberOverloads(solutions, idx1, idx2, overloadDiff); + // Compare overload sets. - for (auto &overload : diff.overloads) { + for (auto &overload : overloadDiff) { unsigned weight = getWeight(overload.locator); auto choice1 = overload.choices[idx1]; @@ -850,7 +879,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions( } // The kinds of overload choice match, but the contents don't. - auto &tc = cs.getTypeChecker(); switch (choice1.getKind()) { case OverloadChoiceKind::TupleIndex: continue; @@ -877,12 +905,12 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // Determine whether one declaration is more specialized than the other. bool firstAsSpecializedAs = false; bool secondAsSpecializedAs = false; - if (isDeclAsSpecializedAs(tc, cs.DC, decl1, decl2, + if (isDeclAsSpecializedAs(cs.DC, decl1, decl2, isDynamicOverloadComparison)) { score1 += weight; firstAsSpecializedAs = true; } - if (isDeclAsSpecializedAs(tc, cs.DC, decl2, decl1, + if (isDeclAsSpecializedAs(cs.DC, decl2, decl1, isDynamicOverloadComparison)) { score2 += weight; secondAsSpecializedAs = true; @@ -909,9 +937,9 @@ SolutionCompareResult ConstraintSystem::compareSolutions( ctor2->getResultInterfaceType()); if (!resType1->isEqual(resType2)) { - if (tc.isSubtypeOf(resType1, resType2, cs.DC)) { + if (TypeChecker::isSubtypeOf(resType1, resType2, cs.DC)) { score1 += weight; - } else if (tc.isSubtypeOf(resType2, resType1, cs.DC)) { + } else if (TypeChecker::isSubtypeOf(resType2, resType1, cs.DC)) { score2 += weight; } } @@ -1002,7 +1030,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // compatibility under Swift 4 mode by ensuring we don't introduce any new // ambiguities. This will become a more general "is more specialised" rule // in Swift 5 mode. - if (!tc.Context.isSwiftVersionAtLeast(5) && + if (!cs.getASTContext().isSwiftVersionAtLeast(5) && choice1.getKind() != OverloadChoiceKind::DeclViaDynamic && choice2.getKind() != OverloadChoiceKind::DeclViaDynamic && isa(decl1) && isa(decl2)) { @@ -1050,20 +1078,37 @@ SolutionCompareResult ConstraintSystem::compareSolutions( } // Compare the type variable bindings. - auto &tc = cs.getTypeChecker(); - for (auto &binding : diff.typeBindings) { + llvm::DenseMap> typeDiff; + + const auto &bindings1 = solutions[idx1].typeBindings; + const auto &bindings2 = solutions[idx2].typeBindings; + + for (const auto &binding1 : bindings1) { + auto *typeVar = binding1.first; + // If the type variable isn't one for which we should be looking at the // bindings, don't. - if (!binding.typeVar->getImpl().prefersSubtypeBinding()) + if (!typeVar->getImpl().prefersSubtypeBinding()) continue; - auto type1 = binding.bindings[idx1]; - auto type2 = binding.bindings[idx2]; - - // If the types are equivalent, there's nothing more to do. - if (type1->isEqual(type2)) + // If both solutions have a binding for this type variable + // let's consider it. + auto binding2 = bindings2.find(typeVar); + if (binding2 == bindings2.end()) continue; - + + auto concreteType1 = binding1.second; + auto concreteType2 = binding2->second; + + if (!concreteType1->isEqual(concreteType2)) { + typeDiff.insert({typeVar, {concreteType1, concreteType2}}); + } + } + + for (auto &binding : typeDiff) { + auto type1 = binding.second.first; + auto type2 = binding.second.second; + // If either of the types still contains type variables, we can't // compare them. // FIXME: This is really unfortunate. More type variable sharing @@ -1073,11 +1118,26 @@ SolutionCompareResult ConstraintSystem::compareSolutions( continue; } + // With introduction of holes it's currently possible to form solutions + // with UnresolvedType bindings, we need to account for that in + // ranking. If one solution has a hole for a given type variable + // it's always worse than any non-hole type other solution might have. + if (type1->is() || type2->is()) { + if (type1->is()) { + ++score2; + } else { + ++score1; + } + + identical = false; + continue; + } + // If one type is a subtype of the other, but not vice-versa, // we prefer the system with the more-constrained type. // FIXME: Collapse this check into the second check. - auto type1Better = tc.isSubtypeOf(type1, type2, cs.DC); - auto type2Better = tc.isSubtypeOf(type2, type1, cs.DC); + auto type1Better = TypeChecker::isSubtypeOf(type1, type2, cs.DC); + auto type2Better = TypeChecker::isSubtypeOf(type2, type1, cs.DC); if (type1Better || type2Better) { if (type1Better) ++score1; @@ -1126,20 +1186,20 @@ SolutionCompareResult ConstraintSystem::compareSolutions( if (!(score1 || score2)) { if (auto nominalType2 = type2->getNominalOrBoundGenericNominal()) { if ((nominalType2->getName() == - cs.TC.Context.Id_OptionalNilComparisonType)) { + cs.getASTContext().Id_OptionalNilComparisonType)) { ++score2; } } if (auto nominalType1 = type1->getNominalOrBoundGenericNominal()) { if ((nominalType1->getName() == - cs.TC.Context.Id_OptionalNilComparisonType)) { + cs.getASTContext().Id_OptionalNilComparisonType)) { ++score1; } } } } - + // All other things considered equal, if any overload choice is more // more constrained than the other, increment the score. if (score1 == score2) { @@ -1162,7 +1222,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // All other things being equal, apply the Swift 4.1 compatibility hack for // preferring var members in concrete types over a protocol requirement // (see the comment above for the rationale of this hack). - if (!tc.Context.isSwiftVersionAtLeast(5) && score1 == score2) { + if (!cs.getASTContext().isSwiftVersionAtLeast(5) && score1 == score2) { score1 += isVarAndNotProtocol1; score2 += isVarAndNotProtocol2; } @@ -1190,7 +1250,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (viable.size() == 1) return 0; - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState->depth * 2) << "Comparing " << viable.size() << " viable solutions\n"; @@ -1207,7 +1267,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, SmallVector losers(viable.size(), false); unsigned bestIdx = 0; for (unsigned i = 1, n = viable.size(); i != n; ++i) { - switch (compareSolutions(*this, viable, diff, i, bestIdx, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, i, bestIdx)) { case SolutionCompareResult::Identical: // FIXME: Might want to warn about this in debug builds, so we can // find a way to eliminate the redundancy in the search space. @@ -1231,7 +1291,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (i == bestIdx) continue; - switch (compareSolutions(*this, viable, diff, bestIdx, i, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, bestIdx, i)) { case SolutionCompareResult::Identical: // FIXME: Might want to warn about this in debug builds, so we can // find a way to eliminate the redundancy in the search space. @@ -1283,7 +1343,7 @@ ConstraintSystem::findBestSolution(SmallVectorImpl &viable, if (losers[j]) continue; - switch (compareSolutions(*this, viable, diff, i, j, ExprWeights)) { + switch (compareSolutions(*this, viable, diff, i, j)) { case SolutionCompareResult::Identical: // FIXME: Dub one of these the loser arbitrarily? break; @@ -1326,12 +1386,6 @@ SolutionDiff::SolutionDiff(ArrayRef solutions) { if (solutions.size() <= 1) return; - // Populate the type bindings with the first solution. - llvm::DenseMap> typeBindings; - for (auto binding : solutions[0].typeBindings) { - typeBindings[binding.first].push_back(binding.second); - } - // Populate the overload choices with the first solution. llvm::DenseMap> overloadChoices; @@ -1342,27 +1396,6 @@ SolutionDiff::SolutionDiff(ArrayRef solutions) { // Find the type variables and overload locators common to all of the // solutions. for (auto &solution : solutions.slice(1)) { - // For each type variable bound in all of the previous solutions, check - // whether we have a binding for this type variable in this solution. - SmallVector removeTypeBindings; - for (auto &binding : typeBindings) { - auto known = solution.typeBindings.find(binding.first); - if (known == solution.typeBindings.end()) { - removeTypeBindings.push_back(binding.first); - continue; - } - - // Add this solution's binding to the results. - binding.second.push_back(known->second); - } - - // Remove those type variables for which this solution did not have a - // binding. - for (auto typeVar : removeTypeBindings) { - typeBindings.erase(typeVar); - } - removeTypeBindings.clear(); - // For each overload locator for which we have an overload choice in // all of the previous solutions. Check whether we have an overload choice // in this solution. @@ -1385,26 +1418,6 @@ SolutionDiff::SolutionDiff(ArrayRef solutions) { } } - // Look through the type variables that have bindings in all of the - // solutions, and add those that have differences to the diff. - for (auto &binding : typeBindings) { - Type singleType; - for (auto type : binding.second) { - if (!singleType) - singleType = type; - else if (!singleType->isEqual(type)) { - // We have a difference. Add this binding to the diff. - this->typeBindings.push_back( - SolutionDiff::TypeBindingDiff{ - binding.first, - std::move(binding.second) - }); - - break; - } - } - } - for (auto &overloadChoice : overloadChoices) { OverloadChoice singleChoice = overloadChoice.second[0]; for (auto choice : overloadChoice.second) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a5c04024fb719..71d01c19f9c19 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" @@ -34,7 +35,7 @@ using namespace constraints; MatchCallArgumentListener::~MatchCallArgumentListener() { } -void MatchCallArgumentListener::extraArgument(unsigned argIdx) { } +bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; } Optional MatchCallArgumentListener::missingArgument(unsigned paramIdx) { @@ -122,10 +123,9 @@ bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy, return true; } -static bool -areConservativelyCompatibleArgumentLabels(OverloadChoice choice, - ArrayRef args, - bool hasTrailingClosure) { +static bool areConservativelyCompatibleArgumentLabels( + OverloadChoice choice, SmallVectorImpl &args, + bool hasTrailingClosure) { ValueDecl *decl = nullptr; switch (choice.getKind()) { case OverloadChoiceKind::Decl: @@ -216,7 +216,7 @@ static bool acceptsTrailingClosure(const AnyFunctionType::Param ¶m) { // FIXME: This should return ConstraintSystem::TypeMatchResult instead // to give more information to the solver about the failure. bool constraints:: -matchCallArguments(ArrayRef args, +matchCallArguments(SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, bool hasTrailingClosure, @@ -430,17 +430,56 @@ matchCallArguments(ArrayRef args, // If we have a trailing closure, it maps to the last parameter. if (hasTrailingClosure && numParams > 0) { + unsigned lastParamIdx = numParams - 1; + bool lastAcceptsTrailingClosure = + acceptsTrailingClosure(params[lastParamIdx]); + + // If the last parameter is defaulted, this might be + // an attempt to use a trailing closure with previous + // parameter that accepts a function type e.g. + // + // func foo(_: () -> Int, _ x: Int = 0) {} + // foo { 42 } + if (!lastAcceptsTrailingClosure && numParams > 1 && + paramInfo.hasDefaultArgument(lastParamIdx)) { + auto paramType = params[lastParamIdx - 1].getPlainType(); + // If the parameter before defaulted last accepts. + if (paramType->is()) { + lastAcceptsTrailingClosure = true; + lastParamIdx -= 1; + } + } + + bool isExtraClosure = false; // If there is no suitable last parameter to accept the trailing closure, // notify the listener and bail if we need to. - if (!acceptsTrailingClosure(params[numParams - 1])) { - if (listener.trailingClosureMismatch(numParams - 1, numArgs - 1)) + if (!lastAcceptsTrailingClosure) { + if (numArgs > numParams) { + // Argument before the trailing closure. + unsigned prevArg = numArgs - 2; + auto &arg = args[prevArg]; + // If the argument before trailing closure matches + // last parameter, this is just a special case of + // an extraneous argument. + const auto param = params[numParams - 1]; + if (param.hasLabel() && param.getLabel() == arg.getLabel()) { + isExtraClosure = true; + if (listener.extraArgument(numArgs - 1)) + return true; + } + } + + if (!isExtraClosure && + listener.trailingClosureMismatch(lastParamIdx, numArgs - 1)) return true; } // Claim the parameter/argument pair. claimedArgs[numArgs-1] = true; ++numClaimedArgs; - parameterBindings[numParams-1].push_back(numArgs-1); + // Let's claim the trailing closure unless it's an extra argument. + if (!isExtraClosure) + parameterBindings[lastParamIdx].push_back(numArgs - 1); } // Mark through the parameters, binding them to their arguments. @@ -560,12 +599,16 @@ matchCallArguments(ArrayRef args, } } - // If we still haven't claimed all of the arguments, fail. + // If we still haven't claimed all of the arguments, + // fail if there is no recovery. if (numClaimedArgs != numArgs) { - nextArgIdx = 0; - skipClaimedArgs(); - listener.extraArgument(nextArgIdx); - return true; + for (auto index : indices(claimedArgs)) { + if (claimedArgs[index]) + continue; + + if (listener.extraArgument(index)) + return true; + } } // FIXME: If we had the actual parameters and knew the body names, those @@ -575,7 +618,6 @@ matchCallArguments(ArrayRef args, // If we have any unfulfilled parameters, check them now. if (haveUnfulfilledParams) { - bool hasSynthesizedArgs = false; for (paramIdx = 0; paramIdx != numParams; ++paramIdx) { // If we have a binding for this parameter, we're done. if (!parameterBindings[paramIdx].empty()) @@ -593,17 +635,11 @@ matchCallArguments(ArrayRef args, if (auto newArgIdx = listener.missingArgument(paramIdx)) { parameterBindings[paramIdx].push_back(*newArgIdx); - hasSynthesizedArgs = true; continue; } return true; } - - // If all of the missing arguments have been synthesized, - // let's stop since we have found the problem. - if (hasSynthesizedArgs) - return false; } // If any arguments were provided out-of-order, check whether we have @@ -728,62 +764,6 @@ matchCallArguments(ArrayRef args, return listener.relabelArguments(actualArgNames); } -/// Find the callee declaration and uncurry level for a given call -/// locator. -static std::tuple, bool, - ConstraintLocator *> -getCalleeDeclAndArgs(ConstraintSystem &cs, - ConstraintLocatorBuilder callBuilder) { - auto formUnknownCallee = - []() -> std::tuple, bool, - ConstraintLocator *> { - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - /*argLabels*/ ArrayRef(), - /*hasTrailingClosure*/ false, - /*calleeLocator*/ nullptr); - }; - - auto *callLocator = cs.getConstraintLocator(callBuilder); - auto *callExpr = callLocator->getAnchor(); - - // Break down the call. - if (!callExpr) - return formUnknownCallee(); - - // Our remaining path can only be 'ApplyArgument'. - auto path = callLocator->getPath(); - if (!path.empty() && - !(path.size() <= 2 && - path.back().getKind() == ConstraintLocator::ApplyArgument)) - return formUnknownCallee(); - - // Dig out the callee information. - auto argInfo = cs.getArgumentInfo(callLocator); - if (!argInfo) - return formUnknownCallee(); - - auto argLabels = argInfo->Labels; - auto hasTrailingClosure = argInfo->HasTrailingClosure; - auto calleeLocator = cs.getCalleeLocator(callLocator); - - // Find the overload choice corresponding to the callee locator. - // FIXME: This linearly walks the list of resolved overloads, which is - // potentially very expensive. - auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); - - // If we didn't find any matching overloads, we're done. Just return the - // argument info. - if (!selectedOverload) - return std::make_tuple(/*decl*/ nullptr, /*hasAppliedSelf*/ false, - argLabels, hasTrailingClosure, - /*calleeLocator*/ nullptr); - - // Return the found declaration, assuming there is one. - auto choice = selectedOverload->Choice; - return std::make_tuple(choice.getDeclOrNull(), hasAppliedSelf(cs, choice), - argLabels, hasTrailingClosure, calleeLocator); -} - class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintSystem &CS; SmallVectorImpl &Arguments; @@ -792,6 +772,7 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { ConstraintLocatorBuilder Locator; unsigned NumSynthesizedArgs = 0; + SmallVector, 4> ExtraArguments; public: ArgumentFailureTracker(ConstraintSystem &cs, @@ -823,19 +804,13 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { unsigned newArgIdx = Arguments.size(); auto *argLoc = CS.getConstraintLocator( - Locator - .withPathElement(LocatorPathElt::ApplyArgToParam( - newArgIdx, paramIdx, param.getParameterFlags())) - .withPathElement(LocatorPathElt::SynthesizedArgument(newArgIdx))); + Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx, + param.getParameterFlags()), + LocatorPathElt::SynthesizedArgument(newArgIdx)}); auto *argType = CS.createTypeVariable(argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - CS.recordHole(argType); - CS.addUnsolvedConstraint( - Constraint::create(CS, ConstraintKind::Defaultable, argType, - CS.getASTContext().TheAnyType, argLoc)); + TVO_CanBindToNoEscape | TVO_CanBindToHole); Arguments.push_back(param.withType(argType)); ++NumSynthesizedArgs; @@ -843,6 +818,14 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { return newArgIdx; } + bool extraArgument(unsigned argIdx) override { + if (!CS.shouldAttemptFixes()) + return true; + + ExtraArguments.push_back(std::make_pair(argIdx, Arguments[argIdx])); + return false; + } + bool missingLabel(unsigned paramIndex) override { return !CS.shouldAttemptFixes(); } @@ -857,6 +840,15 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override { if (CS.shouldAttemptFixes()) { + // If some of the arguments are missing/extraneous, no reason to + // record a fix for this, increase the score so there is a way + // to identify that there is something going on besides just missing + // arguments. + if (NumSynthesizedArgs || !ExtraArguments.empty()) { + CS.increaseScore(SK_Fix); + return false; + } + auto *fix = MoveOutOfOrderArgument::create( CS, argIdx, prevArgIdx, Bindings, CS.getConstraintLocator(Locator)); return CS.recordFix(fix); @@ -869,56 +861,128 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { if (!CS.shouldAttemptFixes()) return true; + // TODO(diagnostics): If re-labeling is mixed with extra arguments, + // let's produce a fix only for extraneous arguments for now, + // because they'd share a locator path which (currently) means + // one fix would overwrite another. + if (!ExtraArguments.empty()) { + CS.increaseScore(SK_Fix); + return false; + } + auto *anchor = Locator.getBaseLocator()->getAnchor(); if (!anchor) return true; unsigned numExtraneous = 0; - for (unsigned paramIdx = 0, n = Bindings.size(); paramIdx != n; - ++paramIdx) { - if (Bindings[paramIdx].empty()) + unsigned numRenames = 0; + unsigned numOutOfOrder = 0; + + for (unsigned i : indices(newLabels)) { + // It's already known how many arguments are missing, + // it would be accounted for in the impact. + if (i >= Arguments.size()) continue; - const auto paramLabel = Parameters[paramIdx].getLabel(); - for (auto argIdx : Bindings[paramIdx]) { - auto argLabel = Arguments[argIdx].getLabel(); - if (paramLabel.empty() && !argLabel.empty()) + auto argLabel = Arguments[i].getLabel(); + auto paramLabel = newLabels[i]; + + if (argLabel == paramLabel) + continue; + + if (!argLabel.empty()) { + // Instead of this being a label mismatch which requires + // re-labeling, this could be an out-of-order argument + // instead which has a completely different impact. + if (llvm::count(newLabels, argLabel) == 1) { + ++numOutOfOrder; + } else if (paramLabel.empty()) { ++numExtraneous; + } else { + ++numRenames; + } } } auto *locator = CS.getConstraintLocator(Locator); auto *fix = RelabelArguments::create(CS, newLabels, locator); - CS.recordFix(fix); - // Re-labeling fixes with extraneous labels should take - // lower priority vs. other fixes on same/different - // overload(s) where labels did line up correctly. - CS.increaseScore(ScoreKind::SK_Fix, numExtraneous); - return false; + // Re-labeling fixes with extraneous/incorrect labels should be + // lower priority vs. other fixes on same/different overload(s) + // where labels did line up correctly. + // + // If there are not only labeling problems but also some of the + // arguments are missing, let's account of that in the impact. + auto impact = 1 + numOutOfOrder + numExtraneous * 2 + numRenames * 3 + + NumSynthesizedArgs * 2; + return CS.recordFix(fix, impact); + } + + bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override { + if (!CS.shouldAttemptFixes()) + return true; + + const auto ¶m = Parameters[paramIdx]; + + auto *argLoc = CS.getConstraintLocator( + Locator.withPathElement(LocatorPathElt::ApplyArgToParam( + argIdx, paramIdx, param.getParameterFlags()))); + + // TODO(diagnostics): This fix should be attempted later + // when the argument is matched to a parameter. Doing that + // would not require special handling of the closure type. + Type argType; + if (auto *closure = + dyn_cast(simplifyLocatorToAnchor(argLoc))) { + argType = CS.getClosureType(closure); + } else { + argType = Arguments[argIdx].getPlainType(); + } + + argType.visit([&](Type type) { + if (auto *typeVar = type->getAs()) + CS.recordPotentialHole(typeVar); + }); + + auto *fix = AllowInvalidUseOfTrailingClosure::create( + CS, argType, param.getPlainType(), argLoc); + return CS.recordFix(fix, /*impact=*/3); + } + + ArrayRef> + getExtraneousArguments() const { + return ExtraArguments; } }; // Match the argument of a call to the parameter. ConstraintSystem::TypeMatchResult constraints::matchCallArguments( - ConstraintSystem &cs, ArrayRef args, + ConstraintSystem &cs, FunctionType *contextualType, + ArrayRef args, ArrayRef params, ConstraintKind subKind, ConstraintLocatorBuilder locator) { - // Extract the parameters. - ValueDecl *callee; - bool hasAppliedSelf; - ArrayRef argLabels; - bool hasTrailingClosure = false; - ConstraintLocator *calleeLocator; - std::tie(callee, hasAppliedSelf, argLabels, hasTrailingClosure, - calleeLocator) = - getCalleeDeclAndArgs(cs, locator); - - ParameterListInfo paramInfo(params, callee, hasAppliedSelf); + auto *loc = cs.getConstraintLocator(locator); + assert(loc->isLastElement()); + + ValueDecl *callee = nullptr; + bool appliedSelf = false; + + // Resolve the callee for the application. + auto *calleeLocator = cs.getCalleeLocator(loc); + if (auto overload = cs.findSelectedOverloadFor(calleeLocator)) { + callee = overload->choice.getDeclOrNull(); + appliedSelf = hasAppliedSelf(cs, overload->choice); + } + + ParameterListInfo paramInfo(params, callee, appliedSelf); + + // Dig out the argument information. + auto argInfo = cs.getArgumentInfo(loc); + assert(argInfo); // Apply labels to arguments. SmallVector argsWithLabels; argsWithLabels.append(args.begin(), args.end()); - AnyFunctionType::relabelParams(argsWithLabels, argLabels); + AnyFunctionType::relabelParams(argsWithLabels, argInfo->Labels); // Special case when a single tuple argument if used // instead of N distinct arguments e.g.: @@ -960,14 +1024,28 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings, locator); if (constraints::matchCallArguments( - argsWithLabels, params, paramInfo, hasTrailingClosure, - cs.shouldAttemptFixes(), listener, parameterBindings)) { - if (!cs.shouldAttemptFixes()) + argsWithLabels, params, paramInfo, argInfo->HasTrailingClosure, + cs.shouldAttemptFixes(), listener, parameterBindings)) + return cs.getTypeMatchFailure(locator); + + auto extraArguments = listener.getExtraneousArguments(); + if (!extraArguments.empty()) { + if (RemoveExtraneousArguments::isMinMaxNameShadowing(cs, locator)) return cs.getTypeMatchFailure(locator); + // First let's see whether this is a situation where a single + // parameter is a tuple, but N distinct arguments were passed in. if (AllowTupleSplatForSingleParameter::attempt( - cs, argsWithLabels, params, parameterBindings, locator)) - return cs.getTypeMatchFailure(locator); + cs, argsWithLabels, params, parameterBindings, locator)) { + // Let's produce a generic "extraneous arguments" + // diagnostic otherwise. + auto *fix = RemoveExtraneousArguments::create( + cs, contextualType, extraArguments, + cs.getConstraintLocator(locator)); + + if (cs.recordFix(fix, /*impact=*/extraArguments.size() * 5)) + return cs.getTypeMatchFailure(locator); + } } } @@ -1026,20 +1104,6 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( } } - // If the parameter has a function builder type and the argument is a - // closure, apply the function builder transformation. - if (Type functionBuilderType - = paramInfo.getFunctionBuilderType(paramIdx)) { - Expr *arg = getArgumentExpr(locator.getAnchor(), argIdx); - if (auto closure = dyn_cast_or_null(arg)) { - auto result = - cs.applyFunctionBuilder(closure, functionBuilderType, - calleeLocator, loc); - if (result.isFailure()) - return result; - } - } - // If argument comes for declaration it should loose // `@autoclosure` flag, because in context it's used // as a function type represented by autoclosure. @@ -1139,10 +1203,12 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a conversion"); } @@ -1203,9 +1269,11 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: return false; } @@ -1268,9 +1336,9 @@ assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType, return 1; unsigned choiceImpact = 0; - if (auto *choice = cs.findSelectedOverloadFor(ODRE)) { + if (auto choice = cs.findSelectedOverloadFor(ODRE)) { auto *typeVar = requirementType->castTo(); - choice->ImpliedType.visit([&](Type type) { + choice->openedType.visit([&](Type type) { if (type->isEqual(typeVar)) ++choiceImpact; }); @@ -1318,7 +1386,7 @@ static bool fixMissingArguments(ConstraintSystem &cs, Expr *anchor, // (which might be anonymous), it's most likely used as a // tuple e.g. `$0.0`. Optional argumentTuple; - if (isa(anchor) && isSingleTupleParam(ctx, args)) { + if (isSingleTupleParam(ctx, args)) { auto argType = args.back().getPlainType(); // Let's unpack argument tuple into N arguments, this corresponds // to something like `foo { (bar: (Int, Int)) in }` where `foo` @@ -1339,24 +1407,26 @@ static bool fixMissingArguments(ConstraintSystem &cs, Expr *anchor, }; // Something like `foo { x in }` or `foo { $0 }` - anchor->forEachChildExpr([&](Expr *expr) -> Expr * { - if (auto *UDE = dyn_cast(expr)) { - if (!isParam(UDE->getBase())) - return expr; - - auto name = UDE->getName().getBaseIdentifier(); - unsigned index = 0; - if (!name.str().getAsInteger(10, index) || - llvm::any_of(params, [&](const AnyFunctionType::Param ¶m) { - return param.getLabel() == name; - })) { - argumentTuple.emplace(typeVar); - args.pop_back(); - return nullptr; + if (isa(anchor)) { + anchor->forEachChildExpr([&](Expr *expr) -> Expr * { + if (auto *UDE = dyn_cast(expr)) { + if (!isParam(UDE->getBase())) + return expr; + + auto name = UDE->getName().getBaseIdentifier(); + unsigned index = 0; + if (!name.str().getAsInteger(10, index) || + llvm::any_of(params, [&](const AnyFunctionType::Param ¶m) { + return param.getLabel() == name; + })) { + argumentTuple.emplace(typeVar); + args.pop_back(); + return nullptr; + } } - } - return expr; - }); + return expr; + }); + } } } @@ -1386,6 +1456,26 @@ static bool fixMissingArguments(ConstraintSystem &cs, Expr *anchor, return false; } +static bool fixExtraneousArguments(ConstraintSystem &cs, + FunctionType *contextualType, + ArrayRef args, + int numExtraneous, + ConstraintLocatorBuilder locator) { + SmallVector, 4> extraneous; + + for (unsigned i = args.size() - numExtraneous, n = args.size(); i != n; ++i) { + extraneous.push_back({i, args[i]}); + if (auto *typeVar = args[i].getPlainType()->getAs()) { + cs.recordPotentialHole(typeVar); + } + } + + return cs.recordFix( + RemoveExtraneousArguments::create(cs, contextualType, extraneous, + cs.getConstraintLocator(locator)), + /*impact=*/numExtraneous * 2); +} + ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, @@ -1411,8 +1501,8 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (!shouldAttemptFixes()) return getTypeMatchFailure(locator); - auto *fix = MarkExplicitlyEscaping::create( - *this, getConstraintLocator(locator), func2); + auto *fix = MarkExplicitlyEscaping::create(*this, func1, func2, + getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); @@ -1459,10 +1549,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a relational constraint"); } @@ -1611,8 +1703,14 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, abs(diff), locator)) return getTypeMatchFailure(argumentLocator); } else { - // TODO(diagnostics): Add handling of extraneous arguments. - return getTypeMatchFailure(argumentLocator); + // If there are extraneous arguments, let's remove + // them from the list. + if (fixExtraneousArguments(*this, func2, func1Params, diff, locator)) + return getTypeMatchFailure(argumentLocator); + + // Drop all of the extraneous arguments. + auto numParams = func2Params.size(); + func1Params.erase(func1Params.begin() + numParams, func1Params.end()); } } @@ -1622,8 +1720,31 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, auto func2Param = func2Params[i]; // Variadic bit must match. - if (func1Param.isVariadic() != func2Param.isVariadic()) - return getTypeMatchFailure(argumentLocator); + if (func1Param.isVariadic() != func2Param.isVariadic()) { + if (!(shouldAttemptFixes() && func2Param.isVariadic())) + return getTypeMatchFailure(argumentLocator); + + auto argType = + getFixedTypeRecursive(func1Param.getPlainType(), /*wantRValue=*/true); + auto varargsType = func2Param.getPlainType(); + + // Delay solving this constriant until argument is resolved. + if (argType->is()) { + addUnsolvedConstraint(Constraint::create( + *this, kind, func1, func2, getConstraintLocator(locator))); + return getTypeMatchSuccess(); + } + + auto *fix = ExpandArrayIntoVarargs::attempt( + *this, argType, varargsType, + argumentLocator.withPathElement(LocatorPathElt::ApplyArgToParam( + i, i, func2Param.getParameterFlags()))); + + if (!fix || recordFix(fix)) + return getTypeMatchFailure(argumentLocator); + + continue; + } // Labels must match. // @@ -1742,8 +1863,33 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2, auto args1 = opaque1->getSubstitutions().getReplacementTypes(); auto args2 = opaque2->getSubstitutions().getReplacementTypes(); - // Match up the replacement types of the respective substitution maps. - return matchDeepTypeArguments(*this, subflags, args1, args2, locator); + + if (!shouldAttemptFixes()) { + // Match up the replacement types of the respective substitution maps. + return matchDeepTypeArguments(*this, subflags, args1, args2, locator); + } + + unsigned numMismatches = 0; + auto result = + matchDeepTypeArguments(*this, subflags, args1, args2, locator, + [&numMismatches](unsigned) { ++numMismatches; }); + + if (numMismatches > 0) { + auto *anchor = locator.getAnchor(); + // TODO(diagnostics): Only assignments are supported at the moment. + if (!(anchor && isa(anchor))) + return getTypeMatchFailure(locator); + + auto *fix = IgnoreAssignmentDestinationType::create( + *this, type1, type2, getConstraintLocator(locator)); + + if (recordFix(fix, /*impact=*/numMismatches)) + return getTypeMatchFailure(locator); + + return getTypeMatchSuccess(); + } + + return result; } // Handle protocol compositions. @@ -1893,9 +2039,8 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, return getTypeMatchSuccess(); if (shouldAttemptFixes()) { - auto &ctx = getASTContext(); - auto *fix = MarkExplicitlyEscaping::create( - *this, getConstraintLocator(locator), ctx.TheAnyType); + auto *fix = MarkExplicitlyEscaping::create(*this, type1, type2, + getConstraintLocator(locator)); if (!recordFix(fix)) return getTypeMatchSuccess(); } @@ -1926,16 +2071,21 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, if (!type1->satisfiesClassConstraint()) { if (shouldAttemptFixes()) { if (auto last = locator.last()) { - // If solver is in diagnostic mode and this is a - // superclass requirement, let's consider conformance - // to `AnyObject` as solved since actual superclass - // requirement is going to fail too (because type can't - // satisfy it), and it's more interesting from diagnostics - // perspective. + // If solver is in diagnostic mode and type1 is a hole, or if this + // is a superclass requirement, let's consider `AnyObject` + // conformance solved. The actual superclass requirement + // will also fail (because type can't satisfy it), and it's + // more interesting for diagnostics. auto req = last->getAs(); - if (req && - req->getRequirementKind() == RequirementKind::Superclass) + if (type1->isHole() || (req && + req->getRequirementKind() == RequirementKind::Superclass)) + return getTypeMatchSuccess(); + + auto *fix = fixRequirementFailure(*this, type1, type2, locator); + if (fix && !recordFix(fix)) { + recordFixedRequirement(type1, RequirementKind::Layout, type2); return getTypeMatchSuccess(); + } } } @@ -1945,9 +2095,17 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, // Subtype relation to AnyObject also allows class-bound // existentials that are not @objc and therefore carry // witness tables. - if (!type1->isClassExistentialType() && - !type1->mayHaveSuperclass()) + if (!type1->isClassExistentialType() && !type1->mayHaveSuperclass()) { + if (shouldAttemptFixes()) { + auto *fix = AllowNonClassTypeToConvertToAnyObject::create( + *this, type1, getConstraintLocator(locator)); + + return recordFix(fix) ? getTypeMatchFailure(locator) + : getTypeMatchSuccess(); + } + return getTypeMatchFailure(locator); + } } // Keep going. @@ -2002,10 +2160,31 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, break; } + + // TODO(diagnostics): If there are any requirement failures associated + // with result types which are part of a function type conversion, + // let's record general conversion mismatch in order for it to capture + // and display complete function types. + // + // Once either reacher locators or better diagnostic presentation for + // nested type failures is available this check could be removed. + if (last->is()) + return getTypeMatchFailure(locator); + } else { // There are no elements in the path auto *anchor = locator.getAnchor(); - if (!(anchor && isa(anchor))) + if (!(anchor && + (isa(anchor) || isa(anchor)))) + return getTypeMatchFailure(locator); + } + + auto *anchor = locator.getAnchor(); + if (isa(anchor)) { + auto *fix = ContextualMismatch::create( + *this, type1, type2, getConstraintLocator(locator)); + if (recordFix(fix)) return getTypeMatchFailure(locator); + break; } auto *fix = MissingConformance::forContextual( @@ -2022,15 +2201,14 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, return getTypeMatchSuccess(); } -static bool isStringCompatiblePointerBaseType(TypeChecker &TC, - DeclContext *DC, +static bool isStringCompatiblePointerBaseType(ASTContext &ctx, Type baseType) { // Allow strings to be passed to pointer-to-byte or pointer-to-void types. - if (baseType->isEqual(TC.getInt8Type(DC))) + if (baseType->isEqual(TypeChecker::getInt8Type(ctx))) return true; - if (baseType->isEqual(TC.getUInt8Type(DC))) + if (baseType->isEqual(TypeChecker::getUInt8Type(ctx))) return true; - if (baseType->isEqual(TC.Context.TheEmptyTupleType)) + if (baseType->isEqual(ctx.TheEmptyTupleType)) return true; return false; @@ -2040,7 +2218,6 @@ static bool isStringCompatiblePointerBaseType(TypeChecker &TC, /// is potentially more optional than the second type with its number of /// optionals. static bool isPotentiallyMoreOptionalThan(Type type1, Type type2) { - SmallVector optionals1; Type objType1 = type1->lookThroughAllOptionalTypes(optionals1); auto numOptionals1 = optionals1.size(); @@ -2091,8 +2268,17 @@ ConstraintSystem::matchTypesBindTypeVar( // Simplify the right-hand type and perform the "occurs" check. typeVar = getRepresentative(typeVar); type = simplifyType(type, flags); - if (!isBindable(typeVar, type)) + if (!isBindable(typeVar, type)) { + if (shouldAttemptFixes()) { + // If type variable is allowed to be a hole and it can't be bound to + // a particular (full resolved) type, just ignore this binding + // instead of re-trying it and failing later. + if (typeVar->getImpl().canBindToHole() && !type->hasTypeVariable()) + return getTypeMatchSuccess(); + } + return formUnsolvedResult(); + } // Since member lookup doesn't check requirements // it might sometimes return types which are not @@ -2154,8 +2340,8 @@ ConstraintSystem::matchTypesBindTypeVar( // but we still have a non-escaping type, fail. if (!typeVar->getImpl().canBindToNoEscape() && type->isNoEscape()) { if (shouldAttemptFixes()) { - auto *fix = MarkExplicitlyEscaping::create( - *this, getConstraintLocator(locator)); + auto *fix = MarkExplicitlyEscaping::create(*this, typeVar, type, + getConstraintLocator(locator)); if (recordFix(fix)) return getTypeMatchFailure(locator); @@ -2210,6 +2396,12 @@ ConstraintSystem::matchTypesBindTypeVar( }); } + if (typeVar->getImpl().isClosureType()) { + return resolveClosure(typeVar, type, locator) + ? getTypeMatchSuccess() + : getTypeMatchFailure(locator); + } + assignFixedType(typeVar, type); return getTypeMatchSuccess(); @@ -2254,8 +2446,7 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1, static ConstraintFix *fixPropertyWrapperFailure( ConstraintSystem &cs, Type baseTy, ConstraintLocator *locator, - llvm::function_ref - attemptFix, + llvm::function_ref attemptFix, Optional toType = None) { Expr *baseExpr = nullptr; @@ -2290,7 +2481,7 @@ static ConstraintFix *fixPropertyWrapperFailure( if (baseTy->isEqual(type)) return nullptr; - if (!attemptFix(resolvedOverload, decl, type)) + if (!attemptFix(*resolvedOverload, decl, type)) return nullptr; switch (fix) { @@ -2307,20 +2498,21 @@ static ConstraintFix *fixPropertyWrapperFailure( llvm_unreachable("Unhandled Fix type in switch"); }; - if (auto storageWrapper = cs.getStorageWrapperInformation(resolvedOverload)) { + if (auto storageWrapper = + cs.getStorageWrapperInformation(*resolvedOverload)) { if (auto *fix = applyFix(Fix::StorageWrapper, storageWrapper->first, storageWrapper->second)) return fix; } - if (auto wrapper = cs.getPropertyWrapperInformation(resolvedOverload)) { + if (auto wrapper = cs.getPropertyWrapperInformation(*resolvedOverload)) { if (auto *fix = applyFix(Fix::PropertyWrapper, wrapper->first, wrapper->second)) return fix; } if (auto wrappedProperty = - cs.getWrappedPropertyInformation(resolvedOverload)) { + cs.getWrappedPropertyInformation(*resolvedOverload)) { if (auto *fix = applyFix(Fix::WrappedValue, wrappedProperty->first, wrappedProperty->second)) return fix; @@ -2337,8 +2529,8 @@ static bool canBridgeThroughCast(ConstraintSystem &cs, Type fromType, if (fromType->isAnyObject() && toType->getClassOrBoundGenericClass()) return true; - auto &TC = cs.getTypeChecker(); - auto bridged = TC.getDynamicBridgedThroughObjCClass(cs.DC, fromType, toType); + auto bridged = TypeChecker::getDynamicBridgedThroughObjCClass(cs.DC, + fromType, toType); if (!bridged) return false; @@ -2365,8 +2557,8 @@ repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType, if (!anchor) return false; - if (auto *overload = cs.findSelectedOverloadFor(anchor)) { - auto *decl = overload->Choice.getDeclOrNull(); + if (auto overload = cs.findSelectedOverloadFor(anchor)) { + auto *decl = overload->choice.getDeclOrNull(); if (decl && decl->isImplicitlyUnwrappedOptional()) fromType = objectType1; } @@ -2380,6 +2572,114 @@ repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType, return true; } +static bool +repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintKind matchKind, + SmallVectorImpl &conversionsOrFixes, + ConstraintLocatorBuilder locator) { + fromType = fromType->getWithoutSpecifierType(); + + if (!fromType->getOptionalObjectType() || toType->is()) + return false; + + // If we have an optional type, try to force-unwrap it. + // FIXME: Should we also try '?'? + auto *anchor = locator.trySimplifyToExpr(); + if (!anchor) + return false; + + // `OptionalEvaluationExpr` doesn't add a new level of + // optionality but it could be hiding concrete types + // behind itself which we can use to better understand + // how many levels of optionality have to be unwrapped. + if (auto *OEE = dyn_cast(anchor)) { + auto type = cs.getType(OEE->getSubExpr()); + // If the type of sub-expression is optional, type of the + // `OptionalEvaluationExpr` could be safely ignored because + // it doesn't add any type information. + if (type->getOptionalObjectType()) + fromType = type; + + // If this is a conversion from optional chain to some + // other type e.g. contextual type or a parameter type, + // let's use `Bind` to match object types because + // object type of the optinal chain is a type variable. + if (matchKind >= ConstraintKind::Conversion) + matchKind = ConstraintKind::Bind; + } + + if (auto *DRE = dyn_cast(anchor)) { + if (DRE->getDecl()->isImplicit()) { + // The expression that provides the first type is implicit and never + // spelled out in source code, e.g. $match in an expression pattern. + // Thus we cannot force unwrap the first type + return false; + } + } + + if (auto *optTryExpr = dyn_cast(anchor)) { + auto subExprType = cs.getType(optTryExpr->getSubExpr()); + const bool isSwift5OrGreater = + cs.getASTContext().LangOpts.isSwiftVersionAtLeast(5); + + if (subExprType->getOptionalObjectType()) { + if (isSwift5OrGreater) { + // For 'try?' expressions, a ForceOptional fix converts 'try?' + // to 'try!'. If the sub-expression is optional, then a force-unwrap + // won't change anything in Swift 5+ because 'try?' already avoids + // adding an additional layer of Optional there. + return false; + } + } else { + // In cases when sub-expression isn't optional, 'try?' + // always adds one level of optinality regardless of + // language mode, so we can safely try to bind its + // object type to contextual type without risk of + // causing more optionality mismatches down the road. + matchKind = ConstraintKind::Bind; + } + } + + auto getObjectTypeAndUnwraps = [](Type type) -> std::pair { + SmallVector optionals; + Type objType = type->lookThroughAllOptionalTypes(optionals); + return std::make_pair(objType, optionals.size()); + }; + + Type fromObjectType, toObjectType; + unsigned fromUnwraps, toUnwraps; + + std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndUnwraps(fromType); + std::tie(toObjectType, toUnwraps) = getObjectTypeAndUnwraps(toType); + + // If `from` is not less optional than `to`, force unwrap is + // not going to help here. In case of object type of `from` + // is a type variable, let's assume that it might be optional. + if (fromUnwraps <= toUnwraps && !fromObjectType->is()) + return false; + + // If the result of optional chaining is converted to + // an optional contextual type represented by a type + // variable e.g. `T?`, there can be no optional mismatch + // because `T` could be bound to an optional of any depth. + if (isa(anchor) && toUnwraps > 0) { + auto last = locator.last(); + if (last && last->is() && + toObjectType->is()) + return false; + } + + auto result = + cs.matchTypes(fromObjectType, toObjectType, matchKind, + ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, locator); + if (!result.isSuccess()) + return false; + + conversionsOrFixes.push_back(ForceOptional::create( + cs, fromType, toType, cs.getConstraintLocator(locator))); + return true; +} + /// Attempt to repair typing failures and record fixes if needed. /// \return true if at least some of the failures has been repaired /// successfully, which allows type matcher to continue. @@ -2410,11 +2710,11 @@ bool ConstraintSystem::repairFailures( if (!anchor) return false; - auto *overload = findSelectedOverloadFor(anchor); - if (!(overload && overload->Choice.isDecl())) + auto overload = findSelectedOverloadFor(anchor); + if (!(overload && overload->choice.isDecl())) return false; - const auto &choice = overload->Choice; + const auto &choice = overload->choice; ParameterListInfo info(fnType->getParams(), choice.getDecl(), hasAppliedSelf(*this, choice)); @@ -2502,10 +2802,49 @@ bool ConstraintSystem::repairFailures( }); }; + auto markAnyTypeVarsAsPotentialHoles = [&](Type type) { + type.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + }; + if (path.empty()) { if (!anchor) return false; + if (auto *coercion = dyn_cast(anchor)) { + // Let's check whether the sub-expression is an optional type which + // is possible to unwrap (either by force or `??`) to satisfy the cast, + // otherwise we'd have to fallback to force downcast. + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, + conversionsOrFixes, + getConstraintLocator(coercion->getSubExpr()))) + return true; + + // Repair a coercion ('as') with a forced checked cast ('as!'). + if (auto *coerceToCheckCastFix = CoerceToCheckedCast::attempt( + *this, lhs, rhs, getConstraintLocator(locator))) { + conversionsOrFixes.push_back(coerceToCheckCastFix); + return true; + } + + // If it has a deep equality restriction, defer the diagnostic to + // GenericMismatch. + if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) && + !hasConversionOrRestriction( + ConversionRestrictionKind::OptionalToOptional)) { + return false; + } + + if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) + return false; + + auto *fix = ContextualMismatch::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + } + // This could be: // - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`. // - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are @@ -2522,6 +2861,41 @@ bool ConstraintSystem::repairFailures( auto *fnType = lhs->getAs(); if (fnType && fnType->getResult()->isEqual(rhs)) return true; + + auto lastComponentType = lhs->lookThroughAllOptionalTypes(); + auto keyPathResultType = rhs->lookThroughAllOptionalTypes(); + + // Propagate contextual information from/to keypath result type. + (void)matchTypes(lastComponentType, keyPathResultType, matchKind, + TMF_ApplyingFix, getConstraintLocator(locator)); + + conversionsOrFixes.push_back(IgnoreContextualType::create( + *this, lhs, rhs, getConstraintLocator(locator))); + return true; + } + + if (auto *ODRE = dyn_cast(anchor)) { + if (lhs->is()) { + conversionsOrFixes.push_back( + TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + return true; + } + } + + if (auto *OEE = dyn_cast(anchor)) { + // If concrete type of the sub-expression can't be converted to the + // type associated with optional evaluation result it could only be + // contextual mismatch where type of the top-level expression + // comes from contextual type or its parent expression. + // + // Because result type of the optional evaluation is supposed to + // represent the type of its sub-expression with added level of + // optionality if needed. + if (!lhs->getOptionalObjectType() && !lhs->hasTypeVariable()) { + conversionsOrFixes.push_back(IgnoreContextualType::create( + *this, lhs, rhs, getConstraintLocator(OEE->getSubExpr()))); + return true; + } } if (auto *AE = dyn_cast(anchor)) { @@ -2541,6 +2915,23 @@ bool ConstraintSystem::repairFailures( if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator)) return true; + // If destination is `AnyObject` it means that source doesn't conform. + if (rhs->getWithoutSpecifierType() + ->lookThroughAllOptionalTypes() + ->isAnyObject()) { + conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( + *this, lhs, rhs, getConstraintLocator(locator))); + return true; + } + + // An attempt to assign `Int?` to `String?`. + if (hasConversionOrRestriction( + ConversionRestrictionKind::OptionalToOptional)) { + conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( + *this, lhs, rhs, getConstraintLocator(locator))); + return true; + } + // If we are trying to assign e.g. `Array` to `Array` let's // give solver a chance to determine which generic parameters are // mismatched and produce a fix for that. @@ -2554,20 +2945,76 @@ bool ConstraintSystem::repairFailures( if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) return false; - // If this is an attempt to assign something to a value of optional type - // there is a possiblity that the problem is related to escapiness, so - // fix has to be delayed. if (hasConversionOrRestriction( - ConversionRestrictionKind::ValueToOptional)) - return false; - - // If the destination of an assignment is l-value type - // it leaves only possible reason for failure - a type mismatch. - if (getType(AE->getDest())->is()) { + ConversionRestrictionKind::MetatypeToExistentialMetatype) || + hasConversionOrRestriction( + ConversionRestrictionKind::ExistentialMetatypeToMetatype) || + hasConversionOrRestriction(ConversionRestrictionKind::Superclass)) { conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( *this, lhs, rhs, getConstraintLocator(locator))); return true; } + + if (hasConversionOrRestriction( + ConversionRestrictionKind::ValueToOptional)) { + lhs = lhs->lookThroughAllOptionalTypes(); + rhs = rhs->lookThroughAllOptionalTypes(); + + // If both object types are functions, let's allow the solver to + // structurally compare them before trying to fix anything. + if (lhs->is() && rhs->is()) + return false; + + // If either object type is a bound generic or existential it means + // that follow-up to value-to-optional is going to be: + // + // 1. "deep equality" check, which is handled by generic argument(s) + // mismatch fix, or + // 2. "existential" check, which is handled by a missing conformance + // fix. + if ((lhs->is() && rhs->is()) || + rhs->isAnyExistentialType()) + return false; + } + + auto *destExpr = AE->getDest(); + // Literal expression as well as call/operator application can't be + // used as an assignment destination because resulting type is immutable. + if (isa(destExpr) || isa(destExpr)) { + conversionsOrFixes.push_back( + TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + return true; + } + + // If destination has a function type, it might either be + // a property with a function type or a method reference, + // e.g. `foo.bar = 42` neither can be used if the destination + // is not l-value. + if (!getType(destExpr)->is() && rhs->is()) { + conversionsOrFixes.push_back( + TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + return true; + } + + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, + conversionsOrFixes, locator)) + return true; + + // Let's try to match source and destination types one more + // time to see whether they line up, if they do - the problem is + // related to immutability, otherwise it's a type mismatch. + auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion, + TMF_ApplyingFix, locator); + + if (getType(destExpr)->is() || result.isFailure()) { + conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( + *this, lhs, rhs, getConstraintLocator(locator))); + } else { + conversionsOrFixes.push_back( + TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + } + + return true; } return false; @@ -2576,7 +3023,7 @@ bool ConstraintSystem::repairFailures( auto elt = path.back(); switch (elt.getKind()) { case ConstraintLocator::LValueConversion: { - auto CTP = getContextualTypePurpose(); + auto CTP = getContextualTypePurpose(anchor); // Special case for `CTP_CallArgument` set by CSDiag // while type-checking each argument because we yet // to cover argument-to-parameter conversions in the @@ -2600,6 +3047,10 @@ bool ConstraintSystem::repairFailures( case ConstraintLocator::ApplyArgToParam: { auto loc = getConstraintLocator(locator); + + if (hasFixFor(loc, FixKind::AllowInvalidUseOfTrailingClosure)) + return true; + if (repairByInsertingExplicitCall(lhs, rhs)) break; @@ -2678,8 +3129,7 @@ bool ConstraintSystem::repairFailures( if (auto *fix = fixPropertyWrapperFailure( *this, lhs, loc, - [&](ResolvedOverloadSetListItem *overload, VarDecl *decl, - Type newBase) { + [&](SelectedOverload overload, VarDecl *decl, Type newBase) { // FIXME: There is currently no easy way to avoid attempting // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag. llvm::SaveAndRestore options( @@ -2744,6 +3194,12 @@ bool ConstraintSystem::repairFailures( // for this call, we can consider overload unrelated. if (llvm::any_of(getFixes(), [&](const ConstraintFix *fix) { auto *locator = fix->getLocator(); + // Since arguments to @dynamicCallable form either an array + // or a dictionary and all have to match the same element type, + // let's allow multiple invalid arguments. + if (locator->findFirst()) + return false; + return locator->findLast() ? locator->getAnchor() == anchor : false; @@ -2820,6 +3276,38 @@ bool ConstraintSystem::repairFailures( locator); } + // If either type has a hole, consider this fixed. + if (lhs->hasHole() || rhs->hasHole()) + return true; + + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + break; + + { + auto *calleeLocator = getCalleeLocator(loc); + if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) { + if (auto overload = findSelectedOverloadFor(calleeLocator)) { + if (auto choice = overload->choice.getDeclOrNull()) { + // If this is an argument of a symetric function/operator let's + // not fix any position rather than first because we'd just end + // up with ambiguity instead of reporting an actual problem with + // mismatched type since each argument can have district bindings. + if (auto *AFD = dyn_cast(choice)) { + auto *paramList = AFD->getParameters(); + auto firstParamType = paramList->get(0)->getInterfaceType(); + if (elt.castTo().getParamIdx() > + 0 && + llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool { + return param->getInterfaceType()->isEqual(firstParamType); + })) + return true; + } + } + } + } + } + conversionsOrFixes.push_back( AllowArgumentMismatch::create(*this, lhs, rhs, loc)); break; @@ -2882,10 +3370,17 @@ bool ConstraintSystem::repairFailures( case ConstraintLocator::TypeParameterRequirement: case ConstraintLocator::ConditionalRequirement: { - // If dependent members are present here it's because - // base doesn't conform to associated type's protocol. - if (lhs->hasDependentMember() || rhs->hasDependentMember()) - break; + // If either type has a hole, consider this fixed. + if (lhs->hasHole() || rhs->hasHole()) + return true; + + // If dependent members are present here it's because the base doesn't + // conform to the associated type's protocol. We can only get here if we + // already applied a fix for the conformance failure. + if (lhs->hasDependentMember() || rhs->hasDependentMember()) { + increaseScore(SK_Fix); + return true; + } // If requirement is something like `T == [Int]` let's let // type matcher a chance to match generic parameters before @@ -2908,6 +3403,10 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::ClosureResult: { + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + return true; + // If we could record a generic arguments mismatch instead of this fix, // don't record a ContextualMismatch here. if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality)) @@ -2920,7 +3419,11 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::ContextualType: { - auto purpose = getContextualTypePurpose(); + // If either type is a hole, consider this fixed + if (lhs->isHole() || rhs->isHole()) + return true; + + auto purpose = getContextualTypePurpose(anchor); if (rhs->isVoid() && (purpose == CTP_ReturnStmt || purpose == CTP_ReturnSingleExpr)) { conversionsOrFixes.push_back( @@ -2976,6 +3479,10 @@ bool ConstraintSystem::repairFailures( if (lhs->is() || rhs->is()) break; + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + break; + // If there is a deep equality, superclass restriction // already recorded, let's not add bother ignoring // contextual type, because actual fix is going to @@ -2995,8 +3502,38 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::FunctionResult: { + auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); + // If this is a mismatch between contextual type and (trailing) + // closure with explicitly specified result type let's record it + // as contextual type mismatch. + if (loc->isLastElement() || + loc->isLastElement()) { + auto *argExpr = simplifyLocatorToAnchor(loc); + if (argExpr && isa(argExpr)) { + auto *locator = + getConstraintLocator(argExpr, ConstraintLocator::ClosureResult); + + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, + conversionsOrFixes, locator)) + break; + + conversionsOrFixes.push_back( + IgnoreContextualType::create(*this, lhs, rhs, locator)); + break; + } + } + // Handle function result coerce expression wrong type conversion. + if (anchor && isa(anchor)) { + auto *fix = + ContextualMismatch::create(*this, lhs, rhs, loc); + conversionsOrFixes.push_back(fix); + break; + } + LLVM_FALLTHROUGH; + } + case ConstraintLocator::Member: - case ConstraintLocator::FunctionResult: case ConstraintLocator::DynamicLookupResult: { // Most likely this is an attempt to use get-only subscript as mutating, // or assign a value of a result of function/member ref e.g. `foo() = 42` @@ -3064,11 +3601,25 @@ bool ConstraintSystem::repairFailures( conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); } - if (lhs->is() && rhs->is()) { - auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, - getConstraintLocator(locator)); - conversionsOrFixes.push_back(fix); + + // Drop the `tuple element` locator element so that all tuple element + // mismatches within the same tuple type can be coalesced later. + auto index = elt.getAs()->getIndex(); + path.pop_back(); + auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path); + + // Let this fail if it's a contextual mismatch with sequence element types, + // as there's a special fix for that. + if (tupleLocator->isLastElement()) + break; + + ConstraintFix *fix; + if (tupleLocator->isLastElement()) { + fix = AllowFunctionTypeMismatch::create(*this, lhs, rhs, tupleLocator, index); + } else { + fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index); } + conversionsOrFixes.push_back(fix); break; } @@ -3078,15 +3629,109 @@ bool ConstraintSystem::repairFailures( if (rhs->isExistentialType()) break; - conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( - *this, lhs, rhs, getConstraintLocator(locator))); + conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + + case ConstraintLocator::SubscriptMember: { + if (repairByTreatingRValueAsLValue(lhs, rhs)) + break; + + break; + } + + case ConstraintLocator::Condition: { + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + break; + + conversionsOrFixes.push_back(IgnoreContextualType::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + + // Unresolved member type mismatches are handled when + // r-value adjustment constraint fails. + case ConstraintLocator::UnresolvedMember: + return true; + + case ConstraintLocator::RValueAdjustment: { + if (!(anchor && isa(anchor))) + break; + + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + break; + + // r-value adjustment is used to connect base type of + // unresolved member to its output type, if there is + // a type mismatch here it's contextual e.g. + // `let x: E = .foo(42)`, where `.foo` is a member of `E` + // but produces an incorrect type. + auto *fix = IgnoreContextualType::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + + case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: { + // If this is an attempt to use readonly IUO as a destination + // of an assignment e.g. + // + // let x: Int! = 0 + // x = 42 <- `x` can be either `Int?` or `Int` but it can't be an l-value. + if (lhs->is() && !rhs->is()) { + auto result = matchTypes(lhs->getWithoutSpecifierType(), rhs, matchKind, + TMF_ApplyingFix, locator); + + if (result.isSuccess()) { + conversionsOrFixes.push_back( + TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + } + } + break; + } + + case ConstraintLocator::InstanceType: { + if (lhs->hasHole() || rhs->hasHole()) + return true; + + break; + } + + case ConstraintLocator::OptionalPayload: { + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + return true; + break; } - case ConstraintLocator::SubscriptMember: { - if (repairByTreatingRValueAsLValue(lhs, rhs)) - break; + case ConstraintLocator::TernaryBranch: { + markAnyTypeVarsAsPotentialHoles(lhs); + markAnyTypeVarsAsPotentialHoles(rhs); + + // If `if` expression has a contextual type, let's consider it a source of + // truth and produce a contextual mismatch instead of per-branch failure, + // because it's a better pointer than potential then-to-else type mismatch. + if (auto contextualType = getContextualType(anchor)) { + if (contextualType->isEqual(rhs)) { + auto *loc = + getConstraintLocator(anchor, LocatorPathElt::ContextualType()); + if (hasFixFor(loc, FixKind::ContextualMismatch)) + return true; + + conversionsOrFixes.push_back( + ContextualMismatch::create(*this, lhs, rhs, loc)); + break; + } + } + // If there is no contextual type, this is most likely a contextual type + // mismatch between then/else branches of ternary operator. + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); break; } @@ -3261,6 +3906,25 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, formUnsolvedResult); } } + + // If the left-hand side of a 'sequence element' constraint + // is a dependent member type without any type variables it + // means that conformance check has been "fixed". + // Let's record other side of the conversion as a "hole" + // to give the solver a chance to continue and avoid + // producing diagnostics for both missing conformance and + // invalid element type. + if (shouldAttemptFixes()) { + if (auto last = locator.last()) { + if (last->is() && + desugar1->is() && + !desugar1->hasTypeVariable()) { + recordPotentialHole(typeVar2); + return getTypeMatchSuccess(); + } + } + } + return formUnsolvedResult(); } @@ -3283,9 +3947,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::SelfObjectOfProtocol: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a relational constraint"); } } @@ -3329,12 +3995,14 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, llvm_unreachable("type variables should have already been handled by now"); case TypeKind::DependentMember: { - // If one of the dependent member types has no type variables, - // this comparison is effectively illformed, because dependent - // member couldn't be simplified down to the actual type, and - // we wouldn't be able to solve this constraint, so let's just fail. - if (!desugar1->hasTypeVariable() || !desugar2->hasTypeVariable()) - return getTypeMatchFailure(locator); + // If one of the dependent member types has no type variables, the + // dependent member can't be simplified because the base doesn't conform + // to the associated type's protocol. We can only get here if we already + // applied a fix for the conformance failure. + if (!desugar1->hasTypeVariable() || !desugar2->hasTypeVariable()) { + increaseScore(SK_Fix); + return getTypeMatchSuccess(); + } // Nothing we can solve yet, since we need to wait until // type variables will get resolved. @@ -3347,8 +4015,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, if (shouldAttemptFixes()) { auto last = locator.last(); // If this happens as part of the argument-to-parameter - // conversion, there is a tailored fix/diagnostic. - if (last && last->is()) + // conversion or type requirement, there is a tailored + // fix/diagnostic. + if (last && (last->is() || + last->is())) break; } // If two module types or archetypes were not already equal, there's @@ -3430,15 +4100,28 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, subKind = ConstraintKind::Bind; } - return matchTypes( - instanceType1, instanceType2, subKind, subflags, - locator.withPathElement(ConstraintLocator::InstanceType)); + auto result = + matchTypes(instanceType1, instanceType2, subKind, subflags, + locator.withPathElement(ConstraintLocator::InstanceType)); + + // If matching of the instance types resulted in the failure make sure + // to give `repairFailure` a chance to run to attempt to fix the issue. + if (shouldAttemptFixes() && result.isFailure()) + break; + + return result; } case TypeKind::Function: { auto func1 = cast(desugar1); auto func2 = cast(desugar2); - return matchFunctionTypes(func1, func2, kind, flags, locator); + + auto result = matchFunctionTypes(func1, func2, kind, flags, locator); + + if (shouldAttemptFixes() && result.isFailure()) + break; + + return result; } case TypeKind::GenericFunction: @@ -3619,8 +4302,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // // Class and protocol metatypes are interoperable with certain Objective-C // runtime classes, but only when ObjC interop is enabled. - - if (TC.getLangOpts().EnableObjCInterop) { + + if (getASTContext().LangOpts.EnableObjCInterop) { // These conversions are between concrete types that don't need further // resolution, so we can consider them immediately solved. auto addSolvedRestrictedConstraint @@ -3667,7 +4350,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } } } - + // Special implicit nominal conversions. if (!type1->is() && kind >= ConstraintKind::Subtype) { // Array -> Array. @@ -3717,18 +4400,22 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, if (!isAutoClosureArgument) { auto inoutBaseType = inoutType1->getInOutObjectType(); - Type simplifiedInoutBaseType = getFixedTypeRecursive( - inoutBaseType, /*wantRValue=*/true); + auto baseIsArray = isArrayType( + getFixedTypeRecursive(inoutBaseType, /*wantRValue=*/true)); // FIXME: If the base is still a type variable, we can't tell // what to do here. Might have to try \c ArrayToPointer and make // it more robust. - if (isArrayType(simplifiedInoutBaseType)) { + if (baseIsArray) conversionsOrFixes.push_back( ConversionRestrictionKind::ArrayToPointer); - } - conversionsOrFixes.push_back( - ConversionRestrictionKind::InoutToPointer); + + // Only try an inout-to-pointer conversion if we know it's not + // an array being converted to a raw pointer type. Such + // conversions can only use array-to-pointer. + if (!baseIsArray || !isRawPointerKind(pointerKind)) + conversionsOrFixes.push_back( + ConversionRestrictionKind::InoutToPointer); } } @@ -3782,11 +4469,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // The pointer can be converted from a string, if the element // type is compatible. - if (type1->isEqual(TC.getStringType(DC))) { + auto &ctx = getASTContext(); + if (type1->isEqual(TypeChecker::getStringType(ctx))) { auto baseTy = getFixedTypeRecursive(pointeeTy, false); if (baseTy->isTypeVariableOrMember() || - isStringCompatiblePointerBaseType(TC, DC, baseTy)) + isStringCompatiblePointerBaseType(ctx, baseTy)) conversionsOrFixes.push_back( ConversionRestrictionKind::StringToPointer); } @@ -3865,54 +4553,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Attempt fixes iff it's allowed, both types are concrete and // we are not in the middle of attempting one already. - bool attemptFixes = - shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix); - - // When we hit this point, we're committed to the set of potential - // conversions recorded thus far. - // - // If we should attempt fixes, add those to the list. They'll only be visited - // if there are no other possible solutions. - if (attemptFixes && kind >= ConstraintKind::Conversion) { - Type objectType1 = type1->getRValueType(); - - // If we have an optional type, try to force-unwrap it. - // FIXME: Should we also try '?'? - if (objectType1->getOptionalObjectType()) { - bool forceUnwrapPossible = true; - if (auto declRefExpr = - dyn_cast_or_null(locator.trySimplifyToExpr())) { - if (declRefExpr->getDecl()->isImplicit()) { - // The expression that provides the first type is implicit and never - // spelled out in source code, e.g. $match in an expression pattern. - // Thus we cannot force unwrap the first type - forceUnwrapPossible = false; - } - } - - if (auto optTryExpr = - dyn_cast_or_null(locator.trySimplifyToExpr())) { - auto subExprType = getType(optTryExpr->getSubExpr()); - bool isSwift5OrGreater = TC.getLangOpts().isSwiftVersionAtLeast(5); - if (isSwift5OrGreater && (bool)subExprType->getOptionalObjectType()) { - // For 'try?' expressions, a ForceOptional fix converts 'try?' - // to 'try!'. If the sub-expression is optional, then a force-unwrap - // won't change anything in Swift 5+ because 'try?' already avoids - // adding an additional layer of Optional there. - forceUnwrapPossible = false; - } - } - - if (forceUnwrapPossible) { - conversionsOrFixes.push_back(ForceOptional::create( - *this, objectType1, objectType1->getOptionalObjectType(), - getConstraintLocator(locator))); - } - } - } - - // Attempt to repair any failures identifiable at this point. - if (attemptFixes) { + if (shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix)) { if (repairFailures(type1, type2, kind, conversionsOrFixes, locator)) { if (conversionsOrFixes.empty()) return getTypeMatchSuccess(); @@ -4029,18 +4670,32 @@ ConstraintSystem::simplifyConstructionConstraint( return SolutionKind::Unsolved; case TypeKind::Tuple: { + // If this is an attempt to construct `Void` with arguments, + // let's diagnose it. + if (shouldAttemptFixes()) { + if (valueType->isVoid() && fnType->getNumParams() > 0) { + auto contextualType = FunctionType::get({}, fnType->getResult()); + if (fixExtraneousArguments( + *this, contextualType, fnType->getParams(), + fnType->getNumParams(), + getConstraintLocator(locator, + ConstraintLocator::FunctionArgument))) + return SolutionKind::Error; + + fnType = contextualType; + } + } + // Tuple construction is simply tuple conversion. Type argType = AnyFunctionType::composeInput(getASTContext(), fnType->getParams(), /*canonicalVararg=*/false); Type resultType = fnType->getResult(); - if (matchTypes(resultType, desugarValueType, - ConstraintKind::Bind, - flags, - ConstraintLocatorBuilder(locator) - .withPathElement(ConstraintLocator::ApplyFunction)) - .isFailure()) + ConstraintLocatorBuilder builder(locator); + if (matchTypes(resultType, desugarValueType, ConstraintKind::Bind, flags, + builder.withPathElement(ConstraintLocator::ApplyFunction)) + .isFailure()) return SolutionKind::Error; return matchTypes(argType, valueType, ConstraintKind::Conversion, @@ -4092,8 +4747,8 @@ ConstraintSystem::simplifyConstructionConstraint( // The constructor will have function type T -> T2, for a fresh type // variable T. T2 is the result type provided via the construction // constraint itself. - addValueMemberConstraint(MetatypeType::get(valueType, TC.Context), - DeclBaseName::createConstructor(), + addValueMemberConstraint(MetatypeType::get(valueType, getASTContext()), + DeclNameRef::createConstructor(), memberType, useDC, functionRefKind, /*outerAlternatives=*/{}, @@ -4143,25 +4798,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( ConstraintLocatorBuilder locator, TypeMatchOptions flags) { auto *typeVar = type->getAs(); - if (shouldAttemptFixes()) { - // If type variable, associated with this conformance check, - // has been determined to be a "hole" in constraint system, - // let's consider this check a success without recording - // a fix, because it's just a consequence of other failure - // e.g. + + // Dig out the fixed type to which this type refers. + type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true); + if (shouldAttemptFixes() && type->isHole()) { + // If the type associated with this conformance check is a "hole" in the + // constraint system, let's consider this check a success without recording + // a fix, because it's just a consequence of the other failure, e.g. // // func foo(_: T) {} // foo(Foo.bar) <- if `Foo` doesn't have `bar` there is // no reason to complain about missing conformance. - if (typeVar && isHole(typeVar)) { - increaseScore(SK_Fix); - return SolutionKind::Solved; - } + increaseScore(SK_Fix); + return SolutionKind::Solved; } - // Dig out the fixed type to which this type refers. - type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true); - // If we hit a type variable without a fixed type, we can't // solve this yet. if (type->isTypeVariableOrMember()) { @@ -4201,26 +4852,26 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( // conform -- they only need to contain the protocol, so check that // separately. switch (kind) { - case ConstraintKind::SelfObjectOfProtocol: - if (auto conformance = - TC.containsProtocol(type, protocol, DC, - (ConformanceCheckFlags::InExpression| - ConformanceCheckFlags::SkipConditionalRequirements))) { - return recordConformance(*conformance); - } - break; + case ConstraintKind::SelfObjectOfProtocol: { + auto conformance = TypeChecker::containsProtocol( + type, protocol, DC, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (conformance) { + return recordConformance(conformance); + } + } break; case ConstraintKind::ConformsTo: case ConstraintKind::LiteralConformsTo: { // Check whether this type conforms to the protocol. - if (auto conformance = - TypeChecker::conformsToProtocol( - type, protocol, DC, - (ConformanceCheckFlags::InExpression| - ConformanceCheckFlags::SkipConditionalRequirements))) { - return recordConformance(*conformance); + auto conformance = TypeChecker::conformsToProtocol( + type, protocol, DC, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (conformance) { + return recordConformance(conformance); } - break; - } + } break; default: llvm_unreachable("bad constraint kind"); @@ -4229,25 +4880,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( if (!shouldAttemptFixes()) return SolutionKind::Error; - // See if there's anything we can do to fix the conformance: - if (auto optionalObjectType = type->getOptionalObjectType()) { - TypeMatchOptions subflags = getDefaultDecompositionOptions(flags); - // The underlying type of an optional may conform to the protocol if the - // optional doesn't; suggest forcing if that's the case. - auto result = simplifyConformsToConstraint( - optionalObjectType, protocol, kind, - locator.withPathElement(LocatorPathElt::GenericArgument(0)), subflags); - if (result == SolutionKind::Solved) { - auto *fix = ForceOptional::create(*this, type, optionalObjectType, - getConstraintLocator(locator)); - if (recordFix(fix)) { - return SolutionKind::Error; - } - } - return result; - } - auto protocolTy = protocol->getDeclaredType(); + // If this conformance has been fixed already, let's just consider this done. if (hasFixedRequirement(type, RequirementKind::Conformance, protocolTy)) return SolutionKind::Solved; @@ -4275,6 +4909,33 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } + // If there is a missing conformance between source and destination + // of the assignment, let's ignore current the types and instead use + // source/destination types directly to make it possible to diagnose + // protocol compositions. + if (anchor && isa(anchor)) { + auto *assignment = cast(anchor); + + // If the locator's last element points to the function result, + // let's check whether there is a problem with function argument + // as well, and if so, avoid producing a fix here, because + // contextual mismatch mentions the source/destination + // types of the assignment. + if (auto last = locator.last()) { + if (last->is() && + hasFixFor(getConstraintLocator(anchor, + LocatorPathElt::FunctionArgument()))) + return SolutionKind::Solved; + } + + auto srcType = getType(assignment->getSrc()); + auto dstType = getType(assignment->getDest()); + + auto *fix = IgnoreAssignmentDestinationType::create( + *this, srcType, dstType, getConstraintLocator(locator)); + return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; + } + if (path.empty()) return SolutionKind::Error; @@ -4518,6 +5179,9 @@ ConstraintSystem::simplifyOptionalObjectConstraint( if (!objectTy) { // Let's see if we can apply a specific fix here. if (shouldAttemptFixes()) { + if (optTy->isHole()) + return SolutionKind::Solved; + auto *fix = RemoveUnwrap::create(*this, optTy, getConstraintLocator(locator)); @@ -4560,6 +5224,12 @@ ConstraintSystem::simplifyFunctionComponentConstraint( // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } + + if (simplified->isHole()) { + if (auto *typeVar = second->getAs()) + recordPotentialHole(typeVar); + return SolutionKind::Solved; + } } if (simplified->isTypeVariableOrMember()) { @@ -4593,97 +5263,15 @@ ConstraintSystem::simplifyFunctionComponentConstraint( } if (unwrapCount > 0) { - auto *fix = ForceOptional::create(*this, simplifiedCopy, - simplifiedCopy->getOptionalObjectType(), + auto *fix = ForceOptional::create(*this, simplifiedCopy, second, getConstraintLocator(locator)); - while (unwrapCount-- > 0) { - if (recordFix(fix)) - return SolutionKind::Error; - } + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; } return SolutionKind::Solved; } -/// Return true if the specified type or a super-class/super-protocol has the -/// @dynamicMemberLookup attribute on it. This implementation is not -/// particularly fast in the face of deep class hierarchies or lots of protocol -/// conformances, but this is fine because it doesn't get invoked in the normal -/// name lookup path (only when lookup is about to fail). -bool swift::hasDynamicMemberLookupAttribute(Type type, - llvm::DenseMap &DynamicMemberLookupCache) { - auto canType = type->getCanonicalType(); - auto it = DynamicMemberLookupCache.find(canType); - if (it != DynamicMemberLookupCache.end()) return it->second; - - // Calculate @dynamicMemberLookup attribute for composite types with multiple - // components (protocol composition types and archetypes). - auto calculateForComponentTypes = - [&](ArrayRef componentTypes) -> bool { - for (auto componentType : componentTypes) - if (hasDynamicMemberLookupAttribute(componentType, - DynamicMemberLookupCache)) - return true; - return false; - }; - - auto calculate = [&]() -> bool { - // If this is an archetype type, check if any types it conforms to - // (superclass or protocols) have the attribute. - if (auto archetype = dyn_cast(canType)) { - SmallVector componentTypes; - for (auto protocolDecl : archetype->getConformsTo()) - componentTypes.push_back(protocolDecl->getDeclaredType()); - if (auto superclass = archetype->getSuperclass()) - componentTypes.push_back(superclass); - return calculateForComponentTypes(componentTypes); - } - - // If this is a protocol composition, check if any of its members have the - // attribute. - if (auto protocolComp = dyn_cast(canType)) - return calculateForComponentTypes(protocolComp->getMembers()); - - // Otherwise, this must be a nominal type. - // Dynamic member lookup doesn't work for tuples, etc. - auto nominal = canType->getAnyNominal(); - if (!nominal) return false; - - // If this type conforms to a protocol with the attribute, then return true. - for (auto p : nominal->getAllProtocols()) - if (p->getAttrs().hasAttribute()) - return true; - - // Walk superclasses, if present. - llvm::SmallPtrSet visitedDecls; - while (1) { - // If we found a circular parent class chain, reject this. - if (!visitedDecls.insert(nominal).second) - return false; - - // If this type has the attribute on it, then yes! - if (nominal->getAttrs().hasAttribute()) - return true; - - // If this is a class with a super class, check super classes as well. - if (auto *cd = dyn_cast(nominal)) { - if (auto superClass = cd->getSuperclassDecl()) { - nominal = superClass; - continue; - } - } - - return false; - } - }; - - auto result = calculate(); - // Cache the result if the type does not contain type variables. - if (!type->hasTypeVariable()) - DynamicMemberLookupCache[canType] = result; - return result; -} - static bool isForKeyPathSubscript(ConstraintSystem &cs, ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) @@ -4738,6 +5326,52 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy, }); } +// Check whether given key path dynamic member lookup is self-recursive, +// which happens when root type of the key path is the same as base type +// of the member and lookup is attempted on non-existing property e.g. +// +// @dynamicMemberLookup +// struct Recurse { +// subscript(dynamicMember member: KeyPath, U>) -> Int { +// return 1 +// } +// } +// +// If we going to lookup any no-existent property or member on `Recursive` +// using key path dynamic member lookup it would attempt to lookup such +// member on root type which is also `Recursive` which leads to an infinite +// recursion. +static bool isSelfRecursiveKeyPathDynamicMemberLookup( + ConstraintSystem &cs, Type keyPathRootTy, ConstraintLocator *locator) { + // Let's check whether this is a recursive call to keypath + // dynamic member lookup on the same type. + if (!locator->isLastElement()) + return false; + + auto path = locator->getPath(); + auto *choiceLoc = + cs.getConstraintLocator(locator->getAnchor(), path.drop_back()); + + if (auto overload = cs.findSelectedOverloadFor(choiceLoc)) { + auto baseTy = overload->choice.getBaseType(); + + // If it's `Foo` vs. `Foo` it doesn't really matter + // for dynamic lookup because it's going to be performed on `Foo`. + if (baseTy->is() && + keyPathRootTy->is()) { + auto *baseDecl = baseTy->castTo()->getDecl(); + auto *keyPathRootDecl = + keyPathRootTy->castTo()->getDecl(); + return baseDecl == keyPathRootDecl; + } + + if (baseTy->isEqual(keyPathRootTy)) + return true; + } + + return false; +} + /// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint, /// perform a lookup into the specified base type to find a candidate list. /// The list returned includes the viable candidates as well as the unviable @@ -4747,7 +5381,7 @@ allFromConditionalConformances(DeclContext *DC, Type baseTy, /// try to identify and classify inaccessible members that may be being /// referenced. MemberLookupResult ConstraintSystem:: -performMemberLookup(ConstraintKind constraintKind, DeclName memberName, +performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, Type baseTy, FunctionRefKind functionRefKind, ConstraintLocator *memberLocator, bool includeInaccessibleMembers) { @@ -4782,6 +5416,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // If the base type is a tuple type, look for the named or indexed member // of the tuple. + auto &ctx = getASTContext(); if (auto baseTuple = baseObjTy->getAs()) { // Tuples don't have compound-name members. if (!memberName.isSimpleName() || memberName.isSpecial()) @@ -4820,7 +5455,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // anything else, because the cost of the general search is so // high. if (auto info = getArgumentInfo(memberLocator)) { - memberName = DeclName(TC.Context, memberName.getBaseName(), info->Labels); + memberName.getFullName() = DeclName(ctx, memberName.getBaseName(), + info->Labels); } } @@ -4854,8 +5490,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // If the instance type is String bridged to NSString, compute // the type we'll look in for bridging. Type bridgedType; - if (baseObjTy->getAnyNominal() == TC.Context.getStringDecl()) { - if (Type classType = TC.Context.getBridgedToObjC(DC, instanceTy)) { + if (baseObjTy->getAnyNominal() == ctx.getStringDecl()) { + if (Type classType = ctx.getBridgedToObjC(DC, instanceTy)) { bridgedType = classType; } } @@ -4864,19 +5500,17 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // reasonable choice. auto addChoice = [&](OverloadChoice candidate) { auto decl = candidate.getDecl(); - + + // Reject circular references immediately. + if (decl->isRecursiveValidation()) + return; + // If the result is invalid, skip it. - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)decl->getInterfaceType(); if (decl->isInvalid()) { result.markErrorAlreadyDiagnosed(); return; } - // FIXME: Deal with broken recursion - if (!decl->hasInterfaceType()) - return; - // Dig out the instance type and figure out what members of the instance type // we are going to see. auto baseTy = candidate.getBaseType(); @@ -5098,16 +5732,14 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // as representing "dynamic lookup" unless it's a direct call // to such subscript (in that case label is expected to match). if (auto *subscript = dyn_cast(cand)) { - if (memberLocator && - ::hasDynamicMemberLookupAttribute(instanceTy, - DynamicMemberLookupCache) && - isValidKeyPathDynamicMemberLookup(subscript, TC)) { + if (memberLocator && instanceTy->hasDynamicMemberLookupAttribute() && + isValidKeyPathDynamicMemberLookup(subscript)) { auto info = getArgumentInfo(memberLocator); if (!(info && info->Labels.size() == 1 && info->Labels[0] == getASTContext().Id_dynamicMember)) { return OverloadChoice::getDynamicMemberLookup( - baseTy, subscript, TC.Context.getIdentifier("subscript"), + baseTy, subscript, ctx.getIdentifier("subscript"), /*isKeyPathBased=*/true); } } @@ -5125,11 +5757,11 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // Backward compatibility hack. In Swift 4, `init` and init were // the same name, so you could write "foo.init" to look up a // method or property named `init`. - if (!TC.Context.isSwiftVersionAtLeast(5) && + if (!ctx.isSwiftVersionAtLeast(5) && memberName.getBaseName() == DeclBaseName::createConstructor() && !isImplicitInit) { auto &compatLookup = lookupMember(instanceTy, - TC.Context.getIdentifier("init")); + DeclNameRef(ctx.getIdentifier("init"))); for (auto result : compatLookup) addChoice(getOverloadChoice(result.getValueDecl(), /*isBridged=*/false, @@ -5189,16 +5821,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, // parameter. if (constraintKind == ConstraintKind::ValueMember && memberName.isSimpleName() && !memberName.isSpecial() && - ::hasDynamicMemberLookupAttribute(instanceTy, DynamicMemberLookupCache)) { + instanceTy->hasDynamicMemberLookupAttribute()) { const auto &candidates = result.ViableCandidates; - if (candidates.empty() || - allFromConditionalConformances(DC, instanceTy, candidates)) { + if ((candidates.empty() || + allFromConditionalConformances(DC, instanceTy, candidates)) && + !isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy, + memberLocator)) { auto &ctx = getASTContext(); // Recursively look up `subscript(dynamicMember:)` methods in this type. - auto subscriptName = - DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember); + DeclNameRef subscriptName( + { ctx, DeclBaseName::createSubscript(), { ctx.Id_dynamicMember } }); auto subscripts = performMemberLookup( constraintKind, subscriptName, baseTy, functionRefKind, memberLocator, includeInaccessibleMembers); @@ -5207,9 +5841,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, auto name = memberName.getBaseIdentifier(); for (const auto &candidate : subscripts.ViableCandidates) { auto *SD = cast(candidate.getDecl()); - bool isKeyPathBased = isValidKeyPathDynamicMemberLookup(SD, TC); + bool isKeyPathBased = isValidKeyPathDynamicMemberLookup(SD); - if (isValidStringDynamicMemberLookup(SD, DC, TC) || isKeyPathBased) + if (isValidStringDynamicMemberLookup(SD, DC) || isKeyPathBased) result.addViable(OverloadChoice::getDynamicMemberLookup( baseTy, SD, name, isKeyPathBased)); } @@ -5218,7 +5852,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, auto *SD = cast(subscripts.UnviableCandidates[index].getDecl()); auto choice = OverloadChoice::getDynamicMemberLookup( - baseTy, SD, name, isValidKeyPathDynamicMemberLookup(SD, TC)); + baseTy, SD, name, isValidKeyPathDynamicMemberLookup(SD)); result.addUnviable(choice, subscripts.UnviableReasons[index]); } } @@ -5236,24 +5870,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName, lookupOptions |= NameLookupFlags::IgnoreAccessControl; // This is only used for diagnostics, so always use KnownPrivate. lookupOptions |= NameLookupFlags::KnownPrivate; - - auto lookup = TC.lookupMember(DC, instanceTy, - memberName, lookupOptions); + + auto lookup = + TypeChecker::lookupMember(DC, instanceTy, memberName, lookupOptions); for (auto entry : lookup) { auto *cand = entry.getValueDecl(); // If the result is invalid, skip it. - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)cand->getInterfaceType(); if (cand->isInvalid()) { result.markErrorAlreadyDiagnosed(); return result; } - // FIXME: Deal with broken recursion - if (!cand->hasInterfaceType()) - continue; - result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false, /*isUnwrappedOptional=*/false), MemberLookupResult::UR_Inaccessible); @@ -5418,7 +6046,7 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, static ConstraintFix * fixMemberRef(ConstraintSystem &cs, Type baseTy, - DeclName memberName, const OverloadChoice &choice, + DeclNameRef memberName, const OverloadChoice &choice, ConstraintLocator *locator, Optional reason = None) { // Not all of the choices handled here are going @@ -5439,10 +6067,6 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy, switch (*reason) { case MemberLookupResult::UR_InstanceMemberOnType: case MemberLookupResult::UR_TypeMemberOnInstance: { - if (choice.getKind() == OverloadChoiceKind::DynamicMemberLookup || - choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) - return nullptr; - return choice.isDecl() ? AllowTypeOrInstanceMember::create( cs, baseTy, choice.getDecl(), memberName, locator) @@ -5486,7 +6110,7 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy, } ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( - ConstraintKind kind, Type baseTy, DeclName member, Type memberTy, + ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy, DeclContext *useDC, FunctionRefKind functionRefKind, ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locatorB) { @@ -5499,6 +6123,38 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( Type baseObjTy = baseTy->getRValueType(); auto locator = getConstraintLocator(locatorB); + + // If the base type of this member lookup is a "hole" there is no + // reason to perform a lookup because it wouldn't return any results. + if (shouldAttemptFixes()) { + auto markMemberTypeAsPotentialHole = [&](Type memberTy) { + if (auto *typeVar = memberTy->getAs()) + recordPotentialHole(typeVar); + }; + + // If this is an unresolved member ref e.g. `.foo` and its contextual base + // type has been determined to be a "hole", let's mark the resulting member + // type as a potential hole and continue solving. + if (kind == ConstraintKind::UnresolvedValueMember && + baseObjTy->getMetatypeInstanceType()->isHole()) { + auto *fix = + SpecifyBaseTypeForContextualMember::create(*this, member, locator); + if (recordFix(fix)) + return SolutionKind::Error; + + markMemberTypeAsPotentialHole(memberTy); + return SolutionKind::Solved; + } else if ((kind == ConstraintKind::ValueMember || + kind == ConstraintKind::ValueWitness) && + baseObjTy->isHole()) { + // If base type is a "hole" there is no reason to record any + // more "member not found" fixes for chained member references. + increaseScore(SK_Fix); + markMemberTypeAsPotentialHole(memberTy); + return SolutionKind::Solved; + } + } + MemberLookupResult result = performMemberLookup(kind, member, baseTy, functionRefKind, locator, /*includeInaccessibleMembers*/ shouldAttemptFixes()); @@ -5526,8 +6182,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( return formUnsolved(); case MemberLookupResult::ErrorAlreadyDiagnosed: - return SolutionKind::Error; - case MemberLookupResult::HasResults: // Keep going! break; @@ -5542,7 +6196,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( // subscript dispatch relies on presence of function application. if (result.ViableCandidates.size() == 1) { auto &choice = result.ViableCandidates.front(); - if (!solverState && choice.isKeyPathDynamicMemberLookup() && + if (Phase == ConstraintSystemPhase::ConstraintGeneration && + choice.isKeyPathDynamicMemberLookup() && member.getBaseName().isSubscript()) { // Let's move this constraint to the active // list so it could be picked up right after @@ -5564,8 +6219,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (candidates.size() == 1) candidates.front()->setFavored(); - generateConstraints(candidates, memberTy, outerAlternatives, - useDC, locator); + // We *might* include any non-members that we found in outer contexts in + // some special cases, for backwards compatibility: first, we have to be + // looking for one of the special names ('min' or 'max'), and second, all + // of the inner (viable) results need to come from conditional + // conformances. The second condition is how the problem here was + // encountered: a type ('Range') was made to conditionally conform to a + // new protocol ('Sequence'), which introduced some extra methods + // ('min' and 'max') that shadowed global functions that people regularly + // called within extensions to that type (usually adding 'clamp'). + bool treatAsViable = + (member.isSimpleName("min") || member.isSimpleName("max")) && + allFromConditionalConformances(DC, baseTy, result.ViableCandidates); + + generateConstraints( + candidates, memberTy, outerAlternatives, useDC, locator, None, + /*requiresFix=*/!treatAsViable, + [&](unsigned, const OverloadChoice &) { + return treatAsViable ? nullptr + : AddQualifierToAccessTopLevelName::create( + *this, locator); + }); } } @@ -5591,20 +6265,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (shouldAttemptFixes()) { auto fixMissingMember = [&](Type baseTy, Type memberTy, ConstraintLocator *locator) -> SolutionKind { - // Let's check whether there are any generic parameters - // associated with base type, we'd have to default them - // to `Any` and record as potential holes if so. - baseTy.transform([&](Type type) -> Type { + // Let's check whether there are any generic parameters associated with + // base type, and record potential holes if so. + simplifyType(baseTy).transform([&](Type type) -> Type { if (auto *typeVar = type->getAs()) { if (typeVar->getImpl().hasRepresentativeOrFixed()) return type; - recordHole(typeVar); + recordPotentialHole(typeVar); } return type; }); - auto *fix = - DefineMemberBasedOnUse::create(*this, baseTy, member, locator); + bool alreadyDiagnosed = (result.OverallResult == + MemberLookupResult::ErrorAlreadyDiagnosed); + auto *fix = DefineMemberBasedOnUse::create(*this, baseTy, member, + alreadyDiagnosed, locator); // Impact is higher if the base is expected to be inferred from context, // because a failure to find a member ultimately means that base type is // not a match in this case. @@ -5613,11 +6288,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (recordFix(fix, impact)) return SolutionKind::Error; - // Allow member type to default to `Any` to make it possible to form - // solutions when contextual type of the result cannot be deduced e.g. - // `let _ = x.foo`. + // Record a hole for memberTy to make it possible to form solutions + // when contextual result type cannot be deduced e.g. `let _ = x.foo`. if (auto *memberTypeVar = memberTy->getAs()) - recordHole(memberTypeVar); + recordPotentialHole(memberTypeVar); return SolutionKind::Solved; }; @@ -5630,16 +6304,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (auto dotExpr = dyn_cast_or_null(locator->getAnchor())) { auto baseExpr = dotExpr->getBase(); - auto resolvedOverload = getResolvedOverloadSets(); - while (resolvedOverload) { - if (resolvedOverload->Locator->getAnchor() == baseExpr) { - if (resolvedOverload->Choice - .isImplicitlyUnwrappedValueOrReturnValue()) - return SolutionKind::Error; - break; - } - resolvedOverload = resolvedOverload->Previous; - } + if (auto overload = findSelectedOverloadFor(baseExpr)) + if (overload->choice.isImplicitlyUnwrappedValueOrReturnValue()) + return SolutionKind::Error; } // Let's check whether the problem is related to optionality of base @@ -5660,7 +6327,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( auto innerTV = createTypeVariable(locator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); - Type optTy = getTypeChecker().getOptionalType(SourceLoc(), innerTV); + Type optTy = TypeChecker::getOptionalType(SourceLoc(), innerTV); SmallVector optionalities; auto nonoptionalResult = Constraint::createFixed( *this, ConstraintKind::Bind, @@ -5682,7 +6349,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( } auto solveWithNewBaseOrName = [&](Type baseType, - DeclName memberName) -> SolutionKind { + DeclNameRef memberName) -> SolutionKind { return simplifyMemberConstraint(kind, baseType, memberName, memberTy, useDC, functionRefKind, outerAlternatives, flags | TMF_ApplyingFix, locatorB); @@ -5699,8 +6366,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( // properties that have matching members. if (auto *fix = fixPropertyWrapperFailure( *this, baseTy, locator, - [&](ResolvedOverloadSetListItem *overload, VarDecl *decl, - Type newBase) { + [&](SelectedOverload overload, VarDecl *decl, Type newBase) { return solveWithNewBaseOrName(newBase, member) == SolutionKind::Solved; })) { @@ -5726,7 +6392,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( // Instead of using subscript operator spelled out `subscript` directly. if (member.getBaseName() == getTokenText(tok::kw_subscript)) { auto result = - solveWithNewBaseOrName(baseTy, DeclBaseName::createSubscript()); + solveWithNewBaseOrName(baseTy, DeclNameRef::createSubscript()); // Looks like it was indeed meant to be a subscript operator. if (result == SolutionKind::Solved) return recordFix(UseSubscriptOperator::create(*this, locator)) @@ -5748,14 +6414,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( } } - // FIXME(diagnostics): Errors related to `AnyObject` could be diagnosed - // better in the future, relevant failure information has to be extracted - // from `performMemberLookup` result, in order to figure out if it was a - // simple labeling or # of arguments mismatch, or member with requested name - // really doesn't exist. - if (baseTy->isAnyObject()) - return SolutionKind::Error; - result = performMemberLookup(kind, member, baseTy, functionRefKind, locator, /*includeInaccessibleMembers*/ true); @@ -5764,40 +6422,113 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (!result.UnviableCandidates.empty()) return SolutionKind::Error; - // Since member with given base and name doesn't exist, let's try to - // fake its presence based on use, that makes it possible to diagnose - // problems related to member lookup more precisely. + // Since member with given base and name doesn't exist, let's try to + // fake its presence based on use, that makes it possible to diagnose + // problems related to member lookup more precisely. + + return fixMissingMember(origBaseTy, memberTy, locator); + } + return SolutionKind::Error; +} + +ConstraintSystem::SolutionKind +ConstraintSystem::simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *requirement, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + // We'd need to record original base type because it might be a type + // variable representing another missing member. + auto origBaseType = baseType; + + auto formUnsolved = [&] { + // If requested, generate a constraint. + if (flags.contains(TMF_GenerateConstraints)) { + auto *witnessConstraint = Constraint::createValueWitness( + *this, kind, origBaseType, memberType, requirement, useDC, + functionRefKind, getConstraintLocator(locator)); + + addUnsolvedConstraint(witnessConstraint); + return SolutionKind::Solved; + } + + return SolutionKind::Unsolved; + }; + + // Resolve the base type, if we can. If we can't resolve the base type, + // then we can't solve this constraint. + Type baseObjectType = getFixedTypeRecursive( + baseType, flags, /*wantRValue=*/true); + if (baseObjectType->isTypeVariableOrMember()) { + return formUnsolved(); + } + + // Check conformance to the protocol. If it doesn't conform, this constraint + // fails. Don't attempt to fix it. + // FIXME: Look in the constraint system to see if we've resolved the + // conformance already? + auto proto = requirement->getDeclContext()->getSelfProtocolDecl(); + assert(proto && "Value witness constraint for a non-requirement"); + auto conformance = TypeChecker::conformsToProtocol( + baseObjectType, proto, useDC, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (!conformance) { + // The conformance failed, so mark the member type as a "hole". We cannot + // do anything further here. + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + memberType.visit([&](Type type) { + if (auto *typeVar = type->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + + // Reference the requirement. + Type resolvedBaseType = simplifyType(baseType, flags); + if (resolvedBaseType->isTypeVariableOrMember()) + return formUnsolved(); + + auto choice = OverloadChoice(resolvedBaseType, requirement, functionRefKind); + resolveOverload(getConstraintLocator(locator), memberType, choice, + useDC); + return SolutionKind::Solved; +} + +ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( + Type first, Type second, TypeMatchOptions flags, + ConstraintLocatorBuilder locator) { + first = getFixedTypeRecursive(first, flags, true); - // If base type is a "hole" there is no reason to record any - // more "member not found" fixes for chained member references. - if (auto *baseType = origBaseTy->getMetatypeInstanceType() - ->getRValueType() - ->getAs()) { - if (isHole(baseType)) { - increaseScore(SK_Fix); - if (auto *memberTypeVar = memberTy->getAs()) - recordHole(memberTypeVar); - return SolutionKind::Solved; - } + if (first->isTypeVariableOrMember()) { + if (flags.contains(TMF_GenerateConstraints)) { + addUnsolvedConstraint( + Constraint::create(*this, ConstraintKind::Defaultable, first, second, + getConstraintLocator(locator))); + return SolutionKind::Solved; } - return fixMissingMember(origBaseTy, memberTy, locator); + return SolutionKind::Unsolved; } - return SolutionKind::Error; + + // Otherwise, any type is fine. + return SolutionKind::Solved; } ConstraintSystem::SolutionKind -ConstraintSystem::simplifyDefaultableConstraint( - Type first, Type second, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator) { - first = getFixedTypeRecursive(first, flags, true); +ConstraintSystem::simplifyDefaultClosureTypeConstraint( + Type closureType, Type inferredType, + ArrayRef referencedOuterParameters, + TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + closureType = getFixedTypeRecursive(closureType, flags, /*wantRValue=*/true); - if (first->isTypeVariableOrMember()) { + if (closureType->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { - addUnsolvedConstraint( - Constraint::create(*this, ConstraintKind::Defaultable, first, second, - getConstraintLocator(locator))); + addUnsolvedConstraint(Constraint::create( + *this, ConstraintKind::DefaultClosureType, closureType, inferredType, + getConstraintLocator(locator), referencedOuterParameters)); return SolutionKind::Solved; } @@ -5827,11 +6558,109 @@ ConstraintSystem::simplifyOneWayConstraint( return SolutionKind::Unsolved; } + // Propagate holes through one-way constraints. + if (secondSimplified->isHole()) { + first.visit([&](Type subType) { + if (auto *typeVar = subType->getAs()) + recordPotentialHole(typeVar); + }); + + return SolutionKind::Solved; + } + // Translate this constraint into a one-way binding constraint. return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, locator); } +static Type getFunctionBuilderTypeFor(ConstraintSystem &cs, unsigned paramIdx, + ConstraintLocator *calleeLocator) { + auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); + if (!(selectedOverload && + selectedOverload->choice.getKind() == OverloadChoiceKind::Decl)) + return Type(); + + auto *choice = selectedOverload->choice.getDecl(); + bool skipCurriedSelf = hasAppliedSelf(cs, selectedOverload->choice); + + if (choice->hasCurriedSelf() && !skipCurriedSelf) + return Type(); + + if (!choice->hasParameterList()) + return Type(); + + auto *PD = getParameterAt(choice, paramIdx); + return PD->getFunctionBuilderType(); +} + +bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, + Type contextualType, + ConstraintLocatorBuilder locator) { + auto *closureLocator = typeVar->getImpl().getLocator(); + auto *closure = cast(closureLocator->getAnchor()); + + auto *closureType = getClosureType(closure); + + auto *paramList = closure->getParameters(); + for (unsigned i = 0, n = paramList->size(); i != n; ++i) { + const auto ¶m = closureType->getParams()[i]; + + Type internalType; + + if (paramList->get(i)->getTypeRepr()) { + // Internal type is the type used in the body of the closure, + // so "external" type translates to it as follows: + // - `Int...` -> `[Int]`, + // - `inout Int` -> `@lvalue Int`. + internalType = param.getParameterType(); + } else { + auto *paramLoc = + getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); + + internalType = createTypeVariable(paramLoc, TVO_CanBindToLValue | + TVO_CanBindToNoEscape); + + auto externalType = param.getOldType(); + addConstraint(ConstraintKind::BindParam, externalType, internalType, + paramLoc); + } + + setType(paramList->get(i), internalType); + } + + assignFixedType(typeVar, closureType, closureLocator); + + if (auto last = locator.last()) { + if (auto argToParam = last->getAs()) { + auto *calleeLocator = getCalleeLocator(getConstraintLocator(locator)); + if (auto functionBuilderType = getFunctionBuilderTypeFor( + *this, argToParam->getParamIdx(), calleeLocator)) { + auto result = matchFunctionBuilder( + closure, functionBuilderType, closureType->getResult(), + ConstraintKind::Conversion, calleeLocator, locator); + return result.isSuccess(); + } + } + } + + // If this is a multi-statement closure its body doesn't participate + // in type-checking. + if (closure->hasSingleExpressionBody()) { + auto *closureBody = generateConstraints(closure); + if (!closureBody) + return false; + + // Since result of the closure type has to be r-value type, we have + // to use equality here. + addConstraint( + ConstraintKind::Conversion, getType(closureBody), + closureType->getResult(), + getConstraintLocator(closure, ConstraintLocator::ClosureResult)); + } + + return true; +} + ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicTypeOfConstraint( Type type1, Type type2, @@ -6006,12 +6835,13 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1, } // Explicit bridging from a value type to an Objective-C class type. + auto &ctx = getASTContext(); if (unwrappedFromType->isPotentiallyBridgedValueType() && (unwrappedToType->isBridgeableObjectType() || (unwrappedToType->isExistentialType() && !unwrappedToType->isAny()))) { countOptionalInjections(); - if (Type classType = TC.Context.getBridgedToObjC(DC, unwrappedFromType)) { + if (Type classType = ctx.getBridgedToObjC(DC, unwrappedFromType)) { return matchTypes(classType, unwrappedToType, ConstraintKind::Conversion, subflags, locator); } @@ -6023,51 +6853,52 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1, if (unwrappedFromType->mayHaveSuperclass() && unwrappedToType->isPotentiallyBridgedValueType()) { Type bridgedValueType; - if (auto objcClass = TC.Context.getBridgedToObjC(DC, unwrappedToType, - &bridgedValueType)) { + if (auto objcClass = ctx.getBridgedToObjC(DC, unwrappedToType, + &bridgedValueType)) { // Bridging NSNumber to NSValue is one-way, since there are multiple Swift // value types that bridge to those object types. It requires a checked // cast to get back. - if (TC.Context.isObjCClassWithMultipleSwiftBridgedTypes(objcClass)) + if (ctx.isObjCClassWithMultipleSwiftBridgedTypes(objcClass)) return SolutionKind::Error; // If the bridged value type is generic, the generic arguments // must either match or be bridged. // FIXME: This should be an associated type of the protocol. + auto &ctx = getASTContext(); if (auto fromBGT = unwrappedToType->getAs()) { - if (fromBGT->getDecl() == TC.Context.getArrayDecl()) { + if (fromBGT->getDecl() == ctx.getArrayDecl()) { // [AnyObject] addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], - TC.Context.getAnyObjectType(), + ctx.getAnyObjectType(), getConstraintLocator(locator.withPathElement( LocatorPathElt::GenericArgument(0)))); - } else if (fromBGT->getDecl() == TC.Context.getDictionaryDecl()) { + } else if (fromBGT->getDecl() == ctx.getDictionaryDecl()) { // [NSObject : AnyObject] - auto NSObjectType = TC.getNSObjectType(DC); - if (!NSObjectType) { + auto nsObjectType = ctx.getNSObjectType(); + if (!nsObjectType) { // Not a bridging case. Should we detect this earlier? return SolutionKind::Error; } addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], - NSObjectType, + nsObjectType, getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(0)))); addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[1], - TC.Context.getAnyObjectType(), + ctx.getAnyObjectType(), getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(1)))); - } else if (fromBGT->getDecl() == TC.Context.getSetDecl()) { - auto NSObjectType = TC.getNSObjectType(DC); - if (!NSObjectType) { + } else if (fromBGT->getDecl() == ctx.getSetDecl()) { + auto nsObjectType = ctx.getNSObjectType(); + if (!nsObjectType) { // Not a bridging case. Should we detect this earlier? return SolutionKind::Error; } addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0], - NSObjectType, + nsObjectType, getConstraintLocator( locator.withPathElement( LocatorPathElt::GenericArgument(0)))); @@ -6193,7 +7024,7 @@ ConstraintSystem::simplifyOpenedExistentialOfConstraint( assert(instanceTy->isExistentialType()); Type openedTy = OpenedArchetypeType::get(instanceTy); if (isMetatype) - openedTy = MetatypeType::get(openedTy, TC.Context); + openedTy = MetatypeType::get(openedTy, getASTContext()); return matchTypes(type1, openedTy, ConstraintKind::Bind, subflags, locator); } if (!type2->isTypeVariableOrMember()) @@ -6226,22 +7057,6 @@ ConstraintSystem::simplifyKeyPathConstraint( auto subflags = getDefaultDecompositionOptions(flags); // The constraint ought to have been anchored on a KeyPathExpr. auto keyPath = cast(locator.getBaseLocator()->getAnchor()); - - // Gather overload choices for any key path components associated with this - // key path. - SmallVector choices; - choices.resize(keyPath->getComponents().size()); - for (auto resolvedItem = resolvedOverloadSets; resolvedItem; - resolvedItem = resolvedItem->Previous) { - auto locator = resolvedItem->Locator; - auto path = locator->getPath(); - if (locator->getAnchor() != keyPath || path.size() > 2) - continue; - - if (auto kpElt = path[0].getAs()) { - choices[kpElt->getIndex()] = resolvedItem->Choice; - } - } keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true); bool definitelyFunctionType = false; @@ -6325,38 +7140,65 @@ ConstraintSystem::simplifyKeyPathConstraint( case KeyPathExpr::Component::Kind::Subscript: case KeyPathExpr::Component::Kind::UnresolvedProperty: case KeyPathExpr::Component::Kind::UnresolvedSubscript: { + auto *componentLoc = getConstraintLocator( + locator.withPathElement(LocatorPathElt::KeyPathComponent(i))); + auto *calleeLoc = getCalleeLocator(componentLoc); + auto overload = findSelectedOverloadFor(calleeLoc); + // If no choice was made, leave the constraint unsolved. But when // generating constraints, we may already have enough information // to determine whether the result will be a function type vs BGT KeyPath // type, so continue through components to create new constraint at the // end. - if (choices[i].isInvalid() || anyComponentsUnresolved) { + if (!overload || anyComponentsUnresolved) { if (flags.contains(TMF_GenerateConstraints)) { anyComponentsUnresolved = true; continue; } + + if (shouldAttemptFixes()) { + auto typeVar = + llvm::find_if(componentTypeVars, [&](TypeVariableType *typeVar) { + auto *locator = typeVar->getImpl().getLocator(); + auto elt = locator->findLast(); + return elt && elt->getIndex() == i; + }); + + // If one of the components haven't been resolved, let's check + // whether it has been determined to be a "hole" and if so, + // let's allow component validation to contiunue. + // + // This helps to, for example, diagnose problems with missing + // members used as part of a key path. + if (typeVar != componentTypeVars.end() && + (*typeVar)->getImpl().canBindToHole()) { + anyComponentsUnresolved = true; + capability = ReadOnly; + continue; + } + } + return SolutionKind::Unsolved; } // tuple elements do not change the capability of the key path - if (choices[i].getKind() == OverloadChoiceKind::TupleIndex) { + auto choice = overload->choice; + if (choice.getKind() == OverloadChoiceKind::TupleIndex) { continue; } // Discarded unsupported non-decl member lookups. - if (!choices[i].isDecl()) { + if (!choice.isDecl()) { return SolutionKind::Error; } - auto storage = dyn_cast(choices[i].getDecl()); - - auto *componentLoc = getConstraintLocator( - locator.withPathElement(LocatorPathElt::KeyPathComponent(i))); + auto storage = dyn_cast(choice.getDecl()); if (auto *fix = AllowInvalidRefInKeyPath::forRef( - *this, choices[i].getDecl(), componentLoc)) { - if (!shouldAttemptFixes() || recordFix(fix)) - return SolutionKind::Error; + *this, choice.getDecl(), calleeLoc)) { + if (!hasFixFor(calleeLoc, FixKind::AllowTypeOrInstanceMember)) + if (!shouldAttemptFixes() || recordFix(fix)) + return SolutionKind::Error; // If this was a method reference let's mark it as read-only. if (!storage) { @@ -6694,7 +7536,7 @@ Type ConstraintSystem::simplifyAppliedOverloads( // If we have a common result type, bind the expected result type to it. if (commonResultType && !commonResultType->is()) { ASTContext &ctx = getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(common result type for $T" << fnTypeVar->getID() << " is " @@ -6726,23 +7568,15 @@ ConstraintSystem::simplifyApplicableFnConstraint( // following: $T1 -> $T2. auto func1 = type1->castTo(); - // Let's check if this member couldn't be found and is fixed - // to exist based on its usage. - if (auto *memberTy = type2->getAs()) { - if (isHole(memberTy)) { - auto *funcTy = type1->castTo(); - auto *locator = memberTy->getImpl().getLocator(); - // Bind type variable associated with member to a type of argument - // application, which makes it seem like member exists with the - // types of the parameters matching argument types exactly. - addConstraint(ConstraintKind::Bind, memberTy, funcTy, locator); - // There might be no contextual type for result of the application, - // in cases like `let _ = x.foo()`, so let's default result to `Any` - // to make expressions like that type-check. - auto resultTy = funcTy->getResult(); - if (auto *typeVar = resultTy->getAs()) - recordHole(typeVar); - return SolutionKind::Solved; + // If a type variable representing "function type" is a hole + // or it could be bound to some concrete type with a help of + // a fix, let's propagate holes to the "input" type. Doing so + // provides more information to upcoming argument and result matching. + if (shouldAttemptFixes()) { + if (auto *typeVar = type2->getAs()) { + auto *locator = typeVar->getImpl().getLocator(); + if (typeVar->isHole() || hasFixFor(locator)) + recordPotentialHole(func1); } } @@ -6813,8 +7647,9 @@ ConstraintSystem::simplifyApplicableFnConstraint( } // If right-hand side is a type variable, the constraint is unsolved. - if (desugar2->isTypeVariableOrMember()) + if (desugar2->isTypeVariableOrMember()) { return formUnsolved(); + } // Strip the 'ApplyFunction' off the locator. // FIXME: Perhaps ApplyFunction can go away entirely? @@ -6828,23 +7663,18 @@ ConstraintSystem::simplifyApplicableFnConstraint( // Handle applications of types with `callAsFunction` methods. // Do this before stripping optional types below, when `shouldAttemptFixes()` // is true. - auto hasCallAsFunctionMethods = - desugar2->mayHaveMembers() && - llvm::any_of(lookupMember(desugar2, DeclName(ctx.Id_callAsFunction)), - [](LookupResultEntry entry) { - return isa(entry.getValueDecl()); - }); - if (hasCallAsFunctionMethods) { + if (desugar2->isCallableNominalType(DC)) { auto memberLoc = getConstraintLocator( - outerLocator.withPathElement(ConstraintLocator::Member)); + locator.withPathElement(ConstraintLocator::ImplicitCallAsFunction)); // Add a `callAsFunction` member constraint, binding the member type to a // type variable. auto memberTy = createTypeVariable(memberLoc, /*options=*/0); // TODO: Revisit this if `static func callAsFunction` is to be supported. // Static member constraint requires `FunctionRefKind::DoubleApply`. - addValueMemberConstraint(origLValueType2, DeclName(ctx.Id_callAsFunction), + addValueMemberConstraint(origLValueType2, + DeclNameRef(ctx.Id_callAsFunction), memberTy, DC, FunctionRefKind::SingleApply, - /*outerAlternatives*/ {}, locator); + /*outerAlternatives*/ {}, memberLoc); // Add new applicable function constraint based on the member type // variable. addConstraint(ConstraintKind::ApplicableFunction, func1, memberTy, @@ -6867,6 +7697,26 @@ ConstraintSystem::simplifyApplicableFnConstraint( // Track how many times we do this so that we can record a fix for each. ++unwrapCount; } + + // Let's account for optional members concept from Objective-C + // which forms a disjunction for member type to check whether + // it would be possible to use optional type directly or it has + // to be force unwrapped (because such types are imported as IUO). + if (unwrapCount > 0 && desugar2->is()) { + auto *typeVar = desugar2->castTo(); + auto *locator = typeVar->getImpl().getLocator(); + if (locator->isLastElement()) { + auto *fix = ForceOptional::create(*this, origType2, desugar2, + getConstraintLocator(locator)); + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; + + // Since the right-hand side of the constraint has been changed + // we have to re-generate this constraint to use new type. + flags |= TMF_GenerateConstraints; + return formUnsolved(); + } + } } // For a function, bind the output and convert the argument to the input. @@ -6877,7 +7727,7 @@ ConstraintSystem::simplifyApplicableFnConstraint( // The argument type must be convertible to the input type. if (::matchCallArguments( - *this, func1->getParams(), func2->getParams(), subKind, + *this, func2, func1->getParams(), func2->getParams(), subKind, outerLocator.withPathElement(ConstraintLocator::ApplyArgument)) .isFailure()) return SolutionKind::Error; @@ -6895,13 +7745,10 @@ ConstraintSystem::simplifyApplicableFnConstraint( return SolutionKind::Solved; // Record any fixes we attempted to get to the correct solution. - auto *fix = ForceOptional::create(*this, origType2, - origType2->getOptionalObjectType(), + auto *fix = ForceOptional::create(*this, origType2, func1, getConstraintLocator(locator)); - while (unwrapCount-- > 0) { - if (recordFix(fix)) - return SolutionKind::Error; - } + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; return SolutionKind::Solved; } @@ -6923,21 +7770,42 @@ ConstraintSystem::simplifyApplicableFnConstraint( if (unwrapCount == 0) return SolutionKind::Solved; - auto *fix = ForceOptional::create(*this, origType2, - origType2->getOptionalObjectType(), + auto *fix = ForceOptional::create(*this, origType2, func1, getConstraintLocator(locator)); - while (unwrapCount-- > 0) { - if (recordFix(fix)) - return SolutionKind::Error; - } + if (recordFix(fix, /*impact=*/unwrapCount)) + return SolutionKind::Error; } return simplified; } // Handle applications of @dynamicCallable types. - return simplifyDynamicCallableApplicableFnConstraint(type1, origType2, - subflags, locator); + auto result = simplifyDynamicCallableApplicableFnConstraint( + type1, origType2, subflags, locator); + + if (shouldAttemptFixes() && result == SolutionKind::Error) { + // Skip this fix if the type is not yet resolved or + // it's a function type/metatype which points to argument mismatches. + if (desugar2->is() || desugar2->is() || + desugar2->is()) + return SolutionKind::Error; + + // If there are any type variables associated with arguments/result + // they have to be marked as "holes". + recordPotentialHole(func1); + + if (desugar2->isHole()) + return SolutionKind::Solved; + + auto *fix = RemoveInvalidCall::create(*this, getConstraintLocator(locator)); + // Let's make this fix as high impact so if there is a function or member + // overload with e.g. argument-to-parameter type mismatches it would take + // a higher priority. + return recordFix(fix, /*impact=*/10) ? SolutionKind::Error + : SolutionKind::Solved; + } + + return result; } /// Looks up and returns the @dynamicCallable required methods (if they exist) @@ -6948,17 +7816,16 @@ lookupDynamicCallableMethods(Type type, ConstraintSystem &CS, Identifier argumentName, bool hasKeywordArgs) { auto &ctx = CS.getASTContext(); auto decl = type->getAnyNominal(); - auto methodName = DeclName(ctx, ctx.Id_dynamicallyCall, { argumentName }); - auto matches = CS.performMemberLookup(ConstraintKind::ValueMember, - methodName, type, - FunctionRefKind::SingleApply, - CS.getConstraintLocator(locator), - /*includeInaccessibleMembers*/ false); + DeclNameRef methodName({ ctx, ctx.Id_dynamicallyCall, { argumentName } }); + auto matches = CS.performMemberLookup( + ConstraintKind::ValueMember, methodName, type, + FunctionRefKind::SingleApply, CS.getConstraintLocator(locator), + /*includeInaccessibleMembers*/ false); // Filter valid candidates. auto candidates = matches.ViableCandidates; auto filter = [&](OverloadChoice choice) { auto cand = cast(choice.getDecl()); - return !isValidDynamicCallableMethod(cand, decl, CS.TC, hasKeywordArgs); + return !isValidDynamicCallableMethod(cand, decl, hasKeywordArgs); }; candidates.erase( std::remove_if(candidates.begin(), candidates.end(), filter), @@ -7175,13 +8042,35 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( // Record the 'dynamicallyCall` method overload set. SmallVector choices; for (auto candidate : candidates) { - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)candidate->getInterfaceType(); if (candidate->isInvalid()) continue; choices.push_back( OverloadChoice(type2, candidate, FunctionRefKind::SingleApply)); } - if (choices.empty()) return SolutionKind::Error; + + if (choices.empty()) { + if (!shouldAttemptFixes()) + return SolutionKind::Error; + + // TODO(diagnostics): This is not going to be necessary once + // `@dynamicCallable` uses existing `member` machinery. + + auto argLabel = useKwargsMethod ? ctx.Id_withKeywordArguments + : ctx.Id_withArguments; + DeclNameRef memberName({ ctx, ctx.Id_dynamicallyCall, {argLabel} }); + + auto *fix = DefineMemberBasedOnUse::create( + *this, desugar2, memberName, /*alreadyDiagnosed=*/false, + getConstraintLocator(loc, ConstraintLocator::DynamicCallable)); + + if (recordFix(fix)) + return SolutionKind::Error; + + recordPotentialHole(tv); + recordPotentialHole(func1); + + return SolutionKind::Solved; + } + addOverloadSet(tv, choices, DC, loc); // Create a type variable for the argument to the `dynamicallyCall` method. @@ -7214,26 +8103,36 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( addConstraint(ConstraintKind::Defaultable, argumentType, ctx.TheAnyType, locator); + auto *baseArgLoc = getConstraintLocator( + loc->getAnchor(), + {ConstraintLocator::DynamicCallable, ConstraintLocator::ApplyArgument}, + /*summaryFlags=*/0); + // All dynamic call parameter types must be convertible to the argument type. for (auto i : indices(func1->getParams())) { auto param = func1->getParams()[i]; auto paramType = param.getPlainType(); - auto locatorBuilder = - locator.withPathElement(LocatorPathElt::TupleElement(i)); - addConstraint(ConstraintKind::ArgumentConversion, paramType, - argumentType, locatorBuilder); + + addConstraint( + ConstraintKind::ArgumentConversion, paramType, argumentType, + getConstraintLocator(baseArgLoc, LocatorPathElt::ApplyArgToParam( + i, 0, param.getParameterFlags()))); } return SolutionKind::Solved; } -static Type getBaseTypeForPointer(ConstraintSystem &cs, TypeBase *type) { - if (Type unwrapped = type->getOptionalObjectType()) - type = unwrapped.getPointer(); +static llvm::PointerIntPair +getBaseTypeForPointer(TypeBase *type) { + unsigned unwrapCount = 0; + while (auto objectTy = type->getOptionalObjectType()) { + type = objectTy.getPointer(); + ++unwrapCount; + } auto pointeeTy = type->getAnyPointerElementType(); assert(pointeeTy); - return pointeeTy; + return {pointeeTy, unwrapCount}; } void ConstraintSystem::addRestrictedConstraint( @@ -7426,36 +8325,43 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto t2 = type2->getDesugaredType(); auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false); - auto baseType2 = getBaseTypeForPointer(*this, t2); + auto ptr2 = getBaseTypeForPointer(t2); - return matchPointerBaseTypes(baseType1, baseType2); + increaseScore(SK_ValueToOptional, ptr2.getInt()); + + return matchPointerBaseTypes(baseType1, ptr2.getPointer()); } // String ===> UnsafePointer<[U]Int8> case ConversionRestrictionKind::StringToPointer: { addContextualScore(); - auto baseType2 = getBaseTypeForPointer(*this, type2->getDesugaredType()); - + auto ptr2 = getBaseTypeForPointer(type2->getDesugaredType()); + + increaseScore(SK_ValueToOptional, ptr2.getInt()); + // The pointer element type must be void or a byte-sized type. // TODO: Handle different encodings based on pointer element type, such as // UTF16 for [U]Int16 or UTF32 for [U]Int32. For now we only interop with // Int8 pointers using UTF8 encoding. - baseType2 = getFixedTypeRecursive(baseType2, false); + auto baseType2 = getFixedTypeRecursive(ptr2.getPointer(), false); // If we haven't resolved the element type, generate constraints. if (baseType2->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { increaseScore(ScoreKind::SK_ValueToPointerConversion); + auto &ctx = getASTContext(); auto int8Con = Constraint::create(*this, ConstraintKind::Bind, - baseType2, TC.getInt8Type(DC), - getConstraintLocator(locator)); + baseType2, + TypeChecker::getInt8Type(ctx), + getConstraintLocator(locator)); auto uint8Con = Constraint::create(*this, ConstraintKind::Bind, - baseType2, TC.getUInt8Type(DC), - getConstraintLocator(locator)); + baseType2, + TypeChecker::getUInt8Type(ctx), + getConstraintLocator(locator)); auto voidCon = Constraint::create(*this, ConstraintKind::Bind, - baseType2, TC.Context.TheEmptyTupleType, - getConstraintLocator(locator)); + baseType2, ctx.TheEmptyTupleType, + getConstraintLocator(locator)); Constraint *disjunctionChoices[] = {int8Con, uint8Con, voidCon}; addDisjunctionConstraint(disjunctionChoices, locator); @@ -7465,7 +8371,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( return SolutionKind::Unsolved; } - if (!isStringCompatiblePointerBaseType(TC, DC, baseType2)) { + if (!isStringCompatiblePointerBaseType(getASTContext(), baseType2)) { return SolutionKind::Error; } @@ -7478,22 +8384,24 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( addContextualScore(); auto t2 = type2->getDesugaredType(); - + auto baseType1 = type1->getInOutObjectType(); - auto baseType2 = getBaseTypeForPointer(*this, t2); + auto ptr2 = getBaseTypeForPointer(t2); - return matchPointerBaseTypes(baseType1, baseType2); + increaseScore(SK_ValueToOptional, ptr2.getInt()); + + return matchPointerBaseTypes(baseType1, ptr2.getPointer()); } // T

UnsafeMutablePointer case ConversionRestrictionKind::PointerToPointer: { auto t1 = type1->getDesugaredType(); auto t2 = type2->getDesugaredType(); - - Type baseType1 = getBaseTypeForPointer(*this, t1); - Type baseType2 = getBaseTypeForPointer(*this, t2); - return matchPointerBaseTypes(baseType1, baseType2); + auto ptr1 = getBaseTypeForPointer(t1); + auto ptr2 = getBaseTypeForPointer(t2); + + return matchPointerBaseTypes(ptr1.getPointer(), ptr2.getPointer()); } // T < U or T is bridged to V where V < U ===> Array @@ -7574,7 +8482,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( } auto hashableProtocol = - TC.Context.getProtocol(KnownProtocolKind::Hashable); + getASTContext().getProtocol(KnownProtocolKind::Hashable); if (!hashableProtocol) return SolutionKind::Error; @@ -7634,10 +8542,27 @@ ConstraintSystem::simplifyRestrictedConstraint( ConstraintLocatorBuilder locator) { switch (simplifyRestrictedConstraintImpl(restriction, type1, type2, matchKind, flags, locator)) { - case SolutionKind::Solved: + case SolutionKind::Solved: { + // If we have an application of a non-ephemeral parameter, then record a + // fix if we have to treat an ephemeral conversion as non-ephemeral. It's + // important that this is solved as an independant constraint, as the + // solving of this restriction may be required in order to evaluate it. For + // example, when solving `foo(&.x)`, we need to first match types for the + // inout-to-pointer conversion, which then allows us to resolve the overload + // of `x`, which may or may not produce an ephemeral pointer. + if (locator.isNonEphemeralParameterApplication()) { + bool downgradeToWarning = + !getASTContext().LangOpts.DiagnoseInvalidEphemeralnessAsError; + + auto *fix = TreatEphemeralAsNonEphemeral::create( + *this, getConstraintLocator(locator), type1, type2, restriction, + downgradeToWarning); + addFixConstraint(fix, matchKind, type1, type2, locator); + } + ConstraintRestrictions.push_back(std::make_tuple(type1, type2, restriction)); return SolutionKind::Solved; - + } case SolutionKind::Unsolved: return SolutionKind::Unsolved; @@ -7659,7 +8584,7 @@ static bool isAugmentingFix(ConstraintFix *fix) { bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { auto &ctx = getASTContext(); - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(attempting fix "; @@ -7669,22 +8594,17 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { // Record the fix. - // If this is just a warning it's shouldn't affect the solver. - if (!fix->isWarning()) { - // Otherswise increase the score. If this would make the current - // solution worse than the best solution we've seen already, stop now. + // If this is just a warning, it shouldn't affect the solver. Otherwise, + // increase the score. + if (!fix->isWarning()) increaseScore(SK_Fix, impact); - if (worseThanBestSolution()) - return true; - } - if (isAugmentingFix(fix)) { - // Always useful, unless duplicate of exactly the same fix and location. - // This situation might happen when the same fix kind is applicable to - // different overload choices. - if (hasFixFor(fix->getLocator())) - return false; + // If we've made the current solution worse than the best solution we've seen + // already, stop now. + if (worseThanBestSolution()) + return true; + if (isAugmentingFix(fix)) { Fixes.push_back(fix); } else { // Only useful to record if no pre-existing fix in the subexpr tree. @@ -7702,32 +8622,48 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { return false; } -void ConstraintSystem::recordHole(TypeVariableType *typeVar) { +void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) { assert(typeVar); - auto *locator = typeVar->getImpl().getLocator(); - if (Holes.insert(locator)) { - addConstraint(ConstraintKind::Defaultable, typeVar, - getASTContext().TheAnyType, locator); - } + typeVar->getImpl().enableCanBindToHole(getSavedBindings()); +} + +void ConstraintSystem::recordPotentialHole(FunctionType *fnType) { + assert(fnType); + Type(fnType).visit([&](Type type) { + if (auto *typeVar = type->getAs()) + recordPotentialHole(typeVar); + }); } ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + + // Local function to form an unsolved result. + auto formUnsolved = [&] { + if (flags.contains(TMF_GenerateConstraints)) { + addUnsolvedConstraint(Constraint::createFixed( + *this, matchKind, fix, type1, type2, getConstraintLocator(locator))); + return SolutionKind::Solved; + } + return SolutionKind::Unsolved; + }; + // Try with the fix. TypeMatchOptions subflags = getDefaultDecompositionOptions(flags) | TMF_ApplyingFix; switch (fix->getKind()) { case FixKind::ForceOptional: { - // Assume that we've unwrapped the first type. - auto result = - matchTypes(type1->getRValueType()->getOptionalObjectType(), type2, - matchKind, subflags, locator); - if (result == SolutionKind::Solved) - if (recordFix(fix)) - return SolutionKind::Error; + SmallVector unwraps1; + type1->lookThroughAllOptionalTypes(unwraps1); - return result; + SmallVector unwraps2; + type2->lookThroughAllOptionalTypes(unwraps2); + + auto impact = unwraps1.size() != unwraps2.size() + ? unwraps1.size() - unwraps2.size() + : 1; + return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; } case FixKind::UnwrapOptionalBase: @@ -7765,6 +8701,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( } case FixKind::AllowTupleTypeMismatch: { + if (fix->getAs()->isElementMismatch()) { + auto *locator = fix->getLocator(); + if (recordFix(fix, /*impact*/locator->isForContextualType() ? 5 : 1)) + return SolutionKind::Error; + return SolutionKind::Solved; + } auto lhs = type1->castTo(); auto rhs = type2->castTo(); // Create a new tuple type the size of the smaller tuple with elements @@ -7776,6 +8718,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( // when the tuples (X, Y, $1) and (X, $0, B) get matched, $0 is equated // to Y, $1 is equated to B, and $2 is defaulted to Any. auto lhsLarger = lhs->getNumElements() >= rhs->getNumElements(); + auto isLabelingFailure = lhs->getNumElements() == rhs->getNumElements(); auto larger = lhsLarger ? lhs : rhs; auto smaller = lhsLarger ? rhs : lhs; llvm::SmallVector newTupleTypes; @@ -7784,16 +8727,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( auto largerElt = larger->getElement(i); if (i < smaller->getNumElements()) { auto smallerElt = smaller->getElement(i); - if (largerElt.getType()->isTypeVariableOrMember() || + if (isLabelingFailure) + newTupleTypes.push_back(TupleTypeElt(largerElt.getType())); + else if (largerElt.getType()->isTypeVariableOrMember() || smallerElt.getType()->isTypeVariableOrMember()) newTupleTypes.push_back(largerElt); else newTupleTypes.push_back(smallerElt); } else { if (largerElt.getType()->isTypeVariableOrMember()) - addConstraint(ConstraintKind::Defaultable, largerElt.getType(), - getASTContext().TheAnyType, - getConstraintLocator(locator)); + recordPotentialHole(largerElt.getType()->getAs()); } } auto matchingType = @@ -7803,20 +8746,67 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator); } + case FixKind::AllowFunctionTypeMismatch: { + if (recordFix(fix, /*impact=*/5)) + return SolutionKind::Error; + return SolutionKind::Solved; + } + + case FixKind::TreatEphemeralAsNonEphemeral: { + auto *theFix = static_cast(fix); + // If we have a non-ephemeral locator for an ephemeral conversion, make a + // note of the fix. + auto conversion = theFix->getConversionKind(); + switch (isConversionEphemeral(conversion, locator)) { + case ConversionEphemeralness::Ephemeral: + // Record the fix with an impact of zero. This ensures that non-ephemeral + // diagnostics don't impact solver behavior. + if (recordFix(fix, /*impact*/ 0)) + return SolutionKind::Error; + + return SolutionKind::Solved; + case ConversionEphemeralness::NonEphemeral: + return SolutionKind::Solved; + case ConversionEphemeralness::Unresolved: + // It's possible we don't yet have enough information to know whether + // the conversion is ephemeral or not, for example if we're dealing with + // an overload that hasn't yet been resolved. + return formUnsolved(); + } + } + case FixKind::InsertCall: case FixKind::RemoveReturn: case FixKind::RemoveAddressOf: - case FixKind::TreatRValueAsLValue: case FixKind::AddMissingArguments: case FixKind::SkipUnhandledConstructInFunctionBuilder: case FixKind::UsePropertyWrapper: case FixKind::UseWrappedValue: case FixKind::ExpandArrayIntoVarargs: case FixKind::UseValueTypeOfRawRepresentative: - case FixKind::ExplicitlyConstructRawRepresentable: { + case FixKind::ExplicitlyConstructRawRepresentable: + case FixKind::SpecifyBaseTypeForContextualMember: + case FixKind::CoerceToCheckedCast: + case FixKind::SpecifyObjectLiteralTypeImport: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } + case FixKind::TreatRValueAsLValue: { + unsigned impact = 1; + // If this is an attempt to use result of a function/subscript call as + // an l-value, it has to have an increased impact because it's either + // a function - which is completely incorrect, or it's a get-only + // subscript, which requires changes to declaration to become mutable. + if (auto last = locator.last()) { + impact += (last->is() || + last->is()) + ? 1 + : 0; + } + + return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved; + } + case FixKind::AddConformance: case FixKind::SkipSameTypeRequirement: case FixKind::SkipSuperclassRequirement: { @@ -7832,18 +8822,44 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( } case FixKind::ContextualMismatch: { - if (recordFix(fix)) + auto impact = 1; + + auto locator = fix->getLocator(); + if (auto branchElt = + locator->getLastElementAs()) { + // If this is `else` branch of a ternary operator, let's + // increase its impact to eliminate the chance of ambiguity. + // + // Branches are connected through two `subtype` constraints + // to a common type variable with represents their join, which + // means that result would attempt a type from each side if + // one is available and that would result in two fixes - one for + // each mismatched branch. + if (branchElt->forElse()) + impact = 10; + } + + if (recordFix(fix, impact)) return SolutionKind::Error; - // If type produced by expression is a function type - // with result type matching contextual, it should have - // been diagnosed as "missing explicit call", let's - // increase the score to make sure that we don't impede that. - if (auto *fnType = type1->getAs()) { - auto result = matchTypes(fnType->getResult(), type2, matchKind, - TMF_ApplyingFix, locator); - if (result == SolutionKind::Solved) - increaseScore(SK_Fix); + if (auto *fnType1 = type1->getAs()) { + // If this is a contextual mismatch between two + // function types which we couldn't find a more + // speficit fix for. Let's assume that such types + // are competely disjoint and adjust impact of + // the fix accordingly. + if (auto *fnType2 = type2->getAs()) { + increaseScore(SK_Fix, 10); + } else { + // If type produced by expression is a function type + // with result type matching contextual, it should have + // been diagnosed as "missing explicit call", let's + // increase the score to make sure that we don't impede that. + auto result = matchTypes(fnType1->getResult(), type2, matchKind, + TMF_ApplyingFix, locator); + if (result == SolutionKind::Solved) + increaseScore(SK_Fix); + } } return SolutionKind::Solved; @@ -7851,24 +8867,29 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::UseSubscriptOperator: case FixKind::ExplicitlyEscaping: - case FixKind::CoerceToCheckedCast: case FixKind::RelabelArguments: + case FixKind::RemoveCall: case FixKind::RemoveUnwrap: case FixKind::DefineMemberBasedOnUse: case FixKind::AllowMemberRefOnExistential: case FixKind::AllowTypeOrInstanceMember: case FixKind::AllowInvalidPartialApplication: case FixKind::AllowInvalidInitRef: + case FixKind::RemoveExtraneousArguments: case FixKind::AllowClosureParameterDestructuring: case FixKind::MoveOutOfOrderArgument: case FixKind::AllowInaccessibleMember: case FixKind::AllowAnyObjectKeyPathRoot: case FixKind::TreatKeyPathSubscriptIndexAsHashable: case FixKind::AllowInvalidRefInKeyPath: - case FixKind::ExplicitlySpecifyGenericArguments: + case FixKind::DefaultGenericArgument: case FixKind::GenericArgumentsMismatch: case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: + case FixKind::AllowInvalidUseOfTrailingClosure: + case FixKind::AllowNonClassTypeToConvertToAnyObject: + case FixKind::SpecifyClosureReturnType: + case FixKind::AddQualifierToAccessTopLevelName: llvm_unreachable("handled elsewhere"); } @@ -7945,10 +8966,12 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::BindOverload: case ConstraintKind::Disjunction: case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: + case ConstraintKind::DefaultClosureType: llvm_unreachable("Use the correct addConstraint()"); } @@ -7965,9 +8988,11 @@ ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocat auto subscript = dyn_cast_or_null(anchor); if (!subscript) return; - - assert(path.size() == 1 && - path[0].getKind() == ConstraintLocator::SubscriptMember); + + assert((path.size() == 1 && + path[0].getKind() == ConstraintLocator::SubscriptMember) || + (path.size() == 2 && + path[1].getKind() == ConstraintLocator::KeyPathDynamicMember)); auto indexTuple = dyn_cast(subscript->getIndex()); if (!indexTuple || indexTuple->getNumElements() != 1) return; @@ -8113,6 +9138,60 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, } } +void ConstraintSystem::addContextualConversionConstraint( + Expr *expr, ContextualTypeInfo contextualType) { + Type convertType = contextualType.getType(); + if (convertType.isNull()) + return; + + // Determine the type of the constraint. + auto constraintKind = ConstraintKind::Conversion; + switch (contextualType.purpose) { + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_Initialization: + if (contextualType.isOpaqueReturnType) + constraintKind = ConstraintKind::OpaqueUnderlyingType; + break; + + case CTP_CallArgument: + constraintKind = ConstraintKind::ArgumentConversion; + break; + + case CTP_YieldByReference: + // In a by-reference yield, we expect the contextual type to be an + // l-value type, so the result must be bound to that. + constraintKind = ConstraintKind::Bind; + break; + + case CTP_ArrayElement: + case CTP_AssignSource: + case CTP_CalleeResult: + case CTP_CannotFail: + case CTP_Condition: + case CTP_Unused: + case CTP_YieldByValue: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_ClosureResult: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_SubscriptAssignSource: + case CTP_ForEachStmt: + break; + } + + // Add the constraint. + bool isForSingleExprFunction = (contextualType.purpose == CTP_ReturnSingleExpr); + auto *convertTypeLocator = getConstraintLocator( + expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + addConstraint(constraintKind, getType(expr), convertType, + convertTypeLocator, /*isFavored*/ true); +} + Type ConstraintSystem::addJoinConstraint( ConstraintLocator *locator, ArrayRef> inputs) { @@ -8142,6 +9221,30 @@ Type ConstraintSystem::addJoinConstraint( return resultTy; } +void ConstraintSystem::addFixConstraint(ConstraintFix *fix, ConstraintKind kind, + Type first, Type second, + ConstraintLocatorBuilder locator, + bool isFavored) { + TypeMatchOptions subflags = TMF_GenerateConstraints; + switch (simplifyFixConstraint(fix, first, second, kind, subflags, locator)) { + case SolutionKind::Error: + // Add a failing constraint, if needed. + if (shouldAddNewFailingConstraint()) { + auto c = Constraint::createFixed(*this, kind, fix, first, second, + getConstraintLocator(locator)); + if (isFavored) c->setFavored(); + addNewFailingConstraint(c); + } + return; + + case SolutionKind::Unsolved: + llvm_unreachable("should have generated constraints"); + + case SolutionKind::Solved: + return; + } +} + void ConstraintSystem::addExplicitConversionConstraint( Type fromType, Type toType, bool allowFixes, @@ -8163,14 +9266,6 @@ void ConstraintSystem::addExplicitConversionConstraint( fromType, toType, locatorPtr); constraints.push_back(bridgingConstraint); - if (allowFixes && shouldAttemptFixes()) { - Constraint *downcastConstraint = - Constraint::createFixed(*this, ConstraintKind::CheckedCast, - CoerceToCheckedCast::create(*this, locatorPtr), - fromType, toType, locatorPtr); - constraints.push_back(downcastConstraint); - } - addDisjunctionConstraint(constraints, locator, allowFixes ? RememberChoice : ForgetChoice); @@ -8259,7 +9354,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::BindOverload: if (auto *fix = constraint.getFix()) { - if (recordFix(fix)) + // TODO(diagnostics): Impact should be associated with a fix unless + // it's a contextual problem, then only solver can decide what the impact + // would be in each particular situation. + auto impact = + fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1; + if (recordFix(fix, impact)) return SolutionKind::Error; } @@ -8314,12 +9414,29 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { TMF_GenerateConstraints, constraint.getLocator()); + case ConstraintKind::ValueWitness: + return simplifyValueWitnessConstraint(constraint.getKind(), + constraint.getFirstType(), + constraint.getRequirement(), + constraint.getSecondType(), + constraint.getMemberUseDC(), + constraint.getFunctionRefKind(), + TMF_GenerateConstraints, + constraint.getLocator()); + case ConstraintKind::Defaultable: return simplifyDefaultableConstraint(constraint.getFirstType(), constraint.getSecondType(), TMF_GenerateConstraints, constraint.getLocator()); + case ConstraintKind::DefaultClosureType: + return simplifyDefaultClosureTypeConstraint(constraint.getFirstType(), + constraint.getSecondType(), + constraint.getTypeVariables(), + TMF_GenerateConstraints, + constraint.getLocator()); + case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: return simplifyFunctionComponentConstraint(constraint.getKind(), diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 91b82eaf0f6ac..9e8c3678a7f2f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -16,6 +16,7 @@ #include "CSStep.h" #include "ConstraintGraph.h" #include "ConstraintSystem.h" +#include "SolutionResult.h" #include "TypeCheckType.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeWalker.h" @@ -55,7 +56,7 @@ TypeVariableType *ConstraintSystem::createTypeVariable( ConstraintLocator *locator, unsigned options) { ++TotalNumTypeVariables; - auto tv = TypeVariableType::getNew(TC.Context, assignTypeVariableID(), + auto tv = TypeVariableType::getNew(getASTContext(), assignTypeVariableID(), locator, options); addTypeVariable(tv); return tv; @@ -68,8 +69,9 @@ Solution ConstraintSystem::finalize() { Solution solution(*this, CurrentScore); // Update the best score we've seen so far. + auto &ctx = getASTContext(); if (!retainAllSolutions()) { - assert(TC.getLangOpts().DisableConstraintSolverPerformanceHacks || + assert(ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks || !solverState->BestScore || CurrentScore <= *solverState->BestScore); if (!solverState->BestScore || CurrentScore <= *solverState->BestScore) { @@ -89,7 +91,7 @@ Solution ConstraintSystem::finalize() { break; case FreeTypeVariableBinding::UnresolvedType: - assignFixedType(tv, TC.Context.TheUnresolvedType); + assignFixedType(tv, ctx.TheUnresolvedType); break; } } @@ -99,12 +101,9 @@ Solution ConstraintSystem::finalize() { solution.typeBindings[tv] = simplifyType(tv)->reconstituteSugar(false); } - // For each of the overload sets, get its overload choice. - for (auto resolved = resolvedOverloadSets; - resolved; resolved = resolved->Previous) { - solution.overloadChoices[resolved->Locator] - = { resolved->Choice, resolved->OpenedFullType, resolved->ImpliedType }; - } + // Copy over the resolved overloads. + solution.overloadChoices.insert(ResolvedOverloads.begin(), + ResolvedOverloads.end()); // For each of the constraint restrictions, record it with simplified, // canonical types. @@ -166,19 +165,18 @@ Solution ConstraintSystem::finalize() { DefaultedConstraints.end()); for (auto &nodeType : addedNodeTypes) { - solution.addedNodeTypes.push_back(nodeType); + solution.addedNodeTypes.insert(nodeType); } + // Remember contextual types. + solution.contextualTypes.assign( + contextualTypes.begin(), contextualTypes.end()); + for (auto &e : CheckedConformances) solution.Conformances.push_back({e.first, e.second}); - for (const auto &transformed : builderTransformedClosures) { - auto known = - solution.builderTransformedClosures.find(transformed.first); - if (known != solution.builderTransformedClosures.end()) { - assert(known->second.singleExpr == transformed.second.singleExpr); - } - solution.builderTransformedClosures.insert(transformed); + for (const auto &transformed : functionBuilderTransformed) { + solution.functionBuilderTransformed.insert(transformed); } return solution; @@ -201,15 +199,8 @@ void ConstraintSystem::applySolution(const Solution &solution) { // Register overload choices. // FIXME: Copy these directly into some kind of partial solution? - for (auto overload : solution.overloadChoices) { - resolvedOverloadSets - = new (*this) ResolvedOverloadSetListItem{resolvedOverloadSets, - Type(), - overload.second.choice, - overload.first, - overload.second.openedFullType, - overload.second.openedType}; - } + for (auto overload : solution.overloadChoices) + ResolvedOverloads.insert(overload); // Register constraint restrictions. // FIXME: Copy these directly into some kind of partial solution? @@ -245,12 +236,21 @@ void ConstraintSystem::applySolution(const Solution &solution) { setType(nodeType.first, nodeType.second); } + // Add the contextual types. + for (const auto &contextualType : solution.contextualTypes) { + if (!getContextualTypeInfo(contextualType.first)) { + setContextualType(contextualType.first, contextualType.second.typeLoc, + contextualType.second.purpose, + contextualType.second.isOpaqueReturnType); + } + } + // Register the conformances checked along the way to arrive to solution. for (auto &conformance : solution.Conformances) CheckedConformances.push_back(conformance); - for (const auto &transformed : solution.builderTransformedClosures) { - builderTransformedClosures.push_back(transformed); + for (const auto &transformed : solution.functionBuilderTransformed) { + functionBuilderTransformed.push_back(transformed); } // Register any fixes produced along this path. @@ -283,7 +283,7 @@ bool ConstraintSystem::simplify(bool ContinueAfterFailures) { failedConstraint = constraint; } - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 : 0) << "(failed constraint "; @@ -343,6 +343,13 @@ void truncate(llvm::SmallSetVector &vec, unsigned newSize) { vec.pop_back(); } +template +void truncate(llvm::MapVector &map, unsigned newSize) { + assert(newSize <= map.size() && "Not a truncation!"); + for (unsigned i = 0, n = map.size() - newSize; i != n; ++i) + map.pop_back(); +} + } // end anonymous namespace ConstraintSystem::SolverState::SolverState( @@ -361,12 +368,12 @@ ConstraintSystem::SolverState::SolverState( // If we're supposed to debug a specific constraint solver attempt, // turn on debugging now. - ASTContext &ctx = CS.getTypeChecker().Context; - LangOptions &langOpts = ctx.LangOpts; - OldDebugConstraintSolver = langOpts.DebugConstraintSolver; - if (langOpts.DebugConstraintSolverAttempt && - langOpts.DebugConstraintSolverAttempt == SolutionAttempt) { - langOpts.DebugConstraintSolver = true; + ASTContext &ctx = CS.getASTContext(); + auto &tyOpts = ctx.TypeCheckerOpts; + OldDebugConstraintSolver = tyOpts.DebugConstraintSolver; + if (tyOpts.DebugConstraintSolverAttempt && + tyOpts.DebugConstraintSolverAttempt == SolutionAttempt) { + tyOpts.DebugConstraintSolver = true; llvm::raw_ostream &dbgOut = ctx.TypeCheckerDebug->getStream(); dbgOut << "---Constraint system #" << SolutionAttempt << "---\n"; CS.print(dbgOut); @@ -408,8 +415,8 @@ ConstraintSystem::SolverState::~SolverState() { } // Restore debugging state. - LangOptions &langOpts = CS.getTypeChecker().Context.LangOpts; - langOpts.DebugConstraintSolver = OldDebugConstraintSolver; + TypeCheckerOptions &tyOpts = CS.getASTContext().TypeCheckerOpts; + tyOpts.DebugConstraintSolver = OldDebugConstraintSolver; // Write our local statistics back to the overall statistics. #define CS_STATISTIC(Name, Description) JOIN2(Overall,Name) += Name; @@ -431,12 +438,10 @@ ConstraintSystem::SolverState::~SolverState() { ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) : cs(cs), CGScope(cs.CG) { - resolvedOverloadSets = cs.resolvedOverloadSets; numTypeVariables = cs.TypeVariables.size(); numSavedBindings = cs.solverState->savedBindings.size(); numConstraintRestrictions = cs.ConstraintRestrictions.size(); numFixes = cs.Fixes.size(); - numHoles = cs.Holes.size(); numFixedRequirements = cs.FixedRequirements.size(); numDisjunctionChoices = cs.DisjunctionChoices.size(); numOpenedTypes = cs.OpenedTypes.size(); @@ -446,7 +451,10 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); - numBuilderTransformedClosures = cs.builderTransformedClosures.size(); + numFunctionBuilderTransformed = cs.functionBuilderTransformed.size(); + numResolvedOverloads = cs.ResolvedOverloads.size(); + numInferredClosureTypes = cs.ClosureTypes.size(); + numContextualTypes = cs.contextualTypes.size(); PreviousScore = cs.CurrentScore; @@ -456,10 +464,11 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) ConstraintSystem::SolverScope::~SolverScope() { // Erase the end of various lists. - cs.resolvedOverloadSets = resolvedOverloadSets; while (cs.TypeVariables.size() > numTypeVariables) cs.TypeVariables.pop_back(); + truncate(cs.ResolvedOverloads, numResolvedOverloads); + // Restore bindings. cs.restoreTypeVariableBindings(cs.solverState->savedBindings.size() - numSavedBindings); @@ -484,9 +493,6 @@ ConstraintSystem::SolverScope::~SolverScope() { // Remove any fixes. truncate(cs.Fixes, numFixes); - // Remove any holes encountered along the current path. - truncate(cs.Holes, numHoles); - // Remove any disjunction choices. truncate(cs.DisjunctionChoices, numDisjunctionChoices); @@ -513,7 +519,13 @@ ConstraintSystem::SolverScope::~SolverScope() { truncate(cs.CheckedConformances, numCheckedConformances); /// Remove any builder transformed closures. - truncate(cs.builderTransformedClosures, numBuilderTransformedClosures); + truncate(cs.functionBuilderTransformed, numFunctionBuilderTransformed); + + // Remove any inferred closure types (e.g. used in function builder body). + truncate(cs.ClosureTypes, numInferredClosureTypes); + + // Remove any contextual types. + truncate(cs.contextualTypes, numContextualTypes); // Reset the previous score. cs.CurrentScore = PreviousScore; @@ -537,7 +549,7 @@ ConstraintSystem::solveSingle(FreeTypeVariableBinding allowFreeTypeVariables, state.recordFixes = allowFixes; SmallVector solutions; - solve(solutions); + solveImpl(solutions); filterSolutions(solutions); if (solutions.size() != 1) @@ -573,14 +585,14 @@ bool ConstraintSystem::Candidate::solve( }; // Allocate new constraint system for sub-expression. - ConstraintSystem cs(TC, DC, None, E); + ConstraintSystem cs(DC, None); cs.baseCS = &BaseCS; // Set up expression type checker timer for the candidate. cs.Timer.emplace(E, cs); // Generate constraints for the new system. - if (auto generatedExpr = cs.generateConstraints(E)) { + if (auto generatedExpr = cs.generateConstraints(E, DC)) { E = generatedExpr; } else { // Failure to generate constraint system for sub-expression @@ -594,12 +606,13 @@ bool ConstraintSystem::Candidate::solve( if (isTooComplexGiven(&cs, shrunkExprs)) return false; - if (TC.getLangOpts().DebugConstraintSolver) { + auto &ctx = cs.getASTContext(); + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log << "--- Solving candidate for shrinking at "; auto R = E->getSourceRange(); if (R.isValid()) { - R.print(log, TC.Context.SourceMgr, /*PrintText=*/ false); + R.print(log, ctx.SourceMgr, /*PrintText=*/ false); } else { log << ""; } @@ -628,10 +641,10 @@ bool ConstraintSystem::Candidate::solve( // Use solve which doesn't try to filter solution list. // Because we want the whole set of possible domain choices. - cs.solve(solutions); + cs.solveImpl(solutions); } - if (TC.getLangOpts().DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); if (solutions.empty()) { log << "--- No Solutions ---\n"; @@ -715,7 +728,7 @@ void ConstraintSystem::Candidate::applySolutions( } void ConstraintSystem::shrink(Expr *expr) { - if (TC.getLangOpts().SolverDisableShrink) + if (getASTContext().TypeCheckerOpts.SolverDisableShrink) return; using DomainMap = llvm::SmallDenseMap>; @@ -751,7 +764,7 @@ void ConstraintSystem::shrink(Expr *expr) { // that have overload sets. if (auto collectionExpr = dyn_cast(expr)) { visitCollectionExpr(collectionExpr, CS.getContextualType(expr), - CS.getContextualTypePurpose()); + CS.getContextualTypePurpose(expr)); // Don't try to walk into the dictionary. return {false, expr}; } @@ -817,11 +830,11 @@ void ConstraintSystem::shrink(Expr *expr) { if (Candidates.empty()) return expr; - auto contextualType = CS.getContextualType(); + auto contextualType = CS.getContextualType(expr); // If there is a contextual type set for this expression. if (!contextualType.isNull()) { Candidates.push_back(Candidate(CS, PrimaryExpr, contextualType, - CS.getContextualTypePurpose())); + CS.getContextualTypePurpose(expr))); return expr; } @@ -1053,34 +1066,35 @@ void ConstraintSystem::shrink(Expr *expr) { // survive even after primary constraint system is destroyed. for (auto &OSR : shrunkExprs) { auto choices = OSR->getDecls(); - auto decls = TC.Context.AllocateUninitialized(choices.size()); + auto decls = + getASTContext().AllocateUninitialized(choices.size()); std::uninitialized_copy(choices.begin(), choices.end(), decls.begin()); OSR->setDecls(decls); } } -static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { - if (C.LangOpts.DebugConstraintSolver) +static bool debugConstraintSolverForTarget( + ASTContext &C, SolutionApplicationTarget target) { + if (C.TypeCheckerOpts.DebugConstraintSolver) return true; - if (C.LangOpts.DebugConstraintSolverOnLines.empty()) + if (C.TypeCheckerOpts.DebugConstraintSolverOnLines.empty()) // No need to compute the line number to find out it's not present. return false; - // Get the lines on which the expression starts and ends. + // Get the lines on which the target starts and ends. unsigned startLine = 0, endLine = 0; - if (expr->getSourceRange().isValid()) { - auto range = - Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, - expr->getSourceRange()); - startLine = C.SourceMgr.getLineNumber(range.getStart()); - endLine = C.SourceMgr.getLineNumber(range.getEnd()); + SourceRange range = target.getSourceRange(); + if (range.isValid()) { + auto charRange = Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, range); + startLine = C.SourceMgr.getLineNumber(charRange.getStart()); + endLine = C.SourceMgr.getLineNumber(charRange.getEnd()); } assert(startLine <= endLine && "expr ends before it starts?"); - auto &lines = C.LangOpts.DebugConstraintSolverOnLines; + auto &lines = C.TypeCheckerOpts.DebugConstraintSolverOnLines; assert(std::is_sorted(lines.begin(), lines.end()) && "DebugConstraintSolverOnLines sorting invariant violated"); @@ -1093,79 +1107,130 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { return startBound != endBound; } -bool ConstraintSystem::solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables) { - llvm::SaveAndRestore - debugForExpr(TC.getLangOpts().DebugConstraintSolver, - debugConstraintSolverForExpr(TC.Context, expr)); - - // Attempt to solve the constraint system. - auto solution = solveImpl(expr, - convertType, - listener, - solutions, - allowFreeTypeVariables); - - // The constraint system has failed - if (solution == SolutionKind::Error) - return true; +/// If we aren't certain that we've emitted a diagnostic, emit a fallback +/// diagnostic. +static void maybeProduceFallbackDiagnostic( + ConstraintSystem &cs, SolutionApplicationTarget target) { + if (cs.Options.contains(ConstraintSystemFlags::SubExpressionDiagnostics) || + cs.Options.contains(ConstraintSystemFlags::SuppressDiagnostics)) + return; - // If the system is unsolved or there are multiple solutions present but - // type checker options do not allow unresolved types, let's try to salvage - if (solution == SolutionKind::Unsolved || - (solutions.size() != 1 && - !Options.contains( - ConstraintSystemFlags::AllowUnresolvedTypeVariables))) { - if (shouldSuppressDiagnostics()) - return true; + // Before producing fatal error here, let's check if there are any "error" + // diagnostics already emitted or waiting to be emitted. Because they are + // a better indication of the problem. + ASTContext &ctx = cs.getASTContext(); + if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors()) + return; - // Try to provide a decent diagnostic. - if (salvage(solutions, expr)) { - // If salvage produced an error message, then it failed to salvage the - // expression, just bail out having reported the error. - return true; + ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); +} + +Optional> ConstraintSystem::solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables +) { + llvm::SaveAndRestore debugForExpr( + getASTContext().TypeCheckerOpts.DebugConstraintSolver, + debugConstraintSolverForTarget(getASTContext(), target)); + + /// Dump solutions for debugging purposes. + auto dumpSolutions = [&](const SolutionResult &result) { + // Debug-print the set of solutions. + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { + auto &log = getASTContext().TypeCheckerDebug->getStream(); + if (result.getKind() == SolutionResult::Success) { + log << "---Solution---\n"; + result.getSolution().dump(log); + } else if (result.getKind() == SolutionResult::Ambiguous) { + auto solutions = result.getAmbiguousSolutions(); + for (unsigned i : indices(solutions)) { + log << "--- Solution #" << i << " ---\n"; + solutions[i].dump(log); + } + } } + }; - // The system was salvaged; continue on as if nothing happened. - } + // Take up to two attempts at solving the system. The first attempts to + // solve a system that is expected to be well-formed, the second kicks in + // when there is an error and attempts to salvage an ill-formed program. + for (unsigned stage = 0; stage != 2; ++stage) { + auto solution = (stage == 0) + ? solveImpl(target, listener, allowFreeTypeVariables) + : salvage(); + + switch (solution.getKind()) { + case SolutionResult::Success: { + // Return the successful solution. + dumpSolutions(solution); + std::vector result; + result.push_back(std::move(solution).takeSolution()); + return std::move(result); + } - if (getExpressionTooComplex(solutions)) { - TC.diagnose(expr->getLoc(), diag::expression_too_complex). - highlight(expr->getSourceRange()); - return true; - } + case SolutionResult::Error: + maybeProduceFallbackDiagnostic(*this, target); + return None; + + case SolutionResult::TooComplex: + getASTContext().Diags.diagnose( + target.getLoc(), diag::expression_too_complex) + .highlight(target.getSourceRange()); + solution.markAsDiagnosed(); + return None; + + case SolutionResult::Ambiguous: + // If salvaging produced an ambiguous result, it has already been + // diagnosed. + if (stage == 1) { + solution.markAsDiagnosed(); + return None; + } - if (TC.getLangOpts().DebugConstraintSolver) { - auto &log = getASTContext().TypeCheckerDebug->getStream(); - if (solutions.size() == 1) { - log << "---Solution---\n"; - solutions[0].dump(log); - } else { - for (unsigned i = 0, e = solutions.size(); i != e; ++i) { - log << "--- Solution #" << i << " ---\n"; - solutions[i].dump(log); + if (Options.contains( + ConstraintSystemFlags::AllowUnresolvedTypeVariables)) { + dumpSolutions(solution); + auto ambiguousSolutions = std::move(solution).takeAmbiguousSolutions(); + std::vector result( + std::make_move_iterator(ambiguousSolutions.begin()), + std::make_move_iterator(ambiguousSolutions.end())); + return std::move(result); + } + + LLVM_FALLTHROUGH; + + case SolutionResult::UndiagnosedError: + if (shouldSuppressDiagnostics()) { + solution.markAsDiagnosed(); + return None; } + + if (stage == 1) { + diagnoseFailureFor(target); + solution.markAsDiagnosed(); + return None; + } + + // Loop again to try to salvage. + solution.markAsDiagnosed(); + continue; } } - return false; + llvm_unreachable("Loop always returns"); } -ConstraintSystem::SolutionKind -ConstraintSystem::solveImpl(Expr *&expr, - Type convertType, +SolutionResult +ConstraintSystem::solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - log << "---Constraint solving for the expression at "; - auto R = expr->getSourceRange(); + log << "---Constraint solving at "; + auto R = target.getSourceRange(); if (R.isValid()) { - R.print(log, TC.Context.SourceMgr, /*PrintText=*/ false); + R.print(log, getASTContext().SourceMgr, /*PrintText=*/ false); } else { log << ""; } @@ -1175,47 +1240,40 @@ ConstraintSystem::solveImpl(Expr *&expr, assert(!solverState && "cannot be used directly"); // Set up the expression type checker timer. + Expr *expr = target.getAsExpr(); Timer.emplace(expr, *this); + Expr *origExpr = expr; + // Try to shrink the system by reducing disjunction domains. This // goes through every sub-expression and generate its own sub-system, to // try to reduce the domains of those subexpressions. shrink(expr); // Generate constraints for the main system. - if (auto generatedExpr = generateConstraints(expr)) + if (auto generatedExpr = generateConstraints(expr, DC)) expr = generatedExpr; else { - if (listener) - listener->constraintGenerationFailed(expr); - return SolutionKind::Error; + return SolutionResult::forError(); } // If there is a type that we're expected to convert to, add the conversion // constraint. - if (convertType) { - auto constraintKind = ConstraintKind::Conversion; - - if ((getContextualTypePurpose() == CTP_ReturnStmt || - getContextualTypePurpose() == CTP_ReturnSingleExpr || - getContextualTypePurpose() == CTP_Initialization) - && Options.contains(ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType)) - constraintKind = ConstraintKind::OpaqueUnderlyingType; - - if (getContextualTypePurpose() == CTP_CallArgument) - constraintKind = ConstraintKind::ArgumentConversion; - - // In a by-reference yield, we expect the contextual type to be an - // l-value type, so the result must be bound to that. - if (getContextualTypePurpose() == CTP_YieldByReference) - constraintKind = ConstraintKind::Bind; - - bool isForSingleExprFunction = - getContextualTypePurpose() == CTP_ReturnSingleExpr; - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + if (Type convertType = target.getExprConversionType()) { + // Determine whether we know more about the contextual type. + ContextualTypePurpose ctp = CTP_Unused; + bool isOpaqueReturnType = false; + if (auto contextualInfo = getContextualTypeInfo(origExpr)) { + ctp = contextualInfo->purpose; + isOpaqueReturnType = contextualInfo->isOpaqueReturnType; + } + // Substitute type variables in for unresolved types. if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { + bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); + auto *convertTypeLocator = getConstraintLocator( + expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + convertType = convertType.transform([&](Type type) -> Type { if (type->is()) return createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); @@ -1223,16 +1281,17 @@ ConstraintSystem::solveImpl(Expr *&expr, }); } - addConstraint(constraintKind, getType(expr), convertType, - convertTypeLocator, /*isFavored*/ true); + ContextualTypeInfo info{ + TypeLoc::withoutLoc(convertType), ctp, isOpaqueReturnType}; + addContextualConversionConstraint(expr, info); } // Notify the listener that we've built the constraint system. if (listener && listener->builtConstraints(*this, expr)) { - return SolutionKind::Error; + return SolutionResult::forError(); } - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Initial constraints for the given expression---\n"; print(log, expr); @@ -1241,23 +1300,35 @@ ConstraintSystem::solveImpl(Expr *&expr, } // Try to solve the constraint system using computed suggestions. - solve(expr, solutions, allowFreeTypeVariables); + SmallVector solutions; + solve(solutions, allowFreeTypeVariables); + + if (getExpressionTooComplex(solutions)) + return SolutionResult::forTooComplex(); + + target.setExpr(expr); - // If there are no solutions let's mark system as unsolved, - // and solved otherwise even if there are multiple solutions still present. - return solutions.empty() ? SolutionKind::Unsolved : SolutionKind::Solved; + switch (solutions.size()) { + case 0: + return SolutionResult::forUndiagnosedError(); + + case 1: + return SolutionResult::forSolved(std::move(solutions.front())); + + default: + return SolutionResult::forAmbiguous(solutions); + } } -bool ConstraintSystem::solve(Expr *const expr, - SmallVectorImpl &solutions, +bool ConstraintSystem::solve(SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables) { // Set up solver state. SolverState state(*this, allowFreeTypeVariables); // Solve the system. - solve(solutions); + solveImpl(solutions); - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Solver statistics---\n"; log << "Total number of scopes explored: " << solverState->NumStatesExplored << "\n"; @@ -1279,9 +1350,13 @@ bool ConstraintSystem::solve(Expr *const expr, return solutions.empty() || getExpressionTooComplex(solutions); } -void ConstraintSystem::solve(SmallVectorImpl &solutions) { +void ConstraintSystem::solveImpl(SmallVectorImpl &solutions) { assert(solverState); + setPhase(ConstraintSystemPhase::Solving); + + SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); }; + // If constraint system failed while trying to // genenerate constraints, let's stop right here. if (failedConstraint) @@ -1391,7 +1466,7 @@ ConstraintSystem::filterDisjunction( continue; } - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(disabled disjunction term "; @@ -1433,7 +1508,7 @@ ConstraintSystem::filterDisjunction( // Early simplification of the "keypath dynamic member lookup" choice // is impossible because it requires constraints associated with // subscript index expression to be present. - if (!solverState) + if (Phase == ConstraintSystemPhase::ConstraintGeneration) return SolutionKind::Unsolved; for (auto *currentChoice : disjunction->getNestedConstraints()) { @@ -1452,7 +1527,7 @@ ConstraintSystem::filterDisjunction( recordDisjunctionChoice(disjunction->getLocator(), choiceIdx); } - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 + 2 : 0) << "(introducing single enabled disjunction term "; @@ -1658,6 +1733,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::BindToPointerType: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Disjunction: case ConstraintKind::CheckedCast: @@ -1671,6 +1747,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::ConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: break; } } @@ -1685,7 +1762,7 @@ void ConstraintSystem::ArgumentInfoCollector::minimizeLiteralProtocols() { llvm::SmallVector skippedProtocols; for (auto *protocol : LiteralProtocols) { - if (auto defaultType = CS.TC.getDefaultType(protocol, CS.DC)) { + if (auto defaultType = TypeChecker::getDefaultType(protocol, CS.DC)) { candidates.push_back({protocol, defaultType}); continue; } @@ -1709,10 +1786,10 @@ void ConstraintSystem::ArgumentInfoCollector::minimizeLiteralProtocols() { auto second = TypeChecker::conformsToProtocol(candidates[result].second, candidate.first, CS.DC, ConformanceCheckFlags::InExpression); - if ((first && second) || (!first && !second)) + if (first.isInvalid() == second.isInvalid()) return; - if (first) + if (!first.isInvalid()) result = i; } @@ -1909,8 +1986,8 @@ void ConstraintSystem::sortDesignatedTypes( ++nextType; break; } else if (auto *protoDecl = dyn_cast(nominalTypes[i])) { - if (TypeChecker::conformsToProtocol(argType, protoDecl, DC, - ConformanceCheckFlags::InExpression)) { + if (TypeChecker::conformsToProtocol( + argType, protoDecl, DC, ConformanceCheckFlags::InExpression)) { std::swap(nominalTypes[nextType], nominalTypes[i]); ++nextType; break; @@ -1923,7 +2000,7 @@ void ConstraintSystem::sortDesignatedTypes( return; for (auto *protocol : argInfo.getLiteralProtocols()) { - auto defaultType = TC.getDefaultType(protocol, DC); + auto defaultType = TypeChecker::getDefaultType(protocol, DC); // ExpressibleByNilLiteral does not have a default type. if (!defaultType) continue; @@ -2010,7 +2087,6 @@ void ConstraintSystem::partitionForDesignatedTypes( // Performance hack: if there are two generic overloads, and one is // more specialized than the other, prefer the more-specialized one. static Constraint *tryOptimizeGenericDisjunction( - TypeChecker &tc, DeclContext *dc, ArrayRef constraints) { llvm::SmallVector choices; @@ -2067,7 +2143,7 @@ static Constraint *tryOptimizeGenericDisjunction( if (!isViable(declA) || !isViable(declB)) return nullptr; - switch (tc.compareDeclarations(dc, declA, declB)) { + switch (TypeChecker::compareDeclarations(dc, declA, declB)) { case Comparison::Better: return choices[0]; @@ -2085,7 +2161,7 @@ void ConstraintSystem::partitionDisjunction( SmallVectorImpl &PartitionBeginning) { // Apply a special-case rule for favoring one generic function over // another. - if (auto favored = tryOptimizeGenericDisjunction(TC, DC, Choices)) { + if (auto favored = tryOptimizeGenericDisjunction(DC, Choices)) { favorConstraint(favored); } @@ -2150,7 +2226,7 @@ void ConstraintSystem::partitionDisjunction( } // Partition SIMD operators. - if (!TC.getLangOpts().SolverEnableOperatorDesignatedTypes && + if (!getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && isOperatorBindOverload(Choices[0])) { forEachChoice(Choices, [&](unsigned index, Constraint *constraint) -> bool { if (!isOperatorBindOverload(constraint)) @@ -2175,7 +2251,7 @@ void ConstraintSystem::partitionDisjunction( } }; - if (TC.getLangOpts().SolverEnableOperatorDesignatedTypes && + if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes && isOperatorBindOverload(Choices[0])) { partitionForDesignatedTypes(Choices, forEachChoice, appendPartition); } @@ -2210,7 +2286,7 @@ Constraint *ConstraintSystem::selectDisjunction() { // disjunctions that we may not be able to short-circuit, allowing // us to eliminate behavior that is exponential in the number of // operators in the expression. - if (TC.getLangOpts().SolverEnableOperatorDesignatedTypes) { + if (getASTContext().TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { if (auto *disjunction = selectApplyDisjunction()) return disjunction; } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index a3ec63db14e54..59afc3ae0d0d2 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -329,8 +329,8 @@ StepResult ComponentStep::take(bool prevFailed) { auto *disjunction = CS.selectDisjunction(); auto bestBindings = CS.determineBestBindings(); - if (bestBindings && (!disjunction || (!bestBindings->InvolvesTypeVariables && - !bestBindings->FullyBound))) { + if (bestBindings && + (!disjunction || bestBindings->favoredOverDisjunction(disjunction))) { // Produce a type variable step. return suspend( llvm::make_unique(CS, *bestBindings, Solutions)); @@ -338,62 +338,12 @@ StepResult ComponentStep::take(bool prevFailed) { // Produce a disjunction step. return suspend( llvm::make_unique(CS, disjunction, Solutions)); - } - - // If there are no disjunctions or type variables to bind - // we can't solve this system unless we have free type variables - // allowed in the solution. - if (!CS.solverState->allowsFreeTypeVariables() && CS.hasFreeTypeVariables()) { - if (!CS.shouldAttemptFixes()) - return finalize(/*isSuccess=*/false); - - // Let's see if all of the free type variables are associated with - // generic parameters and if so, let's default them to `Any` and continue - // solving so we can properly diagnose the problem later by suggesting - // to explictly specify them. - - llvm::SmallDenseMap> - defaultableGenericParams; - - for (auto *typeVar : CS.getTypeVariables()) { - if (typeVar->getImpl().hasRepresentativeOrFixed()) - continue; - - // If this free type variable is not a generic parameter - // we are done. - auto *locator = typeVar->getImpl().getLocator(); - - auto *anchor = locator->getAnchor(); - if (!(anchor && locator->isForGenericParameter())) - return finalize(/*isSuccess=*/false); - - // Increment the score for every missing generic argument - // to make ranking of the solutions with different number - // of generic arguments easier. - CS.increaseScore(ScoreKind::SK_Fix); - // Default argument to `Any`. - CS.assignFixedType(typeVar, CS.getASTContext().TheAnyType); - // Note that this generic argument has been given a default value. - CS.DefaultedConstraints.push_back(locator); - - auto path = locator->getPath(); - // Let's drop `generic parameter '...'` part of the locator to - // group all of the missing generic parameters related to the - // same path together. - defaultableGenericParams[CS.getConstraintLocator(anchor, - path.drop_back())] - .push_back(locator->getGenericParameter()); - } - - for (const auto &missing : defaultableGenericParams) { - auto *locator = missing.first; - auto &missingParams = missing.second; - auto *fix = - ExplicitlySpecifyGenericArguments::create(CS, missingParams, locator); - if (CS.recordFix(fix)) - return finalize(/*isSuccess=*/false); - } + } else if (!CS.solverState->allowsFreeTypeVariables() && + CS.hasFreeTypeVariables()) { + // If there are no disjunctions or type variables to bind + // we can't solve this system unless we have free type variables + // allowed in the solution. + return finalize(/*isSuccess=*/false); } // If this solution is worse than the best solution we've seen so far, @@ -464,13 +414,15 @@ StepResult ComponentStep::finalize(bool isSuccess) { void TypeVariableStep::setup() { ++CS.solverState->NumTypeVariablesBound; if (isDebugMode()) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; auto &log = getDebugLogger(); log << "Initial bindings: "; interleave(InitialBindings.begin(), InitialBindings.end(), [&](const Binding &binding) { - log << TypeVar->getString() - << " := " << binding.BindingType->getString(); + log << TypeVar->getString(PO) + << " := " << binding.BindingType->getString(PO); }, [&log] { log << ", "; }); @@ -565,7 +517,7 @@ bool DisjunctionStep::shouldSkip(const DisjunctionChoice &choice) const { if (!attemptFixes && choice.isUnavailable()) return true; - if (ctx.LangOpts.DisableConstraintSolverPerformanceHacks) + if (ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; // Don't attempt to solve for generic operators if we already have @@ -654,7 +606,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( if (currentChoice->getFix() && !lastSuccessfulChoice->getFix()) return true; - if (ctx.LangOpts.DisableConstraintSolverPerformanceHacks) + if (ctx.TypeCheckerOpts.DisableConstraintSolverPerformanceHacks) return false; if (auto restriction = currentChoice->getRestriction()) { @@ -682,7 +634,7 @@ bool DisjunctionStep::shortCircuitDisjunctionAt( isSIMDOperator(currentChoice->getOverloadChoice().getDecl()) && lastSuccessfulChoice->getKind() == ConstraintKind::BindOverload && !isSIMDOperator(lastSuccessfulChoice->getOverloadChoice().getDecl()) && - !ctx.LangOpts.SolverEnableOperatorDesignatedTypes) { + !ctx.TypeCheckerOpts.SolverEnableOperatorDesignatedTypes) { return true; } diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index da3374b510177..77c54766348c2 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -213,8 +213,9 @@ class SolverStep { CS.CG.addConstraint(constraint); } - ResolvedOverloadSetListItem *getResolvedOverloads() const { - return CS.resolvedOverloadSets; + const llvm::MapVector & + getResolvedOverloads() const { + return CS.ResolvedOverloads; } void recordDisjunctionChoice(ConstraintLocator *disjunctionLocator, @@ -233,7 +234,9 @@ class SolverStep { /// Check whether constraint solver is running in "debug" mode, /// which should output diagnostic information. - bool isDebugMode() const { return CS.TC.getLangOpts().DebugConstraintSolver; } + bool isDebugMode() const { + return CS.getASTContext().TypeCheckerOpts.DebugConstraintSolver; + } llvm::raw_ostream &getDebugLogger(bool indent = true) const { auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); @@ -599,7 +602,9 @@ class TypeVariableStep final : public BindingStep { StepResult resume(bool prevFailed) override; void print(llvm::raw_ostream &Out) override { - Out << "TypeVariableStep for " << TypeVar->getString() << " with #" + PrintOptions PO; + PO.PrintTypesForDebugging = true; + Out << "TypeVariableStep for " << TypeVar->getString(PO) << " with #" << InitialBindings.size() << " initial bindings\n"; } @@ -706,12 +711,12 @@ class DisjunctionStep final : public BindingStep { if (!repr || repr == typeVar) return; - for (auto *resolved = getResolvedOverloads(); resolved; - resolved = resolved->Previous) { - if (!resolved->BoundType->isEqual(repr)) + for (auto elt : getResolvedOverloads()) { + auto resolved = elt.second; + if (!resolved.boundType->isEqual(repr)) continue; - auto &representative = resolved->Choice; + auto &representative = resolved.choice; if (!representative.isDecl()) return; diff --git a/lib/Sema/CalleeCandidateInfo.cpp b/lib/Sema/CalleeCandidateInfo.cpp index 410fa5beb3deb..bb38a77300d7c 100644 --- a/lib/Sema/CalleeCandidateInfo.cpp +++ b/lib/Sema/CalleeCandidateInfo.cpp @@ -49,8 +49,7 @@ static bool isSubstitutableFor(Type type, ArchetypeType *archetype, } for (auto proto : archetype->getConformsTo()) { - if (!dc->getParentModule()->lookupConformance( - type, proto)) + if (dc->getParentModule()->lookupConformance(type, proto).isInvalid()) return false; } @@ -90,30 +89,36 @@ OverloadCandidate::OverloadCandidate(ValueDecl *decl, bool skipCurriedSelf) } } -void OverloadCandidate::dump() const { +void OverloadCandidate::dump(llvm::raw_ostream &os) const { if (auto decl = getDecl()) - decl->dumpRef(llvm::errs()); + decl->dumpRef(os); else - llvm::errs() << "<>"; - llvm::errs() << " - ignore curried self = " << (skipCurriedSelf ? "yes" - : "no"); + os << "<>"; + os << " - ignore curried self = " << (skipCurriedSelf ? "yes" : "no"); if (auto FT = getFunctionType()) - llvm::errs() << " - type: " << Type(FT) << "\n"; + os << " - type: " << Type(FT) << "\n"; else - llvm::errs() << " - type <>: " << entityType << "\n"; + os << " - type <>: " << entityType << "\n"; } -void CalleeCandidateInfo::dump() const { - llvm::errs() << "CalleeCandidateInfo for '" << declName << "': closeness=" +void CalleeCandidateInfo::dump(llvm::raw_ostream &os) const { + os << "CalleeCandidateInfo for '" << declName << "': closeness=" << unsigned(closeness) << "\n"; - llvm::errs() << candidates.size() << " candidates:\n"; + os << candidates.size() << " candidates:\n"; for (auto c : candidates) { - llvm::errs() << " "; - c.dump(); + os << " "; + c.dump(os); } } +void OverloadCandidate::dump() const { + dump(llvm::errs()); +} + +void CalleeCandidateInfo::dump() const { + dump(llvm::errs()); +} /// Given a candidate list, this computes the narrowest closeness to the match /// we're looking for and filters out any worse matches. The predicate @@ -138,7 +143,7 @@ void CalleeCandidateInfo::filterList(ClosenessPredicate predicate) { // treat it as unavailable, which is a very close failure. if (declCloseness.first == CC_ExactMatch && VD->getAttrs().isUnavailable(CS.getASTContext()) && - !CS.TC.getLangOpts().DisableAvailabilityChecking) + !CS.getASTContext().LangOpts.DisableAvailabilityChecking) declCloseness.first = CC_Unavailable; // Likewise, if the candidate is inaccessible from the scope it is being @@ -281,8 +286,9 @@ CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness( CandidateCloseness getResult() const { return result; } - void extraArgument(unsigned argIdx) override { + bool extraArgument(unsigned argIdx) override { result = CC_ArgumentCountMismatch; + return true; } Optional missingArgument(unsigned paramIdx) override { result = CC_ArgumentCountMismatch; @@ -313,12 +319,14 @@ CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness( return true; } } listener; - + // Use matchCallArguments to determine how close the argument list is (in // shape) to the specified candidates parameters. This ignores the concrete // types of the arguments, looking only at the argument labels etc. + SmallVector arguments(actualArgs.begin(), + actualArgs.end()); SmallVector paramBindings; - if (matchCallArguments(actualArgs, candArgs, + if (matchCallArguments(arguments, candArgs, candParamInfo, hasTrailingClosure, /*allowFixes:*/ true, @@ -379,7 +387,7 @@ CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness( // type is identical to the argument type, or substitutable via handling // of functions with primary archetypes in one or more parameters. // We can still do something more sophisticated with this. - // FIXME: Use TC.isConvertibleTo? + // FIXME: Use TypeChecker::isConvertibleTo? TypeSubstitutionMap archetypesMap; bool matched; @@ -594,8 +602,7 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn, auto ctors = TypeChecker::lookupConstructors( CS.DC, instanceType, NameLookupFlags::IgnoreAccessControl); for (auto ctor : ctors) { - if (ctor.getValueDecl()->getInterfaceType()) - candidates.push_back({ ctor.getValueDecl(), 1 }); + candidates.push_back({ ctor.getValueDecl(), 1 }); } } @@ -930,49 +937,11 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) { suggestionText += name; } + auto &DE = CS.getASTContext().Diags; if (sorted.size() == 1) { - CS.TC.diagnose(loc, diag::suggest_expected_match, isResult, suggestionText); + DE.diagnose(loc, diag::suggest_expected_match, isResult, suggestionText); } else { - CS.TC.diagnose(loc, diag::suggest_partial_overloads, isResult, declName, - suggestionText); - } -} - -/// Emit a diagnostic and return true if this is an error condition we can -/// handle uniformly. This should be called after filtering the candidate -/// list. -bool CalleeCandidateInfo::diagnoseSimpleErrors(const Expr *E) { - SourceLoc loc = E->getLoc(); - - // Handle symbols marked as explicitly unavailable. - if (closeness == CC_Unavailable) { - auto decl = candidates[0].getDecl(); - assert(decl && "Only decl-based candidates may be marked unavailable"); - return diagnoseExplicitUnavailability(decl, loc, CS.DC, - dyn_cast(E)); - } - - // Handle symbols that are matches, but are not accessible from the current - // scope. - if (closeness == CC_Inaccessible) { - auto decl = candidates[0].getDecl(); - assert(decl && "Only decl-based candidates may be marked inaccessible"); - - InaccessibleMemberFailure failure( - nullptr, CS, decl, CS.getConstraintLocator(const_cast(E))); - auto diagnosed = failure.diagnoseAsError(); - assert(diagnosed && "failed to produce expected diagnostic"); - - for (auto cand : candidates) { - auto *candidate = cand.getDecl(); - if (candidate && candidate != decl) - CS.TC.diagnose(candidate, diag::decl_declared_here, - candidate->getFullName()); - } - - return true; + DE.diagnose(loc, diag::suggest_partial_overloads, isResult, declName, + suggestionText); } - - return false; } - diff --git a/lib/Sema/CalleeCandidateInfo.h b/lib/Sema/CalleeCandidateInfo.h index 0a11a9c848b3c..df2c5aa4d112d 100644 --- a/lib/Sema/CalleeCandidateInfo.h +++ b/lib/Sema/CalleeCandidateInfo.h @@ -20,6 +20,8 @@ #ifndef SWIFT_SEMA_CALLEECANDIDATEINFO_H #define SWIFT_SEMA_CALLEECANDIDATEINFO_H +#include "swift/Basic/Debug.h" + namespace swift { using namespace constraints; @@ -140,7 +142,8 @@ namespace swift { return Type(); } - void dump() const LLVM_ATTRIBUTE_USED; + void dump(llvm::raw_ostream &os) const; + SWIFT_DEBUG_DUMP; }; class CalleeCandidateInfo { @@ -224,12 +227,8 @@ namespace swift { /// overloads. void suggestPotentialOverloads(SourceLoc loc, bool isResult = false); - /// Emit a diagnostic and return true if this is an error condition we can - /// handle uniformly. This should be called after filtering the candidate - /// list. - bool diagnoseSimpleErrors(const Expr *E); - - void dump() const LLVM_ATTRIBUTE_USED; + void dump(llvm::raw_ostream &os) const; + SWIFT_DEBUG_DUMP; private: void collectCalleeCandidates(Expr *fnExpr, bool implicitDotSyntax); diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index d8f6ecb18f627..85e8249f4c9bd 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -107,7 +107,6 @@ Expr *swift::buildArgumentForwardingExpr(ArrayRef params, } static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, - SmallVectorImpl &defaultInits, unsigned paramSize, ASTContext &ctx) { // First and foremost, if this is a constant don't bother. if (var->isLet()) @@ -121,9 +120,8 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, // Whether we have explicit initialization. bool isExplicitlyInitialized = false; if (auto pbd = var->getParentPatternBinding()) { - auto &entry = pbd->getPatternEntryForVarDecl(var); - isExplicitlyInitialized = - entry.isInitialized() && entry.getEqualLoc().isValid(); + const auto i = pbd->getPatternEntryIndexForVarDecl(var); + isExplicitlyInitialized = pbd->isExplicitlyInitialized(i); } // Whether we can default-initialize this property. @@ -139,13 +137,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, // We can add a default value now. - // Give this some bogus context right now, we'll fix it after making - // the constructor. - auto *initDC = new (ctx) DefaultArgumentInitializer( - arg->getDeclContext(), paramSize); - - defaultInits.push_back(initDC); - // If the variable has a type T? and no initial value, return a nil literal // default arg. All lazy variables return a nil literal as well. *Note* that // the type will always be a sugared T? because we don't default init an @@ -154,7 +145,8 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, var->getAttrs().hasAttribute() || (!isExplicitlyInitialized && isDefaultInitializable && var->getValueInterfaceType()->getAnyNominal() == ctx.getOptionalDecl() && - !var->getAttachedPropertyWrapperTypeInfo(0).defaultInit); + (var->getAttachedPropertyWrappers().empty() || + var->isPropertyMemberwiseInitializedWithWrappedType())); if (isNilInitialized) { arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral); return; @@ -241,8 +233,11 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, arg->setSpecifier(ParamSpecifier::Default); arg->setInterfaceType(varInterfaceType); arg->setImplicit(); - - maybeAddMemberwiseDefaultArg(arg, var, defaultInits, params.size(), ctx); + + // Don't allow the parameter to accept temporary pointer conversions. + arg->setNonEphemeralIfPossible(); + + maybeAddMemberwiseDefaultArg(arg, var, params.size(), ctx); params.push_back(arg); } @@ -264,11 +259,6 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { ctor->setIsMemberwiseInitializer(); - - // Fix default argument init contexts now that we have a constructor. - for (auto initDC : defaultInits) { - initDC->changeFunction(ctor, paramList); - } } // If we are defining a default initializer for a class that has a superclass, @@ -421,15 +411,13 @@ configureGenericDesignatedInitOverride(ASTContext &ctx, auto *gp = cast(type); if (gp->getDepth() < superclassDepth) return Type(gp).subst(subMap); - return CanGenericTypeParamType::get( - gp->getDepth() - superclassDepth + depth, - gp->getIndex(), - ctx); + return genericParams->getParams()[gp->getIndex()] + ->getDeclaredInterfaceType(); }; auto lookupConformanceFn = - [&](CanType depTy, Type substTy, ProtocolDecl *proto) - -> Optional { + [&](CanType depTy, Type substTy, + ProtocolDecl *proto) -> ProtocolConformanceRef { if (auto conf = subMap.lookupConformance(depTy, proto)) return conf; @@ -652,7 +640,8 @@ createDesignatedInitOverride(ClassDecl *classDecl, // Create the initializer parameter patterns. OptionSet options = (ParameterList::Implicit | - ParameterList::Inherited); + ParameterList::Inherited | + ParameterList::NamedArguments); auto *superclassParams = superclassCtor->getParameters(); auto *bodyParams = superclassParams->clone(ctx, options); @@ -686,7 +675,6 @@ createDesignatedInitOverride(ClassDecl *classDecl, // Set the interface type of the initializer. ctor->setGenericSignature(genericSig); - ctor->computeType(); ctor->setImplicitlyUnwrappedOptional( superclassCtor->isImplicitlyUnwrappedOptional()); @@ -801,9 +789,9 @@ static void diagnoseMissingRequiredInitializer( diag::required_initializer_here); } -static bool areAllStoredPropertiesDefaultInitializable(NominalTypeDecl *decl) { - if (decl->hasClangNode()) - return true; +llvm::Expected AreAllStoredPropertiesDefaultInitableRequest::evaluate( + Evaluator &evaluator, NominalTypeDecl *decl) const { + assert(!decl->hasClangNode()); for (auto member : decl->getMembers()) { // If a stored property lacks an initial value and if there is no way to @@ -811,13 +799,13 @@ static bool areAllStoredPropertiesDefaultInitializable(NominalTypeDecl *decl) { // generation of the default initializer. if (auto pbd = dyn_cast(member)) { if (pbd->hasStorage() && !pbd->isStatic()) { - for (auto entry : pbd->getPatternList()) { - if (entry.isInitialized()) continue; + for (auto idx : range(pbd->getNumPatternEntries())) { + if (pbd->isInitialized(idx)) continue; // If one of the bound variables is @NSManaged, go ahead no matter // what. bool CheckDefaultInitializer = true; - entry.getPattern()->forEachVariable([&](VarDecl *vd) { + pbd->getPattern(idx)->forEachVariable([&](VarDecl *vd) { if (vd->getAttrs().hasAttribute()) CheckDefaultInitializer = false; }); @@ -834,257 +822,230 @@ static bool areAllStoredPropertiesDefaultInitializable(NominalTypeDecl *decl) { return true; } -static void addImplicitConstructorsToStruct(StructDecl *decl, ASTContext &ctx) { - assert(!decl->hasClangNode() && - "ClangImporter is responsible for adding implicit constructors"); - assert(!decl->hasUnreferenceableStorage() && - "User-defined structs cannot have unreferenceable storage"); +static bool areAllStoredPropertiesDefaultInitializable(Evaluator &eval, + NominalTypeDecl *decl) { + if (decl->hasClangNode()) + return true; - decl->setAddedImplicitInitializers(); + return evaluateOrDefault( + eval, AreAllStoredPropertiesDefaultInitableRequest{decl}, false); +} - // Check whether there is a user-declared constructor or an instance - // variable. - bool FoundMemberwiseInitializedProperty = false; +llvm::Expected +HasUserDefinedDesignatedInitRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { + assert(!decl->hasClangNode()); - for (auto member : decl->getMembers()) { - if (auto ctor = dyn_cast(member)) { - // Initializers that were synthesized to fulfill derived conformances - // should not prevent default initializer synthesis. + for (auto *member : decl->getMembers()) + if (auto *ctor = dyn_cast(member)) if (ctor->isDesignatedInit() && !ctor->isSynthesized()) - return; - } - - if (auto var = dyn_cast(member)) { - // If this is a backing storage property for a property wrapper, - // skip it. - if (var->getOriginalWrappedProperty()) - continue; - - if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) { - // Initialized 'let' properties have storage, but don't get an argument - // to the memberwise initializer since they already have an initial - // value that cannot be overridden. - if (var->isLet() && var->isParentInitialized()) { - // We cannot handle properties like: - // let (a,b) = (1,2) - // for now, just disable implicit init synthesization in structs in - // this case. - auto SP = var->getParentPattern(); - if (auto *TP = dyn_cast(SP)) - SP = TP->getSubPattern(); - if (!isa(SP)) - return; - - continue; - } - - FoundMemberwiseInitializedProperty = true; - } - } - } + return true; + return false; +} - if (FoundMemberwiseInitializedProperty) { - // Create the implicit memberwise constructor. - auto ctor = createImplicitConstructor( - decl, ImplicitConstructorKind::Memberwise, ctx); - decl->addMember(ctor); - } +static bool hasUserDefinedDesignatedInit(Evaluator &eval, + NominalTypeDecl *decl) { + // Imported decls don't have a designated initializer defined by the user. + if (decl->hasClangNode()) + return false; - if (areAllStoredPropertiesDefaultInitializable(decl)) - TypeChecker::defineDefaultConstructor(decl); + return evaluateOrDefault(eval, HasUserDefinedDesignatedInitRequest{decl}, + false); } -static void addImplicitConstructorsToClass(ClassDecl *decl, ASTContext &ctx) { - // Bail out if we're validating one of our constructors already; - // we'll revisit the issue later. - if (!decl->hasClangNode()) { - for (auto member : decl->getMembers()) { - if (auto ctor = dyn_cast(member)) { - if (ctor->isRecursiveValidation()) - return; - } - } - } - - decl->setAddedImplicitInitializers(); +static bool canInheritDesignatedInits(Evaluator &eval, ClassDecl *decl) { + // We can only inherit designated initializers if the user hasn't defined + // a designated init of their own, and all the stored properties have initial + // values. + return !hasUserDefinedDesignatedInit(eval, decl) && + areAllStoredPropertiesDefaultInitializable(eval, decl); +} - // Check whether there is a user-declared constructor or an instance - // variable. - bool FoundDesignatedInit = false; +static void collectNonOveriddenSuperclassInits( + ClassDecl *subclass, SmallVectorImpl &results) { + auto superclassTy = subclass->getSuperclass(); + assert(superclassTy); - SmallVector, 4> declaredInitializers; + // Record all of the initializers the subclass has overriden, excluding stub + // overrides, which we don't want to consider as viable delegates for + // convenience inits. llvm::SmallPtrSet overriddenInits; - if (decl->hasClangNode()) { - // Objective-C classes may have interesting initializers in extensions. - for (auto member : decl->lookupDirect(DeclBaseName::createConstructor())) { - auto ctor = dyn_cast(member); - if (!ctor) - continue; - - // Swift initializers added in extensions of Objective-C classes can never - // be overrides. - if (!ctor->hasClangNode()) - continue; - - if (auto overridden = ctor->getOverriddenDecl()) - overriddenInits.insert(overridden); - } - - } else { - for (auto member : decl->getMembers()) { - if (auto ctor = dyn_cast(member)) { - // Initializers that were synthesized to fulfill derived conformances - // should not prevent default initializer synthesis. - if (ctor->isDesignatedInit() && !ctor->isSynthesized()) - FoundDesignatedInit = true; - - if (!ctor->isInvalid()) { - auto type = getMemberTypeForComparison(ctx, ctor, nullptr); - declaredInitializers.push_back({ctor, type}); - } - + for (auto member : subclass->getMembers()) + if (auto ctor = dyn_cast(member)) + if (!ctor->hasStubImplementation()) if (auto overridden = ctor->getOverriddenDecl()) overriddenInits.insert(overridden); - continue; - } - } - } + auto superclassCtors = TypeChecker::lookupConstructors( + subclass, superclassTy, NameLookupFlags::IgnoreAccessControl); - bool SuppressDefaultInitializer = - !areAllStoredPropertiesDefaultInitializable(decl); + for (auto memberResult : superclassCtors) { + auto superclassCtor = cast(memberResult.getValueDecl()); - // For a class with a superclass, automatically define overrides - // for all of the superclass's designated initializers. - if (Type superclassTy = decl->getSuperclass()) { - bool canInheritInitializers = (!SuppressDefaultInitializer && - !FoundDesignatedInit); + // Skip invalid superclass initializers. + if (superclassCtor->isInvalid()) + continue; - // We can't define these overrides if we have any uninitialized - // stored properties. - if (SuppressDefaultInitializer && !FoundDesignatedInit && - !decl->hasClangNode()) { - return; - } + // Skip unavailable superclass initializers. + if (AvailableAttr::isUnavailable(superclassCtor)) + continue; - auto *superclassDecl = superclassTy->getClassOrBoundGenericClass(); - assert(superclassDecl && "Superclass of class is not a class?"); - if (!superclassDecl->addedImplicitInitializers()) - ctx.getLazyResolver()->resolveImplicitConstructors(superclassDecl); + if (!overriddenInits.count(superclassCtor)) + results.push_back(superclassCtor); + } +} - auto ctors = TypeChecker::lookupConstructors( - decl, superclassTy, - NameLookupFlags::IgnoreAccessControl); +/// For a class with a superclass, automatically define overrides +/// for all of the superclass's designated initializers. +static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { + // Bail out if we're validating one of our constructors already; + // we'll revisit the issue later. + for (auto member : decl->getMembers()) { + if (auto ctor = dyn_cast(member)) { + if (ctor->isRecursiveValidation()) + return; + } + } - bool canInheritConvenienceInitalizers = - !superclassDecl->hasMissingDesignatedInitializers(); - SmallVector requiredConvenienceInitializers; - for (auto memberResult : ctors) { - auto member = memberResult.getValueDecl(); + decl->setAddedImplicitInitializers(); - // Skip unavailable superclass initializers. - if (AvailableAttr::isUnavailable(member)) - continue; + // We can only inherit initializers if we have a superclass. + // FIXME: We should be bailing out earlier in the function, but unfortunately + // that currently regresses associated type inference for cases like + // compiler_crashers_2_fixed/0124-sr5825.swift due to the fact that we no + // longer eagerly compute the interface types of the other constructors. + auto superclassTy = decl->getSuperclass(); + if (!superclassTy) + return; - // Skip invalid superclass initializers. - auto superclassCtor = dyn_cast(member); - if (superclassCtor->isInvalid()) - continue; + // Check whether the user has defined a designated initializer for this class, + // and whether all of its stored properties have initial values. + auto &ctx = decl->getASTContext(); + bool foundDesignatedInit = hasUserDefinedDesignatedInit(ctx.evaluator, decl); + bool defaultInitable = + areAllStoredPropertiesDefaultInitializable(ctx.evaluator, decl); - // If we have an override for this constructor, it's okay. - if (overriddenInits.count(superclassCtor) > 0) - continue; + // We can't define these overrides if we have any uninitialized + // stored properties. + if (!defaultInitable && !foundDesignatedInit) + return; - // We only care about required or designated initializers. - if (!superclassCtor->isDesignatedInit()) { - if (superclassCtor->isRequired()) { - assert(superclassCtor->isInheritable() && - "factory initializers cannot be 'required'"); - requiredConvenienceInitializers.push_back(superclassCtor); - } - continue; + SmallVector nonOverridenSuperclassCtors; + collectNonOveriddenSuperclassInits(decl, nonOverridenSuperclassCtors); + + bool inheritDesignatedInits = canInheritDesignatedInits(ctx.evaluator, decl); + for (auto *superclassCtor : nonOverridenSuperclassCtors) { + // We only care about required or designated initializers. + if (!superclassCtor->isDesignatedInit()) { + if (superclassCtor->isRequired()) { + assert(superclassCtor->isInheritable() && + "factory initializers cannot be 'required'"); + if (!decl->inheritsSuperclassInitializers()) + diagnoseMissingRequiredInitializer(decl, superclassCtor, ctx); } + continue; + } - // Otherwise, it may no longer be safe to inherit convenience - // initializers. - canInheritConvenienceInitalizers &= canInheritInitializers; - - // Everything after this is only relevant for Swift classes being defined. - if (decl->hasClangNode()) - continue; + // If the superclass initializer is not accessible from the derived + // class, don't synthesize an override, since we cannot reference the + // superclass initializer's method descriptor at all. + // + // FIXME: This should be checked earlier as part of calculating + // canInheritInitializers. + if (!superclassCtor->isAccessibleFrom(decl)) + continue; - // If the superclass initializer is not accessible from the derived - // class, don't synthesize an override, since we cannot reference the - // superclass initializer's method descriptor at all. - // - // FIXME: This should be checked earlier as part of calculating - // canInheritInitializers. - if (!superclassCtor->isAccessibleFrom(decl)) - continue; + // Diagnose a missing override of a required initializer. + if (superclassCtor->isRequired() && !inheritDesignatedInits) { + diagnoseMissingRequiredInitializer(decl, superclassCtor, ctx); + continue; + } - // Diagnose a missing override of a required initializer. - if (superclassCtor->isRequired() && !canInheritInitializers) { - diagnoseMissingRequiredInitializer(decl, superclassCtor, ctx); - continue; - } + // A designated or required initializer has not been overridden. - // A designated or required initializer has not been overridden. + bool alreadyDeclared = false; + for (auto *member : decl->getMembers()) { + if (auto ctor = dyn_cast(member)) { + // Skip any invalid constructors. + if (ctor->isInvalid()) + continue; - bool alreadyDeclared = false; - for (const auto &ctorAndType : declaredInitializers) { - auto *ctor = ctorAndType.first; - auto type = ctorAndType.second; - auto parentType = getMemberTypeForComparison( - ctx, superclassCtor, ctor); + auto type = swift::getMemberTypeForComparison(ctor, nullptr); + auto parentType = swift::getMemberTypeForComparison(superclassCtor, ctor); if (isOverrideBasedOnType(ctor, type, superclassCtor, parentType)) { alreadyDeclared = true; break; } } + } - // If we have already introduced an initializer with this parameter type, - // don't add one now. - if (alreadyDeclared) - continue; + // If we have already introduced an initializer with this parameter type, + // don't add one now. + if (alreadyDeclared) + continue; - // If we're inheriting initializers, create an override delegating - // to 'super.init'. Otherwise, create a stub which traps at runtime. - auto kind = canInheritInitializers - ? DesignatedInitKind::Chaining - : DesignatedInitKind::Stub; + // If we're inheriting initializers, create an override delegating + // to 'super.init'. Otherwise, create a stub which traps at runtime. + auto kind = inheritDesignatedInits ? DesignatedInitKind::Chaining + : DesignatedInitKind::Stub; - if (auto ctor = createDesignatedInitOverride( - decl, superclassCtor, kind, ctx)) { - decl->addMember(ctor); - } + if (auto ctor = createDesignatedInitOverride( + decl, superclassCtor, kind, ctx)) { + decl->addMember(ctor); } + } +} - if (canInheritConvenienceInitalizers) { - decl->setInheritsSuperclassInitializers(); - } else { - for (ConstructorDecl *requiredCtor : requiredConvenienceInitializers) - diagnoseMissingRequiredInitializer(decl, requiredCtor, ctx); - } +llvm::Expected +InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, + ClassDecl *decl) const { + // Check if we parsed the @_inheritsConvenienceInitializers attribute. + if (decl->getAttrs().hasAttribute()) + return true; - return; - } + auto superclass = decl->getSuperclass(); + assert(superclass); - if (!FoundDesignatedInit) { - // For a class with no superclass, automatically define a default - // constructor. + // If the superclass has known-missing designated initializers, inheriting + // is unsafe. + auto *superclassDecl = superclass->getClassOrBoundGenericClass(); + if (superclassDecl->getModuleContext() != decl->getParentModule() && + superclassDecl->hasMissingDesignatedInitializers()) + return false; - // ... unless there are uninitialized stored properties. - if (SuppressDefaultInitializer) - return; + // If we're allowed to inherit designated initializers, then we can inherit + // convenience inits too. + if (canInheritDesignatedInits(eval, decl)) + return true; - // Clang-imported types should never get a default constructor, just a - // memberwise one. - if (decl->hasClangNode()) - return; + // Otherwise we need to check whether the user has overriden all of the + // superclass' designed inits. + SmallVector nonOverridenSuperclassCtors; + collectNonOveriddenSuperclassInits(decl, nonOverridenSuperclassCtors); - TypeChecker::defineDefaultConstructor(decl); - } + auto allDesignatedInitsOverriden = + llvm::none_of(nonOverridenSuperclassCtors, [](ConstructorDecl *ctor) { + return ctor->isDesignatedInit(); + }); + return allDesignatedInitsOverriden; +} + +static bool shouldAttemptInitializerSynthesis(const NominalTypeDecl *decl) { + // Don't synthesize initializers for imported decls. + if (decl->hasClangNode()) + return false; + + // Don't add implicit constructors in module interfaces. + if (auto *SF = decl->getParentSourceFile()) + if (SF->Kind == SourceFileKind::Interface) + return false; + + // Don't attempt if we know the decl is invalid. + if (decl->isInvalid()) + return false; + + return true; } void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { @@ -1092,27 +1053,26 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { if (decl->addedImplicitInitializers()) return; - // Don't add implicit constructors for an invalid declaration - if (decl->isInvalid()) + if (!shouldAttemptInitializerSynthesis(decl)) { + decl->setAddedImplicitInitializers(); return; - - // Don't add implicit constructors in module interfaces. - if (auto *SF = decl->getParentSourceFile()) { - if (SF->Kind == SourceFileKind::Interface) { - decl->setAddedImplicitInitializers(); - return; - } } - if (auto *structDecl = dyn_cast(decl)) - addImplicitConstructorsToStruct(structDecl, Context); if (auto *classDecl = dyn_cast(decl)) - addImplicitConstructorsToClass(classDecl, Context); + addImplicitInheritedConstructorsToClass(classDecl); + + // Force the memberwise and default initializers if the type has them. + // FIXME: We need to be more lazy about synthesizing constructors. + (void)decl->getMemberwiseInitializer(); + (void)decl->getDefaultInitializer(); } -void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, - DeclName member) { - auto baseName = member.getBaseName(); +llvm::Expected +ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *target, + ImplicitMemberAction action) const { + // FIXME: This entire request is a layering violation made of smaller, + // finickier layering violations. See rdar://56844567 // Checks whether the target conforms to the given protocol. If the // conformance is incomplete, force the conformance. @@ -1123,83 +1083,134 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target, return false; auto targetType = target->getDeclaredInterfaceType(); - if (auto ref = conformsToProtocol( - targetType, protocol, target, - ConformanceCheckFlags::SkipConditionalRequirements)) { - if (auto *conformance = dyn_cast( - ref->getConcrete()->getRootConformance())) { - if (conformance->getState() == ProtocolConformanceState::Incomplete) { - checkConformance(conformance); - } - } + auto ref = TypeChecker::conformsToProtocol( + targetType, protocol, target, + ConformanceCheckFlags::SkipConditionalRequirements); - return true; + if (ref.isInvalid()) { + return false; } - return false; + if (auto *conformance = dyn_cast( + ref.getConcrete()->getRootConformance())) { + if (conformance->getState() == ProtocolConformanceState::Incomplete) { + TypeChecker::checkConformance(conformance); + } + } + + return true; }; - if (member.isSimpleName() && !baseName.isSpecial()) { - if (baseName.getIdentifier() == Context.Id_CodingKeys) { - // CodingKeys is a special type which may be synthesized as part of - // Encodable/Decodable conformance. If the target conforms to either - // protocol and would derive conformance to either, the type may be - // synthesized. - // If the target conforms to either and the conformance has not yet been - // evaluated, then we should do that here. - // - // Try to synthesize Decodable first. If that fails, try to synthesize - // Encodable. If either succeeds and CodingKeys should have been - // synthesized, it will be synthesized. - auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable); - auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable); - if (!evaluateTargetConformanceTo(decodableProto)) - (void)evaluateTargetConformanceTo(encodableProto); + auto &Context = target->getASTContext(); + switch (action) { + case ImplicitMemberAction::ResolveImplicitInit: + TypeChecker::addImplicitConstructors(target); + break; + case ImplicitMemberAction::ResolveCodingKeys: { + // CodingKeys is a special type which may be synthesized as part of + // Encodable/Decodable conformance. If the target conforms to either + // protocol and would derive conformance to either, the type may be + // synthesized. + // If the target conforms to either and the conformance has not yet been + // evaluated, then we should do that here. + // + // Try to synthesize Decodable first. If that fails, try to synthesize + // Encodable. If either succeeds and CodingKeys should have been + // synthesized, it will be synthesized. + auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable); + auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable); + if (!evaluateTargetConformanceTo(decodableProto)) { + (void)evaluateTargetConformanceTo(encodableProto); } + } + break; + case ImplicitMemberAction::ResolveEncodable: { + // encode(to:) may be synthesized as part of derived conformance to the + // Encodable protocol. + // If the target should conform to the Encodable protocol, check the + // conformance here to attempt synthesis. + auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable); + (void)evaluateTargetConformanceTo(encodableProto); + } + break; + case ImplicitMemberAction::ResolveDecodable: { + // init(from:) may be synthesized as part of derived conformance to the + // Decodable protocol. + // If the target should conform to the Decodable protocol, check the + // conformance here to attempt synthesis. + TypeChecker::addImplicitConstructors(target); + auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable); + (void)evaluateTargetConformanceTo(decodableProto); + } + break; + } + return true; +} - if ((baseName.getIdentifier().str().startswith("$") || - baseName.getIdentifier().str().startswith("_")) && - baseName.getIdentifier().str().size() > 1) { - // $- and _-prefixed variables can be generated by properties that have - // attached property wrappers. - auto originalPropertyName = - Context.getIdentifier(baseName.getIdentifier().str().substr(1)); - for (auto member : target->lookupDirect(originalPropertyName)) { - if (auto var = dyn_cast(member)) { - if (var->hasAttachedPropertyWrapper()) { - auto sourceFile = var->getDeclContext()->getParentSourceFile(); - if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) - (void)var->getPropertyWrapperBackingPropertyInfo(); - } - } - } - } +llvm::Expected +HasMemberwiseInitRequest::evaluate(Evaluator &evaluator, + StructDecl *decl) const { + if (!shouldAttemptInitializerSynthesis(decl)) + return false; - } else { - auto argumentNames = member.getArgumentNames(); - if (member.isCompoundName() && argumentNames.size() != 1) - return; - - if (baseName == DeclBaseName::createConstructor() && - (member.isSimpleName() || argumentNames.front() == Context.Id_from)) { - // init(from:) may be synthesized as part of derived conformance to the - // Decodable protocol. - // If the target should conform to the Decodable protocol, check the - // conformance here to attempt synthesis. - auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable); - (void)evaluateTargetConformanceTo(decodableProto); - } else if (!baseName.isSpecial() && - baseName.getIdentifier() == Context.Id_encode && - (member.isSimpleName() || - argumentNames.front() == Context.Id_to)) { - // encode(to:) may be synthesized as part of derived conformance to the - // Encodable protocol. - // If the target should conform to the Encodable protocol, check the - // conformance here to attempt synthesis. - auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable); - (void)evaluateTargetConformanceTo(encodableProto); + // If the user has already defined a designated initializer, then don't + // synthesize a memberwise init. + if (hasUserDefinedDesignatedInit(evaluator, decl)) + return false; + + for (auto *member : decl->getMembers()) { + if (auto *var = dyn_cast(member)) { + // If this is a backing storage property for a property wrapper, + // skip it. + if (var->getOriginalWrappedProperty()) + continue; + + if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) + return true; } } + return false; +} + +llvm::Expected +SynthesizeMemberwiseInitRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { + // Create the implicit memberwise constructor. + auto &ctx = decl->getASTContext(); + auto ctor = + createImplicitConstructor(decl, ImplicitConstructorKind::Memberwise, ctx); + decl->addMember(ctor); + return ctor; +} + +llvm::Expected +HasDefaultInitRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { + assert(isa(decl) || isa(decl)); + + if (!shouldAttemptInitializerSynthesis(decl)) + return false; + + if (auto *sd = dyn_cast(decl)) { + assert(!sd->hasUnreferenceableStorage() && + "User-defined structs cannot have unreferenceable storage"); + (void)sd; + } + + // Don't synthesize a default for a subclass, it will attempt to inherit its + // initializers from its superclass. + if (auto *cd = dyn_cast(decl)) + if (cd->getSuperclass()) + return false; + + // If the user has already defined a designated initializer, then don't + // synthesize a default init. + if (hasUserDefinedDesignatedInit(evaluator, decl)) + return false; + + // We can only synthesize a default init if all the stored properties have an + // initial value. + return areAllStoredPropertiesDefaultInitializable(evaluator, decl); } /// Synthesizer callback for a function body consisting of "return". @@ -1212,7 +1223,9 @@ synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) { /*isTypeChecked=*/true }; } -void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) { +llvm::Expected +SynthesizeDefaultInitRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { auto &ctx = decl->getASTContext(); FrontendStatsTracer StatsTracer(ctx.Stats, "define-default-ctor", decl); @@ -1229,4 +1242,5 @@ void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) { // Lazily synthesize an empty body for the default constructor. ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody); + return ctor; } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index a4a8098dc978f..0b3ebb8e1a7cd 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -21,7 +21,6 @@ #include "swift/Basic/Compiler.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/SaveAndRestore.h" #include using namespace swift; @@ -78,9 +77,11 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: llvm_unreachable("Wrong constructor for member constraint"); case ConstraintKind::Defaultable: + case ConstraintKind::DefaultClosureType: assert(!First.isNull()); assert(!Second.isNull()); break; @@ -128,6 +129,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::ApplicableFunction: case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::Defaultable: case ConstraintKind::BindOverload: @@ -136,6 +138,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: llvm_unreachable("Wrong constructor"); case ConstraintKind::KeyPath: @@ -151,13 +154,13 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, } Constraint::Constraint(ConstraintKind kind, Type first, Type second, - DeclName member, DeclContext *useDC, + DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ConstraintLocator *locator, ArrayRef typeVars) : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), RememberChoice(false), IsFavored(false), - NumTypeVariables(typeVars.size()), Member{first, second, member, useDC}, + NumTypeVariables(typeVars.size()), Member{first, second, {member}, useDC}, Locator(locator) { assert(kind == ConstraintKind::ValueMember || kind == ConstraintKind::UnresolvedValueMember); @@ -169,6 +172,28 @@ Constraint::Constraint(ConstraintKind kind, Type first, Type second, std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); } +Constraint::Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, + ConstraintLocator *locator, + ArrayRef typeVars) + : Kind(kind), HasRestriction(false), IsActive(false), IsDisabled(false), + RememberChoice(false), IsFavored(false), + NumTypeVariables(typeVars.size()), Locator(locator) { + Member.First = first; + Member.Second = second; + Member.Member.Ref = requirement; + Member.UseDC = useDC; + TheFunctionRefKind = static_cast(functionRefKind); + + assert(kind == ConstraintKind::ValueWitness); + assert(getFunctionRefKind() == functionRefKind); + assert(requirement && "Value witness constraint has no requirement"); + assert(useDC && "Member constraint has no use DC"); + + std::copy(typeVars.begin(), typeVars.end(), getTypeVariablesBuffer().begin()); +} + Constraint::Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, ArrayRef typeVars) @@ -240,6 +265,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: return create(cs, getKind(), getFirstType(), getSecondType(), getLocator()); case ConstraintKind::BindOverload: @@ -252,6 +278,11 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { getMember(), getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::ValueWitness: + return createValueWitness( + cs, getKind(), getFirstType(), getSecondType(), getRequirement(), + getMemberUseDC(), getFunctionRefKind(), getLocator()); + case ConstraintKind::Disjunction: return createDisjunction(cs, getNestedConstraints(), getLocator()); @@ -265,6 +296,10 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { } void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { + // Print all type variables as $T0 instead of _ here. + PrintOptions PO; + PO.PrintTypesForDebugging = true; + if (Kind == ConstraintKind::Disjunction) { Out << "disjunction"; if (shouldRememberChoice()) @@ -286,7 +321,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { return; } - getFirstType()->print(Out); + Out << getFirstType()->getString(PO); bool skipSecond = false; @@ -313,19 +348,22 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { case ConstraintKind::EscapableFunctionOf: Out << " @escaping type of "; break; case ConstraintKind::OpenedExistentialOf: Out << " opened archetype of "; break; case ConstraintKind::OneWayEqual: Out << " one-way bind to "; break; + case ConstraintKind::DefaultClosureType: + Out << " closure can default to "; + break; case ConstraintKind::KeyPath: Out << " key path from "; - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); Out << " -> "; - getThirdType()->print(Out); + Out << getThirdType()->getString(PO); skipSecond = true; break; case ConstraintKind::KeyPathApplication: Out << " key path projecting "; - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); Out << " -> "; - getThirdType()->print(Out); + Out << getThirdType()->getString(PO); skipSecond = true; break; case ConstraintKind::OptionalObject: @@ -383,11 +421,20 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } case ConstraintKind::ValueMember: - Out << "[." << Member.Member << ": value] == "; + Out << "[." << getMember() << ": value] == "; break; case ConstraintKind::UnresolvedValueMember: - Out << "[(implicit) ." << Member.Member << ": value] == "; + Out << "[(implicit) ." << getMember() << ": value] == "; break; + + case ConstraintKind::ValueWitness: { + auto requirement = getRequirement(); + auto selfNominal = requirement->getDeclContext()->getSelfNominalTypeDecl(); + Out << "[." << selfNominal->getName() << "::" << requirement->getFullName() + << ": witness] == "; + break; + } + case ConstraintKind::Defaultable: Out << " can default to "; break; @@ -396,7 +443,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } if (!skipSecond) - getSecondType()->print(Out); + Out << getSecondType()->getString(PO); if (auto restriction = getRestriction()) { Out << ' ' << getName(*restriction); @@ -420,9 +467,6 @@ void Constraint::dump(SourceManager *sm) const { } void Constraint::dump(ConstraintSystem *CS) const { - // Print all type variables as $T0 instead of _ here. - llvm::SaveAndRestore X(CS->getASTContext().LangOpts. - DebugConstraintSolver, true); // Disable MSVC warning: only for use within the debugger. #if SWIFT_COMPILER_IS_MSVC #pragma warning(push) @@ -510,6 +554,7 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::Subtype: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: + case ConstraintKind::ValueWitness: case ConstraintKind::DynamicTypeOf: case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: @@ -522,6 +567,7 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: constraint->getFirstType()->getTypeVariables(typeVars); constraint->getSecondType()->getTypeVariables(typeVars); break; @@ -562,13 +608,16 @@ bool Constraint::isExplicitConversion() const { Constraint *Constraint::create(ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, - ConstraintLocator *locator) { + ConstraintLocator *locator, + ArrayRef extraTypeVars) { // Collect type variables. SmallVector typeVars; if (first->hasTypeVariable()) first->getTypeVariables(typeVars); if (second && second->hasTypeVariable()) second->getTypeVariables(typeVars); + + typeVars.append(extraTypeVars.begin(), extraTypeVars.end()); uniqueTypeVariables(typeVars); // Conformance constraints expect an existential on the right-hand side. @@ -610,7 +659,7 @@ Constraint *Constraint::create(ConstraintSystem &cs, ConstraintKind kind, Constraint *Constraint::createMemberOrOuterDisjunction( ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, - DeclName member, DeclContext *useDC, FunctionRefKind functionRefKind, + DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ArrayRef outerAlternatives, ConstraintLocator *locator) { auto memberConstraint = createMember(cs, kind, first, second, member, useDC, functionRefKind, locator); @@ -629,8 +678,8 @@ Constraint *Constraint::createMemberOrOuterDisjunction( } Constraint *Constraint::createMember(ConstraintSystem &cs, ConstraintKind kind, - Type first, Type second, DeclName member, - DeclContext *useDC, + Type first, Type second, + DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ConstraintLocator *locator) { // Collect type variables. @@ -648,6 +697,27 @@ Constraint *Constraint::createMember(ConstraintSystem &cs, ConstraintKind kind, functionRefKind, locator, typeVars); } +Constraint *Constraint::createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator) { + assert(kind == ConstraintKind::ValueWitness); + + // Collect type variables. + SmallVector typeVars; + if (first->hasTypeVariable()) + first->getTypeVariables(typeVars); + if (second->hasTypeVariable()) + second->getTypeVariables(typeVars); + uniqueTypeVariables(typeVars); + + // Create the constraint. + unsigned size = totalSizeToAlloc(typeVars.size()); + void *mem = cs.getAllocator().Allocate(size, alignof(Constraint)); + return new (mem) Constraint(kind, first, second, requirement, useDC, + functionRefKind, locator, typeVars); +} + Constraint *Constraint::createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, DeclContext *useDC, diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 9fc9c2dac73c5..23e615fed081d 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -23,6 +23,7 @@ #include "swift/AST/FunctionRefKind.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" @@ -115,6 +116,11 @@ enum class ConstraintKind : char { /// name, and the type of that member, when referenced as a value, is the /// second type. UnresolvedValueMember, + /// The first type conforms to the protocol in which the member requirement + /// resides. Once the conformance is resolved, the value witness will be + /// determined, and the type of that witness, when referenced as a value, + /// will be bound to the second type. + ValueWitness, /// The first type can be defaulted to the second (which currently /// cannot be dependent). This is more like a type property than a /// relational constraint. @@ -155,6 +161,15 @@ enum class ConstraintKind : char { /// type). At that point, this constraint will be treated like an `Equal` /// constraint. OneWayEqual, + /// If there is no contextual info e.g. `_ = { 42 }` default first type + /// to a second type (inferred closure type). This is effectively a + /// `Defaultable` constraint which a couple of differences: + /// + /// - References inferred closure type and all of the outer parameters + /// referenced by closure body. + /// - Handled specially by binding inference, specifically contributes + /// to the bindings only if there are no contextual types available. + DefaultClosureType, }; /// Classification of the different kinds of constraints. @@ -228,6 +243,22 @@ enum class ConversionRestrictionKind { ObjCTollFreeBridgeToCF, }; +/// Specifies whether a given conversion requires the creation of a temporary +/// value which is only valid for a limited scope. For example, the +/// array-to-pointer conversion produces a pointer that is only valid for the +/// duration of the call that it's passed to. Such ephemeral conversions cannot +/// be passed to non-ephemeral parameters. +enum class ConversionEphemeralness { + /// The conversion requires the creation of a temporary value. + Ephemeral, + /// The conversion does not require the creation of a temporary value. + NonEphemeral, + /// It is not currently known whether the conversion will produce a temporary + /// value or not. This can occur for example with an inout-to-pointer + /// conversion of a member whose base type is an unresolved type variable. + Unresolved, +}; + /// Return a string representation of a conversion restriction. llvm::StringRef getName(ConversionRestrictionKind kind); @@ -298,9 +329,18 @@ class Constraint final : public llvm::ilist_node, /// The type of the member. Type Second; - /// If non-null, the name of a member of the first type is that - /// being related to the second type. - DeclName Member; + union { + /// If non-null, the name of a member of the first type is that + /// being related to the second type. + /// + /// Used for ValueMember an UnresolvedValueMember constraints. + DeclNameRef Name; + + /// If non-null, the member being referenced. + /// + /// Used for ValueWitness constraints. + ValueDecl *Ref; + } Member; /// The DC in which the use appears. DeclContext *UseDC; @@ -343,11 +383,17 @@ class Constraint final : public llvm::ilist_node, ArrayRef typeVars); /// Construct a new member constraint. - Constraint(ConstraintKind kind, Type first, Type second, DeclName member, + Constraint(ConstraintKind kind, Type first, Type second, DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ConstraintLocator *locator, ArrayRef typeVars); + /// Construct a new value witness constraint. + Constraint(ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator, + ArrayRef typeVars); + /// Construct a new overload-binding constraint, which might have a fix. Constraint(Type type, OverloadChoice choice, DeclContext *useDC, ConstraintFix *fix, ConstraintLocator *locator, @@ -369,9 +415,9 @@ class Constraint final : public llvm::ilist_node, public: /// Create a new constraint. - static Constraint *create(ConstraintSystem &cs, ConstraintKind Kind, - Type First, Type Second, - ConstraintLocator *locator); + static Constraint *create(ConstraintSystem &cs, ConstraintKind Kind, + Type First, Type Second, ConstraintLocator *locator, + ArrayRef extraTypeVars = {}); /// Create a new constraint. static Constraint *create(ConstraintSystem &cs, ConstraintKind Kind, @@ -383,16 +429,22 @@ class Constraint final : public llvm::ilist_node, /// alternatives. static Constraint *createMemberOrOuterDisjunction( ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, - DeclName member, DeclContext *useDC, FunctionRefKind functionRefKind, + DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ArrayRef outerAlternatives, ConstraintLocator *locator); /// Create a new member constraint. static Constraint *createMember(ConstraintSystem &cs, ConstraintKind kind, - Type first, Type second, DeclName member, + Type first, Type second, DeclNameRef member, DeclContext *useDC, FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create a new value witness constraint. + static Constraint *createValueWitness( + ConstraintSystem &cs, ConstraintKind kind, Type first, Type second, + ValueDecl *requirement, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocator *locator); + /// Create an overload-binding constraint. static Constraint *createBindOverload(ConstraintSystem &cs, Type type, OverloadChoice choice, @@ -497,10 +549,12 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::OptionalObject: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::DefaultClosureType: return ConstraintClassification::Relational; case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return ConstraintClassification::Member; case ConstraintKind::DynamicTypeOf: @@ -531,6 +585,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.First; default: @@ -547,6 +602,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: + case ConstraintKind::ValueWitness: return Member.Second; default: @@ -569,22 +625,23 @@ class Constraint final : public llvm::ilist_node, ProtocolDecl *getProtocol() const; /// Retrieve the name of the member for a member constraint. - DeclName getMember() const { + DeclNameRef getMember() const { assert(Kind == ConstraintKind::ValueMember || Kind == ConstraintKind::UnresolvedValueMember); - return Member.Member; + return Member.Member.Name; } - /// Determine whether this constraint kind has a second type. - static bool hasMember(ConstraintKind kind) { - return kind == ConstraintKind::ValueMember - || kind == ConstraintKind::UnresolvedValueMember; + /// Retrieve the requirement being referenced by a value witness constraint. + ValueDecl *getRequirement() const { + assert(Kind == ConstraintKind::ValueWitness); + return Member.Member.Ref; } /// Determine the kind of function reference we have for a member reference. FunctionRefKind getFunctionRefKind() const { if (Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember) + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness) return static_cast(TheFunctionRefKind); // Conservative answer: drop all of the labels. @@ -630,7 +687,8 @@ class Constraint final : public llvm::ilist_node, /// Retrieve the DC in which the member was used. DeclContext *getMemberUseDC() const { assert(Kind == ConstraintKind::ValueMember || - Kind == ConstraintKind::UnresolvedValueMember); + Kind == ConstraintKind::UnresolvedValueMember || + Kind == ConstraintKind::ValueWitness); return Member.UseDC; } @@ -642,13 +700,9 @@ class Constraint final : public llvm::ilist_node, void print(llvm::raw_ostream &Out, SourceManager *sm) const; - LLVM_ATTRIBUTE_DEPRECATED( - void dump(SourceManager *SM) const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMPER(dump(SourceManager *SM)); - LLVM_ATTRIBUTE_DEPRECATED( - void dump(ConstraintSystem *CS) const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMPER(dump(ConstraintSystem *CS)); void *operator new(size_t bytes, ConstraintSystem& cs, size_t alignment = alignof(Constraint)); diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index 1b4134ca3ed4c..fca49c43e1212 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -972,7 +972,7 @@ namespace { contractedCycle = false; for (const auto &edge : cycleEdges) { if (unionSets(edge.first, edge.second)) { - if (ctx.LangOpts.DebugConstraintSolver) { + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { auto &log = ctx.TypeCheckerDebug->getStream(); if (cs.solverState) log.indent(cs.solverState->depth * 2); @@ -1255,7 +1255,7 @@ bool ConstraintGraph::contractEdges() { rep2->getImpl().canBindToLValue()) || // Allow l-value contractions when binding parameter types. isParamBindingConstraint)) { - if (CS.TC.getLangOpts().DebugConstraintSolver) { + if (CS.getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = CS.getASTContext().TypeCheckerDebug->getStream(); if (CS.solverState) log.indent(CS.solverState->depth * 2); @@ -1319,9 +1319,10 @@ void ConstraintGraph::incrementConstraintsPerContractionCounter() { #pragma mark Debugging output -void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) { +void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent, + PrintOptions PO) const { out.indent(indent); - TypeVar->print(out); + Type(TypeVar).print(out, PO); out << ":\n"; // Print constraints. @@ -1349,9 +1350,9 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) { }); for (auto adj : sortedAdjacencies) { out << ' '; - adj->print(out); + adj->print(out, PO); - auto &info = AdjacencyInfo[adj]; + const auto info = AdjacencyInfo.lookup(adj); auto degree = info.NumConstraints; if (degree > 1) { out << " (" << degree << ")"; @@ -1388,22 +1389,25 @@ void ConstraintGraphNode::print(llvm::raw_ostream &out, unsigned indent) { out << "Equivalence class:"; for (unsigned i = 1, n = EquivalenceClass.size(); i != n; ++i) { out << ' '; - EquivalenceClass[i]->print(out); + EquivalenceClass[i]->print(out, PO); } out << "\n"; } } -void ConstraintGraphNode::dump() { - llvm::SaveAndRestore - debug(TypeVar->getASTContext().LangOpts.DebugConstraintSolver, true); - print(llvm::dbgs(), 0); +void ConstraintGraphNode::dump() const { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + print(llvm::dbgs(), 0, PO); } void ConstraintGraph::print(ArrayRef typeVars, llvm::raw_ostream &out) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + for (auto typeVar : typeVars) { - (*this)[typeVar].print(out, 2); + (*this)[typeVar].print(out, 2, PO); out << "\n"; } } @@ -1413,8 +1417,6 @@ void ConstraintGraph::dump() { } void ConstraintGraph::dump(llvm::raw_ostream &out) { - llvm::SaveAndRestore - debug(CS.getASTContext().LangOpts.DebugConstraintSolver, true); print(CS.getTypeVariables(), out); } @@ -1422,6 +1424,8 @@ void ConstraintGraph::printConnectedComponents( ArrayRef typeVars, llvm::raw_ostream &out) { auto components = computeConnectedComponents(typeVars); + PrintOptions PO; + PO.PrintTypesForDebugging = true; for (const auto& component : components) { out.indent(2); out << component.solutionIndex << ": "; @@ -1432,7 +1436,7 @@ void ConstraintGraph::printConnectedComponents( // Print all of the type variables in this connected component. interleave(component.typeVars, [&](TypeVariableType *typeVar) { - typeVar->print(out); + Type(typeVar).print(out, PO); }, [&] { out << ' '; @@ -1452,8 +1456,6 @@ void ConstraintGraph::printConnectedComponents( } void ConstraintGraph::dumpConnectedComponents() { - llvm::SaveAndRestore - debug(CS.getASTContext().LangOpts.DebugConstraintSolver, true); printConnectedComponents(CS.getTypeVariables(), llvm::dbgs()); } @@ -1559,18 +1561,20 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) { } // Make sure that the adjacencies we expect are the adjacencies we have. + PrintOptions PO; + PO.PrintTypesForDebugging = true; for (auto adj : expectedAdjacencies) { auto knownAdj = AdjacencyInfo.find(adj.first); requireWithContext(knownAdj != AdjacencyInfo.end(), "missing adjacency information for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() << 'n'; + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << 'n'; }); requireWithContext(adj.second == knownAdj->second.NumConstraints, "wrong number of adjacencies for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << " (" << adj.second << " vs. " << knownAdj->second.NumConstraints << ")\n"; @@ -1584,7 +1588,7 @@ void ConstraintGraphNode::verify(ConstraintGraph &cg) { requireWithContext(AdjacencyInfo.count(adj.first) > 0, "extraneous adjacency info for type variable", [&] { - llvm::dbgs() << " type variable=" << adj.first->getString() << '\n'; + llvm::dbgs() << " type variable=" << adj.first->getString(PO) << '\n'; }); } } diff --git a/lib/Sema/ConstraintGraph.h b/lib/Sema/ConstraintGraph.h index 59bc1833a7aa6..f3041fcb4fe6c 100644 --- a/lib/Sema/ConstraintGraph.h +++ b/lib/Sema/ConstraintGraph.h @@ -17,6 +17,7 @@ #ifndef SWIFT_SEMA_CONSTRAINT_GRAPH_H #define SWIFT_SEMA_CONSTRAINT_GRAPH_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" @@ -159,10 +160,10 @@ class ConstraintGraphNode { mutable SmallVector EquivalenceClass; /// Print this graph node. - void print(llvm::raw_ostream &out, unsigned indent); + void print(llvm::raw_ostream &out, unsigned indent, + PrintOptions PO = PrintOptions()) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; /// Verify the invariants of this node within the given constraint graph. void verify(ConstraintGraph &cg); @@ -333,15 +334,15 @@ class ConstraintGraph { void print(ArrayRef typeVars, llvm::raw_ostream &out); void dump(llvm::raw_ostream &out); - LLVM_ATTRIBUTE_DEPRECATED(void dump() LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + // FIXME: Potentially side-effectful. + SWIFT_DEBUG_HELPER(void dump()); /// Print the connected components of the graph. void printConnectedComponents(ArrayRef typeVars, llvm::raw_ostream &out); - LLVM_ATTRIBUTE_DEPRECATED(void dumpConnectedComponents() LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + // FIXME: Potentially side-effectful. + SWIFT_DEBUG_HELPER(void dumpConnectedComponents()); /// Verify the invariants of the graph. void verify(); diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index cb1f97f28e601..bba1582f764df 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -52,24 +52,6 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, id.AddPointer(kpElt.getKeyPathDecl()); break; } - case ApplyArgument: - case ApplyFunction: - case FunctionArgument: - case FunctionResult: - case OptionalPayload: - case Member: - case MemberRefBase: - case UnresolvedMember: - case SubscriptMember: - case ConstructorMember: - case LValueConversion: - case RValueAdjustment: - case ClosureResult: - case ParentType: - case ExistentialSuperclassType: - case InstanceType: - case SequenceElementType: - case AutoclosureResult: case GenericArgument: case NamedTupleElement: case TupleElement: @@ -78,22 +60,78 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case KeyPathComponent: case ConditionalRequirement: case TypeParameterRequirement: - case ImplicitlyUnwrappedDisjunctionChoice: - case DynamicLookupResult: case ContextualType: case SynthesizedArgument: - case KeyPathType: - case KeyPathRoot: - case KeyPathValue: - case KeyPathComponentResult: + case TernaryBranch: { auto numValues = numNumericValuesInPathElement(elt.getKind()); for (unsigned i = 0; i < numValues; ++i) id.AddInteger(elt.getValue(i)); break; } +#define SIMPLE_LOCATOR_PATH_ELT(Name) case Name : +#include "ConstraintLocatorPathElts.def" + // Nothing to do for simple locator elements. + break; + } } } +unsigned LocatorPathElt::getNewSummaryFlags() const { + switch (getKind()) { + case ConstraintLocator::ApplyArgument: + case ConstraintLocator::ApplyFunction: + case ConstraintLocator::SequenceElementType: + case ConstraintLocator::ClosureResult: + case ConstraintLocator::ConstructorMember: + case ConstraintLocator::InstanceType: + case ConstraintLocator::AutoclosureResult: + case ConstraintLocator::OptionalPayload: + case ConstraintLocator::Member: + case ConstraintLocator::MemberRefBase: + case ConstraintLocator::UnresolvedMember: + case ConstraintLocator::ParentType: + case ConstraintLocator::ExistentialSuperclassType: + case ConstraintLocator::LValueConversion: + case ConstraintLocator::RValueAdjustment: + case ConstraintLocator::SubscriptMember: + case ConstraintLocator::OpenedGeneric: + case ConstraintLocator::GenericParameter: + case ConstraintLocator::GenericArgument: + case ConstraintLocator::NamedTupleElement: + case ConstraintLocator::TupleElement: + case ConstraintLocator::ProtocolRequirement: + case ConstraintLocator::Witness: + case ConstraintLocator::KeyPathComponent: + case ConstraintLocator::ConditionalRequirement: + case ConstraintLocator::TypeParameterRequirement: + case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: + case ConstraintLocator::DynamicLookupResult: + case ConstraintLocator::ContextualType: + case ConstraintLocator::SynthesizedArgument: + case ConstraintLocator::KeyPathDynamicMember: + case ConstraintLocator::KeyPathType: + case ConstraintLocator::KeyPathRoot: + case ConstraintLocator::KeyPathValue: + case ConstraintLocator::KeyPathComponentResult: + case ConstraintLocator::Condition: + case ConstraintLocator::DynamicCallable: + case ConstraintLocator::ImplicitCallAsFunction: + case ConstraintLocator::TernaryBranch: + return 0; + + case ConstraintLocator::FunctionArgument: + case ConstraintLocator::FunctionResult: + return IsFunctionConversion; + + case ConstraintLocator::ApplyArgToParam: { + auto flags = castTo().getParameterFlags(); + return flags.isNonEphemeral() ? IsNonEphemeralParam : 0; + } + } + + llvm_unreachable("Unhandled PathElementKind in switch."); +} + bool LocatorPathElt::isResultOfSingleExprFunction() const { if (auto elt = getAs()) return elt->isForSingleExprFunction(); @@ -190,21 +228,26 @@ bool ConstraintLocator::isForContextualType() const { } GenericTypeParamType *ConstraintLocator::getGenericParameter() const { - return castLastElementTo().getType(); + // Check whether we have a path that terminates at a generic parameter. + return isForGenericParameter() ? + castLastElementTo().getType() : nullptr; } -void ConstraintLocator::dump(SourceManager *sm) { +void ConstraintLocator::dump(SourceManager *sm) const { dump(sm, llvm::errs()); llvm::errs() << "\n"; } -void ConstraintLocator::dump(ConstraintSystem *CS) { - dump(&CS->TC.Context.SourceMgr, llvm::errs()); +void ConstraintLocator::dump(ConstraintSystem *CS) const { + dump(&CS->getASTContext().SourceMgr, llvm::errs()); llvm::errs() << "\n"; } -void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { +void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { + PrintOptions PO; + PO.PrintTypesForDebugging = true; + out << "locator@" << (void*) this << " ["; if (anchor) { @@ -239,7 +282,7 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { switch (elt.getKind()) { case GenericParameter: { auto gpElt = elt.castTo(); - out << "generic parameter '" << gpElt.getType()->getString() << "'"; + out << "generic parameter '" << gpElt.getType()->getString(PO) << "'"; break; } case ApplyArgument: @@ -258,6 +301,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { auto argElt = elt.castTo(); out << "comparing call argument #" << llvm::utostr(argElt.getArgIdx()) << " to parameter #" << llvm::utostr(argElt.getParamIdx()); + if (argElt.getParameterFlags().isNonEphemeral()) + out << " (non-ephemeral)"; break; } case ClosureResult: @@ -408,6 +453,24 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) { case KeyPathComponentResult: out << "key path component result"; break; + + case Condition: + out << "condition expression"; + break; + + case DynamicCallable: + out << "implicit call to @dynamicCallable method"; + break; + + case ImplicitCallAsFunction: + out << "implicit reference to callAsFunction"; + break; + + case TernaryBranch: + auto branchElt = elt.castTo(); + out << (branchElt.forThen() ? "'then'" : "'else'") + << " branch of a ternary operator"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index 52e3d7aaf3d03..e7cfd9eaf2132 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -18,6 +18,7 @@ #ifndef SWIFT_SEMA_CONSTRAINTLOCATOR_H #define SWIFT_SEMA_CONSTRAINTLOCATOR_H +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" @@ -62,33 +63,11 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// element kind. static unsigned numNumericValuesInPathElement(PathElementKind kind) { switch (kind) { - case ApplyArgument: - case ApplyFunction: +#define SIMPLE_LOCATOR_PATH_ELT(Name) case Name : +#include "ConstraintLocatorPathElts.def" case GenericParameter: - case FunctionArgument: - case FunctionResult: - case OptionalPayload: - case Member: - case MemberRefBase: - case UnresolvedMember: - case SubscriptMember: - case ConstructorMember: - case LValueConversion: - case RValueAdjustment: - case ClosureResult: - case ParentType: - case InstanceType: - case ExistentialSuperclassType: - case SequenceElementType: - case AutoclosureResult: case ProtocolRequirement: case Witness: - case ImplicitlyUnwrappedDisjunctionChoice: - case DynamicLookupResult: - case KeyPathType: - case KeyPathRoot: - case KeyPathValue: - case KeyPathComponentResult: return 0; case ContextualType: @@ -99,6 +78,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { case KeyPathComponent: case SynthesizedArgument: case KeyPathDynamicMember: + case TernaryBranch: return 1; case TypeParameterRequirement: @@ -122,55 +102,11 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// Does this path involve a function conversion, i.e. a /// FunctionArgument or FunctionResult node? IsFunctionConversion = 0x1, - }; - - static unsigned getSummaryFlagsForPathElement(PathElementKind kind) { - switch (kind) { - case ApplyArgument: - case ApplyFunction: - case ApplyArgToParam: - case SequenceElementType: - case ClosureResult: - case ConstructorMember: - case InstanceType: - case AutoclosureResult: - case OptionalPayload: - case Member: - case MemberRefBase: - case UnresolvedMember: - case ParentType: - case ExistentialSuperclassType: - case LValueConversion: - case RValueAdjustment: - case SubscriptMember: - case OpenedGeneric: - case GenericParameter: - case GenericArgument: - case NamedTupleElement: - case TupleElement: - case ProtocolRequirement: - case Witness: - case KeyPathComponent: - case ConditionalRequirement: - case TypeParameterRequirement: - case ImplicitlyUnwrappedDisjunctionChoice: - case DynamicLookupResult: - case ContextualType: - case SynthesizedArgument: - case KeyPathDynamicMember: - case KeyPathType: - case KeyPathRoot: - case KeyPathValue: - case KeyPathComponentResult: - return 0; - case FunctionArgument: - case FunctionResult: - return IsFunctionConversion; - } - - llvm_unreachable("Unhandled PathElementKind in switch."); - } + /// Does this path involve an argument being applied to a non-ephemeral + /// parameter? + IsNonEphemeralParam = 0x2, + }; /// One element in the path of a locator, which can include both /// a kind (PathElementKind) and a value used to describe specific @@ -329,9 +265,7 @@ class ConstraintLocator : public llvm::FoldingSetNode { bool is() const { return isa(this); } /// Return the summary flags for this particular element. - unsigned getNewSummaryFlags() const { - return getSummaryFlagsForPathElement(getKind()); - } + unsigned getNewSummaryFlags() const; bool isConditionalRequirement() const { return getKind() == PathElementKind::ConditionalRequirement; @@ -380,6 +314,12 @@ class ConstraintLocator : public llvm::FoldingSetNode { return (getSummaryFlags() & IsFunctionConversion); } + /// Checks whether this locator is describing an argument application for a + /// non-ephemeral parameter. + bool isNonEphemeralParameterApplication() const { + return (getSummaryFlags() & IsNonEphemeralParam); + } + /// Determine whether given locator points to the subscript reference /// e.g. `foo[0]` or `\Foo.[0]` bool isSubscriptMemberRef() const; @@ -540,14 +480,10 @@ class ConstraintLocator : public llvm::FoldingSetNode { } /// Produce a debugging dump of this locator. - LLVM_ATTRIBUTE_DEPRECATED( - void dump(SourceManager *SM) LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); - LLVM_ATTRIBUTE_DEPRECATED( - void dump(ConstraintSystem *CS) LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMPER(dump(SourceManager *SM)); + SWIFT_DEBUG_DUMPER(dump(ConstraintSystem *CS)); - void dump(SourceManager *SM, raw_ostream &OS) LLVM_ATTRIBUTE_USED; + void dump(SourceManager *SM, raw_ostream &OS) const LLVM_ATTRIBUTE_USED; private: /// Initialize a constraint locator with an anchor and a path. @@ -847,6 +783,20 @@ class LocatorPathElt::KeyPathDynamicMember final : public LocatorPathElt { } }; +class LocatorPathElt::TernaryBranch final : public LocatorPathElt { +public: + TernaryBranch(bool side) + : LocatorPathElt(ConstraintLocator::TernaryBranch, side) {} + + bool forThen() const { return bool(getValue(0)); } + + bool forElse() const { return !bool(getValue(0)); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::TernaryBranch; + } +}; + /// A simple stack-only builder object that constructs a /// constraint locator without allocating memory. /// @@ -876,8 +826,10 @@ class ConstraintLocatorBuilder { : previous(locator), element(), summaryFlags(locator ? locator->getSummaryFlags() : 0) { } - /// Retrieve a new path with the given path element added to it. - ConstraintLocatorBuilder withPathElement(LocatorPathElt newElt) { + /// Retrieve a new path with the given path element added to it. Note that + /// the produced locator stores a reference to this locator, and therefore + /// must not outlive it. + ConstraintLocatorBuilder withPathElement(LocatorPathElt newElt) & { unsigned newFlags = summaryFlags | newElt.getNewSummaryFlags(); if (!element) return ConstraintLocatorBuilder(previous, newElt, newFlags); @@ -915,6 +867,12 @@ class ConstraintLocatorBuilder { return false; } + /// Checks whether this locator is describing an argument application for a + /// non-ephemeral parameter. + bool isNonEphemeralParameterApplication() const { + return (getSummaryFlags() & ConstraintLocator::IsNonEphemeralParam); + } + /// Retrieve the base constraint locator, on which this builder's /// path is based. ConstraintLocator *getBaseLocator() const { diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index fa9b3d96a71f5..54912bbfa9953 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -76,6 +76,9 @@ SIMPLE_LOCATOR_PATH_ELT(FunctionResult) /// FIXME: Add support for named generic arguments? CUSTOM_LOCATOR_PATH_ELT(GenericArgument) +/// An implicit reference to a 'callAsFunction' method of a nominal type. +SIMPLE_LOCATOR_PATH_ELT(ImplicitCallAsFunction) + /// Locator for a binding from an IUO disjunction choice. SIMPLE_LOCATOR_PATH_ELT(ImplicitlyUnwrappedDisjunctionChoice) @@ -164,6 +167,14 @@ SIMPLE_LOCATOR_PATH_ELT(UnresolvedMember) /// The candidate witness during protocol conformance checking. CUSTOM_LOCATOR_PATH_ELT(Witness) +/// The condition associated with 'if' expression or ternary operator. +SIMPLE_LOCATOR_PATH_ELT(Condition) + +SIMPLE_LOCATOR_PATH_ELT(DynamicCallable) + +/// The 'true' or 'false' branch of a ternary operator. +CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index fd925e40d7bbe..f6690b4a82353 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -19,7 +19,9 @@ #include "ConstraintGraph.h" #include "CSDiagnostics.h" #include "CSFix.h" +#include "SolutionResult.h" #include "TypeCheckType.h" +#include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/ParameterList.h" #include "swift/Basic/Statistic.h" @@ -34,10 +36,11 @@ using namespace constraints; #define DEBUG_TYPE "ConstraintSystem" ExpressionTimer::ExpressionTimer(Expr *E, ConstraintSystem &CS) - : E(E), WarnLimit(CS.TC.getWarnLongExpressionTypeChecking()), + : E(E), Context(CS.getASTContext()), StartTime(llvm::TimeRecord::getCurrentTime()), - PrintDebugTiming(CS.TC.getDebugTimeExpressions()), PrintWarning(true) { + PrintDebugTiming(CS.getASTContext().TypeCheckerOpts.DebugTimeExpressions), + PrintWarning(true) { if (auto *baseCS = CS.baseCS) { // If we already have a timer in the base constraint // system, let's seed its start time to the child. @@ -64,22 +67,20 @@ ExpressionTimer::~ExpressionTimer() { if (!PrintWarning) return; + const auto WarnLimit = getWarnLimit(); if (WarnLimit != 0 && elapsedMS >= WarnLimit && E->getLoc().isValid()) Context.Diags.diagnose(E->getLoc(), diag::debug_long_expression, elapsedMS, WarnLimit) .highlight(E->getSourceRange()); } -ConstraintSystem::ConstraintSystem(TypeChecker &tc, DeclContext *dc, - ConstraintSystemOptions options, - Expr *expr) - : TC(tc), DC(dc), Options(options), - Arena(tc.Context, Allocator), + +ConstraintSystem::ConstraintSystem(DeclContext *dc, + ConstraintSystemOptions options) + : Context(dc->getASTContext()), DC(dc), Options(options), + Arena(dc->getASTContext(), Allocator), CG(*new ConstraintGraph(*this)) { - if (expr) - ExprWeights = expr->getDepthMap(); - assert(DC && "context required"); } @@ -90,13 +91,13 @@ ConstraintSystem::~ConstraintSystem() { void ConstraintSystem::incrementScopeCounter() { CountScopes++; // FIXME: (transitional) increment the redundant "always-on" counter. - if (TC.Context.Stats) - TC.Context.Stats->getFrontendCounters().NumConstraintScopes++; + if (getASTContext().Stats) + getASTContext().Stats->getFrontendCounters().NumConstraintScopes++; } void ConstraintSystem::incrementLeafScopes() { - if (TC.Context.Stats) - TC.Context.Stats->getFrontendCounters().NumLeafScopes++; + if (getASTContext().Stats) + getASTContext().Stats->getFrontendCounters().NumLeafScopes++; } bool ConstraintSystem::hasFreeTypeVariables() { @@ -108,7 +109,7 @@ bool ConstraintSystem::hasFreeTypeVariables() { void ConstraintSystem::addTypeVariable(TypeVariableType *typeVar) { TypeVariables.insert(typeVar); - + // Notify the constraint graph. (void)CG[typeVar]; } @@ -181,14 +182,15 @@ void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type, if (!anchor) continue; - literalProtocol = TC.getLiteralProtocol(anchor); + literalProtocol = + TypeChecker::getLiteralProtocol(getASTContext(), anchor); if (literalProtocol) break; } // If the protocol has a default type, check it. if (literalProtocol) { - if (auto defaultType = TC.getDefaultType(literalProtocol, DC)) { + if (auto defaultType = TypeChecker::getDefaultType(literalProtocol, DC)) { // Check whether the nominal types match. This makes sure that we // properly handle Array vs. Array. if (defaultType->getAnyNominal() != type->getAnyNominal()) @@ -204,14 +206,11 @@ void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type, void ConstraintSystem::addTypeVariableConstraintsToWorkList( TypeVariableType *typeVar) { - // Gather the constraints affected by a change to this type variable. - auto inactiveConstraints = CG.gatherConstraints( - typeVar, ConstraintGraph::GatheringKind::AllMentions, - [](Constraint *constraint) { return !constraint->isActive(); }); - - // Add any constraints that aren't already active to the worklist. - for (auto *constraint : inactiveConstraints) - activateConstraint(constraint); + // Activate the constraints affected by a change to this type variable. + auto gatheringKind = ConstraintGraph::GatheringKind::AllMentions; + for (auto *constraint : CG.gatherConstraints(typeVar, gatheringKind)) + if (!constraint->isActive()) + activateConstraint(constraint); } /// Retrieve a dynamic result signature for the given declaration. @@ -233,7 +232,7 @@ getDynamicResultSignature(ValueDecl *decl) { llvm_unreachable("Not a valid @objc member"); } -LookupResult &ConstraintSystem::lookupMember(Type base, DeclName name) { +LookupResult &ConstraintSystem::lookupMember(Type base, DeclNameRef name) { // Check whether we've already performed this lookup. auto &result = MemberLookups[{base, name}]; if (result) return *result; @@ -243,7 +242,7 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclName name) { if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; - result = TC.lookupMember(DC, base, name, lookupOptions); + result = TypeChecker::lookupMember(DC, base, name, lookupOptions); // If we aren't performing dynamic lookup, we're done. if (!*result || !base->isAnyObject()) @@ -274,8 +273,9 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclName name) { // If the entry we recorded was unavailable but this new entry is not, // replace the recorded entry with this one. - if (uniqueEntry->getAttrs().isUnavailable(TC.Context) && - !decl->getAttrs().isUnavailable(TC.Context)) { + auto &ctx = getASTContext(); + if (uniqueEntry->getAttrs().isUnavailable(ctx) && + !decl->getAttrs().isUnavailable(ctx)) { uniqueEntry = decl; } } @@ -349,9 +349,9 @@ getAlternativeLiteralTypes(KnownProtocolKind kind) { case KnownProtocolKind::ExpressibleByIntegerLiteral: // Integer literals can be treated as floating point literals. - if (auto floatProto = TC.Context.getProtocol( + if (auto floatProto = getASTContext().getProtocol( KnownProtocolKind::ExpressibleByFloatLiteral)) { - if (auto defaultType = TC.getDefaultType(floatProto, DC)) { + if (auto defaultType = TypeChecker::getDefaultType(floatProto, DC)) { types.push_back(defaultType); } } @@ -413,11 +413,46 @@ ConstraintLocator *ConstraintSystem::getConstraintLocator( return getConstraintLocator(anchor, path, builder.getSummaryFlags()); } -ConstraintLocator * -ConstraintSystem::getCalleeLocator(ConstraintLocator *locator) { +ConstraintLocator *ConstraintSystem::getConstraintLocator( + ConstraintLocator *locator, + ArrayRef newElts) { + auto oldPath = locator->getPath(); + SmallVector newPath; + newPath.append(oldPath.begin(), oldPath.end()); + newPath.append(newElts.begin(), newElts.end()); + return getConstraintLocator(locator->getAnchor(), newPath); +} + +ConstraintLocator *ConstraintSystem::getConstraintLocator( + const ConstraintLocatorBuilder &builder, + ArrayRef newElts) { + SmallVector newPath; + auto *anchor = builder.getLocatorParts(newPath); + newPath.append(newElts.begin(), newElts.end()); + return getConstraintLocator(anchor, newPath); +} + +ConstraintLocator *ConstraintSystem::getCalleeLocator( + ConstraintLocator *locator, bool lookThroughApply, + llvm::function_ref getType, + llvm::function_ref simplifyType, + llvm::function_ref(ConstraintLocator *)> + getOverloadFor) { auto *anchor = locator->getAnchor(); assert(anchor && "Expected an anchor!"); + auto path = locator->getPath(); + { + // If we have a locator for a member found through key path dynamic member + // lookup, then we need to chop off the elements after the + // KeyPathDynamicMember element to get the callee locator. + auto iter = path.rbegin(); + if (locator->findLast(iter)) { + auto newPath = path.drop_back(iter - path.rbegin()); + return getConstraintLocator(anchor, newPath); + } + } + // If we have a locator that starts with a key path component element, we // may have a callee given by a property or subscript component. if (auto componentElt = @@ -455,45 +490,134 @@ ConstraintSystem::getCalleeLocator(ConstraintLocator *locator) { if (isa(anchor)) return getConstraintLocator(anchor, ConstraintLocator::SubscriptMember); - if (auto *applyExpr = dyn_cast(anchor)) { - auto *fnExpr = applyExpr->getFn(); + auto getSpecialFnCalleeLoc = [&](Type fnTy) -> ConstraintLocator * { + // FIXME: We should probably assert that we don't get a type variable + // here to make sure we only retrieve callee locators for resolved calls, + // ensuring that callee locators don't change after binding a type. + // Unfortunately CSDiag currently calls into getCalleeLocator, so all bets + // are off. Once we remove that legacy diagnostic logic, we should be able + // to assert here. + fnTy = simplifyType(fnTy); + // For an apply of a metatype, we have a short-form constructor. Unlike // other locators to callees, these are anchored on the apply expression // rather than the function expr. - auto fnTy = getFixedTypeRecursive(getType(fnExpr), /*wantRValue*/ true); if (fnTy->is()) { - auto *fnLocator = - getConstraintLocator(applyExpr, ConstraintLocator::ApplyFunction); - return getConstraintLocator(fnLocator, - ConstraintLocator::ConstructorMember); + return getConstraintLocator(anchor, + {LocatorPathElt::ApplyFunction(), + LocatorPathElt::ConstructorMember()}); } - // Otherwise fall through and look for locators anchored on the function - // expr. For CallExprs, this can look through things like parens and - // optional chaining. - if (auto *callExpr = dyn_cast(anchor)) { - anchor = callExpr->getDirectCallee(); - } else { - anchor = fnExpr; + // Handle an apply of a nominal type which supports callAsFunction. + if (fnTy->isCallableNominalType(DC)) { + return getConstraintLocator(anchor, + {LocatorPathElt::ApplyFunction(), + LocatorPathElt::ImplicitCallAsFunction()}); + } + return nullptr; + }; + + if (lookThroughApply) { + if (auto *applyExpr = dyn_cast(anchor)) { + auto *fnExpr = applyExpr->getFn(); + + // Handle special cases for applies of non-function types. + if (auto *loc = getSpecialFnCalleeLoc(getType(fnExpr))) + return loc; + + // Otherwise fall through and look for locators anchored on the function + // expr. For CallExprs, this can look through things like parens and + // optional chaining. + if (auto *callExpr = dyn_cast(anchor)) { + anchor = callExpr->getDirectCallee(); + } else { + anchor = fnExpr; + } } } if (auto *UDE = dyn_cast(anchor)) { return getConstraintLocator( - anchor, TC.getSelfForInitDelegationInConstructor(DC, UDE) - ? ConstraintLocator::ConstructorMember - : ConstraintLocator::Member); + anchor, TypeChecker::getSelfForInitDelegationInConstructor(DC, UDE) + ? ConstraintLocator::ConstructorMember + : ConstraintLocator::Member); + } + + if (auto *UME = dyn_cast(anchor)) { + auto *calleeLoc = + getConstraintLocator(UME, ConstraintLocator::UnresolvedMember); + + // Handle special cases for applies of non-function types. + // FIXME: Consider re-designing the AST such that an unresolved member expr + // with arguments uses a CallExpr, which would make this logic unnecessary + // and clean up a bunch of other special cases. Doing so may require a bit + // of hacking in CSGen though. + if (UME->hasArguments()) { + if (auto overload = getOverloadFor(calleeLoc)) { + if (auto *loc = getSpecialFnCalleeLoc(overload->boundType)) + return loc; + } + } + return calleeLoc; } - if (isa(anchor)) - return getConstraintLocator(anchor, ConstraintLocator::UnresolvedMember); - if (isa(anchor)) return getConstraintLocator(anchor, ConstraintLocator::Member); return getConstraintLocator(anchor); } +/// Extend the given depth map by adding depths for all of the subexpressions +/// of the given expression. +static void extendDepthMap( + Expr *expr, + llvm::DenseMap> &depthMap) { + class RecordingTraversal : public ASTWalker { + public: + llvm::DenseMap> &DepthMap; + unsigned Depth = 0; + + explicit RecordingTraversal( + llvm::DenseMap> &depthMap) + : DepthMap(depthMap) {} + + std::pair walkToExprPre(Expr *E) override { + DepthMap[E] = {Depth, Parent.getAsExpr()}; + Depth++; + return { true, E }; + } + + Expr *walkToExprPost(Expr *E) override { + Depth--; + return E; + } + }; + + RecordingTraversal traversal(depthMap); + expr->walk(traversal); +} + +Optional> ConstraintSystem::getExprDepthAndParent( + Expr *expr) { + // Check whether the parent has this information. + if (baseCS && baseCS != this) { + if (auto known = baseCS->getExprDepthAndParent(expr)) + return *known; + } + + // Bring the set of expression weights up to date. + while (NumInputExprsInWeights < InputExprs.size()) { + extendDepthMap(InputExprs[NumInputExprsInWeights], ExprWeights); + ++NumInputExprsInWeights; + } + + auto e = ExprWeights.find(expr); + if (e != ExprWeights.end()) + return e->second; + + return None; +} + Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound, ConstraintLocatorBuilder locator, OpenedTypeMap &replacements) { @@ -521,7 +645,7 @@ Type ConstraintSystem::openUnboundGenericType(UnboundGenericType *unbound, locator); } } - + // Map the generic parameters to their corresponding type variables. llvm::SmallVector arguments; for (auto gp : unboundDecl->getInnermostGenericParamTypes()) { @@ -624,8 +748,6 @@ Type ConstraintSystem::openUnboundGenericType( Type type, ConstraintLocatorBuilder locator) { assert(!type->getCanonicalType()->hasTypeParameter()); - checkNestedTypeConstraints(*this, type, locator); - if (!type->hasUnboundGenericType()) return type; @@ -661,7 +783,7 @@ Type ConstraintSystem::openType(Type type, OpenedTypeMap &replacements) { // drop outer generic parameters. // assert(known != replacements.end()); if (known == replacements.end()) - return ErrorType::get(TC.Context); + return ErrorType::get(getASTContext()); return known->second; } @@ -742,7 +864,7 @@ bool ConstraintSystem::isAnyHashableType(Type type) { Type ConstraintSystem::getFixedTypeRecursive(Type type, TypeMatchOptions &flags, - bool wantRValue) { + bool wantRValue) const { if (wantRValue) type = type->getRValueType(); @@ -782,7 +904,7 @@ static bool doesStorageProduceLValue(AbstractStorageDecl *storage, // Unsettable storage decls always produce rvalues. if (!storage->isSettable(useDC, base)) return false; - + if (!storage->isSetterAccessibleFrom(useDC)) return false; @@ -813,19 +935,14 @@ Type ConstraintSystem::getUnopenedTypeOfReference(VarDecl *value, Type baseType, DeclContext *UseDC, const DeclRefExpr *base, bool wantInterfaceType) { - return TC.getUnopenedTypeOfReference( + return TypeChecker::getUnopenedTypeOfReference( value, baseType, UseDC, [&](VarDecl *var) -> Type { - if (auto *param = dyn_cast(var)) - return getType(param); + if (Type type = getTypeIfAvailable(var)) + return type; if (!var->hasInterfaceType()) { - if (!var->isInvalid()) { - TC.diagnose(var->getLoc(), diag::recursive_decl_reference, - var->getDescriptiveKind(), var->getName()); - var->markInvalid(); - } - return ErrorType::get(TC.Context); + return ErrorType::get(getASTContext()); } return wantInterfaceType ? var->getInterfaceType() : var->getType(); @@ -887,7 +1004,7 @@ void ConstraintSystem::recordOpenedTypes( }) == OpenedTypes.end() && "already registered opened types for this locator"); #endif - + OpenedType* openedTypes = Allocator.Allocate(replacements.size()); std::copy(replacements.begin(), replacements.end(), openedTypes); @@ -898,7 +1015,7 @@ void ConstraintSystem::recordOpenedTypes( /// Determine how many levels of argument labels should be removed from the /// function type when referencing the given declaration. -static unsigned getNumRemovedArgumentLabels(TypeChecker &TC, ValueDecl *decl, +static unsigned getNumRemovedArgumentLabels(ValueDecl *decl, bool isCurriedInstanceReference, FunctionRefKind functionRefKind) { unsigned numParameterLists = decl->getNumCurryLevels(); @@ -967,8 +1084,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value, auto funcType = funcDecl->getInterfaceType()->castTo(); auto numLabelsToRemove = getNumRemovedArgumentLabels( - TC, funcDecl, - /*isCurriedInstanceReference=*/false, functionRefKind); + funcDecl, /*isCurriedInstanceReference=*/false, functionRefKind); auto openedType = openFunctionType(funcType, locator, replacements, funcDecl->getDeclContext()) @@ -989,6 +1105,8 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value, TypeResolverContext::InExpression, /*isSpecialized=*/false); + checkNestedTypeConstraints(*this, type, locator); + // Open the type. type = openUnboundGenericType(type, locator); @@ -1075,7 +1193,7 @@ static void bindArchetypesFromContext( if (parentDC->isTypeContext()) { if (parentDC != outerDC && parentDC->getSelfProtocolDecl()) { auto selfTy = parentDC->getSelfInterfaceType(); - auto contextTy = cs.TC.Context.TheUnresolvedType; + auto contextTy = cs.getASTContext().TheUnresolvedType; bindPrimaryArchetype(selfTy, contextTy); } continue; @@ -1122,7 +1240,8 @@ void ConstraintSystem::openGenericParameters(DeclContext *outerDC, auto *paramLocator = getConstraintLocator( locator.withPathElement(LocatorPathElt::GenericParameter(gp))); - auto typeVar = createTypeVariable(paramLocator, TVO_PrefersSubtypeBinding); + auto typeVar = createTypeVariable(paramLocator, TVO_PrefersSubtypeBinding | + TVO_CanBindToHole); auto result = replacements.insert(std::make_pair( cast(gp->getCanonicalType()), typeVar)); @@ -1169,11 +1288,11 @@ void ConstraintSystem::openGenericRequirements( break; } - addConstraint( - *openedReq, - locator.withPathElement(LocatorPathElt::OpenedGeneric(signature)) - .withPathElement( - LocatorPathElt::TypeParameterRequirement(pos, kind))); + auto openedGenericLoc = + locator.withPathElement(LocatorPathElt::OpenedGeneric(signature)); + addConstraint(*openedReq, + openedGenericLoc.withPathElement( + LocatorPathElt::TypeParameterRequirement(pos, kind))); } } @@ -1239,8 +1358,11 @@ ConstraintSystem::getTypeOfMemberReference( if (auto *typeDecl = dyn_cast(value)) { assert(!isa(typeDecl) && "Nested module?"); - auto memberTy = TC.substMemberTypeWithBase(DC->getParentModule(), - typeDecl, baseObjTy); + auto memberTy = TypeChecker::substMemberTypeWithBase(DC->getParentModule(), + typeDecl, baseObjTy); + + checkNestedTypeConstraints(*this, memberTy, locator); + // Open the type if it was a reference to a generic type. memberTy = openUnboundGenericType(memberTy, locator); @@ -1260,8 +1382,7 @@ ConstraintSystem::getTypeOfMemberReference( OpenedTypeMap localReplacements; auto &replacements = replacementsPtr ? *replacementsPtr : localReplacements; unsigned numRemovedArgumentLabels = getNumRemovedArgumentLabels( - TC, value, /*isCurriedInstanceReference*/ !hasAppliedSelf, - functionRefKind); + value, /*isCurriedInstanceReference*/ !hasAppliedSelf, functionRefKind); AnyFunctionType *funcType; @@ -1294,8 +1415,9 @@ ConstraintSystem::getTypeOfMemberReference( ->castTo()->getParams(); refType = FunctionType::get(indices, elementTy); } else { - refType = getUnopenedTypeOfReference(cast(value), baseTy, useDC, - base, /*wantInterfaceType=*/true); + refType = TypeChecker::getUnopenedTypeOfReference( + cast(value), baseTy, useDC, base, + /*wantInterfaceType=*/true); } auto selfTy = outerDC->getSelfInterfaceType(); @@ -1460,13 +1582,20 @@ Type ConstraintSystem::getEffectiveOverloadType(const OverloadChoice &overload, if (decl->isImplicitlyUnwrappedOptional()) return Type(); + // In a pattern binding initializer, all of its bound variables have no + // effective overload type. + if (auto *PBI = dyn_cast(useDC)) { + if (auto *VD = dyn_cast(decl)) { + if (PBI->getBinding() == VD->getParentPatternBinding()) { + return Type(); + } + } + } + // Retrieve the interface type. auto type = decl->getInterfaceType(); - if (!type) { - type = decl->getInterfaceType(); - if (!type) { - return Type(); - } + if (type->hasError()) { + return Type(); } // If we have a generic function type, drop the generic signature; we don't @@ -1581,21 +1710,16 @@ void ConstraintSystem::addOverloadSet(ArrayRef choices, } /// If we're resolving an overload set with a decl that has special type -/// checking semantics, set up the special-case type system and return true; -/// otherwise return false. -static bool -resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, - ConstraintLocator *locator, - Type boundType, - OverloadChoice choice, - Type &refType, - Type &openedFullType) { - assert(choice.getKind() == OverloadChoiceKind::Decl); - - switch (CS.TC.getDeclTypeCheckingSemantics(choice.getDecl())) { +/// checking semantics, compute the type of the reference. For now, follow +/// the lead of \c getTypeOfMemberReference and return a pair of +/// the full opened type and the reference's type. +static std::pair getTypeOfReferenceWithSpecialTypeCheckingSemantics( + ConstraintSystem &CS, ConstraintLocator *locator, + DeclTypeCheckingSemantics semantics) { + switch (semantics) { case DeclTypeCheckingSemantics::Normal: - return false; - + llvm_unreachable("Decl does not have special type checking semantics!"); + case DeclTypeCheckingSemantics::TypeOf: { // Proceed with a "DynamicType" operation. This produces an existential // metatype from existentials, or a concrete metatype from non- @@ -1610,12 +1734,11 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, FunctionType::Param inputArg(input, CS.getASTContext().getIdentifier("of")); - + CS.addConstraint(ConstraintKind::DynamicTypeOf, output, input, CS.getConstraintLocator(locator, ConstraintLocator::RValueAdjustment)); - refType = FunctionType::get({inputArg}, output); - openedFullType = refType; - return true; + auto refType = FunctionType::get({inputArg}, output); + return {refType, refType}; } case DeclTypeCheckingSemantics::WithoutActuallyEscaping: { // Proceed with a "WithoutActuallyEscaping" operation. The body closure @@ -1637,18 +1760,21 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(arg, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr)); FunctionType::Param args[] = { FunctionType::Param(noescapeClosure), FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), }; - - refType = FunctionType::get(args, result, + + auto refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, - /*throws*/ true)); - openedFullType = refType; - return true; + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr)); + return {refType, refType}; } case DeclTypeCheckingSemantics::OpenExistential: { // The body closure receives a freshly-opened archetype constrained by the @@ -1669,17 +1795,20 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS, auto bodyClosure = FunctionType::get(bodyArgs, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ true, - /*throws*/ true)); + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr)); FunctionType::Param args[] = { FunctionType::Param(existentialTy), FunctionType::Param(bodyClosure, CS.getASTContext().getIdentifier("do")), }; - refType = FunctionType::get(args, result, + auto refType = FunctionType::get(args, result, FunctionType::ExtInfo(FunctionType::Representation::Swift, /*noescape*/ false, - /*throws*/ true)); - openedFullType = refType; - return true; + /*throws*/ true, + DifferentiabilityKind::NonDifferentiable, + /*clangFunctionType*/ nullptr)); + return {refType, refType}; } } @@ -1703,7 +1832,7 @@ static bool shouldCheckForPartialApplication(ConstraintSystem &cs, // FIXME(diagnostics): This check should be removed together with // expression based diagnostics. - if (cs.TC.isExprBeingDiagnosed(anchor)) + if (cs.isExprBeingDiagnosed(anchor)) return false; // If this is a reference to instance method marked as 'mutating' @@ -1761,6 +1890,317 @@ isInvalidPartialApplication(ConstraintSystem &cs, const ValueDecl *member, return {true, level}; } +std::pair ConstraintSystem::adjustTypeOfOverloadReference( + const OverloadChoice &choice, ConstraintLocator *locator, + Type boundType, Type refType) { + // If the declaration is unavailable, note that in the score. + if (choice.getDecl()->getAttrs().isUnavailable(getASTContext())) { + increaseScore(SK_Unavailable); + } + + bool bindConstraintCreated = false; + const auto kind = choice.getKind(); + if (kind != OverloadChoiceKind::DeclViaDynamic && + !isRequirementOrWitness(locator) && + choice.getDecl()->getAttrs().hasAttribute() && + !isa(choice.getDecl())) { + // For a non-subscript declaration that is an optional + // requirement in a protocol, strip off the lvalue-ness (FIXME: + // one cannot assign to such declarations for now) and make a + // reference to that declaration be optional. + // + // Subscript declarations are handled within + // getTypeOfMemberReference(); their result types are optional. + + // Deal with values declared as implicitly unwrapped, or + // functions with return types that are implicitly unwrapped. + // TODO: Move this logic to bindOverloadType. + if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { + // Build the disjunction to attempt binding both T? and T (or + // function returning T? and function returning T). + Type ty = createTypeVariable(locator, + TVO_CanBindToLValue | TVO_CanBindToNoEscape); + buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType, locator); + addConstraint(ConstraintKind::Bind, boundType, + OptionalType::get(ty->getRValueType()), locator); + bindConstraintCreated = true; + } + + // TODO: Move this to getTypeOfMemberReference. + refType = OptionalType::get(refType->getRValueType()); + } + + switch (kind) { + case OverloadChoiceKind::Decl: + case OverloadChoiceKind::DeclViaBridge: + case OverloadChoiceKind::DeclViaUnwrappedOptional: + case OverloadChoiceKind::TupleIndex: + case OverloadChoiceKind::BaseType: + case OverloadChoiceKind::KeyPathApplication: + return {refType, bindConstraintCreated}; + case OverloadChoiceKind::DeclViaDynamic: { + // TODO: Move the IUO handling logic here to bindOverloadType. + if (isa(choice.getDecl())) { + // We always expect function type for subscripts. + auto fnTy = refType->castTo(); + if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { + auto resultTy = fnTy->getResult(); + // We expect the element type to be a double-optional. + auto optTy = resultTy->getOptionalObjectType(); + assert(optTy->getOptionalObjectType()); + + // For our original type T -> U?? we will generate: + // A disjunction V = { U?, U } + // and a disjunction boundType = { T -> V?, T -> V } + Type ty = createTypeVariable(locator, TVO_CanBindToNoEscape); + + buildDisjunctionForImplicitlyUnwrappedOptional(ty, optTy, locator); + + // Create a new function type with an optional of this type + // variable as the result type. + if (auto *genFnTy = fnTy->getAs()) { + fnTy = GenericFunctionType::get( + genFnTy->getGenericSignature(), genFnTy->getParams(), + OptionalType::get(ty), genFnTy->getExtInfo()); + } else { + fnTy = FunctionType::get(fnTy->getParams(), OptionalType::get(ty), + fnTy->getExtInfo()); + } + } + + buildDisjunctionForDynamicLookupResult(boundType, fnTy, locator); + } else { + Type ty = refType; + + // If this is something we need to implicitly unwrap, set up a + // new type variable and disjunction that will allow us to make + // the choice of whether to do so. + if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { + // Duplicate the structure of boundType, with fresh type + // variables. We'll create a binding disjunction using this, + // selecting between options for refType, which is either + // Optional or a function type returning Optional. + assert(boundType->hasTypeVariable()); + ty = boundType.transform([this](Type elTy) -> Type { + if (auto *tv = dyn_cast(elTy.getPointer())) { + return createTypeVariable(tv->getImpl().getLocator(), + tv->getImpl().getRawOptions()); + } + return elTy; + }); + + buildDisjunctionForImplicitlyUnwrappedOptional( + ty, refType->getRValueType(), locator); + } + + // Build the disjunction to attempt binding both T? and T (or + // function returning T? and function returning T). + buildDisjunctionForDynamicLookupResult( + boundType, OptionalType::get(ty->getRValueType()), locator); + + // We store an Optional of the originally resolved type in the + // overload set. + // TODO: Move this to getTypeOfMemberReference. + refType = OptionalType::get(refType->getRValueType()); + } + + return {refType, /*bindConstraintCreated*/ true}; + } + case OverloadChoiceKind::DynamicMemberLookup: + case OverloadChoiceKind::KeyPathDynamicMemberLookup: + return {refType, bindConstraintCreated}; + } + + llvm_unreachable("Unhandled OverloadChoiceKind in switch."); +} + +void ConstraintSystem::bindOverloadType( + const SelectedOverload &overload, Type boundType, + ConstraintLocator *locator, DeclContext *useDC, + llvm::function_ref + verifyThatArgumentIsHashable) { + auto choice = overload.choice; + auto openedType = overload.openedType; + + auto bindTypeOrIUO = [&](Type ty) { + if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { + // Build the disjunction to attempt binding both T? and T (or + // function returning T? and function returning T). + buildDisjunctionForImplicitlyUnwrappedOptional(boundType, ty, locator); + } else { + // Add the type binding constraint. + addConstraint(ConstraintKind::Bind, boundType, ty, locator); + } + }; + switch (choice.getKind()) { + case OverloadChoiceKind::Decl: + case OverloadChoiceKind::DeclViaBridge: + case OverloadChoiceKind::DeclViaUnwrappedOptional: + case OverloadChoiceKind::TupleIndex: + case OverloadChoiceKind::BaseType: + case OverloadChoiceKind::KeyPathApplication: + case OverloadChoiceKind::DeclViaDynamic: + bindTypeOrIUO(openedType); + return; + case OverloadChoiceKind::DynamicMemberLookup: { + // DynamicMemberLookup results are always a (dynamicMember:T1)->T2 + // subscript. + auto refFnType = openedType->castTo(); + + // Before we drop the argument type on the floor, we need to constrain it + // to having a literal conformance to ExpressibleByStringLiteral. This + // makes the index default to String if otherwise unconstrained. + assert(refFnType->getParams().size() == 1 && + "subscript always has one arg"); + auto argType = refFnType->getParams()[0].getPlainType(); + + auto stringLiteral = + TypeChecker::getProtocol(getASTContext(), choice.getDecl()->getLoc(), + KnownProtocolKind::ExpressibleByStringLiteral); + if (!stringLiteral) + return; + + addConstraint(ConstraintKind::LiteralConformsTo, argType, + stringLiteral->getDeclaredType(), locator); + + // If this is used inside of the keypath expression, we need to make + // sure that argument is Hashable. + if (isa(locator->getAnchor())) + verifyThatArgumentIsHashable(0, argType, locator); + + // The resolved decl is for subscript(dynamicMember:), however the original + // member constraint was for a property. Therefore we need to bind to the + // result type. + bindTypeOrIUO(refFnType->getResult()); + return; + } + case OverloadChoiceKind::KeyPathDynamicMemberLookup: { + auto *fnType = openedType->castTo(); + assert(fnType->getParams().size() == 1 && + "subscript always has one argument"); + // Parameter type is KeyPath where `T` is a root type + // and U is a leaf type (aka member type). + auto keyPathTy = + fnType->getParams()[0].getPlainType()->castTo(); + + auto *keyPathDecl = keyPathTy->getAnyNominal(); + assert(isKnownKeyPathDecl(getASTContext(), keyPathDecl) && + "parameter is supposed to be a keypath"); + + auto *keyPathLoc = getConstraintLocator( + locator, LocatorPathElt::KeyPathDynamicMember(keyPathDecl)); + + auto rootTy = keyPathTy->getGenericArgs()[0]; + auto leafTy = keyPathTy->getGenericArgs()[1]; + + // Member would either point to mutable or immutable property, we + // don't which at the moment, so let's allow its type to be l-value. + auto memberTy = createTypeVariable(keyPathLoc, TVO_CanBindToLValue | + TVO_CanBindToNoEscape); + // Attempt to lookup a member with a give name in the root type and + // assign result to the leaf type of the keypath. + bool isSubscriptRef = locator->isSubscriptMemberRef(); + DeclNameRef memberName = isSubscriptRef + ? DeclNameRef::createSubscript() + // FIXME: Should propagate name-as-written through. + : DeclNameRef(choice.getName()); + + addValueMemberConstraint(LValueType::get(rootTy), memberName, memberTy, + useDC, + isSubscriptRef ? FunctionRefKind::DoubleApply + : FunctionRefKind::Unapplied, + /*outerAlternatives=*/{}, keyPathLoc); + + // In case of subscript things are more compicated comparing to "dot" + // syntax, because we have to get "applicable function" constraint + // associated with index expression and re-bind it to match "member type" + // looked up by dynamically. + if (isSubscriptRef) { + // Make sure that regular subscript declarations (if any) are + // preferred over key path dynamic member lookup. + increaseScore(SK_KeyPathSubscript); + + auto dynamicResultTy = boundType->castTo(); + auto constraints = getConstraintGraph().gatherConstraints( + dynamicResultTy, ConstraintGraph::GatheringKind::EquivalenceClass, + [](Constraint *constraint) { + return constraint->getKind() == ConstraintKind::ApplicableFunction; + }); + + assert(constraints.size() == 1); + auto *applicableFn = constraints.front(); + retireConstraint(applicableFn); + + // Original subscript expression e.g. `[0]` generated following + // constraint `($T_A0, [$T_A1], ...) -> $T_R applicable fn $T_S` where + // `$T_S` is supposed to be bound to each subscript choice e.g. + // `(Int) -> Int`. + // + // Here is what we need to do to make this work as-if expression was + // `[dynamicMember: \.[0]]`: + // - Right-hand side function type would have to get a new result type + // since it would have to point to result type of `\.[0]`, arguments + // though should stay the same. + // - Left-hand side `$T_S` is going to point to a new "member type" + // we are looking up based on the root type of the key path. + // - Original result type `$T_R` is going to represent result of + // the `[dynamicMember: \.[0]]` invocation. + + // Result of the `WritableKeyPath` is going to be l-value type, + // let's adjust l-valueness of the result type to accommodate that. + // + // This is required because we are binding result of the subscript + // to its "member type" which becomes dynamic result type. We could + // form additional `applicable fn` constraint here and bind it to a + // function type, but it would create inconsistency with how properties + // are handled, which means more special handling in CSApply. + if (keyPathDecl == getASTContext().getWritableKeyPathDecl() || + keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) + dynamicResultTy->getImpl().setCanBindToLValue(getSavedBindings(), + /*enabled=*/true); + + auto fnType = applicableFn->getFirstType()->castTo(); + + auto subscriptResultTy = createTypeVariable( + getConstraintLocator(locator->getAnchor(), + ConstraintLocator::FunctionResult), + TVO_CanBindToLValue | TVO_CanBindToNoEscape); + + auto adjustedFnTy = + FunctionType::get(fnType->getParams(), subscriptResultTy); + + ConstraintLocatorBuilder kpLocBuilder(keyPathLoc); + addConstraint( + ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy, + kpLocBuilder.withPathElement(ConstraintLocator::ApplyFunction)); + + addConstraint(ConstraintKind::Bind, dynamicResultTy, fnType->getResult(), + keyPathLoc); + + addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy, + keyPathLoc); + } else { + // Since member type is going to be bound to "leaf" generic parameter + // of the keypath, it has to be an r-value always, so let's add a new + // constraint to represent that conversion instead of loading member + // type into "leaf" directly. + addConstraint(ConstraintKind::Equal, memberTy, leafTy, keyPathLoc); + } + + if (isa(locator->getAnchor())) + verifyThatArgumentIsHashable(0, keyPathTy, locator); + + // The resolved decl is for subscript(dynamicMember:), however the + // original member constraint was either for a property, or we've + // re-purposed the overload type variable to represent the result type of + // the subscript. In both cases, we need to bind to the result type. + bindTypeOrIUO(fnType->getResult()); + return; + } + } + llvm_unreachable("Unhandled OverloadChoiceKind in switch."); +} + void ConstraintSystem::resolveOverload(ConstraintLocator *locator, Type boundType, OverloadChoice choice, @@ -1770,8 +2210,9 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // components. auto verifyThatArgumentIsHashable = [&](unsigned index, Type argType, ConstraintLocator *locator) { - if (auto *hashable = TC.getProtocol(choice.getDecl()->getLoc(), - KnownProtocolKind::Hashable)) { + if (auto *hashable = TypeChecker::getProtocol( + argType->getASTContext(), choice.getDecl()->getLoc(), + KnownProtocolKind::Hashable)) { addConstraint(ConstraintKind::ConformsTo, argType, hashable->getDeclaredType(), getConstraintLocator( @@ -1783,26 +2224,28 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, Type refType; Type openedFullType; - bool isDynamicResult = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; bool bindConstraintCreated = false; - switch (auto kind = choice.getKind()) { case OverloadChoiceKind::Decl: - // If we refer to a top-level decl with special type-checking semantics, - // handle it now. - if (resolveOverloadForDeclWithSpecialTypeCheckingSemantics( - *this, locator, boundType, choice, refType, openedFullType)) - break; - - LLVM_FALLTHROUGH; - case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::DeclViaUnwrappedOptional: case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: { - // Retrieve the type of a reference to the specific declaration choice. - if (auto baseTy = choice.getBaseType()) { + // If we refer to a top-level decl with special type-checking semantics, + // handle it now. + const auto semantics = + TypeChecker::getDeclTypeCheckingSemantics(choice.getDecl()); + if (semantics != DeclTypeCheckingSemantics::Normal) { + std::tie(openedFullType, refType) = + getTypeOfReferenceWithSpecialTypeCheckingSemantics(*this, locator, + semantics); + // Declarations with special type checking semantics do not require + // any further adjustments to the constraint system. Break out of + // here so we don't do any more work. + break; + } else if (auto baseTy = choice.getBaseType()) { + // Retrieve the type of a reference to the specific declaration choice. assert(!baseTy->hasTypeParameter()); auto getDotBase = [](const Expr *E) -> const DeclRefExpr * { @@ -1824,7 +2267,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, auto base = getDotBase(anchor); std::tie(openedFullType, refType) = getTypeOfMemberReference(baseTy, choice.getDecl(), useDC, - isDynamicResult, + (kind == OverloadChoiceKind::DeclViaDynamic), choice.getFunctionRefKind(), locator, base, nullptr); } else { @@ -1841,255 +2284,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // Subscript declarations are handled within // getTypeOfMemberReference(); their result types are unchecked // optional. - if (isDynamicResult) { - if (isa(choice.getDecl())) { - // We always expect function type for subscripts. - auto fnTy = refType->castTo(); - if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { - auto resultTy = fnTy->getResult(); - // We expect the element type to be a double-optional. - auto optTy = resultTy->getOptionalObjectType(); - assert(optTy->getOptionalObjectType()); - - // For our original type T -> U?? we will generate: - // A disjunction V = { U?, U } - // and a disjunction boundType = { T -> V?, T -> V } - Type ty = createTypeVariable(locator, TVO_CanBindToNoEscape); - - buildDisjunctionForImplicitlyUnwrappedOptional(ty, optTy, locator); - - // Create a new function type with an optional of this type - // variable as the result type. - if (auto *genFnTy = fnTy->getAs()) { - fnTy = GenericFunctionType::get( - genFnTy->getGenericSignature(), genFnTy->getParams(), - OptionalType::get(ty), genFnTy->getExtInfo()); - } else { - fnTy = FunctionType::get(fnTy->getParams(), OptionalType::get(ty), - fnTy->getExtInfo()); - } - } - - buildDisjunctionForDynamicLookupResult(boundType, fnTy, locator); - } else { - Type ty = refType; - - // If this is something we need to implicitly unwrap, set up a - // new type variable and disjunction that will allow us to make - // the choice of whether to do so. - if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { - // Duplicate the structure of boundType, with fresh type - // variables. We'll create a binding disjunction using this, - // selecting between options for refType, which is either - // Optional or a function type returning Optional. - assert(boundType->hasTypeVariable()); - ty = boundType.transform([this](Type elTy) -> Type { - if (auto *tv = dyn_cast(elTy.getPointer())) { - return createTypeVariable(tv->getImpl().getLocator(), - tv->getImpl().getRawOptions()); - } - return elTy; - }); - - buildDisjunctionForImplicitlyUnwrappedOptional( - ty, refType->getRValueType(), locator); - } - - // Build the disjunction to attempt binding both T? and T (or - // function returning T? and function returning T). - buildDisjunctionForDynamicLookupResult( - boundType, OptionalType::get(ty->getRValueType()), locator); - - // We store an Optional of the originally resolved type in the - // overload set. - refType = OptionalType::get(refType->getRValueType()); - } - - bindConstraintCreated = true; - } else if (!isRequirementOrWitness(locator) && - choice.getDecl()->getAttrs().hasAttribute() && - !isa(choice.getDecl())) { - // For a non-subscript declaration that is an optional - // requirement in a protocol, strip off the lvalue-ness (FIXME: - // one cannot assign to such declarations for now) and make a - // reference to that declaration be optional. - // - // Subscript declarations are handled within - // getTypeOfMemberReference(); their result types are optional. - - // Deal with values declared as implicitly unwrapped, or - // functions with return types that are implicitly unwrapped. - if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { - // Build the disjunction to attempt binding both T? and T (or - // function returning T? and function returning T). - Type ty = createTypeVariable(locator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType, locator); - addConstraint(ConstraintKind::Bind, boundType, - OptionalType::get(ty->getRValueType()), locator); - bindConstraintCreated = true; - } - - refType = OptionalType::get(refType->getRValueType()); - } - // If the declaration is unavailable, note that in the score. - if (choice.getDecl()->getAttrs().isUnavailable(getASTContext())) { - increaseScore(SK_Unavailable); - } - - if (kind == OverloadChoiceKind::DynamicMemberLookup) { - // DynamicMemberLookup results are always a (dynamicMember:T1)->T2 - // subscript. - auto refFnType = refType->castTo(); - - // If this is a dynamic member lookup, then the decl we have is for the - // subscript(dynamicMember:) member, but the type we need to return is the - // result of the subscript. Dig through it. - refType = refFnType->getResult(); - - // Before we drop the argument type on the floor, we need to constrain it - // to having a literal conformance to ExpressibleByStringLiteral. This - // makes the index default to String if otherwise unconstrained. - assert(refFnType->getParams().size() == 1 && - "subscript always has one arg"); - auto argType = refFnType->getParams()[0].getPlainType(); - - auto &TC = getTypeChecker(); - - auto stringLiteral = - TC.getProtocol(choice.getDecl()->getLoc(), - KnownProtocolKind::ExpressibleByStringLiteral); - if (!stringLiteral) - break; - - addConstraint(ConstraintKind::LiteralConformsTo, argType, - stringLiteral->getDeclaredType(), locator); - - // If this is used inside of the keypath expression, we need to make - // sure that argument is Hashable. - if (isa(locator->getAnchor())) - verifyThatArgumentIsHashable(0, argType, locator); - } - - if (kind == OverloadChoiceKind::KeyPathDynamicMemberLookup) { - auto *fnType = refType->castTo(); - assert(fnType->getParams().size() == 1 && - "subscript always has one argument"); - // Parameter type is KeyPath where `T` is a root type - // and U is a leaf type (aka member type). - auto keyPathTy = - fnType->getParams()[0].getPlainType()->castTo(); - - refType = fnType->getResult(); - - auto *keyPathDecl = keyPathTy->getAnyNominal(); - assert(isKnownKeyPathDecl(getASTContext(), keyPathDecl) && - "parameter is supposed to be a keypath"); - - auto *keyPathLoc = getConstraintLocator( - locator, LocatorPathElt::KeyPathDynamicMember(keyPathDecl)); - - auto rootTy = keyPathTy->getGenericArgs()[0]; - auto leafTy = keyPathTy->getGenericArgs()[1]; - - // Member would either point to mutable or immutable property, we - // don't which at the moment, so let's allow its type to be l-value. - auto memberTy = createTypeVariable(keyPathLoc, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - // Attempt to lookup a member with a give name in the root type and - // assign result to the leaf type of the keypath. - bool isSubscriptRef = locator->isSubscriptMemberRef(); - DeclName memberName = - isSubscriptRef ? DeclBaseName::createSubscript() : choice.getName(); - - addValueMemberConstraint(LValueType::get(rootTy), memberName, memberTy, - useDC, - isSubscriptRef ? FunctionRefKind::DoubleApply - : FunctionRefKind::Unapplied, - /*outerAlternatives=*/{}, keyPathLoc); - - // In case of subscript things are more compicated comparing to "dot" - // syntax, because we have to get "applicable function" constraint - // associated with index expression and re-bind it to match "member type" - // looked up by dynamically. - if (isSubscriptRef) { - // Make sure that regular subscript declarations (if any) are - // preferred over key path dynamic member lookup. - increaseScore(SK_KeyPathSubscript); - - auto dynamicResultTy = boundType->castTo(); - auto constraints = CG.gatherConstraints( - dynamicResultTy, - ConstraintGraph::GatheringKind::EquivalenceClass, - [](Constraint *constraint) { - return constraint->getKind() == - ConstraintKind::ApplicableFunction; - }); - - assert(constraints.size() == 1); - auto *applicableFn = constraints.front(); - retireConstraint(applicableFn); - - // Original subscript expression e.g. `[0]` generated following - // constraint `($T_A0, [$T_A1], ...) -> $T_R applicable fn $T_S` where - // `$T_S` is supposed to be bound to each subscript choice e.g. - // `(Int) -> Int`. - // - // Here is what we need to do to make this work as-if expression was - // `[dynamicMember: \.[0]]`: - // - Right-hand side function type would have to get a new result type - // since it would have to point to result type of `\.[0]`, arguments - // though should stay the same. - // - Left-hand side `$T_S` is going to point to a new "member type" - // we are looking up based on the root type of the key path. - // - Original result type `$T_R` is going to represent result of - // the `[dynamicMember: \.[0]]` invocation. - - // Result of the `WritableKeyPath` is going to be l-value type, - // let's adjust l-valueness of the result type to accommodate that. - // - // This is required because we are binding result of the subscript - // to its "member type" which becomes dynamic result type. We could - // form additional `applicable fn` constraint here and bind it to a - // function type, but it would create inconsistency with how properties - // are handled, which means more special handling in CSApply. - if (keyPathDecl == getASTContext().getWritableKeyPathDecl() || - keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) - dynamicResultTy->getImpl().setCanBindToLValue(getSavedBindings(), - /*enabled=*/true); - - auto fnType = applicableFn->getFirstType()->castTo(); - - auto subscriptResultTy = createTypeVariable( - getConstraintLocator(locator->getAnchor(), - ConstraintLocator::FunctionResult), - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - - auto adjustedFnTy = - FunctionType::get(fnType->getParams(), subscriptResultTy); - - addConstraint(ConstraintKind::ApplicableFunction, adjustedFnTy, - memberTy, applicableFn->getLocator()); - - addConstraint(ConstraintKind::Bind, dynamicResultTy, - fnType->getResult(), keyPathLoc); - - addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy, - keyPathLoc); - } else { - // Since member type is going to be bound to "leaf" generic parameter - // of the keypath, it has to be an r-value always, so let's add a new - // constraint to represent that conversion instead of loading member - // type into "leaf" directly. - addConstraint(ConstraintKind::Equal, memberTy, leafTy, keyPathLoc); - } - - if (isa(locator->getAnchor())) - verifyThatArgumentIsHashable(0, keyPathTy, locator); - } + std::tie(refType, bindConstraintCreated) = + adjustTypeOfOverloadReference(choice, locator, boundType, refType); break; } @@ -2109,7 +2305,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, refType = tuple->getElementType(choice.getTupleIndex())->getRValueType(); } break; - + case OverloadChoiceKind::KeyPathApplication: { // Key path application looks like a subscript(keyPath: KeyPath). // The element type is T or @lvalue T based on the key path subtype and @@ -2128,7 +2324,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // The element result is an lvalue or rvalue based on the key path class. addKeyPathApplicationConstraint( keyPathIndexTy, choice.getBaseType(), elementTy, locator); - + FunctionType::Param indices[] = { FunctionType::Param(keyPathIndexTy, getASTContext().Id_keyPath), }; @@ -2145,13 +2341,13 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } } assert(!refType->hasTypeParameter() && "Cannot have a dependent type here"); - + if (auto *decl = choice.getDeclOrNull()) { - // If we're binding to an init member, the 'throws' need to line up between - // the bound and reference types. + // If we're binding to an init member, the 'throws' need to line up + // between the bound and reference types. if (auto CD = dyn_cast(decl)) { auto boundFunctionType = boundType->getAs(); - + if (boundFunctionType && CD->hasThrows() != boundFunctionType->throws()) { boundType = boundFunctionType->withExtInfo( @@ -2169,7 +2365,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // Hashable, because it would be used as a component inside key path. for (auto index : indices(subscriptTy->getParams())) { const auto ¶m = subscriptTy->getParams()[index]; - verifyThatArgumentIsHashable(index, param.getPlainType(), locator); + verifyThatArgumentIsHashable(index, param.getParameterType(), locator); } } } @@ -2211,33 +2407,25 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } // Note that we have resolved this overload. - resolvedOverloadSets - = new (*this) ResolvedOverloadSetListItem{resolvedOverloadSets, - boundType, - choice, - locator, - openedFullType, - refType}; + auto overload = SelectedOverload{choice, openedFullType, refType, boundType}; + auto result = ResolvedOverloads.insert({locator, overload}); + assert(result.second && "Already resolved this overload?"); + (void)result; // In some cases we already created the appropriate bind constraints. if (!bindConstraintCreated) { - if (choice.isImplicitlyUnwrappedValueOrReturnValue()) { - // Build the disjunction to attempt binding both T? and T (or - // function returning T? and function returning T). - buildDisjunctionForImplicitlyUnwrappedOptional(boundType, refType, - locator); - } else { - // Add the type binding constraint. - addConstraint(ConstraintKind::Bind, boundType, refType, locator); - } + bindOverloadType(overload, boundType, locator, useDC, + verifyThatArgumentIsHashable); } - if (TC.getLangOpts().DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { + PrintOptions PO; + PO.PrintTypesForDebugging = true; auto &log = getASTContext().TypeCheckerDebug->getStream(); log.indent(solverState ? solverState->depth * 2 : 2) << "(overload set choice binding " - << boundType->getString() << " := " - << refType->getString() << ")\n"; + << boundType->getString(PO) << " := " + << refType->getString(PO) << ")\n"; } // If this overload is disfavored, note that. @@ -2247,8 +2435,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } } -template -Type simplifyTypeImpl(ConstraintSystem &cs, Type type, Fn getFixedTypeFn) { +Type ConstraintSystem::simplifyTypeImpl(Type type, + llvm::function_ref getFixedTypeFn) const { return type.transform([&](Type type) -> Type { if (auto tvt = dyn_cast(type.getPointer())) return getFixedTypeFn(tvt); @@ -2257,7 +2445,7 @@ Type simplifyTypeImpl(ConstraintSystem &cs, Type type, Fn getFixedTypeFn) { // the base to a non-type-variable, perform lookup. if (auto depMemTy = dyn_cast(type.getPointer())) { // Simplify the base. - Type newBase = simplifyTypeImpl(cs, depMemTy->getBase(), getFixedTypeFn); + Type newBase = simplifyTypeImpl(depMemTy->getBase(), getFixedTypeFn); // If nothing changed, we're done. if (newBase.getPointer() == depMemTy->getBase().getPointer()) @@ -2275,13 +2463,13 @@ Type simplifyTypeImpl(ConstraintSystem &cs, Type type, Fn getFixedTypeFn) { if (lookupBaseType->mayHaveMembers()) { auto *proto = assocType->getProtocol(); - auto conformance = cs.DC->getParentModule()->lookupConformance( + auto conformance = DC->getParentModule()->lookupConformance( lookupBaseType, proto); if (!conformance) return DependentMemberType::get(lookupBaseType, assocType); auto subs = SubstitutionMap::getProtocolSubstitutions( - proto, lookupBaseType, *conformance); + proto, lookupBaseType, conformance); auto result = assocType->getDeclaredInterfaceType().subst(subs); if (!result->hasError()) return result; @@ -2294,13 +2482,12 @@ Type simplifyTypeImpl(ConstraintSystem &cs, Type type, Fn getFixedTypeFn) { }); } -Type ConstraintSystem::simplifyType(Type type) { +Type ConstraintSystem::simplifyType(Type type) const { if (!type->hasTypeVariable()) return type; // Map type variables down to the fixed types of their representatives. - return simplifyTypeImpl( - *this, type, + return simplifyTypeImpl(type, [&](TypeVariableType *tvt) -> Type { if (auto fixed = getFixedType(tvt)) return simplifyType(fixed); @@ -2314,8 +2501,7 @@ Type Solution::simplifyType(Type type) const { return type; // Map type variables to fixed types from bindings. - return simplifyTypeImpl( - getConstraintSystem(), type, + return getConstraintSystem().simplifyTypeImpl(type, [&](TypeVariableType *tvt) -> Type { auto known = typeBindings.find(tvt); assert(known != typeBindings.end()); @@ -2340,14 +2526,14 @@ DeclName OverloadChoice::getName() const { case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaUnwrappedOptional: return getDecl()->getFullName(); - + case OverloadChoiceKind::KeyPathApplication: // TODO: This should probably produce subscript(keyPath:), but we // don't currently pre-filter subscript overload sets by argument // keywords, so "subscript" is still the name that keypath subscripts // are looked up by. return DeclBaseName::createSubscript(); - + case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: return DeclName(DynamicMember.getPointer()); @@ -2356,7 +2542,7 @@ DeclName OverloadChoice::getName() const { case OverloadChoiceKind::TupleIndex: llvm_unreachable("no name!"); } - + llvm_unreachable("Unhandled OverloadChoiceKind in switch."); } @@ -2383,17 +2569,21 @@ bool OverloadChoice::isImplicitlyUnwrappedValueOrReturnValue() const { llvm_unreachable("unhandled kind"); } -bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { - if (TC.getLangOpts().DebugConstraintSolver) { - auto &log = TC.Context.TypeCheckerDebug->getStream(); +SolutionResult ConstraintSystem::salvage() { + auto &ctx = getASTContext(); + if (ctx.TypeCheckerOpts.DebugConstraintSolver) { + auto &log = ctx.TypeCheckerDebug->getStream(); log << "---Attempting to salvage and emit diagnostics---\n"; } + setPhase(ConstraintSystemPhase::Diagnostics); + // Attempt to solve again, capturing all states that come from our attempts to // select overloads or bind type variables. // // FIXME: can this be removed? We need to arrange for recordFixes to be // eliminated. + SmallVector viable; viable.clear(); { @@ -2402,7 +2592,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { state.recordFixes = true; // Solve the system. - solve(viable); + solveImpl(viable); // Check whether we have a best solution; this can happen if we found // a series of fixes that worked. @@ -2410,13 +2600,13 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { if (*best != 0) viable[0] = std::move(viable[*best]); viable.erase(viable.begin() + 1, viable.end()); - return false; + return SolutionResult::forSolved(std::move(viable[0])); } // Before removing any "fixed" solutions, let's check // if ambiguity is caused by fixes and diagnose if possible. - if (diagnoseAmbiguityWithFixes(expr, viable)) - return true; + if (diagnoseAmbiguityWithFixes(viable)) + return SolutionResult::forAmbiguous(viable); // FIXME: If we were able to actually fix things along the way, // we may have to hunt for the best solution. For now, we don't care. @@ -2429,7 +2619,7 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { // If there are multiple solutions, try to diagnose an ambiguity. if (viable.size() > 1) { - if (getASTContext().LangOpts.DebugConstraintSolver) { + if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Ambiguity error: " << viable.size() << " solutions found---\n"; @@ -2441,59 +2631,76 @@ bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { } } - if (diagnoseAmbiguity(expr, viable)) { - return true; + if (diagnoseAmbiguity(viable)) { + return SolutionResult::forAmbiguous(viable); } } // Fall through to produce diagnostics. } - if (getExpressionTooComplex(viable)) { - TC.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); - return true; - } + if (getExpressionTooComplex(viable)) + return SolutionResult::forTooComplex(); - // If all else fails, diagnose the failure by looking through the system's - // constraints. - diagnoseFailureForExpr(expr); - return true; + // Could not produce a specific diagnostic; punt to the client. + return SolutionResult::forUndiagnosedError(); } static void diagnoseOperatorAmbiguity(ConstraintSystem &cs, Identifier operatorName, ArrayRef solutions, ConstraintLocator *locator) { - auto &TC = cs.getTypeChecker(); + auto &DE = cs.getASTContext().Diags; auto *anchor = locator->getAnchor(); auto *applyExpr = dyn_cast_or_null(cs.getParentExpr(anchor)); if (!applyExpr) return; + auto isNameOfStandardComparisonOperator = [](Identifier opName) -> bool { + return opName.is("==") || opName.is("!=") || opName.is("===") || + opName.is("!==") || opName.is("<") || opName.is(">") || + opName.is("<=") || opName.is(">="); + }; + + auto isEnumWithAssociatedValues = [](Type type) -> bool { + if (auto *enumType = type->getAs()) + return !enumType->getDecl()->hasOnlyCasesWithoutAssociatedValues(); + return false; + }; + const auto &solution = solutions.front(); if (auto *binaryOp = dyn_cast(applyExpr)) { auto *lhs = binaryOp->getArg()->getElement(0); auto *rhs = binaryOp->getArg()->getElement(1); - auto lhsType = solution.simplifyType(cs.getType(lhs))->getRValueType(); - auto rhsType = solution.simplifyType(cs.getType(rhs))->getRValueType(); + auto lhsType = + solution.simplifyType(solution.getType(lhs))->getRValueType(); + auto rhsType = + solution.simplifyType(solution.getType(rhs))->getRValueType(); if (lhsType->isEqual(rhsType)) { - TC.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_same_args, + DE.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_same_args, operatorName.str(), lhsType) .highlight(lhs->getSourceRange()) .highlight(rhs->getSourceRange()); + + if (isNameOfStandardComparisonOperator(operatorName) && + isEnumWithAssociatedValues(lhsType)) { + DE.diagnose(applyExpr->getLoc(), + diag::no_binary_op_overload_for_enum_with_payload, + operatorName.str()); + return; + } } else { - TC.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_args, + DE.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_args, operatorName.str(), lhsType, rhsType) .highlight(lhs->getSourceRange()) .highlight(rhs->getSourceRange()); } } else { - auto argType = solution.simplifyType(cs.getType(applyExpr->getArg())); - TC.diagnose(anchor->getLoc(), diag::cannot_apply_unop_to_arg, + auto argType = solution.simplifyType(solution.getType(applyExpr->getArg())); + DE.diagnose(anchor->getLoc(), diag::cannot_apply_unop_to_arg, operatorName.str(), argType->getRValueType()); } @@ -2513,41 +2720,87 @@ static void diagnoseOperatorAmbiguity(ConstraintSystem &cs, FunctionType::getParamListAsString(fnType->getParams())); } - TC.diagnose(anchor->getLoc(), diag::suggest_partial_overloads, + // All of the overload choices had generic parameters like `Self`. + if (parameters.empty()) + return; + + DE.diagnose(anchor->getLoc(), diag::suggest_partial_overloads, /*isResult=*/false, operatorName.str(), llvm::join(parameters, ", ")); } bool ConstraintSystem::diagnoseAmbiguityWithFixes( - Expr *expr, ArrayRef solutions) { + SmallVectorImpl &solutions) { if (solutions.empty()) return false; + if (auto bestScore = solverState->BestScore) { + solutions.erase(llvm::remove_if(solutions, + [&](const Solution &solution) { + return solution.getFixedScore() > + *bestScore; + }), + solutions.end()); + + if (llvm::all_of(solutions, [&](const Solution &solution) { + auto score = solution.getFixedScore(); + return score.Data[SK_Fix] == 0 && solution.Fixes.empty(); + })) + return false; + } + // Problems related to fixes forming ambiguous solution set // could only be diagnosed (at the moment), if all of the fixes // have the same callee locator, which means they fix different // overloads of the same declaration. ConstraintLocator *commonCalleeLocator = nullptr; SmallPtrSet distinctChoices; - SmallVector, 4> - viableSolutions; + SmallVector viableSolutions; + + enum class AmbiguityKind { + /// There is exactly one fix associated with each candidate. + CloseMatch, + /// Solution is ambiguous because all candidates had partially matching + /// parameter lists. + ParameterList, + /// General ambiguity failure. + General, + }; + auto ambiguityKind = AmbiguityKind::CloseMatch; bool diagnosable = llvm::all_of(solutions, [&](const Solution &solution) { ArrayRef fixes = solution.Fixes; - // Currently only support a single fix in a solution, - // but ultimately should be able to deal with multiple. - if (fixes.size() != 1) + if (fixes.empty()) return false; - const auto *fix = fixes.front(); - auto *calleeLocator = getCalleeLocator(fix->getLocator()); - if (commonCalleeLocator && commonCalleeLocator != calleeLocator) - return false; + if (fixes.size() > 1) { + ambiguityKind = (ambiguityKind == AmbiguityKind::CloseMatch || + ambiguityKind == AmbiguityKind::ParameterList) && + llvm::all_of(fixes, [](const ConstraintFix *fix) -> bool { + auto *locator = fix->getLocator(); + return locator->findLast().hasValue(); + }) ? AmbiguityKind::ParameterList + : AmbiguityKind::General; + } + + for (const auto *fix: fixes) { + auto *locator = fix->getLocator(); + // Assignment failures are all about the source expression, + // because they treat destination as a contextual type. + if (auto *anchor = locator->getAnchor()) { + if (auto *assignExpr = dyn_cast(anchor)) + locator = getConstraintLocator(assignExpr->getSrc()); + } - commonCalleeLocator = calleeLocator; + auto *calleeLocator = solution.getCalleeLocator(locator); + if (!commonCalleeLocator) + commonCalleeLocator = calleeLocator; + else if (commonCalleeLocator != calleeLocator) + return false; + } - auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator); + auto overload = solution.getOverloadChoiceIfAvailable(commonCalleeLocator); if (!overload) return false; @@ -2559,7 +2812,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // as viable, otherwise we'd produce the same diagnostic multiple // times, which means that actual problem is elsewhere. if (distinctChoices.insert(decl).second) - viableSolutions.push_back({&solution, fix}); + viableSolutions.push_back(&solution); return true; }); @@ -2571,17 +2824,13 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( bool diagnosed = true; { - DiagnosticTransaction transaction(TC.Diags); + DiagnosticTransaction transaction(getASTContext().Diags); - const auto *fix = viableSolutions.front().second; auto *commonAnchor = commonCalleeLocator->getAnchor(); - if (fix->getKind() == FixKind::UseSubscriptOperator) { - auto *UDE = cast(commonAnchor); - TC.diagnose(commonAnchor->getLoc(), - diag::could_not_find_subscript_member_did_you_mean, - getType(UDE->getBase())); - } else { - auto name = decl->getFullName(); + auto &DE = getASTContext().Diags; + auto name = decl->getFullName(); + + auto emitGeneralAmbiguityFailure = [&]() { // Three choices here: // 1. If this is a special name avoid printing it because // printing kind is sufficient; @@ -2589,7 +2838,7 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // 3. If labels in different choices are different, it means // that we can only print a base name. if (name.isSpecial()) { - TC.diagnose(commonAnchor->getLoc(), + DE.diagnose(commonAnchor->getLoc(), diag::no_overloads_match_exactly_in_call_special, decl->getDescriptiveKind()); } else if (name.isOperator()) { @@ -2600,22 +2849,90 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( [&name](const ValueDecl *choice) { return choice->getFullName() == name; })) { - TC.diagnose(commonAnchor->getLoc(), + DE.diagnose(commonAnchor->getLoc(), diag::no_overloads_match_exactly_in_call, decl->getDescriptiveKind(), name); } else { - TC.diagnose(commonAnchor->getLoc(), + DE.diagnose(commonAnchor->getLoc(), diag::no_overloads_match_exactly_in_call_no_labels, decl->getDescriptiveKind(), name.getBaseName()); } + }; + + switch (ambiguityKind) { + case AmbiguityKind::CloseMatch: + // Handled below + break; + case AmbiguityKind::ParameterList: { + emitGeneralAmbiguityFailure(); + + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *fn = overload.openedType->getAs(); + assert(fn); + DE.diagnose(overload.choice.getDecl()->getLoc(), + diag::candidate_partial_match, + fn->getParamListAsString(fn->getParams())); + } + + return true; + } + case AmbiguityKind::General: { + emitGeneralAmbiguityFailure(); + + // Notes for operators are diagnosed through emitGeneralAmbiguityFailure + if (name.isOperator()) + return true; + + llvm::SmallSet candidateTypes; + for (const auto &viable: viableSolutions) { + auto overload = viable->getOverloadChoice(commonCalleeLocator); + auto *decl = overload.choice.getDecl(); + auto type = viable->simplifyType(overload.openedType); + if (decl->getLoc().isInvalid()) { + if (candidateTypes.insert(type->getCanonicalType()).second) + DE.diagnose(commonAnchor->getLoc(), diag::found_candidate_type, type); + } else { + DE.diagnose(decl->getLoc(), diag::found_candidate); + } + } + + return true; + } + } + + auto *fix = viableSolutions.front()->Fixes.front(); + if (fix->getKind() == FixKind::UseSubscriptOperator) { + auto *UDE = cast(commonAnchor); + DE.diagnose(commonAnchor->getLoc(), + diag::could_not_find_subscript_member_did_you_mean, + getType(UDE->getBase())); + } else if (fix->getKind() == FixKind::TreatRValueAsLValue) { + DE.diagnose(commonAnchor->getLoc(), + diag::no_overloads_match_exactly_in_assignment, + decl->getBaseName()); + } else if (llvm::all_of( + viableSolutions, + [](const Solution *viable) { + auto *locator = viable->Fixes.front()->getLocator(); + return locator + ->isLastElement(); + })) { + auto anchor = + viableSolutions.front()->Fixes.front()->getLocator()->getAnchor(); + auto baseName = name.getBaseName(); + DE.diagnose(commonAnchor->getLoc(), diag::no_candidates_match_result_type, + baseName.userFacingName(), getContextualType(anchor)); + } else { + emitGeneralAmbiguityFailure(); } for (const auto &viable : viableSolutions) { // Create scope so each applied solution is rolled back. ConstraintSystem::SolverScope scope(*this); - applySolution(*viable.first); + applySolution(*viable); // All of the solutions supposed to produce a "candidate" note. - diagnosed &= viable.second->diagnose(expr, /*asNote*/ true); + diagnosed &= viable->Fixes.front()->diagnose(/*asNote*/ true); } // If not all of the fixes produced a note, we can't diagnose this. @@ -2662,8 +2979,30 @@ static DeclName getOverloadChoiceName(ArrayRef choices) { return name; } -bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, - ArrayRef solutions) { +/// Extend the given index map with all of the subexpressions in the given +/// expression. +static void extendPreorderIndexMap( + Expr *expr, llvm::DenseMap &indexMap) { + class RecordingTraversal : public ASTWalker { + public: + llvm::DenseMap &IndexMap; + unsigned Index = 0; + + explicit RecordingTraversal(llvm::DenseMap &indexMap) + : IndexMap(indexMap) { } + + std::pair walkToExprPre(Expr *E) override { + IndexMap[E] = Index; + Index++; + return { true, E }; + } + }; + + RecordingTraversal traversal(indexMap); + expr->walk(traversal); +} + +bool ConstraintSystem::diagnoseAmbiguity(ArrayRef solutions) { // Produce a diff of the solutions. SolutionDiff diff(solutions); @@ -2682,8 +3021,10 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, // Heuristically, all other things being equal, we should complain about the // ambiguous expression that (1) has the most overloads, (2) is deepest, or // (3) comes earliest in the expression. - auto depthMap = expr->getDepthMap(); - auto indexMap = expr->getPreorderIndexMap(); + llvm::DenseMap indexMap; + for (auto expr : InputExprs) { + extendPreorderIndexMap(expr, indexMap); + } for (unsigned i = 0, n = diff.overloads.size(); i != n; ++i) { auto &overload = diff.overloads[i]; @@ -2698,10 +3039,10 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, continue; unsigned index = it->second; - auto e = depthMap.find(anchor); - if (e == depthMap.end()) + auto optDepth = getExprDepth(anchor); + if (!optDepth) continue; - unsigned depth = e->second.first; + unsigned depth = *optDepth; // If we don't have a name to hang on to, it'll be hard to diagnose this // overload. @@ -2730,17 +3071,19 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, // depth-first numbering of expressions. if (bestOverload) { auto &overload = diff.overloads[*bestOverload]; - auto name = getOverloadChoiceName(overload.choices); + // FIXME: We would prefer to emit the name as written, but that information + // is not sufficiently centralized in the AST. + DeclNameRef name(getOverloadChoiceName(overload.choices)); auto anchor = simplifyLocatorToAnchor(overload.locator); // Emit the ambiguity diagnostic. - auto &tc = getTypeChecker(); - tc.diagnose(anchor->getLoc(), + auto &DE = getASTContext().Diags; + DE.diagnose(anchor->getLoc(), name.isOperator() ? diag::ambiguous_operator_ref : diag::ambiguous_decl_ref, name); - TrailingClosureAmbiguityFailure failure(expr, *this, anchor, + TrailingClosureAmbiguityFailure failure(*this, anchor, overload.choices); if (failure.diagnoseAsNote()) return true; @@ -2757,7 +3100,7 @@ bool ConstraintSystem::diagnoseAmbiguity(Expr *expr, case OverloadChoiceKind::DeclViaUnwrappedOptional: // FIXME: show deduced types, etc, etc. if (EmittedDecls.insert(choice.getDecl()).second) - tc.diagnose(choice.getDecl(), diag::found_candidate); + DE.diagnose(choice.getDecl(), diag::found_candidate); break; case OverloadChoiceKind::KeyPathApplication: @@ -2839,6 +3182,11 @@ void constraints::simplifyLocator(Expr *&anchor, break; } + case ConstraintLocator::DynamicCallable: { + path = path.slice(1); + continue; + } + case ConstraintLocator::ApplyFunction: // Extract application function. if (auto applyExpr = dyn_cast(anchor)) { @@ -2869,8 +3217,9 @@ void constraints::simplifyLocator(Expr *&anchor, case ConstraintLocator::LValueConversion: case ConstraintLocator::RValueAdjustment: case ConstraintLocator::UnresolvedMember: - // Arguments in autoclosure positions, lvalue and rvalue adjustments, and - // scalar-to-tuple conversions, and unresolved members are + case ConstraintLocator::ImplicitCallAsFunction: + // Arguments in autoclosure positions, lvalue and rvalue adjustments, + // unresolved members, and implicit callAsFunction references are // implicit. path = path.slice(1); continue; @@ -2984,6 +3333,21 @@ void constraints::simplifyLocator(Expr *&anchor, break; } + case ConstraintLocator::Condition: { + anchor = cast(anchor)->getCondExpr(); + path = path.slice(1); + continue; + } + + case ConstraintLocator::TernaryBranch: { + auto branch = path[0].castTo(); + auto *ifExpr = cast(anchor); + + anchor = branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr(); + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; @@ -3027,8 +3391,12 @@ Expr *constraints::getArgumentExpr(Expr *expr, unsigned index) { return PE->getSubExpr(); } - assert(isa(argExpr)); - return cast(argExpr)->getElement(index); + if (auto *tuple = dyn_cast(argExpr)) { + return (tuple->getNumElements() > index) ? tuple->getElement(index) + : nullptr; + } + + return nullptr; } bool constraints::isAutoClosureArgument(Expr *argExpr) { @@ -3061,29 +3429,29 @@ bool constraints::hasAppliedSelf(ConstraintSystem &cs, bool constraints::conformsToKnownProtocol(ConstraintSystem &cs, Type type, KnownProtocolKind protocol) { - if (auto *proto = cs.TC.getProtocol(SourceLoc(), protocol)) - return bool(TypeChecker::conformsToProtocol( - type, proto, cs.DC, ConformanceCheckFlags::InExpression)); + if (auto *proto = + TypeChecker::getProtocol(cs.getASTContext(), SourceLoc(), protocol)) + return (bool)TypeChecker::conformsToProtocol( + type, proto, cs.DC, ConformanceCheckFlags::InExpression); return false; } /// Check whether given type conforms to `RawPepresentable` protocol /// and return the witness type. Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) { - auto &TC = cs.TC; auto *DC = cs.DC; - auto rawReprType = - TC.getProtocol(SourceLoc(), KnownProtocolKind::RawRepresentable); + auto rawReprType = TypeChecker::getProtocol( + cs.getASTContext(), SourceLoc(), KnownProtocolKind::RawRepresentable); if (!rawReprType) return Type(); auto conformance = TypeChecker::conformsToProtocol( type, rawReprType, DC, ConformanceCheckFlags::InExpression); - if (!conformance) + if (conformance.isInvalid()) return Type(); - return conformance->getTypeWitnessByName(type, TC.Context.Id_RawValue); + return conformance.getTypeWitnessByName(type, cs.getASTContext().Id_RawValue); } Type constraints::isRawRepresentable( @@ -3145,11 +3513,29 @@ ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) { if (!anchor) return nullptr; + // Applies and unresolved member exprs can have callee locators that are + // dependent on the type of their function, which may not have been resolved + // yet. Therefore we need to handle them specially. if (auto *apply = dyn_cast(anchor)) { auto *fnExpr = getArgumentLabelTargetExpr(apply->getFn()); return getConstraintLocator(fnExpr); } + if (auto *UME = dyn_cast(anchor)) + return getConstraintLocator(UME); + + auto path = locator->getPath(); + { + // If this is for a dynamic member reference, the argument info is for the + // original call-site, which we can get by stripping away the + // KeyPathDynamicMember elements. + auto iter = path.begin(); + if (locator->findFirst(iter)) { + ArrayRef newPath(path.begin(), iter); + return getConstraintLocator(anchor, newPath); + } + } + return getCalleeLocator(locator); } @@ -3166,6 +3552,165 @@ ConstraintSystem::getArgumentInfo(ConstraintLocator *locator) { return None; } +/// Given an apply expr, returns true if it is expected to have a direct callee +/// overload, resolvable using `getChoiceFor`. Otherwise, returns false. +static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) { + auto *fnExpr = callExpr->getDirectCallee(); + + // An apply of an apply/subscript doesn't have a direct callee. + if (isa(fnExpr) || isa(fnExpr)) + return false; + + // Applies of closures don't have callee overloads. + if (isa(fnExpr)) + return false; + + // No direct callee for a try!/try?. + if (isa(fnExpr) || isa(fnExpr)) + return false; + + // If we have an intermediate cast, there's no direct callee. + if (isa(fnExpr)) + return false; + + // No direct callee for an if expr. + if (isa(fnExpr)) + return false; + + // Assume that anything else would have a direct callee. + return true; +} + +Type ConstraintSystem::resolveInterfaceType(Type type) const { + auto resolvedType = type.transform([&](Type type) -> Type { + if (auto *tvt = type->getAs()) { + // If this type variable is for a generic parameter, return that. + if (auto *gp = tvt->getImpl().getGenericParameter()) + return gp; + + // Otherwise resolve its fixed type, mapped out of context. + if (auto fixed = getFixedType(tvt)) + return resolveInterfaceType(fixed->mapTypeOutOfContext()); + + return getRepresentative(tvt); + } + if (auto *dmt = type->getAs()) { + // For a dependent member, first resolve the base. + auto newBase = resolveInterfaceType(dmt->getBase()); + + // Then reconstruct using its associated type. + assert(dmt->getAssocType()); + return DependentMemberType::get(newBase, dmt->getAssocType()); + } + return type; + }); + + assert(!resolvedType->hasArchetype()); + return resolvedType; +} + +Optional +ConstraintSystem::getFunctionArgApplyInfo(ConstraintLocator *locator) { + auto *anchor = locator->getAnchor(); + auto path = locator->getPath(); + + // Look for the apply-arg-to-param element in the locator's path. We may + // have to look through other elements that are generated from an argument + // conversion such as GenericArgument for an optional-to-optional conversion, + // and OptionalPayload for a value-to-optional conversion. + auto iter = path.rbegin(); + auto applyArgElt = locator->findLast(iter); + if (!applyArgElt) + return None; + + auto nextIter = iter + 1; + assert(!locator->findLast(nextIter) && + "Multiple ApplyArgToParam components?"); + + // Form a new locator that ends at the apply-arg-to-param element, and + // simplify it to get the full argument expression. + auto argPath = path.drop_back(iter - path.rbegin()); + auto *argLocator = getConstraintLocator( + anchor, argPath, ConstraintLocator::getSummaryFlagsForPath(argPath)); + + auto *argExpr = simplifyLocatorToAnchor(argLocator); + + // If we were unable to simplify down to the argument expression, we don't + // know what this is. + if (!argExpr) + return None; + + Optional choice; + Type rawFnType; + auto *calleeLocator = getCalleeLocator(argLocator); + if (auto overload = findSelectedOverloadFor(calleeLocator)) { + // If we have resolved an overload for the callee, then use that to get the + // function type and callee. + choice = overload->choice; + rawFnType = overload->openedType; + } else { + // If we didn't resolve an overload for the callee, we should be dealing + // with a call of an arbitrary function expr. + if (auto *call = dyn_cast(anchor)) { + assert(!shouldHaveDirectCalleeOverload(call) && + "Should we have resolved a callee for this?"); + rawFnType = getType(call->getFn()); + } else if (auto *apply = dyn_cast(anchor)) { + // FIXME: ArgumentMismatchFailure is currently used from CSDiag, meaning + // we can end up a BinaryExpr here with an unresolved callee. It should be + // possible to remove this once we've gotten rid of the old CSDiag logic + // and just assert that we have a CallExpr. + rawFnType = getType(apply->getFn()); + } else { + return None; + } + } + + // Try to resolve the function type by loading lvalues and looking through + // optional types, which can occur for expressions like `fn?(5)`. + auto *fnType = simplifyType(rawFnType) + ->getRValueType() + ->lookThroughAllOptionalTypes() + ->getAs(); + if (!fnType) + return None; + + // Resolve the interface type for the function. Note that this may not be a + // function type, for example it could be a generic parameter. + Type fnInterfaceType; + auto *callee = choice ? choice->getDeclOrNull() : nullptr; + if (callee && callee->hasInterfaceType()) { + // If we have a callee with an interface type, we can use it. This is + // preferable to resolveInterfaceType, as this will allow us to get a + // GenericFunctionType for generic decls. + // + // Note that it's possible to find a callee without an interface type. This + // can happen for example with closure parameters, where the interface type + // isn't set until the solution is applied. In that case, use + // resolveInterfaceType. + fnInterfaceType = callee->getInterfaceType(); + + // Strip off the curried self parameter if necessary. + if (hasAppliedSelf(*this, *choice)) + fnInterfaceType = fnInterfaceType->castTo()->getResult(); + + if (auto *fn = fnInterfaceType->getAs()) { + assert(fn->getNumParams() == fnType->getNumParams() && + "Parameter mismatch?"); + (void)fn; + } + } else { + fnInterfaceType = resolveInterfaceType(rawFnType); + } + + auto argIdx = applyArgElt->getArgIdx(); + auto paramIdx = applyArgElt->getParamIdx(); + + return FunctionArgApplyInfo(getParentExpr(argExpr), argExpr, argIdx, + simplifyType(getType(argExpr)), + paramIdx, fnInterfaceType, fnType, callee); +} + bool constraints::isKnownKeyPathType(Type type) { if (auto *BGT = type->getAs()) return isKnownKeyPathDecl(type->getASTContext(), BGT->getDecl()); @@ -3231,3 +3776,195 @@ bool constraints::isArgumentOfReferenceEqualityOperator( return isOperatorArgument(locator, "===") || isOperatorArgument(locator, "!=="); } + +ConversionEphemeralness +ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion, + ConstraintLocatorBuilder locator) { + switch (conversion) { + case ConversionRestrictionKind::ArrayToPointer: + case ConversionRestrictionKind::StringToPointer: + // Always ephemeral. + return ConversionEphemeralness::Ephemeral; + case ConversionRestrictionKind::InoutToPointer: { + + // Ephemeral, except if the expression is a reference to a global or + // static stored variable, or a directly accessed stored property on such a + // variable. + + auto isDirectlyAccessedStoredVar = [&](ValueDecl *decl) -> bool { + auto *asd = dyn_cast_or_null(decl); + if (!asd) + return false; + + // Check what access strategy is used for a read-write access. It must be + // direct-to-storage in order for the conversion to be non-ephemeral. + auto access = asd->getAccessStrategy( + AccessSemantics::Ordinary, AccessKind::ReadWrite, + DC->getParentModule(), DC->getResilienceExpansion()); + return access.getKind() == AccessStrategy::Storage; + }; + + SourceRange range; + auto *argLoc = simplifyLocator(*this, getConstraintLocator(locator), range); + auto *subExpr = argLoc->getAnchor()->getSemanticsProvidingExpr(); + + // Look through an InOutExpr if we have one. This is usually the case, but + // might not be if e.g we're applying an 'add missing &' fix. + if (auto *ioe = dyn_cast(subExpr)) + subExpr = ioe->getSubExpr(); + + while (true) { + subExpr = subExpr->getSemanticsProvidingExpr(); + + // Look through force unwraps, which can be modelled as physical lvalue + // components. + if (auto *fve = dyn_cast(subExpr)) { + subExpr = fve->getSubExpr(); + continue; + } + + // Look through a member reference if it's directly accessed. + if (auto *ude = dyn_cast(subExpr)) { + auto overload = findSelectedOverloadFor(ude); + + // If we didn't find an overload, it hasn't been resolved yet. + if (!overload) + return ConversionEphemeralness::Unresolved; + + // Tuple indices are always non-ephemeral. + auto *base = ude->getBase(); + if (overload->choice.getKind() == OverloadChoiceKind::TupleIndex) { + subExpr = base; + continue; + } + + // If we don't have a directly accessed declaration associated with the + // choice, it's ephemeral. + auto *member = overload->choice.getDeclOrNull(); + if (!isDirectlyAccessedStoredVar(member)) + return ConversionEphemeralness::Ephemeral; + + // If we found a static member, the conversion is non-ephemeral. We can + // stop iterating as there's nothing interesting about the base. + if (member->isStatic()) + return ConversionEphemeralness::NonEphemeral; + + // For an instance member, the base must be an @lvalue struct type. + if (auto *lvt = simplifyType(getType(base))->getAs()) { + auto *nominal = lvt->getObjectType()->getAnyNominal(); + if (nominal && isa(nominal)) { + subExpr = base; + continue; + } + } + return ConversionEphemeralness::Ephemeral; + } + + break; + } + + auto getBaseEphemeralness = + [&](ValueDecl *base) -> ConversionEphemeralness { + // We must have a base decl that's directly accessed. + if (!isDirectlyAccessedStoredVar(base)) + return ConversionEphemeralness::Ephemeral; + + // The base decl must either be static or global in order for it to be + // non-ephemeral. + if (base->isStatic() || base->getDeclContext()->isModuleScopeContext()) { + return ConversionEphemeralness::NonEphemeral; + } else { + return ConversionEphemeralness::Ephemeral; + } + }; + + // Fast path: We have a direct decl ref. + if (auto *dre = dyn_cast(subExpr)) + return getBaseEphemeralness(dre->getDecl()); + + // Otherwise, try to find an overload for the base. + if (auto baseOverload = findSelectedOverloadFor(subExpr)) + return getBaseEphemeralness(baseOverload->choice.getDeclOrNull()); + + // If we didn't find a base overload for a unresolved member or overloaded + // decl, it hasn't been resolved yet. + if (isa(subExpr) || + isa(subExpr)) + return ConversionEphemeralness::Unresolved; + + // Otherwise, we don't know what we're dealing with. Default to ephemeral. + return ConversionEphemeralness::Ephemeral; + } + case ConversionRestrictionKind::DeepEquality: + case ConversionRestrictionKind::Superclass: + case ConversionRestrictionKind::Existential: + case ConversionRestrictionKind::MetatypeToExistentialMetatype: + case ConversionRestrictionKind::ExistentialMetatypeToMetatype: + case ConversionRestrictionKind::ValueToOptional: + case ConversionRestrictionKind::OptionalToOptional: + case ConversionRestrictionKind::ClassMetatypeToAnyObject: + case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: + case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: + case ConversionRestrictionKind::PointerToPointer: + case ConversionRestrictionKind::ArrayUpcast: + case ConversionRestrictionKind::DictionaryUpcast: + case ConversionRestrictionKind::SetUpcast: + case ConversionRestrictionKind::HashableToAnyHashable: + case ConversionRestrictionKind::CFTollFreeBridgeToObjC: + case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: + // @_nonEphemeral has no effect on these conversions, so treat them as all + // being non-ephemeral in order to allow their passing to an @_nonEphemeral + // parameter. + return ConversionEphemeralness::NonEphemeral; + } +} + +Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr, + FunctionType *closureType) { + auto &Context = DC->getASTContext(); + bool isInDefaultArgumentContext = false; + if (auto *init = dyn_cast(DC)) + isInDefaultArgumentContext = + init->getInitializerKind() == InitializerKind::DefaultArgument; + + auto info = closureType->getExtInfo(); + auto newClosureType = closureType; + + if (isInDefaultArgumentContext && info.isNoEscape()) + newClosureType = closureType->withExtInfo(info.withNoEscape(false)) + ->castTo(); + + auto *closure = new (Context) AutoClosureExpr( + expr, newClosureType, AutoClosureExpr::InvalidDiscriminator, DC); + + closure->setParameterList(ParameterList::createEmpty(Context)); + + Expr *result = closure; + + if (!newClosureType->isEqual(closureType)) { + assert(isInDefaultArgumentContext); + assert(newClosureType + ->withExtInfo(newClosureType->getExtInfo().withNoEscape(true)) + ->isEqual(closureType)); + result = new (Context) FunctionConversionExpr(closure, closureType); + } + + cacheExprTypes(result); + return result; +} + +/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the +/// constraint system, return the decl that it references. +ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) { + // See if we have a resolution for this member. + auto overload = findSelectedOverloadFor(locator); + if (!overload) + return nullptr; + + // We only want to handle the simplest decl binding. + auto choice = overload->choice; + if (choice.getKind() != OverloadChoiceKind::Decl) + return nullptr; + + return choice.getDecl(); +} diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index e1a0080449847..96c87701064b8 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -31,6 +31,7 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/TypeCheckerDebugConsumer.h" #include "swift/AST/Types.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" #include "llvm/ADT/PointerUnion.h" @@ -70,9 +71,12 @@ namespace constraints { /// A handle that holds the saved state of a type variable, which /// can be restored. class SavedTypeVariableBinding { - /// The type variable and type variable options. - llvm::PointerIntPair TypeVarAndOptions; - + /// The type variable that we saved the state of. + TypeVariableType *TypeVar; + + /// The saved type variable options. + unsigned Options; + /// The parent or fixed type. llvm::PointerUnion ParentOrFixed; @@ -81,9 +85,6 @@ class SavedTypeVariableBinding { /// Restore the state of the type variable to the saved state. void restore(); - - TypeVariableType *getTypeVariable() { return TypeVarAndOptions.getPointer(); } - unsigned getOptions() { return TypeVarAndOptions.getInt(); } }; /// A set of saved type variable bindings. @@ -123,7 +124,6 @@ struct RestrictionOrFix { class ExpressionTimer { Expr* E; - unsigned WarnLimit; ASTContext &Context; llvm::TimeRecord StartTime; @@ -135,6 +135,9 @@ class ExpressionTimer { ~ExpressionTimer(); + unsigned getWarnLimit() const { + return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking; + } llvm::TimeRecord startedAt() const { return StartTime; } /// Return the elapsed process time (including fractional seconds) @@ -168,9 +171,12 @@ enum TypeVariableOptions { /// Whether the type variable can be bound to a non-escaping type or not. TVO_CanBindToNoEscape = 0x04, + /// Whether the type variable can be bound to a hole type or not. + TVO_CanBindToHole = 0x08, + /// Whether a more specific deduction for this type variable implies a /// better solution to the constraint system. - TVO_PrefersSubtypeBinding = 0x08, + TVO_PrefersSubtypeBinding = 0x10, }; /// The implementation object for a type variable used within the @@ -237,6 +243,9 @@ class TypeVariableType::Implementation { /// Whether this type variable can bind to an inout type. bool canBindToNoEscape() const { return getRawOptions() & TVO_CanBindToNoEscape; } + /// Whether this type variable can bind to a hole type. + bool canBindToHole() const { return getRawOptions() & TVO_CanBindToHole; } + /// Whether this type variable prefers a subtype binding over a supertype /// binding. bool prefersSubtypeBinding() const { @@ -288,6 +297,12 @@ class TypeVariableType::Implementation { /// Retrieve the generic parameter opened by this type variable. GenericTypeParamType *getGenericParameter() const; + /// Determine whether this type variable represents a closure type. + bool isClosureType() const; + + /// Determine whether this type variable represents a closure result type. + bool isClosureResultType() const; + /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// @@ -426,14 +441,20 @@ class TypeVariableType::Implementation { ~TVO_CanBindToNoEscape; } + void enableCanBindToHole(constraints::SavedTypeVariableBindings *record) { + auto &impl = getRepresentative(record)->getImpl(); + if (record) + impl.recordBinding(*record); + + impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToHole; + } + /// Print the type variable to the given output stream. void print(llvm::raw_ostream &OS); }; namespace constraints { -struct ResolvedOverloadSetListItem; - /// The result of comparing two constraint systems that are a solutions /// to the given set of constraints. enum class SolutionCompareResult { @@ -456,14 +477,154 @@ enum class SolutionCompareResult { /// declaration was opened, which may involve type variables. struct SelectedOverload { /// The overload choice. - OverloadChoice choice; + const OverloadChoice choice; /// The opened type of the base of the reference to this overload, if /// we're referencing a member. - Type openedFullType; + const Type openedFullType; /// The opened type produced by referring to this overload. - Type openedType; + const Type openedType; + + /// The type that this overload binds. Note that this may differ from + /// openedType, for example it will include any IUO unwrapping that has taken + /// place. + const Type boundType; +}; + +/// Provides information about the application of a function argument to a +/// parameter. +class FunctionArgApplyInfo { + Expr *ArgListExpr; + Expr *ArgExpr; + unsigned ArgIdx; + Type ArgType; + + unsigned ParamIdx; + + Type FnInterfaceType; + FunctionType *FnType; + const ValueDecl *Callee; + +public: + FunctionArgApplyInfo(Expr *argListExpr, Expr *argExpr, unsigned argIdx, + Type argType, unsigned paramIdx, Type fnInterfaceType, + FunctionType *fnType, const ValueDecl *callee) + : ArgListExpr(argListExpr), ArgExpr(argExpr), ArgIdx(argIdx), + ArgType(argType), ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType), + FnType(fnType), Callee(callee) {} + + /// \returns The argument being applied. + Expr *getArgExpr() const { return ArgExpr; } + + /// \returns The position of the argument, starting at 1. + unsigned getArgPosition() const { return ArgIdx + 1; } + + /// \returns The position of the parameter, starting at 1. + unsigned getParamPosition() const { return ParamIdx + 1; } + + /// \returns The type of the argument being applied, including any generic + /// substitutions. + /// + /// \param withSpecifier Whether to keep the inout or @lvalue specifier of + /// the argument, if any. + Type getArgType(bool withSpecifier = false) const { + return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType(); + } + + /// \returns The label for the argument being applied. + Identifier getArgLabel() const { + if (auto *te = dyn_cast(ArgListExpr)) + return te->getElementName(ArgIdx); + + assert(isa(ArgListExpr)); + return Identifier(); + } + + /// \returns A textual description of the argument suitable for diagnostics. + /// For an argument with an unambiguous label, this will the label. Otherwise + /// it will be its position in the argument list. + StringRef getArgDescription(SmallVectorImpl &scratch) const { + llvm::raw_svector_ostream stream(scratch); + + // Use the argument label only if it's unique within the argument list. + auto argLabel = getArgLabel(); + auto useArgLabel = [&]() -> bool { + if (argLabel.empty()) + return false; + + if (auto *te = dyn_cast(ArgListExpr)) + return llvm::count(te->getElementNames(), argLabel) == 1; + + return false; + }; + + if (useArgLabel()) { + stream << "'"; + stream << argLabel; + stream << "'"; + } else { + stream << "#"; + stream << getArgPosition(); + } + return StringRef(scratch.data(), scratch.size()); + } + + /// \returns The interface type for the function being applied. Note that this + /// may not a function type, for example it could be a generic parameter. + Type getFnInterfaceType() const { return FnInterfaceType; } + + /// \returns The function type being applied, including any generic + /// substitutions. + FunctionType *getFnType() const { return FnType; } + + /// \returns The callee for the application. + const ValueDecl *getCallee() const { return Callee; } + +private: + Type getParamTypeImpl(AnyFunctionType *fnTy, + bool lookThroughAutoclosure) const { + auto param = fnTy->getParams()[ParamIdx]; + auto paramTy = param.getPlainType(); + if (lookThroughAutoclosure && param.isAutoClosure()) + paramTy = paramTy->castTo()->getResult(); + return paramTy; + } + +public: + /// \returns The type of the parameter which the argument is being applied to, + /// including any generic substitutions. + /// + /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter + /// should be treated as being of type T. + Type getParamType(bool lookThroughAutoclosure = true) const { + return getParamTypeImpl(FnType, lookThroughAutoclosure); + } + + /// \returns The interface type of the parameter which the argument is being + /// applied to. + /// + /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter + /// should be treated as being of type T. + Type getParamInterfaceType(bool lookThroughAutoclosure = true) const { + auto interfaceFnTy = FnInterfaceType->getAs(); + if (!interfaceFnTy) { + // If the interface type isn't a function, then just return the resolved + // parameter type. + return getParamType(lookThroughAutoclosure)->mapTypeOutOfContext(); + } + return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure); + } + + /// \returns The flags of the parameter which the argument is being applied + /// to. + ParameterTypeFlags getParameterFlags() const { + return FnType->getParams()[ParamIdx].getParameterFlags(); + } + + ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const { + return FnType->getParams()[idx].getParameterFlags(); + } }; /// Describes an aspect of a solution that affects its overall score, i.e., a @@ -508,8 +669,34 @@ struct AppliedBuilderTransform { /// The builder type that was applied to the closure. Type builderType; - /// The single expression to which the closure was transformed. - Expr *singleExpr; + /// The result type of the body, to which the returned expression will be + /// converted. + Type bodyResultType; + + /// An expression whose value has been recorded for later use. + struct RecordedExpr { + /// The temporary value that captures the value of the expression, if + /// there is one. + VarDecl *temporaryVar; + + /// The expression that results from generating constraints with this + /// particular builder. + Expr *generatedExpr; + }; + + /// A mapping from expressions whose values are captured by the builder + /// to information about the temporary variable capturing the + llvm::DenseMap capturedExprs; + + /// A mapping from statements to a pair containing the implicit variable + /// declaration that captures the result of that expression, and the + /// set of expressions that can be used to produce a value for that + /// variable. + llvm::DenseMap>> + capturedStmts; + + /// The return expression, capturing the last value to be emitted. + Expr *returnExpr = nullptr; }; /// Describes the fixed score of a solution to the constraint system. @@ -600,6 +787,16 @@ using OpenedType = std::pair; using OpenedTypeMap = llvm::DenseMap; +/// Describes contextual type information about a particular expression +/// within a constraint system. +struct ContextualTypeInfo { + TypeLoc typeLoc; + ContextualTypePurpose purpose; + bool isOpaqueReturnType = false; + + Type getType() const { return typeLoc.getType(); } +}; + /// A complete solution to a constraint system. /// /// A solution to a constraint system consists of type variable bindings to @@ -660,14 +857,17 @@ class Solution { llvm::SmallPtrSet DefaultedConstraints; /// The node -> type mappings introduced by this solution. - llvm::SmallVector, 8> addedNodeTypes; + llvm::MapVector addedNodeTypes; + + /// Contextual types introduced by this solution. + std::vector> contextualTypes; std::vector> Conformances; - /// The set of closures that have been transformed by a function builder. - llvm::MapVector - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + llvm::MapVector + functionBuilderTransformed; /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. @@ -681,17 +881,12 @@ class Solution { /// \param toType The type to coerce the expression to. /// \param locator Locator used to describe the location of this expression. /// - /// \param ignoreTopLevelInjection Whether to suppress diagnostics - /// on a suspicious top-level optional injection (because the caller already - /// diagnosed it). - /// /// \param typeFromPattern Optionally, the caller can specify the pattern /// from where the toType is derived, so that we can deliver better fixit. /// /// \returns the coerced expression, which will have type \c ToType. Expr *coerceToType(Expr *expr, Type toType, ConstraintLocator *locator, - bool ignoreTopLevelInjection = false, Optional typeFromPattern = None) const; /// Compute the set of substitutions for a generic signature opened at the @@ -702,7 +897,12 @@ class Solution { /// \param locator The locator that describes where the substitutions came /// from. SubstitutionMap computeSubstitutions(GenericSignature sig, - ConstraintLocatorBuilder locator) const; + ConstraintLocator *locator) const; + + /// Resolves the contextual substitutions for a reference to a declaration + /// at a given locator. + ConcreteDeclRef + resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocator *locator) const; /// Return the disjunction choice for the given constraint location. unsigned getDisjunctionChoice(ConstraintLocator *locator) const { @@ -720,7 +920,8 @@ class Solution { Type getFixedType(TypeVariableType *typeVar) const; /// Try to resolve the given locator to a declaration within this - /// solution. + /// solution. Note that this only returns a decl for a direct reference such + /// as \c x.foo and will not return a decl for \c x.foo(). ConcreteDeclRef resolveLocatorToDecl(ConstraintLocator *locator) const; /// Retrieve the overload choice associated with the given @@ -739,9 +940,26 @@ class Solution { return None; } - LLVM_ATTRIBUTE_DEPRECATED( - void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + /// Retrieve a fully-resolved protocol conformance at the given locator + /// and with the given protocol. + ProtocolConformanceRef resolveConformance(ConstraintLocator *locator, + ProtocolDecl *proto); + + ConstraintLocator *getCalleeLocator(ConstraintLocator *locator, + bool lookThroughApply = true) const; + + Type getType(const Expr *E) const; + + void setExprTypes(Expr *expr) const; + + /// Retrieve the type of the given node, as recorded in this solution. + Type getType(TypedNode node) const { + auto known = addedNodeTypes.find(node); + assert(known != addedNodeTypes.end()); + return known->second; + } + + SWIFT_DEBUG_DUMP; /// Dump this solution. void dump(raw_ostream &OS) const LLVM_ATTRIBUTE_USED; @@ -760,70 +978,16 @@ class SolutionDiff { SmallVector choices; }; - /// A difference between two type variable bindings. - struct TypeBindingDiff { - /// The type variable. - TypeVariableType *typeVar; - - /// The bindings that each solution made. - SmallVector bindings; - }; - /// The differences between the overload choices between the /// solutions. SmallVector overloads; - /// The differences between the type variable bindings of the - /// solutions. - SmallVector typeBindings; - /// Compute the differences between the given set of solutions. /// /// \param solutions The set of solutions. explicit SolutionDiff(ArrayRef solutions); }; -/// Describes one resolved overload set within the list of overload sets -/// resolved by the solver. -struct ResolvedOverloadSetListItem { - /// The previously resolved overload set in the list. - ResolvedOverloadSetListItem *Previous; - - /// The type that this overload binds. - Type BoundType; - - /// The overload choice. - OverloadChoice Choice; - - /// The locator for this choice. - ConstraintLocator *Locator; - - /// The type of the fully-opened base, if any. - Type OpenedFullType; - - /// The type of the referenced choice. - Type ImpliedType; - - // Make vanilla new/delete illegal for overload set items. - void *operator new(size_t Bytes) = delete; - void operator delete(void *Data) = delete; - - // Only allow allocation of list items using the allocator in the - // constraint system. - void *operator new(size_t bytes, ConstraintSystem &cs, - unsigned alignment - = alignof(ResolvedOverloadSetListItem)); -}; - - - -/// Identifies a specific conversion from -struct SpecificConstraint { - CanType First; - CanType Second; - ConstraintKind Kind; -}; - /// An intrusive, doubly-linked list of constraints. using ConstraintList = llvm::ilist; @@ -849,10 +1013,6 @@ enum class ConstraintSystemFlags { /// expression, and doesn't dig into its subexpressions. ReusePrecheckedType = 0x20, - /// If set, the top-level expression may be able to provide an underlying - /// type for the contextual opaque archetype. - UnderlyingTypeForOpaqueReturnType = 0x40, - /// FIXME(diagnostics): Once diagnostics are completely switched to new /// framework, this flag could be removed as obsolete. /// @@ -977,16 +1137,170 @@ struct DynamicCallableMethods { } }; +/// Describes the target to which a constraint system's solution can be +/// applied. +class SolutionApplicationTarget { + enum class Kind { + expression, + function + } kind; + + union { + struct { + Expr *expression; + + /// The purpose of the contextual type. + ContextualTypePurpose contextualPurpose; + + /// The type to which the expression should be converted. + TypeLoc convertType; + + /// Whether the expression result will be discarded at the end. + bool isDiscarded; + } expression; + + struct { + AnyFunctionRef function; + BraceStmt *body; + } function; + }; + +public: + SolutionApplicationTarget(Expr *expr, + ContextualTypePurpose contextualPurpose, + Type convertType, bool isDiscarded) + : SolutionApplicationTarget(expr, contextualPurpose, + TypeLoc::withoutLoc(convertType), + isDiscarded) { } + + SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose, + TypeLoc convertType, bool isDiscarded) { + kind = Kind::expression; + expression.expression = expr; + expression.contextualPurpose = contextualPurpose; + expression.convertType = convertType; + expression.isDiscarded = isDiscarded; + } + + SolutionApplicationTarget(AnyFunctionRef fn) + : SolutionApplicationTarget(fn, fn.getBody()) { } + + SolutionApplicationTarget(AnyFunctionRef fn, BraceStmt *body) { + kind = Kind::function; + function.function = fn; + function.body = body; + } + + Expr *getAsExpr() const { + switch (kind) { + case Kind::expression: + return expression.expression; + + case Kind::function: + return nullptr; + } + } + + ContextualTypePurpose getExprContextualTypePurpose() const { + assert(kind == Kind::expression); + return expression.contextualPurpose; + } + + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType.getType(); + } + + TypeLoc getExprConversionTypeLoc() const { + assert(kind == Kind::expression); + return expression.convertType; + } + + void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = TypeLoc::withoutLoc(type); + } + + void setExprConversionTypeLoc(TypeLoc type) { + assert(kind == Kind::expression); + expression.convertType = type; + } + + bool isDiscardedExpr() const { + assert(kind == Kind::expression); + return expression.isDiscarded; + } + + void setExpr(Expr *expr) { + assert(kind == Kind::expression); + expression.expression = expr; + } + + Optional getAsFunction() const { + switch (kind) { + case Kind::expression: + return None; + + case Kind::function: + return function.function; + } + } + + BraceStmt *getFunctionBody() const { + assert(kind == Kind::function); + return function.body; + } + + void setFunctionBody(BraceStmt *stmt) { + assert(kind == Kind::function); + function.body = stmt; + } + + /// Retrieve the source range of the target. + SourceRange getSourceRange() const { + switch (kind) { + case Kind::expression: + return expression.expression->getSourceRange(); + + case Kind::function: + return function.body->getSourceRange(); + } + } + + /// Retrieve the source location for the target. + SourceLoc getLoc() const { + switch (kind) { + case Kind::expression: + return expression.expression->getLoc(); + + case Kind::function: + return function.function.getLoc(); + } + } + + /// Walk the contents of the application target. + SolutionApplicationTarget walk(ASTWalker &walker); +}; + +enum class ConstraintSystemPhase { + ConstraintGeneration, + Solving, + Diagnostics, + Finalization +}; + /// Describes a system of constraints on type variables, the /// solution of which assigns concrete types to each of the type variables. /// Constraint systems are typically generated given an (untyped) expression. class ConstraintSystem { + ASTContext &Context; + public: - TypeChecker &TC; DeclContext *DC; ConstraintSystemOptions Options; Optional Timer; - + + friend class Solution; friend class ConstraintFix; friend class OverloadChoice; friend class ConstraintGraph; @@ -1018,6 +1332,15 @@ class ConstraintSystem { unsigned CountDisjunctions = 0; private: + /// Current phase of the constraint system lifetime. + ConstraintSystemPhase Phase = ConstraintSystemPhase::ConstraintGeneration; + + /// The set of expressions for which we have generated constraints. + llvm::SetVector InputExprs; + + /// The number of input expressions whose parents and depths have + /// been entered into \c ExprWeights. + unsigned NumInputExprsInWeights = 0; llvm::DenseMap> ExprWeights; @@ -1044,7 +1367,7 @@ class ConstraintSystem { size_t MaxMemory = 0; /// Cached member lookups. - llvm::DenseMap, Optional> + llvm::DenseMap, Optional> MemberLookups; /// Cached sets of "alternative" literal types. @@ -1056,7 +1379,7 @@ class ConstraintSystem { llvm::FoldingSetVector ConstraintLocators; /// The overload sets that have been resolved along the current path. - ResolvedOverloadSetListItem *resolvedOverloadSets = nullptr; + llvm::MapVector ResolvedOverloads; /// The current fixed score for this constraint system and the (partial) /// solution it represents. @@ -1068,6 +1391,10 @@ class ConstraintSystem { /// type in a disjunction constraint. llvm::DenseMap FavoredTypes; + /// Maps discovered closures to their types inferred + /// from declared parameters/result and body. + llvm::MapVector ClosureTypes; + /// Maps expression types used within all portions of the constraint /// system, instead of directly using the types on the expression /// nodes themselves. This allows us to typecheck an expression and @@ -1079,17 +1406,14 @@ class ConstraintSystem { llvm::DenseMap, TypeBase *> KeyPathComponentTypes; + /// Contextual type information for expressions that are part of this + /// constraint system. + llvm::MapVector contextualTypes; + /// Maps closure parameters to type variables. llvm::DenseMap OpenedParameterTypes; - /// There can only be a single contextual type on the root of the expression - /// being checked. If specified, this holds its type along with the base - /// expression, and the purpose of it. - TypeLoc contextualType; - Expr *contextualTypeNode = nullptr; - ContextualTypePurpose contextualTypePurpose = CTP_Unused; - /// The set of constraint restrictions used to reach the /// current constraint system. /// @@ -1103,13 +1427,6 @@ class ConstraintSystem { /// The set of fixes applied to make the solution work. llvm::SmallVector Fixes; - /// The set of "holes" in the constraint system encountered - /// along the current path identified by locator. A "hole" is - /// a type variable which type couldn't be determined due to - /// an inference failure e.g. missing member, ambiguous generic - /// parameter which hasn't been explicitly specified. - llvm::SmallSetVector Holes; - /// The set of remembered disjunction choices used to reach /// the current constraint system. std::vector> @@ -1159,9 +1476,9 @@ class ConstraintSystem { std::vector> CheckedConformances; - /// The set of closures that have been transformed by a function builder. - std::vector> - builderTransformedClosures; + /// The set of functions that have been transformed by a function builder. + std::vector> + functionBuilderTransformed; public: /// The locators of \c Defaultable constraints whose defaults were used. @@ -1171,9 +1488,6 @@ class ConstraintSystem { /// types. llvm::DenseMap DynamicCallableCache; - /// A cache that stores whether types are valid @dynamicMemberLookup types. - llvm::DenseMap DynamicMemberLookupCache; - private: /// Describe the candidate expression for partial solving. /// This class used by shrink & solve methods which apply @@ -1181,7 +1495,6 @@ class ConstraintSystem { /// to reduce scopes of the overload sets (disjunctions) in the system. class Candidate { Expr *E; - TypeChecker &TC; DeclContext *DC; llvm::BumpPtrAllocator &Allocator; @@ -1194,7 +1507,7 @@ class ConstraintSystem { public: Candidate(ConstraintSystem &cs, Expr *expr, Type ct = Type(), ContextualTypePurpose ctp = ContextualTypePurpose::CTP_Unused) - : E(expr), TC(cs.TC), DC(cs.DC), Allocator(cs.Allocator), BaseCS(cs), + : E(expr), DC(cs.DC), Allocator(cs.Allocator), BaseCS(cs), CT(ct), CTP(ctp) {} /// Return underlying expression. @@ -1246,7 +1559,8 @@ class ConstraintSystem { } } - unsigned threshold = cs->TC.getLangOpts().SolverShrinkUnsolvedThreshold; + unsigned threshold = + cs->getASTContext().TypeCheckerOpts.SolverShrinkUnsolvedThreshold; return unsolvedDisjunctions >= threshold; } }; @@ -1506,56 +1820,40 @@ class ConstraintSystem { bool walkToDeclPre(Decl *decl) override { return false; } }; - class SetExprTypes : public ASTWalker { - Expr *RootExpr; - ConstraintSystem &CS; - bool ExcludeRoot; +public: + ConstraintSystemPhase getPhase() const { return Phase; } - public: - SetExprTypes(Expr *expr, ConstraintSystem &cs, bool excludeRoot) - : RootExpr(expr), CS(cs), ExcludeRoot(excludeRoot) {} + /// Move constraint system to a new phase of its lifetime. + void setPhase(ConstraintSystemPhase newPhase) { + if (Phase == newPhase) + return; - Expr *walkToExprPost(Expr *expr) override { - if (ExcludeRoot && expr == RootExpr) - return expr; +#ifndef NDEBUG + switch (Phase) { + case ConstraintSystemPhase::ConstraintGeneration: + assert(newPhase == ConstraintSystemPhase::Solving); + break; - //assert((!expr->getType() || CS.getType(expr)->isEqual(expr->getType())) - // && "Mismatched types!"); - assert(!CS.getType(expr)->hasTypeVariable() && - "Should not write type variable into expression!"); - expr->setType(CS.getType(expr)); - - if (auto kp = dyn_cast(expr)) { - for (auto i : indices(kp->getComponents())) { - Type componentType; - if (CS.hasType(kp, i)) - componentType = CS.getType(kp, i); - kp->getMutableComponents()[i].setComponentType(componentType); - } - } + case ConstraintSystemPhase::Solving: + // We can come back to constraint generation phase while + // processing function builder body. + assert(newPhase == ConstraintSystemPhase::ConstraintGeneration || + newPhase == ConstraintSystemPhase::Diagnostics || + newPhase == ConstraintSystemPhase::Finalization); + break; - return expr; - } + case ConstraintSystemPhase::Diagnostics: + assert(newPhase == ConstraintSystemPhase::Solving || + newPhase == ConstraintSystemPhase::Finalization); + break; - /// Ignore statements. - std::pair walkToStmtPre(Stmt *stmt) override { - return { false, stmt }; + case ConstraintSystemPhase::Finalization: + assert(newPhase == ConstraintSystemPhase::Diagnostics); + break; } +#endif - /// Ignore declarations. - bool walkToDeclPre(Decl *decl) override { return false; } - }; - -public: - - void setExprTypes(Expr *expr) { - SetExprTypes SET(expr, *this, /* excludeRoot = */ false); - expr->walk(SET); - } - - void setSubExprTypes(Expr *expr) { - SetExprTypes SET(expr, *this, /* excludeRoot = */ true); - expr->walk(SET); + Phase = newPhase; } /// Cache the types of the given expression and all subexpressions. @@ -1598,31 +1896,33 @@ class ConstraintSystem { /// reference at the given locator. Optional getArgumentInfo(ConstraintLocator *locator); - ResolvedOverloadSetListItem *getResolvedOverloadSets() const { - return resolvedOverloadSets; - } - - ResolvedOverloadSetListItem * + Optional findSelectedOverloadFor(ConstraintLocator *locator) const { - auto resolvedOverload = getResolvedOverloadSets(); - while (resolvedOverload) { - if (resolvedOverload->Locator == locator) - return resolvedOverload; - resolvedOverload = resolvedOverload->Previous; - } - return nullptr; + auto result = ResolvedOverloads.find(locator); + if (result == ResolvedOverloads.end()) + return None; + return result->second; } - ResolvedOverloadSetListItem *findSelectedOverloadFor(Expr *expr) const { - auto resolvedOverload = getResolvedOverloadSets(); - while (resolvedOverload) { - if (resolvedOverload->Locator->getAnchor() == expr) - return resolvedOverload; - resolvedOverload = resolvedOverload->Previous; - } - return nullptr; + Optional findSelectedOverloadFor(Expr *expr) { + // Retrieve the callee locator for this expression, making sure not to + // look through applies in order to ensure we only return the "direct" + // callee. + auto *loc = getConstraintLocator(expr); + auto *calleeLoc = getCalleeLocator(loc, /*lookThroughApply*/ false); + return findSelectedOverloadFor(calleeLoc); } + /// Resolve type variables present in the raw type, using generic parameter + /// types where possible. + Type resolveInterfaceType(Type type) const; + + /// For a given locator describing a function argument conversion, or a + /// constraint within an argument conversion, returns information about the + /// application of the argument to its parameter. If the locator is not + /// for an argument conversion, returns \c None. + Optional getFunctionArgApplyInfo(ConstraintLocator *); + private: unsigned assignTypeVariableID() { return TypeCounter++; @@ -1640,9 +1940,6 @@ class ConstraintSystem { class SolverScope { ConstraintSystem &cs; - /// The current resolved overload set list. - ResolvedOverloadSetListItem *resolvedOverloadSets; - /// The length of \c TypeVariables. unsigned numTypeVariables; @@ -1655,9 +1952,6 @@ class ConstraintSystem { /// The length of \c Fixes. unsigned numFixes; - /// The length of \c Holes. - unsigned numHoles; - /// The length of \c FixedRequirements. unsigned numFixedRequirements; @@ -1681,7 +1975,16 @@ class ConstraintSystem { unsigned numFavoredConstraints; - unsigned numBuilderTransformedClosures; + unsigned numFunctionBuilderTransformed; + + /// The length of \c ResolvedOverloads. + unsigned numResolvedOverloads; + + /// The length of \c ClosureTypes. + unsigned numInferredClosureTypes; + + /// The length of \c contextualTypes. + unsigned numContextualTypes; /// The previous score. Score PreviousScore; @@ -1702,19 +2005,15 @@ class ConstraintSystem { ~SolverScope(); }; - ConstraintSystem(TypeChecker &tc, DeclContext *dc, - ConstraintSystemOptions options, - Expr *expr = nullptr); + ConstraintSystem(DeclContext *dc, + ConstraintSystemOptions options); ~ConstraintSystem(); - /// Retrieve the type checker associated with this constraint system. - TypeChecker &getTypeChecker() const { return TC; } - /// Retrieve the constraint graph associated with this constraint system. ConstraintGraph &getConstraintGraph() const { return CG; } /// Retrieve the AST context. - ASTContext &getASTContext() const { return TC.Context; } + ASTContext &getASTContext() const { return Context; } /// Determine whether this constraint system has any free type /// variables. @@ -1741,9 +2040,12 @@ class ConstraintSystem { /// constraint system for further exploration. void applySolution(const Solution &solution); + // FIXME: Allows the type checker to apply solutions. + friend class swift::TypeChecker; + /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. - bool applySolutionFixes(Expr *E, const Solution &solution); + bool applySolutionFixes(const Solution &solution); /// If there is more than one viable solution, /// attempt to pick the best solution and remove all of the rest. @@ -1799,7 +2101,7 @@ class ConstraintSystem { /// and no new names are introduced after name binding. /// /// \returns A reference to the member-lookup result. - LookupResult &lookupMember(Type base, DeclName name); + LookupResult &lookupMember(Type base, DeclNameRef name); /// Retrieve the set of "alternative" literal types that we'll explore /// for a given literal protocol kind. @@ -1820,6 +2122,19 @@ class ConstraintSystem { return TypeVariables.count(typeVar) > 0; } + void setClosureType(const ClosureExpr *closure, FunctionType *type) { + assert(closure); + assert(type && "Expected non-null type"); + assert(ClosureTypes.count(closure) == 0 && "Cannot reset closure type"); + ClosureTypes.insert({closure, type}); + } + + FunctionType *getClosureType(const ClosureExpr *closure) const { + auto result = ClosureTypes.find(closure); + assert(result != ClosureTypes.end()); + return result->second; + } + TypeBase* getFavoredType(Expr *E) { assert(E != nullptr); return this->FavoredTypes[E]; @@ -1848,9 +2163,7 @@ class ConstraintSystem { } // Record the fact that we ascribed a type to this node. - if (solverState && solverState->depth > 0) { - addedNodeTypes.push_back({node, type}); - } + addedNodeTypes.push_back({node, type}); } /// Set the type in our type map for a given expression. The side @@ -1933,6 +2246,15 @@ class ConstraintSystem { return KeyPathComponentTypes.find(std::make_pair(KP, I))->second; } + /// Retrieve the type of the variable, if known. + Type getTypeIfAvailable(const VarDecl *VD) const { + auto known = VarTypes.find(VD); + if (known == VarTypes.end()) + return Type(); + + return known->second; + } + /// Cache the type of the expression argument and return that same /// argument. template @@ -1951,33 +2273,43 @@ class ConstraintSystem { return E; } - void setContextualType(Expr *E, TypeLoc T, ContextualTypePurpose purpose) { - assert(E != nullptr && "Expected non-null expression!"); - contextualTypeNode = E; - contextualType = T; - contextualTypePurpose = purpose; + void setContextualType( + const Expr *expr, TypeLoc T, ContextualTypePurpose purpose, + bool isOpaqueReturnType) { + assert(expr != nullptr && "Expected non-null expression!"); + assert(contextualTypes.count(expr) == 0 && + "Already set this contextual type"); + contextualTypes[expr] = { T, purpose, isOpaqueReturnType }; } - Type getContextualType(Expr *E) const { - assert(E != nullptr && "Expected non-null expression!"); - return E == contextualTypeNode ? contextualType.getType() : Type(); - } - Type getContextualType() const { - return contextualType.getType(); + Optional getContextualTypeInfo(const Expr *expr) const { + auto known = contextualTypes.find(expr); + if (known == contextualTypes.end()) + return None; + return known->second; } - TypeLoc getContextualTypeLoc() const { - return contextualType; + Type getContextualType(const Expr *expr) const { + auto result = getContextualTypeInfo(expr); + if (result) + return result->typeLoc.getType(); + return Type(); } - const Expr *getContextualTypeNode() const { - return contextualTypeNode; + TypeLoc getContextualTypeLoc(const Expr *expr) const { + auto result = getContextualTypeInfo(expr); + if (result) + return result->typeLoc; + return TypeLoc(); } - ContextualTypePurpose getContextualTypePurpose() const { - return contextualTypePurpose; + ContextualTypePurpose getContextualTypePurpose(const Expr *expr) const { + auto result = getContextualTypeInfo(expr); + if (result) + return result->purpose; + return CTP_Unused; } - + /// Retrieve the constraint locator for the given anchor and /// path, uniqued. ConstraintLocator * @@ -2015,21 +2347,43 @@ class ConstraintSystem { ConstraintLocator * getConstraintLocator(ConstraintLocator *locator, ConstraintLocator::PathElement pathElt) { - return getConstraintLocator(ConstraintLocatorBuilder(locator) - .withPathElement(pathElt)); + ConstraintLocatorBuilder builder(locator); + return getConstraintLocator(builder.withPathElement(pathElt)); } + /// Extend the given constraint locator with an array of path elements. + ConstraintLocator * + getConstraintLocator(ConstraintLocator *locator, + ArrayRef newElts); + + /// Retrieve the locator described by a given builder extended by an array of + /// path elements. + ConstraintLocator * + getConstraintLocator(const ConstraintLocatorBuilder &builder, + ArrayRef newElts); + /// Retrieve the constraint locator described by the given /// builder. ConstraintLocator * getConstraintLocator(const ConstraintLocatorBuilder &builder); /// Lookup and return parent associated with given expression. - Expr *getParentExpr(Expr *expr) const { - auto e = ExprWeights.find(expr); - return e != ExprWeights.end() ? e->second.second : nullptr; + Expr *getParentExpr(Expr *expr) { + if (auto result = getExprDepthAndParent(expr)) + return result->second; + return nullptr; } + /// Retrieve the depth of the given expression. + Optional getExprDepth(Expr *expr) { + if (auto result = getExprDepthAndParent(expr)) + return result->first; + return None; + } + + /// Retrieve the depth and parent expression of the given expression. + Optional> getExprDepthAndParent(Expr *expr); + /// Returns a locator describing the callee for the anchor of a given locator. /// /// - For an unresolved dot/member anchor, this will be a locator describing @@ -2049,7 +2403,33 @@ class ConstraintSystem { /// anchored on \c functionA(functionB()) with path elements pointing to the /// argument \c functionB(), the returned callee locator will describe /// \c functionA rather than \c functionB. - ConstraintLocator *getCalleeLocator(ConstraintLocator *locator); + /// + /// \param locator The input locator. + /// \param lookThroughApply Whether to look through applies. If false, a + /// callee locator will only be returned for a direct reference such as + /// \c x.foo rather than \c x.foo(). + /// \param getType The callback to fetch a type for given expression. + /// \param simplifyType The callback to attempt to resolve any type + /// variables which appear in the given type. + /// \param getOverloadFor The callback to fetch overload for a given + /// locator if available. + ConstraintLocator *getCalleeLocator( + ConstraintLocator *locator, bool lookThroughApply, + llvm::function_ref getType, + llvm::function_ref simplifyType, + llvm::function_ref(ConstraintLocator *)> + getOverloadFor); + + ConstraintLocator *getCalleeLocator(ConstraintLocator *locator, + bool lookThroughApply = true) { + return getCalleeLocator( + locator, lookThroughApply, + [&](const Expr *expr) -> Type { return getType(expr); }, + [&](Type type) -> Type { return simplifyType(type)->getRValueType(); }, + [&](ConstraintLocator *locator) -> Optional { + return findSelectedOverloadFor(locator); + }); + } public: @@ -2075,22 +2455,20 @@ class ConstraintSystem { /// subsequent solution would be worse than the best known solution. bool recordFix(ConstraintFix *fix, unsigned impact = 1); - void recordHole(TypeVariableType *typeVar); - - bool isHole(TypeVariableType *typeVar) const { - return isHoleAt(typeVar->getImpl().getLocator()); - } - - bool isHoleAt(ConstraintLocator *locator) const { - return bool(Holes.count(locator)); - } + void recordPotentialHole(TypeVariableType *typeVar); + void recordPotentialHole(FunctionType *fnType); /// Determine whether constraint system already has a fix recorded /// for a particular location. - bool hasFixFor(ConstraintLocator *locator) const { - return llvm::any_of(Fixes, [&locator](const ConstraintFix *fix) { - return fix->getLocator() == locator; - }); + bool hasFixFor(ConstraintLocator *locator, + Optional expectedKind = None) const { + return llvm::any_of( + Fixes, [&locator, &expectedKind](const ConstraintFix *fix) { + if (fix->getLocator() == locator) { + return !expectedKind || fix->getKind() == *expectedKind; + } + return false; + }); } /// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the @@ -2098,31 +2476,22 @@ class ConstraintSystem { ValueDecl *findResolvedMemberRef(ConstraintLocator *locator); /// Try to salvage the constraint system by applying (speculative) - /// fixes to the underlying expression. - /// - /// \param viable the set of viable solutions produced by the initial - /// solution attempt. - /// - /// \param expr the expression we're trying to salvage. - /// - /// \returns false if we were able to salvage the system, in which case - /// \c viable[0] contains the resulting solution. Otherwise, emits a - /// diagnostic and returns true. - bool salvage(SmallVectorImpl &viable, Expr *expr); + /// fixes. + SolutionResult salvage(); /// Mine the active and inactive constraints in the constraint /// system to generate a plausible diagnosis of why the system could not be /// solved. /// - /// \param expr The expression whose constraints we're investigating for a - /// better diagnostic. + /// \param target The solution target whose constraints we're investigating + /// for a better diagnostic. /// /// Assuming that this constraint system is actually erroneous, this *always* /// emits an error message. - void diagnoseFailureForExpr(Expr *expr); + void diagnoseFailureFor(SolutionApplicationTarget target); - bool diagnoseAmbiguity(Expr *expr, ArrayRef solutions); - bool diagnoseAmbiguityWithFixes(Expr *expr, ArrayRef solutions); + bool diagnoseAmbiguity(ArrayRef solutions); + bool diagnoseAmbiguityWithFixes(SmallVectorImpl &solutions); /// Give the deprecation warning for referring to a global function /// when there's a method from a conditional conformance in a smaller/closer @@ -2140,6 +2509,10 @@ class ConstraintSystem { void addConstraint(Requirement req, ConstraintLocatorBuilder locator, bool isFavored = false); + /// Add the appropriate constraint for a contextual conversion. + void addContextualConversionConstraint( + Expr *expr, ContextualTypeInfo contextualType); + /// Add a "join" constraint between a set of types, producing the common /// supertype. /// @@ -2151,6 +2524,12 @@ class ConstraintSystem { Type addJoinConstraint(ConstraintLocator *locator, ArrayRef> inputs); + /// Add a constraint to the constraint system with an associated fix. + void addFixConstraint(ConstraintFix *fix, ConstraintKind kind, + Type first, Type second, + ConstraintLocatorBuilder locator, + bool isFavored = false); + /// Add a key path application constraint to the constraint system. void addKeyPathApplicationConstraint(Type keypath, Type root, Type value, ConstraintLocatorBuilder locator, @@ -2176,7 +2555,7 @@ class ConstraintSystem { } /// Add a value member constraint to the constraint system. - void addValueMemberConstraint(Type baseTy, DeclName name, Type memberTy, + void addValueMemberConstraint(Type baseTy, DeclNameRef name, Type memberTy, DeclContext *useDC, FunctionRefKind functionRefKind, ArrayRef outerAlternatives, @@ -2206,7 +2585,7 @@ class ConstraintSystem { /// Add a value member constraint for an UnresolvedMemberRef /// to the constraint system. - void addUnresolvedValueMemberConstraint(Type baseTy, DeclName name, + void addUnresolvedValueMemberConstraint(Type baseTy, DeclNameRef name, Type memberTy, DeclContext *useDC, FunctionRefKind functionRefKind, ConstraintLocatorBuilder locator) { @@ -2237,6 +2616,26 @@ class ConstraintSystem { } } + /// Add a value witness constraint to the constraint system. + void addValueWitnessConstraint( + Type baseTy, ValueDecl *requirement, Type memberTy, DeclContext *useDC, + FunctionRefKind functionRefKind, ConstraintLocatorBuilder locator) { + assert(baseTy); + assert(memberTy); + assert(requirement); + assert(useDC); + switch (simplifyValueWitnessConstraint( + ConstraintKind::ValueWitness, baseTy, requirement, memberTy, useDC, + functionRefKind, TMF_GenerateConstraints, locator)) { + case SolutionKind::Unsolved: + llvm_unreachable("Unsolved result when generating constraints!"); + + case SolutionKind::Solved: + case SolutionKind::Error: + break; + } + } + /// Add an explicit conversion constraint (e.g., \c 'x as T'). void addExplicitConversionConstraint(Type fromType, Type toType, bool allowFixes, @@ -2348,21 +2747,20 @@ class ConstraintSystem { /// Retrieve the representative of the equivalence class containing /// this type variable. - TypeVariableType *getRepresentative(TypeVariableType *typeVar) { + TypeVariableType *getRepresentative(TypeVariableType *typeVar) const { return typeVar->getImpl().getRepresentative(getSavedBindings()); } /// Gets the VarDecl associateed with resolvedOverload, and the type of the /// storage wrapper if the decl has an associated storage wrapper. Optional> - getStorageWrapperInformation(ResolvedOverloadSetListItem *resolvedOverload) { - assert(resolvedOverload); - if (resolvedOverload->Choice.isDecl()) { - if (auto *decl = dyn_cast(resolvedOverload->Choice.getDecl())) { + getStorageWrapperInformation(SelectedOverload resolvedOverload) { + if (resolvedOverload.choice.isDecl()) { + if (auto *decl = dyn_cast(resolvedOverload.choice.getDecl())) { if (decl->hasAttachedPropertyWrapper()) { if (auto storageWrapper = decl->getPropertyWrapperStorageWrapper()) { Type type = storageWrapper->getInterfaceType(); - if (Type baseType = resolvedOverload->Choice.getBaseType()) { + if (Type baseType = resolvedOverload.choice.getBaseType()) { type = baseType->getTypeOfMember(DC->getParentModule(), storageWrapper, type); } @@ -2377,13 +2775,12 @@ class ConstraintSystem { /// Gets the VarDecl associateed with resolvedOverload, and the type of the /// backing storage if the decl has an associated property wrapper. Optional> - getPropertyWrapperInformation(ResolvedOverloadSetListItem *resolvedOverload) { - assert(resolvedOverload); - if (resolvedOverload->Choice.isDecl()) { - if (auto *decl = dyn_cast(resolvedOverload->Choice.getDecl())) { + getPropertyWrapperInformation(SelectedOverload resolvedOverload) { + if (resolvedOverload.choice.isDecl()) { + if (auto *decl = dyn_cast(resolvedOverload.choice.getDecl())) { if (decl->hasAttachedPropertyWrapper()) { auto wrapperTy = decl->getPropertyWrapperBackingPropertyType(); - if (Type baseType = resolvedOverload->Choice.getBaseType()) { + if (Type baseType = resolvedOverload.choice.getBaseType()) { wrapperTy = baseType->getTypeOfMember(DC->getParentModule(), decl, wrapperTy); } @@ -2398,13 +2795,12 @@ class ConstraintSystem { /// resolved overload has a decl which is the backing storage for a /// property wrapper. Optional> - getWrappedPropertyInformation(ResolvedOverloadSetListItem *resolvedOverload) { - assert(resolvedOverload); - if (resolvedOverload->Choice.isDecl()) { - if (auto *decl = dyn_cast(resolvedOverload->Choice.getDecl())) { + getWrappedPropertyInformation(SelectedOverload resolvedOverload) { + if (resolvedOverload.choice.isDecl()) { + if (auto *decl = dyn_cast(resolvedOverload.choice.getDecl())) { if (auto wrapped = decl->getOriginalWrappedProperty()) { Type type = wrapped->getInterfaceType(); - if (Type baseType = resolvedOverload->Choice.getBaseType()) { + if (Type baseType = resolvedOverload.choice.getBaseType()) { type = baseType->getTypeOfMember(DC->getParentModule(), wrapped, type); } @@ -2443,7 +2839,7 @@ class ConstraintSystem { /// Retrieve the fixed type corresponding to the given type variable, /// or a null type if there is no fixed type. - Type getFixedType(TypeVariableType *typeVar) { + Type getFixedType(TypeVariableType *typeVar) const { return typeVar->getImpl().getFixedType(getSavedBindings()); } @@ -2455,7 +2851,7 @@ class ConstraintSystem { /// /// \param wantRValue Whether this routine should look through /// lvalues at each step. - Type getFixedTypeRecursive(Type type, bool wantRValue) { + Type getFixedTypeRecursive(Type type, bool wantRValue) const { TypeMatchOptions flags = None; return getFixedTypeRecursive(type, flags, wantRValue); } @@ -2473,7 +2869,7 @@ class ConstraintSystem { /// \param wantRValue Whether this routine should look through /// lvalues at each step. Type getFixedTypeRecursive(Type type, TypeMatchOptions &flags, - bool wantRValue); + bool wantRValue) const; /// Determine whether the given type variable occurs within the given type. /// @@ -2484,6 +2880,22 @@ class ConstraintSystem { static bool typeVarOccursInType(TypeVariableType *typeVar, Type type, bool *involvesOtherTypeVariables = nullptr); + /// Given the fact that contextual type is now available for the type + /// variable representing one of the closures, let's set pre-determined + /// closure type and generate constraints for its body, iff it's a + /// single-statement closure. + /// + /// \param typeVar The type variable representing a function type of the + /// closure expression. + /// \param contextualType The contextual type this closure would be + /// converted to. + /// \param locator The locator associated with contextual type. + /// + /// \returns `true` if it was possible to generate constraints for + /// the body and assign fixed type to the closure, `false` otherwise. + bool resolveClosure(TypeVariableType *typeVar, Type contextualType, + ConstraintLocatorBuilder locator); + /// Assign a fixed type to the given type variable. /// /// \param typeVar The type variable to bind. @@ -2495,7 +2907,7 @@ class ConstraintSystem { /// a complete solution from partial solutions. void assignFixedType(TypeVariableType *typeVar, Type type, bool updateState = true); - + /// Determine if the type in question is an Array and, if so, provide the /// element type of the array. static Optional isArrayType(Type type); @@ -2555,6 +2967,9 @@ class ConstraintSystem { /// Coerce the given expression to an rvalue, if it isn't already. Expr *coerceToRValue(Expr *expr); + /// Add implicit "load" expressions to the given expression. + Expr *addImplicitLoadExpr(Expr *expr); + /// "Open" the given unbound type by introducing fresh type /// variables for generic parameters and constructing a bound generic /// type from these type variables. @@ -2682,6 +3097,24 @@ class ConstraintSystem { const DeclRefExpr *base = nullptr, OpenedTypeMap *replacements = nullptr); +private: + /// Adjust the constraint system to accomodate the given selected overload, and + /// recompute the type of the referenced declaration. + /// + /// \returns a pair containing the adjusted opened type of a reference to + /// this member and a bit indicating whether or not a bind constraint was added. + std::pair adjustTypeOfOverloadReference( + const OverloadChoice &choice, ConstraintLocator *locator, Type boundType, + Type refType); + + /// Add the constraints needed to bind an overload's type variable. + void bindOverloadType( + const SelectedOverload &overload, Type boundType, + ConstraintLocator *locator, DeclContext *useDC, + llvm::function_ref + verifyThatArgumentIsHashable); + +public: /// Attempt to simplify the set of overloads corresponding to a given /// function application constraint. /// @@ -2737,10 +3170,15 @@ class ConstraintSystem { return allocateCopy(vec.begin(), vec.end()); } + /// Generate constraints for the body of the given single-statement closure. + /// + /// \returns a possibly-sanitized expression, or null if an error occurred. + Expr *generateConstraints(ClosureExpr *closure); + /// Generate constraints for the given (unchecked) expression. /// /// \returns a possibly-sanitized expression, or null if an error occurred. - Expr *generateConstraints(Expr *E, DeclContext *dc = nullptr); + Expr *generateConstraints(Expr *E, DeclContext *dc); /// Generate constraints for binding the given pattern to the /// value of the given expression. @@ -2748,6 +3186,16 @@ class ConstraintSystem { /// \returns a possibly-sanitized initializer, or null if an error occurred. Type generateConstraints(Pattern *P, ConstraintLocatorBuilder locator); + /// Determines whether we can generate constraints for this statement + /// condition. + static bool canGenerateConstraints(StmtCondition condition); + + /// Generate constraints for a statement condition. + /// + /// \returns true if there was an error in constraint generation, false + /// if generation succeeded. + bool generateConstraints(StmtCondition condition, DeclContext *dc); + /// Generate constraints for a given set of overload choices. /// /// \param constraints The container of generated constraint choices. @@ -3015,7 +3463,7 @@ class ConstraintSystem { /// /// The resulting types can be compared canonically, so long as additional /// type equivalence requirements aren't introduced between comparisons. - Type simplifyType(Type type); + Type simplifyType(Type type) const; /// Simplify a type, by replacing type variables with either their /// fixed types (if available) or their representatives. @@ -3041,12 +3489,29 @@ class ConstraintSystem { /// try to identify and classify inaccessible members that may be being /// referenced. MemberLookupResult performMemberLookup(ConstraintKind constraintKind, - DeclName memberName, Type baseTy, + DeclNameRef memberName, Type baseTy, FunctionRefKind functionRefKind, ConstraintLocator *memberLocator, bool includeInaccessibleMembers); + /// Build implicit autoclosure expression wrapping a given expression. + /// Given expression represents computed result of the closure. + Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType); + private: + /// Determines whether or not a given conversion at a given locator requires + /// the creation of a temporary value that's only valid for a limited scope. + /// Such ephemeral conversions, such as array-to-pointer, cannot be passed to + /// non-ephemeral parameters. + ConversionEphemeralness + isConversionEphemeral(ConversionRestrictionKind conversion, + ConstraintLocatorBuilder locator); + + /// Simplifies a type by replacing type variables with the result of + /// \c getFixedTypeFn and performing lookup on dependent member types. + Type simplifyTypeImpl(Type type, + llvm::function_ref getFixedTypeFn) const; + /// Attempt to simplify the given construction constraint. /// /// \param valueType The type being constructed. @@ -3101,11 +3566,17 @@ class ConstraintSystem { /// Attempt to simplify the given member constraint. SolutionKind simplifyMemberConstraint( - ConstraintKind kind, Type baseType, DeclName member, Type memberType, + ConstraintKind kind, Type baseType, DeclNameRef member, Type memberType, DeclContext *useDC, FunctionRefKind functionRefKind, ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the given value witness constraint. + SolutionKind simplifyValueWitnessConstraint( + ConstraintKind kind, Type baseType, ValueDecl *member, Type memberType, + DeclContext *useDC, FunctionRefKind functionRefKind, + TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the optional object constraint. SolutionKind simplifyOptionalObjectConstraint( Type first, Type second, @@ -3185,6 +3656,12 @@ class ConstraintSystem { TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify the given defaultable closure type constraint. + SolutionKind simplifyDefaultClosureTypeConstraint( + Type closureType, Type inferredType, + ArrayRef referencedOuterParameters, + TypeMatchOptions flags, ConstraintLocatorBuilder locator); + /// Attempt to simplify a one-way constraint. SolutionKind simplifyOneWayConstraint(ConstraintKind kind, Type first, Type second, @@ -3236,9 +3713,10 @@ class ConstraintSystem { void simplifyDisjunctionChoice(Constraint *choice); /// Apply the given function builder to the closure expression. - TypeMatchResult applyFunctionBuilder(ClosureExpr *closure, Type builderType, - ConstraintLocator *calleeLocator, - ConstraintLocatorBuilder locator); + TypeMatchResult matchFunctionBuilder( + AnyFunctionRef fn, Type builderType, Type bodyResultType, + ConstraintKind bodyResultConstraintKind, + ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator); private: /// The kind of bindings that are permitted. @@ -3269,33 +3747,70 @@ class ConstraintSystem { /// The kind of bindings permitted. AllowedBindingKind Kind; - /// The kind of the constraint this binding came from. - ConstraintKind BindingSource; - - /// The defaulted protocol associated with this binding. - ProtocolDecl *DefaultedProtocol; - - /// If this is a binding that comes from a \c Defaultable constraint, - /// the locator of that constraint. - ConstraintLocator *DefaultableBinding = nullptr; + protected: + /// The source of the type information. + /// + /// Determines whether this binding represents a "hole" in + /// constraint system. Such bindings have no originating constraint + /// because they are synthetic, they have a locator instead. + PointerUnion BindingSource; PotentialBinding(Type type, AllowedBindingKind kind, - ConstraintKind bindingSource, - ProtocolDecl *defaultedProtocol = nullptr, - ConstraintLocator *defaultableBinding = nullptr) + PointerUnion source) : BindingType(type->getWithoutParens()), Kind(kind), - BindingSource(bindingSource), DefaultedProtocol(defaultedProtocol), - DefaultableBinding(defaultableBinding) {} + BindingSource(source) {} - bool isDefaultableBinding() const { return DefaultableBinding != nullptr; } + public: + PotentialBinding(Type type, AllowedBindingKind kind, Constraint *source) + : BindingType(type->getWithoutParens()), Kind(kind), + BindingSource(source) {} + + bool isDefaultableBinding() const { + if (auto *constraint = BindingSource.dyn_cast()) + return constraint->getKind() == ConstraintKind::Defaultable; + // If binding source is not constraint - it's a hole, which is + // a last resort default binding for a type variable. + return true; + } + + bool hasDefaultedLiteralProtocol() const { + return bool(getDefaultedLiteralProtocol()); + } + + ProtocolDecl *getDefaultedLiteralProtocol() const { + auto *constraint = BindingSource.dyn_cast(); + if (!constraint) + return nullptr; + + return constraint->getKind() == ConstraintKind::LiteralConformsTo + ? constraint->getProtocol() + : nullptr; + } + + ConstraintLocator *getLocator() const { + if (auto *constraint = BindingSource.dyn_cast()) + return constraint->getLocator(); + return BindingSource.get(); + } PotentialBinding withType(Type type) const { - return {type, Kind, BindingSource, DefaultedProtocol, DefaultableBinding}; + return {type, Kind, BindingSource}; + } + + PotentialBinding withSameSource(Type type, AllowedBindingKind kind) const { + return {type, kind, BindingSource}; + } + + static PotentialBinding forHole(ASTContext &ctx, + ConstraintLocator *locator) { + return {ctx.TheUnresolvedType, AllowedBindingKind::Exact, + /*source=*/locator}; } }; struct PotentialBindings { - using BindingScore = std::tuple; + using BindingScore = + std::tuple; TypeVariableType *TypeVar; @@ -3308,6 +3823,9 @@ class ConstraintSystem { /// Whether the bindings of this type involve other type variables. bool InvolvesTypeVariables = false; + /// Whether this type variable is considered a hole in the constraint system. + bool IsHole = false; + /// Whether the bindings represent (potentially) incomplete set, /// there is no way to say with absolute certainty if that's the /// case, but that could happen when certain constraints like @@ -3329,6 +3847,13 @@ class ConstraintSystem { /// A set of all constraints which contribute to pontential bindings. llvm::SmallPtrSet Sources; + /// A set of all not-yet-resolved type variables this type variable + /// is a subtype of. This is used to determine ordering inside a + /// chain of subtypes because binding inference algorithm can't, + /// at the moment, determine bindings transitively through supertype + /// type variables. + llvm::SmallPtrSet SubtypeOf; + PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {} @@ -3341,7 +3866,8 @@ class ConstraintSystem { } static BindingScore formBindingScore(const PotentialBindings &b) { - return std::make_tuple(!b.hasNonDefaultableBindings(), + return std::make_tuple(b.IsHole, + !b.hasNonDefaultableBindings(), b.FullyBound, b.SubtypeOfExistentialType, b.InvolvesTypeVariables, @@ -3364,6 +3890,20 @@ class ConstraintSystem { if (x.NumDefaultableBindings != y.NumDefaultableBindings) return x.NumDefaultableBindings < y.NumDefaultableBindings; + // If neither type variable is a "hole" let's check whether + // there is a subtype relationship between them and prefer + // type variable which represents superclass first in order + // for "subtype" type variable to attempt more bindings later. + // This is required because algorithm can't currently infer + // bindings for subtype transitively through superclass ones. + if (!(x.IsHole && y.IsHole)) { + if (x.SubtypeOf.count(y.TypeVar)) + return false; + + if (y.SubtypeOf.count(x.TypeVar)) + return true; + } + // As a last resort, let's check if the bindings are // potentially incomplete, and if so, let's de-prioritize them. return x.PotentiallyIncomplete < y.PotentiallyIncomplete; @@ -3406,6 +3946,10 @@ class ConstraintSystem { return false; } + /// Check if this binding is favored over a disjunction e.g. + /// if it has only concrete types or would resolve a closure. + bool favoredOverDisjunction(Constraint *disjunction) const; + void dump(llvm::raw_ostream &out, unsigned indent = 0) const LLVM_ATTRIBUTE_USED { out.indent(indent); @@ -3422,13 +3966,12 @@ class ConstraintSystem { if (NumDefaultableBindings > 0) out << "#defaultable_bindings=" << NumDefaultableBindings << " "; + PrintOptions PO; + PO.PrintTypesForDebugging = true; out << "bindings={"; interleave(Bindings, [&](const PotentialBinding &binding) { auto type = binding.BindingType; - auto &ctx = type->getASTContext(); - llvm::SaveAndRestore debugConstraints( - ctx.LangOpts.DebugConstraintSolver, true); switch (binding.Kind) { case AllowedBindingKind::Exact: break; @@ -3441,10 +3984,9 @@ class ConstraintSystem { out << "(supertypes of) "; break; } - if (binding.DefaultedProtocol) - out << "(default from " - << binding.DefaultedProtocol->getName() << ") "; - out << type.getString(); + if (auto *literal = binding.getDefaultedLiteralProtocol()) + out << "(default from " << literal->getName() << ") "; + out << type.getString(PO); }, [&]() { out << "; "; }); out << "}"; @@ -3466,15 +4008,31 @@ class ConstraintSystem { } }; - Optional checkTypeOfBinding(TypeVariableType *typeVar, Type type); + Optional checkTypeOfBinding(TypeVariableType *typeVar, Type type) const; Optional determineBestBindings(); Optional getPotentialBindingForRelationalConstraint( PotentialBindings &result, Constraint *constraint, bool &hasDependentMemberRelationalConstraints, bool &hasNonDependentMemberRelationalConstraints, - bool &addOptionalSupertypeBindings); - PotentialBindings getPotentialBindings(TypeVariableType *typeVar); + bool &addOptionalSupertypeBindings) const; + PotentialBindings getPotentialBindings(TypeVariableType *typeVar) const; + + /// Detect `subtype` relationship between two type variables and + /// attempt to infer supertype bindings transitively e.g. + /// + /// Given A <: T1 <: T2 transitively A <: T2 + /// + /// Which gives us a new (superclass A) binding for T2 as well as T1. + /// + /// \param inferredBindings The set of all bindings inferred for type + /// variables in the workset. + /// \param bindings The type variable we aim to infer new supertype + /// bindings for. + void inferTransitiveSupertypeBindings( + const llvm::SmallDenseMap + &inferredBindings, + PotentialBindings &bindings); private: /// Add a constraint to the constraint system. @@ -3575,43 +4133,36 @@ class ConstraintSystem { /// Solve the system of constraints generated from provided expression. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target to generate constraints from. /// \param listener The callback to check solving progress. - /// \param solutions The set of solutions to the system of constraints. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. - /// - /// \returns Error is an error occurred, Solved is system is consistent - /// and solutions were found, Unsolved otherwise. - SolutionKind solveImpl(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + SolutionResult solveImpl(SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); public: - /// Solve the system of constraints generated from provided expression. - /// - /// The expression should have already been pre-checked with - /// preCheckExpression(). - /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// Pre-check the expression, validating any types that occur in the + /// expression and folding sequence expressions. + static bool preCheckExpression(Expr *&expr, DeclContext *dc, + ConstraintSystem *baseCS = nullptr); + + /// Solve the system of constraints generated from provided target. + /// + /// \param target The target that we'll generate constraints from, which + /// may be updated by the solving process. /// \param listener The callback to check solving progress. - /// \param solutions The set of solutions to the system of constraints. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// - /// \returns true is an error occurred, false is system is consistent - /// and solutions were found. - bool solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + /// \returns the set of solutions, if any were found, or \c None if an + /// error occurred. When \c None, an error has been emitted. + Optional> solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// @@ -3623,7 +4174,7 @@ class ConstraintSystem { /// \returns true if an error occurred, false otherwise. Note that multiple /// ambiguous solutions for the same constraint system are considered to be /// success by this API. - bool solve(Expr *const expr, SmallVectorImpl &solutions, + bool solve(SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); @@ -3647,7 +4198,7 @@ class ConstraintSystem { /// It doesn't filter solutions, that's the job of top-level `solve` methods. /// /// \param solutions The set of solutions to this system of constraints. - void solve(SmallVectorImpl &solutions); + void solveImpl(SmallVectorImpl &solutions); /// Compare two solutions to the same set of constraints. /// @@ -3658,8 +4209,7 @@ class ConstraintSystem { /// \param idx2 The index of the second solution. static SolutionCompareResult compareSolutions(ConstraintSystem &cs, ArrayRef solutions, - const SolutionDiff &diff, unsigned idx1, unsigned idx2, - llvm::DenseMap> &weights); + const SolutionDiff &diff, unsigned idx1, unsigned idx2); public: /// Increase the score of the given kind for the current (partial) solution @@ -3686,18 +4236,16 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); - /// Apply a given solution to the expression, producing a fully - /// type-checked expression. +public: + /// Apply a given solution to the target, producing a fully + /// type-checked target or \c None if an error occurred. /// - /// \param convertType the contextual type to which the - /// expression should be converted, if any. - /// \param discardedExpr if true, the result of the expression - /// is contextually ignored. - /// \param skipClosures if true, don't descend into bodies of - /// non-single expression closures. - Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, bool discardedExpr, - bool skipClosures); + /// \param target the target to which the solution will be applied. + /// \param performingDiagnostics if true, don't descend into bodies of + /// non-single expression closures, or build curry thunks. + Optional applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully @@ -3711,17 +4259,18 @@ class ConstraintSystem { if (isExpressionAlreadyTooComplex) return true; - auto used = TC.Context.getSolverMemory(); + auto used = getASTContext().getSolverMemory(); for (auto const& s : solutions) { used += s.getTotalMemory(); } MaxMemory = std::max(used, MaxMemory); - auto threshold = TC.Context.LangOpts.SolverMemoryThreshold; + auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; if (MaxMemory > threshold) { return isExpressionAlreadyTooComplex= true; } - auto timeoutThresholdInMillis = TC.getExpressionTimeoutThresholdInSeconds(); + const auto timeoutThresholdInMillis = + getASTContext().TypeCheckerOpts.ExpressionTimeoutThreshold; if (Timer && Timer->isExpired(timeoutThresholdInMillis)) { // Disable warnings about expressions that go over the warning // threshold since we're arbitrarily ending evaluation and @@ -3733,7 +4282,7 @@ class ConstraintSystem { // Bail out once we've looked at a really large number of // choices. - if (CountScopes > TC.Context.LangOpts.SolverBindingThreshold) { + if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) { return isExpressionAlreadyTooComplex = true; } @@ -3790,8 +4339,7 @@ class ConstraintSystem { return LiteralProtocols; } - LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); + SWIFT_DEBUG_DUMP; }; bool haveTypeInformationForAllArguments(FunctionType *fnType); @@ -3822,14 +4370,41 @@ class ConstraintSystem { SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning); - LLVM_ATTRIBUTE_DEPRECATED( - void dump() LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); - LLVM_ATTRIBUTE_DEPRECATED(void dump(Expr *) LLVM_ATTRIBUTE_USED, - "only for use within the debugger"); +private: + /// The set of expressions currently being analyzed for failures. + llvm::DenseMap DiagnosedExprs; + +public: + void addExprForDiagnosis(Expr *E1, Expr *Result) { + DiagnosedExprs[E1] = Result; + } + bool isExprBeingDiagnosed(Expr *E) { + if (DiagnosedExprs.count(E)) { + return true; + } + + if (baseCS && baseCS != this) { + return baseCS->isExprBeingDiagnosed(E); + } + return false; + } + Expr *getExprBeingDiagnosed(Expr *E) { + if (auto *expr = DiagnosedExprs[E]) { + return expr; + } + + if (baseCS && baseCS != this) { + return baseCS->getExprBeingDiagnosed(E); + } + return nullptr; + } + +public: + SWIFT_DEBUG_DUMP; + SWIFT_DEBUG_DUMPER(dump(Expr *)); - void print(raw_ostream &out); - void print(raw_ostream &out, Expr *); + void print(raw_ostream &out) const; + void print(raw_ostream &out, Expr *) const; }; /// Compute the shuffle required to map from a given tuple type to @@ -3873,7 +4448,10 @@ class MatchCallArgumentListener { /// parameter. /// /// \param argIdx The index of the extra argument. - virtual void extraArgument(unsigned argIdx); + /// + /// \returns true to indicate that this should cause a failure, false + /// otherwise. + virtual bool extraArgument(unsigned argIdx); /// Indicates that no argument was provided for the parameter at the given /// indices. @@ -3944,7 +4522,7 @@ class MatchCallArgumentListener { /// \param parameterBindings Will be populated with the arguments that are /// bound to each of the parameters. /// \returns true if the call arguments could not be matched to the parameters. -bool matchCallArguments(ArrayRef args, +bool matchCallArguments(SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, bool hasTrailingClosure, @@ -3954,6 +4532,7 @@ bool matchCallArguments(ArrayRef args, ConstraintSystem::TypeMatchResult matchCallArguments(ConstraintSystem &cs, + FunctionType *contextualType, ArrayRef args, ArrayRef params, ConstraintKind subKind, @@ -4138,13 +4717,17 @@ class TypeVariableBinding { bool isDefaultable() const { return Binding.isDefaultableBinding(); } - bool hasDefaultedProtocol() const { return Binding.DefaultedProtocol; } + bool hasDefaultedProtocol() const { + return Binding.hasDefaultedLiteralProtocol(); + } bool attempt(ConstraintSystem &cs) const; void print(llvm::raw_ostream &Out, SourceManager *) const { - Out << "type variable " << TypeVar->getString() - << " := " << Binding.BindingType->getString(); + PrintOptions PO; + PO.PrintTypesForDebugging = true; + Out << "type variable " << TypeVar->getString(PO) + << " := " << Binding.BindingType->getString(PO); } }; @@ -4392,22 +4975,20 @@ class InputMatcher { // Return true if, when replacing "" with " ?? T", parentheses need // to be added around first in order to maintain the correct precedence. -bool exprNeedsParensBeforeAddingNilCoalescing(TypeChecker &TC, - DeclContext *DC, +bool exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC, Expr *expr); // Return true if, when replacing "" with " as T", parentheses need // to be added around the new expression in order to maintain the correct // precedence. -bool exprNeedsParensAfterAddingNilCoalescing(TypeChecker &TC, - DeclContext *DC, +bool exprNeedsParensAfterAddingNilCoalescing(DeclContext *DC, Expr *expr, Expr *rootExpr); /// Return true if, when replacing "" with " op ", /// parentheses must be added around "" to allow the new operator /// to bind correctly. -bool exprNeedsParensInsideFollowingOperator(TypeChecker &TC, DeclContext *DC, +bool exprNeedsParensInsideFollowingOperator(DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG); @@ -4416,12 +4997,35 @@ bool exprNeedsParensInsideFollowingOperator(TypeChecker &TC, DeclContext *DC, /// the new operator to prevent it from binding incorrectly in the /// surrounding context. bool exprNeedsParensOutsideFollowingOperator( - TypeChecker &TC, DeclContext *DC, Expr *expr, Expr *rootExpr, + DeclContext *DC, Expr *expr, Expr *rootExpr, PrecedenceGroupDecl *followingPG); /// Determine whether this is a SIMD operator. bool isSIMDOperator(ValueDecl *value); +/// Apply the given function builder transform within a specific solution +/// to produce the rewritten body. +/// +/// \param solution The solution to use during application, providing the +/// specific types for each type variable. +/// \param applied The applied builder transform. +/// \param body The body to transform +/// \param dc The context in which the transform occurs. +/// \param rewriteExpr Rewrites expressions that show up in the transform +/// to their final, type-checked versions. +/// \param coerceToType Coerce the given expression to the specified type, +/// which may introduce implicit conversions. +/// +/// \returns the transformed body +BraceStmt *applyFunctionBuilderTransform( + const constraints::Solution &solution, + constraints::AppliedBuilderTransform applied, + BraceStmt *body, + DeclContext *dc, + std::function rewriteExpr, + std::function + coerceToType); + } // end namespace swift #endif // LLVM_SWIFT_SEMA_CONSTRAINT_SYSTEM_H diff --git a/lib/Sema/DebuggerTestingTransform.cpp b/lib/Sema/DebuggerTestingTransform.cpp index cb85e9998a4e3..4853943d687d6 100644 --- a/lib/Sema/DebuggerTestingTransform.cpp +++ b/lib/Sema/DebuggerTestingTransform.cpp @@ -67,10 +67,15 @@ class DebuggerTestingTransform : public ASTWalker { ASTContext &Ctx; DiscriminatorFinder &DF; std::vector LocalDeclContextStack; + const DeclNameRef StringForPrintObjectName; + const DeclNameRef DebuggerTestingCheckExpectName; public: DebuggerTestingTransform(ASTContext &Ctx, DiscriminatorFinder &DF) - : Ctx(Ctx), DF(DF) {} + : Ctx(Ctx), DF(DF), + StringForPrintObjectName(Ctx.getIdentifier("_stringForPrintObject")), + DebuggerTestingCheckExpectName( + Ctx.getIdentifier("_debuggerTestingCheckExpect")) {} bool walkToDeclPre(Decl *D) override { pushLocalDeclContext(D); @@ -200,7 +205,7 @@ class DebuggerTestingTransform : public ASTWalker { // Create _stringForPrintObject($Varname). auto *PODeclRef = new (Ctx) - UnresolvedDeclRefExpr(Ctx.getIdentifier("_stringForPrintObject"), + UnresolvedDeclRefExpr(StringForPrintObjectName, DeclRefKind::Ordinary, DeclNameLoc()); Expr *POArgs[] = {DstRef}; Identifier POLabels[] = {Identifier()}; @@ -211,18 +216,18 @@ class DebuggerTestingTransform : public ASTWalker { Identifier CheckExpectLabels[] = {Identifier(), Identifier()}; Expr *CheckExpectArgs[] = {Varname, POCall}; UnresolvedDeclRefExpr *CheckExpectDRE = new (Ctx) - UnresolvedDeclRefExpr(Ctx.getIdentifier("_debuggerTestingCheckExpect"), + UnresolvedDeclRefExpr(DebuggerTestingCheckExpectName, DeclRefKind::Ordinary, DeclNameLoc()); auto *CheckExpectExpr = CallExpr::createImplicit( Ctx, CheckExpectDRE, CheckExpectArgs, CheckExpectLabels); CheckExpectExpr->setThrows(false); // Create the closure. - TypeChecker &TC = TypeChecker::createForContext(Ctx); auto *Params = ParameterList::createEmpty(Ctx); auto *Closure = new (Ctx) - ClosureExpr(Params, SourceLoc(), SourceLoc(), SourceLoc(), TypeLoc(), - DF.getNextDiscriminator(), getCurrentDeclContext()); + ClosureExpr(SourceRange(), nullptr, Params, SourceLoc(), SourceLoc(), + SourceLoc(), TypeLoc(), DF.getNextDiscriminator(), + getCurrentDeclContext()); Closure->setImplicit(true); // TODO: Save and return the value of $OriginalExpr. @@ -238,7 +243,8 @@ class DebuggerTestingTransform : public ASTWalker { // TODO: typeCheckExpression() seems to assign types to everything here, // but may not be sufficient in some cases. Expr *FinalExpr = ClosureCall; - if (!TC.typeCheckExpression(FinalExpr, getCurrentDeclContext())) + (void)swift::createTypeChecker(Ctx); + if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext())) llvm::report_fatal_error("Could not type-check instrumentation"); // Captures have to be computed after the closure is type-checked. This @@ -255,11 +261,11 @@ void swift::performDebuggerTestingTransform(SourceFile &SF) { // Walk over all decls in the file to find the next available closure // discriminator. DiscriminatorFinder DF; - for (Decl *D : SF.Decls) + for (Decl *D : SF.getTopLevelDecls()) D->walk(DF); // Instrument the decls with checkExpect() sanity-checks. - for (Decl *D : SF.Decls) { + for (Decl *D : SF.getTopLevelDecls()) { DebuggerTestingTransform Transform{D->getASTContext(), DF}; D->walk(Transform); swift::verify(D); diff --git a/lib/Sema/DerivedConformanceCaseIterable.cpp b/lib/Sema/DerivedConformanceCaseIterable.cpp index 40a81958395ec..a1c445efe3257 100644 --- a/lib/Sema/DerivedConformanceCaseIterable.cpp +++ b/lib/Sema/DerivedConformanceCaseIterable.cpp @@ -87,22 +87,19 @@ ValueDecl *DerivedConformance::deriveCaseIterable(ValueDecl *requirement) { if (!canDeriveConformance(Nominal)) return nullptr; - ASTContext &C = TC.Context; - // Build the necessary decl. - if (requirement->getBaseName() != C.Id_allCases) { + if (requirement->getBaseName() != Context.Id_allCases) { requirement->diagnose(diag::broken_case_iterable_requirement); return nullptr; } - // Define the property. auto *returnTy = computeAllCasesType(Nominal); VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = - declareDerivedProperty(C.Id_allCases, returnTy, returnTy, + declareDerivedProperty(Context.Id_allCases, returnTy, returnTy, /*isStatic=*/true, /*isFinal=*/true); // Define the getter. @@ -123,11 +120,12 @@ Type DerivedConformance::deriveCaseIterable(AssociatedTypeDecl *assocType) { if (!canDeriveConformance(Nominal)) return nullptr; - if (assocType->getName() == TC.Context.Id_AllCases) { + if (assocType->getName() == Context.Id_AllCases) { return deriveCaseIterable_AllCases(*this); } - TC.diagnose(assocType->getLoc(), diag::broken_case_iterable_requirement); + Context.Diags.diagnose(assocType->getLoc(), + diag::broken_case_iterable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceCodable.cpp b/lib/Sema/DerivedConformanceCodable.cpp index 16c03a8f62b3c..9d5681d752522 100644 --- a/lib/Sema/DerivedConformanceCodable.cpp +++ b/lib/Sema/DerivedConformanceCodable.cpp @@ -86,25 +86,20 @@ static CodableConformanceType typeConformsToCodable(DeclContext *context, return typeConformsToCodable(context, target->getOptionalObjectType(), false, proto); - return (TypeChecker::conformsToProtocol(target, proto, context, None) - ? Conforms - : DoesNotConform); + auto conf = TypeChecker::conformsToProtocol(target, proto, context, None); + return conf.isInvalid() ? DoesNotConform : Conforms; } /// Returns whether the given variable conforms to the given {En,De}codable /// protocol. /// -/// \param tc The typechecker to use in validating {En,De}codable conformance. -/// -/// \param context The \c DeclContext in which to check conformance. +/// \param DC The \c DeclContext in which to check conformance. /// /// \param varDecl The \c VarDecl to validate. /// /// \param proto The \c ProtocolDecl to check conformance to. -static CodableConformanceType varConformsToCodable(TypeChecker &tc, - DeclContext *context, - VarDecl *varDecl, - ProtocolDecl *proto) { +static CodableConformanceType +varConformsToCodable(DeclContext *DC, VarDecl *varDecl, ProtocolDecl *proto) { // If the decl doesn't yet have a type, we may be seeing it before the type // checker has gotten around to evaluating its type. For example: // @@ -118,8 +113,8 @@ static CodableConformanceType varConformsToCodable(TypeChecker &tc, // // hasn't yet been evaluated // } bool isIUO = varDecl->isImplicitlyUnwrappedOptional(); - return typeConformsToCodable(context, varDecl->getValueInterfaceType(), - isIUO, proto); + return typeConformsToCodable(DC, varDecl->getValueInterfaceType(), isIUO, + proto); } /// Retrieve the variable name for the purposes of encoding/decoding. @@ -136,7 +131,6 @@ static Identifier getVarNameForCoding(VarDecl *var) { /// \param codingKeysDecl The \c CodingKeys enum decl to validate. static bool validateCodingKeysEnum(DerivedConformance &derived, EnumDecl *codingKeysDecl) { - auto &tc = derived.TC; auto conformanceDC = derived.getConformanceContext(); // Look through all var decls in the given type. @@ -162,8 +156,8 @@ static bool validateCodingKeysEnum(DerivedConformance &derived, for (auto elt : codingKeysDecl->getAllElements()) { auto it = properties.find(elt->getName()); if (it == properties.end()) { - tc.diagnose(elt->getLoc(), diag::codable_extraneous_codingkey_case_here, - elt->getName()); + elt->diagnose(diag::codable_extraneous_codingkey_case_here, + elt->getName()); // TODO: Investigate typo-correction here; perhaps the case name was // misspelled and we can provide a fix-it. propertiesAreValid = false; @@ -172,7 +166,7 @@ static bool validateCodingKeysEnum(DerivedConformance &derived, // We have a property to map to. Ensure it's {En,De}codable. auto conformance = - varConformsToCodable(tc, conformanceDC, it->second, derived.Protocol); + varConformsToCodable(conformanceDC, it->second, derived.Protocol); switch (conformance) { case Conforms: // The property was valid. Remove it from the list. @@ -180,9 +174,8 @@ static bool validateCodingKeysEnum(DerivedConformance &derived, break; case DoesNotConform: - tc.diagnose(it->second->getLoc(), - diag::codable_non_conforming_property_here, - derived.getProtocolType(), it->second->getType()); + it->second->diagnose(diag::codable_non_conforming_property_here, + derived.getProtocolType(), it->second->getType()); LLVM_FALLTHROUGH; case TypeNotValidated: @@ -216,8 +209,8 @@ static bool validateCodingKeysEnum(DerivedConformance &derived, // The var was not default initializable, and did not have an explicit // initial value. propertiesAreValid = false; - tc.diagnose(it->second->getLoc(), diag::codable_non_decoded_property_here, - derived.getProtocolType(), it->first); + it->second->diagnose(diag::codable_non_decoded_property_here, + derived.getProtocolType(), it->first); } } @@ -242,8 +235,7 @@ struct CodingKeysValidity { /// /// \returns A \c CodingKeysValidity value representing the result of the check. static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { - auto &tc = derived.TC; - auto &C = tc.Context; + auto &C = derived.Context; auto codingKeysDecls = derived.Nominal->lookupDirect(DeclName(C.Id_CodingKeys)); if (codingKeysDecls.empty()) @@ -256,9 +248,8 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { auto *codingKeysTypeDecl = dyn_cast(result); if (!codingKeysTypeDecl) { - tc.diagnose(result->getLoc(), - diag::codable_codingkeys_type_is_not_an_enum_here, - derived.getProtocolType()); + result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here, + derived.getProtocolType()); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } @@ -271,8 +262,7 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { // Ensure that the type we found conforms to the CodingKey protocol. auto *codingKeyProto = C.getProtocol(KnownProtocolKind::CodingKey); if (!TypeChecker::conformsToProtocol(codingKeysType, codingKeyProto, - derived.getConformanceContext(), - None)) { + derived.getConformanceContext(), None)) { // If CodingKeys is a typealias which doesn't point to a valid nominal type, // codingKeysTypeDecl will be nullptr here. In that case, we need to warn on // the location of the usage, since there isn't an underlying type to @@ -281,8 +271,8 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { codingKeysTypeDecl->getLoc() : cast(result)->getLoc(); - tc.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here, - derived.getProtocolType()); + C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here, + derived.getProtocolType()); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } @@ -290,9 +280,9 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { // CodingKeys must be an enum for synthesized conformance. auto *codingKeysEnum = dyn_cast(codingKeysTypeDecl); if (!codingKeysEnum) { - tc.diagnose(codingKeysTypeDecl->getLoc(), - diag::codable_codingkeys_type_is_not_an_enum_here, - derived.getProtocolType()); + codingKeysTypeDecl->diagnose( + diag::codable_codingkeys_type_is_not_an_enum_here, + derived.getProtocolType()); return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false); } @@ -305,8 +295,7 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) { /// /// If able to synthesize the enum, adds it directly to \c derived.Nominal. static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { - auto &tc = derived.TC; - auto &C = tc.Context; + auto &C = derived.Context; // Create CodingKeys in the parent type always, because both // Encodable and Decodable might want to use it, and they may have // different conditional bounds. CodingKeys is simple and can't @@ -350,7 +339,7 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { // concurrently checking the variables for the current protocol // conformance being synthesized, for which we use the conformance // context, not the type. - auto conformance = varConformsToCodable(tc, derived.getConformanceContext(), + auto conformance = varConformsToCodable(derived.getConformanceContext(), varDecl, derived.Protocol); switch (conformance) { case Conforms: @@ -365,9 +354,8 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { } case DoesNotConform: - tc.diagnose(varDecl->getLoc(), - diag::codable_non_conforming_property_here, - derived.getProtocolType(), varDecl->getType()); + varDecl->diagnose(diag::codable_non_conforming_property_here, + derived.getProtocolType(), varDecl->getType()); LLVM_FALLTHROUGH; case TypeNotValidated: @@ -382,7 +370,7 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) { return nullptr; // Forcibly derive conformance to CodingKey. - tc.checkConformancesInContext(enumDecl, enumDecl); + TypeChecker::checkConformancesInContext(enumDecl, enumDecl); // Add to the type. target->addMember(enumDecl); @@ -472,17 +460,13 @@ static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC, keyedByDecl->setSpecifier(ParamSpecifier::Default); keyedByDecl->setInterfaceType(returnType); - // container(keyedBy:) method name - auto *paramList = ParameterList::createWithoutLoc(keyedByDecl); - DeclName callName(C, C.Id_container, paramList); - // base.container(keyedBy:) expr - auto *unboundCall = new (C) UnresolvedDotExpr(base, SourceLoc(), callName, - DeclNameLoc(), - /*Implicit=*/true); + auto *paramList = ParameterList::createWithoutLoc(keyedByDecl); + auto *unboundCall = UnresolvedDotExpr::createImplicit(C, base, C.Id_container, + paramList); // CodingKeys.self expr - auto *codingKeysExpr = TypeExpr::createForDecl(SourceLoc(), + auto *codingKeysExpr = TypeExpr::createForDecl(DeclNameLoc(), param, param->getDeclContext(), /*Implicit=*/true); @@ -584,7 +568,7 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { auto codingKeysType = codingKeysEnum->getDeclaredType(); auto *containerDecl = createKeyedContainer(C, funcDC, C.getKeyedEncodingContainerDecl(), - codingKeysType, + codingKeysEnum->getDeclaredInterfaceType(), VarDecl::Introducer::Var); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), @@ -640,12 +624,10 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { // encode(_:forKey:)/encodeIfPresent(_:forKey:) auto methodName = useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode; - SmallVector argNames{Identifier(), C.Id_forKey}; - DeclName name(C, methodName, argNames); - auto *encodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), - name, DeclNameLoc(), - /*Implicit=*/true); + + auto *encodeCall = UnresolvedDotExpr::createImplicit(C, containerExpr, + methodName, argNames); // container.encode(self.x, forKey: CodingKeys.x) Expr *args[2] = {varExpr, keyExpr}; @@ -665,9 +647,7 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { // Need to generate `try super.encode(to: container.superEncoder())` // superEncoder() - auto *method = new (C) UnresolvedDeclRefExpr(DeclName(C.Id_superEncoder), - DeclRefKind::Ordinary, - DeclNameLoc()); + auto *method = UnresolvedDeclRefExpr::createImplicit(C, C.Id_superEncoder); // container.superEncoder() auto *superEncoderRef = new (C) DotSyntaxCallExpr(containerExpr, @@ -708,7 +688,7 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) { /// /// Adds the function declaration to the given type before returning it. static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { - auto &C = derived.TC.Context; + auto &C = derived.Context; auto conformanceDC = derived.getConformanceContext(); // Expected type: (Self) -> (Encoder) throws -> () @@ -750,14 +730,11 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) { encodeDecl->getAttrs().add(attr); } - encodeDecl->computeType(FunctionType::ExtInfo().withThrows()); - encodeDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(encodeDecl); - derived.addMembersToConformanceContext({encodeDecl}); + return encodeDecl; } @@ -806,7 +783,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { auto codingKeysType = codingKeysEnum->getDeclaredType(); auto *containerDecl = createKeyedContainer(C, funcDC, C.getKeyedDecodingContainerDecl(), - codingKeysType, + codingKeysEnum->getDeclaredInterfaceType(), VarDecl::Introducer::Let); auto *containerExpr = new (C) DeclRefExpr(ConcreteDeclRef(containerDecl), @@ -874,10 +851,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { // decode(_:forKey:)/decodeIfPresent(_:forKey:) SmallVector argNames{Identifier(), C.Id_forKey}; - DeclName name(C, methodName, argNames); - auto *decodeCall = new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), - name, DeclNameLoc(), - /*Implicit=*/true); + auto *decodeCall = UnresolvedDotExpr::createImplicit( + C, containerExpr, methodName, argNames); // container.decode(Type.self, forKey: CodingKeys.x) Expr *args[2] = {targetExpr, keyExpr}; @@ -890,10 +865,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { /*Implicit=*/true); auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); - auto *varExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - DeclName(varDecl->getName()), - DeclNameLoc(), - /*implicit=*/true); + auto *varExpr = UnresolvedDotExpr::createImplicit(C, selfRef, + varDecl->getFullName()); auto *assignExpr = new (C) AssignExpr(varExpr, SourceLoc(), tryExpr, /*Implicit=*/true); statements.push_back(assignExpr); @@ -909,9 +882,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { // container.superDecoder auto *superDecoderRef = - new (C) UnresolvedDotExpr(containerExpr, SourceLoc(), - DeclName(C.Id_superDecoder), - DeclNameLoc(), /*Implicit=*/true); + UnresolvedDotExpr::createImplicit(C, containerExpr, + C.Id_superDecoder); // container.superDecoder() auto *superDecoderCall = @@ -923,10 +895,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { SourceLoc(), /*Implicit=*/true); // super.init(from:) - auto initName = DeclName(C, DeclBaseName::createConstructor(), C.Id_from); - auto *initCall = new (C) UnresolvedDotExpr(superRef, SourceLoc(), - initName, DeclNameLoc(), - /*Implicit=*/true); + auto *initCall = UnresolvedDotExpr::createImplicit( + C, superRef, DeclBaseName::createConstructor(), {C.Id_from}); // super.decode(from: container.superDecoder()) Expr *args[1] = {superDecoderCall}; @@ -959,9 +929,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { SourceLoc(), /*Implicit=*/true); // super.init() - auto *superInitRef = new (C) UnresolvedDotExpr(superRef, SourceLoc(), - initName, DeclNameLoc(), - /*Implicit=*/true); + auto *superInitRef = UnresolvedDotExpr::createImplicit(C, superRef, + initName); // super.init() call Expr *callExpr = CallExpr::createImplicit(C, superInitRef, ArrayRef(), @@ -987,7 +956,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) { /// /// Adds the function declaration to the given type before returning it. static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { - auto &C = derived.TC.Context; + auto &C = derived.Context; auto classDecl = dyn_cast(derived.Nominal); auto conformanceDC = derived.getConformanceContext(); @@ -1030,14 +999,11 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) { initDecl->getAttrs().add(reqAttr); } - initDecl->computeType(AnyFunctionType::ExtInfo().withThrows()); - initDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); + return initDecl; } @@ -1058,8 +1024,6 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { // // If the required initializer is not available, we shouldn't attempt to // synthesize CodingKeys. - auto &tc = derived.TC; - ASTContext &C = tc.Context; auto proto = derived.Protocol; auto *classDecl = dyn_cast(derived.Nominal); if (proto->isSpecificProtocol(KnownProtocolKind::Decodable) && classDecl) { @@ -1074,11 +1038,14 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) { // super.init() must be accessible. // Passing an empty params array constructs a compound name with no // arguments (as opposed to a simple name when omitted). - memberName = DeclName(C, DeclBaseName::createConstructor(), - ArrayRef()); + memberName = + DeclName(derived.Context, DeclBaseName::createConstructor(), + ArrayRef()); } - auto result = tc.lookupMember(superclassDecl, superType, memberName); + auto result = + TypeChecker::lookupMember(superclassDecl, superType, + DeclNameRef(memberName)); if (result.empty()) { // No super initializer for us to call. @@ -1140,9 +1107,9 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) { if (!isa(Nominal) && !isa(Nominal)) return nullptr; - if (requirement->getBaseName() != TC.Context.Id_encode) { + if (requirement->getBaseName() != Context.Id_encode) { // Unknown requirement. - TC.diagnose(requirement->getLoc(), diag::broken_encodable_requirement); + requirement->diagnose(diag::broken_encodable_requirement); return nullptr; } @@ -1164,12 +1131,12 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) { // diagnostics, then potentially collect notes. If we succeed in // synthesizing Encodable, we can cancel the transaction and get rid of the // fake failures. - DiagnosticTransaction diagnosticTransaction(TC.Context.Diags); - TC.diagnose(ConformanceDecl, diag::type_does_not_conform, - Nominal->getDeclaredType(), getProtocolType()); - TC.diagnose(requirement, diag::no_witnesses, diag::RequirementKind::Func, - requirement->getFullName(), getProtocolType(), - /*AddFixIt=*/false); + DiagnosticTransaction diagnosticTransaction(Context.Diags); + ConformanceDecl->diagnose(diag::type_does_not_conform, + Nominal->getDeclaredType(), getProtocolType()); + requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Func, + requirement->getFullName(), getProtocolType(), + /*AddFixIt=*/false); // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. @@ -1188,7 +1155,7 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) { if (requirement->getBaseName() != DeclBaseName::createConstructor()) { // Unknown requirement. - TC.diagnose(requirement->getLoc(), diag::broken_decodable_requirement); + requirement->diagnose(diag::broken_decodable_requirement); return nullptr; } @@ -1200,12 +1167,12 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) { // diagnostics produced by canSynthesize and deriveDecodable_init to produce // them in the right order -- see the comment in deriveEncodable for // background on this transaction. - DiagnosticTransaction diagnosticTransaction(TC.Context.Diags); - TC.diagnose(ConformanceDecl->getLoc(), diag::type_does_not_conform, - Nominal->getDeclaredType(), getProtocolType()); - TC.diagnose(requirement, diag::no_witnesses, - diag::RequirementKind::Constructor, requirement->getFullName(), - getProtocolType(), /*AddFixIt=*/false); + DiagnosticTransaction diagnosticTransaction(Context.Diags); + ConformanceDecl->diagnose(diag::type_does_not_conform, + Nominal->getDeclaredType(), getProtocolType()); + requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Constructor, + requirement->getFullName(), getProtocolType(), + /*AddFixIt=*/false); // Check other preconditions for synthesized conformance. // This synthesizes a CodingKeys enum if possible. diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index a6e399cb79e80..9b72cbebb2209 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -50,9 +50,8 @@ deriveRawValueReturn(AbstractFunctionDecl *funcDecl, void *) { auto &C = parentDC->getASTContext(); auto *selfRef = DerivedConformance::createSelfDeclRef(funcDecl); - auto *memberRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - C.Id_rawValue, DeclNameLoc(), - /*Implicit=*/true); + auto *memberRef = + UnresolvedDotExpr::createImplicit(C, selfRef, C.Id_rawValue); auto *returnStmt = new (C) ReturnStmt(SourceLoc(), memberRef); auto *body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt), @@ -84,13 +83,10 @@ deriveRawValueInit(AbstractFunctionDecl *initDecl, void *) { rawValueDecl->setImplicit(); auto *paramList = ParameterList::createWithoutLoc(rawValueDecl); - // init(rawValue:) constructor name - DeclName ctorName(C, DeclBaseName::createConstructor(), paramList); - // self.init(rawValue:) expr auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl); - auto *initExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), ctorName, - DeclNameLoc(), /*Implicit=*/true); + auto *initExpr = UnresolvedDotExpr::createImplicit( + C, selfRef, DeclBaseName::createConstructor(), paramList); // Bind the value param in self.init(rawValue: {string,int}Value). Expr *args[1] = {valueParamExpr}; @@ -115,7 +111,7 @@ template static ValueDecl *deriveInitDecl(DerivedConformance &derived, Type paramType, Identifier paramName, const Synthesizer &synthesizer) { - auto &C = derived.TC.Context; + auto &C = derived.Context; auto *parentDC = derived.getConformanceContext(); // rawValue @@ -143,14 +139,10 @@ static ValueDecl *deriveInitDecl(DerivedConformance &derived, Type paramType, // Synthesize the body. synthesizer(initDecl); - // Compute the interface type of the initializer. - initDecl->computeType(); - initDecl->setAccess(derived.Nominal->getFormalAccess()); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); + return initDecl; } @@ -179,9 +171,7 @@ static ValueDecl *deriveProperty(DerivedConformance &derived, Type type, // Synthesize the body. synthesizer(getterDecl); - auto *dc = cast(derived.ConformanceDecl); - dc->addMember(propDecl); - dc->addMember(pbDecl); + derived.addMembersToConformanceContext({propDecl, pbDecl}); return propDecl; } @@ -223,8 +213,8 @@ deriveBodyCodingKey_enum_stringValue(AbstractFunctionDecl *strValDecl, void *) { SmallVector cases; for (auto *elt : elements) { auto *pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, nullptr); pat->setImplicit(); auto labelItem = CaseLabelItem(pat); @@ -340,7 +330,7 @@ static bool canSynthesizeCodingKey(DerivedConformance &derived) { auto *parentDC = derived.getConformanceContext(); rawType = parentDC->mapTypeIntoContext(rawType); - auto &C = derived.TC.Context; + auto &C = derived.Context; auto *nominal = rawType->getCanonicalType()->getAnyNominal(); if (nominal != C.getStringDecl() && nominal != C.getIntDecl()) return false; @@ -367,12 +357,11 @@ ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { if (!canSynthesizeCodingKey(*this)) return nullptr; - auto &C = TC.Context; auto rawType = enumDecl->getRawType(); auto name = requirement->getBaseName(); - if (name == C.Id_stringValue) { + if (name == Context.Id_stringValue) { // Synthesize `var stringValue: String { get }` - auto stringType = C.getStringDecl()->getDeclaredType(); + auto stringType = Context.getStringDecl()->getDeclaredType(); auto synth = [rawType, stringType](AbstractFunctionDecl *getterDecl) { if (rawType && rawType->isEqual(stringType)) { // enum SomeStringEnum : String { @@ -399,11 +388,11 @@ ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { } }; - return deriveProperty(*this, stringType, C.Id_stringValue, synth); + return deriveProperty(*this, stringType, Context.Id_stringValue, synth); - } else if (name == C.Id_intValue) { + } else if (name == Context.Id_intValue) { // Synthesize `var intValue: Int? { get }` - auto intType = C.getIntDecl()->getDeclaredType(); + auto intType = Context.getIntDecl()->getDeclaredType(); auto optionalIntType = OptionalType::get(intType); auto synth = [rawType, intType](AbstractFunctionDecl *getterDecl) { @@ -426,13 +415,13 @@ ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { } }; - return deriveProperty(*this, optionalIntType, C.Id_intValue, synth); + return deriveProperty(*this, optionalIntType, Context.Id_intValue, synth); } else if (name == DeclBaseName::createConstructor()) { auto argumentNames = requirement->getFullName().getArgumentNames(); if (argumentNames.size() == 1) { - if (argumentNames[0] == C.Id_stringValue) { + if (argumentNames[0] == Context.Id_stringValue) { // Derive `init?(stringValue:)` - auto stringType = C.getStringDecl()->getDeclaredType(); + auto stringType = Context.getStringDecl()->getDeclaredType(); auto synth = [rawType, stringType](AbstractFunctionDecl *initDecl) { if (rawType && rawType->isEqual(stringType)) { // enum SomeStringEnum : String { @@ -462,10 +451,10 @@ ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { } }; - return deriveInitDecl(*this, stringType, C.Id_stringValue, synth); - } else if (argumentNames[0] == C.Id_intValue) { + return deriveInitDecl(*this, stringType, Context.Id_stringValue, synth); + } else if (argumentNames[0] == Context.Id_intValue) { // Synthesize `init?(intValue:)` - auto intType = C.getIntDecl()->getDeclaredType(); + auto intType = Context.getIntDecl()->getDeclaredType(); auto synthesizer = [rawType, intType](AbstractFunctionDecl *initDecl) { if (rawType && rawType->isEqual(intType)) { // enum SomeIntEnum : Int { @@ -486,11 +475,12 @@ ValueDecl *DerivedConformance::deriveCodingKey(ValueDecl *requirement) { } }; - return deriveInitDecl(*this, intType, C.Id_intValue, synthesizer); + return deriveInitDecl(*this, intType, Context.Id_intValue, synthesizer); } } } - TC.diagnose(requirement->getLoc(), diag::broken_coding_key_requirement); + Context.Diags.diagnose(requirement->getLoc(), + diag::broken_coding_key_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index fde9b9c710c95..244c4b9d60c63 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -53,8 +53,9 @@ associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, for (auto param : *PL) { auto type = param->getInterfaceType(); - if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), - protocol, DC, None)) { + if (TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), + protocol, DC, None) + .isInvalid()) { nonconformingAssociatedValues.push_back(param); } } @@ -164,6 +165,12 @@ void diagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal, nominal->getDeclaredInterfaceType()); } } + + if (auto *classDecl = dyn_cast(nominal)) { + ctx.Diags.diagnose(classDecl->getLoc(), + diag::classes_automatic_protocol_synthesis, + protocol->getName().str()); + } } /// Creates a named variable based on a prefix character and a numeric index. @@ -186,7 +193,7 @@ static VarDecl *indexedVarDecl(char prefixChar, int index, Type type, /*IsCaptureList*/true, SourceLoc(), C.getIdentifier(indexStrRef), varContext); - varDecl->setType(type); + varDecl->setInterfaceType(type); varDecl->setHasNonPatternBindingInit(true); return varDecl; } @@ -301,8 +308,8 @@ static DeclRefExpr *convertEnumToIndex(SmallVectorImpl &stmts, for (auto elt : enumDecl->getAllElements()) { // generate: case .: auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, nullptr); pat->setImplicit(); pat->setType(enumType); @@ -362,7 +369,7 @@ static GuardStmt *returnIfNotEqualGuard(ASTContext &C, // Next, generate the condition being checked. // lhs == rhs auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclName(C.getIdentifier("==")), DeclRefKind::BinaryOperator, + DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), { lhsExpr, rhsExpr }, @@ -504,8 +511,8 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl, auto lhsSubpattern = enumElementPayloadSubpattern(elt, 'l', eqDecl, lhsPayloadVars); auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, lhsSubpattern); lhsElemPat->setImplicit(); @@ -514,8 +521,8 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl, auto rhsSubpattern = enumElementPayloadSubpattern(elt, 'r', eqDecl, rhsPayloadVars); auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, rhsSubpattern); rhsElemPat->setImplicit(); @@ -700,7 +707,7 @@ deriveEquatable_eq( // } // } - ASTContext &C = derived.TC.Context; + ASTContext &C = derived.Context; auto parentDC = derived.getConformanceContext(); auto selfIfaceTy = parentDC->getDeclaredInterfaceType(); @@ -760,20 +767,14 @@ deriveEquatable_eq( } if (!C.getEqualIntDecl()) { - derived.TC.diagnose(derived.ConformanceDecl->getLoc(), - diag::no_equal_overload_for_int); + derived.ConformanceDecl->diagnose(diag::no_equal_overload_for_int); return nullptr; } eqDecl->setBodySynthesizer(bodySynthesizer); - // Compute the interface type. - eqDecl->computeType(); - eqDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - C.addSynthesizedDecl(eqDecl); - // Add the operator to the parent scope. derived.addMembersToConformanceContext({eqDecl}); @@ -807,7 +808,7 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) { else llvm_unreachable("todo"); } - TC.diagnose(requirement->getLoc(), diag::broken_equatable_requirement); + requirement->diagnose(diag::broken_equatable_requirement); return nullptr; } @@ -832,11 +833,9 @@ static CallExpr *createHasherCombineCall(ASTContext &C, Expr *hashable) { Expr *hasherExpr = new (C) DeclRefExpr(ConcreteDeclRef(hasher), DeclNameLoc(), /*implicit*/ true); - DeclName name(C, C.Id_combine, {Identifier()}); // hasher.combine(_:) - auto *combineCall = new (C) UnresolvedDotExpr(hasherExpr, SourceLoc(), - name, DeclNameLoc(), - /*implicit*/ true); + auto *combineCall = UnresolvedDotExpr::createImplicit( + C, hasherExpr, C.Id_combine, {Identifier()}); // hasher.combine(hashable) return CallExpr::createImplicit(C, combineCall, {hashable}, {Identifier()}); @@ -849,7 +848,7 @@ deriveHashable_hashInto( void *)) { // @derived func hash(into hasher: inout Hasher) - ASTContext &C = derived.TC.Context; + ASTContext &C = derived.Context; auto parentDC = derived.getConformanceContext(); // Expected type: (Self) -> (into: inout Hasher) -> () @@ -862,8 +861,7 @@ deriveHashable_hashInto( auto hasherDecl = C.getHasherDecl(); if (!hasherDecl) { auto hashableProto = C.getProtocol(KnownProtocolKind::Hashable); - derived.TC.diagnose(hashableProto->getLoc(), - diag::broken_hashable_no_hasher); + hashableProto->diagnose(diag::broken_hashable_no_hasher); return nullptr; } Type hasherType = hasherDecl->getDeclaredType(); @@ -892,12 +890,10 @@ deriveHashable_hashInto( hashDecl->setImplicit(); hashDecl->setBodySynthesizer(bodySynthesizer); - hashDecl->computeType(); hashDecl->copyFormalAccessFrom(derived.Nominal); - C.addSynthesizedDecl(hashDecl); - derived.addMembersToConformanceContext({hashDecl}); + return hashDecl; } @@ -914,9 +910,8 @@ deriveBodyHashable_compat_hashInto(AbstractFunctionDecl *hashIntoDecl, void *) { auto selfDecl = hashIntoDecl->getImplicitSelfDecl(); auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/ true); - auto hashValueExpr = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - C.Id_hashValue, DeclNameLoc(), - /*implicit*/ true); + auto hashValueExpr = UnresolvedDotExpr::createImplicit(C, selfRef, + C.Id_hashValue); auto hasherParam = hashIntoDecl->getParameters()->get(0); auto hasherExpr = createHasherCombineCall(C, hasherParam, hashValueExpr); @@ -940,9 +935,8 @@ deriveBodyHashable_enum_rawValue_hashInto( // generate: self.rawValue auto *selfRef = DerivedConformance::createSelfDeclRef(hashIntoDecl); - auto *rawValueRef = new (C) UnresolvedDotExpr(selfRef, SourceLoc(), - C.Id_rawValue, DeclNameLoc(), - /*Implicit=*/true); + auto *rawValueRef = UnresolvedDotExpr::createImplicit(C, selfRef, + C.Id_rawValue); // generate: hasher.combine(discriminator) auto hasherParam = hashIntoDecl->getParameters()->get(0); @@ -1039,8 +1033,9 @@ deriveBodyHashable_enum_hasAssociatedValues_hashInto( auto payloadPattern = enumElementPayloadSubpattern(elt, 'a', hashIntoDecl, payloadVars); auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - elt->getName(), elt, payloadPattern); + SourceLoc(), DeclNameLoc(), + DeclNameRef(elt->getName()), elt, + payloadPattern); pat->setImplicit(); auto labelItem = CaseLabelItem(pat); @@ -1197,25 +1192,24 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // @derived var hashValue: Int { // return _hashValue(for: self) // } - auto &tc = derived.TC; - ASTContext &C = tc.Context; + ASTContext &C = derived.Context; auto parentDC = derived.getConformanceContext(); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. - if (!TypeChecker::conformsToProtocol(intType, - C.getProtocol(KnownProtocolKind::Hashable), - parentDC, None)) { + if (TypeChecker::conformsToProtocol( + intType, C.getProtocol(KnownProtocolKind::Hashable), parentDC, None) + .isInvalid()) { derived.ConformanceDecl->diagnose(diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); - if (!TypeChecker::conformsToProtocol(intType, intLiteralProto, - parentDC, None)) { + if (TypeChecker::conformsToProtocol(intType, intLiteralProto, parentDC, None) + .isInvalid()) { derived.ConformanceDecl->diagnose( diag::broken_int_integer_literal_convertible_conformance); return nullptr; @@ -1225,7 +1219,7 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var, /*IsCaptureList*/false, SourceLoc(), C.Id_hashValue, parentDC); - hashValueDecl->setType(intType); + hashValueDecl->setInterfaceType(intType); ParameterList *params = ParameterList::createEmpty(C); @@ -1240,9 +1234,6 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue); getterDecl->setIsTransparent(false); - // Compute the interface type of hashValue(). - getterDecl->computeType(); - getterDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); @@ -1262,10 +1253,9 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { auto *patDecl = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, hashValuePat, /*InitExpr*/ nullptr, parentDC); - C.addSynthesizedDecl(hashValueDecl); - C.addSynthesizedDecl(getterDecl); derived.addMembersToConformanceContext({hashValueDecl, patDecl}); + return hashValueDecl; } diff --git a/lib/Sema/DerivedConformanceError.cpp b/lib/Sema/DerivedConformanceError.cpp index b18a2dc638373..baf20f85e8f03 100644 --- a/lib/Sema/DerivedConformanceError.cpp +++ b/lib/Sema/DerivedConformanceError.cpp @@ -42,7 +42,7 @@ deriveBodyBridgedNSError_enum_nsErrorDomain(AbstractFunctionDecl *domainDecl, auto self = domainDecl->getImplicitSelfDecl(); auto selfRef = new (C) DeclRefExpr(self, DeclNameLoc(), /*implicit*/ true); - auto stringType = TypeExpr::createForDecl(SourceLoc(), C.getStringDecl(), + auto stringType = TypeExpr::createForDecl(DeclNameLoc(), C.getStringDecl(), domainDecl, /*implicit*/ true); auto initReflectingCall = CallExpr::createImplicit(C, stringType, @@ -90,15 +90,13 @@ deriveBridgedNSError_enum_nsErrorDomain( // } // } - ASTContext &C = derived.TC.Context; - - auto stringTy = C.getStringDecl()->getDeclaredType(); + auto stringTy = derived.Context.getStringDecl()->getDeclaredType(); // Define the property. VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - C.Id_nsErrorDomain, stringTy, stringTy, /*isStatic=*/true, + derived.Context.Id_nsErrorDomain, stringTy, stringTy, /*isStatic=*/true, /*isFinal=*/true); // Define the getter. @@ -115,7 +113,7 @@ ValueDecl *DerivedConformance::deriveBridgedNSError(ValueDecl *requirement) { if (!isa(Nominal)) return nullptr; - if (requirement->getBaseName() == TC.Context.Id_nsErrorDomain) { + if (requirement->getBaseName() == Context.Id_nsErrorDomain) { auto synthesizer = deriveBodyBridgedNSError_enum_nsErrorDomain; auto scope = Nominal->getFormalAccessScope(Nominal->getModuleScopeContext()); @@ -127,6 +125,7 @@ ValueDecl *DerivedConformance::deriveBridgedNSError(ValueDecl *requirement) { return deriveBridgedNSError_enum_nsErrorDomain(*this, synthesizer); } - TC.diagnose(requirement->getLoc(), diag::broken_errortype_requirement); + Context.Diags.diagnose(requirement->getLoc(), + diag::broken_errortype_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index 2cb388e7a2214..c699ff409c574 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -43,6 +43,9 @@ static LiteralExpr *cloneRawLiteralExpr(ASTContext &C, LiteralExpr *expr) { /*implicit*/ true); if (floatLit->isNegative()) cast(clone)->setNegative(expr->getLoc()); + } else if (auto boolLit = dyn_cast(expr)) { + clone = new (C) BooleanLiteralExpr(boolLit->getValue(), expr->getLoc(), + /*implicit*/true); } else { llvm_unreachable("invalid raw literal expr"); } @@ -88,10 +91,8 @@ deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl, void *) { // a bitcast. // return unsafeBitCast(self, to: RawType.self) - DeclName name(C, C.getIdentifier("unsafeBitCast"), {Identifier(), C.Id_to}); - auto functionRef = new (C) UnresolvedDeclRefExpr(name, - DeclRefKind::Ordinary, - DeclNameLoc()); + auto functionRef = UnresolvedDeclRefExpr::createImplicit( + C, C.getIdentifier("unsafeBitCast"), {Identifier(), C.Id_to}); auto selfRef = DerivedConformance::createSelfDeclRef(toRawDecl); auto bareTypeExpr = TypeExpr::createImplicit(rawTy, C); auto typeExpr = new (C) DotSelfExpr(bareTypeExpr, SourceLoc(), SourceLoc()); @@ -108,8 +109,8 @@ deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl, void *) { SmallVector cases; for (auto elt : enumDecl->getAllElements()) { auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, nullptr); pat->setImplicit(); auto labelItem = CaseLabelItem(pat); @@ -136,7 +137,7 @@ deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl, void *) { static void maybeMarkAsInlinable(DerivedConformance &derived, AbstractFunctionDecl *afd) { - ASTContext &C = derived.TC.Context; + ASTContext &C = derived.Context; auto parentDC = derived.getConformanceContext(); if (!parentDC->getParentModule()->isResilient()) { AccessScope access = @@ -150,7 +151,7 @@ static void maybeMarkAsInlinable(DerivedConformance &derived, } static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) { - ASTContext &C = derived.TC.Context; + ASTContext &C = derived.Context; auto enumDecl = cast(derived.Nominal); auto parentDC = derived.getConformanceContext(); @@ -371,9 +372,8 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) { Expr *switchArg = rawRef; if (isStringEnum) { // Call _findStringSwitchCase with an array of strings as argument. - auto *Fun = new (C) UnresolvedDeclRefExpr( - C.getIdentifier("_findStringSwitchCase"), - DeclRefKind::Ordinary, DeclNameLoc()); + auto *Fun = UnresolvedDeclRefExpr::createImplicit( + C, C.getIdentifier("_findStringSwitchCase")); auto *strArray = ArrayExpr::create(C, SourceLoc(), stringExprs, {}, SourceLoc());; Identifier tableId = C.getIdentifier("cases"); @@ -393,19 +393,18 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) { static ConstructorDecl * deriveRawRepresentable_init(DerivedConformance &derived) { - auto &tc = derived.TC; - ASTContext &C = tc.Context; + ASTContext &C = derived.Context; auto enumDecl = cast(derived.Nominal); auto parentDC = derived.getConformanceContext(); auto rawInterfaceType = enumDecl->getRawType(); auto rawType = parentDC->mapTypeIntoContext(rawInterfaceType); - auto equatableProto = tc.getProtocol(enumDecl->getLoc(), - KnownProtocolKind::Equatable); + auto equatableProto = TypeChecker::getProtocol(C, enumDecl->getLoc(), + KnownProtocolKind::Equatable); assert(equatableProto); - assert(TypeChecker::conformsToProtocol(rawType, equatableProto, - enumDecl, None)); + assert( + TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl, None)); (void)equatableProto; (void)rawType; @@ -429,30 +428,27 @@ deriveRawRepresentable_init(DerivedConformance &derived) { initDecl->setImplicit(); initDecl->setBodySynthesizer(&deriveBodyRawRepresentable_init); - // Compute the interface type of the initializer. - initDecl->computeType(); - initDecl->copyFormalAccessFrom(enumDecl, /*sourceIsParentContext*/true); // If the containing module is not resilient, make sure clients can construct // an instance without function call overhead. maybeMarkAsInlinable(derived, initDecl); - C.addSynthesizedDecl(initDecl); - derived.addMembersToConformanceContext({initDecl}); return initDecl; } -static bool canSynthesizeRawRepresentable(DerivedConformance &derived) { - auto enumDecl = cast(derived.Nominal); - auto &tc = derived.TC; +bool DerivedConformance::canDeriveRawRepresentable(DeclContext *DC, + NominalTypeDecl *type) { + auto enumDecl = dyn_cast(type); + if (!enumDecl) + return false; Type rawType = enumDecl->getRawType(); if (!rawType) return false; - auto parentDC = cast(derived.ConformanceDecl); - rawType = parentDC->mapTypeIntoContext(rawType); + + rawType = DC->mapTypeIntoContext(rawType); auto inherited = enumDecl->getInherited(); if (!inherited.empty() && inherited.front().wasValidated() && @@ -462,12 +458,13 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) { // The raw type must be Equatable, so that we have a suitable ~= for // synthesized switch statements. auto equatableProto = - tc.getProtocol(enumDecl->getLoc(), KnownProtocolKind::Equatable); + TypeChecker::getProtocol(enumDecl->getASTContext(), enumDecl->getLoc(), + KnownProtocolKind::Equatable); if (!equatableProto) return false; - if (!TypeChecker::conformsToProtocol(rawType, equatableProto, - enumDecl, None)) + if (TypeChecker::conformsToProtocol(rawType, equatableProto, DC, None) + .isInvalid()) return false; // There must be enum elements. @@ -483,8 +480,6 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) { if (elt->hasAssociatedValues()) return false; - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface type. - (void)elt->getInterfaceType(); if (elt->isInvalid()) { return false; } @@ -496,39 +491,32 @@ static bool canSynthesizeRawRepresentable(DerivedConformance &derived) { ValueDecl *DerivedConformance::deriveRawRepresentable(ValueDecl *requirement) { - // We can only synthesize RawRepresentable for enums. - if (!isa(Nominal)) + // Check preconditions for synthesized conformance. + if (!canDeriveRawRepresentable(cast(ConformanceDecl), Nominal)) return nullptr; - // Check other preconditions for synthesized conformance. - if (!canSynthesizeRawRepresentable(*this)) - return nullptr; - - if (requirement->getBaseName() == TC.Context.Id_rawValue) + if (requirement->getBaseName() == Context.Id_rawValue) return deriveRawRepresentable_raw(*this); if (requirement->getBaseName() == DeclBaseName::createConstructor()) return deriveRawRepresentable_init(*this); - TC.diagnose(requirement->getLoc(), - diag::broken_raw_representable_requirement); + Context.Diags.diagnose(requirement->getLoc(), + diag::broken_raw_representable_requirement); return nullptr; } Type DerivedConformance::deriveRawRepresentable(AssociatedTypeDecl *assocType) { - // We can only synthesize RawRepresentable for enums. - if (!isa(Nominal)) - return nullptr; - - // Check other preconditions for synthesized conformance. - if (!canSynthesizeRawRepresentable(*this)) + // Check preconditions for synthesized conformance. + if (!canDeriveRawRepresentable(cast(ConformanceDecl), Nominal)) return nullptr; - if (assocType->getName() == TC.Context.Id_RawValue) { + if (assocType->getName() == Context.Id_RawValue) { return deriveRawRepresentable_Raw(*this); } - TC.diagnose(assocType->getLoc(), diag::broken_raw_representable_requirement); + Context.Diags.diagnose(assocType->getLoc(), + diag::broken_raw_representable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 4bb0cf1a483c9..7087db91db91e 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -17,16 +17,17 @@ #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" #include "DerivedConformances.h" using namespace swift; -DerivedConformance::DerivedConformance(TypeChecker &tc, Decl *conformanceDecl, +DerivedConformance::DerivedConformance(ASTContext &ctx, Decl *conformanceDecl, NominalTypeDecl *nominal, ProtocolDecl *protocol) - : TC(tc), ConformanceDecl(conformanceDecl), Nominal(nominal), + : Context(ctx), ConformanceDecl(conformanceDecl), Nominal(nominal), Protocol(protocol) { assert(getConformanceContext()->getSelfNominalTypeDecl() == nominal); } @@ -38,8 +39,11 @@ DeclContext *DerivedConformance::getConformanceContext() const { void DerivedConformance::addMembersToConformanceContext( ArrayRef children) { auto IDC = cast(ConformanceDecl); + auto *SF = ConformanceDecl->getDeclContext()->getParentSourceFile(); for (auto child : children) { IDC->addMember(child); + if (SF) + SF->SynthesizedDecls.push_back(child); } } @@ -67,7 +71,7 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, // The presence of a raw type is an explicit declaration that // the compiler should derive a RawRepresentable conformance. case KnownProtocolKind::RawRepresentable: - return enumDecl->hasRawType(); + return canDeriveRawRepresentable(DC, Nominal); // Enums without associated values can implicitly derive Equatable // conformance. @@ -100,7 +104,7 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, } // hasOnlyCasesWithoutAssociatedValues will return true for empty enums; - // empty enumas are allowed to conform as well. + // empty enums are allowed to conform as well. return enumDecl->hasOnlyCasesWithoutAssociatedValues(); } @@ -167,10 +171,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, auto proto = ctx.getProtocol(kind); if (!proto) return nullptr; - if (auto conformance = TypeChecker::conformsToProtocol( - nominal->getDeclaredInterfaceType(), proto, nominal, - ConformanceCheckFlags::SkipConditionalRequirements)) { - auto DC = conformance->getConcrete()->getDeclContext(); + auto conformance = TypeChecker::conformsToProtocol( + nominal->getDeclaredInterfaceType(), proto, nominal, + ConformanceCheckFlags::SkipConditionalRequirements); + if (conformance) { + auto DC = conformance.getConcrete()->getDeclContext(); // Check whether this nominal type derives conformances to the protocol. if (!DerivedConformance::derivesProtocolConformance(DC, nominal, proto)) return nullptr; @@ -308,12 +313,8 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, getterDecl->setImplicit(); getterDecl->setIsTransparent(false); - // Compute the interface type of the getter. - getterDecl->computeType(); - getterDecl->copyFormalAccessFrom(property); - C.addSynthesizedDecl(getterDecl); return getterDecl; } @@ -323,24 +324,24 @@ DerivedConformance::declareDerivedProperty(Identifier name, Type propertyInterfaceType, Type propertyContextType, bool isStatic, bool isFinal) { - auto &C = TC.Context; auto parentDC = getConformanceContext(); - VarDecl *propDecl = new (C) VarDecl(/*IsStatic*/isStatic, VarDecl::Introducer::Var, - /*IsCaptureList*/false, SourceLoc(), name, - parentDC); + VarDecl *propDecl = new (Context) + VarDecl(/*IsStatic*/ isStatic, VarDecl::Introducer::Var, + /*IsCaptureList*/ false, SourceLoc(), name, parentDC); propDecl->setImplicit(); propDecl->copyFormalAccessFrom(Nominal, /*sourceIsParentContext*/ true); propDecl->setInterfaceType(propertyInterfaceType); - Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true); + Pattern *propPat = new (Context) NamedPattern(propDecl, /*implicit*/ true); propPat->setType(propertyContextType); - propPat = TypedPattern::createImplicit(C, propPat, propertyContextType); + propPat = TypedPattern::createImplicit(Context, propPat, propertyContextType); propPat->setType(propertyContextType); auto *pbDecl = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, parentDC); + Context, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, + parentDC); return {propDecl, pbDecl}; } @@ -359,11 +360,9 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( if (!allowCrossfileExtensions && Nominal->getModuleScopeContext() != getConformanceContext()->getModuleScopeContext()) { - TC.diagnose(ConformanceDecl->getLoc(), - diag::cannot_synthesize_in_crossfile_extension, - getProtocolType()); - TC.diagnose(Nominal->getLoc(), diag::kind_declared_here, - DescriptiveDeclKind::Type); + ConformanceDecl->diagnose(diag::cannot_synthesize_in_crossfile_extension, + getProtocolType()); + Nominal->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); return true; } @@ -372,9 +371,9 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( if (auto CD = dyn_cast(Nominal)) { if (!CD->isFinal() && isa(synthesizing) && isa(ConformanceDecl)) { - TC.diagnose(ConformanceDecl->getLoc(), - diag::cannot_synthesize_init_in_extension_of_nonfinal, - getProtocolType(), synthesizing->getFullName()); + ConformanceDecl->diagnose( + diag::cannot_synthesize_init_in_extension_of_nonfinal, + getProtocolType(), synthesizing->getFullName()); return true; } } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 2ec98f23de6da..6a7b3b887dd98 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -33,12 +33,12 @@ class VarDecl; class DerivedConformance { public: - TypeChecker &TC; + ASTContext &Context; Decl *ConformanceDecl; NominalTypeDecl *Nominal; ProtocolDecl *Protocol; - DerivedConformance(TypeChecker &tc, Decl *conformanceDecl, + DerivedConformance(ASTContext &ctx, Decl *conformanceDecl, NominalTypeDecl *nominal, ProtocolDecl *protocol); /// Retrieve the context in which the conformance is declared (either the @@ -109,6 +109,12 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. Type deriveCaseIterable(AssociatedTypeDecl *assocType); + /// Determine if a RawRepresentable requirement can be derived for a type. + /// + /// This is implemented for non-empty enums without associated values, + /// that declare a raw type in the inheritance clause. + static bool canDeriveRawRepresentable(DeclContext *DC, NominalTypeDecl *type); + /// Derive a RawRepresentable requirement for an enum, if it has a valid /// raw type and raw values for all of its cases. /// diff --git a/lib/Sema/IDETypeCheckingRequests.cpp b/lib/Sema/IDETypeCheckingRequests.cpp index 5a1175a38b3bc..4a9ea2df82bb3 100644 --- a/lib/Sema/IDETypeCheckingRequests.cpp +++ b/lib/Sema/IDETypeCheckingRequests.cpp @@ -56,7 +56,7 @@ static bool isExtensionAppliedInternal(const DeclContext *DC, Type BaseTy, if (!ED->isConstrainedExtension()) return true; - (void)TypeChecker::createForContext(DC->getASTContext()); + (void)swift::createTypeChecker(DC->getASTContext()); GenericSignature genericSig = ED->getGenericSignature(); SubstitutionMap substMap = BaseTy->getContextSubstitutionMap( DC->getParentModule(), ED->getExtendedNominal()); @@ -107,16 +107,16 @@ TypeRelationCheckRequest::evaluate(Evaluator &evaluator, break; } assert(CKind.hasValue()); - return canSatisfy(Owner.Pair.FirstTy, Owner.Pair.SecondTy, Owner.OpenArchetypes, - *CKind, Owner.DC); + return TypeChecker::typesSatisfyConstraint(Owner.Pair.FirstTy, + Owner.Pair.SecondTy, + Owner.OpenArchetypes, + *CKind, Owner.DC); } llvm::Expected RootAndResultTypeOfKeypathDynamicMemberRequest::evaluate(Evaluator &evaluator, SubscriptDecl *subscript) const { - auto &TC = TypeChecker::createForContext(subscript->getASTContext()); - - if (!isValidKeyPathDynamicMemberLookup(subscript, TC)) + if (!isValidKeyPathDynamicMemberLookup(subscript)) return TypePair(); const auto *param = subscript->getIndices()->get(0); @@ -128,10 +128,3 @@ RootAndResultTypeOfKeypathDynamicMemberRequest::evaluate(Evaluator &evaluator, "invalid keypath dynamic member"); return TypePair(genericArgs[0], genericArgs[1]); } - -llvm::Expected -HasDynamicMemberLookupAttributeRequest::evaluate(Evaluator &evaluator, - TypeBase *ty) const { - llvm::DenseMap DynamicMemberLookupCache; - return hasDynamicMemberLookupAttribute(Type(ty), DynamicMemberLookupCache); -} diff --git a/lib/Sema/InstrumenterSupport.cpp b/lib/Sema/InstrumenterSupport.cpp index cf09f836343ba..9cac1aeb20bd7 100644 --- a/lib/Sema/InstrumenterSupport.cpp +++ b/lib/Sema/InstrumenterSupport.cpp @@ -36,17 +36,13 @@ class ErrorGatherer : public DiagnosticConsumer { diags.addConsumer(*this); } ~ErrorGatherer() override { diags.takeConsumers(); } - void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { - if (Kind == swift::DiagnosticKind::Error) { + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { + if (Info.Kind == swift::DiagnosticKind::Error) { error = true; } - DiagnosticEngine::formatDiagnosticText(llvm::errs(), FormatString, - FormatArgs); + DiagnosticEngine::formatDiagnosticText(llvm::errs(), Info.FormatString, + Info.FormatArgs); llvm::errs() << "\n"; } bool hadError() { return error; } @@ -94,7 +90,8 @@ InstrumenterBase::InstrumenterBase(ASTContext &C, DeclContext *DC) TypeCheckDC->getParentModule()->lookupValue( moduleIdentifier, NLKind::UnqualifiedLookup, results); - ModuleIdentifier = (results.size() == 1) ? moduleIdentifier : Identifier(); + if (results.size() == 1) + ModuleIdentifier = results.front()->createNameRef(); // Setup File identifier StringRef filePath = TypeCheckDC->getParentSourceFile()->getFilename(); @@ -110,7 +107,8 @@ InstrumenterBase::InstrumenterBase(ASTContext &C, DeclContext *DC) TypeCheckDC->getParentModule()->lookupValue( fileIdentifier, NLKind::UnqualifiedLookup, results); - FileIdentifier = (results.size() == 1) ? fileIdentifier : Identifier(); + if (results.size() == 1) + FileIdentifier = results.front()->createNameRef(); } void InstrumenterBase::anchor() {} @@ -120,9 +118,7 @@ bool InstrumenterBase::doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, DiagnosticSuppression suppression(Ctx.Diags); ErrorGatherer errorGatherer(Ctx.Diags); - TypeChecker &TC = TypeChecker::createForContext(Ctx); - - TC.typeCheckExpression(parsedExpr, DC); + TypeChecker::typeCheckExpression(parsedExpr, DC); if (parsedExpr) { ErrorFinder errorFinder; @@ -134,3 +130,13 @@ bool InstrumenterBase::doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, return false; } + +Expr *InstrumenterBase::buildIDArgumentExpr(Optional name, + SourceRange SR) { + if (!name) + return IntegerLiteralExpr::createFromUnsigned(Context, 0); + + return new (Context) UnresolvedDeclRefExpr(*name, DeclRefKind::Ordinary, + DeclNameLoc(SR.End)); +} + diff --git a/lib/Sema/InstrumenterSupport.h b/lib/Sema/InstrumenterSupport.h index a972632d73261..1197a950eefc5 100644 --- a/lib/Sema/InstrumenterSupport.h +++ b/lib/Sema/InstrumenterSupport.h @@ -28,7 +28,8 @@ template class Added { public: Added() {} - Added(E NewContents) { Contents = NewContents; } + Added(E NewContents) : Contents(NewContents) {} + Added(const Added &rhs) : Contents(rhs.Contents) {} const Added &operator=(const Added &rhs) { Contents = rhs.Contents; return *this; @@ -42,8 +43,8 @@ class InstrumenterBase { protected: ASTContext &Context; DeclContext *TypeCheckDC; - Identifier ModuleIdentifier; - Identifier FileIdentifier; + Optional ModuleIdentifier; + Optional FileIdentifier; InstrumenterBase(ASTContext &C, DeclContext *DC); virtual ~InstrumenterBase() = default; @@ -51,6 +52,10 @@ class InstrumenterBase { virtual BraceStmt *transformBraceStmt(BraceStmt *BS, bool TopLevel = false) = 0; + /// Create an expression which retrieves a valid ModuleIdentifier or + /// FileIdentifier, if available. + Expr *buildIDArgumentExpr(Optional name, SourceRange SR); + class ClosureFinder : public ASTWalker { private: InstrumenterBase &I; diff --git a/lib/Sema/LookupVisibleDecls.cpp b/lib/Sema/LookupVisibleDecls.cpp index a56570fe61631..3e7cd1f9a7eca 100644 --- a/lib/Sema/LookupVisibleDecls.cpp +++ b/lib/Sema/LookupVisibleDecls.cpp @@ -15,6 +15,7 @@ // //===----------------------------------------------------------------------===// +#include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericSignatureBuilder.h" @@ -24,6 +25,7 @@ #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/PropertyWrappers.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/STLExtras.h" @@ -203,6 +205,9 @@ static void collectVisibleMemberDecls(const DeclContext *CurrDC, LookupState LS, } } +static void +synthesizePropertyWrapperStorageWrapperProperties(IterableDeclContext *IDC); + /// Lookup members in extensions of \p LookupType, using \p BaseType as the /// underlying type when checking any constraints on the extensions. static void doGlobalExtensionLookup(Type BaseType, @@ -220,6 +225,8 @@ static void doGlobalExtensionLookup(Type BaseType, extension)), false)) continue; + synthesizePropertyWrapperStorageWrapperProperties(extension); + collectVisibleMemberDecls(CurrDC, LS, BaseType, extension, FoundDecls); } @@ -282,9 +289,6 @@ static void doDynamicLookup(VisibleDeclConsumer &Consumer, if (D->isRecursiveValidation()) return; - // FIXME: This is used to compute isInvalid() below. - (void) D->getInterfaceType(); - switch (D->getKind()) { #define DECL(ID, SUPER) \ case DeclKind::ID: @@ -430,22 +434,16 @@ static void lookupDeclsFromProtocolsBeingConformedTo( continue; } if (auto *VD = dyn_cast(Member)) { - // FIXME(InterfaceTypeRequest): Remove this. - (void)VD->getInterfaceType(); - if (auto *TypeResolver = VD->getASTContext().getLazyResolver()) { - if (!NormalConformance->hasWitness(VD) && - (Conformance->getDeclContext()->getParentSourceFile() != - FromContext->getParentSourceFile())) - TypeResolver->resolveWitness(NormalConformance, VD); - } - // Skip value requirements that have corresponding witnesses. This cuts - // down on duplicates. - if (!NormalConformance->hasWitness(VD) || - !NormalConformance->getWitness(VD) || - NormalConformance->getWitness(VD).getDecl()->getFullName() - != VD->getFullName()) { - Consumer.foundDecl(VD, ReasonForThisProtocol); - } + if (!VD->isProtocolRequirement()) + continue; + + // Skip value requirements that have corresponding witnesses. This + // cuts down on duplicates. + auto witness = NormalConformance->getWitness(VD); + if (witness && witness.getDecl()->getFullName() == VD->getFullName()) + continue; + + Consumer.foundDecl(VD, ReasonForThisProtocol); } } } @@ -483,6 +481,49 @@ static void lookupTypeMembers(BaseTy, PT, Consumer, CurrDC, LS, Reason); } +// Generate '$' and '_' prefixed variables that have attached property +// wrappers. +static void +synthesizePropertyWrapperStorageWrapperProperties(IterableDeclContext *IDC) { + auto SF = IDC->getDecl()->getDeclContext()->getParentSourceFile(); + if (!SF || SF->Kind == SourceFileKind::Interface) + return; + + for (auto Member : IDC->getMembers()) + if (auto var = dyn_cast(Member)) + if (var->hasAttachedPropertyWrapper()) + (void)var->getPropertyWrapperBackingPropertyInfo(); +} + +/// Trigger synthesizing implicit member declarations to make them "visible". +static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD, + const DeclContext *DC) { + // Synthesize the memberwise initializer for structs or default initializer + // for classes. + if (!NTD->getASTContext().evaluator.hasActiveRequest( + SynthesizeMemberwiseInitRequest{NTD})) + TypeChecker::addImplicitConstructors(NTD); + + // Check all conformances to trigger the synthesized decl generation. + // e.g. init(rawValue:) for RawRepresentable. + for (auto Conformance : NTD->getAllConformances()) { + auto Proto = Conformance->getProtocol(); + if (!Proto->isAccessibleFrom(DC)) + continue; + auto NormalConformance = dyn_cast( + Conformance->getRootConformance()); + if (!NormalConformance) + continue; + NormalConformance->forEachTypeWitness( + [](AssociatedTypeDecl *, Type, TypeDecl *) { return false; }, + /*useResolver=*/true); + NormalConformance->forEachValueWitness([](ValueDecl *, Witness) {}, + /*useResolver=*/true); + } + + synthesizePropertyWrapperStorageWrapperProperties(NTD); +} + static void lookupVisibleMemberDeclsImpl( Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC, LookupState LS, DeclVisibilityKind Reason, GenericSignatureBuilder *GSB, @@ -497,6 +538,8 @@ static void lookupVisibleMemberDeclsImpl( // The metatype represents an arbitrary named type: dig through to the // declared type to see what we're dealing with. Type Ty = MTT->getInstanceType(); + if (auto dynSelfTy = Ty->getAs()) + Ty = dynSelfTy->getSelfType(); if (Ty->is()) return; @@ -592,6 +635,8 @@ static void lookupVisibleMemberDeclsImpl( if (!CurNominal) break; + synthesizeMemberDeclsForLookup(CurNominal, CurrDC); + // Look in for members of a nominal type. lookupTypeMembers(BaseTy, BaseTy, Consumer, CurrDC, LS, Reason); lookupDeclsFromProtocolsBeingConformedTo(BaseTy, Consumer, LS, CurrDC, @@ -697,8 +742,10 @@ static Type getBaseTypeForMember(ModuleDecl *M, ValueDecl *OtherVD, Type BaseTy) if (auto *Proto = OtherVD->getDeclContext()->getSelfProtocolDecl()) { if (BaseTy->getClassOrBoundGenericClass()) { if (auto Conformance = M->lookupConformance(BaseTy, Proto)) { - auto *Superclass = Conformance->getConcrete()->getRootConformance() - ->getType()->getClassOrBoundGenericClass(); + auto *Superclass = Conformance.getConcrete() + ->getRootConformance() + ->getType() + ->getClassOrBoundGenericClass(); return BaseTy->getSuperclassForDecl(Superclass); } } @@ -763,9 +810,6 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer { if (VD->isRecursiveValidation()) continue; - // FIXME: This is used to compute isInvalid() below. - (void) VD->getInterfaceType(); - auto &PossiblyConflicting = DeclsByName[VD->getBaseName()]; if (VD->isInvalid()) { @@ -809,9 +853,6 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer { if (OtherVD->isRecursiveValidation()) continue; - // FIXME: This is used to compute isInvalid() below. - (void) OtherVD->getInterfaceType(); - if (OtherVD->isInvalid()) continue; @@ -947,15 +988,14 @@ static void lookupVisibleDynamicMemberLookupDecls( if (!seenDynamicLookup.insert(baseType.getPointer()).second) return; - if (!evaluateOrDefault(dc->getASTContext().evaluator, - HasDynamicMemberLookupAttributeRequest{baseType.getPointer()}, false)) + if (!baseType->hasDynamicMemberLookupAttribute()) return; auto &ctx = dc->getASTContext(); // Lookup the `subscript(dynamicMember:)` methods in this type. - auto subscriptName = - DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember); + DeclNameRef subscriptName( + { ctx, DeclBaseName::createSubscript(), { ctx.Id_dynamicMember} }); SmallVector subscripts; dc->lookupQualified(baseType, subscriptName, NL_QualifiedDefault, @@ -1050,8 +1090,8 @@ static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer, // FIXME: when we can parse and typecheck the function body partially for // code completion, AFD->getBody() check can be removed. if (Loc.isValid() && - AFD->getSourceRange().isValid() && - SM.rangeContainsTokenLoc(AFD->getSourceRange(), Loc) && + AFD->getBodySourceRange().isValid() && + SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) && AFD->getBody()) { namelookup::FindLocalVal(SM, Loc, Consumer).visit(AFD->getBody()); } @@ -1169,11 +1209,11 @@ void swift::lookupVisibleDecls(VisibleDeclConsumer &Consumer, bool isUsableValue(ValueDecl *VD, DeclVisibilityKind Reason) { // Check "use within its own initial value" case. - if (auto *varD = dyn_cast(VD)) - if (auto *PBD = varD->getParentPatternBinding()) - if (!PBD->isImplicit() && - SM.rangeContainsTokenLoc(PBD->getSourceRange(), Loc)) + if (auto *varD = dyn_cast(VD)) { + if (auto *initExpr = varD->getParentInitializer()) + if (SM.rangeContainsTokenLoc(initExpr->getSourceRange(), Loc)) return false; + } switch (Reason) { case DeclVisibilityKind::LocalVariable: diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index df2da7149f2e2..de34690bbaf24 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -19,6 +19,7 @@ #include "TypeCheckAvailability.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/Pattern.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" @@ -61,8 +62,7 @@ static Expr *isImplicitPromotionToOptional(Expr *E) { /// - Error about collection literals that default to Any collections in /// invalid positions. /// -static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, - const DeclContext *DC, +static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, bool isExprStmt) { class DiagnoseWalker : public ASTWalker { SmallPtrSet AlreadyDiagnosedMetatypes; @@ -77,11 +77,11 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, bool IsExprStmt; public: - TypeChecker &TC; + ASTContext &Ctx; const DeclContext *DC; - DiagnoseWalker(TypeChecker &TC, const DeclContext *DC, bool isExprStmt) - : IsExprStmt(isExprStmt), TC(TC), DC(DC) {} + DiagnoseWalker(const DeclContext *DC, bool isExprStmt) + : IsExprStmt(isExprStmt), Ctx(DC->getASTContext()), DC(DC) {} // Not interested in going outside a basic expression. std::pair walkToStmtPre(Stmt *S) override { @@ -194,28 +194,30 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, callee = dynamicMRE->getMember(); } - visitArguments(Call, [&](unsigned argIndex, Expr *arg) { - // InOutExprs can be wrapped in some implicit casts. - Expr *unwrapped = arg; - if (auto *IIO = dyn_cast(arg)) - unwrapped = IIO->getSubExpr(); - - if (isa(unwrapped) || - isa(unwrapped) || - isa(unwrapped)) { - auto operand = - cast(unwrapped)->getSubExpr(); - if (auto *IOE = dyn_cast(operand)) - operand = IOE->getSubExpr(); - - // Also do some additional work based on how the function uses - // the argument. - if (callee) { + if (callee) { + visitArguments(Call, [&](unsigned argIndex, Expr *arg) { + checkMagicIdentifierMismatch(callee, uncurryLevel, argIndex, arg); + + // InOutExprs can be wrapped in some implicit casts. + Expr *unwrapped = arg; + if (auto *IIO = dyn_cast(arg)) + unwrapped = IIO->getSubExpr(); + + if (isa(unwrapped) || + isa(unwrapped) || + isa(unwrapped)) { + auto operand = + cast(unwrapped)->getSubExpr(); + if (auto *IOE = dyn_cast(operand)) + operand = IOE->getSubExpr(); + + // Also do some additional work based on how the function uses + // the argument. checkConvertedPointerArgument(callee, uncurryLevel, argIndex, unwrapped, operand); } - } - }); + }); + } } // If we have an assignment expression, scout ahead for acceptable _'s. @@ -226,7 +228,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, if (auto *DAE = dyn_cast(E)) { if (!CorrectDiscardAssignmentExprs.count(DAE) && !DAE->getType()->hasError()) - TC.diagnose(DAE->getLoc(), diag::discard_expr_outside_of_assignment); + Ctx.Diags.diagnose(DAE->getLoc(), + diag::discard_expr_outside_of_assignment); } // Diagnose 'self.init' or 'super.init' nested in another expression @@ -235,8 +238,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, if (!Parent.isNull() || !IsExprStmt || DC->getParent()->isLocalContext()) { bool isChainToSuper; (void)rebindSelfExpr->getCalledConstructor(isChainToSuper); - TC.diagnose(E->getLoc(), diag::init_delegation_nested, - isChainToSuper, !IsExprStmt); + Ctx.Diags.diagnose(E->getLoc(), diag::init_delegation_nested, + isChainToSuper, !IsExprStmt); } } @@ -244,8 +247,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, if (auto *tupleExpr = dyn_cast(E)) { if (!CallArgs.count(tupleExpr)) { if (tupleExpr->getNumElements() == 1) { - TC.diagnose(tupleExpr->getElementNameLoc(0), - diag::tuple_single_element) + Ctx.Diags.diagnose(tupleExpr->getElementNameLoc(0), + diag::tuple_single_element) .fixItRemoveChars(tupleExpr->getElementNameLoc(0), tupleExpr->getElement(0)->getStartLoc()); } @@ -283,7 +286,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, } if (diagnose) { - TC.diagnose(tupleExpr->getLoc(), diag::tuple_duplicate_label); + Ctx.Diags.diagnose(tupleExpr->getLoc(), + diag::tuple_duplicate_label); } } } @@ -387,13 +391,13 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // produce an error with a fixit to add the type as an explicit // annotation. if (c->getNumElements() == 0) - TC.diagnose(c->getLoc(), diag::collection_literal_empty) + Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_empty) .highlight(c->getSourceRange()); else { assert(c->getType()->hasTypeRepr() && "a defaulted type should always be printable"); - TC.diagnose(c->getLoc(), diag::collection_literal_heterogeneous, - c->getType()) + Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_heterogeneous, + c->getType()) .highlight(c->getSourceRange()) .fixItInsertAfter(c->getEndLoc(), " as " + c->getType()->getString()); } @@ -419,6 +423,91 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Otherwise, we can't support this. } + void checkMagicIdentifierMismatch(ConcreteDeclRef callee, + unsigned uncurryLevel, + unsigned argIndex, + Expr *arg) { + // We only care about args in the arg list. + if (uncurryLevel != (callee.getDecl()->hasCurriedSelf() ? 1 : 0)) + return; + + // Get underlying params for both callee and caller, if declared. + auto *calleeParam = getParameterAt(callee.getDecl(), argIndex); + auto *callerParam = dyn_cast_or_null( + arg->getReferencedDecl(/*stopAtParenExpr=*/true).getDecl() + ); + + // (Otherwise, we don't need to do anything.) + if (!calleeParam || !callerParam) + return; + + auto calleeDefaultArg = getMagicIdentifierDefaultArgKind(calleeParam); + auto callerDefaultArg = getMagicIdentifierDefaultArgKind(callerParam); + + // If one of the parameters doesn't have a default arg, or they both have + // the same one, everything's fine. + if (!calleeDefaultArg || !callerDefaultArg || + *calleeDefaultArg == *callerDefaultArg) + return; + + StringRef calleeDefaultArgString = + MagicIdentifierLiteralExpr::getKindString(*calleeDefaultArg); + StringRef callerDefaultArgString = + MagicIdentifierLiteralExpr::getKindString(*callerDefaultArg); + + // Emit main warning + Ctx.Diags.diagnose(arg->getLoc(), diag::default_magic_identifier_mismatch, + callerParam->getName(), callerDefaultArgString, + calleeParam->getName(), calleeDefaultArgString); + + // Add "change caller default arg" fixit + SourceLoc callerDefaultArgLoc = + callerParam->getStructuralDefaultExpr()->getLoc(); + Ctx.Diags.diagnose(callerDefaultArgLoc, + diag::change_caller_default_to_match_callee, + callerParam->getName(), calleeDefaultArgString) + .fixItReplace(callerDefaultArgLoc, calleeDefaultArgString); + + // Add "silence with parens" fixit + Ctx.Diags.diagnose(arg->getLoc(), + diag::silence_default_magic_identifier_mismatch) + .fixItInsert(arg->getStartLoc(), "(") + .fixItInsertAfter(arg->getEndLoc(), ")"); + + // Point to callee parameter + Ctx.Diags.diagnose(calleeParam, diag::decl_declared_here, + calleeParam->getFullName()); + } + + Optional + getMagicIdentifierDefaultArgKind(const ParamDecl *param) { + switch (param->getDefaultArgumentKind()) { + case DefaultArgumentKind::Column: + return MagicIdentifierLiteralExpr::Kind::Column; + case DefaultArgumentKind::DSOHandle: + return MagicIdentifierLiteralExpr::Kind::DSOHandle; + case DefaultArgumentKind::File: + return MagicIdentifierLiteralExpr::Kind::File; + case DefaultArgumentKind::FilePath: + return MagicIdentifierLiteralExpr::Kind::FilePath; + case DefaultArgumentKind::Function: + return MagicIdentifierLiteralExpr::Kind::Function; + case DefaultArgumentKind::Line: + return MagicIdentifierLiteralExpr::Kind::Line; + + case DefaultArgumentKind::None: + case DefaultArgumentKind::Normal: + case DefaultArgumentKind::Inherited: + case DefaultArgumentKind::NilLiteral: + case DefaultArgumentKind::EmptyArray: + case DefaultArgumentKind::EmptyDictionary: + case DefaultArgumentKind::StoredProperty: + return None; + } + + llvm_unreachable("Unhandled DefaultArgumentKind in " + "getMagicIdentifierDefaultArgKind"); + } void checkUseOfModule(DeclRefExpr *E) { // Allow module values as a part of: @@ -430,7 +519,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, return; } - TC.diagnose(E->getStartLoc(), diag::value_of_module_type); + Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_module_type); } // Diagnose metatype values that don't appear as part of a property, @@ -463,7 +552,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Is this a protocol metatype? - TC.diagnose(E->getStartLoc(), diag::value_of_metatype_type); + Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_metatype_type); // Add fix-it to insert '()', only if this is a metatype of // non-existential type and has any initializers. @@ -475,13 +564,13 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, instanceTy->mayHaveMembers() && !TypeChecker::lookupConstructors(const_cast(DC), instanceTy).empty()) { - TC.diagnose(E->getEndLoc(), diag::add_parens_to_type) + Ctx.Diags.diagnose(E->getEndLoc(), diag::add_parens_to_type) .fixItInsertAfter(E->getEndLoc(), "()"); } } // Add fix-it to insert ".self". - auto diag = TC.diagnose(E->getEndLoc(), diag::add_self_to_type); + auto diag = Ctx.Diags.diagnose(E->getEndLoc(), diag::add_self_to_type); if (E->canAppendPostfixExpression()) { diag.fixItInsertAfter(E->getEndLoc(), ".self"); } else { @@ -517,24 +606,28 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, declParent = VD->getDeclContext()->getParentModule(); } - TC.diagnose(DRE->getLoc(), diag::warn_unqualified_access, - VD->getBaseName().getIdentifier(), VD->getDescriptiveKind(), - declParent->getDescriptiveKind(), declParent->getFullName()); - TC.diagnose(VD, diag::decl_declared_here, VD->getFullName()); + Ctx.Diags.diagnose(DRE->getLoc(), diag::warn_unqualified_access, + VD->getBaseName().getIdentifier(), + VD->getDescriptiveKind(), + declParent->getDescriptiveKind(), + declParent->getFullName()); + Ctx.Diags.diagnose(VD, diag::decl_declared_here, VD->getFullName()); if (VD->getDeclContext()->isTypeContext()) { - TC.diagnose(DRE->getLoc(), diag::fix_unqualified_access_member) + Ctx.Diags.diagnose(DRE->getLoc(), diag::fix_unqualified_access_member) .fixItInsert(DRE->getStartLoc(), "self."); } DeclContext *topLevelContext = DC->getModuleScopeContext(); - UnqualifiedLookup lookup(VD->getBaseName(), topLevelContext, - /*Loc=*/SourceLoc(), - UnqualifiedLookup::Flags::KnownPrivate); + auto descriptor = UnqualifiedLookupDescriptor( + DeclNameRef(VD->getBaseName()), topLevelContext, SourceLoc(), + UnqualifiedLookupFlags::KnownPrivate); + auto lookup = evaluateOrDefault(Ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); // Group results by module. Pick an arbitrary result from each module. llvm::SmallDenseMap resultsByModule; - for (auto &result : lookup.Results) { + for (auto &result : lookup) { const ValueDecl *value = result.getValueDecl(); resultsByModule.insert(std::make_pair(value->getModuleContext(),value)); } @@ -560,8 +653,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, SmallString<32> namePlusDot = pair.first->getName().str(); namePlusDot.push_back('.'); - TC.diagnose(DRE->getLoc(), topLevelDiag, - namePlusDot, k, pair.first->getName()) + Ctx.Diags.diagnose(DRE->getLoc(), topLevelDiag, + namePlusDot, k, pair.first->getName()) .fixItInsert(DRE->getStartLoc(), namePlusDot); } } @@ -570,10 +663,10 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Referencing type(of:) and other decls with special type-checking // behavior as functions is not implemented. Maybe we could wrap up the // special-case behavior in a closure someday... - if (TC.getDeclTypeCheckingSemantics(DRE->getDecl()) + if (TypeChecker::getDeclTypeCheckingSemantics(DRE->getDecl()) != DeclTypeCheckingSemantics::Normal) { - TC.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref, - DRE->getDecl()->getBaseName().getIdentifier()); + Ctx.Diags.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref, + DRE->getDecl()->getBaseName().getIdentifier()); } } @@ -595,7 +688,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, BitcastableNumberKind getBitcastableNumberKind(Type t) const { auto decl = t->getNominalOrBoundGenericNominal(); #define MATCH_DECL(type) \ - if (decl == TC.Context.get##type##Decl()) \ + if (decl == Ctx.get##type##Decl()) \ return BNK_##type; MATCH_DECL(Int8) MATCH_DECL(Int16) @@ -621,7 +714,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, void checkForSuspiciousBitCasts(DeclRefExpr *DRE, Expr *Parent = nullptr) { - if (DRE->getDecl() != TC.Context.getUnsafeBitCast()) + if (DRE->getDecl() != Ctx.getUnsafeBitCast()) return; if (DRE->getDeclRef().getSubstitutions().empty()) @@ -647,15 +740,14 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, subExpr = args->getElement(0); // Determine the fixit range from the start of the application to // the first argument, `unsafeBitCast(` - removeBeforeRange = CharSourceRange(TC.Context.SourceMgr, - DRE->getLoc(), + removeBeforeRange = CharSourceRange(Ctx.SourceMgr, DRE->getLoc(), subExpr->getStartLoc()); // Determine the fixit range from the end of the first argument to // the end of the application, `, to: T.self)` - removeAfterRange = CharSourceRange(TC.Context.SourceMgr, - Lexer::getLocForEndOfToken(TC.Context.SourceMgr, + removeAfterRange = CharSourceRange(Ctx.SourceMgr, + Lexer::getLocForEndOfToken(Ctx.SourceMgr, subExpr->getEndLoc()), - Lexer::getLocForEndOfToken(TC.Context.SourceMgr, + Lexer::getLocForEndOfToken(Ctx.SourceMgr, apply->getEndLoc())); } } @@ -663,8 +755,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Casting to the same type or a superclass is a no-op. if (toTy->isEqual(fromTy) || toTy->isExactSuperclassOf(fromTy)) { - auto d = TC.diagnose(DRE->getLoc(), diag::bitcasting_is_no_op, - fromTy, toTy); + auto d = Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_is_no_op, + fromTy, toTy); if (subExpr) { d.fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) @@ -679,14 +771,15 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Casting a nonescaping function to escaping is UB. // `withoutActuallyEscaping` ought to be used instead. if (fromFnTy->isNoEscape() && !toFnTy->isNoEscape()) { - TC.diagnose(DRE->getLoc(), diag::bitcasting_away_noescape, - fromTy, toTy); + Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_away_noescape, + fromTy, toTy); } // Changing function representation (say, to try to force a // @convention(c) function pointer to exist) is also unlikely to work. if (fromFnTy->getRepresentation() != toFnTy->getRepresentation()) { - TC.diagnose(DRE->getLoc(), diag::bitcasting_to_change_function_rep, - fromTy, toTy); + Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcasting_to_change_function_rep, fromTy, + toTy); } return; } @@ -694,8 +787,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Unchecked casting to a subclass is better done by unsafeDowncast. if (fromTy->isBindableToSuperclassOf(toTy)) { - TC.diagnose(DRE->getLoc(), diag::bitcasting_to_downcast, - fromTy, toTy) + Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_downcast, + fromTy, toTy) .fixItReplace(DRE->getNameLoc().getBaseNameLoc(), "unsafeDowncast"); return; @@ -711,10 +804,10 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // normal initializers on the destination type. if (toPointee->isEqual(fromPointee) || isRawPointerKind(toPTK)) { - auto d = TC.diagnose(DRE->getLoc(), - diag::bitcasting_to_change_pointer_kind, - fromTy, toTy, - toTy->getStructOrBoundGenericStruct()->getName()); + auto d = Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcasting_to_change_pointer_kind, + fromTy, toTy, + toTy->getStructOrBoundGenericStruct()->getName()); if (subExpr) { StringRef before, after; switch (toPTK) { @@ -756,9 +849,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // Casting to a different typed pointer type should use // withMemoryRebound. if (!isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)) { - TC.diagnose(DRE->getLoc(), - diag::bitcasting_to_change_pointee_type, - fromTy, toTy); + Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcasting_to_change_pointee_type, + fromTy, toTy); return; } @@ -766,9 +859,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // (or assume it's already bound). assert(isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK) && "unhandled cast combo?!"); - TC.diagnose(DRE->getLoc(), - diag::bitcasting_to_give_type_to_raw_pointer, - fromTy, toTy); + Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcasting_to_give_type_to_raw_pointer, + fromTy, toTy); if (subExpr) { SmallString<64> fixitBuf; { @@ -777,9 +870,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, toPointee->print(os); os << ".self)"; } - TC.diagnose(DRE->getLoc(), - diag::bitcast_assume_memory_rebound, - toPointee) + Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcast_assume_memory_rebound, + toPointee) .fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) .fixItReplaceChars(removeAfterRange.getStart(), @@ -792,9 +885,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, toPointee->print(os); os << ".self, capacity: <""#capacity#"">)"; } - TC.diagnose(DRE->getLoc(), - diag::bitcast_bind_memory, - toPointee) + Ctx.Diags.diagnose(DRE->getLoc(), + diag::bitcast_bind_memory, + toPointee) .fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) .fixItReplaceChars(removeAfterRange.getStart(), @@ -1071,7 +1164,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, } if (diagID) { - auto d = TC.diagnose(DRE->getLoc(), *diagID, fromTy, toTy); + auto d = Ctx.Diags.diagnose(DRE->getLoc(), *diagID, fromTy, toTy); if (subExpr) { d.fixItReplaceChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd(), @@ -1104,7 +1197,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, // First case -- Optional.none if (auto DRE = dyn_cast(CE->getSemanticFn())) - return DRE->getDecl() == TC.Context.getOptionalNoneDecl(); + return DRE->getDecl() == Ctx.getOptionalNoneDecl(); return false; } @@ -1139,8 +1232,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, Expr *subExpr = nullptr; if (calleeName == "??" && (subExpr = isImplicitPromotionToOptional(lhs))) { - TC.diagnose(DRE->getLoc(), diag::use_of_qq_on_non_optional_value, - subExpr->getType()) + Ctx.Diags.diagnose(DRE->getLoc(), diag::use_of_qq_on_non_optional_value, + subExpr->getType()) .highlight(lhs->getSourceRange()) .fixItRemove(SourceRange(DRE->getLoc(), rhs->getEndLoc())); return; @@ -1154,8 +1247,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, (subExpr = isImplicitPromotionToOptional(rhs)))) { bool isTrue = calleeName == "!=" || calleeName == "!=="; - TC.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_nil, - subExpr->getType(), isTrue) + Ctx.Diags.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_nil, + subExpr->getType(), isTrue) .highlight(lhs->getSourceRange()) .highlight(rhs->getSourceRange()); return; @@ -1164,7 +1257,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, } }; - DiagnoseWalker Walker(TC, DC, isExprStmt); + DiagnoseWalker Walker(DC, isExprStmt); const_cast(E)->walk(Walker); // Diagnose uses of collection literals with defaulted types at the top @@ -1180,8 +1273,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E, /// Diagnose recursive use of properties within their own accessors -static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, - const DeclContext *DC) { +static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { auto fn = dyn_cast(DC); if (!fn) return; @@ -1191,14 +1283,13 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, return; class DiagnoseWalker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; VarDecl *Var; const AccessorDecl *Accessor; public: - explicit DiagnoseWalker(TypeChecker &TC, VarDecl *var, - const AccessorDecl *Accessor) - : TC(TC), Var(var), Accessor(Accessor) {} + explicit DiagnoseWalker(VarDecl *var, const AccessorDecl *Accessor) + : Ctx(var->getASTContext()), Var(var), Accessor(Accessor) {} /// Return true if this is an implicit reference to self. static bool isImplicitSelfUse(Expr *E) { @@ -1249,8 +1340,9 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, shouldDiagnose = false; if (shouldDiagnose) { - TC.diagnose(subExpr->getLoc(), diag::recursive_accessor_reference, - Var->getName(), Accessor->isSetter()); + Ctx.Diags.diagnose(subExpr->getLoc(), + diag::recursive_accessor_reference, + Var->getName(), Accessor->isSetter()); } } @@ -1259,7 +1351,8 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, if (isStore && DRE->getAccessSemantics() == AccessSemantics::DirectToStorage && Accessor->getAccessorKind() == AccessorKind::WillSet) { - TC.diagnose(E->getLoc(), diag::store_in_willset, Var->getName()); + Ctx.Diags.diagnose(E->getLoc(), diag::store_in_willset, + Var->getName()); } } @@ -1281,10 +1374,11 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, shouldDiagnose = isStore; if (shouldDiagnose) { - TC.diagnose(subExpr->getLoc(), diag::recursive_accessor_reference, - Var->getName(), Accessor->isSetter()); - TC.diagnose(subExpr->getLoc(), - diag::recursive_accessor_reference_silence) + Ctx.Diags.diagnose(subExpr->getLoc(), + diag::recursive_accessor_reference, + Var->getName(), Accessor->isSetter()); + Ctx.Diags.diagnose(subExpr->getLoc(), + diag::recursive_accessor_reference_silence) .fixItInsert(subExpr->getStartLoc(), "self."); } } @@ -1294,8 +1388,8 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, if (isStore && MRE->getAccessSemantics() == AccessSemantics::DirectToStorage && Accessor->getAccessorKind() == AccessorKind::WillSet) { - TC.diagnose(subExpr->getLoc(), diag::store_in_willset, - Var->getName()); + Ctx.Diags.diagnose(subExpr->getLoc(), diag::store_in_willset, + Var->getName()); } } @@ -1305,25 +1399,31 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E, } }; - DiagnoseWalker walker(TC, var, fn); + DiagnoseWalker walker(var, fn); const_cast(E)->walk(walker); } -/// Look for any property references in closures that lack a "self." qualifier. -/// Within a closure, we require that the source code contain "self." explicitly -/// because 'self' is captured, not the property value. This is a common source -/// of confusion, so we force an explicit self. -static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E, +/// Look for any property references in closures that lack a 'self.' qualifier. +/// Within a closure, we require that the source code contain 'self.' explicitly +/// (or that the closure explicitly capture 'self' in the capture list) because +/// 'self' is captured, not the property value. This is a common source of +/// confusion, so we force an explicit self. +static void diagnoseImplicitSelfUseInClosure(const Expr *E, const DeclContext *DC) { class DiagnoseWalker : public ASTWalker { - TypeChecker &TC; - unsigned InClosure; + ASTContext &Ctx; + SmallVector Closures; public: - explicit DiagnoseWalker(TypeChecker &TC, bool isAlreadyInClosure) - : TC(TC), InClosure(isAlreadyInClosure) {} + explicit DiagnoseWalker(ASTContext &ctx, AbstractClosureExpr *ACE) + : Ctx(ctx), Closures() { + if (ACE) + Closures.push_back(ACE); + } - /// Return true if this is an implicit reference to self. - static bool isImplicitSelfUse(Expr *E) { + /// Return true if this is an implicit reference to self which is required + /// to be explicit in an escaping closure. Metatype references and value + /// type references are excluded. + static bool isImplicitSelfParamUseLikelyToCauseCycle(Expr *E) { auto *DRE = dyn_cast(E); if (!DRE || !DRE->isImplicit() || !isa(DRE->getDecl()) || @@ -1338,11 +1438,19 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E, return false; // Metatype self captures don't extend the lifetime of an object. - return !ty->is(); + if (ty->is()) + return false; + + // If self does not have reference semantics, it is very unlikely that + // capturing it will create a reference cycle. + if (!ty->hasReferenceSemantics()) + return false; + + return true; } - /// Return true if this is a closure expression that will require "self." - /// qualification of member references. + /// Return true if this is a closure expression that will require explicit + /// use or capture of "self." for qualification of member references. static bool isClosureRequiringSelfQualification( const AbstractClosureExpr *CE) { // If the closure's type was inferred to be noescape, then it doesn't @@ -1364,42 +1472,49 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E, // If this is a potentially-escaping closure expression, start looking // for references to self if we aren't already. if (isClosureRequiringSelfQualification(CE)) - ++InClosure; + Closures.push_back(CE); } // If we aren't in a closure, no diagnostics will be produced. - if (!InClosure) + if (Closures.size() == 0) return { true, E }; - // If we see a property reference with an implicit base from within a - // closure, then reject it as requiring an explicit "self." qualifier. We - // do this in explicit closures, not autoclosures, because otherwise the - // transparence of autoclosures is lost. + auto &Diags = Ctx.Diags; + + // Diagnostics should correct the innermost closure + auto *ACE = Closures[Closures.size() - 1]; + assert(ACE); + + SourceLoc memberLoc = SourceLoc(); if (auto *MRE = dyn_cast(E)) - if (isImplicitSelfUse(MRE->getBase())) { - TC.diagnose(MRE->getLoc(), - diag::property_use_in_closure_without_explicit_self, - MRE->getMember().getDecl()->getBaseName().getIdentifier()) - .fixItInsert(MRE->getLoc(), "self."); - return { false, E }; + if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase())) { + auto baseName = MRE->getMember().getDecl()->getBaseName(); + memberLoc = MRE->getLoc(); + Diags.diagnose(memberLoc, + diag::property_use_in_closure_without_explicit_self, + baseName.getIdentifier()); } // Handle method calls with a specific diagnostic + fixit. if (auto *DSCE = dyn_cast(E)) - if (isImplicitSelfUse(DSCE->getBase()) && + if (isImplicitSelfParamUseLikelyToCauseCycle(DSCE->getBase()) && isa(DSCE->getFn())) { auto MethodExpr = cast(DSCE->getFn()); - TC.diagnose(DSCE->getLoc(), - diag::method_call_in_closure_without_explicit_self, - MethodExpr->getDecl()->getBaseName().getIdentifier()) - .fixItInsert(DSCE->getLoc(), "self."); - return { false, E }; + memberLoc = DSCE->getLoc(); + Diags.diagnose(DSCE->getLoc(), + diag::method_call_in_closure_without_explicit_self, + MethodExpr->getDecl()->getBaseName().getIdentifier()); } + if (memberLoc.isValid()) { + emitFixIts(Diags, memberLoc, ACE); + return { false, E }; + } + // Catch any other implicit uses of self with a generic diagnostic. - if (isImplicitSelfUse(E)) - TC.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure); + if (isImplicitSelfParamUseLikelyToCauseCycle(E)) + Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure); return { true, E }; } @@ -1407,25 +1522,144 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E, Expr *walkToExprPost(Expr *E) override { if (auto *CE = dyn_cast(E)) { if (isClosureRequiringSelfQualification(CE)) { - assert(InClosure); - --InClosure; + assert(Closures.size() > 0); + Closures.pop_back(); } } return E; } + + /// Emit any fix-its for this error. + void emitFixIts(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const AbstractClosureExpr *ACE) { + // This error can be fixed by either capturing self explicitly (if in an + // explicit closure), or referencing self explicitly. + if (auto *CE = dyn_cast(ACE)) { + if (diagnoseAlmostMatchingCaptures(Diags, memberLoc, CE)) { + // Bail on the rest of the diagnostics. Offering the option to + // capture 'self' explicitly will result in an error, and using + // 'self.' explicitly will be accessing something other than the + // self param. + // FIXME: We could offer a special fixit in the [weak self] case to insert 'self?.'... + return; + } + emitFixItsForExplicitClosure(Diags, memberLoc, CE); + } else { + // If this wasn't an explicit closure, just offer the fix-it to + // reference self explicitly. + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + } + } + + /// Diagnose any captures which might have been an attempt to capture + /// \c self strongly, but do not actually enable implicit \c self. Returns + /// whether there were any such captures to diagnose. + bool diagnoseAlmostMatchingCaptures(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + // If we've already captured something with the name "self" other than + // the actual self param, offer special diagnostics. + if (auto *VD = closureExpr->getCapturedSelfDecl()) { + // Either this is a weak capture of self... + if (VD->getType()->is()) { + Diags.diagnose(VD->getLoc(), diag::note_self_captured_weakly); + // ...or something completely different. + } else { + Diags.diagnose(VD->getLoc(), diag::note_other_self_capture); + } + + return true; + } + return false; + } + + /// Emit fix-its for invalid use of implicit \c self in an explicit closure. + /// The error can be solved by capturing self explicitly, + /// or by using \c self. explicitly. + void emitFixItsForExplicitClosure(DiagnosticEngine &Diags, + SourceLoc memberLoc, + const ClosureExpr *closureExpr) { + Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) + .fixItInsert(memberLoc, "self."); + auto diag = Diags.diagnose(closureExpr->getLoc(), + diag::note_capture_self_explicitly); + // There are four different potential fix-its to offer based on the + // closure signature: + // 1. There is an existing capture list which already has some + // entries. We need to insert 'self' into the capture list along + // with a separating comma. + // 2. There is an existing capture list, but it is empty (jusr '[]'). + // We can just insert 'self'. + // 3. Arguments or types are already specified in the signature, + // but there is no existing capture list. We will need to insert + // the capture list, but 'in' will already be present. + // 4. The signature empty so far. We must insert the full capture + // list as well as 'in'. + const auto brackets = closureExpr->getBracketRange(); + if (brackets.isValid()) { + emitInsertSelfIntoCaptureListFixIt(brackets, diag); + } + else { + emitInsertNewCaptureListFixIt(closureExpr, diag); + } + } + + /// Emit a fix-it for inserting \c self into in existing capture list, along + /// with a trailing comma if needed. The fix-it will be attached to the + /// provided diagnostic \c diag. + void emitInsertSelfIntoCaptureListFixIt(SourceRange brackets, + InFlightDiagnostic &diag) { + // Look for any non-comment token. If there's anything before the + // closing bracket, we assume that it is a valid capture list entry and + // insert 'self,'. If it wasn't a valid entry, then we will at least not + // be introducing any new errors/warnings... + const auto locAfterBracket = brackets.Start.getAdvancedLoc(1); + const auto nextAfterBracket = + Lexer::getTokenAtLocation(Ctx.SourceMgr, locAfterBracket, + CommentRetentionMode::None); + if (nextAfterBracket.getLoc() != brackets.End) + diag.fixItInsertAfter(brackets.Start, "self, "); + else + diag.fixItInsertAfter(brackets.Start, "self"); + } + + /// Emit a fix-it for inserting a capture list into a closure that does not + /// already have one, along with a trailing \c in if necessary. The fix-it + /// will be attached to the provided diagnostic \c diag. + void emitInsertNewCaptureListFixIt(const ClosureExpr *closureExpr, + InFlightDiagnostic &diag) { + if (closureExpr->getInLoc().isValid()) { + diag.fixItInsertAfter(closureExpr->getLoc(), " [self]"); + return; + } + + // If there's a (non-comment) token immediately following the + // opening brace of the closure, we may need to pad the fix-it + // with a space. + const auto nextLoc = closureExpr->getLoc().getAdvancedLoc(1); + const auto next = + Lexer::getTokenAtLocation(Ctx.SourceMgr, nextLoc, + CommentRetentionMode::None); + std::string trailing = next.getLoc() == nextLoc ? " " : ""; + + diag.fixItInsertAfter(closureExpr->getLoc(), " [self] in" + trailing); + } }; - bool isAlreadyInClosure = false; + AbstractClosureExpr *ACE = nullptr; if (DC->isLocalContext()) { - while (DC->getParent()->isLocalContext() && !isAlreadyInClosure) { + while (DC->getParent()->isLocalContext() && !ACE) { if (auto *closure = dyn_cast(DC)) if (DiagnoseWalker::isClosureRequiringSelfQualification(closure)) - isAlreadyInClosure = true; + ACE = const_cast(closure); DC = DC->getParent(); } } - const_cast(E)->walk(DiagnoseWalker(TC, isAlreadyInClosure)); + auto &ctx = DC->getASTContext(); + const_cast(E)->walk(DiagnoseWalker(ctx, ACE)); } bool TypeChecker::getDefaultGenericArgumentsString( @@ -1690,7 +1924,7 @@ static const Expr *lookThroughExprsToImmediateDeallocation(const Expr *E) { } } -static void diagnoseUnownedImmediateDeallocationImpl(TypeChecker &TC, +static void diagnoseUnownedImmediateDeallocationImpl(ASTContext &ctx, const VarDecl *varDecl, const Expr *initExpr, SourceLoc diagLoc, @@ -1741,17 +1975,18 @@ static void diagnoseUnownedImmediateDeallocationImpl(TypeChecker &TC, if (varDecl->getDeclContext()->isTypeContext()) storageKind = SK_Property; - TC.diagnose(diagLoc, diag::unowned_assignment_immediate_deallocation, - varDecl->getName(), ownershipAttr->get(), unsigned(storageKind)) + ctx.Diags.diagnose(diagLoc, diag::unowned_assignment_immediate_deallocation, + varDecl->getName(), ownershipAttr->get(), + unsigned(storageKind)) .highlight(diagRange); - TC.diagnose(diagLoc, diag::unowned_assignment_requires_strong) + ctx.Diags.diagnose(diagLoc, diag::unowned_assignment_requires_strong) .highlight(diagRange); - TC.diagnose(varDecl, diag::decl_declared_here, varDecl->getFullName()); + ctx.Diags.diagnose(varDecl, diag::decl_declared_here, varDecl->getFullName()); } -void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, +void swift::diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const AssignExpr *assignExpr) { auto *destExpr = assignExpr->getDest()->getValueProvidingExpr(); auto *initExpr = assignExpr->getSrc(); @@ -1765,12 +2000,12 @@ void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, } if (VD) - diagnoseUnownedImmediateDeallocationImpl(TC, VD, initExpr, + diagnoseUnownedImmediateDeallocationImpl(ctx, VD, initExpr, assignExpr->getLoc(), initExpr->getSourceRange()); } -void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, +void swift::diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const Pattern *pattern, SourceLoc equalLoc, const Expr *initExpr) { @@ -1788,20 +2023,26 @@ void swift::diagnoseUnownedImmediateDeallocation(TypeChecker &TC, const Pattern *subPattern = elt.getPattern(); Expr *subInitExpr = TE->getElement(i); - diagnoseUnownedImmediateDeallocation(TC, subPattern, equalLoc, + diagnoseUnownedImmediateDeallocation(ctx, subPattern, equalLoc, subInitExpr); } } } else if (auto *NP = dyn_cast(pattern)) { - diagnoseUnownedImmediateDeallocationImpl(TC, NP->getDecl(), initExpr, + diagnoseUnownedImmediateDeallocationImpl(ctx, NP->getDecl(), initExpr, equalLoc, initExpr->getSourceRange()); } } -bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, - ValueDecl *decl, - const ValueDecl *base) { +namespace { +enum NoteKind_t { + FixItReplace, + FixItInsert, +}; + +static bool fixItOverrideDeclarationTypesImpl( + ValueDecl *decl, const ValueDecl *base, + SmallVectorImpl> ¬es) { // For now, just rewrite cases where the base uses a value type and the // override uses a reference type, and the value type is bridged to the // reference type. This is a way to migrate code that makes use of types @@ -1864,7 +2105,7 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, options.SynthesizeSugarOnTypes = true; newOverrideTy->print(baseTypeStr, options); - diag.fixItReplace(typeRange, baseTypeStr.str()); + notes.emplace_back(FixItReplace, typeRange, baseTypeStr.str().str()); return true; }; @@ -1884,7 +2125,7 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, overrideFnTy->getExtInfo().isNoEscape() && // The overridden function type should be escaping. !baseFnTy->getExtInfo().isNoEscape()) { - diag.fixItInsert(typeRange.Start, "@escaping "); + notes.emplace_back(FixItInsert, typeRange, "@escaping "); return true; } return false; @@ -1922,7 +2163,7 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, for_each(*fn->getParameters(), *baseFn->getParameters(), [&](ParamDecl *param, const ParamDecl *baseParam) { - fixedAny |= fixItOverrideDeclarationTypes(diag, param, baseParam); + fixedAny |= fixItOverrideDeclarationTypesImpl(param, baseParam, notes); }); } if (auto *method = dyn_cast(decl)) { @@ -1948,7 +2189,7 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, for_each(*subscript->getIndices(), *baseSubscript->getIndices(), [&](ParamDecl *param, const ParamDecl *baseParam) { - fixedAny |= fixItOverrideDeclarationTypes(diag, param, baseParam); + fixedAny |= fixItOverrideDeclarationTypesImpl(param, baseParam, notes); }); } @@ -1964,6 +2205,26 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, llvm_unreachable("unknown overridable member"); } +}; + +bool swift::computeFixitsForOverridenDeclaration( + ValueDecl *decl, const ValueDecl *base, + llvm::function_ref(bool)> diag) { + SmallVector, 4> Notes; + bool hasNotes = ::fixItOverrideDeclarationTypesImpl(decl, base, Notes); + + Optional diagnostic = diag(hasNotes); + if (!diagnostic) return hasNotes; + + for (const auto ¬e : Notes) { + if (std::get<0>(note) == FixItReplace) { + diagnostic->fixItReplace(std::get<1>(note), std::get<2>(note)); + } else { + diagnostic->fixItInsert(std::get<1>(note).Start, std::get<2>(note)); + } + } + return hasNotes; +} //===----------------------------------------------------------------------===// // Per func/init diagnostics @@ -2010,7 +2271,8 @@ class VarDeclUsageChecker : public ASTWalker { void operator=(const VarDeclUsageChecker &) = delete; public: - VarDeclUsageChecker(TypeChecker &TC, AbstractFunctionDecl *AFD) : Diags(TC.Diags) { + VarDeclUsageChecker(AbstractFunctionDecl *AFD) + : Diags(AFD->getASTContext().Diags) { // If this AFD is a setter, track the parameter and the getter for // the containing property so if newValue isn't used but the getter is used // an error can be reported. @@ -2027,7 +2289,7 @@ class VarDeclUsageChecker : public ASTWalker { VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {} - VarDeclUsageChecker(TypeChecker &tc, VarDecl *vd) : Diags(tc.Diags) { + VarDeclUsageChecker(VarDecl *vd) : Diags(vd->getASTContext().Diags) { // Track a specific VarDecl VarDecls[vd] = 0; if (auto *childVd = vd->getCorrespondingCaseBodyVariable().getPtrOrNull()) { @@ -2053,8 +2315,8 @@ class VarDeclUsageChecker : public ASTWalker { if (!PBD) return false; bool sawMutation = false; - for (const auto &PBE : PBD->getPatternList()) { - PBE.getPattern()->forEachVariable([&](VarDecl *VD) { + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) { auto it = VarDecls.find(VD); sawMutation |= it != VarDecls.end() && (it->second & RK_Written); }); @@ -2077,7 +2339,7 @@ class VarDeclUsageChecker : public ASTWalker { // If the variable was invalid, ignore it and notice that the code is // malformed. - if (VD->isInvalid() || !VD->hasType()) { + if (VD->isInvalid()) { sawError = true; return false; } @@ -2173,8 +2435,8 @@ class VarDeclUsageChecker : public ASTWalker { Decl *D = node.get(); auto *PBD = dyn_cast(D); if (!PBD) continue; - for (PatternBindingEntry PBE : PBD->getPatternList()) { - PBE.getPattern()->forEachVariable([&](VarDecl *VD) { + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) { VarDecls[VD] = RK_Read|RK_Written; }); } @@ -2256,7 +2518,7 @@ class VarDeclUsageChecker : public ASTWalker { /// An AST walker that determines the underlying type of an opaque return decl /// from its associated function body. class OpaqueUnderlyingTypeChecker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; AbstractFunctionDecl *Implementation; OpaqueTypeDecl *OpaqueDecl; BraceStmt *Body; @@ -2265,11 +2527,10 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { bool HasInvalidReturn = false; public: - OpaqueUnderlyingTypeChecker(TypeChecker &TC, - AbstractFunctionDecl *Implementation, + OpaqueUnderlyingTypeChecker(AbstractFunctionDecl *Implementation, OpaqueTypeDecl *OpaqueDecl, BraceStmt *Body) - : TC(TC), + : Ctx(Implementation->getASTContext()), Implementation(Implementation), OpaqueDecl(OpaqueDecl), Body(Body) @@ -2289,8 +2550,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { // If there are no candidates, then the body has no return statements, and // we have nothing to infer the underlying type from. if (Candidates.empty()) { - TC.diagnose(Implementation->getLoc(), - diag::opaque_type_no_underlying_type_candidates); + Implementation->diagnose(diag::opaque_type_no_underlying_type_candidates); return; } @@ -2312,12 +2572,12 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { } if (mismatch) { - TC.diagnose(Implementation->getLoc(), - diag::opaque_type_mismatched_underlying_type_candidates); + Implementation->diagnose( + diag::opaque_type_mismatched_underlying_type_candidates); for (auto candidate : Candidates) { - TC.diagnose(candidate.first->getLoc(), - diag::opaque_type_underlying_type_candidate_here, - candidate.second); + Ctx.Diags.diagnose(candidate.first->getLoc(), + diag::opaque_type_underlying_type_candidate_here, + candidate.second); } return; } @@ -2329,9 +2589,9 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker { }); if (isSelfReferencing) { - TC.diagnose(Candidates.front().first->getLoc(), - diag::opaque_type_self_referential_underlying_type, - underlyingType); + Ctx.Diags.diagnose(Candidates.front().first->getLoc(), + diag::opaque_type_self_referential_underlying_type, + underlyingType); return; } @@ -2470,11 +2730,11 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { // _ = foo() if (auto *pbd = var->getParentPatternBinding()) if (pbd->getSingleVar() == var && pbd->getInit(0) != nullptr && - !isa(pbd->getPatternList()[0].getPattern())) { + !isa(pbd->getPattern(0))) { unsigned varKind = var->isLet(); SourceRange replaceRange( pbd->getStartLoc(), - pbd->getPatternList()[0].getPattern()->getEndLoc()); + pbd->getPattern(0)->getEndLoc()); Diags.diagnose(var->getLoc(), diag::pbd_never_used, var->getName(), varKind) .fixItReplace(replaceRange, "_"); @@ -2858,14 +3118,14 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { /// Apply the warnings managed by VarDeclUsageChecker to the top level /// code declarations that haven't been checked yet. void swift:: -performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD) { - VarDeclUsageChecker checker(TC.Diags); +performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) { + auto &ctx = TLCD->getDeclContext()->getASTContext(); + VarDeclUsageChecker checker(ctx.Diags); TLCD->walk(checker); } /// Perform diagnostics for func/init/deinit declarations. -void swift::performAbstractFuncDeclDiagnostics(TypeChecker &TC, - AbstractFunctionDecl *AFD, +void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD, BraceStmt *body) { assert(body && "Need a body to check"); @@ -2875,28 +3135,28 @@ void swift::performAbstractFuncDeclDiagnostics(TypeChecker &TC, // Check for unused variables, as well as variables that are could be // declared as constants. - body->walk(VarDeclUsageChecker(TC, AFD)); + body->walk(VarDeclUsageChecker(AFD)); // If the function has an opaque return type, check the return expressions // to determine the underlying type. if (auto opaqueResultTy = AFD->getOpaqueResultTypeDecl()) { - OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy, body).check(); + OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check(); } else if (auto accessor = dyn_cast(AFD)) { if (accessor->isGetter()) { if (auto opaqueResultTy = accessor->getStorage()->getOpaqueResultTypeDecl()) { - OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy, body).check(); + OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check(); } } } } // Perform MiscDiagnostics on Switch Statements. -static void checkSwitch(TypeChecker &TC, const SwitchStmt *stmt) { +static void checkSwitch(ASTContext &ctx, const SwitchStmt *stmt) { // We want to warn about "case .Foo, .Bar where 1 != 100:" since the where // clause only applies to the second case, and this is surprising. for (auto cs : stmt->getCases()) { - TC.checkUnsupportedProtocolType(cs); + TypeChecker::checkUnsupportedProtocolType(ctx, cs); // The case statement can have multiple case items, each can have a where. // If we find a "where", and there is a preceding item without a where, and @@ -2923,32 +3183,32 @@ static void checkSwitch(TypeChecker &TC, const SwitchStmt *stmt) { if (prevLoc.isInvalid() || thisLoc.isInvalid()) continue; - auto &SM = TC.Context.SourceMgr; + auto &SM = ctx.SourceMgr; auto prevLineCol = SM.getLineAndColumn(prevLoc); if (SM.getLineNumber(thisLoc) != prevLineCol.first) continue; - - TC.diagnose(items[i].getWhereLoc(), diag::where_on_one_item) + + ctx.Diags.diagnose(items[i].getWhereLoc(), diag::where_on_one_item) .highlight(items[i].getPattern()->getSourceRange()) .highlight(where->getSourceRange()); // Whitespace it out to the same column as the previous item. std::string whitespace(prevLineCol.second-1, ' '); - TC.diagnose(thisLoc, diag::add_where_newline) + ctx.Diags.diagnose(thisLoc, diag::add_where_newline) .fixItInsert(thisLoc, "\n"+whitespace); auto whereRange = SourceRange(items[i].getWhereLoc(), where->getEndLoc()); auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, whereRange); auto whereText = SM.extractText(charRange); - TC.diagnose(prevLoc, diag::duplicate_where) + ctx.Diags.diagnose(prevLoc, diag::duplicate_where) .fixItInsertAfter(items[i-1].getEndLoc(), " " + whereText.str()) .highlight(items[i-1].getSourceRange()); } } } -void swift::fixItEncloseTrailingClosure(TypeChecker &TC, +void swift::fixItEncloseTrailingClosure(ASTContext &ctx, InFlightDiagnostic &diag, const CallExpr *call, Identifier closureLabel) { @@ -2984,19 +3244,19 @@ void swift::fixItEncloseTrailingClosure(TypeChecker &TC, replacement += ": "; } - lastLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr, lastLoc); + lastLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, lastLoc); diag .fixItReplaceChars(lastLoc, closureRange.Start, replacement) .fixItInsertAfter(closureRange.End, ")"); } // Perform checkStmtConditionTrailingClosure for single expression. -static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { +static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) { if (E == nullptr || isa(E)) return; - // Shallow walker. just dig into implicit expression. + // Walk into expressions which might have invalid trailing closures class DiagnoseWalker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; void diagnoseIt(const CallExpr *E) { if (!E->hasTrailingClosure()) return; @@ -3018,28 +3278,37 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { closureLabel = TT->getElement(TT->getNumElements() - 1).getName(); } - auto diag = TC.diagnose(closureLoc, - diag::trailing_closure_requires_parens); - fixItEncloseTrailingClosure(TC, diag, E, closureLabel); + auto diag = Ctx.Diags.diagnose(closureLoc, + diag::trailing_closure_requires_parens); + fixItEncloseTrailingClosure(Ctx, diag, E, closureLabel); } public: - DiagnoseWalker(TypeChecker &tc) : TC(tc) { } + DiagnoseWalker(ASTContext &ctx) : Ctx(ctx) { } bool shouldWalkIntoNonSingleExpressionClosure() override { return false; } std::pair walkToExprPre(Expr *E) override { - // Dig into implicit expression. - if (E->isImplicit()) return { true, E }; - // Diagnose call expression. - if (auto CE = dyn_cast(E)) - diagnoseIt(CE); - // Don't dig any further. - return { false, E }; + switch (E->getKind()) { + case ExprKind::Paren: + case ExprKind::Tuple: + case ExprKind::Array: + case ExprKind::Dictionary: + case ExprKind::InterpolatedStringLiteral: + // If a trailing closure appears as a child of one of these types of + // expression, don't diagnose it as there is no ambiguity. + return {E->isImplicit(), E}; + case ExprKind::Call: + diagnoseIt(cast(E)); + break; + default: + break; + } + return {true, E}; } }; - DiagnoseWalker Walker(TC); + DiagnoseWalker Walker(ctx); const_cast(E)->walk(Walker); } @@ -3052,23 +3321,27 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { /// E.g.: /// if let _ = arr?.map {$0+1} { ... } /// for _ in numbers.filter {$0 > 4} { ... } -static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Stmt *S) { +static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Stmt *S) { if (auto LCS = dyn_cast(S)) { for (auto elt : LCS->getCond()) { - if (elt.getKind() == StmtConditionElement::CK_PatternBinding) - checkStmtConditionTrailingClosure(TC, elt.getInitializer()); - else if (elt.getKind() == StmtConditionElement::CK_Boolean) - checkStmtConditionTrailingClosure(TC, elt.getBoolean()); + + if (elt.getKind() == StmtConditionElement::CK_PatternBinding) { + checkStmtConditionTrailingClosure(ctx, elt.getInitializer()); + if (auto *exprPattern = dyn_cast(elt.getPattern())) { + checkStmtConditionTrailingClosure(ctx, exprPattern->getMatchExpr()); + } + } else if (elt.getKind() == StmtConditionElement::CK_Boolean) + checkStmtConditionTrailingClosure(ctx, elt.getBoolean()); // No trailing closure for CK_Availability: e.g. `if #available() {}`. } } else if (auto SS = dyn_cast(S)) { - checkStmtConditionTrailingClosure(TC, SS->getSubjectExpr()); + checkStmtConditionTrailingClosure(ctx, SS->getSubjectExpr()); } else if (auto FES = dyn_cast(S)) { - checkStmtConditionTrailingClosure(TC, FES->getSequence()); - checkStmtConditionTrailingClosure(TC, FES->getWhere()); + checkStmtConditionTrailingClosure(ctx, FES->getSequence()); + checkStmtConditionTrailingClosure(ctx, FES->getWhere()); } else if (auto DCS = dyn_cast(S)) { for (auto CS : DCS->getCatches()) - checkStmtConditionTrailingClosure(TC, CS->getGuardExpr()); + checkStmtConditionTrailingClosure(ctx, CS->getGuardExpr()); } } @@ -3109,7 +3382,7 @@ parseObjCSelector(ASTContext &ctx, StringRef string) { namespace { class ObjCSelectorWalker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; const DeclContext *DC; Type SelectorTy; @@ -3126,11 +3399,10 @@ class ObjCSelectorWalker : public ASTWalker { // Look for members with the given name. auto nominal = method->getDeclContext()->getSelfNominalTypeDecl(); - auto result = TC.lookupMember(const_cast(DC), - nominal->getDeclaredInterfaceType(), - lookupName, - (defaultMemberLookupOptions | - NameLookupFlags::KnownPrivate)); + auto result = TypeChecker::lookupMember( + const_cast(DC), nominal->getDeclaredInterfaceType(), + DeclNameRef(lookupName), + (defaultMemberLookupOptions | NameLookupFlags::KnownPrivate)); // If we didn't find multiple methods, there is no ambiguity. if (result.size() < 2) return false; @@ -3156,8 +3428,8 @@ class ObjCSelectorWalker : public ASTWalker { } public: - ObjCSelectorWalker(TypeChecker &tc, const DeclContext *dc, Type selectorTy) - : TC(tc), DC(dc), SelectorTy(selectorTy) { } + ObjCSelectorWalker(const DeclContext *dc, Type selectorTy) + : Ctx(dc->getASTContext()), DC(dc), SelectorTy(selectorTy) { } bool shouldWalkIntoNonSingleExpressionClosure() override { return false; } @@ -3206,7 +3478,7 @@ class ObjCSelectorWalker : public ASTWalker { if (argNames.size() != 1) return { true, expr }; // Is this the init(stringLiteral:) initializer or init(_:) initializer? - if (argNames[0] == TC.Context.Id_stringLiteral) + if (argNames[0] == Ctx.Id_stringLiteral) fromStringLiteral = true; else if (!argNames[0].empty()) return { true, expr }; @@ -3257,7 +3529,7 @@ class ObjCSelectorWalker : public ASTWalker { if (auto coerce = getParentCoercion()) { // If the string literal was coerced to Selector, replace the // coercion with the ")". - SourceLoc endLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr, + SourceLoc endLoc = Lexer::getLocForEndOfToken(Ctx.SourceMgr, expr->getEndLoc()); diag.fixItReplace(SourceRange(endLoc, coerce->getEndLoc()), ")"); } else { @@ -3268,10 +3540,10 @@ class ObjCSelectorWalker : public ASTWalker { // Try to parse the string literal as an Objective-C selector, and complain // if it isn't one. - auto selector = parseObjCSelector(TC.Context, stringLiteral->getValue()); + auto selector = parseObjCSelector(Ctx, stringLiteral->getValue()); if (!selector) { - auto diag = TC.diagnose(stringLiteral->getLoc(), - diag::selector_literal_invalid); + auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), + diag::selector_literal_invalid); diag.highlight(stringLiteral->getSourceRange()); addSelectorConstruction(diag); return { true, expr }; @@ -3289,9 +3561,9 @@ class ObjCSelectorWalker : public ASTWalker { return { true, expr }; { - auto diag = TC.diagnose(stringLiteral->getLoc(), - diag::selector_literal_undeclared, - *selector); + auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), + diag::selector_literal_undeclared, + *selector); addSelectorConstruction(diag); } @@ -3299,8 +3571,8 @@ class ObjCSelectorWalker : public ASTWalker { // separate note that suggests wrapping the selector in // parentheses to silence the warning. if (!fromStringLiteral) { - TC.diagnose(stringLiteral->getLoc(), - diag::selector_construction_suppress_warning) + Ctx.Diags.diagnose(stringLiteral->getLoc(), + diag::selector_construction_suppress_warning) .fixItInsert(stringLiteral->getStartLoc(), "(") .fixItInsertAfter(stringLiteral->getEndLoc(), ")"); } @@ -3385,7 +3657,20 @@ class ObjCSelectorWalker : public ASTWalker { name = bestMethod->getFullName(); } - out << nominal->getName().str() << "." << name.getBaseName(); + auto typeName = nominal->getName().str(); + // If we're inside a type Foo (or an extension of it) and the suggestion + // is going to be #selector(Foo.bar) (or #selector(SuperclassOfFoo.bar), + // then suggest the more natural #selector(self.bar) instead. + if (auto containingTypeContext = DC->getInnermostTypeContext()) { + auto methodNominalType = nominal->getDeclaredType(); + auto outerNomType = containingTypeContext->getSelfNominalTypeDecl() + ->getDeclaredType(); + if (methodNominalType->isEqual(outerNomType) || + methodNominalType->isExactSuperclassOf(outerNomType)) + typeName = "self"; + } + + out << typeName << "." << name.getBaseName(); auto argNames = name.getArgumentNames(); // Only print the parentheses if there are some argument @@ -3425,18 +3710,20 @@ class ObjCSelectorWalker : public ASTWalker { if (auto coerce = getParentCoercion()) replacementRange.End = coerce->getEndLoc(); - TC.diagnose(expr->getLoc(), - fromStringLiteral ? diag::selector_literal_deprecated_suggest - : diag::selector_construction_suggest) - .fixItReplace(replacementRange, replacement); + Ctx.Diags + .diagnose(expr->getLoc(), + fromStringLiteral + ? diag::selector_literal_deprecated_suggest + : diag::selector_construction_suggest) + .fixItReplace(replacementRange, replacement); return { true, expr }; } // If we couldn't pick a method to use for #selector, just wrap // the string literal in Selector(...). if (fromStringLiteral) { - auto diag = TC.diagnose(stringLiteral->getLoc(), - diag::selector_literal_deprecated); + auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), + diag::selector_literal_deprecated); addSelectorConstruction(diag); return { true, expr }; } @@ -3447,12 +3734,12 @@ class ObjCSelectorWalker : public ASTWalker { }; } // end anonymous namespace -static void diagDeprecatedObjCSelectors(TypeChecker &tc, const DeclContext *dc, +static void diagDeprecatedObjCSelectors(const DeclContext *dc, const Expr *expr) { - auto selectorTy = tc.getObjCSelectorType(const_cast(dc)); + auto selectorTy = dc->getASTContext().getSelectorType(); if (!selectorTy) return; - const_cast(expr)->walk(ObjCSelectorWalker(tc, dc, selectorTy)); + const_cast(expr)->walk(ObjCSelectorWalker(dc, selectorTy)); } @@ -3461,7 +3748,7 @@ static void diagDeprecatedObjCSelectors(TypeChecker &tc, const DeclContext *dc, /// if let x: Int = i { static void checkImplicitPromotionsInCondition(const StmtConditionElement &cond, - TypeChecker &TC) { + ASTContext &ctx) { auto *p = cond.getPatternOrNull(); if (!p) return; @@ -3474,33 +3761,35 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond, // Check for 'if let' to produce a tuned diagnostic. if (isa(TP->getSubPattern()) && TP->getSubPattern()->isImplicit()) { - TC.diagnose(cond.getIntroducerLoc(), diag::optional_check_promotion, - subExpr->getType()) + ctx.Diags.diagnose(cond.getIntroducerLoc(), + diag::optional_check_promotion, + subExpr->getType()) .highlight(subExpr->getSourceRange()) .fixItReplace(TP->getTypeLoc().getSourceRange(), ooType->getString()); return; } - TC.diagnose(cond.getIntroducerLoc(), - diag::optional_pattern_match_promotion, - subExpr->getType(), cond.getInitializer()->getType()) + ctx.Diags.diagnose(cond.getIntroducerLoc(), + diag::optional_pattern_match_promotion, + subExpr->getType(), cond.getInitializer()->getType()) .highlight(subExpr->getSourceRange()); return; } - TC.diagnose(cond.getIntroducerLoc(), diag::optional_check_nonoptional, - subExpr->getType()) + ctx.Diags.diagnose(cond.getIntroducerLoc(), + diag::optional_check_nonoptional, + subExpr->getType()) .highlight(subExpr->getSourceRange()); } } -static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, +static void diagnoseUnintendedOptionalBehavior(const Expr *E, const DeclContext *DC) { if (!E || isa(E) || !E->getType()) return; class UnintendedOptionalBehaviorWalker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; SmallPtrSet IgnoredExprs; class OptionalToAnyCoercion { @@ -3583,8 +3872,8 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, coercionString += " as "; coercionString += destType->getWithoutParens()->getString(); - TC.diagnose(E->getLoc(), diag::silence_optional_to_any, - destType, coercionString.substr(1)) + Ctx.Diags.diagnose(E->getLoc(), diag::silence_optional_to_any, + destType, coercionString.substr(1)) .highlight(E->getSourceRange()) .fixItInsertAfter(E->getEndLoc(), coercionString); } @@ -3637,7 +3926,7 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, // Do not warn on coercions from implicitly unwrapped optionals // for Swift versions less than 5. - if (!TC.Context.isSwiftVersionAtLeast(5) && + if (!Ctx.isSwiftVersionAtLeast(5) && hasImplicitlyUnwrappedResult(subExpr)) return; @@ -3657,25 +3946,26 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, // diagnostic if (hasImplicitlyUnwrappedResult(subExpr)) { if (auto decl = getDeclForImplicitlyUnwrappedExpr(subExpr)) { - TC.diagnose(subExpr->getStartLoc(), diag::iuo_to_any_coercion, - /* from */ srcType, /* to */ destType) + Ctx.Diags.diagnose(subExpr->getStartLoc(), diag::iuo_to_any_coercion, + /* from */ srcType, /* to */ destType) .highlight(subExpr->getSourceRange()); auto noteDiag = isa(decl) ? diag::iuo_to_any_coercion_note_func_result : diag::iuo_to_any_coercion_note; - TC.diagnose(decl->getLoc(), noteDiag, decl->getDescriptiveKind(), - decl->getFullName()); + Ctx.Diags.diagnose(decl->getLoc(), noteDiag, + decl->getDescriptiveKind(), decl->getFullName()); } } else { - TC.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion, - /* from */ srcType, /* to */ destType) + Ctx.Diags.diagnose(subExpr->getStartLoc(), + diag::optional_to_any_coercion, + /* from */ srcType, /* to */ destType) .highlight(subExpr->getSourceRange()); } if (optionalityDifference == 1) { - TC.diagnose(subExpr->getLoc(), diag::default_optional_to_any) + Ctx.Diags.diagnose(subExpr->getLoc(), diag::default_optional_to_any) .highlight(subExpr->getSourceRange()) .fixItInsertAfter(subExpr->getEndLoc(), " ?? <#default value#>"); } @@ -3684,7 +3974,7 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, for (size_t i = 0; i < optionalityDifference; i++) forceUnwrapString += "!"; - TC.diagnose(subExpr->getLoc(), diag::force_optional_to_any) + Ctx.Diags.diagnose(subExpr->getLoc(), diag::force_optional_to_any) .highlight(subExpr->getSourceRange()) .fixItInsertAfter(subExpr->getEndLoc(), forceUnwrapString); @@ -3710,8 +4000,8 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, auto subExpr = E->getSubExpr(); - TC.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion, - /* from */ subExpr->getType(), /* to */ E->getType()) + Ctx.Diags.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion, + /* from */ subExpr->getType(), /* to */ E->getType()) .highlight(subExpr->getSourceRange()); emitSilenceOptionalAnyWarningWithCoercion(subExpr, E->getType()); @@ -3743,7 +4033,7 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, }; void visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E) { - E->forEachSegment(TC.Context, + E->forEachSegment(Ctx, [&](bool isInterpolation, CallExpr *segment) -> void { if (isInterpolation) { diagnoseIfUnintendedInterpolation(segment, @@ -3767,8 +4057,7 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, ValueDecl * fnDecl = appendMethod.getDecl(); // If things aren't set up right, just hope for the best. - if (!fnDecl || !fnDecl->getInterfaceType() || - fnDecl->getInterfaceType()->hasError()) + if (!fnDecl || fnDecl->isInvalid()) return false; // If the decl expects an optional, that's fine. @@ -3835,22 +4124,25 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, } void diagnoseUnintendedInterpolation(Expr * arg, UnintendedInterpolationKind kind) { - TC.diagnose(arg->getStartLoc(), - diag::debug_description_in_string_interpolation_segment, - (bool)kind) - .highlight(arg->getSourceRange()); + Ctx.Diags + .diagnose(arg->getStartLoc(), + diag::debug_description_in_string_interpolation_segment, + (bool)kind) + .highlight(arg->getSourceRange()); // Suggest 'String(describing: )'. auto argStart = arg->getStartLoc(); - TC.diagnose(arg->getLoc(), - diag::silence_debug_description_in_interpolation_segment_call) - .highlight(arg->getSourceRange()) - .fixItInsert(argStart, "String(describing: ") - .fixItInsertAfter(arg->getEndLoc(), ")"); + Ctx.Diags + .diagnose( + arg->getLoc(), + diag::silence_debug_description_in_interpolation_segment_call) + .highlight(arg->getSourceRange()) + .fixItInsert(argStart, "String(describing: ") + .fixItInsertAfter(arg->getEndLoc(), ")"); if (kind == UnintendedInterpolationKind::Optional) { // Suggest inserting a default value. - TC.diagnose(arg->getLoc(), diag::default_optional_to_any) + Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_to_any) .highlight(arg->getSourceRange()) .fixItInsertAfter(arg->getEndLoc(), " ?? <#default value#>"); } @@ -3881,20 +4173,20 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E, } public: - UnintendedOptionalBehaviorWalker(TypeChecker &tc) : TC(tc) { } + UnintendedOptionalBehaviorWalker(ASTContext &ctx) : Ctx(ctx) { } }; - UnintendedOptionalBehaviorWalker Walker(TC); + UnintendedOptionalBehaviorWalker Walker(DC->getASTContext()); const_cast(E)->walk(Walker); } -static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, +static void diagnoseDeprecatedWritableKeyPath(const Expr *E, const DeclContext *DC) { if (!E || isa(E) || !E->getType()) return; class DeprecatedWritableKeyPathWalker : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; const DeclContext *DC; void visitKeyPathApplicationExpr(KeyPathApplicationExpr *E) { @@ -3909,8 +4201,8 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, if (auto *keyPathExpr = dyn_cast(E->getKeyPath())) { auto *decl = keyPathExpr->getType()->getNominalOrBoundGenericNominal(); - if (decl != TC.Context.getWritableKeyPathDecl() && - decl != TC.Context.getReferenceWritableKeyPathDecl()) + if (decl != Ctx.getWritableKeyPathDecl() && + decl != Ctx.getReferenceWritableKeyPathDecl()) return; assert(keyPathExpr->getComponents().size() > 0); @@ -3920,9 +4212,9 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, cast(component.getDeclRef().getDecl()); if (!storage->isSettable(nullptr) || !storage->isSetterAccessibleFrom(DC)) { - TC.diagnose(keyPathExpr->getLoc(), - swift::diag::expr_deprecated_writable_keypath, - storage->getFullName()); + Ctx.Diags.diagnose(keyPathExpr->getLoc(), + swift::diag::expr_deprecated_writable_keypath, + storage->getFullName()); } } } @@ -3943,11 +4235,64 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, } public: - DeprecatedWritableKeyPathWalker(TypeChecker &TC, const DeclContext *DC) - : TC(TC), DC(DC) {} + DeprecatedWritableKeyPathWalker(const DeclContext *DC) + : Ctx(DC->getASTContext()), DC(DC) {} + }; + + DeprecatedWritableKeyPathWalker Walker(DC); + const_cast(E)->walk(Walker); +} + +static void maybeDiagnoseCallToKeyValueObserveMethod(const Expr *E, + const DeclContext *DC) { + class KVOObserveCallWalker : public ASTWalker { + const ASTContext &C; + + public: + KVOObserveCallWalker(ASTContext &ctx) : C(ctx) {} + + void maybeDiagnoseCallExpr(CallExpr *expr) { + auto fn = expr->getCalledValue(); + if (!fn) + return; + if (fn->getModuleContext()->getName() != C.Id_Foundation) + return; + if (!fn->getFullName().isCompoundName("observe", + {"", "options", "changeHandler"})) + return; + auto args = cast(expr->getArg()); + auto firstArg = dyn_cast(args->getElement(0)); + if (!firstArg) + return; + auto lastComponent = firstArg->getComponents().back(); + if (lastComponent.getKind() != KeyPathExpr::Component::Kind::Property) + return; + auto property = lastComponent.getDeclRef().getDecl(); + if (!property) + return; + if (property->isObjCDynamic()) + return; + C.Diags + .diagnose(expr->getLoc(), + diag::observe_keypath_property_not_objc_dynamic, + property->getFullName(), fn->getFullName()) + .highlight(lastComponent.getLoc()); + } + + std::pair walkToExprPre(Expr *E) override { + if (!E || isa(E) || !E->getType()) + return {false, E}; + + if (auto *CE = dyn_cast(E)) { + maybeDiagnoseCallExpr(CE); + return {false, E}; + } + + return {true, E}; + } }; - DeprecatedWritableKeyPathWalker Walker(TC, DC); + KVOObserveCallWalker Walker(DC->getASTContext()); const_cast(E)->walk(Walker); } @@ -3956,34 +4301,36 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, //===----------------------------------------------------------------------===// /// Emit diagnostics for syntactic restrictions on a given expression. -void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E, +void swift::performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC, bool isExprStmt) { - TC.diagnoseSelfAssignment(E); - diagSyntacticUseRestrictions(TC, E, DC, isExprStmt); - diagRecursivePropertyAccess(TC, E, DC); - diagnoseImplicitSelfUseInClosure(TC, E, DC); - diagnoseUnintendedOptionalBehavior(TC, E, DC); - if (!TC.Context.isSwiftVersionAtLeast(5)) - diagnoseDeprecatedWritableKeyPath(TC, E, DC); - if (!TC.getLangOpts().DisableAvailabilityChecking) + auto &ctx = DC->getASTContext(); + TypeChecker::diagnoseSelfAssignment(E); + diagSyntacticUseRestrictions(E, DC, isExprStmt); + diagRecursivePropertyAccess(E, DC); + diagnoseImplicitSelfUseInClosure(E, DC); + diagnoseUnintendedOptionalBehavior(E, DC); + maybeDiagnoseCallToKeyValueObserveMethod(E, DC); + if (!ctx.isSwiftVersionAtLeast(5)) + diagnoseDeprecatedWritableKeyPath(E, DC); + if (!ctx.LangOpts.DisableAvailabilityChecking) diagAvailability(E, const_cast(DC)); - if (TC.Context.LangOpts.EnableObjCInterop) - diagDeprecatedObjCSelectors(TC, DC, E); + if (ctx.LangOpts.EnableObjCInterop) + diagDeprecatedObjCSelectors(DC, E); } -void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) { - TC.checkUnsupportedProtocolType(const_cast(S)); +void swift::performStmtDiagnostics(ASTContext &ctx, const Stmt *S) { + TypeChecker::checkUnsupportedProtocolType(ctx, const_cast(S)); if (auto switchStmt = dyn_cast(S)) - checkSwitch(TC, switchStmt); + checkSwitch(ctx, switchStmt); - checkStmtConditionTrailingClosure(TC, S); + checkStmtConditionTrailingClosure(ctx, S); // Check for implicit optional promotions in stmt-condition patterns. if (auto *lcs = dyn_cast(S)) for (const auto &elt : lcs->getCond()) - checkImplicitPromotionsInCondition(elt, TC); + checkImplicitPromotionsInCondition(elt, ctx); } //===----------------------------------------------------------------------===// @@ -4073,9 +4420,7 @@ static OmissionTypeName getTypeNameForOmission(Type type) { Type boolType; if (auto boolDecl = ctx.getBoolDecl()) boolType = boolDecl->getDeclaredInterfaceType(); - Type objcBoolType; - if (auto objcBoolDecl = ctx.getObjCBoolDecl()) - objcBoolType = objcBoolDecl->getDeclaredInterfaceType(); + auto objcBoolType = ctx.getObjCBoolType(); /// Determine the options associated with the given type. auto getOptions = [&](Type type) { @@ -4180,7 +4525,7 @@ static OmissionTypeName getTypeNameForOmission(Type type) { Optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { auto &Context = afd->getASTContext(); - if (!afd->getInterfaceType() || afd->isInvalid() || isa(afd)) + if (afd->isInvalid() || isa(afd)) return None; DeclName name = afd->getFullName(); @@ -4260,7 +4605,7 @@ Optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { Optional TypeChecker::omitNeedlessWords(VarDecl *var) { auto &Context = var->getASTContext(); - if (!var->getInterfaceType() || var->isInvalid()) + if (var->isInvalid()) return None; if (var->getName().empty()) diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 0737a1185b11a..ba8caab557437 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -35,19 +35,17 @@ namespace swift { class ValueDecl; /// Emit diagnostics for syntactic restrictions on a given expression. -void performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E, - const DeclContext *DC, +void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC, bool isExprStmt); /// Emit diagnostics for a given statement. -void performStmtDiagnostics(TypeChecker &TC, const Stmt *S); +void performStmtDiagnostics(ASTContext &ctx, const Stmt *S); -void performAbstractFuncDeclDiagnostics(TypeChecker &TC, - AbstractFunctionDecl *AFD, +void performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD, BraceStmt *body); /// Perform diagnostics on the top level code declaration. -void performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD); +void performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD); /// Emit a fix-it to set the access of \p VD to \p desiredAccess. /// @@ -75,13 +73,13 @@ bool diagnoseArgumentLabelError(ASTContext &ctx, /// with a non-owning attribute, such as 'weak' or 'unowned' and the initializer /// expression refers to a class constructor, emit a warning that the assigned /// instance will be immediately deallocated. -void diagnoseUnownedImmediateDeallocation(TypeChecker &TC, +void diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const AssignExpr *assignExpr); /// If \p pattern binds to a declaration with a non-owning attribute, such as /// 'weak' or 'unowned' and \p initializer refers to a class constructor, /// emit a warning that the bound instance will be immediately deallocated. -void diagnoseUnownedImmediateDeallocation(TypeChecker &TC, +void diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const Pattern *pattern, SourceLoc equalLoc, const Expr *initializer); @@ -90,13 +88,18 @@ void diagnoseUnownedImmediateDeallocation(TypeChecker &TC, /// \p base...but only if we're highly confident that we know what the user /// should have written. /// +/// The \p diag closure allows the caller to control the diagnostic that is +/// emitted. It is passed true if the diagnostic will be emitted with fixits +/// attached, and false otherwise. If None is returned, no diagnostics are +/// emitted. Else the fixits are attached to the returned diagnostic. +/// /// \returns true iff any fix-its were attached to \p diag. -bool fixItOverrideDeclarationTypes(InFlightDiagnostic &diag, - ValueDecl *decl, - const ValueDecl *base); +bool computeFixitsForOverridenDeclaration( + ValueDecl *decl, const ValueDecl *base, + llvm::function_ref(bool)> diag); /// Emit fix-its to enclose trailing closure in argument parens. -void fixItEncloseTrailingClosure(TypeChecker &TC, +void fixItEncloseTrailingClosure(ASTContext &ctx, InFlightDiagnostic &diag, const CallExpr *call, Identifier closureLabel); diff --git a/lib/Sema/NameBinding.cpp b/lib/Sema/NameBinding.cpp index 4d17b4eefc760..1416e6ad8444b 100644 --- a/lib/Sema/NameBinding.cpp +++ b/lib/Sema/NameBinding.cpp @@ -61,19 +61,19 @@ namespace { /// Load a module referenced by an import statement. /// /// Returns null if no module can be loaded. - ModuleDecl *getModule(ArrayRef> ModuleID); + ModuleDecl *getModule(ArrayRef> ModuleID); }; } // end anonymous namespace ModuleDecl * -NameBinder::getModule(ArrayRef> modulePath) { +NameBinder::getModule(ArrayRef> modulePath) { assert(!modulePath.empty()); auto moduleID = modulePath[0]; // The Builtin module cannot be explicitly imported unless we're a .sil file // or in the REPL. if ((SF.Kind == SourceFileKind::SIL || SF.Kind == SourceFileKind::REPL) && - moduleID.first == Context.TheBuiltinModule->getName()) + moduleID.Item == Context.TheBuiltinModule->getName()) return Context.TheBuiltinModule; // If the imported module name is the same as the current module, @@ -82,10 +82,10 @@ NameBinder::getModule(ArrayRef> modulePath) { // // FIXME: We'd like to only use this in SIL mode, but unfortunately we use it // for our fake overlays as well. - if (moduleID.first == SF.getParentModule()->getName() && + if (moduleID.Item == SF.getParentModule()->getName() && modulePath.size() == 1) { if (auto importer = Context.getClangModuleLoader()) - return importer->loadModule(moduleID.second, modulePath); + return importer->loadModule(moduleID.Loc, modulePath); return nullptr; } @@ -170,17 +170,17 @@ static bool shouldImportSelfImportClang(const ImportDecl *ID, void NameBinder::addImport( SmallVectorImpl &imports, ImportDecl *ID) { - if (ID->getModulePath().front().first == SF.getParentModule()->getName() && + if (ID->getModulePath().front().Item == SF.getParentModule()->getName() && ID->getModulePath().size() == 1 && !shouldImportSelfImportClang(ID, SF)) { // If the imported module name is the same as the current module, // produce a diagnostic. StringRef filename = llvm::sys::path::filename(SF.getFilename()); if (filename.empty()) Context.Diags.diagnose(ID, diag::sema_import_current_module, - ID->getModulePath().front().first); + ID->getModulePath().front().Item); else Context.Diags.diagnose(ID, diag::sema_import_current_module_with_file, - filename, ID->getModulePath().front().first); + filename, ID->getModulePath().front().Item); ID->setModule(SF.getParentModule()); return; } @@ -190,7 +190,7 @@ void NameBinder::addImport( SmallString<64> modulePathStr; interleave(ID->getModulePath(), [&](ImportDecl::AccessPathElement elem) { - modulePathStr += elem.first.str(); + modulePathStr += elem.Item.str(); }, [&] { modulePathStr += "."; }); @@ -214,7 +214,7 @@ void NameBinder::addImport( topLevelModule = M; } else { // If we imported a submodule, import the top-level module as well. - Identifier topLevelName = ID->getModulePath().front().first; + Identifier topLevelName = ID->getModulePath().front().Item; topLevelModule = Context.getLoadedModule(topLevelName); if (!topLevelModule) { // Clang can sometimes import top-level modules as if they were @@ -234,8 +234,8 @@ void NameBinder::addImport( !topLevelModule->isTestingEnabled() && !topLevelModule->isNonSwiftModule() && Context.LangOpts.EnableTestableAttrRequiresTestableModule) { - diagnose(ID->getModulePath().front().second, diag::module_not_testable, - ID->getModulePath().front().first); + diagnose(ID->getModulePath().front().Loc, diag::module_not_testable, + ID->getModulePath().front().Item); testableAttr->setInvalid(); } @@ -243,9 +243,9 @@ void NameBinder::addImport( StringRef privateImportFileName; if (privateImportAttr) { if (!topLevelModule || !topLevelModule->arePrivateImportsEnabled()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_for_private_import, - ID->getModulePath().front().first); + ID->getModulePath().front().Item); privateImportAttr->setInvalid(); } else { privateImportFileName = privateImportAttr->getSourceFile(); @@ -256,7 +256,7 @@ void NameBinder::addImport( !topLevelModule->isResilient() && !topLevelModule->isNonSwiftModule() && !ID->getAttrs().hasAttribute()) { - diagnose(ID->getModulePath().front().second, + diagnose(ID->getModulePath().front().Loc, diag::module_not_compiled_with_library_evolution, topLevelModule->getName(), SF.getParentModule()->getName()); } @@ -296,17 +296,17 @@ void NameBinder::addImport( // FIXME: Doesn't handle scoped testable imports correctly. assert(declPath.size() == 1 && "can't handle sub-decl imports"); SmallVector decls; - lookupInModule(topLevelModule, declPath.front().first, decls, + lookupInModule(topLevelModule, declPath.front().Item, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, &SF); if (decls.empty()) { diagnose(ID, diag::decl_does_not_exist_in_module, static_cast(ID->getImportKind()), - declPath.front().first, - ID->getModulePath().front().first) - .highlight(SourceRange(declPath.front().second, - declPath.back().second)); + declPath.front().Item, + ID->getModulePath().front().Item) + .highlight(SourceRange(declPath.front().Loc, + declPath.back().Loc)); return; } @@ -316,7 +316,7 @@ void NameBinder::addImport( if (!actualKind.hasValue()) { // FIXME: print entire module name? diagnose(ID, diag::ambiguous_decl_in_module, - declPath.front().first, M->getName()); + declPath.front().Item, M->getName()); for (auto next : decls) diagnose(next, diag::found_candidate); @@ -338,7 +338,7 @@ void NameBinder::addImport( getImportKindString(ID->getImportKind()))); } else { emittedDiag.emplace(diagnose(ID, diag::imported_decl_is_wrong_kind, - declPath.front().first, + declPath.front().Item, getImportKindString(ID->getImportKind()), static_cast(*actualKind))); } @@ -398,10 +398,11 @@ static void insertPrecedenceGroupDecl(NameBinder &binder, SourceFile &SF, /// unresolved type names as well. This handles import directives and forward /// references. void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { - SharedTimer timer("Name binding"); + FrontendStatsTracer tracer(SF.getASTContext().Stats, "Name binding"); + // Make sure we skip adding the standard library imports if the // source file is empty. - if (SF.ASTStage == SourceFile::NameBound || SF.Decls.empty()) { + if (SF.ASTStage == SourceFile::NameBound || SF.getTopLevelDecls().empty()) { SF.ASTStage = SourceFile::NameBound; return; } @@ -416,7 +417,7 @@ void swift::performNameBinding(SourceFile &SF, unsigned StartElem) { // Do a prepass over the declarations to find and load the imported modules // and map operator decls. - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { if (auto *ID = dyn_cast(D)) { Binder.addImport(ImportedModules, ID); } else if (auto *OD = dyn_cast(D)) { diff --git a/lib/Sema/PCMacro.cpp b/lib/Sema/PCMacro.cpp index a2f52b7371dcd..839176b7aa88d 100644 --- a/lib/Sema/PCMacro.cpp +++ b/lib/Sema/PCMacro.cpp @@ -13,6 +13,35 @@ // This file implements the 'program counter simulation' for Swift. // Based off the PlaygroundTransform, PCMacro instruments code to call // functions at times that a debugger would show the program counter move. +// It can be used to collect and display information about the flow of control +// through Swift code in "live coding" environments like Playgrounds without +// resorting to more heavyweight mechanisms like profiling. +// +// More specifically, this transformation inserts calls to visible functions +// with these names and signatures (other integer types should work too): +// +// func __builtin_pc_before( +// _ startLine: Int, _ endLine: Int, +// _ startColumn: Int, _ endColumn: Int, +// _ moduleID: Int, _ fileID: Int +// ) -> Void +// func __builtin_pc_after( +// _ startLine: Int, _ endLine: Int, +// _ startColumn: Int, _ endColumn: Int, +// _ moduleID: Int, _ fileID: Int +// ) -> Void +// +// The `startLine`, `endLine`, `startColumn`, and `endColumn` parameters are +// passed 1-based integer literals; 0 is used for invalid (i.e. +// compiler-generated) code. The `moduleID` and `fileID` parameters are passed +// the values of visible variables or constants named +// `__builtin_pg_module_` and +// `__builtin_pg_file_`, or an integer literal 0 if suitable +// variables are not found. +// +// The transform inserts these calls before and after each statement, as well +// as before and after expressions nested inside statements, such as `if` and +// `while` conditions and `var` and `let` initial values. // //===----------------------------------------------------------------------===// @@ -40,10 +69,14 @@ namespace { class Instrumenter : InstrumenterBase { private: unsigned &TmpNameIndex; + DeclNameRef LogBeforeName; + DeclNameRef LogAfterName; public: Instrumenter(ASTContext &C, DeclContext *DC, unsigned &TmpNameIndex) - : InstrumenterBase(C, DC), TmpNameIndex(TmpNameIndex) {} + : InstrumenterBase(C, DC), TmpNameIndex(TmpNameIndex), + LogBeforeName(C.getIdentifier("__builtin_pc_before")), + LogAfterName(C.getIdentifier("__builtin_pc_after")) {} Stmt *transformStmt(Stmt *S) { switch (S->getKind()) { @@ -321,7 +354,7 @@ class Instrumenter : InstrumenterBase { if (NB != B) { FD->setBody(NB); - TypeChecker::createForContext(Context).checkFunctionErrorHandling(FD); + TypeChecker::checkFunctionErrorHandling(FD); } } } else if (auto *NTD = dyn_cast(D)) { @@ -343,8 +376,8 @@ class Instrumenter : InstrumenterBase { if (auto *E = Element.dyn_cast()) { E->walk(CF); - Added LogBefore = buildLoggerCall(E->getSourceRange(), true); - Added LogAfter = buildLoggerCall(E->getSourceRange(), false); + Added LogBefore = buildLoggerCall(LogBeforeName, E->getSourceRange()); + Added LogAfter = buildLoggerCall(LogAfterName, E->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; @@ -368,9 +401,9 @@ class Instrumenter : InstrumenterBase { ReturnStmt *NRS = new (Context) ReturnStmt(SourceLoc(), DRE, true); // implicit Added LogBefore = - buildLoggerCall(RS->getSourceRange(), true); + buildLoggerCall(LogBeforeName, RS->getSourceRange()); Added LogAfter = - buildLoggerCall(RS->getSourceRange(), false); + buildLoggerCall(LogAfterName, RS->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; Elements.insert(Elements.begin() + (EI + 1), PV.first); @@ -381,9 +414,9 @@ class Instrumenter : InstrumenterBase { } } else { Added LogBefore = - buildLoggerCall(RS->getSourceRange(), true); + buildLoggerCall(LogBeforeName, RS->getSourceRange()); Added LogAfter = - buildLoggerCall(RS->getSourceRange(), false); + buildLoggerCall(LogAfterName, RS->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; Elements.insert(Elements.begin() + (EI + 1), *LogAfter); @@ -392,8 +425,8 @@ class Instrumenter : InstrumenterBase { } } } else if (auto *CS = dyn_cast(S)) { - Added LogBefore = buildLoggerCall(CS->getSourceRange(), true); - Added LogAfter = buildLoggerCall(CS->getSourceRange(), false); + Added LogBefore = buildLoggerCall(LogBeforeName, CS->getSourceRange()); + Added LogAfter = buildLoggerCall(LogAfterName, CS->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; Elements.insert(Elements.begin() + (EI + 1), *LogAfter); @@ -402,8 +435,8 @@ class Instrumenter : InstrumenterBase { } } else if (auto *BS = dyn_cast(S)) { - Added LogBefore = buildLoggerCall(BS->getSourceRange(), true); - Added LogAfter = buildLoggerCall(BS->getSourceRange(), false); + Added LogBefore = buildLoggerCall(LogBeforeName, BS->getSourceRange()); + Added LogAfter = buildLoggerCall(LogAfterName, BS->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; Elements.insert(Elements.begin() + (EI + 1), *LogAfter); @@ -412,8 +445,8 @@ class Instrumenter : InstrumenterBase { } } else if (auto *FS = dyn_cast(S)) { - Added LogBefore = buildLoggerCall(FS->getSourceRange(), true); - Added LogAfter = buildLoggerCall(FS->getSourceRange(), false); + Added LogBefore = buildLoggerCall(LogBeforeName, FS->getSourceRange()); + Added LogAfter = buildLoggerCall(LogAfterName, FS->getSourceRange()); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; Elements.insert(Elements.begin() + (EI + 1), *LogAfter); @@ -439,8 +472,8 @@ class Instrumenter : InstrumenterBase { SR = PBD->getOriginalInitRange(0); } - Added LogBefore = buildLoggerCall(SR, true); - Added LogAfter = buildLoggerCall(SR, false); + Added LogBefore = buildLoggerCall(LogBeforeName, SR); + Added LogAfter = buildLoggerCall(LogAfterName, SR); if (*LogBefore && *LogAfter) { Elements[EI] = *LogBefore; @@ -462,10 +495,8 @@ class Instrumenter : InstrumenterBase { std::pair buildPatternAndVariable(Expr *InitExpr) { - // This is 16 because "pctmp" is 5 chars, %u is at most 10 digits long plus - // a null terminator. - char NameBuf[16] = {0}; - snprintf(NameBuf, sizeof(NameBuf), "pctmp%u", TmpNameIndex); + SmallString<16> NameBuf; + (Twine("pctmp") + Twine(TmpNameIndex)).toVector(NameBuf); TmpNameIndex++; Expr *MaybeLoadInitExpr = nullptr; @@ -482,7 +513,6 @@ class Instrumenter : InstrumenterBase { /*IsCaptureList*/false, SourceLoc(), Context.getIdentifier(NameBuf), TypeCheckDC); - VD->setType(MaybeLoadInitExpr->getType()); VD->setInterfaceType(MaybeLoadInitExpr->getType()->mapTypeOutOfContext()); VD->setImplicit(); @@ -493,19 +523,11 @@ class Instrumenter : InstrumenterBase { return std::make_pair(PBD, VD); } - Added buildLoggerCall(SourceRange SR, bool isBefore) { - if (isBefore) { - return buildLoggerCallWithArgs("__builtin_pc_before", SR); - } else { - return buildLoggerCallWithArgs("__builtin_pc_after", SR); - } - } - // Puts a pair of before/after calls at the start of the body, pointing at // that range. BraceStmt *prependLoggerCall(BraceStmt *BS, SourceRange SR) { - Added Before = buildLoggerCall(SR, true); - Added After = buildLoggerCall(SR, false); + Added Before = buildLoggerCall(LogBeforeName, SR); + Added After = buildLoggerCall(LogAfterName, SR); ArrayRef OriginalElements = BS->getElements(); SmallVector Elements(OriginalElements.begin(), @@ -535,17 +557,8 @@ class Instrumenter : InstrumenterBase { Expr *StartColumn = IntegerLiteralExpr::createFromUnsigned(Context, StartLC.second); Expr *EndColumn = IntegerLiteralExpr::createFromUnsigned(Context, EndLC.second); - Expr *ModuleExpr = - !ModuleIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - ModuleIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); - - Expr *FileExpr = - !FileIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - FileIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); + Expr *ModuleExpr = buildIDArgumentExpr(ModuleIdentifier, SR); + Expr *FileExpr = buildIDArgumentExpr(FileIdentifier, SR); llvm::SmallVector ArgsWithSourceRange{}; @@ -553,7 +566,7 @@ class Instrumenter : InstrumenterBase { {StartLine, EndLine, StartColumn, EndColumn, ModuleExpr, FileExpr}); UnresolvedDeclRefExpr *BeforeLoggerRef = new (Context) - UnresolvedDeclRefExpr(Context.getIdentifier("__builtin_pc_before"), + UnresolvedDeclRefExpr(LogBeforeName, DeclRefKind::Ordinary, DeclNameLoc(SR.End)); BeforeLoggerRef->setImplicit(true); SmallVector ArgLabels(ArgsWithSourceRange.size(), @@ -567,7 +580,7 @@ class Instrumenter : InstrumenterBase { } UnresolvedDeclRefExpr *AfterLoggerRef = new (Context) - UnresolvedDeclRefExpr(Context.getIdentifier("__builtin_pc_after"), + UnresolvedDeclRefExpr(LogAfterName, DeclRefKind::Ordinary, DeclNameLoc(SR.End)); AfterLoggerRef->setImplicit(true); ApplyExpr *AfterLoggerCall = CallExpr::createImplicit( @@ -598,8 +611,7 @@ class Instrumenter : InstrumenterBase { return *AddedGet; } - Added buildLoggerCallWithArgs(const char *LoggerName, - SourceRange SR) { + Added buildLoggerCall(DeclNameRef LoggerName, SourceRange SR) { if (!SR.isValid()) { return nullptr; } @@ -615,25 +627,15 @@ class Instrumenter : InstrumenterBase { Expr *StartColumn = IntegerLiteralExpr::createFromUnsigned(Context, StartLC.second); Expr *EndColumn = IntegerLiteralExpr::createFromUnsigned(Context, EndLC.second); - Expr *ModuleExpr = - !ModuleIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - ModuleIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); + Expr *ModuleExpr = buildIDArgumentExpr(ModuleIdentifier, SR); + Expr *FileExpr = buildIDArgumentExpr(FileIdentifier, SR); - Expr *FileExpr = - !FileIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - FileIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); - - llvm::SmallVector ArgsWithSourceRange{}; - - ArgsWithSourceRange.append( - {StartLine, EndLine, StartColumn, EndColumn, ModuleExpr, FileExpr}); + llvm::SmallVector ArgsWithSourceRange{ + StartLine, EndLine, StartColumn, EndColumn, ModuleExpr, FileExpr + }; UnresolvedDeclRefExpr *LoggerRef = new (Context) - UnresolvedDeclRefExpr(Context.getIdentifier(LoggerName), + UnresolvedDeclRefExpr(LoggerName, DeclRefKind::Ordinary, DeclNameLoc(SR.End)); LoggerRef->setImplicit(true); @@ -666,14 +668,13 @@ class Instrumenter : InstrumenterBase { } // end anonymous namespace -void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { +void swift::performPCMacro(SourceFile &SF) { class ExpressionFinder : public ASTWalker { private: unsigned TmpNameIndex = 0; - TopLevelContext &TLC; public: - ExpressionFinder(TopLevelContext &TLC) : TLC(TLC) {} + ExpressionFinder() = default; bool walkToDeclPre(Decl *D) override { ASTContext &ctx = D->getASTContext(); @@ -692,9 +693,8 @@ void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { BraceStmt *NewBody = I.transformBraceStmt(Body, true); if (NewBody != Body) { TLCD->setBody(NewBody); - TypeChecker &TC = TypeChecker::createForContext(ctx); - TC.checkTopLevelErrorHandling(TLCD); - TC.contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::checkTopLevelErrorHandling(TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } return false; } @@ -704,8 +704,8 @@ void swift::performPCMacro(SourceFile &SF, TopLevelContext &TLC) { } }; - ExpressionFinder EF(TLC); - for (Decl *D : SF.Decls) { + ExpressionFinder EF; + for (Decl *D : SF.getTopLevelDecls()) { D->walk(EF); } } diff --git a/lib/Sema/PlaygroundTransform.cpp b/lib/Sema/PlaygroundTransform.cpp index e722d7df42fe2..f7d55ba93b198 100644 --- a/lib/Sema/PlaygroundTransform.cpp +++ b/lib/Sema/PlaygroundTransform.cpp @@ -42,6 +42,14 @@ class Instrumenter : InstrumenterBase { unsigned &TmpNameIndex; bool HighPerformance; + DeclNameRef DebugPrintName; + DeclNameRef PrintName; + DeclNameRef PostPrintName; + DeclNameRef LogWithIDName; + DeclNameRef LogScopeExitName; + DeclNameRef LogScopeEntryName; + DeclNameRef SendDataName; + struct BracePair { public: SourceRange BraceRange; @@ -117,7 +125,14 @@ class Instrumenter : InstrumenterBase { Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG, bool HP, unsigned &TmpNameIndex) : InstrumenterBase(C, DC), RNG(RNG), TmpNameIndex(TmpNameIndex), - HighPerformance(HP) {} + HighPerformance(HP), + DebugPrintName(C.getIdentifier("__builtin_debugPrint")), + PrintName(C.getIdentifier("__builtin_print")), + PostPrintName(C.getIdentifier("__builtin_postPrint")), + LogWithIDName(C.getIdentifier("__builtin_log_with_id")), + LogScopeExitName(C.getIdentifier("__builtin_log_scope_exit")), + LogScopeEntryName(C.getIdentifier("__builtin_log_scope_entry")), + SendDataName(C.getIdentifier("__builtin_send_data")) { } Stmt *transformStmt(Stmt *S) { switch (S->getKind()) { @@ -279,7 +294,7 @@ class Instrumenter : InstrumenterBase { BraceStmt *NB = transformBraceStmt(B); if (NB != B) { FD->setBody(NB); - TypeChecker::createForContext(Context).checkFunctionErrorHandling(FD); + TypeChecker::checkFunctionErrorHandling(FD); } } } else if (auto *NTD = dyn_cast(D)) { @@ -320,21 +335,29 @@ class Instrumenter : InstrumenterBase { } case ExprKind::Load: return digForVariable(cast(E)->getSubExpr()); - case ExprKind::ForceValue: - return digForVariable(cast(E)->getSubExpr()); + case ExprKind::ForceValue: { + std::pair, ValueDecl *> BaseVariable = + digForVariable(cast(E)->getSubExpr()); + if (!*BaseVariable.first || !BaseVariable.second) + return std::make_pair(nullptr, nullptr); + + Added Forced( + new (Context) ForceValueExpr(*BaseVariable.first, SourceLoc())); + return std::make_pair(Forced, BaseVariable.second); + } case ExprKind::InOut: return digForVariable(cast(E)->getSubExpr()); } } - std::string digForName(Expr *E) { + DeclBaseName digForName(Expr *E) { Added RE = nullptr; ValueDecl *VD = nullptr; std::tie(RE, VD) = digForVariable(E); if (VD) { - return VD->getBaseName().getIdentifier().str(); + return VD->getBaseName(); } else { - return std::string(""); + return DeclBaseName(); } } @@ -402,14 +425,14 @@ class Instrumenter : InstrumenterBase { true); // implicit NAE->setType(Context.TheEmptyTupleType); AE->setImplicit(true); - std::string Name = digForName(AE->getDest()); - Added Log(buildLoggerCall( - new (Context) DeclRefExpr( - ConcreteDeclRef(PV.second), DeclNameLoc(), - true, // implicit - AccessSemantics::Ordinary, AE->getSrc()->getType()), - AE->getSrc()->getSourceRange(), Name.c_str())); + DeclBaseName Name = digForName(AE->getDest()); + Expr * PVVarRef = new (Context) DeclRefExpr( + ConcreteDeclRef(PV.second), DeclNameLoc(), /*implicit=*/ true, + AccessSemantics::Ordinary, AE->getSrc()->getType()); + Added Log( + buildLoggerCall(PVVarRef, AE->getSrc()->getSourceRange(), + Name.getIdentifier().str())); if (*Log) { Elements[EI] = PV.first; @@ -424,11 +447,11 @@ class Instrumenter : InstrumenterBase { if (auto *DRE = dyn_cast(AE->getFn())) { auto *FnD = dyn_cast(DRE->getDecl()); if (FnD && FnD->getModuleContext() == Context.TheStdlibModule) { - StringRef FnName = FnD->getNameStr(); - if (FnName.equals("print") || FnName.equals("debugPrint")) { + DeclBaseName FnName = FnD->getBaseName(); + if (FnName == "print" || FnName == "debugPrint") { const bool isOldStyle = false; if (isOldStyle) { - const bool isDebugPrint = FnName.equals("debugPrint"); + const bool isDebugPrint = (FnName == "debugPrint"); PatternBindingDecl *ArgPattern = nullptr; VarDecl *ArgVariable = nullptr; Added Log = @@ -605,39 +628,36 @@ class Instrumenter : InstrumenterBase { new (Context) DeclRefExpr(ConcreteDeclRef(VD), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, Type()), - VD->getSourceRange(), VD->getName().str().str().c_str()); + VD->getSourceRange(), VD->getName().str()); } Added logDeclOrMemberRef(Added RE) { if (auto *DRE = dyn_cast(*RE)) { VarDecl *VD = cast(DRE->getDecl()); - if (isa(TypeCheckDC) && - VD->getNameStr().equals("self")) { + if (isa(TypeCheckDC) && VD->getBaseName() == "self") { // Don't log "self" in a constructor return nullptr; } return buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(VD), DeclNameLoc(), - true, // implicit - AccessSemantics::Ordinary, Type()), - DRE->getSourceRange(), VD->getName().str().str().c_str()); + /*implicit=*/true), + DRE->getSourceRange(), VD->getName().str()); } else if (auto *MRE = dyn_cast(*RE)) { Expr *B = MRE->getBase(); ConcreteDeclRef M = MRE->getMember(); - if (isa(TypeCheckDC) && !digForName(B).compare("self")) { + if (isa(TypeCheckDC) && digForName(B) == "self") { // Don't log attributes of "self" in a constructor return nullptr; } return buildLoggerCall( new (Context) MemberRefExpr(B, SourceLoc(), M, DeclNameLoc(), - true, // implicit - AccessSemantics::Ordinary), + /*implicit=*/true), MRE->getSourceRange(), - M.getDecl()->getBaseName().getIdentifier().str().str().c_str()); + M.getDecl()->getBaseName().userFacingName()); } else { return nullptr; } @@ -699,11 +719,10 @@ class Instrumenter : InstrumenterBase { Added logPrint(bool isDebugPrint, ApplyExpr *AE, PatternBindingDecl *&ArgPattern, VarDecl *&ArgVariable) { - const char *LoggerName = - isDebugPrint ? "__builtin_debugPrint" : "__builtin_print"; + DeclNameRef LoggerName = isDebugPrint ? DebugPrintName : PrintName; UnresolvedDeclRefExpr *LoggerRef = new (Context) UnresolvedDeclRefExpr( - Context.getIdentifier(LoggerName), DeclRefKind::Ordinary, + LoggerName, DeclRefKind::Ordinary, DeclNameLoc(AE->getSourceRange().End)); std::tie(ArgPattern, ArgVariable) = maybeFixupPrintArgument(AE); @@ -719,17 +738,13 @@ class Instrumenter : InstrumenterBase { } Added logPostPrint(SourceRange SR) { - return buildLoggerCallWithArgs("__builtin_postPrint", - MutableArrayRef(), SR); + return buildLoggerCallWithArgs(PostPrintName, {}, SR); } std::pair buildPatternAndVariable(Expr *InitExpr) { - // This is 14 because "tmp" is 3 chars, %u is at most 10 digits long plus a - // null terminator. - char NameBuf[14] = {0}; - snprintf(NameBuf, sizeof(NameBuf), "tmp%u", TmpNameIndex); - TmpNameIndex++; + SmallString<16> NameBuf; + (Twine("tmp") + Twine(TmpNameIndex)).toVector(NameBuf); Expr *MaybeLoadInitExpr = nullptr; @@ -745,7 +760,6 @@ class Instrumenter : InstrumenterBase { /*IsCaptureList*/false, SourceLoc(), Context.getIdentifier(NameBuf), TypeCheckDC); - VD->setType(MaybeLoadInitExpr->getType()); VD->setInterfaceType(MaybeLoadInitExpr->getType()->mapTypeOutOfContext()); VD->setImplicit(); @@ -757,41 +771,27 @@ class Instrumenter : InstrumenterBase { } Added buildLoggerCall(Added E, SourceRange SR, - const char *Name) { - assert(Name); - std::string *NameInContext = Context.AllocateObjectCopy(std::string(Name)); - - Expr *NameExpr = - new (Context) StringLiteralExpr(NameInContext->c_str(), SourceRange()); - NameExpr->setImplicit(true); + StringRef Name) { + Expr *NameExpr = new (Context) StringLiteralExpr( + Context.AllocateCopy(Name), SourceRange(), /*implicit=*/true); std::uniform_int_distribution Distribution(0, 0x7fffffffu); const unsigned id_num = Distribution(RNG); Expr *IDExpr = IntegerLiteralExpr::createFromUnsigned(Context, id_num); - Expr *LoggerArgExprs[] = {*E, NameExpr, IDExpr}; - - return buildLoggerCallWithArgs("__builtin_log_with_id", - MutableArrayRef(LoggerArgExprs), SR); + return buildLoggerCallWithArgs(LogWithIDName, { *E, NameExpr, IDExpr }, SR); } Added buildScopeEntry(SourceRange SR) { - return buildScopeCall(SR, false); + return buildLoggerCallWithArgs(LogScopeEntryName, {}, SR); } Added buildScopeExit(SourceRange SR) { - return buildScopeCall(SR, true); - } - - Added buildScopeCall(SourceRange SR, bool IsExit) { - const char *LoggerName = - IsExit ? "__builtin_log_scope_exit" : "__builtin_log_scope_entry"; - - return buildLoggerCallWithArgs(LoggerName, MutableArrayRef(), SR); + return buildLoggerCallWithArgs(LogScopeExitName, {}, SR); } - Added buildLoggerCallWithArgs(const char *LoggerName, - MutableArrayRef Args, + Added buildLoggerCallWithArgs(DeclNameRef LoggerName, + ArrayRef Args, SourceRange SR) { // If something doesn't have a valid source range it can not be playground // logged. For example, a PC Macro event. @@ -810,17 +810,8 @@ class Instrumenter : InstrumenterBase { Expr *StartColumn = IntegerLiteralExpr::createFromUnsigned(Context, StartLC.second); Expr *EndColumn = IntegerLiteralExpr::createFromUnsigned(Context, EndLC.second); - Expr *ModuleExpr = - !ModuleIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - ModuleIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); - - Expr *FileExpr = - !FileIdentifier.empty() - ? (Expr *)new (Context) UnresolvedDeclRefExpr( - FileIdentifier, DeclRefKind::Ordinary, DeclNameLoc(SR.End)) - : (Expr *)IntegerLiteralExpr::createFromUnsigned(Context, 0); + Expr *ModuleExpr = buildIDArgumentExpr(ModuleIdentifier, SR); + Expr *FileExpr = buildIDArgumentExpr(FileIdentifier, SR); llvm::SmallVector ArgsWithSourceRange(Args.begin(), Args.end()); @@ -828,15 +819,12 @@ class Instrumenter : InstrumenterBase { {StartLine, EndLine, StartColumn, EndColumn, ModuleExpr, FileExpr}); UnresolvedDeclRefExpr *LoggerRef = new (Context) - UnresolvedDeclRefExpr(Context.getIdentifier(LoggerName), - DeclRefKind::Ordinary, DeclNameLoc(SR.End)); - + UnresolvedDeclRefExpr(LoggerName, DeclRefKind::Ordinary, + DeclNameLoc(SR.End)); LoggerRef->setImplicit(true); - SmallVector ArgLabels(ArgsWithSourceRange.size(), - Identifier()); - ApplyExpr *LoggerCall = CallExpr::createImplicit( - Context, LoggerRef, ArgsWithSourceRange, ArgLabels); + ApplyExpr *LoggerCall = CallExpr::createImplicit(Context, LoggerRef, + ArgsWithSourceRange, {}); Added AddedLogger(LoggerCall); if (!doTypeCheck(Context, TypeCheckDC, AddedLogger)) { @@ -858,8 +846,8 @@ class Instrumenter : InstrumenterBase { AccessSemantics::Ordinary, Apply->getType()); UnresolvedDeclRefExpr *SendDataRef = new (Context) - UnresolvedDeclRefExpr(Context.getIdentifier("__builtin_send_data"), - DeclRefKind::Ordinary, DeclNameLoc()); + UnresolvedDeclRefExpr(SendDataName, DeclRefKind::Ordinary, + DeclNameLoc()); SendDataRef->setImplicit(true); @@ -904,7 +892,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) { BraceStmt *NewBody = I.transformBraceStmt(Body); if (NewBody != Body) { FD->setBody(NewBody); - TypeChecker::createForContext(ctx).checkFunctionErrorHandling(FD); + TypeChecker::checkFunctionErrorHandling(FD); } return false; } @@ -916,8 +904,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) { BraceStmt *NewBody = I.transformBraceStmt(Body, true); if (NewBody != Body) { TLCD->setBody(NewBody); - TypeChecker::createForContext(ctx) - .checkTopLevelErrorHandling(TLCD); + TypeChecker::checkTopLevelErrorHandling(TLCD); } return false; } diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index af9c6362884af..37a9d6cf8bc73 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -84,17 +84,6 @@ TypeChecker::getFragileFunctionKind(const DeclContext *DC) { llvm_unreachable("Context is not nested inside a fragile function"); } -void TypeChecker::diagnoseInlinableLocalType(const NominalTypeDecl *NTD) { - auto *DC = NTD->getDeclContext(); - auto expansion = DC->getResilienceExpansion(); - if (expansion == ResilienceExpansion::Minimal) { - auto kind = getFragileFunctionKind(DC); - diagnose(NTD, diag::local_type_in_inlinable_function, - NTD->getFullName(), - static_cast(kind.first)); - } -} - /// A uniquely-typed boolean to reduce the chances of accidentally inverting /// a check. enum class DowngradeToWarning: bool { @@ -156,8 +145,7 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, // Property initializers that are not exposed to clients are OK. if (auto pattern = dyn_cast(DC)) { auto bindingIndex = pattern->getBindingIndex(); - auto &patternEntry = pattern->getBinding()->getPatternList()[bindingIndex]; - auto varDecl = patternEntry.getAnchoringVarDecl(); + auto *varDecl = pattern->getBinding()->getAnchoringVarDecl(bindingIndex); if (!varDecl->isInitExposedToClients()) return false; } diff --git a/lib/Sema/SolutionResult.h b/lib/Sema/SolutionResult.h new file mode 100644 index 0000000000000..e0e73382303f8 --- /dev/null +++ b/lib/Sema/SolutionResult.h @@ -0,0 +1,148 @@ +//===--- SolutionResult.h - Constraint System Solution ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the SolutionResult class. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_TYPECHECK_SOLUTION_RESULT_H +#define SWIFT_TYPECHECK_SOLUTION_RESULT_H + +#include "llvm/ADT/ArrayRef.h" + +namespace swift { + +using llvm::ArrayRef; +using llvm::makeArrayRef; + +namespace constraints { + +class Solution; + +/// Describes the result of solving a constraint system, after +/// potentially taking various corrective actions. +class SolutionResult { +public: + enum Kind : unsigned char { + /// The constraint system was successfully solved, and one can + /// retrieve the resulting solution. + Success, + /// The constraint system had multiple solutions, none of which + /// was better than the others. + Ambiguous, + /// The constraint system had no solution, and a diagnostic has + /// already been emitted. + Error, + /// The constraint system had no solution, but no diagnostic has + /// been emitted yet. + UndiagnosedError, + /// The constraint system was too complex to solve, but no + /// diagnostic has been emitted yet. + TooComplex, + }; + +private: + /// The kind of solution result. + Kind kind; + + /// Whether the client has emitted a diagnostic. + unsigned emittedDiagnostic : 1; + + /// The number of solutions owned by this result. + unsigned numSolutions = 0; + + /// A pointer to the set of solutions, of which there are + /// \c numSolutions entries. + Solution *solutions = nullptr; + + /// General constructor for the named constructors. + SolutionResult(Kind kind) : kind(kind) { + emittedDiagnostic = false; + } + +public: + SolutionResult(const SolutionResult &other) = delete; + + SolutionResult(SolutionResult &&other) + : kind(other.kind), numSolutions(other.numSolutions), + solutions(other.solutions) { + emittedDiagnostic = false; + other.kind = Error; + other.numSolutions = 0; + other.solutions = nullptr; + } + + SolutionResult &operator=(const SolutionResult &other) = delete; + SolutionResult &operator=(SolutionResult &&other) = delete; + + ~SolutionResult(); + + /// Produce a "solved" result, embedding the given solution. + static SolutionResult forSolved(Solution &&solution); + + /// Produce an "ambiguous" result, providing the set of + /// potential solutions. + static SolutionResult forAmbiguous(MutableArrayRef solutions); + + /// Produce a "too complex" failure, which was not yet been + /// diagnosed. + static SolutionResult forTooComplex() { + return SolutionResult(TooComplex); + } + + /// Produce a failure that has already been diagnosed. + static SolutionResult forError() { + return SolutionResult(Error); + } + + /// Produce a failure that has not yet been diagnosed. + static SolutionResult forUndiagnosedError() { + return SolutionResult(UndiagnosedError); + } + + Kind getKind() const{ return kind; } + + /// Retrieve the solution, where there is one. + const Solution &getSolution() const; + + /// Retrieve the solution, where there is one. + Solution &&takeSolution() &&; + + /// Retrieve the set of solutions when there is an ambiguity. + ArrayRef getAmbiguousSolutions() const; + + /// Take the set of solutions when there is an ambiguity. + MutableArrayRef takeAmbiguousSolutions() &&; + + /// Whether this solution requires the client to produce a diagnostic. + bool requiresDiagnostic() const { + switch (kind) { + case Success: + case Ambiguous: + case Error: + return false; + + case UndiagnosedError: + case TooComplex: + return true; + } + } + + /// Note that the failure has been diagnosed. + void markAsDiagnosed() { + emittedDiagnostic = true; + } +}; + +} } + +#endif /* SWIFT_TYPECHECK_SOLUTION_RESULT_H */ diff --git a/lib/Sema/SourceLoader.cpp b/lib/Sema/SourceLoader.cpp index 3026605d6faf5..c1300e38e9054 100644 --- a/lib/Sema/SourceLoader.cpp +++ b/lib/Sema/SourceLoader.cpp @@ -62,15 +62,15 @@ void SourceLoader::collectVisibleTopLevelModuleNames( // TODO: Implement? } -bool SourceLoader::canImportModule(std::pair ID) { +bool SourceLoader::canImportModule(Located ID) { // Search the memory buffers to see if we can find this file on disk. - FileOrError inputFileOrError = findModule(Ctx, ID.first.str(), - ID.second); + FileOrError inputFileOrError = findModule(Ctx, ID.Item.str(), + ID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(ID.second, diag::sema_opening_import, - ID.first, err.message()); + Ctx.Diags.diagnose(ID.Loc, diag::sema_opening_import, + ID.Item, err.message()); } return false; @@ -79,21 +79,20 @@ bool SourceLoader::canImportModule(std::pair ID) { } ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, - ArrayRef> path) { + ArrayRef> path) { // FIXME: Swift submodules? if (path.size() > 1) return nullptr; auto moduleID = path[0]; - FileOrError inputFileOrError = findModule(Ctx, moduleID.first.str(), - moduleID.second); + FileOrError inputFileOrError = findModule(Ctx, moduleID.Item.str(), + moduleID.Loc); if (!inputFileOrError) { auto err = inputFileOrError.getError(); if (err != std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(moduleID.second, diag::sema_opening_import, - moduleID.first, err.message()); + Ctx.Diags.diagnose(moduleID.Loc, diag::sema_opening_import, + moduleID.Item, err.message()); } return nullptr; @@ -106,8 +105,8 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, /*isSystem=*/false); // Turn off debugging while parsing other modules. - llvm::SaveAndRestore turnOffDebug(Ctx.LangOpts.DebugConstraintSolver, - false); + llvm::SaveAndRestore + turnOffDebug(Ctx.TypeCheckerOpts.DebugConstraintSolver, false); unsigned bufferID; if (auto BufID = @@ -116,10 +115,10 @@ ModuleDecl *SourceLoader::loadModule(SourceLoc importLoc, else bufferID = Ctx.SourceMgr.addNewSourceBuffer(std::move(inputFile)); - auto *importMod = ModuleDecl::create(moduleID.first, Ctx); + auto *importMod = ModuleDecl::create(moduleID.Item, Ctx); if (EnableLibraryEvolution) importMod->setResilienceStrategy(ResilienceStrategy::Resilient); - Ctx.LoadedModules[moduleID.first] = importMod; + Ctx.LoadedModules[moduleID.Item] = importMod; auto implicitImportKind = SourceFile::ImplicitModuleImportKind::Stdlib; if (!Ctx.getStdlibModule()) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 142234f1102ab..716872331ef99 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -14,11 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "TypeChecker.h" #include "TypeCheckAccess.h" #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" @@ -73,7 +73,6 @@ using CheckTypeAccessCallback = class AccessControlCheckerBase { protected: - TypeChecker &TC; bool checkUsableFromInline; void checkTypeAccessImpl( @@ -104,19 +103,19 @@ class AccessControlCheckerBase { }); } - AccessControlCheckerBase(TypeChecker &TC, bool checkUsableFromInline) - : TC(TC), checkUsableFromInline(checkUsableFromInline) {} + AccessControlCheckerBase(bool checkUsableFromInline) + : checkUsableFromInline(checkUsableFromInline) {} public: void checkGenericParamAccess( - const GenericParamList *params, - const Decl *owner, + const GenericContext *ownerCtx, + const Decl *ownerDecl, AccessScope accessScope, AccessLevel contextAccess); void checkGenericParamAccess( - const GenericParamList *params, - const ValueDecl *owner); + const GenericContext *ownerCtx, + const ValueDecl *ownerDecl); }; class TypeAccessScopeDiagnoser : private ASTWalker { @@ -193,7 +192,8 @@ void AccessControlCheckerBase::checkTypeAccessImpl( Type type, TypeRepr *typeRepr, AccessScope contextAccessScope, const DeclContext *useDC, bool mayBeInferred, llvm::function_ref diagnose) { - if (TC.Context.isAccessControlDisabled()) + auto &Context = useDC->getASTContext(); + if (Context.isAccessControlDisabled()) return; // Don't spend time checking local declarations; this is always valid by the // time we get to this point. @@ -244,7 +244,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl( // TypeRepr into account). if (typeRepr && mayBeInferred && - !TC.getLangOpts().isSwiftVersionAtLeast(5) && + !Context.LangOpts.isSwiftVersionAtLeast(5) && !useDC->getParentModule()->isResilient()) { // Swift 4.2 and earlier didn't check the Type when a TypeRepr was // present. However, this is a major hole when generic parameters are @@ -298,7 +298,7 @@ void AccessControlCheckerBase::checkTypeAccess( /// declaration if possible. /// /// Just flushes \p diag as is if \p complainRepr is null. -static void highlightOffendingType(TypeChecker &TC, InFlightDiagnostic &diag, +static void highlightOffendingType(InFlightDiagnostic &diag, const TypeRepr *complainRepr) { if (!complainRepr) { diag.flush(); @@ -310,15 +310,16 @@ static void highlightOffendingType(TypeChecker &TC, InFlightDiagnostic &diag, if (auto CITR = dyn_cast(complainRepr)) { const ValueDecl *VD = CITR->getBoundDecl(); - TC.diagnose(VD, diag::kind_declared_here, DescriptiveDeclKind::Type); + VD->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); } } void AccessControlCheckerBase::checkGenericParamAccess( - const GenericParamList *params, - const Decl *owner, + const GenericContext *ownerCtx, + const Decl *ownerDecl, AccessScope accessScope, AccessLevel contextAccess) { + auto params = ownerCtx->getGenericParams(); if (!params) return; @@ -348,7 +349,7 @@ void AccessControlCheckerBase::checkGenericParamAccess( } }; - auto *DC = owner->getDeclContext(); + auto *DC = ownerDecl->getDeclContext(); for (auto param : *params) { if (param->getInherited().empty()) @@ -361,63 +362,61 @@ void AccessControlCheckerBase::checkGenericParamAccess( callbackACEK = ACEK::Requirement; checkRequirementAccess(WhereClauseOwner( - owner->getInnermostDeclContext(), - const_cast(params)), + const_cast(ownerCtx)), accessScope, DC, callback); if (minAccessScope.isPublic()) return; // FIXME: Promote these to an error in the next -swift-version break. - if (isa(owner) || isa(owner)) + if (isa(ownerDecl) || isa(ownerDecl)) downgradeToWarning = DowngradeToWarning::Yes; + auto &Context = ownerDecl->getASTContext(); if (checkUsableFromInline) { - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!Context.isSwiftVersionAtLeast(5)) downgradeToWarning = DowngradeToWarning::Yes; auto diagID = diag::generic_param_usable_from_inline; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::generic_param_usable_from_inline_warn; - auto diag = TC.diagnose(owner, - diagID, - owner->getDescriptiveKind(), - accessControlErrorKind == ACEK::Requirement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = + Context.Diags.diagnose(ownerDecl, diagID, ownerDecl->getDescriptiveKind(), + accessControlErrorKind == ACEK::Requirement); + highlightOffendingType(diag, complainRepr); return; } auto minAccess = minAccessScope.accessLevelForDiagnostics(); bool isExplicit = - owner->getAttrs().hasAttribute() || - isa(owner->getDeclContext()); + ownerDecl->getAttrs().hasAttribute() || + isa(DC); auto diagID = diag::generic_param_access; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::generic_param_access_warn; - auto diag = TC.diagnose(owner, diagID, - owner->getDescriptiveKind(), isExplicit, - contextAccess, minAccess, - isa(owner->getDeclContext()), - accessControlErrorKind == ACEK::Requirement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = Context.Diags.diagnose( + ownerDecl, diagID, ownerDecl->getDescriptiveKind(), isExplicit, + contextAccess, minAccess, isa(DC), + accessControlErrorKind == ACEK::Requirement); + highlightOffendingType(diag, complainRepr); } void AccessControlCheckerBase::checkGenericParamAccess( - const GenericParamList *params, - const ValueDecl *owner) { - checkGenericParamAccess(params, owner, - owner->getFormalAccessScope(nullptr, - checkUsableFromInline), - owner->getFormalAccess()); + const GenericContext *ownerCtx, + const ValueDecl *ownerDecl) { + checkGenericParamAccess(ownerCtx, ownerDecl, + ownerDecl->getFormalAccessScope( + nullptr, checkUsableFromInline), + ownerDecl->getFormalAccess()); } namespace { class AccessControlChecker : public AccessControlCheckerBase, public DeclVisitor { public: - explicit AccessControlChecker(TypeChecker &TC) - : AccessControlCheckerBase(TC, /*checkUsableFromInline=*/false) {} + AccessControlChecker() + : AccessControlCheckerBase(/*checkUsableFromInline=*/false) {} void visit(Decl *D) { if (D->isInvalid() || D->isImplicit()) @@ -477,14 +476,11 @@ class AccessControlChecker : public AccessControlCheckerBase, auto diagID = diag::pattern_type_access_inferred; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::pattern_type_access_inferred_warn; - auto diag = TC.diagnose(NP->getLoc(), diagID, - theVar->isLet(), - isTypeContext, - isExplicit, - theVarAccess, + auto &DE = theVar->getASTContext().Diags; + auto diag = DE.diagnose(NP->getLoc(), diagID, theVar->isLet(), + isTypeContext, isExplicit, theVarAccess, isa(theVar->getDeclContext()), - typeAccess, - theVar->getInterfaceType()); + typeAccess, theVar->getInterfaceType()); }); } @@ -511,14 +507,11 @@ class AccessControlChecker : public AccessControlCheckerBase, auto anyVarAccess = isExplicit ? anyVar->getFormalAccess() : typeAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(TP->getLoc(), diagID, - anyVar->isLet(), - isTypeContext, - isExplicit, - anyVarAccess, - isa(anyVar->getDeclContext()), - typeAccess); - highlightOffendingType(TC, diag, complainRepr); + auto &DE = anyVar->getASTContext().Diags; + auto diag = DE.diagnose( + TP->getLoc(), diagID, anyVar->isLet(), isTypeContext, isExplicit, + anyVarAccess, isa(anyVar->getDeclContext()), typeAccess); + highlightOffendingType(diag, complainRepr); }); // Check the property wrapper types. @@ -542,7 +535,7 @@ class AccessControlChecker : public AccessControlCheckerBase, anyVarAccess, isa(anyVar->getDeclContext()), typeAccess); - highlightOffendingType(TC, diag, complainRepr); + highlightOffendingType(diag, complainRepr); }); } } @@ -551,8 +544,8 @@ class AccessControlChecker : public AccessControlCheckerBase, bool isTypeContext = PBD->getDeclContext()->isTypeContext(); llvm::DenseSet seenVars; - for (auto entry : PBD->getPatternList()) { - entry.getPattern()->forEachNode([&](const Pattern *P) { + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { if (auto *NP = dyn_cast(P)) { // Only check individual variables if we didn't check an enclosing // TypedPattern. @@ -570,7 +563,7 @@ class AccessControlChecker : public AccessControlCheckerBase, } void visitTypeAliasDecl(TypeAliasDecl *TAD) { - checkGenericParamAccess(TAD->getGenericParams(), TAD); + checkGenericParamAccess(TAD, TAD); checkTypeAccess(TAD->getUnderlyingType(), TAD->getUnderlyingTypeRepr(), TAD, /*mayBeInferred*/false, @@ -587,10 +580,9 @@ class AccessControlChecker : public AccessControlCheckerBase, auto aliasAccess = isExplicit ? TAD->getFormalAccess() : typeAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(TAD, diagID, - isExplicit, aliasAccess, - typeAccess, isa(TAD->getDeclContext())); - highlightOffendingType(TC, diag, complainRepr); + auto diag = TAD->diagnose(diagID, isExplicit, aliasAccess, typeAccess, + isa(TAD->getDeclContext())); + highlightOffendingType(diag, complainRepr); }); } @@ -658,7 +650,7 @@ class AccessControlChecker : public AccessControlCheckerBase, // Swift versions before 5.0 did not check requirements on the // protocol's where clause, so emit a warning. - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) downgradeToWarning = DowngradeToWarning::Yes; } }); @@ -668,15 +660,14 @@ class AccessControlChecker : public AccessControlCheckerBase, auto diagID = diag::associated_type_access; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::associated_type_access_warn; - auto diag = TC.diagnose(assocType, diagID, - assocType->getFormalAccess(), - minAccess, accessControlErrorKind); - highlightOffendingType(TC, diag, complainRepr); + auto diag = assocType->diagnose(diagID, assocType->getFormalAccess(), + minAccess, accessControlErrorKind); + highlightOffendingType(diag, complainRepr); } } void visitEnumDecl(EnumDecl *ED) { - checkGenericParamAccess(ED->getGenericParams(), ED); + checkGenericParamAccess(ED, ED); if (ED->hasRawType()) { Type rawType = ED->getRawType(); @@ -702,20 +693,19 @@ class AccessControlChecker : public AccessControlCheckerBase, auto enumDeclAccess = isExplicit ? ED->getFormalAccess() : typeAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(ED, diagID, isExplicit, - enumDeclAccess, typeAccess, - isa(ED->getDeclContext())); - highlightOffendingType(TC, diag, complainRepr); + auto diag = ED->diagnose(diagID, isExplicit, enumDeclAccess, typeAccess, + isa(ED->getDeclContext())); + highlightOffendingType(diag, complainRepr); }); } } void visitStructDecl(StructDecl *SD) { - checkGenericParamAccess(SD->getGenericParams(), SD); + checkGenericParamAccess(SD, SD); } void visitClassDecl(ClassDecl *CD) { - checkGenericParamAccess(CD->getGenericParams(), CD); + checkGenericParamAccess(CD, CD); if (const NominalTypeDecl *superclassDecl = CD->getSuperclassDecl()) { // Be slightly defensive here in the presence of badly-ordered @@ -739,7 +729,7 @@ class AccessControlChecker : public AccessControlCheckerBase, auto outerDowngradeToWarning = DowngradeToWarning::No; if (superclassDecl->isGenericContext() && - !TC.getLangOpts().isSwiftVersionAtLeast(5)) { + !CD->getASTContext().isSwiftVersionAtLeast(5)) { // Swift 4 failed to properly check this if the superclass was generic, // because the above loop was too strict. outerDowngradeToWarning = DowngradeToWarning::Yes; @@ -760,11 +750,11 @@ class AccessControlChecker : public AccessControlCheckerBase, ? CD->getFormalAccess() : typeAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(CD, diagID, isExplicit, classDeclAccess, - typeAccess, - isa(CD->getDeclContext()), - superclassLocIter->getTypeRepr() != complainRepr); - highlightOffendingType(TC, diag, complainRepr); + auto diag = + CD->diagnose(diagID, isExplicit, classDeclAccess, typeAccess, + isa(CD->getDeclContext()), + superclassLocIter->getTypeRepr() != complainRepr); + highlightOffendingType(diag, complainRepr); }); } } @@ -834,7 +824,7 @@ class AccessControlChecker : public AccessControlCheckerBase, declKind = declKindForType(type); // Swift versions before 5.0 did not check requirements on the // protocol's where clause, so emit a warning. - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!proto->getASTContext().isSwiftVersionAtLeast(5)) downgradeToWarning = DowngradeToWarning::Yes; } }); @@ -849,15 +839,15 @@ class AccessControlChecker : public AccessControlCheckerBase, auto diagID = diag::protocol_access; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::protocol_access_warn; - auto diag = TC.diagnose(proto, diagID, isExplicit, protoAccess, - protocolControlErrorKind, minAccess, - isa(proto->getDeclContext()), declKind); - highlightOffendingType(TC, diag, complainRepr); + auto diag = proto->diagnose( + diagID, isExplicit, protoAccess, protocolControlErrorKind, minAccess, + isa(proto->getDeclContext()), declKind); + highlightOffendingType(diag, complainRepr); } } void visitSubscriptDecl(SubscriptDecl *SD) { - checkGenericParamAccess(SD->getGenericParams(), SD); + checkGenericParamAccess(SD, SD); auto minAccessScope = AccessScope::getPublic(); const TypeRepr *complainRepr = nullptr; @@ -904,19 +894,16 @@ class AccessControlChecker : public AccessControlCheckerBase, auto subscriptDeclAccess = isExplicit ? SD->getFormalAccess() : minAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(SD, diagID, - isExplicit, - subscriptDeclAccess, - minAccess, - problemIsElement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = SD->diagnose(diagID, isExplicit, subscriptDeclAccess, + minAccess, problemIsElement); + highlightOffendingType(diag, complainRepr); } } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { bool isTypeContext = fn->getDeclContext()->isTypeContext(); - checkGenericParamAccess(fn->getGenericParams(), fn); + checkGenericParamAccess(fn, fn); // This must stay in sync with diag::function_type_access. enum { @@ -975,14 +962,10 @@ class AccessControlChecker : public AccessControlCheckerBase, auto fnAccess = isExplicit ? fn->getFormalAccess() : minAccessScope.requiredAccessForDiagnostics(); - auto diag = TC.diagnose(fn, diagID, - isExplicit, - fnAccess, - isa(fn->getDeclContext()), - minAccess, - functionKind, - problemIsResult); - highlightOffendingType(TC, diag, complainRepr); + auto diag = fn->diagnose(diagID, isExplicit, fnAccess, + isa(fn->getDeclContext()), minAccess, + functionKind, problemIsResult); + highlightOffendingType(diag, complainRepr); } } @@ -999,8 +982,8 @@ class AccessControlChecker : public AccessControlCheckerBase, if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::enum_case_access_warn; auto diag = - TC.diagnose(EED, diagID, EED->getFormalAccess(), typeAccess); - highlightOffendingType(TC, diag, complainRepr); + EED->diagnose(diagID, EED->getFormalAccess(), typeAccess); + highlightOffendingType(diag, complainRepr); }); } } @@ -1009,8 +992,8 @@ class AccessControlChecker : public AccessControlCheckerBase, class UsableFromInlineChecker : public AccessControlCheckerBase, public DeclVisitor { public: - explicit UsableFromInlineChecker(TypeChecker &TC) - : AccessControlCheckerBase(TC, /*checkUsableFromInline=*/true) {} + UsableFromInlineChecker() + : AccessControlCheckerBase(/*checkUsableFromInline=*/true) {} static bool shouldSkipChecking(const ValueDecl *VD) { if (VD->getFormalAccess() != AccessLevel::Internal) @@ -1019,7 +1002,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, }; void visit(Decl *D) { - if (!TC.Context.isSwiftVersionAtLeast(4, 2)) + if (!D->getASTContext().isSwiftVersionAtLeast(4, 2)) return; if (D->isInvalid() || D->isImplicit()) @@ -1093,23 +1076,22 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, if (seenVars.count(theVar) || theVar->isInvalid()) return; - checkTypeAccess(theVar->getInterfaceType(), nullptr, - fixedLayoutStructContext ? fixedLayoutStructContext - : theVar, - /*mayBeInferred*/false, - [&](AccessScope typeAccessScope, - const TypeRepr *complainRepr, - DowngradeToWarning downgradeToWarning) { - auto diagID = diag::pattern_type_not_usable_from_inline_inferred; - if (fixedLayoutStructContext) { - diagID = - diag::pattern_type_not_usable_from_inline_inferred_frozen; - } else if (!TC.Context.isSwiftVersionAtLeast(5)) { - diagID = diag::pattern_type_not_usable_from_inline_inferred_warn; - } - TC.diagnose(NP->getLoc(), diagID, theVar->isLet(), isTypeContext, - theVar->getInterfaceType()); - }); + checkTypeAccess( + theVar->getInterfaceType(), nullptr, + fixedLayoutStructContext ? fixedLayoutStructContext : theVar, + /*mayBeInferred*/ false, + [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, + DowngradeToWarning downgradeToWarning) { + auto &Ctx = theVar->getASTContext(); + auto diagID = diag::pattern_type_not_usable_from_inline_inferred; + if (fixedLayoutStructContext) { + diagID = diag::pattern_type_not_usable_from_inline_inferred_frozen; + } else if (!Ctx.isSwiftVersionAtLeast(5)) { + diagID = diag::pattern_type_not_usable_from_inline_inferred_warn; + } + Ctx.Diags.diagnose(NP->getLoc(), diagID, theVar->isLet(), + isTypeContext, theVar->getInterfaceType()); + }); } /// \see visitPatternBindingDecl @@ -1130,22 +1112,22 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, if (!fixedLayoutStructContext && shouldSkipChecking(anyVar)) return; - checkTypeAccess(TP->getTypeLoc(), - fixedLayoutStructContext ? fixedLayoutStructContext - : anyVar, - /*mayBeInferred*/true, - [&](AccessScope typeAccessScope, - const TypeRepr *complainRepr, - DowngradeToWarning downgradeToWarning) { - auto diagID = diag::pattern_type_not_usable_from_inline; - if (fixedLayoutStructContext) - diagID = diag::pattern_type_not_usable_from_inline_frozen; - else if (!TC.Context.isSwiftVersionAtLeast(5)) - diagID = diag::pattern_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(TP->getLoc(), diagID, anyVar->isLet(), - isTypeContext); - highlightOffendingType(TC, diag, complainRepr); - }); + checkTypeAccess( + TP->getTypeLoc(), + fixedLayoutStructContext ? fixedLayoutStructContext : anyVar, + /*mayBeInferred*/ true, + [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, + DowngradeToWarning downgradeToWarning) { + auto &Ctx = anyVar->getASTContext(); + auto diagID = diag::pattern_type_not_usable_from_inline; + if (fixedLayoutStructContext) + diagID = diag::pattern_type_not_usable_from_inline_frozen; + else if (!Ctx.isSwiftVersionAtLeast(5)) + diagID = diag::pattern_type_not_usable_from_inline_warn; + auto diag = Ctx.Diags.diagnose(TP->getLoc(), diagID, anyVar->isLet(), + isTypeContext); + highlightOffendingType(diag, complainRepr); + }); for (auto attr : anyVar->getAttachedPropertyWrappers()) { checkTypeAccess(attr->getTypeLoc(), @@ -1158,7 +1140,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, auto diag = anyVar->diagnose( diag::property_wrapper_type_not_usable_from_inline, anyVar->isLet(), isTypeContext); - highlightOffendingType(TC, diag, complainRepr); + highlightOffendingType(diag, complainRepr); }); } } @@ -1174,8 +1156,8 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, getFixedLayoutStructContext(PBD); llvm::DenseSet seenVars; - for (auto entry : PBD->getPatternList()) { - entry.getPattern()->forEachNode([&](const Pattern *P) { + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { if (auto *NP = dyn_cast(P)) { checkNamedPattern(NP, fixedLayoutStructContext, isTypeContext, seenVars); @@ -1193,7 +1175,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, } void visitTypeAliasDecl(TypeAliasDecl *TAD) { - checkGenericParamAccess(TAD->getGenericParams(), TAD); + checkGenericParamAccess(TAD, TAD); checkTypeAccess(TAD->getUnderlyingType(), TAD->getUnderlyingTypeRepr(), TAD, /*mayBeInferred*/false, @@ -1201,10 +1183,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeToWarning) { auto diagID = diag::type_alias_underlying_type_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!TAD->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::type_alias_underlying_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(TAD, diagID); - highlightOffendingType(TC, diag, complainRepr); + auto diag = TAD->diagnose(diagID); + highlightOffendingType(diag, complainRepr); }); } @@ -1223,10 +1205,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::associated_type_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::associated_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(assocType, diagID, ACEK_Requirement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = assocType->diagnose(diagID, ACEK_Requirement); + highlightOffendingType(diag, complainRepr); }); }); checkTypeAccess(assocType->getDefaultDefinitionType(), @@ -1236,10 +1218,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::associated_type_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::associated_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(assocType, diagID, ACEK_DefaultDefinition); - highlightOffendingType(TC, diag, complainRepr); + auto diag = assocType->diagnose(diagID, ACEK_DefaultDefinition); + highlightOffendingType(diag, complainRepr); }); if (assocType->getTrailingWhereClause()) { @@ -1252,16 +1234,16 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::associated_type_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::associated_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(assocType, diagID, ACEK_Requirement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = assocType->diagnose(diagID, ACEK_Requirement); + highlightOffendingType(diag, complainRepr); }); } } void visitEnumDecl(const EnumDecl *ED) { - checkGenericParamAccess(ED->getGenericParams(), ED); + checkGenericParamAccess(ED, ED); if (ED->hasRawType()) { Type rawType = ED->getRawType(); @@ -1280,20 +1262,20 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeToWarning) { auto diagID = diag::enum_raw_type_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!ED->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::enum_raw_type_not_usable_from_inline_warn; - auto diag = TC.diagnose(ED, diagID); - highlightOffendingType(TC, diag, complainRepr); + auto diag = ED->diagnose(diagID); + highlightOffendingType(diag, complainRepr); }); } } void visitStructDecl(StructDecl *SD) { - checkGenericParamAccess(SD->getGenericParams(), SD); + checkGenericParamAccess(SD, SD); } void visitClassDecl(ClassDecl *CD) { - checkGenericParamAccess(CD->getGenericParams(), CD); + checkGenericParamAccess(CD, CD); if (CD->hasSuperclass()) { const NominalTypeDecl *superclassDecl = CD->getSuperclassDecl(); @@ -1322,11 +1304,11 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeToWarning) { auto diagID = diag::class_super_not_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!CD->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::class_super_not_usable_from_inline_warn; - auto diag = TC.diagnose(CD, diagID, - superclassLocIter->getTypeRepr() != complainRepr); - highlightOffendingType(TC, diag, complainRepr); + auto diag = CD->diagnose(diagID, superclassLocIter->getTypeRepr() != + complainRepr); + highlightOffendingType(diag, complainRepr); }); } } @@ -1346,10 +1328,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::protocol_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!proto->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::protocol_usable_from_inline_warn; - auto diag = TC.diagnose(proto, diagID, PCEK_Refine); - highlightOffendingType(TC, diag, complainRepr); + auto diag = proto->diagnose(diagID, PCEK_Refine); + highlightOffendingType(diag, complainRepr); }); }); @@ -1363,16 +1345,16 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::protocol_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!proto->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::protocol_usable_from_inline_warn; - auto diag = TC.diagnose(proto, diagID, PCEK_Requirement); - highlightOffendingType(TC, diag, complainRepr); + auto diag = proto->diagnose(diagID, PCEK_Requirement); + highlightOffendingType(diag, complainRepr); }); } } void visitSubscriptDecl(SubscriptDecl *SD) { - checkGenericParamAccess(SD->getGenericParams(), SD); + checkGenericParamAccess(SD, SD); for (auto &P : *SD->getIndices()) { checkTypeAccess( @@ -1380,11 +1362,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::subscript_type_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!SD->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::subscript_type_usable_from_inline_warn; - auto diag = TC.diagnose(SD, diagID, - /*problemIsElement=*/false); - highlightOffendingType(TC, diag, complainRepr); + auto diag = SD->diagnose(diagID, /*problemIsElement=*/false); + highlightOffendingType(diag, complainRepr); }); } @@ -1393,18 +1374,17 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::subscript_type_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!SD->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::subscript_type_usable_from_inline_warn; - auto diag = TC.diagnose(SD, diagID, - /*problemIsElement=*/true); - highlightOffendingType(TC, diag, complainRepr); + auto diag = SD->diagnose(diagID, /*problemIsElement=*/true); + highlightOffendingType(diag, complainRepr); }); } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { bool isTypeContext = fn->getDeclContext()->isTypeContext(); - checkGenericParamAccess(fn->getGenericParams(), fn); + checkGenericParamAccess(fn, fn); // This must stay in sync with diag::function_type_usable_from_inline. enum { @@ -1423,11 +1403,11 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::function_type_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!fn->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::function_type_usable_from_inline_warn; - auto diag = TC.diagnose(fn, diagID, functionKind, - /*problemIsResult=*/false); - highlightOffendingType(TC, diag, complainRepr); + auto diag = fn->diagnose(diagID, functionKind, + /*problemIsResult=*/false); + highlightOffendingType(diag, complainRepr); }); } @@ -1437,11 +1417,11 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, const TypeRepr *complainRepr, DowngradeToWarning downgradeDiag) { auto diagID = diag::function_type_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!fn->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::function_type_usable_from_inline_warn; - auto diag = TC.diagnose(fn, diagID, functionKind, - /*problemIsResult=*/true); - highlightOffendingType(TC, diag, complainRepr); + auto diag = fn->diagnose(diagID, functionKind, + /*problemIsResult=*/true); + highlightOffendingType(diag, complainRepr); }); } } @@ -1455,10 +1435,10 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, DowngradeToWarning downgradeToWarning) { auto diagID = diag::enum_case_usable_from_inline; - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!EED->getASTContext().isSwiftVersionAtLeast(5)) diagID = diag::enum_case_usable_from_inline_warn; - auto diag = TC.diagnose(EED, diagID); - highlightOffendingType(TC, diag, complainRepr); + auto diag = EED->diagnose(diagID); + highlightOffendingType(diag, complainRepr); }); } } @@ -1470,8 +1450,6 @@ class ExportabilityChecker : public DeclVisitor { using CheckExportabilityConformanceCallback = llvm::function_ref; - TypeChecker &TC; - void checkTypeImpl( Type type, const TypeRepr *typeRepr, const SourceFile &SF, CheckExportabilityTypeCallback diagnoseType, @@ -1588,8 +1566,9 @@ class ExportabilityChecker : public DeclVisitor { diagnoseConformance); } - void checkGenericParams(const GenericParamList *params, - const Decl *owner) { + void checkGenericParams(const GenericContext *ownerCtx, + const ValueDecl *ownerDecl) { + const auto params = ownerCtx->getGenericParams(); if (!params) return; @@ -1597,16 +1576,16 @@ class ExportabilityChecker : public DeclVisitor { if (param->getInherited().empty()) continue; assert(param->getInherited().size() == 1); - checkType(param->getInherited().front(), owner, - getDiagnoseCallback(owner), getDiagnoseCallback(owner)); + checkType(param->getInherited().front(), ownerDecl, + getDiagnoseCallback(ownerDecl), + getDiagnoseCallback(ownerDecl)); } forAllRequirementTypes(WhereClauseOwner( - owner->getInnermostDeclContext(), - const_cast(params)), + const_cast(ownerCtx)), [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, owner, getDiagnoseCallback(owner), - getDiagnoseCallback(owner)); + checkType(type, typeRepr, ownerDecl, getDiagnoseCallback(ownerDecl), + getDiagnoseCallback(ownerDecl)); }); } @@ -1620,26 +1599,24 @@ class ExportabilityChecker : public DeclVisitor { }; class DiagnoseGenerically { - TypeChecker &TC; const Decl *D; Reason reason; public: - DiagnoseGenerically(TypeChecker &TC, const Decl *D, Reason reason) - : TC(TC), D(D), reason(reason) {} + DiagnoseGenerically(const Decl *D, Reason reason) : D(D), reason(reason) {} void operator()(const TypeDecl *offendingType, const TypeRepr *complainRepr) { ModuleDecl *M = offendingType->getModuleContext(); - auto diag = TC.diagnose(D, diag::decl_from_implementation_only_module, + auto diag = D->diagnose(diag::decl_from_implementation_only_module, offendingType->getDescriptiveKind(), offendingType->getFullName(), static_cast(reason), M->getName()); - highlightOffendingType(TC, diag, complainRepr); + highlightOffendingType(diag, complainRepr); } void operator()(const ProtocolConformance *offendingConformance) { ModuleDecl *M = offendingConformance->getDeclContext()->getParentModule(); - TC.diagnose(D, diag::conformance_from_implementation_only_module, + D->diagnose(diag::conformance_from_implementation_only_module, offendingConformance->getType(), offendingConformance->getProtocol()->getFullName(), static_cast(reason), M->getName()); @@ -1657,11 +1634,11 @@ class ExportabilityChecker : public DeclVisitor { DiagnoseGenerically getDiagnoseCallback(const Decl *D, Reason reason = Reason::General) { - return DiagnoseGenerically(TC, D, reason); + return DiagnoseGenerically(D, reason); } public: - explicit ExportabilityChecker(TypeChecker &TC) : TC(TC) {} + ExportabilityChecker() {} static bool shouldSkipChecking(const ValueDecl *VD) { if (VD->getAttrs().hasAttribute()) @@ -1706,20 +1683,20 @@ class ExportabilityChecker : public DeclVisitor { ModuleDecl *M = overridden->getModuleContext(); if (SF->isImportedImplementationOnly(M)) { - TC.diagnose(VD, diag::implementation_only_override_import_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - TC.diagnose(overridden, diag::overridden_here); + VD->diagnose(diag::implementation_only_override_import_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); return; } if (overridden->getAttrs().hasAttribute()) { - TC.diagnose(VD, diag::implementation_only_override_without_attr, - overridden->getDescriptiveKind()) - .fixItInsert(VD->getAttributeInsertionLoc(false), - "@_implementationOnly "); - TC.diagnose(overridden, diag::overridden_here); + VD->diagnose(diag::implementation_only_override_without_attr, + overridden->getDescriptiveKind()) + .fixItInsert(VD->getAttributeInsertionLoc(false), + "@_implementationOnly "); + overridden->diagnose(diag::overridden_here); return; } @@ -1816,8 +1793,8 @@ class ExportabilityChecker : public DeclVisitor { void visitPatternBindingDecl(PatternBindingDecl *PBD) { llvm::DenseSet seenVars; - for (auto entry : PBD->getPatternList()) { - entry.getPattern()->forEachNode([&](const Pattern *P) { + for (auto idx : range(PBD->getNumPatternEntries())) { + PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { if (auto *NP = dyn_cast(P)) { checkNamedPattern(NP, seenVars); return; @@ -1833,7 +1810,7 @@ class ExportabilityChecker : public DeclVisitor { } void visitTypeAliasDecl(TypeAliasDecl *TAD) { - checkGenericParams(TAD->getGenericParams(), TAD); + checkGenericParams(TAD, TAD); checkType(TAD->getUnderlyingType(), TAD->getUnderlyingTypeRepr(), TAD, getDiagnoseCallback(TAD), getDiagnoseCallback(TAD)); @@ -1859,7 +1836,7 @@ class ExportabilityChecker : public DeclVisitor { } void visitNominalTypeDecl(const NominalTypeDecl *nominal) { - checkGenericParams(nominal->getGenericParams(), nominal); + checkGenericParams(nominal, nominal); llvm::for_each(nominal->getInherited(), [&](TypeLoc nextInherited) { @@ -1884,7 +1861,7 @@ class ExportabilityChecker : public DeclVisitor { } void visitSubscriptDecl(SubscriptDecl *SD) { - checkGenericParams(SD->getGenericParams(), SD); + checkGenericParams(SD, SD); for (auto &P : *SD->getIndices()) { checkType(P->getInterfaceType(), P->getTypeRepr(), SD, @@ -1895,7 +1872,7 @@ class ExportabilityChecker : public DeclVisitor { } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { - checkGenericParams(fn->getGenericParams(), fn); + checkGenericParams(fn, fn); for (auto *P : *fn->getParameters()) checkType(P->getInterfaceType(), P->getTypeRepr(), fn, @@ -1970,14 +1947,15 @@ class ExportabilityChecker : public DeclVisitor { if (!SF->isImportedImplementationOnly(M)) return; - auto diag = TC.diagnose(diagLoc, diag::decl_from_implementation_only_module, - PGD->getDescriptiveKind(), PGD->getName(), - static_cast(Reason::General), - M->getName()); + auto &DE = PGD->getASTContext().Diags; + auto diag = + DE.diagnose(diagLoc, diag::decl_from_implementation_only_module, + PGD->getDescriptiveKind(), PGD->getName(), + static_cast(Reason::General), M->getName()); if (refRange.isValid()) diag.highlight(refRange); diag.flush(); - TC.diagnose(PGD, diag::decl_declared_here, PGD->getName()); + PGD->diagnose(diag::decl_declared_here, PGD->getName()); } void visitInfixOperatorDecl(InfixOperatorDecl *IOD) { @@ -2006,8 +1984,7 @@ class ExportabilityChecker : public DeclVisitor { }; } // end anonymous namespace -static void checkExtensionGenericParamAccess(TypeChecker &TC, - const ExtensionDecl *ED) { +static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { auto *AA = ED->getAttrs().getAttribute(); if (!AA) return; @@ -2034,18 +2011,17 @@ static void checkExtensionGenericParamAccess(TypeChecker &TC, break; } - AccessControlChecker(TC).checkGenericParamAccess(ED->getGenericParams(), ED, - desiredAccessScope, - userSpecifiedAccess); + AccessControlChecker().checkGenericParamAccess( + ED, ED, desiredAccessScope, userSpecifiedAccess); } -void swift::checkAccessControl(TypeChecker &TC, Decl *D) { +void swift::checkAccessControl(Decl *D) { if (isa(D) || isa(D)) { - AccessControlChecker(TC).visit(D); - UsableFromInlineChecker(TC).visit(D); + AccessControlChecker().visit(D); + UsableFromInlineChecker().visit(D); } else if (auto *ED = dyn_cast(D)) { - checkExtensionGenericParamAccess(TC, ED); + checkExtensionGenericParamAccess(ED); } - ExportabilityChecker(TC).visit(D); + ExportabilityChecker().visit(D); } diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index 0c8bb4b870062..08ebec0d403a7 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -27,7 +27,7 @@ class TypeChecker; /// At a high level, this checks the given declaration's signature does not /// reference any other declarations that are less visible than the declaration /// itself. Related checks may also be performed. -void checkAccessControl(TypeChecker &TC, Decl *D); +void checkAccessControl(Decl *D); } // end namespace swift diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index c804d5f61f8b1..fcb52f55403bb 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -41,8 +41,8 @@ using namespace swift; namespace { /// This emits a diagnostic with a fixit to remove the attribute. template - void diagnoseAndRemoveAttr(TypeChecker &TC, Decl *D, DeclAttribute *attr, - ArgTypes &&...Args) { + void diagnoseAndRemoveAttr(DiagnosticEngine &Diags, Decl *D, + DeclAttribute *attr, ArgTypes &&...Args) { assert(!D->hasClangNode() && "Clang importer propagated a bogus attribute"); if (!D->hasClangNode()) { SourceLoc loc = attr->getLocation(); @@ -51,7 +51,7 @@ namespace { loc = D->getLoc(); } if (loc.isValid()) { - TC.diagnose(loc, std::forward(Args)...) + Diags.diagnose(loc, std::forward(Args)...) .fixItRemove(attr->getRangeWithAt()); } } @@ -62,16 +62,22 @@ namespace { /// This visits each attribute on a decl. The visitor should return true if /// the attribute is invalid and should be marked as such. class AttributeChecker : public AttributeVisitor { - TypeChecker &TC; + ASTContext &Ctx; Decl *D; public: - AttributeChecker(TypeChecker &TC, Decl *D) : TC(TC), D(D) {} + AttributeChecker(Decl *D) : Ctx(D->getASTContext()), D(D) {} /// This emits a diagnostic with a fixit to remove the attribute. template void diagnoseAndRemoveAttr(DeclAttribute *attr, ArgTypes &&...Args) { - ::diagnoseAndRemoveAttr(TC, D, attr, std::forward(Args)...); + ::diagnoseAndRemoveAttr(Ctx.Diags, D, attr, + std::forward(Args)...); + } + + template + InFlightDiagnostic diagnose(ArgTypes &&... Args) const { + return Ctx.Diags.diagnose(std::forward(Args)...); } /// Deleting this ensures that all attributes are covered by the visitor @@ -87,6 +93,8 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(Exported) IGNORED_ATTR(ForbidSerializingReference) IGNORED_ATTR(HasStorage) + IGNORED_ATTR(HasMissingDesignatedInitializers) + IGNORED_ATTR(InheritsConvenienceInitializers) IGNORED_ATTR(Inline) IGNORED_ATTR(ObjCBridged) IGNORED_ATTR(ObjCNonLazyRealization) @@ -105,13 +113,17 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(DisfavoredOverload) IGNORED_ATTR(ProjectedValueProperty) IGNORED_ATTR(ReferenceOwnership) + IGNORED_ATTR(OriginallyDefinedIn) + // TODO(TF-830): Upstream `@transpose` attribute type-checking from tensorflow + // branch. + IGNORED_ATTR(Transpose) #undef IGNORED_ATTR void visitAlignmentAttr(AlignmentAttr *attr) { // Alignment must be a power of two. auto value = attr->getValue(); if (value == 0 || (value & (value - 1)) != 0) - TC.diagnose(attr->getLocation(), diag::alignment_not_power_of_two); + diagnose(attr->getLocation(), diag::alignment_not_power_of_two); } void visitBorrowedAttr(BorrowedAttr *attr) { @@ -121,8 +133,8 @@ class AttributeChecker : public AttributeVisitor { assert(!D->hasClangNode() && "@_borrowed on imported declaration?"); if (D->getAttrs().hasAttribute()) { - TC.diagnose(attr->getLocation(), diag::borrowed_with_objc_dynamic, - D->getDescriptiveKind()) + diagnose(attr->getLocation(), diag::borrowed_with_objc_dynamic, + D->getDescriptiveKind()) .fixItRemove(attr->getRange()); D->getAttrs().removeAttribute(attr); return; @@ -131,9 +143,8 @@ class AttributeChecker : public AttributeVisitor { auto dc = D->getDeclContext(); auto protoDecl = dyn_cast(dc); if (protoDecl && protoDecl->isObjC()) { - TC.diagnose(attr->getLocation(), - diag::borrowed_on_objc_protocol_requirement, - D->getDescriptiveKind()) + diagnose(attr->getLocation(), diag::borrowed_on_objc_protocol_requirement, + D->getDescriptiveKind()) .fixItRemove(attr->getRange()); D->getAttrs().removeAttribute(attr); return; @@ -151,13 +162,12 @@ class AttributeChecker : public AttributeVisitor { if (auto caseDecl = dyn_cast(D)) { // An indirect case should have a payload. if (!caseDecl->hasAssociatedValues()) - TC.diagnose(attr->getLocation(), - diag::indirect_case_without_payload, caseDecl->getName()); + diagnose(attr->getLocation(), diag::indirect_case_without_payload, + caseDecl->getName()); // If the enum is already indirect, its cases don't need to be. else if (caseDecl->getParentEnum()->getAttrs() .hasAttribute()) - TC.diagnose(attr->getLocation(), - diag::indirect_case_in_indirect_enum); + diagnose(attr->getLocation(), diag::indirect_case_in_indirect_enum); } } @@ -237,16 +247,21 @@ class AttributeChecker : public AttributeVisitor { void visitFunctionBuilderAttr(FunctionBuilderAttr *attr); void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); + void visitNonEphemeralAttr(NonEphemeralAttr *attr); + void checkOriginalDefinedInAttrs(Decl *D, ArrayRef Attrs); + + void visitDifferentiableAttr(DifferentiableAttr *attr); + void visitDerivativeAttr(DerivativeAttr *attr); }; } // end anonymous namespace void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) { - DeclContext *Ctx = D->getDeclContext(); + DeclContext *dc = D->getDeclContext(); // Protocol declarations cannot be transparent. - if (isa(Ctx)) + if (isa(dc)) diagnoseAndRemoveAttr(attr, diag::transparent_in_protocols_not_supported); // Class declarations cannot be transparent. - if (isa(Ctx)) { + if (isa(dc)) { // @transparent is always ok on implicitly generated accessors: they can // be dispatched (even in classes) when the references are within the @@ -254,7 +269,7 @@ void AttributeChecker::visitTransparentAttr(TransparentAttr *attr) { if (!(isa(D) && D->isImplicit())) diagnoseAndRemoveAttr(attr, diag::transparent_in_classes_not_supported); } - + if (auto *VD = dyn_cast(D)) { // Stored properties and variables can't be transparent. if (VD->hasStorage()) @@ -321,7 +336,7 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) { } } } - + // Verify that we don't have a static function. if (FD->isStatic()) diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating); @@ -338,9 +353,9 @@ void AttributeChecker::visitDynamicAttr(DynamicAttr *attr) { } static bool -validateIBActionSignature(TypeChecker &TC, DeclAttribute *attr, const FuncDecl *FD, - unsigned minParameters, unsigned maxParameters, - bool hasVoidResult = true) { +validateIBActionSignature(ASTContext &ctx, DeclAttribute *attr, + const FuncDecl *FD, unsigned minParameters, + unsigned maxParameters, bool hasVoidResult = true) { bool valid = true; auto arity = FD->getParameters()->size(); @@ -352,13 +367,14 @@ validateIBActionSignature(TypeChecker &TC, DeclAttribute *attr, const FuncDecl * diagID = diag::invalid_ibaction_argument_count_exact; else if (minParameters == 0) diagID = diag::invalid_ibaction_argument_count_max; - TC.diagnose(FD, diagID, attr->getAttrName(), minParameters, maxParameters); + ctx.Diags.diagnose(FD, diagID, attr->getAttrName(), minParameters, + maxParameters); valid = false; } if (resultType->isVoid() != hasVoidResult) { - TC.diagnose(FD, diag::invalid_ibaction_result, attr->getAttrName(), - hasVoidResult); + ctx.Diags.diagnose(FD, diag::invalid_ibaction_result, attr->getAttrName(), + hasVoidResult); valid = false; } @@ -370,16 +386,16 @@ validateIBActionSignature(TypeChecker &TC, DeclAttribute *attr, const FuncDecl * return valid; } -static bool isiOS(TypeChecker &TC) { - return TC.getLangOpts().Target.isiOS(); +static bool isiOS(ASTContext &ctx) { + return ctx.LangOpts.Target.isiOS(); } -static bool iswatchOS(TypeChecker &TC) { - return TC.getLangOpts().Target.isWatchOS(); +static bool iswatchOS(ASTContext &ctx) { + return ctx.LangOpts.Target.isWatchOS(); } -static bool isRelaxedIBAction(TypeChecker &TC) { - return isiOS(TC) || iswatchOS(TC); +static bool isRelaxedIBAction(ASTContext &ctx) { + return isiOS(ctx) || iswatchOS(ctx); } void AttributeChecker::visitIBActionAttr(IBActionAttr *attr) { @@ -391,12 +407,12 @@ void AttributeChecker::visitIBActionAttr(IBActionAttr *attr) { return; } - if (isRelaxedIBAction(TC)) + if (isRelaxedIBAction(Ctx)) // iOS, tvOS, and watchOS allow 0-2 parameters to an @IBAction method. - validateIBActionSignature(TC, attr, FD, /*minParams=*/0, /*maxParams=*/2); + validateIBActionSignature(Ctx, attr, FD, /*minParams=*/0, /*maxParams=*/2); else // macOS allows 1 parameter to an @IBAction method. - validateIBActionSignature(TC, attr, FD, /*minParams=*/1, /*maxParams=*/1); + validateIBActionSignature(Ctx, attr, FD, /*minParams=*/1, /*maxParams=*/1); } void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { @@ -406,7 +422,7 @@ void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { diagnoseAndRemoveAttr(attr, diag::invalid_ibaction_decl, attr->getAttrName()); - if (!validateIBActionSignature(TC, attr, FD, + if (!validateIBActionSignature(Ctx, attr, FD, /*minParams=*/1, /*maxParams=*/3, /*hasVoidResult=*/false)) return; @@ -445,8 +461,8 @@ void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { } // Emit the actual error. - TC.diagnose(FD, diag::ibsegueaction_objc_method_family, - attr->getAttrName(), currentSelector); + diagnose(FD, diag::ibsegueaction_objc_method_family, attr->getAttrName(), + currentSelector); // The rest of this is just fix-it generation. @@ -455,7 +471,7 @@ void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { auto replacingPrefix = [&](Identifier oldName) -> Identifier { SmallString<32> scratch = prefix; scratch += oldName.str().drop_while(clang::isLowercase); - return TC.Context.getIdentifier(scratch); + return Ctx.getIdentifier(scratch); }; // Suggest changing the Swift name of the method, unless there is already an @@ -464,9 +480,9 @@ void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { !FD->getAttrs().getAttribute()->hasName()) { auto newSwiftBaseName = replacingPrefix(FD->getBaseName().getIdentifier()); auto argumentNames = FD->getFullName().getArgumentNames(); - DeclName newSwiftName(TC.Context, newSwiftBaseName, argumentNames); + DeclName newSwiftName(Ctx, newSwiftBaseName, argumentNames); - auto diag = TC.diagnose(FD, diag::fixit_rename_in_swift, newSwiftName); + auto diag = diagnose(FD, diag::fixit_rename_in_swift, newSwiftName); fixDeclarationName(diag, FD, newSwiftName); } @@ -474,9 +490,9 @@ void AttributeChecker::visitIBSegueActionAttr(IBSegueActionAttr *attr) { auto oldPieces = currentSelector.getSelectorPieces(); SmallVector newPieces(oldPieces.begin(), oldPieces.end()); newPieces[0] = replacingPrefix(newPieces[0]); - ObjCSelector newSelector(TC.Context, currentSelector.getNumArgs(), newPieces); + ObjCSelector newSelector(Ctx, currentSelector.getNumArgs(), newPieces); - auto diag = TC.diagnose(FD, diag::fixit_rename_in_objc, newSelector); + auto diag = diagnose(FD, diag::fixit_rename_in_objc, newSelector); fixDeclarationObjCName(diag, FD, currentSelector, newSelector); } @@ -506,7 +522,7 @@ void AttributeChecker::visitGKInspectableAttr(GKInspectableAttr *attr) { } static Optional> -isAcceptableOutletType(Type type, bool &isArray, TypeChecker &TC) { +isAcceptableOutletType(Type type, bool &isArray, ASTContext &ctx) { if (type->isObjCExistentialType() || type->isAny()) return None; // @objc existential types are okay @@ -518,13 +534,13 @@ isAcceptableOutletType(Type type, bool &isArray, TypeChecker &TC) { return diag::iboutlet_nonobjc_class; } - if (nominal == TC.Context.getStringDecl()) { + if (nominal == ctx.getStringDecl()) { // String is okay because it is bridged to NSString. // FIXME: BridgesTypes.def is almost sufficient for this. return None; } - if (nominal == TC.Context.getArrayDecl()) { + if (nominal == ctx.getArrayDecl()) { // Arrays of arrays are not allowed. if (isArray) return diag::iboutlet_nonobject_type; @@ -536,12 +552,12 @@ isAcceptableOutletType(Type type, bool &isArray, TypeChecker &TC) { auto boundArgs = boundTy->getGenericArgs(); assert(boundArgs.size() == 1 && "invalid Array declaration"); Type elementTy = boundArgs.front(); - return isAcceptableOutletType(elementTy, isArray, TC); + return isAcceptableOutletType(elementTy, isArray, ctx); } if (type->isExistentialType()) return diag::iboutlet_nonobjc_protocol; - + // No other types are permitted. return diag::iboutlet_nonobject_type; } @@ -576,17 +592,17 @@ void AttributeChecker::visitIBOutletAttr(IBOutletAttr *attr) { } bool isArray = false; - if (auto isError = isAcceptableOutletType(type, isArray, TC)) + if (auto isError = isAcceptableOutletType(type, isArray, Ctx)) diagnoseAndRemoveAttr(attr, isError.getValue(), /*array=*/isArray, type); // If the type wasn't optional, an array, or unowned, complain. if (!wasOptional && !isArray) { - TC.diagnose(attr->getLocation(), diag::iboutlet_non_optional, type); + diagnose(attr->getLocation(), diag::iboutlet_non_optional, type); auto typeRange = VD->getTypeSourceRangeForDiagnostics(); { // Only one diagnostic can be active at a time. - auto diag = TC.diagnose(typeRange.Start, diag::note_make_optional, - OptionalType::get(type)); + auto diag = diagnose(typeRange.Start, diag::note_make_optional, + OptionalType::get(type)); if (type->hasSimpleTypeRepr()) { diag.fixItInsertAfter(typeRange.End, "?"); } else { @@ -595,8 +611,8 @@ void AttributeChecker::visitIBOutletAttr(IBOutletAttr *attr) { } } { // Only one diagnostic can be active at a time. - auto diag = TC.diagnose(typeRange.Start, - diag::note_make_implicitly_unwrapped_optional); + auto diag = diagnose(typeRange.Start, + diag::note_make_implicitly_unwrapped_optional); if (type->hasSimpleTypeRepr()) { diag.fixItInsertAfter(typeRange.End, "!"); } else { @@ -704,7 +720,7 @@ bool AttributeChecker::visitAbstractAccessControlAttr( // Or within protocols. if (isa(D->getDeclContext())) { diagnoseAndRemoveAttr(attr, diag::access_control_in_protocol, attr); - TC.diagnose(attr->getLocation(), diag::access_control_in_protocol_detail); + diagnose(attr->getLocation(), diag::access_control_in_protocol_detail); return true; } @@ -716,7 +732,7 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { if (auto extension = dyn_cast(D)) { if (attr->getAccess() == AccessLevel::Open) { - TC.diagnose(attr->getLocation(), diag::access_control_extension_open) + diagnose(attr->getLocation(), diag::access_control_extension_open) .fixItReplace(attr->getRange(), "public"); attr->setInvalid(); return; @@ -732,10 +748,8 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { AccessLevel typeAccess = nominal->getFormalAccess(); if (attr->getAccess() > typeAccess) { - TC.diagnose(attr->getLocation(), diag::access_control_extension_more, - typeAccess, - nominal->getDescriptiveKind(), - attr->getAccess()) + diagnose(attr->getLocation(), diag::access_control_extension_more, + typeAccess, nominal->getDescriptiveKind(), attr->getAccess()) .fixItRemove(attr->getRange()); attr->setInvalid(); return; @@ -746,12 +760,11 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { if (std::min(attr->getAccess(), AccessLevel::Public) > maxAccess) { // FIXME: It would be nice to say what part of the requirements actually // end up being problematic. - auto diag = - TC.diagnose(attr->getLocation(), - diag::access_control_ext_requirement_member_more, - attr->getAccess(), - D->getDescriptiveKind(), - maxAccess); + auto diag = diagnose(attr->getLocation(), + diag::access_control_ext_requirement_member_more, + attr->getAccess(), + D->getDescriptiveKind(), + maxAccess); swift::fixItAccess(diag, cast(D), maxAccess); return; } @@ -760,20 +773,20 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { extension->getAttrs().getAttribute()) { AccessLevel defaultAccess = extension->getDefaultAccessLevel(); if (attr->getAccess() > defaultAccess) { - auto diag = TC.diagnose(attr->getLocation(), - diag::access_control_ext_member_more, - attr->getAccess(), - extAttr->getAccess()); + auto diag = diagnose(attr->getLocation(), + diag::access_control_ext_member_more, + attr->getAccess(), + extAttr->getAccess()); // Don't try to fix this one; it's just a warning, and fixing it can // lead to diagnostic fights between this and "declaration must be at // least this accessible" checking for overrides and protocol // requirements. } else if (attr->getAccess() == defaultAccess) { - TC.diagnose(attr->getLocation(), - diag::access_control_ext_member_redundant, - attr->getAccess(), - D->getDescriptiveKind(), - extAttr->getAccess()) + diagnose(attr->getLocation(), + diag::access_control_ext_member_redundant, + attr->getAccess(), + D->getDescriptiveKind(), + extAttr->getAccess()) .fixItRemove(attr->getRange()); } } @@ -782,7 +795,7 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { if (attr->getAccess() == AccessLevel::Open) { if (!isa(D) && !D->isPotentiallyOverridable() && !attr->isInvalid()) { - TC.diagnose(attr->getLocation(), diag::access_control_open_bad_decl) + diagnose(attr->getLocation(), diag::access_control_open_bad_decl) .fixItReplace(attr->getRange(), "public"); attr->setInvalid(); } @@ -832,17 +845,17 @@ void AttributeChecker::visitSetterAccessAttr( storageKind = SK_Property; else storageKind = SK_Variable; - TC.diagnose(attr->getLocation(), diag::access_control_setter_more, - getterAccess, storageKind, attr->getAccess()); + diagnose(attr->getLocation(), diag::access_control_setter_more, + getterAccess, storageKind, attr->getAccess()); attr->setInvalid(); return; } else if (attr->getAccess() == getterAccess) { - TC.diagnose(attr->getLocation(), - diag::access_control_setter_redundant, - attr->getAccess(), - D->getDescriptiveKind(), - getterAccess) + diagnose(attr->getLocation(), + diag::access_control_setter_redundant, + attr->getAccess(), + D->getDescriptiveKind(), + getterAccess) .fixItRemove(attr->getRange()); return; } @@ -910,19 +923,19 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { if (objcName->getNumArgs() > 0) { SourceLoc firstNameLoc = attr->getNameLocs().front(); SourceLoc afterFirstNameLoc = - Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc); - TC.diagnose(firstNameLoc, diag::objc_name_req_nullary, - D->getDescriptiveKind()) + Lexer::getLocForEndOfToken(Ctx.SourceMgr, firstNameLoc); + diagnose(firstNameLoc, diag::objc_name_req_nullary, + D->getDescriptiveKind()) .fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc()); const_cast(attr)->setName( - ObjCSelector(TC.Context, 0, objcName->getSelectorPieces()[0]), + ObjCSelector(Ctx, 0, objcName->getSelectorPieces()[0]), /*implicit=*/false); } } else if (isa(D) || isa(D)) { - TC.diagnose(attr->getLParenLoc(), - isa(D) - ? diag::objc_name_subscript - : diag::objc_name_deinit); + diagnose(attr->getLParenLoc(), + isa(D) + ? diag::objc_name_subscript + : diag::objc_name_deinit); const_cast(attr)->clearName(); } else { // We have a function. Make sure that the number of parameters @@ -940,18 +953,16 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) { unsigned numArgumentNames = objcName->getNumArgs(); if (numArgumentNames != numParameters) { - TC.diagnose(attr->getNameLocs().front(), - diag::objc_name_func_mismatch, - isa(func), - numArgumentNames, - numArgumentNames != 1, - numParameters, - numParameters != 1, - func->hasThrows()); + diagnose(attr->getNameLocs().front(), + diag::objc_name_func_mismatch, + isa(func), + numArgumentNames, + numArgumentNames != 1, + numParameters, + numParameters != 1, + func->hasThrows()); D->getAttrs().add( - ObjCAttr::createUnnamed(TC.Context, - attr->AtLoc, - attr->Range.Start)); + ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->Range.Start)); D->getAttrs().removeAttribute(attr); } } @@ -997,8 +1008,8 @@ void AttributeChecker::visitOptionalAttr(OptionalAttr *attr) { } else { auto objcAttr = D->getAttrs().getAttribute(); if (!objcAttr || objcAttr->isImplicit()) { - auto diag = TC.diagnose(attr->getLocation(), - diag::optional_attribute_missing_explicit_objc); + auto diag = diagnose(attr->getLocation(), + diag::optional_attribute_missing_explicit_objc); if (auto VD = dyn_cast(D)) diag.fixItInsert(VD->getAttributeInsertionLoc(false), "@objc "); } @@ -1006,15 +1017,22 @@ void AttributeChecker::visitOptionalAttr(OptionalAttr *attr) { } void TypeChecker::checkDeclAttributes(Decl *D) { - AttributeChecker Checker(*this, D); + AttributeChecker Checker(D); + // We need to check all OriginallyDefinedInAttr relative to each other, so + // collect them and check in batch later. + llvm::SmallVector ODIAttrs; for (auto attr : D->getAttrs()) { if (!attr->isValid()) continue; // If Attr.def says that the attribute cannot appear on this kind of // declaration, diagnose it and disable it. if (attr->canAppearOnDecl(D)) { - // Otherwise, check it. - Checker.visit(attr); + if (auto *ODI = dyn_cast(attr)) { + ODIAttrs.push_back(ODI); + } else { + // Otherwise, check it. + Checker.visit(attr); + } continue; } @@ -1051,6 +1069,7 @@ void TypeChecker::checkDeclAttributes(Decl *D) { else Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr); } + Checker.checkOriginalDefinedInAttrs(D, ODIAttrs); } /// Returns true if the given method is an valid implementation of a @@ -1058,8 +1077,8 @@ void TypeChecker::checkDeclAttributes(Decl *D) { /// as one of the following: `dynamicallyCall(withArguments:)` or /// `dynamicallyCall(withKeywordArguments:)`. bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, - TypeChecker &TC, bool hasKeywordArguments) { + auto &ctx = decl->getASTContext(); // There are two cases to check. // 1. `dynamicallyCall(withArguments:)`. // In this case, the method is valid if the argument has type `A` where @@ -1071,8 +1090,6 @@ bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, // `ExpressibleByStringLiteral`. // `D.Value` and the return type can be arbitrary. - // FIXME(InterfaceTypeRequest): Remove this. - (void)decl->getInterfaceType(); auto paramList = decl->getParameters(); if (paramList->size() != 1 || paramList->get(0)->isVariadic()) return false; auto argType = paramList->get(0)->getType(); @@ -1081,43 +1098,41 @@ bool swift::isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, // `ExpressibleByArrayLiteral`. if (!hasKeywordArguments) { auto arrayLitProto = - TC.Context.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral); - return TypeChecker::conformsToProtocol(argType, arrayLitProto, DC, - ConformanceCheckOptions()).hasValue(); + ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral); + return (bool)TypeChecker::conformsToProtocol(argType, arrayLitProto, DC, + ConformanceCheckOptions()); } // If keyword arguments, check that argument type conforms to // `ExpressibleByDictionaryLiteral` and that the `Key` associated type // conforms to `ExpressibleByStringLiteral`. auto stringLitProtocol = - TC.Context.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); + ctx.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); auto dictLitProto = - TC.Context.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); + ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral); auto dictConf = TypeChecker::conformsToProtocol(argType, dictLitProto, DC, ConformanceCheckOptions()); - if (!dictConf) return false; - auto keyType = dictConf.getValue().getTypeWitnessByName( - argType, TC.Context.Id_Key); - return TypeChecker::conformsToProtocol(keyType, stringLitProtocol, DC, - ConformanceCheckOptions()).hasValue(); + if (dictConf.isInvalid()) + return false; + auto keyType = dictConf.getTypeWitnessByName(argType, ctx.Id_Key); + return (bool)TypeChecker::conformsToProtocol(keyType, stringLitProtocol, DC, + ConformanceCheckOptions()); } /// Returns true if the given nominal type has a valid implementation of a /// @dynamicCallable attribute requirement with the given argument name. -static bool hasValidDynamicCallableMethod(TypeChecker &TC, - NominalTypeDecl *decl, +static bool hasValidDynamicCallableMethod(NominalTypeDecl *decl, Identifier argumentName, bool hasKeywordArgs) { + auto &ctx = decl->getASTContext(); auto declType = decl->getDeclaredType(); - auto methodName = DeclName(TC.Context, - DeclBaseName(TC.Context.Id_dynamicallyCall), - { argumentName }); - auto candidates = TC.lookupMember(decl, declType, methodName); + DeclNameRef methodName({ ctx, ctx.Id_dynamicallyCall, { argumentName } }); + auto candidates = TypeChecker::lookupMember(decl, declType, methodName); if (candidates.empty()) return false; // Filter valid candidates. candidates.filter([&](LookupResultEntry entry, bool isOuter) { auto candidate = cast(entry.getValueDecl()); - return isValidDynamicCallableMethod(candidate, decl, TC, hasKeywordArgs); + return isValidDynamicCallableMethod(candidate, decl, hasKeywordArgs); }); // If there are no valid candidates, return false. @@ -1133,13 +1148,13 @@ visitDynamicCallableAttr(DynamicCallableAttr *attr) { bool hasValidMethod = false; hasValidMethod |= - hasValidDynamicCallableMethod(TC, decl, TC.Context.Id_withArguments, + hasValidDynamicCallableMethod(decl, Ctx.Id_withArguments, /*hasKeywordArgs*/ false); hasValidMethod |= - hasValidDynamicCallableMethod(TC, decl, TC.Context.Id_withKeywordArguments, + hasValidDynamicCallableMethod(decl, Ctx.Id_withKeywordArguments, /*hasKeywordArgs*/ true); if (!hasValidMethod) { - TC.diagnose(attr->getLocation(), diag::invalid_dynamic_callable_type, type); + diagnose(attr->getLocation(), diag::invalid_dynamic_callable_type, type); attr->setInvalid(); } } @@ -1167,22 +1182,22 @@ static bool hasSingleNonVariadicParam(SubscriptDecl *decl, /// The method is given to be defined as `subscript(dynamicMember:)`. bool swift::isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC, - TypeChecker &TC, bool ignoreLabel) { // It could be // - `subscript(dynamicMember: {Writable}KeyPath<...>)`; or // - `subscript(dynamicMember: String*)` - return isValidKeyPathDynamicMemberLookup(decl, TC, ignoreLabel) || - isValidStringDynamicMemberLookup(decl, DC, TC, ignoreLabel); + return isValidKeyPathDynamicMemberLookup(decl, ignoreLabel) || + isValidStringDynamicMemberLookup(decl, DC, ignoreLabel); } bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl, - DeclContext *DC, TypeChecker &TC, + DeclContext *DC, bool ignoreLabel) { + auto &ctx = decl->getASTContext(); // There are two requirements: // - The subscript method has exactly one, non-variadic parameter. // - The parameter type conforms to `ExpressibleByStringLiteral`. - if (!hasSingleNonVariadicParam(decl, TC.Context.Id_dynamicMember, + if (!hasSingleNonVariadicParam(decl, ctx.Id_dynamicMember, ignoreLabel)) return false; @@ -1190,25 +1205,25 @@ bool swift::isValidStringDynamicMemberLookup(SubscriptDecl *decl, auto paramType = param->getType(); auto stringLitProto = - TC.Context.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); + ctx.getProtocol(KnownProtocolKind::ExpressibleByStringLiteral); // If this is `subscript(dynamicMember: String*)` - return bool(TypeChecker::conformsToProtocol(paramType, stringLitProto, DC, - ConformanceCheckOptions())); + return (bool)TypeChecker::conformsToProtocol(paramType, stringLitProto, DC, + ConformanceCheckOptions()); } bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, - TypeChecker &TC, bool ignoreLabel) { - if (!hasSingleNonVariadicParam(decl, TC.Context.Id_dynamicMember, + auto &ctx = decl->getASTContext(); + if (!hasSingleNonVariadicParam(decl, ctx.Id_dynamicMember, ignoreLabel)) return false; const auto *param = decl->getIndices()->get(0); - if (auto NTD = param->getType()->getAnyNominal()) { - return NTD == TC.Context.getKeyPathDecl() || - NTD == TC.Context.getWritableKeyPathDecl() || - NTD == TC.Context.getReferenceWritableKeyPathDecl(); + if (auto NTD = param->getInterfaceType()->getAnyNominal()) { + return NTD == ctx.getKeyPathDecl() || + NTD == ctx.getWritableKeyPathDecl() || + NTD == ctx.getReferenceWritableKeyPathDecl(); } return false; } @@ -1229,23 +1244,21 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { auto &ctx = decl->getASTContext(); auto emitInvalidTypeDiagnostic = [&](const SourceLoc loc) { - TC.diagnose(loc, diag::invalid_dynamic_member_lookup_type, type); + diagnose(loc, diag::invalid_dynamic_member_lookup_type, type); attr->setInvalid(); }; // Look up `subscript(dynamicMember:)` candidates. - auto subscriptName = - DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember); - auto candidates = TC.lookupMember(decl, type, subscriptName); + DeclNameRef subscriptName( + { ctx, DeclBaseName::createSubscript(), { ctx.Id_dynamicMember } }); + auto candidates = TypeChecker::lookupMember(decl, type, subscriptName); if (!candidates.empty()) { // If no candidates are valid, then reject one. auto oneCandidate = candidates.front().getValueDecl(); candidates.filter([&](LookupResultEntry entry, bool isOuter) -> bool { auto cand = cast(entry.getValueDecl()); - // FIXME(InterfaceTypeRequest): Remove this. - (void)cand->getInterfaceType(); - return isValidDynamicMemberLookupSubscript(cand, decl, TC); + return isValidDynamicMemberLookupSubscript(cand, decl); }); if (candidates.empty()) { @@ -1262,14 +1275,12 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { // // Let's do another lookup using just the base name. auto newCandidates = - TC.lookupMember(decl, type, DeclBaseName::createSubscript()); + TypeChecker::lookupMember(decl, type, DeclNameRef::createSubscript()); // Validate the candidates while ignoring the label. newCandidates.filter([&](const LookupResultEntry entry, bool isOuter) { auto cand = cast(entry.getValueDecl()); - // FIXME(InterfaceTypeRequest): Remove this. - (void)cand->getInterfaceType(); - return isValidDynamicMemberLookupSubscript(cand, decl, TC, + return isValidDynamicMemberLookupSubscript(cand, decl, /*ignoreLabel*/ true); }); @@ -1284,13 +1295,13 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) { for (auto cand : newCandidates) { auto SD = cast(cand.getValueDecl()); auto index = SD->getIndices()->get(0); - TC.diagnose(SD, diag::invalid_dynamic_member_lookup_type, type); + diagnose(SD, diag::invalid_dynamic_member_lookup_type, type); // If we have something like `subscript(foo:)` then we want to insert // `dynamicMember` before `foo`. if (index->getParameterNameLoc().isValid() && index->getArgumentNameLoc().isInvalid()) { - TC.diagnose(SD, diag::invalid_dynamic_member_subscript) + diagnose(SD, diag::invalid_dynamic_member_subscript) .highlight(index->getSourceRange()) .fixItInsert(index->getParameterNameLoc(), "dynamicMember "); } @@ -1312,16 +1323,16 @@ static Decl *getEnclosingDeclForDecl(Decl *D) { } void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { - if (TC.getLangOpts().DisableAvailabilityChecking) + if (Ctx.LangOpts.DisableAvailabilityChecking) return; if (auto *PD = dyn_cast(D->getDeclContext())) { if (auto *VD = dyn_cast(D)) { if (VD->isProtocolRequirement()) { - if (attr->isActivePlatform(TC.Context) || + if (attr->isActivePlatform(Ctx) || attr->isLanguageVersionSpecific() || attr->isPackageDescriptionVersionSpecific()) { - auto versionAvailability = attr->getVersionAvailability(TC.Context); + auto versionAvailability = attr->getVersionAvailability(Ctx); if (attr->isUnconditionallyUnavailable() || versionAvailability == AvailableVersionComparison::Obsoleted || versionAvailability == AvailableVersionComparison::Unavailable) { @@ -1335,17 +1346,27 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { } } - if (!attr->hasPlatform() || !attr->isActivePlatform(TC.Context) || + if (!attr->hasPlatform() || !attr->isActivePlatform(Ctx) || !attr->Introduced.hasValue()) { return; } + // Make sure there isn't a more specific attribute we should be using instead. + // findMostSpecificActivePlatform() is O(N), so only do this if we're checking + // an iOS attribute while building for macCatalyst. + if (attr->Platform == PlatformKind::iOS && + isPlatformActive(PlatformKind::macCatalyst, Ctx.LangOpts)) { + if (attr != D->getAttrs().findMostSpecificActivePlatform(Ctx)) { + return; + } + } + SourceLoc attrLoc = attr->getLocation(); Optional> MaybeNotAllowed = - TC.diagnosticIfDeclCannotBePotentiallyUnavailable(D); + TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(D); if (MaybeNotAllowed.hasValue()) { - TC.diagnose(attrLoc, MaybeNotAllowed.getValue()); + diagnose(attrLoc, MaybeNotAllowed.getValue()); } // Find the innermost enclosing declaration with an availability @@ -1357,8 +1378,7 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { while (EnclosingDecl) { EnclosingAnnotatedRange = - AvailabilityInference::annotatedAvailableRange(EnclosingDecl, - TC.Context); + AvailabilityInference::annotatedAvailableRange(EnclosingDecl, Ctx); if (EnclosingAnnotatedRange.hasValue()) break; @@ -1373,23 +1393,20 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { VersionRange::allGTE(attr->Introduced.getValue())}; if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) { - TC.diagnose(attr->getLocation(), - diag::availability_decl_more_than_enclosing); - TC.diagnose(EnclosingDecl->getLoc(), - diag::availability_decl_more_than_enclosing_enclosing_here); + diagnose(attr->getLocation(), diag::availability_decl_more_than_enclosing); + diagnose(EnclosingDecl->getLoc(), + diag::availability_decl_more_than_enclosing_enclosing_here); } } void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) { // Only top-level func decls are currently supported. if (D->getDeclContext()->isTypeContext()) - TC.diagnose(attr->getLocation(), - diag::cdecl_not_at_top_level); - + diagnose(attr->getLocation(), diag::cdecl_not_at_top_level); + // The name must not be empty. if (attr->Name.empty()) - TC.diagnose(attr->getLocation(), - diag::cdecl_empty_name); + diagnose(attr->getLocation(), diag::cdecl_empty_name); } void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr( @@ -1397,15 +1414,15 @@ void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr( // Only class protocols can have the attribute. auto proto = dyn_cast(D); if (!proto) { - TC.diagnose(attr->getLocation(), - diag::no_objc_tagged_pointer_not_class_protocol); + diagnose(attr->getLocation(), + diag::no_objc_tagged_pointer_not_class_protocol); attr->setInvalid(); } if (!proto->requiresClass() && !proto->getAttrs().hasAttribute()) { - TC.diagnose(attr->getLocation(), - diag::no_objc_tagged_pointer_not_class_protocol); + diagnose(attr->getLocation(), + diag::no_objc_tagged_pointer_not_class_protocol); attr->setInvalid(); } } @@ -1415,15 +1432,15 @@ void AttributeChecker::visitSwiftNativeObjCRuntimeBaseAttr( // Only root classes can have the attribute. auto theClass = dyn_cast(D); if (!theClass) { - TC.diagnose(attr->getLocation(), - diag::swift_native_objc_runtime_base_not_on_root_class); + diagnose(attr->getLocation(), + diag::swift_native_objc_runtime_base_not_on_root_class); attr->setInvalid(); return; } - + if (theClass->hasSuperclass()) { - TC.diagnose(attr->getLocation(), - diag::swift_native_objc_runtime_base_not_on_root_class); + diagnose(attr->getLocation(), + diag::swift_native_objc_runtime_base_not_on_root_class); attr->setInvalid(); return; } @@ -1433,8 +1450,8 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) { // Reject combining 'final' with 'open'. if (auto accessAttr = D->getAttrs().getAttribute()) { if (accessAttr->getAccess() == AccessLevel::Open) { - TC.diagnose(attr->getLocation(), diag::open_decl_cannot_be_final, - D->getDescriptiveKind()); + diagnose(attr->getLocation(), diag::open_decl_cannot_be_final, + D->getDescriptiveKind()); return; } } @@ -1445,7 +1462,7 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) { // 'final' only makes sense in the context of a class declaration. // Reject it on global functions, protocols, structs, enums, etc. if (!D->getDeclContext()->getSelfClassDecl()) { - TC.diagnose(attr->getLocation(), diag::member_cannot_be_final) + diagnose(attr->getLocation(), diag::member_cannot_be_final) .fixItRemove(attr->getRange()); // Remove the attribute so child declarations are not flagged as final @@ -1457,7 +1474,7 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) { // We currently only support final on var/let, func and subscript // declarations. if (!isa(D) && !isa(D) && !isa(D)) { - TC.diagnose(attr->getLocation(), diag::final_not_allowed_here) + diagnose(attr->getLocation(), diag::final_not_allowed_here) .fixItRemove(attr->getRange()); return; } @@ -1467,7 +1484,7 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) { unsigned Kind = 2; if (auto *VD = dyn_cast(accessor->getStorage())) Kind = VD->isLet() ? 1 : 0; - TC.diagnose(attr->getLocation(), diag::final_not_on_accessors, Kind) + diagnose(attr->getLocation(), diag::final_not_on_accessors, Kind) .fixItRemove(attr->getRange()); return; } @@ -1491,8 +1508,8 @@ void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) { if (auto *OD = dyn_cast(D)) { // Reject attempts to define builtin operators. if (isBuiltinOperator(OD->getName().str(), attr)) { - TC.diagnose(D->getStartLoc(), diag::redefining_builtin_operator, - attr->getAttrName(), OD->getName().str()); + diagnose(D->getStartLoc(), diag::redefining_builtin_operator, + attr->getAttrName(), OD->getName().str()); attr->setInvalid(); return; } @@ -1504,7 +1521,7 @@ void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) { // Operators implementations may only be defined as functions. auto *FD = dyn_cast(D); if (!FD) { - TC.diagnose(D->getLoc(), diag::operator_not_func); + diagnose(D->getLoc(), diag::operator_not_func); attr->setInvalid(); return; } @@ -1512,24 +1529,24 @@ void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) { // Only functions with an operator identifier can be declared with as an // operator. if (!FD->isOperator()) { - TC.diagnose(D->getStartLoc(), diag::attribute_requires_operator_identifier, - attr->getAttrName()); + diagnose(D->getStartLoc(), diag::attribute_requires_operator_identifier, + attr->getAttrName()); attr->setInvalid(); return; } // Reject attempts to define builtin operators. if (isBuiltinOperator(FD->getName().str(), attr)) { - TC.diagnose(D->getStartLoc(), diag::redefining_builtin_operator, - attr->getAttrName(), FD->getName().str()); + diagnose(D->getStartLoc(), diag::redefining_builtin_operator, + attr->getAttrName(), FD->getName().str()); attr->setInvalid(); return; } // Otherwise, must be unary. if (!FD->isUnaryOperator()) { - TC.diagnose(attr->getLocation(), diag::attribute_requires_single_argument, - attr->getAttrName()); + diagnose(attr->getLocation(), diag::attribute_requires_single_argument, + attr->getAttrName()); attr->setInvalid(); return; } @@ -1542,25 +1559,25 @@ void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) { // It may only be used on class members. auto classDecl = D->getDeclContext()->getSelfClassDecl(); if (!classDecl) { - TC.diagnose(attr->getLocation(), diag::nscopying_only_on_class_properties); + diagnose(attr->getLocation(), diag::nscopying_only_on_class_properties); attr->setInvalid(); return; } if (!VD->isSettable(VD->getDeclContext())) { - TC.diagnose(attr->getLocation(), diag::nscopying_only_mutable); + diagnose(attr->getLocation(), diag::nscopying_only_mutable); attr->setInvalid(); return; } if (!VD->hasStorage()) { - TC.diagnose(attr->getLocation(), diag::nscopying_only_stored_property); + diagnose(attr->getLocation(), diag::nscopying_only_stored_property); attr->setInvalid(); return; } if (VD->hasInterfaceType()) { - if (!TC.checkConformanceToNSCopying(VD)) { + if (TypeChecker::checkConformanceToNSCopying(VD).isInvalid()) { attr->setInvalid(); return; } @@ -1572,7 +1589,7 @@ void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) { // Check the type. It must be an [unchecked]optional, weak, a normal // class, AnyObject, or classbound protocol. // It must conform to the NSCopying protocol. - + } void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, @@ -1584,7 +1601,7 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, UIApplicationMainClass, NSApplicationMainClass, }; - + unsigned applicationMainKind; if (isa(attr)) applicationMainKind = UIApplicationMainClass; @@ -1592,22 +1609,22 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, applicationMainKind = NSApplicationMainClass; else llvm_unreachable("not an ApplicationMain attr"); - + auto *CD = dyn_cast(D); - + // The applicant not being a class should have been diagnosed by the early // checker. if (!CD) return; // The class cannot be generic. if (CD->isGenericContext()) { - TC.diagnose(attr->getLocation(), - diag::attr_generic_ApplicationMain_not_supported, - applicationMainKind); + diagnose(attr->getLocation(), + diag::attr_generic_ApplicationMain_not_supported, + applicationMainKind); attr->setInvalid(); return; } - + // @XXApplicationMain classes must conform to the XXApplicationDelegate // protocol. auto *SF = cast(CD->getModuleScopeContext()); @@ -1627,17 +1644,16 @@ void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr, if (!ApplicationDelegateProto || !TypeChecker::conformsToProtocol(CD->getDeclaredType(), - ApplicationDelegateProto, - CD, None)) { - TC.diagnose(attr->getLocation(), - diag::attr_ApplicationMain_not_ApplicationDelegate, - applicationMainKind); + ApplicationDelegateProto, CD, None)) { + diagnose(attr->getLocation(), + diag::attr_ApplicationMain_not_ApplicationDelegate, + applicationMainKind); attr->setInvalid(); } if (attr->isInvalid()) return; - + // Register the class as the main class in the module. If there are multiples // they will be diagnosed. if (SF->registerMainClass(CD, attr->getLocation())) @@ -1695,14 +1711,14 @@ void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) { // defined in Objective-C if (!isa(ctor->getDeclContext()) && !isObjCClassExtensionInOverlay(ctor->getDeclContext())) { - TC.diagnose(ctor, diag::required_initializer_in_extension, parentTy) + diagnose(ctor, diag::required_initializer_in_extension, parentTy) .highlight(attr->getLocation()); attr->setInvalid(); return; } } else { if (!parentTy->hasError()) { - TC.diagnose(ctor, diag::required_initializer_nonclass, parentTy) + diagnose(ctor, diag::required_initializer_nonclass, parentTy) .highlight(attr->getLocation()); } attr->setInvalid(); @@ -1744,7 +1760,7 @@ void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) { return; } - TC.diagnose(attr->getLocation(), diag::rethrows_without_throwing_parameter); + diagnose(attr->getLocation(), diag::rethrows_without_throwing_parameter); attr->setInvalid(); } @@ -1771,7 +1787,7 @@ static void checkSpecializeAttrRequirements( SpecializeAttr *attr, AbstractFunctionDecl *FD, const SmallPtrSet &constrainedGenericParams, - TypeChecker &TC) { + ASTContext &ctx) { auto genericSig = FD->getGenericSignature(); if (!attr->isFullSpecialization()) @@ -1780,7 +1796,7 @@ static void checkSpecializeAttrRequirements( if (constrainedGenericParams.size() == genericSig->getGenericParams().size()) return; - TC.diagnose( + ctx.Diags.diagnose( attr->getLocation(), diag::specialize_attr_type_parameter_count_mismatch, genericSig->getGenericParams().size(), constrainedGenericParams.size(), constrainedGenericParams.size() < genericSig->getGenericParams().size()); @@ -1792,9 +1808,9 @@ static void checkSpecializeAttrRequirements( continue; auto gpDecl = gp->getDecl(); if (gpDecl) { - TC.diagnose(attr->getLocation(), - diag::specialize_attr_missing_constraint, - gpDecl->getFullName()); + ctx.Diags.diagnose(attr->getLocation(), + diag::specialize_attr_missing_constraint, + gpDecl->getFullName()); } } } @@ -1825,21 +1841,21 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { if (!trailingWhereClause) { // Report a missing "where" clause. - TC.diagnose(attr->getLocation(), diag::specialize_missing_where_clause); + diagnose(attr->getLocation(), diag::specialize_missing_where_clause); return; } if (trailingWhereClause->getRequirements().empty()) { // Report an empty "where" clause. - TC.diagnose(attr->getLocation(), diag::specialize_empty_where_clause); + diagnose(attr->getLocation(), diag::specialize_empty_where_clause); return; } if (!genericSig) { // Only generic functions are permitted to have trailing where clauses. - TC.diagnose(attr->getLocation(), - diag::specialize_attr_nongeneric_trailing_where, - FD->getFullName()) + diagnose(attr->getLocation(), + diag::specialize_attr_nongeneric_trailing_where, + FD->getFullName()) .highlight(trailingWhereClause->getSourceRange()); return; } @@ -1882,11 +1898,10 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { // Exactly one type can have a type parameter. if (firstHasTypeParameter == secondHasTypeParameter) { - TC.diagnose( - attr->getLocation(), - firstHasTypeParameter - ? diag::specialize_attr_non_concrete_same_type_req - : diag::specialize_attr_only_one_concrete_same_type_req) + diagnose(attr->getLocation(), + firstHasTypeParameter + ? diag::specialize_attr_non_concrete_same_type_req + : diag::specialize_attr_only_one_concrete_same_type_req) .highlight(reqRepr->getSourceRange()); return false; } @@ -1904,8 +1919,8 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { } case RequirementKind::Superclass: - TC.diagnose(attr->getLocation(), - diag::specialize_attr_non_protocol_type_constraint_req) + diagnose(attr->getLocation(), + diag::specialize_attr_non_protocol_type_constraint_req) .highlight(reqRepr->getSourceRange()); return false; @@ -1917,14 +1932,14 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { } if (!req.getSecondType()->is()) { - TC.diagnose(attr->getLocation(), - diag::specialize_attr_non_protocol_type_constraint_req) + diagnose(attr->getLocation(), + diag::specialize_attr_non_protocol_type_constraint_req) .highlight(reqRepr->getSourceRange()); return false; } - TC.diagnose(attr->getLocation(), - diag::specialize_attr_unsupported_kind_of_req) + diagnose(attr->getLocation(), + diag::specialize_attr_unsupported_kind_of_req) .highlight(reqRepr->getSourceRange()); return false; @@ -1948,7 +1963,7 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { }); // Check the validity of provided requirements. - checkSpecializeAttrRequirements(attr, FD, constrainedGenericParams, TC); + checkSpecializeAttrRequirements(attr, FD, constrainedGenericParams, Ctx); // Check the result. auto specializedSig = std::move(Builder).computeGenericSignature( @@ -1959,9 +1974,9 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) { void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) { if (isa(D)) { - TC.diagnose(attr->getLocation(), diag::fixed_layout_struct) + diagnose(attr->getLocation(), diag::fixed_layout_struct) .fixItReplace(attr->getRange(), "@frozen"); - } + } auto *VD = cast(D); @@ -1993,7 +2008,7 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) { // On internal declarations, @inlinable implies @usableFromInline. if (VD->getAttrs().hasAttribute()) { - if (TC.Context.isSwiftVersionAtLeast(4,2)) + if (Ctx.isSwiftVersionAtLeast(4,2)) diagnoseAndRemoveAttr(attr, diag::inlinable_implies_usable_from_inline); return; } @@ -2068,10 +2083,10 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) { } /// Lookup the replaced decl in the replacments scope. -void lookupReplacedDecl(DeclName replacedDeclName, - const DynamicReplacementAttr *attr, - const ValueDecl *replacement, - SmallVectorImpl &results) { +static void lookupReplacedDecl(DeclNameRef replacedDeclName, + const DynamicReplacementAttr *attr, + const ValueDecl *replacement, + SmallVectorImpl &results) { auto *declCtxt = replacement->getDeclContext(); // Look at the accessors' storage's context. @@ -2082,12 +2097,13 @@ void lookupReplacedDecl(DeclName replacedDeclName, auto *moduleScopeCtxt = declCtxt->getModuleScopeContext(); if (isa(declCtxt)) { - UnqualifiedLookup lookup(replacedDeclName, moduleScopeCtxt, - attr->getLocation()); - if (lookup.isSuccess()) { - for (auto entry : lookup.Results) { - results.push_back(entry.getValueDecl()); - } + auto &ctx = declCtxt->getASTContext(); + auto descriptor = UnqualifiedLookupDescriptor( + replacedDeclName, moduleScopeCtxt, attr->getLocation()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); + for (auto entry : lookup) { + results.push_back(entry.getValueDecl()); } return; } @@ -2118,16 +2134,13 @@ static Type getDynamicComparisonType(ValueDecl *value) { } auto interfaceType = value->getInterfaceType(); - if (!interfaceType) - return ErrorType::get(value->getASTContext()); - return interfaceType->removeArgumentLabels(numArgumentLabels); } -static FuncDecl *findReplacedAccessor(DeclName replacedVarName, +static FuncDecl *findReplacedAccessor(DeclNameRef replacedVarName, AccessorDecl *replacement, DynamicReplacementAttr *attr, - TypeChecker &TC) { + ASTContext &ctx) { // Retrieve the replaced abstract storage decl. SmallVector results; @@ -2160,20 +2173,23 @@ static FuncDecl *findReplacedAccessor(DeclName replacedVarName, results.end()); } + auto &Diags = ctx.Diags; if (results.empty()) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_accessor_not_found, replacedVarName); + Diags.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_found, + replacedVarName); attr->setInvalid(); return nullptr; } if (results.size() > 1) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_accessor_ambiguous, replacedVarName); + Diags.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_ambiguous, + replacedVarName); for (auto result : results) { - TC.diagnose(result, - diag::dynamic_replacement_accessor_ambiguous_candidate, - result->getModuleContext()->getFullName()); + Diags.diagnose(result, + diag::dynamic_replacement_accessor_ambiguous_candidate, + result->getModuleContext()->getFullName()); } attr->setInvalid(); return nullptr; @@ -2181,13 +2197,11 @@ static FuncDecl *findReplacedAccessor(DeclName replacedVarName, assert(!isa(results[0])); - // FIXME(InterfaceTypeRequest): Remove this. - (void)results[0]->getInterfaceType(); auto *origStorage = cast(results[0]); if (!origStorage->isDynamic()) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_accessor_not_dynamic, - replacedVarName); + Diags.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_dynamic, + origStorage->getFullName()); attr->setInvalid(); return nullptr; } @@ -2198,14 +2212,13 @@ static FuncDecl *findReplacedAccessor(DeclName replacedVarName, if (!origAccessor) return nullptr; - // FIXME(InterfaceTypeRequest): Remove this. - (void)origAccessor->getInterfaceType(); if (origAccessor->isImplicit() && !(origStorage->getReadImpl() == ReadImplKind::Stored && origStorage->getWriteImpl() == WriteImplKind::Stored)) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_accessor_not_explicit, - (unsigned)origAccessor->getAccessorKind(), replacedVarName); + Diags.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_explicit, + (unsigned)origAccessor->getAccessorKind(), + origStorage->getFullName()); attr->setInvalid(); return nullptr; } @@ -2214,9 +2227,9 @@ static FuncDecl *findReplacedAccessor(DeclName replacedVarName, } static AbstractFunctionDecl * -findReplacedFunction(DeclName replacedFunctionName, +findReplacedFunction(DeclNameRef replacedFunctionName, const AbstractFunctionDecl *replacement, - DynamicReplacementAttr *attr, TypeChecker *TC) { + DynamicReplacementAttr *attr, DiagnosticEngine *Diags) { // Note: we might pass a constant attribute when typechecker is nullptr. // Any modification to attr must be guarded by a null check on TC. @@ -2231,16 +2244,17 @@ findReplacedFunction(DeclName replacedFunctionName, // Check for static/instance mismatch. if (result->isStatic() != replacement->isStatic()) continue; - + + auto resultTy = result->getInterfaceType(); + auto replaceTy = replacement->getInterfaceType(); TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; - if (result->getInterfaceType()->getCanonicalType()->matches( - replacement->getInterfaceType()->getCanonicalType(), matchMode)) { + if (resultTy->matches(replaceTy, matchMode)) { if (!result->isDynamic()) { - if (TC) { - TC->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_not_dynamic, - replacedFunctionName); + if (Diags) { + Diags->diagnose(attr->getLocation(), + diag::dynamic_replacement_function_not_dynamic, + result->getFullName()); attr->setInvalid(); } return nullptr; @@ -2249,24 +2263,24 @@ findReplacedFunction(DeclName replacedFunctionName, } } - if (!TC) + if (!Diags) return nullptr; if (results.empty()) { - TC->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_not_found, - attr->getReplacedFunctionName()); + Diags->diagnose(attr->getLocation(), + diag::dynamic_replacement_function_not_found, + replacedFunctionName); } else { - TC->diagnose(attr->getLocation(), - diag::dynamic_replacement_function_of_type_not_found, - attr->getReplacedFunctionName(), - replacement->getInterfaceType()->getCanonicalType()); + Diags->diagnose(attr->getLocation(), + diag::dynamic_replacement_function_of_type_not_found, + replacedFunctionName, + replacement->getInterfaceType()->getCanonicalType()); for (auto *result : results) { - TC->diagnose(SourceLoc(), - diag::dynamic_replacement_found_function_of_type, - attr->getReplacedFunctionName(), - result->getInterfaceType()->getCanonicalType()); + Diags->diagnose(SourceLoc(), + diag::dynamic_replacement_found_function_of_type, + result->getFullName(), + result->getInterfaceType()->getCanonicalType()); } } attr->setInvalid(); @@ -2274,7 +2288,7 @@ findReplacedFunction(DeclName replacedFunctionName, } static AbstractStorageDecl * -findReplacedStorageDecl(DeclName replacedFunctionName, +findReplacedStorageDecl(DeclNameRef replacedFunctionName, const AbstractStorageDecl *replacement, const DynamicReplacementAttr *attr) { @@ -2285,9 +2299,11 @@ findReplacedStorageDecl(DeclName replacedFunctionName, // Check for static/instance mismatch. if (result->isStatic() != replacement->isStatic()) continue; - if (result->getInterfaceType()->getCanonicalType()->matches( - replacement->getInterfaceType()->getCanonicalType(), - TypeMatchFlags::AllowABICompatible)) { + auto resultTy = result->getInterfaceType(); + auto replaceTy = replacement->getInterfaceType(); + TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; + matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; + if (resultTy->matches(replaceTy, matchMode)) { if (!result->isDynamic()) { return nullptr; } @@ -2297,133 +2313,59 @@ findReplacedStorageDecl(DeclName replacedFunctionName, return nullptr; } -ValueDecl *TypeChecker::findReplacedDynamicFunction(const ValueDecl *vd) { - assert(isa(vd) || isa(vd)); - if (isa(vd)) - return nullptr; - - auto *attr = vd->getAttrs().getAttribute(); - if (!attr) - return nullptr; - - auto *afd = dyn_cast(vd); - if (afd) { - // When we pass nullptr as the type checker argument attr is truely const. - return findReplacedFunction(attr->getReplacedFunctionName(), afd, - const_cast(attr), - nullptr); - } - auto *storageDecl = dyn_cast(vd); - if (!storageDecl) - return nullptr; - return findReplacedStorageDecl(attr->getReplacedFunctionName(), storageDecl, attr); -} - void AttributeChecker::visitDynamicReplacementAttr(DynamicReplacementAttr *attr) { assert(isa(D) || isa(D)); - auto *VD = cast(D); + auto *replacement = cast(D); - if (!isa(VD->getDeclContext()) && - !VD->getDeclContext()->isModuleScopeContext()) { - TC.diagnose(attr->getLocation(), diag::dynamic_replacement_not_in_extension, - VD->getBaseName()); + if (!isa(replacement->getDeclContext()) && + !replacement->getDeclContext()->isModuleScopeContext()) { + diagnose(attr->getLocation(), diag::dynamic_replacement_not_in_extension, + replacement->getBaseName()); attr->setInvalid(); return; } - if (VD->isNativeDynamic()) { - TC.diagnose(attr->getLocation(), diag::dynamic_replacement_must_not_be_dynamic, - VD->getBaseName()); + if (replacement->isNativeDynamic()) { + diagnose(attr->getLocation(), diag::dynamic_replacement_must_not_be_dynamic, + replacement->getBaseName()); attr->setInvalid(); return; } - // Don't process a declaration twice. This will happen to accessor decls after - // we have processed their var decls. - if (attr->getReplacedFunction()) + auto *original = replacement->getDynamicallyReplacedDecl(); + if (!original) { + attr->setInvalid(); return; - - SmallVector replacements; - SmallVector origs; - - // Collect the accessor replacement mapping if this is an abstract storage. - if (auto *var = dyn_cast(VD)) { - var->visitParsedAccessors([&](AccessorDecl *accessor) { - if (attr->isInvalid()) - return; - - // FIXME(InterfaceTypeRequest): Remove this. - (void)accessor->getInterfaceType(); - auto *orig = findReplacedAccessor(attr->getReplacedFunctionName(), - accessor, attr, TC); - if (!orig) - return; - - origs.push_back(orig); - replacements.push_back(accessor); - }); - } else { - // Otherwise, find the matching function. - auto *fun = cast(VD); - if (auto *orig = findReplacedFunction(attr->getReplacedFunctionName(), fun, - attr, &TC)) { - origs.push_back(orig); - replacements.push_back(fun); - } else - return; } - // Annotate the replacement with the original func decl. - for (auto index : indices(replacements)) { - if (auto *attr = replacements[index] - ->getAttrs() - .getAttribute()) { - auto *replacedFun = origs[index]; - auto *replacement = replacements[index]; - if (replacedFun->isObjC() && !replacement->isObjC()) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_replacement_not_objc_dynamic, - attr->getReplacedFunctionName()); - attr->setInvalid(); - return; - } - if (!replacedFun->isObjC() && replacement->isObjC()) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_replaced_not_objc_dynamic, - attr->getReplacedFunctionName()); - attr->setInvalid(); - return; - } - attr->setReplacedFunction(replacedFun); - continue; - } - auto *newAttr = DynamicReplacementAttr::create( - VD->getASTContext(), attr->getReplacedFunctionName(), origs[index]); - DeclAttributes &attrs = replacements[index]->getAttrs(); - attrs.add(newAttr); + if (original->isObjC() && !replacement->isObjC()) { + diagnose(attr->getLocation(), + diag::dynamic_replacement_replacement_not_objc_dynamic, + replacement->getFullName()); + attr->setInvalid(); } - if (auto *CD = dyn_cast(VD)) { + if (!original->isObjC() && replacement->isObjC()) { + diagnose(attr->getLocation(), + diag::dynamic_replacement_replaced_not_objc_dynamic, + original->getFullName()); + attr->setInvalid(); + } + + if (auto *CD = dyn_cast(replacement)) { auto *attr = CD->getAttrs().getAttribute(); auto replacedIsConvenienceInit = - cast(attr->getReplacedFunction())->isConvenienceInit(); + cast(original)->isConvenienceInit(); if (replacedIsConvenienceInit &&!CD->isConvenienceInit()) { - TC.diagnose(attr->getLocation(), - diag::dynamic_replacement_replaced_constructor_is_convenience, - attr->getReplacedFunctionName()); + diagnose(attr->getLocation(), + diag::dynamic_replacement_replaced_constructor_is_convenience, + attr->getReplacedFunctionName()); } else if (!replacedIsConvenienceInit && CD->isConvenienceInit()) { - TC.diagnose( + diagnose( attr->getLocation(), diag::dynamic_replacement_replaced_constructor_is_not_convenience, attr->getReplacedFunctionName()); } } - - - // Remove the attribute on the abstract storage (we have moved it to the - // accessor decl). - if (!isa(VD)) - return; - D->getAttrs().removeAttribute(attr); } void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { @@ -2450,12 +2392,13 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { ProtocolDecl *PD = PT->getDecl(); // Check that the ProtocolType has the specified member. - LookupResult R = TC.lookupMember(PD->getDeclContext(), - PT, attr->getMemberName()); + LookupResult R = + TypeChecker::lookupMember(PD->getDeclContext(), PT, + DeclNameRef(attr->getMemberName())); if (!R) { - TC.diagnose(attr->getLocation(), - diag::implements_attr_protocol_lacks_member, - PD->getBaseName(), attr->getMemberName()) + diagnose(attr->getLocation(), + diag::implements_attr_protocol_lacks_member, + PD->getBaseName(), attr->getMemberName()) .highlight(attr->getMemberNameLoc().getSourceRange()); } @@ -2464,15 +2407,14 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { NominalTypeDecl *NTD = DC->getSelfNominalTypeDecl(); SmallVector conformances; if (!NTD->lookupConformance(DC->getParentModule(), PD, conformances)) { - TC.diagnose(attr->getLocation(), - diag::implements_attr_protocol_not_conformed_to, - NTD->getFullName(), PD->getFullName()) + diagnose(attr->getLocation(), + diag::implements_attr_protocol_not_conformed_to, + NTD->getFullName(), PD->getFullName()) .highlight(ProtoTypeLoc.getTypeRepr()->getSourceRange()); } } else { - TC.diagnose(attr->getLocation(), - diag::implements_attr_non_protocol_type) + diagnose(attr->getLocation(), diag::implements_attr_non_protocol_type) .highlight(ProtoTypeLoc.getTypeRepr()->getSourceRange()); } } @@ -2480,7 +2422,7 @@ void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) { if (auto *ED = dyn_cast(D)) { if (!ED->getModuleContext()->isResilient()) { - diagnoseAndRemoveAttr(attr, diag::enum_frozen_nonresilient, attr); + attr->setInvalid(); return; } @@ -2505,7 +2447,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // Figure out which nominal declaration this custom attribute refers to. auto nominal = evaluateOrDefault( - TC.Context.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); + Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); // If there is no nominal type with this name, complain about this being // an unknown attribute. @@ -2518,8 +2460,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { typeName = attr->getTypeLoc().getType().getString(); } - TC.diagnose(attr->getLocation(), diag::unknown_attribute, - typeName); + diagnose(attr->getLocation(), diag::unknown_attribute, typeName); attr->setInvalid(); return; } @@ -2529,16 +2470,16 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { if (nominal->getPropertyWrapperTypeInfo()) { // property wrappers can only be applied to variables if (!isa(D) || isa(D)) { - TC.diagnose(attr->getLocation(), - diag::property_wrapper_attribute_not_on_property, - nominal->getFullName()); + diagnose(attr->getLocation(), + diag::property_wrapper_attribute_not_on_property, + nominal->getFullName()); attr->setInvalid(); return; } - + return; } - + // If the nominal type is a function builder type, verify that D is a // function, storage with an explicit getter, or parameter of function type. if (nominal->getAttrs().hasAttribute()) { @@ -2549,38 +2490,53 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { decl = func; } else if (auto storage = dyn_cast(D)) { decl = storage; - auto getter = storage->getParsedAccessor(AccessorKind::Get); - if (!getter || !getter->hasBody()) { - TC.diagnose(attr->getLocation(), - diag::function_builder_attribute_on_storage_without_getter, - nominal->getFullName(), - isa(storage) ? 0 - : storage->getDeclContext()->isTypeContext() ? 1 - : cast(storage)->isLet() ? 2 : 3); + + // Check whether this is a property without an explicit getter. + auto shouldDiagnose = [&]() -> bool { + auto getter = storage->getParsedAccessor(AccessorKind::Get); + if (!getter) + return true; + + // Module interfaces don't print bodies for all getters, so allow getters + // that don't have a body if we're compiling a module interface. + SourceFile *parent = storage->getDeclContext()->getParentSourceFile(); + bool isInInterface = parent && parent->Kind == SourceFileKind::Interface; + if (!isInInterface && !getter->hasBody()) + return true; + + return false; + }; + + if (shouldDiagnose()) { + diagnose(attr->getLocation(), + diag::function_builder_attribute_on_storage_without_getter, + nominal->getFullName(), + isa(storage) ? 0 + : storage->getDeclContext()->isTypeContext() ? 1 + : cast(storage)->isLet() ? 2 : 3); attr->setInvalid(); return; } } else { - TC.diagnose(attr->getLocation(), - diag::function_builder_attribute_not_allowed_here, - nominal->getFullName()); + diagnose(attr->getLocation(), + diag::function_builder_attribute_not_allowed_here, + nominal->getFullName()); attr->setInvalid(); return; } // Diagnose and ignore arguments. if (attr->getArg()) { - TC.diagnose(attr->getLocation(), diag::function_builder_arguments) + diagnose(attr->getLocation(), diag::function_builder_arguments) .highlight(attr->getArg()->getSourceRange()); } // Complain if this isn't the primary function-builder attribute. auto attached = decl->getAttachedFunctionBuilder(); if (attached != attr) { - TC.diagnose(attr->getLocation(), diag::function_builder_multiple, - isa(decl)); - TC.diagnose(attached->getLocation(), - diag::previous_function_builder_here); + diagnose(attr->getLocation(), diag::function_builder_multiple, + isa(decl)); + diagnose(attached->getLocation(), diag::previous_function_builder_here); attr->setInvalid(); return; } else { @@ -2592,8 +2548,8 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } - TC.diagnose(attr->getLocation(), diag::nominal_type_not_attribute, - nominal->getDescriptiveKind(), nominal->getFullName()); + diagnose(attr->getLocation(), diag::nominal_type_not_attribute, + nominal->getDescriptiveKind(), nominal->getFullName()); nominal->diagnose(diag::decl_declared_here, nominal->getFullName()); attr->setInvalid(); } @@ -2665,9 +2621,9 @@ AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) { } if (!derivedInterfaceTy->isEqual(overrideInterfaceTy)) { - TC.diagnose(VD, diag::implementation_only_override_changed_type, - overrideInterfaceTy); - TC.diagnose(overridden, diag::overridden_here); + diagnose(VD, diag::implementation_only_override_changed_type, + overrideInterfaceTy); + diagnose(overridden, diag::overridden_here); return; } @@ -2676,14 +2632,76 @@ AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) { // it won't necessarily be able to say why. } +void AttributeChecker::visitNonEphemeralAttr(NonEphemeralAttr *attr) { + auto *param = cast(D); + auto type = param->getInterfaceType()->lookThroughSingleOptionalType(); + + // Can only be applied to Unsafe[...]Pointer types + if (type->getAnyPointerElementType()) + return; + + // ... or the protocol Self type. + auto *outerDC = param->getDeclContext()->getParent(); + if (outerDC->getSelfProtocolDecl() && + type->isEqual(outerDC->getProtocolSelfType())) { + return; + } + + diagnose(attr->getLocation(), diag::non_ephemeral_non_pointer_type); + attr->setInvalid(); +} + void TypeChecker::checkParameterAttributes(ParameterList *params) { for (auto param: *params) { checkDeclAttributes(param); } } +void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D, + ArrayRef Attrs) { + if (Attrs.empty()) + return; + auto &Ctx = D->getASTContext(); + std::map seenPlatforms; + + // Attrs are in the reverse order of the source order. We need to visit them + // in source order to diagnose the later attribute. + for (auto *Attr: Attrs) { + if (!Attr->isActivePlatform(Ctx)) + continue; + auto AtLoc = Attr->AtLoc; + auto Platform = Attr->Platform; + if (!seenPlatforms.insert({Platform, AtLoc}).second) { + // We've seen the platform before, emit error to the previous one which + // comes later in the source order. + diagnose(seenPlatforms[Platform], + diag::originally_defined_in_dupe_platform, + platformString(Platform)); + return; + } + static StringRef AttrName = "_originallyDefinedIn"; + if (!D->getDeclContext()->isModuleScopeContext()) { + diagnose(AtLoc, diag::originally_definedin_topleve_decl, AttrName); + return; + } + auto IntroVer = D->getIntroducedOSVersion(Platform); + if (!IntroVer.hasValue()) { + diagnose(AtLoc, diag::originally_definedin_need_available, + AttrName); + return; + } + if (IntroVer.getValue() >= Attr->MovedVersion) { + diagnose(AtLoc, + diag::originally_definedin_must_after_available_version, + AttrName); + return; + } + } +} + Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, ReferenceOwnershipAttr *attr) { + auto &Diags = var->getASTContext().Diags; auto *dc = var->getDeclContext(); // Don't check ownership attribute if the type is invalid. @@ -2699,9 +2717,8 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, switch (optionalityOf(ownershipKind)) { case ReferenceOwnershipOptionality::Disallowed: if (isOptional) { - diagnose(var->getStartLoc(), diag::invalid_ownership_with_optional, - ownershipKind) - .fixItReplace(attr->getRange(), "weak"); + var->diagnose(diag::invalid_ownership_with_optional, ownershipKind) + .fixItReplace(attr->getRange(), "weak"); attr->setInvalid(); } break; @@ -2709,8 +2726,7 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, break; case ReferenceOwnershipOptionality::Required: if (var->isLet()) { - diagnose(var->getStartLoc(), diag::invalid_ownership_is_let, - ownershipKind); + var->diagnose(diag::invalid_ownership_is_let, ownershipKind); attr->setInvalid(); } @@ -2722,10 +2738,8 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, if (var->getAttrs().hasAttribute()) break; - auto diag = diagnose(var->getStartLoc(), - diag::invalid_ownership_not_optional, - ownershipKind, - OptionalType::get(type)); + auto diag = var->diagnose(diag::invalid_ownership_not_optional, + ownershipKind, OptionalType::get(type)); auto typeRange = var->getTypeSourceRangeForDiagnostics(); if (type->hasSimpleTypeRepr()) { diag.fixItInsertAfter(typeRange.End, "?"); @@ -2750,16 +2764,17 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, D = diag::invalid_ownership_protocol_type; } - diagnose(var->getStartLoc(), D, ownershipKind, underlyingType); + var->diagnose(D, ownershipKind, underlyingType); attr->setInvalid(); } ClassDecl *underlyingClass = underlyingType->getClassOrBoundGenericClass(); if (underlyingClass && underlyingClass->isIncompatibleWithWeakReferences()) { - diagnose(attr->getLocation(), - diag::invalid_ownership_incompatible_class, - underlyingType, ownershipKind) - .fixItRemove(attr->getRange()); + Diags + .diagnose(attr->getLocation(), + diag::invalid_ownership_incompatible_class, underlyingType, + ownershipKind) + .fixItRemove(attr->getRange()); attr->setInvalid(); } @@ -2767,11 +2782,11 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, if (PDC && !PDC->isObjC()) { // Ownership does not make sense in protocols, except for "weak" on // properties of Objective-C protocols. - auto D = Context.isSwiftVersionAtLeast(5) - ? diag::ownership_invalid_in_protocols - : diag::ownership_invalid_in_protocols_compat_warning; - diagnose(attr->getLocation(), D, ownershipKind) - .fixItRemove(attr->getRange()); + auto D = var->getASTContext().isSwiftVersionAtLeast(5) + ? diag::ownership_invalid_in_protocols + : diag::ownership_invalid_in_protocols_compat_warning; + Diags.diagnose(attr->getLocation(), D, ownershipKind) + .fixItRemove(attr->getRange()); attr->setInvalid(); } @@ -2779,7 +2794,7 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, return type; // Change the type to the appropriate reference storage type. - return ReferenceStorageType::get(type, ownershipKind, Context); + return ReferenceStorageType::get(type, ownershipKind, var->getASTContext()); } Optional> @@ -2881,3 +2896,1421 @@ void TypeChecker::addImplicitDynamicAttribute(Decl *D) { D->getAttrs().add(attr); } } + +llvm::Expected +DynamicallyReplacedDeclRequest::evaluate(Evaluator &evaluator, + ValueDecl *VD) const { + // Dynamic replacements must be explicit. + if (VD->isImplicit()) + return nullptr; + + auto *attr = VD->getAttrs().getAttribute(); + if (!attr) { + // It's likely that the accessor isn't annotated but its storage is. + if (auto *AD = dyn_cast(VD)) { + // Try to grab the attribute from the storage. + attr = AD->getStorage()->getAttrs().getAttribute(); + } + + if (!attr) { + // Otherwise, it's not dynamically replacing anything. + return nullptr; + } + } + + // If the attribute is invalid, bail. + if (attr->isInvalid()) + return nullptr; + + // If we can lazily resolve the function, do so now. + if (auto *LazyResolver = attr->Resolver) { + auto decl = attr->Resolver->loadDynamicallyReplacedFunctionDecl( + attr, attr->ResolverContextData); + attr->Resolver = nullptr; + return decl; + } + + auto &Ctx = VD->getASTContext(); + if (auto *AD = dyn_cast(VD)) { + return findReplacedAccessor(attr->getReplacedFunctionName(), AD, attr, Ctx); + } + + if (auto *AFD = dyn_cast(VD)) { + return findReplacedFunction(attr->getReplacedFunctionName(), AFD, + attr, &Ctx.Diags); + } + + if (auto *SD = dyn_cast(VD)) { + return findReplacedStorageDecl(attr->getReplacedFunctionName(), SD, attr); + } + + return nullptr; +} + +/// Returns true if the given type conforms to `Differentiable` in the given +/// module. +static bool conformsToDifferentiable(Type type, DeclContext *DC) { + auto &ctx = type->getASTContext(); + auto *differentiableProto = + ctx.getProtocol(KnownProtocolKind::Differentiable); + auto conf = TypeChecker::conformsToProtocol( + type, differentiableProto, DC, ConformanceCheckFlags::InExpression); + if (!conf) + return false; + // Try to get the `TangentVector` type witness, in case the conformance has + // not been fully checked and the type witness cannot be resolved. + Type tanType = conf.getTypeWitnessByName(type, ctx.Id_TangentVector); + return !tanType.isNull() && !tanType->hasError(); +}; + +IndexSubset *TypeChecker::inferDifferentiabilityParameters( + AbstractFunctionDecl *AFD, GenericEnvironment *derivativeGenEnv) { + auto &ctx = AFD->getASTContext(); + auto *functionType = AFD->getInterfaceType()->castTo(); + auto numUncurriedParams = functionType->getNumParams(); + if (auto *resultFnType = + functionType->getResult()->getAs()) { + numUncurriedParams += resultFnType->getNumParams(); + } + llvm::SmallBitVector parameterBits(numUncurriedParams); + SmallVector allParamTypes; + + // Returns true if the i-th parameter type is differentiable. + auto isDifferentiableParam = [&](unsigned i) -> bool { + if (i >= allParamTypes.size()) + return false; + auto paramType = allParamTypes[i]; + if (derivativeGenEnv) + paramType = derivativeGenEnv->mapTypeIntoContext(paramType); + else + paramType = AFD->mapTypeIntoContext(paramType); + // Return false for existential types. + if (paramType->isExistentialType()) + return false; + // Return true if the type conforms to `Differentiable`. + return conformsToDifferentiable(paramType, AFD); + }; + + // Get all parameter types. + // NOTE: To be robust, result function type parameters should be added only if + // `functionType` comes from a static/instance method, and not a free function + // returning a function type. In practice, this code path should not be + // reachable for free functions returning a function type. + if (auto resultFnType = functionType->getResult()->getAs()) + for (auto ¶m : resultFnType->getParams()) + allParamTypes.push_back(param.getPlainType()); + for (auto ¶m : functionType->getParams()) + allParamTypes.push_back(param.getPlainType()); + + // Set differentiability parameters. + for (unsigned i : range(parameterBits.size())) + if (isDifferentiableParam(i)) + parameterBits.set(i); + + return IndexSubset::get(ctx, parameterBits); +} + +// Computes the differentiability parameter indices from the given parsed +// differentiability parameters for the given original or derivative +// `AbstractFunctionDecl` and derivative generic environment. On error, emits +// diagnostics and returns `nullptr`. +// - If parsed parameters are empty, infer parameter indices. +// - Otherwise, build parameter indices from parsed parameters. +// The attribute name/location are used in diagnostics. +static IndexSubset *computeDifferentiabilityParameters( + ArrayRef parsedDiffParams, + AbstractFunctionDecl *function, GenericEnvironment *derivativeGenEnv, + StringRef attrName, SourceLoc attrLoc) { + auto &ctx = function->getASTContext(); + auto &diags = ctx.Diags; + + // Get function type and parameters. + auto *functionType = function->getInterfaceType()->castTo(); + auto ¶ms = *function->getParameters(); + auto numParams = function->getParameters()->size(); + auto isInstanceMethod = function->isInstanceMember(); + + // Diagnose if function has no parameters. + if (params.size() == 0) { + // If function is not an instance method, diagnose immediately. + if (!isInstanceMethod) { + diags + .diagnose(attrLoc, diag::diff_function_no_parameters, + function->getFullName()) + .highlight(function->getSignatureSourceRange()); + return nullptr; + } + // If function is an instance method, diagnose only if `self` does not + // conform to `Differentiable`. + else { + auto selfType = function->getImplicitSelfDecl()->getInterfaceType(); + if (derivativeGenEnv) + selfType = derivativeGenEnv->mapTypeIntoContext(selfType); + else + selfType = function->mapTypeIntoContext(selfType); + if (!conformsToDifferentiable(selfType, function)) { + diags + .diagnose(attrLoc, diag::diff_function_no_parameters, + function->getFullName()) + .highlight(function->getSignatureSourceRange()); + return nullptr; + } + } + } + + // If parsed differentiability parameters are empty, infer parameter indices + // from the function type. + if (parsedDiffParams.empty()) + return TypeChecker::inferDifferentiabilityParameters(function, + derivativeGenEnv); + + // Otherwise, build parameter indices from parsed differentiability + // parameters. + auto numUncurriedParams = functionType->getNumParams(); + if (auto *resultFnType = + functionType->getResult()->getAs()) { + numUncurriedParams += resultFnType->getNumParams(); + } + llvm::SmallBitVector parameterBits(numUncurriedParams); + int lastIndex = -1; + for (unsigned i : indices(parsedDiffParams)) { + auto paramLoc = parsedDiffParams[i].getLoc(); + switch (parsedDiffParams[i].getKind()) { + case ParsedAutoDiffParameter::Kind::Named: { + auto nameIter = llvm::find_if(params.getArray(), [&](ParamDecl *param) { + return param->getName() == parsedDiffParams[i].getName(); + }); + // Parameter name must exist. + if (nameIter == params.end()) { + diags.diagnose(paramLoc, diag::diff_params_clause_param_name_unknown, + parsedDiffParams[i].getName()); + return nullptr; + } + // Parameter names must be specified in the original order. + unsigned index = std::distance(params.begin(), nameIter); + if ((int)index <= lastIndex) { + diags.diagnose(paramLoc, + diag::diff_params_clause_params_not_original_order); + return nullptr; + } + parameterBits.set(index); + lastIndex = index; + break; + } + case ParsedAutoDiffParameter::Kind::Self: { + // 'self' is only applicable to instance methods. + if (!isInstanceMethod) { + diags.diagnose(paramLoc, + diag::diff_params_clause_self_instance_method_only); + return nullptr; + } + // 'self' can only be the first in the list. + if (i > 0) { + diags.diagnose(paramLoc, diag::diff_params_clause_self_must_be_first); + return nullptr; + } + parameterBits.set(parameterBits.size() - 1); + break; + } + case ParsedAutoDiffParameter::Kind::Ordered: { + auto index = parsedDiffParams[i].getIndex(); + if (index >= numParams) { + diags.diagnose(paramLoc, + diag::diff_params_clause_param_index_out_of_range); + return nullptr; + } + // Parameter names must be specified in the original order. + if ((int)index <= lastIndex) { + diags.diagnose(paramLoc, + diag::diff_params_clause_params_not_original_order); + return nullptr; + } + parameterBits.set(index); + lastIndex = index; + break; + } + } + } + return IndexSubset::get(ctx, parameterBits); +} + +// Checks if the given differentiability parameter indices are valid for the +// given original or derivative `AbstractFunctionDecl` and original function +// type in the given derivative generic environment and module context. Returns +// true on error. +// +// The parsed differentiability parameters and attribute location are used in +// diagnostics. +static bool checkDifferentiabilityParameters( + AbstractFunctionDecl *AFD, IndexSubset *diffParamIndices, + AnyFunctionType *functionType, GenericEnvironment *derivativeGenEnv, + ModuleDecl *module, ArrayRef parsedDiffParams, + SourceLoc attrLoc) { + auto &ctx = AFD->getASTContext(); + auto &diags = ctx.Diags; + + // Diagnose empty differentiability indices. No differentiability parameters + // were resolved or inferred. + if (diffParamIndices->isEmpty()) { + diags.diagnose(attrLoc, diag::diff_params_clause_no_inferred_parameters); + return true; + } + + // Check that differentiability parameters have allowed types. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(diffParamIndices, functionType, + diffParamTypes); + for (unsigned i : range(diffParamTypes.size())) { + SourceLoc loc = + parsedDiffParams.empty() ? attrLoc : parsedDiffParams[i].getLoc(); + auto diffParamType = diffParamTypes[i]; + // `inout` parameters are not yet supported. + if (diffParamType->is()) { + diags.diagnose(loc, + diag::diff_params_clause_cannot_diff_wrt_inout_parameter, + diffParamType); + return true; + } + if (!diffParamType->hasTypeParameter()) + diffParamType = diffParamType->mapTypeOutOfContext(); + if (derivativeGenEnv) + diffParamType = derivativeGenEnv->mapTypeIntoContext(diffParamType); + else + diffParamType = AFD->mapTypeIntoContext(diffParamType); + // Parameter must conform to `Differentiable`. + if (!conformsToDifferentiable(diffParamType, AFD)) { + diags.diagnose(loc, diag::diff_params_clause_param_not_differentiable, + diffParamType); + return true; + } + } + return false; +} + +// Returns the function declaration corresponding to the given function name and +// lookup context. If the base type of the function is specified, member lookup +// is performed. Otherwise, unqualified lookup is performed. +// If the function declaration cannot be resolved, emits a diagnostic and +// returns nullptr. +static AbstractFunctionDecl *findAbstractFunctionDecl( + DeclNameRef funcName, SourceLoc funcNameLoc, Type baseType, + DeclContext *lookupContext, + const std::function &isValidCandidate, + const std::function &noneValidDiagnostic, + const std::function &ambiguousDiagnostic, + const std::function ¬FunctionDiagnostic, + NameLookupOptions lookupOptions, + const Optional> + &hasValidTypeCtx, + const Optional> &invalidTypeCtxDiagnostic) { + auto &ctx = lookupContext->getASTContext(); + AbstractFunctionDecl *resolvedCandidate = nullptr; + + // Perform lookup. + LookupResult results; + // If `baseType` is not null but `lookupContext` is a type context, set + // `baseType` to the `self` type of `lookupContext` to perform member lookup. + if (!baseType && lookupContext->isTypeContext()) + baseType = lookupContext->getSelfTypeInContext(); + if (baseType) { + results = TypeChecker::lookupMember(lookupContext, baseType, funcName); + } else { + results = TypeChecker::lookupUnqualified(lookupContext, funcName, + funcNameLoc, lookupOptions); + } + + // Initialize error flags. + bool notFunction = false; + bool wrongTypeContext = false; + bool ambiguousFuncDecl = false; + bool foundInvalid = false; + + // Filter lookup results. + for (auto choice : results) { + auto decl = choice.getValueDecl(); + if (!decl) + continue; + // Cast the candidate to an `AbstractFunctionDecl`. + auto *candidate = dyn_cast(decl); + // If the candidate is an `AbstractStorageDecl`, use its getter as the + // candidate. + if (auto *asd = dyn_cast(decl)) + candidate = asd->getAccessor(AccessorKind::Get); + if (!candidate) { + notFunction = true; + continue; + } + if (hasValidTypeCtx && !(*hasValidTypeCtx)(candidate)) { + wrongTypeContext = true; + continue; + } + if (!isValidCandidate(candidate)) { + foundInvalid = true; + continue; + } + if (resolvedCandidate) { + ambiguousFuncDecl = true; + resolvedCandidate = nullptr; + break; + } + resolvedCandidate = candidate; + } + // If function declaration was resolved, return it. + if (resolvedCandidate) + return resolvedCandidate; + + // Otherwise, emit the appropriate diagnostic and return nullptr. + if (results.empty()) { + ctx.Diags.diagnose(funcNameLoc, diag::use_unresolved_identifier, funcName, + funcName.isOperator()); + return nullptr; + } + if (ambiguousFuncDecl) { + ambiguousDiagnostic(); + return nullptr; + } + if (wrongTypeContext) { + assert(invalidTypeCtxDiagnostic && + "Type context diagnostic should've been specified"); + (*invalidTypeCtxDiagnostic)(); + return nullptr; + } + if (foundInvalid) { + noneValidDiagnostic(); + return nullptr; + } + assert(notFunction && "Expected 'not a function' error"); + notFunctionDiagnostic(); + return nullptr; +} + +// Checks that the `candidate` function type equals the `required` function +// type, disregarding parameter labels and tuple result labels. +// `checkGenericSignature` is used to check generic signatures, if specified. +// Otherwise, generic signatures are checked for equality. +static bool checkFunctionSignature( + CanAnyFunctionType required, CanType candidate, + Optional> + checkGenericSignature = None) { + // Check that candidate is actually a function. + auto candidateFnTy = dyn_cast(candidate); + if (!candidateFnTy) + return false; + + // Erase dynamic self types. + required = dyn_cast(required->getCanonicalType()); + candidateFnTy = dyn_cast(candidateFnTy->getCanonicalType()); + + // Check that generic signatures match. + auto requiredGenSig = required.getOptGenericSignature(); + auto candidateGenSig = candidateFnTy.getOptGenericSignature(); + // Call generic signature check function, if specified. + // Otherwise, check that generic signatures are equal. + if (!checkGenericSignature) { + if (candidateGenSig != requiredGenSig) + return false; + } else if (!(*checkGenericSignature)(requiredGenSig, candidateGenSig)) { + return false; + } + + // Map type into the required function type's generic signature, if it exists. + // This is significant when the required generic signature has same-type + // requirements while the candidate generic signature does not. + auto mapType = [&](Type type) { + if (!requiredGenSig) + return type->getCanonicalType(); + return requiredGenSig->getCanonicalTypeInContext(type); + }; + + // Check that parameter types match, disregarding labels. + if (required->getNumParams() != candidateFnTy->getNumParams()) + return false; + if (!std::equal(required->getParams().begin(), required->getParams().end(), + candidateFnTy->getParams().begin(), + [&](AnyFunctionType::Param x, AnyFunctionType::Param y) { + return x.getPlainType()->isEqual(mapType(y.getPlainType())); + })) + return false; + + // If required result type is not a function type, check that result types + // match exactly. + auto requiredResultFnTy = dyn_cast(required.getResult()); + auto candidateResultTy = mapType(candidateFnTy.getResult()); + if (!requiredResultFnTy) { + auto requiredResultTupleTy = dyn_cast(required.getResult()); + auto candidateResultTupleTy = dyn_cast(candidateResultTy); + if (!requiredResultTupleTy || !candidateResultTupleTy) + return required.getResult()->isEqual(candidateResultTy); + // If result types are tuple types, check that element types match, + // ignoring labels. + if (requiredResultTupleTy->getNumElements() != + candidateResultTupleTy->getNumElements()) + return false; + return std::equal(requiredResultTupleTy.getElementTypes().begin(), + requiredResultTupleTy.getElementTypes().end(), + candidateResultTupleTy.getElementTypes().begin(), + [](CanType x, CanType y) { return x->isEqual(y); }); + } + + // Required result type is a function. Recurse. + return checkFunctionSignature(requiredResultFnTy, candidateResultTy); +}; + +// Returns an `AnyFunctionType` from the given parameters, result type, and +// generic signature. +static AnyFunctionType * +makeFunctionType(ArrayRef parameters, Type resultType, + GenericSignature genericSignature) { + if (genericSignature) + return GenericFunctionType::get(genericSignature, parameters, resultType); + return FunctionType::get(parameters, resultType); +} + +// Computes the original function type corresponding to the given derivative +// function type. Used for `@derivative` attribute type-checking. +static AnyFunctionType * +getDerivativeOriginalFunctionType(AnyFunctionType *derivativeFnTy) { + // Unwrap curry levels. At most, two parameter lists are necessary, for + // curried method types with a `(Self)` parameter list. + SmallVector curryLevels; + auto *currentLevel = derivativeFnTy; + for (unsigned i : range(2)) { + (void)i; + if (currentLevel == nullptr) + break; + curryLevels.push_back(currentLevel); + currentLevel = currentLevel->getResult()->getAs(); + } + + auto derivativeResult = curryLevels.back()->getResult()->getAs(); + assert(derivativeResult && derivativeResult->getNumElements() == 2 && + "Expected derivative result to be a two-element tuple"); + auto originalResult = derivativeResult->getElement(0).getType(); + auto *originalType = makeFunctionType( + curryLevels.back()->getParams(), originalResult, + curryLevels.size() == 1 ? derivativeFnTy->getOptGenericSignature() + : nullptr); + + // Wrap the derivative function type in additional curry levels. + auto curryLevelsWithoutLast = + ArrayRef(curryLevels).drop_back(1); + for (auto pair : enumerate(llvm::reverse(curryLevelsWithoutLast))) { + unsigned i = pair.index(); + AnyFunctionType *curryLevel = pair.value(); + originalType = + makeFunctionType(curryLevel->getParams(), originalType, + i == curryLevelsWithoutLast.size() - 1 + ? derivativeFnTy->getOptGenericSignature() + : nullptr); + } + return originalType; +} + +// Finds a derivative function declaration using the given function specifier, +// original function declaration, expected type, and "is valid" predicate. If no +// valid derivative function is found, emits diagnostics and returns false. +static FuncDecl *findAutoDiffDerivativeFunction( + DeclNameRefWithLoc specifier, AbstractFunctionDecl *original, + Type expectedTy, std::function isValid) { + auto &ctx = original->getASTContext(); + auto &diags = ctx.Diags; + auto noneValidDiagnostic = [&]() { + diags.diagnose(specifier.Loc, diag::differentiable_attr_overload_not_found, + specifier.Name, expectedTy); + }; + auto ambiguousDiagnostic = [&]() { + diags.diagnose(specifier.Loc, diag::attr_ambiguous_reference_to_decl, + specifier.Name, "differentiable"); + }; + auto notFunctionDiagnostic = [&]() { + diags.diagnose(specifier.Loc, + diag::differentiable_attr_derivative_not_function, + specifier.Name); + }; + std::function invalidTypeContextDiagnostic = [&]() { + diags.diagnose(specifier.Loc, + diag::differentiable_attr_function_not_same_type_context, + specifier.Name); + }; + + // Returns true if the original function and derivative function candidate are + // defined in compatible type contexts. If the original function and the + // derivative function have different parents, or if they both have no type + // context and are in different modules, return false. + std::function hasValidTypeContext = + [&](AbstractFunctionDecl *func) { + // Check if both functions are top-level. + if (!original->getInnermostTypeContext() && + !func->getInnermostTypeContext() && + original->getParentModule() == func->getParentModule()) + return true; + // Check if both functions are defined in the same type context. + if (auto typeCtx1 = original->getInnermostTypeContext()) + if (auto typeCtx2 = func->getInnermostTypeContext()) + return typeCtx1->getSelfNominalTypeDecl() == + typeCtx2->getSelfNominalTypeDecl(); + return original->getParent() == func->getParent(); + }; + + auto isABIPublic = [&](AbstractFunctionDecl *func) { + return func->getFormalAccess() >= AccessLevel::Public || + func->getAttrs().hasAttribute() || + func->getAttrs().hasAttribute(); + }; + + // If the original function is exported (i.e. it is public or + // `@usableFromInline`), then the derivative functions must also be exported. + // Returns true on error. + auto checkAccessControl = [&](AbstractFunctionDecl *func) { + if (!isABIPublic(original)) + return false; + if (isABIPublic(func)) + return false; + diags.diagnose(specifier.Loc, diag::differentiable_attr_invalid_access, + specifier.Name, original->getFullName()); + return true; + }; + + auto originalTypeCtx = original->getInnermostTypeContext(); + if (!originalTypeCtx) + originalTypeCtx = original->getParent(); + assert(originalTypeCtx); + + // Set lookup options. + auto lookupOptions = + defaultMemberLookupOptions | NameLookupFlags::IgnoreAccessControl; + + auto *candidate = findAbstractFunctionDecl( + specifier.Name, specifier.Loc.getBaseNameLoc(), /*baseType*/ Type(), + originalTypeCtx, isValid, noneValidDiagnostic, ambiguousDiagnostic, + notFunctionDiagnostic, lookupOptions, hasValidTypeContext, + invalidTypeContextDiagnostic); + if (!candidate) + return nullptr; + // Reject non-`func` registered derivatives. JVPs and VJPs must be `func` + // declarations. + if (isa(candidate)) { + diags.diagnose(specifier.Loc, + diag::differentiable_attr_derivative_not_function, + specifier.Name); + return nullptr; + } + if (checkAccessControl(candidate)) + return nullptr; + // Derivatives of class members must be final. + if (original->getDeclContext()->getSelfClassDecl() && !candidate->isFinal()) { + diags.diagnose(specifier.Loc, + diag::differentiable_attr_class_derivative_not_final); + return nullptr; + } + assert(isa(candidate)); + auto *funcDecl = cast(candidate); + return funcDecl; +} + +/// Given a `@differentiable` attribute, attempts to resolve the original +/// `AbstractFunctionDecl` for which it is registered, using the declaration +/// on which it is actually declared. On error, emits diagnostic and returns +/// `nullptr`. +AbstractFunctionDecl * +resolveDifferentiableAttrOriginalFunction(DifferentiableAttr *attr) { + auto *D = attr->getOriginalDeclaration(); + assert(D && + "Original declaration should be resolved by parsing/deserialization"); + auto &ctx = D->getASTContext(); + auto &diags = ctx.Diags; + auto *original = dyn_cast(D); + if (auto *asd = dyn_cast(D)) { + // Derivative registration is unsupported for stored properties. + if (asd->getImplInfo().isSimpleStored() && + (attr->getJVP() || attr->getVJP())) { + diagnoseAndRemoveAttr( + diags, D, attr, + diag::differentiable_attr_stored_property_variable_unsupported); + attr->setInvalid(); + return nullptr; + } + // If `@differentiable` attribute is declared directly on a + // `AbstractStorageDecl` (a stored/computed property or subscript), + // forward the attribute to the storage's getter. + // TODO(TF-129): Forward `@differentiable` attributes to setters after + // differentiation supports inout parameters. + // TODO(TF-1080): Forward `@differentiable` attributes to `read` and + // `modify` accessors after differentiation supports `inout` parameters. + if (!asd->getDeclContext()->isModuleScopeContext()) { + original = asd->getSynthesizedAccessor(AccessorKind::Get); + } else { + original = nullptr; + } + } + // Non-`get` accessors are not yet supported: `set`, `read`, and `modify`. + // TODO(TF-129): Enable `set` when differentiation supports inout parameters. + // TODO(TF-1080): Enable `read` and `modify` when differentiation supports + // coroutines. + if (auto *accessor = dyn_cast_or_null(original)) + if (!accessor->isGetter()) + original = nullptr; + // Diagnose if original `AbstractFunctionDecl` could not be resolved. + if (!original) { + diagnoseAndRemoveAttr(diags, D, attr, diag::invalid_decl_attribute, attr); + attr->setInvalid(); + return nullptr; + } + // If the original function has an error interface type, return. + // A diagnostic should have already been emitted. + if (original->getInterfaceType()->hasError()) + return nullptr; + return original; +} + +/// Given a `@differentiable` attribute, attempts to resolve the derivative +/// generic signature. The derivative generic signature is returned as +/// `derivativeGenSig`. On error, emits diagnostic, assigns `nullptr` to +/// `derivativeGenSig`, and returns true. +bool resolveDifferentiableAttrDerivativeGenericSignature( + DifferentiableAttr *attr, AbstractFunctionDecl *original, + GenericSignature &derivativeGenSig) { + derivativeGenSig = nullptr; + + auto &ctx = original->getASTContext(); + auto &diags = ctx.Diags; + + bool isOriginalProtocolRequirement = + isa(original->getDeclContext()) && + original->isProtocolRequirement(); + + // Compute the derivative generic signature for the `@differentiable` + // attribute: + // - If the `@differentiable` attribute has a `where` clause, use it to + // compute the derivative generic signature. + // - Otherwise, use the original function's generic signature by default. + derivativeGenSig = original->getGenericSignature(); + + // Handle the `where` clause, if it exists. + // - Resolve attribute where clause requirements and store in the attribute + // for serialization. + // - Compute generic signature for autodiff derivative functions based on + // the original function's generate signature and the attribute's where + // clause requirements. + if (auto *whereClause = attr->getWhereClause()) { + // `@differentiable` attributes on protocol requirements do not support + // `where` clauses. + if (isOriginalProtocolRequirement) { + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_protocol_req_where_clause); + attr->setInvalid(); + return true; + } + if (whereClause->getRequirements().empty()) { + // `where` clause must not be empty. + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_empty_where_clause); + attr->setInvalid(); + return true; + } + + auto originalGenSig = original->getGenericSignature(); + if (!originalGenSig) { + // `where` clauses are valid only when the original function is generic. + diags + .diagnose( + attr->getLocation(), + diag::differentiable_attr_where_clause_for_nongeneric_original, + original->getFullName()) + .highlight(whereClause->getSourceRange()); + attr->setInvalid(); + return true; + } + + // Build a new generic signature for autodiff derivative functions. + GenericSignatureBuilder builder(ctx); + // Add the original function's generic signature. + builder.addGenericSignature(originalGenSig); + + using FloatingRequirementSource = + GenericSignatureBuilder::FloatingRequirementSource; + + bool errorOccurred = false; + WhereClauseOwner(original, attr) + .visitRequirements( + TypeResolutionStage::Structural, + [&](const Requirement &req, RequirementRepr *reqRepr) { + switch (req.getKind()) { + case RequirementKind::SameType: + case RequirementKind::Superclass: + case RequirementKind::Conformance: + break; + + // Layout requirements are not supported. + case RequirementKind::Layout: + diags + .diagnose(attr->getLocation(), + diag::differentiable_attr_layout_req_unsupported) + .highlight(reqRepr->getSourceRange()); + errorOccurred = true; + return false; + } + + // Add requirement to generic signature builder. + builder.addRequirement( + req, reqRepr, FloatingRequirementSource::forExplicit(reqRepr), + nullptr, original->getModuleContext()); + return false; + }); + + if (errorOccurred) { + attr->setInvalid(); + return true; + } + + // Compute generic signature for derivative functions. + derivativeGenSig = std::move(builder).computeGenericSignature( + attr->getLocation(), /*allowConcreteGenericParams=*/true); + } + + // Set the resolved derivative generic signature in the attribute. + // Do not set the derivative generic signature if the original function's + // generic signature is equal to `derivativeGenSig` and all generic parameters + // are concrete. In that case, the original function and derivative functions + // are all lowered as SIL functions with no generic signature (specialized + // with concrete types from same-type requirements), so the derivative generic + // signature should not be set. + auto skipDerivativeGenericSignature = [&] { + auto origCanGenSig = + original->getGenericSignature().getCanonicalSignature(); + auto derivativeCanGenSig = derivativeGenSig.getCanonicalSignature(); + if (!derivativeCanGenSig) + return false; + return origCanGenSig == derivativeCanGenSig && + derivativeCanGenSig->areAllParamsConcrete(); + }; + if (skipDerivativeGenericSignature()) + derivativeGenSig = GenericSignature(); + attr->setDerivativeGenericSignature(derivativeGenSig); + return false; +} + +/// Given a `@differentiable` attribute, attempts to resolve and validate the +/// differentiability parameter indices. The parameter indices are returned as +/// `diffParamIndices`. On error, emits diagnostic, assigns `nullptr` to +/// `diffParamIndices`, and returns true. +bool resolveDifferentiableAttrDifferentiabilityParameters( + DifferentiableAttr *attr, AbstractFunctionDecl *original, + AnyFunctionType *derivativeFnTy, GenericEnvironment *derivativeGenEnv, + IndexSubset *&diffParamIndices) { + diffParamIndices = nullptr; + + // Get the parsed differentiability parameter indices, which have not yet been + // resolved. Parsed differentiability parameter indices are defined only for + // parsed attributes. + auto parsedDiffParams = attr->getParsedParameters(); + + diffParamIndices = computeDifferentiabilityParameters( + parsedDiffParams, original, derivativeGenEnv, attr->getAttrName(), + attr->getLocation()); + if (!diffParamIndices) { + attr->setInvalid(); + return true; + } + + // Check if differentiability parameter indices are valid. + if (checkDifferentiabilityParameters(original, diffParamIndices, + derivativeFnTy, derivativeGenEnv, + original->getModuleContext(), + parsedDiffParams, attr->getLocation())) { + attr->setInvalid(); + return true; + } + + return false; +} + +/// Given a `@differentiable` attribute, attempts to resolve the JVP and VJP +/// derivative function declarations, if specified. The JVP and VJP functions +/// are returned as `jvp` and `vjp`, respectively. On error, emits diagnostic, +/// assigns `nullptr` to `jvp` and `vjp`, and returns true. +bool resolveDifferentiableAttrDerivativeFunctions( + DifferentiableAttr *attr, AbstractFunctionDecl *original, + IndexSubset *resolvedDiffParamIndices, GenericSignature derivativeGenSig, + FuncDecl *&jvp, FuncDecl *&vjp) { + jvp = nullptr; + vjp = nullptr; + + auto &ctx = original->getASTContext(); + auto &diags = ctx.Diags; + + // `@differentiable` attributes on protocol requirements do not support + // JVP/VJP. + bool isOriginalProtocolRequirement = + isa(original->getDeclContext()) && + original->isProtocolRequirement(); + if (isOriginalProtocolRequirement && (attr->getJVP() || attr->getVJP())) { + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_protocol_req_assoc_func); + attr->setInvalid(); + return false; + } + + auto *originalFnTy = original->getInterfaceType()->castTo(); + auto lookupConformance = + LookUpConformanceInModule(original->getDeclContext()->getParentModule()); + + // Resolve the JVP function, if it is specified and exists. + if (attr->getJVP()) { + auto *expectedJVPFnTy = originalFnTy->getAutoDiffDerivativeFunctionType( + resolvedDiffParamIndices, /*resultIndex*/ 0, + AutoDiffDerivativeFunctionKind::JVP, lookupConformance, + derivativeGenSig, /*makeSelfParamFirst*/ true); + auto isValidJVP = [&](AbstractFunctionDecl *jvpCandidate) -> bool { + return checkFunctionSignature( + cast(expectedJVPFnTy->getCanonicalType()), + jvpCandidate->getInterfaceType()->getCanonicalType()); + }; + auto *jvp = findAutoDiffDerivativeFunction( + attr->getJVP().getValue(), original, expectedJVPFnTy, isValidJVP); + if (!jvp) { + attr->setInvalid(); + return true; + } + // Set the JVP function in the attribute. + attr->setJVPFunction(jvp); + } + + // Resolve the VJP function, if it is specified and exists. + if (attr->getVJP()) { + auto *expectedVJPFnTy = originalFnTy->getAutoDiffDerivativeFunctionType( + resolvedDiffParamIndices, /*resultIndex*/ 0, + AutoDiffDerivativeFunctionKind::VJP, lookupConformance, + derivativeGenSig, /*makeSelfParamFirst*/ true); + auto isValidVJP = [&](AbstractFunctionDecl *vjpCandidate) -> bool { + return checkFunctionSignature( + cast(expectedVJPFnTy->getCanonicalType()), + vjpCandidate->getInterfaceType()->getCanonicalType()); + }; + auto *vjp = findAutoDiffDerivativeFunction( + attr->getVJP().getValue(), original, expectedVJPFnTy, isValidVJP); + if (!vjp) { + attr->setInvalid(); + return true; + } + // Set the VJP function in the attribute. + attr->setVJPFunction(vjp); + } + + return false; +} + +llvm::Expected DifferentiableAttributeTypeCheckRequest::evaluate( + Evaluator &evaluator, DifferentiableAttr *attr) const { + // Skip type-checking for implicit `@differentiable` attributes. We currently + // assume that all implicit `@differentiable` attributes are valid. + // + // Motivation: some implicit attributes do not have a `where` clause, and this + // function assumes that the `where` clauses exist. Propagating `where` + // clauses and requirements consistently is a larger problem, to be revisited. + if (attr->isImplicit()) + return nullptr; + + auto *D = attr->getOriginalDeclaration(); + auto &ctx = D->getASTContext(); + auto &diags = ctx.Diags; + // `@differentiable` attribute requires experimental differentiable + // programming to be enabled. + if (!ctx.LangOpts.EnableExperimentalDifferentiableProgramming) { + diags + .diagnose(attr->getLocation(), + diag::experimental_differentiable_programming_disabled) + .highlight(attr->getRangeWithAt()); + return nullptr; + } + // The `Differentiable` protocol must be available. + if (!ctx.getProtocol(KnownProtocolKind::Differentiable)) { + diags + .diagnose(attr->getLocation(), diag::attr_used_without_required_module, + attr, ctx.Id_Differentiation) + .highlight(attr->getRangeWithAt()); + return nullptr; + } + + // Derivative registration is disabled for `@differentiable(linear)` + // attributes. Instead, use `@transpose` attribute to register transpose + // functions. + if (attr->isLinear() && (attr->getVJP() || attr->getJVP())) { + diagnoseAndRemoveAttr(diags, D, attr, + diag::differentiable_attr_no_vjp_or_jvp_when_linear); + attr->setInvalid(); + return nullptr; + } + + // Resolve the original `AbstractFunctionDecl`. + auto *original = resolveDifferentiableAttrOriginalFunction(attr); + if (!original) + return nullptr; + + auto *originalFnTy = original->getInterfaceType()->castTo(); + bool isMethod = original->hasImplicitSelfDecl(); + + // If the original function returns the empty tuple type, there is no output + // to differentiate from. + auto originalResultTy = originalFnTy->getResult(); + if (isMethod) + originalResultTy = originalResultTy->castTo()->getResult(); + if (originalResultTy->isEqual(ctx.TheEmptyTupleType)) { + diags + .diagnose(attr->getLocation(), diag::differentiable_attr_void_result, + original->getFullName()) + .highlight(original->getSourceRange()); + attr->setInvalid(); + return nullptr; + } + + bool isOriginalClassMember = original->getDeclContext() && + original->getDeclContext()->getSelfClassDecl(); + + // Diagnose if original function is an invalid class member. + if (isOriginalClassMember) { + // Class methods returning dynamic `Self` are not supported. + // (For class methods, dynamic `Self` is supported only as the single + // result - tuple-returning JVPs/VJPs would not type-check.) + if (auto *originalFn = dyn_cast(original)) { + if (originalFn->hasDynamicSelfResult()) { + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_class_member_no_dynamic_self); + attr->setInvalid(); + return nullptr; + } + } + + // TODO(TF-654): Class initializers are not yet supported. + // Extra JVP/VJP type calculation logic is necessary because classes have + // both allocators and initializers. + if (auto *initDecl = dyn_cast(original)) { + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_class_init_not_yet_supported); + attr->setInvalid(); + return nullptr; + } + } + + // Resolve the derivative generic signature. + GenericSignature derivativeGenSig = nullptr; + if (resolveDifferentiableAttrDerivativeGenericSignature(attr, original, + derivativeGenSig)) + return nullptr; + GenericEnvironment *derivativeGenEnv = nullptr; + if (derivativeGenSig) + derivativeGenEnv = derivativeGenSig->getGenericEnvironment(); + + // Compute the derivative function type. + auto derivativeFnTy = originalFnTy; + if (derivativeGenEnv) + derivativeFnTy = derivativeGenEnv->mapTypeIntoContext(derivativeFnTy) + ->castTo(); + + // Resolve and validate the differentiability parameters. + IndexSubset *resolvedDiffParamIndices = nullptr; + if (resolveDifferentiableAttrDifferentiabilityParameters( + attr, original, derivativeFnTy, derivativeGenEnv, + resolvedDiffParamIndices)) + return nullptr; + + // Check that original function's result type conforms to `Differentiable`. + if (derivativeGenEnv) + originalResultTy = derivativeGenEnv->mapTypeIntoContext(originalResultTy); + else + originalResultTy = original->mapTypeIntoContext(originalResultTy); + if (!conformsToDifferentiable(originalResultTy, original)) { + diags.diagnose(attr->getLocation(), + diag::differentiable_attr_result_not_differentiable, + originalResultTy); + attr->setInvalid(); + return nullptr; + } + + // Resolve JVP and VJP derivative functions, if specified. + FuncDecl *jvp = nullptr; + FuncDecl *vjp = nullptr; + if (resolveDifferentiableAttrDerivativeFunctions( + attr, original, resolvedDiffParamIndices, derivativeGenSig, jvp, vjp)) + return nullptr; + + if (auto *asd = dyn_cast(D)) { + // Remove `@differentiable` attribute from storage declaration to prevent + // duplicate attribute registration during SILGen. + D->getAttrs().removeAttribute(attr); + // Transfer `@differentiable` attribute from storage declaration to + // getter accessor. + auto *getterDecl = asd->getAccessor(AccessorKind::Get); + auto *newAttr = DifferentiableAttr::create( + getterDecl, /*implicit*/ true, attr->AtLoc, attr->getRange(), + attr->isLinear(), resolvedDiffParamIndices, attr->getJVP(), + attr->getVJP(), attr->getDerivativeGenericSignature()); + newAttr->setJVPFunction(attr->getJVPFunction()); + newAttr->setVJPFunction(attr->getVJPFunction()); + auto insertion = ctx.DifferentiableAttrs.try_emplace( + {getterDecl, resolvedDiffParamIndices}, newAttr); + // Reject duplicate `@differentiable` attributes. + if (!insertion.second) { + diagnoseAndRemoveAttr(diags, D, attr, + diag::differentiable_attr_duplicate); + diags.diagnose(insertion.first->getSecond()->getLocation(), + diag::differentiable_attr_duplicate_note); + return nullptr; + } + getterDecl->getAttrs().add(newAttr); + return resolvedDiffParamIndices; + } + // Reject duplicate `@differentiable` attributes. + auto insertion = + ctx.DifferentiableAttrs.try_emplace({D, resolvedDiffParamIndices}, attr); + if (!insertion.second && insertion.first->getSecond() != attr) { + diagnoseAndRemoveAttr(diags, D, attr, diag::differentiable_attr_duplicate); + diags.diagnose(insertion.first->getSecond()->getLocation(), + diag::differentiable_attr_duplicate_note); + return nullptr; + } + // Register derivative function configuration. + auto *resultIndices = IndexSubset::get(ctx, 1, {0}); + original->addDerivativeFunctionConfiguration( + {resolvedDiffParamIndices, resultIndices, derivativeGenSig}); + return resolvedDiffParamIndices; +} + +void AttributeChecker::visitDifferentiableAttr(DifferentiableAttr *attr) { + // Call `getParameterIndices` to trigger + // `DifferentiableAttributeTypeCheckRequest`. + (void)attr->getParameterIndices(); +} + +/// Typechecks the given derivative attribute `attr` on decl `D`. +/// +/// Effects are: +/// - Sets the original function and parameter indices on `attr`. +/// - Diagnoses errors. +/// - Stores the attribute in `ASTContext::DerivativeAttrs`. +/// +/// \returns true on error, false on success. +static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D, + DerivativeAttr *attr) { + // Note: Implementation must be idempotent because it can get called multiple + // times for the same attribute. + + auto &diags = Ctx.Diags; + + // `@derivative` attribute requires experimental differentiable programming + // to be enabled. + auto &ctx = D->getASTContext(); + if (!ctx.LangOpts.EnableExperimentalDifferentiableProgramming) { + diags.diagnose(attr->getLocation(), + diag::experimental_differentiable_programming_disabled); + return true; + } + auto *derivative = cast(D); + auto lookupConformance = + LookUpConformanceInModule(D->getDeclContext()->getParentModule()); + auto originalName = attr->getOriginalFunctionName(); + + auto *derivativeInterfaceType = + derivative->getInterfaceType()->castTo(); + + // Perform preliminary `@derivative` declaration checks. + // The result type should be a two-element tuple. + // Either a value and pullback: + // (value: R, pullback: (R.TangentVector) -> (T.TangentVector...) + // Or a value and differential: + // (value: R, differential: (T.TangentVector...) -> (R.TangentVector) + auto derivativeResultType = derivative->getResultInterfaceType(); + auto derivativeResultTupleType = derivativeResultType->getAs(); + if (!derivativeResultTupleType || + derivativeResultTupleType->getNumElements() != 2) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_expected_result_tuple); + return true; + } + auto valueResultElt = derivativeResultTupleType->getElement(0); + auto funcResultElt = derivativeResultTupleType->getElement(1); + // Get derivative kind and derivative function identifier. + AutoDiffDerivativeFunctionKind kind; + if (valueResultElt.getName().str() != "value") { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_value_label); + return true; + } + if (funcResultElt.getName().str() == "differential") { + kind = AutoDiffDerivativeFunctionKind::JVP; + } else if (funcResultElt.getName().str() == "pullback") { + kind = AutoDiffDerivativeFunctionKind::VJP; + } else { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_invalid_result_tuple_func_label); + return true; + } + attr->setDerivativeKind(kind); + // `value: R` result tuple element must conform to `Differentiable`. + auto diffableProto = Ctx.getProtocol(KnownProtocolKind::Differentiable); + auto valueResultType = valueResultElt.getType(); + if (valueResultType->hasTypeParameter()) + valueResultType = derivative->mapTypeIntoContext(valueResultType); + auto valueResultConf = TypeChecker::conformsToProtocol( + valueResultType, diffableProto, derivative->getDeclContext(), None); + if (!valueResultConf) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_value_not_differentiable, + valueResultElt.getType()); + return true; + } + + // Compute expected original function type and look up original function. + auto *originalFnType = + getDerivativeOriginalFunctionType(derivativeInterfaceType); + + // Returns true if the generic parameters in `source` satisfy the generic + // requirements in `target`. + std::function + checkGenericSignatureSatisfied = [&](GenericSignature source, + GenericSignature target) { + // If target is null, then its requirements are satisfied. + if (!target) + return true; + // If source is null but target is not null, then target's + // requirements are not satisfied. + if (!source) + return false; + // Check if target's requirements are satisfied by source. + // Cancel diagnostics using `DiagnosticTransaction`. + // Diagnostics should not be emitted because this function is used to + // check candidates; if no candidates match, a separate diagnostic will + // be produced. + DiagnosticTransaction transaction(Ctx.Diags); + SWIFT_DEFER { transaction.abort(); }; + return TypeChecker::checkGenericArguments( + derivative, originalName.Loc.getBaseNameLoc(), + originalName.Loc.getBaseNameLoc(), Type(), + source->getGenericParams(), target->getRequirements(), + [](SubstitutableType *dependentType) { + return Type(dependentType); + }, + lookupConformance, None) == RequirementCheckResult::Success; + }; + + auto isValidOriginal = [&](AbstractFunctionDecl *originalCandidate) { + // TODO(TF-982): Allow derivatives on protocol requirements. + if (isa(originalCandidate->getDeclContext())) + return false; + return checkFunctionSignature( + cast(originalFnType->getCanonicalType()), + originalCandidate->getInterfaceType()->getCanonicalType(), + checkGenericSignatureSatisfied); + }; + + auto noneValidDiagnostic = [&]() { + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_none_valid_found, + originalName.Name, originalFnType); + }; + auto ambiguousDiagnostic = [&]() { + diags.diagnose(originalName.Loc, diag::attr_ambiguous_reference_to_decl, + originalName.Name, attr->getAttrName()); + }; + auto notFunctionDiagnostic = [&]() { + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_invalid_kind, + originalName.Name); + }; + std::function invalidTypeContextDiagnostic = [&]() { + diags.diagnose(originalName.Loc, + diag::autodiff_attr_original_decl_not_same_type_context, + originalName.Name); + }; + + // Returns true if the derivative function and original function candidate are + // defined in compatible type contexts. If the derivative function and the + // original function candidate have different parents, return false. + std::function hasValidTypeContext = + [&](AbstractFunctionDecl *func) { + // Check if both functions are top-level. + if (!derivative->getInnermostTypeContext() && + !func->getInnermostTypeContext()) + return true; + // Check if both functions are defined in the same type context. + if (auto typeCtx1 = derivative->getInnermostTypeContext()) + if (auto typeCtx2 = func->getInnermostTypeContext()) { + return typeCtx1->getSelfNominalTypeDecl() == + typeCtx2->getSelfNominalTypeDecl(); + } + return derivative->getParent() == func->getParent(); + }; + + auto resolution = TypeResolution::forContextual(derivative->getDeclContext()); + Type baseType; + if (auto *baseTypeRepr = attr->getBaseTypeRepr()) { + TypeResolutionOptions options = None; + options |= TypeResolutionFlags::AllowModule; + baseType = resolution.resolveType(baseTypeRepr, options); + } + if (baseType && baseType->hasError()) + return true; + auto lookupOptions = attr->getBaseTypeRepr() + ? defaultMemberLookupOptions + : defaultUnqualifiedLookupOptions; + auto derivativeTypeCtx = derivative->getInnermostTypeContext(); + if (!derivativeTypeCtx) + derivativeTypeCtx = derivative->getParent(); + assert(derivativeTypeCtx); + + // Look up original function. + auto *originalAFD = findAbstractFunctionDecl( + originalName.Name, originalName.Loc.getBaseNameLoc(), baseType, + derivativeTypeCtx, isValidOriginal, noneValidDiagnostic, + ambiguousDiagnostic, notFunctionDiagnostic, lookupOptions, + hasValidTypeContext, invalidTypeContextDiagnostic); + if (!originalAFD) + return true; + // Diagnose original stored properties. Stored properties cannot have custom + // registered derivatives. + if (auto *accessorDecl = dyn_cast(originalAFD)) { + auto *asd = accessorDecl->getStorage(); + if (asd->hasStorage()) { + diags.diagnose(originalName.Loc, + diag::derivative_attr_original_stored_property_unsupported, + originalName.Name); + diags.diagnose(originalAFD->getLoc(), diag::decl_declared_here, + asd->getFullName()); + return true; + } + } + attr->setOriginalFunction(originalAFD); + + // Get the resolved differentiability parameter indices. + auto *resolvedDiffParamIndices = attr->getParameterIndices(); + + // Get the parsed differentiability parameter indices, which have not yet been + // resolved. Parsed differentiability parameter indices are defined only for + // parsed attributes. + auto parsedDiffParams = attr->getParsedParameters(); + + // If differentiability parameter indices are not resolved, compute them. + if (!resolvedDiffParamIndices) + resolvedDiffParamIndices = computeDifferentiabilityParameters( + parsedDiffParams, derivative, derivative->getGenericEnvironment(), + attr->getAttrName(), attr->getLocation()); + if (!resolvedDiffParamIndices) + return true; + + // Check if the differentiability parameter indices are valid. + if (checkDifferentiabilityParameters( + originalAFD, resolvedDiffParamIndices, originalFnType, + derivative->getGenericEnvironment(), derivative->getModuleContext(), + parsedDiffParams, attr->getLocation())) + return true; + + // Set the resolved differentiability parameter indices in the attribute. + attr->setParameterIndices(resolvedDiffParamIndices); + + // Gather differentiability parameters. + SmallVector diffParamTypes; + autodiff::getSubsetParameterTypes(resolvedDiffParamIndices, originalFnType, + diffParamTypes); + + // Get the differentiability parameters' `TangentVector` associated types. + auto diffParamTanTypes = + map>(diffParamTypes, [&](Type paramType) { + if (paramType->hasTypeParameter()) + paramType = derivative->mapTypeIntoContext(paramType); + auto conf = TypeChecker::conformsToProtocol(paramType, diffableProto, + derivative, None); + assert(conf && + "Expected resolved parameter to conform to `Differentiable`"); + auto paramAssocType = + conf.getTypeWitnessByName(paramType, Ctx.Id_TangentVector); + return TupleTypeElt(paramAssocType); + }); + + // Get the `TangentVector` associated type of the `value:` result type. + auto resultTanType = valueResultConf.getTypeWitnessByName( + valueResultType, Ctx.Id_TangentVector); + + // Compute the actual differential/pullback type that we use for comparison + // with the expected type. We must canonicalize the derivative interface type + // before extracting the differential/pullback type from it, so that the + // derivative interface type generic signature is available for simplifying + // types. + CanType canActualResultType = derivativeInterfaceType->getCanonicalType(); + while (isa(canActualResultType)) { + canActualResultType = + cast(canActualResultType).getResult(); + } + CanType actualFuncEltType = + cast(canActualResultType).getElementType(1); + + // Compute expected differential/pullback type. + Type expectedFuncEltType; + if (kind == AutoDiffDerivativeFunctionKind::JVP) { + auto diffParams = map>( + diffParamTanTypes, [&](TupleTypeElt elt) { + return AnyFunctionType::Param(elt.getType()); + }); + expectedFuncEltType = FunctionType::get(diffParams, resultTanType); + } else { + expectedFuncEltType = + FunctionType::get({AnyFunctionType::Param(resultTanType)}, + TupleType::get(diffParamTanTypes, Ctx)); + } + expectedFuncEltType = expectedFuncEltType->mapTypeOutOfContext(); + + // Check if differential/pullback type matches expected type. + if (!actualFuncEltType->isEqual(expectedFuncEltType)) { + // Emit differential/pullback type mismatch error on attribute. + diags.diagnose(attr->getLocation(), + diag::derivative_attr_result_func_type_mismatch, + funcResultElt.getName(), originalAFD->getFullName()); + // Emit note with expected differential/pullback type on actual type + // location. + auto *tupleReturnTypeRepr = + cast(derivative->getBodyResultTypeLoc().getTypeRepr()); + auto *funcEltTypeRepr = tupleReturnTypeRepr->getElementType(1); + diags + .diagnose(funcEltTypeRepr->getStartLoc(), + diag::derivative_attr_result_func_type_mismatch_note, + funcResultElt.getName(), expectedFuncEltType) + .highlight(funcEltTypeRepr->getSourceRange()); + // Emit note showing original function location, if possible. + if (originalAFD->getLoc().isValid()) + diags.diagnose(originalAFD->getLoc(), + diag::derivative_attr_result_func_original_note, + originalAFD->getFullName()); + return true; + } + + // Reject different-file derivative registration. + // TODO(TF-1021): Lift same-file derivative registration restriction. + if (originalAFD->getParentSourceFile() != derivative->getParentSourceFile()) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_not_in_same_file_as_original); + return true; + } + + // Reject duplicate `@derivative` attributes. + auto &derivativeAttrs = Ctx.DerivativeAttrs[std::make_tuple( + originalAFD, resolvedDiffParamIndices, kind)]; + derivativeAttrs.insert(attr); + if (derivativeAttrs.size() > 1) { + diags.diagnose(attr->getLocation(), + diag::derivative_attr_original_already_has_derivative, + originalAFD->getFullName()); + for (auto *duplicateAttr : derivativeAttrs) { + if (duplicateAttr == attr) + continue; + diags.diagnose(duplicateAttr->getLocation(), + diag::derivative_attr_duplicate_note); + } + return true; + } + + return false; +} + +void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) { + if (typeCheckDerivativeAttr(Ctx, D, attr)) + attr->setInvalid(); +} diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 2edca51e0f175..ecbebda7e831a 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -456,6 +456,18 @@ class TypeRefinementContextBuilder : private ASTWalker { AvailabilityContext NewConstraint = contextForSpec(Spec); Query->setAvailableRange(NewConstraint.getOSVersion()); + // When compiling zippered for macCatalyst, we need to collect both + // a macOS version (the target version) and an iOS/macCatalyst version + // (the target-variant). These versions will both be passed to a runtime + // entrypoint that will check either the macOS version or the iOS + // version depending on the kind of process this code is loaded into. + if (Context.LangOpts.TargetVariant) { + AvailabilitySpec *VariantSpec = + bestActiveSpecForQuery(Query, /*ForTargetVariant*/ true); + VersionRange VariantRange = contextForSpec(VariantSpec).getOSVersion(); + Query->setVariantAvailableRange(VariantRange); + } + if (Spec->getKind() == AvailabilitySpecKind::OtherPlatform) { // The wildcard spec '*' represents the minimum deployment target, so // there is no need to create a refinement context for this query. @@ -480,9 +492,18 @@ class TypeRefinementContextBuilder : private ASTWalker { // required compatibility version is different than the deployment // target). if (CurrentTRC->getReason() != TypeRefinementContext::Reason::Root) { + PlatformKind BestPlatform = targetPlatform(Context.LangOpts); + auto *PlatformSpec = + dyn_cast(Spec); + // If possible, try to report the diagnostic in terms for the + // platform the user uttered in the '#available()'. For a platform + // that inherits availability from another platform it may be + // different from the platform specified in the target triple. + if (PlatformSpec) + BestPlatform = PlatformSpec->getPlatform(); Diags.diagnose(Query->getLoc(), diag::availability_query_useless_enclosing_scope, - platformString(targetPlatform(Context.LangOpts))); + platformString(BestPlatform)); Diags.diagnose(CurrentTRC->getIntroductionLoc(), diag::availability_query_useless_enclosing_scope_here); } @@ -535,8 +556,11 @@ class TypeRefinementContextBuilder : private ASTWalker { /// Return the best active spec for the target platform or nullptr if no /// such spec exists. - AvailabilitySpec *bestActiveSpecForQuery(PoundAvailableInfo *available) { + AvailabilitySpec *bestActiveSpecForQuery(PoundAvailableInfo *available, + bool forTargetVariant = false) { OtherPlatformAvailabilitySpec *FoundOtherSpec = nullptr; + PlatformVersionConstraintAvailabilitySpec *BestSpec = nullptr; + for (auto *Spec : available->getQueries()) { if (auto *OtherSpec = dyn_cast(Spec)) { FoundOtherSpec = OtherSpec; @@ -551,11 +575,19 @@ class TypeRefinementContextBuilder : private ASTWalker { // properly. For example, on the OSXApplicationExtension platform // we want to chose the OS X spec unless there is an explicit // OSXApplicationExtension spec. - if (isPlatformActive(VersionSpec->getPlatform(), Context.LangOpts)) { - return VersionSpec; + if (isPlatformActive(VersionSpec->getPlatform(), Context.LangOpts, + forTargetVariant)) { + if (!BestSpec || + inheritsAvailabilityFromPlatform(VersionSpec->getPlatform(), + BestSpec->getPlatform())) { + BestSpec = VersionSpec; + } } } + if (BestSpec) + return BestSpec; + // If we have reached this point, we found no spec for our target, so // we return the other spec ('*'), if we found it, or nullptr, if not. return FoundOtherSpec; @@ -604,7 +636,7 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF, // Build refinement contexts, if necessary, for all declarations starting // with StartElem. TypeRefinementContextBuilder Builder(RootTRC, Context); - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF.getTopLevelDecls().slice(StartElem)) { Builder.build(D); } } @@ -644,8 +676,10 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc, // We can assume we are running on at least the minimum deployment target. auto OverApproximateContext = AvailabilityContext::forDeploymentTarget(Context); - - while (DC && loc.isInvalid()) { + auto isInvalidLoc = [SF](SourceLoc loc) { + return SF ? loc.isInvalid() : true; + }; + while (DC && isInvalidLoc(loc)) { const Decl *D = DC->getInnermostDeclarationDeclContext(); if (!D) break; @@ -902,8 +936,9 @@ static const Decl *findContainingDeclaration(SourceRange ReferenceRange, if (!SF) return nullptr; - auto BestTopLevelDecl = llvm::find_if(SF->Decls, ContainsReferenceRange); - if (BestTopLevelDecl != SF->Decls.end()) + auto BestTopLevelDecl = llvm::find_if(SF->getTopLevelDecls(), + ContainsReferenceRange); + if (BestTopLevelDecl != SF->getTopLevelDecls().end()) return *BestTopLevelDecl; return nullptr; @@ -955,15 +990,8 @@ abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) { // all parsed attribute that appear in the concrete syntax upon on the // PatternBindingDecl are added to all of the VarDecls for the pattern // binding. - ArrayRef Entries = PBD->getPatternList(); - if (!Entries.empty()) { - const VarDecl *AnyVD = nullptr; - // FIXME: This is wasteful; we only need the first variable. - Entries.front().getPattern()->forEachVariable([&](const VarDecl *VD) { - AnyVD = VD; - }); - if (AnyVD) - return AnyVD; + if (PBD->getNumPatternEntries() != 0) { + return PBD->getAnchoringVarDecl(0); } } else if (auto *ECD = dyn_cast(ConcreteSyntaxDecl)) { // Similar to the PatternBindingDecl case above, we return the @@ -1790,8 +1818,7 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag, auto I = argumentLabelIDs.begin(); auto updateLabelsForArg = [&](Expr *expr) -> bool { - if (isa(expr) || - isa(expr)) { + if (isa(expr)) { // Defaulted: remove param label of it. if (I == argumentLabelIDs.end()) return true; @@ -2649,11 +2676,10 @@ static bool isIntegerOrFloatingPointType(Type ty, DeclContext *DC, Context.getProtocol(KnownProtocolKind::ExpressibleByFloatLiteral); if (!integerType || !floatingType) return false; - return - TypeChecker::conformsToProtocol(ty, integerType, DC, - ConformanceCheckFlags::InExpression) || - TypeChecker::conformsToProtocol(ty, floatingType, DC, - ConformanceCheckFlags::InExpression); + return TypeChecker::conformsToProtocol(ty, integerType, DC, + ConformanceCheckFlags::InExpression) || + TypeChecker::conformsToProtocol(ty, floatingType, DC, + ConformanceCheckFlags::InExpression); } diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index da051ad66fdbe..d9173b384bde1 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -25,6 +25,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/SourceFile.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallPtrSet.h" @@ -42,33 +43,33 @@ class FindCapturedVars : public ASTWalker { OpaqueValueExpr *OpaqueValue = nullptr; SourceLoc CaptureLoc; DeclContext *CurDC; - bool NoEscape, ObjC, IsGenericFunction; + bool NoEscape, ObjC; + bool HasGenericParamCaptures; public: - FindCapturedVars(ASTContext &Context, - SourceLoc CaptureLoc, + FindCapturedVars(SourceLoc CaptureLoc, DeclContext *CurDC, bool NoEscape, bool ObjC, bool IsGenericFunction) - : Context(Context), CaptureLoc(CaptureLoc), CurDC(CurDC), - NoEscape(NoEscape), ObjC(ObjC), IsGenericFunction(IsGenericFunction) {} + : Context(CurDC->getASTContext()), CaptureLoc(CaptureLoc), CurDC(CurDC), + NoEscape(NoEscape), ObjC(ObjC), HasGenericParamCaptures(IsGenericFunction) {} CaptureInfo getCaptureInfo() const { DynamicSelfType *dynamicSelfToRecord = nullptr; - bool hasGenericParamCaptures = IsGenericFunction; // Only local functions capture dynamic 'Self'. if (CurDC->getParent()->isLocalContext()) { - if (GenericParamCaptureLoc.isValid()) - hasGenericParamCaptures = true; - if (DynamicSelfCaptureLoc.isValid()) dynamicSelfToRecord = DynamicSelf; } return CaptureInfo(Context, Captures, dynamicSelfToRecord, OpaqueValue, - hasGenericParamCaptures); + HasGenericParamCaptures); + } + + bool hasGenericParamCaptures() const { + return HasGenericParamCaptures; } SourceLoc getGenericParamCaptureLoc() const { @@ -149,8 +150,9 @@ class FindCapturedVars : public ASTWalker { if ((t->is() || t->is()) && !t->isOpenedExistential() && - GenericParamCaptureLoc.isInvalid()) { + !HasGenericParamCaptures) { GenericParamCaptureLoc = loc; + HasGenericParamCaptures = true; } })); } @@ -158,8 +160,9 @@ class FindCapturedVars : public ASTWalker { if (auto *gft = type->getAs()) { TypeCaptureWalker walker(ObjC, [&](Type t) { if (t->is() && - GenericParamCaptureLoc.isInvalid()) { + !HasGenericParamCaptures) { GenericParamCaptureLoc = loc; + HasGenericParamCaptures = true; } }); @@ -217,10 +220,14 @@ class FindCapturedVars : public ASTWalker { if (D->getBaseName() == Context.Id_dollarInterpolation) return { false, DRE }; + // DC is the DeclContext where D was defined + // CurDC is the DeclContext where D was referenced + auto DC = D->getDeclContext(); + // Capture the generic parameters of the decl, unless it's a // local declaration in which case we will pick up generic // parameter references transitively. - if (!D->getDeclContext()->isLocalContext()) { + if (!DC->isLocalContext()) { if (!ObjC || !D->isObjC() || isa(D)) { if (auto subMap = DRE->getDeclRef().getSubstitutions()) { for (auto type : subMap.getReplacementTypes()) { @@ -230,40 +237,57 @@ class FindCapturedVars : public ASTWalker { } } - // DC is the DeclContext where D was defined - // CurDC is the DeclContext where D was referenced - auto DC = D->getDeclContext(); + // Don't "capture" type definitions at all. + if (isa(D)) + return { false, DRE }; // A local reference is not a capture. - if (CurDC == DC) + if (CurDC == DC || isa(CurDC)) return { false, DRE }; auto TmpDC = CurDC; - - if (!isa(DC)) { - while (TmpDC != nullptr) { - if (TmpDC == DC) - break; - - // The initializer of a lazy property will eventually get - // recontextualized into it, so treat it as if it's already there. - if (auto init = dyn_cast(TmpDC)) { - if (auto lazyVar = init->getInitializedLazyVar()) { - // If we have a getter with a body, we're already re-parented - // everything so pretend we're inside the getter. - if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { - if (getter->getBody(/*canSynthesize=*/false)) { - TmpDC = getter; - continue; - } + while (TmpDC != nullptr) { + // Variables defined inside TopLevelCodeDecls are semantically + // local variables. If the reference is not from the top level, + // we have a capture. + if (isa(DC) && + (isa(TmpDC) || isa(TmpDC))) + break; + + if (TmpDC == DC) + break; + + // The initializer of a lazy property will eventually get + // recontextualized into it, so treat it as if it's already there. + if (auto init = dyn_cast(TmpDC)) { + if (auto lazyVar = init->getInitializedLazyVar()) { + // If we have a getter with a body, we're already re-parented + // everything so pretend we're inside the getter. + if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) { + if (getter->getBody(/*canSynthesize=*/false)) { + TmpDC = getter; + continue; } } } + } - // We have an intervening nominal type context that is not the - // declaration context, and the declaration context is not global. - // This is not supported since nominal types cannot capture values. - if (auto NTD = dyn_cast(TmpDC)) { + // We have an intervening nominal type context that is not the + // declaration context, and the declaration context is not global. + // This is not supported since nominal types cannot capture values. + if (auto NTD = dyn_cast(TmpDC)) { + // Allow references to local functions from inside methods of a + // local type, because if the local function has captures, we'll + // diagnose them in SILGen. It's a bit unfortunate that we can't + // ban this outright, but people rely on code like this working: + // + // do { + // func local() {} + // class C { + // func method() { local() } + // } + // } + if (!isa(D)) { if (DC->isLocalContext()) { Context.Diags.diagnose(DRE->getLoc(), diag::capture_across_type_decl, NTD->getDescriptiveKind(), @@ -276,23 +300,19 @@ class FindCapturedVars : public ASTWalker { return { false, DRE }; } } - - TmpDC = TmpDC->getParent(); } - // We walked all the way up to the root without finding the declaration, - // so this is not a capture. - if (TmpDC == nullptr) - return { false, DRE }; + TmpDC = TmpDC->getParent(); } - // Don't "capture" type definitions at all. - if (isa(D)) + // We walked all the way up to the root without finding the declaration, + // so this is not a capture. + if (TmpDC == nullptr) return { false, DRE }; // Only capture var decls at global scope. Other things can be captured // if they are local. - if (!isa(D) && !DC->isLocalContext()) + if (!isa(D) && !D->isLocalCapture()) return { false, DRE }; // We're going to capture this, compute flags for the capture. @@ -319,8 +339,7 @@ class FindCapturedVars : public ASTWalker { return { false, DRE }; } - void propagateCaptures(const CaptureInfo &captureInfo, - SourceLoc loc) { + void propagateCaptures(CaptureInfo captureInfo, SourceLoc loc) { for (auto capture : captureInfo.getCaptures()) { // If the decl was captured from us, it isn't captured *by* us. if (capture.getDecl()->getDeclContext() == CurDC) @@ -341,9 +360,12 @@ class FindCapturedVars : public ASTWalker { addCapture(CapturedValue(capture.getDecl(), Flags, capture.getLoc())); } - if (GenericParamCaptureLoc.isInvalid()) - if (captureInfo.hasGenericParamCaptures()) + if (!HasGenericParamCaptures) { + if (captureInfo.hasGenericParamCaptures()) { GenericParamCaptureLoc = loc; + HasGenericParamCaptures = true; + } + } if (DynamicSelfCaptureLoc.isInvalid()) { if (captureInfo.hasDynamicSelfCapture()) { @@ -596,8 +618,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { isGeneric = (AFD->getGenericParams() != nullptr); auto &Context = AFR.getAsDeclContext()->getASTContext(); - FindCapturedVars finder(Context, - AFR.getLoc(), + FindCapturedVars finder(AFR.getLoc(), AFR.getAsDeclContext(), AFR.isKnownNoEscape(), AFR.isObjC(), @@ -613,9 +634,8 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { // Compute captures for default argument expressions. if (auto *AFD = AFR.getAbstractFunctionDecl()) { for (auto *P : *AFD->getParameters()) { - if (auto E = P->getDefaultValue()) { - FindCapturedVars finder(Context, - E->getLoc(), + if (auto E = P->getTypeCheckedDefaultExpr()) { + FindCapturedVars finder(E->getLoc(), AFD, /*isNoEscape=*/false, /*isObjC=*/false, @@ -635,7 +655,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) { // Extensions of generic ObjC functions can't use generic parameters from // their context. - if (AFD && finder.getGenericParamCaptureLoc().isValid()) { + if (AFD && finder.hasGenericParamCaptures()) { if (auto Clas = AFD->getParent()->getSelfClassDecl()) { if (Clas->usesObjCGenericsModel()) { AFD->diagnose(diag::objc_generic_extension_using_type_parameter); @@ -664,16 +684,14 @@ static bool isLazy(PatternBindingDecl *PBD) { return false; } -void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) { - auto &ctx = typeDecl->getASTContext(); - - for (auto member : typeDecl->getMembers()) { +void TypeChecker::checkPatternBindingCaptures(IterableDeclContext *DC) { + for (auto member : DC->getMembers()) { // Ignore everything other than PBDs. auto *PBD = dyn_cast(member); if (!PBD) continue; // Walk the initializers for all properties declared in the type with // an initializer. - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) { + for (unsigned i : range(PBD->getNumPatternEntries())) { if (PBD->isInitializerSubsumed(i)) continue; @@ -681,14 +699,15 @@ void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) { if (init == nullptr) continue; - FindCapturedVars finder(ctx, - init->getLoc(), - PBD->getInitContext(i), + auto *DC = PBD->getInitContext(i); + FindCapturedVars finder(init->getLoc(), + DC, /*NoEscape=*/false, /*ObjC=*/false, /*IsGenericFunction*/false); init->walk(finder); + auto &ctx = DC->getASTContext(); if (finder.getDynamicSelfCaptureLoc().isValid() && !isLazy(PBD)) { ctx.Diags.diagnose(finder.getDynamicSelfCaptureLoc(), diag::dynamic_self_stored_property_init); diff --git a/lib/Sema/TypeCheckCircularity.cpp b/lib/Sema/TypeCheckCircularity.cpp index b6a2f372c148b..f5fa169bbff88 100644 --- a/lib/Sema/TypeCheckCircularity.cpp +++ b/lib/Sema/TypeCheckCircularity.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "swift/Basic/Debug.h" using namespace swift; @@ -72,7 +73,7 @@ struct PathElement { size_t TupleIndex; Type Ty; - void dump() const; + SWIFT_DEBUG_DUMP; void print(llvm::raw_ostream &out) const; }; @@ -87,7 +88,7 @@ class Path { const PathElement &operator[](size_t index) const { return Elements[index]; } const PathElement &back() const { return Elements.back(); } - void dump() const; + SWIFT_DEBUG_DUMP; void printCycle(llvm::raw_ostream &out, size_t cycleIndex) const; void printInfinite(llvm::raw_ostream &out) const; @@ -97,9 +98,7 @@ class Path { }; /// A helper class for performing a circularity check. -class CircularityChecker { - TypeChecker &TC; - +class CircularityChecker final { /// The original type declaration we're starting with. NominalTypeDecl *OriginalDecl; @@ -110,9 +109,9 @@ class CircularityChecker { SmallVector Workstack; public: - CircularityChecker(TypeChecker &tc, NominalTypeDecl *typeDecl) - : TC(tc), OriginalDecl(typeDecl), - MaxDepth(tc.Context.LangOpts.MaxCircularityDepth) {} + CircularityChecker(NominalTypeDecl *typeDecl) + : OriginalDecl(typeDecl), + MaxDepth(typeDecl->getASTContext().LangOpts.MaxCircularityDepth) {} void run(); @@ -177,7 +176,7 @@ class CircularityChecker { } // end anonymous namespace void TypeChecker::checkDeclCircularity(NominalTypeDecl *decl) { - CircularityChecker(*this, decl).run(); + CircularityChecker(decl).run(); } /// The main routine for performing circularity checks. @@ -522,18 +521,12 @@ bool CircularityChecker::diagnoseCircularity(CanType parentType, auto baseType = path[0].Ty; if (cycleIndex != 0) { - TC.diagnose(OriginalDecl->getLoc(), - diag::unsupported_infinitely_sized_type, - baseType); + OriginalDecl->diagnose(diag::unsupported_infinitely_sized_type, baseType); } else if (isa(OriginalDecl)) { - TC.diagnose(path[1].Member->getLoc(), - diag::unsupported_recursive_struct, - baseType); + path[1].Member->diagnose(diag::unsupported_recursive_struct, baseType); } else if (isa(OriginalDecl)) { - TC.diagnose(OriginalDecl->getLoc(), - diag::recursive_enum_not_indirect, - baseType) - .fixItInsert(OriginalDecl->getStartLoc(), "indirect "); + OriginalDecl->diagnose(diag::recursive_enum_not_indirect, baseType) + .fixItInsert(OriginalDecl->getStartLoc(), "indirect "); } else { llvm_unreachable("what kind of entity was this?"); } @@ -544,12 +537,9 @@ bool CircularityChecker::diagnoseCircularity(CanType parentType, llvm::raw_svector_ostream out(pathString); path.printCycle(out, cycleIndex); } - TC.diagnose(path[1].Member->getLoc(), - diag::note_type_cycle_starts_here, - pathString); + path[1].Member->diagnose(diag::note_type_cycle_starts_here, pathString); } else if (isa(OriginalDecl)) { - TC.diagnose(path[1].Member->getLoc(), - diag::note_recursive_enum_case_here); + path[1].Member->diagnose(diag::note_recursive_enum_case_here); } return true; @@ -572,9 +562,7 @@ bool CircularityChecker::diagnoseInfiniteRecursion(CanType parentType, } auto baseType = path[0].Ty; - TC.diagnose(OriginalDecl->getLoc(), - diag::unsupported_infinitely_sized_type, - baseType); + OriginalDecl->diagnose(diag::unsupported_infinitely_sized_type, baseType); // Add a note about the start of the path. llvm::SmallString<128> pathString; { @@ -582,9 +570,7 @@ bool CircularityChecker::diagnoseInfiniteRecursion(CanType parentType, path.printInfinite(out); } - TC.diagnose(path[1].Member->getLoc(), - diag::note_type_cycle_starts_here, - pathString); + path[1].Member->diagnose(diag::note_type_cycle_starts_here, pathString); return true; } @@ -627,5 +613,5 @@ void CircularityChecker::diagnoseNonWellFoundedEnum(EnumDecl *E) { }; if (isNonWellFounded()) - TC.diagnose(E, diag::enum_non_well_founded); + E->getASTContext().Diags.diagnose(E, diag::enum_non_well_founded); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index dda4796edf27f..2d05095eb2e55 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -18,6 +18,7 @@ #include "ConstraintSystem.h" #include "MiscDiagnostics.h" +#include "SolutionResult.h" #include "TypeChecker.h" #include "TypeCheckType.h" #include "TypoCorrection.h" @@ -35,6 +36,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/TypeCheckerDebugConsumer.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "swift/Parse/Confusables.h" #include "swift/Parse/Lexer.h" @@ -68,30 +70,32 @@ void TypeVariableType::Implementation::print(llvm::raw_ostream &OS) { } SavedTypeVariableBinding::SavedTypeVariableBinding(TypeVariableType *typeVar) - : TypeVarAndOptions(typeVar, typeVar->getImpl().getRawOptions()), + : TypeVar(typeVar), Options(typeVar->getImpl().getRawOptions()), ParentOrFixed(typeVar->getImpl().ParentOrFixed) { } void SavedTypeVariableBinding::restore() { - auto *typeVar = getTypeVariable(); - typeVar->getImpl().setRawOptions(getOptions()); - typeVar->getImpl().ParentOrFixed = ParentOrFixed; + TypeVar->getImpl().setRawOptions(Options); + TypeVar->getImpl().ParentOrFixed = ParentOrFixed; } GenericTypeParamType * TypeVariableType::Implementation::getGenericParameter() const { - // Check whether we have a path that terminates at a generic parameter - // locator. - return locator && locator->isForGenericParameter() - ? locator->getGenericParameter() - : nullptr; + return locator ? locator->getGenericParameter() : nullptr; } -// Only allow allocation of resolved overload set list items using the -// allocator in ASTContext. -void *ResolvedOverloadSetListItem::operator new(size_t bytes, - ConstraintSystem &cs, - unsigned alignment) { - return cs.getAllocator().Allocate(bytes, alignment); +bool TypeVariableType::Implementation::isClosureType() const { + if (!(locator && locator->getAnchor())) + return false; + + return isa(locator->getAnchor()) && locator->getPath().empty(); +} + +bool TypeVariableType::Implementation::isClosureResultType() const { + if (!(locator && locator->getAnchor())) + return false; + + return isa(locator->getAnchor()) && + locator->isLastElement(); } void *operator new(size_t bytes, ConstraintSystem& cs, @@ -229,7 +233,7 @@ static bool containsDeclRefKind(LookupResult &lookupResult, DeclRefKind refKind) { for (auto candidate : lookupResult) { ValueDecl *D = candidate.getValueDecl(); - if (!D || !D->hasInterfaceType()) + if (!D) continue; if (matchesDeclRefKind(D, refKind)) return true; @@ -239,10 +243,9 @@ static bool containsDeclRefKind(LookupResult &lookupResult, /// Emit a diagnostic with a fixit hint for an invalid binary operator, showing /// how to split it according to splitCandidate. -static void diagnoseBinOpSplit(UnresolvedDeclRefExpr *UDRE, +static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE, std::pair splitCandidate, - Diag diagID, - TypeChecker &TC) { + Diag diagID) { unsigned splitLoc = splitCandidate.first; bool isBinOpFirst = splitCandidate.second; @@ -251,9 +254,9 @@ static void diagnoseBinOpSplit(UnresolvedDeclRefExpr *UDRE, auto endStr = nameStr.drop_front(splitLoc); // One valid split found, it is almost certainly the right answer. - auto diag = TC.diagnose(UDRE->getLoc(), diagID, - TC.Context.getIdentifier(startStr), - TC.Context.getIdentifier(endStr), isBinOpFirst); + auto diag = Context.Diags.diagnose( + UDRE->getLoc(), diagID, Context.getIdentifier(startStr), + Context.getIdentifier(endStr), isBinOpFirst); // Highlight the whole operator. diag.highlight(UDRE->getLoc()); // Insert whitespace on the left if the binop is at the start, or to the @@ -272,8 +275,7 @@ static void diagnoseBinOpSplit(UnresolvedDeclRefExpr *UDRE, /// needs whitespace. If so, emit specific diagnostics for it and return true, /// otherwise return false. static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, - DeclContext *DC, - TypeChecker &TC) { + DeclContext *DC) { Identifier name = UDRE->getName().getBaseIdentifier(); StringRef nameStr = name.str(); if (!name.isOperator() || nameStr.size() < 2) @@ -284,8 +286,9 @@ static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, // If this is a binary operator, relex the token, to decide whether it has // whitespace around it or not. If it does "x +++ y", then it isn't likely to // be a case where a space was forgotten. + auto &Context = DC->getASTContext(); if (isBinOp) { - auto tok = Lexer::getTokenAtLocation(TC.Context.SourceMgr, UDRE->getLoc()); + auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc()); if (tok.getKind() != tok::oper_binary_unspaced) return false; } @@ -307,19 +310,19 @@ static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr)) continue; - auto startName = TC.Context.getIdentifier(startStr); - auto endName = TC.Context.getIdentifier(endStr); + DeclNameRef startName(Context.getIdentifier(startStr)); + DeclNameRef endName(Context.getIdentifier(endStr)); // Perform name lookup for the first and second pieces. If either fail to // be found, then it isn't a valid split. NameLookupOptions LookupOptions = defaultUnqualifiedLookupOptions; // This is only used for diagnostics, so always use KnownPrivate. LookupOptions |= NameLookupFlags::KnownPrivate; - auto startLookup = TC.lookupUnqualified(DC, startName, UDRE->getLoc(), - LookupOptions); + auto startLookup = TypeChecker::lookupUnqualified( + DC, startName, UDRE->getLoc(), LookupOptions); if (!startLookup) continue; - auto endLookup = TC.lookupUnqualified(DC, endName, UDRE->getLoc(), - LookupOptions); + auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(), + LookupOptions); if (!endLookup) continue; // If the overall operator is a binary one, then we're looking at @@ -348,32 +351,33 @@ static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE, case 1: // One candidate: produce an error with a fixit on it. if (isBinOp) - diagnoseBinOpSplit(UDRE, WorkableSplits[0], - diag::unspaced_binary_operator_fixit, TC); + diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0], + diag::unspaced_binary_operator_fixit); else - TC.diagnose(UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), - diag::unspaced_unary_operator); + Context.Diags.diagnose( + UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first), + diag::unspaced_unary_operator); return true; default: // Otherwise, we have to produce a series of notes listing the various // options. - TC.diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator : - diag::unspaced_unary_operator) - .highlight(UDRE->getLoc()); + Context.Diags + .diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator + : diag::unspaced_unary_operator) + .highlight(UDRE->getLoc()); if (isBinOp) { for (auto candidateSplit : WorkableSplits) - diagnoseBinOpSplit(UDRE, candidateSplit, - diag::unspaced_binary_operators_candidate, TC); + diagnoseBinOpSplit(Context, UDRE, candidateSplit, + diag::unspaced_binary_operators_candidate); } return true; } } - -static bool diagnoseRangeOperatorMisspell(UnresolvedDeclRefExpr *UDRE, - TypeChecker &TC) { +static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { auto name = UDRE->getName().getBaseIdentifier(); if (!name.isOperator()) return false; @@ -387,18 +391,19 @@ static bool diagnoseRangeOperatorMisspell(UnresolvedDeclRefExpr *UDRE, corrected = "..<"; if (!corrected.empty()) { - TC.diagnose(UDRE->getLoc(), diag::use_unresolved_identifier_corrected, - name, true, corrected) - .highlight(UDRE->getSourceRange()) - .fixItReplace(UDRE->getSourceRange(), corrected); + Diags + .diagnose(UDRE->getLoc(), diag::use_unresolved_identifier_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()) + .fixItReplace(UDRE->getSourceRange(), corrected); return true; } return false; } -static bool diagnoseIncDecOperator(UnresolvedDeclRefExpr *UDRE, - TypeChecker &TC) { +static bool diagnoseIncDecOperator(DiagnosticEngine &Diags, + UnresolvedDeclRefExpr *UDRE) { auto name = UDRE->getName().getBaseIdentifier(); if (!name.isOperator()) return false; @@ -410,17 +415,17 @@ static bool diagnoseIncDecOperator(UnresolvedDeclRefExpr *UDRE, corrected = "-= 1"; if (!corrected.empty()) { - TC.diagnose(UDRE->getLoc(), diag::use_unresolved_identifier_corrected, - name, true, corrected) - .highlight(UDRE->getSourceRange()); + Diags + .diagnose(UDRE->getLoc(), diag::use_unresolved_identifier_corrected, + UDRE->getName(), true, corrected) + .highlight(UDRE->getSourceRange()); return true; } return false; } -static bool findNonMembers(TypeChecker &TC, - ArrayRef lookupResults, +static bool findNonMembers(ArrayRef lookupResults, DeclRefKind refKind, bool breakOnMember, SmallVectorImpl &ResultValues, llvm::function_ref isValid) { @@ -447,45 +452,35 @@ static bool findNonMembers(TypeChecker &TC, return AllDeclRefs; } -/// Whether we should be looking at the outer results for a function called \c -/// name. -/// -/// This is very restrictive because it's a source compatibility issue (see the -/// if (AllConditionalConformances) { (void)findNonMembers(...); } below). -static bool shouldConsiderOuterResultsFor(DeclName name) { - const StringRef specialNames[] = {"min", "max"}; - for (auto specialName : specialNames) - if (name.isSimpleName(specialName)) - return true; - - return false; -} - /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. -Expr *TypeChecker:: -resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { +Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, + DeclContext *DC) { // Process UnresolvedDeclRefExpr by doing an unqualified lookup. - DeclName Name = UDRE->getName(); + DeclNameRef Name = UDRE->getName(); SourceLoc Loc = UDRE->getLoc(); // Perform standard value name lookup. NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; - if (shouldConsiderOuterResultsFor(Name)) - lookupOptions |= NameLookupFlags::IncludeOuterResults; - auto Lookup = lookupUnqualified(DC, Name, Loc, lookupOptions); + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; + + auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); + auto &Context = DC->getASTContext(); if (!Lookup) { // If we failed lookup of an operator, check to see if this is a range // operator misspelling. Otherwise try to diagnose a juxtaposition // e.g. (x*-4) that needs whitespace. - if (diagnoseRangeOperatorMisspell(UDRE, *this) || - diagnoseIncDecOperator(UDRE, *this) || - diagnoseOperatorJuxtaposition(UDRE, DC, *this)) { + if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) || + diagnoseIncDecOperator(Context.Diags, UDRE) || + diagnoseOperatorJuxtaposition(UDRE, DC)) { return new (Context) ErrorExpr(UDRE->getSourceRange()); } @@ -493,19 +488,20 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { NameLookupOptions relookupOptions = lookupOptions; relookupOptions |= NameLookupFlags::KnownPrivate; relookupOptions |= NameLookupFlags::IgnoreAccessControl; - LookupResult inaccessibleResults = lookupUnqualified(DC, Name, Loc, - relookupOptions); + auto inaccessibleResults = + TypeChecker::lookupUnqualified(DC, Name, Loc, relookupOptions); if (inaccessibleResults) { // FIXME: What if the unviable candidates have different levels of access? const ValueDecl *first = inaccessibleResults.front().getValueDecl(); - diagnose(Loc, diag::candidate_inaccessible, Name, - first->getFormalAccessScope().accessLevelForDiagnostics()); + Context.Diags.diagnose( + Loc, diag::candidate_inaccessible, first->getBaseName(), + first->getFormalAccessScope().accessLevelForDiagnostics()); // FIXME: If any of the candidates (usually just one) are in the same // module we could offer a fix-it. for (auto lookupResult : inaccessibleResults) { - diagnose(lookupResult.getValueDecl(), diag::decl_declared_here, - lookupResult.getValueDecl()->getFullName()); + auto *VD = lookupResult.getValueDecl(); + VD->diagnose(diag::decl_declared_here, VD->getFullName()); } // Don't try to recover here; we'll get more access-related diagnostics @@ -540,12 +536,14 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { } auto emitBasicError = [&] { - diagnose(Loc, diag::use_unresolved_identifier, Name, Name.isOperator()) - .highlight(UDRE->getSourceRange()); + Context.Diags + .diagnose(Loc, diag::use_unresolved_identifier, Name, + Name.isOperator()) + .highlight(UDRE->getSourceRange()); }; if (!isConfused) { - if (Name == Context.Id_Self) { + if (Name.isSimpleName(Context.Id_Self)) { if (DeclContext *typeContext = DC->getInnermostTypeContext()){ Type SelfType = typeContext->getSelfInterfaceType(); @@ -557,14 +555,14 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { } } - TypoCorrectionResults corrections(*this, Name, nameLoc); - performTypoCorrection(DC, UDRE->getRefKind(), Type(), - lookupOptions, corrections); + TypoCorrectionResults corrections(Name, nameLoc); + TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), + lookupOptions, corrections); if (auto typo = corrections.claimUniqueCorrection()) { - auto diag = diagnose(Loc, diag::use_unresolved_identifier_corrected, - Name, Name.isOperator(), - typo->CorrectedName.getBaseIdentifier().str()); + auto diag = Context.Diags.diagnose( + Loc, diag::use_unresolved_identifier_corrected, Name, + Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str()); diag.highlight(UDRE->getSourceRange()); typo->addFixits(diag); } else { @@ -575,10 +573,11 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { } else { emitBasicError(); - diagnose(Loc, diag::confusable_character, - UDRE->getName().isOperator(), simpleName.str(), - expectedIdentifier) - .fixItReplace(Loc, expectedIdentifier); + Context.Diags + .diagnose(Loc, diag::confusable_character, + UDRE->getName().isOperator(), simpleName.str(), + expectedIdentifier) + .fixItReplace(Loc, expectedIdentifier); } // TODO: consider recovering from here. We may want some way to suppress @@ -607,26 +606,19 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { } return true; }; - bool AllDeclRefs = findNonMembers( - *this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, - ResultValues, isValid); + bool AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); // If local declaration after use is found, check outer results for // better matching candidates. if (localDeclAfterUse) { auto innerDecl = localDeclAfterUse; - - // Perform a thorough lookup if outer results was not included before. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - auto option = lookupOptions; - option |= NameLookupFlags::IncludeOuterResults; - Lookup = lookupUnqualified(DC, Name, Loc, option); - } - while (localDeclAfterUse) { if (Lookup.outerResults().empty()) { - diagnose(Loc, diag::use_local_before_declaration, Name); - diagnose(innerDecl, diag::decl_declared_here, Name); + Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); + Context.Diags.diagnose(innerDecl, diag::decl_declared_here, + localDeclAfterUse->getFullName()); Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange()); return error; } @@ -634,16 +626,9 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { Lookup.shiftDownResults(); ResultValues.clear(); localDeclAfterUse = nullptr; - AllDeclRefs = findNonMembers( - *this, Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, - ResultValues, isValid); - } - - // Drop outer results if they are not supposed to be included. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - Lookup.filter([&](LookupResultEntry Result, bool isOuter) { - return !isOuter; - }); + AllDeclRefs = + findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), + /*breakOnMember=*/true, ResultValues, isValid); } } @@ -659,7 +644,7 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { D->getInterfaceType()); } - return TypeExpr::createForDecl(Loc, D, + return TypeExpr::createForDecl(UDRE->getNameLoc(), D, Lookup[0].getDeclContext(), UDRE->isImplicit()); } @@ -668,10 +653,11 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { // Diagnose uses of operators that found no matching candidates. if (ResultValues.empty()) { assert(UDRE->getRefKind() != DeclRefKind::Ordinary); - diagnose(Loc, diag::use_nonmatching_operator, - Name, - UDRE->getRefKind() == DeclRefKind::BinaryOperator ? 0 : - UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); + Context.Diags.diagnose( + Loc, diag::use_nonmatching_operator, Name, + UDRE->getRefKind() == DeclRefKind::BinaryOperator + ? 0 + : UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2); return new (Context) ErrorExpr(UDRE->getSourceRange()); } @@ -703,7 +689,6 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { ResultValues.clear(); bool AllMemberRefs = true; - bool AllConditionalConformances = true; ValueDecl *Base = nullptr; DeclContext *BaseDC = nullptr; for (auto Result : Lookup) { @@ -720,25 +705,6 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { Base = ThisBase; BaseDC = Result.getDeclContext(); - - // Check if this result is derived through a conditional conformance, - // meaning it comes from a protocol (or extension) where there's a - // conditional conformance for the type with the method in question - // (NB. that type may not be the type associated with DC, for tested types - // with static methods). - if (auto Proto = Value->getDeclContext()->getSelfProtocolDecl()) { - auto contextSelfType = - BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType(); - auto conformance = conformsToProtocol( - contextSelfType, Proto, DC, - ConformanceCheckFlags::InExpression | - ConformanceCheckFlags::SkipConditionalRequirements); - - if (!conformance || conformance->getConditionalRequirements().empty()) { - AllConditionalConformances = false; - } - } - continue; } @@ -749,33 +715,24 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { if (AllMemberRefs) { Expr *BaseExpr; if (auto PD = dyn_cast(Base)) { - BaseExpr = TypeExpr::createForDecl(Loc, + BaseExpr = TypeExpr::createForDecl(UDRE->getNameLoc(), PD->getGenericParams()->getParams().front(), /*DC*/nullptr, /*isImplicit=*/true); } else if (auto NTD = dyn_cast(Base)) { - BaseExpr = TypeExpr::createForDecl(Loc, NTD, BaseDC, /*isImplicit=*/true); + BaseExpr = TypeExpr::createForDecl(UDRE->getNameLoc(), NTD, BaseDC, + /*isImplicit=*/true); } else { BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(), /*Implicit=*/true); } - // We *might* include any non-members that we found in outer contexts in - // some special cases, for backwards compatibility: first, we have to be - // looking for one of the special names - // ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner - // results need to come from conditional conformances. The second condition - // is how the problem here was encountered: a type ('Range') was made to - // conditionally conform to a new protocol ('Sequence'), which introduced - // some extra methods ('min' and 'max') that shadowed global functions that - // people regularly called within extensions to that type (usually adding - // 'clamp'). llvm::SmallVector outerAlternatives; - if (AllConditionalConformances) { - (void)findNonMembers(*this, Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[&](ValueDecl *) { return true; }); - } + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on // type information. @@ -790,10 +747,10 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) { // // Make sure we emit a diagnostic, since returning an ErrorExpr without // producing one will break things downstream. - diagnose(Loc, diag::ambiguous_decl_ref, Name); + Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name); for (auto Result : Lookup) { auto *Decl = Result.getValueDecl(); - diagnose(Decl, diag::decl_declared_here, Decl->getFullName()); + Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl->getFullName()); } return new (Context) ErrorExpr(UDRE->getSourceRange()); } @@ -808,15 +765,15 @@ TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC, if (ctorRef->getName().getBaseName() != DeclBaseName::createConstructor()) return nullptr; - if (auto ctorContext - = dyn_cast_or_null(DC->getInnermostMethodContext())) { + if (auto ctorContext = + dyn_cast_or_null(DC->getInnermostMethodContext())) { auto nestedArg = ctorRef->getBase(); if (auto inout = dyn_cast(nestedArg)) nestedArg = inout->getSubExpr(); if (nestedArg->isSuperExpr()) return ctorContext->getImplicitSelfDecl(); if (auto declRef = dyn_cast(nestedArg)) - if (declRef->getDecl()->getFullName() == Context.Id_self) + if (declRef->getDecl()->getFullName() == DC->getASTContext().Id_self) return ctorContext->getImplicitSelfDecl(); } return nullptr; @@ -905,9 +862,10 @@ namespace { } class PreCheckExpression : public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; DeclContext *DC; - + ConstraintSystem *BaseCS; + Expr *ParentExpr; /// A stack of expressions being walked, used to determine where to @@ -945,16 +903,16 @@ namespace { /// argument-labeled interpolations. void correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE) { // These expressions are valid in Swift 5+. - if (TC.Context.isSwiftVersionAtLeast(5)) + if (getASTContext().isSwiftVersionAtLeast(5)) return; /// Diagnoses appendInterpolation(...) calls with multiple /// arguments or argument labels and corrects them. class StrangeInterpolationRewriter : public ASTWalker { - TypeChecker &TC; + ASTContext &Context; public: - StrangeInterpolationRewriter(TypeChecker &TC) : TC(TC) { } + StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {} virtual bool walkToDeclPre(Decl *D) { // We don't want to look inside decls. @@ -975,7 +933,7 @@ namespace { auto call = cast(E); if (auto callee = dyn_cast(call->getFn())) { if (callee->getName().getBaseName() == - TC.Context.Id_appendInterpolation) { + Context.Id_appendInterpolation) { Expr *newArg = nullptr; SourceLoc lParen, rParen; @@ -986,13 +944,15 @@ namespace { rParen = args->getRParenLoc(); Expr *secondArg = args->getElement(1); - TC.diagnose(secondArg->getLoc(), - diag::string_interpolation_list_changing) - .highlightChars(secondArg->getLoc(), rParen); - TC.diagnose(secondArg->getLoc(), - diag::string_interpolation_list_insert_parens) - .fixItInsertAfter(lParen, "(") - .fixItInsert(rParen, ")"); + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_changing) + .highlightChars(secondArg->getLoc(), rParen); + Context.Diags + .diagnose(secondArg->getLoc(), + diag::string_interpolation_list_insert_parens) + .fixItInsertAfter(lParen, "(") + .fixItInsert(rParen, ")"); newArg = args; } @@ -1007,27 +967,30 @@ namespace { SourceLoc argLabelLoc = call->getArgumentLabelLoc(0), argLoc = newArg->getStartLoc(); - TC.diagnose(argLabelLoc, - diag::string_interpolation_label_changing) - .highlightChars(argLabelLoc, argLoc); - TC.diagnose(argLabelLoc, - diag::string_interpolation_remove_label, - call->getArgumentLabels().front()) - .fixItRemoveChars(argLabelLoc, argLoc); + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_label_changing) + .highlightChars(argLabelLoc, argLoc); + Context.Diags + .diagnose(argLabelLoc, + diag::string_interpolation_remove_label, + call->getArgumentLabels().front()) + .fixItRemoveChars(argLabelLoc, argLoc); } // If newArg is no longer null, we need to build a new // appendInterpolation(_:) call that takes it to replace the bad // appendInterpolation(...) call. if (newArg) { - auto newCallee = new (TC.Context) UnresolvedDotExpr( + auto newCallee = new (Context) UnresolvedDotExpr( callee->getBase(), /*dotloc=*/SourceLoc(), - DeclName(TC.Context.Id_appendInterpolation), + DeclNameRef(Context.Id_appendInterpolation), /*nameloc=*/DeclNameLoc(), /*Implicit=*/true); - E = CallExpr::create(TC.Context, newCallee, lParen, - { newArg }, { Identifier() }, { SourceLoc() }, - rParen, /*trailingClosure=*/nullptr, /*implicit=*/false); + E = CallExpr::create(Context, newCallee, lParen, {newArg}, + {Identifier()}, {SourceLoc()}, rParen, + /*trailingClosure=*/nullptr, + /*implicit=*/false); } } } @@ -1038,13 +1001,16 @@ namespace { return { false, E }; } }; - - ISLE->getAppendingExpr()->walk(StrangeInterpolationRewriter(TC)); + + ISLE->getAppendingExpr()->walk( + StrangeInterpolationRewriter(getASTContext())); } public: - PreCheckExpression(TypeChecker &tc, DeclContext *dc, Expr *parent) - : TC(tc), DC(dc), ParentExpr(parent) {} + PreCheckExpression(DeclContext *dc, Expr *parent, ConstraintSystem *base) + : Ctx(dc->getASTContext()), DC(dc), BaseCS(base), ParentExpr(parent) {} + + ASTContext &getASTContext() const { return Ctx; } bool walkToClosureExprPre(ClosureExpr *expr); @@ -1101,8 +1067,8 @@ namespace { if (auto captureList = dyn_cast(expr)) { // Validate the capture list. for (auto capture : captureList->getCaptureList()) { - TC.typeCheckDecl(capture.Init); - TC.typeCheckDecl(capture.Var); + TypeChecker::typeCheckDecl(capture.Init); + TypeChecker::typeCheckDecl(capture.Var); } // Since closure expression is contained by capture list @@ -1119,15 +1085,16 @@ namespace { return finish(walkToClosureExprPre(closure), expr); if (auto unresolved = dyn_cast(expr)) { - TC.checkForForbiddenPrefix(unresolved); - return finish(true, TC.resolveDeclRefExpr(unresolved, DC)); + TypeChecker::checkForForbiddenPrefix( + getASTContext(), unresolved->getName().getBaseName()); + return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC)); } if (auto PlaceholderE = dyn_cast(expr)) { if (!PlaceholderE->getTypeLoc().isNull()) { - if (!TypeChecker::validateType(TC.Context, PlaceholderE->getTypeLoc(), - TypeResolution::forContextual(DC), - None)) + if (!TypeChecker::validateType( + getASTContext(), PlaceholderE->getTypeLoc(), + TypeResolution::forContextual(DC), None)) expr->setType(PlaceholderE->getTypeLoc().getType()); } return finish(true, expr); @@ -1144,8 +1111,8 @@ namespace { if (expr->isImplicit()) return finish(true, expr); - if (TC.isExprBeingDiagnosed(ParentExpr) || - TC.isExprBeingDiagnosed(expr)) + if (BaseCS && (BaseCS->isExprBeingDiagnosed(ParentExpr) || + BaseCS->isExprBeingDiagnosed(expr))) return finish(true, expr); auto parents = ParentExpr->getParentMap(); @@ -1165,15 +1132,17 @@ namespace { return finish(true, expr); if (isa(call->getSecond())) { - TC.diagnose(expr->getStartLoc(), - diag::cannot_pass_inout_arg_to_subscript); + getASTContext().Diags.diagnose( + expr->getStartLoc(), + diag::cannot_pass_inout_arg_to_subscript); return finish(false, nullptr); } } } } - TC.diagnose(expr->getStartLoc(), diag::extraneous_address_of); + getASTContext().Diags.diagnose(expr->getStartLoc(), + diag::extraneous_address_of); return finish(false, nullptr); } @@ -1194,7 +1163,7 @@ namespace { // Fold sequence expressions. if (auto *seqExpr = dyn_cast(expr)) { - auto result = TC.foldSequence(seqExpr, DC); + auto result = TypeChecker::foldSequence(seqExpr, DC); return result->walk(*this); } @@ -1210,22 +1179,16 @@ namespace { DC = ce->getParent(); } - // Strip off any AutoClosures that were produced by a previous type check - // so that we don't choke in CSGen. - // FIXME: we shouldn't double typecheck, but it looks like code completion - // may do so in some circumstances. rdar://21466394 - if (auto autoClosure = dyn_cast(expr)) - return autoClosure->getSingleExpressionBody(); - // A 'self.init' or 'super.init' application inside a constructor will // evaluate to void, with the initializer's result implicitly rebound // to 'self'. Recognize the unresolved constructor expression and // determine where to place the RebindSelfInConstructorExpr node. // When updating this logic, also update // RebindSelfInConstructorExpr::getCalledConstructor. + auto &ctx = getASTContext(); if (auto unresolvedDot = dyn_cast(expr)) { - if (auto self - = TC.getSelfForInitDelegationInConstructor(DC, unresolvedDot)) { + if (auto self = TypeChecker::getSelfForInitDelegationInConstructor( + DC, unresolvedDot)) { // Walk our ancestor expressions looking for the appropriate place // to insert the RebindSelfInConstructorExpr. Expr *target = nullptr; @@ -1282,8 +1245,8 @@ namespace { // RebindSelfInConstructorExpr, wrap it in the // RebindSelfInConstructorExpr. if (expr == UnresolvedCtorRebindTarget) { - expr = new (TC.Context) RebindSelfInConstructorExpr(expr, - UnresolvedCtorSelf); + expr = new (ctx) + RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf); UnresolvedCtorRebindTarget = nullptr; return expr; } @@ -1352,15 +1315,12 @@ bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) { // afterwards. This allows for better diagnostics, and keeps the // closure expression type well-formed. for (auto param : *PL) { - // FIXME: Forces computation of isInvalid(). - (void) param->getInterfaceType(); - hadParameterError |= param->isInvalid(); } // Validate the result type, if present. if (closure->hasExplicitResultType() && - TypeChecker::validateType(TC.Context, + TypeChecker::validateType(getASTContext(), closure->getExplicitResultTypeLoc(), TypeResolution::forContextual(closure), TypeResolverContext::InExpression)) { @@ -1389,7 +1349,7 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { UDE->getName().isSpecial()) return nullptr; - auto Name = UDE->getName().getBaseIdentifier(); + auto Name = UDE->getName(); auto NameLoc = UDE->getNameLoc().getBaseNameLoc(); // Qualified type lookup with a module base is represented as a DeclRefExpr @@ -1402,17 +1362,14 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { lookupOptions |= NameLookupFlags::KnownPrivate; // See if the type has a member type with this name. - auto Result = TC.lookupMemberType(DC, - TD->getDeclaredInterfaceType(), - Name, - lookupOptions); + auto Result = TypeChecker::lookupMemberType( + DC, TD->getDeclaredInterfaceType(), Name, lookupOptions); // If there is no nested type with this name, we have a lookup of // a non-type member, so leave the expression as-is. if (Result.size() == 1) { - return TypeExpr::createForMemberDecl(DRE->getNameLoc().getBaseNameLoc(), - TD, NameLoc, - Result.front().Member); + return TypeExpr::createForMemberDecl( + DRE->getNameLoc(), TD, UDE->getNameLoc(), Result.front().Member); } } @@ -1428,18 +1385,18 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { return nullptr; // Fold 'T.Protocol' into a protocol metatype. - if (Name == TC.Context.Id_Protocol) { + if (Name.isSimpleName(getASTContext().Id_Protocol)) { auto *NewTypeRepr = - new (TC.Context) ProtocolTypeRepr(InnerTypeRepr, NameLoc); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold 'T.Type' into an existential metatype if 'T' is a protocol, // or an ordinary metatype otherwise. - if (Name == TC.Context.Id_Type) { + if (Name.isSimpleName(getASTContext().Id_Type)) { auto *NewTypeRepr = - new (TC.Context) MetatypeTypeRepr(InnerTypeRepr, NameLoc); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold 'T.U' into a nested type. @@ -1460,15 +1417,13 @@ TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) { lookupOptions |= NameLookupFlags::KnownPrivate; // See if there is a member type with this name. - auto Result = TC.lookupMemberType(DC, - BaseTy, - Name, - lookupOptions); + auto Result = + TypeChecker::lookupMemberType(DC, BaseTy, Name, lookupOptions); // If there is no nested type with this name, we have a lookup of // a non-type member, so leave the expression as-is. if (Result.size() == 1) { - return TypeExpr::createForMemberDecl(ITR, NameLoc, + return TypeExpr::createForMemberDecl(ITR, UDE->getNameLoc(), Result.front().Member); } } @@ -1492,7 +1447,7 @@ TypeExpr *PreCheckExpression::simplifyUnresolvedSpecializeExpr( if (auto *te = dyn_cast(us->getSubExpr())) { if (auto *ITR = dyn_cast_or_null(te->getTypeRepr())) { return TypeExpr::createForSpecializedDecl(ITR, genericArgs, angleRange, - TC.Context); + getASTContext()); } } @@ -1535,8 +1490,8 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { return TyExpr; auto *NewTypeRepr = - new (TC.Context) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + new (getASTContext()) OptionalTypeRepr(InnerTypeRepr, QuestionLoc); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold T! into an IUO type when T is a TypeExpr. @@ -1549,10 +1504,10 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { "This doesn't work on implicit TypeExpr's, " "the TypeExpr should have been built correctly in the first place"); - auto *NewTypeRepr = - new (TC.Context) ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, - FVE->getExclaimLoc()); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + auto *NewTypeRepr = new (getASTContext()) + ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr, + FVE->getExclaimLoc()); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold (T) into a type T with parens around it. @@ -1564,11 +1519,10 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type && "SubscriptExpr doesn't work on implicit TypeExpr's, " "the TypeExpr should have been built correctly in the first place"); - - auto *NewTypeRepr = TupleTypeRepr::create(TC.Context, - InnerTypeRepr, + + auto *NewTypeRepr = TupleTypeRepr::create(getASTContext(), InnerTypeRepr, PE->getSourceRange()); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold a tuple expr like (T1,T2) into a tuple type (T1,T2). @@ -1599,10 +1553,9 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { Elts.push_back(elt); ++EltNo; } - auto *NewTypeRepr = TupleTypeRepr::create(TC.Context, Elts, - TE->getSourceRange(), - SourceLoc(), Elts.size()); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + auto *NewTypeRepr = TupleTypeRepr::create( + getASTContext(), Elts, TE->getSourceRange(), SourceLoc(), Elts.size()); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } @@ -1615,12 +1568,10 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { if (!TyExpr) return nullptr; - auto *NewTypeRepr = - new (TC.Context) ArrayTypeRepr(TyExpr->getTypeRepr(), - SourceRange(AE->getLBracketLoc(), - AE->getRBracketLoc())); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); - + auto *NewTypeRepr = new (getASTContext()) + ArrayTypeRepr(TyExpr->getTypeRepr(), + SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold [K : V] into a dictionary type. @@ -1657,12 +1608,11 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { valueTypeRepr = TRE->getElementType(1); } - auto *NewTypeRepr = - new (TC.Context) DictionaryTypeRepr(keyTypeRepr, valueTypeRepr, - /*FIXME:colonLoc=*/SourceLoc(), - SourceRange(DE->getLBracketLoc(), - DE->getRBracketLoc())); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + auto *NewTypeRepr = new (getASTContext()) DictionaryTypeRepr( + keyTypeRepr, valueTypeRepr, + /*FIXME:colonLoc=*/SourceLoc(), + SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc())); + return new (getASTContext()) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Reinterpret arrow expr T1 -> T2 as function type. @@ -1670,25 +1620,26 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { if (auto *AE = dyn_cast(E)) { if (!AE->isFolded()) return nullptr; - auto diagnoseMissingParens = [](TypeChecker &TC, TypeRepr *tyR) { + auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) { bool isVoid = false; if (const auto Void = dyn_cast(tyR)) { - if (Void->getIdentifier().str() == "Void") { + if (Void->getNameRef().isSimpleName(ctx.Id_Void)) { isVoid = true; } } if (isVoid) { - TC.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) .fixItReplace(tyR->getStartLoc(), "()"); } else { - TC.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) + ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens) .highlight(tyR->getSourceRange()) .fixItInsert(tyR->getStartLoc(), "(") .fixItInsertAfter(tyR->getEndLoc(), ")"); } }; + auto &ctx = getASTContext(); auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * { if (!E) return nullptr; @@ -1696,13 +1647,13 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { auto ArgRepr = TyE->getTypeRepr(); if (auto *TTyRepr = dyn_cast(ArgRepr)) return TTyRepr; - diagnoseMissingParens(TC, ArgRepr); - return TupleTypeRepr::create(TC.Context, {ArgRepr}, - ArgRepr->getSourceRange()); + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); } if (auto *TE = dyn_cast(E)) if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(TC.Context, TE->getSourceRange()); + return TupleTypeRepr::createEmpty(getASTContext(), + TE->getSourceRange()); // When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int", // it may have been folded at the same time; recursively simplify it. @@ -1710,9 +1661,8 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { auto ArgRepr = ArgsTypeExpr->getTypeRepr(); if (auto *TTyRepr = dyn_cast(ArgRepr)) return TTyRepr; - diagnoseMissingParens(TC, ArgRepr); - return TupleTypeRepr::create(TC.Context, {ArgRepr}, - ArgRepr->getSourceRange()); + diagnoseMissingParens(ctx, ArgRepr); + return TupleTypeRepr::create(ctx, {ArgRepr}, ArgRepr->getSourceRange()); } return nullptr; }; @@ -1724,7 +1674,7 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { return TyE->getTypeRepr(); if (auto *TE = dyn_cast(E)) if (TE->getNumElements() == 0) - return TupleTypeRepr::createEmpty(TC.Context, TE->getSourceRange()); + return TupleTypeRepr::createEmpty(ctx, TE->getSourceRange()); // When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int", // it may have been folded at the same time; recursively simplify it. @@ -1735,27 +1685,26 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr()); if (!ArgsTypeRepr) { - TC.diagnose(AE->getArgsExpr()->getLoc(), - diag::expected_type_before_arrow); + ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(), + diag::expected_type_before_arrow); auto ArgRange = AE->getArgsExpr()->getSourceRange(); - auto ErrRepr = - new (TC.Context) ErrorTypeRepr(ArgRange); - ArgsTypeRepr = TupleTypeRepr::create(TC.Context, {ErrRepr}, ArgRange); + auto ErrRepr = new (ctx) ErrorTypeRepr(ArgRange); + ArgsTypeRepr = + TupleTypeRepr::create(ctx, {ErrRepr}, ArgRange); } TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr()); if (!ResultTypeRepr) { - TC.diagnose(AE->getResultExpr()->getLoc(), - diag::expected_type_after_arrow); - ResultTypeRepr = - new (TC.Context) ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); + ctx.Diags.diagnose(AE->getResultExpr()->getLoc(), + diag::expected_type_after_arrow); + ResultTypeRepr = new (ctx) + ErrorTypeRepr(AE->getResultExpr()->getSourceRange()); } - auto NewTypeRepr = - new (TC.Context) FunctionTypeRepr(nullptr, ArgsTypeRepr, - AE->getThrowsLoc(), AE->getArrowLoc(), - ResultTypeRepr); - return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type())); + auto NewTypeRepr = new (ctx) + FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getThrowsLoc(), + AE->getArrowLoc(), ResultTypeRepr); + return new (ctx) TypeExpr(TypeLoc(NewTypeRepr, Type())); } // Fold 'P & Q' into a composition type @@ -1804,9 +1753,10 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) { if (!rhs) return nullptr; Types.push_back(rhs->getTypeRepr()); - auto CompRepr = CompositionTypeRepr::create(TC.Context, Types, - lhsExpr->getStartLoc(), binaryExpr->getSourceRange()); - return new (TC.Context) TypeExpr(TypeLoc(CompRepr, Type())); + auto CompRepr = CompositionTypeRepr::create(getASTContext(), Types, + lhsExpr->getStartLoc(), + binaryExpr->getSourceRange()); + return new (getASTContext()) TypeExpr(TypeLoc(CompRepr, Type())); } } @@ -1822,6 +1772,7 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { TypeRepr *rootType = nullptr; SmallVector components; + auto &DE = getASTContext().Diags; // Pre-order visit of a sequence foo.bar[0]?.baz, which means that the // components are pushed in reverse order. @@ -1856,8 +1807,8 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { // .[0] or just plain [0] components.push_back( KeyPathExpr::Component::forUnresolvedSubscriptWithPrebuiltIndexExpr( - TC.Context, - SE->getIndex(), SE->getArgumentLabels(), SE->getLoc())); + getASTContext(), SE->getIndex(), SE->getArgumentLabels(), + SE->getLoc())); expr = SE->getBase(); } else if (auto BOE = dyn_cast(expr)) { @@ -1883,10 +1834,10 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { // \() may be an attempt to write a string interpolation outside // of a string literal; diagnose this case specially. if (isa(expr) || isa(expr)) { - TC.diagnose(expr->getLoc(), + DE.diagnose(expr->getLoc(), diag::expr_string_interpolation_outside_string); } else { - TC.diagnose(expr->getLoc(), + DE.diagnose(expr->getLoc(), diag::expr_swift_keypath_invalid_component); } } @@ -1910,7 +1861,7 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { } else { // FIXME: Probably better to catch this case earlier and force-eval as // TypeExpr. - TC.diagnose(root->getLoc(), + DE.diagnose(root->getLoc(), diag::expr_swift_keypath_not_starting_with_type); // Traverse this path for recovery purposes: it may be a typo like @@ -1925,7 +1876,7 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { // Key paths must be spelled with at least one component. if (components.empty()) { - TC.diagnose(KPE->getLoc(), diag::expr_swift_keypath_empty); + DE.diagnose(KPE->getLoc(), diag::expr_swift_keypath_empty); // Passes further down the pipeline expect keypaths to always have at least // one component, so stuff an invalid component in the AST for recovery. components.push_back(KeyPathExpr::Component()); @@ -1934,13 +1885,13 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) { std::reverse(components.begin(), components.end()); KPE->setRootType(rootType); - KPE->resolveComponents(TC.Context, components); + KPE->resolveComponents(getASTContext(), components); } Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { // If constructor call is expected to produce an optional let's not attempt // this optimization because literal initializers aren't failable. - if (!TC.getLangOpts().isSwiftVersionAtLeast(5)) { + if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) { if (!ExprStack.empty()) { auto *parent = ExprStack.back(); if (isa(parent) || isa(parent)) @@ -1961,7 +1912,7 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { if (!literal) return nullptr; - auto *protocol = TC.getLiteralProtocol(literal); + auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal); if (!protocol) return nullptr; @@ -1973,9 +1924,8 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { options |= TypeResolutionFlags::AllowUnboundGenerics; auto &typeLoc = typeExpr->getTypeLoc(); - bool hadError = - TypeChecker::validateType(TC.Context, typeLoc, - TypeResolution::forContextual(DC), options); + bool hadError = TypeChecker::validateType( + getASTContext(), typeLoc, TypeResolution::forContextual(DC), options); if (hadError) return nullptr; @@ -1987,7 +1937,7 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { return nullptr; // Don't bother to convert deprecated selector syntax. - if (auto selectorTy = TC.getObjCSelectorType(DC)) { + if (auto selectorTy = getASTContext().getSelectorType()) { if (type->isEqual(selectorTy)) return nullptr; } @@ -1995,7 +1945,7 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { auto *NTD = type->getAnyNominal(); SmallVector conformances; return NTD->lookupConformance(DC->getParentModule(), protocol, conformances) - ? CoerceExpr::forLiteralInit(TC.Context, argExpr, + ? CoerceExpr::forLiteralInit(getASTContext(), argExpr, call->getSourceRange(), typeExpr->getTypeLoc()) : nullptr; @@ -2003,8 +1953,9 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) { /// Pre-check the expression, validating any types that occur in the /// expression and folding sequence expressions. -bool TypeChecker::preCheckExpression(Expr *&expr, DeclContext *dc) { - PreCheckExpression preCheck(*this, dc, expr); +bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc, + ConstraintSystem *baseCS) { + PreCheckExpression preCheck(dc, expr, baseCS); // Perform the pre-check. if (auto result = expr->walk(preCheck)) { expr = result; @@ -2019,19 +1970,10 @@ bool ExprTypeCheckListener::builtConstraints(ConstraintSystem &cs, Expr *expr) { return false; } -Expr *ExprTypeCheckListener::foundSolution(Solution &solution, Expr *expr) { - return expr; -} - Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} -void ExprTypeCheckListener::constraintGenerationFailed(Expr *expr) {} -void ExprTypeCheckListener::applySolutionFailed(Solution &solution, - Expr *expr) {} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { @@ -2059,87 +2001,39 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } -/// Sometimes constraint solver fails without producing any diagnostics, -/// that leads to crashes down the line in AST Verifier or SILGen -/// which, as a result, are much harder to figure out. -/// -/// This class is intended to guard against situations like that by -/// keeping track of failures of different type-check phases, and -/// emitting fallback fatal error if any of them fail without producing -/// error diagnostic, and there were no errors emitted or scheduled to be -/// emitted previously. -class FallbackDiagnosticListener : public ExprTypeCheckListener { - TypeChecker &TC; - TypeCheckExprOptions Options; - ExprTypeCheckListener *BaseListener; - -public: - FallbackDiagnosticListener(TypeChecker &TC, TypeCheckExprOptions options, - ExprTypeCheckListener *base) - : TC(TC), Options(options), BaseListener(base) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - return BaseListener ? BaseListener->builtConstraints(cs, expr) : false; - } - - Expr *foundSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->foundSolution(solution, expr) : expr; - } - - Expr *appliedSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; - } - - void preCheckFailed(Expr *expr) override { - if (BaseListener) - BaseListener->preCheckFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - - void constraintGenerationFailed(Expr *expr) override { - if (BaseListener) - BaseListener->constraintGenerationFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - - void applySolutionFailed(Solution &solution, Expr *expr) override { - if (BaseListener) - BaseListener->applySolutionFailed(solution, expr); - - if (hadAnyErrors()) - return; - - // If solution involves invalid or incomplete conformances that's - // a probable cause of failure to apply it without producing an error, - // which is going to be diagnosed later, so let's not produce - // fallback diagnostic in this case. - if (llvm::any_of( - solution.Conformances, - [](const std::pair - &conformance) -> bool { - auto &ref = conformance.second; - return ref.isConcrete() && ref.getConcrete()->isInvalid(); - })) - return; - - maybeProduceFallbackDiagnostic(expr); - } - -private: - bool hadAnyErrors() const { return TC.Context.Diags.hadAnyError(); } - - void maybeProduceFallbackDiagnostic(Expr *expr) const { - if (Options.contains(TypeCheckExprFlags::SubExpressionDiagnostics) || - DiagnosticSuppression::isEnabled(TC.Diags)) - return; - - // Before producing fatal error here, let's check if there are any "error" - // diagnostics already emitted or waiting to be emitted. Because they are - // a better indication of the problem. - if (!(hadAnyErrors() || TC.Context.hasDelayedConformanceErrors())) - TC.diagnose(expr->getLoc(), diag::failed_to_produce_diagnostic); +/// Whether the contextual type provided for the given purpose is only a +/// hint, and not a requirement. +static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp, + TypeCheckExprOptions options) { + switch (ctp) { + case CTP_Initialization: + return !options.contains( + TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType); + case CTP_ForEachStmt: + return true; + case CTP_Unused: + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_YieldByValue: + case CTP_YieldByReference: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_CalleeResult: + case CTP_CallArgument: + case CTP_ClosureResult: + case CTP_ArrayElement: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_AssignSource: + case CTP_SubscriptAssignSource: + case CTP_Condition: + case CTP_CannotFail: + return false; } -}; +} #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, @@ -2148,43 +2042,29 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeCheckExprOptions options, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { - FallbackDiagnosticListener diagListener(*this, options, listener); - return typeCheckExpressionImpl(expr, dc, convertType, convertTypePurpose, - options, diagListener, baseCS); -} - -Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - ConstraintSystem *baseCS) { + auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. - if (preCheckExpression(expr, dc)) { - listener.preCheckFailed(expr); + if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { return Type(); } // Construct a constraint system from this expression. ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes; - if (DiagnosticSuppression::isEnabled(Diags)) + if (DiagnosticSuppression::isEnabled(Context.Diags)) csOptions |= ConstraintSystemFlags::SuppressDiagnostics; if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) csOptions |= ConstraintSystemFlags::AllowUnresolvedTypeVariables; - if (options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)) - csOptions |= ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType; - if (options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) csOptions |= ConstraintSystemFlags::SubExpressionDiagnostics; - ConstraintSystem cs(*this, dc, csOptions, expr); + ConstraintSystem cs(dc, csOptions); cs.baseCS = baseCS; // Verify that a purpose was specified if a convertType was. Note that it is @@ -2204,13 +2084,28 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, } } + // For an @autoclosure default parameter, we want to convert to the result + // type. Stash the autoclosure default parameter type. + FunctionType *autoclosureDefaultParamType = nullptr; + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + autoclosureDefaultParamType = convertType.getType()->castTo(); + convertType.setType(autoclosureDefaultParamType->getResult()); + } + // Tell the constraint system what the contextual type is. This informs // diagnostics and is a hint for various performance optimizations. - cs.setContextualType(expr, convertType, convertTypePurpose); + // FIXME: Look through LoadExpr. This is an egregious hack due to the + // way typeCheckExprIndependently works. + Expr *contextualTypeExpr = expr; + if (auto loadExpr = dyn_cast_or_null(contextualTypeExpr)) + contextualTypeExpr = loadExpr->getSubExpr(); + cs.setContextualType( + contextualTypeExpr, convertType, convertTypePurpose, + options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)); // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. - if (options.contains(TypeCheckExprFlags::ConvertTypeIsOnlyAHint)) + if (contextualTypeIsOnlyAHint(convertTypePurpose, options)) convertType = TypeLoc(); // If the client can handle unresolved type variables, leave them in the @@ -2220,6 +2115,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; Type convertTo = convertType.getType(); + if (options.contains(TypeCheckExprFlags::ExpressionTypeMustBeOptional)) { assert(!convertTo && "convertType and type check options conflict"); auto *convertTypeLocator = @@ -2228,44 +2124,56 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, convertTo = getOptionalType(expr->getLoc(), var); } - SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, &listener, viable, allowFreeTypeVariables)) + SolutionApplicationTarget target( + expr, convertTypePurpose, convertTo, + options.contains(TypeCheckExprFlags::IsDiscarded)); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) return Type(); // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && - (viable.size() != 1 || + (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { return ErrorType::get(Context); } - auto result = expr; - auto &solution = viable[0]; - result = listener.foundSolution(solution, result); + auto result = target.getAsExpr(); + auto &solution = (*viable)[0]; if (!result) return Type(); - // Apply the solution to the expression. - result = cs.applySolution( - solution, result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SkipMultiStmtClosures)); + // Apply this solution to the constraint system. + cs.applySolution(solution); - if (!result) { - listener.applySolutionFailed(solution, expr); + // Apply the solution to the expression. + bool performingDiagnostics = + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + // FIXME: HACK! + target.setExprConversionType(convertType.getType()); + auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); + if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. return Type(); } + result = resultTarget->getAsExpr(); + + // For an @autoclosure default parameter type, add the autoclosure + // conversion. + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType); + } // Notify listener that we've applied the solution. - result = listener.appliedSolution(solution, result); + if (listener) + result = listener->appliedSolution(solution, result); if (!result) return Type(); - if (getLangOpts().DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Type-checked expression---\n"; result->dump(log); @@ -2275,9 +2183,9 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, // Unless the client has disabled them, perform syntactic checks on the // expression now. if (!cs.shouldSuppressDiagnostics() && - !options.contains(TypeCheckExprFlags::DisableStructuralChecks)) { + !options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) { bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt); - performSyntacticExprDiagnostics(*this, result, dc, isExprStmt); + performSyntacticExprDiagnostics(result, dc, isExprStmt); } expr = result; @@ -2286,37 +2194,11 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, - bool isAutoClosure, bool canFail) { + bool isAutoClosure) { assert(paramType && !paramType->hasError()); - - if (isAutoClosure) { - class AutoClosureListener : public ExprTypeCheckListener { - DeclContext *DC; - FunctionType *ParamType; - - public: - AutoClosureListener(DeclContext *DC, FunctionType *paramType) - : DC(DC), ParamType(paramType) {} - - Expr *appliedSolution(constraints::Solution &solution, - Expr *expr) override { - auto &cs = solution.getConstraintSystem(); - auto *closure = cs.TC.buildAutoClosureExpr(DC, expr, ParamType); - cs.cacheExprTypes(closure); - return closure; - } - }; - - auto *fnType = paramType->castTo(); - AutoClosureListener listener(DC, fnType); - return typeCheckExpression(defaultValue, DC, - TypeLoc::withoutLoc(fnType->getResult()), - canFail ? CTP_DefaultParameter : CTP_CannotFail, - TypeCheckExprOptions(), &listener); - } - - return typeCheckExpression(defaultValue, DC, TypeLoc::withoutLoc(paramType), - canFail ? CTP_DefaultParameter : CTP_CannotFail); + return typeCheckExpression( + defaultValue, DC, TypeLoc::withoutLoc(paramType), + isAutoClosure ? CTP_AutoclosureDefaultParameter : CTP_DefaultParameter); } Type TypeChecker:: @@ -2324,15 +2206,15 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, ConcreteDeclRef &referencedDecl, FreeTypeVariableBinding allowFreeTypeVariables, ExprTypeCheckListener *listener) { + auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr-no-apply", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); referencedDecl = nullptr; // Construct a constraint system from this expression. - ConstraintSystem cs(*this, dc, ConstraintSystemFlags::SuppressDiagnostics); + ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics); // Attempt to solve the constraint system. - SmallVector viable; const Type originalType = expr->getType(); const bool needClearType = originalType && originalType->hasError(); const auto recoverOriginalType = [&] () { @@ -2344,14 +2226,17 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - if (cs.solve(expr, /*convertType*/Type(), listener, viable, - allowFreeTypeVariables)) { + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) { recoverOriginalType(); return Type(); } // Get the expression's simplified type. - auto &solution = viable[0]; + expr = target.getAsExpr(); + auto &solution = (*viable)[0]; auto &solutionCS = solution.getConstraintSystem(); Type exprType = solution.simplifyType(solutionCS.getType(expr)); @@ -2406,6 +2291,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( Expr *&expr, DeclContext *dc, SmallPtrSetImpl &types, FreeTypeVariableBinding allowFreeTypeVariables, ExprTypeCheckListener *listener) { + auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "get-possible-types-no-apply", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -2414,29 +2300,29 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( options |= ConstraintSystemFlags::ReturnAllDiscoveredSolutions; options |= ConstraintSystemFlags::SuppressDiagnostics; - ConstraintSystem cs(*this, dc, options); + ConstraintSystem cs(dc, options); // Attempt to solve the constraint system. - SmallVector viable; - const Type originalType = expr->getType(); if (originalType && originalType->hasError()) expr->setType(Type()); - cs.solve(expr, /*convertType*/ Type(), listener, viable, - allowFreeTypeVariables); - - for (auto &solution : viable) { - auto exprType = solution.simplifyType(cs.getType(expr)); - assert(exprType && !exprType->hasTypeVariable()); - types.insert(exprType.getPointer()); + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); + if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { + expr = target.getAsExpr(); + for (auto &solution : *viable) { + auto exprType = solution.simplifyType(cs.getType(expr)); + assert(exprType && !exprType->hasTypeVariable()); + types.insert(exprType.getPointer()); + } } } static FunctionType * -getTypeOfCompletionOperatorImpl(TypeChecker &TC, DeclContext *DC, Expr *expr, +getTypeOfCompletionOperatorImpl(DeclContext *DC, Expr *expr, ConcreteDeclRef &referencedDecl) { - ASTContext &Context = TC.Context; + auto &Context = DC->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-completion-operator", expr); @@ -2447,12 +2333,12 @@ getTypeOfCompletionOperatorImpl(TypeChecker &TC, DeclContext *DC, Expr *expr, options |= ConstraintSystemFlags::ReusePrecheckedType; // Construct a constraint system from this expression. - ConstraintSystem CS(TC, DC, options); - expr = CS.generateConstraints(expr); + ConstraintSystem CS(DC, options); + expr = CS.generateConstraints(expr, DC); if (!expr) return nullptr; - if (TC.getLangOpts().DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Initial constraints for the given expression---\n"; expr->dump(log); @@ -2462,11 +2348,11 @@ getTypeOfCompletionOperatorImpl(TypeChecker &TC, DeclContext *DC, Expr *expr, // Attempt to solve the constraint system. SmallVector viable; - if (CS.solve(expr, viable, FreeTypeVariableBinding::Disallow)) + if (CS.solve(viable, FreeTypeVariableBinding::Disallow)) return nullptr; auto &solution = viable[0]; - if (TC.getLangOpts().DebugConstraintSolver) { + if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); log << "---Solution---\n"; solution.dump(log); @@ -2501,7 +2387,7 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, // For the infix operator, find the actual LHS from pre-folded LHS. if (refKind == DeclRefKind::BinaryOperator) - LHS = findLHS(DC, LHS, opName); + LHS = TypeChecker::findLHS(DC, LHS, opName); if (!LHS) return nullptr; @@ -2521,8 +2407,8 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, // Build temporary expression to typecheck. // We allocate these expressions on the stack because we know they can't // escape and there isn't a better way to allocate scratch Expr nodes. - UnresolvedDeclRefExpr UDRE(opName, refKind, DeclNameLoc(Loc)); - auto *opExpr = resolveDeclRefExpr(&UDRE, DC); + UnresolvedDeclRefExpr UDRE(DeclNameRef(opName), refKind, DeclNameLoc(Loc)); + auto *opExpr = TypeChecker::resolveDeclRefExpr(&UDRE, DC); switch (refKind) { @@ -2534,7 +2420,7 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, ParenExpr Args(SourceLoc(), LHS, SourceLoc(), /*hasTrailingClosure=*/false); PostfixUnaryExpr postfixExpr(opExpr, &Args); - return getTypeOfCompletionOperatorImpl(*this, DC, &postfixExpr, + return getTypeOfCompletionOperatorImpl(DC, &postfixExpr, referencedDecl); } @@ -2546,11 +2432,11 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, // (code_completion_expr))) CodeCompletionExpr dummyRHS(Loc); auto Args = TupleExpr::create( - Context, SourceLoc(), {LHS, &dummyRHS}, {}, {}, SourceLoc(), + DC->getASTContext(), SourceLoc(), {LHS, &dummyRHS}, {}, {}, SourceLoc(), /*hasTrailingClosure=*/false, /*isImplicit=*/true); BinaryExpr binaryExpr(opExpr, Args, /*isImplicit=*/true); - return getTypeOfCompletionOperatorImpl(*this, DC, &binaryExpr, + return getTypeOfCompletionOperatorImpl(DC, &binaryExpr, referencedDecl); } @@ -2560,11 +2446,12 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, - DeclContext *DC) { + DeclContext *DC, + Type patternType) { /// Type checking listener for pattern binding initializers. class BindingListener : public ExprTypeCheckListener { - TypeChecker &tc; + ASTContext &context; Pattern *&pattern; Expr *&initializer; @@ -2578,10 +2465,10 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, VarDecl *wrappedVar = nullptr; public: - explicit BindingListener(TypeChecker &tc, Pattern *&pattern, + explicit BindingListener(ASTContext &ctx, Pattern *&pattern, Expr *&initializer) - : tc(tc), pattern(pattern), initializer(initializer), - Locator(nullptr) { + : context(ctx), pattern(pattern), initializer(initializer), + Locator(nullptr) { maybeApplyPropertyWrapper(); } @@ -2596,26 +2483,25 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, if (cs) { Type valueType = LValueType::get(initType); auto dc = wrappedVar->getInnermostDeclContext(); - auto emptyLocator = cs->getConstraintLocator(nullptr); + auto *loc = cs->getConstraintLocator(initializer); for (unsigned i : indices(wrappedVar->getAttachedPropertyWrappers())) { auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); if (!wrapperInfo) break; - Type memberType = - cs->createTypeVariable(emptyLocator, TVO_CanBindToLValue); + loc = cs->getConstraintLocator(loc, ConstraintLocator::Member); + Type memberType = cs->createTypeVariable(loc, TVO_CanBindToLValue); cs->addValueMemberConstraint( - valueType, wrapperInfo.valueVar->getFullName(), - memberType, dc, FunctionRefKind::Unapplied, { }, emptyLocator); + valueType, wrapperInfo.valueVar->createNameRef(), + memberType, dc, FunctionRefKind::Unapplied, { }, loc); valueType = memberType; } // Set up an equality constraint to drop the lvalue-ness of the value // type we produced. - Type propertyType = cs->createTypeVariable(emptyLocator, 0); - cs->addConstraint(ConstraintKind::Equal, propertyType, valueType, - emptyLocator); + Type propertyType = cs->createTypeVariable(loc, 0); + cs->addConstraint(ConstraintKind::Equal, propertyType, valueType, loc); return propertyType; } @@ -2648,8 +2534,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, initType = patternType; // Add a conversion constraint between the types. - if (!cs.Options.contains( - ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType)) { + if (!initType->is()) { cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), patternType, Locator, /*isFavored*/true); } @@ -2660,19 +2545,15 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return false; } - Expr *foundSolution(Solution &solution, Expr *expr) override { - // Figure out what type the constraints decided on. - auto ty = solution.simplifyType(initType); - initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); - - // Just keep going. - return expr; - } - Expr *appliedSolution(Solution &solution, Expr *expr) override { + { + // Figure out what type the constraints decided on. + auto ty = solution.simplifyType(initType); + initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); + } + // Convert the initializer to the type of the pattern. - expr = solution.coerceToType(expr, initType, Locator, - false /* ignoreTopLevelInjection */); + expr = solution.coerceToType(expr, initType, Locator); if (!expr) return nullptr; @@ -2682,7 +2563,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // been subsumed by the backing property. if (wrappedVar) { wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0); - tc.Context.setSideCachedPropertyWrapperBackingPropertyType( + context.setSideCachedPropertyWrapperBackingPropertyType( wrappedVar, initType->mapTypeOutOfContext()); // Record the semantic initializer on the outermost property wrapper. @@ -2746,20 +2627,23 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } }; - BindingListener listener(*this, pattern, initializer); + auto &Context = DC->getASTContext(); + BindingListener listener(Context, pattern, initializer); if (!initializer) return true; TypeLoc contextualType; auto contextualPurpose = CTP_Unused; - TypeCheckExprOptions flags = TypeCheckExprFlags::ConvertTypeIsOnlyAHint; + TypeCheckExprOptions flags = None; // Set the contextual purpose even if the pattern doesn't have a type so // if there's an error we can use that information to inform diagnostics. contextualPurpose = CTP_Initialization; - if (pattern->hasType()) { - contextualType = TypeLoc::withoutLoc(pattern->getType()); + if (isa(pattern)) { + flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; + } else if (patternType && !patternType->isEqual(Context.TheUnresolvedType)) { + contextualType = TypeLoc::withoutLoc(patternType); // If we already had an error, don't repeat the problem. if (contextualType.getType()->hasError()) @@ -2767,19 +2651,19 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Allow the initializer expression to establish the underlying type of an // opaque type. - if (auto opaqueType = pattern->getType()->getAs()){ + if (auto opaqueType = patternType->getAs()){ flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; - flags -= TypeCheckExprFlags::ConvertTypeIsOnlyAHint; } // Only provide a TypeLoc if it makes sense to allow diagnostics. if (auto *typedPattern = dyn_cast(pattern)) { const Pattern *inner = typedPattern->getSemanticsProvidingPattern(); - if (isa(inner) || isa(inner)) + if (isa(inner) || isa(inner)) { contextualType = typedPattern->getTypeLoc(); + if (!contextualType.getType()) + contextualType.setType(patternType); + } } - } else if (isa(pattern)) { - flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; } // Type-check the initializer. @@ -2801,8 +2685,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return true; // Apply the solution to the pattern as well. - if (coercePatternToType(pattern, TypeResolution::forContextual(DC), initTy, - options, TypeLoc())) { + auto contextualPattern = + ContextualPattern::forRawPattern(pattern, DC); + if (auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, initTy, options)) { + pattern = coercedPattern; + } else { return true; } } @@ -2814,17 +2702,18 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // and its variables, to prevent it from being referenced by the constraint // system. if (!resultTy && - (!pattern->hasType() || pattern->getType()->hasUnboundGenericType())) { + (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType())) { pattern->setType(ErrorType::get(Context)); pattern->forEachVariable([&](VarDecl *var) { // Don't change the type of a variable that we've been able to // compute a type for. - if (var->hasType() && + if (var->hasInterfaceType() && !var->getType()->hasUnboundGenericType() && - !var->getType()->hasError()) + !var->isInvalid()) return; - var->markInvalid(); + var->setInvalid(); }); } @@ -2832,8 +2721,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, - unsigned patternNumber) { - const auto &pbe = PBD->getPatternList()[patternNumber]; + unsigned patternNumber, + Type patternType) { Pattern *pattern = PBD->getPattern(patternNumber); Expr *init = PBD->getInit(patternNumber); @@ -2841,12 +2730,28 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, PatternBindingInitializer *initContext = nullptr; DeclContext *DC = PBD->getDeclContext(); if (!DC->isLocalContext()) { - initContext = cast_or_null(pbe.getInitContext()); + initContext = cast_or_null( + PBD->getInitContext(patternNumber)); if (initContext) DC = initContext; } - bool hadError = typeCheckBinding(pattern, init, DC); + // If we weren't given a pattern type, compute one now. + if (!patternType) { + if (pattern->hasType()) + patternType = pattern->getType(); + else { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + patternType = typeCheckPattern(contextualPattern); + } + + if (patternType->hasError()) { + PBD->setInvalid(); + return true; + } + } + + bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC, patternType); if (!init) { PBD->setInvalid(); return true; @@ -2874,7 +2779,7 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, opaque->setUnderlyingTypeSubstitutions(underlyingSubs); } else { - diagnose(var->getLoc(), diag::opaque_type_var_no_underlying_type); + var->diagnose(diag::opaque_type_var_no_underlying_type); } } } @@ -2893,42 +2798,80 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { /// The for-each statement. ForEachStmt *Stmt; + /// The declaration context in which this for-each statement resides. + DeclContext *DC; + /// The locator we're using. ConstraintLocator *Locator; + /// The contextual locator we're using. + ConstraintLocator *ContextualLocator; + + /// The Sequence protocol. + ProtocolDecl *SequenceProto; + + /// The IteratorProtocol. + ProtocolDecl *IteratorProto; + /// The type of the initializer. Type InitType; /// The type of the sequence. Type SequenceType; + /// The conformance of the sequence type to the Sequence protocol. + ProtocolConformanceRef SequenceConformance; + + /// The type of the element. + Type ElementType; + + /// The type of the iterator. + Type IteratorType; + public: - explicit BindingListener(ForEachStmt *stmt) : Stmt(stmt) { } + explicit BindingListener(ForEachStmt *stmt, DeclContext *dc) + : Stmt(stmt), DC(dc) { } bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { // Save the locator we're using for the expression. Locator = cs.getConstraintLocator(expr); + ContextualLocator = + cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); - // The expression type must conform to the Sequence. - auto &tc = cs.getTypeChecker(); - ProtocolDecl *sequenceProto - = tc.getProtocol(Stmt->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { + // The expression type must conform to the Sequence protocol. + SequenceProto = TypeChecker::getProtocol( + cs.getASTContext(), Stmt->getForLoc(), KnownProtocolKind::Sequence); + if (!SequenceProto) { return true; } - auto elementAssocType = sequenceProto->getAssociatedType( - tc.Context.Id_Element); - SequenceType = cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), SequenceType, Locator); cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, - sequenceProto->getDeclaredType(), Locator); + SequenceProto->getDeclaredType(), ContextualLocator); - auto elementLocator = - cs.getConstraintLocator(Locator, - ConstraintLocator::SequenceElementType); + auto elementLocator = cs.getConstraintLocator( + ContextualLocator, ConstraintLocator::SequenceElementType); + + // Check the element pattern. + ASTContext &ctx = cs.getASTContext(); + if (auto *P = TypeChecker::resolvePattern(Stmt->getPattern(), DC, + /*isStmtCondition*/false)) { + Stmt->setPattern(P); + } else { + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } + + auto contextualPattern = + ContextualPattern::forRawPattern(Stmt->getPattern(), DC); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { + // FIXME: Handle errors better. + Stmt->getPattern()->setType(ErrorType::get(ctx)); + return true; + } // Collect constraints from the element pattern. auto pattern = Stmt->getPattern(); @@ -2938,10 +2881,34 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Add a conversion constraint between the element type of the sequence // and the type of the element pattern. - auto elementType = DependentMemberType::get(SequenceType, elementAssocType); - cs.addConstraint(ConstraintKind::Conversion, elementType, InitType, + auto elementAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Element); + ElementType = DependentMemberType::get(SequenceType, elementAssocType); + cs.addConstraint(ConstraintKind::Conversion, ElementType, InitType, elementLocator); + // Determine the iterator type. + auto iteratorAssocType = + SequenceProto->getAssociatedType(cs.getASTContext().Id_Iterator); + IteratorType = DependentMemberType::get(SequenceType, iteratorAssocType); + + // The iterator type must conform to IteratorProtocol. + IteratorProto = TypeChecker::getProtocol( + cs.getASTContext(), Stmt->getForLoc(), + KnownProtocolKind::IteratorProtocol); + if (!IteratorProto) { + return true; + } + + // Reference the makeIterator witness. + FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); + Type makeIteratorType = + cs.createTypeVariable(Locator, TVO_CanBindToNoEscape); + cs.addValueWitnessConstraint( + LValueType::get(SequenceType), makeIterator, + makeIteratorType, DC, FunctionRefKind::Compound, + ContextualLocator); + Stmt->setSequence(expr); return false; } @@ -2949,56 +2916,129 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { Expr *appliedSolution(Solution &solution, Expr *expr) override { // Figure out what types the constraints decided on. auto &cs = solution.getConstraintSystem(); - auto &tc = cs.getTypeChecker(); + ASTContext &ctx = cs.getASTContext(); InitType = solution.simplifyType(InitType); SequenceType = solution.simplifyType(SequenceType); - - // Perform any necessary conversions of the sequence (e.g. [T]! -> [T]). - expr = solution.coerceToType(expr, SequenceType, cs.getConstraintLocator(expr)); - - if (!expr) return nullptr; + ElementType = solution.simplifyType(ElementType); + IteratorType = solution.simplifyType(IteratorType); cs.cacheExprTypes(expr); + Stmt->setSequence(expr); + solution.setExprTypes(expr); // Apply the solution to the iteration pattern as well. Pattern *pattern = Stmt->getPattern(); TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; - if (tc.coercePatternToType(pattern, TypeResolution::forContextual(cs.DC), - InitType, options)) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + pattern = TypeChecker::coercePatternToType(contextualPattern, + InitType, options); + if (!pattern) return nullptr; + Stmt->setPattern(pattern); + + // Get the conformance of the sequence type to the Sequence protocol. + SequenceConformance = solution.resolveConformance( + ContextualLocator, SequenceProto); + assert(!SequenceConformance.isInvalid() && + "Couldn't find sequence conformance"); + Stmt->setSequenceConformance(SequenceConformance); + + // Check the filtering condition. + // FIXME: This should be pulled into the constraint system itself. + if (auto *Where = Stmt->getWhere()) { + if (!TypeChecker::typeCheckCondition(Where, DC)) + Stmt->setWhere(Where); } - Stmt->setPattern(pattern); - Stmt->setSequence(expr); + // Invoke iterator() to get an iterator from the sequence. + VarDecl *iterator; + Type nextResultType = OptionalType::get(ElementType); + { + // Create a local variable to capture the iterator. + std::string name; + if (auto np = dyn_cast_or_null(Stmt->getPattern())) + name = "$"+np->getBoundName().str().str(); + name += "$generator"; + + iterator = new (ctx) VarDecl( + /*IsStatic*/ false, VarDecl::Introducer::Var, + /*IsCaptureList*/ false, Stmt->getInLoc(), + ctx.getIdentifier(name), DC); + iterator->setInterfaceType(IteratorType->mapTypeOutOfContext()); + iterator->setImplicit(); + Stmt->setIteratorVar(iterator); + + auto genPat = new (ctx) NamedPattern(iterator); + genPat->setImplicit(); + + // TODO: test/DebugInfo/iteration.swift requires this extra info to + // be around. + PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, genPat, + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType), + DC, /*VarLoc*/ Stmt->getForLoc()); + } + + // Create the iterator variable. + auto *varRef = TypeChecker::buildCheckedRefExpr( + iterator, DC, DeclNameLoc(Stmt->getInLoc()), /*implicit*/ true); + if (varRef) + Stmt->setIteratorVarRef(varRef); + + // Convert that Optional value to the type of the pattern. + auto optPatternType = OptionalType::get(Stmt->getPattern()->getType()); + if (!optPatternType->isEqual(nextResultType)) { + OpaqueValueExpr *elementExpr = + new (ctx) OpaqueValueExpr(Stmt->getInLoc(), nextResultType, + /*isPlaceholder=*/true); + Expr *convertElementExpr = elementExpr; + if (TypeChecker::typeCheckExpression( + convertElementExpr, DC, + TypeLoc::withoutLoc(optPatternType), + CTP_CoerceOperand).isNull()) { + return nullptr; + } + elementExpr->setIsPlaceholder(false); + Stmt->setElementExpr(elementExpr); + Stmt->setConvertElementExpr(convertElementExpr); + } - cs.setExprTypes(expr); return expr; } }; - BindingListener listener(stmt); + BindingListener listener(stmt, dc); Expr *seq = stmt->getSequence(); assert(seq && "type-checking an uninitialized for-each statement?"); + auto sequenceProto = TypeChecker::getProtocol( + dc->getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence); + if (!sequenceProto) + return true; + // Type-check the for-each loop sequence and element pattern. - auto resultTy = typeCheckExpression(seq, dc, &listener); - return !resultTy; + auto resultTy = TypeChecker::typeCheckExpression( + seq, dc, TypeLoc::withoutLoc(sequenceProto->getDeclaredType()), + CTP_ForEachStmt, None, &listener); + if (!resultTy) + return true; + return false; } bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { // If this expression is already typechecked and has type Bool, then just // re-typecheck it. if (expr->getType() && expr->getType()->isBool()) { - auto resultTy = typeCheckExpression(expr, dc); + auto resultTy = TypeChecker::typeCheckExpression(expr, dc); return !resultTy; } - auto *boolDecl = Context.getBoolDecl(); + auto *boolDecl = dc->getASTContext().getBoolDecl(); if (!boolDecl) return true; - auto resultTy = typeCheckExpression( + auto resultTy = TypeChecker::typeCheckExpression( expr, dc, TypeLoc::withoutLoc(boolDecl->getDeclaredType()), CTP_Condition); return !resultTy; @@ -3006,6 +3046,7 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, Diag<> diagnosticForAlwaysTrue) { + auto &Context = dc->getASTContext(); bool hadError = false; bool hadAnyFalsable = false; for (auto &elt : cond) { @@ -3020,6 +3061,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, hadAnyFalsable = true; continue; } + assert(elt.getKind() != StmtConditionElement::CK_Boolean); // This is cleanup goop run on the various paths where type checking of the // pattern binding fails. @@ -3031,15 +3073,15 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, elt.getPattern()->forEachVariable([&](VarDecl *var) { // Don't change the type of a variable that we've been able to // compute a type for. - if (var->hasType() && !var->getType()->hasError()) + if (var->hasInterfaceType() && !var->getType()->hasError()) return; - var->markInvalid(); + var->setInvalid(); }); }; // Resolve the pattern. - auto *pattern = resolvePattern(elt.getPattern(), dc, - /*isStmtCondition*/true); + auto *pattern = TypeChecker::resolvePattern(elt.getPattern(), dc, + /*isStmtCondition*/true); if (!pattern) { typeCheckPatternFailed(); continue; @@ -3048,10 +3090,9 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // Check the pattern, it allows unspecified types because the pattern can // provide type information. - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - if (typeCheckPattern(pattern, dc, options)) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { typeCheckPatternFailed(); continue; } @@ -3059,7 +3100,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // If the pattern didn't get a type, it's because we ran into some // unknown types along the way. We'll need to check the initializer. auto init = elt.getInitializer(); - hadError |= typeCheckBinding(pattern, init, dc); + hadError |= TypeChecker::typeCheckBinding(pattern, init, dc, patternType); elt.setPattern(pattern); elt.setInitializer(init); hadAnyFalsable |= pattern->isRefutablePattern(); @@ -3068,9 +3109,10 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, // If the binding is not refutable, and there *is* an else, reject it as // unreachable. - if (!hadAnyFalsable && !hadError) - diagnose(cond[0].getStartLoc(), diagnosticForAlwaysTrue); - + if (!hadAnyFalsable && !hadError) { + auto &diags = dc->getASTContext().Diags; + diags.diagnose(cond[0].getStartLoc(), diagnosticForAlwaysTrue); + } return false; } @@ -3078,6 +3120,7 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, /// value of a given type. bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type rhsType) { + auto &Context = DC->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr-pattern", EP); PrettyStackTracePattern stackTrace(Context, "type-checking", EP); @@ -3088,7 +3131,6 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, EP->getLoc(), Context.getIdentifier("$match"), DC); - matchVar->setType(rhsType); matchVar->setInterfaceType(rhsType->mapTypeOutOfContext()); matchVar->setImplicit(); @@ -3098,10 +3140,12 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, // Find '~=' operators for the match. auto lookupOptions = defaultUnqualifiedLookupOptions; lookupOptions |= NameLookupFlags::KnownPrivate; - auto matchLookup = lookupUnqualified(DC, Context.Id_MatchOperator, - SourceLoc(), lookupOptions); + auto matchLookup = + lookupUnqualified(DC, DeclNameRef(Context.Id_MatchOperator), SourceLoc(), + lookupOptions); + auto &diags = DC->getASTContext().Diags; if (!matchLookup) { - diagnose(EP->getLoc(), diag::no_match_operator); + diags.diagnose(EP->getLoc(), diag::no_match_operator); return true; } @@ -3111,14 +3155,15 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, } if (choices.empty()) { - diagnose(EP->getLoc(), diag::no_match_operator); + diags.diagnose(EP->getLoc(), diag::no_match_operator); return true; } // Build the 'expr ~= var' expression. // FIXME: Compound name locations. - auto *matchOp = buildRefExpr(choices, DC, DeclNameLoc(EP->getLoc()), - /*Implicit=*/true, FunctionRefKind::Compound); + auto *matchOp = + TypeChecker::buildRefExpr(choices, DC, DeclNameLoc(EP->getLoc()), + /*Implicit=*/true, FunctionRefKind::Compound); auto *matchVarRef = new (Context) DeclRefExpr(matchVar, DeclNameLoc(EP->getLoc()), /*Implicit=*/true); @@ -3134,30 +3179,7 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, /*Implicit=*/true); // Check the expression as a condition. - // - // TODO: Type-check of `~=` operator can't (yet) use `typeCheckCondition` - // because that utilizes contextual type which interferes with diagnostics. - // We don't yet have a full access to pattern-matching context in - // constraint system, which is required to enable these situations - // to be properly diagnosed. - struct ConditionListener : public ExprTypeCheckListener { - // Add the appropriate Boolean constraint. - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // Otherwise, the result must be convertible to Bool. - auto boolDecl = cs.getASTContext().getBoolDecl(); - if (!boolDecl) - return true; - - // Condition must convert to Bool. - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - boolDecl->getDeclaredType(), - cs.getConstraintLocator(expr)); - return false; - } - }; - - ConditionListener listener; - bool hadError = !typeCheckExpression(matchCall, DC, &listener); + bool hadError = typeCheckCondition(matchCall, DC); // Save the type-checked expression in the pattern. EP->setMatchExpr(matchCall); // Set the type on the pattern. @@ -3220,7 +3242,7 @@ bool TypeChecker::typesSatisfyConstraint(Type type1, Type type2, assert(!type1->hasTypeVariable() && !type2->hasTypeVariable() && "Unexpected type variable in constraint satisfaction testing"); - ConstraintSystem cs(*this, dc, ConstraintSystemOptions()); + ConstraintSystem cs(dc, ConstraintSystemOptions()); if (openArchetypes) { type1 = replaceArchetypesWithTypeVariables(cs, type1); type2 = replaceArchetypesWithTypeVariables(cs, type2); @@ -3231,7 +3253,7 @@ bool TypeChecker::typesSatisfyConstraint(Type type1, Type type2, if (openArchetypes) { assert(!unwrappedIUO && "FIXME"); SmallVector solutions; - return !cs.solve(nullptr, solutions, FreeTypeVariableBinding::Allow); + return !cs.solve(solutions, FreeTypeVariableBinding::Allow); } if (auto solution = cs.solveSingle()) { @@ -3275,27 +3297,28 @@ bool TypeChecker::isObjCBridgedTo(Type type1, Type type2, DeclContext *dc, } bool TypeChecker::checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc) { - auto kind = typeCheckCheckedCast(t1, t2, CheckedCastContextKind::None, dc, + auto kind = TypeChecker::typeCheckCheckedCast(t1, t2, + CheckedCastContextKind::None, dc, SourceLoc(), nullptr, SourceRange()); return (kind != CheckedCastKind::Unresolved); } -Expr *TypeChecker::addImplicitLoadExpr( - Expr *expr, - std::function getType, - std::function setType) { +Expr * +TypeChecker::addImplicitLoadExpr(ASTContext &Context, Expr *expr, + std::function getType, + std::function setType) { class LoadAdder : public ASTWalker { private: using GetTypeFn = std::function; using SetTypeFn = std::function; - TypeChecker &TC; + ASTContext &Ctx; GetTypeFn getType; SetTypeFn setType; public: - LoadAdder(TypeChecker &TC, GetTypeFn getType, SetTypeFn setType) - : TC(TC), getType(getType), setType(setType) {} + LoadAdder(ASTContext &ctx, GetTypeFn getType, SetTypeFn setType) + : Ctx(ctx), getType(getType), setType(setType) {} std::pair walkToExprPre(Expr *E) override { if (isa(E) || isa(E)) @@ -3315,7 +3338,7 @@ Expr *TypeChecker::addImplicitLoadExpr( setType(E, getType(FVE->getSubExpr())->getOptionalObjectType()); if (auto *PE = dyn_cast(E)) - setType(E, ParenType::get(TC.Context, getType(PE->getSubExpr()))); + setType(E, ParenType::get(Ctx, getType(PE->getSubExpr()))); return E; } @@ -3323,18 +3346,19 @@ Expr *TypeChecker::addImplicitLoadExpr( private: LoadExpr *createLoadExpr(Expr *E) { auto objectType = getType(E)->getRValueType(); - auto *LE = new (TC.Context) LoadExpr(E, objectType); + auto *LE = new (Ctx) LoadExpr(E, objectType); setType(LE, objectType); return LE; } }; - return expr->walk(LoadAdder(*this, getType, setType)); + return expr->walk(LoadAdder(Context, getType, setType)); } -Expr *TypeChecker::coerceToRValue(Expr *expr, - llvm::function_ref getType, - llvm::function_ref setType) { +Expr * +TypeChecker::coerceToRValue(ASTContext &Context, Expr *expr, + llvm::function_ref getType, + llvm::function_ref setType) { Type exprTy = getType(expr); // If expr has no type, just assume it's the right expr. @@ -3347,7 +3371,7 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, // Walk into force optionals and coerce the source. if (auto *FVE = dyn_cast(expr)) { - auto sub = coerceToRValue(FVE->getSubExpr(), getType, setType); + auto sub = coerceToRValue(Context, FVE->getSubExpr(), getType, setType); FVE->setSubExpr(sub); setType(FVE, getType(sub)->getOptionalObjectType()); return FVE; @@ -3355,7 +3379,7 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, // Walk into parenthesized expressions to update the subexpression. if (auto paren = dyn_cast(expr)) { - auto sub = coerceToRValue(paren->getSubExpr(), getType, setType); + auto sub = coerceToRValue(Context, paren->getSubExpr(), getType, setType); paren->setSubExpr(sub); setType(paren, ParenType::get(Context, getType(sub))); return paren; @@ -3363,7 +3387,7 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, // Walk into 'try' and 'try!' expressions to update the subexpression. if (auto tryExpr = dyn_cast(expr)) { - auto sub = coerceToRValue(tryExpr->getSubExpr(), getType, setType); + auto sub = coerceToRValue(Context, tryExpr->getSubExpr(), getType, setType); tryExpr->setSubExpr(sub); if (isa(tryExpr) && !getType(sub)->hasError()) setType(tryExpr, OptionalType::get(getType(sub))); @@ -3378,7 +3402,7 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, for (auto &elt : tuple->getElements()) { // Materialize the element. auto oldType = getType(elt); - elt = coerceToRValue(elt, getType, setType); + elt = coerceToRValue(Context, elt, getType, setType); // If the type changed at all, make a note of it. if (getType(elt).getPointer() != oldType.getPointer()) { @@ -3403,73 +3427,12 @@ Expr *TypeChecker::coerceToRValue(Expr *expr, // Load lvalues. if (exprTy->is()) - return addImplicitLoadExpr(expr, getType, setType); + return addImplicitLoadExpr(Context, expr, getType, setType); // Nothing to do. return expr; } -bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern) { - // TODO: need to add kind arg? - // Construct a constraint system from this expression. - ConstraintSystem cs(*this, dc, ConstraintSystemFlags::AllowFixes); - - // Cache the expression type on the system to ensure it is available - // on diagnostics if the convertion fails. - cs.cacheExprTypes(expr); - - // If there is a type that we're expected to convert to, add the conversion - // constraint. - cs.addConstraint(ConstraintKind::Conversion, expr->getType(), type, - cs.getConstraintLocator(expr)); - - if (getLangOpts().DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Initial constraints for the given expression---\n"; - expr->dump(log); - log << "\n"; - cs.print(log); - } - - // Attempt to solve the constraint system. - SmallVector viable; - if ((cs.solve(expr, viable) || viable.size() != 1) && - cs.salvage(viable, expr)) { - return true; - } - - auto &solution = viable[0]; - if (getLangOpts().DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Solution---\n"; - solution.dump(log); - } - - cs.cacheExprTypes(expr); - - // Perform the conversion. - Expr *result = solution.coerceToType(expr, type, - cs.getConstraintLocator(expr), - /*ignoreTopLevelInjection*/false, - typeFromPattern); - if (!result) { - return true; - } - - cs.setExprTypes(expr); - - if (getLangOpts().DebugConstraintSolver) { - auto &log = Context.TypeCheckerDebug->getStream(); - log << "---Type-checked expression---\n"; - result->dump(log); - log << "\n"; - } - - expr = result; - return false; -} - //===----------------------------------------------------------------------===// // Debugging //===----------------------------------------------------------------------===// @@ -3480,24 +3443,23 @@ void Solution::dump() const { } void Solution::dump(raw_ostream &out) const { - ASTContext &ctx = getConstraintSystem().getASTContext(); - llvm::SaveAndRestore debugSolver(ctx.LangOpts.DebugConstraintSolver, - true); + PrintOptions PO; + PO.PrintTypesForDebugging = true; - SourceManager *sm = &ctx.SourceMgr; + SourceManager *sm = &getConstraintSystem().getASTContext().SourceMgr; out << "Fixed score: " << FixedScore << "\n"; out << "Type variables:\n"; for (auto binding : typeBindings) { - auto &typeVar = binding.first->getImpl(); + auto &typeVar = binding.first; out.indent(2); - typeVar.print(out); + Type(typeVar).print(out, PO); out << " as "; - binding.second.print(out); - if (auto *locator = typeVar.getLocator()) { + binding.second.print(out, PO); + if (auto *locator = typeVar->getImpl().getLocator()) { out << " @ "; - locator->dump(&ctx.SourceMgr, out); + locator->dump(sm, out); } out << "\n"; } @@ -3519,30 +3481,30 @@ void Solution::dump(raw_ostream &out) const { choice.getDecl()->dumpRef(out); out << " as "; if (choice.getBaseType()) - out << choice.getBaseType()->getString() << "."; + out << choice.getBaseType()->getString(PO) << "."; out << choice.getDecl()->getBaseName() << ": " - << ovl.second.openedType->getString() << "\n"; + << ovl.second.openedType->getString(PO) << "\n"; break; case OverloadChoiceKind::BaseType: - out << "base type " << choice.getBaseType()->getString() << "\n"; + out << "base type " << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::KeyPathApplication: out << "key path application root " - << choice.getBaseType()->getString() << "\n"; + << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: out << "dynamic member lookup root " - << choice.getBaseType()->getString() + << choice.getBaseType()->getString(PO) << " name='" << choice.getName() << "'\n"; break; case OverloadChoiceKind::TupleIndex: - out << "tuple " << choice.getBaseType()->getString() << " index " + out << "tuple " << choice.getBaseType()->getString(PO) << " index " << choice.getTupleIndex() << "\n"; break; } @@ -3572,9 +3534,9 @@ void Solution::dump(raw_ostream &out) const { out << " opens "; interleave(opened.second.begin(), opened.second.end(), [&](OpenedType opened) { - opened.first->print(out); + Type(opened.first).print(out, PO); out << " -> "; - opened.second->print(out); + Type(opened.second).print(out, PO); }, [&]() { out << ", "; @@ -3588,7 +3550,7 @@ void Solution::dump(raw_ostream &out) const { for (const auto &openedExistential : OpenedExistentialTypes) { out.indent(2); openedExistential.first->dump(sm, out); - out << " opens to " << openedExistential.second->getString(); + out << " opens to " << openedExistential.second->getString(PO); out << "\n"; } } @@ -3612,15 +3574,15 @@ void Solution::dump(raw_ostream &out) const { } } -void ConstraintSystem::dump() { +void ConstraintSystem::dump() const { print(llvm::errs()); } -void ConstraintSystem::dump(Expr *E) { +void ConstraintSystem::dump(Expr *E) const { print(llvm::errs(), E); } -void ConstraintSystem::print(raw_ostream &out, Expr *E) { +void ConstraintSystem::print(raw_ostream &out, Expr *E) const { auto getTypeOfExpr = [&](const Expr *E) -> Type { if (hasType(E)) return getType(E); @@ -3641,16 +3603,16 @@ void ConstraintSystem::print(raw_ostream &out, Expr *E) { E->dump(out, getTypeOfExpr, getTypeOfTypeLoc, getTypeOfKeyPathComponent); } -void ConstraintSystem::print(raw_ostream &out) { +void ConstraintSystem::print(raw_ostream &out) const { // Print all type variables as $T0 instead of _ here. - llvm::SaveAndRestore X(getASTContext().LangOpts.DebugConstraintSolver, - true); + PrintOptions PO; + PO.PrintTypesForDebugging = true; out << "Score: " << CurrentScore << "\n"; - if (contextualType.getType()) { - out << "Contextual Type: " << contextualType.getType(); - if (TypeRepr *TR = contextualType.getTypeRepr()) { + for (const auto &contextualType : contextualTypes) { + out << "Contextual Type: " << contextualType.second.getType().getString(PO); + if (TypeRepr *TR = contextualType.second.typeLoc.getTypeRepr()) { out << " at "; TR->getSourceRange().print(out, getASTContext().SourceMgr, /*text*/false); } @@ -3660,7 +3622,7 @@ void ConstraintSystem::print(raw_ostream &out) { out << "Type Variables:\n"; for (auto tv : getTypeVariables()) { out.indent(2); - tv->getImpl().print(out); + Type(tv).print(out, PO); if (tv->getImpl().canBindToLValue()) out << " [lvalue allowed]"; if (tv->getImpl().canBindToInOut()) @@ -3671,18 +3633,18 @@ void ConstraintSystem::print(raw_ostream &out) { if (rep == tv) { if (auto fixed = getFixedType(tv)) { out << " as "; - fixed->print(out); + Type(fixed).print(out, PO); } else { getPotentialBindings(tv).dump(out, 1); } } else { out << " equivalent to "; - rep->print(out); + Type(rep).print(out, PO); } if (auto *locator = tv->getImpl().getLocator()) { out << " @ "; - locator->dump(&TC.Context.SourceMgr, out); + locator->dump(&getASTContext().SourceMgr, out); } out << "\n"; @@ -3691,14 +3653,14 @@ void ConstraintSystem::print(raw_ostream &out) { out << "\nActive Constraints:\n"; for (auto &constraint : ActiveConstraints) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; } out << "\nInactive Constraints:\n"; for (auto &constraint : InactiveConstraints) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; } @@ -3706,18 +3668,18 @@ void ConstraintSystem::print(raw_ostream &out) { out << "\nRetired Constraints:\n"; solverState->forEachRetired([&](Constraint &constraint) { out.indent(2); - constraint.print(out, &getTypeChecker().Context.SourceMgr); + constraint.print(out, &getASTContext().SourceMgr); out << "\n"; }); } - if (resolvedOverloadSets) { + if (!ResolvedOverloads.empty()) { out << "Resolved overloads:\n"; // Otherwise, report the resolved overloads. - for (auto resolved = resolvedOverloadSets; - resolved; resolved = resolved->Previous) { - auto &choice = resolved->Choice; + for (auto elt : ResolvedOverloads) { + auto resolved = elt.second; + auto &choice = resolved.choice; out << " selected overload set choice "; switch (choice.getKind()) { case OverloadChoiceKind::Decl: @@ -3725,30 +3687,30 @@ void ConstraintSystem::print(raw_ostream &out) { case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaUnwrappedOptional: if (choice.getBaseType()) - out << choice.getBaseType()->getString() << "."; + out << choice.getBaseType()->getString(PO) << "."; out << choice.getDecl()->getBaseName() << ": " - << resolved->BoundType->getString() << " == " - << resolved->ImpliedType->getString() << "\n"; + << resolved.boundType->getString(PO) << " == " + << resolved.openedType->getString(PO) << "\n"; break; case OverloadChoiceKind::BaseType: - out << "base type " << choice.getBaseType()->getString() << "\n"; + out << "base type " << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::KeyPathApplication: out << "key path application root " - << choice.getBaseType()->getString() << "\n"; + << choice.getBaseType()->getString(PO) << "\n"; break; case OverloadChoiceKind::DynamicMemberLookup: case OverloadChoiceKind::KeyPathDynamicMemberLookup: out << "dynamic member lookup:" - << choice.getBaseType()->getString() << " name=" + << choice.getBaseType()->getString(PO) << " name=" << choice.getName() << "\n"; break; case OverloadChoiceKind::TupleIndex: - out << "tuple " << choice.getBaseType()->getString() << " index " + out << "tuple " << choice.getBaseType()->getString(PO) << " index " << choice.getTupleIndex() << "\n"; break; } @@ -3760,7 +3722,7 @@ void ConstraintSystem::print(raw_ostream &out) { out << "\nDisjunction choices:\n"; for (auto &choice : DisjunctionChoices) { out.indent(2); - choice.first->dump(&getTypeChecker().Context.SourceMgr, out); + choice.first->dump(&getASTContext().SourceMgr, out); out << " is #" << choice.second << "\n"; } } @@ -3769,13 +3731,13 @@ void ConstraintSystem::print(raw_ostream &out) { out << "\nOpened types:\n"; for (const auto &opened : OpenedTypes) { out.indent(2); - opened.first->dump(&getTypeChecker().Context.SourceMgr, out); + opened.first->dump(&getASTContext().SourceMgr, out); out << " opens "; interleave(opened.second.begin(), opened.second.end(), [&](OpenedType opened) { - opened.first->print(out); + Type(opened.first).print(out, PO); out << " -> "; - opened.second->print(out); + Type(opened.second).print(out, PO); }, [&]() { out << ", "; @@ -3788,8 +3750,8 @@ void ConstraintSystem::print(raw_ostream &out) { out << "\nOpened existential types:\n"; for (const auto &openedExistential : OpenedExistentialTypes) { out.indent(2); - openedExistential.first->dump(&getTypeChecker().Context.SourceMgr, out); - out << " opens to " << openedExistential.second->getString(); + openedExistential.first->dump(&getASTContext().SourceMgr, out); + out << " opens to " << openedExistential.second->getString(PO); out << "\n"; } } @@ -3797,7 +3759,7 @@ void ConstraintSystem::print(raw_ostream &out) { if (!DefaultedConstraints.empty()) { out << "\nDefaulted constraints: "; interleave(DefaultedConstraints, [&](ConstraintLocator *locator) { - locator->dump(&getTypeChecker().Context.SourceMgr, out); + locator->dump(&getASTContext().SourceMgr, out); }, [&] { out << ", "; }); @@ -3806,7 +3768,7 @@ void ConstraintSystem::print(raw_ostream &out) { if (failedConstraint) { out << "\nFailed constraint:\n"; out.indent(2); - failedConstraint->print(out, &getTypeChecker().Context.SourceMgr); + failedConstraint->print(out, &getASTContext().SourceMgr); out << "\n"; } @@ -3822,12 +3784,12 @@ void ConstraintSystem::print(raw_ostream &out) { /// Determine the semantics of a checked cast operation. CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, - Type toType, - CheckedCastContextKind contextKind, - DeclContext *dc, - SourceLoc diagLoc, - Expr *fromExpr, - SourceRange diagToRange) { + Type toType, + CheckedCastContextKind contextKind, + DeclContext *dc, + SourceLoc diagLoc, + Expr *fromExpr, + SourceRange diagToRange) { SourceRange diagFromRange; if (fromExpr) diagFromRange = fromExpr->getSourceRange(); @@ -3855,6 +3817,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // Determine whether we should suppress diagnostics. bool suppressDiagnostics = (contextKind == CheckedCastContextKind::None); + auto &diags = dc->getASTContext().Diags; bool optionalToOptionalCast = false; // Local function to indicate failure. @@ -3868,7 +3831,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (optionalToOptionalCast) return CheckedCastKind::ValueCast; - diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType) + diags.diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, + origToType) .highlight(diagFromRange) .highlight(diagToRange); @@ -3884,8 +3848,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, auto fromValueType = fromType->getOptionalObjectType(); if (!fromValueType) { if (!suppressDiagnostics) { - diagnose(diagLoc, diag::downcast_to_more_optional, - origFromType, origToType) + diags.diagnose(diagLoc, diag::downcast_to_more_optional, + origFromType, origToType) .highlight(diagFromRange) .highlight(diagToRange); } @@ -3906,6 +3870,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // If the unwrapped from/to types are equivalent or bridged, this isn't a real // downcast. Complain. + auto &Context = dc->getASTContext(); if (extraFromOptionals > 0) { switch (typeCheckCheckedCast(fromType, toType, CheckedCastContextKind::None, dc, @@ -3924,10 +3889,10 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, case CheckedCastContextKind::ForcedCast: { std::string extraFromOptionalsStr(extraFromOptionals, '!'); - auto diag = diagnose(diagLoc, diag::downcast_same_type, - origFromType, origToType, - extraFromOptionalsStr, - isBridged); + auto diag = diags.diagnose(diagLoc, diag::downcast_same_type, + origFromType, origToType, + extraFromOptionalsStr, + isBridged); diag.highlight(diagFromRange); diag.highlight(diagToRange); @@ -3956,11 +3921,12 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (extraFromOptionals == 1) { // A single optional is carried through. It's better to use 'as' to // the appropriate optional type. - auto diag = diagnose(diagLoc, diag::conditional_downcast_same_type, - origFromType, origToType, - fromType->isEqual(toType) ? 0 - : isBridged ? 2 - : 1); + auto diag = diags.diagnose(diagLoc, + diag::conditional_downcast_same_type, + origFromType, origToType, + fromType->isEqual(toType) ? 0 + : isBridged ? 2 + : 1); diag.highlight(diagFromRange); diag.highlight(diagToRange); @@ -3992,8 +3958,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // If we're only unwrapping a single optional, we could have just // checked for 'nil'. if (extraFromOptionals == 1) { - auto diag = diagnose(diagLoc, diag::is_expr_same_type, - origFromType, origToType); + auto diag = diags.diagnose(diagLoc, diag::is_expr_same_type, + origFromType, origToType); diag.highlight(diagFromRange); diag.highlight(diagToRange); @@ -4033,27 +3999,32 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, } } - // Check for casts between specific concrete types that cannot succeed. - if (auto toElementType = ConstraintSystem::isArrayType(toType)) { - if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; + auto checkElementCast = [&](Type fromElt, Type toElt, + CheckedCastKind castKind) -> CheckedCastKind { + switch (typeCheckCheckedCast(fromElt, toElt, CheckedCastContextKind::None, + dc, SourceLoc(), nullptr, SourceRange())) { + case CheckedCastKind::Coercion: + return CheckedCastKind::Coercion; - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; + case CheckedCastKind::BridgingCoercion: + return CheckedCastKind::BridgingCoercion; - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::ArrayDowncast; + case CheckedCastKind::ArrayDowncast: + case CheckedCastKind::DictionaryDowncast: + case CheckedCastKind::SetDowncast: + case CheckedCastKind::ValueCast: + return castKind; - case CheckedCastKind::Unresolved: - return failed(); - } + case CheckedCastKind::Unresolved: + return failed(); + } + }; + + // Check for casts between specific concrete types that cannot succeed. + if (auto toElementType = ConstraintSystem::isArrayType(toType)) { + if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::ArrayDowncast); } } @@ -4123,24 +4094,31 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (auto toElementType = ConstraintSystem::isSetType(toType)) { if (auto fromElementType = ConstraintSystem::isSetType(fromType)) { - switch (typeCheckCheckedCast(*fromElementType, *toElementType, - CheckedCastContextKind::None, dc, - SourceLoc(), nullptr, SourceRange())) { - case CheckedCastKind::Coercion: - return CheckedCastKind::Coercion; - - case CheckedCastKind::BridgingCoercion: - return CheckedCastKind::BridgingCoercion; - - case CheckedCastKind::ArrayDowncast: - case CheckedCastKind::DictionaryDowncast: - case CheckedCastKind::SetDowncast: - case CheckedCastKind::ValueCast: - return CheckedCastKind::SetDowncast; + return checkElementCast(*fromElementType, *toElementType, + CheckedCastKind::SetDowncast); + } + } - case CheckedCastKind::Unresolved: + if (auto toTuple = toType->getAs()) { + if (auto fromTuple = fromType->getAs()) { + if (fromTuple->getNumElements() != toTuple->getNumElements()) return failed(); + + for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) { + const auto &fromElt = fromTuple->getElement(i); + const auto &toElt = toTuple->getElement(i); + + if (fromElt.getName() != toElt.getName()) + return failed(); + + auto result = checkElementCast(fromElt.getType(), toElt.getType(), + CheckedCastKind::ValueCast); + + if (result == CheckedCastKind::Unresolved) + return result; } + + return CheckedCastKind::ValueCast; } } @@ -4165,7 +4143,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, switch (contextKind) { case CheckedCastContextKind::ConditionalCast: case CheckedCastContextKind::ForcedCast: - diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType) + diags.diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, + origToType) .highlight(diagFromRange) .highlight(diagToRange); @@ -4174,7 +4153,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, if (auto DRE = dyn_cast(fromExpr)) { if (auto FD = dyn_cast(DRE->getDecl())) { if (!FD->getResultInterfaceType()->isVoid()) { - diagnose(diagLoc, diag::downcast_to_unrelated_fixit, FD->getName()) + diags.diagnose(diagLoc, diag::downcast_to_unrelated_fixit, + FD->getName()) .fixItInsertAfter(fromExpr->getEndLoc(), "()"); } } @@ -4394,7 +4374,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // Objective-C metaclasses are subclasses of NSObject in the ObjC runtime, // so casts from NSObject to potentially-class metatypes may succeed. - if (auto nsObject = getNSObjectType(dc)) { + if (auto nsObject = Context.getNSObjectType()) { if (fromType->isEqual(nsObject)) { if (auto toMeta = toType->getAs()) { if (toMeta->getInstanceType()->mayHaveSuperclass() @@ -4410,19 +4390,38 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // type. This is handled in the runtime, so it doesn't need a special cast // kind. if (Context.LangOpts.EnableObjCInterop) { + auto nsObject = Context.getNSObjectType(); + auto nsErrorTy = Context.getNSErrorType(); + if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) { - if (conformsToProtocol(toType, errorTypeProto, dc, - ConformanceCheckFlags::InExpression)) { - auto nsError = Context.getNSErrorDecl(); - if (nsError) { - Type NSErrorTy = nsError->getDeclaredInterfaceType(); - if (isSubtypeOf(fromType, NSErrorTy, dc) + if (!conformsToProtocol(toType, errorTypeProto, dc, + ConformanceCheckFlags::InExpression) + .isInvalid()) { + if (nsErrorTy) { + if (isSubtypeOf(fromType, nsErrorTy, dc) // Don't mask "always true" warnings if NSError is cast to // Error itself. && !isSubtypeOf(fromType, toType, dc)) return CheckedCastKind::ValueCast; } } + + if (!conformsToProtocol(fromType, errorTypeProto, dc, + ConformanceCheckFlags::InExpression) + .isInvalid()) { + // Cast of an error-conforming type to NSError or NSObject. + if ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy))) + return CheckedCastKind::BridgingCoercion; + } + } + + // Any class-like type could be dynamically cast to NSObject or NSError + // via an Error conformance. + if (fromType->mayHaveSuperclass() && + ((nsObject && toType->isEqual(nsObject)) || + (nsErrorTy && toType->isEqual(nsErrorTy)))) { + return CheckedCastKind::ValueCast; } } @@ -4517,3 +4516,74 @@ ForcedCheckedCastExpr *swift::findForcedDowncast(ASTContext &ctx, Expr *expr) { return nullptr; } + +llvm::Expected +IsCallableNominalTypeRequest::evaluate(Evaluator &evaluator, CanType ty, + DeclContext *dc) const { + auto options = defaultMemberLookupOptions; + options |= NameLookupFlags::IgnoreAccessControl; + if (isa(dc)) + options |= NameLookupFlags::KnownPrivate; + + // Look for a callAsFunction method. + auto &ctx = ty->getASTContext(); + auto results = + TypeChecker::lookupMember(dc, ty, DeclNameRef(ctx.Id_callAsFunction), + options); + return llvm::any_of(results, [](LookupResultEntry entry) -> bool { + if (auto *fd = dyn_cast(entry.getValueDecl())) + return fd->isCallAsFunctionMethod(); + return false; + }); +} + +llvm::Expected +HasDynamicMemberLookupAttributeRequest::evaluate(Evaluator &evaluator, + CanType ty) const { + // If this is an archetype type, check if any types it conforms to + // (superclass or protocols) have the attribute. + if (auto archetype = dyn_cast(ty)) { + for (auto proto : archetype->getConformsTo()) { + if (proto->getDeclaredType()->hasDynamicMemberLookupAttribute()) + return true; + } + if (auto superclass = archetype->getSuperclass()) { + if (superclass->hasDynamicMemberLookupAttribute()) + return true; + } + } + + // If this is a protocol composition, check if any of its members have the + // attribute. + if (auto protocolComp = dyn_cast(ty)) { + for (auto member : protocolComp->getMembers()) { + if (member->hasDynamicMemberLookupAttribute()) + return true; + } + } + + // Otherwise, this must be a nominal type. + // Dynamic member lookup doesn't work for tuples, etc. + auto nominal = ty->getAnyNominal(); + if (!nominal) + return false; + + // If this type has the attribute on it, then yes! + if (nominal->getAttrs().hasAttribute()) + return true; + + // Check the protocols the type conforms to. + for (auto proto : nominal->getAllProtocols()) { + if (proto->getDeclaredType()->hasDynamicMemberLookupAttribute()) + return true; + } + + // Check the superclass if present. + if (auto classDecl = dyn_cast(nominal)) { + if (auto superclass = classDecl->getSuperclass()) { + if (superclass->hasDynamicMemberLookupAttribute()) + return true; + } + } + return false; +} diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 31dc773b7482b..a394d8671136a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -75,7 +75,7 @@ namespace { /// Float and integer literals are additionally keyed by numeric equivalence. struct RawValueKey { enum class Kind : uint8_t { - String, Float, Int, Tombstone, Empty + String, Float, Int, Bool, Tombstone, Empty } kind; struct IntValueTy { @@ -101,6 +101,7 @@ struct RawValueKey { StringRef stringValue; IntValueTy intValue; FloatValueTy floatValue; + bool boolValue; }; explicit RawValueKey(LiteralExpr *expr) { @@ -136,6 +137,12 @@ struct RawValueKey { kind = Kind::String; stringValue = cast(expr)->getValue(); return; + + case ExprKind::BooleanLiteral: + kind = Kind::Bool; + boolValue = cast(expr)->getValue(); + return; + default: llvm_unreachable("not a valid literal expr for raw value"); } @@ -183,6 +190,8 @@ class DenseMapInfo { DenseMapInfo::getHashValue(k.intValue.v1); case RawValueKey::Kind::String: return DenseMapInfo::getHashValue(k.stringValue); + case RawValueKey::Kind::Bool: + return DenseMapInfo::getHashValue(k.boolValue); case RawValueKey::Kind::Empty: case RawValueKey::Kind::Tombstone: return 0; @@ -204,6 +213,8 @@ class DenseMapInfo { a.intValue.v1 == b.intValue.v1; case RawValueKey::Kind::String: return a.stringValue.equals(b.stringValue); + case RawValueKey::Kind::Bool: + return a.boolValue == b.boolValue; case RawValueKey::Kind::Empty: case RawValueKey::Kind::Tombstone: return true; @@ -215,683 +226,75 @@ class DenseMapInfo { } // namespace llvm -/// Check the inheritance clause of a type declaration or extension thereof. -/// -/// This routine performs detailed checking of the inheritance clause of the -/// given type or extension. It need only be called within the primary source -/// file. -static void checkInheritanceClause( - llvm::PointerUnion declUnion) { - DeclContext *DC; - MutableArrayRef inheritedClause; - ExtensionDecl *ext = nullptr; - TypeDecl *typeDecl = nullptr; - Decl *decl; - if ((ext = declUnion.dyn_cast())) { - decl = ext; - DC = ext; - - inheritedClause = ext->getInherited(); - - // Protocol extensions cannot have inheritance clauses. - if (auto proto = ext->getExtendedProtocolDecl()) { - if (!inheritedClause.empty()) { - ext->diagnose(diag::extension_protocol_inheritance, - proto->getName()) - .highlight(SourceRange(inheritedClause.front().getSourceRange().Start, - inheritedClause.back().getSourceRange().End)); - return; - } - } - } else { - typeDecl = declUnion.get(); - decl = typeDecl; - if (auto nominal = dyn_cast(typeDecl)) { - DC = nominal; - } else { - DC = typeDecl->getDeclContext(); - } - - inheritedClause = typeDecl->getInherited(); - } - - // Can this declaration's inheritance clause contain a class or - // subclass existential? - bool canHaveSuperclass = (isa(decl) || - (isa(decl) && - !cast(decl)->isObjC())); - - ASTContext &ctx = decl->getASTContext(); - auto &diags = ctx.Diags; - - // Retrieve the location of the start of the inheritance clause. - auto getStartLocOfInheritanceClause = [&] { - if (ext) - return ext->getSourceRange().End; - - return typeDecl->getNameLoc(); - }; - - // Compute the source range to be used when removing something from an - // inheritance clause. - auto getRemovalRange = [&](unsigned i) { - // If there is just one entry, remove the entire inheritance clause. - if (inheritedClause.size() == 1) { - SourceLoc start = getStartLocOfInheritanceClause(); - SourceLoc end = inheritedClause[i].getSourceRange().End; - return SourceRange(Lexer::getLocForEndOfToken(ctx.SourceMgr, start), - Lexer::getLocForEndOfToken(ctx.SourceMgr, end)); - } - - // If we're at the first entry, remove from the start of this entry to the - // start of the next entry. - if (i == 0) { - return SourceRange(inheritedClause[i].getSourceRange().Start, - inheritedClause[i+1].getSourceRange().Start); - } - - // Otherwise, remove from the end of the previous entry to the end of this - // entry. - SourceLoc afterPriorLoc = - Lexer::getLocForEndOfToken(ctx.SourceMgr, - inheritedClause[i-1].getSourceRange().End); - - SourceLoc afterMyEndLoc = - Lexer::getLocForEndOfToken(ctx.SourceMgr, - inheritedClause[i].getSourceRange().End); - - return SourceRange(afterPriorLoc, afterMyEndLoc); - }; - - // Check all of the types listed in the inheritance clause. - Type superclassTy; - SourceRange superclassRange; - Optional> inheritedAnyObject; - for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) { - auto &inherited = inheritedClause[i]; - - // Validate the type. - InheritedTypeRequest request{declUnion, i, TypeResolutionStage::Interface}; - Type inheritedTy = evaluateOrDefault(ctx.evaluator, request, Type()); - - // If we couldn't resolve an the inherited type, or it contains an error, - // ignore it. - if (!inheritedTy || inheritedTy->hasError()) - continue; - - // For generic parameters and associated types, the GSB checks constraints; - // however, we still want to fire off the requests to produce diagnostics - // in some circular validation cases. - if (isa(decl)) - continue; - - // Check whether we inherited from 'AnyObject' twice. - // Other redundant-inheritance scenarios are checked below, the - // GenericSignatureBuilder (for protocol inheritance) or the - // ConformanceLookupTable (for protocol conformance). - if (inheritedTy->isAnyObject()) { - if (inheritedAnyObject) { - // If the first occurrence was written as 'class', downgrade the error - // to a warning in such case for backward compatibility with - // Swift <= 4. - auto knownIndex = inheritedAnyObject->first; - auto knownRange = inheritedAnyObject->second; - SourceRange removeRange = getRemovalRange(knownIndex); - if (!ctx.LangOpts.isSwiftVersionAtLeast(5) && - (isa(decl) || isa(decl)) && - Lexer::getTokenAtLocation(ctx.SourceMgr, knownRange.Start) - .is(tok::kw_class)) { - SourceLoc classLoc = knownRange.Start; - - diags.diagnose(classLoc, diag::duplicate_anyobject_class_inheritance) - .fixItRemoveChars(removeRange.Start, removeRange.End); - } else { - diags.diagnose(inherited.getSourceRange().Start, - diag::duplicate_inheritance, inheritedTy) - .fixItRemoveChars(removeRange.Start, removeRange.End); - } - continue; - } - - // Note that we saw inheritance from 'AnyObject'. - inheritedAnyObject = { i, inherited.getSourceRange() }; - } - - if (inheritedTy->isExistentialType()) { - auto layout = inheritedTy->getExistentialLayout(); - - // Subclass existentials are not allowed except on classes and - // non-@objc protocols. - if (layout.explicitSuperclass && - !canHaveSuperclass) { - decl->diagnose(diag::inheritance_from_protocol_with_superclass, - inheritedTy); - continue; - } - - // AnyObject is not allowed except on protocols. - if (layout.hasExplicitAnyObject && - !isa(decl)) { - decl->diagnose(canHaveSuperclass - ? diag::inheritance_from_non_protocol_or_class - : diag::inheritance_from_non_protocol, - inheritedTy); - continue; - } - - // If the existential did not have a class constraint, we're done. - if (!layout.explicitSuperclass) - continue; - - // Classes and protocols can inherit from subclass existentials. - // For classes, we check for a duplicate superclass below. - // For protocols, the GSB emits its own warning instead. - if (isa(decl)) - continue; - - assert(isa(decl)); - assert(canHaveSuperclass); - inheritedTy = layout.explicitSuperclass; - } - - // If this is an enum inheritance clause, check for a raw type. - if (isa(decl)) { - // Check if we already had a raw type. - if (superclassTy) { - if (superclassTy->isEqual(inheritedTy)) { - auto removeRange = getRemovalRange(i); - diags.diagnose(inherited.getSourceRange().Start, - diag::duplicate_inheritance, inheritedTy) - .fixItRemoveChars(removeRange.Start, removeRange.End); - } else { - diags.diagnose(inherited.getSourceRange().Start, - diag::multiple_enum_raw_types, superclassTy, - inheritedTy) - .highlight(superclassRange); - } - continue; - } - - // If this is not the first entry in the inheritance clause, complain. - if (i > 0) { - auto removeRange = getRemovalRange(i); - - diags.diagnose(inherited.getSourceRange().Start, - diag::raw_type_not_first, inheritedTy) - .fixItRemoveChars(removeRange.Start, removeRange.End) - .fixItInsert(inheritedClause[0].getSourceRange().Start, - inheritedTy.getString() + ", "); - - // Fall through to record the raw type. - } - - // Record the raw type. - superclassTy = inheritedTy; - superclassRange = inherited.getSourceRange(); - continue; - } - - // If this is a class type, it may be the superclass. We end up here when - // the inherited type is either itself a class, or when it is a subclass - // existential via the existential type path above. - if (inheritedTy->getClassOrBoundGenericClass()) { - // First, check if we already had a superclass. - if (superclassTy) { - // FIXME: Check for shadowed protocol names, i.e., NSObject? - - if (superclassTy->isEqual(inheritedTy)) { - // Duplicate superclass. - auto removeRange = getRemovalRange(i); - diags.diagnose(inherited.getSourceRange().Start, - diag::duplicate_inheritance, inheritedTy) - .fixItRemoveChars(removeRange.Start, removeRange.End); - } else { - // Complain about multiple inheritance. - // Don't emit a Fix-It here. The user has to think harder about this. - diags.diagnose(inherited.getSourceRange().Start, - diag::multiple_inheritance, superclassTy, inheritedTy) - .highlight(superclassRange); - } - continue; - } - - // If this is not the first entry in the inheritance clause, complain. - if (isa(decl) && i > 0) { - auto removeRange = getRemovalRange(i); - diags.diagnose(inherited.getSourceRange().Start, - diag::superclass_not_first, inheritedTy) - .fixItRemoveChars(removeRange.Start, removeRange.End) - .fixItInsert(inheritedClause[0].getSourceRange().Start, - inheritedTy.getString() + ", "); - - // Fall through to record the superclass. - } - - if (canHaveSuperclass) { - // Record the superclass. - superclassTy = inheritedTy; - superclassRange = inherited.getSourceRange(); - continue; - } - } - - // We can't inherit from a non-class, non-protocol type. - decl->diagnose(canHaveSuperclass - ? diag::inheritance_from_non_protocol_or_class - : diag::inheritance_from_non_protocol, - inheritedTy); - // FIXME: Note pointing to the declaration 'inheritedTy' references? - } +static bool canSkipCircularityCheck(NominalTypeDecl *decl) { + // Don't bother checking imported or deserialized decls. + return decl->hasClangNode() || decl->wasDeserialized(); } -/// Check the inheritance clauses generic parameters along with any -/// requirements stored within the generic parameter list. -static void checkGenericParams(GenericParamList *genericParams, - DeclContext *owningDC, TypeChecker &tc) { - if (!genericParams) - return; - - for (auto gp : *genericParams) { - tc.checkDeclAttributes(gp); - checkInheritanceClause(gp); - } - - // Force visitation of each of the requirements here. - WhereClauseOwner(owningDC, genericParams) - .visitRequirements(TypeResolutionStage::Interface, - [](Requirement, RequirementRepr *) { return false; }); -} +llvm::Expected +HasCircularInheritanceRequest::evaluate(Evaluator &evaluator, + ClassDecl *decl) const { + if (canSkipCircularityCheck(decl) || !decl->hasSuperclass()) + return false; -/// Retrieve the set of protocols the given protocol inherits. -static llvm::TinyPtrVector -getInheritedForCycleCheck(TypeChecker &tc, - ProtocolDecl *proto, - ProtocolDecl **scratch) { - TinyPtrVector result; + auto *superclass = decl->getSuperclassDecl(); + auto result = evaluator(HasCircularInheritanceRequest{superclass}); - bool anyObject = false; - for (const auto &found : - getDirectlyInheritedNominalTypeDecls(proto, anyObject)) { - if (auto protoDecl = dyn_cast(found.second)) - result.push_back(protoDecl); + // If we have a cycle, handle it and return true. + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } - return result; } -/// Retrieve the superclass of the given class. -static ArrayRef getInheritedForCycleCheck(TypeChecker &tc, - ClassDecl *classDecl, - ClassDecl **scratch) { - if (classDecl->hasSuperclass()) { - *scratch = classDecl->getSuperclassDecl(); - return *scratch; - } - return { }; -} - -/// Retrieve the raw type of the given enum. -static ArrayRef getInheritedForCycleCheck(TypeChecker &tc, - EnumDecl *enumDecl, - EnumDecl **scratch) { - if (enumDecl->hasRawType()) { - *scratch = enumDecl->getRawType()->getEnumOrBoundGenericEnum(); - return *scratch ? ArrayRef(*scratch) : ArrayRef{}; - } - return { }; -} - -/// Check for circular inheritance. -template -static void checkCircularity(TypeChecker &tc, T *decl, - Diag circularDiag, - DescriptiveDeclKind declKind, - SmallVectorImpl &path) { - switch (decl->getCircularityCheck()) { - case CircularityCheck::Checked: - return; - - case CircularityCheck::Checking: { - // We're already checking this type, which means we have a cycle. - - // The beginning of the path might not be part of the cycle, so find - // where the cycle starts. - assert(!path.empty()); - - auto cycleStart = path.end() - 1; - while (*cycleStart != decl) { - assert(cycleStart != path.begin() && "Missing cycle start?"); - --cycleStart; - } - - // If the path length is 1 the type directly references itself. - if (path.end() - cycleStart == 1) { - tc.diagnose(path.back()->getLoc(), - circularDiag, - path.back()->getName()); - - break; - } - - // Diagnose the cycle. - tc.diagnose(decl->getLoc(), circularDiag, - (*cycleStart)->getName()); - for (auto i = cycleStart + 1, iEnd = path.end(); i != iEnd; ++i) { - tc.diagnose(*i, diag::kind_declname_declared_here, - declKind, (*i)->getName()); - } +llvm::Expected +HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator, + ProtocolDecl *decl) const { + if (canSkipCircularityCheck(decl)) + return false; - break; - } + bool anyObject = false; + auto inherited = getDirectlyInheritedNominalTypeDecls(decl, anyObject); + for (auto &found : inherited) { + auto *protoDecl = dyn_cast(found.Item); + if (!protoDecl) + continue; - case CircularityCheck::Unchecked: { - // Walk to the inherited class or protocols. - path.push_back(decl); - decl->setCircularityCheck(CircularityCheck::Checking); - T *scratch = nullptr; - for (auto inherited : getInheritedForCycleCheck(tc, decl, &scratch)) { - checkCircularity(tc, inherited, circularDiag, declKind, path); + // If we have a cycle, handle it and return true. + auto result = evaluator(HasCircularInheritedProtocolsRequest{protoDecl}); + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } - decl->setCircularityCheck(CircularityCheck::Checked); - path.pop_back(); - break; - } - } -} - -/// Expose TypeChecker's handling of GenericParamList to SIL parsing. -GenericEnvironment * -TypeChecker::handleSILGenericParams(GenericParamList *genericParams, - DeclContext *DC) { - if (genericParams == nullptr) - return nullptr; - - SmallVector nestedList; - for (; genericParams; genericParams = genericParams->getOuterParameters()) { - nestedList.push_back(genericParams); - } - - std::reverse(nestedList.begin(), nestedList.end()); - for (unsigned i = 0, e = nestedList.size(); i < e; ++i) { - auto genericParams = nestedList[i]; - genericParams->setDepth(i); + // If the underlying request handled a cycle and returned true, bail. + if (*result) + return true; } - - auto sig = TypeChecker::checkGenericSignature( - nestedList.back(), DC, - /*parentSig=*/nullptr, - /*allowConcreteGenericParams=*/true); - return (sig ? sig->getGenericEnvironment() : nullptr); + return false; } -/// Check whether \c current is a redeclaration. -static void checkRedeclaration(ASTContext &ctx, ValueDecl *current) { - // If we've already checked this declaration, don't do it again. - if (current->alreadyCheckedRedeclaration()) - return; - - // Make sure we don't do this checking again. - current->setCheckedRedeclaration(true); - - // FIXME: Computes isInvalid() below. - (void) current->getInterfaceType(); - - // Ignore invalid and anonymous declarations. - if (current->isInvalid() || - !current->hasInterfaceType() || - !current->hasName()) - return; - - // If this declaration isn't from a source file, don't check it. - // FIXME: Should restrict this to the source file we care about. - DeclContext *currentDC = current->getDeclContext(); - SourceFile *currentFile = currentDC->getParentSourceFile(); - if (!currentFile || currentDC->isLocalContext()) - return; - - ReferencedNameTracker *tracker = currentFile->getReferencedNameTracker(); - bool isCascading = (current->getFormalAccess() > AccessLevel::FilePrivate); - - // Find other potential definitions. - SmallVector otherDefinitions; - if (currentDC->isTypeContext()) { - // Look within a type context. - if (auto nominal = currentDC->getSelfNominalTypeDecl()) { - auto found = nominal->lookupDirect(current->getBaseName()); - otherDefinitions.append(found.begin(), found.end()); - if (tracker) - tracker->addUsedMember({nominal, current->getBaseName()}, isCascading); - } - } else { - // Look within a module context. - currentFile->getParentModule()->lookupValue(current->getBaseName(), - NLKind::QualifiedLookup, - otherDefinitions); - if (tracker) - tracker->addTopLevelName(current->getBaseName(), isCascading); - } - - // Compare this signature against the signature of other - // declarations with the same name. - OverloadSignature currentSig = current->getOverloadSignature(); - CanType currentSigType = current->getOverloadSignatureType(); - ModuleDecl *currentModule = current->getModuleContext(); - for (auto other : otherDefinitions) { - // Skip invalid declarations and ourselves. - if (current == other || other->isInvalid()) - continue; - - // Skip declarations in other modules. - if (currentModule != other->getModuleContext()) - continue; - - // If both declarations are in the same file, only diagnose the second one. - if (currentFile == other->getDeclContext()->getParentSourceFile()) - if (current->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer( - current->getLoc(), other->getLoc())) - continue; - - // Don't compare methods vs. non-methods (which only happens with - // operators). - if (currentDC->isTypeContext() != other->getDeclContext()->isTypeContext()) - continue; - - // Check whether the overload signatures conflict (ignoring the type for - // now). - auto otherSig = other->getOverloadSignature(); - if (!conflicting(currentSig, otherSig)) - continue; - - // FIXME: Computes isInvalid() below. - (void) other->getInterfaceType(); - - // Skip invalid declarations. - if (other->isInvalid()) - continue; - - // Skip declarations in other files. - // In practice, this means we will warn on a private declaration that - // shadows a non-private one, but only in the file where the shadowing - // happens. We will warn on conflicting non-private declarations in both - // files. - if (!other->isAccessibleFrom(currentDC)) - continue; - - const auto markInvalid = [¤t]() { - current->setInvalid(); - if (auto *varDecl = dyn_cast(current)) - if (varDecl->hasType()) - varDecl->setType(ErrorType::get(varDecl->getType())); - if (current->hasInterfaceType()) - current->setInterfaceType(ErrorType::get(current->getInterfaceType())); - }; - - // Thwart attempts to override the same declaration more than once. - const auto *currentOverride = current->getOverriddenDecl(); - const auto *otherOverride = other->getOverriddenDecl(); - if (currentOverride && currentOverride == otherOverride) { - current->diagnose(diag::multiple_override, current->getFullName()); - other->diagnose(diag::multiple_override_prev, other->getFullName()); - markInvalid(); - break; - } - - // Get the overload signature type. - CanType otherSigType = other->getOverloadSignatureType(); - - bool wouldBeSwift5Redeclaration = false; - auto isRedeclaration = conflicting(ctx, currentSig, currentSigType, - otherSig, otherSigType, - &wouldBeSwift5Redeclaration); - // If there is another conflict, complain. - if (isRedeclaration || wouldBeSwift5Redeclaration) { - // If the two declarations occur in the same source file, make sure - // we get the diagnostic ordering to be sensible. - if (auto otherFile = other->getDeclContext()->getParentSourceFile()) { - if (currentFile == otherFile && - current->getLoc().isValid() && - other->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer(current->getLoc(), - other->getLoc())) { - std::swap(current, other); - } - } - - // If we're currently looking at a .sil and the conflicting declaration - // comes from a .sib, don't error since we won't be considering the sil - // from the .sib. So it's fine for the .sil to shadow it, since that's the - // one we want. - if (currentFile->Kind == SourceFileKind::SIL) { - auto *otherFile = dyn_cast( - other->getDeclContext()->getModuleScopeContext()); - if (otherFile && otherFile->isSIB()) - continue; - } - - // If the conflicting declarations have non-overlapping availability and, - // we allow the redeclaration to proceed if... - // - // - they are initializers with different failability, - bool isAcceptableVersionBasedChange = false; - { - const auto *currentInit = dyn_cast(current); - const auto *otherInit = dyn_cast(other); - if (currentInit && otherInit && - (currentInit->isFailable() != - otherInit->isFailable())) { - isAcceptableVersionBasedChange = true; - } - } - // - one throws and the other does not, - { - const auto *currentAFD = dyn_cast(current); - const auto *otherAFD = dyn_cast(other); - if (currentAFD && otherAFD && - currentAFD->hasThrows() != otherAFD->hasThrows()) { - isAcceptableVersionBasedChange = true; - } - } - // - or they are computed properties of different types, - { - const auto *currentVD = dyn_cast(current); - const auto *otherVD = dyn_cast(other); - if (currentVD && otherVD && - !currentVD->hasStorage() && - !otherVD->hasStorage() && - !currentVD->getInterfaceType()->isEqual( - otherVD->getInterfaceType())) { - isAcceptableVersionBasedChange = true; - } - } - - if (isAcceptableVersionBasedChange) { - class AvailabilityRange { - Optional introduced; - Optional obsoleted; - - public: - static AvailabilityRange from(const ValueDecl *VD) { - AvailabilityRange result; - for (auto *attr : VD->getAttrs().getAttributes()) { - if (attr->PlatformAgnostic == - PlatformAgnosticAvailabilityKind::SwiftVersionSpecific) { - if (attr->Introduced) - result.introduced = attr->Introduced; - if (attr->Obsoleted) - result.obsoleted = attr->Obsoleted; - } - } - return result; - } - - bool fullyPrecedes(const AvailabilityRange &other) const { - if (!obsoleted.hasValue()) - return false; - if (!other.introduced.hasValue()) - return false; - return *obsoleted <= *other.introduced; - } - - bool overlaps(const AvailabilityRange &other) const { - return !fullyPrecedes(other) && !other.fullyPrecedes(*this); - } - }; - - auto currentAvail = AvailabilityRange::from(current); - auto otherAvail = AvailabilityRange::from(other); - if (!currentAvail.overlaps(otherAvail)) - continue; - } - - // If both are VarDecls, and both have exactly the same type, then - // matching the Swift 4 behaviour (i.e. just emitting the future-compat - // warning) will result in SILGen crashes due to both properties mangling - // the same, so it's better to just follow the Swift 5 behaviour and emit - // the actual error. - if (wouldBeSwift5Redeclaration && isa(current) && - isa(other) && - current->getInterfaceType()->isEqual(other->getInterfaceType())) { - wouldBeSwift5Redeclaration = false; - } +llvm::Expected +HasCircularRawValueRequest::evaluate(Evaluator &evaluator, + EnumDecl *decl) const { + if (canSkipCircularityCheck(decl) || !decl->hasRawType()) + return false; - // If this isn't a redeclaration in the current version of Swift, but - // would be in Swift 5 mode, emit a warning instead of an error. - if (wouldBeSwift5Redeclaration) { - current->diagnose(diag::invalid_redecl_swift5_warning, - current->getFullName()); - other->diagnose(diag::invalid_redecl_prev, other->getFullName()); - } else { - const auto *otherInit = dyn_cast(other); - // Provide a better description for implicit initializers. - if (otherInit && otherInit->isImplicit()) { - // Skip conflicts with inherited initializers, which only happen - // when the current declaration is within an extension. The override - // checker should have already taken care of emitting a more - // productive diagnostic. - if (!other->getOverriddenDecl()) - current->diagnose(diag::invalid_redecl_init, - current->getFullName(), - otherInit->isMemberwiseInitializer()); - } else { - ctx.Diags.diagnoseWithNotes( - current->diagnose(diag::invalid_redecl, - current->getFullName()), [&]() { - other->diagnose(diag::invalid_redecl_prev, other->getFullName()); - }); - } - markInvalid(); - } + auto *inherited = decl->getRawType()->getEnumOrBoundGenericEnum(); + if (!inherited) + return false; - // Make sure we don't do this checking again for the same decl. We also - // set this at the beginning of the function, but we might have swapped - // the decls for diagnostics; so ensure we also set this for the actual - // decl we diagnosed on. - current->setCheckedRedeclaration(true); - break; - } + // If we have a cycle, handle it and return true. + auto result = evaluator(HasCircularRawValueRequest{inherited}); + if (!result) { + using Error = CyclicalRequestError; + llvm::handleAllErrors(result.takeError(), [](const Error &E) {}); + return true; } + return result; } namespace { @@ -1042,13 +445,21 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const { // If we implement the ability for extensions defined in the same module // (or the same file) to add vtable entries, we can re-evaluate this // restriction. - if (dyn_cast(nominal) && - !decl->isSynthesized() && isa(decl->getDeclContext()) && + if (isa(nominal) && !decl->isSynthesized() && + isa(decl->getDeclContext()) && !(decl->getAttrs().hasAttribute())) { - diags.diagnose(decl->getLoc(), diag::designated_init_in_extension, - nominal->getName()) - .fixItInsert(decl->getLoc(), "convenience "); - return CtorInitializerKind::Convenience; + if (cast(nominal)->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { + diags.diagnose(decl->getLoc(), + diag::cfclass_designated_init_in_extension, + nominal->getName()); + return CtorInitializerKind::Designated; + } else { + diags.diagnose(decl->getLoc(), + diag::designated_init_in_extension, + nominal->getName()) + .fixItInsert(decl->getLoc(), "convenience "); + return CtorInitializerKind::Convenience; + } } if (decl->getDeclContext()->getExtendedProtocolDecl()) { @@ -1079,11 +490,11 @@ ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator, // class-bound protocol. for (const auto found : allInheritedNominals) { // Superclass bound. - if (isa(found.second)) + if (isa(found.Item)) return true; // A protocol that might be class-constrained. - if (auto proto = dyn_cast(found.second)) { + if (auto proto = dyn_cast(found.Item)) { if (proto->requiresClass()) return true; } @@ -1256,10 +667,18 @@ IsStaticRequest::evaluate(Evaluator &evaluator, FuncDecl *decl) const { decl->isOperator() && dc->isTypeContext()) { auto operatorName = decl->getFullName().getBaseIdentifier(); - decl->diagnose(diag::nonstatic_operator_in_type, - operatorName, dc->getDeclaredInterfaceType()) - .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), - "static "); + if (auto ED = dyn_cast(dc->getAsDecl())) { + decl->diagnose(diag::nonstatic_operator_in_extension, + operatorName, ED->getExtendedTypeRepr()) + .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), + "static "); + } else { + auto *NTD = cast(dc->getAsDecl()); + decl->diagnose(diag::nonstatic_operator_in_nominal, operatorName, + NTD->getName()) + .fixItInsert(decl->getAttributeInsertionLoc(/*forModifier=*/true), + "static "); + } result = true; } @@ -1470,21 +889,6 @@ NeedsNewVTableEntryRequest::evaluate(Evaluator &evaluator, return !isABICompatibleOverride; } -namespace { - /// How to generate the raw value for each element of an enum that doesn't - /// have one explicitly specified. - enum class AutomaticEnumValueKind { - /// Raw values cannot be automatically generated. - None, - /// The raw value is the enum element's name. - String, - /// The raw value is the previous element's raw value, incremented. - /// - /// For the first element in the enum, the raw value is 0. - Integer, - }; -} // end anonymous namespace - /// Given the raw value literal expression for an enum case, produces the /// auto-incremented raw value for the subsequent case, or returns null if /// the value is not auto-incrementable. @@ -1534,8 +938,8 @@ static LiteralExpr *getAutomaticRawValueExpr(AutomaticEnumValueKind valueKind, llvm_unreachable("Unhandled AutomaticEnumValueKind in switch."); } -static Optional -computeAutomaticEnumValueKind(EnumDecl *ED) { +Optional +swift::computeAutomaticEnumValueKind(EnumDecl *ED) { Type rawTy = ED->getRawType(); assert(rawTy && "Cannot compute value kind without raw type!"); @@ -1546,10 +950,10 @@ computeAutomaticEnumValueKind(EnumDecl *ED) { // primitive literal protocols. auto conformsToProtocol = [&](KnownProtocolKind protoKind) { ProtocolDecl *proto = ED->getASTContext().getProtocol(protoKind); - return TypeChecker::conformsToProtocol(rawTy, proto, - ED->getDeclContext(), None); + return TypeChecker::conformsToProtocol(rawTy, proto, ED->getDeclContext(), + None); }; - + static auto otherLiteralProtocolKinds = { KnownProtocolKind::ExpressibleByFloatLiteral, KnownProtocolKind::ExpressibleByUnicodeScalarLiteral, @@ -1596,9 +1000,6 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, Optional valueKind; for (auto elt : ED->getAllElements()) { - // FIXME: Computes isInvalid() below. - (void) elt->getInterfaceType(); - // If the element has been diagnosed up to now, skip it. if (elt->isInvalid()) continue; @@ -1641,12 +1042,11 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, { - auto *TC = static_cast(ED->getASTContext().getLazyResolver()); - assert(TC && "Must have a lazy resolver set"); Expr *exprToCheck = prevValue; - if (TC->typeCheckExpression(exprToCheck, ED, TypeLoc::withoutLoc(rawTy), - CTP_EnumCaseRawValue)) { - TC->checkEnumElementErrorHandling(elt, exprToCheck); + if (TypeChecker::typeCheckExpression(exprToCheck, ED, + TypeLoc::withoutLoc(rawTy), + CTP_EnumCaseRawValue)) { + TypeChecker::checkEnumElementErrorHandling(elt, exprToCheck); } } @@ -1657,6 +1057,7 @@ EnumRawValuesRequest::evaluate(Evaluator &eval, EnumDecl *ED, continue; } + // If the raw values of the enum case are fixed, then we trust our callers // to have set things up correctly. This comes up with imported enums // and deserialized @objc enums which always have their raw values setup @@ -1836,158 +1237,120 @@ static void checkPrecedenceCircularity(DiagnosticEngine &D, buildLowerThanPath(PGD, rel.Group, str); } - D.diagnose(PGD->getHigherThanLoc(), diag::precedence_group_cycle, path); + D.diagnose(PGD->getHigherThanLoc(), + diag::higher_than_precedence_group_cycle, path); PGD->setInvalid(); return; } } while (!stack.empty()); } -static void validatePrecedenceGroup(PrecedenceGroupDecl *PGD) { +static PrecedenceGroupDecl * +lookupPrecedenceGroup(const PrecedenceGroupDescriptor &descriptor) { + auto *dc = descriptor.dc; + if (auto sf = dc->getParentSourceFile()) { + bool cascading = dc->isCascadingContextForLookup(false); + return sf->lookupPrecedenceGroup(descriptor.ident, cascading, + descriptor.nameLoc); + } else { + return dc->getParentModule()->lookupPrecedenceGroup(descriptor.ident, + descriptor.nameLoc); + } +} + +void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) { assert(PGD && "Cannot validate a null precedence group!"); - if (PGD->isInvalid() || PGD->hasValidationStarted()) + if (PGD->isInvalid()) return; - DeclValidationRAII IBV(PGD); auto &Diags = PGD->getASTContext().Diags; - + // Validate the higherThan relationships. bool addedHigherThan = false; for (auto &rel : PGD->getMutableHigherThan()) { - if (rel.Group) continue; + if (rel.Group) + continue; - auto group = TypeChecker::lookupPrecedenceGroup(PGD->getDeclContext(), - rel.Name, rel.NameLoc); + PrecedenceGroupDescriptor desc{PGD->getDeclContext(), rel.Name, rel.NameLoc, + PrecedenceGroupDescriptor::HigherThan}; + auto group = evaluateOrDefault(PGD->getASTContext().evaluator, + LookupPrecedenceGroupRequest{desc}, nullptr); if (group) { rel.Group = group; addedHigherThan = true; - } else if (!PGD->isInvalid()) { - Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); + } else { + if (!lookupPrecedenceGroup(desc)) + Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); PGD->setInvalid(); } } // Validate the lowerThan relationships. for (auto &rel : PGD->getMutableLowerThan()) { - if (rel.Group) continue; + if (rel.Group) + continue; auto dc = PGD->getDeclContext(); - auto group = TypeChecker::lookupPrecedenceGroup(dc, rel.Name, rel.NameLoc); + PrecedenceGroupDescriptor desc{PGD->getDeclContext(), rel.Name, rel.NameLoc, + PrecedenceGroupDescriptor::LowerThan}; + auto group = evaluateOrDefault(PGD->getASTContext().evaluator, + LookupPrecedenceGroupRequest{desc}, nullptr); + bool hadError = false; if (group) { - if (group->getDeclContext()->getParentModule() == dc->getParentModule()) { - if (!PGD->isInvalid()) { - Diags.diagnose(rel.NameLoc, - diag::precedence_group_lower_within_module); - Diags.diagnose(group->getNameLoc(), diag::kind_declared_here, - DescriptiveDeclKind::PrecedenceGroup); - PGD->setInvalid(); - } + rel.Group = group; + } else { + hadError = true; + if (auto *rawGroup = lookupPrecedenceGroup(desc)) { + // We already know the lowerThan path is errant, try to use the results + // of a raw lookup to enforce the same-module restriction. + group = rawGroup; } else { - rel.Group = group; + Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); } - } else if (!PGD->isInvalid()) { - Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name); - PGD->setInvalid(); } - } - - // Check for circularity. - if (addedHigherThan) { - checkPrecedenceCircularity(Diags, PGD); - } -} - -static Optional -getParamIndex(const ParameterList *paramList, const ParamDecl *decl) { - ArrayRef params = paramList->getArray(); - for (unsigned i = 0; i < params.size(); ++i) { - if (params[i] == decl) return i; - } - return None; -} -static void -checkInheritedDefaultValueRestrictions(TypeChecker &TC, ParamDecl *PD) { - if (PD->getDefaultArgumentKind() != DefaultArgumentKind::Inherited) - return; - - auto *DC = PD->getInnermostDeclContext(); - const SourceFile *SF = DC->getParentSourceFile(); - assert((SF && SF->Kind == SourceFileKind::Interface || PD->isImplicit()) && - "explicit inherited default argument outside of a module interface?"); - - // The containing decl should be a designated initializer. - auto ctor = dyn_cast(DC); - if (!ctor || ctor->isConvenienceInit()) { - TC.diagnose( - PD, diag::inherited_default_value_not_in_designated_constructor); - return; - } + if (group && + group->getDeclContext()->getParentModule() == dc->getParentModule()) { + if (!PGD->isInvalid()) { + Diags.diagnose(rel.NameLoc, diag::precedence_group_lower_within_module); + Diags.diagnose(group->getNameLoc(), diag::kind_declared_here, + DescriptiveDeclKind::PrecedenceGroup); + } + hadError = true; + } - // The decl it overrides should also be a designated initializer. - auto overridden = ctor->getOverriddenDecl(); - if (!overridden || overridden->isConvenienceInit()) { - TC.diagnose( - PD, diag::inherited_default_value_used_in_non_overriding_constructor); - if (overridden) - TC.diagnose(overridden, diag::overridden_here); - return; + if (hadError) + PGD->setInvalid(); } - // The corresponding parameter should have a default value. - Optional idx = getParamIndex(ctor->getParameters(), PD); - assert(idx && "containing decl does not contain param?"); - ParamDecl *equivalentParam = overridden->getParameters()->get(*idx); - if (equivalentParam->getDefaultArgumentKind() == DefaultArgumentKind::None) { - TC.diagnose(PD, diag::corresponding_param_not_defaulted); - TC.diagnose(equivalentParam, diag::inherited_default_param_here); - } + // Try to diagnose trickier cycles that request evaluation alone can't catch. + if (addedHigherThan) + checkPrecedenceCircularity(Diags, PGD); } -/// Check the default arguments that occur within this pattern. -static void checkDefaultArguments(TypeChecker &tc, ParameterList *params, - ValueDecl *VD) { - for (auto *param : *params) { - checkInheritedDefaultValueRestrictions(tc, param); - if (!param->getDefaultValue() || - !param->hasInterfaceType() || - param->getInterfaceType()->hasError()) - continue; - - Expr *e = param->getDefaultValue(); - auto *initContext = param->getDefaultArgumentInitContext(); - - auto resultTy = - tc.typeCheckParameterDefault(e, initContext, param->getType(), - /*isAutoClosure=*/param->isAutoClosure()); - - if (resultTy) { - param->setDefaultValue(e); - } - - tc.checkInitializerErrorHandling(initContext, e); - - // Walk the checked initializer and contextualize any closures - // we saw there. - (void)tc.contextualizeInitializer(initContext, e); +llvm::Expected LookupPrecedenceGroupRequest::evaluate( + Evaluator &eval, PrecedenceGroupDescriptor descriptor) const { + if (auto *group = lookupPrecedenceGroup(descriptor)) { + validatePrecedenceGroup(group); + return group; } + + return nullptr; } PrecedenceGroupDecl *TypeChecker::lookupPrecedenceGroup(DeclContext *dc, Identifier name, SourceLoc nameLoc) { - auto *group = evaluateOrDefault( + return evaluateOrDefault( dc->getASTContext().evaluator, - LookupPrecedenceGroupRequest({dc, name, nameLoc}), nullptr); - if (group) - validatePrecedenceGroup(group); - return group; + LookupPrecedenceGroupRequest({dc, name, nameLoc, None}), nullptr); } static NominalTypeDecl *resolveSingleNominalTypeDecl( DeclContext *DC, SourceLoc loc, Identifier ident, ASTContext &Ctx, TypeResolutionFlags flags = TypeResolutionFlags(0)) { - auto *TyR = new (Ctx) SimpleIdentTypeRepr(loc, ident); + auto *TyR = new (Ctx) SimpleIdentTypeRepr(DeclNameLoc(loc), + DeclNameRef(ident)); TypeLoc typeLoc = TypeLoc(TyR); TypeResolutionOptions options = TypeResolverContext::TypeAliasDecl; @@ -1999,7 +1362,7 @@ static NominalTypeDecl *resolveSingleNominalTypeDecl( return typeLoc.getType()->getAnyNominal(); } -static bool checkDesignatedTypes(OperatorDecl *OD, +bool swift::checkDesignatedTypes(OperatorDecl *OD, ArrayRef identifiers, ArrayRef identifierLocs, ASTContext &ctx) { @@ -2031,7 +1394,7 @@ llvm::Expected OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator, InfixOperatorDecl *IOD) const { auto enableOperatorDesignatedTypes = - IOD->getASTContext().LangOpts.EnableOperatorDesignatedTypes; + IOD->getASTContext().TypeCheckerOpts.EnableOperatorDesignatedTypes; auto &Diags = IOD->getASTContext().Diags; PrecedenceGroupDecl *group = nullptr; @@ -2093,17 +1456,15 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator, return group; } -bool swift::doesContextHaveValueSemantics(DeclContext *dc) { - if (Type contextTy = dc->getDeclaredInterfaceType()) - return !contextTy->hasReferenceSemantics(); - return false; -} - llvm::Expected SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { if (FD->getAttrs().getAttribute(true)) { - if (!FD->isInstanceMember() || - !doesContextHaveValueSemantics(FD->getDeclContext())) { + if (!FD->isInstanceMember() || !FD->getDeclContext()->hasValueSemantics()) { + // If this decl is on a class-constrained protocol extension, then + // respect the explicit mutatingness. Otherwise, we would throw an + // error. + if (FD->getDeclContext()->isClassConstrainedProtocolExtension()) + return SelfAccessKind::Mutating; return SelfAccessKind::NonMutating; } return SelfAccessKind::Mutating; @@ -2125,8 +1486,7 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { case AccessorKind::MutableAddress: case AccessorKind::Set: case AccessorKind::Modify: - if (AD->isInstanceMember() && - doesContextHaveValueSemantics(AD->getDeclContext())) + if (AD->isInstanceMember() && AD->getDeclContext()->hasValueSemantics()) return SelfAccessKind::Mutating; break; @@ -2144,1428 +1504,16 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { return SelfAccessKind::NonMutating; } -/// Check the requirements in the where clause of the given \c source -/// to ensure that they don't introduce additional 'Self' requirements. -static void checkProtocolSelfRequirements(ProtocolDecl *proto, - TypeDecl *source) { - WhereClauseOwner(source).visitRequirements( - TypeResolutionStage::Interface, - [&](const Requirement &req, RequirementRepr *reqRepr) { - switch (req.getKind()) { - case RequirementKind::Conformance: - case RequirementKind::Layout: - case RequirementKind::Superclass: - if (reqRepr && - req.getFirstType()->isEqual(proto->getSelfInterfaceType())) { - auto &diags = proto->getASTContext().Diags; - diags.diagnose(reqRepr->getSubjectLoc().getLoc(), - diag::protocol_where_clause_self_requirement); - } - - return false; - - case RequirementKind::SameType: - return false; - } - llvm_unreachable("unhandled kind"); - }); -} - -/// For now, DynamicSelfType can only appear at the top level of a -/// function result type, possibly wrapped in an optional type. -/// -/// In the future, we could generalize it to allow it in any -/// covariant position, so that for example a class method could -/// return '() -> Self'. -static void checkDynamicSelfType(ValueDecl *decl, Type type) { - if (!type->hasDynamicSelfType()) - return; - - if (auto objectTy = type->getOptionalObjectType()) - type = objectTy; - - if (type->is()) - return; - - if (isa(decl)) - decl->diagnose(diag::dynamic_self_invalid_method); - else if (isa(decl)) - decl->diagnose(diag::dynamic_self_invalid_property); - else { - assert(isa(decl)); - decl->diagnose(diag::dynamic_self_invalid_subscript); - } -} - -namespace { -class DeclChecker : public DeclVisitor { -public: - TypeChecker &TC; - - explicit DeclChecker(TypeChecker &TC) : TC(TC) {} - - void visit(Decl *decl) { - if (TC.Context.Stats) - TC.Context.Stats->getFrontendCounters().NumDeclsTypechecked++; - - FrontendStatsTracer StatsTracer(TC.Context.Stats, "typecheck-decl", decl); - PrettyStackTraceDecl StackTrace("type-checking", decl); - - DeclVisitor::visit(decl); - - TC.checkUnsupportedProtocolType(decl); - - if (auto VD = dyn_cast(decl)) { - auto &Context = TC.Context; - checkRedeclaration(Context, VD); - - // Force some requests, which can produce diagnostics. - - // Compute access level. - (void) VD->getFormalAccess(); - - // Compute overrides. - (void) VD->getOverriddenDecls(); - - // Check whether the member is @objc or dynamic. - (void) VD->isObjC(); - (void) VD->isDynamic(); - - // If this is a member of a nominal type, don't allow it to have a name of - // "Type" or "Protocol" since we reserve the X.Type and X.Protocol - // expressions to mean something builtin to the language. We *do* allow - // these if they are escaped with backticks though. - if (VD->getDeclContext()->isTypeContext() && - (VD->getFullName().isSimpleName(Context.Id_Type) || - VD->getFullName().isSimpleName(Context.Id_Protocol)) && - VD->getNameLoc().isValid() && - Context.SourceMgr.extractText({VD->getNameLoc(), 1}) != "`") { - TC.diagnose(VD->getNameLoc(), diag::reserved_member_name, - VD->getFullName(), VD->getBaseName().getIdentifier().str()); - TC.diagnose(VD->getNameLoc(), diag::backticks_to_escape) - .fixItReplace(VD->getNameLoc(), - "`" + VD->getBaseName().userFacingName().str() + "`"); - } - } - } - - - //===--------------------------------------------------------------------===// - // Visit Methods. - //===--------------------------------------------------------------------===// - - void visitGenericTypeParamDecl(GenericTypeParamDecl *D) { - llvm_unreachable("cannot reach here"); - } - - void visitImportDecl(ImportDecl *ID) { - TC.checkDeclAttributes(ID); - } - - void visitOperatorDecl(OperatorDecl *OD) { - TC.checkDeclAttributes(OD); - auto &Ctx = OD->getASTContext(); - if (auto *IOD = dyn_cast(OD)) { - (void)IOD->getPrecedenceGroup(); - } else { - auto nominalTypes = OD->getDesignatedNominalTypes(); - if (nominalTypes.empty() && Ctx.LangOpts.EnableOperatorDesignatedTypes) { - auto identifiers = OD->getIdentifiers(); - auto identifierLocs = OD->getIdentifierLocs(); - if (checkDesignatedTypes(OD, identifiers, identifierLocs, Ctx)) - OD->setInvalid(); - } - return; - } - checkAccessControl(TC, OD); - } - - void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) { - TC.checkDeclAttributes(PGD); - validatePrecedenceGroup(PGD); - checkAccessControl(TC, PGD); - } - - void visitMissingMemberDecl(MissingMemberDecl *MMD) { - llvm_unreachable("should always be type-checked already"); - } - - void visitBoundVariable(VarDecl *VD) { - // WARNING: Anything you put in this function will only be run when the - // VarDecl is fully type-checked within its own file. It will NOT be run - // when the VarDecl is merely used from another file. - TC.validateDecl(VD); - - // Compute these requests in case they emit diagnostics. - (void) VD->isGetterMutating(); - (void) VD->isSetterMutating(); - (void) VD->getPropertyWrapperBackingProperty(); - (void) VD->getImplInfo(); - - // Add the '@_hasStorage' attribute if this property is stored. - if (VD->hasStorage() && !VD->getAttrs().hasAttribute()) - VD->getAttrs().add(new (TC.Context) HasStorageAttr(/*isImplicit=*/true)); - - // Reject cases where this is a variable that has storage but it isn't - // allowed. - if (VD->hasStorage()) { - // Note: Stored properties in protocols, enums, etc are diagnosed in - // finishStorageImplInfo(). - - // We haven't implemented type-level storage in some contexts. - if (VD->isStatic()) { - auto PBD = VD->getParentPatternBinding(); - // Selector for unimplemented_static_var message. - enum : unsigned { - Misc, - GenericTypes, - Classes, - ProtocolExtensions - }; - auto unimplementedStatic = [&](unsigned diagSel) { - auto staticLoc = PBD->getStaticLoc(); - TC.diagnose(VD->getLoc(), diag::unimplemented_static_var, - diagSel, PBD->getStaticSpelling(), - diagSel == Classes) - .highlight(staticLoc); - }; - - auto DC = VD->getDeclContext(); - - // Stored type variables in a generic context need to logically - // occur once per instantiation, which we don't yet handle. - if (DC->getExtendedProtocolDecl()) { - unimplementedStatic(ProtocolExtensions); - } else if (DC->isGenericContext() - && !DC->getGenericSignatureOfContext()->areAllParamsConcrete()) { - unimplementedStatic(GenericTypes); - } else if (DC->getSelfClassDecl()) { - auto StaticSpelling = PBD->getStaticSpelling(); - if (StaticSpelling != StaticSpellingKind::KeywordStatic) - unimplementedStatic(Classes); - } - } - } - - TC.checkDeclAttributes(VD); - - if (!checkOverrides(VD)) { - // If a property has an override attribute but does not override - // anything, complain. - auto overridden = VD->getOverriddenDecl(); - if (auto *OA = VD->getAttrs().getAttribute()) { - if (!overridden) { - TC.diagnose(VD, diag::property_does_not_override) - .highlight(OA->getLocation()); - OA->setInvalid(); - } - } - } - - if (VD->getDeclContext()->getSelfClassDecl()) { - checkDynamicSelfType(VD, VD->getValueInterfaceType()); - - if (VD->getValueInterfaceType()->hasDynamicSelfType()) { - if (VD->hasStorage()) - VD->diagnose(diag::dynamic_self_in_stored_property); - else if (VD->isSettable(nullptr)) - VD->diagnose(diag::dynamic_self_in_mutable_property); - } - } - - // Under the Swift 3 inference rules, if we have @IBInspectable or - // @GKInspectable but did not infer @objc, warn that the attribute is - if (!VD->isObjC() && TC.Context.LangOpts.EnableSwift3ObjCInference) { - if (auto attr = VD->getAttrs().getAttribute()) { - TC.diagnose(attr->getLocation(), - diag::attribute_meaningless_when_nonobjc, - attr->getAttrName()) - .fixItRemove(attr->getRange()); - } - - if (auto attr = VD->getAttrs().getAttribute()) { - TC.diagnose(attr->getLocation(), - diag::attribute_meaningless_when_nonobjc, - attr->getAttrName()) - .fixItRemove(attr->getRange()); - } - } - - // Now check all the accessors. - VD->visitEmittedAccessors([&](AccessorDecl *accessor) { - visit(accessor); - }); - } - - void visitBoundVars(Pattern *P) { - P->forEachVariable([&] (VarDecl *VD) { this->visitBoundVariable(VD); }); - } - - void visitPatternBindingDecl(PatternBindingDecl *PBD) { - DeclContext *DC = PBD->getDeclContext(); - - // Check all the pattern/init pairs in the PBD. - validatePatternBindingEntries(TC, PBD); - - TC.checkDeclAttributes(PBD); - - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - // Type check each VarDecl that this PatternBinding handles. - visitBoundVars(PBD->getPattern(i)); - - // If we have a type but no initializer, check whether the type is - // default-initializable. If so, do it. - if (PBD->getPattern(i)->hasType() && - !PBD->isInitialized(i) && - PBD->isDefaultInitializable(i) && - PBD->getPattern(i)->hasStorage() && - !PBD->getPattern(i)->getType()->hasError()) { - auto type = PBD->getPattern(i)->getType(); - if (auto defaultInit = TC.buildDefaultInitializer(type)) { - // If we got a default initializer, install it and re-type-check it - // to make sure it is properly coerced to the pattern type. - PBD->setInit(i, defaultInit); - } - } - - if (PBD->isInitialized(i)) { - // Add the attribute that preserves the "has an initializer" value across - // module generation, as required for TBDGen. - PBD->getPattern(i)->forEachVariable([&](VarDecl *VD) { - if (VD->hasStorage() && - !VD->getAttrs().hasAttribute()) { - auto *attr = new (TC.Context) HasInitialValueAttr( - /*IsImplicit=*/true); - VD->getAttrs().add(attr); - } - }); - } - } - - bool isInSILMode = false; - if (auto sourceFile = DC->getParentSourceFile()) - isInSILMode = sourceFile->Kind == SourceFileKind::SIL; - bool isTypeContext = DC->isTypeContext(); - - // If this is a declaration without an initializer, reject code if - // uninitialized vars are not allowed. - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - auto entry = PBD->getPatternList()[i]; - - if (entry.isInitialized() || isInSILMode) continue; - - entry.getPattern()->forEachVariable([&](VarDecl *var) { - // If the variable has no storage, it never needs an initializer. - if (!var->hasStorage()) - return; - - if (var->isInvalid() || PBD->isInvalid()) - return; - - auto markVarAndPBDInvalid = [PBD, var] { - PBD->setInvalid(); - var->setInvalid(); - if (!var->hasType()) - var->markInvalid(); - }; - - // Properties with an opaque return type need an initializer to - // determine their underlying type. - if (var->getOpaqueResultTypeDecl()) { - TC.diagnose(var->getLoc(), diag::opaque_type_var_no_init); - } - - // Non-member observing properties need an initializer. - if (var->getWriteImpl() == WriteImplKind::StoredWithObservers && - !isTypeContext) { - TC.diagnose(var->getLoc(), diag::observingprop_requires_initializer); - markVarAndPBDInvalid(); - return; - } - - // Static/class declarations require an initializer unless in a - // protocol. - if (var->isStatic() && !isa(DC)) { - // ...but don't enforce this for SIL or module interface files. - switch (DC->getParentSourceFile()->Kind) { - case SourceFileKind::Interface: - case SourceFileKind::SIL: - return; - case SourceFileKind::Main: - case SourceFileKind::REPL: - case SourceFileKind::Library: - break; - } - - TC.diagnose(var->getLoc(), diag::static_requires_initializer, - var->getCorrectStaticSpelling()); - markVarAndPBDInvalid(); - return; - } - - // Global variables require an initializer in normal source files. - if (DC->isModuleScopeContext()) { - switch (DC->getParentSourceFile()->Kind) { - case SourceFileKind::Main: - case SourceFileKind::REPL: - case SourceFileKind::Interface: - case SourceFileKind::SIL: - return; - case SourceFileKind::Library: - break; - } - - TC.diagnose(var->getLoc(), diag::global_requires_initializer, - var->isLet()); - markVarAndPBDInvalid(); - return; - } - }); - } - - TC.checkDeclAttributes(PBD); - - checkAccessControl(TC, PBD); - - // If the initializers in the PBD aren't checked yet, do so now. - for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) { - if (!PBD->isInitialized(i)) - continue; - - if (!PBD->isInitializerChecked(i)) { - TC.typeCheckPatternBinding(PBD, i); - } - - if (!PBD->isInvalid()) { - auto &entry = PBD->getPatternList()[i]; - auto *init = PBD->getInit(i); - - // If we're performing an binding to a weak or unowned variable from a - // constructor call, emit a warning that the instance will be immediately - // deallocated. - diagnoseUnownedImmediateDeallocation(TC, PBD->getPattern(i), - entry.getEqualLoc(), - init); - - // If we entered an initializer context, contextualize any - // auto-closures we might have created. - // Note that we don't contextualize the initializer for a property - // with a wrapper, because the initializer will have been subsumed - // by the backing storage property. - if (!DC->isLocalContext() && - !(PBD->getSingleVar() && - PBD->getSingleVar()->hasAttachedPropertyWrapper())) { - auto *initContext = cast_or_null( - entry.getInitContext()); - if (initContext) { - // Check safety of error-handling in the declaration, too. - TC.checkInitializerErrorHandling(initContext, init); - (void) TC.contextualizeInitializer(initContext, init); - } - } - } - } - } - - void visitSubscriptDecl(SubscriptDecl *SD) { - // Force requests that can emit diagnostics. - (void) SD->getInterfaceType(); - (void) SD->getGenericSignature(); - - if (!SD->isInvalid()) { - TC.checkReferencedGenericParams(SD); - checkGenericParams(SD->getGenericParams(), SD, TC); - TC.checkProtocolSelfRequirements(SD); - } - - TC.checkDeclAttributes(SD); - - checkAccessControl(TC, SD); - - if (!checkOverrides(SD)) { - // If a subscript has an override attribute but does not override - // anything, complain. - if (auto *OA = SD->getAttrs().getAttribute()) { - if (!SD->getOverriddenDecl()) { - TC.diagnose(SD, diag::subscript_does_not_override) - .highlight(OA->getLocation()); - OA->setInvalid(); - } - } - } - - // Compute these requests in case they emit diagnostics. - (void) SD->isGetterMutating(); - (void) SD->isSetterMutating(); - (void) SD->getImplInfo(); - - TC.checkParameterAttributes(SD->getIndices()); - checkDefaultArguments(TC, SD->getIndices(), SD); - - if (SD->getDeclContext()->getSelfClassDecl()) { - checkDynamicSelfType(SD, SD->getValueInterfaceType()); - - if (SD->getValueInterfaceType()->hasDynamicSelfType() && - SD->supportsMutation()) { - SD->diagnose(diag::dynamic_self_in_mutable_subscript); - } - } - - // Now check all the accessors. - SD->visitEmittedAccessors([&](AccessorDecl *accessor) { - visit(accessor); - }); - } - - void visitTypeAliasDecl(TypeAliasDecl *TAD) { - // Force requests that can emit diagnostics. - (void) TAD->getGenericSignature(); - (void) TAD->getUnderlyingType(); - - TC.checkDeclAttributes(TAD); - checkAccessControl(TC, TAD); - - } - - void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) { - // Force requests that can emit diagnostics. - (void) OTD->getGenericSignature(); - - TC.checkDeclAttributes(OTD); - checkAccessControl(TC, OTD); - } - - void visitAssociatedTypeDecl(AssociatedTypeDecl *AT) { - TC.checkDeclAttributes(AT); - - checkInheritanceClause(AT); - auto *proto = AT->getProtocol(); - - checkProtocolSelfRequirements(proto, AT); - - if (proto->isObjC()) { - TC.diagnose(AT->getLoc(), - diag::associated_type_objc, - AT->getName(), - proto->getName()); - } - - checkAccessControl(TC, AT); - - // Trigger the checking for overridden declarations. - (void) AT->getOverriddenDecls(); - - auto defaultType = AT->getDefaultDefinitionType(); - if (defaultType && !defaultType->hasError()) { - // associatedtype X = X is invalid - auto mentionsItself = - defaultType.findIf([&](Type defaultType) { - if (auto DMT = defaultType->getAs()) { - return DMT->getAssocType() == AT; - } - return false; - }); - - if (mentionsItself) { - TC.diagnose(AT->getDefaultDefinitionTypeRepr()->getLoc(), - diag::recursive_decl_reference, - AT->getDescriptiveKind(), AT->getName()); - AT->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); - } - } - } - - void checkUnsupportedNestedType(NominalTypeDecl *NTD) { - TC.diagnoseInlinableLocalType(NTD); - - // We don't support protocols outside the top level of a file. - if (isa(NTD) && - !NTD->getParent()->isModuleScopeContext()) { - TC.diagnose(NTD->getLoc(), - diag::unsupported_nested_protocol, - NTD->getName()); - NTD->setInvalid(); - return; - } - - // We don't support nested types in generics yet. - if (NTD->isGenericContext()) { - auto DC = NTD->getDeclContext(); - if (auto proto = DC->getSelfProtocolDecl()) { - if (DC->getExtendedProtocolDecl()) { - TC.diagnose(NTD->getLoc(), - diag::unsupported_type_nested_in_protocol_extension, - NTD->getName(), - proto->getName()); - } else { - TC.diagnose(NTD->getLoc(), - diag::unsupported_type_nested_in_protocol, - NTD->getName(), - proto->getName()); - } - } - - if (DC->isLocalContext() && DC->isGenericContext()) { - // A local generic context is a generic function. - if (auto AFD = dyn_cast(DC)) { - TC.diagnose(NTD->getLoc(), - diag::unsupported_type_nested_in_generic_function, - NTD->getName(), - AFD->getFullName()); - } else { - TC.diagnose(NTD->getLoc(), - diag::unsupported_type_nested_in_generic_closure, - NTD->getName()); - } - } - } - } - - void visitEnumDecl(EnumDecl *ED) { - checkUnsupportedNestedType(ED); - - // FIXME: Remove this once we clean up the mess involving raw values. - (void) ED->getInterfaceType(); - - checkGenericParams(ED->getGenericParams(), ED, TC); - - { - // Check for circular inheritance of the raw type. - SmallVector path; - path.push_back(ED); - checkCircularity(TC, ED, diag::circular_enum_inheritance, - DescriptiveDeclKind::Enum, path); - } - - for (Decl *member : ED->getMembers()) - visit(member); - - TC.checkDeclAttributes(ED); - - checkInheritanceClause(ED); - - checkAccessControl(TC, ED); - - TC.checkPatternBindingCaptures(ED); - - if (auto rawTy = ED->getRawType()) { - // The raw type must be one of the blessed literal convertible types. - if (!computeAutomaticEnumValueKind(ED)) { - TC.diagnose(ED->getInherited().front().getSourceRange().Start, - diag::raw_type_not_literal_convertible, - rawTy); - ED->getInherited().front().setInvalidType(TC.Context); - } - - // We need at least one case to have a raw value. - if (ED->getAllElements().empty()) { - TC.diagnose(ED->getInherited().front().getSourceRange().Start, - diag::empty_enum_raw_type); - } - } - - checkExplicitAvailability(ED); - - TC.checkDeclCircularity(ED); - TC.ConformanceContexts.push_back(ED); - } - - void visitStructDecl(StructDecl *SD) { - checkUnsupportedNestedType(SD); - - checkGenericParams(SD->getGenericParams(), SD, TC); - - // Force lowering of stored properties. - (void) SD->getStoredProperties(); - - TC.addImplicitConstructors(SD); - - for (Decl *Member : SD->getMembers()) - visit(Member); - - TC.checkPatternBindingCaptures(SD); - - TC.checkDeclAttributes(SD); - - checkInheritanceClause(SD); - - checkAccessControl(TC, SD); - - checkExplicitAvailability(SD); - - TC.checkDeclCircularity(SD); - TC.ConformanceContexts.push_back(SD); - } - - /// Check whether the given properties can be @NSManaged in this class. - static bool propertiesCanBeNSManaged(ClassDecl *classDecl, - ArrayRef vars) { - // Check whether we have an Objective-C-defined class in our - // inheritance chain. - if (!classDecl->checkAncestry(AncestryFlags::ClangImported)) - return false; - - // If all of the variables are @objc, we can use @NSManaged. - for (auto var : vars) { - if (!var->isObjC()) - return false; - } - - // Okay, we can use @NSManaged. - return true; - } - - /// Check that all stored properties have in-class initializers. - void checkRequiredInClassInits(ClassDecl *cd) { - ClassDecl *source = nullptr; - for (auto member : cd->getMembers()) { - auto pbd = dyn_cast(member); - if (!pbd) - continue; - - if (pbd->isStatic() || !pbd->hasStorage() || - pbd->isDefaultInitializable() || pbd->isInvalid()) - continue; - - // The variables in this pattern have not been - // initialized. Diagnose the lack of initial value. - pbd->setInvalid(); - SmallVector vars; - for (auto entry : pbd->getPatternList()) - entry.getPattern()->collectVariables(vars); - bool suggestNSManaged = propertiesCanBeNSManaged(cd, vars); - switch (vars.size()) { - case 0: - llvm_unreachable("should have been marked invalid"); - - case 1: - TC.diagnose(pbd->getLoc(), diag::missing_in_class_init_1, - vars[0]->getName(), suggestNSManaged); - break; - - case 2: - TC.diagnose(pbd->getLoc(), diag::missing_in_class_init_2, - vars[0]->getName(), vars[1]->getName(), suggestNSManaged); - break; - - case 3: - TC.diagnose(pbd->getLoc(), diag::missing_in_class_init_3plus, - vars[0]->getName(), vars[1]->getName(), vars[2]->getName(), - false, suggestNSManaged); - break; - - default: - TC.diagnose(pbd->getLoc(), diag::missing_in_class_init_3plus, - vars[0]->getName(), vars[1]->getName(), vars[2]->getName(), - true, suggestNSManaged); - break; - } - - // Figure out where this requirement came from. - if (!source) { - source = cd; - while (true) { - // If this class had the 'requires_stored_property_inits' - // attribute, diagnose here. - if (source->getAttrs(). - hasAttribute()) - break; - - // If the superclass doesn't require in-class initial - // values, the requirement was introduced at this point, so - // stop here. - auto superclass = source->getSuperclassDecl(); - if (!superclass->requiresStoredPropertyInits()) - break; - - // Keep looking. - source = superclass; - } - } - - // Add a note describing why we need an initializer. - TC.diagnose(source, diag::requires_stored_property_inits_here, - source->getDeclaredType(), cd == source, suggestNSManaged); - } - } - - - void visitClassDecl(ClassDecl *CD) { - checkUnsupportedNestedType(CD); - - // Force creation of the generic signature. - (void) CD->getGenericSignature(); - - checkGenericParams(CD->getGenericParams(), CD, TC); - - { - // Check for circular inheritance. - SmallVector path; - path.push_back(CD); - checkCircularity(TC, CD, diag::circular_class_inheritance, - DescriptiveDeclKind::Class, path); - } - - // Force lowering of stored properties. - (void) CD->getStoredProperties(); - - // Force creation of an implicit destructor, if any. - (void) CD->getDestructor(); - - for (Decl *Member : CD->getEmittedMembers()) - visit(Member); - - TC.checkPatternBindingCaptures(CD); - - // If this class requires all of its stored properties to have - // in-class initializers, diagnose this now. - if (CD->requiresStoredPropertyInits()) - checkRequiredInClassInits(CD); - - // Compute @objc for each superclass member, to catch selector - // conflicts resulting from unintended overrides. - // - // FIXME: This should be a request so we can measure how much work - // we're doing here. - CD->walkSuperclasses( - [&](ClassDecl *superclass) { - if (!superclass->getParentSourceFile()) - return TypeWalker::Action::Stop; - - for (auto *member : superclass->getMembers()) { - if (auto *vd = dyn_cast(member)) { - if (vd->isPotentiallyOverridable()) { - (void) vd->isObjC(); - } - } - } - - return TypeWalker::Action::Continue; - }); - - if (auto superclassTy = CD->getSuperclass()) { - ClassDecl *Super = superclassTy->getClassOrBoundGenericClass(); - - if (auto *SF = CD->getParentSourceFile()) { - if (auto *tracker = SF->getReferencedNameTracker()) { - bool isPrivate = - CD->getFormalAccess() <= AccessLevel::FilePrivate; - tracker->addUsedMember({Super, Identifier()}, !isPrivate); - } - } - - bool isInvalidSuperclass = false; - - if (Super->isFinal()) { - TC.diagnose(CD, diag::inheritance_from_final_class, - Super->getName()); - // FIXME: should this really be skipping the rest of decl-checking? - return; - } - - if (Super->hasClangNode() && Super->getGenericParams() - && superclassTy->hasTypeParameter()) { - TC.diagnose(CD, - diag::inheritance_from_unspecialized_objc_generic_class, - Super->getName()); - } - - switch (Super->getForeignClassKind()) { - case ClassDecl::ForeignKind::Normal: - break; - case ClassDecl::ForeignKind::CFType: - TC.diagnose(CD, diag::inheritance_from_cf_class, - Super->getName()); - isInvalidSuperclass = true; - break; - case ClassDecl::ForeignKind::RuntimeOnly: - TC.diagnose(CD, diag::inheritance_from_objc_runtime_visible_class, - Super->getName()); - isInvalidSuperclass = true; - break; - } - - if (!isInvalidSuperclass && Super->hasMissingVTableEntries() && - !Super->isResilient(CD->getParentModule(), - ResilienceExpansion::Minimal)) { - auto *superFile = Super->getModuleScopeContext(); - if (auto *serialized = dyn_cast(superFile)) { - if (serialized->getLanguageVersionBuiltWith() != - TC.getLangOpts().EffectiveLanguageVersion) { - TC.diagnose(CD, - diag::inheritance_from_class_with_missing_vtable_entries_versioned, - Super->getName(), - serialized->getLanguageVersionBuiltWith(), - TC.getLangOpts().EffectiveLanguageVersion); - isInvalidSuperclass = true; - } - } - if (!isInvalidSuperclass) { - TC.diagnose( - CD, diag::inheritance_from_class_with_missing_vtable_entries, - Super->getName()); - isInvalidSuperclass = true; - } - } - - if (!TC.Context.isAccessControlDisabled()) { - // Require the superclass to be open if this is outside its - // defining module. But don't emit another diagnostic if we - // already complained about the class being inherently - // un-subclassable. - if (!isInvalidSuperclass && - !Super->hasOpenAccess(CD->getDeclContext()) && - Super->getModuleContext() != CD->getModuleContext()) { - TC.diagnose(CD, diag::superclass_not_open, superclassTy); - isInvalidSuperclass = true; - } - - // Require superclasses to be open if the subclass is open. - // This is a restriction we can consider lifting in the future, - // e.g. to enable a "sealed" superclass whose subclasses are all - // of one of several alternatives. - if (!isInvalidSuperclass && - CD->getFormalAccess() == AccessLevel::Open && - Super->getFormalAccess() != AccessLevel::Open) { - TC.diagnose(CD, diag::superclass_of_open_not_open, superclassTy); - TC.diagnose(Super, diag::superclass_here); - } - } - } - - TC.checkDeclAttributes(CD); - - checkInheritanceClause(CD); - - checkAccessControl(TC, CD); - - checkExplicitAvailability(CD); - - TC.checkDeclCircularity(CD); - TC.ConformanceContexts.push_back(CD); - } - - void visitProtocolDecl(ProtocolDecl *PD) { - checkUnsupportedNestedType(PD); - - auto *SF = PD->getParentSourceFile(); - { - // Check for circular inheritance within the protocol. - SmallVector path; - path.push_back(PD); - checkCircularity(TC, PD, diag::circular_protocol_def, - DescriptiveDeclKind::Protocol, path); - - if (SF) { - if (auto *tracker = SF->getReferencedNameTracker()) { - bool isNonPrivate = - (PD->getFormalAccess() > AccessLevel::FilePrivate); - for (auto *parentProto : PD->getInheritedProtocols()) - tracker->addUsedMember({parentProto, Identifier()}, isNonPrivate); - } - } - } - - // Check the members. - for (auto Member : PD->getMembers()) - visit(Member); - - TC.checkDeclAttributes(PD); - - checkAccessControl(TC, PD); - - checkInheritanceClause(PD); - - TC.checkDeclCircularity(PD); - if (PD->isResilient()) - if (!SF || SF->Kind != SourceFileKind::Interface) - TC.inferDefaultWitnesses(PD); - - if (TC.Context.LangOpts.DebugGenericSignatures) { - auto requirementsSig = - GenericSignature::get({PD->getProtocolSelfType()}, - PD->getRequirementSignature()); - - llvm::errs() << "Protocol requirement signature:\n"; - PD->dumpRef(llvm::errs()); - llvm::errs() << "\n"; - llvm::errs() << "Requirement signature: "; - requirementsSig->print(llvm::errs()); - llvm::errs() << "\n"; - - // Note: One cannot canonicalize a requirement signature, because - // requirement signatures are necessarily missing requirements. - llvm::errs() << "Canonical requirement signature: "; - auto canRequirementSig = - CanGenericSignature::getCanonical(requirementsSig->getGenericParams(), - requirementsSig->getRequirements(), - /*skipValidation=*/true); - canRequirementSig->print(llvm::errs()); - llvm::errs() << "\n"; - } - - // Explicitly calculate this bit. - (void) PD->existentialTypeSupported(); - - // Explicity compute the requirement signature to detect errors. - (void) PD->getRequirementSignature(); - - checkExplicitAvailability(PD); - } - - void visitVarDecl(VarDecl *VD) { - // Delay type-checking on VarDecls until we see the corresponding - // PatternBindingDecl. - } - - /// Determine whether the given declaration requires a definition. - /// - /// Only valid for declarations that can have definitions, i.e., - /// functions, initializers, etc. - static bool requiresDefinition(Decl *decl) { - // Invalid, implicit, and Clang-imported declarations never - // require a definition. - if (decl->isInvalid() || decl->isImplicit() || decl->hasClangNode()) - return false; - - // Protocol requirements do not require definitions. - if (isa(decl->getDeclContext())) - return false; - - // Functions can have _silgen_name, semantics, and NSManaged attributes. - if (auto func = dyn_cast(decl)) { - if (func->getAttrs().hasAttribute() || - func->getAttrs().hasAttribute() || - func->getAttrs().hasAttribute()) - return false; - } - - // Declarations in SIL and module interface files don't require - // definitions. - if (auto sourceFile = decl->getDeclContext()->getParentSourceFile()) { - switch (sourceFile->Kind) { - case SourceFileKind::SIL: - case SourceFileKind::Interface: - return false; - case SourceFileKind::Library: - case SourceFileKind::Main: - case SourceFileKind::REPL: - break; - } - } - - // Everything else requires a definition. - return true; - } - - - bool shouldSkipBodyTypechecking(const AbstractFunctionDecl *AFD) { - // Make sure we're in the mode that's skipping function bodies. - if (!TC.canSkipNonInlinableBodies()) - return false; - - // Make sure there even _is_ a body that we can skip. - if (!AFD->getBodySourceRange().isValid()) - return false; - - // If we're gonna serialize the body, we can't skip it. - if (AFD->getResilienceExpansion() == ResilienceExpansion::Minimal) - return false; - - return true; - } - - void visitFuncDecl(FuncDecl *FD) { - // Force these requests in case they emit diagnostics. - (void) FD->getInterfaceType(); - (void) FD->getOperatorDecl(); - - if (!FD->isInvalid()) { - checkGenericParams(FD->getGenericParams(), FD, TC); - TC.checkReferencedGenericParams(FD); - TC.checkProtocolSelfRequirements(FD); - } - - checkAccessControl(TC, FD); - - TC.checkParameterAttributes(FD->getParameters()); - TC.checkDeclAttributes(FD); - - if (!checkOverrides(FD)) { - // If a method has an 'override' keyword but does not - // override anything, complain. - if (auto *OA = FD->getAttrs().getAttribute()) { - if (!FD->getOverriddenDecl()) { - TC.diagnose(FD, diag::method_does_not_override) - .highlight(OA->getLocation()); - OA->setInvalid(); - } - } - } - - if (requiresDefinition(FD) && !FD->hasBody()) { - // Complain if we should have a body. - TC.diagnose(FD->getLoc(), diag::func_decl_without_brace); - } else if (FD->getDeclContext()->isLocalContext()) { - // Check local function bodies right away. - TC.typeCheckAbstractFunctionBody(FD); - } else if (shouldSkipBodyTypechecking(FD)) { - FD->setBodySkipped(FD->getBodySourceRange()); - } else { - // Record the body. - TC.definedFunctions.push_back(FD); - } - - checkExplicitAvailability(FD); - - if (FD->getDeclContext()->getSelfClassDecl()) - checkDynamicSelfType(FD, FD->getResultInterfaceType()); - - checkDefaultArguments(TC, FD->getParameters(), FD); - - // Validate 'static'/'class' on functions in extensions. - auto StaticSpelling = FD->getStaticSpelling(); - if (StaticSpelling != StaticSpellingKind::None && - isa(FD->getDeclContext())) { - if (auto *NTD = FD->getDeclContext()->getSelfNominalTypeDecl()) { - if (!isa(NTD)) { - if (StaticSpelling == StaticSpellingKind::KeywordClass) { - FD->diagnose(diag::class_func_not_in_class, false) - .fixItReplace(FD->getStaticLoc(), "static"); - NTD->diagnose(diag::extended_type_declared_here); - } - } - } - } - - // Member functions need some special validation logic. - if (FD->getDeclContext()->isTypeContext()) { - if (FD->isOperator() && !isMemberOperator(FD, nullptr)) { - auto selfNominal = FD->getDeclContext()->getSelfNominalTypeDecl(); - auto isProtocol = selfNominal && isa(selfNominal); - // We did not find 'Self'. Complain. - FD->diagnose(diag::operator_in_unrelated_type, - FD->getDeclContext()->getDeclaredInterfaceType(), isProtocol, - FD->getFullName()); - } - } - - // If the function is exported to C, it must be representable in (Obj-)C. - // FIXME: This needs to be moved to its own request if we want to - // productize @_cdecl. - if (auto CDeclAttr = FD->getAttrs().getAttribute()) { - Optional errorConvention; - if (isRepresentableInObjC(FD, ObjCReason::ExplicitlyCDecl, - errorConvention)) { - if (FD->hasThrows()) { - FD->setForeignErrorConvention(*errorConvention); - TC.diagnose(CDeclAttr->getLocation(), diag::cdecl_throws); - } - } - } - } - - void visitModuleDecl(ModuleDecl *) { } - - void visitEnumCaseDecl(EnumCaseDecl *ECD) { - // The type-checker doesn't care about how these are grouped. - } - - void visitEnumElementDecl(EnumElementDecl *EED) { - (void) EED->getInterfaceType(); - auto *ED = EED->getParentEnum(); - - TC.checkDeclAttributes(EED); - - if (auto *PL = EED->getParameterList()) { - TC.checkParameterAttributes(PL); - checkDefaultArguments(TC, PL, EED); - } - - // We don't yet support raw values on payload cases. - if (EED->hasAssociatedValues()) { - if (auto rawTy = ED->getRawType()) { - TC.diagnose(EED->getLoc(), - diag::enum_with_raw_type_case_with_argument); - TC.diagnose(ED->getInherited().front().getSourceRange().Start, - diag::enum_raw_type_here, rawTy); - EED->setInvalid(); - } - } - - // Force the raw value expr then yell if our parent doesn't have a raw type. - Expr *RVE = EED->getRawValueExpr(); - if (RVE && !ED->hasRawType()) { - TC.diagnose(RVE->getLoc(), diag::enum_raw_value_without_raw_type); - EED->setInvalid(); - } - - checkAccessControl(TC, EED); - } - - void visitExtensionDecl(ExtensionDecl *ED) { - // Produce any diagnostics for the extended type. - auto extType = ED->getExtendedType(); - - auto nominal = ED->computeExtendedNominal(); - if (nominal == nullptr) { - const bool wasAlreadyInvalid = ED->isInvalid(); - ED->setInvalid(); - if (extType && !extType->hasError() && extType->getAnyNominal()) { - // If we've got here, then we have some kind of extension of a prima - // fascie non-nominal type. This can come up when we're projecting - // typealiases out of bound generic types. - // - // struct Array { typealias Indices = Range } - // extension Array.Indices.Bound {} - // - // Offer to rewrite it to the underlying nominal type. - auto canExtType = extType->getCanonicalType(); - ED->diagnose(diag::invalid_nominal_extension, extType, canExtType) - .highlight(ED->getExtendedTypeRepr()->getSourceRange()); - ED->diagnose(diag::invalid_nominal_extension_rewrite, canExtType) - .fixItReplace(ED->getExtendedTypeRepr()->getSourceRange(), - canExtType->getString()); - } else if (!wasAlreadyInvalid) { - // If nothing else applies, fall back to a generic diagnostic. - ED->diagnose(diag::non_nominal_extension, extType); - } - return; - } - - // Produce any diagnostics for the generic signature. - (void) ED->getGenericSignature(); - - if (extType && !extType->hasError()) { - // The first condition catches syntactic forms like - // protocol A & B { ... } // may be protocols or typealiases - // The second condition also looks through typealiases and catches - // typealias T = P1 & P2 // P2 is a refined protocol of P1 - // extension T { ... } - // However, it is trickier to catch cases like - // typealias T = P2 & P1 // P2 is a refined protocol of P1 - // extension T { ... } - // so we don't do that here. - auto extTypeRepr = ED->getExtendedTypeRepr(); - auto *extTypeNominal = extType->getAnyNominal(); - bool firstNominalIsNotMostSpecific = - extTypeNominal && extTypeNominal != nominal; - if (isa(extTypeRepr) - || firstNominalIsNotMostSpecific) { - auto firstNominalType = nominal->getDeclaredType(); - auto diag = ED->diagnose(diag::composition_in_extended_type, - firstNominalType); - diag.highlight(extTypeRepr->getSourceRange()); - if (firstNominalIsNotMostSpecific) { - diag.flush(); - Type mostSpecificProtocol = extTypeNominal->getDeclaredType(); - ED->diagnose(diag::composition_in_extended_type_alternative, - mostSpecificProtocol) - .fixItReplace(extTypeRepr->getSourceRange(), - mostSpecificProtocol->getString()); - } else { - diag.fixItReplace(extTypeRepr->getSourceRange(), - firstNominalType->getString()); - } - } - } - - checkInheritanceClause(ED); - - // Only generic and protocol types are permitted to have - // trailing where clauses. - if (auto trailingWhereClause = ED->getTrailingWhereClause()) { - if (!ED->getGenericParams() && !ED->isInvalid()) { - ED->diagnose(diag::extension_nongeneric_trailing_where, - nominal->getFullName()) - .highlight(trailingWhereClause->getSourceRange()); - } - } - - checkGenericParams(ED->getGenericParams(), ED, TC); - - for (Decl *Member : ED->getMembers()) - visit(Member); - - TC.ConformanceContexts.push_back(ED); - - TC.checkDeclAttributes(ED); - checkAccessControl(TC, ED); - - checkExplicitAvailability(ED); - } - - void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { - // See swift::performTypeChecking for TopLevelCodeDecl handling. - llvm_unreachable("TopLevelCodeDecls are handled elsewhere"); - } - - void visitIfConfigDecl(IfConfigDecl *ICD) { - // The active members of the #if block will be type checked along with - // their enclosing declaration. - TC.checkDeclAttributes(ICD); - } - - void visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) { - if (PDD->hasBeenEmitted()) { return; } - PDD->markEmitted(); - TC.diagnose(PDD->getMessage()->getStartLoc(), - PDD->isError() ? diag::pound_error : diag::pound_warning, - PDD->getMessage()->getValue()) - .highlight(PDD->getMessage()->getSourceRange()); - } - - void visitConstructorDecl(ConstructorDecl *CD) { - (void) CD->getInterfaceType(); - - // Compute these requests in case they emit diagnostics. - (void) CD->getInitKind(); - - if (!CD->isInvalid()) { - checkGenericParams(CD->getGenericParams(), CD, TC); - TC.checkReferencedGenericParams(CD); - TC.checkProtocolSelfRequirements(CD); - } - - TC.checkDeclAttributes(CD); - TC.checkParameterAttributes(CD->getParameters()); - - // Check whether this initializer overrides an initializer in its - // superclass. - if (!checkOverrides(CD)) { - // If an initializer has an override attribute but does not override - // anything or overrides something that doesn't need an 'override' - // keyword (e.g., a convenience initializer), complain. - // anything, or overrides something that complain. - if (auto *attr = CD->getAttrs().getAttribute()) { - if (!CD->getOverriddenDecl()) { - TC.diagnose(CD, diag::initializer_does_not_override) - .highlight(attr->getLocation()); - attr->setInvalid(); - } else if (attr->isImplicit()) { - // Don't diagnose implicit attributes. - } else if (overrideRequiresKeyword(CD->getOverriddenDecl()) - == OverrideRequiresKeyword::Never) { - // Special case: we are overriding a 'required' initializer, so we - // need (only) the 'required' keyword. - if (cast(CD->getOverriddenDecl())->isRequired()) { - if (CD->getAttrs().hasAttribute()) { - TC.diagnose(CD, diag::required_initializer_override_keyword) - .fixItRemove(attr->getLocation()); - } else { - TC.diagnose(CD, diag::required_initializer_override_wrong_keyword) - .fixItReplace(attr->getLocation(), "required"); - CD->getAttrs().add( - new (TC.Context) RequiredAttr(/*IsImplicit=*/true)); - } - - TC.diagnose(findNonImplicitRequiredInit(CD->getOverriddenDecl()), - diag::overridden_required_initializer_here); - } else { - // We tried to override a convenience initializer. - TC.diagnose(CD, diag::initializer_does_not_override) - .highlight(attr->getLocation()); - TC.diagnose(CD->getOverriddenDecl(), - diag::convenience_init_override_here); - } - } - } - - // A failable initializer cannot override a non-failable one. - // This would normally be diagnosed by the covariance rules; - // however, those are disabled so that we can provide a more - // specific diagnostic here. - if (CD->isFailable() && - CD->getOverriddenDecl() && - !CD->getOverriddenDecl()->isFailable()) { - TC.diagnose(CD, diag::failable_initializer_override, - CD->getFullName()); - TC.diagnose(CD->getOverriddenDecl(), - diag::nonfailable_initializer_override_here, - CD->getOverriddenDecl()->getFullName()); - } - } - - // If this initializer overrides a 'required' initializer, it must itself - // be marked 'required'. - if (!CD->getAttrs().hasAttribute()) { - if (CD->getOverriddenDecl() && CD->getOverriddenDecl()->isRequired()) { - TC.diagnose(CD, diag::required_initializer_missing_keyword) - .fixItInsert(CD->getLoc(), "required "); - - TC.diagnose(findNonImplicitRequiredInit(CD->getOverriddenDecl()), - diag::overridden_required_initializer_here); - - CD->getAttrs().add( - new (TC.Context) RequiredAttr(/*IsImplicit=*/true)); - } - } - - if (CD->isRequired()) { - if (auto nominal = CD->getDeclContext()->getSelfNominalTypeDecl()) { - AccessLevel requiredAccess; - switch (nominal->getFormalAccess()) { - case AccessLevel::Open: - requiredAccess = AccessLevel::Public; - break; - case AccessLevel::Public: - case AccessLevel::Internal: - requiredAccess = AccessLevel::Internal; - break; - case AccessLevel::FilePrivate: - case AccessLevel::Private: - requiredAccess = AccessLevel::FilePrivate; - break; - } - if (CD->getFormalAccess() < requiredAccess) { - auto diag = TC.diagnose(CD, diag::required_initializer_not_accessible, - nominal->getFullName()); - fixItAccess(diag, CD, requiredAccess); - } - } - } - - checkAccessControl(TC, CD); - - if (requiresDefinition(CD) && !CD->hasBody()) { - // Complain if we should have a body. - TC.diagnose(CD->getLoc(), diag::missing_initializer_def); - } else if (CD->getDeclContext()->isLocalContext()) { - // Check local function bodies right away. - TC.typeCheckAbstractFunctionBody(CD); - } else if (shouldSkipBodyTypechecking(CD)) { - CD->setBodySkipped(CD->getBodySourceRange()); - } else { - TC.definedFunctions.push_back(CD); - } - - checkDefaultArguments(TC, CD->getParameters(), CD); - } - - void visitDestructorDecl(DestructorDecl *DD) { - TC.checkDeclAttributes(DD); - - if (DD->getDeclContext()->isLocalContext()) { - // Check local function bodies right away. - TC.typeCheckAbstractFunctionBody(DD); - } else if (shouldSkipBodyTypechecking(DD)) { - DD->setBodySkipped(DD->getBodySourceRange()); - } else { - TC.definedFunctions.push_back(DD); - } - } -}; -} // end anonymous namespace - -bool TypeChecker::isAvailabilitySafeForConformance( - ProtocolDecl *proto, ValueDecl *requirement, ValueDecl *witness, - DeclContext *dc, AvailabilityContext &requirementInfo) { +bool TypeChecker::isAvailabilitySafeForConformance( + ProtocolDecl *proto, ValueDecl *requirement, ValueDecl *witness, + DeclContext *dc, AvailabilityContext &requirementInfo) { // We assume conformances in // non-SourceFiles have already been checked for availability. if (!dc->getParentSourceFile()) return true; + auto &Context = proto->getASTContext(); NominalTypeDecl *conformingDecl = dc->getSelfNominalTypeDecl(); assert(conformingDecl && "Must have conforming declaration"); @@ -3600,11 +1548,6 @@ bool TypeChecker::isAvailabilitySafeForConformance( return requirementInfo.isContainedIn(witnessInfo); } -void TypeChecker::typeCheckDecl(Decl *D) { - checkForForbiddenPrefix(D); - DeclChecker(*this).visit(D); -} - // Returns 'nullptr' if this is the setter's 'newValue' parameter; // otherwise, returns the corresponding parameter of the subscript // declaration. @@ -3666,7 +1609,7 @@ IsImplicitlyUnwrappedOptionalRequest::evaluate(Evaluator &evaluator, if (auto *subscript = dyn_cast(storage)) TyR = subscript->getElementTypeLoc().getTypeRepr(); else - TyR = cast(storage)->getTypeRepr(); + TyR = cast(storage)->getTypeReprOrParentPatternTypeRepr(); break; } @@ -3708,7 +1651,7 @@ IsImplicitlyUnwrappedOptionalRequest::evaluate(Evaluator &evaluator, } case DeclKind::Var: - // FIXME: See the comment in validateTypedPattern(). + TyR = cast(decl)->getTypeReprOrParentPatternTypeRepr(); break; default: @@ -3749,7 +1692,8 @@ UnderlyingTypeRequest::evaluate(Evaluator &evaluator, return underlyingLoc.getType(); } -/// Bind the given function declaration, which declares an operator, to the corresponding operator declaration. +/// Bind the given function declaration, which declares an operator, to the +/// corresponding operator declaration. llvm::Expected FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { auto &C = FD->getASTContext(); @@ -3905,11 +1849,8 @@ bool swift::isMemberOperator(FuncDecl *decl, Type type) { // Check the parameters for a reference to 'Self'. bool isProtocol = selfNominal && isa(selfNominal); for (auto param : *decl->getParameters()) { - auto paramType = param->getInterfaceType(); - if (!paramType) break; - // Look through a metatype reference, if there is one. - paramType = paramType->getMetatypeInstanceType(); + auto paramType = param->getInterfaceType()->getMetatypeInstanceType(); auto nominal = paramType->getAnyNominal(); if (type.isNull()) { @@ -4053,7 +1994,7 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator, if (isa(nestedRepr) && param->isDefaultArgument()) { auto &ctx = param->getASTContext(); - ctx.Diags.diagnose(param->getDefaultValue()->getLoc(), + ctx.Diags.diagnose(param->getStructuralDefaultExpr()->getLoc(), swift::diag::cannot_provide_default_value_inout, param->getName()); return ParamSpecifier::Default; @@ -4124,20 +2065,11 @@ static Type validateParameterType(ParamDecl *decl) { return TL.getType(); } -void TypeChecker::validateDecl(ValueDecl *D) { - // Handling validation failure due to re-entrancy is left - // up to the caller, who must call hasInterfaceType() to - // check that validateDecl() returned a fully-formed decl. - if (D->isBeingValidated() || D->hasInterfaceType()) - return; - - PrettyStackTraceDecl StackTrace("validating", D); - FrontendStatsTracer StatsTracer(Context.Stats, "validate-decl", D); - - checkForForbiddenPrefix(D); +llvm::Expected +InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { + auto &Context = D->getASTContext(); - if (Context.Stats) - Context.Stats->getFrontendCounters().NumDeclsValidated++; + TypeChecker::checkForForbiddenPrefix(Context, D->getBaseName()); switch (D->getKind()) { case DeclKind::Import: @@ -4156,13 +2088,12 @@ void TypeChecker::validateDecl(ValueDecl *D) { case DeclKind::OpaqueType: case DeclKind::GenericTypeParam: llvm_unreachable("should not get here"); - return; + return Type(); case DeclKind::AssociatedType: { auto assocType = cast(D); auto interfaceTy = assocType->getDeclaredInterfaceType(); - assocType->setInterfaceType(MetatypeType::get(interfaceTy, Context)); - break; + return MetatypeType::get(interfaceTy, Context); } case DeclKind::TypeAlias: { @@ -4179,8 +2110,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { parent = parentDC->getSelfInterfaceType(); auto sugaredType = TypeAliasType::get(typeAlias, parent, subs, typeAlias->getUnderlyingType()); - typeAlias->setInterfaceType(MetatypeType::get(sugaredType, Context)); - break; + return MetatypeType::get(sugaredType, Context); } case DeclKind::Enum: @@ -4189,22 +2119,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { case DeclKind::Protocol: { auto nominal = cast(D); Type declaredInterfaceTy = nominal->getDeclaredInterfaceType(); - nominal->setInterfaceType(MetatypeType::get(declaredInterfaceTy, Context)); - - if (auto *ED = dyn_cast(nominal)) { - // @objc enums use their raw values as the value representation, so we - // need to force the values to be checked even in non-primaries. - // - // FIXME: This check can be removed once IRGen can be made tolerant of - // semantic failures post-Sema. - if (ED->isObjC()) { - (void)evaluateOrDefault( - Context.evaluator, - EnumRawValuesRequest{ED, TypeResolutionStage::Interface}, true); - } - } - - break; + return MetatypeType::get(declaredInterfaceTy, Context); } case DeclKind::Param: { @@ -4214,8 +2129,7 @@ void TypeChecker::validateDecl(ValueDecl *D) { auto selfParam = computeSelfParam(AFD, /*isInitializingCtor*/true, /*wantDynamicSelf*/true); - PD->setInterfaceType(selfParam.getPlainType()); - break; + return selfParam.getPlainType(); } if (auto *accessor = dyn_cast(PD->getDeclContext())) { @@ -4223,53 +2137,39 @@ void TypeChecker::validateDecl(ValueDecl *D) { auto *originalParam = getOriginalParamFromAccessor( storage, accessor, PD); if (originalParam == nullptr) { - auto type = storage->getValueInterfaceType(); - PD->setInterfaceType(type); - break; + return storage->getValueInterfaceType(); } if (originalParam != PD) { - PD->setInterfaceType(originalParam->getInterfaceType()); - break; + return originalParam->getInterfaceType(); } } if (!PD->getTypeRepr()) - return; + return Type(); - auto ty = validateParameterType(PD); - PD->setInterfaceType(ty); - break; + return validateParameterType(PD); } case DeclKind::Var: { auto *VD = cast(D); - auto *PBD = VD->getParentPatternBinding(); - - // Note that we need to handle the fact that some VarDecls don't - // have a PatternBindingDecl, for example the iterator in a - // 'for ... in ...' loop. - if (PBD == nullptr) { - VD->markInvalid(); - break; + auto *namingPattern = VD->getNamingPattern(); + if (!namingPattern) { + return ErrorType::get(Context); } - // If we're already checking our PatternBindingDecl, bail out - // without setting our own 'is being validated' flag, since we - // will attempt validation again later. - if (PBD->isBeingValidated()) - return; - - // Attempt to infer the type using initializer expressions. - validatePatternBindingEntries(*this, PBD); + Type interfaceType = namingPattern->getType(); + if (interfaceType->hasArchetype()) + interfaceType = interfaceType->mapTypeOutOfContext(); - auto parentPattern = VD->getParentPattern(); - if (PBD->isInvalid() || !parentPattern->hasType()) { - parentPattern->setType(ErrorType::get(Context)); - setBoundVarsTypeError(parentPattern, Context); + // In SIL mode, VarDecls are written as having reference storage types. + if (!interfaceType->is()) { + if (auto *attr = VD->getAttrs().getAttribute()) + interfaceType = + TypeChecker::checkReferenceOwnershipAttr(VD, interfaceType, attr); } - break; + return interfaceType; } case DeclKind::Func: @@ -4277,14 +2177,58 @@ void TypeChecker::validateDecl(ValueDecl *D) { case DeclKind::Constructor: case DeclKind::Destructor: { auto *AFD = cast(D); - DeclValidationRAII IBV(AFD); - AFD->computeType(); - break; + + auto sig = AFD->getGenericSignature(); + bool hasSelf = AFD->hasImplicitSelfDecl(); + + AnyFunctionType::ExtInfo info; + + // Result + Type resultTy; + if (auto fn = dyn_cast(D)) { + resultTy = fn->getResultInterfaceType(); + } else if (auto ctor = dyn_cast(D)) { + resultTy = ctor->getResultInterfaceType(); + } else { + assert(isa(D)); + resultTy = TupleType::getEmpty(AFD->getASTContext()); + } + + // (Args...) -> Result + Type funcTy; + + { + SmallVector argTy; + AFD->getParameters()->getParams(argTy); + + // 'throws' only applies to the innermost function. + info = info.withThrows(AFD->hasThrows()); + // Defer bodies must not escape. + if (auto fd = dyn_cast(D)) + info = info.withNoEscape(fd->isDeferBody()); + + if (sig && !hasSelf) { + funcTy = GenericFunctionType::get(sig, argTy, resultTy, info); + } else { + funcTy = FunctionType::get(argTy, resultTy, info); + } + } + + // (Self) -> (Args...) -> Result + if (hasSelf) { + // Substitute in our own 'self' parameter. + auto selfParam = computeSelfParam(AFD); + if (sig) + funcTy = GenericFunctionType::get(sig, {selfParam}, funcTy); + else + funcTy = FunctionType::get({selfParam}, funcTy); + } + + return funcTy; } case DeclKind::Subscript: { auto *SD = cast(D); - DeclValidationRAII IBV(SD); auto elementTy = SD->getElementInterfaceType(); @@ -4297,14 +2241,11 @@ void TypeChecker::validateDecl(ValueDecl *D) { else funcTy = FunctionType::get(argTy, elementTy); - // Record the interface type. - SD->setInterfaceType(funcTy); - break; + return funcTy; } case DeclKind::EnumElement: { auto *EED = cast(D); - DeclValidationRAII IBV(EED); auto *ED = EED->getParentEnum(); @@ -4326,13 +2267,67 @@ void TypeChecker::validateDecl(ValueDecl *D) { else resultTy = FunctionType::get({selfTy}, resultTy); - // Record the interface type. - EED->setInterfaceType(resultTy); - break; + return resultTy; + } + } +} + +llvm::Expected +NamingPatternRequest::evaluate(Evaluator &evaluator, VarDecl *VD) const { + auto &Context = VD->getASTContext(); + auto *PBD = VD->getParentPatternBinding(); + // FIXME: In order for this request to properly express its dependencies, + // all of the places that allow variable bindings need to also use pattern + // binding decls. Otherwise, we'll have to go digging around in case + // statements and patterns to find named patterns. + if (PBD) { + // FIXME: For now, this works because PatternBindingEntryRequest fills in + // the naming pattern as a side effect in this case, and TypeCheckStmt + // and TypeCheckPattern handle the others. But that's all really gross. + unsigned i = PBD->getPatternEntryIndexForVarDecl(VD); + (void)evaluateOrDefault(evaluator, + PatternBindingEntryRequest{PBD, i}, + nullptr); + if (PBD->isInvalid()) { + VD->getParentPattern()->setType(ErrorType::get(Context)); + setBoundVarsTypeError(VD->getParentPattern(), Context); + return nullptr; + } + } else if (!VD->getParentPatternStmt() && !VD->getParentVarDecl()) { + // No parent? That's an error. + return nullptr; + } + + // Go digging for the named pattern that declares this variable. + auto *namingPattern = VD->NamingPattern; + if (!namingPattern) { + auto *canVD = VD->getCanonicalVarDecl(); + namingPattern = canVD->NamingPattern; + + // HACK: If no other diagnostic applies, emit a generic diagnostic about + // a variable being unbound. We can't do better than this at the + // moment because TypeCheckPattern does not reliably invalidate parts of + // the pattern AST on failure. + // + // Once that's through, this will only fire during circular validation. + if (!namingPattern) { + if (VD->hasInterfaceType() && + !VD->isInvalid() && !VD->getParentPattern()->isImplicit()) { + VD->diagnose(diag::variable_bound_by_no_pattern, VD->getName()); + } + + VD->getParentPattern()->setType(ErrorType::get(Context)); + setBoundVarsTypeError(VD->getParentPattern(), Context); + return nullptr; + } } + + if (!namingPattern->hasType()) { + namingPattern->setType(ErrorType::get(Context)); + setBoundVarsTypeError(namingPattern, Context); } - assert(D->hasInterfaceType()); + return namingPattern; } llvm::Expected @@ -4343,23 +2338,23 @@ EmittedMembersRequest::evaluate(Evaluator &evaluator, auto &Context = CD->getASTContext(); - // FIXME: Remove TypeChecker dependencies below - auto &TC = *(TypeChecker *) Context.getLazyResolver(); - // We need to add implicit initializers because they // affect vtable layout. - TC.addImplicitConstructors(CD); + TypeChecker::addImplicitConstructors(CD); auto forceConformance = [&](ProtocolDecl *protocol) { - if (auto ref = TypeChecker::conformsToProtocol( - CD->getDeclaredInterfaceType(), protocol, CD, - ConformanceCheckFlags::SkipConditionalRequirements, - SourceLoc())) { - auto conformance = ref->getConcrete(); - if (conformance->getDeclContext() == CD && - conformance->getState() == ProtocolConformanceState::Incomplete) { - TC.checkConformance(conformance->getRootNormalConformance()); - } + auto ref = TypeChecker::conformsToProtocol( + CD->getDeclaredInterfaceType(), protocol, CD, + ConformanceCheckFlags::SkipConditionalRequirements, SourceLoc()); + + if (ref.isInvalid()) { + return; + } + + auto conformance = ref.getConcrete(); + if (conformance->getDeclContext() == CD && + conformance->getState() == ProtocolConformanceState::Incomplete) { + TypeChecker::checkConformance(conformance->getRootNormalConformance()); } }; @@ -4509,269 +2504,3 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return extendedType; } - -/// Build a default initializer string for the given pattern. -/// -/// This string is suitable for display in diagnostics. -static Optional buildDefaultInitializerString(TypeChecker &tc, - DeclContext *dc, - Pattern *pattern) { - switch (pattern->getKind()) { -#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id: -#define PATTERN(Id, Parent) -#include "swift/AST/PatternNodes.def" - return None; - case PatternKind::Any: - return None; - - case PatternKind::Named: { - if (!pattern->hasType()) - return None; - - // Special-case the various types we might see here. - auto type = pattern->getType(); - - // For literal-convertible types, form the corresponding literal. -#define CHECK_LITERAL_PROTOCOL(Kind, String) \ - if (auto proto = tc.getProtocol(SourceLoc(), KnownProtocolKind::Kind)) { \ - if (tc.conformsToProtocol(type, proto, dc, \ - ConformanceCheckFlags::InExpression)) \ - return std::string(String); \ - } - CHECK_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "[]") - CHECK_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "[:]") - CHECK_LITERAL_PROTOCOL(ExpressibleByUnicodeScalarLiteral, "\"\"") - CHECK_LITERAL_PROTOCOL(ExpressibleByExtendedGraphemeClusterLiteral, "\"\"") - CHECK_LITERAL_PROTOCOL(ExpressibleByFloatLiteral, "0.0") - CHECK_LITERAL_PROTOCOL(ExpressibleByIntegerLiteral, "0") - CHECK_LITERAL_PROTOCOL(ExpressibleByStringLiteral, "\"\"") -#undef CHECK_LITERAL_PROTOCOL - - // For optional types, use 'nil'. - if (type->getOptionalObjectType()) - return std::string("nil"); - - return None; - } - - case PatternKind::Paren: { - if (auto sub = buildDefaultInitializerString( - tc, dc, cast(pattern)->getSubPattern())) { - return "(" + *sub + ")"; - } - - return None; - } - - case PatternKind::Tuple: { - std::string result = "("; - bool first = true; - for (auto elt : cast(pattern)->getElements()) { - if (auto sub = buildDefaultInitializerString(tc, dc, elt.getPattern())) { - if (first) { - first = false; - } else { - result += ", "; - } - - result += *sub; - } else { - return None; - } - } - result += ")"; - return result; - } - - case PatternKind::Typed: - return buildDefaultInitializerString( - tc, dc, cast(pattern)->getSubPattern()); - - case PatternKind::Var: - return buildDefaultInitializerString( - tc, dc, cast(pattern)->getSubPattern()); - } - - llvm_unreachable("Unhandled PatternKind in switch."); -} - -/// Diagnose a class that does not have any initializers. -static void diagnoseClassWithoutInitializers(TypeChecker &tc, - ClassDecl *classDecl) { - tc.diagnose(classDecl, diag::class_without_init, - classDecl->getDeclaredType()); - - // HACK: We've got a special case to look out for and diagnose specifically to - // improve the experience of seeing this, and mitigate some confusion. - // - // For a class A which inherits from Decodable class B, class A may have - // additional members which prevent default initializer synthesis (and - // inheritance of other initializers). The user may have assumed that this - // case would synthesize Encodable/Decodable conformance for class A the same - // way it may have for class B, or other classes. - // - // It is helpful to suggest here that the user may have forgotten to override - // init(from:) (and encode(to:), if applicable) in a note, before we start - // listing the members that prevented initializer synthesis. - // TODO: Add a fixit along with this suggestion. - if (auto *superclassDecl = classDecl->getSuperclassDecl()) { - ASTContext &C = tc.Context; - auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable); - auto superclassType = superclassDecl->getDeclaredInterfaceType(); - if (auto ref = TypeChecker::conformsToProtocol(superclassType, decodableProto, - superclassDecl, - ConformanceCheckOptions(), - SourceLoc())) { - // super conforms to Decodable, so we've failed to inherit init(from:). - // Let's suggest overriding it here. - // - // We're going to diagnose on the concrete init(from:) decl if it exists - // and isn't implicit; otherwise, on the subclass itself. - ValueDecl *diagDest = classDecl; - auto initFrom = DeclName(C, DeclBaseName::createConstructor(), C.Id_from); - auto result = tc.lookupMember(superclassDecl, superclassType, initFrom, - NameLookupFlags::ProtocolMembers | - NameLookupFlags::IgnoreAccessControl); - - if (!result.empty() && !result.front().getValueDecl()->isImplicit()) - diagDest = result.front().getValueDecl(); - - auto diagName = diag::decodable_suggest_overriding_init_here; - - // This is also a bit of a hack, but the best place we've got at the - // moment to suggest this. - // - // If the superclass also conforms to Encodable, it's quite - // likely that the user forgot to override its encode(to:). In this case, - // we can produce a slightly different diagnostic to suggest doing so. - auto *encodableProto = C.getProtocol(KnownProtocolKind::Encodable); - if ((ref = tc.conformsToProtocol(superclassType, encodableProto, - superclassDecl, - ConformanceCheckOptions(), - SourceLoc()))) { - // We only want to produce this version of the diagnostic if the - // subclass doesn't directly implement encode(to:). - // The direct lookup here won't see an encode(to:) if it is inherited - // from the superclass. - auto encodeTo = DeclName(C, C.Id_encode, C.Id_to); - if (classDecl->lookupDirect(encodeTo).empty()) - diagName = diag::codable_suggest_overriding_init_here; - } - - tc.diagnose(diagDest, diagName); - } - } - - // Lazily construct a mapping from backing storage properties to the - // declared properties. - bool computedBackingToOriginalVars = false; - llvm::SmallDenseMap backingToOriginalVars; - auto getOriginalVar = [&](VarDecl *var) -> VarDecl * { - // If we haven't computed the mapping yet, do so now. - if (!computedBackingToOriginalVars) { - for (auto member : classDecl->getMembers()) { - if (auto var = dyn_cast(member)) { - if (auto backingVar = var->getPropertyWrapperBackingProperty()) { - backingToOriginalVars[backingVar] = var; - } - } - } - - computedBackingToOriginalVars = true; - } - - auto known = backingToOriginalVars.find(var); - if (known == backingToOriginalVars.end()) - return nullptr; - - return known->second; - }; - - for (auto member : classDecl->getMembers()) { - auto pbd = dyn_cast(member); - if (!pbd) - continue; - - if (pbd->isStatic() || !pbd->hasStorage() || - pbd->isDefaultInitializable() || pbd->isInvalid()) - continue; - - for (auto entry : pbd->getPatternList()) { - if (entry.isInitialized()) continue; - - SmallVector vars; - entry.getPattern()->collectVariables(vars); - if (vars.empty()) continue; - - // Replace the variables we found with the originals for diagnostic - // purposes. - for (auto &var : vars) { - if (auto originalVar = getOriginalVar(var)) - var = originalVar; - } - - auto varLoc = vars[0]->getLoc(); - - Optional diag; - switch (vars.size()) { - case 1: - diag.emplace(tc.diagnose(varLoc, diag::note_no_in_class_init_1, - vars[0]->getName())); - break; - case 2: - diag.emplace(tc.diagnose(varLoc, diag::note_no_in_class_init_2, - vars[0]->getName(), vars[1]->getName())); - break; - case 3: - diag.emplace(tc.diagnose(varLoc, diag::note_no_in_class_init_3plus, - vars[0]->getName(), vars[1]->getName(), - vars[2]->getName(), false)); - break; - default: - diag.emplace(tc.diagnose(varLoc, diag::note_no_in_class_init_3plus, - vars[0]->getName(), vars[1]->getName(), - vars[2]->getName(), true)); - break; - } - - if (auto defaultValueSuggestion - = buildDefaultInitializerString(tc, classDecl, entry.getPattern())) - diag->fixItInsertAfter(entry.getPattern()->getEndLoc(), - " = " + *defaultValueSuggestion); - } - } -} - -void TypeChecker::maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { - if (auto *SF = classDecl->getParentSourceFile()) { - // Allow classes without initializers in SIL and module interface files. - switch (SF->Kind) { - case SourceFileKind::SIL: - case SourceFileKind::Interface: - return; - case SourceFileKind::Library: - case SourceFileKind::Main: - case SourceFileKind::REPL: - break; - } - } - - // Some heuristics to skip emitting a diagnostic if the class is already - // irreperably busted. - if (classDecl->isInvalid() || - classDecl->inheritsSuperclassInitializers()) - return; - - auto *superclassDecl = classDecl->getSuperclassDecl(); - if (superclassDecl && - superclassDecl->hasMissingDesignatedInitializers()) - return; - - for (auto member : classDecl->lookupDirect(DeclBaseName::createConstructor())) { - auto ctor = dyn_cast(member); - if (ctor && ctor->isDesignatedInit()) - return; - } - - diagnoseClassWithoutInitializers(*this, classDecl); -} diff --git a/lib/Sema/TypeCheckDecl.h b/lib/Sema/TypeCheckDecl.h index f546f78762fe9..1bfd954260cdf 100644 --- a/lib/Sema/TypeCheckDecl.h +++ b/lib/Sema/TypeCheckDecl.h @@ -25,8 +25,6 @@ class DeclContext; class ValueDecl; class Pattern; -bool doesContextHaveValueSemantics(DeclContext *dc); - /// Walks up the override chain for \p CD until it finds an initializer that is /// required and non-implicit. If no such initializer exists, returns the /// declaration where \c required was introduced (i.e. closest to the root @@ -38,8 +36,30 @@ bool checkOverrides(ValueDecl *decl); // Implemented in TypeCheckStorage.cpp void setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx); -void validatePatternBindingEntries(TypeChecker &tc, - PatternBindingDecl *binding); + + +/// How to generate the raw value for each element of an enum that doesn't +/// have one explicitly specified. +enum class AutomaticEnumValueKind { + /// Raw values cannot be automatically generated. + None, + /// The raw value is the enum element's name. + String, + /// The raw value is the previous element's raw value, incremented. + /// + /// For the first element in the enum, the raw value is 0. + Integer, +}; + +Optional computeAutomaticEnumValueKind(EnumDecl *ED); + +void validatePrecedenceGroup(PrecedenceGroupDecl *PGD); + +bool checkDesignatedTypes(OperatorDecl *OD, + ArrayRef identifiers, + ArrayRef identifierLocs, + ASTContext &ctx); + } #endif diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 3ed5a8490747a..54fe278987e52 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -225,13 +225,10 @@ static void diagnoseFunctionParamNotRepresentable( AFD->diagnose(diag::objc_invalid_on_func_param_type, ParamIndex + 1, getObjCDiagnosticAttrKind(Reason)); } - if (P->hasType()) { - Type ParamTy = P->getType(); - SourceRange SR; - if (auto typeRepr = P->getTypeRepr()) - SR = typeRepr->getSourceRange(); - diagnoseTypeNotRepresentableInObjC(AFD, ParamTy, SR); - } + SourceRange SR; + if (auto typeRepr = P->getTypeRepr()) + SR = typeRepr->getSourceRange(); + diagnoseTypeNotRepresentableInObjC(AFD, P->getType(), SR); describeObjCReason(AFD, Reason); } @@ -488,8 +485,7 @@ bool swift::isRepresentableInObjC( // If you change this function, you must add or modify a test in PrintAsObjC. ASTContext &ctx = AFD->getASTContext(); - // FIXME(InterfaceTypeRequest): Remove this. - (void)AFD->getInterfaceType(); + bool Diagnose = shouldDiagnoseObjCReason(Reason, ctx); if (checkObjCInForeignClassContext(AFD, Reason)) @@ -682,11 +678,10 @@ bool swift::isRepresentableInObjC( } // The error type is always 'AutoreleasingUnsafeMutablePointer?'. - auto nsError = ctx.getNSErrorDecl(); + auto nsErrorTy = ctx.getNSErrorType(); Type errorParameterType; - if (nsError) { - errorParameterType = nsError->getDeclaredInterfaceType(); - errorParameterType = OptionalType::get(errorParameterType); + if (nsErrorTy) { + errorParameterType = OptionalType::get(nsErrorTy); errorParameterType = BoundGenericType::get( ctx.getAutoreleasingUnsafeMutablePointerDecl(), @@ -812,10 +807,7 @@ bool swift::isRepresentableInObjC( bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { // If you change this function, you must add or modify a test in PrintAsObjC. - - // FIXME: Computes isInvalid() below. - (void) VD->getInterfaceType(); - + if (VD->isInvalid()) return false; @@ -944,92 +936,6 @@ bool swift::canBeRepresentedInObjC(const ValueDecl *decl) { return false; } -static Type getObjectiveCNominalType(Type &cache, - Identifier ModuleName, - Identifier TypeName, - DeclContext *dc) { - if (cache) - return cache; - - // FIXME: Does not respect visibility of the module. - ASTContext &ctx = dc->getASTContext(); - ModuleDecl *module = ctx.getLoadedModule(ModuleName); - if (!module) - return nullptr; - - SmallVector decls; - NLOptions options = NL_QualifiedDefault | NL_OnlyTypes; - dc->lookupQualified(module, TypeName, options, decls); - for (auto decl : decls) { - if (auto nominal = dyn_cast(decl)) { - cache = nominal->getDeclaredType(); - return cache; - } - } - - return nullptr; -} - -#pragma mark Objective-C-specific types - -Type TypeChecker::getNSObjectType(DeclContext *dc) { - return getObjectiveCNominalType(NSObjectType, Context.Id_ObjectiveC, - Context.getSwiftId( - KnownFoundationEntity::NSObject), - dc); -} - -Type TypeChecker::getObjCSelectorType(DeclContext *dc) { - return getObjectiveCNominalType(ObjCSelectorType, - Context.Id_ObjectiveC, - Context.Id_Selector, - dc); -} - -#pragma mark Bridging support - -/// Check runtime functions responsible for implicit bridging of Objective-C -/// types. -static void checkObjCBridgingFunctions(ModuleDecl *mod, - StringRef bridgedTypeName, - StringRef forwardConversion, - StringRef reverseConversion) { - assert(mod); - SmallVector results; - - auto &ctx = mod->getASTContext(); - mod->lookupValue(ctx.getIdentifier(bridgedTypeName), - NLKind::QualifiedLookup, results); - mod->lookupValue(ctx.getIdentifier(forwardConversion), - NLKind::QualifiedLookup, results); - mod->lookupValue(ctx.getIdentifier(reverseConversion), - NLKind::QualifiedLookup, results); - - for (auto D : results) { - // FIXME(InterfaceTypeRequest): Remove this. - (void)D->getInterfaceType(); - } -} - -void swift::checkBridgedFunctions(ASTContext &ctx) { - #define BRIDGE_TYPE(BRIDGED_MOD, BRIDGED_TYPE, _, NATIVE_TYPE, OPT) \ - Identifier ID_##BRIDGED_MOD = ctx.getIdentifier(#BRIDGED_MOD);\ - if (ModuleDecl *module = ctx.getLoadedModule(ID_##BRIDGED_MOD)) {\ - checkObjCBridgingFunctions(module, #BRIDGED_TYPE, \ - "_convert" #BRIDGED_TYPE "To" #NATIVE_TYPE, \ - "_convert" #NATIVE_TYPE "To" #BRIDGED_TYPE); \ - } - #include "swift/SIL/BridgedTypes.def" - - if (ModuleDecl *module = ctx.getLoadedModule(ctx.Id_Foundation)) { - checkObjCBridgingFunctions(module, - ctx.getSwiftName( - KnownFoundationEntity::NSError), - "_convertNSErrorToError", - "_convertErrorToNSError"); - } -} - #pragma mark "@objc declaration handling" /// Whether this declaration is a member of a class extension marked @objc. @@ -1139,11 +1045,7 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { if (isa(VD) || isa(VD)) if (auto *replacementAttr = VD->getAttrs().getAttribute()) { - if (auto *replaced = replacementAttr->getReplacedFunction()) { - if (replaced->isObjC()) - return ObjCReason(ObjCReason::ImplicitlyObjC); - } else if (auto *replaced = - TypeChecker::findReplacedDynamicFunction(VD)) { + if (auto *replaced = VD->getDynamicallyReplacedDecl()) { if (replaced->isObjC()) return ObjCReason(ObjCReason::ImplicitlyObjC); } @@ -1394,7 +1296,7 @@ IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { // Classes can be @objc. // Protocols and enums can also be @objc, but this is covered by the - // isObjC() check a the beginning.; + // isObjC() check at the beginning. isObjC = shouldMarkAsObjC(VD, /*allowImplicit=*/false); } else if (auto enumDecl = dyn_cast(VD)) { // Enums can be @objc so long as they have a raw type that is representable diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index c8158b23de629..3b04ea630bf20 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -65,18 +65,19 @@ static Type dropResultOptionality(Type type, unsigned uncurryLevel) { return FunctionType::get(parameters, resultType, fnType->getExtInfo()); } -Type swift::getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member, - ValueDecl *derivedDecl) { +Type swift::getMemberTypeForComparison(const ValueDecl *member, + const ValueDecl *derivedDecl) { auto *method = dyn_cast(member); - ConstructorDecl *ctor = nullptr; - if (method) - ctor = dyn_cast(method); + auto *ctor = dyn_cast_or_null(method); auto abstractStorage = dyn_cast(member); assert((method || abstractStorage) && "Not a method or abstractStorage?"); - SubscriptDecl *subscript = dyn_cast_or_null(abstractStorage); + auto *subscript = dyn_cast_or_null(abstractStorage); auto memberType = member->getInterfaceType(); + if (memberType->is()) + return memberType; + if (derivedDecl) { auto *dc = derivedDecl->getDeclContext(); auto owningType = dc->getDeclaredInterfaceType(); @@ -84,8 +85,6 @@ Type swift::getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member, memberType = owningType->adjustSuperclassMemberDeclType(member, derivedDecl, memberType); - if (memberType->hasError()) - return memberType; } if (method) { @@ -111,8 +110,9 @@ Type swift::getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member, return memberType; } -static bool areAccessorsOverrideCompatible(AbstractStorageDecl *storage, - AbstractStorageDecl *parentStorage) { +static bool +areAccessorsOverrideCompatible(const AbstractStorageDecl *storage, + const AbstractStorageDecl *parentStorage) { // It's okay for the storage to disagree about whether to use a getter or // a read accessor; we'll patch up any differences when setting overrides // for the accessors. We don't want to diagnose anything involving @@ -142,8 +142,9 @@ static bool areAccessorsOverrideCompatible(AbstractStorageDecl *storage, return true; } -bool swift::isOverrideBasedOnType(ValueDecl *decl, Type declTy, - ValueDecl *parentDecl, Type parentDeclTy) { +bool swift::isOverrideBasedOnType(const ValueDecl *decl, Type declTy, + const ValueDecl *parentDecl, + Type parentDeclTy) { auto genericSig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); @@ -167,8 +168,8 @@ bool swift::isOverrideBasedOnType(ValueDecl *decl, Type declTy, auto sig = ctx.getOverrideGenericSignature(parentDecl, decl); if (sig && declGenericCtx && - declGenericCtx->getGenericSignature()->getCanonicalSignature() != - sig->getCanonicalSignature()) { + declGenericCtx->getGenericSignature().getCanonicalSignature() != + sig.getCanonicalSignature()) { return false; } @@ -233,6 +234,11 @@ static bool areOverrideCompatibleSimple(ValueDecl *decl, return false; } + // Ignore declarations that are defined inside constrained extensions. + if (auto *ext = dyn_cast(parentDecl->getDeclContext())) + if (ext->isConstrainedExtension()) + return false; + // The declarations must be of the same kind. if (decl->getKind() != parentDecl->getKind()) return false; @@ -281,9 +287,6 @@ diagnoseMismatchedOptionals(const ValueDecl *member, Type paramTy = decl->getType(); Type parentParamTy = parentDecl->getType(); - if (!paramTy || !parentParamTy) - return; - auto *repr = decl->getTypeRepr(); if (!repr) return; @@ -422,44 +425,40 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base); static bool noteFixableMismatchedTypes(ValueDecl *decl, const ValueDecl *base) { auto &ctx = decl->getASTContext(); auto &diags = ctx.Diags; - DiagnosticTransaction tentativeDiags(diags); - { - Type baseTy = base->getInterfaceType(); - if (baseTy->hasError()) - return false; - - Optional activeDiag; - if (auto *baseInit = dyn_cast(base)) { - // Special-case initializers, whose "type" isn't useful besides the - // input arguments. - auto *fnType = baseTy->getAs(); - baseTy = fnType->getResult(); - Type argTy = FunctionType::composeInput(ctx, - baseTy->getAs() - ->getParams(), - false); - auto diagKind = diag::override_type_mismatch_with_fixits_init; - unsigned numArgs = baseInit->getParameters()->size(); - activeDiag.emplace(diags.diagnose(decl, diagKind, - /*plural*/std::min(numArgs, 2U), - argTy)); - } else { - if (isa(base)) - baseTy = baseTy->getAs()->getResult(); - - activeDiag.emplace( - diags.diagnose(decl, - diag::override_type_mismatch_with_fixits, - base->getDescriptiveKind(), baseTy)); - } + Type baseTy = base->getInterfaceType(); + if (baseTy->hasError()) + return false; - if (fixItOverrideDeclarationTypes(*activeDiag, decl, base)) - return true; + if (auto *baseInit = dyn_cast(base)) { + // Special-case initializers, whose "type" isn't useful besides the + // input arguments. + auto *fnType = baseTy->getAs(); + baseTy = fnType->getResult(); + Type argTy = FunctionType::composeInput( + ctx, baseTy->getAs()->getParams(), false); + auto diagKind = diag::override_type_mismatch_with_fixits_init; + unsigned numArgs = baseInit->getParameters()->size(); + return computeFixitsForOverridenDeclaration( + decl, base, [&](bool HasNotes) -> Optional { + if (!HasNotes) + return None; + return diags.diagnose(decl, diagKind, + /*plural*/ std::min(numArgs, 2U), argTy); + }); + } else { + if (isa(base)) + baseTy = baseTy->getAs()->getResult(); + + return computeFixitsForOverridenDeclaration( + decl, base, [&](bool HasNotes) -> Optional { + if (!HasNotes) + return None; + return diags.diagnose(decl, diag::override_type_mismatch_with_fixits, + base->getDescriptiveKind(), baseTy); + }); } - // There weren't any fixes we knew how to make. Drop this diagnostic. - tentativeDiags.abort(); return false; } @@ -596,6 +595,111 @@ static bool parameterTypesMatch(const ValueDecl *derivedDecl, return true; } +/// Returns true if `derivedDecl` has a `@differentiable` attribute that +/// overrides one from `baseDecl`. +static bool hasOverridingDifferentiableAttribute(ValueDecl *derivedDecl, + ValueDecl *baseDecl) { + ASTContext &ctx = derivedDecl->getASTContext(); + auto &diags = ctx.Diags; + + auto *derivedAFD = dyn_cast(derivedDecl); + auto *baseAFD = dyn_cast(baseDecl); + + if (!derivedAFD || !baseAFD) + return false; + + auto derivedDAs = + derivedAFD->getAttrs() + .getAttributes(); + auto baseDAs = baseAFD->getAttrs().getAttributes(); + + // Make sure all the `@differentiable` attributes on `baseDecl` are + // also declared on `derivedDecl`. + bool diagnosed = false; + for (auto *baseDA : baseDAs) { + auto baseParameters = baseDA->getParameterIndices(); + auto defined = false; + for (auto derivedDA : derivedDAs) { + auto derivedParameters = derivedDA->getParameterIndices(); + // If base and derived parameter indices are both defined, check whether + // base parameter indices are a subset of derived parameter indices. + if (derivedParameters && baseParameters && + baseParameters->isSubsetOf(derivedParameters)) { + defined = true; + break; + } + // Parameter indices may not be resolved because override matching happens + // before attribute checking for declaration type-checking. + // If parameter indices have not been resolved, avoid emitting diagnostic. + // Assume that attributes are valid. + if (!derivedParameters || !baseParameters) { + defined = true; + break; + } + } + if (defined) + continue; + diagnosed = true; + // Emit an error and fix-it showing the missing base declaration's + // `@differentiable` attribute. + // Omit printing `wrt:` clause if attribute's differentiability parameters + // match inferred differentiability parameters. + auto *inferredParameters = + TypeChecker::inferDifferentiabilityParameters(derivedAFD, nullptr); + bool omitWrtClause = + !baseParameters || + baseParameters->getNumIndices() == inferredParameters->getNumIndices(); + // Get `@differentiable` attribute description. + std::string baseDiffAttrString; + llvm::raw_string_ostream os(baseDiffAttrString); + baseDA->print(os, derivedDecl, omitWrtClause, + /*omitDerivativeFunctions*/ true); + os.flush(); + diags + .diagnose(derivedDecl, + diag::overriding_decl_missing_differentiable_attr, + baseDiffAttrString) + .fixItInsert(derivedDecl->getStartLoc(), baseDiffAttrString + ' '); + diags.diagnose(baseDecl, diag::overridden_here); + } + // If a diagnostic was produced, return false. + if (diagnosed) + return false; + + // If there is no `@differentiable` attribute in `derivedDecl`, then + // overriding is not allowed. + auto *derivedDC = derivedDecl->getDeclContext(); + auto *baseDC = baseDecl->getDeclContext(); + if (derivedDC->getSelfClassDecl() && baseDC->getSelfClassDecl()) + return false; + + // Finally, go through all `@differentiable` attributes in `derivedDecl` and + // check if they subsume any of the `@differentiable` attributes in + // `baseDecl`. + for (auto derivedDA : derivedDAs) { + auto derivedParameters = derivedDA->getParameterIndices(); + auto overrides = true; + for (auto baseDA : baseDAs) { + auto baseParameters = baseDA->getParameterIndices(); + // If the parameter indices of `derivedDA` are a subset of those of + // `baseDA`, then `baseDA` subsumes `derivedDA` and the function is + // marked as overridden. + if (derivedParameters && baseParameters && + derivedParameters->isSubsetOf(baseParameters)) { + overrides = false; + break; + } + if (!derivedParameters && !baseParameters) { + assert(false); + } + } + if (overrides) + return true; + } + + return false; +} + /// Returns true if the given declaration is for the `NSObject.hashValue` /// property. static bool isNSObjectHashValue(ValueDecl *baseDecl) { @@ -670,7 +774,7 @@ namespace { /// Retrieve the type of the declaration, to be used in comparisons. Type getDeclComparisonType() { if (!cachedDeclType) { - cachedDeclType = getMemberTypeForComparison(ctx, decl); + cachedDeclType = getMemberTypeForComparison(decl); } return cachedDeclType; @@ -696,7 +800,8 @@ OverrideMatcher::OverrideMatcher(ValueDecl *decl) // The final step for this constructor is to set up the superclass type, // without which we will not perform an matching. Early exits therefore imply // that there is no way we can match this declaration. - if (decl->isInvalid()) + // FIXME: Break the cycle here. + if (decl->hasInterfaceType() && decl->isInvalid()) return; auto *dc = decl->getDeclContext(); @@ -739,7 +844,13 @@ SmallVector OverrideMatcher::match( if (members.empty() || name != membersName) { membersName = name; members.clear(); - dc->lookupQualified(superContexts, membersName, + // FIXME: This suggests we need to use TypeChecker's high-level lookup + // entrypoints. But first we need one that supports additive qualified + // lookup. + for (auto *ctx : superContexts) { + ctx->synthesizeSemanticMembersIfNeeded(membersName); + } + dc->lookupQualified(superContexts, DeclNameRef(membersName), NL_QualifiedDefault, members); } @@ -751,6 +862,11 @@ SmallVector OverrideMatcher::match( if (!areOverrideCompatibleSimple(decl, parentDecl)) continue; + // Check whether the derived declaration has a `@differentiable` attribute + // that overrides one from the parent declaration. + if (hasOverridingDifferentiableAttribute(decl, parentDecl)) + continue; + auto parentMethod = dyn_cast(parentDecl); auto parentStorage = dyn_cast(parentDecl); assert(parentMethod || parentStorage); @@ -758,7 +874,7 @@ SmallVector OverrideMatcher::match( (void)parentStorage; // Check whether the types are identical. - auto parentDeclTy = getMemberTypeForComparison(ctx, parentDecl, decl); + auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl); if (parentDeclTy->hasError()) continue; @@ -933,7 +1049,7 @@ static void checkOverrideAccessControl(ValueDecl *baseDecl, ValueDecl *decl, bool OverrideMatcher::checkOverride(ValueDecl *baseDecl, OverrideCheckingAttempt attempt) { auto &diags = ctx.Diags; - auto baseTy = getMemberTypeForComparison(ctx, baseDecl, decl); + auto baseTy = getMemberTypeForComparison(baseDecl, decl); bool emittedMatchError = false; // If the name of our match differs from the name we were looking for, @@ -1171,6 +1287,11 @@ bool swift::checkOverrides(ValueDecl *decl) { // Otherwise, we have more checking to do. } + // Members of constrained extensions are not considered to be overrides. + if (auto *ext = dyn_cast(decl->getDeclContext())) + if (ext->isConstrainedExtension()) + return false; + // Accessor methods get overrides through their storage declaration, and // all checking can be performed via that mechanism. if (isa(decl)) { @@ -1281,12 +1402,14 @@ namespace { UNINTERESTING_ATTR(Exported) UNINTERESTING_ATTR(ForbidSerializingReference) UNINTERESTING_ATTR(GKInspectable) + UNINTERESTING_ATTR(HasMissingDesignatedInitializers) UNINTERESTING_ATTR(IBAction) UNINTERESTING_ATTR(IBDesignable) UNINTERESTING_ATTR(IBInspectable) UNINTERESTING_ATTR(IBOutlet) UNINTERESTING_ATTR(IBSegueAction) UNINTERESTING_ATTR(Indirect) + UNINTERESTING_ATTR(InheritsConvenienceInitializers) UNINTERESTING_ATTR(Inline) UNINTERESTING_ATTR(Optimize) UNINTERESTING_ATTR(Inlinable) @@ -1297,6 +1420,7 @@ namespace { UNINTERESTING_ATTR(LLDBDebuggerFunction) UNINTERESTING_ATTR(Mutating) UNINTERESTING_ATTR(NonMutating) + UNINTERESTING_ATTR(NonEphemeral) UNINTERESTING_ATTR(NonObjC) UNINTERESTING_ATTR(NonOverride) UNINTERESTING_ATTR(NSApplicationMain) @@ -1321,6 +1445,11 @@ namespace { UNINTERESTING_ATTR(DynamicReplacement) UNINTERESTING_ATTR(PrivateImport) + // Differentiation-related attributes. + UNINTERESTING_ATTR(Differentiable) + UNINTERESTING_ATTR(Derivative) + UNINTERESTING_ATTR(Transpose) + // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) UNINTERESTING_ATTR(Postfix) @@ -1350,6 +1479,7 @@ namespace { UNINTERESTING_ATTR(DisfavoredOverload) UNINTERESTING_ATTR(FunctionBuilder) UNINTERESTING_ATTR(ProjectedValueProperty) + UNINTERESTING_ATTR(OriginallyDefinedIn) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { @@ -1734,16 +1864,6 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { diagnoseOverrideForAvailability(override, base); } - // Overrides of NSObject.hashValue are deprecated; one should override - // NSObject.hash instead. - // FIXME: Remove this when NSObject.hashValue becomes non-open in - // swift-corelibs-foundation. - if (isNSObjectHashValue(base) && - base->hasOpenAccess(override->getDeclContext())) { - override->diagnose(diag::override_nsobject_hashvalue_warning) - .fixItReplace(SourceRange(override->getNameLoc()), "hash"); - } - /// Check attributes associated with the base; some may need to merged with /// or checked against attributes in the overriding declaration. AttributeOverrideChecker attrChecker(base, override); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp new file mode 100644 index 0000000000000..5659c6f8912aa --- /dev/null +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -0,0 +1,2510 @@ +//===--- TypeCheckDeclPrimary.cpp - Type Checking for Primary Files -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements type checking for primary files, that is, files whose +// declarations we're planning to emit. This exhaustively triggers diagnostics +// and type checking of all delayed bodies in those files. +// +//===----------------------------------------------------------------------===// + +#include "CodeSynthesis.h" +#include "ConstraintSystem.h" +#include "DerivedConformances.h" +#include "TypeChecker.h" +#include "TypeCheckAccess.h" +#include "TypeCheckDecl.h" +#include "TypeCheckAvailability.h" +#include "TypeCheckObjC.h" +#include "TypeCheckType.h" +#include "MiscDiagnostics.h" +#include "swift/AST/AccessScope.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/ExistentialLayout.h" +#include "swift/AST/Expr.h" +#include "swift/AST/ForeignErrorConvention.h" +#include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" +#include "swift/AST/Initializer.h" +#include "swift/AST/NameLookup.h" +#include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/ReferencedNameTracker.h" +#include "swift/AST/SourceFile.h" +#include "swift/AST/TypeWalker.h" +#include "swift/Basic/Statistic.h" +#include "swift/Parse/Lexer.h" +#include "swift/Parse/Parser.h" +#include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/Strings.h" +#include "swift/AST/NameLookupRequests.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/Basic/Defer.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" + +using namespace swift; + +#define DEBUG_TYPE "TypeCheckDeclPrimary" + +/// Check the inheritance clause of a type declaration or extension thereof. +/// +/// This routine performs detailed checking of the inheritance clause of the +/// given type or extension. It need only be called within the primary source +/// file. +static void checkInheritanceClause( + llvm::PointerUnion declUnion) { + DeclContext *DC; + MutableArrayRef inheritedClause; + ExtensionDecl *ext = nullptr; + TypeDecl *typeDecl = nullptr; + Decl *decl; + if ((ext = declUnion.dyn_cast())) { + decl = ext; + DC = ext; + + inheritedClause = ext->getInherited(); + + // Protocol extensions cannot have inheritance clauses. + if (auto proto = ext->getExtendedProtocolDecl()) { + if (!inheritedClause.empty()) { + ext->diagnose(diag::extension_protocol_inheritance, + proto->getName()) + .highlight(SourceRange(inheritedClause.front().getSourceRange().Start, + inheritedClause.back().getSourceRange().End)); + return; + } + } + } else { + typeDecl = declUnion.get(); + decl = typeDecl; + if (auto nominal = dyn_cast(typeDecl)) { + DC = nominal; + } else { + DC = typeDecl->getDeclContext(); + } + + inheritedClause = typeDecl->getInherited(); + } + + // Can this declaration's inheritance clause contain a class or + // subclass existential? + bool canHaveSuperclass = (isa(decl) || + (isa(decl) && + !cast(decl)->isObjC())); + + ASTContext &ctx = decl->getASTContext(); + auto &diags = ctx.Diags; + + // Retrieve the location of the start of the inheritance clause. + auto getStartLocOfInheritanceClause = [&] { + if (ext) + return ext->getSourceRange().End; + + return typeDecl->getNameLoc(); + }; + + // Compute the source range to be used when removing something from an + // inheritance clause. + auto getRemovalRange = [&](unsigned i) { + // If there is just one entry, remove the entire inheritance clause. + if (inheritedClause.size() == 1) { + SourceLoc start = getStartLocOfInheritanceClause(); + SourceLoc end = inheritedClause[i].getSourceRange().End; + return SourceRange(Lexer::getLocForEndOfToken(ctx.SourceMgr, start), + Lexer::getLocForEndOfToken(ctx.SourceMgr, end)); + } + + // If we're at the first entry, remove from the start of this entry to the + // start of the next entry. + if (i == 0) { + return SourceRange(inheritedClause[i].getSourceRange().Start, + inheritedClause[i+1].getSourceRange().Start); + } + + // Otherwise, remove from the end of the previous entry to the end of this + // entry. + SourceLoc afterPriorLoc = + Lexer::getLocForEndOfToken(ctx.SourceMgr, + inheritedClause[i-1].getSourceRange().End); + + SourceLoc afterMyEndLoc = + Lexer::getLocForEndOfToken(ctx.SourceMgr, + inheritedClause[i].getSourceRange().End); + + return SourceRange(afterPriorLoc, afterMyEndLoc); + }; + + // Check all of the types listed in the inheritance clause. + Type superclassTy; + SourceRange superclassRange; + Optional> inheritedAnyObject; + for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) { + auto &inherited = inheritedClause[i]; + + // Validate the type. + InheritedTypeRequest request{declUnion, i, TypeResolutionStage::Interface}; + Type inheritedTy = evaluateOrDefault(ctx.evaluator, request, Type()); + + // If we couldn't resolve an the inherited type, or it contains an error, + // ignore it. + if (!inheritedTy || inheritedTy->hasError()) + continue; + + // For generic parameters and associated types, the GSB checks constraints; + // however, we still want to fire off the requests to produce diagnostics + // in some circular validation cases. + if (isa(decl)) + continue; + + // Check whether we inherited from 'AnyObject' twice. + // Other redundant-inheritance scenarios are checked below, the + // GenericSignatureBuilder (for protocol inheritance) or the + // ConformanceLookupTable (for protocol conformance). + if (inheritedTy->isAnyObject()) { + if (inheritedAnyObject) { + // If the first occurrence was written as 'class', downgrade the error + // to a warning in such case for backward compatibility with + // Swift <= 4. + auto knownIndex = inheritedAnyObject->first; + auto knownRange = inheritedAnyObject->second; + SourceRange removeRange = getRemovalRange(knownIndex); + if (!ctx.LangOpts.isSwiftVersionAtLeast(5) && + (isa(decl) || isa(decl)) && + Lexer::getTokenAtLocation(ctx.SourceMgr, knownRange.Start) + .is(tok::kw_class)) { + SourceLoc classLoc = knownRange.Start; + + diags.diagnose(classLoc, diag::duplicate_anyobject_class_inheritance) + .fixItRemoveChars(removeRange.Start, removeRange.End); + } else { + diags.diagnose(inherited.getSourceRange().Start, + diag::duplicate_inheritance, inheritedTy) + .fixItRemoveChars(removeRange.Start, removeRange.End); + } + continue; + } + + // Note that we saw inheritance from 'AnyObject'. + inheritedAnyObject = { i, inherited.getSourceRange() }; + } + + if (inheritedTy->isExistentialType()) { + auto layout = inheritedTy->getExistentialLayout(); + + // Subclass existentials are not allowed except on classes and + // non-@objc protocols. + if (layout.explicitSuperclass && + !canHaveSuperclass) { + decl->diagnose(diag::inheritance_from_protocol_with_superclass, + inheritedTy); + continue; + } + + // AnyObject is not allowed except on protocols. + if (layout.hasExplicitAnyObject && + !isa(decl)) { + decl->diagnose(canHaveSuperclass + ? diag::inheritance_from_non_protocol_or_class + : diag::inheritance_from_non_protocol, + inheritedTy); + continue; + } + + // If the existential did not have a class constraint, we're done. + if (!layout.explicitSuperclass) + continue; + + // Classes and protocols can inherit from subclass existentials. + // For classes, we check for a duplicate superclass below. + // For protocols, the GSB emits its own warning instead. + if (isa(decl)) + continue; + + assert(isa(decl)); + assert(canHaveSuperclass); + inheritedTy = layout.explicitSuperclass; + } + + // If this is an enum inheritance clause, check for a raw type. + if (isa(decl)) { + // Check if we already had a raw type. + if (superclassTy) { + if (superclassTy->isEqual(inheritedTy)) { + auto removeRange = getRemovalRange(i); + diags.diagnose(inherited.getSourceRange().Start, + diag::duplicate_inheritance, inheritedTy) + .fixItRemoveChars(removeRange.Start, removeRange.End); + } else { + diags.diagnose(inherited.getSourceRange().Start, + diag::multiple_enum_raw_types, superclassTy, + inheritedTy) + .highlight(superclassRange); + } + continue; + } + + // If this is not the first entry in the inheritance clause, complain. + if (i > 0) { + auto removeRange = getRemovalRange(i); + + diags.diagnose(inherited.getSourceRange().Start, + diag::raw_type_not_first, inheritedTy) + .fixItRemoveChars(removeRange.Start, removeRange.End) + .fixItInsert(inheritedClause[0].getSourceRange().Start, + inheritedTy.getString() + ", "); + + // Fall through to record the raw type. + } + + // Record the raw type. + superclassTy = inheritedTy; + superclassRange = inherited.getSourceRange(); + continue; + } + + // If this is a class type, it may be the superclass. We end up here when + // the inherited type is either itself a class, or when it is a subclass + // existential via the existential type path above. + if (inheritedTy->getClassOrBoundGenericClass()) { + // First, check if we already had a superclass. + if (superclassTy) { + // FIXME: Check for shadowed protocol names, i.e., NSObject? + + if (superclassTy->isEqual(inheritedTy)) { + // Duplicate superclass. + auto removeRange = getRemovalRange(i); + diags.diagnose(inherited.getSourceRange().Start, + diag::duplicate_inheritance, inheritedTy) + .fixItRemoveChars(removeRange.Start, removeRange.End); + } else { + // Complain about multiple inheritance. + // Don't emit a Fix-It here. The user has to think harder about this. + diags.diagnose(inherited.getSourceRange().Start, + diag::multiple_inheritance, superclassTy, inheritedTy) + .highlight(superclassRange); + } + continue; + } + + // If this is not the first entry in the inheritance clause, complain. + if (isa(decl) && i > 0) { + auto removeRange = getRemovalRange(i); + diags.diagnose(inherited.getSourceRange().Start, + diag::superclass_not_first, inheritedTy) + .fixItRemoveChars(removeRange.Start, removeRange.End) + .fixItInsert(inheritedClause[0].getSourceRange().Start, + inheritedTy.getString() + ", "); + + // Fall through to record the superclass. + } + + if (canHaveSuperclass) { + // Record the superclass. + superclassTy = inheritedTy; + superclassRange = inherited.getSourceRange(); + continue; + } + } + + // We can't inherit from a non-class, non-protocol type. + decl->diagnose(canHaveSuperclass + ? diag::inheritance_from_non_protocol_or_class + : diag::inheritance_from_non_protocol, + inheritedTy); + // FIXME: Note pointing to the declaration 'inheritedTy' references? + } +} + +static void installCodingKeysIfNecessary(NominalTypeDecl *NTD) { + auto req = + ResolveImplicitMemberRequest{NTD, ImplicitMemberAction::ResolveCodingKeys}; + (void)evaluateOrDefault(NTD->getASTContext().evaluator, req, false); +} + +// Check for static properties that produce empty option sets +// using a rawValue initializer with a value of '0' +static void checkForEmptyOptionSet(const VarDecl *VD) { + // Check if property is a 'static let' + if (!VD->isStatic() || !VD->isLet()) + return; + + auto DC = VD->getDeclContext(); + + // Make sure property is of same type as the type it is declared in + if (!VD->getType()->isEqual(DC->getSelfTypeInContext())) + return; + + // Make sure this type conforms to OptionSet + auto *optionSetProto = VD->getASTContext().getProtocol(KnownProtocolKind::OptionSet); + bool conformsToOptionSet = (bool)TypeChecker::containsProtocol( + DC->getSelfTypeInContext(), + optionSetProto, + DC, + /*Flags*/None); + + if (!conformsToOptionSet) + return; + + auto PBD = VD->getParentPatternBinding(); + if (!PBD) + return; + + auto initIndex = PBD->getPatternEntryIndexForVarDecl(VD); + auto init = PBD->getInit(initIndex); + + // Make sure property is being set with a constructor + auto ctor = dyn_cast_or_null(init); + if (!ctor) + return; + auto ctorCalledVal = ctor->getCalledValue(); + if (!ctorCalledVal) + return; + if (!isa(ctorCalledVal)) + return; + + // Make sure it is calling the rawValue constructor + if (ctor->getNumArguments() != 1) + return; + if (ctor->getArgumentLabels().front() != VD->getASTContext().Id_rawValue) + return; + + // Make sure the rawValue parameter is a '0' integer literal + auto *args = cast(ctor->getArg()); + auto intArg = dyn_cast(args->getElement(0)); + if (!intArg) + return; + if (intArg->getValue() != 0) + return; + + VD->diagnose(diag::option_set_zero_constant, VD->getName()); + VD->diagnose(diag::option_set_empty_set_init) + .fixItReplace(args->getSourceRange(), "([])"); +} + + +/// Check the inheritance clauses generic parameters along with any +/// requirements stored within the generic parameter list. +static void checkGenericParams(GenericContext *ownerCtx) { + const auto genericParams = ownerCtx->getGenericParams(); + if (!genericParams) + return; + + for (auto gp : *genericParams) { + TypeChecker::checkDeclAttributes(gp); + checkInheritanceClause(gp); + } + + // Force visitation of each of the requirements here. + WhereClauseOwner(ownerCtx) + .visitRequirements(TypeResolutionStage::Interface, + [](Requirement, RequirementRepr *) { return false; }); +} + +/// Check whether \c current is a redeclaration. +static void checkRedeclaration(ASTContext &ctx, ValueDecl *current) { + // If we've already checked this declaration, don't do it again. + if (current->alreadyCheckedRedeclaration()) + return; + + // Make sure we don't do this checking again. + current->setCheckedRedeclaration(true); + + // Ignore invalid and anonymous declarations. + if (current->isInvalid() || !current->hasName()) + return; + + // If this declaration isn't from a source file, don't check it. + // FIXME: Should restrict this to the source file we care about. + DeclContext *currentDC = current->getDeclContext(); + SourceFile *currentFile = currentDC->getParentSourceFile(); + if (!currentFile || currentDC->isLocalContext()) + return; + + ReferencedNameTracker *tracker = currentFile->getReferencedNameTracker(); + bool isCascading = (current->getFormalAccess() > AccessLevel::FilePrivate); + + // Find other potential definitions. + SmallVector otherDefinitions; + if (currentDC->isTypeContext()) { + // Look within a type context. + if (auto nominal = currentDC->getSelfNominalTypeDecl()) { + auto found = nominal->lookupDirect(current->getBaseName()); + otherDefinitions.append(found.begin(), found.end()); + if (tracker) + tracker->addUsedMember({nominal, current->getBaseName()}, isCascading); + } + } else { + // Look within a module context. + currentFile->getParentModule()->lookupValue(current->getBaseName(), + NLKind::QualifiedLookup, + otherDefinitions); + if (tracker) + tracker->addTopLevelName(current->getBaseName(), isCascading); + } + + // Compare this signature against the signature of other + // declarations with the same name. + OverloadSignature currentSig = current->getOverloadSignature(); + CanType currentSigType = current->getOverloadSignatureType(); + ModuleDecl *currentModule = current->getModuleContext(); + for (auto other : otherDefinitions) { + // Skip invalid declarations and ourselves. + // + // FIXME: Breaking a cycle here with hasInterfaceType() is bogus. + if (current == other || (other->hasInterfaceType() && other->isInvalid())) + continue; + + // Skip declarations in other modules. + if (currentModule != other->getModuleContext()) + continue; + + // If both declarations are in the same file, only diagnose the second one. + if (currentFile == other->getDeclContext()->getParentSourceFile()) + if (current->getLoc().isValid() && + ctx.SourceMgr.isBeforeInBuffer( + current->getLoc(), other->getLoc())) + continue; + + // Don't compare methods vs. non-methods (which only happens with + // operators). + if (currentDC->isTypeContext() != other->getDeclContext()->isTypeContext()) + continue; + + // Check whether the overload signatures conflict (ignoring the type for + // now). + auto otherSig = other->getOverloadSignature(); + if (!conflicting(currentSig, otherSig)) + continue; + + // Skip declarations in other files. + // In practice, this means we will warn on a private declaration that + // shadows a non-private one, but only in the file where the shadowing + // happens. We will warn on conflicting non-private declarations in both + // files. + if (!other->isAccessibleFrom(currentDC)) + continue; + + // Skip invalid declarations. + if (other->isInvalid()) + continue; + + // Thwart attempts to override the same declaration more than once. + const auto *currentOverride = current->getOverriddenDecl(); + const auto *otherOverride = other->getOverriddenDecl(); + if (currentOverride && currentOverride == otherOverride) { + current->diagnose(diag::multiple_override, current->getFullName()); + other->diagnose(diag::multiple_override_prev, other->getFullName()); + current->setInvalid(); + break; + } + + // Get the overload signature type. + CanType otherSigType = other->getOverloadSignatureType(); + + bool wouldBeSwift5Redeclaration = false; + auto isRedeclaration = conflicting(ctx, currentSig, currentSigType, + otherSig, otherSigType, + &wouldBeSwift5Redeclaration); + // If there is another conflict, complain. + if (isRedeclaration || wouldBeSwift5Redeclaration) { + // If the two declarations occur in the same source file, make sure + // we get the diagnostic ordering to be sensible. + if (auto otherFile = other->getDeclContext()->getParentSourceFile()) { + if (currentFile == otherFile && + current->getLoc().isValid() && + other->getLoc().isValid() && + ctx.SourceMgr.isBeforeInBuffer(current->getLoc(), + other->getLoc())) { + std::swap(current, other); + } + } + + // If we're currently looking at a .sil and the conflicting declaration + // comes from a .sib, don't error since we won't be considering the sil + // from the .sib. So it's fine for the .sil to shadow it, since that's the + // one we want. + if (currentFile->Kind == SourceFileKind::SIL) { + auto *otherFile = dyn_cast( + other->getDeclContext()->getModuleScopeContext()); + if (otherFile && otherFile->isSIB()) + continue; + } + + // If the conflicting declarations have non-overlapping availability and, + // we allow the redeclaration to proceed if... + // + // - they are initializers with different failability, + bool isAcceptableVersionBasedChange = false; + { + const auto *currentInit = dyn_cast(current); + const auto *otherInit = dyn_cast(other); + if (currentInit && otherInit && + (currentInit->isFailable() != + otherInit->isFailable())) { + isAcceptableVersionBasedChange = true; + } + } + // - one throws and the other does not, + { + const auto *currentAFD = dyn_cast(current); + const auto *otherAFD = dyn_cast(other); + if (currentAFD && otherAFD && + currentAFD->hasThrows() != otherAFD->hasThrows()) { + isAcceptableVersionBasedChange = true; + } + } + // - or they are computed properties of different types, + { + const auto *currentVD = dyn_cast(current); + const auto *otherVD = dyn_cast(other); + if (currentVD && otherVD && + !currentVD->hasStorage() && + !otherVD->hasStorage() && + !currentVD->getInterfaceType()->isEqual( + otherVD->getInterfaceType())) { + isAcceptableVersionBasedChange = true; + } + } + + if (isAcceptableVersionBasedChange) { + class AvailabilityRange { + Optional introduced; + Optional obsoleted; + + public: + static AvailabilityRange from(const ValueDecl *VD) { + AvailabilityRange result; + for (auto *attr : VD->getAttrs().getAttributes()) { + if (attr->PlatformAgnostic == + PlatformAgnosticAvailabilityKind::SwiftVersionSpecific) { + if (attr->Introduced) + result.introduced = attr->Introduced; + if (attr->Obsoleted) + result.obsoleted = attr->Obsoleted; + } + } + return result; + } + + bool fullyPrecedes(const AvailabilityRange &other) const { + if (!obsoleted.hasValue()) + return false; + if (!other.introduced.hasValue()) + return false; + return *obsoleted <= *other.introduced; + } + + bool overlaps(const AvailabilityRange &other) const { + return !fullyPrecedes(other) && !other.fullyPrecedes(*this); + } + }; + + auto currentAvail = AvailabilityRange::from(current); + auto otherAvail = AvailabilityRange::from(other); + if (!currentAvail.overlaps(otherAvail)) + continue; + } + + // If both are VarDecls, and both have exactly the same type, then + // matching the Swift 4 behaviour (i.e. just emitting the future-compat + // warning) will result in SILGen crashes due to both properties mangling + // the same, so it's better to just follow the Swift 5 behaviour and emit + // the actual error. + if (wouldBeSwift5Redeclaration && isa(current) && + isa(other) && + current->getInterfaceType()->isEqual(other->getInterfaceType())) { + wouldBeSwift5Redeclaration = false; + } + + // If this isn't a redeclaration in the current version of Swift, but + // would be in Swift 5 mode, emit a warning instead of an error. + if (wouldBeSwift5Redeclaration) { + current->diagnose(diag::invalid_redecl_swift5_warning, + current->getFullName()); + other->diagnose(diag::invalid_redecl_prev, other->getFullName()); + } else { + const auto *otherInit = dyn_cast(other); + // Provide a better description for implicit initializers. + if (otherInit && otherInit->isImplicit()) { + // Skip conflicts with inherited initializers, which only happen + // when the current declaration is within an extension. The override + // checker should have already taken care of emitting a more + // productive diagnostic. + if (!other->getOverriddenDecl()) + current->diagnose(diag::invalid_redecl_init, + current->getFullName(), + otherInit->isMemberwiseInitializer()); + } else { + ctx.Diags.diagnoseWithNotes( + current->diagnose(diag::invalid_redecl, + current->getFullName()), [&]() { + other->diagnose(diag::invalid_redecl_prev, other->getFullName()); + }); + } + current->setInvalid(); + } + + // Make sure we don't do this checking again for the same decl. We also + // set this at the beginning of the function, but we might have swapped + // the decls for diagnostics; so ensure we also set this for the actual + // decl we diagnosed on. + current->setCheckedRedeclaration(true); + break; + } + } +} + +static Optional +getParamIndex(const ParameterList *paramList, const ParamDecl *decl) { + ArrayRef params = paramList->getArray(); + for (unsigned i = 0; i < params.size(); ++i) { + if (params[i] == decl) return i; + } + return None; +} + +static void checkInheritedDefaultValueRestrictions(ParamDecl *PD) { + assert(PD->getDefaultArgumentKind() == DefaultArgumentKind::Inherited); + + auto *DC = PD->getInnermostDeclContext(); + const SourceFile *SF = DC->getParentSourceFile(); + assert((SF && SF->Kind == SourceFileKind::Interface || PD->isImplicit()) && + "explicit inherited default argument outside of a module interface?"); + + // The containing decl should be a designated initializer. + auto ctor = dyn_cast(DC); + if (!ctor || ctor->isConvenienceInit()) { + PD->diagnose(diag::inherited_default_value_not_in_designated_constructor); + return; + } + + // The decl it overrides should also be a designated initializer. + auto overridden = ctor->getOverriddenDecl(); + if (!overridden || overridden->isConvenienceInit()) { + PD->diagnose( + diag::inherited_default_value_used_in_non_overriding_constructor); + if (overridden) + overridden->diagnose(diag::overridden_here); + return; + } + + // The corresponding parameter should have a default value. + Optional idx = getParamIndex(ctor->getParameters(), PD); + assert(idx && "containing decl does not contain param?"); + ParamDecl *equivalentParam = overridden->getParameters()->get(*idx); + if (equivalentParam->getDefaultArgumentKind() == DefaultArgumentKind::None) { + PD->diagnose(diag::corresponding_param_not_defaulted); + equivalentParam->diagnose(diag::inherited_default_param_here); + } +} + +/// Check the default arguments that occur within this pattern. +static void checkDefaultArguments(ParameterList *params) { + // Force the default values in case they produce diagnostics. + for (auto *param : *params) + (void)param->getTypeCheckedDefaultExpr(); +} + +llvm::Expected +DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, + ParamDecl *param) const { + if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) { + // Inherited default arguments don't have expressions, but we need to + // perform a couple of semantic checks to make sure they're valid. + checkInheritedDefaultValueRestrictions(param); + return nullptr; + } + + auto &ctx = param->getASTContext(); + auto paramTy = param->getType(); + auto *initExpr = param->getStructuralDefaultExpr(); + assert(initExpr); + + if (paramTy->hasError()) + return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); + + auto *dc = param->getDefaultArgumentInitContext(); + assert(dc); + + if (!TypeChecker::typeCheckParameterDefault(initExpr, dc, paramTy, + param->isAutoClosure())) { + return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); + } + + TypeChecker::checkInitializerErrorHandling(dc, initExpr); + + // Walk the checked initializer and contextualize any closures + // we saw there. + (void)TypeChecker::contextualizeInitializer(dc, initExpr); + return initExpr; +} + +llvm::Expected +DefaultArgumentInitContextRequest::evaluate(Evaluator &eval, + ParamDecl *param) const { + auto &ctx = param->getASTContext(); + auto *parentDC = param->getDeclContext(); + auto *paramList = getParameterList(cast(parentDC->getAsDecl())); + + // In order to compute the initializer context for this parameter, we need to + // know its index in the parameter list. Therefore iterate over the parameters + // looking for it and fill in the other parameter's contexts while we're here. + Initializer *result = nullptr; + for (auto idx : indices(*paramList)) { + auto *otherParam = paramList->get(idx); + + // If this param doesn't need a context, we're done. + if (!otherParam->hasDefaultExpr() && !otherParam->getStoredProperty()) + continue; + + // If this param already has a context, continue using it. + if (otherParam->getCachedDefaultArgumentInitContext()) + continue; + + // Create a new initializer context. If this is for the parameter that + // kicked off the request, make a note of it for when we return. Otherwise + // cache the result ourselves. + auto *initDC = new (ctx) DefaultArgumentInitializer(parentDC, idx); + if (param == otherParam) { + result = initDC; + } else { + eval.cacheOutput(DefaultArgumentInitContextRequest{otherParam}, + std::move(initDC)); + } + } + assert(result && "Didn't create init context?"); + return result; +} + +/// Check the requirements in the where clause of the given \c atd +/// to ensure that they don't introduce additional 'Self' requirements. +static void checkProtocolSelfRequirements(ProtocolDecl *proto, + AssociatedTypeDecl *atd) { + WhereClauseOwner(atd).visitRequirements( + TypeResolutionStage::Interface, + [proto](const Requirement &req, RequirementRepr *reqRepr) { + switch (req.getKind()) { + case RequirementKind::Conformance: + case RequirementKind::Layout: + case RequirementKind::Superclass: + if (reqRepr && + req.getFirstType()->isEqual(proto->getSelfInterfaceType())) { + auto &diags = proto->getASTContext().Diags; + diags.diagnose(reqRepr->getSubjectLoc().getLoc(), + diag::protocol_where_clause_self_requirement); + } + + return false; + + case RequirementKind::SameType: + return false; + } + llvm_unreachable("unhandled kind"); + }); +} + +/// For now, DynamicSelfType can only appear at the top level of a +/// function result type, possibly wrapped in an optional type. +/// +/// In the future, we could generalize it to allow it in any +/// covariant position, so that for example a class method could +/// return '() -> Self'. +static void checkDynamicSelfType(ValueDecl *decl, Type type) { + if (!type->hasDynamicSelfType()) + return; + + if (auto objectTy = type->getOptionalObjectType()) + type = objectTy; + + if (type->is()) + return; + + if (isa(decl)) + decl->diagnose(diag::dynamic_self_invalid_method); + else if (isa(decl)) + decl->diagnose(diag::dynamic_self_invalid_property); + else { + assert(isa(decl)); + decl->diagnose(diag::dynamic_self_invalid_subscript); + } +} + +/// Build a default initializer string for the given pattern. +/// +/// This string is suitable for display in diagnostics. +static Optional buildDefaultInitializerString(DeclContext *dc, + Pattern *pattern) { + switch (pattern->getKind()) { +#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id: +#define PATTERN(Id, Parent) +#include "swift/AST/PatternNodes.def" + return None; + case PatternKind::Any: + return None; + + case PatternKind::Named: { + if (!pattern->hasType()) + return None; + + // Special-case the various types we might see here. + auto type = pattern->getType(); + + // For literal-convertible types, form the corresponding literal. +#define CHECK_LITERAL_PROTOCOL(Kind, String) \ + if (auto proto = TypeChecker::getProtocol( \ + type->getASTContext(), SourceLoc(), KnownProtocolKind::Kind)) { \ + if (TypeChecker::conformsToProtocol(type, proto, dc, \ + ConformanceCheckFlags::InExpression)) \ + return std::string(String); \ + } + CHECK_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "[]") + CHECK_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "[:]") + CHECK_LITERAL_PROTOCOL(ExpressibleByUnicodeScalarLiteral, "\"\"") + CHECK_LITERAL_PROTOCOL(ExpressibleByExtendedGraphemeClusterLiteral, "\"\"") + CHECK_LITERAL_PROTOCOL(ExpressibleByFloatLiteral, "0.0") + CHECK_LITERAL_PROTOCOL(ExpressibleByIntegerLiteral, "0") + CHECK_LITERAL_PROTOCOL(ExpressibleByStringLiteral, "\"\"") +#undef CHECK_LITERAL_PROTOCOL + + // For optional types, use 'nil'. + if (type->getOptionalObjectType()) + return std::string("nil"); + + return None; + } + + case PatternKind::Paren: { + if (auto sub = buildDefaultInitializerString( + dc, cast(pattern)->getSubPattern())) { + return "(" + *sub + ")"; + } + + return None; + } + + case PatternKind::Tuple: { + std::string result = "("; + bool first = true; + for (auto elt : cast(pattern)->getElements()) { + if (auto sub = buildDefaultInitializerString(dc, elt.getPattern())) { + if (first) { + first = false; + } else { + result += ", "; + } + + result += *sub; + } else { + return None; + } + } + result += ")"; + return result; + } + + case PatternKind::Typed: + return buildDefaultInitializerString( + dc, cast(pattern)->getSubPattern()); + + case PatternKind::Var: + return buildDefaultInitializerString( + dc, cast(pattern)->getSubPattern()); + } + + llvm_unreachable("Unhandled PatternKind in switch."); +} + +/// Create a fix-it string for the 'decodable_suggest_overriding_init_here' and +/// optionally, the 'codable_suggest_overriding_init_here' diagnostics. +static std::string getFixItStringForDecodable(ClassDecl *CD, + bool includeEncodeTo) { + auto &ctx = CD->getASTContext(); + SourceLoc indentationLoc = CD->getBraces().End; + StringRef extraIndentation; + StringRef indentation = Lexer::getIndentationForLine( + ctx.SourceMgr, indentationLoc, &extraIndentation); + std::string fixItStringToReturn; + { + llvm::raw_string_ostream out(fixItStringToReturn); + ExtraIndentStreamPrinter printer(out, indentation); + + printer.printNewline(); + printer << "override init(from decoder: Decoder) throws"; + + // Add a dummy body. + auto printDummyBody = [&]() { + printer << " {"; + printer.printNewline(); + printer << extraIndentation << getCodePlaceholder(); + printer.printNewline(); + printer << "}"; + }; + + printDummyBody(); + + if (includeEncodeTo) { + printer.printNewline(); + printer.printNewline(); + printer << "override func encode(to encoder: Encoder) throws"; + printDummyBody(); + } + } + + return fixItStringToReturn; +} + +/// Diagnose a class that does not have any initializers. +static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) { + ASTContext &C = classDecl->getASTContext(); + C.Diags.diagnose(classDecl, diag::class_without_init, + classDecl->getDeclaredType()); + + // HACK: We've got a special case to look out for and diagnose specifically to + // improve the experience of seeing this, and mitigate some confusion. + // + // For a class A which inherits from Decodable class B, class A may have + // additional members which prevent default initializer synthesis (and + // inheritance of other initializers). The user may have assumed that this + // case would synthesize Encodable/Decodable conformance for class A the same + // way it may have for class B, or other classes. + // + // It is helpful to suggest here that the user may have forgotten to override + // init(from:) (and encode(to:), if applicable) in a note, before we start + // listing the members that prevented initializer synthesis. + if (auto *superclassDecl = classDecl->getSuperclassDecl()) { + auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable); + auto superclassType = superclassDecl->getDeclaredInterfaceType(); + auto ref = TypeChecker::conformsToProtocol( + superclassType, decodableProto, superclassDecl, + ConformanceCheckOptions(), SourceLoc()); + if (ref) { + // super conforms to Decodable, so we've failed to inherit init(from:). + // Let's suggest overriding it here. + // + // We're going to diagnose on the concrete init(from:) decl if it exists + // and isn't implicit; otherwise, on the subclass itself. + ValueDecl *diagDest = classDecl; + DeclNameRef initFrom( + { C, DeclBaseName::createConstructor(), { C.Id_from } }); + auto result = + TypeChecker::lookupMember(superclassDecl, superclassType, initFrom, + NameLookupFlags::ProtocolMembers | + NameLookupFlags::IgnoreAccessControl); + + if (!result.empty() && !result.front().getValueDecl()->isImplicit()) + diagDest = result.front().getValueDecl(); + + auto diagName = diag::decodable_suggest_overriding_init_here; + auto shouldEmitFixItForEncodeTo = false; + + // This is also a bit of a hack, but the best place we've got at the + // moment to suggest this. + // + // If the superclass also conforms to Encodable, it's quite + // likely that the user forgot to override its encode(to:). In this case, + // we can produce a slightly different diagnostic to suggest doing so. + auto *encodableProto = C.getProtocol(KnownProtocolKind::Encodable); + auto ref = TypeChecker::conformsToProtocol( + superclassType, encodableProto, superclassDecl, + ConformanceCheckOptions(), SourceLoc()); + if (ref) { + // We only want to produce this version of the diagnostic if the + // subclass doesn't directly implement encode(to:). + // The direct lookup here won't see an encode(to:) if it is inherited + // from the superclass. + auto encodeTo = DeclName(C, C.Id_encode, C.Id_to); + if (classDecl->lookupDirect(encodeTo).empty()) { + diagName = diag::codable_suggest_overriding_init_here; + shouldEmitFixItForEncodeTo = true; + } + } + + auto insertionLoc = + Lexer::getLocForEndOfLine(C.SourceMgr, classDecl->getBraces().Start); + auto fixItString = + getFixItStringForDecodable(classDecl, shouldEmitFixItForEncodeTo); + C.Diags.diagnose(diagDest, diagName) + .fixItInsert(insertionLoc, fixItString); + } + } + + // Lazily construct a mapping from backing storage properties to the + // declared properties. + bool computedBackingToOriginalVars = false; + llvm::SmallDenseMap backingToOriginalVars; + auto getOriginalVar = [&](VarDecl *var) -> VarDecl * { + // If we haven't computed the mapping yet, do so now. + if (!computedBackingToOriginalVars) { + for (auto member : classDecl->getMembers()) { + if (auto var = dyn_cast(member)) { + if (auto backingVar = var->getPropertyWrapperBackingProperty()) { + backingToOriginalVars[backingVar] = var; + } + } + } + + computedBackingToOriginalVars = true; + } + + auto known = backingToOriginalVars.find(var); + if (known == backingToOriginalVars.end()) + return nullptr; + + return known->second; + }; + + for (auto member : classDecl->getMembers()) { + auto pbd = dyn_cast(member); + if (!pbd) + continue; + + if (pbd->isStatic() || !pbd->hasStorage() || + pbd->isDefaultInitializable() || pbd->isInvalid()) + continue; + + for (auto idx : range(pbd->getNumPatternEntries())) { + if (pbd->isInitialized(idx)) continue; + + auto *pattern = pbd->getPattern(idx); + SmallVector vars; + pattern->collectVariables(vars); + if (vars.empty()) continue; + + // Replace the variables we found with the originals for diagnostic + // purposes. + for (auto &var : vars) { + if (auto originalVar = getOriginalVar(var)) + var = originalVar; + } + + auto varLoc = vars[0]->getLoc(); + + Optional diag; + switch (vars.size()) { + case 1: + diag.emplace(C.Diags.diagnose(varLoc, diag::note_no_in_class_init_1, + vars[0]->getName())); + break; + case 2: + diag.emplace(C.Diags.diagnose(varLoc, diag::note_no_in_class_init_2, + vars[0]->getName(), vars[1]->getName())); + break; + case 3: + diag.emplace(C.Diags.diagnose(varLoc, diag::note_no_in_class_init_3plus, + vars[0]->getName(), vars[1]->getName(), + vars[2]->getName(), false)); + break; + default: + diag.emplace(C.Diags.diagnose(varLoc, diag::note_no_in_class_init_3plus, + vars[0]->getName(), vars[1]->getName(), + vars[2]->getName(), true)); + break; + } + + if (auto defaultValueSuggestion = + buildDefaultInitializerString(classDecl, pattern)) + diag->fixItInsertAfter(pattern->getEndLoc(), + " = " + *defaultValueSuggestion); + } + } +} + +static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { + if (auto *SF = classDecl->getParentSourceFile()) { + // Allow classes without initializers in SIL and module interface files. + switch (SF->Kind) { + case SourceFileKind::SIL: + case SourceFileKind::Interface: + return; + case SourceFileKind::Library: + case SourceFileKind::Main: + case SourceFileKind::REPL: + break; + } + } + + // Some heuristics to skip emitting a diagnostic if the class is already + // irreperably busted. + if (classDecl->isInvalid() || + classDecl->inheritsSuperclassInitializers()) + return; + + auto *superclassDecl = classDecl->getSuperclassDecl(); + if (superclassDecl && + superclassDecl->getModuleContext() != classDecl->getModuleContext() && + superclassDecl->hasMissingDesignatedInitializers()) + return; + + for (auto member : classDecl->lookupDirect(DeclBaseName::createConstructor())) { + auto ctor = dyn_cast(member); + if (ctor && ctor->isDesignatedInit()) + return; + } + + diagnoseClassWithoutInitializers(classDecl); +} + +namespace { +class DeclChecker : public DeclVisitor { +public: + ASTContext &Ctx; + + explicit DeclChecker(ASTContext &ctx) : Ctx(ctx) {} + + ASTContext &getASTContext() const { return Ctx; } + + void visit(Decl *decl) { + if (getASTContext().Stats) + getASTContext().Stats->getFrontendCounters().NumDeclsTypechecked++; + + FrontendStatsTracer StatsTracer(getASTContext().Stats, "typecheck-decl", + decl); + PrettyStackTraceDecl StackTrace("type-checking", decl); + + DeclVisitor::visit(decl); + + TypeChecker::checkUnsupportedProtocolType(decl); + + if (auto VD = dyn_cast(decl)) { + auto &Context = getASTContext(); + TypeChecker::checkForForbiddenPrefix(Context, VD->getBaseName()); + + checkRedeclaration(Context, VD); + + // Force some requests, which can produce diagnostics. + + // Compute access level. + (void) VD->getFormalAccess(); + + // Compute overrides. + (void) VD->getOverriddenDecls(); + + // Check whether the member is @objc or dynamic. + (void) VD->isObjC(); + (void) VD->isDynamic(); + + // If this is a member of a nominal type, don't allow it to have a name of + // "Type" or "Protocol" since we reserve the X.Type and X.Protocol + // expressions to mean something builtin to the language. We *do* allow + // these if they are escaped with backticks though. + if (VD->getDeclContext()->isTypeContext() && + (VD->getFullName().isSimpleName(Context.Id_Type) || + VD->getFullName().isSimpleName(Context.Id_Protocol)) && + VD->getNameLoc().isValid() && + Context.SourceMgr.extractText({VD->getNameLoc(), 1}) != "`") { + auto &DE = getASTContext().Diags; + DE.diagnose(VD->getNameLoc(), diag::reserved_member_name, + VD->getFullName(), VD->getBaseName().getIdentifier().str()); + DE.diagnose(VD->getNameLoc(), diag::backticks_to_escape) + .fixItReplace(VD->getNameLoc(), + "`" + VD->getBaseName().userFacingName().str() + "`"); + } + } + } + + + //===--------------------------------------------------------------------===// + // Visit Methods. + //===--------------------------------------------------------------------===// + + void visitGenericTypeParamDecl(GenericTypeParamDecl *D) { + llvm_unreachable("cannot reach here"); + } + + void visitImportDecl(ImportDecl *ID) { + TypeChecker::checkDeclAttributes(ID); + } + + void visitOperatorDecl(OperatorDecl *OD) { + TypeChecker::checkDeclAttributes(OD); + auto &Ctx = OD->getASTContext(); + if (auto *IOD = dyn_cast(OD)) { + (void)IOD->getPrecedenceGroup(); + } else { + auto nominalTypes = OD->getDesignatedNominalTypes(); + const auto wantsDesignatedTypes = + Ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes; + if (nominalTypes.empty() && wantsDesignatedTypes) { + auto identifiers = OD->getIdentifiers(); + auto identifierLocs = OD->getIdentifierLocs(); + if (checkDesignatedTypes(OD, identifiers, identifierLocs, Ctx)) + OD->setInvalid(); + } + return; + } + checkAccessControl(OD); + } + + void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) { + TypeChecker::checkDeclAttributes(PGD); + validatePrecedenceGroup(PGD); + checkAccessControl(PGD); + } + + void visitMissingMemberDecl(MissingMemberDecl *MMD) { + llvm_unreachable("should always be type-checked already"); + } + + void visitBoundVariable(VarDecl *VD) { + // WARNING: Anything you put in this function will only be run when the + // VarDecl is fully type-checked within its own file. It will NOT be run + // when the VarDecl is merely used from another file. + + // Compute these requests in case they emit diagnostics. + (void) VD->getInterfaceType(); + (void) VD->isGetterMutating(); + (void) VD->isSetterMutating(); + (void) VD->getPropertyWrapperBackingProperty(); + (void) VD->getImplInfo(); + + // Add the '@_hasStorage' attribute if this property is stored. + if (VD->hasStorage() && !VD->getAttrs().hasAttribute()) + VD->getAttrs().add(new (getASTContext()) + HasStorageAttr(/*isImplicit=*/true)); + + // Reject cases where this is a variable that has storage but it isn't + // allowed. + if (VD->hasStorage()) { + // Note: Stored properties in protocols, enums, etc are diagnosed in + // finishStorageImplInfo(). + + // We haven't implemented type-level storage in some contexts. + if (VD->isStatic()) { + auto PBD = VD->getParentPatternBinding(); + // Selector for unimplemented_static_var message. + enum : unsigned { + Misc, + GenericTypes, + Classes, + ProtocolExtensions + }; + auto unimplementedStatic = [&](unsigned diagSel) { + auto staticLoc = PBD->getStaticLoc(); + VD->diagnose(diag::unimplemented_static_var, diagSel, + PBD->getStaticSpelling(), diagSel == Classes) + .highlight(staticLoc); + }; + + auto DC = VD->getDeclContext(); + + // Stored type variables in a generic context need to logically + // occur once per instantiation, which we don't yet handle. + if (DC->getExtendedProtocolDecl()) { + unimplementedStatic(ProtocolExtensions); + } else if (DC->isGenericContext() + && !DC->getGenericSignatureOfContext()->areAllParamsConcrete()) { + unimplementedStatic(GenericTypes); + } else if (DC->getSelfClassDecl()) { + auto StaticSpelling = PBD->getStaticSpelling(); + if (StaticSpelling != StaticSpellingKind::KeywordStatic) + unimplementedStatic(Classes); + } + } + } + + TypeChecker::checkDeclAttributes(VD); + + if (!checkOverrides(VD)) { + // If a property has an override attribute but does not override + // anything, complain. + auto overridden = VD->getOverriddenDecl(); + if (auto *OA = VD->getAttrs().getAttribute()) { + if (!overridden) { + VD->diagnose(diag::property_does_not_override) + .highlight(OA->getLocation()); + OA->setInvalid(); + } + } + } + + if (VD->getDeclContext()->getSelfClassDecl()) { + if (VD->getValueInterfaceType()->hasDynamicSelfType()) { + if (VD->hasStorage()) + VD->diagnose(diag::dynamic_self_in_stored_property); + else if (VD->isSettable(nullptr)) + VD->diagnose(diag::dynamic_self_in_mutable_property); + else + checkDynamicSelfType(VD, VD->getValueInterfaceType()); + } + } + + checkForEmptyOptionSet(VD); + + // Under the Swift 3 inference rules, if we have @IBInspectable or + // @GKInspectable but did not infer @objc, warn that the attribute is + auto &DE = getASTContext().Diags; + if (!VD->isObjC() && + VD->getASTContext().LangOpts.EnableSwift3ObjCInference) { + if (auto attr = VD->getAttrs().getAttribute()) { + DE.diagnose(attr->getLocation(), + diag::attribute_meaningless_when_nonobjc, + attr->getAttrName()) + .fixItRemove(attr->getRange()); + } + + if (auto attr = VD->getAttrs().getAttribute()) { + DE.diagnose(attr->getLocation(), + diag::attribute_meaningless_when_nonobjc, + attr->getAttrName()) + .fixItRemove(attr->getRange()); + } + } + + // Now check all the accessors. + VD->visitEmittedAccessors([&](AccessorDecl *accessor) { + visit(accessor); + }); + } + + + void visitPatternBindingDecl(PatternBindingDecl *PBD) { + DeclContext *DC = PBD->getDeclContext(); + + TypeChecker::checkDeclAttributes(PBD); + + bool isInSILMode = false; + if (auto sourceFile = DC->getParentSourceFile()) + isInSILMode = sourceFile->Kind == SourceFileKind::SIL; + bool isTypeContext = DC->isTypeContext(); + + auto &Ctx = getASTContext(); + for (auto i : range(PBD->getNumPatternEntries())) { + const auto *entry = evaluateOrDefault( + Ctx.evaluator, PatternBindingEntryRequest{PBD, i}, nullptr); + assert(entry && "No pattern binding entry?"); + + PBD->getPattern(i)->forEachVariable([&](VarDecl *var) { + this->visitBoundVariable(var); + + if (PBD->isInitialized(i)) { + // Add the attribute that preserves the "has an initializer" value + // across module generation, as required for TBDGen. + if (var->hasStorage() && + !var->getAttrs().hasAttribute()) { + var->getAttrs().add(new (Ctx) + HasInitialValueAttr(/*IsImplicit=*/true)); + } + return; + } + + // If this is a declaration without an initializer, reject code if + // uninitialized vars are not allowed. + if (isInSILMode) return; + + // If the variable has no storage, it never needs an initializer. + if (!var->hasStorage()) + return; + + if (var->isInvalid() || PBD->isInvalid()) + return; + + auto markVarAndPBDInvalid = [PBD, var] { + PBD->setInvalid(); + var->setInvalid(); + }; + + // Properties with an opaque return type need an initializer to + // determine their underlying type. + if (var->getOpaqueResultTypeDecl()) { + var->diagnose(diag::opaque_type_var_no_init); + } + + // Non-member observing properties need an initializer. + if (var->getWriteImpl() == WriteImplKind::StoredWithObservers && + !isTypeContext) { + var->diagnose(diag::observingprop_requires_initializer); + markVarAndPBDInvalid(); + return; + } + + // Static/class declarations require an initializer unless in a + // protocol. + if (var->isStatic() && !isa(DC)) { + // ...but don't enforce this for SIL or module interface files. + switch (DC->getParentSourceFile()->Kind) { + case SourceFileKind::Interface: + case SourceFileKind::SIL: + return; + case SourceFileKind::Main: + case SourceFileKind::REPL: + case SourceFileKind::Library: + break; + } + + var->diagnose(diag::static_requires_initializer, + var->getCorrectStaticSpelling()); + markVarAndPBDInvalid(); + return; + } + + // Global variables require an initializer in normal source files. + if (DC->isModuleScopeContext()) { + switch (DC->getParentSourceFile()->Kind) { + case SourceFileKind::Main: + case SourceFileKind::REPL: + case SourceFileKind::Interface: + case SourceFileKind::SIL: + return; + case SourceFileKind::Library: + break; + } + + var->diagnose(diag::global_requires_initializer, var->isLet()); + markVarAndPBDInvalid(); + return; + } + }); + } + + TypeChecker::checkDeclAttributes(PBD); + + checkAccessControl(PBD); + + // If the initializers in the PBD aren't checked yet, do so now. + for (auto i : range(PBD->getNumPatternEntries())) { + if (!PBD->isInitialized(i)) + continue; + + if (!PBD->isInitializerChecked(i)) { + TypeChecker::typeCheckPatternBinding(PBD, i); + } + + if (!PBD->isInvalid()) { + auto *init = PBD->getInit(i); + + // If we're performing an binding to a weak or unowned variable from a + // constructor call, emit a warning that the instance will be immediately + // deallocated. + diagnoseUnownedImmediateDeallocation(Ctx, PBD->getPattern(i), + PBD->getEqualLoc(i), + init); + + // If we entered an initializer context, contextualize any + // auto-closures we might have created. + // Note that we don't contextualize the initializer for a property + // with a wrapper, because the initializer will have been subsumed + // by the backing storage property. + if (!DC->isLocalContext() && + !(PBD->getSingleVar() && + PBD->getSingleVar()->hasAttachedPropertyWrapper())) { + auto *initContext = cast_or_null( + PBD->getInitContext(i)); + if (initContext) { + // Check safety of error-handling in the declaration, too. + TypeChecker::checkInitializerErrorHandling(initContext, init); + (void)TypeChecker::contextualizeInitializer(initContext, init); + } + } + } + } + } + + void visitSubscriptDecl(SubscriptDecl *SD) { + // Force requests that can emit diagnostics. + (void) SD->getInterfaceType(); + (void) SD->getGenericSignature(); + + if (!SD->isInvalid()) { + TypeChecker::checkReferencedGenericParams(SD); + checkGenericParams(SD); + TypeChecker::checkProtocolSelfRequirements(SD); + } + + TypeChecker::checkDeclAttributes(SD); + + checkAccessControl(SD); + + if (!checkOverrides(SD)) { + // If a subscript has an override attribute but does not override + // anything, complain. + if (auto *OA = SD->getAttrs().getAttribute()) { + if (!SD->getOverriddenDecl()) { + SD->diagnose(diag::subscript_does_not_override) + .highlight(OA->getLocation()); + OA->setInvalid(); + } + } + } + + // Compute these requests in case they emit diagnostics. + (void) SD->isGetterMutating(); + (void) SD->isSetterMutating(); + (void) SD->getImplInfo(); + + TypeChecker::checkParameterAttributes(SD->getIndices()); + + checkDefaultArguments(SD->getIndices()); + + if (SD->getDeclContext()->getSelfClassDecl()) { + checkDynamicSelfType(SD, SD->getValueInterfaceType()); + + if (SD->getValueInterfaceType()->hasDynamicSelfType() && + SD->supportsMutation()) { + SD->diagnose(diag::dynamic_self_in_mutable_subscript); + } + } + + // Now check all the accessors. + SD->visitEmittedAccessors([&](AccessorDecl *accessor) { + visit(accessor); + }); + } + + void visitTypeAliasDecl(TypeAliasDecl *TAD) { + // Force requests that can emit diagnostics. + (void) TAD->getGenericSignature(); + (void) TAD->getUnderlyingType(); + + TypeChecker::checkDeclAttributes(TAD); + checkAccessControl(TAD); + } + + void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) { + // Force requests that can emit diagnostics. + (void) OTD->getGenericSignature(); + + TypeChecker::checkDeclAttributes(OTD); + checkAccessControl(OTD); + } + + void visitAssociatedTypeDecl(AssociatedTypeDecl *AT) { + TypeChecker::checkDeclAttributes(AT); + + checkInheritanceClause(AT); + auto *proto = AT->getProtocol(); + + checkProtocolSelfRequirements(proto, AT); + + if (proto->isObjC()) { + AT->diagnose(diag::associated_type_objc, AT->getName(), proto->getName()); + } + + checkAccessControl(AT); + + // Trigger the checking for overridden declarations. + (void) AT->getOverriddenDecls(); + + auto defaultType = AT->getDefaultDefinitionType(); + if (defaultType && !defaultType->hasError()) { + // associatedtype X = X is invalid + auto mentionsItself = + defaultType.findIf([&](Type defaultType) { + if (auto DMT = defaultType->getAs()) { + return DMT->getAssocType() == AT; + } + return false; + }); + + if (mentionsItself) { + auto &DE = getASTContext().Diags; + DE.diagnose(AT->getDefaultDefinitionTypeRepr()->getLoc(), + diag::recursive_decl_reference, AT->getDescriptiveKind(), + AT->getName()); + AT->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); + } + } + } + + void checkUnsupportedNestedType(NominalTypeDecl *NTD) { + auto *DC = NTD->getDeclContext(); + if (DC->getResilienceExpansion() == ResilienceExpansion::Minimal) { + auto kind = TypeChecker::getFragileFunctionKind(DC); + NTD->diagnose(diag::local_type_in_inlinable_function, NTD->getFullName(), + static_cast(kind.first)); + } + + // We don't support protocols outside the top level of a file. + if (isa(NTD) && + !NTD->getParent()->isModuleScopeContext()) { + NTD->diagnose(diag::unsupported_nested_protocol, NTD->getName()); + NTD->setInvalid(); + return; + } + + // We don't support nested types in generics yet. + if (NTD->isGenericContext()) { + auto DC = NTD->getDeclContext(); + if (auto proto = DC->getSelfProtocolDecl()) { + if (DC->getExtendedProtocolDecl()) { + NTD->diagnose(diag::unsupported_type_nested_in_protocol_extension, + NTD->getName(), proto->getName()); + } else { + NTD->diagnose(diag::unsupported_type_nested_in_protocol, + NTD->getName(), proto->getName()); + } + } + + if (DC->isLocalContext() && DC->isGenericContext()) { + // A local generic context is a generic function. + if (auto AFD = dyn_cast(DC)) { + NTD->diagnose(diag::unsupported_type_nested_in_generic_function, + NTD->getName(), AFD->getFullName()); + } else { + NTD->diagnose(diag::unsupported_type_nested_in_generic_closure, + NTD->getName()); + } + } + } + } + + void visitEnumDecl(EnumDecl *ED) { + checkUnsupportedNestedType(ED); + + // FIXME: Remove this once we clean up the mess involving raw values. + (void) ED->getInterfaceType(); + + checkGenericParams(ED); + + // Check for circular inheritance of the raw type. + (void)ED->hasCircularRawValue(); + + for (Decl *member : ED->getMembers()) + visit(member); + + TypeChecker::checkDeclAttributes(ED); + + checkInheritanceClause(ED); + + checkAccessControl(ED); + + TypeChecker::checkPatternBindingCaptures(ED); + + auto &DE = getASTContext().Diags; + if (auto rawTy = ED->getRawType()) { + // The raw type must be one of the blessed literal convertible types. + if (!computeAutomaticEnumValueKind(ED)) { + DE.diagnose(ED->getInherited().front().getSourceRange().Start, + diag::raw_type_not_literal_convertible, rawTy); + ED->getInherited().front().setInvalidType(getASTContext()); + } + + // We need at least one case to have a raw value. + if (ED->getAllElements().empty()) { + DE.diagnose(ED->getInherited().front().getSourceRange().Start, + diag::empty_enum_raw_type); + } + } + + checkExplicitAvailability(ED); + + TypeChecker::checkDeclCircularity(ED); + + TypeChecker::checkConformancesInContext(ED, ED); + } + + void visitStructDecl(StructDecl *SD) { + checkUnsupportedNestedType(SD); + + checkGenericParams(SD); + + // Force lowering of stored properties. + (void) SD->getStoredProperties(); + + TypeChecker::addImplicitConstructors(SD); + + installCodingKeysIfNecessary(SD); + + for (Decl *Member : SD->getMembers()) + visit(Member); + + TypeChecker::checkPatternBindingCaptures(SD); + + TypeChecker::checkDeclAttributes(SD); + + checkInheritanceClause(SD); + + checkAccessControl(SD); + + checkExplicitAvailability(SD); + + TypeChecker::checkDeclCircularity(SD); + + TypeChecker::checkConformancesInContext(SD, SD); + } + + /// Check whether the given properties can be @NSManaged in this class. + static bool propertiesCanBeNSManaged(ClassDecl *classDecl, + ArrayRef vars) { + // Check whether we have an Objective-C-defined class in our + // inheritance chain. + if (!classDecl->checkAncestry(AncestryFlags::ClangImported)) + return false; + + // If all of the variables are @objc, we can use @NSManaged. + for (auto var : vars) { + if (!var->isObjC()) + return false; + } + + // Okay, we can use @NSManaged. + return true; + } + + /// Check that all stored properties have in-class initializers. + void checkRequiredInClassInits(ClassDecl *cd) { + ClassDecl *source = nullptr; + for (auto member : cd->getMembers()) { + auto pbd = dyn_cast(member); + if (!pbd) + continue; + + if (pbd->isStatic() || !pbd->hasStorage() || + pbd->isDefaultInitializable() || pbd->isInvalid()) + continue; + + // The variables in this pattern have not been + // initialized. Diagnose the lack of initial value. + pbd->setInvalid(); + SmallVector vars; + for (auto idx : range(pbd->getNumPatternEntries())) + pbd->getPattern(idx)->collectVariables(vars); + bool suggestNSManaged = propertiesCanBeNSManaged(cd, vars); + switch (vars.size()) { + case 0: + llvm_unreachable("should have been marked invalid"); + + case 1: + pbd->diagnose(diag::missing_in_class_init_1, vars[0]->getName(), + suggestNSManaged); + break; + + case 2: + pbd->diagnose(diag::missing_in_class_init_2, vars[0]->getName(), + vars[1]->getName(), suggestNSManaged); + break; + + case 3: + pbd->diagnose(diag::missing_in_class_init_3plus, vars[0]->getName(), + vars[1]->getName(), vars[2]->getName(), false, + suggestNSManaged); + break; + + default: + pbd->diagnose(diag::missing_in_class_init_3plus, vars[0]->getName(), + vars[1]->getName(), vars[2]->getName(), true, + suggestNSManaged); + break; + } + + // Figure out where this requirement came from. + if (!source) { + source = cd; + while (true) { + // If this class had the 'requires_stored_property_inits' + // attribute, diagnose here. + if (source->getAttrs(). + hasAttribute()) + break; + + // If the superclass doesn't require in-class initial + // values, the requirement was introduced at this point, so + // stop here. + auto superclass = source->getSuperclassDecl(); + if (!superclass->requiresStoredPropertyInits()) + break; + + // Keep looking. + source = superclass; + } + } + + // Add a note describing why we need an initializer. + source->diagnose(diag::requires_stored_property_inits_here, + source->getDeclaredType(), cd == source, + suggestNSManaged); + } + } + + + void visitClassDecl(ClassDecl *CD) { + checkUnsupportedNestedType(CD); + + // Force creation of the generic signature. + (void) CD->getGenericSignature(); + + checkGenericParams(CD); + + // Check for circular inheritance. + (void)CD->hasCircularInheritance(); + + // Force lowering of stored properties. + (void) CD->getStoredProperties(); + + // Force creation of an implicit destructor, if any. + (void) CD->getDestructor(); + + for (Decl *Member : CD->getEmittedMembers()) + visit(Member); + + TypeChecker::checkPatternBindingCaptures(CD); + + // If this class requires all of its stored properties to have + // in-class initializers, diagnose this now. + if (CD->requiresStoredPropertyInits()) + checkRequiredInClassInits(CD); + + // Compute @objc for each superclass member, to catch selector + // conflicts resulting from unintended overrides. + // + // FIXME: This should be a request so we can measure how much work + // we're doing here. + CD->walkSuperclasses( + [&](ClassDecl *superclass) { + if (!superclass->getParentSourceFile()) + return TypeWalker::Action::Stop; + + for (auto *member : superclass->getMembers()) { + if (auto *vd = dyn_cast(member)) { + if (vd->isPotentiallyOverridable()) { + (void) vd->isObjC(); + } + } + } + + return TypeWalker::Action::Continue; + }); + + if (auto superclassTy = CD->getSuperclass()) { + ClassDecl *Super = superclassTy->getClassOrBoundGenericClass(); + + if (auto *SF = CD->getParentSourceFile()) { + if (auto *tracker = SF->getReferencedNameTracker()) { + bool isPrivate = + CD->getFormalAccess() <= AccessLevel::FilePrivate; + tracker->addUsedMember({Super, Identifier()}, !isPrivate); + } + } + + bool isInvalidSuperclass = false; + + if (Super->isFinal()) { + CD->diagnose(diag::inheritance_from_final_class, Super->getName()); + // FIXME: should this really be skipping the rest of decl-checking? + return; + } + + if (Super->hasClangNode() && Super->getGenericParams() + && superclassTy->hasTypeParameter()) { + CD->diagnose(diag::inheritance_from_unspecialized_objc_generic_class, + Super->getName()); + } + + switch (Super->getForeignClassKind()) { + case ClassDecl::ForeignKind::Normal: + break; + case ClassDecl::ForeignKind::CFType: + CD->diagnose(diag::inheritance_from_cf_class, Super->getName()); + isInvalidSuperclass = true; + break; + case ClassDecl::ForeignKind::RuntimeOnly: + CD->diagnose(diag::inheritance_from_objc_runtime_visible_class, + Super->getName()); + isInvalidSuperclass = true; + break; + } + + if (!isInvalidSuperclass && Super->hasMissingVTableEntries() && + !Super->isResilient(CD->getParentModule(), + ResilienceExpansion::Minimal)) { + auto *superFile = Super->getModuleScopeContext(); + if (auto *serialized = dyn_cast(superFile)) { + const auto effVersion = + CD->getASTContext().LangOpts.EffectiveLanguageVersion; + if (serialized->getLanguageVersionBuiltWith() != effVersion) { + CD->diagnose( + diag:: + inheritance_from_class_with_missing_vtable_entries_versioned, + Super->getName(), serialized->getLanguageVersionBuiltWith(), + effVersion); + isInvalidSuperclass = true; + } + } + if (!isInvalidSuperclass) { + CD->diagnose(diag::inheritance_from_class_with_missing_vtable_entries, + Super->getName()); + isInvalidSuperclass = true; + } + } + + if (!getASTContext().isAccessControlDisabled()) { + // Require the superclass to be open if this is outside its + // defining module. But don't emit another diagnostic if we + // already complained about the class being inherently + // un-subclassable. + if (!isInvalidSuperclass && + !Super->hasOpenAccess(CD->getDeclContext()) && + Super->getModuleContext() != CD->getModuleContext()) { + CD->diagnose(diag::superclass_not_open, superclassTy); + isInvalidSuperclass = true; + } + + // Require superclasses to be open if the subclass is open. + // This is a restriction we can consider lifting in the future, + // e.g. to enable a "sealed" superclass whose subclasses are all + // of one of several alternatives. + if (!isInvalidSuperclass && + CD->getFormalAccess() == AccessLevel::Open && + Super->getFormalAccess() != AccessLevel::Open) { + CD->diagnose(diag::superclass_of_open_not_open, superclassTy); + Super->diagnose(diag::superclass_here); + } + } + } + + TypeChecker::checkDeclAttributes(CD); + + checkInheritanceClause(CD); + + checkAccessControl(CD); + + checkExplicitAvailability(CD); + + TypeChecker::checkDeclCircularity(CD); + + TypeChecker::checkConformancesInContext(CD, CD); + + maybeDiagnoseClassWithoutInitializers(CD); + } + + void visitProtocolDecl(ProtocolDecl *PD) { + checkUnsupportedNestedType(PD); + + // Check for circular inheritance within the protocol. + (void)PD->hasCircularInheritedProtocols(); + + auto *SF = PD->getParentSourceFile(); + if (SF) { + if (auto *tracker = SF->getReferencedNameTracker()) { + bool isNonPrivate = (PD->getFormalAccess() > AccessLevel::FilePrivate); + for (auto *parentProto : PD->getInheritedProtocols()) + tracker->addUsedMember({parentProto, Identifier()}, isNonPrivate); + } + } + + // Check the members. + for (auto Member : PD->getMembers()) + visit(Member); + + TypeChecker::checkDeclAttributes(PD); + + checkAccessControl(PD); + + checkInheritanceClause(PD); + + TypeChecker::checkDeclCircularity(PD); + if (PD->isResilient()) + if (!SF || SF->Kind != SourceFileKind::Interface) + TypeChecker::inferDefaultWitnesses(PD); + + if (PD->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { + auto requirementsSig = + GenericSignature::get({PD->getProtocolSelfType()}, + PD->getRequirementSignature()); + + llvm::errs() << "Protocol requirement signature:\n"; + PD->dumpRef(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "Requirement signature: "; + requirementsSig->print(llvm::errs()); + llvm::errs() << "\n"; + + // Note: One cannot canonicalize a requirement signature, because + // requirement signatures are necessarily missing requirements. + llvm::errs() << "Canonical requirement signature: "; + auto canRequirementSig = + CanGenericSignature::getCanonical(requirementsSig->getGenericParams(), + requirementsSig->getRequirements(), + /*skipValidation=*/true); + canRequirementSig->print(llvm::errs()); + llvm::errs() << "\n"; + } + + // Explicitly calculate this bit. + (void) PD->existentialTypeSupported(); + + // Explicity compute the requirement signature to detect errors. + (void) PD->getRequirementSignature(); + + checkExplicitAvailability(PD); + } + + void visitVarDecl(VarDecl *VD) { + // Delay type-checking on VarDecls until we see the corresponding + // PatternBindingDecl. + } + + /// Determine whether the given declaration requires a definition. + /// + /// Only valid for declarations that can have definitions, i.e., + /// functions, initializers, etc. + static bool requiresDefinition(Decl *decl) { + // Invalid, implicit, and Clang-imported declarations never + // require a definition. + if (decl->isInvalid() || decl->isImplicit() || decl->hasClangNode()) + return false; + + // Protocol requirements do not require definitions. + if (isa(decl->getDeclContext())) + return false; + + // Functions can have _silgen_name, semantics, and NSManaged attributes. + if (auto func = dyn_cast(decl)) { + if (func->getAttrs().hasAttribute() || + func->getAttrs().hasAttribute() || + func->getAttrs().hasAttribute()) + return false; + } + + // Declarations in SIL and module interface files don't require + // definitions. + if (auto sourceFile = decl->getDeclContext()->getParentSourceFile()) { + switch (sourceFile->Kind) { + case SourceFileKind::SIL: + case SourceFileKind::Interface: + return false; + case SourceFileKind::Library: + case SourceFileKind::Main: + case SourceFileKind::REPL: + break; + } + } + + // Everything else requires a definition. + return true; + } + + + bool shouldSkipBodyTypechecking(const AbstractFunctionDecl *AFD) { + // Make sure we're in the mode that's skipping function bodies. + if (!getASTContext().TypeCheckerOpts.SkipNonInlinableFunctionBodies) + return false; + + // Make sure there even _is_ a body that we can skip. + if (!AFD->getBodySourceRange().isValid()) + return false; + + // If we're gonna serialize the body, we can't skip it. + if (AFD->getResilienceExpansion() == ResilienceExpansion::Minimal) + return false; + + return true; + } + + void visitFuncDecl(FuncDecl *FD) { + // Force these requests in case they emit diagnostics. + (void) FD->getInterfaceType(); + (void) FD->getOperatorDecl(); + (void) FD->getDynamicallyReplacedDecl(); + + if (!FD->isInvalid()) { + checkGenericParams(FD); + TypeChecker::checkReferencedGenericParams(FD); + TypeChecker::checkProtocolSelfRequirements(FD); + } + + checkAccessControl(FD); + + TypeChecker::checkParameterAttributes(FD->getParameters()); + TypeChecker::checkDeclAttributes(FD); + + if (!checkOverrides(FD)) { + // If a method has an 'override' keyword but does not + // override anything, complain. + if (auto *OA = FD->getAttrs().getAttribute()) { + if (!FD->getOverriddenDecl()) { + FD->diagnose(diag::method_does_not_override) + .highlight(OA->getLocation()); + OA->setInvalid(); + } + } + } + + if (requiresDefinition(FD) && !FD->hasBody()) { + // Complain if we should have a body. + FD->diagnose(diag::func_decl_without_brace); + } else if (FD->getDeclContext()->isLocalContext()) { + // Check local function bodies right away. + TypeChecker::typeCheckAbstractFunctionBody(FD); + } else if (shouldSkipBodyTypechecking(FD)) { + FD->setBodySkipped(FD->getBodySourceRange()); + } else { + // FIXME: Remove TypeChecker dependency. + auto &TC = *Ctx.getLegacyGlobalTypeChecker(); + TC.definedFunctions.push_back(FD); + } + + checkExplicitAvailability(FD); + + // Skip this for accessors, since we should have diagnosed the + // storage itself. + if (!isa(FD)) + if (FD->getDeclContext()->getSelfClassDecl()) + checkDynamicSelfType(FD, FD->getResultInterfaceType()); + + checkDefaultArguments(FD->getParameters()); + + // Validate 'static'/'class' on functions in extensions. + auto StaticSpelling = FD->getStaticSpelling(); + if (StaticSpelling != StaticSpellingKind::None && + isa(FD->getDeclContext())) { + if (auto *NTD = FD->getDeclContext()->getSelfNominalTypeDecl()) { + if (!isa(NTD)) { + if (StaticSpelling == StaticSpellingKind::KeywordClass) { + FD->diagnose(diag::class_func_not_in_class, false) + .fixItReplace(FD->getStaticLoc(), "static"); + NTD->diagnose(diag::extended_type_declared_here); + } + } + } + } + + // Member functions need some special validation logic. + if (FD->getDeclContext()->isTypeContext()) { + if (FD->isOperator() && !isMemberOperator(FD, nullptr)) { + auto selfNominal = FD->getDeclContext()->getSelfNominalTypeDecl(); + auto isProtocol = selfNominal && isa(selfNominal); + // We did not find 'Self'. Complain. + FD->diagnose(diag::operator_in_unrelated_type, + FD->getDeclContext()->getDeclaredInterfaceType(), isProtocol, + FD->getFullName()); + } + } + + // If the function is exported to C, it must be representable in (Obj-)C. + // FIXME: This needs to be moved to its own request if we want to + // productize @_cdecl. + if (auto CDeclAttr = FD->getAttrs().getAttribute()) { + Optional errorConvention; + if (isRepresentableInObjC(FD, ObjCReason::ExplicitlyCDecl, + errorConvention)) { + if (FD->hasThrows()) { + FD->setForeignErrorConvention(*errorConvention); + getASTContext().Diags.diagnose(CDeclAttr->getLocation(), + diag::cdecl_throws); + } + } + } + } + + void visitModuleDecl(ModuleDecl *) { } + + void visitEnumCaseDecl(EnumCaseDecl *ECD) { + // The type-checker doesn't care about how these are grouped. + } + + void visitEnumElementDecl(EnumElementDecl *EED) { + (void) EED->getInterfaceType(); + auto *ED = EED->getParentEnum(); + + TypeChecker::checkDeclAttributes(EED); + + if (auto *PL = EED->getParameterList()) { + TypeChecker::checkParameterAttributes(PL); + + checkDefaultArguments(PL); + } + + auto &DE = getASTContext().Diags; + // We don't yet support raw values on payload cases. + if (EED->hasAssociatedValues()) { + if (auto rawTy = ED->getRawType()) { + EED->diagnose(diag::enum_with_raw_type_case_with_argument); + DE.diagnose(ED->getInherited().front().getSourceRange().Start, + diag::enum_raw_type_here, rawTy); + EED->setInvalid(); + } + } + + // Force the raw value expr then yell if our parent doesn't have a raw type. + Expr *RVE = EED->getRawValueExpr(); + if (RVE && !ED->hasRawType()) { + DE.diagnose(RVE->getLoc(), diag::enum_raw_value_without_raw_type); + EED->setInvalid(); + } + + checkAccessControl(EED); + } + + void visitExtensionDecl(ExtensionDecl *ED) { + // Produce any diagnostics for the extended type. + auto extType = ED->getExtendedType(); + + auto nominal = ED->computeExtendedNominal(); + if (nominal == nullptr) { + const bool wasAlreadyInvalid = ED->isInvalid(); + ED->setInvalid(); + if (extType && !extType->hasError() && extType->getAnyNominal()) { + // If we've got here, then we have some kind of extension of a prima + // fascie non-nominal type. This can come up when we're projecting + // typealiases out of bound generic types. + // + // struct Array { typealias Indices = Range } + // extension Array.Indices.Bound {} + // + // Offer to rewrite it to the underlying nominal type. + auto canExtType = extType->getCanonicalType(); + ED->diagnose(diag::invalid_nominal_extension, extType, canExtType) + .highlight(ED->getExtendedTypeRepr()->getSourceRange()); + ED->diagnose(diag::invalid_nominal_extension_rewrite, canExtType) + .fixItReplace(ED->getExtendedTypeRepr()->getSourceRange(), + canExtType->getString()); + } else if (!wasAlreadyInvalid) { + // If nothing else applies, fall back to a generic diagnostic. + ED->diagnose(diag::non_nominal_extension, extType); + } + return; + } + + // Produce any diagnostics for the generic signature. + (void) ED->getGenericSignature(); + + if (extType && !extType->hasError()) { + // The first condition catches syntactic forms like + // protocol A & B { ... } // may be protocols or typealiases + // The second condition also looks through typealiases and catches + // typealias T = P1 & P2 // P2 is a refined protocol of P1 + // extension T { ... } + // However, it is trickier to catch cases like + // typealias T = P2 & P1 // P2 is a refined protocol of P1 + // extension T { ... } + // so we don't do that here. + auto extTypeRepr = ED->getExtendedTypeRepr(); + auto *extTypeNominal = extType->getAnyNominal(); + bool firstNominalIsNotMostSpecific = + extTypeNominal && extTypeNominal != nominal; + if (isa(extTypeRepr) + || firstNominalIsNotMostSpecific) { + auto firstNominalType = nominal->getDeclaredType(); + auto diag = ED->diagnose(diag::composition_in_extended_type, + firstNominalType); + diag.highlight(extTypeRepr->getSourceRange()); + if (firstNominalIsNotMostSpecific) { + diag.flush(); + Type mostSpecificProtocol = extTypeNominal->getDeclaredType(); + ED->diagnose(diag::composition_in_extended_type_alternative, + mostSpecificProtocol) + .fixItReplace(extTypeRepr->getSourceRange(), + mostSpecificProtocol->getString()); + } else { + diag.fixItReplace(extTypeRepr->getSourceRange(), + firstNominalType->getString()); + } + } + } + + checkInheritanceClause(ED); + + // Only generic and protocol types are permitted to have + // trailing where clauses. + if (auto trailingWhereClause = ED->getTrailingWhereClause()) { + if (!ED->getGenericParams() && !ED->isInvalid()) { + ED->diagnose(diag::extension_nongeneric_trailing_where, + nominal->getFullName()) + .highlight(trailingWhereClause->getSourceRange()); + } + } + + checkGenericParams(ED); + + for (Decl *Member : ED->getMembers()) + visit(Member); + + TypeChecker::checkPatternBindingCaptures(ED); + + TypeChecker::checkConformancesInContext(ED, ED); + + TypeChecker::checkDeclAttributes(ED); + checkAccessControl(ED); + + checkExplicitAvailability(ED); + } + + void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { + // See swift::performTypeChecking for TopLevelCodeDecl handling. + llvm_unreachable("TopLevelCodeDecls are handled elsewhere"); + } + + void visitIfConfigDecl(IfConfigDecl *ICD) { + // The active members of the #if block will be type checked along with + // their enclosing declaration. + TypeChecker::checkDeclAttributes(ICD); + } + + void visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) { + if (PDD->hasBeenEmitted()) { return; } + PDD->markEmitted(); + getASTContext() + .Diags + .diagnose(PDD->getMessage()->getStartLoc(), + PDD->isError() ? diag::pound_error : diag::pound_warning, + PDD->getMessage()->getValue()) + .highlight(PDD->getMessage()->getSourceRange()); + } + + void visitConstructorDecl(ConstructorDecl *CD) { + (void) CD->getInterfaceType(); + + // Compute these requests in case they emit diagnostics. + (void) CD->getInitKind(); + + if (!CD->isInvalid()) { + checkGenericParams(CD); + TypeChecker::checkReferencedGenericParams(CD); + TypeChecker::checkProtocolSelfRequirements(CD); + } + + TypeChecker::checkDeclAttributes(CD); + TypeChecker::checkParameterAttributes(CD->getParameters()); + + // Check whether this initializer overrides an initializer in its + // superclass. + if (!checkOverrides(CD)) { + // If an initializer has an override attribute but does not override + // anything or overrides something that doesn't need an 'override' + // keyword (e.g., a convenience initializer), complain. + // anything, or overrides something that complain. + if (auto *attr = CD->getAttrs().getAttribute()) { + if (!CD->getOverriddenDecl()) { + CD->diagnose(diag::initializer_does_not_override) + .highlight(attr->getLocation()); + attr->setInvalid(); + } else if (attr->isImplicit()) { + // Don't diagnose implicit attributes. + } else if (overrideRequiresKeyword(CD->getOverriddenDecl()) + == OverrideRequiresKeyword::Never) { + // Special case: we are overriding a 'required' initializer, so we + // need (only) the 'required' keyword. + if (cast(CD->getOverriddenDecl())->isRequired()) { + if (CD->getAttrs().hasAttribute()) { + CD->diagnose(diag::required_initializer_override_keyword) + .fixItRemove(attr->getLocation()); + } else { + CD->diagnose(diag::required_initializer_override_wrong_keyword) + .fixItReplace(attr->getLocation(), "required"); + CD->getAttrs().add(new (getASTContext()) + RequiredAttr(/*IsImplicit=*/true)); + } + + auto *reqInit = + findNonImplicitRequiredInit(CD->getOverriddenDecl()); + reqInit->diagnose(diag::overridden_required_initializer_here); + } else { + // We tried to override a convenience initializer. + CD->diagnose(diag::initializer_does_not_override) + .highlight(attr->getLocation()); + CD->getOverriddenDecl()->diagnose( + diag::convenience_init_override_here); + } + } + } + + // A failable initializer cannot override a non-failable one. + // This would normally be diagnosed by the covariance rules; + // however, those are disabled so that we can provide a more + // specific diagnostic here. + if (CD->isFailable() && + CD->getOverriddenDecl() && + !CD->getOverriddenDecl()->isFailable()) { + CD->diagnose(diag::failable_initializer_override, CD->getFullName()); + auto *OD = CD->getOverriddenDecl(); + OD->diagnose(diag::nonfailable_initializer_override_here, + OD->getFullName()); + } + } + + // If this initializer overrides a 'required' initializer, it must itself + // be marked 'required'. + if (!CD->getAttrs().hasAttribute()) { + if (CD->getOverriddenDecl() && CD->getOverriddenDecl()->isRequired()) { + CD->diagnose(diag::required_initializer_missing_keyword) + .fixItInsert(CD->getLoc(), "required "); + + auto *reqInit = findNonImplicitRequiredInit(CD->getOverriddenDecl()); + reqInit->diagnose(diag::overridden_required_initializer_here); + + CD->getAttrs().add(new (getASTContext()) + RequiredAttr(/*IsImplicit=*/true)); + } + } + + if (CD->isRequired()) { + if (auto nominal = CD->getDeclContext()->getSelfNominalTypeDecl()) { + AccessLevel requiredAccess; + switch (nominal->getFormalAccess()) { + case AccessLevel::Open: + requiredAccess = AccessLevel::Public; + break; + case AccessLevel::Public: + case AccessLevel::Internal: + requiredAccess = AccessLevel::Internal; + break; + case AccessLevel::FilePrivate: + case AccessLevel::Private: + requiredAccess = AccessLevel::FilePrivate; + break; + } + if (CD->getFormalAccess() < requiredAccess) { + auto diag = CD->diagnose(diag::required_initializer_not_accessible, + nominal->getFullName()); + fixItAccess(diag, CD, requiredAccess); + } + } + } + + checkAccessControl(CD); + + if (requiresDefinition(CD) && !CD->hasBody()) { + // Complain if we should have a body. + CD->diagnose(diag::missing_initializer_def); + } else if (CD->getDeclContext()->isLocalContext()) { + // Check local function bodies right away. + TypeChecker::typeCheckAbstractFunctionBody(CD); + } else if (shouldSkipBodyTypechecking(CD)) { + CD->setBodySkipped(CD->getBodySourceRange()); + } else { + // FIXME: Remove TypeChecker dependency. + auto &TC = *Ctx.getLegacyGlobalTypeChecker(); + TC.definedFunctions.push_back(CD); + } + + checkDefaultArguments(CD->getParameters()); + } + + void visitDestructorDecl(DestructorDecl *DD) { + TypeChecker::checkDeclAttributes(DD); + + if (DD->getDeclContext()->isLocalContext()) { + // Check local function bodies right away. + TypeChecker::typeCheckAbstractFunctionBody(DD); + } else if (shouldSkipBodyTypechecking(DD)) { + DD->setBodySkipped(DD->getBodySourceRange()); + } else { + // FIXME: Remove TypeChecker dependency. + auto &TC = *Ctx.getLegacyGlobalTypeChecker(); + TC.definedFunctions.push_back(DD); + } + } +}; +} // end anonymous namespace + +void TypeChecker::typeCheckDecl(Decl *D) { + DeclChecker(D->getASTContext()).visit(D); +} diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index 1eed9a50b1a20..c44dc471deb9d 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -679,8 +679,7 @@ class ApplyClassifier { Classification classifyRethrowsArgument(Expr *arg, Type paramType) { arg = arg->getValueProvidingExpr(); - if (isa(arg) || - isa(arg)) { + if (isa(arg)) { return classifyArgumentByType(arg->getType(), PotentialReason::forDefaultArgument()); } @@ -978,13 +977,14 @@ class Context { return InterpolatedString; } - static void diagnoseThrowInIllegalContext(TypeChecker &TC, ASTNode node, + static void diagnoseThrowInIllegalContext(DiagnosticEngine &Diags, + ASTNode node, StringRef description, bool throwInDefer = false) { if (auto *e = node.dyn_cast()) if (isa(e)) { - TC.diagnose(e->getLoc(), diag::throwing_call_in_illegal_context, - description); + Diags.diagnose(e->getLoc(), diag::throwing_call_in_illegal_context, + description); return; } @@ -993,11 +993,11 @@ class Context { return; } - TC.diagnose(node.getStartLoc(), diag::throw_in_illegal_context, - description); + Diags.diagnose(node.getStartLoc(), diag::throw_in_illegal_context, + description); } - static void maybeAddRethrowsNote(TypeChecker &TC, SourceLoc loc, + static void maybeAddRethrowsNote(DiagnosticEngine &Diags, SourceLoc loc, const PotentialReason &reason) { switch (reason.getKind()) { case PotentialReason::Kind::Throw: @@ -1006,18 +1006,19 @@ class Context { // Already fully diagnosed. return; case PotentialReason::Kind::CallRethrowsWithExplicitThrowingArgument: - TC.diagnose(reason.getThrowingArgument()->getLoc(), - diag::because_rethrows_argument_throws); + Diags.diagnose(reason.getThrowingArgument()->getLoc(), + diag::because_rethrows_argument_throws); return; case PotentialReason::Kind::CallRethrowsWithDefaultThrowingArgument: - TC.diagnose(loc, diag::because_rethrows_default_argument_throws); + Diags.diagnose(loc, diag::because_rethrows_default_argument_throws); return; } llvm_unreachable("bad reason kind"); } - void diagnoseUncoveredThrowSite(TypeChecker &TC, ASTNode E, + void diagnoseUncoveredThrowSite(ASTContext &ctx, ASTNode E, const PotentialReason &reason) { + auto &Diags = ctx.Diags; auto message = diag::throwing_call_without_try; auto loc = E.getStartLoc(); SourceLoc insertLoc; @@ -1036,14 +1037,14 @@ class Context { if (InterpolatedString && e->getCalledValue() && e->getCalledValue()->getBaseName() == - TC.Context.Id_appendInterpolation) { + ctx.Id_appendInterpolation) { message = diag::throwing_interpolation_without_try; insertLoc = InterpolatedString->getLoc(); } } - TC.diagnose(loc, message).highlight(highlight); - maybeAddRethrowsNote(TC, loc, reason); + Diags.diagnose(loc, message).highlight(highlight); + maybeAddRethrowsNote(Diags, loc, reason); // If this is a call without expected 'try[?|!]', like this: // @@ -1055,15 +1056,15 @@ class Context { if (reason.getKind() != PotentialReason::Kind::CallThrows) return; - TC.diagnose(loc, diag::note_forgot_try) + Diags.diagnose(loc, diag::note_forgot_try) .fixItInsert(insertLoc, "try "); - TC.diagnose(loc, diag::note_error_to_optional) + Diags.diagnose(loc, diag::note_error_to_optional) .fixItInsert(insertLoc, "try? "); - TC.diagnose(loc, diag::note_disable_error_propagation) + Diags.diagnose(loc, diag::note_disable_error_propagation) .fixItInsert(insertLoc, "try! "); } - void diagnoseThrowInLegalContext(TypeChecker &TC, ASTNode node, + void diagnoseThrowInLegalContext(DiagnosticEngine &Diags, ASTNode node, bool isTryCovered, const PotentialReason &reason, Diag<> diagForThrow, @@ -1071,7 +1072,7 @@ class Context { Diag<> diagForTrylessThrowingCall) { auto loc = node.getStartLoc(); if (reason.isThrow()) { - TC.diagnose(loc, diagForThrow); + Diags.diagnose(loc, diagForThrow); return; } @@ -1085,14 +1086,15 @@ class Context { } if (isTryCovered) { - TC.diagnose(loc, diagForThrowingCall); + Diags.diagnose(loc, diagForThrowingCall); } else { - TC.diagnose(loc, diagForTrylessThrowingCall); + Diags.diagnose(loc, diagForTrylessThrowingCall); } - maybeAddRethrowsNote(TC, loc, reason); + maybeAddRethrowsNote(Diags, loc, reason); } - void diagnoseUnhandledThrowSite(TypeChecker &TC, ASTNode E, bool isTryCovered, + void diagnoseUnhandledThrowSite(DiagnosticEngine &Diags, ASTNode E, + bool isTryCovered, const PotentialReason &reason) { switch (getKind()) { case Kind::Handled: @@ -1103,64 +1105,64 @@ class Context { // notes for the throw sites. case Kind::RethrowingFunction: - diagnoseThrowInLegalContext(TC, E, isTryCovered, reason, + diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throw_in_rethrows_function, diag::throwing_call_in_rethrows_function, diag::tryless_throwing_call_in_rethrows_function); return; case Kind::NonThrowingFunction: - diagnoseThrowInLegalContext(TC, E, isTryCovered, reason, + diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throw_in_nonthrowing_function, diag::throwing_call_unhandled, diag::tryless_throwing_call_unhandled); return; case Kind::NonThrowingAutoClosure: - diagnoseThrowInLegalContext(TC, E, isTryCovered, reason, + diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throw_in_nonthrowing_autoclosure, diag::throwing_call_in_nonthrowing_autoclosure, diag::tryless_throwing_call_in_nonthrowing_autoclosure); return; case Kind::NonExhaustiveCatch: - diagnoseThrowInLegalContext(TC, E, isTryCovered, reason, + diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throw_in_nonexhaustive_catch, diag::throwing_call_in_nonexhaustive_catch, diag::tryless_throwing_call_in_nonexhaustive_catch); return; case Kind::EnumElementInitializer: - diagnoseThrowInIllegalContext(TC, E, "an enum case raw value"); + diagnoseThrowInIllegalContext(Diags, E, "an enum case raw value"); return; case Kind::GlobalVarInitializer: - diagnoseThrowInIllegalContext(TC, E, "a global variable initializer"); + diagnoseThrowInIllegalContext(Diags, E, "a global variable initializer"); return; case Kind::IVarInitializer: - diagnoseThrowInIllegalContext(TC, E, "a property initializer"); + diagnoseThrowInIllegalContext(Diags, E, "a property initializer"); return; case Kind::DefaultArgument: - diagnoseThrowInIllegalContext(TC, E, "a default argument"); + diagnoseThrowInIllegalContext(Diags, E, "a default argument"); return; case Kind::CatchPattern: - diagnoseThrowInIllegalContext(TC, E, "a catch pattern"); + diagnoseThrowInIllegalContext(Diags, E, "a catch pattern"); return; case Kind::CatchGuard: - diagnoseThrowInIllegalContext(TC, E, "a catch guard expression"); + diagnoseThrowInIllegalContext(Diags, E, "a catch guard expression"); return; case Kind::DeferBody: - diagnoseThrowInIllegalContext(TC, E, "a defer body", isInDefer); + diagnoseThrowInIllegalContext(Diags, E, "a defer body", isInDefer); return; } llvm_unreachable("bad context kind"); } - void diagnoseUnhandledTry(TypeChecker &TC, TryExpr *E) { + void diagnoseUnhandledTry(DiagnosticEngine &Diags, TryExpr *E) { switch (getKind()) { case Kind::Handled: case Kind::RethrowingFunction: @@ -1168,12 +1170,13 @@ class Context { case Kind::NonThrowingFunction: if (DiagnoseErrorOnTry) - TC.diagnose(E->getTryLoc(), diag::try_unhandled); + Diags.diagnose(E->getTryLoc(), diag::try_unhandled); return; case Kind::NonExhaustiveCatch: if (DiagnoseErrorOnTry) - TC.diagnose(E->getTryLoc(), diag::try_unhandled_in_nonexhaustive_catch); + Diags.diagnose(E->getTryLoc(), + diag::try_unhandled_in_nonexhaustive_catch); return; case Kind::NonThrowingAutoClosure: @@ -1197,7 +1200,7 @@ class Context { class CheckErrorCoverage : public ErrorHandlingWalker { friend class ErrorHandlingWalker; - TypeChecker &TC; + ASTContext &Ctx; ApplyClassifier Classifier; @@ -1334,8 +1337,8 @@ class CheckErrorCoverage : public ErrorHandlingWalker { }; public: - CheckErrorCoverage(TypeChecker &tc, Context initialContext) - : TC(tc), CurContext(initialContext), + CheckErrorCoverage(ASTContext &ctx, Context initialContext) + : Ctx(ctx), CurContext(initialContext), MaxThrowingKind(ThrowingKind::None) { if (auto rethrowsDC = initialContext.getRethrowsDC()) { @@ -1413,8 +1416,8 @@ class CheckErrorCoverage : public ErrorHandlingWalker { // implicit do/catch in a debugger function. if (!Flags.has(ContextFlags::HasAnyThrowSite) && !scope.wasTopLevelDebuggerFunction()) { - TC.diagnose(S->getCatches().front()->getCatchLoc(), - diag::no_throw_in_do_with_catch); + Ctx.Diags.diagnose(S->getCatches().front()->getCatchLoc(), + diag::no_throw_in_do_with_catch); } } @@ -1548,10 +1551,10 @@ class CheckErrorCoverage : public ErrorHandlingWalker { bool isTryCovered = (!requiresTry || Flags.has(ContextFlags::IsTryCovered)); if (!CurContext.handles(classification.getResult())) { - CurContext.diagnoseUnhandledThrowSite(TC, E, isTryCovered, + CurContext.diagnoseUnhandledThrowSite(Ctx.Diags, E, isTryCovered, classification.getThrowsReason()); } else if (!isTryCovered) { - CurContext.diagnoseUncoveredThrowSite(TC, E, + CurContext.diagnoseUncoveredThrowSite(Ctx, E, classification.getThrowsReason()); } return; @@ -1569,12 +1572,12 @@ class CheckErrorCoverage : public ErrorHandlingWalker { // Warn about 'try' expressions that weren't actually needed. if (!Flags.has(ContextFlags::HasTryThrowSite)) { if (!E->isImplicit()) - TC.diagnose(E->getTryLoc(), diag::no_throw_in_try); + Ctx.Diags.diagnose(E->getTryLoc(), diag::no_throw_in_try); // Diagnose all the call sites within a single unhandled 'try' // at the same time. } else if (CurContext.handlesNothing()) { - CurContext.diagnoseUnhandledTry(TC, E); + CurContext.diagnoseUnhandledTry(Ctx.Diags, E); } scope.preserveCoverageFromTryOperand(); @@ -1590,7 +1593,7 @@ class CheckErrorCoverage : public ErrorHandlingWalker { // Warn about 'try' expressions that weren't actually needed. if (!Flags.has(ContextFlags::HasTryThrowSite)) { - TC.diagnose(E->getLoc(), diag::no_throw_in_try); + Ctx.Diags.diagnose(E->getLoc(), diag::no_throw_in_try); } return ShouldNotRecurse; } @@ -1604,7 +1607,7 @@ class CheckErrorCoverage : public ErrorHandlingWalker { // Warn about 'try' expressions that weren't actually needed. if (!Flags.has(ContextFlags::HasTryThrowSite)) { - TC.diagnose(E->getLoc(), diag::no_throw_in_try); + Ctx.Diags.diagnose(E->getLoc(), diag::no_throw_in_try); } return ShouldNotRecurse; } @@ -1613,10 +1616,11 @@ class CheckErrorCoverage : public ErrorHandlingWalker { } // end anonymous namespace void TypeChecker::checkTopLevelErrorHandling(TopLevelCodeDecl *code) { - CheckErrorCoverage checker(*this, Context::forTopLevelCode(code)); + auto &ctx = code->getDeclContext()->getASTContext(); + CheckErrorCoverage checker(ctx, Context::forTopLevelCode(code)); // In some language modes, we allow top-level code to omit 'try' marking. - if (Context.LangOpts.EnableThrowWithoutTry) + if (ctx.LangOpts.EnableThrowWithoutTry) checker.setTopLevelThrowWithoutTry(); code->getBody()->walk(checker); @@ -1634,7 +1638,8 @@ void TypeChecker::checkFunctionErrorHandling(AbstractFunctionDecl *fn) { auto isDeferBody = isa(fn) && cast(fn)->isDeferBody(); auto context = isDeferBody ? Context::forDeferBody() : Context::forFunction(fn); - CheckErrorCoverage checker(*this, context); + auto &ctx = fn->getASTContext(); + CheckErrorCoverage checker(ctx, context); // If this is a debugger function, suppress 'try' marking at the top level. if (fn->getAttrs().hasAttribute()) @@ -1650,7 +1655,8 @@ void TypeChecker::checkFunctionErrorHandling(AbstractFunctionDecl *fn) { void TypeChecker::checkInitializerErrorHandling(Initializer *initCtx, Expr *init) { - CheckErrorCoverage checker(*this, Context::forInitializer(initCtx)); + auto &ctx = initCtx->getASTContext(); + CheckErrorCoverage checker(ctx, Context::forInitializer(initCtx)); init->walk(checker); } @@ -1663,12 +1669,14 @@ void TypeChecker::checkInitializerErrorHandling(Initializer *initCtx, /// perhaps accidentally, and (2) allows the verifier to assert that /// all calls have been checked. void TypeChecker::checkEnumElementErrorHandling(EnumElementDecl *elt, Expr *E) { - CheckErrorCoverage checker(*this, Context::forEnumElementInitializer(elt)); + auto &ctx = elt->getASTContext(); + CheckErrorCoverage checker(ctx, Context::forEnumElementInitializer(elt)); E->walk(checker); } void TypeChecker::checkPropertyWrapperErrorHandling( PatternBindingDecl *binding, Expr *expr) { - CheckErrorCoverage checker(*this, Context::forPatternBinding(binding)); + auto &ctx = binding->getASTContext(); + CheckErrorCoverage checker(ctx, Context::forPatternBinding(binding)); expr->walk(checker); } diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index aa3c557fc6575..4b29c80c079bd 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -237,7 +237,7 @@ Expr *TypeChecker::findLHS(DeclContext *DC, Expr *E, Identifier name) { if (!left) // LHS is not binary expression. return E; - switch (Context.associateInfixOperators(left, right)) { + switch (DC->getASTContext().associateInfixOperators(left, right)) { case swift::Associativity::None: return nullptr; case swift::Associativity::Left: @@ -267,7 +267,7 @@ Expr *TypeChecker::findLHS(DeclContext *DC, Expr *E, Identifier name) { // The way we compute isEndOfSequence relies on the assumption that // the sequence-folding algorithm never recurses with a prefix of the // entire sequence. -static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS, +static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS, PrecedenceGroupDecl *opPrecedence, bool isEndOfSequence) { if (!LHS || !RHS) @@ -330,16 +330,17 @@ static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS, (opPrecedence && opPrecedence->isAssignment())) { if (!isEndOfSequence) { if (isa(Op)) { - TC.diagnose(RHS->getStartLoc(), diag::try_if_rhs_noncovering, - static_cast(tryKind)); + Ctx.Diags.diagnose(RHS->getStartLoc(), diag::try_if_rhs_noncovering, + static_cast(tryKind)); } else { - TC.diagnose(RHS->getStartLoc(), diag::try_assign_rhs_noncovering, - static_cast(tryKind)); + Ctx.Diags.diagnose(RHS->getStartLoc(), + diag::try_assign_rhs_noncovering, + static_cast(tryKind)); } } } else { - TC.diagnose(RHS->getStartLoc(), diag::try_rhs, - static_cast(tryKind)); + Ctx.Diags.diagnose(RHS->getStartLoc(), diag::try_rhs, + static_cast(tryKind)); } } @@ -390,9 +391,9 @@ static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS, // Build the argument to the operation. Expr *ArgElts[] = { LHS, RHS }; - auto ArgElts2 = TC.Context.AllocateCopy(MutableArrayRef(ArgElts)); - TupleExpr *Arg = TupleExpr::create(TC.Context, - SourceLoc(), + auto ArgElts2 = Ctx.AllocateCopy(MutableArrayRef(ArgElts)); + TupleExpr *Arg = TupleExpr::create(Ctx, + SourceLoc(), ArgElts2, { }, { }, SourceLoc(), /*HasTrailingClosure=*/false, /*Implicit=*/true); @@ -400,7 +401,7 @@ static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS, // Build the operation. - return makeResultExpr(new (TC.Context) BinaryExpr(Op, Arg, Op->isImplicit())); + return makeResultExpr(new (Ctx) BinaryExpr(Op, Arg, Op->isImplicit())); } namespace { @@ -411,12 +412,12 @@ namespace { PrecedenceBound(PrecedenceGroupDecl *decl, bool isStrict) : GroupAndIsStrict(decl, isStrict) {} - bool shouldConsider(TypeChecker &TC, PrecedenceGroupDecl *group) { + bool shouldConsider(PrecedenceGroupDecl *group) { auto storedGroup = GroupAndIsStrict.getPointer(); if (!storedGroup) return true; if (!group) return false; if (storedGroup == group) return !GroupAndIsStrict.getInt(); - return TC.Context.associateInfixOperators(group, storedGroup) + return group->getASTContext().associateInfixOperators(group, storedGroup) != Associativity::Right; } }; @@ -424,7 +425,7 @@ namespace { /// foldSequence - Take a sequence of expressions and fold a prefix of /// it into a tree of BinaryExprs using precedence parsing. -static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, +static Expr *foldSequence(DeclContext *DC, Expr *LHS, ArrayRef &S, PrecedenceBound precedenceBound) { @@ -446,7 +447,7 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, // If the operator's precedence is lower than the minimum, stop here. auto opPrecedence = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, op); - if (!precedenceBound.shouldConsider(TC, opPrecedence)) + if (!precedenceBound.shouldConsider(opPrecedence)) return {nullptr, nullptr}; return {op, opPrecedence}; }; @@ -459,15 +460,16 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, // Pull out the prospective RHS and slice off the first two elements. Expr *RHS = S[1]; S = S.slice(2); - + + auto &Ctx = DC->getASTContext(); while (!S.empty()) { assert((S.size() & 1) == 0); - assert(precedenceBound.shouldConsider(TC, op1.precedence)); + assert(precedenceBound.shouldConsider(op1.precedence)); // If the operator is a cast operator, the RHS can't extend past the type // that's part of the cast production. if (isa(op1.op)) { - LHS = makeBinOp(TC, op1.op, LHS, RHS, op1.precedence, S.empty()); + LHS = makeBinOp(Ctx, op1.op, LHS, RHS, op1.precedence, S.empty()); op1 = getNextOperator(); if (!op1) return LHS; RHS = S[1]; @@ -480,7 +482,7 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, // If the second operator's precedence is lower than the // precedence bound, break out of the loop. - if (!precedenceBound.shouldConsider(TC, op2.precedence)) break; + if (!precedenceBound.shouldConsider(op2.precedence)) break; // If we're missing precedence info for either operator, treat them // as non-associative. @@ -489,13 +491,13 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, associativity = Associativity::None; } else { associativity = - TC.Context.associateInfixOperators(op1.precedence, op2.precedence); + Ctx.associateInfixOperators(op1.precedence, op2.precedence); } // Apply left-associativity immediately by folding the first two // operands. if (associativity == Associativity::Left) { - LHS = makeBinOp(TC, op1.op, LHS, RHS, op1.precedence, S.empty()); + LHS = makeBinOp(Ctx, op1.op, LHS, RHS, op1.precedence, S.empty()); op1 = op2; RHS = S[1]; S = S.slice(2); @@ -508,7 +510,7 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, // repeat. if (associativity == Associativity::Right && op1.precedence != op2.precedence) { - RHS = foldSequence(TC, DC, RHS, S, + RHS = foldSequence(DC, RHS, S, PrecedenceBound(op1.precedence, /*strict*/ true)); continue; } @@ -516,15 +518,15 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, // Apply right-associativity by recursively folding operators // starting from this point, then immediately folding the LHS and RHS. if (associativity == Associativity::Right) { - RHS = foldSequence(TC, DC, RHS, S, + RHS = foldSequence(DC, RHS, S, PrecedenceBound(op1.precedence, /*strict*/ false)); - LHS = makeBinOp(TC, op1.op, LHS, RHS, op1.precedence, S.empty()); + LHS = makeBinOp(Ctx, op1.op, LHS, RHS, op1.precedence, S.empty()); // If we've drained the entire sequence, we're done. if (S.empty()) return LHS; // Otherwise, start all over with our new LHS. - return foldSequence(TC, DC, LHS, S, precedenceBound); + return foldSequence(DC, LHS, S, precedenceBound); } // If we ended up here, it's because we're either: @@ -540,52 +542,60 @@ static Expr *foldSequence(TypeChecker &TC, DeclContext *DC, } else if (op1.precedence == op2.precedence) { assert(op1.precedence->isNonAssociative()); // FIXME: QoI ranges - TC.diagnose(op1.op->getLoc(), diag::non_associative_adjacent_operators, - op1.precedence->getName()) + Ctx.Diags.diagnose(op1.op->getLoc(), + diag::non_associative_adjacent_operators, + op1.precedence->getName()) .highlight(SourceRange(op2.op->getLoc(), op2.op->getLoc())); } else { - TC.diagnose(op1.op->getLoc(), diag::unordered_adjacent_operators, - op1.precedence->getName(), op2.precedence->getName()) + Ctx.Diags.diagnose(op1.op->getLoc(), + diag::unordered_adjacent_operators, + op1.precedence->getName(), op2.precedence->getName()) .highlight(SourceRange(op2.op->getLoc(), op2.op->getLoc())); } // Recover by arbitrarily binding the first two. - LHS = makeBinOp(TC, op1.op, LHS, RHS, op1.precedence, S.empty()); - return foldSequence(TC, DC, LHS, S, precedenceBound); + LHS = makeBinOp(Ctx, op1.op, LHS, RHS, op1.precedence, S.empty()); + return foldSequence(DC, LHS, S, precedenceBound); } // Fold LHS and RHS together and declare completion. - return makeBinOp(TC, op1.op, LHS, RHS, op1.precedence, S.empty()); + return makeBinOp(Ctx, op1.op, LHS, RHS, op1.precedence, S.empty()); } -bool TypeChecker::requireOptionalIntrinsics(SourceLoc loc) { - if (Context.hasOptionalIntrinsics()) return false; +bool TypeChecker::requireOptionalIntrinsics(ASTContext &ctx, SourceLoc loc) { + if (ctx.hasOptionalIntrinsics()) + return false; - diagnose(loc, diag::optional_intrinsics_not_found); + ctx.Diags.diagnose(loc, diag::optional_intrinsics_not_found); return true; } -bool TypeChecker::requirePointerArgumentIntrinsics(SourceLoc loc) { - if (Context.hasPointerArgumentIntrinsics()) return false; +bool TypeChecker::requirePointerArgumentIntrinsics(ASTContext &ctx, + SourceLoc loc) { + if (ctx.hasPointerArgumentIntrinsics()) + return false; - diagnose(loc, diag::pointer_argument_intrinsics_not_found); + ctx.Diags.diagnose(loc, diag::pointer_argument_intrinsics_not_found); return true; } -bool TypeChecker::requireArrayLiteralIntrinsics(SourceLoc loc) { - if (Context.hasArrayLiteralIntrinsics()) return false; - - diagnose(loc, diag::array_literal_intrinsics_not_found); +bool TypeChecker::requireArrayLiteralIntrinsics(ASTContext &ctx, + SourceLoc loc) { + if (ctx.hasArrayLiteralIntrinsics()) + return false; + + ctx.Diags.diagnose(loc, diag::array_literal_intrinsics_not_found); return true; } Expr *TypeChecker::buildCheckedRefExpr(VarDecl *value, DeclContext *UseDC, DeclNameLoc loc, bool Implicit) { - auto type = getUnopenedTypeOfReference(value, Type(), UseDC); + auto type = TypeChecker::getUnopenedTypeOfReference(value, Type(), UseDC); auto semantics = value->getAccessSemanticsFromContext(UseDC, /*isAccessOnSelf*/false); - return new (Context) DeclRefExpr(value, loc, Implicit, semantics, type); + return new (value->getASTContext()) + DeclRefExpr(value, loc, Implicit, semantics, type); } Expr *TypeChecker::buildRefExpr(ArrayRef Decls, @@ -593,6 +603,7 @@ Expr *TypeChecker::buildRefExpr(ArrayRef Decls, bool Implicit, FunctionRefKind functionRefKind) { assert(!Decls.empty() && "Must have at least one declaration"); + auto &Context = UseDC->getASTContext(); if (Decls.size() == 1 && !isa(Decls[0]->getDeclContext())) { auto semantics = Decls[0]->getAccessSemanticsFromContext(UseDC, /*isAccessOnSelf*/false); @@ -606,54 +617,20 @@ Expr *TypeChecker::buildRefExpr(ArrayRef Decls, return result; } -Expr *TypeChecker::buildAutoClosureExpr(DeclContext *DC, Expr *expr, - FunctionType *closureType) { - bool isInDefaultArgumentContext = false; - if (auto *init = dyn_cast(DC)) - isInDefaultArgumentContext = - init->getInitializerKind() == InitializerKind::DefaultArgument; - - auto info = closureType->getExtInfo(); - auto newClosureType = closureType; - - if (isInDefaultArgumentContext && info.isNoEscape()) - newClosureType = closureType->withExtInfo(info.withNoEscape(false)) - ->castTo(); - - auto *closure = new (Context) AutoClosureExpr( - expr, newClosureType, AutoClosureExpr::InvalidDiscriminator, DC); - - closure->setParameterList(ParameterList::createEmpty(Context)); - - ClosuresWithUncomputedCaptures.push_back(closure); - - if (!newClosureType->isEqual(closureType)) { - assert(isInDefaultArgumentContext); - assert(newClosureType - ->withExtInfo(newClosureType->getExtInfo().withNoEscape(true)) - ->isEqual(closureType)); - return new (Context) FunctionConversionExpr(closure, closureType); - } - - return closure; -} - -static Type lookupDefaultLiteralType(TypeChecker &TC, const DeclContext *dc, +static Type lookupDefaultLiteralType(const DeclContext *dc, StringRef name) { + auto &ctx = dc->getASTContext(); auto lookupOptions = defaultUnqualifiedLookupOptions; if (isa(dc)) lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupUnqualified(dc->getModuleScopeContext(), - TC.Context.getIdentifier(name), - SourceLoc(), - lookupOptions); + DeclNameRef nameRef(ctx.getIdentifier(name)); + auto lookup = TypeChecker::lookupUnqualified(dc->getModuleScopeContext(), + nameRef, SourceLoc(), + lookupOptions); TypeDecl *TD = lookup.getSingleTypeResult(); if (!TD) return Type(); - // FIXME: Make isInvalid ask for the interface type. - (void)TD->getInterfaceType(); - if (TD->isInvalid()) return Type(); @@ -664,10 +641,10 @@ static Type lookupDefaultLiteralType(TypeChecker &TC, const DeclContext *dc, static Optional getKnownProtocolKindIfAny(const ProtocolDecl *protocol) { - TypeChecker &tc = TypeChecker::createForContext(protocol->getASTContext()); - #define EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(Id, _, __, ___) \ - if (protocol == tc.getProtocol(SourceLoc(), KnownProtocolKind::Id)) \ + if (protocol == TypeChecker::getProtocol(protocol->getASTContext(), \ + SourceLoc(), \ + KnownProtocolKind::Id)) \ return KnownProtocolKind::Id; #include "swift/AST/KnownProtocols.def" #undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME @@ -678,7 +655,7 @@ getKnownProtocolKindIfAny(const ProtocolDecl *protocol) { Type TypeChecker::getDefaultType(ProtocolDecl *protocol, DeclContext *dc) { if (auto knownProtocolKindIfAny = getKnownProtocolKindIfAny(protocol)) { return evaluateOrDefault( - Context.evaluator, + protocol->getASTContext().evaluator, DefaultTypeRequest{knownProtocolKindIfAny.getValue(), dc}, nullptr); } return Type(); @@ -709,15 +686,12 @@ swift::DefaultTypeRequest::evaluate(Evaluator &evaluator, if (!name) return nullptr; - // FIXME: Creating a whole type checker just to do lookup is unnecessary. - TypeChecker &tc = TypeChecker::createForContext(dc->getASTContext()); - Type type; if (performLocalLookup) - type = lookupDefaultLiteralType(tc, dc, name); + type = lookupDefaultLiteralType(dc, name); if (!type) - type = lookupDefaultLiteralType(tc, tc.getStdlibModule(dc), name); + type = lookupDefaultLiteralType(TypeChecker::getStdlibModule(dc), name); // Strip off one level of sugar; we don't actually want to print // the name of the typealias itself anywhere. @@ -736,8 +710,93 @@ Expr *TypeChecker::foldSequence(SequenceExpr *expr, DeclContext *dc) { Expr *LHS = Elts[0]; Elts = Elts.slice(1); - Expr *Result = ::foldSequence(*this, dc, LHS, Elts, PrecedenceBound()); + Expr *Result = ::foldSequence(dc, LHS, Elts, PrecedenceBound()); assert(Elts.empty()); return Result; } + +static Expr *synthesizeCallerSideDefault(const ParamDecl *param, + SourceLoc loc) { + auto &ctx = param->getASTContext(); + switch (param->getDefaultArgumentKind()) { + case DefaultArgumentKind::Column: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::Column, loc, + /*implicit=*/true); + + case DefaultArgumentKind::File: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::File, loc, + /*implicit=*/true); + + case DefaultArgumentKind::FilePath: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::FilePath, loc, + /*implicit=*/true); + + case DefaultArgumentKind::Line: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::Line, loc, + /*implicit=*/true); + + case DefaultArgumentKind::Function: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::Function, loc, + /*implicit=*/true); + + case DefaultArgumentKind::DSOHandle: + return new (ctx) + MagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr::DSOHandle, loc, + /*implicit=*/true); + + case DefaultArgumentKind::NilLiteral: + return new (ctx) NilLiteralExpr(loc, /*Implicit=*/true); + break; + + case DefaultArgumentKind::EmptyArray: { + auto *initExpr = ArrayExpr::create(ctx, loc, {}, {}, loc); + initExpr->setImplicit(); + return initExpr; + } + case DefaultArgumentKind::EmptyDictionary: { + auto *initExpr = DictionaryExpr::create(ctx, loc, {}, {}, loc); + initExpr->setImplicit(); + return initExpr; + } + case DefaultArgumentKind::None: + case DefaultArgumentKind::Normal: + case DefaultArgumentKind::Inherited: + case DefaultArgumentKind::StoredProperty: + llvm_unreachable("Not a caller-side default"); + } + llvm_unreachable("Unhandled case in switch"); +} + +llvm::Expected CallerSideDefaultArgExprRequest::evaluate( + Evaluator &evaluator, DefaultArgumentExpr *defaultExpr) const { + auto *param = defaultExpr->getParamDecl(); + auto paramTy = defaultExpr->getType(); + + // Re-create the default argument using the location info of the call site. + auto *initExpr = synthesizeCallerSideDefault(param, defaultExpr->getLoc()); + auto *dc = defaultExpr->ContextOrCallerSideExpr.get(); + assert(dc && "Expected a DeclContext before type-checking caller-side arg"); + + auto &ctx = param->getASTContext(); + DiagnosticTransaction transaction(ctx.Diags); + if (!TypeChecker::typeCheckParameterDefault(initExpr, dc, paramTy, + param->isAutoClosure())) { + if (param->hasDefaultExpr()) { + // HACK: If we were unable to type-check the default argument in context, + // then retry by type-checking it within the parameter decl, which should + // also fail. This will present the user with a better error message and + // allow us to avoid diagnosing on each call site. + transaction.abort(); + (void)param->getTypeCheckedDefaultExpr(); + assert(ctx.Diags.hadAnyError()); + } + return new (ctx) ErrorExpr(initExpr->getSourceRange(), paramTy); + } + return initExpr; +} diff --git a/lib/Sema/TypeCheckExprObjC.cpp b/lib/Sema/TypeCheckExprObjC.cpp index 74710b189877d..d29415397e7d3 100644 --- a/lib/Sema/TypeCheckExprObjC.cpp +++ b/lib/Sema/TypeCheckExprObjC.cpp @@ -30,8 +30,10 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, if (expr->getObjCStringLiteralExpr() && !requireResultType) return None; // ObjC #keyPath only makes sense when we have the Objective-C runtime. + auto &Context = dc->getASTContext(); + auto &diags = Context.Diags; if (!Context.LangOpts.EnableObjCInterop) { - diagnose(expr->getLoc(), diag::expr_keypath_no_objc_runtime); + diags.diagnose(expr->getLoc(), diag::expr_keypath_no_objc_runtime); expr->setObjCStringLiteralExpr( new (Context) StringLiteralExpr("", expr->getSourceRange(), @@ -160,7 +162,7 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, }; // Local function to perform name lookup for the current index. - auto performLookup = [&](DeclBaseName componentName, + auto performLookup = [&](DeclNameRef componentName, SourceLoc componentNameLoc, Type &lookupType) -> LookupResult { if (state == Beginning) @@ -184,7 +186,7 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // Local function to print a component to the string. bool needDot = false; - auto printComponent = [&](DeclBaseName component) { + auto printComponent = [&](DeclName component) { if (needDot) keyPathOS << "."; else @@ -212,9 +214,9 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, case KeyPathExpr::Component::Kind::OptionalChain: case KeyPathExpr::Component::Kind::OptionalForce: case KeyPathExpr::Component::Kind::TupleElement: - diagnose(componentNameLoc, - diag::expr_unsupported_objc_key_path_component, - (unsigned)kind); + diags.diagnose(componentNameLoc, + diag::expr_unsupported_objc_key_path_component, + (unsigned)kind); continue; case KeyPathExpr::Component::Kind::OptionalWrap: case KeyPathExpr::Component::Kind::Property: @@ -222,20 +224,19 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, llvm_unreachable("already resolved!"); } - auto componentFullName = component.getUnresolvedDeclName(); - if (!componentFullName.isSimpleName()) { - diagnose(componentNameLoc, - diag::expr_unsupported_objc_key_path_compound_name); + auto componentName = component.getUnresolvedDeclName(); + if (!componentName.isSimpleName()) { + diags.diagnose(componentNameLoc, + diag::expr_unsupported_objc_key_path_compound_name); continue; } - auto componentName = componentFullName.getBaseName(); // If we are resolving into a dictionary, any component is // well-formed because the keys are unknown dynamically. if (state == ResolvingDictionary) { // Just print the component unchanged; there's no checking we // can do here. - printComponent(componentName); + printComponent(componentName.getBaseName()); // From here, we're resolving a property. Use the current type. updateState(/*isProperty=*/true, currentType); @@ -251,19 +252,19 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // If we didn't find anything, try to apply typo-correction. bool resultsAreFromTypoCorrection = false; if (!lookup) { - TypoCorrectionResults corrections(*this, componentName, + TypoCorrectionResults corrections(componentName, DeclNameLoc(componentNameLoc)); - performTypoCorrection(dc, DeclRefKind::Ordinary, lookupType, + TypeChecker::performTypoCorrection(dc, DeclRefKind::Ordinary, lookupType, (lookupType ? defaultMemberTypeLookupOptions : defaultUnqualifiedLookupOptions), corrections); if (currentType) - diagnose(componentNameLoc, diag::could_not_find_type_member, - currentType, componentName); + diags.diagnose(componentNameLoc, diag::could_not_find_type_member, + currentType, componentName); else - diagnose(componentNameLoc, diag::use_unresolved_identifier, - componentName, false); + diags.diagnose(componentNameLoc, diag::use_unresolved_identifier, + componentName, false); // Note all the correction candidates. corrections.noteAllCandidates(); @@ -300,15 +301,15 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, break; if (lookupType) - diagnose(componentNameLoc, diag::ambiguous_member_overload_set, - componentName); + diags.diagnose(componentNameLoc, diag::ambiguous_member_overload_set, + componentName); else - diagnose(componentNameLoc, diag::ambiguous_decl_ref, - componentName); + diags.diagnose(componentNameLoc, diag::ambiguous_decl_ref, + componentName); for (auto result : lookup) { - diagnose(result.getValueDecl(), diag::decl_declared_here, - result.getValueDecl()->getFullName()); + diags.diagnose(result.getValueDecl(), diag::decl_declared_here, + result.getValueDecl()->getFullName()); } isInvalid = true; break; @@ -327,11 +328,11 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // Check that the property is @objc. if (!var->isObjC()) { - diagnose(componentNameLoc, diag::expr_keypath_non_objc_property, - componentName); + diags.diagnose(componentNameLoc, diag::expr_keypath_non_objc_property, + var->getFullName()); if (var->getLoc().isValid() && var->getDeclContext()->isTypeContext()) { - diagnose(var, diag::make_decl_objc, - var->getDescriptiveKind()) + diags.diagnose(var, diag::make_decl_objc, + var->getDescriptiveKind()) .fixItInsert(var->getAttributeInsertionLoc(false), "@objc "); } @@ -341,10 +342,12 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, if (attr->isSwift3Inferred() && Context.LangOpts.WarnSwift3ObjCInference == Swift3ObjCInferenceWarnings::Minimal) { - diagnose(componentNameLoc, diag::expr_keypath_swift3_objc_inference, - var->getFullName(), - var->getDeclContext()->getSelfNominalTypeDecl()->getName()); - diagnose(var, diag::make_decl_objc, var->getDescriptiveKind()) + auto *parent = var->getDeclContext()->getSelfNominalTypeDecl(); + diags.diagnose(componentNameLoc, + diag::expr_keypath_swift3_objc_inference, + var->getFullName(), + parent->getName()); + diags.diagnose(var, diag::make_decl_objc, var->getDescriptiveKind()) .fixItInsert(var->getAttributeInsertionLoc(false), "@objc "); } @@ -361,16 +364,16 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, if (auto type = dyn_cast(found)) { // We cannot refer to a type via a property. if (isResolvingProperty()) { - diagnose(componentNameLoc, diag::expr_keypath_type_of_property, - componentName, currentType); + diags.diagnose(componentNameLoc, diag::expr_keypath_type_of_property, + componentName, currentType); isInvalid = true; break; } // We cannot refer to a generic type. if (type->getDeclaredInterfaceType()->hasTypeParameter()) { - diagnose(componentNameLoc, diag::expr_keypath_generic_type, - componentName); + diags.diagnose(componentNameLoc, diag::expr_keypath_generic_type, + type->getFullName()); isInvalid = true; break; } @@ -392,9 +395,9 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, } // Declarations that cannot be part of a key-path. - diagnose(componentNameLoc, diag::expr_keypath_not_property, - found->getDescriptiveKind(), found->getFullName(), - /*isForDynamicKeyPathMemberLookup=*/false); + diags.diagnose(componentNameLoc, diag::expr_keypath_not_property, + found->getDescriptiveKind(), found->getFullName(), + /*isForDynamicKeyPathMemberLookup=*/false); isInvalid = true; break; } @@ -406,7 +409,7 @@ Optional TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, // Check for an empty key-path string. auto keyPathString = keyPathOS.str(); if (keyPathString.empty() && !isInvalid) - diagnose(expr->getLoc(), diag::expr_keypath_empty); + diags.diagnose(expr->getLoc(), diag::expr_keypath_empty); // Set the string literal expression for the ObjC key path. if (!expr->getObjCStringLiteralExpr()) { diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index cfd442c89044c..70ffd4360b4bb 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -246,6 +246,7 @@ void TypeChecker::checkProtocolSelfRequirements(ValueDecl *decl) { // For a generic requirement in a protocol, make sure that the requirement // set didn't add any requirements to Self or its associated types. if (auto *proto = dyn_cast(decl->getDeclContext())) { + auto &ctx = proto->getASTContext(); auto protoSelf = proto->getSelfInterfaceType(); auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); for (auto req : sig->getRequirements()) { @@ -261,12 +262,12 @@ void TypeChecker::checkProtocolSelfRequirements(ValueDecl *decl) { req.getFirstType()->is()) continue; - diagnose(decl, - diag::requirement_restricts_self, - decl->getDescriptiveKind(), decl->getFullName(), - req.getFirstType().getString(), - static_cast(req.getKind()), - req.getSecondType().getString()); + ctx.Diags.diagnose(decl, + diag::requirement_restricts_self, + decl->getDescriptiveKind(), decl->getFullName(), + req.getFirstType().getString(), + static_cast(req.getKind()), + req.getSecondType().getString()); } } } @@ -437,9 +438,8 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) { continue; } // Produce an error that this generic parameter cannot be bound. - diagnose(paramDecl->getLoc(), diag::unreferenced_generic_parameter, - paramDecl->getNameStr()); - decl->setInterfaceType(ErrorType::get(Context)); + paramDecl->diagnose(diag::unreferenced_generic_parameter, + paramDecl->getNameStr()); decl->setInvalid(); } } @@ -467,7 +467,7 @@ GenericSignature TypeChecker::checkGenericSignature( // Debugging of the generic signature builder and generic signature // generation. - if (dc->getASTContext().LangOpts.DebugGenericSignatures) { + if (dc->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { if (auto *VD = dyn_cast_or_null(dc->getAsDecl())) { VD->dumpRef(llvm::errs()); } else { @@ -478,7 +478,7 @@ GenericSignature TypeChecker::checkGenericSignature( sig->print(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Canonical generic signature: "; - sig->getCanonicalSignature()->print(llvm::errs()); + sig.getCanonicalSignature()->print(llvm::errs()); llvm::errs() << "\n"; } @@ -589,14 +589,14 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, // Debugging of the generic signature builder and generic signature // generation. - if (GC->getASTContext().LangOpts.DebugGenericSignatures) { + if (GC->getASTContext().TypeCheckerOpts.DebugGenericSignatures) { PD->printContext(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Generic signature: "; sig->print(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Canonical generic signature: "; - sig->getCanonicalSignature()->print(llvm::errs()); + sig.getCanonicalSignature()->print(llvm::errs()); llvm::errs() << "\n"; } return sig; @@ -803,12 +803,10 @@ RequirementCheckResult TypeChecker::checkGenericArguments( // FIXME: Poor location information. How much better can we do here? // FIXME: This call should support listener to be able to properly // diagnose problems with conformances. - auto result = - conformsToProtocol(firstType, proto->getDecl(), dc, - conformanceOptions, loc); + auto conformance = conformsToProtocol(firstType, proto->getDecl(), dc, + conformanceOptions, loc); - if (result) { - auto conformance = *result; + if (conformance) { // Report the conformance. if (listener && valid && current.Parents.empty()) { listener->satisfiedConformance(rawFirstType, firstType, diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 6cfc142cca7e0..707d454122c5e 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -20,53 +20,14 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Initializer.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SourceFile.h" #include "swift/Basic/TopCollection.h" #include using namespace swift; -void LookupResult::filter( - llvm::function_ref pred) { - size_t index = 0; - size_t originalFirstOuter = IndexOfFirstOuterResult; - Results.erase(std::remove_if(Results.begin(), Results.end(), - [&](LookupResultEntry result) -> bool { - auto isInner = index < originalFirstOuter; - index++; - if (pred(result, !isInner)) - return false; - - // Need to remove this, which means, if it is - // an inner result, the outer results need to - // shift down. - if (isInner) - IndexOfFirstOuterResult--; - return true; - }), - Results.end()); -} - -void LookupResult::shiftDownResults() { - // Remove inner results. - Results.erase(Results.begin(), Results.begin() + IndexOfFirstOuterResult); - IndexOfFirstOuterResult = 0; - - if (Results.empty()) - return; - - // Compute IndexOfFirstOuterResult. - const DeclContext *dcInner = Results.front().getValueDecl()->getDeclContext(); - for (auto &&result : Results) { - const DeclContext *dc = result.getValueDecl()->getDeclContext(); - if (dc == dcInner || - (dc->isModuleScopeContext() && dcInner->isModuleScopeContext())) - ++IndexOfFirstOuterResult; - else - break; - } -} - namespace { /// Builder that helps construct a lookup result from the raw lookup /// data. @@ -200,7 +161,7 @@ namespace { auto conformance = TypeChecker::conformsToProtocol(conformingType, foundProto, DC, conformanceOptions); - if (!conformance) { + if (conformance.isInvalid()) { // If there's no conformance, we have an existential // and we found a member from one of the protocols, and // not a class constraint if any. @@ -210,7 +171,7 @@ namespace { return; } - if (conformance->isAbstract()) { + if (conformance.isAbstract()) { assert(foundInType->is() || foundInType->isExistentialType()); addResult(found); @@ -219,10 +180,9 @@ namespace { // Dig out the witness. ValueDecl *witness = nullptr; - auto concrete = conformance->getConcrete(); + auto concrete = conformance.getConcrete(); if (auto assocType = dyn_cast(found)) { - witness = concrete->getTypeWitnessAndDecl(assocType) - .second; + witness = concrete->getTypeWitnessAndDecl(assocType).getWitnessDecl(); } else if (found->isProtocolRequirement()) { witness = concrete->getWitnessDecl(found); @@ -235,6 +195,12 @@ namespace { addResult(found); return; } + } else if (isa(found)) { + // Declaring nested types inside other types is currently + // not supported by lookup would still return such members + // so we have to account for that here as well. + addResult(found); + return; } // FIXME: the "isa()" check will be wrong for @@ -249,66 +215,116 @@ namespace { }; } // end anonymous namespace -static UnqualifiedLookup::Options +static UnqualifiedLookupOptions convertToUnqualifiedLookupOptions(NameLookupOptions options) { - UnqualifiedLookup::Options newOptions; + UnqualifiedLookupOptions newOptions; if (options.contains(NameLookupFlags::KnownPrivate)) - newOptions |= UnqualifiedLookup::Flags::KnownPrivate; + newOptions |= UnqualifiedLookupFlags::KnownPrivate; if (options.contains(NameLookupFlags::ProtocolMembers)) - newOptions |= UnqualifiedLookup::Flags::AllowProtocolMembers; + newOptions |= UnqualifiedLookupFlags::AllowProtocolMembers; if (options.contains(NameLookupFlags::IgnoreAccessControl)) - newOptions |= UnqualifiedLookup::Flags::IgnoreAccessControl; + newOptions |= UnqualifiedLookupFlags::IgnoreAccessControl; if (options.contains(NameLookupFlags::IncludeOuterResults)) - newOptions |= UnqualifiedLookup::Flags::IncludeOuterResults; + newOptions |= UnqualifiedLookupFlags::IncludeOuterResults; return newOptions; } -LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name, +static void installPropertyWrapperMembersIfNeeded(NominalTypeDecl *target, + DeclName member) { + if (!target) return; + + auto &Context = target->getASTContext(); + auto baseName = member.getBaseName(); + if (!member.isSimpleName() || baseName.isSpecial()) + return; + + if ((!baseName.getIdentifier().str().startswith("$") && + !baseName.getIdentifier().str().startswith("_")) || + baseName.getIdentifier().str().size() <= 1) { + return; + } + + // $- and _-prefixed variables can be generated by properties that have + // attached property wrappers. + auto originalPropertyName = + Context.getIdentifier(baseName.getIdentifier().str().substr(1)); + for (auto member : target->lookupDirect(originalPropertyName)) { + if (auto var = dyn_cast(member)) { + if (var->hasAttachedPropertyWrapper()) { + auto sourceFile = var->getDeclContext()->getParentSourceFile(); + if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) + (void)var->getPropertyWrapperBackingProperty(); + } + } + } +} + +LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclNameRef name, SourceLoc loc, NameLookupOptions options) { - UnqualifiedLookup lookup(name, dc, loc, - convertToUnqualifiedLookupOptions(options)); + auto ulOptions = convertToUnqualifiedLookupOptions(options); + + // Make sure we've resolved implicit members, if we need them. + if (auto *current = dc->getInnermostTypeContext()) { + installPropertyWrapperMembersIfNeeded(current->getSelfNominalTypeDecl(), + name.getFullName()); + } + + auto &ctx = dc->getASTContext(); + auto descriptor = UnqualifiedLookupDescriptor(name, dc, loc, ulOptions); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); LookupResult result; LookupResultBuilder builder(result, dc, options); - for (auto idx : indices(lookup.Results)) { - const auto &found = lookup.Results[idx]; + for (auto idx : indices(lookup.allResults())) { + const auto &found = lookup[idx]; // Determine which type we looked through to find this result. Type foundInType; - if (auto *baseDC = found.getDeclContext()) { - if (!baseDC->isTypeContext()) { - baseDC = baseDC->getParent(); - assert(baseDC->isTypeContext()); + if (auto *typeDC = found.getDeclContext()) { + if (!typeDC->isTypeContext()) { + // If we don't have a type context this is an implicit 'self' reference. + if (auto *CE = dyn_cast(typeDC)) { + // If we found the result in a self capture, look through the capture. + assert(CE->getCapturedSelfDecl()); + typeDC = found.getValueDecl()->getDeclContext(); + } else { + // Otherwise, we must have the method context. + typeDC = typeDC->getParent(); + } + assert(typeDC->isTypeContext()); } foundInType = dc->mapTypeIntoContext( - baseDC->getDeclaredInterfaceType()); + typeDC->getDeclaredInterfaceType()); assert(foundInType && "bogus base declaration?"); } builder.add(found.getValueDecl(), found.getDeclContext(), foundInType, - /*isOuter=*/idx >= lookup.IndexOfFirstOuterResult); + /*isOuter=*/idx >= lookup.getIndexOfFirstOuterResult()); } return result; } LookupResult -TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, +TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclNameRef name, SourceLoc loc, NameLookupOptions options) { + auto &ctx = dc->getASTContext(); auto ulOptions = convertToUnqualifiedLookupOptions(options) | - UnqualifiedLookup::Flags::TypeLookup; + UnqualifiedLookupFlags::TypeLookup; { // Try lookup without ProtocolMembers first. - UnqualifiedLookup lookup( + auto desc = UnqualifiedLookupDescriptor( name, dc, loc, - ulOptions - UnqualifiedLookup::Flags::AllowProtocolMembers); + ulOptions - UnqualifiedLookupFlags::AllowProtocolMembers); - if (!lookup.Results.empty() || - !options.contains(NameLookupFlags::ProtocolMembers)) { - return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); - } + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + if (!lookup.allResults().empty() || + !options.contains(NameLookupFlags::ProtocolMembers)) + return lookup; } { @@ -317,16 +333,15 @@ TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclName name, // FIXME: Fix the problem where if NominalTypeDecl::getAllProtocols() // is called too early, we start resolving extensions -- even those // which do provide not conformances. - UnqualifiedLookup lookup( + auto desc = UnqualifiedLookupDescriptor( name, dc, loc, - ulOptions | UnqualifiedLookup::Flags::AllowProtocolMembers); - - return LookupResult(lookup.Results, lookup.IndexOfFirstOuterResult); + ulOptions | UnqualifiedLookupFlags::AllowProtocolMembers); + return evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); } } LookupResult TypeChecker::lookupMember(DeclContext *dc, - Type type, DeclName name, + Type type, DeclNameRef name, NameLookupOptions options) { assert(type->mayHaveMembers()); @@ -347,6 +362,12 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, subOptions &= ~NL_RemoveOverridden; subOptions &= ~NL_RemoveNonVisible; + // Make sure we've resolved implicit members, if we need them. + if (auto *current = type->getAnyNominal()) { + current->synthesizeSemanticMembersIfNeeded(name.getFullName()); + installPropertyWrapperMembersIfNeeded(current, name.getFullName()); + } + LookupResultBuilder builder(result, dc, options); SmallVector lookupResults; dc->lookupQualified(type, name, subOptions, lookupResults); @@ -406,7 +427,7 @@ bool TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) { } LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, - Type type, Identifier name, + Type type, DeclNameRef name, NameLookupOptions options) { LookupTypeResult result; @@ -421,6 +442,12 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, if (options.contains(NameLookupFlags::IgnoreAccessControl)) subOptions |= NL_IgnoreAccessControl; + // Make sure we've resolved implicit members, if we need them. + if (auto *current = type->getAnyNominal()) { + current->synthesizeSemanticMembersIfNeeded(name.getFullName()); + installPropertyWrapperMembersIfNeeded(current, name.getFullName()); + } + if (!dc->lookupQualified(type, name, subOptions, decls)) return result; @@ -430,9 +457,15 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, for (auto decl : decls) { auto *typeDecl = cast(decl); - auto memberType = typeDecl->getDeclaredInterfaceType(); + // HACK: Lookups rooted at a typealias are trying to look for its underlying + // type so they shouldn't also find that same typealias. + if (decl == dyn_cast(dc)) { + continue; + } if (isUnsupportedMemberTypeAccess(type, typeDecl)) { + auto memberType = typeDecl->getDeclaredInterfaceType(); + // Add the type to the result set, so that we can diagnose the // reference instead of just saying the member does not exist. if (types.insert(memberType->getCanonicalType()).second) @@ -477,8 +510,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, } // Substitute the base into the member's type. - memberType = substMemberTypeWithBase(dc->getParentModule(), - typeDecl, type); + auto memberType = substMemberTypeWithBase(dc->getParentModule(), + typeDecl, type); // If we haven't seen this type result yet, add it to the result set. if (types.insert(memberType->getCanonicalType()).second) @@ -506,7 +539,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, } // Use the type witness. - auto concrete = conformance->getConcrete(); + auto concrete = conformance.getConcrete(); // This is the only case where NormalProtocolConformance:: // getTypeWitnessAndDecl() returns a null type. @@ -514,8 +547,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, ProtocolConformanceState::CheckingTypeWitnesses) continue; - auto typeDecl = - concrete->getTypeWitnessAndDecl(assocType).second; + auto *typeDecl = + concrete->getTypeWitnessAndDecl(assocType).getWitnessDecl(); // Circularity. if (!typeDecl) @@ -533,10 +566,10 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, LookupResult TypeChecker::lookupConstructors(DeclContext *dc, Type type, NameLookupOptions options) { - return lookupMember(dc, type, DeclBaseName::createConstructor(), options); + return lookupMember(dc, type, DeclNameRef::createConstructor(), options); } -unsigned TypeChecker::getCallEditDistance(DeclName writtenName, +unsigned TypeChecker::getCallEditDistance(DeclNameRef writtenName, DeclName correctedName, unsigned maxEditDistance) { // TODO: consider arguments. @@ -572,7 +605,7 @@ unsigned TypeChecker::getCallEditDistance(DeclName writtenName, return distance; } -static bool isPlausibleTypo(DeclRefKind refKind, DeclName typedName, +static bool isPlausibleTypo(DeclRefKind refKind, DeclNameRef typedName, ValueDecl *candidate) { // Ignore anonymous declarations. if (!candidate->hasName()) @@ -598,13 +631,12 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, unsigned maxResults) { // Disable typo-correction if we won't show the diagnostic anyway or if // we've hit our typo correction limit. - if (NumTypoCorrections >= getLangOpts().TypoCorrectionLimit || - (Diags.hasFatalErrorOccurred() && - !Diags.getShowDiagnosticsAfterFatalError())) + auto &Ctx = DC->getASTContext(); + if (!Ctx.shouldPerformTypoCorrection() || + (Ctx.Diags.hasFatalErrorOccurred() && + !Ctx.Diags.getShowDiagnosticsAfterFatalError())) return; - ++NumTypoCorrections; - // Fill in a collection of the most reasonable entries. TopCollection entries(maxResults); auto consumer = makeDeclConsumer([&](ValueDecl *decl, @@ -665,7 +697,7 @@ static Decl *findExplicitParentForImplicitDecl(ValueDecl *decl) { } static InFlightDiagnostic -noteTypoCorrection(TypeChecker &tc, DeclNameLoc loc, ValueDecl *decl, +noteTypoCorrection(DeclNameLoc loc, ValueDecl *decl, bool wasClaimed) { if (auto var = dyn_cast(decl)) { // Suggest 'self' at the use point instead of pointing at the start @@ -677,8 +709,9 @@ noteTypoCorrection(TypeChecker &tc, DeclNameLoc loc, ValueDecl *decl, return InFlightDiagnostic(); } - return tc.diagnose(loc.getBaseNameLoc(), diag::note_typo_candidate, - var->getName().str()); + auto &Diags = decl->getASTContext().Diags; + return Diags.diagnose(loc.getBaseNameLoc(), diag::note_typo_candidate, + var->getName().str()); } } @@ -688,24 +721,24 @@ noteTypoCorrection(TypeChecker &tc, DeclNameLoc loc, ValueDecl *decl, isa(decl) ? "method" : "member"); - return tc.diagnose(parentDecl, + return parentDecl->diagnose( wasClaimed ? diag::implicit_member_declared_here : diag::note_typo_candidate_implicit_member, decl->getBaseName().userFacingName(), kind); } if (wasClaimed) { - return tc.diagnose(decl, diag::decl_declared_here, decl->getBaseName()); + return decl->diagnose(diag::decl_declared_here, decl->getBaseName()); } else { - return tc.diagnose(decl, diag::note_typo_candidate, - decl->getBaseName().userFacingName()); + return decl->diagnose(diag::note_typo_candidate, + decl->getBaseName().userFacingName()); } } void TypoCorrectionResults::noteAllCandidates() const { for (auto candidate : Candidates) { auto &&diagnostic = - noteTypoCorrection(TC, Loc, candidate, ClaimedCorrection); + noteTypoCorrection(Loc, candidate, ClaimedCorrection); // Don't add fix-its if we claimed the correction for the primary // diagnostic. diff --git a/lib/Sema/TypeCheckObjC.h b/lib/Sema/TypeCheckObjC.h index adfd6da94e94d..0914b693e7759 100644 --- a/lib/Sema/TypeCheckObjC.h +++ b/lib/Sema/TypeCheckObjC.h @@ -135,11 +135,6 @@ bool isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason); /// Check whether the given declaration can be represented in Objective-C. bool canBeRepresentedInObjC(const ValueDecl *decl); -/// Check that specific, known bridging functions are fully type-checked. -/// -/// NOTE: This is only here to support the --enable-source-import hack. -void checkBridgedFunctions(ASTContext &ctx); - /// Attach Fix-Its to the given diagnostic that updates the name of the /// given declaration to the desired target name. /// diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 3b6b159fdb08b..7a4bd42d0eff5 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -24,6 +24,7 @@ #include "swift/AST/SourceFile.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/Support/SaveAndRestore.h" #include using namespace swift; @@ -34,7 +35,7 @@ using namespace swift; /// This requires the getter's body to have a certain syntactic form. It should /// be kept in sync with importEnumCaseAlias in the ClangImporter library. static EnumElementDecl * -extractEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc, +extractEnumElement(DeclContext *DC, SourceLoc UseLoc, const VarDecl *constant) { diagnoseExplicitUnavailability(constant, UseLoc, DC, nullptr); @@ -46,7 +47,7 @@ extractEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc, if (!body || body->getNumElements() != 1) return nullptr; - auto *retStmtRaw = body->getElement(0).dyn_cast(); + auto *retStmtRaw = body->getFirstElement().dyn_cast(); auto *retStmt = dyn_cast_or_null(retStmtRaw); if (!retStmt) return nullptr; @@ -73,7 +74,7 @@ extractEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc, /// If there are no enum elements but there are properties, attempts to map /// an arbitrary property to an enum element using extractEnumElement. static EnumElementDecl * -filterForEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc, +filterForEnumElement(DeclContext *DC, SourceLoc UseLoc, bool unqualifiedLookup, LookupResult foundElements) { EnumElementDecl *foundElement = nullptr; VarDecl *foundConstant = nullptr; @@ -106,34 +107,42 @@ filterForEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc, } if (!foundElement && foundConstant && foundConstant->hasClangNode()) - foundElement = extractEnumElement(TC, DC, UseLoc, foundConstant); + foundElement = extractEnumElement(DC, UseLoc, foundConstant); return foundElement; } /// Find an unqualified enum element. static EnumElementDecl * -lookupUnqualifiedEnumMemberElement(TypeChecker &TC, DeclContext *DC, - Identifier name, SourceLoc UseLoc) { +lookupUnqualifiedEnumMemberElement(DeclContext *DC, DeclNameRef name, + SourceLoc UseLoc) { + // FIXME: We should probably pay attention to argument labels someday. + name = name.withoutArgumentLabels(); + auto lookupOptions = defaultUnqualifiedLookupOptions; lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupUnqualified(DC, name, SourceLoc(), lookupOptions); - return filterForEnumElement(TC, DC, UseLoc, + auto lookup = + TypeChecker::lookupUnqualified(DC, name, SourceLoc(), lookupOptions); + return filterForEnumElement(DC, UseLoc, /*unqualifiedLookup=*/true, lookup); } /// Find an enum element in an enum type. static EnumElementDecl * -lookupEnumMemberElement(TypeChecker &TC, DeclContext *DC, Type ty, - Identifier name, SourceLoc UseLoc) { +lookupEnumMemberElement(DeclContext *DC, Type ty, + DeclNameRef name, SourceLoc UseLoc) { if (!ty->mayHaveMembers()) return nullptr; + // FIXME: We should probably pay attention to argument labels someday. + name = name.withoutArgumentLabels(); + // Look up the case inside the enum. // FIXME: We should be able to tell if this is a private lookup. NameLookupOptions lookupOptions = defaultMemberLookupOptions; - LookupResult foundElements = TC.lookupMember(DC, ty, name, lookupOptions); - return filterForEnumElement(TC, DC, UseLoc, + LookupResult foundElements = + TypeChecker::lookupMember(DC, ty, name, lookupOptions); + return filterForEnumElement(DC, UseLoc, /*unqualifiedLookup=*/false, foundElements); } @@ -166,7 +175,7 @@ struct ExprToIdentTypeRepr : public ASTVisitor // Get the declared type. if (auto *td = dyn_cast(dre->getDecl())) { components.push_back( - new (C) SimpleIdentTypeRepr(dre->getLoc(), td->getName())); + new (C) SimpleIdentTypeRepr(dre->getNameLoc(), td->createNameRef())); components.back()->setValue(td, nullptr); return true; } @@ -177,8 +186,8 @@ struct ExprToIdentTypeRepr : public ASTVisitor assert(components.empty() && "decl ref should be root element of expr"); // Track the AST location of the component. components.push_back( - new (C) SimpleIdentTypeRepr(udre->getLoc(), - udre->getName().getBaseIdentifier())); + new (C) SimpleIdentTypeRepr(udre->getNameLoc(), + udre->getName())); return true; } @@ -190,8 +199,8 @@ struct ExprToIdentTypeRepr : public ASTVisitor // Track the AST location of the new component. components.push_back( - new (C) SimpleIdentTypeRepr(ude->getLoc(), - ude->getName().getBaseIdentifier())); + new (C) SimpleIdentTypeRepr(ude->getNameLoc(), + ude->getName())); return true; } @@ -207,8 +216,8 @@ struct ExprToIdentTypeRepr : public ASTVisitor argTypeReprs.push_back(arg.getTypeRepr()); auto origComponent = components.back(); components.back() = - GenericIdentTypeRepr::create(C, origComponent->getIdLoc(), - origComponent->getIdentifier(), argTypeReprs, + GenericIdentTypeRepr::create(C, origComponent->getNameLoc(), + origComponent->getNameRef(), argTypeReprs, SourceRange(use->getLAngleLoc(), use->getRAngleLoc())); @@ -253,18 +262,18 @@ class ResolvePattern : public ASTVisitor { public: - TypeChecker &TC; + ASTContext &Context; DeclContext *DC; - - ResolvePattern(TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) {} - + + ResolvePattern(DeclContext *DC) : Context(DC->getASTContext()), DC(DC) {} + // Convert a subexpression to a pattern if possible, or wrap it in an // ExprPattern. Pattern *getSubExprPattern(Expr *E) { if (Pattern *p = visit(E)) return p; - - return new (TC.Context) ExprPattern(E, nullptr, nullptr); + + return new (Context) ExprPattern(E, nullptr, nullptr); } // Handle productions that are always leaf patterns or are already resolved. @@ -291,10 +300,11 @@ class ResolvePattern : public ASTVisitorforEachVariable([&](VarDecl *VD) { HasVariable = true; }); if (!HasVariable) { - TC.diagnose(P->getLoc(), diag::var_pattern_didnt_bind_variables, - P->isLet() ? "let" : "var") - .highlight(P->getSubPattern()->getSourceRange()) - .fixItRemove(P->getLoc()); + Context.Diags + .diagnose(P->getLoc(), diag::var_pattern_didnt_bind_variables, + P->isLet() ? "let" : "var") + .highlight(P->getSubPattern()->getSourceRange()) + .fixItRemove(P->getLoc()); } } @@ -337,7 +347,7 @@ class ResolvePattern : public ASTVisitorgetLoc(), E->isImplicit()); + return new (Context) AnyPattern(E->getLoc(), E->isImplicit()); } // Cast expressions 'x as T' get resolved to checked cast patterns. @@ -351,17 +361,15 @@ class ResolvePattern : public ASTVisitorgetElement(0)); - return new (TC.Context) IsPattern(cast->getLoc(), - cast->getCastTypeLoc(), - subPattern, - CheckedCastKind::Unresolved); + return new (Context) IsPattern(cast->getLoc(), cast->getCastTypeLoc(), + subPattern, CheckedCastKind::Unresolved); } // Convert a paren expr to a pattern if it contains a pattern. Pattern *visitParenExpr(ParenExpr *E) { Pattern *subPattern = getSubExprPattern(E->getSubExpr()); - return new (TC.Context) ParenPattern(E->getLParenLoc(), subPattern, - E->getRParenLoc()); + return new (Context) + ParenPattern(E->getLParenLoc(), subPattern, E->getRParenLoc()); } // Convert all tuples to patterns. @@ -375,9 +383,9 @@ class ResolvePattern : public ASTVisitorgetElementNameLoc(i), pattern)); } - - return TuplePattern::create(TC.Context, E->getLoc(), - patternElts, E->getRParenLoc()); + + return TuplePattern::create(Context, E->getLoc(), patternElts, + E->getRParenLoc()); } Pattern *convertBindingsToOptionalSome(Expr *E) { @@ -404,14 +412,14 @@ class ResolvePattern : public ASTVisitorgetSubExpr()); - return new (TC.Context) OptionalSomePattern(subExpr, - bindExpr->getQuestionLoc()); + return new (Context) + OptionalSomePattern(subExpr, bindExpr->getQuestionLoc()); } // Convert a x? to OptionalSome pattern. In the AST form, this will look like @@ -439,55 +447,43 @@ class ResolvePattern : public ASTVisitorgetName().getBaseName().isSpecial()) return nullptr; - // FIXME: Compound names. - return new (TC.Context) EnumElementPattern( - ume->getDotLoc(), - ume->getNameLoc().getBaseNameLoc(), - ume->getName().getBaseIdentifier(), - subPattern, - ume); + return new (Context) + EnumElementPattern(ume->getDotLoc(), ume->getNameLoc(), ume->getName(), + subPattern, ume); } // Member syntax 'T.Element' forms a pattern if 'T' is an enum and the // member name is a member of the enum. Pattern *visitUnresolvedDotExpr(UnresolvedDotExpr *ude) { SmallVector components; - if (!ExprToIdentTypeRepr(components, TC.Context).visit(ude->getBase())) + if (!ExprToIdentTypeRepr(components, Context).visit(ude->getBase())) return nullptr; TypeResolutionOptions options = None; options |= TypeResolutionFlags::AllowUnboundGenerics; options |= TypeResolutionFlags::SilenceErrors; - auto *repr = IdentTypeRepr::create(TC.Context, components); - + auto *repr = IdentTypeRepr::create(Context, components); + // See if the repr resolves to a type. - Type ty = TC.resolveIdentifierType(TypeResolution::forContextual(DC), repr, - options); - + Type ty = TypeChecker::resolveIdentifierType( + TypeResolution::forContextual(DC), repr, options); + auto *enumDecl = dyn_cast_or_null(ty->getAnyNominal()); if (!enumDecl) return nullptr; - // FIXME: Argument labels? EnumElementDecl *referencedElement - = lookupEnumMemberElement(TC, DC, ty, ude->getName().getBaseIdentifier(), - ude->getLoc()); + = lookupEnumMemberElement(DC, ty, ude->getName(), ude->getLoc()); if (!referencedElement) return nullptr; // Build a TypeRepr from the head of the full path. - // FIXME: Compound names. TypeLoc loc(repr); loc.setType(ty); - return new (TC.Context) EnumElementPattern(loc, - ude->getDotLoc(), - ude->getNameLoc() - .getBaseNameLoc(), - ude->getName() - .getBaseIdentifier(), - referencedElement, - nullptr); + return new (Context) EnumElementPattern( + loc, ude->getDotLoc(), ude->getNameLoc(), ude->getName(), + referencedElement, nullptr); } // A DeclRef 'E' that refers to an enum element forms an EnumElementPattern. @@ -499,11 +495,8 @@ class ResolvePattern : public ASTVisitorgetParentEnum()->getDeclaredTypeInContext()); - return new (TC.Context) EnumElementPattern(loc, SourceLoc(), - de->getLoc(), - elt->getName(), - elt, - nullptr); + return new (Context) EnumElementPattern(loc, SourceLoc(), de->getNameLoc(), + elt->createNameRef(), elt, nullptr); } Pattern *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *ude) { // FIXME: This shouldn't be needed. It is only necessary because of the @@ -512,24 +505,20 @@ class ResolvePattern : public ASTVisitorgetName().getBaseIdentifier(), + = lookupUnqualifiedEnumMemberElement(DC, ude->getName(), ude->getLoc())) { auto *enumDecl = referencedElement->getParentEnum(); auto enumTy = enumDecl->getDeclaredTypeInContext(); TypeLoc loc = TypeLoc::withoutLoc(enumTy); - - return new (TC.Context) EnumElementPattern(loc, SourceLoc(), - ude->getLoc(), - ude->getName() - .getBaseIdentifier(), - referencedElement, - nullptr); + + return new (Context) EnumElementPattern( + loc, SourceLoc(), ude->getNameLoc(), ude->getName(), + referencedElement, nullptr); } // Perform unqualified name lookup to find out what the UDRE is. - return getSubExprPattern(TC.resolveDeclRefExpr(ude, DC)); + return getSubExprPattern(TypeChecker::resolveDeclRefExpr(ude, DC)); } // Call syntax forms a pattern if: @@ -546,7 +535,7 @@ class ResolvePattern : public ASTVisitor components; - if (!ExprToIdentTypeRepr(components, TC.Context).visit(ce->getFn())) + if (!ExprToIdentTypeRepr(components, Context).visit(ce->getFn())) return nullptr; if (components.empty()) @@ -559,8 +548,7 @@ class ResolvePattern : public ASTVisitorgetIdentifier(), + = lookupUnqualifiedEnumMemberElement(DC, tailComponent->getNameRef(), tailComponent->getLoc()); if (!referencedElement) return nullptr; @@ -574,17 +562,17 @@ class ResolvePattern : public ASTVisitor(enumTy->getAnyNominal())) return nullptr; referencedElement - = lookupEnumMemberElement(TC, DC, enumTy, - tailComponent->getIdentifier(), + = lookupEnumMemberElement(DC, enumTy, + tailComponent->getNameRef(), tailComponent->getLoc()); if (!referencedElement) return nullptr; @@ -597,12 +585,10 @@ class ResolvePattern : public ASTVisitorgetArg()); - return new (TC.Context) EnumElementPattern(loc, - SourceLoc(), - tailComponent->getIdLoc(), - tailComponent->getIdentifier(), - referencedElement, - subPattern); + return new (Context) EnumElementPattern( + loc, SourceLoc(), tailComponent->getNameLoc(), + tailComponent->getNameRef(), referencedElement, + subPattern); } }; @@ -616,16 +602,17 @@ class ResolvePattern : public ASTVisitorgetASTContext(); if (auto *EP = dyn_cast(P)) if (auto *TE = dyn_cast(EP->getSubExpr())) { - diagnose(TE->getStartLoc(), diag::type_pattern_missing_is) + Context.Diags.diagnose(TE->getStartLoc(), diag::type_pattern_missing_is) .fixItInsert(TE->getStartLoc(), "is "); P = new (Context) IsPattern(TE->getStartLoc(), TE->getTypeLoc(), @@ -648,7 +635,8 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC, // Check for this and recover nicely if they wrote that. if (auto *OSP = dyn_cast(Body)) { if (!OSP->getSubPattern()->isRefutablePattern()) { - diagnose(OSP->getStartLoc(), diag::iflet_implicitly_unwraps) + Context.Diags.diagnose(OSP->getStartLoc(), + diag::iflet_implicitly_unwraps) .highlight(OSP->getSourceRange()) .fixItRemove(OSP->getQuestionLoc()); return P; @@ -659,7 +647,7 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC, // probably meant: // if case let = if (Body->isRefutablePattern()) { - diagnose(P->getLoc(), diag::iflet_pattern_matching) + Context.Diags.diagnose(P->getLoc(), diag::iflet_pattern_matching) .fixItInsert(P->getLoc(), "case "); return P; } @@ -677,13 +665,9 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC, return P; } -static bool validateTypedPattern(TypeChecker &TC, - TypeResolution resolution, +static Type validateTypedPattern(TypeResolution resolution, TypedPattern *TP, TypeResolutionOptions options) { - if (TP->hasType()) - return TP->getType()->hasError(); - TypeLoc TL = TP->getTypeLoc(); bool hadError; @@ -691,7 +675,8 @@ static bool validateTypedPattern(TypeChecker &TC, // If the pattern declares an opaque type, and applies to a single // variable binding, then we can bind the opaque return type from the // property definition. - if (auto opaqueRepr = dyn_cast(TL.getTypeRepr())) { + auto &Context = resolution.getASTContext(); + if (auto opaqueRepr = dyn_cast_or_null(TL.getTypeRepr())) { auto named = dyn_cast( TP->getSubPattern()->getSemanticsProvidingPattern()); if (named) { @@ -699,53 +684,58 @@ static bool validateTypedPattern(TypeChecker &TC, auto opaqueDecl = var->getOpaqueResultTypeDecl(); auto opaqueTy = (opaqueDecl ? opaqueDecl->getDeclaredInterfaceType() - : ErrorType::get(TC.Context)); + : ErrorType::get(Context)); TL.setType(named->getDecl()->getDeclContext() ->mapTypeIntoContext(opaqueTy)); hadError = opaqueTy->hasError(); } else { - TC.diagnose(TP->getLoc(), diag::opaque_type_unsupported_pattern); + Context.Diags.diagnose(TP->getLoc(), diag::opaque_type_unsupported_pattern); hadError = true; } } else { - hadError = TypeChecker::validateType(TC.Context, TL, resolution, options); + hadError = TypeChecker::validateType(Context, TL, resolution, options); } if (hadError) { - TP->setType(ErrorType::get(TC.Context)); - return hadError; + return ErrorType::get(Context); } - TP->setType(TL.getType()); - assert(!dyn_cast_or_null(TL.getTypeRepr())); + return TL.getType(); +} - // Track whether the decl in this typed pattern should be - // implicitly unwrapped as needed during expression type checking. - if (TL.getTypeRepr() && TL.getTypeRepr()->getKind() == - TypeReprKind::ImplicitlyUnwrappedOptional) { - auto *subPattern = TP->getSubPattern(); - - while (auto *parenPattern = dyn_cast(subPattern)) - subPattern = parenPattern->getSubPattern(); - - if (auto *namedPattern = dyn_cast(subPattern)) { - // FIXME: This needs to be done as part of - // IsImplicitlyUnwrappedOptionalRequest::evaluate(); we just - // need to find the right TypedPattern there for the VarDecl - // in order to recover it's TypeRepr. - namedPattern->getDecl()->setImplicitlyUnwrappedOptional(true); - } else { - assert(isa(subPattern) && - "Unexpected pattern nested in typed pattern!"); - } +Type TypeChecker::typeCheckPattern(ContextualPattern pattern) { + DeclContext *dc = pattern.getDeclContext(); + ASTContext &ctx = dc->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, PatternTypeRequest{pattern}, ErrorType::get(ctx)); +} + +/// Apply the contextual pattern's context to the type resolution options. +static TypeResolutionOptions applyContextualPatternOptions( + TypeResolutionOptions options, ContextualPattern pattern) { + if (pattern.allowsInference()) { + options |= TypeResolutionFlags::AllowUnspecifiedTypes; + options |= TypeResolutionFlags::AllowUnboundGenerics; } - return hadError; + return options; } -bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options) { +llvm::Expected PatternTypeRequest::evaluate( + Evaluator &evaluator, ContextualPattern pattern) const { + Pattern *P = pattern.getPattern(); + DeclContext *dc = pattern.getDeclContext(); + + TypeResolutionOptions options(pattern.getPatternBindingDecl() + ? TypeResolverContext::PatternBindingDecl + : TypeResolverContext::InExpression); + options = applyContextualPatternOptions(options, pattern); + if (!pattern.isTopLevel()) { + options = options.withoutContext(); + } + + auto &Context = dc->getASTContext(); switch (P->getKind()) { // Type-check paren patterns by checking the sub-pattern and // propagating that type out. @@ -756,17 +746,15 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, SP = PP->getSubPattern(); else SP = cast(P)->getSubPattern(); - if (typeCheckPattern(SP, dc, options)) { - P->setType(ErrorType::get(Context)); - return true; - } - if (SP->hasType()) { - auto type = SP->getType(); - if (P->getKind() == PatternKind::Paren) - type = ParenType::get(Context, type); - P->setType(type); - } - return false; + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(SP, /*retainTopLevel=*/true)); + if (subType->hasError()) + return ErrorType::get(Context); + + auto type = subType; + if (P->getKind() == PatternKind::Paren) + type = ParenType::get(Context, type); + return type; } // If we see an explicit type annotation, coerce the sub-pattern to @@ -774,23 +762,7 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, case PatternKind::Typed: { auto resolution = TypeResolution::forContextual(dc); TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(*this, resolution, TP, options); - - // If we have unbound generic types, don't apply them below; instead, - // the caller will call typeCheckBinding() later. - if (P->getType()->hasUnboundGenericType()) - return hadError; - - Pattern *subPattern = TP->getSubPattern(); - if (coercePatternToType(subPattern, resolution, P->getType(), - options|TypeResolutionFlags::FromNonInferredPattern, - TP->getTypeLoc())) - hadError = true; - else { - TP->setSubPattern(subPattern); - TP->setType(subPattern->getType()); - } - return hadError; + return validateTypedPattern(resolution, TP, options); } // A wildcard or name pattern cannot appear by itself in a context @@ -800,16 +772,15 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, // If we're type checking this pattern in a context that can provide type // information, then the lack of type information is not an error. if (options & TypeResolutionFlags::AllowUnspecifiedTypes) - return false; + return Context.TheUnresolvedType; - diagnose(P->getLoc(), diag::cannot_infer_type_for_pattern); - P->setType(ErrorType::get(Context)); + Context.Diags.diagnose(P->getLoc(), diag::cannot_infer_type_for_pattern); if (auto named = dyn_cast(P)) { if (auto var = named->getDecl()) { - var->markInvalid(); + var->setInvalid(); } } - return true; + return ErrorType::get(Context); // A tuple pattern propagates its tuple-ness out. case PatternKind::Tuple: { @@ -817,32 +788,21 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, bool hadError = false; SmallVector typeElts; - const auto elementOptions = options.withoutContext(); - bool missingType = false; for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { TuplePatternElt &elt = tuplePat->getElement(i); - Pattern *pattern = elt.getPattern(); - if (typeCheckPattern(pattern, dc, elementOptions)) { + Type subType = TypeChecker::typeCheckPattern( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false)); + if (subType->hasError()) hadError = true; - continue; - } - if (!pattern->hasType()) { - missingType = true; - continue; - } - typeElts.push_back(TupleTypeElt(pattern->getType(), elt.getLabel())); + typeElts.push_back(TupleTypeElt(subType, elt.getLabel())); } if (hadError) { - P->setType(ErrorType::get(Context)); - return true; + return ErrorType::get(Context); } - if (!missingType && !(options & - TypeResolutionFlags::AllowUnspecifiedTypes)) { - P->setType(TupleType::get(typeElts, Context)); - } - return false; + + return TupleType::get(typeElts, Context); } //--- Refutable patterns. @@ -856,12 +816,12 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc, case PatternKind::Expr: // In a let/else, these always require an initial value to match against. if (!(options & TypeResolutionFlags::AllowUnspecifiedTypes)) { - diagnose(P->getLoc(), diag::refutable_pattern_requires_initializer); - P->setType(ErrorType::get(Context)); - return true; + Context.Diags.diagnose(P->getLoc(), + diag::refutable_pattern_requires_initializer); + return ErrorType::get(Context); } - return false; + return Context.TheUnresolvedType; } llvm_unreachable("bad pattern kind!"); } @@ -879,44 +839,54 @@ namespace { // 1b. pat // type ~ ((T1, ..., Tn)) (n >= 2) // 2. pat ~ (P1, ..., Pm) (m >= 2) -void implicitlyUntuplePatternIfApplicable(TypeChecker *TC, +void implicitlyUntuplePatternIfApplicable(ASTContext &Ctx, Pattern *&enumElementInnerPat, Type enumPayloadType) { + auto &DE = Ctx.Diags; if (auto *tupleType = dyn_cast(enumPayloadType.getPointer())) { if (tupleType->getNumElements() >= 2 && enumElementInnerPat->getKind() == PatternKind::Paren) { auto *semantic = enumElementInnerPat->getSemanticsProvidingPattern(); if (auto *tuplePattern = dyn_cast(semantic)) { if (tuplePattern->getNumElements() >= 2) { - TC->diagnose(tuplePattern->getLoc(), - diag::matching_tuple_pattern_with_many_assoc_values); + DE.diagnose(tuplePattern->getLoc(), + diag::matching_tuple_pattern_with_many_assoc_values); enumElementInnerPat = semantic; } } else { - TC->diagnose(enumElementInnerPat->getLoc(), - diag::matching_pattern_with_many_assoc_values); + DE.diagnose(enumElementInnerPat->getLoc(), + diag::matching_pattern_with_many_assoc_values); } } } else if (auto *tupleType = enumPayloadType->getAs()) { if (tupleType->getNumElements() >= 2 - && enumElementInnerPat->getKind() == PatternKind::Tuple) - TC->diagnose(enumElementInnerPat->getLoc(), - diag::matching_many_patterns_with_tupled_assoc_value); + && enumElementInnerPat->getKind() == PatternKind::Tuple) { + DE.diagnose(enumElementInnerPat->getLoc(), + diag::matching_many_patterns_with_tupled_assoc_value); + enumElementInnerPat = + new (Ctx) ParenPattern(enumElementInnerPat->getStartLoc(), + enumElementInnerPat, + enumElementInnerPat->getEndLoc()); + } } } } /// Perform top-down type coercion on the given pattern. -bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, - Type type, - TypeResolutionOptions options, - TypeLoc tyLoc) { -recur: +Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, + Type type, + TypeResolutionOptions options, + TypeLoc tyLoc) { if (tyLoc.isNull()) { tyLoc = TypeLoc::withoutLoc(type); } - auto dc = resolution.getDeclContext(); + auto P = pattern.getPattern(); + auto dc = pattern.getDeclContext(); + auto &Context = dc->getASTContext(); + auto &diags = Context.Diags; + + options = applyContextualPatternOptions(options, pattern); auto subOptions = options; subOptions.setContext(None); switch (P->getKind()) { @@ -934,100 +904,100 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (tupleType->getNumElements() == 1 && !tupleType->getElement(0).isVararg()) { auto elementTy = tupleType->getElementType(0); - if (coercePatternToType(sub, resolution, elementTy, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/true), elementTy, + subOptions); + if (!sub) + return nullptr; TuplePatternElt elt(sub); P = TuplePattern::create(Context, PP->getLParenLoc(), elt, PP->getRParenLoc()); if (PP->isImplicit()) P->setImplicit(); P->setType(type); - return false; + return P; } } } - - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; + PP->setSubPattern(sub); PP->setType(sub->getType()); - return false; + return P; } case PatternKind::Var: { auto VP = cast(P); Pattern *sub = VP->getSubPattern(); - if (coercePatternToType(sub, resolution, type, subOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + if (!sub) + return nullptr; VP->setSubPattern(sub); if (sub->hasType()) VP->setType(sub->getType()); - return false; + return P; } // If we see an explicit type annotation, coerce the sub-pattern to // that type. case PatternKind::Typed: { TypedPattern *TP = cast(P); - bool hadError = validateTypedPattern(*this, resolution, TP, options); - if (!hadError) { - if (!type->isEqual(TP->getType()) && !type->hasError()) { + Type patternType = TypeChecker::typeCheckPattern(pattern); + bool hadError = false; + if (!patternType->hasError()) { + if (!type->isEqual(patternType) && !type->hasError()) { if (options & TypeResolutionFlags::OverrideType) { TP->setType(type); } else { - diagnose(P->getLoc(), diag::pattern_type_mismatch_context, type); + diags.diagnose(P->getLoc(), diag::pattern_type_mismatch_context, + type); hadError = true; } } + } else { + hadError = true; } Pattern *sub = TP->getSubPattern(); - hadError |= coercePatternToType(sub, resolution, TP->getType(), - subOptions | TypeResolutionFlags::FromNonInferredPattern); - if (!hadError) { - TP->setSubPattern(sub); - TP->setType(sub->getType()); - } - return hadError; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions | TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + + TP->setSubPattern(sub); + TP->setType(sub->getType()); + return P; } // For wildcard and name patterns, set the type. case PatternKind::Named: { NamedPattern *NP = cast(P); VarDecl *var = NP->getDecl(); - if (var->isInvalid()) + if (var->hasInterfaceType() && var->isInvalid()) type = ErrorType::get(Context); - Type interfaceType = type; - if (interfaceType->hasArchetype()) - interfaceType = interfaceType->mapTypeOutOfContext(); - // In SIL mode, VarDecls are written as having reference storage types. - if (type->is()) { - assert(interfaceType->is()); - type = type->getReferenceStorageReferent(); - } else { - if (auto *attr = var->getAttrs().getAttribute()) - interfaceType = checkReferenceOwnershipAttr(var, interfaceType, attr); - } + type = type->getReferenceStorageReferent(); // Note that the pattern's type does not include the reference storage type. P->setType(type); - var->setInterfaceType(interfaceType); - var->setType(var->getDeclContext()->mapTypeIntoContext(interfaceType)); - var->setTypeRepr(tyLoc.getTypeRepr()); + var->setNamingPattern(NP); - // FIXME: Should probably just remove the forbidden prefix stuff, it no - // longer makes a lot of sense in a request-based world. - checkForForbiddenPrefix(var); + // FIXME: This call can be removed once pattern binding validation is + // sufficiently requestified. + TypeChecker::checkForForbiddenPrefix(Context, var->getBaseName()); // If we are inferring a variable to have type AnyObject.Type, - // "()", an uninhabited type, or optional thereof, emit a diagnostic. - // In the first 2 cases, the coder probably forgot a cast and expected a - // concrete type. In the later case, they probably didn't mean to bind to - // a variable, or there is some other bug. We always tell them that they - // can silence the warning with an explicit type annotation - // (and provide a fixit) as a note. + // "()", "[()]", an uninhabited type, or optional thereof, emit a diagnostic. + // They are probably missing a cast or didn't mean to bind to a variable. + // We always tell them that they can silence the warning with an + // explicit type annotation (and provide a fixit) as a note. Type diagTy = type->lookThroughAllOptionalTypes(); bool isOptional = !type->getOptionalObjectType().isNull(); if (!diagTy) diagTy = type; @@ -1054,22 +1024,26 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, assert((diagTy->is() || diagTy->is()) && "unknown structurally uninhabited type"); } + } else if (auto *BST = diagTy->getAs()) { + if (BST->getDecl() == Context.getArrayDecl()) + shouldRequireType = BST->getGenericArgs()[0]->isEqual(Context.TheEmptyTupleType); } if (shouldRequireType && !options.is(TypeResolverContext::ForEachStmt) && !options.is(TypeResolverContext::EditorPlaceholderExpr) && !(options & TypeResolutionFlags::FromNonInferredPattern)) { - diagnose(NP->getLoc(), diag, NP->getDecl()->getName(), type, - NP->getDecl()->isLet()); - diagnose(NP->getLoc(), diag::add_explicit_type_annotation_to_silence); + diags.diagnose(NP->getLoc(), diag, NP->getDecl()->getName(), type, + NP->getDecl()->isLet()); + diags.diagnose(NP->getLoc(), diag::add_explicit_type_annotation_to_silence) + .fixItInsertAfter(var->getNameLoc(), ": " + type->getWithoutParens()->getString()); } - return false; + return P; } case PatternKind::Any: P->setType(type); - return false; + return P; // We can match a tuple pattern with a tuple type. // TODO: permit implicit conversions? @@ -1080,12 +1054,15 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Sometimes a paren is just a paren. If the tuple pattern has a single // element, we can reduce it to a paren pattern. bool canDecayToParen = TP->getNumElements() == 1; - auto decayToParen = [&]() -> bool { + auto decayToParen = [&]() -> Pattern * { assert(canDecayToParen); Pattern *sub = TP->getElement(0).getPattern(); - if (this->coercePatternToType(sub, resolution, type, subOptions)) - return true; - + sub = TypeChecker::coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, + subOptions); + if (!sub) + return nullptr; + if (TP->getLParenLoc().isValid()) { P = new (Context) ParenPattern(TP->getLParenLoc(), sub, TP->getRParenLoc(), @@ -1094,7 +1071,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else { P = sub; } - return false; + return P; }; // The context type must be a tuple. @@ -1102,8 +1079,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (!tupleTy && !hadError) { if (canDecayToParen) return decayToParen(); - diagnose(TP->getStartLoc(), diag::tuple_pattern_in_non_tuple_context, - type); + diags.diagnose(TP->getStartLoc(), + diag::tuple_pattern_in_non_tuple_context, type); hadError = true; } @@ -1112,7 +1089,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (canDecayToParen) return decayToParen(); - diagnose(TP->getStartLoc(), diag::tuple_pattern_length_mismatch, type); + diags.diagnose(TP->getStartLoc(), diag::tuple_pattern_length_mismatch, + type); hadError = true; } @@ -1121,7 +1099,6 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, for (unsigned i = 0, e = TP->getNumElements(); i != e; ++i) { TuplePatternElt &elt = TP->getElement(i); - Pattern *pattern = elt.getPattern(); Type CoercionType; if (hadError) @@ -1133,18 +1110,25 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // the label for the tuple type being matched. if (!hadError && !elt.getLabel().empty() && elt.getLabel() != tupleTy->getElement(i).getName()) { - diagnose(elt.getLabelLoc(), diag::tuple_pattern_label_mismatch, - elt.getLabel(), tupleTy->getElement(i).getName()); + diags.diagnose(elt.getLabelLoc(), diag::tuple_pattern_label_mismatch, + elt.getLabel(), tupleTy->getElement(i).getName()); hadError = true; } - - hadError |= coercePatternToType(pattern, resolution, CoercionType, - options); + + auto sub = coercePatternToType( + pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false), + CoercionType, subOptions); + if (!sub) + return nullptr; + if (!hadError) - elt.setPattern(pattern); + elt.setPattern(sub); } - return hadError; + if (hadError) + return nullptr; + + return P; } // Coerce expressions by finding a '~=' operator that can compare the @@ -1160,7 +1144,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, EP->getSubExpr()->getSemanticsProvidingExpr())) { P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); P->setType(type); - return false; + return P; } } @@ -1170,13 +1154,19 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (auto *NLE = dyn_cast(EP->getSubExpr())) { auto *NoneEnumElement = Context.getOptionalNoneDecl(); P = new (Context) EnumElementPattern(TypeLoc::withoutLoc(type), - NLE->getLoc(), NLE->getLoc(), - NoneEnumElement->getName(), + NLE->getLoc(), + DeclNameLoc(NLE->getLoc()), + NoneEnumElement->createNameRef(), NoneEnumElement, nullptr, false); - return coercePatternToType(P, resolution, type, options); + return TypeChecker::coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); } } - return typeCheckExprPattern(cast(P), dc, type); + + if (TypeChecker::typeCheckExprPattern(cast(P), dc, type)) + return nullptr; + + return P; } // Coerce an 'is' pattern by determining the cast kind. @@ -1184,9 +1174,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto IP = cast(P); // Type-check the type parameter. - TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolutionOptions paramOptions(TypeResolverContext::InExpression); + TypeResolution resolution = TypeResolution::forContextual(dc); if (validateType(Context, IP->getCastTypeLoc(), resolution, paramOptions)) - return true; + return nullptr; auto castType = IP->getCastTypeLoc().getType(); @@ -1205,29 +1196,30 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto some = Context.getOptionalDecl()->getUniqueElement(/*hasVal*/true); sub = new (Context) EnumElementPattern(TypeLoc(), IP->getStartLoc(), - IP->getEndLoc(), - some->getName(), + DeclNameLoc(IP->getEndLoc()), + some->createNameRef(), nullptr, sub, /*Implicit=*/true); } P = sub; - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevle=*/true), type, options); } CheckedCastKind castKind - = typeCheckCheckedCast(type, IP->getCastTypeLoc().getType(), - type->hasError() - ? CheckedCastContextKind::None - : CheckedCastContextKind::IsPattern, - dc, - IP->getLoc(), - nullptr, - IP->getCastTypeLoc().getSourceRange()); + = TypeChecker::typeCheckCheckedCast(type, IP->getCastTypeLoc().getType(), + type->hasError() + ? CheckedCastContextKind::None + : CheckedCastContextKind::IsPattern, + dc, + IP->getLoc(), + nullptr, + IP->getCastTypeLoc().getSourceRange()); switch (castKind) { case CheckedCastKind::Unresolved: - return true; + return nullptr; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: // If this is an 'as' pattern coercing between two different types, then @@ -1236,8 +1228,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // types are the same, then produce a warning. if (!IP->getSubPattern() || type->isEqual(IP->getCastTypeLoc().getType())) { - diagnose(IP->getLoc(), diag::isa_is_always_true, - IP->getSubPattern() ? "as" : "is"); + diags.diagnose(IP->getLoc(), diag::isa_is_always_true, + IP->getSubPattern() ? "as" : "is"); } IP->setCastKind(castKind); break; @@ -1246,10 +1238,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, case CheckedCastKind::ArrayDowncast: case CheckedCastKind::DictionaryDowncast: case CheckedCastKind::SetDowncast: { - diagnose(IP->getLoc(), - diag::isa_collection_downcast_pattern_value_unimplemented, - IP->getCastTypeLoc().getType()); - return false; + diags.diagnose(IP->getLoc(), + diag::isa_collection_downcast_pattern_value_unimplemented, + IP->getCastTypeLoc().getType()); + return P; } case CheckedCastKind::ValueCast: @@ -1260,13 +1252,17 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // Coerce the subpattern to the destination type. if (Pattern *sub = IP->getSubPattern()) { - if (coercePatternToType(sub, resolution, IP->getCastTypeLoc().getType(), - subOptions|TypeResolutionFlags::FromNonInferredPattern)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), + IP->getCastTypeLoc().getType(), + subOptions|TypeResolutionFlags::FromNonInferredPattern); + if (!sub) + return nullptr; + IP->setSubPattern(sub); } - return false; + return P; } case PatternKind::EnumElement: { @@ -1280,7 +1276,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, Type enumTy; if (!elt) { - elt = lookupEnumMemberElement(*this, dc, type, EEP->getName(), + elt = lookupEnumMemberElement(dc, type, EEP->getName(), EEP->getLoc()); if (!elt) { if (!type->hasError()) { @@ -1290,17 +1286,20 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // isn't a static VarDecl, so the existing mechanics in // extractEnumElement won't work. if (type->getAnyNominal() == Context.getOptionalDecl()) { - if (EEP->getName().str() == "None" || - EEP->getName().str() == "Some") { + if (EEP->getName().isSimpleName("None") || + EEP->getName().isSimpleName("Some")) { SmallString<4> Rename; - camel_case::toLowercaseWord(EEP->getName().str(), Rename); - diagnose( + camel_case::toLowercaseWord(EEP->getName() + .getBaseIdentifier().str(), + Rename); + diags.diagnose( EEP->getLoc(), diag::availability_decl_unavailable_rename, - /*"getter" prefix*/ 2, EEP->getName(), /*replaced*/ false, - /*special kind*/ 0, Rename.str(), /*message*/ StringRef()) + /*"getter" prefix*/ 2, EEP->getName().getBaseName(), + /*replaced*/ false, /*special kind*/ 0, Rename.str(), + /*message*/ StringRef()) .fixItReplace(EEP->getLoc(), Rename.str()); - return true; + return nullptr; } // If we have the original expression parse tree, try reinterpreting @@ -1309,7 +1308,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, } else if (EEP->hasUnresolvedOriginalExpr()) { P = new (Context) ExprPattern(EEP->getUnresolvedOriginalExpr(), nullptr, nullptr); - goto recur; + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } // If we have an optional type, let's try to see if the case @@ -1318,24 +1319,63 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, // to add multiple levels of OptionalSomePattern if the optional // is nested. if (auto baseType = type->getOptionalObjectType()) { - if (lookupEnumMemberElement(*this, dc, + if (lookupEnumMemberElement(dc, baseType->lookThroughAllOptionalTypes(), EEP->getName(), EEP->getLoc())) { P = new (Context) OptionalSomePattern(EEP, EEP->getEndLoc(), /*implicit*/true); - return coercePatternToType(P, resolution, type, options); + return coercePatternToType( + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, + options); } else { - diagnose(EEP->getLoc(), - diag::enum_element_pattern_member_not_found, - EEP->getName().str(), type); - return true; + diags.diagnose(EEP->getLoc(), + diag::enum_element_pattern_member_not_found, + EEP->getName(), type); + return nullptr; } } } } if (!elt) - return true; + return nullptr; + + // Emit an ambiguous none diagnostic if: + // 1) We have an Optional type. + // 2) We're matching a 'none' enum case. + // 3) The 'none' enum case exists in T too. + if (EEP->getName().isSimpleName("none") && + type->getOptionalObjectType()) { + SmallVector allOptionals; + auto baseTyUnwrapped = type->lookThroughAllOptionalTypes(allOptionals); + if (lookupEnumMemberElement(dc, baseTyUnwrapped, EEP->getName(), + EEP->getLoc())) { + auto baseTyName = type->getCanonicalType().getString(); + auto baseTyUnwrappedName = baseTyUnwrapped->getString(); + diags.diagnoseWithNotes( + diags.diagnose(EEP->getLoc(), diag::optional_ambiguous_case_ref, + baseTyName, baseTyUnwrappedName, "none"), + [&]() { + // Emit a note to swap '.none' with 'nil' to match with + // the 'none' case in Optional. + diags.diagnose(EEP->getLoc(), + diag::optional_fixit_ambiguous_case_ref_switch) + .fixItReplace(EEP->getSourceRange(), "nil"); + // Emit a note to swap '.none' with 'none?' to match with the + // 'none' case in T. Add as many '?' as needed to look though + // all the optionals. + std::string fixItString = "none"; + llvm::for_each(allOptionals, + [&](const Type) { fixItString += "?"; }); + diags.diagnose( + EEP->getLoc(), + diag::type_fixit_optional_ambiguous_case_ref_switch, + fixItString) + .fixItReplace(EEP->getNameLoc().getSourceRange(), + fixItString); + }); + } + } enumTy = type; } else { @@ -1355,9 +1395,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, parentTy->getAnyNominal() == type->getAnyNominal()) { enumTy = type; } else { - diagnose(EEP->getLoc(), diag::ambiguous_enum_pattern_type, - parentTy, type); - return true; + diags.diagnose(EEP->getLoc(), diag::ambiguous_enum_pattern_type, + parentTy, type); + return nullptr; } } // Otherwise, see if we can introduce a cast pattern to get from an @@ -1371,17 +1411,17 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, nullptr, SourceRange()); // If the cast failed, we can't resolve the pattern. if (foundCastKind < CheckedCastKind::First_Resolved) - return true; + return nullptr; // Otherwise, we can type-check as the enum type, and insert a cast // from the outer pattern type. castKind = foundCastKind; enumTy = parentTy; } else { - diagnose(EEP->getLoc(), - diag::enum_element_pattern_not_member_of_enum, - EEP->getName().str(), type); - return true; + diags.diagnose(EEP->getLoc(), + diag::enum_element_pattern_not_member_of_enum, + EEP->getName(), type); + return nullptr; } } @@ -1390,12 +1430,13 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, if (EEP->hasSubPattern()) { Pattern *sub = EEP->getSubPattern(); if (!elt->hasAssociatedValues()) { - diagnose(EEP->getLoc(), - diag::enum_element_pattern_assoc_values_mismatch, - EEP->getName()); - diagnose(EEP->getLoc(), diag::enum_element_pattern_assoc_values_remove) + diags.diagnose(EEP->getLoc(), + diag::enum_element_pattern_assoc_values_mismatch, + EEP->getName()); + diags.diagnose(EEP->getLoc(), + diag::enum_element_pattern_assoc_values_remove) .fixItRemove(sub->getSourceRange()); - return true; + return nullptr; } Type elementType; @@ -1408,10 +1449,14 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - ::implicitlyUntuplePatternIfApplicable(this, sub, elementType); + ::implicitlyUntuplePatternIfApplicable(Context, sub, elementType); + + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; EEP->setSubPattern(sub); } else if (argType) { // Else if the element pattern has no sub-pattern but the element type has @@ -1441,8 +1486,11 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; EEP->setSubPattern(sub); } @@ -1464,7 +1512,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, P = isPattern; } - return false; + return P; } case PatternKind::OptionalSome: { @@ -1480,13 +1528,13 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, loc = OP->getLoc(); } - diagnose(loc, diagID, type); - return true; + diags.diagnose(loc, diagID, type); + return nullptr; } EnumElementDecl *elementDecl = Context.getOptionalSomeDecl(); if (!elementDecl) - return true; + return nullptr; OP->setElementDecl(elementDecl); @@ -1494,21 +1542,24 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution, auto newSubOptions = subOptions; newSubOptions.setContext(TypeResolverContext::EnumPatternPayload); newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; - if (coercePatternToType(sub, resolution, elementType, newSubOptions)) - return true; + sub = coercePatternToType( + pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, + newSubOptions); + if (!sub) + return nullptr; + OP->setSubPattern(sub); OP->setType(type); - return false; + return P; } case PatternKind::Bool: P->setType(type); - return false; + return P; } llvm_unreachable("bad pattern kind!"); } - /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, @@ -1526,10 +1577,7 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, if (param->isInvalid()) return true; - if (auto type = param->getType()) - return !isValidType(type); - - return true; + return !isValidType(param->getType()); }; auto handleParameter = [&](ParamDecl *param, Type ty, bool forceMutable) { @@ -1540,7 +1588,6 @@ void TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE, // trying to coerce argument to contextual type would mean erasing // valuable diagnostic information. if (isValidType(ty) || shouldOverwriteParam(param)) { - param->setType(ty); param->setInterfaceType(ty->mapTypeOutOfContext()); } }; diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index f59e9c5e921ab..7f6869de91197 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -36,6 +36,12 @@ enum class PropertyWrapperInitKind { Default }; +static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl, + NominalTypeDecl *parent) { + return decl->getFormalAccess() < + std::min(parent->getFormalAccess(), AccessLevel::Public); +} + /// Find the named property in a property wrapper to which access will /// be delegated. static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, @@ -43,7 +49,8 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, SmallVector vars; { SmallVector decls; - nominal->lookupQualified(nominal, name, NL_QualifiedDefault, decls); + nominal->lookupQualified(nominal, DeclNameRef(name), NL_QualifiedDefault, + decls); for (const auto &foundDecl : decls) { auto foundVar = dyn_cast(foundDecl); if (!foundVar || foundVar->isStatic() || @@ -78,7 +85,7 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, // The property must be as accessible as the nominal type. VarDecl *var = vars.front(); - if (var->getFormalAccess() < nominal->getFormalAccess()) { + if (isDeclNotAsAccessibleAsParent(var, nominal)) { var->diagnose(diag::property_wrapper_type_requirement_not_accessible, var->getFormalAccess(), var->getDescriptiveKind(), var->getFullName(), nominal->getDeclaredType(), @@ -117,7 +124,7 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, break; } - nominal->lookupQualified(nominal, DeclBaseName::createConstructor(), + nominal->lookupQualified(nominal, DeclNameRef::createConstructor(), NL_QualifiedDefault, decls); for (const auto &decl : decls) { auto init = dyn_cast(decl); @@ -156,7 +163,7 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, } // Check accessibility. - if (init->getFormalAccess() < nominal->getFormalAccess()) { + if (isDeclNotAsAccessibleAsParent(init, nominal)) { nonviable.push_back( std::make_tuple(init, NonViableReason::Inaccessible, Type())); continue; @@ -167,13 +174,13 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal, if (!argumentParam) continue; - if (!argumentParam->hasInterfaceType()) - continue; - if (argumentParam->isInOut() || argumentParam->isVariadic()) continue; auto paramType = argumentParam->getInterfaceType(); + if (paramType->is()) + continue; + if (argumentParam->isAutoClosure()) { if (auto *fnType = paramType->getAs()) paramType = fnType->getResult(); @@ -272,7 +279,7 @@ static SubscriptDecl *findEnclosingSelfSubscript(ASTContext &ctx, auto subscript = subscripts.front(); // the subscript must be as accessible as the nominal type. - if (subscript->getFormalAccess() < nominal->getFormalAccess()) { + if (isDeclNotAsAccessibleAsParent(subscript, nominal)) { subscript->diagnose(diag::property_wrapper_type_requirement_not_accessible, subscript->getFormalAccess(), subscript->getDescriptiveKind(), @@ -491,7 +498,7 @@ AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, return Type(); ASTContext &ctx = var->getASTContext(); - if (!ctx.getLazyResolver()) + if (!ctx.areSemanticQueriesEnabled()) return nullptr; auto resolution = @@ -499,8 +506,8 @@ AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator, TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); options |= TypeResolutionFlags::AllowUnboundGenerics; - auto &tc = *static_cast(ctx.getLazyResolver()); - if (TypeChecker::validateType(tc.Context, customAttr->getTypeLoc(), + if (TypeChecker::validateType(var->getASTContext(), + customAttr->getTypeLoc(), resolution, options)) return ErrorType::get(ctx); @@ -527,18 +534,17 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( return Type(); ASTContext &ctx = var->getASTContext(); - if (!ctx.getLazyResolver()) + if (!ctx.areSemanticQueriesEnabled()) return Type(); // If there's an initializer of some sort, checking it will determine the // property wrapper type. unsigned index = binding->getPatternEntryIndexForVarDecl(var); - TypeChecker &tc = *static_cast(ctx.getLazyResolver()); if (binding->isInitialized(index)) { // FIXME(InterfaceTypeRequest): Remove this. (void)var->getInterfaceType(); if (!binding->isInitializerChecked(index)) - tc.typeCheckPatternBinding(binding, index); + TypeChecker::typeCheckPatternBinding(binding, index); Type type = ctx.getSideCachedPropertyWrapperBackingPropertyType(var); assert(type || ctx.Diags.hadAnyError()); @@ -546,15 +552,13 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( } // Compute the type of the property to plug in to the wrapper type. - // FIXME(InterfaceTypeRequest): Remove this. - (void)var->getInterfaceType(); Type propertyType = var->getType(); - if (!propertyType || propertyType->hasError()) + if (propertyType->hasError()) return Type(); using namespace constraints; auto dc = var->getInnermostDeclContext(); - ConstraintSystem cs(tc, dc, None); + ConstraintSystem cs(dc, None); auto emptyLocator = cs.getConstraintLocator(nullptr); auto wrapperAttrs = var->getAttachedPropertyWrappers(); @@ -593,9 +597,10 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate( propertyType, emptyLocator); SmallVector solutions; - if (cs.solve(nullptr, solutions) || solutions.size() != 1) { + if (cs.solve(solutions) || solutions.size() != 1) { var->diagnose(diag::property_wrapper_incompatible_property, propertyType, rawType); + var->setInvalid(); if (auto nominalWrapper = rawType->getAnyNominal()) { nominalWrapper->diagnose(diag::property_wrapper_declared_here, nominalWrapper->getFullName()); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 94dd5df428f18..c292bbbe6d2ce 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -21,13 +21,10 @@ #include "TypeAccessScopeChecker.h" #include "TypeCheckAvailability.h" #include "TypeCheckObjC.h" -#include "swift/Basic/SourceManager.h" -#include "swift/Basic/StringExtras.h" -#include "swift/Basic/Statistic.h" -#include "swift/AST/AccessScope.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTPrinter.h" +#include "swift/AST/AccessScope.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" @@ -38,10 +35,14 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ReferencedNameTracker.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeDeclFinder.h" #include "swift/AST/TypeMatcher.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" +#include "swift/Basic/SourceManager.h" +#include "swift/Basic/Statistic.h" +#include "swift/Basic/StringExtras.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Serialization/SerializedModuleLoader.h" @@ -209,8 +210,7 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO, /// Check that the Objective-C method(s) provided by the witness have /// the same selectors as those required by the requirement. -static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req, - ValueDecl *witness) { +static bool checkObjCWitnessSelector(ValueDecl *req, ValueDecl *witness) { // Simple case: for methods and initializers, check that the selectors match. if (auto reqFunc = dyn_cast(req)) { auto witnessFunc = cast(witness); @@ -218,10 +218,9 @@ static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req, return false; auto diagInfo = getObjCMethodDiagInfo(witnessFunc); - auto diag = tc.diagnose(witness, diag::objc_witness_selector_mismatch, - diagInfo.first, diagInfo.second, - witnessFunc->getObjCSelector(), - reqFunc->getObjCSelector()); + auto diag = witness->diagnose( + diag::objc_witness_selector_mismatch, diagInfo.first, diagInfo.second, + witnessFunc->getObjCSelector(), reqFunc->getObjCSelector()); fixDeclarationObjCName(diag, witnessFunc, witnessFunc->getObjCSelector(), reqFunc->getObjCSelector()); @@ -239,7 +238,7 @@ static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req, if (auto reqGetter = reqStorage->getParsedAccessor(AccessorKind::Get)) { auto *witnessGetter = witnessStorage->getSynthesizedAccessor(AccessorKind::Get); - if (checkObjCWitnessSelector(tc, reqGetter, witnessGetter)) + if (checkObjCWitnessSelector(reqGetter, witnessGetter)) return true; } @@ -247,21 +246,13 @@ static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req, if (auto reqSetter = reqStorage->getParsedAccessor(AccessorKind::Set)) { auto *witnessSetter = witnessStorage->getSynthesizedAccessor(AccessorKind::Set); - if (checkObjCWitnessSelector(tc, reqSetter, witnessSetter)) + if (checkObjCWitnessSelector(reqSetter, witnessSetter)) return true; } return false; } -static ParameterList *getParameterList(ValueDecl *value) { - if (auto func = dyn_cast(value)) - return func->getParameters(); - - auto subscript = cast(value); - return subscript->getIndices(); -} - // Find a standin declaration to place the diagnostic at for the // given accessor kind. static ValueDecl *getStandinForAccessor(AbstractStorageDecl *witness, @@ -312,6 +303,132 @@ static ValueDecl *getStandinForAccessor(AbstractStorageDecl *witness, return witness; } +/// Given a witness, a requirement, and an existing `RequirementMatch` result, +/// check if the requirement's `@differentiable` attributes are met by the +/// witness. +/// - If requirement's `@differentiable` attributes are met, or if `result` is +/// not viable, returns `result`. +/// - Otherwise, returns a `DifferentiableConflict` `RequirementMatch`. +// Note: the `result` argument is only necessary for using +// `RequirementMatch::WitnessSubstitutions`. +static RequirementMatch +matchWitnessDifferentiableAttr(DeclContext *dc, ValueDecl *req, + ValueDecl *witness, RequirementMatch result) { + if (!result.isViable()) + return result; + + // Get the requirement and witness attributes. + const auto &reqAttrs = req->getAttrs(); + const auto &witnessAttrs = witness->getAttrs(); + + // For all `@differentiable` attributes of the protocol requirement, check + // that the witness has a derivative configuration with exactly the same + // parameter indices, or one with "superset" parameter indices. If there + // exists a witness derivative configuration with "superset" parameter + // indices, create an implicit `@differentiable` attribute for the witness + // with the exact parameter indices from the requirement `@differentiable` + // attribute. + ASTContext &ctx = witness->getASTContext(); + auto *witnessAFD = dyn_cast(witness); + if (auto *witnessASD = dyn_cast(witness)) + witnessAFD = witnessASD->getAccessor(AccessorKind::Get); + // NOTE: Validate `@differentiable` attributes by calling + // `getParameterIndices`. This is important for type-checking + // `@differentiable` attributes in non-primary files to skip invalid + // attributes and to resolve derivative configurations, used below. + for (auto *witnessDiffAttr : + witnessAttrs.getAttributes()) { + (void)witnessDiffAttr->getParameterIndices(); + } + for (auto *reqDiffAttr : reqAttrs.getAttributes()) { + (void)reqDiffAttr->getParameterIndices(); + } + for (auto *reqDiffAttr : reqAttrs.getAttributes()) { + bool foundExactConfig = false; + Optional supersetConfig = None; + for (auto witnessConfig : + witnessAFD->getDerivativeFunctionConfigurations()) { + // All the witness's derivative generic requirements must be satisfied + // by the requirement's derivative generic requirements OR by the + // conditional conformance requirements. + if (witnessConfig.derivativeGenericSignature) { + bool genericRequirementsSatisfied = true; + auto reqDiffGenSig = reqDiffAttr->getDerivativeGenericSignature(); + auto conformanceGenSig = dc->getGenericSignatureOfContext(); + for (const auto &req : + witnessConfig.derivativeGenericSignature->getRequirements()) { + auto substReq = req.subst(result.WitnessSubstitutions); + bool reqDiffGenSigSatisfies = + reqDiffGenSig && substReq && + reqDiffGenSig->isRequirementSatisfied(*substReq); + bool conformanceGenSigSatisfies = + conformanceGenSig && + conformanceGenSig->isRequirementSatisfied(req); + if (!reqDiffGenSigSatisfies && !conformanceGenSigSatisfies) { + genericRequirementsSatisfied = false; + break; + } + } + if (!genericRequirementsSatisfied) + continue; + } + + if (witnessConfig.parameterIndices == + reqDiffAttr->getParameterIndices()) { + foundExactConfig = true; + break; + } + if (witnessConfig.parameterIndices->isSupersetOf( + reqDiffAttr->getParameterIndices())) + supersetConfig = witnessConfig; + } + if (!foundExactConfig) { + bool success = false; + if (supersetConfig) { + // If the witness has a "superset" derivative configuration, create an + // implicit `@differentiable` attribute with the exact requirement + // `@differentiable` attribute parameter indices. + auto *newAttr = DifferentiableAttr::create( + witnessAFD, /*implicit*/ true, reqDiffAttr->AtLoc, + reqDiffAttr->getRange(), reqDiffAttr->isLinear(), + reqDiffAttr->getParameterIndices(), /*jvp*/ None, + /*vjp*/ None, supersetConfig->derivativeGenericSignature); + auto insertion = ctx.DifferentiableAttrs.try_emplace( + {witnessAFD, newAttr->getParameterIndices()}, newAttr); + // Valid `@differentiable` attributes are uniqued by original function + // and parameter indices. Reject duplicate attributes. + if (!insertion.second) { + newAttr->setInvalid(); + } else { + witness->getAttrs().add(newAttr); + success = true; + } + } + if (!success) { + LLVM_DEBUG({ + llvm::dbgs() << "Protocol requirement match failure: missing " + "`@differentiable` attribute for witness "; + witnessAFD->dumpRef(llvm::dbgs()); + llvm::dbgs() << " from requirement "; + req->dumpRef(llvm::dbgs()); + llvm::dbgs() << '\n'; + }); + // FIXME(TF-1014): `@differentiable` attribute diagnostic does not + // appear if associated type inference is involved. + if (auto *vdWitness = dyn_cast(witness)) { + return RequirementMatch( + getStandinForAccessor(vdWitness, AccessorKind::Get), + MatchKind::DifferentiableConflict, reqDiffAttr); + } else { + return RequirementMatch(witness, MatchKind::DifferentiableConflict, + reqDiffAttr); + } + } + } + } + return result; +} + RequirementMatch swift::matchWitness( DeclContext *dc, ValueDecl *req, ValueDecl *witness, @@ -329,13 +446,13 @@ swift::matchWitness( if (req->getKind() != witness->getKind()) return RequirementMatch(witness, MatchKind::KindConflict); - // If the witness is invalid, record that and stop now. - if (witness->isInvalid()) - return RequirementMatch(witness, MatchKind::WitnessInvalid); - // If we're currently validating the witness, bail out. if (witness->isRecursiveValidation()) return RequirementMatch(witness, MatchKind::Circularity); + + // If the witness is invalid, record that and stop now. + if (witness->isInvalid()) + return RequirementMatch(witness, MatchKind::WitnessInvalid); // Get the requirement and witness attributes. const auto &reqAttrs = req->getAttrs(); @@ -539,7 +656,11 @@ swift::matchWitness( } // Now finalize the match. - return finalize(anyRenaming, optionalAdjustments); + auto result = finalize(anyRenaming, optionalAdjustments); + // Check if the requirement's `@differentiable` attributes are satisfied by + // the witness. + result = matchWitnessDifferentiableAttr(dc, req, witness, result); + return result; } /// Checks \p reqEnvCache for a requirement environment appropriate for @@ -635,8 +756,7 @@ static Optional findMissingGenericRequirementForSolutionFix( } RequirementMatch -swift::matchWitness(TypeChecker &tc, - WitnessChecker::RequirementEnvironmentCache &reqEnvCache, +swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache, ProtocolDecl *proto, ProtocolConformance *conformance, DeclContext *dc, ValueDecl *req, ValueDecl *witness) { using namespace constraints; @@ -714,7 +834,7 @@ swift::matchWitness(TypeChecker &tc, auto setup = [&]() -> std::tuple, Type, Type> { // Construct a constraint system to use to solve the equality between // the required type and the witness type. - cs.emplace(tc, dc, ConstraintSystemFlags::AllowFixes); + cs.emplace(dc, ConstraintSystemFlags::AllowFixes); auto reqGenericEnv = reqEnvironment.getSyntheticEnvironment(); auto reqSubMap = reqEnvironment.getRequirementToSyntheticMap(); @@ -857,8 +977,7 @@ witnessHasImplementsAttrForExactRequirement(ValueDecl *witness, } /// Determine whether one requirement match is better than the other. -static bool isBetterMatch(TypeChecker &tc, DeclContext *dc, - ValueDecl *requirement, +static bool isBetterMatch(DeclContext *dc, ValueDecl *requirement, const RequirementMatch &match1, const RequirementMatch &match2) { @@ -877,7 +996,9 @@ static bool isBetterMatch(TypeChecker &tc, DeclContext *dc, } // Check whether one declaration is better than the other. - switch (tc.compareDeclarations(dc, match1.Witness, match2.Witness)) { + const auto comparisonResult = + TypeChecker::compareDeclarations(dc, match1.Witness, match2.Witness); + switch (comparisonResult) { case Comparison::Better: return true; @@ -897,9 +1018,9 @@ static bool isBetterMatch(TypeChecker &tc, DeclContext *dc, return false; } -WitnessChecker::WitnessChecker(TypeChecker &tc, ProtocolDecl *proto, +WitnessChecker::WitnessChecker(ASTContext &ctx, ProtocolDecl *proto, Type adoptee, DeclContext *dc) - : TC(tc), Proto(proto), Adoptee(adoptee), DC(dc) {} + : Context(ctx), Proto(proto), Adoptee(adoptee), DC(dc) {} void WitnessChecker::lookupValueWitnessesViaImplementsAttr( @@ -907,8 +1028,8 @@ WitnessChecker::lookupValueWitnessesViaImplementsAttr( auto lookupOptions = defaultMemberTypeLookupOptions; lookupOptions -= NameLookupFlags::PerformConformanceCheck; lookupOptions |= NameLookupFlags::IncludeAttributeImplements; - auto candidates = TC.lookupMember(DC, Adoptee, req->getFullName(), - lookupOptions); + auto candidates = TypeChecker::lookupMember(DC, Adoptee, req->createNameRef(), + lookupOptions); for (auto candidate : candidates) { if (witnessHasImplementsAttrForExactRequirement(candidate.getValueDecl(), req)) { witnesses.push_back(candidate.getValueDecl()); @@ -927,15 +1048,17 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) { // for this requirement. lookupValueWitnessesViaImplementsAttr(req, witnesses); + auto reqName = req->createNameRef(); + auto reqBaseName = reqName.withoutArgumentLabels(); + if (req->isOperator()) { // Operator lookup is always global. auto lookupOptions = defaultUnqualifiedLookupOptions; if (!DC->isCascadingContextForLookup(false)) lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupUnqualified(DC->getModuleScopeContext(), - req->getBaseName(), - SourceLoc(), - lookupOptions); + auto lookup = TypeChecker::lookupUnqualified(DC->getModuleScopeContext(), + reqBaseName, SourceLoc(), + lookupOptions); for (auto candidate : lookup) { auto decl = candidate.getValueDecl(); if (swift::isMemberOperator(cast(decl), Adoptee)) { @@ -947,14 +1070,14 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) { auto lookupOptions = defaultMemberTypeLookupOptions; lookupOptions -= NameLookupFlags::PerformConformanceCheck; - auto candidates = TC.lookupMember(DC, Adoptee, req->getFullName(), - lookupOptions); + auto candidates = TypeChecker::lookupMember(DC, Adoptee, reqName, + lookupOptions); // If we didn't find anything with the appropriate name, look // again using only the base name. if (candidates.empty() && ignoringNames) { - candidates = TC.lookupMember(DC, Adoptee, req->getBaseName(), - lookupOptions); + candidates = TypeChecker::lookupMember(DC, Adoptee, reqBaseName, + lookupOptions); *ignoringNames = true; } @@ -982,7 +1105,6 @@ bool WitnessChecker::findBestWitness( bool anyFromUnconstrainedExtension; numViable = 0; - for (Attempt attempt = Regular; numViable == 0 && attempt != Done; attempt = static_cast(attempt + 1)) { SmallVector witnesses; @@ -1007,8 +1129,8 @@ bool WitnessChecker::findBestWitness( auto lookupOptions = defaultUnqualifiedLookupOptions; lookupOptions |= NameLookupFlags::KnownPrivate; - auto lookup = TC.lookupUnqualified(overlay, requirement->getBaseName(), - SourceLoc(), lookupOptions); + auto lookup = TypeChecker::lookupUnqualified( + overlay, requirement->createNameRef(), SourceLoc(), lookupOptions); for (auto candidate : lookup) witnesses.push_back(candidate.getValueDecl()); break; @@ -1030,8 +1152,8 @@ bool WitnessChecker::findBestWitness( continue; } - auto match = matchWitness(TC, ReqEnvironmentCache, Proto, conformance, DC, - requirement, witness); + auto match = matchWitness(ReqEnvironmentCache, Proto, conformance, + DC, requirement, witness); if (match.isViable()) { ++numViable; bestIdx = matches.size(); @@ -1055,7 +1177,7 @@ bool WitnessChecker::findBestWitness( if (conformance && !conformance->isInvalid()) { if (auto *SF = DC->getParentSourceFile()) { if (SF->Kind == SourceFileKind::Interface) { - auto match = matchWitness(TC, ReqEnvironmentCache, Proto, + auto match = matchWitness(ReqEnvironmentCache, Proto, conformance, DC, requirement, requirement); assert(match.isViable()); numViable = 1; @@ -1088,7 +1210,7 @@ bool WitnessChecker::findBestWitness( // Find the best match. bestIdx = 0; for (unsigned i = 1, n = matches.size(); i != n; ++i) { - if (isBetterMatch(TC, DC, requirement, matches[i], matches[bestIdx])) + if (isBetterMatch(DC, requirement, matches[i], matches[bestIdx])) bestIdx = i; } @@ -1097,7 +1219,7 @@ bool WitnessChecker::findBestWitness( if (i == bestIdx) continue; - if (!isBetterMatch(TC, DC, requirement, matches[bestIdx], matches[i])) { + if (!isBetterMatch(DC, requirement, matches[bestIdx], matches[i])) { isReallyBest = false; break; } @@ -1196,9 +1318,9 @@ bool WitnessChecker:: checkWitnessAvailability(ValueDecl *requirement, ValueDecl *witness, AvailabilityContext *requiredAvailability) { - return (!TC.getLangOpts().DisableAvailabilityChecking && - !TC.isAvailabilitySafeForConformance(Proto, requirement, witness, - DC, *requiredAvailability)); + return (!getASTContext().LangOpts.DisableAvailabilityChecking && + !TypeChecker::isAvailabilitySafeForConformance( + Proto, requirement, witness, DC, *requiredAvailability)); } RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, @@ -1227,7 +1349,7 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, return RequirementCheck(CheckKind::Availability, requiredAvailability); } - if (requirement->getAttrs().isUnavailable(TC.Context) && + if (requirement->getAttrs().isUnavailable(getASTContext()) && match.Witness->getDeclContext() == DC) { return RequirementCheck(CheckKind::Unavailable); } @@ -1250,8 +1372,8 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, } } - if (match.Witness->getAttrs().isUnavailable(TC.Context) && - !requirement->getAttrs().isUnavailable(TC.Context)) { + if (match.Witness->getAttrs().isUnavailable(getASTContext()) && + !requirement->getAttrs().isUnavailable(getASTContext())) { return CheckKind::WitnessUnavailable; } @@ -1265,7 +1387,7 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, /// having this wrapper can help issue a fixit that inserts protocol stubs from /// multiple protocols under checking. class swift::MultiConformanceChecker { - TypeChecker &TC; + ASTContext &Context; llvm::SmallVector UnsatisfiedReqs; llvm::SmallVector AllUsedCheckers; llvm::SmallVector AllConformances; @@ -1279,9 +1401,9 @@ class swift::MultiConformanceChecker { /// Determine whether the given requirement was left unsatisfied. bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req); public: - MultiConformanceChecker(TypeChecker &TC): TC(TC){} + MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {} - TypeChecker &getTypeChecker() const { return TC; } + ASTContext &getASTContext() const { return Context; } /// Add a conformance into the batched checker. void addConformance(NormalProtocolConformance *conformance) { @@ -1308,8 +1430,8 @@ isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { if (isa(req)) return false; auto witness = conformance->hasWitness(req) - ? conformance->getWitness(req).getDecl() - : nullptr; + ? conformance->getWitnessUncached(req).getDecl() + : nullptr; // An optional requirement might not have a witness... if (!witness) @@ -1496,22 +1618,24 @@ static void diagnoseConformanceImpliedByConditionalConformance( ProtocolConformance *MultiConformanceChecker:: checkIndividualConformance(NormalProtocolConformance *conformance, bool issueFixit) { - PrettyStackTraceConformance trace(TC.Context, "type-checking", conformance); + PrettyStackTraceConformance trace(getASTContext(), "type-checking", + conformance); std::vector revivedMissingWitnesses; switch (conformance->getState()) { case ProtocolConformanceState::Incomplete: if (conformance->isInvalid()) { // Revive registered missing witnesses to handle it below. - revivedMissingWitnesses = TC.Context. - takeDelayedMissingWitnesses(conformance); + revivedMissingWitnesses = + getASTContext().takeDelayedMissingWitnesses(conformance); // If we have no missing witnesses for this invalid conformance, the // conformance is invalid for other reasons, so emit diagnosis now. if (revivedMissingWitnesses.empty()) { // Emit any delayed diagnostics. - ConformanceChecker(TC, conformance, MissingWitnesses, false). - emitDelayedDiags(); + ConformanceChecker(getASTContext(), conformance, MissingWitnesses, + false) + .emitDelayedDiags(); } } @@ -1532,15 +1656,12 @@ checkIndividualConformance(NormalProtocolConformance *conformance, auto Proto = conformance->getProtocol(); auto ProtoType = Proto->getDeclaredType(); SourceLoc ComplainLoc = conformance->getLoc(); + auto &C = ProtoType->getASTContext(); // Note that we are checking this conformance now. conformance->setState(ProtocolConformanceState::Checking); SWIFT_DEFER { conformance->setState(ProtocolConformanceState::Complete); }; - // FIXME(InterfaceTypeRequest): isInvalid() should be based on the interface - // type. - (void)Proto->getInterfaceType(); - // If the protocol itself is invalid, there's nothing we can do. if (Proto->isInvalid()) { conformance->setInvalid(); @@ -1549,8 +1670,9 @@ checkIndividualConformance(NormalProtocolConformance *conformance, // If the protocol requires a class, non-classes are a non-starter. if (Proto->requiresClass() && !canT->getClassOrBoundGenericClass()) { - TC.diagnose(ComplainLoc, diag::non_class_cannot_conform_to_class_protocol, - T, ProtoType); + C.Diags.diagnose(ComplainLoc, + diag::non_class_cannot_conform_to_class_protocol, T, + ProtoType); conformance->setInvalid(); return conformance; } @@ -1571,7 +1693,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, break; } if (diagKind) { - TC.diagnose(ComplainLoc, diagKind.getValue(), T, ProtoType); + C.Diags.diagnose(ComplainLoc, diagKind.getValue(), T, ProtoType); conformance->setInvalid(); return conformance; } @@ -1583,9 +1705,9 @@ checkIndividualConformance(NormalProtocolConformance *conformance, // with the Obj-C runtime when they're satisfied, but we'd still have solve // the problem with extensions that we check for below. if (!conformance->getConditionalRequirements().empty()) { - TC.diagnose(ComplainLoc, - diag::objc_protocol_cannot_have_conditional_conformance, - T, ProtoType); + C.Diags.diagnose(ComplainLoc, + diag::objc_protocol_cannot_have_conditional_conformance, + T, ProtoType); conformance->setInvalid(); return conformance; } @@ -1597,8 +1719,9 @@ checkIndividualConformance(NormalProtocolConformance *conformance, if (auto classDecl = ext->getSelfClassDecl()) { if (classDecl->isGenericContext()) { if (!classDecl->usesObjCGenericsModel()) { - TC.diagnose(ComplainLoc, diag::objc_protocol_in_generic_extension, - classDecl->isGeneric(), T, ProtoType); + C.Diags.diagnose(ComplainLoc, + diag::objc_protocol_in_generic_extension, + classDecl->isGeneric(), T, ProtoType); conformance->setInvalid(); return conformance; } @@ -1617,9 +1740,9 @@ checkIndividualConformance(NormalProtocolConformance *conformance, while (nestedType) { if (auto clas = nestedType->getClassOrBoundGenericClass()) { if (clas->usesObjCGenericsModel()) { - TC.diagnose(ComplainLoc, - diag::objc_generics_cannot_conditionally_conform, T, - ProtoType); + C.Diags.diagnose(ComplainLoc, + diag::objc_generics_cannot_conditionally_conform, T, + ProtoType); conformance->setInvalid(); return conformance; } @@ -1635,18 +1758,19 @@ checkIndividualConformance(NormalProtocolConformance *conformance, bool hasDiagnosed = false; auto *protoFile = Proto->getModuleScopeContext(); if (auto *serialized = dyn_cast(protoFile)) { - if (serialized->getLanguageVersionBuiltWith() != - TC.getLangOpts().EffectiveLanguageVersion) { - TC.diagnose(ComplainLoc, - diag::protocol_has_missing_requirements_versioned, T, - ProtoType, serialized->getLanguageVersionBuiltWith(), - TC.getLangOpts().EffectiveLanguageVersion); + const auto effectiveVers = + getASTContext().LangOpts.EffectiveLanguageVersion; + if (serialized->getLanguageVersionBuiltWith() != effectiveVers) { + C.Diags.diagnose(ComplainLoc, + diag::protocol_has_missing_requirements_versioned, T, + ProtoType, serialized->getLanguageVersionBuiltWith(), + effectiveVers); hasDiagnosed = true; } } if (!hasDiagnosed) { - TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements, T, - ProtoType); + C.Diags.diagnose(ComplainLoc, diag::protocol_has_missing_requirements, T, + ProtoType); } conformance->setInvalid(); return conformance; @@ -1678,7 +1802,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, impliedDisablesMissingWitnessFixits = true; diagnoseConformanceImpliedByConditionalConformance( - TC.Diags, conformance, implyingConf, issueFixit); + C.Diags, conformance, implyingConf, issueFixit); conformance->setInvalid(); } @@ -1691,13 +1815,13 @@ checkIndividualConformance(NormalProtocolConformance *conformance, T, InheritedProto, DC, ConformanceCheckFlags::SkipConditionalRequirements, ComplainLoc); - if (!InheritedConformance || !InheritedConformance->isConcrete()) { + if (InheritedConformance.isInvalid() || + !InheritedConformance.isConcrete()) { // Recursive call already diagnosed this problem, but tack on a note // to establish the relationship. if (ComplainLoc.isValid()) { - TC.diagnose(Proto, - diag::inherited_protocol_does_not_conform, T, - InheritedProto->getDeclaredType()); + C.Diags.diagnose(Proto, diag::inherited_protocol_does_not_conform, T, + InheritedProto->getDeclaredType()); } conformance->setInvalid(); @@ -1709,7 +1833,7 @@ checkIndividualConformance(NormalProtocolConformance *conformance, return conformance; // The conformance checker we're using. - AllUsedCheckers.emplace_back(TC, conformance, MissingWitnesses); + AllUsedCheckers.emplace_back(getASTContext(), conformance, MissingWitnesses); MissingWitnesses.insert(revivedMissingWitnesses.begin(), revivedMissingWitnesses.end()); @@ -1765,7 +1889,7 @@ static Type getTypeForDisplay(ModuleDecl *module, ValueDecl *decl) { if (!decl->getDeclContext()->isTypeContext()) return type; - GenericSignature sigWithoutReqts = GenericSignature(); + GenericSignature sigWithoutReqts; if (auto genericFn = type->getAs()) { // For generic functions, build a new generic function... but strip off // the requirements. They don't add value. @@ -2069,12 +2193,19 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance, break; case MatchKind::TypeConflict: { - auto diag = diags.diagnose(match.Witness, - diag::protocol_witness_type_conflict, - getTypeForDisplay(module, match.Witness), - withAssocTypes); - if (!isa(req)) - fixItOverrideDeclarationTypes(diag, match.Witness, req); + if (!isa(req)) { + computeFixitsForOverridenDeclaration(match.Witness, req, [&](bool){ + return diags.diagnose(match.Witness, + diag::protocol_witness_type_conflict, + getTypeForDisplay(module, match.Witness), + withAssocTypes); + }); + } else { + diags.diagnose(match.Witness, + diag::protocol_witness_type_conflict, + getTypeForDisplay(module, match.Witness), + withAssocTypes); + } break; } @@ -2187,20 +2318,44 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance, case MatchKind::NonObjC: diags.diagnose(match.Witness, diag::protocol_witness_not_objc); break; + case MatchKind::DifferentiableConflict: { + // Emit a note and fix-it showing the missing requirement `@differentiable` + // attribute. + auto *reqAttr = cast(match.UnmetAttribute); + assert(reqAttr); + // Omit printing `wrt:` clause if attribute's differentiability + // parameters match inferred differentiability parameters. + auto *original = cast(match.Witness); + auto *whereClauseGenEnv = + reqAttr->getDerivativeGenericEnvironment(original); + auto *inferredParameters = TypeChecker::inferDifferentiabilityParameters( + original, whereClauseGenEnv); + bool omitWrtClause = reqAttr->getParameterIndices()->getNumIndices() == + inferredParameters->getNumIndices(); + std::string reqDiffAttrString; + llvm::raw_string_ostream os(reqDiffAttrString); + reqAttr->print(os, req, omitWrtClause, /*omitDerivativeFunctions*/ true); + os.flush(); + diags + .diagnose(match.Witness, + diag::protocol_witness_missing_differentiable_attr, + reqDiffAttrString) + .fixItInsert(match.Witness->getStartLoc(), reqDiffAttrString + ' '); + break; + } } } ConformanceChecker::ConformanceChecker( - TypeChecker &tc, NormalProtocolConformance *conformance, - llvm::SetVector &GlobalMissingWitnesses, - bool suppressDiagnostics) - : WitnessChecker(tc, conformance->getProtocol(), - conformance->getType(), + ASTContext &ctx, NormalProtocolConformance *conformance, + llvm::SetVector &GlobalMissingWitnesses, + bool suppressDiagnostics) + : WitnessChecker(ctx, conformance->getProtocol(), conformance->getType(), conformance->getDeclContext()), Conformance(conformance), Loc(conformance->getLoc()), GlobalMissingWitnesses(GlobalMissingWitnesses), LocalMissingWitnessesStartIndex(GlobalMissingWitnesses.size()), - SuppressDiagnostics(suppressDiagnostics) { } + SuppressDiagnostics(suppressDiagnostics) {} ArrayRef ConformanceChecker::getReferencedAssociatedTypes(ValueDecl *req) { @@ -2210,16 +2365,39 @@ ConformanceChecker::getReferencedAssociatedTypes(ValueDecl *req) { return known->second; // Collect the set of associated types rooted on Self in the - // signature. + // signature. Note that for references to nested types, we only + // want to consider the outermost dependent member type. + // + // For example, a requirement typed '(Iterator.Element) -> ()' + // is not considered to reference the associated type 'Iterator'. auto &assocTypes = ReferencedAssociatedTypes[req]; - llvm::SmallPtrSet knownAssocTypes; - req->getInterfaceType()->getCanonicalType().visit([&](CanType type) { - if (auto assocType = getReferencedAssocTypeOfProtocol(type, Proto)) { - if (knownAssocTypes.insert(assocType).second) { - assocTypes.push_back(assocType); + + class Walker : public TypeWalker { + ProtocolDecl *Proto; + llvm::SmallVectorImpl &assocTypes; + llvm::SmallPtrSet knownAssocTypes; + + public: + Walker(ProtocolDecl *Proto, + llvm::SmallVectorImpl &assocTypes) + : Proto(Proto), assocTypes(assocTypes) {} + + Action walkToTypePre(Type type) override { + if (type->is()) { + if (auto assocType = getReferencedAssocTypeOfProtocol(type, Proto)) { + if (knownAssocTypes.insert(assocType).second) + assocTypes.push_back(assocType); } + + return Action::SkipChildren; } - }); + + return Action::Continue; + } + }; + + Walker walker(Proto, assocTypes); + req->getInterfaceType()->getCanonicalType().walk(walker); return assocTypes; } @@ -2228,20 +2406,21 @@ void ConformanceChecker::recordWitness(ValueDecl *requirement, const RequirementMatch &match) { // If we already recorded this witness, don't do so again. if (Conformance->hasWitness(requirement)) { - assert(Conformance->getWitness(requirement).getDecl() == - match.Witness && "Deduced different witnesses?"); + assert(Conformance->getWitnessUncached(requirement).getDecl() == + match.Witness && + "Deduced different witnesses?"); return; } // Record this witness in the conformance. - auto witness = match.getWitness(TC.Context); + auto witness = match.getWitness(getASTContext()); Conformance->setWitness(requirement, witness); } void ConformanceChecker::recordOptionalWitness(ValueDecl *requirement) { // If we already recorded this witness, don't do so again. if (Conformance->hasWitness(requirement)) { - assert(!Conformance->getWitness(requirement).getDecl() && + assert(!Conformance->getWitnessUncached(requirement).getDecl() && "Already have a non-optional witness?"); return; } @@ -2255,7 +2434,7 @@ void ConformanceChecker::recordInvalidWitness(ValueDecl *requirement) { // If we already recorded this witness, don't do so again. if (Conformance->hasWitness(requirement)) { - assert(!Conformance->getWitness(requirement).getDecl() && + assert(!Conformance->getWitnessUncached(requirement).getDecl() && "Already have a non-optional witness?"); return; } @@ -2382,11 +2561,8 @@ static void diagnoseWitnessFixAccessLevel(DiagnosticEngine &diags, if (extAccess < requiredAccess) { shouldMoveToAnotherExtension = true; } else if (extAccess == requiredAccess) { - auto declAttr = decl->getAttrs().getAttribute(); - assert(declAttr && declAttr->getAccess() < requiredAccess && - "expect an explicitly specified access control level which is " - "less accessible than required."); - (void)declAttr; + assert(decl->getFormalAccess() < requiredAccess && + "witness is accessible?"); shouldUseDefaultAccess = true; } } @@ -2413,7 +2589,9 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, // If we already recoded this type witness, there's nothing to do. if (Conformance->hasTypeWitness(assocType)) { - assert(Conformance->getTypeWitness(assocType)->isEqual(type) && + assert(Conformance->getTypeWitnessUncached(assocType) + .getWitnessType() + ->isEqual(type) && "Conflicting type witness deductions"); return; } @@ -2463,12 +2641,9 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, } } else { // If there was no type declaration, synthesize one. - auto aliasDecl = new (TC.Context) TypeAliasDecl(SourceLoc(), - SourceLoc(), - assocType->getName(), - SourceLoc(), - /*genericparams*/nullptr, - DC); + auto aliasDecl = new (getASTContext()) TypeAliasDecl( + SourceLoc(), SourceLoc(), assocType->getName(), SourceLoc(), + /*genericparams*/ nullptr, DC); aliasDecl->setUnderlyingType(type); aliasDecl->setImplicit(); @@ -2479,7 +2654,7 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, if (auto nominal = DC->getSelfNominalTypeDecl()) { AccessScope requiredAccessScope = getRequiredAccessScope(); - if (!TC.Context.isSwiftVersionAtLeast(5) && + if (!getASTContext().isSwiftVersionAtLeast(5) && !DC->getParentModule()->isResilient()) { // HACK: In pre-Swift-5, these typealiases were synthesized with the // same access level as the conforming type, which might be more @@ -2508,7 +2683,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, aliasDecl->setAccess(requiredAccess); if (isUsableFromInlineRequired()) { - auto *attr = new (TC.Context) UsableFromInlineAttr(/*implicit=*/true); + auto *attr = + new (getASTContext()) UsableFromInlineAttr(/*implicit=*/true); aliasDecl->getAttrs().add(attr); } @@ -2548,17 +2724,26 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, auto overriddenConformance = DC->getParentModule()->lookupConformance(Adoptee, overridden->getProtocol()); - if (!overriddenConformance || - !overriddenConformance->isConcrete()) + if (overriddenConformance.isInvalid() || + !overriddenConformance.isConcrete()) continue; auto overriddenRootConformance = - overriddenConformance->getConcrete()->getRootNormalConformance(); - ConformanceChecker(TC, overriddenRootConformance, GlobalMissingWitnesses) - .recordTypeWitness(overridden, type, typeDecl); + overriddenConformance.getConcrete()->getRootNormalConformance(); + ConformanceChecker(getASTContext(), overriddenRootConformance, + GlobalMissingWitnesses) + .recordTypeWitness(overridden, type, typeDecl); } } +/// Whether this protocol is the Objective-C "NSObject" protocol. +static bool isNSObjectProtocol(ProtocolDecl *proto) { + if (proto->getNameStr() != "NSObjectProtocol") + return false; + + return proto->hasClangNode(); +} + bool swift:: printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) { @@ -2620,6 +2805,15 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, Options.FunctionDefinitions = true; Options.PrintAccessorBodiesInProtocols = true; + bool AdopterIsClass = Adopter->getSelfClassDecl() != nullptr; + // Skip 'mutating' only inside classes: mutating methods usually + // don't have a sensible non-mutating implementation. + if (AdopterIsClass) + Options.ExcludeAttrList.push_back(DAK_Mutating); + // 'nonmutating' is only meaningful on value type member accessors. + if (AdopterIsClass || !isa(Requirement)) + Options.ExcludeAttrList.push_back(DAK_NonMutating); + // FIXME: Once we support move-only types, remove this if the // conforming type is move-only. Until then, don't suggest printing // __consuming on a protocol requirement. @@ -2634,10 +2828,19 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter, }; Options.setBaseType(AdopterTy); Options.CurrentModule = Adopter->getParentModule(); - if (!isa(Adopter)) { + if (isa(Adopter)) { // Create a variable declaration instead of a computed property in - // nominal types + // nominal types... Options.PrintPropertyAccessors = false; + + // ...but a non-mutating setter requirement will force us into a + // computed property in non-class adopters; don't leave the user + // wondering why a conformance fails. + if (!AdopterIsClass) + if (const auto VD = dyn_cast(Requirement)) + if (const auto Set = VD->getAccessor(AccessorKind::Set)) + if (Set->getAttrs().hasAttribute()) + Options.PrintPropertyAccessors = true; } Requirement->print(Printer, Options); Printer << "\n"; @@ -2670,8 +2873,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { // If this conformance has nothing to complain, return. if (LocalMissing.empty()) return; + SourceLoc ComplainLoc = Loc; - bool EditorMode = TC.getLangOpts().DiagnosticsEditorMode; + bool EditorMode = getASTContext().LangOpts.DiagnosticsEditorMode; llvm::SetVector MissingWitnesses(GlobalMissingWitnesses.begin(), GlobalMissingWitnesses.end()); auto InsertFixitCallback = [ComplainLoc, EditorMode, MissingWitnesses] @@ -2711,6 +2915,11 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { auto &SM = DC->getASTContext().SourceMgr; auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation); for (auto VD : MissingWitnesses) { + // Don't ever emit a diagnostic for a requirement in the NSObject + // protocol. They're not implementable. + if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl())) + continue; + // Whether this VD has a stub printed. bool AddFixit = !NoStubRequirements.count(VD); bool SameFile = VD->getLoc().isValid() ? @@ -2729,11 +2938,14 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { // because editor may assume the fixit is in the same file with the note. Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type, MissingTypeWitness->getName()); - Diags.diagnose(ComplainLoc, diag::missing_witnesses_general). - fixItInsertAfter(FixitLocation, FixIt); + if (EditorMode) { + Diags.diagnose(ComplainLoc, diag::missing_witnesses_general) + .fixItInsertAfter(FixitLocation, FixIt); + } } continue; } + // Issue diagnostics for witness values. Type RequirementType = getRequirementTypeForDisplay(DC->getParentModule(), Conf, VD); @@ -2743,15 +2955,17 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { // we can directly associate the fixit with the note issued to the // requirement. Diags.diagnose(VD, diag::no_witnesses, getRequirementKind(VD), - VD->getFullName(), RequirementType, true). - fixItInsertAfter(FixitLocation, FixIt); + VD->getFullName(), RequirementType, true) + .fixItInsertAfter(FixitLocation, FixIt); } else { // Otherwise, we have to issue another note to carry the fixit, // because editor may assume the fixit is in the same file with the note. Diags.diagnose(VD, diag::no_witnesses, getRequirementKind(VD), VD->getFullName(), RequirementType, false); - Diags.diagnose(ComplainLoc, diag::missing_witnesses_general). - fixItInsertAfter(FixitLocation, FixIt); + if (EditorMode) { + Diags.diagnose(ComplainLoc, diag::missing_witnesses_general) + .fixItInsertAfter(FixitLocation, FixIt); + } } } else { Diags.diagnose(VD, diag::no_witnesses, getRequirementKind(VD), @@ -2766,8 +2980,8 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) { // If the diagnostics are suppressed, we register these missing witnesses // for later revisiting. Conformance->setInvalid(); - TC.Context.addDelayedMissingWitnesses(Conformance, - MissingWitnesses.getArrayRef()); + getASTContext().addDelayedMissingWitnesses( + Conformance, MissingWitnesses.getArrayRef()); } else { diagnoseOrDefer(LocalMissing[0], true, InsertFixitCallback); } @@ -3015,11 +3229,10 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { // Otherwise, go satisfy the derivable requirement, which can introduce // a member that could in turn satisfy *this* requirement. auto derivableProto = cast(derivable->getDeclContext()); - if (auto conformance = - TypeChecker::conformsToProtocol(Adoptee, derivableProto, - DC, None)) { - if (conformance->isConcrete()) - (void)conformance->getConcrete()->getWitnessDecl(derivable); + auto conformance = + TypeChecker::conformsToProtocol(Adoptee, derivableProto, DC, None); + if (conformance.isConcrete()) { + (void)conformance.getConcrete()->getWitnessDecl(derivable); } } } @@ -3031,8 +3244,8 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { bool doNotDiagnoseMatches = false; bool ignoringNames = false; bool considerRenames = - !canDerive && !requirement->getAttrs().hasAttribute() && - !requirement->getAttrs().isUnavailable(TC.Context); + !canDerive && !requirement->getAttrs().hasAttribute() && + !requirement->getAttrs().isUnavailable(getASTContext()); if (findBestWitness(requirement, considerRenames ? &ignoringNames : nullptr, Conformance, @@ -3092,7 +3305,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { // // Work around this by discarding the witness if its not sufficiently // visible. - if (!TC.Context.isSwiftVersionAtLeast(5)) + if (!getASTContext().isSwiftVersionAtLeast(5)) if (requirement->getAttrs().hasAttribute()) return ResolveWitnessResult::Missing; @@ -3162,7 +3375,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { } case CheckKind::Unavailable: { - auto *attr = requirement->getAttrs().getUnavailable(TC.Context); + auto *attr = requirement->getAttrs().getUnavailable(getASTContext()); diagnoseUnavailableOverride(witness, requirement, attr); break; } @@ -3265,7 +3478,8 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) { // Treat 'unavailable' implicitly as if it were 'optional'. // The compiler will reject actual uses. auto Attrs = requirement->getAttrs(); - if (Attrs.hasAttribute() || Attrs.isUnavailable(TC.Context)) { + if (Attrs.hasAttribute() || + Attrs.isUnavailable(getASTContext())) { return ResolveWitnessResult::Missing; } @@ -3336,12 +3550,13 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDerivation( } // Attempt to derive the witness. - auto derived = TC.deriveProtocolRequirement(DC, derivingTypeDecl, requirement); + auto derived = + TypeChecker::deriveProtocolRequirement(DC, derivingTypeDecl, requirement); if (!derived) return ResolveWitnessResult::ExplicitFailed; // Try to match the derived requirement. - auto match = matchWitness(TC, ReqEnvironmentCache, Proto, Conformance, DC, + auto match = matchWitness(ReqEnvironmentCache, Proto, Conformance, DC, requirement, derived); if (match.isViable()) { recordWitness(requirement, match); @@ -3368,7 +3583,8 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault( // An optional requirement is trivially satisfied with an empty requirement. // An 'unavailable' requirement is treated like an optional requirement. auto Attrs = requirement->getAttrs(); - if (Attrs.hasAttribute() || Attrs.isUnavailable(TC.Context)) { + if (Attrs.hasAttribute() || + Attrs.isUnavailable(getASTContext())) { recordOptionalWitness(requirement); return ResolveWitnessResult::Success; } @@ -3379,16 +3595,16 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault( # pragma mark Type witness resolution -CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc, +CheckTypeWitnessResult swift::checkTypeWitness(DeclContext *dc, ProtocolDecl *proto, - AssociatedTypeDecl *assocType, + AssociatedTypeDecl *assocType, Type type) { auto genericSig = proto->getGenericSignature(); auto *depTy = DependentMemberType::get(proto->getSelfInterfaceType(), assocType); if (type->hasError()) - return ErrorType::get(tc.Context); + return ErrorType::get(proto->getASTContext()); Type contextType = type->hasTypeParameter() ? dc->mapTypeIntoContext(type) : type; @@ -3402,9 +3618,10 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc, // Check protocol conformances. for (auto reqProto : genericSig->getConformsTo(depTy)) { - if (!TypeChecker::conformsToProtocol( - contextType, reqProto, dc, - ConformanceCheckFlags::SkipConditionalRequirements)) + if (TypeChecker::conformsToProtocol( + contextType, reqProto, dc, + ConformanceCheckFlags::SkipConditionalRequirements) + .isInvalid()) return CheckTypeWitnessResult(reqProto->getDeclaredType()); // FIXME: Why is conformsToProtocol() not enough? The stdlib doesn't @@ -3425,7 +3642,7 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc, if (genericSig->requiresClass(depTy) && !contextType->satisfiesClassConstraint()) - return CheckTypeWitnessResult(tc.Context.getAnyObjectType()); + return CheckTypeWitnessResult(proto->getASTContext().getAnyObjectType()); // Success! return CheckTypeWitnessResult(); @@ -3466,8 +3683,9 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( } // Look for a member type with the same name as the associated type. - auto candidates = TC.lookupMemberType(DC, Adoptee, assocType->getName(), - NameLookupFlags::ProtocolMembers); + auto candidates = TypeChecker::lookupMemberType( + DC, Adoptee, assocType->createNameRef(), + NameLookupFlags::ProtocolMembers); // If there aren't any candidates, we're done. if (!candidates) { @@ -3490,7 +3708,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( // Check this type against the protocol requirements. if (auto checkResult = - checkTypeWitness(TC, DC, Proto, assocType, candidate.MemberType)) { + checkTypeWitness(DC, Proto, assocType, candidate.MemberType)) { nonViable.push_back({candidate.Member, checkResult}); } else { viable.push_back(candidate); @@ -3517,7 +3735,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup( } // Record an error. - recordTypeWitness(assocType, ErrorType::get(TC.Context), nullptr); + recordTypeWitness(assocType, ErrorType::get(getASTContext()), nullptr); // If we had multiple viable types, diagnose the ambiguity. if (!viable.empty()) { @@ -3669,7 +3887,7 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() { } } listener(Conformance, fileForCheckingExportability); - auto result = TC.checkGenericArguments( + auto result = TypeChecker::checkGenericArguments( DC, Loc, Loc, // FIXME: maybe this should be the conformance's type proto->getDeclaredInterfaceType(), @@ -3691,8 +3909,8 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() { // Diagnose the failure generically. // FIXME: Would be nice to give some more context here! if (!Conformance->isInvalid()) { - TC.diagnose(Loc, diag::type_does_not_conform, - Adoptee, Proto->getDeclaredType()); + proto->getASTContext().Diags.diagnose(Loc, diag::type_does_not_conform, + Adoptee, Proto->getDeclaredType()); Conformance->setInvalid(); } return; @@ -3718,13 +3936,14 @@ void ConformanceChecker::resolveValueWitnesses() { /// Local function to finalize the witness. auto finalizeWitness = [&] { // Find the witness. - auto witness = Conformance->getWitness(requirement).getDecl(); + auto witness = Conformance->getWitnessUncached(requirement).getDecl(); if (!witness) return; // Objective-C checking for @objc requirements. + auto &C = witness->getASTContext(); if (requirement->isObjC() && requirement->getFullName() == witness->getFullName() && - !requirement->getAttrs().isUnavailable(TC.Context)) { + !requirement->getAttrs().isUnavailable(getASTContext())) { // The witness must also be @objc. if (!witness->isObjC()) { bool isOptional = @@ -3732,19 +3951,18 @@ void ConformanceChecker::resolveValueWitnesses() { SourceLoc diagLoc = getLocForDiagnosingWitness(Conformance, witness); if (auto witnessFunc = dyn_cast(witness)) { auto diagInfo = getObjCMethodDiagInfo(witnessFunc); - Optional fixItDiag = - TC.diagnose(diagLoc, - isOptional ? diag::witness_non_objc_optional - : diag::witness_non_objc, - diagInfo.first, diagInfo.second, - Proto->getFullName()); + Optional fixItDiag = C.Diags.diagnose( + diagLoc, + isOptional ? diag::witness_non_objc_optional + : diag::witness_non_objc, + diagInfo.first, diagInfo.second, Proto->getFullName()); if (diagLoc != witness->getLoc()) { // If the main diagnostic is emitted on the conformance, we want // to attach the fix-it to the note that shows where the // witness is defined. fixItDiag.getValue().flush(); - fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, - witness->getDescriptiveKind())); + fixItDiag.emplace(witness->diagnose( + diag::make_decl_objc, witness->getDescriptiveKind())); } if (!witness->canInferObjCFromRequirement(requirement)) { fixDeclarationObjCName( @@ -3753,20 +3971,19 @@ void ConformanceChecker::resolveValueWitnesses() { requirement->getObjCRuntimeName()); } } else if (isa(witness)) { - Optional fixItDiag = - TC.diagnose(diagLoc, - isOptional ? diag::witness_non_objc_storage_optional - : diag::witness_non_objc_storage, - /*isSubscript=*/false, - witness->getFullName(), - Proto->getFullName()); + Optional fixItDiag = C.Diags.diagnose( + diagLoc, + isOptional ? diag::witness_non_objc_storage_optional + : diag::witness_non_objc_storage, + /*isSubscript=*/false, witness->getFullName(), + Proto->getFullName()); if (diagLoc != witness->getLoc()) { // If the main diagnostic is emitted on the conformance, we want // to attach the fix-it to the note that shows where the // witness is defined. fixItDiag.getValue().flush(); - fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, - witness->getDescriptiveKind())); + fixItDiag.emplace(witness->diagnose( + diag::make_decl_objc, witness->getDescriptiveKind())); } if (!witness->canInferObjCFromRequirement(requirement)) { fixDeclarationObjCName( @@ -3775,21 +3992,19 @@ void ConformanceChecker::resolveValueWitnesses() { requirement->getObjCRuntimeName()); } } else if (isa(witness)) { - Optional fixItDiag = - TC.diagnose(diagLoc, - isOptional - ? diag::witness_non_objc_storage_optional - : diag::witness_non_objc_storage, - /*isSubscript=*/true, - witness->getFullName(), - Proto->getFullName()); + Optional fixItDiag = C.Diags.diagnose( + diagLoc, + isOptional ? diag::witness_non_objc_storage_optional + : diag::witness_non_objc_storage, + /*isSubscript=*/true, witness->getFullName(), + Proto->getFullName()); if (diagLoc != witness->getLoc()) { // If the main diagnostic is emitted on the conformance, we want // to attach the fix-it to the note that shows where the // witness is defined. fixItDiag.getValue().flush(); - fixItDiag.emplace(TC.diagnose(witness, diag::make_decl_objc, - witness->getDescriptiveKind())); + fixItDiag.emplace(witness->diagnose( + diag::make_decl_objc, witness->getDescriptiveKind())); } fixItDiag->fixItInsert(witness->getAttributeInsertionLoc(false), "@objc "); @@ -3798,21 +4013,21 @@ void ConformanceChecker::resolveValueWitnesses() { // If the requirement is optional, @nonobjc suppresses the // diagnostic. if (isOptional) { - TC.diagnose(witness, diag::req_near_match_nonobjc, false) - .fixItInsert(witness->getAttributeInsertionLoc(false), - "@nonobjc "); + witness->diagnose(diag::req_near_match_nonobjc, false) + .fixItInsert(witness->getAttributeInsertionLoc(false), + "@nonobjc "); } - TC.diagnose(requirement, diag::kind_declname_declared_here, - DescriptiveDeclKind::Requirement, - requirement->getFullName()); + requirement->diagnose(diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, + requirement->getFullName()); Conformance->setInvalid(); return; } // The selectors must coincide. - if (checkObjCWitnessSelector(TC, requirement, witness)) { + if (checkObjCWitnessSelector(requirement, witness)) { Conformance->setInvalid(); return; } @@ -3821,16 +4036,16 @@ void ConformanceChecker::resolveValueWitnesses() { // Swift 3 rules, warn if asked. if (auto attr = witness->getAttrs().getAttribute()) { if (attr->isSwift3Inferred() && - TC.Context.LangOpts.WarnSwift3ObjCInference - == Swift3ObjCInferenceWarnings::Minimal) { - TC.diagnose(Conformance->getLoc(), - diag::witness_swift3_objc_inference, - witness->getDescriptiveKind(), witness->getFullName(), - Conformance->getProtocol()->getDeclaredInterfaceType()); - TC.diagnose(witness, diag::make_decl_objc, - witness->getDescriptiveKind()) - .fixItInsert(witness->getAttributeInsertionLoc(false), - "@objc "); + getASTContext().LangOpts.WarnSwift3ObjCInference == + Swift3ObjCInferenceWarnings::Minimal) { + C.Diags.diagnose( + Conformance->getLoc(), diag::witness_swift3_objc_inference, + witness->getDescriptiveKind(), witness->getFullName(), + Conformance->getProtocol()->getDeclaredInterfaceType()); + witness + ->diagnose(diag::make_decl_objc, witness->getDescriptiveKind()) + .fixItInsert(witness->getAttributeInsertionLoc(false), + "@objc "); } } } @@ -3843,7 +4058,7 @@ void ConformanceChecker::resolveValueWitnesses() { } // Make sure we've got an interface type. - if (!requirement->getInterfaceType() || requirement->isInvalid()) { + if (requirement->isInvalid()) { Conformance->setInvalid(); continue; } @@ -3872,7 +4087,7 @@ void ConformanceChecker::resolveValueWitnesses() { void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { assert(!Conformance->isComplete() && "Conformance is already complete"); - FrontendStatsTracer statsTracer(TC.Context.Stats, "check-conformance", + FrontendStatsTracer statsTracer(getASTContext().Stats, "check-conformance", Conformance); llvm::SaveAndRestore restoreSuppressDiagnostics(SuppressDiagnostics); @@ -3930,14 +4145,16 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) { // between an imported Objective-C module and its overlay. if (Proto->isSpecificProtocol(KnownProtocolKind::ObjectiveCBridgeable)) { auto nominal = Adoptee->getAnyNominal(); - if (!TC.Context.isTypeBridgedInExternalModule(nominal)) { - auto clangLoader = TC.Context.getClangModuleLoader(); + if (!getASTContext().isTypeBridgedInExternalModule(nominal)) { + auto clangLoader = getASTContext().getClangModuleLoader(); if (nominal->getParentModule() != DC->getParentModule() && !(clangLoader && clangLoader->isInOverlayModuleForImportedModule(DC, nominal))) { auto nominalModule = nominal->getParentModule(); - TC.diagnose(Loc, diag::nonlocal_bridged_to_objc, nominal->getName(), - Proto->getName(), nominalModule->getName()); + auto &C = nominal->getASTContext(); + C.Diags.diagnose(Loc, diag::nonlocal_bridged_to_objc, + nominal->getName(), Proto->getName(), + nominalModule->getName()); } } } @@ -3980,8 +4197,6 @@ static void diagnoseConformanceFailure(Type T, // conformance to RawRepresentable was inferred. if (auto enumDecl = T->getEnumOrBoundGenericEnum()) { if (Proto->isSpecificProtocol(KnownProtocolKind::RawRepresentable) && - DerivedConformance::derivesProtocolConformance(DC, enumDecl, - Proto) && enumDecl->hasRawType() && !enumDecl->getRawType()->is()) { @@ -3997,8 +4212,9 @@ static void diagnoseConformanceFailure(Type T, if (!equatableProto) return; - if (!TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl, - None)) { + if (TypeChecker::conformsToProtocol(rawType, equatableProto, enumDecl, + None) + .isInvalid()) { SourceLoc loc = enumDecl->getInherited()[0].getSourceRange().Start; diags.diagnose(loc, diag::enum_raw_type_not_equatable, rawType); return; @@ -4008,6 +4224,45 @@ static void diagnoseConformanceFailure(Type T, } } + // One cannot meaningfully declare conformance to the NSObject protocol + // in Swift. Suggest inheritance from NSObject instead. + if (isNSObjectProtocol(Proto)) { + if (T->getClassOrBoundGenericClass()) { + auto diag = + diags.diagnose(ComplainLoc, diag::type_cannot_conform_to_nsobject, + T); + + // Try to suggest inheriting from NSObject instead. + auto classDecl = dyn_cast(DC); + if (!classDecl) + return; + + auto inheritedClause = classDecl->getInherited(); + for (unsigned i : indices(inheritedClause)) { + auto &inherited = inheritedClause[i]; + + // Find the inherited type. + InheritedTypeRequest request{classDecl, i, TypeResolutionStage::Interface}; + Type inheritedTy = evaluateOrDefault(ctx.evaluator, request, Type()); + + // If it's a class, we cannot suggest a different class to inherit + // from. + if (inheritedTy->getClassOrBoundGenericClass()) + return; + + // Is it the NSObject protocol? + if (auto protoTy = inheritedTy->getAs()) { + if (isNSObjectProtocol(protoTy->getDecl())) { + diag.fixItReplace(inherited.getSourceRange(), "NSObject"); + return; + } + } + } + + return; + } + } + diags.diagnose(ComplainLoc, diag::type_does_not_conform, T, Proto->getDeclaredType()); } @@ -4021,12 +4276,9 @@ void ConformanceChecker::diagnoseOrDefer( if (SuppressDiagnostics) { // Stash this in the ASTContext for later emission. auto conformance = Conformance; - TC.Context.addDelayedConformanceDiag(conformance, - { requirement, - [conformance, fn] { - fn(conformance); - }, - isError }); + getASTContext().addDelayedConformanceDiag( + conformance, + {requirement, [conformance, fn] { fn(conformance); }, isError}); return; } @@ -4040,7 +4292,7 @@ void ConformanceChecker::diagnoseOrDefer( } void ConformanceChecker::emitDelayedDiags() { - auto diags = TC.Context.takeDelayedConformanceDiags(Conformance); + auto diags = getASTContext().takeDelayedConformanceDiags(Conformance); assert(!SuppressDiagnostics && "Should not be suppressing diagnostics now"); for (const auto &diag: diags) { @@ -4051,10 +4303,9 @@ void ConformanceChecker::emitDelayedDiags() { } } -Optional TypeChecker::containsProtocol( - Type T, ProtocolDecl *Proto, - DeclContext *DC, - ConformanceCheckOptions options) { +ProtocolConformanceRef +TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, + ConformanceCheckOptions options) { // Existential types don't need to conform, i.e., they only need to // contain the protocol. if (T->isExistentialType()) { @@ -4063,7 +4314,9 @@ Optional TypeChecker::containsProtocol( // First, if we have a superclass constraint, the class may conform // concretely. if (auto superclass = layout.getSuperclass()) { - if (auto result = conformsToProtocol(superclass, Proto, DC, options)) { + auto result = + TypeChecker::conformsToProtocol(superclass, Proto, DC, options); + if (result) { return result; } } @@ -4082,18 +4335,17 @@ Optional TypeChecker::containsProtocol( return ProtocolConformanceRef(Proto); } - return None; + return ProtocolConformanceRef::forInvalid(); } // For non-existential types, this is equivalent to checking conformance. - return conformsToProtocol(T, Proto, DC, options); + return TypeChecker::conformsToProtocol(T, Proto, DC, options); } -Optional TypeChecker::conformsToProtocol( - Type T, ProtocolDecl *Proto, - DeclContext *DC, - ConformanceCheckOptions options, - SourceLoc ComplainLoc) { +ProtocolConformanceRef +TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, + ConformanceCheckOptions options, + SourceLoc ComplainLoc) { bool InExpression = options.contains(ConformanceCheckFlags::InExpression); auto recordDependency = [=](ProtocolConformance *conformance = nullptr) { @@ -4105,18 +4357,18 @@ Optional TypeChecker::conformsToProtocol( // Look up conformance in the module. ModuleDecl *M = DC->getParentModule(); auto lookupResult = M->lookupConformance(T, Proto); - if (!lookupResult) { + if (lookupResult.isInvalid()) { if (ComplainLoc.isValid()) diagnoseConformanceFailure(T, Proto, DC, ComplainLoc); else recordDependency(); - return None; + return ProtocolConformanceRef::forInvalid(); } // Store the conformance and record the dependency. - if (lookupResult->isConcrete()) { - recordDependency(lookupResult->getConcrete()); + if (lookupResult.isConcrete()) { + recordDependency(lookupResult.getConcrete()); } else { recordDependency(); } @@ -4124,7 +4376,7 @@ Optional TypeChecker::conformsToProtocol( if (options.contains(ConformanceCheckFlags::SkipConditionalRequirements)) return lookupResult; - auto condReqs = lookupResult->getConditionalRequirementsIfAvailable(); + auto condReqs = lookupResult.getConditionalRequirementsIfAvailable(); assert(condReqs && "unhandled recursion: missing conditional requirements when they're " "required"); @@ -4133,7 +4385,7 @@ Optional TypeChecker::conformsToProtocol( // we need to check, do so now. if (!condReqs->empty()) { // Figure out the location of the conditional conformance. - auto conformanceDC = lookupResult->getConcrete()->getDeclContext(); + auto conformanceDC = lookupResult.getConcrete()->getDeclContext(); SourceLoc noteLoc; if (auto ext = dyn_cast(conformanceDC)) noteLoc = ext->getLoc(); @@ -4142,8 +4394,7 @@ Optional TypeChecker::conformsToProtocol( auto conditionalCheckResult = checkGenericArguments( DC, ComplainLoc, noteLoc, T, - {lookupResult->getRequirement()->getSelfInterfaceType()}, - *condReqs, + {lookupResult.getRequirement()->getSelfInterfaceType()}, *condReqs, [](SubstitutableType *dependentType) { return Type(dependentType); }, LookUpConformance(DC), options); switch (conditionalCheckResult) { @@ -4152,7 +4403,7 @@ Optional TypeChecker::conformsToProtocol( case RequirementCheckResult::Failure: case RequirementCheckResult::SubstitutionFailure: - return None; + return ProtocolConformanceRef::forInvalid(); } } @@ -4173,7 +4424,7 @@ Optional TypeChecker::conformsToProtocol( // is only valid during type checking, will never be invoked. // // - mapTypeIntoContext will be a nop. -Optional +ProtocolConformanceRef ModuleDecl::conformsToProtocol(Type sourceTy, ProtocolDecl *targetProtocol) { auto flags = ConformanceCheckFlags::SuppressDependencyTracking; @@ -4181,11 +4432,9 @@ ModuleDecl::conformsToProtocol(Type sourceTy, ProtocolDecl *targetProtocol) { return TypeChecker::conformsToProtocol(sourceTy, targetProtocol, this, flags); } -Optional -TypeChecker::LookUpConformance::operator()( - CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const { +ProtocolConformanceRef TypeChecker::LookUpConformance:: +operator()(CanType dependentType, Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const { if (conformingReplacementType->isTypeParameter()) return ProtocolConformanceRef(conformedProtocol); @@ -4198,7 +4447,7 @@ TypeChecker::LookUpConformance::operator()( } void TypeChecker::checkConformance(NormalProtocolConformance *conformance) { - MultiConformanceChecker checker(*this); + MultiConformanceChecker checker(conformance->getProtocol()->getASTContext()); checker.addConformance(conformance); checker.checkAllConformances(); } @@ -4293,32 +4542,30 @@ static Optional scorePotentiallyMatchingNames(DeclName lhs, return score; } -/// Apply omit-needless-words to the given declaration, if possible. -static Optional omitNeedlessWords(TypeChecker &tc, ValueDecl *value) { - if (auto func = dyn_cast(value)) - return tc.omitNeedlessWords(func); - if (auto var = dyn_cast(value)) { - if (auto newName = tc.omitNeedlessWords(var)) - return DeclName(*newName); +/// Determine the score between two potentially-matching declarations. +static Optional +scorePotentiallyMatching(ValueDecl *req, ValueDecl *witness, unsigned limit) { + /// Apply omit-needless-words to the given declaration, if possible. + auto omitNeedlessWordsIfPossible = [](ValueDecl *VD) -> Optional { + if (auto func = dyn_cast(VD)) + return TypeChecker::omitNeedlessWords(func); + if (auto var = dyn_cast(VD)) { + if (auto newName = TypeChecker::omitNeedlessWords(var)) + return DeclName(*newName); + return None; + } return None; - } - return None; -} + }; -/// Determine the score between two potentially-matching declarations. -static Optional scorePotentiallyMatching(TypeChecker &tc, - ValueDecl *req, - ValueDecl *witness, - unsigned limit) { DeclName reqName = req->getFullName(); DeclName witnessName = witness->getFullName(); // For @objc protocols, apply the omit-needless-words heuristics to // both names. if (cast(req->getDeclContext())->isObjC()) { - if (auto adjustedReqName = ::omitNeedlessWords(tc, req)) + if (auto adjustedReqName = omitNeedlessWordsIfPossible(req)) reqName = *adjustedReqName; - if (auto adjustedWitnessName = ::omitNeedlessWords(tc, witness)) + if (auto adjustedWitnessName = omitNeedlessWordsIfPossible(witness)) witnessName = *adjustedWitnessName; } @@ -4501,31 +4748,27 @@ static bool shouldWarnAboutPotentialWitness( } /// Diagnose a potential witness. -static void diagnosePotentialWitness(TypeChecker &tc, - NormalProtocolConformance *conformance, - ValueDecl *req, - ValueDecl *witness, +static void diagnosePotentialWitness(NormalProtocolConformance *conformance, + ValueDecl *req, ValueDecl *witness, AccessLevel access) { auto proto = cast(req->getDeclContext()); // Primary warning. - tc.diagnose(witness, diag::req_near_match, - witness->getDescriptiveKind(), - witness->getFullName(), - req->getAttrs().hasAttribute(), - req->getFullName(), - proto->getFullName()); + witness->diagnose(diag::req_near_match, witness->getDescriptiveKind(), + witness->getFullName(), + req->getAttrs().hasAttribute(), + req->getFullName(), proto->getFullName()); // Describe why the witness didn't satisfy the requirement. WitnessChecker::RequirementEnvironmentCache oneUseCache; auto dc = conformance->getDeclContext(); - auto match = matchWitness(tc, oneUseCache, conformance->getProtocol(), + auto match = matchWitness(oneUseCache, conformance->getProtocol(), conformance, dc, req, witness); if (match.Kind == MatchKind::ExactMatch && req->isObjC() && !witness->isObjC()) { // Special case: note to add @objc. - auto diag = tc.diagnose(witness, - diag::optional_req_nonobjc_near_match_add_objc); + auto diag = + witness->diagnose(diag::optional_req_nonobjc_near_match_add_objc); if (!witness->canInferObjCFromRequirement(req)) fixDeclarationObjCName(diag, witness, witness->getObjCRuntimeName(), @@ -4538,26 +4781,26 @@ static void diagnosePotentialWitness(TypeChecker &tc, // If moving the declaration can help, suggest that. if (auto move = canSuppressPotentialWitnessWarningWithMovement(req, witness)) { - tc.diagnose(witness, diag::req_near_match_move, - witness->getFullName(), static_cast(*move)); + witness->diagnose(diag::req_near_match_move, witness->getFullName(), + static_cast(*move)); } // If adding 'private', 'fileprivate', or 'internal' can help, suggest that. if (access > AccessLevel::FilePrivate && !witness->getAttrs().hasAttribute()) { - tc.diagnose(witness, diag::req_near_match_access, - witness->getFullName(), access) - .fixItInsert(witness->getAttributeInsertionLoc(true), "private "); + witness + ->diagnose(diag::req_near_match_access, witness->getFullName(), access) + .fixItInsert(witness->getAttributeInsertionLoc(true), "private "); } // If adding @nonobjc can help, suggest that. if (canSuppressPotentialWitnessWarningWithNonObjC(req, witness)) { - tc.diagnose(witness, diag::req_near_match_nonobjc, false) - .fixItInsert(witness->getAttributeInsertionLoc(false), "@nonobjc "); + witness->diagnose(diag::req_near_match_nonobjc, false) + .fixItInsert(witness->getAttributeInsertionLoc(false), "@nonobjc "); } - tc.diagnose(req, diag::kind_declname_declared_here, - DescriptiveDeclKind::Requirement, req->getFullName()); + req->diagnose(diag::kind_declname_declared_here, + DescriptiveDeclKind::Requirement, req->getFullName()); } /// Whether the given protocol is "NSCoding". @@ -4585,9 +4828,8 @@ static bool hasExplicitObjCName(ClassDecl *classDecl) { /// Check if the name of a class might be unstable, and if so, emit a /// diag::nscoding_unstable_mangled_name diagnostic. -static void -diagnoseUnstableName(TypeChecker &tc, ProtocolConformance *conformance, - ClassDecl *classDecl) { +static void diagnoseUnstableName(ProtocolConformance *conformance, + ClassDecl *classDecl) { // Note: these 'kind' values are synchronized with // diag::nscoding_unstable_mangled_name. enum class UnstableNameKind : unsigned { @@ -4619,13 +4861,14 @@ diagnoseUnstableName(TypeChecker &tc, ProtocolConformance *conformance, } } - if (kind && tc.getLangOpts().EnableNSKeyedArchiverDiagnostics && + auto &C = classDecl->getASTContext(); + if (kind && C.LangOpts.EnableNSKeyedArchiverDiagnostics && isa(conformance) && !hasExplicitObjCName(classDecl)) { - tc.diagnose(cast(conformance)->getLoc(), - diag::nscoding_unstable_mangled_name, - static_cast(kind.getValue()), - classDecl->getDeclaredInterfaceType()); + C.Diags.diagnose(cast(conformance)->getLoc(), + diag::nscoding_unstable_mangled_name, + static_cast(kind.getValue()), + classDecl->getDeclaredInterfaceType()); auto insertionLoc = classDecl->getAttributeInsertionLoc(/*forModifier=*/false); // Note: this is intentionally using the Swift 3 mangling, @@ -4635,26 +4878,25 @@ diagnoseUnstableName(TypeChecker &tc, ProtocolConformance *conformance, std::string mangledName = mangler.mangleObjCRuntimeName(classDecl); assert(Lexer::isIdentifier(mangledName) && "mangled name is not an identifier; can't use @objc"); - tc.diagnose(classDecl, diag::unstable_mangled_name_add_objc) - .fixItInsert(insertionLoc, - "@objc(" + mangledName + ")"); - tc.diagnose(classDecl, diag::unstable_mangled_name_add_objc_new) - .fixItInsert(insertionLoc, - "@objc(<#prefixed Objective-C class name#>)"); + classDecl->diagnose(diag::unstable_mangled_name_add_objc) + .fixItInsert(insertionLoc, "@objc(" + mangledName + ")"); + classDecl->diagnose(diag::unstable_mangled_name_add_objc_new) + .fixItInsert(insertionLoc, + "@objc(<#prefixed Objective-C class name#>)"); } } /// Infer the attribute tostatic-initialize the Objective-C metadata for the /// given class, if needed. -static void inferStaticInitializeObjCMetadata(TypeChecker &tc, - ClassDecl *classDecl) { +static void inferStaticInitializeObjCMetadata(ClassDecl *classDecl) { // If we already have the attribute, there's nothing to do. if (classDecl->getAttrs().hasAttribute()) return; // If the class does not have a custom @objc name and the deployment target // supports the objc_getClass() hook, the workaround is unnecessary. - if (tc.Context.LangOpts.doesTargetSupportObjCGetClassHook() && + ASTContext &ctx = classDecl->getASTContext(); + if (ctx.LangOpts.doesTargetSupportObjCGetClassHook() && !hasExplicitObjCName(classDecl)) return; @@ -4674,21 +4916,19 @@ static void inferStaticInitializeObjCMetadata(TypeChecker &tc, for (Decl *enclosingDecl = classDecl; enclosingDecl; enclosingDecl = enclosingDecl->getDeclContext() ->getInnermostDeclarationDeclContext()) { - if (!tc.isDeclAvailable(enclosingDecl, SourceLoc(), sourceFile, - availableInfo)) + if (!TypeChecker::isDeclAvailable(enclosingDecl, SourceLoc(), sourceFile, + availableInfo)) return; } } // Infer @_staticInitializeObjCMetadata. - ASTContext &ctx = classDecl->getASTContext(); classDecl->getAttrs().add( new (ctx) StaticInitializeObjCMetadataAttr(/*implicit=*/true)); } static void -diagnoseMissingAppendInterpolationMethod(TypeChecker &tc, - NominalTypeDecl *typeDecl) { +diagnoseMissingAppendInterpolationMethod(NominalTypeDecl *typeDecl) { struct InvalidMethod { enum class Reason : unsigned { ReturnType, @@ -4701,69 +4941,72 @@ diagnoseMissingAppendInterpolationMethod(TypeChecker &tc, InvalidMethod(FuncDecl *method, Reason reason) : method(method), reason(reason) {} - - static bool hasValidMethod(TypeChecker &tc, NominalTypeDecl *typeDecl, - SmallVectorImpl &invalid) { + + static bool hasValidMethod(NominalTypeDecl *typeDecl, + SmallVectorImpl &invalid) { auto type = typeDecl->getDeclaredType(); - auto baseName = DeclName(tc.Context.Id_appendInterpolation); + DeclNameRef baseName(typeDecl->getASTContext().Id_appendInterpolation); auto lookupOptions = defaultMemberTypeLookupOptions; lookupOptions -= NameLookupFlags::PerformConformanceCheck; - - for (auto resultEntry : tc.lookupMember(typeDecl, type, baseName, - lookupOptions)) { + + for (auto resultEntry : + TypeChecker::lookupMember(typeDecl, type, baseName, lookupOptions)) { auto method = dyn_cast(resultEntry.getValueDecl()); if (!method) continue; if (method->isStatic()) { - invalid.push_back(InvalidMethod(method, Reason::Static)); + invalid.emplace_back(method, Reason::Static); continue; } if (!method->getResultInterfaceType()->isVoid() && !method->getAttrs().hasAttribute()) { - invalid.push_back(InvalidMethod(method, Reason::ReturnType)); + invalid.emplace_back(method, Reason::ReturnType); continue; } if (method->getFormalAccess() < typeDecl->getFormalAccess()) { - invalid.push_back(InvalidMethod(method, Reason::AccessControl)); + invalid.emplace_back(method, Reason::AccessControl); continue; } return true; } - + return false; } }; SmallVector invalidMethods; - - if (InvalidMethod::hasValidMethod(tc, typeDecl, invalidMethods)) + + if (InvalidMethod::hasValidMethod(typeDecl, invalidMethods)) return; - - tc.diagnose(typeDecl, diag::missing_append_interpolation); - + + typeDecl->diagnose(diag::missing_append_interpolation); + + auto &C = typeDecl->getASTContext(); for (auto invalidMethod : invalidMethods) { switch (invalidMethod.reason) { case InvalidMethod::Reason::Static: - tc.diagnose(invalidMethod.method->getStaticLoc(), - diag::append_interpolation_static) + C.Diags + .diagnose(invalidMethod.method->getStaticLoc(), + diag::append_interpolation_static) .fixItRemove(invalidMethod.method->getStaticLoc()); break; case InvalidMethod::Reason::ReturnType: - tc.diagnose(invalidMethod.method->getBodyResultTypeLoc().getLoc(), - diag::append_interpolation_void_or_discardable) + C.Diags + .diagnose(invalidMethod.method->getBodyResultTypeLoc().getLoc(), + diag::append_interpolation_void_or_discardable) .fixItInsert(invalidMethod.method->getStartLoc(), "@discardableResult "); break; case InvalidMethod::Reason::AccessControl: - tc.diagnose(invalidMethod.method, - diag::append_interpolation_access_control, - invalidMethod.method->getFormalAccess(), - typeDecl->getName(), typeDecl->getFormalAccess()); + C.Diags.diagnose(invalidMethod.method, + diag::append_interpolation_access_control, + invalidMethod.method->getFormalAccess(), + typeDecl->getName(), typeDecl->getFormalAccess()); } } } @@ -4799,7 +5042,8 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, &diagnostics); // The conformance checker bundle that checks all conformances in the context. - MultiConformanceChecker groupChecker(*this); + auto &Context = dc->getASTContext(); + MultiConformanceChecker groupChecker(Context); bool anyInvalid = false; for (auto conformance : conformances) { @@ -4819,16 +5063,16 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, isNSCoding(conformance->getProtocol()) && !classDecl->isGenericContext() && !classDecl->hasClangNode()) { - diagnoseUnstableName(*this, conformance, classDecl); + diagnoseUnstableName(conformance, classDecl); // Infer @_staticInitializeObjCMetadata if needed. - inferStaticInitializeObjCMetadata(*this, classDecl); + inferStaticInitializeObjCMetadata(classDecl); } } if (conformance->getProtocol()-> isSpecificProtocol(KnownProtocolKind::StringInterpolationProtocol)) { if (auto typeDecl = dc->getSelfNominalTypeDecl()) { - diagnoseMissingAppendInterpolationMethod(*this, typeDecl); + diagnoseMissingAppendInterpolationMethod(typeDecl); } } } @@ -4836,12 +5080,12 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, // Check all conformances. groupChecker.checkAllConformances(); - if (Context.LangOpts.DebugGenericSignatures) { + if (Context.TypeCheckerOpts.DebugGenericSignatures) { // Now that they're filled out, print out information about the conformances // here, when requested. for (auto conformance : conformances) { - dc->dumpContext(); - conformance->dump(); + dc->printContext(llvm::errs()); + conformance->dump(llvm::errs()); } } @@ -4863,8 +5107,8 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, auto currentSig = dc->getGenericSignatureOfContext(); auto existingSig = diag.ExistingDC->getGenericSignatureOfContext(); auto differentlyConditional = currentSig && existingSig && - currentSig->getCanonicalSignature() != - existingSig->getCanonicalSignature(); + currentSig.getCanonicalSignature() != + existingSig.getCanonicalSignature(); // If we've redundantly stated a conformance for which the original // conformance came from the module of the type or the module of the @@ -4879,11 +5123,11 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, auto diagID = differentlyConditional ? diag::redundant_conformance_adhoc_conditional : diag::redundant_conformance_adhoc; - diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), - diag.Protocol->getName(), - existingModule->getName() == - extendedNominal->getParentModule()->getName(), - existingModule->getName()); + Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), + diag.Protocol->getName(), + existingModule->getName() == + extendedNominal->getParentModule()->getName(), + existingModule->getName()); // Complain about any declarations in this extension whose names match // a requirement in that protocol. @@ -4899,8 +5143,10 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, continue; bool valueIsType = isa(value); + const auto flags = + NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; for (auto requirement - : diag.Protocol->lookupDirect(value->getFullName())) { + : diag.Protocol->lookupDirect(value->getFullName(), flags)) { if (requirement->getDeclContext() != diag.Protocol) continue; @@ -4908,9 +5154,9 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, if (valueIsType != requirementIsType) continue; - diagnose(value, diag::redundant_conformance_witness_ignored, - value->getDescriptiveKind(), value->getFullName(), - diag.Protocol->getFullName()); + value->diagnose(diag::redundant_conformance_witness_ignored, + value->getDescriptiveKind(), value->getFullName(), + diag.Protocol->getFullName()); break; } } @@ -4918,8 +5164,8 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, auto diagID = differentlyConditional ? diag::redundant_conformance_conditional : diag::redundant_conformance; - diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), - diag.Protocol->getName()); + Context.Diags.diagnose(diag.Loc, diagID, dc->getDeclaredInterfaceType(), + diag.Protocol->getName()); } // Special case: explain that 'RawRepresentable' conformance @@ -4931,18 +5177,19 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, diag.Protocol) && enumDecl->hasRawType() && enumDecl->getInherited()[0].getSourceRange().isValid()) { - diagnose(enumDecl->getInherited()[0].getSourceRange().Start, - diag::enum_declares_rawrep_with_raw_type, - dc->getDeclaredInterfaceType(), enumDecl->getRawType()); + auto inheritedLoc = enumDecl->getInherited()[0].getSourceRange().Start; + Context.Diags.diagnose( + inheritedLoc, diag::enum_declares_rawrep_with_raw_type, + dc->getDeclaredInterfaceType(), enumDecl->getRawType()); continue; } } - diagnose(existingDecl, diag::declared_protocol_conformance_here, - dc->getDeclaredInterfaceType(), - static_cast(diag.ExistingKind), - diag.Protocol->getName(), - diag.ExistingExplicitProtocol->getName()); + existingDecl->diagnose(diag::declared_protocol_conformance_here, + dc->getDeclaredInterfaceType(), + static_cast(diag.ExistingKind), + diag.Protocol->getName(), + diag.ExistingExplicitProtocol->getName()); } // If there were any unsatisfied requirements, check whether there @@ -4978,7 +5225,7 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, if (req->getAttrs().isUnavailable(Context)) continue; // Score this particular optional requirement. - auto score = scorePotentiallyMatching(*this, req, value, bestScore); + auto score = scorePotentiallyMatching(req, value, bestScore); if (!score) continue; // If the score is better than the best we've seen, update the best @@ -5014,8 +5261,7 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc, bool diagnosed = false; for (auto conformance : conformances) { if (conformance->getProtocol() == req->getDeclContext()) { - diagnosePotentialWitness(*this, - conformance->getRootNormalConformance(), + diagnosePotentialWitness(conformance->getRootNormalConformance(), req, value, defaultAccess); diagnosed = true; break; @@ -5101,7 +5347,8 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, if (!proto->isObjC()) continue; Optional conformance; - for (auto req : proto->lookupDirect(name)) { + const auto flags = NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; + for (auto req : proto->lookupDirect(name, flags)) { // Skip anything in a protocol extension. if (req->getDeclContext() != proto) continue; @@ -5147,10 +5394,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, if (accessorKind) witnessToMatch = cast(witness)->getStorage(); - auto lazyResolver = ctx.getLazyResolver(); - assert(lazyResolver && "Need a type checker to match witnesses"); - auto &tc = *static_cast(lazyResolver); - if (matchWitness(tc, reqEnvCache, proto, *conformance, + if (matchWitness(reqEnvCache, proto, *conformance, witnessToMatch->getDeclContext(), req, const_cast(witnessToMatch)) .Kind == MatchKind::ExactMatch) { @@ -5206,27 +5450,43 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, return result; } -void TypeChecker::resolveTypeWitness( - const NormalProtocolConformance *conformance, - AssociatedTypeDecl *assocType) { +llvm::Expected +TypeWitnessRequest::evaluate(Evaluator &eval, + NormalProtocolConformance *conformance, + AssociatedTypeDecl *requirement) const { llvm::SetVector MissingWitnesses; - ConformanceChecker checker( - *this, - const_cast(conformance), - MissingWitnesses); - checker.resolveSingleTypeWitness(assocType); + ConformanceChecker checker(requirement->getASTContext(), conformance, + MissingWitnesses); + checker.resolveSingleTypeWitness(requirement); checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt); + // FIXME: ConformanceChecker and the other associated WitnessCheckers have + // an extremely convoluted caching scheme that doesn't fit nicely into the + // evaluator's model. All of this should be refactored away. + const auto known = conformance->TypeWitnesses.find(requirement); + assert(known != conformance->TypeWitnesses.end() && + "Didn't resolve witness?"); + return known->second; } -void TypeChecker::resolveWitness(const NormalProtocolConformance *conformance, - ValueDecl *requirement) { +llvm::Expected +ValueWitnessRequest::evaluate(Evaluator &eval, + NormalProtocolConformance *conformance, + ValueDecl *requirement) const { llvm::SetVector MissingWitnesses; - ConformanceChecker checker( - *this, - const_cast(conformance), - MissingWitnesses); + ConformanceChecker checker(requirement->getASTContext(), conformance, + MissingWitnesses); checker.resolveSingleWitness(requirement); checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt); + // FIXME: ConformanceChecker and the other associated WitnessCheckers have + // an extremely convoluted caching scheme that doesn't fit nicely into the + // evaluator's model. All of this should be refactored away. + const auto known = conformance->Mapping.find(requirement); + if (known == conformance->Mapping.end()) { + assert((!conformance->isComplete() || conformance->isInvalid()) && + "Resolver did not resolve requirement"); + return Witness(); + } + return known->second; } ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, @@ -5245,7 +5505,8 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, if (Decl->isInvalid()) return nullptr; - DerivedConformance derived(*this, Decl, TypeDecl, protocol); + DerivedConformance derived(TypeDecl->getASTContext(), Decl, TypeDecl, + protocol); switch (*knownKind) { case KnownProtocolKind::RawRepresentable: @@ -5289,7 +5550,8 @@ Type TypeChecker::deriveTypeWitness(DeclContext *DC, auto Decl = DC->getInnermostDeclarationDeclContext(); - DerivedConformance derived(*this, Decl, TypeDecl, protocol); + DerivedConformance derived(TypeDecl->getASTContext(), Decl, TypeDecl, + protocol); switch (*knownKind) { case KnownProtocolKind::RawRepresentable: return derived.deriveRawRepresentable(AssocType); @@ -5304,9 +5566,9 @@ namespace { class DefaultWitnessChecker : public WitnessChecker { public: - DefaultWitnessChecker(TypeChecker &tc, - ProtocolDecl *proto) - : WitnessChecker(tc, proto, proto->getDeclaredType(), proto) { } + DefaultWitnessChecker(ProtocolDecl *proto) + : WitnessChecker(proto->getASTContext(), proto, + proto->getDeclaredType(), proto) {} ResolveWitnessResult resolveWitnessViaLookup(ValueDecl *requirement); void recordWitness(ValueDecl *requirement, const RequirementMatch &match); @@ -5348,30 +5610,25 @@ DefaultWitnessChecker::resolveWitnessViaLookup(ValueDecl *requirement) { void DefaultWitnessChecker::recordWitness( ValueDecl *requirement, const RequirementMatch &match) { - Proto->setDefaultWitness(requirement, - match.getWitness(TC.Context)); + Proto->setDefaultWitness(requirement, match.getWitness(getASTContext())); } void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { - DefaultWitnessChecker checker(*this, proto); + DefaultWitnessChecker checker(proto); // Find the default for the given associated type. - auto findAssociatedTypeDefault = - [&](AssociatedTypeDecl *assocType, - AssociatedTypeDecl **defaultedAssocTypeOut = nullptr) -> Type { + auto findAssociatedTypeDefault = [](AssociatedTypeDecl *assocType) + -> std::pair { auto defaultedAssocType = - AssociatedTypeInference::findDefaultedAssociatedType(*this, assocType); + AssociatedTypeInference::findDefaultedAssociatedType(assocType); if (!defaultedAssocType) - return nullptr;; + return {Type(), nullptr}; Type defaultType = defaultedAssocType->getDefaultDefinitionType(); if (!defaultType) - return nullptr; - - if (defaultedAssocTypeOut) - *defaultedAssocTypeOut = defaultedAssocType; + return {Type(), nullptr}; - return defaultType; + return {defaultType, defaultedAssocType}; }; for (auto *requirement : proto->getMembers()) { @@ -5384,7 +5641,7 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { if (auto assocType = dyn_cast(valueDecl)) { if (assocType->getOverriddenDecls().empty()) { - if (Type defaultType = findAssociatedTypeDefault(assocType)) + if (Type defaultType = findAssociatedTypeDefault(assocType).first) proto->setDefaultTypeWitness(assocType, defaultType); } @@ -5431,7 +5688,7 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { if (assocType->getProtocol() != proto) { SmallVector found; proto->getModuleContext()->lookupQualified( - proto, assocType->getFullName(), + proto, DeclNameRef(assocType->getFullName()), NL_QualifiedDefault|NL_ProtocolMembers|NL_OnlyTypes, found); if (found.size() == 1 && isa(found[0])) @@ -5439,9 +5696,10 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { } // Dig out the default associated type definition. - AssociatedTypeDecl *defaultedAssocType = nullptr; - Type defaultAssocType = findAssociatedTypeDefault(assocType, - &defaultedAssocType); + AssociatedTypeDecl *defaultedAssocDecl = nullptr; + Type defaultAssocType; + std::tie(defaultAssocType, defaultedAssocDecl) = + findAssociatedTypeDefault(assocType); if (!defaultAssocType) continue; @@ -5451,22 +5709,23 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { req.getSecondType()->castTo()->getDecl(); auto conformance = conformsToProtocol(defaultAssocTypeInContext, requirementProto, proto, None); - if (!conformance) { + if (conformance.isInvalid()) { // Diagnose the lack of a conformance. This is potentially an ABI // incompatibility. - diagnose(proto, diag::assoc_type_default_conformance_failed, - defaultAssocType, assocType->getFullName(), req.getFirstType(), - req.getSecondType()); - diagnose(defaultedAssocType, diag::assoc_type_default_here, - assocType->getFullName(), defaultAssocType) - .highlight( - defaultedAssocType->getDefaultDefinitionTypeRepr()->getSourceRange()); + proto->diagnose(diag::assoc_type_default_conformance_failed, + defaultAssocType, assocType->getFullName(), + req.getFirstType(), req.getSecondType()); + defaultedAssocDecl + ->diagnose(diag::assoc_type_default_here, assocType->getFullName(), + defaultAssocType) + .highlight(defaultedAssocDecl->getDefaultDefinitionTypeRepr() + ->getSourceRange()); continue; } // Record the default associated conformance. proto->setDefaultAssociatedConformanceWitness( - req.getFirstType()->getCanonicalType(), requirementProto, *conformance); + req.getFirstType()->getCanonicalType(), requirementProto, conformance); } } diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index ca23ac08c66ed..ec45d594fc499 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -24,6 +24,7 @@ #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/AST/Witness.h" +#include "swift/Basic/Debug.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -94,8 +95,7 @@ class CheckTypeWitnessResult { /// associated type. /// /// \returns an empty result on success, or a description of the error. -CheckTypeWitnessResult checkTypeWitness(TypeChecker &tc, DeclContext *dc, - ProtocolDecl *proto, +CheckTypeWitnessResult checkTypeWitness(DeclContext *dc, ProtocolDecl *proto, AssociatedTypeDecl *assocType, Type type); @@ -115,8 +115,7 @@ struct InferredAssociatedTypesByWitness { void dump(llvm::raw_ostream &out, unsigned indent) const; - LLVM_ATTRIBUTE_DEPRECATED(void dump() const, - "only for use in the debugger"); + SWIFT_DEBUG_DUMP; }; /// The set of witnesses that were considered when attempting to @@ -210,6 +209,10 @@ enum class MatchKind : uint8_t { /// The witness is explicitly @nonobjc but the requirement is @objc. NonObjC, + + /// The witness does not have a `@differentiable` attribute satisfying one + /// from the requirement. + DifferentiableConflict, }; /// Describes the kind of optional adjustment performed when @@ -355,6 +358,14 @@ struct RequirementMatch { assert(!hasWitnessType() && "Should have witness type"); } + RequirementMatch(ValueDecl *witness, MatchKind kind, + const DeclAttribute *attr) + : Witness(witness), Kind(kind), WitnessType(), UnmetAttribute(attr), + ReqEnv(None) { + assert(!hasWitnessType() && "Should have witness type"); + assert(UnmetAttribute); + } + RequirementMatch(ValueDecl *witness, MatchKind kind, Type witnessType, Optional env = None, @@ -391,6 +402,9 @@ struct RequirementMatch { /// Requirement not met. Optional MissingRequirement; + /// Unmet attribute from the requirement. + const DeclAttribute *UnmetAttribute = nullptr; + /// The requirement environment to use for the witness thunk. Optional ReqEnv; @@ -424,6 +438,7 @@ struct RequirementMatch { case MatchKind::RethrowsConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: + case MatchKind::DifferentiableConflict: return false; } @@ -453,6 +468,7 @@ struct RequirementMatch { case MatchKind::RethrowsConflict: case MatchKind::ThrowsConflict: case MatchKind::NonObjC: + case MatchKind::DifferentiableConflict: return false; } @@ -462,6 +478,9 @@ struct RequirementMatch { /// Determine whether this requirement match has a requirement. bool hasRequirement() { return Kind == MatchKind::MissingRequirement; } + /// Determine whether this requirement match has an unmet attribute. + bool hasUnmetAttribute() { return Kind == MatchKind::DifferentiableConflict; } + swift::Witness getWitness(ASTContext &ctx) const; }; @@ -475,12 +494,14 @@ class WitnessChecker { llvm::DenseMap; protected: - TypeChecker &TC; + ASTContext &Context; ProtocolDecl *Proto; Type Adoptee; // The conforming context, either a nominal type or extension. DeclContext *DC; + ASTContext &getASTContext() const { return Context; } + // An auxiliary lookup table to be used for witnesses remapped via // @_implements(Protocol, DeclName) llvm::DenseMap> ImplementsTable; @@ -489,8 +510,8 @@ class WitnessChecker { Optional> RequiredAccessScopeAndUsableFromInline; - WitnessChecker(TypeChecker &tc, ProtocolDecl *proto, - Type adoptee, DeclContext *dc); + WitnessChecker(ASTContext &ctx, ProtocolDecl *proto, Type adoptee, + DeclContext *dc); bool isMemberOperator(FuncDecl *decl, Type type); @@ -675,8 +696,8 @@ class ConformanceChecker : public WitnessChecker { /// Emit any diagnostics that have been delayed. void emitDelayedDiags(); - ConformanceChecker(TypeChecker &tc, NormalProtocolConformance *conformance, - llvm::SetVector &GlobalMissingWitnesses, + ConformanceChecker(ASTContext &ctx, NormalProtocolConformance *conformance, + llvm::SetVector &GlobalMissingWitnesses, bool suppressDiagnostics = true); /// Resolve all of the type witnesses. @@ -711,7 +732,7 @@ class ConformanceChecker : public WitnessChecker { /// Captures the state needed to infer associated types. class AssociatedTypeInference { /// The type checker we'll need to validate declarations etc. - TypeChecker &tc; + ASTContext &ctx; /// The conformance for which we are inferring associated types. NormalProtocolConformance *conformance; @@ -751,10 +772,13 @@ class AssociatedTypeInference { unsigned numTypeWitnessesBeforeConflict = 0; public: - AssociatedTypeInference(TypeChecker &tc, + AssociatedTypeInference(ASTContext &ctx, NormalProtocolConformance *conformance); private: + /// Retrieve the AST context. + ASTContext &getASTContext() const { return ctx; } + /// Infer associated type witnesses for the given tentative /// requirement/witness match. InferredAssociatedTypesByWitness inferTypeWitnessesViaValueWitness( @@ -879,7 +903,6 @@ class AssociatedTypeInference { /// Find an associated type declaration that provides a default definition. static AssociatedTypeDecl *findDefaultedAssociatedType( - TypeChecker &tc, AssociatedTypeDecl *assocType); }; @@ -898,10 +921,9 @@ RequirementMatch matchWitness( > finalize); RequirementMatch - matchWitness(TypeChecker &tc, - WitnessChecker::RequirementEnvironmentCache &reqEnvCache, - ProtocolDecl *proto, ProtocolConformance *conformance, - DeclContext *dc, ValueDecl *req, ValueDecl *witness); +matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache, + ProtocolDecl *proto, ProtocolConformance *conformance, + DeclContext *dc, ValueDecl *req, ValueDecl *witness); /// If the given type is a direct reference to an associated type of /// the given protocol, return the referenced associated type. diff --git a/lib/Sema/TypeCheckProtocolInference.cpp b/lib/Sema/TypeCheckProtocolInference.cpp index 27143998d343d..061aac27b2861 100644 --- a/lib/Sema/TypeCheckProtocolInference.cpp +++ b/lib/Sema/TypeCheckProtocolInference.cpp @@ -86,10 +86,10 @@ void InferredTypeWitnessesSolution::dump() const { auto &valueWitness = ValueWitnesses[i]; llvm::errs() << i << ": " << (Decl*)valueWitness.first << ' ' << valueWitness.first->getBaseName() << '\n'; - valueWitness.first->getDeclContext()->dumpContext(); + valueWitness.first->getDeclContext()->printContext(llvm::errs()); llvm::errs() << " for " << (Decl*)valueWitness.second << ' ' << valueWitness.second->getBaseName() << '\n'; - valueWitness.second->getDeclContext()->dumpContext(); + valueWitness.second->getDeclContext()->printContext(llvm::errs()); } } @@ -133,13 +133,9 @@ namespace { } AssociatedTypeInference::AssociatedTypeInference( - TypeChecker &tc, - NormalProtocolConformance *conformance) - : tc(tc), conformance(conformance), proto(conformance->getProtocol()), - dc(conformance->getDeclContext()), - adoptee(conformance->getType()) -{ -} + ASTContext &ctx, NormalProtocolConformance *conformance) + : ctx(ctx), conformance(conformance), proto(conformance->getProtocol()), + dc(conformance->getDeclContext()), adoptee(conformance->getType()) {} static bool associatedTypesAreSameEquivalenceClass(AssociatedTypeDecl *a, AssociatedTypeDecl *b) { @@ -217,8 +213,9 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( case RequirementKind::Conformance: case RequirementKind::Superclass: // FIXME: This is the wrong check - if (selfTy->isEqual(reqt.getFirstType()) - && !tc.isSubtypeOf(conformance->getType(),reqt.getSecondType(), dc)) + if (selfTy->isEqual(reqt.getFirstType()) && + !TypeChecker::isSubtypeOf(conformance->getType(), + reqt.getSecondType(), dc)) return false; break; @@ -378,8 +375,8 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( if (!canInferFromOtherAssociatedType) { // Check that the type witness meets the // requirements on the associated type. - if (auto failed = checkTypeWitness(tc, dc, proto, result.first, - result.second)) { + if (auto failed = + checkTypeWitness(dc, proto, result.first, result.second)) { witnessResult.NonViable.push_back( std::make_tuple(result.first,result.second,failed)); LLVM_DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n"); @@ -447,7 +444,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses( } // Validate the requirement. - if (!req->getInterfaceType() || req->isInvalid()) + if (req->isInvalid()) continue; // Check whether any of the associated types we care about are @@ -489,15 +486,11 @@ static Type mapErrorTypeToOriginal(Type type) { } /// Produce the type when matching a witness. -static Type getWitnessTypeForMatching(TypeChecker &tc, - NormalProtocolConformance *conformance, +static Type getWitnessTypeForMatching(NormalProtocolConformance *conformance, ValueDecl *witness) { if (witness->isRecursiveValidation()) return Type(); - // FIXME: This is here to trigger the isInvalid() computation. - (void) witness->getInterfaceType(); - if (witness->isInvalid()) return Type(); @@ -554,7 +547,6 @@ static Type getWitnessTypeForMatching(TypeChecker &tc, return resultType.transform(mapErrorTypeToOriginal); } - /// Remove the 'self' type from the given type, if it's a method type. static Type removeSelfParam(ValueDecl *value, Type type) { if (auto func = dyn_cast(value)) { @@ -570,10 +562,8 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType( ConformanceChecker &checker, const llvm::SetVector &allUnresolved, AssociatedTypeDecl *assocType) { - auto &tc = checker.TC; - // Form the default name _Default_Foo. - Identifier defaultName; + DeclNameRef defaultName; { SmallString<32> defaultNameStr; { @@ -582,7 +572,7 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType( out << assocType->getName().str(); } - defaultName = tc.Context.getIdentifier(defaultNameStr); + defaultName = DeclNameRef(getASTContext().getIdentifier(defaultNameStr)); } // Look for types with the given default name that have appropriate @@ -590,8 +580,8 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType( InferredAssociatedTypesByWitnesses result; auto lookupOptions = defaultMemberTypeLookupOptions; lookupOptions -= NameLookupFlags::PerformConformanceCheck; - for (auto candidate : tc.lookupMember(dc, adoptee, defaultName, - lookupOptions)) { + for (auto candidate : + TypeChecker::lookupMember(dc, adoptee, defaultName, lookupOptions)) { // We want type declarations. auto typeDecl = dyn_cast(candidate.getValueDecl()); if (!typeDecl || isa(typeDecl)) @@ -603,7 +593,7 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType( continue; // Determine the witness type. - Type witnessType = getWitnessTypeForMatching(tc, conformance, typeDecl); + Type witnessType = getWitnessTypeForMatching(conformance, typeDecl); if (!witnessType) continue; if (auto witnessMetaType = witnessType->getAs()) @@ -651,7 +641,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req, inferred.Witness = witness; // Compute the requirement and witness types we'll use for matching. - Type fullWitnessType = getWitnessTypeForMatching(tc, conformance, witness); + Type fullWitnessType = getWitnessTypeForMatching(conformance, witness); if (!fullWitnessType) { return inferred; } @@ -764,7 +754,6 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req, } AssociatedTypeDecl *AssociatedTypeInference::findDefaultedAssociatedType( - TypeChecker &tc, AssociatedTypeDecl *assocType) { // If this associated type has a default, we're done. if (assocType->hasDefaultDefinitionType()) @@ -774,7 +763,7 @@ AssociatedTypeDecl *AssociatedTypeInference::findDefaultedAssociatedType( SmallPtrSet canonicalTypes; SmallVector results; for (auto overridden : assocType->getOverriddenDecls()) { - auto overriddenDefault = findDefaultedAssociatedType(tc, overridden); + auto overriddenDefault = findDefaultedAssociatedType(overridden); if (!overriddenDefault) continue; Type overriddenType = @@ -824,7 +813,7 @@ Type AssociatedTypeInference::computeFixedTypeWitness( Type AssociatedTypeInference::computeDefaultTypeWitness( AssociatedTypeDecl *assocType) { // Go find a default definition. - auto defaultedAssocType = findDefaultedAssociatedType(tc, assocType); + auto defaultedAssocType = findDefaultedAssociatedType(assocType); if (!defaultedAssocType) return Type(); // If we don't have a default definition, we're done. @@ -868,7 +857,7 @@ Type AssociatedTypeInference::computeDefaultTypeWitness( if (defaultType->hasError()) return Type(); - if (auto failed = checkTypeWitness(tc, dc, proto, assocType, defaultType)) { + if (auto failed = checkTypeWitness(dc, proto, assocType, defaultType)) { // Record the failure, if we haven't seen one already. if (!failedDefaultedAssocType && !failed.isError()) { failedDefaultedAssocType = defaultedAssocType; @@ -894,12 +883,13 @@ Type AssociatedTypeInference::computeDerivedTypeWitness( return Type(); // Try to derive the type witness. - Type derivedType = tc.deriveTypeWitness(dc, derivingTypeDecl, assocType); + Type derivedType = + TypeChecker::deriveTypeWitness(dc, derivingTypeDecl, assocType); if (!derivedType) return Type(); // Make sure that the derived type is sane. - if (checkTypeWitness(tc, dc, proto, assocType, derivedType)) { + if (checkTypeWitness(dc, proto, assocType, derivedType)) { /// FIXME: Diagnose based on this. failedDerivedAssocType = assocType; failedDerivedWitness = derivedType; @@ -970,9 +960,9 @@ Type AssociatedTypeInference::substCurrentTypeWitnesses(Type type) { = TypeChecker::conformsToProtocol( baseTy, assocType->getProtocol(), dc, ConformanceCheckFlags::SkipConditionalRequirements); - if (!localConformance || localConformance->isAbstract() || - (localConformance->getConcrete()->getRootConformance() - != conformance)) { + if (localConformance.isInvalid() || localConformance.isAbstract() || + (localConformance.getConcrete()->getRootConformance() != + conformance)) { return nullptr; } @@ -1102,13 +1092,13 @@ bool AssociatedTypeInference::checkCurrentTypeWitnesses( sanitizeProtocolRequirements(proto, proto->getRequirementSignature(), sanitizedRequirements); auto result = - tc.checkGenericArguments(dc, SourceLoc(), SourceLoc(), - typeInContext, - { proto->getSelfInterfaceType() }, - sanitizedRequirements, - QuerySubstitutionMap{substitutions}, - TypeChecker::LookUpConformance(dc), - None, nullptr, options); + TypeChecker::checkGenericArguments(dc, SourceLoc(), SourceLoc(), + typeInContext, + { proto->getSelfInterfaceType() }, + sanitizedRequirements, + QuerySubstitutionMap{substitutions}, + TypeChecker::LookUpConformance(dc), + None, nullptr, options); switch (result) { case RequirementCheckResult::Failure: ++NumSolutionStatesFailedCheck; @@ -1150,7 +1140,7 @@ bool AssociatedTypeInference::checkConstrainedExtension(ExtensionDecl *ext) { auto subs = typeInContext->getContextSubstitutions(ext); SubstOptions options = getSubstOptionsWithCurrentTypeWitnesses(); - switch (tc.checkGenericArguments( + switch (TypeChecker::checkGenericArguments( dc, SourceLoc(), SourceLoc(), adoptee, ext->getGenericSignature()->getGenericParams(), ext->getGenericSignature()->getRequirements(), @@ -1405,10 +1395,9 @@ void AssociatedTypeInference::findSolutionsRec( } } -static Comparison -compareDeclsForInference(TypeChecker &TC, DeclContext *DC, - ValueDecl *decl1, ValueDecl *decl2) { - // TC.compareDeclarations assumes that it's comparing two decls that +static Comparison compareDeclsForInference(DeclContext *DC, ValueDecl *decl1, + ValueDecl *decl2) { + // TypeChecker::compareDeclarations assumes that it's comparing two decls that // apply equally well to a call site. We haven't yet inferred the // associated types for a type, so the ranking algorithm used by // compareDeclarations to score protocol extensions is inappropriate, @@ -1438,7 +1427,7 @@ compareDeclsForInference(TypeChecker &TC, DeclContext *DC, auto dc2 = decl2->getDeclContext(); if (dc1 == dc2) - return TC.compareDeclarations(DC, decl1, decl2); + return TypeChecker::compareDeclarations(DC, decl1, decl2); auto isProtocolExt1 = (bool)dc1->getExtendedProtocolDecl(); auto isProtocolExt2 = (bool)dc2->getExtendedProtocolDecl(); @@ -1453,7 +1442,7 @@ compareDeclsForInference(TypeChecker &TC, DeclContext *DC, // Associated type inference shouldn't impact the result. // FIXME: It could, if someone constrained to ConcreteType.AssocType... if (!isProtocolExt1) - return TC.compareDeclarations(DC, decl1, decl2); + return TypeChecker::compareDeclarations(DC, decl1, decl2); // Compare protocol extensions by which protocols they require Self to // conform to. If one extension requires a superset of the other's @@ -1464,9 +1453,9 @@ compareDeclsForInference(TypeChecker &TC, DeclContext *DC, // FIXME: Extensions sometimes have null generic signatures while // checking the standard library... if (!sig1 || !sig2) - return TC.compareDeclarations(DC, decl1, decl2); + return TypeChecker::compareDeclarations(DC, decl1, decl2); - auto selfParam = GenericTypeParamType::get(0, 0, TC.Context); + auto selfParam = GenericTypeParamType::get(0, 0, decl1->getASTContext()); // Collect the protocols required by extension 1. Type class1; @@ -1558,7 +1547,7 @@ compareDeclsForInference(TypeChecker &TC, DeclContext *DC, // If they require the same set of protocols, or non-overlapping // sets, judge them normally. - return TC.compareDeclarations(DC, decl1, decl2); + return TypeChecker::compareDeclarations(DC, decl1, decl2); } bool AssociatedTypeInference::isBetterSolution( @@ -1574,7 +1563,7 @@ bool AssociatedTypeInference::isBetterSolution( if (firstWitness == secondWitness) continue; - switch (compareDeclsForInference(tc, dc, firstWitness, secondWitness)) { + switch (compareDeclsForInference(dc, firstWitness, secondWitness)) { case Comparison::Better: if (secondBetter) return false; @@ -1984,7 +1973,7 @@ auto AssociatedTypeInference::solve(ConformanceChecker &checker) void ConformanceChecker::resolveTypeWitnesses() { // Attempt to infer associated type witnesses. - AssociatedTypeInference inference(TC, Conformance); + AssociatedTypeInference inference(getASTContext(), Conformance); if (auto inferred = inference.solve(*this)) { for (const auto &inferredWitness : *inferred) { recordTypeWitness(inferredWitness.first, inferredWitness.second, @@ -2004,7 +1993,7 @@ void ConformanceChecker::resolveTypeWitnesses() { if (Conformance->hasTypeWitness(assocType)) continue; - recordTypeWitness(assocType, ErrorType::get(TC.Context), nullptr); + recordTypeWitness(assocType, ErrorType::get(getASTContext()), nullptr); } } @@ -2037,7 +2026,7 @@ void ConformanceChecker::resolveSingleWitness(ValueDecl *requirement) { SWIFT_DEFER { ResolvingWitnesses.erase(requirement); }; // Make sure we've validated the requirement. - if (!requirement->getInterfaceType() || requirement->isInvalid()) { + if (requirement->isInvalid()) { Conformance->setInvalid(); return; } diff --git a/lib/Sema/TypeCheckREPL.cpp b/lib/Sema/TypeCheckREPL.cpp index f0c53aab08449..ef4a5aab3dc9e 100644 --- a/lib/Sema/TypeCheckREPL.cpp +++ b/lib/Sema/TypeCheckREPL.cpp @@ -17,6 +17,7 @@ #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ASTWalker.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Expr.h" #include "swift/AST/NameLookup.h" @@ -30,33 +31,60 @@ using namespace swift; namespace { + +/// Find available closure discriminators. +/// +/// The parser typically takes care of assigning unique discriminators to +/// closures, but the parser is unavailable to this transform. +class DiscriminatorFinder : public ASTWalker { + unsigned NextDiscriminator = 0; + +public: + Expr *walkToExprPost(Expr *E) override { + auto *ACE = dyn_cast(E); + if (!ACE) + return E; + + unsigned Discriminator = ACE->getDiscriminator(); + assert(Discriminator != AbstractClosureExpr::InvalidDiscriminator && + "Existing closures should have valid discriminators"); + if (Discriminator >= NextDiscriminator) + NextDiscriminator = Discriminator + 1; + return E; + } + + // Get the next available closure discriminator. + unsigned getNextDiscriminator() { + if (NextDiscriminator == AbstractClosureExpr::InvalidDiscriminator) + llvm::report_fatal_error("Out of valid closure discriminators"); + return NextDiscriminator++; + } +}; + struct REPLContext { - TypeChecker &TC; ASTContext &Context; SourceFile &SF; SmallVector PrintDecls; SmallVector DebugPrintlnDecls; - REPLContext(TypeChecker &TC, SourceFile &SF) - : TC(TC), Context(TC.Context), SF(SF) {} + REPLContext(SourceFile &SF) : Context(SF.getASTContext()), SF(SF) {} bool requirePrintDecls() { if (!PrintDecls.empty() && !DebugPrintlnDecls.empty()) return false; + auto *stdlib = TypeChecker::getStdlibModule(&SF); { - Identifier Id(Context.getIdentifier("_replPrintLiteralString")); - auto lookup = TC.lookupUnqualified(TC.getStdlibModule(&SF), - Id, SourceLoc()); + DeclNameRef Id(Context.getIdentifier("_replPrintLiteralString")); + auto lookup = TypeChecker::lookupUnqualified(stdlib, Id, SourceLoc()); if (!lookup) return true; for (auto result : lookup) PrintDecls.push_back(result.getValueDecl()); } { - Identifier Id(Context.getIdentifier("_replDebugPrintln")); - auto lookup = TC.lookupUnqualified(TC.getStdlibModule(&SF), - Id, SourceLoc()); + DeclNameRef Id(Context.getIdentifier("_replDebugPrintln")); + auto lookup = TypeChecker::lookupUnqualified(stdlib, Id, SourceLoc()); if (!lookup) return true; for (auto result : lookup) @@ -69,18 +97,17 @@ struct REPLContext { class StmtBuilder { REPLContext &C; - TypeChecker &TC; ASTContext &Context; DeclContext *DC; SmallVector Body; public: StmtBuilder(REPLContext &C, DeclContext *DC) - : C(C), TC(C.TC), Context(C.Context), DC(DC) { + : C(C), Context(C.Context), DC(DC) { assert(DC); } StmtBuilder(StmtBuilder &parent) - : C(parent.C), TC(C.TC), Context(C.Context), DC(parent.DC) {} + : C(parent.C), Context(C.Context), DC(parent.DC) {} ~StmtBuilder() { assert(Body.empty() && "statements remain in builder?"); } @@ -97,14 +124,16 @@ class StmtBuilder { Expr *buildPrintRefExpr(SourceLoc loc) { assert(!C.PrintDecls.empty()); - return TC.buildRefExpr(C.PrintDecls, DC, DeclNameLoc(loc), - /*Implicit=*/true, FunctionRefKind::Compound); + return TypeChecker::buildRefExpr(C.PrintDecls, DC, DeclNameLoc(loc), + /*Implicit=*/true, + FunctionRefKind::Compound); } Expr *buildDebugPrintlnRefExpr(SourceLoc loc) { assert(!C.DebugPrintlnDecls.empty()); - return TC.buildRefExpr(C.DebugPrintlnDecls, DC, DeclNameLoc(loc), - /*Implicit=*/true, FunctionRefKind::Compound); + return TypeChecker::buildRefExpr(C.DebugPrintlnDecls, DC, DeclNameLoc(loc), + /*Implicit=*/true, + FunctionRefKind::Compound); } }; } // unnamed namespace @@ -117,29 +146,11 @@ void StmtBuilder::printLiteralString(StringRef Str, SourceLoc Loc) { void StmtBuilder::printReplExpr(VarDecl *Arg, SourceLoc Loc) { Expr *DebugPrintlnFn = buildDebugPrintlnRefExpr(Loc); - Expr *ArgRef = TC.buildRefExpr(Arg, DC, DeclNameLoc(Loc), /*Implicit=*/true, - FunctionRefKind::Compound); + Expr *ArgRef = TypeChecker::buildRefExpr( + Arg, DC, DeclNameLoc(Loc), /*Implicit=*/true, FunctionRefKind::Compound); addToBody(CallExpr::createImplicit(Context, DebugPrintlnFn, { ArgRef }, { })); } -Identifier TypeChecker::getNextResponseVariableName(DeclContext *DC) { - llvm::SmallString<4> namebuf; - Identifier ident; - - bool nameUsed = false; - do { - namebuf.clear(); - llvm::raw_svector_ostream names(namebuf); - names << "r" << NextResponseVariableIndex++; - - ident = Context.getIdentifier(names.str()); - nameUsed = static_cast(lookupUnqualified(DC, ident, SourceLoc())); - } while (nameUsed); - - return ident; -} - - static VarDecl *getObviousDeclFromExpr(Expr *E) { // Ignore lvalue->rvalue and other implicit conversions. while (auto *ICE = dyn_cast(E)) @@ -202,15 +213,20 @@ struct PatternBindingPrintLHS : public ASTVisitor { namespace { class REPLChecker : public REPLContext { - TopLevelContext &TLC; + DiscriminatorFinder &DF; + + /// The index of the next response metavariable to bind to a REPL result. + unsigned NextResponseVariableIndex = 0; + public: - REPLChecker(TypeChecker &TC, SourceFile &SF, TopLevelContext &TLC) - : REPLContext(TC, SF), TLC(TLC) {} + REPLChecker(SourceFile &SF, DiscriminatorFinder &DF) + : REPLContext(SF), DF(DF) {} void processREPLTopLevelExpr(Expr *E); void processREPLTopLevelPatternBinding(PatternBindingDecl *PBD); private: void generatePrintOfExpression(StringRef name, Expr *E); + Identifier getNextResponseVariableName(DeclContext *DC); }; } // end anonymous namespace @@ -218,7 +234,7 @@ namespace { /// description of the pattern involved. void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { // Always print rvalues, not lvalues. - E = TC.coerceToRValue(E); + E = TypeChecker::coerceToRValue(Context, E); SourceLoc Loc = E->getStartLoc(); SourceLoc EndLoc = E->getEndLoc(); @@ -233,16 +249,16 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { auto *Arg = new (Context) ParamDecl( SourceLoc(), SourceLoc(), Identifier(), Loc, Context.getIdentifier("arg"), /*DC*/ newTopLevel); - Arg->setType(E->getType()); Arg->setInterfaceType(E->getType()); Arg->setSpecifier(ParamSpecifier::Default); auto params = ParameterList::createWithoutLoc(Arg); - unsigned discriminator = TLC.claimNextClosureDiscriminator(); + unsigned discriminator = DF.getNextDiscriminator(); ClosureExpr *CE = - new (Context) ClosureExpr(params, SourceLoc(), SourceLoc(), SourceLoc(), - TypeLoc(), discriminator, newTopLevel); + new (Context) ClosureExpr(SourceRange(), nullptr, params, SourceLoc(), + SourceLoc(), SourceLoc(), TypeLoc(), + discriminator, newTopLevel); SmallVector args; params->getParams(args); @@ -270,7 +286,9 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { // Typecheck the function. BraceStmt *Body = builder.createBodyStmt(Loc, EndLoc); CE->setBody(Body, false); - TC.typeCheckClosureBody(CE); + + TypeChecker::typeCheckClosureBody(CE); + TypeChecker::computeCaptures(CE); auto *TheCall = CallExpr::createImplicit(Context, CE, { E }, { }); TheCall->getArg()->setType(AnyFunctionType::composeInput(Context, args, false)); @@ -280,9 +298,9 @@ void REPLChecker::generatePrintOfExpression(StringRef NameStr, Expr *E) { auto *BS = BraceStmt::create(Context, Loc, ASTNode(TheCall), EndLoc); newTopLevel->setBody(BS); - TC.checkTopLevelErrorHandling(newTopLevel); + TypeChecker::checkTopLevelErrorHandling(newTopLevel); - SF.Decls.push_back(newTopLevel); + SF.addTopLevelDecl(newTopLevel); } /// When we see an expression in a TopLevelCodeDecl in the REPL, process it, @@ -305,18 +323,17 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { // Remove the expression from being in the list of decls to execute, we're // going to reparent it. - auto TLCD = cast(SF.Decls.back()); + auto TLCD = cast(SF.getTopLevelDecls().back()); - E = TC.coerceToRValue(E); + E = TypeChecker::coerceToRValue(Context, E); // Create the meta-variable, let the typechecker name it. - Identifier name = TC.getNextResponseVariableName(&SF); + Identifier name = getNextResponseVariableName(&SF); VarDecl *vd = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, /*IsCaptureList*/false, E->getStartLoc(), name, &SF); - vd->setType(E->getType()); vd->setInterfaceType(E->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); @@ -335,8 +352,8 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) { /*implicit*/true)); // Finally, print the variable's value. - E = TC.buildCheckedRefExpr(vd, &SF, DeclNameLoc(E->getStartLoc()), - /*Implicit=*/true); + E = TypeChecker::buildCheckedRefExpr(vd, &SF, DeclNameLoc(E->getStartLoc()), + /*Implicit=*/true); generatePrintOfExpression(vd->getName().str(), E); } @@ -347,15 +364,14 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // This would just cause a confusing definite initialization error. Some // day we will do some high level analysis of uninitialized variables // (rdar://15157729) but until then, output a specialized error. - unsigned entryIdx = 0U-1; - for (auto patternEntry : PBD->getPatternList()) { - ++entryIdx; - if (!patternEntry.getInit()) { - TC.diagnose(PBD->getStartLoc(), diag::repl_must_be_initialized); + for (auto entryIdx : range(PBD->getNumPatternEntries())) { + auto *entryInit = PBD->getInit(entryIdx); + if (!entryInit) { + PBD->diagnose(diag::repl_must_be_initialized); continue; } - auto pattern = patternEntry.getPattern(); + auto *pattern = PBD->getPattern(entryIdx); llvm::SmallString<16> PatternString; PatternBindingPrintLHS(PatternString).visit(pattern); @@ -364,9 +380,9 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // underlying Decl to print it. if (auto *NP = dyn_cast(pattern-> getSemanticsProvidingPattern())) { - Expr *E = TC.buildCheckedRefExpr(NP->getDecl(), &SF, - DeclNameLoc(PBD->getStartLoc()), - /*Implicit=*/true); + Expr *E = TypeChecker::buildCheckedRefExpr( + NP->getDecl(), &SF, DeclNameLoc(PBD->getStartLoc()), + /*Implicit=*/true); generatePrintOfExpression(PatternString, E); continue; } @@ -381,72 +397,95 @@ void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // replPrint(r123) // Remove PBD from the list of Decls so we can insert before it. - auto PBTLCD = cast(SF.Decls.back()); - SF.Decls.pop_back(); + auto PBTLCD = cast(SF.getTopLevelDecls().back()); + SF.truncateTopLevelDecls(SF.getTopLevelDecls().size() - 1); // Create the meta-variable, let the typechecker name it. - Identifier name = TC.getNextResponseVariableName(SF.getParentModule()); + Identifier name = getNextResponseVariableName(SF.getParentModule()); VarDecl *vd = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, /*IsCaptureList*/false, PBD->getStartLoc(), name, &SF); - vd->setType(pattern->getType()); vd->setInterfaceType(pattern->getType()); - SF.Decls.push_back(vd); + SF.addTopLevelDecl(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); metavarPat->setType(vd->getType()); - PatternBindingDecl *metavarBinding = PatternBindingDecl::create( + auto *metavarBinding = PatternBindingDecl::create( Context, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::None, /*VarLoc*/ PBD->getStartLoc(), metavarPat, /*EqualLoc*/ SourceLoc(), - patternEntry.getInit(), &SF); + entryInit, &SF); auto MVBrace = BraceStmt::create(Context, metavarBinding->getStartLoc(), ASTNode(metavarBinding), metavarBinding->getEndLoc()); auto *MVTLCD = new (Context) TopLevelCodeDecl(&SF, MVBrace); - SF.Decls.push_back(MVTLCD); + SF.addTopLevelDecl(MVTLCD); // Replace the initializer of PBD with a reference to our repl temporary. - Expr *E = TC.buildCheckedRefExpr(vd, &SF, DeclNameLoc(vd->getStartLoc()), - /*Implicit=*/true); - E = TC.coerceToRValue(E); + Expr *E = TypeChecker::buildCheckedRefExpr(vd, &SF, + DeclNameLoc(vd->getStartLoc()), + /*Implicit=*/true); + E = TypeChecker::coerceToRValue(Context, E); PBD->setInit(entryIdx, E); - SF.Decls.push_back(PBTLCD); + SF.addTopLevelDecl(PBTLCD); // Finally, print out the result, by referring to the repl temp. - E = TC.buildCheckedRefExpr(vd, &SF, DeclNameLoc(vd->getStartLoc()), - /*Implicit=*/true); + E = TypeChecker::buildCheckedRefExpr(vd, &SF, + DeclNameLoc(vd->getStartLoc()), + /*Implicit=*/true); generatePrintOfExpression(PatternString, E); } } +Identifier REPLChecker::getNextResponseVariableName(DeclContext *DC) { + llvm::SmallString<4> namebuf; + Identifier ident; + bool nameUsed = false; + do { + namebuf.clear(); + llvm::raw_svector_ostream names(namebuf); + names << "r" << NextResponseVariableIndex++; + + ident = Context.getIdentifier(names.str()); + nameUsed = (bool)TypeChecker::lookupUnqualified(DC, DeclNameRef(ident), + SourceLoc()); + } while (nameUsed); + + return ident; +} /// processREPLTopLevel - This is called after we've parsed and typechecked some /// new decls at the top level. We inject code to print out expressions and /// pattern bindings the are evaluated. -void TypeChecker::processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, - unsigned FirstDecl) { +void TypeChecker::processREPLTopLevel(SourceFile &SF, unsigned FirstDecl) { + // Walk over all decls in the file to find the next available closure + // discriminator. + DiscriminatorFinder DF; + for (Decl *D : SF.getTopLevelDecls()) + D->walk(DF); + // Move new declarations out. - std::vector NewDecls(SF.Decls.begin()+FirstDecl, SF.Decls.end()); - SF.Decls.resize(FirstDecl); + std::vector NewDecls(SF.getTopLevelDecls().begin()+FirstDecl, + SF.getTopLevelDecls().end()); + SF.truncateTopLevelDecls(FirstDecl); - REPLChecker RC(*this, SF, TLC); + REPLChecker RC(SF, DF); // Loop over each of the new decls, processing them, adding them back to // the Decls list. for (Decl *D : NewDecls) { - SF.Decls.push_back(D); + SF.addTopLevelDecl(D); auto *TLCD = dyn_cast(D); if (!TLCD || TLCD->getBody()->getElements().empty()) continue; - auto Entry = TLCD->getBody()->getElement(0); + auto Entry = TLCD->getBody()->getFirstElement(); // Check to see if the TLCD has an expression that we have to transform. if (auto *E = Entry.dyn_cast()) @@ -455,7 +494,7 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, if (auto *PBD = dyn_cast(D)) RC.processREPLTopLevelPatternBinding(PBD); - contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } SF.clearLookupCache(); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 129dc6ee5c48a..bdceddf11f525 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -112,6 +112,8 @@ namespace { ParentDC = CE; CE->getBody()->walk(*this); ParentDC = oldParentDC; + + TypeChecker::computeCaptures(CE); return { false, E }; } @@ -142,16 +144,21 @@ namespace { } } - // If the closure has a single expression body, we need to walk into it - // with a new sequence. Otherwise, it'll have been separately - // type-checked. - if (CE->hasSingleExpressionBody()) + // If the closure has a single expression body or has had a function + // builder applied to it, we need to walk into it with a new sequence. + // Otherwise, it'll have been separately type-checked. + if (CE->hasSingleExpressionBody() || CE->hasAppliedFunctionBuilder()) CE->getBody()->walk(ContextualizeClosures(CE)); - // In neither case do we need to continue the *current* walk. + TypeChecker::computeCaptures(CE); return { false, E }; } + // Caller-side default arguments need their @autoclosures checked. + if (auto *DAE = dyn_cast(E)) + if (DAE->isCallerSide() && DAE->getParamDecl()->isAutoClosure()) + DAE->getCallerSideDefaultExpr()->walk(*this); + return { true, E }; } @@ -178,13 +185,9 @@ namespace { class FunctionBodyTimer { AnyFunctionRef Function; llvm::TimeRecord StartTime = llvm::TimeRecord::getCurrentTime(); - unsigned WarnLimit; - bool ShouldDump; public: - FunctionBodyTimer(AnyFunctionRef Fn, bool shouldDump, - unsigned warnLimit) - : Function(Fn), WarnLimit(warnLimit), ShouldDump(shouldDump) {} + FunctionBodyTimer(AnyFunctionRef Fn) : Function(Fn) {} ~FunctionBodyTimer() { llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false); @@ -195,7 +198,7 @@ namespace { ASTContext &ctx = Function.getAsDeclContext()->getASTContext(); auto *AFD = Function.getAbstractFunctionDecl(); - if (ShouldDump) { + if (ctx.TypeCheckerOpts.WarnLongFunctionBodies) { // Round up to the nearest 100th of a millisecond. llvm::errs() << llvm::format("%0.2f", ceil(elapsed * 100000) / 100) << "ms\t"; Function.getLoc().print(llvm::errs(), ctx.SourceMgr); @@ -210,6 +213,7 @@ namespace { llvm::errs() << "\n"; } + const auto WarnLimit = ctx.TypeCheckerOpts.DebugTimeFunctionBodies; if (WarnLimit != 0 && elapsedMS >= WarnLimit) { if (AFD) { ctx.Diags.diagnose(AFD, diag::debug_long_function_body, @@ -234,14 +238,14 @@ bool TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) { return CC.hasAutoClosures(); } -void TypeChecker::contextualizeTopLevelCode(TopLevelContext &TLC, - TopLevelCodeDecl *TLCD) { - unsigned nextDiscriminator = TLC.NextAutoClosureDiscriminator; +void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) { + auto &Context = TLCD->DeclContext::getASTContext(); + unsigned nextDiscriminator = Context.NextAutoClosureDiscriminator; ContextualizeClosures CC(TLCD, nextDiscriminator); TLCD->getBody()->walk(CC); - assert(nextDiscriminator == TLC.NextAutoClosureDiscriminator && + assert(nextDiscriminator == Context.NextAutoClosureDiscriminator && "reentrant/concurrent invocation of contextualizeTopLevelCode?"); - TLC.NextAutoClosureDiscriminator = CC.NextDiscriminator; + Context.NextAutoClosureDiscriminator = CC.NextDiscriminator; } /// Emits an error with a fixit for the case of unnecessary cast over a @@ -297,7 +301,7 @@ static void tryDiagnoseUnnecessaryCastOverOptionSet(ASTContext &Ctx, namespace { class StmtChecker : public StmtVisitor { public: - TypeChecker &TC; + ASTContext &Ctx; /// This is the current function or closure being checked. /// This is null for top level code. @@ -332,6 +336,8 @@ class StmtChecker : public StmtVisitor { /// Used to distinguish the first BraceStmt that starts a TopLevelCodeDecl. bool IsBraceStmtFromTopLevelDecl; + ASTContext &getASTContext() const { return Ctx; }; + struct AddLabeledStmt { StmtChecker &SC; AddLabeledStmt(StmtChecker &SC, LabeledStmt *LS) : SC(SC) { @@ -339,11 +345,12 @@ class StmtChecker : public StmtVisitor { if (!LS->getLabelInfo().Name.empty()) for (auto PrevLS : SC.ActiveLabeledStmts) { if (PrevLS->getLabelInfo().Name == LS->getLabelInfo().Name) { - SC.TC.diagnose(LS->getLabelInfo().Loc, + auto &DE = SC.getASTContext().Diags; + DE.diagnose(LS->getLabelInfo().Loc, diag::label_shadowed, LS->getLabelInfo().Name); - SC.TC.diagnose(PrevLS->getLabelInfo().Loc, - diag::invalid_redecl_prev, - PrevLS->getLabelInfo().Name); + DE.diagnose(PrevLS->getLabelInfo().Loc, + diag::invalid_redecl_prev, + PrevLS->getLabelInfo().Name); } } @@ -370,16 +377,16 @@ class StmtChecker : public StmtVisitor { } }; - StmtChecker(TypeChecker &TC, AbstractFunctionDecl *AFD) - : TC(TC), TheFunc(AFD), DC(AFD), IsREPL(false), + StmtChecker(AbstractFunctionDecl *AFD) + : Ctx(AFD->getASTContext()), TheFunc(AFD), DC(AFD), IsREPL(false), IsBraceStmtFromTopLevelDecl(false) {} - StmtChecker(TypeChecker &TC, ClosureExpr *TheClosure) - : TC(TC), TheFunc(TheClosure), DC(TheClosure), IsREPL(false), - IsBraceStmtFromTopLevelDecl(false) {} + StmtChecker(ClosureExpr *TheClosure) + : Ctx(TheClosure->getASTContext()), TheFunc(TheClosure), + DC(TheClosure), IsREPL(false), IsBraceStmtFromTopLevelDecl(false) {} - StmtChecker(TypeChecker &TC, DeclContext *DC) - : TC(TC), TheFunc(), DC(DC), IsREPL(false), + StmtChecker(DeclContext *DC) + : Ctx(DC->getASTContext()), TheFunc(), DC(DC), IsREPL(false), IsBraceStmtFromTopLevelDecl(false) { if (const SourceFile *SF = DC->getParentSourceFile()) if (SF->Kind == SourceFileKind::REPL) @@ -401,13 +408,13 @@ class StmtChecker : public StmtVisitor { template bool typeCheckStmt(StmtTy *&S) { - FrontendStatsTracer StatsTracer(TC.Context.Stats, "typecheck-stmt", S); - PrettyStackTraceStmt trace(TC.Context, "type-checking", S); + FrontendStatsTracer StatsTracer(getASTContext().Stats, "typecheck-stmt", S); + PrettyStackTraceStmt trace(getASTContext(), "type-checking", S); StmtTy *S2 = cast_or_null(visit(S)); if (S2 == nullptr) return true; S = S2; - performStmtDiagnostics(TC, S); + performStmtDiagnostics(getASTContext(), S); return false; } @@ -426,13 +433,15 @@ class StmtChecker : public StmtVisitor { Stmt *visitReturnStmt(ReturnStmt *RS) { if (!TheFunc.hasValue()) { - TC.diagnose(RS->getReturnLoc(), diag::return_invalid_outside_func); + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_invalid_outside_func); return nullptr; } // If the return is in a defer, then it isn't valid either. if (isInDefer()) { - TC.diagnose(RS->getReturnLoc(), diag::jump_out_of_defer, "return"); + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::jump_out_of_defer, "return"); return nullptr; } @@ -442,7 +451,8 @@ class StmtChecker : public StmtVisitor { if (!RS->hasResult()) { if (!ResultTy->isVoid()) - TC.diagnose(RS->getReturnLoc(), diag::return_expr_missing); + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_expr_missing); return RS; } @@ -464,7 +474,8 @@ class StmtChecker : public StmtVisitor { auto expr = TheFunc->getSingleExpressionBody(); if (expr->isImplicit() && isa(expr) && cast(expr)->getNumElements() == 0) { - TC.diagnose(RS->getReturnLoc(), diag::return_expr_missing); + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_expr_missing); return RS; } } @@ -479,7 +490,8 @@ class StmtChecker : public StmtVisitor { // 'nil'. auto nilExpr = dyn_cast(E->getSemanticsProvidingExpr()); if (!nilExpr) { - TC.diagnose(RS->getReturnLoc(), diag::return_init_non_nil) + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_init_non_nil) .highlight(E->getSourceRange()); RS->setResult(nullptr); return RS; @@ -487,9 +499,10 @@ class StmtChecker : public StmtVisitor { // "return nil" is only permitted in a failable initializer. if (!ctor->isFailable()) { - TC.diagnose(RS->getReturnLoc(), diag::return_non_failable_init) + getASTContext().Diags.diagnose(RS->getReturnLoc(), + diag::return_non_failable_init) .highlight(E->getSourceRange()); - TC.diagnose(ctor->getLoc(), diag::make_init_failable, + getASTContext().Diags.diagnose(ctor->getLoc(), diag::make_init_failable, ctor->getFullName()) .fixItInsertAfter(ctor->getLoc(), "?"); RS->setResult(nullptr); @@ -497,8 +510,9 @@ class StmtChecker : public StmtVisitor { } // Replace the "return nil" with a new 'fail' statement. - return new (TC.Context) FailStmt(RS->getReturnLoc(), nilExpr->getLoc(), - RS->isImplicit()); + return new (getASTContext()) FailStmt(RS->getReturnLoc(), + nilExpr->getLoc(), + RS->isImplicit()); } TypeCheckExprOptions options = {}; @@ -521,7 +535,7 @@ class StmtChecker : public StmtVisitor { } if (EndTypeCheckLoc.isValid()) { - assert(DiagnosticSuppression::isEnabled(TC.Diags) && + assert(DiagnosticSuppression::isEnabled(getASTContext().Diags) && "Diagnosing and AllowUnresolvedTypeVariables don't seem to mix"); options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; } @@ -534,13 +548,13 @@ class StmtChecker : public StmtVisitor { } } - auto exprTy = TC.typeCheckExpression(E, DC, TypeLoc::withoutLoc(ResultTy), - ctp, - options); + auto exprTy = TypeChecker::typeCheckExpression(E, DC, + TypeLoc::withoutLoc(ResultTy), + ctp, options); RS->setResult(E); if (!exprTy) { - tryDiagnoseUnnecessaryCastOverOptionSet(TC.Context, E, ResultTy, + tryDiagnoseUnnecessaryCastOverOptionSet(getASTContext(), E, ResultTy, DC->getParentModule()); } @@ -550,7 +564,8 @@ class StmtChecker : public StmtVisitor { Stmt *visitYieldStmt(YieldStmt *YS) { // If the yield is in a defer, then it isn't valid. if (isInDefer()) { - TC.diagnose(YS->getYieldLoc(), diag::jump_out_of_defer, "yield"); + getASTContext().Diags.diagnose(YS->getYieldLoc(), + diag::jump_out_of_defer, "yield"); return YS; } @@ -559,7 +574,7 @@ class StmtChecker : public StmtVisitor { auto yieldExprs = YS->getMutableYields(); if (yieldExprs.size() != yieldResults.size()) { - TC.diagnose(YS->getYieldLoc(), diag::bad_yield_count, + getASTContext().Diags.diagnose(YS->getYieldLoc(), diag::bad_yield_count, yieldResults.size()); return YS; } @@ -583,10 +598,10 @@ class StmtChecker : public StmtVisitor { // about the unparented &. exprToCheck = inout->getSubExpr(); } else { - TC.diagnose(exprToCheck->getLoc(), + getASTContext().Diags.diagnose(exprToCheck->getLoc(), diag::missing_address_of_yield, yieldType) .highlight(exprToCheck->getSourceRange()); - inout = new (TC.Context) InOutExpr(exprToCheck->getStartLoc(), + inout = new (getASTContext()) InOutExpr(exprToCheck->getStartLoc(), exprToCheck, Type(), /*implicit*/ true); } @@ -594,9 +609,9 @@ class StmtChecker : public StmtVisitor { contextTypePurpose = CTP_YieldByValue; } - TC.typeCheckExpression(exprToCheck, DC, - TypeLoc::withoutLoc(contextType), - contextTypePurpose); + TypeChecker::typeCheckExpression(exprToCheck, DC, + TypeLoc::withoutLoc(contextType), + contextTypePurpose); // Propagate the change into the inout expression we stripped before. if (inout) { @@ -614,17 +629,19 @@ class StmtChecker : public StmtVisitor { Stmt *visitThrowStmt(ThrowStmt *TS) { // If the throw is in a defer, then it isn't valid. if (isInDefer()) { - TC.diagnose(TS->getThrowLoc(), diag::jump_out_of_defer, "throw"); + getASTContext().Diags.diagnose(TS->getThrowLoc(), + diag::jump_out_of_defer, "throw"); return nullptr; } // Coerce the operand to the exception type. auto E = TS->getSubExpr(); - Type exnType = TC.getExceptionType(DC, TS->getThrowLoc()); + Type exnType = getASTContext().getErrorDecl()->getDeclaredType(); if (!exnType) return TS; - TC.typeCheckExpression(E, DC, TypeLoc::withoutLoc(exnType), CTP_ThrowStmt); + TypeChecker::typeCheckExpression(E, DC, TypeLoc::withoutLoc(exnType), + CTP_ThrowStmt); TS->setSubExpr(E); return TS; @@ -632,16 +649,16 @@ class StmtChecker : public StmtVisitor { Stmt *visitPoundAssertStmt(PoundAssertStmt *PA) { Expr *C = PA->getCondition(); - TC.typeCheckCondition(C, DC); + TypeChecker::typeCheckCondition(C, DC); PA->setCondition(C); return PA; } Stmt *visitDeferStmt(DeferStmt *DS) { - TC.typeCheckDecl(DS->getTempDecl()); + TypeChecker::typeCheckDecl(DS->getTempDecl()); Expr *theCall = DS->getCallExpr(); - TC.typeCheckExpression(theCall, DC); + TypeChecker::typeCheckExpression(theCall, DC); DS->setCallExpr(theCall); return DS; @@ -649,7 +666,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitIfStmt(IfStmt *IS) { StmtCondition C = IS->getCond(); - TC.typeCheckStmtCondition(C, DC, diag::if_always_true); + TypeChecker::typeCheckStmtCondition(C, DC, diag::if_always_true); IS->setCond(C); AddLabeledStmt ifNest(*this, IS); @@ -668,7 +685,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitGuardStmt(GuardStmt *GS) { StmtCondition C = GS->getCond(); - TC.typeCheckStmtCondition(C, DC, diag::guard_always_succeeds); + TypeChecker::typeCheckStmtCondition(C, DC, diag::guard_always_succeeds); GS->setCond(C); AddLabeledStmt ifNest(*this, GS); @@ -689,7 +706,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitWhileStmt(WhileStmt *WS) { StmtCondition C = WS->getCond(); - TC.typeCheckStmtCondition(C, DC, diag::while_always_true); + TypeChecker::typeCheckStmtCondition(C, DC, diag::while_always_true); WS->setCond(C); AddLabeledStmt loopNest(*this, WS); @@ -706,170 +723,16 @@ class StmtChecker : public StmtVisitor { typeCheckStmt(S); RWS->setBody(S); } - + Expr *E = RWS->getCond(); - TC.typeCheckCondition(E, DC); + TypeChecker::typeCheckCondition(E, DC); RWS->setCond(E); return RWS; } Stmt *visitForEachStmt(ForEachStmt *S) { - TypeResolutionOptions options(TypeResolverContext::InExpression); - options |= TypeResolutionFlags::AllowUnspecifiedTypes; - options |= TypeResolutionFlags::AllowUnboundGenerics; - - if (auto *P = TC.resolvePattern(S->getPattern(), DC, - /*isStmtCondition*/false)) { - S->setPattern(P); - } else { - S->getPattern()->setType(ErrorType::get(TC.Context)); - return nullptr; - } - - if (TC.typeCheckPattern(S->getPattern(), DC, options)) { - // FIXME: Handle errors better. - S->getPattern()->setType(ErrorType::get(TC.Context)); - return nullptr; - } - - if (TC.typeCheckForEachBinding(DC, S)) - return nullptr; - - if (auto *Where = S->getWhere()) { - if (TC.typeCheckCondition(Where, DC)) - return nullptr; - S->setWhere(Where); - } - - - // Retrieve the 'Sequence' protocol. - ProtocolDecl *sequenceProto - = TC.getProtocol(S->getForLoc(), KnownProtocolKind::Sequence); - if (!sequenceProto) { - return nullptr; - } - - // Retrieve the 'Iterator' protocol. - ProtocolDecl *iteratorProto = - TC.getProtocol(S->getForLoc(), KnownProtocolKind::IteratorProtocol); - if (!iteratorProto) { + if (TypeChecker::typeCheckForEachBinding(DC, S)) return nullptr; - } - - Expr *sequence = S->getSequence(); - - // Invoke iterator() to get an iterator from the sequence. - Type iteratorTy; - VarDecl *iterator; - { - Type sequenceType = sequence->getType(); - auto conformance = - TypeChecker::conformsToProtocol(sequenceType, sequenceProto, DC, - ConformanceCheckFlags::InExpression, - sequence->getLoc()); - if (!conformance) - return nullptr; - S->setSequenceConformance(conformance); - - iteratorTy = conformance->getTypeWitnessByName(sequenceType, - TC.Context.Id_Iterator); - if (iteratorTy->hasError()) - return nullptr; - - auto witness = conformance->getWitnessByName( - sequenceType, TC.Context.Id_makeIterator); - if (!witness) - return nullptr; - S->setMakeIterator(witness); - - // Create a local variable to capture the iterator. - std::string name; - if (auto np = dyn_cast_or_null(S->getPattern())) - name = "$"+np->getBoundName().str().str(); - name += "$generator"; - - iterator = new (TC.Context) VarDecl( - /*IsStatic*/ false, VarDecl::Introducer::Var, - /*IsCaptureList*/ false, S->getInLoc(), - TC.Context.getIdentifier(name), DC); - iterator->setType(iteratorTy); - iterator->setInterfaceType(iteratorTy->mapTypeOutOfContext()); - iterator->setImplicit(); - S->setIteratorVar(iterator); - - auto genPat = new (TC.Context) NamedPattern(iterator); - genPat->setImplicit(); - - // TODO: test/DebugInfo/iteration.swift requires this extra info to - // be around. - auto nextResultType = - OptionalType::get(conformance->getTypeWitnessByName( - sequenceType, TC.Context.Id_Element)); - PatternBindingDecl::createImplicit( - TC.Context, StaticSpellingKind::None, genPat, - new (TC.Context) OpaqueValueExpr(S->getInLoc(), nextResultType), DC, - /*VarLoc*/ S->getForLoc()); - - Type newSequenceType = cast(witness.getDecl()) - ->getInterfaceType() - ->castTo() - ->getParams()[0].getPlainType().subst(witness.getSubstitutions()); - - // Necessary type coersion for method application. - if (TC.convertToType(sequence, newSequenceType, DC, None)) { - return nullptr; - } - S->setSequence(sequence); - } - - // Working with iterators requires Optional. - if (TC.requireOptionalIntrinsics(S->getForLoc())) - return nullptr; - - // Gather the witnesses from the Iterator protocol conformance, which - // we'll use to drive the loop. - // FIXME: Would like to customize the diagnostic emitted in - // conformsToProtocol(). - auto genConformance = TypeChecker::conformsToProtocol( - iteratorTy, iteratorProto, DC, ConformanceCheckFlags::InExpression, - sequence->getLoc()); - if (!genConformance) - return nullptr; - - Type elementTy = genConformance->getTypeWitnessByName(iteratorTy, - TC.Context.Id_Element); - if (elementTy->hasError()) - return nullptr; - - auto *varRef = - TC.buildCheckedRefExpr(iterator, DC, DeclNameLoc(S->getInLoc()), - /*implicit*/ true); - if (!varRef) - return nullptr; - - S->setIteratorVarRef(varRef); - - auto witness = - genConformance->getWitnessByName(iteratorTy, TC.Context.Id_next); - S->setIteratorNext(witness); - - auto nextResultType = cast(S->getIteratorNext().getDecl()) - ->getResultInterfaceType() - .subst(S->getIteratorNext().getSubstitutions()); - - // Convert that Optional value to Optional. - auto optPatternType = OptionalType::get(S->getPattern()->getType()); - if (!optPatternType->isEqual(nextResultType)) { - OpaqueValueExpr *elementExpr = - new (TC.Context) OpaqueValueExpr(S->getInLoc(), nextResultType); - Expr *convertElementExpr = elementExpr; - if (TC.convertToType(convertElementExpr, optPatternType, DC, - S->getPattern())) { - return nullptr; - } - S->setElementExpr(elementExpr); - S->setConvertElementExpr(convertElementExpr); - } // Type-check the body of the loop. AddLabeledStmt loopNest(*this, S); @@ -904,8 +767,9 @@ class StmtChecker : public StmtVisitor { break; } else { unsigned distance = - TC.getCallEditDistance(S->getTargetName(), (*I)->getLabelInfo().Name, - TypeChecker::UnreasonableCallEditDistance); + TypeChecker::getCallEditDistance( + DeclNameRef(S->getTargetName()), (*I)->getLabelInfo().Name, + TypeChecker::UnreasonableCallEditDistance); if (distance < TypeChecker::UnreasonableCallEditDistance) labelCorrections.insert(distance, std::move(*I)); } @@ -917,7 +781,8 @@ class StmtChecker : public StmtVisitor { if (!Target) { // If we're in a defer, produce a tailored diagnostic. if (isInDefer()) { - TC.diagnose(S->getLoc(), diag::jump_out_of_defer, "break"); + getASTContext().Diags.diagnose(S->getLoc(), + diag::jump_out_of_defer, "break"); } else if (S->getTargetName().empty()) { // If we're dealing with an unlabeled break inside of an 'if' or 'do' // statement, produce a more specific error. @@ -926,13 +791,15 @@ class StmtChecker : public StmtVisitor { [&](Stmt *S) -> bool { return isa(S) || isa(S); })) { - TC.diagnose(S->getLoc(), diag::unlabeled_break_outside_loop); + getASTContext().Diags.diagnose(S->getLoc(), + diag::unlabeled_break_outside_loop); } else { // Otherwise produce a generic error. - TC.diagnose(S->getLoc(), diag::break_outside_loop); + getASTContext().Diags.diagnose(S->getLoc(), diag::break_outside_loop); } } else { - emitUnresolvedLabelDiagnostics(TC, S->getTargetLoc(), S->getTargetName(), + emitUnresolvedLabelDiagnostics(getASTContext().Diags, + S->getTargetLoc(), S->getTargetName(), labelCorrections); } return nullptr; @@ -965,8 +832,9 @@ class StmtChecker : public StmtVisitor { break; } else { unsigned distance = - TC.getCallEditDistance(S->getTargetName(), (*I)->getLabelInfo().Name, - TypeChecker::UnreasonableCallEditDistance); + TypeChecker::getCallEditDistance( + DeclNameRef(S->getTargetName()), (*I)->getLabelInfo().Name, + TypeChecker::UnreasonableCallEditDistance); if (distance < TypeChecker::UnreasonableCallEditDistance) labelCorrections.insert(distance, std::move(*I)); } @@ -978,19 +846,23 @@ class StmtChecker : public StmtVisitor { if (Target) { // Continue cannot be used to repeat switches, use fallthrough instead. if (!Target->isPossibleContinueTarget()) { - TC.diagnose(S->getLoc(), diag::continue_not_in_this_stmt, - isa(Target) ? "switch" : "if"); + getASTContext().Diags.diagnose( + S->getLoc(), diag::continue_not_in_this_stmt, + isa(Target) ? "switch" : "if"); return nullptr; } } else { // If we're in a defer, produce a tailored diagnostic. if (isInDefer()) { - TC.diagnose(S->getLoc(), diag::jump_out_of_defer, "break"); + getASTContext().Diags.diagnose(S->getLoc(), + diag::jump_out_of_defer, "break"); } else if (S->getTargetName().empty()) { // If we're dealing with an unlabeled continue, produce a generic error. - TC.diagnose(S->getLoc(), diag::continue_outside_loop); + getASTContext().Diags.diagnose(S->getLoc(), + diag::continue_outside_loop); } else { - emitUnresolvedLabelDiagnostics(TC, S->getTargetLoc(), S->getTargetName(), + emitUnresolvedLabelDiagnostics(getASTContext().Diags, + S->getTargetLoc(), S->getTargetName(), labelCorrections); } return nullptr; @@ -1000,26 +872,27 @@ class StmtChecker : public StmtVisitor { } static void - emitUnresolvedLabelDiagnostics(TypeChecker &tc, SourceLoc targetLoc, Identifier targetName, + emitUnresolvedLabelDiagnostics(DiagnosticEngine &DE, + SourceLoc targetLoc, Identifier targetName, TopCollection corrections) { // If an unresolved label was used, but we have a single correction, // produce the specific diagnostic and fixit. if (corrections.size() == 1) { - tc.diagnose(targetLoc, diag::unresolved_label_corrected, + DE.diagnose(targetLoc, diag::unresolved_label_corrected, targetName, corrections.begin()->Value->getLabelInfo().Name) .highlight(SourceRange(targetLoc)) .fixItReplace(SourceRange(targetLoc), corrections.begin()->Value->getLabelInfo().Name.str()); - tc.diagnose(corrections.begin()->Value->getLabelInfo().Loc, + DE.diagnose(corrections.begin()->Value->getLabelInfo().Loc, diag::decl_declared_here, corrections.begin()->Value->getLabelInfo().Name); } else { // If we have multiple corrections or none, produce a generic diagnostic // and all corrections available. - tc.diagnose(targetLoc, diag::unresolved_label, targetName) + DE.diagnose(targetLoc, diag::unresolved_label, targetName) .highlight(SourceRange(targetLoc)); for (auto &entry : corrections) - tc.diagnose(entry.Value->getLabelInfo().Loc, diag::note_typo_candidate, + DE.diagnose(entry.Value->getLabelInfo().Loc, diag::note_typo_candidate, entry.Value->getLabelInfo().Name.str()) .fixItReplace(SourceRange(targetLoc), entry.Value->getLabelInfo().Name.str()); @@ -1028,11 +901,13 @@ class StmtChecker : public StmtVisitor { Stmt *visitFallthroughStmt(FallthroughStmt *S) { if (!SwitchLevel) { - TC.diagnose(S->getLoc(), diag::fallthrough_outside_switch); + getASTContext().Diags.diagnose(S->getLoc(), + diag::fallthrough_outside_switch); return nullptr; } if (!FallthroughDest) { - TC.diagnose(S->getLoc(), diag::fallthrough_from_last_case); + getASTContext().Diags.diagnose(S->getLoc(), + diag::fallthrough_from_last_case); return nullptr; } S->setFallthroughSource(FallthroughSource); @@ -1047,8 +922,8 @@ class StmtChecker : public StmtVisitor { SmallVectorImpl **prevCaseDecls, SmallVectorImpl **nextCaseDecls) { Pattern *pattern = labelItem.getPattern(); - auto *newPattern = TC.resolvePattern(pattern, DC, - /*isStmtCondition*/ false); + auto *newPattern = TypeChecker::resolvePattern(pattern, DC, + /*isStmtCondition*/ false); if (!newPattern) { pattern->collectVariables(**nextCaseDecls); std::swap(*prevCaseDecls, *nextCaseDecls); @@ -1056,16 +931,28 @@ class StmtChecker : public StmtVisitor { } pattern = newPattern; + // Coerce the pattern to the subject's type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!subjectType || - TC.coercePatternToType(pattern, TypeResolution::forContextual(DC), - subjectType, patternOptions)) { + bool coercionError = false; + if (subjectType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = TypeChecker::coercePatternToType( + contextualPattern, subjectType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!subjectType || coercionError) { limitExhaustivityChecks = true; // If that failed, mark any variables binding pieces of the pattern // as invalid to silence follow-on errors. - pattern->forEachVariable([&](VarDecl *VD) { VD->markInvalid(); }); + pattern->forEachVariable([&](VarDecl *VD) { + VD->setInvalid(); + }); } labelItem.setPattern(pattern); @@ -1136,22 +1023,20 @@ class StmtChecker : public StmtVisitor { } assert(isa(initialCaseVarDecl->getParentPatternStmt())); - if (vd->hasType() && initialCaseVarDecl->hasType() && - !initialCaseVarDecl->isInvalid() && + if (!initialCaseVarDecl->isInvalid() && !vd->getType()->isEqual(initialCaseVarDecl->getType())) { - TC.diagnose(vd->getLoc(), diag::type_mismatch_multiple_pattern_list, + getASTContext().Diags.diagnose(vd->getLoc(), diag::type_mismatch_multiple_pattern_list, vd->getType(), initialCaseVarDecl->getType()); - vd->markInvalid(); - initialCaseVarDecl->markInvalid(); + vd->setInvalid(); + initialCaseVarDecl->setInvalid(); } if (initialCaseVarDecl->isLet() == vd->isLet()) { return; } - auto diag = TC.diagnose(vd->getLoc(), - diag::mutability_mismatch_multiple_pattern_list, - vd->isLet(), initialCaseVarDecl->isLet()); + auto diag = vd->diagnose(diag::mutability_mismatch_multiple_pattern_list, + vd->isLet(), initialCaseVarDecl->isLet()); VarPattern *foundVP = nullptr; vd->getParentPattern()->forEachNode([&](Pattern *P) { @@ -1162,8 +1047,8 @@ class StmtChecker : public StmtVisitor { if (foundVP) diag.fixItReplace(foundVP->getLoc(), initialCaseVarDecl->isLet() ? "let" : "var"); - vd->markInvalid(); - initialCaseVarDecl->markInvalid(); + vd->setInvalid(); + initialCaseVarDecl->setInvalid(); } }); @@ -1178,27 +1063,31 @@ class StmtChecker : public StmtVisitor { if (caseBlock->getCaseLabelItems().size() != 1) { assert(!caseBlock->getCaseLabelItems().empty() && "parser should not produce case blocks with no items"); - TC.diagnose(caseBlock->getLoc(), diag::unknown_case_multiple_patterns) + getASTContext().Diags.diagnose(caseBlock->getLoc(), + diag::unknown_case_multiple_patterns) .highlight(caseBlock->getCaseLabelItems()[1].getSourceRange()); limitExhaustivityChecks = true; } if (FallthroughDest != nullptr) { if (!caseBlock->isDefault()) - TC.diagnose(caseBlock->getLoc(), diag::unknown_case_must_be_last); + getASTContext().Diags.diagnose(caseBlock->getLoc(), + diag::unknown_case_must_be_last); limitExhaustivityChecks = true; } const auto &labelItem = caseBlock->getCaseLabelItems().front(); if (labelItem.getGuardExpr() && !labelItem.isDefault()) { - TC.diagnose(labelItem.getStartLoc(), diag::unknown_case_where_clause) + getASTContext().Diags.diagnose(labelItem.getStartLoc(), + diag::unknown_case_where_clause) .highlight(labelItem.getGuardExpr()->getSourceRange()); } const Pattern *pattern = labelItem.getPattern()->getSemanticsProvidingPattern(); if (!isa(pattern)) { - TC.diagnose(labelItem.getStartLoc(), diag::unknown_case_must_be_catchall) + getASTContext().Diags.diagnose(labelItem.getStartLoc(), + diag::unknown_case_must_be_catchall) .highlight(pattern->getSourceRange()); } } @@ -1227,11 +1116,11 @@ class StmtChecker : public StmtVisitor { } if (!previous->getType()->isEqual(expected->getType())) { - TC.diagnose(previous->getLoc(), + getASTContext().Diags.diagnose(previous->getLoc(), diag::type_mismatch_fallthrough_pattern_list, previous->getType(), expected->getType()); - previous->markInvalid(); - expected->markInvalid(); + previous->setInvalid(); + expected->setInvalid(); } // Ok, we found our match. Make the previous fallthrough statement var @@ -1242,7 +1131,7 @@ class StmtChecker : public StmtVisitor { } if (!matched) { - TC.diagnose(PreviousFallthrough->getLoc(), + getASTContext().Diags.diagnose(PreviousFallthrough->getLoc(), diag::fallthrough_into_case_with_var_binding, expected->getName()); } @@ -1252,9 +1141,10 @@ class StmtChecker : public StmtVisitor { Stmt *visitSwitchStmt(SwitchStmt *switchStmt) { // Type-check the subject expression. Expr *subjectExpr = switchStmt->getSubjectExpr(); - auto resultTy = TC.typeCheckExpression(subjectExpr, DC); + auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC); auto limitExhaustivityChecks = !resultTy; - if (Expr *newSubjectExpr = TC.coerceToRValue(subjectExpr)) + if (Expr *newSubjectExpr = + TypeChecker::coerceToRValue(getASTContext(), subjectExpr)) subjectExpr = newSubjectExpr; switchStmt->setSubjectExpr(subjectExpr); Type subjectType = switchStmt->getSubjectExpr()->getType(); @@ -1268,7 +1158,7 @@ class StmtChecker : public StmtVisitor { for (auto &node : switchStmt->getRawCases()) { if (!node.is()) continue; - TC.typeCheckDecl(node.get()); + TypeChecker::typeCheckDecl(node.get()); } SmallVector scratchMemory1; @@ -1310,7 +1200,7 @@ class StmtChecker : public StmtVisitor { // Check the guard expression, if present. if (auto *guard = labelItem.getGuardExpr()) { - limitExhaustivityChecks |= TC.typeCheckCondition(guard, DC); + limitExhaustivityChecks |= TypeChecker::typeCheckCondition(guard, DC); labelItem.setGuardExpr(guard); } } @@ -1322,8 +1212,6 @@ class StmtChecker : public StmtVisitor { if (!prev->hasName() || expected->getName() != prev->getName()) { continue; } - if (prev->hasType()) - expected->setType(prev->getType()); if (prev->hasInterfaceType()) expected->setInterfaceType(prev->getInterfaceType()); break; @@ -1338,7 +1226,7 @@ class StmtChecker : public StmtVisitor { subjectType, &prevCaseDecls, &nextCaseDecls); // Check the guard expression, if present. if (auto *guard = labelItem.getGuardExpr()) { - limitExhaustivityChecks |= TC.typeCheckCondition(guard, DC); + limitExhaustivityChecks |= TypeChecker::typeCheckCondition(guard, DC); labelItem.setGuardExpr(guard); } } @@ -1374,8 +1262,8 @@ class StmtChecker : public StmtVisitor { checkUnknownAttrRestrictions(caseBlock, limitExhaustivityChecks); } - // If the previous case fellthrough, similarly check that that case's bindings - // includes our first label item's pattern bindings and types. + // If the previous case fellthrough, similarly check that that case's + // bindings includes our first label item's pattern bindings and types. if (PreviousFallthrough && previousBlock) { checkFallthroughPatternBindingsAndTypes(caseBlock, previousBlock); } @@ -1389,7 +1277,8 @@ class StmtChecker : public StmtVisitor { } if (!switchStmt->isImplicit()) { - TC.checkSwitchExhaustiveness(switchStmt, DC, limitExhaustivityChecks); + TypeChecker::checkSwitchExhaustiveness(switchStmt, DC, + limitExhaustivityChecks); } return switchStmt; @@ -1407,11 +1296,11 @@ class StmtChecker : public StmtVisitor { void checkCatchStmt(CatchStmt *S) { // Check the catch pattern. - TC.typeCheckCatchPattern(S, DC); + TypeChecker::typeCheckCatchPattern(S, DC); // Check the guard expression, if present. if (Expr *guard = S->getGuardExpr()) { - TC.typeCheckCondition(guard, DC); + TypeChecker::typeCheckCondition(guard, DC); S->setGuardExpr(guard); } @@ -1452,23 +1341,32 @@ class StmtChecker : public StmtVisitor { bool TypeChecker::typeCheckCatchPattern(CatchStmt *S, DeclContext *DC) { // Grab the standard exception type. - Type exnType = getExceptionType(DC, S->getCatchLoc()); + Type exnType = DC->getASTContext().getErrorDecl()->getDeclaredType(); Pattern *pattern = S->getErrorPattern(); - if (Pattern *newPattern = resolvePattern(pattern, DC, + if (Pattern *newPattern = TypeChecker::resolvePattern(pattern, DC, /*isStmtCondition*/false)) { pattern = newPattern; // Coerce the pattern to the exception type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - if (!exnType || - coercePatternToType(pattern, TypeResolution::forContextual(DC), exnType, - patternOptions)) { + bool coercionError = false; + if (exnType) { + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + auto coercedPattern = coercePatternToType( + contextualPattern, exnType, patternOptions); + if (coercedPattern) + pattern = coercedPattern; + else + coercionError = true; + } + + if (!exnType || coercionError) { // If that failed, be sure to give the variables error types // before we type-check the guard. (This will probably kill // most of the type-checking, but maybe not.) pattern->forEachVariable([&](VarDecl *var) { - var->markInvalid(); + var->setInvalid(); }); } @@ -1483,7 +1381,7 @@ static bool isDiscardableType(Type type) { type->lookThroughAllOptionalTypes()->isVoid()); } -static void diagnoseIgnoredLiteral(TypeChecker &TC, LiteralExpr *LE) { +static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) { const auto getLiteralDescription = [](LiteralExpr *LE) -> StringRef { switch (LE->getKind()) { case ExprKind::IntegerLiteral: return "integer"; @@ -1492,13 +1390,8 @@ static void diagnoseIgnoredLiteral(TypeChecker &TC, LiteralExpr *LE) { case ExprKind::StringLiteral: return "string"; case ExprKind::InterpolatedStringLiteral: return "string"; case ExprKind::MagicIdentifierLiteral: - switch (cast(LE)->getKind()) { - case MagicIdentifierLiteralExpr::Kind::File: return "#file"; - case MagicIdentifierLiteralExpr::Kind::Line: return "#line"; - case MagicIdentifierLiteralExpr::Kind::Column: return "#column"; - case MagicIdentifierLiteralExpr::Kind::Function: return "#function"; - case MagicIdentifierLiteralExpr::Kind::DSOHandle: return "#dsohandle"; - } + return MagicIdentifierLiteralExpr::getKindString( + cast(LE)->getKind()); case ExprKind::NilLiteral: return "nil"; case ExprKind::ObjectLiteral: return "object"; @@ -1514,8 +1407,8 @@ static void diagnoseIgnoredLiteral(TypeChecker &TC, LiteralExpr *LE) { llvm_unreachable("Unhandled ExprKind in switch."); }; - TC.diagnose(LE->getLoc(), diag::expression_unused_literal, - getLiteralDescription(LE)) + Ctx.Diags.diagnose(LE->getLoc(), diag::expression_unused_literal, + getLiteralDescription(LE)) .highlight(LE->getSourceRange()); } @@ -1537,6 +1430,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { } // Complain about l-values that are neither loaded nor stored. + auto &Context = E->getType()->getASTContext(); + auto &DE = Context.Diags; if (E->getType()->hasLValueType()) { // This must stay in sync with diag::expression_unused_lvalue. enum { @@ -1551,7 +1446,7 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { else if (decl->getDeclContext()->isTypeContext()) storageKind = SK_Property; } - diagnose(E->getLoc(), diag::expression_unused_lvalue, storageKind) + DE.diagnose(E->getLoc(), diag::expression_unused_lvalue, storageKind) .highlight(E->getSourceRange()); return; } @@ -1596,8 +1491,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { if (auto *Fn = dyn_cast(expr)) { if (auto *declRef = dyn_cast(Fn->getFn())) { - if (auto *funcDecl = dyn_cast(declRef->getDecl())) { - if (funcDecl->getAttrs().hasAttribute()) { + if (auto *FD = dyn_cast(declRef->getDecl())) { + if (FD->getAttrs().hasAttribute()) { isDiscardable = true; } } @@ -1605,7 +1500,7 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { } if (!isDiscardable) { - diagnose(E->getLoc(), diag::expression_unused_function) + DE.diagnose(E->getLoc(), diag::expression_unused_function) .highlight(E->getSourceRange()); return; } @@ -1619,21 +1514,21 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { // Complain about '#selector'. if (auto *ObjCSE = dyn_cast(valueE)) { - diagnose(ObjCSE->getLoc(), diag::expression_unused_selector_result) + DE.diagnose(ObjCSE->getLoc(), diag::expression_unused_selector_result) .highlight(E->getSourceRange()); return; } // Complain about '#keyPath'. if (isa(valueE)) { - diagnose(valueE->getLoc(), diag::expression_unused_keypath_result) + DE.diagnose(valueE->getLoc(), diag::expression_unused_keypath_result) .highlight(E->getSourceRange()); return; } // Always complain about 'try?'. if (auto *OTE = dyn_cast(valueE)) { - diagnose(OTE->getTryLoc(), diag::expression_unused_optional_try) + DE.diagnose(OTE->getTryLoc(), diag::expression_unused_optional_try) .highlight(E->getSourceRange()); return; } @@ -1651,7 +1546,7 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { } if (auto *LE = dyn_cast(valueE)) { - diagnoseIgnoredLiteral(*this, LE); + diagnoseIgnoredLiteral(Context, LE); return; } @@ -1701,14 +1596,14 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { arg = TE->getElement(0); if (auto *LE = dyn_cast(arg)) { - diagnoseIgnoredLiteral(*this, LE); + diagnoseIgnoredLiteral(Context, LE); return; } } // Other unused constructor calls. if (callee && isa(callee) && !call->isImplicit()) { - diagnose(fn->getLoc(), diag::expression_unused_init_result, + DE.diagnose(fn->getLoc(), diag::expression_unused_init_result, callee->getDeclContext()->getDeclaredInterfaceType()) .highlight(call->getArg()->getSourceRange()); return; @@ -1727,7 +1622,7 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { // Translate calls to implicit functions to their user-facing names if (callee->getBaseName() == ctx.Id_derived_enum_equals || callee->getBaseName() == ctx.Id_derived_struct_equals) { - diagnose(fn->getLoc(), diag::expression_unused_result_operator, + DE.diagnose(fn->getLoc(), diag::expression_unused_result_operator, ctx.Id_EqualsOperator) .highlight(SR1).highlight(SR2); return; @@ -1738,10 +1633,10 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { if (callee->getFullName().isOperator()) diagID = diag::expression_unused_result_operator; - diagnose(fn->getLoc(), diagID, callee->getFullName()) + DE.diagnose(fn->getLoc(), diagID, callee->getFullName()) .highlight(SR1).highlight(SR2); } else - diagnose(fn->getLoc(), diag::expression_unused_result_unknown, + DE.diagnose(fn->getLoc(), diag::expression_unused_result_unknown, isa(fn), valueE->getType()) .highlight(SR1).highlight(SR2); @@ -1749,22 +1644,24 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { } // Produce a generic diagnostic. - diagnose(valueE->getLoc(), diag::expression_unused_result, valueE->getType()) + DE.diagnose(valueE->getLoc(), diag::expression_unused_result, + valueE->getType()) .highlight(valueE->getSourceRange()); } Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) { - const SourceManager &SM = TC.Context.SourceMgr; + const SourceManager &SM = getASTContext().SourceMgr; // Diagnose defer statement being last one in block (only if // BraceStmt does not start a TopLevelDecl). if (IsBraceStmtFromTopLevelDecl) { IsBraceStmtFromTopLevelDecl = false; - } else if (BS->getNumElements() > 0) { + } else if (!BS->empty()) { if (auto stmt = - BS->getElement(BS->getNumElements() - 1).dyn_cast()) { + BS->getLastElement().dyn_cast()) { if (auto deferStmt = dyn_cast(stmt)) { - TC.diagnose(deferStmt->getStartLoc(), diag::defer_stmt_at_block_end) + getASTContext().Diags.diagnose(deferStmt->getStartLoc(), + diag::defer_stmt_at_block_end) .fixItReplace(deferStmt->getStartLoc(), "do"); } } @@ -1780,33 +1677,36 @@ Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) { // Type check the expression. TypeCheckExprOptions options = TypeCheckExprFlags::IsExprStmt; bool isDiscarded = !(IsREPL && isa(DC)) - && !TC.Context.LangOpts.Playground - && !TC.Context.LangOpts.DebuggerSupport; + && !getASTContext().LangOpts.Playground + && !getASTContext().LangOpts.DebuggerSupport; if (isDiscarded) options |= TypeCheckExprFlags::IsDiscarded; if (EndTypeCheckLoc.isValid()) { - assert(DiagnosticSuppression::isEnabled(TC.Diags) && + assert(DiagnosticSuppression::isEnabled(getASTContext().Diags) && "Diagnosing and AllowUnresolvedTypeVariables don't seem to mix"); options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; } auto resultTy = - TC.typeCheckExpression(SubExpr, DC, TypeLoc(), CTP_Unused, options); + TypeChecker::typeCheckExpression(SubExpr, DC, TypeLoc(), + CTP_Unused, options); // If a closure expression is unused, the user might have intended // to write "do { ... }". auto *CE = dyn_cast(SubExpr); if (CE || isa(SubExpr)) { - TC.diagnose(SubExpr->getLoc(), diag::expression_unused_closure); + getASTContext().Diags.diagnose(SubExpr->getLoc(), + diag::expression_unused_closure); if (CE && CE->hasAnonymousClosureVars() && CE->getParameters()->size() == 0) { - TC.diagnose(CE->getStartLoc(), diag::brace_stmt_suggest_do) + getASTContext().Diags.diagnose(CE->getStartLoc(), + diag::brace_stmt_suggest_do) .fixItInsert(CE->getStartLoc(), "do "); } } else if (isDiscarded && resultTy) - TC.checkIgnoredExpr(SubExpr); + TypeChecker::checkIgnoredExpr(SubExpr); elem = SubExpr; continue; @@ -1829,7 +1729,7 @@ Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) { (Loc == EndTypeCheckLoc || SM.isBeforeInBuffer(EndTypeCheckLoc, Loc))) break; - TC.typeCheckDecl(SubDecl); + TypeChecker::typeCheckDecl(SubDecl); } return BS; @@ -1850,9 +1750,9 @@ static Type getFunctionBuilderType(FuncDecl *FD) { } bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) { - auto result = typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()); - checkFunctionErrorHandling(AFD); - return result; + auto res = TypeChecker::typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()); + TypeChecker::checkFunctionErrorHandling(AFD); + return res; } static Expr* constructCallToSuperInit(ConstructorDecl *ctor, @@ -1860,20 +1760,17 @@ static Expr* constructCallToSuperInit(ConstructorDecl *ctor, ASTContext &Context = ctor->getASTContext(); Expr *superRef = new (Context) SuperRefExpr(ctor->getImplicitSelfDecl(), SourceLoc(), /*Implicit=*/true); - Expr *r = new (Context) UnresolvedDotExpr(superRef, SourceLoc(), - DeclBaseName::createConstructor(), - DeclNameLoc(), - /*Implicit=*/true); + Expr *r = UnresolvedDotExpr::createImplicit( + Context, superRef, DeclBaseName::createConstructor()); r = CallExpr::createImplicit(Context, r, { }, { }); if (ctor->hasThrows()) r = new (Context) TryExpr(SourceLoc(), r, Type(), /*implicit=*/true); - TypeChecker &tc = *static_cast(Context.getLazyResolver()); - DiagnosticSuppression suppression(tc.Diags); + DiagnosticSuppression suppression(ctor->getASTContext().Diags); auto resultTy = - tc.typeCheckExpression(r, ctor, TypeLoc(), CTP_Unused, - TypeCheckExprFlags::IsDiscarded); + TypeChecker::typeCheckExpression(r, ctor, TypeLoc(), CTP_Unused, + TypeCheckExprFlags::IsDiscarded); if (!resultTy) return nullptr; @@ -1883,7 +1780,7 @@ static Expr* constructCallToSuperInit(ConstructorDecl *ctor, /// Check a super.init call. /// /// \returns true if an error occurred. -static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor, +static bool checkSuperInit(ConstructorDecl *fromCtor, ApplyExpr *apply, bool implicitlyGenerated) { // Make sure we are referring to a designated initializer. auto otherCtorRef = dyn_cast( @@ -1897,8 +1794,9 @@ static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor, auto selfTy = fromCtor->getDeclContext()->getSelfInterfaceType(); if (auto classTy = selfTy->getClassOrBoundGenericClass()) { assert(classTy->getSuperclass()); - tc.diagnose(apply->getArg()->getLoc(), diag::chain_convenience_init, - classTy->getSuperclass()); + auto &Diags = fromCtor->getASTContext().Diags; + Diags.diagnose(apply->getArg()->getLoc(), diag::chain_convenience_init, + classTy->getSuperclass()); ctor->diagnose(diag::convenience_init_here); } } @@ -1936,7 +1834,7 @@ static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor, bool treatUsableFromInlineAsPublic; std::tie(fragileKind, treatUsableFromInlineAsPublic) = TypeChecker::getFragileFunctionKind(fromCtor); - tc.diagnoseInlinableDeclRef( + TypeChecker::diagnoseInlinableDeclRef( fromCtor->getLoc(), ctor, fromCtor, fragileKind, treatUsableFromInlineAsPublic); } @@ -1959,7 +1857,6 @@ static void checkClassConstructorBody(ClassDecl *classDecl, ConstructorDecl *ctor, BraceStmt *body) { ASTContext &ctx = classDecl->getASTContext(); - TypeChecker &tc = *static_cast(ctx.getLazyResolver()); bool wantSuperInitCall = false; bool isDelegating = false; ApplyExpr *initExpr = nullptr; @@ -1970,7 +1867,7 @@ static void checkClassConstructorBody(ClassDecl *classDecl, break; case ConstructorDecl::BodyInitKind::Chained: - checkSuperInit(tc, ctor, initExpr, false); + checkSuperInit(ctor, initExpr, false); /// A convenience initializer cannot chain to a superclass constructor. if (ctor->isConvenienceInit()) { @@ -1992,9 +1889,15 @@ static void checkClassConstructorBody(ClassDecl *classDecl, // A class designated initializer must never be delegating. if (ctor->isDesignatedInit() && isDelegating) { - ctor->diagnose(diag::delegating_designated_init, - ctor->getDeclContext()->getDeclaredInterfaceType()) - .fixItInsert(ctor->getLoc(), "convenience "); + if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { + ctor->diagnose(diag::delegating_designated_init_in_extension, + ctor->getDeclContext()->getDeclaredInterfaceType()); + } else { + ctor->diagnose(diag::delegating_designated_init, + ctor->getDeclContext()->getDeclaredInterfaceType()) + .fixItInsert(ctor->getLoc(), "convenience "); + } + ctx.Diags.diagnose(initExpr->getLoc(), diag::delegation_here); } @@ -2040,7 +1943,7 @@ static void checkClassConstructorBody(ClassDecl *classDecl, FindOtherConstructorRef finder; SuperInitCall->walk(finder); - if (!checkSuperInit(tc, ctor, finder.Found, true)) { + if (!checkSuperInit(ctor, finder.Found, true)) { // Store the super.init expression within the constructor declaration // to be emitted during SILGen. ctor->setSuperInitCall(SuperInitCall); @@ -2063,35 +1966,34 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, ctx.Stats->getFrontendCounters().NumFunctionsTypechecked++; Optional timer; - TypeChecker &tc = *static_cast(ctx.getLazyResolver()); - if (tc.DebugTimeFunctionBodies || tc.WarnLongFunctionBodies) - timer.emplace(AFD, tc.DebugTimeFunctionBodies, tc.WarnLongFunctionBodies); - - // FIXME(InterfaceTypeRequest): Remove this. - (void)AFD->getInterfaceType(); + const auto &tyOpts = ctx.TypeCheckerOpts; + if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) + timer.emplace(AFD); BraceStmt *body = AFD->getBody(); if (!body || AFD->isBodyTypeChecked()) return false; + bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { if (Type builderType = getFunctionBuilderType(func)) { - body = tc.applyFunctionBuilderBodyTransform(func, body, builderType); - if (!body) - return true; - } else if (func->hasSingleExpressionBody()) { - auto resultTypeLoc = func->getBodyResultTypeLoc(); - auto expr = func->getSingleExpressionBody(); - - if (resultTypeLoc.isNull() || resultTypeLoc.getType()->isVoid()) { - // The function returns void. We don't need an explicit return, no matter - // what the type of the expression is. Take the inserted return back out. - body->setElement(0, expr); + if (auto optBody = + TypeChecker::applyFunctionBuilderBodyTransform(func, builderType)) { + if (!*optBody) + return true; + + body = *optBody; + alreadyTypeChecked = true; } + } else if (func->hasSingleExpressionBody() && + func->getResultInterfaceType()->isVoid()) { + // The function returns void. We don't need an explicit return, no matter + // what the type of the expression is. Take the inserted return back out. + body->setFirstElement(func->getSingleExpressionBody()); } } else if (isa(AFD) && - (body->getNumElements() == 0 || - !isKnownEndOfConstructor(body->getElements().back()))) { + (body->empty() || + !isKnownEndOfConstructor(body->getLastElement()))) { // For constructors, we make sure that the body ends with a "return" stmt, // which we either implicitly synthesize, or the user can write. This // simplifies SILGen. @@ -2104,21 +2006,31 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, body->getRBraceLoc(), body->isImplicit()); } - StmtChecker SC(tc, AFD); - SC.EndTypeCheckLoc = endTypeCheckLoc; - bool hadError = SC.typeCheckBody(body); + // Typechecking, in particular ApplySolution is going to replace closures + // with OpaqueValueExprs and then try to do lookups into the closures. + // So, build out the body now. + if (ctx.LangOpts.EnableASTScopeLookup) + ASTScope::expandFunctionBody(AFD); + + // Type check the function body if needed. + bool hadError = false; + if (!alreadyTypeChecked) { + StmtChecker SC(AFD); + SC.EndTypeCheckLoc = endTypeCheckLoc; + hadError = SC.typeCheckBody(body); + } // If this was a function with a single expression body, let's see // if implicit return statement came out to be `Never` which means // that we have eagerly converted something like `{ fatalError() }` // into `{ return fatalError() }` that has to be corrected here. if (isa(AFD) && cast(AFD)->hasSingleExpressionBody()) { - if (auto *stmt = body->getElement(0).dyn_cast()) { + if (auto *stmt = body->getFirstElement().dyn_cast()) { if (auto *retStmt = dyn_cast(stmt)) { if (retStmt->isImplicit() && retStmt->hasResult()) { auto returnType = retStmt->getResult()->getType(); if (returnType && returnType->isUninhabited()) - body->setElement(0, retStmt->getResult()); + body->setFirstElement(retStmt->getResult()); } } } @@ -2133,7 +2045,7 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, // If nothing went wrong yet, perform extra checking. if (!hadError && endTypeCheckLoc.isInvalid()) - performAbstractFuncDeclDiagnostics(tc, AFD, body); + performAbstractFuncDeclDiagnostics(AFD, body); // Wire up the function body now. AFD->setBody(body, AbstractFunctionDecl::BodyKind::TypeChecked); @@ -2143,7 +2055,7 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD, SourceLoc EndTypeCheckLoc) { return evaluateOrDefault( - Context.evaluator, + AFD->getASTContext().evaluator, TypeCheckFunctionBodyUntilRequest{AFD, EndTypeCheckLoc}, true); } @@ -2154,10 +2066,11 @@ bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { BraceStmt *body = closure->getBody(); Optional timer; - if (DebugTimeFunctionBodies || WarnLongFunctionBodies) - timer.emplace(closure, DebugTimeFunctionBodies, WarnLongFunctionBodies); + const auto &tyOpts = closure->getASTContext().TypeCheckerOpts; + if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) + timer.emplace(closure); - bool HadError = StmtChecker(*this, closure).typeCheckBody(body); + bool HadError = StmtChecker(closure).typeCheckBody(body); if (body) { closure->setBody(body, closure->hasSingleExpressionBody()); } @@ -2168,7 +2081,7 @@ bool TypeChecker::typeCheckTapBody(TapExpr *expr, DeclContext *DC) { // We intentionally use typeCheckStmt instead of typeCheckBody here // because we want to contextualize TapExprs with the body they're in. BraceStmt *body = expr->getBody(); - bool HadError = StmtChecker(*this, DC).typeCheckStmt(body); + bool HadError = StmtChecker(DC).typeCheckStmt(body); if (body) { expr->setBody(body); } @@ -2180,8 +2093,8 @@ void TypeChecker::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { // because we want to contextualize all the TopLevelCode // declarations simultaneously. BraceStmt *Body = TLCD->getBody(); - StmtChecker(*this, TLCD).typeCheckStmt(Body); + StmtChecker(TLCD).typeCheckStmt(Body); TLCD->setBody(Body); checkTopLevelErrorHandling(TLCD); - performTopLevelDeclDiagnostics(*this, TLCD); + performTopLevelDeclDiagnostics(TLCD); } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index c874465c696bb..6d490fda71ac0 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -17,6 +17,7 @@ #include "CodeSynthesis.h" #include "TypeChecker.h" +#include "TypeCheckAvailability.h" #include "TypeCheckDecl.h" #include "TypeCheckType.h" #include "swift/AST/ASTContext.h" @@ -39,15 +40,16 @@ void swift::setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx) { pattern->forEachVariable([&](VarDecl *var) { // Don't change the type of a variable that we've been able to // compute a type for. - if (var->hasType() && !var->getType()->hasError()) + if (var->hasInterfaceType()) return; - var->markInvalid(); + var->setInvalid(); }); } /// Build a default initializer for the given type. Expr *TypeChecker::buildDefaultInitializer(Type type) { + auto &Context = type->getASTContext(); // Default-initialize optional types and weak values to 'nil'. if (type->getReferenceStorageReferent()->getOptionalObjectType()) return new (Context) NilLiteralExpr(SourceLoc(), /*Implicit=*/true); @@ -59,7 +61,7 @@ Expr *TypeChecker::buildDefaultInitializer(Type type) { if (elt.isVararg()) return nullptr; - auto eltInit = buildDefaultInitializer(elt.getType()); + auto eltInit = TypeChecker::buildDefaultInitializer(elt.getType()); if (!eltInit) return nullptr; @@ -162,25 +164,25 @@ StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator, } /// Validate the \c entryNumber'th entry in \c binding. -static void validatePatternBindingEntry(TypeChecker &tc, - PatternBindingDecl *binding, - unsigned entryNumber) { - // If the pattern already has a type, we're done. - if (binding->getPattern(entryNumber)->hasType()) - return; +llvm::Expected +PatternBindingEntryRequest::evaluate(Evaluator &eval, + PatternBindingDecl *binding, + unsigned entryNumber) const { + const auto &pbe = binding->getPatternList()[entryNumber]; + auto &Context = binding->getASTContext(); // Resolve the pattern. - auto *pattern = tc.resolvePattern(binding->getPattern(entryNumber), - binding->getDeclContext(), - /*isStmtCondition*/true); + auto *pattern = TypeChecker::resolvePattern(binding->getPattern(entryNumber), + binding->getDeclContext(), + /*isStmtCondition*/ true); if (!pattern) { binding->setInvalid(); - binding->getPattern(entryNumber)->setType(ErrorType::get(tc.Context)); - return; + binding->getPattern(entryNumber)->setType(ErrorType::get(Context)); + return &pbe; } binding->setPattern(entryNumber, pattern, - binding->getPatternList()[entryNumber].getInitContext()); + binding->getInitContext(entryNumber)); // Validate 'static'/'class' on properties in nominal type decls. auto StaticSpelling = binding->getStaticSpelling(); @@ -189,9 +191,9 @@ static void validatePatternBindingEntry(TypeChecker &tc, if (auto *NTD = binding->getDeclContext()->getSelfNominalTypeDecl()) { if (!isa(NTD)) { if (StaticSpelling == StaticSpellingKind::KeywordClass) { - tc.diagnose(binding, diag::class_var_not_in_class, false) - .fixItReplace(binding->getStaticLoc(), "static"); - tc.diagnose(NTD, diag::extended_type_declared_here); + binding->diagnose(diag::class_var_not_in_class, false) + .fixItReplace(binding->getStaticLoc(), "static"); + NTD->diagnose(diag::extended_type_declared_here); } } } @@ -204,6 +206,8 @@ static void validatePatternBindingEntry(TypeChecker &tc, // In particular, it's /not/ correct to check the PBD's DeclContext because // top-level variables in a script file are accessible from other files, // even though the PBD is inside a TopLevelCodeDecl. + auto contextualPattern = + ContextualPattern::forPatternBindingDecl(binding, entryNumber); TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl); if (binding->isInitialized(entryNumber)) { @@ -212,33 +216,61 @@ static void validatePatternBindingEntry(TypeChecker &tc, options |= TypeResolutionFlags::AllowUnboundGenerics; } - if (tc.typeCheckPattern(pattern, binding->getDeclContext(), options)) { - setBoundVarsTypeError(pattern, tc.Context); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + if (patternType->hasError()) { + swift::setBoundVarsTypeError(pattern, Context); binding->setInvalid(); - pattern->setType(ErrorType::get(tc.Context)); - return; + pattern->setType(ErrorType::get(Context)); + return &pbe; + } + + // If we have a type but no initializer, check whether the type is + // default-initializable. If so, do it. + if (!pbe.isInitialized() && + binding->isDefaultInitializable(entryNumber) && + pattern->hasStorage()) { + if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) { + // If we got a default initializer, install it and re-type-check it + // to make sure it is properly coerced to the pattern type. + binding->setInit(entryNumber, defaultInit); + } } - // If the pattern didn't get a type or if it contains an unbound generic type, - // we'll need to check the initializer. - if (!pattern->hasType() || pattern->getType()->hasUnboundGenericType()) { - if (tc.typeCheckPatternBinding(binding, entryNumber)) - return; - + // If the pattern contains some form of unresolved type, we'll need to + // check the initializer. + if (patternType->hasUnresolvedType() || + patternType->hasUnboundGenericType()) { + if (TypeChecker::typeCheckPatternBinding(binding, entryNumber, + patternType)) { + binding->setInvalid(); + return &pbe; + } + // A pattern binding at top level is not allowed to pick up another decl's // opaque result type as its type by type inference. - if (!binding->getDeclContext()->isLocalContext() - && binding->getInit(entryNumber)->getType()->hasOpaqueArchetype()) { + if (!binding->getDeclContext()->isLocalContext() && + binding->getInit(entryNumber)->getType()->hasOpaqueArchetype()) { // TODO: Check whether the type is the pattern binding's own opaque type. - tc.diagnose(binding, diag::inferred_opaque_type, - binding->getInit(entryNumber)->getType()); + binding->diagnose(diag::inferred_opaque_type, + binding->getInit(entryNumber)->getType()); + } + } else { + // Coerce the pattern to the computed type. + if (auto newPattern = TypeChecker::coercePatternToType( + contextualPattern, patternType, + TypeResolverContext::PatternBindingDecl)) { + pattern = newPattern; + } else { + binding->setInvalid(); + pattern->setType(ErrorType::get(Context)); + return &pbe; } } // If the pattern binding appears in a type or library file context, then // it must bind at least one variable. if (!contextAllowsPatternBindingWithoutVariables(binding->getDeclContext())) { - llvm::SmallVector vars; + llvm::SmallVector vars; binding->getPattern(entryNumber)->collectVariables(vars); if (vars.empty()) { // Selector for error message. @@ -246,31 +278,22 @@ static void validatePatternBindingEntry(TypeChecker &tc, Property, GlobalVariable, }; - tc.diagnose(binding->getPattern(entryNumber)->getLoc(), - diag::pattern_binds_no_variables, - binding->getDeclContext()->isTypeContext() - ? Property : GlobalVariable); + Context.Diags.diagnose(binding->getPattern(entryNumber)->getLoc(), + diag::pattern_binds_no_variables, + binding->getDeclContext()->isTypeContext() + ? Property + : GlobalVariable); } } -} - -/// Validate the entries in the given pattern binding declaration. -void swift::validatePatternBindingEntries(TypeChecker &tc, - PatternBindingDecl *binding) { - if (binding->hasValidationStarted()) - return; - - DeclValidationRAII IBV(binding); - - for (unsigned i = 0, e = binding->getNumPatternEntries(); i != e; ++i) - validatePatternBindingEntry(tc, binding, i); + return &pbe; } llvm::Expected IsGetterMutatingRequest::evaluate(Evaluator &evaluator, AbstractStorageDecl *storage) const { - bool result = (!storage->isStatic() && - doesContextHaveValueSemantics(storage->getDeclContext())); + auto storageDC = storage->getDeclContext(); + bool result = (!storage->isStatic() && storageDC->isTypeContext() && + storageDC->hasValueSemantics()); // 'lazy' overrides the normal accessor-based rules and heavily // restricts what accessors can be used. The getter is considered @@ -298,7 +321,7 @@ IsGetterMutatingRequest::evaluate(Evaluator &evaluator, // Protocol requirements are always written as '{ get }' or '{ get set }'; // the @_borrowed attribute determines if getReadImpl() becomes Get or Read. - if (isa(storage->getDeclContext())) + if (isa(storageDC)) return checkMutability(AccessorKind::Get); switch (storage->getReadImpl()) { @@ -324,8 +347,9 @@ IsSetterMutatingRequest::evaluate(Evaluator &evaluator, AbstractStorageDecl *storage) const { // By default, the setter is mutating if we have an instance member of a // value type, but this can be overridden below. - bool result = (!storage->isStatic() && - doesContextHaveValueSemantics(storage->getDeclContext())); + auto storageDC = storage->getDeclContext(); + bool result = (!storage->isStatic() && storageDC->isTypeContext() && + storageDC->hasValueSemantics()); // If we have an attached property wrapper, the setter is mutating // or not based on the composition of the wrappers. @@ -577,7 +601,9 @@ getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) { return result; } -/// Build an l-value for the storage of a declaration. +/// Build an l-value for the storage of a declaration. Returns nullptr if there +/// was an error. This should only occur if an invalid declaration was type +/// checked; another diagnostic should have been emitted already. static Expr *buildStorageReference(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, @@ -664,12 +690,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -685,7 +707,20 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // Perform accesses to the wrappedValues along the composition chain. for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) { auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i); - underlyingVars.push_back(wrapperInfo.valueVar); + auto wrappedValue = wrapperInfo.valueVar; + + // Check for availability of wrappedValue. + if (accessor->getAccessorKind() == AccessorKind::Get || + accessor->getAccessorKind() == AccessorKind::Read) { + if (auto *attr = wrappedValue->getAttrs().getUnavailable(ctx)) { + diagnoseExplicitUnavailability( + wrappedValue, + var->getAttachedPropertyWrappers()[i]->getRangeWithAt(), + var->getDeclContext(), nullptr); + } + } + + underlyingVars.push_back(wrappedValue); } semantics = AccessSemantics::DirectToStorage; selfAccessKind = SelfAccessorKind::Peer; @@ -698,12 +733,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor, auto *backing = var->getPropertyWrapperBackingProperty(); // Error recovery. - if (!backing) { - auto type = storage->getValueInterfaceType(); - if (isLValue) - type = LValueType::get(type); - return new (ctx) ErrorExpr(SourceRange(), type); - } + if (!backing) + return nullptr; storage = backing; @@ -732,19 +763,38 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } bool isMemberLValue = isLValue; + auto propertyWrapperMutability = + [&](Decl *decl) -> Optional> { + auto var = dyn_cast(decl); + if (!var) + return None; + auto mut = var->getPropertyWrapperMutability(); + if (!mut) + return None; + return std::make_pair(mut->Getter == PropertyWrapperMutability::Mutating, + mut->Setter == PropertyWrapperMutability::Mutating); + }; - // If we're acessing a property wrapper, determine if the + // If we're accessing a property wrapper, determine if the // intermediate access requires an lvalue. - if (underlyingVars.size() > 0) { - isMemberLValue = underlyingVars[0]->isGetterMutating(); + if (auto mut = propertyWrapperMutability(accessor->getStorage())) { + isMemberLValue = mut->first; if (isLValue) - isMemberLValue |= underlyingVars[0]->isSetterMutating(); + isMemberLValue |= mut->second; } bool isSelfLValue = storage->isGetterMutating(); if (isMemberLValue) isSelfLValue |= storage->isSetterMutating(); + // If we're accessing a property wrapper, determine if + // the self requires an lvalue. + if (auto mut = propertyWrapperMutability(storage)) { + isSelfLValue = mut->first; + if (isMemberLValue) + isSelfLValue |= mut->second; + } + Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isSelfLValue, ctx); @@ -772,18 +822,15 @@ static Expr *buildStorageReference(AccessorDecl *accessor, // Key path referring to the property being accessed. Expr *propertyKeyPath = new (ctx) KeyPathDotExpr(SourceLoc()); - propertyKeyPath = new (ctx) UnresolvedDotExpr( - propertyKeyPath, SourceLoc(), - enclosingSelfAccess->accessedProperty->getFullName(), DeclNameLoc(), - /*Implicit=*/true); + propertyKeyPath = UnresolvedDotExpr::createImplicit(ctx, propertyKeyPath, + enclosingSelfAccess->accessedProperty->getFullName()); propertyKeyPath = new (ctx) KeyPathExpr( SourceLoc(), nullptr, propertyKeyPath); // Key path referring to the backing storage property. Expr *storageKeyPath = new (ctx) KeyPathDotExpr(SourceLoc()); - storageKeyPath = new (ctx) UnresolvedDotExpr( - storageKeyPath, SourceLoc(), storage->getFullName(), DeclNameLoc(), - /*Implicit=*/true); + storageKeyPath = UnresolvedDotExpr::createImplicit(ctx, storageKeyPath, + storage->getFullName()); storageKeyPath = new (ctx) KeyPathExpr( SourceLoc(), nullptr, storageKeyPath); Expr *args[3] = { @@ -793,12 +840,16 @@ static Expr *buildStorageReference(AccessorDecl *accessor, }; SubscriptDecl *subscriptDecl = enclosingSelfAccess->subscript; - auto &tc = static_cast(*ctx.getLazyResolver()); lookupExpr = SubscriptExpr::create( ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getFullName().getArgumentNames(), { }, SourceLoc(), nullptr, subscriptDecl, /*Implicit=*/true); - tc.typeCheckExpression(lookupExpr, accessor); + + // FIXME: Since we're not resolving overloads or anything, we should be + // building fully type-checked AST above; we already have all the + // information that we need. + if (!TypeChecker::typeCheckExpression(lookupExpr, accessor)) + return nullptr; // Make sure we produce an lvalue only when desired. if (isMemberLValue != lookupExpr->getType()->is()) { @@ -846,19 +897,19 @@ createPropertyLoadOrCallSuperclassGetter(AccessorDecl *accessor, ctx); } -static Optional -checkConformanceToNSCopying(ASTContext &ctx, VarDecl *var, Type type) { +static ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var, + Type type) { auto dc = var->getDeclContext(); + auto &ctx = dc->getASTContext(); auto proto = ctx.getNSCopyingDecl(); if (proto) { - auto result = TypeChecker::conformsToProtocol(type, proto, dc, None); - if (result) + if (auto result = TypeChecker::conformsToProtocol(type, proto, dc, None)) return result; } ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform); - return None; + return ProtocolConformanceRef::forInvalid(); } static std::pair getUnderlyingTypeOfVariable(VarDecl *var) { @@ -871,10 +922,9 @@ static std::pair getUnderlyingTypeOfVariable(VarDecl *var) { } } -Optional -TypeChecker::checkConformanceToNSCopying(VarDecl *var) { +ProtocolConformanceRef TypeChecker::checkConformanceToNSCopying(VarDecl *var) { Type type = getUnderlyingTypeOfVariable(var).first; - return ::checkConformanceToNSCopying(Context, var, type); + return ::checkConformanceToNSCopying(var, type); } /// Synthesize the code to store 'Val' to 'VD', given that VD has an @NSCopying @@ -891,14 +941,14 @@ static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, // The element type must conform to NSCopying. If not, emit an error and just // recovery by synthesizing without the copy call. - auto conformance = checkConformanceToNSCopying(Ctx, VD, underlyingType); + auto conformance = checkConformanceToNSCopying(VD, underlyingType); if (!conformance) return Val; //- (id)copyWithZone:(NSZone *)zone; DeclName copyWithZoneName(Ctx, Ctx.getIdentifier("copy"), { Ctx.Id_with }); FuncDecl *copyMethod = nullptr; - for (auto member : conformance->getRequirement()->getMembers()) { + for (auto member : conformance.getRequirement()->getMembers()) { if (auto func = dyn_cast(member)) { if (func->getFullName() == copyWithZoneName) { copyMethod = func; @@ -916,9 +966,8 @@ static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, } SubstitutionMap subs = - SubstitutionMap::get(copyMethod->getGenericSignature(), - {underlyingType}, - ArrayRef(*conformance)); + SubstitutionMap::get(copyMethod->getGenericSignature(), {underlyingType}, + ArrayRef(conformance)); ConcreteDeclRef copyMethodRef(copyMethod, subs); auto copyMethodType = copyMethod->getInterfaceType() ->castTo() @@ -998,6 +1047,10 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, Expr *dest = buildStorageReference(accessor, storage, target, /*isLValue=*/true, ctx); + // Error recovery. + if (dest == nullptr) + return; + // A lazy property setter will store a value of type T into underlying storage // of type T?. auto destType = dest->getType()->getWithoutSpecifierType(); @@ -1007,6 +1060,7 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, return; if (!destType->isEqual(value->getType())) { + assert(destType->getOptionalObjectType()); assert(destType->getOptionalObjectType()->isEqual(value->getType())); value = new (ctx) InjectIntoOptionalExpr(value, destType); } @@ -1042,10 +1096,15 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target, Expr *result = createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); - ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, - /*IsImplicit=*/true); - return { BraceStmt::create(ctx, loc, returnStmt, loc, true), + SmallVector body; + if (result != nullptr) { + ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, + /*IsImplicit=*/true); + body.push_back(returnStmt); + } + + return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; } @@ -1131,9 +1190,6 @@ namespace { static std::pair synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage, ASTContext &Ctx) { - // FIXME: Remove TypeChecker dependencies below. - auto &TC = *(TypeChecker *) Ctx.getLazyResolver(); - // The getter checks the optional, storing the initial value in if nil. The // specific pattern we generate is: // get { @@ -1200,13 +1256,13 @@ synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage, unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD); Expr *InitValue; - if (PBD->getPatternList()[entryIndex].getInit()) { + if (PBD->getInit(entryIndex)) { PBD->setInitializerSubsumed(entryIndex); if (!PBD->isInitializerChecked(entryIndex)) - TC.typeCheckPatternBinding(PBD, entryIndex); + TypeChecker::typeCheckPatternBinding(PBD, entryIndex); - InitValue = PBD->getPatternList()[entryIndex].getInit(); + InitValue = PBD->getInit(entryIndex); } else { InitValue = new (Ctx) ErrorExpr(SourceRange(), Tmp2VD->getType()); } @@ -1440,7 +1496,13 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target, if (VD->getParsedAccessor(AccessorKind::DidSet)) { Expr *OldValueExpr = buildStorageReference(Set, VD, target, /*isLValue=*/true, Ctx); - OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + + // Error recovery. + if (OldValueExpr == nullptr) { + OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType()); + } else { + OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType()); + } OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, /*IsCaptureList*/false, SourceLoc(), @@ -1503,7 +1565,7 @@ synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) { return synthesizePropertyWrapperSetterBody(setter, ctx); } - // Synthesize a getter for the storage wrapper property of a property + // Synthesize a setter for the storage wrapper property of a property // with an attached wrapper. if (auto original = var->getOriginalWrappedProperty( PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { @@ -1548,6 +1610,19 @@ synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { ? TargetImpl::Ordinary : TargetImpl::Implementation); + // If this is a variable with an attached property wrapper, then + // the accessors need to yield the wrappedValue or projectedValue. + if (auto var = dyn_cast(storage)) { + if (var->hasAttachedPropertyWrapper()) { + target = TargetImpl::Wrapper; + } + + if (var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { + target = TargetImpl::WrapperStorage; + } + } + SourceLoc loc = storage->getLoc(); SmallVector body; @@ -1555,13 +1630,14 @@ synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) { // Build a reference to the storage. Expr *ref = buildStorageReference(accessor, storage, target, isLValue, ctx); + if (ref != nullptr) { + // Wrap it with an `&` marker if this is a modify. + ref = maybeWrapInOutExpr(ref, ctx); - // Wrap it with an `&` marker if this is a modify. - ref = maybeWrapInOutExpr(ref, ctx); - - // Yield it. - YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); - body.push_back(yield); + // Yield it. + YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true); + body.push_back(yield); + } return { BraceStmt::create(ctx, loc, body, loc, true), /*isTypeChecked=*/true }; @@ -1578,8 +1654,11 @@ synthesizeReadCoroutineBody(AccessorDecl *read, ASTContext &ctx) { static std::pair synthesizeModifyCoroutineBody(AccessorDecl *modify, ASTContext &ctx) { #ifndef NDEBUG - auto impl = modify->getStorage()->getReadWriteImpl(); - assert(impl != ReadWriteImplKind::Modify && + auto storage = modify->getStorage(); + auto impl = storage->getReadWriteImpl(); + auto hasWrapper = isa(storage) && + cast(storage)->hasAttachedPropertyWrapper(); + assert((hasWrapper || impl != ReadWriteImplKind::Modify) && impl != ReadWriteImplKind::Immutable); #endif return synthesizeCoroutineAccessorBody(modify, ctx); @@ -1636,8 +1715,9 @@ static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage, // For lazy properties, steal the 'self' from the initializer context. auto *varDecl = cast(storage); auto *bindingDecl = varDecl->getParentPatternBinding(); + const auto i = bindingDecl->getPatternEntryIndexForVarDecl(varDecl); auto *bindingInit = cast( - bindingDecl->getPatternEntryForVarDecl(varDecl).getInitContext()); + bindingDecl->getInitContext(i)); selfDecl = bindingInit->getImplicitSelfDecl(); } @@ -1959,22 +2039,22 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, // Getters and setters for lazy properties are not @_transparent. if (storage->getAttrs().hasAttribute()) return false; + } - // Getters/setters for a property with a wrapper are not @_transparent if - // the backing variable has more-restrictive access than the original - // property. The same goes for its storage wrapper. - if (auto var = dyn_cast(storage)) { - if (auto backingVar = var->getPropertyWrapperBackingProperty()) { - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + // Accessors for a property with a wrapper are not @_transparent if + // the backing variable has more-restrictive access than the original + // property. The same goes for its storage wrapper. + if (auto var = dyn_cast(storage)) { + if (auto backingVar = var->getPropertyWrapperBackingProperty()) { + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; + } - if (auto original = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { - auto backingVar = original->getPropertyWrapperBackingProperty(); - if (backingVar->getFormalAccess() < var->getFormalAccess()) - return false; - } + if (auto original = var->getOriginalWrappedProperty( + PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) { + auto backingVar = original->getPropertyWrapperBackingProperty(); + if (backingVar->getFormalAccess() < var->getFormalAccess()) + return false; } } @@ -2091,7 +2171,7 @@ static VarDecl *synthesizePropertyWrapperStorageWrapperProperty( // that to find the storage wrapper property. if (auto attr = var->getAttrs().getAttribute()){ SmallVector declsFound; - auto projectionName = attr->ProjectionPropertyName; + DeclNameRef projectionName(attr->ProjectionPropertyName); auto dc = var->getDeclContext(); if (dc->isTypeContext()) { dc->lookupQualified(dc->getSelfNominalTypeDecl(), projectionName, @@ -2181,15 +2261,13 @@ static void typeCheckSynthesizedWrapperInitializer( } // Type-check the initialization. - ASTContext &ctx = pbd->getASTContext(); - auto &tc = *static_cast(ctx.getLazyResolver()); - tc.typeCheckExpression(initializer, originalDC); + TypeChecker::typeCheckExpression(initializer, originalDC); + const auto i = pbd->getPatternEntryIndexForVarDecl(backingVar); if (auto initializerContext = - dyn_cast_or_null( - pbd->getPatternEntryForVarDecl(backingVar).getInitContext())) { - tc.contextualizeInitializer(initializerContext, initializer); + dyn_cast_or_null(pbd->getInitContext(i))) { + TypeChecker::contextualizeInitializer(initializerContext, initializer); } - tc.checkPropertyWrapperErrorHandling(pbd, initializer); + TypeChecker::checkPropertyWrapperErrorHandling(pbd, initializer); } static PropertyWrapperMutability::Value @@ -2226,9 +2304,15 @@ PropertyWrapperMutabilityRequest::evaluate(Evaluator &, isProjectedValue = true; } - if (var->getParsedAccessor(AccessorKind::Get)) + // Make sure we don't ignore .swiftinterface files, because those will + // have the accessors printed + auto varSourceFile = var->getDeclContext()->getParentSourceFile(); + auto isVarNotInInterfaceFile = + varSourceFile && varSourceFile->Kind != SourceFileKind::Interface; + + if (var->getParsedAccessor(AccessorKind::Get) && isVarNotInInterfaceFile) return None; - if (var->getParsedAccessor(AccessorKind::Set)) + if (var->getParsedAccessor(AccessorKind::Set) && isVarNotInInterfaceFile) return None; // Figure out which member we're looking through. @@ -2314,6 +2398,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, !propertyType->isEqual(expectedPropertyType)) { var->diagnose(diag::property_wrapper_incompatible_property, propertyType, wrapperType); + var->setInvalid(); if (auto nominalWrapper = wrapperType->getAnyNominal()) { nominalWrapper->diagnose(diag::property_wrapper_declared_here, nominalWrapper->getFullName()); @@ -2356,16 +2441,14 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator, if (!parentPBD->isInitialized(patternNumber) && parentPBD->isDefaultInitializable(patternNumber) && !wrapperInfo.defaultInit) { - auto &tc = *static_cast(ctx.getLazyResolver()); auto ty = parentPBD->getPattern(patternNumber)->getType(); - if (auto defaultInit = tc.buildDefaultInitializer(ty)) + if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty)) parentPBD->setInit(patternNumber, defaultInit); } if (parentPBD->isInitialized(patternNumber) && !parentPBD->isInitializerChecked(patternNumber)) { - auto &tc = *static_cast(ctx.getLazyResolver()); - tc.typeCheckPatternBinding(parentPBD, patternNumber); + TypeChecker::typeCheckPatternBinding(parentPBD, patternNumber); } Expr *originalInitialValue = nullptr; @@ -2421,7 +2504,7 @@ static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage, StorageImplInfo &info) { if (auto *var = dyn_cast(storage)) { SourceLoc typeLoc; - if (auto *repr = var->getTypeRepr()) + if (auto *repr = var->getTypeReprOrParentPatternTypeRepr()) typeLoc = repr->getLoc(); if (info.hasStorage()) { @@ -2526,7 +2609,8 @@ static void finishPropertyWrapperImplInfo(VarDecl *var, } if (wrapperSetterIsUsable) - info = StorageImplInfo::getMutableComputed(); + info = StorageImplInfo(ReadImplKind::Get, WriteImplKind::Set, + ReadWriteImplKind::Modify); else info = StorageImplInfo::getImmutableComputed(); } @@ -2538,7 +2622,16 @@ static void finishNSManagedImplInfo(VarDecl *var, if (var->isLet()) diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property); + SourceFile *parentFile = var->getDeclContext()->getParentSourceFile(); + auto diagnoseNotStored = [&](unsigned kind) { + // Skip diagnosing @NSManaged declarations in module interfaces. They are + // properties that are stored, but have specially synthesized observers + // and we should allow them to have getters and setters in a module + // interface. + if (parentFile && parentFile->Kind == SourceFileKind::Interface) + return; + diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind); }; @@ -2718,7 +2811,8 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, // Check if we have observers. } else if (hasWillSet || hasDidSet) { - if (storage->getAttrs().hasAttribute()) { + if (storage->getAttrs().hasAttribute() && + storage->getDeclContext()->isTypeContext()) { readImpl = ReadImplKind::Inherited; } else { readImpl = ReadImplKind::Stored; diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 9bd2198a560fc..96ae079900674 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ASTPrinter.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/Pattern.h" +#include "swift/Basic/Debug.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/APIntMap.h" @@ -120,7 +121,7 @@ namespace { DeclName Head; std::forward_list Spaces; - size_t computeSize(TypeChecker &TC, const DeclContext *DC, + size_t computeSize(const DeclContext *DC, SmallPtrSetImpl &cache) const { switch (getKind()) { case SpaceKind::Empty: @@ -136,11 +137,11 @@ namespace { cache.insert(getType().getPointer()); SmallVector spaces; - decompose(TC, DC, getType(), spaces); + decompose(DC, getType(), spaces); size_t acc = 0; for (auto &sp : spaces) { // Decomposed pattern spaces grow with the sum of the subspaces. - acc += sp.computeSize(TC, DC, cache); + acc += sp.computeSize(DC, cache); } cache.erase(getType().getPointer()); @@ -156,7 +157,7 @@ namespace { } // Constructor spaces grow with the product of their arguments. - acc *= sp.computeSize(TC, DC, cache); + acc *= sp.computeSize(DC, cache); } return acc; } @@ -164,7 +165,7 @@ namespace { size_t acc = 0; for (auto &sp : getSpaces()) { // Disjoint grow with the sum of the subspaces. - acc += sp.computeSize(TC, DC, cache); + acc += sp.computeSize(DC, cache); } return acc; } @@ -242,11 +243,11 @@ namespace { SpaceKind getKind() const { return Kind; } - void dump() const LLVM_ATTRIBUTE_USED; + SWIFT_DEBUG_DUMP; - size_t getSize(TypeChecker &TC, const DeclContext *DC) const { + size_t getSize(const DeclContext *DC) const { SmallPtrSet cache; - return computeSize(TC, DC, cache); + return computeSize(DC, cache); } bool isEmpty() const { return getKind() == SpaceKind::Empty; } @@ -291,8 +292,7 @@ namespace { // An optimization that computes if the difference of this space and // another space is empty. - bool isSubspace(const Space &other, TypeChecker &TC, - const DeclContext *DC) const { + bool isSubspace(const Space &other, const DeclContext *DC) const { if (this->isEmpty()) { return true; } @@ -309,7 +309,7 @@ namespace { PAIRCASE (SpaceKind::Disjunct, SpaceKind::UnknownCase): { // (S1 | ... | Sn) <= S iff (S1 <= S) && ... && (Sn <= S) for (auto &space : this->getSpaces()) { - if (!space.isSubspace(other, TC, DC)) { + if (!space.isSubspace(other, DC)) { return false; } } @@ -323,15 +323,15 @@ namespace { // (_ : Ty1) <= (_ : Ty2) iff D(Ty1) == D(Ty2) if (canDecompose(this->getType(), DC)) { - Space or1Space = decompose(TC, DC, this->getType()); - if (or1Space.isSubspace(other, TC, DC)) { + Space or1Space = decompose(DC, this->getType()); + if (or1Space.isSubspace(other, DC)) { return true; } } if (canDecompose(other.getType(), DC)) { - Space or2Space = decompose(TC, DC, other.getType()); - return this->isSubspace(or2Space, TC, DC); + Space or2Space = decompose(DC, other.getType()); + return this->isSubspace(or2Space, DC); } return false; @@ -339,7 +339,7 @@ namespace { PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct): { // (_ : Ty1) <= (S1 | ... | Sn) iff (S1 <= S) || ... || (Sn <= S) for (auto &dis : other.getSpaces()) { - if (this->isSubspace(dis, TC, DC)) { + if (this->isSubspace(dis, DC)) { return true; } } @@ -348,14 +348,14 @@ namespace { if (!canDecompose(this->getType(), DC)) { return false; } - Space or1Space = decompose(TC, DC, this->getType()); - return or1Space.isSubspace(other, TC, DC); + Space or1Space = decompose(DC, this->getType()); + return or1Space.isSubspace(other, DC); } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { // (_ : Ty1) <= H(p1 | ... | pn) iff D(Ty1) <= H(p1 | ... | pn) if (canDecompose(this->getType(), DC)) { - Space or1Space = decompose(TC, DC, this->getType()); - return or1Space.isSubspace(other, TC, DC); + Space or1Space = decompose(DC, this->getType()); + return or1Space.isSubspace(other, DC); } // An undecomposable type is always larger than its constructor space. return false; @@ -389,7 +389,7 @@ namespace { auto j = other.getSpaces().begin(); for (; i != this->getSpaces().end() && j != other.getSpaces().end(); ++i, ++j) { - if (!(*i).isSubspace(*j, TC, DC)) { + if (!(*i).isSubspace(*j, DC)) { return false; } } @@ -397,7 +397,7 @@ namespace { } PAIRCASE (SpaceKind::Constructor, SpaceKind::UnknownCase): for (auto ¶m : this->getSpaces()) { - if (param.isSubspace(other, TC, DC)) { + if (param.isSubspace(other, DC)) { return true; } } @@ -408,7 +408,7 @@ namespace { PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Disjunct): { // S <= (S1 | ... | Sn) <= S iff (S <= S1) || ... || (S <= Sn) for (auto ¶m : other.getSpaces()) { - if (this->isSubspace(param, TC, DC)) { + if (this->isSubspace(param, DC)) { return true; } } @@ -447,13 +447,11 @@ namespace { // computation had to be abandoned. // // \p minusCount is an optional pointer counting the number of - // times minus has run. + // remaining calls to minus before the computation times out. // Returns None if the computation "timed out". - Optional minus(const Space &other, TypeChecker &TC, - const DeclContext *DC, unsigned *minusCount) const { - - if (minusCount && TC.getSwitchCheckingInvocationThreshold() && - (*minusCount)++ >= TC.getSwitchCheckingInvocationThreshold()) + Optional minus(const Space &other, const DeclContext *DC, + unsigned *minusCount) const { + if (minusCount && (*minusCount)-- == 0) return None; if (this->isEmpty()) { @@ -473,8 +471,8 @@ namespace { } PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): { if (canDecompose(this->getType(), DC)) { - auto decomposition = decompose(TC, DC, this->getType()); - return decomposition.minus(other, TC, DC, minusCount); + auto decomposition = decompose(DC, this->getType()); + return decomposition.minus(other, DC, minusCount); } else { return *this; } @@ -495,7 +493,7 @@ namespace { PAIRCASE (SpaceKind::UnknownCase, SpaceKind::Disjunct): { Space tot = *this; for (auto s : other.getSpaces()) { - if (auto diff = tot.minus(s, TC, DC, minusCount)) + if (auto diff = tot.minus(s, DC, minusCount)) tot = *diff; else return None; @@ -510,7 +508,7 @@ namespace { PAIRCASE (SpaceKind::Disjunct, SpaceKind::UnknownCase): { SmallVector smallSpaces; for (auto s : this->getSpaces()) { - auto diff = s.minus(other, TC, DC, minusCount); + auto diff = s.minus(other, DC, minusCount); if (!diff) return None; if (diff->getKind() == SpaceKind::Disjunct) { @@ -531,7 +529,7 @@ namespace { for (const Space &space : smallSpaces) { bool alreadyHandled = llvm::any_of(usefulSmallSpaces, [&](const Space &previousSpace) { - return space.isSubspace(previousSpace, TC, DC); + return space.isSubspace(previousSpace, DC); }); if (alreadyHandled) continue; @@ -545,7 +543,7 @@ namespace { PAIRCASE (SpaceKind::Constructor, SpaceKind::UnknownCase): { SmallVector newSubSpaces; for (auto subSpace : this->getSpaces()) { - auto nextSpace = subSpace.minus(other, TC, DC, minusCount); + auto nextSpace = subSpace.minus(other, DC, minusCount); if (!nextSpace) return None; if (nextSpace.getValue().isEmpty()) @@ -582,7 +580,7 @@ namespace { // If one constructor parameter doesn't cover the other then we've // got to report the uncovered cases in a user-friendly way. - if (!s1.isSubspace(s2, TC, DC)) { + if (!s1.isSubspace(s2, DC)) { foundBad = true; } // Copy the params and replace the parameter at each index with the @@ -591,7 +589,7 @@ namespace { SmallVector copyParams(this->getSpaces().begin(), this->getSpaces().end()); - auto reducedSpaceOrNone = s1.minus(s2, TC, DC, minusCount); + auto reducedSpaceOrNone = s1.minus(s2, DC, minusCount); if (!reducedSpaceOrNone) return None; auto reducedSpace = *reducedSpaceOrNone; @@ -635,8 +633,8 @@ namespace { } if (canDecompose(other.getType(), DC)) { - auto decomposition = decompose(TC, DC, other.getType()); - return this->minus(decomposition, TC, DC, minusCount); + auto decomposition = decompose(DC, other.getType()); + return this->minus(decomposition, DC, minusCount); } return *this; } @@ -647,8 +645,8 @@ namespace { PAIRCASE (SpaceKind::Type, SpaceKind::BooleanConstant): { if (canDecompose(this->getType(), DC)) { - auto orSpace = decompose(TC, DC, this->getType()); - return orSpace.minus(other, TC, DC, minusCount); + auto orSpace = decompose(DC, this->getType()); + return orSpace.minus(other, DC, minusCount); } else { return *this; } @@ -786,7 +784,7 @@ namespace { }; // Decompose a type into its component spaces. - static void decompose(TypeChecker &TC, const DeclContext *DC, Type tp, + static void decompose(const DeclContext *DC, Type tp, SmallVectorImpl &arr) { assert(canDecompose(tp, DC) && "Non-decomposable type?"); @@ -844,10 +842,9 @@ namespace { } } - static Space decompose(TypeChecker &TC, const DeclContext *DC, - Type type) { + static Space decompose(const DeclContext *DC, Type type) { SmallVector spaces; - decompose(TC, DC, type, spaces); + decompose(DC, type, spaces); return Space::forDisjunct(spaces); } @@ -887,16 +884,16 @@ namespace { } }; - TypeChecker &TC; + ASTContext &Context; const SwitchStmt *Switch; const DeclContext *DC; APIntMap IntLiteralCache; llvm::DenseMap FloatLiteralCache; llvm::DenseMap StringLiteralCache; - SpaceEngine(TypeChecker &C, const SwitchStmt *SS, const DeclContext *DC) - : TC(C), Switch(SS), DC(DC) {} - + SpaceEngine(ASTContext &C, const SwitchStmt *SS, const DeclContext *DC) + : Context(C), Switch(SS), DC(DC) {} + bool checkRedundantLiteral(const Pattern *Pat, Expr *&PrevPattern) { if (Pat->getKind() != PatternKind::Expr) { return false; @@ -961,6 +958,7 @@ namespace { const CaseStmt *unknownCase = nullptr; SmallVector spaces; + auto &DE = Context.Diags; for (auto *caseBlock : Switch->getCases()) { if (caseBlock->hasUnknownAttr()) { assert(unknownCase == nullptr && "multiple unknown cases"); @@ -978,13 +976,13 @@ namespace { if (caseItem.isDefault()) return; - Space projection = projectPattern(TC, caseItem.getPattern()); + Space projection = projectPattern(caseItem.getPattern()); bool isRedundant = !projection.isEmpty() && llvm::any_of(spaces, [&](const Space &handled) { - return projection.isSubspace(handled, TC, DC); + return projection.isSubspace(handled, DC); }); if (isRedundant) { - TC.diagnose(caseItem.getStartLoc(), + DE.diagnose(caseItem.getStartLoc(), diag::redundant_particular_case) .highlight(caseItem.getSourceRange()); continue; @@ -993,10 +991,10 @@ namespace { Expr *cachedExpr = nullptr; if (checkRedundantLiteral(caseItem.getPattern(), cachedExpr)) { assert(cachedExpr && "Cache found hit but no expr?"); - TC.diagnose(caseItem.getStartLoc(), + DE.diagnose(caseItem.getStartLoc(), diag::redundant_particular_literal_case) .highlight(caseItem.getSourceRange()); - TC.diagnose(cachedExpr->getLoc(), + DE.diagnose(cachedExpr->getLoc(), diag::redundant_particular_literal_case_here) .highlight(cachedExpr->getSourceRange()); continue; @@ -1010,8 +1008,9 @@ namespace { Space totalSpace = Space::forType(subjectType, Identifier()); Space coveredSpace = Space::forDisjunct(spaces); - unsigned minusCount = 0; - auto diff = totalSpace.minus(coveredSpace, TC, DC, &minusCount); + unsigned minusCount + = Context.TypeCheckerOpts.SwitchCheckingInvocationThreshold; + auto diff = totalSpace.minus(coveredSpace, DC, &minusCount); if (!diff) { diagnoseMissingCases(RequiresDefault::SpaceTooLarge, Space(), unknownCase); @@ -1020,7 +1019,7 @@ namespace { auto uncovered = diff.getValue(); if (unknownCase && uncovered.isEmpty()) { - TC.diagnose(unknownCase->getLoc(), diag::redundant_particular_case) + DE.diagnose(unknownCase->getLoc(), diag::redundant_particular_case) .highlight(unknownCase->getSourceRange()); } @@ -1028,7 +1027,7 @@ namespace { // all handled; otherwise, we ignore the ones that were added for enums // that are implicitly frozen. uncovered = *uncovered.minus(Space::forUnknown(unknownCase == nullptr), - TC, DC, /*&minusCount*/ nullptr); + DC, /*&minusCount*/ nullptr); if (uncovered.isEmpty()) return; @@ -1039,7 +1038,7 @@ namespace { if (uncovered.getKind() == SpaceKind::Type) { if (Space::canDecompose(uncovered.getType(), DC)) { SmallVector spaces; - Space::decompose(TC, DC, uncovered.getType(), spaces); + Space::decompose(DC, uncovered.getType(), spaces); diagnoseMissingCases(RequiresDefault::No, Space::forDisjunct(spaces), unknownCase); } else { @@ -1063,6 +1062,7 @@ namespace { void diagnoseMissingCases(RequiresDefault defaultReason, Space uncovered, const CaseStmt *unknownCase = nullptr) { + auto &DE = Context.Diags; SourceLoc startLoc = Switch->getStartLoc(); SourceLoc insertLoc; if (unknownCase) @@ -1073,7 +1073,7 @@ namespace { llvm::SmallString<128> buffer; llvm::raw_svector_ostream OS(buffer); - bool InEditor = TC.Context.LangOpts.DiagnosticsEditorMode; + bool InEditor = Context.LangOpts.DiagnosticsEditorMode; // Decide whether we want an error or a warning. Optional mainDiagType = @@ -1091,8 +1091,8 @@ namespace { auto diagnostic = defaultReason == RequiresDefault::UncoveredSwitch ? diag::non_exhaustive_switch : diag::possibly_non_exhaustive_switch; - TC.diagnose(startLoc, diagnostic); - TC.diagnose(unknownCase->getLoc(), + DE.diagnose(startLoc, diagnostic); + DE.diagnose(unknownCase->getLoc(), diag::non_exhaustive_switch_drop_unknown) .fixItRemoveChars(unknownCase->getStartLoc(), unknownCase->getLoc()); @@ -1105,9 +1105,9 @@ namespace { case DowngradeToWarning::No: break; case DowngradeToWarning::ForUnknownCase: { - if (TC.Context.LangOpts.DebuggerSupport || - TC.Context.LangOpts.Playground || - !TC.getLangOpts().EnableNonFrozenEnumExhaustivityDiagnostics) { + if (Context.LangOpts.DebuggerSupport || + Context.LangOpts.Playground || + !Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics) { // Don't require covering unknown cases in the debugger or in // playgrounds. return; @@ -1119,7 +1119,7 @@ namespace { shouldIncludeFutureVersionComment = theEnum->getParentModule()->isSystemModule(); } - TC.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only, + DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only, subjectType, shouldIncludeFutureVersionComment); mainDiagType = None; } @@ -1131,21 +1131,21 @@ namespace { break; case RequiresDefault::EmptySwitchBody: { OS << tok::kw_default << ":\n" << placeholder << "\n"; - TC.diagnose(startLoc, diag::empty_switch_stmt) + DE.diagnose(startLoc, diag::empty_switch_stmt) .fixItInsert(insertLoc, buffer.str()); } return; case RequiresDefault::UncoveredSwitch: { OS << tok::kw_default << ":\n" << placeholder << "\n"; - TC.diagnose(startLoc, mainDiagType.getValue()); - TC.diagnose(startLoc, diag::missing_several_cases, /*default*/true) + DE.diagnose(startLoc, mainDiagType.getValue()); + DE.diagnose(startLoc, diag::missing_several_cases, /*default*/true) .fixItInsert(insertLoc, buffer.str()); } return; case RequiresDefault::SpaceTooLarge: { OS << tok::kw_default << ":\n" << "<#fatalError()#>" << "\n"; - TC.diagnose(startLoc, diag::possibly_non_exhaustive_switch); - TC.diagnose(startLoc, diag::missing_several_cases, /*default*/true) + DE.diagnose(startLoc, diag::possibly_non_exhaustive_switch); + DE.diagnose(startLoc, diag::missing_several_cases, /*default*/true) .fixItInsert(insertLoc, buffer.str()); } return; @@ -1156,7 +1156,7 @@ namespace { // Check if we still have to emit the main diganostic. if (mainDiagType.hasValue()) - TC.diagnose(startLoc, mainDiagType.getValue()); + DE.diagnose(startLoc, mainDiagType.getValue()); // Add notes to explain what's missing. auto processUncoveredSpaces = @@ -1175,7 +1175,7 @@ namespace { flatsSortedBySize.push_back(&space); std::stable_sort(flatsSortedBySize.begin(), flatsSortedBySize.end(), [&](const Space *left, const Space *right) { - return left->getSize(TC, DC) > right->getSize(TC, DC); + return left->getSize(DC) > right->getSize(DC); }); // ...and then remove any of the later spaces that are contained @@ -1184,7 +1184,7 @@ namespace { for (const Space *space : flatsSortedBySize) { bool alreadyHandled = llvm::any_of(flatsToEmit, [&](const Space *previousSpace) { - return space->isSubspace(*previousSpace, TC, DC); + return space->isSubspace(*previousSpace, DC); }); if (alreadyHandled) continue; @@ -1205,7 +1205,13 @@ namespace { // will later decompose the space into cases. continue; } - if (!TC.getLangOpts().EnableNonFrozenEnumExhaustivityDiagnostics) + if (!Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics) + continue; + + // This can occur if the switch is empty and the subject type is an + // enum. If decomposing the enum type yields an unknown space that + // is not required, don't suggest adding it in the fix-it. + if (flat.isAllowedButNotRequired()) continue; } @@ -1237,7 +1243,7 @@ namespace { OS << "@unknown " << tok::kw_default; if (onlyOneUncoveredSpace) { OS << ":\n<#fatalError()#>\n"; - TC.diagnose(startLoc, diag::missing_unknown_case) + DE.diagnose(startLoc, diag::missing_unknown_case) .fixItInsert(insertLoc, buffer.str()); alreadyEmittedSomething = true; return; @@ -1250,7 +1256,7 @@ namespace { }); if (!alreadyEmittedSomething) { - TC.diagnose(startLoc, diag::missing_several_cases, false) + DE.diagnose(startLoc, diag::missing_several_cases, false) .fixItInsert(insertLoc, buffer.str()); } @@ -1258,7 +1264,7 @@ namespace { processUncoveredSpaces([&](const Space &space, bool onlyOneUncoveredSpace) { if (space.getKind() == SpaceKind::UnknownCase) { - auto note = TC.diagnose(startLoc, diag::missing_unknown_case); + auto note = DE.diagnose(startLoc, diag::missing_unknown_case); if (onlyOneUncoveredSpace) note.fixItInsert(insertLoc, "@unknown default:\n<#fatalError#>()\n"); return; @@ -1266,7 +1272,7 @@ namespace { buffer.clear(); space.show(OS); - TC.diagnose(startLoc, diag::missing_particular_case, buffer.str()); + DE.diagnose(startLoc, diag::missing_particular_case, buffer.str()); }); } } @@ -1371,7 +1377,7 @@ namespace { } /// Recursively project a pattern into a Space. - static Space projectPattern(TypeChecker &TC, const Pattern *item) { + static Space projectPattern(const Pattern *item) { switch (item->getKind()) { case PatternKind::Any: return Space::forType(item->getType(), Identifier()); @@ -1387,7 +1393,7 @@ namespace { case CheckedCastKind::BridgingCoercion: { if (auto *subPattern = IP->getSubPattern()) { // Project the cast target's subpattern. - Space castSubSpace = projectPattern(TC, subPattern); + Space castSubSpace = projectPattern(subPattern); // If we recieved a type space from a named pattern or a wildcard // we have to re-project with the cast's target type to maintain // consistency with the scrutinee's type. @@ -1418,17 +1424,18 @@ namespace { return Space(); case PatternKind::Var: { auto *VP = cast(item); - return projectPattern(TC, VP->getSubPattern()); + return projectPattern(VP->getSubPattern()); } case PatternKind::Paren: { auto *PP = cast(item); - return projectPattern(TC, PP->getSubPattern()); + return projectPattern(PP->getSubPattern()); } case PatternKind::OptionalSome: { auto *OSP = cast(item); - Identifier name = TC.Context.getOptionalSomeDecl()->getName(); + auto &Ctx = OSP->getElementDecl()->getASTContext(); + Identifier name = Ctx.getOptionalSomeDecl()->getName(); - auto subSpace = projectPattern(TC, OSP->getSubPattern()); + auto subSpace = projectPattern(OSP->getSubPattern()); // To match patterns like (_, _, ...)?, we must rewrite the underlying // tuple pattern to .some(_, _, ...) first. if (subSpace.getKind() == SpaceKind::Constructor && @@ -1444,7 +1451,9 @@ namespace { if (!SP) { // If there's no sub-pattern then there's no further recursive // structure here. Yield the constructor space. - return Space::forConstructor(item->getType(), VP->getName(), None); + // FIXME: Compound names. + return Space::forConstructor(item->getType(), + VP->getName().getBaseIdentifier(), None); } SmallVector conArgSpace; @@ -1454,9 +1463,11 @@ namespace { std::transform(TP->getElements().begin(), TP->getElements().end(), std::back_inserter(conArgSpace), [&](TuplePatternElt pate) { - return projectPattern(TC, pate.getPattern()); + return projectPattern(pate.getPattern()); }); - return Space::forConstructor(item->getType(), VP->getName(), + // FIXME: Compound names. + return Space::forConstructor(item->getType(), + VP->getName().getBaseIdentifier(), conArgSpace); } case PatternKind::Paren: { @@ -1476,9 +1487,9 @@ namespace { if (auto *TTy = outerType->getAs()) Space::getTupleTypeSpaces(outerType, TTy, conArgSpace); else - conArgSpace.push_back(projectPattern(TC, SP)); + conArgSpace.push_back(projectPattern(SP)); } else if (SP->getKind() == PatternKind::Tuple) { - Space argTupleSpace = projectPattern(TC, SP); + Space argTupleSpace = projectPattern(SP); // Tuples are modeled as if they are enums with a single, nameless // case, which means argTupleSpace will either be a Constructor or // Empty space. If it's empty (i.e. it contributes nothing to the @@ -1488,13 +1499,15 @@ namespace { assert(argTupleSpace.getKind() == SpaceKind::Constructor); conArgSpace.push_back(argTupleSpace); } else { - conArgSpace.push_back(projectPattern(TC, SP)); + conArgSpace.push_back(projectPattern(SP)); } - return Space::forConstructor(item->getType(), VP->getName(), + // FIXME: Compound names. + return Space::forConstructor(item->getType(), + VP->getName().getBaseIdentifier(), conArgSpace); } default: - return projectPattern(TC, SP); + return projectPattern(SP); } } case PatternKind::Tuple: { @@ -1503,7 +1516,7 @@ namespace { std::transform(TP->getElements().begin(), TP->getElements().end(), std::back_inserter(conArgSpace), [&](TuplePatternElt pate) { - return projectPattern(TC, pate.getPattern()); + return projectPattern(pate.getPattern()); }); return Space::forConstructor(item->getType(), Identifier(), conArgSpace); @@ -1517,7 +1530,7 @@ namespace { void TypeChecker::checkSwitchExhaustiveness(const SwitchStmt *stmt, const DeclContext *DC, bool limited) { - SpaceEngine(*this, stmt, DC).checkExhaustiveness(limited); + SpaceEngine(DC->getASTContext(), stmt, DC).checkExhaustiveness(limited); } void SpaceEngine::Space::dump() const { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 5bd46898a11ee..483c9db23cb4d 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -140,9 +140,13 @@ Type TypeResolution::resolveDependentMemberType( Type baseTy, DeclContext *DC, SourceRange baseRange, ComponentIdentTypeRepr *ref) const { + // FIXME(ModQual): Reject qualified names immediately; they cannot be + // dependent member types. + Identifier refIdentifier = ref->getNameRef().getBaseIdentifier(); + switch (stage) { case TypeResolutionStage::Structural: - return DependentMemberType::get(baseTy, ref->getIdentifier()); + return DependentMemberType::get(baseTy, refIdentifier); case TypeResolutionStage::Contextual: llvm_unreachable("Dependent type after archetype substitution"); @@ -168,19 +172,17 @@ Type TypeResolution::resolveDependentMemberType( // Look for a nested type with the given name. if (auto nestedType = - baseEquivClass->lookupNestedType(*builder, ref->getIdentifier())) { + baseEquivClass->lookupNestedType(*builder, refIdentifier)) { // Record the type we found. ref->setValue(nestedType, nullptr); } else { // Resolve the base to a potential archetype. // Perform typo correction. - TypeChecker &tc = static_cast(*ctx.getLazyResolver()); - TypoCorrectionResults corrections(tc, ref->getIdentifier(), - DeclNameLoc(ref->getIdLoc())); - tc.performTypoCorrection(DC, DeclRefKind::Ordinary, - MetatypeType::get(baseTy), - NameLookupFlags::ProtocolMembers, - corrections, builder); + TypoCorrectionResults corrections(ref->getNameRef(), ref->getNameLoc()); + TypeChecker::performTypoCorrection(DC, DeclRefKind::Ordinary, + MetatypeType::get(baseTy), + NameLookupFlags::ProtocolMembers, + corrections, builder); // Check whether we have a single type result. auto singleType = cast_or_null( @@ -190,8 +192,8 @@ Type TypeResolution::resolveDependentMemberType( // If we don't have a single result, complain and fail. if (!singleType) { - Identifier name = ref->getIdentifier(); - SourceLoc nameLoc = ref->getIdLoc(); + auto name = ref->getNameRef(); + auto nameLoc = ref->getNameLoc(); ctx.Diags.diagnose(nameLoc, diag::invalid_member_type, name, baseTy) .highlight(baseRange); corrections.noteAllCandidates(); @@ -200,14 +202,14 @@ Type TypeResolution::resolveDependentMemberType( } // We have a single type result. Suggest it. - ctx.Diags.diagnose(ref->getIdLoc(), diag::invalid_member_type_suggest, - baseTy, ref->getIdentifier(), - singleType->getBaseName().getIdentifier()) - .fixItReplace(ref->getIdLoc(), + ctx.Diags.diagnose(ref->getNameLoc(), diag::invalid_member_type_suggest, + baseTy, ref->getNameRef(), + singleType->getBaseName()) + .fixItReplace(ref->getNameLoc().getSourceRange(), singleType->getBaseName().userFacingName()); // Correct to the single type result. - ref->overwriteIdentifier(singleType->getBaseName().getIdentifier()); + ref->overwriteNameRef(singleType->createNameRef()); ref->setValue(singleType, nullptr); } @@ -376,49 +378,41 @@ Type TypeChecker::getOptionalType(SourceLoc loc, Type elementType) { return OptionalType::get(elementType); } -Type TypeChecker::getStringType(DeclContext *dc) { +Type TypeChecker::getStringType(ASTContext &Context) { if (auto typeDecl = Context.getStringDecl()) return typeDecl->getDeclaredInterfaceType(); return Type(); } -Type TypeChecker::getSubstringType(DeclContext *dc) { +Type TypeChecker::getSubstringType(ASTContext &Context) { if (auto typeDecl = Context.getSubstringDecl()) return typeDecl->getDeclaredInterfaceType(); return Type(); } -Type TypeChecker::getIntType(DeclContext *dc) { +Type TypeChecker::getIntType(ASTContext &Context) { if (auto typeDecl = Context.getIntDecl()) return typeDecl->getDeclaredInterfaceType(); return Type(); } -Type TypeChecker::getInt8Type(DeclContext *dc) { +Type TypeChecker::getInt8Type(ASTContext &Context) { if (auto typeDecl = Context.getInt8Decl()) return typeDecl->getDeclaredInterfaceType(); return Type(); } -Type TypeChecker::getUInt8Type(DeclContext *dc) { +Type TypeChecker::getUInt8Type(ASTContext &Context) { if (auto typeDecl = Context.getUInt8Decl()) return typeDecl->getDeclaredInterfaceType(); return Type(); } -/// Find the standard type of exceptions. -/// -/// We call this the "exception type" to try to avoid confusion with -/// the AST's ErrorType node. -Type TypeChecker::getExceptionType(DeclContext *dc, SourceLoc loc) { - return Context.getErrorDecl()->getDeclaredType(); -} - Type TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc, Type dynamicType, @@ -431,7 +425,7 @@ TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc, if (!valueType->isPotentiallyBridgedValueType()) return Type(); - return Context.getBridgedToObjC(dc, valueType); + return dc->getASTContext().getBridgedToObjC(dc, valueType); } Type TypeChecker::resolveTypeInContext(TypeDecl *typeDecl, DeclContext *foundDC, @@ -635,17 +629,107 @@ static bool isPointerToVoid(ASTContext &Ctx, Type Ty, bool &IsMutable) { return BGT->getGenericArgs().front()->isVoid(); } -Type TypeChecker::applyGenericArguments(Type type, - SourceLoc loc, - TypeResolution resolution, - GenericIdentTypeRepr *generic, - TypeResolutionOptions options) { +static Type checkConstrainedExtensionRequirements(Type type, + SourceLoc loc, + DeclContext *dc) { + // Even if the type is not generic, it might be inside of a generic + // context, so we need to check requirements. + GenericTypeDecl *decl; + Type parentTy; + if (auto *aliasTy = dyn_cast(type.getPointer())) { + decl = aliasTy->getDecl(); + parentTy = aliasTy->getParent(); + } else if (auto *nominalTy = type->getAs()) { + decl = nominalTy->getDecl(); + parentTy = nominalTy->getParent(); + } else { + return type; + } + + // FIXME: Some day the type might also have its own 'where' clause, even + // if its not generic. + + auto *ext = dyn_cast(decl->getDeclContext()); + if (!ext || !ext->isConstrainedExtension()) + return type; + + if (parentTy->hasUnboundGenericType() || + parentTy->hasTypeVariable()) { + return type; + } + + auto subMap = parentTy->getContextSubstitutions(ext); + + SourceLoc noteLoc = ext->getLoc(); + if (noteLoc.isInvalid()) + noteLoc = loc; + + auto genericSig = ext->getGenericSignature(); + auto result = + TypeChecker::checkGenericArguments( + dc, loc, noteLoc, type, + genericSig->getGenericParams(), + genericSig->getRequirements(), + QueryTypeSubstitutionMap{subMap}, + TypeChecker::LookUpConformance(dc), + None); + + switch (result) { + case RequirementCheckResult::Failure: + case RequirementCheckResult::SubstitutionFailure: + return ErrorType::get(dc->getASTContext()); + case RequirementCheckResult::Success: + return type; + } +} + +static void diagnoseUnboundGenericType(Type ty, SourceLoc loc); + +/// Apply generic arguments to the given type. +/// +/// If the type is itself not generic, this does nothing. +/// +/// This function emits diagnostics about an invalid type or the wrong number +/// of generic arguments, whereas applyUnboundGenericArguments requires this +/// to be in a correct and valid form. +/// +/// \param type The generic type to which to apply arguments. +/// \param resolution The type resolution to perform. +/// \param comp The arguments to apply with the angle bracket range for +/// diagnostics. +/// \param options The type resolution context. +/// +/// \returns A BoundGenericType bound to the given arguments, or null on +/// error. +/// +/// \see applyUnboundGenericArguments +static Type applyGenericArguments(Type type, + TypeResolution resolution, + ComponentIdentTypeRepr *comp, + TypeResolutionOptions options) { + auto dc = resolution.getDeclContext(); + auto loc = comp->getNameLoc().getBaseNameLoc(); + + auto *generic = dyn_cast(comp); + if (!generic) { + if (type->is() && + !options.is(TypeResolverContext::TypeAliasDecl) && + !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { + diagnoseUnboundGenericType(type, loc); + return ErrorType::get(type->getASTContext()); + } + + if (resolution.getStage() == TypeResolutionStage::Structural) + return type; + + return checkConstrainedExtensionRequirements(type, loc, dc); + } + if (type->hasError()) { generic->setInvalid(); return type; } - auto dc = resolution.getDeclContext(); auto &ctx = dc->getASTContext(); auto &diags = ctx.Diags; @@ -725,15 +809,14 @@ Type TypeChecker::applyGenericArguments(Type type, args.push_back(substTy); } - auto result = applyUnboundGenericArguments(unboundType, genericDecl, loc, - resolution, args); - if (!result) - return result; + auto result = TypeChecker::applyUnboundGenericArguments( + unboundType, genericDecl, loc, + resolution, args); if (!options.contains(TypeResolutionFlags::AllowUnavailable)) { if (options.isAnyExpr() || dc->getParent()->isLocalContext()) if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) - diagnoseGenericTypeExportability(loc, result, dc); + TypeChecker::diagnoseGenericTypeExportability(loc, result, dc); } // Migration hack. @@ -892,15 +975,15 @@ static void maybeDiagnoseBadConformanceRef(DeclContext *dc, // If we weren't given a conformance, go look it up. ProtocolConformance *conformance = nullptr; - if (protocol) - if (auto conformanceRef = TypeChecker::conformsToProtocol( - parentTy, protocol, dc, - (ConformanceCheckFlags::InExpression | - ConformanceCheckFlags::SuppressDependencyTracking | - ConformanceCheckFlags::SkipConditionalRequirements))) { - if (conformanceRef->isConcrete()) - conformance = conformanceRef->getConcrete(); - } + if (protocol) { + auto conformanceRef = TypeChecker::conformsToProtocol( + parentTy, protocol, dc, + (ConformanceCheckFlags::InExpression | + ConformanceCheckFlags::SuppressDependencyTracking | + ConformanceCheckFlags::SkipConditionalRequirements)); + if (conformanceRef.isConcrete()) + conformance = conformanceRef.getConcrete(); + } // If any errors have occurred, don't bother diagnosing this cross-file // issue. @@ -917,41 +1000,26 @@ static void maybeDiagnoseBadConformanceRef(DeclContext *dc, } /// Returns a valid type or ErrorType in case of an error. -static Type resolveTypeDecl(TypeDecl *typeDecl, SourceLoc loc, +static Type resolveTypeDecl(TypeDecl *typeDecl, DeclContext *foundDC, TypeResolution resolution, - GenericIdentTypeRepr *generic, + ComponentIdentTypeRepr *comp, TypeResolutionOptions options) { - // Resolve the type declaration to a specific type. How this occurs // depends on the current context and where the type was found. - Type type = TypeChecker::resolveTypeInContext(typeDecl, foundDC, resolution, - options, generic); - - if (type->is() && !generic && - !options.is(TypeResolverContext::TypeAliasDecl) && - !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { - diagnoseUnboundGenericType(type, loc); - return ErrorType::get(typeDecl->getASTContext()); - } + Type type = TypeChecker::resolveTypeInContext( + typeDecl, foundDC, resolution, options, + isa(comp)); if (type->hasError() && foundDC && (isa(typeDecl) || isa(typeDecl))) { auto fromDC = resolution.getDeclContext(); assert(fromDC && "No declaration context for type resolution?"); maybeDiagnoseBadConformanceRef(fromDC, foundDC->getDeclaredInterfaceType(), - loc, typeDecl); - } - - if (generic) { - // Apply the generic arguments to the type. - type = TypeChecker::applyGenericArguments(type, loc, resolution, generic, - options); - if (!type) - return nullptr; + comp->getNameLoc().getBaseNameLoc(), + typeDecl); } - assert(type); - return type; + return applyGenericArguments(type, resolution, comp, options); } static std::string getDeclNameFromContext(DeclContext *dc, @@ -1003,7 +1071,7 @@ static Type diagnoseUnknownType(TypeResolution resolution, // Unqualified lookup case. if (parentType.isNull()) { - if (comp->getIdentifier() == ctx.Id_Self && + if (comp->getNameRef().isSimpleName(ctx.Id_Self) && !isa(comp)) { DeclContext *nominalDC = nullptr; NominalTypeDecl *nominal = nullptr; @@ -1015,18 +1083,18 @@ static Type diagnoseUnknownType(TypeResolution resolution, // Produce a Fix-It replacing 'Self' with the nominal type name. auto name = getDeclNameFromContext(dc, nominal); - diags.diagnose(comp->getIdLoc(), diag::dynamic_self_invalid, name) - .fixItReplace(comp->getIdLoc(), name); + diags.diagnose(comp->getNameLoc(), diag::dynamic_self_invalid, name) + .fixItReplace(comp->getNameLoc().getSourceRange(), name); auto type = resolution.mapTypeIntoContext( dc->getInnermostTypeContext()->getSelfInterfaceType()); - comp->overwriteIdentifier(nominal->getName()); + comp->overwriteNameRef(DeclNameRef(nominal->getName())); comp->setValue(nominal, nominalDC->getParent()); return type; } // Attempt to refer to 'Self' from a free function. - diags.diagnose(comp->getIdLoc(), diag::dynamic_self_non_method, + diags.diagnose(comp->getNameLoc(), diag::dynamic_self_non_method, dc->getParent()->isLocalContext()); return ErrorType::get(ctx); @@ -1037,13 +1105,13 @@ static Type diagnoseUnknownType(TypeResolution resolution, relookupOptions |= NameLookupFlags::KnownPrivate; relookupOptions |= NameLookupFlags::IgnoreAccessControl; auto inaccessibleResults = - TypeChecker::lookupUnqualifiedType(dc, comp->getIdentifier(), - comp->getIdLoc(), relookupOptions); + TypeChecker::lookupUnqualifiedType(dc, comp->getNameRef(), + comp->getLoc(), relookupOptions); if (!inaccessibleResults.empty()) { // FIXME: What if the unviable candidates have different levels of access? auto first = cast(inaccessibleResults.front().getValueDecl()); - diags.diagnose(comp->getIdLoc(), diag::candidate_inaccessible, - comp->getIdentifier(), first->getFormalAccess()); + diags.diagnose(comp->getNameLoc(), diag::candidate_inaccessible, + first->getBaseName(), first->getFormalAccess()); // FIXME: If any of the candidates (usually just one) are in the same // module we could offer a fix-it. @@ -1057,22 +1125,22 @@ static Type diagnoseUnknownType(TypeResolution resolution, } // Fallback. - SourceLoc L = comp->getIdLoc(); - SourceRange R = SourceRange(comp->getIdLoc()); + auto L = comp->getNameLoc(); + SourceRange R = comp->getNameLoc().getSourceRange(); // Check if the unknown type is in the type remappings. auto &Remapped = ctx.RemappedTypes; - auto TypeName = comp->getIdentifier().str(); + auto TypeName = comp->getNameRef().getBaseIdentifier().str(); auto I = Remapped.find(TypeName); if (I != Remapped.end()) { auto RemappedTy = I->second->getString(); diags.diagnose(L, diag::use_undeclared_type_did_you_mean, - comp->getIdentifier(), RemappedTy) + comp->getNameRef(), RemappedTy) .highlight(R) .fixItReplace(R, RemappedTy); // Replace the computed type with the suggested type. - comp->overwriteIdentifier(ctx.getIdentifier(RemappedTy)); + comp->overwriteNameRef(DeclNameRef(ctx.getIdentifier(RemappedTy))); // HACK: 'NSUInteger' suggests both 'UInt' and 'Int'. if (TypeName == ctx.getSwiftName(KnownFoundationEntity::NSUInteger)) { @@ -1084,7 +1152,7 @@ static Type diagnoseUnknownType(TypeResolution resolution, } diags.diagnose(L, diag::use_undeclared_type, - comp->getIdentifier()) + comp->getNameRef()) .highlight(R); return ErrorType::get(ctx); @@ -1092,8 +1160,8 @@ static Type diagnoseUnknownType(TypeResolution resolution, // Qualified lookup case. if (!parentType->mayHaveMembers()) { - diags.diagnose(comp->getIdLoc(), diag::invalid_member_type, - comp->getIdentifier(), parentType) + diags.diagnose(comp->getNameLoc(), diag::invalid_member_type, + comp->getNameRef(), parentType) .highlight(parentRange); return ErrorType::get(ctx); } @@ -1103,13 +1171,13 @@ static Type diagnoseUnknownType(TypeResolution resolution, relookupOptions |= NameLookupFlags::KnownPrivate; relookupOptions |= NameLookupFlags::IgnoreAccessControl; auto inaccessibleMembers = - TypeChecker::lookupMemberType(dc, parentType, comp->getIdentifier(), + TypeChecker::lookupMemberType(dc, parentType, comp->getNameRef(), relookupOptions); if (inaccessibleMembers) { // FIXME: What if the unviable candidates have different levels of access? const TypeDecl *first = inaccessibleMembers.front().Member; - diags.diagnose(comp->getIdLoc(), diag::candidate_inaccessible, - comp->getIdentifier(), first->getFormalAccess()); + diags.diagnose(comp->getNameLoc(), diag::candidate_inaccessible, + first->getBaseName(), first->getFormalAccess()); // FIXME: If any of the candidates (usually just one) are in the same module // we could offer a fix-it. @@ -1126,8 +1194,8 @@ static Type diagnoseUnknownType(TypeResolution resolution, // Lookup into a type. if (auto moduleType = parentType->getAs()) { - diags.diagnose(comp->getIdLoc(), diag::no_module_type, - comp->getIdentifier(), moduleType->getModule()->getName()); + diags.diagnose(comp->getNameLoc(), diag::no_module_type, + comp->getNameRef(), moduleType->getModule()->getName()); } else { LookupResult memberLookup; // Let's try to lookup given identifier as a member of the parent type, @@ -1138,19 +1206,19 @@ static Type diagnoseUnknownType(TypeResolution resolution, memberLookupOptions |= NameLookupFlags::KnownPrivate; memberLookup = TypeChecker::lookupMember(dc, parentType, - comp->getIdentifier(), + comp->getNameRef(), memberLookupOptions); // Looks like this is not a member type, but simply a member of parent type. if (!memberLookup.empty()) { auto member = memberLookup[0].getValueDecl(); - diags.diagnose(comp->getIdLoc(), diag::invalid_member_reference, - member->getDescriptiveKind(), comp->getIdentifier(), + diags.diagnose(comp->getNameLoc(), diag::invalid_member_reference, + member->getDescriptiveKind(), member->getFullName(), parentType) .highlight(parentRange); } else { - diags.diagnose(comp->getIdLoc(), diag::invalid_member_type, - comp->getIdentifier(), parentType) + diags.diagnose(comp->getNameLoc(), diag::invalid_member_type, + comp->getNameRef(), parentType) .highlight(parentRange); // Note where the type was defined, this can help diagnose if the user // expected name lookup to find a module when there's a conflicting type. @@ -1226,18 +1294,17 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, // that now. if (auto *typeDecl = comp->getBoundDecl()) { // Resolve the type declaration within this context. - return resolveTypeDecl(typeDecl, comp->getIdLoc(), - comp->getDeclContext(), resolution, - dyn_cast(comp), options); + return resolveTypeDecl(typeDecl, comp->getDeclContext(), resolution, + comp, options); } // Resolve the first component, which is the only one that requires // unqualified name lookup. auto DC = resolution.getDeclContext(); - auto id = comp->getIdentifier(); + auto id = comp->getNameRef(); // Dynamic 'Self' in the result type of a function body. - if (id == ctx.Id_Self) { + if (id.isSimpleName(ctx.Id_Self)) { if (auto *typeDC = DC->getInnermostTypeContext()) { // FIXME: The passed-in TypeRepr should get 'typechecked' as well. // The issue is though that ComponentIdentTypeRepr only accepts a ValueDecl @@ -1260,9 +1327,7 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (options.contains(TypeResolutionFlags::KnownNonCascadingDependency)) lookupOptions |= NameLookupFlags::KnownPrivate; - auto globals = TypeChecker::lookupUnqualifiedType(DC, - id, - comp->getIdLoc(), + auto globals = TypeChecker::lookupUnqualifiedType(DC, id, comp->getLoc(), lookupOptions); // Process the names we found. @@ -1274,13 +1339,8 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, auto *foundDC = entry.getDeclContext(); auto *typeDecl = cast(entry.getValueDecl()); - Type type = resolveTypeDecl(typeDecl, comp->getIdLoc(), - foundDC, resolution, - dyn_cast(comp), options); - - if (!type) - return type; - + Type type = resolveTypeDecl(typeDecl, foundDC, resolution, + comp, options); if (type->is()) return type; @@ -1306,9 +1366,9 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, // FIXME: We could recover by looking at later components. if (isAmbiguous) { if (!options.contains(TypeResolutionFlags::SilenceErrors)) { - diags.diagnose(comp->getIdLoc(), diag::ambiguous_type_base, - comp->getIdentifier()) - .highlight(comp->getIdLoc()); + diags.diagnose(comp->getNameLoc(), diag::ambiguous_type_base, + comp->getNameRef()) + .highlight(comp->getNameLoc().getSourceRange()); for (auto entry : globals) { entry.getValueDecl()->diagnose(diag::found_candidate); } @@ -1334,7 +1394,7 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution, } static void diagnoseAmbiguousMemberType(Type baseTy, SourceRange baseRange, - Identifier name, SourceLoc nameLoc, + DeclNameRef name, DeclNameLoc nameLoc, LookupTypeResult &lookup) { ASTContext &ctx = baseTy->getASTContext(); auto &diags = ctx.Diags; @@ -1364,23 +1424,6 @@ static Type resolveNestedIdentTypeComponent( auto &ctx = DC->getASTContext(); auto &diags = ctx.Diags; - auto maybeApplyGenericArgs = [&](Type memberType) { - // If there are generic arguments, apply them now. - if (auto genComp = dyn_cast(comp)) { - return TypeChecker::applyGenericArguments(memberType, comp->getIdLoc(), - resolution, genComp, options); - } - - if (memberType->is() && - !options.is(TypeResolverContext::TypeAliasDecl) && - !options.contains(TypeResolutionFlags::AllowUnboundGenerics)) { - diagnoseUnboundGenericType(memberType, comp->getLoc()); - return ErrorType::get(ctx); - } - - return memberType; - }; - auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType, AssociatedTypeDecl *inferredAssocType) { // Diagnose invalid cases. @@ -1390,12 +1433,12 @@ static Type resolveNestedIdentTypeComponent( diagnoseUnboundGenericType(parentTy, parentRange.End); else if (parentTy->isExistentialType() && isa(member)) { - diags.diagnose(comp->getIdLoc(), diag::assoc_type_outside_of_protocol, - comp->getIdentifier()); + diags.diagnose(comp->getNameLoc(), diag::assoc_type_outside_of_protocol, + comp->getNameRef()); } else if (parentTy->isExistentialType() && isa(member)) { - diags.diagnose(comp->getIdLoc(), diag::typealias_outside_of_protocol, - comp->getIdentifier()); + diags.diagnose(comp->getNameLoc(), diag::typealias_outside_of_protocol, + comp->getNameRef()); } } @@ -1415,22 +1458,13 @@ static Type resolveNestedIdentTypeComponent( // Diagnose a bad conformance reference if we need to. if (!options.contains(TypeResolutionFlags::SilenceErrors) && - inferredAssocType && memberType && memberType->hasError()) { + inferredAssocType && memberType->hasError()) { maybeDiagnoseBadConformanceRef(DC, parentTy, comp->getLoc(), inferredAssocType); } - // If we found a reference to an associated type or other member type that - // was marked invalid, just return ErrorType to silence downstream errors. - if (member->isInvalid()) - return ErrorType::get(ctx); - - // At this point, we need to have resolved the type of the member. - if (!memberType || memberType->hasError()) - return memberType; - // If there are generic arguments, apply them now. - return maybeApplyGenericArgs(memberType); + return applyGenericArguments(memberType, resolution, comp, options); }; // Short-circuiting. @@ -1447,7 +1481,7 @@ static Type resolveNestedIdentTypeComponent( // type later on. if (!memberType->is() || memberType->castTo()->getAssocType()) { - return maybeApplyGenericArgs(memberType); + return applyGenericArguments(memberType, resolution, comp, options); } return memberType; @@ -1479,7 +1513,7 @@ static Type resolveNestedIdentTypeComponent( LookupTypeResult memberTypes; if (parentTy->mayHaveMembers()) memberTypes = TypeChecker::lookupMemberType(DC, parentTy, - comp->getIdentifier(), + comp->getNameRef(), lookupOptions); // Name lookup was ambiguous. Complain. @@ -1487,8 +1521,8 @@ static Type resolveNestedIdentTypeComponent( // that resolves things. But do we really want that to succeed? if (memberTypes.size() > 1) { if (!options.contains(TypeResolutionFlags::SilenceErrors)) - diagnoseAmbiguousMemberType(parentTy, parentRange, comp->getIdentifier(), - comp->getIdLoc(), memberTypes); + diagnoseAmbiguousMemberType(parentTy, parentRange, comp->getNameRef(), + comp->getNameLoc(), memberTypes); return ErrorType::get(ctx); } @@ -1535,8 +1569,8 @@ static Type resolveIdentTypeComponent( Type parentTy = resolveIdentTypeComponent(resolution, parentComps, options); if (!parentTy || parentTy->hasError()) return parentTy; - SourceRange parentRange(parentComps.front()->getIdLoc(), - parentComps.back()->getSourceRange().End); + SourceRange parentRange(parentComps.front()->getStartLoc(), + parentComps.back()->getEndLoc()); // Resolve the nested type. return resolveNestedIdentTypeComponent(resolution, parentTy, @@ -1554,7 +1588,8 @@ static bool diagnoseAvailability(IdentTypeRepr *IdType, auto componentRange = IdType->getComponentRange(); for (auto comp : componentRange) { if (auto *typeDecl = comp->getBoundDecl()) { - if (diagnoseDeclAvailability(typeDecl, DC, comp->getIdLoc(), flags)) { + if (diagnoseDeclAvailability(typeDecl, DC, + comp->getNameLoc().getSourceRange(), flags)) { return true; } } @@ -1605,11 +1640,15 @@ Type TypeChecker::resolveIdentifierType( if (!result) return nullptr; if (auto moduleTy = result->getAs()) { + // Allow module types only if flag is specified. + if (options.contains(TypeResolutionFlags::AllowModule)) + return moduleTy; + // Otherwise, emit an error. if (!options.contains(TypeResolutionFlags::SilenceErrors)) { auto moduleName = moduleTy->getModule()->getName(); - diags.diagnose(Components.back()->getIdLoc(), - diag::use_undeclared_type, moduleName); - diags.diagnose(Components.back()->getIdLoc(), + diags.diagnose(Components.back()->getNameLoc(), + diag::use_undeclared_type, DeclNameRef(moduleName)); + diags.diagnose(Components.back()->getNameLoc(), diag::note_module_as_type, moduleName); } Components.back()->setInvalid(); @@ -1724,7 +1763,7 @@ namespace { public: explicit TypeResolver(TypeResolution resolution) - : Context(resolution.getDeclContext()->getASTContext()), + : Context(resolution.getASTContext()), resolution(resolution), DC(resolution.getDeclContext()) { @@ -1739,18 +1778,32 @@ namespace { return diags.diagnose(std::forward(Args)...); } + template + InFlightDiagnostic diagnoseInvalid(TypeRepr *repr, + ArgTypes &&... Args) const { + auto &diags = Context.Diags; + repr->setInvalid(); + return diags.diagnose(std::forward(Args)...); + } + Type resolveAttributedType(AttributedTypeRepr *repr, TypeResolutionOptions options); Type resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, TypeResolutionOptions options); Type resolveASTFunctionType(FunctionTypeRepr *repr, TypeResolutionOptions options, - FunctionType::ExtInfo extInfo - = FunctionType::ExtInfo()); + AnyFunctionType::Representation representation + = AnyFunctionType::Representation::Swift, + bool noescape = false, + const clang::Type *parsedClangFunctionType + = nullptr, + DifferentiabilityKind diffKind + = DifferentiabilityKind::NonDifferentiable); bool resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, TypeResolutionOptions options, bool requiresMappingOut, + DifferentiabilityKind diffKind, SmallVectorImpl &ps); Type resolveSILFunctionType(FunctionTypeRepr *repr, @@ -1812,11 +1865,11 @@ namespace { Type TypeResolution::resolveType(TypeRepr *TyR, TypeResolutionOptions options) { auto &ctx = getASTContext(); - FrontendStatsTracer StatsTracer(ctx.Stats, "resolve-type", TyR); PrettyStackTraceTypeRepr stackTrace(ctx, "resolving", TyR); TypeResolver typeResolver(*this); + auto result = typeResolver.resolveType(TyR, options); if (result) { @@ -1984,6 +2037,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Remember whether this is a function parameter. bool isParam = options.is(TypeResolverContext::FunctionInput); + // Remember whether this is a variadic function parameter. + bool isVariadicFunctionParam = + options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl); + // The type we're working with, in case we want to build it differently // based on the attributes we see. Type ty; @@ -2028,18 +2086,21 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Check for @thick. if (attrs.has(TAK_thick)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } + storedRepr = MetatypeRepresentation::Thick; attrs.clearAttribute(TAK_thick); } // Check for @objc_metatype. if (attrs.has(TAK_objc_metatype)) { - if (storedRepr) - diagnose(repr->getStartLoc(), diag::sil_metatype_multiple_reprs); - + if (storedRepr) { + diagnoseInvalid(repr, repr->getStartLoc(), + diag::sil_metatype_multiple_reprs); + } storedRepr = MetatypeRepresentation::ObjC; attrs.clearAttribute(TAK_objc_metatype); } @@ -2062,13 +2123,13 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, static const TypeAttrKind FunctionAttrs[] = { TAK_convention, TAK_pseudogeneric, TAK_callee_owned, TAK_callee_guaranteed, TAK_noescape, TAK_autoclosure, - TAK_escaping, TAK_yield_once, TAK_yield_many + TAK_differentiable, TAK_escaping, TAK_yield_once, TAK_yield_many }; auto checkUnsupportedAttr = [&](TypeAttrKind attr) { if (attrs.has(attr)) { - diagnose(attrs.getLoc(attr), diag::unknown_attribute, - TypeAttributes::getAttrName(attr)); + diagnoseInvalid(repr, attrs.getLoc(attr), diag::unknown_attribute, + TypeAttributes::getAttrName(attr)); attrs.clearAttribute(attr); } }; @@ -2103,7 +2164,29 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Function attributes require a syntactic function type. auto *fnRepr = dyn_cast(repr); + auto tryParseClangType = [this](TypeAttributes::Convention &conv, + bool hasConventionCOrBlock) + -> const clang::Type * { + if (conv.ClangType.Item.empty()) + return nullptr; + if (!hasConventionCOrBlock) { + diagnose(conv.ClangType.Loc, + diag::unexpected_ctype_for_non_c_convention, + conv.Name, conv.ClangType.Item); + return nullptr; + } + + const clang::Type *type = Context.getClangModuleLoader() + ->parseClangFunctionType(conv.ClangType.Item, + conv.ClangType.Loc); + if (!type) + diagnose(conv.ClangType.Loc, diag::unable_to_parse_c_function_type, + conv.ClangType.Item); + return type; + }; + if (fnRepr && hasFunctionAttr) { + const clang::Type *parsedClangFunctionType = nullptr; if (options & TypeResolutionFlags::SILType) { SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; @@ -2118,8 +2201,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto calleeConvention = ParameterConvention::Direct_Unowned; if (attrs.has(TAK_callee_owned)) { if (attrs.has(TAK_callee_guaranteed)) { - diagnose(attrs.getLoc(TAK_callee_owned), - diag::sil_function_repeat_convention, /*callee*/ 2); + diagnoseInvalid(repr, attrs.getLoc(TAK_callee_owned), + diag::sil_function_repeat_convention, /*callee*/ 2); } calleeConvention = ParameterConvention::Direct_Owned; } else if (attrs.has(TAK_callee_guaranteed)) { @@ -2129,7 +2212,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (!attrs.hasConvention()) { rep = SILFunctionType::Representation::Thick; } else { - auto convention = attrs.getConvention(); + auto convention = attrs.getConventionName(); // SIL exposes a greater number of conventions than Swift source. auto parsedRep = llvm::StringSwitch>( @@ -2145,23 +2228,44 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, SILFunctionType::Representation::WitnessMethod) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), - diag::unsupported_sil_convention, attrs.getConvention()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_sil_convention, + attrs.getConventionName()); rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; + bool isCOrBlock = + rep == SILFunctionTypeRepresentation::CFunctionPointer + || rep == SILFunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } if (rep == SILFunctionType::Representation::WitnessMethod) { - auto protocolName = *attrs.conventionWitnessMethodProtocol; + auto protocolName = + attrs.ConventionArguments.getValue().WitnessMethodProtocol; witnessMethodProtocol = new (Context) SimpleIdentTypeRepr( - SourceLoc(), Context.getIdentifier(protocolName)); + DeclNameLoc(), protocolName); } } + if (attrs.has(TAK_differentiable) && + !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); + } + + DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; + if (attrs.has(TAK_differentiable)) { + diffKind = attrs.linear ? DifferentiabilityKind::Linear + : DifferentiabilityKind::Normal; + } + // Resolve the function type directly with these attributes. + // TODO: [store-sil-clang-function-type] SILFunctionType::ExtInfo extInfo(rep, attrs.has(TAK_pseudogeneric), - attrs.has(TAK_noescape)); + attrs.has(TAK_noescape), diffKind, + nullptr); ty = resolveSILFunctionType(fnRepr, options, coroutineKind, extInfo, calleeConvention, witnessMethodProtocol); @@ -2172,55 +2276,77 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.hasConvention()) { auto parsedRep = llvm::StringSwitch>( - attrs.getConvention()) + attrs.getConventionName()) .Case("swift", FunctionType::Representation::Swift) .Case("block", FunctionType::Representation::Block) .Case("thin", FunctionType::Representation::Thin) .Case("c", FunctionType::Representation::CFunctionPointer) .Default(None); if (!parsedRep) { - diagnose(attrs.getLoc(TAK_convention), diag::unsupported_convention, - attrs.getConvention()); + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::unsupported_convention, + attrs.getConventionName()); rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; - - if (attrs.has(TAK_autoclosure)) { - // @convention(c) and @convention(block) are not allowed with an @autoclosure type. - if (rep == FunctionType::Representation::CFunctionPointer || - rep == FunctionType::Representation::Block) { - diagnose(attrs.getLoc(TAK_convention), - diag::invalid_autoclosure_and_convention_attributes, - attrs.getConvention()); - attrs.clearAttribute(TAK_convention); - } - } + + bool isCOrBlock = rep == FunctionTypeRepresentation::CFunctionPointer + || rep == FunctionTypeRepresentation::Block; + parsedClangFunctionType = + tryParseClangType(attrs.ConventionArguments.getValue(), isCOrBlock); } } - // @autoclosure is only valid on parameters. - if (!isParam && attrs.has(TAK_autoclosure)) { - bool isVariadicFunctionParam = - options.is(TypeResolverContext::VariadicFunctionInput) && - !options.hasBase(TypeResolverContext::EnumElementDecl); - - diagnose(attrs.getLoc(TAK_autoclosure), - isVariadicFunctionParam ? diag::attr_not_on_variadic_parameters - : diag::attr_only_on_parameters, - "@autoclosure"); - attrs.clearAttribute(TAK_autoclosure); + if (attrs.has(TAK_differentiable) && + !Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnoseInvalid(repr, attrs.getLoc(TAK_differentiable), + diag::experimental_differentiable_programming_disabled); } - // Resolve the function type directly with these attributes. - FunctionType::ExtInfo extInfo(rep, /*noescape=*/false, - fnRepr->throws()); + DifferentiabilityKind diffKind = DifferentiabilityKind::NonDifferentiable; + if (attrs.has(TAK_differentiable)) { + diffKind = attrs.linear ? DifferentiabilityKind::Linear + : DifferentiabilityKind::Normal; + } - ty = resolveASTFunctionType(fnRepr, options, extInfo); + ty = resolveASTFunctionType(fnRepr, options, rep, /*noescape=*/false, + parsedClangFunctionType, + diffKind); if (!ty || ty->hasError()) return ty; } } + // Validate use of @autoclosure + if (attrs.has(TAK_autoclosure)) { + bool didDiagnose = false; + if (attrs.hasConvention()) { + if (attrs.getConventionName() == "c" || + attrs.getConventionName() == "block") { + diagnoseInvalid(repr, attrs.getLoc(TAK_convention), + diag::invalid_autoclosure_and_convention_attributes, + attrs.getConventionName()); + attrs.clearAttribute(TAK_convention); + didDiagnose = true; + } + } else if (options.is(TypeResolverContext::VariadicFunctionInput) && + !options.hasBase(TypeResolverContext::EnumElementDecl)) { + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_not_on_variadic_parameters, "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; + } else if (!options.is(TypeResolverContext::FunctionInput)) { + diagnoseInvalid(repr, attrs.getLoc(TAK_autoclosure), + diag::attr_only_on_parameters, "@autoclosure"); + attrs.clearAttribute(TAK_autoclosure); + didDiagnose = true; + } + + if (didDiagnose) { + ty = ErrorType::get(Context); + } + } + auto instanceOptions = options; instanceOptions.setContext(None); @@ -2246,12 +2372,13 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, auto loc = attrs.getLoc(TAK_escaping); auto attrRange = getTypeAttrRangeWithAt(Context, loc); - diagnose(loc, diag::escaping_non_function_parameter) - .fixItRemove(attrRange); + diagnoseInvalid(repr, loc, diag::escaping_non_function_parameter) + .fixItRemove(attrRange); // Try to find a helpful note based on how the type is being used if (options.is(TypeResolverContext::ImmediateOptionalTypeArgument)) { - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } @@ -2267,6 +2394,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // @autoclosure is going to be diagnosed when type of // the parameter is validated, because that attribute // applies to the declaration now. + repr->setInvalid(); attrs.clearAttribute(TAK_autoclosure); } @@ -2274,9 +2402,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (!attrs.has(i)) continue; - auto diag = diagnose(attrs.getLoc(i), - diag::attribute_requires_function_type, - TypeAttributes::getAttrName(i)); + auto diag = diagnoseInvalid(repr, attrs.getLoc(i), + diag::attribute_requires_function_type, + TypeAttributes::getAttrName(i)); // If we see @escaping among the attributes on this type, because it isn't // a function type, we'll remove it. @@ -2286,7 +2414,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Specialize the diagnostic for Optionals. if (ty->getOptionalObjectType()) { diag.flush(); - diagnose(repr->getLoc(), diag::escaping_optional_type_argument); + diagnoseInvalid(repr, repr->getLoc(), + diag::escaping_optional_type_argument); } } attrs.clearAttribute(i); @@ -2295,13 +2424,29 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Remove the function attributes from the set so that we don't diagnose. for (auto i : FunctionAttrs) attrs.clearAttribute(i); - attrs.convention = None; + attrs.ConventionArguments = None; + } + + if (attrs.has(TAK_noDerivative)) { + if (!Context.LangOpts.EnableExperimentalDifferentiableProgramming) { + diagnose(attrs.getLoc(TAK_noDerivative), + diag::experimental_differentiable_programming_disabled); + } else if (!isParam) { + // @noDerivative is only valid on parameters. + diagnose(attrs.getLoc(TAK_noDerivative), + (isVariadicFunctionParam + ? diag::attr_not_on_variadic_parameters + : diag::attr_only_on_parameters_of_differentiable), + "@noDerivative"); + } + attrs.clearAttribute(TAK_noDerivative); } // In SIL, handle @opened (n), which creates an existential archetype. if (attrs.has(TAK_opened)) { if (!ty->isExistentialType()) { - diagnose(attrs.getLoc(TAK_opened), diag::opened_non_protocol, ty); + diagnoseInvalid(repr, attrs.getLoc(TAK_opened), diag::opened_non_protocol, + ty); } else { ty = OpenedArchetypeType::get(ty, attrs.OpenedID); } @@ -2341,16 +2486,17 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, } for (unsigned i = 0; i != TypeAttrKind::TAK_Count; ++i) - if (attrs.has((TypeAttrKind)i)) - diagnose(attrs.getLoc((TypeAttrKind)i), - diag::attribute_does_not_apply_to_type); + if (attrs.has((TypeAttrKind)i)) { + diagnoseInvalid(repr, attrs.getLoc((TypeAttrKind)i), + diag::attribute_does_not_apply_to_type); + } return ty; } bool TypeResolver::resolveASTFunctionTypeParams( TupleTypeRepr *inputRepr, TypeResolutionOptions options, - bool requiresMappingOut, + bool requiresMappingOut, DifferentiabilityKind diffKind, SmallVectorImpl &elements) { elements.reserve(inputRepr->getNumElements()); @@ -2414,8 +2560,24 @@ bool TypeResolver::resolveASTFunctionTypeParams( ownership = ValueOwnership::Default; break; } + + bool noDerivative = false; + if (auto *attrTypeRepr = dyn_cast(eltTypeRepr)) { + if (attrTypeRepr->getAttrs().has(TAK_noDerivative)) { + if (diffKind == DifferentiabilityKind::NonDifferentiable && + Context.LangOpts.EnableExperimentalDifferentiableProgramming) + diagnose(eltTypeRepr->getLoc(), + diag::attr_only_on_parameters_of_differentiable, + "@noDerivative") + .highlight(eltTypeRepr->getSourceRange()); + else + noDerivative = true; + } + } + auto paramFlags = ParameterTypeFlags::fromParameterType( - ty, variadic, autoclosure, ownership); + ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership, + noDerivative); elements.emplace_back(ty, Identifier(), paramFlags); } @@ -2459,23 +2621,25 @@ Type TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, return ty; } -Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr, - TypeResolutionOptions parentOptions, - FunctionType::ExtInfo extInfo) { +Type TypeResolver::resolveASTFunctionType( + FunctionTypeRepr *repr, TypeResolutionOptions parentOptions, + AnyFunctionType::Representation representation, bool noescape, + const clang::Type *parsedClangFunctionType, + DifferentiabilityKind diffKind) { + TypeResolutionOptions options = None; options |= parentOptions.withoutContext().getFlags(); SmallVector params; if (resolveASTFunctionTypeParams(repr->getArgsTypeRepr(), options, - repr->getGenericEnvironment() != nullptr, params)) { + repr->getGenericEnvironment() != nullptr, + diffKind, params)) { return Type(); } Type outputTy = resolveType(repr->getResultTypeRepr(), options); if (!outputTy || outputTy->hasError()) return outputTy; - extInfo = extInfo.withThrows(repr->throws()); - // If this is a function type without parens around the parameter list, // diagnose this and produce a fixit to add them. if (!repr->isWarnedAbout()) { @@ -2486,7 +2650,7 @@ Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr, if (args->getNumElements() == 1) { if (const auto Void = dyn_cast(args->getElementType(0))) { - if (Void->getIdentifier().str() == "Void") { + if (Void->getNameRef().isSimpleName(Context.Id_Void)) { diagnose(args->getStartLoc(), diag::paren_void_probably_void) .fixItReplace(args->getSourceRange(), "()"); repr->setWarned(); @@ -2495,6 +2659,20 @@ Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr, } } + FunctionType::ExtInfo incompleteExtInfo(FunctionTypeRepresentation::Swift, + noescape, repr->throws(), diffKind, + /*clangFunctionType*/nullptr); + + const clang::Type *clangFnType = parsedClangFunctionType; + if (representation == AnyFunctionType::Representation::CFunctionPointer + && !clangFnType) + clangFnType = Context.getClangFunctionType( + params, outputTy, incompleteExtInfo, + AnyFunctionType::Representation::CFunctionPointer); + + auto extInfo = incompleteExtInfo.withRepresentation(representation) + .withClangFunctionType(clangFnType); + // SIL uses polymorphic function types to resolve overloaded member functions. if (auto genericEnv = repr->getGenericEnvironment()) { outputTy = outputTy->mapTypeOutOfContext(); @@ -2505,12 +2683,14 @@ Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr, auto fnTy = FunctionType::get(params, outputTy, extInfo); // If the type is a block or C function pointer, it must be representable in // ObjC. - switch (auto rep = extInfo.getRepresentation()) { + switch (representation) { case AnyFunctionType::Representation::Block: case AnyFunctionType::Representation::CFunctionPointer: if (!fnTy->isRepresentableIn(ForeignLanguage::ObjectiveC, DC)) { StringRef strName = - rep == AnyFunctionType::Representation::Block ? "block" : "c"; + (representation == AnyFunctionType::Representation::Block) + ? "block" + : "c"; auto extInfo2 = extInfo.withRepresentation(AnyFunctionType::Representation::Swift); auto simpleFnTy = FunctionType::get(params, outputTy, extInfo2); @@ -2552,8 +2732,8 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, // Substitute out parsed context types into interface types. CanGenericSignature genericSig; if (auto *genericEnv = repr->getGenericEnvironment()) { - genericSig = genericEnv->getGenericSignature()->getCanonicalSignature(); - + genericSig = genericEnv->getGenericSignature().getCanonicalSignature(); + for (auto &field : fields) { auto transTy = field.getLoweredType()->mapTypeOutOfContext(); field = {transTy->getCanonicalType(), field.isMutable()}; @@ -2577,27 +2757,11 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, auto argTy = resolveType(repr->getGenericArguments()[i], options); genericArgMap.insert({params[i], argTy->getCanonicalType()}); } - - bool ok = true; + subMap = SubstitutionMap::get( genericSig, QueryTypeSubstitutionMap{genericArgMap}, - [&](CanType depTy, Type replacement, ProtocolDecl *proto) - -> ProtocolConformanceRef { - auto result = TypeChecker::conformsToProtocol( - replacement, proto, DC, - ConformanceCheckOptions()); - // TODO: getSubstitutions callback ought to return Optional. - if (!result) { - ok = false; - return ProtocolConformanceRef(proto); - } - - return *result; - }); - - if (!ok) - return ErrorType::get(Context); + TypeChecker::LookUpConformance(DC)); } auto layout = SILLayout::get(Context, genericSig, fields); @@ -2648,9 +2812,9 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, elementOptions.setContext(TypeResolverContext::FunctionInput); auto param = resolveSILParameter(elt.Type, elementOptions); params.push_back(param); - if (!param.getType()) return nullptr; + if (!param.getInterfaceType()) return nullptr; - if (param.getType()->hasError()) + if (param.getInterfaceType()->hasError()) hasError = true; } @@ -2669,6 +2833,23 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, } } } // restore generic type resolution + + // Resolve substitutions if we have them. + SubstitutionMap subs; + if (!repr->getSubstitutions().empty()) { + auto sig = repr->getGenericEnvironment() + ->getGenericSignature() + .getCanonicalSignature(); + TypeSubstitutionMap subsMap; + auto params = sig->getGenericParams(); + for (unsigned i : indices(repr->getSubstitutions())) { + auto resolved = resolveType(repr->getSubstitutions()[i], options); + subsMap.insert({params[i], resolved->getCanonicalType()}); + } + subs = SubstitutionMap::get(sig, QueryTypeSubstitutionMap{subsMap}, + TypeChecker::LookUpConformance(DC)) + .getCanonical(); + } if (hasError) { return ErrorType::get(Context); @@ -2681,29 +2862,30 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, SmallVector interfaceResults; Optional interfaceErrorResult; if (auto *genericEnv = repr->getGenericEnvironment()) { - genericSig = genericEnv->getGenericSignature()->getCanonicalSignature(); - + genericSig = genericEnv->getGenericSignature().getCanonicalSignature(); + for (auto ¶m : params) { - auto transParamType = param.getType()->mapTypeOutOfContext() + auto transParamType = param.getInterfaceType()->mapTypeOutOfContext() ->getCanonicalType(); - interfaceParams.push_back(param.getWithType(transParamType)); + interfaceParams.push_back(param.getWithInterfaceType(transParamType)); } for (auto &yield : yields) { - auto transYieldType = yield.getType()->mapTypeOutOfContext() + auto transYieldType = yield.getInterfaceType()->mapTypeOutOfContext() ->getCanonicalType(); - interfaceYields.push_back(yield.getWithType(transYieldType)); + interfaceYields.push_back(yield.getWithInterfaceType(transYieldType)); } for (auto &result : results) { - auto transResultType = result.getType()->mapTypeOutOfContext() + auto transResultType = result.getInterfaceType()->mapTypeOutOfContext() ->getCanonicalType(); - interfaceResults.push_back(result.getWithType(transResultType)); + interfaceResults.push_back(result.getWithInterfaceType(transResultType)); } if (errorResult) { - auto transErrorResultType = errorResult->getType()->mapTypeOutOfContext() + auto transErrorResultType = errorResult->getInterfaceType() + ->mapTypeOutOfContext() ->getCanonicalType(); interfaceErrorResult = - errorResult->getWithType(transErrorResultType); + errorResult->getWithInterfaceType(transErrorResultType); } } else { interfaceParams = params; @@ -2711,7 +2893,7 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, interfaceResults = results; interfaceErrorResult = errorResult; } - Optional witnessMethodConformance; + ProtocolConformanceRef witnessMethodConformance; if (witnessMethodProtocol) { auto resolved = resolveType(witnessMethodProtocol, options); if (resolved->hasError()) @@ -2721,7 +2903,7 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, if (!protocolType) return ErrorType::get(Context); - Type selfType = params.back().getType(); + Type selfType = params.back().getInterfaceType(); // The Self type can be nested in a few layers of metatypes (etc.). while (auto metatypeType = selfType->getAs()) { auto next = metatypeType->getInstanceType(); @@ -2740,6 +2922,8 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, callee, interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, + subs, + repr->areGenericParamsImplied(), Context, witnessMethodConformance); } @@ -2749,7 +2933,7 @@ SILYieldInfo TypeResolver::resolveSILYield(TypeAttributes &attrs, AttributedTypeRepr attrRepr(attrs, repr); options.setContext(TypeResolverContext::FunctionInput); SILParameterInfo paramInfo = resolveSILParameter(&attrRepr, options); - return SILYieldInfo(paramInfo.getType(), paramInfo.getConvention()); + return SILYieldInfo(paramInfo.getInterfaceType(), paramInfo.getConvention()); } SILParameterInfo TypeResolver::resolveSILParameter( @@ -2760,6 +2944,8 @@ SILParameterInfo TypeResolver::resolveSILParameter( auto convention = DefaultParameterConvention; Type type; bool hadError = false; + auto differentiability = + SILParameterDifferentiability::DifferentiableOrNotApplicable; if (auto attrRepr = dyn_cast(repr)) { auto attrs = attrRepr->getAttrs(); @@ -2785,6 +2971,10 @@ SILParameterInfo TypeResolver::resolveSILParameter( checkFor(TypeAttrKind::TAK_owned, ParameterConvention::Direct_Owned); checkFor(TypeAttrKind::TAK_guaranteed, ParameterConvention::Direct_Guaranteed); + if (attrs.has(TAK_noDerivative)) { + attrs.clearAttribute(TAK_noDerivative); + differentiability = SILParameterDifferentiability::NotDifferentiable; + } type = resolveAttributedType(attrs, attrRepr->getTypeRepr(), options); } else { @@ -2801,7 +2991,8 @@ SILParameterInfo TypeResolver::resolveSILParameter( } if (hadError) type = ErrorType::get(Context); - return SILParameterInfo(type->getCanonicalType(), convention); + return SILParameterInfo(type->getCanonicalType(), convention, + differentiability); } bool TypeResolver::resolveSingleSILResult(TypeRepr *repr, @@ -2823,7 +3014,7 @@ bool TypeResolver::resolveSingleSILResult(TypeRepr *repr, // The treatment from this point on is basically completely different. auto yield = resolveSILYield(attrs, attrRepr->getTypeRepr(), options); - if (yield.getType()->hasError()) + if (yield.getInterfaceType()->hasError()) return true; yields.push_back(yield); @@ -2948,8 +3139,7 @@ Type TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr, default: llvm_unreachable("unknown SpecifierTypeRepr kind"); } - diagnose(repr->getSpecifierLoc(), diagID, name); - repr->setInvalid(); + diagnoseInvalid(repr, repr->getSpecifierLoc(), diagID, name); return ErrorType::get(Context); } @@ -3383,13 +3573,13 @@ namespace { class UnsupportedProtocolVisitor : public TypeReprVisitor, public ASTWalker { - TypeChecker &TC; + ASTContext &Ctx; bool checkStatements; bool hitTopStmt; public: - UnsupportedProtocolVisitor(TypeChecker &tc, bool checkStatements) - : TC(tc), checkStatements(checkStatements), hitTopStmt(false) { } + UnsupportedProtocolVisitor(ASTContext &ctx, bool checkStatements) + : Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false) { } bool walkToTypeReprPre(TypeRepr *T) override { if (T->isInvalid()) @@ -3428,8 +3618,9 @@ class UnsupportedProtocolVisitor auto comp = T->getComponentRange().back(); if (auto *proto = dyn_cast_or_null(comp->getBoundDecl())) { if (!proto->existentialTypeSupported()) { - TC.diagnose(comp->getIdLoc(), diag::unsupported_existential_type, - proto->getName()); + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::unsupported_existential_type, + proto->getName()); T->setInvalid(); } } else if (auto *alias = dyn_cast_or_null(comp->getBoundDecl())) { @@ -3445,8 +3636,9 @@ class UnsupportedProtocolVisitor if (protoDecl->existentialTypeSupported()) continue; - TC.diagnose(comp->getIdLoc(), diag::unsupported_existential_type, - protoDecl->getName()); + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::unsupported_existential_type, + protoDecl->getName()); T->setInvalid(); } } @@ -3473,49 +3665,52 @@ void TypeChecker::checkUnsupportedProtocolType(Decl *decl) { if (!decl || decl->isInvalid()) return; + auto &ctx = decl->getASTContext(); if (auto *protocolDecl = dyn_cast(decl)) - checkUnsupportedProtocolType(protocolDecl->getTrailingWhereClause()); + checkUnsupportedProtocolType(ctx, protocolDecl->getTrailingWhereClause()); else if (auto *genericDecl = dyn_cast(decl)) - checkUnsupportedProtocolType(genericDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, genericDecl->getGenericParams()); else if (auto *assocType = dyn_cast(decl)) - checkUnsupportedProtocolType(assocType->getTrailingWhereClause()); + checkUnsupportedProtocolType(ctx, assocType->getTrailingWhereClause()); else if (auto *extDecl = dyn_cast(decl)) - checkUnsupportedProtocolType(extDecl->getTrailingWhereClause()); + checkUnsupportedProtocolType(ctx, extDecl->getTrailingWhereClause()); else if (auto *subscriptDecl = dyn_cast(decl)) - checkUnsupportedProtocolType(subscriptDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, subscriptDecl->getGenericParams()); else if (auto *funcDecl = dyn_cast(decl)) { if (!isa(funcDecl)) - checkUnsupportedProtocolType(funcDecl->getGenericParams()); + checkUnsupportedProtocolType(ctx, funcDecl->getGenericParams()); } if (isa(decl) || isa(decl)) return; - UnsupportedProtocolVisitor visitor(*this, /*checkStatements=*/false); + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); decl->walk(visitor); } -void TypeChecker::checkUnsupportedProtocolType(Stmt *stmt) { +void TypeChecker::checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt) { if (!stmt) return; - UnsupportedProtocolVisitor visitor(*this, /*checkStatements=*/true); + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/true); stmt->walk(visitor); } -void TypeChecker::checkUnsupportedProtocolType(TrailingWhereClause *whereClause) { +void TypeChecker::checkUnsupportedProtocolType( + ASTContext &ctx, TrailingWhereClause *whereClause) { if (whereClause == nullptr) return; - UnsupportedProtocolVisitor visitor(*this, /*checkStatements=*/false); + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); visitor.visitRequirements(whereClause->getRequirements()); } -void TypeChecker::checkUnsupportedProtocolType(GenericParamList *genericParams) { +void TypeChecker::checkUnsupportedProtocolType( + ASTContext &ctx, GenericParamList *genericParams) { if (genericParams == nullptr) return; - UnsupportedProtocolVisitor visitor(*this, /*checkStatements=*/false); + UnsupportedProtocolVisitor visitor(ctx, /*checkStatements=*/false); visitor.visitRequirements(genericParams->getRequirements()); } diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index e9c5c3162ba4e..bcd564c9b20ba 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -66,6 +66,9 @@ enum class TypeResolutionFlags : uint16_t { /// Whether we should not produce diagnostics if the type is invalid. SilenceErrors = 1 << 10, + + /// Whether to allow module declaration types. + AllowModule = 1 << 11 }; /// Type resolution contexts that require special handling. diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index ee7f7826a222b..b5a702309c7f3 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "swift/Subsystems.h" +#include "ConstraintSystem.h" #include "TypeChecker.h" #include "TypeCheckObjC.h" #include "TypeCheckType.h" @@ -34,6 +35,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/Timer.h" @@ -52,88 +54,94 @@ using namespace swift; TypeChecker &TypeChecker::createForContext(ASTContext &ctx) { - (void)ctx.createLazyResolverIfMissing(); - return *static_cast(ctx.getLazyResolver()); + assert(!ctx.getLegacyGlobalTypeChecker() && + "Cannot install more than one instance of the global type checker!"); + auto *TC = new TypeChecker(); + ctx.installGlobalTypeChecker(TC); + ctx.addCleanup([=](){ delete TC; }); + return *ctx.getLegacyGlobalTypeChecker(); } -TypeChecker::TypeChecker(ASTContext &Ctx) - : Context(Ctx), Diags(Ctx.Diags) {} - -TypeChecker::~TypeChecker() {} - -ProtocolDecl *TypeChecker::getProtocol(SourceLoc loc, KnownProtocolKind kind) { +ProtocolDecl *TypeChecker::getProtocol(ASTContext &Context, SourceLoc loc, + KnownProtocolKind kind) { auto protocol = Context.getProtocol(kind); if (!protocol && loc.isValid()) { - diagnose(loc, diag::missing_protocol, - Context.getIdentifier(getProtocolName(kind))); + Context.Diags.diagnose(loc, diag::missing_protocol, + Context.getIdentifier(getProtocolName(kind))); } - if (protocol && !protocol->getInterfaceType()) { - if (protocol->isInvalid()) - return nullptr; + if (protocol && protocol->isInvalid()) { + return nullptr; } return protocol; } -ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) { +ProtocolDecl *TypeChecker::getLiteralProtocol(ASTContext &Context, Expr *expr) { if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByArrayLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), KnownProtocolKind::ExpressibleByArrayLiteral); if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByDictionaryLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByDictionaryLiteral); if (!isa(expr)) return nullptr; if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByNilLiteral); - + return TypeChecker::getProtocol(Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByNilLiteral); + if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByIntegerLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByIntegerLiteral); if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByFloatLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), KnownProtocolKind::ExpressibleByFloatLiteral); if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByBooleanLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByBooleanLiteral); if (const auto *SLE = dyn_cast(expr)) { if (SLE->isSingleUnicodeScalar()) - return getProtocol( - expr->getLoc(), + return TypeChecker::getProtocol( + Context, expr->getLoc(), KnownProtocolKind::ExpressibleByUnicodeScalarLiteral); if (SLE->isSingleExtendedGraphemeCluster()) return getProtocol( - expr->getLoc(), + Context, expr->getLoc(), KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral); - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByStringLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral); } if (isa(expr)) - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByStringInterpolation); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByStringInterpolation); if (auto E = dyn_cast(expr)) { switch (E->getKind()) { case MagicIdentifierLiteralExpr::File: + case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::Function: - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByStringLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByStringLiteral); case MagicIdentifierLiteralExpr::Line: case MagicIdentifierLiteralExpr::Column: - return getProtocol(expr->getLoc(), - KnownProtocolKind::ExpressibleByIntegerLiteral); + return TypeChecker::getProtocol( + Context, expr->getLoc(), + KnownProtocolKind::ExpressibleByIntegerLiteral); case MagicIdentifierLiteralExpr::DSOHandle: return nullptr; @@ -142,9 +150,10 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) { if (auto E = dyn_cast(expr)) { switch (E->getLiteralKind()) { -#define POUND_OBJECT_LITERAL(Name, Desc, Protocol)\ - case ObjectLiteralExpr::Name:\ - return getProtocol(expr->getLoc(), KnownProtocolKind::Protocol); +#define POUND_OBJECT_LITERAL(Name, Desc, Protocol) \ + case ObjectLiteralExpr::Name: \ + return TypeChecker::getProtocol(Context, expr->getLoc(), \ + KnownProtocolKind::Protocol); #include "swift/Syntax/TokenKinds.def" } } @@ -152,7 +161,8 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) { return nullptr; } -DeclName TypeChecker::getObjectLiteralConstructorName(ObjectLiteralExpr *expr) { +DeclName TypeChecker::getObjectLiteralConstructorName(ASTContext &Context, + ObjectLiteralExpr *expr) { switch (expr->getLiteralKind()) { case ObjectLiteralExpr::colorLiteral: { return DeclName(Context, DeclBaseName::createConstructor(), @@ -191,6 +201,7 @@ Type TypeChecker::getObjectLiteralParameterType(ObjectLiteralExpr *expr, newParams.append(params.begin(), params.end()); auto replace = [&](StringRef replacement) -> Type { + auto &Context = ctor->getASTContext(); newParams[0] = AnyFunctionType::Param(newParams[0].getPlainType(), Context.getIdentifier(replacement), newParams[0].getParameterFlags()); @@ -209,7 +220,7 @@ Type TypeChecker::getObjectLiteralParameterType(ObjectLiteralExpr *expr, } ModuleDecl *TypeChecker::getStdlibModule(const DeclContext *dc) { - if (auto *stdlib = Context.getStdlibModule()) { + if (auto *stdlib = dc->getASTContext().getStdlibModule()) { return stdlib; } @@ -252,7 +263,7 @@ static void bindExtensions(SourceFile &SF) { if (!SF) continue; - for (auto D : SF->Decls) { + for (auto D : SF->getTopLevelDecls()) { if (auto ED = dyn_cast(D)) if (!tryBindExtension(ED)) worklist.push_back(ED); @@ -282,22 +293,6 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) unsigned currentFunctionIdx = 0; unsigned currentSynthesizedDecl = SF.LastCheckedSynthesizedDecl; do { - // Type check conformance contexts. - for (unsigned i = 0; i != TC.ConformanceContexts.size(); ++i) { - auto decl = TC.ConformanceContexts[i]; - if (auto *ext = dyn_cast(decl)) - TC.checkConformancesInContext(ext, ext); - else { - auto *ntd = cast(decl); - TC.checkConformancesInContext(ntd, ntd); - - // Finally, we can check classes for missing initializers. - if (auto *classDecl = dyn_cast(ntd)) - TC.maybeDiagnoseClassWithoutInitializers(classDecl); - } - } - TC.ConformanceContexts.clear(); - // Type check the body of each of the function in turn. Note that outside // functions must be visited before nested functions for type-checking to // work correctly. @@ -306,7 +301,7 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) auto *AFD = TC.definedFunctions[currentFunctionIdx]; assert(!AFD->getDeclContext()->isLocalContext()); - TC.typeCheckAbstractFunctionBody(AFD); + TypeChecker::typeCheckAbstractFunctionBody(AFD); } // Type check synthesized functions and their bodies. @@ -314,21 +309,12 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) currentSynthesizedDecl != n; ++currentSynthesizedDecl) { auto decl = SF.SynthesizedDecls[currentSynthesizedDecl]; - TC.typeCheckDecl(decl); + TypeChecker::typeCheckDecl(decl); } } while (currentFunctionIdx < TC.definedFunctions.size() || - currentSynthesizedDecl < SF.SynthesizedDecls.size() || - !TC.ConformanceContexts.empty()); + currentSynthesizedDecl < SF.SynthesizedDecls.size()); - // FIXME: Horrible hack. Store this somewhere more appropriate. - SF.LastCheckedSynthesizedDecl = currentSynthesizedDecl; - - // Compute captures for functions and closures we visited. - for (auto *closure : TC.ClosuresWithUncomputedCaptures) { - TypeChecker::computeCaptures(closure); - } - TC.ClosuresWithUncomputedCaptures.clear(); for (AbstractFunctionDecl *FD : llvm::reverse(TC.definedFunctions)) { TypeChecker::computeCaptures(FD); @@ -337,141 +323,85 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) TC.definedFunctions.clear(); } -void swift::typeCheckExternalDefinitions(SourceFile &SF) { - assert(SF.ASTStage == SourceFile::TypeChecked); - auto &Ctx = SF.getASTContext(); - typeCheckFunctionsAndExternalDecls(SF, createTypeChecker(Ctx)); +void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) { + return (void)evaluateOrDefault(SF.getASTContext().evaluator, + TypeCheckSourceFileRequest{&SF, StartElem}, + false); } -void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, - OptionSet Options, - unsigned StartElem, - unsigned WarnLongFunctionBodies, - unsigned WarnLongExpressionTypeChecking, - unsigned ExpressionTimeoutThreshold, - unsigned SwitchCheckingInvocationThreshold) { - if (SF.ASTStage == SourceFile::TypeChecked) - return; +llvm::Expected +TypeCheckSourceFileRequest::evaluate(Evaluator &eval, + SourceFile *SF, unsigned StartElem) const { + assert(SF && "Source file cannot be null!"); + assert(SF->ASTStage != SourceFile::TypeChecked && + "Should not be re-typechecking this file!"); // Eagerly build the top-level scopes tree before type checking // because type-checking expressions mutates the AST and that throws off the // scope-based lookups. Only the top-level scopes because extensions have not // been bound yet. - if (SF.getASTContext().LangOpts.EnableASTScopeLookup && - SF.isSuitableForASTScopes()) - SF.getScope() + auto &Ctx = SF->getASTContext(); + if (Ctx.LangOpts.EnableASTScopeLookup && SF->isSuitableForASTScopes()) + SF->getScope() .buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals(); - auto &Ctx = SF.getASTContext(); - BufferIndirectlyCausingDiagnosticRAII cpr(SF); + BufferIndirectlyCausingDiagnosticRAII cpr(*SF); // Make sure we have a type checker. TypeChecker &TC = createTypeChecker(Ctx); // Make sure that name binding has been completed before doing any type // checking. - performNameBinding(SF, StartElem); + performNameBinding(*SF, StartElem); // Could build scope maps here because the AST is stable now. { - SharedTimer timer("Type checking and Semantic analysis"); - - TC.setWarnLongFunctionBodies(WarnLongFunctionBodies); - TC.setWarnLongExpressionTypeChecking(WarnLongExpressionTypeChecking); - if (ExpressionTimeoutThreshold != 0) - TC.setExpressionTimeoutThreshold(ExpressionTimeoutThreshold); - - if (SwitchCheckingInvocationThreshold != 0) - TC.setSwitchCheckingInvocationThreshold( - SwitchCheckingInvocationThreshold); - - if (Options.contains(TypeCheckingFlags::DebugTimeFunctionBodies)) - TC.enableDebugTimeFunctionBodies(); + FrontendStatsTracer tracer(Ctx.Stats, "Type checking and Semantic analysis"); - if (Options.contains(TypeCheckingFlags::DebugTimeExpressions)) - TC.enableDebugTimeExpressions(); - - if (Options.contains(TypeCheckingFlags::ForImmediateMode)) - TC.setInImmediateMode(true); - - if (Options.contains(TypeCheckingFlags::SkipNonInlinableFunctionBodies)) + if (Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies) // Disable this optimization if we're compiling SwiftOnoneSupport, because // we _definitely_ need to look inside every declaration to figure out // what gets prespecialized. - if (!SF.getParentModule()->isOnoneSupportModule()) - TC.setSkipNonInlinableBodies(true); - - // Lookup the swift module. This ensures that we record all known - // protocols in the AST. - (void) TC.getStdlibModule(&SF); + if (SF->getParentModule()->isOnoneSupportModule()) + Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies = false; if (!Ctx.LangOpts.DisableAvailabilityChecking) { // Build the type refinement hierarchy for the primary // file before type checking. - TC.buildTypeRefinementContextHierarchy(SF, StartElem); + TypeChecker::buildTypeRefinementContextHierarchy(*SF, StartElem); } // Resolve extensions. This has to occur first during type checking, // because the extensions need to be wired into the AST for name lookup // to work. - bindExtensions(SF); - - // Look for bridging functions. This only matters when - // -enable-source-import is provided. - checkBridgedFunctions(TC.Context); + ::bindExtensions(*SF); // Type check the top-level elements of the source file. - for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { + for (auto D : SF->getTopLevelDecls().slice(StartElem)) { if (auto *TLCD = dyn_cast(D)) { // Immediately perform global name-binding etc. - TC.typeCheckTopLevelCodeDecl(TLCD); - TC.contextualizeTopLevelCode(TLC, TLCD); + TypeChecker::typeCheckTopLevelCodeDecl(TLCD); + TypeChecker::contextualizeTopLevelCode(TLCD); } else { - TC.typeCheckDecl(D); + TypeChecker::typeCheckDecl(D); } } // If we're in REPL mode, inject temporary result variables and other stuff // that the REPL needs to synthesize. - if (SF.Kind == SourceFileKind::REPL && !Ctx.hadError()) - TC.processREPLTopLevel(SF, TLC, StartElem); + if (SF->Kind == SourceFileKind::REPL && !Ctx.hadError()) + TypeChecker::processREPLTopLevel(*SF, StartElem); - typeCheckFunctionsAndExternalDecls(SF, TC); + typeCheckFunctionsAndExternalDecls(*SF, TC); } // Checking that benefits from having the whole module available. - if (!(Options & TypeCheckingFlags::DelayWholeModuleChecking)) { - performWholeModuleTypeChecking(SF); + if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking) { + performWholeModuleTypeChecking(*SF); } - // Verify that we've checked types correctly. - SF.ASTStage = SourceFile::TypeChecked; - - { - SharedTimer timer("AST verification"); - // Verify the SourceFile. - verify(SF); - - // Verify imported modules. - // - // Skip per-file verification in whole-module mode. Verifying imports - // between files could cause the importer to cache declarations without - // adding them to the ASTContext. This happens when the importer registers a - // declaration without a valid TypeChecker instance, as is the case during - // verification. A subsequent file may require that declaration to be fully - // imported (e.g. to synthesized a function body), but since it has already - // been cached, it will never be added to the ASTContext. The solution is to - // skip verification and avoid caching it. -#ifndef NDEBUG - if (!(Options & TypeCheckingFlags::DelayWholeModuleChecking) && - SF.Kind != SourceFileKind::REPL && - SF.Kind != SourceFileKind::SIL && - !Ctx.LangOpts.DebuggerSupport) { - Ctx.verifyAllLoadedModules(); - } -#endif - } + return true; } void swift::performWholeModuleTypeChecking(SourceFile &SF) { @@ -535,7 +465,7 @@ void swift::checkInconsistentImplementationOnlyImports(ModuleDecl *MainModule) { if (!SF) continue; - for (auto *topLevelDecl : SF->Decls) { + for (auto *topLevelDecl : SF->getTopLevelDecls()) { auto *nextImport = dyn_cast(topLevelDecl); if (!nextImport) continue; @@ -607,15 +537,34 @@ bool swift::performTypeLocChecking(ASTContext &Ctx, TypeLoc &T, Optional suppression; if (!ProduceDiagnostics) suppression.emplace(Ctx.Diags); - TypeChecker &TC = createTypeChecker(Ctx); - return TypeChecker::validateType(TC.Context, T, resolution, options); + assert(Ctx.areSemanticQueriesEnabled()); + return TypeChecker::validateType(Ctx, T, resolution, options); } /// Expose TypeChecker's handling of GenericParamList to SIL parsing. GenericEnvironment * -swift::handleSILGenericParams(ASTContext &Ctx, GenericParamList *genericParams, +swift::handleSILGenericParams(GenericParamList *genericParams, DeclContext *DC) { - return createTypeChecker(Ctx).handleSILGenericParams(genericParams, DC); + if (genericParams == nullptr) + return nullptr; + + SmallVector nestedList; + for (; genericParams; genericParams = genericParams->getOuterParameters()) { + nestedList.push_back(genericParams); + } + + std::reverse(nestedList.begin(), nestedList.end()); + + for (unsigned i = 0, e = nestedList.size(); i < e; ++i) { + auto genericParams = nestedList[i]; + genericParams->setDepth(i); + } + + auto sig = + TypeChecker::checkGenericSignature(nestedList.back(), DC, + /*parentSig=*/nullptr, + /*allowConcreteGenericParams=*/true); + return (sig ? sig->getGenericEnvironment() : nullptr); } void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, @@ -625,17 +574,18 @@ void swift::typeCheckPatternBinding(PatternBindingDecl *PBD, auto &Ctx = PBD->getASTContext(); DiagnosticSuppression suppression(Ctx.Diags); - TypeChecker &TC = createTypeChecker(Ctx); - TC.typeCheckPatternBinding(PBD, bindingIndex); + (void)createTypeChecker(Ctx); + (void)evaluateOrDefault( + Ctx.evaluator, PatternBindingEntryRequest{PBD, bindingIndex}, nullptr); + TypeChecker::typeCheckPatternBinding(PBD, bindingIndex); } static Optional getTypeOfCompletionContextExpr( - TypeChecker &TC, DeclContext *DC, CompletionTypeCheckKind kind, Expr *&parsedExpr, ConcreteDeclRef &referencedDecl) { - if (TC.preCheckExpression(parsedExpr, DC)) + if (constraints::ConstraintSystem::preCheckExpression(parsedExpr, DC)) return None; switch (kind) { @@ -646,13 +596,14 @@ static Optional getTypeOfCompletionContextExpr( case CompletionTypeCheckKind::KeyPath: referencedDecl = nullptr; if (auto keyPath = dyn_cast(parsedExpr)) - return TC.checkObjCKeyPathExpr(DC, keyPath, /*requireResultType=*/true); + return TypeChecker::checkObjCKeyPathExpr(DC, keyPath, + /*requireResultType=*/true); return None; } Type originalType = parsedExpr->getType(); - if (auto T = TC.getTypeOfExpressionWithoutApplying(parsedExpr, DC, + if (auto T = TypeChecker::getTypeOfExpressionWithoutApplying(parsedExpr, DC, referencedDecl, FreeTypeVariableBinding::UnresolvedType)) return T; @@ -678,10 +629,10 @@ Optional swift::getTypeOfCompletionContextExpr( Expr *&parsedExpr, ConcreteDeclRef &referencedDecl) { DiagnosticSuppression suppression(Ctx.Diags); - TypeChecker &TC = createTypeChecker(Ctx); + (void)createTypeChecker(Ctx); // Try to solve for the actual type of the expression. - return ::getTypeOfCompletionContextExpr(TC, DC, kind, parsedExpr, + return ::getTypeOfCompletionContextExpr(DC, kind, parsedExpr, referencedDecl); } @@ -693,18 +644,17 @@ swift::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, ConcreteDeclRef &referencedDecl) { auto &ctx = DC->getASTContext(); DiagnosticSuppression suppression(ctx.Diags); - TypeChecker &TC = createTypeChecker(ctx); - return TC.getTypeOfCompletionOperator(DC, LHS, opName, refKind, - referencedDecl); + (void)createTypeChecker(ctx); + return TypeChecker::getTypeOfCompletionOperator(DC, LHS, opName, refKind, + referencedDecl); } bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) { auto &ctx = DC->getASTContext(); DiagnosticSuppression suppression(ctx.Diags); - TypeChecker &TC = createTypeChecker(ctx); - - auto resultTy = TC.typeCheckExpression(parsedExpr, DC, TypeLoc(), - ContextualTypePurpose::CTP_Unused); + (void)createTypeChecker(ctx); + auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC, TypeLoc(), + CTP_Unused); return !resultTy; } @@ -713,54 +663,36 @@ bool swift::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD, auto &Ctx = AFD->getASTContext(); DiagnosticSuppression suppression(Ctx.Diags); - TypeChecker &TC = createTypeChecker(Ctx); - return !TC.typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc); + (void)createTypeChecker(Ctx); + return !TypeChecker::typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc); } bool swift::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { auto &Ctx = static_cast(TLCD)->getASTContext(); DiagnosticSuppression suppression(Ctx.Diags); - TypeChecker &TC = createTypeChecker(Ctx); - TC.typeCheckTopLevelCodeDecl(TLCD); + (void)createTypeChecker(Ctx); + TypeChecker::typeCheckTopLevelCodeDecl(TLCD); return true; } TypeChecker &swift::createTypeChecker(ASTContext &Ctx) { + if (auto *TC = Ctx.getLegacyGlobalTypeChecker()) + return *TC; return TypeChecker::createForContext(Ctx); } -// checkForForbiddenPrefix is for testing purposes. - -void TypeChecker::checkForForbiddenPrefix(const Decl *D) { - if (!hasEnabledForbiddenTypecheckPrefix()) +void TypeChecker::checkForForbiddenPrefix(ASTContext &C, DeclBaseName Name) { + if (C.TypeCheckerOpts.DebugForbidTypecheckPrefix.empty()) return; - if (auto VD = dyn_cast(D)) { - if (!VD->getBaseName().isSpecial()) - checkForForbiddenPrefix(VD->getBaseName().getIdentifier().str()); - } -} -void TypeChecker::checkForForbiddenPrefix(const UnresolvedDeclRefExpr *E) { - if (!hasEnabledForbiddenTypecheckPrefix()) + // Don't touch special names or empty names. + if (Name.isSpecial() || Name.empty()) return; - if (!E->getName().isSpecial()) - checkForForbiddenPrefix(E->getName().getBaseIdentifier()); -} - -void TypeChecker::checkForForbiddenPrefix(Identifier Ident) { - if (!hasEnabledForbiddenTypecheckPrefix()) - return; - checkForForbiddenPrefix(Ident.empty() ? StringRef() : Ident.str()); -} -void TypeChecker::checkForForbiddenPrefix(StringRef Name) { - if (!hasEnabledForbiddenTypecheckPrefix()) - return; - if (Name.empty()) - return; - if (Name.startswith(Context.LangOpts.DebugForbidTypecheckPrefix)) { + StringRef Str = Name.getIdentifier().str(); + if (Str.startswith(C.TypeCheckerOpts.DebugForbidTypecheckPrefix)) { std::string Msg = "forbidden typecheck occurred: "; - Msg += Name; + Msg += Str; llvm::report_fatal_error(Msg); } } @@ -778,3 +710,12 @@ TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) { } return DeclTypeCheckingSemantics::Normal; } + +void swift::bindExtensions(SourceFile &SF) { + ::bindExtensions(SF); +} + +LookupResult +swift::lookupSemanticMember(DeclContext *DC, Type ty, DeclName name) { + return TypeChecker::lookupMember(DC, ty, DeclNameRef(name), None); +} diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 997cb49b87ea7..47e4aef224621 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -38,7 +38,6 @@ namespace swift { class GenericSignatureBuilder; class NominalTypeDecl; class NormalProtocolConformance; -class TopLevelContext; class TypeChecker; class TypeResolution; class TypeResolutionOptions; @@ -48,9 +47,9 @@ enum class TypeResolutionStage : uint8_t; namespace constraints { enum class ConstraintKind : char; - enum class SolutionKind : char; class ConstraintSystem; class Solution; + class SolutionResult; } /// A mapping from substitutable types to the protocol-conformance @@ -76,81 +75,6 @@ enum class DeclTypeCheckingSemantics { OpenExistential, }; -/// The result of name lookup. -class LookupResult { -private: - /// The set of results found. - SmallVector Results; - size_t IndexOfFirstOuterResult = 0; - -public: - LookupResult() {} - - explicit LookupResult(const SmallVectorImpl &Results, - size_t indexOfFirstOuterResult) - : Results(Results.begin(), Results.end()), - IndexOfFirstOuterResult(indexOfFirstOuterResult) {} - - using iterator = SmallVectorImpl::iterator; - iterator begin() { return Results.begin(); } - iterator end() { - return Results.begin() + IndexOfFirstOuterResult; - } - unsigned size() const { return innerResults().size(); } - bool empty() const { return innerResults().empty(); } - - ArrayRef innerResults() const { - return llvm::makeArrayRef(Results).take_front(IndexOfFirstOuterResult); - } - - ArrayRef outerResults() const { - return llvm::makeArrayRef(Results).drop_front(IndexOfFirstOuterResult); - } - - const LookupResultEntry& operator[](unsigned index) const { - return Results[index]; - } - - LookupResultEntry front() const { return innerResults().front(); } - LookupResultEntry back() const { return innerResults().back(); } - - /// Add a result to the set of results. - void add(LookupResultEntry result, bool isOuter) { - Results.push_back(result); - if (!isOuter) { - IndexOfFirstOuterResult++; - assert(IndexOfFirstOuterResult == Results.size() && - "found an outer result before an inner one"); - } else { - assert(IndexOfFirstOuterResult > 0 && - "found outer results without an inner one"); - } - } - - void clear() { Results.clear(); } - - /// Determine whether the result set is nonempty. - explicit operator bool() const { - return !empty(); - } - - TypeDecl *getSingleTypeResult() const { - if (size() != 1) - return nullptr; - - return dyn_cast(front().getValueDecl()); - } - - /// Filter out any results that aren't accepted by the given predicate. - void - filter(llvm::function_ref pred); - - /// Shift down results by dropping inner results while keeping outer - /// results (if any), the innermost of which are recogized as inner - /// results afterwards. - void shiftDownResults(); -}; - /// An individual result of a name lookup for a type. struct LookupTypeResultEntry { TypeDecl *Member; @@ -209,6 +133,10 @@ enum ContextualTypePurpose { CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. + /// Default value in @autoclosure parameter + /// 'foo(a : @autoclosure () -> Int = 42)'. + CTP_AutoclosureDefaultParameter, + CTP_CalleeResult, ///< Constraint is placed on the result of a callee. CTP_CallArgument, ///< Call to function or operator requires type. CTP_ClosureResult, ///< Closure result expects a specific type. @@ -221,22 +149,20 @@ enum ContextualTypePurpose { ///< result type. CTP_Condition, ///< Condition expression of various statements e.g. ///< `if`, `for`, `while` etc. + CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop + ///< is expected to conform to 'Sequence' protocol. CTP_CannotFail, ///< Conversion can never fail. abort() if it does. }; -/// Flags that can be used to control name lookup. +/// Flags that can be used to control type checking. enum class TypeCheckExprFlags { /// Whether we know that the result of the expression is discarded. This /// disables constraints forcing an lvalue result to be loadable. IsDiscarded = 0x01, - /// Whether the client wants to disable the structural syntactic restrictions - /// that we force for style or other reasons. - DisableStructuralChecks = 0x02, - /// If set, the client wants a best-effort solution to the constraint system, /// but can tolerate a solution where all of the constraints are solved, but /// not all type variables have been determined. In this case, the constraint @@ -244,24 +170,11 @@ enum class TypeCheckExprFlags { /// left in-tact. AllowUnresolvedTypeVariables = 0x08, - /// If set, the 'convertType' specified to typeCheckExpression should not - /// produce a conversion constraint, but it should be used to guide the - /// solution in terms of performance optimizations of the solver, and in terms - /// of guiding diagnostics. - ConvertTypeIsOnlyAHint = 0x10, - /// If set, this expression isn't embedded in a larger expression or /// statement. This should only be used for syntactic restrictions, and should /// not affect type checking itself. IsExprStmt = 0x20, - /// If set, this expression is being re-type checked as part of diagnostics, - /// and so we should not visit bodies of non-single expression closures. - SkipMultiStmtClosures = 0x40, - - /// This is an inout yield. - IsInOutYield = 0x100, - /// If set, a conversion constraint should be specified so that the result of /// the expression is an optional type. ExpressionTypeMustBeOptional = 0x200, @@ -383,13 +296,6 @@ class ExprTypeCheckListener { /// constraint system, or false otherwise. virtual bool builtConstraints(constraints::ConstraintSystem &cs, Expr *expr); - /// Callback invoked once a solution has been found. - /// - /// The callback may further alter the expression, returning either a - /// new expression (to replace the result) or a null pointer to indicate - /// failure. - virtual Expr *foundSolution(constraints::Solution &solution, Expr *expr); - /// Callback invokes once the chosen solution has been applied to the /// expression. /// @@ -398,18 +304,6 @@ class ExprTypeCheckListener { /// failure. virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - - /// Callback invoked if expression is structurally unsound and can't - /// be correctly processed by the constraint solver. - virtual void preCheckFailed(Expr *expr); - - /// Callback invoked if constraint system failed to generate - /// constraints for a given expression. - virtual void constraintGenerationFailed(Expr *expr); - - /// Callback invoked if application of chosen solution to - /// expression has failed. - virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); }; /// A conditional conformance that implied some other requirements. That is, \c @@ -528,229 +422,39 @@ enum class CheckedCastContextKind { EnumElementPattern, }; -enum class FunctionBuilderClosurePreCheck : uint8_t { - /// There were no problems pre-checking the closure. - Okay, - - /// There was an error pre-checking the closure. - Error, - - /// The closure has a return statement. - HasReturnStmt, -}; - /// The Swift type checker, which takes a parsed AST and performs name binding, /// type checking, and semantic analysis to produce a type-annotated AST. -class TypeChecker final : public LazyResolver { +class TypeChecker final { public: - ASTContext &Context; - DiagnosticEngine &Diags; - /// The list of function definitions we've encountered. std::vector definedFunctions; - /// Declarations that need their conformances checked. - llvm::SmallVector ConformanceContexts; - - // Caches whether a given declaration is "as specialized" as another. - llvm::DenseMap, - bool> - specializedOverloadComparisonCache; - - /// A list of closures for the most recently type-checked function, which we - /// will need to compute captures for. - std::vector ClosuresWithUncomputedCaptures; - -private: - /// The # of times we have performed typo correction. - unsigned NumTypoCorrections = 0; - private: - Type MaxIntegerType; - Type NSObjectType; - Type NSNumberType; - Type NSValueType; - Type ObjCSelectorType; - - /// The set of expressions currently being analyzed for failures. - llvm::DenseMap DiagnosedExprs; - - /// The index of the next response metavariable to bind to a REPL result. - unsigned NextResponseVariableIndex = 0; - - /// If non-zero, warn when a function body takes longer than this many - /// milliseconds to type-check. - /// - /// Intended for debugging purposes only. - unsigned WarnLongFunctionBodies = 0; - - /// If non-zero, warn when type-checking an expression takes longer - /// than this many milliseconds. - /// - /// Intended for debugging purposes only. - unsigned WarnLongExpressionTypeChecking = 0; - - /// If non-zero, abort the expression type checker if it takes more - /// than this many seconds. - unsigned ExpressionTimeoutThreshold = 600; - - /// If non-zero, abort the switch statement exhaustiveness checker if - /// the Space::minus function is called more than this many times. - /// - /// Why this number? Times out in about a second on a 2017 iMac, Retina 5K, - // 4.2 GHz Intel Core i7. - // (It's arbitrary, but will keep the compiler from taking too much time.) - unsigned SwitchCheckingInvocationThreshold = 200000; - - /// If true, the time it takes to type-check each function will be dumped - /// to llvm::errs(). - bool DebugTimeFunctionBodies = false; - - /// If true, the time it takes to type-check each expression will be - /// dumped to llvm::errs(). - bool DebugTimeExpressions = false; - - /// Indicate that the type checker is checking code that will be - /// immediately executed. This will suppress certain warnings - /// when executing scripts. - bool InImmediateMode = false; - - /// Indicate that the type checker should skip type-checking non-inlinable - /// function bodies. - bool SkipNonInlinableFunctionBodies = false; + TypeChecker() = default; + ~TypeChecker() = default; - /// Closure expressions whose bodies have already been prechecked as - /// part of trying to apply a function builder. - llvm::DenseMap - precheckedFunctionBuilderClosures; - - TypeChecker(ASTContext &Ctx); friend class ASTContext; - friend class constraints::ConstraintSystem; - friend class TypeCheckFunctionBodyUntilRequest; - + public: /// Create a new type checker instance for the given ASTContext, if it /// doesn't already have one. /// - /// \returns a reference to the type vchecker. + /// \returns a reference to the type checker. static TypeChecker &createForContext(ASTContext &ctx); +public: TypeChecker(const TypeChecker&) = delete; TypeChecker& operator=(const TypeChecker&) = delete; - ~TypeChecker(); - - LangOptions &getLangOpts() const { return Context.LangOpts; } - - /// Dump the time it takes to type-check each function to llvm::errs(). - void enableDebugTimeFunctionBodies() { - DebugTimeFunctionBodies = true; - } - - /// Dump the time it takes to type-check each function to llvm::errs(). - void enableDebugTimeExpressions() { - DebugTimeExpressions = true; - } - - bool getDebugTimeExpressions() { - return DebugTimeExpressions; - } - - /// If \p timeInMS is non-zero, warn when a function body takes longer than - /// this many milliseconds to type-check. - /// - /// Intended for debugging purposes only. - void setWarnLongFunctionBodies(unsigned timeInMS) { - WarnLongFunctionBodies = timeInMS; - } - - /// If \p timeInMS is non-zero, warn when type-checking an expression - /// takes longer than this many milliseconds. - /// - /// Intended for debugging purposes only. - void setWarnLongExpressionTypeChecking(unsigned timeInMS) { - WarnLongExpressionTypeChecking = timeInMS; - } - - /// Return the current setting for the number of milliseconds - /// threshold we use to determine whether to warn about an - /// expression taking a long time. - unsigned getWarnLongExpressionTypeChecking() { - return WarnLongExpressionTypeChecking; - } - - /// Set the threshold that determines the upper bound for the number - /// of seconds we'll let the expression type checker run before - /// considering an expression "too complex". - void setExpressionTimeoutThreshold(unsigned timeInSeconds) { - ExpressionTimeoutThreshold = timeInSeconds; - } - - /// Return the current setting for the threshold that determines - /// the upper bound for the number of seconds we'll let the - /// expression type checker run before considering an expression - /// "too complex". - /// If zero, do not limit the checking. - unsigned getExpressionTimeoutThresholdInSeconds() { - return ExpressionTimeoutThreshold; - } - - /// Get the threshold that determines the upper bound for the number - /// of times we'll let the Space::minus routine run before - /// considering a switch statement "too complex". - /// If zero, do not limit the checking. - unsigned getSwitchCheckingInvocationThreshold() const { - return SwitchCheckingInvocationThreshold; - } - - /// Set the threshold that determines the upper bound for the number - /// of times we'll let the Space::minus routine run before - /// considering a switch statement "too complex". - void setSwitchCheckingInvocationThreshold(unsigned invocationCount) { - SwitchCheckingInvocationThreshold = invocationCount; - } - - void setSkipNonInlinableBodies(bool skip) { - SkipNonInlinableFunctionBodies = skip; - } - - bool canSkipNonInlinableBodies() const { - return SkipNonInlinableFunctionBodies; - } - - bool getInImmediateMode() { - return InImmediateMode; - } - - void setInImmediateMode(bool InImmediateMode) { - this->InImmediateMode = InImmediateMode; - } - - template - InFlightDiagnostic diagnose(ArgTypes &&...Args) { - return Diags.diagnose(std::forward(Args)...); - } - - void diagnoseWithNotes(InFlightDiagnostic parentDiag, - llvm::function_ref builder) { - CompoundDiagnosticTransaction transaction(Diags); - parentDiag.flush(); - builder(); - } static Type getArraySliceType(SourceLoc loc, Type elementType); static Type getDictionaryType(SourceLoc loc, Type keyType, Type valueType); static Type getOptionalType(SourceLoc loc, Type elementType); - Type getStringType(DeclContext *dc); - Type getSubstringType(DeclContext *dc); - Type getIntType(DeclContext *dc); - Type getInt8Type(DeclContext *dc); - Type getUInt8Type(DeclContext *dc); - Type getNSObjectType(DeclContext *dc); - Type getObjCSelectorType(DeclContext *dc); - Type getExceptionType(DeclContext *dc, SourceLoc loc); - + static Type getStringType(ASTContext &ctx); + static Type getSubstringType(ASTContext &ctx); + static Type getIntType(ASTContext &ctx); + static Type getInt8Type(ASTContext &ctx); + static Type getUInt8Type(ASTContext &ctx); + /// Try to resolve an IdentTypeRepr, returning either the referenced /// Type or an ErrorType in case of error. static Type resolveIdentifierType(TypeResolution resolution, @@ -760,7 +464,8 @@ class TypeChecker final : public LazyResolver { /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. - Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context); + static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, + DeclContext *Context); /// Validate the given type. /// @@ -781,28 +486,20 @@ class TypeChecker final : public LazyResolver { TypeResolutionOptions options); /// Check for unsupported protocol types in the given declaration. - void checkUnsupportedProtocolType(Decl *decl); + static void checkUnsupportedProtocolType(Decl *decl); /// Check for unsupported protocol types in the given statement. - void checkUnsupportedProtocolType(Stmt *stmt); + static void checkUnsupportedProtocolType(ASTContext &ctx, Stmt *stmt); /// Check for unsupported protocol types in the given generic requirement /// list. - void checkUnsupportedProtocolType(TrailingWhereClause *whereClause); + static void checkUnsupportedProtocolType(ASTContext &ctx, + TrailingWhereClause *whereClause); /// Check for unsupported protocol types in the given generic requirement /// list. - void checkUnsupportedProtocolType(GenericParamList *genericParams); - - /// Expose TypeChecker's handling of GenericParamList to SIL parsing. - GenericEnvironment *handleSILGenericParams(GenericParamList *genericParams, - DeclContext *DC); - - void validateDecl(ValueDecl *D); - - /// Validate the given extension declaration, ensuring that it - /// properly extends the nominal type it names. - void validateExtension(ExtensionDecl *ext); + static void checkUnsupportedProtocolType(ASTContext &ctx, + GenericParamList *genericParams); /// Resolve a reference to the given type declaration within a particular /// context. @@ -822,28 +519,6 @@ class TypeChecker final : public LazyResolver { TypeResolutionOptions options, bool isSpecialized); - /// Apply generic arguments to the given type. - /// - /// This function emits diagnostics about an invalid type or the wrong number - /// of generic arguments, whereas applyUnboundGenericArguments requires this - /// to be in a correct and valid form. - /// - /// \param type The generic type to which to apply arguments. - /// \param loc The source location for diagnostic reporting. - /// \param resolution The type resolution to perform. - /// \param generic The arguments to apply with the angle bracket range for - /// diagnostics. - /// \param options The type resolution context. - /// - /// \returns A BoundGenericType bound to the given arguments, or null on - /// error. - /// - /// \see applyUnboundGenericArguments - static Type applyGenericArguments(Type type, SourceLoc loc, - TypeResolution resolution, - GenericIdentTypeRepr *generic, - TypeResolutionOptions options); - /// Apply generic arguments to the given type. /// /// This function requires a valid unbound generic type with the correct @@ -907,7 +582,7 @@ class TypeChecker final : public LazyResolver { /// \param dc The context of the check. /// /// \returns true if \c t1 is a subtype of \c t2. - bool isSubtypeOf(Type t1, Type t2, DeclContext *dc); + static bool isSubtypeOf(Type t1, Type t2, DeclContext *dc); /// Determine whether one type is implicitly convertible to another. /// @@ -921,8 +596,8 @@ class TypeChecker final : public LazyResolver { /// conversion force-unwrapped an implicitly-unwrapped optional. /// /// \returns true if \c t1 can be implicitly converted to \c t2. - bool isConvertibleTo(Type t1, Type t2, DeclContext *dc, - bool *unwrappedIUO = nullptr); + static bool isConvertibleTo(Type t1, Type t2, DeclContext *dc, + bool *unwrappedIUO = nullptr); /// Determine whether one type is explicitly convertible to another, /// i.e. using an 'as' expression. @@ -934,7 +609,7 @@ class TypeChecker final : public LazyResolver { /// \param dc The context of the conversion. /// /// \returns true if \c t1 can be explicitly converted to \c t2. - bool isExplicitlyConvertibleTo(Type t1, Type t2, DeclContext *dc); + static bool isExplicitlyConvertibleTo(Type t1, Type t2, DeclContext *dc); /// Determine whether one type is bridged to another type. /// @@ -948,8 +623,8 @@ class TypeChecker final : public LazyResolver { /// conversion force-unwrapped an implicitly-unwrapped optional. /// /// \returns true if \c t1 can be explicitly converted to \c t2. - bool isObjCBridgedTo(Type t1, Type t2, DeclContext *dc, - bool *unwrappedIUO = nullptr); + static bool isObjCBridgedTo(Type t1, Type t2, DeclContext *dc, + bool *unwrappedIUO = nullptr); /// Return true if performing a checked cast from one type to another /// with the "as!" operator could possibly succeed. @@ -962,7 +637,7 @@ class TypeChecker final : public LazyResolver { /// /// \returns true if a checked cast from \c t1 to \c t2 may succeed, and /// false if it will certainly fail, e.g. because the types are unrelated. - bool checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc); + static bool checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc); /// Determine whether a constraint of the given kind can be satisfied /// by the two types. @@ -981,70 +656,61 @@ class TypeChecker final : public LazyResolver { /// or bridge operation force-unwraps an implicitly-unwrapped optional. /// /// \returns true if \c t1 and \c t2 satisfy the constraint. - bool typesSatisfyConstraint(Type t1, Type t2, - bool openArchetypes, - constraints::ConstraintKind kind, - DeclContext *dc, - bool *unwrappedIUO = nullptr); + static bool typesSatisfyConstraint(Type t1, Type t2, + bool openArchetypes, + constraints::ConstraintKind kind, + DeclContext *dc, + bool *unwrappedIUO = nullptr); /// If the inputs to an apply expression use a consistent "sugar" type /// (that is, a typealias or shorthand syntax) equivalent to the result type /// of the function, set the result type of the expression to that sugar type. - Expr *substituteInputSugarTypeForResult(ApplyExpr *E); - - bool typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD, - SourceLoc EndTypeCheckLoc); - bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD); + static Expr *substituteInputSugarTypeForResult(ApplyExpr *E); - BraceStmt *applyFunctionBuilderBodyTransform(FuncDecl *FD, - BraceStmt *body, - Type builderType); - bool typeCheckClosureBody(ClosureExpr *closure); + static bool typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD, + SourceLoc EndTypeCheckLoc); + static bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD); - bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); + /// Try to apply the function builder transform of the given builder type + /// to the body of the function. + /// + /// \returns \c None if the builder transformation cannot be applied at all, + /// e.g., because of a \c return statement. Otherwise, returns either the + /// fully type-checked body of the function (on success) or a \c nullptr + /// value if an error occurred while type checking the transformed body. + static Optional applyFunctionBuilderBodyTransform( + FuncDecl *func, Type builderType); - Type typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, - Type paramType, bool isAutoClosure = false, - bool canFail = true); + static bool typeCheckClosureBody(ClosureExpr *closure); - void typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD); + static bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); - void processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC, - unsigned StartElem); - Identifier getNextResponseVariableName(DeclContext *DC); + static Type typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, + Type paramType, bool isAutoClosure); - void typeCheckDecl(Decl *D); + static void typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD); - static void addImplicitDynamicAttribute(Decl *D); - void checkDeclAttributes(Decl *D); - void checkParameterAttributes(ParameterList *params); - static ValueDecl *findReplacedDynamicFunction(const ValueDecl *d); + static void processREPLTopLevel(SourceFile &SF, unsigned StartElem); - Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, - ReferenceOwnershipAttr *attr); + static void typeCheckDecl(Decl *D); - virtual void resolveDeclSignature(ValueDecl *VD) override { - validateDecl(VD); - } - - virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) override { - addImplicitConstructors(nominal); - } + static void addImplicitDynamicAttribute(Decl *D); + static void checkDeclAttributes(Decl *D); + static void checkParameterAttributes(ParameterList *params); - virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) override { - synthesizeMemberForLookup(nominal, member); - } + static Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, + ReferenceOwnershipAttr *attr); /// Infer default value witnesses for all requirements in the given protocol. - void inferDefaultWitnesses(ProtocolDecl *proto); + static void inferDefaultWitnesses(ProtocolDecl *proto); /// For a generic requirement in a protocol, make sure that the requirement /// set didn't add any requirements to Self or its associated types. - void checkProtocolSelfRequirements(ValueDecl *decl); + static void checkProtocolSelfRequirements(ValueDecl *decl); /// All generic parameters of a generic function must be referenced in the /// declaration's type, otherwise we have no way to infer them. - void checkReferencedGenericParams(GenericContext *dc); + static void checkReferencedGenericParams(GenericContext *dc); /// Construct a new generic environment for the given declaration context. /// @@ -1116,54 +782,21 @@ class TypeChecker final : public LazyResolver { GenericRequirementsCheckListener *listener = nullptr, SubstOptions options = None); - /// Diagnose if the class has no designated initializers. - void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl); - - /// /// Add any implicitly-defined constructors required for the given /// struct or class. - void addImplicitConstructors(NominalTypeDecl *typeDecl); - - /// Synthesize the member with the given name on the target if applicable, - /// i.e. if the member is synthesizable and has not yet been added to the - /// target. - void synthesizeMemberForLookup(NominalTypeDecl *target, DeclName member); - - /// Pre-check the expression, validating any types that occur in the - /// expression and folding sequence expressions. - bool preCheckExpression(Expr *&expr, DeclContext *dc); - - /// Pre-check the body of the given closure, which we are about to - /// generate constraints for. - /// - /// This mutates the body of the closure, but only in ways that should be - /// valid even if we end up not actually applying the function-builder - /// transform: it just does a normal pre-check of all the expressions in - /// the closure. - FunctionBuilderClosurePreCheck - preCheckFunctionBuilderClosureBody(ClosureExpr *closure); - - /// \name Name lookup - /// - /// Routines that perform name lookup. - /// - /// During type checking, these routines should be used instead of - /// \c MemberLookup and \c UnqualifiedLookup, because these routines will - /// lazily introduce declarations and (FIXME: eventually) perform recursive - /// type-checking that the AST-level lookup routines don't. - /// - /// @{ -private: - Optional boolType; + static void addImplicitConstructors(NominalTypeDecl *typeDecl); public: - /// Define the default constructor for the given struct or class. - static void defineDefaultConstructor(NominalTypeDecl *decl); - /// Fold the given sequence expression into an (unchecked) expression /// tree. - Expr *foldSequence(SequenceExpr *expr, DeclContext *dc); + static Expr *foldSequence(SequenceExpr *expr, DeclContext *dc); +private: + /// Given an pre-folded expression, find LHS from the expression if a binary + /// operator \c name appended to the expression. + static Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); + +public: /// Type check the given expression. /// /// \param expr The expression to type-check, which will be modified in @@ -1175,11 +808,9 @@ class TypeChecker final : public LazyResolver { /// to be possible. /// /// \param convertType The type that the expression is being converted to, - /// or null if the expression is standalone. If the 'ConvertTypeIsOnlyAHint' - /// option is specified, then this is only a hint, it doesn't produce a full - /// conversion constraint. The location information is only used for - /// diagnostics should the conversion fail; it is safe to pass a TypeLoc - /// without location information. + /// or null if the expression is standalone. The location information is + /// only used for diagnostics should the conversion fail; it is safe to pass + /// a TypeLoc without location information. /// /// \param options Options that control how type checking is performed. /// @@ -1193,7 +824,7 @@ class TypeChecker final : public LazyResolver { /// /// \returns The type of the top-level expression, or Type() if an /// error occurred. - Type + static Type typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType = TypeLoc(), ContextualTypePurpose convertTypePurpose = CTP_Unused, @@ -1201,21 +832,6 @@ class TypeChecker final : public LazyResolver { ExprTypeCheckListener *listener = nullptr, constraints::ConstraintSystem *baseCS = nullptr); - Type typeCheckExpression(Expr *&expr, DeclContext *dc, - ExprTypeCheckListener *listener) { - return typeCheckExpression(expr, dc, TypeLoc(), CTP_Unused, - TypeCheckExprOptions(), listener); - } - -private: - Type typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - constraints::ConstraintSystem *baseCS); - -public: /// Type check the given expression and return its type without /// applying the solution. /// @@ -1233,14 +849,14 @@ class TypeChecker final : public LazyResolver { /// /// \returns the type of \p expr on success, Type() otherwise. /// FIXME: expr may still be modified... - Type getTypeOfExpressionWithoutApplying( + static Type getTypeOfExpressionWithoutApplying( Expr *&expr, DeclContext *dc, ConcreteDeclRef &referencedDecl, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, ExprTypeCheckListener *listener = nullptr); - void getPossibleTypesOfExpressionWithoutApplying( + static void getPossibleTypesOfExpressionWithoutApplying( Expr *&expr, DeclContext *dc, SmallPtrSetImpl &types, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, @@ -1248,23 +864,23 @@ class TypeChecker final : public LazyResolver { /// Return the type of operator function for specified LHS, or a null /// \c Type on error. - FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, - Identifier opName, - DeclRefKind refKind, - ConcreteDeclRef &referencedDecl); + static FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, + Identifier opName, + DeclRefKind refKind, + ConcreteDeclRef &refdDecl); /// Check the key-path expression. /// /// Returns the type of the last component of the key-path. - Optional checkObjCKeyPathExpr(DeclContext *dc, KeyPathExpr *expr, - bool requireResultType = false); + static Optional checkObjCKeyPathExpr(DeclContext *dc, KeyPathExpr *expr, + bool requireResultType = false); /// Type check whether the given type declaration includes members of /// unsupported recursive value types. /// /// \param decl The declaration to be type-checked. This process will not /// modify the declaration. - void checkDeclCircularity(NominalTypeDecl *decl); + static void checkDeclCircularity(NominalTypeDecl *decl); /// Type check whether the given switch statement exhaustively covers /// its domain. @@ -1275,8 +891,9 @@ class TypeChecker final : public LazyResolver { /// \param limitChecking The checking process relies on the switch statement /// being well-formed. If it is not, pass true to this flag to run a limited /// form of analysis. - void checkSwitchExhaustiveness(const SwitchStmt *stmt, const DeclContext *DC, - bool limitChecking); + static void checkSwitchExhaustiveness(const SwitchStmt *stmt, + const DeclContext *DC, + bool limitChecking); /// Type check the given expression as a condition, which converts /// it to a logic value. @@ -1285,7 +902,7 @@ class TypeChecker final : public LazyResolver { /// to return a logic value (builtin i1). /// /// \returns true if an error occurred, false otherwise. - bool typeCheckCondition(Expr *&expr, DeclContext *dc); + static bool typeCheckCondition(Expr *&expr, DeclContext *dc); /// Type check the given 'if' or 'while' statement condition, which /// either converts an expression to a logic value or bind variables to the @@ -1294,8 +911,8 @@ class TypeChecker final : public LazyResolver { /// \param cond The condition to type-check, which will be modified in place. /// /// \returns true if an error occurred, false otherwise. - bool typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, - Diag<> diagnosticForAlwaysTrue); + static bool typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc, + Diag<> diagnosticForAlwaysTrue); /// Determine the semantics of a checked cast operation. /// @@ -1309,13 +926,13 @@ class TypeChecker final : public LazyResolver { /// \returns a CheckedCastKind indicating the semantics of the cast. If the /// cast is invalid, Unresolved is returned. If the cast represents an implicit /// conversion, Coercion is returned. - CheckedCastKind typeCheckCheckedCast(Type fromType, - Type toType, - CheckedCastContextKind contextKind, - DeclContext *dc, - SourceLoc diagLoc, - Expr *fromExpr, - SourceRange diagToRange); + static CheckedCastKind typeCheckCheckedCast(Type fromType, + Type toType, + CheckedCastContextKind ctxKind, + DeclContext *dc, + SourceLoc diagLoc, + Expr *fromExpr, + SourceRange diagToRange); /// Find the Objective-C class that bridges between a value of the given /// dynamic type and the given value type. @@ -1332,65 +949,67 @@ class TypeChecker final : public LazyResolver { /// type as an Objective-C class, e.g., \c NSString represents \c /// String, or a null type if there is no such type or if the /// dynamic type isn't something we can start from. - Type getDynamicBridgedThroughObjCClass(DeclContext *dc, - Type dynamicType, - Type valueType); + static Type getDynamicBridgedThroughObjCClass(DeclContext *dc, + Type dynamicType, + Type valueType); /// Resolve ambiguous pattern/expr productions inside a pattern using /// name lookup information. Must be done before type-checking the pattern. - Pattern *resolvePattern(Pattern *P, DeclContext *dc, - bool isStmtCondition); + static Pattern *resolvePattern(Pattern *P, DeclContext *dc, + bool isStmtCondition); /// Type check the given pattern. /// - /// \param P The pattern to type check. - /// \param dc The context in which type checking occurs. - /// \param options Options that control type resolution. - /// - /// \returns true if any errors occurred during type checking. - bool typeCheckPattern(Pattern *P, DeclContext *dc, - TypeResolutionOptions options); + /// \returns the type of the pattern, which may be an error type if an + /// unrecoverable error occurred. If the options permit it, the type may + /// involve \c UnresolvedType (for patterns with no type information) and + /// unbound generic types. + static Type typeCheckPattern(ContextualPattern pattern); - bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc); + static bool typeCheckCatchPattern(CatchStmt *S, DeclContext *dc); /// Coerce a pattern to the given type. /// - /// \param P The pattern, which may be modified by this coercion. - /// \param resolution The type resolution. + /// \param pattern The contextual pattern. /// \param type the type to coerce the pattern to. - /// \param options Options describing how to perform this coercion. + /// \param options Options that control the coercion. /// - /// \returns true if an error occurred, false otherwise. - bool coercePatternToType(Pattern *&P, TypeResolution resolution, Type type, - TypeResolutionOptions options, - TypeLoc tyLoc = TypeLoc()); - bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, - Type type); + /// \returns the coerced pattern, or nullptr if the coercion failed. + static Pattern *coercePatternToType(ContextualPattern pattern, Type type, + TypeResolutionOptions options, + TypeLoc tyLoc = TypeLoc()); + static bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, + Type type); /// Coerce the specified parameter list of a ClosureExpr to the specified /// contextual type. - void coerceParameterListToType(ParameterList *P, ClosureExpr *CE, AnyFunctionType *FN); + static void coerceParameterListToType(ParameterList *P, ClosureExpr *CE, + AnyFunctionType *FN); /// Type-check an initialized variable pattern declaration. - bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC); - bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber); + static bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC, + Type patternType); + static bool typeCheckPatternBinding(PatternBindingDecl *PBD, + unsigned patternNumber, + Type patternType = Type()); /// Type-check a for-each loop's pattern binding and sequence together. - bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt); + /// + /// \returns true if a failure occurred. + static bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt); /// Compute the set of captures for the given function or closure. static void computeCaptures(AnyFunctionRef AFR); /// Check for invalid captures from stored property initializers. - static void checkPatternBindingCaptures(NominalTypeDecl *typeDecl); + static void checkPatternBindingCaptures(IterableDeclContext *DC); /// Change the context of closures in the given initializer /// expression to the given context. /// /// \returns true if any closures were found static bool contextualizeInitializer(Initializer *DC, Expr *init); - static void contextualizeTopLevelCode(TopLevelContext &TLC, - TopLevelCodeDecl *TLCD); + static void contextualizeTopLevelCode(TopLevelCodeDecl *TLCD); /// Return the type-of-reference of the given value. /// @@ -1405,11 +1024,11 @@ class TypeChecker final : public LazyResolver { /// \param wantInterfaceType Whether we want the interface type, if available. /// /// \param getType Optional callback to extract a type for given declaration. - Type getUnopenedTypeOfReference(VarDecl *value, Type baseType, - DeclContext *UseDC, - llvm::function_ref getType, - const DeclRefExpr *base = nullptr, - bool wantInterfaceType = false); + static Type + getUnopenedTypeOfReference(VarDecl *value, Type baseType, DeclContext *UseDC, + llvm::function_ref getType, + const DeclRefExpr *base = nullptr, + bool wantInterfaceType = false); /// Return the type-of-reference of the given value. /// @@ -1422,16 +1041,13 @@ class TypeChecker final : public LazyResolver { /// \param base The optional base expression of this value reference /// /// \param wantInterfaceType Whether we want the interface type, if available. - Type getUnopenedTypeOfReference(VarDecl *value, Type baseType, - DeclContext *UseDC, - const DeclRefExpr *base = nullptr, - bool wantInterfaceType = false) { + static Type getUnopenedTypeOfReference(VarDecl *value, Type baseType, + DeclContext *UseDC, + const DeclRefExpr *base = nullptr, + bool wantInterfaceType = false) { return getUnopenedTypeOfReference( value, baseType, UseDC, [&](VarDecl *var) -> Type { - if (!var->getInterfaceType() || var->isInvalid()) - return ErrorType::get(Context); - return wantInterfaceType ? value->getInterfaceType() : value->getType(); }, @@ -1446,48 +1062,25 @@ class TypeChecker final : public LazyResolver { /// /// \returns the default type, or null if there is no default type for /// this protocol. - Type getDefaultType(ProtocolDecl *protocol, DeclContext *dc); - - /// Convert the given expression to the given type. - /// - /// \param expr The expression, which will be updated in place. - /// \param type The type to convert to. - /// \param typeFromPattern Optionally, the caller can specify the pattern - /// from where the toType is derived, so that we can deliver better fixit. - /// - /// \returns true if an error occurred, false otherwise. - bool convertToType(Expr *&expr, Type type, DeclContext *dc, - Optional typeFromPattern = None); + static Type getDefaultType(ProtocolDecl *protocol, DeclContext *dc); /// Coerce the given expression to materializable type, if it /// isn't already. - Expr *coerceToRValue(Expr *expr, - llvm::function_ref getType - = [](Expr *expr) { return expr->getType(); }, - llvm::function_ref setType - = [](Expr *expr, Type type) { - expr->setType(type); - }); + static Expr *coerceToRValue( + ASTContext &Context, Expr *expr, + llvm::function_ref getType = + [](Expr *expr) { return expr->getType(); }, + llvm::function_ref setType = + [](Expr *expr, Type type) { expr->setType(type); }); /// Add implicit load expression to given AST, this is sometimes /// more complicated than simplify wrapping given root in newly created /// `LoadExpr`, because `ForceValueExpr` and `ParenExpr` supposed to appear /// only at certain positions in AST. - Expr *addImplicitLoadExpr(Expr *expr, - std::function getType, - std::function setType); - - /// Require that the library intrinsics for working with Optional - /// exist. - bool requireOptionalIntrinsics(SourceLoc loc); - - /// Require that the library intrinsics for working with - /// UnsafeMutablePointer exist. - bool requirePointerArgumentIntrinsics(SourceLoc loc); - - /// Require that the library intrinsics for creating - /// array literals exist. - bool requireArrayLiteralIntrinsics(SourceLoc loc); + static Expr * + addImplicitLoadExpr(ASTContext &Context, Expr *expr, + std::function getType, + std::function setType); /// Determine whether the given type contains the given protocol. /// @@ -1498,10 +1091,9 @@ class TypeChecker final : public LazyResolver { /// /// \returns the conformance, if \c T conforms to the protocol \c Proto, or /// an empty optional. - static Optional containsProtocol( - Type T, ProtocolDecl *Proto, - DeclContext *DC, - ConformanceCheckOptions options); + static ProtocolConformanceRef + containsProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, + ConformanceCheckOptions options); /// Determine whether the given type conforms to the given protocol. /// @@ -1520,12 +1112,10 @@ class TypeChecker final : public LazyResolver { /// /// \returns The protocol conformance, if \c T conforms to the /// protocol \c Proto, or \c None. - static Optional conformsToProtocol( - Type T, - ProtocolDecl *Proto, - DeclContext *DC, - ConformanceCheckOptions options, - SourceLoc ComplainLoc = SourceLoc()); + static ProtocolConformanceRef + conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC, + ConformanceCheckOptions options, + SourceLoc ComplainLoc = SourceLoc()); /// Functor class suitable for use as a \c LookupConformanceFn to look up a /// conformance through a particular declaration context using the given @@ -1536,21 +1126,20 @@ class TypeChecker final : public LazyResolver { public: explicit LookUpConformance(DeclContext *dc) : dc(dc) { } - Optional - operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) const; + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) const; }; /// Completely check the given conformance. - void checkConformance(NormalProtocolConformance *conformance); + static void checkConformance(NormalProtocolConformance *conformance); /// Check all of the conformances in the given context. - void checkConformancesInContext(DeclContext *dc, - IterableDeclContext *idc); + static void checkConformancesInContext(DeclContext *dc, + IterableDeclContext *idc); /// Check that the type of the given property conforms to NSCopying. - Optional checkConformanceToNSCopying(VarDecl *var); + static ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var); /// Derive an implicit declaration to satisfy a requirement of a derived /// protocol conformance. @@ -1563,16 +1152,21 @@ class TypeChecker final : public LazyResolver { /// \returns nullptr if the derivation failed, or the derived declaration /// if it succeeded. If successful, the derived declaration is added /// to TypeDecl's body. - ValueDecl *deriveProtocolRequirement(DeclContext *DC, - NominalTypeDecl *TypeDecl, - ValueDecl *Requirement); + static ValueDecl *deriveProtocolRequirement(DeclContext *DC, + NominalTypeDecl *TypeDecl, + ValueDecl *Requirement); /// Derive an implicit type witness for the given associated type in /// the conformance of the given nominal type to some known /// protocol. - Type deriveTypeWitness(DeclContext *DC, - NominalTypeDecl *nominal, - AssociatedTypeDecl *assocType); + static Type deriveTypeWitness(DeclContext *DC, NominalTypeDecl *nominal, + AssociatedTypeDecl *assocType); + + /// \name Name lookup + /// + /// Routines that perform name lookup. + /// + /// @{ /// Perform unqualified name lookup at the given source location /// within a particular declaration context. @@ -1581,7 +1175,7 @@ class TypeChecker final : public LazyResolver { /// \param name The name of the entity to look for. /// \param loc The source location at which name lookup occurs. /// \param options Options that control name lookup. - static LookupResult lookupUnqualified(DeclContext *dc, DeclName name, + static LookupResult lookupUnqualified(DeclContext *dc, DeclNameRef name, SourceLoc loc, NameLookupOptions options = defaultUnqualifiedLookupOptions); @@ -1594,7 +1188,7 @@ class TypeChecker final : public LazyResolver { /// \param loc The source location at which name lookup occurs. /// \param options Options that control name lookup. LookupResult - static lookupUnqualifiedType(DeclContext *dc, DeclName name, SourceLoc loc, + static lookupUnqualifiedType(DeclContext *dc, DeclNameRef name, SourceLoc loc, NameLookupOptions options = defaultUnqualifiedLookupOptions); @@ -1606,14 +1200,10 @@ class TypeChecker final : public LazyResolver { /// \param options Options that control name lookup. /// /// \returns The result of name lookup. - static LookupResult lookupMember(DeclContext *dc, Type type, DeclName name, + static LookupResult lookupMember(DeclContext *dc, Type type, DeclNameRef name, NameLookupOptions options = defaultMemberLookupOptions); - /// Check whether the given declaration can be written as a - /// member of the given base type. - static bool isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); - /// Look up a member type within the given type. /// /// This routine looks for member types with the given name within the @@ -1626,7 +1216,7 @@ class TypeChecker final : public LazyResolver { /// /// \returns The result of name lookup. static LookupTypeResult lookupMemberType(DeclContext *dc, Type type, - Identifier name, + DeclNameRef name, NameLookupOptions options = defaultMemberTypeLookupOptions); @@ -1650,9 +1240,9 @@ class TypeChecker final : public LazyResolver { Identifier name, SourceLoc nameLoc); - /// Given an pre-folded expression, find LHS from the expression if a binary - /// operator \c name appended to the expression. - Expr *findLHS(DeclContext *DC, Expr *E, Identifier name); + /// Check whether the given declaration can be written as a + /// member of the given base type. + static bool isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl); /// @} @@ -1668,24 +1258,18 @@ class TypeChecker final : public LazyResolver { /// A declaration is more specialized than another declaration if its type /// is a subtype of the other declaration's type (ignoring the 'self' /// parameter of function declarations) and if - Comparison compareDeclarations(DeclContext *dc, - ValueDecl *decl1, - ValueDecl *decl2); + static Comparison compareDeclarations(DeclContext *dc, ValueDecl *decl1, + ValueDecl *decl2); /// Build a type-checked reference to the given value. - Expr *buildCheckedRefExpr(VarDecl *D, DeclContext *UseDC, - DeclNameLoc nameLoc, bool Implicit); + static Expr *buildCheckedRefExpr(VarDecl *D, DeclContext *UseDC, + DeclNameLoc nameLoc, bool Implicit); /// Build a reference to a declaration, where name lookup returned /// the given set of declarations. - Expr *buildRefExpr(ArrayRef Decls, DeclContext *UseDC, - DeclNameLoc NameLoc, bool Implicit, - FunctionRefKind functionRefKind); - - /// Build implicit autoclosure expression wrapping a given expression. - /// Given expression represents computed result of the closure. - Expr *buildAutoClosureExpr(DeclContext *DC, Expr *expr, - FunctionType *closureType); + static Expr *buildRefExpr(ArrayRef Decls, DeclContext *UseDC, + DeclNameLoc NameLoc, bool Implicit, + FunctionRefKind functionRefKind); /// @} /// Retrieve a specific, known protocol. @@ -1695,38 +1279,29 @@ class TypeChecker final : public LazyResolver { /// /// \returns null if the protocol is not available. This represents a /// problem with the Standard Library. - ProtocolDecl *getProtocol(SourceLoc loc, KnownProtocolKind kind); + static ProtocolDecl *getProtocol(ASTContext &ctx, SourceLoc loc, + KnownProtocolKind kind); /// Retrieve the literal protocol for the given expression. /// /// \returns the literal protocol, if known and available, or null if the /// expression does not have an associated literal protocol. - ProtocolDecl *getLiteralProtocol(Expr *expr); + static ProtocolDecl *getLiteralProtocol(ASTContext &ctx, Expr *expr); - DeclName getObjectLiteralConstructorName(ObjectLiteralExpr *expr); + static DeclName getObjectLiteralConstructorName(ASTContext &ctx, + ObjectLiteralExpr *expr); - Type getObjectLiteralParameterType(ObjectLiteralExpr *expr, - ConstructorDecl *ctor); + static Type getObjectLiteralParameterType(ObjectLiteralExpr *expr, + ConstructorDecl *ctor); /// Get the module appropriate for looking up standard library types. /// /// This is "Swift", if that module is imported, or the current module if /// we're parsing the standard library. - ModuleDecl *getStdlibModule(const DeclContext *dc); - - /// \name Lazy resolution. - /// - /// Routines that perform lazy resolution as required for AST operations. - /// @{ - void resolveTypeWitness(const NormalProtocolConformance *conformance, - AssociatedTypeDecl *assocType) override; - void resolveWitness(const NormalProtocolConformance *conformance, - ValueDecl *requirement) override; + static ModuleDecl *getStdlibModule(const DeclContext *dc); /// \name Resilience diagnostics - void diagnoseInlinableLocalType(const NominalTypeDecl *NTD); - /// Used in diagnostic %selects. enum class FragileFunctionKind : unsigned { Transparent, @@ -1741,7 +1316,7 @@ class TypeChecker final : public LazyResolver { FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic); - Expr *buildDefaultInitializer(Type type); + static Expr *buildDefaultInitializer(Type type); private: static bool diagnoseInlinableDeclRefAccess(SourceLoc loc, const ValueDecl *D, @@ -1787,7 +1362,7 @@ class TypeChecker final : public LazyResolver { /// is sufficient to safely conform to the requirement in the context /// the provided conformance. On return, requiredAvailability holds th /// availability levels required for conformance. - bool + static bool isAvailabilitySafeForConformance(ProtocolDecl *proto, ValueDecl *requirement, ValueDecl *witness, DeclContext *dc, AvailabilityContext &requiredAvailability); @@ -1841,7 +1416,7 @@ class TypeChecker final : public LazyResolver { /// /// An ignored expression is one that is not nested within a larger /// expression or statement. - void checkIgnoredExpr(Expr *E); + static void checkIgnoredExpr(Expr *E); // Emits a diagnostic, if necessary, for a reference to a declaration // that is potentially unavailable at the given source location. @@ -1883,43 +1458,26 @@ class TypeChecker final : public LazyResolver { /// @} /// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl - /// has a name with that prefix, an llvm fatal_error is triggered. + /// name starts with that prefix, an llvm fatal_error is triggered. /// This is for testing purposes. - void checkForForbiddenPrefix(const Decl *D); - void checkForForbiddenPrefix(const UnresolvedDeclRefExpr *E); - void checkForForbiddenPrefix(Identifier Ident); - void checkForForbiddenPrefix(StringRef Name); - - bool hasEnabledForbiddenTypecheckPrefix() const { - return !Context.LangOpts.DebugForbidTypecheckPrefix.empty(); - } + static void checkForForbiddenPrefix(ASTContext &C, DeclBaseName Name); /// Check error handling in the given type-checked top-level code. - void checkTopLevelErrorHandling(TopLevelCodeDecl *D); - void checkFunctionErrorHandling(AbstractFunctionDecl *D); - void checkInitializerErrorHandling(Initializer *I, Expr *E); - void checkEnumElementErrorHandling(EnumElementDecl *D, Expr *expr); - void checkPropertyWrapperErrorHandling(PatternBindingDecl *binding, - Expr *expr); - - void addExprForDiagnosis(Expr *E1, Expr *Result) { - DiagnosedExprs[E1] = Result; - } - bool isExprBeingDiagnosed(Expr *E) { - return DiagnosedExprs.count(E); - } - Expr *getExprBeingDiagnosed(Expr *E) { - return DiagnosedExprs[E]; - } + static void checkTopLevelErrorHandling(TopLevelCodeDecl *D); + static void checkFunctionErrorHandling(AbstractFunctionDecl *D); + static void checkInitializerErrorHandling(Initializer *I, Expr *E); + static void checkEnumElementErrorHandling(EnumElementDecl *D, Expr *expr); + static void checkPropertyWrapperErrorHandling(PatternBindingDecl *binding, + Expr *expr); /// If an expression references 'self.init' or 'super.init' in an /// initializer context, returns the implicit 'self' decl of the constructor. /// Otherwise, return nil. - VarDecl *getSelfForInitDelegationInConstructor(DeclContext *DC, - UnresolvedDotExpr *ctorRef); + static VarDecl *getSelfForInitDelegationInConstructor(DeclContext *DC, + UnresolvedDotExpr *UDE); /// Diagnose assigning variable to itself. - bool diagnoseSelfAssignment(const Expr *E); + static bool diagnoseSelfAssignment(const Expr *E); /// Builds a string representing a "default" generic argument list for /// \p typeDecl. In general, this means taking the bound of each generic @@ -1939,13 +1497,13 @@ class TypeChecker final : public LazyResolver { [](const GenericTypeParamDecl *) { return Type(); }); /// Attempt to omit needless words from the name of the given declaration. - Optional omitNeedlessWords(AbstractFunctionDecl *afd); + static Optional omitNeedlessWords(AbstractFunctionDecl *afd); /// Attempt to omit needless words from the name of the given declaration. - Optional omitNeedlessWords(VarDecl *var); + static Optional omitNeedlessWords(VarDecl *var); /// Calculate edit distance between declaration names. - static unsigned getCallEditDistance(DeclName writtenName, + static unsigned getCallEditDistance(DeclNameRef writtenName, DeclName correctedName, unsigned maxEditDistance); @@ -1959,17 +1517,45 @@ class TypeChecker final : public LazyResolver { }; /// Check for a typo correction. - void performTypoCorrection(DeclContext *DC, - DeclRefKind refKind, - Type baseTypeOrNull, - NameLookupOptions lookupOptions, - TypoCorrectionResults &corrections, - GenericSignatureBuilder *gsb = nullptr, - unsigned maxResults = 4); + static void performTypoCorrection(DeclContext *DC, + DeclRefKind refKind, + Type baseTypeOrNull, + NameLookupOptions lookupOptions, + TypoCorrectionResults &corrections, + GenericSignatureBuilder *gsb = nullptr, + unsigned maxResults = 4); /// Check if the given decl has a @_semantics attribute that gives it /// special case type-checking behavior. - DeclTypeCheckingSemantics getDeclTypeCheckingSemantics(ValueDecl *decl); + static DeclTypeCheckingSemantics + getDeclTypeCheckingSemantics(ValueDecl *decl); + + /// Infers the differentiability parameter indices for the given + /// original or derivative `AbstractFunctionDecl`. + /// + /// The differentiability parameters are inferred to be: + /// - All parameters of the function that conform to `Differentiable`. + /// - If the function result type is a function type (i.e. the function has + /// a curried method type), then also all parameters of the function result + /// type that conform to `Differentiable`. + /// + /// Used by `@differentiable` and `@derivative` attribute type-checking. + static IndexSubset * + inferDifferentiabilityParameters(AbstractFunctionDecl *AFD, + GenericEnvironment *derivativeGenEnv); + +public: + /// Require that the library intrinsics for working with Optional + /// exist. + static bool requireOptionalIntrinsics(ASTContext &ctx, SourceLoc loc); + + /// Require that the library intrinsics for working with + /// UnsafeMutablePointer exist. + static bool requirePointerArgumentIntrinsics(ASTContext &ctx, SourceLoc loc); + + /// Require that the library intrinsics for creating + /// array literals exist. + static bool requireArrayLiteralIntrinsics(ASTContext &ctx, SourceLoc loc); }; /// Temporary on-stack storage and unescaping for encoded diagnostic @@ -1993,13 +1579,12 @@ class EncodedDiagnosticMessage { /// as one of the following: `dynamicallyCall(withArguments:)` or /// `dynamicallyCall(withKeywordArguments:)`. bool isValidDynamicCallableMethod(FuncDecl *decl, DeclContext *DC, - TypeChecker &TC, bool hasKeywordArguments); + bool hasKeywordArguments); /// Returns true if the given subscript method is an valid implementation of /// the `subscript(dynamicMember:)` requirement for @dynamicMemberLookup. /// The method is given to be defined as `subscript(dynamicMember:)`. bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC, - TypeChecker &TC, bool ignoreLabel = false); /// Returns true if the given subscript method is an valid implementation of @@ -2008,7 +1593,6 @@ bool isValidDynamicMemberLookupSubscript(SubscriptDecl *decl, DeclContext *DC, /// takes a single non-variadic parameter that conforms to /// `ExpressibleByStringLiteral` protocol. bool isValidStringDynamicMemberLookup(SubscriptDecl *decl, DeclContext *DC, - TypeChecker &TC, bool ignoreLabel = false); /// Returns true if the given subscript method is an valid implementation of @@ -2016,7 +1600,7 @@ bool isValidStringDynamicMemberLookup(SubscriptDecl *decl, DeclContext *DC, /// @dynamicMemberLookup. /// The method is given to be defined as `subscript(dynamicMember:)` which /// takes a single non-variadic parameter of `{Writable}KeyPath` type. -bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, TypeChecker &TC, +bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl, bool ignoreLabel = false); /// Compute the wrapped value type for the given property that has attached @@ -2052,13 +1636,13 @@ OverrideRequiresKeyword overrideRequiresKeyword(ValueDecl *overridden); /// Compute the type of a member that will be used for comparison when /// performing override checking. -Type getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member, - ValueDecl *derivedDecl = nullptr); +Type getMemberTypeForComparison(const ValueDecl *member, + const ValueDecl *derivedDecl = nullptr); /// Determine whether the given declaration is an override by comparing type /// information. -bool isOverrideBasedOnType(ValueDecl *decl, Type declTy, - ValueDecl *parentDecl, Type parentDeclTy); +bool isOverrideBasedOnType(const ValueDecl *decl, Type declTy, + const ValueDecl *parentDecl, Type parentDeclTy); /// Determine whether the given declaration is an operator defined in a /// protocol. If \p type is not null, check specifically whether \p decl @@ -2094,12 +1678,6 @@ bool areGenericRequirementsSatisfied(const DeclContext *DC, GenericSignature sig, SubstitutionMap Substitutions, bool isExtension); - -bool canSatisfy(Type type1, Type type2, bool openArchetypes, - constraints::ConstraintKind kind, DeclContext *dc); - -bool hasDynamicMemberLookupAttribute(Type type, - llvm::DenseMap &DynamicMemberLookupCache); } // end namespace swift #endif diff --git a/lib/Sema/TypoCorrection.h b/lib/Sema/TypoCorrection.h index 7253e51a41ad9..2f2dc8c927e25 100644 --- a/lib/Sema/TypoCorrection.h +++ b/lib/Sema/TypoCorrection.h @@ -34,11 +34,11 @@ class TypeChecker; /// correction even if the corrected name resolves to an overload set. class SyntacticTypoCorrection { public: - DeclName WrittenName; + DeclNameRef WrittenName; DeclNameLoc Loc; DeclName CorrectedName; - SyntacticTypoCorrection(DeclName writtenName, DeclNameLoc writtenLoc, + SyntacticTypoCorrection(DeclNameRef writtenName, DeclNameLoc writtenLoc, DeclName correctedName) : WrittenName(writtenName), Loc(writtenLoc), CorrectedName(correctedName) {} @@ -48,15 +48,14 @@ class SyntacticTypoCorrection { /// A collection of typo-correction candidates. class TypoCorrectionResults { public: - TypeChecker &TC; - DeclName WrittenName; + DeclNameRef WrittenName; DeclNameLoc Loc; bool ClaimedCorrection = false; SmallVector Candidates; - TypoCorrectionResults(TypeChecker &tc, DeclName writtenName, DeclNameLoc loc) - : TC(tc), WrittenName(writtenName), Loc(loc) {} + TypoCorrectionResults(DeclNameRef writtenName, DeclNameLoc loc) + : WrittenName(writtenName), Loc(loc) {} /// Try to claim a unique correction from this collection that's simple /// enough to include "inline" in the primary diagnostic. Note that diff --git a/lib/Serialization/BCReadingExtras.h b/lib/Serialization/BCReadingExtras.h index 1ef3e8e33ad8e..928a2cb82580b 100644 --- a/lib/Serialization/BCReadingExtras.h +++ b/lib/Serialization/BCReadingExtras.h @@ -13,7 +13,7 @@ #ifndef SWIFT_SERIALIZATION_BCREADINGEXTRAS_H #define SWIFT_SERIALIZATION_BCREADINGEXTRAS_H -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" namespace swift { namespace serialization { @@ -38,7 +38,8 @@ class BCOffsetRAII { ~BCOffsetRAII() { if (Cursor) - Cursor->JumpToBit(Offset); + cantFail(Cursor->JumpToBit(Offset), + "BCOffsetRAII must be able to go back"); } }; diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index f569669e05960..44a2a90660bdf 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -6,7 +6,11 @@ add_swift_host_library(swiftSerialization STATIC SerializedModuleLoader.cpp SerializedSILLoader.cpp SerializeDoc.cpp - SerializeSIL.cpp) + SerializeSIL.cpp + + LLVM_LINK_COMPONENTS + BitstreamReader + ) target_link_libraries(swiftSerialization PRIVATE swiftClangImporter) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e53900a280421..78d5bed929e30 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -15,6 +15,7 @@ #include "ModuleFile.h" #include "ModuleFormat.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/Expr.h" #include "swift/AST/ForeignErrorConvention.h" @@ -135,16 +136,18 @@ const char TypeError::ID = '\0'; void TypeError::anchor() {} const char ExtensionError::ID = '\0'; void ExtensionError::anchor() {} +const char DeclAttributesDidNotMatch::ID = '\0'; +void DeclAttributesDidNotMatch::anchor() {} /// Skips a single record in the bitstream. /// -/// Returns true if the next entry is a record of type \p recordKind. /// Destroys the stream position if the next entry is not a record. static void skipRecord(llvm::BitstreamCursor &cursor, unsigned recordKind) { - auto next = cursor.advance(AF_DontPopBlockAtEnd); + auto next = llvm::cantFail( + cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); - unsigned kind = cursor.skipRecord(next.ID); + unsigned kind = llvm::cantFail(cursor.skipRecord(next.ID)); assert(kind == recordKind); (void)kind; } @@ -204,6 +207,8 @@ getActualDefaultArgKind(uint8_t raw) { return swift::DefaultArgumentKind::Column; case serialization::DefaultArgumentKind::File: return swift::DefaultArgumentKind::File; + case serialization::DefaultArgumentKind::FilePath: + return swift::DefaultArgumentKind::FilePath; case serialization::DefaultArgumentKind::Line: return swift::DefaultArgumentKind::Line; case serialization::DefaultArgumentKind::Function: @@ -226,8 +231,10 @@ ParameterList *ModuleFile::readParameterList() { using namespace decls_block; SmallVector scratch; - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); + unsigned recordID = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); assert(recordID == PARAMETERLIST); (void) recordID; @@ -259,7 +266,8 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { SmallVector scratch; BCOffsetRAII restoreOffset(DeclTypeCursor); - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -271,7 +279,8 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { pattern->setType(type); }; - unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); switch (kind) { case decls_block::PAREN_PATTERN: { bool isImplicit; @@ -302,10 +311,10 @@ Expected ModuleFile::readPattern(DeclContext *owningDC) { SmallVector elements; for ( ; count > 0; --count) { scratch.clear(); - next = DeclTypeCursor.advance(); + next = fatalIfUnexpected(DeclTypeCursor.advance()); assert(next.Kind == llvm::BitstreamEntry::Record); - kind = DeclTypeCursor.readRecord(next.ID, scratch); + kind = fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); assert(kind == decls_block::TUPLE_PATTERN_ELT); // FIXME: Add something for this record or remove it. @@ -397,10 +406,11 @@ SILLayout *ModuleFile::readSILLayout(llvm::BitstreamCursor &Cursor) { SmallVector scratch; - auto next = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); - unsigned kind = Cursor.readRecord(next.ID, scratch); + unsigned kind = fatalIfUnexpected(Cursor.readRecord(next.ID, scratch)); switch (kind) { case decls_block::SIL_LAYOUT: { GenericSignatureID rawGenericSig; @@ -420,7 +430,7 @@ SILLayout *ModuleFile::readSILLayout(llvm::BitstreamCursor &Cursor) { CanGenericSignature canSig; if (auto sig = getGenericSignature(rawGenericSig)) - canSig = sig->getCanonicalSignature(); + canSig = sig.getCanonicalSignature(); return SILLayout::get(getContext(), canSig, fields); } default: @@ -444,13 +454,14 @@ ModuleFile::readConformanceChecked(llvm::BitstreamCursor &Cursor, SmallVector scratch; - auto next = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); assert(next.Kind == llvm::BitstreamEntry::Record); if (getContext().Stats) getContext().Stats->getFrontendCounters().NumConformancesDeserialized++; - unsigned kind = Cursor.readRecord(next.ID, scratch); + unsigned kind = fatalIfUnexpected(Cursor.readRecord(next.ID, scratch)); switch (kind) { case INVALID_PROTOCOL_CONFORMANCE: { return ProtocolConformanceRef::forInvalid(); @@ -591,8 +602,8 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( // Find the conformance record. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(conformanceEntry); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(conformanceEntry)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -602,9 +613,11 @@ NormalProtocolConformance *ModuleFile::readNormalConformance( ArrayRef rawIDs; SmallVector scratch; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); if (kind != NORMAL_PROTOCOL_CONFORMANCE) fatal(); + NormalProtocolConformanceLayout::readRecord(scratch, protoID, contextID, typeCount, valueCount, conformanceCount, @@ -651,11 +664,13 @@ GenericParamList *ModuleFile::maybeReadGenericParams(DeclContext *DC) { SmallVector scratch; StringRef blobData; - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return nullptr; - unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch, &blobData); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch, &blobData)); if (kind != GENERIC_PARAM_LIST) return nullptr; lastRecordOffset.reset(); @@ -700,12 +715,14 @@ llvm::Error ModuleFile::readGenericRequirementsChecked( lastRecordOffset.reset(); bool shouldContinue = true; - auto entry = Cursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(Cursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = Cursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + Cursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case GENERIC_REQUIREMENT: { uint8_t rawKind; @@ -829,29 +846,36 @@ llvm::Error ModuleFile::readGenericRequirementsChecked( } /// Advances past any records that might be part of a requirement signature. -static void skipGenericRequirements(llvm::BitstreamCursor &Cursor) { +static llvm::Error skipGenericRequirements(llvm::BitstreamCursor &Cursor) { using namespace decls_block; BCOffsetRAII lastRecordOffset(Cursor); while (true) { - auto entry = Cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + Cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind != llvm::BitstreamEntry::Record) break; - unsigned recordID = Cursor.skipRecord(entry.ID); - switch (recordID) { + Expected maybeRecordID = Cursor.skipRecord(entry.ID); + if (!maybeRecordID) + return maybeRecordID.takeError(); + switch (maybeRecordID.get()) { case GENERIC_REQUIREMENT: case LAYOUT_REQUIREMENT: break; default: // This record is not a generic requirement. - return; + return llvm::Error::success(); } lastRecordOffset.reset(); } + return llvm::Error::success(); } GenericSignature ModuleFile::getGenericSignature( @@ -879,18 +903,20 @@ ModuleFile::getGenericSignatureChecked(serialization::GenericSignatureID ID) { // Read the generic signature. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(sigOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(sigOffset)); // Read the parameter types. SmallVector paramTypes; StringRef blobData; SmallVector scratch; - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case GENERIC_SIGNATURE: { ArrayRef rawParamIDs; @@ -902,6 +928,7 @@ ModuleFile::getGenericSignatureChecked(serialization::GenericSignatureID ID) { } break; } + case SIL_GENERIC_SIGNATURE: { ArrayRef rawParamIDs; SILGenericSignatureLayout::readRecord(scratch, rawParamIDs); @@ -977,16 +1004,18 @@ ModuleFile::getSubstitutionMapChecked(serialization::SubstitutionMapID id) { // Read the substitution map. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(substitutionsOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(substitutionsOrOffset)); // Read the substitution map. - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); StringRef blobData; SmallVector scratch; - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != SUBSTITUTION_MAP) fatal(); @@ -1031,13 +1060,15 @@ ModuleFile::getSubstitutionMapChecked(serialization::SubstitutionMapID id) { bool ModuleFile::readDefaultWitnessTable(ProtocolDecl *proto) { using namespace decls_block; - auto entry = DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) return true; SmallVector witnessIDBuffer; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, witnessIDBuffer); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, witnessIDBuffer)); assert(kind == DEFAULT_WITNESS_TABLE); (void)kind; @@ -1122,11 +1153,8 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule, return true; // If we're expecting a type, make sure this decl has the expected type. - if (canTy) { - auto ifaceTy = value->getInterfaceType(); - if (!ifaceTy || !ifaceTy->isEqual(canTy)) - return true; - } + if (canTy && !value->getInterfaceType()->isEqual(canTy)) + return true; if (value->isStatic() != isStatic) return true; @@ -1146,8 +1174,9 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule, // If we're expecting a member within a constrained extension with a // particular generic signature, match that signature. if (expectedGenericSig && - value->getDeclContext()->getGenericSignatureOfContext() - ->getCanonicalSignature() != expectedGenericSig) + value->getDeclContext() + ->getGenericSignatureOfContext() + .getCanonicalSignature() != expectedGenericSig) return true; // If we don't expect a specific generic signature, ignore anything from a @@ -1178,6 +1207,21 @@ static void filterValues(Type expectedTy, ModuleDecl *expectedModule, values.erase(newEnd, values.end()); } +static TypeDecl * +findNestedTypeDeclInModule(FileUnit *thisFile, ModuleDecl *extensionModule, + Identifier name, NominalTypeDecl *parent) { + assert(extensionModule && "NULL is not a valid module"); + for (FileUnit *file : extensionModule->getFiles()) { + if (file == thisFile) + continue; + + if (auto nestedType = file->lookupNestedType(name, parent)) { + return nestedType; + } + } + return nullptr; +} + Expected ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { using namespace decls_block; @@ -1190,7 +1234,8 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { assert(baseModule && "missing dependency"); PrettyXRefTrace pathTrace(*baseModule); - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -1203,8 +1248,8 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // In particular, operator path pieces represent actual operators here, but // filters on operator functions when they appear later on. scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: case XREF_VALUE_PATH_PIECE: { @@ -1232,7 +1277,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto maybeType = getTypeChecked(TID); if (!maybeType) { // FIXME: Don't throw away the inner error's information. - llvm::consumeError(maybeType.takeError()); + consumeError(maybeType.takeError()); return llvm::make_error("couldn't decode type", pathTrace, name); } @@ -1244,7 +1289,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { baseModule->lookupMember(values, baseModule, name, getIdentifier(privateDiscriminator)); } else { - baseModule->lookupQualified(baseModule, name, + baseModule->lookupQualified(baseModule, DeclNameRef(name), NL_QualifiedDefault | NL_KnownNoDependency, values); } @@ -1306,13 +1351,14 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto getXRefDeclNameForError = [&]() -> DeclName { DeclName result = pathTrace.getLastName(); while (--pathLen) { - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) return Identifier(); scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: { IdentifierID IID; @@ -1375,13 +1421,14 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // For remaining path pieces, filter or drill down into the results we have. while (--pathLen) { - auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); scratch.clear(); - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { case XREF_TYPE_PATH_PIECE: { if (values.size() == 1 && isa(values.front())) { @@ -1410,13 +1457,23 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { // Fault in extensions, then ask every file in the module. (void)baseType->getExtensions(); - TypeDecl *nestedType = nullptr; - for (FileUnit *file : extensionModule->getFiles()) { - if (file == getFile()) - continue; - nestedType = file->lookupNestedType(memberName, baseType); - if (nestedType) - break; + auto *nestedType = + findNestedTypeDeclInModule(getFile(), extensionModule, + memberName, baseType); + + // For clang module units, also search tables in the overlays. + if (!nestedType) { + if (auto LF = + dyn_cast(baseType->getModuleScopeContext())) { + if (auto overlayModule = LF->getOverlayModule()) { + nestedType = findNestedTypeDeclInModule(getFile(), overlayModule, + memberName, baseType); + } else if (LF->getParentModule() != extensionModule) { + nestedType = findNestedTypeDeclInModule(getFile(), + LF->getParentModule(), + memberName, baseType); + } + } } if (nestedType) { @@ -1487,7 +1544,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { auto maybeType = getTypeChecked(TID); if (!maybeType) { // FIXME: Don't throw away the inner error's information. - llvm::consumeError(maybeType.takeError()); + consumeError(maybeType.takeError()); return llvm::make_error("couldn't decode type", pathTrace, memberName); } @@ -1600,15 +1657,15 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { ValueDecl *base = values.front(); - GenericSignature currentSig = GenericSignature(); + GenericSignature currentSig; if (auto nominal = dyn_cast(base)) { if (genericSig) { // Find an extension in the requested module that has the // correct generic signature. for (auto ext : nominal->getExtensions()) { if (ext->getModuleContext() == M && - ext->getGenericSignature()->getCanonicalSignature() - == genericSig) { + ext->getGenericSignature().getCanonicalSignature() == + genericSig) { currentSig = ext->getGenericSignature(); break; } @@ -1778,8 +1835,8 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { return declContextOrOffset; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(declContextOrOffset); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(declContextOrOffset)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); @@ -1788,8 +1845,8 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { SmallVector scratch; StringRef blobData; - unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch(recordID) { case decls_block::ABSTRACT_CLOSURE_EXPR_CONTEXT: { TypeID closureTypeID; @@ -1858,14 +1915,25 @@ DeclContext *ModuleFile::getLocalDeclContext(LocalDeclContextID DCID) { } DeclContext *ModuleFile::getDeclContext(DeclContextID DCID) { + auto deserialized = getDeclContextChecked(DCID); + if (!deserialized) { + fatal(deserialized.takeError()); + } + return deserialized.get(); +} + +Expected ModuleFile::getDeclContextChecked(DeclContextID DCID) { if (!DCID) return FileContext; if (Optional contextID = DCID.getAsLocalDeclContextID()) return getLocalDeclContext(contextID.getValue()); - auto D = getDecl(DCID.getAsDeclID().getValue()); + auto deserialized = getDeclChecked(DCID.getAsDeclID().getValue()); + if (!deserialized) + return deserialized.takeError(); + auto D = deserialized.get(); if (auto GTD = dyn_cast(D)) return GTD; if (auto ED = dyn_cast(D)) @@ -2093,6 +2161,21 @@ getActualReadWriteImplKind(unsigned rawKind) { return None; } +/// Translate from the serialization DifferentiabilityKind enumerators, which +/// are guaranteed to be stable, to the AST ones. +static Optional +getActualAutoDiffDerivativeFunctionKind(uint8_t raw) { + switch (serialization::AutoDiffDerivativeFunctionKind(raw)) { +#define CASE(ID) \ + case serialization::AutoDiffDerivativeFunctionKind::ID: \ + return {swift::AutoDiffDerivativeFunctionKind::ID}; + CASE(JVP) + CASE(VJP) +#undef CASE + } + return None; +} + void ModuleFile::configureStorage(AbstractStorageDecl *decl, uint8_t rawOpaqueReadOwnership, uint8_t rawReadImplKind, @@ -2175,7 +2258,8 @@ Decl *ModuleFile::getDecl(DeclID DID) { } /// Used to split up methods that would otherwise live in ModuleFile. -class swift::DeclDeserializer { +namespace swift { +class DeclDeserializer { template using Serialized = ModuleFile::Serialized; using TypeID = serialization::TypeID; @@ -2253,7 +2337,8 @@ class swift::DeclDeserializer { /// passing each one to AddAttribute. llvm::Error deserializeDeclAttributes(); - Expected getDeclCheckedImpl(); + Expected getDeclCheckedImpl( + llvm::function_ref matchAttributes = nullptr); Expected deserializeTypeAlias(ArrayRef scratch, StringRef blobData) { @@ -2564,7 +2649,6 @@ class swift::DeclDeserializer { } ctor->setImplicitlyUnwrappedOptional(isIUO); - ctor->computeType(); return ctor; } @@ -2577,6 +2661,7 @@ class swift::DeclDeserializer { uint8_t rawIntroducer; bool isGetterMutating, isSetterMutating; bool isLazyStorageProperty; + bool isTopLevelGlobal; DeclID lazyStorageID; unsigned numAccessors, numBackingProperties; uint8_t readImpl, writeImpl, readWriteImpl, opaqueReadOwnership; @@ -2593,6 +2678,7 @@ class swift::DeclDeserializer { hasNonPatternBindingInit, isGetterMutating, isSetterMutating, isLazyStorageProperty, + isTopLevelGlobal, lazyStorageID, opaqueReadOwnership, readImpl, writeImpl, readWriteImpl, @@ -2724,10 +2810,23 @@ class swift::DeclDeserializer { } var->setLazyStorageProperty(isLazyStorageProperty); + var->setTopLevelGlobal(isTopLevelGlobal); // If there are any backing properties, record them. if (numBackingProperties > 0) { - VarDecl *backingVar = cast(MF.getDecl(backingPropertyIDs[0])); + auto backingDecl = MF.getDeclChecked(backingPropertyIDs[0]); + if (!backingDecl) { + if (numBackingProperties > 1 && + backingDecl.errorIsA()) { + // A property wrapper defined behind an implementation-only import + // is safe to drop when it can't be deserialized. + // rdar://problem/56599179 + consumeError(backingDecl.takeError()); + } else + return backingDecl.takeError(); + } + + VarDecl *backingVar = cast(backingDecl.get()); VarDecl *storageWrapperVar = nullptr; if (numBackingProperties > 1) { storageWrapperVar = cast(MF.getDecl(backingPropertyIDs[1])); @@ -2786,8 +2885,8 @@ class swift::DeclDeserializer { if (paramTy->hasError()) { // FIXME: This should never happen, because we don't serialize // error types. - DC->dumpContext(); - paramTy->dump(); + DC->printContext(llvm::errs()); + paramTy->dump(llvm::errs()); MF.fatal(); } @@ -2822,7 +2921,7 @@ class swift::DeclDeserializer { DeclID associatedDeclID; DeclID overriddenID; DeclID accessorStorageDeclID; - bool needsNewVTableEntry, isTransparent; + bool overriddenAffectsABI, needsNewVTableEntry, isTransparent; DeclID opaqueReturnTypeID; ArrayRef nameAndDependencyIDs; @@ -2835,6 +2934,7 @@ class swift::DeclDeserializer { resultInterfaceTypeID, isIUO, associatedDeclID, overriddenID, + overriddenAffectsABI, numNameComponentsBiased, rawAccessLevel, needsNewVTableEntry, @@ -2849,6 +2949,7 @@ class swift::DeclDeserializer { resultInterfaceTypeID, isIUO, overriddenID, + overriddenAffectsABI, accessorStorageDeclID, rawAccessorKind, rawAccessLevel, @@ -2914,20 +3015,11 @@ class swift::DeclDeserializer { overridden = overriddenOrError.get(); } else { llvm::consumeError(overriddenOrError.takeError()); - // There's one case where we know it's safe to ignore a missing override: - // if this declaration is '@objc' and 'dynamic'. - bool canIgnoreMissingOverriddenDecl = false; - if (isObjC && ctx.LangOpts.EnableDeserializationRecovery) { - canIgnoreMissingOverriddenDecl = - std::any_of(DeclAttributes::iterator(DAttrs), - DeclAttributes::iterator(nullptr), - [](const DeclAttribute *attr) -> bool { - return isa(attr); - }); - } - if (!canIgnoreMissingOverriddenDecl) + + if (overriddenAffectsABI || !ctx.LangOpts.EnableDeserializationRecovery) { return llvm::make_error( name, errorFlags, numVTableEntries); + } overridden = nullptr; } @@ -3036,9 +3128,6 @@ class swift::DeclDeserializer { cast(MF.getDecl(opaqueReturnTypeID))); } - // Set the interface type. - fn->computeType(); - return fn; } @@ -3217,11 +3306,10 @@ class swift::DeclDeserializer { proto->setImplicit(); proto->setIsObjC(isObjC); - proto->setCircularityCheck(CircularityCheck::Checked); - proto->setLazyRequirementSignature(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); - skipGenericRequirements(MF.DeclTypeCursor); + if (llvm::Error Err = skipGenericRequirements(MF.DeclTypeCursor)) + MF.fatal(std::move(Err)); proto->setMemberLoader(&MF, MF.DeclTypeCursor.GetCurrentBitNo()); @@ -3368,6 +3456,7 @@ class swift::DeclDeserializer { DeclContextID contextID; bool isImplicit, isObjC; bool inheritsSuperclassInitializers; + bool hasMissingDesignatedInits; GenericSignatureID genericSigID; TypeID superclassID; uint8_t rawAccessLevel; @@ -3376,6 +3465,7 @@ class swift::DeclDeserializer { decls_block::ClassLayout::readRecord(scratch, nameID, contextID, isImplicit, isObjC, inheritsSuperclassInitializers, + hasMissingDesignatedInits, genericSigID, superclassID, rawAccessLevel, numConformances, numInheritedTypes, @@ -3416,8 +3506,10 @@ class swift::DeclDeserializer { theClass->setImplicit(); theClass->setIsObjC(isObjC); theClass->setSuperclass(MF.getType(superclassID)); - if (inheritsSuperclassInitializers) - theClass->setInheritsSuperclassInitializers(); + ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass}, + std::move(inheritsSuperclassInitializers)); + ctx.evaluator.cacheOutput(HasMissingDesignatedInitializersRequest{theClass}, + std::move(hasMissingDesignatedInits)); handleInherited(theClass, rawInheritedAndDependencyIDs.slice(0, numInheritedTypes)); @@ -3428,8 +3520,6 @@ class swift::DeclDeserializer { &MF, encodeLazyConformanceContextData(numConformances, MF.DeclTypeCursor.GetCurrentBitNo())); - - theClass->setCircularityCheck(CircularityCheck::Checked); return theClass; } @@ -3451,7 +3541,6 @@ class swift::DeclDeserializer { numConformances, numInherited, rawInheritedAndDependencyIDs); - auto DC = MF.getDeclContext(contextID); if (declOrOffset.isComplete()) return declOrOffset; @@ -3465,6 +3554,11 @@ class swift::DeclDeserializer { } } + auto DCOrError = MF.getDeclContextChecked(contextID); + if (!DCOrError) + return DCOrError.takeError(); + auto DC = DCOrError.get(); + auto genericParams = MF.maybeReadGenericParams(DC); if (declOrOffset.isComplete()) return declOrOffset; @@ -3820,7 +3914,6 @@ class swift::DeclDeserializer { dtor->setAccess(std::max(cast(DC)->getFormalAccess(), AccessLevel::Internal)); - dtor->computeType(); if (isImplicit) dtor->setImplicit(); @@ -3829,9 +3922,12 @@ class swift::DeclDeserializer { return dtor; } }; +} Expected -ModuleFile::getDeclChecked(DeclID DID) { +ModuleFile::getDeclChecked( + DeclID DID, + llvm::function_ref matchAttributes) { if (DID == 0) return nullptr; @@ -3841,12 +3937,17 @@ ModuleFile::getDeclChecked(DeclID DID) { if (!declOrOffset.isComplete()) { ++NumDeclsLoaded; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(declOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(declOrOffset)); Expected deserialized = - DeclDeserializer(*this, declOrOffset).getDeclCheckedImpl(); + DeclDeserializer(*this, declOrOffset).getDeclCheckedImpl( + matchAttributes); if (!deserialized) return deserialized; + } else if (matchAttributes) { + // Decl was cached but we may need to filter it + if (!matchAttributes(declOrOffset.get()->getAttrs())) + return llvm::make_error(); } // Tag every deserialized ValueDecl coming out of getDeclChecked with its ID. @@ -3869,17 +3970,19 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { StringRef blobData; while (true) { BCOffsetRAII restoreOffset(MF.DeclTypeCursor); - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize decls represented by sub-blocks. MF.fatal(); } - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (isDeclAttrRecord(recordID)) { DeclAttribute *Attr = nullptr; + bool skipAttr = false; switch (recordID) { case decls_block::SILGenName_DECL_ATTR: { bool isImplicit; @@ -3951,6 +4054,28 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { Attr = new (ctx) EffectsAttr((EffectsKind)kind); break; } + case decls_block::OriginallyDefinedIn_DECL_ATTR: { + bool isImplicit; + unsigned Platform; + DEF_VER_TUPLE_PIECES(MovedVer); + // Decode the record, pulling the version tuple information. + serialization::decls_block::OriginallyDefinedInDeclAttrLayout::readRecord( + scratch, + isImplicit, + LIST_VER_TUPLE_PIECES(MovedVer), + Platform); + llvm::VersionTuple MovedVer; + DECODE_VER_TUPLE(MovedVer) + auto ModuleNameEnd = blobData.find('\0'); + assert(ModuleNameEnd != StringRef::npos); + auto ModuleName = blobData.slice(0, ModuleNameEnd); + Attr = new (ctx) OriginallyDefinedInAttr(SourceLoc(), SourceRange(), + ModuleName, + (PlatformKind)Platform, + MovedVer, + isImplicit); + break; + } case decls_block::Available_DECL_ATTR: { bool isImplicit; @@ -4056,9 +4181,6 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { serialization::decls_block::DynamicReplacementDeclAttrLayout:: readRecord(scratch, isImplicit, replacedFunID, numArgs, rawPieceIDs); - auto replacedFunDecl = MF.getDeclChecked(replacedFunID); - if (!replacedFunDecl) - return replacedFunDecl.takeError(); auto baseName = MF.getDeclBaseName(rawPieceIDs[0]); SmallVector pieces; for (auto pieceID : rawPieceIDs.slice(1)) @@ -4067,8 +4189,7 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { assert(numArgs != 0); assert(!isImplicit && "Need to update for implicit"); Attr = DynamicReplacementAttr::create( - ctx, DeclName(ctx, baseName, ArrayRef(pieces)), - cast(*replacedFunDecl)); + ctx, DeclNameRef({ ctx, baseName, pieces }), &MF, replacedFunID); break; } @@ -4080,13 +4201,19 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { Expected deserialized = MF.getTypeChecked(typeID); if (!deserialized) { - MF.fatal(deserialized.takeError()); - break; + if (deserialized.errorIsA()) { + // A custom attribute defined behind an implementation-only import + // is safe to drop when it can't be deserialized. + // rdar://problem/56599179 + consumeError(deserialized.takeError()); + skipAttr = true; + } else + return deserialized.takeError(); + } else { + Attr = CustomAttr::create(ctx, SourceLoc(), + TypeLoc::withoutLoc(deserialized.get()), + isImplicit); } - - Attr = CustomAttr::create(ctx, SourceLoc(), - TypeLoc::withoutLoc(deserialized.get()), - isImplicit); break; } @@ -4102,6 +4229,38 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { break; } + case decls_block::Derivative_DECL_ATTR: { + bool isImplicit; + uint64_t origNameId; + DeclID origDeclId; + uint64_t rawDerivativeKind; + ArrayRef parameters; + + serialization::decls_block::DerivativeDeclAttrLayout::readRecord( + scratch, isImplicit, origNameId, origDeclId, rawDerivativeKind, + parameters); + + DeclNameRefWithLoc origName{ + DeclNameRef(MF.getDeclBaseName(origNameId)), DeclNameLoc()}; + auto *origDecl = cast(MF.getDecl(origDeclId)); + auto derivativeKind = + getActualAutoDiffDerivativeFunctionKind(rawDerivativeKind); + if (!derivativeKind) + MF.fatal(); + llvm::SmallBitVector parametersBitVector(parameters.size()); + for (unsigned i : indices(parameters)) + parametersBitVector[i] = parameters[i]; + auto *indices = IndexSubset::get(ctx, parametersBitVector); + + auto *derivativeAttr = + DerivativeAttr::create(ctx, isImplicit, SourceLoc(), SourceRange(), + /*baseType*/ nullptr, origName, indices); + derivativeAttr->setOriginalFunction(origDecl); + derivativeAttr->setDerivativeKind(*derivativeKind); + Attr = derivativeAttr; + break; + } + #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case decls_block::CLASS##_DECL_ATTR: { \ bool isImplicit; \ @@ -4117,10 +4276,12 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { MF.fatal(); } - if (!Attr) - return llvm::Error::success(); + if (!skipAttr) { + if (!Attr) + return llvm::Error::success(); - AddAttribute(Attr); + AddAttribute(Attr); + } } else if (recordID == decls_block::PRIVATE_DISCRIMINATOR) { IdentifierID discriminatorID; @@ -4147,21 +4308,32 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { } Expected -DeclDeserializer::getDeclCheckedImpl() { - if (auto s = ctx.Stats) - s->getFrontendCounters().NumDeclsDeserialized++; +DeclDeserializer::getDeclCheckedImpl( + llvm::function_ref matchAttributes) { auto attrError = deserializeDeclAttributes(); if (attrError) return std::move(attrError); + if (matchAttributes) { + // Deserialize the full decl only if matchAttributes finds a match. + DeclAttributes attrs = DeclAttributes(); + attrs.setRawAttributeChain(DAttrs); + if (!matchAttributes(attrs)) + return llvm::make_error(); + } + + if (auto s = ctx.Stats) + s->getFrontendCounters().NumDeclsDeserialized++; + // FIXME: @_dynamicReplacement(for:) includes a reference to another decl, // usually in the same type, and that can result in this decl being // re-entrantly deserialized. If that happens, don't fail here. if (declOrOffset.isComplete()) return declOrOffset; - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize decls represented by sub-blocks. MF.fatal(); @@ -4169,8 +4341,8 @@ DeclDeserializer::getDeclCheckedImpl() { SmallVector scratch; StringRef blobData; - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); PrettyDeclDeserialization stackTraceEntry( &MF, declOrOffset, static_cast(recordID)); @@ -4242,6 +4414,26 @@ getActualFunctionTypeRepresentation(uint8_t rep) { } } +/// Translate from the Serialization differentiability kind enum values to the +/// AST strongly-typed enum. +/// +/// The former is guaranteed to be stable, but may not reflect this version of +/// the AST. +static Optional +getActualDifferentiabilityKind(uint8_t rep) { + switch (rep) { +#define CASE(THE_CC) \ + case (uint8_t)serialization::DifferentiabilityKind::THE_CC: \ + return swift::DifferentiabilityKind::THE_CC; + CASE(NonDifferentiable) + CASE(Normal) + CASE(Linear) +#undef CASE + default: + return None; + } +} + /// Translate from the Serialization function type repr enum values to the AST /// strongly-typed enum. /// @@ -4339,6 +4531,21 @@ Optional getActualParameterConvention(uint8_t raw) { return None; } +/// Translate from the serialization SILParameterDifferentiability enumerators, +/// which are guaranteed to be stable, to the AST ones. +static Optional +getActualSILParameterDifferentiability(uint8_t raw) { + switch (serialization::SILParameterDifferentiability(raw)) { +#define CASE(ID) \ + case serialization::SILParameterDifferentiability::ID: \ + return swift::SILParameterDifferentiability::ID; + CASE(DifferentiableOrNotApplicable) + CASE(NotDifferentiable) +#undef CASE + } + return None; +} + /// Translate from the serialization ResultConvention enumerators, /// which are guaranteed to be stable, to the AST ones. static @@ -4364,7 +4571,8 @@ Type ModuleFile::getType(TypeID TID) { return deserialized.get(); } -class swift::TypeDeserializer { +namespace swift { +class TypeDeserializer { using TypeID = serialization::TypeID; ModuleFile &MF; @@ -4544,13 +4752,14 @@ class swift::TypeDeserializer { // The tuple record itself is empty. Read all trailing elements. SmallVector elements; while (true) { - auto entry = MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != decls_block::TUPLE_TYPE_ELT) break; @@ -4572,22 +4781,21 @@ class swift::TypeDeserializer { StringRef blobData, bool isGeneric) { TypeID resultID; - uint8_t rawRepresentation; + uint8_t rawRepresentation, rawDiffKind; bool noescape = false, throws; - GenericSignature genericSig = GenericSignature(); + GenericSignature genericSig; + clang::Type *clangFunctionType = nullptr; + // FIXME: [clang-function-type-serialization] Deserialize a clang::Type out + // of the record. if (!isGeneric) { - decls_block::FunctionTypeLayout::readRecord(scratch, resultID, - rawRepresentation, - noescape, - throws); + decls_block::FunctionTypeLayout::readRecord( + scratch, resultID, rawRepresentation, noescape, throws, rawDiffKind); } else { GenericSignatureID rawGenericSig; - decls_block::GenericFunctionTypeLayout::readRecord(scratch, - resultID, - rawRepresentation, - throws, - rawGenericSig); + decls_block::GenericFunctionTypeLayout::readRecord( + scratch, resultID, rawRepresentation, throws, rawDiffKind, + rawGenericSig); genericSig = MF.getGenericSignature(rawGenericSig); } @@ -4595,7 +4803,13 @@ class swift::TypeDeserializer { if (!representation.hasValue()) MF.fatal(); - auto info = FunctionType::ExtInfo(*representation, noescape, throws); + auto diffKind = getActualDifferentiabilityKind(rawDiffKind); + if (!diffKind.hasValue()) + MF.fatal(); + + auto info = FunctionType::ExtInfo(*representation, noescape, throws, + *diffKind, clangFunctionType); + auto resultTy = MF.getTypeChecked(resultID); if (!resultTy) @@ -4603,23 +4817,24 @@ class swift::TypeDeserializer { SmallVector params; while (true) { - auto entry = MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); if (recordID != decls_block::FUNCTION_PARAM) break; IdentifierID labelID; TypeID typeID; - bool isVariadic, isAutoClosure; + bool isVariadic, isAutoClosure, isNonEphemeral, isNoDerivative; unsigned rawOwnership; - decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID, - isVariadic, isAutoClosure, - rawOwnership); + decls_block::FunctionParamLayout::readRecord( + scratch, labelID, typeID, isVariadic, isAutoClosure, isNonEphemeral, + rawOwnership, isNoDerivative); auto ownership = getActualValueOwnership((serialization::ValueOwnership)rawOwnership); @@ -4630,10 +4845,10 @@ class swift::TypeDeserializer { if (!paramTy) return paramTy.takeError(); - params.emplace_back(paramTy.get(), - MF.getIdentifier(labelID), + params.emplace_back(paramTy.get(), MF.getIdentifier(labelID), ParameterTypeFlags(isVariadic, isAutoClosure, - *ownership)); + isNonEphemeral, *ownership, + isNoDerivative)); } if (!isGeneric) { @@ -4897,7 +5112,7 @@ class swift::TypeDeserializer { } BCOffsetRAII saveOffset(MF.DeclTypeCursor); - MF.DeclTypeCursor.JumpToBit(layoutOrOffset); + MF.fatalIfNotSuccess(MF.DeclTypeCursor.JumpToBit(layoutOrOffset)); auto layout = MF.readSILLayout(MF.DeclTypeCursor); if (!layout) MF.fatal(); @@ -4918,26 +5133,35 @@ class swift::TypeDeserializer { uint8_t rawCoroutineKind; uint8_t rawCalleeConvention; uint8_t rawRepresentation; + uint8_t rawDiffKind; bool pseudogeneric = false; bool noescape; bool hasErrorResult; unsigned numParams; unsigned numYields; unsigned numResults; + bool isGenericSignatureImplied; GenericSignatureID rawGenericSig; + SubstitutionMapID rawSubs; ArrayRef variableData; + clang::FunctionType *clangFunctionType = nullptr; + // FIXME: [clang-function-type-serialization] Deserialize a + // clang::FunctionType out of the record. decls_block::SILFunctionTypeLayout::readRecord(scratch, rawCoroutineKind, rawCalleeConvention, rawRepresentation, pseudogeneric, noescape, + rawDiffKind, hasErrorResult, numParams, numYields, numResults, + isGenericSignatureImplied, rawGenericSig, + rawSubs, variableData); // Process the ExtInfo. @@ -4945,7 +5169,13 @@ class swift::TypeDeserializer { = getActualSILFunctionTypeRepresentation(rawRepresentation); if (!representation.hasValue()) MF.fatal(); - SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape); + + auto diffKind = getActualDifferentiabilityKind(rawDiffKind); + if (!diffKind.hasValue()) + MF.fatal(); + + SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape, + *diffKind, clangFunctionType); // Process the coroutine kind. auto coroutineKind = getActualSILCoroutineKind(rawCoroutineKind); @@ -4957,15 +5187,26 @@ class swift::TypeDeserializer { if (!calleeConvention.hasValue()) MF.fatal(); - auto processParameter = [&](TypeID typeID, uint64_t rawConvention) - -> llvm::Expected { + auto processParameter = + [&](TypeID typeID, uint64_t rawConvention, + uint64_t ramDifferentiability) -> llvm::Expected { auto convention = getActualParameterConvention(rawConvention); if (!convention) MF.fatal(); auto type = MF.getTypeChecked(typeID); if (!type) return type.takeError(); - return SILParameterInfo(type.get()->getCanonicalType(), *convention); + auto differentiability = + swift::SILParameterDifferentiability::DifferentiableOrNotApplicable; + if (diffKind != DifferentiabilityKind::NonDifferentiable) { + auto differentiabilityOpt = + getActualSILParameterDifferentiability(ramDifferentiability); + if (!differentiabilityOpt) + MF.fatal(); + differentiability = *differentiabilityOpt; + } + return SILParameterInfo(type.get()->getCanonicalType(), *convention, + differentiability); }; auto processYield = [&](TypeID typeID, uint64_t rawConvention) @@ -5004,7 +5245,10 @@ class swift::TypeDeserializer { for (unsigned i = 0; i != numParams; ++i) { auto typeID = variableData[nextVariableDataIndex++]; auto rawConvention = variableData[nextVariableDataIndex++]; - auto param = processParameter(typeID, rawConvention); + uint64_t differentiability = 0; + if (diffKind != DifferentiabilityKind::NonDifferentiable) + differentiability = variableData[nextVariableDataIndex++]; + auto param = processParameter(typeID, rawConvention, differentiability); if (!param) return param.takeError(); allParams.push_back(param.get()); @@ -5045,17 +5289,20 @@ class swift::TypeDeserializer { errorResult = maybeErrorResult.get(); } - Optional witnessMethodConformance; + ProtocolConformanceRef witnessMethodConformance; if (*representation == SILFunctionTypeRepresentation::WitnessMethod) { witnessMethodConformance = MF.readConformance(MF.DeclTypeCursor); } GenericSignature genericSig = MF.getGenericSignature(rawGenericSig); - + SubstitutionMap subs = MF.getSubstitutionMap(rawSubs).getCanonical(); + return SILFunctionType::get(genericSig, extInfo, coroutineKind.getValue(), calleeConvention.getValue(), allParams, allYields, allResults, - errorResult, ctx, witnessMethodConformance); + errorResult, + subs, isGenericSignatureImplied, + ctx, witnessMethodConformance); } Expected deserializeArraySliceType(ArrayRef scratch, @@ -5116,6 +5363,7 @@ class swift::TypeDeserializer { return UnboundGenericType::get(genericDecl, parentTy, ctx); } }; +} Expected ModuleFile::getTypeChecked(TypeID TID) { if (TID == 0) @@ -5128,7 +5376,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { return typeOrOffset; BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(typeOrOffset); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(typeOrOffset)); auto result = TypeDeserializer(*this).getTypeCheckedImpl(); if (!result) @@ -5138,7 +5386,7 @@ Expected ModuleFile::getTypeChecked(TypeID TID) { #ifndef NDEBUG PrettyStackTraceType trace(getContext(), "deserializing", typeOrOffset.get()); if (typeOrOffset.get()->hasError()) { - typeOrOffset.get()->dump(); + typeOrOffset.get()->dump(llvm::errs()); llvm_unreachable("deserialization produced an invalid type " "(rdar://problem/30382791)"); } @@ -5153,7 +5401,8 @@ Expected TypeDeserializer::getTypeCheckedImpl() { if (auto s = ctx.Stats) s->getFrontendCounters().NumTypesDeserialized++; - auto entry = MF.DeclTypeCursor.advance(); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize types represented by sub-blocks. @@ -5162,8 +5411,8 @@ Expected TypeDeserializer::getTypeCheckedImpl() { SmallVector scratch; StringRef blobData; - unsigned recordID = MF.DeclTypeCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); switch (recordID) { #define CASE(RECORD_NAME) \ @@ -5211,7 +5460,8 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context, Decl *suppliedMissingMember = nullptr; auto handleMissingClassMember = [&](const DeclDeserializationError &error) { if (error.isDesignatedInitializer()) - containingClass->setHasMissingDesignatedInitializers(); + context.evaluator.cacheOutput( + HasMissingDesignatedInitializersRequest{containingClass}, true); if (error.getNumberOfVTableEntries() > 0) containingClass->setHasMissingVTableEntries(); @@ -5275,14 +5525,15 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) { IDC = cast(container); BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) fatal(); SmallVector memberIDBuffer; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, memberIDBuffer); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, memberIDBuffer)); assert(kind == decls_block::MEMBERS); (void)kind; @@ -5332,7 +5583,7 @@ ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData, = decodeLazyConformanceContextData(contextData); BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(bitPosition); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(bitPosition)); while (numConformances--) { auto conf = readConformance(DeclTypeCursor); @@ -5347,6 +5598,11 @@ ModuleFile::loadAssociatedTypeDefault(const swift::AssociatedTypeDecl *ATD, return getType(contextData); } +ValueDecl *ModuleFile::loadDynamicallyReplacedFunctionDecl( + const DynamicReplacementAttr *DRA, uint64_t contextData) { + return cast(getDecl(contextData)); +} + void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) { using namespace decls_block; @@ -5364,8 +5620,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, // Find the conformance record. BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); - auto entry = DeclTypeCursor.advance(); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); assert(entry.Kind == llvm::BitstreamEntry::Record && "registered lazy loader incorrectly"); @@ -5375,7 +5631,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, ArrayRef rawIDs; SmallVector scratch; - unsigned kind = DeclTypeCursor.readRecord(entry.ID, scratch); + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); (void) kind; assert(kind == NORMAL_PROTOCOL_CONFORMANCE && "registered lazy loader incorrectly"); @@ -5394,9 +5651,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, // conformance requirements are on Self. This isn't actually a /safe/ change // even in Objective-C, but we mostly just don't want to crash. - // FIXME: DenseMap requires that its value type be default-constructible, - // which ProtocolConformanceRef is not, hence the extra Optional. - llvm::SmallDenseMap, 16> + llvm::SmallDenseMap conformancesForProtocols; while (conformanceCount--) { ProtocolConformanceRef nextConformance = readConformance(DeclTypeCursor); @@ -5411,7 +5666,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, req.getSecondType()->castTo()->getDecl(); auto iter = conformancesForProtocols.find(proto); if (iter != conformancesForProtocols.end()) { - reqConformances.push_back(iter->getSecond().getValue()); + reqConformances.push_back(iter->getSecond()); } else { // Put in an abstract conformance as a placeholder. This is a lie, but // there's not much better we can do. We're relying on the fact that @@ -5458,14 +5713,15 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, // rest of the compiler. third = nullptr; } - typeWitnesses[first] = std::make_pair(second, third); + typeWitnesses[first] = {second, third}; } assert(rawIDIter <= rawIDs.end() && "read too much"); // Set type witnesses. for (auto typeWitness : typeWitnesses) { - conformance->setTypeWitness(typeWitness.first, typeWitness.second.first, - typeWitness.second.second); + conformance->setTypeWitness(typeWitness.first, + typeWitness.second.getWitnessType(), + typeWitness.second.getWitnessDecl()); } // An imported requirement may have changed type between Swift versions. @@ -5567,7 +5823,7 @@ void ModuleFile::loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) { BCOffsetRAII restoreOffset(DeclTypeCursor); - DeclTypeCursor.JumpToBit(contextData); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(contextData)); readGenericRequirements(reqs, DeclTypeCursor); } @@ -5596,11 +5852,13 @@ Optional ModuleFile::maybeReadInlinableBodyText() { BCOffsetRAII restoreOffset(DeclTypeCursor); StringRef blobData; - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return None; - unsigned recKind = DeclTypeCursor.readRecord(next.ID, scratch, &blobData); + unsigned recKind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch, &blobData)); if (recKind != INLINABLE_BODY_TEXT) return None; @@ -5615,11 +5873,13 @@ Optional ModuleFile::maybeReadForeignErrorConvention() { BCOffsetRAII restoreOffset(DeclTypeCursor); - auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); + llvm::BitstreamEntry next = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); if (next.Kind != llvm::BitstreamEntry::Record) return None; - unsigned recKind = DeclTypeCursor.readRecord(next.ID, scratch); + unsigned recKind = + fatalIfUnexpected(DeclTypeCursor.readRecord(next.ID, scratch)); switch (recKind) { case FOREIGN_ERROR_CONVENTION: restoreOffset.reset(); diff --git a/lib/Serialization/DeserializationErrors.h b/lib/Serialization/DeserializationErrors.h index 2856b310ee4ca..318eeb9c5de9b 100644 --- a/lib/Serialization/DeserializationErrors.h +++ b/lib/Serialization/DeserializationErrors.h @@ -411,6 +411,26 @@ class SILEntityError : public llvm::ErrorInfo { } }; +// Decl was not deserialized because its attributes did not match the filter. +// +// \sa getDeclChecked +class DeclAttributesDidNotMatch : public llvm::ErrorInfo { + friend ErrorInfo; + static const char ID; + void anchor() override; + +public: + DeclAttributesDidNotMatch() {} + + void log(raw_ostream &OS) const override { + OS << "Decl attributes did not match filter"; + } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; + LLVM_NODISCARD static inline std::unique_ptr takeErrorInfo(llvm::Error error) { diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index a1866316d70e7..dff773eb2394e 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -140,7 +140,7 @@ SILDeserializer::SILDeserializer( return; // Load any abbrev records at the start of the block. - SILCursor.advance(); + MF->fatalIfUnexpected(SILCursor.advance()); llvm::BitstreamCursor cursor = SILIndexCursor; // We expect SIL_FUNC_NAMES first, then SIL_VTABLE_NAMES, then @@ -149,14 +149,15 @@ SILDeserializer::SILDeserializer( // omitted if no entries exist in the module file. unsigned kind = 0; while (kind != sil_index_block::SIL_PROPERTY_OFFSETS) { - auto next = cursor.advance(); + llvm::BitstreamEntry next = MF->fatalIfUnexpected(cursor.advance()); if (next.Kind == llvm::BitstreamEntry::EndBlock) return; SmallVector scratch; StringRef blobData; unsigned prevKind = kind; - kind = cursor.readRecord(next.ID, scratch, &blobData); + kind = + MF->fatalIfUnexpected(cursor.readRecord(next.ID, scratch, &blobData)); assert((next.Kind == llvm::BitstreamEntry::Record && kind > prevKind && (kind == sil_index_block::SIL_FUNC_NAMES || @@ -186,9 +187,10 @@ SILDeserializer::SILDeserializer( } // Read SIL_FUNC|VTABLE|GLOBALVAR_OFFSETS record. - next = cursor.advance(); + next = MF->fatalIfUnexpected(cursor.advance()); scratch.clear(); - unsigned offKind = cursor.readRecord(next.ID, scratch, &blobData); + unsigned offKind = + MF->fatalIfUnexpected(cursor.readRecord(next.ID, scratch, &blobData)); (void)offKind; if (kind == sil_index_block::SIL_FUNC_NAMES) { assert((next.Kind == llvm::BitstreamEntry::Record && @@ -266,7 +268,7 @@ SILValue SILDeserializer::getLocalValue(ValueID Id, SILType Type) { // The first two IDs are special undefined values. if (Id == 0) - return SILUndef::get(Type, SILMod, ValueOwnershipKind::Any); + return SILUndef::get(Type, SILMod, ValueOwnershipKind::None); else if (Id == 1) return SILUndef::get(Type, SILMod, ValueOwnershipKind::Owned); @@ -326,10 +328,15 @@ SILBasicBlock *SILDeserializer::getBBForReference(SILFunction *Fn, } /// Helper function to convert from Type to SILType. -static SILType getSILType(Type Ty, SILValueCategory Category) { +SILType SILDeserializer::getSILType(Type Ty, SILValueCategory Category, + SILFunction *inContext) { auto TyLoc = TypeLoc::withoutLoc(Ty); - return SILType::getPrimitiveType(TyLoc.getType()->getCanonicalType(), - Category); + if (!inContext) { + return SILType::getPrimitiveType(TyLoc.getType()->getCanonicalType(), + Category); + } + return inContext->getLoweredType(TyLoc.getType()->getCanonicalType()) + .getCategoryType(Category); } /// Helper function to find a SILFunction, given its name and type. @@ -448,9 +455,14 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, return cacheEntry.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(cacheEntry.getOffset()); - - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(cacheEntry.getOffset())) + return std::move(Err); + + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readSILFunction.\n"); MF->fatal(); @@ -458,7 +470,11 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_FUNCTION && "expect a sil function"); (void)kind; @@ -491,10 +507,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, return llvm::make_error( name, takeErrorInfo(astType.takeError())); } - llvm::consumeError(astType.takeError()); + consumeError(astType.takeError()); return existingFn; } - auto ty = getSILType(astType.get(), SILValueCategory::Object); + auto ty = getSILType(astType.get(), SILValueCategory::Object, nullptr); if (!ty.is()) { LLVM_DEBUG(llvm::dbgs() << "not a function type for SILFunction\n"); MF->fatal(); @@ -511,12 +527,13 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, replacedObjectiveCFunc = MF->getIdentifier(replacedFunctionID); } - auto linkage = fromStableSILLinkage(rawLinkage); - if (!linkage) { + auto linkageOpt = fromStableSILLinkage(rawLinkage); + if (!linkageOpt) { LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage << " for SILFunction\n"); MF->fatal(); } + SILLinkage linkage = linkageOpt.getValue(); ValueDecl *clangNodeOwner = nullptr; if (clangNodeOwnerID != 0) { @@ -553,16 +570,22 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setSerialized(IsSerialized_t(isSerialized)); if (SILMod.getOptions().MergePartialModules) - fn->setLinkage(*linkage); + fn->setLinkage(linkage); // Don't override the transparency or linkage of a function with // an existing declaration, except if we deserialized a // PublicNonABI function, which has HiddenExternal when // referenced as a declaration, and SharedExternal when it has // a deserialized body. - if (fn->getLinkage() == SILLinkage::HiddenExternal && - linkage == SILLinkage::PublicNonABI) { - fn->setLinkage(SILLinkage::SharedExternal); + if (isAvailableExternally(fn->getLinkage())) { + if (linkage == SILLinkage::PublicNonABI) { + fn->setLinkage(SILLinkage::SharedExternal); + } else if (hasPublicVisibility(linkage)) { + // Cross-module-optimization can change the linkage to public. In this + // case we need to update the linkage of the function (which is + // originally just derived from the AST). + fn->setLinkage(SILLinkage::PublicExternal); + } } if (fn->isDynamicallyReplaceable() != isDynamic) { @@ -573,7 +596,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, } else { // Otherwise, create a new function. fn = builder.createDeclaration(name, ty, loc); - fn->setLinkage(linkage.getValue()); + fn->setLinkage(linkage); fn->setTransparent(IsTransparent_t(isTransparent == 1)); fn->setSerialized(IsSerialized_t(isSerialized)); fn->setThunk(IsThunk_t(isThunk)); @@ -627,11 +650,18 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // Read and instantiate the specialize attributes. while (numSpecAttrs--) { - auto next = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeNext = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeNext) + return maybeNext.takeError(); + llvm::BitstreamEntry next = maybeNext.get(); assert(next.Kind == llvm::BitstreamEntry::Record); scratch.clear(); - kind = SILCursor.readRecord(next.ID, scratch); + llvm::Expected maybeKind = SILCursor.readRecord(next.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + unsigned kind = maybeKind.get(); assert(kind == SIL_SPECIALIZE_ATTR && "Missing specialization attribute"); unsigned exported; @@ -658,7 +688,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // If the next entry is the end of the block, then this function has // no contents. - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + entry = maybeEntry.get(); bool isEmptyFunction = (entry.Kind == llvm::BitstreamEntry::EndBlock); assert((!isEmptyFunction || !genericEnv) && "generic environment without body?!"); @@ -686,7 +719,10 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setGenericEnvironment(genericEnv); scratch.clear(); - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + kind = maybeKind.get(); SILBasicBlock *CurrentBB = nullptr; @@ -745,12 +781,19 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + return maybeEntry.takeError(); + llvm::BitstreamEntry entry = maybeEntry.get(); // EndBlock means the end of this SILFunction. if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + return maybeKind.takeError(); + kind = maybeKind.get(); } // If fn is empty, we failed to deserialize its body. Return nullptr to signal @@ -806,7 +849,7 @@ SILBasicBlock *SILDeserializer::readSILBasicBlock(SILFunction *Fn, auto ArgTy = MF->getType(TyID); SILArgument *Arg; auto ValueCategory = SILValueCategory(Args[I + 1] & 0xF); - SILType SILArgTy = getSILType(ArgTy, ValueCategory); + SILType SILArgTy = getSILType(ArgTy, ValueCategory, Fn); if (IsEntry) { Arg = CurrentBB->createFunctionArgument(SILArgTy); } else { @@ -1013,12 +1056,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ConcreteTyID, NumConformances); break; - case SIL_INST_CAST: - SILInstCastLayout::readRecord(scratch, RawOpCode, Attr, - TyID, TyCategory, - TyID2, TyCategory2, - ValID); - break; case SIL_ONE_TYPE_VALUES: SILOneTypeValuesLayout::readRecord(scratch, RawOpCode, TyID, TyCategory, ListOfValues); @@ -1101,68 +1138,68 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, break; case SILInstructionKind::AllocStackInst: assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); - ResultVal = Builder.createAllocStack(Loc, - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), - None, /*bool hasDynamicLifetime*/ Attr != 0); + ResultVal = Builder.createAllocStack( + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), + None, /*bool hasDynamicLifetime*/ Attr != 0); break; case SILInstructionKind::MetatypeInst: assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); - ResultVal = Builder.createMetatype(Loc, - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); - break; - -#define ONETYPE_ONEOPERAND_INST(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ - "Layout should be OneTypeOneOperand."); \ - ResultVal = Builder.create##ID(Loc, \ - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), \ - getLocalValue(ValID, \ - getSILType(MF->getType(TyID2), \ - (SILValueCategory)TyCategory2))); \ - break; - ONETYPE_ONEOPERAND_INST(ValueMetatype) - ONETYPE_ONEOPERAND_INST(ExistentialMetatype) - ONETYPE_ONEOPERAND_INST(AllocValueBuffer) - ONETYPE_ONEOPERAND_INST(ProjectValueBuffer) - ONETYPE_ONEOPERAND_INST(ProjectExistentialBox) - ONETYPE_ONEOPERAND_INST(DeallocValueBuffer) + ResultVal = Builder.createMetatype( + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + break; + +#define ONETYPE_ONEOPERAND_INST(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ + "Layout should be OneTypeOneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), \ + getLocalValue(ValID, getSILType(MF->getType(TyID2), \ + (SILValueCategory)TyCategory2, Fn))); \ + break; + ONETYPE_ONEOPERAND_INST(ValueMetatype) + ONETYPE_ONEOPERAND_INST(ExistentialMetatype) + ONETYPE_ONEOPERAND_INST(AllocValueBuffer) + ONETYPE_ONEOPERAND_INST(ProjectValueBuffer) + ONETYPE_ONEOPERAND_INST(ProjectExistentialBox) + ONETYPE_ONEOPERAND_INST(DeallocValueBuffer) #undef ONETYPE_ONEOPERAND_INST case SILInstructionKind::DeallocBoxInst: assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createDeallocBox(Loc, - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2))); + ResultVal = Builder.createDeallocBox( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn))); break; case SILInstructionKind::OpenExistentialAddrInst: assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); ResultVal = Builder.createOpenExistentialAddr( - Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), Attr == 0 ? OpenedExistentialAccess::Immutable : OpenedExistentialAccess::Mutable); break; -#define ONEOPERAND_ONETYPE_INST(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ - "Layout should be OneTypeOneOperand."); \ - ResultVal = Builder.create##ID(Loc, \ - getLocalValue(ValID, \ - getSILType(MF->getType(TyID2), \ - (SILValueCategory)TyCategory2)), \ - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory));\ - break; - ONEOPERAND_ONETYPE_INST(OpenExistentialRef) - ONEOPERAND_ONETYPE_INST(OpenExistentialMetatype) - ONEOPERAND_ONETYPE_INST(OpenExistentialBox) - ONEOPERAND_ONETYPE_INST(OpenExistentialValue) - ONEOPERAND_ONETYPE_INST(OpenExistentialBoxValue) - // Conversion instructions. +#define ONEOPERAND_ONETYPE_INST(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && \ + "Layout should be OneTypeOneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID2), \ + (SILValueCategory)TyCategory2, Fn)), \ + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); \ + break; + ONEOPERAND_ONETYPE_INST(OpenExistentialRef) + ONEOPERAND_ONETYPE_INST(OpenExistentialMetatype) + ONEOPERAND_ONETYPE_INST(OpenExistentialBox) + ONEOPERAND_ONETYPE_INST(OpenExistentialValue) + ONEOPERAND_ONETYPE_INST(OpenExistentialBoxValue) + // Conversion instructions. #define LOADABLE_REF_STORAGE(Name, ...) \ ONEOPERAND_ONETYPE_INST(RefTo##Name) \ ONEOPERAND_ONETYPE_INST(Name##ToRef) @@ -1190,11 +1227,11 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::ProjectBoxInst: { assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createProjectBox(Loc, - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - TyID); + ResultVal = Builder.createProjectBox( + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + TyID); break; } case SILInstructionKind::ConvertEscapeToNoEscapeInst: { @@ -1204,8 +1241,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ResultVal = Builder.createConvertEscapeToNoEscape( Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), isLifetimeGuaranteed); break; } @@ -1216,8 +1253,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, ResultVal = Builder.createConvertFunction( Loc, getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), withoutActuallyEscaping); break; } @@ -1227,29 +1264,30 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, bool isStrict = Attr & 0x01; bool isInvariant = Attr & 0x02; ResultVal = Builder.createPointerToAddress( - Loc, - getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory), - isStrict, isInvariant); + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), isStrict, + isInvariant); break; } case SILInstructionKind::DeallocExistentialBoxInst: { assert(RecordKind == SIL_ONE_TYPE_ONE_OPERAND && "Layout should be OneTypeOneOperand."); - ResultVal = Builder.createDeallocExistentialBox(Loc, - MF->getType(TyID)->getCanonicalType(), - getLocalValue(ValID, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2))); + ResultVal = Builder.createDeallocExistentialBox( + Loc, MF->getType(TyID)->getCanonicalType(), + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::RefToBridgeObjectInst: { - auto RefTy = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto RefTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Ref = getLocalValue(ValID, RefTy); - auto BitsTy = getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2); + auto BitsTy = + getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2, Fn); auto Bits = getLocalValue(ValID2, BitsTy); ResultVal = Builder.createRefToBridgeObject(Loc, Ref, Bits); @@ -1257,7 +1295,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::ObjCProtocolInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Proto = MF->getDecl(ValID); ResultVal = Builder.createObjCProtocol(Loc, cast(Proto), Ty); break; @@ -1269,15 +1307,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::InitExistentialRefInst: case SILInstructionKind::AllocExistentialBoxInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Ty2 = MF->getType(TyID2); CanType ConcreteTy; if (OpCode != SILInstructionKind::InitExistentialMetatypeInst) ConcreteTy = MF->getType(ConcreteTyID)->getCanonicalType(); SILValue operand; if (OpCode != SILInstructionKind::AllocExistentialBoxInst) - operand = getLocalValue(ValID, - getSILType(Ty2, (SILValueCategory)TyCategory2)); + operand = getLocalValue( + ValID, getSILType(Ty2, (SILValueCategory)TyCategory2, Fn)); SmallVector conformances; while (NumConformances--) { @@ -1325,16 +1363,17 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, unsigned Flags = ListOfValues[0]; bool isObjC = (bool)(Flags & 1); bool canAllocOnStack = (bool)((Flags >> 1) & 1); - SILType ClassTy = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType ClassTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SmallVector Counts; SmallVector TailTypes; unsigned i = 1; for (; i + 2 < NumVals; i += 3) { SILType TailType = getSILType(MF->getType(ListOfValues[i]), - SILValueCategory::Object); + SILValueCategory::Object, Fn); TailTypes.push_back(TailType); - SILType CountType = getSILType(MF->getType(ListOfValues[i+2]), - SILValueCategory::Object); + SILType CountType = getSILType(MF->getType(ListOfValues[i + 2]), + SILValueCategory::Object, Fn); SILValue CountVal = getLocalValue(ListOfValues[i+1], CountType); Counts.push_back(CountVal); } @@ -1342,7 +1381,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(i + 2 == NumVals); assert(!canAllocOnStack); SILType MetadataType = getSILType(MF->getType(ListOfValues[i+1]), - SILValueCategory::Object); + SILValueCategory::Object, Fn); SILValue MetadataOp = getLocalValue(ListOfValues[i], MetadataType); ResultVal = Builder.createAllocRefDynamic(Loc, MetadataOp, ClassTy, isObjC, TailTypes, Counts); @@ -1360,8 +1399,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // is represented with 2 IDs: ValueID and ValueResultNumber. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); + SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILFunctionConventions substConventions(SubstFnTy.castTo(), Builder.getModule()); assert(substConventions.getNumSILArguments() == ListOfValues.size() @@ -1390,8 +1429,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // two values in the list are the basic block identifiers. auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); + SILType SubstFnTy = getSILType(Ty2, SILValueCategory::Object, Fn); SILBasicBlock *errorBB = getBBForReference(Fn, ListOfValues.back()); ListOfValues = ListOfValues.drop_back(); @@ -1416,14 +1455,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::PartialApplyInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - SILType FnTy = getSILType(Ty, SILValueCategory::Object); - SILType closureTy = getSILType(Ty2, SILValueCategory::Object); + SILType FnTy = getSILType(Ty, SILValueCategory::Object, Fn); + SILType closureTy = getSILType(Ty2, SILValueCategory::Object, Fn); SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); auto SubstFnTy = SILType::getPrimitiveObjectType( - FnTy.castTo() - ->substGenericArgs(Builder.getModule(), Substitutions)); + FnTy.castTo()->substGenericArgs( + Builder.getModule(), Substitutions, + Builder.getTypeExpansionContext())); SILFunctionConventions fnConv(SubstFnTy.castTo(), Builder.getModule()); @@ -1448,12 +1488,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::BuiltinInst: { auto ASTTy = MF->getType(TyID); - auto ResultTy = getSILType(ASTTy, (SILValueCategory)(unsigned)TyID2); + auto ResultTy = getSILType(ASTTy, (SILValueCategory)(unsigned)TyID2, Fn); SmallVector Args; for (unsigned i = 0, e = ListOfValues.size(); i < e; i += 3) { auto ArgASTTy = MF->getType(ListOfValues[i+1]); - auto ArgTy = getSILType(ArgASTTy, - (SILValueCategory)(unsigned)ListOfValues[i+2]); + auto ArgTy = getSILType( + ArgASTTy, (SILValueCategory)(unsigned)ListOfValues[i + 2], Fn); Args.push_back(getLocalValue(ListOfValues[i], ArgTy)); } SubstitutionMap Substitutions = MF->getSubstitutionMap(NumSubs); @@ -1483,10 +1523,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Find the global variable. SILGlobalVariable *g = getGlobalForReference(Name); assert(g && "Can't deserialize global variable"); - SILType expectedType = (OpCode == SILInstructionKind::GlobalAddrInst ? - g->getLoweredType().getAddressType() : - g->getLoweredType()); - assert(expectedType == getSILType(Ty, (SILValueCategory)TyCategory) && + SILType expectedType = + (OpCode == SILInstructionKind::GlobalAddrInst + ? g->getLoweredTypeInContext(TypeExpansionContext(*Fn)) + .getAddressType() + : g->getLoweredTypeInContext(TypeExpansionContext(*Fn))); + assert(expectedType == getSILType(Ty, (SILValueCategory)TyCategory, Fn) && "Type of a global variable does not match GlobalAddr."); (void)Ty; (void)expectedType; @@ -1499,17 +1541,18 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::DeallocStackInst: { auto Ty = MF->getType(TyID); - ResultVal = Builder.createDeallocStack(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory))); + ResultVal = Builder.createDeallocStack( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn))); break; } case SILInstructionKind::DeallocRefInst: { auto Ty = MF->getType(TyID); bool OnStack = (bool)Attr; - ResultVal = Builder.createDeallocRef(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), OnStack); + ResultVal = Builder.createDeallocRef( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + OnStack); break; } case SILInstructionKind::DeallocPartialRefInst: { @@ -1517,81 +1560,87 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty2 = MF->getType(TyID2); ResultVal = Builder.createDeallocPartialRef(Loc, getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::FunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::DynamicFunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createDynamicFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::PreviousDynamicFunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); ResultVal = Builder.createPreviousDynamicFunctionRef( - Loc, getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + Loc, + getFuncForReference( + FuncName, getSILType(Ty, (SILValueCategory)TyCategory, nullptr))); break; } case SILInstructionKind::MarkDependenceInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createMarkDependence(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createMarkDependence( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::CopyBlockWithoutEscapingInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); ResultVal = Builder.createCopyBlockWithoutEscaping( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), - getLocalValue(ValID2, getSILType(Ty2, (SILValueCategory)TyCategory2))); + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + getLocalValue(ValID2, + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::IndexAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createIndexAddr(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createIndexAddr( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::TailAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); auto ResultTy = MF->getType(TyID3); - ResultVal = Builder.createTailAddr(Loc, - getLocalValue(ValID, getSILType(Ty, SILValueCategory::Address)), - getLocalValue(ValID2, getSILType(Ty2, SILValueCategory::Object)), - getSILType(ResultTy, SILValueCategory::Address)); + ResultVal = Builder.createTailAddr( + Loc, + getLocalValue(ValID, getSILType(Ty, SILValueCategory::Address, Fn)), + getLocalValue(ValID2, getSILType(Ty2, SILValueCategory::Object, Fn)), + getSILType(ResultTy, SILValueCategory::Address, Fn)); break; } case SILInstructionKind::IndexRawPointerInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); - ResultVal = Builder.createIndexRawPointer(Loc, - getLocalValue(ValID, - getSILType(Ty, (SILValueCategory)TyCategory)), + ResultVal = Builder.createIndexRawPointer( + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), getLocalValue(ValID2, - getSILType(Ty2, (SILValueCategory)TyCategory2))); + getSILType(Ty2, (SILValueCategory)TyCategory2, Fn))); break; } case SILInstructionKind::IntegerLiteralInst: { @@ -1601,9 +1650,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, bool negate = text[0] == '-'; if (negate) text = text.drop_front(); APInt value = intTy->getWidth().parse(text, 10, negate); - ResultVal = Builder.createIntegerLiteral(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - value); + ResultVal = Builder.createIntegerLiteral( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), value); break; } case SILInstructionKind::FloatLiteralInst: { @@ -1617,9 +1665,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, APFloat value(floatTy->getAPFloatSemantics(), bits); - ResultVal = Builder.createFloatLiteral(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - value); + ResultVal = Builder.createFloatLiteral( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), value); break; } case SILInstructionKind::StringLiteralInst: { @@ -1631,8 +1678,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, break; } case SILInstructionKind::CondFailInst: { - SILValue Op = getLocalValue(ValID, getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory)); + SILValue Op = getLocalValue( + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); StringRef StringVal = MF->getIdentifierText(ValID2); ResultVal = Builder.createCondFail(Loc, Op, StringVal); break; @@ -1643,47 +1690,54 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector OpList; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto EltTy = MF->getType(ListOfValues[I]); - OpList.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(EltTy, (SILValueCategory)ListOfValues[I+1]))); + OpList.push_back(getLocalValue( + ListOfValues[I + 2], + getSILType(EltTy, (SILValueCategory)ListOfValues[I + 1], Fn))); } ResultVal = Builder.createMarkFunctionEscape(Loc, OpList); break; } // Checked Conversion instructions. case SILInstructionKind::UnconditionalCheckedCastInst: { - SILValue Val = getLocalValue(ValID, - getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); - ResultVal = Builder.createUnconditionalCheckedCast(Loc, Val, Ty); - break; - } - -#define UNARY_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_OPERAND && \ - "Layout should be OneOperand."); \ - ResultVal = Builder.create##ID(Loc, getLocalValue(ValID, \ - getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory))); \ - break; - -#define REFCOUNTING_INSTRUCTION(ID) \ - case SILInstructionKind::ID##Inst: \ - assert(RecordKind == SIL_ONE_OPERAND && \ - "Layout should be OneOperand."); \ - ResultVal = Builder.create##ID(Loc, getLocalValue(ValID, \ - getSILType(MF->getType(TyID), \ - (SILValueCategory)TyCategory)), \ - (Atomicity)Attr); \ - break; - -#define UNCHECKED_REF_STORAGE(Name, ...) UNARY_INSTRUCTION(Copy##Name##Value) -#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - REFCOUNTING_INSTRUCTION(Name##Retain) \ - REFCOUNTING_INSTRUCTION(Name##Release) \ - REFCOUNTING_INSTRUCTION(StrongRetain##Name) \ - UNARY_INSTRUCTION(Copy##Name##Value) + SILType srcLoweredType = getSILType(MF->getType(ListOfValues[1]), + (SILValueCategory)ListOfValues[2], Fn); + SILValue src = getLocalValue(ListOfValues[0], srcLoweredType); + + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + CanType targetFormalType = + MF->getType(ListOfValues[3])->getCanonicalType(); + ResultVal = Builder.createUnconditionalCheckedCast( + Loc, src, targetLoweredType, targetFormalType); + break; + } + +#define UNARY_INSTRUCTION(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID), \ + (SILValueCategory)TyCategory, Fn))); \ + break; + +#define REFCOUNTING_INSTRUCTION(ID) \ + case SILInstructionKind::ID##Inst: \ + assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); \ + ResultVal = Builder.create##ID( \ + Loc, \ + getLocalValue(ValID, getSILType(MF->getType(TyID), \ + (SILValueCategory)TyCategory, Fn)), \ + (Atomicity)Attr); \ + break; + +#define UNCHECKED_REF_STORAGE(Name, ...) \ + UNARY_INSTRUCTION(StrongCopy##Name##Value) +#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + REFCOUNTING_INSTRUCTION(Name##Retain) \ + REFCOUNTING_INSTRUCTION(Name##Release) \ + REFCOUNTING_INSTRUCTION(StrongRetain##Name) \ + UNARY_INSTRUCTION(StrongCopy##Name##Value) #include "swift/AST/ReferenceStorage.def" REFCOUNTING_INSTRUCTION(RetainValue) REFCOUNTING_INSTRUCTION(RetainValueAddr) @@ -1722,8 +1776,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, unsigned verificationType = Attr; ResultVal = Builder.createIsEscapingClosure( Loc, - getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)), + getLocalValue(ValID, getSILType(MF->getType(TyID), + (SILValueCategory)TyCategory, Fn)), verificationType); break; } @@ -1731,14 +1785,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::DestructureTupleInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); SILValue Operand = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); ResultVal = Builder.createDestructureTuple(Loc, Operand); break; } case SILInstructionKind::DestructureStructInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); SILValue Operand = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); ResultVal = Builder.createDestructureStruct(Loc, Operand); break; } @@ -1746,7 +1800,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty = MF->getType(TyID); auto ResultKind = ValueOwnershipKind(Attr); ResultVal = Builder.createUncheckedOwnershipConversion( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), ResultKind); break; } @@ -1755,35 +1810,35 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto Ty = MF->getType(TyID); auto Qualifier = LoadOwnershipQualifier(Attr); ResultVal = Builder.createLoad( - Loc, getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory)), + Loc, + getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), Qualifier); break; } -#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Load##Name##Inst: { \ - auto Ty = MF->getType(TyID); \ - bool isTake = (Attr > 0); \ - auto Val = getLocalValue(ValID, getSILType(Ty, \ - SILValueCategory(TyCategory)));\ - ResultVal = Builder.createLoad##Name(Loc, Val, IsTake_t(isTake)); \ - break; \ - } \ - case SILInstructionKind::Store##Name##Inst: { \ - auto Ty = MF->getType(TyID); \ - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); \ - auto refType = addrType.castTo(); \ +#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Load##Name##Inst: { \ + auto Ty = MF->getType(TyID); \ + bool isTake = (Attr > 0); \ + auto Val = getLocalValue( \ + ValID, getSILType(Ty, SILValueCategory(TyCategory), Fn)); \ + ResultVal = Builder.createLoad##Name(Loc, Val, IsTake_t(isTake)); \ + break; \ + } \ + case SILInstructionKind::Store##Name##Inst: { \ + auto Ty = MF->getType(TyID); \ + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); \ + auto refType = addrType.castTo(); \ auto ValType = SILType::getPrimitiveObjectType(refType.getReferentType()); \ - bool isInit = (Attr > 0); \ - ResultVal = Builder.createStore##Name(Loc, \ - getLocalValue(ValID, ValType), \ - getLocalValue(ValID2, addrType), \ - IsInitialization_t(isInit)); \ - break; \ + bool isInit = (Attr > 0); \ + ResultVal = Builder.createStore##Name(Loc, getLocalValue(ValID, ValType), \ + getLocalValue(ValID2, addrType), \ + IsInitialization_t(isInit)); \ + break; \ } #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::MarkUninitializedInst: { - auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + auto Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto Kind = (MarkUninitializedInst::Kind)Attr; auto Val = getLocalValue(ValID, Ty); ResultVal = Builder.createMarkUninitialized(Loc, Val, Kind); @@ -1791,7 +1846,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::StoreInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType ValType = addrType.getObjectType(); auto Qualifier = StoreOwnershipQualifier(Attr); ResultVal = Builder.createStore(Loc, getLocalValue(ValID, ValType), @@ -1800,7 +1855,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::StoreBorrowInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType ValType = addrType.getObjectType(); ResultVal = Builder.createStoreBorrow(Loc, getLocalValue(ValID, ValType), getLocalValue(ValID2, addrType)); @@ -1808,7 +1863,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::BeginAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x3); bool noNestedConflict = (Attr >> 4) & 0x01; @@ -1820,16 +1875,17 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::EndAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); bool aborted = Attr & 0x1; ResultVal = Builder.createEndAccess(Loc, op, aborted); break; } case SILInstructionKind::BeginUnpairedAccessInst: { SILValue source = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); - SILValue buffer = getLocalValue( - ValID2, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); + SILValue buffer = + getLocalValue(ValID2, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)); auto accessKind = SILAccessKind(Attr & 0x3); auto enforcement = SILAccessEnforcement((Attr >> 2) & 0x03); bool noNestedConflict = (Attr >> 4) & 0x01; @@ -1841,7 +1897,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::EndUnpairedAccessInst: { SILValue op = getLocalValue( - ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); bool aborted = Attr & 0x1; auto enforcement = SILAccessEnforcement((Attr >> 1) & 0x03); bool fromBuiltin = (Attr >> 3) & 0x01; @@ -1851,7 +1907,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::CopyAddrInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); bool isInit = (Attr & 0x2) > 0; bool isTake = (Attr & 0x1) > 0; ResultVal = Builder.createCopyAddr(Loc, @@ -1863,7 +1919,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::AssignInst: { auto Ty = MF->getType(TyID); - SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory); + SILType addrType = getSILType(Ty, (SILValueCategory)TyCategory, Fn); SILType valType = addrType.getObjectType(); auto qualifier = AssignOwnershipQualifier(Attr); ResultVal = Builder.createAssign(Loc, @@ -1879,14 +1935,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, "Layout should be OneTypeValues."); auto Ty = MF->getType(TyID); // BoundTy ResultVal = Builder.createBindMemory( - Loc, - getLocalValue(ListOfValues[2], - getSILType(MF->getType(ListOfValues[0]), - (SILValueCategory)ListOfValues[1])), - getLocalValue(ListOfValues[5], - getSILType(MF->getType(ListOfValues[3]), - (SILValueCategory)ListOfValues[4])), - getSILType(Ty, (SILValueCategory)TyCategory)); + Loc, + getLocalValue(ListOfValues[2], + getSILType(MF->getType(ListOfValues[0]), + (SILValueCategory)ListOfValues[1], Fn)), + getLocalValue(ListOfValues[5], + getSILType(MF->getType(ListOfValues[3]), + (SILValueCategory)ListOfValues[4], Fn)), + getSILType(Ty, (SILValueCategory)TyCategory, Fn)); break; } case SILInstructionKind::StructElementAddrInst: @@ -1894,9 +1950,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Use SILOneValueOneOperandLayout. VarDecl *Field = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); - auto Val = getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + auto Val = + getLocalValue(ValID2, getSILType(Ty, (SILValueCategory)TyCategory, Fn)); + auto ResultTy = Val->getType().getFieldType( + Field, SILMod, Builder.getTypeExpansionContext()); if (OpCode == SILInstructionKind::StructElementAddrInst) ResultVal = Builder.createStructElementAddr(Loc, Val, Field, ResultTy.getAddressType()); @@ -1912,35 +1969,33 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector OpList; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto EltTy = MF->getType(ListOfValues[I]); - OpList.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(EltTy, (SILValueCategory)ListOfValues[I+1]))); + OpList.push_back(getLocalValue( + ListOfValues[I + 2], + getSILType(EltTy, (SILValueCategory)ListOfValues[I + 1], Fn))); } - ResultVal = Builder.createStruct(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - OpList); + ResultVal = Builder.createStruct( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), OpList); break; } case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::TupleExtractInst: { // Use OneTypeOneOperand layout where the field number is stored in TypeID. auto Ty2 = MF->getType(TyID2); - SILType ST = getSILType(Ty2, (SILValueCategory)TyCategory2); + SILType ST = getSILType(Ty2, (SILValueCategory)TyCategory2, Fn); TupleType *TT = ST.castTo(); auto ResultTy = TT->getElement(TyID).getType(); switch (OpCode) { default: llvm_unreachable("Out of sync with parent switch"); case SILInstructionKind::TupleElementAddrInst: - ResultVal = Builder.createTupleElementAddr(Loc, - getLocalValue(ValID, ST), - TyID, getSILType(ResultTy, SILValueCategory::Address)); + ResultVal = Builder.createTupleElementAddr( + Loc, getLocalValue(ValID, ST), TyID, + getSILType(ResultTy, SILValueCategory::Address, Fn)); break; case SILInstructionKind::TupleExtractInst: - ResultVal = Builder.createTupleExtract(Loc, - getLocalValue(ValID,ST), - TyID, - getSILType(ResultTy, SILValueCategory::Object)); + ResultVal = Builder.createTupleExtract( + Loc, getLocalValue(ValID, ST), TyID, + getSILType(ResultTy, SILValueCategory::Object, Fn)); break; } break; @@ -1956,11 +2011,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, Type EltTy = TT->getElement(I).getType(); OpList.push_back( getLocalValue(ListOfValues[I], - getSILType(EltTy, SILValueCategory::Object))); + getSILType(EltTy, SILValueCategory::Object, Fn))); } - ResultVal = Builder.createTuple(Loc, - getSILType(Ty, (SILValueCategory)TyCategory), - OpList); + ResultVal = Builder.createTuple( + Loc, getSILType(Ty, (SILValueCategory)TyCategory, Fn), OpList); break; } case SILInstructionKind::ObjectInst: { @@ -1970,9 +2024,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector Args; for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) Args.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); ResultVal = Builder.createBranch(Loc, getBBForReference(Fn, TyID), Args); @@ -1984,9 +2038,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list has value for condition, true basic block ID, // false basic block ID, number of true arguments, and a list of true|false // arguments. - SILValue Cond = getLocalValue(ListOfValues[0], - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); unsigned NumTrueArgs = ListOfValues[3]; unsigned StartOfTrueArg = 4; @@ -1994,16 +2048,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SmallVector TrueArgs; for (unsigned I = StartOfTrueArg, E = StartOfFalseArg; I < E; I += 3) TrueArgs.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); SmallVector FalseArgs; for (unsigned I = StartOfFalseArg, E = ListOfValues.size(); I < E; I += 3) FalseArgs.push_back( - getLocalValue(ListOfValues[I+2], - getSILType(MF->getType(ListOfValues[I]), - (SILValueCategory)ListOfValues[I+1]))); + getLocalValue(ListOfValues[I + 2], + getSILType(MF->getType(ListOfValues[I]), + (SILValueCategory)ListOfValues[I + 1], Fn))); ResultVal = Builder.createCondBranch(Loc, Cond, getBBForReference(Fn, ListOfValues[1]), TrueArgs, @@ -2016,9 +2070,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // default basic block ID. Use SILOneTypeValuesLayout: the type is // for condition, the list has value for condition, hasDefault, default // basic block ID, a list of (DeclID, BasicBlock ID). - SILValue Cond = getLocalValue(ListOfValues[0], - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); SILBasicBlock *DefaultBB = nullptr; if (ListOfValues[1]) @@ -2045,12 +2099,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // basic block ID, a list of (DeclID, BasicBlock ID). SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + (SILValueCategory)TyCategory, Fn)); Type ResultLoweredTy = MF->getType(ListOfValues[1]); SILValueCategory ResultCategory = (SILValueCategory)ListOfValues[2]; - SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory); - + SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory, Fn); + SILValue DefaultVal = nullptr; if (ListOfValues[3]) DefaultVal = getLocalValue(ListOfValues[4], ResultTy); @@ -2075,9 +2129,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list contains value for condition, hasDefault, default // basic block ID, a list of (Value ID, BasicBlock ID). SILType ResultTy = getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory); - SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + (SILValueCategory)TyCategory, Fn); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); SILBasicBlock *DefaultBB = nullptr; if (ListOfValues[1]) @@ -2097,12 +2152,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // for condition, the list has value for condition, result type, // hasDefault, default, // basic block ID, a list of (Value ID, Value ID). - SILValue Cond = getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + SILValue Cond = getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); Type ResultLoweredTy = MF->getType(ListOfValues[1]); SILValueCategory ResultCategory = (SILValueCategory)ListOfValues[2]; - SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory); + SILType ResultTy = getSILType(ResultLoweredTy, ResultCategory, Fn); SILValue DefaultVal = nullptr; if (ListOfValues[3]) @@ -2124,21 +2180,21 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // (DeclID + hasOperand), and an operand. SILValue Operand; if (Attr) - Operand = getLocalValue(ValID2, - getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)); - ResultVal = Builder.createEnum(Loc, Operand, - cast(MF->getDecl(ValID)), - getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)); + Operand = + getLocalValue(ValID2, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)); + ResultVal = Builder.createEnum( + Loc, Operand, cast(MF->getDecl(ValID)), + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)); break; } case SILInstructionKind::InitEnumDataAddrInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + (SILValueCategory) TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createInitEnumDataAddr(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2147,9 +2203,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::UncheckedEnumDataInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); - SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + SILType OperandTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createUncheckedEnumData(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2158,9 +2215,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); - SILType OperandTy = getSILType(MF->getType(TyID), - (SILValueCategory) TyCategory); - SILType ResultTy = OperandTy.getEnumElementType(Elt, SILMod); + SILType OperandTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILType ResultTy = OperandTy.getEnumElementType( + Elt, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createUncheckedTakeEnumDataAddr(Loc, getLocalValue(ValID2, OperandTy), Elt, ResultTy); @@ -2170,10 +2228,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // Use SILOneValueOneOperandLayout. EnumElementDecl *Elt = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); - ResultVal = Builder.createInjectEnumAddr(Loc, - getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)), - Elt); + ResultVal = Builder.createInjectEnumAddr( + Loc, + getLocalValue(ValID2, getSILType(Ty, (SILValueCategory)TyCategory, Fn)), + Elt); break; } case SILInstructionKind::RefElementAddrInst: { @@ -2181,8 +2239,9 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, VarDecl *Field = cast(MF->getDecl(ValID)); auto Ty = MF->getType(TyID); auto Val = getLocalValue(ValID2, - getSILType(Ty, (SILValueCategory)TyCategory)); - auto ResultTy = Val->getType().getFieldType(Field, SILMod); + getSILType(Ty, (SILValueCategory)TyCategory, Fn)); + auto ResultTy = Val->getType().getFieldType( + Field, SILMod, Builder.getTypeExpansionContext()); ResultVal = Builder.createRefElementAddr(Loc, Val, Field, ResultTy); break; @@ -2193,10 +2252,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, assert(Attr == 0); assert((SILValueCategory)TyCategory == SILValueCategory::Address); ResultVal = Builder.createRefTailAddr( - Loc, - getLocalValue(ValID, getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2)), - getSILType(MF->getType(TyID), SILValueCategory::Address)); + Loc, + getLocalValue(ValID, getSILType(MF->getType(TyID2), + (SILValueCategory)TyCategory2, Fn)), + getSILType(MF->getType(TyID), SILValueCategory::Address, Fn)); break; } case SILInstructionKind::ClassMethodInst: @@ -2207,11 +2266,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, // type, Attr, SILDeclRef (DeclID, Kind, uncurryLevel), and an operand. unsigned NextValueIndex = 0; SILDeclRef DRef = getSILDeclRef(MF, ListOfValues, NextValueIndex); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType Ty = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); assert(ListOfValues.size() >= NextValueIndex + 2 && "Out of entries for MethodInst"); - SILType operandTy = getSILType(MF->getType(ListOfValues[NextValueIndex]), - (SILValueCategory)ListOfValues[NextValueIndex+1]); + SILType operandTy = + getSILType(MF->getType(ListOfValues[NextValueIndex]), + (SILValueCategory)ListOfValues[NextValueIndex + 1], Fn); NextValueIndex += 2; switch (OpCode) { @@ -2246,15 +2307,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, "Out of entries for MethodInst"); CanType Ty = MF->getType(TyID)->getCanonicalType(); - SILType OperandTy = getSILType(MF->getType(TyID2), - (SILValueCategory)TyCategory2); + SILType OperandTy = + getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2, Fn); auto Conformance = MF->readConformance(SILCursor); // Read the optional opened existential. SILValue ExistentialOperand; if (TyID3) { SILType ExistentialOperandTy = - getSILType(MF->getType(TyID3), (SILValueCategory)TyCategory3); + getSILType(MF->getType(TyID3), (SILValueCategory)TyCategory3, Fn); if (ValID3) ExistentialOperand = getLocalValue(ValID3, ExistentialOperandTy); } @@ -2269,89 +2330,100 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, SILDeclRef DRef = getSILDeclRef(MF, ListOfValues, NextValueIndex); assert(ListOfValues.size() == NextValueIndex + 2 && "Wrong number of entries for DynamicMethodBranchInst"); - ResultVal = Builder.createDynamicMethodBranch(Loc, - getLocalValue(ListOfValues[0], getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory)), - DRef, getBBForReference(Fn, ListOfValues[NextValueIndex]), - getBBForReference(Fn, ListOfValues[NextValueIndex+1])); + ResultVal = Builder.createDynamicMethodBranch( + Loc, + getLocalValue( + ListOfValues[0], + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn)), + DRef, getBBForReference(Fn, ListOfValues[NextValueIndex]), + getBBForReference(Fn, ListOfValues[NextValueIndex + 1])); break; } case SILInstructionKind::CheckedCastBranchInst: { // Format: the cast kind, a typed value, a BasicBlock ID for success, // a BasicBlock ID for failure. Uses SILOneTypeValuesLayout. - assert(ListOfValues.size() == 6 && - "expect 7 numbers for CheckedCastBranchInst"); bool isExact = ListOfValues[0] != 0; SILType opTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); + (SILValueCategory)ListOfValues[3], Fn); SILValue op = getLocalValue(ListOfValues[1], opTy); - SILType castTy = getSILType(MF->getType(TyID), - (SILValueCategory)TyCategory); - auto *successBB = getBBForReference(Fn, ListOfValues[4]); - auto *failureBB = getBBForReference(Fn, ListOfValues[5]); + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + CanType targetFormalType = + MF->getType(ListOfValues[4])->getCanonicalType(); + auto *successBB = getBBForReference(Fn, ListOfValues[5]); + auto *failureBB = getBBForReference(Fn, ListOfValues[6]); - ResultVal = Builder.createCheckedCastBranch(Loc, isExact, op, castTy, - successBB, failureBB); + ResultVal = Builder.createCheckedCastBranch( + Loc, isExact, op, targetLoweredType, targetFormalType, + successBB, failureBB); break; } case SILInstructionKind::CheckedCastValueBranchInst: { - // Format: the cast kind, a typed value, a BasicBlock ID for success, - // a BasicBlock ID for failure. Uses SILOneTypeValuesLayout. - assert(ListOfValues.size() == 5 && - "expect 6 numbers for CheckedCastValueBranchInst"); - SILType opTy = getSILType(MF->getType(ListOfValues[1]), - (SILValueCategory)ListOfValues[2]); - SILValue op = getLocalValue(ListOfValues[0], opTy); - SILType castTy = - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); - auto *successBB = getBBForReference(Fn, ListOfValues[3]); - auto *failureBB = getBBForReference(Fn, ListOfValues[4]); - - ResultVal = Builder.createCheckedCastValueBranch(Loc, op, castTy, successBB, - failureBB); + CanType srcFormalType = MF->getType(ListOfValues[0])->getCanonicalType(); + SILType srcLoweredType = getSILType(MF->getType(ListOfValues[2]), + (SILValueCategory)ListOfValues[3], Fn); + SILValue op = getLocalValue(ListOfValues[1], srcLoweredType); + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + CanType targetFormalType = + MF->getType(ListOfValues[4])->getCanonicalType(); + auto *successBB = getBBForReference(Fn, ListOfValues[5]); + auto *failureBB = getBBForReference(Fn, ListOfValues[6]); + + ResultVal = Builder.createCheckedCastValueBranch( + Loc, op, srcFormalType, targetLoweredType, targetFormalType, + successBB, failureBB); break; } case SILInstructionKind::UnconditionalCheckedCastValueInst: { - SILValue Val = getLocalValue( - ValID, getSILType(MF->getType(TyID2), (SILValueCategory)TyCategory2)); - SILType Ty = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); - ResultVal = Builder.createUnconditionalCheckedCastValue(Loc, Val, Ty); + CanType srcFormalType = MF->getType(ListOfValues[0])->getCanonicalType(); + SILType srcLoweredType = getSILType(MF->getType(ListOfValues[2]), + (SILValueCategory)ListOfValues[3], Fn); + SILValue src = getLocalValue(ListOfValues[1], srcLoweredType); + + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + CanType targetFormalType = MF->getType(ListOfValues[4])->getCanonicalType(); + ResultVal = Builder.createUnconditionalCheckedCastValue(Loc, src, srcFormalType, + targetLoweredType, + targetFormalType); break; } case SILInstructionKind::UnconditionalCheckedCastAddrInst: { // ignore attr. - CanType sourceType = MF->getType(ListOfValues[0])->getCanonicalType(); - SILType srcAddrTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); - SILValue src = getLocalValue(ListOfValues[1], srcAddrTy); + CanType srcFormalType = MF->getType(ListOfValues[0])->getCanonicalType(); + SILType srcLoweredType = getSILType(MF->getType(ListOfValues[2]), + (SILValueCategory)ListOfValues[3], Fn); + SILValue src = getLocalValue(ListOfValues[1], srcLoweredType); - CanType targetType = MF->getType(ListOfValues[4])->getCanonicalType(); - SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); - SILValue dest = getLocalValue(ListOfValues[5], destAddrTy); + CanType targetFormalType = MF->getType(ListOfValues[4])->getCanonicalType(); + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILValue dest = getLocalValue(ListOfValues[5], targetLoweredType); - ResultVal = Builder.createUnconditionalCheckedCastAddr(Loc, src, sourceType, - dest, targetType); + ResultVal = Builder.createUnconditionalCheckedCastAddr(Loc, src, srcFormalType, + dest, targetFormalType); break; } case SILInstructionKind::CheckedCastAddrBranchInst: { CastConsumptionKind consumption = getCastConsumptionKind(ListOfValues[0]); - CanType sourceType = MF->getType(ListOfValues[1])->getCanonicalType(); - SILType srcAddrTy = getSILType(MF->getType(ListOfValues[3]), - (SILValueCategory)ListOfValues[4]); - SILValue src = getLocalValue(ListOfValues[2], srcAddrTy); + CanType srcFormalType = MF->getType(ListOfValues[1])->getCanonicalType(); + SILType srcLoweredType = getSILType(MF->getType(ListOfValues[3]), + (SILValueCategory)ListOfValues[4], Fn); + SILValue src = getLocalValue(ListOfValues[2], srcLoweredType); - CanType targetType = MF->getType(ListOfValues[5])->getCanonicalType(); - SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory) TyCategory); - SILValue dest = getLocalValue(ListOfValues[6], destAddrTy); + CanType targetFormalType = + MF->getType(ListOfValues[5])->getCanonicalType(); + SILType targetLoweredType = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); + SILValue dest = getLocalValue(ListOfValues[6], targetLoweredType); auto *successBB = getBBForReference(Fn, ListOfValues[7]); auto *failureBB = getBBForReference(Fn, ListOfValues[8]); ResultVal = Builder.createCheckedCastAddrBranch(Loc, consumption, - src, sourceType, - dest, targetType, + src, srcFormalType, + dest, targetFormalType, successBB, failureBB); break; } @@ -2359,12 +2431,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, CanType sourceType = MF->getType(ListOfValues[0])->getCanonicalType(); // ignore attr. SILType srcAddrTy = getSILType(MF->getType(ListOfValues[2]), - (SILValueCategory)ListOfValues[3]); + (SILValueCategory)ListOfValues[3], Fn); SILValue src = getLocalValue(ListOfValues[1], srcAddrTy); CanType targetType = MF->getType(ListOfValues[4])->getCanonicalType(); SILType destAddrTy = - getSILType(MF->getType(TyID), (SILValueCategory) TyCategory); + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILValue dest = getLocalValue(ListOfValues[5], destAddrTy); ResultVal = Builder.createUncheckedRefCastAddr(Loc, src, sourceType, @@ -2374,16 +2446,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::InitBlockStorageHeaderInst: { assert(ListOfValues.size() == 5 && "expected 5 values for InitBlockStorageHeader"); - SILType blockTy - = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType blockTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); SILType storageTy = getSILType(MF->getType(ListOfValues[1]), - SILValueCategory::Address); + SILValueCategory::Address, Fn); SILValue storage = getLocalValue(ListOfValues[0], storageTy); - SILType invokeTy = getSILType(MF->getType(ListOfValues[3]), - SILValueCategory::Object); + SILType invokeTy = + getSILType(MF->getType(ListOfValues[3]), SILValueCategory::Object, Fn); SILValue invoke = getLocalValue(ListOfValues[2], invokeTy); @@ -2411,8 +2483,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, for (unsigned I = 0, E = ListOfValues.size(); I < E; I += 3) { auto valueTy = MF->getType(ListOfValues[I]); auto valueCategory = (SILValueCategory) ListOfValues[I+1]; - yieldedValues.push_back( - getLocalValue(ListOfValues[I+2], getSILType(valueTy, valueCategory))); + yieldedValues.push_back(getLocalValue( + ListOfValues[I + 2], getSILType(valueTy, valueCategory, Fn))); } ResultVal = Builder.createYield(Loc, yieldedValues, resumeBB, unwindBB); @@ -2420,8 +2492,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, } case SILInstructionKind::KeyPathInst: { unsigned nextValue = 0; - SILType kpTy - = getSILType(MF->getType(TyID), (SILValueCategory)TyCategory); + SILType kpTy = + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn); auto rootTy = MF->getType(ListOfValues[nextValue++]); auto valueTy = MF->getType(ListOfValues[nextValue++]); @@ -2449,8 +2521,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, CanGenericSignature sig = CanGenericSignature(); if (!genericParams.empty() || !requirements.empty()) sig = GenericSignature::get(genericParams, requirements) - ->getCanonicalSignature(); - + .getCanonicalSignature(); + auto pattern = KeyPathPattern::get(SILMod, sig, rootTy->getCanonicalType(), valueTy->getCanonicalType(), @@ -2464,7 +2536,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, auto opValue = ListOfValues[nextValue++]; auto opTy = MF->getType(ListOfValues[nextValue++]); auto opCat = (SILValueCategory)ListOfValues[nextValue++]; - operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat))); + operands.push_back(getLocalValue(opValue, getSILType(opTy, opCat, Fn))); } ResultVal = Builder.createKeyPath(Loc, pattern, subMap, operands, kpTy); @@ -2480,7 +2552,8 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, return false; } -SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc) { +SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc, + bool onlyUpdateLinkage) { StringRef name = InFunc->getName(); if (!FuncTable) return nullptr; @@ -2488,11 +2561,12 @@ SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc) { if (iter == FuncTable->end()) return nullptr; + // Re-reading the function as declaration will update the linkage. auto maybeFunc = readSILFunctionChecked(*iter, InFunc, name, - /*declarationOnly*/ false); + /*declarationOnly*/ onlyUpdateLinkage); if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); return nullptr; } @@ -2525,9 +2599,14 @@ bool SILDeserializer::hasSILFunction(StringRef Name, return !Linkage || cacheEntry.get()->getLinkage() == *Linkage; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(cacheEntry.getOffset()); - - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(cacheEntry.getOffset())) + MF->fatal(std::move(Err)); + + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in hasSILFunction.\n"); MF->fatal(); @@ -2535,7 +2614,11 @@ bool SILDeserializer::hasSILFunction(StringRef Name, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_FUNCTION && "expect a sil function"); (void)kind; @@ -2589,7 +2672,7 @@ SILFunction *SILDeserializer::lookupSILFunction(StringRef name, if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); return nullptr; } @@ -2624,8 +2707,13 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { return globalVarOrOffset; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(globalVarOrOffset); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(globalVarOrOffset)) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readGlobalVar.\n"); return nullptr; @@ -2633,7 +2721,11 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_GLOBALVAR && "expect a sil global var"); (void)kind; @@ -2658,7 +2750,7 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { SILGlobalVariable *v = SILGlobalVariable::create( SILMod, linkage.getValue(), isSerialized ? IsSerialized : IsNotSerialized, - Name.str(), getSILType(Ty, SILValueCategory::Object), + Name.str(), getSILType(Ty, SILValueCategory::Object, nullptr), None, dID ? cast(MF->getDecl(dID)): nullptr); v->setLet(IsLet); @@ -2697,7 +2789,7 @@ void SILDeserializer::getAllSILFunctions() { false/*errorIfEmptyBody*/); if (!maybeFunc) { // Ignore the error; treat it as if we didn't have a definition. - llvm::consumeError(maybeFunc.takeError()); + consumeError(maybeFunc.takeError()); } } } @@ -2712,8 +2804,13 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { return vTableOrOffset; BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(vTableOrOffset); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(vTableOrOffset)) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readVTable.\n"); return nullptr; @@ -2721,7 +2818,11 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_VTABLE && "expect a sil vtable"); (void)kind; @@ -2739,11 +2840,17 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // This vtable has no contents. return nullptr; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); std::vector vtableEntries; // Another SIL_VTABLE record means the end of this VTable. @@ -2769,11 +2876,17 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // EndBlock means the end of this VTable. break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); } // If we've already serialized the module, don't mark the witness table @@ -2819,8 +2932,13 @@ SILProperty *SILDeserializer::readProperty(DeclID PId) { return propOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(propOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(propOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readProperty.\n"); return nullptr; @@ -2828,7 +2946,11 @@ SILProperty *SILDeserializer::readProperty(DeclID PId) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_PROPERTY && "expect a sil_property"); (void)kind; @@ -2858,7 +2980,10 @@ void SILDeserializer::readWitnessTableEntries( std::vector &conditionalConformances) { SmallVector scratch; - unsigned kind = SILCursor.readRecord(entry.ID, scratch); + llvm::Expected maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); // Another record means the end of this WitnessTable. while (kind != SIL_WITNESS_TABLE && @@ -2920,11 +3045,18 @@ void SILDeserializer::readWitnessTableEntries( // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) // EndBlock means the end of this WitnessTable. break; - kind = SILCursor.readRecord(entry.ID, scratch); + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); } } @@ -2940,8 +3072,13 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, return wTableOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(wTableOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(wTableOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in readWitnessTable.\n"); return nullptr; @@ -2949,7 +3086,11 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_WITNESS_TABLE && "expect a sil witnesstable"); (void)kind; @@ -3016,7 +3157,10 @@ SILWitnessTable *SILDeserializer::readWitnessTable(DeclID WId, // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) return nullptr; @@ -3083,8 +3227,13 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { return wTableOrOffset.get(); BCOffsetRAII restoreOffset(SILCursor); - SILCursor.JumpToBit(wTableOrOffset.getOffset()); - auto entry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (llvm::Error Err = SILCursor.JumpToBit(wTableOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::Error) { LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in " "readDefaultWitnessTable.\n"); @@ -3093,7 +3242,11 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { SmallVector scratch; StringRef blobData; - unsigned kind = SILCursor.readRecord(entry.ID, scratch, &blobData); + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); assert(kind == SIL_DEFAULT_WITNESS_TABLE && "expect a sil default witness table"); (void)kind; @@ -3140,7 +3293,10 @@ readDefaultWitnessTable(DeclID WId, SILDefaultWitnessTable *existingWt) { // Fetch the next record. scratch.clear(); - entry = SILCursor.advance(AF_DontPopBlockAtEnd); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) return nullptr; diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index c2790c01e48c2..24147978b6bba 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -110,6 +110,9 @@ namespace swift { SILValue getLocalValue(serialization::ValueID Id, SILType Type); + SILType getSILType(Type ty, SILValueCategory category, + SILFunction *inContext); + SILFunction *getFuncForReference(StringRef Name, SILType Ty); SILFunction *getFuncForReference(StringRef Name); SILVTable *readVTable(serialization::DeclID); @@ -137,7 +140,7 @@ namespace swift { FileUnit *getFile() const { return MF->getFile(); } - SILFunction *lookupSILFunction(SILFunction *InFunc); + SILFunction *lookupSILFunction(SILFunction *InFunc, bool onlyUpdateLinkage); SILFunction *lookupSILFunction(StringRef Name, bool declarationOnly = false); bool hasSILFunction(StringRef Name, Optional Linkage = None); diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index f9871952f6de7..510a62db0c63b 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -44,16 +44,31 @@ static_assert(IsTriviallyDestructible::value, static bool checkModuleSignature(llvm::BitstreamCursor &cursor, ArrayRef signature) { - for (unsigned char byte : signature) - if (cursor.AtEndOfStream() || cursor.Read(8) != byte) + for (unsigned char byte : signature) { + if (cursor.AtEndOfStream()) return false; + if (Expected maybeRead = + cursor.Read(8)) { + if (maybeRead.get() != byte) + return false; + } else { + consumeError(maybeRead.takeError()); + return false; + } + } return true; } static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID, bool shouldReadBlockInfo = true) { - auto next = cursor.advance(); + Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); if (next.Kind != llvm::BitstreamEntry::SubBlock) return false; @@ -72,7 +87,12 @@ static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, if (next.ID != ID) return false; - cursor.EnterSubBlock(ID); + if (llvm::Error Err = cursor.EnterSubBlock(ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } + return true; } @@ -83,7 +103,13 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch, ExtendedValidationInfo &extendedInfo) { while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -100,7 +126,14 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case options_block::SDK_PATH: extendedInfo.setSDKPath(blobData); @@ -145,7 +178,14 @@ validateControlBlock(llvm::BitstreamCursor &cursor, bool versionSeen = false; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + result.status = Status::Malformed; + return result; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -156,7 +196,12 @@ validateControlBlock(llvm::BitstreamCursor &cursor, if (entry.Kind == llvm::BitstreamEntry::SubBlock) { if (entry.ID == OPTIONS_BLOCK_ID && extendedInfo) { - cursor.EnterSubBlock(OPTIONS_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(OPTIONS_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } if (!readOptionsBlock(cursor, scratch, *extendedInfo)) { result.status = Status::Malformed; return result; @@ -174,7 +219,15 @@ validateControlBlock(llvm::BitstreamCursor &cursor, scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + result.status = Status::Malformed; + return result; + } + unsigned kind = maybeKind.get(); switch (kind) { case control_block::METADATA: { if (versionSeen) { @@ -247,7 +300,13 @@ static bool validateInputBlock( SmallString<256> dependencyFullPathBuffer; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return true; + } + llvm::BitstreamEntry entry = maybeEntry.get(); if (entry.Kind == llvm::BitstreamEntry::EndBlock) break; @@ -256,7 +315,14 @@ static bool validateInputBlock( scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return true; + } + unsigned kind = maybeKind.get(); switch (kind) { case input_block::FILE_DEPENDENCY: { bool isHashBased = scratch[2] != 0; @@ -309,8 +375,8 @@ ValidationInfo serialization::validateSerializedAST( ValidationInfo result; // Check 32-bit alignment. - if (data.size() % 4 != 0 || - reinterpret_cast(data.data()) % 4 != 0) + if (data.size() % SWIFTMODULE_ALIGNMENT != 0 || + reinterpret_cast(data.data()) % SWIFTMODULE_ALIGNMENT != 0) return result; llvm::BitstreamCursor cursor(data); @@ -323,12 +389,25 @@ ValidationInfo serialization::validateSerializedAST( llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { - topLevelEntry = cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + result.status = Status::Malformed; + return result; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; if (topLevelEntry.ID == CONTROL_BLOCK_ID) { - cursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } result = validateControlBlock(cursor, scratch, {SWIFTMODULE_VERSION_MAJOR, SWIFTMODULE_VERSION_MINOR}, @@ -338,7 +417,12 @@ ValidationInfo serialization::validateSerializedAST( } else if (dependencies && result.status == Status::Valid && topLevelEntry.ID == INPUT_BLOCK_ID) { - cursor.EnterSubBlock(INPUT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + result.status = Status::Malformed; + return result; + } if (validateInputBlock(cursor, scratch, *dependencies)) { result.status = Status::Malformed; return result; @@ -827,13 +911,23 @@ ModuleFile::readObjCMethodTable(ArrayRef fields, StringRef blobData) { } bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(INDEX_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INDEX_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); switch (entry.Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -844,13 +938,25 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::SubBlock: if (entry.ID == DECL_MEMBER_TABLES_BLOCK_ID) { DeclMemberTablesCursor = cursor; - DeclMemberTablesCursor.EnterSubBlock(DECL_MEMBER_TABLES_BLOCK_ID); + if (llvm::Error Err = DeclMemberTablesCursor.EnterSubBlock( + DECL_MEMBER_TABLES_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } llvm::BitstreamEntry subentry; do { // Scan forward, to load the cursor with any abbrevs we'll need while // seeking inside this block later. - subentry = DeclMemberTablesCursor.advance( - llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + Expected maybeEntry = + DeclMemberTablesCursor.advance( + llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + subentry = maybeEntry.get(); } while (!DeclMemberTablesCursor.AtEndOfStream() && subentry.Kind != llvm::BitstreamEntry::Record && subentry.Kind != llvm::BitstreamEntry::EndBlock); @@ -862,7 +968,14 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); blobData = {}; - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case index_block::DECL_OFFSETS: @@ -1044,13 +1157,23 @@ ModuleFile::readGroupTable(ArrayRef Fields, StringRef BlobData) { } bool ModuleFile::readCommentBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(COMMENT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(COMMENT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); + Expected maybeEntry = cursor.advance(); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry entry = maybeEntry.get(); switch (entry.Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -1066,7 +1189,14 @@ bool ModuleFile::readCommentBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case comment_block::DECL_COMMENTS: @@ -1181,13 +1311,24 @@ bool ModuleFile::readModuleDocIfPresent() { ValidationInfo info; while (!docCursor.AtEndOfStream()) { - topLevelEntry = docCursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + docCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeEntry.takeError()); + return false; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (topLevelEntry.ID) { case CONTROL_BLOCK_ID: { - docCursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = docCursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } info = validateControlBlock(docCursor, scratch, {SWIFTDOC_VERSION_MAJOR, @@ -1261,7 +1402,7 @@ class ModuleFile::DeclUSRTableInfo { std::unique_ptr ModuleFile::readDeclUSRsTable(ArrayRef fields, StringRef blobData) { if (fields.empty() || blobData.empty()) - return nullptr; + return nullptr; uint32_t tableOffset = static_cast(fields.front()); auto base = reinterpret_cast(blobData.data()); return std::unique_ptr( @@ -1270,14 +1411,21 @@ ModuleFile::readDeclUSRsTable(ArrayRef fields, StringRef blobData) { } bool ModuleFile::readDeclLocsBlock(llvm::BitstreamCursor &cursor) { - cursor.EnterSubBlock(DECL_LOCS_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + consumeError(std::move(Err)); + return false; + } SmallVector scratch; StringRef blobData; while (!cursor.AtEndOfStream()) { - auto entry = cursor.advance(); - switch (entry.Kind) { + Expected entry = cursor.advance(); + if (!entry) { + consumeError(entry.takeError()); + return false; + } + switch (entry->Kind) { case llvm::BitstreamEntry::EndBlock: return true; @@ -1292,9 +1440,13 @@ bool ModuleFile::readDeclLocsBlock(llvm::BitstreamCursor &cursor) { case llvm::BitstreamEntry::Record: scratch.clear(); - unsigned kind = cursor.readRecord(entry.ID, scratch, &blobData); - - switch (kind) { + Expected kind = + cursor.readRecord(entry->ID, scratch, &blobData); + if (!kind) { + consumeError(kind.takeError()); + return false; + } + switch (*kind) { case decl_locs_block::BASIC_DECL_LOCS: BasicDeclLocsData = blobData; break; @@ -1326,20 +1478,28 @@ bool ModuleFile::readModuleSourceInfoIfPresent() { } SmallVector scratch; - llvm::BitstreamEntry topLevelEntry; bool hasValidControlBlock = false; ValidationInfo info; + unsigned kind = llvm::BitstreamEntry::Error; while (!infoCursor.AtEndOfStream()) { - topLevelEntry = infoCursor.advance(AF_DontPopBlockAtEnd); - if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) + Expected topLevelEntry = + infoCursor.advance(AF_DontPopBlockAtEnd); + if (!topLevelEntry) { + consumeError(topLevelEntry.takeError()); + return false; + } + kind = topLevelEntry->Kind; + if (kind != llvm::BitstreamEntry::SubBlock) break; - switch (topLevelEntry.ID) { + switch (topLevelEntry->ID) { case CONTROL_BLOCK_ID: { - infoCursor.EnterSubBlock(CONTROL_BLOCK_ID); - + if (llvm::Error Err = infoCursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + consumeError(std::move(Err)); + return false; + } info = validateControlBlock(infoCursor, scratch, {SWIFTSOURCEINFO_VERSION_MAJOR, SWIFTSOURCEINFO_VERSION_MINOR}, @@ -1368,7 +1528,7 @@ bool ModuleFile::readModuleSourceInfoIfPresent() { } } - if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) + if (kind != llvm::BitstreamEntry::EndBlock) return false; return true; @@ -1405,13 +1565,26 @@ ModuleFile::ModuleFile( llvm::BitstreamEntry topLevelEntry; while (!cursor.AtEndOfStream()) { - topLevelEntry = cursor.advance(AF_DontPopBlockAtEnd); + Expected maybeEntry = + cursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) { + // FIXME this drops the error diagnostic on the floor. + consumeError(maybeEntry.takeError()); + info.status = error(Status::Malformed); + return; + } + topLevelEntry = maybeEntry.get(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (topLevelEntry.ID) { case CONTROL_BLOCK_ID: { - cursor.EnterSubBlock(CONTROL_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(CONTROL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } info = validateControlBlock(cursor, scratch, {SWIFTMODULE_VERSION_MAJOR, @@ -1436,13 +1609,33 @@ ModuleFile::ModuleFile( return; } - cursor.EnterSubBlock(INPUT_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(INPUT_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } - auto next = cursor.advance(); + Expected maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + info.status = error(Status::Malformed); + return; + } + unsigned kind = maybeKind.get(); switch (kind) { case input_block::IMPORTED_MODULE: { unsigned rawImportControl; @@ -1504,7 +1697,14 @@ ModuleFile::ModuleFile( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + next = maybeNext.get(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) @@ -1522,8 +1722,22 @@ ModuleFile::ModuleFile( // The decls-and-types block is lazily loaded. Save the cursor and load // any abbrev records at the start of the block. DeclTypeCursor = cursor; - DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID); - if (DeclTypeCursor.advance().Kind == llvm::BitstreamEntry::Error) + if (llvm::Error Err = + DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } + + Expected maybeCursor = DeclTypeCursor.advance(); + if (!maybeCursor) { + // FIXME this drops the error on the floor. + consumeError(maybeCursor.takeError()); + info.status = error(Status::Malformed); + return; + } + if (maybeCursor.get().Kind == llvm::BitstreamEntry::Error) info.status = error(Status::Malformed); // With the main cursor, skip over the block and continue. @@ -1540,13 +1754,34 @@ ModuleFile::ModuleFile( return; } - cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID); + if (llvm::Error Err = cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } - auto next = cursor.advanceSkippingSubblocks(); + Expected maybeNext = + cursor.advanceSkippingSubblocks(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + Expected maybeKind = + cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + info.status = error(Status::Malformed); + return; + } + unsigned kind = maybeKind.get(); switch (kind) { case identifier_block::IDENTIFIER_DATA: @@ -1559,7 +1794,14 @@ ModuleFile::ModuleFile( break; } - next = cursor.advanceSkippingSubblocks(); + maybeNext = cursor.advanceSkippingSubblocks(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + info.status = error(Status::Malformed); + return; + } + next = maybeNext.get(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) { @@ -1581,7 +1823,12 @@ ModuleFile::ModuleFile( case SIL_INDEX_BLOCK_ID: { // Save the cursor. SILIndexCursor = cursor; - SILIndexCursor.EnterSubBlock(SIL_INDEX_BLOCK_ID); + if (llvm::Error Err = SILIndexCursor.EnterSubBlock(SIL_INDEX_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } // With the main cursor, skip over the block and continue. if (cursor.SkipBlock()) { @@ -1594,7 +1841,12 @@ ModuleFile::ModuleFile( case SIL_BLOCK_ID: { // Save the cursor. SILCursor = cursor; - SILCursor.EnterSubBlock(SIL_BLOCK_ID); + if (llvm::Error Err = SILCursor.EnterSubBlock(SIL_BLOCK_ID)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + info.status = error(Status::Malformed); + return; + } // With the main cursor, skip over the block and continue. if (cursor.SkipBlock()) { @@ -1731,7 +1983,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, auto scopeID = ctx.getIdentifier(scopePath); assert(!scopeID.empty() && "invalid decl name (non-top-level decls not supported)"); - std::pair accessPathElem(scopeID, SourceLoc()); + Located accessPathElem = { scopeID, SourceLoc() }; dependency.Import = {ctx.AllocateCopy(llvm::makeArrayRef(accessPathElem)), module}; } @@ -1950,14 +2202,14 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { if (Dep.isScoped()) std::tie(ModulePathStr, ScopePath) = ModulePathStr.rsplit('\0'); - SmallVector, 1> AccessPath; + SmallVector, 1> AccessPath; while (!ModulePathStr.empty()) { StringRef NextComponent; std::tie(NextComponent, ModulePathStr) = ModulePathStr.split('\0'); AccessPath.push_back({Ctx.getIdentifier(NextComponent), SourceLoc()}); } - if (AccessPath.size() == 1 && AccessPath[0].first == Ctx.StdlibModuleName) + if (AccessPath.size() == 1 && AccessPath[0].Item == Ctx.StdlibModuleName) continue; ModuleDecl *M = Ctx.getLoadedModule(AccessPath); @@ -1976,11 +2228,11 @@ void ModuleFile::getImportDecls(SmallVectorImpl &Results) { // Lookup the decl in the top-level module. ModuleDecl *TopLevelModule = M; if (AccessPath.size() > 1) - TopLevelModule = Ctx.getLoadedModule(AccessPath.front().first); + TopLevelModule = Ctx.getLoadedModule(AccessPath.front().Item); SmallVector Decls; TopLevelModule->lookupQualified( - TopLevelModule, ScopeID, + TopLevelModule, DeclNameRef(ScopeID), NL_QualifiedDefault | NL_KnownNoDependency, Decls); Optional FoundKind = ImportDecl::findBestImportKind(Decls); assert(FoundKind.hasValue() && @@ -2026,7 +2278,7 @@ void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath, }; if (!accessPath.empty()) { - auto iter = TopLevelDecls->find(accessPath.front().first); + auto iter = TopLevelDecls->find(accessPath.front().Item); if (iter == TopLevelDecls->end()) return; @@ -2138,16 +2390,17 @@ ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, DeclMembersTables[subTableOffset]; if (!subTable) { BCOffsetRAII restoreOffset(DeclMemberTablesCursor); - DeclMemberTablesCursor.JumpToBit(subTableOffset); - auto entry = DeclMemberTablesCursor.advance(); + fatalIfNotSuccess(DeclMemberTablesCursor.JumpToBit(subTableOffset)); + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclMemberTablesCursor.advance()); if (entry.Kind != llvm::BitstreamEntry::Record) { fatal(); return None; } SmallVector scratch; StringRef blobData; - unsigned kind = DeclMemberTablesCursor.readRecord(entry.ID, scratch, - &blobData); + unsigned kind = fatalIfUnexpected( + DeclMemberTablesCursor.readRecord(entry.ID, scratch, &blobData)); assert(kind == decl_member_tables_block::DECL_MEMBERS); (void)kind; subTable = readDeclMembersTable(scratch, blobData); @@ -2166,7 +2419,7 @@ ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, } else { if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(mem.takeError()); - llvm::consumeError(mem.takeError()); + consumeError(mem.takeError()); // Treat this as a cache-miss to the caller and let them attempt // to refill through the normal loadAllMembers() path. @@ -2201,7 +2454,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } else { @@ -2214,7 +2467,7 @@ void ModuleFile::lookupClassMember(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) results.push_back(vd); } } @@ -2243,7 +2496,7 @@ void ModuleFile::lookupClassMembers(ModuleDecl::AccessPathTy accessPath, while (!dc->getParent()->isModuleScopeContext()) dc = dc->getParent(); if (auto nominal = dc->getSelfNominalTypeDecl()) - if (nominal->getName() == accessPath.front().first) + if (nominal->getName() == accessPath.front().Item) consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, DynamicLookupInfo::AnyObject); } @@ -2286,14 +2539,23 @@ ModuleFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const callback(LinkLibrary(Name, LibraryKind::Framework)); } -void ModuleFile::getTopLevelDecls(SmallVectorImpl &results) { +void ModuleFile::getTopLevelDecls( + SmallVectorImpl &results, + llvm::function_ref matchAttributes) { PrettyStackTraceModuleFile stackEntry(*this); for (DeclID entry : OrderedTopLevelDecls) { - Expected declOrError = getDeclChecked(entry); + Expected declOrError = getDeclChecked(entry, matchAttributes); if (!declOrError) { + if (declOrError.errorIsA()) { + // Decl rejected by matchAttributes, ignore it. + assert(matchAttributes); + consumeError(declOrError.takeError()); + continue; + } + if (!getContext().LangOpts.EnableDeserializationRecovery) fatal(declOrError.takeError()); - llvm::consumeError(declOrError.takeError()); + consumeError(declOrError.takeError()); continue; } results.push_back(declOrError.get()); @@ -2357,6 +2619,8 @@ Optional ModuleFile::getCommentForDecl(const Decl *D) const { if (!DeclCommentTable) return None; + if (D->isImplicit()) + return None; // Compute the USR. llvm::SmallString<128> USRBuffer; llvm::raw_svector_ostream OS(USRBuffer); @@ -2381,6 +2645,8 @@ ModuleFile::getBasicDeclLocsForDecl(const Decl *D) const { // Future compilers may not provide BasicDeclLocsData anymore. if (BasicDeclLocsData.empty()) return None; + if (D->isImplicit()) + return None; // Compute the USR. llvm::SmallString<128> USRBuffer; llvm::raw_svector_ostream OS(USRBuffer); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 4fc5e2c355470..2ae1a88a3da06 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -26,7 +26,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" @@ -474,6 +474,15 @@ class ModuleFile /// Emits one last diagnostic, logs the error, and then aborts for the stack /// trace. LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error error); + void fatalIfNotSuccess(llvm::Error error) { + if (error) + fatal(std::move(error)); + } + template T fatalIfUnexpected(llvm::Expected expected) { + if (expected) + return std::move(expected.get()); + fatal(expected.takeError()); + } LLVM_ATTRIBUTE_NORETURN void fatal() { fatal(llvm::make_error( @@ -639,7 +648,8 @@ class ModuleFile /// \returns Whether the module was successfully loaded, or what went wrong /// if it was not. static serialization::ValidationInfo - load(std::unique_ptr moduleInputBuffer, + load(StringRef moduleInterfacePath, + std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, bool isFramework, std::unique_ptr &theModule, @@ -649,6 +659,8 @@ class ModuleFile std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, info, extInfo)); + if (!moduleInterfacePath.empty()) + theModule->ModuleInterfacePath = moduleInterfacePath; return info; } @@ -774,7 +786,15 @@ class ModuleFile void collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const; /// Adds all top-level decls to the given vector. - void getTopLevelDecls(SmallVectorImpl &Results); + /// + /// \param Results Vector collecting the decls. + /// + /// \param matchAttributes Optional check on the attributes of a decl to + /// filter which decls to fully deserialize. Only decls with accepted + /// attributes are deserialized and added to Results. + void getTopLevelDecls( + SmallVectorImpl &Results, + llvm::function_ref matchAttributes = nullptr); /// Adds all precedence groups to the given vector. void getPrecedenceGroups(SmallVectorImpl &Results); @@ -819,6 +839,10 @@ class ModuleFile virtual Type loadAssociatedTypeDefault(const AssociatedTypeDecl *ATD, uint64_t contextData) override; + virtual ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) override; + virtual void finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) override; @@ -876,12 +900,24 @@ class ModuleFile /// Returns the decl with the given ID, deserializing it if needed. /// /// \param DID The ID for the decl within this module. + /// + /// \param matchAttributes Optional check on the attributes of the decl to + /// determine if it should be fully deserialized and returned. If the + /// attributes fail the check, the decl is not deserialized and + /// \c DeclAttributesDidNotMatch is returned. llvm::Expected - getDeclChecked(serialization::DeclID DID); + getDeclChecked( + serialization::DeclID DID, + llvm::function_ref matchAttributes = nullptr); /// Returns the decl context with the given ID, deserializing it if needed. DeclContext *getDeclContext(serialization::DeclContextID DID); + /// Returns the decl context with the given ID, deserializing it if needed, + /// or the first error. + llvm::Expected + getDeclContextChecked(serialization::DeclContextID DCID); + /// Returns the local decl context with the given ID, deserializing it if needed. DeclContext *getLocalDeclContext(serialization::LocalDeclContextID DID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 28c2447a416df..c2769a9c5ef97 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -22,7 +22,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/Types.h" #include "llvm/Bitcode/RecordLayout.h" -#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Bitstream/BitCodes.h" #include "llvm/ADT/PointerEmbeddedInt.h" namespace swift { @@ -39,6 +39,9 @@ using llvm::BCVBR; /// Magic number for serialized module files. const unsigned char SWIFTMODULE_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x0E }; +/// Alignment of each serialized modules inside a .swift_ast section. +const unsigned char SWIFTMODULE_ALIGNMENT = 4; + /// Serialized module format major version number. /// /// Always 0 for Swift 1.x - 4.x. @@ -52,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 519; // SIL function availability +const uint16_t SWIFTMODULE_VERSION_MINOR = 535; // top-level var decls /// A standard hash seed used for all string hashes in a serialized module. /// @@ -223,6 +226,23 @@ enum class FunctionTypeRepresentation : uint8_t { }; using FunctionTypeRepresentationField = BCFixed<4>; +// These IDs must \em not be renumbered or reordered without incrementing +// the module version. +enum class DifferentiabilityKind : uint8_t { + NonDifferentiable = 0, + Normal, + Linear, +}; +using DifferentiabilityKindField = BCFixed<2>; + +// These IDs must \em not be renumbered or reordered without incrementing the +// module version. +enum class AutoDiffDerivativeFunctionKind : uint8_t { + JVP = 0, + VJP +}; +using AutoDiffDerivativeFunctionKindField = BCFixed<1>; + enum class ForeignErrorConventionKind : uint8_t { ZeroResult, NonZeroResult, @@ -327,6 +347,13 @@ enum class ParameterConvention : uint8_t { }; using ParameterConventionField = BCFixed<4>; +// These IDs must \em not be renumbered or reordered without incrementing +// the module version. +enum class SILParameterDifferentiability : uint8_t { + DifferentiableOrNotApplicable, + NotDifferentiable, +}; + // These IDs must \em not be renumbered or reordered without incrementing // the module version. enum class ResultConvention : uint8_t { @@ -428,6 +455,7 @@ enum class DefaultArgumentKind : uint8_t { None = 0, Normal, File, + FilePath, Line, Column, Function, @@ -876,18 +904,21 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // noescape? - BCFixed<1> // throws? + BCFixed<1>, // throws? + DifferentiabilityKindField // differentiability kind // trailed by parameters >; using FunctionParamLayout = BCRecordLayout< FUNCTION_PARAM, - IdentifierIDField, // name - TypeIDField, // type - BCFixed<1>, // vararg? - BCFixed<1>, // autoclosure? - ValueOwnershipField // inout, shared or owned? + IdentifierIDField, // name + TypeIDField, // type + BCFixed<1>, // vararg? + BCFixed<1>, // autoclosure? + BCFixed<1>, // non-ephemeral? + ValueOwnershipField, // inout, shared or owned? + BCFixed<1> // noDerivative? >; using MetatypeTypeLayout = BCRecordLayout< @@ -949,6 +980,7 @@ namespace decls_block { TypeIDField, // output FunctionTypeRepresentationField, // representation BCFixed<1>, // throws? + DifferentiabilityKindField, // differentiability kind GenericSignatureIDField // generic signture // trailed by parameters @@ -961,15 +993,19 @@ namespace decls_block { SILFunctionTypeRepresentationField, // representation BCFixed<1>, // pseudogeneric? BCFixed<1>, // noescape? + DifferentiabilityKindField, // differentiability kind BCFixed<1>, // error result? BCVBR<6>, // number of parameters BCVBR<5>, // number of yields BCVBR<5>, // number of results + BCFixed<1>, // generic signature implied GenericSignatureIDField, // generic signature + SubstitutionMapIDField, // substitutions BCArray // parameter types/conventions, alternating // followed by result types/conventions, alternating // followed by error result type/convention // Optionally a protocol conformance (for witness_methods) + // Optionally a substitution map (for substituted function types) >; using SILBlockStorageTypeLayout = BCRecordLayout< @@ -1085,6 +1121,7 @@ namespace decls_block { BCFixed<1>, // implicit? BCFixed<1>, // explicitly objc? BCFixed<1>, // inherits convenience initializers from its superclass? + BCFixed<1>, // has missing designated initializers? GenericSignatureIDField, // generic environment TypeIDField, // superclass AccessLevelField, // access level @@ -1154,6 +1191,7 @@ namespace decls_block { BCFixed<1>, // is getter mutating? BCFixed<1>, // is setter mutating? BCFixed<1>, // is this the backing storage for a lazy property? + BCFixed<1>, // top level global? DeclIDField, // if this is a lazy property, this is the backing storage OpaqueReadOwnershipField, // opaque read ownership ReadImplKindField, // read implementation @@ -1200,6 +1238,7 @@ namespace decls_block { BCFixed<1>, // IUO result? DeclIDField, // operator decl DeclIDField, // overridden function + BCFixed<1>, // whether the overridden decl affects ABI BCVBR<5>, // 0 for a simple name, otherwise the number of parameter name // components plus one AccessLevelField, // access level @@ -1241,6 +1280,7 @@ namespace decls_block { TypeIDField, // result interface type BCFixed<1>, // IUO result? DeclIDField, // overridden function + BCFixed<1>, // whether the overridden decl affects ABI DeclIDField, // AccessorStorageDecl AccessorKindField, // accessor kind AccessLevelField, // access level @@ -1715,6 +1755,14 @@ namespace decls_block { BCBlob // platform, followed by message >; + using OriginallyDefinedInDeclAttrLayout = BCRecordLayout< + OriginallyDefinedIn_DECL_ATTR, + BCFixed<1>, // implicit flag + BC_AVAIL_TUPLE, // moved OS version + BCVBR<5>, // platform + BCBlob // original module name + >; + using ObjCDeclAttrLayout = BCRecordLayout< ObjC_DECL_ATTR, BCFixed<1>, // implicit flag @@ -1731,7 +1779,36 @@ namespace decls_block { GenericSignatureIDField // specialized signature >; -#define SIMPLE_DECL_ATTR(X, CLASS, ...) \ + using DifferentiableDeclAttrLayout = BCRecordLayout< + Differentiable_DECL_ATTR, + BCFixed<1>, // Implicit flag. + BCFixed<1>, // Linear flag. + IdentifierIDField, // JVP name. + DeclIDField, // JVP function declaration. + IdentifierIDField, // VJP name. + DeclIDField, // VJP function declaration. + GenericSignatureIDField, // Derivative generic signature. + BCArray> // Differentiation parameter indices' bitvector. + >; + + using DerivativeDeclAttrLayout = BCRecordLayout< + Derivative_DECL_ATTR, + BCFixed<1>, // Implicit flag. + IdentifierIDField, // Original name. + DeclIDField, // Original function declaration. + AutoDiffDerivativeFunctionKindField, // Derivative function kind. + BCArray> // Differentiation parameter indices' bitvector. + >; + + using TransposeDeclAttrLayout = BCRecordLayout< + Transpose_DECL_ATTR, + BCFixed<1>, // Implicit flag. + IdentifierIDField, // Original name. + DeclIDField, // Original function declaration. + BCArray> // Transposed parameter indices' bitvector. + >; + +#define SIMPLE_DECL_ATTR(X, CLASS, ...) \ using CLASS##DeclAttrLayout = BCRecordLayout< \ CLASS##_DECL_ATTR, \ BCFixed<1> /* implicit flag */ \ diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index a3ec006d240e1..0e201761ac36c 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -56,22 +56,6 @@ enum SILVTableEntryKindEncoding : uint8_t { }; using SILVTableEntryKindField = BCFixed<2>; -enum CheckedCastKindEncoding : uint8_t { - SIL_CHECKED_CAST_ARCHETYPE_TO_ARCHETYPE, - SIL_CHECKED_CAST_ARCHETYPE_TO_CONCRETE, - SIL_CHECKED_CAST_ARRAY_DOWNCAST, - SIL_CHECKED_CAST_ARRAY_DOWNCAST_BRIDGED, - SIL_CHECKED_CAST_DICTIONARY_DOWNCAST, - SIL_CHECKED_CAST_DICTIONARY_DOWNCAST_BRIDGED, - SIL_CHECKED_CAST_DOWNCAST, - SIL_CHECKED_CAST_IDENTICAL, - SIL_CHECKED_CAST_EXISTENTIAL_TO_ARCHETYPE, - SIL_CHECKED_CAST_EXISTENTIAL_TO_CONCRETE, - SIL_CHECKED_CAST_SUPER_TO_ARCHETYPE, - SIL_CHECKED_CAST_CONCRETE_TO_ARCHETYPE, - SIL_CHECKED_CAST_CONCRETE_TO_UNRELATED_EXISTENTIAL, -}; - enum CastConsumptionKindEncoding : uint8_t { SIL_CAST_CONSUMPTION_TAKE_ALWAYS, SIL_CAST_CONSUMPTION_TAKE_ON_SUCCESS, @@ -95,15 +79,6 @@ enum class KeyPathComputedComponentIdKindEncoding : uint8_t { DeclRef, }; -// Constants for packing an encoded CheckedCastKind and -// CastConsumptionKind together. -enum { - // Must be large enough to store all the CheckedCastKindEncodings - SIL_CAST_CONSUMPTION_BIT_OFFSET = 4, - SIL_CHECKED_CAST_MASK = - (1 << SIL_CAST_CONSUMPTION_BIT_OFFSET) - 1 -}; - /// The record types within the "sil-index" block. /// /// \sa SIL_INDEX_BLOCK_ID @@ -157,7 +132,6 @@ namespace sil_block { SIL_VTABLE, SIL_VTABLE_ENTRY, SIL_GLOBALVAR, - SIL_INST_CAST, // It has a cast kind instead of an attribute. SIL_INIT_EXISTENTIAL, SIL_WITNESS_TABLE, SIL_WITNESS_METHOD_ENTRY, @@ -352,18 +326,6 @@ namespace sil_block { // Trailed by protocol conformance info (if any) >; - // SIL Cast instructions with a cast kind, one type and one typed valueref. - using SILInstCastLayout = BCRecordLayout< - SIL_INST_CAST, - SILInstOpCodeField, - BCFixed<4>, // Cast kind - TypeIDField, - SILTypeCategoryField, - TypeIDField, - SILTypeCategoryField, - ValueIDField - >; - // SIL instructions with one type and a list of values. using SILOneTypeValuesLayout = BCRecordLayout< SIL_ONE_TYPE_VALUES, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 80ebaeb35cd75..18abc58342f53 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -15,11 +15,13 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Expr.h" #include "swift/AST/FileSystem.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/IndexSubset.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/LinkLibrary.h" @@ -44,8 +46,8 @@ #include "swift/Strings.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Config/config.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Chrono.h" @@ -769,7 +771,6 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(sil_block, SIL_VTABLE); BLOCK_RECORD(sil_block, SIL_VTABLE_ENTRY); BLOCK_RECORD(sil_block, SIL_GLOBALVAR); - BLOCK_RECORD(sil_block, SIL_INST_CAST); BLOCK_RECORD(sil_block, SIL_INIT_EXISTENTIAL); BLOCK_RECORD(sil_block, SIL_WITNESS_TABLE); BLOCK_RECORD(sil_block, SIL_WITNESS_METHOD_ENTRY); @@ -914,7 +915,7 @@ static void flattenImportPath(const ModuleDecl::ImportedModule &import, outStream << '\0'; assert(import.first.size() == 1 && "can only handle top-level decl imports"); auto accessPathElem = import.first.front(); - outStream << accessPathElem.first.str(); + outStream << accessPathElem.Item.str(); } uint64_t getRawModTimeOrHash(const SerializationOptions::FileDependency &dep) { @@ -1064,6 +1065,7 @@ static uint8_t getRawStableDefaultArgumentKind(swift::DefaultArgumentKind kind) CASE(Inherited) CASE(Column) CASE(File) + CASE(FilePath) CASE(Line) CASE(Function) CASE(DSOHandle) @@ -1583,7 +1585,7 @@ void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) { abbrCode = DeclTypeAbbrCodes[XRefExtensionPathPieceLayout::Code]; CanGenericSignature genericSig(nullptr); if (ext->isConstrainedExtension()) { - genericSig = ext->getGenericSignature()->getCanonicalSignature(); + genericSig = ext->getGenericSignature().getCanonicalSignature(); } XRefExtensionPathPieceLayout::emitRecord( Out, ScratchRecord, abbrCode, addContainingModuleRef(DC), @@ -1851,11 +1853,10 @@ void Serializer::writePatternBindingInitializer(PatternBindingDecl *binding, StringRef initStr; SmallString<128> scratch; - auto &entry = binding->getPatternList()[bindingIndex]; - auto varDecl = entry.getAnchoringVarDecl(); - if (entry.hasInitStringRepresentation() && + auto varDecl = binding->getAnchoringVarDecl(bindingIndex); + if (binding->hasInitStringRepresentation(bindingIndex) && varDecl->isInitExposedToClients()) { - initStr = entry.getInitStringRepresentation(scratch); + initStr = binding->getInitStringRepresentation(bindingIndex, scratch); } PatternBindingInitializerLayout::emitRecord(Out, ScratchRecord, @@ -1996,6 +1997,19 @@ static uint8_t getRawStableVarDeclIntroducer(swift::VarDecl::Introducer intr) { llvm_unreachable("bad variable decl introducer kind"); } +/// Translate from the AST derivative function kind enum to the Serialization +/// enum values, which are guaranteed to be stable. +static uint8_t getRawStableAutoDiffDerivativeFunctionKind( + swift::AutoDiffDerivativeFunctionKind kind) { + switch (kind) { + case swift::AutoDiffDerivativeFunctionKind::JVP: + return uint8_t(serialization::AutoDiffDerivativeFunctionKind::JVP); + case swift::AutoDiffDerivativeFunctionKind::VJP: + return uint8_t(serialization::AutoDiffDerivativeFunctionKind::VJP); + } + llvm_unreachable("bad derivative function kind"); +} + /// Returns true if the declaration of \p decl depends on \p problemContext /// based on lexical nesting. /// @@ -2069,7 +2083,7 @@ class Serializer::DeclSerializer : public DeclVisitor { didVerifyAttrs = true; } - void writeDeclAttribute(const DeclAttribute *DA) { + void writeDeclAttribute(const Decl *D, const DeclAttribute *DA) { using namespace decls_block; // Completely ignore attributes that aren't serialized. @@ -2180,6 +2194,22 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_OriginallyDefinedIn: { + auto *theAttr = cast(DA); + ENCODE_VER_TUPLE(Moved, llvm::Optional(theAttr->MovedVersion)); + auto abbrCode = S.DeclTypeAbbrCodes[OriginallyDefinedInDeclAttrLayout::Code]; + llvm::SmallString<32> blob; + blob.append(theAttr->OriginalModuleName.str()); + blob.push_back('\0'); + OriginallyDefinedInDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, + theAttr->isImplicit(), + LIST_VER_TUPLE_PIECES(Moved), + static_cast(theAttr->Platform), + blob); + return; + } + case DAK_Available: { auto *theAttr = cast(DA); ENCODE_VER_TUPLE(Introduced, theAttr->Introduced) @@ -2245,10 +2275,11 @@ class Serializer::DeclSerializer : public DeclVisitor { pieces.push_back(S.addDeclBaseNameRef(replacedFun.getBaseName())); for (auto argName : replacedFun.getArgumentNames()) pieces.push_back(S.addDeclBaseNameRef(argName)); - assert(theAttr->getReplacedFunction()); + auto *afd = cast(D)->getDynamicallyReplacedDecl(); + assert(afd && "Missing replaced decl!"); DynamicReplacementDeclAttrLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, false, /*implicit flag*/ - S.addDeclRef(theAttr->getReplacedFunction()), pieces.size(), pieces); + S.addDeclRef(afd), pieces.size(), pieces); return; } @@ -2271,6 +2302,89 @@ class Serializer::DeclSerializer : public DeclVisitor { break; } + case DAK_Differentiable: { + auto abbrCode = S.DeclTypeAbbrCodes[DifferentiableDeclAttrLayout::Code]; + auto *attr = cast(DA); + + IdentifierID jvpName = 0; + DeclID jvpRef = 0; + if (auto jvp = attr->getJVP()) + jvpName = S.addDeclBaseNameRef(jvp->Name.getBaseName()); + if (auto jvpFunction = attr->getJVPFunction()) + jvpRef = S.addDeclRef(jvpFunction); + + IdentifierID vjpName = 0; + DeclID vjpRef = 0; + if (auto vjp = attr->getVJP()) + vjpName = S.addDeclBaseNameRef(vjp->Name.getBaseName()); + if (auto vjpFunction = attr->getVJPFunction()) + vjpRef = S.addDeclRef(vjpFunction); + + auto paramIndices = attr->getParameterIndices(); + // NOTE(TF-836): `@differentiable` attribute serialization is blocked by + // `@differentiable` attribute type-checking (TF-828), which resolves + // parameter indices (`IndexSubset *`). + if (!paramIndices) + return; + assert(paramIndices && "Parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(paramIndices->getCapacity())) + indices.push_back(paramIndices->contains(i)); + + DifferentiableDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), + attr->isLinear(), jvpName, jvpRef, vjpName, vjpRef, + S.addGenericSignatureRef(attr->getDerivativeGenericSignature()), + indices); + return; + } + + case DAK_Derivative: { + auto abbrCode = S.DeclTypeAbbrCodes[DerivativeDeclAttrLayout::Code]; + auto *attr = cast(DA); + assert(attr->getOriginalFunction() && + "`@derivative` attribute should have original declaration set " + "during construction or parsing"); + auto origName = attr->getOriginalFunctionName().Name.getBaseName(); + IdentifierID origNameId = S.addDeclBaseNameRef(origName); + DeclID origDeclID = S.addDeclRef(attr->getOriginalFunction()); + auto derivativeKind = + getRawStableAutoDiffDerivativeFunctionKind(attr->getDerivativeKind()); + auto *parameterIndices = attr->getParameterIndices(); + assert(parameterIndices && "Parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(parameterIndices->getCapacity())) + indices.push_back(parameterIndices->contains(i)); + DerivativeDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), origNameId, + origDeclID, derivativeKind, indices); + return; + } + + case DAK_Transpose: { + auto abbrCode = S.DeclTypeAbbrCodes[TransposeDeclAttrLayout::Code]; + auto *attr = cast(DA); + // NOTE(TF-838): `@transpose` attribute serialization is blocked by + // `@transpose` attribute type-checking (TF-830), which resolves + // the original declaration. + if (!attr->getOriginalFunction()) + return; + assert(attr->getOriginalFunction() && + "`@transpose` attribute should have original declaration set " + "during construction or parsing"); + auto origName = attr->getOriginalFunctionName().Name.getBaseName(); + IdentifierID origNameId = S.addDeclBaseNameRef(origName); + DeclID origDeclID = S.addDeclRef(attr->getOriginalFunction()); + auto *parameterIndices = attr->getParameterIndices(); + assert(parameterIndices && "Parameter indices must be resolved"); + SmallVector indices; + for (unsigned i : range(parameterIndices->getCapacity())) + indices.push_back(parameterIndices->contains(i)); + TransposeDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), origNameId, + origDeclID, indices); + return; + } } } @@ -2594,6 +2708,24 @@ class Serializer::DeclSerializer : public DeclVisitor { return count; } + /// Returns true if a client can still use decls that override \p overridden + /// even if \p overridden itself isn't available (isn't found, can't be + /// imported, can't be deserialized, whatever). + /// + /// This should be kept conservative. Compiler crashes are still better than + /// miscompiles. + static bool overriddenDeclAffectsABI(const ValueDecl *overridden) { + if (!overridden) + return false; + // There's one case where we know a declaration doesn't affect the ABI of + // its overrides after they've been compiled: if the declaration is '@objc' + // and 'dynamic'. In that case, all accesses to the method or property will + // go through the Objective-C method tables anyway. + if (overridden->hasClangNode() || overridden->isObjCDynamic()) + return false; + return true; + } + public: DeclSerializer(Serializer &S, DeclID id) : S(S), id(id) {} ~DeclSerializer() { @@ -2603,7 +2735,7 @@ class Serializer::DeclSerializer : public DeclVisitor { void visit(const Decl *D) { // Emit attributes (if any). for (auto Attr : D->getAttrs()) - writeDeclAttribute(Attr); + writeDeclAttribute(D, Attr); if (auto *value = dyn_cast(D)) writeDiscriminatorsIfNeeded(value); @@ -2698,7 +2830,7 @@ class Serializer::DeclSerializer : public DeclVisitor { SmallVector initContextIDs; for (unsigned i : range(binding->getNumPatternEntries())) { auto initContextID = - S.addDeclContextRef(binding->getPatternList()[i].getInitContext()); + S.addDeclContextRef(binding->getInitContext(i)); if (!initContextIDs.empty()) { initContextIDs.push_back(initContextID.getOpaqueValue()); } else if (initContextID) { @@ -2719,8 +2851,8 @@ class Serializer::DeclSerializer : public DeclVisitor { if (binding->getDeclContext()->isTypeContext()) owningDC = binding->getDeclContext(); - for (auto entry : binding->getPatternList()) { - writePattern(entry.getPattern()); + for (auto entryIdx : range(binding->getNumPatternEntries())) { + writePattern(binding->getPattern(entryIdx)); // Ignore initializer; external clients don't need to know about it. } } @@ -2999,9 +3131,7 @@ class Serializer::DeclSerializer : public DeclVisitor { uint8_t rawAccessLevel = getRawStableAccessLevel(theClass->getFormalAccess()); - bool inheritsSuperclassInitializers = - const_cast(theClass)-> - inheritsSuperclassInitializers(); + auto mutableClass = const_cast(theClass); unsigned abbrCode = S.DeclTypeAbbrCodes[ClassLayout::Code]; ClassLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, @@ -3009,7 +3139,8 @@ class Serializer::DeclSerializer : public DeclVisitor { contextID.getOpaqueValue(), theClass->isImplicit(), theClass->isObjC(), - inheritsSuperclassInitializers, + mutableClass->inheritsSuperclassInitializers(), + mutableClass->hasMissingDesignatedInitializers(), S.addGenericSignatureRef( theClass->getGenericSignature()), S.addTypeRef(theClass->getSuperclass()), @@ -3124,6 +3255,7 @@ class Serializer::DeclSerializer : public DeclVisitor { var->isGetterMutating(), var->isSetterMutating(), var->isLazyStorageProperty(), + var->isTopLevelGlobal(), S.addDeclRef(lazyStorage), accessors.OpaqueReadOwnership, accessors.ReadImpl, @@ -3171,8 +3303,8 @@ class Serializer::DeclSerializer : public DeclVisitor { defaultArgumentText); if (interfaceType->hasError()) { - param->getDeclContext()->dumpContext(); - interfaceType->dump(); + param->getDeclContext()->printContext(llvm::errs()); + interfaceType->dump(llvm::errs()); llvm_unreachable("error in interface type of parameter"); } } @@ -3213,6 +3345,7 @@ class Serializer::DeclSerializer : public DeclVisitor { fn->isImplicitlyUnwrappedOptional(), S.addDeclRef(fn->getOperatorDecl()), S.addDeclRef(fn->getOverriddenDecl()), + overriddenDeclAffectsABI(fn->getOverriddenDecl()), fn->getFullName().getArgumentNames().size() + fn->getFullName().isCompoundName(), rawAccessLevel, @@ -3268,6 +3401,9 @@ class Serializer::DeclSerializer : public DeclVisitor { uint8_t rawAccessorKind = uint8_t(getStableAccessorKind(fn->getAccessorKind())); + bool overriddenAffectsABI = + overriddenDeclAffectsABI(fn->getOverriddenDecl()); + Type ty = fn->getInterfaceType(); SmallVector dependencies; for (auto dependency : collectDependenciesFromType(ty->getCanonicalType())) @@ -3289,6 +3425,7 @@ class Serializer::DeclSerializer : public DeclVisitor { S.addTypeRef(fn->getResultInterfaceType()), fn->isImplicitlyUnwrappedOptional(), S.addDeclRef(fn->getOverriddenDecl()), + overriddenAffectsABI, S.addDeclRef(fn->getStorage()), rawAccessorKind, rawAccessLevel, @@ -3549,6 +3686,18 @@ static uint8_t getRawStableFunctionTypeRepresentation( llvm_unreachable("bad calling convention"); } +/// Translate from the AST differentiability kind enum to the Serialization enum +/// values, which are guaranteed to be stable. +static uint8_t getRawStableDifferentiabilityKind( + swift::DifferentiabilityKind diffKind) { + switch (diffKind) { + SIMPLE_CASE(DifferentiabilityKind, NonDifferentiable) + SIMPLE_CASE(DifferentiabilityKind, Normal) + SIMPLE_CASE(DifferentiabilityKind, Linear) + } + llvm_unreachable("bad differentiability kind"); +} + /// Translate from the AST function representation enum to the Serialization enum /// values, which are guaranteed to be stable. static uint8_t getRawStableSILFunctionTypeRepresentation( @@ -3618,6 +3767,17 @@ static uint8_t getRawStableParameterConvention(swift::ParameterConvention pc) { llvm_unreachable("bad parameter convention kind"); } +/// Translate from AST SILParameterDifferentiability enum to the Serialization +/// enum values, which are guaranteed to be stable. +static uint8_t +getRawSILParameterDifferentiability(swift::SILParameterDifferentiability pd) { + switch (pd) { + SIMPLE_CASE(SILParameterDifferentiability, DifferentiableOrNotApplicable) + SIMPLE_CASE(SILParameterDifferentiability, NotDifferentiable) + } + llvm_unreachable("bad parameter differentiability kind"); +} + /// Translate from the AST ResultConvention enum to the /// Serialization enum values, which are guaranteed to be stable. static uint8_t getRawStableResultConvention(swift::ResultConvention rc) { @@ -3864,19 +4024,22 @@ class Serializer::TypeSerializer : public TypeVisitor { S.Out, S.ScratchRecord, abbrCode, S.addDeclBaseNameRef(param.getLabel()), S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(), - paramFlags.isAutoClosure(), rawOwnership); + paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(), rawOwnership, + paramFlags.isNoDerivative()); } } void visitFunctionType(const FunctionType *fnTy) { using namespace decls_block; + // FIXME: [clang-function-type-serialization] Serialize the clang type here unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->isNoEscape(), - fnTy->throws()); + fnTy->throws(), + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind())); serializeFunctionTypeParams(fnTy); } @@ -3884,13 +4047,13 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitGenericFunctionType(const GenericFunctionType *fnTy) { using namespace decls_block; assert(!fnTy->isNoEscape()); - auto genericSig = fnTy->getGenericSignature(); unsigned abbrCode = S.DeclTypeAbbrCodes[GenericFunctionTypeLayout::Code]; GenericFunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addTypeRef(fnTy->getResult()), getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), fnTy->throws(), + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()), S.addGenericSignatureRef(genericSig)); serializeFunctionTypeParams(fnTy); @@ -3921,28 +4084,31 @@ class Serializer::TypeSerializer : public TypeVisitor { SmallVector variableData; for (auto param : fnTy->getParameters()) { - variableData.push_back(S.addTypeRef(param.getType())); + variableData.push_back(S.addTypeRef(param.getInterfaceType())); unsigned conv = getRawStableParameterConvention(param.getConvention()); variableData.push_back(TypeID(conv)); + if (fnTy->isDifferentiable()) + variableData.push_back(TypeID( + getRawSILParameterDifferentiability(param.getDifferentiability()))); } for (auto yield : fnTy->getYields()) { - variableData.push_back(S.addTypeRef(yield.getType())); + variableData.push_back(S.addTypeRef(yield.getInterfaceType())); unsigned conv = getRawStableParameterConvention(yield.getConvention()); variableData.push_back(TypeID(conv)); } for (auto result : fnTy->getResults()) { - variableData.push_back(S.addTypeRef(result.getType())); + variableData.push_back(S.addTypeRef(result.getInterfaceType())); unsigned conv = getRawStableResultConvention(result.getConvention()); variableData.push_back(TypeID(conv)); } if (fnTy->hasErrorResult()) { auto abResult = fnTy->getErrorResult(); - variableData.push_back(S.addTypeRef(abResult.getType())); + variableData.push_back(S.addTypeRef(abResult.getInterfaceType())); unsigned conv = getRawStableResultConvention(abResult.getConvention()); variableData.push_back(TypeID(conv)); } - auto sig = fnTy->getGenericSignature(); + auto sig = fnTy->getSubstGenericSignature(); auto stableCoroutineKind = getRawStableSILCoroutineKind(fnTy->getCoroutineKind()); @@ -3950,17 +4116,23 @@ class Serializer::TypeSerializer : public TypeVisitor { auto stableCalleeConvention = getRawStableParameterConvention(fnTy->getCalleeConvention()); + auto stableDiffKind = + getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind()); + unsigned abbrCode = S.DeclTypeAbbrCodes[SILFunctionTypeLayout::Code]; SILFunctionTypeLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, stableCoroutineKind, stableCalleeConvention, stableRepresentation, fnTy->isPseudogeneric(), fnTy->isNoEscape(), - fnTy->hasErrorResult(), fnTy->getParameters().size(), + stableDiffKind, fnTy->hasErrorResult(), fnTy->getParameters().size(), fnTy->getNumYields(), fnTy->getNumResults(), - S.addGenericSignatureRef(sig), variableData); + fnTy->isGenericSignatureImplied(), + S.addGenericSignatureRef(sig), + S.addSubstitutionMapRef(fnTy->getSubstitutions()), + variableData); - if (auto conformance = fnTy->getWitnessMethodConformanceOrNone()) - S.writeConformance(*conformance, S.DeclTypeAbbrCodes); + if (auto conformance = fnTy->getWitnessMethodConformanceOrInvalid()) + S.writeConformance(conformance, S.DeclTypeAbbrCodes); } void visitArraySliceType(const ArraySliceType *sliceTy) { @@ -4739,7 +4911,8 @@ void swift::serializeToBuffers( assert(!StringRef::withNullAsEmpty(options.OutputPath).empty()); { - SharedTimer timer("Serialization, swiftmodule, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftmodule, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); Serializer::writeToStream(stream, DC, M, options); @@ -4757,7 +4930,8 @@ void swift::serializeToBuffers( } if (!StringRef::withNullAsEmpty(options.DocOutputPath).empty()) { - SharedTimer timer("Serialization, swiftdoc, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftdoc, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); writeDocToStream(stream, DC, options.GroupInfoPath); @@ -4773,7 +4947,8 @@ void swift::serializeToBuffers( } if (!StringRef::withNullAsEmpty(options.SourceInfoOutputPath).empty()) { - SharedTimer timer("Serialization, swiftsourceinfo, to buffer"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftsourceinfo, to buffer"); llvm::SmallString<1024> buf; llvm::raw_svector_ostream stream(buf); writeSourceInfoToStream(stream, DC); @@ -4804,7 +4979,8 @@ void swift::serialize(ModuleOrSourceFile DC, bool hadError = withOutputFile(getContext(DC).Diags, options.OutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftmodule"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftmodule"); Serializer::writeToStream(out, DC, M, options); return false; }); @@ -4815,7 +4991,8 @@ void swift::serialize(ModuleOrSourceFile DC, (void)withOutputFile(getContext(DC).Diags, options.DocOutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftdoc"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftdoc"); writeDocToStream(out, DC, options.GroupInfoPath); return false; }); @@ -4825,7 +5002,8 @@ void swift::serialize(ModuleOrSourceFile DC, (void)withOutputFile(getContext(DC).Diags, options.SourceInfoOutputPath, [&](raw_ostream &out) { - SharedTimer timer("Serialization, swiftsourceinfo"); + FrontendStatsTracer tracer(getContext(DC).Stats, + "Serialization, swiftsourceinfo"); writeSourceInfoToStream(out, DC); return false; }); diff --git a/lib/Serialization/SerializeDoc.cpp b/lib/Serialization/SerializeDoc.cpp index 8bac1ec16e5e8..5947e1b5c7495 100644 --- a/lib/Serialization/SerializeDoc.cpp +++ b/lib/Serialization/SerializeDoc.cpp @@ -153,8 +153,16 @@ class DeclGroupNameContext { // We need the file path, so there has to be a location. if (VD->getLoc().isInvalid()) return 0; - StringRef FullPath = - VD->getDeclContext()->getParentSourceFile()->getFilename(); + + // If the decl being serialized isn't actually from a source file, don't + // put it in a group. + // FIXME: How do we preserve group information through partial module + // merging for multi-frontend builds, then? + SourceFile *SF = VD->getDeclContext()->getParentSourceFile(); + if (!SF) + return 0; + + StringRef FullPath = SF->getFilename(); if (FullPath.empty()) return 0; StringRef FileName = llvm::sys::path::filename(FullPath); @@ -591,12 +599,12 @@ class StringWriter { llvm::SmallString<1024> Buffer; public: uint32_t getTextOffset(StringRef Text) { - if (IndexMap.find(Text) == IndexMap.end()) { - IndexMap.insert({Text, Buffer.size()}); + auto IterAndIsNew = IndexMap.insert({Text, Buffer.size()}); + if (IterAndIsNew.second) { Buffer.append(Text); Buffer.push_back('\0'); } - return IndexMap[Text]; + return IterAndIsNew.first->getValue(); } void emitSourceFilesRecord(llvm::BitstreamWriter &Out) { diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 7af9f58a06163..3d9bbec929e62 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -347,7 +347,7 @@ ValueID SILSerializer::addValueRef(const ValueBase *Val) { if (auto *Undef = dyn_cast(Val)) { // The first two IDs are reserved for SILUndef. - if (Undef->getOwnershipKind() == ValueOwnershipKind::Any) + if (Undef->getOwnershipKind() == ValueOwnershipKind::None) return 0; assert(Undef->getOwnershipKind() == ValueOwnershipKind::Owned); @@ -1235,14 +1235,14 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } #define UNCHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Copy##Name##ValueInst: + case SILInstructionKind::StrongCopy##Name##ValueInst: #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: -#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ - case SILInstructionKind::Name##RetainInst: \ - case SILInstructionKind::Name##ReleaseInst: \ - case SILInstructionKind::StrongRetain##Name##Inst: \ - case SILInstructionKind::Copy##Name##ValueInst: +#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ + case SILInstructionKind::Name##RetainInst: \ + case SILInstructionKind::Name##ReleaseInst: \ + case SILInstructionKind::StrongRetain##Name##Inst: \ + case SILInstructionKind::StrongCopy##Name##ValueInst: #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") @@ -1569,60 +1569,67 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { // Checked Conversion instructions. case SILInstructionKind::UnconditionalCheckedCastInst: { auto CI = cast(&SI); - SILInstCastLayout::emitRecord(Out, ScratchRecord, - SILAbbrCodes[SILInstCastLayout::Code], - (unsigned)SI.getKind(), /*attr*/ 0, - S.addTypeRef(CI->getType().getASTType()), - (unsigned)CI->getType().getCategory(), - S.addTypeRef(CI->getOperand()->getType().getASTType()), - (unsigned)CI->getOperand()->getType().getCategory(), - addValueRef(CI->getOperand())); + ValueID listOfValues[] = { + addValueRef(CI->getOperand()), + S.addTypeRef(CI->getSourceLoweredType().getASTType()), + (unsigned)CI->getSourceLoweredType().getCategory(), + S.addTypeRef(CI->getTargetFormalType()) + }; + + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), + S.addTypeRef(CI->getTargetLoweredType().getASTType()), + (unsigned)CI->getTargetLoweredType().getCategory(), + llvm::makeArrayRef(listOfValues)); break; } case SILInstructionKind::UnconditionalCheckedCastAddrInst: { auto CI = cast(&SI); ValueID listOfValues[] = { - S.addTypeRef(CI->getSourceType()), + S.addTypeRef(CI->getSourceFormalType()), addValueRef(CI->getSrc()), - S.addTypeRef(CI->getSrc()->getType().getASTType()), - (unsigned)CI->getSrc()->getType().getCategory(), - S.addTypeRef(CI->getTargetType()), + S.addTypeRef(CI->getSourceLoweredType().getASTType()), + (unsigned)CI->getSourceLoweredType().getCategory(), + S.addTypeRef(CI->getTargetFormalType()), addValueRef(CI->getDest()) }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CI->getDest()->getType().getASTType()), - (unsigned)CI->getDest()->getType().getCategory(), + S.addTypeRef(CI->getTargetLoweredType().getASTType()), + (unsigned)CI->getTargetLoweredType().getCategory(), llvm::makeArrayRef(listOfValues)); break; } case SILInstructionKind::UnconditionalCheckedCastValueInst: { auto CI = cast(&SI); - SILInstCastLayout::emitRecord( - Out, ScratchRecord, SILAbbrCodes[SILInstCastLayout::Code], - (unsigned)SI.getKind(), - /*attr*/ 0, - S.addTypeRef(CI->getType().getASTType()), - (unsigned)CI->getType().getCategory(), - S.addTypeRef(CI->getOperand()->getType().getASTType()), - (unsigned)CI->getOperand()->getType().getCategory(), - addValueRef(CI->getOperand())); + ValueID listOfValues[] = { + S.addTypeRef(CI->getSourceFormalType()), + addValueRef(CI->getOperand()), + S.addTypeRef(CI->getSourceLoweredType().getASTType()), + (unsigned)CI->getSourceLoweredType().getCategory(), + S.addTypeRef(CI->getTargetFormalType()) + }; + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), + S.addTypeRef(CI->getTargetLoweredType().getASTType()), + (unsigned)CI->getTargetLoweredType().getCategory(), + llvm::makeArrayRef(listOfValues)); break; } case SILInstructionKind::UncheckedRefCastAddrInst: { auto CI = cast(&SI); ValueID listOfValues[] = { - S.addTypeRef(CI->getSourceType()), + S.addTypeRef(CI->getSourceFormalType()), addValueRef(CI->getSrc()), - S.addTypeRef(CI->getSrc()->getType().getASTType()), - (unsigned)CI->getSrc()->getType().getCategory(), - S.addTypeRef(CI->getTargetType()), + S.addTypeRef(CI->getSourceLoweredType().getASTType()), + (unsigned)CI->getSourceLoweredType().getCategory(), + S.addTypeRef(CI->getTargetFormalType()), addValueRef(CI->getDest()) }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CI->getDest()->getType().getASTType()), - (unsigned)CI->getDest()->getType().getCategory(), + S.addTypeRef(CI->getTargetLoweredType().getASTType()), + (unsigned)CI->getTargetLoweredType().getCategory(), llvm::makeArrayRef(listOfValues)); break; } @@ -2004,66 +2011,62 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } case SILInstructionKind::CheckedCastBranchInst: { - // Format: the cast kind, a typed value, a BasicBlock ID for success, - // a BasicBlock ID for failure. Uses SILOneTypeValuesLayout. const CheckedCastBranchInst *CBI = cast(&SI); - SmallVector ListOfValues; - ListOfValues.push_back(CBI->isExact()), - ListOfValues.push_back(addValueRef(CBI->getOperand())); - ListOfValues.push_back( - S.addTypeRef(CBI->getOperand()->getType().getASTType())); - ListOfValues.push_back((unsigned)CBI->getOperand()->getType().getCategory()); - ListOfValues.push_back(BasicBlockMap[CBI->getSuccessBB()]); - ListOfValues.push_back(BasicBlockMap[CBI->getFailureBB()]); + ValueID listOfValues[] = { + CBI->isExact(), + addValueRef(CBI->getOperand()), + S.addTypeRef(CBI->getSourceLoweredType().getASTType()), + (unsigned)CBI->getSourceLoweredType().getCategory(), + S.addTypeRef(CBI->getTargetFormalType()), + BasicBlockMap[CBI->getSuccessBB()], + BasicBlockMap[CBI->getFailureBB()] + }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getCastType().getASTType()), - (unsigned)CBI->getCastType().getCategory(), - ListOfValues); + S.addTypeRef(CBI->getTargetLoweredType().getASTType()), + (unsigned)CBI->getTargetLoweredType().getCategory(), + llvm::makeArrayRef(listOfValues)); break; } case SILInstructionKind::CheckedCastValueBranchInst: { - // Format: the cast kind, a typed value, a BasicBlock ID for success, - // a BasicBlock ID for failure. Uses SILOneTypeValuesLayout. const CheckedCastValueBranchInst *CBI = cast(&SI); - SmallVector ListOfValues; - ListOfValues.push_back(addValueRef(CBI->getOperand())); - ListOfValues.push_back( - S.addTypeRef(CBI->getOperand()->getType().getASTType())); - ListOfValues.push_back( - (unsigned)CBI->getOperand()->getType().getCategory()); - ListOfValues.push_back(BasicBlockMap[CBI->getSuccessBB()]); - ListOfValues.push_back(BasicBlockMap[CBI->getFailureBB()]); + ValueID listOfValues[] = { + S.addTypeRef(CBI->getSourceFormalType()), + addValueRef(CBI->getOperand()), + S.addTypeRef(CBI->getSourceLoweredType().getASTType()), + (unsigned)CBI->getSourceLoweredType().getCategory(), + S.addTypeRef(CBI->getTargetFormalType()), + BasicBlockMap[CBI->getSuccessBB()], + BasicBlockMap[CBI->getFailureBB()] + }; SILOneTypeValuesLayout::emitRecord( Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getCastType().getASTType()), - (unsigned)CBI->getCastType().getCategory(), ListOfValues); + S.addTypeRef(CBI->getTargetLoweredType().getASTType()), + (unsigned)CBI->getTargetLoweredType().getCategory(), + llvm::makeArrayRef(listOfValues)); break; } case SILInstructionKind::CheckedCastAddrBranchInst: { - // Format: the cast kind, two typed values, a BasicBlock ID for - // success, a BasicBlock ID for failure. Uses SILOneTypeValuesLayout; - // the type is the type of the second (dest) operand. auto CBI = cast(&SI); ValueID listOfValues[] = { toStableCastConsumptionKind(CBI->getConsumptionKind()), - S.addTypeRef(CBI->getSourceType()), + S.addTypeRef(CBI->getSourceFormalType()), addValueRef(CBI->getSrc()), - S.addTypeRef(CBI->getSrc()->getType().getASTType()), - (unsigned)CBI->getSrc()->getType().getCategory(), - S.addTypeRef(CBI->getTargetType()), + S.addTypeRef(CBI->getSourceLoweredType().getASTType()), + (unsigned)CBI->getSourceLoweredType().getCategory(), + S.addTypeRef(CBI->getTargetFormalType()), addValueRef(CBI->getDest()), BasicBlockMap[CBI->getSuccessBB()], BasicBlockMap[CBI->getFailureBB()] }; SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, SILAbbrCodes[SILOneTypeValuesLayout::Code], (unsigned)SI.getKind(), - S.addTypeRef(CBI->getDest()->getType().getASTType()), - (unsigned)CBI->getDest()->getType().getCategory(), + S.addTypeRef(CBI->getTargetLoweredType().getASTType()), + (unsigned)CBI->getTargetLoweredType().getCategory(), llvm::makeArrayRef(listOfValues)); break; } @@ -2471,7 +2474,6 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); - registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 2ece9e5e0bf63..1e9594f75ed15 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -104,8 +104,10 @@ Optional forEachModuleSearchPath( // Defined out-of-line so that we can see ~ModuleFile. SerializedModuleLoaderBase::SerializedModuleLoaderBase( - ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode) - : ModuleLoader(tracker), Ctx(ctx), LoadMode(loadMode) {} + ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, + bool IgnoreSwiftSourceInfoFile) + : ModuleLoader(tracker), Ctx(ctx), LoadMode(loadMode), + IgnoreSwiftSourceInfoFile(IgnoreSwiftSourceInfoFile) {} SerializedModuleLoaderBase::~SerializedModuleLoaderBase() = default; SerializedModuleLoader::~SerializedModuleLoader() = default; @@ -240,8 +242,9 @@ void SerializedModuleLoader::collectVisibleTopLevelModuleNames( names, file_types::getExtension(file_types::TY_SwiftModuleFile)); } -std::error_code SerializedModuleLoaderBase::openModuleDocFile( - AccessPathElem ModuleID, StringRef ModuleDocPath, +std::error_code SerializedModuleLoaderBase::openModuleDocFileIfPresent( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, std::unique_ptr *ModuleDocBuffer) { if (!ModuleDocBuffer) @@ -251,6 +254,9 @@ std::error_code SerializedModuleLoaderBase::openModuleDocFile( // Try to open the module documentation file. If it does not exist, ignore // the error. However, pass though all other errors. + SmallString<256> + ModuleDocPath{BaseName.getName(file_types::TY_SwiftModuleDocFile)}; + llvm::ErrorOr> ModuleDocOrErr = FS.getBufferForFile(ModuleDocPath); if (ModuleDocOrErr) { @@ -263,115 +269,133 @@ std::error_code SerializedModuleLoaderBase::openModuleDocFile( return std::error_code(); } -void +std::error_code SerializedModuleLoaderBase::openModuleSourceInfoFileIfPresent( - AccessPathElem ModuleID, - StringRef ModulePath, - StringRef ModuleSourceInfoFilename, - std::unique_ptr *ModuleSourceInfoBuffer) { - if (!ModuleSourceInfoBuffer) - return; + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + std::unique_ptr *ModuleSourceInfoBuffer) { + if (IgnoreSwiftSourceInfoFile || !ModuleSourceInfoBuffer) + return std::error_code(); + llvm::vfs::FileSystem &FS = *Ctx.SourceMgr.getFileSystem(); - llvm::SmallString<128> PathWithoutProjectDir(ModulePath); - llvm::sys::path::replace_extension(PathWithoutProjectDir, - file_types::getExtension(file_types::TY_SwiftSourceInfoFile)); - llvm::SmallString<128> PathWithProjectDir = PathWithoutProjectDir.str(); + + llvm::SmallString<128> + PathWithoutProjectDir{BaseName.getName(file_types::TY_SwiftSourceInfoFile)}; + + llvm::SmallString<128> PathWithProjectDir = PathWithoutProjectDir; + + // Insert "Project" before the filename in PathWithProjectDir. StringRef FileName = llvm::sys::path::filename(PathWithoutProjectDir); llvm::sys::path::remove_filename(PathWithProjectDir); llvm::sys::path::append(PathWithProjectDir, "Project"); llvm::sys::path::append(PathWithProjectDir, FileName); // Try to open the module source info file from the "Project" directory. - // If it does not exist, ignore the error. - if (auto ModuleSourceInfoOrErr = FS.getBufferForFile(PathWithProjectDir)) { - *ModuleSourceInfoBuffer = std::move(*ModuleSourceInfoOrErr); - return; - } - // Try to open the module source info file adjacent to the .swiftmodule file. - if (auto ModuleSourceInfoOrErr = FS.getBufferForFile(PathWithoutProjectDir)) { + llvm::ErrorOr> + ModuleSourceInfoOrErr = FS.getBufferForFile(PathWithProjectDir); + + // If it does not exist, try to open the module source info file adjacent to + // the .swiftmodule file. + if (ModuleSourceInfoOrErr.getError() == std::errc::no_such_file_or_directory) + ModuleSourceInfoOrErr = FS.getBufferForFile(PathWithoutProjectDir); + + // If we ended up with a different file system error, return it. + if (ModuleSourceInfoOrErr) *ModuleSourceInfoBuffer = std::move(*ModuleSourceInfoOrErr); - return; - } -} + else if (ModuleSourceInfoOrErr.getError() != + std::errc::no_such_file_or_directory) + return ModuleSourceInfoOrErr.getError(); -std::error_code SerializedModuleLoaderBase::openModuleFiles( - AccessPathElem ModuleID, StringRef ModulePath, StringRef ModuleDocPath, - StringRef ModuleSourceInfoFileName, - std::unique_ptr *ModuleBuffer, - std::unique_ptr *ModuleDocBuffer, - std::unique_ptr *ModuleSourceInfoBuffer) { - assert(((ModuleBuffer && ModuleDocBuffer) || - (!ModuleBuffer && !ModuleDocBuffer)) && - "Module and Module Doc buffer must both be initialized or NULL"); + return std::error_code(); +} +std::error_code SerializedModuleLoaderBase::openModuleFile( + AccessPathElem ModuleID, const SerializedModuleBaseName &BaseName, + std::unique_ptr *ModuleBuffer) { llvm::vfs::FileSystem &FS = *Ctx.SourceMgr.getFileSystem(); // Try to open the module file first. If we fail, don't even look for the // module documentation file. + SmallString<256> ModulePath{BaseName.getName(file_types::TY_SwiftModuleFile)}; - // If there are no buffers to load into, simply check for the existence of + // If there's no buffer to load into, simply check for the existence of // the module file. - if (!(ModuleBuffer || ModuleDocBuffer)) { + if (!ModuleBuffer) { llvm::ErrorOr statResult = FS.status(ModulePath); if (!statResult) return statResult.getError(); + if (!statResult->exists()) return std::make_error_code(std::errc::no_such_file_or_directory); + // FIXME: llvm::vfs::FileSystem doesn't give us information on whether or // not we can /read/ the file without actually trying to do so. return std::error_code(); } + // Actually load the file and error out if necessary. llvm::ErrorOr> ModuleOrErr = FS.getBufferForFile(ModulePath); if (!ModuleOrErr) return ModuleOrErr.getError(); - // Open .swiftsourceinfo file if it's present. - openModuleSourceInfoFileIfPresent(ModuleID, ModulePath, - ModuleSourceInfoFileName, - ModuleSourceInfoBuffer); - auto ModuleDocErr = - openModuleDocFile(ModuleID, ModuleDocPath, ModuleDocBuffer); - if (ModuleDocErr) - return ModuleDocErr; - *ModuleBuffer = std::move(ModuleOrErr.get()); - return std::error_code(); } std::error_code SerializedModuleLoader::findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, StringRef ModuleSourceInfoFileName, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) { + assert(((ModuleBuffer && ModuleDocBuffer) || + (!ModuleBuffer && !ModuleDocBuffer)) && + "Module and Module Doc buffer must both be initialized or NULL"); + if (LoadMode == ModuleLoadingMode::OnlyInterface) return std::make_error_code(std::errc::not_supported); - llvm::SmallString<256> ModulePath{DirPath}; - llvm::sys::path::append(ModulePath, ModuleFilename); - llvm::SmallString<256> ModuleDocPath{DirPath}; - llvm::sys::path::append(ModuleDocPath, ModuleDocFilename); - return SerializedModuleLoaderBase::openModuleFiles(ModuleID, - ModulePath, - ModuleDocPath, - ModuleSourceInfoFileName, - ModuleBuffer, - ModuleDocBuffer, - ModuleSourceInfoBuffer); + auto ModuleErr = openModuleFile(ModuleID, BaseName, ModuleBuffer); + if (ModuleErr) + return ModuleErr; + + // If there are no buffers to load into, all we care about is whether the + // module file existed. + if (ModuleBuffer || ModuleDocBuffer || ModuleSourceInfoBuffer) { + auto ModuleSourceInfoError = openModuleSourceInfoFileIfPresent( + ModuleID, BaseName, ModuleSourceInfoBuffer + ); + if (ModuleSourceInfoError) + return ModuleSourceInfoError; + + auto ModuleDocErr = openModuleDocFileIfPresent( + ModuleID, BaseName, ModuleDocBuffer + ); + if (ModuleDocErr) + return ModuleDocErr; + } + + return std::error_code(); } bool SerializedModuleLoader::maybeDiagnoseTargetMismatch( - SourceLoc sourceLocation, StringRef moduleName, StringRef archName, - StringRef directoryPath) { + SourceLoc sourceLocation, StringRef moduleName, + const SerializedModuleBaseName &absoluteBaseName) { llvm::vfs::FileSystem &fs = *Ctx.SourceMgr.getFileSystem(); + // Get the last component of the base name, which is the target-specific one. + auto target = llvm::sys::path::filename(absoluteBaseName.baseName); + + // Strip off the last component to get the .swiftmodule folder. + auto dir = absoluteBaseName.baseName; + llvm::sys::path::remove_filename(dir); + std::error_code errorCode; std::string foundArchs; for (llvm::vfs::directory_iterator directoryIterator = - fs.dir_begin(directoryPath, errorCode), endIterator; + fs.dir_begin(dir, errorCode), endIterator; directoryIterator != endIterator; directoryIterator.increment(errorCode)) { if (errorCode) @@ -394,44 +418,44 @@ bool SerializedModuleLoader::maybeDiagnoseTargetMismatch( } Ctx.Diags.diagnose(sourceLocation, diag::sema_no_import_target, moduleName, - archName, foundArchs); + target, foundArchs); return true; } -struct ModuleFilenamePair { - llvm::SmallString<64> module; - llvm::SmallString<64> moduleDoc; - llvm::SmallString<64> moduleSourceInfo; - - ModuleFilenamePair(StringRef baseName) - : module(baseName), moduleDoc(baseName), moduleSourceInfo(baseName) - { - module += '.'; - module += file_types::getExtension(file_types::TY_SwiftModuleFile); +SerializedModuleBaseName::SerializedModuleBaseName( + StringRef parentDir, const SerializedModuleBaseName &name) + : baseName(parentDir) { + llvm::sys::path::append(baseName, name.baseName); +} - moduleDoc += '.'; - moduleDoc += file_types::getExtension(file_types::TY_SwiftModuleDocFile); +std::string SerializedModuleBaseName::getName(file_types::ID fileTy) const { + auto result = baseName; + result += '.'; + result += file_types::getExtension(fileTy); - moduleSourceInfo += '.'; - moduleSourceInfo += file_types::getExtension(file_types::TY_SwiftSourceInfoFile); - } -}; + return result.str(); +} bool SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, + SmallVectorImpl *moduleInterfacePath, std::unique_ptr *moduleBuffer, std::unique_ptr *moduleDocBuffer, std::unique_ptr *moduleSourceInfoBuffer, bool &isFramework, bool &isSystemModule) { - llvm::SmallString<64> moduleName(moduleID.first.str()); - ModuleFilenamePair fileNames(moduleName); + SmallString<32> moduleName(moduleID.Item.str()); + SerializedModuleBaseName genericBaseName(moduleName); + + auto genericModuleFileName = + genericBaseName.getName(file_types::TY_SwiftModuleFile); - SmallVector targetFileNamePairs; - SmallString<32> primaryTargetSpecificName; + SmallVector targetSpecificBaseNames; forEachTargetModuleBasename(Ctx, [&](StringRef targetName) { - targetFileNamePairs.emplace_back(targetName); - if (primaryTargetSpecificName.empty()) - primaryTargetSpecificName = targetName; + // Construct a base name like ModuleName.swiftmodule/arch-vendor-os + SmallString<64> targetBaseName{genericModuleFileName}; + llvm::sys::path::append(targetBaseName, targetName); + + targetSpecificBaseNames.emplace_back(targetBaseName.str()); }); auto &fs = *Ctx.SourceMgr.getFileSystem(); @@ -442,10 +466,18 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, /// was diagnosed, or None if neither one happened and the search should /// continue. auto findTargetSpecificModuleFiles = [&]() -> Optional { - for (const auto &targetFileNames : targetFileNamePairs) { - auto result = findModuleFilesInDirectory(moduleID, currPath, - targetFileNames.module, targetFileNames.moduleDoc, - targetFileNames.moduleSourceInfo, + Optional firstAbsoluteBaseName; + + for (const auto &targetSpecificBaseName : targetSpecificBaseNames) { + SerializedModuleBaseName + absoluteBaseName{currPath, targetSpecificBaseName}; + + if (!firstAbsoluteBaseName.hasValue()) + firstAbsoluteBaseName.emplace(absoluteBaseName); + + auto result = findModuleFilesInDirectory(moduleID, + absoluteBaseName, + moduleInterfacePath, moduleBuffer, moduleDocBuffer, moduleSourceInfoBuffer); if (!result) { @@ -459,8 +491,9 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // We can only get here if all targetFileNamePairs failed with // 'std::errc::no_such_file_or_directory'. - if (maybeDiagnoseTargetMismatch(moduleID.second, moduleName, - primaryTargetSpecificName, currPath)) { + if (firstAbsoluteBaseName + && maybeDiagnoseTargetMismatch(moduleID.Loc, moduleName, + *firstAbsoluteBaseName)) { return false; } else { return None; @@ -478,7 +511,6 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, case SearchPathKind::Import: case SearchPathKind::RuntimeLibrary: { isFramework = false; - llvm::sys::path::append(currPath, fileNames.module.str()); bool checkTargetSpecificModule; if (Kind == SearchPathKind::RuntimeLibrary) { @@ -487,7 +519,10 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // always use single-architecture swiftmodules. checkTargetSpecificModule = Ctx.LangOpts.Target.isOSDarwin(); } else { - llvm::ErrorOr statResult = fs.status(currPath); + auto modulePath = currPath; + llvm::sys::path::append(modulePath, genericModuleFileName); + + llvm::ErrorOr statResult = fs.status(modulePath); // Even if stat fails, we can't just return the error; the path // we're looking for might not be "Foo.swiftmodule". checkTargetSpecificModule = statResult && statResult->isDirectory(); @@ -497,9 +532,10 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // A .swiftmodule directory contains architecture-specific files. return findTargetSpecificModuleFiles(); + SerializedModuleBaseName absoluteBaseName{currPath, genericBaseName}; + auto result = findModuleFilesInDirectory( - moduleID, path, fileNames.module.str(), fileNames.moduleDoc.str(), - fileNames.moduleSourceInfo.str(), + moduleID, absoluteBaseName, moduleInterfacePath, moduleBuffer, moduleDocBuffer, moduleSourceInfoBuffer); if (!result) return true; @@ -510,8 +546,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, } case SearchPathKind::Framework: { isFramework = true; - llvm::sys::path::append(currPath, - moduleID.first.str() + ".framework"); + llvm::sys::path::append(currPath, moduleName + ".framework"); // Check if the framework directory exists. if (!fs.exists(currPath)) @@ -519,7 +554,7 @@ SerializedModuleLoaderBase::findModule(AccessPathElem moduleID, // Frameworks always use architecture-specific files within a // .swiftmodule directory. - llvm::sys::path::append(currPath, "Modules", fileNames.module.str()); + llvm::sys::path::append(currPath, "Modules"); return findTargetSpecificModuleFiles(); } } @@ -566,6 +601,7 @@ getOSAndVersionForDiagnostics(const llvm::Triple &triple) { FileUnit *SerializedModuleLoaderBase::loadAST( ModuleDecl &M, Optional diagLoc, + StringRef moduleInterfacePath, std::unique_ptr moduleInputBuffer, std::unique_ptr moduleDocInputBuffer, std::unique_ptr moduleSourceInfoInputBuffer, @@ -587,7 +623,8 @@ FileUnit *SerializedModuleLoaderBase::loadAST( serialization::ExtendedValidationInfo extendedInfo; std::unique_ptr loadedModuleFile; serialization::ValidationInfo loadInfo = - ModuleFile::load(std::move(moduleInputBuffer), + ModuleFile::load(moduleInterfacePath, + std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), isFramework, loadedModuleFile, @@ -739,7 +776,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( auto circularDependencyIter = llvm::find_if(loadedModuleFile->getDependencies(), [](const ModuleFile::Dependency &next) { - return !next.Import.second->hasResolvedImports(); + return next.isLoaded() && + !next.Import.second->hasResolvedImports(); }); assert(circularDependencyIter != loadedModuleFile->getDependencies().end() && @@ -816,17 +854,23 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( } bool SerializedModuleLoaderBase::canImportModule( - std::pair mID) { + Located mID) { // Look on disk. + SmallVector *unusedModuleInterfacePath = nullptr; + std::unique_ptr *unusedModuleBuffer = nullptr; + std::unique_ptr *unusedModuleDocBuffer = nullptr; + std::unique_ptr *unusedModuleSourceInfoBuffer = nullptr; bool isFramework = false; bool isSystemModule = false; - return findModule(mID, nullptr, nullptr, nullptr, isFramework, isSystemModule); + return findModule(mID, unusedModuleInterfacePath, unusedModuleBuffer, + unusedModuleDocBuffer, unusedModuleSourceInfoBuffer, + isFramework, isSystemModule); } bool MemoryBufferSerializedModuleLoader::canImportModule( - std::pair mID) { + Located mID) { // See if we find it in the registered memory buffers. - return MemoryBuffers.count(mID.first.str()); + return MemoryBuffers.count(mID.Item.str()); } ModuleDecl * @@ -840,13 +884,14 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, bool isFramework = false; bool isSystemModule = false; + llvm::SmallString<256> moduleInterfacePath; std::unique_ptr moduleInputBuffer; std::unique_ptr moduleDocInputBuffer; std::unique_ptr moduleSourceInfoInputBuffer; // Look on disk. - if (!findModule(moduleID, &moduleInputBuffer, &moduleDocInputBuffer, - &moduleSourceInfoInputBuffer, + if (!findModule(moduleID, &moduleInterfacePath, &moduleInputBuffer, + &moduleDocInputBuffer, &moduleSourceInfoInputBuffer, isFramework, isSystemModule)) { return nullptr; } @@ -860,13 +905,17 @@ SerializedModuleLoaderBase::loadModule(SourceLoc importLoc, assert(moduleInputBuffer); - auto M = ModuleDecl::create(moduleID.first, Ctx); + auto M = ModuleDecl::create(moduleID.Item, Ctx); M->setIsSystemModule(isSystemModule); - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.Item] = M; SWIFT_DEFER { M->setHasResolvedImports(); }; - if (!loadAST(*M, moduleID.second, std::move(moduleInputBuffer), - std::move(moduleDocInputBuffer), std::move(moduleSourceInfoInputBuffer), + StringRef moduleInterfacePathStr = + Ctx.AllocateCopy(moduleInterfacePath.str()); + + if (!loadAST(*M, moduleID.Loc, moduleInterfacePathStr, + std::move(moduleInputBuffer), std::move(moduleDocInputBuffer), + std::move(moduleSourceInfoInputBuffer), isFramework, /*treatAsPartialModule*/false)) { M->setFailedToLoad(); } @@ -888,7 +937,7 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, // FIXME: Right now this works only with access paths of length 1. // Once submodules are designed, this needs to support suffix // matching and a search path. - auto bufIter = MemoryBuffers.find(moduleID.first.str()); + auto bufIter = MemoryBuffers.find(moduleID.Item.str()); if (bufIter == MemoryBuffers.end()) return nullptr; @@ -899,15 +948,16 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc, MemoryBuffers.erase(bufIter); assert(moduleInputBuffer); - auto *M = ModuleDecl::create(moduleID.first, Ctx); + auto *M = ModuleDecl::create(moduleID.Item, Ctx); SWIFT_DEFER { M->setHasResolvedImports(); }; - if (!loadAST(*M, moduleID.second, std::move(moduleInputBuffer), {}, {}, + if (!loadAST(*M, moduleID.Loc, /*moduleInterfacePath*/ "", + std::move(moduleInputBuffer), {}, {}, isFramework, treatAsPartialModule)) { return nullptr; } - Ctx.LoadedModules[moduleID.first] = M; + Ctx.LoadedModules[moduleID.Item] = M; return M; } @@ -935,8 +985,9 @@ void SerializedModuleLoaderBase::loadObjCMethods( } std::error_code MemoryBufferSerializedModuleLoader::findModuleFilesInDirectory( - AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, - StringRef ModuleDocFilename, StringRef ModuleSourceInfoFilename, + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, std::unique_ptr *ModuleBuffer, std::unique_ptr *ModuleDocBuffer, std::unique_ptr *ModuleSourceInfoBuffer) { @@ -948,8 +999,8 @@ std::error_code MemoryBufferSerializedModuleLoader::findModuleFilesInDirectory( } bool MemoryBufferSerializedModuleLoader::maybeDiagnoseTargetMismatch( - SourceLoc sourceLocation, StringRef moduleName, StringRef archName, - StringRef directoryPath) { + SourceLoc sourceLocation, StringRef moduleName, + const SerializedModuleBaseName &absoluteBaseName) { return false; } @@ -1099,6 +1150,12 @@ SerializedASTFile::getTopLevelDecls(SmallVectorImpl &results) const { File.getTopLevelDecls(results); } +void SerializedASTFile::getTopLevelDeclsWhereAttributesMatch( + SmallVectorImpl &results, + llvm::function_ref matchAttributes) const { + File.getTopLevelDecls(results, matchAttributes); +} + void SerializedASTFile::getPrecedenceGroups( SmallVectorImpl &results) const { File.getPrecedenceGroups(results); diff --git a/lib/Serialization/SerializedSILLoader.cpp b/lib/Serialization/SerializedSILLoader.cpp index 3e910c8e5324f..683d7e30573cd 100644 --- a/lib/Serialization/SerializedSILLoader.cpp +++ b/lib/Serialization/SerializedSILLoader.cpp @@ -42,12 +42,13 @@ SerializedSILLoader::SerializedSILLoader( SerializedSILLoader::~SerializedSILLoader() {} -SILFunction *SerializedSILLoader::lookupSILFunction(SILFunction *Callee) { +SILFunction *SerializedSILLoader::lookupSILFunction(SILFunction *Callee, + bool onlyUpdateLinkage) { // It is possible that one module has a declaration of a SILFunction, while // another has the full definition. SILFunction *retVal = nullptr; for (auto &Des : LoadedSILSections) { - if (auto Func = Des->lookupSILFunction(Callee)) { + if (auto Func = Des->lookupSILFunction(Callee, onlyUpdateLinkage)) { LLVM_DEBUG(llvm::dbgs() << "Deserialized " << Func->getName() << " from " << Des->getModuleIdentifier().str() << "\n"); if (!Func->empty()) diff --git a/lib/SwiftDemangle/CMakeLists.txt b/lib/SwiftDemangle/CMakeLists.txt index b2c775a76a1c7..864bea94cc167 100644 --- a/lib/SwiftDemangle/CMakeLists.txt +++ b/lib/SwiftDemangle/CMakeLists.txt @@ -8,6 +8,9 @@ target_link_libraries(swiftDemangle PRIVATE add_dependencies(compiler swiftDemangle) swift_install_in_component(TARGETS swiftDemangle + RUNTIME + DESTINATION "bin" + COMPONENT compiler LIBRARY DESTINATION "lib${LLVM_LIBDIR_SUFFIX}" COMPONENT compiler diff --git a/lib/SwiftReflection/CMakeLists.txt b/lib/SwiftReflection/CMakeLists.txt new file mode 100644 index 0000000000000..1a20995e4c2d8 --- /dev/null +++ b/lib/SwiftReflection/CMakeLists.txt @@ -0,0 +1,8 @@ + +add_swift_host_library(swiftReflection STATIC + ${SWIFT_SOURCE_DIR}/stdlib/public/Reflection/MetadataSource.cpp + ${SWIFT_SOURCE_DIR}/stdlib/public/Reflection/TypeLowering.cpp + ${SWIFT_SOURCE_DIR}/stdlib/public/Reflection/TypeRef.cpp + ${SWIFT_SOURCE_DIR}/stdlib/public/Reflection/TypeRefBuilder.cpp) +target_link_libraries(swiftReflection PUBLIC + swiftDemangling) diff --git a/lib/SwiftRemoteMirror/CMakeLists.txt b/lib/SwiftRemoteMirror/CMakeLists.txt new file mode 100644 index 0000000000000..e67c34a24938e --- /dev/null +++ b/lib/SwiftRemoteMirror/CMakeLists.txt @@ -0,0 +1,5 @@ +add_swift_host_library(swiftRemoteMirror STATIC + ${SWIFT_SOURCE_DIR}/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + target_compile_definitions(swiftRemoteMirror PRIVATE _LIB) +endif() diff --git a/lib/SymbolGraphGen/CMakeLists.txt b/lib/SymbolGraphGen/CMakeLists.txt new file mode 100644 index 0000000000000..64f34c90254b7 --- /dev/null +++ b/lib/SymbolGraphGen/CMakeLists.txt @@ -0,0 +1,13 @@ +add_swift_host_library(swiftSymbolGraphGen STATIC + DeclarationFragmentPrinter.cpp + Edge.cpp + JSON.cpp + Symbol.cpp + SymbolGraph.cpp + SymbolGraphGen.cpp + SymbolGraphASTWalker.cpp) + +target_link_libraries(swiftSymbolGraphGen PRIVATE + swiftAST + swiftFrontend + swiftMarkup) diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp new file mode 100644 index 0000000000000..2e8db85e2d3b2 --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp @@ -0,0 +1,140 @@ +//===--- DeclarationFragmentPrinter.cpp - Declaration Fragment Printer ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void DeclarationFragmentPrinter::openFragment(FragmentKind Kind) { + assert(Kind != FragmentKind::None); + if (this->Kind != Kind) { + closeFragment(); + this->Kind = Kind, + Spelling.clear(); + USR.clear(); + } +} + +StringRef +DeclarationFragmentPrinter::getKindSpelling(FragmentKind Kind) const { + switch (Kind) { + case FragmentKind::Keyword: + return "keyword"; + case FragmentKind::Attribute: + return "attribute"; + case FragmentKind::NumberLiteral: + return "number"; + case FragmentKind::StringLiteral: + return "string"; + case FragmentKind::Identifier: + return "identifier"; + case FragmentKind::TypeIdentifier: + return "typeIdentifier"; + case FragmentKind::GenericParameter: + return "genericParameter"; + case FragmentKind::Text: + return "text"; + case FragmentKind::None: + llvm_unreachable("Fragment kind of 'None' has no spelling"); + } +} + +void DeclarationFragmentPrinter::closeFragment() { + if (Kind == FragmentKind::None) { + return; + } + + if (!Spelling.empty()) { + OS.object([&](){ + OS.attribute("kind", getKindSpelling(Kind)); + OS.attribute("spelling", Spelling.str()); + if (!USR.empty()) { + OS.attribute("preciseIdentifier", USR.str()); + } + }); + } + + Spelling.clear(); + USR.clear(); + Kind = FragmentKind::None; +} + +void DeclarationFragmentPrinter::printDeclLoc(const Decl *D) { + switch (D->getKind()) { + case DeclKind::Constructor: + case DeclKind::Destructor: + case DeclKind::Subscript: + openFragment(FragmentKind::Keyword); + break; + default: + openFragment(FragmentKind::Identifier); + break; + } +} + +void +DeclarationFragmentPrinter::printNamePre(PrintNameContext Context) { + switch (Context) { + case PrintNameContext::Keyword: + openFragment(FragmentKind::Keyword); + break; + case PrintNameContext::GenericParameter: + openFragment(FragmentKind::GenericParameter); + break; + case PrintNameContext::Attribute: + openFragment(FragmentKind::Attribute); + break; + case PrintNameContext::ClassDynamicSelf: + case PrintNameContext::FunctionParameterExternal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::FunctionParameterLocal: + openFragment(FragmentKind::Identifier); + break; + case PrintNameContext::TupleElement: + case PrintNameContext::TypeMember: + case PrintNameContext::Normal: + break; + } +} + +void DeclarationFragmentPrinter::printStructurePre(PrintStructureKind Kind, + const Decl *D) { + switch (Kind) { + case PrintStructureKind::NumberLiteral: + openFragment(FragmentKind::NumberLiteral); + break; + case PrintStructureKind::StringLiteral: + openFragment(FragmentKind::StringLiteral); + break; + default: + break; + } +} + +void DeclarationFragmentPrinter::printTypeRef(Type T, const TypeDecl *RefTo, + Identifier Name, + PrintNameContext NameContext) { + openFragment(FragmentKind::TypeIdentifier); + printText(Name.str()); + USR = Walker.getUSR(RefTo); + closeFragment(); +} + +void DeclarationFragmentPrinter::printText(StringRef Text) { + if (Kind == FragmentKind::None) { + openFragment(FragmentKind::Text); + } + Spelling.append(Text); +} diff --git a/lib/SymbolGraphGen/DeclarationFragmentPrinter.h b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h new file mode 100644 index 0000000000000..2e5ec449bd34f --- /dev/null +++ b/lib/SymbolGraphGen/DeclarationFragmentPrinter.h @@ -0,0 +1,123 @@ +//===--- DeclarationFragmentPrinter.h - Declaration Fragment Printer ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H +#define SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/Basic/LLVM.h" + +namespace swift { + +class Decl; +class Type; +class TypeDecl; + +namespace symbolgraphgen { + +struct SymbolGraphASTWalker; + +/// Prints AST nodes as a stream of tagged fragments for syntax highlighting. +/// +/// These fragments are meant to display a somewhat abbreviated part of the +/// declaration for display in documentation, ignoring things like member and +/// function bodies. +/// +/// For example, a function: +/// +/// ```swift +/// func foo() { +/// print("Hello, world!") +/// } +/// ``` +/// +/// Will have fragments representing the `func foo()` part. +class DeclarationFragmentPrinter : public ASTPrinter { + enum class FragmentKind { + None, + Keyword, + Attribute, + NumberLiteral, + StringLiteral, + Identifier, + TypeIdentifier, + GenericParameter, + Text, + }; + + SymbolGraphASTWalker &Walker; + + /// The output stream to print fragment objects to. + llvm::json::OStream &OS; + + /// The current fragment being considered. + FragmentKind Kind; + + /// The actual source text of the fragment. + SmallString<256> Spelling; + + SmallString<256> USR; + + StringRef getKindSpelling(FragmentKind Kind) const; + + /// Open a new kind of fragment without committing its spelling. + void openFragment(FragmentKind Kind); + + /// Close the current fragment if there is one, and commit it for display. + void closeFragment(); + +public: + DeclarationFragmentPrinter(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS, + Optional Key = None) + : Walker(Walker), + OS(OS), + Kind(FragmentKind::None) { + if (Key) { + OS.attributeBegin(*Key); + OS.arrayBegin(); + } else { + OS.arrayBegin(); + } + } + + void printDeclLoc(const Decl *D) override; + + void printDeclNameEndLoc(const Decl *D) override { + closeFragment(); + } + + void printNamePre(PrintNameContext Context) override; + + void printStructurePre(PrintStructureKind Kind, const Decl *D) override; + + void printNamePost(PrintNameContext Context) override { + closeFragment(); + } + + void printTypeRef(Type T, const TypeDecl *RefTo, Identifier Name, + PrintNameContext NameContext) override; + + void printText(StringRef Text) override; + + ~DeclarationFragmentPrinter() { + closeFragment(); + OS.arrayEnd(); + OS.attributeEnd(); + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_DECLARATIONFRAGMENTPRINTER_H diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp new file mode 100644 index 0000000000000..22f43eacc5402 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.cpp @@ -0,0 +1,42 @@ +//===--- Edge.cpp - Symbol Graph Edge -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "Edge.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Edge::serialize(llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("kind", Kind.Name); + OS.attribute("source", Walker->getUSR(Source)); + OS.attribute("target", Walker->getUSR(Target)); + + // In case a dependent module isn't available, serialize a fallback name. + auto TargetModuleName = Target->getModuleContext()->getName().str(); + + if (TargetModuleName != Walker->M.getName().str()) { + SmallVector, 8> TargetPathComponents; + Walker->getPathComponents(Target, TargetPathComponents); + + SmallString<128> Scratch(TargetModuleName); + for (auto it = TargetPathComponents.begin(); + it != TargetPathComponents.end(); ++it) { + Scratch.push_back('.'); + Scratch.append(*it); + } + OS.attribute("targetFallback", Scratch.str()); + } + }); +} diff --git a/lib/SymbolGraphGen/Edge.h b/lib/SymbolGraphGen/Edge.h new file mode 100644 index 0000000000000..e22d2880151a7 --- /dev/null +++ b/lib/SymbolGraphGen/Edge.h @@ -0,0 +1,167 @@ +//===--- Edge.h - Symbol Graph Edge ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_EDGE_H +#define SWIFT_SYMBOLGRAPHGEN_EDGE_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Decl.h" +#include "swift/Basic/LLVM.h" + +#include "JSON.h" +#include "Symbol.h" + +namespace swift { +namespace symbolgraphgen { + +/// The kind of relationship, tagging an edge in the graph. +struct RelationshipKind { + StringRef Name; + + RelationshipKind(llvm::StringRef Name) : Name(Name) {} + + /** + A symbol A is a member of another symbol B. + + For example, a method or field of a class would be a member of that class. + + The implied inverse of this relationship is a symbol B is the owner + of a member symbol A. + */ + static inline RelationshipKind MemberOf() { + return RelationshipKind { "memberOf" }; + } + + /** + A symbol A conforms to an interface/protocol symbol B. + + For example, a class `C` that conforms to protocol `P` in Swift would use + this relationship. + + The implied inverse of this relationship is a symbol B that has + a conformer A. + */ + static inline RelationshipKind ConformsTo() { + return RelationshipKind { "conformsTo" }; + } + /** + A symbol A inherits from another symbol B. + + For example, a derived class inherits from a base class, or a protocol + that refines another protocol would use this relationship. + + The implied inverse of this relationship is a symbol B is a base + of another symbol A. + */ + static inline RelationshipKind InheritsFrom() { + return RelationshipKind { "inheritsFrom" }; + } + /** + A symbol A serves as a default implementation of an interface requirement B. + + The implied inverse of this relationship is an interface requirement B + has a default implementation of A. + */ + static inline RelationshipKind DefaultImplementationOf() { + return RelationshipKind { "defaultImplementationOf" }; + } + /** + A symbol A overrides another symbol B, such as through inheritance. + + The implied inverse of this relationship is a symbol A is the base + of symbol B. + */ + static inline RelationshipKind Overrides() { + return RelationshipKind { "overrides" }; + } + /** + A symbol A is a requirement of interface B. + + The implied inverse of this relationship is an interface B + has a requirement of A. + */ + static inline RelationshipKind RequirementOf() { + return RelationshipKind { "requirementOf" }; + } + /** + A symbol A is an optional requirement of interface B. + + The implied inverse of this relationship is an interface B + has an optional requirement of A. + */ + static inline RelationshipKind OptionalRequirementOf() { + return RelationshipKind { "optionalRequirementOf" }; + } + + bool operator==(const RelationshipKind &Other) const { + return Name == Other.Name; + } + + bool operator<(const RelationshipKind &Other) const { + return Name < Other.Name; + } +}; + +/// A relationship between two symbols: an edge in a directed graph. +struct Edge { + SymbolGraphASTWalker *Walker; + + /// The kind of relationship this edge represents. + RelationshipKind Kind; + + /// The precise identifier of the source symbol node. + const ValueDecl *Source; + + /// The precise identifier of the target symbol node. + const ValueDecl *Target; + + void serialize(llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +namespace llvm { +using Edge = swift::symbolgraphgen::Edge; +template <> struct DenseMapInfo { + static inline Edge getEmptyKey() { + return { + nullptr, + { "Empty" }, + nullptr, + nullptr, + }; + } + static inline Edge getTombstoneKey() { + return { + nullptr, + { "Tombstone" }, + nullptr, + nullptr, + }; + } + static unsigned getHashValue(const Edge E) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(E.Kind.Name); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Source)); + H ^= DenseMapInfo::getHashValue(reinterpret_cast(E.Target)); + return H; + } + static bool isEqual(const Edge LHS, const Edge RHS) { + return LHS.Kind == RHS.Kind && + LHS.Source == RHS.Source && + LHS.Target == RHS.Target; + } +}; +} // end namespace llvm + +#endif // SWIFT_SYMBOLGRAPHGEN_EDGE_H diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h new file mode 100644 index 0000000000000..74ac1c196c5b4 --- /dev/null +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -0,0 +1,20 @@ +//===--- FormatVersion.h - Symbol Graph Format Version 00------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H +#define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H + +#define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0 +#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 1 +#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0 + +#endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H diff --git a/lib/SymbolGraphGen/JSON.cpp b/lib/SymbolGraphGen/JSON.cpp new file mode 100644 index 0000000000000..6bb117daaeeb4 --- /dev/null +++ b/lib/SymbolGraphGen/JSON.cpp @@ -0,0 +1,57 @@ +//===--- JSON.cpp - Symbol Graph JSON Helpers -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#include "JSON.h" + +void swift::symbolgraphgen::serialize(const llvm::VersionTuple &VT, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("major", VT.getMajor()); + if (VT.getMinor()) { + OS.attribute("minor", *VT.getMinor()); + } + if (VT.getSubminor()) { + OS.attribute("patch", *VT.getSubminor()); + } + // Despite the name, + // this is not Semantic Versioning "build metadata" + if (VT.getBuild()) { + OS.attribute("prerelease", *VT.getBuild()); + } + }); +} + +void swift::symbolgraphgen::serialize(const llvm::Triple &T, + llvm::json::OStream &OS) { + OS.object([&](){ + OS.attribute("architecture", T.getArchName()); + if (!T.getEnvironmentName().empty()) { + OS.attribute("environment", T.getEnvironmentName()); + } + OS.attribute("vendor", T.getVendorName()); + OS.attributeObject("operatingSystem", [&](){ + OS.attribute("name", T.getOSTypeName(T.getOS())); + + unsigned Major; + unsigned Minor; + unsigned Patch; + T.getOSVersion(Major, Minor, Patch); + llvm::VersionTuple OSVersion(Major, Minor, Patch); + + OS.attributeBegin("minimumVersion"); + serialize(OSVersion, OS); + OS.attributeEnd(); + }); + }); +} diff --git a/lib/SymbolGraphGen/JSON.h b/lib/SymbolGraphGen/JSON.h new file mode 100644 index 0000000000000..0c0018da724cb --- /dev/null +++ b/lib/SymbolGraphGen/JSON.h @@ -0,0 +1,45 @@ +//===--- JSON.h - Symbol Graph JSON Helpers -------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// Adds Symbol Graph JSON serialization to other types. +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_JSON_H +#define SWIFT_SYMBOLGRAPHGEN_JSON_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/AST/GenericSignature.h" + +namespace swift { +namespace symbolgraphgen { + +struct AttributeRAII { + StringRef Key; + llvm::json::OStream &OS; + AttributeRAII(StringRef Key, llvm::json::OStream &OS) + : Key(Key), OS(OS) { + OS.attributeBegin(Key); + } + + ~AttributeRAII() { + OS.attributeEnd(); + } +}; + +void serialize(const llvm::VersionTuple &VT, llvm::json::OStream &OS); +void serialize(const llvm::Triple &T, llvm::json::OStream &OS); + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_JSON_H diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp new file mode 100644 index 0000000000000..425b98217b921 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -0,0 +1,400 @@ +//===--- Symbol.cpp - Symbol Graph Node -----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/Basic/SourceManager.h" +#include "JSON.h" +#include "Symbol.h" +#include "SymbolGraph.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("identifier", Identifier); + OS.attribute("displayName", DisplayName); + }); +} + +void Symbol::serializeKind(llvm::json::OStream &OS) const { + AttributeRAII A("kind", OS); + switch (VD->getKind()) { + case swift::DeclKind::Class: + serializeKind("swift.class", "Class", OS); + break; + case swift::DeclKind::Struct: + serializeKind("swift.struct", "Structure", OS); + break; + case swift::DeclKind::Enum: + serializeKind("swift.enum", "Enumeration", OS); + break; + case swift::DeclKind::EnumElement: + serializeKind("swift.enum.case", "Case", OS); + break; + case swift::DeclKind::Protocol: + serializeKind("swift.protocol", "Protocol", OS); + break; + case swift::DeclKind::Constructor: + serializeKind("swift.initializer", "Initializer", OS); + break; + case swift::DeclKind::Func: + serializeKind("swift.function", "Function", OS); + break; + case swift::DeclKind::Var: + serializeKind("swift.variable", "Variable", OS); + break; + case swift::DeclKind::TypeAlias: + serializeKind("swift.typealias", "Type Alias", OS); + break; + case swift::DeclKind::InfixOperator: + serializeKind("swift.infixOperator", "Infix Operator", OS); + break; + case swift::DeclKind::PrefixOperator: + serializeKind("swift.prefixOperator", "Prefix Operator", OS); + break; + case swift::DeclKind::PostfixOperator: + serializeKind("swift.postfixOperator", "Postfix Operator", OS); + break; + default: + llvm_unreachable("Unsupported declaration kind for symbol graph"); + } +} + +void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("identifier", [&](){ + OS.attribute("precise", Walker.getUSR(VD)); + OS.attribute("interfaceLanguage", "swift"); + }); +} + +void Symbol::serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeArray("pathComponents", [&](){ + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + for (auto Component : PathComponents) { + OS.value(Component); + } + }); +} + +void Symbol::serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("names", [&](){ + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + + OS.attribute("title", PathComponents.back()); + // "navigator": null + Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); + // "prose": null + }); +} + +void Symbol::serializePosition(StringRef Key, + unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const { + OS.attributeObject(Key, [&](){ + OS.attribute("line", Line); + OS.attribute("character", ByteOffset); + }); +} + +void Symbol::serializeRange(size_t InitialIndentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const { + OS.attributeObject("range", [&](){ + auto StartLineAndColumn = SourceMgr.getLineAndColumn(Range.Start); + auto StartLine = StartLineAndColumn.first; + auto StartColumn = StartLineAndColumn.second + InitialIndentation; + serializePosition("start", StartLine, StartColumn, OS); + + auto EndLineAndColumn = SourceMgr.getLineAndColumn(Range.End); + auto EndLine = EndLineAndColumn.first; + auto EndColumn = EndLineAndColumn.second + InitialIndentation; + serializePosition("end", EndLine, EndColumn, OS); + }); +} + +void Symbol::serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeObject("docComment", [&](){ + auto LL = Walker.Ctx.getLineList(VD->getRawComment()); + size_t InitialIndentation = LL.getLines().empty() + ? 0 + : markup::measureIndentation(LL.getLines().front().Text); + OS.attributeArray("lines", [&](){ + for (const auto &Line : LL.getLines()) { + // Line object + OS.object([&](){ + // Trim off any initial indentation from the line's + // text and start of its source range, if it has one. + if (Line.Range.isValid()) { + serializeRange(InitialIndentation, + Line.Range, Walker.M.getASTContext().SourceMgr, OS); + } + auto TrimmedLine = Line.Text.drop_front(std::min(InitialIndentation, + Line.FirstNonspaceOffset)); + OS.attribute("text", TrimmedLine); + }); + } + }); // end lines: [] + }); // end docComment: +} + +void Symbol::serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *FD = dyn_cast_or_null(VD)) { + OS.attributeObject("functionSignature", [&](){ + + // Parameters + if (const auto *ParamList = FD->getParameters()) { + if (ParamList->size()) { + OS.attributeArray("parameters", [&](){ + for (const auto *Param : *ParamList) { + auto ExternalName = Param->getArgumentName().str(); + auto InternalName = Param->getParameterName().str(); + + OS.object([&](){ + if (ExternalName.empty()) { + OS.attribute("name", InternalName); + } else { + OS.attribute("name", ExternalName); + if (ExternalName != InternalName && + !InternalName.empty()) { + OS.attribute("internalName", InternalName); + } + } + Walker.serializeDeclarationFragments("declarationFragments", + Param, OS); + }); // end parameter object + } + }); // end parameters: + } + } + + // Returns + if (const auto ReturnType = FD->getResultInterfaceType()) { + Walker.serializeDeclarationFragments("returns", ReturnType, OS); + } + }); + } +} + +void Symbol::serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attribute("name", Param.getName().str()); + OS.attribute("index", Param.getIndex()); + OS.attribute("depth", Param.getDepth()); + }); +} + +void Symbol::serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const { + OS.object([&](){ + switch (Req.getKind()) { + case swift::RequirementKind::Conformance: + OS.attribute("kind", "conformance"); + break; + case swift::RequirementKind::Superclass: + OS.attribute("kind", "superclass"); + break; + case swift::RequirementKind::SameType: + OS.attribute("kind", "sameType"); + break; + case swift::RequirementKind::Layout: + return; + } + OS.attribute("lhs", Req.getFirstType()->getString()); + OS.attribute("rhs", Req.getSecondType()->getString()); + }); +} + +void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { + if (const auto *GC = VD->getAsGenericContext()) { + if (const auto Generics = GC->getGenericSignature()) { + + OS.attributeObject("swiftGenerics", [&](){ + if (!Generics->getGenericParams().empty()) { + OS.attributeArray("parameters", [&](){ + for (const auto Param : Generics->getGenericParams()) { + if (const auto *D = Param->getDecl()) { + if (D->isImplicit()) { + continue; + } + } + serializeGenericParam(*Param, OS); + } + }); // end parameters: + } + + if (!Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + + }); // end swiftGenerics: + } + } +} + +void Symbol::serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + if (const auto *Extension + = dyn_cast_or_null(VD->getInnermostDeclContext())) { + OS.attributeObject("swiftExtension", [&](){ + OS.attribute("definedInModule", Walker.M.getNameStr()); + auto Generics = Extension->getGenericSignature(); + if (Generics && !Generics->getRequirements().empty()) { + OS.attributeArray("constraints", [&](){ + for (const auto &Requirement : Generics->getRequirements()) { + serializeGenericRequirement(Requirement, OS); + } + }); // end constraints: + } + }); // end swiftExtension: + } +} + +void Symbol::serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + Walker.serializeDeclarationFragments("declarationFragments", VD, OS); +} + +void Symbol::serializeAccessLevelMixin(llvm::json::OStream &OS) const { + OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess())); +} + +llvm::Optional +Symbol::getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const { + switch (AgnosticKind) { + // SPM- and Swift-specific availability. + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + return { "SwiftPM" }; + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + return { "Swift" }; + // Although these are in the agnostic kinds, they are actually a signal + // that there is either platform-specific or completely platform-agnostic. + // They'll be handled below. + case PlatformAgnosticAvailabilityKind::Deprecated: + case PlatformAgnosticAvailabilityKind::Unavailable: + case PlatformAgnosticAvailabilityKind::None: + break; + } + + // Platform-specific availability. + switch (Kind) { + case swift::PlatformKind::iOS: + return { "iOS" }; + case swift::PlatformKind::macCatalyst: + return { "macCatalyst" }; + case swift::PlatformKind::OSX: + return { "macOS" }; + case swift::PlatformKind::tvOS: + return { "tvOS" }; + case swift::PlatformKind::watchOS: + return { "watchOS" }; + case swift::PlatformKind::iOSApplicationExtension: + return { "iOSAppExtension" }; + case swift::PlatformKind::macCatalystApplicationExtension: + return { "macCatalystAppExtension" }; + case swift::PlatformKind::OSXApplicationExtension: + return { "macOSAppExtension" }; + case swift::PlatformKind::tvOSApplicationExtension: + return { "tvOSAppExtension" }; + case swift::PlatformKind::watchOSApplicationExtension: + return { "watchOSAppExtension" }; + // Platform-agnostic availability, such as "unconditionally deprecated" + // or "unconditionally obsoleted". + case swift::PlatformKind::none: + return None; + } +} + +void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { + SmallVector Availabilities; + for (const auto *Attr : VD->getAttrs()) { + if (const auto *AvAttr = dyn_cast(Attr)) { + Availabilities.push_back(AvAttr); + } + } + if (Availabilities.empty()) { + return; + } + + OS.attributeArray("availability", [&](){ + for (const auto *AvAttr : Availabilities) { + OS.object([&](){ + auto Domain = getDomain(AvAttr->getPlatformAgnosticAvailability(), + AvAttr->Platform); + if (Domain) { + OS.attribute("domain", *Domain); + } + if (AvAttr->Introduced) { + AttributeRAII Introduced("introduced", OS); + symbolgraphgen::serialize(*AvAttr->Introduced, OS); + } + if (AvAttr->Deprecated) { + AttributeRAII Deprecated("deprecated", OS); + symbolgraphgen::serialize(*AvAttr->Deprecated, OS); + } + if (AvAttr->Obsoleted) { + AttributeRAII Obsoleted("obsoleted", OS); + symbolgraphgen::serialize(*AvAttr->Obsoleted, OS); + } + if (!AvAttr->Message.empty()) { + OS.attribute("message", AvAttr->Message); + } + if (!AvAttr->Rename.empty()) { + OS.attribute("renamed", AvAttr->Rename); + } + if (AvAttr->isUnconditionallyDeprecated()) { + OS.attribute("isUnconditionallyDeprecated", true); + } + if (AvAttr->isUnconditionallyUnavailable()) { + OS.attribute("isUnconditionallyUnavailable", true); + } + }); // end availability object + } + }); // end availability: [] +} + +void Symbol::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + serializeKind(OS); + serializeIdentifier(Walker, OS); + serializePathComponents(Walker, OS); + serializeNames(Walker, OS); + serializeDocComment(Walker, OS); + + // "Mixins" + serializeFunctionSignature(Walker, OS); + serializeSwiftGenericMixin(OS); + serializeSwiftExtensionMixin(Walker, OS); + serializeDeclarationFragmentMixin(Walker, OS); + serializeAccessLevelMixin(OS); + serializeAvailabilityMixin(OS); + }); +} diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h new file mode 100644 index 0000000000000..6e22255596b50 --- /dev/null +++ b/lib/SymbolGraphGen/Symbol.h @@ -0,0 +1,91 @@ +//===--- Symbol.h- Symbol Graph Node --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOL_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOL_H + +#include "llvm/Support/JSON.h" +#include "swift/AST/Attr.h" +#include "swift/Basic/LLVM.h" +#include "swift/Markup/Markup.h" + +namespace swift { +namespace symbolgraphgen { + +struct AvailabilityDomain; +struct SymbolGraphASTWalker; + +/// A symbol from a module: a node in a graph. +struct Symbol { + const ValueDecl *VD; + + void serializeKind(StringRef Identifier, StringRef DisplayName, + llvm::json::OStream &OS) const; + + void serializeKind(llvm::json::OStream &OS) const; + + void serializeIdentifier(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeNames(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializePosition(StringRef Key, unsigned Line, unsigned ByteOffset, + llvm::json::OStream &OS) const; + + void serializeRange(size_t InitialIdentation, + SourceRange Range, SourceManager &SourceMgr, + llvm::json::OStream &OS) const; + + void serializeDocComment(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeFunctionSignature(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeGenericParam(const swift::GenericTypeParamType &Param, + llvm::json::OStream &OS) const; + + void serializeGenericRequirement(const swift::Requirement &Req, + llvm::json::OStream &OS) const; + + void serializeSwiftGenericMixin(llvm::json::OStream &OS) const; + + void serializeSwiftExtensionMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeDeclarationFragmentMixin(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + void serializeAccessLevelMixin(llvm::json::OStream &OS) const; + + llvm::Optional + getDomain(PlatformAgnosticAvailabilityKind AgnosticKind, + PlatformKind Kind) const; + + void serializeAvailabilityMixin(llvm::json::OStream &OS) const; + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + + bool operator==(const Symbol &Other) const { + return VD == Other.VD; + } +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOL_H diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp new file mode 100644 index 0000000000000..0d41bdcf4a638 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -0,0 +1,68 @@ +//===--- SymbolGraph.cpp - Symbol Graph Data Structure -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Module.h" +#include "swift/Basic/Version.h" + +#include "FormatVersion.h" +#include "SymbolGraph.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraph::SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion) +: M(M), Target(Target), ModuleVersion(ModuleVersion) {} + +void SymbolGraph::serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.object([&](){ + OS.attributeObject("metadata", [&](){ + { + AttributeRAII FV("formatVersion", OS); + llvm::VersionTuple FormatVersion(SWIFT_SYMBOLGRAPH_FORMAT_MAJOR, + SWIFT_SYMBOLGRAPH_FORMAT_MINOR, + SWIFT_SYMBOLGRAPH_FORMAT_PATCH); + symbolgraphgen::serialize(FormatVersion, OS); + } // end formatVersion: + + auto VersionString = version::getSwiftFullVersion(); + StringRef VersionStringRef(VersionString.c_str(), VersionString.size()); + OS.attribute("generator", VersionStringRef); + }); // end metadata: + + OS.attributeObject("module", [&](){ + OS.attribute("name", M.getNameStr()); + AttributeRAII Platform("platform", OS); + symbolgraphgen::serialize(Target, OS); + }); + + if (ModuleVersion) { + AttributeRAII MV("moduleVersion", OS); + symbolgraphgen::serialize(*ModuleVersion, OS); + } + + OS.attributeArray("symbols", [&](){ + for (const auto *VD: Nodes) { + Symbol S { VD }; + S.serialize(Walker, OS); + } + }); + + OS.attributeArray("relationships", [&](){ + for (const auto Relationship : Edges) { + Relationship.serialize(OS); + } + }); + + }); +} diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h new file mode 100644 index 0000000000000..1e893780e35f3 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -0,0 +1,64 @@ +//===--- SymbolGraph.h - Symbol Graph Data Structure ----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "swift/Basic/LLVM.h" +#include "Edge.h" +#include "JSON.h" + +namespace swift { +namespace symbolgraphgen { + +/// A graph of symbols and the relationships between them. +struct SymbolGraph { + /** + The module this symbol graph represents. + */ + ModuleDecl &M; + + /** + The module's target triple. + */ + llvm::Triple Target; + + /** + The semantic version of the module that this symbol graph describes, + if known. + */ + Optional ModuleVersion; + + /** + The symbols in a module: the nodes in the graph. + */ + llvm::SmallPtrSet Nodes; + + /** + The relationships between symbols: the edges in the graph. + */ + llvm::DenseSet Edges; + + SymbolGraph(ModuleDecl &M, llvm::Triple Target, + Optional ModuleVersion = None); + + void serialize(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPH_H diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp new file mode 100644 index 0000000000000..cc370438be29b --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -0,0 +1,347 @@ +//===--- SymbolGraphASTWalker.cpp - Symbol Graph AST Walker ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "llvm/ADT/StringSwitch.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/AST/Decl.h" +#include "swift/AST/GenericSignature.h" +#include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/USRGeneration.h" +#include "swift/Basic/PrimitiveParsing.h" +#include "swift/Markup/Markup.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "DeclarationFragmentPrinter.h" +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M, + const SymbolGraphOptions &Options) + : Options(Options), + M(M), + Graph(M, Options.Target) {} + +/// Returns `true` if the symbol should be included as a node in the graph. +bool SymbolGraphASTWalker::shouldIncludeNode(const Decl *D) const { + // If this decl isn't in this module, don't record it, + // as it will appear elsewhere in its module's symbol graph. + if (D->getModuleContext()->getName() != M.getName()) { + return false; + } + + // Implicit declarations are probably not going to have documentation, + // so don't record it in the symbol graph. + if (D->isImplicit()) { + return false; + } + + // At this point, the declaration must be a ValueDecl. + auto VD = cast(D); + + // Don't record unconditionally private declarations + if (VD->isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) { + return false; + } + + // Don't record effectively internal declarations if specified + if (Options.MinimumAccessLevel > AccessLevel::Internal && + VD->hasUnderscoredNaming()) { + return false; + } + + // Symbols must meet the minimum access level to be included in the graph. + if (VD->getFormalAccess() < Options.MinimumAccessLevel) { + return false; + } + + // Special cases + + auto BaseName = VD->getBaseName().userFacingName(); + + // ${MODULE}Version{Number,String} in ${Module}.h + SmallString<32> VersionNameIdentPrefix { M.getName().str() }; + VersionNameIdentPrefix.append("Version"); + + if (BaseName.startswith(VersionNameIdentPrefix.str())) { + return false; + } + + // Automatically mapped SIMD types + bool ShouldInclude = llvm::StringSwitch(BaseName) +#define MAP_SIMD_TYPE(C_TYPE, _, __) \ + .Case("swift_" #C_TYPE "2", false) \ + .Case("swift_" #C_TYPE "3", false) \ + .Case("swift_" #C_TYPE "4", false) +#include "swift/ClangImporter/SIMDMappedTypes.def" + .Case("SWIFT_TYPEDEFS", false) + .Case("char16_t", false) + .Case("char32_t", false) + .Default(true); + + return ShouldInclude; +} + +bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) { + + switch (D->getKind()) { + // We'll record nodes for the following kinds of declarations. + case swift::DeclKind::Class: + case swift::DeclKind::Struct: + case swift::DeclKind::Enum: + case swift::DeclKind::EnumElement: + case swift::DeclKind::Protocol: + case swift::DeclKind::Constructor: + case swift::DeclKind::Func: + case swift::DeclKind::Var: + case swift::DeclKind::TypeAlias: + break; + + // We'll descend into everything else. + default: + return true; + } + + if (!shouldIncludeNode(D)) { + return false; + } + + auto *VD = cast(D); + Graph.Nodes.insert(VD); + + // Record all of the possible relationships (edges) originating + // with this declaration. + recordMemberRelationship(VD); + recordConformanceRelationships(VD); + recordInheritanceRelationships(VD); + recordDefaultImplementationRelationships(VD); + recordOverrideRelationship(VD); + recordRequirementRelationships(VD); + recordOptionalRequirementRelationships(VD); + + return true; +} + +StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { + auto Found = USRCache.find(VD); + if (Found != USRCache.end()) { + return Found->second; + } + llvm::SmallString<32> Scratch; + llvm::raw_svector_ostream OS(Scratch); + ide::printDeclUSR(VD, OS); + auto USR = Ctx.allocateCopy(Scratch.str()); + USRCache.insert({VD, USR}); + return USR; +} + +void +SymbolGraphASTWalker::getPathComponents(const ValueDecl *VD, + SmallVectorImpl> &Components) { + // Collect the spellings of the fully qualified identifier components. + auto Decl = VD; + while (Decl && !isa(Decl)) { + SmallString<32> Scratch; + Decl->getFullName().getString(Scratch); + Components.push_back(Scratch); + if (const auto *DC = Decl->getDeclContext()) { + if (const auto *Proto = DC->getExtendedProtocolDecl()) { + Decl = Proto; + } else if (const auto *Ext = dyn_cast_or_null(DC->getAsDecl())) { + Decl = Ext->getExtendedNominal(); + } else { + Decl = dyn_cast_or_null(DC->getAsDecl()); + } + } else { + Decl = nullptr; + } + } + + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(Components.begin(), Components.end()); +} + +PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { + PrintOptions Opts; + Opts.FunctionDefinitions = false; + Opts.ArgAndParamPrinting = + PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; + Opts.PrintGetSetOnRWProperties = false; + Opts.PrintPropertyAccessors = false; + Opts.PrintSubscriptAccessors = false; + Opts.SkipUnderscoredKeywords = true; + Opts.SkipAttributes = true; + Opts.PrintOverrideKeyword = true; + Opts.PrintImplicitAttrs = false; + Opts.PrintFunctionRepresentationAttrs = + PrintOptions::FunctionRepresentationMode::None; + Opts.PrintUserInaccessibleAttrs = false; + Opts.SkipPrivateStdlibDecls = true; + Opts.SkipUnderscoredStdlibProtocols = true; + + Opts.ExclusiveAttrList.clear(); + +#define DECL_ATTR(SPELLING, CLASS, OPTIONS, CODE) Opts.ExcludeAttrList.push_back(DAK_##CLASS); +#define TYPE_ATTR(X) Opts.ExcludeAttrList.push_back(TAK_##X); +#include "swift/AST/Attr.def" + + return Opts; +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + VD->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void +SymbolGraphASTWalker::serializeSubheadingDeclarationFragments(StringRef Key, + const ValueDecl *VD, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + auto Options = getDeclarationFragmentsPrintOptions(); + Options.VarInitializers = false; + Options.PrintDefaultArgumentValue = false; + Options.PrintEmptyArgumentNames = false; + Options.PrintOverrideKeyword = false; + VD->print(Printer, Options); +} + +void +SymbolGraphASTWalker::serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS) { + DeclarationFragmentPrinter Printer(*this, OS, Key); + T->print(Printer, getDeclarationFragmentsPrintOptions()); +} + +void SymbolGraphASTWalker::recordEdge(const ValueDecl *Source, + const ValueDecl *Target, + RelationshipKind Kind) { + if (Target->isPrivateStdlibDecl( + /*treatNonBuiltinProtocolsAsPublic = */false)) { + return; + } + + // There might be relationships on implicit declarations, + // such as overriding implicit @objc init(). + if (Target->isImplicit()) { + return; + } + + Graph.Nodes.insert(Source); + Graph.Nodes.insert(Target); + + Graph.Edges.insert({this, Kind, Source, Target}); +} + +void SymbolGraphASTWalker::recordMemberRelationship(const ValueDecl *VD) { + auto *DC = VD->getDeclContext(); + switch (DC->getContextKind()) { + case DeclContextKind::GenericTypeDecl: + case DeclContextKind::ExtensionDecl: + case swift::DeclContextKind::EnumElementDecl: + return recordEdge(VD, VD->getDeclContext()->getSelfNominalTypeDecl(), + RelationshipKind::MemberOf()); + case swift::DeclContextKind::AbstractClosureExpr: + case swift::DeclContextKind::Initializer: + case swift::DeclContextKind::TopLevelCodeDecl: + case swift::DeclContextKind::SubscriptDecl: + case swift::DeclContextKind::AbstractFunctionDecl: + case swift::DeclContextKind::SerializedLocal: + case swift::DeclContextKind::Module: + case swift::DeclContextKind::FileUnit: + break; + } +} + +void +SymbolGraphASTWalker::recordInheritanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto &InheritanceLoc : NTD->getInherited()) { + auto Ty = InheritanceLoc.getType(); + if (!Ty) { + continue; + } + auto *InheritedTypeDecl = + dyn_cast_or_null(Ty->getAnyNominal()); + if (!InheritedTypeDecl) { + continue; + } + + recordEdge(VD, InheritedTypeDecl, RelationshipKind::InheritsFrom()); + } + } +} + +void SymbolGraphASTWalker::recordDefaultImplementationRelationships( + const ValueDecl *VD) { + if (const auto *Extension = dyn_cast(VD->getDeclContext())) { + if (const auto *Protocol = Extension->getExtendedProtocolDecl()) { + for (const auto *Member : Protocol->getMembers()) { + if (const auto *MemberVD = dyn_cast(Member)) { + if (MemberVD->getFullName().compare(VD->getFullName()) == 0) { + recordEdge(VD, MemberVD, + RelationshipKind::DefaultImplementationOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordRequirementRelationships(const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + recordEdge(VD, Protocol, RelationshipKind::RequirementOf()); + } + } +} + +void SymbolGraphASTWalker::recordOptionalRequirementRelationships( + const ValueDecl *VD) { + if (const auto *Protocol = dyn_cast(VD->getDeclContext())) { + if (VD->isProtocolRequirement()) { + if (const auto *ClangDecl = VD->getClangDecl()) { + if (const auto *Method = dyn_cast(ClangDecl)) { + if (Method->isOptional()) { + recordEdge(VD, Protocol, + RelationshipKind::OptionalRequirementOf()); + } + } + } + } + } +} + +void +SymbolGraphASTWalker::recordConformanceRelationships(const ValueDecl *VD) { + if (const auto *NTD = dyn_cast(VD)) { + for (const auto *Conformance : NTD->getAllConformances()) { + recordEdge(VD, Conformance->getProtocol(), + RelationshipKind::ConformsTo()); + } + } +} + +void SymbolGraphASTWalker::recordOverrideRelationship(const ValueDecl *VD) { + if (const auto *Override = VD->getOverriddenDecl()) { + recordEdge(VD, Override, RelationshipKind::Overrides()); + } +} diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h new file mode 100644 index 0000000000000..adf15ec5d46ca --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -0,0 +1,157 @@ +//===--- SymbolGraphASTWalker.h - Symbol Graph AST Walker -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H +#define SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H + +#include "swift/Basic/LLVM.h" +#include "swift/IDE/SourceEntityWalker.h" +#include "swift/Markup/Markup.h" + +#include "SymbolGraph.h" + +namespace swift { + +class Decl; +class Type; +class ValueDecl; + +namespace symbolgraphgen { + +struct SymbolGraph; +struct SymbolGraphOptions; + +/** + The `SymbolGraphASTWalker` is the core implementation that builds a + symbol graph. It walks a module for declarations, recording facts about + symbols and relationships between them. + */ +struct SymbolGraphASTWalker : public SourceEntityWalker { + /// Options for collecting and serialization. + const SymbolGraphOptions &Options; + + /// The module that this symbol graph will represent. + const ModuleDecl &M; + + /// The symbol graph. + SymbolGraph Graph; + + /// A context for allocations. + markup::MarkupContext Ctx; + + /// A cache of USRs for declarations. + llvm::DenseMap USRCache; + + // MARK: - + + SymbolGraphASTWalker(ModuleDecl &M, const SymbolGraphOptions &Options); + virtual ~SymbolGraphASTWalker() {} + + // MARK: - + + /// Returns `true` if the symbol should be included as a node in the graph. + bool shouldIncludeNode(const Decl *D) const; + + virtual bool walkToDeclPre(Decl *D, CharSourceRange Range); + + // MARK: - Utilities and Conversions + + /// Get the USR of a declaration and add it to the local allocator. + StringRef getUSR(const ValueDecl *VD); + + /// Returns an array of path components for a declaration. + void getPathComponents(const ValueDecl *VD, SmallVectorImpl> &Components); + + // MARK: - Declaration Fragments + + /// Get the base print options for declaration fragments. + PrintOptions getDeclarationFragmentsPrintOptions() const; + + /// Serialize the overall declaration fragments for a `ValueDecl`. + void + serializeDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration fragments for a `ValueDecl` when it is viewed + /// as a subheading and/or part of a larger group of symbol listings. + void + serializeSubheadingDeclarationFragments(StringRef Key, const ValueDecl *VD, + llvm::json::OStream &OS); + + /// Get the overall declaration for a type declaration. + void + serializeDeclarationFragments(StringRef Key, Type T, + llvm::json::OStream &OS); + + // MARK: - Relationships (Edges) + + /** + Record a relationship between two declarations as an edge in the graph. + + \param Source The declaration serving as the source of the edge in the + directed graph. + \param Target The declaration serving as the target of the edge in the + directed graph. + \param Kind The kind of relationship the edge represents. + */ + void recordEdge(const ValueDecl *Source, const ValueDecl *Target, + RelationshipKind Kind); + + /** + Record a MemberOf relationship, if the given declaration is nested + in another. + */ + void recordMemberRelationship(const ValueDecl *VD); + + /** + Record InheritsFrom relationships for every class from which the + declaration inherits. + */ + void recordInheritanceRelationships(const ValueDecl *VD); + + /** + If the declaration is a default implementation in a protocol extension, + record a DefaultImplementationOf relationship between the declaration and + the requirement. + */ + void recordDefaultImplementationRelationships(const ValueDecl *VD); + + /** + Record a RequirementOf relationship if the declaration is a requirement + of a protocol. + */ + void recordRequirementRelationships(const ValueDecl *VD); + + /** + If the declaration is an Objective-C-based optional protocol requirement, + record an OptionalRequirementOf relationship between the declaration + and its containing protocol. + */ + void recordOptionalRequirementRelationships(const ValueDecl *VD); + + /** + Record ConformsTo relationships for each protocol conformance of + the declaration. + */ + void recordConformanceRelationships(const ValueDecl *VD); + + /** + Records an Overrides relationship if the given declaration + overrides another. + */ + void recordOverrideRelationship(const ValueDecl *VD); +}; + +} // end namespace symbolgraphgen +} // end namespace swift + +#endif // SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHASTWALKER_H diff --git a/lib/SymbolGraphGen/SymbolGraphGen.cpp b/lib/SymbolGraphGen/SymbolGraphGen.cpp new file mode 100644 index 0000000000000..667d7b3e328c4 --- /dev/null +++ b/lib/SymbolGraphGen/SymbolGraphGen.cpp @@ -0,0 +1,56 @@ +//===--- SymbolGraphGen.cpp - Symbol Graph Generator Entry Point ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" +#include "swift/AST/Module.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" + +#include "SymbolGraphASTWalker.h" + +using namespace swift; +using namespace symbolgraphgen; + +// MARK: - Main Entry Point + +/// Emit a symbol graph JSON file for a `ModuleDecl`. +int +symbolgraphgen::emitSymbolGraphForModule(ModuleDecl *M, + const SymbolGraphOptions &Options) { + SymbolGraphASTWalker Walker(*M, Options); + SmallVector ModuleDecls; + M->getDisplayDecls(ModuleDecls); + + llvm::errs() << ModuleDecls.size() + << " top-level declarations in this module.\n"; + + for (auto *Decl : ModuleDecls) { + Walker.walk(Decl); + } + + llvm::errs() + << "Found " << Walker.Graph.Nodes.size() << " symbols and " + << Walker.Graph.Edges.size() << " relationships.\n"; + + std::error_code Error; + llvm::raw_fd_ostream OS(Options.OutputPath, Error, llvm::sys::fs::FA_Write); + if (Error) { + llvm::errs() << "Couldn't open output file for writing: " + << Error.message() << "\n"; + return EXIT_FAILURE; + } + + llvm::json::OStream J(OS, Options.PrettyPrint ? 2 : 0); + Walker.Graph.serialize(Walker, J); + + return EXIT_SUCCESS; +} diff --git a/lib/Syntax/README.md b/lib/Syntax/README.md index 795b3c12cda7d..f6399b9767c4a 100644 --- a/lib/Syntax/README.md +++ b/lib/Syntax/README.md @@ -260,7 +260,7 @@ pieces of syntax that aren't really relevant to the semantics of the program, such as whitespace and comments. These are modeled as collections and, with the exception of comments, are sort of "run-length" encoded. For example, a sequence of four spaces is represented by `{ Kind: TriviaKind::Space, Count: 4 }`, not -the literal text `" "`. +the literal text `"    "`. Some examples of the "atoms" of `Trivia`: @@ -505,7 +505,7 @@ following fields: | Key | Type | Description | | --- | ---- | ----------- | -| `kind` | `String` | The `SyntaxKind` of this child. This must have a corresponding `Node` with that kind. | +| `kind` | `String` | The `SyntaxKind` of this child. This must have a corresponding `Node` with that kind (or corresponding `Token` in both `include/swift/Syntax/TokenKinds.def` and `SYNTAX_TOKENS`). | | `is_optional` | `Bool?` | Whether this child is required in a fully-formed object, or if it is allowed to remain `missing`. Defaults to `false` if not present. | `token_choices` | `[String]?` | A list of `Token`s which are considered "valid" values for `Token` children. | | `text_choices` | `[String]?` | A list of valid textual values for tokens. If this is not provided, any textual value is accepted for tokens like `IdentifierToken`. | @@ -513,7 +513,7 @@ following fields: #### Tokens A `Token` represents one of the `tok::` enums in -`include/Syntax/TokenKinds.def`. `Token.py` has a top-level array of token +`include/swift/Syntax/TokenKinds.def`. `Token.py` has a top-level array of token declarations. The `Token` class has the following fields. | Key | Type | Description | diff --git a/lib/Syntax/RawSyntax.cpp b/lib/Syntax/RawSyntax.cpp index 25b2474805ceb..71cc2f36353b1 100644 --- a/lib/Syntax/RawSyntax.cpp +++ b/lib/Syntax/RawSyntax.cpp @@ -325,6 +325,10 @@ void AbsolutePosition::dump(llvm::raw_ostream &OS) const { OS << ')'; } +void AbsolutePosition::dump() const { + dump(llvm::errs()); +} + void RawSyntax::Profile(llvm::FoldingSetNodeID &ID, tok TokKind, OwnedString Text, ArrayRef LeadingTrivia, ArrayRef TrailingTrivia) { diff --git a/lib/Syntax/Syntax.cpp b/lib/Syntax/Syntax.cpp index 6f15143c3d962..4d8c79842b745 100644 --- a/lib/Syntax/Syntax.cpp +++ b/lib/Syntax/Syntax.cpp @@ -13,7 +13,6 @@ #include "swift/Syntax/Syntax.h" #include "swift/Syntax/SyntaxData.h" #include "swift/Syntax/SyntaxVisitor.h" -#include "swift/Syntax/TokenSyntax.h" using namespace swift; using namespace swift::syntax; @@ -97,21 +96,3 @@ llvm::Optional Syntax::getChild(const size_t N) const { return llvm::None; return Syntax {Root, ChildData.get()}; } - -Optional Syntax::getPreviousNode() const { - if (auto prev = getData().getPreviousNode()) - return Syntax(Root, prev.get()); - return None; -} - -Optional Syntax::getFirstToken() const { - if (auto tok = getData().getFirstToken()) - return TokenSyntax(Root, tok.get()); - return None; -} - -Optional Syntax::getLastToken() const { - if (auto tok = getData().getLastToken()) - return TokenSyntax(Root, tok.get()); - return None; -} diff --git a/lib/Syntax/SyntaxBuilders.cpp.gyb b/lib/Syntax/SyntaxBuilders.cpp.gyb index 9309327befe07..8c93726dc5123 100644 --- a/lib/Syntax/SyntaxBuilders.cpp.gyb +++ b/lib/Syntax/SyntaxBuilders.cpp.gyb @@ -25,7 +25,7 @@ using namespace swift; using namespace swift::syntax; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_buildable(): % for child in node.children: ${node.name}Builder & diff --git a/lib/Syntax/SyntaxData.cpp b/lib/Syntax/SyntaxData.cpp index c31a451321c56..7861f41aee51e 100644 --- a/lib/Syntax/SyntaxData.cpp +++ b/lib/Syntax/SyntaxData.cpp @@ -85,8 +85,6 @@ RC SyntaxData::getNextNode() const { } RC SyntaxData::getFirstToken() const { - if (getRaw()->isMissing()) - return nullptr; if (getRaw()->isToken()) { // Get a reference counted version of this assert(hasParent() && "The syntax tree should not conisist only of the root"); @@ -107,28 +105,6 @@ RC SyntaxData::getFirstToken() const { return nullptr; } -RC SyntaxData::getLastToken() const { - if (getRaw()->isMissing()) - return nullptr; - if (getRaw()->isToken()) { - // Get a reference counted version of this - assert(hasParent() && "The syntax tree should not conisist only of the root"); - return getParent()->getChild(getIndexInParent()); - } - for (size_t I = getNumChildren(); I != 0; --I) { - if (auto Child = getChild(I - 1)) { - if (Child->getRaw()->isMissing()) - continue; - if (Child->getRaw()->isToken()) { - return Child; - } else if (auto Token = Child->getLastToken()) { - return Token; - } - } - } - return nullptr; -} - AbsolutePosition SyntaxData::getAbsolutePositionBeforeLeadingTrivia() const { if (PositionCache.hasValue()) return *PositionCache; diff --git a/lib/Syntax/SyntaxFactory.cpp.gyb b/lib/Syntax/SyntaxFactory.cpp.gyb index fc8394411358f..dc6ad1a8c90dd 100644 --- a/lib/Syntax/SyntaxFactory.cpp.gyb +++ b/lib/Syntax/SyntaxFactory.cpp.gyb @@ -63,7 +63,7 @@ SyntaxFactory::makeUnknownSyntax(llvm::ArrayRef Tokens, Syntax SyntaxFactory::makeBlankCollectionSyntax(SyntaxKind Kind) { switch(Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_syntax_collection(): case SyntaxKind::${node.syntax_kind}: return makeBlank${node.syntax_kind}(); % end @@ -76,7 +76,7 @@ Syntax SyntaxFactory::makeBlankCollectionSyntax(SyntaxKind Kind) { std::pair SyntaxFactory::countChildren(SyntaxKind Kind){ switch(Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if not node.is_syntax_collection(): case SyntaxKind::${node.syntax_kind}: % child_count = len(node.children) @@ -92,7 +92,7 @@ SyntaxFactory::countChildren(SyntaxKind Kind){ bool SyntaxFactory::canServeAsCollectionMemberRaw(SyntaxKind CollectionKind, SyntaxKind MemberKind) { switch (CollectionKind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_syntax_collection(): case SyntaxKind::${node.syntax_kind}: % if node.collection_element_choices: @@ -125,7 +125,7 @@ RC SyntaxFactory::createRaw(SyntaxKind Kind, llvm::ArrayRef> Elements, RC Arena) { switch (Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: case SyntaxKind::${node.syntax_kind}: { % if node.children: % child_count = len(node.children) @@ -178,7 +178,7 @@ Optional SyntaxFactory::createSyntax(SyntaxKind Kind, return None; } -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.children: % child_params = [] % for child in node.children: diff --git a/lib/Syntax/SyntaxKind.cpp.gyb b/lib/Syntax/SyntaxKind.cpp.gyb index 7e4d070c19a83..2b16108ea2067 100644 --- a/lib/Syntax/SyntaxKind.cpp.gyb +++ b/lib/Syntax/SyntaxKind.cpp.gyb @@ -39,20 +39,14 @@ bool isTokenTextDetermined(tok kind) { } StringRef getTokenText(tok kind) { - return getTokenTextInternal(kind); -} - -bool isTokenKeyword(tok kind) { - switch (kind) { -#define KEYWORD(X) case tok::kw_##X: return true; -#include "swift/Syntax/TokenKinds.def" - default: return false; - } + auto text = getTokenTextInternal(kind); + assert(!text.empty() && "token kind cannot be determined"); + return text; } bool parserShallOmitWhenNoChildren(syntax::SyntaxKind Kind) { switch(Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.shall_be_omitted_when_empty(): case syntax::SyntaxKind::${node.syntax_kind}: % end @@ -73,7 +67,7 @@ void dumpSyntaxKind(llvm::raw_ostream &os, const SyntaxKind kind) { case SyntaxKind::Unknown: os << "Unknown"; break; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: case SyntaxKind::${node.syntax_kind}: os << "${node.syntax_kind}"; break; @@ -83,7 +77,7 @@ void dumpSyntaxKind(llvm::raw_ostream &os, const SyntaxKind kind) { bool isCollectionKind(SyntaxKind Kind) { switch(Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.is_syntax_collection(): case SyntaxKind::${node.syntax_kind}: % end @@ -147,7 +141,7 @@ SyntaxKind getUnknownKind(SyntaxKind Kind) { llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS, swift::syntax::SyntaxKind Kind) { switch (Kind) { -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: case swift::syntax::SyntaxKind::${node.syntax_kind}: OS << "${node.syntax_kind}"; break; diff --git a/lib/Syntax/SyntaxNodes.cpp.gyb b/lib/Syntax/SyntaxNodes.cpp.gyb index 55c024a68fbd0..34418e182f1ca 100644 --- a/lib/Syntax/SyntaxNodes.cpp.gyb +++ b/lib/Syntax/SyntaxNodes.cpp.gyb @@ -24,7 +24,7 @@ using namespace swift; using namespace swift::syntax; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if node.requires_validation(): void ${node.name}::validate() const { #ifndef NDEBUG @@ -55,14 +55,14 @@ void ${node.name}::validate() const { % for child in node.children: % if child.is_optional: -llvm::Optional<${child.type_name}> ${node.name}::get${child.name}() const { +llvm::Optional<${child.type_name}> ${node.name}::get${child.name}() { auto ChildData = Data->getChild(Cursor::${child.name}); if (!ChildData) return llvm::None; return ${child.type_name} {Root, ChildData.get()}; } % else: -${child.type_name} ${node.name}::get${child.name}() const { +${child.type_name} ${node.name}::get${child.name}() { return ${child.type_name} {Root, Data->getChild(Cursor::${child.name}).get()}; } % end diff --git a/lib/Syntax/SyntaxVisitor.cpp.gyb b/lib/Syntax/SyntaxVisitor.cpp.gyb index f98a0d958e994..722ab5d46f4e7 100644 --- a/lib/Syntax/SyntaxVisitor.cpp.gyb +++ b/lib/Syntax/SyntaxVisitor.cpp.gyb @@ -21,7 +21,7 @@ #include "swift/Syntax/SyntaxVisitor.h" #include "swift/Basic/Defer.h" -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if is_visitable(node): void swift::syntax::SyntaxVisitor::visit(${node.name} node) { visitChildren(node); @@ -36,7 +36,7 @@ void swift::syntax::SyntaxVisitor::visit(Syntax node) { case SyntaxKind::Token: visit(node.castTo()); return; -% for node in SYNTAX_NODES + PARSEONLY_NODES: +% for node in SYNTAX_NODES: % if is_visitable(node): case SyntaxKind::${node.syntax_kind}: visit(node.castTo<${node.name}>()); diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index e549407aaae6b..675c9d2a3faea 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -19,10 +19,12 @@ #include "swift/AST/Availability.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/Basic/LLVM.h" +#include "swift/ClangImporter/ClangImporter.h" #include "swift/IRGen/IRGenPublic.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/FormalLinkage.h" @@ -32,9 +34,14 @@ #include "swift/SIL/SILWitnessTable.h" #include "swift/SIL/SILWitnessVisitor.h" #include "swift/SIL/TypeLowering.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/Mangler.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Process.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/YAMLParser.h" #include "llvm/TextAPI/MachO/InterfaceFile.h" #include "llvm/TextAPI/MachO/TextAPIReader.h" #include "llvm/TextAPI/MachO/TextAPIWriter.h" @@ -44,6 +51,7 @@ using namespace swift; using namespace swift::irgen; using namespace swift::tbdgen; +using namespace llvm::yaml; using StringSet = llvm::StringSet<>; using SymbolKind = llvm::MachO::SymbolKind; @@ -51,9 +59,12 @@ static bool isGlobalOrStaticVar(VarDecl *VD) { return VD->isStatic() || VD->getDeclContext()->isModuleScopeContext(); } -void TBDGenVisitor::addSymbol(StringRef name, SymbolKind kind) { - Symbols.addSymbol(kind, name, Archs); - +void TBDGenVisitor::addSymbolInternal(StringRef name, + llvm::MachO::SymbolKind kind, + bool isLinkerDirective) { + if (!isLinkerDirective && Opts.LinkerDirectivesOnly) + return; + Symbols.addSymbol(kind, name, Targets); if (StringSymbols && kind == SymbolKind::GlobalSymbol) { auto isNewValue = StringSymbols->insert(name).second; (void)isNewValue; @@ -61,6 +72,305 @@ void TBDGenVisitor::addSymbol(StringRef name, SymbolKind kind) { } } +static std::vector +getAllMovedPlatformVersions(Decl *D) { + std::vector Results; + for (auto *attr: D->getAttrs()) { + if (auto *ODA = dyn_cast(attr)) { + auto Active = ODA->isActivePlatform(D->getASTContext()); + if (Active.hasValue()) { + Results.push_back(*Active); + } + } + } + return Results; +} + +static StringRef getLinkerPlatformName(uint8_t Id) { + switch (Id) { +#define LD_PLATFORM(Name, Id) case Id: return #Name; +#include "ldPlatformKinds.def" + default: + llvm_unreachable("unrecognized platform id"); + } +} + +static Optional getLinkerPlatformId(StringRef Platform) { + return llvm::StringSwitch>(Platform) +#define LD_PLATFORM(Name, Id) .Case(#Name, Id) +#include "ldPlatformKinds.def" + .Default(None); +} + +StringRef InstallNameStore::getInstallName(LinkerPlatformId Id) const { + auto It = PlatformInstallName.find((uint8_t)Id); + if (It == PlatformInstallName.end()) + return InstallName; + else + return It->second; +} + +void InstallNameStore::remark(ASTContext &Ctx, StringRef ModuleName) const { + Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name, + ModuleName, InstallName); + for (auto Pair: PlatformInstallName) { + Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name, + ModuleName, getLinkerPlatformName(Pair.first), + Pair.second); + } +} + +static std::string getScalaNodeText(Node *N) { + SmallString<32> Buffer; + return cast(N)->getValue(Buffer).str(); +} + +static std::set getSequenceNodePlatformList(ASTContext &Ctx, Node *N) { + std::set Results; + for (auto &E: *cast(N)) { + auto Platform = getScalaNodeText(&E); + auto Id = getLinkerPlatformId(Platform); + if (Id.hasValue()) { + Results.insert(*Id); + } else { + // Diagnose unrecognized platform name. + Ctx.Diags.diagnose(SourceLoc(), diag::unknown_platform_name, Platform); + } + } + return Results; +} + +/// Parse an entry like this, where the "platforms" key-value pair is optional: +/// { +/// "module": "Foo", +/// "platforms": ["macOS"], +/// "install_name": "/System/MacOS" +/// }, +static int +parseEntry(ASTContext &Ctx, + Node *Node, std::map &Stores) { + if (auto *SN = cast(Node)) { + for (auto It = SN->begin(); It != SN->end(); ++It) { + auto *MN = cast(&*It); + std::string ModuleName; + std::string InstallName; + Optional> Platforms; + for (auto &Pair: *MN) { + auto Key = getScalaNodeText(Pair.getKey()); + auto* Value = Pair.getValue(); + if (Key == "module") { + ModuleName = getScalaNodeText(Value); + } else if (Key == "platforms") { + Platforms = getSequenceNodePlatformList(Ctx, Value); + } else if (Key == "install_name") { + InstallName = getScalaNodeText(Value); + } else { + return 1; + } + } + if (ModuleName.empty() || InstallName.empty()) + return 1; + auto &Store = Stores.insert(std::make_pair(ModuleName, + InstallNameStore())).first->second; + if (Platforms.hasValue()) { + // This install name is platform-specific. + for (auto Id: Platforms.getValue()) { + Store.PlatformInstallName[Id] = InstallName; + } + } else { + // The install name is the default one. + Store.InstallName = InstallName; + } + } + } else { + return 1; + } + return 0; +} + +std::unique_ptr> +TBDGenVisitor::parsePreviousModuleInstallNameMap() { + StringRef FileName = Opts.ModuleInstallNameMapPath; + // Nothing to parse. + if (FileName.empty()) + return nullptr; + namespace yaml = llvm::yaml; + ASTContext &Ctx = SwiftModule->getASTContext(); + std::unique_ptr> pResult( + new std::map()); + auto &AllInstallNames = *pResult; + SWIFT_DEFER { + for (auto Pair: AllInstallNames) { + Pair.second.remark(Ctx, Pair.first); + } + }; + + // Load the input file. + llvm::ErrorOr> FileBufOrErr = + llvm::MemoryBuffer::getFile(FileName); + if (!FileBufOrErr) { + Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_missing, + FileName); + return nullptr; + } + StringRef Buffer = FileBufOrErr->get()->getBuffer(); + + // Use a new source manager instead of the one from ASTContext because we + // don't want the Json file to be persistent. + SourceManager SM; + yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, FileName), + SM.getLLVMSourceMgr()); + for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { + assert(DI != Stream.end() && "Failed to read a document"); + yaml::Node *N = DI->getRoot(); + assert(N && "Failed to find a root"); + if (parseEntry(Ctx, N, AllInstallNames)) { + Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_corrupted, + FileName); + return nullptr; + } + } + return pResult; +} + +static LinkerPlatformId +getLinkerPlatformId(OriginallyDefinedInAttr::ActiveVersion Ver) { + switch(Ver.Platform) { + case swift::PlatformKind::none: + llvm_unreachable("cannot find platform kind"); + case swift::PlatformKind::iOS: + case swift::PlatformKind::iOSApplicationExtension: + return Ver.IsSimulator ? LinkerPlatformId::iOS_sim: + LinkerPlatformId::iOS; + case swift::PlatformKind::tvOS: + case swift::PlatformKind::tvOSApplicationExtension: + return Ver.IsSimulator ? LinkerPlatformId::tvOS_sim: + LinkerPlatformId::tvOS; + case swift::PlatformKind::watchOS: + case swift::PlatformKind::watchOSApplicationExtension: + return Ver.IsSimulator ? LinkerPlatformId::watchOS_sim: + LinkerPlatformId::watchOS; + case swift::PlatformKind::OSX: + case swift::PlatformKind::OSXApplicationExtension: + return LinkerPlatformId::macOS; + case swift::PlatformKind::macCatalyst: + case swift::PlatformKind::macCatalystApplicationExtension: + return LinkerPlatformId::macCatalyst; + } +} + +static StringRef +getLinkerPlatformName(OriginallyDefinedInAttr::ActiveVersion Ver) { + return getLinkerPlatformName((uint8_t)getLinkerPlatformId(Ver)); +} + +void TBDGenVisitor::addLinkerDirectiveSymbolsLdPrevious(StringRef name, + llvm::MachO::SymbolKind kind) { + if (kind != llvm::MachO::SymbolKind::GlobalSymbol) + return; + if (!TopLevelDecl) + return; + auto MovedVers = getAllMovedPlatformVersions(TopLevelDecl); + if (MovedVers.empty()) + return; + assert(!MovedVers.empty()); + assert(previousInstallNameMap); + auto &Ctx = TopLevelDecl->getASTContext(); + for (auto &Ver: MovedVers) { + auto IntroVer = TopLevelDecl->getIntroducedOSVersion(Ver.Platform); + assert(IntroVer && "cannot find OS intro version"); + if (!IntroVer.hasValue()) + continue; + auto PlatformNumber = getLinkerPlatformId(Ver); + auto It = previousInstallNameMap->find(Ver.ModuleName); + if (It == previousInstallNameMap->end()) { + Ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_install_name, + Ver.ModuleName, getLinkerPlatformName(Ver)); + continue; + } + auto InstallName = It->second.getInstallName(PlatformNumber); + if (InstallName.empty()) { + Ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_install_name, + Ver.ModuleName, getLinkerPlatformName(Ver)); + continue; + } + llvm::SmallString<64> Buffer; + llvm::raw_svector_ostream OS(Buffer); + // Empty compatible version indicates using the current compatible version. + StringRef ComptibleVersion = ""; + OS << "$ld$previous$"; + OS << InstallName << "$"; + OS << ComptibleVersion << "$"; + OS << std::to_string((uint8_t)PlatformNumber) << "$"; + static auto getMinor = [](Optional Minor) { + return Minor.hasValue() ? *Minor : 0; + }; + OS << IntroVer->getMajor() << "." << getMinor(IntroVer->getMinor()) << "$"; + OS << Ver.Version.getMajor() << "." << getMinor(Ver.Version.getMinor()) << "$"; + OS << name << "$"; + addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol, + /*LinkerDirective*/true); + } +} + +void TBDGenVisitor::addLinkerDirectiveSymbolsLdHide(StringRef name, + llvm::MachO::SymbolKind kind) { + if (kind != llvm::MachO::SymbolKind::GlobalSymbol) + return; + if (!TopLevelDecl) + return; + auto MovedVers = getAllMovedPlatformVersions(TopLevelDecl); + if (MovedVers.empty()) + return; + assert(!MovedVers.empty()); + + // Using $ld$add and $ld$hide cannot encode platform name in the version number, + // so we can only handle one version. + // FIXME: use $ld$previous instead + auto MovedVer = MovedVers.front().Version; + auto Platform = MovedVers.front().Platform; + unsigned Major[2]; + unsigned Minor[2]; + Major[1] = MovedVer.getMajor(); + Minor[1] = MovedVer.getMinor().hasValue() ? *MovedVer.getMinor(): 0; + auto IntroVer = TopLevelDecl->getIntroducedOSVersion(Platform); + assert(IntroVer && "cannot find the start point of availability"); + if (!IntroVer.hasValue()) + return; + assert(*IntroVer < MovedVer); + if (*IntroVer >= MovedVer) + return; + Major[0] = IntroVer->getMajor(); + Minor[0] = IntroVer->getMinor().hasValue() ? IntroVer->getMinor().getValue() : 0; + for (auto CurMaj = Major[0]; CurMaj <= Major[1]; ++ CurMaj) { + unsigned MinRange[2] = {0, 31}; + if (CurMaj == Major[0]) + MinRange[0] = Minor[0]; + if (CurMaj == Major[1]) + MinRange[1] = Minor[1]; + for (auto CurMin = MinRange[0]; CurMin != MinRange[1]; ++ CurMin) { + llvm::SmallString<64> Buffer; + llvm::raw_svector_ostream OS(Buffer); + OS << "$ld$hide$os" << CurMaj << "." << CurMin << "$" << name; + addSymbolInternal(OS.str(), llvm::MachO::SymbolKind::GlobalSymbol, + /*LinkerDirective*/true); + } + } +} + +void TBDGenVisitor::addSymbol(StringRef name, SymbolKind kind) { + // The linker expects to see mangled symbol names in TBD files, so make sure + // to mangle before inserting the symbol. + SmallString<32> mangled; + llvm::Mangler::getNameWithPrefix(mangled, name, DataLayout); + addSymbolInternal(mangled, kind); + if (previousInstallNameMap) { + addLinkerDirectiveSymbolsLdPrevious(mangled, kind); + } else { + addLinkerDirectiveSymbolsLdHide(mangled, kind); + } +} + void TBDGenVisitor::addSymbol(SILDeclRef declRef) { auto linkage = effectiveLinkageForClassMember( declRef.getLinkage(ForDefinition), @@ -216,7 +526,7 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { addSymbol( LinkEntity::forDynamicallyReplaceableFunctionKey(AFD, useAllocator)); } - if (AFD->getAttrs().hasAttribute()) { + if (AFD->getDynamicallyReplacedDecl()) { bool useAllocator = shouldUseAllocatorMangling(AFD); addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable( AFD, useAllocator)); @@ -244,7 +554,7 @@ void TBDGenVisitor::visitFuncDecl(FuncDecl *FD) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorKey(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } - if (FD->getAttrs().hasAttribute()) { + if (FD->getDynamicallyReplacedDecl()) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessor(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } @@ -272,7 +582,7 @@ void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorKey(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } - if (ASD->hasAnyDynamicReplacementAccessors()) { + if (ASD->getDynamicallyReplacedDecl()) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessor(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } @@ -584,20 +894,55 @@ void TBDGenVisitor::addFirstFileSymbols() { } } -/// Converts a version tuple into a packed version, ignoring components beyond -/// major, minor, and subminor. -static llvm::MachO::PackedVersion -convertToPacked(const version::Version &version) { - // FIXME: Warn if version is greater than 3 components? - unsigned major = 0, minor = 0, subminor = 0; - if (version.size() > 0) major = version[0]; - if (version.size() > 1) minor = version[1]; - if (version.size() > 2) subminor = version[2]; - return llvm::MachO::PackedVersion(major, minor, subminor); +/// The kind of version being parsed, used for diagnostics. +/// Note: Must match the order in DiagnosticsFrontend.def +enum DylibVersionKind_t: unsigned { + CurrentVersion, + CompatibilityVersion +}; + +/// Converts a version string into a packed version, truncating each component +/// if necessary to fit all 3 into a 32-bit packed structure. +/// +/// For example, the version '1219.37.11' will be packed as +/// +/// Major (1,219) Minor (37) Patch (11) +/// ┌───────────────────┬──────────┬──────────┐ +/// │ 00001100 11000011 │ 00100101 │ 00001011 │ +/// └───────────────────┴──────────┴──────────┘ +/// +/// If an individual component is greater than the highest number that can be +/// represented in its alloted space, it will be truncated to the maximum value +/// that fits in the alloted space, which matches the behavior of the linker. +static Optional +parsePackedVersion(DylibVersionKind_t kind, StringRef versionString, + ASTContext &ctx) { + if (versionString.empty()) + return None; + + llvm::MachO::PackedVersion version; + auto result = version.parse64(versionString); + if (!result.first) { + ctx.Diags.diagnose(SourceLoc(), diag::tbd_err_invalid_version, + (unsigned)kind, versionString); + return None; + } + if (result.second) { + ctx.Diags.diagnose(SourceLoc(), diag::tbd_warn_truncating_version, + (unsigned)kind, versionString); + } + return version; } static bool isApplicationExtensionSafe(const LangOptions &LangOpts) { - return LangOpts.EnableAppExtensionRestrictions; + // Existing linkers respect these flags to determine app extension safety. + return LangOpts.EnableAppExtensionRestrictions || + llvm::sys::Process::GetEnv("LD_NO_ENCRYPT") || + llvm::sys::Process::GetEnv("LD_APPLICATION_EXTENSION_SAFE"); +} + +static bool hasLinkerDirective(Decl *D) { + return !getAllMovedPlatformVersions(D).empty(); } static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, @@ -606,8 +951,8 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, const TBDGenOptions &opts) { auto &ctx = M->getASTContext(); auto isWholeModule = singleFile == nullptr; - const auto &target = ctx.LangOpts.Target; - UniversalLinkageInfo linkInfo(target, opts.HasMultipleIGMs, false, + const auto &triple = ctx.LangOpts.Target; + UniversalLinkageInfo linkInfo(triple, opts.HasMultipleIGMs, false, isWholeModule); llvm::MachO::InterfaceFile file; @@ -615,32 +960,27 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, file.setApplicationExtensionSafe( isApplicationExtensionSafe(M->getASTContext().LangOpts)); file.setInstallName(opts.InstallName); - file.setCurrentVersion(convertToPacked(opts.CurrentVersion)); - file.setCompatibilityVersion(convertToPacked(opts.CompatibilityVersion)); file.setTwoLevelNamespace(); file.setSwiftABIVersion(irgen::getSwiftABIVersion()); file.setInstallAPI(opts.IsInstallAPI); - auto getPlatformKind = - [](const llvm::Triple &Target) -> llvm::MachO::PlatformKind { - switch (Target.getOS()) { - default: - return llvm::MachO::PlatformKind::unknown; - case llvm::Triple::MacOSX: - return llvm::MachO::PlatformKind::macOS; - case llvm::Triple::IOS: - return llvm::MachO::PlatformKind::iOS; - case llvm::Triple::TvOS: - return llvm::MachO::PlatformKind::tvOS; - case llvm::Triple::WatchOS: - return llvm::MachO::PlatformKind::watchOS; - } - }; - auto arch = llvm::MachO::getArchitectureFromName(target.getArchName()); - file.addArch(arch); - file.setPlatform(getPlatformKind(target)); + if (auto packed = parsePackedVersion(CurrentVersion, + opts.CurrentVersion, ctx)) { + file.setCurrentVersion(*packed); + } + + if (auto packed = parsePackedVersion(CompatibilityVersion, + opts.CompatibilityVersion, ctx)) { + file.setCompatibilityVersion(*packed); + } - TBDGenVisitor visitor(file, arch, symbols, linkInfo, M, opts); + llvm::MachO::Target target(triple); + file.addTarget(target); + + auto *clang = static_cast(ctx.getClangModuleLoader()); + TBDGenVisitor visitor(file, {target}, symbols, + clang->getTargetInfo().getDataLayout(), + linkInfo, M, opts); auto visitFile = [&](FileUnit *file) { if (file == M->getFiles()[0]) { @@ -652,8 +992,13 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, visitor.addMainIfNecessary(file); - for (auto d : decls) + for (auto d : decls) { + if (opts.LinkerDirectivesOnly && !hasLinkerDirective(d)) + continue; + visitor.TopLevelDecl = d; + SWIFT_DEFER { visitor.TopLevelDecl = nullptr; }; visitor.visit(d); + } }; if (singleFile) { diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index 227fed22400a5..bf34d426ed53e 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -34,23 +34,52 @@ using namespace swift::irgen; using StringSet = llvm::StringSet<>; +namespace llvm { +class DataLayout; +} + namespace swift { struct TBDGenOptions; namespace tbdgen { +enum class LinkerPlatformId: uint8_t { +#define LD_PLATFORM(Name, Id) Name = Id, +#include "ldPlatformKinds.def" +}; + +struct InstallNameStore { + // The default install name to use when no specific install name is specified. + std::string InstallName; + // The install name specific to the platform id. This takes precedence over + // the default install name. + std::map PlatformInstallName; + StringRef getInstallName(LinkerPlatformId Id) const; + void remark(ASTContext &Ctx, StringRef ModuleName) const; +}; + class TBDGenVisitor : public ASTVisitor { public: llvm::MachO::InterfaceFile &Symbols; - llvm::MachO::ArchitectureSet Archs; + llvm::MachO::TargetList Targets; StringSet *StringSymbols; + const llvm::DataLayout &DataLayout; const UniversalLinkageInfo &UniversalLinkInfo; ModuleDecl *SwiftModule; const TBDGenOptions &Opts; + Decl* TopLevelDecl = nullptr; private: + std::unique_ptr> + previousInstallNameMap; + std::unique_ptr> + parsePreviousModuleInstallNameMap(); + void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind, + bool isLinkerDirective = false); + void addLinkerDirectiveSymbolsLdHide(StringRef name, llvm::MachO::SymbolKind kind); + void addLinkerDirectiveSymbolsLdPrevious(StringRef name, llvm::MachO::SymbolKind kind); void addSymbol(StringRef name, llvm::MachO::SymbolKind kind = llvm::MachO::SymbolKind::GlobalSymbol); @@ -71,12 +100,14 @@ class TBDGenVisitor : public ASTVisitor { public: TBDGenVisitor(llvm::MachO::InterfaceFile &symbols, - llvm::MachO::ArchitectureSet archs, StringSet *stringSymbols, + llvm::MachO::TargetList targets, StringSet *stringSymbols, + const llvm::DataLayout &dataLayout, const UniversalLinkageInfo &universalLinkInfo, ModuleDecl *swiftModule, const TBDGenOptions &opts) - : Symbols(symbols), Archs(archs), StringSymbols(stringSymbols), - UniversalLinkInfo(universalLinkInfo), SwiftModule(swiftModule), - Opts(opts) {} + : Symbols(symbols), Targets(targets), StringSymbols(stringSymbols), + DataLayout(dataLayout), UniversalLinkInfo(universalLinkInfo), + SwiftModule(swiftModule), Opts(opts), + previousInstallNameMap(parsePreviousModuleInstallNameMap()) {} void addMainIfNecessary(FileUnit *file) { // HACK: 'main' is a special symbol that's always emitted in SILGen if diff --git a/lib/TBDGen/ldPlatformKinds.def b/lib/TBDGen/ldPlatformKinds.def new file mode 100644 index 0000000000000..46ab4d99c7dc8 --- /dev/null +++ b/lib/TBDGen/ldPlatformKinds.def @@ -0,0 +1,30 @@ +//===--- ldPlatformKinds.def - Compiler declaration metaprogramming --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines platform IDs used to emit linker directives $ld$previous +// +//===----------------------------------------------------------------------===// + +#ifndef LD_PLATFORM +# define LD_PLATFORM(Name, Id) +#endif + +LD_PLATFORM(macOS, 1) +LD_PLATFORM(iOS, 2) +LD_PLATFORM(tvOS, 3) +LD_PLATFORM(watchOS, 4) +LD_PLATFORM(macCatalyst, 6) +LD_PLATFORM(iOS_sim, 7) +LD_PLATFORM(tvOS_sim, 8) +LD_PLATFORM(watchOS_sim, 9) + +#undef LD_PLATFORM diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 0f7aa8edc7332..a80005624d4ec 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -15,33 +15,20 @@ function(add_swift_target_executable name) set(SWIFTEXE_TARGET_SOURCES ${SWIFTEXE_TARGET_UNPARSED_ARGUMENTS}) - translate_flag(${SWIFTEXE_TARGET_EXCLUDE_FROM_ALL} - "EXCLUDE_FROM_ALL" - SWIFTEXE_TARGET_EXCLUDE_FROM_ALL_FLAG) + if(SWIFTEXE_TARGET_EXCLUDE_FROM_ALL) + message(SEND_ERROR "${name} is using EXCLUDE_FROM_ALL which is deprecated.") + endif() # All Swift executables depend on the standard library. list(APPEND SWIFTEXE_TARGET_LINK_LIBRARIES swiftCore) # All Swift executables depend on the swiftSwiftOnoneSupport library. list(APPEND SWIFTEXE_TARGET_DEPENDS swiftSwiftOnoneSupport) - if(NOT "${SWIFT_BUILD_STDLIB}") - list(REMOVE_ITEM SWIFTEXE_TARGET_LINK_LIBRARIES - swiftCore) - endif() - foreach(sdk ${SWIFT_SDKS}) foreach(arch ${SWIFT_SDK_${sdk}_ARCHITECTURES}) set(VARIANT_SUFFIX "-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}") set(VARIANT_NAME "${name}${VARIANT_SUFFIX}") - set(SWIFTEXE_TARGET_EXCLUDE_FROM_ALL_FLAG_CURRENT - ${SWIFTEXE_TARGET_EXCLUDE_FROM_ALL_FLAG}) - if(NOT "${VARIANT_SUFFIX}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SUFFIX}") - # By default, don't build executables for target SDKs to avoid building - # target stdlibs. - set(SWIFTEXE_TARGET_EXCLUDE_FROM_ALL_FLAG_CURRENT "EXCLUDE_FROM_ALL") - endif() - if(SWIFTEXE_TARGET_BUILD_WITH_STDLIB) add_dependencies("swift-test-stdlib${VARIANT_SUFFIX}" ${VARIANT_NAME}) endif() @@ -59,8 +46,14 @@ function(add_swift_target_executable name) LLVM_LINK_COMPONENTS ${SWIFTEXE_TARGET_LLVM_LINK_COMPONENTS} SDK "${sdk}" ARCHITECTURE "${arch}" - LINK_LIBRARIES ${SWIFTEXE_TARGET_LINK_LIBRARIES} - ${SWIFTEXE_TARGET_EXCLUDE_FROM_ALL_FLAG_CURRENT}) + LINK_LIBRARIES ${SWIFTEXE_TARGET_LINK_LIBRARIES}) + + if(NOT "${VARIANT_SUFFIX}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SUFFIX}") + # By default, don't build executables for target SDKs to avoid building + # target stdlibs. + set_target_properties(${VARIANT_NAME} PROPERTIES + EXCLUDE_FROM_ALL TRUE) + endif() if(${sdk} IN_LIST SWIFT_APPLE_PLATFORMS) add_custom_command_target(unused_var2 diff --git a/stdlib/private/OSLog/CMakeLists.txt b/stdlib/private/OSLog/CMakeLists.txt index 4cbcb186b4cc5..4dcf40af1cc98 100644 --- a/stdlib/private/OSLog/CMakeLists.txt +++ b/stdlib/private/OSLog/CMakeLists.txt @@ -6,11 +6,12 @@ add_swift_target_library(swiftOSLogPrototype OSLogMessage.swift OSLogIntegerTypes.swift OSLogStringTypes.swift + OSLogNSObjectType.swift - SWIFT_MODULE_DEPENDS_IOS Darwin os - SWIFT_MODULE_DEPENDS_OSX Darwin os - SWIFT_MODULE_DEPENDS_TVOS Darwin os - SWIFT_MODULE_DEPENDS_WATCHOS Darwin os + SWIFT_MODULE_DEPENDS_IOS Darwin os ObjectiveC + SWIFT_MODULE_DEPENDS_OSX Darwin os ObjectiveC + SWIFT_MODULE_DEPENDS_TVOS Darwin os ObjectiveC + SWIFT_MODULE_DEPENDS_WATCHOS Darwin os ObjectiveC TARGET_SDKS ALL_APPLE_PLATFORMS SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT never_install diff --git a/stdlib/private/OSLog/OSLog.swift b/stdlib/private/OSLog/OSLog.swift index 4c9ee1fefd171..2b3091e1fcf30 100644 --- a/stdlib/private/OSLog/OSLog.swift +++ b/stdlib/private/OSLog/OSLog.swift @@ -21,16 +21,21 @@ public struct Logger { @usableFromInline internal let logObject: OSLog - /// Create a custom OS log object. + /// Create a custom OSLog object for logging. public init(subsystem: String, category: String) { logObject = OSLog(subsystem: subsystem, category: category) } - /// Return the default OS log object. + /// Use the default OSLog object for logging. public init() { logObject = OSLog.default } + /// Create a Logger instance from an existing OSLog Object. + public init(_ logObj: OSLog) { + logObject = logObj + } + // Functions defined below are marked @_optimize(none) to prevent inlining // of string internals (such as String._StringGuts) which will interfere with // constant evaluation and folding. Note that these functions will be inlined, @@ -41,23 +46,71 @@ public struct Logger { @_transparent @_optimize(none) public func log(level: OSLogType = .default, _ message: OSLogMessage) { - osLog(logObject, level, message) + osLog(log: logObject, level: level, message) + } + + // The following overloads are for logging at specific levels. The levels that + // are supported are debug (also called trace), info, notice (also called + // default), error (also called warning), fault (also called critical). + + @_transparent + @_optimize(none) + public func trace(_ message: OSLogMessage) { + osLog(log: logObject, level: .debug, message) + } + + @_transparent + @_optimize(none) + public func debug(_ message: OSLogMessage) { + osLog(log: logObject, level: .debug, message) + } + + @_transparent + @_optimize(none) + public func info(_ message: OSLogMessage) { + osLog(log: logObject, level: .info, message) + } + + @_transparent + @_optimize(none) + public func notice(_ message: OSLogMessage) { + osLog(log: logObject, level: .default, message) } - // TODO: define overloads for logging at specific levels: debug, info, notice, - // error, fault based on the Swift forum "logging-levels" discussion. + @_transparent + @_optimize(none) + public func warning(_ message: OSLogMessage) { + osLog(log: logObject, level: .error, message) + } + + @_transparent + @_optimize(none) + public func error(_ message: OSLogMessage) { + osLog(log: logObject, level: .error, message) + } + + @_transparent + @_optimize(none) + public func critical(_ message: OSLogMessage) { + osLog(log: logObject, level: .fault, message) + } + + @_transparent + @_optimize(none) + public func fault(_ message: OSLogMessage) { + osLog(log: logObject, level: .fault, message) + } } /// Given an instance of the custom string interpolation type: `OSLogMessage`, /// extract the format string, serialize the arguments to a byte buffer, /// and pass them to the OS logging system. @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -@usableFromInline @_transparent @_optimize(none) -internal func osLog( - _ logObject: OSLog, - _ logLevel: OSLogType, +public func osLog( + log logObject: OSLog, + level logLevel: OSLogType, _ message: OSLogMessage ) { // Compute static constants first so that they can be folded by @@ -66,13 +119,14 @@ internal func osLog( let preamble = message.interpolation.preamble let argumentCount = message.interpolation.argumentCount let bufferSize = message.bufferSize + let uint32bufferSize = UInt32(bufferSize) + let argumentClosures = message.interpolation.arguments.argumentClosures + let formatStringPointer = _getGlobalStringTablePointer(formatString) // Code that will execute at runtime. guard logObject.isEnabled(type: logLevel) else { return } - let arguments = message.interpolation.arguments - // Allocate a byte buffer to store the arguments. The buffer could be stack // allocated as it is local to this function and also its size is a // compile-time constant. @@ -84,18 +138,21 @@ internal func osLog( var currentBufferPosition = bufferMemory serialize(preamble, at: ¤tBufferPosition) serialize(argumentCount, at: ¤tBufferPosition) - arguments.serializeAt(¤tBufferPosition, using: &stringStorageObjects) + argumentClosures.forEach { $0(¤tBufferPosition, &stringStorageObjects) } ___os_log_impl(UnsafeMutableRawPointer(mutating: #dsohandle), logObject, logLevel, formatStringPointer, bufferMemory, - UInt32(bufferSize)) - - // The following operation extends the lifetime of stringStorageObjects - // and also of the objects stored in it till this point. - stringStorageObjects.removeAll() + uint32bufferSize) + + // The following operation extends the lifetime of argumentClosures, + // stringStorageObjects, and also of the objects stored in them till this + // point. This is necessary because __os_log_impl is passed internal pointers + // to the objects/strings stored in these arrays. + _fixLifetime(argumentClosures) + _fixLifetime(stringStorageObjects) bufferMemory.deallocate() } @@ -120,6 +177,7 @@ func _checkFormatStringAndBuffer( let preamble = message.interpolation.preamble let argumentCount = message.interpolation.argumentCount let bufferSize = message.bufferSize + let argumentClosures = message.interpolation.arguments.argumentClosures // Code that will execute at runtime. let bufferMemory = UnsafeMutablePointer.allocate(capacity: bufferSize) @@ -128,14 +186,13 @@ func _checkFormatStringAndBuffer( var currentBufferPosition = bufferMemory serialize(preamble, at: ¤tBufferPosition) serialize(argumentCount, at: ¤tBufferPosition) - message.interpolation.arguments.serializeAt( - ¤tBufferPosition, - using: &stringStorageObjects) + argumentClosures.forEach { $0(¤tBufferPosition, &stringStorageObjects) } assertion( formatString, UnsafeBufferPointer(start: UnsafePointer(bufferMemory), count: bufferSize)) - stringStorageObjects.removeAll() + _fixLifetime(argumentClosures) + _fixLifetime(stringStorageObjects) bufferMemory.deallocate() } diff --git a/stdlib/private/OSLog/OSLogIntegerTypes.swift b/stdlib/private/OSLog/OSLogIntegerTypes.swift index c73c0982a1698..673a22ce7ac0f 100644 --- a/stdlib/private/OSLog/OSLogIntegerTypes.swift +++ b/stdlib/private/OSLog/OSLogIntegerTypes.swift @@ -29,7 +29,8 @@ extension OSLogInterpolation { /// enum `IntFormat`. /// - privacy: a privacy qualifier which is either private or public. /// The default is public. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int, @@ -46,7 +47,8 @@ extension OSLogInterpolation { /// enum `IntFormat`. /// - privacy: a privacy qualifier which is either private or public. /// The default is public. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int32, @@ -59,9 +61,9 @@ extension OSLogInterpolation { /// Given an integer, create and append a format specifier for the integer to the /// format string property. Also, append the integer along with necessary headers /// to the OSLogArguments property. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) - @usableFromInline internal mutating func appendInteger( _ number: @escaping () -> T, format: IntFormat, @@ -83,9 +85,9 @@ extension OSLogInterpolation { /// Update preamble and append argument headers based on the parameters of /// the interpolation. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) - @usableFromInline internal mutating func addIntHeaders(_ isPrivate: Bool, _ byteCount: Int) { // Append argument header. let argumentHeader = getArgumentHeader(isPrivate: isPrivate, type: .scalar) @@ -139,7 +141,9 @@ extension OSLogArguments { /// Append an (autoclosured) interpolated expression of integer type, passed to /// `OSLogMessage.appendInterpolation`, to the array of closures tracked /// by this instance. - @usableFromInline + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) internal mutating func append( _ value: @escaping () -> T ) where T: FixedWidthInteger { @@ -151,9 +155,8 @@ extension OSLogArguments { /// Return the number of bytes needed for serializing an integer argument as /// specified by os_log. This function must be constant evaluable. -@inlinable @_semantics("constant_evaluable") -@_effects(readonly) +@inlinable @_optimize(none) internal func sizeForEncoding( _ type: T.Type @@ -163,8 +166,9 @@ internal func sizeForEncoding( /// Serialize an integer at the buffer location that `position` points to and /// increment `position` by the byte size of `T`. -@usableFromInline +@inlinable @_alwaysEmitIntoClient +@inline(__always) internal func serialize( _ value: T, at bufferPosition: inout ByteBufferPointer diff --git a/stdlib/private/OSLog/OSLogMessage.swift b/stdlib/private/OSLog/OSLogMessage.swift index c5e58dac738c0..7d27b797700ba 100644 --- a/stdlib/private/OSLog/OSLogMessage.swift +++ b/stdlib/private/OSLog/OSLogMessage.swift @@ -43,11 +43,14 @@ public enum Privacy { /// Maximum number of arguments i.e., interpolated expressions that can /// be used in the string interpolations passed to the log APIs. /// This limit is imposed by the ABI of os_log. -@_transparent +@_semantics("constant_evaluable") +@inlinable +@_optimize(none) public var maxOSLogArgumentCount: UInt8 { return 48 } -@usableFromInline -@_transparent +@_semantics("constant_evaluable") +@inlinable +@_optimize(none) internal var logBitsPerByte: Int { return 3 } /// Represents a string interpolation passed to the log APIs. @@ -180,24 +183,13 @@ public struct OSLogInterpolation : StringInterpolationProtocol { // constant evaluation and folding. Note that these methods will be inlined, // constant evaluated/folded and optimized in the context of a caller. - @_transparent - @_optimize(none) - public init(literalCapacity: Int, interpolationCount: Int) { - // Since the format string is fully constructed at compile time, - // the parameter `literalCapacity` is ignored. - formatString = "" - arguments = OSLogArguments(capacity: interpolationCount) - preamble = 0 - argumentCount = 0 - totalBytesForSerializingArguments = 0 - } - - /// An internal initializer that should be used only when there are no - /// interpolated expressions. This function must be constant evaluable. - @inlinable + @_semantics("oslog.interpolation.init") @_semantics("constant_evaluable") + @inlinable @_optimize(none) - internal init() { + public init(literalCapacity: Int, interpolationCount: Int) { + // Since the format string and the arguments array are fully constructed + // at compile time, the parameters are ignored. formatString = "" arguments = OSLogArguments() preamble = 0 @@ -205,7 +197,8 @@ public struct OSLogInterpolation : StringInterpolationProtocol { totalBytesForSerializingArguments = 0 } - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) public mutating func appendLiteral(_ literal: String) { formatString += literal.percentEscapedString @@ -305,7 +298,7 @@ public struct OSLogMessage : @_semantics("oslog.message.init_stringliteral") @_semantics("constant_evaluable") public init(stringLiteral value: String) { - var s = OSLogInterpolation() + var s = OSLogInterpolation(literalCapacity: 1, interpolationCount: 0) s.appendLiteral(value) self.interpolation = s } @@ -313,7 +306,8 @@ public struct OSLogMessage : /// The byte size of the buffer that will be passed to the C os_log ABI. /// It will contain the elements of `interpolation.arguments` and the two /// summary bytes: preamble and argument count. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) public var bufferSize: Int { return interpolation.totalBytesForSerializingArguments + 2 @@ -340,23 +334,18 @@ internal struct OSLogArguments { internal var argumentClosures: [(inout ByteBufferPointer, inout StorageObjects) -> ()] - /// This function must be constant evaluable. - @inlinable @_semantics("constant_evaluable") + @inlinable @_optimize(none) internal init() { argumentClosures = [] } - @usableFromInline - internal init(capacity: Int) { - argumentClosures = [] - argumentClosures.reserveCapacity(capacity) - } - /// Append a byte-sized header, constructed by /// `OSLogMessage.appendInterpolation`, to the tracked array of closures. - @usableFromInline + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) internal mutating func append(_ header: UInt8) { argumentClosures.append({ (position, _) in serialize(header, at: &position) @@ -364,28 +353,13 @@ internal struct OSLogArguments { } /// `append` for other types must be implemented by extensions. - - /// Serialize the arguments tracked by self in a byte buffer. - /// - Parameters: - /// - bufferPosition: the pointer to a location within a byte buffer where - /// the argument must be serialized. This will be incremented by the number - /// of bytes used up to serialize the arguments. - /// - storageObjects: An array to store references to objects representing - /// auxiliary storage created during serialization. This is only used while - /// serializing strings. - @usableFromInline - internal func serializeAt( - _ bufferPosition: inout ByteBufferPointer, - using storageObjects: inout StorageObjects - ) { - argumentClosures.forEach { $0(&bufferPosition, &storageObjects) } - } } /// Serialize a UInt8 value at the buffer location pointed to by `bufferPosition`, /// and increment the `bufferPosition` with the byte size of the serialized value. -@usableFromInline +@inlinable @_alwaysEmitIntoClient +@inline(__always) internal func serialize( _ value: UInt8, at bufferPosition: inout ByteBufferPointer) diff --git a/stdlib/private/OSLog/OSLogNSObjectType.swift b/stdlib/private/OSLog/OSLogNSObjectType.swift new file mode 100644 index 0000000000000..72d64e65081d8 --- /dev/null +++ b/stdlib/private/OSLog/OSLogNSObjectType.swift @@ -0,0 +1,118 @@ +//===----------------- OSLogNSObjectType.swift ----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// This file defines extensions for interpolating NSObject into a OSLogMesage. +// It defines `appendInterpolation` function for NSObject type. It also defines +// extensions for generating an os_log format string for NSObjects (using the +// format specifier %@) and for serializing NSObject into the argument buffer +// passed to os_log ABIs. +// +// The `appendInterpolation` function defined in this file accept formatting +// and privacy options along with the interpolated expression as shown below: +// +// "\(x, privacy: .public\)" +import ObjectiveC + +extension OSLogInterpolation { + + /// Define interpolation for expressions of type NSObject. + /// - Parameters: + /// - argumentObject: the interpolated expression of type NSObject, which is autoclosured. + /// - privacy: a privacy qualifier which is either private or public. Default is private. + /// TODO: create a specifier to denote auto-inferred privacy level and make it default. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public mutating func appendInterpolation( + _ argumentObject: @autoclosure @escaping () -> NSObject, + privacy: Privacy = .private + ) { + guard argumentCount < maxOSLogArgumentCount else { return } + + let isPrivateArgument = isPrivate(privacy) + formatString += getNSObjectFormatSpecifier(isPrivateArgument) + addNSObjectHeaders(isPrivateArgument) + + arguments.append(argumentObject) + argumentCount += 1 + } + + /// Update preamble and append argument headers based on the parameters of + /// the interpolation. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func addNSObjectHeaders(_ isPrivate: Bool) { + // Append argument header. + let header = getArgumentHeader(isPrivate: isPrivate, type: .object) + arguments.append(header) + + // Append number of bytes needed to serialize the argument. + let byteCount = pointerSizeInBytes() + arguments.append(UInt8(byteCount)) + + // Increment total byte size by the number of bytes needed for this + // argument, which is the sum of the byte size of the argument and + // two bytes needed for the headers. + totalBytesForSerializingArguments += byteCount + 2 + + preamble = getUpdatedPreamble(isPrivate: isPrivate, isScalar: false) + } + + /// Construct an os_log format specifier from the given parameters. + /// This function must be constant evaluable and all its arguments + /// must be known at compile time. + @inlinable + @_semantics("constant_evaluable") + @_effects(readonly) + @_optimize(none) + internal func getNSObjectFormatSpecifier(_ isPrivate: Bool) -> String { + // TODO: create a specifier to denote auto-inferred privacy. + return isPrivate ? "%{private}@" : "%{public}@" + } +} + +extension OSLogArguments { + /// Append an (autoclosured) interpolated expression of type NSObject, passed to + /// `OSLogMessage.appendInterpolation`, to the array of closures tracked + /// by this instance. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal mutating func append(_ value: @escaping () -> NSObject) { + argumentClosures.append({ (position, _) in + serialize(value(), at: &position) + }) + } +} + +/// Serialize an NSObject pointer at the buffer location pointed by +/// `bufferPosition`. +@inlinable +@_alwaysEmitIntoClient +@inline(__always) +internal func serialize( + _ object: NSObject, + at bufferPosition: inout ByteBufferPointer +) { + let byteCount = pointerSizeInBytes(); + let dest = + UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount) + // Get the address of this NSObject as an UnsafeRawPointer. + let objectAddress = Unmanaged.passUnretained(object).toOpaque() + // Copy the address into the destination buffer. Note that the input NSObject + // is an interpolated expression and is guaranteed to be alive until the + // os_log ABI call is completed by the implementation. Therefore, passing + // this address to the os_log ABI is safe. + withUnsafeBytes(of: objectAddress) { dest.copyMemory(from: $0) } + bufferPosition += byteCount +} diff --git a/stdlib/private/OSLog/OSLogStringTypes.swift b/stdlib/private/OSLog/OSLogStringTypes.swift index dceba377e2795..00226082cb843 100644 --- a/stdlib/private/OSLog/OSLogStringTypes.swift +++ b/stdlib/private/OSLog/OSLogStringTypes.swift @@ -32,7 +32,8 @@ extension OSLogInterpolation { /// - argumentString: the interpolated expression of type String, which is autoclosured. /// - privacy: a privacy qualifier which is either private or public. Default is private. /// TODO: create a specifier to denote auto-inferred privacy level and make it default. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) public mutating func appendInterpolation( _ argumentString: @autoclosure @escaping () -> String, @@ -50,16 +51,16 @@ extension OSLogInterpolation { /// Update preamble and append argument headers based on the parameters of /// the interpolation. - @_transparent + @_semantics("constant_evaluable") + @inlinable @_optimize(none) - @usableFromInline internal mutating func addStringHeaders(_ isPrivate: Bool) { // Append argument header. let header = getArgumentHeader(isPrivate: isPrivate, type: .string) arguments.append(header) // Append number of bytes needed to serialize the argument. - let byteCount = sizeForEncoding() + let byteCount = pointerSizeInBytes() arguments.append(UInt8(byteCount)) // Increment total byte size by the number of bytes needed for this @@ -87,7 +88,9 @@ extension OSLogArguments { /// Append an (autoclosured) interpolated expression of String type, passed to /// `OSLogMessage.appendInterpolation`, to the array of closures tracked /// by this instance. - @usableFromInline + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) internal mutating func append(_ value: @escaping () -> String) { argumentClosures.append({ serialize(value(), at: &$0, using: &$1) }) } @@ -99,11 +102,10 @@ extension OSLogArguments { /// evaluator, this function returns the byte size of Int, which must equal the /// word length of the target architecture and hence the pointer size. /// This function must be constant evaluable. +@_semantics("constant_evaluable") @inlinable @_optimize(none) -@_effects(readonly) -@_semantics("constant_evaluable") -internal func sizeForEncoding() -> Int { +internal func pointerSizeInBytes() -> Int { return Int.bitWidth &>> logBitsPerByte } @@ -111,8 +113,9 @@ internal func sizeForEncoding() -> Int { /// pointed by `bufferPosition`. When necessary, this function would copy the /// string contents to a storage with a stable pointer. If that happens, a reference /// to the storage will be added to `storageObjects`. -@usableFromInline +@inlinable @_alwaysEmitIntoClient +@inline(__always) internal func serialize( _ stringValue: String, at bufferPosition: inout ByteBufferPointer, @@ -126,7 +129,7 @@ internal func serialize( storageObjects.append(storage) } - let byteCount = sizeForEncoding() + let byteCount = pointerSizeInBytes() let dest = UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount) withUnsafeBytes(of: bytePointer) { dest.copyMemory(from: $0) } diff --git a/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift b/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift index beeb60d36f6db..03626108de34c 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift +++ b/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift @@ -906,7 +906,6 @@ public func checkOneLevelOfRandomAccessCollection< //===------------------------------------------------------------------===// let succ = { collection.index(after: $0) } - let pred = { collection.index(before: $0) } // Advances up to 1 positions without passing endIndex. Don't use // advanced(by: n) to do this because it's under test here. let next = { $0 == collection.endIndex ? $0 : succ($0) } @@ -924,7 +923,6 @@ public func checkOneLevelOfRandomAccessCollection< let count: Distance = collection.count let offset0 = min(5, count) let offset1 = min(10, count) - let offset2 = min(15, count) let distanceCandidates: [Distance] = [ -11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11] @@ -1676,7 +1674,6 @@ public func checkOneLevelOfRandomAccessCollection< //===------------------------------------------------------------------===// let succ = { collection.index(after: $0) } - let pred = { collection.index(before: $0) } // Advances up to 1 positions without passing endIndex. Don't use // advanced(by: n) to do this because it's under test here. let next = { $0 == collection.endIndex ? $0 : succ($0) } @@ -1694,7 +1691,6 @@ public func checkOneLevelOfRandomAccessCollection< let count: Distance = collection.count let offset0 = min(5, count) let offset1 = min(10, count) - let offset2 = min(15, count) let distanceCandidates: [Distance] = [ -11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11] @@ -1871,7 +1867,7 @@ public func checkRangeReplaceable( let source = Array(makeCollection()) for (ix, i) in source.indices.enumerated() { - for (jx_, j) in (i.. UnsafeMutableRawPointer? { +#if os(Windows) + return unsafeBitCast(GetProcAddress(hStdlibCore, name), + to: UnsafeMutableRawPointer?.self) +#else + return dlsym(RTLD_DEFAULT, name) +#endif +} diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 002aef8e74145..bc849536a9429 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -162,10 +162,7 @@ public class _stdlib_Barrier { } deinit { - let ret = _stdlib_thread_barrier_destroy(_threadBarrierPtr) - if ret != 0 { - fatalError("_stdlib_thread_barrier_destroy() failed") - } + _stdlib_thread_barrier_destroy(_threadBarrierPtr) } public func wait() { diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9f5e183447021..54517a901b117 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -69,13 +69,12 @@ public func _stdlib_thread_barrier_init( InitializeConditionVariable(barrier.pointee.cond!) #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_mutex_init(barrier.pointee.mutex!, nil) != 0 { - // FIXME: leaking memory. - return -1 - } barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_cond_init(barrier.pointee.cond!, nil) != 0 { - // FIXME: leaking memory, leaking a mutex. + guard _stdlib_thread_barrier_mutex_and_cond_init(barrier) == 0 else { + barrier.pointee.mutex!.deinitialize(count: 1) + barrier.pointee.mutex!.deallocate() + barrier.pointee.cond!.deinitialize(count: 1) + barrier.pointee.cond!.deallocate() return -1 } #endif @@ -83,20 +82,29 @@ public func _stdlib_thread_barrier_init( return 0 } +#if !os(Windows) +private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t>) -> CInt { + guard pthread_mutex_init(barrier.pointee.mutex!, nil) == 0 else { + return -1 + } + guard pthread_cond_init(barrier.pointee.cond!, nil) == 0 else { + pthread_mutex_destroy(barrier.pointee.mutex!) + return -1 + } + return 0 +} +#endif + public func _stdlib_thread_barrier_destroy( _ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t> -) -> CInt { +) { #if os(Windows) // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed #else - if pthread_cond_destroy(barrier.pointee.cond!) != 0 { - // FIXME: leaking memory, leaking a mutex. - return -1 - } - if pthread_mutex_destroy(barrier.pointee.mutex!) != 0 { - // FIXME: leaking memory. - return -1 + guard pthread_cond_destroy(barrier.pointee.cond!) == 0 && + pthread_mutex_destroy(barrier.pointee.mutex!) == 0 else { + fatalError("_stdlib_thread_barrier_destroy() failed") } #endif barrier.pointee.cond!.deinitialize(count: 1) @@ -105,7 +113,7 @@ public func _stdlib_thread_barrier_destroy( barrier.pointee.mutex!.deinitialize(count: 1) barrier.pointee.mutex!.deallocate() - return 0 + return } public func _stdlib_thread_barrier_wait( diff --git a/stdlib/private/SwiftReflectionTest/CMakeLists.txt b/stdlib/private/SwiftReflectionTest/CMakeLists.txt index a61b29996a2c6..7c33850c96a2c 100644 --- a/stdlib/private/SwiftReflectionTest/CMakeLists.txt +++ b/stdlib/private/SwiftReflectionTest/CMakeLists.txt @@ -9,17 +9,15 @@ if (SWIFT_INCLUDE_TESTS) SWIFT_MODULE_DEPENDS_TVOS Darwin SWIFT_MODULE_DEPENDS_WATCHOS Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc + SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT INSTALL_IN_COMPONENT stdlib-experimental DARWIN_INSTALL_NAME_DIR "${SWIFT_DARWIN_STDLIB_PRIVATE_INSTALL_NAME_DIR}") foreach(SDK ${SWIFT_SDKS}) - if("${SDK}" IN_LIST SWIFT_APPLE_PLATFORMS) - foreach(ARCH ${SWIFT_SDK_${SDK}_ARCHITECTURES}) - set(VARIANT_SUFFIX "-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-${ARCH}") - add_dependencies( - "swiftSwiftReflectionTest${VARIANT_SUFFIX}" - "swift-reflection-test${VARIANT_SUFFIX}") - endforeach() - endif() + foreach(ARCH ${SWIFT_SDK_${SDK}_ARCHITECTURES}) + set(VARIANT_SUFFIX "-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-${ARCH}") + add_dependencies("swiftSwiftReflectionTest${VARIANT_SUFFIX}" + "swift-reflection-test${VARIANT_SUFFIX}") + endforeach() endforeach() endif() diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 8b661b508cfc7..e8d5da6490b76 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -61,6 +61,10 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(stubs) add_subdirectory(core) add_subdirectory(SwiftOnoneSupport) + + if(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) + add_subdirectory(Differentiation) + endif() endif() if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) diff --git a/stdlib/public/Darwin/ARKit/CMakeLists.txt b/stdlib/public/Darwin/ARKit/CMakeLists.txt index df79968511cb8..f8e3eeff16388 100644 --- a/stdlib/public/Darwin/ARKit/CMakeLists.txt +++ b/stdlib/public/Darwin/ARKit/CMakeLists.txt @@ -11,6 +11,7 @@ add_swift_target_library(swiftARKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_ TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch GLKit SceneKit simd Foundation AVFoundation SpriteKit CoreMedia QuartzCore ModelIO CoreFoundation CoreAudio ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK ARKit + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_ARKIT_IOS} INSTALL_IN_COMPONENT sdk-overlay diff --git a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt index ad2afdf90eced..e77e6e3618d0b 100644 --- a/stdlib/public/Darwin/AVFoundation/CMakeLists.txt +++ b/stdlib/public/Darwin/AVFoundation/CMakeLists.txt @@ -16,7 +16,7 @@ add_swift_target_library(swiftAVFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYP TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation CoreMedia QuartzCore XPC CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreGraphics Metal Dispatch simd Foundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/CloudKit/CKRecord.swift b/stdlib/public/Darwin/CloudKit/CKRecord.swift index 8d5ad1ab0b67a..1438b03756d8a 100644 --- a/stdlib/public/Darwin/CloudKit/CKRecord.swift +++ b/stdlib/public/Darwin/CloudKit/CKRecord.swift @@ -58,17 +58,17 @@ extension CKRecord { @available(swift 4.2) public enum SystemType { - public static let userRecord: CKRecord.RecordType = __CKRecordTypeUserRecord as CKRecord.RecordType + public static let userRecord: CKRecord.RecordType = __CKRecordTypeUserRecord @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public static let share: CKRecord.RecordType = __CKRecordTypeShare as CKRecord.RecordType + public static let share: CKRecord.RecordType = __CKRecordTypeShare } @available(swift 4.2) public enum SystemFieldKey { @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public static let parent: CKRecord.FieldKey = __CKRecordParentKey as CKRecord.RecordType + public static let parent: CKRecord.FieldKey = __CKRecordParentKey @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) - public static let share: CKRecord.FieldKey = __CKRecordShareKey as CKRecord.RecordType + public static let share: CKRecord.FieldKey = __CKRecordShareKey } } diff --git a/stdlib/public/Darwin/CloudKit/CKShare.swift b/stdlib/public/Darwin/CloudKit/CKShare.swift index 4ee425359b6f7..51409a3182b34 100644 --- a/stdlib/public/Darwin/CloudKit/CKShare.swift +++ b/stdlib/public/Darwin/CloudKit/CKShare.swift @@ -17,9 +17,9 @@ extension CKShare { @available(swift 4.2) public enum SystemFieldKey { - public static let title: CKRecord.FieldKey = __CKShareTitleKey as CKRecord.FieldKey - public static let thumbnailImageData: CKRecord.FieldKey = __CKShareThumbnailImageDataKey as CKRecord.FieldKey - public static let shareType: CKRecord.FieldKey = __CKShareTypeKey as CKRecord.FieldKey + public static let title: CKRecord.FieldKey = __CKShareTitleKey + public static let thumbnailImageData: CKRecord.FieldKey = __CKShareThumbnailImageDataKey + public static let shareType: CKRecord.FieldKey = __CKShareTypeKey } } diff --git a/stdlib/public/Darwin/CloudKit/TypealiasStrings.swift b/stdlib/public/Darwin/CloudKit/TypealiasStrings.swift index c6646076a1bdb..8995cdbb07054 100644 --- a/stdlib/public/Darwin/CloudKit/TypealiasStrings.swift +++ b/stdlib/public/Darwin/CloudKit/TypealiasStrings.swift @@ -43,7 +43,7 @@ extension CKOperation { */ @available(swift 4.2) public var operationID: CKOperation.ID { - get { return self.__operationID as CKOperation.ID } + get { return self.__operationID } } } diff --git a/stdlib/public/Darwin/CoreMedia/CMTime.swift b/stdlib/public/Darwin/CoreMedia/CMTime.swift index ee4a4683b953e..bf515ae4649f0 100644 --- a/stdlib/public/Darwin/CoreMedia/CMTime.swift +++ b/stdlib/public/Darwin/CoreMedia/CMTime.swift @@ -62,7 +62,7 @@ extension CMTime { } public var seconds: Double { - return CMTimeGetSeconds(self) as Double + return CMTimeGetSeconds(self) } public func convertScale(_ newTimescale: Int32, method: CMTimeRoundingMethod) diff --git a/stdlib/public/Darwin/Foundation/CMakeLists.txt b/stdlib/public/Darwin/Foundation/CMakeLists.txt index 9389a4a7646bb..dcee6a4c27dbb 100644 --- a/stdlib/public/Darwin/Foundation/CMakeLists.txt +++ b/stdlib/public/Darwin/Foundation/CMakeLists.txt @@ -32,7 +32,6 @@ add_swift_target_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES NSCoder.swift NSDate.swift NSDictionary.swift - NSError.mm NSError.swift NSExpression.swift NSFastEnumeration.swift diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift index bf8ed4b6a3580..931ed4408781f 100644 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ b/stdlib/public/Darwin/Foundation/Data.swift @@ -106,10 +106,10 @@ internal final class __DataStorage { @usableFromInline var _bytes: UnsafeMutableRawPointer? @usableFromInline var _length: Int @usableFromInline var _capacity: Int - @usableFromInline var _needToZero: Bool - @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? @usableFromInline var _offset: Int - + @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? + @usableFromInline var _needToZero: Bool + @inlinable // This is @inlinable as trivially computable. var bytes: UnsafeRawPointer? { return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) @@ -1402,7 +1402,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl if newValue == 0 { return nil } else if InlineData.canStore(count: newValue) { - return .inline(InlineData()) + return .inline(InlineData(count: newValue)) } else if InlineSlice.canStore(count: newValue) { return .slice(InlineSlice(count: newValue)) } else { diff --git a/stdlib/public/Darwin/Foundation/DateComponents.swift b/stdlib/public/Darwin/Foundation/DateComponents.swift index e7b80159007b6..cc113c7951b21 100644 --- a/stdlib/public/Darwin/Foundation/DateComponents.swift +++ b/stdlib/public/Darwin/Foundation/DateComponents.swift @@ -204,11 +204,7 @@ public struct DateComponents : ReferenceConvertible, Hashable, Equatable, _Mutab /// Returns a `Date` calculated from the current components using the `calendar` property. public var date: Date? { - if let d = _handle.map({$0.date}) { - return d as Date - } else { - return nil - } + return _handle.map { $0.date } } // MARK: - Generic Setter/Getters diff --git a/stdlib/public/Darwin/Foundation/DateInterval.swift b/stdlib/public/Darwin/Foundation/DateInterval.swift index 6ee7f4afe01fc..13b45c1271e28 100644 --- a/stdlib/public/Darwin/Foundation/DateInterval.swift +++ b/stdlib/public/Darwin/Foundation/DateInterval.swift @@ -54,10 +54,7 @@ public struct DateInterval : ReferenceConvertible, Comparable, Hashable, Codable /// /// - precondition: `end >= start` public init(start: Date, end: Date) { - if end < start { - fatalError("Reverse intervals are not allowed") - } - + precondition(end >= start, "Reverse intervals are not allowed") self.start = start duration = end.timeIntervalSince(start) } diff --git a/stdlib/public/Darwin/Foundation/FileManager.swift b/stdlib/public/Darwin/Foundation/FileManager.swift index f6d3d4319c180..d026b3a8b667d 100644 --- a/stdlib/public/Darwin/Foundation/FileManager.swift +++ b/stdlib/public/Darwin/Foundation/FileManager.swift @@ -48,7 +48,7 @@ extension FileManager { return __NSFileManagerEnumeratorAtURL(self, url, keys, mask, { (url, error) in var errorResult = true if let h = handler { - errorResult = h(url as URL, error) + errorResult = h(url, error) } return errorResult }) diff --git a/stdlib/public/Darwin/Foundation/IndexSet.swift b/stdlib/public/Darwin/Foundation/IndexSet.swift index 157e9bfaf8218..76f20e21923fe 100644 --- a/stdlib/public/Darwin/Foundation/IndexSet.swift +++ b/stdlib/public/Darwin/Foundation/IndexSet.swift @@ -442,16 +442,23 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio /// Union the `IndexSet` with `other`. public func union(_ other: IndexSet) -> IndexSet { - // This algorithm is naïve but it works. We could avoid calling insert in some cases. - - var result = IndexSet() - for r in self.rangeView { - result.insert(integersIn: r) + var result: IndexSet + var dense: IndexSet + + // Prepare to make a copy of the more sparse IndexSet to prefer copy over repeated inserts + if self.rangeView.count > other.rangeView.count { + result = self + dense = other + } else { + result = other + dense = self } - - for r in other.rangeView { - result.insert(integersIn: r) + + // Insert each range from the less sparse IndexSet + dense.rangeView.forEach { + result.insert(integersIn: $0) } + return result } @@ -593,7 +600,7 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio stop.pointee = true return false } - }) as IndexSet + }) if let e = error { throw e } else { diff --git a/stdlib/public/Darwin/Foundation/NSCoder.swift b/stdlib/public/Darwin/Foundation/NSCoder.swift index df8aef0f9dab6..7e1f9e687b14d 100644 --- a/stdlib/public/Darwin/Foundation/NSCoder.swift +++ b/stdlib/public/Darwin/Foundation/NSCoder.swift @@ -53,7 +53,7 @@ extension NSCoder { if let theClasses = classes { classesAsNSObjects = NSSet(array: theClasses.map { $0 as AnyObject }) } - return __NSCoderDecodeObjectOfClassesForKey(self, classesAsNSObjects, key, nil).map { $0 as Any } + return __NSCoderDecodeObjectOfClassesForKey(self, classesAsNSObjects, key, nil).map { $0 } } @nonobjc @@ -62,7 +62,7 @@ extension NSCoder { var error: NSError? let result = __NSCoderDecodeObject(self, &error) try resolveError(error) - return result.map { $0 as Any } + return result.map { $0 } } @available(*, unavailable, renamed: "decodeTopLevelObject(forKey:)") @@ -125,7 +125,7 @@ extension NSCoder { } let result = __NSCoderDecodeObjectOfClassesForKey(self, classesAsNSObjects, key, &error) try resolveError(error) - return result.map { $0 as Any } + return result.map { $0 } } } diff --git a/stdlib/public/Darwin/Foundation/NSDictionary.swift b/stdlib/public/Darwin/Foundation/NSDictionary.swift index b51934345c9ef..4e21739c27ab4 100644 --- a/stdlib/public/Darwin/Foundation/NSDictionary.swift +++ b/stdlib/public/Darwin/Foundation/NSDictionary.swift @@ -64,7 +64,7 @@ extension Dictionary { extension Dictionary : _ObjectiveCBridgeable { @_semantics("convertToObjectiveC") public func _bridgeToObjectiveC() -> NSDictionary { - return unsafeBitCast(_bridgeToObjectiveCImpl() as AnyObject, + return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSDictionary.self) } diff --git a/stdlib/public/Darwin/Foundation/NSError.h b/stdlib/public/Darwin/Foundation/NSError.h deleted file mode 100644 index 01e9f2b259a50..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSError.h +++ /dev/null @@ -1,45 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_FOUNDATION_NSERROR_H -#define SWIFT_FOUNDATION_NSERROR_H - -#include "swift/Runtime/Metadata.h" -#include "../../runtime/SwiftHashableSupport.h" -#include - -namespace swift { - -/// The name of the symbol that ErrorObject.mm will look up using dlsym. It uses -/// this to locate various items related to Error bridging to NS/CFError. -#define ERROR_BRIDGING_SYMBOL_NAME swift_errorBridgingInfo_ -#define ERROR_BRIDGING_SYMBOL_NAME_STRING "swift_errorBridgingInfo_" - -/// The items that ErrorObject.mm needs for bridging. The -/// ERROR_BRIDGING_SYMBOL_NAME symbol will contain an instance of this struct. -struct ErrorBridgingInfo { - const ProtocolConformanceDescriptor *CFErrorErrorConformance; - const ProtocolConformanceDescriptor *NSObjectHashableConformance; - - SWIFT_CC(swift) NSDictionary *(*GetErrorDefaultUserInfo)(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - - SWIFT_CC(swift) bool (*BridgeErrorToNSError)(NSError *, OpaqueValue*, const Metadata *, - const WitnessTable *); - - const ProtocolDescriptor *ObjectiveCBridgeableError; -}; - -} - -#endif // SWIFT_FOUNDATION_NSERROR_H diff --git a/stdlib/public/Darwin/Foundation/NSError.mm b/stdlib/public/Darwin/Foundation/NSError.mm deleted file mode 100644 index f70c2ab4bf362..0000000000000 --- a/stdlib/public/Darwin/Foundation/NSError.mm +++ /dev/null @@ -1,49 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "NSError.h" - -#include "swift/Demangling/ManglingMacros.h" - -using namespace swift; - -// Declare the mangled Swift symbols that we'll be putting in the bridging info. -extern "C" const ProtocolConformanceDescriptor - MANGLE_SYM(So10CFErrorRefas5Error10FoundationMc); - -extern "C" const ProtocolConformanceDescriptor - MANGLE_SYM(So8NSObjectCSH10ObjectiveCMc); - -extern "C" SWIFT_CC(swift) - NSDictionary *MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)( - const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); - -extern "C" SWIFT_CC(swift) bool - MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)( - NSError *, OpaqueValue*, const Metadata *, const WitnessTable *); - -extern "C" const ProtocolDescriptor - MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp); - -// Define the bridging info struct. -extern "C" ErrorBridgingInfo ERROR_BRIDGING_SYMBOL_NAME = { - &MANGLE_SYM(So10CFErrorRefas5Error10FoundationMc), - &MANGLE_SYM(So8NSObjectCSH10ObjectiveCMc), - MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF), - MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF), - &MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp) -}; - -// This directive ensures that the symbol is preserved even when statically -// linked into an executable and stripped, so that the dlsym lookup from -// ErrorObject.mm still works. -asm(".desc _" ERROR_BRIDGING_SYMBOL_NAME_STRING ", 0x10"); diff --git a/stdlib/public/Darwin/Foundation/NSError.swift b/stdlib/public/Darwin/Foundation/NSError.swift index e844752f9e2d3..aa41c239ec8fa 100644 --- a/stdlib/public/Darwin/Foundation/NSError.swift +++ b/stdlib/public/Darwin/Foundation/NSError.swift @@ -234,8 +234,7 @@ public func _getErrorDefaultUserInfo(_ error: T) if domain != NSCocoaErrorDomain { _errorDomainUserInfoProviderQueue.sync { if NSError.userInfoValueProvider(forDomain: domain) != nil { return } - NSError.setUserInfoValueProvider(forDomain: domain) { (nsError, key) in - let error = nsError as Error + NSError.setUserInfoValueProvider(forDomain: domain) { (error, key) in switch key { case NSLocalizedDescriptionKey: diff --git a/stdlib/public/Darwin/Foundation/NSSet.swift b/stdlib/public/Darwin/Foundation/NSSet.swift index e571475fc7b8b..b52148a5cf32a 100644 --- a/stdlib/public/Darwin/Foundation/NSSet.swift +++ b/stdlib/public/Darwin/Foundation/NSSet.swift @@ -55,7 +55,7 @@ extension NSOrderedSet : Sequence { extension Set : _ObjectiveCBridgeable { @_semantics("convertToObjectiveC") public func _bridgeToObjectiveC() -> NSSet { - return unsafeBitCast(_bridgeToObjectiveCImpl() as AnyObject, to: NSSet.self) + return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSSet.self) } public static func _forceBridgeFromObjectiveC(_ s: NSSet, result: inout Set?) { diff --git a/stdlib/public/Darwin/Foundation/NSStringAPI.swift b/stdlib/public/Darwin/Foundation/NSStringAPI.swift index 8aa1929268b92..512a06182b288 100644 --- a/stdlib/public/Darwin/Foundation/NSStringAPI.swift +++ b/stdlib/public/Darwin/Foundation/NSStringAPI.swift @@ -353,7 +353,7 @@ extension String { usedEncoding: inout Encoding ) throws { var enc: UInt = 0 - let ns = try NSString(contentsOf: url as URL, usedEncoding: &enc) + let ns = try NSString(contentsOf: url, usedEncoding: &enc) usedEncoding = Encoding(rawValue: enc) self = String._unconditionallyBridgeFromObjectiveC(ns) } @@ -566,7 +566,7 @@ extension StringProtocol where Index == String.Index { /// Case transformations aren’t guaranteed to be symmetrical or to produce /// strings of the same lengths as the originals. public var capitalized: String { - return _ns.capitalized as String + return _ns.capitalized } // @property (readonly, copy) NSString *localizedCapitalizedString NS_AVAILABLE(10_11, 9_0); @@ -583,7 +583,7 @@ extension StringProtocol where Index == String.Index { /// Returns a capitalized representation of the string /// using the specified locale. public func capitalized(with locale: Locale?) -> String { - return _ns.capitalized(with: locale) as String + return _ns.capitalized(with: locale) } // - (NSComparisonResult)caseInsensitiveCompare:(NSString *)aString @@ -1166,7 +1166,7 @@ extension StringProtocol where Index == String.Index { /// locale. @available(macOS 10.11, iOS 9.0, *) public var localizedUppercase: String { - return _ns.localizedUppercase as String + return _ns.localizedUppercase } // - (NSString *)uppercaseStringWithLocale:(Locale *)locale @@ -1671,7 +1671,7 @@ extension StringProtocol where Index == String.Index { /// Returns `true` if `other` is non-empty and contained within `self` by /// case-sensitive, non-literal search. Otherwise, returns `false`. /// - /// Equivalent to `self.rangeOfString(other) != nil` + /// Equivalent to `self.range(of: other) != nil` public func contains(_ other: T) -> Bool { let r = self.range(of: other) != nil if #available(macOS 10.10, iOS 8.0, *) { diff --git a/stdlib/public/Darwin/Foundation/Progress.swift b/stdlib/public/Darwin/Foundation/Progress.swift index 9db3bdacf9e6c..6164d119e68b2 100644 --- a/stdlib/public/Darwin/Foundation/Progress.swift +++ b/stdlib/public/Darwin/Foundation/Progress.swift @@ -17,7 +17,7 @@ extension Progress { public var estimatedTimeRemaining: TimeInterval? { get { guard let v = self.__estimatedTimeRemaining else { return nil } - return v.doubleValue as TimeInterval + return v.doubleValue } set { guard let nv = newValue else { diff --git a/stdlib/public/Darwin/Foundation/String.swift b/stdlib/public/Darwin/Foundation/String.swift index df50b1ae349da..6ad3fa8c19a25 100644 --- a/stdlib/public/Darwin/Foundation/String.swift +++ b/stdlib/public/Darwin/Foundation/String.swift @@ -32,7 +32,7 @@ extension String : _ObjectiveCBridgeable { // This method should not do anything extra except calling into the // implementation inside core. (These two entry points should be // equivalent.) - return unsafeBitCast(_bridgeToObjectiveCImpl() as AnyObject, to: NSString.self) + return unsafeBitCast(_bridgeToObjectiveCImpl(), to: NSString.self) } public static func _forceBridgeFromObjectiveC( diff --git a/stdlib/public/Darwin/Foundation/URL.swift b/stdlib/public/Darwin/Foundation/URL.swift index aa3c59d7b88c4..63113e94f3d74 100644 --- a/stdlib/public/Darwin/Foundation/URL.swift +++ b/stdlib/public/Darwin/Foundation/URL.swift @@ -1090,7 +1090,7 @@ public struct URL : ReferenceConvertible, Equatable { /// Returns bookmark data for the URL, created with specified options and resource values. public func bookmarkData(options: BookmarkCreationOptions = [], includingResourceValuesForKeys keys: Set? = nil, relativeTo url: URL? = nil) throws -> Data { let result = try _url.bookmarkData(options: options, includingResourceValuesForKeys: keys.flatMap { Array($0) }, relativeTo: url) - return result as Data + return result } /// Returns the resource values for properties identified by a specified array of keys contained in specified bookmark data. If the result dictionary does not contain a resource value for one or more of the requested resource keys, it means those resource properties are not available in the bookmark data. @@ -1107,7 +1107,7 @@ public struct URL : ReferenceConvertible, Equatable { /// Initializes and returns bookmark data derived from an alias file pointed to by a specified URL. If bookmarkFileURL refers to an alias file created prior to OS X v10.6 that contains Alias Manager information but no bookmark data, this method synthesizes bookmark data for the file. public static func bookmarkData(withContentsOf url: URL) throws -> Data { let result = try NSURL.bookmarkData(withContentsOf: url) - return result as Data + return result } /// Given an NSURL created by resolving a bookmark data created with security scope, make the resource referenced by the url accessible to the process. When access to this resource is no longer needed the client must call stopAccessingSecurityScopedResource. Each call to startAccessingSecurityScopedResource must be balanced with a call to stopAccessingSecurityScopedResource (Note: this is not reference counted). diff --git a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt index 2befab06cdfef..0700ae75b0d6b 100644 --- a/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt +++ b/stdlib/public/Darwin/MediaPlayer/CMakeLists.txt @@ -7,10 +7,11 @@ add_swift_target_library(swiftMediaPlayer ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPE "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal UIKit Dispatch simd Foundation AVFoundation CoreMedia QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated + SWIFT_MODULE_DEPENDS_FROM_SDK CoreMIDI FRAMEWORK_DEPENDS_WEAK MediaPlayer DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_MEDIAPLAYER_IOS} diff --git a/stdlib/public/Darwin/Network/NWConnection.swift b/stdlib/public/Darwin/Network/NWConnection.swift index 666bda0ec9475..f25e0719a27e6 100644 --- a/stdlib/public/Darwin/Network/NWConnection.swift +++ b/stdlib/public/Darwin/Network/NWConnection.swift @@ -479,7 +479,7 @@ public final class NWConnection : CustomDebugStringConvertible { _ isComplete: Bool, _ error: NWError?) -> Void) { nw_connection_receive(self.nw, UInt32(minimumIncompleteLength), UInt32(maximumLength)) { (content, context, complete, nwError) in - completion(NWCreateNSDataFromDispatchData(content) as Data?, ContentContext(context), complete, NWError(nwError)); + completion(NWCreateNSDataFromDispatchData(content), ContentContext(context), complete, NWError(nwError)); } } @@ -491,7 +491,7 @@ public final class NWConnection : CustomDebugStringConvertible { _ contentContext: NWConnection.ContentContext?, _ isComplete: Bool, _ error: NWError?) -> Void) { nw_connection_receive_message(self.nw) { (content, context, complete, nwError) in - completion(NWCreateNSDataFromDispatchData(content) as Data?, ContentContext(context), complete, NWError(nwError)) + completion(NWCreateNSDataFromDispatchData(content), ContentContext(context), complete, NWError(nwError)) } } diff --git a/stdlib/public/Darwin/Photos/CMakeLists.txt b/stdlib/public/Darwin/Photos/CMakeLists.txt index e59dc5b2445af..7d8a8aae1e786 100644 --- a/stdlib/public/Darwin/Photos/CMakeLists.txt +++ b/stdlib/public/Darwin/Photos/CMakeLists.txt @@ -8,7 +8,7 @@ add_swift_target_library(swiftPhotos ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR OSX SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_TVOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation AVFoundation CoreMedia CoreLocation QuartzCore CoreFoundation CoreAudio ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/Vision/CMakeLists.txt b/stdlib/public/Darwin/Vision/CMakeLists.txt index c956ccd3ef2c6..5fd9cb4202f1e 100644 --- a/stdlib/public/Darwin/Vision/CMakeLists.txt +++ b/stdlib/public/Darwin/Vision/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftVision ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR SWIFT_MODULE_DEPENDS_OSX Darwin CoreImage CoreGraphics Metal Dispatch IOKit simd Foundation XPC CoreFoundation ObjectiveC # auto-updated SWIFT_MODULE_DEPENDS_IOS Darwin CoreImage CoreGraphics Metal Dispatch simd Foundation CoreFoundation ObjectiveC # auto-updated diff --git a/stdlib/public/Darwin/WatchKit/CMakeLists.txt b/stdlib/public/Darwin/WatchKit/CMakeLists.txt index dbe216c09d61c..37431bd22ceff 100644 --- a/stdlib/public/Darwin/WatchKit/CMakeLists.txt +++ b/stdlib/public/Darwin/WatchKit/CMakeLists.txt @@ -7,7 +7,7 @@ add_swift_target_library(swiftWatchKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" "-L${sdk}/usr/lib/swift/" TARGET_SDKS WATCHOS WATCHOS_SIMULATOR SWIFT_MODULE_DEPENDS_WATCHOS Darwin HomeKit CoreGraphics UIKit Dispatch SceneKit simd Foundation MapKit CoreLocation CoreFoundation ObjectiveC # auto-updated FRAMEWORK_DEPENDS_WEAK WatchKit diff --git a/stdlib/public/Darwin/os/format.m b/stdlib/public/Darwin/os/format.m index e64415603fe03..5ef8a790ddd79 100644 --- a/stdlib/public/Darwin/os/format.m +++ b/stdlib/public/Darwin/os/format.m @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include #include #include "format.h" @@ -24,7 +25,7 @@ _os_log_encode(char buf[OS_LOG_FMT_BUF_SIZE], const char *format, va_list args, int saved_errno, os_trace_blob_t ob) { - os_log_fmt_hdr_s hdr = { }; + os_log_fmt_hdr_s hdr = {0}; os_trace_blob_add(ob, &hdr, sizeof(hdr)); const char *percent = strchr(format, '%'); @@ -32,7 +33,7 @@ while (percent != NULL) { ++percent; if (percent[0] != '%') { - os_log_fmt_cmd_s cmd = { }; + os_log_fmt_cmd_s cmd = {0}; os_log_count_type_t widtht = T_C_NONE; os_log_count_type_t prect = T_C_NONE; os_log_fmt_cmd_flags_t flags = 0; @@ -99,159 +100,192 @@ } break; -#define encode_width() ({ \ - if (widtht == T_C_DYNAMIC) { \ - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; \ - encode(int, 0); \ - }}) +#define encode_width() \ + do { \ + if (widtht == T_C_DYNAMIC) { \ + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; \ + encode(int, 0); \ + } \ + } while (0) // clang inconsistency: static precision counts are still marked with the // privacy bits of the command they preceed -#define encode_precision(type) ({ \ - if (prect != T_C_NONE) { \ - cmd.cmd_type = type; \ - cmd.cmd_size = sizeof(int); \ - if (prect == T_C_STATIC && type == OSLF_CMD_TYPE_COUNT) { \ - cmd.cmd_flags = flags; \ - } else if (prect == T_C_DYNAMIC) { \ - precision = va_arg(args, int); \ - } \ - _os_log_encode_arg(ob, &cmd, &precision); \ - hdr.hdr_cmd_cnt++; \ - prect = T_C_NONE; \ - }}) +#define encode_precision(type) \ + do { \ + if (prect != T_C_NONE) { \ + cmd.cmd_type = type; \ + cmd.cmd_size = sizeof(int); \ + if (prect == T_C_STATIC && type == OSLF_CMD_TYPE_COUNT) { \ + cmd.cmd_flags = flags; \ + } else if (prect == T_C_DYNAMIC) { \ + precision = va_arg(args, int); \ + } \ + _os_log_encode_arg(ob, &cmd, &precision); \ + hdr.hdr_cmd_cnt++; \ + prect = T_C_NONE; \ + } \ + } while (0) // scalar data types encode their precision as a scalar -#define encode_scalar_preamble() ({ \ - encode_width(); \ - if (prect == T_C_DYNAMIC) { \ - encode_precision(OSLF_CMD_TYPE_SCALAR); \ - }}) +#define encode_scalar_preamble() \ + do { \ + encode_width(); \ + if (prect == T_C_DYNAMIC) { \ + encode_precision(OSLF_CMD_TYPE_SCALAR); \ + } \ + } while (0) -#define encode_pointer_preamble() ({ \ - encode_width(); \ - encode_precision(OSLF_CMD_TYPE_COUNT); \ -}) +#define encode_pointer_preamble() \ + do { \ + encode_width(); \ + encode_precision(OSLF_CMD_TYPE_COUNT); \ + } while (0) -#define encode_nsstring(flags) ({ \ - NSString *__arg = va_arg(args, NSString *); \ - const char * _Nullable __var = __arg.UTF8String; \ - cmd.cmd_flags = flags; \ - cmd.cmd_size = sizeof(__var); \ - _os_log_encode_arg(ob, &cmd, &__var); \ - hdr.hdr_cmd_cnt++; \ -}) +#define encode_nsstring(flags) \ + do { \ + NSString *__arg = va_arg(args, NSString *); \ + const char *_Nullable __var = __arg.UTF8String; \ + cmd.cmd_flags = flags; \ + cmd.cmd_size = sizeof(__var); \ + _os_log_encode_arg(ob, &cmd, &__var); \ + hdr.hdr_cmd_cnt++; \ + } while (0) -#define encode_smallint(ty, flags) ({ \ - int __var = va_arg(args, int); \ - cmd.cmd_flags = flags; \ - cmd.cmd_size = sizeof(__var); \ - _os_log_encode_arg(ob, &cmd, &__var); \ - hdr.hdr_cmd_cnt++; \ -}) +#define encode_smallint(ty, flags) \ + do { \ + int __var = va_arg(args, int); \ + cmd.cmd_flags = flags; \ + cmd.cmd_size = sizeof(__var); \ + _os_log_encode_arg(ob, &cmd, &__var); \ + hdr.hdr_cmd_cnt++; \ + } while (0) -#define encode(ty, flags) ({ \ - ty __var = va_arg(args, ty); \ - cmd.cmd_flags = flags; \ - cmd.cmd_size = sizeof(__var); \ - _os_log_encode_arg(ob, &cmd, &__var); \ - hdr.hdr_cmd_cnt++; \ -}) +#define encode(ty, flags) \ + do { \ + ty __var = va_arg(args, ty); \ + cmd.cmd_flags = flags; \ + cmd.cmd_size = sizeof(__var); \ + _os_log_encode_arg(ob, &cmd, &__var); \ + hdr.hdr_cmd_cnt++; \ + } while (0) - /* fixed types */ - case 'c': // char - case 'd': // integer - case 'i': // integer - case 'o': // octal - case 'u': // unsigned - case 'x': // hex - case 'X': // upper-hex - encode_scalar_preamble(); - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; - switch (type) { - case T_CHAR: encode_smallint(char, flags); break; - case T_SHORT: encode_smallint(short, flags); break; - case T_INT: encode(int, flags); break; - case T_LONG: encode(long, flags); break; - case T_LONGLONG: encode(long long, flags); break; - case T_SIZE: encode(size_t, flags); break; - case T_INTMAX: encode(intmax_t, flags); break; - case T_PTRDIFF: encode(ptrdiff_t, flags); break; - default: return false; - } - done = true; + /* fixed types */ + case 'c': // char + case 'd': // integer + case 'i': // integer + case 'o': // octal + case 'u': // unsigned + case 'x': // hex + case 'X': // upper-hex + encode_scalar_preamble(); + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; + switch (type) { + case T_CHAR: + encode_smallint(char, flags); break; - - case 'p': // emit pointers as scalars - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; - encode(void *, flags); - done = true; + case T_SHORT: + encode_smallint(short, flags); break; - - case 'C': // wchar is treated like %lc - encode_scalar_preamble(); - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; - encode_smallint(wint_t, flags); - done = true; + case T_INT: + encode(int, flags); break; - - case 'P': // pointer data - encode_pointer_preamble(); - hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; - cmd.cmd_type = OSLF_CMD_TYPE_DATA; - cmd.cmd_size = sizeof(void *); - encode(void *, flags); - done = true; + case T_LONG: + encode(long, flags); break; - - case 'L': // long double - long_double = true; + case T_LONGLONG: + encode(long long, flags); break; - - case 'a': case 'A': case 'e': case 'E': // floating types - case 'f': case 'F': case 'g': case 'G': - encode_scalar_preamble(); - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; - if (long_double) { - encode(long double, flags); - } else { - encode(double, flags); - } - done = true; + case T_SIZE: + encode(size_t, flags); break; - - case 's': // Strings sent from Swift as NSString objects - encode_pointer_preamble(); - hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; - cmd.cmd_type = OSLF_CMD_TYPE_STRING; - encode_nsstring(flags); - done = true; + case T_INTMAX: + encode(intmax_t, flags); break; - - case '@': // CFTypeRef aka NSObject * - // %@ does not support precision - encode_width(); - hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; - cmd.cmd_type = OSLF_CMD_TYPE_OBJECT; - encode(void *, flags); - done = true; + case T_PTRDIFF: + encode(ptrdiff_t, flags); break; + default: + return false; + } + done = true; + break; - case 'm': - cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; - cmd.cmd_size = sizeof(int); - cmd.cmd_flags = flags; - _os_log_encode_arg(ob, &cmd, &saved_errno); - hdr.hdr_cmd_cnt++; - done = true; - break; + case 'p': // emit pointers as scalars + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; + encode(void *, flags); + done = true; + break; - default: - if (isdigit(ch)) { // [0-9] - continue; - } - done = true; - break; + case 'C': // wchar is treated like %lc + encode_scalar_preamble(); + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; + encode_smallint(wint_t, flags); + done = true; + break; + + case 'P': // pointer data + encode_pointer_preamble(); + hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; + cmd.cmd_type = OSLF_CMD_TYPE_DATA; + cmd.cmd_size = sizeof(void *); + encode(void *, flags); + done = true; + break; + + case 'L': // long double + long_double = true; + break; + + case 'a': + case 'A': + case 'e': + case 'E': // floating types + case 'f': + case 'F': + case 'g': + case 'G': + encode_scalar_preamble(); + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; + if (long_double) { + encode(long double, flags); + } else { + encode(double, flags); + } + done = true; + break; + + case 's': // Strings sent from Swift as NSString objects + encode_pointer_preamble(); + hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; + cmd.cmd_type = OSLF_CMD_TYPE_STRING; + encode_nsstring(flags); + done = true; + break; + + case '@': // CFTypeRef aka NSObject * + // %@ does not support precision + encode_width(); + hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; + cmd.cmd_type = OSLF_CMD_TYPE_OBJECT; + encode(void *, flags); + done = true; + break; + + case 'm': + cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; + cmd.cmd_size = sizeof(int); + cmd.cmd_flags = flags; + _os_log_encode_arg(ob, &cmd, &saved_errno); + hdr.hdr_cmd_cnt++; + done = true; + break; + + default: + if (isdigit(ch)) { // [0-9] + continue; + } + done = true; + break; } if (done) { diff --git a/stdlib/public/Darwin/os/os.m b/stdlib/public/Darwin/os/os.m index 787d90cef31a5..ec3b08c5b3c9c 100644 --- a/stdlib/public/Darwin/os/os.m +++ b/stdlib/public/Darwin/os/os.m @@ -33,7 +33,7 @@ const void *olp_mh; const void *olp_pc; const char *olp_format; - uint8_t olp_data[0]; + uint8_t olp_data[]; } os_log_pack_s, *os_log_pack_t; API_AVAILABLE(macosx(10.12.4), ios(10.3), tvos(10.2), watchos(3.2)) diff --git a/stdlib/public/Differentiation/CMakeLists.txt b/stdlib/public/Differentiation/CMakeLists.txt new file mode 100644 index 0000000000000..d84bf132716e2 --- /dev/null +++ b/stdlib/public/Differentiation/CMakeLists.txt @@ -0,0 +1,18 @@ +#===--- CMakeLists.txt - Differentiable programming support library ------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +#===----------------------------------------------------------------------===# + +add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + Differentiable.swift + + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + INSTALL_IN_COMPONENT stdlib) diff --git a/stdlib/public/Differentiation/Differentiable.swift b/stdlib/public/Differentiation/Differentiable.swift new file mode 100644 index 0000000000000..91c7abcd24522 --- /dev/null +++ b/stdlib/public/Differentiation/Differentiable.swift @@ -0,0 +1,60 @@ +//===--- Differentiable.swift ---------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the Differentiable protocol, used by the experimental +// differentiable programming project. This API is not stable and subject to +// change. +// +// Please see forum discussion for more information about the differentiable +// programming project: +// https://forums.swift.org/t/differentiable-programming-mega-proposal/28547 +// +//===----------------------------------------------------------------------===// + +/// A type that mathematically represents a differentiable manifold whose +/// tangent spaces are finite-dimensional. +public protocol Differentiable { + /// A type representing a differentiable value's derivatives. + /// + /// Mathematically, this is equivalent to the tangent bundle of the + /// differentiable manifold represented by the differentiable type. + associatedtype TangentVector: Differentiable & AdditiveArithmetic + where TangentVector.TangentVector == TangentVector + + /// Moves `self` along the given direction. In Riemannian geometry, this is + /// equivalent to exponential map, which moves `self` on the geodesic surface + /// along the given tangent vector. + mutating func move(along direction: TangentVector) +} + +public extension Differentiable where TangentVector == Self { + @_alwaysEmitIntoClient + mutating func move(along direction: TangentVector) { + self += direction + } +} + +//===----------------------------------------------------------------------===// +// `Differentiable` conformances +//===----------------------------------------------------------------------===// + +extension Float: Differentiable { + public typealias TangentVector = Self +} +extension Double: Differentiable { + public typealias TangentVector = Self +} +#if (arch(i386) || arch(x86_64)) && !(os(Windows) || os(Android)) +extension Float80: Differentiable { + public typealias TangentVector = Self +} +#endif diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 90fba95ec1b4e..841dc6c602d2d 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -22,7 +22,10 @@ add_swift_target_library(swiftDarwin ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_ ${swift_platform_gyb_sources} Darwin.swift.gyb - SWIFT_COMPILE_FLAGS -Xfrontend -disable-objc-attr-requires-foundation-module ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + SWIFT_COMPILE_FLAGS + ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -Xfrontend -disable-objc-attr-requires-foundation-module LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" TARGET_SDKS ALL_APPLE_PLATFORMS INSTALL_IN_COMPONENT sdk-overlay @@ -37,7 +40,9 @@ add_swift_target_library(swiftGlibc ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_O ${swift_platform_gyb_sources} Glibc.swift.gyb - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + SWIFT_COMPILE_FLAGS + ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" TARGET_SDKS ANDROID CYGWIN FREEBSD LINUX HAIKU INSTALL_IN_COMPONENT sdk-overlay @@ -51,7 +56,10 @@ add_swift_target_library(swiftMSVCRT ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_ GYB_SOURCES ${swift_platform_gyb_sources} - SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + SWIFT_COMPILE_FLAGS + ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -Xcc -D_USE_MATH_DEFINES LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" TARGET_SDKS WINDOWS INSTALL_IN_COMPONENT sdk-overlay) diff --git a/stdlib/public/Platform/bionic.modulemap.gyb b/stdlib/public/Platform/bionic.modulemap.gyb index 69564568bffad..e44f9082653a6 100644 --- a/stdlib/public/Platform/bionic.modulemap.gyb +++ b/stdlib/public/Platform/bionic.modulemap.gyb @@ -282,6 +282,10 @@ module SwiftGlibc [system] { module sys { export * + module cdefs { + header "${GLIBC_ARCH_INCLUDE_PATH}/sys/cdefs.h" + export * + } module file { header "${GLIBC_ARCH_INCLUDE_PATH}/sys/file.h" export * diff --git a/stdlib/public/Reflection/CMakeLists.txt b/stdlib/public/Reflection/CMakeLists.txt index 9452f569c3044..bae4f12e2d443 100644 --- a/stdlib/public/Reflection/CMakeLists.txt +++ b/stdlib/public/Reflection/CMakeLists.txt @@ -19,38 +19,9 @@ if (LLVM_ENABLE_ASSERTIONS) "${SWIFT_SOURCE_DIR}/lib/Demangling/NodeDumper.cpp") endif(LLVM_ENABLE_ASSERTIONS) -if(SWIFT_BUILD_STDLIB) - add_swift_target_library(swiftReflection STATIC - ${swiftReflection_SOURCES} - C_COMPILE_FLAGS ${SWIFT_RUNTIME_CXX_FLAGS} -DswiftCore_EXPORTS - LINK_FLAGS ${SWIFT_RUNTIME_LINK_FLAGS} - SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - INSTALL_IN_COMPONENT dev) -endif() - -# Build a specific version for the host with the host toolchain. This is going -# to be used by tools (e.g. lldb) -if(SWIFT_INCLUDE_TOOLS) - if(NOT SWIFT_BUILD_STDLIB) - add_custom_target(swiftReflection-${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}) - endif() - - if(NOT SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER) - set(CURRENT_CMAKE_C_COMPILER ${CMAKE_C_COMPILER}) - set(CURRENT_CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}) - set(CMAKE_C_COMPILER ${HOST_CMAKE_C_COMPILER}) - set(CMAKE_CXX_COMPILER ${HOST_CMAKE_CXX_COMPILER}) - endif() - - add_swift_host_library(swiftReflection STATIC - ${swiftReflection_SOURCES}) - target_compile_options(swiftReflection PRIVATE - ${SWIFT_RUNTIME_CXX_FLAGS}) - set_property(TARGET swiftReflection - APPEND_STRING PROPERTY LINK_FLAGS ${SWIFT_RUNTIME_LINK_FLAGS}) - - if(NOT SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER) - set(CMAKE_C_COMPILER ${CURRENT_CMAKE_C_COMPILER}) - set(CMAKE_CXX_COMPILER ${CURRENT_CMAKE_CXX_COMPILER}) - endif() -endif() +add_swift_target_library(swiftReflection STATIC + ${swiftReflection_SOURCES} + C_COMPILE_FLAGS ${SWIFT_RUNTIME_CXX_FLAGS} -DswiftCore_EXPORTS + LINK_FLAGS ${SWIFT_RUNTIME_LINK_FLAGS} + SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + INSTALL_IN_COMPONENT dev) diff --git a/stdlib/public/Reflection/MetadataSource.cpp b/stdlib/public/Reflection/MetadataSource.cpp index 7278c53c776ee..6ca4ad7859e04 100644 --- a/stdlib/public/Reflection/MetadataSource.cpp +++ b/stdlib/public/Reflection/MetadataSource.cpp @@ -19,31 +19,30 @@ using namespace reflection; class PrintMetadataSource : public MetadataSourceVisitor { - std::ostream &OS; + FILE *file; unsigned Indent; - std::ostream &indent(unsigned Amount) { + FILE * indent(unsigned Amount) { for (unsigned i = 0; i < Amount; ++i) - OS << ' '; - return OS; + fprintf(file, " "); + return file; } - std::ostream &printHeader(std::string Name) { - indent(Indent) << '(' << Name; - return OS; + FILE * printHeader(std::string Name) { + fprintf(indent(Indent), "(%s", Name.c_str()); + return file; } - template - std::ostream &printField(std::string name, const T &value) { + FILE * printField(std::string name, std::string value) { if (!name.empty()) - OS << " " << name << "=" << value; + fprintf(file, " %s=%s", name.c_str(), value.c_str()); else - OS << " " << value; - return OS; + fprintf(file, " %s", value.c_str()); + return file; } void printRec(const MetadataSource *MS) { - OS << "\n"; + fprintf(file, "\n"); Indent += 2; visit(MS); @@ -51,38 +50,38 @@ class PrintMetadataSource } void closeForm() { - OS << ')'; + fprintf(file, ")"); } public: - PrintMetadataSource(std::ostream &OS, unsigned Indent) - : OS(OS), Indent(Indent) {} + PrintMetadataSource(FILE *file, unsigned Indent) + : file(file), Indent(Indent) {} void visitClosureBindingMetadataSource(const ClosureBindingMetadataSource *CB) { printHeader("closure_binding"); - printField("index", CB->getIndex()); + printField("index", std::to_string(CB->getIndex())); closeForm(); } void visitReferenceCaptureMetadataSource(const ReferenceCaptureMetadataSource *RC){ printHeader("reference_capture"); - printField("index", RC->getIndex()); + printField("index", std::to_string(RC->getIndex())); closeForm(); } void visitMetadataCaptureMetadataSource(const MetadataCaptureMetadataSource *MC){ printHeader("metadata_capture"); - printField("index", MC->getIndex()); + printField("index", std::to_string(MC->getIndex())); closeForm(); } void visitGenericArgumentMetadataSource(const GenericArgumentMetadataSource *GA) { printHeader("generic_argument"); - printField("index", GA->getIndex()); + printField("index", std::to_string(GA->getIndex())); printRec(GA->getSource()); closeForm(); } @@ -100,10 +99,10 @@ class PrintMetadataSource }; void MetadataSource::dump() const { - dump(std::cerr, 0); + dump(stderr, 0); } -void MetadataSource::dump(std::ostream &OS, unsigned Indent) const { - PrintMetadataSource(OS, Indent).visit(this); - OS << '\n'; +void MetadataSource::dump(FILE *file, unsigned Indent) const { + PrintMetadataSource(file, Indent).visit(this); + fprintf(file, "\n"); } diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index 4e20f197e5afa..c93afa1d25067 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -24,8 +24,6 @@ #include "swift/Reflection/TypeRefBuilder.h" #include "swift/Runtime/Unreachable.h" -#include - #ifdef DEBUG_TYPE_LOWERING #define DEBUG_LOG(expr) expr; #else @@ -36,37 +34,36 @@ namespace swift { namespace reflection { void TypeInfo::dump() const { - dump(std::cerr); + dump(stderr); } namespace { class PrintTypeInfo { - std::ostream &OS; + FILE *file; unsigned Indent; - std::ostream &indent(unsigned Amount) { + FILE * &indent(unsigned Amount) { for (unsigned i = 0; i < Amount; ++i) - OS << ' '; - return OS; + fprintf(file, " "); + return file; } - std::ostream &printHeader(const std::string &name) { - indent(Indent) << '(' << name; - return OS; + FILE * &printHeader(const std::string &name) { + fprintf(indent(Indent), "(%s", name.c_str()); + return file; } - template - std::ostream &printField(const std::string &name, const T &value) { + FILE * &printField(const std::string &name, const std::string &value) { if (!name.empty()) - OS << " " << name << "=" << value; + fprintf(file, " %s=%s", name.c_str(), value.c_str()); else - OS << " " << value; - return OS; + fprintf(file, " %s", value.c_str()); + return file; } void printRec(const TypeInfo &TI) { - OS << "\n"; + fprintf(file, "\n"); Indent += 2; print(TI); @@ -74,37 +71,37 @@ class PrintTypeInfo { } void printBasic(const TypeInfo &TI) { - printField("size", TI.getSize()); - printField("alignment", TI.getAlignment()); - printField("stride", TI.getStride()); - printField("num_extra_inhabitants", TI.getNumExtraInhabitants()); - printField("bitwise_takable", TI.isBitwiseTakable()); + printField("size", std::to_string(TI.getSize())); + printField("alignment", std::to_string(TI.getAlignment())); + printField("stride", std::to_string(TI.getStride())); + printField("num_extra_inhabitants", std::to_string(TI.getNumExtraInhabitants())); + printField("bitwise_takable", TI.isBitwiseTakable() ? "1" : "0"); } void printFields(const RecordTypeInfo &TI) { Indent += 2; for (auto Field : TI.getFields()) { - OS << "\n"; + fprintf(file, "\n"); printHeader("field"); if (!Field.Name.empty()) printField("name", Field.Name); - printField("offset", Field.Offset); + printField("offset", std::to_string(Field.Offset)); printRec(Field.TI); - OS << ")"; + fprintf(file, ")"); } Indent -= 2; } public: - PrintTypeInfo(std::ostream &OS, unsigned Indent) - : OS(OS), Indent(Indent) {} + PrintTypeInfo(FILE *file, unsigned Indent) + : file(file), Indent(Indent) {} void print(const TypeInfo &TI) { switch (TI.getKind()) { case TypeInfoKind::Builtin: printHeader("builtin"); printBasic(TI); - OS << ")"; + fprintf(file, ")"); return; case TypeInfoKind::Record: { @@ -152,7 +149,7 @@ class PrintTypeInfo { } printBasic(TI); printFields(RecordTI); - OS << ")"; + fprintf(file, ")"); return; } @@ -175,7 +172,7 @@ class PrintTypeInfo { break; } - OS << ")"; + fprintf(file, ")"); return; } } @@ -186,9 +183,9 @@ class PrintTypeInfo { } // end anonymous namespace -void TypeInfo::dump(std::ostream &OS, unsigned Indent) const { - PrintTypeInfo(OS, Indent).print(*this); - OS << '\n'; +void TypeInfo::dump(FILE *file, unsigned Indent) const { + PrintTypeInfo(file, Indent).print(*this); + fprintf(file, "\n"); } BuiltinTypeInfo::BuiltinTypeInfo(TypeRefBuilder &builder, @@ -245,7 +242,7 @@ class ExistentialTypeInfoBuilder { auto *NTD = dyn_cast(P); auto *OP = dyn_cast(P); if (!NTD && !OP) { - DEBUG_LOG(std::cerr << "Bad protocol: "; P->dump()) + DEBUG_LOG(fprintf(stderr, "Bad protocol: "); P->dump()) Invalid = true; continue; } @@ -258,7 +255,7 @@ class ExistentialTypeInfoBuilder { auto FD = TC.getBuilder().getFieldTypeInfo(P); if (FD == nullptr) { - DEBUG_LOG(std::cerr << "No field descriptor: "; P->dump()) + DEBUG_LOG(fprintf(stderr, "No field descriptor: "); P->dump()) Invalid = true; continue; } @@ -275,14 +272,14 @@ class ExistentialTypeInfoBuilder { if (auto *Superclass = TC.getBuilder().lookupSuperclass(P)) { auto *SuperclassTI = TC.getTypeInfo(Superclass); if (SuperclassTI == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for superclass: "; + DEBUG_LOG(fprintf(stderr, "No TypeInfo for superclass: "); Superclass->dump()); Invalid = true; continue; } if (!isa(SuperclassTI)) { - DEBUG_LOG(std::cerr << "Superclass not a reference type: "; + DEBUG_LOG(fprintf(stderr, "Superclass not a reference type: ") SuperclassTI->dump()); Invalid = true; continue; @@ -334,7 +331,7 @@ class ExistentialTypeInfoBuilder { if (!isa(T) && !isa(T) && !isa(T)) { - DEBUG_LOG(std::cerr << "Bad existential member: "; T->dump()) + DEBUG_LOG(fprintf(stderr, "Bad existential member: "); T->dump()) Invalid = true; return; } @@ -347,7 +344,7 @@ class ExistentialTypeInfoBuilder { const auto &FD = TC.getBuilder().getFieldTypeInfo(T); if (FD == nullptr) { - DEBUG_LOG(std::cerr << "No field descriptor: "; T->dump()) + DEBUG_LOG(fprintf(stderr, "No field descriptor: "); T->dump()) Invalid = true; return; } @@ -364,7 +361,7 @@ class ExistentialTypeInfoBuilder { break; default: - DEBUG_LOG(std::cerr << "Bad existential member: "; T->dump()) + DEBUG_LOG(fprintf(stderr, "Bad existential member: "); T->dump()) Invalid = true; return; } @@ -387,7 +384,7 @@ class ExistentialTypeInfoBuilder { if (ObjC) { if (WitnessTableCount > 0) { - DEBUG_LOG(std::cerr << "@objc existential with witness tables\n"); + DEBUG_LOG(fprintf(stderr, "@objc existential with witness tables\n")); return nullptr; } @@ -422,7 +419,7 @@ class ExistentialTypeInfoBuilder { case ExistentialTypeRepresentation::Opaque: { auto *TI = TC.getTypeInfo(TC.getRawPointerTypeRef()); if (TI == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for RawPointer\n"); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for RawPointer\n")); return nullptr; } @@ -456,7 +453,7 @@ class ExistentialTypeInfoBuilder { if (ObjC) { if (WitnessTableCount > 0) { - DEBUG_LOG(std::cerr << "@objc existential with witness tables\n"); + DEBUG_LOG(fprintf(stderr, "@objc existential with witness tables\n")); return nullptr; } @@ -531,7 +528,7 @@ void RecordTypeInfoBuilder::addField(const std::string &Name, const TypeRef *TR) { const TypeInfo *TI = TC.getTypeInfo(TR); if (TI == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for field type: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for field type: "); TR->dump()); Invalid = true; return; } @@ -583,7 +580,7 @@ TypeConverter::getReferenceTypeInfo(ReferenceKind Kind, auto BuiltinTI = Builder.getBuiltinTypeInfo(TR); if (BuiltinTI == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for reference type: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for reference type: "); TR->dump()); return nullptr; } @@ -624,7 +621,7 @@ TypeConverter::getThinFunctionTypeInfo() { auto descriptor = getBuilder().getBuiltinTypeInfo(getThinFunctionTypeRef()); if (descriptor == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for function type\n"); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for function type\n")); return nullptr; } @@ -659,7 +656,7 @@ TypeConverter::getAnyMetatypeTypeInfo() { auto descriptor = getBuilder().getBuiltinTypeInfo(getAnyMetatypeTypeRef()); if (descriptor == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for metatype type\n"); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for metatype type\n")); return nullptr; } @@ -914,13 +911,13 @@ class HasSingletonMetatype MetatypeRepresentation visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP) { - DEBUG_LOG(std::cerr << "Unresolved generic TypeRef: "; GTP->dump()); + DEBUG_LOG(fprintf(stderr, "Unresolved generic TypeRef: "); GTP->dump()); return MetatypeRepresentation::Unknown; } MetatypeRepresentation visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { - DEBUG_LOG(std::cerr << "Unresolved generic TypeRef: "; DM->dump()); + DEBUG_LOG(fprintf(stderr, "Unresolved generic TypeRef: "); DM->dump()); return MetatypeRepresentation::Unknown; } @@ -969,7 +966,7 @@ class EnumTypeInfoBuilder { void addCase(const std::string &Name, const TypeRef *TR, const TypeInfo *TI) { if (TI == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for case type: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for case type: "); TR->dump()); Invalid = true; return; } @@ -1010,10 +1007,22 @@ class EnumTypeInfoBuilder { // NoPayloadEnumImplStrategy if (PayloadCases.empty()) { Kind = RecordKind::NoPayloadEnum; - Size += getEnumTagCounts(/*size=*/0, - NoPayloadCases, - /*payloadCases=*/0).numTagBytes; - + switch (NoPayloadCases) { + case 0: + case 1: // Zero or one tag has size = 0, extra_inhab = 0 + NumExtraInhabitants = 0; + break; + default: { // 2 or more tags + auto tagCounts = getEnumTagCounts(/*size=*/0, + NoPayloadCases, + /*payloadCases=*/0); + Size += tagCounts.numTagBytes; + NumExtraInhabitants = + (1 << (tagCounts.numTagBytes * 8)) - tagCounts.numTags; + NumExtraInhabitants = std::min(NumExtraInhabitants, + unsigned(ValueWitnessFlags::MaxNumExtraInhabitants)); + } + } // SinglePayloadEnumImplStrategy } else if (PayloadCases.size() == 1) { auto *CaseTR = getCaseTypeRef(PayloadCases[0]); @@ -1125,7 +1134,7 @@ class LowerType /// metadata. auto descriptor = TC.getBuilder().getBuiltinTypeInfo(B); if (descriptor == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for builtin type: "; B->dump()); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for builtin type: "); B->dump()); return nullptr; } return TC.makeTypeInfo(TC.getBuilder(), descriptor); @@ -1143,7 +1152,7 @@ class LowerType // Otherwise, we're out of luck. if (FD == nullptr) { - DEBUG_LOG(std::cerr << "No TypeInfo for nominal type: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "No TypeInfo for nominal type: "); TR->dump()); return nullptr; } } @@ -1177,7 +1186,7 @@ class LowerType case FieldDescriptorKind::ObjCProtocol: case FieldDescriptorKind::ClassProtocol: case FieldDescriptorKind::Protocol: - DEBUG_LOG(std::cerr << "Invalid field descriptor: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "Invalid field descriptor: "); TR->dump()); return nullptr; } @@ -1225,7 +1234,7 @@ class LowerType const TypeInfo *visitMetatypeTypeRef(const MetatypeTypeRef *M) { switch (HasSingletonMetatype().visit(M)) { case MetatypeRepresentation::Unknown: - DEBUG_LOG(std::cerr << "Unknown metatype representation: "; M->dump()); + DEBUG_LOG(fprintf(stderr, "Unknown metatype representation: "); M->dump()); return nullptr; case MetatypeRepresentation::Thin: return TC.getEmptyTypeInfo(); @@ -1244,7 +1253,7 @@ class LowerType if (auto *PC = dyn_cast(TR)) { builder.addProtocolComposition(PC); } else { - DEBUG_LOG(std::cerr << "Invalid existential metatype: "; EM->dump()); + DEBUG_LOG(fprintf(stderr, "Invalid existential metatype: "); EM->dump()); return nullptr; } @@ -1253,13 +1262,13 @@ class LowerType const TypeInfo * visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP) { - DEBUG_LOG(std::cerr << "Unresolved generic TypeRef: "; GTP->dump()); + DEBUG_LOG(fprintf(stderr, "Unresolved generic TypeRef: "); GTP->dump()); return nullptr; } const TypeInfo * visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { - DEBUG_LOG(std::cerr << "Unresolved generic TypeRef: "; DM->dump()); + DEBUG_LOG(fprintf(stderr, "Unresolved generic TypeRef: "); DM->dump()); return nullptr; } @@ -1285,7 +1294,7 @@ class LowerType rebuildStorageTypeInfo(const TypeInfo *TI, ReferenceKind Kind) { // If we can't lower the original storage type, give up. if (TI == nullptr) { - DEBUG_LOG(std::cerr << "Invalid reference type"); + DEBUG_LOG(fprintf(stderr, "Invalid reference type")); return nullptr; } @@ -1331,7 +1340,7 @@ class LowerType } // Anything else -- give up - DEBUG_LOG(std::cerr << "Invalid reference type"); + DEBUG_LOG(fprintf(stderr, "Invalid reference type")); return nullptr; } @@ -1353,7 +1362,7 @@ class LowerType } const TypeInfo *visitOpaqueTypeRef(const OpaqueTypeRef *O) { - DEBUG_LOG(std::cerr << "Can't lower opaque TypeRef"); + DEBUG_LOG(fprintf(stderr, "Can't lower opaque TypeRef")); return nullptr; } }; @@ -1367,7 +1376,7 @@ const TypeInfo *TypeConverter::getTypeInfo(const TypeRef *TR) { // Detect invalid recursive value types (IRGen should not emit // them in the first place, but there might be bugs) if (!RecursionCheck.insert(TR).second) { - DEBUG_LOG(std::cerr << "TypeRef recursion detected"); + DEBUG_LOG(fprintf(stderr, "TypeRef recursion detected")); return nullptr; } @@ -1384,7 +1393,7 @@ const TypeInfo *TypeConverter::getClassInstanceTypeInfo(const TypeRef *TR, unsigned start) { auto FD = getBuilder().getFieldTypeInfo(TR); if (FD == nullptr) { - DEBUG_LOG(std::cerr << "No field descriptor: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "No field descriptor: ";) TR->dump()); return nullptr; } @@ -1417,7 +1426,7 @@ const TypeInfo *TypeConverter::getClassInstanceTypeInfo(const TypeRef *TR, case FieldDescriptorKind::ClassProtocol: case FieldDescriptorKind::Protocol: // Invalid field descriptor. - DEBUG_LOG(std::cerr << "Invalid field descriptor: "; TR->dump()); + DEBUG_LOG(fprintf(stderr, "Invalid field descriptor: "); TR->dump()); return nullptr; } diff --git a/stdlib/public/Reflection/TypeRef.cpp b/stdlib/public/Reflection/TypeRef.cpp index 28a92394d9a9a..3503dd1e79e91 100644 --- a/stdlib/public/Reflection/TypeRef.cpp +++ b/stdlib/public/Reflection/TypeRef.cpp @@ -24,31 +24,30 @@ using namespace swift; using namespace reflection; class PrintTypeRef : public TypeRefVisitor { - std::ostream &OS; + FILE *file; unsigned Indent; - std::ostream &indent(unsigned Amount) { + FILE * &indent(unsigned Amount) { for (unsigned i = 0; i < Amount; ++i) - OS << ' '; - return OS; + fprintf(file, " "); + return file; } - std::ostream &printHeader(std::string Name) { - indent(Indent) << '(' << Name; - return OS; + FILE * &printHeader(std::string Name) { + fprintf(indent(Indent), "(%s", Name.c_str()); + return file; } - template - std::ostream &printField(std::string name, const T &value) { + FILE * &printField(std::string name, std::string value) { if (!name.empty()) - OS << " " << name << "=" << value; + fprintf(file, " %s=%s", name.c_str(), value.c_str()); else - OS << " " << value; - return OS; + fprintf(file, " %s", value.c_str()); + return file; } void printRec(const TypeRef *typeRef) { - OS << "\n"; + fprintf(file, "\n"); Indent += 2; visit(typeRef); @@ -56,14 +55,14 @@ class PrintTypeRef : public TypeRefVisitor { } public: - PrintTypeRef(std::ostream &OS, unsigned Indent) - : OS(OS), Indent(Indent) {} + PrintTypeRef(FILE *file, unsigned Indent) + : file(file), Indent(Indent) {} void visitBuiltinTypeRef(const BuiltinTypeRef *B) { printHeader("builtin"); auto demangled = Demangle::demangleTypeAsString(B->getMangledName()); printField("", demangled); - OS << ')'; + fprintf(file, ")"); } void visitNominalTypeRef(const NominalTypeRef *N) { @@ -86,7 +85,7 @@ class PrintTypeRef : public TypeRefVisitor { printField("", demangled); if (auto parent = N->getParent()) printRec(parent); - OS << ')'; + fprintf(file, ")"); } void visitBoundGenericTypeRef(const BoundGenericTypeRef *BG) { @@ -105,14 +104,14 @@ class PrintTypeRef : public TypeRefVisitor { printRec(param); if (auto parent = BG->getParent()) printRec(parent); - OS << ')'; + fprintf(file, ")"); } void visitTupleTypeRef(const TupleTypeRef *T) { printHeader("tuple"); for (auto element : T->getElements()) printRec(element); - OS << ')'; + fprintf(file, ")"); } void visitFunctionTypeRef(const FunctionTypeRef *F) { @@ -132,7 +131,7 @@ class PrintTypeRef : public TypeRefVisitor { break; } - OS << '\n'; + fprintf(file, "\n"); Indent += 2; printHeader("parameters"); @@ -142,7 +141,7 @@ class PrintTypeRef : public TypeRefVisitor { if (!flags.isNone()) { Indent += 2; - OS << '\n'; + fprintf(file, "\n"); } switch (flags.getValueOwnership()) { @@ -167,17 +166,17 @@ class PrintTypeRef : public TypeRefVisitor { if (!flags.isNone()) { Indent -= 2; - OS << ')'; + fprintf(file, ")"); } } if (parameters.empty()) - OS << ')'; + fprintf(file, ")"); - OS << '\n'; + fprintf(file, "\n"); printHeader("result"); printRec(F->getResult()); - OS << ')'; + fprintf(file, ")"); Indent -= 2; } @@ -185,12 +184,12 @@ class PrintTypeRef : public TypeRefVisitor { void visitProtocolCompositionTypeRef(const ProtocolCompositionTypeRef *PC) { printHeader("protocol_composition"); if (PC->hasExplicitAnyObject()) - OS << " any_object"; + fprintf(file, " any_object"); if (auto superclass = PC->getSuperclass()) printRec(superclass); for (auto protocol : PC->getProtocols()) printRec(protocol); - OS << ')'; + fprintf(file, ")"); } void visitMetatypeTypeRef(const MetatypeTypeRef *M) { @@ -198,20 +197,20 @@ class PrintTypeRef : public TypeRefVisitor { if (M->wasAbstract()) printField("", "was_abstract"); printRec(M->getInstanceType()); - OS << ')'; + fprintf(file, ")"); } void visitExistentialMetatypeTypeRef(const ExistentialMetatypeTypeRef *EM) { printHeader("existential_metatype"); printRec(EM->getInstanceType()); - OS << ')'; + fprintf(file, ")"); } void visitGenericTypeParameterTypeRef(const GenericTypeParameterTypeRef *GTP){ printHeader("generic_type_parameter"); - printField("depth", GTP->getDepth()); - printField("index", GTP->getIndex()); - OS << ')'; + printField("depth", std::to_string(GTP->getDepth())); + printField("index", std::to_string(GTP->getIndex())); + fprintf(file, ")"); } void visitDependentMemberTypeRef(const DependentMemberTypeRef *DM) { @@ -219,47 +218,47 @@ class PrintTypeRef : public TypeRefVisitor { printField("protocol", DM->getProtocol()); printRec(DM->getBase()); printField("member", DM->getMember()); - OS << ')'; + fprintf(file, ")"); } void visitForeignClassTypeRef(const ForeignClassTypeRef *F) { printHeader("foreign"); if (!F->getName().empty()) printField("name", F->getName()); - OS << ')'; + fprintf(file, ")"); } void visitObjCClassTypeRef(const ObjCClassTypeRef *OC) { printHeader("objective_c_class"); if (!OC->getName().empty()) printField("name", OC->getName()); - OS << ')'; + fprintf(file, ")"); } void visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OC) { printHeader("objective_c_protocol"); if (!OC->getName().empty()) printField("name", OC->getName()); - OS << ')'; + fprintf(file, ")"); } #define REF_STORAGE(Name, name, ...) \ void visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \ printHeader(#name "_storage"); \ printRec(US->getType()); \ - OS << ')'; \ + fprintf(file, ")"); \ } #include "swift/AST/ReferenceStorage.def" void visitSILBoxTypeRef(const SILBoxTypeRef *SB) { printHeader("sil_box"); printRec(SB->getBoxedType()); - OS << ')'; + fprintf(file, ")"); } void visitOpaqueTypeRef(const OpaqueTypeRef *O) { printHeader("opaque"); - OS << ')'; + fprintf(file, ")"); } }; @@ -369,12 +368,12 @@ const OpaqueTypeRef *OpaqueTypeRef::get() { } void TypeRef::dump() const { - dump(std::cerr); + dump(stderr); } -void TypeRef::dump(std::ostream &OS, unsigned Indent) const { - PrintTypeRef(OS, Indent).visit(this); - OS << std::endl; +void TypeRef::dump(FILE *file, unsigned Indent) const { + PrintTypeRef(file, Indent).visit(this); + fprintf(file, "\n"); } bool TypeRef::isConcrete() const { diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 5a609fa483d4e..b2e2a7d27afbd 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -44,8 +44,9 @@ RemoteRef TypeRefBuilder::readTypeRef(uint64_t remoteAddr) { } } // TODO: Try using MetadataReader to read the string here? - fputs("invalid type ref pointer\n", stderr); - abort(); + + // Invalid type ref pointer. + return nullptr; found_type_ref: // Make sure there's a valid mangled string within the bounds of the @@ -57,16 +58,16 @@ RemoteRef TypeRefBuilder::readTypeRef(uint64_t remoteAddr) { goto valid_type_ref; if (c >= '\1' && c <= '\x17') - i = i.atByteOffset(4); + i = i.atByteOffset(5); else if (c >= '\x18' && c <= '\x1F') { - i = i.atByteOffset(PointerSize); + i = i.atByteOffset(PointerSize + 1); } else { i = i.atByteOffset(1); } } - fputs("unterminated type ref\n", stderr); - abort(); + // Unterminated string. + return nullptr; valid_type_ref: // Look past the $s prefix if the string has one. @@ -252,11 +253,18 @@ TypeRefBuilder::getBuiltinTypeInfo(const TypeRef *TR) { for (auto Info : ReflectionInfos) { for (auto BuiltinTypeDescriptor : Info.Builtin) { - assert(BuiltinTypeDescriptor->Size > 0); - assert(BuiltinTypeDescriptor->getAlignment() > 0); - assert(BuiltinTypeDescriptor->Stride > 0); + if (BuiltinTypeDescriptor->Stride <= 0) + continue; if (!BuiltinTypeDescriptor->hasMangledTypeName()) continue; + + auto Alignment = BuiltinTypeDescriptor->getAlignment(); + if (Alignment <= 0) + continue; + // Reject any alignment that's not a power of two. + if (Alignment & (Alignment - 1)) + continue; + auto CandidateMangledName = readTypeRef(BuiltinTypeDescriptor, BuiltinTypeDescriptor->TypeName); if (!reflectionNameMatches(CandidateMangledName, MangledName)) @@ -329,48 +337,46 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { void TypeRefBuilder::dumpTypeRef(RemoteRef MangledName, - std::ostream &OS, bool printTypeName) { + FILE *file, bool printTypeName) { auto DemangleTree = demangleTypeRef(MangledName); auto TypeName = nodeToString(DemangleTree); - OS << TypeName << '\n'; + fprintf(file, "%s\n", TypeName.c_str()); auto TR = swift::Demangle::decodeMangledType(*this, DemangleTree); if (!TR) { auto str = getTypeRefString(MangledName); - OS << "!!! Invalid typeref: " - << std::string(str.begin(), str.end()) - << '\n'; + fprintf(file, "!!! Invalid typeref: %s\n", std::string(str.begin(), str.end()).c_str()); return; } - TR->dump(OS); - OS << '\n'; + TR->dump(file); + fprintf(file, "\n"); } -void TypeRefBuilder::dumpFieldSection(std::ostream &OS) { +void TypeRefBuilder::dumpFieldSection(FILE *file) { for (const auto §ions : ReflectionInfos) { for (auto descriptor : sections.Field) { auto TypeDemangling = demangleTypeRef(readTypeRef(descriptor, descriptor->MangledTypeName)); auto TypeName = nodeToString(TypeDemangling); - OS << TypeName << '\n'; + fprintf(file, "%s\n", TypeName.c_str()); for (size_t i = 0; i < TypeName.size(); ++i) - OS << '-'; - OS << '\n'; + fprintf(file, "-"); + fprintf(file, "\n"); for (auto &fieldRef : *descriptor.getLocalBuffer()) { auto field = descriptor.getField(fieldRef); auto fieldName = getTypeRefString(readTypeRef(field, field->FieldName)); - OS << std::string(fieldName.begin(), fieldName.end()); + fprintf(file, "%*s", (int)fieldName.size(), fieldName.data()); if (field->hasMangledTypeName()) { - OS << ": "; - dumpTypeRef(readTypeRef(field, field->MangledTypeName), OS); + fprintf(file, ": "); + dumpTypeRef(readTypeRef(field, field->MangledTypeName), file); } else { - OS << "\n\n"; + fprintf(file, "\n\n"); } } } } } -void TypeRefBuilder::dumpAssociatedTypeSection(std::ostream &OS) { +void TypeRefBuilder::dumpAssociatedTypeSection(FILE *file) { for (const auto §ions : ReflectionInfos) { for (auto descriptor : sections.AssociatedType) { auto conformingTypeNode = demangleTypeRef( @@ -380,89 +386,89 @@ void TypeRefBuilder::dumpAssociatedTypeSection(std::ostream &OS) { readTypeRef(descriptor, descriptor->ProtocolTypeName)); auto protocolName = nodeToString(protocolNode); - OS << "- " << conformingTypeName << " : " << protocolName; - OS << '\n'; + fprintf(file, "- %s : %s", conformingTypeName.c_str(), protocolName.c_str()); + fprintf(file, "\n"); for (const auto &associatedTypeRef : *descriptor.getLocalBuffer()) { auto associatedType = descriptor.getField(associatedTypeRef); std::string name = getTypeRefString( readTypeRef(associatedType, associatedType->Name)); - OS << "typealias " << name << " = "; + fprintf(file, "typealias %s = ", name.c_str()); dumpTypeRef( - readTypeRef(associatedType, associatedType->SubstitutedTypeName), OS); + readTypeRef(associatedType, associatedType->SubstitutedTypeName), file); } } } } -void TypeRefBuilder::dumpBuiltinTypeSection(std::ostream &OS) { +void TypeRefBuilder::dumpBuiltinTypeSection(FILE *file) { for (const auto §ions : ReflectionInfos) { for (auto descriptor : sections.Builtin) { auto typeNode = demangleTypeRef(readTypeRef(descriptor, descriptor->TypeName)); auto typeName = nodeToString(typeNode); - OS << "\n- " << typeName << ":\n"; - OS << "Size: " << descriptor->Size << "\n"; - OS << "Alignment: " << descriptor->getAlignment() << "\n"; - OS << "Stride: " << descriptor->Stride << "\n"; - OS << "NumExtraInhabitants: " << descriptor->NumExtraInhabitants << "\n"; - OS << "BitwiseTakable: " << descriptor->isBitwiseTakable() << "\n"; + fprintf(file, "\n- %s:\n", typeName.c_str()); + fprintf(file, "Size: %u\n", descriptor->Size); + fprintf(file, "Alignment: %u:\n", descriptor->getAlignment()); + fprintf(file, "Stride: %u:\n", descriptor->Stride); + fprintf(file, "NumExtraInhabitants: %u:\n", descriptor->NumExtraInhabitants); + fprintf(file, "BitwiseTakable: %d:\n", descriptor->isBitwiseTakable()); } } } void ClosureContextInfo::dump() const { - dump(std::cerr); + dump(stderr); } -void ClosureContextInfo::dump(std::ostream &OS) const { - OS << "- Capture types:\n"; +void ClosureContextInfo::dump(FILE *file) const { + fprintf(file, "- Capture types:\n"); for (auto *TR : CaptureTypes) { if (TR == nullptr) - OS << "!!! Invalid typeref\n"; + fprintf(file, "!!! Invalid typeref\n"); else - TR->dump(OS); + TR->dump(file); } - OS << "- Metadata sources:\n"; + fprintf(file, "- Metadata sources:\n"); for (auto MS : MetadataSources) { if (MS.first == nullptr) - OS << "!!! Invalid typeref\n"; + fprintf(file, "!!! Invalid typeref\n"); else - MS.first->dump(OS); + MS.first->dump(file); if (MS.second == nullptr) - OS << "!!! Invalid metadata source\n"; + fprintf(file, "!!! Invalid metadata source\n"); else - MS.second->dump(OS); + MS.second->dump(file); } - OS << "\n"; + fprintf(file, "\n"); } -void TypeRefBuilder::dumpCaptureSection(std::ostream &OS) { +void TypeRefBuilder::dumpCaptureSection(FILE *file) { for (const auto §ions : ReflectionInfos) { - for (const auto &descriptor : sections.Capture) { + for (const auto descriptor : sections.Capture) { auto info = getClosureContextInfo(descriptor); - info.dump(OS); + info.dump(file); } } } -void TypeRefBuilder::dumpAllSections(std::ostream &OS) { - OS << "FIELDS:\n"; - OS << "=======\n"; - dumpFieldSection(OS); - OS << '\n'; - OS << "ASSOCIATED TYPES:\n"; - OS << "=================\n"; - dumpAssociatedTypeSection(OS); - OS << '\n'; - OS << "BUILTIN TYPES:\n"; - OS << "==============\n"; - dumpBuiltinTypeSection(OS); - OS << '\n'; - OS << "CAPTURE DESCRIPTORS:\n"; - OS << "====================\n"; - dumpCaptureSection(OS); - OS << '\n'; +void TypeRefBuilder::dumpAllSections(FILE *file) { + fprintf(file, "FIELDS:\n"); + fprintf(file, "=======\n"); + dumpFieldSection(file); + fprintf(file, "\n"); + fprintf(file, "ASSOCIATED TYPES:\n"); + fprintf(file, "=================\n"); + dumpAssociatedTypeSection(file); + fprintf(file, "\n"); + fprintf(file, "BUILTIN TYPES:\n"); + fprintf(file, "==============\n"); + dumpBuiltinTypeSection(file); + fprintf(file, "\n"); + fprintf(file, "CAPTURE DESCRIPTORS:\n"); + fprintf(file, "====================\n"); + dumpCaptureSection(file); + fprintf(file, "\n"); } diff --git a/stdlib/public/SwiftRemoteMirror/CMakeLists.txt b/stdlib/public/SwiftRemoteMirror/CMakeLists.txt index addc756efac50..87288662e328b 100644 --- a/stdlib/public/SwiftRemoteMirror/CMakeLists.txt +++ b/stdlib/public/SwiftRemoteMirror/CMakeLists.txt @@ -14,31 +14,3 @@ if(SWIFT_BUILD_DYNAMIC_STDLIB) INSTALL_IN_COMPONENT swift-remote-mirror) endif() - -# Build a specific version for the host with the host toolchain. This is going -# to be used by tools (e.g. lldb) -if(SWIFT_INCLUDE_TOOLS) - if(NOT SWIFT_BUILD_DYNAMIC_STDLIB) - add_custom_target(swiftRemoteMirror-${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}) - endif() - - if(NOT SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER) - set(CURRENT_CMAKE_C_COMPILER ${CMAKE_C_COMPILER}) - set(CURRENT_CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}) - set(CMAKE_C_COMPILER ${HOST_CMAKE_C_COMPILER}) - set(CMAKE_CXX_COMPILER ${HOST_CMAKE_CXX_COMPILER}) - endif() - - add_swift_host_library(swiftRemoteMirror STATIC - SwiftRemoteMirror.cpp) - target_compile_definitions(swiftRemoteMirror PRIVATE _LIB) - target_compile_options(swiftRemoteMirror PRIVATE - ${SWIFT_RUNTIME_CXX_FLAGS}) - set_property(TARGET swiftRemoteMirror APPEND_STRING PROPERTY LINK_FLAGS - ${SWIFT_RUNTIME_LINK_FLAGS}) - - if(NOT SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER) - set(CMAKE_C_COMPILER ${CURRENT_CMAKE_C_COMPILER}) - set(CMAKE_CXX_COMPILER ${CURRENT_CMAKE_CXX_COMPILER}) - endif() -endif() diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 7bf0cdf0392f9..9cfda37a6c4b9 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -416,9 +416,9 @@ int swift_reflection_projectExistential(SwiftReflectionContextRef ContextRef, void swift_reflection_dumpTypeRef(swift_typeref_t OpaqueTypeRef) { auto TR = reinterpret_cast(OpaqueTypeRef); if (TR == nullptr) { - std::cout << "\n"; + fprintf(stdout, "\n"); } else { - TR->dump(std::cout); + TR->dump(stdout); } } @@ -428,9 +428,9 @@ void swift_reflection_dumpInfoForTypeRef(SwiftReflectionContextRef ContextRef, auto TR = reinterpret_cast(OpaqueTypeRef); auto TI = Context->getTypeInfo(TR); if (TI == nullptr) { - std::cout << "\n"; + fprintf(stdout, "\n"); } else { - TI->dump(std::cout); + TI->dump(stdout); } } @@ -439,9 +439,9 @@ void swift_reflection_dumpInfoForMetadata(SwiftReflectionContextRef ContextRef, auto Context = ContextRef->nativeContext; auto TI = Context->getMetadataTypeInfo(Metadata); if (TI == nullptr) { - std::cout << "\n"; + fprintf(stdout, "\n"); } else { - TI->dump(std::cout); + TI->dump(stdout); } } @@ -450,9 +450,9 @@ void swift_reflection_dumpInfoForInstance(SwiftReflectionContextRef ContextRef, auto Context = ContextRef->nativeContext; auto TI = Context->getInstanceTypeInfo(Object); if (TI == nullptr) { - std::cout << "\n"; + fprintf(stdout, "%s", "\n"); } else { - TI->dump(std::cout); + TI->dump(stdout); } } diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h index 5725f294885f9..1c60a7561c5b8 100644 --- a/stdlib/public/SwiftShims/LibcShims.h +++ b/stdlib/public/SwiftShims/LibcShims.h @@ -43,6 +43,8 @@ typedef __swift_uint32_t __swift_mode_t; typedef __swift_uint16_t __swift_mode_t; #elif defined(_WIN32) typedef __swift_int32_t __swift_mode_t; +#elif defined(__wasi__) +typedef __swift_uint32_t __swift_mode_t; #else // just guessing typedef __swift_uint16_t __swift_mode_t; #endif @@ -105,7 +107,7 @@ static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { return malloc_size(ptr); } #elif defined(__linux__) || defined(__CYGWIN__) || defined(__ANDROID__) \ - || defined(__HAIKU__) || defined(__FreeBSD__) + || defined(__HAIKU__) || defined(__FreeBSD__) || defined(__wasi__) static inline __swift_size_t _swift_stdlib_malloc_size(const void *ptr) { #if defined(__ANDROID__) #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 17 diff --git a/stdlib/public/SwiftShims/RefCount.h b/stdlib/public/SwiftShims/RefCount.h index d9ba9e5c77c62..1de032c56f49b 100644 --- a/stdlib/public/SwiftShims/RefCount.h +++ b/stdlib/public/SwiftShims/RefCount.h @@ -238,14 +238,29 @@ struct RefCountBitOffsets; // 32-bit out of line template <> struct RefCountBitOffsets<8> { - static const size_t IsImmortalShift = 0; - static const size_t IsImmortalBitCount = 1; - static const uint64_t IsImmortalMask = maskForField(IsImmortal); - - static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal); + /* + The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount + field are effectively a union of two different configurations: + + ---Normal case--- + Bit 0: Does this object need to call out to the ObjC runtime for deallocation + Bits 1-31: Unowned refcount + + ---Immortal case--- + All bits set, the object does not deallocate or have a refcount + */ + static const size_t PureSwiftDeallocShift = 0; + static const size_t PureSwiftDeallocBitCount = 1; + static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc); + + static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc); static const size_t UnownedRefCountBitCount = 31; static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount); + static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount + static const size_t IsImmortalBitCount = 32; + static const uint64_t IsImmortalMask = maskForField(IsImmortal); + static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount); static const size_t IsDeinitingBitCount = 1; static const uint64_t IsDeinitingMask = maskForField(IsDeiniting); @@ -271,14 +286,18 @@ struct RefCountBitOffsets<8> { // 32-bit inline template <> struct RefCountBitOffsets<4> { - static const size_t IsImmortalShift = 0; - static const size_t IsImmortalBitCount = 1; - static const uint64_t IsImmortalMask = maskForField(IsImmortal); + static const size_t PureSwiftDeallocShift = 0; + static const size_t PureSwiftDeallocBitCount = 1; + static const uint32_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc); - static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal); + static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc); static const size_t UnownedRefCountBitCount = 7; static const uint32_t UnownedRefCountMask = maskForField(UnownedRefCount); + static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount + static const size_t IsImmortalBitCount = 8; + static const uint32_t IsImmortalMask = maskForField(IsImmortal); + static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount); static const size_t IsDeinitingBitCount = 1; static const uint32_t IsDeinitingMask = maskForField(IsDeiniting); @@ -369,16 +388,39 @@ class RefCountBitsT { enum Immortal_t { Immortal }; LLVM_ATTRIBUTE_ALWAYS_INLINE - bool isImmortal() const { - return bool(getField(IsImmortal)); + bool isImmortal(bool checkSlowRCBit) const { + if (checkSlowRCBit) { + return (getField(IsImmortal) == Offsets::IsImmortalMask) && + bool(getField(UseSlowRC)); + } else { + return (getField(IsImmortal) == Offsets::IsImmortalMask); + } + } + + LLVM_ATTRIBUTE_ALWAYS_INLINE + bool isOverflowingUnownedRefCount(uint32_t oldValue, uint32_t inc) const { + auto newValue = getUnownedRefCount(); + return newValue != oldValue + inc || + newValue == Offsets::UnownedRefCountMask; } LLVM_ATTRIBUTE_ALWAYS_INLINE void setIsImmortal(bool value) { - setField(IsImmortal, value); + assert(value); + setField(IsImmortal, Offsets::IsImmortalMask); setField(UseSlowRC, value); } + LLVM_ATTRIBUTE_ALWAYS_INLINE + bool pureSwiftDeallocation() const { + return bool(getField(PureSwiftDealloc)) && !bool(getField(UseSlowRC)); + } + + LLVM_ATTRIBUTE_ALWAYS_INLINE + void setPureSwiftDeallocation(bool value) { + setField(PureSwiftDealloc, value); + } + LLVM_ATTRIBUTE_ALWAYS_INLINE RefCountBitsT() = default; @@ -386,16 +428,16 @@ class RefCountBitsT { constexpr RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount) : bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) | + (BitsType(1) << Offsets::PureSwiftDeallocShift) | (BitsType(unownedCount) << Offsets::UnownedRefCountShift)) { } LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr RefCountBitsT(Immortal_t immortal) - : bits((BitsType(2) << Offsets::StrongExtraRefCountShift) | - (BitsType(2) << Offsets::UnownedRefCountShift) | - (BitsType(1) << Offsets::IsImmortalShift) | - (BitsType(1) << Offsets::UseSlowRCShift)) + : bits((BitsType(2) << Offsets::StrongExtraRefCountShift) | + (BitsType(Offsets::IsImmortalMask)) | + (BitsType(1) << Offsets::UseSlowRCShift)) { } LLVM_ATTRIBUTE_ALWAYS_INLINE @@ -433,7 +475,7 @@ class RefCountBitsT { LLVM_ATTRIBUTE_ALWAYS_INLINE bool hasSideTable() const { - bool hasSide = getUseSlowRC() && !isImmortal(); + bool hasSide = getUseSlowRC() && !isImmortal(false); // Side table refcount must not point to another side table. assert((refcountIsInline || !hasSide) && @@ -523,7 +565,7 @@ class RefCountBitsT { LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE bool decrementStrongExtraRefCount(uint32_t dec) { #ifndef NDEBUG - if (!hasSideTable() && !isImmortal()) { + if (!hasSideTable() && !isImmortal(false)) { // Can't check these assertions with side table present. if (getIsDeiniting()) @@ -558,7 +600,7 @@ class RefCountBitsT { static_assert(Offsets::UnownedRefCountBitCount + Offsets::IsDeinitingBitCount + Offsets::StrongExtraRefCountBitCount + - Offsets::IsImmortalBitCount + + Offsets::PureSwiftDeallocBitCount + Offsets::UseSlowRCBitCount == sizeof(bits)*8, "inspect isUniquelyReferenced after adding fields"); @@ -715,7 +757,7 @@ class RefCounts { void setIsImmortal(bool immortal) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(true)) { return; } RefCountBits newbits; @@ -725,7 +767,28 @@ class RefCounts { } while (!refCounts.compare_exchange_weak(oldbits, newbits, std::memory_order_relaxed)); } - + + void setPureSwiftDeallocation(bool nonobjc) { + auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); + //Immortal and no objc complications share a bit, so don't let setting + //the complications one clear the immmortal one + if (oldbits.isImmortal(true) || oldbits.pureSwiftDeallocation() == nonobjc){ + assert(!oldbits.hasSideTable()); + return; + } + RefCountBits newbits; + do { + newbits = oldbits; + newbits.setPureSwiftDeallocation(nonobjc); + } while (!refCounts.compare_exchange_weak(oldbits, newbits, + std::memory_order_relaxed)); + } + + bool getPureSwiftDeallocation() { + auto bits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); + return bits.pureSwiftDeallocation(); + } + // Initialize from another refcount bits. // Only inline -> out-of-line is allowed (used for new side table entries). void init(InlineRefCountBits newBits) { @@ -740,7 +803,7 @@ class RefCounts { newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(inc); if (SWIFT_UNLIKELY(!fast)) { - if (oldbits.isImmortal()) + if (oldbits.isImmortal(false)) return; return incrementSlow(oldbits, inc); } @@ -753,7 +816,7 @@ class RefCounts { auto newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(inc); if (SWIFT_UNLIKELY(!fast)) { - if (oldbits.isImmortal()) + if (oldbits.isImmortal(false)) return; return incrementNonAtomicSlow(oldbits, inc); } @@ -771,7 +834,7 @@ class RefCounts { newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(1); if (SWIFT_UNLIKELY(!fast)) { - if (oldbits.isImmortal()) + if (oldbits.isImmortal(false)) return true; return tryIncrementSlow(oldbits); } @@ -788,7 +851,7 @@ class RefCounts { auto newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(1); if (SWIFT_UNLIKELY(!fast)) { - if (oldbits.isImmortal()) + if (oldbits.isImmortal(false)) return true; return tryIncrementNonAtomicSlow(oldbits); } @@ -824,7 +887,7 @@ class RefCounts { // Precondition: the reference count must be 1 void decrementFromOneNonAtomic() { auto bits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (bits.isImmortal()) { + if (bits.isImmortal(true)) { return; } if (bits.hasSideTable()) @@ -922,7 +985,7 @@ class RefCounts { // Decrement completed normally. New refcount is not zero. deinitNow = false; } - else if (oldbits.isImmortal()) { + else if (oldbits.isImmortal(false)) { return false; } else if (oldbits.hasSideTable()) { // Decrement failed because we're on some other slow path. @@ -961,7 +1024,7 @@ class RefCounts { // Decrement completed normally. New refcount is not zero. deinitNow = false; } - else if (oldbits.isImmortal()) { + else if (oldbits.isImmortal(false)) { return false; } else if (oldbits.hasSideTable()) { @@ -1001,7 +1064,7 @@ class RefCounts { bool fast = newbits.decrementStrongExtraRefCount(dec); if (SWIFT_UNLIKELY(!fast)) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return false; } // Slow paths include side table; deinit; underflow @@ -1025,7 +1088,7 @@ class RefCounts { // Increment the unowned reference count. void incrementUnowned(uint32_t inc) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (oldbits.isImmortal()) + if (oldbits.isImmortal(true)) return; RefCountBits newbits; do { @@ -1037,7 +1100,7 @@ class RefCounts { uint32_t oldValue = newbits.incrementUnownedRefCount(inc); // Check overflow and use the side table on overflow. - if (newbits.getUnownedRefCount() != oldValue + inc) + if (newbits.isOverflowingUnownedRefCount(oldValue, inc)) return incrementUnownedSlow(inc); } while (!refCounts.compare_exchange_weak(oldbits, newbits, @@ -1046,7 +1109,7 @@ class RefCounts { void incrementUnownedNonAtomic(uint32_t inc) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (oldbits.isImmortal()) + if (oldbits.isImmortal(true)) return; if (oldbits.hasSideTable()) return oldbits.getSideTable()->incrementUnownedNonAtomic(inc); @@ -1056,7 +1119,7 @@ class RefCounts { uint32_t oldValue = newbits.incrementUnownedRefCount(inc); // Check overflow and use the side table on overflow. - if (newbits.getUnownedRefCount() != oldValue + inc) + if (newbits.isOverflowingUnownedRefCount(oldValue, inc)) return incrementUnownedSlow(inc); refCounts.store(newbits, std::memory_order_relaxed); @@ -1066,7 +1129,7 @@ class RefCounts { // Return true if the caller should free the object. bool decrementUnownedShouldFree(uint32_t dec) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (oldbits.isImmortal()) + if (oldbits.isImmortal(true)) return false; RefCountBits newbits; @@ -1094,7 +1157,7 @@ class RefCounts { bool decrementUnownedShouldFreeNonAtomic(uint32_t dec) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); - if (oldbits.isImmortal()) + if (oldbits.isImmortal(true)) return false; if (oldbits.hasSideTable()) return oldbits.getSideTable()->decrementUnownedShouldFreeNonAtomic(dec); @@ -1383,7 +1446,7 @@ inline bool RefCounts::doDecrementNonAtomic(uint32_t dec) { auto newbits = oldbits; bool fast = newbits.decrementStrongExtraRefCount(dec); if (!fast) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return false; } return doDecrementNonAtomicSlow(oldbits, dec); @@ -1475,25 +1538,13 @@ HeapObject* RefCounts::getHeapObject() { // for use by SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS typedef swift::InlineRefCounts InlineRefCounts; -// __cplusplus -#endif - // These assertions apply to both the C and the C++ declarations. -#if defined(_MSC_VER) && !defined(__clang__) static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), "InlineRefCounts and InlineRefCountsPlaceholder must match"); static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), "InlineRefCounts must be pointer-sized"); static_assert(__alignof(InlineRefCounts) == __alignof(__swift_uintptr_t), "InlineRefCounts must be pointer-aligned"); -#else -_Static_assert(sizeof(InlineRefCounts) == sizeof(InlineRefCountsPlaceholder), - "InlineRefCounts and InlineRefCountsPlaceholder must match"); -_Static_assert(sizeof(InlineRefCounts) == sizeof(__swift_uintptr_t), - "InlineRefCounts must be pointer-sized"); -_Static_assert(_Alignof(InlineRefCounts) == _Alignof(__swift_uintptr_t), - "InlineRefCounts must be pointer-aligned"); -#endif #if defined(_WIN32) && defined(_M_ARM64) #if defined(__cplusplus) @@ -1508,4 +1559,6 @@ inline void _Atomic_storage::_Unlock() const n #endif #endif +#endif // !defined(__swift__) + #endif diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index 3947fc81e924f..722074bf05ed6 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -120,7 +120,7 @@ internal struct _ConcreteHashableBox: _AnyHashableBox { /// AnyHashable(Set(["a", "b"])): "a set of strings" /// ] /// print(descriptions[AnyHashable(42)]!) // prints "an Int" -/// print(descriptions[AnyHashable(43)]) // prints "nil" +/// print(descriptions[AnyHashable(45)]) // prints "nil" /// print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" /// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" @frozen diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 79f0f70247eab..bec36482144c3 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -346,7 +346,8 @@ extension Array { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -1017,19 +1018,65 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } + + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`, but at least + /// kept at `minimumCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`, but at least kept at `minimumCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) + + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -1048,7 +1095,9 @@ extension Array: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -1077,7 +1126,9 @@ extension Array: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -1118,6 +1169,8 @@ extension Array: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -1154,7 +1207,7 @@ extension Array: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -1162,38 +1215,59 @@ extension Array: RangeReplaceableCollection { "newElements.underestimatedCount was an overestimate") // can't check for overflow as sequences can underestimate - // This check prevents a data race writting to _swiftEmptyArrayStorage + // This check prevents a data race writing to _swiftEmptyArrayStorage if writtenCount > 0 { _buffer.count += writtenCount } - if writtenUpTo == buf.endIndex { + if _slowPath(writtenUpTo == buf.endIndex) { + + // A shortcut for appending an Array: If newElements is an Array then it's + // guaranteed that buf.initialize(from: newElements) already appended all + // elements. It reduces code size, because the following code + // can be removed by the optimizer by constant folding this check in a + // generic specialization. + if newElements is [Element] { + _internalInvariant(remainder.next() == nil) + return + } + // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty Array") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount @@ -1217,11 +1291,13 @@ extension Array: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() - let newCount = _getCount() - 1 + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") + let newCount = currentCount - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() pointer.moveInitialize(from: pointer + 1, count: newCount - index) @@ -1518,9 +1594,8 @@ extension Array { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1632,19 +1707,12 @@ extension Array { _precondition(subrange.upperBound <= _buffer.endIndex, "Array replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } diff --git a/stdlib/public/core/ArrayBuffer.swift b/stdlib/public/core/ArrayBuffer.swift index 0548b810bec77..2914a7387aee3 100644 --- a/stdlib/public/core/ArrayBuffer.swift +++ b/stdlib/public/core/ArrayBuffer.swift @@ -266,7 +266,7 @@ extension _ArrayBuffer { internal var count: Int { @inline(__always) get { - return _fastPath(_isNative) ? _native.count : _nonNative.count + return _fastPath(_isNative) ? _native.count : _nonNative.endIndex } set { _internalInvariant(_isNative, "attempting to update count of Cocoa array") diff --git a/stdlib/public/core/ArrayShared.swift b/stdlib/public/core/ArrayShared.swift index ee9fa395b432b..3e7d5939bac03 100644 --- a/stdlib/public/core/ArrayShared.swift +++ b/stdlib/public/core/ArrayShared.swift @@ -9,8 +9,14 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -/// This type is used as a result of the _checkSubscript call to associate the +/// This type is used as a result of the `_checkSubscript` call to associate the /// call with the array access call it guards. +/// +/// In order for the optimizer see that a call to `_checkSubscript` is semantically +/// associated with an array access, a value of this type is returned and later passed +/// to the accessing function. For example, a typical call to `_getElement` looks like +/// let token = _checkSubscript(index, ...) +/// return _getElement(index, ... , matchingSubscriptCheck: token) @frozen public struct _DependenceToken { @inlinable @@ -132,6 +138,23 @@ internal func _growArrayCapacity(_ capacity: Int) -> Int { return capacity * 2 } +@_alwaysEmitIntoClient +internal func _growArrayCapacity( + oldCapacity: Int, minimumCapacity: Int, growForAppend: Bool +) -> Int { + if growForAppend { + if oldCapacity < minimumCapacity { + // When appending to an array, grow exponentially. + return Swift.max(minimumCapacity, _growArrayCapacity(oldCapacity)) + } + return oldCapacity + } + // If not for append, just use the specified capacity, ignoring oldCapacity. + // This means that we "shrink" the buffer in case minimumCapacity is less + // than oldCapacity. + return minimumCapacity +} + //===--- generic helpers --------------------------------------------------===// extension _ArrayBufferProtocol { diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 1d235c4a64aa4..5d17d8704dde3 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -953,7 +953,10 @@ extension ArraySlice: RangeReplaceableCollection { "newElements.underestimatedCount was an overestimate") // can't check for overflow as sequences can underestimate - _buffer.count += writtenCount + // This check prevents a data race writing to _swiftEmptyArrayStorage + if writtenCount > 0 { + _buffer.count += writtenCount + } if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, diff --git a/stdlib/public/core/BridgeObjectiveC.swift b/stdlib/public/core/BridgeObjectiveC.swift index 654bc0fdd84ac..f38362fc2084a 100644 --- a/stdlib/public/core/BridgeObjectiveC.swift +++ b/stdlib/public/core/BridgeObjectiveC.swift @@ -509,7 +509,9 @@ public struct AutoreleasingUnsafeMutablePointer /// - Warning: Accessing `pointee` as a type that is unrelated to /// the underlying memory's bound type is undefined. @usableFromInline @_transparent - internal init(_ from: UnsafePointer) { + internal init( + @_nonEphemeral _ from: UnsafePointer + ) { self._rawValue = from._rawValue } @@ -523,7 +525,9 @@ public struct AutoreleasingUnsafeMutablePointer /// - Warning: Accessing `pointee` as a type that is unrelated to /// the underlying memory's bound type is undefined. @usableFromInline @_transparent - internal init?(_ from: UnsafePointer?) { + internal init?( + @_nonEphemeral _ from: UnsafePointer? + ) { guard let unwrapped = from else { return nil } self.init(unwrapped) } @@ -535,7 +539,9 @@ extension UnsafeMutableRawPointer { /// /// - Parameter other: The pointer to convert. @_transparent - public init(_ other: AutoreleasingUnsafeMutablePointer) { + public init( + @_nonEphemeral _ other: AutoreleasingUnsafeMutablePointer + ) { _rawValue = other._rawValue } @@ -545,7 +551,9 @@ extension UnsafeMutableRawPointer { /// - Parameter other: The pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: AutoreleasingUnsafeMutablePointer?) { + public init?( + @_nonEphemeral _ other: AutoreleasingUnsafeMutablePointer? + ) { guard let unwrapped = other else { return nil } self.init(unwrapped) } @@ -557,7 +565,9 @@ extension UnsafeRawPointer { /// /// - Parameter other: The pointer to convert. @_transparent - public init(_ other: AutoreleasingUnsafeMutablePointer) { + public init( + @_nonEphemeral _ other: AutoreleasingUnsafeMutablePointer + ) { _rawValue = other._rawValue } @@ -567,7 +577,9 @@ extension UnsafeRawPointer { /// - Parameter other: The pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: AutoreleasingUnsafeMutablePointer?) { + public init?( + @_nonEphemeral _ other: AutoreleasingUnsafeMutablePointer? + ) { guard let unwrapped = other else { return nil } self.init(unwrapped) } diff --git a/stdlib/public/core/BuiltinMath.swift b/stdlib/public/core/BuiltinMath.swift new file mode 100644 index 0000000000000..762da5135f640 --- /dev/null +++ b/stdlib/public/core/BuiltinMath.swift @@ -0,0 +1,161 @@ +//===--- BuiltinMath.swift --------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// Each of the following functions is ordered to match math.h + +// These functions have a corresponding LLVM intrinsic. +// Note, keep this up to date with Darwin/tgmath.swift.gyb + +// Order of intrinsics: cos, sin, exp, exp2, log, log10, log2, nearbyint, rint + +// Float Intrinsics (32 bits) + +@_transparent +public func _cos(_ x: Float) -> Float { + return Float(Builtin.int_cos_FPIEEE32(x._value)) +} + +@_transparent +public func _sin(_ x: Float) -> Float { + return Float(Builtin.int_sin_FPIEEE32(x._value)) +} + +@_transparent +public func _exp(_ x: Float) -> Float { + return Float(Builtin.int_exp_FPIEEE32(x._value)) +} + +@_transparent +public func _exp2(_ x: Float) -> Float { + return Float(Builtin.int_exp2_FPIEEE32(x._value)) +} + +@_transparent +public func _log(_ x: Float) -> Float { + return Float(Builtin.int_log_FPIEEE32(x._value)) +} + +@_transparent +public func _log10(_ x: Float) -> Float { + return Float(Builtin.int_log10_FPIEEE32(x._value)) +} + +@_transparent +public func _log2(_ x: Float) -> Float { + return Float(Builtin.int_log2_FPIEEE32(x._value)) +} + +@_transparent +public func _nearbyint(_ x: Float) -> Float { + return Float(Builtin.int_nearbyint_FPIEEE32(x._value)) +} + +@_transparent +public func _rint(_ x: Float) -> Float { + return Float(Builtin.int_rint_FPIEEE32(x._value)) +} + +// Double Intrinsics (64 bits) + +@_transparent +public func _cos(_ x: Double) -> Double { + return Double(Builtin.int_cos_FPIEEE64(x._value)) +} + +@_transparent +public func _sin(_ x: Double) -> Double { + return Double(Builtin.int_sin_FPIEEE64(x._value)) +} + +@_transparent +public func _exp(_ x: Double) -> Double { + return Double(Builtin.int_exp_FPIEEE64(x._value)) +} + +@_transparent +public func _exp2(_ x: Double) -> Double { + return Double(Builtin.int_exp2_FPIEEE64(x._value)) +} + +@_transparent +public func _log(_ x: Double) -> Double { + return Double(Builtin.int_log_FPIEEE64(x._value)) +} + +@_transparent +public func _log10(_ x: Double) -> Double { + return Double(Builtin.int_log10_FPIEEE64(x._value)) +} + +@_transparent +public func _log2(_ x: Double) -> Double { + return Double(Builtin.int_log2_FPIEEE64(x._value)) +} + +@_transparent +public func _nearbyint(_ x: Double) -> Double { + return Double(Builtin.int_nearbyint_FPIEEE64(x._value)) +} + +@_transparent +public func _rint(_ x: Double) -> Double { + return Double(Builtin.int_rint_FPIEEE64(x._value)) +} + +// Float80 Intrinsics (80 bits) + +#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) +@_transparent +public func _cos(_ x: Float80) -> Float80 { + return Float80(Builtin.int_cos_FPIEEE80(x._value)) +} + +@_transparent +public func _sin(_ x: Float80) -> Float80 { + return Float80(Builtin.int_sin_FPIEEE80(x._value)) +} + +@_transparent +public func _exp(_ x: Float80) -> Float80 { + return Float80(Builtin.int_exp_FPIEEE80(x._value)) +} + +@_transparent +public func _exp2(_ x: Float80) -> Float80 { + return Float80(Builtin.int_exp2_FPIEEE80(x._value)) +} + +@_transparent +public func _log(_ x: Float80) -> Float80 { + return Float80(Builtin.int_log_FPIEEE80(x._value)) +} + +@_transparent +public func _log10(_ x: Float80) -> Float80 { + return Float80(Builtin.int_log10_FPIEEE80(x._value)) +} + +@_transparent +public func _log2(_ x: Float80) -> Float80 { + return Float80(Builtin.int_log2_FPIEEE80(x._value)) +} + +@_transparent +public func _nearbyint(_ x: Float80) -> Float80 { + return Float80(Builtin.int_nearbyint_FPIEEE80(x._value)) +} + +@_transparent +public func _rint(_ x: Float80) -> Float80 { + return Float80(Builtin.int_rint_FPIEEE80(x._value)) +} +#endif diff --git a/stdlib/public/core/BuiltinMath.swift.gyb b/stdlib/public/core/BuiltinMath.swift.gyb deleted file mode 100644 index 91d595e6f7018..0000000000000 --- a/stdlib/public/core/BuiltinMath.swift.gyb +++ /dev/null @@ -1,78 +0,0 @@ -//===--- BuiltinMath.swift.gyb --------------------------------*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -%{ - -# Don't need 64-bit (Double/CDouble) overlays. The ordinary C imports work fine. -overlayFloatBits = [32, 80] -allFloatBits = [32, 64, 80] - -def floatName(bits): - if bits == 32: - return 'Float' - if bits == 64: - return 'Double' - if bits == 80: - return 'Float80' - -def cFloatName(bits): - if bits == 32: - return 'CFloat' - if bits == 64: - return 'CDouble' - if bits == 80: - return 'CLongDouble' - -def cFuncSuffix(bits): - if bits == 32: - return 'f' - if bits == 64: - return '' - if bits == 80: - return 'l' - -# Each of the following lists is ordered to match math.h - -# These functions have a corresponding LLVM intrinsic -# Note, keep this up to date with Darwin/tgmath.swift.gyb -UnaryIntrinsicFunctions = [ - 'cos', 'sin', - 'exp', 'exp2', - 'log', 'log10', 'log2', - 'nearbyint', 'rint', -] - -def TypedUnaryIntrinsicFunctions(): - for ufunc in UnaryIntrinsicFunctions: - for bits in allFloatBits: - yield floatName(bits), cFloatName(bits), bits, ufunc - -}% - -// Unary intrinsic functions -// Note these have a corresponding LLVM intrinsic -% for T, CT, bits, ufunc in TypedUnaryIntrinsicFunctions(): -% if bits == 80: -#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) -% end -@_transparent -public func _${ufunc}(_ x: ${T}) -> ${T} { - return ${T}(Builtin.int_${ufunc}_FPIEEE${bits}(x._value)) -} -% if bits == 80: -#endif -% end -% end - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 7175fab8345fc..9fcf71508a7bf 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -2,7 +2,7 @@ # # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -37,8 +37,10 @@ set(SWIFTLIB_ESSENTIAL BridgeStorage.swift BridgingBuffer.swift Builtin.swift + BuiltinMath.swift Character.swift CocoaArray.swift + Codable.swift Collection.swift CollectionAlgorithms.swift Comparable.swift @@ -61,6 +63,7 @@ set(SWIFTLIB_ESSENTIAL EmptyCollection.swift Equatable.swift ErrorType.swift + ExistentialCollection.swift Filter.swift FixedArray.swift FlatMap.swift @@ -91,6 +94,7 @@ set(SWIFTLIB_ESSENTIAL Map.swift MemoryLayout.swift UnicodeScalar.swift # ORDER DEPENDENCY: Must precede Mirrors.swift + Mirrors.swift Misc.swift MutableCollection.swift NativeDictionary.swift @@ -113,6 +117,7 @@ set(SWIFTLIB_ESSENTIAL REPL.swift Result.swift Reverse.swift + Runtime.swift RuntimeFunctionCounters.swift SipHash.swift Sequence.swift @@ -152,6 +157,7 @@ set(SWIFTLIB_ESSENTIAL StringNormalization.swift StringRangeReplaceableCollection.swift StringStorage.swift + StringStorageBridge.swift StringSwitch.swift StringTesting.swift StringUnicodeScalarView.swift @@ -162,6 +168,7 @@ set(SWIFTLIB_ESSENTIAL SwiftNativeNSArray.swift ThreadLocalStorage.swift UIntBuffer.swift + UnavailableStringAPIs.swift UnicodeEncoding.swift UnicodeHelpers.swift UnicodeParser.swift @@ -184,14 +191,9 @@ set(SWIFTLIB_ESSENTIAL set(SWIFTLIB_ESSENTIAL_GYB_SOURCES AtomicInt.swift.gyb - BuiltinMath.swift.gyb - Codable.swift.gyb FloatingPointParsing.swift.gyb FloatingPointTypes.swift.gyb IntegerTypes.swift.gyb - Mirrors.swift.gyb - Runtime.swift.gyb - UnavailableStringAPIs.swift.gyb UnsafeBufferPointer.swift.gyb UnsafeRawBufferPointer.swift.gyb ) @@ -218,7 +220,6 @@ set(SWIFTLIB_SOURCES set(SWIFTLIB_GYB_SOURCES ${SWIFTLIB_ESSENTIAL_GYB_SOURCES} - ExistentialCollection.swift.gyb SIMDVectorTypes.swift.gyb Tuple.swift.gyb ) diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index ee218685bad76..395f112dccbb5 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -141,7 +141,7 @@ public struct OpaquePointer { /// Converts a typed `UnsafePointer` to an opaque C pointer. @_transparent - public init(_ from: UnsafePointer) { + public init(@_nonEphemeral _ from: UnsafePointer) { self._rawValue = from._rawValue } @@ -149,14 +149,14 @@ public struct OpaquePointer { /// /// The result is `nil` if `from` is `nil`. @_transparent - public init?(_ from: UnsafePointer?) { + public init?(@_nonEphemeral _ from: UnsafePointer?) { guard let unwrapped = from else { return nil } self.init(unwrapped) } /// Converts a typed `UnsafeMutablePointer` to an opaque C pointer. @_transparent - public init(_ from: UnsafeMutablePointer) { + public init(@_nonEphemeral _ from: UnsafeMutablePointer) { self._rawValue = from._rawValue } @@ -164,7 +164,7 @@ public struct OpaquePointer { /// /// The result is `nil` if `from` is `nil`. @_transparent - public init?(_ from: UnsafeMutablePointer?) { + public init?(@_nonEphemeral _ from: UnsafeMutablePointer?) { guard let unwrapped = from else { return nil } self.init(unwrapped) } diff --git a/stdlib/public/core/Codable.swift b/stdlib/public/core/Codable.swift new file mode 100644 index 0000000000000..c23a0fa8103f8 --- /dev/null +++ b/stdlib/public/core/Codable.swift @@ -0,0 +1,5982 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Codable +//===----------------------------------------------------------------------===// + +/// A type that can encode itself to an external representation. +public protocol Encodable { + /// Encodes this value into the given encoder. + /// + /// If the value fails to encode anything, `encoder` will encode an empty + /// keyed container in its place. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + func encode(to encoder: Encoder) throws +} + +/// A type that can decode itself from an external representation. +public protocol Decodable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + init(from decoder: Decoder) throws +} + +/// A type that can convert itself into and out of an external representation. +/// +/// `Codable` is a type alias for the `Encodable` and `Decodable` protocols. +/// When you use `Codable` as a type or a generic constraint, it matches +/// any type that conforms to both protocols. +public typealias Codable = Encodable & Decodable + +//===----------------------------------------------------------------------===// +// CodingKey +//===----------------------------------------------------------------------===// + +/// A type that can be used as a key for encoding and decoding. +public protocol CodingKey: CustomStringConvertible, + CustomDebugStringConvertible { + /// The string to use in a named collection (e.g. a string-keyed dictionary). + var stringValue: String { get } + + /// Creates a new instance from the given string. + /// + /// If the string passed as `stringValue` does not correspond to any instance + /// of this type, the result is `nil`. + /// + /// - parameter stringValue: The string value of the desired key. + init?(stringValue: String) + + /// The value to use in an integer-indexed collection (e.g. an int-keyed + /// dictionary). + var intValue: Int? { get } + + /// Creates a new instance from the specified integer. + /// + /// If the value passed as `intValue` does not correspond to any instance of + /// this type, the result is `nil`. + /// + /// - parameter intValue: The integer value of the desired key. + init?(intValue: Int) +} + +extension CodingKey { + /// A textual representation of this key. + public var description: String { + let intValue = self.intValue?.description ?? "nil" + return "\(type(of: self))(stringValue: \"\(stringValue)\", intValue: \(intValue))" + } + + /// A textual representation of this key, suitable for debugging. + public var debugDescription: String { + return description + } +} + +//===----------------------------------------------------------------------===// +// Encoder & Decoder +//===----------------------------------------------------------------------===// + +/// A type that can encode values into a native format for external +/// representation. +public protocol Encoder { + /// The path of coding keys taken to get to this point in encoding. + var codingPath: [CodingKey] { get } + + /// Any contextual information set by the user for encoding. + var userInfo: [CodingUserInfoKey: Any] { get } + + /// Returns an encoding container appropriate for holding multiple values + /// keyed by the given key type. + /// + /// You must use only one kind of top-level encoding container. This method + /// must not be called after a call to `unkeyedContainer()` or after + /// encoding a value through a call to `singleValueContainer()` + /// + /// - parameter type: The key type to use for the container. + /// - returns: A new keyed encoding container. + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer + + /// Returns an encoding container appropriate for holding multiple unkeyed + /// values. + /// + /// You must use only one kind of top-level encoding container. This method + /// must not be called after a call to `container(keyedBy:)` or after + /// encoding a value through a call to `singleValueContainer()` + /// + /// - returns: A new empty unkeyed container. + func unkeyedContainer() -> UnkeyedEncodingContainer + + /// Returns an encoding container appropriate for holding a single primitive + /// value. + /// + /// You must use only one kind of top-level encoding container. This method + /// must not be called after a call to `unkeyedContainer()` or + /// `container(keyedBy:)`, or after encoding a value through a call to + /// `singleValueContainer()` + /// + /// - returns: A new empty single value container. + func singleValueContainer() -> SingleValueEncodingContainer +} + +/// A type that can decode values from a native format into in-memory +/// representations. +public protocol Decoder { + /// The path of coding keys taken to get to this point in decoding. + var codingPath: [CodingKey] { get } + + /// Any contextual information set by the user for decoding. + var userInfo: [CodingUserInfoKey: Any] { get } + + /// Returns the data stored in this decoder as represented in a container + /// keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into this decoder. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not a keyed container. + func container( + keyedBy type: Key.Type + ) throws -> KeyedDecodingContainer + + /// Returns the data stored in this decoder as represented in a container + /// appropriate for holding values with no keys. + /// + /// - returns: An unkeyed container view into this decoder. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not an unkeyed container. + func unkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Returns the data stored in this decoder as represented in a container + /// appropriate for holding a single primitive value. + /// + /// - returns: A single value container view into this decoder. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not a single value container. + func singleValueContainer() throws -> SingleValueDecodingContainer +} + +//===----------------------------------------------------------------------===// +// Keyed Encoding Containers +//===----------------------------------------------------------------------===// + +/// A type that provides a view into an encoder's storage and is used to hold +/// the encoded properties of an encodable type in a keyed manner. +/// +/// Encoders should provide types conforming to +/// `KeyedEncodingContainerProtocol` for their format. +public protocol KeyedEncodingContainerProtocol { + associatedtype Key: CodingKey + + /// The path of coding keys taken to get to this point in encoding. + var codingPath: [CodingKey] { get } + + /// Encodes a null value for the given key. + /// + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if a null value is invalid in the + /// current context for this format. + mutating func encodeNil(forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Bool, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: String, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Double, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Float, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int8, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int16, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int32, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int64, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt8, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt16, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt32, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt64, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: T, forKey key: Key) throws + + /// Encodes a reference to the given object only if it is encoded + /// unconditionally elsewhere in the payload (previously, or in the future). + /// + /// For encoders which don't support this feature, the default implementation + /// encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeConditional( + _ object: T, + forKey key: Key + ) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Bool?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: String?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Double?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Float?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Int?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Int8?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Int16?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Int32?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: Int64?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: UInt?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeIfPresent( + _ value: T?, + forKey key: Key + ) throws + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer( + forKey key: Key + ) -> UnkeyedEncodingContainer + + /// Stores a new nested container for the default `super` key and returns A + /// new encoder instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with + /// `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new encoder to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder + + /// Stores a new nested container for the given key and returns A new encoder + /// instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new encoder to pass to `super.encode(to:)`. + mutating func superEncoder(forKey key: Key) -> Encoder +} + +// An implementation of _KeyedEncodingContainerBase and +// _KeyedEncodingContainerBox are given at the bottom of this file. + +/// A concrete container that provides a view into an encoder's storage, making +/// the encoded properties of an encodable type accessible by keys. +public struct KeyedEncodingContainer : + KeyedEncodingContainerProtocol +{ + public typealias Key = K + + /// The container for the concrete encoder. The type is _*Base so that it's + /// generic on the key type. + internal var _box: _KeyedEncodingContainerBase + + /// Creates a new instance with the given container. + /// + /// - parameter container: The container to hold. + public init( + _ container: Container + ) where Container.Key == Key { + _box = _KeyedEncodingContainerBox(container) + } + + /// The path of coding keys taken to get to this point in encoding. + public var codingPath: [CodingKey] { + return _box.codingPath + } + + /// Encodes a null value for the given key. + /// + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if a null value is invalid in the + /// current context for this format. + public mutating func encodeNil(forKey key: Key) throws { + try _box.encodeNil(forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Bool, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: String, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Double, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Float, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Int, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Int8, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Int16, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Int32, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: Int64, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: UInt, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: UInt8, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: UInt16, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: UInt32, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode(_ value: UInt64, forKey key: Key) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encode( + _ value: T, + forKey key: Key + ) throws { + try _box.encode(value, forKey: key) + } + + /// Encodes a reference to the given object only if it is encoded + /// unconditionally elsewhere in the payload (previously, or in the future). + /// + /// For encoders which don't support this feature, the default implementation + /// encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeConditional( + _ object: T, + forKey key: Key + ) throws { + try _box.encodeConditional(object, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Bool?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: String?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Double?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Float?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Int?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Int8?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Int16?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Int32?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: Int64?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: UInt?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: UInt8?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: UInt16?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: UInt32?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: UInt64?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Encodes the given value for the given key if it is not `nil`. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + public mutating func encodeIfPresent( + _ value: T?, + forKey key: Key + ) throws { + try _box.encodeIfPresent(value, forKey: key) + } + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + public mutating func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer { + return _box.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + public mutating func nestedUnkeyedContainer( + forKey key: Key + ) -> UnkeyedEncodingContainer { + return _box.nestedUnkeyedContainer(forKey: key) + } + + /// Stores a new nested container for the default `super` key and returns A + /// new encoder instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with + /// `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new encoder to pass to `super.encode(to:)`. + public mutating func superEncoder() -> Encoder { + return _box.superEncoder() + } + + /// Stores a new nested container for the given key and returns A new encoder + /// instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new encoder to pass to `super.encode(to:)`. + public mutating func superEncoder(forKey key: Key) -> Encoder { + return _box.superEncoder(forKey: key) + } +} + +/// A type that provides a view into a decoder's storage and is used to hold +/// the encoded properties of a decodable type in a keyed manner. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for +/// their format. +public protocol KeyedDecodingContainerProtocol { + associatedtype Key: CodingKey + + /// The path of coding keys taken to get to this point in decoding. + var codingPath: [CodingKey] { get } + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different + /// keys here; it is possible to encode with multiple key types which are + /// not convertible to one another. This should report all keys present + /// which are convertible to the requested type. + var allKeys: [Key] { get } + + /// Returns a Boolean value indicating whether the decoder contains a value + /// associated with the given key. + /// + /// The value associated with `key` may be a null value as appropriate for + /// the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + func contains(_ key: Key) -> Bool + + /// Decodes a null value for the given key. + /// + /// - parameter key: The key that the decoded value is associated with. + /// - returns: Whether the encountered value was null. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + func decodeNil(forKey key: Key) throws -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: String.Type, forKey key: Key) throws -> String + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Double.Type, forKey key: Key) throws -> Double + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Float.Type, forKey key: Key) throws -> Float + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Int.Type, forKey key: Key) throws -> Int + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func decode(_ type: T.Type, forKey key: Key) throws -> T + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + func decodeIfPresent( + _ type: T.Type, + forKey key: Key + ) throws -> T? + + /// Returns the data stored for the given key as represented in a container + /// keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not a keyed container. + func nestedContainer( + keyedBy type: NestedKey.Type, + forKey key: Key + ) throws -> KeyedDecodingContainer + + /// Returns the data stored for the given key as represented in an unkeyed + /// container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not an unkeyed container. + func nestedUnkeyedContainer( + forKey key: Key + ) throws -> UnkeyedDecodingContainer + + /// Returns a `Decoder` instance for decoding `super` from the container + /// associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with + /// `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the default `super` key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the default `super` key. + func superDecoder() throws -> Decoder + + /// Returns a `Decoder` instance for decoding `super` from the container + /// associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + func superDecoder(forKey key: Key) throws -> Decoder +} + +// An implementation of _KeyedDecodingContainerBase and +// _KeyedDecodingContainerBox are given at the bottom of this file. + +/// A concrete container that provides a view into a decoder's storage, making +/// the encoded properties of a decodable type accessible by keys. +public struct KeyedDecodingContainer : + KeyedDecodingContainerProtocol +{ + public typealias Key = K + + /// The container for the concrete decoder. The type is _*Base so that it's + /// generic on the key type. + internal var _box: _KeyedDecodingContainerBase + + /// Creates a new instance with the given container. + /// + /// - parameter container: The container to hold. + public init( + _ container: Container + ) where Container.Key == Key { + _box = _KeyedDecodingContainerBox(container) + } + + /// The path of coding keys taken to get to this point in decoding. + public var codingPath: [CodingKey] { + return _box.codingPath + } + + /// All the keys the decoder has for this container. + /// + /// Different keyed containers from the same decoder may return different + /// keys here, because it is possible to encode with multiple key types + /// which are not convertible to one another. This should report all keys + /// present which are convertible to the requested type. + public var allKeys: [Key] { + return _box.allKeys + } + + /// Returns a Boolean value indicating whether the decoder contains a value + /// associated with the given key. + /// + /// The value associated with the given key may be a null value as + /// appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + public func contains(_ key: Key) -> Bool { + return _box.contains(key) + } + + /// Decodes a null value for the given key. + /// + /// - parameter key: The key that the decoded value is associated with. + /// - returns: Whether the encountered value was null. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + public func decodeNil(forKey key: Key) throws -> Bool { + return try _box.decodeNil(forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { + return try _box.decode(Bool.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: String.Type, forKey key: Key) throws -> String { + return try _box.decode(String.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { + return try _box.decode(Double.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { + return try _box.decode(Float.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { + return try _box.decode(Int.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { + return try _box.decode(Int8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { + return try _box.decode(Int16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { + return try _box.decode(Int32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { + return try _box.decode(Int64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { + return try _box.decode(UInt.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { + return try _box.decode(UInt8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { + return try _box.decode(UInt16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { + return try _box.decode(UInt32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { + return try _box.decode(UInt64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func decode( + _ type: T.Type, + forKey key: Key + ) throws -> T { + return try _box.decode(T.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool? { + return try _box.decodeIfPresent(Bool.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: String.Type, + forKey key: Key + ) throws -> String? { + return try _box.decodeIfPresent(String.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Double.Type, + forKey key: Key + ) throws -> Double? { + return try _box.decodeIfPresent(Double.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Float.Type, + forKey key: Key + ) throws -> Float? { + return try _box.decodeIfPresent(Float.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Int.Type, + forKey key: Key + ) throws -> Int? { + return try _box.decodeIfPresent(Int.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8? { + return try _box.decodeIfPresent(Int8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16? { + return try _box.decodeIfPresent(Int16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32? { + return try _box.decodeIfPresent(Int32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64? { + return try _box.decodeIfPresent(Int64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt? { + return try _box.decodeIfPresent(UInt.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8? { + return try _box.decodeIfPresent(UInt8.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16? { + return try _box.decodeIfPresent(UInt16.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32? { + return try _box.decodeIfPresent(UInt32.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64? { + return try _box.decodeIfPresent(UInt64.self, forKey: key) + } + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value + /// associated with `key`, or if the value is null. The difference between + /// these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the + /// `Decoder` does not have an entry associated with the given key, or if + /// the value is a null value. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + public func decodeIfPresent( + _ type: T.Type, + forKey key: Key + ) throws -> T? { + return try _box.decodeIfPresent(T.self, forKey: key) + } + + /// Returns the data stored for the given key as represented in a container + /// keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not a keyed container. + public func nestedContainer( + keyedBy type: NestedKey.Type, + forKey key: Key + ) throws -> KeyedDecodingContainer { + return try _box.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + /// Returns the data stored for the given key as represented in an unkeyed + /// container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not an unkeyed container. + public func nestedUnkeyedContainer( + forKey key: Key + ) throws -> UnkeyedDecodingContainer { + return try _box.nestedUnkeyedContainer(forKey: key) + } + + /// Returns a `Decoder` instance for decoding `super` from the container + /// associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with + /// `Key(stringValue: "super", intValue: 0)`. + /// + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the default `super` key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the default `super` key. + public func superDecoder() throws -> Decoder { + return try _box.superDecoder() + } + + /// Returns a `Decoder` instance for decoding `super` from the container + /// associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry + /// for the given key. + /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for + /// the given key. + public func superDecoder(forKey key: Key) throws -> Decoder { + return try _box.superDecoder(forKey: key) + } +} + +//===----------------------------------------------------------------------===// +// Unkeyed Encoding Containers +//===----------------------------------------------------------------------===// + +/// A type that provides a view into an encoder's storage and is used to hold +/// the encoded properties of an encodable type sequentially, without keys. +/// +/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for +/// their format. +public protocol UnkeyedEncodingContainer { + /// The path of coding keys taken to get to this point in encoding. + var codingPath: [CodingKey] { get } + + /// The number of elements encoded into the container. + var count: Int { get } + + /// Encodes a null value. + /// + /// - throws: `EncodingError.invalidValue` if a null value is invalid in the + /// current context for this format. + mutating func encodeNil() throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Bool) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: String) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Double) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Float) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int8) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int16) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int32) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: Int64) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt8) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt16) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt32) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: UInt64) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encode(_ value: T) throws + + /// Encodes a reference to the given object only if it is encoded + /// unconditionally elsewhere in the payload (previously, or in the future). + /// + /// For encoders which don't support this feature, the default implementation + /// encodes the given object unconditionally. + /// + /// For formats which don't support this feature, the default implementation + /// encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + mutating func encodeConditional(_ object: T) throws + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Bool + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == String + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Double + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Float + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int8 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int16 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int32 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int64 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt8 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt16 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt32 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt64 + + /// Encodes the elements of the given sequence. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode( + contentsOf sequence: T + ) throws where T.Element: Encodable + + /// Encodes a nested container keyed by the given type and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - returns: A new keyed encoding container. + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type + ) -> KeyedEncodingContainer + + /// Encodes an unkeyed encoding container and returns it. + /// + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer + + /// Encodes a nested container and returns an `Encoder` instance for encoding + /// `super` into that container. + /// + /// - returns: A new encoder to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder +} + +/// A type that provides a view into a decoder's storage and is used to hold +/// the encoded properties of a decodable type sequentially, without keys. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for +/// their format. +public protocol UnkeyedDecodingContainer { + /// The path of coding keys taken to get to this point in decoding. + var codingPath: [CodingKey] { get } + + /// The number of elements contained within this container. + /// + /// If the number of elements is unknown, the value is `nil`. + var count: Int? { get } + + /// A Boolean value indicating whether there are no more elements left to be + /// decoded in the container. + var isAtEnd: Bool { get } + + /// The current decoding index of the container (i.e. the index of the next + /// element to be decoded.) Incremented after every successful decode call. + var currentIndex: Int { get } + + /// Decodes a null value. + /// + /// If the value is not null, does not increment currentIndex. + /// + /// - returns: Whether the encountered value was null. + /// - throws: `DecodingError.valueNotFound` if there are no more values to + /// decode. + mutating func decodeNil() throws -> Bool + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Bool.Type) throws -> Bool + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: String.Type) throws -> String + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Double.Type) throws -> Double + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Float.Type) throws -> Float + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Int.Type) throws -> Int + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Int8.Type) throws -> Int8 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Int16.Type) throws -> Int16 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Int32.Type) throws -> Int32 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: Int64.Type) throws -> Int64 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: UInt.Type) throws -> UInt + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: UInt8.Type) throws -> UInt8 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: UInt16.Type) throws -> UInt16 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: UInt32.Type) throws -> UInt32 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: UInt64.Type) throws -> UInt64 + + /// Decodes a value of the given type. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key + /// and convertible to the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func decode(_ type: T.Type) throws -> T + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: String.Type) throws -> String? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to + /// decode, or if the value is null. The difference between these states can + /// be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value + /// is a null value, or if there are no more elements to decode. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// is not convertible to the requested type. + mutating func decodeIfPresent(_ type: T.Type) throws -> T? + + /// Decodes a nested container keyed by the given type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not a keyed container. + mutating func nestedContainer( + keyedBy type: NestedKey.Type + ) throws -> KeyedDecodingContainer + + /// Decodes an unkeyed nested container. + /// + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `DecodingError.typeMismatch` if the encountered stored value is + /// not an unkeyed container. + mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Decodes a nested container and returns a `Decoder` instance for decoding + /// `super` from that container. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null, or of there are no more values to decode. + mutating func superDecoder() throws -> Decoder +} + +//===----------------------------------------------------------------------===// +// Single Value Encoding Containers +//===----------------------------------------------------------------------===// + +/// A container that can support the storage and direct encoding of a single +/// non-keyed value. +public protocol SingleValueEncodingContainer { + /// The path of coding keys taken to get to this point in encoding. + var codingPath: [CodingKey] { get } + + /// Encodes a null value. + /// + /// - throws: `EncodingError.invalidValue` if a null value is invalid in the + /// current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encodeNil() throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Bool) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: String) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Double) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Float) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Int) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Int8) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Int16) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Int32) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: Int64) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: UInt) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: UInt8) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: UInt16) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: UInt32) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: UInt64) throws + + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `EncodingError.invalidValue` if the given value is invalid in + /// the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` + /// call. + mutating func encode(_ value: T) throws +} + +/// A container that can support the storage and direct decoding of a single +/// nonkeyed value. +public protocol SingleValueDecodingContainer { + /// The path of coding keys taken to get to this point in encoding. + var codingPath: [CodingKey] { get } + + /// Decodes a null value. + /// + /// - returns: Whether the encountered value was null. + func decodeNil() -> Bool + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Bool.Type) throws -> Bool + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: String.Type) throws -> String + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Double.Type) throws -> Double + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Float.Type) throws -> Float + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Int.Type) throws -> Int + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Int8.Type) throws -> Int8 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Int16.Type) throws -> Int16 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Int32.Type) throws -> Int32 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: Int64.Type) throws -> Int64 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: UInt.Type) throws -> UInt + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: UInt8.Type) throws -> UInt8 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: UInt16.Type) throws -> UInt16 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: UInt32.Type) throws -> UInt32 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: UInt64.Type) throws -> UInt64 + + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `DecodingError.typeMismatch` if the encountered encoded value + /// cannot be converted to the requested type. + /// - throws: `DecodingError.valueNotFound` if the encountered encoded value + /// is null. + func decode(_ type: T.Type) throws -> T +} + +//===----------------------------------------------------------------------===// +// User Info +//===----------------------------------------------------------------------===// + +/// A user-defined key for providing context during encoding and decoding. +public struct CodingUserInfoKey: RawRepresentable, Equatable, Hashable { + public typealias RawValue = String + + /// The key's string value. + public let rawValue: String + + /// Creates a new instance with the given raw value. + /// + /// - parameter rawValue: The value of the key. + public init?(rawValue: String) { + self.rawValue = rawValue + } + + /// Returns a Boolean value indicating whether the given keys are equal. + /// + /// - parameter lhs: The key to compare against. + /// - parameter rhs: The key to compare with. + public static func ==( + lhs: CodingUserInfoKey, + rhs: CodingUserInfoKey + ) -> Bool { + return lhs.rawValue == rhs.rawValue + } + + /// The key's hash value. + public var hashValue: Int { + return self.rawValue.hashValue + } + + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + public func hash(into hasher: inout Hasher) { + hasher.combine(self.rawValue) + } +} + +//===----------------------------------------------------------------------===// +// Errors +//===----------------------------------------------------------------------===// + +/// An error that occurs during the encoding of a value. +public enum EncodingError: Error { + /// The context in which the error occurred. + public struct Context { + /// The path of coding keys taken to get to the point of the failing encode + /// call. + public let codingPath: [CodingKey] + + /// A description of what went wrong, for debugging purposes. + public let debugDescription: String + + /// The underlying error which caused this error, if any. + public let underlyingError: Error? + + /// Creates a new context with the given path of coding keys and a + /// description of what went wrong. + /// + /// - parameter codingPath: The path of coding keys taken to get to the + /// point of the failing encode call. + /// - parameter debugDescription: A description of what went wrong, for + /// debugging purposes. + /// - parameter underlyingError: The underlying error which caused this + /// error, if any. + public init( + codingPath: [CodingKey], + debugDescription: String, + underlyingError: Error? = nil + ) { + self.codingPath = codingPath + self.debugDescription = debugDescription + self.underlyingError = underlyingError + } + } + + /// An indication that an encoder or its containers could not encode the + /// given value. + /// + /// As associated values, this case contains the attempted value and context + /// for debugging. + case invalidValue(Any, Context) + + // MARK: - NSError Bridging + + // CustomNSError bridging applies only when the CustomNSError conformance is + // applied in the same module as the declared error type. Since we cannot + // access CustomNSError (which is defined in Foundation) from here, we can + // use the "hidden" entry points. + + public var _domain: String { + return "NSCocoaErrorDomain" + } + + public var _code: Int { + switch self { + case .invalidValue: return 4866 + } + } + + public var _userInfo: AnyObject? { + // The error dictionary must be returned as an AnyObject. We can do this + // only on platforms with bridging, unfortunately. + #if _runtime(_ObjC) + let context: Context + switch self { + case .invalidValue(_, let c): context = c + } + + var userInfo: [String: Any] = [ + "NSCodingPath": context.codingPath, + "NSDebugDescription": context.debugDescription + ] + + if let underlyingError = context.underlyingError { + userInfo["NSUnderlyingError"] = underlyingError + } + + return userInfo as AnyObject + #else + return nil + #endif + } +} + +/// An error that occurs during the decoding of a value. +public enum DecodingError: Error { + /// The context in which the error occurred. + public struct Context { + /// The path of coding keys taken to get to the point of the failing decode + /// call. + public let codingPath: [CodingKey] + + /// A description of what went wrong, for debugging purposes. + public let debugDescription: String + + /// The underlying error which caused this error, if any. + public let underlyingError: Error? + + /// Creates a new context with the given path of coding keys and a + /// description of what went wrong. + /// + /// - parameter codingPath: The path of coding keys taken to get to the + /// point of the failing decode call. + /// - parameter debugDescription: A description of what went wrong, for + /// debugging purposes. + /// - parameter underlyingError: The underlying error which caused this + /// error, if any. + public init( + codingPath: [CodingKey], + debugDescription: String, + underlyingError: Error? = nil + ) { + self.codingPath = codingPath + self.debugDescription = debugDescription + self.underlyingError = underlyingError + } + } + + /// An indication that a value of the given type could not be decoded because + /// it did not match the type of what was found in the encoded payload. + /// + /// As associated values, this case contains the attempted type and context + /// for debugging. + case typeMismatch(Any.Type, Context) + + /// An indication that a non-optional value of the given type was expected, + /// but a null value was found. + /// + /// As associated values, this case contains the attempted type and context + /// for debugging. + case valueNotFound(Any.Type, Context) + + /// An indication that a keyed decoding container was asked for an entry for + /// the given key, but did not contain one. + /// + /// As associated values, this case contains the attempted key and context + /// for debugging. + case keyNotFound(CodingKey, Context) + + /// An indication that the data is corrupted or otherwise invalid. + /// + /// As an associated value, this case contains the context for debugging. + case dataCorrupted(Context) + + // MARK: - NSError Bridging + + // CustomNSError bridging applies only when the CustomNSError conformance is + // applied in the same module as the declared error type. Since we cannot + // access CustomNSError (which is defined in Foundation) from here, we can + // use the "hidden" entry points. + + public var _domain: String { + return "NSCocoaErrorDomain" + } + + public var _code: Int { + switch self { + case .keyNotFound, .valueNotFound: return 4865 + case .typeMismatch, .dataCorrupted: return 4864 + } + } + + public var _userInfo: AnyObject? { + // The error dictionary must be returned as an AnyObject. We can do this + // only on platforms with bridging, unfortunately. + #if _runtime(_ObjC) + let context: Context + switch self { + case .keyNotFound(_, let c): context = c + case .valueNotFound(_, let c): context = c + case .typeMismatch(_, let c): context = c + case .dataCorrupted( let c): context = c + } + + var userInfo: [String: Any] = [ + "NSCodingPath": context.codingPath, + "NSDebugDescription": context.debugDescription + ] + + if let underlyingError = context.underlyingError { + userInfo["NSUnderlyingError"] = underlyingError + } + + return userInfo as AnyObject + #else + return nil + #endif + } +} + +// The following extensions allow for easier error construction. + +internal struct _GenericIndexKey: CodingKey { + internal var stringValue: String + internal var intValue: Int? + + internal init?(stringValue: String) { + return nil + } + + internal init?(intValue: Int) { + self.stringValue = "Index \(intValue)" + self.intValue = intValue + } +} + +extension DecodingError { + /// Returns a new `.dataCorrupted` error using a constructed coding path and + /// the given debug description. + /// + /// The coding path for the returned error is constructed by appending the + /// given key to the given container's coding path. + /// + /// - param key: The key which caused the failure. + /// - param container: The container in which the corrupted data was + /// accessed. + /// - param debugDescription: A description of the error to aid in debugging. + /// + /// - Returns: A new `.dataCorrupted` error with the given information. + public static func dataCorruptedError( + forKey key: C.Key, + in container: C, + debugDescription: String + ) -> DecodingError { + let context = DecodingError.Context( + codingPath: container.codingPath + [key], + debugDescription: debugDescription) + return .dataCorrupted(context) + } + + /// Returns a new `.dataCorrupted` error using a constructed coding path and + /// the given debug description. + /// + /// The coding path for the returned error is constructed by appending the + /// given container's current index to its coding path. + /// + /// - param container: The container in which the corrupted data was + /// accessed. + /// - param debugDescription: A description of the error to aid in debugging. + /// + /// - Returns: A new `.dataCorrupted` error with the given information. + public static func dataCorruptedError( + in container: UnkeyedDecodingContainer, + debugDescription: String + ) -> DecodingError { + let context = DecodingError.Context( + codingPath: container.codingPath + + [_GenericIndexKey(intValue: container.currentIndex)!], + debugDescription: debugDescription) + return .dataCorrupted(context) + } + + /// Returns a new `.dataCorrupted` error using a constructed coding path and + /// the given debug description. + /// + /// The coding path for the returned error is the given container's coding + /// path. + /// + /// - param container: The container in which the corrupted data was + /// accessed. + /// - param debugDescription: A description of the error to aid in debugging. + /// + /// - Returns: A new `.dataCorrupted` error with the given information. + public static func dataCorruptedError( + in container: SingleValueDecodingContainer, + debugDescription: String + ) -> DecodingError { + let context = DecodingError.Context(codingPath: container.codingPath, + debugDescription: debugDescription) + return .dataCorrupted(context) + } +} + +//===----------------------------------------------------------------------===// +// Keyed Encoding Container Implementations +//===----------------------------------------------------------------------===// + +internal class _KeyedEncodingContainerBase { + internal init(){} + + deinit {} + + // These must all be given a concrete implementation in _*Box. + internal var codingPath: [CodingKey] { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeNil(forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Bool, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: String, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Double, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Float, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Int, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Int8, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Int16, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Int32, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: Int64, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: UInt, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: UInt8, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: UInt16, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: UInt32, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: UInt64, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encode(_ value: T, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeConditional( + _ object: T, + forKey key: Key + ) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: String?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Double?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Float?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Int?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Int32?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: Int64?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: UInt?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func encodeIfPresent( + _ value: T?, + forKey key: Key + ) throws { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func nestedUnkeyedContainer( + forKey key: Key + ) -> UnkeyedEncodingContainer { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func superEncoder() -> Encoder { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } + + internal func superEncoder(forKey key: Key) -> Encoder { + fatalError("_KeyedEncodingContainerBase cannot be used directly.") + } +} + +internal final class _KeyedEncodingContainerBox< + Concrete: KeyedEncodingContainerProtocol +>: _KeyedEncodingContainerBase { + typealias Key = Concrete.Key + + internal var concrete: Concrete + + internal init(_ container: Concrete) { + concrete = container + } + + override internal var codingPath: [CodingKey] { + return concrete.codingPath + } + + override internal func encodeNil(forKey key: Key) throws { + try concrete.encodeNil(forKey: key) + } + + override internal func encode(_ value: Bool, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: String, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Double, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Float, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Int, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Int8, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Int16, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Int32, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: Int64, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: UInt, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: UInt8, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: UInt16, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: UInt32, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode(_ value: UInt64, forKey key: Key) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encode( + _ value: T, + forKey key: Key + ) throws { + try concrete.encode(value, forKey: key) + } + + override internal func encodeConditional( + _ object: T, + forKey key: Key + ) throws { + try concrete.encodeConditional(object, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Bool?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: String?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Double?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Float?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Int?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Int8?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Int16?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Int32?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: Int64?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: UInt?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: UInt8?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: UInt16?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: UInt32?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: UInt64?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func encodeIfPresent( + _ value: T?, + forKey key: Key + ) throws { + try concrete.encodeIfPresent(value, forKey: key) + } + + override internal func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer { + return concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + override internal func nestedUnkeyedContainer( + forKey key: Key + ) -> UnkeyedEncodingContainer { + return concrete.nestedUnkeyedContainer(forKey: key) + } + + override internal func superEncoder() -> Encoder { + return concrete.superEncoder() + } + + override internal func superEncoder(forKey key: Key) -> Encoder { + return concrete.superEncoder(forKey: key) + } +} + +internal class _KeyedDecodingContainerBase { + internal init(){} + + deinit {} + + internal var codingPath: [CodingKey] { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal var allKeys: [Key] { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func contains(_ key: Key) -> Bool { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeNil(forKey key: Key) throws -> Bool { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: String.Type, + forKey key: Key + ) throws -> String { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Double.Type, + forKey key: Key + ) throws -> Double { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Float.Type, + forKey key: Key + ) throws -> Float { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Int.Type, + forKey key: Key + ) throws -> Int { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64 { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decode( + _ type: T.Type, + forKey key: Key + ) throws -> T { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: String.Type, + forKey key: Key + ) throws -> String? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Double.Type, + forKey key: Key + ) throws -> Double? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Float.Type, + forKey key: Key + ) throws -> Float? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Int.Type, + forKey key: Key + ) throws -> Int? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func decodeIfPresent( + _ type: T.Type, + forKey key: Key + ) throws -> T? { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func nestedContainer( + keyedBy type: NestedKey.Type, + forKey key: Key + ) throws -> KeyedDecodingContainer { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func nestedUnkeyedContainer( + forKey key: Key + ) throws -> UnkeyedDecodingContainer { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func superDecoder() throws -> Decoder { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } + + internal func superDecoder(forKey key: Key) throws -> Decoder { + fatalError("_KeyedDecodingContainerBase cannot be used directly.") + } +} + +internal final class _KeyedDecodingContainerBox< + Concrete: KeyedDecodingContainerProtocol +>: _KeyedDecodingContainerBase { + typealias Key = Concrete.Key + + internal var concrete: Concrete + + internal init(_ container: Concrete) { + concrete = container + } + + override var codingPath: [CodingKey] { + return concrete.codingPath + } + + override var allKeys: [Key] { + return concrete.allKeys + } + + override internal func contains(_ key: Key) -> Bool { + return concrete.contains(key) + } + + override internal func decodeNil(forKey key: Key) throws -> Bool { + return try concrete.decodeNil(forKey: key) + } + + override internal func decode( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool { + return try concrete.decode(Bool.self, forKey: key) + } + + override internal func decode( + _ type: String.Type, + forKey key: Key + ) throws -> String { + return try concrete.decode(String.self, forKey: key) + } + + override internal func decode( + _ type: Double.Type, + forKey key: Key + ) throws -> Double { + return try concrete.decode(Double.self, forKey: key) + } + + override internal func decode( + _ type: Float.Type, + forKey key: Key + ) throws -> Float { + return try concrete.decode(Float.self, forKey: key) + } + + override internal func decode( + _ type: Int.Type, + forKey key: Key + ) throws -> Int { + return try concrete.decode(Int.self, forKey: key) + } + + override internal func decode( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8 { + return try concrete.decode(Int8.self, forKey: key) + } + + override internal func decode( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16 { + return try concrete.decode(Int16.self, forKey: key) + } + + override internal func decode( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32 { + return try concrete.decode(Int32.self, forKey: key) + } + + override internal func decode( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64 { + return try concrete.decode(Int64.self, forKey: key) + } + + override internal func decode( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt { + return try concrete.decode(UInt.self, forKey: key) + } + + override internal func decode( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8 { + return try concrete.decode(UInt8.self, forKey: key) + } + + override internal func decode( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16 { + return try concrete.decode(UInt16.self, forKey: key) + } + + override internal func decode( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32 { + return try concrete.decode(UInt32.self, forKey: key) + } + + override internal func decode( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64 { + return try concrete.decode(UInt64.self, forKey: key) + } + + override internal func decode( + _ type: T.Type, + forKey key: Key + ) throws -> T { + return try concrete.decode(T.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool? { + return try concrete.decodeIfPresent(Bool.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: String.Type, + forKey key: Key + ) throws -> String? { + return try concrete.decodeIfPresent(String.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Double.Type, + forKey key: Key + ) throws -> Double? { + return try concrete.decodeIfPresent(Double.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Float.Type, + forKey key: Key + ) throws -> Float? { + return try concrete.decodeIfPresent(Float.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Int.Type, + forKey key: Key + ) throws -> Int? { + return try concrete.decodeIfPresent(Int.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8? { + return try concrete.decodeIfPresent(Int8.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16? { + return try concrete.decodeIfPresent(Int16.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32? { + return try concrete.decodeIfPresent(Int32.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64? { + return try concrete.decodeIfPresent(Int64.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt? { + return try concrete.decodeIfPresent(UInt.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8? { + return try concrete.decodeIfPresent(UInt8.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16? { + return try concrete.decodeIfPresent(UInt16.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32? { + return try concrete.decodeIfPresent(UInt32.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64? { + return try concrete.decodeIfPresent(UInt64.self, forKey: key) + } + + override internal func decodeIfPresent( + _ type: T.Type, + forKey key: Key + ) throws -> T? { + return try concrete.decodeIfPresent(T.self, forKey: key) + } + + override internal func nestedContainer( + keyedBy type: NestedKey.Type, + forKey key: Key + ) throws -> KeyedDecodingContainer { + return try concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) + } + + override internal func nestedUnkeyedContainer( + forKey key: Key + ) throws -> UnkeyedDecodingContainer { + return try concrete.nestedUnkeyedContainer(forKey: key) + } + + override internal func superDecoder() throws -> Decoder { + return try concrete.superDecoder() + } + + override internal func superDecoder(forKey key: Key) throws -> Decoder { + return try concrete.superDecoder(forKey: key) + } +} + +//===----------------------------------------------------------------------===// +// Primitive and RawRepresentable Extensions +//===----------------------------------------------------------------------===// + +extension Bool: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Bool.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Bool, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Bool`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Bool, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Bool`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension String: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(String.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == String, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `String`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == String, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `String`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Double: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Double.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Double, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Double`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Double, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Double`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Float: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Float.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Float, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Float`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Float, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Float`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Int: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Int, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Int`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Int, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Int`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Int8: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int8.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Int8, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Int8`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Int8, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Int8`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Int16: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int16.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Int16, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Int16`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Int16, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Int16`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Int32: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int32.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Int32, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Int32`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Int32, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Int32`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension Int64: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Int64.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == Int64, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `Int64`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == Int64, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `Int64`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension UInt: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == UInt, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `UInt`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == UInt, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `UInt`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension UInt8: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt8.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == UInt8, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `UInt8`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == UInt8, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `UInt8`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension UInt16: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt16.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == UInt16, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `UInt16`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == UInt16, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `UInt16`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension UInt32: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt32.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == UInt32, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `UInt32`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == UInt32, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `UInt32`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +extension UInt64: Codable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(UInt64.self) + } + + /// Encodes this value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self) + } +} + +extension RawRepresentable where RawValue == UInt64, Self: Encodable { + /// Encodes this value into the given encoder, when the type's `RawValue` + /// is `UInt64`. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.rawValue) + } +} + +extension RawRepresentable where RawValue == UInt64, Self: Decodable { + /// Creates a new instance by decoding from the given decoder, when the + /// type's `RawValue` is `UInt64`. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)" + ) + ) + } + + self = value + } +} + +//===----------------------------------------------------------------------===// +// Optional/Collection Type Conformances +//===----------------------------------------------------------------------===// + +extension Optional: Encodable where Wrapped: Encodable { + /// Encodes this optional value into the given encoder. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .none: try container.encodeNil() + case .some(let wrapped): try container.encode(wrapped) + } + } +} + +extension Optional: Decodable where Wrapped: Decodable { + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if container.decodeNil() { + self = .none + } else { + let element = try container.decode(Wrapped.self) + self = .some(element) + } + } +} + +extension Array: Encodable where Element: Encodable { + /// Encodes the elements of this array into the given encoder in an unkeyed + /// container. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + for element in self { + try container.encode(element) + } + } +} + +extension Array: Decodable where Element: Decodable { + /// Creates a new array by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self.init() + + var container = try decoder.unkeyedContainer() + while !container.isAtEnd { + let element = try container.decode(Element.self) + self.append(element) + } + } +} + +extension ContiguousArray: Encodable where Element: Encodable { + /// Encodes the elements of this contiguous array into the given encoder + /// in an unkeyed container. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + for element in self { + try container.encode(element) + } + } +} + +extension ContiguousArray: Decodable where Element: Decodable { + /// Creates a new contiguous array by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self.init() + + var container = try decoder.unkeyedContainer() + while !container.isAtEnd { + let element = try container.decode(Element.self) + self.append(element) + } + } +} + +extension Set: Encodable where Element: Encodable { + /// Encodes the elements of this set into the given encoder in an unkeyed + /// container. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + for element in self { + try container.encode(element) + } + } +} + +extension Set: Decodable where Element: Decodable { + /// Creates a new set by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self.init() + + var container = try decoder.unkeyedContainer() + while !container.isAtEnd { + let element = try container.decode(Element.self) + self.insert(element) + } + } +} + +/// A wrapper for dictionary keys which are Strings or Ints. +internal struct _DictionaryCodingKey: CodingKey { + internal let stringValue: String + internal let intValue: Int? + + internal init?(stringValue: String) { + self.stringValue = stringValue + self.intValue = Int(stringValue) + } + + internal init?(intValue: Int) { + self.stringValue = "\(intValue)" + self.intValue = intValue + } +} + +extension Dictionary: Encodable where Key: Encodable, Value: Encodable { + /// Encodes the contents of this dictionary into the given encoder. + /// + /// If the dictionary uses `String` or `Int` keys, the contents are encoded + /// in a keyed container. Otherwise, the contents are encoded as alternating + /// key-value pairs in an unkeyed container. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + public func encode(to encoder: Encoder) throws { + if Key.self == String.self { + // Since the keys are already Strings, we can use them as keys directly. + var container = encoder.container(keyedBy: _DictionaryCodingKey.self) + for (key, value) in self { + let codingKey = _DictionaryCodingKey(stringValue: key as! String)! + try container.encode(value, forKey: codingKey) + } + } else if Key.self == Int.self { + // Since the keys are already Ints, we can use them as keys directly. + var container = encoder.container(keyedBy: _DictionaryCodingKey.self) + for (key, value) in self { + let codingKey = _DictionaryCodingKey(intValue: key as! Int)! + try container.encode(value, forKey: codingKey) + } + } else { + // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily + // convert to keys. We can encode as an array of alternating key-value + // pairs, though. + var container = encoder.unkeyedContainer() + for (key, value) in self { + try container.encode(key) + try container.encode(value) + } + } + } +} + +extension Dictionary: Decodable where Key: Decodable, Value: Decodable { + /// Creates a new dictionary by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + public init(from decoder: Decoder) throws { + self.init() + + if Key.self == String.self { + // The keys are Strings, so we should be able to expect a keyed container. + let container = try decoder.container(keyedBy: _DictionaryCodingKey.self) + for key in container.allKeys { + let value = try container.decode(Value.self, forKey: key) + self[key.stringValue as! Key] = value + } + } else if Key.self == Int.self { + // The keys are Ints, so we should be able to expect a keyed container. + let container = try decoder.container(keyedBy: _DictionaryCodingKey.self) + for key in container.allKeys { + guard key.intValue != nil else { + // We provide stringValues for Int keys; if an encoder chooses not to + // use the actual intValues, we've encoded string keys. + // So on init, _DictionaryCodingKey tries to parse string keys as + // Ints. If that succeeds, then we would have had an intValue here. + // We don't, so this isn't a valid Int key. + var codingPath = decoder.codingPath + codingPath.append(key) + throw DecodingError.typeMismatch( + Int.self, + DecodingError.Context( + codingPath: codingPath, + debugDescription: "Expected Int key but found String key instead." + ) + ) + } + + let value = try container.decode(Value.self, forKey: key) + self[key.intValue! as! Key] = value + } + } else { + // We should have encoded as an array of alternating key-value pairs. + var container = try decoder.unkeyedContainer() + + // We're expecting to get pairs. If the container has a known count, it + // had better be even; no point in doing work if not. + if let count = container.count { + guard count % 2 == 0 else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected collection of key-value pairs; encountered odd-length array instead." + ) + ) + } + } + + while !container.isAtEnd { + let key = try container.decode(Key.self) + + guard !container.isAtEnd else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Unkeyed container reached end before value in key-value pair." + ) + ) + } + + let value = try container.decode(Value.self) + self[key] = value + } + } + } +} + +//===----------------------------------------------------------------------===// +// Convenience Default Implementations +//===----------------------------------------------------------------------===// + +// Default implementation of encodeConditional(_:forKey:) in terms of +// encode(_:forKey:) +extension KeyedEncodingContainerProtocol { + public mutating func encodeConditional( + _ object: T, + forKey key: Key + ) throws { + try encode(object, forKey: key) + } +} + +// Default implementation of encodeIfPresent(_:forKey:) in terms of +// encode(_:forKey:) +extension KeyedEncodingContainerProtocol { + public mutating func encodeIfPresent( + _ value: Bool?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: String?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Double?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Float?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Int?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Int8?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Int16?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Int32?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: Int64?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: UInt?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: UInt8?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: UInt16?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: UInt32?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: UInt64?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } + + public mutating func encodeIfPresent( + _ value: T?, + forKey key: Key + ) throws { + guard let value = value else { return } + try encode(value, forKey: key) + } +} + +// Default implementation of decodeIfPresent(_:forKey:) in terms of +// decode(_:forKey:) and decodeNil(forKey:) +extension KeyedDecodingContainerProtocol { + public func decodeIfPresent( + _ type: Bool.Type, + forKey key: Key + ) throws -> Bool? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Bool.self, forKey: key) + } + + public func decodeIfPresent( + _ type: String.Type, + forKey key: Key + ) throws -> String? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(String.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Double.Type, + forKey key: Key + ) throws -> Double? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Double.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Float.Type, + forKey key: Key + ) throws -> Float? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Float.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Int.Type, + forKey key: Key + ) throws -> Int? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Int.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Int8.Type, + forKey key: Key + ) throws -> Int8? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Int8.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Int16.Type, + forKey key: Key + ) throws -> Int16? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Int16.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Int32.Type, + forKey key: Key + ) throws -> Int32? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Int32.self, forKey: key) + } + + public func decodeIfPresent( + _ type: Int64.Type, + forKey key: Key + ) throws -> Int64? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(Int64.self, forKey: key) + } + + public func decodeIfPresent( + _ type: UInt.Type, + forKey key: Key + ) throws -> UInt? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(UInt.self, forKey: key) + } + + public func decodeIfPresent( + _ type: UInt8.Type, + forKey key: Key + ) throws -> UInt8? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(UInt8.self, forKey: key) + } + + public func decodeIfPresent( + _ type: UInt16.Type, + forKey key: Key + ) throws -> UInt16? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(UInt16.self, forKey: key) + } + + public func decodeIfPresent( + _ type: UInt32.Type, + forKey key: Key + ) throws -> UInt32? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(UInt32.self, forKey: key) + } + + public func decodeIfPresent( + _ type: UInt64.Type, + forKey key: Key + ) throws -> UInt64? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(UInt64.self, forKey: key) + } + + public func decodeIfPresent( + _ type: T.Type, + forKey key: Key + ) throws -> T? { + guard try self.contains(key) && !self.decodeNil(forKey: key) + else { return nil } + return try self.decode(T.self, forKey: key) + } +} + +// Default implementation of encodeConditional(_:) in terms of encode(_:), +// and encode(contentsOf:) in terms of encode(_:) loop. +extension UnkeyedEncodingContainer { + public mutating func encodeConditional( + _ object: T + ) throws { + try self.encode(object) + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Bool { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == String { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Double { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Float { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int8 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int16 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int32 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == Int64 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt8 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt16 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt32 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element == UInt64 { + for element in sequence { + try self.encode(element) + } + } + + public mutating func encode( + contentsOf sequence: T + ) throws where T.Element: Encodable { + for element in sequence { + try self.encode(element) + } + } +} + +// Default implementation of decodeIfPresent(_:) in terms of decode(_:) and +// decodeNil() +extension UnkeyedDecodingContainer { + public mutating func decodeIfPresent( + _ type: Bool.Type + ) throws -> Bool? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Bool.self) + } + + public mutating func decodeIfPresent( + _ type: String.Type + ) throws -> String? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(String.self) + } + + public mutating func decodeIfPresent( + _ type: Double.Type + ) throws -> Double? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Double.self) + } + + public mutating func decodeIfPresent( + _ type: Float.Type + ) throws -> Float? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Float.self) + } + + public mutating func decodeIfPresent( + _ type: Int.Type + ) throws -> Int? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Int.self) + } + + public mutating func decodeIfPresent( + _ type: Int8.Type + ) throws -> Int8? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Int8.self) + } + + public mutating func decodeIfPresent( + _ type: Int16.Type + ) throws -> Int16? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Int16.self) + } + + public mutating func decodeIfPresent( + _ type: Int32.Type + ) throws -> Int32? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Int32.self) + } + + public mutating func decodeIfPresent( + _ type: Int64.Type + ) throws -> Int64? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(Int64.self) + } + + public mutating func decodeIfPresent( + _ type: UInt.Type + ) throws -> UInt? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(UInt.self) + } + + public mutating func decodeIfPresent( + _ type: UInt8.Type + ) throws -> UInt8? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(UInt8.self) + } + + public mutating func decodeIfPresent( + _ type: UInt16.Type + ) throws -> UInt16? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(UInt16.self) + } + + public mutating func decodeIfPresent( + _ type: UInt32.Type + ) throws -> UInt32? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(UInt32.self) + } + + public mutating func decodeIfPresent( + _ type: UInt64.Type + ) throws -> UInt64? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(UInt64.self) + } + + public mutating func decodeIfPresent( + _ type: T.Type + ) throws -> T? { + guard try !self.isAtEnd && !self.decodeNil() else { return nil } + return try self.decode(T.self) + } +} diff --git a/stdlib/public/core/Codable.swift.gyb b/stdlib/public/core/Codable.swift.gyb deleted file mode 100644 index 582e2bf9c92e5..0000000000000 --- a/stdlib/public/core/Codable.swift.gyb +++ /dev/null @@ -1,2069 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -%{ -codable_types = ['Bool', 'String', 'Double', 'Float', - 'Int', 'Int8', 'Int16', 'Int32', 'Int64', - 'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'] -}% - -//===----------------------------------------------------------------------===// -// Codable -//===----------------------------------------------------------------------===// - -/// A type that can encode itself to an external representation. -public protocol Encodable { - /// Encodes this value into the given encoder. - /// - /// If the value fails to encode anything, `encoder` will encode an empty - /// keyed container in its place. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - func encode(to encoder: Encoder) throws -} - -/// A type that can decode itself from an external representation. -public protocol Decodable { - /// Creates a new instance by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - init(from decoder: Decoder) throws -} - -/// A type that can convert itself into and out of an external representation. -/// -/// `Codable` is a type alias for the `Encodable` and `Decodable` protocols. -/// When you use `Codable` as a type or a generic constraint, it matches -/// any type that conforms to both protocols. -public typealias Codable = Encodable & Decodable - -//===----------------------------------------------------------------------===// -// CodingKey -//===----------------------------------------------------------------------===// - -/// A type that can be used as a key for encoding and decoding. -public protocol CodingKey : - CustomStringConvertible, CustomDebugStringConvertible -{ - /// The string to use in a named collection (e.g. a string-keyed dictionary). - var stringValue: String { get } - - /// Creates a new instance from the given string. - /// - /// If the string passed as `stringValue` does not correspond to any instance - /// of this type, the result is `nil`. - /// - /// - parameter stringValue: The string value of the desired key. - init?(stringValue: String) - - /// The value to use in an integer-indexed collection (e.g. an int-keyed - /// dictionary). - var intValue: Int? { get } - - /// Creates a new instance from the specified integer. - /// - /// If the value passed as `intValue` does not correspond to any instance of - /// this type, the result is `nil`. - /// - /// - parameter intValue: The integer value of the desired key. - init?(intValue: Int) -} - -extension CodingKey { - /// A textual representation of this key. - public var description: String { - let intValue = self.intValue?.description ?? "nil" - return "\(type(of: self))(stringValue: \"\(stringValue)\", intValue: \(intValue))" - } - - /// A textual representation of this key, suitable for debugging. - public var debugDescription: String { - return description - } -} - -//===----------------------------------------------------------------------===// -// Encoder & Decoder -//===----------------------------------------------------------------------===// - -/// A type that can encode values into a native format for external -/// representation. -public protocol Encoder { - /// The path of coding keys taken to get to this point in encoding. - var codingPath: [CodingKey] { get } - - /// Any contextual information set by the user for encoding. - var userInfo: [CodingUserInfoKey: Any] { get } - - /// Returns an encoding container appropriate for holding multiple values - /// keyed by the given key type. - /// - /// You must use only one kind of top-level encoding container. This method - /// must not be called after a call to `unkeyedContainer()` or after - /// encoding a value through a call to `singleValueContainer()` - /// - /// - parameter type: The key type to use for the container. - /// - returns: A new keyed encoding container. - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer - - /// Returns an encoding container appropriate for holding multiple unkeyed - /// values. - /// - /// You must use only one kind of top-level encoding container. This method - /// must not be called after a call to `container(keyedBy:)` or after - /// encoding a value through a call to `singleValueContainer()` - /// - /// - returns: A new empty unkeyed container. - func unkeyedContainer() -> UnkeyedEncodingContainer - - /// Returns an encoding container appropriate for holding a single primitive - /// value. - /// - /// You must use only one kind of top-level encoding container. This method - /// must not be called after a call to `unkeyedContainer()` or - /// `container(keyedBy:)`, or after encoding a value through a call to - /// `singleValueContainer()` - /// - /// - returns: A new empty single value container. - func singleValueContainer() -> SingleValueEncodingContainer -} - -/// A type that can decode values from a native format into in-memory -/// representations. -public protocol Decoder { - /// The path of coding keys taken to get to this point in decoding. - var codingPath: [CodingKey] { get } - - /// Any contextual information set by the user for decoding. - var userInfo: [CodingUserInfoKey: Any] { get } - - /// Returns the data stored in this decoder as represented in a container - /// keyed by the given key type. - /// - /// - parameter type: The key type to use for the container. - /// - returns: A keyed decoding container view into this decoder. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not a keyed container. - func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer - - /// Returns the data stored in this decoder as represented in a container - /// appropriate for holding values with no keys. - /// - /// - returns: An unkeyed container view into this decoder. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not an unkeyed container. - func unkeyedContainer() throws -> UnkeyedDecodingContainer - - /// Returns the data stored in this decoder as represented in a container - /// appropriate for holding a single primitive value. - /// - /// - returns: A single value container view into this decoder. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not a single value container. - func singleValueContainer() throws -> SingleValueDecodingContainer -} - -//===----------------------------------------------------------------------===// -// Keyed Encoding Containers -//===----------------------------------------------------------------------===// - -/// A type that provides a view into an encoder's storage and is used to hold -/// the encoded properties of an encodable type in a keyed manner. -/// -/// Encoders should provide types conforming to -/// `KeyedEncodingContainerProtocol` for their format. -public protocol KeyedEncodingContainerProtocol { - associatedtype Key: CodingKey - - /// The path of coding keys taken to get to this point in encoding. - var codingPath: [CodingKey] { get } - - /// Encodes a null value for the given key. - /// - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if a null value is invalid in the - /// current context for this format. - mutating func encodeNil(forKey key: Key) throws - -% for type in codable_types: - /// Encodes the given value for the given key. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encode(_ value: ${type}, forKey key: Key) throws -% end - - /// Encodes the given value for the given key. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encode(_ value: T, forKey key: Key) throws - - /// Encodes a reference to the given object only if it is encoded - /// unconditionally elsewhere in the payload (previously, or in the future). - /// - /// For encoders which don't support this feature, the default implementation - /// encodes the given object unconditionally. - /// - /// - parameter object: The object to encode. - /// - parameter key: The key to associate the object with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encodeConditional( - _ object: T, forKey key: Key) throws - -% for type in codable_types: - /// Encodes the given value for the given key if it is not `nil`. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encodeIfPresent(_ value: ${type}?, forKey key: Key) throws -% end - - /// Encodes the given value for the given key if it is not `nil`. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encodeIfPresent( - _ value: T?, forKey key: Key) throws - - /// Stores a keyed encoding container for the given key and returns it. - /// - /// - parameter keyType: The key type to use for the container. - /// - parameter key: The key to encode the container for. - /// - returns: A new keyed encoding container. - mutating func nestedContainer( - keyedBy keyType: NestedKey.Type, forKey key: Key - ) -> KeyedEncodingContainer - - /// Stores an unkeyed encoding container for the given key and returns it. - /// - /// - parameter key: The key to encode the container for. - /// - returns: A new unkeyed encoding container. - mutating func nestedUnkeyedContainer( - forKey key: Key) -> UnkeyedEncodingContainer - - /// Stores a new nested container for the default `super` key and returns A - /// new encoder instance for encoding `super` into that container. - /// - /// Equivalent to calling `superEncoder(forKey:)` with - /// `Key(stringValue: "super", intValue: 0)`. - /// - /// - returns: A new encoder to pass to `super.encode(to:)`. - mutating func superEncoder() -> Encoder - - /// Stores a new nested container for the given key and returns A new encoder - /// instance for encoding `super` into that container. - /// - /// - parameter key: The key to encode `super` for. - /// - returns: A new encoder to pass to `super.encode(to:)`. - mutating func superEncoder(forKey key: Key) -> Encoder -} - -// An implementation of _KeyedEncodingContainerBase and -// _KeyedEncodingContainerBox are given at the bottom of this file. - -/// A concrete container that provides a view into an encoder's storage, making -/// the encoded properties of an encodable type accessible by keys. -public struct KeyedEncodingContainer : - KeyedEncodingContainerProtocol -{ - public typealias Key = K - - /// The container for the concrete encoder. The type is _*Base so that it's - /// generic on the key type. - internal var _box: _KeyedEncodingContainerBase - - /// Creates a new instance with the given container. - /// - /// - parameter container: The container to hold. - public init( - _ container: Container) where Container.Key == Key - { - _box = _KeyedEncodingContainerBox(container) - } - - /// The path of coding keys taken to get to this point in encoding. - public var codingPath: [CodingKey] { - return _box.codingPath - } - - /// Encodes a null value for the given key. - /// - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if a null value is invalid in the - /// current context for this format. - public mutating func encodeNil(forKey key: Key) throws { - try _box.encodeNil(forKey: key) - } - -% for type in codable_types: - /// Encodes the given value for the given key. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - public mutating func encode(_ value: ${type}, forKey key: Key) throws { - try _box.encode(value, forKey: key) - } -% end - - /// Encodes the given value for the given key. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - public mutating func encode( - _ value: T, forKey key: Key) throws - { - try _box.encode(value, forKey: key) - } - - /// Encodes a reference to the given object only if it is encoded - /// unconditionally elsewhere in the payload (previously, or in the future). - /// - /// For encoders which don't support this feature, the default implementation - /// encodes the given object unconditionally. - /// - /// - parameter object: The object to encode. - /// - parameter key: The key to associate the object with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - public mutating func encodeConditional( - _ object: T, forKey key: Key) throws - { - try _box.encodeConditional(object, forKey: key) - } - -% for type in codable_types: - /// Encodes the given value for the given key if it is not `nil`. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - public mutating func encodeIfPresent( - _ value: ${type}?, forKey key: Key) throws - { - try _box.encodeIfPresent(value, forKey: key) - } -% end - - /// Encodes the given value for the given key if it is not `nil`. - /// - /// - parameter value: The value to encode. - /// - parameter key: The key to associate the value with. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - public mutating func encodeIfPresent( - _ value: T?, forKey key: Key) throws - { - try _box.encodeIfPresent(value, forKey: key) - } - - /// Stores a keyed encoding container for the given key and returns it. - /// - /// - parameter keyType: The key type to use for the container. - /// - parameter key: The key to encode the container for. - /// - returns: A new keyed encoding container. - public mutating func nestedContainer( - keyedBy keyType: NestedKey.Type, forKey key: Key - ) -> KeyedEncodingContainer { - return _box.nestedContainer(keyedBy: NestedKey.self, forKey: key) - } - - /// Stores an unkeyed encoding container for the given key and returns it. - /// - /// - parameter key: The key to encode the container for. - /// - returns: A new unkeyed encoding container. - public mutating func nestedUnkeyedContainer( - forKey key: Key) -> UnkeyedEncodingContainer - { - return _box.nestedUnkeyedContainer(forKey: key) - } - - /// Stores a new nested container for the default `super` key and returns A - /// new encoder instance for encoding `super` into that container. - /// - /// Equivalent to calling `superEncoder(forKey:)` with - /// `Key(stringValue: "super", intValue: 0)`. - /// - /// - returns: A new encoder to pass to `super.encode(to:)`. - public mutating func superEncoder() -> Encoder { - return _box.superEncoder() - } - - /// Stores a new nested container for the given key and returns A new encoder - /// instance for encoding `super` into that container. - /// - /// - parameter key: The key to encode `super` for. - /// - returns: A new encoder to pass to `super.encode(to:)`. - public mutating func superEncoder(forKey key: Key) -> Encoder { - return _box.superEncoder(forKey: key) - } -} - -/// A type that provides a view into a decoder's storage and is used to hold -/// the encoded properties of a decodable type in a keyed manner. -/// -/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for -/// their format. -public protocol KeyedDecodingContainerProtocol { - associatedtype Key: CodingKey - - /// The path of coding keys taken to get to this point in decoding. - var codingPath: [CodingKey] { get } - - /// All the keys the `Decoder` has for this container. - /// - /// Different keyed containers from the same `Decoder` may return different - /// keys here; it is possible to encode with multiple key types which are - /// not convertible to one another. This should report all keys present - /// which are convertible to the requested type. - var allKeys: [Key] { get } - - /// Returns a Boolean value indicating whether the decoder contains a value - /// associated with the given key. - /// - /// The value associated with `key` may be a null value as appropriate for - /// the data format. - /// - /// - parameter key: The key to search for. - /// - returns: Whether the `Decoder` has an entry for the given key. - func contains(_ key: Key) -> Bool - - /// Decodes a null value for the given key. - /// - /// - parameter key: The key that the decoded value is associated with. - /// - returns: Whether the encountered value was null. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - func decodeNil(forKey key: Key) throws -> Bool - -% for type in codable_types: - /// Decodes a value of the given type for the given key. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - func decode(_ type: ${type}.Type, forKey key: Key) throws -> ${type} -% end - - /// Decodes a value of the given type for the given key. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - func decode(_ type: T.Type, forKey key: Key) throws -> T - -% for type in codable_types: - /// Decodes a value of the given type for the given key, if present. - /// - /// This method returns `nil` if the container does not have a value - /// associated with `key`, or if the value is null. The difference between - /// these states can be distinguished with a `contains(_:)` call. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A decoded value of the requested type, or `nil` if the - /// `Decoder` does not have an entry associated with the given key, or if - /// the value is a null value. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - func decodeIfPresent(_ type: ${type}.Type, forKey key: Key) throws -> ${type}? -% end - - /// Decodes a value of the given type for the given key, if present. - /// - /// This method returns `nil` if the container does not have a value - /// associated with `key`, or if the value is null. The difference between - /// these states can be distinguished with a `contains(_:)` call. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A decoded value of the requested type, or `nil` if the - /// `Decoder` does not have an entry associated with the given key, or if - /// the value is a null value. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - func decodeIfPresent( - _ type: T.Type, forKey key: Key) throws -> T? - - /// Returns the data stored for the given key as represented in a container - /// keyed by the given key type. - /// - /// - parameter type: The key type to use for the container. - /// - parameter key: The key that the nested container is associated with. - /// - returns: A keyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not a keyed container. - func nestedContainer( - keyedBy type: NestedKey.Type, forKey key: Key - ) throws -> KeyedDecodingContainer - - /// Returns the data stored for the given key as represented in an unkeyed - /// container. - /// - /// - parameter key: The key that the nested container is associated with. - /// - returns: An unkeyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not an unkeyed container. - func nestedUnkeyedContainer( - forKey key: Key) throws -> UnkeyedDecodingContainer - - /// Returns a `Decoder` instance for decoding `super` from the container - /// associated with the default `super` key. - /// - /// Equivalent to calling `superDecoder(forKey:)` with - /// `Key(stringValue: "super", intValue: 0)`. - /// - /// - returns: A new `Decoder` to pass to `super.init(from:)`. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the default `super` key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the default `super` key. - func superDecoder() throws -> Decoder - - /// Returns a `Decoder` instance for decoding `super` from the container - /// associated with the given key. - /// - /// - parameter key: The key to decode `super` for. - /// - returns: A new `Decoder` to pass to `super.init(from:)`. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - func superDecoder(forKey key: Key) throws -> Decoder -} - -// An implementation of _KeyedDecodingContainerBase and -// _KeyedDecodingContainerBox are given at the bottom of this file. - -/// A concrete container that provides a view into a decoder's storage, making -/// the encoded properties of a decodable type accessible by keys. -public struct KeyedDecodingContainer : - KeyedDecodingContainerProtocol -{ - public typealias Key = K - - /// The container for the concrete decoder. The type is _*Base so that it's - /// generic on the key type. - internal var _box: _KeyedDecodingContainerBase - - /// Creates a new instance with the given container. - /// - /// - parameter container: The container to hold. - public init( - _ container: Container) where Container.Key == Key - { - _box = _KeyedDecodingContainerBox(container) - } - - /// The path of coding keys taken to get to this point in decoding. - public var codingPath: [CodingKey] { - return _box.codingPath - } - - /// All the keys the decoder has for this container. - /// - /// Different keyed containers from the same decoder may return different - /// keys here, because it is possible to encode with multiple key types - /// which are not convertible to one another. This should report all keys - /// present which are convertible to the requested type. - public var allKeys: [Key] { - return _box.allKeys - } - - /// Returns a Boolean value indicating whether the decoder contains a value - /// associated with the given key. - /// - /// The value associated with the given key may be a null value as - /// appropriate for the data format. - /// - /// - parameter key: The key to search for. - /// - returns: Whether the `Decoder` has an entry for the given key. - public func contains(_ key: Key) -> Bool { - return _box.contains(key) - } - - /// Decodes a null value for the given key. - /// - /// - parameter key: The key that the decoded value is associated with. - /// - returns: Whether the encountered value was null. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - public func decodeNil(forKey key: Key) throws -> Bool { - return try _box.decodeNil(forKey: key) - } - -% for type in codable_types: - /// Decodes a value of the given type for the given key. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - public func decode(_ type: ${type}.Type, forKey key: Key) throws -> ${type} { - return try _box.decode(${type}.self, forKey: key) - } -% end - - /// Decodes a value of the given type for the given key. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - public func decode(_ type: T.Type, forKey key: Key) throws -> T { - return try _box.decode(T.self, forKey: key) - } - -% for type in codable_types: - /// Decodes a value of the given type for the given key, if present. - /// - /// This method returns `nil` if the container does not have a value - /// associated with `key`, or if the value is null. The difference between - /// these states can be distinguished with a `contains(_:)` call. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A decoded value of the requested type, or `nil` if the - /// `Decoder` does not have an entry associated with the given key, or if - /// the value is a null value. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - public func decodeIfPresent( - _ type: ${type}.Type, forKey key: Key) throws -> ${type}? - { - return try _box.decodeIfPresent(${type}.self, forKey: key) - } -% end - - /// Decodes a value of the given type for the given key, if present. - /// - /// This method returns `nil` if the container does not have a value - /// associated with `key`, or if the value is null. The difference between - /// these states can be distinguished with a `contains(_:)` call. - /// - /// - parameter type: The type of value to decode. - /// - parameter key: The key that the decoded value is associated with. - /// - returns: A decoded value of the requested type, or `nil` if the - /// `Decoder` does not have an entry associated with the given key, or if - /// the value is a null value. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - public func decodeIfPresent( - _ type: T.Type, forKey key: Key) throws -> T? - { - return try _box.decodeIfPresent(T.self, forKey: key) - } - - /// Returns the data stored for the given key as represented in a container - /// keyed by the given key type. - /// - /// - parameter type: The key type to use for the container. - /// - parameter key: The key that the nested container is associated with. - /// - returns: A keyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not a keyed container. - public func nestedContainer( - keyedBy type: NestedKey.Type, forKey key: Key - ) throws -> KeyedDecodingContainer { - return try _box.nestedContainer(keyedBy: NestedKey.self, forKey: key) - } - - /// Returns the data stored for the given key as represented in an unkeyed - /// container. - /// - /// - parameter key: The key that the nested container is associated with. - /// - returns: An unkeyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not an unkeyed container. - public func nestedUnkeyedContainer( - forKey key: Key) throws -> UnkeyedDecodingContainer - { - return try _box.nestedUnkeyedContainer(forKey: key) - } - - /// Returns a `Decoder` instance for decoding `super` from the container - /// associated with the default `super` key. - /// - /// Equivalent to calling `superDecoder(forKey:)` with - /// `Key(stringValue: "super", intValue: 0)`. - /// - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the default `super` key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the default `super` key. - public func superDecoder() throws -> Decoder { - return try _box.superDecoder() - } - - /// Returns a `Decoder` instance for decoding `super` from the container - /// associated with the given key. - /// - /// - parameter key: The key to decode `super` for. - /// - returns: A new `Decoder` to pass to `super.init(from:)`. - /// - throws: `DecodingError.keyNotFound` if `self` does not have an entry - /// for the given key. - /// - throws: `DecodingError.valueNotFound` if `self` has a null entry for - /// the given key. - public func superDecoder(forKey key: Key) throws -> Decoder { - return try _box.superDecoder(forKey: key) - } -} - -//===----------------------------------------------------------------------===// -// Unkeyed Encoding Containers -//===----------------------------------------------------------------------===// - -/// A type that provides a view into an encoder's storage and is used to hold -/// the encoded properties of an encodable type sequentially, without keys. -/// -/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for -/// their format. -public protocol UnkeyedEncodingContainer { - /// The path of coding keys taken to get to this point in encoding. - var codingPath: [CodingKey] { get } - - /// The number of elements encoded into the container. - var count: Int { get } - - /// Encodes a null value. - /// - /// - throws: `EncodingError.invalidValue` if a null value is invalid in the - /// current context for this format. - mutating func encodeNil() throws - -% for type in codable_types: - /// Encodes the given value. - /// - /// - parameter value: The value to encode. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encode(_ value: ${type}) throws -% end - - /// Encodes the given value. - /// - /// - parameter value: The value to encode. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encode(_ value: T) throws - - /// Encodes a reference to the given object only if it is encoded - /// unconditionally elsewhere in the payload (previously, or in the future). - /// - /// For encoders which don't support this feature, the default implementation - /// encodes the given object unconditionally. - /// - /// For formats which don't support this feature, the default implementation - /// encodes the given object unconditionally. - /// - /// - parameter object: The object to encode. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - mutating func encodeConditional(_ object: T) throws - -% for type in codable_types: - /// Encodes the elements of the given sequence. - /// - /// - parameter sequence: The sequences whose contents to encode. - /// - throws: An error if any of the contained values throws an error. - mutating func encode( - contentsOf sequence: T) throws where T.Element == ${type} -% end - - /// Encodes the elements of the given sequence. - /// - /// - parameter sequence: The sequences whose contents to encode. - /// - throws: An error if any of the contained values throws an error. - mutating func encode( - contentsOf sequence: T) throws where T.Element: Encodable - - /// Encodes a nested container keyed by the given type and returns it. - /// - /// - parameter keyType: The key type to use for the container. - /// - returns: A new keyed encoding container. - mutating func nestedContainer( - keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer - - /// Encodes an unkeyed encoding container and returns it. - /// - /// - returns: A new unkeyed encoding container. - mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer - - /// Encodes a nested container and returns an `Encoder` instance for encoding - /// `super` into that container. - /// - /// - returns: A new encoder to pass to `super.encode(to:)`. - mutating func superEncoder() -> Encoder -} - -/// A type that provides a view into a decoder's storage and is used to hold -/// the encoded properties of a decodable type sequentially, without keys. -/// -/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for -/// their format. -public protocol UnkeyedDecodingContainer { - /// The path of coding keys taken to get to this point in decoding. - var codingPath: [CodingKey] { get } - - /// The number of elements contained within this container. - /// - /// If the number of elements is unknown, the value is `nil`. - var count: Int? { get } - - /// A Boolean value indicating whether there are no more elements left to be - /// decoded in the container. - var isAtEnd: Bool { get } - - /// The current decoding index of the container (i.e. the index of the next - /// element to be decoded.) Incremented after every successful decode call. - var currentIndex: Int { get } - - /// Decodes a null value. - /// - /// If the value is not null, does not increment currentIndex. - /// - /// - returns: Whether the encountered value was null. - /// - throws: `DecodingError.valueNotFound` if there are no more values to - /// decode. - mutating func decodeNil() throws -> Bool - -% for type in codable_types: - /// Decodes a value of the given type. - /// - /// - parameter type: The type of value to decode. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.valueNotFound` if the encountered encoded value - /// is null, or of there are no more values to decode. - mutating func decode(_ type: ${type}.Type) throws -> ${type} -% end - - /// Decodes a value of the given type. - /// - /// - parameter type: The type of value to decode. - /// - returns: A value of the requested type, if present for the given key - /// and convertible to the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - /// - throws: `DecodingError.valueNotFound` if the encountered encoded value - /// is null, or of there are no more values to decode. - mutating func decode(_ type: T.Type) throws -> T - -% for type in codable_types: - /// Decodes a value of the given type, if present. - /// - /// This method returns `nil` if the container has no elements left to - /// decode, or if the value is null. The difference between these states can - /// be distinguished by checking `isAtEnd`. - /// - /// - parameter type: The type of value to decode. - /// - returns: A decoded value of the requested type, or `nil` if the value - /// is a null value, or if there are no more elements to decode. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - mutating func decodeIfPresent(_ type: ${type}.Type) throws -> ${type}? -% end - - /// Decodes a value of the given type, if present. - /// - /// This method returns `nil` if the container has no elements left to - /// decode, or if the value is null. The difference between these states can - /// be distinguished by checking `isAtEnd`. - /// - /// - parameter type: The type of value to decode. - /// - returns: A decoded value of the requested type, or `nil` if the value - /// is a null value, or if there are no more elements to decode. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// is not convertible to the requested type. - mutating func decodeIfPresent(_ type: T.Type) throws -> T? - - /// Decodes a nested container keyed by the given type. - /// - /// - parameter type: The key type to use for the container. - /// - returns: A keyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not a keyed container. - mutating func nestedContainer( - keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer - - /// Decodes an unkeyed nested container. - /// - /// - returns: An unkeyed decoding container view into `self`. - /// - throws: `DecodingError.typeMismatch` if the encountered stored value is - /// not an unkeyed container. - mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer - - /// Decodes a nested container and returns a `Decoder` instance for decoding - /// `super` from that container. - /// - /// - returns: A new `Decoder` to pass to `super.init(from:)`. - /// - throws: `DecodingError.valueNotFound` if the encountered encoded value - /// is null, or of there are no more values to decode. - mutating func superDecoder() throws -> Decoder -} - -//===----------------------------------------------------------------------===// -// Single Value Encoding Containers -//===----------------------------------------------------------------------===// - -/// A container that can support the storage and direct encoding of a single -/// non-keyed value. -public protocol SingleValueEncodingContainer { - /// The path of coding keys taken to get to this point in encoding. - var codingPath: [CodingKey] { get } - - /// Encodes a null value. - /// - /// - throws: `EncodingError.invalidValue` if a null value is invalid in the - /// current context for this format. - /// - precondition: May not be called after a previous `self.encode(_:)` - /// call. - mutating func encodeNil() throws - -% for type in codable_types: - /// Encodes a single value of the given type. - /// - /// - parameter value: The value to encode. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - /// - precondition: May not be called after a previous `self.encode(_:)` - /// call. - mutating func encode(_ value: ${type}) throws -% end - - /// Encodes a single value of the given type. - /// - /// - parameter value: The value to encode. - /// - throws: `EncodingError.invalidValue` if the given value is invalid in - /// the current context for this format. - /// - precondition: May not be called after a previous `self.encode(_:)` - /// call. - mutating func encode(_ value: T) throws -} - -/// A container that can support the storage and direct decoding of a single -/// nonkeyed value. -public protocol SingleValueDecodingContainer { - /// The path of coding keys taken to get to this point in encoding. - var codingPath: [CodingKey] { get } - - /// Decodes a null value. - /// - /// - returns: Whether the encountered value was null. - func decodeNil() -> Bool - -% for type in codable_types: - /// Decodes a single value of the given type. - /// - /// - parameter type: The type to decode as. - /// - returns: A value of the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// cannot be converted to the requested type. - /// - throws: `DecodingError.valueNotFound` if the encountered encoded value - /// is null. - func decode(_ type: ${type}.Type) throws -> ${type} -% end - - /// Decodes a single value of the given type. - /// - /// - parameter type: The type to decode as. - /// - returns: A value of the requested type. - /// - throws: `DecodingError.typeMismatch` if the encountered encoded value - /// cannot be converted to the requested type. - /// - throws: `DecodingError.valueNotFound` if the encountered encoded value - /// is null. - func decode(_ type: T.Type) throws -> T -} - -//===----------------------------------------------------------------------===// -// User Info -//===----------------------------------------------------------------------===// - -/// A user-defined key for providing context during encoding and decoding. -public struct CodingUserInfoKey: RawRepresentable, Equatable, Hashable { - public typealias RawValue = String - - /// The key's string value. - public let rawValue: String - - /// Creates a new instance with the given raw value. - /// - /// - parameter rawValue: The value of the key. - public init?(rawValue: String) { - self.rawValue = rawValue - } - - /// Returns a Boolean value indicating whether the given keys are equal. - /// - /// - parameter lhs: The key to compare against. - /// - parameter rhs: The key to compare with. - public static func ==( - lhs: CodingUserInfoKey, rhs: CodingUserInfoKey) -> Bool - { - return lhs.rawValue == rhs.rawValue - } - - /// The key's hash value. - public var hashValue: Int { - return self.rawValue.hashValue - } - - /// Hashes the essential components of this value by feeding them into the - /// given hasher. - /// - /// - Parameter hasher: The hasher to use when combining the components - /// of this instance. - public func hash(into hasher: inout Hasher) { - hasher.combine(self.rawValue) - } -} - -//===----------------------------------------------------------------------===// -// Errors -//===----------------------------------------------------------------------===// - -/// An error that occurs during the encoding of a value. -public enum EncodingError: Error { - /// The context in which the error occurred. - public struct Context { - /// The path of coding keys taken to get to the point of the failing encode - /// call. - public let codingPath: [CodingKey] - - /// A description of what went wrong, for debugging purposes. - public let debugDescription: String - - /// The underlying error which caused this error, if any. - public let underlyingError: Error? - - /// Creates a new context with the given path of coding keys and a - /// description of what went wrong. - /// - /// - parameter codingPath: The path of coding keys taken to get to the - /// point of the failing encode call. - /// - parameter debugDescription: A description of what went wrong, for - /// debugging purposes. - /// - parameter underlyingError: The underlying error which caused this - /// error, if any. - public init( - codingPath: [CodingKey], - debugDescription: String, - underlyingError: Error? = nil) - { - self.codingPath = codingPath - self.debugDescription = debugDescription - self.underlyingError = underlyingError - } - } - - /// An indication that an encoder or its containers could not encode the - /// given value. - /// - /// As associated values, this case contains the attempted value and context - /// for debugging. - case invalidValue(Any, Context) - - // MARK: - NSError Bridging - - // CustomNSError bridging applies only when the CustomNSError conformance is - // applied in the same module as the declared error type. Since we cannot - // access CustomNSError (which is defined in Foundation) from here, we can - // use the "hidden" entry points. - - public var _domain: String { - return "NSCocoaErrorDomain" - } - - public var _code: Int { - switch self { - case .invalidValue: return 4866 - } - } - - public var _userInfo: AnyObject? { - // The error dictionary must be returned as an AnyObject. We can do this - // only on platforms with bridging, unfortunately. - #if _runtime(_ObjC) - let context: Context - switch self { - case .invalidValue(_, let c): context = c - } - - var userInfo: [String: Any] = [ - "NSCodingPath": context.codingPath, - "NSDebugDescription": context.debugDescription - ] - - if let underlyingError = context.underlyingError { - userInfo["NSUnderlyingError"] = underlyingError - } - - return userInfo as AnyObject - #else - return nil - #endif - } -} - -/// An error that occurs during the decoding of a value. -public enum DecodingError: Error { - /// The context in which the error occurred. - public struct Context { - /// The path of coding keys taken to get to the point of the failing decode - /// call. - public let codingPath: [CodingKey] - - /// A description of what went wrong, for debugging purposes. - public let debugDescription: String - - /// The underlying error which caused this error, if any. - public let underlyingError: Error? - - /// Creates a new context with the given path of coding keys and a - /// description of what went wrong. - /// - /// - parameter codingPath: The path of coding keys taken to get to the - /// point of the failing decode call. - /// - parameter debugDescription: A description of what went wrong, for - /// debugging purposes. - /// - parameter underlyingError: The underlying error which caused this - /// error, if any. - public init( - codingPath: [CodingKey], - debugDescription: String, - underlyingError: Error? = nil) - { - self.codingPath = codingPath - self.debugDescription = debugDescription - self.underlyingError = underlyingError - } - } - - /// An indication that a value of the given type could not be decoded because - /// it did not match the type of what was found in the encoded payload. - /// - /// As associated values, this case contains the attempted type and context - /// for debugging. - case typeMismatch(Any.Type, Context) - - /// An indication that a non-optional value of the given type was expected, - /// but a null value was found. - /// - /// As associated values, this case contains the attempted type and context - /// for debugging. - case valueNotFound(Any.Type, Context) - - /// An indication that a keyed decoding container was asked for an entry for - /// the given key, but did not contain one. - /// - /// As associated values, this case contains the attempted key and context - /// for debugging. - case keyNotFound(CodingKey, Context) - - /// An indication that the data is corrupted or otherwise invalid. - /// - /// As an associated value, this case contains the context for debugging. - case dataCorrupted(Context) - - // MARK: - NSError Bridging - - // CustomNSError bridging applies only when the CustomNSError conformance is - // applied in the same module as the declared error type. Since we cannot - // access CustomNSError (which is defined in Foundation) from here, we can - // use the "hidden" entry points. - - public var _domain: String { - return "NSCocoaErrorDomain" - } - - public var _code: Int { - switch self { - case .keyNotFound, .valueNotFound: return 4865 - case .typeMismatch, .dataCorrupted: return 4864 - } - } - - public var _userInfo: AnyObject? { - // The error dictionary must be returned as an AnyObject. We can do this - // only on platforms with bridging, unfortunately. - #if _runtime(_ObjC) - let context: Context - switch self { - case .keyNotFound(_, let c): context = c - case .valueNotFound(_, let c): context = c - case .typeMismatch(_, let c): context = c - case .dataCorrupted( let c): context = c - } - - var userInfo: [String: Any] = [ - "NSCodingPath": context.codingPath, - "NSDebugDescription": context.debugDescription - ] - - if let underlyingError = context.underlyingError { - userInfo["NSUnderlyingError"] = underlyingError - } - - return userInfo as AnyObject - #else - return nil - #endif - } -} - -// The following extensions allow for easier error construction. - -internal struct _GenericIndexKey: CodingKey { - internal var stringValue: String - internal var intValue: Int? - - internal init?(stringValue: String) { - return nil - } - - internal init?(intValue: Int) { - self.stringValue = "Index \(intValue)" - self.intValue = intValue - } -} - -extension DecodingError { - /// Returns a new `.dataCorrupted` error using a constructed coding path and - /// the given debug description. - /// - /// The coding path for the returned error is constructed by appending the - /// given key to the given container's coding path. - /// - /// - param key: The key which caused the failure. - /// - param container: The container in which the corrupted data was - /// accessed. - /// - param debugDescription: A description of the error to aid in debugging. - /// - /// - Returns: A new `.dataCorrupted` error with the given information. - public static func dataCorruptedError( - forKey key: C.Key, in container: C, debugDescription: String - ) -> DecodingError { - let context = DecodingError.Context( - codingPath: container.codingPath + [key], - debugDescription: debugDescription) - return .dataCorrupted(context) - } - - /// Returns a new `.dataCorrupted` error using a constructed coding path and - /// the given debug description. - /// - /// The coding path for the returned error is constructed by appending the - /// given container's current index to its coding path. - /// - /// - param container: The container in which the corrupted data was - /// accessed. - /// - param debugDescription: A description of the error to aid in debugging. - /// - /// - Returns: A new `.dataCorrupted` error with the given information. - public static func dataCorruptedError( - in container: UnkeyedDecodingContainer, - debugDescription: String - ) -> DecodingError { - let context = DecodingError.Context( - codingPath: container.codingPath + - [_GenericIndexKey(intValue: container.currentIndex)!], - debugDescription: debugDescription) - return .dataCorrupted(context) - } - - /// Returns a new `.dataCorrupted` error using a constructed coding path and - /// the given debug description. - /// - /// The coding path for the returned error is the given container's coding - /// path. - /// - /// - param container: The container in which the corrupted data was - /// accessed. - /// - param debugDescription: A description of the error to aid in debugging. - /// - /// - Returns: A new `.dataCorrupted` error with the given information. - public static func dataCorruptedError( - in container: SingleValueDecodingContainer, debugDescription: String - ) -> DecodingError { - let context = DecodingError.Context(codingPath: container.codingPath, - debugDescription: debugDescription) - return .dataCorrupted(context) - } -} - -//===----------------------------------------------------------------------===// -// Keyed Encoding Container Implementations -//===----------------------------------------------------------------------===// - -internal class _KeyedEncodingContainerBase { - internal init(){} - - deinit {} - - // These must all be given a concrete implementation in _*Box. - internal var codingPath: [CodingKey] { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func encodeNil(forKey key: Key) throws { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - -% for type in codable_types: - internal func encode(_ value: ${type}, forKey key: Key) throws { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } -% end - - internal func encode(_ value: T, forKey key: Key) throws { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func encodeConditional( - _ object: T, forKey key: Key) throws - { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - -% for type in codable_types: - internal func encodeIfPresent(_ value: ${type}?, forKey key: Key) throws { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } -% end - - internal func encodeIfPresent( - _ value: T?, forKey key: Key) throws - { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func nestedContainer( - keyedBy keyType: NestedKey.Type, forKey key: Key - ) -> KeyedEncodingContainer { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func nestedUnkeyedContainer( - forKey key: Key) -> UnkeyedEncodingContainer { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func superEncoder() -> Encoder { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } - - internal func superEncoder(forKey key: Key) -> Encoder { - fatalError("_KeyedEncodingContainerBase cannot be used directly.") - } -} - -internal final class _KeyedEncodingContainerBox< - Concrete: KeyedEncodingContainerProtocol ->: _KeyedEncodingContainerBase { - typealias Key = Concrete.Key - - internal var concrete: Concrete - - internal init(_ container: Concrete) { - concrete = container - } - - override internal var codingPath: [CodingKey] { - return concrete.codingPath - } - - override internal func encodeNil(forKey key: Key) throws { - try concrete.encodeNil(forKey: key) - } - -% for type in codable_types: - override internal func encode(_ value: ${type}, forKey key: Key) throws { - try concrete.encode(value, forKey: key) - } -% end - - override internal func encode( - _ value: T, forKey key: Key) throws - { - try concrete.encode(value, forKey: key) - } - - override internal func encodeConditional( - _ object: T, forKey key: Key) throws - { - try concrete.encodeConditional(object, forKey: key) - } - -% for type in codable_types: - override internal func encodeIfPresent( - _ value: ${type}?, forKey key: Key) throws - { - try concrete.encodeIfPresent(value, forKey: key) - } -% end - - override internal func encodeIfPresent( - _ value: T?, forKey key: Key) throws - { - try concrete.encodeIfPresent(value, forKey: key) - } - - override internal func nestedContainer( - keyedBy keyType: NestedKey.Type, forKey key: Key - ) -> KeyedEncodingContainer { - return concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) - } - - override internal func nestedUnkeyedContainer( - forKey key: Key) -> UnkeyedEncodingContainer - { - return concrete.nestedUnkeyedContainer(forKey: key) - } - - override internal func superEncoder() -> Encoder { - return concrete.superEncoder() - } - - override internal func superEncoder(forKey key: Key) -> Encoder { - return concrete.superEncoder(forKey: key) - } -} - -internal class _KeyedDecodingContainerBase { - internal init(){} - - deinit {} - - internal var codingPath: [CodingKey] { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal var allKeys: [Key] { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func contains(_ key: Key) -> Bool { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func decodeNil(forKey key: Key) throws -> Bool { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - -% for type in codable_types: - internal func decode( - _ type: ${type}.Type, forKey key: Key) throws -> ${type} - { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } -% end - - internal func decode( - _ type: T.Type, forKey key: Key) throws -> T - { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - -% for type in codable_types: - internal func decodeIfPresent( - _ type: ${type}.Type, forKey key: Key) throws -> ${type}? - { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } -% end - - internal func decodeIfPresent( - _ type: T.Type, forKey key: Key) throws -> T? - { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func nestedContainer( - keyedBy type: NestedKey.Type, forKey key: Key - ) throws -> KeyedDecodingContainer { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func nestedUnkeyedContainer( - forKey key: Key) throws -> UnkeyedDecodingContainer - { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func superDecoder() throws -> Decoder { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } - - internal func superDecoder(forKey key: Key) throws -> Decoder { - fatalError("_KeyedDecodingContainerBase cannot be used directly.") - } -} - -internal final class _KeyedDecodingContainerBox< - Concrete: KeyedDecodingContainerProtocol ->: _KeyedDecodingContainerBase { - typealias Key = Concrete.Key - - internal var concrete: Concrete - - internal init(_ container: Concrete) { - concrete = container - } - - override var codingPath: [CodingKey] { - return concrete.codingPath - } - - override var allKeys: [Key] { - return concrete.allKeys - } - - override internal func contains(_ key: Key) -> Bool { - return concrete.contains(key) - } - - override internal func decodeNil(forKey key: Key) throws -> Bool { - return try concrete.decodeNil(forKey: key) - } - -% for type in codable_types: - override internal func decode( - _ type: ${type}.Type, forKey key: Key) throws -> ${type} - { - return try concrete.decode(${type}.self, forKey: key) - } -% end - - override internal func decode( - _ type: T.Type, forKey key: Key) throws -> T - { - return try concrete.decode(T.self, forKey: key) - } - -% for type in codable_types: - override internal func decodeIfPresent( - _ type: ${type}.Type, forKey key: Key) throws -> ${type}? - { - return try concrete.decodeIfPresent(${type}.self, forKey: key) - } -% end - - override internal func decodeIfPresent( - _ type: T.Type, forKey key: Key) throws -> T? - { - return try concrete.decodeIfPresent(T.self, forKey: key) - } - - override internal func nestedContainer( - keyedBy type: NestedKey.Type, forKey key: Key - ) throws -> KeyedDecodingContainer { - return try concrete.nestedContainer(keyedBy: NestedKey.self, forKey: key) - } - - override internal func nestedUnkeyedContainer( - forKey key: Key) throws -> UnkeyedDecodingContainer - { - return try concrete.nestedUnkeyedContainer(forKey: key) - } - - override internal func superDecoder() throws -> Decoder { - return try concrete.superDecoder() - } - - override internal func superDecoder(forKey key: Key) throws -> Decoder { - return try concrete.superDecoder(forKey: key) - } -} - -//===----------------------------------------------------------------------===// -// Primitive and RawRepresentable Extensions -//===----------------------------------------------------------------------===// - -% for type in codable_types: -extension ${type}: Codable { - /// Creates a new instance by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - self = try decoder.singleValueContainer().decode(${type}.self) - } - - /// Encodes this value into the given encoder. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self) - } -} - -extension RawRepresentable where RawValue == ${type}, Self: Encodable { - /// Encodes this value into the given encoder, when the type's `RawValue` - /// is `${type}`. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.rawValue) - } -} - -extension RawRepresentable where RawValue == ${type}, Self: Decodable { - /// Creates a new instance by decoding from the given decoder, when the - /// type's `RawValue` is `${type}`. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - let decoded = try decoder.singleValueContainer().decode(RawValue.self) - guard let value = Self(rawValue: decoded) else { - throw DecodingError.dataCorrupted( - DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(decoded)")) - } - - self = value - } -} -% end - -//===----------------------------------------------------------------------===// -// Optional/Collection Type Conformances -//===----------------------------------------------------------------------===// - -extension Optional: Encodable where Wrapped: Encodable { - /// Encodes this optional value into the given encoder. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .none: try container.encodeNil() - case .some(let wrapped): try container.encode(wrapped) - } - } -} - -extension Optional: Decodable where Wrapped: Decodable { - /// Creates a new instance by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - self = .none - } else { - let element = try container.decode(Wrapped.self) - self = .some(element) - } - } -} - -extension Array: Encodable where Element: Encodable { - /// Encodes the elements of this array into the given encoder in an unkeyed - /// container. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - for element in self { - try container.encode(element) - } - } -} - -extension Array: Decodable where Element: Decodable { - /// Creates a new array by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - self.init() - - var container = try decoder.unkeyedContainer() - while !container.isAtEnd { - let element = try container.decode(Element.self) - self.append(element) - } - } -} - -extension ContiguousArray: Encodable where Element: Encodable { - /// Encodes the elements of this contiguous array into the given encoder - /// in an unkeyed container. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - for element in self { - try container.encode(element) - } - } -} - -extension ContiguousArray: Decodable where Element: Decodable { - /// Creates a new contiguous array by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - self.init() - - var container = try decoder.unkeyedContainer() - while !container.isAtEnd { - let element = try container.decode(Element.self) - self.append(element) - } - } -} - -extension Set: Encodable where Element: Encodable { - /// Encodes the elements of this set into the given encoder in an unkeyed - /// container. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - for element in self { - try container.encode(element) - } - } -} - -extension Set: Decodable where Element: Decodable { - /// Creates a new set by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - self.init() - - var container = try decoder.unkeyedContainer() - while !container.isAtEnd { - let element = try container.decode(Element.self) - self.insert(element) - } - } -} - -/// A wrapper for dictionary keys which are Strings or Ints. -internal struct _DictionaryCodingKey: CodingKey { - internal let stringValue: String - internal let intValue: Int? - - internal init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = Int(stringValue) - } - - internal init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } -} - -extension Dictionary: Encodable where Key: Encodable, Value: Encodable { - /// Encodes the contents of this dictionary into the given encoder. - /// - /// If the dictionary uses `String` or `Int` keys, the contents are encoded - /// in a keyed container. Otherwise, the contents are encoded as alternating - /// key-value pairs in an unkeyed container. - /// - /// This function throws an error if any values are invalid for the given - /// encoder's format. - /// - /// - Parameter encoder: The encoder to write data to. - public func encode(to encoder: Encoder) throws { - if Key.self == String.self { - // Since the keys are already Strings, we can use them as keys directly. - var container = encoder.container(keyedBy: _DictionaryCodingKey.self) - for (key, value) in self { - let codingKey = _DictionaryCodingKey(stringValue: key as! String)! - try container.encode(value, forKey: codingKey) - } - } else if Key.self == Int.self { - // Since the keys are already Ints, we can use them as keys directly. - var container = encoder.container(keyedBy: _DictionaryCodingKey.self) - for (key, value) in self { - let codingKey = _DictionaryCodingKey(intValue: key as! Int)! - try container.encode(value, forKey: codingKey) - } - } else { - // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily - // convert to keys. We can encode as an array of alternating key-value - // pairs, though. - var container = encoder.unkeyedContainer() - for (key, value) in self { - try container.encode(key) - try container.encode(value) - } - } - } -} - -extension Dictionary: Decodable where Key: Decodable, Value: Decodable { - /// Creates a new dictionary by decoding from the given decoder. - /// - /// This initializer throws an error if reading from the decoder fails, or - /// if the data read is corrupted or otherwise invalid. - /// - /// - Parameter decoder: The decoder to read data from. - public init(from decoder: Decoder) throws { - self.init() - - if Key.self == String.self { - // The keys are Strings, so we should be able to expect a keyed container. - let container = try decoder.container(keyedBy: _DictionaryCodingKey.self) - for key in container.allKeys { - let value = try container.decode(Value.self, forKey: key) - self[key.stringValue as! Key] = value - } - } else if Key.self == Int.self { - // The keys are Ints, so we should be able to expect a keyed container. - let container = try decoder.container(keyedBy: _DictionaryCodingKey.self) - for key in container.allKeys { - guard key.intValue != nil else { - // We provide stringValues for Int keys; if an encoder chooses not to - // use the actual intValues, we've encoded string keys. - // So on init, _DictionaryCodingKey tries to parse string keys as - // Ints. If that succeeds, then we would have had an intValue here. - // We don't, so this isn't a valid Int key. - var codingPath = decoder.codingPath - codingPath.append(key) - throw DecodingError.typeMismatch( - Int.self, DecodingError.Context( - codingPath: codingPath, - debugDescription: "Expected Int key but found String key instead.")) - } - - let value = try container.decode(Value.self, forKey: key) - self[key.intValue! as! Key] = value - } - } else { - // We should have encoded as an array of alternating key-value pairs. - var container = try decoder.unkeyedContainer() - - // We're expecting to get pairs. If the container has a known count, it - // had better be even; no point in doing work if not. - if let count = container.count { - guard count % 2 == 0 else { - throw DecodingError.dataCorrupted( - DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Expected collection of key-value pairs; encountered odd-length array instead.")) - } - } - - while !container.isAtEnd { - let key = try container.decode(Key.self) - - guard !container.isAtEnd else { - throw DecodingError.dataCorrupted( - DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Unkeyed container reached end before value in key-value pair.")) - } - - let value = try container.decode(Value.self) - self[key] = value - } - } - } -} - -//===----------------------------------------------------------------------===// -// Convenience Default Implementations -//===----------------------------------------------------------------------===// - -// Default implementation of encodeConditional(_:forKey:) in terms of -// encode(_:forKey:) -extension KeyedEncodingContainerProtocol { - public mutating func encodeConditional( - _ object: T, forKey key: Key) throws - { - try encode(object, forKey: key) - } -} - -// Default implementation of encodeIfPresent(_:forKey:) in terms of -// encode(_:forKey:) -extension KeyedEncodingContainerProtocol { -% for type in codable_types: - public mutating func encodeIfPresent( - _ value: ${type}?, forKey key: Key) throws - { - guard let value = value else { return } - try encode(value, forKey: key) - } -% end - - public mutating func encodeIfPresent( - _ value: T?, forKey key: Key) throws - { - guard let value = value else { return } - try encode(value, forKey: key) - } -} - -// Default implementation of decodeIfPresent(_:forKey:) in terms of -// decode(_:forKey:) and decodeNil(forKey:) -extension KeyedDecodingContainerProtocol { -% for type in codable_types: - public func decodeIfPresent( - _ type: ${type}.Type, forKey key: Key) throws -> ${type}? - { - guard try self.contains(key) && !self.decodeNil(forKey: key) - else { return nil } - return try self.decode(${type}.self, forKey: key) - } -% end - - public func decodeIfPresent( - _ type: T.Type, forKey key: Key) throws -> T? - { - guard try self.contains(key) && !self.decodeNil(forKey: key) - else { return nil } - return try self.decode(T.self, forKey: key) - } -} - -// Default implementation of encodeConditional(_:) in terms of encode(_:), -// and encode(contentsOf:) in terms of encode(_:) loop. -extension UnkeyedEncodingContainer { - public mutating func encodeConditional( - _ object: T) throws - { - try self.encode(object) - } - -% for type in codable_types: - public mutating func encode( - contentsOf sequence: T) throws where T.Element == ${type} - { - for element in sequence { - try self.encode(element) - } - } -% end - - public mutating func encode( - contentsOf sequence: T) throws where T.Element: Encodable - { - for element in sequence { - try self.encode(element) - } - } -} - -// Default implementation of decodeIfPresent(_:) in terms of decode(_:) and -// decodeNil() -extension UnkeyedDecodingContainer { -% for type in codable_types: - public mutating func decodeIfPresent( - _ type: ${type}.Type) throws -> ${type}? - { - guard try !self.isAtEnd && !self.decodeNil() else { return nil } - return try self.decode(${type}.self) - } -% end - - public mutating func decodeIfPresent( - _ type: T.Type) throws -> T? - { - guard try !self.isAtEnd && !self.decodeNil() else { return nil } - return try self.decode(T.self) - } -} - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index c2bbe5d887de2..13275086d2f26 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -67,7 +67,8 @@ extension ContiguousArray { @_semantics("array.make_mutable") internal mutating func _makeMutableAndUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _buffer = _Buffer(copying: _buffer) + _createNewBuffer(bufferIsUnique: false, minimumCapacity: count, + growForAppend: false) } } @@ -655,19 +656,64 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.mutate_unknown") public mutating func reserveCapacity(_ minimumCapacity: Int) { - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: minimumCapacity) == nil { + _reserveCapacityImpl(minimumCapacity: minimumCapacity, + growForAppend: false) + } - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) + /// Reserves enough space to store `minimumCapacity` elements. + /// If a new buffer needs to be allocated and `growForAppend` is true, + /// the new capacity is calculated using `_growArrayCapacity`. + @_alwaysEmitIntoClient + internal mutating func _reserveCapacityImpl( + minimumCapacity: Int, growForAppend: Bool + ) { + let isUnique = _buffer.isUniquelyReferenced() + if _slowPath(!isUnique || _getCapacity() < minimumCapacity) { + _createNewBuffer(bufferIsUnique: isUnique, + minimumCapacity: Swift.max(minimumCapacity, count), + growForAppend: growForAppend) + } + _internalInvariant(capacity >= minimumCapacity) + _internalInvariant(capacity == 0 || _buffer.isUniquelyReferenced()) + } + /// Creates a new buffer, replacing the current buffer. + /// + /// If `bufferIsUnique` is true, the buffer is assumed to be uniquely + /// referenced by this array and the elements are moved - instead of copied - + /// to the new buffer. + /// The `minimumCapacity` is the lower bound for the new capacity. + /// If `growForAppend` is true, the new capacity is calculated using + /// `_growArrayCapacity`. + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _createNewBuffer( + bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool + ) { + let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(), + minimumCapacity: minimumCapacity, + growForAppend: growForAppend) + let count = _getCount() + _internalInvariant(newCapacity >= count) + + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: newCapacity) + + if bufferIsUnique { + _internalInvariant(_buffer.isUniquelyReferenced()) + + // As an optimization, if the original buffer is unique, we can just move + // the elements instead of copying. + let dest = newBuffer.firstElementAddress + dest.moveInitialize(from: _buffer.firstElementAddress, + count: count) + _buffer.count = 0 + } else { _buffer._copyContents( - subRange: _buffer.indices, + subRange: 0..= minimumCapacity) + _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0) } /// Copy the contents of the current buffer to a new unique mutable buffer. @@ -687,7 +733,9 @@ extension ContiguousArray: RangeReplaceableCollection { @_semantics("array.make_mutable") internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() { if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) { - _copyToNewBuffer(oldCount: _buffer.count) + _createNewBuffer(bufferIsUnique: false, + minimumCapacity: count + 1, + growForAppend: true) } } @@ -716,7 +764,9 @@ extension ContiguousArray: RangeReplaceableCollection { _buffer.isMutableAndUniquelyReferenced()) if _slowPath(oldCount + 1 > _buffer.capacity) { - _copyToNewBuffer(oldCount: oldCount) + _createNewBuffer(bufferIsUnique: true, + minimumCapacity: oldCount + 1, + growForAppend: true) } } @@ -757,6 +807,8 @@ extension ContiguousArray: RangeReplaceableCollection { @inlinable @_semantics("array.append_element") public mutating func append(_ newElement: __owned Element) { + // Separating uniqueness check and capacity check allows hoisting the + // uniqueness check out of a loop. _makeUniqueAndReserveCapacityIfNotUnique() let oldCount = _getCount() _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount) @@ -793,7 +845,7 @@ extension ContiguousArray: RangeReplaceableCollection { start: startNewElements, count: self.capacity - oldCount) - let (remainder,writtenUpTo) = buf.initialize(from: newElements) + var (remainder,writtenUpTo) = buf.initialize(from: newElements) // trap on underflow from the sequence's underestimate: let writtenCount = buf.distance(from: buf.startIndex, to: writtenUpTo) @@ -801,35 +853,48 @@ extension ContiguousArray: RangeReplaceableCollection { "newElements.underestimatedCount was an overestimate") // can't check for overflow as sequences can underestimate - _buffer.count += writtenCount + // This check prevents a data race writing to _swiftEmptyArrayStorage + if writtenCount > 0 { + _buffer.count += writtenCount + } if writtenUpTo == buf.endIndex { // there may be elements that didn't fit in the existing buffer, // append them in slow sequence-only mode - _buffer._arrayAppendSequence(IteratorSequence(remainder)) + var newCount = _getCount() + var nextItem = remainder.next() + while nextItem != nil { + reserveCapacityForAppend(newElementsCount: 1) + + let currentCapacity = _getCapacity() + let base = _buffer.firstElementAddress + + // fill while there is another item and spare capacity + while let next = nextItem, newCount < currentCapacity { + (base + newCount).initialize(to: next) + newCount += 1 + nextItem = remainder.next() + } + _buffer.count = newCount + } } } @inlinable @_semantics("array.reserve_capacity_for_append") internal mutating func reserveCapacityForAppend(newElementsCount: Int) { - let oldCount = self.count - let oldCapacity = self.capacity - let newCount = oldCount + newElementsCount - // Ensure uniqueness, mutability, and sufficient storage. Note that // for consistency, we need unique self even if newElements is empty. - self.reserveCapacity( - newCount > oldCapacity ? - Swift.max(newCount, _growArrayCapacity(oldCapacity)) - : newCount) + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) } @inlinable + @_semantics("array.mutate_unknown") public mutating func _customRemoveLast() -> Element? { + _makeMutableAndUnique() let newCount = _getCount() - 1 _precondition(newCount >= 0, "Can't removeLast from an empty ContiguousArray") - _makeUniqueAndReserveCapacityIfNotUnique() let pointer = (_buffer.firstElementAddress + newCount) let element = pointer.move() _buffer.count = newCount @@ -853,10 +918,12 @@ extension ContiguousArray: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the length of the array. @inlinable @discardableResult + @_semantics("array.mutate_unknown") public mutating func remove(at index: Int) -> Element { - _precondition(index < endIndex, "Index out of range") - _precondition(index >= startIndex, "Index out of range") - _makeUniqueAndReserveCapacityIfNotUnique() + _makeMutableAndUnique() + let currentCount = _getCount() + _precondition(index < currentCount, "Index out of range") + _precondition(index >= 0, "Index out of range") let newCount = _getCount() - 1 let pointer = (_buffer.firstElementAddress + index) let result = pointer.move() @@ -1103,9 +1170,8 @@ extension ContiguousArray { public mutating func withUnsafeMutableBufferPointer( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R { + _makeMutableAndUnique() let count = self.count - // Ensure unique storage - _buffer._outlinedMakeUniqueBuffer(bufferCount: count) // Ensure that body can't invalidate the storage or its bounds by // moving self into a temporary working array. @@ -1218,19 +1284,12 @@ extension ContiguousArray { _precondition(subrange.upperBound <= _buffer.endIndex, "ContiguousArray replace: subrange extends past the end") - let oldCount = _buffer.count let eraseCount = subrange.count let insertCount = newElements.count let growth = insertCount - eraseCount - if _buffer.requestUniqueMutableBackingBuffer( - minimumCapacity: oldCount + growth) != nil { - - _buffer.replaceSubrange( - subrange, with: insertCount, elementsOf: newElements) - } else { - _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount) - } + reserveCapacityForAppend(newElementsCount: growth) + _buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements) } } diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index 0f339f8b124ea..5f47025a0ff38 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -79,6 +79,97 @@ internal final class _ContiguousArrayStorage< } #if _runtime(_ObjC) + + internal final override func withUnsafeBufferOfObjects( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R { + _internalInvariant(_isBridgedVerbatimToObjectiveC(Element.self)) + let count = countAndCapacity.count + let elements = UnsafeRawPointer(_elementPointer) + .assumingMemoryBound(to: AnyObject.self) + defer { _fixLifetime(self) } + return try body(UnsafeBufferPointer(start: elements, count: count)) + } + + @objc(countByEnumeratingWithState:objects:count:) + @_effects(releasenone) + internal final override func countByEnumerating( + with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, + objects: UnsafeMutablePointer?, count: Int + ) -> Int { + var enumerationState = state.pointee + + if enumerationState.state != 0 { + return 0 + } + + return withUnsafeBufferOfObjects { + objects in + enumerationState.mutationsPtr = _fastEnumerationStorageMutationsPtr + enumerationState.itemsPtr = + AutoreleasingUnsafeMutablePointer(objects.baseAddress) + enumerationState.state = 1 + state.pointee = enumerationState + return objects.count + } + } + + @inline(__always) + @_effects(readonly) + @nonobjc private func _objectAt(_ index: Int) -> Unmanaged { + return withUnsafeBufferOfObjects { + objects in + _precondition( + _isValidArraySubscript(index, count: objects.count), + "Array index out of range") + return Unmanaged.passUnretained(objects[index]) + } + } + + @objc(objectAtIndexedSubscript:) + @_effects(readonly) + final override internal func objectAtSubscript(_ index: Int) -> Unmanaged { + return _objectAt(index) + } + + @objc(objectAtIndex:) + @_effects(readonly) + final override internal func objectAt(_ index: Int) -> Unmanaged { + return _objectAt(index) + } + + @objc internal override final var count: Int { + @_effects(readonly) get { + return withUnsafeBufferOfObjects { $0.count } + } + } + + @_effects(releasenone) + @objc internal override final func getObjects( + _ aBuffer: UnsafeMutablePointer, range: _SwiftNSRange + ) { + return withUnsafeBufferOfObjects { + objects in + _precondition( + _isValidArrayIndex(range.location, count: objects.count), + "Array index out of range") + + _precondition( + _isValidArrayIndex( + range.location + range.length, count: objects.count), + "Array index out of range") + + if objects.isEmpty { return } + + // These objects are "returned" at +0, so treat them as pointer values to + // avoid retains. Copy bytes via a raw pointer to circumvent reference + // counting while correctly aliasing with all other pointer types. + UnsafeMutableRawPointer(aBuffer).copyMemory( + from: objects.baseAddress! + range.location, + byteCount: range.length * MemoryLayout.stride) + } + } + /// If the `Element` is bridged verbatim, invoke `body` on an /// `UnsafeBufferPointer` to the elements and return the result. /// Otherwise, return `nil`. @@ -688,7 +779,7 @@ internal struct _UnsafePartiallyInitializedContiguousArrayBuffer { p = newResult.firstElementAddress + result.capacity remainingCapacity = newResult.capacity - result.capacity if !result.isEmpty { - // This check prevents a data race writting to _swiftEmptyArrayStorage + // This check prevents a data race writing to _swiftEmptyArrayStorage // Since count is always 0 there, this code does nothing anyway newResult.firstElementAddress.moveInitialize( from: result.firstElementAddress, count: result.capacity) diff --git a/stdlib/public/core/DictionaryStorage.swift b/stdlib/public/core/DictionaryStorage.swift index 39ac76578f126..ac931fd2ed51c 100644 --- a/stdlib/public/core/DictionaryStorage.swift +++ b/stdlib/public/core/DictionaryStorage.swift @@ -196,6 +196,35 @@ extension __RawDictionaryStorage { return Builtin.bridgeFromRawPointer( Builtin.addressof(&_swiftEmptyDictionarySingleton)) } + + @_alwaysEmitIntoClient + @inline(__always) + internal final func uncheckedKey(at bucket: _HashTable.Bucket) -> Key { + defer { _fixLifetime(self) } + _internalInvariant(_hashTable.isOccupied(bucket)) + let keys = _rawKeys.assumingMemoryBound(to: Key.self) + return keys[bucket.offset] + } + + @_alwaysEmitIntoClient + @inline(never) + internal final func find(_ key: Key) -> (bucket: _HashTable.Bucket, found: Bool) { + return find(key, hashValue: key._rawHashValue(seed: _seed)) + } + + @_alwaysEmitIntoClient + @inline(never) + internal final func find(_ key: Key, hashValue: Int) -> (bucket: _HashTable.Bucket, found: Bool) { + let hashTable = _hashTable + var bucket = hashTable.idealBucket(forHashValue: hashValue) + while hashTable._isOccupied(bucket) { + if uncheckedKey(at: bucket) == key { + return (bucket, true) + } + bucket = hashTable.bucket(wrappedAfter: bucket) + } + return (bucket, false) + } } @usableFromInline diff --git a/stdlib/public/core/Diffing.swift b/stdlib/public/core/Diffing.swift index 32b417f746c8e..7b956cee38981 100644 --- a/stdlib/public/core/Diffing.swift +++ b/stdlib/public/core/Diffing.swift @@ -319,7 +319,7 @@ fileprivate func _myers( y &-= 1 } - assert((x == prev_x && y > prev_y) || (y == prev_y && x > prev_x)) + _internalInvariant((x == prev_x && y > prev_y) || (y == prev_y && x > prev_x)) if y != prev_y { changes.append(.insert(offset: prev_y, element: b[prev_y], associatedWith: nil)) } else { diff --git a/stdlib/public/core/ErrorType.swift b/stdlib/public/core/ErrorType.swift index 474ffea3a50fb..6e59ad0c594ed 100644 --- a/stdlib/public/core/ErrorType.swift +++ b/stdlib/public/core/ErrorType.swift @@ -148,7 +148,7 @@ internal func _getErrorCode(_ x: UnsafePointer) -> Int { @_silgen_name("") internal func _getErrorUserInfoNSDictionary(_ x: UnsafePointer) -> AnyObject? { - return x.pointee._userInfo.map { $0 as AnyObject } + return x.pointee._userInfo.map { $0 } } // Called by the casting machinery to extract an NSError from an Error value. diff --git a/stdlib/public/core/ExistentialCollection.swift b/stdlib/public/core/ExistentialCollection.swift new file mode 100644 index 0000000000000..50d2ee925e3be --- /dev/null +++ b/stdlib/public/core/ExistentialCollection.swift @@ -0,0 +1,2428 @@ +//===--- ExistentialCollection.swift --------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// TODO: swift-3-indexing-model: perform type erasure on the associated +// `Indices` type. + +import SwiftShims + +@inline(never) +@usableFromInline +internal func _abstract( + file: StaticString = #file, + line: UInt = #line +) -> Never { + fatalError("Method must be overridden", file: file, line: line) +} + +//===--- Iterator ---------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +/// A type-erased iterator of `Element`. +/// +/// This iterator forwards its `next()` method to an arbitrary underlying +/// iterator having the same `Element` type, hiding the specifics of the +/// underlying `IteratorProtocol`. +@frozen +public struct AnyIterator { + @usableFromInline + internal let _box: _AnyIteratorBoxBase + + /// Creates an iterator that wraps a base iterator but whose type depends + /// only on the base iterator's element type. + /// + /// You can use `AnyIterator` to hide the type signature of a more complex + /// iterator. For example, the `digits()` function in the following example + /// creates an iterator over a collection that lazily maps the elements of a + /// `Range` instance to strings. Instead of returning an + /// iterator with a type that encapsulates the implementation of the + /// collection, the `digits()` function first wraps the iterator in an + /// `AnyIterator` instance. + /// + /// func digits() -> AnyIterator { + /// let lazyStrings = (0..<10).lazy.map { String($0) } + /// let iterator: + /// LazyMapIterator>, String> + /// = lazyStrings.makeIterator() + /// + /// return AnyIterator(iterator) + /// } + /// + /// - Parameter base: An iterator to type-erase. + @inlinable + public init(_ base: I) where I.Element == Element { + self._box = _IteratorBox(base) + } + + /// Creates an iterator that wraps the given closure in its `next()` method. + /// + /// The following example creates an iterator that counts up from the initial + /// value of an integer `x` to 15: + /// + /// var x = 7 + /// let iterator: AnyIterator = AnyIterator { + /// defer { x += 1 } + /// return x < 15 ? x : nil + /// } + /// let a = Array(iterator) + /// // a == [7, 8, 9, 10, 11, 12, 13, 14] + /// + /// - Parameter body: A closure that returns an optional element. `body` is + /// executed each time the `next()` method is called on the resulting + /// iterator. + @inlinable + public init(_ body: @escaping () -> Element?) { + self._box = _IteratorBox(_ClosureBasedIterator(body)) + } + + @inlinable + internal init(_box: _AnyIteratorBoxBase) { + self._box = _box + } +} + +extension AnyIterator: IteratorProtocol { + /// Advances to the next element and returns it, or `nil` if no next element + /// exists. + /// + /// Once `nil` has been returned, all subsequent calls return `nil`. + @inlinable + public func next() -> Element? { + return _box.next() + } +} + +/// Every `IteratorProtocol` can also be a `Sequence`. Note that +/// traversing the sequence consumes the iterator. +extension AnyIterator: Sequence { } + +@usableFromInline +@frozen +internal struct _ClosureBasedIterator: IteratorProtocol { + @inlinable + internal init(_ body: @escaping () -> Element?) { + self._body = body + } + + @inlinable + internal func next() -> Element? { return _body() } + + @usableFromInline + internal let _body: () -> Element? +} + +@_fixed_layout +@usableFromInline +internal class _AnyIteratorBoxBase: IteratorProtocol { + @inlinable // FIXME(sil-serialize-all) + internal init() {} + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + /// Advances to the next element and returns it, or `nil` if no next element + /// exists. + /// + /// Once `nil` has been returned, all subsequent calls return `nil`. + /// + /// - Note: Subclasses must override this method. + @inlinable // FIXME(sil-serialize-all) + internal func next() -> Element? { _abstract() } +} + +@_fixed_layout +@usableFromInline +internal final class _IteratorBox + : _AnyIteratorBoxBase { + + @inlinable + internal init(_ base: Base) { self._base = base } + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal override func next() -> Base.Element? { return _base.next() } + + @usableFromInline + internal var _base: Base +} + +//===--- Sequence ---------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +@_fixed_layout +@usableFromInline +internal class _AnySequenceBox { + @inlinable // FIXME(sil-serialize-all) + internal init() { } + + @inlinable + internal func _makeIterator() -> AnyIterator { _abstract() } + + @inlinable + internal var _underestimatedCount: Int { _abstract() } + + @inlinable + internal func _map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + _abstract() + } + + @inlinable + internal func _filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + _abstract() + } + + @inlinable + internal func _forEach( + _ body: (Element) throws -> Void + ) rethrows { + _abstract() + } + + @inlinable + internal func __customContainsEquatableElement( + _ element: Element + ) -> Bool? { + _abstract() + } + + @inlinable + internal func __copyToContiguousArray() -> ContiguousArray { + _abstract() + } + + @inlinable + internal func __copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator, UnsafeMutableBufferPointer.Index) { + _abstract() + } + + // This deinit has to be present on all the types + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnySequenceBox { + _abstract() + } + + @inlinable + internal func _dropFirst(_ n: Int) -> _AnySequenceBox { + _abstract() + } + + @inlinable + internal func _dropLast(_ n: Int) -> [Element] { + _abstract() + } + + @inlinable + internal func _prefix(_ maxLength: Int) -> _AnySequenceBox { + _abstract() + } + + @inlinable + internal func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> [Element] { + _abstract() + } + + @inlinable + internal func _suffix(_ maxLength: Int) -> [Element] { + _abstract() + } +} + +@_fixed_layout +@usableFromInline +internal class _AnyCollectionBox: _AnySequenceBox { + + // This deinit has to be present on all the types + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal override func _dropFirst(_ n: Int) -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal func _dropLast(_ n: Int) -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal func _suffix(_ maxLength: Int) -> _AnyCollectionBox { + _abstract() + } + + @inlinable + internal subscript(i: _AnyIndexBox) -> Element { _abstract() } + + @inlinable + internal func _index(after i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } + + @inlinable + internal func _formIndex(after i: _AnyIndexBox) { _abstract() } + + @inlinable + internal func _index( + _ i: _AnyIndexBox, + offsetBy n: Int + ) -> _AnyIndexBox { + _abstract() + } + + @inlinable + internal func _index( + _ i: _AnyIndexBox, + offsetBy n: Int, + limitedBy limit: _AnyIndexBox + ) -> _AnyIndexBox? { + _abstract() + } + + @inlinable + internal func _formIndex(_ i: inout _AnyIndexBox, offsetBy n: Int) { + _abstract() + } + + @inlinable + internal func _formIndex( + _ i: inout _AnyIndexBox, + offsetBy n: Int, + limitedBy limit: _AnyIndexBox + ) -> Bool { + _abstract() + } + + @inlinable + internal func _distance( + from start: _AnyIndexBox, + to end: _AnyIndexBox + ) -> Int { + _abstract() + } + + // TODO: swift-3-indexing-model: forward the following methods. + /* + var _indices: Indices + + __consuming func prefix(upTo end: Index) -> SubSequence + + __consuming func suffix(from start: Index) -> SubSequence + + func prefix(through position: Index) -> SubSequence + + var isEmpty: Bool { get } + */ + + @inlinable // FIXME(sil-serialize-all) + internal var _count: Int { _abstract() } + + // TODO: swift-3-indexing-model: forward the following methods. + /* + func _customIndexOfEquatableElement(element: Element) -> Index?? + func _customLastIndexOfEquatableElement(element: Element) -> Index?? + */ + + @inlinable + internal init( + _startIndex: _AnyIndexBox, + endIndex: _AnyIndexBox + ) { + self._startIndex = _startIndex + self._endIndex = endIndex + } + + @usableFromInline + internal let _startIndex: _AnyIndexBox + + @usableFromInline + internal let _endIndex: _AnyIndexBox + + @inlinable + internal subscript( + start start: _AnyIndexBox, + end end: _AnyIndexBox + ) -> _AnyCollectionBox { _abstract() } +} + +@_fixed_layout +@usableFromInline +internal class _AnyBidirectionalCollectionBox + : _AnyCollectionBox +{ + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override func _dropFirst( + _ n: Int + ) -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override func _dropLast( + _ n: Int + ) -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override func _suffix( + _ maxLength: Int + ) -> _AnyBidirectionalCollectionBox { + _abstract() + } + + @inlinable + internal override subscript( + start start: _AnyIndexBox, + end end: _AnyIndexBox + ) -> _AnyBidirectionalCollectionBox { _abstract() } + + @inlinable + internal func _index(before i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } + + @inlinable + internal func _formIndex(before i: _AnyIndexBox) { _abstract() } +} + +@_fixed_layout +@usableFromInline +internal class _AnyRandomAccessCollectionBox + : _AnyBidirectionalCollectionBox +{ + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override func _dropFirst( + _ n: Int + ) -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override func _dropLast( + _ n: Int + ) -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override func _suffix( + _ maxLength: Int + ) -> _AnyRandomAccessCollectionBox { + _abstract() + } + + @inlinable + internal override subscript( + start start: _AnyIndexBox, + end end: _AnyIndexBox + ) -> _AnyRandomAccessCollectionBox { _abstract() } +} + +@_fixed_layout +@usableFromInline +internal final class _SequenceBox: _AnySequenceBox { + @usableFromInline + internal typealias Element = S.Element + + @inline(__always) + @inlinable + internal override func _makeIterator() -> AnyIterator { + return AnyIterator(_base.makeIterator()) + } + @inlinable + internal override var _underestimatedCount: Int { + return _base.underestimatedCount + } + @inlinable + internal override func _map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _base.map(transform) + } + @inlinable + internal override func _filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _base.filter(isIncluded) + } + @inlinable + internal override func _forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _base.forEach(body) + } + @inlinable + internal override func __customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _base._customContainsEquatableElement(element) + } + @inlinable + internal override func __copyToContiguousArray() -> ContiguousArray { + return _base._copyToContiguousArray() + } + @inlinable + internal override func __copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _base._copyContents(initializing: buf) + return (AnyIterator(it),idx) + } + + @inlinable + internal override func _dropFirst(_ n: Int) -> _AnySequenceBox { + return _SequenceBox>(_base: _base.dropFirst(n)) + } + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnySequenceBox { + return try _SequenceBox>(_base: _base.drop(while: predicate)) + } + @inlinable + internal override func _dropLast(_ n: Int) -> [Element] { + return _base.dropLast(n) + } + @inlinable + internal override func _prefix(_ n: Int) -> _AnySequenceBox { + return _SequenceBox>(_base: _base.prefix(n)) + } + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _base.prefix(while: predicate) + } + @inlinable + internal override func _suffix(_ maxLength: Int) -> [Element] { + return _base.suffix(maxLength) + } + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal init(_base: S) { + self._base = _base + } + + @usableFromInline + internal var _base: S +} + +@_fixed_layout +@usableFromInline +internal final class _CollectionBox: _AnyCollectionBox +{ + @usableFromInline + internal typealias Element = S.Element + + @inline(__always) + @inlinable + internal override func _makeIterator() -> AnyIterator { + return AnyIterator(_base.makeIterator()) + } + @inlinable + internal override var _underestimatedCount: Int { + return _base.underestimatedCount + } + @inlinable + internal override func _map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _base.map(transform) + } + @inlinable + internal override func _filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _base.filter(isIncluded) + } + @inlinable + internal override func _forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _base.forEach(body) + } + @inlinable + internal override func __customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _base._customContainsEquatableElement(element) + } + @inlinable + internal override func __copyToContiguousArray() -> ContiguousArray { + return _base._copyToContiguousArray() + } + @inlinable + internal override func __copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _base._copyContents(initializing: buf) + return (AnyIterator(it),idx) + } + + @inline(__always) + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyCollectionBox { + return try _CollectionBox(_base: _base.drop(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _dropFirst(_ n: Int) -> _AnyCollectionBox { + return _CollectionBox(_base: _base.dropFirst(n)) + } + @inline(__always) + @inlinable + internal override func _dropLast(_ n: Int) -> _AnyCollectionBox { + return _CollectionBox(_base: _base.dropLast(n)) + } + @inline(__always) + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyCollectionBox { + return try _CollectionBox(_base: _base.prefix(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyCollectionBox { + return _CollectionBox(_base: _base.prefix(maxLength)) + } + @inline(__always) + @inlinable + internal override func _suffix( + _ maxLength: Int + ) -> _AnyCollectionBox { + return _CollectionBox(_base: _base.suffix(maxLength)) + } + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal init(_base: S) { + self._base = _base + super.init( + _startIndex: _IndexBox(_base: _base.startIndex), + endIndex: _IndexBox(_base: _base.endIndex) + ) + } + + @inlinable + internal func _unbox( + _ position: _AnyIndexBox, file: StaticString = #file, line: UInt = #line + ) -> S.Index { + if let i = position._unbox() as S.Index? { + return i + } + fatalError("Index type mismatch!", file: file, line: line) + } + + @inlinable + internal override subscript(position: _AnyIndexBox) -> Element { + return _base[_unbox(position)] + } + + @inlinable + internal override subscript(start start: _AnyIndexBox, end end: _AnyIndexBox) + -> _AnyCollectionBox + { + return _CollectionBox(_base: + _base[_unbox(start)..<_unbox(end)] + ) + } + + @inlinable + internal override func _index(after position: _AnyIndexBox) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(after: _unbox(position))) + } + + @inlinable + internal override func _formIndex(after position: _AnyIndexBox) { + if let p = position as? _IndexBox { + return _base.formIndex(after: &p._base) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, offsetBy n: Int + ) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(_unbox(i), offsetBy: numericCast(n))) + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, + offsetBy n: Int, + limitedBy limit: _AnyIndexBox + ) -> _AnyIndexBox? { + return _base.index( + _unbox(i), + offsetBy: numericCast(n), + limitedBy: _unbox(limit)) + .map { _IndexBox(_base: $0) } + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int + ) { + if let box = i as? _IndexBox { + return _base.formIndex(&box._base, offsetBy: numericCast(n)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox + ) -> Bool { + if let box = i as? _IndexBox { + return _base.formIndex( + &box._base, + offsetBy: numericCast(n), + limitedBy: _unbox(limit)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _distance( + from start: _AnyIndexBox, + to end: _AnyIndexBox + ) -> Int { + return numericCast(_base.distance(from: _unbox(start), to: _unbox(end))) + } + + @inlinable + internal override var _count: Int { + return numericCast(_base.count) + } + + @usableFromInline + internal var _base: S +} + +@_fixed_layout +@usableFromInline +internal final class _BidirectionalCollectionBox + : _AnyBidirectionalCollectionBox +{ + @usableFromInline + internal typealias Element = S.Element + + @inline(__always) + @inlinable + internal override func _makeIterator() -> AnyIterator { + return AnyIterator(_base.makeIterator()) + } + @inlinable + internal override var _underestimatedCount: Int { + return _base.underestimatedCount + } + @inlinable + internal override func _map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _base.map(transform) + } + @inlinable + internal override func _filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _base.filter(isIncluded) + } + @inlinable + internal override func _forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _base.forEach(body) + } + @inlinable + internal override func __customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _base._customContainsEquatableElement(element) + } + @inlinable + internal override func __copyToContiguousArray() -> ContiguousArray { + return _base._copyToContiguousArray() + } + @inlinable + internal override func __copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _base._copyContents(initializing: buf) + return (AnyIterator(it),idx) + } + + @inline(__always) + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyBidirectionalCollectionBox { + return try _BidirectionalCollectionBox(_base: _base.drop(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _dropFirst( + _ n: Int + ) -> _AnyBidirectionalCollectionBox { + return _BidirectionalCollectionBox(_base: _base.dropFirst(n)) + } + @inline(__always) + @inlinable + internal override func _dropLast( + _ n: Int + ) -> _AnyBidirectionalCollectionBox { + return _BidirectionalCollectionBox(_base: _base.dropLast(n)) + } + @inline(__always) + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyBidirectionalCollectionBox { + return try _BidirectionalCollectionBox(_base: _base.prefix(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyBidirectionalCollectionBox { + return _BidirectionalCollectionBox(_base: _base.prefix(maxLength)) + } + @inline(__always) + @inlinable + internal override func _suffix( + _ maxLength: Int + ) -> _AnyBidirectionalCollectionBox { + return _BidirectionalCollectionBox(_base: _base.suffix(maxLength)) + } + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal init(_base: S) { + self._base = _base + super.init( + _startIndex: _IndexBox(_base: _base.startIndex), + endIndex: _IndexBox(_base: _base.endIndex) + ) + } + + @inlinable + internal func _unbox( + _ position: _AnyIndexBox, file: StaticString = #file, line: UInt = #line + ) -> S.Index { + if let i = position._unbox() as S.Index? { + return i + } + fatalError("Index type mismatch!", file: file, line: line) + } + + @inlinable + internal override subscript(position: _AnyIndexBox) -> Element { + return _base[_unbox(position)] + } + + @inlinable + internal override subscript( + start start: _AnyIndexBox, + end end: _AnyIndexBox + ) -> _AnyBidirectionalCollectionBox { + return _BidirectionalCollectionBox(_base: + _base[_unbox(start)..<_unbox(end)] + ) + } + + @inlinable + internal override func _index(after position: _AnyIndexBox) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(after: _unbox(position))) + } + + @inlinable + internal override func _formIndex(after position: _AnyIndexBox) { + if let p = position as? _IndexBox { + return _base.formIndex(after: &p._base) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, offsetBy n: Int + ) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(_unbox(i), offsetBy: numericCast(n))) + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, + offsetBy n: Int, + limitedBy limit: _AnyIndexBox + ) -> _AnyIndexBox? { + return _base.index( + _unbox(i), + offsetBy: numericCast(n), + limitedBy: _unbox(limit) + ) + .map { _IndexBox(_base: $0) } + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int + ) { + if let box = i as? _IndexBox { + return _base.formIndex(&box._base, offsetBy: numericCast(n)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox + ) -> Bool { + if let box = i as? _IndexBox { + return _base.formIndex( + &box._base, + offsetBy: numericCast(n), + limitedBy: _unbox(limit)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _distance( + from start: _AnyIndexBox, + to end: _AnyIndexBox + ) -> Int { + return numericCast(_base.distance(from: _unbox(start), to: _unbox(end))) + } + + @inlinable + internal override var _count: Int { + return numericCast(_base.count) + } + + @inlinable + internal override func _index(before position: _AnyIndexBox) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(before: _unbox(position))) + } + + @inlinable + internal override func _formIndex(before position: _AnyIndexBox) { + if let p = position as? _IndexBox { + return _base.formIndex(before: &p._base) + } + fatalError("Index type mismatch!") + } + + @usableFromInline + internal var _base: S +} + +@_fixed_layout +@usableFromInline +internal final class _RandomAccessCollectionBox + : _AnyRandomAccessCollectionBox +{ + @usableFromInline + internal typealias Element = S.Element + + @inline(__always) + @inlinable + internal override func _makeIterator() -> AnyIterator { + return AnyIterator(_base.makeIterator()) + } + @inlinable + internal override var _underestimatedCount: Int { + return _base.underestimatedCount + } + @inlinable + internal override func _map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _base.map(transform) + } + @inlinable + internal override func _filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _base.filter(isIncluded) + } + @inlinable + internal override func _forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _base.forEach(body) + } + @inlinable + internal override func __customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _base._customContainsEquatableElement(element) + } + @inlinable + internal override func __copyToContiguousArray() -> ContiguousArray { + return _base._copyToContiguousArray() + } + @inlinable + internal override func __copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _base._copyContents(initializing: buf) + return (AnyIterator(it),idx) + } + + @inline(__always) + @inlinable + internal override func _drop( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyRandomAccessCollectionBox { + return try _RandomAccessCollectionBox(_base: _base.drop(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _dropFirst( + _ n: Int + ) -> _AnyRandomAccessCollectionBox { + return _RandomAccessCollectionBox(_base: _base.dropFirst(n)) + } + @inline(__always) + @inlinable + internal override func _dropLast( + _ n: Int + ) -> _AnyRandomAccessCollectionBox { + return _RandomAccessCollectionBox(_base: _base.dropLast(n)) + } + @inline(__always) + @inlinable + internal override func _prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> _AnyRandomAccessCollectionBox { + return try _RandomAccessCollectionBox(_base: _base.prefix(while: predicate)) + } + @inline(__always) + @inlinable + internal override func _prefix( + _ maxLength: Int + ) -> _AnyRandomAccessCollectionBox { + return _RandomAccessCollectionBox(_base: _base.prefix(maxLength)) + } + @inline(__always) + @inlinable + internal override func _suffix( + _ maxLength: Int + ) -> _AnyRandomAccessCollectionBox { + return _RandomAccessCollectionBox(_base: _base.suffix(maxLength)) + } + + @inlinable // FIXME(sil-serialize-all) + deinit {} + + @inlinable + internal init(_base: S) { + self._base = _base + super.init( + _startIndex: _IndexBox(_base: _base.startIndex), + endIndex: _IndexBox(_base: _base.endIndex) + ) + } + + @inlinable + internal func _unbox( + _ position: _AnyIndexBox, file: StaticString = #file, line: UInt = #line + ) -> S.Index { + if let i = position._unbox() as S.Index? { + return i + } + fatalError("Index type mismatch!", file: file, line: line) + } + + @inlinable + internal override subscript(position: _AnyIndexBox) -> Element { + return _base[_unbox(position)] + } + + @inlinable + internal override subscript(start start: _AnyIndexBox, end end: _AnyIndexBox) + -> _AnyRandomAccessCollectionBox + { + return _RandomAccessCollectionBox(_base: + _base[_unbox(start)..<_unbox(end)] + ) + } + + @inlinable + internal override func _index(after position: _AnyIndexBox) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(after: _unbox(position))) + } + + @inlinable + internal override func _formIndex(after position: _AnyIndexBox) { + if let p = position as? _IndexBox { + return _base.formIndex(after: &p._base) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, offsetBy n: Int + ) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(_unbox(i), offsetBy: numericCast(n))) + } + + @inlinable + internal override func _index( + _ i: _AnyIndexBox, + offsetBy n: Int, + limitedBy limit: _AnyIndexBox + ) -> _AnyIndexBox? { + return _base.index( + _unbox(i), + offsetBy: numericCast(n), + limitedBy: _unbox(limit) + ) + .map { _IndexBox(_base: $0) } + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int + ) { + if let box = i as? _IndexBox { + return _base.formIndex(&box._base, offsetBy: numericCast(n)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _formIndex( + _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox + ) -> Bool { + if let box = i as? _IndexBox { + return _base.formIndex( + &box._base, + offsetBy: numericCast(n), + limitedBy: _unbox(limit)) + } + fatalError("Index type mismatch!") + } + + @inlinable + internal override func _distance( + from start: _AnyIndexBox, + to end: _AnyIndexBox + ) -> Int { + return numericCast(_base.distance(from: _unbox(start), to: _unbox(end))) + } + + @inlinable + internal override var _count: Int { + return numericCast(_base.count) + } + + @inlinable + internal override func _index(before position: _AnyIndexBox) -> _AnyIndexBox { + return _IndexBox(_base: _base.index(before: _unbox(position))) + } + + @inlinable + internal override func _formIndex(before position: _AnyIndexBox) { + if let p = position as? _IndexBox { + return _base.formIndex(before: &p._base) + } + fatalError("Index type mismatch!") + } + + @usableFromInline + internal var _base: S +} + +@usableFromInline +@frozen +internal struct _ClosureBasedSequence { + @usableFromInline + internal var _makeUnderlyingIterator: () -> Iterator + + @inlinable + internal init(_ makeUnderlyingIterator: @escaping () -> Iterator) { + self._makeUnderlyingIterator = makeUnderlyingIterator + } +} + +extension _ClosureBasedSequence: Sequence { + @inlinable + internal func makeIterator() -> Iterator { + return _makeUnderlyingIterator() + } +} + +/// A type-erased sequence. +/// +/// An instance of `AnySequence` forwards its operations to an underlying base +/// sequence having the same `Element` type, hiding the specifics of the +/// underlying sequence. +@frozen +public struct AnySequence { + @usableFromInline + internal let _box: _AnySequenceBox + + /// Creates a sequence whose `makeIterator()` method forwards to + /// `makeUnderlyingIterator`. + @inlinable + public init( + _ makeUnderlyingIterator: @escaping () -> I + ) where I.Element == Element { + self.init(_ClosureBasedSequence(makeUnderlyingIterator)) + } + + @inlinable + internal init(_box: _AnySequenceBox) { + self._box = _box + } +} + +extension AnySequence: Sequence { + public typealias Iterator = AnyIterator + + /// Creates a new sequence that wraps and forwards operations to `base`. + @inlinable + public init(_ base: S) + where + S.Element == Element { + self._box = _SequenceBox(_base: base) + } +} + +extension AnySequence { + + /// Returns an iterator over the elements of this sequence. + @inline(__always) + @inlinable + public __consuming func makeIterator() -> Iterator { + return _box._makeIterator() + } + @inlinable + public __consuming func dropLast(_ n: Int = 1) -> [Element] { + return _box._dropLast(n) + } + @inlinable + public __consuming func prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _box._prefix(while: predicate) + } + @inlinable + public __consuming func suffix(_ maxLength: Int) -> [Element] { + return _box._suffix(maxLength) + } + + @inlinable + public var underestimatedCount: Int { + return _box._underestimatedCount + } + + @inlinable + public func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _box._map(transform) + } + + @inlinable + public __consuming func filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _box._filter(isIncluded) + } + + @inlinable + public __consuming func forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _box._forEach(body) + } + + @inlinable + public __consuming func drop( + while predicate: (Element) throws -> Bool + ) rethrows -> AnySequence { + return try AnySequence(_box: _box._drop(while: predicate)) + } + + @inlinable + public __consuming func dropFirst(_ n: Int = 1) -> AnySequence { + return AnySequence(_box: _box._dropFirst(n)) + } + + @inlinable + public __consuming func prefix(_ maxLength: Int = 1) -> AnySequence { + return AnySequence(_box: _box._prefix(maxLength)) + } + + @inlinable + public func _customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _box.__customContainsEquatableElement(element) + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + return self._box.__copyToContiguousArray() + } + + @inlinable + public __consuming func _copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _box.__copyContents(initializing: buf) + return (AnyIterator(it),idx) + } +} + +extension AnyCollection { + /// Returns an iterator over the elements of this collection. + @inline(__always) + @inlinable + public __consuming func makeIterator() -> Iterator { + return _box._makeIterator() + } + @inlinable + public __consuming func dropLast(_ n: Int = 1) -> AnyCollection { + return AnyCollection(_box: _box._dropLast(n)) + } + @inlinable + public __consuming func prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyCollection { + return try AnyCollection(_box: _box._prefix(while: predicate)) + } + @inlinable + public __consuming func suffix(_ maxLength: Int) -> AnyCollection { + return AnyCollection(_box: _box._suffix(maxLength)) + } + + @inlinable + public var underestimatedCount: Int { + return _box._underestimatedCount + } + + @inlinable + public func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _box._map(transform) + } + + @inlinable + public __consuming func filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _box._filter(isIncluded) + } + + @inlinable + public __consuming func forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _box._forEach(body) + } + + @inlinable + public __consuming func drop( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyCollection { + return try AnyCollection(_box: _box._drop(while: predicate)) + } + + @inlinable + public __consuming func dropFirst(_ n: Int = 1) -> AnyCollection { + return AnyCollection(_box: _box._dropFirst(n)) + } + + @inlinable + public __consuming func prefix( + _ maxLength: Int = 1 + ) -> AnyCollection { + return AnyCollection(_box: _box._prefix(maxLength)) + } + + @inlinable + public func _customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _box.__customContainsEquatableElement(element) + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + return self._box.__copyToContiguousArray() + } + + @inlinable + public __consuming func _copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _box.__copyContents(initializing: buf) + return (AnyIterator(it),idx) + } +} + +extension AnyBidirectionalCollection { + /// Returns an iterator over the elements of this collection. + @inline(__always) + @inlinable + public __consuming func makeIterator() -> Iterator { + return _box._makeIterator() + } + @inlinable + public __consuming func dropLast( + _ n: Int = 1 + ) -> AnyBidirectionalCollection { + return AnyBidirectionalCollection(_box: _box._dropLast(n)) + } + @inlinable + public __consuming func prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyBidirectionalCollection { + return try AnyBidirectionalCollection(_box: _box._prefix(while: predicate)) + } + @inlinable + public __consuming func suffix( + _ maxLength: Int + ) -> AnyBidirectionalCollection { + return AnyBidirectionalCollection(_box: _box._suffix(maxLength)) + } + + @inlinable + public var underestimatedCount: Int { + return _box._underestimatedCount + } + + @inlinable + public func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _box._map(transform) + } + + @inlinable + public __consuming func filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _box._filter(isIncluded) + } + + @inlinable + public __consuming func forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _box._forEach(body) + } + + @inlinable + public __consuming func drop( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyBidirectionalCollection { + return try AnyBidirectionalCollection(_box: _box._drop(while: predicate)) + } + + @inlinable + public __consuming func dropFirst( + _ n: Int = 1 + ) -> AnyBidirectionalCollection { + return AnyBidirectionalCollection(_box: _box._dropFirst(n)) + } + + @inlinable + public __consuming func prefix( + _ maxLength: Int = 1 + ) -> AnyBidirectionalCollection { + return AnyBidirectionalCollection(_box: _box._prefix(maxLength)) + } + + @inlinable + public func _customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _box.__customContainsEquatableElement(element) + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + return self._box.__copyToContiguousArray() + } + + @inlinable + public __consuming func _copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _box.__copyContents(initializing: buf) + return (AnyIterator(it),idx) + } +} + +extension AnyRandomAccessCollection { + /// Returns an iterator over the elements of this collection. + @inline(__always) + @inlinable + public __consuming func makeIterator() -> Iterator { + return _box._makeIterator() + } + @inlinable + public __consuming func dropLast( + _ n: Int = 1 + ) -> AnyRandomAccessCollection { + return AnyRandomAccessCollection(_box: _box._dropLast(n)) + } + @inlinable + public __consuming func prefix( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyRandomAccessCollection { + return try AnyRandomAccessCollection(_box: _box._prefix(while: predicate)) + } + @inlinable + public __consuming func suffix( + _ maxLength: Int + ) -> AnyRandomAccessCollection { + return AnyRandomAccessCollection(_box: _box._suffix(maxLength)) + } + + @inlinable + public var underestimatedCount: Int { + return _box._underestimatedCount + } + + @inlinable + public func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + return try _box._map(transform) + } + + @inlinable + public __consuming func filter( + _ isIncluded: (Element) throws -> Bool + ) rethrows -> [Element] { + return try _box._filter(isIncluded) + } + + @inlinable + public __consuming func forEach( + _ body: (Element) throws -> Void + ) rethrows { + return try _box._forEach(body) + } + + @inlinable + public __consuming func drop( + while predicate: (Element) throws -> Bool + ) rethrows -> AnyRandomAccessCollection { + return try AnyRandomAccessCollection(_box: _box._drop(while: predicate)) + } + + @inlinable + public __consuming func dropFirst( + _ n: Int = 1 + ) -> AnyRandomAccessCollection { + return AnyRandomAccessCollection(_box: _box._dropFirst(n)) + } + + @inlinable + public __consuming func prefix( + _ maxLength: Int = 1 + ) -> AnyRandomAccessCollection { + return AnyRandomAccessCollection(_box: _box._prefix(maxLength)) + } + + @inlinable + public func _customContainsEquatableElement( + _ element: Element + ) -> Bool? { + return _box.__customContainsEquatableElement(element) + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + return self._box.__copyToContiguousArray() + } + + @inlinable + public __consuming func _copyContents( + initializing buf: UnsafeMutableBufferPointer + ) -> (AnyIterator,UnsafeMutableBufferPointer.Index) { + let (it,idx) = _box.__copyContents(initializing: buf) + return (AnyIterator(it),idx) + } +} + +//===--- Index ------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +@usableFromInline +internal protocol _AnyIndexBox: class { + var _typeID: ObjectIdentifier { get } + + func _unbox() -> T? + + func _isEqual(to rhs: _AnyIndexBox) -> Bool + + func _isLess(than rhs: _AnyIndexBox) -> Bool +} + +@_fixed_layout +@usableFromInline +internal final class _IndexBox: _AnyIndexBox { + @usableFromInline + internal var _base: BaseIndex + + @inlinable + internal init(_base: BaseIndex) { + self._base = _base + } + + @inlinable + internal func _unsafeUnbox(_ other: _AnyIndexBox) -> BaseIndex { + return unsafeDowncast(other, to: _IndexBox.self)._base + } + + @inlinable + internal var _typeID: ObjectIdentifier { + return ObjectIdentifier(type(of: self)) + } + + @inlinable + internal func _unbox() -> T? { + return (self as _AnyIndexBox as? _IndexBox)?._base + } + + @inlinable + internal func _isEqual(to rhs: _AnyIndexBox) -> Bool { + return _base == _unsafeUnbox(rhs) + } + + @inlinable + internal func _isLess(than rhs: _AnyIndexBox) -> Bool { + return _base < _unsafeUnbox(rhs) + } +} + +/// A wrapper over an underlying index that hides the specific underlying type. +@frozen +public struct AnyIndex { + @usableFromInline + internal var _box: _AnyIndexBox + + /// Creates a new index wrapping `base`. + @inlinable + public init(_ base: BaseIndex) { + self._box = _IndexBox(_base: base) + } + + @inlinable + internal init(_box: _AnyIndexBox) { + self._box = _box + } + + @inlinable + internal var _typeID: ObjectIdentifier { + return _box._typeID + } +} + +extension AnyIndex: Comparable { + /// Returns a Boolean value indicating whether two indices wrap equal + /// underlying indices. + /// + /// The types of the two underlying indices must be identical. + /// + /// - Parameters: + /// - lhs: An index to compare. + /// - rhs: Another index to compare. + @inlinable + public static func == (lhs: AnyIndex, rhs: AnyIndex) -> Bool { + _precondition(lhs._typeID == rhs._typeID, "Base index types differ") + return lhs._box._isEqual(to: rhs._box) + } + + /// Returns a Boolean value indicating whether the first argument represents a + /// position before the second argument. + /// + /// The types of the two underlying indices must be identical. + /// + /// - Parameters: + /// - lhs: An index to compare. + /// - rhs: Another index to compare. + @inlinable + public static func < (lhs: AnyIndex, rhs: AnyIndex) -> Bool { + _precondition(lhs._typeID == rhs._typeID, "Base index types differ") + return lhs._box._isLess(than: rhs._box) + } +} + +//===--- Collections ------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +public // @testable +protocol _AnyCollectionProtocol: Collection { + /// Identifies the underlying collection stored by `self`. Instances + /// copied or upgraded/downgraded from one another have the same `_boxID`. + var _boxID: ObjectIdentifier { get } +} + +/// A type-erased wrapper over any collection with indices that +/// support forward traversal. +/// +/// An `AnyCollection` instance forwards its operations to a base collection having the +/// same `Element` type, hiding the specifics of the underlying +/// collection. +@frozen +public struct AnyCollection { + @usableFromInline + internal let _box: _AnyCollectionBox + + @inlinable + internal init(_box: _AnyCollectionBox) { + self._box = _box + } +} + +extension AnyCollection: Collection { + public typealias Indices = DefaultIndices + public typealias Iterator = AnyIterator + public typealias Index = AnyIndex + public typealias SubSequence = AnyCollection + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: Forward + // SubTraversal: Forward + self._box = _CollectionBox( + _base: base) + } + + /// Creates an `AnyCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyCollection) { + self._box = other._box + } + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: Forward + // SubTraversal: Bidirectional + self._box = _BidirectionalCollectionBox( + _base: base) + } + + /// Creates an `AnyCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyBidirectionalCollection) { + self._box = other._box + } + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: Forward + // SubTraversal: RandomAccess + self._box = _RandomAccessCollectionBox( + _base: base) + } + + /// Creates an `AnyCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyRandomAccessCollection) { + self._box = other._box + } + + /// The position of the first element in a non-empty collection. + /// + /// In an empty collection, `startIndex == endIndex`. + @inlinable + public var startIndex: Index { + return AnyIndex(_box: _box._startIndex) + } + + /// The collection's "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// + /// `endIndex` is always reachable from `startIndex` by zero or more + /// applications of `index(after:)`. + @inlinable + public var endIndex: Index { + return AnyIndex(_box: _box._endIndex) + } + + /// Accesses the element indicated by `position`. + /// + /// - Precondition: `position` indicates a valid position in `self` and + /// `position != endIndex`. + @inlinable + public subscript(position: Index) -> Element { + return _box[position._box] + } + + @inlinable + public subscript(bounds: Range) -> SubSequence { + return AnyCollection(_box: + _box[start: bounds.lowerBound._box, end: bounds.upperBound._box]) + } + + @inlinable + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func index(after i: Index) -> Index { + return AnyIndex(_box: _box._index(after: i._box)) + } + + @inlinable + public func formIndex(after i: inout Index) { + if _isUnique(&i._box) { + _box._formIndex(after: i._box) + } + else { + i = index(after: i) + } + } + + @inlinable + public func index(_ i: Index, offsetBy n: Int) -> Index { + return AnyIndex(_box: _box._index(i._box, offsetBy: n)) + } + + @inlinable + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + return _box._index(i._box, offsetBy: n, limitedBy: limit._box) + .map { AnyIndex(_box:$0) } + } + + @inlinable + public func formIndex(_ i: inout Index, offsetBy n: Int) { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n) + } else { + i = index(i, offsetBy: n) + } + } + + @inlinable + public func formIndex( + _ i: inout Index, + offsetBy n: Int, + limitedBy limit: Index + ) -> Bool { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n, limitedBy: limit._box) + } + if let advanced = index(i, offsetBy: n, limitedBy: limit) { + i = advanced + return true + } + i = limit + return false + } + + @inlinable + public func distance(from start: Index, to end: Index) -> Int { + return _box._distance(from: start._box, to: end._box) + } + + /// The number of elements. + /// + /// To check whether a collection is empty, use its `isEmpty` property + /// instead of comparing `count` to zero. Calculating `count` can be an O(*n*) + /// operation. + /// + /// - Complexity: O(*n*) + @inlinable + public var count: Int { + return _box._count + } +} + +extension AnyCollection: _AnyCollectionProtocol { + /// Uniquely identifies the stored underlying collection. + @inlinable + public // Due to language limitations only + var _boxID: ObjectIdentifier { + return ObjectIdentifier(_box) + } +} + +/// A type-erased wrapper over any collection with indices that +/// support bidirectional traversal. +/// +/// An `AnyBidirectionalCollection` instance forwards its operations to a base collection having the +/// same `Element` type, hiding the specifics of the underlying +/// collection. +@frozen +public struct AnyBidirectionalCollection { + @usableFromInline + internal let _box: _AnyBidirectionalCollectionBox + + @inlinable + internal init(_box: _AnyBidirectionalCollectionBox) { + self._box = _box + } +} + +extension AnyBidirectionalCollection: BidirectionalCollection { + public typealias Indices = DefaultIndices + public typealias Iterator = AnyIterator + public typealias Index = AnyIndex + public typealias SubSequence = AnyBidirectionalCollection + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: Bidirectional + // SubTraversal: Bidirectional + self._box = _BidirectionalCollectionBox( + _base: base) + } + + /// Creates an `AnyBidirectionalCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyBidirectionalCollection) { + self._box = other._box + } + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: Bidirectional + // SubTraversal: RandomAccess + self._box = _RandomAccessCollectionBox( + _base: base) + } + + /// Creates an `AnyBidirectionalCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyRandomAccessCollection) { + self._box = other._box + } + + /// Creates an `AnyBidirectionalCollection` having the same underlying collection as `other`. + /// + /// If the underlying collection stored by `other` does not satisfy + /// `BidirectionalCollection`, the result is `nil`. + /// + /// - Complexity: O(1) + @inlinable + public init?(_ other: AnyCollection) { + guard let box = + other._box as? _AnyBidirectionalCollectionBox else { + return nil + } + self._box = box + } + + /// The position of the first element in a non-empty collection. + /// + /// In an empty collection, `startIndex == endIndex`. + @inlinable + public var startIndex: Index { + return AnyIndex(_box: _box._startIndex) + } + + /// The collection's "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// + /// `endIndex` is always reachable from `startIndex` by zero or more + /// applications of `index(after:)`. + @inlinable + public var endIndex: Index { + return AnyIndex(_box: _box._endIndex) + } + + /// Accesses the element indicated by `position`. + /// + /// - Precondition: `position` indicates a valid position in `self` and + /// `position != endIndex`. + @inlinable + public subscript(position: Index) -> Element { + return _box[position._box] + } + + @inlinable + public subscript(bounds: Range) -> SubSequence { + return AnyBidirectionalCollection(_box: + _box[start: bounds.lowerBound._box, end: bounds.upperBound._box]) + } + + @inlinable + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func index(after i: Index) -> Index { + return AnyIndex(_box: _box._index(after: i._box)) + } + + @inlinable + public func formIndex(after i: inout Index) { + if _isUnique(&i._box) { + _box._formIndex(after: i._box) + } + else { + i = index(after: i) + } + } + + @inlinable + public func index(_ i: Index, offsetBy n: Int) -> Index { + return AnyIndex(_box: _box._index(i._box, offsetBy: n)) + } + + @inlinable + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + return _box._index(i._box, offsetBy: n, limitedBy: limit._box) + .map { AnyIndex(_box:$0) } + } + + @inlinable + public func formIndex(_ i: inout Index, offsetBy n: Int) { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n) + } else { + i = index(i, offsetBy: n) + } + } + + @inlinable + public func formIndex( + _ i: inout Index, + offsetBy n: Int, + limitedBy limit: Index + ) -> Bool { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n, limitedBy: limit._box) + } + if let advanced = index(i, offsetBy: n, limitedBy: limit) { + i = advanced + return true + } + i = limit + return false + } + + @inlinable + public func distance(from start: Index, to end: Index) -> Int { + return _box._distance(from: start._box, to: end._box) + } + + /// The number of elements. + /// + /// To check whether a collection is empty, use its `isEmpty` property + /// instead of comparing `count` to zero. Calculating `count` can be an O(*n*) + /// operation. + /// + /// - Complexity: O(*n*) + @inlinable + public var count: Int { + return _box._count + } + + @inlinable + public func index(before i: Index) -> Index { + return AnyIndex(_box: _box._index(before: i._box)) + } + + @inlinable + public func formIndex(before i: inout Index) { + if _isUnique(&i._box) { + _box._formIndex(before: i._box) + } + else { + i = index(before: i) + } + } +} + +extension AnyBidirectionalCollection: _AnyCollectionProtocol { + /// Uniquely identifies the stored underlying collection. + @inlinable + public // Due to language limitations only + var _boxID: ObjectIdentifier { + return ObjectIdentifier(_box) + } +} + +/// A type-erased wrapper over any collection with indices that +/// support random access traversal. +/// +/// An `AnyRandomAccessCollection` instance forwards its operations to a base collection having the +/// same `Element` type, hiding the specifics of the underlying +/// collection. +@frozen +public struct AnyRandomAccessCollection { + @usableFromInline + internal let _box: _AnyRandomAccessCollectionBox + + @inlinable + internal init(_box: _AnyRandomAccessCollectionBox) { + self._box = _box + } +} + +extension AnyRandomAccessCollection: RandomAccessCollection { + public typealias Indices = DefaultIndices + public typealias Iterator = AnyIterator + public typealias Index = AnyIndex + public typealias SubSequence = AnyRandomAccessCollection + + /// Creates a type-erased collection that wraps the given collection. + /// + /// - Parameter base: The collection to wrap. + /// + /// - Complexity: O(1). + @inline(__always) + @inlinable + public init(_ base: C) where C.Element == Element { + // Traversal: RandomAccess + // SubTraversal: RandomAccess + self._box = _RandomAccessCollectionBox( + _base: base) + } + + /// Creates an `AnyRandomAccessCollection` having the same underlying collection as `other`. + /// + /// - Complexity: O(1) + @inlinable + public init(_ other: AnyRandomAccessCollection) { + self._box = other._box + } + + /// Creates an `AnyRandomAccessCollection` having the same underlying collection as `other`. + /// + /// If the underlying collection stored by `other` does not satisfy + /// `RandomAccessCollection`, the result is `nil`. + /// + /// - Complexity: O(1) + @inlinable + public init?(_ other: AnyCollection) { + guard let box = + other._box as? _AnyRandomAccessCollectionBox else { + return nil + } + self._box = box + } + + /// Creates an `AnyRandomAccessCollection` having the same underlying collection as `other`. + /// + /// If the underlying collection stored by `other` does not satisfy + /// `RandomAccessCollection`, the result is `nil`. + /// + /// - Complexity: O(1) + @inlinable + public init?(_ other: AnyBidirectionalCollection) { + guard let box = + other._box as? _AnyRandomAccessCollectionBox else { + return nil + } + self._box = box + } + + /// The position of the first element in a non-empty collection. + /// + /// In an empty collection, `startIndex == endIndex`. + @inlinable + public var startIndex: Index { + return AnyIndex(_box: _box._startIndex) + } + + /// The collection's "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// + /// `endIndex` is always reachable from `startIndex` by zero or more + /// applications of `index(after:)`. + @inlinable + public var endIndex: Index { + return AnyIndex(_box: _box._endIndex) + } + + /// Accesses the element indicated by `position`. + /// + /// - Precondition: `position` indicates a valid position in `self` and + /// `position != endIndex`. + @inlinable + public subscript(position: Index) -> Element { + return _box[position._box] + } + + @inlinable + public subscript(bounds: Range) -> SubSequence { + return AnyRandomAccessCollection(_box: + _box[start: bounds.lowerBound._box, end: bounds.upperBound._box]) + } + + @inlinable + public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { + // Do nothing. Doing a range check would involve unboxing indices, + // performing dynamic dispatch etc. This seems to be too costly for a fast + // range check for QoI purposes. + } + + @inlinable + public func index(after i: Index) -> Index { + return AnyIndex(_box: _box._index(after: i._box)) + } + + @inlinable + public func formIndex(after i: inout Index) { + if _isUnique(&i._box) { + _box._formIndex(after: i._box) + } + else { + i = index(after: i) + } + } + + @inlinable + public func index(_ i: Index, offsetBy n: Int) -> Index { + return AnyIndex(_box: _box._index(i._box, offsetBy: n)) + } + + @inlinable + public func index( + _ i: Index, offsetBy n: Int, limitedBy limit: Index + ) -> Index? { + return _box._index(i._box, offsetBy: n, limitedBy: limit._box) + .map { AnyIndex(_box:$0) } + } + + @inlinable + public func formIndex(_ i: inout Index, offsetBy n: Int) { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n) + } else { + i = index(i, offsetBy: n) + } + } + + @inlinable + public func formIndex( + _ i: inout Index, + offsetBy n: Int, + limitedBy limit: Index + ) -> Bool { + if _isUnique(&i._box) { + return _box._formIndex(&i._box, offsetBy: n, limitedBy: limit._box) + } + if let advanced = index(i, offsetBy: n, limitedBy: limit) { + i = advanced + return true + } + i = limit + return false + } + + @inlinable + public func distance(from start: Index, to end: Index) -> Int { + return _box._distance(from: start._box, to: end._box) + } + + /// The number of elements. + /// + /// - Complexity: O(1) + @inlinable + public var count: Int { + return _box._count + } + + @inlinable + public func index(before i: Index) -> Index { + return AnyIndex(_box: _box._index(before: i._box)) + } + + @inlinable + public func formIndex(before i: inout Index) { + if _isUnique(&i._box) { + _box._formIndex(before: i._box) + } + else { + i = index(before: i) + } + } +} + +extension AnyRandomAccessCollection: _AnyCollectionProtocol { + /// Uniquely identifies the stored underlying collection. + @inlinable + public // Due to language limitations only + var _boxID: ObjectIdentifier { + return ObjectIdentifier(_box) + } +} diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb deleted file mode 100644 index 5376d98040390..0000000000000 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ /dev/null @@ -1,1172 +0,0 @@ -//===--- ExistentialCollection.swift.gyb ----------------------*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -%{ - -from gyb_stdlib_support import ( - TRAVERSALS, - collectionForTraversal -) - -}% - -// TODO: swift-3-indexing-model: perform type erasure on the associated -// `Indices` type. - -import SwiftShims - -@inline(never) -@usableFromInline -internal func _abstract( - file: StaticString = #file, - line: UInt = #line -) -> Never { - fatalError("Method must be overridden", file: file, line: line) -} - -//===--- Iterator ---------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -/// A type-erased iterator of `Element`. -/// -/// This iterator forwards its `next()` method to an arbitrary underlying -/// iterator having the same `Element` type, hiding the specifics of the -/// underlying `IteratorProtocol`. -@frozen -public struct AnyIterator { - @usableFromInline - internal let _box: _AnyIteratorBoxBase - - /// Creates an iterator that wraps a base iterator but whose type depends - /// only on the base iterator's element type. - /// - /// You can use `AnyIterator` to hide the type signature of a more complex - /// iterator. For example, the `digits()` function in the following example - /// creates an iterator over a collection that lazily maps the elements of a - /// `Range` instance to strings. Instead of returning an - /// iterator with a type that encapsulates the implementation of the - /// collection, the `digits()` function first wraps the iterator in an - /// `AnyIterator` instance. - /// - /// func digits() -> AnyIterator { - /// let lazyStrings = (0..<10).lazy.map { String($0) } - /// let iterator: - /// LazyMapIterator>, String> - /// = lazyStrings.makeIterator() - /// - /// return AnyIterator(iterator) - /// } - /// - /// - Parameter base: An iterator to type-erase. - @inlinable - public init(_ base: I) where I.Element == Element { - self._box = _IteratorBox(base) - } - - /// Creates an iterator that wraps the given closure in its `next()` method. - /// - /// The following example creates an iterator that counts up from the initial - /// value of an integer `x` to 15: - /// - /// var x = 7 - /// let iterator: AnyIterator = AnyIterator { - /// defer { x += 1 } - /// return x < 15 ? x : nil - /// } - /// let a = Array(iterator) - /// // a == [7, 8, 9, 10, 11, 12, 13, 14] - /// - /// - Parameter body: A closure that returns an optional element. `body` is - /// executed each time the `next()` method is called on the resulting - /// iterator. - @inlinable - public init(_ body: @escaping () -> Element?) { - self._box = _IteratorBox(_ClosureBasedIterator(body)) - } - - @inlinable - internal init(_box: _AnyIteratorBoxBase) { - self._box = _box - } -} - -extension AnyIterator: IteratorProtocol { - /// Advances to the next element and returns it, or `nil` if no next element - /// exists. - /// - /// Once `nil` has been returned, all subsequent calls return `nil`. - @inlinable - public func next() -> Element? { - return _box.next() - } -} - -/// Every `IteratorProtocol` can also be a `Sequence`. Note that -/// traversing the sequence consumes the iterator. -extension AnyIterator: Sequence { } - -@usableFromInline -@frozen -internal struct _ClosureBasedIterator: IteratorProtocol { - @inlinable - internal init(_ body: @escaping () -> Element?) { - self._body = body - } - @inlinable - internal func next() -> Element? { return _body() } - @usableFromInline - internal let _body: () -> Element? -} - -@_fixed_layout -@usableFromInline -internal class _AnyIteratorBoxBase: IteratorProtocol { - @inlinable // FIXME(sil-serialize-all) - internal init() {} - - @inlinable // FIXME(sil-serialize-all) - deinit {} - /// Advances to the next element and returns it, or `nil` if no next element - /// exists. - /// - /// Once `nil` has been returned, all subsequent calls return `nil`. - /// - /// - Note: Subclasses must override this method. - @inlinable // FIXME(sil-serialize-all) - internal func next() -> Element? { _abstract() } -} - -@_fixed_layout -@usableFromInline -internal final class _IteratorBox< - Base: IteratorProtocol ->: _AnyIteratorBoxBase { - @inlinable - internal init(_ base: Base) { self._base = base } - @inlinable // FIXME(sil-serialize-all) - deinit {} - @inlinable - internal override func next() -> Base.Element? { return _base.next() } - @usableFromInline - internal var _base: Base -} - -//===--- Sequence ---------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -% for Kind in ['Sequence', 'Collection', 'BidirectionalCollection', 'RandomAccessCollection']: - -@_fixed_layout -@usableFromInline -% if Kind == 'Sequence': -internal class _AnySequenceBox -% elif Kind == 'Collection': -internal class _AnyCollectionBox: _AnySequenceBox -% elif Kind == 'BidirectionalCollection': -internal class _AnyBidirectionalCollectionBox - : _AnyCollectionBox -% elif Kind == 'RandomAccessCollection': -internal class _AnyRandomAccessCollectionBox - : _AnyBidirectionalCollectionBox -% else: -% assert False, 'Unknown kind' -% end -{ - -% if Kind == 'Sequence': - @inlinable // FIXME(sil-serialize-all) - internal init() { } - - @inlinable - internal func _makeIterator() -> AnyIterator { _abstract() } - - @inlinable - internal var _underestimatedCount: Int { _abstract() } - - @inlinable - internal func _map( - _ transform: (Element) throws -> T - ) rethrows -> [T] { - _abstract() - } - - @inlinable - internal func _filter( - _ isIncluded: (Element) throws -> Bool - ) rethrows -> [Element] { - _abstract() - } - - @inlinable - internal func _forEach( - _ body: (Element) throws -> Void - ) rethrows { - _abstract() - } - - @inlinable - internal func __customContainsEquatableElement( - _ element: Element - ) -> Bool? { - _abstract() - } - - @inlinable - internal func __copyToContiguousArray() -> ContiguousArray { - _abstract() - } - - @inlinable - internal func __copyContents(initializing buf: UnsafeMutableBufferPointer) - -> (AnyIterator,UnsafeMutableBufferPointer.Index) { - _abstract() - } - -% end - -% # This deinit has to be present on all the types - @inlinable // FIXME(sil-serialize-all) - deinit {} - -% if Kind == 'Sequence': - @inlinable - internal func _drop( - while predicate: (Element) throws -> Bool - ) rethrows -> _AnySequenceBox { - _abstract() - } - - @inlinable - internal func _dropFirst(_ n: Int) -> _AnySequenceBox { - _abstract() - } - - @inlinable - internal func _dropLast(_ n: Int) -> [Element] { - _abstract() - } - - @inlinable - internal func _prefix(_ maxLength: Int) -> _AnySequenceBox { - _abstract() - } - - @inlinable - internal func _prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> [Element] { - _abstract() - } - - @inlinable - internal func _suffix(_ maxLength: Int) -> [Element] { - _abstract() - } -% else: -% override = 'override' if Kind != 'Collection' else '' - @inlinable - internal override func _drop( - while predicate: (Element) throws -> Bool - ) rethrows -> _Any${Kind}Box { - _abstract() - } - - @inlinable - internal override func _dropFirst(_ n: Int) -> _Any${Kind}Box { - _abstract() - } - - @inlinable - internal ${override} func _dropLast(_ n: Int) -> _Any${Kind}Box { - _abstract() - } - - @inlinable - internal override func _prefix(_ maxLength: Int) -> _Any${Kind}Box { - _abstract() - } - - @inlinable - internal ${override} func _prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> _Any${Kind}Box { - _abstract() - } - - @inlinable - internal ${override} func _suffix(_ maxLength: Int) -> _Any${Kind}Box { - _abstract() - } -% end - -% if Kind == 'Collection': - @inlinable - internal subscript(i: _AnyIndexBox) -> Element { _abstract() } - - @inlinable - internal func _index(after i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } - - @inlinable - internal func _formIndex(after i: _AnyIndexBox) { _abstract() } - - @inlinable - internal func _index( - _ i: _AnyIndexBox, offsetBy n: Int - ) -> _AnyIndexBox { - _abstract() - } - - @inlinable - internal func _index( - _ i: _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox - ) -> _AnyIndexBox? { - _abstract() - } - - @inlinable - internal func _formIndex(_ i: inout _AnyIndexBox, offsetBy n: Int) { - _abstract() - } - - @inlinable - internal func _formIndex( - _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox - ) -> Bool { - _abstract() - } - - @inlinable - internal func _distance( - from start: _AnyIndexBox, to end: _AnyIndexBox - ) -> Int { - _abstract() - } - - // TODO: swift-3-indexing-model: forward the following methods. - /* - var _indices: Indices - - __consuming func prefix(upTo end: Index) -> SubSequence - - __consuming func suffix(from start: Index) -> SubSequence - - func prefix(through position: Index) -> SubSequence - - var isEmpty: Bool { get } - */ - - @inlinable // FIXME(sil-serialize-all) - internal var _count: Int { _abstract() } - - // TODO: swift-3-indexing-model: forward the following methods. - /* - func _customIndexOfEquatableElement(element: Element) -> Index?? - func _customLastIndexOfEquatableElement(element: Element) -> Index?? - */ - - @inlinable - internal init( - _startIndex: _AnyIndexBox, - endIndex: _AnyIndexBox - ) { - self._startIndex = _startIndex - self._endIndex = endIndex - } - - @usableFromInline - internal let _startIndex: _AnyIndexBox - - @usableFromInline - internal let _endIndex: _AnyIndexBox -% end - -% if Kind in ['Collection', 'BidirectionalCollection', 'RandomAccessCollection']: -% override = 'override' if Kind != 'Collection' else '' - @inlinable - internal ${override} subscript( - start start: _AnyIndexBox, - end end: _AnyIndexBox - ) -> _Any${Kind}Box { _abstract() } -% end - -% if Kind == 'BidirectionalCollection': - @inlinable - internal func _index(before i: _AnyIndexBox) -> _AnyIndexBox { _abstract() } - @inlinable - internal func _formIndex(before i: _AnyIndexBox) { _abstract() } -% end -} - -% end - -% for Kind in ['Sequence', 'Collection', 'BidirectionalCollection', 'RandomAccessCollection']: -% if Kind == 'Sequence': -% EqualAndWeakerKinds = ['Sequence'] -% elif Kind == 'Collection': -% EqualAndWeakerKinds = ['Sequence', 'Collection'] -% elif Kind == 'BidirectionalCollection': -% EqualAndWeakerKinds = ['Sequence', 'Collection', 'BidirectionalCollection'] -% elif Kind == 'RandomAccessCollection': -% EqualAndWeakerKinds = ['Sequence', 'Collection', 'BidirectionalCollection', 'RandomAccessCollection'] -% else: -% assert False, 'Unknown kind' -% end - - - -@_fixed_layout -@usableFromInline -internal final class _${Kind}Box: _Any${Kind}Box -{ - @usableFromInline - internal typealias Element = S.Element - - @inline(__always) - @inlinable - internal override func _makeIterator() -> AnyIterator { - return AnyIterator(_base.makeIterator()) - } - @inlinable - internal override var _underestimatedCount: Int { - return _base.underestimatedCount - } - @inlinable - internal override func _map( - _ transform: (Element) throws -> T - ) rethrows -> [T] { - return try _base.map(transform) - } - @inlinable - internal override func _filter( - _ isIncluded: (Element) throws -> Bool - ) rethrows -> [Element] { - return try _base.filter(isIncluded) - } - @inlinable - internal override func _forEach( - _ body: (Element) throws -> Void - ) rethrows { - return try _base.forEach(body) - } - @inlinable - internal override func __customContainsEquatableElement( - _ element: Element - ) -> Bool? { - return _base._customContainsEquatableElement(element) - } - @inlinable - internal override func __copyToContiguousArray() -> ContiguousArray { - return _base._copyToContiguousArray() - } - @inlinable - internal override func __copyContents(initializing buf: UnsafeMutableBufferPointer) - -> (AnyIterator,UnsafeMutableBufferPointer.Index) { - let (it,idx) = _base._copyContents(initializing: buf) - return (AnyIterator(it),idx) - } -% if Kind == 'Sequence': - @inlinable - internal override func _dropFirst(_ n: Int) -> _AnySequenceBox { - return _SequenceBox>(_base: _base.dropFirst(n)) - } - @inlinable - internal override func _drop( - while predicate: (Element) throws -> Bool - ) rethrows -> _Any${Kind}Box { - return try _SequenceBox>(_base: _base.drop(while: predicate)) - } - @inlinable - internal override func _dropLast(_ n: Int) -> [Element] { - return _base.dropLast(n) - } - @inlinable - internal override func _prefix(_ n: Int) -> _AnySequenceBox { - return _SequenceBox>(_base: _base.prefix(n)) - } - @inlinable - internal override func _prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> [Element] { - return try _base.prefix(while: predicate) - } - @inlinable - internal override func _suffix(_ maxLength: Int) -> [Element] { - return _base.suffix(maxLength) - } -% else: - @inline(__always) - @inlinable - internal override func _drop( - while predicate: (Element) throws -> Bool - ) rethrows -> _Any${Kind}Box { - return try _${Kind}Box(_base: _base.drop(while: predicate)) - } - @inline(__always) - @inlinable - internal override func _dropFirst(_ n: Int) -> _Any${Kind}Box { - return _${Kind}Box(_base: _base.dropFirst(n)) - } - @inline(__always) - @inlinable - internal override func _dropLast(_ n: Int) -> _Any${Kind}Box { - return _${Kind}Box(_base: _base.dropLast(n)) - } - @inline(__always) - @inlinable - internal override func _prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> _Any${Kind}Box { - return try _${Kind}Box(_base: _base.prefix(while: predicate)) - } - @inline(__always) - @inlinable - internal override func _prefix(_ maxLength: Int) -> _Any${Kind}Box { - return _${Kind}Box(_base: _base.prefix(maxLength)) - } - @inline(__always) - @inlinable - internal override func _suffix(_ maxLength: Int) -> _Any${Kind}Box { - return _${Kind}Box(_base: _base.suffix(maxLength)) - } -% end - - @inlinable // FIXME(sil-serialize-all) - deinit {} - -% if Kind == 'Sequence': - @inlinable - internal init(_base: S) { - self._base = _base - } -% else: - @inlinable - internal init(_base: S) { - self._base = _base - super.init( - _startIndex: _IndexBox(_base: _base.startIndex), - endIndex: _IndexBox(_base: _base.endIndex)) - } - - @inlinable - internal func _unbox( - _ position: _AnyIndexBox, file: StaticString = #file, line: UInt = #line - ) -> S.Index { - if let i = position._unbox() as S.Index? { - return i - } - fatalError("Index type mismatch!", file: file, line: line) - } - - @inlinable - internal override subscript(position: _AnyIndexBox) -> Element { - return _base[_unbox(position)] - } - - @inlinable - internal override subscript(start start: _AnyIndexBox, end end: _AnyIndexBox) - -> _Any${Kind}Box - { - return _${Kind}Box(_base: - _base[_unbox(start)..<_unbox(end)] - ) - } - - @inlinable - internal override func _index(after position: _AnyIndexBox) -> _AnyIndexBox { - return _IndexBox(_base: _base.index(after: _unbox(position))) - } - - @inlinable - internal override func _formIndex(after position: _AnyIndexBox) { - if let p = position as? _IndexBox { - return _base.formIndex(after: &p._base) - } - fatalError("Index type mismatch!") - } - - @inlinable - internal override func _index( - _ i: _AnyIndexBox, offsetBy n: Int - ) -> _AnyIndexBox { - return _IndexBox(_base: _base.index(_unbox(i), offsetBy: numericCast(n))) - } - - @inlinable - internal override func _index( - _ i: _AnyIndexBox, - offsetBy n: Int, - limitedBy limit: _AnyIndexBox - ) -> _AnyIndexBox? { - return _base.index( - _unbox(i), - offsetBy: numericCast(n), - limitedBy: _unbox(limit)) - .map { _IndexBox(_base: $0) } - } - - @inlinable - internal override func _formIndex( - _ i: inout _AnyIndexBox, offsetBy n: Int - ) { - if let box = i as? _IndexBox { - return _base.formIndex(&box._base, offsetBy: numericCast(n)) - } - fatalError("Index type mismatch!") - } - - @inlinable - internal override func _formIndex( - _ i: inout _AnyIndexBox, offsetBy n: Int, limitedBy limit: _AnyIndexBox - ) -> Bool { - if let box = i as? _IndexBox { - return _base.formIndex( - &box._base, - offsetBy: numericCast(n), - limitedBy: _unbox(limit)) - } - fatalError("Index type mismatch!") - } - - @inlinable - internal override func _distance( - from start: _AnyIndexBox, - to end: _AnyIndexBox - ) -> Int { - return numericCast(_base.distance(from: _unbox(start), to: _unbox(end))) - } - - @inlinable - internal override var _count: Int { - return numericCast(_base.count) - } - -% if Kind in ['BidirectionalCollection', 'RandomAccessCollection']: - @inlinable - internal override func _index(before position: _AnyIndexBox) -> _AnyIndexBox { - return _IndexBox(_base: _base.index(before: _unbox(position))) - } - - @inlinable - internal override func _formIndex(before position: _AnyIndexBox) { - if let p = position as? _IndexBox { - return _base.formIndex(before: &p._base) - } - fatalError("Index type mismatch!") - } - -% end - -% end - @usableFromInline - internal var _base: S -} -% end - -@usableFromInline -@frozen -internal struct _ClosureBasedSequence { - @usableFromInline - internal var _makeUnderlyingIterator: () -> Iterator - - @inlinable - internal init(_ makeUnderlyingIterator: @escaping () -> Iterator) { - self._makeUnderlyingIterator = makeUnderlyingIterator - } -} - -extension _ClosureBasedSequence: Sequence { - @inlinable - internal func makeIterator() -> Iterator { - return _makeUnderlyingIterator() - } -} - -/// A type-erased sequence. -/// -/// An instance of `AnySequence` forwards its operations to an underlying base -/// sequence having the same `Element` type, hiding the specifics of the -/// underlying sequence. -@frozen -public struct AnySequence { - @usableFromInline - internal let _box: _AnySequenceBox - - /// Creates a sequence whose `makeIterator()` method forwards to - /// `makeUnderlyingIterator`. - @inlinable - public init( - _ makeUnderlyingIterator: @escaping () -> I - ) where I.Element == Element { - self.init(_ClosureBasedSequence(makeUnderlyingIterator)) - } - - @inlinable - internal init(_box: _AnySequenceBox) { - self._box = _box - } -} - -extension AnySequence: Sequence { - public typealias Iterator = AnyIterator - - /// Creates a new sequence that wraps and forwards operations to `base`. - @inlinable - public init(_ base: S) - where - S.Element == Element { - self._box = _SequenceBox(_base: base) - } -} - -% for Kind in ['Sequence', 'Collection', 'BidirectionalCollection', 'RandomAccessCollection']: -extension Any${Kind} { -% if Kind == 'Sequence': - /// Returns an iterator over the elements of this sequence. - @inline(__always) - @inlinable - public __consuming func makeIterator() -> Iterator { - return _box._makeIterator() - } - @inlinable - public __consuming func dropLast(_ n: Int = 1) -> [Element] { - return _box._dropLast(n) - } - @inlinable - public __consuming func prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> [Element] { - return try _box._prefix(while: predicate) - } - @inlinable - public __consuming func suffix(_ maxLength: Int) -> [Element] { - return _box._suffix(maxLength) - } -% else: - /// Returns an iterator over the elements of this collection. - @inline(__always) - @inlinable - public __consuming func makeIterator() -> Iterator { - return _box._makeIterator() - } - @inlinable - public __consuming func dropLast(_ n: Int = 1) -> Any${Kind} { - return Any${Kind}(_box: _box._dropLast(n)) - } - @inlinable - public __consuming func prefix( - while predicate: (Element) throws -> Bool - ) rethrows -> Any${Kind} { - return try Any${Kind}(_box: _box._prefix(while: predicate)) - } - @inlinable - public __consuming func suffix(_ maxLength: Int) -> Any${Kind} { - return Any${Kind}(_box: _box._suffix(maxLength)) - } -% end - - @inlinable - public var underestimatedCount: Int { - return _box._underestimatedCount - } - - @inlinable - public func map( - _ transform: (Element) throws -> T - ) rethrows -> [T] { - return try _box._map(transform) - } - - @inlinable - public __consuming func filter( - _ isIncluded: (Element) throws -> Bool - ) rethrows -> [Element] { - return try _box._filter(isIncluded) - } - - @inlinable - public __consuming func forEach( - _ body: (Element) throws -> Void - ) rethrows { - return try _box._forEach(body) - } - - @inlinable - public __consuming func drop( - while predicate: (Element) throws -> Bool - ) rethrows -> Any${Kind} { - return try Any${Kind}(_box: _box._drop(while: predicate)) - } - - @inlinable - public __consuming func dropFirst(_ n: Int = 1) -> Any${Kind} { - return Any${Kind}(_box: _box._dropFirst(n)) - } - - @inlinable - public __consuming func prefix(_ maxLength: Int = 1) -> Any${Kind} { - return Any${Kind}(_box: _box._prefix(maxLength)) - } - - @inlinable - public func _customContainsEquatableElement( - _ element: Element - ) -> Bool? { - return _box.__customContainsEquatableElement(element) - } - - @inlinable - public __consuming func _copyToContiguousArray() -> ContiguousArray { - return self._box.__copyToContiguousArray() - } - - @inlinable - public __consuming func _copyContents(initializing buf: UnsafeMutableBufferPointer) - -> (AnyIterator,UnsafeMutableBufferPointer.Index) { - let (it,idx) = _box.__copyContents(initializing: buf) - return (AnyIterator(it),idx) - } -} -% end - -//===--- Index ------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -@usableFromInline -internal protocol _AnyIndexBox: class { - var _typeID: ObjectIdentifier { get } - - func _unbox() -> T? - - func _isEqual(to rhs: _AnyIndexBox) -> Bool - - func _isLess(than rhs: _AnyIndexBox) -> Bool -} - -@_fixed_layout -@usableFromInline -internal final class _IndexBox: _AnyIndexBox { - @usableFromInline - internal var _base: BaseIndex - - @inlinable - internal init(_base: BaseIndex) { - self._base = _base - } - - @inlinable - internal func _unsafeUnbox(_ other: _AnyIndexBox) -> BaseIndex { - return unsafeDowncast(other, to: _IndexBox.self)._base - } - - @inlinable - internal var _typeID: ObjectIdentifier { - return ObjectIdentifier(type(of: self)) - } - - @inlinable - internal func _unbox() -> T? { - return (self as _AnyIndexBox as? _IndexBox)?._base - } - - @inlinable - internal func _isEqual(to rhs: _AnyIndexBox) -> Bool { - return _base == _unsafeUnbox(rhs) - } - - @inlinable - internal func _isLess(than rhs: _AnyIndexBox) -> Bool { - return _base < _unsafeUnbox(rhs) - } -} - -/// A wrapper over an underlying index that hides the specific underlying type. -@frozen -public struct AnyIndex { - @usableFromInline - internal var _box: _AnyIndexBox - - /// Creates a new index wrapping `base`. - @inlinable - public init(_ base: BaseIndex) { - self._box = _IndexBox(_base: base) - } - - @inlinable - internal init(_box: _AnyIndexBox) { - self._box = _box - } - - @inlinable - internal var _typeID: ObjectIdentifier { - return _box._typeID - } -} - -extension AnyIndex: Comparable { - /// Returns a Boolean value indicating whether two indices wrap equal - /// underlying indices. - /// - /// The types of the two underlying indices must be identical. - /// - /// - Parameters: - /// - lhs: An index to compare. - /// - rhs: Another index to compare. - @inlinable - public static func == (lhs: AnyIndex, rhs: AnyIndex) -> Bool { - _precondition(lhs._typeID == rhs._typeID, "Base index types differ") - return lhs._box._isEqual(to: rhs._box) - } - - /// Returns a Boolean value indicating whether the first argument represents a - /// position before the second argument. - /// - /// The types of the two underlying indices must be identical. - /// - /// - Parameters: - /// - lhs: An index to compare. - /// - rhs: Another index to compare. - @inlinable - public static func < (lhs: AnyIndex, rhs: AnyIndex) -> Bool { - _precondition(lhs._typeID == rhs._typeID, "Base index types differ") - return lhs._box._isLess(than: rhs._box) - } -} - -//===--- Collections ------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -public // @testable -protocol _AnyCollectionProtocol: Collection { - /// Identifies the underlying collection stored by `self`. Instances - /// copied or upgraded/downgraded from one another have the same `_boxID`. - var _boxID: ObjectIdentifier { get } -} - -% for (ti, Traversal) in enumerate(TRAVERSALS): -% SelfProtocol = collectionForTraversal(Traversal) -% Self = 'Any' + SelfProtocol -/// A type-erased wrapper over any collection with indices that -/// support ${Traversal.lower().replace('omacc', 'om acc')} traversal. -/// -/// An `${Self}` instance forwards its operations to a base collection having the -/// same `Element` type, hiding the specifics of the underlying -/// collection. -@frozen -public struct ${Self} { - @usableFromInline - internal let _box: _${Self}Box - - @inlinable - internal init(_box: _${Self}Box) { - self._box = _box - } -} - -extension ${Self}: ${SelfProtocol} { - public typealias Indices = DefaultIndices<${Self}> - public typealias Iterator = AnyIterator - public typealias Index = AnyIndex - public typealias SubSequence = ${Self} - -% for SubTraversal in TRAVERSALS[ti:]: -% SubProtocol = collectionForTraversal(SubTraversal) - /// Creates a type-erased collection that wraps the given collection. - /// - /// - Parameter base: The collection to wrap. - /// - /// - Complexity: O(1). - @inline(__always) - @inlinable - public init(_ base: C) where C.Element == Element { - // Traversal: ${Traversal} - // SubTraversal: ${SubTraversal} - self._box = _${SubProtocol}Box( - _base: base) - } - - /// Creates an `${Self}` having the same underlying collection as `other`. - /// - /// - Complexity: O(1) - @inlinable - public init(_ other: Any${SubProtocol}) { - self._box = other._box - } -% end - -% for SuperTraversal in TRAVERSALS[:ti]: - /// Creates an `${Self}` having the same underlying collection as `other`. - /// - /// If the underlying collection stored by `other` does not satisfy - /// `${SelfProtocol}`, the result is `nil`. - /// - /// - Complexity: O(1) - @inlinable - public init?(_ other: Any${collectionForTraversal(SuperTraversal)}) { - guard let box = - other._box as? _${Self}Box else { - return nil - } - self._box = box - } -% end - - /// The position of the first element in a non-empty collection. - /// - /// In an empty collection, `startIndex == endIndex`. - @inlinable - public var startIndex: Index { - return AnyIndex(_box: _box._startIndex) - } - - /// The collection's "past the end" position---that is, the position one - /// greater than the last valid subscript argument. - /// - /// `endIndex` is always reachable from `startIndex` by zero or more - /// applications of `index(after:)`. - @inlinable - public var endIndex: Index { - return AnyIndex(_box: _box._endIndex) - } - - /// Accesses the element indicated by `position`. - /// - /// - Precondition: `position` indicates a valid position in `self` and - /// `position != endIndex`. - @inlinable - public subscript(position: Index) -> Element { - return _box[position._box] - } - - @inlinable - public subscript(bounds: Range) -> SubSequence { - return ${Self}(_box: - _box[start: bounds.lowerBound._box, end: bounds.upperBound._box]) - } - - @inlinable - public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { - // Do nothing. Doing a range check would involve unboxing indices, - // performing dynamic dispatch etc. This seems to be too costly for a fast - // range check for QoI purposes. - } - - @inlinable - public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { - // Do nothing. Doing a range check would involve unboxing indices, - // performing dynamic dispatch etc. This seems to be too costly for a fast - // range check for QoI purposes. - } - - @inlinable - public func index(after i: Index) -> Index { - return AnyIndex(_box: _box._index(after: i._box)) - } - - @inlinable - public func formIndex(after i: inout Index) { - if _isUnique(&i._box) { - _box._formIndex(after: i._box) - } - else { - i = index(after: i) - } - } - - @inlinable - public func index(_ i: Index, offsetBy n: Int) -> Index { - return AnyIndex(_box: _box._index(i._box, offsetBy: n)) - } - - @inlinable - public func index( - _ i: Index, offsetBy n: Int, limitedBy limit: Index - ) -> Index? { - return _box._index(i._box, offsetBy: n, limitedBy: limit._box) - .map { AnyIndex(_box:$0) } - } - - @inlinable - public func formIndex(_ i: inout Index, offsetBy n: Int) { - if _isUnique(&i._box) { - return _box._formIndex(&i._box, offsetBy: n) - } else { - i = index(i, offsetBy: n) - } - } - - @inlinable - public func formIndex( - _ i: inout Index, - offsetBy n: Int, - limitedBy limit: Index - ) -> Bool { - if _isUnique(&i._box) { - return _box._formIndex(&i._box, offsetBy: n, limitedBy: limit._box) - } - if let advanced = index(i, offsetBy: n, limitedBy: limit) { - i = advanced - return true - } - i = limit - return false - } - - @inlinable - public func distance(from start: Index, to end: Index) -> Int { - return _box._distance(from: start._box, to: end._box) - } - - /// The number of elements. - /// -% if Traversal != 'RandomAccess': - /// To check whether a collection is empty, use its `isEmpty` property - /// instead of comparing `count` to zero. Calculating `count` can be an O(*n*) - /// operation. - /// -% end - /// - Complexity: ${'O(1)' if Traversal == 'RandomAccess' else 'O(*n*)'} - @inlinable - public var count: Int { - return _box._count - } - -% if Traversal == 'Bidirectional' or Traversal == 'RandomAccess': - @inlinable - public func index(before i: Index) -> Index { - return AnyIndex(_box: _box._index(before: i._box)) - } - - @inlinable - public func formIndex(before i: inout Index) { - if _isUnique(&i._box) { - _box._formIndex(before: i._box) - } - else { - i = index(before: i) - } - } -% end -} - -extension ${Self}: _AnyCollectionProtocol { - /// Uniquely identifies the stored underlying collection. - @inlinable - public // Due to language limitations only - var _boxID: ObjectIdentifier { - return ObjectIdentifier(_box) - } -} -% end - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/Filter.swift b/stdlib/public/core/Filter.swift index 694961585d01f..d643eaca126b3 100644 --- a/stdlib/public/core/Filter.swift +++ b/stdlib/public/core/Filter.swift @@ -339,7 +339,7 @@ extension LazyFilterSequence { _ isIncluded: @escaping (Element) -> Bool ) -> LazyFilterSequence { return LazyFilterSequence(_base: _base) { - isIncluded($0) && self._predicate($0) + self._predicate($0) && isIncluded($0) } } } diff --git a/stdlib/public/core/FixedArray.swift b/stdlib/public/core/FixedArray.swift index 47f5b5f4f0d02..ef864a4329081 100644 --- a/stdlib/public/core/FixedArray.swift +++ b/stdlib/public/core/FixedArray.swift @@ -1,8 +1,8 @@ -//===--- FixedArray.swift.gyb ---------------------------------*- swift -*-===// +//===--- FixedArray.swift -------------------------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/stdlib/public/core/FloatingPointParsing.swift.gyb b/stdlib/public/core/FloatingPointParsing.swift.gyb index 1225f297d12d5..b4cddc1a467bd 100644 --- a/stdlib/public/core/FloatingPointParsing.swift.gyb +++ b/stdlib/public/core/FloatingPointParsing.swift.gyb @@ -126,25 +126,38 @@ extension ${Self}: LosslessStringConvertible { /// is `nil`. @inlinable // FIXME(sil-serialize-all) public init?(_ text: S) { - let u8 = text.utf8 - - let (result, n): (${Self}, Int) = text.withCString { chars in + let result: ${Self}? = text.withCString { chars in + // TODO: We should change the ABI for + // _swift_stdlib_strtoX_clocale so that it returns + // a boolean `false` for leading whitespace, empty + // string, or invalid character. Then we could avoid + // inlining these checks into every single client. + switch chars[0] { + case 9, 10, 11, 12, 13, 32: + // Reject any input with leading whitespace. + return nil + case 0: + // Reject the empty string + return nil + default: + break + } var result: ${Self} = 0 let endPtr = withUnsafeMutablePointer(to: &result) { _swift_stdlib_strto${cFuncSuffix2[bits]}_clocale(chars, $0) } - return (result, endPtr == nil ? 0 : endPtr! - chars) + // Verify that all the characters were consumed. + if endPtr == nil || endPtr![0] != 0 { + return nil + } + return result } - if n == 0 || n != u8.count - || u8.contains(where: { codeUnit in - // Check if the code unit is either non-ASCII or if isspace(codeUnit) - // would return nonzero when the current locale is the C locale. - codeUnit > 127 || "\t\n\u{b}\u{c}\r ".utf8.contains(codeUnit) - }) { + if let result = result { + self = result + } else { return nil } - self = result } } diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 050752ea176bb..56e85f0dfc57d 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -1557,6 +1557,7 @@ extension BinaryInteger { } /// A textual representation of this value. + @_semantics("binaryInteger.description") public var description: String { return _description(radix: 10, uppercase: false) } @@ -2746,7 +2747,7 @@ extension FixedWidthInteger { // If we used &+ instead, the result would be zero, which isn't helpful, // so we actually need to handle this case separately. if delta == Magnitude.max { - return Self(truncatingIfNeeded: generator.next() as Magnitude) + return Self(truncatingIfNeeded: generator.next()) } // Need to widen delta to account for the right-endpoint of a closed range. delta += 1 @@ -3366,7 +3367,7 @@ extension FixedWidthInteger { using generator: inout R ) -> Self { if bitWidth <= UInt64.bitWidth { - return Self(truncatingIfNeeded: generator.next() as UInt64) + return Self(truncatingIfNeeded: generator.next()) } let (quotient, remainder) = bitWidth.quotientAndRemainder( diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 2180ab7471d3e..414d3e09d290e 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -3151,7 +3151,7 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor { case .unresolvedIndirectOffset(let pointerToOffset): // Look up offset in the indirectly-referenced variable we have a // pointer. - assert(pointerToOffset.pointee <= UInt32.max) + _internalInvariant(pointerToOffset.pointee <= UInt32.max) let offset = UInt32(truncatingIfNeeded: pointerToOffset.pointee) let header = RawKeyPathComponent.Header(storedWithOutOfLineOffset: kind, mutable: mutable) diff --git a/stdlib/public/core/MigrationSupport.swift b/stdlib/public/core/MigrationSupport.swift index 59a48f5cf2ea0..16124058138bc 100644 --- a/stdlib/public/core/MigrationSupport.swift +++ b/stdlib/public/core/MigrationSupport.swift @@ -390,16 +390,16 @@ extension UnsafeMutablePointer { extension UnsafeMutableRawPointer { @available(*, unavailable, renamed: "init(mutating:)") - public init(_ from: UnsafeRawPointer) { Builtin.unreachable() } + public init(@_nonEphemeral _ from: UnsafeRawPointer) { Builtin.unreachable() } @available(*, unavailable, renamed: "init(mutating:)") - public init?(_ from: UnsafeRawPointer?) { Builtin.unreachable() } + public init?(@_nonEphemeral _ from: UnsafeRawPointer?) { Builtin.unreachable() } @available(*, unavailable, renamed: "init(mutating:)") - public init(_ from: UnsafePointer) { Builtin.unreachable() } + public init(@_nonEphemeral _ from: UnsafePointer) { Builtin.unreachable() } @available(*, unavailable, renamed: "init(mutating:)") - public init?(_ from: UnsafePointer?) { Builtin.unreachable() } + public init?(@_nonEphemeral _ from: UnsafePointer?) { Builtin.unreachable() } } extension UnsafeRawPointer: _CustomPlaygroundQuickLookable { diff --git a/stdlib/public/core/Mirrors.swift b/stdlib/public/core/Mirrors.swift new file mode 100644 index 0000000000000..171adc99673f6 --- /dev/null +++ b/stdlib/public/core/Mirrors.swift @@ -0,0 +1,260 @@ +//===--- Mirrors.swift - Common _Mirror implementations -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Float: CustomReflectable { + /// A mirror that reflects the `Float` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Float: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Float` instance. + @available(*, deprecated, message: "Float.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .float(self) + } +} + +extension Double: CustomReflectable { + /// A mirror that reflects the `Double` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Double: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Double` instance. + @available(*, deprecated, message: "Double.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .double(self) + } +} + +extension Bool: CustomReflectable { + /// A mirror that reflects the `Bool` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Bool: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Bool` instance. + @available(*, deprecated, message: "Bool.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .bool(self) + } +} + +extension String: CustomReflectable { + /// A mirror that reflects the `String` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension String: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `String` instance. + @available(*, deprecated, message: "String.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .text(self) + } +} + +extension Character: CustomReflectable { + /// A mirror that reflects the `Character` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Character: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Character` instance. + @available(*, deprecated, message: "Character.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .text(String(self)) + } +} + +extension Unicode.Scalar: CustomReflectable { + /// A mirror that reflects the `Unicode.Scalar` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Unicode.Scalar: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Unicode.Scalar` instance. + @available(*, deprecated, message: "Unicode.Scalar.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension UInt8: CustomReflectable { + /// A mirror that reflects the `UInt8` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension UInt8: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `UInt8` instance. + @available(*, deprecated, message: "UInt8.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension Int8: CustomReflectable { + /// A mirror that reflects the `Int8` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Int8: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Int8` instance. + @available(*, deprecated, message: "Int8.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .int(Int64(self)) + } +} + +extension UInt16: CustomReflectable { + /// A mirror that reflects the `UInt16` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension UInt16: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `UInt16` instance. + @available(*, deprecated, message: "UInt16.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension Int16: CustomReflectable { + /// A mirror that reflects the `Int16` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Int16: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Int16` instance. + @available(*, deprecated, message: "Int16.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .int(Int64(self)) + } +} + +extension UInt32: CustomReflectable { + /// A mirror that reflects the `UInt32` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension UInt32: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `UInt32` instance. + @available(*, deprecated, message: "UInt32.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension Int32: CustomReflectable { + /// A mirror that reflects the `Int32` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Int32: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Int32` instance. + @available(*, deprecated, message: "Int32.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .int(Int64(self)) + } +} + +extension UInt64: CustomReflectable { + /// A mirror that reflects the `UInt64` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension UInt64: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `UInt64` instance. + @available(*, deprecated, message: "UInt64.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension Int64: CustomReflectable { + /// A mirror that reflects the `Int64` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Int64: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Int64` instance. + @available(*, deprecated, message: "Int64.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .int(Int64(self)) + } +} + +extension UInt: CustomReflectable { + /// A mirror that reflects the `UInt` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension UInt: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `UInt` instance. + @available(*, deprecated, message: "UInt.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .uInt(UInt64(self)) + } +} + +extension Int: CustomReflectable { + /// A mirror that reflects the `Int` instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +extension Int: _CustomPlaygroundQuickLookable { + /// A custom playground Quick Look for the `Int` instance. + @available(*, deprecated, message: "Int.customPlaygroundQuickLook will be removed in a future Swift version") + public var customPlaygroundQuickLook: _PlaygroundQuickLook { + return .int(Int64(self)) + } +} + +#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) +extension Float80: CustomReflectable { + /// A mirror that reflects the Float80 instance. + public var customMirror: Mirror { + return Mirror(self, unlabeledChildren: EmptyCollection()) + } +} +#endif diff --git a/stdlib/public/core/Mirrors.swift.gyb b/stdlib/public/core/Mirrors.swift.gyb deleted file mode 100644 index f6840b5b6b28a..0000000000000 --- a/stdlib/public/core/Mirrors.swift.gyb +++ /dev/null @@ -1,67 +0,0 @@ -//===--- Mirrors.swift.gyb - Common _Mirror implementations ---*- swift -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -%{ - -from SwiftIntTypes import all_integer_types - -# Number of bits in the Builtin.Word type -word_bits = int(CMAKE_SIZEOF_VOID_P) * 8 - -Types = [ - ('Float', '.float', 'self'), - ('Double', '.double', 'self'), - ('Bool', '.bool', 'self'), - ('String', '.text', 'self'), - ('Character', '.text', 'String(self)'), - ('Unicode.Scalar', '.uInt', 'UInt64(self)'), -] - -for self_ty in all_integer_types(word_bits): - Self = self_ty.stdlib_name - if self_ty.is_signed: - Types.append( (Self, '.int', 'Int64(self)') ) - else: - Types.append( (Self, '.uInt', 'UInt64(self)') ) - -}% - -%for Type in Types: - -extension ${Type[0]}: CustomReflectable { - /// A mirror that reflects the `${Type[0]}` instance. - public var customMirror: Mirror { - return Mirror(self, unlabeledChildren: EmptyCollection()) - } -} - -extension ${Type[0]}: _CustomPlaygroundQuickLookable { - /// A custom playground Quick Look for the `${Type[0]}` instance. - @available(*, deprecated, message: "${Type[0]}.customPlaygroundQuickLook will be removed in a future Swift version") - public var customPlaygroundQuickLook: _PlaygroundQuickLook { - return ${Type[1]}(${Type[2]}) - } -} -% end - -#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) -extension Float80: CustomReflectable { - /// A mirror that reflects the Float80 instance. - public var customMirror: Mirror { - return Mirror(self, unlabeledChildren: EmptyCollection()) - } -} -#endif - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/NativeDictionary.swift b/stdlib/public/core/NativeDictionary.swift index 4dcbd68ce642d..e387b966f4b09 100644 --- a/stdlib/public/core/NativeDictionary.swift +++ b/stdlib/public/core/NativeDictionary.swift @@ -161,7 +161,7 @@ extension _NativeDictionary { // Low-level lookup operations @inlinable @inline(__always) internal func find(_ key: Key) -> (bucket: Bucket, found: Bool) { - return find(key, hashValue: self.hashValue(for: key)) + return _storage.find(key) } /// Search for a given element, assuming it has the specified hash value. @@ -174,58 +174,54 @@ extension _NativeDictionary { // Low-level lookup operations _ key: Key, hashValue: Int ) -> (bucket: Bucket, found: Bool) { - let hashTable = self.hashTable - var bucket = hashTable.idealBucket(forHashValue: hashValue) - while hashTable._isOccupied(bucket) { - if uncheckedKey(at: bucket) == key { - return (bucket, true) - } - bucket = hashTable.bucket(wrappedAfter: bucket) - } - return (bucket, false) + return _storage.find(key, hashValue: hashValue) } } extension _NativeDictionary { // ensureUnique - @inlinable - internal mutating func resize(capacity: Int) { + @_alwaysEmitIntoClient + @inline(never) + internal mutating func _copyOrMoveAndResize( + capacity: Int, + moveElements: Bool + ) { let capacity = Swift.max(capacity, self.capacity) let newStorage = _DictionaryStorage.resize( original: _storage, capacity: capacity, - move: true) + move: moveElements) let result = _NativeDictionary(newStorage) if count > 0 { for bucket in hashTable { - let key = (_keys + bucket.offset).move() - let value = (_values + bucket.offset).move() + let key: Key + let value: Value + if moveElements { + key = (_keys + bucket.offset).move() + value = (_values + bucket.offset).move() + } else { + key = self.uncheckedKey(at: bucket) + value = self.uncheckedValue(at: bucket) + } result._unsafeInsertNew(key: key, value: value) } - // Clear out old storage, ensuring that its deinit won't overrelease the - // elements we've just moved out. - _storage._hashTable.clear() - _storage._count = 0 + if moveElements { + // Clear out old storage, ensuring that its deinit won't overrelease the + // elements we've just moved out. + _storage._hashTable.clear() + _storage._count = 0 + } } _storage = result._storage } @inlinable - @_semantics("optimize.sil.specialize.generic.size.never") + internal mutating func resize(capacity: Int) { + _copyOrMoveAndResize(capacity: capacity, moveElements: true) + } + + @inlinable internal mutating func copyAndResize(capacity: Int) { - let capacity = Swift.max(capacity, self.capacity) - let newStorage = _DictionaryStorage.resize( - original: _storage, - capacity: capacity, - move: false) - let result = _NativeDictionary(newStorage) - if count > 0 { - for bucket in hashTable { - result._unsafeInsertNew( - key: self.uncheckedKey(at: bucket), - value: self.uncheckedValue(at: bucket)) - } - } - _storage = result._storage + _copyOrMoveAndResize(capacity: capacity, moveElements: false) } @inlinable diff --git a/stdlib/public/core/OptionSet.swift b/stdlib/public/core/OptionSet.swift index 47d9141c156c0..427a33d960edc 100644 --- a/stdlib/public/core/OptionSet.swift +++ b/stdlib/public/core/OptionSet.swift @@ -286,9 +286,13 @@ extension OptionSet where Element == Self { @inlinable // generic-performance @discardableResult public mutating func remove(_ member: Element) -> Element? { - let r = isSuperset(of: member) ? Optional(member) : nil + let intersectionElements = intersection(member) + guard !intersectionElements.isEmpty else { + return nil + } + self.subtract(member) - return r + return intersectionElements } /// Inserts the given element into the set. diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index a00bab337d49f..358696961007b 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -384,7 +384,9 @@ internal func _print_unlocked( // string. Check for Optional first, before checking protocol // conformance below, because an Optional value is convertible to a // protocol if its wrapped type conforms to that protocol. - if _isOptional(type(of: value)) { + // Note: _isOptional doesn't work here when T == Any, hence we + // use a more elaborate formulation: + if _openExistential(type(of: value as Any), do: _isOptional) { let debugPrintable = value as! CustomDebugStringConvertible debugPrintable.debugDescription.write(to: &target) return diff --git a/stdlib/public/core/Pointer.swift b/stdlib/public/core/Pointer.swift index 602a80632141c..0d14423c743ef 100644 --- a/stdlib/public/core/Pointer.swift +++ b/stdlib/public/core/Pointer.swift @@ -80,7 +80,7 @@ extension _Pointer { /// /// - Parameter other: The typed pointer to convert. @_transparent - public init(_ other: Self) { + public init(@_nonEphemeral _ other: Self) { self.init(other._rawValue) } @@ -89,7 +89,7 @@ extension _Pointer { /// - Parameter other: The typed pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: Self?) { + public init?(@_nonEphemeral _ other: Self?) { guard let unwrapped = other else { return nil } self.init(unwrapped._rawValue) } @@ -267,17 +267,17 @@ extension UInt { // Pointer arithmetic operators (formerly via Strideable) extension Strideable where Self: _Pointer { @_transparent - public static func + (lhs: Self, rhs: Self.Stride) -> Self { + public static func + (@_nonEphemeral lhs: Self, rhs: Self.Stride) -> Self { return lhs.advanced(by: rhs) } @_transparent - public static func + (lhs: Self.Stride, rhs: Self) -> Self { + public static func + (lhs: Self.Stride, @_nonEphemeral rhs: Self) -> Self { return rhs.advanced(by: lhs) } @_transparent - public static func - (lhs: Self, rhs: Self.Stride) -> Self { + public static func - (@_nonEphemeral lhs: Self, rhs: Self.Stride) -> Self { return lhs.advanced(by: -rhs) } diff --git a/stdlib/public/core/Random.swift b/stdlib/public/core/Random.swift index 58bfeff2da169..1d32a55527cd6 100644 --- a/stdlib/public/core/Random.swift +++ b/stdlib/public/core/Random.swift @@ -144,6 +144,7 @@ extension RandomNumberGenerator { /// - Apple platforms use `arc4random_buf(3)`. /// - Linux platforms use `getrandom(2)` when available; otherwise, they read /// from `/dev/urandom`. +/// - Windows uses `BCryptGenRandom`. @frozen public struct SystemRandomNumberGenerator: RandomNumberGenerator { /// Creates a new instance of the system's default random number generator. diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift index c6ae18393087c..29fd24bf1b081 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift +++ b/stdlib/public/core/RangeReplaceableCollection.swift @@ -1135,8 +1135,6 @@ extension RangeReplaceableCollection { public mutating func removeAll( where shouldBeRemoved: (Element) throws -> Bool ) rethrows { - // FIXME: Switch to using RRC.filter once stdlib is compiled for 4.0 - // self = try filter { try !predicate($0) } - self = try Self(self.lazy.filter { try !shouldBeRemoved($0) }) + self = try filter { try !shouldBeRemoved($0) } } } diff --git a/stdlib/public/core/Runtime.swift.gyb b/stdlib/public/core/Runtime.swift similarity index 51% rename from stdlib/public/core/Runtime.swift.gyb rename to stdlib/public/core/Runtime.swift index 90788227a8541..477ab09afccdf 100644 --- a/stdlib/public/core/Runtime.swift.gyb +++ b/stdlib/public/core/Runtime.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -25,8 +25,8 @@ public // @testable func _stdlib_atomicCompareExchangeStrongPtr( object target: UnsafeMutablePointer, expected: UnsafeMutablePointer, - desired: UnsafeRawPointer?) -> Bool { - + desired: UnsafeRawPointer? +) -> Bool { // We use Builtin.Word here because Builtin.RawPointer can't be nil. let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Word( target._rawValue, @@ -36,7 +36,6 @@ func _stdlib_atomicCompareExchangeStrongPtr( return Bool(won) } -% for optional in ['', '?']: /// Atomic compare and exchange of `UnsafeMutablePointer` with sequentially /// consistent memory ordering. Precise semantics are defined in C++11 or C11. /// @@ -67,9 +66,53 @@ func _stdlib_atomicCompareExchangeStrongPtr( @_transparent public // @testable func _stdlib_atomicCompareExchangeStrongPtr( - object target: UnsafeMutablePointer${optional}>, - expected: UnsafeMutablePointer${optional}>, - desired: UnsafeMutablePointer${optional} + object target: UnsafeMutablePointer>, + expected: UnsafeMutablePointer>, + desired: UnsafeMutablePointer +) -> Bool { + let rawTarget = UnsafeMutableRawPointer(target).assumingMemoryBound( + to: Optional.self) + let rawExpected = UnsafeMutableRawPointer(expected).assumingMemoryBound( + to: Optional.self) + return _stdlib_atomicCompareExchangeStrongPtr( + object: rawTarget, + expected: rawExpected, + desired: UnsafeRawPointer(desired)) +} + +/// Atomic compare and exchange of `UnsafeMutablePointer` with sequentially +/// consistent memory ordering. Precise semantics are defined in C++11 or C11. +/// +/// - Warning: This operation is extremely tricky to use correctly because of +/// writeback semantics. +/// +/// It is best to use it directly on an +/// `UnsafeMutablePointer>` that is known to point +/// directly to the memory where the value is stored. +/// +/// In a call like this: +/// +/// _stdlib_atomicCompareExchangeStrongPtr(&foo.property1.property2, ...) +/// +/// you need to manually make sure that: +/// +/// - all properties in the chain are physical (to make sure that no writeback +/// happens; the compare-and-exchange instruction should operate on the +/// shared memory); and +/// +/// - the shared memory that you are accessing is located inside a heap +/// allocation (a class instance property, a `_BridgingBuffer`, a pointer to +/// an `Array` element etc.) +/// +/// If the conditions above are not met, the code will still compile, but the +/// compare-and-exchange instruction will operate on the writeback buffer, and +/// you will get a *race* while doing writeback into shared memory. +@_transparent +public // @testable +func _stdlib_atomicCompareExchangeStrongPtr( + object target: UnsafeMutablePointer?>, + expected: UnsafeMutablePointer?>, + desired: UnsafeMutablePointer? ) -> Bool { let rawTarget = UnsafeMutableRawPointer(target).assumingMemoryBound( to: Optional.self) @@ -80,14 +123,14 @@ func _stdlib_atomicCompareExchangeStrongPtr( expected: rawExpected, desired: UnsafeRawPointer(desired)) } -% end # optional @_transparent @discardableResult public // @testable func _stdlib_atomicInitializeARCRef( object target: UnsafeMutablePointer, - desired: AnyObject) -> Bool { + desired: AnyObject +) -> Bool { var expected: UnsafeRawPointer? let desiredPtr = Unmanaged.passRetained(desired).toOpaque() let rawTarget = UnsafeMutableRawPointer(target).assumingMemoryBound( @@ -121,9 +164,39 @@ func _stdlib_atomicLoadARCRef( /// A 32 byte buffer. internal struct _Buffer32 { internal init() {} -% for i in range(32): - internal var _x${i}: UInt8 = 0 -% end + + internal var _x0: UInt8 = 0 + internal var _x1: UInt8 = 0 + internal var _x2: UInt8 = 0 + internal var _x3: UInt8 = 0 + internal var _x4: UInt8 = 0 + internal var _x5: UInt8 = 0 + internal var _x6: UInt8 = 0 + internal var _x7: UInt8 = 0 + internal var _x8: UInt8 = 0 + internal var _x9: UInt8 = 0 + internal var _x10: UInt8 = 0 + internal var _x11: UInt8 = 0 + internal var _x12: UInt8 = 0 + internal var _x13: UInt8 = 0 + internal var _x14: UInt8 = 0 + internal var _x15: UInt8 = 0 + internal var _x16: UInt8 = 0 + internal var _x17: UInt8 = 0 + internal var _x18: UInt8 = 0 + internal var _x19: UInt8 = 0 + internal var _x20: UInt8 = 0 + internal var _x21: UInt8 = 0 + internal var _x22: UInt8 = 0 + internal var _x23: UInt8 = 0 + internal var _x24: UInt8 = 0 + internal var _x25: UInt8 = 0 + internal var _x26: UInt8 = 0 + internal var _x27: UInt8 = 0 + internal var _x28: UInt8 = 0 + internal var _x29: UInt8 = 0 + internal var _x30: UInt8 = 0 + internal var _x31: UInt8 = 0 internal mutating func withBytes( _ body: (UnsafeMutablePointer) throws -> Result @@ -137,9 +210,79 @@ internal struct _Buffer32 { /// A 72 byte buffer. internal struct _Buffer72 { internal init() {} -% for i in range(72): - internal var _x${i}: UInt8 = 0 -% end + + internal var _x0: UInt8 = 0 + internal var _x1: UInt8 = 0 + internal var _x2: UInt8 = 0 + internal var _x3: UInt8 = 0 + internal var _x4: UInt8 = 0 + internal var _x5: UInt8 = 0 + internal var _x6: UInt8 = 0 + internal var _x7: UInt8 = 0 + internal var _x8: UInt8 = 0 + internal var _x9: UInt8 = 0 + internal var _x10: UInt8 = 0 + internal var _x11: UInt8 = 0 + internal var _x12: UInt8 = 0 + internal var _x13: UInt8 = 0 + internal var _x14: UInt8 = 0 + internal var _x15: UInt8 = 0 + internal var _x16: UInt8 = 0 + internal var _x17: UInt8 = 0 + internal var _x18: UInt8 = 0 + internal var _x19: UInt8 = 0 + internal var _x20: UInt8 = 0 + internal var _x21: UInt8 = 0 + internal var _x22: UInt8 = 0 + internal var _x23: UInt8 = 0 + internal var _x24: UInt8 = 0 + internal var _x25: UInt8 = 0 + internal var _x26: UInt8 = 0 + internal var _x27: UInt8 = 0 + internal var _x28: UInt8 = 0 + internal var _x29: UInt8 = 0 + internal var _x30: UInt8 = 0 + internal var _x31: UInt8 = 0 + internal var _x32: UInt8 = 0 + internal var _x33: UInt8 = 0 + internal var _x34: UInt8 = 0 + internal var _x35: UInt8 = 0 + internal var _x36: UInt8 = 0 + internal var _x37: UInt8 = 0 + internal var _x38: UInt8 = 0 + internal var _x39: UInt8 = 0 + internal var _x40: UInt8 = 0 + internal var _x41: UInt8 = 0 + internal var _x42: UInt8 = 0 + internal var _x43: UInt8 = 0 + internal var _x44: UInt8 = 0 + internal var _x45: UInt8 = 0 + internal var _x46: UInt8 = 0 + internal var _x47: UInt8 = 0 + internal var _x48: UInt8 = 0 + internal var _x49: UInt8 = 0 + internal var _x50: UInt8 = 0 + internal var _x51: UInt8 = 0 + internal var _x52: UInt8 = 0 + internal var _x53: UInt8 = 0 + internal var _x54: UInt8 = 0 + internal var _x55: UInt8 = 0 + internal var _x56: UInt8 = 0 + internal var _x57: UInt8 = 0 + internal var _x58: UInt8 = 0 + internal var _x59: UInt8 = 0 + internal var _x60: UInt8 = 0 + internal var _x61: UInt8 = 0 + internal var _x62: UInt8 = 0 + internal var _x63: UInt8 = 0 + internal var _x64: UInt8 = 0 + internal var _x65: UInt8 = 0 + internal var _x66: UInt8 = 0 + internal var _x67: UInt8 = 0 + internal var _x68: UInt8 = 0 + internal var _x69: UInt8 = 0 + internal var _x70: UInt8 = 0 + internal var _x71: UInt8 = 0 internal mutating func withBytes( _ body: (UnsafeMutablePointer) throws -> Result @@ -150,90 +293,153 @@ internal struct _Buffer72 { } } -% for bits in [ 32, 64, 80 ]: +// Returns a UInt64, but that value is the length of the string, so it's +// guaranteed to fit into an Int. This is part of the ABI, so we can't +// trivially change it to Int. Callers can safely convert the result +// to any integer type without checks, however. +@_silgen_name("swift_float32ToString") +internal func _float32ToStringImpl( + _ buffer: UnsafeMutablePointer, + _ bufferLength: UInt, + _ value: Float32, + _ debug: Bool +) -> UInt64 -% if bits == 80: -#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) -% end +internal func _float32ToString( + _ value: Float32, + debug: Bool +) -> (buffer: _Buffer32, length: Int) { + _internalInvariant(MemoryLayout<_Buffer32>.size == 32) + var buffer = _Buffer32() + let length = buffer.withBytes { (bufferPtr) in Int( + truncatingIfNeeded: _float32ToStringImpl(bufferPtr, 32, value, debug) + )} + return (buffer, length) +} -@_silgen_name("swift_float${bits}ToString") -internal func _float${bits}ToStringImpl( +// Returns a UInt64, but that value is the length of the string, so it's +// guaranteed to fit into an Int. This is part of the ABI, so we can't +// trivially change it to Int. Callers can safely convert the result +// to any integer type without checks, however. +@_silgen_name("swift_float64ToString") +internal func _float64ToStringImpl( _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, _ value: Float${bits}, + _ bufferLength: UInt, + _ value: Float64, _ debug: Bool -) -> UInt +) -> UInt64 -internal func _float${bits}ToString( - _ value: Float${bits}, debug: Bool +internal func _float64ToString( + _ value: Float64, + debug: Bool ) -> (buffer: _Buffer32, length: Int) { _internalInvariant(MemoryLayout<_Buffer32>.size == 32) var buffer = _Buffer32() - let length = buffer.withBytes { (bufferPtr) in - Int(_float${bits}ToStringImpl(bufferPtr, 32, value, debug)) - } + let length = buffer.withBytes { (bufferPtr) in Int( + truncatingIfNeeded: _float64ToStringImpl(bufferPtr, 32, value, debug) + )} return (buffer, length) } -% if bits == 80: -#endif -% end -% end +#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64)) + +// Returns a UInt64, but that value is the length of the string, so it's +// guaranteed to fit into an Int. This is part of the ABI, so we can't +// trivially change it to Int. Callers can safely convert the result +// to any integer type without checks, however. +@_silgen_name("swift_float80ToString") +internal func _float80ToStringImpl( + _ buffer: UnsafeMutablePointer, + _ bufferLength: UInt, + _ value: Float80, + _ debug: Bool +) -> UInt64 +internal func _float80ToString( + _ value: Float80, + debug: Bool +) -> (buffer: _Buffer32, length: Int) { + _internalInvariant(MemoryLayout<_Buffer32>.size == 32) + var buffer = _Buffer32() + let length = buffer.withBytes { (bufferPtr) in Int( + truncatingIfNeeded: _float80ToStringImpl(bufferPtr, 32, value, debug) + )} + return (buffer, length) +} +#endif + +// Returns a UInt64, but that value is the length of the string, so it's +// guaranteed to fit into an Int. This is part of the ABI, so we can't +// trivially change it to Int. Callers can safely convert the result +// to any integer type without checks, however. @_silgen_name("swift_int64ToString") internal func _int64ToStringImpl( _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, _ value: Int64, - _ radix: Int64, _ uppercase: Bool -) -> UInt + _ bufferLength: UInt, + _ value: Int64, + _ radix: Int64, + _ uppercase: Bool +) -> UInt64 internal func _int64ToString( - _ value: Int64, radix: Int64 = 10, uppercase: Bool = false + _ value: Int64, + radix: Int64 = 10, + uppercase: Bool = false ) -> String { if radix >= 10 { var buffer = _Buffer32() return buffer.withBytes { (bufferPtr) in - let actualLength - = _int64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return String._fromASCII( - UnsafeBufferPointer(start: bufferPtr, count: Int(actualLength))) + let actualLength = _int64ToStringImpl(bufferPtr, 32, value, radix, uppercase) + return String._fromASCII(UnsafeBufferPointer( + start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) + )) } } else { var buffer = _Buffer72() return buffer.withBytes { (bufferPtr) in - let actualLength - = _int64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return String._fromASCII( - UnsafeBufferPointer(start: bufferPtr, count: Int(actualLength))) + let actualLength = _int64ToStringImpl(bufferPtr, 72, value, radix, uppercase) + return String._fromASCII(UnsafeBufferPointer( + start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) + )) } } } +// Returns a UInt64, but that value is the length of the string, so it's +// guaranteed to fit into an Int. This is part of the ABI, so we can't +// trivially change it to Int. Callers can safely convert the result +// to any integer type without checks, however. @_silgen_name("swift_uint64ToString") internal func _uint64ToStringImpl( _ buffer: UnsafeMutablePointer, - _ bufferLength: UInt, _ value: UInt64, _ radix: Int64, _ uppercase: Bool -) -> UInt + _ bufferLength: UInt, + _ value: UInt64, + _ radix: Int64, + _ uppercase: Bool +) -> UInt64 public // @testable func _uint64ToString( - _ value: UInt64, radix: Int64 = 10, uppercase: Bool = false + _ value: UInt64, + radix: Int64 = 10, + uppercase: Bool = false ) -> String { if radix >= 10 { var buffer = _Buffer32() return buffer.withBytes { (bufferPtr) in - let actualLength - = _uint64ToStringImpl(bufferPtr, 32, value, radix, uppercase) - return String._fromASCII( - UnsafeBufferPointer(start: bufferPtr, count: Int(actualLength))) + let actualLength = _uint64ToStringImpl(bufferPtr, 32, value, radix, uppercase) + return String._fromASCII(UnsafeBufferPointer( + start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) + )) } } else { var buffer = _Buffer72() return buffer.withBytes { (bufferPtr) in - let actualLength - = _uint64ToStringImpl(bufferPtr, 72, value, radix, uppercase) - return String._fromASCII( - UnsafeBufferPointer(start: bufferPtr, count: Int(actualLength))) + let actualLength = _uint64ToStringImpl(bufferPtr, 72, value, radix, uppercase) + return String._fromASCII(UnsafeBufferPointer( + start: bufferPtr, count: Int(truncatingIfNeeded: actualLength) + )) } } } @@ -382,7 +588,3 @@ internal class __SwiftNativeNSSet { } #endif - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/RuntimeFunctionCounters.swift b/stdlib/public/core/RuntimeFunctionCounters.swift index 8e361c8643561..89cceeb2b2850 100644 --- a/stdlib/public/core/RuntimeFunctionCounters.swift +++ b/stdlib/public/core/RuntimeFunctionCounters.swift @@ -108,7 +108,7 @@ struct _RuntimeFunctionCounters { public static let runtimeFunctionCountersOffsets = _RuntimeFunctionCounters.getRuntimeFunctionCountersOffsets() public static let numRuntimeFunctionCounters = - _RuntimeFunctionCounters.getNumRuntimeFunctionCounters() + Int(_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()) public static let runtimeFunctionNameToIndex: [String: Int] = getRuntimeFunctionNameToIndex() @@ -121,7 +121,7 @@ struct _RuntimeFunctionCounters { public static func getRuntimeFunctionNames() -> [String] { let names = _RuntimeFunctionCounters._getRuntimeFunctionNames() let numRuntimeFunctionCounters = - _RuntimeFunctionCounters.getNumRuntimeFunctionCounters() + Int(_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()) var functionNames: [String] = [] functionNames.reserveCapacity(numRuntimeFunctionCounters) for index in 0.. Int + public static func getNumRuntimeFunctionCounters() -> UInt64 /// Dump all per-object runtime function counters. @_silgen_name("_swift_dumpObjectsRuntimeFunctionPointers") @@ -166,7 +166,7 @@ struct _RuntimeFunctionCounters { internal static func getRuntimeFunctionNameToIndex() -> [String: Int] { let runtimeFunctionNames = _RuntimeFunctionCounters.getRuntimeFunctionNames() let numRuntimeFunctionCounters = - _RuntimeFunctionCounters.getNumRuntimeFunctionCounters() + Int(_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()) var runtimeFunctionNameToIndex: [String: Int] = [:] runtimeFunctionNameToIndex.reserveCapacity(numRuntimeFunctionCounters) diff --git a/stdlib/public/core/SetBridging.swift b/stdlib/public/core/SetBridging.swift index 75332860ec8bd..85ce73edb5213 100644 --- a/stdlib/public/core/SetBridging.swift +++ b/stdlib/public/core/SetBridging.swift @@ -20,7 +20,7 @@ internal func _stdlib_NSSet_allObjects(_ object: AnyObject) -> _BridgingBuffer { let nss = unsafeBitCast(object, to: _NSSet.self) let count = nss.count let storage = _BridgingBuffer(count) - nss.getObjects(storage.baseAddress, count: count) + nss.getObjects(storage.baseAddress) return storage } diff --git a/stdlib/public/core/ShadowProtocols.swift b/stdlib/public/core/ShadowProtocols.swift index bdffbd02ff4e6..0e9bb7128604c 100644 --- a/stdlib/public/core/ShadowProtocols.swift +++ b/stdlib/public/core/ShadowProtocols.swift @@ -175,6 +175,10 @@ internal protocol _NSSet: _NSSetCore { _ buffer: UnsafeMutablePointer, count: Int ) + + @objc(getObjects:) func getObjects( + _ buffer: UnsafeMutablePointer + ) } /// A shadow for the API of NSNumber we will use in the core diff --git a/stdlib/public/core/Slice.swift b/stdlib/public/core/Slice.swift index 3ad2c2be2ebf3..751e88cd10bbe 100644 --- a/stdlib/public/core/Slice.swift +++ b/stdlib/public/core/Slice.swift @@ -215,6 +215,18 @@ extension Slice: Collection { public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { _base._failEarlyRangeCheck(range, bounds: bounds) } + + @_alwaysEmitIntoClient @inlinable + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + try _base.withContiguousStorageIfAvailable { buffer in + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + let slice = UnsafeBufferPointer(rebasing: buffer[start ..< start + count]) + return try body(slice) + } + } } extension Slice: BidirectionalCollection where Base: BidirectionalCollection { @@ -258,6 +270,34 @@ extension Slice: MutableCollection where Base: MutableCollection { _writeBackMutableSlice(&self, bounds: bounds, slice: newValue) } } + + @_alwaysEmitIntoClient @inlinable + public mutating func withContiguousMutableStorageIfAvailable( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + // We're calling `withContiguousMutableStorageIfAvailable` twice here so + // that we don't calculate index distances unless we know we'll use them. + // The expectation here is that the base collection will make itself + // contiguous on the first try and the second call will be relatively cheap. + guard _base.withContiguousMutableStorageIfAvailable({ _ in }) != nil + else { + return nil + } + let start = _base.distance(from: _base.startIndex, to: _startIndex) + let count = _base.distance(from: _startIndex, to: _endIndex) + return try _base.withContiguousMutableStorageIfAvailable { buffer in + var slice = UnsafeMutableBufferPointer( + rebasing: buffer[start ..< start + count]) + let copy = slice + defer { + _precondition( + slice.baseAddress == copy.baseAddress && + slice.count == copy.count, + "Slice.withUnsafeMutableBufferPointer: replacing the buffer is not allowed") + } + return try body(&slice) + } + } } diff --git a/stdlib/public/core/Sort.swift b/stdlib/public/core/Sort.swift index 444bd333435d2..62701d0d807aa 100644 --- a/stdlib/public/core/Sort.swift +++ b/stdlib/public/core/Sort.swift @@ -692,7 +692,7 @@ extension UnsafeMutableBufferPointer { result = try result && _finalizeRuns( &runs, buffer: buffer.baseAddress!, by: areInIncreasingOrder) - assert(runs.count == 1, "Didn't complete final merge") + _internalInvariant(runs.count == 1, "Didn't complete final merge") } // FIXME: Remove this, it works around rdar://problem/45044610 diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift index 4970cf7a10d75..1896d974204b6 100644 --- a/stdlib/public/core/StringBridge.swift +++ b/stdlib/public/core/StringBridge.swift @@ -23,27 +23,27 @@ internal typealias _CocoaString = AnyObject // Foundation. @objc private protocol _StringSelectorHolder : _NSCopying { - + @objc var length: Int { get } - + @objc var hash: UInt { get } - + @objc(characterAtIndex:) func character(at offset: Int) -> UInt16 - + @objc(getCharacters:range:) func getCharacters( _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange ) - + @objc(_fastCStringContents:) func _fastCStringContents( _ requiresNulTermination: Int8 ) -> UnsafePointer? - + @objc(_fastCharacterContents) func _fastCharacterContents() -> UnsafePointer? - + @objc(getBytes:maxLength:usedLength:encoding:options:range:remainingRange:) func getBytes(_ buffer: UnsafeMutableRawPointer?, maxLength maxBufferCount: Int, @@ -52,7 +52,7 @@ internal typealias _CocoaString = AnyObject options: UInt, range: _SwiftNSRange, remaining leftover: UnsafeMutablePointer<_SwiftNSRange>?) -> Int8 - + @objc(compare:options:range:locale:) func compare(_ string: _CocoaString, options: UInt, @@ -60,7 +60,7 @@ internal typealias _CocoaString = AnyObject locale: AnyObject?) -> Int @objc(newTaggedNSStringWithASCIIBytes_:length_:) - func createTaggedString(bytes: UnsafePointer, + func createTaggedString(bytes: UnsafePointer, count: Int) -> AnyObject? } @@ -68,9 +68,9 @@ internal typealias _CocoaString = AnyObject Passing a _CocoaString through _objc() lets you call ObjC methods that the compiler doesn't know about, via the protocol above. In order to get good performance, you need a double indirection like this: - + func a -> _objc -> func a' - + because any refcounting @_effects on 'a' will be lost when _objc breaks ARC's knowledge that the _CocoaString and _StringSelectorHolder are the same object. */ @@ -383,7 +383,7 @@ internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts { // happen; might as well eagerly bridge it in. // 3) If it's mutable with associated information, must make the call let immutableCopy - = _stdlib_binary_CFStringCreateCopy(cocoaString) as AnyObject + = _stdlib_binary_CFStringCreateCopy(cocoaString) #if !(arch(i386) || arch(arm)) if _isObjCTaggedPointer(immutableCopy) { @@ -442,9 +442,9 @@ extension String { @_effects(releasenone) public // SPI(Foundation) func _bridgeToObjectiveCImpl() -> AnyObject { - + _connectOrphanedFoundationSubclassesIfNeeded() - + // Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM if _guts.isSmallASCII { let maybeTagged = _guts.asSmall.withUTF8 { bufPtr in @@ -456,7 +456,7 @@ extension String { } if let tagged = maybeTagged { return tagged } } - + if _guts.isSmall { // We can't form a tagged pointer String, so grow to a non-small String, // and bridge that instead. Also avoids CF deleting any BOM that may be diff --git a/stdlib/public/core/StringCreate.swift b/stdlib/public/core/StringCreate.swift index 32169601df299..cce18f9b5378e 100644 --- a/stdlib/public/core/StringCreate.swift +++ b/stdlib/public/core/StringCreate.swift @@ -50,12 +50,10 @@ internal func _allASCII(_ input: UnsafeBufferPointer) -> Bool { } extension String { - @usableFromInline - internal static func _fromASCII( + + internal static func _uncheckedFromASCII( _ input: UnsafeBufferPointer ) -> String { - _internalInvariant(_allASCII(input), "not actually ASCII") - if let smol = _SmallString(input) { return String(_StringGuts(smol)) } @@ -63,6 +61,23 @@ extension String { let storage = __StringStorage.create(initializingFrom: input, isASCII: true) return storage.asString } + + @usableFromInline + internal static func _fromASCII( + _ input: UnsafeBufferPointer + ) -> String { + _internalInvariant(_allASCII(input), "not actually ASCII") + return _uncheckedFromASCII(input) + } + + internal static func _fromASCIIValidating( + _ input: UnsafeBufferPointer + ) -> String? { + if _fastPath(_allASCII(input)) { + return _uncheckedFromASCII(input) + } + return nil + } public // SPI(Foundation) static func _tryFromUTF8(_ input: UnsafeBufferPointer) -> String? { @@ -166,9 +181,9 @@ extension String { return contents.withUnsafeBufferPointer { String._uncheckedFromUTF8($0) } } - - @usableFromInline @inline(never) // slow-path - internal static func _fromCodeUnits< + + @inline(never) // slow path + private static func _slowFromCodeUnits< Input: Collection, Encoding: Unicode.Encoding >( @@ -194,6 +209,46 @@ extension String { let str = contents.withUnsafeBufferPointer { String._uncheckedFromUTF8($0) } return (str, repaired) } + + @usableFromInline @inline(never) // can't be inlined w/out breaking ABI + @_specialize( + where Input == UnsafeBufferPointer, Encoding == Unicode.ASCII) + @_specialize( + where Input == Array, Encoding == Unicode.ASCII) + internal static func _fromCodeUnits< + Input: Collection, + Encoding: Unicode.Encoding + >( + _ input: Input, + encoding: Encoding.Type, + repair: Bool + ) -> (String, repairsMade: Bool)? + where Input.Element == Encoding.CodeUnit { + guard _fastPath(encoding == Unicode.ASCII.self) else { + return _slowFromCodeUnits(input, encoding: encoding, repair: repair) + } + + var result:String? = nil + + if let contigBytes = input as? _HasContiguousBytes, + contigBytes._providesContiguousBytesNoCopy { + result = contigBytes.withUnsafeBytes { rawBufPtr in + let buffer = UnsafeBufferPointer( + start: rawBufPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + count: rawBufPtr.count) + return String._fromASCIIValidating(buffer) + } + } else { + result = Array(input).withUnsafeBufferPointer { + let buffer = UnsafeRawBufferPointer($0).bindMemory(to: UInt8.self) + return String._fromASCIIValidating(buffer) + } + } + + return result != nil ? + (result!, repairsMade: false) : + _slowFromCodeUnits(input, encoding: encoding, repair: repair) + } public // @testable static func _fromInvalidUTF16( diff --git a/stdlib/public/core/StringNormalization.swift b/stdlib/public/core/StringNormalization.swift index c50285baf1df8..c9037015aa0af 100644 --- a/stdlib/public/core/StringNormalization.swift +++ b/stdlib/public/core/StringNormalization.swift @@ -108,7 +108,7 @@ extension UnsafeBufferPointer where Element == UInt8 { if index == 0 || index == count { return true } - assert(!UTF8.isContinuation(self[_unchecked: index])) + _internalInvariant(!UTF8.isContinuation(self[_unchecked: index])) // Sub-300 latiny fast-path if self[_unchecked: index] < 0xCC { return true } diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index bb85cee4d20ff..8e8a3d29d2133 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -10,8 +10,6 @@ // //===----------------------------------------------------------------------===// -import SwiftShims - // Having @objc stuff in an extension creates an ObjC category, which we don't // want. #if _runtime(_ObjC) @@ -24,14 +22,6 @@ internal protocol _AbstractStringStorage: _NSCopying { var UTF16Length: Int { get } } -internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */ -internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */ - -@_effects(readonly) -private func _isNSString(_ str:AnyObject) -> UInt8 { - return _swift_stdlib_isNSString(str) -} - #else internal protocol _AbstractStringStorage { @@ -43,134 +33,6 @@ internal protocol _AbstractStringStorage { #endif -extension _AbstractStringStorage { - -// ObjC interfaces. -#if _runtime(_ObjC) - - @inline(__always) - @_effects(releasenone) - internal func _getCharacters( - _ buffer: UnsafeMutablePointer, _ aRange: _SwiftNSRange - ) { - _precondition(aRange.location >= 0 && aRange.length >= 0, - "Range out of bounds") - _precondition(aRange.location + aRange.length <= Int(count), - "Range out of bounds") - - let range = Range( - uncheckedBounds: (aRange.location, aRange.location+aRange.length)) - let str = asString - str._copyUTF16CodeUnits( - into: UnsafeMutableBufferPointer(start: buffer, count: range.count), - range: range) - } - - @inline(__always) - @_effects(releasenone) - internal func _getCString( - _ outputPtr: UnsafeMutablePointer, _ maxLength: Int, _ encoding: UInt - ) -> Int8 { - switch (encoding, isASCII) { - case (_cocoaASCIIEncoding, true), - (_cocoaUTF8Encoding, _): - guard maxLength >= count + 1 else { return 0 } - outputPtr.initialize(from: start, count: count) - outputPtr[count] = 0 - return 1 - default: - return _cocoaGetCStringTrampoline(self, outputPtr, maxLength, encoding) - } - } - - @inline(__always) - @_effects(readonly) - internal func _cString(encoding: UInt) -> UnsafePointer? { - switch (encoding, isASCII) { - case (_cocoaASCIIEncoding, true), - (_cocoaUTF8Encoding, _): - return start - default: - return _cocoaCStringUsingEncodingTrampoline(self, encoding) - } - } - - @_effects(readonly) - internal func _nativeIsEqual( - _ nativeOther: T - ) -> Int8 { - if count != nativeOther.count { - return 0 - } - return (start == nativeOther.start || - (memcmp(start, nativeOther.start, count) == 0)) ? 1 : 0 - } - - @inline(__always) - @_effects(readonly) - internal func _isEqual(_ other: AnyObject?) -> Int8 { - guard let other = other else { - return 0 - } - - if self === other { - return 1 - } - - // Handle the case where both strings were bridged from Swift. - // We can't use String.== because it doesn't match NSString semantics. - let knownOther = _KnownCocoaString(other) - switch knownOther { - case .storage: - return _nativeIsEqual( - _unsafeUncheckedDowncast(other, to: __StringStorage.self)) - case .shared: - return _nativeIsEqual( - _unsafeUncheckedDowncast(other, to: __SharedStringStorage.self)) -#if !(arch(i386) || arch(arm)) - case .tagged: - fallthrough -#endif - case .cocoa: - // We're allowed to crash, but for compatibility reasons NSCFString allows - // non-strings here. - if _isNSString(other) != 1 { - return 0 - } - // At this point we've proven that it is an NSString of some sort, but not - // one of ours. - - defer { _fixLifetime(other) } - - let otherUTF16Length = _stdlib_binary_CFStringGetLength(other) - - // CFString will only give us ASCII bytes here, but that's fine. - // We already handled non-ASCII UTF8 strings earlier since they're Swift. - if let otherStart = _cocoaASCIIPointer(other) { - //We know that otherUTF16Length is also its byte count at this point - if count != otherUTF16Length { - return 0 - } - return (start == otherStart || - (memcmp(start, otherStart, count) == 0)) ? 1 : 0 - } - - if UTF16Length != otherUTF16Length { - return 0 - } - - /* - The abstract implementation of -isEqualToString: falls back to -compare: - immediately, so when we run out of fast options to try, do the same. - We can likely be more clever here if need be - */ - return _cocoaStringCompare(self, other) == 0 ? 1 : 0 - } - } - -#endif //_runtime(_ObjC) -} - private typealias CountAndFlags = _StringObject.CountAndFlags // @@ -221,102 +83,10 @@ final internal class __StringStorage final internal var isASCII: Bool { return _countAndFlags.isASCII } final internal var asString: String { - @_effects(readonly) @inline(__always) get { - return String(_StringGuts(self)) - } - } - -#if _runtime(_ObjC) - - @objc(length) - final internal var UTF16Length: Int { - @_effects(readonly) @inline(__always) get { - return asString.utf16.count // UTF16View special-cases ASCII for us. - } - } - - @objc - final internal var hash: UInt { - @_effects(readonly) get { - if isASCII { - return _cocoaHashASCIIBytes(start, length: count) - } - return _cocoaHashString(self) - } - } - - @objc(characterAtIndex:) - @_effects(readonly) - final internal func character(at offset: Int) -> UInt16 { - let str = asString - return str.utf16[str._toUTF16Index(offset)] - } - - @objc(getCharacters:range:) - @_effects(releasenone) - final internal func getCharacters( - _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange - ) { - _getCharacters(buffer, aRange) - } - - @objc(_fastCStringContents:) - @_effects(readonly) - final internal func _fastCStringContents( - _ requiresNulTermination: Int8 - ) -> UnsafePointer? { - if isASCII { - return start._asCChar - } - return nil - } - - @objc(UTF8String) - @_effects(readonly) - final internal func _utf8String() -> UnsafePointer? { - return start - } - - @objc(cStringUsingEncoding:) - @_effects(readonly) - final internal func cString(encoding: UInt) -> UnsafePointer? { - return _cString(encoding: encoding) - } - - @objc(getCString:maxLength:encoding:) - @_effects(releasenone) - final internal func getCString( - _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt - ) -> Int8 { - return _getCString(outputPtr, maxLength, encoding) - } - - @objc - final internal var fastestEncoding: UInt { - @_effects(readonly) get { - if isASCII { - return _cocoaASCIIEncoding - } - return _cocoaUTF8Encoding - } - } - - @objc(isEqualToString:) - @_effects(readonly) - final internal func isEqual(to other: AnyObject?) -> Int8 { - return _isEqual(other) - } - - @objc(copyWithZone:) - final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { - // While __StringStorage instances aren't immutable in general, - // mutations may only occur when instances are uniquely referenced. - // Therefore, it is safe to return self here; any outstanding Objective-C - // reference will make the instance non-unique. - return self + @_effects(readonly) @inline(__always) + get { return String(_StringGuts(self)) } } -#endif // _runtime(_ObjC) private init(_doNotCallMe: ()) { _internalInvariantFailure("Use the create method") @@ -398,7 +168,7 @@ extension __StringStorage { return __StringStorage.create( realCodeUnitCapacity: realCapacity, countAndFlags: countAndFlags) } - + // The caller is expected to check UTF8 validity and ASCII-ness and update // the resulting StringStorage accordingly internal static func create( @@ -414,7 +184,7 @@ extension __StringStorage { let buffer = UnsafeMutableBufferPointer(start: storage.mutableStart, count: capacity) let count = try initializer(buffer) - + let countAndFlags = CountAndFlags(mortalCount: count, isASCII: false) #if arch(i386) || arch(arm) storage._count = countAndFlags.count @@ -422,7 +192,7 @@ extension __StringStorage { #else storage._countAndFlags = countAndFlags #endif - + storage.terminator.pointee = 0 // nul-terminated return storage } @@ -733,99 +503,6 @@ final internal class __SharedStringStorage return String(_StringGuts(self)) } } - -#if _runtime(_ObjC) - - @objc(length) - final internal var UTF16Length: Int { - @_effects(readonly) get { - return asString.utf16.count // UTF16View special-cases ASCII for us. - } - } - - @objc - final internal var hash: UInt { - @_effects(readonly) get { - if isASCII { - return _cocoaHashASCIIBytes(start, length: count) - } - return _cocoaHashString(self) - } - } - - @objc(characterAtIndex:) - @_effects(readonly) - final internal func character(at offset: Int) -> UInt16 { - let str = asString - return str.utf16[str._toUTF16Index(offset)] - } - - @objc(getCharacters:range:) - @_effects(releasenone) - final internal func getCharacters( - _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange - ) { - _getCharacters(buffer, aRange) - } - - @objc - final internal var fastestEncoding: UInt { - @_effects(readonly) get { - if isASCII { - return _cocoaASCIIEncoding - } - return _cocoaUTF8Encoding - } - } - - @objc(_fastCStringContents:) - @_effects(readonly) - final internal func _fastCStringContents( - _ requiresNulTermination: Int8 - ) -> UnsafePointer? { - if isASCII { - return start._asCChar - } - return nil - } - - @objc(UTF8String) - @_effects(readonly) - final internal func _utf8String() -> UnsafePointer? { - return start - } - - @objc(cStringUsingEncoding:) - @_effects(readonly) - final internal func cString(encoding: UInt) -> UnsafePointer? { - return _cString(encoding: encoding) - } - - @objc(getCString:maxLength:encoding:) - @_effects(releasenone) - final internal func getCString( - _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt - ) -> Int8 { - return _getCString(outputPtr, maxLength, encoding) - } - - @objc(isEqualToString:) - @_effects(readonly) - final internal func isEqual(to other:AnyObject?) -> Int8 { - return _isEqual(other) - } - - @objc(copyWithZone:) - final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { - // While __StringStorage instances aren't immutable in general, - // mutations may only occur when instances are uniquely referenced. - // Therefore, it is safe to return self here; any outstanding Objective-C - // reference will make the instance non-unique. - return self - } - -#endif // _runtime(_ObjC) - } extension __SharedStringStorage { diff --git a/stdlib/public/core/StringStorageBridge.swift b/stdlib/public/core/StringStorageBridge.swift new file mode 100644 index 0000000000000..ddf98d4855741 --- /dev/null +++ b/stdlib/public/core/StringStorageBridge.swift @@ -0,0 +1,340 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftShims + +#if _runtime(_ObjC) + +@_effects(readonly) +private func _isNSString(_ str:AnyObject) -> UInt8 { + return _swift_stdlib_isNSString(str) +} + +internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */ +internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */ + +// ObjC interfaces. +extension _AbstractStringStorage { + @inline(__always) + @_effects(releasenone) + internal func _getCharacters( + _ buffer: UnsafeMutablePointer, _ aRange: _SwiftNSRange + ) { + _precondition(aRange.location >= 0 && aRange.length >= 0, + "Range out of bounds") + _precondition(aRange.location + aRange.length <= Int(count), + "Range out of bounds") + + let range = Range( + uncheckedBounds: (aRange.location, aRange.location+aRange.length)) + let str = asString + str._copyUTF16CodeUnits( + into: UnsafeMutableBufferPointer(start: buffer, count: range.count), + range: range) + } + + @inline(__always) + @_effects(releasenone) + internal func _getCString( + _ outputPtr: UnsafeMutablePointer, _ maxLength: Int, _ encoding: UInt + ) -> Int8 { + switch (encoding, isASCII) { + case (_cocoaASCIIEncoding, true), + (_cocoaUTF8Encoding, _): + guard maxLength >= count + 1 else { return 0 } + outputPtr.initialize(from: start, count: count) + outputPtr[count] = 0 + return 1 + default: + return _cocoaGetCStringTrampoline(self, outputPtr, maxLength, encoding) + } + } + + @inline(__always) + @_effects(readonly) + internal func _cString(encoding: UInt) -> UnsafePointer? { + switch (encoding, isASCII) { + case (_cocoaASCIIEncoding, true), + (_cocoaUTF8Encoding, _): + return start + default: + return _cocoaCStringUsingEncodingTrampoline(self, encoding) + } + } + + @_effects(readonly) + internal func _nativeIsEqual( + _ nativeOther: T + ) -> Int8 { + if count != nativeOther.count { + return 0 + } + return (start == nativeOther.start || + (memcmp(start, nativeOther.start, count) == 0)) ? 1 : 0 + } + + @inline(__always) + @_effects(readonly) + internal func _isEqual(_ other: AnyObject?) -> Int8 { + guard let other = other else { + return 0 + } + + if self === other { + return 1 + } + + // Handle the case where both strings were bridged from Swift. + // We can't use String.== because it doesn't match NSString semantics. + let knownOther = _KnownCocoaString(other) + switch knownOther { + case .storage: + return _nativeIsEqual( + _unsafeUncheckedDowncast(other, to: __StringStorage.self)) + case .shared: + return _nativeIsEqual( + _unsafeUncheckedDowncast(other, to: __SharedStringStorage.self)) +#if !(arch(i386) || arch(arm)) + case .tagged: + fallthrough +#endif + case .cocoa: + // We're allowed to crash, but for compatibility reasons NSCFString allows + // non-strings here. + if _isNSString(other) != 1 { + return 0 + } + // At this point we've proven that it is an NSString of some sort, but not + // one of ours. + + defer { _fixLifetime(other) } + + let otherUTF16Length = _stdlib_binary_CFStringGetLength(other) + + // CFString will only give us ASCII bytes here, but that's fine. + // We already handled non-ASCII UTF8 strings earlier since they're Swift. + if let otherStart = _cocoaASCIIPointer(other) { + //We know that otherUTF16Length is also its byte count at this point + if count != otherUTF16Length { + return 0 + } + return (start == otherStart || + (memcmp(start, otherStart, count) == 0)) ? 1 : 0 + } + + if UTF16Length != otherUTF16Length { + return 0 + } + + /* + The abstract implementation of -isEqualToString: falls back to -compare: + immediately, so when we run out of fast options to try, do the same. + We can likely be more clever here if need be + */ + return _cocoaStringCompare(self, other) == 0 ? 1 : 0 + } + } +} + +extension __StringStorage { + @objc(length) + final internal var UTF16Length: Int { + @_effects(readonly) @inline(__always) get { + return asString.utf16.count // UTF16View special-cases ASCII for us. + } + } + + @objc + final internal var hash: UInt { + @_effects(readonly) get { + if isASCII { + return _cocoaHashASCIIBytes(start, length: count) + } + return _cocoaHashString(self) + } + } + + @objc(characterAtIndex:) + @_effects(readonly) + final internal func character(at offset: Int) -> UInt16 { + let str = asString + return str.utf16[str._toUTF16Index(offset)] + } + + @objc(getCharacters:range:) + @_effects(releasenone) + final internal func getCharacters( + _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange + ) { + _getCharacters(buffer, aRange) + } + + @objc(_fastCStringContents:) + @_effects(readonly) + final internal func _fastCStringContents( + _ requiresNulTermination: Int8 + ) -> UnsafePointer? { + if isASCII { + return start._asCChar + } + return nil + } + + @objc(UTF8String) + @_effects(readonly) + final internal func _utf8String() -> UnsafePointer? { + return start + } + + @objc(cStringUsingEncoding:) + @_effects(readonly) + final internal func cString(encoding: UInt) -> UnsafePointer? { + return _cString(encoding: encoding) + } + + @objc(getCString:maxLength:encoding:) + @_effects(releasenone) + final internal func getCString( + _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt + ) -> Int8 { + return _getCString(outputPtr, maxLength, encoding) + } + + @objc + final internal var fastestEncoding: UInt { + @_effects(readonly) get { + if isASCII { + return _cocoaASCIIEncoding + } + return _cocoaUTF8Encoding + } + } + + @objc(isEqualToString:) + @_effects(readonly) + final internal func isEqualToString(to other: AnyObject?) -> Int8 { + return _isEqual(other) + } + + @objc(isEqual:) + @_effects(readonly) + final internal func isEqual(to other: AnyObject?) -> Int8 { + return _isEqual(other) + } + + @objc(copyWithZone:) + final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { + // While __StringStorage instances aren't immutable in general, + // mutations may only occur when instances are uniquely referenced. + // Therefore, it is safe to return self here; any outstanding Objective-C + // reference will make the instance non-unique. + return self + } +} + +extension __SharedStringStorage { + @objc(length) + final internal var UTF16Length: Int { + @_effects(readonly) get { + return asString.utf16.count // UTF16View special-cases ASCII for us. + } + } + + @objc + final internal var hash: UInt { + @_effects(readonly) get { + if isASCII { + return _cocoaHashASCIIBytes(start, length: count) + } + return _cocoaHashString(self) + } + } + + @objc(characterAtIndex:) + @_effects(readonly) + final internal func character(at offset: Int) -> UInt16 { + let str = asString + return str.utf16[str._toUTF16Index(offset)] + } + + @objc(getCharacters:range:) + @_effects(releasenone) + final internal func getCharacters( + _ buffer: UnsafeMutablePointer, range aRange: _SwiftNSRange + ) { + _getCharacters(buffer, aRange) + } + + @objc + final internal var fastestEncoding: UInt { + @_effects(readonly) get { + if isASCII { + return _cocoaASCIIEncoding + } + return _cocoaUTF8Encoding + } + } + + @objc(_fastCStringContents:) + @_effects(readonly) + final internal func _fastCStringContents( + _ requiresNulTermination: Int8 + ) -> UnsafePointer? { + if isASCII { + return start._asCChar + } + return nil + } + + @objc(UTF8String) + @_effects(readonly) + final internal func _utf8String() -> UnsafePointer? { + return start + } + + @objc(cStringUsingEncoding:) + @_effects(readonly) + final internal func cString(encoding: UInt) -> UnsafePointer? { + return _cString(encoding: encoding) + } + + @objc(getCString:maxLength:encoding:) + @_effects(releasenone) + final internal func getCString( + _ outputPtr: UnsafeMutablePointer, maxLength: Int, encoding: UInt + ) -> Int8 { + return _getCString(outputPtr, maxLength, encoding) + } + + @objc(isEqualToString:) + @_effects(readonly) + final internal func isEqualToString(to other: AnyObject?) -> Int8 { + return _isEqual(other) + } + + @objc(isEqual:) + @_effects(readonly) + final internal func isEqual(to other: AnyObject?) -> Int8 { + return _isEqual(other) + } + + @objc(copyWithZone:) + final internal func copy(with zone: _SwiftNSZone?) -> AnyObject { + // While __StringStorage instances aren't immutable in general, + // mutations may only occur when instances are uniquely referenced. + // Therefore, it is safe to return self here; any outstanding Objective-C + // reference will make the instance non-unique. + return self + } +} + +#endif // _runtime(_ObjC) \ No newline at end of file diff --git a/stdlib/public/core/StringSwitch.swift b/stdlib/public/core/StringSwitch.swift index 4ab39526dcd0c..ed70e9fd7de00 100644 --- a/stdlib/public/core/StringSwitch.swift +++ b/stdlib/public/core/StringSwitch.swift @@ -92,7 +92,8 @@ internal func _createStringTableCache(_ cacheRawPtr: Builtin.RawPointer) { let context = UnsafePointer<_StringSwitchContext>(cacheRawPtr).pointee var cache = _StringSwitchCache() cache.reserveCapacity(context.cases.count) - assert(MemoryLayout<_StringSwitchCache>.size <= MemoryLayout.size) + _internalInvariant( + MemoryLayout<_StringSwitchCache>.size <= MemoryLayout.size) for (idx, s) in context.cases.enumerated() { let key = String(_builtinStringLiteral: s.utf8Start._rawValue, diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index 9786f34dfb148..c187129c51b0c 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -389,6 +389,13 @@ extension Substring.UTF8View: BidirectionalCollection { return _slice.distance(from: start, to: end) } + @_alwaysEmitIntoClient + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + return try _slice.withContiguousStorageIfAvailable(body) + } + @inlinable public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { _slice._failEarlyRangeCheck(index, bounds: bounds) diff --git a/stdlib/public/core/SwiftNativeNSArray.swift b/stdlib/public/core/SwiftNativeNSArray.swift index b54f45386f0b7..bbf163a101741 100644 --- a/stdlib/public/core/SwiftNativeNSArray.swift +++ b/stdlib/public/core/SwiftNativeNSArray.swift @@ -64,29 +64,32 @@ internal class __SwiftNativeNSArrayWithContiguousStorage private let NSNotFound: Int = .max // Implement the APIs required by NSArray -extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { +extension __SwiftNativeNSArrayWithContiguousStorage { @objc internal var count: Int { return withUnsafeBufferOfObjects { $0.count } } @inline(__always) - @nonobjc private func _objectAt(_ index: Int) -> AnyObject { + @_effects(readonly) + @nonobjc private func _objectAt(_ index: Int) -> Unmanaged { return withUnsafeBufferOfObjects { objects in _precondition( _isValidArraySubscript(index, count: objects.count), "Array index out of range") - return objects[index] + return Unmanaged.passUnretained(objects[index]) } } @objc(objectAtIndexedSubscript:) - dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject { + @_effects(readonly) + dynamic internal func objectAtSubscript(_ index: Int) -> Unmanaged { return _objectAt(index) } @objc(objectAtIndex:) - dynamic internal func objectAt(_ index: Int) -> AnyObject { + @_effects(readonly) + dynamic internal func objectAt(_ index: Int) -> Unmanaged { return _objectAt(index) } @@ -146,7 +149,7 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { @_fixed_layout @usableFromInline @objc internal final class _SwiftNSMutableArray : - _SwiftNativeNSMutableArray, _NSArrayCore + _SwiftNativeNSMutableArray { internal var contents: [AnyObject] @@ -160,15 +163,17 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { } @objc(objectAtIndexedSubscript:) - dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject { + @_effects(readonly) + dynamic internal func objectAtSubscript(_ index: Int) -> Unmanaged { //TODO: exception instead of precondition, once that's possible - return contents[index] + return Unmanaged.passUnretained(contents[index]) } @objc(objectAtIndex:) - dynamic internal func objectAt(_ index: Int) -> AnyObject { + @_effects(readonly) + dynamic internal func objectAt(_ index: Int) -> Unmanaged { //TODO: exception instead of precondition, once that's possible - return contents[index] + return Unmanaged.passUnretained(contents[index]) } @objc internal func getObjects( @@ -241,7 +246,9 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { @objc(removeLastObject) dynamic internal func removeLastObject() { - contents.removeLast() + if !contents.isEmpty { + contents.removeLast() + } } @objc(replaceObjectAtIndex:withObject:) diff --git a/stdlib/public/core/UnavailableStringAPIs.swift.gyb b/stdlib/public/core/UnavailableStringAPIs.swift similarity index 57% rename from stdlib/public/core/UnavailableStringAPIs.swift.gyb rename to stdlib/public/core/UnavailableStringAPIs.swift index b129d7d6926e0..2245b604d572a 100644 --- a/stdlib/public/core/UnavailableStringAPIs.swift.gyb +++ b/stdlib/public/core/UnavailableStringAPIs.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,8 +10,7 @@ // //===----------------------------------------------------------------------===// -%{ -stringSubscriptComment = """ +extension String { /// Subscripting strings with integers is not available. /// /// The concept of "the `i`th character in a string" has @@ -48,28 +47,57 @@ stringSubscriptComment = """ /// Unicode algorithms instead, for example, /// `String.localizedStandardCompare()`, /// `String.localizedLowercaseString`, - /// `String.localizedStandardRangeOfString()` etc.""" -}% - -extension String { -${stringSubscriptComment} + /// `String.localizedStandardRangeOfString()` etc. @available( *, unavailable, - message: "cannot subscript String with an Int, use a String.Index instead.") + message: "cannot subscript String with an Int, use a String.Index instead." + ) public subscript(i: Int) -> Character { Builtin.unreachable() } -${stringSubscriptComment} + /// Subscripting strings with integers is not available. + /// + /// The concept of "the `i`th character in a string" has + /// different interpretations in different libraries and system + /// components. The correct interpretation should be selected + /// according to the use case and the APIs involved, so `String` + /// cannot be subscripted with an integer. + /// + /// Swift provides several different ways to access the character + /// data stored inside strings. + /// + /// - `String.utf8` is a collection of UTF-8 code units in the + /// string. Use this API when converting the string to UTF-8. + /// Most POSIX APIs process strings in terms of UTF-8 code units. + /// + /// - `String.utf16` is a collection of UTF-16 code units in + /// string. Most Cocoa and Cocoa touch APIs process strings in + /// terms of UTF-16 code units. For example, instances of + /// `NSRange` used with `NSAttributedString` and + /// `NSRegularExpression` store substring offsets and lengths in + /// terms of UTF-16 code units. + /// + /// - `String.unicodeScalars` is a collection of Unicode scalars. + /// Use this API when you are performing low-level manipulation + /// of character data. + /// + /// - `String.characters` is a collection of extended grapheme + /// clusters, which are an approximation of user-perceived + /// characters. + /// + /// Note that when processing strings that contain human-readable + /// text, character-by-character processing should be avoided to + /// the largest extent possible. Use high-level locale-sensitive + /// Unicode algorithms instead, for example, + /// `String.localizedStandardCompare()`, + /// `String.localizedLowercaseString`, + /// `String.localizedStandardRangeOfString()` etc. @available( *, unavailable, - message: "cannot subscript String with an integer range, use a String.Index range instead.") + message: "cannot subscript String with an integer range, use a String.Index range instead." + ) public subscript(bounds: R) -> String where R.Bound == Int { Builtin.unreachable() } } - - -// ${'Local Variables'}: -// eval: (read-only-mode 1) -// End: diff --git a/stdlib/public/core/UnicodeHelpers.swift b/stdlib/public/core/UnicodeHelpers.swift index b8e0d57cbd40a..64e7ea189fd9f 100644 --- a/stdlib/public/core/UnicodeHelpers.swift +++ b/stdlib/public/core/UnicodeHelpers.swift @@ -186,6 +186,13 @@ extension _StringGuts { return String.Index(_encodedOffset: idx._encodedOffset)._scalarAligned } if _slowPath(self.isForeign) { + // In 5.1 this check was added to foreignScalarAlign, but when this is + // emitted into a client that then runs against a 5.0 stdlib, it calls + // a version of foreignScalarAlign that doesn't check for this, which + // ends up asking CFString for its endIndex'th character, which throws + // an exception. So we duplicate the check here for back deployment. + guard idx._encodedOffset != self.count else { return idx._scalarAligned } + let foreignIdx = foreignScalarAlign(idx) _internalInvariant_5_1(foreignIdx._isScalarAligned) return foreignIdx diff --git a/stdlib/public/core/Unmanaged.swift b/stdlib/public/core/Unmanaged.swift index 4cc875f51c7df..ccaab2a636e65 100644 --- a/stdlib/public/core/Unmanaged.swift +++ b/stdlib/public/core/Unmanaged.swift @@ -31,7 +31,9 @@ public struct Unmanaged { /// - Parameter value: An opaque C pointer. /// - Returns: An unmanaged class reference to `value`. @_transparent - public static func fromOpaque(_ value: UnsafeRawPointer) -> Unmanaged { + public static func fromOpaque( + @_nonEphemeral _ value: UnsafeRawPointer + ) -> Unmanaged { return Unmanaged(_private: unsafeBitCast(value, to: Instance.self)) } diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index c11226d1745c7..8c66a6ef8546e 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -398,7 +398,9 @@ extension Unsafe${Mutable}BufferPointer { /// - count: The number of instances in the buffer. `count` must not be /// negative. @inlinable // unsafe-performance - public init(start: Unsafe${Mutable}Pointer?, count: Int) { + public init( + @_nonEphemeral start: Unsafe${Mutable}Pointer?, count: Int + ) { _precondition( count >= 0, "Unsafe${Mutable}BufferPointer with negative count") _precondition( diff --git a/stdlib/public/core/UnsafePointer.swift b/stdlib/public/core/UnsafePointer.swift index 8cf943324ebab..dac1d17ba76c1 100644 --- a/stdlib/public/core/UnsafePointer.swift +++ b/stdlib/public/core/UnsafePointer.swift @@ -530,7 +530,7 @@ public struct UnsafeMutablePointer: _Pointer { /// /// - Parameter other: The immutable pointer to convert. @_transparent - public init(mutating other: UnsafePointer) { + public init(@_nonEphemeral mutating other: UnsafePointer) { self._rawValue = other._rawValue } @@ -540,7 +540,7 @@ public struct UnsafeMutablePointer: _Pointer { /// - Parameter other: The immutable pointer to convert. If `other` is `nil`, /// the result is `nil`. @_transparent - public init?(mutating other: UnsafePointer?) { + public init?(@_nonEphemeral mutating other: UnsafePointer?) { guard let unwrapped = other else { return nil } self.init(mutating: unwrapped) } @@ -550,7 +550,7 @@ public struct UnsafeMutablePointer: _Pointer { /// /// - Parameter other: The pointer to convert. @_transparent - public init(_ other: UnsafeMutablePointer) { + public init(@_nonEphemeral _ other: UnsafeMutablePointer) { self._rawValue = other._rawValue } @@ -560,7 +560,7 @@ public struct UnsafeMutablePointer: _Pointer { /// - Parameter other: The pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: UnsafeMutablePointer?) { + public init?(@_nonEphemeral _ other: UnsafeMutablePointer?) { guard let unwrapped = other else { return nil } self.init(unwrapped) } @@ -784,7 +784,9 @@ public struct UnsafeMutablePointer: _Pointer { /// - count: The number of instances to move from `source` to this /// pointer's memory. `count` must not be negative. @inlinable - public func moveInitialize(from source: UnsafeMutablePointer, count: Int) { + public func moveInitialize( + @_nonEphemeral from source: UnsafeMutablePointer, count: Int + ) { _debugPrecondition( count >= 0, "UnsafeMutablePointer.moveInitialize with negative count") if self < source || self >= source + count { @@ -855,7 +857,9 @@ public struct UnsafeMutablePointer: _Pointer { /// - count: The number of instances to move from `source` to this /// pointer's memory. `count` must not be negative. @inlinable - public func moveAssign(from source: UnsafeMutablePointer, count: Int) { + public func moveAssign( + @_nonEphemeral from source: UnsafeMutablePointer, count: Int + ) { _debugPrecondition( count >= 0, "UnsafeMutablePointer.moveAssign(from:) with negative count") _debugPrecondition( diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index d4c89bf5f7a47..03a0099534798 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -429,7 +429,9 @@ extension Unsafe${Mutable}RawBufferPointer { /// - count: The number of bytes to include in the buffer. `count` must not /// be negative. @inlinable - public init(start: Unsafe${Mutable}RawPointer?, count: Int) { + public init( + @_nonEphemeral start: Unsafe${Mutable}RawPointer?, count: Int + ) { _precondition(count >= 0, "${Self} with negative count") _precondition(count == 0 || start != nil, "${Self} has a nil start and nonzero count") diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 8ca70fbd96ab0..d6ada21f91298 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -191,7 +191,7 @@ public struct UnsafeRawPointer: _Pointer { /// /// - Parameter other: The typed pointer to convert. @_transparent - public init(_ other: UnsafePointer) { + public init(@_nonEphemeral _ other: UnsafePointer) { _rawValue = other._rawValue } @@ -204,7 +204,7 @@ public struct UnsafeRawPointer: _Pointer { /// - Parameter other: The typed pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: UnsafePointer?) { + public init?(@_nonEphemeral _ other: UnsafePointer?) { guard let unwrapped = other else { return nil } _rawValue = unwrapped._rawValue } @@ -217,7 +217,7 @@ public struct UnsafeRawPointer: _Pointer { /// /// - Parameter other: The mutable raw pointer to convert. @_transparent - public init(_ other: UnsafeMutableRawPointer) { + public init(@_nonEphemeral _ other: UnsafeMutableRawPointer) { _rawValue = other._rawValue } @@ -230,7 +230,7 @@ public struct UnsafeRawPointer: _Pointer { /// - Parameter other: The mutable raw pointer to convert. If `other` is /// `nil`, the result is `nil`. @_transparent - public init?(_ other: UnsafeMutableRawPointer?) { + public init?(@_nonEphemeral _ other: UnsafeMutableRawPointer?) { guard let unwrapped = other else { return nil } _rawValue = unwrapped._rawValue } @@ -243,7 +243,7 @@ public struct UnsafeRawPointer: _Pointer { /// /// - Parameter other: The typed pointer to convert. @_transparent - public init(_ other: UnsafeMutablePointer) { + public init(@_nonEphemeral _ other: UnsafeMutablePointer) { _rawValue = other._rawValue } @@ -256,7 +256,7 @@ public struct UnsafeRawPointer: _Pointer { /// - Parameter other: The typed pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: UnsafeMutablePointer?) { + public init?(@_nonEphemeral _ other: UnsafeMutablePointer?) { guard let unwrapped = other else { return nil } _rawValue = unwrapped._rawValue } @@ -543,7 +543,7 @@ public struct UnsafeMutableRawPointer: _Pointer { /// /// - Parameter other: The typed pointer to convert. @_transparent - public init(_ other: UnsafeMutablePointer) { + public init(@_nonEphemeral _ other: UnsafeMutablePointer) { _rawValue = other._rawValue } @@ -556,7 +556,7 @@ public struct UnsafeMutableRawPointer: _Pointer { /// - Parameter other: The typed pointer to convert. If `other` is `nil`, the /// result is `nil`. @_transparent - public init?(_ other: UnsafeMutablePointer?) { + public init?(@_nonEphemeral _ other: UnsafeMutablePointer?) { guard let unwrapped = other else { return nil } _rawValue = unwrapped._rawValue } @@ -569,7 +569,7 @@ public struct UnsafeMutableRawPointer: _Pointer { /// /// - Parameter other: The immutable raw pointer to convert. @_transparent - public init(mutating other: UnsafeRawPointer) { + public init(@_nonEphemeral mutating other: UnsafeRawPointer) { _rawValue = other._rawValue } @@ -582,7 +582,7 @@ public struct UnsafeMutableRawPointer: _Pointer { /// - Parameter other: The immutable raw pointer to convert. If `other` is /// `nil`, the result is `nil`. @_transparent - public init?(mutating other: UnsafeRawPointer?) { + public init?(@_nonEphemeral mutating other: UnsafeRawPointer?) { guard let unwrapped = other else { return nil } _rawValue = unwrapped._rawValue } @@ -997,23 +997,23 @@ extension UnsafeMutableRawPointer: Strideable { extension OpaquePointer { @_transparent - public init(_ from: UnsafeMutableRawPointer) { + public init(@_nonEphemeral _ from: UnsafeMutableRawPointer) { self._rawValue = from._rawValue } @_transparent - public init?(_ from: UnsafeMutableRawPointer?) { + public init?(@_nonEphemeral _ from: UnsafeMutableRawPointer?) { guard let unwrapped = from else { return nil } self._rawValue = unwrapped._rawValue } @_transparent - public init(_ from: UnsafeRawPointer) { + public init(@_nonEphemeral _ from: UnsafeRawPointer) { self._rawValue = from._rawValue } @_transparent - public init?(_ from: UnsafeRawPointer?) { + public init?(@_nonEphemeral _ from: UnsafeRawPointer?) { guard let unwrapped = from else { return nil } self._rawValue = unwrapped._rawValue } diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index fbe6cc277b9f7..7b0c16cc9245e 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -37,6 +37,7 @@ set(swift_runtime_sources CygwinPort.cpp Demangle.cpp Enum.cpp + ErrorObjectCommon.cpp ErrorObjectConstants.cpp ErrorObjectNative.cpp Errors.cpp @@ -162,11 +163,16 @@ if(SWIFT_BUILD_STATIC_STDLIB AND "${sdk}" STREQUAL "LINUX") INSTALL_IN_COMPONENT never_install) endif() +if(SWIFT_STDLIB_USE_NONATOMIC_RC) + set(_RUNTIME_NONATOMIC_FLAGS -DSWIFT_STDLIB_USE_NONATOMIC_RC) +endif() add_swift_target_library(swiftRuntime OBJECT_LIBRARY ${swift_runtime_sources} ${swift_runtime_objc_sources} ${swift_runtime_leaks_sources} - C_COMPILE_FLAGS ${swift_runtime_library_compile_flags} + C_COMPILE_FLAGS + ${swift_runtime_library_compile_flags} + ${_RUNTIME_NONATOMIC_FLAGS} LINK_FLAGS ${swift_runtime_linker_flags} SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} INSTALL_IN_COMPONENT never_install) @@ -181,6 +187,7 @@ foreach(sdk ${SWIFT_CONFIGURED_SDKS}) endif() endforeach() + add_swift_target_library(swiftImageRegistrationObjectELF OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE SwiftRT-ELF.cpp diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index c8349b170830d..2c516a9952fb4 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -37,6 +37,7 @@ #include "llvm/Support/Compiler.h" #if SWIFT_OBJC_INTEROP #include "swift/Runtime/ObjCBridge.h" +#include "SwiftObject.h" #include "SwiftValue.h" #endif @@ -403,7 +404,7 @@ static bool _conformsToProtocols(const OpaqueValue *value, for (auto protocol : existentialType->getProtocols()) { if (!_conformsToProtocol(value, type, protocol, conformances)) return false; - if (protocol.needsWitnessTable()) { + if (conformances != nullptr && protocol.needsWitnessTable()) { assert(*conformances != nullptr); ++conformances; } @@ -587,15 +588,6 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype, } } -// internal func _getErrorEmbeddedNSErrorIndirect( -// _ x: UnsafePointer) -> AnyObject? -#define getErrorEmbeddedNSErrorIndirect \ - MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) -SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL -id getErrorEmbeddedNSErrorIndirect(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - #endif /******************************************************************************/ @@ -910,7 +902,9 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, srcDynamicType, errorWitness)) { *destBoxAddr = reinterpret_cast(embedded); - maybeDeallocateSource(true); + if (shouldDeallocateSource(true, flags)) { + srcType->vw_destroy(src); + } return true; } #endif @@ -1115,6 +1109,13 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType, } break; + case MetadataKind::Existential: { + auto targetTypeAsExistential = static_cast(targetType); + if (!_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr)) + return nullptr; + return origSourceType; + } + default: // The cast succeeds only if the metadata pointers are statically // equivalent. @@ -1246,7 +1247,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, // Okay, we're doing a conditional cast. void *result = const_cast(swift_dynamicCastUnknownClass(object, targetType)); - assert(result == nullptr || object == result); // If the cast failed, destroy the input and return false. if (!result) { @@ -1268,14 +1268,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest, /******************************** Existentials ********************************/ /******************************************************************************/ -#if SWIFT_OBJC_INTEROP -extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); - -static const WitnessTable *findErrorWitness(const Metadata *srcType) { - return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); -} -#endif - /// Perform a dynamic cast from an existential type to some kind of /// class type. static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, @@ -1288,14 +1280,6 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest, auto classContainer = reinterpret_cast(src); void *obj = classContainer->Value; -#if SWIFT_OBJC_INTEROP - // If we're casting to NSError, we may need a representation change, - // so fall into the general swift_dynamicCast path. - if (targetType == getNSErrorMetadata()) { - return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj), - targetType, flags); - } -#endif return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags); } case ExistentialTypeRepresentation::Opaque: { @@ -1804,32 +1788,6 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } } -/******************************************************************************/ -/****************************** Bridging NSError ******************************/ -/******************************************************************************/ - -#if SWIFT_OBJC_INTEROP -static id dynamicCastValueToNSError(OpaqueValue *src, - const Metadata *srcType, - const WitnessTable *srcErrorWitness, - DynamicCastFlags flags) { - // Check whether there is an embedded NSError. - if (auto embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, - srcErrorWitness)) { - if (flags & DynamicCastFlags::TakeOnSuccess) - srcType->vw_destroy(src); - - return embedded; - } - - BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, - /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); - auto *error = (SwiftError *)errorBox.object; - return _swift_stdlib_bridgeErrorToNSError(error); -} - -#endif - /******************************************************************************/ /********************************* Optionals **********************************/ /******************************************************************************/ @@ -1920,18 +1878,44 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest, assert(!(flags & DynamicCastFlags::Unconditional)); assert(!(flags & DynamicCastFlags::DestroyOnFailure)); + auto originalSrc = src; + auto originalSrcType = srcType; + // Swift type should be AnyObject or a class type. - if (!srcType->isAnyClass()) { - auto existential = dyn_cast(srcType); - if (!existential || !isAnyObjectExistentialType(existential)) + while (true) { + if (srcType->isAnyClass()) { + break; + } + auto existentialType = dyn_cast(srcType); + if (!existentialType) + return false; + switch (existentialType->getRepresentation()) { + case ExistentialTypeRepresentation::Class: { + // If it's a Class object, it must be `AnyObject` + if (isAnyObjectExistentialType(existentialType)) { + goto validated; + } return false; + } + case ExistentialTypeRepresentation::Opaque: { + // If it's an opaque existential, unwrap it and check again + auto opaqueContainer = reinterpret_cast(src); + srcType = opaqueContainer->Type; + src = existentialType->projectValue(src); + break; + } + default: { + return false; + } + } } + validated: #if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class: if (swift_unboxFromSwiftValueWithType(src, dest, targetType)) { // Release the source if we need to. if (flags & DynamicCastFlags::TakeOnSuccess) - srcType->vw_destroy(src); + originalSrcType->vw_destroy(originalSrc); return true; } #endif @@ -1961,7 +1945,7 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest, const_cast(boxedValue)); // Release the box if we need to. if (flags & DynamicCastFlags::TakeOnSuccess) - objc_release((id)srcSwiftValue); + originalSrcType->vw_destroy(originalSrc); return true; } @@ -1973,7 +1957,7 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest, boxedType, targetType, innerFlags)) { // Release the box if we need to. if (flags & DynamicCastFlags::TakeOnSuccess) - objc_release((id)srcSwiftValue); + originalSrcType->vw_destroy(originalSrc); return true; } #endif @@ -2322,25 +2306,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, // Casts to class type. case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: -#if SWIFT_OBJC_INTEROP - // If the destination type is an NSError, and the source type is an - // Error, then the cast can succeed by NSError bridging. - if (targetType == getNSErrorMetadata()) { - // Don't rebridge if the source is already some kind of NSError. - if (srcType->isAnyClass() - && swift_dynamicCastObjCClass(*reinterpret_cast(src), - static_cast(targetType)->Class)) - return _succeed(dest, src, srcType, flags); - if (auto srcErrorWitness = findErrorWitness(srcType)) { - auto error = dynamicCastValueToNSError(src, srcType, - srcErrorWitness, flags); - *reinterpret_cast(dest) = error; - return true; - } - } - LLVM_FALLTHROUGH; -#endif - case MetadataKind::ForeignClass: switch (srcType->getKind()) { case MetadataKind::Class: @@ -2376,6 +2341,21 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, srcBridgeWitness, flags); } + +#if SWIFT_OBJC_INTEROP + // If the destination type is an NSError or NSObject, and the source type + // is an Error, then the cast can succeed by NSError bridging. + if (targetType == getNSErrorMetadata() || + targetType == getNSObjectMetadata()) { + if (auto srcErrorWitness = findErrorWitness(srcType)) { + auto error = dynamicCastValueToNSError(src, srcType, + srcErrorWitness, flags); + *reinterpret_cast(dest) = error; + return true; + } + } +#endif + return _fail(src, srcType, targetType, flags); } @@ -2428,7 +2408,10 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src, #if SWIFT_OBJC_INTEROP // If the source is an NSError, and the target is a bridgeable // Error, try to bridge. - if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) { + auto innerFlags = flags - DynamicCastFlags::Unconditional + - DynamicCastFlags::DestroyOnFailure; + if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, + innerFlags)) { return true; } #endif @@ -2835,9 +2818,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src, // Handle Errors. } else if (auto srcErrorWitness = findErrorWitness(srcType)) { // Bridge the source value to an NSError. - auto box = swift_allocError(srcType, srcErrorWitness, src, consume) - .object; - return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box); + auto flags = consume ? DynamicCastFlags::TakeOnSuccess + : DynamicCastFlags::Default; + return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags); } // Fall back to boxing. diff --git a/stdlib/public/runtime/CompatibilityOverride.cpp b/stdlib/public/runtime/CompatibilityOverride.cpp index 09122d08212b6..3eebb8581ff24 100644 --- a/stdlib/public/runtime/CompatibilityOverride.cpp +++ b/stdlib/public/runtime/CompatibilityOverride.cpp @@ -27,7 +27,7 @@ using namespace swift; /// The definition of the contents of the override section. /// /// The runtime looks in the main executable (not any libraries!) for a -/// __swift51_hooks section and uses the hooks defined therein. This struct +/// __swift52_hooks section and uses the hooks defined therein. This struct /// defines the layout of that section. These hooks allow extending /// runtime functionality when running apps built with a more recent /// compiler. If additional hooks are needed, they may be added at the @@ -51,9 +51,8 @@ static OverrideSection *getOverrideSectionPtr() { static swift_once_t Predicate; swift_once(&Predicate, [](void *) { size_t Size; - OverrideSectionPtr = static_cast(lookupSection("__DATA", - "__swift51_hooks", - &Size)); + OverrideSectionPtr = static_cast( + lookupSection("__DATA", "__swift52_hooks", &Size)); if (Size < sizeof(OverrideSection)) OverrideSectionPtr = nullptr; }, nullptr); diff --git a/stdlib/public/runtime/ErrorObject.h b/stdlib/public/runtime/ErrorObject.h index b9f211a46d2a1..5e0b702cad5d2 100644 --- a/stdlib/public/runtime/ErrorObject.h +++ b/stdlib/public/runtime/ErrorObject.h @@ -253,6 +253,17 @@ Class getNSErrorClass(); /// Get the NSError metadata. const Metadata *getNSErrorMetadata(); +/// Find the witness table for the conformance of the given type to the +/// Error protocol, or return nullptr if it does not conform. +const WitnessTable *findErrorWitness(const Metadata *srcType); + +/// Dynamically cast a value whose conformance to the Error protocol is known +/// into an NSError instance. +id dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags); + #endif SWIFT_RUNTIME_STDLIB_SPI @@ -263,4 +274,15 @@ const size_t _swift_lldb_sizeof_SwiftError; } // namespace swift +#if SWIFT_OBJC_INTEROP +// internal func _getErrorEmbeddedNSErrorIndirect( +// _ x: UnsafePointer) -> AnyObject? +#define getErrorEmbeddedNSErrorIndirect \ + MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +id getErrorEmbeddedNSErrorIndirect(const swift::OpaqueValue *error, + const swift::Metadata *T, + const swift::WitnessTable *Error); +#endif + #endif diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index b970b9fdba194..e40ece2d8d597 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -24,20 +24,20 @@ #include "swift/Runtime/Config.h" #if SWIFT_OBJC_INTEROP +#include "ErrorObject.h" +#include "Private.h" +#include "SwiftObject.h" +#include "swift/Basic/Lazy.h" +#include "swift/Demangling/ManglingMacros.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/ObjCBridge.h" -#include "swift/Basic/Lazy.h" -#include "swift/Demangling/ManglingMacros.h" -#include "ErrorObject.h" -#include "Private.h" +#include #include #include -#include #include #include -#include -#include "../Darwin/Foundation/NSError.h" +#include using namespace swift; using namespace swift::hashable_support; @@ -100,6 +100,12 @@ - (void)dealloc { return cf_const_cast(domain); } +- (id /* NSString */)description { + auto error = (const SwiftError *)self; + return getDescription(const_cast(error->getValue()), + error->type); +} + - (NSInteger)code { auto error = (const SwiftError*)self; return error->code.load(SWIFT_MEMORY_ORDER_CONSUME); @@ -179,6 +185,31 @@ - (BOOL)isEqual:(id)other { swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass())); } +extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); + +const WitnessTable *swift::findErrorWitness(const Metadata *srcType) { + return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error)); +} + +id swift::dynamicCastValueToNSError(OpaqueValue *src, + const Metadata *srcType, + const WitnessTable *srcErrorWitness, + DynamicCastFlags flags) { + // Check whether there is an embedded NSError. + if (id embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, + srcErrorWitness)) { + if (flags & DynamicCastFlags::TakeOnSuccess) + srcType->vw_destroy(src); + + return embedded; + } + + BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, + /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); + auto *error = (SwiftError *)errorBox.object; + return _swift_stdlib_bridgeErrorToNSError(error); +} + static Class getAndBridgeSwiftNativeNSErrorClass() { Class nsErrorClass = swift::getNSErrorClass(); Class ourClass = [__SwiftNativeNSError class]; @@ -260,19 +291,6 @@ static id getEmptyNSDictionary() { object_dispose((id)error); } -/// Get the error bridging info from the Foundation overlay. If it can't -/// be loaded, return all NULLs. -static ErrorBridgingInfo getErrorBridgingInfo() { - auto *info = SWIFT_LAZY_CONSTANT( - reinterpret_cast( - dlsym(RTLD_DEFAULT, ERROR_BRIDGING_SYMBOL_NAME_STRING))); - if (!info) { - ErrorBridgingInfo nulls = {}; - return nulls; - } - return *info; -} - static const WitnessTable *getNSErrorConformanceToError() { // CFError and NSError are toll-free-bridged, so we can use either type's // witness table interchangeably. CFError's is potentially slightly more @@ -281,7 +299,10 @@ static ErrorBridgingInfo getErrorBridgingInfo() { // safe to assume that that's been linked in if a user is using NSError in // their Swift source. - auto conformance = getErrorBridgingInfo().CFErrorErrorConformance; + auto *conformance = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(So10CFErrorRefas5Error10FoundationMc))))); assert(conformance && "Foundation overlay not loaded, or 'CFError : Error' conformance " "not available"); @@ -291,7 +312,10 @@ static ErrorBridgingInfo getErrorBridgingInfo() { } static const HashableWitnessTable *getNSErrorConformanceToHashable() { - auto conformance = getErrorBridgingInfo().NSObjectHashableConformance; + auto *conformance = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(So8NSObjectCSH10ObjectiveCMc))))); assert(conformance && "ObjectiveC overlay not loaded, or 'NSObject : Hashable' conformance " "not available"); @@ -427,7 +451,15 @@ id _swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error, const WitnessTable *Error) { // public func Foundation._getErrorDefaultUserInfo(_ error: T) // -> AnyObject? - auto foundationGetDefaultUserInfo = getErrorBridgingInfo().GetErrorDefaultUserInfo; + typedef SWIFT_CC(swift) NSDictionary *(*GetErrorDefaultUserInfoFunction)( + const OpaqueValue *error, + const Metadata *T, + const WitnessTable *Error); + auto foundationGetDefaultUserInfo = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF))))); + if (!foundationGetDefaultUserInfo) { return nullptr; } @@ -512,7 +544,8 @@ id _swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error, Class NSErrorClass = getNSErrorClass(); // The object must be an NSError subclass. - if (![reinterpret_cast(object) isKindOfClass: NSErrorClass]) + if (isObjCTaggedPointerOrNull(object) || + ![reinterpret_cast(object) isKindOfClass: NSErrorClass]) return false; id srcInstance = reinterpret_cast(object); @@ -532,9 +565,19 @@ ProtocolDescriptorRef theErrorProtocol(&PROTOCOL_DESCR_SYM(s5Error), // public func Foundation._bridgeNSErrorToError< // T : _ObjectiveCBridgeableError // >(error: NSError, out: UnsafeMutablePointer) -> Bool { - auto bridgeNSErrorToError = getErrorBridgingInfo().BridgeErrorToNSError; + typedef SWIFT_CC(swift) bool (*BridgeErrorToNSErrorFunction)( + NSError *, OpaqueValue*, const Metadata *, + const WitnessTable *); + auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF))))); + // protocol _ObjectiveCBridgeableError - auto TheObjectiveCBridgeableError = getErrorBridgingInfo().ObjectiveCBridgeableError; + auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT( + reinterpret_cast( + dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp))))); // If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be // bridged. @@ -598,12 +641,5 @@ ProtocolDescriptorRef theErrorProtocol(&PROTOCOL_DESCR_SYM(s5Error), return objc_release((id)error); } -/// Breakpoint hook for debuggers. -SWIFT_CC(swift) void -swift::swift_willThrow(SWIFT_CONTEXT void *unused, - SWIFT_ERROR_RESULT SwiftError **error) { - // empty -} - #endif diff --git a/stdlib/public/runtime/ErrorObjectCommon.cpp b/stdlib/public/runtime/ErrorObjectCommon.cpp new file mode 100644 index 0000000000000..e000603431aba --- /dev/null +++ b/stdlib/public/runtime/ErrorObjectCommon.cpp @@ -0,0 +1,38 @@ +//===--- ErrorObjectCommon.cpp - Recoverable error object -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This implements the parts of the standard Error protocol type which are +// shared between the ObjC-interoperable implementation and the native +// implementation. The parts specific to each implementation can be found in +// ErrorObject.mm (for the ObjC-interoperable parts) and ErrorObjectNative.cpp. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Concurrent.h" +#include "swift/Runtime/Config.h" +#include "ErrorObject.h" +#include "ErrorObjectTestSupport.h" + +using namespace swift; + +void (*swift::_swift_willThrow)(SwiftError *error); + +/// Breakpoint hook for debuggers, and calls _swift_willThrow if set. +SWIFT_CC(swift) void +swift::swift_willThrow(SWIFT_CONTEXT void *unused, + SWIFT_ERROR_RESULT SwiftError **error) { + // Cheap check to bail out early, since we expect there to be no callbacks + // the vast majority of the time. + if (SWIFT_LIKELY(!_swift_willThrow)) + return; + _swift_willThrow(*error); +} diff --git a/stdlib/public/runtime/ErrorObjectNative.cpp b/stdlib/public/runtime/ErrorObjectNative.cpp index c765c36cfae8e..f0fd77f42b711 100644 --- a/stdlib/public/runtime/ErrorObjectNative.cpp +++ b/stdlib/public/runtime/ErrorObjectNative.cpp @@ -106,11 +106,4 @@ swift::swift_getErrorValue(const SwiftError *errorObject, out->errorConformance = errorObject->errorConformance; } -/// Breakpoint hook for debuggers. -SWIFT_CC(swift) void -swift::swift_willThrow(SWIFT_CONTEXT void *unused, - SWIFT_ERROR_RESULT SwiftError **error) { - // do nothing -} - #endif diff --git a/stdlib/public/runtime/ErrorObjectTestSupport.h b/stdlib/public/runtime/ErrorObjectTestSupport.h new file mode 100644 index 0000000000000..9e4f52287d2db --- /dev/null +++ b/stdlib/public/runtime/ErrorObjectTestSupport.h @@ -0,0 +1,26 @@ +//===--- ErrorObjectTestSupport.h - Support for Instruments.app -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Swift runtime support for tests involving errors. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_ERROROBJECT_TEST_SUPPORT_H +#define SWIFT_RUNTIME_ERROROBJECT_TEST_SUPPORT_H + +namespace swift { + +SWIFT_RUNTIME_EXPORT void (*_swift_willThrow)(SwiftError *error); + +} + +#endif diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index 5352f8738e9f9..6e202434750f0 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -62,6 +62,8 @@ #include #endif +#include + namespace FatalErrorFlags { enum: uint32_t { ReportBacktrace = 1 << 0 diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 712c0afca7fc7..13a97355fb326 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -42,6 +42,8 @@ # include # include # include "swift/Runtime/ObjCBridge.h" +# include "swift/Runtime/Once.h" +# include #endif #include "Leaks.h" @@ -84,7 +86,6 @@ static inline bool isValidPointerForNativeRetain(const void *p) { return _ ## name ## _ args; \ } while(0) - static HeapObject *_swift_allocObject_(HeapMetadata const *metadata, size_t requiredSize, size_t requiredAlignmentMask) { @@ -609,13 +610,23 @@ void swift::swift_rootObjCDealloc(HeapObject *self) { } #endif +#if SWIFT_OBJC_INTEROP +static bool _check_fast_dealloc() { + return dlsym(RTLD_NEXT, "_objc_has_weak_formation_callout") != nullptr; +} +#endif + void swift::swift_deallocClassInstance(HeapObject *object, size_t allocatedSize, size_t allocatedAlignMask) { + #if SWIFT_OBJC_INTEROP // We need to let the ObjC runtime clean up any associated objects or weak // references associated with this object. - objc_destructInstance((id)object); + const bool fastDeallocSupported = SWIFT_LAZY_CONSTANT(_check_fast_dealloc()); + if (!fastDeallocSupported || !object->refCounts.getPureSwiftDeallocation()) { + objc_destructInstance((id)object); + } #endif swift_deallocObject(object, allocatedSize, allocatedAlignMask); } diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index b57e40d386ed2..2546d90dec5cd 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -404,7 +404,10 @@ initializeClassMetadataFromPattern(ClassMetadata *metadata, auto extraDataPattern = pattern->getExtraDataPattern(); // Zero memory up to the offset. - memset(metadataExtraData, 0, size_t(extraDataPattern->OffsetInWords)); + // [pre-5.2-extra-data-zeroing] Before Swift 5.2, the runtime did not + // correctly zero the zero-prefix of the extra-data pattern. + memset(metadataExtraData, 0, + size_t(extraDataPattern->OffsetInWords) * sizeof(void *)); // Copy the pattern into the rest of the extra data. copyMetadataPattern(metadataExtraData, extraDataPattern); @@ -2169,7 +2172,10 @@ namespace { #if __POINTER_WIDTH__ == 64 uint32_t Reserved; #endif - const uint8_t *IvarLayout; + union { + const uint8_t *IvarLayout; + ClassMetadata *NonMetaClass; + }; const char *Name; const void *MethodList; const void *ProtocolList; @@ -2194,7 +2200,7 @@ static inline ClassROData *getROData(ClassMetadata *theClass) { return (ClassROData*)(theClass->Data & ~uintptr_t(SWIFT_CLASS_IS_SWIFT_MASK)); } -static void initGenericClassObjCName(ClassMetadata *theClass) { +static char *copyGenericClassObjCName(ClassMetadata *theClass) { // Use the remangler to generate a mangled name from the type metadata. Demangle::StackAllocatedDemangler<4096> Dem; @@ -2227,11 +2233,54 @@ static void initGenericClassObjCName(ClassMetadata *theClass) { } else { fullNameBuf[string.size()] = '\0'; } + return fullNameBuf; +} +static void initGenericClassObjCName(ClassMetadata *theClass) { auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); - getROData(theClass)->Name = fullNameBuf; - getROData(theMetaclass)->Name = fullNameBuf; + char *name = copyGenericClassObjCName(theClass); + getROData(theClass)->Name = name; + getROData(theMetaclass)->Name = name; +} + +static bool installLazyClassNameHook() { +#if !OBJC_SETHOOK_LAZYCLASSNAMER_DEFINED + using objc_hook_lazyClassNamer = + const char * _Nullable (*)(_Nonnull Class cls); + auto objc_setHook_lazyClassNamer = + (void (*)(objc_hook_lazyClassNamer, objc_hook_lazyClassNamer *)) + dlsym(RTLD_NEXT, "objc_setHook_lazyClassNamer"); +#endif + + static objc_hook_lazyClassNamer oldHook; + auto myHook = [](Class theClass) -> const char * { + ClassMetadata *metadata = (ClassMetadata *)theClass; + if (metadata->isTypeMetadata()) + return copyGenericClassObjCName(metadata); + return oldHook(theClass); + }; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (objc_setHook_lazyClassNamer == nullptr) + return false; + objc_setHook_lazyClassNamer(myHook, &oldHook); +#pragma clang diagnostic pop + + return true; +} + +static void setUpGenericClassObjCName(ClassMetadata *theClass) { + bool supportsLazyNames = SWIFT_LAZY_CONSTANT(installLazyClassNameHook()); + if (supportsLazyNames) { + getROData(theClass)->Name = nullptr; + auto theMetaclass = (ClassMetadata *)object_getClass((id)theClass); + getROData(theMetaclass)->Name = nullptr; + getROData(theMetaclass)->NonMetaClass = theClass; + } else { + initGenericClassObjCName(theClass); + } } #endif @@ -2485,7 +2534,7 @@ initGenericObjCClass(ClassMetadata *self, size_t numFields, const TypeLayout * const *fieldTypes, size_t *fieldOffsets) { // If the class is generic, we need to give it a name for Objective-C. - initGenericClassObjCName(self); + setUpGenericClassObjCName(self); ClassROData *rodata = getROData(self); @@ -3859,6 +3908,13 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff return refAndValueAddr.buffer; } +template <> void Metadata::deallocateBoxForExistentialIn(ValueBuffer *buffer) const { + auto *vwt = getValueWitnesses(); + if (vwt->isValueInline()) + return; + swift_deallocBox(reinterpret_cast(buffer->PrivateData[0])); +} + template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const { auto *vwt = getValueWitnesses(); if (vwt->isValueInline()) @@ -3952,13 +4008,41 @@ void Metadata::dump() const { if (auto *contextDescriptor = getTypeContextDescriptor()) { printf("Name: %s.\n", contextDescriptor->Name.get()); printf("Type Context Description: %p.\n", contextDescriptor); + + if (contextDescriptor->isGeneric()) { + auto genericCount = contextDescriptor->getFullGenericContextHeader().Base.getNumArguments(); + auto *args = getGenericArgs(); + printf("Generic Args: %u: [", genericCount); + for (uint32_t i = 0; i < genericCount; i++) { + if (i > 0) + printf(", "); + printf("%p", args[i]); + } + printf("]\n"); + } } if (auto *tuple = dyn_cast(this)) { printf("Labels: %s.\n", tuple->Labels); } - printf("Generic Args: %p.\n", getGenericArgs()); + if (auto *existential = dyn_cast(this)) { + printf("Is class bounded: %s.\n", + existential->isClassBounded() ? "true" : "false"); + auto protocols = existential->getProtocols(); + bool first = true; + printf("Protocols: "); + for (auto protocol : protocols) { + if (!first) + printf(" & "); + printf("%s", protocol.getName()); + first = false; + } + if (auto *superclass = existential->getSuperclassConstraint()) + if (auto *contextDescriptor = superclass->getTypeContextDescriptor()) + printf("Superclass constraint: %s.\n", contextDescriptor->Name.get()); + printf("\n"); + } #if SWIFT_OBJC_INTEROP if (auto *classObject = getClassObject()) { @@ -4546,6 +4630,14 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness( template static Result performOnMetadataCache(const Metadata *metadata, Callbacks &&callbacks) { + // TODO: Once more than just structs have canonical statically specialized + // metadata, calling an updated + // isCanonicalStaticallySpecializedGenericMetadata would entail + // dyn_casting to the same type more than once. Avoid that by combining + // that function's implementation with the dyn_casts below. + if (metadata->isCanonicalStaticallySpecializedGenericMetadata()) + return std::move(callbacks).forOtherMetadata(metadata); + // Handle different kinds of type that can delay their metadata. const TypeContextDescriptor *description; if (auto classMetadata = dyn_cast(metadata)) { @@ -5127,6 +5219,14 @@ bool Metadata::satisfiesClassConstraint() const { return isAnyClass(); } +template <> +bool Metadata::isCanonicalStaticallySpecializedGenericMetadata() const { + if (auto *metadata = dyn_cast(this)) + return metadata->isCanonicalStaticallySpecializedGenericMetadata(); + + return false; +} + #if !NDEBUG static bool referencesAnonymousContext(Demangle::Node *node) { if (node->getKind() == Demangle::Node::Kind::AnonymousContext) diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index d3ec0a4afc394..3e10ea06c9a04 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -205,21 +205,10 @@ namespace { }; } // end anonymous namespace -inline llvm::hash_code llvm::hash_value(StringRef S) { - return hash_combine_range(S.begin(), S.end()); -} - struct TypeMetadataPrivateState { ConcurrentMap NominalCache; ConcurrentReadableArray SectionsToScan; - llvm::DenseMap> - ContextDescriptorCache; - size_t ConformanceDescriptorLastSectionScanned = 0; - size_t TypeContextDescriptorLastSectionScanned = 0; - Mutex ContextDescriptorCacheLock; - TypeMetadataPrivateState() { initializeTypeMetadataRecordLookup(); } @@ -235,29 +224,6 @@ _registerTypeMetadataRecords(TypeMetadataPrivateState &T, T.SectionsToScan.push_back(TypeMetadataSection{begin, end}); } -/// Iterate over type metadata sections starting from the given index. -/// The index is updated to the current number of sections. Passing -/// the same index to the next call will iterate over any sections that were -/// added after the previous call. -/// -/// Takes a function to call for each section found. The two parameters are -/// the start and end of the section. -static void _forEachTypeMetadataSectionAfter( - TypeMetadataPrivateState &T, - size_t *start, - const std::function &f) { - auto snapshot = T.SectionsToScan.snapshot(); - if (snapshot.Count > *start) { - auto *begin = snapshot.begin() + *start; - auto *end = snapshot.end(); - for (auto *section = begin; section != end; section++) { - f(section->Begin, section->End); - } - *start = snapshot.Count; - } -} - void swift::addImageTypeMetadataRecordBlockCallbackUnsafe( const void *records, uintptr_t recordsSize) { assert(recordsSize % sizeof(TypeMetadataRecord) == 0 @@ -654,70 +620,6 @@ _searchTypeMetadataRecords(TypeMetadataPrivateState &T, return nullptr; } -// Read ContextDescriptors for any loaded images that haven't already been -// scanned, if any. -static void -_scanAdditionalContextDescriptors(TypeMetadataPrivateState &T) { - _forEachTypeMetadataSectionAfter( - T, - &T.TypeContextDescriptorLastSectionScanned, - [&T](const TypeMetadataRecord *Begin, - const TypeMetadataRecord *End) { - for (const auto *record = Begin; record != End; record++) { - if (auto ntd = record->getContextDescriptor()) { - if (auto type = llvm::dyn_cast(ntd)) { - auto identity = ParsedTypeIdentity::parse(type); - auto name = identity.getABIName(); - T.ContextDescriptorCache[name].insert(type); - } - } - } - }); - - _forEachProtocolConformanceSectionAfter( - &T.ConformanceDescriptorLastSectionScanned, - [&T](const ProtocolConformanceRecord *Begin, - const ProtocolConformanceRecord *End) { - for (const auto *record = Begin; record != End; record++) { - if (auto ntd = record[0]->getTypeDescriptor()) { - if (auto type = llvm::dyn_cast(ntd)) { - auto identity = ParsedTypeIdentity::parse(type); - auto name = identity.getABIName(); - T.ContextDescriptorCache[name].insert(type); - } - } - } - }); -} - -// Search for a ContextDescriptor in the context descriptor cache matching the -// given demangle node. Returns the found node, or nullptr if no match was -// found. -static llvm::SmallDenseSet -_findContextDescriptorInCache(TypeMetadataPrivateState &T, - Demangle::NodePointer node) { - if (node->getNumChildren() < 2) - return { }; - - auto nameNode = node->getChild(1); - - // Declarations synthesized by the Clang importer get a small tag - // string in addition to their name. - if (nameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName) - nameNode = nameNode->getChild(1); - - if (nameNode->getKind() != Demangle::Node::Kind::Identifier) - return { }; - - auto name = nameNode->getText(); - - auto iter = T.ContextDescriptorCache.find(name); - if (iter == T.ContextDescriptorCache.end()) - return { }; - - return iter->getSecond(); -} - #define DESCRIPTOR_MANGLING_SUFFIX_Structure Mn #define DESCRIPTOR_MANGLING_SUFFIX_Enum Mn #define DESCRIPTOR_MANGLING_SUFFIX_Protocol Mp @@ -785,51 +687,18 @@ _findContextDescriptor(Demangle::NodePointer node, if (auto Value = T.NominalCache.find(mangledName)) return Value->getDescription(); + // Check type metadata records // Scan any newly loaded images for context descriptors, then try the context - // descriptor cache. This must be done with the cache's lock held. - llvm::SmallDenseSet cachedContexts; - { - ScopedLock guard(T.ContextDescriptorCacheLock); - _scanAdditionalContextDescriptors(T); - cachedContexts = _findContextDescriptorInCache(T, node); - } - - bool foundInCache = false; - for (auto cachedContext : cachedContexts) { - if (_contextDescriptorMatchesMangling(cachedContext, node)) { - foundContext = cachedContext; - foundInCache = true; - break; - } - } - - if (!foundContext) { - // Slow path, as a fallback if the cache itself isn't capturing everything. - (void)foundInCache; - - // Check type metadata records - foundContext = _searchTypeMetadataRecords(T, node); - - // Check protocol conformances table. Note that this has no support for - // resolving generic types yet. - if (!foundContext) - foundContext = _searchConformancesByMangledTypeName(node); - } - - if (foundContext) { + foundContext = _searchTypeMetadataRecords(T, node); + + // Check protocol conformances table. Note that this has no support for + // resolving generic types yet. + if (!foundContext) + foundContext = _searchConformancesByMangledTypeName(node); + + if (foundContext) T.NominalCache.getOrInsert(mangledName, foundContext); -#ifndef NDEBUG - // If we found something in the slow path but not in the cache, it is a - // bug in the cache. Fail in assertions builds. - if (!foundInCache) { - fatalError(0, - "_findContextDescriptor cache miss for demangled tree:\n%s\n", - getNodeTreeAsString(node).c_str()); - } -#endif - } - return foundContext; } @@ -1689,6 +1558,26 @@ swift_getTypeByMangledNameInEnvironment( }).getMetadata(); } +SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT +const Metadata * _Nullable +swift_getTypeByMangledNameInEnvironmentInMetadataState( + size_t metadataState, + const char *typeNameStart, + size_t typeNameLength, + const TargetGenericEnvironment *environment, + const void * const *genericArgs) { + llvm::StringRef typeName(typeNameStart, typeNameLength); + SubstGenericParametersFromMetadata substitutions(environment, genericArgs); + return swift_getTypeByMangledName((MetadataState)metadataState, typeName, + genericArgs, + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }).getMetadata(); +} + SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT const Metadata * _Nullable swift_getTypeByMangledNameInContext( @@ -1708,6 +1597,26 @@ swift_getTypeByMangledNameInContext( }).getMetadata(); } +SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT +const Metadata * _Nullable +swift_getTypeByMangledNameInContextInMetadataState( + size_t metadataState, + const char *typeNameStart, + size_t typeNameLength, + const TargetContextDescriptor *context, + const void * const *genericArgs) { + llvm::StringRef typeName(typeNameStart, typeNameLength); + SubstGenericParametersFromMetadata substitutions(context, genericArgs); + return swift_getTypeByMangledName((MetadataState)metadataState, typeName, + genericArgs, + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }).getMetadata(); +} + /// Demangle a mangled name, but don't allow symbolic references. SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL const Metadata *_Nullable diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 9bd4a808876fa..49162640f3ffd 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -50,8 +50,10 @@ class TypeReferenceOwnership { #define REF_STORAGE(Name, ...) \ void set##Name() { Data |= Name; } \ - bool is##Name() const { return Data & Name; } + bool is##Name() const { return Data == Name; } #include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return Data == 0; } }; /// Type information consists of metadata and its ownership info, @@ -76,9 +78,11 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } - bool isWeak() const { return ReferenceOwnership.isWeak(); } - bool isUnowned() const { return ReferenceOwnership.isUnowned(); } - bool isUnmanaged() const { return ReferenceOwnership.isUnmanaged(); } +#define REF_STORAGE(Name, ...) \ + bool is##Name() const { return ReferenceOwnership.is##Name(); } +#include "swift/AST/ReferenceStorage.def" + + bool isStrong() const { return ReferenceOwnership.isStrong(); } TypeReferenceOwnership getReferenceOwnership() const { return ReferenceOwnership; @@ -249,19 +253,6 @@ class TypeInfo { const ContextDescriptor * _searchConformancesByMangledTypeName(Demangle::NodePointer node); - /// Iterate over protocol conformance sections starting from the given index. - /// The index is updated to the current number of protocol sections. Passing - /// the same index to the next call will iterate over any sections that were - /// added after the previous call. - /// - /// Takes a function to call for each section found. The two parameters are - /// the start and end of the section. - void - _forEachProtocolConformanceSectionAfter( - size_t *start, - const std::function &f); - Demangle::NodePointer _swift_buildDemanglingForMetadata(const Metadata *type, Demangle::Demangler &Dem); diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 5cc50b09051c0..df83c30bce373 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -642,22 +642,6 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) { return nullptr; } -void -swift::_forEachProtocolConformanceSectionAfter( - size_t *start, - const std::function &f) { - auto snapshot = Conformances.get().SectionsToScan.snapshot(); - if (snapshot.Count > *start) { - auto *begin = snapshot.begin() + *start; - auto *end = snapshot.end(); - for (auto *section = begin; section != end; section++) { - f(section->Begin, section->End); - } - *start = snapshot.Count; - } -} - bool swift::_checkGenericRequirements( llvm::ArrayRef requirements, SmallVectorImpl &extraArguments, diff --git a/stdlib/public/runtime/RefCount.cpp b/stdlib/public/runtime/RefCount.cpp index 4b3ca72a4e2fe..4dc7394f92540 100644 --- a/stdlib/public/runtime/RefCount.cpp +++ b/stdlib/public/runtime/RefCount.cpp @@ -17,7 +17,7 @@ namespace swift { template void RefCounts::incrementSlow(RefCountBits oldbits, uint32_t n) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return; } else if (oldbits.hasSideTable()) { @@ -36,7 +36,7 @@ template void RefCounts::incrementSlow(SideTableRefCountB template void RefCounts::incrementNonAtomicSlow(RefCountBits oldbits, uint32_t n) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return; } else if (oldbits.hasSideTable()) { @@ -52,7 +52,7 @@ template void RefCounts::incrementNonAtomicSlow(SideTable template bool RefCounts::tryIncrementSlow(RefCountBits oldbits) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return true; } else if (oldbits.hasSideTable()) @@ -65,7 +65,7 @@ template bool RefCounts::tryIncrementSlow(SideTableRefCou template bool RefCounts::tryIncrementNonAtomicSlow(RefCountBits oldbits) { - if (oldbits.isImmortal()) { + if (oldbits.isImmortal(false)) { return true; } else if (oldbits.hasSideTable()) diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 310bd403d3b29..32dd7e7398cab 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -89,6 +89,29 @@ - (id)debugQuickLookObject; namespace { +class FieldType { + const Metadata *type; + bool indirect; + TypeReferenceOwnership referenceOwnership; +public: + + constexpr FieldType() : type(nullptr), indirect(false), referenceOwnership() { } + constexpr FieldType(const Metadata *T) : type(T), indirect(false), referenceOwnership() { } + + static constexpr FieldType untypedEnumCase(bool indirect) { + FieldType type{}; + type.indirect = indirect; + return type; + } + const Metadata *getType() const { return type; } + const TypeReferenceOwnership getReferenceOwnership() const { return referenceOwnership; } + bool isIndirect() const { return indirect; } + void setIndirect(bool value) { indirect = value; } + void setReferenceOwnership(TypeReferenceOwnership newOwnership) { + referenceOwnership = newOwnership; + } +}; + /// The layout of Any. using Any = OpaqueExistentialContainer; @@ -123,58 +146,63 @@ - (id)debugQuickLookObject; return std::make_tuple(T, Value); } -static bool loadSpecialReferenceStorage(OpaqueValue *fieldData, - const FieldType fieldType, - Any *outValue) { - // isWeak() implies a reference type via Sema. - if (!fieldType.isWeak()) - return false; - - auto type = fieldType.getType(); +static void copyWeakFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { assert(type->getKind() == MetadataKind::Optional); + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectWeakLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(WeakClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} - auto *weakField = reinterpret_cast(fieldData); - auto *strongValue = swift_unknownObjectWeakLoadStrong(weakField); - - // Now that we have a strong reference, we need to create a temporary buffer - // from which to copy the whole value, which might be a native class-bound - // existential, which means we also need to copy n witness tables, for - // however many protocols are in the protocol composition. For example, if we - // are copying a: - // weak var myWeakProperty : (Protocol1 & Protocol2)? - // then we need to copy three values: - // - the instance - // - the witness table for Protocol1 - // - the witness table for Protocol2 - - auto *weakContainer = - reinterpret_cast(fieldData); - - // Create a temporary existential where we can put the strong reference. - // The allocateBuffer value witness requires a ValueBuffer to own the - // allocated storage. - ValueBuffer temporaryBuffer; - - auto *temporaryValue = reinterpret_cast( - type->allocateBufferIn(&temporaryBuffer)); - - // Now copy the entire value out of the parent, which will include the - // witness tables. - temporaryValue->Value = strongValue; - auto valueWitnessesSize = type->getValueWitnesses()->getSize() - - sizeof(WeakClassExistentialContainer); - memcpy(temporaryValue->getWitnessTables(), weakContainer->getWitnessTables(), - valueWitnessesSize); - - outValue->Type = type; - auto *opaqueValueAddr = type->allocateBoxForExistentialIn(&outValue->Buffer); - type->vw_initializeWithCopy(opaqueValueAddr, - reinterpret_cast(temporaryValue)); - - type->deallocateBufferIn(&temporaryBuffer); - swift_unknownObjectRelease(strongValue); - - return true; +static void copyUnownedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + auto *srcContainer = reinterpret_cast(fieldData); + auto *destClassContainer = reinterpret_cast(destContainer); + destClassContainer->Value = swift_unknownObjectUnownedLoadStrong(&srcContainer->Value); + auto witnessTablesSize = type->vw_size() - sizeof(UnownedClassExistentialContainer); + memcpy(destClassContainer->getWitnessTables(), srcContainer->getWitnessTables(), witnessTablesSize); +} + +static void copyUnmanagedFieldContents(OpaqueValue *destContainer, const Metadata *type, OpaqueValue *fieldData) { + // Also known as "unowned(unsafe)". + // This is simpler than the unowned/weak cases because unmanaged + // references are fundamentally the same as strong ones, so we + // can use the regular strong reference support that already + // knows how to handle existentials and Obj-C references. + type->vw_initializeWithCopy(destContainer, fieldData); +} + +static AnyReturn copyFieldContents(OpaqueValue *fieldData, + const FieldType fieldType) { + Any outValue; + auto *type = fieldType.getType(); + outValue.Type = type; + auto ownership = fieldType.getReferenceOwnership(); + auto *destContainer = type->allocateBoxForExistentialIn(&outValue.Buffer); + + if (ownership.isStrong()) { + type->vw_initializeWithCopy(destContainer, fieldData); + } + + // Generate a conditional clause for every known ownership type. + // If this causes errors, it's because someone added a new ownership type + // to ReferenceStorage.def and missed some related updates. +#define REF_STORAGE(Name, ...) \ + else if (ownership.is##Name()) { \ + copy##Name##FieldContents(destContainer, type, fieldData); \ + } +#include "swift/AST/ReferenceStorage.def" + + else { + // The field was declared with a reference type we don't understand. + warning(0, "Value with unrecognized reference type is reflected as ()"); + // Clean up the buffer allocated above + type->deallocateBoxForExistentialIn(&outValue.Buffer); + // Return an existential containing Void + outValue.Type = &METADATA_SYM(EMPTY_TUPLE_MANGLING); + } + + return AnyReturn(outValue); } @@ -310,11 +338,7 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); - return {"unknown", - FieldType() - .withType(&METADATA_SYM(EMPTY_TUPLE_MANGLING)) - .withIndirect(false) - .withWeak(false)}; + return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; }; auto *baseDesc = base->getTypeContextDescriptor(); @@ -325,14 +349,13 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { if (!fields) return failedToFindMetadata(); - const FieldDescriptor &descriptor = *fields; - auto &field = descriptor.getFields()[index]; + auto &field = fields->getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) - return {name, FieldType().withIndirect(field.isIndirectCase())}; + return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); @@ -360,10 +383,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { (int)typeName.size(), typeName.data()); } - return {name, FieldType() - .withType(typeInfo.getMetadata()) - .withIndirect(field.isIndirectCase()) - .withWeak(typeInfo.isWeak())}; + auto fieldType = FieldType(typeInfo.getMetadata()); + fieldType.setIndirect(field.isIndirectCase()); + fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); + return {name, fieldType}; } // Implementation for structs. @@ -397,7 +420,6 @@ AnyReturn subscript(intptr_t i, const char **outName, // Load the offset from its respective vector. auto fieldOffset = Struct->getFieldOffsets()[i]; - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -409,15 +431,7 @@ AnyReturn subscript(intptr_t i, const char **outName, auto *bytes = reinterpret_cast(value); auto *fieldData = reinterpret_cast(bytes + fieldOffset); - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } }; @@ -559,7 +573,6 @@ AnyReturn subscript(intptr_t i, const char **outName, #endif } - Any result; StringRef name; FieldType fieldInfo; std::tie(name, fieldInfo) = getFieldAt(type, i); @@ -571,15 +584,7 @@ AnyReturn subscript(intptr_t i, const char **outName, *outName = name.data(); *outFreeFunc = nullptr; - bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result); - if (!didLoad) { - result.Type = fieldInfo.getType(); - auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer); - result.Type->vw_initializeWithCopy(opaqueValueAddr, - const_cast(fieldData)); - } - - return AnyReturn(result); + return copyFieldContents(fieldData, fieldInfo); } #if SWIFT_OBJC_INTEROP diff --git a/stdlib/public/runtime/SwiftObject.h b/stdlib/public/runtime/SwiftObject.h index 5b1bbcca2ab29..1f266c4efadeb 100644 --- a/stdlib/public/runtime/SwiftObject.h +++ b/stdlib/public/runtime/SwiftObject.h @@ -29,6 +29,7 @@ #if SWIFT_OBJC_INTEROP +#if __OBJC__ // Source code: "SwiftObject" // Real class name: mangled "Swift._SwiftObject" @@ -83,5 +84,13 @@ id getDescription(OpaqueValue *value, const Metadata *type); } #endif +#endif + +namespace swift { + +/// Get the NSObject metadata. +const Metadata *getNSObjectMetadata(); + +} #endif diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 6cd40846f1f03..3fe43c5e75598 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -35,6 +35,7 @@ #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" #include "CompatibilityOverride.h" +#include "ErrorObject.h" #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" @@ -201,7 +202,10 @@ + (void)initialize { // runs on older OSes in certain testing scenarios, so that doesn't matter. // Only perform the check on newer OSes where the value should definitely // match. - if (!_swift_isBackDeploying()) { +# if SWIFT_BUILD_HAS_BACK_DEPLOYMENT + if (!_swift_isBackDeploying()) +# endif + { assert(&objc_debug_isa_class_mask); assert(objc_debug_isa_class_mask == SWIFT_ISA_MASK); } @@ -260,33 +264,7 @@ - (void)doesNotRecognizeSelector: (SEL) sel { class_getName(cls), sel_getName(sel)); } -- (id)retain { - auto SELF = reinterpret_cast(self); - swift_retain(SELF); - return self; -} -- (void)release { - auto SELF = reinterpret_cast(self); - swift_release(SELF); -} -- (id)autorelease { - return _objc_rootAutorelease(self); -} -- (NSUInteger)retainCount { - return swift::swift_retainCount(reinterpret_cast(self)); -} -- (BOOL)_isDeallocating { - return swift_isDeallocating(reinterpret_cast(self)); -} -- (BOOL)_tryRetain { - return swift_tryRetain(reinterpret_cast(self)) != nullptr; -} -- (BOOL)allowsWeakReference { - return !swift_isDeallocating(reinterpret_cast(self)); -} -- (BOOL)retainWeakReference { - return swift_tryRetain(reinterpret_cast(self)) != nullptr; -} +STANDARD_OBJC_METHOD_IMPLS_FOR_SWIFT_OBJECTS // Retaining the class object itself is a no-op. + (id)retain { @@ -314,10 +292,6 @@ + (BOOL)retainWeakReference { return YES; } -- (void)dealloc { - swift_rootObjCDealloc(reinterpret_cast(self)); -} - - (BOOL)isKindOfClass:(Class)someClass { for (auto cls = _swift_getClassOfAllocated(self); cls != nullptr; cls = cls->Superclass) @@ -1095,6 +1069,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + return nullptr; } @@ -1111,6 +1099,20 @@ static bool isObjCForUnownedReference(void *value) { return object; } + // For casts to NSError or NSObject, we might need to bridge via the Error + // protocol. Try it now. + if (targetType == reinterpret_cast(getNSErrorClass()) || + targetType == reinterpret_cast([NSObject class])) { + auto srcType = swift_getObjCClassMetadata( + reinterpret_cast( + object_getClass(id_const_cast(object)))); + if (auto srcErrorWitness = findErrorWitness(srcType)) { + return dynamicCastValueToNSError((OpaqueValue*)&object, srcType, + srcErrorWitness, + DynamicCastFlags::TakeOnSuccess); + } + } + Class sourceType = object_getClass(id_const_cast(object)); swift_dynamicCastFailure(reinterpret_cast(sourceType), targetType); @@ -1549,6 +1551,11 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector, free(nullTerminatedFilename); } +const Metadata *swift::getNSObjectMetadata() { + return SWIFT_LAZY_CONSTANT( + swift_getObjCClassMetadata((const ClassMetadata *)[NSObject class])); +} + #endif const ClassMetadata *swift::getRootSuperclass() { diff --git a/stdlib/public/runtime/WeakReference.h b/stdlib/public/runtime/WeakReference.h index ca8c67c5967a2..c089edf18dbdd 100644 --- a/stdlib/public/runtime/WeakReference.h +++ b/stdlib/public/runtime/WeakReference.h @@ -91,6 +91,9 @@ class WeakReferenceBits { #elif defined(__arm__) || defined(_M_ARM) NativeMarkerMask = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_MASK, NativeMarkerValue = SWIFT_ABI_ARM_OBJC_WEAK_REFERENCE_MARKER_VALUE +#elif defined(__s390x__) + NativeMarkerMask = SWIFT_ABI_S390X_OBJC_WEAK_REFERENCE_MARKER_MASK, + NativeMarkerValue = SWIFT_ABI_S390X_OBJC_WEAK_REFERENCE_MARKER_VALUE #elif defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) NativeMarkerMask = SWIFT_ABI_ARM64_OBJC_WEAK_REFERENCE_MARKER_MASK, NativeMarkerValue = SWIFT_ABI_ARM64_OBJC_WEAK_REFERENCE_MARKER_VALUE diff --git a/stdlib/public/stubs/CommandLine.cpp b/stdlib/public/stubs/CommandLine.cpp index 0b4506391beeb..cf371e5ababe4 100644 --- a/stdlib/public/stubs/CommandLine.cpp +++ b/stdlib/public/stubs/CommandLine.cpp @@ -206,6 +206,42 @@ char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { return outBuf; } +#elif defined(__wasi__) +#include +#include +#include + +SWIFT_RUNTIME_STDLIB_API +char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { + assert(outArgLen != nullptr); + + if (_swift_stdlib_ProcessOverrideUnsafeArgv) { + *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; + return _swift_stdlib_ProcessOverrideUnsafeArgv; + } + + __wasi_errno_t err; + + size_t argv_buf_size; + size_t argc; + err = __wasi_args_sizes_get(&argc, &argv_buf_size); + if (err != __WASI_ERRNO_SUCCESS) return nullptr; + + size_t num_ptrs = argc + 1; + char *argv_buf = (char *)malloc(argv_buf_size); + char **argv = (char **)calloc(num_ptrs, sizeof(char *)); + + err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); + if (err != __WASI_ERRNO_SUCCESS) { + free(argv_buf); + free(argv); + return nullptr; + } + + *outArgLen = static_cast(argc); + + return argv; +} #else // Add your favorite OS's command line arg grabber here. SWIFT_RUNTIME_STDLIB_API char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 083deaa4747a1..b54ede8c61f66 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -23,7 +23,7 @@ #include #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__wasi__) #include #endif diff --git a/stdlib/public/stubs/Random.cpp b/stdlib/public/stubs/Random.cpp index bb69f7e7793d1..bc130b145015b 100644 --- a/stdlib/public/stubs/Random.cpp +++ b/stdlib/public/stubs/Random.cpp @@ -42,6 +42,8 @@ #include "swift/Runtime/Mutex.h" #include "../SwiftShims/Random.h" +#include // required for std::min + #if defined(__APPLE__) SWIFT_RUNTIME_STDLIB_API @@ -88,7 +90,7 @@ void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) { if (getrandom_available) { actual_nbytes = WHILE_EINTR(syscall(__NR_getrandom, buf, nbytes, 0)); } -#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__)) +#elif __has_include() && (defined(__CYGWIN__) || defined(__Fuchsia__) || defined(__wasi__)) __swift_size_t getentropy_nbytes = std::min(nbytes, __swift_size_t{256}); if (0 == getentropy(buf, getentropy_nbytes)) { diff --git a/stdlib/public/stubs/Stubs.cpp b/stdlib/public/stubs/Stubs.cpp index 55bada6a1db92..d5548c388df57 100644 --- a/stdlib/public/stubs/Stubs.cpp +++ b/stdlib/public/stubs/Stubs.cpp @@ -26,7 +26,7 @@ #define NOMINMAX #include #else -#if !defined(__HAIKU__) +#if !defined(__HAIKU__) && !defined(__wasi__) #include #else #include @@ -67,7 +67,7 @@ static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) { #define strtod_l swift_strtod_l #define strtof_l swift_strtof_l #endif -#elif defined(__linux__) +#elif defined(__linux__) || defined(__wasi__) #include #else #include @@ -503,6 +503,8 @@ const char *swift::_swift_stdlib_strtof_clocale( void swift::_swift_stdlib_flockfile_stdout() { #if defined(_WIN32) _lock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet https://bugs.swift.org/browse/SR-12097 #else flockfile(stdout); #endif @@ -511,6 +513,8 @@ void swift::_swift_stdlib_flockfile_stdout() { void swift::_swift_stdlib_funlockfile_stdout() { #if defined(_WIN32) _unlock_file(stdout); +#elif defined(__wasi__) + // WebAssembly/WASI doesn't support file locking yet https://bugs.swift.org/browse/SR-12097 #else funlockfile(stdout); #endif diff --git a/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb b/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb index 9016bb1c97d2b..89e389f8ba8bc 100644 --- a/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb +++ b/stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb @@ -65,36 +65,6 @@ SWIFT_RUNTIME_STDLIB_API - (id)initWithCoder: (NSCoder *)coder { return [super init]; } -- (id)retain { - auto SELF = reinterpret_cast(self); - swift_retain(SELF); - return self; -} -- (oneway void)release { - auto SELF = reinterpret_cast(self); - swift_release(SELF); -} -- (id)autorelease { - return _objc_rootAutorelease(self); -} -- (NSUInteger)retainCount { - auto SELF = reinterpret_cast(self); - return swift_retainCount(SELF); -} - -- (BOOL)_tryRetain { - auto SELF = reinterpret_cast(self); - return (bool)swift_tryRetain(SELF); -} -- (BOOL)_isDeallocating { - return swift_isDeallocating(reinterpret_cast(self)); -} -- (BOOL)allowsWeakReference { - return !swift_isDeallocating(reinterpret_cast(self)); -} -- (BOOL)retainWeakReference { - return swift_tryRetain(reinterpret_cast(self)) != nullptr; -} + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { return NO; @@ -102,9 +72,9 @@ SWIFT_RUNTIME_STDLIB_API #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-missing-super-calls" -- (void)dealloc { - swift_rootObjCDealloc(reinterpret_cast(self)); -} + +STANDARD_OBJC_METHOD_IMPLS_FOR_SWIFT_OBJECTS + #pragma clang diagnostic pop @end diff --git a/test/APINotes/versioned-objc.swift b/test/APINotes/versioned-objc.swift index b98fb8d838e6e..9f9b2b64611cd 100644 --- a/test/APINotes/versioned-objc.swift +++ b/test/APINotes/versioned-objc.swift @@ -16,7 +16,7 @@ class ProtoWithVersionedUnavailableMemberImpl: ProtoWithVersionedUnavailableMemb func testNonGeneric() { // CHECK-DIAGS-4:[[@LINE+1]]:{{[0-9]+}}: error: cannot convert value of type 'Any' to specified type 'Int' let _: Int = NewlyGenericSub.defaultElement() - // CHECK-DIAGS-5:[[@LINE-1]]:{{[0-9]+}}: error: generic parameter 'Element' could not be inferred + // CHECK-DIAGS-5:[[@LINE-1]]:{{[0-9]+}}: error: generic class 'NewlyGenericSub' requires that 'Int' be a class type // CHECK-DIAGS-4:[[@LINE+1]]:{{[0-9]+}}: error: cannot specialize non-generic type 'NewlyGenericSub' let _: Int = NewlyGenericSub.defaultElement() @@ -166,7 +166,7 @@ extension PrintingInterference { func testDroppingRenamedPrints() { // CHECK-DIAGS-4: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method print(self) - // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: missing argument for parameter 'extra' in call + // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' refers to instance method rather than global function 'print(_:separator:terminator:)' in module 'Swift' // CHECK-DIAGS-4-NOT: [[@LINE+1]]:{{[0-9]+}}: print(self, extra: self) diff --git a/test/AutoDiff/ModuleInterface/differentiation.swift b/test/AutoDiff/ModuleInterface/differentiation.swift new file mode 100644 index 0000000000000..35ee0f9067891 --- /dev/null +++ b/test/AutoDiff/ModuleInterface/differentiation.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface -enable-library-evolution -enable-experimental-differentiable-programming %s +// RUN: %FileCheck %s < %t.swiftinterface + +public func a(f: @differentiable (Float) -> Float) {} +// CHECK: public func a(f: @differentiable (Swift.Float) -> Swift.Float) + +public func b(f: @differentiable(linear) (Float) -> Float) {} +// CHECK: public func b(f: @differentiable(linear) (Swift.Float) -> Swift.Float) + +public func c(f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: public func c(f: @differentiable (Swift.Float, @noDerivative Swift.Float) -> Swift.Float) diff --git a/test/AutoDiff/Parse/derivative_attr_parse.swift b/test/AutoDiff/Parse/derivative_attr_parse.swift new file mode 100644 index 0000000000000..5bdf00cfb303d --- /dev/null +++ b/test/AutoDiff/Parse/derivative_attr_parse.swift @@ -0,0 +1,92 @@ +// RUN: %target-swift-frontend -parse -verify %s + +/// Good + +@derivative(of: sin, wrt: x) // ok +func vjpSin(x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) +} + +@derivative(of: add, wrt: (x, y)) // ok +func vjpAdd(x: Float, y: Float) + -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x + y, { ($0, $0) }) +} + +extension AdditiveArithmetic where Self: Differentiable { + @derivative(of: +) // ok + static func vjpAdd(x: Self, y: Self) + -> (value: Self, pullback: (TangentVector) -> (TangentVector, TangentVector)) { + return (x + y, { v in (v, v) }) + } +} + +@derivative(of: foo) // ok +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +/// Bad + +// expected-error @+2 {{expected an original function name}} +// expected-error @+1 {{expected declaration}} +@derivative(of: 3) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{expected label 'wrt:' in '@derivative' attribute}} +@derivative(of: wrt, foo) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{expected a colon ':' after 'wrt'}} +@derivative(of: foo, wrt) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{expected label 'wrt:' in '@derivative' attribute}} +@derivative(of: foo, blah, wrt: x) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+2 {{expected ')' in 'derivative' attribute}} +// expected-error @+1 {{expected declaration}} +@derivative(of: foo, wrt: x, blah) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{unexpected ',' separator}} +@derivative(of: foo,) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+2 {{expected ')' in 'derivative' attribute}} +// expected-error @+1 {{expected declaration}} +@derivative(of: foo, wrt: x,) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{expected label 'wrt:' in '@derivative' attribute}} +@derivative(of: foo, foo,) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +// expected-error @+1 {{unexpected ',' separator}} +@derivative(of: foo,) +func dfoo(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +func localDerivativeRegistration() { + // expected-error @+1 {{attribute '@derivative' can only be used in a non-local scope}} + @derivative(of: sin) + func dsin() +} diff --git a/test/AutoDiff/Parse/differentiable_attr_parse.swift b/test/AutoDiff/Parse/differentiable_attr_parse.swift new file mode 100644 index 0000000000000..eb94ff59728ab --- /dev/null +++ b/test/AutoDiff/Parse/differentiable_attr_parse.swift @@ -0,0 +1,215 @@ +// RUN: %target-swift-frontend -parse -verify %s + +// TODO(TF-1021): Remove "deprecated 'jvp:' and 'vjp:' argument" warnings. + +/// Good + +struct Foo { + @differentiable + var x: Float +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:) where T : FloatingPoint) // okay +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: (self, x, y), vjp: foo(_:_:)) // okay +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: foo(_:_:)) // okay +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:)) // okay +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +@differentiable // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: x) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: (x)) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@differentiable(wrt: self) // okay +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@_transparent +@differentiable // okay +@inlinable +func playWellWithOtherAttrs(_ x: Float, _: Float) -> Float { + return 1 + x +} + +@_transparent +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: (self), vjp: _vjpSquareRoot) // okay +public func squareRoot() -> Self { + var lhs = self + lhs.formSquareRoot() + return lhs +} + +@differentiable(linear) // okay +func identity(_ x: Float) -> Float { + return x +} + +@differentiable(linear, wrt: x) // okay +func slope2(_ x: Float) -> Float { + return 2 * x +} + +@differentiable(wrt: y) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, y)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, y)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, 1)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, 1)) // ok +func two(x: Float, y: Float) -> Float { + return x + y +} + +/// Bad + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(3) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(vjp: foo(_:_:), 3) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: (x), foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, y) +func bar(_ x: Float, _ y: Float) -> Float { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, 1) +func two(x: Float, y: Float) -> Float { + return x + y +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: 0, y) +func two(x: Float, y: Float) -> Float { + return x + y +} + +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(wrt: 0,) +func two(x: Float, y: Float) -> Float { + return x + y +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ')' in 'differentiable' attribute}} +@differentiable(vjp: foo(_:_:) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected ':' or '==' to indicate a conformance or same-type requirement}} +@differentiable(vjp: foo(_:_:) where T) +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(,) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:),) +func bar(_ x: Float, _: Float) -> Float { + return 1 + x +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{unexpected ',' separator}} +@differentiable(vjp: foo(_:_:), where T) +func bar(_ x: T, _: T) -> T { + return 1 + x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear) +func slope4(_ x: Float) -> Float { + return 4 * x +} + +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, linear, vjp: const5) +func slope5(_ x: Float) -> Float { + return 5 * x +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{expected either 'wrt:' or a function specifier label, e.g. 'jvp:', or 'vjp:'}} +@differentiable(wrt: x, vjp: const6, linear) +func slope5(_ x: Float) -> Float { + return 6 * x +} diff --git a/test/AutoDiff/Parse/differentiable_func_type.swift b/test/AutoDiff/Parse/differentiable_func_type.swift new file mode 100644 index 0000000000000..e163a246a4fba --- /dev/null +++ b/test/AutoDiff/Parse/differentiable_func_type.swift @@ -0,0 +1,107 @@ +// RUN: %target-swift-frontend -dump-parse -verify %s | %FileCheck %s + +let a: @differentiable (Float) -> Float // okay +// CHECK: (pattern_named 'a' +// CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + +let b: @differentiable(linear) (Float) -> Float // okay +// CHECK: (pattern_named 'b' +// CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + +let c: @differentiable (Float, @noDerivative Float) -> Float // okay +// CHECK: (pattern_named 'c' +// CHECK-NEXT: (type_attributed attrs=@differentiable +// CHECK-NEXT: (type_function +// CHECK-NEXT: (type_tuple +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none)) +// CHECK-NEXT: (type_attributed attrs=@noDerivative +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))) +// CHECK-NEXT: (type_ident +// CHECK-NEXT: (component id='Float' bind=none))))) + +let d: @differentiable (Float) throws -> Float // okay +// CHECK: (pattern_named 'd' +// CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + +let e: @differentiable(linear) (Float) throws -> Float // okay +// CHECK: (pattern_named 'e' +// CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + +// Generic type test. +struct A { + func foo() { + let local: @differentiable(linear) (T) -> T // okay + // CHECK: (pattern_named 'local' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + } +} + +// expected-error @+1 {{expected ')' in '@differentiable' attribute}} +let c: @differentiable(linear (Float) -> Float + +// expected-error @+1 {{expected ')' in '@differentiable' attribute}} +let c: @differentiable(notValidArg (Float) -> Float + +// expected-error @+1 {{unexpected argument 'notValidArg' in '@differentiable' attribute}} +let d: @differentiable(notValidArg) (Float) -> Float + +// Using 'linear' as a type +struct B { + struct linear {} + let propertyB1: @differentiable (linear) -> Float // okay + // CHECK: (pattern_named 'propertyB1' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyB2: @differentiable(linear) (linear) -> linear // okay + // CHECK: (pattern_named 'propertyB2' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + + let propertyB3: @differentiable (linear, linear) -> linear // okay + // CHECK: (pattern_named 'propertyB3' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyB4: @differentiable (linear, Float) -> linear // okay + // CHECK: (pattern_named 'propertyB4' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyB5: @differentiable (Float, linear) -> linear // okay + // CHECK: (pattern_named 'propertyB5' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyB6: @differentiable(linear) (linear, linear, Float, linear) + -> Float // okay + // CHECK: (pattern_named 'propertyB6' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + + // expected-error @+1 {{expected ')' in '@differentiable' attribute}} + let propertyB7: @differentiable(linear (linear) -> Float +} + +// Using 'linear' as a typealias +struct C { + typealias linear = (C) -> C + let propertyC1: @differentiable (linear) -> Float // okay + // CHECK: (pattern_named 'propertyC1' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyC2: @differentiable(linear) (linear) -> linear // okay + // CHECK: (pattern_named 'propertyC2' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + + let propertyC3: @differentiable linear // okay + // CHECK: (pattern_named 'propertyC3' + // CHECK-NEXT: (type_attributed attrs=@differentiable{{[^(]}} + + let propertyC4: linear // okay + // CHECK: (pattern_named 'propertyC4' + + let propertyC5: @differentiable(linear) linear // okay + // CHECK: (pattern_named 'propertyC5' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) + + let propertyC6: @differentiable(linear) @convention(c) linear // okay + // CHECK: (pattern_named 'propertyC6' + // CHECK-NEXT: (type_attributed attrs=@differentiable(linear) +} diff --git a/test/AutoDiff/Parse/transpose_attr_parse.swift b/test/AutoDiff/Parse/transpose_attr_parse.swift new file mode 100644 index 0000000000000..09f9685283896 --- /dev/null +++ b/test/AutoDiff/Parse/transpose_attr_parse.swift @@ -0,0 +1,96 @@ +// RUN: %target-swift-frontend -parse -verify %s + +/// Good + +@transpose(of: foo) +func transpose(v: Float) -> Float + +@transpose(of: foo(_:_:)) +func transpose(v: Float) -> Float + +@transpose(of: wrt, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: 0) +func transpose(v: Float) -> Float + +@transpose(of: foo, wrt: (0, 1)) +func transpose(v: Float) -> (Float, Float) + +@transpose(of: foo, wrt: (self, 0, 1, 2)) +func transpose(v: Float) -> (Float, Float, Float, Float) + +// Qualified declaration. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + +// Qualified declaration with specialized generic type. +@transpose(of: A.B.C.foo(x:y:_:z:)) +func transpose(v: Float) -> Float + +// Qualified operator. +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float.+) +func transpose(v: Float) -> Float + +// Qualified leading-period operator (confusing). +// TODO(TF-1065): Consider disallowing qualified operators. +@transpose(of: Swift.Float..<) +func transpose(v: Float) -> Float + +// `init` keyword edge case. +@transpose(of: Swift.Float.init(_:)) +func transpose(v: Float) -> Float + +// `subscript` keyword edge case. +@transpose(of: Swift.Array.subscript(_:)) +func transpose(v: Float) -> Float + +/// Bad + +// expected-error @+2 {{expected an original function name}} +// expected-error @+1 {{expected declaration}} +@transpose(of: 3) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected label 'wrt:' in '@transpose' attribute}} +@transpose(of: foo, blah) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a colon ':' after 'wrt'}} +@transpose(of: foo, wrt) +func transpose(v: Float) -> Float + +// expected-error @+1 {{unexpected ',' separator}} +@transpose(of: foo,) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: foo, wrt: 0,) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: v) +func transpose(v: Float) -> Float + +// expected-error @+1 {{expected a parameter, which can be a function parameter index or 'self'}} +@transpose(of: foo, wrt: (0, v)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+(_:_)) +func transpose(v: Float) -> Float + +// expected-error @+2 {{expected ')' in 'transpose' attribute}} +// expected-error @+1 {{expected declaration}} +@transpose(of: Swift.Float.+.a) +func transpose(v: Float) -> Float + +func testLocalTransposeRegistration() { + // Transpose registration can only be non-local. + // expected-error @+1 {{attribute '@transpose' can only be used in a non-local scope}} + @transpose(of: +) + func transpose(_ x: Float) -> (Float, Float) +} diff --git a/test/AutoDiff/SIL/Serialization/differentiation.swift b/test/AutoDiff/SIL/Serialization/differentiation.swift new file mode 100644 index 0000000000000..828b08ccb52b7 --- /dev/null +++ b/test/AutoDiff/SIL/Serialization/differentiation.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name differentiation -enable-experimental-differentiable-programming +// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.2.sib -module-name differentiation -enable-experimental-differentiable-programming +// RUN: %target-sil-opt %t/tmp.2.sib -module-name differentiation -emit-sorted-sil -enable-experimental-differentiable-programming | %FileCheck %s + +sil_stage raw + +import Swift + +sil @a : $@convention(thin) (@differentiable (Float) -> Float) -> @differentiable (Float) -> Float { +bb0(%0 : $@differentiable (Float) -> Float): + return %0 : $@differentiable (Float) -> Float +} + +// CHECK-LABEL: sil @a : $@convention(thin) (@differentiable (Float) -> Float) -> @differentiable (Float) -> Float { +// CHECK: bb0([[ARG:%.*]] : $@differentiable (Float) -> Float): +// CHECK: return [[ARG]] : $@differentiable (Float) -> Float +// CHECK: } + +sil @b : $@convention(thin) (@differentiable(linear) (Float) -> Float) -> @differentiable(linear) (Float) -> Float { +bb0(%0 : $@differentiable(linear) (Float) -> Float): + return %0 : $@differentiable(linear) (Float) -> Float +} + +// CHECK-LABEL: sil @b : $@convention(thin) (@differentiable(linear) (Float) -> Float) -> @differentiable(linear) (Float) -> Float { +// CHECK: bb0([[ARG:%.*]] : $@differentiable(linear) (Float) -> Float): +// CHECK: return [[ARG]] : $@differentiable(linear) (Float) -> Float +// CHECK: } + +sil @c : $@convention(thin) (@differentiable (Float, @noDerivative Float) -> Float) -> @differentiable (Float, @noDerivative Float) -> Float { +bb0(%0 : $@differentiable (Float, @noDerivative Float) -> Float): + return %0 : $@differentiable (Float, @noDerivative Float) -> Float +} + +// CHECK-LABEL: sil @c : $@convention(thin) (@differentiable (Float, @noDerivative Float) -> Float) -> @differentiable (Float, @noDerivative Float) -> Float { +// CHECK: bb0(%0 : $@differentiable (Float, @noDerivative Float) -> Float): +// CHECK: return %0 : $@differentiable (Float, @noDerivative Float) -> Float +// CHECK: } + +sil @d : $@convention(thin) (@differentiable(linear) (Float, @noDerivative Float) -> Float) -> @differentiable(linear) (Float, @noDerivative Float) -> Float { +bb0(%0 : $@differentiable(linear) (Float, @noDerivative Float) -> Float): + return %0 : $@differentiable(linear) (Float, @noDerivative Float) -> Float +} + +// CHECK-LABEL: sil @d : $@convention(thin) (@differentiable(linear) (Float, @noDerivative Float) -> Float) -> @differentiable(linear) (Float, @noDerivative Float) -> Float { +// CHECK: bb0(%0 : $@differentiable(linear) (Float, @noDerivative Float) -> Float): +// CHECK: return %0 : $@differentiable(linear) (Float, @noDerivative Float) -> Float +// CHECK: } diff --git a/test/AutoDiff/SILGen/differentiable_function.swift b/test/AutoDiff/SILGen/differentiable_function.swift new file mode 100644 index 0000000000000..bd1282fa59199 --- /dev/null +++ b/test/AutoDiff/SILGen/differentiable_function.swift @@ -0,0 +1,56 @@ +// RUN: %target-swift-frontend -emit-silgen -enable-experimental-differentiable-programming %s | %FileCheck %s +// REQUIRES: differentiable_programming + +// Test SILGen for `@differentiable` function typed values. + +import _Differentiation + +@_silgen_name("differentiable") +func differentiable(_ fn: @escaping @differentiable (Float) -> Float) + -> @differentiable (Float) -> Float { + return fn +} + +@_silgen_name("linear") +func linear(_ fn: @escaping @differentiable(linear) (Float) -> Float) + -> @differentiable(linear) (Float) -> Float { + return fn +} + +@_silgen_name("differentiable_noDerivative") +func differentiable_noDerivative( + _ fn: @escaping @differentiable (Float, @noDerivative Float) -> Float +) -> @differentiable (Float, @noDerivative Float) -> Float { + return fn +} + +@_silgen_name("linear_noDerivative") +func linear_noDerivative( + _ fn: @escaping @differentiable(linear) (Float, @noDerivative Float) -> Float +) -> @differentiable(linear) (Float, @noDerivative Float) -> Float { + return fn +} + +// CHECK-LABEL: sil hidden [ossa] @differentiable : $@convention(thin) (@guaranteed @differentiable @callee_guaranteed (Float) -> Float) -> @owned @differentiable @callee_guaranteed (Float) -> Float { +// CHECK: bb0([[FN:%.*]] : @guaranteed $@differentiable @callee_guaranteed (Float) -> Float): +// CHECK: [[COPIED_FN:%.*]] = copy_value [[FN]] : $@differentiable @callee_guaranteed (Float) -> Float +// CHECK: return [[COPIED_FN]] : $@differentiable @callee_guaranteed (Float) -> Float +// CHECK: } + +// CHECK-LABEL: sil hidden [ossa] @linear : $@convention(thin) (@guaranteed @differentiable(linear) @callee_guaranteed (Float) -> Float) -> @owned @differentiable(linear) @callee_guaranteed (Float) -> Float { +// CHECK: bb0([[FN:%.*]] : @guaranteed $@differentiable(linear) @callee_guaranteed (Float) -> Float): +// CHECK: [[COPIED_FN:%.*]] = copy_value [[FN]] : $@differentiable(linear) @callee_guaranteed (Float) -> Float +// CHECK: return [[COPIED_FN]] : $@differentiable(linear) @callee_guaranteed (Float) -> Float +// CHECK: } + +// CHECK-LABEL: sil hidden [ossa] @differentiable_noDerivative : $@convention(thin) (@guaranteed @differentiable @callee_guaranteed (Float, @noDerivative Float) -> Float) -> @owned @differentiable @callee_guaranteed (Float, @noDerivative Float) -> Float { +// CHECK: bb0([[FN:%.*]] : @guaranteed $@differentiable @callee_guaranteed (Float, @noDerivative Float) -> Float): +// CHECK: [[COPIED_FN:%.*]] = copy_value [[FN]] : $@differentiable @callee_guaranteed (Float, @noDerivative Float) -> Float +// CHECK: return [[COPIED_FN]] : $@differentiable @callee_guaranteed (Float, @noDerivative Float) -> Float +// CHECK: } + +// CHECK-LABEL: sil hidden [ossa] @linear_noDerivative : $@convention(thin) (@guaranteed @differentiable(linear) @callee_guaranteed (Float, @noDerivative Float) -> Float) -> @owned @differentiable(linear) @callee_guaranteed (Float, @noDerivative Float) -> Float { +// CHECK: bb0([[FN:%.*]] : @guaranteed $@differentiable(linear) @callee_guaranteed (Float, @noDerivative Float) -> Float): +// CHECK: [[COPIED_FN:%.*]] = copy_value [[FN]] : $@differentiable(linear) @callee_guaranteed (Float, @noDerivative Float) -> Float +// CHECK: return [[COPIED_FN]] : $@differentiable(linear) @callee_guaranteed (Float, @noDerivative Float) -> Float +// CHECK: } diff --git a/test/AutoDiff/Sema/derivative_attr_type_checking.swift b/test/AutoDiff/Sema/derivative_attr_type_checking.swift new file mode 100644 index 0000000000000..babb8fad87aa5 --- /dev/null +++ b/test/AutoDiff/Sema/derivative_attr_type_checking.swift @@ -0,0 +1,600 @@ +// RUN: %target-swift-frontend-typecheck -enable-experimental-differentiable-programming -verify %s +// REQUIRES: differentiable_programming + +import _Differentiation + +// Test top-level functions. + +func id(_ x: Float) -> Float { + return x +} +@derivative(of: id) +func jvpId(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} +@derivative(of: id, wrt: x) +func vjpIdExplicitWrt(x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) +} + +func generic(_ x: T, _ y: T) -> T { + return x +} +@derivative(of: generic) +func jvpGeneric(x: T, y: T) -> ( + value: T, differential: (T.TangentVector, T.TangentVector) -> T.TangentVector +) { + return (x, { $0 + $1 }) +} +@derivative(of: generic) +func vjpGenericExtraGenericRequirements( + x: T, y: T +) -> (value: T, pullback: (T) -> (T, T)) where T == T.TangentVector { + return (x, { ($0, $0) }) +} + +// Test `wrt` parameter clauses. + +func add(x: Float, y: Float) -> Float { + return x + y +} +@derivative(of: add, wrt: x) // ok +func vjpAddWrtX(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float)) { + return (x + y, { $0 }) +} +@derivative(of: add, wrt: (x, y)) // ok +func vjpAddWrtXY(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x + y, { ($0, $0) }) +} + +// Test index-based `wrt` parameters. + +func subtract(x: Float, y: Float) -> Float { + return x - y +} +@derivative(of: subtract, wrt: (0, y)) // ok +func vjpSubtractWrt0Y(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x - y, { ($0, $0) }) +} +@derivative(of: subtract, wrt: (1)) // ok +func vjpSubtractWrt1(x: Float, y: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x - y, { $0 }) +} + +// Test incorrect `@derivative` declaration type. + +// expected-note @+1 {{'incorrectDerivativeType' defined here}} +func incorrectDerivativeType(_ x: Float) -> Float { + return x +} + +// expected-error @+1 {{'@derivative(of:)' attribute requires function to return a two-element tuple of type '(value: T..., pullback: (U.TangentVector) -> T.TangentVector...)' or '(value: T..., differential: (T.TangentVector...) -> U.TangentVector)'}} +@derivative(of: incorrectDerivativeType) +func jvpResultIncorrect(x: Float) -> Float { + return x +} +// expected-error @+1 {{'@derivative(of:)' attribute requires function to return a two-element tuple (first element must have label 'value:'}} +@derivative(of: incorrectDerivativeType) +func vjpResultIncorrectFirstLabel(x: Float) -> (Float, (Float) -> Float) { + return (x, { $0 }) +} +// expected-error @+1 {{'@derivative(of:)' attribute requires function to return a two-element tuple (second element must have label 'pullback:' or 'differential:')}} +@derivative(of: incorrectDerivativeType) +func vjpResultIncorrectSecondLabel(x: Float) -> (value: Float, (Float) -> Float) { + return (x, { $0 }) +} +// expected-error @+1 {{'@derivative(of:)' attribute requires function to return a two-element tuple (first element type 'Int' must conform to 'Differentiable')}} +@derivative(of: incorrectDerivativeType) +func vjpResultNotDifferentiable(x: Int) -> ( + value: Int, pullback: (Int) -> Int +) { + return (x, { $0 }) +} +// expected-error @+2 {{function result's 'pullback' type does not match 'incorrectDerivativeType'}} +// expected-note @+3 {{'pullback' does not have expected type '(Float.TangentVector) -> (Float.TangentVector)' (aka '(Float) -> Float')}} +@derivative(of: incorrectDerivativeType) +func vjpResultIncorrectPullbackType(x: Float) -> ( + value: Float, pullback: (Double) -> Double +) { + return (x, { $0 }) +} + +// Test invalid `wrt:` differentiation parameters. + +func invalidWrtParam(_ x: Float, _ y: Float) -> Float { + return x +} + +// expected-error @+1 {{unknown parameter name 'z'}} +@derivative(of: add, wrt: z) +func vjpUnknownParam(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float)) { + return (x + y, { $0 }) +} +// expected-error @+1 {{parameters must be specified in original order}} +@derivative(of: invalidWrtParam, wrt: (y, x)) +func vjpParamOrderNotIncreasing(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x + y, { ($0, $0) }) +} +// expected-error @+1 {{'self' parameter is only applicable to instance methods}} +@derivative(of: invalidWrtParam, wrt: self) +func vjpInvalidSelfParam(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x + y, { ($0, $0) }) +} +// expected-error @+1 {{parameter index is larger than total number of parameters}} +@derivative(of: invalidWrtParam, wrt: 2) +func vjpSubtractWrt2(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x - y, { ($0, $0) }) +} +// expected-error @+1 {{parameters must be specified in original order}} +@derivative(of: invalidWrtParam, wrt: (1, x)) +func vjpSubtractWrt1x(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x - y, { ($0, $0) }) +} +// expected-error @+1 {{parameters must be specified in original order}} +@derivative(of: invalidWrtParam, wrt: (1, 0)) +func vjpSubtractWrt10(x: Float, y: Float) -> (value: Float, pullback: (Float) -> (Float, Float)) { + return (x - y, { ($0, $0) }) +} + +func noParameters() -> Float { + return 1 +} +// expected-error @+1 {{'vjpNoParameters()' has no parameters to differentiate with respect to}} +@derivative(of: noParameters) +func vjpNoParameters() -> (value: Float, pullback: (Float) -> Float) { + return (1, { $0 }) +} + +func noDifferentiableParameters(x: Int) -> Float { + return 1 +} +// expected-error @+1 {{no differentiation parameters could be inferred; must differentiate with respect to at least one parameter conforming to 'Differentiable'}} +@derivative(of: noDifferentiableParameters) +func vjpNoDifferentiableParameters(x: Int) -> ( + value: Float, pullback: (Float) -> Int +) { + return (1, { _ in 0 }) +} + +func functionParameter(_ fn: (Float) -> Float) -> Float { + return fn(1) +} +// expected-error @+1 {{can only differentiate with respect to parameters that conform to 'Differentiable', but '(Float) -> Float' does not conform to 'Differentiable'}} +@derivative(of: functionParameter, wrt: fn) +func vjpFunctionParameter(_ fn: (Float) -> Float) -> ( + value: Float, pullback: (Float) -> Float +) { + return (functionParameter(fn), { $0 }) +} + +func inoutParameter(_ x: inout Float) -> Float { + return x +} +// expected-error @+1 {{cannot differentiate with respect to 'inout' parameter ('inout Float'}} +@derivative(of: inoutParameter) +func vjpInoutParameter(x: inout Float) -> ( + value: Float, pullback: (Float) -> Float +) { + return (inoutParameter(&x), { $0 }) +} + +// Test static methods. + +protocol StaticMethod: Differentiable { + static func foo(_ x: Float) -> Float + static func generic(_ x: T) -> T +} + +extension StaticMethod { + static func foo(_ x: Float) -> Float { x } + static func generic(_ x: T) -> T { x } +} + +extension StaticMethod { + @derivative(of: foo) + static func jvpFoo(x: Float) -> (value: Float, differential: (Float) -> Float) + { + return (x, { $0 }) + } + + // Test qualified declaration name. + @derivative(of: StaticMethod.foo) + static func vjpFoo(x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) + } + + @derivative(of: generic) + static func vjpGeneric(_ x: T) -> ( + value: T, pullback: (T.TangentVector) -> (T.TangentVector) + ) { + return (x, { $0 }) + } + + // expected-error @+1 {{'self' parameter is only applicable to instance methods}} + @derivative(of: foo, wrt: (self, x)) + static func vjpFooWrtSelf(x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) + } +} + +// Test instance methods. + +protocol InstanceMethod: Differentiable { + func foo(_ x: Self) -> Self + func generic(_ x: T) -> Self +} + +extension InstanceMethod { + // expected-note @+1 {{'foo' defined here}} + func foo(_ x: Self) -> Self { x } + + // expected-note @+1 {{'generic' defined here}} + func generic(_ x: T) -> Self { self } +} + +extension InstanceMethod { + @derivative(of: foo) + func jvpFoo(x: Self) -> ( + value: Self, differential: (TangentVector, TangentVector) -> (TangentVector) + ) { + return (x, { $0 + $1 }) + } + + // Test qualified declaration name. + @derivative(of: InstanceMethod.foo, wrt: x) + func jvpFooWrtX(x: Self) -> ( + value: Self, differential: (TangentVector) -> (TangentVector) + ) { + return (x, { $0 }) + } + + @derivative(of: generic) + func vjpGeneric(_ x: T) -> ( + value: Self, pullback: (TangentVector) -> (TangentVector, T.TangentVector) + ) { + return (self, { ($0, .zero) }) + } + + @derivative(of: generic, wrt: (self, x)) + func jvpGenericWrt(_ x: T) -> (value: Self, differential: (TangentVector, T.TangentVector) -> TangentVector) { + return (self, { dself, dx in dself }) + } + + // expected-error @+1 {{'self' parameter must come first in the parameter list}} + @derivative(of: generic, wrt: (x, self)) + func jvpGenericWrtSelf(_ x: T) -> (value: Self, differential: (TangentVector, T.TangentVector) -> TangentVector) { + return (self, { dself, dx in dself }) + } +} + +extension InstanceMethod { + // If `Self` conforms to `Differentiable`, then `Self` is inferred to be a differentiation parameter. + // expected-error @+2 {{function result's 'pullback' type does not match 'foo'}} + // expected-note @+3 {{'pullback' does not have expected type '(Self.TangentVector) -> (Self.TangentVector, Self.TangentVector)'}} + @derivative(of: foo) + func vjpFoo(x: Self) -> ( + value: Self, pullback: (TangentVector) -> TangentVector + ) { + return (x, { $0 }) + } + + // If `Self` conforms to `Differentiable`, then `Self` is inferred to be a differentiation parameter. + // expected-error @+2 {{function result's 'pullback' type does not match 'generic'}} + // expected-note @+3 {{'pullback' does not have expected type '(Self.TangentVector) -> (Self.TangentVector, T.TangentVector)'}} + @derivative(of: generic) + func vjpGeneric(_ x: T) -> ( + value: Self, pullback: (TangentVector) -> T.TangentVector + ) { + return (self, { _ in .zero }) + } +} + +// Test `@derivative` declaration with more constrained generic signature. + +func req1(_ x: T) -> T { + return x +} +@derivative(of: req1) +func vjpExtraConformanceConstraint(_ x: T) -> ( + value: T, pullback: (T.TangentVector) -> T.TangentVector +) { + return (x, { $0 }) +} + +func req2(_ x: T, _ y: U) -> T { + return x +} +@derivative(of: req2) +func vjpExtraConformanceConstraints( _ x: T, _ y: U) -> ( + value: T, pullback: (T) -> (T, U) +) where T == T.TangentVector, U == U.TangentVector, T: CustomStringConvertible { + return (x, { ($0, .zero) }) +} + +// Test `@derivative` declaration with extra same-type requirements. +func req3(_ x: T) -> T { + return x +} +@derivative(of: req3) +func vjpSameTypeRequirementsGenericParametersAllConcrete(_ x: T) -> ( + value: T, pullback: (T.TangentVector) -> T.TangentVector +) where T: Differentiable, T.TangentVector == Float { + return (x, { $0 }) +} + +struct Wrapper: Equatable { + var x: T + init(_ x: T) { self.x = x } +} +extension Wrapper: AdditiveArithmetic where T: AdditiveArithmetic { + static var zero: Self { .init(.zero) } + static func + (lhs: Self, rhs: Self) -> Self { .init(lhs.x + rhs.x) } + static func - (lhs: Self, rhs: Self) -> Self { .init(lhs.x - rhs.x) } +} +extension Wrapper: Differentiable where T: Differentiable, T == T.TangentVector { + typealias TangentVector = Wrapper +} +extension Wrapper where T: Differentiable, T == T.TangentVector { + @derivative(of: init(_:)) + static func vjpInit(_ x: T) -> (value: Self, pullback: (Wrapper.TangentVector) -> (T)) { + fatalError() + } +} + +// Test class methods. + +class Super { + @differentiable + func foo(_ x: Float) -> Float { + return x + } + + @derivative(of: foo) + func vjpFoo(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (foo(x), { v in v }) + } +} + +class Sub: Super { + // TODO(TF-649): Enable `@derivative` to override derivatives for original + // declaration defined in superclass. + // expected-error @+1 {{'foo' is not defined in the current type context}} + @derivative(of: foo) + override func vjpFoo(_ x: Float) -> (value: Float, pullback: (Float) -> Float) + { + return (foo(x), { v in v }) + } +} + +// Test non-`func` original declarations. + +struct Struct { + var x: T +} +extension Struct: Equatable where T: Equatable {} +extension Struct: Differentiable & AdditiveArithmetic +where T: Differentiable & AdditiveArithmetic { + static var zero: Self { + fatalError() + } + static func + (lhs: Self, rhs: Self) -> Self { + fatalError() + } + static func - (lhs: Self, rhs: Self) -> Self { + fatalError() + } + typealias TangentVector = Struct + mutating func move(along direction: TangentVector) { + x.move(along: direction.x) + } +} + +// Test computed properties. + +extension Struct { + var computedProperty: T { x } +} +extension Struct where T: Differentiable & AdditiveArithmetic { + @derivative(of: computedProperty) + func vjpProperty() -> (value: T, pullback: (T.TangentVector) -> TangentVector) { + return (x, { v in .init(x: v) }) + } +} + +// Test initializers. + +extension Struct { + init(_ x: Float) {} + init(_ x: T, y: Float) {} +} +extension Struct where T: Differentiable & AdditiveArithmetic { + @derivative(of: init) + static func vjpInit(_ x: Float) -> ( + value: Struct, pullback: (TangentVector) -> Float + ) { + return (.init(x), { _ in .zero }) + } + + @derivative(of: init(_:y:)) + static func vjpInit2(_ x: T, _ y: Float) -> ( + value: Struct, pullback: (TangentVector) -> (T.TangentVector, Float) + ) { + return (.init(x, y: y), { _ in (.zero, .zero) }) + } +} + +// Test subscripts. + +extension Struct { + subscript() -> Float { + get { 1 } + set {} + } + subscript(float float: Float) -> Float { 1 } + subscript(x: T) -> T { x } +} +extension Struct where T: Differentiable & AdditiveArithmetic { + @derivative(of: subscript) + func vjpSubscript() -> (value: Float, pullback: (Float) -> TangentVector) { + return (1, { _ in .zero }) + } + + @derivative(of: subscript(float:), wrt: self) + func vjpSubscriptLabelled(float: Float) -> (value: Float, pullback: (Float) -> TangentVector) { + return (1, { _ in .zero }) + } + + @derivative(of: subscript(_:), wrt: self) + func vjpSubscriptGeneric(x: T) -> (value: T, pullback: (T.TangentVector) -> TangentVector) { + return (x, { _ in .zero }) + } +} + +// Test duplicate `@derivative` attribute. + +func duplicate(_ x: Float) -> Float { x } +// expected-note @+1 {{other attribute declared here}} +@derivative(of: duplicate) +func jvpDuplicate1(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + return (duplicate(x), { $0 }) +} +// expected-error @+1 {{a derivative already exists for 'duplicate'}} +@derivative(of: duplicate) +func jvpDuplicate2(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + return (duplicate(x), { $0 }) +} + +// Test invalid original declaration kind. + +var globalVariable: Float +// expected-error @+1 {{'globalVariable' is not a 'func', 'init', 'subscript', or 'var' computed property declaration}} +@derivative(of: globalVariable) +func invalidOriginalDeclaration(x: Float) -> ( + value: Float, differential: (Float) -> (Float) +) { + return (x, { $0 }) +} + +// Test ambiguous original declaration. + +protocol P1 {} +protocol P2 {} +func ambiguous(_ x: T) -> T { x } +func ambiguous(_ x: T) -> T { x } + +// expected-error @+1 {{ambiguous reference to 'ambiguous' in '@derivative' attribute}} +@derivative(of: ambiguous) +func jvpAmbiguous(x: T) + -> (value: T, differential: (T.TangentVector) -> (T.TangentVector)) +{ + return (x, { $0 }) +} + +// Test no valid original declaration. +// Original declarations are invalid because they have extra generic +// requirements unsatisfied by the `@derivative` function. + +func invalid(x: T) -> T { x } +func invalid(x: T) -> T { x } +func invalid(x: T) -> T { x } + +// expected-error @+1 {{could not find function 'invalid' with expected type ' (x: T) -> T'}} +@derivative(of: invalid) +func jvpInvalid(x: T) -> ( + value: T, differential: (T.TangentVector) -> T.TangentVector +) { + return (x, { $0 }) +} + +// Test invalid derivative type context: instance vs static method mismatch. + +struct InvalidTypeContext { + static func staticMethod(_ x: T) -> T { x } + + // expected-error @+1 {{could not find function 'staticMethod' with expected type ' (InvalidTypeContext) -> (T) -> T'}} + @derivative(of: staticMethod) + func jvpStatic(_ x: T) -> ( + value: T, differential: (T.TangentVector) -> (T.TangentVector) + ) { + return (x, { $0 }) + } +} + +// Test stored property original declaration. + +struct HasStoredProperty { + // expected-note @+1 {{'stored' declared here}} + var stored: Float +} +extension HasStoredProperty: Differentiable & AdditiveArithmetic { + static var zero: Self { + fatalError() + } + static func + (lhs: Self, rhs: Self) -> Self { + fatalError() + } + static func - (lhs: Self, rhs: Self) -> Self { + fatalError() + } + typealias TangentVector = Self +} +extension HasStoredProperty { + // expected-error @+1 {{cannot register derivative for stored property 'stored'}} + @derivative(of: stored) + func vjpStored() -> (value: Float, pullback: (Float) -> TangentVector) { + return (stored, { _ in .zero }) + } +} + +// Test derivative registration for protocol requirements. Currently unsupported. +// TODO(TF-982): Lift this restriction and add proper support. + +protocol ProtocolRequirementDerivative { + func requirement(_ x: Float) -> Float +} +extension ProtocolRequirementDerivative { + // NOTE: the error is misleading because `findAbstractFunctionDecl` in + // TypeCheckAttr.cpp is not setup to show customized error messages for + // invalid original function candidates. + // expected-error @+1 {{could not find function 'requirement' with expected type ' (Self) -> (Float) -> Float'}} + @derivative(of: requirement) + func vjpRequirement(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + fatalError() + } +} + +// Test cross-file derivative registration. Currently unsupported. +// TODO(TF-1021): Lift this restriction. + +extension FloatingPoint where Self: Differentiable { + // expected-error @+1 {{derivative not in the same file as the original function}} + @derivative(of: rounded) + func vjpRounded() -> ( + value: Self, + pullback: (Self.TangentVector) -> (Self.TangentVector) + ) { + fatalError() + } +} + +extension Differentiable where Self: AdditiveArithmetic { + // expected-error @+1 {{'+' is not defined in the current type context}} + @derivative(of: +) + static func vjpPlus(x: Self, y: Self) -> ( + value: Self, + pullback: (Self.TangentVector) -> (Self.TangentVector, Self.TangentVector) + ) { + return (x + y, { v in (v, v) }) + } +} + +extension AdditiveArithmetic +where Self: Differentiable, Self == Self.TangentVector { + // expected-error @+1 {{could not find function '+' with expected type ' (Self) -> (Self, Self) -> Self'}} + @derivative(of: +) + func vjpPlusInstanceMethod(x: Self, y: Self) -> ( + value: Self, pullback: (Self) -> (Self, Self) + ) { + return (x + y, { v in (v, v) }) + } +} diff --git a/test/AutoDiff/Sema/differentiable_attr_type_checking.swift b/test/AutoDiff/Sema/differentiable_attr_type_checking.swift new file mode 100644 index 0000000000000..5b64818a29c8d --- /dev/null +++ b/test/AutoDiff/Sema/differentiable_attr_type_checking.swift @@ -0,0 +1,1173 @@ +// RUN: %target-swift-frontend-typecheck -enable-experimental-differentiable-programming -verify %s +// REQUIRES: differentiable_programming + +import _Differentiation + +// Dummy `Differentiable`-conforming type. +struct DummyTangentVector: Differentiable & AdditiveArithmetic { + static var zero: Self { Self() } + static func + (_: Self, _: Self) -> Self { Self() } + static func - (_: Self, _: Self) -> Self { Self() } + typealias TangentVector = Self +} + +@differentiable // expected-error {{'@differentiable' attribute cannot be applied to this declaration}} +let globalConst: Float = 1 + +@differentiable // expected-error {{'@differentiable' attribute cannot be applied to this declaration}} +var globalVar: Float = 1 + +func testLocalVariables() { + // expected-error @+1 {{'_' has no parameters to differentiate with respect to}} + @differentiable + var getter: Float { + return 1 + } + + // expected-error @+1 {{'_' has no parameters to differentiate with respect to}} + @differentiable + var getterSetter: Float { + get { return 1 } + set {} + } +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: dfoo) // expected-error {{'@differentiable' attribute cannot be applied to this declaration}} +protocol P {} + +@differentiable() // ok! +func no_jvp_or_vjp(_ x: Float) -> Float { + return x * x +} + +// Test duplicate `@differentiable` attributes. + +@differentiable // expected-error {{duplicate '@differentiable' attribute with same parameters}} +@differentiable // expected-note {{other attribute declared here}} +func dupe_attributes(arg: Float) -> Float { return arg } + +@differentiable(wrt: arg1) +@differentiable(wrt: arg2) // expected-error {{duplicate '@differentiable' attribute with same parameters}} +@differentiable(wrt: arg2) // expected-note {{other attribute declared here}} +func dupe_attributes(arg1: Float, arg2: Float) -> Float { return arg1 } + +struct ComputedPropertyDupeAttributes: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + var value: T + + @differentiable // expected-note {{other attribute declared here}} + var computed1: T { + @differentiable // expected-error {{duplicate '@differentiable' attribute with same parameters}} + get { value } + set { value = newValue } + } + + // TODO(TF-482): Remove diagnostics when `@differentiable` attributes are + // also uniqued based on generic requirements. + @differentiable(where T == Float) // expected-error {{duplicate '@differentiable' attribute with same parameters}} + @differentiable(where T == Double) // expected-note {{other attribute declared here}} + var computed2: T { + get { value } + set { value = newValue } + } +} + +// Test TF-568. +protocol WrtOnlySelfProtocol: Differentiable { + @differentiable + var computedProperty: Float { get } + + @differentiable + func method() -> Float +} + +class Class: Differentiable { + typealias TangentVector = DummyTangentVector + func move(along _: TangentVector) {} +} +@differentiable(wrt: x) +func invalidDiffWrtClass(_ x: Class) -> Class { + return x +} + +protocol Proto {} +// expected-error @+1 {{can only differentiate with respect to parameters that conform to 'Differentiable', but 'Proto' does not conform to 'Differentiable'}} +@differentiable(wrt: x) +func invalidDiffWrtExistential(_ x: Proto) -> Proto { + return x +} + +// expected-error @+1 {{can only differentiate with respect to parameters that conform to 'Differentiable', but '@differentiable (Float) -> Float' does not conform to 'Differentiable'}} +@differentiable(wrt: fn) +func invalidDiffWrtFunction(_ fn: @differentiable(Float) -> Float) -> Float { + return fn(.pi) +} + +// expected-error @+1 {{'invalidDiffNoParams()' has no parameters to differentiate with respect to}} +@differentiable +func invalidDiffNoParams() -> Float { + return 1 +} + +// expected-error @+1 {{cannot differentiate void function 'invalidDiffVoidResult(x:)'}} +@differentiable +func invalidDiffVoidResult(x: Float) {} + +// Test static methods. +struct StaticMethod { + // expected-error @+1 {{'invalidDiffNoParams()' has no parameters to differentiate with respect to}} + @differentiable + static func invalidDiffNoParams() -> Float { + return 1 + } + + // expected-error @+1 {{cannot differentiate void function 'invalidDiffVoidResult(x:)'}} + @differentiable + static func invalidDiffVoidResult(x: Float) {} +} + +// Test instance methods. +struct InstanceMethod { + // expected-error @+1 {{'invalidDiffNoParams()' has no parameters to differentiate with respect to}} + @differentiable + func invalidDiffNoParams() -> Float { + return 1 + } + + // expected-error @+1 {{cannot differentiate void function 'invalidDiffVoidResult(x:)'}} + @differentiable + func invalidDiffVoidResult(x: Float) {} +} + +// Test instance methods for a `Differentiable` type. +struct DifferentiableInstanceMethod: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + @differentiable // ok + func noParams() -> Float { + return 1 + } +} + +// Test subscript methods. +struct SubscriptMethod { + @differentiable // ok + subscript(implicitGetter x: Float) -> Float { + return x + } + + @differentiable // ok + subscript(implicitGetterSetter x: Float) -> Float { + get { return x } + set {} + } + + subscript(explicit x: Float) -> Float { + @differentiable // ok + get { return x } + @differentiable // expected-error {{'@differentiable' attribute cannot be applied to this declaration}} + set {} + } + + subscript(x: Float, y: Float) -> Float { + @differentiable // ok + get { return x + y } + @differentiable // expected-error {{'@differentiable' attribute cannot be applied to this declaration}} + set {} + } +} + +// JVP + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(jvp: jvpSimpleJVP) +func jvpSimple(x: Float) -> Float { + return x +} + +func jvpSimpleJVP(x: Float) -> (Float, ((Float) -> Float)) { + return (x, { v in v }) +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: y, jvp: jvpWrtSubsetJVP) +func jvpWrtSubset1(x: Float, y: Float) -> Float { + return x + y +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: (y), jvp: jvpWrtSubsetJVP) +func jvpWrtSubset2(x: Float, y: Float) -> Float { + return x + y +} + +func jvpWrtSubsetJVP(x: Float, y: Float) -> (Float, (Float) -> Float) { + return (x + y, { v in v }) +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(jvp: jvp2ParamsJVP) +func jvp2Params(x: Float, y: Float) -> Float { + return x + y +} + +func jvp2ParamsJVP(x: Float, y: Float) -> (Float, (Float, Float) -> Float) { + return (x + y, { (a, b) in a + b }) +} + +// expected-error @+1 {{unknown parameter name 'y'}} +@differentiable(wrt: (y)) +func jvpUnknownParam(x: Float) -> Float { + return x +} + +// expected-error @+1 {{parameters must be specified in original order}} +@differentiable(wrt: (y, x)) +func jvpParamOrderNotIncreasing(x: Float, y: Float) -> Float { + return x * y +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{'jvpWrongTypeJVP' does not have expected type '(Float) -> (Float, (Float.TangentVector) -> Float.TangentVector)' (aka '(Float) -> (Float, (Float) -> Float)'}} +@differentiable(jvp: jvpWrongTypeJVP) +func jvpWrongType(x: Float) -> Float { + return x +} + +func jvpWrongTypeJVP(x: Float) -> (Float, (Float) -> Int) { + return (x, { v in Int(v) }) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{no differentiation parameters could be inferred; must differentiate with respect to at least one parameter conforming to 'Differentiable'}} +@differentiable(jvp: jvpSimpleJVP) +func jvpNonDiffParam(x: Int) -> Float { + return Float(x) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{can only differentiate functions with results that conform to 'Differentiable', but 'Int' does not conform to 'Differentiable'}} +@differentiable(jvp: jvpSimpleJVP) +func jvpNonDiffResult(x: Float) -> Int { + return Int(x) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{can only differentiate functions with results that conform to 'Differentiable', but '(Float, Int)' does not conform to 'Differentiable'}} +@differentiable(jvp: jvpSimpleJVP) +func jvpNonDiffResult2(x: Float) -> (Float, Int) { + return (x, Int(x)) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{ambiguous reference to 'jvpAmbiguousVJP' in '@differentiable' attribute}} +@differentiable(jvp: jvpAmbiguousVJP) +func jvpAmbiguous(x: Float) -> Float { + return x +} +func jvpAmbiguousVJP(_ x: Float) -> (Float, (Float) -> Float) { + return (x, { $0 }) +} +func jvpAmbiguousVJP(x: Float) -> (Float, (Float) -> Float) { + return (x, { $0 }) +} + +class DifferentiableClassMethod { + // Direct differentiation case. + @differentiable + func foo(_ x: Float) -> Float { + return x + } +} + +struct JVPStruct { + @differentiable + let p: Float + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'funcJVP' does not have expected type '(JVPStruct) -> () -> (Double, (JVPStruct.TangentVector) -> Double.TangentVector)' (aka '(JVPStruct) -> () -> (Double, (JVPStruct) -> Double)'}} + @differentiable(wrt: (self), jvp: funcJVP) + func funcWrongType() -> Double { + fatalError("unimplemented") + } +} + +extension JVPStruct { + func funcJVP() -> (Float, (JVPStruct) -> Float) { + fatalError("unimplemented") + } +} + +extension JVPStruct: AdditiveArithmetic { + static var zero: JVPStruct { fatalError("unimplemented") } + static func + (lhs: JVPStruct, rhs: JVPStruct) -> JVPStruct { + fatalError("unimplemented") + } + static func - (lhs: JVPStruct, rhs: JVPStruct) -> JVPStruct { + fatalError("unimplemented") + } + typealias Scalar = Float + static func * (lhs: Float, rhs: JVPStruct) -> JVPStruct { + fatalError("unimplemented") + } +} + +extension JVPStruct: Differentiable { + typealias TangentVector = JVPStruct +} + +extension JVPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: x, jvp: wrtAllNonSelfJVP) + func wrtAllNonSelf(x: Float) -> Float { + return x + p + } + + func wrtAllNonSelfJVP(x: Float) -> (Float, (Float) -> Float) { + return (x + p, { v in v }) + } +} + +extension JVPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: (self, x), jvp: wrtAllJVP) + func wrtAll(x: Float) -> Float { + return x + p + } + + func wrtAllJVP(x: Float) -> (Float, (JVPStruct, Float) -> Float) { + return (x + p, { (a, b) in a.p + b }) + } +} + +extension JVPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(jvp: computedPropJVP) + var computedPropOk1: Float { + return 0 + } + + var computedPropOk2: Float { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(jvp: computedPropJVP) + get { + return 0 + } + } + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'computedPropJVP' does not have expected type '(JVPStruct) -> () -> (Double, (JVPStruct.TangentVector) -> Double.TangentVector)' (aka '(JVPStruct) -> () -> (Double, (JVPStruct) -> Double)'}} + @differentiable(jvp: computedPropJVP) + var computedPropWrongType: Double { + return 0 + } + + var computedPropWrongAccessor: Float { + get { + return 0 + } + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'@differentiable' attribute cannot be applied to this declaration}} + @differentiable(jvp: computedPropJVP) + set { + fatalError("unimplemented") + } + } + + func computedPropJVP() -> (Float, (JVPStruct) -> Float) { + fatalError("unimplemented") + } +} + +// VJP + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: vjpSimpleVJP) +func vjpSimple(x: Float) -> Float { + return x +} + +func vjpSimpleVJP(x: Float) -> (Float, ((Float) -> Float)) { + return (x, { v in v }) +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(wrt: (y), vjp: vjpWrtSubsetVJP) +func vjpWrtSubset(x: Float, y: Float) -> Float { + return x + y +} + +func vjpWrtSubsetVJP(x: Float, y: Float) -> (Float, (Float) -> Float) { + return (x + y, { v in v }) +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(vjp: vjp2ParamsVJP) +func vjp2Params(x: Float, y: Float) -> Float { + return x + y +} + +func vjp2ParamsVJP(x: Float, y: Float) -> (Float, (Float) -> (Float, Float)) { + return (x + y, { v in (v, v) }) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{'vjpWrongTypeVJP' does not have expected type '(Float) -> (Float, (Float.TangentVector) -> Float.TangentVector)' (aka '(Float) -> (Float, (Float) -> Float)'}} +@differentiable(vjp: vjpWrongTypeVJP) +func vjpWrongType(x: Float) -> Float { + return x +} + +func vjpWrongTypeVJP(x: Float) -> (Float, (Float) -> Int) { + return (x, { v in Int(v) }) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{no differentiation parameters could be inferred; must differentiate with respect to at least one parameter conforming to 'Differentiable'}} +@differentiable(vjp: vjpSimpleVJP) +func vjpNonDiffParam(x: Int) -> Float { + return Float(x) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{can only differentiate functions with results that conform to 'Differentiable', but 'Int' does not conform to 'Differentiable'}} +@differentiable(vjp: vjpSimpleVJP) +func vjpNonDiffResult(x: Float) -> Int { + return Int(x) +} + +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{can only differentiate functions with results that conform to 'Differentiable', but '(Float, Int)' does not conform to 'Differentiable'}} +@differentiable(vjp: vjpSimpleVJP) +func vjpNonDiffResult2(x: Float) -> (Float, Int) { + return (x, Int(x)) +} + +struct VJPStruct { + let p: Float + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'funcVJP' does not have expected type '(VJPStruct) -> () -> (Double, (Double.TangentVector) -> VJPStruct.TangentVector)' (aka '(VJPStruct) -> () -> (Double, (Double) -> VJPStruct)'}} + @differentiable(vjp: funcVJP) + func funcWrongType() -> Double { + fatalError("unimplemented") + } +} + +extension VJPStruct { + func funcVJP() -> (Float, (Float) -> VJPStruct) { + fatalError("unimplemented") + } +} + +extension VJPStruct: AdditiveArithmetic { + static var zero: VJPStruct { fatalError("unimplemented") } + static func + (lhs: VJPStruct, rhs: VJPStruct) -> VJPStruct { + fatalError("unimplemented") + } + static func - (lhs: VJPStruct, rhs: VJPStruct) -> VJPStruct { + fatalError("unimplemented") + } + typealias Scalar = Float + static func * (lhs: Float, rhs: VJPStruct) -> VJPStruct { + fatalError("unimplemented") + } +} + +extension VJPStruct: Differentiable { + typealias TangentVector = VJPStruct +} + +extension VJPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: x, vjp: wrtAllNonSelfVJP) + func wrtAllNonSelf(x: Float) -> Float { + return x + p + } + + func wrtAllNonSelfVJP(x: Float) -> (Float, (Float) -> Float) { + return (x + p, { v in v }) + } +} + +extension VJPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: (self, x), vjp: wrtAllVJP) + func wrtAll(x: Float) -> Float { + return x + p + } + + func wrtAllVJP(x: Float) -> (Float, (Float) -> (VJPStruct, Float)) { + fatalError("unimplemented") + } +} + +extension VJPStruct { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(vjp: computedPropVJP) + var computedPropOk1: Float { + return 0 + } + + var computedPropOk2: Float { + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(vjp: computedPropVJP) + get { + return 0 + } + } + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'computedPropVJP' does not have expected type '(VJPStruct) -> () -> (Double, (Double.TangentVector) -> VJPStruct.TangentVector)' (aka '(VJPStruct) -> () -> (Double, (Double) -> VJPStruct)'}} + @differentiable(vjp: computedPropVJP) + var computedPropWrongType: Double { + return 0 + } + + var computedPropWrongAccessor: Float { + get { + return 0 + } + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'@differentiable' attribute cannot be applied to this declaration}} + @differentiable(vjp: computedPropVJP) + set { + fatalError("unimplemented") + } + } + + func computedPropVJP() -> (Float, (Float) -> VJPStruct) { + fatalError("unimplemented") + } +} + +// expected-error @+2 {{empty 'where' clause in '@differentiable' attribute}} +// expected-error @+1 {{expected type}} +@differentiable(where) +func emptyWhereClause(x: T) -> T { + return x +} + +// expected-error @+1 {{'where' clause is valid only when original function is generic 'nongenericWhereClause(x:)'}} +@differentiable(where T: Differentiable) +func nongenericWhereClause(x: Float) -> Float { + return x +} + +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(jvp: jvpWhere1, vjp: vjpWhere1 where T: Differentiable) +func where1(x: T) -> T { + return x +} +func jvpWhere1(x: T) -> (T, (T.TangentVector) -> T.TangentVector) { + return (x, { v in v }) +} +func vjpWhere1(x: T) -> (T, (T.TangentVector) -> T.TangentVector) { + return (x, { v in v }) +} + +// Test derivative functions with result tuple type labels. +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(jvp: jvpResultLabels, vjp: vjpResultLabels) +func derivativeResultLabels(_ x: Float) -> Float { + return x +} +func jvpResultLabels(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + return (x, { $0 }) +} +func vjpResultLabels(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) +} +struct ResultLabelTest { + // expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(jvp: jvpResultLabels, vjp: vjpResultLabels) + static func derivativeResultLabels(_ x: Float) -> Float { + return x + } + static func jvpResultLabels(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + return (x, { $0 }) + } + static func vjpResultLabels(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) + } + + // expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(jvp: jvpResultLabels, vjp: vjpResultLabels) + func derivativeResultLabels(_ x: Float) -> Float { + return x + } + func jvpResultLabels(_ x: Float) -> (value: Float, differential: (Float) -> Float) { + return (x, { $0 }) + } + func vjpResultLabels(_ x: Float) -> (value: Float, pullback: (Float) -> Float) { + return (x, { $0 }) + } +} + +struct Tensor: AdditiveArithmetic { + static var zero: Self { Self() } + static func + (_: Self, _: Self) -> Self { Self() } + static func - (_: Self, _: Self) -> Self { Self() } +} +extension Tensor: Differentiable where Scalar: Differentiable { + typealias TangentVector = Self +} +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(jvp: jvpWhere2, vjp: vjpWhere2 where Scalar: Differentiable) +func where2(x: Tensor) -> Tensor { + return x +} +func jvpWhere2(x: Tensor) -> (Tensor, (Tensor) -> Tensor) { + return (x, { v in v }) +} +func vjpWhere2(x: Tensor) -> (Tensor, (Tensor) -> Tensor) { + return (x, { v in v }) +} + +struct A { + struct B { + @differentiable(wrt: x where T: Differentiable, V: Differentiable, V.TangentVector == V) + func whereInGenericContext(x: T) -> T { + return x + } + } +} + +extension FloatingPoint { + @differentiable(wrt: (self) where Self: Differentiable) + func whereClauseExtension() -> Self { + return self + } +} +// expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +// expected-error @+1 {{'vjpNonvariadic' does not have expected type '(Float, Int32...) -> (Float, (Float.TangentVector) -> Float.TangentVector)' (aka '(Float, Int32...) -> (Float, (Float) -> Float)')}} +@differentiable(wrt: x, vjp: vjpNonvariadic) +func variadic(_ x: Float, indices: Int32...) -> Float { + return x +} +func vjpNonvariadic(_ x: Float, indices: [Int32]) -> (Float, (Float) -> Float) { + return (x, { $0 }) +} + +// expected-error @+3 {{type 'Scalar' constrained to non-protocol, non-class type 'Float'}} +// expected-error @+2 {{no differentiation parameters could be inferred; must differentiate with respect to at least one parameter conforming to 'Differentiable'}} +// expected-note @+1 {{use 'Scalar == Float' to require 'Scalar' to be 'Float'}} +@differentiable(where Scalar: Float) +func invalidRequirementConformance(x: Scalar) -> Scalar { + return x +} + +@differentiable(where T: AnyObject) +func invalidAnyObjectRequirement(x: T) -> T { + return x +} + +// expected-error @+1 {{'@differentiable' attribute does not yet support layout requirements}} +@differentiable(where Scalar: _Trivial) +func invalidRequirementLayout(x: Scalar) -> Scalar { + return x +} + +// expected-error @+1 {{no differentiation parameters could be inferred; must differentiate with respect to at least one parameter conforming to 'Differentiable'}} +@differentiable +func missingConformance(_ x: T) -> T { + return x +} + +protocol ProtocolRequirements: Differentiable { + // expected-note @+2 {{protocol requires initializer 'init(x:y:)' with type '(x: Float, y: Float)'}} + @differentiable + init(x: Float, y: Float) + + // expected-note @+2 {{protocol requires initializer 'init(x:y:)' with type '(x: Float, y: Int)'}} + @differentiable(wrt: x) + init(x: Float, y: Int) + + // expected-note @+2 {{protocol requires function 'amb(x:y:)' with type '(Float, Float) -> Float';}} + @differentiable + func amb(x: Float, y: Float) -> Float + + // expected-note @+2 {{protocol requires function 'amb(x:y:)' with type '(Float, Int) -> Float';}} + @differentiable(wrt: x) + func amb(x: Float, y: Int) -> Float + + // expected-note @+3 {{protocol requires function 'f1'}} + // expected-note @+2 {{overridden declaration is here}} + @differentiable(wrt: (self, x)) + func f1(_ x: Float) -> Float + + // expected-note @+2 {{protocol requires function 'f2'}} + @differentiable(wrt: (self, x, y)) + func f2(_ x: Float, _ y: Float) -> Float +} + +protocol ProtocolRequirementsRefined: ProtocolRequirements { + // expected-error @+1 {{overriding declaration is missing attribute '@differentiable'}} {{3-3=@differentiable }} + func f1(_ x: Float) -> Float +} + +// expected-error @+1 {{does not conform to protocol 'ProtocolRequirements'}} +struct DiffAttrConformanceErrors: ProtocolRequirements { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + var x: Float + var y: Float + + // FIXME(TF-284): Fix unexpected diagnostic. + // expected-note @+2 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + // expected-note @+1 {{candidate has non-matching type '(x: Float, y: Float)'}} + init(x: Float, y: Float) { + self.x = x + self.y = y + } + + // FIXME(TF-284): Fix unexpected diagnostic. + // expected-note @+2 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + // expected-note @+1 {{candidate has non-matching type '(x: Float, y: Int)'}} + init(x: Float, y: Int) { + self.x = x + self.y = Float(y) + } + + // expected-note @+2 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + // expected-note @+1 {{candidate has non-matching type '(Float, Float) -> Float'}} + func amb(x: Float, y: Float) -> Float { + return x + } + + // expected-note @+2 {{candidate is missing attribute '@differentiable(wrt: x)'}} {{3-3=@differentiable(wrt: x) }} + // expected-note @+1 {{candidate has non-matching type '(Float, Int) -> Float'}} + func amb(x: Float, y: Int) -> Float { + return x + } + + // expected-note @+1 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + func f1(_ x: Float) -> Float { + return x + } + + // expected-note @+2 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + @differentiable(wrt: (self, x)) + func f2(_ x: Float, _ y: Float) -> Float { + return x + y + } +} + +protocol ProtocolRequirementsWithDefault_NoConformingTypes { + @differentiable + func f1(_ x: Float) -> Float +} +extension ProtocolRequirementsWithDefault_NoConformingTypes { + // TODO(TF-650): It would be nice to diagnose protocol default implementation + // with missing `@differentiable` attribute. + func f1(_ x: Float) -> Float { x } +} + +protocol ProtocolRequirementsWithDefault { + // expected-note @+2 {{protocol requires function 'f1'}} + @differentiable + func f1(_ x: Float) -> Float +} +extension ProtocolRequirementsWithDefault { + // expected-note @+1 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + func f1(_ x: Float) -> Float { x } +} +// expected-error @+1 {{type 'DiffAttrConformanceErrors2' does not conform to protocol 'ProtocolRequirementsWithDefault'}} +struct DiffAttrConformanceErrors2: ProtocolRequirementsWithDefault { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + // expected-note @+1 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + func f1(_ x: Float) -> Float { x } +} + +protocol NotRefiningDiffable { + @differentiable(wrt: x) + // expected-note @+1 {{protocol requires function 'a' with type '(Float) -> Float'; do you want to add a stub?}} + func a(_ x: Float) -> Float +} + +// expected-error @+1 {{type 'CertainlyNotDiffableWrtSelf' does not conform to protocol 'NotRefiningDiffable'}} +struct CertainlyNotDiffableWrtSelf: NotRefiningDiffable { + // expected-note @+1 {{candidate is missing attribute '@differentiable'}} {{3-3=@differentiable }} + func a(_ x: Float) -> Float { return x * 5.0 } +} + + +protocol TF285: Differentiable { + @differentiable(wrt: (x, y)) + @differentiable(wrt: x) + // expected-note @+1 {{protocol requires function 'foo(x:y:)' with type '(Float, Float) -> Float'; do you want to add a stub?}} + func foo(x: Float, y: Float) -> Float +} + +// expected-error @+1 {{type 'TF285MissingOneDiffAttr' does not conform to protocol 'TF285'}} +struct TF285MissingOneDiffAttr: TF285 { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + // Requirement is missing an attribute. + @differentiable(wrt: x) + // expected-note @+1 {{candidate is missing attribute '@differentiable(wrt: (x, y))}} {{3-3=@differentiable(wrt: (x, y)) }} + func foo(x: Float, y: Float) -> Float { + return x + } +} + +// TF-521: Test invalid `@differentiable` attribute due to invalid +// `Differentiable` conformance (`TangentVector` does not conform to +// `AdditiveArithmetic`). +struct TF_521 { + var real: T + var imaginary: T + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{can only differentiate functions with results that conform to 'Differentiable', but 'TF_521' does not conform to 'Differentiable'}} + @differentiable(vjp: _vjpInit where T: Differentiable, T == T.TangentVector) + init(real: T = 0, imaginary: T = 0) { + self.real = real + self.imaginary = imaginary + } +} +// expected-error @+1 {{type 'TF_521' does not conform to protocol 'Differentiable'}} +extension TF_521: Differentiable where T: Differentiable { + // expected-note @+1 {{possibly intended match 'TF_521.TangentVector' does not conform to 'AdditiveArithmetic'}} + typealias TangentVector = TF_521 + typealias AllDifferentiableVariables = TF_521 +} +extension TF_521 where T: Differentiable, T == T.TangentVector { + static func _vjpInit(real: T, imaginary: T) -> (TF_521, (TF_521) -> (T, T)) { + return (TF_521(real: real, imaginary: imaginary), { ($0.real, $0.imaginary) }) + } +} +let _: @differentiable (Float, Float) -> TF_521 = { r, i in + TF_521(real: r, imaginary: i) +} + +// TF-296: Infer `@differentiable` wrt parameters to be to all parameters that conform to `Differentiable`. + +@differentiable +func infer1(_ a: Float, _ b: Int) -> Float { + return a + Float(b) +} + +@differentiable +func infer2(_ fn: @differentiable(Float) -> Float, x: Float) -> Float { + return fn(x) +} + +struct DiffableStruct: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + var a: Float + + @differentiable + func fn(_ b: Float, _ c: Int) -> Float { + return a + b + Float(c) + } +} + +struct NonDiffableStruct { + var a: Float + + @differentiable + func fn(_ b: Float) -> Float { + return a + b + } +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(linear, wrt: x, vjp: const3) // expected-error {{cannot specify 'vjp:' or 'jvp:' for linear functions; use '@transpose' attribute for transpose registration instead}} +func slope1(_ x: Float) -> Float { + return 3 * x +} + +// expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(linear, wrt: x, jvp: const3) // expected-error {{cannot specify 'vjp:' or 'jvp:' for linear functions; use '@transpose' attribute for transpose registration instead}} +func slope2(_ x: Float) -> Float { + return 3 * x +} + +// expected-warning @+1 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} +@differentiable(linear, jvp: const3, vjp: const3) // expected-error {{cannot specify 'vjp:' or 'jvp:' for linear functions; use '@transpose' attribute for transpose registration instead}} +func slope3(_ x: Float) -> Float { + return 3 * x +} + +// Check that `@differentiable` attribute rejects stored properties. +struct StoredProperty: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'@differentiable' attribute on stored property cannot specify 'jvp:' or 'vjp:'}} + @differentiable(vjp: vjpStored) + var stored: Float + + func vjpStored() -> (Float, (Float) -> TangentVector) { + (stored, { _ in .zero }) + } +} + +// Check that `@differentiable` attribute rejects non-`func` derivatives. +struct Struct: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{registered derivative 'computedPropertyVJP' must be a 'func' declaration}} + @differentiable(vjp: computedPropertyVJP) + func testComputedProperty() -> Float { 1 } + var computedPropertyVJP: (Float, (Float) -> TangentVector) { + (1, { _ in .zero }) + } + + // expected-error @+1 {{expected a vjp function name}} + @differentiable(vjp: init) + func testInitializer() -> Struct { self } + init(_ x: Struct) {} + + // expected-error @+1 {{expected a vjp function name}} + @differentiable(vjp: subscript) + func testSubscript() -> Float { 1 } + subscript() -> (Float, (Float) -> TangentVector) { + (1, { _ in .zero }) + } +} + +// Index based 'wrt:' + +struct NumberWrtStruct: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + var a, b: Float + + @differentiable(wrt: 0) // ok + @differentiable(wrt: 1) // ok + func foo1(_ x: Float, _ y: Float) -> Float { + return a*x + b*y + } + + @differentiable(wrt: -1) // expected-error {{expected a parameter, which can be a function parameter name, parameter index, or 'self'}} + @differentiable(wrt: (1, x)) // expected-error {{parameters must be specified in original order}} + func foo2(_ x: Float, _ y: Float) -> Float { + return a*x + b*y + } + + @differentiable(wrt: (x, 1)) // ok + @differentiable(wrt: (0)) // ok + static func staticFoo1(_ x: Float, _ y: Float) -> Float { + return x + y + } + + @differentiable(wrt: (1, 1)) // expected-error {{parameters must be specified in original order}} + @differentiable(wrt: (2)) // expected-error {{parameter index is larger than total number of parameters}} + static func staticFoo2(_ x: Float, _ y: Float) -> Float { + return x + y + } +} + +@differentiable(wrt: y) // ok +func two1(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, y)) // ok +func two2(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, y)) // ok +func two3(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (x, 1)) // ok +func two4(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (0, 1)) // ok +func two5(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: 2) // expected-error {{parameter index is larger than total number of parameters}} +func two6(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (1, 0)) // expected-error {{parameters must be specified in original order}} +func two7(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (1, x)) // expected-error {{parameters must be specified in original order}} +func two8(x: Float, y: Float) -> Float { + return x + y +} + +@differentiable(wrt: (y, 0)) // expected-error {{parameters must be specified in original order}} +func two9(x: Float, y: Float) -> Float { + return x + y +} + +// Inout 'wrt:' arguments. + +@differentiable(wrt: y) // expected-error {{cannot differentiate void function 'inout1(x:y:)'}} +func inout1(x: Float, y: inout Float) -> Void { + let _ = x + y +} + +@differentiable(wrt: y) // expected-error {{cannot differentiate with respect to 'inout' parameter ('inout Float')}} +func inout2(x: Float, y: inout Float) -> Float { + let _ = x + y +} + +// Test refining protocol requirements with `@differentiable` attribute. + +public protocol Distribution { + associatedtype Value + func logProbability(of value: Value) -> Float +} + +public protocol DifferentiableDistribution: Differentiable, Distribution { + // expected-note @+2 {{overridden declaration is here}} + @differentiable(wrt: self) + func logProbability(of value: Value) -> Float +} + +// Adding a more general `@differentiable` attribute. +public protocol DoubleDifferentiableDistribution: DifferentiableDistribution + where Value: Differentiable { + // expected-error @+1 {{overriding declaration is missing attribute '@differentiable(wrt: self)'}} {{3-3=@differentiable(wrt: self) }} + func logProbability(of value: Value) -> Float +} + +// Test protocol requirement `@differentiable` attribute unsupported features. + +protocol ProtocolRequirementUnsupported: Differentiable { + associatedtype Scalar + + // expected-error @+1 {{'@differentiable' attribute on protocol requirement cannot specify 'where' clause}} + @differentiable(where Scalar: Differentiable) + func unsupportedWhereClause(value: Scalar) -> Float + + // expected-warning @+2 2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'@differentiable' attribute on protocol requirement cannot specify 'jvp:' or 'vjp:'}} + @differentiable(wrt: x, jvp: dfoo, vjp: dfoo) + func unsupportedDerivatives(_ x: Float) -> Float +} +extension ProtocolRequirementUnsupported { + func dfoo(_ x: Float) -> (Float, (Float) -> Float) { + (x, { $0 }) + } +} + +// Classes. + +class Super: Differentiable { + typealias TangentVector = DummyTangentVector + func move(along _: TangentVector) {} + + var base: Float + + // NOTE(TF-654): Class initializers are not yet supported. + // expected-error @+1 {{'@differentiable' attribute does not yet support class initializers}} + @differentiable + init(base: Float) { + self.base = base + } + + // NOTE(TF-1040): `@differentiable` attribute on class methods currently + // does two orthogonal things: + // - Requests derivative generation for the class method. + // - Adds JVP/VJP vtable entries for the class method. + // There's currently no way using `@differentiable` to do only one of the + // above. + @differentiable + func testClassMethod(_ x: Float) -> Float { x } + + @differentiable + final func testFinalMethod(_ x: Float) -> Float { x } + + @differentiable + static func testStaticMethod(_ x: Float) -> Float { x } + + @differentiable(wrt: (self, x)) + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: x, vjp: vjp) + // expected-note @+1 2 {{overridden declaration is here}} + func testMissingAttributes(_ x: Float) -> Float { x } + + // expected-warning @+1 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + @differentiable(wrt: x, vjp: vjp) + func testSuperclassDerivatives(_ x: Float) -> Float { x } + + final func vjp(_ x: Float) -> (Float, (Float) -> Float) { + fatalError() + } + + // Test duplicate attributes with different derivative generic signatures. + // expected-error @+1 {{duplicate '@differentiable' attribute with same parameters}} + @differentiable(wrt: x where T: Differentiable) + // expected-note @+1 {{other attribute declared here}} + @differentiable(wrt: x) + func instanceMethod(_ x: Float, y: T) -> Float { x } + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'@differentiable' attribute cannot be declared on class methods returning 'Self'}} + @differentiable(vjp: vjpDynamicSelfResult) + func dynamicSelfResult() -> Self { self } + + // TODO(TF-632): Fix "'TangentVector' is not a member type of 'Self'" diagnostic. + // The underlying error should appear instead: + // "covariant 'Self' can only appear at the top level of method result type". + // expected-error @+1 2 {{'TangentVector' is not a member type of 'Self'}} + func vjpDynamicSelfResult() -> (Self, (Self.TangentVector) -> Self.TangentVector) { + return (self, { $0 }) + } +} + +class Sub: Super { + // expected-error @+2 {{overriding declaration is missing attribute '@differentiable(wrt: x)'}} {{12-12=@differentiable(wrt: x) }} + // expected-error @+1 {{overriding declaration is missing attribute '@differentiable'}} {{12-12=@differentiable }} + override func testMissingAttributes(_ x: Float) -> Float { x } + + // expected-warning @+2 {{'jvp:' and 'vjp:' arguments in '@differentiable' attribute are deprecated}} + // expected-error @+1 {{'vjp' is not defined in the current type context}} + @differentiable(wrt: x, vjp: vjp) + override func testSuperclassDerivatives(_ x: Float) -> Float { x } +} + +// Test unsupported accessors: `set`, `_read`, `_modify`. + +struct UnsupportedAccessors: Differentiable { + typealias TangentVector = DummyTangentVector + mutating func move(along _: TangentVector) {} + + var stored: Float + var computed: Float { + // `set` has an `inout` parameter: `(inout Self) -> (Float) -> ()`. + // expected-error @+1 {{'@differentiable' attribute cannot be applied to this declaration}} + @differentiable + set { stored = newValue } + + // `_read` is a coroutine: `(Self) -> () -> ()`. + // expected-error @+1 {{'@differentiable' attribute cannot be applied to this declaration}} + @differentiable + _read { yield stored } + + // `_modify` is a coroutine: `(inout Self) -> () -> ()`. + // expected-error @+1 {{'@differentiable' attribute cannot be applied to this declaration}} + @differentiable + _modify { yield &stored } + } +} diff --git a/test/AutoDiff/Sema/differentiable_features_disabled.swift b/test/AutoDiff/Sema/differentiable_features_disabled.swift new file mode 100644 index 0000000000000..1ba3ec05e8a80 --- /dev/null +++ b/test/AutoDiff/Sema/differentiable_features_disabled.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @differentiable (Float) -> Float + +// expected-error @+2 {{differentiable programming is an experimental feature that is currently disabled}} +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @differentiable (Float, @noDerivative Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: (Float, @noDerivative Float) -> Float + +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +let _: @noDerivative Float + +func id(_ x: Float) -> Float { + return x +} +// expected-error @+1 {{differentiable programming is an experimental feature that is currently disabled}} +@derivative(of: id) +func jvpId(x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} diff --git a/test/AutoDiff/Sema/differentiable_func_type.swift b/test/AutoDiff/Sema/differentiable_func_type.swift new file mode 100644 index 0000000000000..0515dcc95d027 --- /dev/null +++ b/test/AutoDiff/Sema/differentiable_func_type.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -enable-experimental-differentiable-programming -typecheck -verify %s + +// expected-error @+1 {{@differentiable attribute only applies to function types}} +let _: @differentiable Float + +let _: @differentiable (Float) -> Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (Float) -> @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: @differentiable (Float) -> @noDerivative Float + +// expected-error @+1 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +let _: (@noDerivative Float) -> Float + +// expected-error @+2 {{'@noDerivative' may only be used on parameters of '@differentiable' function types}} +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: (Float, @noDerivative Float...) -> Float + +let _: @differentiable (@noDerivative Float, Float) -> Float + +// expected-error @+1 {{'@noDerivative' must not be used on variadic parameters}} +let _: @differentiable (Float, @noDerivative Float...) -> Float diff --git a/test/AutoDiff/Serialization/derivative_attr.swift b/test/AutoDiff/Serialization/derivative_attr.swift new file mode 100644 index 0000000000000..8b306740424c1 --- /dev/null +++ b/test/AutoDiff/Serialization/derivative_attr.swift @@ -0,0 +1,108 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-experimental-differentiable-programming %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/derivative_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -enable-experimental-differentiable-programming -disable-sil-linking -enable-sil-verify-all %t/derivative_attr.swiftmodule -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode + +// REQUIRES: differentiable_programming + +import _Differentiation + +// Dummy `Differentiable`-conforming type. +struct S: Differentiable & AdditiveArithmetic { + static var zero: S { S() } + static func + (_: S, _: S) -> S { S() } + static func - (_: S, _: S) -> S { S() } + typealias TangentVector = S +} + +// Test top-level functions. + +func top1(_ x: S) -> S { + x +} +// CHECK: @derivative(of: top1, wrt: x) +@derivative(of: top1, wrt: x) +func derivativeTop1(_ x: S) -> (value: S, differential: (S) -> S) { + (x, { $0 }) +} + +func top2(_ x: T, _ i: Int, _ y: U) -> U { + y +} +// CHECK: @derivative(of: top2, wrt: (x, y)) +@derivative(of: top2, wrt: (x, y)) +func derivativeTop2( + _ x: T, _ i: Int, _ y: U +) -> (value: U, differential: (T.TangentVector, U.TangentVector) -> U.TangentVector) { + (y, { (dx, dy) in dy }) +} + +// Test instance methods. + +extension S { + func instanceMethod(_ x: S) -> S { + self + } + + // CHECK: @derivative(of: instanceMethod, wrt: x) + @derivative(of: instanceMethod, wrt: x) + func derivativeInstanceMethodWrtX(_ x: S) -> (value: S, differential: (S) -> S) { + (self, { _ in .zero }) + } + + // CHECK: @derivative(of: instanceMethod, wrt: self) + @derivative(of: instanceMethod, wrt: self) + func derivativeInstanceMethodWrtSelf(_ x: S) -> (value: S, differential: (S) -> S) { + (self, { $0 }) + } + + // CHECK: @derivative(of: instanceMethod, wrt: (self, x)) + @derivative(of: instanceMethod, wrt: (self, x)) + func derivativeInstanceMethodWrtAll(_ x: S) -> (value: S, differential: (S, S) -> S) { + (self, { (dself, dx) in self }) + } +} + +// Test static methods. + +extension S { + static func staticMethod(_ x: S) -> S { + x + } + + // CHECK: @derivative(of: staticMethod, wrt: x) + @derivative(of: staticMethod, wrt: x) + static func derivativeStaticMethod(_ x: S) -> (value: S, differential: (S) -> S) { + (x, { $0 }) + } +} + +// Test computed properties. + +extension S { + var computedProperty: S { + self + } + + // CHECK: @derivative(of: computedProperty, wrt: self) + @derivative(of: computedProperty, wrt: self) + func derivativeProperty() -> (value: S, differential: (S) -> S) { + (self, { $0 }) + } +} + +// Test subscripts. + +extension S { + subscript(x: T) -> S { + self + } + + // CHECK: @derivative(of: subscript, wrt: self) + @derivative(of: subscript(_:), wrt: self) + func derivativeSubscript(x: T) -> (value: S, differential: (S) -> S) { + (self, { $0 }) + } +} diff --git a/test/AutoDiff/Serialization/differentiable_attr.swift b/test/AutoDiff/Serialization/differentiable_attr.swift new file mode 100644 index 0000000000000..c1dcdfacfc5b2 --- /dev/null +++ b/test/AutoDiff/Serialization/differentiable_attr.swift @@ -0,0 +1,127 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/differentiable_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiable_attr.swiftmodule -o - | %FileCheck %s +// REQUIRES: differentiable_programming + +// TODO(TF-836): Enable this test. +// Blocked by TF-828: `@differentiable` attribute type-checking. +// XFAIL: * + +// BCANALYZER-NOT: UnknownCode + +import _Differentiation + +// CHECK: @differentiable(wrt: x, jvp: jvpSimple, vjp: vjpSimple) +// CHECK-NEXT: func simple(x: Float) -> Float +@differentiable(jvp: jvpSimple, vjp: vjpSimple) +func simple(x: Float) -> Float { + return x +} + +// CHECK: @differentiable(linear, wrt: x) +// CHECK-NEXT: func simple2(x: Float) -> Float +@differentiable(linear) +func simple2(x: Float) -> Float { + return x +} + +// CHECK: @differentiable(linear, wrt: x) +// CHECK-NEXT: func simple4(x: Float) -> Float +@differentiable(linear, wrt: x) +func simple4(x: Float) -> Float { + return x +} + +func jvpSimple(x: Float) -> (Float, (Float) -> Float) { + return (x, { v in v }) +} + +func vjpSimple(x: Float) -> (Float, (Float) -> Float) { + return (x, { v in v }) +} + +// CHECK: @differentiable(wrt: x) +// CHECK-NEXT: func testWrtClause(x: Float, y: Float) -> Float +@differentiable(wrt: x) +func testWrtClause(x: Float, y: Float) -> Float { + return x + y +} + +struct InstanceMethod : Differentiable { + // CHECK: @differentiable(wrt: (self, y)) + // CHECK-NEXT: func testWrtClause(x: Float, y: Float) -> Float + @differentiable(wrt: (self, y)) + func testWrtClause(x: Float, y: Float) -> Float { + return x + y + } + + struct TangentVector: Differentiable, AdditiveArithmetic { + typealias TangentVector = Self + static func ==(_: Self, _: Self) -> Bool { fatalError() } + static var zero: Self { fatalError() } + static func +(_: Self, _: Self) -> Self { fatalError() } + static func -(_: Self, _: Self) -> Self { fatalError() } + } + mutating func move(along direction: TangentVector) {} +} + +// CHECK: @differentiable(wrt: x where T : Differentiable) +// CHECK-NEXT: func testOnlyWhereClause(x: T) -> T where T : Numeric +@differentiable(where T : Differentiable) +func testOnlyWhereClause(x: T) -> T { + return x +} + +// CHECK: @differentiable(wrt: x, vjp: vjpTestWhereClause where T : Differentiable) +// CHECK-NEXT: func testWhereClause(x: T) -> T where T : Numeric +@differentiable(vjp: vjpTestWhereClause where T : Differentiable) +func testWhereClause(x: T) -> T { + return x +} +func vjpTestWhereClause(x: T) -> (T, (T.TangentVector) -> T.TangentVector) + where T : Numeric, T : Differentiable +{ + return (x, { v in v }) +} + +protocol P {} +extension P { + // CHECK: @differentiable(wrt: self, vjp: vjpTestWhereClauseMethod where Self : Differentiable) + // CHECK-NEXT: func testWhereClauseMethod() -> Self + @differentiable(wrt: self, vjp: vjpTestWhereClauseMethod where Self : Differentiable) + func testWhereClauseMethod() -> Self { + return self + } +} +extension P where Self : Differentiable { + func vjpTestWhereClauseMethod() -> (Self, (Self.TangentVector) -> Self.TangentVector) { + return (self, { v in v }) + } +} + +// CHECK: @differentiable(wrt: x, vjp: vjpTestWhereClauseMethodTypeConstraint where T : Differentiable, T == T.TangentVector) +// CHECK-NEXT: func testWhereClauseMethodTypeConstraint(x: T) -> T where T : Numeric +@differentiable(vjp: vjpTestWhereClauseMethodTypeConstraint where T : Differentiable, T == T.TangentVector) +func testWhereClauseMethodTypeConstraint(x: T) -> T { + return x +} +func vjpTestWhereClauseMethodTypeConstraint(x: T) -> (T, (T) -> T) + where T : Numeric, T : Differentiable, T == T.TangentVector +{ + return (x, { v in v }) +} + +extension P { + // CHECK: @differentiable(wrt: self, vjp: vjpTestWhereClauseMethodTypeConstraint where Self : Differentiable, Self == Self.TangentVector) + // CHECK-NEXT: func testWhereClauseMethodTypeConstraint() -> Self + @differentiable(wrt: self, vjp: vjpTestWhereClauseMethodTypeConstraint where Self.TangentVector == Self, Self : Differentiable) + func testWhereClauseMethodTypeConstraint() -> Self { + return self + } +} +extension P where Self : Differentiable, Self == Self.TangentVector { + func vjpTestWhereClauseMethodTypeConstraint() -> (Self, (Self.TangentVector) -> Self.TangentVector) { + return (self, { v in v }) + } +} diff --git a/test/AutoDiff/Serialization/differentiation.swift b/test/AutoDiff/Serialization/differentiation.swift new file mode 100644 index 0000000000000..3a17b5440b8a9 --- /dev/null +++ b/test/AutoDiff/Serialization/differentiation.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module -parse-as-library -enable-experimental-differentiable-programming -o %t +// RUN: llvm-bcanalyzer %t/differentiation.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -disable-sil-linking -enable-sil-verify-all %t/differentiation.swiftmodule -enable-experimental-differentiable-programming -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode + +func a(_ f: @differentiable (Float) -> Float) {} +// CHECK: func a(_ f: @differentiable (Float) -> Float) + +func b(_ f: @differentiable(linear) (Float) -> Float) {} +// CHECK: func b(_ f: @differentiable(linear) (Float) -> Float) + +func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) {} +// CHECK: func c(_ f: @differentiable (Float, @noDerivative Float) -> Float) diff --git a/test/AutoDiff/Serialization/transpose_attr.swift b/test/AutoDiff/Serialization/transpose_attr.swift new file mode 100644 index 0000000000000..80bc7a220586b --- /dev/null +++ b/test/AutoDiff/Serialization/transpose_attr.swift @@ -0,0 +1,99 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-experimental-differentiable-programming %s -emit-module -parse-as-library -o %t +// RUN: llvm-bcanalyzer %t/transpose_attr.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER +// RUN: %target-sil-opt -enable-experimental-differentiable-programming -disable-sil-linking -enable-sil-verify-all %t/transpose_attr.swiftmodule -o - | %FileCheck %s + +// BCANALYZER-NOT: UnknownCode +// REQUIRES: differentiable_programming + +// TODO(TF-838): Enable this test. +// Blocked by TF-830: `@transpose` attribute type-checking. +// XFAIL: * + +import _Differentiation + +// Dummy `Differentiable`-conforming type. +struct S: Differentiable & AdditiveArithmetic { + static var zero: S { S() } + static func + (_: S, _: S) -> S { S() } + static func - (_: S, _: S) -> S { S() } + typealias TangentVector = S +} + +// Test top-level functions. + +func top1(_ x: S) -> S { + x +} +// CHECK: @transpose(of: top1, wrt: 0) +@transpose(of: top1, wrt: 0) +func transposeTop1(v: S) -> S { + v +} + +func top2(_ x: T, _ i: Int, _ y: U) -> U { + y +} +// CHECK: @transpose(of: top2, wrt: (0, 2)) +@transpose(of: top2, wrt: (0, 2)) +func transposeTop2(_ int: Int, v: U) -> (T, U) +where T: Differentiable, U: Differentiable, + T == T.TangentVector, U == U.TangentVector { + (.zero, v) +} + +// Test instance methods. + +extension S { + func instanceMethod(_ other: S) -> S { + self + other + } + + // CHECK: @transpose(of: instanceMethod, wrt: 0) + @transpose(of: instanceMethod, wrt: 0) + func transposeInstanceMethod(v: S) -> (S, S) { + (v, v) + } + + // CHECK: @transpose(of: instanceMethod, wrt: self) + @transpose(of: instanceMethod, wrt: self) + func transposeInstanceMethodWrtSelf(v: S) -> (S, S) { + (v, v) + } +} + +// Test static methods. + +extension S { + static func staticMethod(x: S) -> S { + x + } + + // CHECK: @transpose(of: staticMethod, wrt: 0) + @transpose(of: staticMethod, wrt: 0) + func transposeStaticMethod(_: S.Type) -> S { + self + } +} + +// Test computed properties. +extension S { + var computedProperty: S { self } + + // CHECK: @transpose(of: computedProperty, wrt: self) + @transpose(of: computedProperty, wrt: self) + func transposeProperty() -> Self { + self + } +} + +// Test subscripts. +extension S { + subscript(x: T) -> Self { self } + + // CHECK: @transpose(of: subscript, wrt: self) + @transpose(of: subscript(_:), wrt: self) + func transposeSubscript(x: T) -> Self { + self + } +} diff --git a/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds new file mode 100644 index 0000000000000..c49bacacb7633 --- /dev/null +++ b/test/AutoDiff/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -0,0 +1,77 @@ +// RUN: rm -rf %t +// RUN: %swift-syntax-test -input-source-filename %s -parse-gen > %t +// RUN: diff -u %s %t +// RUN: %swift-syntax-test -input-source-filename %s -parse-gen -print-node-kind > %t.withkinds +// RUN: diff -u %S/Outputs/round_trip_parse_gen.swift.withkinds %t.withkinds +// RUN: %swift-syntax-test -input-source-filename %s -eof > %t +// RUN: diff -u %s %t +// RUN: %swift-syntax-test -serialize-raw-tree -input-source-filename %s > %t.dump +// RUN: %swift-syntax-test -deserialize-raw-tree -input-source-filename %t.dump -output-filename %t +// RUN: diff -u %s %t + +// Note: RUN lines copied from test/Syntax/round_trip_parse_gen.swift. + +@differentiable(jvp: foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { return 1 } + +@differentiable(jvp: foo(_:_:) where T : FloatingPoint) +func bar<T : Numeric>(_ x: T, _: T) -> T { return 1 } + +@differentiable(wrt: x, jvp: foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { return 1 } + +@differentiable(wrt: (self, x, y), jvp: foo(_:_:)) +func bar(_ x: Float, y: Float) -> Float { return 1 } + +@differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:) where T : FloatingPoint) +func bar<T : Numeric>(_ x: T, y: T) -> T { return 1 } + +@derivative(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: x) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@derivative(of: A<T>.B<U, V>.C.foo(label:_:), wrt: 0) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} diff --git a/test/AutoDiff/Syntax/round_trip_parse_gen.swift b/test/AutoDiff/Syntax/round_trip_parse_gen.swift new file mode 100644 index 0000000000000..bfde3d5e04cfd --- /dev/null +++ b/test/AutoDiff/Syntax/round_trip_parse_gen.swift @@ -0,0 +1,77 @@ +// RUN: rm -rf %t +// RUN: %swift-syntax-test -input-source-filename %s -parse-gen > %t +// RUN: diff -u %s %t +// RUN: %swift-syntax-test -input-source-filename %s -parse-gen -print-node-kind > %t.withkinds +// RUN: diff -u %S/Outputs/round_trip_parse_gen.swift.withkinds %t.withkinds +// RUN: %swift-syntax-test -input-source-filename %s -eof > %t +// RUN: diff -u %s %t +// RUN: %swift-syntax-test -serialize-raw-tree -input-source-filename %s > %t.dump +// RUN: %swift-syntax-test -deserialize-raw-tree -input-source-filename %t.dump -output-filename %t +// RUN: diff -u %s %t + +// Note: RUN lines copied from test/Syntax/round_trip_parse_gen.swift. + +@differentiable(jvp: foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { return 1 } + +@differentiable(jvp: foo(_:_:) where T : FloatingPoint) +func bar(_ x: T, _: T) -> T { return 1 } + +@differentiable(wrt: x, jvp: foo(_:_:)) +func bar(_ x: Float, _: Float) -> Float { return 1 } + +@differentiable(wrt: (self, x, y), jvp: foo(_:_:)) +func bar(_ x: Float, y: Float) -> Float { return 1 } + +@differentiable(wrt: (self, x, y), jvp: bar, vjp: foo(_:_:) where T : FloatingPoint) +func bar(_ x: T, y: T) -> T { return 1 } + +@derivative(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: -) +func negateDerivative(_ x: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (-x, { v in -v }) +} + +@derivative(of: baz(label:_:), wrt: (x)) +func bazDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@derivative(of: A.B.C.foo(label:_:), wrt: x) +func qualifiedDerivative(_ x: Float, y: Float) + -> (value: Float, pullback: (Float) -> Float) { + return (x, { v in v }) +} + +@transpose(of: +) +func addTranspose(_ v: Float) -> (Float, Float) { + return (v, v) +} + +@transpose(of: -, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@transpose(of: Float.-, wrt: (0, 1)) +func subtractTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} + +@derivative(of: A.B.C.foo(label:_:), wrt: 0) +func qualifiedTranspose(_ v: Float) -> (Float, Float) { + return (v, -v) +} diff --git a/test/AutoDiff/stdlib/differentiable_protocol.swift b/test/AutoDiff/stdlib/differentiable_protocol.swift new file mode 100644 index 0000000000000..4ff49e8708301 --- /dev/null +++ b/test/AutoDiff/stdlib/differentiable_protocol.swift @@ -0,0 +1,46 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: differentiable_programming + +import _Differentiation + +// Test `Differentiable` protocol conformances. + +struct FloatWrapper { + var value: Float +} +extension FloatWrapper: AdditiveArithmetic { + static var zero: Self { + FloatWrapper(value: Float.zero) + } + static func + (lhs: Self, rhs: Self) -> Self { + return FloatWrapper(value: lhs.value + rhs.value) + } + static func - (lhs: Self, rhs: Self) -> Self { + return FloatWrapper(value: lhs.value + rhs.value) + } +} +extension FloatWrapper: Differentiable { + public typealias TangentVector = Self +} + +struct Wrapper { + var value: T +} +extension Wrapper: Equatable where T: Equatable {} +extension Wrapper: AdditiveArithmetic where T: AdditiveArithmetic { + static var zero: Self { + Wrapper(value: T.zero) + } + static func + (lhs: Self, rhs: Self) -> Self { + return Wrapper(value: lhs.value + rhs.value) + } + static func - (lhs: Self, rhs: Self) -> Self { + return Wrapper(value: lhs.value + rhs.value) + } +} +extension Wrapper: Differentiable where T: Differentiable { + typealias TangentVector = Wrapper + mutating func move(along direction: TangentVector) { + value.move(along: direction.value) + } +} diff --git a/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift new file mode 100644 index 0000000000000..977072bac6318 --- /dev/null +++ b/test/AutoDiff/stdlib/differentiable_stdlib_conformances.swift @@ -0,0 +1,21 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test +// REQUIRES: differentiable_programming + +import _Differentiation + +// Test `Differentiable` protocol conformances for stdlib types. + +func assertConformsToDifferentiable(_: T.Type) where T: Differentiable {} + +func assertSelfEqualsTangentVector(_: T.Type) +where T: Differentiable, T == T.TangentVector {} + +// Test `FloatingPoint` types. +func testFloatingPointDifferentiableConformance() { + assertSelfEqualsTangentVector(Float.self) + assertSelfEqualsTangentVector(Double.self) + #if (arch(i386) || arch(x86_64)) && !(os(Windows) || os(Android)) + assertSelfEqualsTangentVector(Float80.self) + #endif +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4996ed48f913..796b9bf1da4cb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -87,7 +87,7 @@ function(get_test_dependencies SDK result_var_name) list(APPEND deps ${deps_binaries}) else() foreach(binary ${deps_binaries}) - list(APPEND deps "${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/${binary}") + list(APPEND deps "${SWIFT_NATIVE_SWIFT_TOOLS_PATH}/${binary}${CMAKE_EXECUTABLE_SUFFIX}") endforeach() endif() @@ -134,6 +134,7 @@ normalize_boolean_spelling(SWIFT_AST_VERIFIER) normalize_boolean_spelling(SWIFT_ASAN_BUILD) normalize_boolean_spelling(SWIFT_BUILD_SYNTAXPARSERLIB) normalize_boolean_spelling(SWIFT_ENABLE_SOURCEKIT_TESTS) +normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) is_build_type_optimized("${SWIFT_STDLIB_BUILD_TYPE}" SWIFT_OPTIMIZED) set(profdata_merge_worker @@ -162,10 +163,28 @@ endif() foreach(SDK ${SWIFT_SDKS}) foreach(ARCH ${SWIFT_SDK_${SDK}_ARCHITECTURES}) + # macCatalyst needs to run two sets of tests: one with the normal macosx target triple + # and one with the the macCatalyst ios-macabi triple. The build_flavors list will + # have have only the "default" flavor for all SDKs and architectures except + # OSX when macCatalyst support is enabled. + set(build_flavors "default") + if(SWIFT_ENABLE_MACCATALYST AND "${SDK}" STREQUAL "OSX") + list(APPEND build_flavors "ios-like" ) + endif() + + foreach(BUILD_FLAVOR ${build_flavors}) # Configure variables for this subdirectory. set(VARIANT_SUFFIX "-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-${ARCH}") set(VARIANT_TRIPLE "${SWIFT_SDK_${SDK}_ARCH_${ARCH}_TRIPLE}${SWIFT_SDK_${SDK}_DEPLOYMENT_VERSION}") set(VARIANT_SDK "${SWIFT_SDK_${SDK}_ARCH_${ARCH}_PATH}") + set(DEFAULT_OSX_VARIANT_SUFFIX "") + + if(BUILD_FLAVOR STREQUAL "ios-like") + set(DEFAULT_OSX_VARIANT_SUFFIX "${VARIANT_SUFFIX}") + # Use the macCatalyst target triple and compiler resources for the iOS-like build flavor. + set(VARIANT_SUFFIX "-${SWIFT_SDK_${SDK}_LIB_SUBDIR}-maccatalyst-${ARCH}") + set(VARIANT_TRIPLE "${ARCH}-apple-ios13.0-macabi") + endif() # A directory where to put the xUnit-style XML test results. set(SWIFT_TEST_RESULTS_DIR @@ -231,8 +250,17 @@ _Block_release(void) { }\n") if(SWIFT_BUILD_STDLIB AND SWIFT_INCLUDE_TESTS) list(APPEND test_dependencies "swift-test-stdlib-${SWIFT_SDK_${SDK}_LIB_SUBDIR}") - list(APPEND test_dependencies - "swift-reflection-test${VARIANT_SUFFIX}_signed") + + if(BUILD_FLAVOR STREQUAL "ios-like") + # When testing the iOS-like build flavor, use the the normal macOS + # swift-reflection-test-tool. That tool runs out of process so it + # doesn't need to be build for macCatalyst. + list(APPEND test_dependencies + "swift-reflection-test${DEFAULT_OSX_VARIANT_SUFFIX}") + else() + list(APPEND test_dependencies + "swift-reflection-test${VARIANT_SUFFIX}_signed") + endif() endif() if(NOT "${COVERAGE_DB}" STREQUAL "") @@ -305,6 +333,10 @@ _Block_release(void) { }\n") list(APPEND LIT_ARGS "--xunit-xml-output=${SWIFT_TEST_RESULTS_DIR}/lit-tests.xml") + if(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) + list(APPEND LIT_ARGS "--param" "differentiable_programming") + endif() + foreach(test_subset ${TEST_SUBSETS}) set(directories) set(dependencies ${test_dependencies}) @@ -386,6 +418,7 @@ _Block_release(void) { }\n") endforeach() endforeach() endforeach() + endforeach() endforeach() # Add shortcuts for the default variant. diff --git a/test/CircularReferences/global_typealias.swift b/test/CircularReferences/global_typealias.swift index 2b94ca7ce2e05..fdfc7d573917e 100644 --- a/test/CircularReferences/global_typealias.swift +++ b/test/CircularReferences/global_typealias.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -typealias A = B // expected-error {{type alias 'A' references itself}} -typealias C = D // expected-note {{through reference here}} -typealias D = (A, Int) // expected-note {{through reference here}} -typealias B = C // expected-note {{through reference here}} +typealias A = B // expected-error {{type alias 'A' references itself}} expected-note {{through reference here}} +typealias C = D // expected-note {{through reference here}} expected-note {{through reference here}} +typealias D = (A, Int) // expected-note {{through reference here}} expected-note {{through reference here}} +typealias B = C // expected-note {{through reference here}} expected-note {{through reference here}} diff --git a/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Headers/PrivateWarning.h b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Headers/PrivateWarning.h new file mode 100644 index 0000000000000..b5e7639e45d9e --- /dev/null +++ b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Headers/PrivateWarning.h @@ -0,0 +1 @@ +/* No content */ diff --git a/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.modulemap b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..6680b13edffa8 --- /dev/null +++ b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module PrivateWarning { + header "PrivateWarning.h" +} diff --git a/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.private.modulemap b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.private.modulemap new file mode 100644 index 0000000000000..cbbe967dba158 --- /dev/null +++ b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/Modules/module.private.modulemap @@ -0,0 +1,3 @@ +framework module PrivateWarning.Private { + header "PrivateWarning_Private.h" +} diff --git a/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/PrivateHeaders/PrivateWarning_Private.h b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/PrivateHeaders/PrivateWarning_Private.h new file mode 100644 index 0000000000000..b5e7639e45d9e --- /dev/null +++ b/test/ClangImporter/Inputs/ModuleMapWarning/PrivateWarning.framework/PrivateHeaders/PrivateWarning_Private.h @@ -0,0 +1 @@ +/* No content */ diff --git a/test/ClangImporter/Inputs/ModuleMapWarning/bridging-pch.h b/test/ClangImporter/Inputs/ModuleMapWarning/bridging-pch.h new file mode 100644 index 0000000000000..fc2314ecd6265 --- /dev/null +++ b/test/ClangImporter/Inputs/ModuleMapWarning/bridging-pch.h @@ -0,0 +1,2 @@ +@import PrivateWarning; +@import PrivateWarning.Private; diff --git a/test/ClangImporter/Inputs/SwiftPrivateAttr.txt b/test/ClangImporter/Inputs/SwiftPrivateAttr.txt index 97cb55de5cd60..ed38550938e26 100644 --- a/test/ClangImporter/Inputs/SwiftPrivateAttr.txt +++ b/test/ClangImporter/Inputs/SwiftPrivateAttr.txt @@ -1,4 +1,4 @@ -import Foundation +@_exported import Foundation protocol __PrivProto { } diff --git a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h index b7d0f2271a940..c7048060a9957 100644 --- a/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h +++ b/test/ClangImporter/Inputs/custom-modules/EnumExhaustivity.h @@ -5,6 +5,9 @@ enum _name _name; \ enum __attribute__((enum_extensibility(closed))) _name + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference" typedef MY_ENUM(RegularEnum) { RegularEnumA, RegularEnumB @@ -47,3 +50,4 @@ enum __attribute__((enum_extensibility(closed))) UnavailableCases { UnavailableCasesB, UnavailableCasesThisIsTheUnavailableOne __attribute__((availability(swift, unavailable))) }; +#pragma clang diagnostic pop \ No newline at end of file diff --git a/test/ClangImporter/Inputs/custom-modules/ImportAsMember.apinotes b/test/ClangImporter/Inputs/custom-modules/ImportAsMember.apinotes index e6f05c5bc0a83..b819569e08a55 100644 --- a/test/ClangImporter/Inputs/custom-modules/ImportAsMember.apinotes +++ b/test/ClangImporter/Inputs/custom-modules/ImportAsMember.apinotes @@ -12,6 +12,8 @@ Functions: Typedefs: - Name: IAMStruct1APINoteType SwiftName: Struct1.NewApiNoteType +- Name: IAMBadInnerIntAPINotes + SwiftName: IAMNonexistent.Inner2 SwiftVersions: - Version: 3 Globals: diff --git a/test/ClangImporter/Inputs/custom-modules/ImportAsMember.h b/test/ClangImporter/Inputs/custom-modules/ImportAsMember.h index 347914d3dd0de..42c1fc088699e 100644 --- a/test/ClangImporter/Inputs/custom-modules/ImportAsMember.h +++ b/test/ClangImporter/Inputs/custom-modules/ImportAsMember.h @@ -68,4 +68,12 @@ struct IAMMultipleNested { typedef int MNInnerInt __attribute__((swift_name("IAMMultipleNested.Inner"))); typedef float MNInnerFloat __attribute__((swift_name("IAMMultipleNested.Inner"))); +typedef int IAMBadInnerInt + __attribute__((swift_name("IAMNonexistent.Inner"))); +// CHECK: ImportAsMember.h:[[@LINE-1]]:{{[0-9]+}}: warning: imported declaration 'IAMBadInnerInt' could not be mapped to 'IAMNonexistent.Inner' +// CHECK: ImportAsMember.h:[[@LINE-2]]:{{[0-9]+}}: note: please report this issue to the owners of 'ImportAsMember' +typedef int IAMBadInnerIntAPINotes; +// CHECK: ImportAsMember.h:[[@LINE-1]]:{{[0-9]+}}: warning: imported declaration 'IAMBadInnerIntAPINotes' could not be mapped to 'IAMNonexistent.Inner2' +// CHECK: ImportAsMember.h:[[@LINE-2]]:{{[0-9]+}}: note: please report this issue to the owners of 'ImportAsMember' + #endif // IMPORT_AS_MEMBER_H diff --git a/test/ClangImporter/Inputs/custom-modules/LocalVsFileScope.h b/test/ClangImporter/Inputs/custom-modules/LocalVsFileScope.h new file mode 100644 index 0000000000000..6a09d94cc2006 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/LocalVsFileScope.h @@ -0,0 +1,3 @@ +#include "LocalVsFileScopeBase.h" + +void theFunctionInQuestion(int); diff --git a/test/ClangImporter/Inputs/custom-modules/LocalVsFileScopeBase.h b/test/ClangImporter/Inputs/custom-modules/LocalVsFileScopeBase.h new file mode 100644 index 0000000000000..59d2572c2cf88 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/LocalVsFileScopeBase.h @@ -0,0 +1,3 @@ +void aFunctionInBase(void) { + void theFunctionInQuestion(int); +} diff --git a/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.apinotes b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.apinotes new file mode 100644 index 0000000000000..b08ab00a228e9 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.apinotes @@ -0,0 +1,7 @@ +Name: ObjCParseExtras +Classes: +- Name: SubclassWithSwiftPrivateDesignatedInit + Methods: + - Selector: "initWithI:" + MethodKind: Instance + SwiftPrivate: true diff --git a/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h index e552800b93b53..117ba3ce0c318 100644 --- a/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h +++ b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h @@ -249,3 +249,14 @@ struct TrivialToCopy { @interface OverrideInExtensionSub : OverrideInExtensionBase @end + +@interface SuperclassWithDesignatedInitInCategory +@end + +@interface SubclassWithSwiftPrivateDesignatedInit : SuperclassWithDesignatedInitInCategory +-(instancetype) initWithI:(NSInteger)i __attribute__((objc_designated_initializer)); +@end + +@interface SuperclassWithDesignatedInitInCategory () +-(instancetype) initWithI:(NSInteger)i __attribute__((objc_designated_initializer)); +@end diff --git a/test/ClangImporter/Inputs/custom-modules/module.map b/test/ClangImporter/Inputs/custom-modules/module.map index 09ef0bf160620..6e9e9113650cd 100644 --- a/test/ClangImporter/Inputs/custom-modules/module.map +++ b/test/ClangImporter/Inputs/custom-modules/module.map @@ -165,6 +165,7 @@ module SubclassExistentialsExtra { module SwiftPrivateAttr { header "SwiftPrivateAttr.h" + export * } module TestProtocols { header "Protocols.h" } @@ -233,3 +234,11 @@ module ConditionallyFoo { module ForwardDeclarationsHelper { header "ForwardDeclarationsHelper.h" } + +module LocalVsFileScopeBase { + header "LocalVsFileScopeBase.h" +} +module LocalVsFileScope { + header "LocalVsFileScope.h" + export * +} diff --git a/test/ClangImporter/Inputs/enum-inferred-exhaustivity.h b/test/ClangImporter/Inputs/enum-inferred-exhaustivity.h index 130ca99e2aa65..c307a9aff3e03 100644 --- a/test/ClangImporter/Inputs/enum-inferred-exhaustivity.h +++ b/test/ClangImporter/Inputs/enum-inferred-exhaustivity.h @@ -5,6 +5,8 @@ // Make this C-compatible by leaving out the type. #define CF_ENUM(_name) enum _name _name; enum _name +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference" typedef CF_ENUM(EnumWithDefaultExhaustivity) { EnumWithDefaultExhaustivityLoneCase }; @@ -14,3 +16,4 @@ typedef CF_ENUM(EnumWithDefaultExhaustivity) { typedef CF_ENUM(EnumWithSpecialAttributes) { EnumWithSpecialAttributesLoneCase } __CF_ENUM_ATTRIBUTES; +#pragma clang diagnostic pop diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h new file mode 100644 index 0000000000000..4c66878d60f8a --- /dev/null +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h @@ -0,0 +1,34 @@ +__attribute__((objc_root_class)) +@interface Base +- (nonnull instancetype)init; +@end + +typedef struct SomeStruct_s { + int inner; +} SomeStruct; + +@interface MyColor : Base +@property (class, nonatomic, readonly) MyColor *systemRedColor; +@end + +@interface MyBaseClass : Base +// @property (nonatomic, strong, nullable) Base *derivedMember; +@property (nonatomic, assign, readonly) SomeStruct myStructure; +@end + +@interface MyDerivedClass : MyBaseClass +@property (nonatomic, strong, nullable) Base *derivedMember; +@end + +typedef enum { + Caster, + Grantulated, + Confectioners, + Cane, + Demerara, + Turbinado, +} RefinedSugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); + +@interface Refinery : Base +@property (nonatomic, readonly) RefinedSugar sugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); +@end diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Modules/module.modulemap b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..93c285ea2de68 --- /dev/null +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Modules/module.modulemap @@ -0,0 +1,7 @@ +framework module CategoryOverrides { + umbrella header "CategoryOverrides.h" + module * { + export * + } + exclude header "Private.h" +} diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h new file mode 100644 index 0000000000000..0c4ed45702bca --- /dev/null +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h @@ -0,0 +1,20 @@ +#import + +@interface MyBaseClass () +@property (nonatomic, strong, nullable) Base *derivedMember; +@end + +@interface MyColor () ++ (MyColor * _Null_unspecified) systemRedColor; +@end + +@protocol MyPrivateProtocol +- (SomeStruct) myStructure; +@end + +@interface MyBaseClass () +@end + +@interface Refinery () +@property (nonatomic, readwrite) RefinedSugar sugar; +@end diff --git a/test/ClangImporter/Inputs/objc_direct.h b/test/ClangImporter/Inputs/objc_direct.h new file mode 100644 index 0000000000000..d283e587c45f1 --- /dev/null +++ b/test/ClangImporter/Inputs/objc_direct.h @@ -0,0 +1,12 @@ +@interface Bar +@property (readonly, direct, nonatomic) int directProperty; +- (void)directMethod __attribute__((objc_direct)); ++ (void)directClassMethod __attribute__((objc_direct)); +@end + +__attribute__((objc_direct_members)) +@interface Bar () +@property (readonly, nonatomic) int directProperty2; +- (void)directMethod2; ++ (void)directClassMethod2; +@end diff --git a/test/ClangImporter/Inputs/objc_init_override_kind.h b/test/ClangImporter/Inputs/objc_init_override_kind.h new file mode 100644 index 0000000000000..981578f5f5d90 --- /dev/null +++ b/test/ClangImporter/Inputs/objc_init_override_kind.h @@ -0,0 +1,8 @@ +#import + +@interface Base : NSObject + +- (instancetype) init; + +- (instancetype) initWithFoo: (int) x NS_DESIGNATED_INITIALIZER; +@end diff --git a/test/ClangImporter/MixedSource/Inputs/import-as-member-swift.h b/test/ClangImporter/MixedSource/Inputs/import-as-member-swift.h index d0c6771a39acd..7d6ecb0132de9 100644 --- a/test/ClangImporter/MixedSource/Inputs/import-as-member-swift.h +++ b/test/ClangImporter/MixedSource/Inputs/import-as-member-swift.h @@ -1,5 +1,14 @@ @class Outer; - -struct Nested { - int value; +struct NestedInOuter { + int a; } __attribute((swift_name("Outer.Nested"))); + +@class OuterByObjCName_ObjC; +struct NestedInOuterByObjCName { + int b; +} __attribute((swift_name("OuterByObjCName_ObjC.Nested"))); + +@class OuterBySwiftName_Swift; +struct NestedInOuterBySwiftName { + int c; +} __attribute((swift_name("OuterBySwiftName_Swift.Nested"))); diff --git a/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h b/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h index 815088bbda918..206cb15a82f31 100644 --- a/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h +++ b/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h @@ -68,6 +68,65 @@ SWIFT_PROTOCOL("SwiftProto") id _Nonnull convertToProto(SwiftClassWithCustomName *_Nonnull obj); +SWIFT_CLASS("ObjCClass") +__attribute__((objc_root_class)) +@interface ObjCClass +@end + +SWIFT_CLASS("ImplicitlyObjCClass") +@interface ImplicitlyObjCClass : ObjCClass +@end +void consumeImplicitlyObjCClass(ImplicitlyObjCClass *_Nonnull obj); + +SWIFT_CLASS("ExplicitlyObjCClass") +__attribute__((objc_root_class)) +@interface ExplicitlyObjCClass +@end +void consumeExplicitlyObjCClass(ExplicitlyObjCClass *_Nonnull obj); + +SWIFT_CLASS_NAMED("HasSameCustomNameClass") +__attribute__((objc_root_class)) +@interface HasSameCustomNameClass +@end +void consumeHasSameCustomNameClass(HasSameCustomNameClass *_Nonnull obj); + +SWIFT_CLASS_NAMED("SwiftNativeTypeHasDifferentCustomNameClass") +__attribute__((objc_root_class)) +@interface NativeTypeHasDifferentCustomNameClass +@end +SWIFT_CLASS_NAMED("NativeTypeHasDifferentCustomNameClass") +__attribute__((objc_root_class)) +@interface ObjCNativeTypeHasDifferentCustomNameClass +@end +void consumeNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass *_Nonnull obj); +void consumeObjCNativeTypeHasDifferentCustomNameClass(ObjCNativeTypeHasDifferentCustomNameClass *_Nonnull obj); + +SWIFT_CLASS_NAMED("SwiftNativeTypeIsNonObjCClass") +__attribute__((objc_root_class)) +@interface NativeTypeIsNonObjCClass +@end +void consumeNativeTypeIsNonObjCClass(NativeTypeIsNonObjCClass *_Nonnull obj); + +@class ForwardImplicitlyObjCClass; +void consumeForwardImplicitlyObjCClass(ForwardImplicitlyObjCClass *_Nonnull obj); + +@class ForwardExplicitlyObjCClass; +void consumeForwardExplicitlyObjCClass(ForwardExplicitlyObjCClass *_Nonnull obj); + +@class ForwardHasSameCustomNameClass; +void consumeForwardHasSameCustomNameClass(ForwardHasSameCustomNameClass *_Nonnull obj); + +@class ForwardNativeTypeHasDifferentCustomNameClass; +@class ObjCForwardNativeTypeHasDifferentCustomNameClass; +void consumeForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass *_Nonnull obj); +void consumeObjCForwardNativeTypeHasDifferentCustomNameClass(ObjCForwardNativeTypeHasDifferentCustomNameClass *_Nonnull obj); + +@class ForwardNativeTypeIsNonObjCClass; +void consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass *_Nonnull obj); + +@class ForwardNativeTypeIsUnambiguouslyNonObjCClass; +void consumeForwardNativeTypeIsUnambiguouslyNonObjCClass(ForwardNativeTypeIsUnambiguouslyNonObjCClass *_Nonnull obj); + SWIFT_CLASS("BOGUS") @interface BogusClass @end diff --git a/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.swift b/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.swift index 175bad311fe28..611d3477c3690 100644 --- a/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.swift +++ b/test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.swift @@ -31,3 +31,74 @@ public class CustomNameClass : CustomName { @objc func protoMethod() @objc var protoProperty: Int { get } } + +@objc +public class ObjCClass { + public init() {} +} + +public class ImplicitlyObjCClass : ObjCClass { + public override init() { super.init() } +} + +@objc +public class ExplicitlyObjCClass { + public init() {} +} + +@objc(HasSameCustomNameClass) +public class HasSameCustomNameClass { + public init() {} +} + +@objc(ObjCNativeTypeHasDifferentCustomNameClass) +public class NativeTypeHasDifferentCustomNameClass { + public init() {} +} +@objc(NativeTypeHasDifferentCustomNameClass) +public class SwiftNativeTypeHasDifferentCustomNameClass { + public init() {} +} + +public class NativeTypeIsNonObjCClass { + public init() {} +} +@objc(NativeTypeIsNonObjCClass) +public class SwiftNativeTypeIsNonObjCClass { + public init() {} +} + +public class ForwardImplicitlyObjCClass : ObjCClass { + public override init() { super.init() } +} + +@objc +public class ForwardExplicitlyObjCClass { + public init() {} +} + +@objc(ForwardHasSameCustomNameClass) +public class ForwardHasSameCustomNameClass { + public init() {} +} + +@objc(ObjCForwardNativeTypeHasDifferentCustomNameClass) +public class ForwardNativeTypeHasDifferentCustomNameClass { + public init() {} +} +@objc(ForwardNativeTypeHasDifferentCustomNameClass) +public class SwiftForwardNativeTypeHasDifferentCustomNameClass { + public init() {} +} + +public class ForwardNativeTypeIsNonObjCClass { + public init() {} +} +@objc(ForwardNativeTypeIsNonObjCClass) +public class SwiftForwardNativeTypeIsNonObjCClass { + public init() {} +} + +public class ForwardNativeTypeIsUnambiguouslyNonObjCClass { + public init() {} +} diff --git a/test/ClangImporter/MixedSource/import-as-member-swift.swift b/test/ClangImporter/MixedSource/import-as-member-swift.swift index 4dc0be9207666..54f023739ce13 100644 --- a/test/ClangImporter/MixedSource/import-as-member-swift.swift +++ b/test/ClangImporter/MixedSource/import-as-member-swift.swift @@ -2,4 +2,12 @@ @objc internal class Outer {} -_ = Outer.Nested() +@objc(OuterByObjCName_ObjC) +internal class OuterByObjCName_Swift {} + +@objc(OuterBySwiftName_ObjC) +internal class OuterBySwiftName_Swift {} + +_ = Outer.Nested(a: 1) +_ = OuterByObjCName_Swift.Nested(b: 2) +_ = OuterBySwiftName_Swift.Nested(c: 3) diff --git a/test/ClangImporter/MixedSource/import-mixed-framework.swift b/test/ClangImporter/MixedSource/import-mixed-framework.swift index 004ded545121d..8ce4b8b78da57 100644 --- a/test/ClangImporter/MixedSource/import-mixed-framework.swift +++ b/test/ClangImporter/MixedSource/import-mixed-framework.swift @@ -32,3 +32,33 @@ func testAnyObject(_ obj: AnyObject) { obj.protoMethod() _ = obj.protoProperty } + +consumeImplicitlyObjCClass(ImplicitlyObjCClass()) + +consumeExplicitlyObjCClass(ExplicitlyObjCClass()) + +consumeHasSameCustomNameClass(HasSameCustomNameClass()) + +consumeNativeTypeHasDifferentCustomNameClass(SwiftNativeTypeHasDifferentCustomNameClass()) +consumeObjCNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass()) +consumeNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'NativeTypeHasDifferentCustomNameClass' to expected argument type 'SwiftNativeTypeHasDifferentCustomNameClass'}} +consumeObjCNativeTypeHasDifferentCustomNameClass(SwiftNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'SwiftNativeTypeHasDifferentCustomNameClass' to expected argument type 'NativeTypeHasDifferentCustomNameClass'}} + +consumeNativeTypeIsNonObjCClass(SwiftNativeTypeIsNonObjCClass()) +consumeNativeTypeIsNonObjCClass(NativeTypeIsNonObjCClass()) // expected-error {{cannot convert value of type 'NativeTypeIsNonObjCClass' to expected argument type 'SwiftNativeTypeIsNonObjCClass'}} + +consumeForwardImplicitlyObjCClass(ForwardImplicitlyObjCClass()) + +consumeForwardExplicitlyObjCClass(ForwardExplicitlyObjCClass()) + +consumeForwardHasSameCustomNameClass(ForwardHasSameCustomNameClass()) + +consumeForwardNativeTypeHasDifferentCustomNameClass(SwiftForwardNativeTypeHasDifferentCustomNameClass()) +consumeObjCForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass()) +consumeForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'ForwardNativeTypeHasDifferentCustomNameClass' to expected argument type 'SwiftForwardNativeTypeHasDifferentCustomNameClass'}} +consumeObjCForwardNativeTypeHasDifferentCustomNameClass(SwiftForwardNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'SwiftForwardNativeTypeHasDifferentCustomNameClass' to expected argument type 'ForwardNativeTypeHasDifferentCustomNameClass'}} + +consumeForwardNativeTypeIsNonObjCClass(SwiftForwardNativeTypeIsNonObjCClass()) +consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass()) // expected-error {{cannot convert value of type 'ForwardNativeTypeIsNonObjCClass' to expected argument type 'SwiftForwardNativeTypeIsNonObjCClass'}} + +consumeForwardNativeTypeIsUnambiguouslyNonObjCClass(ForwardNativeTypeIsUnambiguouslyNonObjCClass()) diff --git a/test/ClangImporter/attr-swift_name.swift b/test/ClangImporter/attr-swift_name.swift index b626c829c8d13..f0cb32c55e385 100644 --- a/test/ClangImporter/attr-swift_name.swift +++ b/test/ClangImporter/attr-swift_name.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s 2>&1 | %FileCheck %s +// RUN: %empty-directory(%t.mcp) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -Xcc -w -typecheck %s -module-cache-path %t.mcp -disable-named-lazy-member-loading 2>&1 | %FileCheck %s // REQUIRES: objc_interop @@ -19,11 +20,13 @@ func test(_ i: Int) { // CHECK: warning: too few parameters in swift_name attribute (expected 2; got 1) // CHECK: + (instancetype)g:(id)x outParam:(int *)foo SWIFT_NAME(init(g:)); - + // CHECK-NOT: warning: + // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' // CHECK-NOT: warning: // CHECK: warning: too few parameters in swift_name attribute (expected 2; got 1) // CHECK: + (instancetype)testW:(id)x out:(id *)outObject SWIFT_NAME(ww(_:)); + // CHECK-NOT: warning: + // CHECK: note: please report this issue to the owners of 'ObjCIRExtras' + // CHECK-NOT: warning: } - -// CHECK-NOT: warning: diff --git a/test/ClangImporter/attr-swift_private.swift b/test/ClangImporter/attr-swift_private.swift index 05ddc81835f30..f249454b15d96 100644 --- a/test/ClangImporter/attr-swift_private.swift +++ b/test/ClangImporter/attr-swift_private.swift @@ -121,7 +121,7 @@ func testCF(_ a: __PrivCFType, b: __PrivCFSub, c: __PrivInt) { makeSureAnyObject(a) makeSureAnyObject(b) #if !IRGEN - makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') does not conform to expected type 'AnyObject'}} + makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') expected to be an instance of a class or class-constrained type}} #endif } diff --git a/test/ClangImporter/cf.swift b/test/ClangImporter/cf.swift index d74a0cd14c306..790161d37abd6 100644 --- a/test/ClangImporter/cf.swift +++ b/test/ClangImporter/cf.swift @@ -119,7 +119,6 @@ func testOutParametersBad() { let power: CCPowerSupply? CCRefrigeratorGetPowerSupplyIndirect(0, power) // expected-error {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator?'}} - // expected-error@-1 {{cannot convert value of type 'CCPowerSupply?' to expected argument type 'AutoreleasingUnsafeMutablePointer'}} let item: CCItem? CCRefrigeratorGetItemUnaudited(0, 0, item) // expected-error {{cannot convert value of type 'Int' to expected argument type 'CCRefrigerator?'}} diff --git a/test/ClangImporter/cfuncs_scope.swift b/test/ClangImporter/cfuncs_scope.swift new file mode 100644 index 0000000000000..5fab79591e3ca --- /dev/null +++ b/test/ClangImporter/cfuncs_scope.swift @@ -0,0 +1,18 @@ +// RUN: not %target-swift-frontend -typecheck %s -I %S/Inputs/custom-modules 2>&1 | %FileCheck %s + +import LocalVsFileScope + +func testLocalVsFileScope() { + LocalVsFileScopeBase.theFunctionInQuestion() + // CHECK: :[[@LINE-1]]:3: error: module 'LocalVsFileScopeBase' has no member named 'theFunctionInQuestion' + + theFunctionInQuestion() + // CHECK: :[[@LINE-1]]:25: error: missing argument + // CHECK: LocalVsFileScope.theFunctionInQuestion:1:{{[0-9]+}}: note: + // This is not a wonderful test because it's relying on the diagnostic + // engine's synthesis of fake declarations to figure out what module the + // importer assigned the function to. But, well, that's what we get. + + aFunctionInBase() // just make sure it's imported + // CHECK-NOT: :[[@LINE-1]]:{{[0-9]+}}: +} diff --git a/test/ClangImporter/clang-function-types.swift b/test/ClangImporter/clang-function-types.swift new file mode 100644 index 0000000000000..52d7dae1dd75a --- /dev/null +++ b/test/ClangImporter/clang-function-types.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +import ctypes + +// CHECK: f1: (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)? +public let f1 = getFunctionPointer_() + +// CHECK: f2: (@convention(c, cType: "int (*(*)(int (*)(int)))(int)") ((@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?) -> (@convention(c, cType: "int (*)(int)") (Swift.Int32) -> Swift.Int32)?)? +public let f2 = getHigherOrderFunctionPointer() + +// CHECK: f3: () -> (@convention(c, cType: "Dummy *(*)(Dummy *)") (Swift.UnsafeMutablePointer?) -> Swift.UnsafeMutablePointer?)? +public let f3 = getFunctionPointer3 diff --git a/test/ClangImporter/disable-source-import.swift b/test/ClangImporter/disable-source-import.swift new file mode 100644 index 0000000000000..826a2b10d2e24 --- /dev/null +++ b/test/ClangImporter/disable-source-import.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t.mcp) + +// This should fail only if -disable-clangimporter-source-import is present. + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp %s | %FileCheck --allow-empty %s + +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: -enable-objc-interop -typecheck -o - -I %S/Inputs/custom-modules \ +// RUN: -module-cache-path %t.mcp -disable-clangimporter-source-import %s \ +// RUN: 2>&1 | %FileCheck --check-prefix=ERROR %s + +import ExternIntX + +public let y = x // ERROR: error + +// CHECK-NOT: error diff --git a/test/ClangImporter/import-as-member.swift b/test/ClangImporter/import-as-member.swift index 670a49c8af9fc..3011c1a54d1da 100644 --- a/test/ClangImporter/import-as-member.swift +++ b/test/ClangImporter/import-as-member.swift @@ -1,4 +1,6 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules %s -verify +// RUN: %empty-directory(%t.mcp) +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules -module-cache-path %t.mcp %s 2>&1 | %FileCheck %S/Inputs/custom-modules/ImportAsMember.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules -module-cache-path %t.mcp %s -verify import ImportAsMember import ImportAsMemberSubmodules diff --git a/test/ClangImporter/newtype_conformance.swift b/test/ClangImporter/newtype_conformance.swift index cc6fd585d375a..2e7a718f36640 100644 --- a/test/ClangImporter/newtype_conformance.swift +++ b/test/ClangImporter/newtype_conformance.swift @@ -13,12 +13,15 @@ import MoreSwiftNewtypes func acceptEquatable(_: T) {} func acceptHashable(_: T) {} +// expected-note@-1 {{where 'T' = 'WrappedRef'}} +// expected-note@-2 {{where 'T' = 'WrappedValue'}} func acceptComparable(_: T) {} +// expected-note@-1 {{where 'T' = 'NSNotification.Name'}} func testNewTypeWrapper(x: NSNotification.Name, y: NSNotification.Name) { acceptEquatable(x) acceptHashable(x) - acceptComparable(x) // expected-error {{does not conform to expected type 'Comparable'}} + acceptComparable(x) // expected-error {{global function 'acceptComparable' requires that 'NSNotification.Name' conform to 'Comparable'}} _ = x == y _ = x != y @@ -30,6 +33,6 @@ func testNewTypeWrapper(x: NSNotification.Name, y: NSNotification.Name) { func testCustomWrappers(wrappedRef: WrappedRef, wrappedValue: WrappedValue) { acceptEquatable(wrappedRef) acceptEquatable(wrappedValue) - acceptHashable(wrappedRef) // expected-error {{does not conform to expected type 'Hashable'}} - acceptHashable(wrappedValue) // expected-error {{does not conform to expected type 'Hashable'}} + acceptHashable(wrappedRef) // expected-error {{global function 'acceptHashable' requires that 'WrappedRef' conform to 'Hashable'}} + acceptHashable(wrappedValue) // expected-error {{global function 'acceptHashable' requires that 'WrappedValue' conform to 'Hashable'}} } diff --git a/test/ClangImporter/objc_bridging_generics.swift b/test/ClangImporter/objc_bridging_generics.swift index 7a3d122476947..8de2c4b6be402 100644 --- a/test/ClangImporter/objc_bridging_generics.swift +++ b/test/ClangImporter/objc_bridging_generics.swift @@ -11,7 +11,9 @@ func testNSArrayBridging(_ hive: Hive) { } func testNSDictionaryBridging(_ hive: Hive) { - _ = hive.beesByName as [String : Bee] // expected-error{{'[String : Bee]?' is not convertible to '[String : Bee]'; did you mean to use 'as!' to force downcast?}} + _ = hive.beesByName as [String : Bee] // expected-error{{value of optional type '[String : Bee]?' must be unwrapped to a value of type '[String : Bee]'}} + // expected-note@-1 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} + // expected-note@-2 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} var dict1 = hive.anythingToBees let dict2: [AnyHashable : Bee] = dict1 diff --git a/test/ClangImporter/objc_direct.swift b/test/ClangImporter/objc_direct.swift new file mode 100644 index 0000000000000..494e002b8d46e --- /dev/null +++ b/test/ClangImporter/objc_direct.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_direct.h -verify-ignore-unknown + +// REQUIRES: objc_interop + +func callThingsOnBar(_ x: Bar) { + let _ = x.directProperty // expected-error {{getter for 'directProperty' is unavailable in Swift}} + let _ = x.directProperty2 // expected-error {{getter for 'directProperty2' is unavailable in Swift}} + + x.directMethod() // expected-error {{'directMethod()' is unavailable in Swift}} + x.directMethod2() // expected-error {{'directMethod2()' is unavailable in Swift}} + + Bar.directClassMethod() // expected-error {{'directClassMethod()' is unavailable in Swift}} + Bar.directClassMethod2() // expected-error {{'directClassMethod2()' is unavailable in Swift}} +} diff --git a/test/ClangImporter/objc_factory_method.swift b/test/ClangImporter/objc_factory_method.swift index 489169e5b9e09..cef3cae8707a2 100644 --- a/test/ClangImporter/objc_factory_method.swift +++ b/test/ClangImporter/objc_factory_method.swift @@ -23,8 +23,7 @@ func testInstanceTypeFactoryMethodInherited() { _ = NSObjectFactorySub(integer: 1) _ = NSObjectFactorySub(double: 314159) _ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}} - let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument labels '(buildingWidgets:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (double: Double), (integer: Int)}} + let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument passed to call that takes no arguments}} _ = a } @@ -76,8 +75,7 @@ func testNSErrorFactoryMethod(_ path: String) throws { } func testNonInstanceTypeFactoryMethod(_ s: String) { - _ = NSObjectFactory(string: s) // expected-error{{argument labels '(string:)' do not match any available overloads}} - // expected-note @-1 {{(double: Double), (float: Float), (integer: Int)}} + _ = NSObjectFactory(string: s) // expected-error{{argument passed to call that takes no arguments}} } func testUseOfFactoryMethod(_ queen: Bee) { diff --git a/test/ClangImporter/objc_failable_inits.swift b/test/ClangImporter/objc_failable_inits.swift index aa1718335b5db..b4d64beae4446 100644 --- a/test/ClangImporter/objc_failable_inits.swift +++ b/test/ClangImporter/objc_failable_inits.swift @@ -13,7 +13,9 @@ func testDictionary() { func testString() throws { // Optional let stringOpt = NSString(path: "blah", encoding: 0) - _ = stringOpt as NSString // expected-error{{'NSString?' is not convertible to 'NSString'; did you mean to use 'as!' to force downcast?}} + _ = stringOpt as NSString // expected-error{{value of optional type 'NSString?' must be unwrapped to a value of type 'NSString'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} // Implicitly unwrapped optional let stringIUO = NSString(path: "blah") diff --git a/test/ClangImporter/objc_implicit_with.swift b/test/ClangImporter/objc_implicit_with.swift index 70f3f51516d42..ec5702c83bd4f 100644 --- a/test/ClangImporter/objc_implicit_with.swift +++ b/test/ClangImporter/objc_implicit_with.swift @@ -34,8 +34,7 @@ func testInstanceTypeFactoryMethodInherited() { _ = NSObjectFactorySub(integer: 1) _ = NSObjectFactorySub(double: 314159) _ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}} - let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument labels '(buildingWidgets:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (double: Double), (integer: Int)}} + let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument passed to call that takes no arguments}} _ = a } @@ -44,8 +43,7 @@ func testNSErrorFactoryMethod(_ path: String) throws { } func testNonInstanceTypeFactoryMethod(_ s: String) { - _ = NSObjectFactory(string: s) // expected-error{{argument labels '(string:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'NSObjectFactory' exist with these partially matching parameter lists: (double: Double), (float: Float), (integer: Int)}} + _ = NSObjectFactory(string: s) // expected-error{{argument passed to call that takes no arguments}} } func testUseOfFactoryMethod(_ queen: Bee) { diff --git a/test/ClangImporter/objc_init.swift b/test/ClangImporter/objc_init.swift index af14b6120279e..dc973f49a6463 100644 --- a/test/ClangImporter/objc_init.swift +++ b/test/ClangImporter/objc_init.swift @@ -197,3 +197,16 @@ func classPropertiesAreNotInit() -> ProcessInfo { procInfo = ProcessInfo.processInfo // okay return procInfo } + +// Make sure we can still inherit a convenience initializer when we have a +// designated initializer override in Obj-C that isn't considered a proper +// override in Swift. In this case, both the superclass and subclass have a +// designed init with the selector `initWithI:`, however the Swift signature for +// the subclass' init is `init(__i:)` rather than `init(i:)`. +extension SuperclassWithDesignatedInitInCategory { + convenience init(y: Int) { self.init(i: y) } +} + +func testConvenienceInitInheritance() { + _ = SubclassWithSwiftPrivateDesignatedInit(y: 5) +} diff --git a/test/ClangImporter/objc_init_override_kind.swift b/test/ClangImporter/objc_init_override_kind.swift new file mode 100644 index 0000000000000..d695721ef434d --- /dev/null +++ b/test/ClangImporter/objc_init_override_kind.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -import-objc-header %S/Inputs/objc_init_override_kind.h %s + +// REQUIRES: objc_interop + +import Foundation + +// rdar://problem/56674158 +class Derived : Base { + // This is not flagged as an error, because Base.init() is a + // convenience init. + init() { super.init(foo: 123) } + + required init?(coder: NSCoder) {} +} + diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift index d3b28831ee4ac..01087fb0038e8 100644 --- a/test/ClangImporter/objc_parse.swift +++ b/test/ClangImporter/objc_parse.swift @@ -371,13 +371,13 @@ class ProtocolAdopter2 : FooProto { set { /* do nothing! */ } } } -class ProtocolAdopterBad1 : FooProto { // expected-error {{type 'ProtocolAdopterBad1' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} +class ProtocolAdopterBad1 : FooProto { // expected-error {{type 'ProtocolAdopterBad1' does not conform to protocol 'FooProto'}} @objc var bar: Int = 0 // expected-note {{candidate has non-matching type 'Int'}} } -class ProtocolAdopterBad2 : FooProto { // expected-error {{type 'ProtocolAdopterBad2' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} +class ProtocolAdopterBad2 : FooProto { // expected-error {{type 'ProtocolAdopterBad2' does not conform to protocol 'FooProto'}} let bar: CInt = 0 // expected-note {{candidate is not settable, but protocol requires it}} } -class ProtocolAdopterBad3 : FooProto { // expected-error {{type 'ProtocolAdopterBad3' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} +class ProtocolAdopterBad3 : FooProto { // expected-error {{type 'ProtocolAdopterBad3' does not conform to protocol 'FooProto'}} var bar: CInt { // expected-note {{candidate is not settable, but protocol requires it}} return 42 } @@ -550,8 +550,8 @@ func testProtocolQualified(_ obj: CopyableNSObject, cell: CopyableSomeCell, _ = cell as NSCopying _ = cell as SomeCell - _ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}} - _ = plainCell as CopyableSomeCell // expected-error {{'SomeCell' is not convertible to 'CopyableSomeCell' (aka 'SomeCell & NSCopying'); did you mean to use 'as!' to force downcast?}} + _ = plainObj as CopyableNSObject // expected-error {{value of type 'NSObject' does not conform to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') in coercion}} + _ = plainCell as CopyableSomeCell // expected-error {{value of type 'SomeCell' does not conform to 'CopyableSomeCell' (aka 'SomeCell & NSCopying') in coercion}} } extension Printing { diff --git a/test/ClangImporter/objc_redeclared_properties.swift b/test/ClangImporter/objc_redeclared_properties.swift index 97973f7b65957..2d74d53ecea21 100644 --- a/test/ClangImporter/objc_redeclared_properties.swift +++ b/test/ClangImporter/objc_redeclared_properties.swift @@ -15,9 +15,11 @@ import RedeclaredPropertiesSplit2 func test(obj: RPFoo) { if let _ = obj.nonnullToNullable {} // expected-error {{initializer for conditional binding must have Optional type}} obj.nonnullToNullable = obj // expected-error {{cannot assign to property: 'nonnullToNullable' is a get-only property}} + // expected-error@-1 {{cannot assign value of type 'RPFoo' to type 'UnsafeMutablePointer'}} if let _ = obj.nullableToNonnull {} // okay obj.nullableToNonnull = obj // expected-error {{cannot assign to property: 'nullableToNonnull' is a get-only property}} + // expected-error@-1 {{cannot assign value of type 'RPFoo' to type 'UnsafeMutablePointer'}} let _: RPFoo = obj.typeChangeMoreSpecific // expected-error {{cannot convert value of type 'Any' to specified type 'RPFoo'}} obj.typeChangeMoreSpecific = obj // expected-error {{cannot assign to property: 'typeChangeMoreSpecific' is a get-only property}} diff --git a/test/ClangImporter/objc_redeclared_properties_categories.swift b/test/ClangImporter/objc_redeclared_properties_categories.swift new file mode 100644 index 0000000000000..7cf1ae534e37b --- /dev/null +++ b/test/ClangImporter/objc_redeclared_properties_categories.swift @@ -0,0 +1,80 @@ +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s + +// RUN: echo '#import ' > %t.h +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s + +import CategoryOverrides + +// Nail down some emergent behaviors of the Clang Importer's override checking: + + +// A category declared in a (private) header can happen to double-import a property +// and a function with the same name - both before and after omit-needless-words - +// as long as they have different contextual types. +// +// This configuration appears as an undiagnosed redeclaration of a property and +// function, which is illegal. +func colors() { + // CHECK-PUBLIC: cannot call value of non-function type 'MyColor?' + // CHECK-PRIVATE-NOT: cannot call value of non-function type 'MyColor?' + let _ : MyColor = MyColor.systemRed + let _ : MyColor = MyColor.systemRed()! +} + +// Another manifestation of the above for an instance property this time. +func structs(_ base: MyBaseClass, _ derived: MyDerivedClass) { + // CHECK-PUBLIC: cannot call value of non-function type 'SomeStruct' + // CHECK-PRIVATE-NOT: cannot call value of non-function type 'SomeStruct' + let _ : SomeStruct = base.myStructure + let _ : SomeStruct = base.myStructure() + + // CHECK-PUBLIC: cannot call value of non-function type 'SomeStruct' + // CHECK-PRIVATE-NOT: cannot call value of non-function type 'SomeStruct' + let _ : SomeStruct = derived.myStructure + let _ : SomeStruct = derived.myStructure() +} + +// A category declared in a (private) header can introduce overrides of a property +// that is otherwise not declared in a base class. +// +// This configuration appears as an undiagnosed override in a Swift extension, +// which is illegal. +func takesADerivedClass(_ x: MyDerivedClass) { + // CHECK-PUBLIC-NOT: has no member 'derivedMember' + // CHECK-PRIVATE-NOT: has no member 'derivedMember' + x.derivedMember = Base() +} + +func takesABaseClass(_ x: MyBaseClass) { + // CHECK-PUBLIC: has no member 'derivedMember' + // CHECK-PRIVATE-NOT: has no member 'derivedMember' + x.derivedMember = Base() +} + +// A category declared in a (private) header can introduce overrides of a +// property that has mismatched Swift naming conventions. If we see a +// non-__attribute__((swift_private)) decl, sometimes it comes in too. + +extension Refinery { + public enum RefinedSugar { + case caster + case grantulated + case confectioners + case cane + case demerara + case turbinado + } + + public var sugar: Refinery.RefinedSugar { + return .caster // RefinedSugar(self.__sugar) + } +} + +func takesARefinery(_ x: Refinery) { + // CHECK: cannot assign to property: 'sugar' is a get-only property + x.sugar = .caster +} diff --git a/test/ClangImporter/objc_redeclared_properties_incompatible.swift b/test/ClangImporter/objc_redeclared_properties_incompatible.swift index 84c2462404f84..99a1a64643b18 100644 --- a/test/ClangImporter/objc_redeclared_properties_incompatible.swift +++ b/test/ClangImporter/objc_redeclared_properties_incompatible.swift @@ -75,13 +75,16 @@ func testCategoryWithInitializer() { let obj = PropertiesInitCategory() let _: Int = obj.nullabilityChange - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'Base?' to specified type 'Int' let _: Int = obj.missingGenerics - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' let _: Int = obj.typeChange - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'PrivateSubclass' to specified type 'Int' obj.readwriteChange = Base() // CHECK-PRIVATE-NOT: [[@LINE]]:{{.+}}: error // CHECK-PUBLIC: [[@LINE-1]]:7: error: cannot assign to property: 'readwriteChange' is a get-only property @@ -89,13 +92,16 @@ func testCategoryWithInitializer() { func testCategoryWithoutInitializer(obj: PropertiesNoInitCategory) { let _: Int = obj.nullabilityChange - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'Base?' to specified type 'Int' let _: Int = obj.missingGenerics - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'GenericClass' to specified type 'Int' let _: Int = obj.typeChange - // CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int' + // CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'PrivateSubclass' to specified type 'Int' obj.readwriteChange = Base() // CHECK-PRIVATE-NOT: [[@LINE]]:{{.+}}: error // CHECK-PUBLIC: [[@LINE-1]]:7: error: cannot assign to property: 'readwriteChange' is a get-only property diff --git a/test/ClangImporter/pch-bridging-header-deps-fine.swift b/test/ClangImporter/pch-bridging-header-deps-fine.swift new file mode 100644 index 0000000000000..6087230474c16 --- /dev/null +++ b/test/ClangImporter/pch-bridging-header-deps-fine.swift @@ -0,0 +1,30 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: rm -f %t.* +// +// Generate a bridging PCH, use it in a swift file, and check that the swift file's .swiftdeps +// mention the .h the PCH was generated from, and any .h files included in it. +// +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-pch -o %t.pch %/S/Inputs/chained-unit-test-bridging-header-to-pch.h +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -module-name test -c -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -primary-file %s -import-objc-header %t.pch +// RUN: %FileCheck --check-prefix CHECK-DEPS %s < %t.d +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS --enable-yaml-compatibility %s < %t-processed.swiftdeps +// RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS2 --enable-yaml-compatibility %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -module-name test -c -emit-dependencies-path %t.persistent.d -emit-reference-dependencies-path %t.persistent.swiftdeps -primary-file %s -import-objc-header %/S/Inputs/chained-unit-test-bridging-header-to-pch.h -pch-output-dir %t/pch +// RUN: %FileCheck --check-prefix CHECK-DEPS %s < %t.persistent.d +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.persistent.swiftdeps >%t-processed.persistent.swiftdeps +// RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS --enable-yaml-compatibility %s < %t-processed.persistent.swiftdeps +// RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS2 --enable-yaml-compatibility %s < %t-processed.persistent.swiftdeps + +print(app_function(1)) + +// CHECK-DEPS: pch-bridging-header-deps-fine.o : {{.*}}SOURCE_DIR{{/|\\}}test{{/|\\}}ClangImporter{{/|\\}}Inputs{{/|\\}}app-bridging-header-to-pch.h {{.*}}SOURCE_DIR{{/|\\}}test{{/|\\}}ClangImporter{{/|\\}}Inputs{{/|\\}}chained-unit-test-bridging-header-to-pch.h + +// CHECK-SWIFTDEPS: externalDepend {{.*}} 'SOURCE_DIR{{/|\\\\}}test{{/|\\\\}}ClangImporter{{/|\\\\}}Inputs{{/|\\\\}}app-bridging-header-to-pch.h' +// CHECK-SWIFTDEPS: externalDepend {{.*}} 'SOURCE_DIR{{/|\\\\}}test{{/|\\\\}}ClangImporter{{/|\\\\}}Inputs{{/|\\\\}}chained-unit-test-bridging-header-to-pch.h' + +// CHECK-SWIFTDEPS2-NOT: {{.*}}.pch diff --git a/test/ClangImporter/pch-bridging-header-deps.swift b/test/ClangImporter/pch-bridging-header-deps.swift index b7e9434741cc1..504185f6627dc 100644 --- a/test/ClangImporter/pch-bridging-header-deps.swift +++ b/test/ClangImporter/pch-bridging-header-deps.swift @@ -3,13 +3,13 @@ // Generate a bridging PCH, use it in a swift file, and check that the swift file's .swiftdeps // mention the .h the PCH was generated from, and any .h files included in it. // -// RUN: %target-swift-frontend -emit-pch -o %t.pch %/S/Inputs/chained-unit-test-bridging-header-to-pch.h -// RUN: %target-swift-frontend -module-name test -c -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -primary-file %s -import-objc-header %t.pch +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-pch -o %t.pch %/S/Inputs/chained-unit-test-bridging-header-to-pch.h +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -module-name test -c -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -primary-file %s -import-objc-header %t.pch // RUN: %FileCheck --check-prefix CHECK-DEPS %s < %t.d // RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS --enable-yaml-compatibility %s < %t.swiftdeps // RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS2 --enable-yaml-compatibility %s < %t.swiftdeps -// RUN: %target-swift-frontend -module-name test -c -emit-dependencies-path %t.persistent.d -emit-reference-dependencies-path %t.persistent.swiftdeps -primary-file %s -import-objc-header %/S/Inputs/chained-unit-test-bridging-header-to-pch.h -pch-output-dir %t/pch +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -module-name test -c -emit-dependencies-path %t.persistent.d -emit-reference-dependencies-path %t.persistent.swiftdeps -primary-file %s -import-objc-header %/S/Inputs/chained-unit-test-bridging-header-to-pch.h -pch-output-dir %t/pch // RUN: %FileCheck --check-prefix CHECK-DEPS %s < %t.persistent.d // RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS --enable-yaml-compatibility %s < %t.persistent.swiftdeps // RUN: %FileCheck --check-prefix CHECK-SWIFTDEPS2 --enable-yaml-compatibility %s < %t.persistent.swiftdeps diff --git a/test/ClangImporter/pch-bridging-header-module-map-diags.swift b/test/ClangImporter/pch-bridging-header-module-map-diags.swift new file mode 100644 index 0000000000000..0b9dbfc557756 --- /dev/null +++ b/test/ClangImporter/pch-bridging-header-module-map-diags.swift @@ -0,0 +1,13 @@ +// RUN: rm -f %t.* +// REQUIRES: objc_interop + +// Build a bridging PCH for involving a module map that contains a warning +// RUN: %target-swift-frontend -F %S/Inputs/ModuleMapWarning -emit-pch %S/Inputs/ModuleMapWarning/bridging-pch.h -pch-output-dir %t/pch 2> %t.stderr +// RUN: %FileCheck %s < %t.stderr + +// CHECK: module.private.modulemap:1:33: warning: private submodule 'PrivateWarning.Private' in private module map, expected top-level module + +// Check that loading that bridging PCH Does not crash the compiler. +// RUN: %target-swift-frontend -F %S/Inputs/ModuleMapWarning -import-objc-header %S/Inputs/ModuleMapWarning/bridging-pch.h -pch-output-dir %t/pch -typecheck %s + + diff --git a/test/ClangImporter/pcm-emit-and-import.swift b/test/ClangImporter/pcm-emit-and-import.swift new file mode 100644 index 0000000000000..c48ccb22eed30 --- /dev/null +++ b/test/ClangImporter/pcm-emit-and-import.swift @@ -0,0 +1,15 @@ +// Emit the explicit module. +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-pcm -module-name script -o %t/script.pcm %S/Inputs/custom-modules/module.map + +// Verify some of the output of the -dump-pcm flag. +// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP +// CHECK-DUMP: Information for module file '{{.*}}/script.pcm': +// CHECK-DUMP: Module name: script +// CHECK-DUMP: Module map file: {{.*[/\\]}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map + +// Compile a source file that imports the explicit module. +// RUN: %target-swift-frontend -typecheck -verify -Xcc -fmodule-file=%t/script.pcm %s + +import script +var _ : ScriptTy diff --git a/test/ClangImporter/serialization-sil.swift b/test/ClangImporter/serialization-sil.swift index 172a43662c548..afb46c8cd8f86 100644 --- a/test/ClangImporter/serialization-sil.swift +++ b/test/ClangImporter/serialization-sil.swift @@ -12,32 +12,39 @@ public func testPartialApply(_ obj: Test) { // CHECK: dynamic_method_br [[CURRIED1_OBJ:%.+]] : $@opened([[CURRIED1_EXISTENTIAL:.+]]) Test, #Test.normalObject!1.foreign, [[CURRIED1_TRUE:[^,]+]], [[CURRIED1_FALSE:[^,]+]] // CHECK: [[CURRIED1_FALSE]]: // CHECK: [[CURRIED1_TRUE]]([[CURRIED1_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject): - // CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject - // CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @$syXlIego_ypIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @owned AnyObject) -> @out Any - // CHECK: = partial_apply [callee_guaranteed] [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]]) + // CHECK: [[CURRIED1_OBJECT_COPY:%.*]] = copy_value [[CURRIED1_OBJ]] + // CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJECT_COPY]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject + // CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @$syXlIego_ypIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @owned AnyObject) -> @out Any + // CHECK: = partial_apply [callee_guaranteed] [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]]) curried1() } if let curried2 = obj.innerPointer { // CHECK: dynamic_method_br [[CURRIED2_OBJ:%.+]] : $@opened([[CURRIED2_EXISTENTIAL:.+]]) Test, #Test.innerPointer!1.foreign, [[CURRIED2_TRUE:[^,]+]], [[CURRIED2_FALSE:[^,]+]] // CHECK: [[CURRIED2_FALSE]]: // CHECK: [[CURRIED2_TRUE]]([[CURRIED2_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer): - // CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer + // CHECK: [[CURRIED2_OBJ_COPY:%.*]] = copy_value [[CURRIED2_OBJ]] + // CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer curried2() } if let prop1 = obj.normalObjectProp { // CHECK: dynamic_method_br [[PROP1_OBJ:%.+]] : $@opened([[PROP1_EXISTENTIAL:.+]]) Test, #Test.normalObjectProp!getter.1.foreign, [[PROP1_TRUE:[^,]+]], [[PROP1_FALSE:[^,]+]] // CHECK: [[PROP1_FALSE]]: // CHECK: [[PROP1_TRUE]]([[PROP1_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject): - // CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject - // CHECK: = apply [[PROP1_PARTIAL]]() : $@callee_guaranteed () -> @owned AnyObject + // CHECK: [[PROP1_OBJ_COPY:%.*]] = copy_value [[PROP1_OBJ]] + // CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject + // CHECK: [[PROP1_PARTIAL_COPY:%.*]] = copy_value [[PROP1_PARTIAL]] + // CHECK: [[PROP1_PARTIAL_COPY_BORROW:%.*]] = begin_borrow [[PROP1_PARTIAL_COPY]] + // CHECK: = apply [[PROP1_PARTIAL_COPY_BORROW]]() : $@callee_guaranteed () -> @owned AnyObject _ = prop1 } if let prop2 = obj.innerPointerProp { // CHECK: dynamic_method_br [[PROP2_OBJ:%.+]] : $@opened([[PROP2_EXISTENTIAL:.+]]) Test, #Test.innerPointerProp!getter.1.foreign, [[PROP2_TRUE:[^,]+]], [[PROP2_FALSE:[^,]+]] // CHECK: [[PROP2_FALSE]]: // CHECK: [[PROP2_TRUE]]([[PROP2_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer): - // CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer - // CHECK: = apply [[PROP2_PARTIAL]]() : $@callee_guaranteed () -> UnsafeMutableRawPointer + // CHECK: [[PROP2_OBJ_COPY:%.*]] = copy_value [[PROP2_OBJ]] + // CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutableRawPointer + // CHECK: [[PROP2_PARTIAL_BORROW:%.*]] = begin_borrow [[PROP2_PARTIAL]] + // CHECK: = apply [[PROP2_PARTIAL_BORROW]]() : $@callee_guaranteed () -> UnsafeMutableRawPointer _ = prop2 } } // CHECK: // end sil function '$s4Test16testPartialApplyyySoAA_pF' diff --git a/test/ClangImporter/subclass_existentials.swift b/test/ClangImporter/subclass_existentials.swift index b7e3de51db58e..db72f6e159733 100644 --- a/test/ClangImporter/subclass_existentials.swift +++ b/test/ClangImporter/subclass_existentials.swift @@ -21,7 +21,7 @@ class SwiftLaundryService : NSLaundry { // FIXME: Consider better diagnostics here. -class OldSwiftLaundryService : NSLaundry { // expected-note 3 {{do you want to add protocol stubs?}} +class OldSwiftLaundryService : NSLaundry { // expected-error@-1 {{type 'OldSwiftLaundryService' does not conform to protocol 'NSLaundry'}} var g: Coat? = nil diff --git a/test/Constraints/ErrorBridging.swift b/test/Constraints/ErrorBridging.swift index d80afc5d06d4e..81a8a7675fc60 100644 --- a/test/Constraints/ErrorBridging.swift +++ b/test/Constraints/ErrorBridging.swift @@ -73,3 +73,24 @@ extension Error { func throwErrorCode() throws { throw FictionalServerError.meltedDown // expected-error{{thrown error code type 'FictionalServerError.Code' does not conform to 'Error'; construct an 'FictionalServerError' instance}}{{29-29=(}}{{40-40=)}} } + +class MyErrorClass { } +extension MyErrorClass: Error { } + +class MyClass { } + +func testUnknownErrorBridge(cond: Bool, mc: MyClass) -> NSObject? { + if cond { + return mc as? NSError // okay + } + + return mc as? NSObject // okay +} + +func testAlwaysErrorBridge(cond: Bool, mec: MyErrorClass) -> NSObject? { + if cond { + return mec as? NSError // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSError' always succeeds + } + + return mec as? NSObject // expected-warning{{conditional cast from 'MyErrorClass}}' to 'NSObject' always succeeds +} diff --git a/test/Constraints/assignment.swift b/test/Constraints/assignment.swift index 4bc885b0fbd57..8888050f4fd53 100644 --- a/test/Constraints/assignment.swift +++ b/test/Constraints/assignment.swift @@ -67,6 +67,7 @@ value(_) // expected-error{{'_' can only appear in a pattern or on the left side func f23798944() { let s = "" if s.count = 0 { // expected-error {{use of '=' in a boolean context, did you mean '=='?}} + // expected-error@-1{{cannot assign to property: 'count' is a get-only property}} } } diff --git a/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb b/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb index 54ff345dcfe84..52995ab17df95 100644 --- a/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb +++ b/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb @@ -72,12 +72,12 @@ func bridgeNSNumberBackToSpecificType(object: ${ObjectType}, _ = optional as? ${Type} _ = optional as! ${Type} - _ = array as [${Type}] // expected-error{{use 'as!'}} + _ = array as [${Type}] // expected-error{{cannot convert value of type '[${ObjectType}]' to type '[${Type}]' in coercion}} expected-note {{arguments to generic parameter 'Element' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = array is [${Type}] _ = array as? [${Type}] _ = array as! [${Type}] - _ = dictKeys as [${Type}: Any] // expected-error{{use 'as!'}} + _ = dictKeys as [${Type}: Any] // expected-error{{cannot convert value of type '[${ObjectType} : Any]' to type '[${Type} : Any]' in coercion}} expected-note {{arguments to generic parameter 'Key' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = dictKeys is [${Type}: Any] _ = dictKeys as? [${Type}: Any] _ = dictKeys as! [${Type}: Any] @@ -87,7 +87,7 @@ func bridgeNSNumberBackToSpecificType(object: ${ObjectType}, _ = dictKeys as? [${Type}: AnyObject] _ = dictKeys as! [${Type}: AnyObject] - _ = dictValues as [AnyHashable: ${Type}] // expected-error{{use 'as!'}} + _ = dictValues as [AnyHashable: ${Type}] // expected-error{{cannot convert value of type '[AnyHashable : ${ObjectType}]' to type '[AnyHashable : ${Type}]' in coercion}} expected-note {{arguments to generic parameter 'Value' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = dictValues is [AnyHashable: ${Type}] _ = dictValues as? [AnyHashable: ${Type}] _ = dictValues as! [AnyHashable: ${Type}] @@ -97,12 +97,12 @@ func bridgeNSNumberBackToSpecificType(object: ${ObjectType}, _ = dictValues as? [NSObject: ${Type}] _ = dictValues as! [NSObject: ${Type}] - _ = dictBoth as [${ObjectType}: ${Type}] // expected-error{{use 'as!'}} + _ = dictBoth as [${ObjectType}: ${Type}] // expected-error{{cannot convert value of type '[${ObjectType} : ${ObjectType}]' to type '[${ObjectType} : ${Type}]' in coercion}} expected-note {{arguments to generic parameter 'Value' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = dictBoth is [${ObjectType}: ${Type}] _ = dictBoth as? [${ObjectType}: ${Type}] _ = dictBoth as! [${ObjectType}: ${Type}] - _ = dictBoth as [${Type}: ${ObjectType}] // expected-error{{use 'as!'}} + _ = dictBoth as [${Type}: ${ObjectType}] // expected-error{{cannot convert value of type '[${ObjectType} : ${ObjectType}]' to type '[${Type} : ${ObjectType}]' in coercion}} expected-note {{arguments to generic parameter 'Key' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = dictBoth is [${Type}: ${ObjectType}] _ = dictBoth as? [${Type}: ${ObjectType}] _ = dictBoth as! [${Type}: ${ObjectType}] @@ -112,7 +112,7 @@ func bridgeNSNumberBackToSpecificType(object: ${ObjectType}, _ = dictBoth as? [${Type}: ${Type}] _ = dictBoth as! [${Type}: ${Type}] - _ = set as Set<${Type}> // expected-error{{use 'as!'}} + _ = set as Set<${Type}> // expected-error{{cannot convert value of type 'Set<${ObjectType}>' to type 'Set<${Type}>' in coercion}} expected-note {{arguments to generic parameter 'Element' ('${ObjectType}' and '${Type}') are expected to be equal}} _ = set is Set<${Type}> _ = set as? Set<${Type}> _ = set as! Set<${Type}> diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index f6ef698c65723..c40fb0bdf181d 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -89,7 +89,7 @@ func bridgeToObjC(_ s: BridgedStruct) -> BridgedClass { } func bridgeToAnyObject(_ s: BridgedStruct) -> AnyObject { - return s // expected-error{{return expression of type 'BridgedStruct' does not conform to 'AnyObject'}} + return s // expected-error{{return expression of type 'BridgedStruct' expected to be an instance of a class or class-constrained type}} return s as AnyObject } @@ -248,9 +248,9 @@ func rdar19770981(_ s: String, ns: NSString) { _ = ns as String > s // 'as' has lower precedence than '+' so add parens with the fixit: - s + ns // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{7-7=(}}{{9-9= as String)}} + s + ns // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{9-9= as String}} _ = s + (ns as String) - ns + s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{3-3=(}}{{5-5= as String)}} + ns + s // expected-error{{'NSString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?}}{{5-5= as String}} _ = (ns as String) + s } @@ -265,7 +265,7 @@ func rdar19831698() { var v71 = true + 1.0 // expected-error{{binary operator '+' cannot be applied to operands of type 'Bool' and 'Double'}} // expected-note@-1{{overloads for '+'}} var v72 = true + true // expected-error{{binary operator '+' cannot be applied to two 'Bool' operands}} - var v73 = true + [] // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'Array'}} + var v73 = true + [] // expected-error@:18 {{binary operator '+' cannot be applied to operands of type 'Bool' and '[Any]'}} var v75 = true + "str" // expected-error@:13 {{cannot convert value of type 'Bool' to expected argument type 'String'}} } @@ -344,14 +344,14 @@ func forceUniversalBridgeToAnyObject(a: T, b: U, c: An z = g as AnyObject z = h as AnyObject - z = a // expected-error{{does not conform to 'AnyObject'}} + z = a // expected-error{{value of type 'T' expected to be an instance of a class or class-constrained type in assignment}} z = b - z = c // expected-error{{does not conform to 'AnyObject'}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} - z = d // expected-error{{does not conform to 'AnyObject'}} + z = c // expected-error{{value of type 'Any' expected to be an instance of a class or class-constrained type in assignment}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} + z = d // expected-error{{value of type 'KnownUnbridged' expected to be an instance of a class or class-constrained type in assignment}} z = e z = f z = g - z = h // expected-error{{does not conform to 'AnyObject'}} + z = h // expected-error{{value of type 'String' expected to be an instance of a class or class-constrained type in assignment}} _ = z } diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index 8db37fb601e72..3747e66dec5c5 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -220,3 +220,21 @@ func process(p: Any?) { func compare(_: T, _: T) {} // expected-note {{'compare' declared here}} func compare(_: T?, _: T?) {} + +_ = nil? as? Int?? // expected-error {{nil literal cannot be the source of a conditional cast}} + +func test_tuple_casts_no_warn() { + struct Foo {} + + let arr: [(Any, Any)] = [(Foo(), Foo())] + let tup: (Any, Any) = (Foo(), Foo()) + + _ = arr as! [(Foo, Foo)] // Ok + _ = tup as! (Foo, Foo) // Ok + + _ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}} + _ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}} + + _ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}} + _ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}} +} diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 012aa0fdac01a..fb15d6de202ce 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -100,8 +100,8 @@ func r21544303() { inSubcall = false var v2 : Bool = false - v2 = inSubcall - { // expected-error {{cannot call value of non-function type 'Bool'}} expected-note {{did you mean to use a 'do' statement?}} {{3-3=do }} + v2 = inSubcall // expected-error {{cannot call value of non-function type 'Bool'}} + { } } @@ -114,56 +114,56 @@ func SR3671() { { consume($0) }(42) ; - ({ $0(42) } { consume($0) }) // expected-note {{callee is here}} + ({ $0(42) } { consume($0) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { print(42) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-note {{did you mean to use a 'do' statement?}} {{3-3=do }} expected-error {{cannot call value of non-function type '()'}} + { print(42) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-note {{did you mean to use a 'do' statement?}} {{3-3=do }} ; - ({ $0(42) } { consume($0) }) // expected-note {{callee is here}} + ({ $0(42) } { consume($0) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { print($0) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-error {{cannot call value of non-function type '()'}} + { print($0) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} ; - ({ $0(42) } { consume($0) }) // expected-note {{callee is here}} + ({ $0(42) } { consume($0) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { [n] in print(42) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-error {{cannot call value of non-function type '()'}} + { [n] in print(42) } // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} ; - ({ $0(42) } { consume($0) }) // expected-note {{callee is here}} + ({ $0(42) } { consume($0) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { consume($0) }(42) // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-error {{cannot call value of non-function type '()'}} + { consume($0) }(42) // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} ; - ({ $0(42) } { consume($0) }) // expected-note {{callee is here}} + ({ $0(42) } { consume($0) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { (x: Int) in consume(x) }(42) // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} expected-error {{cannot call value of non-function type '()'}} + { (x: Int) in consume(x) }(42) // expected-warning {{braces here form a trailing closure separated from its callee by multiple newlines}} ; // This is technically a valid call, so nothing goes wrong until (42) - { $0(3) } - { consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + { $0(3) } // expected-error {{cannot call value of non-function type '()'}} + { consume($0) }(42) ; - ({ $0(42) }) - { consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + ({ $0(42) }) // expected-error {{cannot call value of non-function type '()'}} + { consume($0) }(42) ; - { $0(3) } - { [n] in consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + { $0(3) } // expected-error {{cannot call value of non-function type '()'}} + { [n] in consume($0) }(42) ; - ({ $0(42) }) - { [n] in consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + ({ $0(42) }) // expected-error {{cannot call value of non-function type '()'}} + { [n] in consume($0) }(42) ; // Equivalent but more obviously unintended. - { $0(3) } // expected-note {{callee is here}} + { $0(3) } // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + { consume($0) }(42) // expected-warning@-1 {{braces here form a trailing closure separated from its callee by multiple newlines}} - ({ $0(3) }) // expected-note {{callee is here}} + ({ $0(3) }) // expected-error {{cannot call value of non-function type '()'}} expected-note {{callee is here}} - { consume($0) }(42) // expected-error {{cannot call value of non-function type '()'}} + { consume($0) }(42) // expected-warning@-1 {{braces here form a trailing closure separated from its callee by multiple newlines}} ; @@ -186,7 +186,7 @@ func testMap() { } // "UnresolvedDot" "in wrong phase" assertion from verifier -[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(@escaping (_, _) -> _)'}} +[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(_)'}} @@ -252,7 +252,7 @@ struct CC {} func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{11-11= () -> Int in }} + callCC { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} _ = $0 return 1 } @@ -273,6 +273,7 @@ func verify_NotAC_to_AC_failure(_ arg: () -> ()) { // SR-1069 - Error diagnostic refers to wrong argument class SR1069_W { func append(value: T, forKey key: Key) where Key: Hashable {} + // expected-note@-1 {{where 'Key' = 'Object?'}} } class SR1069_C { let w: SR1069_W<(AnyObject, T) -> ()> = SR1069_W() } struct S { @@ -280,9 +281,8 @@ struct S { func subscribe(object: Object?, method: (Object, T) -> ()) where Object: Hashable { let wrappedMethod = { (object: AnyObject, value: T) in } - // expected-error @+3 {{value of optional type 'Object?' must be unwrapped to a value of type 'Object'}} - // expected-note @+2{{coalesce using '??' to provide a default when the optional value contains 'nil'}} - // expected-note @+1{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} + // expected-error @+2 {{instance method 'append(value:forKey:)' requires that 'Object?' be a class type}} + // expected-note @+1 {{wrapped type 'Object' satisfies this requirement}} cs.forEach { $0.w.append(value: wrappedMethod, forKey: object) } } } @@ -292,11 +292,11 @@ func simplified1069() { class C {} struct S { func genericallyNonOptional(_ a: T, _ b: T, _ c: T) { } + // expected-note@-1 {{where 'T' = 'Optional'}} func f(_ a: C?, _ b: C?, _ c: C) { - genericallyNonOptional(a, b, c) // expected-error 2{{value of optional type 'C?' must be unwrapped to a value of type 'C'}} - // expected-note @-1 2{{coalesce}} - // expected-note @-2 2{{force-unwrap}} + genericallyNonOptional(a, b, c) // expected-error {{instance method 'genericallyNonOptional' requires that 'Optional' be a class type}} + // expected-note @-1 {{wrapped type 'C' satisfies this requirement}} } } } @@ -304,7 +304,7 @@ func simplified1069() { // Make sure we cannot infer an () argument from an empty parameter list. func acceptNothingToInt (_: () -> Int) {} func testAcceptNothingToInt(ac1: @autoclosure () -> Int) { - acceptNothingToInt({ac1($0)}) + acceptNothingToInt({ac1($0)}) // expected-error@:27 {{argument passed to call that takes no arguments}} // expected-error@-1{{contextual closure type '() -> Int' expects 0 arguments, but 1 was used in closure body}} } @@ -313,7 +313,7 @@ struct Thing { init?() {} } // This throws a compiler error -let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> Thing }} +let things = Thing().map { thing in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} // Commenting out this makes it compile _ = thing return thing @@ -322,7 +322,7 @@ let things = Thing().map { thing in // expected-error {{unable to infer complex // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> String in }} + let x: String = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} if true { return "foo" } @@ -345,7 +345,7 @@ var afterMessageCount : Int? func uintFunc() -> UInt {} func takeVoidVoidFn(_ a : () -> ()) {} takeVoidVoidFn { () -> Void in - afterMessageCount = uintFunc() // expected-error {{cannot assign value of type 'UInt' to type 'Int?'}} + afterMessageCount = uintFunc() // expected-error {{cannot assign value of type 'UInt' to type 'Int'}} } // Swift: Incorrect compile error when calling a function inside a closure @@ -360,7 +360,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{5-5=-> Int }} +[0].map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} _ in let r = (1,2).0 return r @@ -371,7 +371,7 @@ func someGeneric19997471(_ x: T) { func rdar21078316() { var foo : [String : String]? var bar : [(String, String)]? - bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '([String : String]) throws -> [(String, String)]' expects 1 argument, but 2 were used in closure body}} + bar = foo.map { ($0, $1) } // expected-error {{contextual closure type '(Dictionary) throws -> (Dictionary, _)' expects 1 argument, but 2 were used in closure body}} } @@ -406,8 +406,9 @@ func r20789423() { let p: C print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} + // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> String }} + let _f = { (v: Int) in // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} print("a") return "hi" } @@ -442,7 +443,7 @@ class C_SR_2505 : P_SR_2505 { func call(_ c: C_SR_2505) -> Bool { // Note: the diagnostic about capturing 'self', indicates that we have // selected test(_) rather than test(it:) - return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit 'self.' to make capture semantics explicit}} + return c.test { o in test(o) } // expected-error{{call to method 'test' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} expected-note{{reference 'self.' explicitly}} } } @@ -483,7 +484,7 @@ let _ = { $0 = $0 = 42 } // expected-error {{assigning a variable to itself}} let mismatchInClosureResultType : (String) -> ((Int) -> Void) = { (String) -> ((Int) -> Void) in return { } - // expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} + // expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{13-13= _ in}} } // SR-3520: Generic function taking closure with inout parameter can result in a variety of compiler errors or EXC_BAD_ACCESS @@ -495,7 +496,9 @@ struct S_3520 { var number1: Int } func sr3520_set_via_closure(_ closure: (inout S, T) -> ()) {} // expected-note {{in call to function 'sr3520_set_via_closure'}} -sr3520_set_via_closure({ $0.number1 = $1 }) // expected-error {{generic parameter 'S' could not be inferred}} +sr3520_set_via_closure({ $0.number1 = $1 }) +// expected-error@-1 {{generic parameter 'S' could not be inferred}} +// expected-error@-2 {{generic parameter 'T' could not be inferred}} // SR-3073: UnresolvedDotExpr in single expression closure @@ -714,7 +717,7 @@ func rdar37790062() { } // -typealias KeyedItem = (key: K, value: T) // expected-note {{'T' declared as parameter to type 'KeyedItem'}} +typealias KeyedItem = (key: K, value: T) protocol Node { associatedtype T @@ -728,7 +731,7 @@ extension Node { func getChild(for key:K)->(key: K, value: T) { return children.first(where: { (item:KeyedItem) -> Bool in return item.key == key - // expected-error@-1 {{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{binary operator '==' cannot be applied to two 'Self.K' operands}} })! } } @@ -760,9 +763,10 @@ overloaded { print("hi"); print("bye") } // multiple expression closure without // expected-error@-1 {{ambiguous use of 'overloaded'}} func not_overloaded(_ handler: () -> Int) {} +// expected-note@-1 {{'not_overloaded' declared here}} not_overloaded { } // empty body -// expected-error@-1 {{cannot convert value of type '() -> ()' to expected argument type '() -> Int'}} +// expected-error@-1 {{cannot convert value of type '()' to closure result type 'Int'}} not_overloaded { print("hi") } // single-expression closure // expected-error@-1 {{cannot convert value of type '()' to closure result type 'Int'}} @@ -783,7 +787,7 @@ func test() -> Int? { } var fn: () -> [Int] = {} -// expected-error@-1 {{cannot convert value of type '() -> ()' to specified type '() -> [Int]'}} +// expected-error@-1 {{cannot convert value of type '()' to closure result type '[Int]'}} fn = {} // expected-error@-1 {{cannot assign value of type '() -> ()' to type '() -> [Int]'}} @@ -817,7 +821,7 @@ func rdar_40537960() { } var arr: [S] = [] - _ = A(arr, fn: { L($0.v) }) // expected-error {{cannot convert value of type 'L' to closure result type 'R'}} + _ = A(arr, fn: { L($0.v) }) // expected-error {{cannot convert value of type 'L' to closure result type 'R

'}} // expected-error@-1 {{generic parameter 'P' could not be inferred}} // expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} {{8-8=<[S], <#P: P_40537960#>>}} } @@ -905,3 +909,41 @@ do { // via the 'T -> U => T -> ()' implicit conversion. let badResult = { (fn: () -> ()) in fn } // expected-error@-1 {{expression resolves to an unused function}} + +// rdar://problem/55102498 - closure's result type can't be inferred if the last parameter has a default value +func test_trailing_closure_with_defaulted_last() { + func foo(fn: () -> T, value: Int = 0) {} + foo { 42 } // Ok + foo(fn: { 42 }) // Ok +} + +// Test that even in multi-statement closure case we still pick up `(Action) -> Void` over `Optional<(Action) -> Void>`. +// Such behavior used to rely on ranking of partial solutions but with delayed constraint generation of closure bodies +// it's no longer the case, so we need to make sure that even in case of complete solutions we still pick the right type. + +protocol Action { } +protocol StateType { } + +typealias Fn = (Action) -> Void +typealias Middleware = (@escaping Fn, @escaping () -> State?) -> (@escaping Fn) -> Fn + +class Foo { + var state: State! + var fn: Fn! + + init(middleware: [Middleware]) { + self.fn = middleware + .reversed() + .reduce({ action in }, + { (fun, middleware) in // Ok, to type-check result type has to be `(Action) -> Void` + let dispatch: (Action) -> Void = { _ in } + let getState = { [weak self] in self?.state } + return middleware(dispatch, getState)(fun) + }) + } +} + +// Make sure that `String...` is translated into `[String]` in the body +func test_explicit_variadic_is_interpreted_correctly() { + _ = { (T: String...) -> String in T[0] + "" } // Ok +} diff --git a/test/Constraints/conditionally_defined_types.swift b/test/Constraints/conditionally_defined_types.swift index b1b51a6defae5..193d40a7f766d 100644 --- a/test/Constraints/conditionally_defined_types.swift +++ b/test/Constraints/conditionally_defined_types.swift @@ -15,7 +15,7 @@ struct Z2: AssociatedType { } struct SameType {} -extension SameType where T == X { // expected-note 5 {{where 'T' = 'Y'}} +extension SameType where T == X { // expected-note 13{{requirement specified as 'T' == 'X' [with T = Y]}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U) // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} @@ -36,25 +36,24 @@ let _ = SameType.Decl3.self let _ = SameType.Decl4.self let _ = SameType.Decl5.self -let _ = SameType.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.TypeAlias1.self // expected-error {{'SameType.TypeAlias1' (aka 'X') requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.TypeAlias2.self // expected-error {{'SameType.TypeAlias2' (aka 'Y') requires the types 'Y' and 'X' be equivalent}} let _ = SameType.TypeAlias3.self // expected-error {{'SameType.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl2.self // expected-error {{'SameType.Decl2' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl3.self // expected-error {{'SameType.Decl3' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.self // expected-error {{'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl5.self // expected-error {{'SameType.Decl5' requires the types 'Y' and 'X' be equivalent}} -extension SameType: AssociatedType where T == X {} // expected-note {{where 'T' = 'Y'}} - -// (Y first here, because there were issues caused by running associated type -// inference for the first time) -let _ = SameType.T.self // expected-error {{referencing type alias 'T' on 'SameType' requires the types 'Y' and 'X' be equivalent}} +extension SameType: AssociatedType where T == X {} +// expected-note@-1 {{requirement specified as 'T' == 'X' [with T = Y]}} let _ = SameType.T.self +let _ = SameType.T.self // expected-error {{'SameType.T' (aka 'X') requires the types 'Y' and 'X' be equivalent}} + struct Conforms {} -extension Conforms where T: P { // expected-note 5 {{where 'T' = 'Y'}} +extension Conforms where T: P { typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U) @@ -75,33 +74,33 @@ let _ = Conforms.Decl3.self let _ = Conforms.Decl4.self let _ = Conforms.Decl5.self -let _ = Conforms.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.TypeAlias1.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.TypeAlias2.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.TypeAlias3.self // expected-error {{type 'Y' does not conform to protocol 'P'}} -let _ = Conforms.Decl1.self // expected-error {{referencing struct 'Decl1' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.Decl2.self // expected-error {{referencing enum 'Decl2' on 'Conforms' requires that 'Y' conform to 'P'}} -let _ = Conforms.Decl3.self // expected-error {{referencing class 'Decl3' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.Decl1.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.Decl2.self // expected-error {{type 'Y' does not conform to protocol 'P'}} +let _ = Conforms.Decl3.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.Decl4.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.Decl5.self // expected-error {{type 'Y' does not conform to protocol 'P'}} -extension Conforms: AssociatedType where T: P {} // expected-note {{where 'T' = 'Y'}} +extension Conforms: AssociatedType where T: P {} -let _ = Conforms.T.self // expected-error {{referencing type alias 'T' on 'Conforms' requires that 'Y' conform to 'P'}} +let _ = Conforms.T.self // expected-error {{type 'Y' does not conform to protocol 'P'}} let _ = Conforms.T.self // Now, even more nesting! -extension SameType.Decl1 { // expected-note 5 {{where 'T' = 'Y'}} +extension SameType.Decl1 { typealias TypeAlias1 = T typealias TypeAlias2 = Y - typealias TypeAlias3 = (T, U) // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} + typealias TypeAlias3 = (T, U) struct Decl1 {} enum Decl2 {} class Decl3 {} - struct Decl4 {} // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} - enum Decl5 {} // expected-note {{requirement specified as 'T' == 'X' [with T = Y]}} + struct Decl4 {} + enum Decl5 {} } let _ = SameType.Decl1.TypeAlias1.self @@ -113,16 +112,16 @@ let _ = SameType.Decl1.Decl3.self let _ = SameType.Decl1.Decl4.self let _ = SameType.Decl1.Decl5.self -let _ = SameType.Decl1.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.TypeAlias3.self // expected-error {{'SameType.Decl1.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl4.self // expected-error {{'SameType.Decl1.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl1.Decl5.self // expected-error {{'SameType.Decl1.Decl5' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias2.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.TypeAlias3.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl1.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl2.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl3.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl4.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl1.Decl5.self // expected-error {{'SameType.Decl1' requires the types 'Y' and 'X' be equivalent}} -extension SameType.Decl4 where U == X { // expected-note 5 {{where 'U' = 'Y'}} +extension SameType.Decl4 where U == X { // expected-note 5 {{requirement specified as 'U' == 'X' [with U = Y]}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U, V) // expected-note {{requirement specified as 'U' == 'X' [with U = Y]}} @@ -145,12 +144,12 @@ let _ = SameType.Decl4.Decl3.self let _ = SameType.Decl4.Decl4.self let _ = SameType.Decl4.Decl5.self -let _ = SameType.Decl4.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.TypeAlias1.self // expected-error {{'SameType.Decl4.TypeAlias1' (aka 'X') requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.TypeAlias2.self // expected-error {{'SameType.Decl4.TypeAlias2' (aka 'Y') requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.TypeAlias3.self // expected-error {{'SameType.Decl4.TypeAlias3' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl1.self // expected-error {{referencing struct 'Decl1' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl2.self // expected-error {{referencing enum 'Decl2' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} -let _ = SameType.Decl4.Decl3.self // expected-error {{referencing class 'Decl3' on 'SameType.Decl4' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl1.self // expected-error {{'SameType.Decl4.Decl1' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl2.self // expected-error {{'SameType.Decl4.Decl2' requires the types 'Y' and 'X' be equivalent}} +let _ = SameType.Decl4.Decl3.self // expected-error {{'SameType.Decl4.Decl3' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.Decl4.self // expected-error {{'SameType.Decl4.Decl4' requires the types 'Y' and 'X' be equivalent}} let _ = SameType.Decl4.Decl5.self // expected-error {{'SameType.Decl4.Decl5' requires the types 'Y' and 'X' be equivalent}} @@ -174,9 +173,6 @@ let _ = SameType.Decl4.Decl5.self // expected-error {{'SameType.Decl // Finally, extra complicated: extension Conforms.Decl4 where U: AssociatedType, U.T: P { -// expected-note@-1 5 {{where 'U' = 'Y'}} -// expected-note@-2 5 {{'U.T' = 'Z2.T' (aka 'Y')}} -// expected-note@-3 5 {{'U.T' = 'Y.T'}} typealias TypeAlias1 = T typealias TypeAlias2 = Y typealias TypeAlias3 = (T, U, V) @@ -200,34 +196,41 @@ let _ = Conforms.Decl4.Decl5.self // Two different forms of badness, corresponding to the two requirements: let _ = Conforms.Decl4.TypeAlias1.self -// expected-error@-1 {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.TypeAlias2.self -// expected-error@-1 {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.TypeAlias3.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl1.self -// expected-error@-1 {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl2.self -// expected-error@-1 {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl3.self -// expected-error@-1 {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Y.T' conform to 'P'}} -// expected-error@-2 {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Y' conform to 'AssociatedType'}} +// expected-error@-1 {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}} -let _ = Conforms.Decl4.TypeAlias1.self // expected-error {{referencing type alias 'TypeAlias1' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.TypeAlias2.self // expected-error {{referencing type alias 'TypeAlias2' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} +let _ = Conforms.Decl4.TypeAlias1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.TypeAlias2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.TypeAlias3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} -let _ = Conforms.Decl4.Decl1.self // expected-error {{referencing struct 'Decl1' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.Decl2.self // expected-error {{referencing enum 'Decl2' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} -let _ = Conforms.Decl4.Decl3.self // expected-error {{referencing class 'Decl3' on 'Conforms.Decl4' requires that 'Z2.T' (aka 'Y') conform to 'P'}} +let _ = Conforms.Decl4.Decl1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.Decl2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} +let _ = Conforms.Decl4.Decl3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} + +// rdar://problem/45271663 +protocol PP { associatedtype V } +struct SS {} +enum EE {} +extension EE : PP where A : PP, B : PP { typealias V = EE } + +protocol P2 { associatedtype U } +func f(s: SS) -> SS> where PI.U : PP, PI.V : P2 { + let t: EE.V + // expected-error@-1 {{type 'PI.V.U' does not conform to protocol 'PP'}} +} diff --git a/test/Constraints/construction.swift b/test/Constraints/construction.swift index af3034af9b793..b99ff42dbda63 100644 --- a/test/Constraints/construction.swift +++ b/test/Constraints/construction.swift @@ -60,6 +60,7 @@ Optional(1) // expected-warning{{unused}} Optional(1) // expected-warning{{unused}} _ = .none as Optional Optional(.none) // expected-error{{generic parameter 'T' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{9-9=}} +// expected-error@-1 {{cannot infer contextual base in reference to member 'none'}} // Interpolation _ = "\(hello), \(world) #\(i)!" @@ -215,6 +216,7 @@ func rdar_50668864() { struct Foo { init(anchors: [Int]) { // expected-note {{'init(anchors:)' declared here}} self = .init { _ in [] } // expected-error {{trailing closure passed to parameter of type '[Int]' that does not accept a closure}} + // expected-error@-1 {{generic parameter 'Element' could not be inferred}} } } } diff --git a/test/Constraints/diag_missing_arg.swift b/test/Constraints/diag_missing_arg.swift index 9cf2548d8e964..a7e5a2e8c38ae 100644 --- a/test/Constraints/diag_missing_arg.swift +++ b/test/Constraints/diag_missing_arg.swift @@ -19,11 +19,11 @@ func attributedInOutFunc(x: inout @convention(c) () -> Int32) {} // expected-not attributedInOutFunc() // expected-error {{missing argument for parameter 'x' in call}} {{21-21=x: &<#@convention(c) () -> Int32#>}} func genericFunc1(x: T) {} // expected-note * {{here}} -genericFunc1() // expected-error {{missing argument for parameter 'x' in call}} {{14-14=x: <#Any#>}} +genericFunc1() // expected-error {{missing argument for parameter 'x' in call}} {{14-14=x: <#_#>}} protocol P {} func genericFunc2(x: T) {} // expected-note * {{here}} -genericFunc2() // expected-error {{missing argument for parameter 'x' in call}} {{14-14=x: <#Any#>}} +genericFunc2() // expected-error {{missing argument for parameter 'x' in call}} {{14-14=x: <#_#>}} typealias MyInt = Int func aliasedFunc(x: MyInt) {} // expected-note * {{here}} @@ -34,8 +34,12 @@ trailingClosureSingle1 { 1 } // expected-error {{missing argument for parameter trailingClosureSingle1() { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{24-24=x: <#Int#>}} func trailingClosureSingle2(x: () -> Int, y: Int) {} // expected-note * {{here}} -trailingClosureSingle2 { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} -trailingClosureSingle2() { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} +trailingClosureSingle2 { 1 } +// expected-error@-1 {{missing argument for parameter 'x' in call}} {{23-23=(x: <#() -> Int#>)}} +// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} +trailingClosureSingle2() { 1 } +// expected-error@-1 {{missing argument for parameter 'x' in call}} {{24-24=x: <#() -> Int#>}} +// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} func trailingClosureMulti1(x: Int, y: Int, z: () -> Int) {} // expected-note * {{here}} trailingClosureMulti1(y: 1) { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=x: <#Int#>, }} @@ -43,9 +47,15 @@ trailingClosureMulti1(x: 1) { 1 } // expected-error {{missing argument for param trailingClosureMulti1(x: 1, y: 1) // expected-error {{missing argument for parameter 'z' in call}} {{33-33=, z: <#() -> Int#>}} func trailingClosureMulti2(x: Int, y: () -> Int, z: Int) {} // expected-note * {{here}} -trailingClosureMulti2 { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} -trailingClosureMulti2() { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} -trailingClosureMulti2(x: 1) { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} +trailingClosureMulti2 { 1 } +// expected-error@-1 {{missing arguments for parameters 'x', 'y' in call}} +// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} +trailingClosureMulti2() { 1 } +// expected-error@-1 {{missing arguments for parameters 'x', 'y' in call}} +// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} +trailingClosureMulti2(x: 1) { 1 } +// expected-error@-1 {{missing argument for parameter 'y' in call}} {{27-27=, y: <#() -> Int#>}} +// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} func param2Func(x: Int, y: Int) {} // expected-note * {{here}} param2Func(x: 1) // expected-error {{missing argument for parameter 'y' in call}} {{16-16=, y: <#Int#>}} diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 05401f7a4958a..c603df2c04b76 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -31,6 +31,7 @@ func f5(_ : T) { } // expected-note@-1 {{required by global function 'f5' where 'T' = '(Int) -> Int'}} // expected-note@-2 {{required by global function 'f5' where 'T' = '(Int, String)'}} // expected-note@-3 {{required by global function 'f5' where 'T' = 'Int.Type'}} +// expected-note@-4 {{where 'T' = 'Int'}} func f6(_ t: T, _ u: U) where T.SomeType == U.SomeType {} @@ -45,7 +46,7 @@ f1( ) // Tuple element unused. -f0(i, i, +f0(i, i, // expected-error@:7 {{cannot convert value of type 'Int' to expected argument type 'Float'}} i) // expected-error{{extra argument in call}} @@ -71,21 +72,21 @@ f3( f4(i, d) // expected-error {{extra argument in call}} // Missing member. -i.wobble() // expected-error{{value of type 'Int' has no member 'wobble'}} +i.missingMember() // expected-error{{value of type 'Int' has no member 'missingMember'}} // Generic member does not conform. extension Int { - func wibble(_ x: T, _ y: T) -> T { return x } + func wibble(_ x: T, _ y: T) -> T { return x } // expected-note {{where 'T' = 'Int'}} func wubble(_ x: (Int) -> T) -> T { return x(self) } } -i.wibble(3, 4) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}} +i.wibble(3, 4) // expected-error {{instance method 'wibble' requires that 'Int' conform to 'P2'}} // Generic member args correct, but return type doesn't match. struct A : P2 { func wonka() {} } let a = A() -for j in i.wibble(a, a) { // expected-error {{type 'A' does not conform to protocol 'Sequence'}} +for j in i.wibble(a, a) { // expected-error {{for-in loop requires 'A' to conform to 'Sequence'}} } // Generic as part of function/tuple types @@ -98,12 +99,12 @@ func f7() -> (c: Int, v: A) { return f6(g) // expected-error {{cannot convert return expression of type '(c: Int, i: A)' to return type '(c: Int, v: A)'}} } -func f8(_ n: T, _ f: @escaping (T) -> T) {} -// expected-note@-1 {{required by global function 'f8' where 'T' = 'Tup' (aka '(Int, Double)')}} -f8(3, f4) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}} +func f8(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' = 'Int'}} +// expected-note@-1 {{required by global function 'f8' where 'T' = '(Int, Double)'}} +f8(3, f4) // expected-error {{global function 'f8' requires that 'Int' conform to 'P2'}} typealias Tup = (Int, Double) func f9(_ x: Tup) -> Tup { return x } -f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} +f8((1,2.0), f9) // expected-error {{type '(Int, Double)' cannot conform to 'P2'; only struct/enum/class types can conform to protocols}} // QoI: Incorrect diagnostic for calling nonexistent members on literals 1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}} @@ -111,7 +112,7 @@ f8((1,2.0), f9) // expected-error {{type 'Tup' (aka '(Int, Double)') cannot conf "awfawf".doesntExist(0) // expected-error {{value of type 'String' has no member 'doesntExist'}} // Does not conform to protocol. -f5(i) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}} +f5(i) // expected-error {{global function 'f5' requires that 'Int' conform to 'P2'}} // Make sure we don't leave open existentials when diagnosing. // @@ -145,7 +146,7 @@ func ***~(_: Int, _: String) { } i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} @available(*, unavailable, message: "call the 'map()' method on the sequence") -public func myMap( // expected-note {{'myMap' has been explicitly marked unavailable here}} +public func myMap( _ source: C, _ transform: (C.Iterator.Element) -> T ) -> [T] { fatalError("unavailable function can't be called") @@ -158,7 +159,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{'myMap' is unavailable: call the 'map()' method on the sequence}} + myMap(0..<10, { x in // expected-error{{unable to infer complex closure return type; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} () return x }) @@ -168,7 +169,7 @@ func rdar20142523() { func rdar21080030() { var s = "Hello" // SR-7599: This should be `cannot_call_non_function_value` - if s.count() == 0 {} // expected-error{{cannot invoke 'count' with no arguments}} + if s.count() == 0 {} // expected-error{{cannot call value of non-function type 'Int'}} {{13-15=}} } // QoI: problem with return type inference mis-diagnosed as invalid arguments @@ -287,7 +288,7 @@ func r18800223(_ i : Int) { var buttonTextColor: String? - _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{type of expression is ambiguous without more context}} + _ = (buttonTextColor != nil) ? 42 : {$0}; // expected-error {{result values in '? :' expression have mismatching types 'Int' and '(_) -> _'}} } // Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back @@ -430,11 +431,13 @@ CurriedClass.method1(c)() _ = CurriedClass.method1(c) CurriedClass.method1(c)(1) // expected-error {{argument passed to call that takes no arguments}} CurriedClass.method1(2.0)(1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'CurriedClass'}} +// expected-error@-1:27 {{argument passed to call that takes no arguments}} CurriedClass.method2(c)(32)(b: 1) // expected-error{{extraneous argument label 'b:' in call}} _ = CurriedClass.method2(c) _ = CurriedClass.method2(c)(32) _ = CurriedClass.method2(1,2) // expected-error {{extra argument in call}} +// expected-error@-1 {{instance member 'method2' cannot be used on type 'CurriedClass'; did you mean to use a value of this type instead?}} CurriedClass.method2(c)(1.0)(b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} // expected-error@-1 {{extraneous argument label 'b:' in call}} CurriedClass.method2(c)(1)(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} @@ -449,7 +452,9 @@ _ = CurriedClass.method3(1, 2) // expected-error {{instance member 'me // expected-error@-1 {{missing argument label 'b:' in call}} CurriedClass.method3(c)(1.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} CurriedClass.method3(c)(1) // expected-error {{missing argument for parameter 'b' in call}} -CurriedClass.method3(c)(c: 1.0) // expected-error {{missing argument for parameter #1 in call}} expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} +CurriedClass.method3(c)(c: 1.0) // expected-error {{incorrect argument labels in call (have 'c:', expected '_:b:')}} +// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} +// expected-error@-2 {{missing argument for parameter #1 in call}} extension CurriedClass { @@ -487,14 +492,16 @@ enum Color { static func rainbow() -> Color {} static func overload(a : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(a:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (a: Int)}} static func overload(b : Int) -> Color {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(b:)')}} + // expected-note@-1 {{candidate has partially matching parameter list (b: Int)}} static func frob(_ a : Int, b : inout Int) -> Color {} static var svar: Color { return .Red } } -// FIXME: This used to be better: "'map' produces '[T]', not the expected contextual result type '(Int, Color)'" -let _: (Int, Color) = [1,2].map({ ($0, .Unknown("")) }) // expected-error {{expression type '((Int) throws -> _) throws -> Array<_>' is ambiguous without more context}} +let _: (Int, Color) = [1,2].map({ ($0, .Unknown("")) }) // expected-error {{cannot convert value of type 'Array<(Int, _)>' to specified type '(Int, Color)'}} +// expected-error@-1 {{cannot infer contextual base in reference to member 'Unknown'}} let _: [(Int, Color)] = [1,2].map({ ($0, .Unknown("")) })// expected-error {{missing argument label 'description:' in call}} @@ -502,18 +509,17 @@ let _: [Color] = [1,2].map { _ in .Unknown("") }// expected-error {{missing argu let _: (Int) -> (Int, Color) = { ($0, .Unknown("")) } // expected-error {{missing argument label 'description:' in call}} {{48-48=description: }} let _: Color = .Unknown("") // expected-error {{missing argument label 'description:' in call}} {{25-25=description: }} -let _: Color = .Unknown // expected-error {{member 'Unknown' expects argument of type '(description: String)'}} +let _: Color = .Unknown // expected-error {{member 'Unknown(description:)' expects argument of type 'String'}} let _: Color = .Unknown(42) // expected-error {{missing argument label 'description:' in call}} // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}} let _ : Color = .rainbow(42) // expected-error {{argument passed to call that takes no arguments}} -let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type 'Double' to specified type 'Int'}} +let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type '(Double, Float)' to specified type '(Int, Float)'}} -let _ : Color = .rainbow // expected-error {{member 'rainbow' is a function; did you mean to call it?}} {{25-25=()}} +let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}} let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -let _: Color = .overload(1.0) // expected-error {{ambiguous reference to member 'overload'}} -// expected-note @-1 {{overloads for 'overload' exist with these partially matching parameter lists: (a: Int), (b: Int)}} +let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .overload(1) // expected-error {{no exact matches in call to static method 'overload'}} let _: Color = .frob(1.0, &i) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} @@ -524,10 +530,10 @@ let _: Color = .frob(1, b: i) // expected-error {{passing value of type 'Int' t let _: Color = .frob(1, &d) // expected-error {{missing argument label 'b:' in call}} // expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Int'}} let _: Color = .frob(1, b: &d) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} -var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} -someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'}} -someColor = .svar() // expected-error {{static property 'svar' is not a function}} -someColor = .svar(1) // expected-error {{static property 'svar' is not a function}} +var someColor : Color = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} +someColor = .red // expected-error {{enum type 'Color' has no case 'red'; did you mean 'Red'?}} +someColor = .svar() // expected-error {{cannot call value of non-function type 'Color'}} +someColor = .svar(1) // expected-error {{cannot call value of non-function type 'Color'}} func testTypeSugar(_ a : Int) { typealias Stride = Int @@ -548,7 +554,7 @@ protocol r22020088P {} func r22020088Foo(_ t: T) {} func r22020088bar(_ p: r22020088P?) { - r22020088Foo(p.fdafs) // expected-error {{value of type 'r22020088P?' has no member 'fdafs'}} + r22020088Foo(p.fdafs) // expected-error {{value of type 'r22020088P?' has no member 'fdafs'}} } // QoI: poor diagnostic involving closure, bad parameter label, and mismatch return type @@ -696,11 +702,10 @@ let a = safeAssign // expected-error {{generic parameter 'T' could not be inferr // QoI: Incorrect 'add ()' fixit with trailing closure struct Radar21692808 { - init(count: Int, value: Element) {} + init(count: Int, value: Element) {} // expected-note {{'init(count:value:)' declared here}} } func radar21692808() -> Radar21692808 { - return Radar21692808(count: 1) { // expected-error {{cannot invoke initializer for type 'Radar21692808' with an argument list of type '(count: Int, @escaping () -> Int)'}} - // expected-note @-1 {{expected an argument list of type '(count: Int, value: Element)'}} + return Radar21692808(count: 1) { // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}} return 1 } } @@ -906,7 +911,7 @@ struct rdar27891805 { } try rdar27891805(contentsOfURL: nil, usedEncoding: nil) -// expected-error@-1 {{incorrect argument labels in call (have 'contentsOfURL:usedEncoding:', expected 'contentsOf:encoding:')}} +// expected-error@-1 {{incorrect argument label in call (have 'contentsOfURL:usedEncoding:', expected 'contentsOf:usedEncoding:')}} // expected-error@-2 {{'nil' is not compatible with expected argument type 'String'}} // expected-error@-3 {{'nil' is not compatible with expected argument type 'String'}} @@ -916,13 +921,13 @@ class NSCache { } class CacheValue { - func value(x: Int) -> Int {} // expected-note {{found this candidate}} - func value(y: String) -> String {} // expected-note {{found this candidate}} + func value(x: Int) -> Int {} // expected-note {{found candidate with type '(Int) -> Int'}} + func value(y: String) -> String {} // expected-note {{found candidate with type '(String) -> String'}} } func valueForKey(_ key: K) -> CacheValue? { let cache = NSCache() - return cache.object(forKey: key)?.value // expected-error {{ambiguous reference to member 'value(x:)'}} + return cache.object(forKey: key)?.value // expected-error {{no exact matches in call to instance method 'value'}} } // SR-2242: poor diagnostic when argument label is omitted @@ -943,7 +948,7 @@ r27212391(y: 3, 5) // expected-error {{incorrect argument label in call r27212391(x: 3, x: 5) // expected-error {{extraneous argument label 'x:' in call}} r27212391(a: 1, 3, y: 5) // expected-error {{incorrect argument labels in call (have 'a:_:y:', expected 'a:x:_:')}} r27212391(1, x: 3, y: 5) // expected-error {{incorrect argument labels in call (have '_:x:y:', expected 'a:x:_:')}} -r27212391(a: 1, y: 3, x: 5) // expected-error {{incorrect argument labels in call (have 'a:y:x:', expected 'a:x:_:')}} {{17-18=x}} {{23-26=}} +r27212391(a: 1, y: 3, x: 5) // expected-error {{incorrect argument labels in call (have 'a:y:x:', expected 'a:x:_:')}} r27212391(a: 1, 3, x: 5) // expected-error {{argument 'x' must precede unnamed argument #2}} {{17-17=x: 5, }} {{18-24=}} // SR-1255 @@ -1140,24 +1145,6 @@ for var i in 0..<10 { // expected-warning {{variable 'i' was never mutated; cons _ = i + 1 } -// rdar://problem/32726044 - shrink reduced domains too far - -public protocol P_32726044 {} - -extension Int: P_32726044 {} -extension Float: P_32726044 {} - -public func *(lhs: P_32726044, rhs: P_32726044) -> Double { - fatalError() -} - -func rdar32726044() -> Float { - var f: Float = 0 - f = Float(1) * 100 // Ok - let _: Float = Float(42) + 0 // Ok - return f -} - // SR-5045 - Attempting to return result of reduce(_:_:) in a method with no return produces ambiguous error func sr5045() { let doubles: [Double] = [1, 2, 3] @@ -1254,22 +1241,15 @@ func rdar17170728() { var j: Int? var k: Int? = 2 - let _ = [i, j, k].reduce(0 as Int?) { // expected-error 3 {{cannot convert value of type 'Int?' to expected element type 'Bool'}} - // expected-error@-1 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} + let _ = [i, j, k].reduce(0 as Int?) { $0 && $1 ? $0! + $1! : ($0 ? $0! : ($1 ? $1! : nil)) - // expected-error@-1 {{binary operator '+' cannot be applied to two 'Bool' operands}} - // expected-error@-2 4 {{cannot force unwrap value of non-optional type 'Bool'}} - // expected-error@-3 {{value of optional type 'Bool?' must be unwrapped to a value of type 'Bool'}} - // expected-note@-4 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} - // expected-note@-5 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} + // expected-error@-1 4 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} } - let _ = [i, j, k].reduce(0 as Int?) { // expected-error 3 {{cannot convert value of type 'Int?' to expected element type 'Bool'}} - // expected-error@-1 {{missing argument label 'into:' in call}} - // expected-error@-2 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} + let _ = [i, j, k].reduce(0 as Int?) { $0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil)) - // expected-error@-1 {{binary operator '+' cannot be applied to operands of type '@lvalue Bool' and 'Bool'}} - // expected-error@-2 {{'nil' cannot be used in context expecting type 'Bool'}} + // expected-error@-1 {{binary operator '+' cannot be applied to two 'Int?' operands}} + // expected-error@-2 4 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} } } @@ -1291,21 +1271,24 @@ func badTypes() { // rdar://34357545 func unresolvedTypeExistential() -> Bool { return (Int.self==_{}) - // expected-error@-1 {{ambiguous reference to member '=='}} + // expected-error@-1 {{'_' can only appear in a pattern or on the left side of an assignment}} } func rdar43525641(_ a: Int, _ b: Int = 0, c: Int = 0, _ d: Int) {} rdar43525641(1, c: 2, 3) // Ok -struct Array {} -let foo: Swift.Array = Array() // expected-error {{cannot convert value of type 'Array' to specified type 'Array'}} +do { + struct Array {} + let foo: Swift.Array = Array() // expected-error {{cannot convert value of type 'Array' to specified type 'Array'}} + // expected-error@-1 {{generic parameter 'Element' could not be inferred}} -struct Error {} -let bar: Swift.Error = Error() //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} -let baz: (Swift.Error) = Error() //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} -let baz2: Swift.Error = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} -let baz3: (Swift.Error) = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} -let baz4: ((Swift.Error)) = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} + struct Error {} + let bar: Swift.Error = Error() //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} + let baz: (Swift.Error) = Error() //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} + let baz2: Swift.Error = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} + let baz3: (Swift.Error) = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} + let baz4: ((Swift.Error)) = (Error()) //expected-error {{value of type 'diagnostics.Error' does not conform to specified type 'Swift.Error'}} +} // SyntaxSugarTypes with unresolved types func takesGenericArray(_ x: [T]) {} @@ -1319,16 +1302,16 @@ takesArrayOfSetOfGenericArrays(1) // expected-error {{cannot convert value of ty func takesArrayOfGenericOptionals(_ x: [T?]) {} takesArrayOfGenericOptionals(1) // expected-error {{cannot convert value of type 'Int' to expected argument type '[Int?]'}} func takesGenericDictionary(_ x: [T : U]) {} // expected-note {{in call to function 'takesGenericDictionary'}} -takesGenericDictionary(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '[Any : Any]'}} +takesGenericDictionary(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '[T : U]'}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} // expected-error@-2 {{generic parameter 'U' could not be inferred}} typealias Z = Int func takesGenericDictionaryWithTypealias(_ x: [T : Z]) {} // expected-note {{in call to function 'takesGenericDictionaryWithTypealias'}} -takesGenericDictionaryWithTypealias(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '[Any : Z]' (aka 'Dictionary'}} +takesGenericDictionaryWithTypealias(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '[T : Z]'}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} func takesGenericFunction(_ x: ([T]) -> Void) {} // expected-note {{in call to function 'takesGenericFunction'}} -takesGenericFunction(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([Any]) -> Void'}} +takesGenericFunction(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([T]) -> Void'}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} func takesTuple(_ x: ([T], [T])) {} // expected-note {{in call to function 'takesTuple'}} -takesTuple(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([Any], [Any])'}} +takesTuple(true) // expected-error {{cannot convert value of type 'Bool' to expected argument type '([T], [T])'}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} diff --git a/test/Constraints/diagnostics_swift4.swift b/test/Constraints/diagnostics_swift4.swift index b053ed39632fa..3cffe9d38ab67 100644 --- a/test/Constraints/diagnostics_swift4.swift +++ b/test/Constraints/diagnostics_swift4.swift @@ -24,7 +24,7 @@ extension C_2505 { class C2_2505: P_2505 { } -let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{incorrect argument label in call (have 'arg:', expected 'from:')}} +let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{extraneous argument label 'arg:' in call}} // rdar://problem/31898542 - Swift 4: 'type of expression is ambiguous without more context' errors, without a fixit diff --git a/test/Constraints/dynamic_lookup.swift b/test/Constraints/dynamic_lookup.swift index 43c36a6c09f82..bf13cf25fe960 100644 --- a/test/Constraints/dynamic_lookup.swift +++ b/test/Constraints/dynamic_lookup.swift @@ -267,7 +267,7 @@ func rdar29960565(_ o: AnyObject) { @objc class DynamicIUO : NSObject, Q { @objc var t: String! = "" - @objc func bar() -> String! {} + @objc func baz() -> String! {} @objc subscript(_: DynamicIUO) -> DynamicIUO! { get { return self @@ -299,11 +299,11 @@ let _: String = o.t let _: String = o.t! let _: String = o.t!! let _: String? = o.t -let _: String = o.bar() -let _: String = o.bar!() -let _: String = o.bar()! -let _: String = o.bar!()! -let _: String? = o.bar() +let _: String = o.baz() +let _: String = o.baz!() +let _: String = o.baz()! +let _: String = o.baz!()! +let _: String? = o.baz() let _: DynamicIUO = o[dyn_iuo] let _: DynamicIUO = o[dyn_iuo]! let _: DynamicIUO = o[dyn_iuo]!! @@ -389,3 +389,22 @@ func testAnyObjectAmbiguity(_ x: AnyObject) { // FIX-ME(SR-8611): This is currently ambiguous but shouldn't be. _ = x[unambiguousSubscript: ""] // expected-error {{ambiguous use of 'subscript(unambiguousSubscript:)'}} } + +// SR-11648 +class HasMethodWithDefault { + @objc func hasDefaultParam(_ x: Int = 0) {} +} + +func testAnyObjectWithDefault(_ x: AnyObject) { + x.hasDefaultParam() +} + +// SR-11829: Don't perform dynamic lookup for callAsFunction. +class ClassWithObjcCallAsFunction { + @objc func callAsFunction() {} +} + +func testCallAsFunctionAnyObject(_ x: AnyObject) { + x() // expected-error {{cannot call value of non-function type 'AnyObject'}} + x.callAsFunction() // Okay. +} diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index e4f1bb77ae36c..dbfe34ec5ca59 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -22,15 +22,14 @@ let _ = arr.map(E.bar) // Ok let _ = arr.map(E.two) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int, Int) -> E)'}} // expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} -let _ = arr.map(E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = arr.map(G_E.foo) // Ok let _ = arr.map(G_E.bar) // Ok -let _ = arr.map(G_E.two) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (String, String) -> G_E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} -let _ = arr.map(G_E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> G_E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(G_E.two) // expected-error {{cannot convert value of type '(String, String) -> G_E' to expected argument type '(String) throws -> G_E'}} +let _ = arr.map(G_E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}} let _ = E.bar("hello") // Ok @@ -97,15 +96,17 @@ foo(Foo.a, Foo.b) // Ok in Swift 4 because we strip labels from the arguments // rdar://problem/32551313 - Useless SE-0110 diagnostic -enum E_32551313 { +enum E_32551313 { // expected-note {{'R' declared as parameter to type 'E_32551313'}} case Left(L) case Right(R) } struct Foo_32551313 { + // FIXME(diagnostics): We should be able to figure out L and R from contextual type static func bar() -> E_32551313<(String, Foo_32551313?), (String, String)>? { - return E_32551313.Left("", Foo_32551313()) // expected-error {{enum case 'Left' expects a single parameter of type 'L' [with L = (String, Foo_32551313?)]}} - // expected-note@-1 {{did you mean to pass a tuple?}} {{28-28=(}} {{46-46=)}} + return E_32551313.Left("", Foo_32551313()) // expected-error {{extra argument in call}} + // expected-error@-1 {{generic parameter 'R' could not be inferred}} expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} + // expected-error@-2 {{cannot convert return expression of type 'E_32551313' to return type 'E_32551313<(String, Foo_32551313?), (String, String)>?'}} } } @@ -120,7 +121,7 @@ func rdar34583132() { func bar(_ s: S) { guard s.foo(1 + 2) == .timeout else { - // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'}} + // expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'?}} fatalError() } } diff --git a/test/Constraints/fixes.swift b/test/Constraints/fixes.swift index 028977eeafde7..088672386864f 100644 --- a/test/Constraints/fixes.swift +++ b/test/Constraints/fixes.swift @@ -167,8 +167,9 @@ struct S1116 { } let a1116: [S1116] = [] -var s1116 = Set(1...10).subtracting(a1116.map({ $0.s })) // expected-error {{cannot convert value of type '[Int?]' to expected argument type 'Set'}} - +var s1116 = Set(1...10).subtracting(a1116.map({ $0.s })) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} +// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49=(}} {{53-53= ?? <#default value#>)}} +// expected-note@-2{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{53-53=!}} func moreComplexUnwrapFixes() { struct S { @@ -198,14 +199,14 @@ func moreComplexUnwrapFixes() { // expected-note@-2{{force-unwrap using '!'}}{{12-12=!}} takeOpt(t.optS.value) // expected-error{{value of optional type 'T?' must be unwrapped to refer to member 'optS' of wrapped base type 'T'}} - // expected-note@-1{{chain the optional using '?'}}{{17-17=?}} + // expected-note@-1{{chain the optional using '?'}}{{12-12=?}} // expected-error@-2{{value of optional type 'S?' must be unwrapped to refer to member 'value' of wrapped base type 'S'}} - // expected-note@-3{{chain the optional using '?'}}{{12-12=?}} + // expected-note@-3{{chain the optional using '?'}}{{17-17=?}} takeNon(t.optS.value) // expected-error{{value of optional type 'T?' must be unwrapped to refer to member 'optS' of wrapped base type 'T'}} - // expected-note@-1{{chain the optional using '?'}}{{17-17=?}} + // expected-note@-1{{chain the optional using '?'}}{{12-12=?}} // expected-error@-2{{value of optional type 'S?' must be unwrapped to refer to member 'value' of wrapped base type 'S'}} - // expected-note@-3{{chain the optional using '?'}}{{12-12=?}} + // expected-note@-3{{chain the optional using '?'}}{{17-17=?}} // expected-note@-4{{force-unwrap using '!'}}{{17-17=!}} takeNon(os?.value) // expected-error{{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}} diff --git a/test/Constraints/function.swift b/test/Constraints/function.swift index 596bf9d52a34f..be6b85bde7f12 100644 --- a/test/Constraints/function.swift +++ b/test/Constraints/function.swift @@ -85,7 +85,7 @@ sr590(()) sr590((1, 2)) // SR-2657: Poor diagnostics when function arguments should be '@escaping'. -private class SR2657BlockClass { // expected-note 3 {{generic parameters are always considered '@escaping'}} +private class SR2657BlockClass { // expected-note 4 {{generic parameters are always considered '@escaping'}} let f: T init(f: T) { self.f = f } } @@ -94,7 +94,7 @@ func takesAny(_: Any) {} func foo(block: () -> (), other: () -> Int) { let _ = SR2657BlockClass(f: block) - // expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}} + // expected-error@-1 {{converting non-escaping parameter 'block' to generic parameter 'T' may allow it to escape}} let _ = SR2657BlockClass<()->()>(f: block) // expected-error@-1 {{converting non-escaping parameter 'block' to generic parameter 'T' may allow it to escape}} let _: SR2657BlockClass<()->()> = SR2657BlockClass(f: block) diff --git a/test/Constraints/function_builder.swift b/test/Constraints/function_builder.swift index b83be4c27faaf..19a211e2096ab 100644 --- a/test/Constraints/function_builder.swift +++ b/test/Constraints/function_builder.swift @@ -8,6 +8,10 @@ enum Either { @_functionBuilder struct TupleBuilder { + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { return (t1, t2) } @@ -316,6 +320,57 @@ func testAcceptColorTagged(b: Bool, i: Int, s: String, d: Double) { testAcceptColorTagged(b: true, i: 17, s: "Hello", d: 3.14159) +// Use buildExpression() when it's available. +enum Component { + case string(StaticString) + case floating(Double) + case color(Color) + indirect case array([Component]) + indirect case optional(Component?) +} + +@_functionBuilder +struct ComponentBuilder { + static func buildExpression(_ string: StaticString) -> Component { + return .string(string) + } + + static func buildExpression(_ float: Double) -> Component { + return .floating(float) + } + + static func buildExpression(_ color: Color) -> Component { + return .color(color) + } + + static func buildBlock(_ components: Component...) -> Component { + return .array(components) + } + + static func buildIf(_ value: Component?) -> Component { + return .optional(value) + } +} + +func acceptComponentBuilder(@ComponentBuilder _ body: () -> Component) { + print(body()) +} + +func colorWithAutoClosure(_ color: @autoclosure () -> Color) -> Color { + return color() +} + +var trueValue = true +acceptComponentBuilder { + "hello" + if trueValue { + 3.14159 + colorWithAutoClosure(.red) + } + .red +} +// CHECK: array([main.Component.string("hello"), main.Component.optional(Optional(main.Component.array([main.Component.floating(3.14159), main.Component.color(main.Color.red)]))), main.Component.color(main.Color.red)]) + // rdar://53325810 // Test that we don't have problems with expression pre-checking when @@ -364,3 +419,60 @@ testForEach1.show() // CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.first("begin", true)) // CHECK: ("testForEach1", main.Either<(Swift.String, Swift.Bool), (Swift.Bool, Swift.String)>.second(true, "end")) + +func test_single_stmt_closure_support() { + @_functionBuilder + struct MyBuilder { + static func buildBlock(_ numbers: Int...) -> Int { + return 42 + } + } + + func test(@MyBuilder builder: () -> Int) -> Int { + builder() + } + + let _ = test { 0 } // ok +} + +// Check a case involving nested closures that refer to parameters of their +// enclosing closures. +struct X { + init(_ c: C, @TupleBuilder body: (C.Element) -> T) { } +} + +struct Y { + init(@TupleBuilder body: () -> T) { } +} + +struct Z { + init(@TupleBuilder body: () -> T) { } +} + +func testNestedClosuresWithDependencies(cond: Bool) { + tuplify(cond) { _ in + X([1, 2, 3]) { x in + Y { + Z { + x + 1 + } + } + } + } +} + +// Check that we can handle multiple conditions in an 'if' statement. +func testIfConditions(cond: Bool, c1: Bool, i1: Int, i2: Int) { + tuplify(cond) { x in + "testIfConditions" + if i1 == i2, c1, x { + 1 + "hello" + } + 3.14159 + } +} +testIfConditions(cond: true, c1: true, i1: 1, i2: 1) +// CHECK: testIfConditions +// CHECK-SAME: hello diff --git a/test/Constraints/function_builder_availability.swift b/test/Constraints/function_builder_availability.swift new file mode 100644 index 0000000000000..ac63adc4a7f7f --- /dev/null +++ b/test/Constraints/function_builder_availability.swift @@ -0,0 +1,74 @@ +// RUN: %target-typecheck-verify-swift -target x86_64-apple-macosx10.50 + +// REQUIRES: OS=macosx + +enum Either { + case first(T) + case second(U) +} + +@_functionBuilder +struct TupleBuilder { + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + static func buildDo(_ value: T) -> T { return value } + static func buildIf(_ value: T?) -> T? { return value } + + static func buildEither(first value: T) -> Either { + return .first(value) + } + static func buildEither(second value: U) -> Either { + return .second(value) + } +} + +func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { + print(body(cond)) +} + +@available(OSX, introduced: 10.9) +func globalFuncAvailableOn10_9() -> Int { return 9 } + +@available(OSX, introduced: 10.51) +func globalFuncAvailableOn10_51() -> Int { return 10 } + +@available(OSX, introduced: 10.52) +func globalFuncAvailableOn10_52() -> Int { return 11 } + +tuplify(true) { cond in + globalFuncAvailableOn10_9() + if #available(OSX 10.51, *) { + globalFuncAvailableOn10_51() + tuplify(false) { cond2 in + if cond, #available(OSX 10.52, *) { + cond2 + globalFuncAvailableOn10_52() + } else { + globalFuncAvailableOn10_52() // expected-error{{'globalFuncAvailableOn10_52()' is only available in macOS 10.52 or newer}} + // expected-note@-1{{add 'if #available' version check}} + } + } + } +} diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index d7de38e317334..8769496a8bb7b 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -144,7 +144,7 @@ struct TupleP : P { @_functionBuilder struct Builder { - static func buildBlock(_ stmt1: S0, _ stmt2: S1) // expected-note {{required by static method 'buildBlock' where 'S1' = 'Label.Type'}} + static func buildBlock(_ stmt1: S0, _ stmt2: S1) // expected-note {{required by static method 'buildBlock' where 'S1' = 'Label<_>.Type'}} -> TupleP<(S0, S1)> where S0: P, S1: P { return TupleP((stmt1, stmt2)) } @@ -160,19 +160,28 @@ struct Text : P { init(_: T) {} } -struct Label : P where L : P { // expected-note {{'L' declared as parameter to type 'Label'}} +struct Label : P where L : P { // expected-note 2 {{'L' declared as parameter to type 'Label'}} typealias T = L - init(@Builder _: () -> L) {} + init(@Builder _: () -> L) {} // expected-note {{'init(_:)' declared here}} } func test_51167632() -> some P { - AnyP(G { // expected-error {{type 'Label.Type' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} + AnyP(G { // expected-error {{type 'Label<_>.Type' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} Text("hello") Label // expected-error {{generic parameter 'L' could not be inferred}} // expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} {{10-10=<<#L: P#>>}} }) } +func test_56221372() -> some P { + AnyP(G { + Text("hello") + Label() // expected-error {{generic parameter 'L' could not be inferred}} + // expected-error@-1 {{missing argument for parameter #1 in call}} + // expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} {{10-10=<<#L: P#>>}} + }) +} + struct SR11440 { typealias ReturnsTuple = () -> (T, T) subscript(@TupleBuilder x: ReturnsTuple) -> (ReturnsTuple) -> Void { //expected-note {{in call to 'subscript(_:)'}} @@ -211,6 +220,67 @@ func erroneousSR11350(x: Int) { if b { acceptInt(0) { } } - }).domap(0) // expected-error{{value of type 'Optional<()>' has no member 'domap'}} + }).domap(0) // expected-error{{value of type '()?' has no member 'domap'}} + } +} + +func extraArg() { + tuplify(true) { _ in + 1 + 2 + 3 + 4 + 5 + 6 // expected-error {{extra argument in call}} + } +} + +// rdar://problem/53209000 - use of #warning and #error +tuplify(true) { x in + 1 + #error("boom") // expected-error{{boom}} + "hello" + #warning("oops") // expected-warning{{oops}} + 3.14159 +} + +struct MyTuplifiedStruct { + var condition: Bool + + @TupleBuilder var computed: some Any { // expected-note{{remove the attribute to explicitly disable the function builder}}{{3-17=}} + if condition { + return 17 // expected-warning{{application of function builder 'TupleBuilder' disabled by explicit 'return' statement}} + // expected-note@-1{{remove 'return' statements to apply the function builder}}{{7-14=}}{{12-19=}} + } else { + return 42 + } + } +} + +// Check that we're performing syntactic use diagnostics. +func acceptMetatype(_: T.Type) -> Bool { true } + +func syntacticUses(_: T) { + tuplify(true) { x in + if x && acceptMetatype(T) { // expected-error{{expected member name or constructor call after type name}} + // expected-note@-1{{use '.self' to reference the type object}} + acceptMetatype(T) // expected-error{{expected member name or constructor call after type name}} + // expected-note@-1{{use '.self' to reference the type object}} + } + } +} + +// Check custom diagnostics within "if" conditions. +struct HasProperty { + var property: Bool = false +} + +func checkConditions(cond: Bool) { + var x = HasProperty() + + tuplify(cond) { value in + if x.property = value { // expected-error{{use of '=' in a boolean context, did you mean '=='?}} + "matched it" + } } } diff --git a/test/Constraints/function_builder_one_way.swift b/test/Constraints/function_builder_one_way.swift index 6c95ed221647a..c3ae16e5dc7af 100644 --- a/test/Constraints/function_builder_one_way.swift +++ b/test/Constraints/function_builder_one_way.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -enable-function-builder-one-way-constraints -// RUN: %target-typecheck-verify-swift -debug-constraints -enable-function-builder-one-way-constraints > %t.log 2>&1 +// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -debug-constraints > %t.log 2>&1 // RUN: %FileCheck %s < %t.log enum Either { @@ -49,17 +49,16 @@ func tuplify(_ collection: C, @TupleBuilder body: (C.Element) } // CHECK: ---Connected components--- -// CHECK-NEXT: 0: $T1 $T2 $T3 $T5 $T6 $T7 $T8 $T69 depends on 1 -// CHECK-NEXT: 1: $T9 $T11 $T16 $T30 $T62 $T63 $T64 $T65 $T66 $T67 $T68 depends on 2, 3, 4, 5 -// CHECK-NEXT: 5: $T32 $T43 $T44 $T45 $T46 $T47 $T57 $T58 $T59 $T60 $T61 depends on 6, 9 -// CHECK-NEXT: 9: $T48 $T54 $T55 $T56 depends on 10 -// CHECK-NEXT: 10: $T49 $T50 $T51 $T52 $T53 -// CHECK-NEXT: 6: $T33 $T35 $T39 $T40 $T41 $T42 depends on 7, 8 -// CHECK-NEXT: 8: $T36 $T37 $T38 -// CHECK-NEXT: 7: $T34 -// CHECK-NEXT: 4: $T17 $T18 $T19 $T20 $T21 $T22 $T23 $T24 $T25 $T26 $T27 $T28 $T29 -// CHECK-NEXT: 3: $T14 $T15 -// CHECK-NEXT: 2: $T10 +// CHECK-NEXT: 0: $T1 $T2 $T3 $T5 $T6 $T7 $T8 $T10 $T11 $T78 $T79 depends on 2 +// CHECK-NEXT: 2: $T13 $T18 $T29 $T42 $T53 $T55 $T56 $T57 $T58 $T59 $T60 $T61 $T62 $T63 $T64 $T65 $T66 $T67 $T69 $T70 $T71 $T72 $T73 $T74 $T75 $T76 $T77 depends on 1, 3, 4, 6, 9 +// CHECK-NEXT: 9: $T48 $T49 $T50 $T51 $T52 depends on 8 +// CHECK-NEXT: 8: $T43 $T44 $T45 $T46 $T47 +// CHECK-NEXT: 6: $T31 $T35 $T36 $T37 $T38 $T39 $T40 $T41 depends on 5, 7 +// CHECK-NEXT: 7: $T32 $T33 $T34 +// CHECK-NEXT: 5: $T30 +// CHECK-NEXT: 4: $T19 $T20 $T21 $T22 $T23 $T24 $T25 $T26 $T27 $T28 +// CHECK-NEXT: 3: $T16 $T17 +// CHECK-NEXT: 1: $T12 let names = ["Alice", "Bob", "Charlie"] let b = true var number = 17 diff --git a/test/Constraints/generic_protocol_witness.swift b/test/Constraints/generic_protocol_witness.swift index 025c777f70418..82d9da908aad0 100644 --- a/test/Constraints/generic_protocol_witness.swift +++ b/test/Constraints/generic_protocol_witness.swift @@ -60,4 +60,5 @@ struct L: Sequence {} // expected-error {{type 'L' does not conform to pro func z(_ x: L) { for xx in x {} + // expected-warning@-1{{immutable value 'xx' was never used; consider replacing with '_' or removing it}} } diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index b78bb1b3df302..450af7f1212bd 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -2,7 +2,7 @@ infix operator +++ -protocol ConcatToAnything { +protocol ConcatToAnything { // expected-note {{where 'Self' = 'U'}} static func +++ (lhs: Self, other: T) } @@ -14,7 +14,7 @@ func min(_ x: T, y: T) -> T { func weirdConcat(_ t: T, u: U) { t +++ u t +++ 1 - u +++ t // expected-error{{argument type 'U' does not conform to expected type 'ConcatToAnything'}} + u +++ t // expected-error{{referencing operator function '+++' on 'ConcatToAnything' requires that 'U' conform to 'ConcatToAnything'}} } // Make sure that the protocol operators don't get in the way. @@ -234,8 +234,7 @@ class Whatever { // expected-note 2 {{'A' declared as p Whatever.foo(a: 23) // expected-error {{generic parameter 'A' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{9-9=<<#A: Numeric#>, Int>}} // Swift useless error: cannot invoke 'foo' with no arguments -// TODO(diagnostics): We should try to produce a single note in this case. -Whatever.bar() // expected-error {{generic parameter 'A' could not be inferred}} expected-note 2 {{explicitly specify the generic arguments to fix this issue}} {{9-9=<<#A: Numeric#>, <#B: Numeric#>>}} +Whatever.bar() // expected-error {{generic parameter 'A' could not be inferred}} expected-note 1 {{explicitly specify the generic arguments to fix this issue}} {{9-9=<<#A: Numeric#>, <#B: Numeric#>>}} // expected-error@-1 {{generic parameter 'B' could not be inferred}} // Type checker doesn't enforce same-type constraint if associated type is Any @@ -647,7 +646,7 @@ struct BottleLayout { } let arr = [BottleLayout]() let layout = BottleLayout(count:1) -let ix = arr.firstIndex(of:layout) // expected-error {{argument type 'BottleLayout' does not conform to expected type 'Equatable'}} +let ix = arr.firstIndex(of:layout) // expected-error {{referencing instance method 'firstIndex(of:)' on 'Collection' requires that 'BottleLayout' conform to 'Equatable'}} let _: () -> UInt8 = { .init("a" as Unicode.Scalar) } // expected-error {{missing argument label 'ascii:' in call}} @@ -836,3 +835,14 @@ func sr_11491(_ value: [String]) { arr.insert(value) // expected-error@-1 {{cannot convert value of type '[String]' to expected argument type 'String'}} } + +func test_dictionary_with_generic_mismatch_in_key_or_value() { + struct S : Hashable {} + // expected-note@-1 2 {{arguments to generic parameter 'T' ('Int' and 'Bool') are expected to be equal}} + + let _: [Int: S] = [0: S(), 1: S()] + // expected-error@-1 {{cannot convert value of type 'S' to expected dictionary value type 'S'}} + + let _: [S: Int] = [S(): 42] + // expected-error@-1 {{cannot convert value of type 'S' to expected dictionary key type 'S'}} +} diff --git a/test/Constraints/if_expr.swift b/test/Constraints/if_expr.swift index ffc3e4fb63223..d8aa0fd62a898 100644 --- a/test/Constraints/if_expr.swift +++ b/test/Constraints/if_expr.swift @@ -28,7 +28,7 @@ useDouble(c) useDouble(d) var z = true ? a : b // expected-error{{result values in '? :' expression have mismatching types 'Int' and 'Double'}} -var _ = a ? b : b // expected-error{{'Int' is not convertible to 'Bool'}} +var _ = a ? b : b // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} @@ -51,13 +51,13 @@ useD1(i) // expected-error{{cannot convert value of type 'B' to expected argumen useD2(i) // expected-error{{cannot convert value of type 'B' to expected argument type 'D2'}} var x = true ? 1 : 0 -var y = 22 ? 1 : 0 // expected-error{{'Int' is not convertible to 'Bool'}} +var y = 22 ? 1 : 0 // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} -_ = x ? x : x // expected-error {{'Int' is not convertible to 'Bool'}} +_ = x ? x : x // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} _ = true ? x : 1.2 // expected-error {{result values in '? :' expression have mismatching types 'Int' and 'Double'}} -_ = (x: true) ? true : false // expected-error {{'(x: Bool)' is not convertible to 'Bool'}} -_ = (x: 1) ? true : false // expected-error {{'(x: Int)' is not convertible to 'Bool'}} +_ = (x: true) ? true : false // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} +_ = (x: 1) ? true : false // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}} let ib: Bool! = false let eb: Bool? = .some(false) diff --git a/test/Constraints/invalid_stdlib_2.swift b/test/Constraints/invalid_stdlib_2.swift index 80bec25e9a2d0..34141d341862e 100644 --- a/test/Constraints/invalid_stdlib_2.swift +++ b/test/Constraints/invalid_stdlib_2.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -class Dictionary : ExpressibleByDictionaryLiteral { // expected-error {{type 'Dictionary' does not conform to protocol 'ExpressibleByDictionaryLiteral'}} expected-note {{do you want to add protocol stubs?}} +class Dictionary : ExpressibleByDictionaryLiteral { // expected-error {{type 'Dictionary' does not conform to protocol 'ExpressibleByDictionaryLiteral'}} typealias Key = K typealias Value = V init(dictionaryLiteral xs: (K)...){} // expected-note {{candidate has non-matching type '(dictionaryLiteral: (K)...)'}} diff --git a/test/Constraints/iuo.swift b/test/Constraints/iuo.swift index 27c9839aee146..4011993727759 100644 --- a/test/Constraints/iuo.swift +++ b/test/Constraints/iuo.swift @@ -210,8 +210,10 @@ func conditionalDowncastToOptional(b: B?) -> D? { } func conditionalDowncastToObject(b: B?) -> D { - return b as? D! // expected-error {{cannot convert return expression of type 'D??' to return type 'D'}} - // expected-warning@-1 {{using '!' here is deprecated and will be removed in a future release}} + return b as? D! // expected-error {{value of optional type 'D?' must be unwrapped to a value of type 'D'}} + // expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} + // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} + // expected-warning@-3 {{using '!' here is deprecated and will be removed in a future release}} } // Ensure that we select the overload that does *not* involve forcing an IUO. diff --git a/test/Constraints/iuo_objc.swift b/test/Constraints/iuo_objc.swift index 1d11b98423eca..22c5ae63f479e 100644 --- a/test/Constraints/iuo_objc.swift +++ b/test/Constraints/iuo_objc.swift @@ -9,7 +9,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo.optional!() let _: Coat? = prop.iuo.optional!()! let _: Coat? = prop.iuo!.optional() @@ -17,7 +19,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat? = prop.iuo!.optional!() let _: Coat? = prop.iuo!.optional!()! let _: Coat = prop.iuo.optional() @@ -25,7 +29,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-2{{coalesce}} // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo.optional!() let _: Coat = prop.iuo.optional!()! let _: Coat = prop.iuo!.optional() @@ -34,7 +40,9 @@ func iuo_error(prop: IUOProperty) { // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional()! - // expected-error@-1 {{cannot invoke 'optional' with no arguments}} + // expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped to a value of type '() -> Coat?'}} + // expected-note@-2{{coalesce}} + // expected-note@-3{{force-unwrap}} let _: Coat = prop.iuo!.optional!() let _: Coat = prop.iuo!.optional!()! diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift index e224618afcb96..9badc9039a2d9 100644 --- a/test/Constraints/keypath.swift +++ b/test/Constraints/keypath.swift @@ -31,8 +31,8 @@ class Demo { } let some = Some(keyPath: \Demo.here) -// expected-error@-1 {{cannot convert value of type 'KeyPath Void)?>' to expected argument type 'KeyPath Void)?>'}} -// expected-note@-2 {{arguments to generic parameter 'Value' ('(() -> Void)?' and '((Any) -> Void)?') are expected to be equal}} +// expected-error@-1 {{cannot convert value of type 'KeyPath Void)?>' to expected argument type 'KeyPath Void)?>'}} +// expected-note@-2 {{arguments to generic parameter 'Value' ('(() -> Void)?' and '((V) -> Void)?') are expected to be equal}} // expected-error@-3 {{generic parameter 'V' could not be inferred}} // expected-note@-4 {{explicitly specify the generic arguments to fix this issue}} @@ -102,3 +102,16 @@ func rdar56131416() { // This type should be selected correctly. Rdar56131416.takesCorrectType(result) } + +func test_mismatch_with_contextual_optional_result() { + struct A { + init(_ data: T, keyPath: KeyPath) {} + } + + struct B { + var arr: [Int] = [] + } + + let _ = A(B(), keyPath: \.arr) + // expected-error@-1 {{key path value type '[Int]' cannot be converted to contextual type '[Int]?'}} +} diff --git a/test/Constraints/keypath_dynamic_member_lookup.swift b/test/Constraints/keypath_dynamic_member_lookup.swift index 73209315f7c6d..68b7eac7f0c07 100644 --- a/test/Constraints/keypath_dynamic_member_lookup.swift +++ b/test/Constraints/keypath_dynamic_member_lookup.swift @@ -390,3 +390,96 @@ func test_constrained_ext_vs_dynamic_member() { // CHECK: function_ref @$s29keypath_dynamic_member_lookup8SR_11465VAASHRzlE9hashValueSivg _ = SR_11465(rawValue: 1).hashValue // Ok, keep choice from constrained extension } + +// SR-11893: Make sure we properly handle IUO unwraps for key path dynamic members. +struct SR_11893_Base { + var i: Int! + subscript(_ x: Int) -> Int! { x } +} + +@dynamicMemberLookup +struct SR_11893 { + subscript(dynamicMember kp: KeyPath) -> Void { () } +} + +// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup13testIUOUnwrapyyAA8SR_11893VF : $@convention(thin) (SR_11893) -> () +func testIUOUnwrap(_ x: SR_11893) { + // CHECK: keypath $KeyPath, (root $SR_11893_Base; stored_property #SR_11893_Base.i : $Optional; optional_force : $Int) + x.i + + // CHECK: keypath $KeyPath, (root $SR_11893_Base; gettable_property $Optional, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int) + x[5] + + // CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath, (root $SR_11893_Base; stored_property #SR_11893_Base.i : $Optional; optional_force : $Int) + // CHECK: keypath $KeyPath, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath : $KeyPath], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]]) + _ = \SR_11893.i + + // CHECK: [[INNER_SUB_KP:%[0-9]+]] = keypath $KeyPath, (root $SR_11893_Base; gettable_property $Optional, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int) + // CHECK: keypath $KeyPath, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath : $KeyPath], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_SUB_KP]]) + _ = \SR_11893.[5] +} + +// SR-11896: Make sure the outer key path reflects the mutability of the 'dynamicMember:' subscript. +struct SR_11896_Base { + var mutable: Int + let immutable: Int +} + +@dynamicMemberLookup +struct SR_11896_Mutable { + subscript(dynamicMember kp: KeyPath) -> Int { + get { 5 } set {} + } +} + +@dynamicMemberLookup +struct SR_11896_Immutable { + subscript(dynamicMember kp: KeyPath) -> Int { + get { 5 } + } +} + +// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup21testKeyPathMutabilityyyF : $@convention(thin) () -> () +func testKeyPathMutability() { + // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int) + // CHECK: keypath $WritableKeyPath, (root $SR_11896_Mutable; settable_property $Int + _ = \SR_11896_Mutable.mutable + + // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.immutable : $Int) + // CHECK: keypath $WritableKeyPath, (root $SR_11896_Mutable; settable_property $Int + _ = \SR_11896_Mutable.immutable + + // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.mutable : $Int) + // CHECK: keypath $KeyPath, (root $SR_11896_Immutable; gettable_property $Int + _ = \SR_11896_Immutable.mutable + + // CHECK: keypath $KeyPath, (root $SR_11896_Base; stored_property #SR_11896_Base.immutable : $Int) + // CHECK: keypath $KeyPath, (root $SR_11896_Immutable; gettable_property $Int + _ = \SR_11896_Immutable.immutable +} + +// SR-11933: Make sure we properly handle default arguments. +struct HasDefaultedSubscript { + subscript(_ x: Int = 0) -> Int { x } +} + +@dynamicMemberLookup +struct SR_11933 { + subscript(dynamicMember kp: KeyPath) -> Int { 0 } +} + +// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup28testDynamicMemberWithDefaultyyAA8SR_11933VF : $@convention(thin) (SR_11933) -> () +func testDynamicMemberWithDefault(_ x: SR_11933) { + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[SUB_GET:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int + // CHECK: apply [[SUB_GET]]([[KP]], {{%[0-9]+}}) + _ = x[] + + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_FN]]() + // CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath, (root $HasDefaultedSubscript; gettable_property $Int, id @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icig : $@convention(method) (Int, HasDefaultedSubscript) -> Int, getter @$s29keypath_dynamic_member_lookup21HasDefaultedSubscriptVyS2icipACTK : $@convention(thin) (@in_guaranteed HasDefaultedSubscript, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[DEF_ARG]]) + // CHECK: [[OUTER_KP:%[0-9]+]] = keypath $KeyPath, (root $SR_11933; gettable_property $Int, id @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcig : $@convention(method) (@guaranteed KeyPath, SR_11933) -> Int, getter @$s29keypath_dynamic_member_lookup8SR_11933V0B6MemberSis7KeyPathCyAA21HasDefaultedSubscriptVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11933, UnsafeRawPointer) -> @out Int, indices [%$0 : $KeyPath : $KeyPath], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup21HasDefaultedSubscriptVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]]) + _ = \SR_11933.[] +} diff --git a/test/Constraints/keyword_arguments.swift b/test/Constraints/keyword_arguments.swift index c50c528f59258..6458fd4c36a66 100644 --- a/test/Constraints/keyword_arguments.swift +++ b/test/Constraints/keyword_arguments.swift @@ -255,22 +255,20 @@ missingargs2(x: 1, y: 2) // expected-error{{missing argument for parameter #3 in // ------------------------------------------- // Extra arguments // ------------------------------------------- -// FIXME: Diagnostics could be improved with all extra arguments and -// note pointing to the declaration being called. -func extraargs1(x: Int) {} +func extraargs1(x: Int) {} // expected-note {{'extraargs1(x:)' declared here}} extraargs1(x: 1, y: 2) // expected-error{{extra argument 'y' in call}} -extraargs1(x: 1, 2, 3) // expected-error{{extra argument in call}} +extraargs1(x: 1, 2, 3) // expected-error{{extra arguments at positions #2, #3 in call}} // ------------------------------------------- // Argument name mismatch // ------------------------------------------- -func mismatch1(thisFoo: Int = 0, bar: Int = 0, wibble: Int = 0) { } +func mismatch1(thisFoo: Int = 0, bar: Int = 0, wibble: Int = 0) { } // expected-note {{'mismatch1(thisFoo:bar:wibble:)' declared here}} mismatch1(foo: 5) // expected-error {{extra argument 'foo' in call}} mismatch1(baz: 1, wobble: 2) // expected-error{{incorrect argument labels in call (have 'baz:wobble:', expected 'bar:wibble:')}} {{11-14=bar}} {{19-25=wibble}} -mismatch1(food: 1, zap: 2) // expected-error{{extra argument 'food' in call}} +mismatch1(food: 1, zap: 2) // expected-error{{extra arguments at positions #1, #2 in call}} // ------------------------------------------- // Subscript keyword arguments @@ -344,12 +342,24 @@ func trailingclosure4(f: () -> Int) {} trailingclosure4 { 5 } func trailingClosure5(_ file: String = #file, line: UInt = #line, expression: () -> T?) { } +// expected-note@-1 {{in call to function 'trailingClosure5(_:line:expression:)'}} func trailingClosure6(value: Int, expression: () -> T?) { } - -trailingClosure5(file: "hello", line: 17) { return Optional.Some(5) } // expected-error{{extraneous argument label 'file:' in call}}{{18-24=}} -// expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{61-65=some}} -trailingClosure6(5) { return Optional.Some(5) } // expected-error{{missing argument label 'value:' in call}}{{18-18=value: }} -// expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'}} {{39-43=some}} +// expected-note@-1 {{in call to function 'trailingClosure6(value:expression:)'}} + +trailingClosure5(file: "hello", line: 17) { // expected-error{{extraneous argument label 'file:' in call}}{{18-24=}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} + return Optional.Some(5) + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} + // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} + // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} +} +trailingClosure6(5) { // expected-error{{missing argument label 'value:' in call}}{{18-18=value: }} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} + return Optional.Some(5) + // expected-error@-1 {{enum type 'Optional' has no case 'Some'; did you mean 'some'?}} {{19-23=some}} + // expected-error@-2 {{generic parameter 'Wrapped' could not be inferred}} + // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} +} class MismatchOverloaded1 { func method1(_ x: Int!, arg: ((Int) -> Int)!) { } diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 6f58c329c46db..1720948609d3c 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -301,7 +301,7 @@ func test15117741(_ s: r15117741S) { // References to unavailable decls sometimes diagnosed as ambiguous struct UnavailMember { @available(*, unavailable) - static var XYZ : X { get {} } // expected-note {{'XYZ' has been explicitly marked unavailable here}} + static var XYZ : UnavailMember { get {} } // expected-note {{'XYZ' has been explicitly marked unavailable here}} } let _ : [UnavailMember] = [.XYZ] // expected-error {{'XYZ' is unavailable}} @@ -316,7 +316,7 @@ struct S22490787 { func f22490787() { var path: S22490787 = S22490787() - for p in path { // expected-error {{type 'S22490787' does not conform to protocol 'Sequence'}} + for p in path { // expected-error {{for-in loop requires 'S22490787' to conform to 'Sequence'}} } } @@ -325,6 +325,7 @@ enum r23942743 { case Tomato(cloud: String) } let _ = .Tomato(cloud: .none) // expected-error {{reference to member 'Tomato' cannot be resolved without a contextual type}} +// expected-error@-1 {{cannot infer contextual base in reference to member 'none'}} @@ -354,7 +355,7 @@ do { // rdar://problem/25341015 extension Sequence { func r25341015_1() -> Int { - return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} + return max(1, 2) // expected-error {{use of 'max' refers to instance method rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} } } @@ -380,7 +381,7 @@ func r25341015() { class Bar { func baz() {} func qux() { - baz(1, 2) // expected-error {{argument passed to call that takes no arguments}} + baz(1, 2) // expected-error {{use of 'baz' refers to instance method rather than local function 'baz'}} } } } @@ -404,17 +405,17 @@ func bar_32854314() -> Int { extension Array where Element == Int { func foo() { let _ = min(foo_32854314(), bar_32854314()) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min()'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func foo(_ x: Int, _ y: Double) { let _ = min(x, y) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min()'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func bar() { let _ = min(1.0, 2) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min()'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } } @@ -566,15 +567,15 @@ func rdar_48114578() { } struct S_Min { - var min: Int = 42 + var xmin: Int = 42 } -func min(_: Int, _: Float) -> Int { return 0 } -func min(_: Float, _: Int) -> Int { return 0 } +func xmin(_: Int, _: Float) -> Int { return 0 } +func xmin(_: Float, _: Int) -> Int { return 0 } extension S_Min : CustomStringConvertible { public var description: String { - return "\(min)" // Ok + return "\(xmin)" // Ok } } @@ -615,8 +616,11 @@ func rdar50679161() { func rdar_50467583_and_50909555() { // rdar://problem/50467583 - let _: Set = [Int][] - // expected-error@-1 {{instance member 'subscript' cannot be used on type '[Int]'}} + let _: Set = [Int][] // expected-error {{no exact matches in call to subscript}} + // expected-note@-1 {{found candidate with type '(Int) -> Int'}} + // expected-note@-2 {{found candidate with type '(Range) -> ArraySlice'}} + // expected-note@-3 {{found candidate with type '((UnboundedRange_) -> ()) -> ArraySlice'}} + // expected-note@-4 {{found candidate with type '(Range.Index>) -> Slice<[Int]>' (aka '(Range) -> Slice>')}} // rdar://problem/50909555 struct S { diff --git a/test/Constraints/one_way_constraints.swift b/test/Constraints/one_way_constraints.swift index a56c143246f40..d830367d35d78 100644 --- a/test/Constraints/one_way_constraints.swift +++ b/test/Constraints/one_way_constraints.swift @@ -10,11 +10,11 @@ func testTernaryOneWay(b: Bool) { let _: Float = b ? 3.14159 : 17 // Errors due to one-way inference. - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{result values in '? :' expression have mismatching types 'Double' and 'Int'}} : 17 - let _: Float = b ? 3.14159 - : Builtin.one_way(17) // expected-error{{cannot convert value of type 'Int' to specified type 'Float'}} - let _: Float = b ? Builtin.one_way(3.14159) // expected-error{{cannot convert value of type 'Double' to specified type 'Float'}} + let _: Float = b ? 3.14159 // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} + : Builtin.one_way(17) + let _: Float = b ? Builtin.one_way(3.14159) // expected-error {{cannot convert value of type 'Int' to specified type 'Float'}} : Builtin.one_way(17) // Okay: default still works. diff --git a/test/Constraints/operator.swift b/test/Constraints/operator.swift index 14cdeb624d2e2..3b85bda0b8792 100644 --- a/test/Constraints/operator.swift +++ b/test/Constraints/operator.swift @@ -218,7 +218,6 @@ func rdar46459603() { _ = arr.values == [e] // expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Dictionary.Values' and '[E]'}} - // expected-note@-2 {{expected an argument list of type '(Self, Self)'}} _ = [arr.values] == [[e]] // expected-error@-1 {{value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}} // expected-note@-2 {{requirement from conditional conformance of '[Any]' to 'Equatable'}} diff --git a/test/Constraints/optional.swift b/test/Constraints/optional.swift index 958f80f3615e2..ea9c8f7a2fa83 100644 --- a/test/Constraints/optional.swift +++ b/test/Constraints/optional.swift @@ -373,3 +373,50 @@ func rdar_53238058() { // expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} } } + +// SR-8411 - Inconsistent ambiguity with optional and non-optional inout-to-pointer +func sr8411() { + struct S { + init(_ x: UnsafeMutablePointer) {} + init(_ x: UnsafeMutablePointer?) {} + + static func foo(_ x: UnsafeMutablePointer) {} + static func foo(_ x: UnsafeMutablePointer?) {} + + static func bar(_ x: UnsafeMutablePointer, _ y: Int) {} + static func bar(_ x: UnsafeMutablePointer?, _ y: Int) {} + } + + var foo = 0 + + _ = S(&foo) // Ok + _ = S.init(&foo) // Ok + _ = S.foo(&foo) // Ok + _ = S.bar(&foo, 42) // Ok +} + +// SR-11104 - Slightly misleading diagnostics for contextual failures with multiple fixes +func sr_11104() { + func bar(_: Int) {} + + bar(["hello"].first) + // expected-error@-1 {{cannot convert value of type 'String?' to expected argument type 'Int'}} +} + +// rdar://problem/57668873 - Too eager force optional unwrap fix + +@objc class Window {} + +@objc protocol WindowDelegate { + @objc optional var window: Window? { get set } +} + +func test_force_unwrap_not_being_too_eager() { + struct WindowContainer { + unowned(unsafe) var delegate: WindowDelegate? = nil + } + + let obj: WindowContainer = WindowContainer() + if let _ = obj.delegate?.window { // Ok + } +} diff --git a/test/Constraints/overload.swift b/test/Constraints/overload.swift index da7dae970f8f5..5838918cba3be 100644 --- a/test/Constraints/overload.swift +++ b/test/Constraints/overload.swift @@ -127,12 +127,11 @@ func test20886179(_ handlers: [(Int) -> Void], buttonIndex: Int) { // The problem here is that the call has a contextual result type incompatible // with *all* overload set candidates. This is not an ambiguity. -func overloaded_identity(_ a : Int) -> Int {} -func overloaded_identity(_ b : Float) -> Float {} +func overloaded_identity(_ a : Int) -> Int {} // expected-note {{found this candidate}} +func overloaded_identity(_ b : Float) -> Float {} // expected-note {{found this candidate}} func test_contextual_result_1() { - return overloaded_identity() // expected-error {{cannot invoke 'overloaded_identity' with no arguments}} - // expected-note @-1 {{overloads for 'overloaded_identity' exist with these partially matching parameter lists: (Float), (Int)}} + return overloaded_identity() // expected-error {{no exact matches in call to global function 'overloaded_identity'}} } func test_contextual_result_2() { diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 8d48e0ebea3c0..ef03365f1ce4a 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -87,7 +87,7 @@ default: } // Raise an error if pattern productions are used in expressions. -var b = var a // expected-error{{expected initial value after '='}} expected-error {{type annotation missing in pattern}} expected-error {{consecutive statements on a line must be separated by ';'}} {{8-8=;}} +var b = var x // expected-error{{expected initial value after '='}} expected-error {{type annotation missing in pattern}} expected-error {{consecutive statements on a line must be separated by ';'}} {{8-8=;}} var c = is Int // expected-error{{expected initial value after '='}} expected-error {{expected expression}} expected-error {{consecutive statements on a line must be separated by ';'}} {{8-8=;}} // TODO: Bad recovery in these cases. Although patterns are never valid @@ -230,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{10-10= () -> Int in }} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{10-10= () -> Int in }} + a.map { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{none}} switch $0 { case .A: return 1 @@ -274,7 +274,9 @@ struct StaticMembers: Equatable { static var optProp: Optional = StaticMembers() static func method(_: Int) -> StaticMembers { return prop } + // expected-note@-1 {{found candidate with type '(Int) -> StaticMembers'}} static func method(withLabel: Int) -> StaticMembers { return prop } + // expected-note@-1 {{found candidate with type '(Int) -> StaticMembers'}} static func optMethod(_: Int) -> StaticMembers? { return optProp } static func ==(x: StaticMembers, y: StaticMembers) -> Bool { return true } @@ -284,8 +286,8 @@ let staticMembers = StaticMembers() let optStaticMembers: Optional = StaticMembers() switch staticMembers { - case .init: break // expected-error{{cannot match values of type 'StaticMembers'}} - case .init(opt:): break // expected-error{{cannot match values of type 'StaticMembers'}} + case .init: break // expected-error{{member 'init(opt:)' expects argument of type 'Int'}} + case .init(opt:): break // expected-error{{member 'init(opt:)' expects argument of type 'Int'}} case .init(): break case .init(0): break @@ -299,17 +301,17 @@ switch staticMembers { // TODO: repeated error message case .optProp: break // expected-error* {{not unwrapped}} - case .method: break // expected-error{{cannot match}} + case .method: break // expected-error{{no exact matches in call to static method 'method'}} case .method(0): break case .method(_): break // expected-error{{'_' can only appear in a pattern}} case .method(let x): break // expected-error{{cannot appear in an expression}} - case .method(withLabel:): break // expected-error{{cannot match}} + case .method(withLabel:): break // expected-error{{member 'method(withLabel:)' expects argument of type 'Int'}} case .method(withLabel: 0): break case .method(withLabel: _): break // expected-error{{'_' can only appear in a pattern}} case .method(withLabel: let x): break // expected-error{{cannot appear in an expression}} - case .optMethod: break // expected-error{{cannot match}} + case .optMethod: break // expected-error{{member 'optMethod' expects argument of type 'Int'}} case .optMethod(0): break // expected-error@-1 {{value of optional type 'StaticMembers?' must be unwrapped to a value of type 'StaticMembers'}} // expected-note@-2 {{coalesce}} @@ -378,10 +380,10 @@ func test8347() -> String { return "" } - func h() -> String { // expected-note {{found this candidate}} + func h() -> String { return "" } - func h() -> Double { // expected-note {{found this candidate}} + func h() -> Double { return 3.0 } func h() -> Int? { //expected-note{{found this candidate}} diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index 9953af9442eab..eef0fe619afe3 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -212,6 +212,7 @@ func staticExistential(_ p: P.Type, pp: P.Protocol) { // Instance member of existential metatype -- not allowed _ = p.bar // expected-error{{instance member 'bar' cannot be used on type 'P'}} _ = p.mut // expected-error{{instance member 'mut' cannot be used on type 'P'}} + // expected-error@-1 {{partial application of 'mutating' method is not allowed}} // Static member of metatype -- not allowed _ = pp.tum // expected-error{{static member 'tum' cannot be used on protocol metatype 'P.Protocol'}} @@ -414,3 +415,10 @@ func rdar_50512161() { foo(item: item) // expected-error {{generic parameter 'I' could not be inferred}} } } + +// SR-11609: Compiler crash on missing conformance for default param +func test_sr_11609() { + func foo(_ x: T = .init()) -> T { x } // expected-note {{where 'T' = 'String'}} + let _: String = foo() + // expected-error@-1 {{local function 'foo' requires that 'String' conform to 'Initable'}} +} diff --git a/test/Constraints/rdar32726044.swift b/test/Constraints/rdar32726044.swift new file mode 100644 index 0000000000000..86b8545fd33ca --- /dev/null +++ b/test/Constraints/rdar32726044.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://problem/32726044 - shrink reduced domains too far +public protocol P_32726044 {} + +extension Int: P_32726044 {} +extension Float: P_32726044 {} + +public func *(lhs: P_32726044, rhs: P_32726044) -> Double { + fatalError() +} + +func rdar32726044() -> Float { + var f: Float = 0 + f = Float(1) * 100 // Ok + let _: Float = Float(42) + 0 // Ok + return f +} diff --git a/test/Constraints/rdar39931339.swift b/test/Constraints/rdar39931339.swift index b0c3e5d09b6bb..40add2eb11c18 100644 --- a/test/Constraints/rdar39931339.swift +++ b/test/Constraints/rdar39931339.swift @@ -6,26 +6,23 @@ struct S {} class C : P {} -extension S where T : P { -// expected-note@-1 {{where 'T' = 'T'}} -// expected-note@-2 {{where 'T' = 'Int'}} +extension S where T : P { // expected-note {{where 'T' = 'T'}} typealias A = Int typealias B = S } -extension S where T == Float { // expected-note {{where 'T' = 'Int'}} +extension S where T == Float { // expected-note {{requirement specified as 'T' == 'Float' [with T = Int]}} typealias C = Int } class A {} -extension A where T == [U], U: P { // expected-note {{where 'U' = 'Float'}} +extension A where T == [U], U: P { typealias S1 = Int } extension A where T == [U], U == Int { -// expected-note@-1 {{where 'U' = 'String'}} -// expected-note@-2 {{where 'T' = '[String]'}} +// expected-note@-1 {{requirement specified as 'T' == '[Int]' [with T = [String]]}} typealias S2 = Int } @@ -33,15 +30,14 @@ class B : A<[U], U> {} _ = B.S1() // Ok _ = B.S2() // Ok -_ = B.S1() // expected-error {{referencing type alias 'S1' on 'A' requires that 'Float' conform to 'P'}} +_ = B.S1() // expected-error {{type 'Float' does not conform to protocol 'P'}} _ = B.S2() -// expected-error@-1 {{referencing type alias 'S2' on 'A' requires the types '[String]' and '[Int]' be equivalent}} -// expected-error@-2 {{referencing type alias 'S2' on 'A' requires the types 'String' and 'Int' be equivalent}} +// expected-error@-1 {{'B.S2' (aka 'Int') requires the types '[String]' and '[Int]' be equivalent}} _ = S.A() // Ok -_ = S.A() // expected-error {{referencing type alias 'A' on 'S' requires that 'Int' conform to 'P'}} +_ = S.A() // expected-error {{type 'Int' does not conform to protocol 'P'}} _ = S.B() // expected-error {{type 'String' does not conform to protocol 'P'}} -_ = S.C() // expected-error {{referencing type alias 'C' on 'S' requires the types 'Int' and 'Float' be equivalent}} +_ = S.C() // expected-error {{'S.C' (aka 'Int') requires the types 'Int' and 'Float' be equivalent}} func foo(_ s: S.Type) { _ = s.A() // expected-error {{referencing type alias 'A' on 'S' requires that 'T' conform to 'P'}} diff --git a/test/Constraints/rdar40002266.swift b/test/Constraints/rdar40002266.swift index 11adad0bee811..580f971ed72e4 100644 --- a/test/Constraints/rdar40002266.swift +++ b/test/Constraints/rdar40002266.swift @@ -6,6 +6,6 @@ import Foundation struct S { init(_ num: T) { // expected-note {{where 'T' = 'Bool'}} self.init(num != 0) // expected-error {{initializer 'init(_:)' requires that 'Bool' inherit from 'NSNumber'}} - // expected-error@-1 {{argument type 'T' does not conform to expected type 'BinaryInteger'}} + // expected-error@-1 {{referencing operator function '!=' on 'BinaryInteger' requires that 'T' conform to 'BinaryInteger'}} } } diff --git a/test/Constraints/rdar42678836.swift b/test/Constraints/rdar42678836.swift index 23f07201f1fde..ff9ebe2d9b1cc 100644 --- a/test/Constraints/rdar42678836.swift +++ b/test/Constraints/rdar42678836.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift func foo(chr: Character) -> String { - return String(repeating: String(chr)) // expected-error {{incorrect argument label in call (have 'repeating:', expected 'stringLiteral:')}} + return String(repeating: String(chr)) // expected-error {{missing argument for parameter 'count' in call}} {{39-39=, count: <#Int#>}} } diff --git a/test/Constraints/rdar46377919.swift b/test/Constraints/rdar46377919.swift index ffdaab5b384e9..2f95e1cf28ec6 100644 --- a/test/Constraints/rdar46377919.swift +++ b/test/Constraints/rdar46377919.swift @@ -9,5 +9,4 @@ class Foo { func foo() -> Foo { return Foo(lhs: 2, rhs: 2) - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type '<>'}} } diff --git a/test/Constraints/requirement_failures_in_contextual_type.swift b/test/Constraints/requirement_failures_in_contextual_type.swift index f3eeff105f65f..d7896b494bc93 100644 --- a/test/Constraints/requirement_failures_in_contextual_type.swift +++ b/test/Constraints/requirement_failures_in_contextual_type.swift @@ -2,8 +2,8 @@ struct A {} -extension A where T == Int32 { // expected-note 2 {{where 'T' = 'Int'}} - struct B : ExpressibleByIntegerLiteral { // expected-note {{where 'T' = 'Int'}} +extension A where T == Int32 { // expected-note 3{{requirement specified as 'T' == 'Int32' [with T = Int]}} + struct B : ExpressibleByIntegerLiteral { typealias E = Int typealias IntegerLiteralType = Int @@ -14,8 +14,8 @@ extension A where T == Int32 { // expected-note 2 {{where 'T' = 'Int'}} } let _: A.B = 0 -// expected-error@-1 {{referencing struct 'B' on 'A' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.B' requires the types 'Int' and 'Int32' be equivalent}} let _: A.C = 0 -// expected-error@-1 {{referencing type alias 'C' on 'A' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.C' (aka 'Int') requires the types 'Int' and 'Int32' be equivalent}} let _: A.B.E = 0 -// expected-error@-1 {{referencing type alias 'E' on 'A.B' requires the types 'Int' and 'Int32' be equivalent}} +// expected-error@-1 {{'A.B' requires the types 'Int' and 'Int32' be equivalent}} diff --git a/test/Constraints/same_types.swift b/test/Constraints/same_types.swift index c4f2939c8617b..6129d8046338b 100644 --- a/test/Constraints/same_types.swift +++ b/test/Constraints/same_types.swift @@ -320,3 +320,38 @@ struct Bar where A.Assoc == B.Assoc { fatalError() } } + +protocol P7 { + associatedtype A + static func fn(args: A) +} + +class R: P7 where T: P7, T.A == T.Type { // expected-note {{'T' declared as parameter to type 'R'}} + typealias A = T.Type + static func fn(args: T.Type) {} +} + +R.fn(args: R.self) // expected-error {{generic parameter 'T' could not be inferred}} +// expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} + +// rdar://problem/58607155 +protocol AssocType1 { associatedtype A } +protocol AssocType2 { associatedtype A } + +func rdar58607155() { + func f(t1: T1, t2: T2) where T1.A == T2.A {} + // expected-note@-1 2 {{where 'T2' = 'MissingConformance'}} + // expected-note@-2 2 {{where 'T1' = 'MissingConformance'}} + + class Conformance: AssocType1, AssocType2 { typealias A = Int } + class MissingConformance {} + + // One generic argument has a conformance failure + f(t1: MissingConformance(), t2: Conformance()) // expected-error {{local function 'f(t1:t2:)' requires that 'MissingConformance' conform to 'AssocType1'}} + f(t1: Conformance(), t2: MissingConformance()) // expected-error {{local function 'f(t1:t2:)' requires that 'MissingConformance' conform to 'AssocType2'}} + + // Both generic arguments have a conformance failure + f(t1: MissingConformance(), t2: MissingConformance()) + // expected-error@-1 {{local function 'f(t1:t2:)' requires that 'MissingConformance' conform to 'AssocType1'}} + // expected-error@-2 {{local function 'f(t1:t2:)' requires that 'MissingConformance' conform to 'AssocType2'}} +} diff --git a/test/Constraints/sr10728.swift b/test/Constraints/sr10728.swift index 88e3f0b606646..c91d85e2298fc 100644 --- a/test/Constraints/sr10728.swift +++ b/test/Constraints/sr10728.swift @@ -19,5 +19,5 @@ struct S: P { typealias R = T3 static let foo: (T1, (R) -> T2) = bind() - // expected-error@-1 {{tuple type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') is not convertible to tuple '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}} + // expected-error@-1 {{cannot convert value of type '(T1, (S.R) -> T3)' (aka '(Int, (Bool) -> Bool)') to specified type '(T1, (S.R) -> T2)' (aka '(Int, (Bool) -> Float)')}} } diff --git a/test/Constraints/sr4664.swift b/test/Constraints/sr4664.swift new file mode 100644 index 0000000000000..d3a10fd8f918a --- /dev/null +++ b/test/Constraints/sr4664.swift @@ -0,0 +1,22 @@ +// RUN: %target-typecheck-verify-swift + +struct M where T : Collection { // expected-note {{where 'T' = 'X.Y'}} + static func f(a: T, b: T) -> [E] { + } +} + +enum E {} + +struct S {} + +struct X { + struct Y { + let s: [S] + } + + let y: [Y] +} + +let x = X(y: []) +let a = M.f(a: x.y[0], b: x.y[1]) +// expected-error@-1 {{generic struct 'M' requires that 'X.Y' conform to 'Collection'}} diff --git a/test/Constraints/super_constructor.swift b/test/Constraints/super_constructor.swift index c87cfead80eba..01e15a2990432 100644 --- a/test/Constraints/super_constructor.swift +++ b/test/Constraints/super_constructor.swift @@ -20,8 +20,7 @@ class D : B { } init(g:Int) { - super.init("aoeu") // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'B.init' exist with these partially matching parameter lists: (a: UnicodeScalar), (b: UnicodeScalar), (x: Int), (z: Float)}} + super.init("aoeu") // expected-error{{no exact matches in call to initializer}} } init(h:Int) { @@ -40,15 +39,15 @@ class B { init() { } - init(x:Int) { + init(x:Int) { // expected-note{{candidate has partially matching parameter list (x: Int)}} } - init(a:UnicodeScalar) { + init(a:UnicodeScalar) { // expected-note {{candidate has partially matching parameter list (a: UnicodeScalar)}} } - init(b:UnicodeScalar) { + init(b:UnicodeScalar) { // expected-note{{candidate has partially matching parameter list (b: UnicodeScalar)}} } - init(z:Float) { + init(z:Float) { // expected-note{{candidate has partially matching parameter list (z: Float)}} super.init() // expected-error{{'super' members cannot be referenced in a root class}} } } diff --git a/test/Constraints/suspicious_bit_casts.swift b/test/Constraints/suspicious_bit_casts.swift index 5e5148d791b4d..baaf382d1230f 100644 --- a/test/Constraints/suspicious_bit_casts.swift +++ b/test/Constraints/suspicious_bit_casts.swift @@ -2,7 +2,7 @@ func escapeByBitCast(f: () -> ()) -> () -> () { return unsafeBitCast(f, to: (() -> ()).self) - // expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}} + // expected-error@-1 {{converting non-escaping parameter 'f' to generic parameter 'T' may allow it to escape}} } func changeFnRep(f: @escaping () -> ()) -> @convention(block) () -> () { diff --git a/test/Constraints/trailing_closures_objc.swift b/test/Constraints/trailing_closures_objc.swift index 57f4b7c551a2e..546cf914a7f61 100644 --- a/test/Constraints/trailing_closures_objc.swift +++ b/test/Constraints/trailing_closures_objc.swift @@ -16,7 +16,7 @@ func foo(options: [AVMediaSelectionOption]) { func rdar28004686(a: [IndexPath]) { _ = a.sorted { (lhs: NSIndexPath, rhs: NSIndexPath) -> Bool in true } - // expected-error@-1 {{'NSIndexPath' is not convertible to 'IndexPath'}} + // expected-error@-1 {{cannot convert value of type '(NSIndexPath, NSIndexPath) -> Bool' to expected argument type '(IndexPath, IndexPath) throws -> Bool'}} } class Test: NSObject { @@ -24,6 +24,6 @@ class Test: NSObject { func rdar28012273() { let categories = ["hello", "world"] self.categories = categories.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending } - // expected-error@-1 {{cannot assign value of type '[String]' to type 'NSArray?'}} {{121-121= as NSArray}} + // expected-error@-1 {{cannot assign value of type '[String]' to type 'NSArray'}} {{121-121= as NSArray}} } } diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index 4d94ee1af1d39..258fed72d63f8 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -181,7 +181,7 @@ variadicWithTrailingClosure(fn: +) func gcd_23700031(_ a: T, b: T) { var a = a var b = b - (a, b) = (b, a % b) // expected-error {{argument type 'T' does not conform to expected type 'BinaryInteger'}} + (a, b) = (b, a % b) // expected-error {{protocol 'BinaryInteger' requires that 'T' conform to 'BinaryInteger'}} } // diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index c7f183e317aae..0b1852ea221ed 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -69,12 +69,12 @@ func genericTuple(_ x: (T, U)) {} do { generic(3) - generic(3, 4) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(3, 4) // expected-error {{extra argument in call}} generic((3)) generic((3, 4)) genericLabeled(x: 3) - genericLabeled(x: 3, 4) // expected-error {{global function 'genericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{20-20=(}} {{25-25=)}} + genericLabeled(x: 3, 4) // expected-error {{extra argument in call}} genericLabeled(x: (3)) genericLabeled(x: (3, 4)) @@ -92,7 +92,7 @@ do { let d = (a, b) generic(a) - generic(a, b) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(a, b) // expected-error {{extra argument in call}} generic((a)) generic(c) generic((a, b)) @@ -114,7 +114,7 @@ do { var d = (a, b) generic(a) - generic(a, b) // expected-error {{global function 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{11-11=(}} {{15-15=)}} + generic(a, b) // expected-error {{extra argument in call}} generic((a)) generic(c) generic((a, b)) @@ -256,12 +256,12 @@ do { let s = Concrete() s.generic(3) - s.generic(3, 4) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(3, 4) // expected-error {{extra argument in call}} s.generic((3)) s.generic((3, 4)) s.genericLabeled(x: 3) - s.genericLabeled(x: 3, 4) // expected-error {{instance method 'genericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{22-22=(}} {{27-27=)}} + s.genericLabeled(x: 3, 4) // expected-error {{extra argument in call}} s.genericLabeled(x: (3)) s.genericLabeled(x: (3, 4)) @@ -281,7 +281,7 @@ do { let d = (a, b) s.generic(a) - s.generic(a, b) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(a, b) // expected-error {{extra argument in call}} s.generic((a)) s.generic((a, b)) s.generic(d) @@ -304,7 +304,7 @@ do { var d = (a, b) s.generic(a) - s.generic(a, b) // expected-error {{instance method 'generic' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{13-13=(}} {{17-17=)}} + s.generic(a, b) // expected-error {{extra argument in call}} s.generic((a)) s.generic((a, b)) s.generic(d) @@ -390,12 +390,12 @@ do { var s = Concrete() s.mutatingGeneric(3) - s.mutatingGeneric(3, 4) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(3, 4) // expected-error {{extra argument in call}} s.mutatingGeneric((3)) s.mutatingGeneric((3, 4)) s.mutatingGenericLabeled(x: 3) - s.mutatingGenericLabeled(x: 3, 4) // expected-error {{instance method 'mutatingGenericLabeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{30-30=(}} {{35-35=)}} + s.mutatingGenericLabeled(x: 3, 4) // expected-error {{extra argument in call}} s.mutatingGenericLabeled(x: (3)) s.mutatingGenericLabeled(x: (3, 4)) @@ -415,7 +415,7 @@ do { let d = (a, b) s.mutatingGeneric(a) - s.mutatingGeneric(a, b) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(a, b) // expected-error {{extra argument in call}} s.mutatingGeneric((a)) s.mutatingGeneric((a, b)) s.mutatingGeneric(d) @@ -438,7 +438,7 @@ do { var d = (a, b) s.mutatingGeneric(a) - s.mutatingGeneric(a, b) // expected-error {{instance method 'mutatingGeneric' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{21-21=(}} {{25-25=)}} + s.mutatingGeneric(a, b) // expected-error {{extra argument in call}} s.mutatingGeneric((a)) s.mutatingGeneric((a, b)) s.mutatingGeneric(d) @@ -929,10 +929,10 @@ struct GenericInitLabeledTuple { } do { - _ = GenericInit(3, 4) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(3, 4) // expected-error {{extra argument in call}} _ = GenericInit((3, 4)) - _ = GenericInitLabeled(x: 3, 4) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{28-28=(}} {{33-33=)}} + _ = GenericInitLabeled(x: 3, 4) // expected-error {{extra argument in call}} _ = GenericInitLabeled(x: (3, 4)) _ = GenericInitTwo(3, 4) @@ -967,7 +967,7 @@ do { let b = 4 let c = (a, b) - _ = GenericInit(a, b) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(a, b) // expected-error {{extra argument in call}} _ = GenericInit((a, b)) _ = GenericInit(c) @@ -1003,7 +1003,7 @@ do { var b = 4 var c = (a, b) - _ = GenericInit(a, b) // expected-error {{initializer expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{19-19=(}} {{23-23=)}} + _ = GenericInit(a, b) // expected-error {{extra argument in call}} _ = GenericInit((a, b)) _ = GenericInit(c) @@ -1127,12 +1127,12 @@ enum GenericEnum { } do { - _ = GenericEnum.one(3, 4) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.one((3, 4)) - _ = GenericEnum.labeled(x: 3, 4) // expected-error {{enum case 'labeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{29-29=(}} {{34-34=)}} + _ = GenericEnum.labeled(x: 3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.labeled(x: (3, 4)) - _ = GenericEnum.labeled(3, 4) // expected-error {{enum case 'labeled' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{27-27=(}} {{31-31=)}} + _ = GenericEnum.labeled(3, 4) // expected-error {{extra argument in call}} _ = GenericEnum.labeled((3, 4)) // expected-error {{missing argument label 'x:' in call}} _ = GenericEnum.two(3, 4) @@ -1163,7 +1163,7 @@ do { let b = 4 let c = (a, b) - _ = GenericEnum.one(a, b) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(a, b) // expected-error {{extra argument in call}} _ = GenericEnum.one((a, b)) _ = GenericEnum.one(c) @@ -1199,7 +1199,7 @@ do { var b = 4 var c = (a, b) - _ = GenericEnum.one(a, b) // expected-error {{enum case 'one' expects a single parameter of type 'T' [with T = (Int, Int)]}} expected-note {{did you mean to pass a tuple?}} {{23-23=(}} {{27-27=)}} + _ = GenericEnum.one(a, b) // expected-error {{extra argument in call}} _ = GenericEnum.one((a, b)) _ = GenericEnum.one(c) @@ -1468,20 +1468,22 @@ let _ = sr4745.enumerated().map { (count, element) in "\(count): \(element)" } // SR-4738 let sr4738 = (1, (2, 3)) -// expected-error@+2{{use of undeclared type 'y'}} -// expected-error@+1{{use of undeclared type 'z'}} [sr4738].map { (x, (y, z)) -> Int in x + y + z } // expected-error@-1 {{closure tuple parameter does not support destructuring}} {{20-26=arg1}} {{38-38=let (y, z) = arg1; }} +// expected-warning@-2 {{unnamed parameters must be written with the empty name '_'}} {{20-20=_: }} +// expected-error@-3 {{use of undeclared type 'y'}} +// expected-error@-4 {{use of undeclared type 'z'}} // rdar://problem/31892961 let r31892961_1 = [1: 1, 2: 2] r31892961_1.forEach { (k, v) in print(k + v) } let r31892961_2 = [1, 2, 3] -// expected-error@+1{{use of undeclared type 'val'}} let _: [Int] = r31892961_2.enumerated().map { ((index, val)) in // expected-error@-1 {{closure tuple parameter does not support destructuring}} {{48-60=arg0}} {{3-3=\n let (index, val) = arg0\n }} - // expected-error@-2 {{use of undeclared type 'index'}} + // expected-warning@-2 {{unnamed parameters must be written with the empty name '_'}} {{48-48=_: }} + // expected-error@-3 {{use of undeclared type 'index'}} + // expected-error@-4 {{use of undeclared type 'val'}} val + 1 } @@ -1494,12 +1496,16 @@ let r31892961_4 = (1, 2) _ = [r31892961_4].map { x, y in x + y } let r31892961_5 = (x: 1, (y: 2, (w: 3, z: 4))) -[r31892961_5].map { (x: Int, (y: Int, (w: Int, z: Int))) in x + y } +[r31892961_5].map { (x: Int, (y: Int, (w: Int, z: Int))) in x + y } // expected-note {{'x' declared here}} // expected-error@-1 {{closure tuple parameter does not support destructuring}} {{30-56=arg1}} {{61-61=let (y, (w, z)) = arg1; }} +// expected-warning@-2 {{unnamed parameters must be written with the empty name '_'}} {{30-30=_: }} +// expected-error@-3{{use of unresolved identifier 'y'; did you mean 'x'?}} let r31892961_6 = (x: 1, (y: 2, z: 4)) -[r31892961_6].map { (x: Int, (y: Int, z: Int)) in x + y } +[r31892961_6].map { (x: Int, (y: Int, z: Int)) in x + y } // expected-note {{'x' declared here}} // expected-error@-1 {{closure tuple parameter does not support destructuring}} {{30-46=arg1}} {{51-51=let (y, z) = arg1; }} +// expected-warning@-2 {{unnamed parameters must be written with the empty name '_'}} {{30-30=_: }} +// expected-error@-3{{use of unresolved identifier 'y'; did you mean 'x'?}} // rdar://problem/32214649 -- these regressed in Swift 4 mode // with SE-0110 because of a problem in associated type inference @@ -1651,14 +1657,14 @@ public extension Optional { // https://bugs.swift.org/browse/SR-6837 // FIXME: Can't overlaod local functions so these must be top-level -func takePairOverload(_ pair: (Int, Int?)) {} // expected-note {{found this candidate}} -func takePairOverload(_: () -> ()) {} // expected-note {{found this candidate}} +func takePairOverload(_ pair: (Int, Int?)) {} +func takePairOverload(_: () -> ()) {} do { func takeFn(fn: (_ i: Int, _ j: Int?) -> ()) {} func takePair(_ pair: (Int, Int?)) {} takeFn(fn: takePair) // expected-error {{cannot convert value of type '((Int, Int?)) -> ()' to expected argument type '(Int, Int?) -> ()'}} - takeFn(fn: takePairOverload) // expected-error {{ambiguous reference to member 'takePairOverload'}} + takeFn(fn: takePairOverload) // expected-error {{cannot convert value of type '((Int, Int?)) -> ()' to expected argument type '(Int, Int?) -> ()'}} takeFn(fn: { (pair: (Int, Int?)) in } ) // Disallow for -swift-version 4 and later // expected-error@-1 {{contextual closure type '(Int, Int?) -> ()' expects 2 arguments, but 1 was used in closure body}} takeFn { (pair: (Int, Int?)) in } // Disallow for -swift-version 4 and later @@ -1689,12 +1695,15 @@ class Mappable { } let x = Mappable(()) +// expected-note@-1 2{{'x' declared here}} _ = x.map { (_: Void) in return () } +_ = x.map { (_: ()) in () } // https://bugs.swift.org/browse/SR-9470 do { func f(_: Int...) {} - let _ = [(1, 2, 3)].map(f) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int...) -> ())'}} + let _ = [(1, 2, 3)].map(f) // expected-error {{cannot convert value of type '(Int...) -> ()' to expected argument type '((Int, Int, Int)) throws -> T'}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} } // rdar://problem/48443263 - cannot convert value of type '() -> Void' to expected argument type '(_) -> Void' @@ -1728,7 +1737,8 @@ func autoclosureSplat() { // wrap the closure in a function conversion. takeFn { (fn: @autoclosure () -> Int, x: Int) in } - // expected-error@-1 {{contextual closure type '(_) -> ()' expects 1 argument, but 2 were used in closure body}} + // expected-error@-1 {{contextual closure type '(() -> Int) -> ()' expects 1 argument, but 2 were used in closure body}} + // expected-error@-2 {{converting non-escaping value to 'T' may allow it to escape}} takeFn { (fn: @autoclosure @escaping () -> Int) in } // FIXME: It looks like matchFunctionTypes() does not check @autoclosure at all. @@ -1737,7 +1747,7 @@ func autoclosureSplat() { // instead of changing the test. takeFn { (fn: @autoclosure @escaping () -> Int, x: Int) in } - // expected-error@-1 {{contextual closure type '(_) -> ()' expects 1 argument, but 2 were used in closure body}} + // expected-error@-1 {{contextual closure type '(@escaping () -> Int) -> ()' expects 1 argument, but 2 were used in closure body}} } func noescapeSplat() { @@ -1756,3 +1766,4 @@ func noescapeSplat() { takesEscaping(t.0) } } + diff --git a/test/Constraints/valid_pointer_conversions.swift b/test/Constraints/valid_pointer_conversions.swift index f06b7f4c30aad..9649d9e11f3a5 100644 --- a/test/Constraints/valid_pointer_conversions.swift +++ b/test/Constraints/valid_pointer_conversions.swift @@ -8,5 +8,7 @@ func foo(_ a: [[UInt8]], _ p: [UnsafeRawPointer]) { func takesPtr(_: UnsafePointer) {} func givesPtr(_ str: String) { - takesPtr(UnsafePointer(str)) + takesPtr(UnsafePointer(str)) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note @-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} } diff --git a/test/DebugInfo/ASTSection-multi.swift b/test/DebugInfo/ASTSection-multi.swift new file mode 100644 index 0000000000000..3d719608841f9 --- /dev/null +++ b/test/DebugInfo/ASTSection-multi.swift @@ -0,0 +1,49 @@ +// REQUIRES: OS=linux-gnu +// REQUIRES: executable_test +// REQUIRES: swift_tools_extra + +// Test that concatenated .swift_ast sections of various sizes can be parsed. + +// RUN: %empty-directory(%t) + +// RUN: echo "public let a0 = 0" >%t/a0.swift + +// RUN: echo "public let a1 = 0" >%t/a1.swift +// RUN: echo "public let b1 = 0" >>%t/a1.swift + +// RUN: echo "public let a2 = 0" >%t/a2.swift +// RUN: echo "public let b2 = 0" >>%t/a2.swift +// RUN: echo "public let c2 = 0" >>%t/a2.swift + +// RUN: echo "public let a3 = 0" >%t/a3.swift +// RUN: echo "public let b3 = 0" >>%t/a3.swift +// RUN: echo "public let c3 = 0" >>%t/a3.swift +// RUN: echo "public let d3 = 0" >>%t/a3.swift + +// RUN: %target-build-swift %t/a0.swift -c -g -o %t/a0.o -parse-as-library +// RUN: %target-build-swift %t/a1.swift -c -g -o %t/a1.o -parse-as-library +// RUN: %target-build-swift %t/a2.swift -c -g -o %t/a2.o -parse-as-library +// RUN: %target-build-swift %t/a3.swift -c -g -o %t/a3.o -parse-as-library + +// RUN: %target-build-swift %t/a0.swift -emit-module -emit-module-path %t/a0.swiftmodule +// RUN: %target-build-swift %t/a1.swift -emit-module -emit-module-path %t/a1.swiftmodule +// RUN: %target-build-swift %t/a2.swift -emit-module -emit-module-path %t/a2.swiftmodule +// RUN: %target-build-swift %t/a3.swift -emit-module -emit-module-path %t/a3.swiftmodule + +// RUN: %target-swift-modulewrap %t/a0.swiftmodule -o %t/a0-mod.o +// RUN: %target-swift-modulewrap %t/a1.swiftmodule -o %t/a1-mod.o +// RUN: %target-swift-modulewrap %t/a2.swiftmodule -o %t/a2-mod.o +// RUN: %target-swift-modulewrap %t/a3.swiftmodule -o %t/a3-mod.o + +// RUN: %target-build-swift -o %t/a.out %s \ +// RUN: %t/a0.o %t/a0-mod.o \ +// RUN: %t/a1.o %t/a1-mod.o \ +// RUN: %t/a2.o %t/a2-mod.o \ +// RUN: %t/a3.o %t/a3-mod.o + +// RUN: %lldb-moduleimport-test -verbose %t/a.out | %FileCheck %s +// CHECK: Importing a0... ok! +// CHECK: Importing a1... ok! +// CHECK: Importing a2... ok! +// CHECK: Importing a3... ok! + diff --git a/test/DebugInfo/BoundGenericEnum.swift b/test/DebugInfo/BoundGenericEnum.swift new file mode 100644 index 0000000000000..204a1d47aff00 --- /dev/null +++ b/test/DebugInfo/BoundGenericEnum.swift @@ -0,0 +1,87 @@ +// RUN: %target-swift-frontend %s -Onone -emit-ir -g -o - -module-name a \ +// RUN: -disable-debugger-shadow-copies | %FileCheck %s +public enum Result { + case success(Value) + case failure(Error) +} + +extension Result { + public func map(_ transform: (Value) -> U) -> Result { + switch self { + case .success(let value): + return .success(transform(value)) + case .failure(let error): + return .failure(error) + } + } +} + +func use(_ t : T) { +} + +public class SomeClass { + public let s = "hello" +} + +extension Result where Value : SomeClass { + public func f() -> Self { + use(self) + return map({ $0 }) + } +} + +extension Result { + public func g() { + use(self) + } +} + +let x : Result = .success(SomeClass()) +let y : Result<(Int64, Int64, Int64, Int64)> = .success((1, 2, 3, 4)) +x.f() +y.g() + +// Here we have three types, all named $s1a6ResultOyxGD (---> a.Result), +// but with different storage sizes: +// +// 0. Unsized from the subroutine tupe of map. +// 1. Enum wrapping a pointer-sized object [map() and f()]. +// 2. Enum wrapping a 4x64-bit tuple. +// +// Test that bound generic enums are *not* using their mangled name as a unique +// identifier. + +// (0) unsized. +// CHECK: !DISubprogram(name: "map", {{.*}}line: 9, type: ![[SBTY:[0-9]+]] +// CHECK: ![[SBTY]] = !DISubroutineType(types: ![[SBTYS:[0-9]+]]) +// CHECK: ![[SBTYS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[SELFTY:[0-9]+]]} +// CHECK: ![[SELFTY]] = +// CHECK-SAME: !DICompositeType(tag: DW_TAG_structure_type, {{.*}}line: 3, +// CHECK-SAME: elements: ![[UNSIZED_ELTS:[0-9]+]] +// CHECK: ![[UNSIZED_ELTS]] = !{![[UNSIZED_MEM:[0-9]+]]} +// CHECK: ![[UNSIZED_MEM]] = !DIDerivedType(tag: DW_TAG_member, +// CHECK-SAME: baseType: ![[UNIQ:[0-9]+]] + +// The unique unsized type. +// CHECK: ![[UNIQ]] = !DICompositeType( +// CHECK-SAME: tag: DW_TAG_structure_type, name: "Result", +// CHECK-SAME: line: 3, +// CHECK-NOT: size: +// CHECK-SAME: runtimeLang: DW_LANG_Swift, +// CHECK-SAME: identifier: "$s1a6ResultOyxGD") + +// (2) +// CHECK: !DILocalVariable(name: "self", arg: 2, {{.*}}line: 9, +// CHECK-SAME: type: ![[C_TUP:[0-9]+]] +// CHECK: ![[C_TUP]] = !DIDerivedType(tag: DW_TAG_const_type, +// CHECK-SAME: baseType: ![[TUP:[0-9]+]]) +// CHECK: ![[TUP]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: line: 3, size: {{256|512}}, + +// (1) +// CHECK: !DILocalVariable(name: "self", arg: 1, {{.*}}line: 27, +// CHECK-SAME: type: ![[C_CLASS:[0-9]+]] +// CHECK: ![[C_CLASS]] = !DIDerivedType(tag: DW_TAG_const_type, +// CHECK-SAME: baseType: ![[CLASS:[0-9]+]]) +// CHECK: ![[CLASS]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: line: 3, size: diff --git a/test/DebugInfo/ParseableInterfaceImports.swift b/test/DebugInfo/ParseableInterfaceImports.swift index 215316fbf62c5..97d7cb3b69809 100644 --- a/test/DebugInfo/ParseableInterfaceImports.swift +++ b/test/DebugInfo/ParseableInterfaceImports.swift @@ -11,11 +11,10 @@ import basic // CHECK: !DIModule(scope: null, name: "basic", includePath: " // CHECK-SAME: basic.swiftinterface" -// We don't record any module interfaces from the SDK. -// They're in the SDK after all. +// Even if the module interface is in the SDK, we still return the path +// to the swiftinterface. // SDK: !DIModule(scope: null, name: "basic", includePath: " -// SDK-SAME: basic{{.*}}.swiftmodule" - +// SDK-SAME: basic{{.*}}.swiftinterface" func markUsed(_ t: T) {} markUsed(basic.foo(1, 2)) diff --git a/test/DebugInfo/bound-namealiastype.swift b/test/DebugInfo/bound-namealiastype.swift index be47294b649f3..37bdb22e4edf8 100644 --- a/test/DebugInfo/bound-namealiastype.swift +++ b/test/DebugInfo/bound-namealiastype.swift @@ -9,7 +9,10 @@ func dispatch_queue_create() -> dispatch_queue_t! { } // CHECK: !DIGlobalVariable(name: "queue", -// CHECK-SAME: line: [[@LINE+3]], type: ![[T:[0-9]+]] -// CHECK: ![[T]] = !DICompositeType( +// CHECK-SAME: line: [[@LINE+6]], type: ![[TY_CONTAINER:[0-9]+]] +// CHECK: ![[TY_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TY_ELTS:[0-9]+]] +// CHECK: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} +// CHECK: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] +// CHECK: ![[TY]] = !DICompositeType( // CHECK-SAME: identifier: "$s4main16dispatch_queue_taSgD" public var queue = dispatch_queue_create() diff --git a/test/DebugInfo/enum.swift b/test/DebugInfo/enum.swift index 0f917efa54bbf..d9a30648ec637 100644 --- a/test/DebugInfo/enum.swift +++ b/test/DebugInfo/enum.swift @@ -52,7 +52,6 @@ let r = Color.Red let c = MaybeIntPair.just(74, 75) // CHECK: !DICompositeType({{.*}}name: "Maybe", // CHECK-SAME: line: [[@LINE-8]], -// CHECK-SAME: size: 8{{[,)]}} // CHECK-SAME: identifier: "$s4enum5MaybeOyAA5ColorOGD" let movie : Maybe = .none @@ -79,8 +78,12 @@ public enum Tuple

{ func bar(_ x : Tuple) -> Tuple { return x } -// CHECK: ![[LIST:.*]] = !DICompositeType({{.*}}identifier: "$s4enum4ListOyxGD" -// CHECK-DAG: ![[LET_LIST:.*]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[LIST]]) +// CHECK-DAG: ![[LIST:.*]] = !DICompositeType({{.*}}identifier: "$s4enum4ListOyxGD" +// CHECK-DAG: ![[LIST_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}} baseType: ![[LIST]] +// CHECK-DAG: ![[LIST_ELTS:.*]] = !{![[LIST_MEMBER]]} +// CHECK-DAG: ![[LIST_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[LIST_ELTS]] + +// CHECK-DAG: ![[LET_LIST:.*]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[LIST_CONTAINER]]) // CHECK-DAG: !DILocalVariable(name: "self", arg: 1, {{.*}} line: [[@LINE+4]], type: ![[LET_LIST]], flags: DIFlagArtificial) public enum List { indirect case Tail(List, T) diff --git a/test/DebugInfo/generic_arg5.swift b/test/DebugInfo/generic_arg5.swift index 419cd34867eb3..f1585921a00a4 100644 --- a/test/DebugInfo/generic_arg5.swift +++ b/test/DebugInfo/generic_arg5.swift @@ -15,10 +15,13 @@ public func foo(_ values : [S]) // CHECK: ![[TYP:[0-9]+]] = !DICompositeType({{.*}}, identifier: "$s12generic_arg51SVyxGD") // The argument is a by-ref struct and thus needs to be dereferenced. // CHECK: ![[ARG]] = !DILocalVariable(name: "arg", arg: 1, - // CHECK-SAME: line: [[@LINE+4]], + // CHECK-SAME: line: [[@LINE+7]], // CHECK-SAME: type: ![[LET_TYP:[0-9]+]]) // CHECK: ![[LET_TYP]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[TYP]]) + // CHECK-SAME: baseType: ![[TYP_CONTAINER:[0-9]+]]) + // CHECK: ![[TYP_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TYP_ELTS:[0-9]+]] + // CHECK: ![[TYP_ELTS]] = !{![[TYP_MEMBER:[0-9]+]]} + // CHECK: ![[TYP_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TYP]] let _ = values.flatMap { arg in return .some(arg) } diff --git a/test/DebugInfo/generic_enum.swift b/test/DebugInfo/generic_enum.swift index da93a7252c7de..e50117ccefa50 100644 --- a/test/DebugInfo/generic_enum.swift +++ b/test/DebugInfo/generic_enum.swift @@ -17,7 +17,10 @@ func wrapTrivialGeneric(_ t: T, u: U) -> TrivialGeneric { return .x(t, u) } // CHECK-DAG: ![[T1:.*]] = !DICompositeType({{.*}}identifier: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" -// CHECK-DAG: !DIGlobalVariable(name: "tg",{{.*}} line: [[@LINE+2]],{{.*}} type: ![[T1]],{{.*}} isLocal: false, isDefinition: true +// CHECK-DAG: ![[T1_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[T1]] +// CHECK-DAG: ![[T1_ELTS:.*]] = !{![[T1_MEMBER]]} +// CHECK-DAG: ![[T1_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[T1_ELTS]] +// CHECK-DAG: !DIGlobalVariable(name: "tg",{{.*}} line: [[@LINE+2]],{{.*}} type: ![[T1_CONTAINER]],{{.*}} isLocal: false, isDefinition: true // CHECK-DAG: !DICompositeType({{.*}}, name: "TrivialGeneric", {{.*}}identifier: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" var tg : TrivialGeneric = .x(23, "skidoo") switch tg { diff --git a/test/DebugInfo/generic_enum_closure.swift b/test/DebugInfo/generic_enum_closure.swift index bc7e026e0f99c..0e8063b942e78 100644 --- a/test/DebugInfo/generic_enum_closure.swift +++ b/test/DebugInfo/generic_enum_closure.swift @@ -15,8 +15,11 @@ struct CErrorOr // CHECK-SAME: !DIExpression(DW_OP_deref)) // CHECK-DAG: store i8* %[[DYN:.*]], i8** %[[SHADOW]] // CHECK-DAG: %[[DYN]] = alloca i8, i{{32|64}} % - // CHECK-DAG: ![[SELF]] = !DILocalVariable(name: "self", scope:{{.*}}, type: ![[T1:.*]]) - // CHECK-DAG: ![[T1]] = !DICompositeType({{.*}}, identifier: "$s20generic_enum_closure8CErrorOrVyxGD") + // CHECK-DAG: ![[SELF]] = !DILocalVariable(name: "self", scope:{{.*}}, type: ![[TY_CONTAINER:.*]]) + // CHECK-DAG: ![[TY_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TY_ELTS:[0-9]+]] + // CHECK-DAG: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} + // CHECK-DAG: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] + // CHECK-DAG: ![[TY]] = !DICompositeType({{.*}}, identifier: "$s20generic_enum_closure8CErrorOrVyxGD") value = .none } } diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index d1f6fbb686973..93f13e5d05955 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -43,8 +43,8 @@ public class C { let r : R init(_ _r: R) { r = _r } - // SIL: // C.f(_:) - // IR: define {{.*}} @"$s1A1CC1fyyqd__lF" + // SIL-LABEL: // C.f(_:) + // IR-LABEL: define {{.*}} @"$s1A1CC1fyyqd__lF" #sourceLocation(file: "f.swift", line: 1) public func f(_ s: S) { // SIL: debug_value_addr %0 : $*S, let, name "s", argno 1,{{.*}} scope [[F]] @@ -57,7 +57,13 @@ public class C { // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 2) g(s) - // IR: dbg.value({{.*}}, metadata ![[GR_T:[0-9]+]] + // Jump-threading removes the basic block containing debug_value + // "t" before the second call to `g(r)`. When this happens, the + // ref_element_addr in that removed block is left with a single + // debug_value use, so they are both deleted. This means we have + // no debug value for "t" in the call to `g(r)`. + // dbg.value({{.*}}, metadata ![[GR_T:[0-9]+]] + // IR: dbg.value({{.*}}, metadata ![[GR_U:[0-9]+]] // IR: call {{.*}}3use #sourceLocation(file: "f.swift", line: 3) @@ -81,6 +87,8 @@ public class C { g(false) } } +// SIL-LABEL: } // end sil function '$s1A1CC1fyyqd__lF' +// IR-LABEL: ret void // IR: ![[BOOL:[0-9]+]] = !DICompositeType({{.*}}name: "Bool" // IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) @@ -96,9 +104,12 @@ public class C { // IR: ![[SP_GS_T]] = {{.*}}linkageName: "$s1A1gyyxlFqd___Ti5" // IR: ![[GS_U]] = !DILocalVariable(name: "u", {{.*}} scope: ![[SP_GS_U:[0-9]+]], {{.*}} type: ![[LET_TAU_1_0]]) // IR: ![[SP_GS_U]] = {{.*}}linkageName: "$s1A1hyyxlFqd___Ti5" -// IR: ![[GR_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GR_T:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) + +// Debug info for this variable is removed. See the note above the call to g(r). +// ![[GR_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GR_T:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) // S has the same generic parameter numbering s T and U. -// IR: ![[SP_GR_T]] = {{.*}}linkageName: "$s1A1gyyxlF" +// ![[SP_GR_T]] = {{.*}}linkageName: "$s1A1gyyxlF" + // IR: ![[GR_U]] = !DILocalVariable(name: "u", {{.*}} scope: ![[SP_GR_U:[0-9]+]], {{.*}}type: ![[LET_TAU_0_0]]) // IR: ![[SP_GR_U]] = {{.*}}linkageName: "$s1A1hyyxlF" // IR: ![[GRS_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GRS_T:[0-9]+]], {{.*}}type: ![[LET_TUPLE:[0-9]+]] diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 70c0f87270dbf..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -5,14 +5,12 @@ // RUN: %FileCheck %s < %t.ll // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll -// CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main -// CHECK: call {{.*}}noinline{{.*}}, !dbg ![[CALL:.*]] +// CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar -func use(_ t: T) {} - @inline(never) func noinline(_ x: Int64) -> Int64 { return x } @@ -29,8 +27,7 @@ func inlined(_ x: Int64) -> Int64 { return result } // CHECK-DAG: !DIGlobalVariable(name: "y",{{.*}} file: ![[TOPLEVEL]],{{.*}} line: [[@LINE+1]] -let y = inlined(x) -use(y) +public let y = inlined(x) // Check if the inlined and removed function still has the correct linkage name. // CHECK-DAG: !DISubprogram(name: "inlined", linkageName: "$s4main7inlinedys5Int64VADF" diff --git a/test/DebugInfo/linetable-codeview.swift b/test/DebugInfo/linetable-codeview.swift index 524d49c8561b9..77c7141e7a1ad 100644 --- a/test/DebugInfo/linetable-codeview.swift +++ b/test/DebugInfo/linetable-codeview.swift @@ -81,7 +81,7 @@ func foo() { // FIXME: The location of ``@llvm.trap`` should be in Integers.swift.gyb // instead of being artificial. // CHECK: ![[INLINEDADD]] = !DILocation(line: 0, scope: ![[FAILURE_FUNC:[0-9]+]], inlinedAt: ![[INLINELOC:[0-9]+]] -// CHECK-DAG: !{{.*}} = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}}) +// CHECK-DAG: !{{.*}} = distinct !DISubprogram(name: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}}) // CHECK-DAG: ![[INLINELOC]] = !DILocation(line: 0, scope: !{{[0-9]+}}, inlinedAt: ![[ADD]] // NOTE: These prologue instructions are given artificial line locations for diff --git a/test/DebugInfo/liverange-extension-vector.swift b/test/DebugInfo/liverange-extension-vector.swift deleted file mode 100644 index 7320a1c4a4aa7..0000000000000 --- a/test/DebugInfo/liverange-extension-vector.swift +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s -// REQUIRES: objc_interop, CPU=x86_64 -import simd - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -public func rangeExtension(x: Int32, y: Int32) { - let p = int2(x, y) - // CHECK: define {{.*}}rangeExtension - // CHECK: llvm.dbg.value(metadata <2 x i32> %[[P:.*]], metadata {{.*}}, metadata - use(p) - // CHECK: asm sideeffect "", "r"{{.*}}[[P]] -} diff --git a/test/DebugInfo/liverange-extension.swift b/test/DebugInfo/liverange-extension.swift deleted file mode 100644 index a7ba868005393..0000000000000 --- a/test/DebugInfo/liverange-extension.swift +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s - -// REQUIRES: CPU=x86_64 -// -// We require x86_64 to make the test easier to read. Without this, -// writing check lines that ensure our asm gadgets match up with the -// right values is painfully hard to do. - -func use(_ x: T) {} - -func getInt32() -> Int32 { return -1 } - -// CHECK-LABEL: define {{.*}}rangeExtension -public func rangeExtension(_ b: Bool) { - let i = getInt32() - // CHECK: [[I:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I:!.*]], metadata - - use(i) - - // CHECK: br i1 - - if b { - // CHECK: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I]] - - let j = getInt32() - // CHECK: [[J:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - // CHECK: llvm.dbg.value(metadata i32 [[J]], metadata [[MD_J:!.*]], metadata - - use(j) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]]) - - // CHECK: br label - } - - // CHECK-NOT: llvm.dbg.value(metadata i32 [[J]] - // CHECK: llvm.dbg.value(metadata i32 [[I]] - - let z = getInt32() - // CHECK: [[Z:%.*]] = call swiftcc i32 @"{{.*}}getInt32 - // CHECK: llvm.dbg.value(metadata i32 [[Z]], metadata [[MD_Z:!.*]], metadata - - use(z) - // CHECK: call swiftcc void @"{{.*}}use - - // CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]]) - // CHECK-NEXT: [[Z_ZEXT:%.*]] = zext i32 [[Z]] to i64 - // CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[Z_ZEXT]]) -} - -// CHECK-DAG: [[MD_I]] = !DILocalVariable(name: "i" -// CHECK-DAG: [[MD_J]] = !DILocalVariable(name: "j" -// CHECK-DAG: [[MD_Z]] = !DILocalVariable(name: "z" diff --git a/test/DebugInfo/local-vars.swift.gyb b/test/DebugInfo/local-vars.swift.gyb index 46aaedbe6add5..611ef2270dc12 100644 --- a/test/DebugInfo/local-vars.swift.gyb +++ b/test/DebugInfo/local-vars.swift.gyb @@ -2,7 +2,9 @@ // test only verifies that the variables show up in the debug info at // all. There are other tests testing liveness and representation. -// REQUIRES: rdar47777473 +// rdar://problem/57611302 +// XFAIL: windows + // RUN: %gyb %s -o %t.swift // RUN: %target-swift-frontend %t.swift -g -emit-ir -o - | %FileCheck %t.swift // RUN: %target-swift-frontend %t.swift -g -c -o %t.o @@ -67,7 +69,7 @@ type_initializer_map = [ ("(Int, C)", "(42, C(42))"), ("C", "C(42)"), ("S", "S()"), - ("SLarge", "SLarge(tuple: (1,2,3,4,5,6,7,8))"), + ("SLarge", "SLarge(tuple: (1,2,3,4,5,6,7,8))"), #// Enums. ("ENoPayload", ".two"), ("ESinglePayload", ".two"), @@ -86,14 +88,14 @@ def derive_name((type, val)): for name, type, val in map(derive_name, type_initializer_map): generic = (type in ['T', 'U']) function = "->" in type -}% +}% % if generic: public class ContextA_${name} { let t : T let u : U init(_ t: T, _ u: U) { self.t = t; self.u = u; } % end - + public func constant_${name}() -> ${type} { let v : ${type} = ${val} // CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]] @@ -223,7 +225,7 @@ public func case_let_${name}() { // DWARF-NOT: DW_TAG // DWARF: DW_AT_name ("v") } - + public func optional_${name}() -> ${type}? { return constant_${name}(); } @@ -402,7 +404,7 @@ public func arg_tuple_${name}(_ v: (${type}, ${type})) { // DWARF: DW_AT_name {{.*}}"y" use(y) } - + public func closure_capture_${name}() { let v : ${type} = constant_${name}(); // CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]] @@ -465,7 +467,7 @@ public func closure_capture_byref_${name}() { // DWARF: DW_AT_artificial % end } - + % if generic: } // End of Context_${name}. % end diff --git a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift b/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift index feb8bc8113227..7a8ab9aa9e7a6 100644 --- a/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift +++ b/test/DebugInfo/mandatoryinlining-wrongdebugscope.swift @@ -2,9 +2,9 @@ // RUN: -sil-print-after=mandatory-inlining \ // RUN: -Xllvm -sil-print-debuginfo -o /dev/null 2>&1 | %FileCheck %s -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> @out (), loc {{.*}}:18:17, scope 9 -// CHECK: strong_release {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> @out (), loc {{.*}}:18:17, scope 9 +// CHECK: destroy_value {{.*}} : $@callee_guaranteed () -> (), loc {{.*}}:21:5, scope 9 func patatino(_ function: @escaping (d) -> i, g: d...) -> () -> i { return { typealias h = ([d]) -> i diff --git a/test/DebugInfo/mangling.swift b/test/DebugInfo/mangling.swift index 3fc14f37536a0..859bec5506f1d 100644 --- a/test/DebugInfo/mangling.swift +++ b/test/DebugInfo/mangling.swift @@ -9,8 +9,11 @@ func markUsed(_ t: T) {} // mangling.myDict : Swift.Dictionary // CHECK: !DIGlobalVariable(name: "myDict", // CHECK-SAME: linkageName: "$s8mangling6myDictSDys5Int64VSSGvp", -// CHECK-SAME: line: [[@LINE+3]] -// CHECK-SAME: type: ![[DT:[0-9]+]] +// CHECK-SAME: line: [[@LINE+6]] +// CHECK-SAME: type: ![[DT_CONTAINER:[0-9]+]] +// CHECK: ![[DT_CONTAINER]] = !DICompositeType({{.*}}elements: ![[DT_ELTS:[0-9]+]] +// CHECK: ![[DT_ELTS]] = !{![[DT_MEMBER:[0-9]+]]} +// CHECK: ![[DT_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[DT:[0-9]+]] // CHECK: ![[DT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Dictionary" var myDict = Dictionary() myDict[12] = "Hello!" diff --git a/test/DebugInfo/protocol-sugar.swift b/test/DebugInfo/protocol-sugar.swift index 77e7455e7ccde..5b602eec058de 100644 --- a/test/DebugInfo/protocol-sugar.swift +++ b/test/DebugInfo/protocol-sugar.swift @@ -4,5 +4,8 @@ protocol B {} typealias C = B & A protocol D {} var p: (C & D)? -// CHECK-DAG: !DIGlobalVariable(name: "p", {{.*}}type: ![[TY:[0-9]+]] +// CHECK-DAG: !DIGlobalVariable(name: "p", {{.*}}type: ![[TY_CONTAINER:[0-9]+]] +// CHECK-DAG: ![[TY_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TY_ELTS:[0-9]+]] +// CHECK-DAG: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} +// CHECK-DAG: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] // CHECK-DAG: ![[TY]] = {{.*}}identifier: "$s4main1A_AA1BAA1DpXSpSgD" diff --git a/test/DebugInfo/self.swift b/test/DebugInfo/self.swift index 9439dda2516c6..26b0268024146 100644 --- a/test/DebugInfo/self.swift +++ b/test/DebugInfo/self.swift @@ -1,4 +1,6 @@ // RUN: %target-swift-frontend -primary-file %s -emit-ir -g -o - | %FileCheck %s +// rdar://problem/56255858 - failing on no-asserts builds +// XFAIL: no_asserts public struct stuffStruct { var a: Int64 = 6 diff --git a/test/DebugInfo/shadow_copies.swift b/test/DebugInfo/shadow_copies.swift index ab5554888fcfa..e38001ed34479 100644 --- a/test/DebugInfo/shadow_copies.swift +++ b/test/DebugInfo/shadow_copies.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend %s -Onone -emit-ir -g -o - | %FileCheck %s - +// RUN: %target-swift-frontend %s -Onone -emit-ir -g -o - \ +// RUN: -disable-debugger-shadow-copies | %FileCheck %s --check-prefix=NOCOPY class ClassA { var x : Int64 @@ -16,13 +17,46 @@ class ClassB : ClassA { override init (_ input : Int64) { - // CHECK: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" - // CHECK: alloca {{.*}}ClassBC* - // CHECK: alloca i64 - // CHECK-NOT: alloca - // CHECK: ret {{.*}}ClassBC + // CHECK: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" + // NOCOPY: @"$s{{.*}}6ClassBCyACs5Int64Vcfc" + // CHECK: alloca {{.*}}ClassBC* + // NOCOPY: alloca {{.*}}ClassBC* + + // CHECK: alloca i64 + + // CHECK-NOT: alloca + // NOCOPY-NOT: alloca + // CHECK: ret {{.*}}ClassBC + // NOCOPY: ret {{.*}}ClassBC super.init (input) } } let b = ClassB(1); + +func use(_ x: Int) {} + +class ClassC +{ + // CHECK: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + // NOCOPY: define {{.*}}@"$s13shadow_copies6ClassCCACycfc" + init () + { + // CHECK: alloca %T13shadow_copies6ClassCC* + // CHECK-NOT: alloca + // NOCOPY-NOT: alloca + + // CHECK: call void @llvm.dbg.value(metadata i{{(64|32)}} 10 + // NOCOPY: call void @llvm.dbg.value(metadata i{{(64|32)}} 10 + let x = 10 + + use(x) + + use(x) + + // CHECK: ret + // NOCOPY: ret + } +} + +let c = ClassC() diff --git a/test/DebugInfo/variables.swift b/test/DebugInfo/variables.swift index c350815511b66..196802e6f8df5 100644 --- a/test/DebugInfo/variables.swift +++ b/test/DebugInfo/variables.swift @@ -72,7 +72,10 @@ myprint(tuple) // Arrays are represented as an instantiation of Array. // CHECK-DAG: ![[ARRAYTY:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Array", -// CHECK-DAG: !DIGlobalVariable(name: "array_of_tuples",{{.*}} type: ![[ARRAYTY]] +// CHECK-DAG: ![[ARRAY_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[ARRAYTY]] +// CHECK-DAG: ![[ARRAY_ELTS:.*]] = !{![[ARRAY_MEMBER]]} +// CHECK-DAG: ![[ARRAY_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[ARRAY_ELTS]] +// CHECK-DAG: !DIGlobalVariable(name: "array_of_tuples",{{.*}} type: ![[ARRAY_CONTAINER]] var array_of_tuples : [(a : Int, b : Int)] = [(1,2)] var twod : [[Int]] = [[1]] diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index ce1face21c57e..d541c4fea727e 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -293,6 +293,7 @@ _T03nix6testitSaySiGyFTv_ ---> outlined variable #0 of nix.testit() -> [Swift.In _T03nix6testitSaySiGyFTv0_ ---> outlined variable #1 of nix.testit() -> [Swift.Int] _T0So11UITextFieldC4textSSSgvgToTepb_ ---> outlined bridged method (pb) of @objc __C.UITextField.text.getter : Swift.String? _T0So11UITextFieldC4textSSSgvgToTeab_ ---> outlined bridged method (ab) of @objc __C.UITextField.text.getter : Swift.String? +$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_ ---> outlined bridged method (mbgnn) of @objc __C.Gizmo.doSomething([Swift.String]?) -> Any? _T04test1SVyxGAA1RA2A1ZRzAA1Y2ZZRpzl1A_AhaGPWT ---> {C} associated type witness table accessor for A.ZZ : test.Y in test.S : test.R in test _T0s24_UnicodeScalarExceptions33_0E4228093681F6920F0AB2E48B4F1C69LLVACycfC ---> {T:_T0s24_UnicodeScalarExceptions33_0E4228093681F6920F0AB2E48B4F1C69LLVACycfc} Swift.(_UnicodeScalarExceptions in _0E4228093681F6920F0AB2E48B4F1C69).init() -> Swift.(_UnicodeScalarExceptions in _0E4228093681F6920F0AB2E48B4F1C69) _T0D ---> _T0D @@ -328,6 +329,7 @@ _$S3BBBBf0602365061_ ---> _$S3BBBBf0602365061_ _$S3BBBBi0602365061_ ---> _$S3BBBBi0602365061_ _$S3BBBBv0602365061_ ---> _$S3BBBBv0602365061_ _T0lxxxmmmTk ---> _T0lxxxmmmTk +$s4Test5ProtoP8IteratorV10collectionAEy_qd__Gqd___tcfc ---> Test.Proto.Iterator.init(collection: A1) -> Test.Proto.Iterator $s4test3fooV4blahyAA1SV1fQryFQOy_Qo_AHF ---> test.foo.blah(< some>>.0) -> < some>>.0 $S3nix8MystructV1xACyxGx_tcfc7MyaliasL_ayx__GD ---> Myalias #1 in nix.Mystruct.init(x: A) -> nix.Mystruct $S3nix7MyclassCfd7MyaliasL_ayx__GD ---> Myalias #1 in nix.Myclass.deinit @@ -352,3 +354,5 @@ $s18resilient_protocol24ResilientDerivedProtocolPxAA0c4BaseE0Tn --> associated c $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF --> red.test(red.Res) -> red.Res $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP --> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) +$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () + diff --git a/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/main.swift new file mode 100644 index 0000000000000..39cf040875f08 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 8276a546203ebde599da50b466729230 + sequenceNumber: 0 + defsIDependUpon: [ 4, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 8276a546203ebde599da50b466729230 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: dynamicLookup + aspect: interface + context: '' + name: z + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: dynamicLookup + aspect: implementation + context: '' + name: z + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/output.json b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/yet-another.swift new file mode 100644 index 0000000000000..b12cbada09730 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-additional-kinds-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: f0a22821b1bfd1d40363b3f89c7a7693 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: f0a22821b1bfd1d40363b3f89c7a7693 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: dynamicLookup + aspect: interface + context: '' + name: z + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-after-fine/main.swift new file mode 100644 index 0000000000000..2e54000a1b441 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 42e5bb8d6f23bfc1b055b85bd466a86c + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 42e5bb8d6f23bfc1b055b85bd466a86c + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1zV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/chained-after-fine/main.swiftdeps new file mode 100644 index 0000000000000..ab68f2a75146f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 42e5bb8d6f23bfc1b055b85bd466a86c + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 42e5bb8d6f23bfc1b055b85bd466a86c + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-after-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/chained-after-fine/other.swiftdeps new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/output.json b/test/Driver/Dependencies/Inputs/chained-after-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swift new file mode 100644 index 0000000000000..463b4dfae1c71 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: aae342945c458d008a9989daac618092 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: aae342945c458d008a9989daac618092 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swiftdeps b/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swiftdeps new file mode 100644 index 0000000000000..463b4dfae1c71 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-after-fine/yet-another.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: aae342945c458d008a9989daac618092 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: aae342945c458d008a9989daac618092 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-fine/main.swift new file mode 100644 index 0000000000000..7ff49dfaab860 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 279f98f41b2fb1a907b1ce04fa09ce50 + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 279f98f41b2fb1a907b1ce04fa09ce50 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1zV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-fine/output.json b/test/Driver/Dependencies/Inputs/chained-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-fine/yet-another.swift new file mode 100644 index 0000000000000..3c9d96f11c369 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 59c8ff39595b320cb70847063b7410b9 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 59c8ff39595b320cb70847063b7410b9 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swift new file mode 100644 index 0000000000000..e2c53e24f18b3 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swiftdeps new file mode 100644 index 0000000000000..6269b88840fbf --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/main.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swiftdeps new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/output.json b/test/Driver/Dependencies/Inputs/chained-private-after-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swift new file mode 100644 index 0000000000000..6c6e6bd8f844a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 0435fef7d7170574edab927508293b15 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 0435fef7d7170574edab927508293b15 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swiftdeps new file mode 100644 index 0000000000000..6c6e6bd8f844a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-fine/yet-another.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 0435fef7d7170574edab927508293b15 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 0435fef7d7170574edab927508293b15 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swift new file mode 100644 index 0000000000000..154ad90f112e5 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swift @@ -0,0 +1,62 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: a4e513b4b5693bf6f50d6c0d531b542b + sequenceNumber: 0 + defsIDependUpon: [ 2, 4, 5, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: a4e513b4b5693bf6f50d6c0d531b542b + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: z + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: x + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swiftdeps new file mode 100644 index 0000000000000..90cb9c17cc4b0 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/main.swiftdeps @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: a4e513b4b5693bf6f50d6c0d531b542b + sequenceNumber: 0 + defsIDependUpon: [ 2, 4, 5 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: a4e513b4b5693bf6f50d6c0d531b542b + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: x + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swiftdeps new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/output.json b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swift new file mode 100644 index 0000000000000..045438f5550d6 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 913de1b71ef48d4c730689fd99d7090e + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 913de1b71ef48d4c730689fd99d7090e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swiftdeps new file mode 100644 index 0000000000000..045438f5550d6 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-fine/yet-another.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 913de1b71ef48d4c730689fd99d7090e + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 913de1b71ef48d4c730689fd99d7090e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swift new file mode 100644 index 0000000000000..b4ed07232136b --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swift @@ -0,0 +1,86 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: d281713b8f6ef5935679e8b3cbe6d5ce + sequenceNumber: 0 + defsIDependUpon: [ 2, 4, 5, 6, 7, 8, 9 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: d281713b8f6ef5935679e8b3cbe6d5ce + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1zV + name: z + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1xV + name: '' + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: a + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1xV + name: x + sequenceNumber: 9 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swiftdeps new file mode 100644 index 0000000000000..ad8a56b02c2e3 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/main.swiftdeps @@ -0,0 +1,70 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: d281713b8f6ef5935679e8b3cbe6d5ce + sequenceNumber: 0 + defsIDependUpon: [ 2, 4, 5, 6, 7 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: d281713b8f6ef5935679e8b3cbe6d5ce + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1xV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: a + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1xV + name: x + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swift new file mode 100644 index 0000000000000..ee50c4b42ca6f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 2221cd1a52ad1f50cba3610cd9d80d45 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 2221cd1a52ad1f50cba3610cd9d80d45 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: member + aspect: interface + context: 4main1aV + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: member + aspect: implementation + context: 4main1aV + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swiftdeps new file mode 100644 index 0000000000000..ee50c4b42ca6f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 2221cd1a52ad1f50cba3610cd9d80d45 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 2221cd1a52ad1f50cba3610cd9d80d45 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: member + aspect: interface + context: 4main1aV + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: member + aspect: implementation + context: 4main1aV + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/output.json b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swift new file mode 100644 index 0000000000000..c279ebaf043ab --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: d7ad1a83584a3d3976404bcb830d57d1 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: d7ad1a83584a3d3976404bcb830d57d1 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swiftdeps b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swiftdeps new file mode 100644 index 0000000000000..c279ebaf043ab --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-after-multiple-nominal-members-fine/yet-another.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: d7ad1a83584a3d3976404bcb830d57d1 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: d7ad1a83584a3d3976404bcb830d57d1 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-fine/main.swift b/test/Driver/Dependencies/Inputs/chained-private-fine/main.swift new file mode 100644 index 0000000000000..cd55008fda157 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-fine/main.swift @@ -0,0 +1,47 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: ea2e880fb7f4944d6f6093dad31c2ff9 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: ea2e880fb7f4944d6f6093dad31c2ff9 + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1zV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-fine/other.swift b/test/Driver/Dependencies/Inputs/chained-private-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-fine/output.json b/test/Driver/Dependencies/Inputs/chained-private-fine/output.json new file mode 100644 index 0000000000000..78134f1ab01d1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "./yet-another.swift": { + "object": "./yet-another.o", + "swift-dependencies": "./yet-another.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/chained-private-fine/yet-another.swift b/test/Driver/Dependencies/Inputs/chained-private-fine/yet-another.swift new file mode 100644 index 0000000000000..3c9d96f11c369 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/chained-private-fine/yet-another.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: yet-another.swiftdeps + fingerprint: 59c8ff39595b320cb70847063b7410b9 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: yet-another.swiftdeps + fingerprint: 59c8ff39595b320cb70847063b7410b9 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1zV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/a.swift b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/a.swift new file mode 100644 index 0000000000000..1b37c49d62fb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/a.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: a.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: a.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/bad.swift b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/bad.swift new file mode 100644 index 0000000000000..f9b4faa7b636c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/bad.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: b.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: b.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/c.swift b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/c.swift new file mode 100644 index 0000000000000..5238c4cef26d2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/c.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: c.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: c.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: c + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: c + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/output.json b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/output.json new file mode 100644 index 0000000000000..438752aa2616a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/check-interface-implementation-fine/output.json @@ -0,0 +1,33 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./b.swift": { + "object": "./b.o", + "swift-dependencies": "./b.swiftdeps" + }, + "./c.swift": { + "object": "./c.o", + "swift-dependencies": "./c.swiftdeps" + }, + "./d.swift": { + "object": "./d.o", + "swift-dependencies": "./d.swiftdeps" + }, + "./e.swift": { + "object": "./e.o", + "swift-dependencies": "./e.swiftdeps" + }, + "./f.swift": { + "object": "./f.o", + "swift-dependencies": "./f.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/crash-simple-fine/crash.swift b/test/Driver/Dependencies/Inputs/crash-simple-fine/crash.swift new file mode 100644 index 0000000000000..85eaae84fcadc --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-fine/crash.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './crash.swiftdeps' + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 5, 4, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './crash.swiftdeps' + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-fine/main.swift b/test/Driver/Dependencies/Inputs/crash-simple-fine/main.swift new file mode 100644 index 0000000000000..b8d18969d5a2c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './main.swiftdeps' + fingerprint: 316328d5be8544f05f3cc73c32cea2d2 + sequenceNumber: 0 + defsIDependUpon: [ 4, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './main.swiftdeps' + fingerprint: 316328d5be8544f05f3cc73c32cea2d2 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: M + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: M + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-fine/other.swift b/test/Driver/Dependencies/Inputs/crash-simple-fine/other.swift new file mode 100644 index 0000000000000..965cdbd4552c1 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-fine/other.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: 0665c1c79514536cfd19ee3359008f19 + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: 0665c1c79514536cfd19ee3359008f19 + sequenceNumber: 1 + defsIDependUpon: [ 5 ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: F + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: F + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: AssignmentPrecedence + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-fine/output.json b/test/Driver/Dependencies/Inputs/crash-simple-fine/output.json new file mode 100644 index 0000000000000..55ef51f19bb04 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./crash.swift": { + "object": "./crash.o", + "swift-dependencies": "./crash.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swift new file mode 100644 index 0000000000000..22d8fe572608b --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: crash.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: crash.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swiftdeps b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swiftdeps new file mode 100644 index 0000000000000..bf9965ea5ef76 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/crash.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: crash.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: crash.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swift new file mode 100644 index 0000000000000..75f41afc9a28c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swiftdeps new file mode 100644 index 0000000000000..f8db87488755d --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/main.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swift new file mode 100644 index 0000000000000..70e509c947cbd --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swiftdeps new file mode 100644 index 0000000000000..08e363ec1de1a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/other.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/output.json b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/output.json new file mode 100644 index 0000000000000..55ef51f19bb04 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./crash.swift": { + "object": "./crash.o", + "swift-dependencies": "./crash.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/crash.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/crash.swift new file mode 100644 index 0000000000000..7e7daa298c540 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/crash.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +provides-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/chained-after/main.o b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/crash.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-after/main.o rename to test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/crash.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/main.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/main.swift new file mode 100644 index 0000000000000..c6dd8d475b207 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/main.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +depends-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/chained-after/other.o b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/main.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-after/other.o rename to test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/main.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/other.swift b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/other.swift new file mode 100644 index 0000000000000..33392ce138612 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/other.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +depends-top-level: [!private a] diff --git a/test/Driver/Dependencies/Inputs/chained-after/yet-another.o b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/other.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-after/yet-another.o rename to test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/other.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/output.json b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/output.json new file mode 100644 index 0000000000000..55ef51f19bb04 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple-with-swiftdeps/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./crash.swift": { + "object": "./crash.o", + "swift-dependencies": "./crash.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/a.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/a.swift new file mode 100644 index 0000000000000..1b37c49d62fb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/a.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: a.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: a.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/b.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/b.swift new file mode 100644 index 0000000000000..94b97c820e1c0 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/b.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: b.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: b.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/bad.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/bad.swift new file mode 100644 index 0000000000000..fb8ba5df522e9 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/bad.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: bad.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: bad.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ 5 ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/c.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/c.swift new file mode 100644 index 0000000000000..191523e36ae3f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/c.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: c.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: c.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: c + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: c + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/d.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/d.swift new file mode 100644 index 0000000000000..5e83dd44702fc --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/d.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: d.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: d.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: d + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: d + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: c + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/e.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/e.swift new file mode 100644 index 0000000000000..d869269a08cc9 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/e.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: e.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: e.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: e + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: e + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/f.swift b/test/Driver/Dependencies/Inputs/fail-chained-fine/f.swift new file mode 100644 index 0000000000000..9aef8fa8d69bb --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/f.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: f.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: f.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: f + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: f + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: e + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-chained-fine/output.json b/test/Driver/Dependencies/Inputs/fail-chained-fine/output.json new file mode 100644 index 0000000000000..438752aa2616a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-chained-fine/output.json @@ -0,0 +1,33 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./b.swift": { + "object": "./b.o", + "swift-dependencies": "./b.swiftdeps" + }, + "./c.swift": { + "object": "./c.o", + "swift-dependencies": "./c.swiftdeps" + }, + "./d.swift": { + "object": "./d.o", + "swift-dependencies": "./d.swiftdeps" + }, + "./e.swift": { + "object": "./e.o", + "swift-dependencies": "./e.swiftdeps" + }, + "./f.swift": { + "object": "./f.o", + "swift-dependencies": "./f.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swift b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swift new file mode 100644 index 0000000000000..69b4df42447fe --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swift @@ -0,0 +1,39 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: bad.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: bad.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swiftdeps new file mode 100644 index 0000000000000..527f7825b3eaf --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/bad.swiftdeps @@ -0,0 +1,39 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: bad.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: bad.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swift b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swift new file mode 100644 index 0000000000000..dcabfbb17b9c0 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swift @@ -0,0 +1,31 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: dependsonbad.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: dependsonbad.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swiftdeps new file mode 100644 index 0000000000000..7a6124707d076 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-bad.swiftdeps @@ -0,0 +1,31 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: dependsonbad.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: dependsonbad.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swift b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swift new file mode 100644 index 0000000000000..db1727b5dea2f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: dependsonmain.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: dependsonmain.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swiftdeps new file mode 100644 index 0000000000000..f5ff3de10eb27 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/depends-on-main.swiftdeps @@ -0,0 +1,31 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: dependsonmain.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: dependsonmain.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swift b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swift new file mode 100644 index 0000000000000..7bcdbac8e03e5 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swift @@ -0,0 +1,39 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: main + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swiftdeps new file mode 100644 index 0000000000000..43ededaf30de4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/main.swiftdeps @@ -0,0 +1,39 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: main + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... + diff --git a/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/output.json b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/output.json new file mode 100644 index 0000000000000..981629c77c49e --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-interface-hash-fine/output.json @@ -0,0 +1,21 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "./depends-on-main.swift": { + "object": "./depends-on-main.o", + "swift-dependencies": "./depends-on-main.swiftdeps" + }, + "./depends-on-bad.swift": { + "object": "./depends-on-bad.o", + "swift-dependencies": "./depends-on-bad.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/fail-simple-fine/bad.swift b/test/Driver/Dependencies/Inputs/fail-simple-fine/bad.swift new file mode 100644 index 0000000000000..f3494a74be563 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-simple-fine/bad.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: bad.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: bad.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/fail-simple-fine/main.swift b/test/Driver/Dependencies/Inputs/fail-simple-fine/main.swift new file mode 100644 index 0000000000000..75f41afc9a28c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-simple-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-simple-fine/other.swift b/test/Driver/Dependencies/Inputs/fail-simple-fine/other.swift new file mode 100644 index 0000000000000..ddc5106f05d1c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-simple-fine/other.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-simple-fine/output.json b/test/Driver/Dependencies/Inputs/fail-simple-fine/output.json new file mode 100644 index 0000000000000..32ad1dd72d6f7 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-simple-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swift new file mode 100644 index 0000000000000..b262c5c8ce0ac --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swift @@ -0,0 +1,55 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './bad.swiftdeps' + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2, 5, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './bad.swiftdeps' + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +garbage: "" +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swiftdeps new file mode 100644 index 0000000000000..d9a1141f39ea6 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/bad.swiftdeps @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './bad.swiftdeps' + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2, 5, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './bad.swiftdeps' + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: bad + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swift new file mode 100644 index 0000000000000..3e02521f45483 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './depends-on-bad.swiftdeps' + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './depends-on-bad.swiftdeps' + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: DB + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: DB + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swiftdeps new file mode 100644 index 0000000000000..ff755423fc661 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-bad.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './depends-on-bad.swiftdeps' + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './depends-on-bad.swiftdeps' + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: DB + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: DB + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: bad + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swift new file mode 100644 index 0000000000000..59102cef0a1af --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: M + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: M + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swiftdeps new file mode 100644 index 0000000000000..427c0381ec65f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/depends-on-main.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: M + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: M + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swift new file mode 100644 index 0000000000000..59102cef0a1af --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './depends-on-main.swiftdeps' + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: M + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: M + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swiftdeps new file mode 100644 index 0000000000000..5844ed2c32e39 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/main.swiftdeps @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './main.swiftdeps' + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 5, 4, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './main.swiftdeps' + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: main + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: main + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/output.json b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/output.json new file mode 100644 index 0000000000000..981629c77c49e --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps-fine/output.json @@ -0,0 +1,21 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "./depends-on-main.swift": { + "object": "./depends-on-main.o", + "swift-dependencies": "./depends-on-main.swiftdeps" + }, + "./depends-on-bad.swift": { + "object": "./depends-on-bad.o", + "swift-dependencies": "./depends-on-bad.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/independent-fine/main.swift b/test/Driver/Dependencies/Inputs/independent-fine/main.swift new file mode 100644 index 0000000000000..9143f163412ce --- /dev/null +++ b/test/Driver/Dependencies/Inputs/independent-fine/main.swift @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/independent-fine/other.swift b/test/Driver/Dependencies/Inputs/independent-fine/other.swift new file mode 100644 index 0000000000000..3d9348aa25859 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/independent-fine/other.swift @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/independent-fine/output.json b/test/Driver/Dependencies/Inputs/independent-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/independent-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swift b/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swift new file mode 100644 index 0000000000000..a9561fcf725d2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swiftdeps new file mode 100644 index 0000000000000..a9561fcf725d2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-after-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swift b/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swift new file mode 100644 index 0000000000000..d8b260499b5cf --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +*** This is not a valid YAML file *** diff --git a/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swiftdeps new file mode 100644 index 0000000000000..3d9348aa25859 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-after-fine/other.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/malformed-after-fine/output.json b/test/Driver/Dependencies/Inputs/malformed-after-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-after-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swift b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swift new file mode 100644 index 0000000000000..a9561fcf725d2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swiftdeps new file mode 100644 index 0000000000000..a9561fcf725d2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: ec443bb982c3a06a433bdd47b85eeba2 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swift b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swift new file mode 100644 index 0000000000000..ccd7aeff500d0 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swift @@ -0,0 +1,24 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +bogusEntry: snort +... + diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swiftdeps new file mode 100644 index 0000000000000..3d9348aa25859 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/other.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/output.json b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/mutual-fine/main.swift b/test/Driver/Dependencies/Inputs/mutual-fine/main.swift new file mode 100644 index 0000000000000..53a6d2eb6ffb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-fine/main.swift @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 4, 8, 2, 7, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: A + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: A + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-fine/other.swift b/test/Driver/Dependencies/Inputs/mutual-fine/other.swift new file mode 100644 index 0000000000000..8d6ea70853470 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-fine/other.swift @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: same + sequenceNumber: 0 + defsIDependUpon: [ 8, 7, 2, 4, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: same + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: B + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: B + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-fine/output.json b/test/Driver/Dependencies/Inputs/mutual-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swift b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swift new file mode 100644 index 0000000000000..f10c8c1139062 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swift @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: does-change.swiftdeps + fingerprint: after + sequenceNumber: 0 + defsIDependUpon: [ 4, 8, 2, 7, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: does-change.swiftdeps + fingerprint: after + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: A + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: A + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swiftdeps b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swiftdeps new file mode 100644 index 0000000000000..c3f3e8151be88 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-change.swiftdeps @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: does-change.swiftdeps + fingerprint: before + sequenceNumber: 0 + defsIDependUpon: [ 4, 8, 2, 7, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: does-change.swiftdeps + fingerprint: before + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: A + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: A + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swift b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swift new file mode 100644 index 0000000000000..5dc734cb0e000 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swift @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: does-not-change.swiftdeps + fingerprint: same + sequenceNumber: 0 + defsIDependUpon: [ 8, 7, 2, 4, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: does-not-change.swiftdeps + fingerprint: same + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: B + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: B + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swiftdeps b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swiftdeps new file mode 100644 index 0000000000000..5dc734cb0e000 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/does-not-change.swiftdeps @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: does-not-change.swiftdeps + fingerprint: same + sequenceNumber: 0 + defsIDependUpon: [ 8, 7, 2, 4, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: does-not-change.swiftdeps + fingerprint: same + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: B + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: B + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/output.json b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/output.json new file mode 100644 index 0000000000000..dfbb111fcdce4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-interface-hash-fine/output.json @@ -0,0 +1,13 @@ +{ + "./does-change.swift": { + "object": "./does-change.o", + "swift-dependencies": "./does-change.swiftdeps" + }, + "./does-not-change.swift": { + "object": "./does-not-change.o", + "swift-dependencies": "./does-not-change.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swift b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swift new file mode 100644 index 0000000000000..2e42e0cc8b47d --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 741650dd633ae2cd89de22c880ddadc9 + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 741650dd633ae2cd89de22c880ddadc9 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: b + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swiftdeps new file mode 100644 index 0000000000000..0c0a447001ba3 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/main.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: 741650dd633ae2cd89de22c880ddadc9 + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: 741650dd633ae2cd89de22c880ddadc9 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swift b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swift new file mode 100644 index 0000000000000..8e9d62c832e6b --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: e12419713170057a991bc883225f56fc + sequenceNumber: 0 + defsIDependUpon: [ 4, 2] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: e12419713170057a991bc883225f56fc + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: b + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swiftdeps new file mode 100644 index 0000000000000..fedd90b310a49 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/other.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: e12419713170057a991bc883225f56fc + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: e12419713170057a991bc883225f56fc + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/output.json b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/main.swift b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/main.swift new file mode 100644 index 0000000000000..98f98ba6ec681 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/main.swift @@ -0,0 +1,3 @@ +# Dependencies after compilation: +depends-top-level: [a] +provides-top-level: [b] diff --git a/test/Driver/Dependencies/Inputs/chained-private-after/main.o b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/main.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-private-after/main.o rename to test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/main.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/other.swift b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/other.swift new file mode 100644 index 0000000000000..b6e0280958d77 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/other.swift @@ -0,0 +1,3 @@ +# Dependencies after compilation: +depends-top-level: [b] +provides-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/chained-private-after/other.o b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/other.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-private-after/other.o rename to test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/other.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/output.json b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/mutual-with-swiftdeps/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/nominal-members-fine/a-ext.swift b/test/Driver/Dependencies/Inputs/nominal-members-fine/a-ext.swift new file mode 100644 index 0000000000000..a68fd33d3ee02 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/nominal-members-fine/a-ext.swift @@ -0,0 +1,102 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: a-ext.swiftdeps + fingerprint: d19650fa01b61a0a8d27eee5258aa70e + sequenceNumber: 0 + defsIDependUpon: [ 4, 10, 7, 11, 6 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: a-ext.swiftdeps + fingerprint: d19650fa01b61a0a8d27eee5258aa70e + sequenceNumber: 1 + defsIDependUpon: [ 9, 8 ] + isProvides: true + - key: + kind: potentialMember + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: potentialMember + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: member + aspect: interface + context: 4main1aV + name: ext + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: member + aspect: implementation + context: 4main1aV + name: ext + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: Int + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: aaa + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 9 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1aaaV + name: '' + sequenceNumber: 10 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aaaV + name: Int + sequenceNumber: 11 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/nominal-members-fine/a.swift b/test/Driver/Dependencies/Inputs/nominal-members-fine/a.swift new file mode 100644 index 0000000000000..287d5a52883a6 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/nominal-members-fine/a.swift @@ -0,0 +1,78 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: a.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 0 + defsIDependUpon: [ 8, 4, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: a.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: potentialMember + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 6 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: potentialMember + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: member + aspect: interface + context: 4main1aV + name: init + sequenceNumber: 8 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-ext.swift b/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-ext.swift new file mode 100644 index 0000000000000..321e7f40733b6 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-ext.swift @@ -0,0 +1,70 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: depends-on-a-ext.swiftdeps + fingerprint: eef421a8e034b3c72f85a3106b51620e + sequenceNumber: 0 + defsIDependUpon: [ 6, 5, 7, 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: depends-on-a-ext.swiftdeps + fingerprint: eef421a8e034b3c72f85a3106b51620e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: V + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: V + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: ext + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: init + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-foo.swift b/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-foo.swift new file mode 100644 index 0000000000000..129ade88c6191 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/nominal-members-fine/depends-on-a-foo.swift @@ -0,0 +1,70 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: depends-on-a-foo.swiftdeps + fingerprint: 341612d23b9b4ab590b3d75e35b5c6e0 + sequenceNumber: 0 + defsIDependUpon: [ 6, 5, 4, 7, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: depends-on-a-foo.swiftdeps + fingerprint: 341612d23b9b4ab590b3d75e35b5c6e0 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: Q + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: Q + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: foo + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: member + aspect: interface + context: 4main1aV + name: init + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/nominal-members-fine/output.json b/test/Driver/Dependencies/Inputs/nominal-members-fine/output.json new file mode 100644 index 0000000000000..d4d6d49c54405 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/nominal-members-fine/output.json @@ -0,0 +1,21 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./a-ext.swift": { + "object": "./a-ext.o", + "swift-dependencies": "./a-ext.swiftdeps" + }, + "./depends-on-a-ext.swift": { + "object": "./depends-on-a-ext.o", + "swift-dependencies": "./depends-on-a-ext.swiftdeps" + }, + "./depends-on-a-foo.swift": { + "object": "./depends-on-a-foo.o", + "swift-dependencies": "./depends-on-a-foo.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swift new file mode 100644 index 0000000000000..b21ca1bfe8e11 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swiftdeps new file mode 100644 index 0000000000000..9183a8ba33282 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/main.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swift new file mode 100644 index 0000000000000..b0a83b5a9b5be --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swiftdeps new file mode 100644 index 0000000000000..b0a83b5a9b5be --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-after-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swift new file mode 100644 index 0000000000000..9183a8ba33282 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swift @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swiftdeps new file mode 100644 index 0000000000000..b21ca1bfe8e11 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swift new file mode 100644 index 0000000000000..b0a83b5a9b5be --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swiftdeps new file mode 100644 index 0000000000000..b0a83b5a9b5be --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/other.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-depends-before-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before/main.o b/test/Driver/Dependencies/Inputs/one-way-depends-before/main.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-before/other.o b/test/Driver/Dependencies/Inputs/one-way-depends-before/other.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-external-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-external-fine/main.swift new file mode 100644 index 0000000000000..c7d4f89856338 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-external-fine/main.swift @@ -0,0 +1,62 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 6, 5, 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: V + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: V + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: externalDepend + aspect: interface + context: '' + name: './main1-external' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: externalDepend + aspect: interface + context: '' + name: './main2-external' + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/chained-private-after/yet-another.o b/test/Driver/Dependencies/Inputs/one-way-external-fine/main1-external similarity index 100% rename from test/Driver/Dependencies/Inputs/chained-private-after/yet-another.o rename to test/Driver/Dependencies/Inputs/one-way-external-fine/main1-external diff --git a/test/Driver/Dependencies/Inputs/malformed-after/main.o b/test/Driver/Dependencies/Inputs/one-way-external-fine/main2-external similarity index 100% rename from test/Driver/Dependencies/Inputs/malformed-after/main.o rename to test/Driver/Dependencies/Inputs/one-way-external-fine/main2-external diff --git a/test/Driver/Dependencies/Inputs/one-way-external-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-external-fine/other.swift new file mode 100644 index 0000000000000..20b5cf60db63a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-external-fine/other.swift @@ -0,0 +1,70 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 6, 7, 2, 5, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: externalDepend + aspect: interface + context: '' + name: './other1-external' + sequenceNumber: 6 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: externalDepend + aspect: interface + context: '' + name: './other2-external' + sequenceNumber: 7 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/malformed-after/other.o b/test/Driver/Dependencies/Inputs/one-way-external-fine/other1-external similarity index 100% rename from test/Driver/Dependencies/Inputs/malformed-after/other.o rename to test/Driver/Dependencies/Inputs/one-way-external-fine/other1-external diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml/main.o b/test/Driver/Dependencies/Inputs/one-way-external-fine/other2-external similarity index 100% rename from test/Driver/Dependencies/Inputs/malformed-but-valid-yaml/main.o rename to test/Driver/Dependencies/Inputs/one-way-external-fine/other2-external diff --git a/test/Driver/Dependencies/Inputs/one-way-external-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-external-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-external-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-fine/main.swift new file mode 100644 index 0000000000000..b21ca1bfe8e11 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './main.swiftdeps' + fingerprint: 68a74ca633848ae5e65ddc9d5e28b0e6 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-fine/other.swift new file mode 100644 index 0000000000000..b0a83b5a9b5be --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: './other.swiftdeps' + fingerprint: befb33f4269c9adc0644b060f467ef06 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-fine/output.json new file mode 100644 index 0000000000000..f2eb4d2dddbeb --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swift new file mode 100644 index 0000000000000..2f22faafc626f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swiftdeps new file mode 100644 index 0000000000000..2f22faafc626f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swift new file mode 100644 index 0000000000000..b3fe2d3a5af0f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: x.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 5, 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: x.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swiftdeps new file mode 100644 index 0000000000000..4e4cfd71a465a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/other.swiftdeps @@ -0,0 +1,23 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... + diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-after-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after/main.o b/test/Driver/Dependencies/Inputs/one-way-provides-after/main.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-after/other.o b/test/Driver/Dependencies/Inputs/one-way-provides-after/other.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swift new file mode 100644 index 0000000000000..2f22faafc626f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swiftdeps new file mode 100644 index 0000000000000..2f22faafc626f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/main.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swift new file mode 100644 index 0000000000000..5310b5d7ab52c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swift @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: d41d8cd98f00b204e9800998ecf8427e + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swiftdeps new file mode 100644 index 0000000000000..b3fe2d3a5af0f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/other.swiftdeps @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: x.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 5, 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: x.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: FloatLiteralType + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: topLevel + aspect: interface + context: '' + name: IntegerLiteralType + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/output.json new file mode 100644 index 0000000000000..f847af2da52ff --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-provides-before-fine/output.json @@ -0,0 +1,13 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before/main.o b/test/Driver/Dependencies/Inputs/one-way-provides-before/main.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-provides-before/other.o b/test/Driver/Dependencies/Inputs/one-way-provides-before/other.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swift b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swift new file mode 100644 index 0000000000000..2f22faafc626f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swift @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swiftdeps new file mode 100644 index 0000000000000..f5b2d6b0dd35c --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/main.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: main.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swift b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swift new file mode 100644 index 0000000000000..fdebaf57ebfb4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: 72e95f4a11b98227c1f6ad6ea7f6cdba + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: topLevel + aspect: interface + context: '' + name: a + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: topLevel + aspect: implementation + context: '' + name: a + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swiftdeps b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swiftdeps new file mode 100644 index 0000000000000..bacf9fc59d6bd --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/other.swiftdeps @@ -0,0 +1,22 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: other.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 0 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: other.swiftdeps + fingerprint: f216f45027a3fa6bf3d16c1b05dd8feb + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/output.json b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/output.json new file mode 100644 index 0000000000000..f2eb4d2dddbeb --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps-fine/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/main.swift b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/main.swift new file mode 100644 index 0000000000000..c6dd8d475b207 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/main.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +depends-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/malformed-but-valid-yaml/other.o b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/main.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/malformed-but-valid-yaml/other.o rename to test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/main.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/other.swift b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/other.swift new file mode 100644 index 0000000000000..7e7daa298c540 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/other.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +provides-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after/main.o b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/other.swiftdeps similarity index 100% rename from test/Driver/Dependencies/Inputs/one-way-depends-after/main.o rename to test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/other.swiftdeps diff --git a/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/output.json b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/output.json new file mode 100644 index 0000000000000..f2eb4d2dddbeb --- /dev/null +++ b/test/Driver/Dependencies/Inputs/one-way-with-swiftdeps/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps", + "swiftmodule": "./main.swiftmodule", + "swiftdoc": "./main.swiftdoc", + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/a.swift b/test/Driver/Dependencies/Inputs/private-after-fine/a.swift new file mode 100644 index 0000000000000..9d80d172a2de4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/a.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/a.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/a.swiftdeps new file mode 100644 index 0000000000000..9d80d172a2de4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/a.swiftdeps @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/b.swift b/test/Driver/Dependencies/Inputs/private-after-fine/b.swift new file mode 100644 index 0000000000000..c256595b686c8 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/b.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: b.swiftdeps + fingerprint: 68e9c0980ab7233dfbd965d8c4354027 + sequenceNumber: 0 + defsIDependUpon: [ 5, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: b.swiftdeps + fingerprint: 68e9c0980ab7233dfbd965d8c4354027 + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/b.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/b.swiftdeps new file mode 100644 index 0000000000000..c256595b686c8 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/b.swiftdeps @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: b.swiftdeps + fingerprint: 68e9c0980ab7233dfbd965d8c4354027 + sequenceNumber: 0 + defsIDependUpon: [ 5, 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: b.swiftdeps + fingerprint: 68e9c0980ab7233dfbd965d8c4354027 + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/c.swift b/test/Driver/Dependencies/Inputs/private-after-fine/c.swift new file mode 100644 index 0000000000000..7aec07e89f1a4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/c.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1cV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1cV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/c.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/c.swiftdeps new file mode 100644 index 0000000000000..7aec07e89f1a4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/c.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1cV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1cV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/d.swift b/test/Driver/Dependencies/Inputs/private-after-fine/d.swift new file mode 100644 index 0000000000000..8c454a819f3fc --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/d.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1dV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1dV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/d.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/d.swiftdeps new file mode 100644 index 0000000000000..93de7a407514b --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/d.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/e.swift b/test/Driver/Dependencies/Inputs/private-after-fine/e.swift new file mode 100644 index 0000000000000..07a12351010e8 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/e.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: e.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: e.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1eV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1dV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/e.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/e.swiftdeps new file mode 100644 index 0000000000000..9208ef0fb1be2 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/e.swiftdeps @@ -0,0 +1,30 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: e.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: e.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1dV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/f.swift b/test/Driver/Dependencies/Inputs/private-after-fine/f.swift new file mode 100644 index 0000000000000..993aae48b3a01 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/f.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: f.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: f.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1fV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1fV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/f.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/f.swiftdeps new file mode 100644 index 0000000000000..993aae48b3a01 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/f.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: f.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: f.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ 4 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1fV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1fV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/g.swift b/test/Driver/Dependencies/Inputs/private-after-fine/g.swift new file mode 100644 index 0000000000000..cea259c341645 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/g.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: g.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: g.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1gV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1gV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1fV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/g.swiftdeps b/test/Driver/Dependencies/Inputs/private-after-fine/g.swiftdeps new file mode 100644 index 0000000000000..cea259c341645 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/g.swiftdeps @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: g.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: g.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1gV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1gV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1fV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-after-fine/output.json b/test/Driver/Dependencies/Inputs/private-after-fine/output.json new file mode 100644 index 0000000000000..c15439d5780e4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-after-fine/output.json @@ -0,0 +1,33 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./b.swift": { + "object": "./b.o", + "swift-dependencies": "./b.swiftdeps" + }, + "./c.swift": { + "object": "./c.o", + "swift-dependencies": "./c.swiftdeps" + }, + "./d.swift": { + "object": "./d.o", + "swift-dependencies": "./d.swiftdeps" + }, + "./e.swift": { + "object": "./e.o", + "swift-dependencies": "./e.swiftdeps" + }, + "./f.swift": { + "object": "./f.o", + "swift-dependencies": "./f.swiftdeps" + }, + "./g.swift": { + "object": "./g.o", + "swift-dependencies": "./g.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/private-after/a.o b/test/Driver/Dependencies/Inputs/private-after/a.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/b.o b/test/Driver/Dependencies/Inputs/private-after/b.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/c.o b/test/Driver/Dependencies/Inputs/private-after/c.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/d.o b/test/Driver/Dependencies/Inputs/private-after/d.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/e.o b/test/Driver/Dependencies/Inputs/private-after/e.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/f.o b/test/Driver/Dependencies/Inputs/private-after/f.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-after/g.o b/test/Driver/Dependencies/Inputs/private-after/g.o deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/test/Driver/Dependencies/Inputs/private-fine/a.swift b/test/Driver/Dependencies/Inputs/private-fine/a.swift new file mode 100644 index 0000000000000..9d80d172a2de4 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/a.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: A.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1aV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/private-fine/b.swift b/test/Driver/Dependencies/Inputs/private-fine/b.swift new file mode 100644 index 0000000000000..c1ece8eaba069 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/b.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: b.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: b.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1bV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1aV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-fine/c.swift b/test/Driver/Dependencies/Inputs/private-fine/c.swift new file mode 100644 index 0000000000000..977466a86f224 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/c.swift @@ -0,0 +1,54 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: c.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ 5 ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1cV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1cV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false + - key: + kind: nominal + aspect: interface + context: 4main1bV + name: '' + sequenceNumber: 5 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-fine/d.swift b/test/Driver/Dependencies/Inputs/private-fine/d.swift new file mode 100644 index 0000000000000..a2c4fc4b078bb --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/d.swift @@ -0,0 +1,46 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 0 + defsIDependUpon: [ 2, 4 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: d.swiftdeps + fingerprint: 9e53bc0d9f7b3db367329e46ec87a57a + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1dV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1dV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1cV + name: '' + sequenceNumber: 4 + defsIDependUpon: [ ] + isProvides: false +... diff --git a/test/Driver/Dependencies/Inputs/private-fine/e.swift b/test/Driver/Dependencies/Inputs/private-fine/e.swift new file mode 100644 index 0000000000000..5a83550fc2d60 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/e.swift @@ -0,0 +1,38 @@ +# Fine-grained v0 +--- +allNodes: + - key: + kind: sourceFileProvide + aspect: interface + context: '' + name: e.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 0 + defsIDependUpon: [ 2 ] + isProvides: true + - key: + kind: sourceFileProvide + aspect: implementation + context: '' + name: e.swiftdeps + fingerprint: 605b8543d8bbf247217381a68ce188b8 + sequenceNumber: 1 + defsIDependUpon: [ ] + isProvides: true + - key: + kind: nominal + aspect: interface + context: 4main1eV + name: '' + sequenceNumber: 2 + defsIDependUpon: [ 0 ] + isProvides: true + - key: + kind: nominal + aspect: implementation + context: 4main1eV + name: '' + sequenceNumber: 3 + defsIDependUpon: [ ] + isProvides: true +... diff --git a/test/Driver/Dependencies/Inputs/private-fine/output.json b/test/Driver/Dependencies/Inputs/private-fine/output.json new file mode 100644 index 0000000000000..497186398e457 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/private-fine/output.json @@ -0,0 +1,25 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./b.swift": { + "object": "./b.o", + "swift-dependencies": "./b.swiftdeps" + }, + "./c.swift": { + "object": "./c.o", + "swift-dependencies": "./c.swiftdeps" + }, + "./d.swift": { + "object": "./d.o", + "swift-dependencies": "./d.swiftdeps" + }, + "./e.swift": { + "object": "./e.o", + "swift-dependencies": "./e.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/touch.py b/test/Driver/Dependencies/Inputs/touch.py index 99e9c339b94d5..f5c609089303c 100755 --- a/test/Driver/Dependencies/Inputs/touch.py +++ b/test/Driver/Dependencies/Inputs/touch.py @@ -23,6 +23,6 @@ # Update the output file mtime, or create it if necessary. # From http://stackoverflow.com/a/1160227. -for outputFile in sys.argv[1:]: +for outputFile in sys.argv[2:]: with open(outputFile, 'a'): os.utime(outputFile, (timeVal, timeVal)) diff --git a/test/Driver/Dependencies/bindings-build-record-options.swift b/test/Driver/Dependencies/bindings-build-record-options.swift index ed218b7332c5d..f858428433b7d 100644 --- a/test/Driver/Dependencies/bindings-build-record-options.swift +++ b/test/Driver/Dependencies/bindings-build-record-options.swift @@ -2,51 +2,53 @@ // RUN: cp -r %S/Inputs/bindings-build-record/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-INITIAL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings -driver-show-incremental ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-INITIAL // MUST-EXEC-INITIAL-NOT: warning -// MUST-EXEC-INITIAL: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC-INITIAL: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC-INITIAL: inputs: ["./yet-another.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// MUST-EXEC-INITIAL: inputs: ["./main.swift"], output: {object: "./main.o", swift-dependencies: "./main.swiftdeps"} +// MUST-EXEC-INITIAL: inputs: ["./other.swift"], output: {object: "./other.o", swift-dependencies: "./other.swiftdeps"} +// MUST-EXEC-INITIAL: inputs: ["./yet-another.swift"], output: {object: "./yet-another.o", swift-dependencies: "./yet-another.swiftdeps"} + + // MUST-EXEC-ALL-NOT: warning // MUST-EXEC-ALL: inputs: ["./main.swift"], output: {{[{].*[}]$}} // MUST-EXEC-ALL: inputs: ["./other.swift"], output: {{[{].*[}]$}} // MUST-EXEC-ALL: inputs: ["./yet-another.swift"], output: {{[{].*[}]$}} -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC // NO-EXEC: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: check-dependencies // NO-EXEC: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: check-dependencies // NO-EXEC: inputs: ["./yet-another.swift"], output: {{[{].*[}]}}, condition: check-dependencies -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings -serialize-diagnostics ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings -serialize-diagnostics ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -serialize-diagnostics -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -serialize-diagnostics -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -DDEBUG -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC-ALL +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -DDEBUG -I. -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC diff --git a/test/Driver/Dependencies/bindings-build-record.swift b/test/Driver/Dependencies/bindings-build-record.swift index 60fb5ed10c9db..95ffe59d0325f 100644 --- a/test/Driver/Dependencies/bindings-build-record.swift +++ b/test/Driver/Dependencies/bindings-build-record.swift @@ -3,12 +3,13 @@ // RUN: cp -r %S/Inputs/bindings-build-record/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* -// RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -driver-show-incremental -output-file-map %t/output.json 2>&1 |%FileCheck %s -check-prefix=MUST-EXEC // MUST-EXEC-NOT: warning -// MUST-EXEC: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC: inputs: ["./yet-another.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// MUST-EXEC: inputs: ["./main.swift"], output: {object: "./main.o", swift-dependencies: "./main.swiftdeps"} +// MUST-EXEC: inputs: ["./other.swift"], output: {object: "./other.o", swift-dependencies: "./other.swiftdeps"} +// MUST-EXEC: inputs: ["./yet-another.swift"], output: {object: "./yet-another.o", swift-dependencies: "./yet-another.swiftdeps"} +// MUST-EXEC: Disabling incremental build: could not read build record // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0], "./yet-another.swift": [443865900, 0]}, build_time: [443865901, 0]}' > %t/main~buildrecord.swiftdeps // RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | %FileCheck %s -check-prefix=NO-EXEC diff --git a/test/Driver/Dependencies/build-record-invalid.swift b/test/Driver/Dependencies/build-record-invalid.swift index bd08aad561e5e..c32b767c9d2d4 100644 --- a/test/Driver/Dependencies/build-record-invalid.swift +++ b/test/Driver/Dependencies/build-record-invalid.swift @@ -2,11 +2,11 @@ // RUN: cp -r %S/Inputs/bindings-build-record/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s -check-prefix=CHECK-ALL-BUILT +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s -check-prefix=CHECK-ALL-BUILT // CHECK-ALL-BUILT: Handled main.swift // CHECK-ALL-BUILT: Handled other.swift // RUN: echo '{version: "bogus", inputs: {"./main.swift": [443865900, 0], "./other.swift": !private [443865900, 0], "./yet-another.swift": !dirty [443865900, 0]}}' > %t/main~buildrecord.swiftdeps // RUN: echo 'provides-nominal: garbage' > %t/main.swiftdeps -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s -check-prefix=CHECK-ALL-BUILT +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s -check-prefix=CHECK-ALL-BUILT diff --git a/test/Driver/Dependencies/chained-additional-kinds-fine.swift b/test/Driver/Dependencies/chained-additional-kinds-fine.swift new file mode 100644 index 0000000000000..0e24992119b73 --- /dev/null +++ b/test/Driver/Dependencies/chained-additional-kinds-fine.swift @@ -0,0 +1,26 @@ +// other ==> main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-additional-kinds-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Handled yet-another.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled other.swift +// CHECK-THIRD-DAG: Handled main.swift +// CHECK-THIRD-DAG: Handled yet-another.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/chained-additional-kinds.swift b/test/Driver/Dependencies/chained-additional-kinds.swift index f7b4cbf30375a..18f996ad99a14 100644 --- a/test/Driver/Dependencies/chained-additional-kinds.swift +++ b/test/Driver/Dependencies/chained-additional-kinds.swift @@ -4,25 +4,23 @@ // RUN: cp -r %S/Inputs/chained-additional-kinds/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift // CHECK-FIRST: Handled yet-another.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// CHECK-THIRD: Handled other.swift +// CHECK-THIRD-DAG: Handled other.swift // CHECK-THIRD-DAG: Handled main.swift // CHECK-THIRD-DAG: Handled yet-another.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s - -// RUN: touch -t 201401240008 %t/other.swift +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/chained-after-fine.swift b/test/Driver/Dependencies/chained-after-fine.swift new file mode 100644 index 0000000000000..b14dfe39727e5 --- /dev/null +++ b/test/Driver/Dependencies/chained-after-fine.swift @@ -0,0 +1,23 @@ +/// other ==> main | yet-another +/// other ==> main +==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/chained-after-fine/*.swiftdeps %t +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift +// CHECK-THIRD: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-after.swift b/test/Driver/Dependencies/chained-after.swift index fe4f5c4ebc974..364efa7918945 100644 --- a/test/Driver/Dependencies/chained-after.swift +++ b/test/Driver/Dependencies/chained-after.swift @@ -6,18 +6,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/chained-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// CHECK-THIRD: Handled other.swift // CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift // CHECK-THIRD: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-fine.swift b/test/Driver/Dependencies/chained-fine.swift new file mode 100644 index 0000000000000..9542da2740385 --- /dev/null +++ b/test/Driver/Dependencies/chained-fine.swift @@ -0,0 +1,32 @@ +// other ==> main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Handled yet-another.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled other.swift +// CHECK-THIRD-DAG: Handled main.swift +// CHECK-THIRD-DAG: Handled yet-another.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// RUN: touch -t 201401240008 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// RUN: touch -t 201401240009 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./yet-another.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/chained-private-after-fine.swift b/test/Driver/Dependencies/chained-private-after-fine.swift new file mode 100644 index 0000000000000..5ff06392fa42b --- /dev/null +++ b/test/Driver/Dependencies/chained-private-after-fine.swift @@ -0,0 +1,23 @@ +/// other --> main ==> yet-another +/// other ==>+ main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-private-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/chained-private-after-fine/*.swiftdeps %t +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift +// CHECK-SECOND: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-after-multiple-fine.swift b/test/Driver/Dependencies/chained-private-after-multiple-fine.swift new file mode 100644 index 0000000000000..e40dc1752b3d1 --- /dev/null +++ b/test/Driver/Dependencies/chained-private-after-multiple-fine.swift @@ -0,0 +1,27 @@ +/// other --> main ==> yet-another +/// other ==>+ main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-private-after-multiple-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v + + + + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/chained-private-after-multiple-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift +// CHECK-SECOND-DAG: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-after-multiple-nominal-members-fine.swift b/test/Driver/Dependencies/chained-private-after-multiple-nominal-members-fine.swift new file mode 100644 index 0000000000000..f240f6ce4f15e --- /dev/null +++ b/test/Driver/Dependencies/chained-private-after-multiple-nominal-members-fine.swift @@ -0,0 +1,23 @@ +/// other --> main ==> yet-another +/// other ==>+ main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-private-after-multiple-nominal-members-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/chained-private-after-multiple-nominal-members-fine/*.swiftdeps %t +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift +// CHECK-SECOND-DAG: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-after-multiple-nominal-members.swift b/test/Driver/Dependencies/chained-private-after-multiple-nominal-members.swift index a0ffd2e157550..a44c27b655b9a 100644 --- a/test/Driver/Dependencies/chained-private-after-multiple-nominal-members.swift +++ b/test/Driver/Dependencies/chained-private-after-multiple-nominal-members.swift @@ -6,18 +6,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/chained-private-after-multiple-nominal-members/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift -// CHECK-SECOND: Handled main.swift +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift // CHECK-SECOND: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-after-multiple.swift b/test/Driver/Dependencies/chained-private-after-multiple.swift index ab54a8c96888f..a3ca757a3aa8a 100644 --- a/test/Driver/Dependencies/chained-private-after-multiple.swift +++ b/test/Driver/Dependencies/chained-private-after-multiple.swift @@ -6,18 +6,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/chained-private-after-multiple/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift -// CHECK-SECOND: Handled main.swift +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift // CHECK-SECOND: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-after.swift b/test/Driver/Dependencies/chained-private-after.swift index e5365c7559351..570653a2e3bd3 100644 --- a/test/Driver/Dependencies/chained-private-after.swift +++ b/test/Driver/Dependencies/chained-private-after.swift @@ -1,23 +1,24 @@ /// other --> main ==> yet-another /// other ==>+ main ==> yet-another +/// Coarse and fine // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/chained-private-after/* %t // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/chained-private-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift -// CHECK-SECOND: Handled main.swift +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift // CHECK-SECOND: Handled yet-another.swift diff --git a/test/Driver/Dependencies/chained-private-fine.swift b/test/Driver/Dependencies/chained-private-fine.swift new file mode 100644 index 0000000000000..c9a2724621c82 --- /dev/null +++ b/test/Driver/Dependencies/chained-private-fine.swift @@ -0,0 +1,30 @@ +/// other --> main ==> yet-another + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/chained-private-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Handled yet-another.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v >%t/outputToCheck 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-THIRD %s < %t/outputToCheck + +// Driver now schedules jobs in the order the inputs were given, but +// either order is fine. +// CHECK-THIRD-DAG: Handled main.swift +// CHECK-THIRD-DAG: Handled other.swift + +// RUN: %FileCheck -check-prefix=CHECK-THIRD-EXCLUSION %s < %t/outputToCheck + +// CHECK-THIRD-EXCLUSION-NOT: Handled yet-another.swift + diff --git a/test/Driver/Dependencies/chained-private.swift b/test/Driver/Dependencies/chained-private.swift index c02deb99be5d6..ebe36ce9b9b8e 100644 --- a/test/Driver/Dependencies/chained-private.swift +++ b/test/Driver/Dependencies/chained-private.swift @@ -4,22 +4,27 @@ // RUN: cp -r %S/Inputs/chained-private/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift // CHECK-FIRST: Handled yet-another.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v >%t/outputToCheck 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-THIRD %s < %t/outputToCheck -// CHECK-THIRD-NOT: Handled yet-another.swift -// CHECK-THIRD: Handled other.swift -// CHECK-THIRD-NOT: Handled yet-another.swift +// Driver now schedules jobs in the order the inputs were given, but +// either order is fine. // CHECK-THIRD-DAG: Handled main.swift -// CHECK-THIRD-NOT: Handled yet-another.swift +// CHECK-THIRD-DAG: Handled other.swift + +// RUN: %FileCheck -check-prefix=CHECK-THIRD-EXCLUSION %s < %t/outputToCheck + +// CHECK-THIRD-EXCLUSION-NOT: Handled yet-another.swift + diff --git a/test/Driver/Dependencies/chained.swift b/test/Driver/Dependencies/chained.swift index f83bd12141a92..58d76f0350431 100644 --- a/test/Driver/Dependencies/chained.swift +++ b/test/Driver/Dependencies/chained.swift @@ -4,29 +4,29 @@ // RUN: cp -r %S/Inputs/chained/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift // CHECK-FIRST: Handled yet-another.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// CHECK-THIRD: Handled other.swift +// CHECK-THIRD-DAG: Handled other.swift // CHECK-THIRD-DAG: Handled main.swift // CHECK-THIRD-DAG: Handled yet-another.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift ./yet-another.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240008 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./yet-another.swift ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240009 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./yet-another.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./yet-another.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/check-interface-implementation-fine.swift b/test/Driver/Dependencies/check-interface-implementation-fine.swift new file mode 100644 index 0000000000000..5997b878bf2c4 --- /dev/null +++ b/test/Driver/Dependencies/check-interface-implementation-fine.swift @@ -0,0 +1,40 @@ +/// The fine-grained dependency graph has implicit dependencies from interfaces to implementations. +/// These are not presently tested because depends nodes are marked as depending in the interface, +/// as of 1/9/20. But this test will check fail if those links are not followed. + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/check-interface-implementation-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./c.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled a.swift +// CHECK-FIRST: Handled c.swift +// CHECK-FIRST: Handled bad.swift + +// CHECK-RECORD-CLEAN-DAG: "./a.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./bad.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./c.swift": [ + + +// RUN: touch -t 201401240006 %t/a.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./bad.swift ./c.swift -module-name main -j1 -v -driver-show-incremental > %t/a.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-A %s < %t/main~buildrecord.swiftdeps + +// CHECK-A: Handled a.swift +// CHECK-A: Handled bad.swift +// NEGATIVE-A-NOT: Handled c.swift + +// CHECK-RECORD-A-DAG: "./a.swift": [ +// CHECK-RECORD-A-DAG: "./bad.swift": !private [ +// CHECK-RECORD-A-DAG: "./c.swift": !private [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./bad.swift ./c.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix CHECK-BC %s + +// CHECK-BC-NOT: Handled a.swift +// CHECK-BC-DAG: Handled bad.swift +// CHECK-BC-DAG: Handled c.swift diff --git a/test/Driver/Dependencies/crash-added-fine.swift b/test/Driver/Dependencies/crash-added-fine.swift new file mode 100644 index 0000000000000..bb046909a3030 --- /dev/null +++ b/test/Driver/Dependencies/crash-added-fine.swift @@ -0,0 +1,39 @@ +/// crash ==> main | crash --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/crash-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled main.swift +// CHECK-INITIAL: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -c -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./crash.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps + +// CHECK-ADDED-NOT: Handled +// CHECK-ADDED: Handled crash.swift +// CHECK-ADDED-NOT: Handled + +// CHECK-RECORD-ADDED-DAG: "./crash.swift": !private [ +// CHECK-RECORD-ADDED-DAG: "./main.swift": [ +// CHECK-RECORD-ADDED-DAG: "./other.swift": [ + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/crash-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// RUN: cd %t && not %swiftc_driver -c -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps + + +// RUN: cd %t && %swiftc_driver -c -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIXED %s + +// CHECK-FIXED-DAG: Handled crash.swift +// CHECK-FIXED-DAG: Handled main.swift +// CHECK-FIXED-DAG: Handled other.swift diff --git a/test/Driver/Dependencies/crash-added.swift b/test/Driver/Dependencies/crash-added.swift index 2e4f6af7bbb3b..fdd4cb1707804 100644 --- a/test/Driver/Dependencies/crash-added.swift +++ b/test/Driver/Dependencies/crash-added.swift @@ -4,13 +4,13 @@ // RUN: cp -r %S/Inputs/crash-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -c -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL-NOT: warning // CHECK-INITIAL: Handled main.swift // CHECK-INITIAL: Handled other.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./crash.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: cd %t && not %swiftc_driver -c -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./crash.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s // RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps // CHECK-ADDED-NOT: Handled @@ -26,7 +26,7 @@ // RUN: cp -r %S/Inputs/crash-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -c -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: cd %t && not %swiftc_driver -c -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s // RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/crash-new-fine.swift b/test/Driver/Dependencies/crash-new-fine.swift new file mode 100644 index 0000000000000..1dfaa848bb73e --- /dev/null +++ b/test/Driver/Dependencies/crash-new-fine.swift @@ -0,0 +1,76 @@ +/// crash ==> main | crash --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/crash-simple-with-swiftdeps-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// Initially compile all inputs, crash will fail. + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// CHECK-NOT: warning +// CHECK: Handled main.swift +// CHECK: Handled crash.swift +// CHECK-NOT: Handled other.swift + +// Put crash.swift first to assure it gets scheduled first. +// The others get queued, but not dispatched because crash crashes. + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s + +// CHECK-BAD-ONLY-NOT: warning +// CHECK-BAD-ONLY-NOT: Handled +// CHECK-BAD-ONLY: Handled crash.swift +// CHECK-BAD-ONLY-NOT: Handled + +// Make crash succeed and all get compiled, exactly once. + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// CHECK-OKAY: Handled main.swift +// CHECK-OKAY: Handled crash.swift +// CHECK-OKAY: Handled other.swift +// CHECK-OKAY-NOT: Handled + +// Make crash crash again: + +// RUN: touch -t 201401240006 %t/crash.swift +// RUN: rm %t/crash.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s + +// And repair crash: + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s + +// CHECK-OKAY-2-DAG: Handled crash.swift +// CHECK-OKAY-2-DAG: Handled other.swift +// CHECK-OKAY-2-DAG: Handled main.swift + +// Touch main so its newer, remove main.swiftdeps and make crash crash: +// Driver will fall back to non-incremental, will compile main, +// will compile crash, and then stop. + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: rm %t/main.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NO-MAIN-SWIFTDEPS %s + +// CHECK-NO-MAIN-SWIFTDEPS-NOT: warning +// CHECK-NO-MAIN-SWIFTDEPS: Handled main.swift +// CHECK-NO-MAIN-SWIFTDEPS: Handled crash.swift +// CHECK-NO-MAIN-SWIFTDEPS-NOT: Handled other.swift + + +// Touch all files earlier than last compiled date in the build record. + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 -driver-show-incremental | %FileCheck -check-prefix=CHECK-CURRENT-WITH-CRASH %s + +// CHECK-CURRENT-WITH-CRASH: Handled main.swift +// CHECK-CURRENT-WITH-CRASH: Handled crash.swift +// CHECK-CURRENT-WITH-CRASH: Handled other.swift +// CHECK-CURRENT-WITH-CRASH-NOT: Handled + +// Touch other, but remove its swiftdeps. Should compile everything. + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: rm %t/other.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s diff --git a/test/Driver/Dependencies/crash-new.swift b/test/Driver/Dependencies/crash-new.swift index e7d5ba051e9c8..2d90e1f65fd7d 100644 --- a/test/Driver/Dependencies/crash-new.swift +++ b/test/Driver/Dependencies/crash-new.swift @@ -1,45 +1,76 @@ /// crash ==> main | crash --> other // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/crash-simple/* %t +// RUN: cp -r %S/Inputs/crash-simple-with-swiftdeps/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// Initially compile all inputs, crash will fail. + +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s // CHECK-NOT: warning // CHECK: Handled main.swift // CHECK: Handled crash.swift // CHECK-NOT: Handled other.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s +// Put crash.swift first to assure it gets scheduled first. +// The others get queued, but not dispatched because crash crashes. + +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s // CHECK-BAD-ONLY-NOT: warning // CHECK-BAD-ONLY-NOT: Handled // CHECK-BAD-ONLY: Handled crash.swift // CHECK-BAD-ONLY-NOT: Handled -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// Make crash succeed and all get compiled, exactly once. + +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s // CHECK-OKAY: Handled main.swift // CHECK-OKAY: Handled crash.swift // CHECK-OKAY: Handled other.swift // CHECK-OKAY-NOT: Handled +// Make crash crash again: + // RUN: touch -t 201401240006 %t/crash.swift // RUN: rm %t/crash.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s + +// And repair crash: // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s + +// CHECK-OKAY-2-DAG: Handled crash.swift +// CHECK-OKAY-2-DAG: Handled other.swift +// CHECK-OKAY-2-DAG: Handled main.swift -// CHECK-OKAY-2: Handled crash.swift -// CHECK-OKAY-2: Handled other.swift -// CHECK-OKAY-2: Handled main.swift +// Touch main so its newer, remove main.swiftdeps and make crash crash: +// Driver will fall back to non-incremental, will compile main, +// will compile crash, and then stop. // RUN: touch -t 201401240006 %t/main.swift // RUN: rm %t/main.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NO-MAIN-SWIFTDEPS %s + +// CHECK-NO-MAIN-SWIFTDEPS-NOT: warning +// CHECK-NO-MAIN-SWIFTDEPS: Handled main.swift +// CHECK-NO-MAIN-SWIFTDEPS: Handled crash.swift +// CHECK-NO-MAIN-SWIFTDEPS-NOT: Handled other.swift + + +// Touch all files earlier than last compiled date in the build record. // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 -driver-show-incremental | %FileCheck -check-prefix=CHECK-CURRENT-WITH-CRASH %s + +// CHECK-CURRENT-WITH-CRASH: Handled main.swift +// CHECK-CURRENT-WITH-CRASH: Handled crash.swift +// CHECK-CURRENT-WITH-CRASH: Handled other.swift +// CHECK-CURRENT-WITH-CRASH-NOT: Handled + +// Touch other, but remove its swiftdeps. Should compile everything. + // RUN: touch -t 201401240006 %t/other.swift // RUN: rm %t/other.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s diff --git a/test/Driver/Dependencies/crash-simple-fine.swift b/test/Driver/Dependencies/crash-simple-fine.swift new file mode 100644 index 0000000000000..213a8ca615e37 --- /dev/null +++ b/test/Driver/Dependencies/crash-simple-fine.swift @@ -0,0 +1,31 @@ +/// crash ==> main | crash --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/crash-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled crash.swift +// CHECK-FIRST: Handled other.swift + +// RUN: touch -t 201401240006 %t/crash.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: Handled crash.swift +// CHECK-SECOND-NOT: Handled main.swift +// CHECK-SECOND-NOT: Handled other.swift + +// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-RECORD-DAG: "./crash.swift": !private [ +// CHECK-RECORD-DAG: "./main.swift": !private [ +// CHECK-RECORD-DAG: "./other.swift": !private [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled main.swift +// CHECK-THIRD-DAG: Handled crash.swift +// CHECK-THIRD-DAG: Handled other.swift diff --git a/test/Driver/Dependencies/crash-simple.swift b/test/Driver/Dependencies/crash-simple.swift index 49db203066f66..5aa604efab773 100644 --- a/test/Driver/Dependencies/crash-simple.swift +++ b/test/Driver/Dependencies/crash-simple.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/crash-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift @@ -12,13 +12,14 @@ // CHECK-FIRST: Handled other.swift // RUN: touch -t 201401240006 %t/crash.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: Handled crash.swift // CHECK-SECOND-NOT: Handled main.swift // CHECK-SECOND-NOT: Handled other.swift +// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + // CHECK-RECORD-DAG: "./crash.swift": !dirty [ // CHECK-RECORD-DAG: "./main.swift": !dirty [ // CHECK-RECORD-DAG: "./other.swift": !private [ diff --git a/test/Driver/Dependencies/dependencies-preservation-fine.swift b/test/Driver/Dependencies/dependencies-preservation-fine.swift new file mode 100644 index 0000000000000..cf2551280cbd5 --- /dev/null +++ b/test/Driver/Dependencies/dependencies-preservation-fine.swift @@ -0,0 +1,20 @@ +// REQUIRES: shell +// Verify that the top-level build record file from the last incremental +// compilation is preserved with the same name, suffixed by a '~'. + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json + +// RUN: %FileCheck -check-prefix CHECK-ORIGINAL %s < main~buildrecord.swiftdeps~ +// CHECK-ORIGINAL: inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]} + +// RUN: %FileCheck -check-prefix CHECK-OVERWRITTEN %s < main~buildrecord.swiftdeps +// CHECK-OVERWRITTEN: version: "{{.*}}" +// CHECK-OVERWRITTEN: options: "{{.*}}" +// CHECK-OVERWRITTEN: build_time: [{{[0-9]*}}, {{[0-9]*}}] +// CHECK-OVERWRITTEN: inputs: +// CHECK-OVERWRITTEN: "./main.swift": [443865900, 0] +// CHECK-OVERWRITTEN: "./other.swift": [443865900, 0] diff --git a/test/Driver/Dependencies/dependencies-preservation.swift b/test/Driver/Dependencies/dependencies-preservation.swift index c88a5acd05a39..937324a008c44 100644 --- a/test/Driver/Dependencies/dependencies-preservation.swift +++ b/test/Driver/Dependencies/dependencies-preservation.swift @@ -6,7 +6,7 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json // RUN: %FileCheck -check-prefix CHECK-ORIGINAL %s < main~buildrecord.swiftdeps~ // CHECK-ORIGINAL: inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]} diff --git a/test/Driver/Dependencies/driver-show-incremental-arguments-fine.swift b/test/Driver/Dependencies/driver-show-incremental-arguments-fine.swift new file mode 100644 index 0000000000000..f307eeba92e29 --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-arguments-fine.swift @@ -0,0 +1,25 @@ +// REQUIRES: shell +// Test that when: +// +// 1. Using -incremental -v -driver-show-incremental, and... +// 2. ...the arguments passed to the Swift compiler version differ from the ones +// used in the original compilation... +// +// ...then the driver prints a message indicating that incremental compilation +// is disabled. + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled +// CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-ARGS-MISMATCH %s +// CHECK-ARGS-MISMATCH: Incremental compilation has been disabled{{.*}}different arguments +// CHECK-ARGS-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} + + diff --git a/test/Driver/Dependencies/driver-show-incremental-arguments.swift b/test/Driver/Dependencies/driver-show-incremental-arguments.swift index 7da72ed02d3e7..f963c5cb78c5e 100644 --- a/test/Driver/Dependencies/driver-show-incremental-arguments.swift +++ b/test/Driver/Dependencies/driver-show-incremental-arguments.swift @@ -10,15 +10,16 @@ // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/one-way/* %t +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s // CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled // CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-ARGS-MISMATCH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-ARGS-MISMATCH %s // CHECK-ARGS-MISMATCH: Incremental compilation has been disabled{{.*}}different arguments // CHECK-ARGS-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} + diff --git a/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments-fine.swift b/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments-fine.swift new file mode 100644 index 0000000000000..1efac0f62987d --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments-fine.swift @@ -0,0 +1,32 @@ +// REQUIRES: shell +// Test that when: +// +// 1. Using -incremental -v -driver-show-incremental, but... +// 2. ...options that disable incremental compilation, such as whole module +// optimization or bitcode embedding are specified... +// +// ...then the driver prints a message indicating that incremental compilation +// is disabled. If both are specified, the driver should only print one message. + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled +// CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO %s +// CHECK-WMO: Incremental compilation has been disabled{{.*}}whole module optimization +// CHECK-WMO-NOT: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-BITCODE %s +// CHECK-BITCODE: Incremental compilation has been disabled{{.*}}LLVM IR bitcode +// CHECK-BITCODE-NOT: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO-AND-BITCODE %s +// CHECK-WMO-AND-BITCODE: Incremental compilation has been disabled{{.*}}whole module optimization +// CHECK-WMO-AND-BITCODE-NOT: Incremental compilation has been disabled +// CHECK-WMO-AND-BITCODE-NOT: Queuing (initial): {compile: main.o <= main.swift} diff --git a/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments.swift b/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments.swift index 64ff0c36e81e9..91063e6bcd4a5 100644 --- a/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments.swift +++ b/test/Driver/Dependencies/driver-show-incremental-conflicting-arguments.swift @@ -10,24 +10,23 @@ // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/one-way/* %t +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s // CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled // CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO %s // CHECK-WMO: Incremental compilation has been disabled{{.*}}whole module optimization // CHECK-WMO-NOT: Queuing (initial): {compile: main.o <= main.swift} -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-BITCODE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-BITCODE %s // CHECK-BITCODE: Incremental compilation has been disabled{{.*}}LLVM IR bitcode // CHECK-BITCODE-NOT: Queuing (initial): {compile: main.o <= main.swift} -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO-AND-BITCODE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -whole-module-optimization -embed-bitcode -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-WMO-AND-BITCODE %s // CHECK-WMO-AND-BITCODE: Incremental compilation has been disabled{{.*}}whole module optimization // CHECK-WMO-AND-BITCODE-NOT: Incremental compilation has been disabled // CHECK-WMO-AND-BITCODE-NOT: Queuing (initial): {compile: main.o <= main.swift} - diff --git a/test/Driver/Dependencies/driver-show-incremental-inputs-fine.swift b/test/Driver/Dependencies/driver-show-incremental-inputs-fine.swift new file mode 100644 index 0000000000000..d3c91bfea77c9 --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-inputs-fine.swift @@ -0,0 +1,24 @@ +// REQUIRES: shell +// Test that when: +// +// 1. Using -incremental -v -driver-show-incremental, and... +// 2. ...the inputs passed to the Swift compiler version differ from the ones +// used in the original compilation... +// +// ...then the driver prints a message indicating that incremental compilation +// is disabled. + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps + +// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -enable-fine-grained-dependencies -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled +// CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -enable-fine-grained-dependencies -c ./main.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INPUTS-MISMATCH %s +// CHECK-INPUTS-MISMATCH: Incremental compilation has been disabled{{.*}}inputs +// CHECK-INPUTS-MISMATCH: ./other.swift +// CHECK-INPUTS-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} diff --git a/test/Driver/Dependencies/driver-show-incremental-inputs.swift b/test/Driver/Dependencies/driver-show-incremental-inputs.swift index 0bb1f509ee304..de4aee77dcd24 100644 --- a/test/Driver/Dependencies/driver-show-incremental-inputs.swift +++ b/test/Driver/Dependencies/driver-show-incremental-inputs.swift @@ -10,16 +10,15 @@ // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/one-way/* %t +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -disable-fine-grained-dependencies -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s // CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled // CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INPUTS-MISMATCH %s +// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -disable-fine-grained-dependencies -c ./main.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INPUTS-MISMATCH %s // CHECK-INPUTS-MISMATCH: Incremental compilation has been disabled{{.*}}inputs // CHECK-INPUTS-MISMATCH: ./other.swift // CHECK-INPUTS-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} - diff --git a/test/Driver/Dependencies/driver-show-incremental-malformed-fine.swift b/test/Driver/Dependencies/driver-show-incremental-malformed-fine.swift new file mode 100644 index 0000000000000..7477d79b0c190 --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-malformed-fine.swift @@ -0,0 +1,36 @@ +// REQUIRES: shell +// Test that when: +// +// 1. Using -incremental -v -driver-show-incremental, and... +// 2. ...the build record file does not contain valid JSON... +// +// ...then the driver prints a message indicating that incremental compilation +// is enabled. + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* + +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// CHECK-INCREMENTAL-NOT: Incremental compilation has been enabled +// CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: rm %t/main~buildrecord.swiftdeps && touch %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s + +// RUN: echo 'foo' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s + +// CHECK-MALFORMED: Incremental compilation has been disabled{{.*}}malformed build record file +// CHECK-MALFORMED-NOT: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: echo '{version, inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s + +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s + +// CHECK-MISSING-KEY: Incremental compilation has been disabled{{.*}}malformed build record file{{.*}}Malformed value for key +// CHECK-MISSING-KEY-NOT: Queuing (initial): {compile: main.o <= main.swift} diff --git a/test/Driver/Dependencies/driver-show-incremental-malformed.swift b/test/Driver/Dependencies/driver-show-incremental-malformed.swift index 9121ee709df62..083e97e8d9c68 100644 --- a/test/Driver/Dependencies/driver-show-incremental-malformed.swift +++ b/test/Driver/Dependencies/driver-show-incremental-malformed.swift @@ -9,29 +9,28 @@ // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/one-way/* %t +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s // CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled // CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} // RUN: rm %t/main~buildrecord.swiftdeps && touch %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s // RUN: echo 'foo' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MALFORMED %s // CHECK-MALFORMED: Incremental compilation has been disabled{{.*}}malformed build record file // CHECK-MALFORMED-NOT: Queuing (initial): {compile: main.o <= main.swift} // RUN: echo '{version, inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -g -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-MISSING-KEY %s // CHECK-MISSING-KEY: Incremental compilation has been disabled{{.*}}malformed build record file{{.*}}Malformed value for key // CHECK-MISSING-KEY-NOT: Queuing (initial): {compile: main.o <= main.swift} - diff --git a/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift b/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift new file mode 100644 index 0000000000000..1107dccd2e4c4 --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-mutual-fine.swift @@ -0,0 +1,19 @@ +/// main <==> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/mutual-with-swiftdeps-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Disabling incremental build: could not read build record + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// CHECK-SECOND-NOT: Queuing + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// CHECK-THIRD: Queuing (initial): {compile: other.o <= other.swift} +// CHECK-THIRD: Queuing because of the initial set: {compile: main.o <= main.swift} +// CHECK-THIRD-NEXT: interface of top-level name 'a' in other.swift -> interface of source file main.swiftdeps diff --git a/test/Driver/Dependencies/driver-show-incremental-mutual.swift b/test/Driver/Dependencies/driver-show-incremental-mutual.swift index 050212534f676..331a24286209a 100644 --- a/test/Driver/Dependencies/driver-show-incremental-mutual.swift +++ b/test/Driver/Dependencies/driver-show-incremental-mutual.swift @@ -1,18 +1,19 @@ /// main <==> other // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/mutual/* %t +// RUN: cp -r %S/Inputs/mutual-with-swiftdeps/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s -// CHECK-FIRST: Queuing (initial): {compile: main.o <= main.swift} +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Disabling incremental build: could not read build record -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Queuing // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v -driver-show-incremental 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: Queuing (initial): {compile: other.o <= other.swift} // CHECK-THIRD: Queuing because of the initial set: {compile: main.o <= main.swift} // CHECK-THIRD-NEXT: other.swift provides top-level name 'a' - diff --git a/test/Driver/Dependencies/driver-show-incremental-swift-version-fine.swift b/test/Driver/Dependencies/driver-show-incremental-swift-version-fine.swift new file mode 100644 index 0000000000000..6c1d692a6577d --- /dev/null +++ b/test/Driver/Dependencies/driver-show-incremental-swift-version-fine.swift @@ -0,0 +1,26 @@ +// REQUIRES: shell +// Test that when: +// +// 1. Using -incremental -v -driver-show-incremental, and... +// 2. ...the Swift compiler version used to perform the incremental +// compilation differs the original compilation... +// +// ...then the driver prints a message indicating that incremental compilation +// is enabled. + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps-fine/* %t +// RUN: %{python} %S/Inputs/touch.py 443865900 %t/* + +// RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// CHECK-INCREMENTAL-NOT: Incremental compilation has been enabled +// CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} + +// RUN: echo '{version: "bogus", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-VERSION-MISMATCH %s +// CHECK-VERSION-MISMATCH: Incremental compilation has been disabled{{.*}}compiler version mismatch +// CHECK-VERSION-MISMATCH: Compiling with: +// CHECK-VERSION-MISMATCH: Previously compiled with: bogus +// CHECK-VERSION-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} diff --git a/test/Driver/Dependencies/driver-show-incremental-swift-version.swift b/test/Driver/Dependencies/driver-show-incremental-swift-version.swift index c86ff68247849..b6f227998e3d3 100644 --- a/test/Driver/Dependencies/driver-show-incremental-swift-version.swift +++ b/test/Driver/Dependencies/driver-show-incremental-swift-version.swift @@ -10,18 +10,17 @@ // RUN: %empty-directory(%t) -// RUN: cp -r %S/Inputs/one-way/* %t +// RUN: cp -r %S/Inputs/one-way-with-swiftdeps/* %t // RUN: %{python} %S/Inputs/touch.py 443865900 %t/* // RUN: echo '{version: "'$(%swiftc_driver_plain -version | head -n1)'", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-INCREMENTAL %s // CHECK-INCREMENTAL-NOT: Incremental compilation has been disabled // CHECK-INCREMENTAL: Queuing (initial): {compile: main.o <= main.swift} // RUN: echo '{version: "bogus", inputs: {"./main.swift": [443865900, 0], "./other.swift": [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-VERSION-MISMATCH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -c ./main.swift ./other.swift -module-name main -incremental -v -driver-show-incremental -output-file-map %t/output.json | %FileCheck --check-prefix CHECK-VERSION-MISMATCH %s // CHECK-VERSION-MISMATCH: Incremental compilation has been disabled{{.*}}compiler version mismatch // CHECK-VERSION-MISMATCH: Compiling with: // CHECK-VERSION-MISMATCH: Previously compiled with: bogus // CHECK-VERSION-MISMATCH-NOT: Queuing (initial): {compile: main.o <= main.swift} - diff --git a/test/Driver/Dependencies/embed-bitcode-parallel-fine.swift b/test/Driver/Dependencies/embed-bitcode-parallel-fine.swift new file mode 100644 index 0000000000000..590919db0df7a --- /dev/null +++ b/test/Driver/Dependencies/embed-bitcode-parallel-fine.swift @@ -0,0 +1,91 @@ +// Windows doesn't support parallel execution yet +// XFAIL: windows +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/main.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled main.swift\n" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/other.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled other.swift\n" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "backend" +// CHECK-FIRST: ".\/main.o" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "backend" +// CHECK-FIRST: "output": "Produced main.o\n" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "backend" +// CHECK-FIRST: ".\/other.o" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "backend" +// CHECK-FIRST: "output": "Produced other.o\n" +// CHECK-FIRST: {{^}$}} + + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: "kind": "began" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/main.swift" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND-NOT: finished + +// CHECK-SECOND: "kind": "began" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/other.swift" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND-NOT: began + +// CHECK-SECOND: "kind": "finished" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: "output": "Handled {{other.swift|main.swift}}\n" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: "kind": "began" +// CHECK-SECOND: "name": "backend" +// CHECK-SECOND: ".\/{{other.o|main.o}}" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: "kind": "finished" +// CHECK-SECOND: "name": "backend" +// CHECK-SECOND: "output": "Produced {{other.o|main.o}}\n" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND-NOT: "skipped" diff --git a/test/Driver/Dependencies/embed-bitcode-parallel.swift b/test/Driver/Dependencies/embed-bitcode-parallel.swift index f1c08f2b9ead0..50f0b5effe366 100644 --- a/test/Driver/Dependencies/embed-bitcode-parallel.swift +++ b/test/Driver/Dependencies/embed-bitcode-parallel.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: {{^{$}} @@ -57,7 +57,7 @@ // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/fake-build-for-bitcode.py" -output-file-map %t/output.json -incremental ./main.swift ./other.swift -embed-bitcode -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: "kind": "began" // CHECK-SECOND: "name": "compile" diff --git a/test/Driver/Dependencies/fail-added-fine.swift b/test/Driver/Dependencies/fail-added-fine.swift new file mode 100644 index 0000000000000..c3a84fa443d41 --- /dev/null +++ b/test/Driver/Dependencies/fail-added-fine.swift @@ -0,0 +1,32 @@ +/// bad ==> main | bad --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled main.swift +// CHECK-INITIAL: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps + +// CHECK-ADDED-NOT: Handled +// CHECK-ADDED: Handled bad.swift +// CHECK-ADDED-NOT: Handled + +// CHECK-RECORD-ADDED-DAG: "./bad.swift": !private [ +// CHECK-RECORD-ADDED-DAG: "./main.swift": [ +// CHECK-RECORD-ADDED-DAG: "./other.swift": [ + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/fail-added.swift b/test/Driver/Dependencies/fail-added.swift index 9065f825ff5fd..af5252a4390f8 100644 --- a/test/Driver/Dependencies/fail-added.swift +++ b/test/Driver/Dependencies/fail-added.swift @@ -4,13 +4,13 @@ // RUN: cp -r %S/Inputs/fail-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL-NOT: warning // CHECK-INITIAL: Handled main.swift // CHECK-INITIAL: Handled other.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s // RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps // CHECK-ADDED-NOT: Handled @@ -26,7 +26,7 @@ // RUN: cp -r %S/Inputs/fail-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-ADDED %s // RUN: %FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/fail-chained-fine.swift b/test/Driver/Dependencies/fail-chained-fine.swift new file mode 100644 index 0000000000000..ed830aed2a15e --- /dev/null +++ b/test/Driver/Dependencies/fail-chained-fine.swift @@ -0,0 +1,129 @@ +/// a ==> bad ==> c ==> d | b --> bad --> e ==> f + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-chained-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled a.swift +// CHECK-FIRST: Handled b.swift +// CHECK-FIRST: Handled c.swift +// CHECK-FIRST: Handled d.swift +// CHECK-FIRST: Handled e.swift +// CHECK-FIRST: Handled f.swift +// CHECK-FIRST: Handled bad.swift + +// CHECK-RECORD-CLEAN-DAG: "./a.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./b.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./c.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./d.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./e.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./f.swift": [ +// CHECK-RECORD-CLEAN-DAG: "./bad.swift": [ + + +// RUN: touch -t 201401240006 %t/a.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./bad.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-A %s < %t/main~buildrecord.swiftdeps + +// CHECK-A: Handled a.swift +// CHECK-A: Handled bad.swift +// NEGATIVE-A-NOT: Handled b.swift +// NEGATIVE-A-NOT: Handled c.swift +// NEGATIVE-A-NOT: Handled d.swift +// NEGATIVE-A-NOT: Handled e.swift +// NEGATIVE-A-NOT: Handled f.swift + +// CHECK-RECORD-A-DAG: "./a.swift": [ +// CHECK-RECORD-A-DAG: "./b.swift": [ +// CHECK-RECORD-A-DAG: "./c.swift": !private [ +// CHECK-RECORD-A-DAG: "./d.swift": !private [ +// CHECK-RECORD-A-DAG: "./e.swift": !private [ +// CHECK-RECORD-A-DAG: "./f.swift": [ +// CHECK-RECORD-A-DAG: "./bad.swift": !private [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/a2.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A2 %s < %t/a2.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-A2 %s < %t/a2.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps + +// CHECK-A2-DAG: Handled c.swift +// CHECK-A2-DAG: Handled d.swift +// CHECK-A2-DAG: Handled e.swift +// CHECK-A2-DAG: Handled bad.swift +// NEGATIVE-A2-NOT: Handled a.swift +// NEGATIVE-A2-NOT: Handled b.swift +// NEGATIVE-A2-NOT: Handled f.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-chained-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/b.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-B %s < %t/b.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-B %s < %t/b.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-B %s < %t/main~buildrecord.swiftdeps + +// CHECK-B: Handled b.swift +// CHECK-B: Handled bad.swift +// NEGATIVE-B-NOT: Handled a.swift +// NEGATIVE-B-NOT: Handled c.swift +// NEGATIVE-B-NOT: Handled d.swift +// NEGATIVE-B-NOT: Handled e.swift +// NEGATIVE-B-NOT: Handled f.swift + +// CHECK-RECORD-B-DAG: "./a.swift": [ +// CHECK-RECORD-B-DAG: "./b.swift": [ +// CHECK-RECORD-B-DAG: "./c.swift": [ +// CHECK-RECORD-B-DAG: "./d.swift": [ +// CHECK-RECORD-B-DAG: "./e.swift": [ +// CHECK-RECORD-B-DAG: "./f.swift": [ +// CHECK-RECORD-B-DAG: "./bad.swift": !private [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b2.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-B2 %s < %t/b2.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-B2 %s < %t/b2.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps + +// CHECK-B2-DAG: Handled bad.swift +// NEGATIVE-B2-NOT: Handled a.swift +// NEGATIVE-B2-NOT: Handled b.swift +// NEGATIVE-B2-NOT: Handled c.swift +// NEGATIVE-B2-NOT: Handled d.swift +// NEGATIVE-B2-NOT: Handled e.swift +// NEGATIVE-B2-NOT: Handled f.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-chained-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/bad.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift -module-name main -j1 -v > %t/bad.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-BAD %s < %t/bad.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-BAD %s < %t/bad.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-A %s < %t/main~buildrecord.swiftdeps + +// CHECK-BAD: Handled bad.swift +// NEGATIVE-BAD-NOT: Handled a.swift +// NEGATIVE-BAD-NOT: Handled b.swift +// NEGATIVE-BAD-NOT: Handled c.swift +// NEGATIVE-BAD-NOT: Handled d.swift +// NEGATIVE-BAD-NOT: Handled e.swift +// NEGATIVE-BAD-NOT: Handled f.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/bad2.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A2 %s < %t/bad2.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-A2 %s < %t/bad2.txt +// RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/fail-chained.swift b/test/Driver/Dependencies/fail-chained.swift index 13f7415378374..7f0bb329e560e 100644 --- a/test/Driver/Dependencies/fail-chained.swift +++ b/test/Driver/Dependencies/fail-chained.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/fail-chained/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps // CHECK-FIRST-NOT: warning @@ -26,7 +26,7 @@ // RUN: touch -t 201401240006 %t/a.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./bad.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift -module-name main -j1 -v > %t/a.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt // RUN: %FileCheck -check-prefix=NEGATIVE-A %s < %t/a.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-A %s < %t/main~buildrecord.swiftdeps @@ -47,7 +47,7 @@ // CHECK-RECORD-A-DAG: "./f.swift": [ // CHECK-RECORD-A-DAG: "./bad.swift": !dirty [ -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/a2.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/a2.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-A2 %s < %t/a2.txt // RUN: %FileCheck -check-prefix=NEGATIVE-A2 %s < %t/a2.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps @@ -65,10 +65,10 @@ // RUN: cp -r %S/Inputs/fail-chained/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/b.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b.txt 2>&1 +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-B %s < %t/b.txt // RUN: %FileCheck -check-prefix=NEGATIVE-B %s < %t/b.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-B %s < %t/main~buildrecord.swiftdeps @@ -89,7 +89,7 @@ // CHECK-RECORD-B-DAG: "./f.swift": [ // CHECK-RECORD-B-DAG: "./bad.swift": !private [ -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b2.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/b2.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-B2 %s < %t/b2.txt // RUN: %FileCheck -check-prefix=NEGATIVE-B2 %s < %t/b2.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps @@ -107,10 +107,10 @@ // RUN: cp -r %S/Inputs/fail-chained/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/bad.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/bad.txt 2>&1 +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift -module-name main -j1 -v > %t/bad.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-BAD %s < %t/bad.txt // RUN: %FileCheck -check-prefix=NEGATIVE-BAD %s < %t/bad.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-A %s < %t/main~buildrecord.swiftdeps @@ -123,7 +123,7 @@ // NEGATIVE-BAD-NOT: Handled e.swift // NEGATIVE-BAD-NOT: Handled f.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/bad2.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./bad.swift -module-name main -j1 -v > %t/bad2.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-A2 %s < %t/bad2.txt // RUN: %FileCheck -check-prefix=NEGATIVE-A2 %s < %t/bad2.txt // RUN: %FileCheck -check-prefix=CHECK-RECORD-CLEAN %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/fail-interface-hash-fine.swift b/test/Driver/Dependencies/fail-interface-hash-fine.swift new file mode 100644 index 0000000000000..3ed85c673d6e1 --- /dev/null +++ b/test/Driver/Dependencies/fail-interface-hash-fine.swift @@ -0,0 +1,38 @@ +/// main ==> depends-on-main | bad ==> depends-on-bad + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-interface-hash-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled bad.swift +// CHECK-FIRST: Handled depends-on-main.swift +// CHECK-FIRST: Handled depends-on-bad.swift + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-interface-hash-fine/*.swiftdeps %t + +// RUN: touch -t 201401240006 %t/bad.swift %t/main.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-SECOND: Handled main.swift +// CHECK-SECOND-NOT: Handled depends +// CHECK-SECOND: Handled bad.swift +// CHECK-SECOND-NOT: Handled depends + +// CHECK-RECORD-DAG: "./bad.swift": !private [ +// CHECK-RECORD-DAG: "./main.swift": [ +// CHECK-RECORD-DAG: "./depends-on-main.swift": !private [ +// CHECK-RECORD-DAG: "./depends-on-bad.swift": [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled bad +// CHECK-THIRD-DAG: Handled depends-on-bad +// CHECK-THIRD-DAG: Handled depends-on-main +// CHECK-THIRD-DAG: Handled main + diff --git a/test/Driver/Dependencies/fail-interface-hash.swift b/test/Driver/Dependencies/fail-interface-hash.swift index 11a5004b5ace1..c769520c711a2 100644 --- a/test/Driver/Dependencies/fail-interface-hash.swift +++ b/test/Driver/Dependencies/fail-interface-hash.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/fail-interface-hash/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift @@ -16,7 +16,7 @@ // RUN: cp -r %S/Inputs/fail-interface-hash/*.swiftdeps %t // RUN: touch -t 201401240006 %t/bad.swift %t/main.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps // CHECK-SECOND: Handled main.swift diff --git a/test/Driver/Dependencies/fail-new-fine.swift b/test/Driver/Dependencies/fail-new-fine.swift new file mode 100644 index 0000000000000..f0505588507a4 --- /dev/null +++ b/test/Driver/Dependencies/fail-new-fine.swift @@ -0,0 +1,45 @@ +/// bad ==> main | bad --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// CHECK-NOT: warning +// CHECK: Handled main.swift +// CHECK: Handled bad.swift +// CHECK-NOT: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s + +// CHECK-BAD-ONLY-NOT: warning +// CHECK-BAD-ONLY-NOT: Handled +// CHECK-BAD-ONLY: Handled bad.swift +// CHECK-BAD-ONLY-NOT: Handled + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// CHECK-OKAY: Handled main.swift +// CHECK-OKAY: Handled bad.swift +// CHECK-OKAY: Handled other.swift +// CHECK-OKAY-NOT: Handled + +// RUN: touch -t 201401240006 %t/bad.swift +// RUN: rm %t/bad.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s + +// CHECK-OKAY-2-DAG: Handled bad.swift +// CHECK-OKAY-2-DAG: Handled other.swift +// CHECK-OKAY-2-DAG: Handled main.swift + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: rm %t/main.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// RUN: touch -t 201401240006 %t/other.swift +// RUN: rm %t/other.swiftdeps +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s diff --git a/test/Driver/Dependencies/fail-new.swift b/test/Driver/Dependencies/fail-new.swift index a0e7cc3850d6f..39be90afae39f 100644 --- a/test/Driver/Dependencies/fail-new.swift +++ b/test/Driver/Dependencies/fail-new.swift @@ -4,20 +4,20 @@ // RUN: cp -r %S/Inputs/fail-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s // CHECK-NOT: warning // CHECK: Handled main.swift // CHECK: Handled bad.swift // CHECK-NOT: Handled other.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BAD-ONLY %s // CHECK-BAD-ONLY-NOT: warning // CHECK-BAD-ONLY-NOT: Handled // CHECK-BAD-ONLY: Handled bad.swift // CHECK-BAD-ONLY-NOT: Handled -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s // CHECK-OKAY: Handled main.swift // CHECK-OKAY: Handled bad.swift // CHECK-OKAY: Handled other.swift @@ -25,21 +25,21 @@ // RUN: touch -t 201401240006 %t/bad.swift // RUN: rm %t/bad.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY-2 %s -// CHECK-OKAY-2: Handled bad.swift -// CHECK-OKAY-2: Handled other.swift -// CHECK-OKAY-2: Handled main.swift +// CHECK-OKAY-2-DAG: Handled bad.swift +// CHECK-OKAY-2-DAG: Handled other.swift +// CHECK-OKAY-2-DAG: Handled main.swift // RUN: touch -t 201401240006 %t/main.swift // RUN: rm %t/main.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-OKAY %s // RUN: touch -t 201401240006 %t/other.swift // RUN: rm %t/other.swiftdeps -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s diff --git a/test/Driver/Dependencies/fail-simple-fine.swift b/test/Driver/Dependencies/fail-simple-fine.swift new file mode 100644 index 0000000000000..bf577bf60de84 --- /dev/null +++ b/test/Driver/Dependencies/fail-simple-fine.swift @@ -0,0 +1,30 @@ +/// bad ==> main | bad --> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-simple-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled bad.swift +// CHECK-FIRST: Handled other.swift + +// RUN: touch -t 201401240006 %t/bad.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-SECOND: Handled bad.swift +// CHECK-SECOND-NOT: Handled main.swift +// CHECK-SECOND-NOT: Handled other.swift + +// CHECK-RECORD-DAG: "./bad.swift": !private [ +// CHECK-RECORD-DAG: "./main.swift": !private [ +// CHECK-RECORD-DAG: "./other.swift": !private [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled main.swift +// CHECK-THIRD-DAG: Handled bad.swift +// CHECK-THIRD-DAG: Handled other.swift diff --git a/test/Driver/Dependencies/fail-simple.swift b/test/Driver/Dependencies/fail-simple.swift index 8ed9daffa6310..7d244861255fa 100644 --- a/test/Driver/Dependencies/fail-simple.swift +++ b/test/Driver/Dependencies/fail-simple.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/fail-simple/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift @@ -12,7 +12,7 @@ // CHECK-FIRST: Handled other.swift // RUN: touch -t 201401240006 %t/bad.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps // CHECK-SECOND: Handled bad.swift diff --git a/test/Driver/Dependencies/fail-with-bad-deps-fine.swift b/test/Driver/Dependencies/fail-with-bad-deps-fine.swift new file mode 100644 index 0000000000000..066214d3c5158 --- /dev/null +++ b/test/Driver/Dependencies/fail-with-bad-deps-fine.swift @@ -0,0 +1,50 @@ +/// main ==> depends-on-main | bad ==> depends-on-bad + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/fail-with-bad-deps-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled bad.swift +// CHECK-FIRST: Handled depends-on-main.swift +// CHECK-FIRST: Handled depends-on-bad.swift + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NONE %s +// CHECK-NONE-NOT: Handled + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps-fine/*.swiftdeps %t + +// RUN: touch -t 201401240006 %t/bad.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BUILD-ALL %s + +// CHECK-BUILD-ALL-NOT: warning +// CHECK-BUILD-ALL: Handled bad.swift +// CHECK-BUILD-ALL-DAG: Handled main.swift +// CHECK-BUILD-ALL-DAG: Handled depends-on-main.swift +// CHECK-BUILD-ALL-DAG: Handled depends-on-bad.swift + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps-fine/*.swiftdeps %t + +// RUN: touch -t 201401240007 %t/bad.swift %t/main.swift +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-WITH-FAIL %s +// RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-WITH-FAIL: Handled main.swift +// CHECK-WITH-FAIL-NOT: Handled depends +// CHECK-WITH-FAIL: Handled bad.swift +// CHECK-WITH-FAIL-NOT: Handled depends + +// CHECK-RECORD-DAG: "./bad.swift": !private [ +// CHECK-RECORD-DAG: "./main.swift": [ +// CHECK-RECORD-DAG: "./depends-on-main.swift": !private [ +// CHECK-RECORD-DAG: "./depends-on-bad.swift": [ + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./bad.swift ./main.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BUILD-ALL %s diff --git a/test/Driver/Dependencies/fail-with-bad-deps.swift b/test/Driver/Dependencies/fail-with-bad-deps.swift index 7e5e4c93337e1..ad51ad1212d06 100644 --- a/test/Driver/Dependencies/fail-with-bad-deps.swift +++ b/test/Driver/Dependencies/fail-with-bad-deps.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/fail-with-bad-deps/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift @@ -15,14 +15,14 @@ // Reset the .swiftdeps files. // RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NONE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NONE %s // CHECK-NONE-NOT: Handled // Reset the .swiftdeps files. // RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t // RUN: touch -t 201401240006 %t/bad.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BUILD-ALL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-BUILD-ALL %s // CHECK-BUILD-ALL-NOT: warning // CHECK-BUILD-ALL: Handled bad.swift @@ -34,7 +34,7 @@ // RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t // RUN: touch -t 201401240007 %t/bad.swift %t/main.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-WITH-FAIL %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies-bad.py" -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-WITH-FAIL %s // RUN: %FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps // CHECK-WITH-FAIL: Handled main.swift diff --git a/test/Driver/Dependencies/file-added-fine.swift b/test/Driver/Dependencies/file-added-fine.swift new file mode 100644 index 0000000000000..05dcc408ace0a --- /dev/null +++ b/test/Driver/Dependencies/file-added-fine.swift @@ -0,0 +1,20 @@ +/// other ==> main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-NOT: Handled other.swift +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/file-added.swift b/test/Driver/Dependencies/file-added.swift index 6016edacf8b88..66d359cc5b9ae 100644 --- a/test/Driver/Dependencies/file-added.swift +++ b/test/Driver/Dependencies/file-added.swift @@ -4,16 +4,16 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-NOT: Handled other.swift // CHECK-THIRD: Handled main.swift diff --git a/test/Driver/Dependencies/independent-fine.swift b/test/Driver/Dependencies/independent-fine.swift new file mode 100644 index 0000000000000..280ec5fa03b54 --- /dev/null +++ b/test/Driver/Dependencies/independent-fine.swift @@ -0,0 +1,46 @@ +// main | other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: ls %t/main~buildrecord.swiftdeps + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s + +// CHECK-FIRST-MULTI: Handled main.swift +// CHECK-FIRST-MULTI: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// RUN: touch -t 201401240006 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SINGLE %s +// CHECK-SINGLE: Handled main.swift + +// RUN: ls %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/independent-half-dirty-fine.swift b/test/Driver/Dependencies/independent-half-dirty-fine.swift new file mode 100644 index 0000000000000..6c9ea917e1259 --- /dev/null +++ b/test/Driver/Dependencies/independent-half-dirty-fine.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift + +// RUN: touch -t 201401240005 %t/other.o +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled main.swift +// CHECK-SECOND: Handled other.swift +// CHECK-SECOND-NOT: Handled main.swift + +// RUN: rm %t/other.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift diff --git a/test/Driver/Dependencies/independent-half-dirty.swift b/test/Driver/Dependencies/independent-half-dirty.swift index 2d5fbf8e41d36..615a2f18c83c6 100644 --- a/test/Driver/Dependencies/independent-half-dirty.swift +++ b/test/Driver/Dependencies/independent-half-dirty.swift @@ -2,7 +2,7 @@ // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift @@ -10,14 +10,14 @@ // RUN: touch -t 201401240005 %t/other.o // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled main.swift // CHECK-SECOND: Handled other.swift // CHECK-SECOND-NOT: Handled main.swift // RUN: rm %t/other.swiftdeps -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: Handled main.swift // CHECK-THIRD: Handled other.swift diff --git a/test/Driver/Dependencies/independent-parseable-fine.swift b/test/Driver/Dependencies/independent-parseable-fine.swift new file mode 100644 index 0000000000000..10346870914aa --- /dev/null +++ b/test/Driver/Dependencies/independent-parseable-fine.swift @@ -0,0 +1,78 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/main.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-FIRST: {{^}$}} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "skipped" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/main.swift" +// CHECK-SECOND: {{^}$}} + +// RUN: touch -t 201401240006 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/independent-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s + +// CHECK-FIRST-MULTI: {{^{$}} +// CHECK-FIRST-MULTI: "kind": "began" +// CHECK-FIRST-MULTI: "name": "compile" +// CHECK-FIRST-MULTI: ".\/main.swift" +// CHECK-FIRST-MULTI: {{^}$}} + +// CHECK-FIRST-MULTI: {{^{$}} +// CHECK-FIRST-MULTI: "kind": "finished" +// CHECK-FIRST-MULTI: "name": "compile" +// CHECK-FIRST-MULTI: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-FIRST-MULTI: {{^}$}} + +// CHECK-FIRST-MULTI: {{^{$}} +// CHECK-FIRST-MULTI: "kind": "began" +// CHECK-FIRST-MULTI: "name": "compile" +// CHECK-FIRST-MULTI: ".\/other.swift" +// CHECK-FIRST-MULTI: {{^}$}} + +// CHECK-FIRST-MULTI: {{^{$}} +// CHECK-FIRST-MULTI: "kind": "finished" +// CHECK-FIRST-MULTI: "name": "compile" +// CHECK-FIRST-MULTI: "output": "Handled other.swift{{(\\r)?}}\n" +// CHECK-FIRST-MULTI: {{^}$}} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND-MULTI %s + +// CHECK-SECOND-MULTI: {{^{$}} +// CHECK-SECOND-MULTI: "kind": "skipped" +// CHECK-SECOND-MULTI: "name": "compile" +// CHECK-SECOND-MULTI: ".\/main.swift" +// CHECK-SECOND-MULTI: {{^}$}} + +// CHECK-SECOND-MULTI: {{^{$}} +// CHECK-SECOND-MULTI: "kind": "skipped" +// CHECK-SECOND-MULTI: "name": "compile" +// CHECK-SECOND-MULTI: ".\/other.swift" +// CHECK-SECOND-MULTI: {{^}$}} + +// RUN: touch -t 201401240006 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s + diff --git a/test/Driver/Dependencies/independent-parseable.swift b/test/Driver/Dependencies/independent-parseable.swift index e30f8281dfb44..c90bddbbeffc1 100644 --- a/test/Driver/Dependencies/independent-parseable.swift +++ b/test/Driver/Dependencies/independent-parseable.swift @@ -2,7 +2,7 @@ // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: {{^{$}} @@ -17,7 +17,7 @@ // CHECK-FIRST: "output": "Handled main.swift{{(\\r)?}}\n" // CHECK-FIRST: {{^}$}} -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: {{^{$}} // CHECK-SECOND: "kind": "skipped" @@ -26,14 +26,14 @@ // CHECK-SECOND: {{^}$}} // RUN: touch -t 201401240006 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s // CHECK-FIRST-MULTI: {{^{$}} // CHECK-FIRST-MULTI: "kind": "began" @@ -59,7 +59,7 @@ // CHECK-FIRST-MULTI: "output": "Handled other.swift{{(\\r)?}}\n" // CHECK-FIRST-MULTI: {{^}$}} -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND-MULTI %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND-MULTI %s // CHECK-SECOND-MULTI: {{^{$}} // CHECK-SECOND-MULTI: "kind": "skipped" @@ -74,5 +74,5 @@ // CHECK-SECOND-MULTI: {{^}$}} // RUN: touch -t 201401240006 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s diff --git a/test/Driver/Dependencies/independent.swift b/test/Driver/Dependencies/independent.swift index a39a8c019f145..b202c69171f46 100644 --- a/test/Driver/Dependencies/independent.swift +++ b/test/Driver/Dependencies/independent.swift @@ -4,43 +4,43 @@ // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: ls %t/main~buildrecord.swiftdeps // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s // CHECK-FIRST-MULTI: Handled main.swift // CHECK-FIRST-MULTI: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // RUN: touch -t 201401240006 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST-MULTI %s // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/independent/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SINGLE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SINGLE %s // CHECK-SINGLE: Handled main.swift // RUN: ls %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/malformed-but-valid-yaml-fine.swift b/test/Driver/Dependencies/malformed-but-valid-yaml-fine.swift new file mode 100644 index 0000000000000..4928975ba79fc --- /dev/null +++ b/test/Driver/Dependencies/malformed-but-valid-yaml-fine.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/malformed-but-valid-yaml-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/malformed-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: Handled other.swift +// CHECK-SECOND: Handled main.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/malformed-but-valid-yaml-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/malformed-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/malformed-but-valid-yaml.swift b/test/Driver/Dependencies/malformed-but-valid-yaml.swift index 0679f79d01466..1f7ef58b6518a 100644 --- a/test/Driver/Dependencies/malformed-but-valid-yaml.swift +++ b/test/Driver/Dependencies/malformed-but-valid-yaml.swift @@ -3,24 +3,24 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/malformed-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: Handled other.swift // CHECK-SECOND: Handled main.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: Handled main.swift // CHECK-THIRD: Handled other.swift @@ -30,18 +30,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/malformed-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift diff --git a/test/Driver/Dependencies/malformed-fine.swift b/test/Driver/Dependencies/malformed-fine.swift new file mode 100644 index 0000000000000..266d859e8ab9d --- /dev/null +++ b/test/Driver/Dependencies/malformed-fine.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/malformed-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/malformed-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: Handled other.swift +// CHECK-SECOND: Handled main.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/malformed-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/malformed-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/malformed.swift b/test/Driver/Dependencies/malformed.swift index bc42b472b8436..bc40ab1659536 100644 --- a/test/Driver/Dependencies/malformed.swift +++ b/test/Driver/Dependencies/malformed.swift @@ -3,24 +3,24 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/malformed-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: Handled other.swift // CHECK-SECOND: Handled main.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: Handled main.swift // CHECK-THIRD: Handled other.swift @@ -30,18 +30,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/malformed-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift diff --git a/test/Driver/Dependencies/mutual-fine.swift b/test/Driver/Dependencies/mutual-fine.swift new file mode 100644 index 0000000000000..d712c7a5fab50 --- /dev/null +++ b/test/Driver/Dependencies/mutual-fine.swift @@ -0,0 +1,24 @@ +/// main <==> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/mutual-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s diff --git a/test/Driver/Dependencies/mutual-interface-hash-fine.swift b/test/Driver/Dependencies/mutual-interface-hash-fine.swift new file mode 100644 index 0000000000000..d1657666d5faa --- /dev/null +++ b/test/Driver/Dependencies/mutual-interface-hash-fine.swift @@ -0,0 +1,50 @@ +/// does-change <==> does-not-change + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/mutual-interface-hash-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/mutual-interface-hash-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s + +// CHECK-CLEAN-NOT: Handled + +// RUN: touch -t 201401240006 %t/does-change.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CHANGE %s + +// CHECK-CHANGE-DAG: Handled does-change.swift +// CHECK-CHANGE-DAG: Handled does-not-change.swift + + +// RUN: cp -r %S/Inputs/mutual-interface-hash-fine/*.swiftdeps %t + +// RUN: touch -t 201401240006 %t/does-not-change.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NO-CHANGE %s + +// CHECK-NO-CHANGE-NOT: Handled +// CHECK-NO-CHANGE: Handled does-not-change.swift +// CHECK-NO-CHANGE-NOT: Handled + + +// RUN: cp -r %S/Inputs/mutual-interface-hash-fine/*.swiftdeps %t + +// Make sure the files really were dependent on one another. + +// RUN: touch -t 201401240007 %t/does-not-change.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s + +// CHECK-REBUILD-DEPENDENTS-DAG: Handled does-not-change.swift +// CHECK-REBUILD-DEPENDENTS-DAG: Handled does-change.swift + + +// Check that cascading builds triggered by the build record are still +// considered cascading. + +// RUN: cp -r %S/Inputs/mutual-interface-hash-fine/*.swiftdeps %t +// RUN: sed -E -e 's/"[^"]*does-not-change.swift":/& !dirty/' -i.prev %t/main~buildrecord.swiftdeps +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s diff --git a/test/Driver/Dependencies/mutual-interface-hash.swift b/test/Driver/Dependencies/mutual-interface-hash.swift index 8a1afc0bb413d..906a67910f3aa 100644 --- a/test/Driver/Dependencies/mutual-interface-hash.swift +++ b/test/Driver/Dependencies/mutual-interface-hash.swift @@ -5,17 +5,17 @@ // RUN: touch -t 201401240005 %t/* // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/mutual-interface-hash/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s // CHECK-CLEAN-NOT: Handled // RUN: touch -t 201401240006 %t/does-change.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CHANGE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CHANGE %s // CHECK-CHANGE: Handled does-change.swift // CHECK-CHANGE: Handled does-not-change.swift @@ -24,7 +24,7 @@ // RUN: cp -r %S/Inputs/mutual-interface-hash/*.swiftdeps %t // RUN: touch -t 201401240006 %t/does-not-change.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NO-CHANGE %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-NO-CHANGE %s // CHECK-NO-CHANGE-NOT: Handled // CHECK-NO-CHANGE: Handled does-not-change.swift @@ -34,15 +34,17 @@ // RUN: cp -r %S/Inputs/mutual-interface-hash/*.swiftdeps %t // Make sure the files really were dependent on one another. + // RUN: touch -t 201401240007 %t/does-not-change.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s -// CHECK-REBUILD-DEPENDENTS: Handled does-not-change.swift -// CHECK-REBUILD-DEPENDENTS: Handled does-change.swift +// CHECK-REBUILD-DEPENDENTS-DAG: Handled does-not-change.swift +// CHECK-REBUILD-DEPENDENTS-DAG: Handled does-change.swift // Check that cascading builds triggered by the build record are still // considered cascading. + // RUN: cp -r %S/Inputs/mutual-interface-hash/*.swiftdeps %t // RUN: sed -E -e 's/"[^"]*does-not-change.swift":/& !dirty/' -i.prev %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental ./does-change.swift ./does-not-change.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD-DEPENDENTS %s diff --git a/test/Driver/Dependencies/mutual.swift b/test/Driver/Dependencies/mutual.swift index 63220a64eda1a..7b6c22db1e277 100644 --- a/test/Driver/Dependencies/mutual.swift +++ b/test/Driver/Dependencies/mutual.swift @@ -4,21 +4,21 @@ // RUN: cp -r %S/Inputs/mutual/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// CHECK-THIRD: Handled other.swift // CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s diff --git a/test/Driver/Dependencies/nominal-members-fine.swift b/test/Driver/Dependencies/nominal-members-fine.swift new file mode 100644 index 0000000000000..cb2bb4376cbd9 --- /dev/null +++ b/test/Driver/Dependencies/nominal-members-fine.swift @@ -0,0 +1,39 @@ +/// a ==> depends-on-a-ext, depends-on-a-foo | a-ext ==> depends-on-a-ext + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/nominal-members-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled a.swift +// CHECK-INITIAL: Handled a-ext.swift +// CHECK-INITIAL: Handled depends-on-a-foo.swift +// CHECK-INITIAL: Handled depends-on-a-ext.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s + +// CHECK-CLEAN-NOT: Handled + +// RUN: touch -t 201401240006 %t/a.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v > %t/touched-a.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-TOUCHED-A %s < %t/touched-a.txt +// RUN: %FileCheck -check-prefix=NEGATIVE-TOUCHED-A %s < %t/touched-a.txt + +// CHECK-TOUCHED-A: Handled a.swift +// CHECK-TOUCHED-A-DAG: Handled depends-on-a-foo.swift +// CHECK-TOUCHED-A-DAG: Handled depends-on-a-ext.swift +// NEGATIVE-TOUCHED-A-NOT: Handled a-ext.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s + + +// RUN: touch -t 201401240007 %t/a-ext.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-TOUCHED-EXT %s + +// CHECK-TOUCHED-EXT-NOT: Handled +// CHECK-TOUCHED-EXT: Handled a-ext.swift +// CHECK-TOUCHED-EXT-NOT: Handled +// CHECK-TOUCHED-EXT: Handled depends-on-a-ext.swift +// CHECK-TOUCHED-EXT-NOT: Handled diff --git a/test/Driver/Dependencies/nominal-members.swift b/test/Driver/Dependencies/nominal-members.swift index 60e0af14aacca..59af3725f5c3b 100644 --- a/test/Driver/Dependencies/nominal-members.swift +++ b/test/Driver/Dependencies/nominal-members.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/nominal-members/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL-NOT: warning // CHECK-INITIAL: Handled a.swift @@ -12,12 +12,12 @@ // CHECK-INITIAL: Handled depends-on-a-foo.swift // CHECK-INITIAL: Handled depends-on-a-ext.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s // CHECK-CLEAN-NOT: Handled // RUN: touch -t 201401240006 %t/a.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v > %t/touched-a.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v > %t/touched-a.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-TOUCHED-A %s < %t/touched-a.txt // RUN: %FileCheck -check-prefix=NEGATIVE-TOUCHED-A %s < %t/touched-a.txt @@ -26,11 +26,11 @@ // CHECK-TOUCHED-A-DAG: Handled depends-on-a-ext.swift // NEGATIVE-TOUCHED-A-NOT: Handled a-ext.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-CLEAN %s // RUN: touch -t 201401240007 %t/a-ext.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-TOUCHED-EXT %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./a-ext.swift ./depends-on-a-foo.swift ./depends-on-a-ext.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-TOUCHED-EXT %s // CHECK-TOUCHED-EXT-NOT: Handled // CHECK-TOUCHED-EXT: Handled a-ext.swift diff --git a/test/Driver/Dependencies/one-way-depends-after-fine.swift b/test/Driver/Dependencies/one-way-depends-after-fine.swift new file mode 100644 index 0000000000000..a7a978fdfbe69 --- /dev/null +++ b/test/Driver/Dependencies/one-way-depends-after-fine.swift @@ -0,0 +1,56 @@ +/// other | main +/// other ==>+ main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-depends-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-depends-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled main.swift +// CHECK-SECOND: Handled other.swift +// CHECK-SECOND-NOT: Handled main.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-depends-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-depends-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-NOT: Handled other.swift +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD-NOT: Handled other.swift + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// RUN: touch -t 201401240008 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-DAG: Handled other.swift +// CHECK-FOURTH-DAG: Handled main.swift diff --git a/test/Driver/Dependencies/one-way-depends-after.swift b/test/Driver/Dependencies/one-way-depends-after.swift index 9c9e5265ee777..a0690129a4bcd 100644 --- a/test/Driver/Dependencies/one-way-depends-after.swift +++ b/test/Driver/Dependencies/one-way-depends-after.swift @@ -6,25 +6,25 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-depends-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled main.swift // CHECK-SECOND: Handled other.swift // CHECK-SECOND-NOT: Handled main.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // RUN: %empty-directory(%t) @@ -32,25 +32,25 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-depends-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-NOT: Handled other.swift // CHECK-THIRD: Handled main.swift // CHECK-THIRD-NOT: Handled other.swift // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240008 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s -// CHECK-FOURTH: Handled other.swift -// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-DAG: Handled other.swift +// CHECK-FOURTH-DAG: Handled main.swift diff --git a/test/Driver/Dependencies/one-way-depends-before-fine.swift b/test/Driver/Dependencies/one-way-depends-before-fine.swift new file mode 100644 index 0000000000000..af6d95d7fa59a --- /dev/null +++ b/test/Driver/Dependencies/one-way-depends-before-fine.swift @@ -0,0 +1,56 @@ +/// other ==>+ main +/// other | main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-depends-before-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-depends-before-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: Handled main.swift +// CHECK-SECOND: Handled other.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-NOT: Handled main.swift +// CHECK-THIRD: Handled other.swift +// CHECK-THIRD-NOT: Handled main.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-depends-before-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-depends-before-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// RUN: touch -t 201401240008 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/one-way-depends-before.swift b/test/Driver/Dependencies/one-way-depends-before.swift index 90258dbd33c49..6a957b9025f4e 100644 --- a/test/Driver/Dependencies/one-way-depends-before.swift +++ b/test/Driver/Dependencies/one-way-depends-before.swift @@ -6,24 +6,24 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-depends-before/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift // CHECK-SECOND: Handled main.swift +// CHECK-SECOND: Handled other.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-NOT: Handled main.swift // CHECK-THIRD: Handled other.swift @@ -35,22 +35,22 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-depends-before/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift // CHECK-FOURTH-NOT: Handled other.swift // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: touch -t 201401240008 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s diff --git a/test/Driver/Dependencies/one-way-external-delete-fine.swift b/test/Driver/Dependencies/one-way-external-delete-fine.swift new file mode 100644 index 0000000000000..f4c97346a6cba --- /dev/null +++ b/test/Driver/Dependencies/one-way-external-delete-fine.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-external-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: rm %t/other1-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled other.swift +// CHECK-THIRD-DAG: Handled main.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-external-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: rm %t/main1-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/one-way-external-delete.swift b/test/Driver/Dependencies/one-way-external-delete.swift index 1cc724ae07771..2c058e58da8cd 100644 --- a/test/Driver/Dependencies/one-way-external-delete.swift +++ b/test/Driver/Dependencies/one-way-external-delete.swift @@ -2,13 +2,13 @@ // RUN: cp -r %S/Inputs/one-way-external/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled @@ -17,7 +17,7 @@ // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: rm %t/other1-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-DAG: Handled other.swift // CHECK-THIRD-DAG: Handled main.swift @@ -27,14 +27,14 @@ // RUN: cp -r %S/Inputs/one-way-external/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240005 %t/* // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: rm %t/main1-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift diff --git a/test/Driver/Dependencies/one-way-external-fine.swift b/test/Driver/Dependencies/one-way-external-fine.swift new file mode 100644 index 0000000000000..3c6128a1cef26 --- /dev/null +++ b/test/Driver/Dependencies/one-way-external-fine.swift @@ -0,0 +1,52 @@ +/// other ==> main +/// "./main1-external" ==> main +/// "./main2-external" ==> main +/// "./other1-external" ==> other +/// "./other2-external" ==> other + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-external-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: touch -t 203704010005 %t/other1-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-DAG: Handled other.swift +// CHECK-THIRD-DAG: Handled main.swift + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: touch -t 203704010005 %t/other2-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: touch -t 203704010005 %t/main1-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift + +// RUN: touch -t 201401240005 %t/* +// RUN: touch -t 201401240006 %t/*.o +// RUN: touch -t 201401240004 %t/*-external +// RUN: touch -t 203704010005 %t/main2-external +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s diff --git a/test/Driver/Dependencies/one-way-external.swift b/test/Driver/Dependencies/one-way-external.swift index bdf937d47efa5..94dcdd266164b 100644 --- a/test/Driver/Dependencies/one-way-external.swift +++ b/test/Driver/Dependencies/one-way-external.swift @@ -8,13 +8,13 @@ // RUN: cp -r %S/Inputs/one-way-external/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled @@ -23,7 +23,7 @@ // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/other1-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-DAG: Handled other.swift // CHECK-THIRD-DAG: Handled main.swift @@ -32,14 +32,14 @@ // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/other2-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240005 %t/* // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/main1-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift @@ -49,4 +49,4 @@ // RUN: touch -t 201401240006 %t/*.o // RUN: touch -t 201401240004 %t/*-external // RUN: touch -t 203704010005 %t/main2-external -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s diff --git a/test/Driver/Dependencies/one-way-fine.swift b/test/Driver/Dependencies/one-way-fine.swift new file mode 100644 index 0000000000000..5a930d7779a10 --- /dev/null +++ b/test/Driver/Dependencies/one-way-fine.swift @@ -0,0 +1,75 @@ +/// other ==> main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift + +// RUN: rm %t/main.o +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// RUN: rm %t/other.o +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIFTH %s + +// CHECK-FIFTH-NOT: Handled main.swift +// CHECK-FIFTH: Handled other.swift +// CHECK-FIFTH-NOT: Handled main.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// Try modifying the inputs /backwards/ in time rather than forwards. +// RUN: touch -t 201401240004 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// RUN: touch -t 201401240004 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s + +// CHECK-REV-FIRST: Handled other.swift +// CHECK-REV-FIRST: Handled main.swift + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-SECOND %s + +// CHECK-REV-SECOND-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FOURTH %s + +// CHECK-REV-FOURTH-NOT: Handled other.swift +// CHECK-REV-FOURTH: Handled main.swift +// CHECK-REV-FOURTH-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/one-way-merge-module-fine.swift b/test/Driver/Dependencies/one-way-merge-module-fine.swift new file mode 100644 index 0000000000000..4a740e01f5b79 --- /dev/null +++ b/test/Driver/Dependencies/one-way-merge-module-fine.swift @@ -0,0 +1,18 @@ +/// other ==> main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled other.swift +// CHECK-FIRST: Produced master.swiftmodule + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-NOT: warning +// CHECK-SECOND-NOT: Handled +// CHECK-SECOND: Produced master.swiftmodule diff --git a/test/Driver/Dependencies/one-way-merge-module.swift b/test/Driver/Dependencies/one-way-merge-module.swift index ce9d8a56f556c..b913484f2a368 100644 --- a/test/Driver/Dependencies/one-way-merge-module.swift +++ b/test/Driver/Dependencies/one-way-merge-module.swift @@ -4,14 +4,14 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift // CHECK-FIRST: Produced master.swiftmodule -// RUN: cd %t && %swiftc_driver -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -emit-module-path %t/master.swiftmodule -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: warning // CHECK-SECOND-NOT: Handled diff --git a/test/Driver/Dependencies/one-way-parallel-fine.swift b/test/Driver/Dependencies/one-way-parallel-fine.swift new file mode 100644 index 0000000000000..a205b9ec38a7b --- /dev/null +++ b/test/Driver/Dependencies/one-way-parallel-fine.swift @@ -0,0 +1,61 @@ +// Windows doesn't support parallel execution yet +// XFAIL: windows +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/main.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled main.swift\n" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/other.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled other.swift\n" +// CHECK-FIRST: {{^}$}} + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "began" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/other.swift" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "began" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/main.swift" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "finished" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: "output": "Handled {{other.swift|main.swift}}\n" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "finished" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: "output": "Handled {{main.swift|other.swift}}\n" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND-NOT: "skipped" diff --git a/test/Driver/Dependencies/one-way-parallel.swift b/test/Driver/Dependencies/one-way-parallel.swift index e2487b9b60cff..c2a61411a34a8 100644 --- a/test/Driver/Dependencies/one-way-parallel.swift +++ b/test/Driver/Dependencies/one-way-parallel.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: {{^{$}} @@ -32,7 +32,7 @@ // CHECK-FIRST: {{^}$}} // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j2 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: {{^{$}} // CHECK-SECOND: "kind": "began" diff --git a/test/Driver/Dependencies/one-way-parseable-fine.swift b/test/Driver/Dependencies/one-way-parseable-fine.swift new file mode 100644 index 0000000000000..6f87d5caaec67 --- /dev/null +++ b/test/Driver/Dependencies/one-way-parseable-fine.swift @@ -0,0 +1,92 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/main.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "began" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: ".\/other.swift" +// CHECK-FIRST: {{^}$}} + +// CHECK-FIRST: {{^{$}} +// CHECK-FIRST: "kind": "finished" +// CHECK-FIRST: "name": "compile" +// CHECK-FIRST: "output": "Handled other.swift{{(\\r)?}}\n" +// CHECK-FIRST: {{^}$}} + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "skipped" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/main.swift" +// CHECK-SECOND: {{^}$}} + +// CHECK-SECOND: {{^{$}} +// CHECK-SECOND: "kind": "skipped" +// CHECK-SECOND: "name": "compile" +// CHECK-SECOND: ".\/other.swift" +// CHECK-SECOND: {{^}$}} + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD: {{^{$}} +// CHECK-THIRD: "kind": "began" +// CHECK-THIRD: "name": "compile" +// CHECK-THIRD: ".\/main.swift" +// CHECK-THIRD: {{^}$}} + +// CHECK-THIRD: {{^{$}} +// CHECK-THIRD: "kind": "finished" +// CHECK-THIRD: "name": "compile" +// CHECK-THIRD: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-THIRD: {{^}$}} + +// CHECK-THIRD: {{^{$}} +// CHECK-THIRD: "kind": "began" +// CHECK-THIRD: "name": "compile" +// CHECK-THIRD: ".\/other.swift" +// CHECK-THIRD: {{^}$}} + +// CHECK-THIRD: {{^{$}} +// CHECK-THIRD: "kind": "finished" +// CHECK-THIRD: "name": "compile" +// CHECK-THIRD: "output": "Handled other.swift{{(\\r)?}}\n" +// CHECK-THIRD: {{^}$}} + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH: {{^{$}} +// CHECK-FOURTH: "kind": "began" +// CHECK-FOURTH: "name": "compile" +// CHECK-FOURTH: ".\/main.swift" +// CHECK-FOURTH: {{^}$}} + +// CHECK-FOURTH: {{^{$}} +// CHECK-FOURTH: "kind": "finished" +// CHECK-FOURTH: "name": "compile" +// CHECK-FOURTH: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-FOURTH: {{^}$}} + +// CHECK-FOURTH: {{^{$}} +// CHECK-FOURTH: "kind": "skipped" +// CHECK-FOURTH: "name": "compile" +// CHECK-FOURTH: ".\/other.swift" +// CHECK-FOURTH: {{^}$}} diff --git a/test/Driver/Dependencies/one-way-parseable.swift b/test/Driver/Dependencies/one-way-parseable.swift index 1f155cb4fb5cd..c85d1a9de9abf 100644 --- a/test/Driver/Dependencies/one-way-parseable.swift +++ b/test/Driver/Dependencies/one-way-parseable.swift @@ -2,7 +2,7 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: {{^{$}} @@ -29,7 +29,7 @@ // CHECK-FIRST: "output": "Handled other.swift{{(\\r)?}}\n" // CHECK-FIRST: {{^}$}} -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND: {{^{$}} // CHECK-SECOND: "kind": "skipped" @@ -44,34 +44,34 @@ // CHECK-SECOND: {{^}$}} // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD: {{^{$}} // CHECK-THIRD: "kind": "began" // CHECK-THIRD: "name": "compile" -// CHECK-THIRD: ".\/other.swift" +// CHECK-THIRD: ".\/main.swift" // CHECK-THIRD: {{^}$}} // CHECK-THIRD: {{^{$}} // CHECK-THIRD: "kind": "finished" // CHECK-THIRD: "name": "compile" -// CHECK-THIRD: "output": "Handled other.swift{{(\\r)?}}\n" +// CHECK-THIRD: "output": "Handled main.swift{{(\\r)?}}\n" // CHECK-THIRD: {{^}$}} // CHECK-THIRD: {{^{$}} // CHECK-THIRD: "kind": "began" // CHECK-THIRD: "name": "compile" -// CHECK-THIRD: ".\/main.swift" +// CHECK-THIRD: ".\/other.swift" // CHECK-THIRD: {{^}$}} // CHECK-THIRD: {{^{$}} // CHECK-THIRD: "kind": "finished" // CHECK-THIRD: "name": "compile" -// CHECK-THIRD: "output": "Handled main.swift{{(\\r)?}}\n" +// CHECK-THIRD: "output": "Handled other.swift{{(\\r)?}}\n" // CHECK-THIRD: {{^}$}} // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -parseable-output 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH: {{^{$}} // CHECK-FOURTH: "kind": "began" diff --git a/test/Driver/Dependencies/one-way-provides-after-fine.swift b/test/Driver/Dependencies/one-way-provides-after-fine.swift new file mode 100644 index 0000000000000..411e5cfe99094 --- /dev/null +++ b/test/Driver/Dependencies/one-way-provides-after-fine.swift @@ -0,0 +1,48 @@ +/// other | main +/// other +==> main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-provides-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-provides-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-provides-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-provides-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// RUN: touch -t 201401240008 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-NOT: Handled other.swift +// CHECK-THIRD: Handled main.swift +// CHECK-THIRD-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/one-way-provides-after.swift b/test/Driver/Dependencies/one-way-provides-after.swift index 6ad55592d7966..d025f35d84544 100644 --- a/test/Driver/Dependencies/one-way-provides-after.swift +++ b/test/Driver/Dependencies/one-way-provides-after.swift @@ -6,42 +6,42 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-provides-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift -// CHECK-SECOND: Handled main.swift +// CHECK-SECOND-DAG: Handled other.swift +// CHECK-SECOND-DAG: Handled main.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/one-way-provides-after/* %t // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-provides-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240008 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-NOT: Handled other.swift // CHECK-THIRD: Handled main.swift diff --git a/test/Driver/Dependencies/one-way-provides-before-fine.swift b/test/Driver/Dependencies/one-way-provides-before-fine.swift new file mode 100644 index 0000000000000..c6d731a8f0c93 --- /dev/null +++ b/test/Driver/Dependencies/one-way-provides-before-fine.swift @@ -0,0 +1,52 @@ +/// other +==> main +/// other | main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-provides-before-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-provides-before-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST-NOT: Handled + +// RUN: touch -t 201401240006 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s + +// CHECK-SECOND: Handled main.swift +// CHECK-SECOND: Handled other.swift + +// RUN: touch -t 201401240007 %t/other.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s + +// CHECK-THIRD-NOT: Handled main.swift +// CHECK-THIRD: Handled other.swift +// CHECK-THIRD-NOT: Handled main.swift + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-provides-before-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/one-way-provides-before-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// RUN: touch -t 201401240007 %t/main.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s + +// CHECK-FOURTH-NOT: Handled other.swift +// CHECK-FOURTH: Handled main.swift +// CHECK-FOURTH-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/one-way-provides-before.swift b/test/Driver/Dependencies/one-way-provides-before.swift index bff964d9712a4..13fc603bc9a93 100644 --- a/test/Driver/Dependencies/one-way-provides-before.swift +++ b/test/Driver/Dependencies/one-way-provides-before.swift @@ -6,24 +6,24 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-provides-before/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s -// CHECK-SECOND: Handled other.swift // CHECK-SECOND: Handled main.swift +// CHECK-SECOND: Handled other.swift // RUN: touch -t 201401240007 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // CHECK-THIRD-NOT: Handled main.swift // CHECK-THIRD: Handled other.swift @@ -34,18 +34,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/one-way-provides-before/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: touch -t 201401240007 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift diff --git a/test/Driver/Dependencies/one-way-while-editing-fine.swift b/test/Driver/Dependencies/one-way-while-editing-fine.swift new file mode 100644 index 0000000000000..489793bf60b8c --- /dev/null +++ b/test/Driver/Dependencies/one-way-while-editing-fine.swift @@ -0,0 +1,34 @@ +/// other ==> main + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/one-way-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s + +// CHECK: Handled main.swift +// CHECK: Handled other.swift +// CHECK-NOT: error +// CHECK: error: input file 'other.swift' was modified during the build +// CHECK-NOT: error + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-RECOVER %s + +// CHECK-RECOVER: Handled main.swift +// CHECK-RECOVER: Handled other.swift + + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && not %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED %s + +// CHECK-REVERSED: Handled other.swift +// CHECK-REVERSED: Handled main.swift +// CHECK-REVERSED-NOT: error +// CHECK-REVERSED: error: input file 'main.swift' was modified during the build +// CHECK-REVERSED-NOT: error + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED-RECOVER %s + +// CHECK-REVERSED-RECOVER-NOT: Handled other.swift +// CHECK-REVERSED-RECOVER: Handled main.swift +// CHECK-REVERSED-RECOVER-NOT: Handled other.swift diff --git a/test/Driver/Dependencies/one-way-while-editing.swift b/test/Driver/Dependencies/one-way-while-editing.swift index f6c81c8ef9fee..c4937d9c69385 100644 --- a/test/Driver/Dependencies/one-way-while-editing.swift +++ b/test/Driver/Dependencies/one-way-while-editing.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck %s // CHECK: Handled main.swift // CHECK: Handled other.swift @@ -12,14 +12,14 @@ // CHECK: error: input file 'other.swift' was modified during the build // CHECK-NOT: error -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-RECOVER %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-RECOVER %s // CHECK-RECOVER: Handled main.swift // CHECK-RECOVER: Handled other.swift // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED %s +// RUN: cd %t && not %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/modify-non-primary-files.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED %s // CHECK-REVERSED: Handled other.swift // CHECK-REVERSED: Handled main.swift @@ -27,7 +27,7 @@ // CHECK-REVERSED: error: input file 'main.swift' was modified during the build // CHECK-REVERSED-NOT: error -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED-RECOVER %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REVERSED-RECOVER %s // CHECK-REVERSED-RECOVER-NOT: Handled other.swift // CHECK-REVERSED-RECOVER: Handled main.swift diff --git a/test/Driver/Dependencies/one-way.swift b/test/Driver/Dependencies/one-way.swift index 25e6071f8cac2..46b97d0d6ca33 100644 --- a/test/Driver/Dependencies/one-way.swift +++ b/test/Driver/Dependencies/one-way.swift @@ -4,34 +4,34 @@ // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // CHECK-FIRST-NOT: warning // CHECK-FIRST: Handled main.swift // CHECK-FIRST: Handled other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-SECOND %s // CHECK-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s -// CHECK-THIRD: Handled other.swift // CHECK-THIRD: Handled main.swift +// CHECK-THIRD: Handled other.swift // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // CHECK-FOURTH-NOT: Handled other.swift // CHECK-FOURTH: Handled main.swift // CHECK-FOURTH-NOT: Handled other.swift // RUN: rm %t/main.o -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: rm %t/other.o -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIFTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIFTH %s // CHECK-FIFTH-NOT: Handled main.swift // CHECK-FIFTH: Handled other.swift @@ -41,34 +41,34 @@ // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FIRST %s // Try modifying the inputs /backwards/ in time rather than forwards. // RUN: touch -t 201401240004 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-THIRD %s // RUN: touch -t 201401240004 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-FOURTH %s // RUN: %empty-directory(%t) // RUN: cp -r %S/Inputs/one-way/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s // CHECK-REV-FIRST: Handled other.swift // CHECK-REV-FIRST: Handled main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-SECOND %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-SECOND %s // CHECK-REV-SECOND-NOT: Handled // RUN: touch -t 201401240006 %t/other.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FIRST %s // RUN: touch -t 201401240006 %t/main.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FOURTH %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./other.swift ./main.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-REV-FOURTH %s // CHECK-REV-FOURTH-NOT: Handled other.swift // CHECK-REV-FOURTH: Handled main.swift diff --git a/test/Driver/Dependencies/only-skip-once.swift b/test/Driver/Dependencies/only-skip-once.swift index 8cb049b07e3b6..f3b590549cc97 100644 --- a/test/Driver/Dependencies/only-skip-once.swift +++ b/test/Driver/Dependencies/only-skip-once.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/only-skip-once/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %target-swiftc_driver -driver-show-job-lifecycle -output-file-map %t/output-file-map.json -incremental main.swift file1.swift file2.swift -j1 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %target-swiftc_driver -driver-show-job-lifecycle -output-file-map %t/output-file-map.json -incremental main.swift file1.swift file2.swift -j1 2>%t/stderr.txt | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL: Job finished: {compile: main.o <= main.swift} // CHECK-INITIAL: Job finished: {compile: file1.o <= file1.swift} @@ -12,7 +12,7 @@ // CHECK-INITIAL: Job finished: {link: main <= main.o file1.o file2.o} // RUN: touch -t 201401240006 %t/file2.swift -// RUN: cd %t && %target-swiftc_driver -driver-show-job-lifecycle -output-file-map %t/output-file-map.json -incremental main.swift file1.swift file2.swift -j1 2>&1 | %FileCheck -check-prefix=CHECK-REBUILD %s +// RUN: cd %t && %target-swiftc_driver -driver-show-job-lifecycle -output-file-map %t/output-file-map.json -incremental main.swift file1.swift file2.swift -j1 2>%t/stderr.txt | %FileCheck -check-prefix=CHECK-REBUILD %s // We should skip the main and file1 rebuilds here, but we should only note skipping them _once_ // CHECK-REBUILD: Job finished: {compile: file2.o <= file2.swift} diff --git a/test/Driver/Dependencies/private-after-fine.swift b/test/Driver/Dependencies/private-after-fine.swift new file mode 100644 index 0000000000000..e6494551dad3d --- /dev/null +++ b/test/Driver/Dependencies/private-after-fine.swift @@ -0,0 +1,54 @@ +/// a --> b ==> c | a ==> d | e ==> b | f ==> g +/// a --> b ==> c | a ==> d +==> e +==> b, e --> f ==> g + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/private-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/private-after-fine/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL-NOT: Handled + +// RUN: touch -t 201401240006 %t/a.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=CHECK-A-NEG %s < %t/a.txt + +// CHECK-A: Handled a.swift +// CHECK-A-DAG: Handled b.swift +// CHECK-A-DAG: Handled d.swift +// CHECK-A: Handled e.swift +// CHECK-A-DAG: Handled c.swift +// CHECK-A-DAG: Handled f.swift +// CHECK-A-NEG-NOT: Handled g.swift + + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/private-after-fine/* %t +// RUN: touch -t 201401240005 %t/*.swift + +// Generate the build record... +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v + +// ...then reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/private-after-fine/*.swiftdeps %t + +// RUN: touch -t 201401240006 %t/f.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/f.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-F %s < %t/f.txt +// RUN: %FileCheck -check-prefix=CHECK-F-NEG %s < %t/f.txt + +// CHECK-F: Handled f.swift +// CHECK-F: Handled g.swift +// CHECK-F-NEG-NOT: Handled a.swift +// CHECK-F-NEG-NOT: Handled b.swift +// CHECK-F-NEG-NOT: Handled c.swift +// CHECK-F-NEG-NOT: Handled d.swift +// CHECK-F-NEG-NOT: Handled e.swift diff --git a/test/Driver/Dependencies/private-after.swift b/test/Driver/Dependencies/private-after.swift index 2901f299c5a94..cddc579396520 100644 --- a/test/Driver/Dependencies/private-after.swift +++ b/test/Driver/Dependencies/private-after.swift @@ -6,18 +6,18 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/private-after/*.swiftdeps %t -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL-NOT: warning // CHECK-INITIAL-NOT: Handled // RUN: touch -t 201401240006 %t/a.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/a.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt // RUN: %FileCheck -check-prefix=CHECK-A-NEG %s < %t/a.txt @@ -35,13 +35,13 @@ // RUN: touch -t 201401240005 %t/*.swift // Generate the build record... -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v // ...then reset the .swiftdeps files. // RUN: cp -r %S/Inputs/private-after/*.swiftdeps %t // RUN: touch -t 201401240006 %t/f.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/f.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift ./f.swift ./g.swift -module-name main -j1 -v > %t/f.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-F %s < %t/f.txt // RUN: %FileCheck -check-prefix=CHECK-F-NEG %s < %t/f.txt diff --git a/test/Driver/Dependencies/private-fine.swift b/test/Driver/Dependencies/private-fine.swift new file mode 100644 index 0000000000000..ab2508bae32e8 --- /dev/null +++ b/test/Driver/Dependencies/private-fine.swift @@ -0,0 +1,87 @@ +// a ==> b --> c ==> d | e ==> c + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/private-fine/* %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled a.swift +// CHECK-INITIAL: Handled b.swift +// CHECK-INITIAL: Handled c.swift +// CHECK-INITIAL: Handled d.swift +// CHECK-INITIAL: Handled e.swift + +// RUN: touch -t 201401240006 %t/a.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt +// RUN: %FileCheck -check-prefix=CHECK-A-NEG %s < %t/a.txt + +// CHECK-A: Handled a.swift +// CHECK-A-DAG: Handled b.swift +// CHECK-A-DAG: Handled c.swift +// CHECK-A-NEG-NOT: Handled d.swift +// CHECK-A-NEG-NOT: Handled e.swift + +// RUN: touch -t 201401240006 %t/b.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/b.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-B %s < %t/b.txt +// RUN: %FileCheck -check-prefix=CHECK-B-NEG %s < %t/b.txt + +// CHECK-B-NEG-NOT: Handled a.swift +// CHECK-B: Handled b.swift +// CHECK-B: Handled c.swift +// CHECK-B-NEG-NOT: Handled d.swift +// CHECK-B-NEG-NOT: Handled e.swift + +// RUN: touch -t 201401240006 %t/c.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/c.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-C %s < %t/c.txt +// RUN: %FileCheck -check-prefix=CHECK-C-NEG %s < %t/c.txt + +// CHECK-C-NEG-NOT: Handled a.swift +// CHECK-C-NEG-NOT: Handled b.swift +// CHECK-C: Handled c.swift +// CHECK-C: Handled d.swift +// CHECK-C-NEG-NOT: Handled e.swift + +// RUN: touch -t 201401240006 %t/d.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/d.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-D %s < %t/d.txt +// RUN: %FileCheck -check-prefix=CHECK-D-NEG %s < %t/d.txt + +// CHECK-D-NEG-NOT: Handled a.swift +// CHECK-D-NEG-NOT: Handled b.swift +// CHECK-D-NEG-NOT: Handled c.swift +// CHECK-D: Handled d.swift +// CHECK-D-NEG-NOT: Handled e.swift + +// RUN: touch -t 201401240006 %t/e.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/e.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-E %s < %t/e.txt +// RUN: %FileCheck -check-prefix=CHECK-E-NEG %s < %t/e.txt + +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift +// CHECK-E: Handled c.swift +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift +// CHECK-E: Handled d.swift +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift +// CHECK-E: Handled e.swift +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift + +// RUN: touch -t 201401240007 %t/a.swift %t/e.swift +// RUN: cd %t && %swiftc_driver -enable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/ae.txt 2>&1 +// RUN: %FileCheck -check-prefix=CHECK-AE %s < %t/ae.txt +// RUN: %FileCheck -check-prefix=CHECK-AE-NEG %s < %t/ae.txt + +// CHECK-AE: Handled a.swift +// CHECK-AE: Handled b.swift +// CHECK-AE: Handled c.swift +// CHECK-AE: Handled d.swift +// CHECK-AE: Handled e.swift +// CHECK-AE-NEG: Handled diff --git a/test/Driver/Dependencies/private.swift b/test/Driver/Dependencies/private.swift index e1d33771617b0..c66f96157372d 100644 --- a/test/Driver/Dependencies/private.swift +++ b/test/Driver/Dependencies/private.swift @@ -4,7 +4,7 @@ // RUN: cp -r %S/Inputs/private/* %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v 2>&1 | %FileCheck -check-prefix=CHECK-INITIAL %s // CHECK-INITIAL-NOT: warning // CHECK-INITIAL: Handled a.swift @@ -14,7 +14,7 @@ // CHECK-INITIAL: Handled e.swift // RUN: touch -t 201401240006 %t/a.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/a.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/a.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-A %s < %t/a.txt // RUN: %FileCheck -check-prefix=CHECK-A-NEG %s < %t/a.txt @@ -25,7 +25,7 @@ // CHECK-A-NEG-NOT: Handled e.swift // RUN: touch -t 201401240006 %t/b.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/b.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/b.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-B %s < %t/b.txt // RUN: %FileCheck -check-prefix=CHECK-B-NEG %s < %t/b.txt @@ -36,7 +36,7 @@ // CHECK-B-NEG-NOT: Handled e.swift // RUN: touch -t 201401240006 %t/c.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/c.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/c.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-C %s < %t/c.txt // RUN: %FileCheck -check-prefix=CHECK-C-NEG %s < %t/c.txt @@ -47,7 +47,7 @@ // CHECK-C-NEG-NOT: Handled e.swift // RUN: touch -t 201401240006 %t/d.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/d.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/d.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-D %s < %t/d.txt // RUN: %FileCheck -check-prefix=CHECK-D-NEG %s < %t/d.txt @@ -58,24 +58,30 @@ // CHECK-D-NEG-NOT: Handled e.swift // RUN: touch -t 201401240006 %t/e.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/e.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/e.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-E %s < %t/e.txt // RUN: %FileCheck -check-prefix=CHECK-E-NEG %s < %t/e.txt +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift +// CHECK-E: Handled c.swift +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift +// CHECK-E: Handled d.swift // CHECK-E-NEG-NOT: Handled a.swift // CHECK-E-NEG-NOT: Handled b.swift // CHECK-E: Handled e.swift -// CHECK-E-DAG: Handled c.swift -// CHECK-E-DAG: Handled d.swift +// CHECK-E-NEG-NOT: Handled a.swift +// CHECK-E-NEG-NOT: Handled b.swift // RUN: touch -t 201401240007 %t/a.swift %t/e.swift -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/ae.txt 2>&1 +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -c -driver-use-frontend-path "%{python};%S/Inputs/update-dependencies.py" -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./a.swift ./b.swift ./c.swift ./d.swift ./e.swift -module-name main -j1 -v > %t/ae.txt 2>&1 // RUN: %FileCheck -check-prefix=CHECK-AE %s < %t/ae.txt // RUN: %FileCheck -check-prefix=CHECK-AE-NEG %s < %t/ae.txt // CHECK-AE: Handled a.swift +// CHECK-AE: Handled b.swift +// CHECK-AE: Handled c.swift +// CHECK-AE: Handled d.swift // CHECK-AE: Handled e.swift -// CHECK-AE-DAG: Handled b.swift -// CHECK-AE-DAG: Handled c.swift -// CHECK-AE-DAG: Handled d.swift // CHECK-AE-NEG: Handled diff --git a/test/Driver/Dependencies/Inputs/one-way-depends-after/other.o b/test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/linux/libclang_rt.scudo-x86_64.a similarity index 100% rename from test/Driver/Dependencies/Inputs/one-way-depends-after/other.o rename to test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/linux/libclang_rt.scudo-x86_64.a diff --git a/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileA.swift b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileA.swift new file mode 100644 index 0000000000000..fd1f550930586 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileA.swift @@ -0,0 +1,7 @@ +func func1InA() { + _ = Struct1InB() + print(3) +} +func another(i: Int) -> String { + return "another" +} diff --git a/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileB.swift b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileB.swift new file mode 100644 index 0000000000000..23ea06ef46891 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/fileB.swift @@ -0,0 +1,6 @@ +struct Struct1InB { // used by A + func f1() { } +} +struct Struct2InB { // not used + func f2() {} +} diff --git a/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/main.swift b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/main.swift new file mode 100644 index 0000000000000..447a2de13e96a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/main.swift @@ -0,0 +1,6 @@ +private var privateInMain = 33 +// watchMe will get more and more specific +// By running the program at various stages, one can see if the incrementality +// is correct. +func watchMe(_: Any) -> String {"Any"} +print ("watchMe is", watchMe(17)) diff --git a/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/output.json b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/output.json new file mode 100644 index 0000000000000..2e351e235d39c --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-incremental-no-build-record/output.json @@ -0,0 +1,21 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./fileA.swift": { + "object": "./fileA.o", + "swift-dependencies": "./fileA.swiftdeps" + }, + "./fileB.swift": { + "object": "./fileB.o", + "swift-dependencies": "./fileB.swiftdeps" + }, + "./fileC.swift": { + "object": "./fileC.o", + "swift-dependencies": "./fileC.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/fileA.swift b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileA.swift new file mode 100644 index 0000000000000..fd1f550930586 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileA.swift @@ -0,0 +1,7 @@ +func func1InA() { + _ = Struct1InB() + print(3) +} +func another(i: Int) -> String { + return "another" +} diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB0-baseline.swift b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB0-baseline.swift new file mode 100644 index 0000000000000..23ea06ef46891 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB0-baseline.swift @@ -0,0 +1,6 @@ +struct Struct1InB { // used by A + func f1() { } +} +struct Struct2InB { // not used + func f2() {} +} diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB1-internal-change.swift b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB1-internal-change.swift new file mode 100644 index 0000000000000..4e1a6ffbf9cd1 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB1-internal-change.swift @@ -0,0 +1,6 @@ +struct Struct1InB { // used by A + func f1() { _ = 3 + 4 } +} +struct Struct2InB { // not used + func f2() { _ = ["foobar", "baz"]} +} diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB2-external-change.swift b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB2-external-change.swift new file mode 100644 index 0000000000000..292a50aaf65a9 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/fileB2-external-change.swift @@ -0,0 +1,6 @@ +struct Struct1InB { // used by A + func f1() { _ = 3 + 4 } +} +struct Struct2InBxx { // not used + func f2() { _ = ["foobar", "baz"]} +} diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/main.swift b/test/Driver/SourceRanges/Inputs/range-lifecycle/main.swift new file mode 100644 index 0000000000000..447a2de13e96a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/main.swift @@ -0,0 +1,6 @@ +private var privateInMain = 33 +// watchMe will get more and more specific +// By running the program at various stages, one can see if the incrementality +// is correct. +func watchMe(_: Any) -> String {"Any"} +print ("watchMe is", watchMe(17)) diff --git a/test/Driver/SourceRanges/Inputs/range-lifecycle/output.json b/test/Driver/SourceRanges/Inputs/range-lifecycle/output.json new file mode 100644 index 0000000000000..2e351e235d39c --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-lifecycle/output.json @@ -0,0 +1,21 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./fileA.swift": { + "object": "./fileA.o", + "swift-dependencies": "./fileA.swiftdeps" + }, + "./fileB.swift": { + "object": "./fileB.o", + "swift-dependencies": "./fileB.swiftdeps" + }, + "./fileC.swift": { + "object": "./fileC.o", + "swift-dependencies": "./fileC.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/dummy.swiftdeps b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/dummy.swiftdeps new file mode 100644 index 0000000000000..d344d43a190ad --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/dummy.swiftdeps @@ -0,0 +1,11 @@ +### Swift dependencies file v0 ### +provides-top-level: +provides-nominal: +provides-member: +provides-dynamic-lookup: +depends-top-level: +depends-member: +depends-nominal: +depends-dynamic-lookup: +depends-external: +interface-hash: "9e509c73af21c62807a87d08505b85dd" diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.compiledsource new file mode 100644 index 0000000000000..901f4f31f219d --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.compiledsource @@ -0,0 +1,3 @@ +} +func nine() {} +func ten() {} diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swift new file mode 100644 index 0000000000000..b978841807dd8 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swift @@ -0,0 +1,2 @@ +} +func ten() {} diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in1.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.compiledsource new file mode 100644 index 0000000000000..00dedf6bd5f3e --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.compiledsource @@ -0,0 +1 @@ +abcde diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swift new file mode 100644 index 0000000000000..ccc3e23a9f052 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swift @@ -0,0 +1 @@ +abde diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in2.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.compiledsource new file mode 100644 index 0000000000000..d0a234c6d7279 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.compiledsource @@ -0,0 +1 @@ +line0 diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swift new file mode 100644 index 0000000000000..f6dc06a3d2851 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swift @@ -0,0 +1,3 @@ +line0 +line1 + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in3.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.compiledsource new file mode 100644 index 0000000000000..c1a5c1200f6f2 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.compiledsource @@ -0,0 +1,2 @@ +var snort = 333 + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swift new file mode 100644 index 0000000000000..364d91229bcc0 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swift @@ -0,0 +1,2 @@ +var fred = 123456 + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in4.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.compiledsource new file mode 100644 index 0000000000000..60a7d2f3cf881 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.compiledsource @@ -0,0 +1,8 @@ +line1 +line2a +line3 +line4a +line5 +line6 + + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swift new file mode 100644 index 0000000000000..7c1285d04f585 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swift @@ -0,0 +1,7 @@ +line1 +line2b +line3 +line4b +line6 + + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in5.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.compiledsource b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.compiledsource new file mode 100644 index 0000000000000..0fa4519174ae4 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.compiledsource @@ -0,0 +1,4 @@ + + } + } + } diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swift b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swift new file mode 100644 index 0000000000000..2870ec2dfc133 --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swift @@ -0,0 +1,5 @@ +f + } + } + } + diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swiftranges b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swiftranges new file mode 100644 index 0000000000000..f3871fb7e299a --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/in6.swiftranges @@ -0,0 +1,4 @@ +### Swift source ranges file v0 ### +--- +noninlinableFunctionBodies: [] +... diff --git a/test/Driver/SourceRanges/Inputs/range-sourcecomparator/output.json b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/output.json new file mode 100644 index 0000000000000..27cd13e14b02f --- /dev/null +++ b/test/Driver/SourceRanges/Inputs/range-sourcecomparator/output.json @@ -0,0 +1,29 @@ +{ + "./in1.swift": { + "object": "./in1.o", + "swift-dependencies": "./in1.swiftdeps" + }, + "./in2.swift": { + "object": "./in2.o", + "swift-dependencies": "./in2.swiftdeps" + }, + "./in3.swift": { + "object": "./in3.o", + "swift-dependencies": "./in3.swiftdeps" + }, + "./in4.swift": { + "object": "./in4.o", + "swift-dependencies": "./in4.swiftdeps" + }, + "./in5.swift": { + "object": "./in5.o", + "swift-dependencies": "./in5.swiftdeps" + }, + "./in6.swift": { + "object": "./in6.o", + "swift-dependencies": "./in6.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/SourceRanges/range-incremental-no-build-record.swift b/test/Driver/SourceRanges/range-incremental-no-build-record.swift new file mode 100644 index 0000000000000..035adc6f8db04 --- /dev/null +++ b/test/Driver/SourceRanges/range-incremental-no-build-record.swift @@ -0,0 +1,53 @@ +// Check behavior with no build record. +// +// ============================================================================= +// First, build without range dependencies with no new options. +// ============================================================================= + + +// Copy in the inputs. +// The lack of a build record or swiftdeps files should disable incremental compilation + + +// Ensure that the extra outputs are not generated when they should not be: +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/range-incremental-no-build-record/* %t +// RUN: cd %t && %swiftc_driver -output-file-map %t/output.json -incremental -enable-batch-mode ./main.swift ./fileA.swift ./fileB.swift -module-name main -j2 -driver-show-job-lifecycle -driver-show-incremental >& %t/output0 + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-NO-BUILD-REC %s < %t/output0 +// CHECK-NO-BUILD-REC: Incremental compilation could not read build record. + + +// RUN: ls %t | %FileCheck -check-prefix=CHECK-NO-RANGE-OUTPUTS %s +// CHECK-NO-RANGE-OUTPUTS-NOT: .swiftranges +// CHECK-NO-RANGE-OUTPUTS-NOT: .compiledsource +// CHECK-NO-RANGE-OUTPUTS: .swiftdeps +// CHECK-NO-RANGE-OUTPUTS-NOT: .swiftranges +// CHECK-NO-RANGE-OUTPUTS-NOT: .compiledsource + +// RUN: %FileCheck -check-prefix=CHECK-HAS-BATCHES %s < %t/output0 + +// CHECK-HAS-BATCHES: Batchable: {compile: + +// RUN: %t/main | tee run0 | grep Any > /dev/null && rm %t/main + +// ============================================================================= +// Same, except force the driver to compute both strategies via -driver-compare-incremental-schemes +// ============================================================================= + + +// Copy in the inputs. +// The lack of a build record or swiftdeps files should disable incremental compilation + + +// Ensure that the extra outputs are not generated when they should not be: +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/range-incremental-no-build-record/* %t +// RUN: cd %t && %swiftc_driver -driver-compare-incremental-schemes -output-file-map %t/output.json -incremental -enable-batch-mode ./main.swift ./fileA.swift ./fileB.swift -module-name main -j2 -driver-show-job-lifecycle -driver-show-incremental >& %t/output1 + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-NO-BUILD-REC %s < %t/output1 + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-COMPARE-DISABLED-NO-BUILD-RECORD %s < %t/output1 +// CHECK-COMPARE-DISABLED-NO-BUILD-RECORD: *** Incremental build disabled because could not read build record, cannot compare *** + +// RUN: %t/main | tee run1 | grep Any > /dev/null && rm %t/main diff --git a/test/Driver/SourceRanges/range-lifecycle.swift b/test/Driver/SourceRanges/range-lifecycle.swift new file mode 100644 index 0000000000000..0bb90411ca4ed --- /dev/null +++ b/test/Driver/SourceRanges/range-lifecycle.swift @@ -0,0 +1,169 @@ +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/range-lifecycle/* %t && cp %t/fileB{0-baseline,}.swift + +// ============================================================================= +// Without range dependencies, but logging comparison +// ============================================================================= + +// RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./fileA.swift ./fileB.swift -module-name main -output-file-map %t/output.json >& output1 + +// ============================================================================= +// Compile with range dependencies enabled +// and logging the comparison to comparo. +// ============================================================================= + + +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -enable-batch-mode -j2 -incremental -enable-source-range-dependencies -driver-compare-incremental-schemes-path=./comparo -driver-show-incremental -driver-show-job-lifecycle ./main.swift ./fileA.swift ./fileB.swift -module-name main -output-file-map %t/output.json >& %t/output2 + +// RUN: tail -1 %t/comparo | %FileCheck -match-full-lines -check-prefix=CHECK-COMPARO-1 %s +// CHECK-COMPARO-1: *** Incremental build disabled because different arguments passed to compiler, cannot compare *** + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-TURN-ON %s < %t/output2 + +// CHECK-TURN-ON-DAG: Incremental compilation has been disabled, because different arguments were passed to the compiler. +// CHECK-TURN-ON-DAG: Batchable: {compile: main.o <= main.swift} +// CHECK-TURN-ON-DAG: Batchable: {compile: fileA.o <= fileA.swift} +// CHECK-TURN-ON-DAG: Batchable: {compile: fileB.o <= fileB.swift} + + +// RUN: cmp main.swift main.compiledsource +// RUN: cmp fileA.swift fileA.compiledsource +// RUN: cmp fileB.swift fileB.compiledsource + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-MAIN-1 %s <%t/main.swiftranges + +// CHECK-MAIN-1: ### Swift source ranges file v0 ### +// CHECK-MAIN-1-NEXT: --- +// CHECK-MAIN-1-NEXT: noninlinableFunctionBodies: +// CHECK-MAIN-1-NEXT: - { start: { line: 5, column: 32 }, end: { line: 5, column: 39 } } +// CHECK-MAIN-1-NEXT: ... + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEA-1 %s <%t/fileA.swiftranges + +// CHECK-FILEA-1: ### Swift source ranges file v0 ### +// CHECK-FILEA-1: --- +// CHECK-FILEA-1: noninlinableFunctionBodies: +// CHECK-FILEA-1: - { start: { line: 1, column: 17 }, end: { line: 4, column: 2 } } +// CHECK-FILEA-1: - { start: { line: 5, column: 32 }, end: { line: 7, column: 2 } } +// CHECK-FILEA-1: ... + + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEB-1 %s <%t/fileB.swiftranges + +// CHECK-FILEB-1: ### Swift source ranges file v0 ### +// CHECK-FILEB-1: --- +// CHECK-FILEB-1: noninlinableFunctionBodies: +// CHECK-FILEB-1: - { start: { line: 2, column: 13 }, end: { line: 2, column: 16 } } +// CHECK-FILEB-1: - { start: { line: 5, column: 13 }, end: { line: 5, column: 15 } } +// CHECK-FILEB-1: ... + + +// ============================================================================= +// Steady-state: Now, do it again with no changes +// ============================================================================= + +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -enable-batch-mode -j2 -incremental -enable-source-range-dependencies -driver-compare-incremental-schemes-path=./comparo -driver-show-incremental -driver-show-job-lifecycle ./main.swift ./fileA.swift ./fileB.swift -module-name main -output-file-map %t/output.json >& %t/output3 + +// RUN: tail -1 %t/comparo | %FileCheck -match-full-lines -check-prefix=CHECK-COMPARO-2 %s +// CHECK-COMPARO-2: *** Range benefit: 0 compilations, 0 stages, without ranges: 0, with ranges: 0, used ranges, total: 3 *** + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-NO-CHANGES %s < %t/output3 +// CHECK-NO-CHANGES: (tentatively) Skipping file is up-to-date and output exists: {compile: main.o <= main.swift} +// CHECK-NO-CHANGES: (tentatively) Skipping file is up-to-date and output exists: {compile: fileA.o <= fileA.swift} +// CHECK-NO-CHANGES: (tentatively) Skipping file is up-to-date and output exists: {compile: fileB.o <= fileB.swift} +// CHECK-NO-CHANGES: Hypothetically: (tentatively) Skipping file is up-to-date and output exists: {compile: main.o <= main.swift} +// CHECK-NO-CHANGES: Hypothetically: (tentatively) Skipping file is up-to-date and output exists: {compile: fileA.o <= fileA.swift} +// CHECK-NO-CHANGES: Hypothetically: (tentatively) Skipping file is up-to-date and output exists: {compile: fileB.o <= fileB.swift} +// CHECK-NO-CHANGES: Skipping : {compile: main.o <= main.swift} +// CHECK-NO-CHANGES: Skipping : {compile: fileA.o <= fileA.swift} +// CHECK-NO-CHANGES: Skipping : {compile: fileB.o <= fileB.swift} + +// RUN: %FileCheck -check-prefix=CHECK-NO-MAIN %s < %t/output3 +// RUN: %FileCheck -check-prefix=CHECK-NO-FILEA %s < %t/output3 +// RUN: %FileCheck -check-prefix=CHECK-NO-FILEB %s < %t/output3 +// CHECK-NO-MAIN-NOT: Added to TaskQueue: {{.*}} main.cpp +// CHECK-NO-FILEA-NOT: Added to TaskQueue: {{.*}} fileA.cpp +// CHECK-NO-FILEB-NOT: Added to TaskQueue: {{.*}} fileB.cpp + + + +// Recheck supplementaries: +// RUN: cmp main.swift main.compiledsource +// RUN: cmp fileA.swift fileA.compiledsource +// RUN: cmp fileB.swift fileB.compiledsource +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-MAIN-1 %s <%t/main.swiftranges +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEA-1 %s <%t/fileA.swiftranges +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEB-1 %s <%t/fileB.swiftranges + +// ============================================================================= +// Steady-state: Now, do it again with no changes, but touching files +// ============================================================================= + +// RUN: touch %t/*.swift +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -enable-batch-mode -j2 -incremental -enable-source-range-dependencies -driver-compare-incremental-schemes-path=./comparo -driver-show-incremental -driver-show-job-lifecycle ./main.swift ./fileA.swift ./fileB.swift -module-name main -output-file-map %t/output.json >& %t/output4 + + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-COMPARO-3 %s < %t/comparo +// CHECK-COMPARO-3: *** Range benefit: 3 compilations, 1 stages, without ranges: 3, with ranges: 0, used ranges, total: 3 *** + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-TOUCHING-FILES %s < %t/output4 +// CHECK-TOUCHING-FILES: (tentatively) Queuing (initial): {compile: main.o <= main.swift} +// CHECK-TOUCHING-FILES: Skipping Did not change at all: {compile: main.o <= main.swift} +// CHECK-TOUCHING-FILES: (tentatively) Queuing (initial): {compile: fileA.o <= fileA.swift} +// CHECK-TOUCHING-FILES: Skipping Did not change at all: {compile: fileA.o <= fileA.swift} +// CHECK-TOUCHING-FILES: (tentatively) Queuing (initial): {compile: fileB.o <= fileB.swift} +// CHECK-TOUCHING-FILES: Skipping Did not change at all: {compile: fileB.o <= fileB.swift} +// CHECK-TOUCHING-FILES: Hypothetically: (tentatively) Queuing (initial): {compile: main.o <= main.swift} +// CHECK-TOUCHING-FILES: Hypothetically: (tentatively) Queuing (initial): {compile: fileA.o <= fileA.swift} +// CHECK-TOUCHING-FILES: Hypothetically: (tentatively) Queuing (initial): {compile: fileB.o <= fileB.swift} +// CHECK-TOUCHING-FILES: Skipping : {compile: main.o <= main.swift} +// CHECK-TOUCHING-FILES: Skipping : {compile: fileA.o <= fileA.swift} +// CHECK-TOUCHING-FILES: Skipping : {compile: fileB.o <= fileB.swift} + +// RUN: %FileCheck -check-prefix=CHECK-NO-MAIN %s < %t/output4 +// RUN: %FileCheck -check-prefix=CHECK-NO-FILEA %s < %t/output4 +// RUN: %FileCheck -check-prefix=CHECK-NO-FILEB %s < %t/output4 + + +// Recheck supplementaries: +// RUN: cmp main.swift main.compiledsource +// RUN: cmp fileA.swift fileA.compiledsource +// RUN: cmp fileB.swift fileB.compiledsource +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-MAIN-1 %s <%t/main.swiftranges +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEA-1 %s <%t/fileA.swiftranges +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-FILEB-1 %s <%t/fileB.swiftranges + + +// ============================================================================= +// Make an internal change, should not recompile dependents at all +// ============================================================================= + +// RUN: cp %t/fileB{1-internal-change,}.swift +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-compare-incremental-schemes -enable-source-range-dependencies -output-file-map %t/output.json -incremental -enable-batch-mode ./main.swift ./fileA.swift ./fileB.swift -module-name main -j2 -driver-show-job-lifecycle -driver-show-incremental >& %t/output5 + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-INTERNAL-CHANGE %s < %t/output5 +// CHECK-INTERNAL-CHANGE: Skipping : {compile: main.o <= main.swift} +// CHECK-INTERNAL-CHANGE: Skipping : {compile: fileA.o <= fileA.swift} +// CHECK-INTERNAL-CHANGE: *** Range benefit: 0 compilations, 0 stages, without ranges: 1, with ranges: 1, used ranges, total: 3 *** + +// RUN: %FileCheck -check-prefix=CHECK-NO-MAIN %s < %t/output5 +// RUN: %FileCheck -check-prefix=CHECK-NO-FILEA %s < %t/output5 + +// ============================================================================= +// Make an external change, should recompile dependents right away with ranges +// ============================================================================= + +// RUN: cp %t/fileB{2-external-change,}.swift +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -driver-compare-incremental-schemes -enable-source-range-dependencies -output-file-map %t/output.json -incremental -enable-batch-mode ./main.swift ./fileA.swift ./fileB.swift -module-name main -j2 -driver-show-job-lifecycle -driver-show-incremental >& %t/output6 + + +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-EXTERNAL-CHANGE-1 %s < %t/output6 +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-EXTERNAL-CHANGE-2 %s < %t/output6 +// RUN: %FileCheck -match-full-lines -check-prefix=CHECK-EXTERNAL-CHANGE-3 %s < %t/output6 + +// CHECK-EXTERNAL-CHANGE-1: Queuing changed at [4:18--4:18): {compile: fileB.o <= fileB.swift} +// CHECK-EXTERNAL-CHANGE-NEXT-1: - Will immediately schedule dependents of {compile: fileB.o <= fileB.swift} because changed outside a function body at: [4:18--4:18) +// CHECK-EXTERNAL-CHANGE-2: Queuing because of the initial set: {compile: fileA.o <= fileA.swift} +// CHECK-EXTERNAL-CHANGE-NEXT-2: fileB.swift provides type 'main.Struct1InB' +// CHECK-EXTERNAL-CHANGE-3: Skipping : {compile: main.o <= main.swift} +// CHECK-EXTERNAL-CHANGE-3: *** Range benefit: 0 compilations, 1 stages, without ranges: 2, with ranges: 2, used ranges, total: 3 *** diff --git a/test/Driver/SourceRanges/range-sourcecomparator.swift b/test/Driver/SourceRanges/range-sourcecomparator.swift new file mode 100644 index 0000000000000..279f3694f12e6 --- /dev/null +++ b/test/Driver/SourceRanges/range-sourcecomparator.swift @@ -0,0 +1,69 @@ +// Tests the diff algorithm (the SourceComparator) used for range-based +// incremental compilation. +// +// Disable fine-grained-dependencies because this test copies over dummy +// swiftdeps. +// +// If you try to add inputs, be sure to update output.json and add a swiftranges +// input. + +// Copy in the inputs. + + +// Ensure that the extra outputs are not generated when they should not be: +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/range-sourcecomparator/* %t + +// The lack of a build record or swiftdeps files should disable incremental compilation +// So, do one run just to build the build record + +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -output-file-map %t/output.json -incremental -c ./in1.swift ./in2.swift ./in3.swift ./in4.swift ./in5.swift ./in6.swift -module-name main -incremental -enable-source-range-dependencies -driver-skip-execution -driver-compare-incremental-schemes >& output + +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in1.swiftdeps +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in2.swiftdeps +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in3.swiftdeps +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in4.swiftdeps +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in5.swiftdeps +// RUN: cp %S/Inputs/range-sourcecomparator/dummy.swiftdeps %t/in6.swiftdeps + +// RUN: cd %t && %swiftc_driver -disable-fine-grained-dependencies -output-file-map %t/output.json -incremental -c ./in1.swift ./in2.swift ./in3.swift ./in4.swift ./in5.swift ./in6.swift -module-name main -incremental -enable-source-range-dependencies -driver-dump-compiled-source-diffs -driver-skip-execution -driver-compare-incremental-schemes >& output1 + + +// RUN: %FileCheck %s <%t/output1 --match-full-lines +// CHECK: *** all changed ranges in 'in1.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:1--2:15) +// CHECK-NEXT: *** all changed ranges in 'in1.swift' (w.r.t to-be-compiled) *** +// CHECK-NEXT: - [2:1--2:1) +// CHECK-NEXT: *** nonlocal changed ranges in 'in1.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:1--2:15) +// CHECK-EMPTY: +// CHECK-NEXT: *** all changed ranges in 'in2.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [1:3--1:4) +// CHECK-NEXT: *** all changed ranges in 'in2.swift' (w.r.t to-be-compiled) *** +// CHECK-NEXT: - [1:3--1:3) +// CHECK-NEXT: *** nonlocal changed ranges in 'in2.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [1:3--1:4) +// CHECK-EMPTY: +// CHECK-NEXT: *** all changed ranges in 'in3.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:1--2:1) +// CHECK-NEXT: *** all changed ranges in 'in3.swift' (w.r.t to-be-compiled) *** +// CHECK-NEXT: - [2:1--4:1) +// CHECK-NEXT: *** nonlocal changed ranges in 'in3.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:1--2:1) +// CHECK-EMPTY: +// CHECK-NEXT: *** all changed ranges in 'in4.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [1:5--1:16) +// CHECK-NEXT: *** all changed ranges in 'in4.swift' (w.r.t to-be-compiled) *** +// CHECK-NEXT: - [1:5--1:18) +// CHECK-NEXT: *** nonlocal changed ranges in 'in4.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [1:5--1:16) +// CHECK-EMPTY: +// CHECK-NEXT: *** all changed ranges in 'in5.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:6--2:7) +// CHECK-NEXT: - [4:6--5:6) +// CHECK-NEXT: *** all changed ranges in 'in5.swift' (w.r.t to-be-compiled) *** +// CHECK-NEXT: - [2:6--2:7) +// CHECK-NEXT: - [4:6--4:7) +// CHECK-NEXT: *** nonlocal changed ranges in 'in5.swift' (w.r.t previously-compiled) *** +// CHECK-NEXT: - [2:6--2:7) +// CHECK-NEXT: - [4:6--5:6) diff --git a/test/Driver/advanced_output_file_map.swift b/test/Driver/advanced_output_file_map.swift index 4a32e9a8f0aa2..3bd984c7bb178 100644 --- a/test/Driver/advanced_output_file_map.swift +++ b/test/Driver/advanced_output_file_map.swift @@ -1,27 +1,94 @@ -// RUN: echo "{\"%/s\": {\"object\": \"/build/obj/advanced_output_file_map.o\", \"swiftmodule\": \"/build/swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"/build/dia/advanced_output_file_map.dia\", \"dependencies\": \"/build/d/advanced_output_file_map.d\"}, \"%/S/Inputs/main.swift\": {\"object\": \"/build/obj/main.o\", \"swiftmodule\": \"/build/swiftmodule/main.swiftmodule\", \"swiftdoc\": "/build/swiftmodule/main_x.swiftdoc", \"diagnostics\": \"/build/dia/main.dia\", \"dependencies\": \"/build/d/main.d\"}, \"%/S/Inputs/lib.swift\": {\"object\": \"/build/obj/lib.o\", \"swiftmodule\": \"/build/swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"/build/swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"/build/dia/lib.dia\", \"dependencies\": \"/build/d/lib.d\"}}" > %t.json - -// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM -// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o /build/advanced_output_file_map.out -emit-module-path /build/OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS - -// DUMPOFM: {{.*}}/Inputs/lib.swift -> object: "/build/obj/lib.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "/build/d/lib.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "/build/swiftmodule/lib.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "/build/swiftmodule/lib_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "/build/dia/lib.dia" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> object: "/build/obj/main.o" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "/build/d/main.d" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "/build/swiftmodule/main.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "/build/swiftmodule/main_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "/build/dia/main.dia" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "/build/obj/advanced_output_file_map.o" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "/build/d/advanced_output_file_map.d" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc" -// DUMPOFM-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "/build/dia/advanced_output_file_map.dia" - -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "/build/obj/advanced_output_file_map.o", dependencies: "/build/d/advanced_output_file_map.d", swiftmodule: "/build/swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "/build/swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "/build/dia/advanced_output_file_map.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "/build/obj/main.o", dependencies: "/build/d/main.d", swiftmodule: "/build/swiftmodule/main.swiftmodule", swiftdoc: "/build/swiftmodule/main_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "/build/dia/main.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "/build/obj/lib.o", dependencies: "/build/d/lib.d", swiftmodule: "/build/swiftmodule/lib.swiftmodule", swiftdoc: "/build/swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "/build/swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "/build/dia/lib.dia"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o"], output: {swiftmodule: "/build/OutputFileMap.swiftmodule", swiftdoc: "/build{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: "/build{{[/\\]}}OutputFileMap.swiftsourceinfo"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["/build/obj/advanced_output_file_map.o", "/build/obj/main.o", "/build/obj/lib.o", "/build/OutputFileMap.swiftmodule"], output: {image: "/build/advanced_output_file_map.out"} -// BINDINGS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["/build/advanced_output_file_map.out"], output: {dSYM: "/build/advanced_output_file_map.out.dSYM"} +// Test both ways: -disable-only-one-dependency-file, and the default which should be same as -enable-only-one-dependency-file + +// RUN: %empty-directory(%t) + +// Create an output file map +// RUN: echo "{\"%/s\": {\"object\": \"./obj/advanced_output_file_map.o\", \"swiftmodule\": \"./swiftmodule/advanced_output_file_map.swiftmodule\", \"swiftdoc\": "./swiftmodule/advanced_output_file_map_x.swiftdoc", \"diagnostics\": \"./dia/advanced_output_file_map.dia\", \"dependencies\": \"./d/advanced_output_file_map.d\"}, " >%t/ofm.json +// RUN: echo " \"%/S/Inputs/main.swift\": {\"object\": \"./obj/main.o\", \"swiftmodule\": \"./swiftmodule/main.swiftmodule\", \"swiftdoc\": \"./swiftmodule/main_x.swiftdoc\", \"diagnostics\": \"./dia/main.dia\", \"dependencies\": \"./d/main.d\"}, " >> %t/ofm.json +// RUN: echo " \"%/S/Inputs/lib.swift\": {\"object\": \"./obj/lib.o\", \"swiftmodule\": \"./swiftmodule/lib.swiftmodule\", \"swiftdoc\": \"./swiftmodule/lib_x.swiftdoc\", \"diagnostics\": \"./dia/lib.dia\", \"dependencies\": \"./d/lib.d\"}}" >> %t/ofm.json + +// With -disable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-DIS + +// DUMPOFM-DIS: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-DIS-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -disable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-DIS + +// Should be no dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test ! -e %t/d/main.d +// RUN: test ! -e %t/d/lib.d + + +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", dependencies: "./d/main.d", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", dependencies: "./d/lib.d", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-DIS: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + + +// With -enable-only-one-dependency-file + +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + + +// DUMPOFM-ENA: {{.*}}/Inputs/lib.swift -> object: "./obj/lib.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> dependencies: "./d/lib.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftmodule: "./swiftmodule/lib.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> swiftdoc: "./swiftmodule/lib_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/lib.swift -> diagnostics: "./dia/lib.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> object: "./obj/main.o" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> dependencies: "./d/main.d" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftmodule: "./swiftmodule/main.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> swiftdoc: "./swiftmodule/main_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/Inputs/main.swift -> diagnostics: "./dia/main.dia" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> object: "./obj/advanced_output_file_map.o" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> dependencies: "./d/advanced_output_file_map.d" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc" +// DUMPOFM-ENA-NEXT: {{.*}}/advanced_output_file_map.swift -> diagnostics: "./dia/advanced_output_file_map.dia" + +// RUN: %empty-directory(%t/d) +// RUN: cd %t && %swiftc_driver -enable-only-one-dependency-file -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-ENA + + +// Should be two dummy files: +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d + + +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/advanced_output_file_map.swift"], output: {object: "./obj/advanced_output_file_map.o", dependencies: "./d/advanced_output_file_map.d", swiftmodule: "./swiftmodule/advanced_output_file_map.swiftmodule", swiftdoc: "./swiftmodule/advanced_output_file_map_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}advanced_output_file_map.swiftsourceinfo", diagnostics: "./dia/advanced_output_file_map.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/main.swift"], output: {object: "./obj/main.o", swiftmodule: "./swiftmodule/main.swiftmodule", swiftdoc: "./swiftmodule/main_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}main.swiftsourceinfo", diagnostics: "./dia/main.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["{{.*}}/Inputs/lib.swift"], output: {object: "./obj/lib.o", swiftmodule: "./swiftmodule/lib.swiftmodule", swiftdoc: "./swiftmodule/lib_x.swiftdoc", swiftsourceinfo: "./swiftmodule{{[/\\]}}lib.swiftsourceinfo", diagnostics: "./dia/lib.dia"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "swift{{c?(\.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o"], output: {swiftmodule: "./OutputFileMap.swiftmodule", swiftdoc: ".{{[/\\]}}OutputFileMap.swiftdoc", swiftsourceinfo: ".{{[/\\]}}OutputFileMap.swiftsourceinfo"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "ld{{(.exe)?}}", inputs: ["./obj/advanced_output_file_map.o", "./obj/main.o", "./obj/lib.o", "./OutputFileMap.swiftmodule"], output: {image: "./advanced_output_file_map.out"} +// BINDINGS-ENA: # "x86_64-apple-macosx10.9" - "dsymutil{{(\.exe)?}}", inputs: ["./advanced_output_file_map.out"], output: {dSYM: "./advanced_output_file_map.out.dSYM"} + +// Defaulting to: -enable-only-one-dependency-file + +// RUN: %swiftc_driver -driver-print-output-file-map -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=DUMPOFM-ENA + + +// RUN: %empty-directory(%t/d) +// RUN: %swiftc_driver -driver-print-bindings -target x86_64-apple-macosx10.9 -emit-executable -emit-module -serialize-diagnostics -emit-dependencies %/s %/S/Inputs/main.swift %/S/Inputs/lib.swift -g -o ./advanced_output_file_map.out -emit-module-path ./OutputFileMap.swiftmodule -module-name OutputFileMap -output-file-map %t/ofm.json 2>&1 | %FileCheck %/s -check-prefix=BINDINGS-ENA +// RUN: test ! -e %t/d/advanced_output_file_map.d +// RUN: test -e %t/d/main.d -a ! -s %t/d/main.d +// RUN: test -e %t/d/lib.d -a ! -s %t/d/lib.d diff --git a/test/Driver/ast_dump_with_WMO.swift b/test/Driver/ast_dump_with_WMO.swift new file mode 100644 index 0000000000000..2b2a142f730c8 --- /dev/null +++ b/test/Driver/ast_dump_with_WMO.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) + + +// Check that -wmo is ignored when -dump-ast is present and that the AST gets +// dumped to stdout, no matter the order of the options + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_WMO_dump | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %swiftc_driver -dump-ast -whole-module-optimization -module-name=main %S/../Inputs/empty.swift -### 2>%t/stderr_dump_WMO | %FileCheck -check-prefix CHECK-STDOUT %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_dump +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_dump_WMO + + +// Check that ignoring -wmo doesn't affect the output file paths for the AST +// dumps + +// RUN: cd %t +// RUN: echo ' ' > a.swift +// RUN: echo ' ' > main.swift +// RUN: echo '{ "a.swift": { "ast-dump": "a.ast" }, "main.swift": { "ast-dump": "main.ast" }}' > outputFileMap.json + +// RUN: %swiftc_driver -whole-module-optimization -dump-ast -module-name=main -output-file-map=outputFileMap.json main.swift a.swift -### 2>%t/stderr_WMO_OFM | %FileCheck -check-prefix CHECK-OFM %s +// RUN: %FileCheck -check-prefix CHECK-WMO %s <%t/stderr_WMO_OFM + + + +// CHECK-WMO: warning: ignoring '-wmo' because '-dump-ast' was also specified + +// CHECK-STDOUT-NOT: -whole-module-optimization +// CHECK-STDOUT-NOT: -wmo +// CHECK-STDOUT: -dump-ast +// CHECK-STDOUT: -o - + +// CHECK-OFM-NOT: -whole-module-optimization +// CHECK-OFM-NOT: -wmo +// CHECK-OFM: -dump-ast +// CHECK-OFM-SAME: -o main.ast +// CHECK-OFM-NEXT: -dump-ast +// CHECK-OFM-SAME: -o a.ast diff --git a/test/Driver/autolink-force-load-no-comdat.swift b/test/Driver/autolink-force-load-no-comdat.swift index c4dc53a473410..f87d27a0337a8 100644 --- a/test/Driver/autolink-force-load-no-comdat.swift +++ b/test/Driver/autolink-force-load-no-comdat.swift @@ -2,7 +2,7 @@ // RUN: not %swiftc_driver -autolink-force-load -incremental %s 2>&1 | %FileCheck -check-prefix=AUTOLINK_FORCE_LOAD %s // MACHO targets do not support COMDAT -// REQUIRES-ANY: OS=macosx, OS=tvos, OS=watchos, OS=ios +// REQUIRES: OS=macosx || OS=tvos || OS=watchos || OS=ios // AUTOLINK_FORCE_LOAD: error: '-autolink-force-load' is not supported with '-incremental' diff --git a/test/Driver/batch_mode_dependencies_make_wrong_order.swift b/test/Driver/batch_mode_dependencies_make_wrong_order.swift index 6888ee6461564..8e26f32a3e87e 100644 --- a/test/Driver/batch_mode_dependencies_make_wrong_order.swift +++ b/test/Driver/batch_mode_dependencies_make_wrong_order.swift @@ -16,13 +16,13 @@ // RUN: cd %t && %swiftc_driver -enable-batch-mode -incremental -output-file-map %S/Inputs/abcd_filemap.yaml -module-name main -j 1 d.swift c.swift b.swift a.swift main.swift -driver-show-incremental -driver-show-job-lifecycle >%t/out.txt 2>&1 // RUN: %FileCheck %s <%t/out.txt // -// Check that we saw invalidation happen in alphabetic order -// CHECK: Queuing because of dependencies discovered later: {compile: b.o <= b.swift} -// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// Check that we saw invalidation happen in command-line argument order // CHECK: Queuing because of dependencies discovered later: {compile: d.o <= d.swift} -// CHECK: Batchable: {compile: b.o <= b.swift} -// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: c.o <= c.swift} +// CHECK: Queuing because of dependencies discovered later: {compile: b.o <= b.swift} // CHECK: Batchable: {compile: d.o <= d.swift} +// CHECK: Batchable: {compile: c.o <= c.swift} +// CHECK: Batchable: {compile: b.o <= b.swift} // -// But check that we still issued the job in reverse-alphabetic order +// Check that we still issued the job in reverse-alphabetic order // CHECK: Adding batch job to task queue: {compile: d.o c.o b.o <= d.swift c.swift b.swift} diff --git a/test/Driver/batch_mode_parseable_output-fine.swift b/test/Driver/batch_mode_parseable_output-fine.swift new file mode 100644 index 0000000000000..da7888cb2a4ba --- /dev/null +++ b/test/Driver/batch_mode_parseable_output-fine.swift @@ -0,0 +1,310 @@ +// RUN: %empty-directory(%t) +// RUN: touch %t/file-01.swift %t/file-02.swift %t/file-03.swift +// RUN: echo 'public func main() {}' >%t/main.swift +// +// RUN: %swiftc_driver -enable-fine-grained-dependencies -enable-batch-mode -parseable-output -driver-skip-execution -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift 2>&1 | %FileCheck -check-prefix CHECK %s +// +// +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*[\\/]}}swift{{c?(\.exe)?(\\")?}} -frontend -c -primary-file {{.*}}/file-01.swift{{(\\")?}} {{.*}}file-02.swift{{(\\")?}} {{.*}}file-03.swift{{(\\")?}} {{.*}}main.swift{{(\\")?}} -emit-module-path {{.*}}file-01-[[MODULE01:[a-z0-9]+]].swiftmodule{{(\\")?}} -emit-module-doc-path {{.*}}file-01-[[SWIFTDOC01:[a-z0-9]+]].swiftdoc{{(\\")?}} {{.*}} -enable-fine-grained-dependencies {{.*}} -module-name main -o {{.*}}file-01-[[OBJ01:[a-z0-9]+]].o{{(\\")?}}", +// CHECK-NEXT: "command_executable": "{{.*[\\/]}}swift{{c?(\.exe)?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*[\\/]}}file-01.swift", +// CHECK-NEXT: "{{.*[\\/]}}file-02.swift", +// CHECK-NEXT: "{{.*[\\/]}}file-03.swift", +// CHECK-NEXT: "{{.*[\\/]}}main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*[\\/]}}file-01-[[MODULE01:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*[\\/]}}file-01-[[SWIFTDOC01:[a-z0-9]+]].swiftdoc", +// CHECK: "-enable-fine-grained-dependencies", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*[\\/]}}file-01-[[OBJ01:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*[\\/]}}file-01.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-01-[[OBJ01]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-01-[[MODULE01]].swiftmodule" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftdoc", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-01-[[SWIFTDOC01]].swiftdoc" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftsourceinfo", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-01-[[MODULE01]].swiftsourceinfo" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*[\\/]}}swift{{c?(\.exe)?(\\")?}} -frontend -c {{.*}}file-01.swift{{(\\")?}} -primary-file {{.*}}file-02.swift{{(\\")?}} {{.*}}file-03.swift{{(\\")?}} {{.*}}main.swift{{(\\")?}} -emit-module-path {{.*}}file-02-[[MODULE02:[a-z0-9]+]].swiftmodule{{(\\")?}} -emit-module-doc-path {{.*}}file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc{{(\\")?}} {{.*}} -enable-fine-grained-dependencies {{.*}} -module-name main -o {{.*}}file-02-[[OBJ02:[a-z0-9]+]].o{{(\\")?}}", +// CHECK-NEXT: "command_executable": "{{.*[\\/]}}swift{{c?(\.exe)?(\\")?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "{{.*[\\/]}}file-01.swift", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*[\\/]}}file-02.swift", +// CHECK-NEXT: "{{.*[\\/]}}file-03.swift", +// CHECK-NEXT: "{{.*[\\/]}}main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*[\\/]}}file-02-[[MODULE02:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*[\\/]}}file-02-[[SWIFTDOC02:[a-z0-9]+]].swiftdoc", +// CHECK: "-enable-fine-grained-dependencies", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*[\\/]}}file-02-[[OBJ02:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*[\\/]}}file-02.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-02-[[OBJ02]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-02-[[MODULE02]].swiftmodule" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftdoc", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-02-[[SWIFTDOC02]].swiftdoc" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftsourceinfo", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-02-[[MODULE02]].swiftsourceinfo" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*}}swift{{c?(\.exe)?(\\")?}} -frontend -c {{.*}}file-01.swift{{(\\")?}} {{.*}}file-02.swift{{(\\")?}} -primary-file {{.*}}file-03.swift{{(\\")?}} {{.*}}main.swift{{(\\")?}} -emit-module-path {{.*}}file-03-[[MODULE03:[a-z0-9]+]].swiftmodule{{(\\")?}} -emit-module-doc-path {{.*}}file-03-[[SWIFTDOC03:[a-z0-9]+]].swiftdoc{{(\\")?}} {{.*}} -enable-fine-grained-dependencies {{.*}} -module-name main -o {{.*}}file-03-[[OBJ03:[a-z0-9]+]].o{{(\\")?}}", +// CHECK-NEXT: "command_executable": "{{.*[\\/]}}swift{{c?(\.exe)?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "{{.*}}/file-01.swift", +// CHECK-NEXT: "{{.*}}/file-02.swift", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*}}/file-03.swift", +// CHECK-NEXT: "{{.*}}/main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*[\\/]}}file-03-[[MODULE03:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*[\\/]}}file-03-[[SWIFTDOC03:[a-z0-9]+]].swiftdoc", +// CHECK: "-enable-fine-grained-dependencies", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*[\\/]}}file-03-[[OBJ03:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*[\\/]}}file-03.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-03-[[OBJ03]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-03-[[MODULE03]].swiftmodule" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftdoc", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-03-[[SWIFTDOC03]].swiftdoc" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftsourceinfo", +// CHECK-NEXT: "path": "{{.*[\\/]}}file-03-[[MODULE03]].swiftsourceinfo" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "command": "{{.*[\\/]}}swift{{c?(\.exe)?(\\")?}} -frontend -c {{.*[\\/]}}file-01.swift{{(\\")?}} {{.*[\\/]}}file-02.swift{{(\\")?}} {{.*[\\/]}}file-03.swift{{(\\")?}} -primary-file {{.*[\\/]}}main.swift{{(\\")?}} -emit-module-path {{.*[\\/]}}main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule{{(\\")?}} -emit-module-doc-path {{.*[\\/]}}main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc{{(\\")?}} {{.*}} -enable-fine-grained-dependencies {{.*}} -module-name main -o {{.*[\\/]}}main-[[OBJMAIN:[a-z0-9]+]].o{{(\\")?}}", +// CHECK-NEXT: "command_executable": "{{.*[\\/]}}swift{{c?(\.exe)?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-c", +// CHECK-NEXT: "{{.*[\\/]}}file-01.swift", +// CHECK-NEXT: "{{.*[\\/]}}file-02.swift", +// CHECK-NEXT: "{{.*[\\/]}}file-03.swift", +// CHECK-NEXT: "-primary-file", +// CHECK-NEXT: "{{.*[\\/]}}main.swift", +// CHECK-NEXT: "-emit-module-path", +// CHECK-NEXT: "{{.*[\\/]}}main-[[MODULEMAIN:[a-z0-9]+]].swiftmodule", +// CHECK-NEXT: "-emit-module-doc-path", +// CHECK-NEXT: "{{.*[\\/]}}main-[[SWIFTDOCMAIN:[a-z0-9]+]].swiftdoc", +// CHECK: "-enable-fine-grained-dependencies", +// CHECK: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*[\\/]}}main-[[OBJMAIN:[a-z0-9]+]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*[\\/]}}main.swift" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "object", +// CHECK-NEXT: "path": "{{.*[\\/]}}main-[[OBJMAIN]].o" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "{{.*[\\/]}}main-[[MODULEMAIN]].swiftmodule" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftdoc", +// CHECK-NEXT: "path": "{{.*[\\/]}}main-[[SWIFTDOCMAIN]].swiftdoc" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftsourceinfo", +// CHECK-NEXT: "path": "{{.*[\\/]}}main-[[MODULEMAIN]].swiftsourceinfo" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "output": "Output placeholder\n", +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: }, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "output": "Output placeholder\n", +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: }, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "output": "Output placeholder\n", +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: }, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "compile", +// CHECK-NEXT: "pid": -{{[1-9][0-9]*}}, +// CHECK-NEXT: "output": "Output placeholder\n", +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: }, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "began", +// CHECK-NEXT: "name": "merge-module", +// CHECK-NEXT: "command": "{{.*[\\/]}}swift{{c?(\.exe)?(\\")?}} -frontend -merge-modules -emit-module {{.*[\\/]}}file-01-[[MODULE01]].swiftmodule{{(\\")?}} {{.*[\\/]}}file-02-[[MODULE02]].swiftmodule{{(\\")?}} {{.*[\\/]}}file-03-[[MODULE03]].swiftmodule{{(\\")?}} {{.*[\\/]}}main-[[MODULEMAIN]].swiftmodule{{(\\")?}} {{.*}} -emit-module-doc-path main.swiftdoc -emit-module-source-info-path main.swiftsourceinfo -module-name main -o main.swiftmodule", +// CHECK-NEXT: "command_executable": "{{.*[\\/]}}swift{{c?(\.exe)?}}", +// CHECK-NEXT: "command_arguments": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-merge-modules", +// CHECK-NEXT: "-emit-module", +// CHECK-NEXT: "{{.*[\\/]}}file-01-[[MODULE01]].swiftmodule", +// CHECK-NEXT: "{{.*[\\/]}}file-02-[[MODULE02]].swiftmodule", +// CHECK-NEXT: "{{.*[\\/]}}file-03-[[MODULE03]].swiftmodule", +// CHECK-NEXT: "{{.*[\\/]}}main-[[MODULEMAIN]].swiftmodule", +// CHECK: "-emit-module-doc-path", +// CHECK-NEXT: "main.swiftdoc", +// CHECK: "-emit-module-source-info-path", +// CHECK-NEXT: "main.swiftsourceinfo", +// CHECK-NEXT: "-module-name", +// CHECK-NEXT: "main", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "main.swiftmodule" +// CHECK-NEXT: ], +// CHECK-NEXT: "inputs": [ +// CHECK-NEXT: "{{.*[\\/]}}file-01-[[OBJ01]].o", +// CHECK-NEXT: "{{.*[\\/]}}file-02-[[OBJ02]].o", +// CHECK-NEXT: "{{.*[\\/]}}file-03-[[OBJ03]].o", +// CHECK-NEXT: "{{.*[\\/]}}main-[[OBJMAIN]].o" +// CHECK-NEXT: ], +// CHECK-NEXT: "outputs": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftmodule", +// CHECK-NEXT: "path": "main.swiftmodule" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftdoc", +// CHECK-NEXT: "path": "main.swiftdoc" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "type": "swiftsourceinfo", +// CHECK-NEXT: "path": "main.swiftsourceinfo" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "pid": {{[1-9][0-9]*}}, +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: {{[1-9][0-9]*}} +// CHECK-NEXT: { +// CHECK-NEXT: "kind": "finished", +// CHECK-NEXT: "name": "merge-module", +// CHECK-NEXT: "pid": {{[1-9][0-9]*}}, +// CHECK-NEXT: "output": "Output placeholder\n", +// CHECK-NEXT: "process": { +// CHECK-NEXT: "real_pid": {{[1-9][0-9]*}} +// CHECK-NEXT: }, +// CHECK-NEXT: "exit-status": 0 +// CHECK-NEXT: } diff --git a/test/Driver/batch_mode_parseable_output.swift b/test/Driver/batch_mode_parseable_output.swift index 607cf8daa7db6..dff6db72806f9 100644 --- a/test/Driver/batch_mode_parseable_output.swift +++ b/test/Driver/batch_mode_parseable_output.swift @@ -2,7 +2,7 @@ // RUN: touch %t/file-01.swift %t/file-02.swift %t/file-03.swift // RUN: echo 'public func main() {}' >%t/main.swift // -// RUN: %swiftc_driver -enable-batch-mode -parseable-output -driver-skip-execution -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift 2>&1 | %FileCheck %s +// RUN: %swiftc_driver -disable-fine-grained-dependencies -enable-batch-mode -parseable-output -driver-skip-execution -c -emit-module -module-name main -j 2 %t/file-01.swift %t/file-02.swift %t/file-03.swift %t/main.swift 2>&1 | %FileCheck %s // // // CHECK: {{[1-9][0-9]*}} diff --git a/test/Driver/emit-dependencies.swift b/test/Driver/emit-dependencies.swift new file mode 100644 index 0000000000000..89a2258cad39d --- /dev/null +++ b/test/Driver/emit-dependencies.swift @@ -0,0 +1,5 @@ +// RUN: %swiftc_driver_plain -emit-executable %s -o %t.out -emit-module -emit-module-path %t.swiftmodule -emit-objc-header-path %t.h -serialize-diagnostics -emit-dependencies -parseable-output -driver-skip-execution 2>&1 | %FileCheck %s --check-prefix=CHECK-OFF +// RUN: %swiftc_driver_plain -emit-executable %s -o %t.out -emit-module -emit-module-path %t.swiftmodule -emit-objc-header-path %t.h -serialize-diagnostics -emit-dependencies -parseable-output -track-system-dependencies -driver-skip-execution 2>&1 | %FileCheck %s --check-prefix=CHECK-ON + +// CHECK-OFF-NOT: -track-system-dependencies +// CHECK-ON: -track-system-dependencies diff --git a/test/Driver/filelists.swift b/test/Driver/filelists.swift index c6201c8b223fd..72301790d08c1 100644 --- a/test/Driver/filelists.swift +++ b/test/Driver/filelists.swift @@ -56,12 +56,12 @@ // CHECK-WMO-THREADED-NOT: Handled // RUN: mkdir -p %t/tmp-fail/ -// RUN: (cd %t && not env TMPDIR="%t/tmp-fail/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/fail.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) +// RUN: (cd %t && env TMPDIR="%t/tmp-fail/" not %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/fail.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) // RUN: not ls %t/tmp-fail/sources-* // RUN: not ls %t/tmp-fail/outputs-* // RUN: mkdir -p %t/tmp-crash/ -// RUN: (cd %t && not env TMPDIR="%t/tmp-crash/" %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/crash.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) +// RUN: (cd %t && env TMPDIR="%t/tmp-crash/" not %swiftc_driver_plain -driver-use-frontend-path %S/Inputs/crash.py -c ./a.swift ./b.swift ./c.swift -module-name main -target x86_64-apple-macosx10.9 -driver-filelist-threshold=0 -output-file-map=%S/Inputs/filelists/output.json -force-single-frontend-invocation -num-threads 1) // RUN: ls %t/tmp-crash/sources-* %t/tmp-crash/outputs-* diff --git a/test/Driver/linker-clang_rt.swift b/test/Driver/linker-clang_rt.swift index 6e1a37e03f6d2..7c8ad36805a54 100644 --- a/test/Driver/linker-clang_rt.swift +++ b/test/Driver/linker-clang_rt.swift @@ -14,6 +14,7 @@ // RUN: touch %t/lib/swift/clang/lib/darwin/libclang_rt.osx.a %t/lib/swift/clang/lib/darwin/libclang_rt.ios.a %t/lib/swift/clang/lib/darwin/libclang_rt.tvos.a %t/lib/swift/clang/lib/darwin/libclang_rt.watchos.a // RUN: %t/bin/swiftc -driver-print-jobs -target x86_64-apple-macosx10.9 %S/../Inputs/empty.swift | %FileCheck -check-prefix CHECK -check-prefix CHECK-MACOS %s +// RUN: %t/bin/swiftc -driver-print-jobs -target x86_64-apple-ios13.0-macabi %S/../Inputs/empty.swift | %FileCheck -check-prefix CHECK -check-prefix CHECK-MACCATALYST %s // RUN: %t/bin/swiftc -driver-print-jobs -target i386-apple-ios7 %S/../Inputs/empty.swift | %FileCheck -check-prefix CHECK -check-prefix CHECK-IOS %s // RUN: %t/bin/swiftc -driver-print-jobs -target x86_64-apple-ios7 %S/../Inputs/empty.swift | %FileCheck -check-prefix CHECK -check-prefix CHECK-IOS %s @@ -31,6 +32,7 @@ // CHECK: {{(bin/)?}}ld{{(.exe)?"? }} // CHECK-NO-RUNTIME-NOT: libclang_rt +// CHECK-MACCATALYST-SAME: {{[^ ]+(/|\\\\)lib(/|\\\\)swift(/|\\\\)clang(/|\\\\)lib(/|\\\\)darwin(/|\\\\)libclang_rt.osx.a}} // CHECK-MACOS-SAME: {{[^ ]+(/|\\\\)lib(/|\\\\)swift(/|\\\\)clang(/|\\\\)lib(/|\\\\)darwin(/|\\\\)libclang_rt.osx.a}} // CHECK-IOS-SAME: {{[^ ]+(/|\\\\)lib(/|\\\\)swift(/|\\\\)clang(/|\\\\)lib(/|\\\\)darwin(/|\\\\)libclang_rt.ios.a}} // CHECK-TVOS-SAME: {{[^ ]+(/|\\\\)lib(/|\\\\)swift(/|\\\\)clang(/|\\\\)lib(/|\\\\)darwin(/|\\\\)libclang_rt.tvos.a}} diff --git a/test/Driver/linker.swift b/test/Driver/linker.swift index f431df79d2b41..7b5f0c3425a8d 100644 --- a/test/Driver/linker.swift +++ b/test/Driver/linker.swift @@ -56,6 +56,8 @@ // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -Xclang-linker -foo -Xclang-linker foopath %s 2>&1 > %t.windows.txt // RUN: %FileCheck -check-prefix WINDOWS-clang-linker-order %s < %t.windows.txt +// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -Xclang-linker -flag -Xclang-linker arg %s 2>&1 | %FileCheck -check-prefix WASI-clang-linker-order %s + // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -g %s | %FileCheck -check-prefix DEBUG %s // RUN: %empty-directory(%t) @@ -70,14 +72,20 @@ // RUN: %FileCheck -check-prefix SIMPLE %s < %t.simple-macosx10.10.txt // RUN: %empty-directory(%t) -// RUN: touch %t/a.o +// RUN: echo "int dummy;" >%t/a.cpp +// RUN: cc -c %t/a.cpp -o %t/a.o // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -o linker 2>&1 | %FileCheck -check-prefix COMPILE_AND_LINK %s -// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -driver-filelist-threshold=0 -o linker 2>&1 | %FileCheck -check-prefix FILELIST %s +// RUN: %swiftc_driver -save-temps -driver-print-jobs -target x86_64-apple-macosx10.9 %s %t/a.o -driver-filelist-threshold=0 -o linker 2>&1 | tee %t/forFilelistCapture | %FileCheck -check-prefix FILELIST %s + +// Extract filelist name and check it out +// RUN: tail -1 %t/forFilelistCapture | sed 's/.*-filelist //' | sed 's/ .*//' >%t/filelistName +// RUN: %FileCheck -check-prefix FILELIST-CONTENTS %s < `cat %t/filelistName` // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_DARWIN %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_LINUX %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-cygnus -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_WINDOWS %s // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_WINDOWS %s +// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -emit-library %s -module-name LINKER | %FileCheck -check-prefix INFERRED_NAME_WASI %s // Here we specify an output file name using '-o'. For ease of writing these // tests, we happen to specify the same file name as is inferred in the @@ -317,6 +325,13 @@ // WINDOWS-clang-linker-order: -foo foopath // WINDOWS-clang-linker-order: -o {{.*}} +// WASI-clang-linker-order: swift +// WASI-clang-linker-order: -o [[OBJECTFILE:.*]] + +// WASI-clang-linker-order: clang{{"? }} +// WASI-clang-linker-order: -flag arg +// WASI-clang-linker-order: -o {{.*}} + // DEBUG: bin{{/|\\\\}}swift{{c?(\.EXE)?}} // DEBUG-NEXT: bin{{/|\\\\}}swift{{c?(\.EXE)?}} // DEBUG-NEXT: {{(bin/)?}}ld{{"? }} @@ -346,10 +361,10 @@ // FILELIST-NOT: .o{{"? }} // FILELIST: -filelist {{"?[^-]}} // FILELIST-NOT: .o{{"? }} -// FILELIST: /a.o{{"? }} -// FILELIST-NOT: .o{{"? }} // FILELIST: -o linker +// FILELIST-CONTENTS: /linker-{{.*}}.o +// FILELIST-CONTENTS: /a.o // INFERRED_NAME_DARWIN: bin{{/|\\\\}}swift{{c?(\.EXE)?}} // INFERRED_NAME_DARWIN: -module-name LINKER @@ -357,7 +372,7 @@ // INFERRED_NAME_DARWIN: -o libLINKER.dylib // INFERRED_NAME_LINUX: -o libLINKER.so // INFERRED_NAME_WINDOWS: -o LINKER.dll - +// INFERRED_NAME_WASI: -o libLINKER.so // Test ld detection. We use hard links to make sure // the Swift driver really thinks it's been moved. diff --git a/test/Driver/loaded_module_trace_nocrash.swift b/test/Driver/loaded_module_trace_nocrash.swift new file mode 100644 index 0000000000000..70960b5bbcdcb --- /dev/null +++ b/test/Driver/loaded_module_trace_nocrash.swift @@ -0,0 +1,22 @@ +// UNSUPPORTED: -windows-msvc + +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/Mods/Foo.swiftmodule +// RUN: mkdir -p %t/Mods/Bar.swiftmodule + +// RUN: %target-swift-frontend %s -DFoo -emit-module -o %t/Mods/Foo.swiftmodule/armv7s-apple-ios.swiftmodule -emit-module-interface-path %t/Mods/Foo.swiftmodule/armv7s-apple-ios.swiftinterface -target armv7s-apple-ios10 -module-name Foo -enable-library-evolution -parse-stdlib +// RUN: %target-swift-frontend %s -DFoo -emit-module -o %t/Mods/Foo.swiftmodule/arm.swiftmodule -emit-module-interface-path %t/Mods/Foo.swiftmodule/arm.swiftinterface -target arm-apple-ios10 -module-name Foo -enable-library-evolution -parse-stdlib +// RUN: %target-swift-frontend %s -DBar -typecheck -emit-module-interface-path %t/Mods/Bar.swiftmodule/arm.swiftinterface -I %t/Mods -target arm-apple-ios10 -module-name Bar -enable-library-evolution -parse-stdlib +// RUN: %target-swift-frontend %s -DFooBar -typecheck -emit-loaded-module-trace-path - -I %t/Mods -target armv7s-apple-ios10 -parse-stdlib + +#if Foo +#endif + +#if Bar +import Foo +#endif + +#if FooBar +import Foo +import Bar +#endif diff --git a/test/Driver/loaded_module_trace_swiftinterface.swift b/test/Driver/loaded_module_trace_swiftinterface.swift index 860971171e671..a92a34e974d0c 100644 --- a/test/Driver/loaded_module_trace_swiftinterface.swift +++ b/test/Driver/loaded_module_trace_swiftinterface.swift @@ -1,9 +1,13 @@ +// UNSUPPORTED: -windows-msvc + +// 1) If there is no swiftmodule, use the swiftinterface +// // RUN: %empty-directory(%t) -// RUN: %target-build-swift -enable-library-evolution -swift-version 5 -emit-module-interface-path %t/Module.swiftinterface -module-name Module %S/Inputs/loaded_module_trace_empty.swift -// RUN: %target-build-swift -swift-version 5 -emit-module -module-name Module2 %S/Inputs/loaded_module_trace_imports_module.swift -o %t/Module2.swiftmodule -I %t -// RUN: %target-build-swift %s -emit-loaded-module-trace -o %t/loaded_module_trace_swiftinterface -I %t +// RUN: %target-build-swift -swift-version 5 -enable-library-evolution -module-name Module %S/Inputs/loaded_module_trace_empty.swift -emit-module-interface-path %t/Module.swiftinterface +// RUN: %target-build-swift -swift-version 5 -I %t -module-name Module2 %S/Inputs/loaded_module_trace_imports_module.swift -emit-module -o %t/Module2.swiftmodule +// RUN: %target-build-swift -I %t %s -emit-loaded-module-trace -o %t/loaded_module_trace_swiftinterface // RUN: %FileCheck -check-prefix=CHECK %s < %t/loaded_module_trace_swiftinterface.trace.json - +// // CHECK: { // CHECK: "version":2 // CHECK: "name":"loaded_module_trace_swiftinterface" @@ -13,6 +17,7 @@ // CHECK-DAG: "{{[^"]*\\[/\\]}}Swift.swiftmodule{{(\\[/\\][^"]+[.]swiftmodule)?}}" // CHECK-DAG: "{{[^"]*\\[/\\]}}SwiftOnoneSupport.swiftmodule{{(\\[/\\][^"]+[.]swiftmodule)?}}" // CHECK-DAG: "{{[^"]*\\[/\\]}}Module.swiftinterface" +// CHECK-NOT: Module.swiftmodule // CHECK: ] // CHECK: "swiftmodulesDetailedInfo":[ // CHECK-DAG: {"name":"Module2","path":"{{[^"]*\\[/\\]}}Module2.swiftmodule","isImportedDirectly":true,"supportsLibraryEvolution":false} @@ -22,4 +27,42 @@ // CHECK: ] // CHECK: } + +// 2) If there is a swiftmodule next to the swiftinterface, use that instead. +// +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -swift-version 5 -enable-library-evolution -module-name Module %S/Inputs/loaded_module_trace_empty.swift -emit-module-interface-path %t/Module.swiftinterface -emit-module -o %t/Module.swiftmodule +// RUN: %target-swift-frontend -swift-version 5 -I %t -module-name Module2 %S/Inputs/loaded_module_trace_imports_module.swift -emit-module -o %t/Module2.swiftmodule +// RUN: %target-build-swift -I %t %s -emit-loaded-module-trace -o %t/loaded_module_trace_swiftinterface +// RUN: %FileCheck -check-prefix=ADJMODULE %s < %t/loaded_module_trace_swiftinterface.trace.json +// +// ADJMODULE: Module.swiftmodule +// ADJMODULE-NOT: Module.swiftinterface + + +// 3) If there is a swiftmodule in the (non-prebuilt) module cache path pointing to the swiftinterface, use the swiftinterface. +// +// RUN: %empty-directory(%t) +// RUN: mkdir %t/MCP +// RUN: %target-build-swift -swift-version 5 -enable-library-evolution -module-cache-path %t/MCP -module-name Module %S/Inputs/loaded_module_trace_empty.swift -emit-module-interface-path %t/Module.swiftinterface -emit-module -o %t/MCP/Module.swiftmodule +// RUN: %target-swift-frontend -swift-version 5 -I %t -module-cache-path %t/MCP -module-name Module2 %S/Inputs/loaded_module_trace_imports_module.swift -emit-module -o %t/Module2.swiftmodule +// RUN: %target-build-swift -I %t -module-cache-path %t/MCP %s -emit-loaded-module-trace -o %t/loaded_module_trace_swiftinterface +// RUN: %FileCheck -check-prefix=CACHEDMODULE %s < %t/loaded_module_trace_swiftinterface.trace.json +// +// CACHEDMODULE: Module.swiftinterface +// CACHEDMODULE-NOT: Module.swiftmodule + + +// 4) If there is a swiftmodule in the prebuilt module cache path pointing to the swiftinterface, use the swiftinterface. +// +// RUN: %empty-directory(%t) +// RUN: mkdir %t/MCP %t/PB %t/Main +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -prebuilt-module-cache-path %t/PB -module-cache-path %/MCP -module-name Module %S/Inputs/loaded_module_trace_empty.swift -emit-module -o %t/PB/Module.swiftmodule -emit-module-interface-path %t/Main/Module.swiftinterface +// RUN: %target-swift-frontend -swift-version 5 -I %t/Main -prebuilt-module-cache-path %t/PB -module-cache-path %t/MCP -module-name Module2 %S/Inputs/loaded_module_trace_imports_module.swift -emit-module -o %t/Main/Module2.swiftmodule +// RUN: %target-build-swift -I %t/Main -module-cache-path %t/MCP %s -emit-loaded-module-trace -o %t/loaded_module_trace_swiftinterface +// RUN: %FileCheck -check-prefix=PREBUILTMODULE %s < %t/loaded_module_trace_swiftinterface.trace.json +// +// PREBUILTMODULE: Module.swiftinterface +// PREBUILTMODULE-NOT: Module.swiftmodule + import Module2 diff --git a/test/Driver/lock_interface.swift b/test/Driver/lock_interface.swift new file mode 100644 index 0000000000000..5fb992c6d8f62 --- /dev/null +++ b/test/Driver/lock_interface.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: echo 'public func foo() {}' > %t/Foo.swift +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %t/Foo.swift -enable-library-evolution +// RUN: touch %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift +// RUN: echo 'import Foo' > %t/file-01.swift +// RUN: echo 'import Foo' > %t/file-02.swift +// RUN: echo 'import Foo' > %t/file-03.swift +// RUN: %target-swiftc_driver -j20 %t/main.swift %t/file-01.swift %t/file-02.swift %t/file-03.swift -I %t -Xfrontend -Rmodule-interface-rebuild &> %t/result.txt +// RUN: %FileCheck %s -check-prefix=CHECK-REBUILD < %t/result.txt + +// Ensure we only build Foo module once from the interface +// CHECK-REBUILD: rebuilding module 'Foo' from interface +// CHECK-REBUILD-NOT: rebuilding module 'Foo' from interface diff --git a/test/Driver/macabi-environment.swift b/test/Driver/macabi-environment.swift new file mode 100644 index 0000000000000..516d90fba6140 --- /dev/null +++ b/test/Driver/macabi-environment.swift @@ -0,0 +1,83 @@ +// Tests to check that the driver finds standard library in the macabi environment. + +// UNSUPPORTED: windows + +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-ios13.0-macabi -sdk %S/../Inputs/clang-importer-sdk %s | %FileCheck -check-prefix=IOS13-MACABI %s +// IOS13-MACABI: bin/swift +// IOS13-MACABI: -target x86_64-apple-ios13.0-macabi + +// IOS13-MACABI: bin/ld +// IOS13-MACABI-DAG: -L [[MACCATALYST_STDLIB_PATH:[^ ]+/lib/swift/maccatalyst]] +// IOS13-MACABI-DAG: -L [[MACOSX_STDLIB_PATH:[^ ]+/lib/swift/macosx]] +// IOS13-MACABI-DAG: -L [[MACCATALYST_SDK_STDLIB_PATH:[^ ]+/clang-importer-sdk/System/iOSSupport/usr/lib/swift]] +// IOS13-MACABI-DAG: -L [[MACOSX_SDK_STDLIB_PATH:[^ ]+/clang-importer-sdk/usr/lib/swift]] +// IOS13-MACABI-DAG: -rpath [[MACCATALYST_STDLIB_PATH]] +// IOS13-MACABI-DAG: -rpath [[MACOSX_STDLIB_PATH]] +// IOS13-MACABI-DAG: -rpath [[MACCATALYST_SDK_STDLIB_PATH]] +// IOS13-MACABI-DAG: -rpath [[MACOSX_SDK_STDLIB_PATH]] +// IOS13-MACABI-DAG: -maccatalyst_version_min 13.0.0 + + +// Test using target-variant to build zippered outputs + +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi %s | %FileCheck -check-prefix=ZIPPERED-VARIANT-OBJECT %s +// ZIPPERED-VARIANT-OBJECT: bin/swift +// ZIPPERED-VARIANT-OBJECT: -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi + +// RUN: %swiftc_driver -driver-print-jobs -emit-library -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi -module-name foo %s | %FileCheck -check-prefix=ZIPPERED-VARIANT-LIBRARY %s +// ZIPPERED-VARIANT-LIBRARY: bin/swift +// ZIPPERED-VARIANT-LIBRARY: -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi + +// ZIPPERED-VARIANT-LIBRARY: bin/ld +// ZIPPERED-VARIANT-LIBRARY: -macosx_version_min 10.14.0 -maccatalyst_version_min 13.0.0 + +// Make sure we pass the -target-variant when creating the pre-compiled header. +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi -enable-bridging-pch -import-objc-header %S/Inputs/bridging-header.h %s | %FileCheck -check-prefix=ZIPPERED-VARIANT-PCH %s +// ZIPPERED-VARIANT-PCH: bin/swift +// ZIPPERED-VARIANT-PCH: -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi +// ZIPPERED_VARIANT-PCH -emit-pch +// ZIPPERED-VARIANT-PCH: bin/swift +// ZIPPERED-VARIANT-PCH: -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0-macabi +// ZIPPERED-VARIANT-PCH: bin/ld +// ZIPPERED-VARIANT-PCH: -macosx_version_min 10.14.0 -maccatalyst_version_min 13.0.0 + +// Test using 'reverse' target-variant to build zippered outputs when the primary +// target is ios-macabi + +// RUN: %swiftc_driver -driver-print-jobs -c -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 %s | %FileCheck -check-prefix=REVERSE-ZIPPERED-VARIANT-OBJECT %s +// REVERSE-ZIPPERED-VARIANT-OBJECT: bin/swift +// REVERSE-ZIPPERED-VARIANT-OBJECT: -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 + +// RUN: %swiftc_driver -driver-print-jobs -emit-library -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 -module-name foo %s | %FileCheck -check-prefix=REVERSE-ZIPPERED-VARIANT-LIBRARY %s +// REVERSE-ZIPPERED-VARIANT-LIBRARY: bin/swift +// REVERSE-ZIPPERED-VARIANT-LIBRARY: -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 + +// REVERSE-ZIPPERED-VARIANT-LIBRARY: bin/ld +// REVERSE-ZIPPERED-VARIANT-LIBRARY: -maccatalyst_version_min 13.0.0 -macosx_version_min 10.14.0 + +// Make sure we pass the -target-variant when creating the pre-compiled header. +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 -enable-bridging-pch -import-objc-header %S/Inputs/bridging-header.h %s | %FileCheck -check-prefix=REVERSE-ZIPPERED-VARIANT-PCH %s +// REVERSE-ZIPPERED-VARIANT-PCH: bin/swift +// REVERSE-ZIPPERED-VARIANT-PCH: -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 +// REVERSE-ZIPPERED_VARIANT-PCH -emit-pch +// REVERSE-ZIPPERED-VARIANT-PCH: bin/swift +// REVERSE-ZIPPERED-VARIANT-PCH: -target x86_64-apple-ios13.0-macabi -target-variant x86_64-apple-macosx10.14 +// REVERSE-ZIPPERED-VARIANT-PCH: bin/ld +// REVERSE-ZIPPERED-VARIANT-PCH: -maccatalyst_version_min 13.0.0 -macosx_version_min 10.14.0 + +// RUN: not %swiftc_driver -target x86_64-apple-macosx10.14 -target-variant x86_64-apple-ios13.0 %s 2>&1 | %FileCheck --check-prefix=UNSUPPORTED-TARGET-VARIANT %s +// RUN: not %swiftc_driver -target x86_64-apple-ios13.0 -target-variant x86_64-apple-macosx10.14 %s 2>&1 | %FileCheck --check-prefix=UNSUPPORTED-TARGET %s + +// UNSUPPORTED-TARGET-VARIANT: error: unsupported '-target-variant' value {{.*}}; use 'ios-macabi' instead +// UNSUPPORTED-TARGET: error: unsupported '-target' value {{.*}}; use 'ios-macabi' instead + +// When compiling for iOS, pass iphoneos_version_min to the linker, not maccatalyst_version_min. + +// RUN: %swiftc_driver -driver-print-jobs -target arm64-apple-ios13.0 -sdk %S/../Inputs/clang-importer-sdk %s | %FileCheck -check-prefix=IOS13-NO-MACABI -implicit-check-not=maccatalyst_version_min %s +// IOS13-NO-MACABI: bin/swift +// IOS13-NO-MACABI: -target arm64-apple-ios13.0 + +// IOS13-NO-MACABI: bin/ld +// IOS13-NO-MACABI-DAG: -L {{[^ ]+/lib/swift/iphoneos}} +// IOS13-NO-MACABI-DAG: -L {{[^ ]+/clang-importer-sdk/usr/lib/swift}} +// IOS13-NO-MACABI-DAG: -iphoneos_version_min 13.0.0 diff --git a/test/Driver/multi-threaded.swift b/test/Driver/multi-threaded.swift index 5ad449bae73e4..84a15e5c6d5ab 100644 --- a/test/Driver/multi-threaded.swift +++ b/test/Driver/multi-threaded.swift @@ -5,7 +5,8 @@ // RUN: %target-swiftc_driver -driver-print-jobs -module-name=ThisModule -wmo -num-threads 4 %S/Inputs/main.swift %s -c | %FileCheck -check-prefix=OBJECT %s // RUN: cd %t && %target-swiftc_driver -parseable-output -module-name=ThisModule -wmo -num-threads 4 %S/Inputs/main.swift %s -c 2> %t/parseable-output // RUN: cat %t/parseable-output | %FileCheck -check-prefix=PARSEABLE %s -// RUN: cd %t && env TMPDIR=/tmp %swiftc_driver -driver-print-jobs -module-name=ThisModule -wmo -num-threads 4 %S/Inputs/main.swift %s -o a.out | %FileCheck -check-prefix=EXEC %s +// RUN: %empty-directory(%t/tmp) +// RUN: cd %t && env TMPDIR=%t/tmp/ %swiftc_driver -driver-print-jobs -module-name=ThisModule -wmo -num-threads 4 %S/Inputs/main.swift %s -o a.out | %FileCheck -check-prefix=EXEC %s // RUN: echo "{\"%/s\": {\"llvm-bc\": \"%/t/multi-threaded.bc\", \"object\": \"%/t/multi-threaded.o\"}, \"%/S/Inputs/main.swift\": {\"llvm-bc\": \"%/t/main.bc\", \"object\": \"%/t/main.o\"}}" > %t/ofmo.json // RUN: %target-swiftc_driver -module-name=ThisModule -wmo -num-threads 4 %S/Inputs/main.swift %s -emit-dependencies -output-file-map %t/ofmo.json -c // RUN: cat %t/*.d | %FileCheck -check-prefix=DEPENDENCIES %s @@ -54,9 +55,9 @@ // EXEC: -frontend // EXEC-DAG: -num-threads 4 // EXEC-DAG: {{[^ ]*[/\\]}}Inputs{{/|\\\\}}main.swift{{"?}} {{[^ ]*[/\\]}}multi-threaded.swift -// EXEC-DAG: -o {{.*te?mp.*[/\\]}}main{{[^ ]*}}.o{{"?}} -o {{.*te?mp.*[/\\]}}multi-threaded{{[^ ]*}}.o +// EXEC-DAG: -o {{.*[tT]e?mp.*[/\\]}}main{{[^ ]*}}.o{{"?}} -o {{.*[tT]e?mp.*[/\\]}}multi-threaded{{[^ ]*}}.o // EXEC: {{ld|clang}} -// EXEC: {{.*te?mp.*[/\\]}}main{{[^ ]*}}.o{{"?}} {{.*te?mp.*[/\\]}}multi-threaded{{[^ ]*}}.o +// EXEC: {{.*[tT]e?mp.*[/\\]}}main{{[^ ]*}}.o{{"?}} {{.*[tT]e?mp.*[/\\]}}multi-threaded{{[^ ]*}}.o // DEPENDENCIES-DAG: {{.*}}multi-threaded.o : {{.*[/\\]}}multi-threaded.swift {{.*[/\\]}}Inputs{{[/\\]}}main.swift // DEPENDENCIES-DAG: {{.*}}main.o : {{.*[/\\]}}multi-threaded.swift {{.*[/\\]}}Inputs{{[/\\]}}main.swift diff --git a/test/Driver/negating_WMO.swift b/test/Driver/negating_WMO.swift new file mode 100644 index 0000000000000..6fc30ae91c893 --- /dev/null +++ b/test/Driver/negating_WMO.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) + +// RUN: %swiftc_driver -whole-module-optimization %S/../Inputs/empty.swift -### 2>&1 | %FileCheck -check-prefix WMO %s +// WMO-NOT: -primary-file +// RUN: %swiftc_driver -whole-module-optimization -no-whole-module-optimization %S/../Inputs/empty.swift -### 2>&1 | %FileCheck -check-prefix NO-WMO %s +// NO-WMO: -primary-file + +// RUN: %swiftc_driver -enable-batch-mode -whole-module-optimization -no-whole-module-optimization %S/../Inputs/empty.swift -### 2>&1 | %FileCheck -check-prefix BATCH %s +// BATCH: -primary-file +// BATCH-NOT: warning: ignoring '-enable-batch-mode' because '-whole-module-optimization' was also specified diff --git a/test/Driver/print_target_info.swift b/test/Driver/print_target_info.swift new file mode 100644 index 0000000000000..28a65dea78d06 --- /dev/null +++ b/test/Driver/print_target_info.swift @@ -0,0 +1,28 @@ +// RUN: %swift_driver -print-target-info -target arm64-apple-ios12.0 | %FileCheck -check-prefix CHECK-IOS %s +// RUN: %target-swift-frontend -print-target-info -target arm64-apple-ios12.0 | %FileCheck -check-prefix CHECK-IOS %s + +// RUN: %swift_driver -print-target-info -target x86_64-unknown-linux | %FileCheck -check-prefix CHECK-LINUX %s +// RUN: %target-swift-frontend -print-target-info -target x86_64-unknown-linux | %FileCheck -check-prefix CHECK-LINUX %s + +// CHECK-IOS: "target": { +// CHECK-IOS: "triple": "arm64-apple-ios12.0", +// CHECK-IOS: "unversionedTriple": "arm64-apple-ios", +// CHECK-IOS: "moduleTriple": "arm64-apple-ios", +// CHECK-IOS: "swiftRuntimeCompatibilityVersion": "5.0", +// CHECK-IOS: "librariesRequireRPath": true +// CHECK-IOS: } + +// CHECK-IOS: "paths": { +// CHECK-IOS: "runtimeLibraryPaths": [ +// CHECK-IOS: ], +// CHECK-IOS: "runtimeLibraryImportPaths": [ +// CHECK-IOS: ], +// CHECK-IOS: "runtimeResourcePath": "{{.*}}lib{{(/|\\\\)}}swift" +// CHECK-IOS: } + + +// CHECK-LINUX: "target": { +// CHECK-LINUX: "triple": "x86_64-unknown-linux", +// CHECK-LINUX: "moduleTriple": "x86_64-unknown-linux", +// CHECK-LINUX: "librariesRequireRPath": false +// CHECK-LINUX: } diff --git a/test/Driver/print_target_info_macos.swift b/test/Driver/print_target_info_macos.swift new file mode 100644 index 0000000000000..14074424cf484 --- /dev/null +++ b/test/Driver/print_target_info_macos.swift @@ -0,0 +1,11 @@ +// Test that -print-target-info infers the host OS when that host OS is macOS +// +// We could duplicate this test for other host platforms. + +// RUN: %swift_driver -print-target-info | %FileCheck %s +// RUN: %target-swift-frontend -print-target-info | %FileCheck %s + +// REQUIRES: OS=macosx + +// CHECK: "triple": "x86_64-apple-macosx10 +// CHECK: "unversionedTriple": "x86_64-apple-macosx" diff --git a/test/Driver/profiling.swift b/test/Driver/profiling.swift index 75dab36e7323b..f70ec0fe63fae 100644 --- a/test/Driver/profiling.swift +++ b/test/Driver/profiling.swift @@ -20,6 +20,8 @@ // RUN: %swiftc_driver -driver-print-jobs -profile-generate -target x86_64-unknown-linux-gnu %s | %FileCheck -check-prefix=CHECK -check-prefix=LINUX %s // RUN: %swiftc_driver -driver-print-jobs -profile-generate -target x86_64-unknown-windows-msvc %s | %FileCheck -check-prefix=CHECK -check-prefix=WINDOWS %s +// RUN: %swiftc_driver -driver-print-jobs -profile-generate -target wasm32-unknown-wasi %s | %FileCheck -check-prefix CHECK -check-prefix WASI %s + // CHECK: swift // CHECK: -profile-generate @@ -52,6 +54,10 @@ // WINDOWS: lib{{(\\\\|/)}}swift{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}windows{{(\\\\|/)}}clang_rt.profile-x86_64.lib // WINDOWS: -u__llvm_profile_runtime +// WASI: clang{{(\.exe)?"? }} +// WASI: lib{{(\\\\|/)}}swift{{(\\\\|/)}}clang{{(\\\\|/)}}lib{{(\\\\|/)}}wasi{{(\\\\|/)}}libclang_rt.profile-wasm32.a +// WASI: -u__llvm_profile_runtime + // RUN: not %swiftc_driver -driver-print-jobs -profile-generate -profile-use=/dev/null %s 2>&1 | %FileCheck -check-prefix=MIX_GEN_USE %s // MIX_GEN_USE: conflicting options '-profile-generate' and '-profile-use' diff --git a/test/Driver/sanitize_recover.swift b/test/Driver/sanitize_recover.swift new file mode 100644 index 0000000000000..ca7ca240acb41 --- /dev/null +++ b/test/Driver/sanitize_recover.swift @@ -0,0 +1,19 @@ +// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=foo %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_INVALID_ARG %s +// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=thread %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_UNSUPPORTED_ARG %s +// RUN: %swiftc_driver -v -sanitize-recover=address %s -o %t 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION %s +// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITH_RECOVER %s +// RUN: %swiftc_driver -driver-print-jobs -sanitize=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITHOUT_RECOVER --implicit-check-not='-sanitize-recover=address' %s +// REQUIRES: asan_runtime + +// SAN_RECOVER_INVALID_ARG: unsupported argument 'foo' to option '-sanitize-recover=' +// SAN_RECOVER_UNSUPPORTED_ARG: unsupported argument 'thread' to option '-sanitize-recover=' + +// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer +// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION-NOT: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer + +// ASAN_WITH_RECOVER: swift +// ASAN_WITH_RECOVER-DAG: -sanitize=address +// ASAN_WITH_RECOVER-DAG: -sanitize-recover=address + +// ASAN_WITHOUT_RECOVER: swift +// ASAN_WITHOUT_RECOVER: -sanitize=address diff --git a/test/Driver/sanitize_scudo.swift b/test/Driver/sanitize_scudo.swift new file mode 100644 index 0000000000000..6f4e66f2612b3 --- /dev/null +++ b/test/Driver/sanitize_scudo.swift @@ -0,0 +1,40 @@ +// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-unknown-linux-gnu %s | %FileCheck -check-prefix=SCUDO_LINUX %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target arm64-apple-ios7.1 %s 2>&1 | %FileCheck -check-prefix=SCUDO_IOS %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target arm64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_tvOS %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target armv7k-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_watchOS %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target i386-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_watchOS_SIM %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target i386-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=SCUDO_OSX_32 %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-ios7.1 %s 2>&1 | %FileCheck -check-prefix=SCUDO_IOSSIM %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=SCUDO_OSX_64 %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=SCUDO_tvOS_SIM %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=SCUDO_WINDOWS %s + +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,address -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_ASAN %s +// RUN: not %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,thread -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_TSAN %s +// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=scudo,undefined -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_UBSAN_LINUX %s + + +/* + * Make sure we don't accidentally add the sanitizer library path when building libraries or modules + */ +// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -emit-library -sanitize=scudo -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=SCUDO_LIBRARY_LINUX %s + +// SCUDO_LINUX: bin{{/|\\\\}}clang +// SCUDO_LINUX-SAME: -pie +// SCUDO_LINUX-SAME: -fsanitize=scudo +// SCUDO_OSX_32: unsupported option '-sanitize=scudo' for target 'i386-apple-macosx10.9' +// SCUDO_OSX_64: unsupported option '-sanitize=scudo' for target 'x86_64-apple-macosx10.9' +// SCUDO_IOSSIM: unsupported option '-sanitize=scudo' for target 'x86_64-apple-ios7.1' +// SCUDO_IOS: unsupported option '-sanitize=scudo' for target 'arm64-apple-ios7.1' +// SCUDO_tvOS_SIM: unsupported option '-sanitize=scudo' for target 'x86_64-apple-tvos9.0' +// SCUDO_tvOS: unsupported option '-sanitize=scudo' for target 'arm64-apple-tvos9.0' +// SCUDO_watchOS_SIM: unsupported option '-sanitize=scudo' for target 'i386-apple-watchos2.0' +// SCUDO_watchOS: unsupported option '-sanitize=scudo' for target 'armv7k-apple-watchos2.0' +// SCUDO_WINDOWS: unsupported option '-sanitize=scudo' for target 'x86_64-unknown-windows-msvc' + +// SCUDO_ASAN: argument '-sanitize=scudo' is not allowed with '-sanitize=address' +// SCUDO_TSAN: argument '-sanitize=scudo' is not allowed with '-sanitize=thread' +// SCUDO_UBSAN_LINUX: -fsanitize=undefined,scudo +// SCUDO_LIBRARY_LINUX: bin{{/|\\\\}}clang +// SCUDO_LIBRARY_LINUX-NOT: -fsanitize=scudo + diff --git a/test/Driver/sdk.swift b/test/Driver/sdk.swift index 2accb502e4319..98cc05c55160c 100644 --- a/test/Driver/sdk.swift +++ b/test/Driver/sdk.swift @@ -2,6 +2,7 @@ // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix LINUX // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-freebsd -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix FREEBSD // RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix WINDOWS +// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -g -sdk %S/../Inputs/clang-importer-sdk %s 2>&1 | %FileCheck %s --check-prefix WASI // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-apple-macosx10.9 -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix OSX // RUN: env SDKROOT=%S/../Inputs/clang-importer-sdk %swiftc_driver_plain -target x86_64-unknown-linux-gnu -g -driver-print-jobs %s 2>&1 | %FileCheck %s --check-prefix LINUX @@ -44,6 +45,14 @@ // WINDOWS: {{.*}}Inputs/clang-importer-sdk{{.*}}swiftrt.o // WINDOWS: {{-I}} {{.*}}/Inputs/clang-importer-sdk +// WASI-NOT: warning: no such SDK: +// WASI: bin{{/|\\\\}}swift +// WASI: Driver{{/|\\\\}}sdk.swift +// WASI: -sdk {{.*}}/Inputs/clang-importer-sdk +// WASI-NEXT: bin{{/|\\\\}}swift +// WASI: -sdk {{.*}}/Inputs/clang-importer-sdk +// WASI: {{-syslibroot|--sysroot}} {{.*}}/Inputs/clang-importer-sdk + // RUN: %swift_driver -driver-print-jobs -repl -sdk %S/Inputs/nonexistent-sdk 2>&1 | %FileCheck %s --check-prefix=SDKWARNING // RUN: %swift_driver -driver-print-jobs -sdk %S/Inputs/nonexistent-sdk 2>&1 | %FileCheck %s --check-prefix=SDKWARNING // RUN: env SDKROOT=%S/Inputs/nonexistent-sdk %swift_driver_plain -driver-print-jobs -repl 2>&1 | %FileCheck %s --check-prefix=SDKWARNING diff --git a/test/FixCode/fixits-apply-objc.swift b/test/FixCode/fixits-apply-objc.swift index 2a3f2920e1042..505bce01daccb 100644 --- a/test/FixCode/fixits-apply-objc.swift +++ b/test/FixCode/fixits-apply-objc.swift @@ -4,6 +4,95 @@ import ObjectiveC // REQUIRES: objc_interop +@objc class Selectors { + func takeSel(_: Selector) {} + @objc func mySel() {} + func test() { + takeSel("mySel") + takeSel(Selector("mySel")) + } +} + +@objc class OtherClass { + func test(s: Selectors) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + } +} + +@objc class Base { + @objc func baseSel() {} +} + +@objc class Outer { + func takeSel(_: Selector) {} + @objc func outerSel() {} + + @objc class Inner: Base { + func takeSel(_: Selector) {} + + @objc func innerSel() {} + + func test(s: Selectors, o: Outer) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + takeSel("innerSel") + takeSel(Selector("innerSel")) + + takeSel("baseSel") + takeSel(Selector("baseSel")) + + o.takeSel("outerSel") + o.takeSel(Selector("outerSel")) + } + } + + func test(s: Selectors, i: Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + takeSel("outerSel") + takeSel(Selector("outerSel")) + } +} + +extension Outer { + func test2(s: Selectors, i: Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + takeSel("outerSel") + takeSel(Selector("outerSel")) + } +} + +func freeTest(s: Selectors, o: Outer, i: Outer.Inner) { + s.takeSel("mySel") + s.takeSel(Selector("mySel")) + + i.takeSel("innerSel") + i.takeSel(Selector("innerSel")) + + i.takeSel("baseSel") + i.takeSel(Selector("baseSel")) + + o.takeSel("outerSel") + o.takeSel(Selector("outerSel")) +} + func foo(an : Any) { let a1 : AnyObject a1 = an diff --git a/test/FixCode/fixits-apply-objc.swift.result b/test/FixCode/fixits-apply-objc.swift.result index 607a3bd8723fa..a3c0aca923034 100644 --- a/test/FixCode/fixits-apply-objc.swift.result +++ b/test/FixCode/fixits-apply-objc.swift.result @@ -4,6 +4,95 @@ import ObjectiveC // REQUIRES: objc_interop +@objc class Selectors { + func takeSel(_: Selector) {} + @objc func mySel() {} + func test() { + takeSel(#selector(self.mySel)) + takeSel(#selector(self.mySel)) + } +} + +@objc class OtherClass { + func test(s: Selectors) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + } +} + +@objc class Base { + @objc func baseSel() {} +} + +@objc class Outer { + func takeSel(_: Selector) {} + @objc func outerSel() {} + + @objc class Inner: Base { + func takeSel(_: Selector) {} + + @objc func innerSel() {} + + func test(s: Selectors, o: Outer) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + takeSel(#selector(self.innerSel)) + takeSel(#selector(self.innerSel)) + + takeSel(#selector(self.baseSel)) + takeSel(#selector(self.baseSel)) + + o.takeSel(#selector(Outer.outerSel)) + o.takeSel(#selector(Outer.outerSel)) + } + } + + func test(s: Selectors, i: Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + takeSel(#selector(self.outerSel)) + takeSel(#selector(self.outerSel)) + } +} + +extension Outer { + func test2(s: Selectors, i: Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + takeSel(#selector(self.outerSel)) + takeSel(#selector(self.outerSel)) + } +} + +func freeTest(s: Selectors, o: Outer, i: Outer.Inner) { + s.takeSel(#selector(Selectors.mySel)) + s.takeSel(#selector(Selectors.mySel)) + + i.takeSel(#selector(Inner.innerSel)) + i.takeSel(#selector(Inner.innerSel)) + + i.takeSel(#selector(Base.baseSel)) + i.takeSel(#selector(Base.baseSel)) + + o.takeSel(#selector(Outer.outerSel)) + o.takeSel(#selector(Outer.outerSel)) +} + func foo(an : Any) { let a1 : AnyObject a1 = an as AnyObject diff --git a/test/Frontend/Inputs/type-fingerprint/a.swift b/test/Frontend/Inputs/type-fingerprint/a.swift new file mode 100644 index 0000000000000..19fbcc61d8ab3 --- /dev/null +++ b/test/Frontend/Inputs/type-fingerprint/a.swift @@ -0,0 +1,3 @@ +func bar() { + B2() +} diff --git a/test/Frontend/Inputs/type-fingerprint/b0.swift b/test/Frontend/Inputs/type-fingerprint/b0.swift new file mode 100644 index 0000000000000..f4beb31af40a1 --- /dev/null +++ b/test/Frontend/Inputs/type-fingerprint/b0.swift @@ -0,0 +1,6 @@ +struct B1 { + var a: Int = 1 +} +struct B2 { + var b: Int = 2 +} diff --git a/test/Frontend/Inputs/type-fingerprint/b1.swift b/test/Frontend/Inputs/type-fingerprint/b1.swift new file mode 100644 index 0000000000000..d034c6d560f0c --- /dev/null +++ b/test/Frontend/Inputs/type-fingerprint/b1.swift @@ -0,0 +1,8 @@ +struct B1 { + var a: Int = 1 + var b: Int = 17 +} +struct B2 { + var b: Int = 2 +} + diff --git a/test/Frontend/Inputs/type-fingerprint/main.swift b/test/Frontend/Inputs/type-fingerprint/main.swift new file mode 100644 index 0000000000000..10e11f1d3cebb --- /dev/null +++ b/test/Frontend/Inputs/type-fingerprint/main.swift @@ -0,0 +1,3 @@ +func foo() { + B1() +} diff --git a/test/Frontend/Inputs/type-fingerprint/ofm.json b/test/Frontend/Inputs/type-fingerprint/ofm.json new file mode 100644 index 0000000000000..3307c238c8fdf --- /dev/null +++ b/test/Frontend/Inputs/type-fingerprint/ofm.json @@ -0,0 +1,17 @@ +{ + "./a.swift": { + "object": "./a.o", + "swift-dependencies": "./a.swiftdeps" + }, + "./b.swift": { + "object": "./b.o", + "swift-dependencies": "./b.swiftdeps" + }, + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Frontend/ast-dump.swift b/test/Frontend/ast-dump.swift index 042d736c7aa89..1ca998f0b3eeb 100644 --- a/test/Frontend/ast-dump.swift +++ b/test/Frontend/ast-dump.swift @@ -4,9 +4,9 @@ // RUN: echo 'public func main() {a(); b()}' >%t/main.swift // Test printing to stdout -// RUN: %target-swift-frontend -dump-ast -primary-file %t/a.swift %t/b.swift %t/main.swift -module-name main -o - 2>&1 | %FileCheck -check-prefix A-AST %s -// RUN: %target-swift-frontend -dump-ast %t/a.swift -primary-file %t/b.swift %t/main.swift -module-name main -o - 2>&1 | %FileCheck -check-prefix B-AST %s -// RUN: %target-swift-frontend -dump-ast %t/a.swift %t/b.swift -primary-file %t/main.swift -module-name main -o - 2>&1 | %FileCheck -check-prefix MAIN-AST %s +// RUN: %target-swift-frontend -dump-ast -primary-file %t/a.swift %t/b.swift %t/main.swift -module-name main -o - 2>%t/a.swift.stderr | %FileCheck -check-prefix A-AST %s +// RUN: %target-swift-frontend -dump-ast %t/a.swift -primary-file %t/b.swift %t/main.swift -module-name main -o - 2>%t/b.swift.stderr | %FileCheck -check-prefix B-AST %s +// RUN: %target-swift-frontend -dump-ast %t/a.swift %t/b.swift -primary-file %t/main.swift -module-name main -o - 2>%t/main.swift.stderr | %FileCheck -check-prefix MAIN-AST %s // Test printing to files // RUN: %target-swift-frontend -dump-ast -primary-file %t/a.swift %t/b.swift %t/main.swift -module-name main -o %t/a.ast diff --git a/test/Frontend/crash-in-user-code.swift b/test/Frontend/crash-in-user-code.swift new file mode 100644 index 0000000000000..4a36a0c3b5a4c --- /dev/null +++ b/test/Frontend/crash-in-user-code.swift @@ -0,0 +1,21 @@ + +// RUN: echo %s > %t.filelist.txt +// RUN: not --crash %target-swift-frontend -interpret -filelist %t.filelist.txt 2>&1 | %FileCheck %s + +// REQUIRES: executable_test + +// UNSUPPORTED: OS=ios +// UNSUPPORTED: OS=tvos +// UNSUPPORTED: OS=watchos + +// CHECK: Stack dump: +// CHECK-NEXT: Program arguments: +// CHECK-NEXT: Swift version +// CHECK-NEXT: Contents of {{.*}}.filelist.txt: +// CHECK-NEXT: --- +// CHECK-NEXT: crash-in-user-code.swift +// CHECK-NEXT: --- +// CHECK-NEXT: While running user code "{{.*}}crash-in-user-code.swift" + +let x: Int? = nil +x! diff --git a/test/Frontend/debug-diagnostic-names.swift b/test/Frontend/debug-diagnostic-names.swift index 0ba2f1a035612..f625a04d08779 100644 --- a/test/Frontend/debug-diagnostic-names.swift +++ b/test/Frontend/debug-diagnostic-names.swift @@ -5,6 +5,15 @@ let x = // CHECK_NAMES: error: expected initial value after '=' [expected_init_value]{{$}} // CHECK_NONAMES: error: expected initial value after '='{{$}} +// CHECK_NAMES: warning: expression following 'return' is treated as an argument of the 'return' [unindented_code_after_return]{{$}} +// CHECK_NAMES: note: indent the expression to silence this warning [indent_expression_to_silence]{{$}} +// CHECK_NONAMES: warning: expression following 'return' is treated as an argument of the 'return'{{$}} +// CHECK_NONAMES: note: indent the expression to silence this warning{{$}} +func foo() -> Int { + return + 42 +} + guard let y = 0 else {} // CHECK_NAMES: error: initializer for conditional binding must have Optional type, not 'Int' [condition_optional_element_pattern_not_valid_type]{{$}} // CHECK_NONAMES: error: initializer for conditional binding must have Optional type, not 'Int'{{$}} @@ -12,12 +21,3 @@ guard let y = 0 else {} let z: Double = "" // CHECK_NAMES: error: cannot convert value of type 'String' to specified type 'Double' [cannot_convert_initializer_value]{{$}} // CHECK_NONAMES: error: cannot convert value of type 'String' to specified type 'Double'{{$}} - -func foo() -> Int { - return - 42 -} -// CHECK_NAMES: warning: expression following 'return' is treated as an argument of the 'return' [unindented_code_after_return]{{$}} -// CHECK_NAMES: note: indent the expression to silence this warning [indent_expression_to_silence]{{$}} -// CHECK_NONAMES: warning: expression following 'return' is treated as an argument of the 'return'{{$}} -// CHECK_NONAMES: note: indent the expression to silence this warning{{$}} diff --git a/test/Frontend/debug-generic-signatures.swift b/test/Frontend/debug-generic-signatures.swift index 2f351664675ed..b41418d287492 100644 --- a/test/Frontend/debug-generic-signatures.swift +++ b/test/Frontend/debug-generic-signatures.swift @@ -72,6 +72,9 @@ struct NonRecur: P2 { // Conditional conformance. +// CHECK: Generic signature: +// CHECK-NEXT: Canonical generic signature: <τ_0_0> +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Generic struct Generic {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=Generic // CHECK-NEXT: (normal_conformance type=Generic protocol=P1 @@ -85,6 +88,10 @@ extension Generic: P1 where T: P1 { // Satisfying associated types with requirements with generic params + +// CHECK: Generic signature: +// CHECK-NEXT: Canonical generic signature: <τ_0_0, τ_0_1> +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Super class Super {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=Super // CHECK-NEXT: (normal_conformance type=Super protocol=P2 diff --git a/test/Frontend/dependencies-fine.swift b/test/Frontend/dependencies-fine.swift new file mode 100644 index 0000000000000..9c9365dcfd9f0 --- /dev/null +++ b/test/Frontend/dependencies-fine.swift @@ -0,0 +1,117 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-dependencies-path - -resolve-imports "%S/../Inputs/empty file.swift" | %FileCheck -check-prefix=CHECK-BASIC %s +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-reference-dependencies-path - -typecheck -primary-file "%S/../Inputs/empty file.swift" > %t.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-BASIC-YAML %s <%t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" +// RUN: %FileCheck -check-prefix=CHECK-BASIC %s < %t.d +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-BASIC-YAML %s < %t-processed.swiftdeps + +// CHECK-BASIC-LABEL: - : +// CHECK-BASIC: Inputs/empty\ file.swift +// CHECK-BASIC: Swift.swiftmodule +// CHECK-BASIC-NOT: {{ }}:{{ }} + +// CHECK-BASIC-YAML-NOT: externalDepend {{.*}}empty +// CHECK-BASIC-YAML: externalDepend {{.*}} '{{.*}}Swift.swiftmodule{{(/.+[.]swiftmodule)?}}' + + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck "%S/../Inputs/empty file.swift" 2>&1 | %FileCheck -check-prefix=NO-PRIMARY-FILE %s + +// NO-PRIMARY-FILE: warning: ignoring -emit-reference-dependencies (requires -primary-file) + + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-dependencies-path - -emit-module "%S/../Inputs/empty file.swift" -o "%t/empty file.swiftmodule" -emit-module-doc-path "%t/empty file.swiftdoc" -emit-objc-header-path "%t/empty file.h" -emit-module-interface-path "%t/empty file.swiftinterface" | %FileCheck -check-prefix=CHECK-MULTIPLE-OUTPUTS %s + +// CHECK-MULTIPLE-OUTPUTS-LABEL: empty\ file.swiftmodule : +// CHECK-MULTIPLE-OUTPUTS: Inputs/empty\ file.swift +// CHECK-MULTIPLE-OUTPUTS: Swift.swiftmodule +// CHECK-MULTIPLE-OUTPUTS-LABEL: empty\ file.swiftdoc : +// CHECK-MULTIPLE-OUTPUTS: Inputs/empty\ file.swift +// CHECK-MULTIPLE-OUTPUTS: Swift.swiftmodule +// CHECK-MULTIPLE-OUTPUTS-LABEL: empty\ file.swiftinterface : +// CHECK-MULTIPLE-OUTPUTS: Inputs/empty\ file.swift +// CHECK-MULTIPLE-OUTPUTS: Swift.swiftmodule +// CHECK-MULTIPLE-OUTPUTS-LABEL: empty\ file.h : +// CHECK-MULTIPLE-OUTPUTS: Inputs/empty\ file.swift +// CHECK-MULTIPLE-OUTPUTS: Swift.swiftmodule +// CHECK-MULTIPLE-OUTPUTS-NOT: {{ }}:{{ }} + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -track-system-dependencies -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT-TRACK-SYSTEM %s + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file %s +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-IMPORT-YAML %s <%t-processed.swiftdeps + +// CHECK-IMPORT-LABEL: - : +// CHECK-IMPORT: dependencies-fine.swift +// CHECK-IMPORT-DAG: Swift.swiftmodule +// CHECK-IMPORT-DAG: Inputs/dependencies/$$$$$$$$$$.h +// CHECK-IMPORT-DAG: Inputs/dependencies{{/|\\}}UserClangModule.h +// CHECK-IMPORT-DAG: Inputs/dependencies/extra-header.h +// CHECK-IMPORT-DAG: Inputs/dependencies{{/|\\}}module.modulemap +// CHECK-IMPORT-DAG: ObjectiveC.swift +// CHECK-IMPORT-DAG: Foundation.swift +// CHECK-IMPORT-DAG: CoreGraphics.swift +// CHECK-IMPORT-NOT: {{[^\\]}}: + +// CHECK-IMPORT-TRACK-SYSTEM-LABEL: - : +// CHECK-IMPORT-TRACK-SYSTEM: dependencies-fine.swift +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Swift.swiftmodule +// CHECK-IMPORT-TRACK-SYSTEM-DAG: SwiftOnoneSupport.swiftmodule +// CHECK-IMPORT-TRACK-SYSTEM-DAG: CoreFoundation.swift +// CHECK-IMPORT-TRACK-SYSTEM-DAG: CoreGraphics.swift +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Foundation.swift +// CHECK-IMPORT-TRACK-SYSTEM-DAG: ObjectiveC.swift +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Inputs/dependencies/$$$$$$$$$$.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Inputs/dependencies{{/|\\}}UserClangModule.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Inputs/dependencies/extra-header.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: Inputs/dependencies{{/|\\}}module.modulemap +// CHECK-IMPORT-TRACK-SYSTEM-DAG: swift{{/|\\}}shims{{/|\\}}module.modulemap +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}CoreFoundation.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}CoreGraphics.apinotes +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}CoreGraphics.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}Foundation.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}objc{{/|\\}}NSObject.h +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}objc{{/|\\}}ObjectiveC.apinotes +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}objc{{/|\\}}module.map +// CHECK-IMPORT-TRACK-SYSTEM-DAG: usr{{/|\\}}include{{/|\\}}objc{{/|\\}}objc.h +// CHECK-IMPORT-TRACK-SYSTEM-NOT: {{[^\\]}}: + +// CHECK-IMPORT-YAML-NOT: externalDepend {{.*}}dependencies-fine.swift +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}{{/|\\}}Swift.swiftmodule{{(/.+[.]swiftmodule)?}}' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}Inputs/dependencies/$$$$$.h' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}Inputs/dependencies{{/|\\\\}}UserClangModule.h' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}Inputs/dependencies/extra-header.h' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}Inputs/dependencies{{/|\\\\}}module.modulemap' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}{{/|\\\\}}ObjectiveC.swift' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}{{/|\\\\}}Foundation.swift' +// CHECK-IMPORT-YAML-DAG: externalDepend {{.*}} '{{.*}}{{/|\\\\}}CoreGraphics.swift' + +// CHECK-ERROR-YAML: # Dependencies are unknown because a compilation error occurred. + +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -typecheck %s | %FileCheck -check-prefix=CHECK-IMPORT %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -typecheck -primary-file %s - %FileCheck -check-prefix=CHECK-ERROR-YAML %s + + +import Foundation +import UserClangModule + +class Test: NSObject {} + +_ = A() +_ = USER_VERSION +_ = EXTRA_VERSION +_ = MONEY + +#if ERROR +_ = someRandomUndefinedName +#endif diff --git a/test/Frontend/dependencies-preservation-fine.swift b/test/Frontend/dependencies-preservation-fine.swift new file mode 100644 index 0000000000000..b51dee6eff5d6 --- /dev/null +++ b/test/Frontend/dependencies-preservation-fine.swift @@ -0,0 +1,29 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// This test verifies that copies of dependency files are preserved after a +// compilation. For example, if the first compilation produces 'foo.swiftdeps', +// a second compilation should move 'foo.swiftdeps' to 'foo.swiftdeps~', then +// overwrite 'foo.swiftdeps' with new dependency information. + +// RUN: %empty-directory(%t) + +// First, produce the dependency files and verify their contents. +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK %s < %t-processed.swiftdeps + +// CHECK-NOT: topLevel{{.*}}EmptyStruct{{.*}}true + +// Next, produce the dependency files again, but this time using a different +// Swift source file than before. .swiftdeps~ should contain the same content +// as before. .swiftdeps should contain content that matches the new source +// file. +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file %S/../Inputs/global_resilience.swift +// RUN: %FileCheck -check-prefix=CHECK %s < %t.swiftdeps~ +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-OVERWRITTEN %s < %t-processed.swiftdeps + +// CHECK-OVERWRITTEN:topLevel{{.*}}EmptyStruct{{.*}}true + diff --git a/test/Frontend/dependencies-preservation.swift b/test/Frontend/dependencies-preservation.swift index 8677308414224..9ccee77a2ae71 100644 --- a/test/Frontend/dependencies-preservation.swift +++ b/test/Frontend/dependencies-preservation.swift @@ -6,7 +6,7 @@ // RUN: %empty-directory(%t) // First, produce the dependency files and verify their contents. -// RUN: %target-swift-frontend -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" // RUN: %FileCheck -check-prefix=CHECK %s < %t.swiftdeps // CHECK-LABEL: provides-top-level: @@ -16,7 +16,7 @@ // Swift source file than before. .swiftdeps~ should contain the same content // as before. .swiftdeps should contain content that matches the new source // file. -// RUN: %target-swift-frontend -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file %S/../Inputs/global_resilience.swift +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file %S/../Inputs/global_resilience.swift // RUN: %FileCheck -check-prefix=CHECK %s < %t.swiftdeps~ // RUN: %FileCheck -check-prefix=CHECK-OVERWRITTEN %s < %t.swiftdeps diff --git a/test/Frontend/dependencies.swift b/test/Frontend/dependencies.swift index fe3723f69888b..b305c29b0fbd7 100644 --- a/test/Frontend/dependencies.swift +++ b/test/Frontend/dependencies.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-dependencies-path - -resolve-imports "%S/../Inputs/empty file.swift" | %FileCheck -check-prefix=CHECK-BASIC %s -// RUN: %target-swift-frontend -emit-reference-dependencies-path - -typecheck -primary-file "%S/../Inputs/empty file.swift" | %FileCheck -check-prefix=CHECK-BASIC-YAML %s +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-dependencies-path - -resolve-imports "%S/../Inputs/empty file.swift" | %FileCheck -check-prefix=CHECK-BASIC %s +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-reference-dependencies-path - -typecheck -primary-file "%S/../Inputs/empty file.swift" | %FileCheck -check-prefix=CHECK-BASIC-YAML %s -// RUN: %target-swift-frontend -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck -primary-file "%S/../Inputs/empty file.swift" // RUN: %FileCheck -check-prefix=CHECK-BASIC %s < %t.d // RUN: %FileCheck -check-prefix=CHECK-BASIC-YAML %s < %t.swiftdeps @@ -18,12 +18,12 @@ // CHECK-BASIC-YAML-NOT: {{:$}} -// RUN: %target-swift-frontend -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck "%S/../Inputs/empty file.swift" 2>&1 | %FileCheck -check-prefix=NO-PRIMARY-FILE %s +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-dependencies-path %t.d -emit-reference-dependencies-path %t.swiftdeps -typecheck "%S/../Inputs/empty file.swift" 2>&1 | %FileCheck -check-prefix=NO-PRIMARY-FILE %s // NO-PRIMARY-FILE: warning: ignoring -emit-reference-dependencies (requires -primary-file) -// RUN: %target-swift-frontend -emit-dependencies-path - -emit-module "%S/../Inputs/empty file.swift" -o "%t/empty file.swiftmodule" -emit-module-doc-path "%t/empty file.swiftdoc" -emit-objc-header-path "%t/empty file.h" -emit-module-interface-path "%t/empty file.swiftinterface" | %FileCheck -check-prefix=CHECK-MULTIPLE-OUTPUTS %s +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-dependencies-path - -emit-module "%S/../Inputs/empty file.swift" -o "%t/empty file.swiftmodule" -emit-module-doc-path "%t/empty file.swiftdoc" -emit-objc-header-path "%t/empty file.h" -emit-module-interface-path "%t/empty file.swiftinterface" | %FileCheck -check-prefix=CHECK-MULTIPLE-OUTPUTS %s // CHECK-MULTIPLE-OUTPUTS-LABEL: empty\ file.swiftmodule : // CHECK-MULTIPLE-OUTPUTS: Inputs/empty\ file.swift @@ -39,9 +39,9 @@ // CHECK-MULTIPLE-OUTPUTS: Swift.swiftmodule // CHECK-MULTIPLE-OUTPUTS-NOT: {{ }}:{{ }} -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -track-system-dependencies -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT-TRACK-SYSTEM %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-reference-dependencies-path - -typecheck -primary-file %s | %FileCheck -check-prefix=CHECK-IMPORT-YAML %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -track-system-dependencies -emit-dependencies-path - -resolve-imports %s | %FileCheck -check-prefix=CHECK-IMPORT-TRACK-SYSTEM %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-reference-dependencies-path - -typecheck -primary-file %s | %FileCheck -check-prefix=CHECK-IMPORT-YAML %s // CHECK-IMPORT-LABEL: - : // CHECK-IMPORT: dependencies.swift @@ -91,8 +91,8 @@ // CHECK-IMPORT-YAML-NOT: {{^-}} // CHECK-IMPORT-YAML-NOT: {{:$}} -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -typecheck %s | %FileCheck -check-prefix=CHECK-IMPORT %s -// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-reference-dependencies-path - -typecheck -primary-file %s | %FileCheck -check-prefix=CHECK-IMPORT-YAML %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-dependencies-path - -typecheck %s | %FileCheck -check-prefix=CHECK-IMPORT %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -DERROR -import-objc-header %S/Inputs/dependencies/extra-header.h -emit-reference-dependencies-path - -typecheck -primary-file %s | %FileCheck -check-prefix=CHECK-IMPORT-YAML %s import Foundation diff --git a/test/Frontend/sil-merge-partial-modules.swift b/test/Frontend/sil-merge-partial-modules.swift index 55a84cd6b0940..249941e100b1e 100644 --- a/test/Frontend/sil-merge-partial-modules.swift +++ b/test/Frontend/sil-merge-partial-modules.swift @@ -42,11 +42,11 @@ public class CircleManager : ShapeManager { // CHECK-LABEL: sil [canonical] @$s4test14publicFunctionyyF : $@convention(thin) () -> () -// CHECK-LABEL: sil [serialized] [canonical] @$s4test17inlinableFunctionyyF : $@convention(thin) () -> () { +// CHECK-LABEL: sil [serialized] [canonical] [ossa] @$s4test17inlinableFunctionyyF : $@convention(thin) () -> () { // CHECK: function_ref @$s4test17inlinableFunctionyyFyycfU_ // CHECK: } -// CHECK-LABEL: sil shared [serialized] [canonical] @$s4test17inlinableFunctionyyFyycfU_ : $@convention(thin) () -> () { +// CHECK-LABEL: sil shared [serialized] [canonical] [ossa] @$s4test17inlinableFunctionyyFyycfU_ : $@convention(thin) () -> () { // CHECK: function_ref @$s4test17versionedFunctionyyF // CHECK: } @@ -54,15 +54,15 @@ public class CircleManager : ShapeManager { // CHECK-LABEL: sil [canonical] @$s4test9RectangleV4areaSfvg : $@convention(method) (Rectangle) -> Float -// CHECK-LABEL: sil [serialized] [canonical] @$s4test9RectangleV4drawyyF : $@convention(method) (Rectangle) -> () { +// CHECK-LABEL: sil [serialized] [canonical] [ossa] @$s4test9RectangleV4drawyyF : $@convention(method) (Rectangle) -> () { // CHECK: function_ref @$s4test14publicFunctionyyF // CHECK: } -// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] @$s4test9RectangleVAA5ShapeA2aDP4areaSfvgTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> Float { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] [ossa] @$s4test9RectangleVAA5ShapeA2aDP4areaSfvgTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> Float { // CHECK: function_ref @$s4test9RectangleV4areaSfvg // CHECK: } -// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] @$s4test9RectangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> () { +// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [canonical] [ossa] @$s4test9RectangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Rectangle) -> () { // CHECK: function_ref @$s4test9RectangleV4drawyyF // CHECK: } diff --git a/test/Frontend/skip-non-inlinable-function-bodies.swift b/test/Frontend/skip-non-inlinable-function-bodies.swift index ddee3dc87e14f..bc342f4a9ceee 100644 --- a/test/Frontend/skip-non-inlinable-function-bodies.swift +++ b/test/Frontend/skip-non-inlinable-function-bodies.swift @@ -9,7 +9,7 @@ // 2. Emit the SIL for a module and check it against the expected strings in function bodies. // If we're doing the right skipping, then the strings that are CHECK-NOT'd won't have been typechecked or SILGen'd. -// RUN: %target-swift-frontend -emit-sil -O -experimental-skip-non-inlinable-function-bodies %s 2>&1 | %FileCheck %s --check-prefixes CHECK,CHECK-SIL-ONLY +// RUN: %target-swift-frontend -emit-sil -O -experimental-skip-non-inlinable-function-bodies -emit-sorted-sil %s 2>&1 | %FileCheck %s --check-prefixes CHECK,CHECK-SIL-ONLY // 3. Emit the module interface while skipping non-inlinable function bodies. Check it against the same set of strings. @@ -29,220 +29,225 @@ @inline(never) func _blackHole(_ s: String) {} +// NOTE: The order of the checks below is important. The checks try to be +// logically grouped, but sometimes -emit-sorted-sil needs to break the logical +// order. + +@inlinable public func inlinableFunc() { + _blackHole("@inlinable func body") // CHECK: "@inlinable func body" +} + +@_fixed_layout +public class InlinableDeinit { + @inlinable deinit { + _blackHole("@inlinable deinit body") // CHECK: "@inlinable deinit body" + } +} + +@_fixed_layout +public class InlineAlwaysDeinit { + @inline(__always) deinit { + _blackHole("@inline(__always) deinit body") // CHECK-NOT: "@inline(__always) deinit body" + } +} + +public class NormalDeinit { + deinit { + _blackHole("regular deinit body") // CHECK-NOT: "regular deinit body" + } +} + +@_transparent public func transparentFunc() { + _blackHole("@_transparent func body") // CHECK: "@_transparent func body" +} + +@inline(__always) public func inlineAlwaysFunc() { + _blackHole("@inline(__always) func body") // CHECK-NOT: "@inline(__always) func body" +} + +func internalFunc() { + _blackHole("internal func body") // CHECK-NOT: "internal func body" +} + +public func publicFunc() { + _blackHole("public func body") // CHECK-NOT: "public func body" +} + +private func privateFunc() { + _blackHole("private func body") // CHECK-NOT: "private func body" +} + +@inline(__always) public func inlineAlwaysLocalTypeFunc() { + typealias InlineAlwaysLocalType = Int + _blackHole("@inline(__always) func body with local type") // CHECK-NOT: "@inline(__always) func body with local type" + func takesInlineAlwaysLocalType(_ x: InlineAlwaysLocalType) { + _blackHole("nested func body inside @inline(__always) func body taking local type") // CHECK-NOT: "nested func body inside @inline(__always) func body taking local type" + } + takesInlineAlwaysLocalType(0) +} + +public func publicLocalTypeFunc() { + typealias LocalType = Int + _blackHole("public func body with local type") // CHECK-NOT: "public func body with local type" + func takesLocalType(_ x: LocalType) { + _blackHole("nested func body inside public func body taking local type") // CHECK-NOT: "nested func body inside public func body taking local type" + } + takesLocalType(0) +} + +@inlinable +public func inlinableLocalTypeFunc() { + typealias InlinableLocalType = Int + _blackHole("@inlinable func body with local type") // CHECK: "@inlinable func body with local type" + func takesInlinableLocalType(_ x: InlinableLocalType) { + _blackHole("nested func body inside @inlinable func body taking local type") // CHECK: "nested func body inside @inlinable func body taking local type" + } + takesInlinableLocalType(0) +} + +@_transparent public func _transparentLocalTypeFunc() { + typealias TransparentLocalType = Int + _blackHole("@_transparent func body with local type") // CHECK: "@_transparent func body with local type" + func takesTransparentLocalType(_ x: TransparentLocalType) { + _blackHole("nested func body inside @_transparent func body taking local type") // CHECK: "nested func body inside @_transparent func body taking local type" + } + takesTransparentLocalType(0) +} + +@inlinable +public func inlinableNestedLocalTypeFunc() { + func nestedFunc() { + _blackHole("nested func body inside @inlinable func body") // CHECK: "nested func body inside @inlinable func body" + typealias InlinableNestedLocalType = Int + func takesLocalType(_ x: InlinableNestedLocalType) { + _blackHole("nested func body inside @inlinable func body taking local type") // CHECK: "nested func body inside @inlinable func body taking local type" + } + takesLocalType(0) + } + nestedFunc() +} + public struct Struct { + @inlinable public var inlinableVar: Int { + _blackHole("@inlinable getter body") // CHECK: "@inlinable getter body" + return 0 + } + @inlinable public func inlinableFunc() { - _blackHole("@inlinable method body") // CHECK: @inlinable method body + _blackHole("@inlinable method body") // CHECK: "@inlinable method body" } @inline(__always) public func inlineAlwaysFunc() { - _blackHole("@inline(__always) method body") // CHECK-NOT: @inline(__always) method body + _blackHole("@inline(__always) method body") // CHECK-NOT: "@inline(__always) method body" + } + + @_transparent public var transparentVar: Int { + _blackHole("@_transparent getter body") // CHECK: "@_transparent getter body" + return 0 + } + + public var inlinableSetter: Int { + get { 0 } + @inlinable set { + _blackHole("@inlinable setter body") // CHECK: "@inlinable setter body" + } } @_transparent public func transparentFunc() { - _blackHole("@_transparent method body") // CHECK: @_transparent method body + _blackHole("@_transparent method body") // CHECK: "@_transparent method body" } func internalFunc() { - _blackHole("internal method body") // CHECK-NOT: internal method body + _blackHole("internal method body") // CHECK-NOT: "internal method body" } public func publicFunc() { - _blackHole("public method body") // CHECK-NOT: public method body + _blackHole("public method body") // CHECK-NOT: "public method body" } private func privateFunc() { - _blackHole("private method body") // CHECK-NOT: private method body + _blackHole("private method body") // CHECK-NOT: "private method body" } - @inlinable public init() { - _blackHole("@inlinable init body") // CHECK: @inlinable init body + @_transparent public init(b: Int) { + _blackHole("@_transparent init body") // CHECK: "@_transparent init body" } - @inline(__always) public init(a: Int) { - _blackHole("@inline(__always) init body") // CHECK-NOT: @inline(__always) init body + @inlinable public init() { + _blackHole("@inlinable init body") // CHECK: "@inlinable init body" } - @_transparent public init(b: Int) { - _blackHole("@_transparent init body") // CHECK: @_transparent init body + @inline(__always) public init(a: Int) { + _blackHole("@inline(__always) init body") // CHECK-NOT: "@inline(__always) init body" } init(c: Int) { - _blackHole("internal init body") // CHECK-NOT: internal init body + _blackHole("internal init body") // CHECK-NOT: "internal init body" } public init(d: Int) { - _blackHole("public init body") // CHECK-NOT: public init body + _blackHole("public init body") // CHECK-NOT: "public init body" } private init(e: Int) { - _blackHole("private init body") // CHECK-NOT: private init body + _blackHole("private init body") // CHECK-NOT: "private init body" } @inlinable public subscript() -> Int { - _blackHole("@inlinable subscript getter") // CHECK: @inlinable subscript getter + _blackHole("@inlinable subscript getter") // CHECK: "@inlinable subscript getter" return 0 } @inline(__always) public subscript(a: Int, b: Int) -> Int { - _blackHole("@inline(__always) subscript getter") // CHECK-NOT: @inline(__always) subscript getter + _blackHole("@inline(__always) subscript getter") // CHECK-NOT: "@inline(__always) subscript getter" return 0 } public subscript(a: Int, b: Int, c: Int) -> Int { @_transparent get { - _blackHole("@_transparent subscript getter") // CHECK: @_transparent subscript getter + _blackHole("@_transparent subscript getter") // CHECK: "@_transparent subscript getter" return 0 } } subscript(a: Int, b: Int, c: Int, d: Int) -> Int { - _blackHole("internal subscript getter") // CHECK-NOT: internal subscript getter + _blackHole("internal subscript getter") // CHECK-NOT: "internal subscript getter" return 0 } public subscript(a: Int, b: Int, c: Int, d: Int, e: Int) -> Int { - _blackHole("public subscript getter") // CHECK-NOT: public subscript getter + _blackHole("public subscript getter") // CHECK-NOT: "public subscript getter" return 0 } private subscript(e: Int) -> Int { - _blackHole("private subscript getter") // CHECK-NOT: private subscript getter - return 0 - } - - @inlinable public var inlinableVar: Int { - _blackHole("@inlinable getter body") // CHECK: @inlinable getter body + _blackHole("private subscript getter") // CHECK-NOT: "private subscript getter" return 0 } @inline(__always) public var inlineAlwaysVar: Int { - _blackHole("@inline(__always) getter body") // CHECK-NOT: @inline(__always) getter body - return 0 - } - - @_transparent public var transparentVar: Int { - _blackHole("@_transparent getter body") // CHECK: @_transparent getter body + _blackHole("@inline(__always) getter body") // CHECK-NOT: "@inline(__always) getter body" return 0 } public var publicVar: Int { - _blackHole("public getter body") // CHECK-NOT: public getter body + _blackHole("public getter body") // CHECK-NOT: "public getter body" return 0 } - public var inlinableSetter: Int { - get { 0 } - @inlinable set { - _blackHole("@inlinable setter body") // CHECK: @inlinable setter body - } - } - public var inlineAlwaysSetter: Int { get { 0 } @inline(__always) set { - _blackHole("@inline(__always) setter body") // CHECK-NOT: @inline(__always) setter body + _blackHole("@inline(__always) setter body") // CHECK-NOT: "@inline(__always) setter body" } } public var regularSetter: Int { get { 0 } set { - _blackHole("@inline(__always) setter body") // CHECK-NOT: regular setter body + _blackHole("@inline(__always) setter body") // CHECK-NOT: "regular setter body" } } } - -@_fixed_layout -public class InlinableDesubscript { - @inlinable deinit { - _blackHole("@inlinable deinit body") // CHECK: @inlinable deinit body - } -} - -@_fixed_layout -public class InlineAlwaysDeinit { - @inline(__always) deinit { - _blackHole("@inline(__always) deinit body") // CHECK-NOT: @inline(__always) deinit body - } -} - -public class NormalDeinit { - deinit { - _blackHole("regular deinit body") // CHECK-NOT: regular deinit body - } -} - -@inlinable public func inlinableFunc() { - _blackHole("@inlinable func body") // CHECK: @inlinable func body -} - -@inline(__always) public func inlineAlwaysFunc() { - _blackHole("@inline(__always) func body") // CHECK-NOT: @inline(__always) func body -} - -@_transparent public func transparentFunc() { - _blackHole("@_transparent func body") // CHECK: @_transparent func body -} - -func internalFunc() { - _blackHole("internal func body") // CHECK-NOT: internal func body -} - -public func publicFunc() { - _blackHole("public func body") // CHECK-NOT: public func body -} - -private func privateFunc() { - _blackHole("private func body") // CHECK-NOT: private func body -} - -@inlinable -public func inlinableLocalTypeFunc() { - typealias InlinableLocalType = Int - _blackHole("@inlinable func body with local type") // CHECK: @inlinable func body with local type - func takesInlinableLocalType(_ x: InlinableLocalType) { - _blackHole("nested func body inside @inlinable func body taking local type") // CHECK: nested func body inside @inlinable func body taking local type - } - takesInlinableLocalType(0) -} - -@inline(__always) public func inlineAlwaysLocalTypeFunc() { - typealias InlineAlwaysLocalType = Int - _blackHole("@inline(__always) func body with local type") // CHECK-NOT: @inline(__always) func body with local type - func takesInlineAlwaysLocalType(_ x: InlineAlwaysLocalType) { - _blackHole("nested func body inside @inline(__always) func body taking local type") // CHECK-NOT: nested func body inside @inline(__always) func body taking local type - } - takesInlineAlwaysLocalType(0) -} - -@_transparent public func _transparentLocalTypeFunc() { - typealias TransparentLocalType = Int - _blackHole("@_transparent func body with local type") // CHECK: @_transparent func body with local type - func takesTransparentLocalType(_ x: TransparentLocalType) { - _blackHole("nested func body inside @_transparent func body taking local type") // CHECK: nested func body inside @_transparent func body taking local type - } - takesTransparentLocalType(0) -} - -public func publicLocalTypeFunc() { - typealias LocalType = Int - _blackHole("public func body with local type") // CHECK-NOT: public func body with local type - func takesLocalType(_ x: LocalType) { - _blackHole("nested func body inside public func body taking local type") // CHECK-NOT: nested func body inside public func body taking local type - } - takesLocalType(0) -} -@inlinable -public func inlinableNestedLocalTypeFunc() { - func nestedFunc() { - _blackHole("nested func body inside @inlinable func body") // CHECK: nested func body inside @inlinable func body - typealias InlinableNestedLocalType = Int - func takesLocalType(_ x: InlinableNestedLocalType) { - _blackHole("nested func body inside @inlinable func body taking local type") // CHECK: nested func body inside @inlinable func body taking local type - } - takesLocalType(0) - } - nestedFunc() -} diff --git a/test/Frontend/type-fingerprint.swift b/test/Frontend/type-fingerprint.swift new file mode 100644 index 0000000000000..aa08da31bedf2 --- /dev/null +++ b/test/Frontend/type-fingerprint.swift @@ -0,0 +1,80 @@ + +// Test per-type-body fingerprints +// + +// ============================================================================= +// Without the fingerprints +// ============================================================================= + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t +// RUN: cp %S/Inputs/type-fingerprint/ofm.json %t +// RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output1 + +// only-run-for-debugging: cp %t/b.swiftdeps %t/b1.swiftdeps + +// Change one type, but uses of all types get recompiled + +// RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + +// RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output2 + +// Save for debugging: +// only-run-for-debugging: cp %t/b.swiftdeps %t/b2.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 + +// CHECK-MAINAB-RECOMPILED: Queuing (initial): {compile: b.o <= b.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: main.o <= main.swift} +// CHECK-MAINAB-RECOMPILED: Queuing because of dependencies discovered later: {compile: a.o <= a.swift} + + +// ============================================================================= +// With the fingerprints +// ============================================================================= + +// Establish status quo + +// RUN: %empty-directory(%t) +// RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t +// RUN: cp %S/Inputs/type-fingerprint/ofm.json %t +// RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + +// RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output3 + +// only-run-for-debugging: cp %t/b.swiftdeps %t/b3.swiftdeps + +// Change one type, only uses of that type get recompiled + +// RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + +// RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output4 + +// only-run-for-debugging: cp %t/b.swiftdeps %t/b4.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-MAINB-RECOMPILED %s < %t/output4 + +// CHECK-MAINB-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: a.o <= a.swift} +// CHECK-MAINB-RECOMPILED: Queuing because of dependencies discovered later: {compile: main.o <= main.swift} +// CHECK-MAINB-RECOMPILED-NOT: Queuing because of dependencies discovered later: {compile: // CHECK-MAINB-RECOMPILED: a.o <= a.swift} + diff --git a/test/Generics/class_constraint.swift b/test/Generics/class_constraint.swift index d158524b36064..3175d9f269c04 100644 --- a/test/Generics/class_constraint.swift +++ b/test/Generics/class_constraint.swift @@ -45,3 +45,14 @@ where T.B.A: AnyObject, U.B: AnyObject, T.B == T.B.A, U.B.A == U.B { requiresAnyObject(t.b.a) requiresAnyObject(u.b.a) } + +func test_class_constraint_diagnostics_with_contextual_type() { + func foo(_: AnyObject) -> T {} // expected-note 2 {{where 'T' = 'P'}} + + class A : P {} + + // TODO(diagnostics): We could also add a note here that protocols do not conform to themselves + + let _: P = foo(A() as AnyObject) // expected-error {{local function 'foo' requires that 'P' be a class type}} + let _: P = foo(A()) // expected-error {{local function 'foo' requires that 'P' be a class type}} +} diff --git a/test/Generics/conditional_conformances.swift b/test/Generics/conditional_conformances.swift index 69bce408c79c0..7aa6f7497ba1e 100644 --- a/test/Generics/conditional_conformances.swift +++ b/test/Generics/conditional_conformances.swift @@ -11,7 +11,7 @@ protocol P6: P2 {} protocol Assoc { associatedtype AT } -func takes_P2(_: X) {} // expected-note {{in call to function 'takes_P2'}} +func takes_P2(_: X) {} func takes_P5(_: X) {} // Skip the first generic signature declcontext dump @@ -21,7 +21,9 @@ struct Free {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=Free // CHECK-NEXT: (normal_conformance type=Free protocol=P2 // CHECK-NEXT: conforms_to: T P1) -extension Free: P2 where T: P1 {} // expected-note {{requirement from conditional conformance of 'Free' to 'P2'}} +extension Free: P2 where T: P1 {} +// expected-note@-1 {{requirement from conditional conformance of 'Free' to 'P2'}} +// expected-note@-2 {{requirement from conditional conformance of 'Free' to 'P2'}} func free_good(_: U) { takes_P2(Free()) } @@ -31,6 +33,7 @@ func free_bad(_: U) { struct Constrained {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=Constrained +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Constrained // CHECK-NEXT: (normal_conformance type=Constrained protocol=P2 // CHECK-NEXT: conforms_to: T P3) extension Constrained: P2 where T: P3 {} // expected-note {{requirement from conditional conformance of 'Constrained' to 'P2'}} @@ -43,16 +46,19 @@ func constrained_bad(_: U) { struct RedundantSame {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundantSame +// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundantSame // CHECK-NEXT: (normal_conformance type=RedundantSame protocol=P2) extension RedundantSame: P2 where T: P1 {} struct RedundantSuper {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundantSuper +// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundantSuper // CHECK-NEXT: (normal_conformance type=RedundantSuper protocol=P2) extension RedundantSuper: P2 where T: P1 {} struct OverlappingSub {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=OverlappingSub +// CHECK-LABEL: ExtensionDecl line={{.*}} base=OverlappingSub // CHECK-NEXT: (normal_conformance type=OverlappingSub protocol=P2 // CHECK-NEXT: conforms_to: T P4) extension OverlappingSub: P2 where T: P4 {} // expected-note {{requirement from conditional conformance of 'OverlappingSub' to 'P2'}} @@ -66,6 +72,7 @@ func overlapping_sub_bad(_: U) { struct SameType {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=SameType +// CHECK-LABEL: ExtensionDecl line={{.*}} base=SameType // CHECK-NEXT: (normal_conformance type=SameType protocol=P2 // CHECK-NEXT: same_type: T Int) extension SameType: P2 where T == Int {} @@ -82,11 +89,13 @@ func same_type_bad(_: U) { struct SameTypeGeneric {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=SameTypeGeneric +// CHECK-LABEL: ExtensionDecl line={{.*}} base=SameTypeGeneric // CHECK-NEXT: (normal_conformance type=SameTypeGeneric protocol=P2 // CHECK-NEXT: same_type: T U) extension SameTypeGeneric: P2 where T == U {} // expected-note@-1 {{requirement from conditional conformance of 'SameTypeGeneric' to 'P2'}} // expected-note@-2 {{requirement from conditional conformance of 'SameTypeGeneric' to 'P2'}} +// expected-note@-3 {{requirement from conditional conformance of 'SameTypeGeneric' to 'P2'}} func same_type_generic_good(_: U, _: V) where U: Assoc, V: Assoc, U.AT == V.AT { @@ -96,7 +105,7 @@ func same_type_generic_good(_: U, _: V) } func same_type_bad(_: U, _: V) { takes_P2(SameTypeGeneric()) - // expected-error@-1{{generic parameter 'X' could not be inferred}} + // expected-error@-1{{global function 'takes_P2' requires the types 'U' and 'V' be equivalent}} takes_P2(SameTypeGeneric()) // expected-error@-1{{global function 'takes_P2' requires the types 'U' and 'Int' be equivalent}} takes_P2(SameTypeGeneric()) @@ -106,6 +115,7 @@ func same_type_bad(_: U, _: V) { struct Infer {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=Infer +// CHECK-LABEL: ExtensionDecl line={{.*}} base=Infer // CHECK-NEXT: (normal_conformance type=Infer protocol=P2 // CHECK-NEXT: same_type: T Constrained // CHECK-NEXT: conforms_to: U P1) @@ -124,6 +134,7 @@ func infer_bad(_: U, _: V) { struct InferRedundant {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InferRedundant +// CHECK-LABEL: ExtensionDecl line={{.*}} base=InferRedundant // CHECK-NEXT: (normal_conformance type=InferRedundant protocol=P2 // CHECK-NEXT: same_type: T Constrained) extension InferRedundant: P2 where T == Constrained {} @@ -144,6 +155,7 @@ class C3: C2 {} struct ClassFree {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassFree +// CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassFree // CHECK-NEXT: (normal_conformance type=ClassFree protocol=P2 // CHECK-NEXT: superclass: T C1) extension ClassFree: P2 where T: C1 {} // expected-note {{requirement from conditional conformance of 'ClassFree' to 'P2'}} @@ -157,6 +169,7 @@ func class_free_bad(_: U) { struct ClassMoreSpecific {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassMoreSpecific +// CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassMoreSpecific // CHECK-NEXT: (normal_conformance type=ClassMoreSpecific protocol=P2 // CHECK-NEXT: superclass: T C3) extension ClassMoreSpecific: P2 where T: C3 {} // expected-note {{requirement from conditional conformance of 'ClassMoreSpecific' to 'P2'}} @@ -171,6 +184,7 @@ func class_more_specific_bad(_: U) { struct ClassLessSpecific {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassLessSpecific +// CHECK-LABEL: ExtensionDecl line={{.*}} base=ClassLessSpecific // CHECK-NEXT: (normal_conformance type=ClassLessSpecific protocol=P2) extension ClassLessSpecific: P2 where T: C1 {} @@ -193,10 +207,12 @@ func subclass_bad() { struct InheritEqual {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual +// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual // CHECK-NEXT: (normal_conformance type=InheritEqual protocol=P2 // CHECK-NEXT: conforms_to: T P1) extension InheritEqual: P2 where T: P1 {} // expected-note {{requirement from conditional conformance of 'InheritEqual' to 'P2'}} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual +// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual // CHECK-NEXT: (normal_conformance type=InheritEqual protocol=P5 // CHECK-NEXT: (normal_conformance type=InheritEqual protocol=P2 // CHECK-NEXT: conforms_to: T P1) @@ -221,10 +237,12 @@ extension InheritLess: P5 {} // expected-error{{type 'T' does not conform to pro struct InheritMore {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore +// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore // CHECK-NEXT: (normal_conformance type=InheritMore protocol=P2 // CHECK-NEXT: conforms_to: T P1) extension InheritMore: P2 where T: P1 {} // expected-note {{requirement from conditional conformance of 'InheritMore' to 'P2'}} // CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore +// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore // CHECK-NEXT: (normal_conformance type=InheritMore protocol=P5 // CHECK-NEXT: (normal_conformance type=InheritMore protocol=P2 // CHECK-NEXT: conforms_to: T P1) @@ -308,11 +326,13 @@ extension TwoDisjointConformances: P2 where T == String {} // true in the original type's generic signature. struct RedundancyOrderDependenceGood {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceGood +// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceGood // CHECK-NEXT: (normal_conformance type=RedundancyOrderDependenceGood protocol=P2 // CHECK-NEXT: same_type: T U) extension RedundancyOrderDependenceGood: P2 where U: P1, T == U {} struct RedundancyOrderDependenceBad {} // CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceBad +// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceBad // CHECK-NEXT: (normal_conformance type=RedundancyOrderDependenceBad protocol=P2 // CHECK-NEXT: conforms_to: T P1 // CHECK-NEXT: same_type: T U) @@ -324,8 +344,7 @@ func existential_good(_: T.Type) { } func existential_bad(_: T.Type) { - // FIXME: Poor diagnostic. - _ = Free() as P2 // expected-error{{'Free' is not convertible to 'P2'; did you mean to use 'as!' to force downcast?}} + _ = Free() as P2 // expected-error{{protocol 'P2' requires that 'T' conform to 'P1'}} } // rdar://problem/35837054 diff --git a/test/Generics/conditional_conformances_literals.swift b/test/Generics/conditional_conformances_literals.swift index f722b89c3ae56..932a51aa76f76 100644 --- a/test/Generics/conditional_conformances_literals.swift +++ b/test/Generics/conditional_conformances_literals.swift @@ -9,13 +9,13 @@ struct Works: Hashable, Conforms {} struct Fails: Hashable {} extension Array: SameType where Element == Works {} -// expected-note@-1 2 {{requirement from conditional conformance of '[Fails]' to 'SameType'}} +// expected-note@-1 3 {{requirement from conditional conformance of '[Fails]' to 'SameType'}} extension Dictionary: SameType where Value == Works {} -// expected-note@-1 2 {{requirement from conditional conformance of '[Int : Fails]' to 'SameType'}} +// expected-note@-1 3 {{requirement from conditional conformance of '[Int : Fails]' to 'SameType'}} extension Array: Conforms where Element: Conforms {} -// expected-note@-1 5 {{requirement from conditional conformance of '[Fails]' to 'Conforms'}} +// expected-note@-1 7 {{requirement from conditional conformance of '[Fails]' to 'Conforms'}} extension Dictionary: Conforms where Value: Conforms {} -// expected-note@-1 3 {{requirement from conditional conformance of '[Int : Fails]' to 'Conforms'}} +// expected-note@-1 5 {{requirement from conditional conformance of '[Int : Fails]' to 'Conforms'}} // expected-note@-2 2 {{requirement from conditional conformance of '[Int : Conforms]' to 'Conforms'}} let works = Works() @@ -39,11 +39,11 @@ func arraySameType() { let _: SameType = [works] as SameType let _: SameType = [fails] as SameType - // expected-error@-1 {{'[Fails]' is not convertible to 'SameType'}} + // expected-error@-1 {{cannot convert value of type 'Fails' to expected element type 'Works'}} let _: SameType = arrayWorks as SameType let _: SameType = arrayFails as SameType - // expected-error@-1 {{'[Fails]' is not convertible to 'SameType'}} + // expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}} } func dictionarySameType() { @@ -64,11 +64,11 @@ func dictionarySameType() { let _: SameType = [0 : works] as SameType let _: SameType = [0 : fails] as SameType - // expected-error@-1 {{'[Int : Fails]' is not convertible to 'SameType'}} + // expected-error@-1 {{cannot convert value of type 'Fails' to expected dictionary value type 'Works'}} let _: SameType = dictWorks as SameType let _: SameType = dictFails as SameType - // expected-error@-1 {{'[Int : Fails]' is not convertible to 'SameType'}} + // expected-error@-1 {{protocol 'SameType' requires the types 'Fails' and 'Works' be equivalent}} } func arrayConforms() { @@ -89,11 +89,11 @@ func arrayConforms() { let _: Conforms = [works] as Conforms let _: Conforms = [fails] as Conforms - // expected-error@-1 {{'[Fails]' is not convertible to 'Conforms'}} + // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} let _: Conforms = arrayWorks as Conforms let _: Conforms = arrayFails as Conforms - // expected-error@-1 {{'[Fails]' is not convertible to 'Conforms'}} + // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} } func dictionaryConforms() { @@ -114,11 +114,11 @@ func dictionaryConforms() { let _: Conforms = [0 : works] as Conforms let _: Conforms = [0 : fails] as Conforms - // expected-error@-1 {{'[Int : Fails]' is not convertible to 'Conforms'}} + // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} let _: Conforms = dictWorks as Conforms let _: Conforms = dictFails as Conforms - // expected-error@-1 {{'[Int : Fails]' is not convertible to 'Conforms'}} + // expected-error@-1 {{protocol 'Conforms' requires that 'Fails' conform to 'Conforms'}} } func combined() { diff --git a/test/Generics/deduction.swift b/test/Generics/deduction.swift index 0059c723a4a7b..9ce7ff1f3419a 100644 --- a/test/Generics/deduction.swift +++ b/test/Generics/deduction.swift @@ -7,7 +7,9 @@ func identity(_ value: T) -> T { return value } func identity2(_ value: T) -> T { return value } +// expected-note@-1 {{'identity2' produces 'Y', not the expected contextual result type 'X'}} func identity2(_ value: T) -> Int { return 0 } +// expected-note@-1 {{'identity2' produces 'Int', not the expected contextual result type 'X'}} struct X { } struct Y { } @@ -23,7 +25,7 @@ func useIdentity(_ x: Int, y: Float, i32: Int32) { // Deduction where the result type and input type can get different results var xx : X, yy : Y xx = identity(yy) // expected-error{{cannot assign value of type 'Y' to type 'X'}} - xx = identity2(yy) // expected-error{{cannot convert value of type 'Y' to expected argument type 'X'}} + xx = identity2(yy) // expected-error{{no exact matches in call to global function 'identity2'}} } // FIXME: Crummy diagnostic! @@ -194,7 +196,7 @@ protocol IsBefore { func isBefore(_ other: Self) -> Bool } -func min2(_ x: T, _ y: T) -> T { +func min2(_ x: T, _ y: T) -> T { // expected-note {{where 'T' = 'Float'}} if y.isBefore(x) { return y } return x } @@ -205,7 +207,7 @@ extension Int : IsBefore { func callMin(_ x: Int, y: Int, a: Float, b: Float) { _ = min2(x, y) - min2(a, b) // expected-error{{argument type 'Float' does not conform to expected type 'IsBefore'}} + min2(a, b) // expected-error{{global function 'min2' requires that 'Float' conform to 'IsBefore'}} } func rangeOfIsBefore(_ range: R) where R.Element : IsBefore {} // expected-note {{where 'R.Element' = 'IndexingIterator<[Double]>.Element' (aka 'Double')}} @@ -241,11 +243,11 @@ genericInheritsA(C_GI()) //===----------------------------------------------------------------------===// // Deduction for member operators //===----------------------------------------------------------------------===// -protocol Addable { +protocol Addable { // expected-note {{where 'Self' = 'U'}} static func +(x: Self, y: Self) -> Self } func addAddables(_ x: T, y: T, u: U) -> T { - u + u // expected-error{{argument type 'U' does not conform to expected type 'Addable'}} + u + u // expected-error{{protocol 'Addable' requires that 'U' conform to 'Addable'}} return x+y } @@ -266,7 +268,7 @@ func testGetVectorSize(_ vi: MyVector, vf: MyVector) { i = getVectorSize(vi) i = getVectorSize(vf) - getVectorSize(i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'MyVector'}} + getVectorSize(i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'MyVector'}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} var x : X, y : Y @@ -311,14 +313,13 @@ class DeducePropertyParams { // SR-69 struct A {} func foo() { - for i in min(1,2) { // expected-error{{type 'Int' does not conform to protocol 'Sequence'}} + for i in min(1,2) { // expected-error{{for-in loop requires 'Int' to conform to 'Sequence'}} } let j = min(Int(3), Float(2.5)) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}} - let k = min(A(), A()) // expected-error{{argument type 'A' does not conform to expected type 'Comparable'}} + let k = min(A(), A()) // expected-error{{global function 'min' requires that 'A' conform to 'Comparable'}} let oi : Int? = 5 - let l = min(3, oi) // expected-error{{value of optional type 'Int?' must be unwrapped}} - // expected-note@-1{{coalesce}} - // expected-note@-2{{force-unwrap}} + let l = min(3, oi) // expected-error{{global function 'min' requires that 'Int?' conform to 'Comparable'}} + // expected-note@-1{{wrapped type 'Int' satisfies this requirement}} } infix operator +& @@ -341,3 +342,29 @@ prefix func +-(_: T) where T: Sequence, T.Element == Int {} +-"hello" // expected-error@-1 {{operator function '+-' requires the types 'String.Element' (aka 'Character') and 'Int' be equivalent}} + +func test_transitive_subtype_deduction_for_generic_params() { + class A {} + + func foo(_: [(String, (T) -> Void)]) {} + + func bar(_: @escaping (U) -> Void) -> (U) -> Void { + return { _ in } + } + + // Here we have: + // - `W subtype of A` + // - `W subtype of U` + // + // Type variable associated with `U` has to be attempted + // first because solver can't infer bindings for `W` transitively + // through `U`. + func baz(_ arr: [(String, (W) -> Void)]) { + foo(arr.map { ($0.0, bar($0.1)) }) // Ok + } + + func fiz(_ a: T, _ op: (T, T) -> Bool, _ b: T) {} + func biz(_ v: Int32) { + fiz(v, !=, -1) // Ok because -1 literal should be inferred as Int32 + } +} diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index 8f1d2e459dabc..fbf2f29375fc5 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -16,13 +16,16 @@ func fOP(_ t: T) { } func fOPE(_ t: OP) { } func fSP(_ t: T) { } func fAO(_ t: T) { } +// expected-note@-1 {{where 'T' = 'P'}} +// expected-note@-2 {{where 'T' = 'CP'}} +// expected-note@-3 {{where 'T' = 'OP & P'}} func fAOE(_ t: AnyObject) { } func fT(_ t: T) { } func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, ao: AnyObject) { fP(p) // expected-error{{value of protocol type 'P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} - fAO(p) // expected-error{{cannot invoke 'fAO' with an argument list of type '(P)'}} // expected-note{{expected an argument list of type '(T)'}} - fAOE(p) // expected-error{{argument type 'P' does not conform to expected type 'AnyObject'}} + fAO(p) // expected-error{{global function 'fAO' requires that 'P' be a class type}} + fAOE(p) // expected-error{{argument type 'P' expected to be an instance of a class or class-constrained type}} fT(p) fOP(op) @@ -30,13 +33,13 @@ func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, fAOE(op) fT(op) - fAO(cp) // expected-error{{cannot invoke 'fAO' with an argument list of type '(CP)'}} // expected-note{{expected an argument list of type '(T)'}} + fAO(cp) // expected-error{{global function 'fAO' requires that 'CP' be a class type}} fAOE(cp) fT(cp) fP(opp) // expected-error{{value of protocol type 'OP & P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} fOP(opp) // expected-error{{value of protocol type 'OP & P' cannot conform to 'OP'; only struct/enum/class types can conform to protocols}} - fAO(opp) // expected-error{{cannot invoke 'fAO' with an argument list of type '(OP & P)'}} // expected-note{{expected an argument list of type '(T)'}} + fAO(opp) // expected-error{{global function 'fAO' requires that 'OP & P' be a class type}} fAOE(opp) fT(opp) diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index 1ab8677be96ad..9aa0ab436477c 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -53,7 +53,7 @@ func otherExistential(_ t1: T) { otherEqComp2 = t1 // expected-error{{value of type 'T' does not conform to 'OtherEqualComparable' in assignment}} _ = otherEqComp2 - _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{'T' is not convertible to 'EqualComparable & OtherEqualComparable'; did you mean to use 'as!' to force downcast?}} {{10-12=as!}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} + _ = t1 as EqualComparable & OtherEqualComparable // expected-error{{value of type 'T' does not conform to 'EqualComparable & OtherEqualComparable' in coercion}} expected-error{{protocol 'OtherEqualComparable' can only be used as a generic constraint}} expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}} } protocol Runcible { @@ -78,9 +78,9 @@ protocol Overload { func f1(_: B) -> B // expected-note {{candidate expects value of type 'OtherOvl.B' for parameter #1}} func f2(_: Int) -> A // expected-note{{found this candidate}} func f2(_: Int) -> B // expected-note{{found this candidate}} - func f3(_: Int) -> Int // expected-note {{found this candidate}} - func f3(_: Float) -> Float // expected-note {{found this candidate}} - func f3(_: Self) -> Self // expected-note {{found this candidate}} + func f3(_: Int) -> Int // expected-note {{found candidate with type '(Int) -> Int'}} + func f3(_: Float) -> Float // expected-note {{found candidate with type '(Float) -> Float'}} + func f3(_: Self) -> Self // expected-note {{found candidate with type '(OtherOvl) -> OtherOvl'}} var prop : Self { get } } @@ -112,7 +112,7 @@ func testOverload(_ ovl: Ovl, ovl2: Ovl, var f3f : (Float) -> Float = ovl.f3 var f3ovl_1 : (Ovl) -> Ovl = ovl.f3 var f3ovl_2 : (Ovl) -> Ovl = ovl2.f3 - var f3ovl_3 : (Ovl) -> Ovl = other.f3 // expected-error{{ambiguous reference to member 'f3'}} + var f3ovl_3 : (Ovl) -> Ovl = other.f3 // expected-error{{no 'f3' candidates produce the expected contextual result type '(Ovl) -> Ovl'}} var f3i_unbound : (Ovl) -> (Int) -> Int = Ovl.f3 var f3f_unbound : (Ovl) -> (Float) -> Float = Ovl.f3 @@ -154,6 +154,7 @@ func subscripting(_ t: T) { // Suggests the Int form because we prefer concrete matches to generic matches in diagnosis. t[value] = 17 // expected-error{{cannot convert value of type 'T.Value' to expected argument type 'Int'}} + // expected-error@-1 {{cannot assign value of type 'Int' to subscript of type 'T.ElementType'}} } //===----------------------------------------------------------------------===// diff --git a/test/Generics/inheritance.swift b/test/Generics/inheritance.swift index 2b12505ed47ba..ebfd77736f1d5 100644 --- a/test/Generics/inheritance.swift +++ b/test/Generics/inheritance.swift @@ -56,10 +56,10 @@ func testGenericInherit() { struct SS : T { } // expected-error{{inheritance from non-protocol type 'T'}} -enum SE : T { case X } // expected-error{{raw type 'T' is not expressible by a string, integer, or floating-point literal}} // expected-error {{SE' declares raw type 'T', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-error{{RawRepresentable conformance cannot be synthesized because raw type 'T' is not Equatable}} expected-note {{do you want to add protocol stubs?}} +enum SE : T { case X } // expected-error{{raw type 'T' is not expressible by a string, integer, or floating-point literal}} // expected-error {{SE' declares raw type 'T', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-error{{RawRepresentable conformance cannot be synthesized because raw type 'T' is not Equatable}} // Also need Equatable for init?(RawValue) -enum SE2 // expected-note {{do you want to add protocol stubs?}} +enum SE2 : T // expected-error {{'SE2' declares raw type 'T', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-error{{RawRepresentable conformance cannot be synthesized because raw type 'T' is not Equatable}} { case X } diff --git a/test/Generics/unbound.swift b/test/Generics/unbound.swift index 13b41d2a4471f..aed3469e8f6b5 100644 --- a/test/Generics/unbound.swift +++ b/test/Generics/unbound.swift @@ -59,14 +59,14 @@ class SomeClassWithInvalidMethod { // QoI: Cannot invoke with argument list (T), expected an argument list of (T) protocol r20792596P {} -func foor20792596(x: T) -> T { +func foor20792596(x: T) -> T { // expected-note {{where 'T' = 'T'}} return x } func callfoor20792596(x: T) -> T { return foor20792596(x) // expected-error@-1 {{missing argument label 'x:' in call}} - // expected-error@-2 {{argument type 'T' does not conform to expected type 'r20792596P'}} + // expected-error@-2 {{global function 'foor20792596(x:)' requires that 'T' conform to 'r20792596P'}} } // parameter "not used in function signature" when part of a superclass constraint diff --git a/test/IDE/Inputs/custom-modules/ImportAsMemberC.h b/test/IDE/Inputs/custom-modules/ImportAsMemberC.h index 089152904d268..ee6bd68fd64e2 100644 --- a/test/IDE/Inputs/custom-modules/ImportAsMemberC.h +++ b/test/IDE/Inputs/custom-modules/ImportAsMemberC.h @@ -1,3 +1,5 @@ +@import ObjectiveC; + typedef const void *CFTypeRef __attribute__((objc_bridge(id))); typedef const struct __attribute__((objc_bridge(id))) CCPowerSupply *CCPowerSupplyRef; diff --git a/test/IDE/Inputs/custom-modules/InferImportAsMember.h b/test/IDE/Inputs/custom-modules/InferImportAsMember.h index d3acd656a3149..8922eb224d73e 100644 --- a/test/IDE/Inputs/custom-modules/InferImportAsMember.h +++ b/test/IDE/Inputs/custom-modules/InferImportAsMember.h @@ -113,4 +113,12 @@ extern void IAMClassInvert(IAMOtherName iamOtherName); // Test collision where we can see the getter, but not setter extern float IAMStruct1GetCollisionNonProperty(struct IAMStruct1, int); +/// Struct with pointer properties and a synthesized memberwise init. +struct IAMPointerStruct { + double *ptr1; + double *ptr2; +}; + +extern struct IAMPointerStruct IAMPointerStructCreateOther(double *ptr); + #endif // INFER_IMPORT_AS_MEMBER_H diff --git a/test/IDE/Inputs/custom-modules/OmitNeedlessWords.h b/test/IDE/Inputs/custom-modules/OmitNeedlessWords.h index 6443532568b7b..beb456bfa9798 100644 --- a/test/IDE/Inputs/custom-modules/OmitNeedlessWords.h +++ b/test/IDE/Inputs/custom-modules/OmitNeedlessWords.h @@ -79,3 +79,16 @@ typedef NS_OPTIONS(NSUInteger, OMWWobbleOptions) { -(void)conflicting1; @property (readonly) NSInteger conflictingProp1; @end + +@interface OMWObjectType : NSObject +-(void)_enumerateObjectTypesWithHandler:(nonnull void (^)(void))handler; +@end + +@interface OMWTerrifyingGarbage4DTypeRefMask_t : NSObject +-(void)throwGarbageAway; +-(void)throwGarbage4DAwayHarder; +-(void)throwGarbage4DTypeRefMask_tAwayHardest; +-(void)burnGarbage; +-(void)carefullyBurnGarbage4D; +-(void)veryCarefullyBurnGarbage4DTypeRefMask_t; +@end diff --git a/test/IDE/Inputs/custom-modules/module.map b/test/IDE/Inputs/custom-modules/module.map index d87b1692cc467..222d9bc7337d1 100644 --- a/test/IDE/Inputs/custom-modules/module.map +++ b/test/IDE/Inputs/custom-modules/module.map @@ -35,6 +35,7 @@ module ImportAsMember { module C { requires objc header "ImportAsMemberC.h" + export * } module APINotes { diff --git a/test/IDE/coloring_invalid.swift b/test/IDE/coloring_invalid.swift index 0348c49039a87..caf0e67bf85d0 100644 --- a/test/IDE/coloring_invalid.swift +++ b/test/IDE/coloring_invalid.swift @@ -1,5 +1,13 @@ // RUN: %target-swift-ide-test -syntax-coloring -source-filename %s | %FileCheck %s +public enum Foo {} +// CHECK: public enum Foo {} +public extension Foo { +// CHECK: public extension Foo { + protocol Bar {} +// CHECK: protocol Bar {} +} + public enum Result { // CHECK: public enum Result { case success(a b = { diff --git a/test/IDE/comment_brief.swift b/test/IDE/comment_brief.swift index 1409f3e9fd36f..db447aaca1807 100644 --- a/test/IDE/comment_brief.swift +++ b/test/IDE/comment_brief.swift @@ -145,8 +145,8 @@ struct Indentation { // CHECK-NEXT: Func/briefBlockWithASCIIArt5 {{.*}} BriefComment=[Aaa. Bbb. *Ccc.] // CHECK-NEXT: Func/briefBlockWithASCIIArt6 {{.*}} BriefComment=[Aaa.] // CHECK-NEXT: Func/briefMixed1 {{.*}} BriefComment=[Aaa. Bbb.] -// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa.] -// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa.] +// CHECK-NEXT: Func/briefMixed2 {{.*}} BriefComment=[Aaa. Bbb.] +// CHECK-NEXT: Func/briefMixed3 {{.*}} BriefComment=[Aaa. Bbb.] // CHECK-NEXT: Struct/Indentation RawComment=none // CHECK-NEXT: Func/Indentation.briefBlockWithASCIIArt1 {{.*}} BriefComment=[Aaa.] diff --git a/test/IDE/complete_after_self.swift b/test/IDE/complete_after_self.swift index ba848f674aab4..0f616400ab595 100644 --- a/test/IDE/complete_after_self.swift +++ b/test/IDE/complete_after_self.swift @@ -10,6 +10,10 @@ // RUN: %FileCheck %s -check-prefix=CONSTRUCTOR_SELF_DOT_1 < %t.self.txt // RUN: %FileCheck %s -check-prefix=COMMON_SELF_DOT_1 < %t.self.txt +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONVENIENCE_SELF_DOT_1 > %t.self.txt +// RUN: %FileCheck %s -check-prefix=CONVENIENCE_SELF_DOT_1 < %t.self.txt +// RUN: %FileCheck %s -check-prefix=COMMON_SELF_DOT_1 < %t.self.txt + // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONSTRUCTOR_NONSELF_DOT_1 > %t.self.txt // RUN: %FileCheck %s -check-prefix=COMMON_SELF_DOT_1 < %t.self.txt // RUN: %FileCheck %s -check-prefix=NO_INIT < %t.self.txt @@ -49,6 +53,10 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_CONSTRUCTOR_NONSELF_DOT_1 > %t.self.txt // RUN: %FileCheck %s -check-prefix=NO_INIT < %t.self.txt +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=EXTENSION_CONSTRUCTOR_SELF_DOT_1 > %t.self.txt +// RUN: %FileCheck %s -check-prefix=COMMON_SELF_DOT_1 < %t.self.txt +// RUN: %FileCheck %s -check-prefix=EXTENSION_CONSTRUCTOR_SELF_DOT_1 < %t.self.txt + // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STRUCT_FUNC_SELF_DOT_1 > %t.self.txt // RUN: %FileCheck %s -check-prefix=NO_INIT < %t.self.txt @@ -177,16 +185,24 @@ class ThisDerived1 : ThisBase1 { // NO_INIT-NOT: init() } - init(a : Int) { + init(a: Int) { self.#^CONSTRUCTOR_SELF_DOT_1^# // CONSTRUCTOR_SELF_DOT_1: Begin completions, 16 items // CONSTRUCTOR_SELF_DOT_1-NOT: Decl[Constructor] - // CONSTRUCTOR_SELF_DOT_1: End completions let d: ThisDerived1 d.#^CONSTRUCTOR_NONSELF_DOT_1^# } + convenience init(conv: Int) { + self.#^CONVENIENCE_SELF_DOT_1^# +// CONVENIENCE_SELF_DOT_1: Begin completions, 20 items +// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#]; name=init() +// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a: Int) +// CONVENIENCE_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#conv: Int#})[#ThisDerived1#]; name=init(conv: Int) +// CONVENIENCE_SELF_DOT_1: End completions + } + deinit { self#^DESTRUCTOR_SELF_NO_DOT_1^# // DESTRUCTOR_SELF_NO_DOT_1: Begin completions, 21 items @@ -217,15 +233,16 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .derivedStaticFunc0()[#Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init()[#ThisDerived1#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init({#a: Int#})[#ThisDerived1#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init({#conv: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .test1({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .test2({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .staticTest1()[#Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .staticTest2()[#Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: .derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: .derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Struct]/CurrNominal: .DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Class]/CurrNominal: .DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Enum]/CurrNominal: .DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: .DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[Constructor]/CurrNominal: .init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_NO_DOT_1-NEXT: Decl[InstanceMethod]/Super: .baseFunc0({#(self): ThisBase1#})[#() -> Void#] @@ -251,15 +268,16 @@ class ThisDerived1 : ThisBase1 { // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: derivedStaticFunc0()[#Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init({#conv: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: test1({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: test2({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: staticTest1()[#Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: staticTest2()[#Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/CurrNominal: derivedExtInstanceFunc0({#(self): ThisDerived1#})[#() -> Void#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[StaticMethod]/CurrNominal: derivedExtStaticFunc0()[#Void#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#ThisDerived1.DerivedExtNestedStruct#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#ThisDerived1.DerivedExtNestedClass#] -// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#ThisDerived1.DerivedExtNestedEnum#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#] +// FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#] // FUNC_STATIC_SELF_DOT_1-NEXT: Decl[InstanceMethod]/Super: baseFunc0({#(self): ThisBase1#})[#() -> Void#] @@ -312,6 +330,12 @@ extension ThisDerived1 { convenience init(someExtensionArg: Int) { self.#^EXTENSION_CONSTRUCTOR_SELF_DOT_1^# +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Begin completions, 20 items +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init()[#ThisDerived1#]; name=init() +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#a: Int#})[#ThisDerived1#]; name=init(a: Int) +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: Decl[Constructor]/CurrNominal: init({#someExtensionArg: Int#})[#ThisDerived1#]; name=init(someExtensionArg: Int) +// EXTENSION_CONSTRUCTOR_SELF_DOT_1: End completions + } } @@ -319,10 +343,11 @@ struct S1 { init() {} init(x: Int) { self.#^STRUCT_CONSTRUCTOR_SELF_DOT_1^# -// STRUCT_CONSTRUCTOR_SELF_DOT_1: Begin completions, 2 items -// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Keyword[self]/CurrNominal: self[#S1#]; name=self -// STRUCT_CONSTRUCTOR_SELF_DOT_1-NOT: Decl[Constructor] -// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: f()[#Void#]; +// STRUCT_CONSTRUCTOR_SELF_DOT_1: Begin completions, 4 items +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Keyword[self]/CurrNominal: self[#S1#]; name=self +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init()[#S1#]; name=init() +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#})[#S1#]; name=init(x: Int) +// STRUCT_CONSTRUCTOR_SELF_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: f()[#Void#]; name=f() // STRUCT_CONSTRUCTOR_SELF_DOT_1: End completions let s: S1 s.#^STRUCT_CONSTRUCTOR_NONSELF_DOT_1^# diff --git a/test/IDE/complete_after_sself.swift b/test/IDE/complete_after_sself.swift new file mode 100644 index 0000000000000..ebb5d85a19363 --- /dev/null +++ b/test/IDE/complete_after_sself.swift @@ -0,0 +1,84 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyClass | %FileCheck %s --check-prefixes=CHECK-MyClass +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_DEINIT_MyClass | %FileCheck %s --check-prefixes=CHECK-MyClass +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyClass | %FileCheck %s --check-prefixes=CHECK-MyClass +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INSTANCEMETHOD_MyClass | %FileCheck %s --check-prefixes=CHECK-MyClass +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_STATICMETHOD_MyClass | %FileCheck %s --check-prefixes=CHECK-MyClass + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyStruct | %FileCheck %s --check-prefixes=CHECK-MyStruct +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_DEINIT_MyStruct | %FileCheck %s --check-prefixes=CHECK-MyStruct +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyStruct | %FileCheck %s --check-prefixes=CHECK-MyStruct +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INSTANCEMETHOD_MyStruct | %FileCheck %s --check-prefixes=CHECK-MyStruct +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_STATICMETHOD_MyStruct | %FileCheck %s --check-prefixes=CHECK-MyStruct + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyProto | %FileCheck %s --check-prefixes=CHECK-MyProto +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_DEINIT_MyProto | %FileCheck %s --check-prefixes=CHECK-MyProto +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INIT_MyProto | %FileCheck %s --check-prefixes=CHECK-MyProto +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_INSTANCEMETHOD_MyProto | %FileCheck %s --check-prefixes=CHECK-MyProto +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SSELF_DOT_IN_STATICMETHOD_MyProto | %FileCheck %s --check-prefixes=CHECK-MyProto + +class MyClass { + init() { + Self.#^SSELF_DOT_IN_INIT_MyClass^# + } + deinit { + Self.#^SSELF_DOT_IN_DEINIT_MyClass^# + } + func instanceMethod() { + Self.#^SSELF_DOT_IN_INSTANCEMETHOD_MyClass^# + } + static func staticMethod() { + Self.#^SSELF_DOT_IN_STATICMETHOD_MyClass^# + } +// CHECK-MyClass: Begin completions, 5 items +// CHECK-MyClass-DAG: Keyword[self]/CurrNominal: self[#Self.Type#]; +// CHECK-MyClass-DAG: Keyword/CurrNominal: Type[#Self.Type#]; +// CHECK-MyClass-DAG: Decl[Constructor]/CurrNominal: init()[#MyClass#]; +// CHECK-MyClass-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod({#(self): MyClass#})[#() -> Void#]; +// CHECK-MyClass-DAG: Decl[StaticMethod]/CurrNominal: staticMethod()[#Void#]; +// CHECK-MyClass: End completions +} + +struct MyStruct { + init() { + Self.#^SSELF_DOT_IN_INIT_MyStruct^# + } + deinit { + Self.#^SSELF_DOT_IN_DEINIT_MyStruct^# + } + func instanceMethod() { + Self.#^SSELF_DOT_IN_INSTANCEMETHOD_MyStruct^# + } + static func staticMethod() { + Self.#^SSELF_DOT_IN_STATICMETHOD_MyStruct^# + } +// CHECK-MyStruct: Begin completions, 5 items +// CHECK-MyStruct-DAG: Keyword[self]/CurrNominal: self[#MyStruct.Type#]; +// CHECK-MyStruct-DAG: Keyword/CurrNominal: Type[#MyStruct.Type#]; +// CHECK-MyStruct-DAG: Decl[Constructor]/CurrNominal: init()[#MyStruct#]; +// CHECK-MyStruct-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod({#(self): MyStruct#})[#() -> Void#]; +// CHECK-MyStruct-DAG: Decl[StaticMethod]/CurrNominal: staticMethod()[#Void#]; +// CHECK-MyStruct: End completions +} + +protocol MyProto { } +extension MyProto { + init() { + Self.#^SSELF_DOT_IN_INIT_MyProto^# + } + deinit { + Self.#^SSELF_DOT_IN_DEINIT_MyProto^# + } + func instanceMethod() { + Self.#^SSELF_DOT_IN_INSTANCEMETHOD_MyProto^# + } + static func staticMethod() { + Self.#^SSELF_DOT_IN_STATICMETHOD_MyProto^# + } +// CHECK-MyProto: Begin completions, 5 items +// CHECK-MyProto-DAG: Keyword[self]/CurrNominal: self[#Self.Type#]; +// CHECK-MyProto-DAG: Keyword/CurrNominal: Type[#Self.Type#]; +// CHECK-MyProto-DAG: Decl[Constructor]/CurrNominal: init()[#MyProto#]; +// CHECK-MyProto-DAG: Decl[InstanceMethod]/CurrNominal: instanceMethod({#(self): MyProto#})[#() -> Void#]; +// CHECK-MyProto-DAG: Decl[StaticMethod]/CurrNominal: staticMethod()[#Void#]; +// CHECK-MyProto: End completions +} diff --git a/test/IDE/complete_after_super.swift b/test/IDE/complete_after_super.swift index 71b80010f8d34..5727dd9892051 100644 --- a/test/IDE/complete_after_super.swift +++ b/test/IDE/complete_after_super.swift @@ -266,13 +266,13 @@ class SuperDerivedA : SuperBaseA { init (a: Float) { super.init#^CONSTRUCTOR_SUPER_INIT_1^# // CONSTRUCTOR_SUPER_INIT_1: Begin completions -// CONSTRUCTOR_SUPER_INIT_1-DAG: Pattern/CurrModule: ()[#SuperBaseA#]; +// CONSTRUCTOR_SUPER_INIT_1-DAG: Decl[Constructor]/CurrNominal: ()[#SuperBaseA#]; name=() // CONSTRUCTOR_SUPER_INIT_1: End completions } init (b: Float) { super.init(#^CONSTRUCTOR_SUPER_INIT_PAREN_1^# // CONSTRUCTOR_SUPER_INIT_PAREN_1: Begin completions, 1 items -// CONSTRUCTOR_SUPER_INIT_PAREN_1: Pattern/CurrModule: ['('][')'][#SuperBaseA#]; name= +// CONSTRUCTOR_SUPER_INIT_PAREN_1: Decl[Constructor]/CurrNominal: ['('][')'][#SuperBaseA#]; name= // CONSTRUCTOR_SUPER_INIT_PAREN_1: End completions } @@ -491,7 +491,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: End completions } - func instanceFunc1() { + override func instanceFunc1() { #^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} @@ -504,7 +504,7 @@ class SemanticContextDerived1 : SemanticContextBase1 { // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: End completions } - func instanceFunc1(_ a: Int) { + override func instanceFunc1(_ a: Int) { super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5^# // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5: Begin completions // SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}} diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 80c5c93b13556..50c58a4dbef6b 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -128,16 +128,16 @@ func f2() { } // ASSIGN_5: Begin completions, 2 items -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#C1.D1#]; name=case2 -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#C1.D1#]; name=case1 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#D1#]; name=case2 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#D1#]; name=case1 func f6() { var d : D2 d = .#^ASSIGN_6^# } // ASSIGN_6: Begin completions, 2 items -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#C1.D2#]; name=case3 -// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#C1.D2#]; name=case4 +// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#D2#]; name=case3 +// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#D2#]; name=case4 func f7 (C : C2) { var i : Int @@ -147,10 +147,10 @@ func f2() { // ASSIGN_7: Begin completions // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_7-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_7-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f8 (C : C2) { var i : Int? @@ -160,10 +160,10 @@ func f2() { // ASSIGN_8: Begin completions // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: IntGen()[#Int#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntOpGen()[#Int?#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_8-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_8-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f9 (C : C2) { var d : D1 @@ -173,10 +173,10 @@ func f2() { // ASSIGN_9: Begin completions // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_9-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_9-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f10 (C : C2) { var d : D1 @@ -186,10 +186,10 @@ func f2() { // ASSIGN_10: Begin completions // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_10-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_10-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f11(C: C2) { d = C.#^ASSIGN_11^# @@ -198,10 +198,10 @@ func f2() { // ASSIGN_11: Begin completions // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_11-DAG: Decl[InstanceMethod]/CurrNominal: VoidGen()[#Void#] -// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_11-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f12() { var i : Int @@ -211,10 +211,10 @@ func f2() { // ASSIGN_12: Begin completions // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_12-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_12-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f13() { var i : Int = #^ASSIGN_13^# @@ -250,10 +250,10 @@ func f2() { // ASSIGN_16: Begin completions // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_16-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_16-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f17() { var i : Int = C2Gen().#^ASSIGN_17^# @@ -262,10 +262,10 @@ func f2() { // ASSIGN_17: Begin completions // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: IntGen()[#Int#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#C1.D1#] -// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D1Gen()[#D1#] +// ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_17-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_17-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] func f18 (C : C2) { var d : D1 = C.#^ASSIGN_18^# @@ -274,10 +274,10 @@ func f2() { // ASSIGN_18: Begin completions // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntGen()[#Int#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: IntOpGen()[#Int?#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#C1.D1#] -// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#C1.D2#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: D1Gen()[#D1#] +// ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal: D2Gen()[#D2#] // ASSIGN_18-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: VoidGen()[#Void#] -// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C1.C2#] +// ASSIGN_18-DAG: Decl[InstanceVar]/CurrNominal: InternalC2[#C2#] } diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 5170466b86a7c..050118eb993ea 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -714,7 +714,6 @@ func testImplicitMemberInArrayLiteral() { .create2(1, #^IMPLICIT_MEMBER_ARRAY_1_SECOND^# // Same as IMPLICIT_MEMBER_SECOND. ]) - func sync() {} Receiver(arg1: 12, arg2: [ .create2(1, arg3: 2, #^IMPLICIT_MEMBER_ARRAY_1_SKIPPED^# // Same as IMPLICIT_MEMBER_SKIPPED. diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index 65d4457e21c4c..6d75f7b6afcc9 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -338,7 +338,7 @@ struct ClosureInInit1 { var prop1: S = { return S(#^CLOSURE_IN_INIT_1^# } -// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#ClosureInInit1.S#]; +// CLOSURE_IN_INIT_1: Decl[Constructor]/CurrNominal{{(/TypeRelation\[Identical\])?}}: ['(']{#Int#}[')'][#S#]; var prop2: S = { return S(#^CLOSURE_IN_INIT_2^# }() diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index 821ed5f4b32dc..76d93abec633c 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -67,9 +67,9 @@ while true { // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_PARAM_AND_ASSOC_TYPE | %FileCheck %s -check-prefix=GENERIC_PARAM_AND_ASSOC_TYPE struct CustomGenericCollection : ExpressibleByDictionaryLiteral { // GENERIC_PARAM_AND_ASSOC_TYPE: Begin completions - // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: count[#Int#]; name=count + // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended/TypeRelation[Identical]: count[#Int#]; name=count // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[GenericTypeParam]/Local: Key[#Key#]; name=Key - // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#CustomGenericCollection.Value#]; name=Value + // GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#Value#]; name=Value // GENERIC_PARAM_AND_ASSOC_TYPE: End completions var count: Int { #^GENERIC_PARAM_AND_ASSOC_TYPE^# } diff --git a/test/IDE/complete_crossmodule.swift b/test/IDE/complete_crossmodule.swift new file mode 100644 index 0000000000000..f52c8d238c20b --- /dev/null +++ b/test/IDE/complete_crossmodule.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Test.swift -I %t -code-completion-token=OPAQUE_RESULT | %FileCheck --check-prefix=OPAQUE_RESULT %s + +// BEGIN MyModule.swift + +public protocol HasAssocWithConstraint { + associatedtype AssocWithContraint: HasAssocWithConstraint + var value: AssocWithContraint { get } +} + +// BEGIN Test.swift +import MyModule + +struct MyValue: HasAssocWithConstraint { + var #^OPAQUE_RESULT^# +// OPAQUE_RESULT: Begin completions +// OPAQUE_RESULT-DAG: Decl[InstanceVar]/Super: value: some HasAssocWithConstraint; +// OPAQUE_RESULT: End completions +} diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 7ec0053cba608..9ab5063a1707b 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -37,6 +37,8 @@ struct MyStruct {} // AVAILABILITY1-NEXT: Keyword/None: tvOSApplicationExtension[#Platform#]; name=tvOSApplicationExtension{{$}} // AVAILABILITY1-NEXT: Keyword/None: watchOSApplicationExtension[#Platform#]; name=watchOSApplicationExtension{{$}} // AVAILABILITY1-NEXT: Keyword/None: OSXApplicationExtension[#Platform#]; name=OSXApplicationExtension{{$}} +// AVAILABILITY1-NEXT: Keyword/None: macCatalyst[#Platform#]; name=macCatalyst +// AVAILABILITY1-NEXT: Keyword/None: macCatalystApplicationExtension[#Platform#]; name=macCatalystApplicationExtension // AVAILABILITY1-NEXT: End completions @available(*, #^AVAILABILITY2^#) @@ -62,7 +64,10 @@ struct MyStruct {} // KEYWORD2-NEXT: Keyword/None: warn_unqualified_access[#Func Attribute#]; name=warn_unqualified_access{{$}} // KEYWORD2-NEXT: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // KEYWORD2-NEXT: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult +// KEYWORD2-NEXT: Keyword/None: differentiable[#Func Attribute#]; name=differentiable // KEYWORD2-NEXT: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction{{$}} +// KEYWORD2-NEXT: Keyword/None: derivative[#Func Attribute#]; name=derivative +// KEYWORD2-NEXT: Keyword/None: transpose[#Func Attribute#]; name=transpose // KEYWORD2-NOT: Keyword // KEYWORD2: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // KEYWORD2: End completions @@ -124,6 +129,7 @@ struct MyStruct {} // ON_GLOBALVAR-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_GLOBALVAR-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_GLOBALVAR-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable +// ON_GLOBALVAR-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable // ON_GLOBALVAR-NOT: Keyword // ON_GLOBALVAR: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_GLOBALVAR: End completions @@ -153,6 +159,7 @@ struct _S { // ON_PROPERTY-DAG: Keyword/None: inlinable[#Var Attribute#]; name=inlinable // ON_PROPERTY-DAG: Keyword/None: usableFromInline[#Var Attribute#]; name=usableFromInline // ON_PROPERTY-DAG: Keyword/None: GKInspectable[#Var Attribute#]; name=GKInspectable +// ON_PROPERTY-DAG: Keyword/None: differentiable[#Var Attribute#]; name=differentiable // ON_PROPERTY-NOT: Keyword // ON_PROPERTY: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_PROPERTY-NOT: Decl[PrecedenceGroup] @@ -172,6 +179,9 @@ struct _S { // ON_METHOD-DAG: Keyword/None: usableFromInline[#Func Attribute#]; name=usableFromInline // ON_METHOD-DAG: Keyword/None: discardableResult[#Func Attribute#]; name=discardableResult // ON_METHOD-DAG: Keyword/None: IBSegueAction[#Func Attribute#]; name=IBSegueAction +// ON_METHOD-DAG: Keyword/None: differentiable[#Func Attribute#]; name=differentiable +// ON_METHOD-DAG: Keyword/None: derivative[#Func Attribute#]; name=derivative +// ON_METHOD-DAG: Keyword/None: transpose[#Func Attribute#]; name=transpose // ON_METHOD-NOT: Keyword // ON_METHOD: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_METHOD: End completions @@ -226,6 +236,9 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction // ON_MEMBER_LAST-DAG: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // ON_MEMBER_LAST-DAG: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder +// ON_MEMBER_LAST-DAG: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable +// ON_MEMBER_LAST-DAG: Keyword/None: derivative[#Declaration Attribute#]; name=derivative +// ON_MEMBER_LAST-DAG: Keyword/None: transpose[#Declaration Attribute#]; name=transpose // ON_MEMBER_LAST-NOT: Keyword // ON_MEMBER_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_MEMBER_LAST-NOT: Decl[PrecedenceGroup] @@ -268,7 +281,10 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: frozen[#Declaration Attribute#]; name=frozen // KEYWORD_LAST-NEXT: Keyword/None: propertyWrapper[#Declaration Attribute#]; name=propertyWrapper // KEYWORD_LAST-NEXT: Keyword/None: _functionBuilder[#Declaration Attribute#]; name=_functionBuilder{{$}} +// KEYWORD_LAST-NEXT: Keyword/None: differentiable[#Declaration Attribute#]; name=differentiable // KEYWORD_LAST-NEXT: Keyword/None: IBSegueAction[#Declaration Attribute#]; name=IBSegueAction{{$}} +// KEYWORD_LAST-NEXT: Keyword/None: derivative[#Declaration Attribute#]; name=derivative +// KEYWORD_LAST-NEXT: Keyword/None: transpose[#Declaration Attribute#]; name=transpose // KEYWORD_LAST-NOT: Keyword // KEYWORD_LAST: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // KEYWORD_LAST: End completions diff --git a/test/IDE/complete_default_arguments.swift b/test/IDE/complete_default_arguments.swift index 5e798ccf9baf0..abd2197d2a23a 100644 --- a/test/IDE/complete_default_arguments.swift +++ b/test/IDE/complete_default_arguments.swift @@ -20,7 +20,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_INIT_1 | %FileCheck %s -check-prefix=DEFAULT_ARG_INIT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_INIT_2 | %FileCheck %s -check-prefix=DEFAULT_ARG_INIT_INTCONTEXT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_INIT_3 | %FileCheck %s -check-prefix=DEFAULT_ARG_INIT_INTCONTEXT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_INIT_4 | %FileCheck %s -check-prefix=DEFAULT_ARG_INIT_INTCONTEXT func freeFuncWithDefaultArgs1( _ a: Int, b: Int = 42, file: String = #file, line: Int = #line, @@ -135,8 +134,7 @@ func testDefaultArgs9(_ x: C2) { let globalVar = 1 func testDefaultArgInit1(x = #^DEFAULT_ARG_INIT_1^#) { } func testDefaultArgInit2(_: Int = #^DEFAULT_ARG_INIT_2^#) { } -func testDefaultArgInit3(Int = #^DEFAULT_ARG_INIT_3^#) { } -func testDefaultArgInit4(_ x: Int = #^DEFAULT_ARG_INIT_4^#) { } +func testDefaultArgInit3(_ x: Int = #^DEFAULT_ARG_INIT_3^#) { } // DEFAULT_ARG_INIT: Begin completions // DEFAULT_ARG_INIT: Decl[GlobalVar]/CurrModule: globalVar[#Int#]{{; name=.+$}} // DEFAULT_ARG_INIT: End completions diff --git a/test/IDE/complete_expr_postfix_begin.swift b/test/IDE/complete_expr_postfix_begin.swift index ae7bfe90200ef..21242c75eaf59 100644 --- a/test/IDE/complete_expr_postfix_begin.swift +++ b/test/IDE/complete_expr_postfix_begin.swift @@ -64,10 +64,10 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_5 | %FileCheck %s -check-prefix=IN_FOR_EACH_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_6 | %FileCheck %s -check-prefix=IN_FOR_EACH_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_7 | %FileCheck %s -check-prefix=IN_FOR_EACH_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_8 | %FileCheck %s -check-prefix=IN_FOR_EACH_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_8 | %FileCheck %s -check-prefix=IN_FOR_EACH_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_9 | %FileCheck %s -check-prefix=IN_FOR_EACH_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_10 | %FileCheck %s -check-prefix=IN_FOR_EACH_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_11 | %FileCheck %s -check-prefix=IN_FOR_EACH_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_11 | %FileCheck %s -check-prefix=IN_FOR_EACH_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FOR_EACH_12 | %FileCheck %s -check-prefix=IN_FOR_EACH_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEPRECATED_1 | %FileCheck %s -check-prefix=DEPRECATED_1 @@ -75,14 +75,31 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_1 | %FileCheck %s -check-prefix=IN_TUPLE_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_2 | %FileCheck %s -check-prefix=IN_TUPLE_2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2 +// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1 +// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_3 | %FileCheck %s -check-prefix=OWN_INIT_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_4 | %FileCheck %s -check-prefix=OWN_INIT_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_5 | %FileCheck %s -check-prefix=OWN_INIT_5 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_6 | %FileCheck %s -check-prefix=OWN_INIT_6 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_7 | %FileCheck %s -check-prefix=OWN_INIT_7 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_1 | %FileCheck %s -check-prefix=OWN_ACCESSOR_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_2 | %FileCheck %s -check-prefix=OWN_ACCESSOR_2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_3 | %FileCheck %s -check-prefix=OWN_ACCESSOR_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_4 | %FileCheck %s -check-prefix=OWN_ACCESSOR_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_5 | %FileCheck %s -check-prefix=OWN_ACCESSOR_5 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_6 | %FileCheck %s -check-prefix=OWN_ACCESSOR_6 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_7 | %FileCheck %s -check-prefix=OWN_ACCESSOR_7 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_8 | %FileCheck %s -check-prefix=OWN_ACCESSOR_7 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_9 | %FileCheck %s -check-prefix=OWN_ACCESSOR_9 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_10 | %FileCheck %s -check-prefix=OWN_ACCESSOR_10 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_11 | %FileCheck %s -check-prefix=OWN_ACCESSOR_11 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_12 | %FileCheck %s -check-prefix=OWN_ACCESSOR_11 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_13 | %FileCheck %s -check-prefix=OWN_ACCESSOR_13 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_14 | %FileCheck %s -check-prefix=OWN_ACCESSOR_13 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_15 | %FileCheck %s -check-prefix=OWN_ACCESSOR_13 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_ACCESSOR_16 | %FileCheck %s -check-prefix=OWN_ACCESSOR_13 + // // Test code completion at the beginning of expr-postfix. // @@ -563,6 +580,7 @@ func testTuple(localInt: Int) { var ownInit1: Int = #^OWN_INIT_1^# // OWN_INIT_1: Begin completions // OWN_INIT_1-NOT: ownInit1 +func sync() {} var ownInit2: () -> Void = { #^OWN_INIT_2^# } // OWN_INIT_2: Begin completions // OWN_INIT_2-NOT: ownInit2 @@ -587,3 +605,65 @@ func ownInitTestingShadow(ownInit7: Int) { // OWN_INIT_7: Begin completions // OWN_INIT_7: Decl[LocalVar]/Local/TypeRelation[Identical]: ownInit7[#Int#]; } + +var inAccessor1: Int { + get { #^OWN_ACCESSOR_1^# } +// OWN_ACCESSOR_1: Begin completions +// OWN_ACCESSOR_1: Decl[GlobalVar]/CurrModule/NotRecommended/TypeRelation[Identical]: inAccessor1[#Int#]; + set { #^OWN_ACCESSOR_2^# } +// OWN_ACCESSOR_2: Begin completions +// OWN_ACCESSOR_2: Decl[GlobalVar]/CurrModule: inAccessor1[#Int#]; +} +var inAccessor2: Int = 1 { + didSet { #^OWN_ACCESSOR_3^# } +// OWN_ACCESSOR_3: Begin completions +// OWN_ACCESSOR_3: Decl[GlobalVar]/CurrModule: inAccessor2[#Int#]; + willSet { #^OWN_ACCESSOR_4^# } +} +class InAccessorTest { + var inAccessor3: Int { + get { #^OWN_ACCESSOR_5^# } +// OWN_ACCESSOR_5: Begin completions +// OWN_ACCESSOR_5: Decl[InstanceVar]/CurrNominal/NotRecommended/TypeRelation[Identical]: inAccessor3[#Int#]; + set { #^OWN_ACCESSOR_6^# } +// OWN_ACCESSOR_6: Begin completions +// OWN_ACCESSOR_6: Decl[InstanceVar]/CurrNominal: inAccessor3[#Int#]; + } + var inAccessor4: Int = 1 { + didSet { #^OWN_ACCESSOR_7^# } +// OWN_ACCESSOR_7: Begin completions +// OWN_ACCESSOR_7: Decl[InstanceVar]/CurrNominal: inAccessor4[#Int#]; + willSet { #^OWN_ACCESSOR_8^# } + } +} +func inAccessorTest() { + var inAccessor5: Int { + get { #^OWN_ACCESSOR_9^# } +// OWN_ACCESSOR_9: Begin completions +// OWN_ACCESSOR_9: Decl[LocalVar]/Local/NotRecommended/TypeRelation[Identical]: inAccessor5[#Int#]; + set { #^OWN_ACCESSOR_10^# } +// OWN_ACCESSOR_10: Begin completions +// OWN_ACCESSOR_10: Decl[LocalVar]/Local: inAccessor5[#Int#]; + } + var inAccessor6: Int = 1 { + didSet { #^OWN_ACCESSOR_11^# } +// OWN_ACCESSOR_11: Begin completions +// OWN_ACCESSOR_11: Decl[LocalVar]/Local: inAccessor6[#Int#]; + willSet { #^OWN_ACCESSOR_12^# } + } +} +class InAccessorTestQualified { + var inAccessorProp: Int { + get { + let _ = self.#^OWN_ACCESSOR_13^# +// OWN_ACCESSOR_13: Begin completions +// OWN_ACCESSOR_13-DAG: Decl[InstanceVar]/CurrNominal: inAccessorProp[#Int#]; +// OWN_ACCESSOR_13: End completions + let _ = \InAccessorTestQualified.#^OWN_ACCESSOR_14^# + } + set { + let _ = self.#^OWN_ACCESSOR_15^# + let _ = \InAccessorTestQualified.#^OWN_ACCESSOR_16^# + } + } +} diff --git a/test/IDE/complete_from_clang_framework.swift b/test/IDE/complete_from_clang_framework.swift index f90fdda03dc82..14a2e82fd712b 100644 --- a/test/IDE/complete_from_clang_framework.swift +++ b/test/IDE/complete_from_clang_framework.swift @@ -28,6 +28,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -F %S/Inputs/mock-sdk -enable-objc-interop -code-completion-token=CLANG_CLASS_MEMBERS_2 | %FileCheck %s -check-prefix=CLANG_CLASS_MEMBERS_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -F %S/Inputs/mock-sdk -enable-objc-interop -code-completion-token=CLANG_INSTANCE_MEMBERS_1 | %FileCheck %s -check-prefix=CLANG_INSTANCE_MEMBERS_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -F %S/Inputs/mock-sdk -enable-objc-interop -code-completion-token=TYPE_MODULE_QUALIFIER | %FileCheck %s -check-prefix=MODULE_QUALIFIER +// RUN: %target-swift-ide-test -code-completion -source-filename %s -F %S/Inputs/mock-sdk -enable-objc-interop -code-completion-token=EXPR_MODULE_QUALIFIER | %FileCheck %s -check-prefix=MODULE_QUALIFIER + import Foo // Don't import FooHelper directly in this test! // import FooHelper @@ -332,3 +335,15 @@ func testCompleteInstanceMembers1(fooObject: FooClassDerived) { // CLANG_INSTANCE_MEMBERS_1-NEXT: Decl[InstanceMethod]/Super: ._internalMeth1()[#Any!#] // CLANG_INSTANCE_MEMBERS_1-NOT: Instance } + +// Check the FooHelper module is suggested even though it's not imported directly +func testExportedModuleCompletion() -> #^TYPE_MODULE_QUALIFIER^# { + let x = #^EXPR_MODULE_QUALIFIER^# +// MODULE_QUALIFIER: Begin completions +// MODULE_QUALIFIER-DAG: Decl[Module]/None: swift_ide_test[#Module#]; name=swift_ide_test +// MODULE_QUALIFIER-DAG: Decl[Module]/None: Swift[#Module#]; name=Swift +// MODULE_QUALIFIER-DAG: Decl[Module]/None: Foo[#Module#]; name=Foo +// MODULE_QUALIFIER-DAG: Decl[Module]/None: FooHelper[#Module#]; name=FooHelper +// MODULE_QUALIFIER-DAG: Decl[Module]/None: Bar[#Module#]; name=Bar +// MODULE_QUALIFIER: End completions +} diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index 0c034b42e5741..0b3fd424f4f04 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -66,6 +66,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_STRING_1 | %FileCheck %s -check-prefix=INFIX_STRING // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INFIX_EXT_STRING_1 | %FileCheck %s -check-prefix=INFIX_EXT_STRING +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONFORM_SEQUENCE | %FileCheck %s -check-prefix=CONFORM_SEQUENCE + // NO_STDLIB_PRIVATE: Begin completions // NO_STDLIB_PRIVATE: End completions @@ -286,3 +288,14 @@ func testInfixOperator4(_ x: String) { // INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: && {#Bool#}[#Bool#] // INFIX_EXT_STRING-NOT: == // INFIX_EXT_STRING: End completions + +class TestSequence : Sequence { +#^CONFORM_SEQUENCE^# +// CONFORM_SEQUENCE: Begin completions +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Element = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[AssociatedType]/Super: typealias Iterator = {#(Type)#}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func makeIterator() -> some IteratorProtocol {|}; +// CONFORM_SEQUENCE-DAG: Decl[InstanceVar]/Super: var underestimatedCount: Int; +// CONFORM_SEQUENCE-DAG: Decl[InstanceMethod]/Super: func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? {|}; +// CONFORM_SEQUENCE: End completions +} diff --git a/test/IDE/complete_from_swift_module.swift b/test/IDE/complete_from_swift_module.swift index 0e321fd2d35ba..0f95c0560b344 100644 --- a/test/IDE/complete_from_swift_module.swift +++ b/test/IDE/complete_from_swift_module.swift @@ -4,6 +4,15 @@ // // Note: this test checks both module import case and file import case. +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=QUALIFYING_MODULE > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=QUALIFYING_MODULE < %t.compl.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=QUALIFYING_MODULE_2 > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=QUALIFYING_MODULE < %t.compl.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=ALREADY_QUALIFIED > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=ALREADY_QUALIFIED < %t.compl.txt + // RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=MODULE_QUALIFIED_1 > %t.compl.txt // RUN: %FileCheck %s -check-prefix=MODULE_QUALIFIED_1 < %t.compl.txt // @@ -21,6 +30,15 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=MODULE_QUALIFIED_5 | %FileCheck %s -check-prefix=ERROR_COMMON +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=STDLIB_TYPE_QUALIFIED_NESTED > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=STDLIB_TYPE_QUALIFIED_NESTED < %t.compl.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=STDLIB_TYPE_QUALIFIED > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=STDLIB_TYPE_QUALIFIED < %t.compl.txt + +// RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=MODULE_TYPE_QUALIFIED > %t.compl.txt +// RUN: %FileCheck %s -check-prefix=MODULE_TYPE_QUALIFIED < %t.compl.txt + // RUN: %target-swift-ide-test -code-completion -source-filename %s -I %t -code-completion-token=POSTFIX_OPERATOR_1 > %t.compl.txt // RUN: %FileCheck %s -check-prefix=POSTFIX_OPERATOR_1 < %t.compl.txt // RUN: %FileCheck %s -check-prefix=NEGATIVE_POSTFIX_OPERATOR_1 < %t.compl.txt @@ -41,6 +59,27 @@ import foo_swift_module import corrupted_module +func testQualifyingModulesSuggested() -> #^QUALIFYING_MODULE^# { + let x = #^QUALIFYING_MODULE_2^# + // QUALIFYING_MODULE: Begin completions + // QUALIFYING_MODULE-DAG: Decl[Module]/None: swift_ide_test[#Module#]; name=swift_ide_test + // QUALIFYING_MODULE-DAG: Decl[Module]/None: Swift[#Module#]; name=Swift + // QUALIFYING_MODULE-DAG: Decl[Module]/None: foo_swift_module[#Module#]; name=foo_swift_module + // QUALIFYING_MODULE: End completions +} + +struct SomeStructInThisModule {} +func testQualifyingModulesNotSuggested() { + let x: swift_ide_test.#^ALREADY_QUALIFIED^#; + // ALREADY_QUALIFIED: Begin completions + // ALREADY_QUALIFIED-NOT: Decl[Module] + // ALREADY_QUALIFIED-NOT: name=Type + // ALREADY_QUALIFIED: name=SomeStructInThisModule + // ALREADY_QUALIFIED-NOT: Decl[Module] + // ALREADY_QUALIFIED-NOT: name=Type + // ALREADY_QUALIFIED: End completions +} + var hiddenImport : Int // TOP_LEVEL_1_NEGATIVE-NOT: hiddenImport() @@ -99,3 +138,26 @@ func testPostfixOperator1(x: Int) { // TOP_LEVEL_1-DAG: Decl[GlobalVar]/Local: hiddenImport[#Int#]{{; name=.+$}} // TOP_LEVEL_1-DAG: Decl[GlobalVar]/OtherModule[foo_swift_module]: globalVar[#Int#]{{; name=.+$}} // TOP_LEVEL_1: End completions + +struct Foo: Swift.Array.#^STDLIB_TYPE_QUALIFIED_NESTED^# {} +// STDLIB_TYPE_QUALIFIED_NESTED: Begin completions +// STDLIB_TYPE_QUALIFIED_NESTED: Decl[TypeAlias]/CurrNominal: Index[#Int#]; name=Index +// STDLIB_TYPE_QUALIFIED_NESTED: Decl[TypeAlias]/CurrNominal: Element[#Element#]; name=Element +// STDLIB_TYPE_QUALIFIED_NESTED: Keyword/None: Type[#Array.Type#]; name=Type +// STDLIB_TYPE_QUALIFIED_NESTED: End completions + +struct Bar: Swift.#^STDLIB_TYPE_QUALIFIED^# {} +// STDLIB_TYPE_QUALIFIED: Begin completions +// STDLIB_TYPE_QUALIFIED-NOT: Decl[Module] +// STDLIB_TYPE_QUALIFIED: Decl[Struct]/OtherModule[Swift]: AnyCollection[#AnyCollection#]; name=AnyCollection +// STDLIB_TYPE_QUALIFIED-NOT: Decl[Module] +// STDLIB_TYPE_QUALIFIED: End completions + +func foo() -> foo_swift_module.#^MODULE_TYPE_QUALIFIED^# {} +// MODULE_TYPE_QUALIFIED: Begin completions +// MODULE_TYPE_QUALIFIED: Decl[Protocol]/OtherModule[foo_swift_module]: BarProtocol[#BarProtocol#]; name=BarProtocol +// MODULE_TYPE_QUALIFIED: Decl[Enum]/OtherModule[foo_swift_module]: MyQuickLookObject[#MyQuickLookObject#]; name=MyQuickLookObject +// MODULE_TYPE_QUALIFIED: Decl[Struct]/OtherModule[foo_swift_module]: BarGenericSwiftStruct1[#BarGenericSwiftStruct1#]; name=BarGenericSwiftStruct1 +// MODULE_TYPE_QUALIFIED: Decl[Struct]/OtherModule[foo_swift_module]: FooSwiftStruct[#FooSwiftStruct#]; name=FooSwiftStruct +// MODULE_TYPE_QUALIFIED: Decl[Struct]/OtherModule[foo_swift_module]: BarGenericSwiftStruct2[#BarGenericSwiftStruct2#]; name=BarGenericSwiftStruct2 +// MODULE_TYPE_QUALIFIED: End completions diff --git a/test/IDE/complete_function_builder.swift b/test/IDE/complete_function_builder.swift index 1d2e986bbaf0a..480a63359c530 100644 --- a/test/IDE/complete_function_builder.swift +++ b/test/IDE/complete_function_builder.swift @@ -1,13 +1,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_TOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_NONTOP | %FileCheck %s -check-prefix=IN_CLOSURE_TOP -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=IN_CLOSURE_COLOR_CONTEXT | %FileCheck %s -check-prefix=IN_CLOSURE_COLOR_CONTEXT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=IN_CLOSURE_COLOR_CONTEXT_DOT | %FileCheck %s -check-prefix=IN_CLOSURE_COLOR_CONTEXT_DOT - -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=CONTEXTUAL_TYPE_1 | %FileCheck %s -check-prefix=CONTEXTUAL_TYPE_VALID -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=CONTEXTUAL_TYPE_2 | %FileCheck %s -check-prefix=CONTEXTUAL_TYPE_VALID -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=CONTEXTUAL_TYPE_3 | %FileCheck %s -check-prefix=CONTEXTUAL_TYPE_VALID -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=CONTEXTUAL_TYPE_4 | %FileCheck %s -check-prefix=CONTEXTUAL_TYPE_VALID -// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-function-builder-one-way-constraints -code-completion-token=CONTEXTUAL_TYPE_5 | %FileCheck %s -check-prefix=CONTEXTUAL_TYPE_INVALID +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_CLOSURE_COLOR_CONTEXT | %FileCheck %s -check-prefix=IN_CLOSURE_COLOR_CONTEXT struct Tagged { let tag: Tag @@ -58,14 +51,14 @@ let globalStringVal: String = "" func testAcceptColorTagged(paramIntVal: Int, paramStringVal: String) { let taggedValue = paramIntVal.tag(Color.red) - + acceptColorTagged { color in #^IN_CLOSURE_TOP^# // IN_CLOSURE_TOP_CONTEXT: Begin completions -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: taggedValue[#Tagged#]; name=taggedValue +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local{{.*}}: taggedValue[#Tagged#]; name=taggedValue // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalIntVal[#Int#]; name=globalIntVal // IN_CLOSURE_TOP-DAG: Decl[GlobalVar]/CurrModule: globalStringVal[#String#]; name=globalStringVal -// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color; name=color +// IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: color{{.*}}; name=color // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramIntVal[#Int#]; name=paramIntVal // IN_CLOSURE_TOP-DAG: Decl[LocalVar]/Local: paramStringVal[#String#]; name=paramStringVal // IN_CLOSURE_TOP: End completions @@ -80,23 +73,12 @@ func testAcceptColorTagged(paramIntVal: Int, paramStringVal: String) { acceptColorTagged { color in paramIntVal.tag(#^IN_CLOSURE_COLOR_CONTEXT^#) // IN_CLOSURE_COLOR_CONTEXT: Begin completions -// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(tag): Color#}[')'][#Tagged#]; name=tag: Color -// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: color[#Color#]; name=color +// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: color; name=color // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: taggedValue[#Tagged#]; name=taggedValue // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: paramIntVal[#Int#]; name=paramIntVal // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: paramStringVal[#String#]; name=paramStringVal -// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[Enum]/CurrModule/TypeRelation[Identical]: Color[#Color#]; name=Color // IN_CLOSURE_COLOR_CONTEXT: End completions } - - acceptColorTagged { color in - paramIntVal.tag(.#^IN_CLOSURE_COLOR_CONTEXT_DOT^#) -// IN_CLOSURE_COLOR_CONTEXT_DOT: Begin completions, 3 items -// IN_CLOSURE_COLOR_CONTEXT_DOT-DAG: Decl[EnumElement]/ExprSpecific: red[#Color#]; name=red -// IN_CLOSURE_COLOR_CONTEXT_DOT-DAG: Decl[EnumElement]/ExprSpecific: green[#Color#]; name=green -// IN_CLOSURE_COLOR_CONTEXT_DOT-DAG: Decl[EnumElement]/ExprSpecific: blue[#Color#]; name=blue -// IN_CLOSURE_COLOR_CONTEXT_DOT: End completions - } } enum MyEnum { @@ -111,38 +93,3 @@ struct EnumToVoidBuilder { static func buildBlock(_ :MyEnum, _: MyEnum, _: MyEnum) {} } func acceptBuilder(@EnumToVoidBuilder body: () -> Void) {} - -// CONTEXTUAL_TYPE_INVALID-NOT: Begin completions - -// CONTEXTUAL_TYPE_VALID: Begin completions, 4 items -// CONTEXTUAL_TYPE_VALID-DAG: Decl[EnumElement]/ExprSpecific: east[#MyEnum#]; name=east -// CONTEXTUAL_TYPE_VALID-DAG: Decl[EnumElement]/ExprSpecific: west[#MyEnum#]; name=west -// CONTEXTUAL_TYPE_VALID-DAG: Decl[EnumElement]/ExprSpecific: north[#MyEnum#]; name=north -// CONTEXTUAL_TYPE_VALID-DAG: Decl[EnumElement]/ExprSpecific: south[#MyEnum#]; name=south -// CONTEXTUAL_TYPE_VALID: End completions - -func testContextualType() { - acceptBuilder { - .#^CONTEXTUAL_TYPE_1^# - } - acceptBuilder { - .#^CONTEXTUAL_TYPE_2^#; - .north; - } - acceptBuilder { - .north; - .#^CONTEXTUAL_TYPE_3^#; - } - acceptBuilder { - .north; - .east; - .#^CONTEXTUAL_TYPE_4^# - } - acceptBuilder { - .north; - .east; - .south; -// NOTE: Invalid because 'EnumToVoidBuilder' doesn't have 4 params overload. - .#^CONTEXTUAL_TYPE_5^# - } -} diff --git a/test/IDE/complete_import.swift b/test/IDE/complete_import.swift index 3d717b8c3501c..c207dc8bd52e2 100644 --- a/test/IDE/complete_import.swift +++ b/test/IDE/complete_import.swift @@ -12,9 +12,9 @@ import #^CLANG_IMPORT1^# // CLANG_IMPORT1: Begin completions -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[Foo]: Foo[#Module#]; name=Foo -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[FooHelper]: FooHelper[#Module#]; name=FooHelper -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[Bar]: Bar[#Module#]; name=Bar +// CLANG_IMPORT1-DAG: Decl[Module]/None: Foo[#Module#]; name=Foo +// CLANG_IMPORT1-DAG: Decl[Module]/None: FooHelper[#Module#]; name=FooHelper +// CLANG_IMPORT1-DAG: Decl[Module]/None: Bar[#Module#]; name=Bar // CLANG_IMPORT1-NOT: SwiftShims import Foo @@ -22,15 +22,15 @@ import Foo import #^CLANG_IMPORT2^# // CLANG_IMPORT2: Begin completions -// CLANG_IMPORT2-DAG: Decl[Module]/OtherModule[Foo]/NotRecommended: Foo[#Module#]; name=Foo -// CLANG_IMPORT2-DAG: Decl[Module]/OtherModule[FooHelper]/NotRecommended: FooHelper[#Module#]; name=FooHelper -// CLANG_IMPORT2-DAG: Decl[Module]/OtherModule[Bar]: Bar[#Module#]; name=Bar -// CLANG_IMPORT2-NOT: SwiftShims +// CLANG_IMPORT2-DAG: Decl[Module]/None/NotRecommended: Foo[#Module#]; name=Foo +// CLANG_IMPORT2-DAG: Decl[Module]/None/NotRecommended: FooHelper[#Module#]; name=FooHelper +// CLANG_IMPORT2-DAG: Decl[Module]/None: Bar[#Module#]; name=Bar +// CLANG_IMPORT2-NOT: SwiftShims import Foo.#^CLANG_IMPORT3^# // CLANG_IMPORT3: Begin completions -// CLANG_IMPORT3-NEXT: Decl[Module]/OtherModule[FooSub]: FooSub[#Module#]; name=FooSub +// CLANG_IMPORT3-NEXT: Decl[Module]/None: FooSub[#Module#]; name=FooSub import Foo.FooSub @@ -38,7 +38,7 @@ import Foo.#^CLANG_IMPORT8^# // FIXME: This should be marked as not recommended, holding for Swift's submodules support. // CLANG_IMPORT8: Begin completions -// CLANG_IMPORT8-NEXT: Decl[Module]/OtherModule[FooSub]: FooSub[#Module#]; name=FooSub +// CLANG_IMPORT8-NEXT: Decl[Module]/None: FooSub[#Module#]; name=FooSub import Foo#^CLANG_IMPORT4^# // CLANG_IMPORT4-NOT: Begin completions diff --git a/test/IDE/complete_import_multifile1.swift b/test/IDE/complete_import_multifile1.swift index 5f4b877471291..0b44a60332152 100644 --- a/test/IDE/complete_import_multifile1.swift +++ b/test/IDE/complete_import_multifile1.swift @@ -5,7 +5,7 @@ import #^CLANG_IMPORT1^# // CLANG_IMPORT1: Begin completions -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[Foo]: Foo[#Module#]; name=Foo -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[FooHelper]: FooHelper[#Module#]; name=FooHelper -// CLANG_IMPORT1-DAG: Decl[Module]/OtherModule[Bar]: Bar[#Module#]; name=Bar +// CLANG_IMPORT1-DAG: Decl[Module]/None: Foo[#Module#]; name=Foo +// CLANG_IMPORT1-DAG: Decl[Module]/None: FooHelper[#Module#]; name=FooHelper +// CLANG_IMPORT1-DAG: Decl[Module]/None: Bar[#Module#]; name=Bar // CLANG_IMPORT1-NOT: SwiftShims diff --git a/test/IDE/complete_member_decls_from_parent_decl_context.swift b/test/IDE/complete_member_decls_from_parent_decl_context.swift index ca15843815cdb..55a28528f30e2 100644 --- a/test/IDE/complete_member_decls_from_parent_decl_context.swift +++ b/test/IDE/complete_member_decls_from_parent_decl_context.swift @@ -83,9 +83,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_INSTANCE_METHOD_1: End completions } @@ -100,9 +100,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInClassMethods1.Type#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): CodeCompletionInClassMethods1#})[#() -> Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): CodeCompletionInClassMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -116,9 +116,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_CONSTRUCTOR_1: End completions } @@ -130,9 +130,9 @@ class CodeCompletionInClassMethods1 { // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInClassMethods1.NestedStruct#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInClassMethods1.NestedClass#]{{; name=.+$}} -// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInClassMethods1.NestedEnum#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_CLASS_DESTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_CLASS_DESTRUCTOR_1: End completions } @@ -184,9 +184,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_INSTANCE_METHOD_1: End completions } @@ -201,9 +201,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[LocalVar]/Local: self[#CodeCompletionInStructMethods1.Type#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0({#(self): &CodeCompletionInStructMethods1#})[#() -> Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(self): &CodeCompletionInStructMethods1#})[#(Int) -> Void#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_STATIC_METHOD_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_STATIC_METHOD_1-DAG: Decl[StaticMethod]/CurrNominal: staticFunc1({#(a): Int#})[#Void#]{{; name=.+$}} @@ -217,9 +217,9 @@ struct CodeCompletionInStructMethods1 { // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#CodeCompletionInStructMethods1.NestedStruct#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#CodeCompletionInStructMethods1.NestedClass#]{{; name=.+$}} -// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#CodeCompletionInStructMethods1.NestedEnum#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // IN_STRUCT_CONSTRUCTOR_1: End completions } @@ -241,9 +241,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceVar]/CurrNominal: aInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_1-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_1-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -264,9 +264,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_A_2-DAG: Decl[InstanceMethod]/CurrNominal: aInstanceFunc({#(self): &NestedInnerA#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticVar]/CurrNominal: aStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[StaticMethod]/CurrNominal: aStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/CurrNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Class]/CurrNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_2-DAG: Decl[Enum]/CurrNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_2-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -281,9 +281,9 @@ struct NestedOuter1 { typealias ATestTypealias = #^NESTED_NOMINAL_DECL_A_3^# // NESTED_NOMINAL_DECL_A_3: Begin completions -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerA.NestedInnerAStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerA.NestedInnerAClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerA.NestedInnerAEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/OutNominal: NestedInnerAStruct[#NestedInnerAStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Class]/OutNominal: NestedInnerAClass[#NestedInnerAClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_A_3-DAG: Decl[Enum]/OutNominal: NestedInnerAEnum[#NestedInnerAEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_A_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerATypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_A_3-DAG: Decl[Struct]/Local: NestedInnerA[#NestedInnerA#]{{; name=.+$}} @@ -343,9 +343,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceVar]/CurrNominal: bInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_1-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_1-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -369,9 +369,9 @@ struct NestedOuter1 { // NESTED_NOMINAL_DECL_B_2-DAG: Decl[InstanceMethod]/CurrNominal: bInstanceFunc({#(self): &NestedInnerB#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticVar]/CurrNominal: bStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[StaticMethod]/CurrNominal: bStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/CurrNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Class]/CurrNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_2-DAG: Decl[Enum]/CurrNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_2-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -389,9 +389,9 @@ struct NestedOuter1 { typealias BTestTypealias = #^NESTED_NOMINAL_DECL_B_3^# // NESTED_NOMINAL_DECL_B_3: Begin completions -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerB.NestedInnerBStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerB.NestedInnerBClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerB.NestedInnerBEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/OutNominal: NestedInnerBStruct[#NestedInnerBStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Class]/OutNominal: NestedInnerBClass[#NestedInnerBClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_B_3-DAG: Decl[Enum]/OutNominal: NestedInnerBEnum[#NestedInnerBEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_B_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerBTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_B_3-DAG: Decl[Struct]/Local: NestedInnerB[#NestedInnerB#]{{; name=.+$}} @@ -461,9 +461,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cTestInstanceFunc()[#Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceVar]/CurrNominal: cInstanceVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_1-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_1-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_1-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -479,9 +479,9 @@ func testOuterC() { // NESTED_NOMINAL_DECL_C_2-DAG: Decl[InstanceMethod]/CurrNominal: cInstanceFunc({#(self): &NestedInnerC#})[#() -> Void#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticVar]/CurrNominal: cStaticVar[#Int#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[StaticMethod]/CurrNominal: cStaticFunc()[#Void#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/CurrNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Class]/CurrNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_2-DAG: Decl[Enum]/CurrNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_2-DAG: Decl[TypeAlias]/CurrNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_2-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} @@ -491,9 +491,9 @@ func testOuterC() { typealias CTestTypealias = #^NESTED_NOMINAL_DECL_C_3^# // NESTED_NOMINAL_DECL_C_3: Begin completions -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerC.NestedInnerCStruct#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerC.NestedInnerCClass#]{{; name=.+$}} -// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerC.NestedInnerCEnum#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/OutNominal: NestedInnerCStruct[#NestedInnerCStruct#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Class]/OutNominal: NestedInnerCClass[#NestedInnerCClass#]{{; name=.+$}} +// NESTED_NOMINAL_DECL_C_3-DAG: Decl[Enum]/OutNominal: NestedInnerCEnum[#NestedInnerCEnum#]{{; name=.+$}} // NESTED_NOMINAL_DECL_C_3-DAG: Decl[TypeAlias]/OutNominal: NestedInnerCTypealias[#Int#]{{; name=.+$}} // FIXME: should this really come as Local? // NESTED_NOMINAL_DECL_C_3-DAG: Decl[Struct]/Local: NestedInnerC[#NestedInnerC#]{{; name=.+$}} diff --git a/test/IDE/complete_multifile.swift b/test/IDE/complete_multifile.swift new file mode 100644 index 0000000000000..8dd332f36d5ec --- /dev/null +++ b/test/IDE/complete_multifile.swift @@ -0,0 +1,104 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_PAREN | %FileCheck --check-prefix=POINT_PAREN %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_DOT | %FileCheck --check-prefix=POINT_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=LENS_DOT | %FileCheck --check-prefix=LENS_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=MYENUM_DOT | %FileCheck --check-prefix=MYENUM_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=MYENUM_INSTANCE_DOT | %FileCheck --check-prefix=MYENUM_INSTANCE_DOT %s +// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=HASWRAPPED_DOT| %FileCheck --check-prefix=HASWRAPPED_DOT %s + +// BEGIN Library.swift + +public struct Point { + var x: Int + var y: Int +} + +@dynamicMemberLookup +struct Lens { + var obj: T + init(_ obj: T) { self.obj = obj } + + subscript(dynamicMember member: WritableKeyPath) -> Lens { + get { return Lens(obj[keyPath: member]) } + set { obj[keyPath: member] = newValue.obj } + } +} + +enum MyEnum: String, CaseIterable { + case foo = "foo" + case bar = "bar" +} + +@propertyWrapper +struct Wrap { + var wrappedValue: T + + public var projectedValue: Self { + get { self } + set { self = newValue } + } +} + +struct HasWrapped { + @Wrap + var wrapped: Int = 1 +} + +// BEGIN Main.swift + +func testStructDefaultInit() { + Point(#^POINT_PAREN^# +// POINT_PAREN: Begin completions, 1 items +// POINT_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}, {#y: Int#}[')'][#Point#]; +// POINT_PAREN: End completions + func sync() {} + Point.#^POINT_DOT^# +// POINT_DOT: Begin completions, 3 items +// POINT_DOT-DAG: Keyword[self]/CurrNominal: self[#Point.Type#]; +// POINT_DOT-DAG: Keyword/CurrNominal: Type[#Point.Type#]; +// POINT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#}, {#y: Int#})[#Point#]; +// POINT_DOT: End completions +} + +func testDynamicMemberLookup(lens: Lens) { + _ = lens.#^LENS_DOT^# +// LENS_DOT: Begin completions, 4 items +// LENS_DOT-DAG: Keyword[self]/CurrNominal: self[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: x[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: y[#Lens#]; +// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: obj[#Point#]; +// LENS_DOT: End completions +} +func testRawRepresentable() { + MyEnum.#^MYENUM_DOT^# +// MYENUM_DOT: Begin completions, 9 items +// MYENUM_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum.Type#]; +// MYENUM_DOT-DAG: Keyword/CurrNominal: Type[#MyEnum.Type#]; +// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: foo[#MyEnum#]; +// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: bar[#MyEnum#]; +// MYENUM_DOT-DAG: Decl[TypeAlias]/CurrNominal: RawValue[#String#]; +// MYENUM_DOT-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: String#})[#MyEnum?#]; +// MYENUM_DOT-DAG: Decl[TypeAlias]/CurrNominal: AllCases[#[MyEnum]#]; +// MYENUM_DOT-DAG: Decl[StaticVar]/CurrNominal: allCases[#[MyEnum]#]; +// MYENUM_DOT-DAG: Decl[InstanceMethod]/Super: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; +// MYENUM_DOT: End completions +} +func testRawRepesentableInstance(value: MyEnum) { + value.#^MYENUM_INSTANCE_DOT^# +// MYENUM_INSTANCE_DOT: Begin completions, 4 items +// MYENUM_INSTANCE_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum#]; +// MYENUM_INSTANCE_DOT-DAG: Decl[InstanceVar]/CurrNominal: rawValue[#String#]; +// MYENUM_INSTANCE_DOT-DAG: Decl[InstanceVar]/Super: hashValue[#Int#]; +// MYENUM_INSTANCE_DOT-DAG: Decl[InstanceMethod]/Super: hash({#into: &Hasher#})[#Void#]; +// MYENUM_INSTANCE_DOT: End completions +} +func testHasWrappedValue(value: HasWrapped) { + value.#^HASWRAPPED_DOT^# +// HASWRAPPED_DOT: Begin completions, 3 items +// HASWRAPPED_DOT: Keyword[self]/CurrNominal: self[#HasWrapped#]; +// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: wrapped[#Int#]; +// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: $wrapped[#Wrap#]; +// HASWRAPPED_DOT: End completions +} diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index 1d376c8710a67..ef7accb15c7b2 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -92,7 +92,7 @@ protocol HasAssocWithSuperClassConstraint { } protocol HasAssocWithCompositionConstraint { associatedtype AssocWithCompositionConstraint: MyClass & MyProtocol - subscript(idx: T) -> AssocWithCompositionConstraint where T: Comparable { get } + subscript(idx: Int) -> AssocWithCompositionConstraint { get } } protocol HasAssocWithDefault { associatedtype AssocWithDefault = MyEnum @@ -102,6 +102,22 @@ protocol HasAssocWithConstraintAndDefault { associatedtype AssocWithConstraintAndDefault: MyProtocol = ConcreteMyProtocol func returnAssocWithConstraintAndDefault() -> AssocWithConstraintAndDefault } +protocol HasAssocWithAnyObjectConstraint { + associatedtype AssocWithAnyObjectConstraint: AnyObject & MyProtocol + func returnAssocWithAnyObjectConstraint() -> AssocWithAnyObjectConstraint +} +protocol HasAssocWithConstraintOnProto where Self.AssocWithConstraintOnProto : MyProtocol { + associatedtype AssocWithConstraintOnProto + func returnAssocWithConstraintOnProto() -> AssocWithConstraintOnProto +} +protocol HasAssocWithSameTypeConstraint where Self.AssocWithSameTypeConstraint == ConcreteMyProtocol { + associatedtype AssocWithSameTypeConstraint : MyProtocol + func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint +} +protocol HasAssocWithConformanceConstraintGeneric { + associatedtype AssocWithConformanceConstraintGeneric: MyProtocol + func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric +} class TestClass : HasAssocPlain, @@ -109,16 +125,23 @@ class TestClass : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestClass^# -// OVERRIDE: found code completion token OVERRIDE_[[BASETYPE:[A-Za-z0-9]+]] at // OVERRIDE: Begin completions -// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> [[BASETYPE]].AssocPlain {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocPlain() -> AssocPlain {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraint(fn: (Int) -> Int) -> some MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceVar]/Super: var valAssocWithSuperClassConstraint: some MyClass; -// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: T) -> some MyClass & MyProtocol where T : Comparable {|}; +// OVERRIDE-DAG: Decl[Subscript]/Super: subscript(idx: Int) -> some MyClass & MyProtocol {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithDefault() -> MyEnum {|}; // OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintAndDefault() -> ConcreteMyProtocol {|}; +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithAnyObjectConstraint() -> some MyProtocol & AnyObject {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConstraintOnProto() -> some MyProtocol {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithSameTypeConstraint() -> AssocWithSameTypeConstraint {|} +// OVERRIDE-DAG: Decl[InstanceMethod]/Super: func returnAssocWithConformanceConstraintGeneric(arg: T) -> AssocWithConformanceConstraintGeneric {|} // OVERRIDE: End completions } @@ -128,7 +151,11 @@ struct TestStruct : HasAssocWithSuperClassConstraint, HasAssocWithCompositionConstraint, HasAssocWithDefault, - HasAssocWithConstraintAndDefault { + HasAssocWithConstraintAndDefault, + HasAssocWithAnyObjectConstraint, + HasAssocWithConstraintOnProto, + HasAssocWithSameTypeConstraint, + HasAssocWithConformanceConstraintGeneric { #^OVERRIDE_TestStruct^# } diff --git a/test/IDE/complete_override.swift b/test/IDE/complete_override.swift index 2970a0efc6980..4d85e3a36a40d 100644 --- a/test/IDE/complete_override.swift +++ b/test/IDE/complete_override.swift @@ -724,9 +724,10 @@ class Override26 : OverrideBase, OverrideP { // Same as MODIFIER24 } -// MODIFIER1: Begin completions, 9 items +// MODIFIER1: Begin completions, 10 items // MODIFIER1-DAG: Decl[Constructor]/Super: required init(p: Int) {|}; name=required init(p: Int) // MODIFIER1-DAG: Decl[StaticMethod]/Super: override class func classMethod() {|}; name=classMethod() +// MODIFIER1-DAG: Decl[StaticVar]/Super: override class var classVar: Int; name=classVar: Int // MODIFIER1-DAG: Decl[StaticVar]/Super: override class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER1-DAG: Decl[InstanceMethod]/Super: override func defaultMethod() {|}; name=defaultMethod() // MODIFIER1-DAG: Decl[InstanceMethod]/Super: override func openMethod() {|}; name=openMethod() @@ -736,7 +737,8 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER1-DAG: Decl[AssociatedType]/Super: typealias Assoc = {#(Type)#}; name=Assoc = Type // MODIFIER1: End completions -// MODIFIER2: Begin completions, 5 items +// MODIFIER2: Begin completions, 6 items +// MODIFIER2-DAG: Decl[StaticVar]/Super: override class var classVar: Int; name=classVar: Int // MODIFIER2-DAG: Decl[StaticVar]/Super: override class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER2-DAG: Decl[StaticMethod]/Super: override class func classMethod() {|}; name=classMethod() // MODIFIER2-DAG: Decl[InstanceMethod]/Super: override func defaultMethod() {|}; name=defaultMethod() @@ -760,7 +762,8 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER6-DAG: Decl[AssociatedType]/Super: Assoc = {#(Type)#}; name=Assoc = Type // MODIFIER6: End completions -// MODIFIER7: Begin completions, 7 items +// MODIFIER7: Begin completions, 8 items +// MODIFIER7-DAG: Decl[StaticVar]/Super: class var classVar: Int; name=classVar: Int // MODIFIER7-DAG: Decl[StaticVar]/Super: class var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER7-DAG: Decl[StaticMethod]/Super: class func classMethod() {|}; name=classMethod() // MODIFIER7-DAG: Decl[InstanceMethod]/Super: func defaultMethod() {|}; name=defaultMethod() @@ -785,11 +788,13 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER13-NOT: Begin completions -// MODIFIER15: Begin completions, 1 items +// MODIFIER15: Begin completions, 2 items +// MODIFIER15-DAG: Decl[StaticVar]/Super/Erase[4]: override var classVar: Int; name=classVar: Int // MODIFIER15-DAG: Decl[StaticVar]/Super/Erase[4]: override var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER15: End completions -// MODIFIER17: Begin completions, 1 items +// MODIFIER17: Begin completions, 2 items +// MODIFIER17-DAG: Decl[StaticVar]/Super: classVar: Int; name=classVar: Int // MODIFIER17-DAG: Decl[StaticVar]/Super: classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER17: End completions @@ -801,13 +806,15 @@ class Override26 : OverrideBase, OverrideP { // MODIFIER22: Decl[StaticMethod]/Super/Erase[5]: override func classMethod() {|}; name=classMethod() // MODIFIER22: End completions -// MODIFIER23: Begin completions, 2 items +// MODIFIER23: Begin completions, 3 items // MODIFIER23-DAG: Decl[StaticMethod]/Super: override func classMethod() {|}; name=classMethod() +// MODIFIER23-DAG: Decl[StaticVar]/Super: override var classVar: Int; name=classVar: Int // MODIFIER23-DAG: Decl[StaticVar]/Super: override var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER23: End completions -// MODIFIER24: Begin completions, 2 items +// MODIFIER24: Begin completions, 3 items // MODIFIER24-DAG: Decl[StaticMethod]/Super: func classMethod() {|}; name=classMethod() +// MODIFIER24-DAG: Decl[StaticVar]/Super: var classVar: Int; name=classVar: Int // MODIFIER24-DAG: Decl[StaticVar]/Super: var classGetOnlyVar: Int; name=classGetOnlyVar: Int // MODIFIER24: End completions @@ -850,7 +857,7 @@ struct MissingAssoc: AssocAndMethod { func #^MISSING_ASSOC_1^# } // MISSING_ASSOC_1: Begin completions -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: MissingAssoc.T) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: MissingAssoc.U) {|}; -// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: MissingAssoc.V) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f1(_: T) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f2(_: U) {|}; +// MISSING_ASSOC_1-DAG: Decl[InstanceMethod]/Super: f3(_: V) {|}; // MISSING_ASSOC_1: End completions diff --git a/test/IDE/complete_override_typealias.swift b/test/IDE/complete_override_typealias.swift index 0485e4a279c70..dd0e77cc53474 100644 --- a/test/IDE/complete_override_typealias.swift +++ b/test/IDE/complete_override_typealias.swift @@ -8,8 +8,10 @@ protocol MyProtocol { associatedtype Result typealias Arg1 = Generic typealias Arg2 = Generic + typealias Func = () -> Int func foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2? arg6: Generic>>>) -> Result func bar() -> Result + func baz(arg: @escaping Func) } struct MyStruct1 : MyProtocol { @@ -29,17 +31,20 @@ struct MyStruct3 : MyProtocol { func #^OVERRIDE_3^# } -// OVERRIDE_1: Begin completions, 2 items -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct1.Arg1, arg2: MyStruct1.Arg2, arg3: MyStruct1.Arg2, arg4: [MyStruct1.Arg1], arg5: MyStruct1.Arg2?, arg6: Generic>>>) -> MyStruct1.Result {|}; -// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> MyStruct1.Result {|}; +// OVERRIDE_1: Begin completions, 3 items +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: bar() -> Result {|}; +// OVERRIDE_1-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_1: End completions -// OVERRIDE_2: Begin completions, 2 items -// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct2.Arg1, arg2: MyStruct2.Arg2, arg3: MyStruct2.Arg2, arg4: [MyStruct2.Arg1], arg5: MyStruct2.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_2: Begin completions, 3 items +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; // OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: bar() -> String {|}; +// OVERRIDE_2-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_2: End completions -// OVERRIDE_3-DAG: Begin completions, 1 items -// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: MyStruct3.Arg1, arg2: MyStruct3.Arg2, arg3: MyStruct3.Arg2, arg4: [MyStruct3.Arg1], arg5: MyStruct3.Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Begin completions, 2 items +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: foo(arg1: Arg1, arg2: Arg2, arg3: Arg2, arg4: [Arg1], arg5: Arg2?, arg6: Generic>>>) -> String {|}; +// OVERRIDE_3-DAG: Decl[InstanceMethod]/Super: baz(arg: @escaping Func) {|}; // OVERRIDE_3-DAG: End completions diff --git a/test/IDE/complete_single_expression_return.swift b/test/IDE/complete_single_expression_return.swift index 4e7a4f0212a38..1df478228eb2d 100644 --- a/test/IDE/complete_single_expression_return.swift +++ b/test/IDE/complete_single_expression_return.swift @@ -98,7 +98,7 @@ struct TestSingleExprClosureRetUnresolved { // TestSingleExprClosureRetUnresolved: Begin completions // TestSingleExprClosureRetUnresolved-NOT: notMine -// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExprClosure{{(Ret)?}}Unresolved.MyEnum#]; +// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprClosureRetUnresolved-NOT: notMine // TestSingleExprClosureRetUnresolved: End completions } @@ -263,7 +263,7 @@ struct TestSingleExprFuncUnresolved { // TestSingleExprFuncUnresolved: Begin completions // TestSingleExprFuncUnresolved-NOT: notMine -// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}FuncUnresolved.MyEnum#]; +// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprFuncUnresolved-NOT: notMine // TestSingleExprFuncUnresolved: End completions } @@ -373,7 +373,7 @@ struct TestSingleExprAccessorUnresolved { // TestSingleExprAccessorUnresolved: Begin completions // TestSingleExprAccessorUnresolved-NOT: notMine -// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Accessor{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprAccessorUnresolved-NOT: notMine // TestSingleExprAccessorUnresolved: End completions } @@ -525,7 +525,7 @@ struct TestSingleExprSubscriptUnresolved { // TestSingleExprSubscriptUnresolved: Begin completions // TestSingleExprSubscriptUnresolved-NOT: notMine -// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExpr{{(Local)?}}Subscript{{(Get)?}}Unresolved.MyEnum#]; +// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; // TestSingleExprSubscriptUnresolved-NOT: notMine // TestSingleExprSubscriptUnresolved: End completions } @@ -556,7 +556,7 @@ struct TestSingleExprSubscriptGlobal { // TestSingleExprSubscriptGlobal: Begin completions // TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#]; -// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: int()[#Int#]; +// TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#]; // TestSingleExprSubscriptGlobal-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#]; // TestSingleExprSubscriptGlobal: End completions } diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index 16a7b8323f948..37a3a5d039f8a 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -117,7 +117,8 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_4 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT_BOOL // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_5 | %FileCheck %s -check-prefix=FOOSTRUCT_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_6 | %FileCheck %s -check-prefix=FOOSTRUCT_NODOT - +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_7 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GUARD_LET_BIND_8 | %FileCheck %s -check-prefix=FOOSTRUCT_LOCALVAL struct FooStruct { var instanceVar : Int @@ -605,6 +606,13 @@ func testGuardLetBinding5(x: FooStruct?) { func testGuardLetBinding5(x: FooStruct?) { guard let y = x, z = y#^GUARD_LET_BIND_6^# else {} } +func testGuardLetBinding7(x: FooStruct?) { + guard let boundVal = x, let other = #^GUARD_LET_BIND_7^# else {} +} +func testGuardLetBinding8(_ x: FooStruct?) { + guard let boundVal = x, let other = testGuardLetBinding8(#^GUARD_LET_BIND_8^#) else {} +} + // FOOSTRUCT_DOT: Begin completions // FOOSTRUCT_DOT-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]; @@ -623,3 +631,7 @@ func testGuardLetBinding5(x: FooStruct?) { // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .boolGen()[#Bool#]; // FOOSTRUCT_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .intGen()[#Int#]; // FOOSTRUCT_NODOT: End completions + +// FOOSTRUCT_LOCALVAL: Begin completions +// FOOSTRUCT_LOCALVAL-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: boundVal[#FooStruct#]; +// FOOSTRUCT_LOCALVAL: End completions diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index 8b5ef8fe2f1b4..c3ffb661adffd 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -247,64 +247,64 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_FUNC_PARAM_NESTED_TYPES_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_INSTANCE_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_1 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_2 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_BASE_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_3 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_IN_LOCAL_VAR_4 > %t.types.txt -// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES < %t.types.txt +// RUN: %FileCheck %s -check-prefix=VAR_DERIVED_1_TYPES_INCONTEXT < %t.types.txt // RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_TYPES < %t.types.txt // RUN: %FileCheck %s -check-prefix=GLOBAL_NEGATIVE < %t.types.txt @@ -400,8 +400,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNBOUND_DOT | %FileCheck %s -check-prefix=UNBOUND_DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNBOUND_DOT_2 | %FileCheck %s -check-prefix=UNBOUND_DOT_2 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICARG_OPTIONAL | %FileCheck %s -check-prefix=GENERICARG_OPTIONAL - //===--- Helper types that are used in this test struct FooStruct { @@ -673,9 +671,9 @@ class TestTypeInLocalVarInMemberFunc1 { } } // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: Begin completions -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#TestTypeInLocalVarInMemberFunc1.NestedStruct#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#TestTypeInLocalVarInMemberFunc1.NestedClass#]{{; name=.+$}} -// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#TestTypeInLocalVarInMemberFunc1.NestedEnum#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Struct]/CurrNominal: NestedStruct[#NestedStruct#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Class]/CurrNominal: NestedClass[#NestedClass#]{{; name=.+$}} +// TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[Enum]/CurrNominal: NestedEnum[#NestedEnum#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1-DAG: Decl[TypeAlias]/CurrNominal: NestedTypealias[#Int#]{{; name=.+$}} // TYPE_IN_LOCAL_VAR_IN_MEMBER_FUNC_1: End completions @@ -913,6 +911,19 @@ extension VarBase1 { // VAR_BASE_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_BASE_1_TYPES: End completions +// VAR_BASE_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseNestedStruct[#BaseNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseNestedClass[#BaseNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseNestedEnum[#BaseNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: BaseExtNestedStruct[#BaseExtNestedStruct#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: BaseExtNestedClass[#BaseExtNestedClass#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: BaseExtNestedEnum[#BaseExtNestedEnum#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_BASE_1_TYPES_INCONTEXT: End completions + // VAR_BASE_1_NO_DOT_TYPES: Begin completions // From VarBase1 // VAR_BASE_1_NO_DOT_TYPES-DAG: Decl[Struct]/CurrNominal: .BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} @@ -989,6 +1000,29 @@ extension VarDerived1 { // VAR_DERIVED_1_TYPES-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} // VAR_DERIVED_1_TYPES: End completions +// VAR_DERIVED_1_TYPES_INCONTEXT: Begin completions +// From VarBase1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseNestedStruct[#VarBase1.BaseNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseNestedClass[#VarBase1.BaseNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseNestedEnum[#VarBase1.BaseNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseNestedTypealias[#Int#]{{; name=.+$}} +// From VarBase1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/Super: BaseExtNestedStruct[#VarBase1.BaseExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/Super: BaseExtNestedClass[#VarBase1.BaseExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/Super: BaseExtNestedEnum[#VarBase1.BaseExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/Super: BaseExtNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedNestedStruct[#DerivedNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedNestedClass[#DerivedNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedNestedEnum[#DerivedNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedNestedTypealias[#Int#]{{; name=.+$}} +// From VarDerived1 extension +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Struct]/CurrNominal: DerivedExtNestedStruct[#DerivedExtNestedStruct#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Class]/CurrNominal: DerivedExtNestedClass[#DerivedExtNestedClass#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[Enum]/CurrNominal: DerivedExtNestedEnum[#DerivedExtNestedEnum#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT-DAG: Decl[TypeAlias]/CurrNominal: DerivedExtNestedTypealias[#Int#]{{; name=.+$}} +// VAR_DERIVED_1_TYPES_INCONTEXT: End completions + //===--- //===--- Test that we can complete based on user-provided type-identifier. //===--- @@ -1131,6 +1165,3 @@ func testUnbound2(x: OuterStruct.Inner.#^UNBOUND_DOT_2^#) {} // UNBOUND_DOT_2: Begin completions // UNBOUND_DOT_2-DAG: Keyword/None: Type[#OuterStruct.Inner.Type#]; name=Type // UNBOUND_DOT_2: End completions - -func testGenericArgForOptional() -> Set<#^GENERICARG_OPTIONAL^#>? {} -// GENERICARG_OPTIONAL: Begin completions diff --git a/test/IDE/complete_type_subscript.swift b/test/IDE/complete_type_subscript.swift index e93fcf59a96e6..5411068a60b6c 100644 --- a/test/IDE/complete_type_subscript.swift +++ b/test/IDE/complete_type_subscript.swift @@ -20,7 +20,7 @@ struct S1 { subscript(x: MyStruct#^PARAM_1^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct#^RETURN_1^# { } } -// MYSTRUCT_0: Keyword/None: .Type[#S1.MyStruct.Type#]; +// MYSTRUCT_0: Keyword/None: .Type[#MyStruct.Type#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_2 | %FileCheck %s -check-prefix=MYSTRUCT_1 @@ -30,8 +30,8 @@ struct S2 { subscript(x: MyStruct.#^PARAM_2^#) -> Int { return 0 } subscript(x: MyStruct) -> MyStruct.#^RETURN_2^# { } } -// MYSTRUCT_1: Keyword/None: Type[#S2.MyStruct.Type#]; -// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#S2.MyStruct#]; +// MYSTRUCT_1: Keyword/None: Type[#MyStruct.Type#]; +// MYSTRUCT_1-NOT: Keyword/CurrNominal: self[#MyStruct#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_PARAM_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GEN_RETURN_0 | %FileCheck %s -check-prefix=GEN_TOP_LEVEL_0 diff --git a/test/IDE/complete_uninferred_generic.swift b/test/IDE/complete_uninferred_generic.swift new file mode 100644 index 0000000000000..5ae930730aaff --- /dev/null +++ b/test/IDE/complete_uninferred_generic.swift @@ -0,0 +1,18 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNINFERRED | %FileCheck %s -check-prefix=UNINFERRED + +struct S1 {} +protocol P1 { + associatedtype A1 +} +extension P1 where A1 == S1 { + subscript(v0: T) -> Int { fatalError() } + subscript(v0: T) -> Undefined { fatalError() } +} +struct S2 : P1 { + typealias A1 = S1 +} +_ = S2()#^UNINFERRED^# + +// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#Int#]; name=[v0: T] +// UNINFERRED: Decl[Subscript]/Super: [{#(v0): T#}][#<>#]; name=[v0: T] +// UNINFERRED: Keyword[self]/CurrNominal: .self[#S2<_>#]; name=self diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index c19557f524fff..9feb9e6e2b4b2 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -45,15 +45,21 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_34 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_35 | %FileCheck %s -check-prefix=UNRESOLVED_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_36 | %FileCheck %s -check-prefix=UNRESOLVED_3 -// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_37 | %FileCheck %s -check-prefix=UNRESOLVED_3 -// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_38 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_37 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_38 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_39 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_40 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_41 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_42 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_43 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN-FIXME: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_44 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_AVAIL_1 | %FileCheck %s -check-prefix=ENUM_AVAIL_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OPTIONS_AVAIL_1 | %FileCheck %s -check-prefix=OPTIONS_AVAIL_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_1 | %FileCheck %s -check-prefix=WITH_LITERAL_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_2 | %FileCheck %s -check-prefix=WITH_LITERAL_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=WITH_LITERAL_3 | %FileCheck %s -check-prefix=WITH_LITERAL_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INVALID_1 @@ -109,6 +115,14 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPEPARAM_IN_CONTEXTTYPE_1 | %FileCheck %s -check-prefix=TYPEPARAM_IN_CONTEXTTYPE_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_2 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_3 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_4 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_5 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_6 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TERNARY_CONDITION | %FileCheck %s -check-prefix=TERNARY_CONDITION + enum SomeEnum1 { case South case North @@ -119,7 +133,7 @@ enum SomeEnum2 { case West } -enum SomeEnum3 { +enum SomeEnum3: Hashable { case Payload(SomeEnum1) } @@ -388,6 +402,12 @@ let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .South:.#^UNRESOLVED_35^#] let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_36^#:.Option1] let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_37^#] let _: [SomeEnum1:SomeOptions1] = [.South:.Option1, .#^UNRESOLVED_38^#:] +let _: [SomeEnum1:SomeOptions1] = [.#^UNRESOLVED_39^#] +let _: [SomeEnum1:SomeOptions1] = [.#^UNRESOLVED_40^#:] +let _: [SomeEnum1:SomeEnum3] = [.South:.Payload(.South), .South: .Payload(.#^UNRESOLVED_41^#)] +let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_42^#):.South] +let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_43^#):] +let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_44^#)] func testAvail1(_ x: EnumAvail1) { testAvail1(.#^ENUM_AVAIL_1^#) @@ -443,6 +463,9 @@ func testWithLiteral3() { func takeEnum(thing: MyEnum, other: Double) {} func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) +// WITH_LITERAL_3: Begin completions, 1 items +// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: End completions } } } @@ -717,3 +740,24 @@ func testTypeParamInContextType() { // TYPEPARAM_IN_CONTEXTTYPE_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: myProtocolOption[#MyStruct#]; // TYPEPARAM_IN_CONTEXTTYPE_1: End completions } + +func testTernaryOperator(cond: Bool) { + let _: SomeEnum1 = cond ? .#^TERNARY_1^# + func sync(){} + let _: SomeEnum1 = cond ? .#^TERNARY_2^# : + func sync(){} + let _: SomeEnum1 = cond ? .#^TERNARY_3^# : .South + func sync(){} + let _: SomeEnum1 = cond ? .South : .#^TERNARY_4^# +} + +func testTernaryOperator2(cond: Bool) { + let _: SomeEnum1 = cond ? .#^TERNARY_5^# : .bogus + func sync(){} + let _: SomeEnum1 = cond ? .bogus : .#^TERNARY_6^# + func sync(){} + let _: SomeEnum1 = .#^TERNARY_CONDITION^# ? .bogus : .bogus +// TERNARY_CONDITION: Begin completions +// TERNARY_CONDITION-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#Bool#]; name=init() +// TERNARY_CONDITION: End completions +} diff --git a/test/IDE/import_as_member.swift b/test/IDE/import_as_member.swift index 7e5e81126c377..05e34cba2e111 100644 --- a/test/IDE/import_as_member.swift +++ b/test/IDE/import_as_member.swift @@ -106,6 +106,9 @@ // PRINT-APINOTES-4: @available(swift, obsoleted: 3, renamed: "Struct1.NewApiNoteType") // PRINT-APINOTES-4-NEXT: typealias IAMStruct1APINoteType = Struct1.NewApiNoteType +#if canImport(Foundation) +import Foundation +#endif import ImportAsMember.A import ImportAsMember.B import ImportAsMember.APINotes diff --git a/test/IDE/infer_import_as_member.swift b/test/IDE/infer_import_as_member.swift index d47de880ad10e..c50d5f7ecf020 100644 --- a/test/IDE/infer_import_as_member.swift +++ b/test/IDE/infer_import_as_member.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/custom-modules/CollisionImportAsMember.h -I %t -I %S/Inputs/custom-modules -print-module -source-filename %s -module-to-print=InferImportAsMember -always-argument-labels -enable-infer-import-as-member -skip-unavailable > %t.printed.A.txt -// RUN: %target-swift-frontend -typecheck -import-objc-header %S/Inputs/custom-modules/CollisionImportAsMember.h -I %t -I %S/Inputs/custom-modules %s -enable-infer-import-as-member -verify +// RUN: %target-swift-frontend -typecheck -import-objc-header %S/Inputs/custom-modules/CollisionImportAsMember.h -I %t -I %S/Inputs/custom-modules %s -enable-infer-import-as-member -verify -enable-invalid-ephemeralness-as-error // RUN: %FileCheck %s -check-prefix=PRINT -strict-whitespace < %t.printed.A.txt // REQUIRES: objc_interop @@ -111,3 +111,27 @@ let _ = mine.getCollisionNonProperty(1) // PRINT-NEXT: init!(i i: Double) // PRINT-NEXT: class func invert(_ iamOtherName: IAMOtherName!) // PRINT-NEXT: } +// +// PRINT-LABEL: struct IAMPointerStruct { +// PRINT-NEXT: var ptr1: UnsafeMutablePointer! +// PRINT-NEXT: var ptr2: UnsafeMutablePointer! +// PRINT-NEXT: init() +// PRINT-NEXT: init(ptr1 ptr1: UnsafeMutablePointer!, ptr2 ptr2: UnsafeMutablePointer!) +// PRINT-NEXT: } +// PRINT-NEXT: extension IAMPointerStruct { +// PRINT-NEXT: init(otherPtr ptr: UnsafeMutablePointer!) +// PRINT-NEXT: } + +func testNonEphemeralInitParams(x: Double) { + var x = x + + _ = IAMPointerStruct(ptr1: &x, ptr2: &x) + // expected-error@-1 {{cannot use inout expression here; argument 'ptr1' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-2 {{implicit argument conversion from 'Double' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-3 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + // expected-error@-4 {{cannot use inout expression here; argument 'ptr2' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-5 {{implicit argument conversion from 'Double' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-6 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = IAMPointerStruct(otherPtr: &x) // Okay. +} diff --git a/test/IDE/newtype.swift b/test/IDE/newtype.swift index c3f492a44c815..975584445bcb3 100644 --- a/test/IDE/newtype.swift +++ b/test/IDE/newtype.swift @@ -234,3 +234,23 @@ func testFixit() { let _ = NSMyContextName // expected-error@-1{{'NSMyContextName' has been renamed to 'NSSomeContext.Name.myContextName'}} {{10-25=NSSomeContext.Name.myContextName}} } + +func testNonEphemeralInitParams(x: OpaquePointer) { + var x = x + + _ = TRefRef(&x) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'init(_:)'}} + // expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = TRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument 'rawValue' should be a pointer that outlives the call to 'init(rawValue:)'}} + // expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = ConstTRefRef(&x) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'init(_:)'}} + // expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafePointer' produces a pointer valid only for the duration of the call}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = ConstTRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument 'rawValue' should be a pointer that outlives the call to 'init(rawValue:)'}} + // expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafePointer' produces a pointer valid only for the duration of the call}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} +} diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 48ded10a61b5f..6e58881b90b4f 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -452,7 +452,7 @@ class d0120_TestClassBase { } class d0121_TestClassDerived : d0120_TestClassBase { -// PASS_COMMON-LABEL: {{^}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} +// PASS_COMMON-LABEL: {{^}}@_inheritsConvenienceInitializers {{()?}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}} required init() { super.init() } // PASS_COMMON-NEXT: {{^}} required init(){{$}} @@ -611,8 +611,8 @@ struct d0200_EscapedIdentifiers { // PASS_ONE_LINE_TYPEREPR-DAG: {{^}} typealias `protocol` = `class`{{$}} class `extension` : `class` {} -// PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} -// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}} +// PASS_ONE_LINE_TYPE-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : d0200_EscapedIdentifiers.`class` {{{$}} +// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : `class` {{{$}} // PASS_COMMON: {{^}} @objc deinit{{$}} // PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} @@ -748,7 +748,7 @@ class d0260_ExplodePattern_TestClassBase { } class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase { -// PASS_EXPLODE_PATTERN-LABEL: {{^}}class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} +// PASS_EXPLODE_PATTERN-LABEL: {{^}}@_inheritsConvenienceInitializers class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}} override final var baseProp2: Int { get { @@ -791,13 +791,13 @@ class ClassWithInheritance2 : FooProtocol, BarProtocol {} // PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance2 : FooProtocol, BarProtocol {{{$}} class ClassWithInheritance3 : FooClass {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance3 : FooClass {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance3 : FooClass {{{$}} class ClassWithInheritance4 : FooClass, FooProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance4 : FooClass, FooProtocol {{{$}} class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {} -// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} +// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}} class ClassWithInheritance6 : QuxProtocol, SubFooProtocol { typealias Qux = Int diff --git a/test/IDE/print_ast_tc_decls_errors.swift b/test/IDE/print_ast_tc_decls_errors.swift index 83f022d22cfff..234551d5df63d 100644 --- a/test/IDE/print_ast_tc_decls_errors.swift +++ b/test/IDE/print_ast_tc_decls_errors.swift @@ -117,19 +117,19 @@ enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {} // // NO-TYREPR: {{^}}enum EnumWithInheritance2 : <>, <> {{{$}} // TYREPR: {{^}}enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {{{$}} -enum EnumWithInheritance3 : FooClass { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} expected-note {{do you want to add protocol stubs?}} +enum EnumWithInheritance3 : FooClass { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} // expected-error@-1{{'EnumWithInheritance3' declares raw type 'FooClass', but does not conform to RawRepresentable and conformance could not be synthesized}} // expected-error@-2{{RawRepresentable conformance cannot be synthesized because raw type 'FooClass' is not Equatable}} // NO-TYREPR: {{^}}enum EnumWithInheritance3 : <> {{{$}} // TYREPR: {{^}}enum EnumWithInheritance3 : FooClass {{{$}} -enum EnumWithInheritance4 : FooClass, FooProtocol { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} expected-note {{do you want to add protocol stubs?}} +enum EnumWithInheritance4 : FooClass, FooProtocol { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} // expected-error@-1{{'EnumWithInheritance4' declares raw type 'FooClass', but does not conform to RawRepresentable and conformance could not be synthesized}} // expected-error@-2{{RawRepresentable conformance cannot be synthesized because raw type 'FooClass' is not Equatable}} // NO-TYREPR: {{^}}enum EnumWithInheritance4 : <>, FooProtocol {{{$}} // TYREPR: {{^}}enum EnumWithInheritance4 : FooClass, FooProtocol {{{$}} -enum EnumWithInheritance5 : FooClass, BarClass { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} expected-error {{multiple enum raw types 'FooClass' and 'BarClass'}} expected-note {{do you want to add protocol stubs?}} +enum EnumWithInheritance5 : FooClass, BarClass { case X } // expected-error {{raw type 'FooClass' is not expressible by a string, integer, or floating-point literal}} expected-error {{multiple enum raw types 'FooClass' and 'BarClass'}} // expected-error@-1{{'EnumWithInheritance5' declares raw type 'FooClass', but does not conform to RawRepresentable and conformance could not be synthesized}} // expected-error@-2{{RawRepresentable conformance cannot be synthesized because raw type 'FooClass' is not Equatable}} // NO-TYREPR: {{^}}enum EnumWithInheritance5 : <>, <> {{{$}} diff --git a/test/IDE/print_omit_needless_words.swift b/test/IDE/print_omit_needless_words.swift index 4ed84cacdab30..15f1a19db920c 100644 --- a/test/IDE/print_omit_needless_words.swift +++ b/test/IDE/print_omit_needless_words.swift @@ -300,6 +300,25 @@ // CHECK-OMIT-NEEDLESS-WORDS-NEXT: func jumpSuper() // CHECK-OMIT-NEEDLESS-WORDS-NEXT: func joinSub() +// CHECK-OMIT-NEEDLESS-WORDS-LABEL: class OMWObjectType +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func _enumerateTypes(handler: @escaping () -> Void) + +// CHECK-OMIT-NEEDLESS-WORDS-LABEL: class OMWTerrifyingGarbage4DTypeRefMask_t +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func throwAway() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: @available(swift, obsoleted: 3, renamed: "throwAway()") +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func throwGarbageAway() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func throwGarbage4DAwayHarder() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func throwGarbage4DTypeRefMask_tAwayHardest() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func burn() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: @available(swift, obsoleted: 3, renamed: "burn()") +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func burnGarbage() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func carefullyBurn() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: @available(swift, obsoleted: 3, renamed: "carefullyBurn()") +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func carefullyBurnGarbage4D() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func veryCarefullyBurn() +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: @available(swift, obsoleted: 3, renamed: "veryCarefullyBurn()") +// CHECK-OMIT-NEEDLESS-WORDS-NEXT: func veryCarefullyBurnGarbage4DTypeRefMask_t() + // CHECK-OMIT-NEEDLESS-WORDS-DIAGS: inconsistent Swift name for Objective-C method 'conflicting1' in 'OMWSub' ('waggle1()' in 'OMWWaggle' vs. 'wiggle1()' in 'OMWWiggle') // CHECK-OMIT-NEEDLESS-WORDS-DIAGS: inconsistent Swift name for Objective-C property 'conflictingProp1' in 'OMWSub' ('waggleProp1' in 'OMWWaggle' vs. 'wiggleProp1' in 'OMWSuper') diff --git a/test/IDE/print_property_wrappers.swift b/test/IDE/print_property_wrappers.swift index 116864a9e920c..6018dc5b97ca1 100644 --- a/test/IDE/print_property_wrappers.swift +++ b/test/IDE/print_property_wrappers.swift @@ -47,7 +47,7 @@ struct HasWrappers { var z: String // Memberwise initializer. - // CHECK: init(x: Wrapper = Wrapper(closure: foo), y: Bool = true, z: String = Wrapper()) + // CHECK: init(x: Wrapper = Wrapper(closure: foo), y: Bool = true, z: Wrapper = Wrapper()) } func trigger() { diff --git a/test/IDE/print_swift_module.swift b/test/IDE/print_swift_module.swift index f28054dee5ae8..63e4a9d54aa55 100644 --- a/test/IDE/print_swift_module.swift +++ b/test/IDE/print_swift_module.swift @@ -1,7 +1,9 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module-path %t/print_swift_module.swiftmodule -emit-module-doc -emit-module-doc-path %t/print_swift_module.swiftdoc %s // RUN: %target-swift-ide-test -print-module -print-interface -no-empty-line-between-members -module-to-print=print_swift_module -I %t -source-filename=%s > %t.syn.txt +// RUN: %target-swift-ide-test -print-module -access-filter-internal -no-empty-line-between-members -module-to-print=print_swift_module -I %t -source-filename=%s > %t.syn.internal.txt // RUN: %FileCheck %s -check-prefix=CHECK1 < %t.syn.txt +// RUN: %FileCheck %s -check-prefix=CHECK2 < %t.syn.internal.txt public protocol P1 { /// foo1 comment from P1 @@ -53,3 +55,14 @@ public struct City { // CHECK1: /// returnsAlias() comment // CHECK1-NEXT: func returnsAlias() -> print_swift_module.Alias + +// CHECK2: struct Event { +public struct Event { + public var start: Int + public var end: Int? + public var repeating: ((), Int?) + public var name = "untitled event" + + // CHECK2: init(start: Int, end: Int? = nil, repeating: ((), Int?) = ((), nil), name: String = "untitled event") +} +// CHECK2: } diff --git a/test/IDE/print_type_interface.swift b/test/IDE/print_type_interface.swift index d61f4af59aa4d..7af1876b8af34 100644 --- a/test/IDE/print_type_interface.swift +++ b/test/IDE/print_type_interface.swift @@ -83,3 +83,22 @@ extension D { // TYPE5-DAG: public func formIndex(_ i: inout Int, offsetBy distance: Int) // TYPE5-DAG: public func distance(from start: Int, to end: Int) -> Int // TYPE5-DAG: public func joined(separator: String = "") -> String + +extension Array { + public struct Inner {} +} + +public protocol P2 {} + +extension Array.Inner where Element: P2 { + public func innerFoo() {} +} + +extension Int: P2 {} + +// Print interface for Array.Inner +// RUN: %target-swift-ide-test -print-type-interface -usr='$sSa20print_type_interfaceE5InnerVySi_GD' -module-name print_type_interface -source-filename %s | %FileCheck %s -check-prefix=TYPE6 + +// TYPE6-LABEL: public struct Inner { +// TYPE6: public func innerFoo() +// TYPE6: } diff --git a/test/IDE/reconstruct_type_from_mangled_name.swift b/test/IDE/reconstruct_type_from_mangled_name.swift index 8e1ac96af60cd..f5ad1f0b6ff35 100644 --- a/test/IDE/reconstruct_type_from_mangled_name.swift +++ b/test/IDE/reconstruct_type_from_mangled_name.swift @@ -247,3 +247,9 @@ private func patatino(_ vers1: T, _ vers2: T) -> Bool { return vers1 < vers2; } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "OtherModule", OSX 10.13) +public struct MovedHere { + public func foo() {} +} diff --git a/test/IRGen/ELF-remove-autolink-section.swift b/test/IRGen/ELF-remove-autolink-section.swift index d74d51e10d867..3a2b30ff3c415 100644 --- a/test/IRGen/ELF-remove-autolink-section.swift +++ b/test/IRGen/ELF-remove-autolink-section.swift @@ -13,5 +13,15 @@ print("Hi from Swift!") // ELF: module asm ".section .swift1_autolink_entries,\220x80000000\22" + +// Find the metadata entry for the blacklisting of the metadata symbol +// Ensure that it is in the ASAN metadata + +// ELF-DAG: !llvm.asan.globals = !{ +// ELF-SAME: [[MD:![0-9]+]] +// ELF-SAME: } + +// ELF-DAG: [[MD]] = !{[37 x i8]* @_swift1_autolink_entries, null, null, i1 false, i1 true} + // SECTION: .swift1_autolink_entries // NOSECTION-NOT: .swift1_autolink_entries diff --git a/test/IRGen/Inputs/legacy_type_info/vfsoverlay.yaml b/test/IRGen/Inputs/legacy_type_info/vfsoverlay.yaml new file mode 100644 index 0000000000000..308bd124764cf --- /dev/null +++ b/test/IRGen/Inputs/legacy_type_info/vfsoverlay.yaml @@ -0,0 +1,14 @@ +{ + 'version': 0, + 'use-external-names': false, + 'roots': [ + { + 'name': 'OUT_DIR', 'type': 'directory', + 'contents': [ + { 'name': 'a_moved.yaml', 'type': 'file', + 'external-contents': 'INPUT_DIR/legacy_type_info/a.yaml' + }, + ] + }, + ] +} diff --git a/test/IRGen/Inputs/multithread_module/main.swift b/test/IRGen/Inputs/multithread_module/main.swift index 55b749ca54c2e..27f8e6c257572 100644 --- a/test/IRGen/Inputs/multithread_module/main.swift +++ b/test/IRGen/Inputs/multithread_module/main.swift @@ -23,6 +23,10 @@ public struct MyStruct : MyProto { return x + 3 } +public func mutateMyStructArray(_ arr: inout [MyStruct], _ x: MyStruct) { + arr.append(x) +} + public var g1 = 234 let i = testit(27) diff --git a/test/IRGen/Inputs/opaque_result_type_private_underlying_2.swift b/test/IRGen/Inputs/opaque_result_type_private_underlying_2.swift new file mode 100644 index 0000000000000..2699a3405908a --- /dev/null +++ b/test/IRGen/Inputs/opaque_result_type_private_underlying_2.swift @@ -0,0 +1,47 @@ +public protocol A { + associatedtype Assoc + func bindAssoc() -> Assoc +} + +public protocol Q {} + +public struct PublicS : Q { + public init() {} +} + +internal struct InternalS : Q { + init() {} +} + +fileprivate struct PrivateS : Q { + init() {} +} + +public struct PrivateUnderlying : A { + public init() {} + public func bindAssoc() -> some Q { + return PrivateS() + } +} + +public struct InternalUnderlying : A { + public init() {} + public func bindAssoc() -> some Q { + return InternalS() + } +} + +public struct PublicUnderlying : A { + public init() {} + public func bindAssoc() -> some Q { + return PublicS() + } +} + +public struct PublicUnderlyingInlinable : A { + public init() {} + @inlinable + public func bindAssoc() -> some Q { + return PublicS() + } +} diff --git a/test/IRGen/Inputs/tail_allocated_c_array.h b/test/IRGen/Inputs/tail_allocated_c_array.h new file mode 100644 index 0000000000000..b68e933954c74 --- /dev/null +++ b/test/IRGen/Inputs/tail_allocated_c_array.h @@ -0,0 +1,7 @@ +#include + +typedef struct foo { + uint8_t a; + uint16_t b; + uint8_t tailallocatedarray[0]; +} foo; diff --git a/test/IRGen/Inputs/usr/include/Gizmo.h b/test/IRGen/Inputs/usr/include/Gizmo.h index d741d042ca29a..474f15047b0ce 100644 --- a/test/IRGen/Inputs/usr/include/Gizmo.h +++ b/test/IRGen/Inputs/usr/include/Gizmo.h @@ -45,6 +45,7 @@ typedef long NSInteger; - (void) setFrame: (struct NSRect) rect; - (void) frob; - (void) test: (struct Fob) fob; +- (void) perform: (void (^)(NS_CONSUMED Gizmo*)) block; + (void) runce; @end diff --git a/test/IRGen/Inputs/usr/include/module.map b/test/IRGen/Inputs/usr/include/module.map index e6ca8ec2e7d35..438caf2c8b194 100644 --- a/test/IRGen/Inputs/usr/include/module.map +++ b/test/IRGen/Inputs/usr/include/module.map @@ -1,32 +1,40 @@ module gizmo { header "Gizmo.h" + export * } module SRoA { header "SRoA.h" + export * } module ObjectiveC { header "BridgeTestObjectiveC.h" + export * } module CoreCooling { header "CoreCooling.h" + export * } module Foundation { header "BridgeTestFoundation.h" + export * } module CoreFoundation { header "BridgeTestCoreFoundation.h" + export * } module ObjCInterop { header "ObjCInterop.h" + export * } module error_domains { header "error_domains.h" + export * } diff --git a/test/IRGen/Inputs/weak_import_extension_helper.swift b/test/IRGen/Inputs/weak_import_extension_helper.swift new file mode 100644 index 0000000000000..d45e3f5e9551f --- /dev/null +++ b/test/IRGen/Inputs/weak_import_extension_helper.swift @@ -0,0 +1,8 @@ +@available(macOS 10.60, *) +public struct Foo { + public init() {} +} + +extension Foo { + public func extensionMethod() {} +} diff --git a/test/IRGen/abi_v7k.swift b/test/IRGen/abi_v7k.swift index 79961d695b27e..779e5d41e0903 100644 --- a/test/IRGen/abi_v7k.swift +++ b/test/IRGen/abi_v7k.swift @@ -18,8 +18,8 @@ func addFloats(x: Float, y : Float) -> Float { // CHECK: fadd double // CHECK: ret double // V7K-LABEL: _$s8test_v7k10addDoubles -// V7K: vadd.f64 d0, d0, d1 -// V7K: vadd.f64 d0, d0, d2 +// V7K: vadd.f64 [[R:d[0-9]+]], d0, d1 +// V7K: vadd.f64 d0, [[R]], d2 func addDoubles(x: Double, y: Double, z: Double) -> Double { return x+y+z } diff --git a/test/IRGen/abitypes.swift b/test/IRGen/abitypes.swift index f6263302c7483..9c27c7d4674cd 100644 --- a/test/IRGen/abitypes.swift +++ b/test/IRGen/abitypes.swift @@ -19,50 +19,50 @@ import Foundation class Foo { // x86_64-macosx: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { // x86_64-ios: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-ios: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // x86_64-ios: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { // i386-ios: define hidden swiftcc void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%TSo6MyRectV* noalias nocapture sret, %T8abitypes3FooC* swiftself) {{.*}} { - // i386-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) unnamed_addr {{.*}} { + // i386-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) {{[#0-9]*}} { // armv7-ios: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // armv7-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) unnamed_addr {{.*}} { + // armv7-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) {{[#0-9]*}} { // armv7s-ios: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // armv7s-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) unnamed_addr {{.*}} { + // armv7s-ios: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) {{[#0-9]*}} { // arm64-ios: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // arm64-ios: define hidden [[ARM64_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // arm64-ios: define hidden [[ARM64_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { // x86_64-tvos: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-tvos: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // x86_64-tvos: define hidden { <2 x float>, <2 x float> } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { // arm64-tvos: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // arm64-tvos: define hidden [[ARM64_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // arm64-tvos: define hidden [[ARM64_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { // i386-watchos: define hidden swiftcc void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%TSo6MyRectV* noalias nocapture sret, %T8abitypes3FooC* swiftself) {{.*}} { - // i386-watchos: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) unnamed_addr {{.*}} { + // i386-watchos: define hidden void @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(%TSo6MyRectV* noalias nocapture sret, i8*, i8*) {{[#0-9]*}} { // armv7k-watchos: define hidden swiftcc { float, float, float, float } @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // armv7k-watchos: define hidden [[ARMV7K_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // armv7k-watchos: define hidden [[ARMV7K_MYRECT]] @"$s8abitypes3FooC3bar{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { @objc dynamic func bar() -> MyRect { return MyRect(x: 1, y: 2, width: 3, height: 4) } // x86_64-macosx: define hidden swiftcc double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}F"(double, double, double, double, %T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, %TSo6CGRectV* byval align 8) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, %TSo6CGRectV* byval align 8) {{[#0-9]*}} { // armv7-ios: define hidden swiftcc double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7-ios: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) unnamed_addr {{.*}} { + // armv7-ios: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) {{[#0-9]*}} { // armv7s-ios: define hidden swiftcc double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7s-ios: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) unnamed_addr {{.*}} { + // armv7s-ios: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) {{[#0-9]*}} { // armv7k-watchos: define hidden swiftcc double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7k-watchos: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x float]) unnamed_addr {{.*}} { + // armv7k-watchos: define hidden double @"$s8abitypes3FooC14getXFromNSRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x float]) {{[#0-9]*}} { @objc dynamic func getXFromNSRect(_ r: NSRect) -> Double { return Double(r.origin.x) } // x86_64-macosx: define hidden swiftcc float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, <2 x float>, <2 x float>) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, <2 x float>, <2 x float>) {{[#0-9]*}} { // armv7-ios: define hidden swiftcc float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7-ios: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) unnamed_addr {{.*}} { + // armv7-ios: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) {{[#0-9]*}} { // armv7s-ios: define hidden swiftcc float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7s-ios: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) unnamed_addr {{.*}} { + // armv7s-ios: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x i32]) {{[#0-9]*}} { // armv7k-watchos: define hidden swiftcc float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}F"(float, float, float, float, %T8abitypes3FooC* swiftself) {{.*}} { - // armv7k-watchos: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x float]) unnamed_addr {{.*}} { + // armv7k-watchos: define hidden float @"$s8abitypes3FooC12getXFromRect{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, [4 x float]) {{[#0-9]*}} { @objc dynamic func getXFromRect(_ r: MyRect) -> Float { return r.x } @@ -110,7 +110,7 @@ class Foo { } // Ensure that MyRect is passed as an indirect-byval on x86_64 because we run out of registers for direct arguments - // x86_64-macosx: define hidden float @"$s8abitypes3FooC25getXFromRectIndirectByVal{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, float, float, float, float, float, float, float, %TSo6MyRectV* byval align 8) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden float @"$s8abitypes3FooC25getXFromRectIndirectByVal{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, float, float, float, float, float, float, float, %TSo6MyRectV* byval align 8) {{[#0-9]*}} { @objc dynamic func getXFromRectIndirectByVal(_: Float, second _: Float, third _: Float, fourth _: Float, fifth _: Float, sixth _: Float, @@ -173,7 +173,7 @@ class Foo { } // x86_64-macosx: define hidden swiftcc { double, double, double } @"$s8abitypes3FooC3baz{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden void @"$s8abitypes3FooC3baz{{[_0-9a-zA-Z]*}}FTo"(%TSo4TrioV* noalias nocapture sret, i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden void @"$s8abitypes3FooC3baz{{[_0-9a-zA-Z]*}}FTo"(%TSo4TrioV* noalias nocapture sret, i8*, i8*) {{[#0-9]*}} { @objc dynamic func baz() -> Trio { return Trio(i: 1.0, j: 2.0, k: 3.0) } @@ -197,7 +197,7 @@ class Foo { return p.newPair() } - // x86_64-macosx: define hidden i64 @"$s8abitypes3FooC8takepair{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i64) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i64 @"$s8abitypes3FooC8takepair{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i64) {{[#0-9]*}} { @objc dynamic func takepair(_ p: IntPair) -> IntPair { return p } @@ -217,7 +217,7 @@ class Foo { return p.newNestedInts() } - // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC9copyClass{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC9copyClass{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) {{[#0-9]*}} { // x86_64-macosx: [[VALUE:%[0-9]+]] = call swiftcc [[TYPE:%.*]]* @"$s8abitypes3FooC9copyClass{{[_0-9a-zA-Z]*}}F" // x86_64-macosx: [[T0:%.*]] = call [[OBJC:%objc_class]]* @swift_getObjCClassFromMetadata([[TYPE]]* [[VALUE]]) // x86_64-macosx: [[RESULT:%[0-9]+]] = bitcast [[OBJC]]* [[T0]] to i8* @@ -226,7 +226,7 @@ class Foo { return a } - // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC9copyProto{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC9copyProto{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) {{[#0-9]*}} { // x86_64-macosx: [[VALUE:%[0-9]+]] = call swiftcc [[TYPE:%.*]] @"$s8abitypes3FooC9copyProto{{[_0-9a-zA-Z]*}}F" // x86_64-macosx: [[RESULT:%[0-9]+]] = bitcast [[TYPE]] [[VALUE]] to i8* // x86_64-macosx: ret i8* [[RESULT]] @@ -234,7 +234,7 @@ class Foo { return a } - // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC13copyProtoComp{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i8* @"$s8abitypes3FooC13copyProtoComp{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) {{[#0-9]*}} { // x86_64-macosx: [[VALUE:%[0-9]+]] = call swiftcc [[TYPE:%.*]] @"$s8abitypes3FooC13copyProtoComp{{[_0-9a-zA-Z]*}}F" // x86_64-macosx: [[RESULT:%[0-9]+]] = bitcast [[TYPE]] [[VALUE]] to i8* // x86_64-macosx: ret i8* [[RESULT]] @@ -243,7 +243,7 @@ class Foo { } // x86_64-macosx: define hidden swiftcc i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1, %T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) {{[#0-9]*}} { // x86_64-macosx: [[R1:%[0-9]+]] = call swiftcc i1 @"$s10ObjectiveC22_convertObjCBoolToBool{{[_0-9a-zA-Z]*}}F" // x86_64-macosx: [[R2:%[0-9]+]] = call swiftcc i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F" // x86_64-macosx: [[R3:%[0-9]+]] = call swiftcc i8 @"$s10ObjectiveC22_convertBoolToObjCBool{{[_0-9a-zA-Z]*}}F"(i1 [[R2]] @@ -257,14 +257,14 @@ class Foo { // x86_64-ios-fixme: ret i1 [[R3]] // // armv7-ios-fixme: define hidden i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1, %T8abitypes3FooC*) {{.*}} { - // armv7-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) unnamed_addr {{.*}} { + // armv7-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) {{[#0-9]*}} { // armv7-ios-fixme: [[R1:%[0-9]+]] = call i1 @"$s10ObjectiveC22_convertObjCBoolToBool1xSbAA0cD0V_tF" // armv7-ios-fixme: [[R2:%[0-9]+]] = call i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1 [[R1]] // armv7-ios-fixme: [[R3:%[0-9]+]] = call i8 @"$s10ObjectiveC22_convertBoolToObjCBoolAA0eF0VSb1x_tF"(i1 [[R2]] // armv7-ios-fixme: ret i8 [[R3]] // // armv7s-ios-fixme: define hidden i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1, %T8abitypes3FooC*) {{.*}} { - // armv7s-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) unnamed_addr {{.*}} { + // armv7s-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) {{[#0-9]*}} { // armv7s-ios-fixme: [[R1:%[0-9]+]] = call i1 @"$s10ObjectiveC22_convertObjCBoolToBool1xSbAA0cD0V_tF" // armv7s-ios-fixme: [[R2:%[0-9]+]] = call i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1 [[R1]] // armv7s-ios-fixme: [[R3:%[0-9]+]] = call i8 @"$s10ObjectiveC22_convertBoolToObjCBoolAA0eF0VSb1x_tF"(i1 [[R2]] @@ -276,7 +276,7 @@ class Foo { // arm64-ios-fixme: ret i1 [[R2]] // // i386-ios-fixme: define hidden i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1, %T8abitypes3FooC*) {{.*}} { - // i386-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) unnamed_addr {{.*}} { + // i386-ios-fixme: define internal signext i8 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8 signext) {{[#0-9]*}} { // i386-ios-fixme: [[R1:%[0-9]+]] = call i1 @"$s10ObjectiveC22_convertObjCBoolToBool{{[_0-9a-zA-Z]*}}F" // i386-ios-fixme: [[R2:%[0-9]+]] = call i1 @"$s8abitypes3FooC6negate{{[_0-9a-zA-Z]*}}F"(i1 [[R1]] // i386-ios-fixme: [[R3:%[0-9]+]] = call i8 @"$s10ObjectiveC22_convertBoolToObjCBool{{[_0-9a-zA-Z]*}}F"(i1 [[R2]] @@ -464,17 +464,17 @@ class Foo { try g.negateThrowing(b) } - // x86_64-macosx: define hidden i32* @"$s8abitypes3FooC24copyUnsafeMutablePointer{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i32*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i32* @"$s8abitypes3FooC24copyUnsafeMutablePointer{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i32*) {{[#0-9]*}} { @objc dynamic func copyUnsafeMutablePointer(_ p: UnsafeMutablePointer) -> UnsafeMutablePointer { return p } - // x86_64-macosx: define hidden i64 @"$s8abitypes3FooC17returnNSEnumValue{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i64 @"$s8abitypes3FooC17returnNSEnumValue{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { @objc dynamic func returnNSEnumValue() -> ByteCountFormatter.CountStyle { return .file } - // x86_64-macosx: define hidden zeroext i16 @"$s8abitypes3FooC20returnOtherEnumValue{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i16 zeroext) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden zeroext i16 @"$s8abitypes3FooC20returnOtherEnumValue{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i16 zeroext) {{[#0-9]*}} { @objc dynamic func returnOtherEnumValue(_ choice: ChooseTo) -> ChooseTo { switch choice { case .takeIt: return .leaveIt @@ -483,7 +483,7 @@ class Foo { } // x86_64-macosx: define hidden swiftcc i32 @"$s8abitypes3FooC10getRawEnum{{[_0-9a-zA-Z]*}}F"(%T8abitypes3FooC* swiftself) {{.*}} { - // x86_64-macosx: define hidden i32 @"$s8abitypes3FooC10getRawEnum{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden i32 @"$s8abitypes3FooC10getRawEnum{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*) {{[#0-9]*}} { @objc dynamic func getRawEnum() -> RawEnum { return Intergalactic } @@ -493,7 +493,7 @@ class Foo { self.work = work } - // x86_64-macosx: define hidden void @"$s8abitypes3FooC13testArchetype{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) unnamed_addr {{.*}} { + // x86_64-macosx: define hidden void @"$s8abitypes3FooC13testArchetype{{[_0-9a-zA-Z]*}}FTo"(i8*, i8*, i8*) {{[#0-9]*}} { @objc dynamic func testArchetype(_ work: Work) { work.doStuff(1) // x86_64-macosx: [[OBJCPTR:%.*]] = bitcast i8* %2 to %objc_object* @@ -514,10 +514,10 @@ class Foo { } // arm64-ios: define hidden swiftcc { i64, i64, i64, i64 } @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}F"(%TSo13StructReturnsC*, i64, i64, i64, i64, %T8abitypes3FooC* swiftself) {{.*}} { - // arm64-ios: define hidden void @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}FTo"(%TSo9BigStructV* noalias nocapture sret, i8*, i8*, [[OPAQUE:.*]]*, %TSo9BigStructV*) unnamed_addr {{.*}} { + // arm64-ios: define hidden void @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}FTo"(%TSo9BigStructV* noalias nocapture sret, i8*, i8*, [[OPAQUE:.*]]*, %TSo9BigStructV*) {{[#0-9]*}} { // // arm64-tvos: define hidden swiftcc { i64, i64, i64, i64 } @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}F"(%TSo13StructReturnsC*, i64, i64, i64, i64, %T8abitypes3FooC* swiftself) {{.*}} { - // arm64-tvos: define hidden void @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}FTo"(%TSo9BigStructV* noalias nocapture sret, i8*, i8*, [[OPAQUE:.*]]*, %TSo9BigStructV*) unnamed_addr {{.*}} { + // arm64-tvos: define hidden void @"$s8abitypes3FooC14callJustReturn{{[_0-9a-zA-Z]*}}FTo"(%TSo9BigStructV* noalias nocapture sret, i8*, i8*, [[OPAQUE:.*]]*, %TSo9BigStructV*) {{[#0-9]*}} { @objc dynamic func callJustReturn(_ r: StructReturns, with v: BigStruct) -> BigStruct { return r.justReturn(v) } diff --git a/test/IRGen/access_type_metadata_by_mangled_name_objc.swift b/test/IRGen/access_type_metadata_by_mangled_name_objc.swift index 3322426d97dbc..68b1d6467334e 100644 --- a/test/IRGen/access_type_metadata_by_mangled_name_objc.swift +++ b/test/IRGen/access_type_metadata_by_mangled_name_objc.swift @@ -12,7 +12,8 @@ public func test() { var x: Any.Type // Access ObjC classes by mangled name. - // CHECK: @"$sSo8NSObjectCMD" + // FIXME: Disabled for now. + // CHECK: @"$sSo8NSObjectCMa" x = NSObject.self // Use the metadata accessor for CF classes that already has to exist. diff --git a/test/IRGen/address_sanitizer_recover.swift b/test/IRGen/address_sanitizer_recover.swift new file mode 100644 index 0000000000000..259fbbb5b5da8 --- /dev/null +++ b/test/IRGen/address_sanitizer_recover.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-recover=address %s | %FileCheck %s -check-prefix=ASAN_RECOVER +// RUN: %target-swift-frontend -emit-ir -sanitize=address %s | %FileCheck %s -check-prefix=ASAN_NO_RECOVER +// RUN: %target-swift-frontend -emit-ir -sanitize-recover=address %s | %FileCheck %s -check-prefix=NO_ASAN_RECOVER + +// ASAN_RECOVER: declare void @__asan_loadN_noabort( +// ASAN_NO_RECOVER: declare void @__asan_loadN( + +let size:Int = 128; +let x = UnsafeMutablePointer.allocate(capacity: size) +x.initialize(repeating: 0, count: size) +x.advanced(by: 0).pointee = 5; +print("Read first element:\(x.advanced(by: 0).pointee)") +x.deallocate(); + +// There should be no ASan instrumentation in this case. +// NO_ASAN_RECOVER-NOT: declare void @__asan_load diff --git a/test/IRGen/associated_type_witness.swift b/test/IRGen/associated_type_witness.swift index 690c8eac25474..9342c8ba5addb 100644 --- a/test/IRGen/associated_type_witness.swift +++ b/test/IRGen/associated_type_witness.swift @@ -133,6 +133,37 @@ struct UsesVoid : HasSimpleAssoc { typealias Assoc = () } +// SR-11642: Failure to canonicalize type in associated type witness. +struct Validator { + let validatorFailureType: Any.Type +} + + +protocol ValidatorType { + associatedtype Data + associatedtype Failure + func validator() -> Validator +} + + +extension ValidatorType { + func validator() -> Validator { + .init(validatorFailureType: Failure.self) + } +} + + +// MARK: Failing example +extension Validator where T == String { + // GLOBAL: @"symbolic _____ySS__G 23associated_type_witness9ValidatorVAASSRszlE1VV7FailureV" + struct V: ValidatorType { + typealias Data = T // or String + + struct Failure {} + } +} + + // Protocol conformance descriptor for Computed : Assocked. // GLOBAL-LABEL: @"$s23associated_type_witness8ComputedVyxq_GAA8AssockedAAMc" = hidden constant // GLOBAL-SAME: i16 4, diff --git a/test/IRGen/autolink-runtime-compatibility.swift b/test/IRGen/autolink-runtime-compatibility.swift index 295b00e0521a9..7eef891d20044 100644 --- a/test/IRGen/autolink-runtime-compatibility.swift +++ b/test/IRGen/autolink-runtime-compatibility.swift @@ -5,10 +5,10 @@ // RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version 5.0 -disable-autolinking-runtime-compatibility -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s // Doesn't autolink compatibility library because runtime compatibility library is disabled -// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -runtime-compatibility-version none -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s +// RUN: %target-swift-frontend -runtime-compatibility-version none -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s // Doesn't autolink compatibility library because target OS doesn't need it -// RUN: %target-swift-frontend -disable-autolinking-runtime-compatibility-dynamic-replacements -target x86_64-apple-macosx10.24 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s +// RUN: %target-swift-frontend -target x86_64-apple-macosx10.24 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=NO-FORCE-LOAD %s // Autolinks because compatibility library was explicitly asked for // RUN: %target-swift-frontend -runtime-compatibility-version 5.0 -emit-ir -parse-stdlib %s | %FileCheck -check-prefix=FORCE-LOAD %s @@ -18,8 +18,11 @@ public func foo() {} // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibility50"} +// NO-FORCE-LOAD-NOT: !{!"-lswiftCompatibilityDynamicReplacements"} // FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibility50" +// FORCE-LOAD: declare {{.*}} @"_swift_FORCE_LOAD_$_swiftCompatibilityDynamicReplacements" // FORCE-LOAD-DAG: [[AUTOLINK_SWIFT_COMPAT:![0-9]+]] = !{!"-lswiftCompatibility50"} +// FORCE-LOAD-DAG: !{!"-lswiftCompatibilityDynamicReplacements"} // FORCE-LOAD-DAG: !llvm.linker.options = !{{{.*}}[[AUTOLINK_SWIFT_COMPAT]]{{[,}]}} diff --git a/test/IRGen/autorelease.sil b/test/IRGen/autorelease.sil index 2d58f85098960..00b7d9251085c 100644 --- a/test/IRGen/autorelease.sil +++ b/test/IRGen/autorelease.sil @@ -46,6 +46,14 @@ bb0(%0 : @owned $C?): // arm64-NEXT: [[T3:%.*]] = ptrtoint i8* [[T2]] to i64 // arm64-NEXT: ret i64 [[T3]] +// aarch64: define{{( dllexport| protected)?}} swiftcc i64 @bar(i64) +// aarch64: [[T0:%.*]] = call swiftcc i64 @foo(i64 %0) +// aarch64-NEXT: call void asm sideeffect "mov +// aarch64-NEXT: [[T1:%.*]] = inttoptr i64 [[T0]] to i8* +// aarch64-NEXT: [[T2:%.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* [[T1]]) +// aarch64-NEXT: [[T3:%.*]] = ptrtoint i8* [[T2]] to i64 +// aarch64-NEXT: ret i64 [[T3]] + // i386: define{{( dllexport| protected)?}} swiftcc i32 @bar(i32) // i386: [[T0:%.*]] = call swiftcc i32 @foo(i32 %0) // i386-NEXT: [[T1:%.*]] = inttoptr i32 [[T0]] to i8* @@ -76,3 +84,4 @@ bb0(%0 : @owned $C?): // armv7k-NEXT: [[T2:%.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* [[T1]]) // armv7k-NEXT: [[T3:%.*]] = ptrtoint i8* [[T2]] to i32 // armv7k-NEXT: ret i32 [[T3]] + diff --git a/test/IRGen/big_types_corner_cases.swift b/test/IRGen/big_types_corner_cases.swift index d51ed3da109b1..0520bbd6cc7bf 100644 --- a/test/IRGen/big_types_corner_cases.swift +++ b/test/IRGen/big_types_corner_cases.swift @@ -1,6 +1,8 @@ // XFAIL: CPU=powerpc64le +// XFAIL: CPU=s390x // RUN: %target-swift-frontend -enable-large-loadable-types %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: optimized_stdlib +// UNSUPPORTED: CPU=powerpc64le public struct BigStruct { var i0 : Int32 = 0 @@ -213,7 +215,7 @@ public func testGetFunc() { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC5test2yyF"(%T22big_types_corner_cases7TestBigC* swiftself) // CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGMD" // CHECK: [[CALL2:%.*]] = call i8** @"$sSaySS2ID_y22big_types_corner_cases9BigStructVcSg7handlertGSayxGSlsWl" -// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) +// CHECK: call swiftcc void @"$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF"(%Ts16IndexingIteratorV{{.*}}* noalias nocapture sret {{.*}}, %swift.type* [[CALL1]], i8** [[CALL2]], %swift.opaque* noalias nocapture swiftself {{.*}}) // CHECK: ret void class TestBig { typealias Handler = (BigStruct) -> Void diff --git a/test/IRGen/builtins.swift b/test/IRGen/builtins.swift index dfcf2d0a8b82b..5688f02017928 100644 --- a/test/IRGen/builtins.swift +++ b/test/IRGen/builtins.swift @@ -314,10 +314,12 @@ func testStaticReport(_ b: Bool, ptr: Builtin.RawPointer) -> () { // CHECK-LABEL: define hidden {{.*}}void @"$s8builtins12testCondFail{{[_0-9a-zA-Z]*}}F"(i1, i1) func testCondFail(_ b: Bool, c: Bool) { - // CHECK: br i1 %0, label %[[FAIL:.*]], label %[[CONT:.*]] + // CHECK: [[EXPECT:%.*]] = call i1 @llvm.expect.i1(i1 %0, i1 false) + // CHECK: br i1 [[EXPECT]], label %[[FAIL:.*]], label %[[CONT:.*]] Builtin.condfail_message(b, StaticString("message").unsafeRawPointer) // CHECK: [[CONT]] - // CHECK: br i1 %1, label %[[FAIL2:.*]], label %[[CONT:.*]] + // CHECK: [[EXPECT:%.*]] = call i1 @llvm.expect.i1(i1 %1, i1 false) + // CHECK: br i1 [[EXPECT]], label %[[FAIL2:.*]], label %[[CONT:.*]] Builtin.condfail_message(c, StaticString("message").unsafeRawPointer) // CHECK: [[CONT]] // CHECK: ret void diff --git a/test/IRGen/c_functions.swift b/test/IRGen/c_functions.swift index 280b62e3be802..5f87bd05f9643 100644 --- a/test/IRGen/c_functions.swift +++ b/test/IRGen/c_functions.swift @@ -33,3 +33,4 @@ func test_indirect_by_val_alignment() { // armv7: define hidden swiftcc void @"$s11c_functions30test_indirect_by_val_alignmentyyF"() // i386: define hidden swiftcc void @"$s11c_functions30test_indirect_by_val_alignmentyyF"() // s390x: define hidden swiftcc void @"$s11c_functions30test_indirect_by_val_alignmentyyF"() +// powerpc64le: define hidden swiftcc void @"$s11c_functions30test_indirect_by_val_alignmentyyF"() diff --git a/test/IRGen/casts.sil b/test/IRGen/casts.sil index 45a9946acb03a..2d27987f213a2 100644 --- a/test/IRGen/casts.sil +++ b/test/IRGen/casts.sil @@ -66,7 +66,7 @@ entry(%n : $Builtin.NativeObject): // CHECK: call void @llvm.trap() sil @u_cast_to_class_existential : $@convention(thin) (@owned AnyObject) -> @owned CP { entry(%a : $AnyObject): - %p = unconditional_checked_cast %a : $AnyObject to $CP + %p = unconditional_checked_cast %a : $AnyObject to CP return %p : $CP } @@ -74,7 +74,7 @@ entry(%a : $AnyObject): // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %1, %swift.type* %0, {{.*}} @"$s5casts2CPMp" sil @u_cast_to_existential_metatype : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick CP.Type { entry(%a : $@thick Any.Type): - %p = unconditional_checked_cast %a : $@thick Any.Type to $@thick CP.Type + %p = unconditional_checked_cast %a : $@thick Any.Type to @thick CP.Type return %p : $@thick CP.Type } @@ -94,7 +94,7 @@ entry(%a : $@thick Any.Type): // CHECK: call void @llvm.trap() sil @u_cast_to_class_existential_2 : $@convention(thin) (@owned AnyObject) -> @owned CP & CP2 { entry(%a : $AnyObject): - %p = unconditional_checked_cast %a : $AnyObject to $CP & CP2 + %p = unconditional_checked_cast %a : $AnyObject to CP & CP2 return %p : $CP & CP2 } @@ -103,7 +103,7 @@ entry(%a : $AnyObject): // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, {{.*}} @"$s5casts2CPMp" {{[^,]*}}, {{.*}} @"$s5casts3CP2Mp" sil @u_cast_to_class_existential_mixed : $@convention(thin) (@owned AnyObject) -> @owned CP & OP & CP2 { entry(%a : $AnyObject): - %p = unconditional_checked_cast %a : $AnyObject to $CP & OP & CP2 + %p = unconditional_checked_cast %a : $AnyObject to CP & OP & CP2 return %p : $CP & OP & CP2 } @@ -115,7 +115,7 @@ entry(%a : $AnyObject): // CHECK: insertvalue {{.*}} [[OBJ]] sil @u_cast_to_existential_metatype_mixed : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick (CP & OP & CP2).Type { entry(%a : $@thick Any.Type): - %p = unconditional_checked_cast %a : $@thick Any.Type to $@thick (CP & OP & CP2).Type + %p = unconditional_checked_cast %a : $@thick Any.Type to @thick (CP & OP & CP2).Type return %p : $@thick (CP & OP & CP2).Type } @@ -132,7 +132,7 @@ entry(%a : $@thick Any.Type): // CHECK: ret { i8*, i8** } zeroinitializer sil @c_cast_to_class_existential : $@convention(thin) (@owned AnyObject) -> @owned CP { entry(%a : $AnyObject): - checked_cast_br %a : $AnyObject to $CP, yea, nay + checked_cast_br %a : $AnyObject to CP, yea, nay yea(%p : $CP): return %p : $CP nay: @@ -143,7 +143,7 @@ nay: // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* %1, %swift.type* %0, {{.*}} @"$s5casts2CPMp" sil @c_cast_to_existential_metatype : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick CP.Type { entry(%a : $@thick Any.Type): - checked_cast_br %a : $@thick Any.Type to $@thick CP.Type, yea, nay + checked_cast_br %a : $@thick Any.Type to @thick CP.Type, yea, nay yea(%p : $@thick CP.Type): return %p : $@thick CP.Type nay: @@ -166,7 +166,7 @@ nay: // CHECK: ret { i8*, i8**, i8** } zeroinitializer sil @c_cast_to_class_existential_2 : $@convention(thin) (@owned AnyObject) -> @owned CP & CP2 { entry(%a : $AnyObject): - checked_cast_br %a : $AnyObject to $CP & CP2, yea, nay + checked_cast_br %a : $AnyObject to CP & CP2, yea, nay yea(%p : $CP & CP2): return %p : $CP & CP2 nay: @@ -184,7 +184,7 @@ nay: // CHECK: phi %objc_object* [ [[CAST:%.*]], %success ], [ null, %entry ] sil @c_cast_to_class_existential_mixed : $@convention(thin) (@owned AnyObject) -> @owned CP & OP & CP2 { entry(%a : $AnyObject): - checked_cast_br %a : $AnyObject to $CP & OP & CP2, yea, nay + checked_cast_br %a : $AnyObject to CP & OP & CP2, yea, nay yea(%p : $CP & OP & CP2): return %p : $CP & OP & CP2 nay: @@ -199,7 +199,7 @@ nay: // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8* {{.*}}, %swift.type* %0, {{.*}} @"$s5casts2CPMp" {{[^,]*}}, {{.*}} @"$s5casts3CP2Mp" sil @c_cast_to_existential_metatype_mixed : $@convention(thin) (@owned @thick Any.Type) -> @owned @thick (CP & OP & CP2).Type { entry(%a : $@thick Any.Type): - checked_cast_br %a : $@thick Any.Type to $@thick (CP & OP & CP2).Type, yea, nay + checked_cast_br %a : $@thick Any.Type to @thick (CP & OP & CP2).Type, yea, nay yea(%p : $@thick (CP & OP & CP2).Type): return %p : $@thick (CP & OP & CP2).Type nay: @@ -212,7 +212,7 @@ nay: // CHECK: phi %objc_object* sil @checked_upcast : $@convention(thin) (@owned A) -> @owned AnyObject { entry(%a : $A): - checked_cast_br %a : $A to $AnyObject, yea, nay + checked_cast_br %a : $A to AnyObject, yea, nay yea(%o : $AnyObject): return %o : $AnyObject nay: @@ -231,7 +231,7 @@ nay: // CHECK: br i1 [[COND]] sil @checked_downcast_optional : $@convention(thin) (Optional) -> @owned A { entry(%a : $Optional): - checked_cast_br %a : $Optional to $A, yea, nay + checked_cast_br %a : $Optional to A, yea, nay yea(%aa : $A): return %aa : $A nay: @@ -247,7 +247,7 @@ nay: // CHECK: br i1 [[COND]] sil @checked_downcast_optional_metatype : $@convention(thin) (Optional<@thick A.Type>) -> @thick B.Type { entry(%a : $Optional<@thick A.Type>): - checked_cast_br %a : $Optional<@thick A.Type> to $@thick B.Type, yea, nay + checked_cast_br %a : $Optional<@thick A.Type> to @thick B.Type, yea, nay yea(%b : $@thick B.Type): return %b : $@thick B.Type nay: @@ -263,7 +263,7 @@ nay: // CHECK: br i1 [[COND]] sil @checked_downcast_optional_exmetatype : $@convention(thin) (Optional<@thick CP.Type>) -> @thick B.Type { entry(%a : $Optional<@thick CP.Type>): - checked_cast_br %a : $Optional<@thick CP.Type> to $@thick B.Type, yea, nay + checked_cast_br %a : $Optional<@thick CP.Type> to @thick B.Type, yea, nay yea(%b : $@thick B.Type): return %b : $@thick B.Type nay: @@ -280,7 +280,7 @@ nay: // CHECK: load %swift.type*, %swift.type** [[V4]] sil @checked_downcast_optional_class_to_ex : $@convention(thin) (@guaranteed Optional) -> @owned Optional { bb0(%0 : $Optional): - checked_cast_br %0 : $Optional to $CP, bb1, bb2 + checked_cast_br %0 : $Optional to CP, bb1, bb2 bb1(%3 : $CP): %4 = enum $Optional, #Optional.some!enumelt.1, %3 : $CP @@ -300,22 +300,22 @@ sil @checked_metatype_to_object_casts : $@convention(thin) (@thick Any.Type) entry(%e : $@thick Any.Type): %a = metatype $@thin NotClass.Type // CHECK: br i1 false - checked_cast_br %a : $@thin NotClass.Type to $AnyObject, a_yea, a_nay + checked_cast_br %a : $@thin NotClass.Type to AnyObject, a_yea, a_nay a_yea(%1 : $AnyObject): %b = metatype $@thick A.Type // CHECK: bitcast %swift.type* {{%.*}} to %objc_object* - checked_cast_br %b : $@thick A.Type to $AnyObject, b_yea, b_nay + checked_cast_br %b : $@thick A.Type to AnyObject, b_yea, b_nay b_yea(%2 : $AnyObject): %c = metatype $@objc_metatype A.Type // CHECK: bitcast %objc_class* {{%.*}} to %objc_object* - checked_cast_br %c : $@objc_metatype A.Type to $AnyObject, c_yea, c_nay + checked_cast_br %c : $@objc_metatype A.Type to AnyObject, c_yea, c_nay c_yea(%3 : $AnyObject): %d = metatype $@thick T.Type // CHECK: call %objc_object* @swift_dynamicCastMetatypeToObjectConditional(%swift.type* %T) - checked_cast_br %d : $@thick T.Type to $AnyObject, d_yea, d_nay + checked_cast_br %d : $@thick T.Type to AnyObject, d_yea, d_nay d_yea(%4 : $AnyObject): // CHECK: call %objc_object* @swift_dynamicCastMetatypeToObjectConditional(%swift.type* %0) - checked_cast_br %e : $@thick Any.Type to $AnyObject, e_yea, e_nay + checked_cast_br %e : $@thick Any.Type to AnyObject, e_yea, e_nay e_yea(%5 : $AnyObject): return undef : $() a_nay: @@ -348,7 +348,7 @@ entry(%x : $OB): // CHECK: @swift_dynamicCastClassUnconditional sil @checked_object_to_object_casts : $@convention(thin) (A) -> B { entry(%a : $A): - %b = unconditional_checked_cast %a : $A to $B + %b = unconditional_checked_cast %a : $A to B return %b : $B } @@ -356,6 +356,54 @@ entry(%a : $A): // CHECK: @swift_dynamicCastClassUnconditional sil @checked_objc_object_to_object_casts : $@convention(thin) (OA) -> OB { entry(%a : $OA): - %b = unconditional_checked_cast %a : $OA to $OB + %b = unconditional_checked_cast %a : $OA to OB return %b : $OB } + +protocol P {} +protocol PAnyObject: AnyObject {} +class C {} +sil_vtable C {} + +// CHECK-LABEL: define{{.*}} @cast_protocol_composition_with_anyobject +// CHECK: [[C:%.*]] = call swiftcc %swift.metadata_response @"$s5casts1CCMa" +// CHECK: [[C_META:%.*]] = extractvalue %swift.metadata_response [[C]], 0 +// CHECK: call { i8*, i8** } @dynamic_cast_existential_1_superclass_conditional({{.*}}, %swift.type* [[C_META]], %swift.protocol* {{.*}}@"$s5casts1PMp" + +sil @cast_protocol_composition_with_anyobject : $@convention(thin) (@owned P & AnyObject ) -> @owned Optional { +bb0(%0: $P & AnyObject): + checked_cast_br %0 : $P & AnyObject to C & P, bb1, bb2 + +bb1(%2 : $C & P): + %3 = enum $Optional, #Optional.some!enumelt.1, %2 : $C & P + br bb3(%3 : $Optional) + +bb2: + strong_release %0 : $P & AnyObject + %6 = enum $Optional, #Optional.none!enumelt + br bb3(%6 : $Optional) + +bb3(%11 : $Optional): + return %11 : $Optional +} + +// CHECK-LABEL: define{{.*}} @cast_protocol_with_anyobject +// CHECK: [[C:%.*]] = call swiftcc %swift.metadata_response @"$s5casts1CCMa" +// CHECK: [[C_META:%.*]] = extractvalue %swift.metadata_response [[C]], 0 +// CHECK: call { i8*, i8** } @dynamic_cast_existential_1_superclass_conditional({{.*}}, %swift.type* [[C_META]], %swift.protocol* {{.*}}@"$s5casts10PAnyObjectMp" +sil @cast_protocol_with_anyobject : $@convention(thin) (@owned PAnyObject ) -> @owned Optional { +bb0(%0: $PAnyObject): + checked_cast_br %0 : $PAnyObject to C & PAnyObject, bb1, bb2 + +bb1(%2 : $C & PAnyObject): + %3 = enum $Optional, #Optional.some!enumelt.1, %2 : $C & PAnyObject + br bb3(%3 : $Optional) + +bb2: + strong_release %0 : $PAnyObject + %6 = enum $Optional, #Optional.none!enumelt + br bb3(%6 : $Optional) + +bb3(%11 : $Optional): + return %11 : $Optional +} diff --git a/test/IRGen/clang_inline_opt.swift b/test/IRGen/clang_inline_opt.swift index 4bde0f16c30f0..db38583eaad49 100644 --- a/test/IRGen/clang_inline_opt.swift +++ b/test/IRGen/clang_inline_opt.swift @@ -7,7 +7,7 @@ func return10() -> UInt32 { // Sanity check that we tell Clang to generate optimizable code when // we're optimizing. -// CHECK: define internal{{( zeroext)?}} i32 @return7() [[CLANG_ATTRS:#[0-9]+]] { +// CHECK: define internal{{(zeroext)?}} i32 @return7() [[CLANG_ATTRS:#[0-9]+]] { // CHECK: attributes [[CLANG_ATTRS]] = { // CHECK-NOT: noinline diff --git a/test/IRGen/class_resilience_objc.swift b/test/IRGen/class_resilience_objc.swift index 9161f6c115e73..99294cdb147e9 100644 --- a/test/IRGen/class_resilience_objc.swift +++ b/test/IRGen/class_resilience_objc.swift @@ -4,6 +4,7 @@ // XFAIL: CPU=armv7k // XFAIL: CPU=powerpc64le +// XFAIL: CPU=s390x import Foundation import resilient_struct diff --git a/test/IRGen/class_update_callback_with_fixed_layout.sil b/test/IRGen/class_update_callback_with_fixed_layout.sil index e557e15eeaac3..e7570f1f749c2 100644 --- a/test/IRGen/class_update_callback_with_fixed_layout.sil +++ b/test/IRGen/class_update_callback_with_fixed_layout.sil @@ -4,6 +4,10 @@ // RUN: %target-swift-frontend -I %t -emit-ir -enable-library-evolution %s -read-legacy-type-info-path=%S/Inputs/legacy_type_info/a.yaml | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize // RUN: %target-swift-frontend -I %t -emit-ir -enable-library-evolution -O %s -read-legacy-type-info-path=%S/Inputs/legacy_type_info/a.yaml +// Verify that this feature works with the VFS. +// RUN: sed -e "s|INPUT_DIR|%/S/Inputs|g" -e "s|OUT_DIR|%/t|g" %S/Inputs/legacy_type_info/vfsoverlay.yaml > %t/overlay.yaml +// RUN: %target-swift-frontend -vfsoverlay %/t/overlay.yaml -I %t -emit-ir -enable-library-evolution -O %s -read-legacy-type-info-path=%t/a_moved.yaml + // We only use fragile class layouts when Objective-C interop is enabled. // REQUIRES: objc_interop diff --git a/test/IRGen/class_update_callback_without_fixed_layout_stable_abi.sil b/test/IRGen/class_update_callback_without_fixed_layout_stable_abi.sil index e35ff9ba5579c..1afdce5f73de7 100644 --- a/test/IRGen/class_update_callback_without_fixed_layout_stable_abi.sil +++ b/test/IRGen/class_update_callback_without_fixed_layout_stable_abi.sil @@ -5,4 +5,4 @@ // RUN: %target-swift-frontend -I %t -emit-ir -enable-library-evolution -O %S/class_update_callback_without_fixed_layout.sil -target %target-stable-abi-triple // REQUIRES: objc_interop -// REQUIRES: swift_stable_abi \ No newline at end of file +// REQUIRES: swift_stable_abi diff --git a/test/IRGen/condfail_message.swift b/test/IRGen/condfail_message.swift index 8c3eabb2da652..5a5fc702cf465 100644 --- a/test/IRGen/condfail_message.swift +++ b/test/IRGen/condfail_message.swift @@ -11,6 +11,6 @@ func testit(_ a: Int8) -> Int8 { // CHECK: [[CALLER_LOC:![0-9]+]] = !DILocation(line: 9, column: 12, scope: !{{.*}}) // CHECK: [[LOC]] = !DILocation(line: 0, scope: [[FAILURE_FUNC:![0-9]+]], inlinedAt: [[CALLER_LOC]]) -// CHECK: [[FAILURE_FUNC]] = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, file: {{.*}}, type: [[FUNC_TYPE:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}}) +// CHECK: [[FAILURE_FUNC]] = distinct !DISubprogram(name: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, file: {{.*}}, type: [[FUNC_TYPE:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}}) // CHECK: [[FUNC_TYPE]] = !DISubroutineType(types: null) diff --git a/test/IRGen/conditional_conformances.swift b/test/IRGen/conditional_conformances.swift index 29a16aaa4ed8e..8d9cff14aff4a 100644 --- a/test/IRGen/conditional_conformances.swift +++ b/test/IRGen/conditional_conformances.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc.swift -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass.swift -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift +// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift --check-prefix=CHECK --check-prefix=%target-os // Too many pointer-sized integers in the IR // REQUIRES: PTRSIZE=64 diff --git a/test/IRGen/conditional_conformances_gettypemetdatabyname.swift b/test/IRGen/conditional_conformances_gettypemetdatabyname.swift new file mode 100644 index 0000000000000..bf602f5920b80 --- /dev/null +++ b/test/IRGen/conditional_conformances_gettypemetdatabyname.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -target x86_64-apple-macosx10.99 -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=TYPEBYNAME + +// Too many pointer-sized integers in the IR +// REQUIRES: PTRSIZE=64 +// REQUIRES: OS=macosx + diff --git a/test/IRGen/copy_value_destroy_value.sil b/test/IRGen/copy_value_destroy_value.sil deleted file mode 100644 index 48bf5ef29348d..0000000000000 --- a/test/IRGen/copy_value_destroy_value.sil +++ /dev/null @@ -1,46 +0,0 @@ -// RUN: %target-swift-frontend -parse-sil -emit-ir %s | %FileCheck %s -// REQUIRES: OS=macosx - -// Make sure that we are using type lowering and that we are handling the return -// value correctly. - -sil_stage canonical - -import Builtin - -struct Foo { - var t1 : Builtin.Int32 - var c1 : Builtin.NativeObject - var t2 : Builtin.Int32 - var c2 : Builtin.NativeObject - var t3 : Builtin.Int32 -} - -// CHECK: define{{( protected)?}} swiftcc void @non_trivial( -// CHECK: [[GEP1:%.*]] = getelementptr inbounds %T019copy_value_destroy_B03FooV, %T019copy_value_destroy_B03FooV* %1, i32 0, i32 2 -// CHECK-NEXT: [[VAL1:%.*]] = load %swift.refcounted*, %swift.refcounted** [[GEP1]], align 8 -// CHECK: [[GEP2:%.*]] = getelementptr inbounds %T019copy_value_destroy_B03FooV, %T019copy_value_destroy_B03FooV* %1, i32 0, i32 5 -// CHECK-NEXT: [[VAL2:%.*]] = load %swift.refcounted*, %swift.refcounted** [[GEP2]], align 8 -// CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[VAL1]]) -// CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned [[VAL2]]) -// CHECK: call void @swift_release(%swift.refcounted* [[VAL1]]) -// CHECK: call void @swift_release(%swift.refcounted* [[VAL2]]) -sil [ossa] @non_trivial : $@convention(thin) (@guaranteed Foo) -> () { -bb0(%0 : @guaranteed $Foo): - %1 = copy_value %0 : $Foo - destroy_value %1 : $Foo - %2 = tuple() - return %2 : $() -} - -// CHECK: define{{( protected)?}} swiftcc void @non_trivial_unowned( -// CHECK: call %swift.refcounted* @swift_unownedRetainStrong(%swift.refcounted* returned %0) -// CHECK: call void @swift_release(%swift.refcounted* %0) -sil [ossa] @non_trivial_unowned : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { -bb0(%0 : @guaranteed $Builtin.NativeObject): - %1 = ref_to_unowned %0 : $Builtin.NativeObject to $@sil_unowned Builtin.NativeObject - %2 = copy_unowned_value %1 : $@sil_unowned Builtin.NativeObject - destroy_value %2 : $Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} diff --git a/test/IRGen/dynamic_self_cast.swift b/test/IRGen/dynamic_self_cast.swift new file mode 100644 index 0000000000000..f3d94f97d5d72 --- /dev/null +++ b/test/IRGen/dynamic_self_cast.swift @@ -0,0 +1,91 @@ +// RUN: %target-swift-frontend -emit-ir -disable-objc-interop %s | %FileCheck %s + +// Note: -disable-objc-interop is used to give consistent results on Darwin +// and Linux, avoiding differences like %swift.refcounted -vs- %objc_object, +// etc. + +public class SelfCasts { + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC02toD0yACXDACFZ"(%T17dynamic_self_cast9SelfCastsC*, %swift.type* swiftself) + // CHECK: [[ARG:%.*]] = bitcast %T17dynamic_self_cast9SelfCastsC* %0 to i8* + // CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8* + // CHECK: call i8* @swift_dynamicCastClassUnconditional(i8* [[ARG]], i8* [[METATYPE]], i8* null, i32 0, i32 0) + // CHECK: ret + public static func toSelf(_ s: SelfCasts) -> Self { + return s as! Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC09genericToD0yACXDxlFZ"(%swift.opaque* noalias nocapture, %swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %T, %swift.type* %1, {{.*}}) + // CHECK: ret + public static func genericToSelf(_ s: T) -> Self { + return s as! Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %T17dynamic_self_cast9SelfCastsC* @"$s17dynamic_self_cast9SelfCastsC014classGenericToD0yACXDxRlzClFZ"(%swift.refcounted*, %swift.type* %T, %swift.type* swiftself) + // CHECK: [[ARG:%.*]] = bitcast %swift.refcounted* %0 to i8* + // CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8* + // CHECK: call i8* @swift_dynamicCastClassUnconditional(i8* [[ARG]], i8* [[METATYPE]], i8* null, i32 0, i32 0) + // CHECK: ret + public static func classGenericToSelf(_ s: T) -> Self { + return s as! Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s17dynamic_self_cast9SelfCastsC011genericFromD0xylFZ"(%swift.opaque* noalias nocapture sret, %swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %1, %swift.type* %T, {{.*}}) + // CHECK: ret + public static func genericFromSelf() -> T { + let s = Self() + return s as! T + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc %swift.refcounted* @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ"(%swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %0, %swift.type* %T, {{.*}}) + // CHECK: ret + public static func classGenericFromSelf() -> T { + let s = Self() + return s as! T + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC02toD11ConditionalyACXDSgACFZ"(%T17dynamic_self_cast9SelfCastsC*, %swift.type* swiftself) + // CHECK: [[ARG:%.*]] = bitcast %T17dynamic_self_cast9SelfCastsC* %0 to i8* + // CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8* + // CHECK: call i8* @swift_dynamicCastClass(i8* [[ARG]], i8* [[METATYPE]]) + // CHECK: ret + public static func toSelfConditional(_ s: SelfCasts) -> Self? { + return s as? Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC09genericToD11ConditionalyACXDSgxlFZ"(%swift.opaque* noalias nocapture, %swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %T, %swift.type* %1, {{.*}}) + // CHECK: ret + public static func genericToSelfConditional(_ s: T) -> Self? { + return s as? Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC014classGenericToD11ConditionalyACXDSgxRlzClFZ"(%swift.refcounted*, %swift.type* %T, %swift.type* swiftself) + // CHECK: [[ARG:%.*]] = bitcast %swift.refcounted* %0 to i8* + // CHECK: [[METATYPE:%.*]] = bitcast %swift.type* %1 to i8* + // CHECK: call i8* @swift_dynamicCastClass(i8* [[ARG]], i8* [[METATYPE]]) + // CHECK: ret + public static func classGenericToSelfConditional(_ s: T) -> Self? { + return s as? Self + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s17dynamic_self_cast9SelfCastsC011genericFromD11ConditionalxSgylFZ"(%TSq* noalias nocapture sret, %swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %1, %swift.type* %T, {{.*}}) + // CHECK: ret + public static func genericFromSelfConditional() -> T? { + let s = Self() + return s as? T + } + + // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ"(%swift.type* %T, %swift.type* swiftself) + // CHECK: call i1 @swift_dynamicCast(%swift.opaque* {{%.*}}, %swift.opaque* {{%.*}}, %swift.type* %0, %swift.type* %T, {{.*}}) + // CHECK: ret + public static func classGenericFromSelfConditional() -> T? { + let s = Self() + return s as? T + } + + public required init() {} +} diff --git a/test/IRGen/errors.sil b/test/IRGen/errors.sil index f393b0a1e31d5..bbbcceb5ab08e 100644 --- a/test/IRGen/errors.sil +++ b/test/IRGen/errors.sil @@ -1,4 +1,5 @@ // XFAIL: CPU=powerpc64le +// XFAIL: CPU=s390x // RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime --check-prefix=CHECK-%target-cpu sil_stage canonical @@ -64,6 +65,7 @@ entry(%0 : $AnyObject): // CHECK-armv7k: [[ERRORSLOT:%.*]] = alloca [[SWIFTERROR:.*]] %swift.error*, align // CHECK-arm64: [[ERRORSLOT:%.*]] = alloca [[SWIFTERROR:.*]] %swift.error*, align // CHECK-aarch64: [[ERRORSLOT:%.*]] = alloca [[SWIFTERROR:.*]] %swift.error*, align + // CHECK-powerpc64le: [[ERRORSLOT:%.*]] = alloca [[SWIFTERROR:.*]] %swift.error*, align // CHECK-NEXT: store %swift.error* null, %swift.error** [[ERRORSLOT]], align // CHECK-objc-NEXT: [[RESULT:%.*]] = call swiftcc %objc_object* @try_apply_helper(%objc_object* %0, %swift.refcounted* swiftself undef, %swift.error** noalias nocapture [[SWIFTERROR]]{{( )?}}dereferenceable({{.}}) [[ERRORSLOT]]) diff --git a/test/IRGen/exactcast.sil b/test/IRGen/exactcast.sil index 8e29f80e9ac6f..0ce270ee0bd2a 100644 --- a/test/IRGen/exactcast.sil +++ b/test/IRGen/exactcast.sil @@ -30,7 +30,7 @@ bb0(%0 : $Node): //CHECK-INDIRECT-NEXT: = icmp eq %objc_class* //CHECK-NEXT: = bitcast //CHECK-NEXT: br i1 - checked_cast_br [exact] %0 : $Node to $ParentNode, bb2, bb3 // id: %2 + checked_cast_br [exact] %0 : $Node to ParentNode, bb2, bb3 // id: %2 bb1: // Preds: bb2 bb3 return undef : $Int @@ -67,7 +67,7 @@ bb0(%0 : $BaseBase): //CHECK: bitcast //CHECK: icmp eq %swift.type //CHECK: br - checked_cast_br [exact] %0 : $BaseBase to $DerivedInt, bb2, bb3 // id: %2 + checked_cast_br [exact] %0 : $BaseBase to DerivedInt, bb2, bb3 // id: %2 bb1: return undef : $Int diff --git a/test/IRGen/exactcast2.sil b/test/IRGen/exactcast2.sil index d7992e1808454..9599e42839aa5 100644 --- a/test/IRGen/exactcast2.sil +++ b/test/IRGen/exactcast2.sil @@ -27,7 +27,7 @@ sil @_TFC4main4HashCfMS0_FT_S0_ : $@convention(thin) (@thick Hash.Type) -> @owne sil @_TFC4main4Hash6updatefS0_FT_T_ : $@convention(method) (@guaranteed Hash) -> () { bb0(%0 : $Hash): %1 = class_method %0 : $Hash, #Hash.hash!1 : (Hash) -> () -> (), $@convention(method) (@guaranteed Hash) -> () // user: %9 - checked_cast_br [exact] %0 : $Hash to $MD5, bb2, bb3 // id: %2 + checked_cast_br [exact] %0 : $Hash to MD5, bb2, bb3 // id: %2 bb1: // Preds: bb2 bb3 %3 = tuple () // user: %4 diff --git a/test/IRGen/existentials.sil b/test/IRGen/existentials.sil index 1efe3f4dc484e..d5ce86b9491aa 100644 --- a/test/IRGen/existentials.sil +++ b/test/IRGen/existentials.sil @@ -33,7 +33,7 @@ entry(%s : $CP): %v = ref_to_unmanaged %s : $CP to $@sil_unmanaged CP // CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* returned %0) - %v_copy = copy_unmanaged_value %v : $@sil_unmanaged CP + %v_copy = strong_copy_unmanaged_value %v : $@sil_unmanaged CP // CHECK: call void @swift_release(%swift.refcounted* %0) strong_release %v_copy : $CP diff --git a/test/IRGen/existentials_objc.sil b/test/IRGen/existentials_objc.sil index 8d8f2208ad3f4..0f8e3359a38b0 100644 --- a/test/IRGen/existentials_objc.sil +++ b/test/IRGen/existentials_objc.sil @@ -87,7 +87,7 @@ entry(%s : $CP): %v = ref_to_unmanaged %s : $CP to $@sil_unmanaged CP // CHECK: call %objc_object* @swift_unknownObjectRetain(%objc_object* - %v_copy = copy_unmanaged_value %v : $@sil_unmanaged CP + %v_copy = strong_copy_unmanaged_value %v : $@sil_unmanaged CP // CHECK: call void @swift_unknownObjectRelease(%objc_object* strong_release %v_copy : $CP diff --git a/test/IRGen/foreign_type_metadata.swift b/test/IRGen/foreign_type_metadata.swift new file mode 100644 index 0000000000000..19761bc2d107c --- /dev/null +++ b/test/IRGen/foreign_type_metadata.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend %s -emit-ir | %FileCheck %s + +// REQUIRES: objc_interop + +import Foundation + +// Make sure we emit a metadata accessor for foreign types even if the type +// metadata is not required by this TU. Another TU could require it and the +// linker could choose the less defined one of the two. + +// CHECK: @"$sSo8_NSRangeVMn" = linkonce_odr hidden constant <{ {{.*}}sSo8_NSRangeVMa{{.*}} }>, section "__TEXT,__const" + +func use(_ closure: @escaping (Int) -> ()) {} + +public func captureRange(_ r: NSRange?) { + var l = r + use { + if $0 == 0 { + l = NSRange() + } + } +} diff --git a/test/IRGen/generic_metatypes.swift b/test/IRGen/generic_metatypes.swift index e454e0fd5c6c1..f43d5cfbfa266 100644 --- a/test/IRGen/generic_metatypes.swift +++ b/test/IRGen/generic_metatypes.swift @@ -145,5 +145,5 @@ func makeGenericMetatypes() { // CHECK-NOT: call void @llvm.lifetime.end // CHECK: ret %swift.metadata_response -// CHECK: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } -// CHECK: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" +// CHECK-DAG: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } +// CHECK-DAG: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" diff --git a/test/IRGen/generic_structs.sil b/test/IRGen/generic_structs.sil index 549786e6412de..77dd4817e79bd 100644 --- a/test/IRGen/generic_structs.sil +++ b/test/IRGen/generic_structs.sil @@ -160,8 +160,7 @@ entry(%0 : $*ComplexDynamic, %1 : $*Byteful, %2 : $*A, %3 : $*B, %4 : $*Ch %a = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.a2 // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* - // CHECK: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 8 - // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 2 + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 10 // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 // CHECK: [[BYTES:%.*]] = bitcast %T15generic_structs14ComplexDynamicV* %0 to i8* // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] @@ -169,8 +168,7 @@ entry(%0 : $*ComplexDynamic, %1 : $*Byteful, %2 : $*A, %3 : $*B, %4 : $*Ch %b = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.b // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* - // CHECK: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 8 - // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 3 + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 11 // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 // CHECK: [[BYTES:%.*]] = bitcast %T15generic_structs14ComplexDynamicV* %0 to i8* // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] @@ -179,8 +177,7 @@ entry(%0 : $*ComplexDynamic, %1 : $*Byteful, %2 : $*A, %3 : $*B, %4 : $*Ch %c = struct_element_addr %5 : $*SingleDynamic, #SingleDynamic.x // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* - // CHECK: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 8 - // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 4 + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 12 // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 // CHECK: [[BYTES:%.*]] = bitcast %T15generic_structs14ComplexDynamicV* %0 to i8* // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] diff --git a/test/IRGen/generic_structs.swift b/test/IRGen/generic_structs.swift index e1994c381d204..189c30af268cd 100644 --- a/test/IRGen/generic_structs.swift +++ b/test/IRGen/generic_structs.swift @@ -42,8 +42,7 @@ public struct GenericStruct { // CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s15generic_structs13GenericStructVMa"([[INT]] 0, %swift.type* %T, i8** %T.Proto) // CHECK: [[TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK: [[PTR:%.*]] = bitcast %swift.type* [[TYPE]] to [[INT_32]]* -// CHECK: [[FIELDOFFSETS:%.*]] = getelementptr inbounds [[INT_32]], [[INT_32]]* [[PTR]], [[INT]] [[IDX:4|8]] -// CHECK: [[FIELDOFFSET:%.*]] = getelementptr inbounds [[INT_32]], [[INT_32]]* [[FIELDOFFSETS]], i32 2 +// CHECK: [[FIELDOFFSET:%.*]] = getelementptr inbounds [[INT_32]], [[INT_32]]* [[PTR]], [[INT]] [[IDX:6|10]] // CHECK: [[OFFSET:%.*]] = load [[INT_32]], [[INT_32]]* [[FIELDOFFSET]] // CHECK: [[ADDROFOPT:%.*]] = getelementptr inbounds i8, i8* {{.*}}, [[INT_32]] [[OFFSET]] // CHECK: [[OPTPTR:%.*]] = bitcast i8* [[ADDROFOPT]] to %TSq* diff --git a/test/IRGen/lazy_field_metadata.swift b/test/IRGen/lazy_field_metadata.swift index 9a33e0feb34ad..423d7fcd730eb 100644 --- a/test/IRGen/lazy_field_metadata.swift +++ b/test/IRGen/lazy_field_metadata.swift @@ -33,4 +33,4 @@ struct HasPropertyType { } // NEGATIVE-NOT: @"$s19lazy_field_metadata14TypeOfPropertyVMn" -// NEGATIVE-NOT: @"$s19lazy_field_metadata15HasPropertyTypeVMn" \ No newline at end of file +// NEGATIVE-NOT: @"$s19lazy_field_metadata15HasPropertyTypeVMn" diff --git a/test/IRGen/lazy_metadata_no_reflection.swift b/test/IRGen/lazy_metadata_no_reflection.swift index 677b85f054484..e9896a4bfd776 100644 --- a/test/IRGen/lazy_metadata_no_reflection.swift +++ b/test/IRGen/lazy_metadata_no_reflection.swift @@ -35,4 +35,4 @@ public func forceMetadata() { } // CHECK-LABEL: @"$s4test1SVMn" = hidden constant -// CHECK-LABEL: @"$s4test1SVSQAAMc" = hidden constant \ No newline at end of file +// CHECK-LABEL: @"$s4test1SVSQAAMc" = hidden constant diff --git a/test/IRGen/lazy_opaque_result_type.swift b/test/IRGen/lazy_opaque_result_type.swift index 382ad927ec039..d526623ebff0b 100644 --- a/test/IRGen/lazy_opaque_result_type.swift +++ b/test/IRGen/lazy_opaque_result_type.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -enable-implicit-dynamic -disable-availability-checking -Xllvm -sil-disable-pass=OpaqueArchetypeSpecializer -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll +// RUN: %target-swift-frontend -enable-implicit-dynamic -disable-availability-checking -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll // RUN: %FileCheck %s < %t.ll protocol P { } diff --git a/test/IRGen/lit.local.cfg b/test/IRGen/lit.local.cfg index ff48ffce548be..0597958565874 100644 --- a/test/IRGen/lit.local.cfg +++ b/test/IRGen/lit.local.cfg @@ -5,6 +5,10 @@ config.substitutions.insert(0, ('%build-irgen-test-overlays', '%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk %S/Inputs %S/Inputs/ObjectiveC.swift && ' '%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk %S/Inputs %S/Inputs/Foundation.swift -I %t')) +config.substitutions.insert(0, ('%build-irgen-test-overlays\(mock-sdk-directory: ([^)]+)\)', + SubstituteCaptures(r'%target-swift-frontend -enable-objc-interop -disable-objc-attr-requires-foundation-module -emit-module -o %t -sdk \1 \1/ObjectiveC.swift && ' + r'%target-swift-frontend -enable-objc-interop -emit-module -o %t -sdk \1 \1/Foundation.swift -I %t'))) + def get_target_os(): import re (run_cpu, run_vendor, run_os, run_version) = re.match('([^-]+)-([^-]+)-([^0-9]+)(.*)', config.variant_triple).groups() diff --git a/test/IRGen/metadata.swift b/test/IRGen/metadata.swift index 6ebbb5e000690..dbd61ff466e6a 100644 --- a/test/IRGen/metadata.swift +++ b/test/IRGen/metadata.swift @@ -1,6 +1,24 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift -// RUN: %target-swift-frontend -module-name A -I %t %S/Inputs/metadata2.swift -primary-file %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -module-name A -I %t %S/Inputs/metadata2.swift -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-os + +import resilient_struct + +enum Singleton { + case only +} + +// CHECK: @"$s1A1GC14zeroSizedFieldAA9SingletonOvpWvd" = hidden constant i{{(64|32)}} 0 +// Check that the instance start is after the header (at 8 or 16). +// CHECK-macosx: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}} +// CHECK-ios: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}} +// CHECK-watchos: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}} +// CHECK-tvos: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}} + +class G { + var zeroSizedField = Singleton.only + var r = ResilientInt(i:1) +} // CHECK-LABEL: define {{.*}}swiftcc %swift.metadata_response @"$s1A12MyControllerCMr"(%swift.type*, i8*, i8**) // CHECK-NOT: ret diff --git a/test/IRGen/metatype_casts.sil b/test/IRGen/metatype_casts.sil index 47a896a9884b8..a45330cc73321 100644 --- a/test/IRGen/metatype_casts.sil +++ b/test/IRGen/metatype_casts.sil @@ -12,7 +12,7 @@ import Swift sil @archetype_metatype_cast : $@convention(thin) () -> () { entry: %0 = metatype $@thick T.Type - checked_cast_br %0 : $@thick T.Type to $@thick U.Type, yes, no + checked_cast_br %0 : $@thick T.Type to @thick U.Type, yes, no yes(%1 : $@thick U.Type): br end @@ -30,7 +30,7 @@ protocol P {} // CHECK: [[T1:%.*]] = icmp ne %swift.type* [[T0]], null sil @existential_archetype_metatype_cast : $@convention(thin) (@thick P.Type) -> () { entry(%0 : $@thick P.Type): - checked_cast_br %0 : $@thick P.Type to $@thick T.Type, yes, no + checked_cast_br %0 : $@thick P.Type to @thick T.Type, yes, no yes(%1 : $@thick T.Type): br end @@ -77,7 +77,7 @@ sil_vtable OtherClass {} sil @value_metatype_cast : $@convention(thin) (SomeClass) -> () { entry(%0 : $SomeClass): %1 = value_metatype $@thick SomeClass.Type, %0 : $SomeClass - checked_cast_br [exact] %1 : $@thick SomeClass.Type to $@thick OtherClass.Type, yes, no + checked_cast_br [exact] %1 : $@thick SomeClass.Type to @thick OtherClass.Type, yes, no yes(%2 : $@thick OtherClass.Type): br end @@ -97,7 +97,7 @@ end: // CHECK: ret %swift.type* [[RESULT]] sil @checked_cast_to_anyobject_type : $@convention(thin) (@thick Any.Type) -> @thick AnyObject.Type { entry(%0 : $@thick Any.Type): - %2 = unconditional_checked_cast %0 : $@thick Any.Type to $@thick AnyObject.Type + %2 = unconditional_checked_cast %0 : $@thick Any.Type to @thick AnyObject.Type return %2 : $@thick AnyObject.Type } @@ -111,6 +111,6 @@ entry(%0 : $@thick Any.Type): sil @checked_cast_class_to_anyobject_type : $@convention(thin) () -> @thick AnyObject.Type { entry: %1 = metatype $@thick SomeClass.Type - %2 = unconditional_checked_cast %1 : $@thick SomeClass.Type to $@thick AnyObject.Type + %2 = unconditional_checked_cast %1 : $@thick SomeClass.Type to @thick AnyObject.Type return %2 : $@thick AnyObject.Type } diff --git a/test/IRGen/method_linkage.swift b/test/IRGen/method_linkage.swift index d417df1576431..886191eac93df 100644 --- a/test/IRGen/method_linkage.swift +++ b/test/IRGen/method_linkage.swift @@ -31,12 +31,24 @@ class Base { // RESILIENT: define hidden swiftcc void @"$s14method_linkage4Base{{.*}}5other0 fileprivate func other() { } + + // CHECK: define hidden swiftcc void @"$s14method_linkage4BaseC4prop{{.*}}LLytvg + // RESILIENT: define hidden swiftcc void @"$s14method_linkage4BaseC4prop{{.*}}LLytvg + fileprivate var prop: () { + return () + } } class Derived : Base { - // CHECK: define internal swiftcc void @"$s14method_linkage7Derived{{.*}}3foo0 - // RESILIENT: define internal swiftcc void @"$s14method_linkage7Derived{{.*}}3foo0 + // CHECK: define hidden swiftcc void @"$s14method_linkage7Derived{{.*}}3foo0 + // RESILIENT: define hidden swiftcc void @"$s14method_linkage7Derived{{.*}}3foo0 fileprivate final override func foo() { } + + // CHECK: define hidden swiftcc void @"$s14method_linkage7DerivedC4prop{{.*}}LLytvg + // RESILIENT: define hidden swiftcc void @"$s14method_linkage7DerivedC4prop{{.*}}LLytvg + fileprivate final override var prop: () { + return () + } } extension Base { @@ -92,6 +104,24 @@ open class OpenClass { // RESILIENT: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s14method_linkage9OpenClassC5pquuxyyF" public final func pquux() { } + + // CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s14method_linkage9OpenClassC4prop{{.*}}LLytvg + // RESILIENT: define hidden swiftcc void @"$s14method_linkage9OpenClassC4prop{{.*}}LLytvg + fileprivate var prop: () { + return () + } +} + +open class OpenSubclass : OpenClass { + // CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s14method_linkage12OpenSubclassC4prop{{.*}}LLytvg + // RESILIENT: define hidden swiftcc void @"$s14method_linkage12OpenSubclassC4prop{{.*}}LLytvg + fileprivate final override var prop: () { + return () + } + + // CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s14method_linkage12OpenSubclassC4pbazyyF" + // RESILIENT: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s14method_linkage12OpenSubclassC4pbazyyF" + public final override func pbaz() {} } // Just in case anyone wants to delete unused methods... @@ -100,4 +130,5 @@ func callit(b: Base) { b.bar() b.other() b.extfunc() + _ = b.prop } diff --git a/test/IRGen/multi_file_resilience.swift b/test/IRGen/multi_file_resilience.swift index 5487120c83bda..aaadb088feec4 100644 --- a/test/IRGen/multi_file_resilience.swift +++ b/test/IRGen/multi_file_resilience.swift @@ -28,13 +28,10 @@ // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyFoo(foo: Foo) -> Foo { let copy = foo return copy diff --git a/test/IRGen/multi_module_resilience.swift b/test/IRGen/multi_module_resilience.swift index 06a3896c448f7..b9fdfcf79a1af 100644 --- a/test/IRGen/multi_module_resilience.swift +++ b/test/IRGen/multi_module_resilience.swift @@ -32,13 +32,10 @@ import OtherModule // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[FOO]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[FOO]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyFoo(foo: Foo) -> Foo { let copy = foo return copy @@ -61,13 +58,10 @@ public func copyFoo(foo: Foo) -> Foo { // CHECK: [[DEST:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[BAR]]* %1 to %swift.opaque* // CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) -// Perform 'initializeWithTake' via the VWT. -// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4 -// CHECK: [[T1:%.*]] = load i8*, i8** [[T0]], -// CHECK: [[TAKEFN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* +// Perform 'initializeWithCopy' via the VWT. // CHECK: [[DEST:%.*]] = bitcast [[BAR]]* %0 to %swift.opaque* // CHECK: [[SRC:%.*]] = bitcast [[BAR]]* [[COPY]] to %swift.opaque* -// CHECK: call %swift.opaque* [[TAKEFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) +// CHECK: call %swift.opaque* [[COPYFN]](%swift.opaque* noalias [[DEST]], %swift.opaque* noalias [[SRC]], %swift.type* [[METADATA]]) public func copyBar(bar: Bar) -> Bar { let copy = bar return copy diff --git a/test/IRGen/multithread_module.swift b/test/IRGen/multithread_module.swift index 00e95e389cfc7..83eec8c2d3a1a 100644 --- a/test/IRGen/multithread_module.swift +++ b/test/IRGen/multithread_module.swift @@ -58,8 +58,18 @@ func callproto(_ p: MyProto) { print(p.protofunc()) } +public func mutateBaseArray(_ arr: inout [Base], _ x: Base) { + arr.append(x) +} + + // Check the llvm IR files: +// Check if all specializations from stdlib functions are created in the same LLVM module. + +// CHECK-MAINLL-DAG: define {{.*}} @"$sSa16_createNewBuffer14bufferIsUnique15minimumCapacity13growForAppendySb_SiSbtF4test8MyStructV_Tg5" +// CHECK-MAINLL-DAG: define {{.*}} @"$sSa16_createNewBuffer14bufferIsUnique15minimumCapacity13growForAppendySb_SiSbtF4test4BaseC_Tg5" + // Check if the DI filename is correct and not "". // CHECK-MAINLL: [[F:![0-9]+]] = !DIFile(filename: "{{.*}}IRGen/Inputs/multithread_module/main.swift", directory: "{{.*}}") diff --git a/test/IRGen/objc.swift b/test/IRGen/objc.swift index 190a627f6d50a..424cebccbacab 100644 --- a/test/IRGen/objc.swift +++ b/test/IRGen/objc.swift @@ -145,7 +145,7 @@ class WeakObjC { // CHECK: i32 1, !"Objective-C Version", i32 2} // CHECK: i32 1, !"Objective-C Image Info Version", i32 0} // CHECK: i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} -// 83953408 == (5 << 24) | (1 << 16) | (7 << 8). -// 5 and 0 is the current major.minor version. 7 is the Swift ABI version. -// CHECK: i32 4, !"Objective-C Garbage Collection", i32 83953408} +// 84018944 == (5 << 24) | (2 << 16) | (7 << 8). +// 5 and 2 is the current major.minor version. 7 is the Swift ABI version. +// CHECK: i32 4, !"Objective-C Garbage Collection", i32 84018944} // CHECK: i32 1, !"Swift Version", i32 7} diff --git a/test/IRGen/objc_block_consumed.swift b/test/IRGen/objc_block_consumed.swift new file mode 100644 index 0000000000000..8fc68c2a31346 --- /dev/null +++ b/test/IRGen/objc_block_consumed.swift @@ -0,0 +1,19 @@ + +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-silgen -disable-objc-attr-requires-foundation-module | %FileCheck %s + +// We want to test that IRGen doesn't assert on this code. SIL is the best place +// to file check that the block parameter is actually +1. + +// REQUIRES: CPU=x86_64 +// REQUIRES: objc_interop + +import gizmo + +// CHECK-LABEL: sil hidden [ossa] @$s19objc_block_consumed24passBlockWithConsumedArgyySo5GizmoC_ADtF : $@convention(thin) (@guaranteed Gizmo, @guaranteed Gizmo) -> () { +func passBlockWithConsumedArg(_ g: Gizmo, _ other: Gizmo) { + // CHECK: objc_method %0 : $Gizmo, #Gizmo.perform!1.foreign : (Gizmo) -> (((Gizmo?) -> ())?) -> (), $@convention(objc_method) (Optional<@convention(block) (@owned Optional) -> ()>, Gizmo) -> () + g.perform { other in } +} diff --git a/test/IRGen/objc_bridge.swift b/test/IRGen/objc_bridge.swift index 6425f31b92caf..eef063e5c76d3 100644 --- a/test/IRGen/objc_bridge.swift +++ b/test/IRGen/objc_bridge.swift @@ -130,8 +130,8 @@ var NSS : NSString = NSString() // -- NSString methods don't convert 'self' extension NSString { - // CHECK: define internal [[OPAQUE:.*]]* @"$sSo8NSStringC11objc_bridgeE13nsstrFakePropABvgTo"([[OPAQUE:.*]]*, i8*) unnamed_addr - // CHECK: define internal void @"$sSo8NSStringC11objc_bridgeE13nsstrFakePropABvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr + // CHECK: define internal [[OPAQUE:.*]]* @"$sSo8NSStringC11objc_bridgeE13nsstrFakePropABvgTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { + // CHECK: define internal void @"$sSo8NSStringC11objc_bridgeE13nsstrFakePropABvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc var nsstrFakeProp : NSString { get { return NSS @@ -139,20 +139,20 @@ extension NSString { set {} } - // CHECK: define internal [[OPAQUE:.*]]* @"$sSo8NSStringC11objc_bridgeE11nsstrResultAByFTo"([[OPAQUE:.*]]*, i8*) unnamed_addr + // CHECK: define internal [[OPAQUE:.*]]* @"$sSo8NSStringC11objc_bridgeE11nsstrResultAByFTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { @objc func nsstrResult() -> NSString { return NSS } - // CHECK: define internal void @"$sSo8NSStringC11objc_bridgeE8nsstrArg1syAB_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr + // CHECK: define internal void @"$sSo8NSStringC11objc_bridgeE8nsstrArg1syAB_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc func nsstrArg(s s: NSString) { } } class Bas : NSObject { - // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11strRealPropSSvgTo"([[OPAQUE:.*]]*, i8*) unnamed_addr {{.*}} { - // CHECK: define internal void @"$s11objc_bridge3BasC11strRealPropSSvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11strRealPropSSvgTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC11strRealPropSSvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc var strRealProp : String - // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11strFakePropSSvgTo"([[OPAQUE:.*]]*, i8*) unnamed_addr {{.*}} { - // CHECK: define internal void @"$s11objc_bridge3BasC11strFakePropSSvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11strFakePropSSvgTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC11strFakePropSSvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc var strFakeProp : String { get { return "" @@ -160,12 +160,12 @@ class Bas : NSObject { set {} } - // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC13nsstrRealPropSo8NSStringCvgTo"([[OPAQUE:.*]]*, i8*) unnamed_addr {{.*}} { - // CHECK: define internal void @"$s11objc_bridge3BasC13nsstrRealPropSo8NSStringCvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC13nsstrRealPropSo8NSStringCvgTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC13nsstrRealPropSo8NSStringCvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc var nsstrRealProp : NSString // CHECK: define hidden swiftcc %TSo8NSStringC* @"$s11objc_bridge3BasC13nsstrFakePropSo8NSStringCvg"(%T11objc_bridge3BasC* swiftself) {{.*}} { - // CHECK: define internal void @"$s11objc_bridge3BasC13nsstrFakePropSo8NSStringCvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC13nsstrFakePropSo8NSStringCvsTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc var nsstrFakeProp : NSString { get { return NSS @@ -173,14 +173,14 @@ class Bas : NSObject { set {} } - // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC9strResultSSyFTo"([[OPAQUE:.*]]*, i8*) unnamed_addr {{.*}} { + // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC9strResultSSyFTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { @objc func strResult() -> String { return "" } - // CHECK: define internal void @"$s11objc_bridge3BasC6strArg1sySS_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC6strArg1sySS_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc func strArg(s s: String) { } - // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11nsstrResultSo8NSStringCyFTo"([[OPAQUE:.*]]*, i8*) unnamed_addr {{.*}} { + // CHECK: define internal [[OPAQUE:.*]]* @"$s11objc_bridge3BasC11nsstrResultSo8NSStringCyFTo"([[OPAQUE:.*]]*, i8*) {{[#0-9]*}} { @objc func nsstrResult() -> NSString { return NSS } - // CHECK: define internal void @"$s11objc_bridge3BasC8nsstrArg1sySo8NSStringC_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s11objc_bridge3BasC8nsstrArg1sySo8NSStringC_tFTo"([[OPAQUE:.*]]*, i8*, [[OPAQUE:.*]]*) {{[#0-9]*}} { @objc func nsstrArg(s s: NSString) { } override init() { diff --git a/test/IRGen/objc_casts.sil b/test/IRGen/objc_casts.sil index f9e49bb5dc102..3fad2576f1df8 100644 --- a/test/IRGen/objc_casts.sil +++ b/test/IRGen/objc_casts.sil @@ -25,7 +25,7 @@ protocol ClassProto : class {} sil hidden @checkedClassBoundCast : $@convention(thin) (@thick T.Type, @owned NSObject) -> @owned T { bb0(%unused : $@thick T.Type, %obj : $NSObject): strong_retain %obj : $NSObject - %result = unconditional_checked_cast %obj : $NSObject to $T + %result = unconditional_checked_cast %obj : $NSObject to T strong_release %obj : $NSObject return %result : $T } @@ -41,7 +41,7 @@ bb0(%unused : $@thick T.Type, %obj : $NSObject): // CHECK-NEXT: call i8* @swift_dynamicCastObjCClassUnconditional(i8* [[T1]], i8* [[T3]], {{.*}}) sil hidden @metatype_to_objc_class : $@convention(thin) (@thick T.Type) -> () { bb0(%metatype : $@thick T.Type): - %result = unconditional_checked_cast %metatype : $@thick T.Type to $Foo + %result = unconditional_checked_cast %metatype : $@thick T.Type to Foo %tuple = tuple () return %tuple : $() } @@ -57,7 +57,7 @@ bb0(%metatype : $@thick T.Type): // CHECK-NEXT: call i8* @swift_dynamicCastObjCClassUnconditional(i8* [[T1]], i8* [[T3]], {{.*}}) sil hidden @opt_metatype_to_objc_class : $@convention(thin) (Optional<@thick T.Type>) -> () { bb0(%metatype : $Optional<@thick T.Type>): - %result = unconditional_checked_cast %metatype : $Optional<@thick T.Type> to $Foo + %result = unconditional_checked_cast %metatype : $Optional<@thick T.Type> to Foo %tuple = tuple () return %tuple : $() } @@ -70,7 +70,7 @@ bb0(%metatype : $Optional<@thick T.Type>): // CHECK-NEXT: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[T0]], %swift.type* [[TYPE]], {{.*}} @"$s10objc_casts10ClassProtoMp" sil hidden @swift_class_bounded_to_cp : $@convention(thin) (@owned T) -> @owned ClassProto { entry(%a : $T): - %0 = unconditional_checked_cast %a : $T to $ClassProto + %0 = unconditional_checked_cast %a : $T to ClassProto return %0 : $ClassProto } @@ -82,6 +82,6 @@ entry(%a : $T): // CHECK-NEXT: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[T0]], %swift.type* [[TYPE]], {{.*}} @"$s10objc_casts10ClassProtoMp" sil hidden @objc_class_bounded_to_cp : $@convention(thin) (@owned T) -> @owned ClassProto { entry(%a : $T): - %0 = unconditional_checked_cast %a : $T to $ClassProto + %0 = unconditional_checked_cast %a : $T to ClassProto return %0 : $ClassProto } diff --git a/test/IRGen/objc_class_export.swift b/test/IRGen/objc_class_export.swift index ccb5adc47c545..2c5b42125c4df 100644 --- a/test/IRGen/objc_class_export.swift +++ b/test/IRGen/objc_class_export.swift @@ -81,7 +81,7 @@ struct BigStructWithNativeObjects { @objc func drawInRect(dirty dirty: NSRect) { } - // CHECK: define internal void @"$s17objc_class_export3FooC10drawInRect5dirtyySo6NSRectV_tFTo"([[OPAQUE:%.*]]*, i8*, [[NSRECT]]* byval align 8) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s17objc_class_export3FooC10drawInRect5dirtyySo6NSRectV_tFTo"([[OPAQUE:%.*]]*, i8*, [[NSRECT]]* byval align 8) {{[#0-9]*}} { // CHECK: [[CAST:%[a-zA-Z0-9]+]] = bitcast [[OPAQUE]]* %0 to [[FOO]]* // CHECK: call swiftcc void @"$s17objc_class_export3FooC10drawInRect5dirtyySo6NSRectV_tF"(double {{.*}}, double {{.*}}, double {{.*}}, double {{.*}}, [[FOO]]* swiftself [[CAST]]) // CHECK: } @@ -90,14 +90,14 @@ struct BigStructWithNativeObjects { return NSRect(origin: NSPoint(x: 0, y: 0), size: NSSize(width: 0, height: 0)) } - // CHECK: define internal void @"$s17objc_class_export3FooC6boundsSo6NSRectVyFTo"([[NSRECT]]* noalias nocapture sret, [[OPAQUE4:%.*]]*, i8*) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s17objc_class_export3FooC6boundsSo6NSRectVyFTo"([[NSRECT]]* noalias nocapture sret, [[OPAQUE4:%.*]]*, i8*) {{[#0-9]*}} { // CHECK: [[CAST:%[a-zA-Z0-9]+]] = bitcast [[OPAQUE4]]* %1 to [[FOO]]* // CHECK: call swiftcc { double, double, double, double } @"$s17objc_class_export3FooC6boundsSo6NSRectVyF"([[FOO]]* swiftself [[CAST]]) @objc func convertRectToBacking(r r: NSRect) -> NSRect { return r } - // CHECK: define internal void @"$s17objc_class_export3FooC20convertRectToBacking1rSo6NSRectVAG_tFTo"([[NSRECT]]* noalias nocapture sret, [[OPAQUE5:%.*]]*, i8*, [[NSRECT]]* byval align 8) unnamed_addr {{.*}} { + // CHECK: define internal void @"$s17objc_class_export3FooC20convertRectToBacking1rSo6NSRectVAG_tFTo"([[NSRECT]]* noalias nocapture sret, [[OPAQUE5:%.*]]*, i8*, [[NSRECT]]* byval align 8) {{[#0-9]*}} { // CHECK: [[CAST:%[a-zA-Z0-9]+]] = bitcast [[OPAQUE5]]* %1 to [[FOO]]* // CHECK: call swiftcc { double, double, double, double } @"$s17objc_class_export3FooC20convertRectToBacking1rSo6NSRectVAG_tF"(double {{.*}}, double {{.*}}, double {{.*}}, double {{.*}}, [[FOO]]* swiftself [[CAST]]) diff --git a/test/IRGen/objc_dealloc.sil b/test/IRGen/objc_dealloc.sil index 59899abc69649..2c9a0624b62e4 100644 --- a/test/IRGen/objc_dealloc.sil +++ b/test/IRGen/objc_dealloc.sil @@ -70,7 +70,7 @@ bb0(%0 : @unowned $X, %1 : @unowned $SwiftGizmo): return %7 : $() // id: %8 } -// CHECK: define internal void @"$s12objc_dealloc10SwiftGizmoCfDTo"([[OPAQUE:%.*]]*, i8*) unnamed_addr +// CHECK: define internal void @"$s12objc_dealloc10SwiftGizmoCfDTo"([[OPAQUE:%.*]]*, i8*) {{[#0-9]*}} { sil [ossa] @$s12objc_dealloc10SwiftGizmoCfDTo : $@convention(objc_method) (SwiftGizmo) -> () { bb0(%0 : @unowned $SwiftGizmo): // CHECK-NEXT: entry diff --git a/test/IRGen/objc_enum_multi_file.swift b/test/IRGen/objc_enum_multi_file.swift index 2f70b371933c1..1ef5a4e160f6a 100644 --- a/test/IRGen/objc_enum_multi_file.swift +++ b/test/IRGen/objc_enum_multi_file.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -module-name main -primary-file %s %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main -primary-file %s %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir | %FileCheck %s // RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -emit-module %S/Inputs/objc_enum_multi_file_helper.swift -o %t // RUN: %target-swift-frontend -module-name main -primary-file %s -I %t -DIMPORT -emit-ir | %FileCheck %s diff --git a/test/IRGen/objc_function_merge.swift b/test/IRGen/objc_function_merge.swift new file mode 100644 index 0000000000000..c2e07effb02fc --- /dev/null +++ b/test/IRGen/objc_function_merge.swift @@ -0,0 +1,56 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O %s -module-name=test -o %t/a.out +// RUN: %target-build-swift -O %s -module-name=test -emit-ir | %FileCheck --check-prefix=CHECK-IR %s +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// In some places Foundation is comparing ObjC method pointers. +// Therefore LLVM's function merging pass must not create aliases for identical +// functions, but create thunks. This can be ensured if ObjC methods are not +// created with the unnamed_addr attribute. + +import Foundation + +class Base: NSObject, NSSecureCoding { + @objc public class var supportsSecureCoding: Bool { + return true + } + + @objc let s: String + + func encode(with coder: NSCoder) { + coder.encode(s, forKey: #keyPath(s)) + } + + init(s: String) { + self.s = s + } + + required init?(coder: NSCoder) { + self.s = coder.value(forKey: #keyPath(s)) as! String + } +} + +class Derived : Base { + // Make sure the overridden method is not merged with the base method (without + // creating a thunk), so that the method pointers remain distinct. + @objc public class override var supportsSecureCoding: Bool { + return true + } +} + + +// Check if the objc methods are not generated with the unnamed_addr attribute. +// CHECK-IR-DAG: define {{.*}} @"$s4test4BaseC20supportsSecureCodingSbvgZTo"({{[^\)]*}}) #{{[0-9]+}} { +// CHECK-IR-DAG: define {{.*}} @"$s4test4BaseC6encode4withySo7NSCoderC_tFTo"({{[^\)]*}}) #{{[0-9]+}} { +// CHECK-IR-DAG: define {{.*}} @"$s4test7DerivedC20supportsSecureCodingSbvgZTo"({{[^\)]*}}) #{{[0-9]+}} { + +let d = Derived(s: "") +if #available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *) { + // Check that we don't crash here. + _ = try NSKeyedArchiver.archivedData(withRootObject: d, requiringSecureCoding: true) +} +// CHECK: okay +print("okay") diff --git a/test/IRGen/objc_protocol_multi_file.swift b/test/IRGen/objc_protocol_multi_file.swift index 03affcc33d975..21c739982baa7 100644 --- a/test/IRGen/objc_protocol_multi_file.swift +++ b/test/IRGen/objc_protocol_multi_file.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -primary-file %s %S/Inputs/objc_protocol_multi_file_helper.swift -g -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -primary-file %s %S/Inputs/objc_protocol_multi_file_helper.swift -g -emit-ir | %FileCheck %s // This used to crash . // To tickle the crash, SubProto must not be used elsewhere in this file. diff --git a/test/IRGen/objc_subscripts.swift b/test/IRGen/objc_subscripts.swift index 6beda46f8b313..3113c823aa8f9 100644 --- a/test/IRGen/objc_subscripts.swift +++ b/test/IRGen/objc_subscripts.swift @@ -42,7 +42,7 @@ @objc class SomeObject { @objc subscript (i : Int) -> SomeObject { - // CHECK: define internal [[OPAQUE0:%.*]]* @"$s15objc_subscripts10SomeObjectCyACSicigTo"([[OPAQUE1]]*, i8*, i64) unnamed_addr + // CHECK: define internal [[OPAQUE0:%.*]]* @"$s15objc_subscripts10SomeObjectCyACSicigTo"([[OPAQUE1]]*, i8*, i64) {{[#0-9]*}} { get { // CHECK: call swiftcc %T15objc_subscripts10SomeObjectC* @"$s15objc_subscripts10SomeObjectCyACSicig" return self diff --git a/test/IRGen/objc_types_as_member.sil b/test/IRGen/objc_types_as_member.sil index 6d72b62786c94..013f006b943cb 100644 --- a/test/IRGen/objc_types_as_member.sil +++ b/test/IRGen/objc_types_as_member.sil @@ -9,8 +9,10 @@ import gizmo sil @use_metatype : $@convention(thin) (@thin T.Type) -> () // CHECK-LABEL: define swiftcc void @test(%TSo014OuterTypeInnerB0C* swiftself, %swift.type* %Self, i8** %SelfWitnessTable) -// CHECK: [[TMP:%.*]] = call {{.*}}@"$sSo9OuterTypeCMD" -// CHECK: call swiftcc void @use_metatype(%swift.type* [[TMP]]) +// FIXME: Metadata realization via demangling is disabled for now +// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$sSo9OuterTypeCMa" +// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response %1, 0 +// CHECK: call swiftcc void @use_metatype(%swift.type* [[METADATA]]) // CHECK: ret void sil @test : $@convention(witness_method: NSRuncing) (@guaranteed OuterType.InnerType) -> () { diff --git a/test/IRGen/opaque_result_type_debug.swift b/test/IRGen/opaque_result_type_debug.swift index c4e26d4d201db..a5b0de69c28f4 100644 --- a/test/IRGen/opaque_result_type_debug.swift +++ b/test/IRGen/opaque_result_type_debug.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-availability-checking -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s +// RUN: %target-swift-frontend -enable-library-evolution -disable-availability-checking -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s public protocol P {} extension Int: P {} @@ -24,6 +24,7 @@ public var prop: some P { // CHECK: @"$s24opaque_result_type_debug3FooVQrycipQOMQ" = {{.*}}constant{{.*}} @"$s24opaque_result_type_debug3FooVQrycipMXX" public struct Foo { + public init() {} public subscript() -> some P { return 0 } @@ -33,6 +34,7 @@ public struct Foo { @_silgen_name("use") public func use(_: T) +@inlinable public func bar(genericValue: T) { use(genericValue) diff --git a/test/IRGen/opaque_result_type_private_underlying.swift b/test/IRGen/opaque_result_type_private_underlying.swift new file mode 100644 index 0000000000000..7db2337d4e77d --- /dev/null +++ b/test/IRGen/opaque_result_type_private_underlying.swift @@ -0,0 +1,51 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -primary-file %s -primary-file %S/Inputs/opaque_result_type_private_underlying_2.swift | %FileCheck %s --check-prefix=SINGLEMODULE +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -disable-availability-checking -emit-module -emit-module-path=%t/Repo1.swiftmodule -module-name=Repo1 %S/Inputs/opaque_result_type_private_underlying_2.swift +// RUN: %target-swift-frontend -disable-availability-checking -I %t -emit-ir -primary-file %s -DUSEMODULE | %FileCheck %s --check-prefix=NONRESILIENT +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -disable-availability-checking -enable-library-evolution -emit-module -emit-module-path=%t/Repo1.swiftmodule -module-name=Repo1 %S/Inputs/opaque_result_type_private_underlying_2.swift +// RUN: %target-swift-frontend -disable-availability-checking -I %t -emit-ir -primary-file %s -DUSEMODULE | %FileCheck %s --check-prefix=RESILIENT + +#if USEMODULE +import Repo1 +#endif + +// SINGLEMODULE: s37opaque_result_type_private_underlying18UsePublicInlinableVAA1AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}37opaque_result_type_private_underlying7PublicSV +// NONRESILIENT: s37opaque_result_type_private_underlying18UsePublicInlinableV5Repo11AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}5Repo17PublicSV +// RESILIENT: s37opaque_result_type_private_underlying18UsePublicInlinableV5Repo11AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}5Repo17PublicSV +public struct UsePublicInlinable : A { + public init() {} + public func bindAssoc() -> some Q { + return PublicUnderlyingInlinable().bindAssoc() + } +} + +// SINGLEMODULE: s37opaque_result_type_private_underlying9UsePublicVAA1AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}37opaque_result_type_private_underlying7PublicSV +// NONRESILIENT: s37opaque_result_type_private_underlying9UsePublicV5Repo11AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}5Repo17PublicSV +// RESILIENT: s37opaque_result_type_private_underlying9UsePublicV5Repo11AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 5Repo116PublicUnderlyingV9bindAssocQryFQO +public struct UsePublic : A { + public init() {} + public func bindAssoc() -> some Q { + return PublicUnderlying().bindAssoc() + } +} + +// SINGLEMODULE: s37opaque_result_type_private_underlying11UseInternalVAA1AAAMA" = internal constant { {{.*}}symbolic {{(_____ )?}}37opaque_result_type_private_underlying9InternalSV +// NONRESILIENT: s37opaque_result_type_private_underlying11UseInternalV5Repo11AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 5Repo118InternalUnderlyingV9bindAssocQryFQO +// RESILIENT: s37opaque_result_type_private_underlying11UseInternalV5Repo11AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 5Repo118InternalUnderlyingV9bindAssocQryFQO +public struct UseInternal: A { + public init() {} + public func bindAssoc() -> some Q { + return InternalUnderlying().bindAssoc() + } +} + +// SINGLEMODULE: s37opaque_result_type_private_underlying10UsePrivateVAA1AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 37opaque_result_type_private_underlying17PrivateUnderlyingV9bindAssocQryFQO +// NONRESILIENT: s37opaque_result_type_private_underlying10UsePrivateV5Repo11AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 5Repo117PrivateUnderlyingV9bindAssocQryFQO +// RESILIENT: s37opaque_result_type_private_underlying10UsePrivateV5Repo11AAAMA" = internal constant { {{.*}}symbolic _____y_Qo_ 5Repo117PrivateUnderlyingV9bindAssocQryFQO +public struct UsePrivate: A { + public init() {} + public func bindAssoc() -> some Q { + return PrivateUnderlying().bindAssoc() + } +} diff --git a/test/IRGen/opaque_result_type_substitution.swift b/test/IRGen/opaque_result_type_substitution.swift index e5d80c020f113..5fbe15e35217e 100644 --- a/test/IRGen/opaque_result_type_substitution.swift +++ b/test/IRGen/opaque_result_type_substitution.swift @@ -1,21 +1,22 @@ -// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -primary-file %s | %FileCheck %s +// RUN: %target-swift-frontend -enable-library-evolution -disable-availability-checking -emit-ir -primary-file %s | %FileCheck %s -protocol E {} +public protocol E {} -struct Pair : E { +public struct Pair : E { var fst : T var snd : V - init(_ f: T, _ s: V) { + public init(_ f: T, _ s: V) { self.fst = f self.snd = s } - func foobar() -> some E { + public func foobar() -> some E { return self } } +@inlinable public func usePair(_ t: T, _ v: V) { var x = Pair(t, v) let q = x.foobar() diff --git a/test/IRGen/original-defined-attr-linker-directives-fail.swift b/test/IRGen/original-defined-attr-linker-directives-fail.swift new file mode 100644 index 0000000000000..0eede2d3bb264 --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-directives-fail.swift @@ -0,0 +1,22 @@ +// REQUIRES: OS=macosx +// RUN: %empty-directory(%t) +// RUN: echo '[ { "module": "OriginalModule", "platforms": ["macOS"], "install_name": "/System/OriginalModule.dylib"} ]' > %t/install-name.json +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE -previous-module-installname-map-file %t/install-name.json | %FileCheck %s -check-prefix=CHECK-SYMBOLS + +// RUN: NOT %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE -previous-module-installname-map-file %t/nil.json >& %t/error.txt +// RUN: %FileCheck %s -check-prefix=CHECK-ERROR < %t/error.txt + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-SYMBOLS: $ld$previous$/System/OriginalModule.dylib$$1$10.8$10.10$_$s14OriginalModule6EntityVMn$ +// CHECK-SYMBOLS: $ld$previous$/System/OriginalModule.dylib$$1$10.8$10.10$_$s14OriginalModule6EntityV03addC0yyACF$ +// CHECK-SYMBOLS: $ld$previous$/System/OriginalModule.dylib$$1$10.8$10.10$_$s14OriginalModule6EntityVN$ +// CHECK-SYMBOLS: $ld$previous$/System/OriginalModule.dylib$$1$10.8$10.10$_$s14OriginalModule6EntityVMa$ +// CHECK-SYMBOLS: $ld$previous$/System/OriginalModule.dylib$$1$10.8$10.10$_$s14OriginalModule6EntityV06removeC0yyACF$ + +// CHECK-ERROR: cannot open previous install name map diff --git a/test/IRGen/original-defined-attr-linker-hide-ios.swift b/test/IRGen/original-defined-attr-linker-hide-ios.swift new file mode 100644 index 0000000000000..02ddaf7932d1f --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide-ios.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s + +// REQUIRES: OS=ios + +@available(iOS 5.0, OSX 10.10, *) +@_originallyDefinedIn(module: "OriginalModule", iOS 5.4, OSX 10.13) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK: $ld$hide$os5.0$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.1$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.2$_$s14OriginalModule6EntityVN +// CHECK: $ld$hide$os5.3$_$s14OriginalModule6EntityVN +// CHECK-NOT: $ld$hide$os5.4$_$s14OriginalModule6EntityVN diff --git a/test/IRGen/original-defined-attr-linker-hide.swift b/test/IRGen/original-defined-attr-linker-hide.swift new file mode 100644 index 0000000000000..a6bb17141b875 --- /dev/null +++ b/test/IRGen/original-defined-attr-linker-hide.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-SAMEMAJOR --check-prefix=CHECK-DIFFMAJOR +// REQUIRES: OS=macosx + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-SAMEMAJOR: $ld$hide$os10.8$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR: $ld$hide$os10.9$_$s14OriginalModule6EntityVN +// CHECK-SAMEMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule6EntityVN + +@available(OSX 9.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.10) +public struct OldEntity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +// CHECK-DIFFMAJOR: $ld$hide$os9.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.13$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os9.30$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.8$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR: $ld$hide$os10.9$_$s14OriginalModule9OldEntityVN +// CHECK-DIFFMAJOR-NOT: $ld$hide$os10.10$_$s14OriginalModule9OldEntityVN diff --git a/test/IRGen/original-defined-attr.swift b/test/IRGen/original-defined-attr.swift new file mode 100644 index 0000000000000..65ce1ce378152 --- /dev/null +++ b/test/IRGen/original-defined-attr.swift @@ -0,0 +1,74 @@ +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name CurrentModule -D CURRENT_MODULE | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-CURRENT --check-prefix=CHECK-CURRENT-%target-ptrsize +// RUN: %target-swift-frontend -swift-version 4 -enforce-exclusivity=checked %s -emit-ir -module-name OriginalModule | %FileCheck %s --check-prefix=CHECK-COMMON --check-prefix=CHECK-ORIGINAL --check-prefix=CHECK-ORIGINAL-%target-ptrsize +// REQUIRES: OS=macosx + +#if CURRENT_MODULE + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.15) +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.15) +public protocol Movable { + func MovableFuncFoo() +} + +public protocol Unmoveable {} + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "OriginalModule", macOS 10.15) +public class MovedClass: Movable, Unmoveable { + public func MovableFuncFoo() {} +} + +public class UnmovableClass {} + +#else + +public struct Entity { + public func addEntity(_ e: Entity) {} + public func removeEntity(_ e: Entity) {} +} + +public protocol Movable { + func MovableFuncFoo() +} + +public protocol Unmoveable {} + +public class MovedClass: Movable, Unmoveable { + public func MovableFuncFoo() {} +} + +public class UnmovableClass {} + +#endif + + +func entityClient() { + let root = Entity() + // CHECK-COMMON: call swiftcc void @"$s14OriginalModule6EntityVACycfC"() + let leaf = Entity() + // CHECK-COMMON: call swiftcc void @"$s14OriginalModule6EntityVACycfC"() + root.addEntity(leaf) + // CHECK-COMMON: call swiftcc void @"$s14OriginalModule6EntityV03addC0yyACF"() + let moved = MovedClass() + // CHECK-COMMON: call swiftcc %T14OriginalModule10MovedClassC* @"$s14OriginalModule10MovedClassCACycfC" + moved.MovableFuncFoo() + // CHECK-COMMON: call swiftcc void @"$s14OriginalModule10MovedClassC14MovableFuncFooyyF" +} + +public func unmovableClient() { + let unmovable = UnmovableClass() + // CHECK-CURRENT-64: call swiftcc %swift.metadata_response @"$s13CurrentModule14UnmovableClassCMa"(i64 0) + // CHECK-ORIGINAL-64: call swiftcc %swift.metadata_response @"$s14OriginalModule14UnmovableClassCMa"(i64 0) + // CHECK-CURRENT-32: call swiftcc %swift.metadata_response @"$s13CurrentModule14UnmovableClassCMa"(i32 0) + // CHECK-ORIGINAL-32: call swiftcc %swift.metadata_response @"$s14OriginalModule14UnmovableClassCMa"(i32 0) +} + +entityClient() +unmovableClient() diff --git a/test/IRGen/partial_apply.sil b/test/IRGen/partial_apply.sil index 87974e3d3c67c..13135833dce0f 100644 --- a/test/IRGen/partial_apply.sil +++ b/test/IRGen/partial_apply.sil @@ -377,8 +377,8 @@ sil_vtable Sub {} sil @parametric_casting_closure : $@convention(thin) (@owned Base) -> @owned C { bb0(%0 : $Base): - %1 = unconditional_checked_cast %0 : $Base to $C - return %1 : $C + %1 = unconditional_checked_cast %0 : $Base to C + return %1 : $C } sil public_external @receive_closure : $@convention(thin) (@owned @callee_owned () -> (@owned C)) -> () @@ -527,7 +527,14 @@ enum GenericEnum2 { sil public_external @generic_indirect_return2 : $@convention(thin) (Int) -> @owned GenericEnum2 // CHECK-LABEL: define{{.*}} @partial_apply_generic_indirect_return2 -// CHECK: insertvalue {{.*}}$s24generic_indirect_return2TA +// CHECK: [[CTX:%.]] = call noalias %swift.refcounted* @swift_allocObject +// CHECK: store {{.*}}$s24generic_indirect_return2TA +// CHECK: store %swift.refcounted* [[CTX]] +// CHECK: [[FN:%.*]] = load i8* +// CHECK: [[CTX2:%.*]] = load %swift.refcounted* +// CHECK: [[R1:%.]] = insertvalue { i8*, %swift.refcounted* } undef, i8* [[FN]], 0 +// CHECK: [[R2:%.*]] = insertvalue { i8*, %swift.refcounted* } [[R1]], %swift.refcounted* [[CTX2]], 1 +// CHECK: ret { i8*, %swift.refcounted* } [[R2]] // CHECK-LABEL: define internal swiftcc void @"$s24generic_indirect_return2TA"(%T13partial_apply12GenericEnum2OySiG* noalias nocapture sret, %swift.refcounted* swiftself) // CHECK: [[CASTED_ADDR:%.*]] = bitcast %T13partial_apply12GenericEnum2OySiG* %0 to %T13partial_apply12GenericEnum2O* diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..8c77d62e50823 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,33 @@ +// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +precedencegroup AssignmentPrecedence {} + +class Namespace {} + +class Zang { +} + +extension Namespace where T == Zang { + class ExtensionNonGeneric {} +} + +@inline(never) +func consume(_ t: T) { + Builtin.fixLifetime(t) +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[METADATA_RESPONSE:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s4main9NamespaceCA2A4ZangCRszlE19ExtensionNonGenericCyAE_GMa"([[INT]] 0) #{{[0-9]+}} +// CHECK: [[METADATA:%[0-9]+]] = extractvalue %swift.metadata_response [[METADATA_RESPONSE]], 0 +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %5, %swift.type* [[METADATA]]) +// CHECK: } +func doit() { + consume( Namespace.ExtensionNonGeneric() ) +} + +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..b1dff106c6518 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,56 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5Value33_BD97D79156BC586FAA2AE8FB32F3D812LLVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1:[0-9A-Z_]+]]VMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +fileprivate struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define internal swiftcc %swift.metadata_response @"$s4main5Value[[UNIQUE_ID_1]]VMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5Value[[UNIQUE_ID_1]]VySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5Value[[UNIQUE_ID_1]]VMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..9a5f1d7f91bef --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,58 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceC5ValueVySi_GMf" = internal constant <{ +// CHECk-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVySi_GWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + + +final class Namespace { + struct Value { + let first: Arg + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: br i1 [[EQUAL_TYPES_1_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySi_GMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift new file mode 100644 index 0000000000000..13da1ee56a613 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -0,0 +1,23 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #0 { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* bitcast ([[INT]]* getelementptr inbounds (<{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>, <{ i8**, [[INT]], <{ i32, i32, i32, i32, i32, i32, i32 }>*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVMf", i32 0, i32 1) to %swift.type*)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift new file mode 100644 index 0000000000000..42dff49c51270 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -0,0 +1,29 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift new file mode 100644 index 0000000000000..3848a109a2764 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -0,0 +1,63 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external{{( dllimport)?}} global %swift.full_type + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +protocol P {} +extension Int : P {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift new file mode 100644 index 0000000000000..054f4cf84b7ea --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -0,0 +1,34 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK-NOT: @"$s4main5ValueVyAA7IntegerVGMf" +struct Value { + let first: First +} + +struct Integer : Hashable { + let value: Int +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +func doit() { + consume( Value(first: Integer(value: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_CONFORMANCE:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_CONFORMANCE]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift new file mode 100644 index 0000000000000..dfd9a6c74cc9d --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -0,0 +1,39 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Outer { + let first: First +} + +struct Inner { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// TODO: Once prespecialization is done for generic arguments which are +// themselves generic (Outer>, here), a direct reference to +// the prespecialized metadata should be emitted here. +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5OuterVyAA5InnerVySiGGMD") +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume( Outer(first: Inner(first: 13)) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..a795c6e59df83 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,56 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift new file mode 100644 index 0000000000000..0d24ee487f082 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -0,0 +1,68 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external{{( dllimport)?}} global %swift.full_type + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +protocol P {} +protocol Q {} +extension Int : P {} +extension Int : Q {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, i8**, i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TABLE_1:%[0-9]+]] = bitcast i8** %2 to i8* +// CHECK: [[ERASED_TABLE_2:%[0-9]+]] = bitcast i8** %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* [[ERASED_TABLE_1]], i8* [[ERASED_TABLE_2]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift new file mode 100644 index 0000000000000..ef389a11c716c --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -0,0 +1,81 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ +// CHECk-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECk-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi64_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdGWV", i32 0, i32 0), +// CHECk-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSdN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift new file mode 100644 index 0000000000000..521dd19ffe7d2 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -0,0 +1,72 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external{{( dllimport)?}} global %swift.full_type + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +protocol P {} +protocol Q {} +protocol R {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift new file mode 100644 index 0000000000000..3102bc9930274 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -0,0 +1,108 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi64_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSdN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift new file mode 100644 index 0000000000000..f3d26c4aa84b6 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -0,0 +1,76 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external{{( dllimport)?}} global %swift.full_type + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift new file mode 100644 index 0000000000000..ca964defcb8fd --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -0,0 +1,115 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi8_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$ss5UInt8VN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi64_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSdN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECk-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift new file mode 100644 index 0000000000000..e01d083d6cb94 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -0,0 +1,80 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$sytN" = external{{( dllimport)?}} global %swift.full_type + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i8**, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECk-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0) +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1PAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1QAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1RAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1SAAWP", i32 0, i32 0), +// CHECK-SAME: i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$sSi4main1TAAWP", i32 0, i32 0), +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +protocol P {} +protocol Q {} +protocol R {} +protocol S {} +protocol T {} +extension Int : P {} +extension Int : Q {} +extension Int : R {} +extension Int : S {} +extension Int : T {} +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], i8**) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_ARGUMENT_BUFFER:%[0-9]+]] = bitcast i8** %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[ERASED_TYPE_ADDRESS:%[0-9]+]] = getelementptr i8*, i8** %1, i64 0 +// CHECK: %"load argument at index 0 from buffer" = load i8*, i8** [[ERASED_TYPE_ADDRESS]] +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), %"load argument at index 0 from buffer" +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i8**, i8**, i8**, i8**, i8**, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* [[ERASED_ARGUMENT_BUFFER]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift new file mode 100644 index 0000000000000..0680062c7c2f7 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -0,0 +1,159 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + + +// CHECK: @"$s4main5ValueVys4Int8VGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi8_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys4Int8VGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$ss4Int8VN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVys5UInt8VGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi8_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$ss5UInt8VN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_[[ALIGNMENT]] to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF target +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", i32 0{{(, \[4 x i8\] zeroinitializer)?}}, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sBi64_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSdN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) + consume( Value(first: 13.0) ) + consume( Value(first: "13") ) + consume( Value(first: 13 as UInt8) ) + consume( Value(first: 13 as Int8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1]] +// CHECK: br i1 [[EQUAL_TYPES_1]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_2:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2]] +// CHECK: br i1 [[EQUAL_TYPES_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_3:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3]] +// CHECK: br i1 [[EQUAL_TYPES_3]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_4:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4]] +// CHECK: br i1 [[EQUAL_TYPES_4]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES_5:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5]] +// CHECK: br i1 [[EQUAL_TYPES_5]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVys4Int8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift new file mode 100644 index 0000000000000..d8003b4b7e322 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays(mock-sdk-directory: %S/../Inputs) +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// REQUIRES: objc_interop + +import Foundation + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +struct Value { + let value: T + + init(_ value: T) { + self.value = value + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5ValueVySo12NSDictionaryCGMD") #{{[0-9]+}} +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* [[TYPE]]) +// CHECK: } +func doit() { + consume(Value(NSDictionary())) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..1786c68b9a46e --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,63 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVySS_SiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSSN", +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +final class Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..1c9b9fd78db4d --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -0,0 +1,63 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceO5ValueVySS_SiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceO5ValueVySS_SiGWV", i32 0, i32 0) +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSSN", +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +enum Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceO5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceO5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceO5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..abc8ff26c07d9 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -0,0 +1,63 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceV5ValueVySS_SiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceV5ValueVySS_SiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSSN", +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +struct Namespace { + struct Value { + let first: First + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceV5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main9NamespaceV5ValueVySS_SiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceV5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift new file mode 100644 index 0000000000000..00c7687d1ea0c --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -0,0 +1,31 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: } +func doit() { +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift new file mode 100644 index 0000000000000..8fd6839978295 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -0,0 +1,61 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift new file mode 100644 index 0000000000000..c8a714665d342 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -0,0 +1,90 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift new file mode 100644 index 0000000000000..ce3a93ad70ff4 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -0,0 +1,119 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAMEL [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift new file mode 100644 index 0000000000000..fe4e7d319c0e3 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -0,0 +1,148 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift new file mode 100644 index 0000000000000..ae00a9425fbdb --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -0,0 +1,158 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVys5UInt8VSSGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVys5UInt8VSSGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVys5UInt8VSSGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVys5UInt8VSSGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$ss5UInt8VN", %swift.type* @"$sSSN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSSdGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast (%swift.opaque* ([{{[0-9]+}} x i8]*, [{{[0-9]+}} x i8]*, %swift.type*)* @"$s4main5ValueVySSSdGwCP" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwxx" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwcp" to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwca" to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (%swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* @"$s4main5ValueVySSSdGwta" to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySSSdGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySSSdGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySSSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySSSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSdN", i32 0, i32 16, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK_SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVySdSiGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVySdSiGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVySdSiGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySdSiGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSdN", %swift.type* @"$sSiN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main5ValueVyS2iGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main5ValueVyS2iGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore COMDAT on PE/COFF targets +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main5ValueVyS2iGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVyS2iGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSiN", %swift.type* @"$sSiN", i32 0, i32 [[ALIGNMENT]], i64 3 }>, align [[ALIGNMENT]] +struct Value { + let first: First + let second: Second +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13, second: 13) ) + consume( Value(first: 13.0, second: 13) ) + consume( Value(first: "13.0", second: 13.0) ) + consume( Value(first: 13 as UInt8, second: "13.0") ) + consume( Value(first: 13 as Int8, second: 13 as UInt8) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[TYPE_COMPARISON_2:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_2]]: +// CHECK: [[EQUAL_TYPE_2_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_2_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_2_1]] +// CHECK: [[EQUAL_TYPE_2_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_2_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_2_1]], [[EQUAL_TYPE_2_2]] +// CHECK: br i1 [[EQUAL_TYPES_2_2]], label %[[EXIT_PRESPECIALIZED_2:[0-9]+]], label %[[TYPE_COMPARISON_3:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_3]]: +// CHECK: [[EQUAL_TYPE_3_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_3_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_3_1]] +// CHECK: [[EQUAL_TYPE_3_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_3_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_3_1]], [[EQUAL_TYPE_3_2]] +// CHECK: br i1 [[EQUAL_TYPES_3_2]], label %[[EXIT_PRESPECIALIZED_3:[0-9]+]], label %[[TYPE_COMPARISON_4:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_4]]: +// CHECK: [[EQUAL_TYPE_4_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_4_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_4_1]] +// CHECK: [[EQUAL_TYPE_4_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_4_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_4_1]], [[EQUAL_TYPE_4_2]] +// CHECK: br i1 [[EQUAL_TYPES_4_2]], label %[[EXIT_PRESPECIALIZED_4:[0-9]+]], label %[[TYPE_COMPARISON_5:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_5]]: +// CHECK: [[EQUAL_TYPE_5_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss4Int8VN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_5_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_5_1]] +// CHECK: [[EQUAL_TYPE_5_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$ss5UInt8VN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_5_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_5_1]], [[EQUAL_TYPE_5_2]] +// CHECK: br i1 [[EQUAL_TYPES_5_2]], label %[[EXIT_PRESPECIALIZED_5:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVyS2iGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_2]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySdSiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_3]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVySSSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_4]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys5UInt8VSSGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_PRESPECIALIZED_5]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main5ValueVys4Int8Vs5UInt8VGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..77c31c0675727 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -0,0 +1,66 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGWV" = linkonce_odr hidden constant %swift.vwtable { +// CHECK-SAME: i8* bitcast ({{(%swift.opaque\* \(\[[0-9]+ x i8\]\*, \[[0-9]+ x i8\]\*, %swift.type\*\)\* @"\$[a-zA-Z0-9_]+" to i8\*|i8\* \(i8\*, i8\*, %swift.type\*\)\* @__swift_memcpy[0-9]+_[0-9]+ to i8\*)}}), +// CHECK-SAME: i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy{{[0-9]+}}_{{[0-9]+}} to i8*), +// CHECK-SAME: i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwet" to i8*), +// CHECK-SAME: i8* bitcast (void (%swift.opaque*, i32, i32, %swift.type*)* @"$s4main9NamespaceC5ValueVySS_SiSdGwst" to i8*), +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: [[INT]] {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}}, +// CHECK-SAME: i32 {{[0-9]+}} +// CHECK-SAME: }, +// NOTE: ignore the COMDAT on PE/COFF platforms +// CHECK-SAME: align [[ALIGNMENT]] + +// CHECK: @"$s4main9NamespaceC5ValueVySS_SiSdGMf" = internal constant <{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }> <{ i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main9NamespaceC5ValueVySS_SiSdGWV", i32 0, i32 0), [[INT]] 512, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*), %swift.type* @"$sSSN", %swift.type* @"$sSiN", %swift.type* @"$sSdN", i32 0, i32 8, i64 3 }>, align [[ALIGNMENT]] +final class Namespace { + struct Value { + let first: First + let second: Second + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Namespace.Value(first: 13, second: 13.0) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceC5ValueVMa"([[INT]], %swift.type*, %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: [[ERASED_TYPE_3:%[0-9]+]] = bitcast %swift.type* %3 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: [[EQUAL_TYPE_1_3:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSdN" to i8*), [[ERASED_TYPE_3]] +// CHECK: [[EQUAL_TYPES_1_3:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_2]], [[EQUAL_TYPE_1_3]] +// CHECK: br i1 [[EQUAL_TYPES_1_3]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, %swift.type*, %swift.type*, i32, i32, i64 }>* @"$s4main9NamespaceC5ValueVySS_SiSdGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE_1]], i8* [[ERASED_TYPE_2]], i8* [[ERASED_TYPE_3]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main9NamespaceC5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift new file mode 100644 index 0000000000000..c51b6b64eb0d3 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -0,0 +1,57 @@ +// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main5ValueVySiGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32{{(, \[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** @"$sB[[INT]]_WV", +// i8** getelementptr inbounds (%swift.vwtable, %swift.vwtable* @"$s4main5ValueVySiGWV", i32 0, i32 0), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: i32 0{{(, \[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, align [[ALIGNMENT]] + +@frozen +public struct Value { + let first: First +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1)) +// CHECK: } +func doit() { + consume( Value(first: 13) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define{{( protected| dllexport)?}} swiftcc %swift.metadata_response @"$s4main5ValueVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { %swift.type* getelementptr inbounds (%swift.full_type, %swift.full_type* bitcast (<{ i8**, [[INT]], %swift.type_descriptor*, %swift.type*, i32{{(, \[4 x i8\])?}}, i64 }>* @"$s4main5ValueVySiGMf" to %swift.full_type*), i32 0, i32 1), [[INT]] 0 } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5ValueVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } diff --git a/test/IRGen/protocol_with_superclass.sil b/test/IRGen/protocol_with_superclass.sil index 1b412b434c200..e4adade3b87c8 100644 --- a/test/IRGen/protocol_with_superclass.sil +++ b/test/IRGen/protocol_with_superclass.sil @@ -36,7 +36,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %5 = unconditional_checked_cast %0 : $Concrete to $ProtoRefinesClass + %5 = unconditional_checked_cast %0 : $Concrete to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %5 : $ProtoRefinesClass @@ -47,7 +47,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %6 = unconditional_checked_cast %0 : $Concrete to $SubProto + %6 = unconditional_checked_cast %0 : $Concrete to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %6 : $SubProto @@ -60,7 +60,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %7 = unconditional_checked_cast %1 : $SuperProto to $ProtoRefinesClass + %7 = unconditional_checked_cast %1 : $SuperProto to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %7 : $ProtoRefinesClass @@ -73,7 +73,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %8 = unconditional_checked_cast %1 : $SuperProto to $SubProto + %8 = unconditional_checked_cast %1 : $SuperProto to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %8 : $SubProto @@ -84,7 +84,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %9 = unconditional_checked_cast %2 : $SuperProto & Concrete to $ProtoRefinesClass + %9 = unconditional_checked_cast %2 : $SuperProto & Concrete to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %9 : $ProtoRefinesClass @@ -95,7 +95,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %10 = unconditional_checked_cast %2 : $SuperProto & Concrete to $SubProto + %10 = unconditional_checked_cast %2 : $SuperProto & Concrete to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %10 : $SubProto @@ -106,7 +106,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass7DerivedC* - %11 = unconditional_checked_cast %3 : $ProtoRefinesClass to $Derived + %11 = unconditional_checked_cast %3 : $ProtoRefinesClass to Derived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass7DerivedC*)*)(%T24protocol_with_superclass7DerivedC* [[REFERENCE]]) strong_release %11 : $Derived @@ -117,7 +117,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass10SubDerivedC* - %12 = unconditional_checked_cast %3 : $ProtoRefinesClass to $SubDerived + %12 = unconditional_checked_cast %3 : $ProtoRefinesClass to SubDerived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass10SubDerivedC*)*)(%T24protocol_with_superclass10SubDerivedC* [[REFERENCE]]) strong_release %12 : $SubDerived @@ -128,7 +128,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %13 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & Concrete + %13 = unconditional_checked_cast %3 : $ProtoRefinesClass to OtherProto & Concrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %13 : $OtherProto & Concrete @@ -141,7 +141,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], %swift.type* [[METADATA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass11SubConcreteC* - %14 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & SubConcrete + %14 = unconditional_checked_cast %3 : $ProtoRefinesClass to OtherProto & SubConcrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11SubConcreteC*)*)(%T24protocol_with_superclass11SubConcreteC* [[REFERENCE]]) strong_release %14 : $OtherProto & SubConcrete @@ -152,7 +152,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass11MoreDerivedC* - %15 = unconditional_checked_cast %4 : $SubProto to $MoreDerived + %15 = unconditional_checked_cast %4 : $SubProto to MoreDerived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11MoreDerivedC*)*)(%T24protocol_with_superclass11MoreDerivedC* [[REFERENCE]]) strong_release %15 : $MoreDerived @@ -163,7 +163,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %16 = unconditional_checked_cast %4 : $SubProto to $OtherProto & Concrete + %16 = unconditional_checked_cast %4 : $SubProto to OtherProto & Concrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %16 : $OtherProto & Concrete diff --git a/test/IRGen/protocol_with_superclass_where_clause.sil b/test/IRGen/protocol_with_superclass_where_clause.sil index 95ca6ecf3597f..1a158a08c7336 100644 --- a/test/IRGen/protocol_with_superclass_where_clause.sil +++ b/test/IRGen/protocol_with_superclass_where_clause.sil @@ -36,7 +36,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %5 = unconditional_checked_cast %0 : $Concrete to $ProtoRefinesClass + %5 = unconditional_checked_cast %0 : $Concrete to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %5 : $ProtoRefinesClass @@ -47,7 +47,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %6 = unconditional_checked_cast %0 : $Concrete to $SubProto + %6 = unconditional_checked_cast %0 : $Concrete to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %6 : $SubProto @@ -60,7 +60,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %7 = unconditional_checked_cast %1 : $SuperProto to $ProtoRefinesClass + %7 = unconditional_checked_cast %1 : $SuperProto to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %7 : $ProtoRefinesClass @@ -73,7 +73,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %8 = unconditional_checked_cast %1 : $SuperProto to $SubProto + %8 = unconditional_checked_cast %1 : $SuperProto to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %8 : $SubProto @@ -84,7 +84,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass17ProtoRefinesClassMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %9 = unconditional_checked_cast %2 : $SuperProto & Concrete to $ProtoRefinesClass + %9 = unconditional_checked_cast %2 : $SuperProto & Concrete to ProtoRefinesClass // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %9 : $ProtoRefinesClass @@ -95,7 +95,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass8SubProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %10 = unconditional_checked_cast %2 : $SuperProto & Concrete to $SubProto + %10 = unconditional_checked_cast %2 : $SuperProto & Concrete to SubProto // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %10 : $SubProto @@ -106,7 +106,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass7DerivedC* - %11 = unconditional_checked_cast %3 : $ProtoRefinesClass to $Derived + %11 = unconditional_checked_cast %3 : $ProtoRefinesClass to Derived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass7DerivedC*)*)(%T24protocol_with_superclass7DerivedC* [[REFERENCE]]) strong_release %11 : $Derived @@ -117,7 +117,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass10SubDerivedC* - %12 = unconditional_checked_cast %3 : $ProtoRefinesClass to $SubDerived + %12 = unconditional_checked_cast %3 : $ProtoRefinesClass to SubDerived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass10SubDerivedC*)*)(%T24protocol_with_superclass10SubDerivedC* [[REFERENCE]]) strong_release %12 : $SubDerived @@ -128,7 +128,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %13 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & Concrete + %13 = unconditional_checked_cast %3 : $ProtoRefinesClass to OtherProto & Concrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %13 : $OtherProto & Concrete @@ -141,7 +141,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], %swift.type* [[METADATA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass11SubConcreteC* - %14 = unconditional_checked_cast %3 : $ProtoRefinesClass to $OtherProto & SubConcrete + %14 = unconditional_checked_cast %3 : $ProtoRefinesClass to OtherProto & SubConcrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11SubConcreteC*)*)(%T24protocol_with_superclass11SubConcreteC* [[REFERENCE]]) strong_release %14 : $OtherProto & SubConcrete @@ -152,7 +152,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[METADATA_PTR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[OBJECT]], i8* [[METADATA_PTR]], {{.*}}) // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[RESULT]] to %T24protocol_with_superclass11MoreDerivedC* - %15 = unconditional_checked_cast %4 : $SubProto to $MoreDerived + %15 = unconditional_checked_cast %4 : $SubProto to MoreDerived // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass11MoreDerivedC*)*)(%T24protocol_with_superclass11MoreDerivedC* [[REFERENCE]]) strong_release %15 : $MoreDerived @@ -163,7 +163,7 @@ bb0(%0 : $Concrete, %1 : $SuperProto, %2 : $SuperProto & Concrete, %3 : $ProtoRe // CHECK-NEXT: [[RESULT:%.*]] = call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* [[OBJECT]], %swift.type* [[ISA]], {{.*}} @"$s24protocol_with_superclass10OtherProtoMp" // CHECK-NEXT: [[FIRST:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[REFERENCE:%.*]] = bitcast i8* [[FIRST]] to %T24protocol_with_superclass8ConcreteC* - %16 = unconditional_checked_cast %4 : $SubProto to $OtherProto & Concrete + %16 = unconditional_checked_cast %4 : $SubProto to OtherProto & Concrete // CHECK: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T24protocol_with_superclass8ConcreteC*)*)(%T24protocol_with_superclass8ConcreteC* [[REFERENCE]]) strong_release %16 : $OtherProto & Concrete diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index 78910d6790295..b4d4df28850c5 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -66,8 +66,7 @@ public func functionWithResilientTypesRectangle(_ r: Rectangle) { // CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s16resilient_struct9RectangleVMa"([[INT]] 0) // CHECK-NEXT: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i32* -// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4]] -// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 2 +// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4|6]] // CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_PTR]] // CHECK-NEXT: [[STRUCT_ADDR:%.*]] = bitcast %T16resilient_struct9RectangleV* %0 to i8* // CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], i32 [[FIELD_OFFSET]] @@ -148,8 +147,7 @@ public struct StructWithResilientStorage { // CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s17struct_resilience26StructWithResilientStorageVMa"([[INT]] 0) // CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i32* -// CHECK-NEXT: [[FIELD_OFFSET_VECTOR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4]] -// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[FIELD_OFFSET_VECTOR]], i32 2 +// CHECK-NEXT: [[FIELD_OFFSET_PTR:%.*]] = getelementptr inbounds i32, i32* [[METADATA_ADDR]], [[INT]] [[IDX:2|4|6]] // CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_PTR]] // CHECK-NEXT: [[STRUCT_ADDR:%.*]] = bitcast %T17struct_resilience26StructWithResilientStorageV* %0 to i8* // CHECK-NEXT: [[FIELD_ADDR:%.*]] = getelementptr inbounds i8, i8* [[STRUCT_ADDR]], i32 [[FIELD_OFFSET]] diff --git a/test/IRGen/subclass_existentials.sil b/test/IRGen/subclass_existentials.sil index b2f50de2f9bf2..246a4be511416 100644 --- a/test/IRGen/subclass_existentials.sil +++ b/test/IRGen/subclass_existentials.sil @@ -90,7 +90,7 @@ bb0(%0 : @owned $C, %1 : @owned $C & P): // CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %T21subclass_existentials1DC* // CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1 - %2 = unconditional_checked_cast %0 : $C to $D & R + %2 = unconditional_checked_cast %0 : $C to D & R // CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T21subclass_existentials1DC*)*)(%T21subclass_existentials1DC* [[VALUE]]) destroy_value %2 : $D & R @@ -99,7 +99,7 @@ bb0(%0 : @owned $C, %1 : @owned $C & P): // CHECK-NEXT: [[CLASS_ADDR:%.*]] = bitcast %swift.type* [[SUPERCLASS]] to i8* // CHECK-NEXT: [[RESULT:%.*]] = call i8* @swift_dynamicCastClassUnconditional(i8* [[VALUE]], i8* [[CLASS_ADDR]], {{.*}}) // CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[RESULT]] to %T21subclass_existentials1DC* - %3 = unconditional_checked_cast %1 : $C & P to $D + %3 = unconditional_checked_cast %1 : $C & P to D // CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T21subclass_existentials1DC*)*)(%T21subclass_existentials1DC* [[VALUE]]) destroy_value %3 : $D @@ -140,7 +140,7 @@ bb0(%0 : @owned $C): // CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %T21subclass_existentials1CC* // CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1 - %2 = unconditional_checked_cast %0 : $C to $C & P + %2 = unconditional_checked_cast %0 : $C to C & P // CHECK-NEXT: call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T21subclass_existentials1CC*)*)(%T21subclass_existentials1CC* [[VALUE]]) destroy_value %2 : $C & P @@ -176,10 +176,10 @@ bb0(%0 : $@thick C.Type, %1 : $@thick (C & P).Type): // CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %swift.type* // CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1 - %2 = unconditional_checked_cast %0 : $@thick C.Type to $@thick (D & R).Type + %2 = unconditional_checked_cast %0 : $@thick C.Type to @thick (D & R).Type // CHECK-NEXT: [[RESULT:%.*]] = call %swift.type* @swift_dynamicCastMetatypeUnconditional(%swift.type* %1, %swift.type* [[SUPERCLASS]], {{.*}}) - %3 = unconditional_checked_cast %1 : $@thick (C & P).Type to $@thick D.Type + %3 = unconditional_checked_cast %1 : $@thick (C & P).Type to @thick D.Type // CHECK-NEXT: ret void %result = tuple () @@ -195,7 +195,7 @@ bb0(%0 : $@thick C.Type): // CHECK-NEXT: [[VALUE_ADDR:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 0 // CHECK-NEXT: [[VALUE:%.*]] = bitcast i8* [[VALUE_ADDR]] to %swift.type* // CHECK-NEXT: [[WTABLE:%.*]] = extractvalue { i8*, i8** } [[RESULT]], 1 - %2 = unconditional_checked_cast %0 : $@thick C.Type to $@thick (C & P).Type + %2 = unconditional_checked_cast %0 : $@thick C.Type to @thick (C & P).Type // CHECK-NEXT: ret void %result = tuple () diff --git a/test/IRGen/tail_allocated_c_array.swift b/test/IRGen/tail_allocated_c_array.swift new file mode 100644 index 0000000000000..6108de7468d07 --- /dev/null +++ b/test/IRGen/tail_allocated_c_array.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -import-objc-header %S/Inputs/tail_allocated_c_array.h -primary-file %s -emit-ir -o - | %FileCheck %s + +// 25165828 = 0x1800004 The bottom bits designate the offset = 4 +// CHECK: @keypath = private global <{{.*}}i32 0, i32 -2147483644, i32 25165828 }> + +_ = MemoryLayout.offset(of: \foo.tailallocatedarray)! diff --git a/test/IRGen/unconditional_checked_cast.sil b/test/IRGen/unconditional_checked_cast.sil index 29422b251751a..eb0426f065179 100644 --- a/test/IRGen/unconditional_checked_cast.sil +++ b/test/IRGen/unconditional_checked_cast.sil @@ -25,7 +25,7 @@ sil_vtable D {} sil @downcast_test : $@convention(thin) (@inout C) -> @out D { bb0(%0 : $*D, %1 : $*C): %a = load %1 : $*C - %2 = unconditional_checked_cast %a : $C to $D + %2 = unconditional_checked_cast %a : $C to D store %2 to %0 : $*D %33 = tuple() return %33 : $() diff --git a/test/IRGen/unknown_object.sil b/test/IRGen/unknown_object.sil index b3f380d26ef26..e5e86bb1b9d7a 100644 --- a/test/IRGen/unknown_object.sil +++ b/test/IRGen/unknown_object.sil @@ -10,6 +10,9 @@ entry(%x : @guaranteed $Builtin.AnyObject): // CHECK-native: swift_retain // CHECK-objc: swift_unknownObjectRetain %y = copy_value %x : $Builtin.AnyObject + br bb1 + +bb1: // CHECK-native: swift_release // CHECK-objc: swift_unknownObjectRelease destroy_value %y : $Builtin.AnyObject diff --git a/test/IRGen/unmanaged_objc_throw_func.swift b/test/IRGen/unmanaged_objc_throw_func.swift index dcccf5174ba71..128b41b3999f7 100644 --- a/test/IRGen/unmanaged_objc_throw_func.swift +++ b/test/IRGen/unmanaged_objc_throw_func.swift @@ -18,7 +18,7 @@ import Foundation // CHECK-NEXT: store i{{32|64}} 1, i{{32|64}}* %._value, align {{[0-9]+}} // CHECK-NEXT: %[[T4:.+]] = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %[[T1]], %swift.type* @"$sSiN") // CHECK-NEXT: %[[T5:.+]] = bitcast %TSo7NSArrayC* %[[T4]] to %TSo10CFArrayRefa* - // CHECK-NEXT: call void asm sideeffect "", "r"(%TSo10CFArrayRefa* %[[T5]]) + // CHECK-NEXT: store %TSo10CFArrayRefa* %[[T5]] // CHECK-NEXT: call void @swift_bridgeObjectRelease(%swift.bridge* %[[T1]]) #{{[0-9]+}} // CHECK-NEXT: %[[T6:.+]] = bitcast %TSo10CFArrayRefa* %[[T5]] to i8* // CHECK-NEXT: call void @llvm.objc.release(i8* %[[T6]]) diff --git a/test/IRGen/weak_import_deployment_target.swift b/test/IRGen/weak_import_deployment_target.swift index adbdc9bcfcb94..cbab1d9dce2ef 100644 --- a/test/IRGen/weak_import_deployment_target.swift +++ b/test/IRGen/weak_import_deployment_target.swift @@ -30,4 +30,4 @@ public func callsNew() { // CHECK-NEW-LABEL: declare swiftcc void @"$s36weak_import_deployment_target_helper13hasDefaultArgyySiF"(i64) // CHECK-NEW-LABEL: declare swiftcc void @"$s36weak_import_deployment_target_helper22hasAvailableDefaultArgyySiF"(i64) // CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper17availableFunctionSiyF"() -// CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"() \ No newline at end of file +// CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"() diff --git a/test/IRGen/weak_import_extension.swift b/test/IRGen/weak_import_extension.swift new file mode 100644 index 0000000000000..87d2a40b9613c --- /dev/null +++ b/test/IRGen/weak_import_extension.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// +// RUN: %target-swift-frontend -enable-library-evolution -emit-module -target x86_64-apple-macosx10.60 -emit-module-path %t/weak_import_extension_helper.swiftmodule -parse-as-library %S/Inputs/weak_import_extension_helper.swift +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target x86_64-apple-macosx10.50 | %FileCheck %s --check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -primary-file %s -I %t -emit-ir -target x86_64-apple-macosx10.60 | %FileCheck %s --check-prefix=CHECK-NEW +// +// REQUIRES: OS=macosx + +import weak_import_extension_helper + +@available(macOS 10.60, *) +public func callsExtensionMethod() { + Foo().extensionMethod() +} + +// CHECK-OLD: declare extern_weak swiftcc %swift.metadata_response @"$s28weak_import_extension_helper3FooVMa" +// CHECK-OLD: declare extern_weak swiftcc void @"$s28weak_import_extension_helper3FooVACycfC" +// CHECK-OLD: declare extern_weak swiftcc void @"$s28weak_import_extension_helper3FooV0C6MethodyyF" + +// CHECK-NEW: declare swiftcc %swift.metadata_response @"$s28weak_import_extension_helper3FooVMa" +// CHECK-NEW: declare swiftcc void @"$s28weak_import_extension_helper3FooVACycfC" +// CHECK-NEW: declare swiftcc void @"$s28weak_import_extension_helper3FooV0C6MethodyyF" \ No newline at end of file diff --git a/test/Index/Store/record-sourcefile.swift b/test/Index/Store/record-sourcefile.swift index fd03fa49be245..e35cb8ba0c198 100644 --- a/test/Index/Store/record-sourcefile.swift +++ b/test/Index/Store/record-sourcefile.swift @@ -9,7 +9,7 @@ // CHECK: struct/Swift | S1 | s:4file2S1V | | Def,Ref,RelCont - // CHECK: instance-method/acc-get/Swift | getter:property | s:4file2S1V8propertySivg | | Def,Ref,Call,Impl,RelChild,RelRec,RelCall,RelAcc,RelCont - // CHECK: instance-property/Swift | property | [[property_USR:s:4file2S1V8propertySivp]] | | Def,Ref,Read,RelChild,RelCont - -// CHECK: static-method/acc-get/Swift | getter:staticProperty | s:4file2S1V14staticPropertySivg | | Def,Ref,Call,Impl,RelChild,RelRec,RelCall,RelAcc,RelCont - +// CHECK: static-method/acc-get/Swift | getter:staticProperty | s:4file2S1V14staticPropertySivgZ | | Def,Ref,Call,Impl,RelChild,RelRec,RelCall,RelAcc,RelCont - // CHECK: static-property/Swift | staticProperty | s:{{.*}} | | Def,Ref,Read,RelChild,RelCont - // CHECK: instance-property/Swift | computedPropertyGetSet | s:{{.*}} | | Def,RelChild - // CHECK: struct/Swift | Int | s:Si | | Ref - diff --git a/test/Index/index_swift_only_systemmodule.swift b/test/Index/index_swift_only_systemmodule.swift index bddec98843706..59eb09400e2ba 100644 --- a/test/Index/index_swift_only_systemmodule.swift +++ b/test/Index/index_swift_only_systemmodule.swift @@ -89,7 +89,7 @@ print(someFunc()) // RUN: %s // // --- Ensure module cache is populated. -// RUN: find %t/modulecache -maxdepth 1 -name 'SomeModule-*.swiftmodule' | grep . +// RUN: ls %t/modulecache/SomeModule-*.swiftmodule // // --- Built with indexing // RUN: %target-swift-frontend \ diff --git a/test/Index/invalid_code.swift b/test/Index/invalid_code.swift index 1ffd85bd44827..8cac081c70535 100644 --- a/test/Index/invalid_code.swift +++ b/test/Index/invalid_code.swift @@ -17,7 +17,7 @@ class CrashTest { init() { } } // CHECK: [[@LINE+1]]:13 | instance-method/Swift | returnSelf -CrashTest().returnSelf(["": 0]).something() +CrashTest().returnSelf(["": 0]).something class CrashTest2 { // CHECK: [[@LINE+1]]:8 | instance-method/Swift | bar @@ -48,4 +48,4 @@ extension Protector where T: RangeReplaceableCollection { _ = newElement } } -} \ No newline at end of file +} diff --git a/test/Index/kinds.swift b/test/Index/kinds.swift index 0b97258e85018..d7233399d8f14 100644 --- a/test/Index/kinds.swift +++ b/test/Index/kinds.swift @@ -108,14 +108,16 @@ class AClass { } // ClassProperty - class let classProperty = 1 - // CHECK: [[@LINE-1]]:13 | class-property/Swift | classProperty | s:14swift_ide_test6AClassC13classPropertySivpZ | Def,RelChild | rel: 1 + class var classProperty: Int! + // CHECK: [[@LINE-1]]:13 | class-property/Swift | classProperty | s:14swift_ide_test6AClassC13classPropertySiSgvpZ | Def,RelChild | rel: 1 // CHECK-NEXT: RelChild | class/Swift | AClass | s:14swift_ide_test6AClassC + // CHECK: [[@LINE-3]]:13 | class-method/acc-get/Swift | getter:classProperty | s:14swift_ide_test6AClassC13classPropertySiSgvgZ | Def,Dyn,Impl,RelChild,RelAcc | rel: 1 // StaticProperty - static let staticProperty = 1 - // CHECK: [[@LINE-1]]:14 | static-property/Swift | staticProperty | s:14swift_ide_test6AClassC14staticPropertySivpZ | Def,RelChild | rel: 1 + static var staticProperty: Int! + // CHECK: [[@LINE-1]]:14 | static-property/Swift | staticProperty | s:14swift_ide_test6AClassC14staticPropertySiSgvpZ | Def,RelChild | rel: 1 // CHECK-NEXT: RelChild | class/Swift | AClass | s:14swift_ide_test6AClassC + // CHECK: [[@LINE-3]]:14 | static-method/acc-get/Swift | getter:staticProperty | s:14swift_ide_test6AClassC14staticPropertySiSgvgZ | Def,Impl,RelChild,RelAcc | rel: 1 // Constructor init() {} diff --git a/test/Index/patterns.swift b/test/Index/patterns.swift index 20ccaf9bc6d52..1b210caddbe30 100644 --- a/test/Index/patterns.swift +++ b/test/Index/patterns.swift @@ -4,6 +4,8 @@ struct Foo { enum Inner { case bar } + + struct Bar {} } let a: Foo.Inner = .bar @@ -26,3 +28,19 @@ case Foo.Inner.bar: default: break } + +let prop1 = 1 + +if prop1 is Foo {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +if prop1 is Foo.Bar {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +// CHECK: [[@LINE-2]]:17 | struct/Swift | Bar | + +let prop2: Int? = 1 + +if prop2 is Foo {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +if prop2 is Foo.Bar {} +// CHECK: [[@LINE-1]]:13 | struct/Swift | Foo | +// CHECK: [[@LINE-2]]:17 | struct/Swift | Bar | diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index c055b48ebff88..4d4a7cf018575 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -205,6 +205,8 @@ typedef int (*fptr)(int); fptr getFunctionPointer(void); void useFunctionPointer(fptr); +int (*getFunctionPointer_(void))(int); + struct FunctionPointerWrapper { fptr a; fptr b; @@ -214,6 +216,14 @@ typedef void (*fptr2)(int, long, void *); fptr2 getFunctionPointer2(void); void useFunctionPointer2(fptr2); +int (*(*getHigherOrderFunctionPointer(void))(int (*)(int)))(int); + +typedef struct Dummy { + int x; +} Dummy; + +Dummy * (*getFunctionPointer3(void))(Dummy *); + //===--- // Unions //===--- diff --git a/test/Inputs/conditional_conformance_basic_conformances.swift b/test/Inputs/conditional_conformance_basic_conformances.swift index 0667489b77866..bfba78057fc8e 100644 --- a/test/Inputs/conditional_conformance_basic_conformances.swift +++ b/test/Inputs/conditional_conformance_basic_conformances.swift @@ -83,9 +83,21 @@ public func single_concrete() { // CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont // CHECK: cacheIsNull: -// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMa"(i64 255) -// CHECK-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// macosx-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMa"(i64 255) +// macosx-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// macosx-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// ios-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMa"(i64 255) +// ios-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// ios-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// tvos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMa"(i64 255) +// tvos-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// tvos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// watchos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMa"(i64 255) +// watchos-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// watchos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// linux-gnu-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMD") +// linux-android-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMD") +// windows-msvc-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMD") // CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 // CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 @@ -100,6 +112,28 @@ public func single_concrete() { // CHECK-NEXT: ret i8** [[T0]] // CHECK-NEXT: } +// TYPEBYNAME-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// TYPEBYNAME-NEXT: entry: +// TYPEBYNAME-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// TYPEBYNAME-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL", align 8 +// TYPEBYNAME-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// TYPEBYNAME-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// TYPEBYNAME: cacheIsNull: +// TYPEBYNAME-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMD") +// TYPEBYNAME-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// TYPEBYNAME-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// TYPEBYNAME-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 + +// TYPEBYNAME-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable +// TYPEBYNAME-NEXT: store atomic i8** [[Single_P1]], i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL" release, align 8 +// TYPEBYNAME-NEXT: br label %cont + +// TYPEBYNAME: cont: +// TYPEBYNAME-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Single_P1]], %cacheIsNull ] +// TYPEBYNAME-NEXT: ret i8** [[T0]] +// TYPEBYNAME-NEXT: } + public struct Double {} extension Double: P1 where B: P2, C: P3 { @@ -218,9 +252,21 @@ public func double_concrete_concrete() { // CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont // CHECK: cacheIsNull: -// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMa"(i64 255) -// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// macosx-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMa"(i64 255) +// macosx-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// macosx-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// ios-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMa"(i64 255) +// ios-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// ios-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// tvos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMa"(i64 255) +// tvos-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// tvos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// watchos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMa"(i64 255) +// watchos-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// watchos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// linux-gnu-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMD") +// linux-android-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMD") +// windows-msvc-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMD") // CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [2 x i8**], [2 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 // CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 diff --git a/test/Inputs/conditional_conformance_subclass.swift b/test/Inputs/conditional_conformance_subclass.swift index 31731f6d46a5a..b16b58599d572 100644 --- a/test/Inputs/conditional_conformance_subclass.swift +++ b/test/Inputs/conditional_conformance_subclass.swift @@ -82,9 +82,21 @@ public func subclassgeneric_concrete() { // CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont // CHECK: cacheIsNull: -// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMa"(i64 255) -// CHECK-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// macosx-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMa"(i64 255) +// macosx-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// macosx-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// ios-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMa"(i64 255) +// ios-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// ios-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// watchos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMa"(i64 255) +// watchos-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// watchos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// tvos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMa"(i64 255) +// tvos-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// tvos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// linux-gnu-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// linux-android-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// windows-msvc-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") // CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 // CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 diff --git a/test/Inputs/conditional_conformance_with_assoc.swift b/test/Inputs/conditional_conformance_with_assoc.swift index cb5749883d7ef..5f5992b715376 100644 --- a/test/Inputs/conditional_conformance_with_assoc.swift +++ b/test/Inputs/conditional_conformance_with_assoc.swift @@ -199,9 +199,21 @@ public func concrete_concrete() { // CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont // CHECK: cacheIsNull: -// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMa"(i64 255) -// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 -// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// macosx-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMa"(i64 255) +// macosx-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// macosx-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// ios-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMa"(i64 255) +// ios-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// ios-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// watchos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMa"(i64 255) +// watchos-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// watchos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// tvos-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMa"(i64 255) +// tvos-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// tvos-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// linux-gnu-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMD") +// linux-android-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMD") +// windows-msvc-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMD") // CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 // CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 // CHECK-NEXT: store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"$s34conditional_conformance_with_assoc4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[C_P3_PTR]], align 8 diff --git a/test/Inputs/process_fine_grained_swiftdeps.sh b/test/Inputs/process_fine_grained_swiftdeps.sh new file mode 100755 index 0000000000000..9d3426ccbd047 --- /dev/null +++ b/test/Inputs/process_fine_grained_swiftdeps.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +# Fine-grained swiftdeps files use multiple lines for each graph node. +# Compress such a file so that each entry is one line of the form: +# +# Also sort for consistency, since the node order can vary. + +awk '/kind:/ {k = $2}; /aspect:/ {a = $2}; /context:/ {c = $2}; /name/ {n = $2}; /sequenceNumber/ {s = $2}; /isProvides:/ {print k, a, c, n, $2}' | sort diff --git a/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh new file mode 100755 index 0000000000000..80bb392c22cfb --- /dev/null +++ b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +# Fine-grained swiftdeps files use multiple lines for each graph node. +# Compress such a file so that each entry is one line of the form: +# +# Also sort for consistency, since the node order can vary. + +awk '/kind:/ {k = $2; f = ""}; /aspect:/ {a = $2}; /context:/ {c = $2}; /name/ {n = $2}; /sequenceNumber/ {s = $2}; /fingerprint:/ {f = $2 }; /isProvides:/ {isP = $2; print k, a, c, n, isP, f}' | sort diff --git a/test/InterfaceHash/added_method-type-fingerprints.swift b/test/InterfaceHash/added_method-type-fingerprints.swift new file mode 100644 index 0000000000000..7b51e1f67b758 --- /dev/null +++ b/test/InterfaceHash/added_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_method.swift b/test/InterfaceHash/added_method.swift index bbc1dc76bc546..28dd75f2d4329 100644 --- a/test/InterfaceHash/added_method.swift +++ b/test/InterfaceHash/added_method.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..cf3a104cc71b1 --- /dev/null +++ b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_private_property.swift b/test/InterfaceHash/added_private_class_private_property.swift index cd3f9d6c35632..e3ca285b2cb10 100644 --- a/test/InterfaceHash/added_private_class_private_property.swift +++ b/test/InterfaceHash/added_private_class_private_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift new file mode 100644 index 0000000000000..f4e01619c1b76 --- /dev/null +++ b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property.swift b/test/InterfaceHash/added_private_class_property.swift index 671af8bb7de22..cdeb03d8944a7 100644 --- a/test/InterfaceHash/added_private_class_property.swift +++ b/test/InterfaceHash/added_private_class_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..9ad0b28d56449 --- /dev/null +++ b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + + +// BEGIN a.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } + + var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property.swift b/test/InterfaceHash/added_private_enum_private_property.swift index f446cceb08905..a35d20b2928d0 100644 --- a/test/InterfaceHash/added_private_enum_private_property.swift +++ b/test/InterfaceHash/added_private_enum_private_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c8220f6f9a54b --- /dev/null +++ b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } + + private var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property.swift b/test/InterfaceHash/added_private_enum_property.swift index 5062347d9c996..370272d91d251 100644 --- a/test/InterfaceHash/added_private_enum_property.swift +++ b/test/InterfaceHash/added_private_enum_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_method-type-fingerprints.swift b/test/InterfaceHash/added_private_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0c60206d9c92 --- /dev/null +++ b/test/InterfaceHash/added_private_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method.swift b/test/InterfaceHash/added_private_method.swift index af141bae655dc..a07bed690cf16 100644 --- a/test/InterfaceHash/added_private_method.swift +++ b/test/InterfaceHash/added_private_method.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift new file mode 100644 index 0000000000000..42fee00e31bf0 --- /dev/null +++ b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift @@ -0,0 +1,88 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct A { + func f2() -> Int { + return 0 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +struct A { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types.swift b/test/InterfaceHash/added_private_method_value_types.swift index ea1adbbbed601..29ff2314d0c1e 100644 --- a/test/InterfaceHash/added_private_method_value_types.swift +++ b/test/InterfaceHash/added_private_method_value_types.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0ccd54a11771 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + func f3() -> Int + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method.swift b/test/InterfaceHash/added_private_protocol_method.swift index da302b0713b31..965c82b67af79 100644 --- a/test/InterfaceHash/added_private_protocol_method.swift +++ b/test/InterfaceHash/added_private_protocol_method.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c27cee3752b83 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + var x: Int { get set } + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property.swift b/test/InterfaceHash/added_private_protocol_property.swift index d80761f2e8de3..f47353d2d6521 100644 --- a/test/InterfaceHash/added_private_protocol_property.swift +++ b/test/InterfaceHash/added_private_protocol_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..47e371aff0901 --- /dev/null +++ b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +struct S { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property.swift b/test/InterfaceHash/added_private_struct_private_property.swift index 82dab2bea3459..7bba778652d58 100644 --- a/test/InterfaceHash/added_private_struct_private_property.swift +++ b/test/InterfaceHash/added_private_struct_private_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift new file mode 100644 index 0000000000000..bd92b5dfbe37f --- /dev/null +++ b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +private struct S { + func f2() -> Int { + return 0 + } + + var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property.swift b/test/InterfaceHash/added_private_struct_property.swift index b30eddbb9d73c..49e58b356b684 100644 --- a/test/InterfaceHash/added_private_struct_property.swift +++ b/test/InterfaceHash/added_private_struct_property.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: not cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/edited_method_body-type-fingerprints.swift b/test/InterfaceHash/edited_method_body-type-fingerprints.swift new file mode 100644 index 0000000000000..10ae3e000c09f --- /dev/null +++ b/test/InterfaceHash/edited_method_body-type-fingerprints.swift @@ -0,0 +1,31 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 1 + } +} diff --git a/test/InterfaceHash/edited_method_body.swift b/test/InterfaceHash/edited_method_body.swift index 2f87dd9f02fef..b0aebce44e500 100644 --- a/test/InterfaceHash/edited_method_body.swift +++ b/test/InterfaceHash/edited_method_body.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift new file mode 100644 index 0000000000000..0a0b5fdaaff54 --- /dev/null +++ b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift @@ -0,0 +1,33 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + var p: Int { + return 0 + } +} + +// BEGIN b.swift +class C { + var p: Int { + let x = 1 + return x + } +} + diff --git a/test/InterfaceHash/edited_property_getter.swift b/test/InterfaceHash/edited_property_getter.swift index 42584e33f6467..deaeb8ccc08dc 100644 --- a/test/InterfaceHash/edited_property_getter.swift +++ b/test/InterfaceHash/edited_property_getter.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/Interpreter/ForeignMetadata.swift b/test/Interpreter/ForeignMetadata.swift new file mode 100644 index 0000000000000..e42eb59e6ed08 --- /dev/null +++ b/test/Interpreter/ForeignMetadata.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift +// RUN: %target-build-swift -o %t/main %S/Inputs/ForeignTypeMetadata1.swift %S/Inputs/ForeignTypeMetadata2.swift %t/main.swift -swift-version 5 +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main + + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +useType() diff --git a/test/Interpreter/Inputs/ForeignTypeMetadata1.swift b/test/Interpreter/Inputs/ForeignTypeMetadata1.swift new file mode 100644 index 0000000000000..5bdd0f08b3dc5 --- /dev/null +++ b/test/Interpreter/Inputs/ForeignTypeMetadata1.swift @@ -0,0 +1,12 @@ +import Foundation + +func use(_ closure: @escaping (Int) -> ()) {} + +public func captureRange(_ r: NSRange?) { + var l = r + use { + if $0 == 0 { + l = NSRange() + } + } +} diff --git a/test/Interpreter/Inputs/ForeignTypeMetadata2.swift b/test/Interpreter/Inputs/ForeignTypeMetadata2.swift new file mode 100644 index 0000000000000..386639931ac45 --- /dev/null +++ b/test/Interpreter/Inputs/ForeignTypeMetadata2.swift @@ -0,0 +1,7 @@ +import Foundation + +public func useType() { + var x = [NSRange]() + x.append(NSRange()) + print(x) +} diff --git a/test/Interpreter/Inputs/dynamic_replacement_protocol_self_orig.swift b/test/Interpreter/Inputs/dynamic_replacement_protocol_self_orig.swift new file mode 100644 index 0000000000000..9dae9fede38c5 --- /dev/null +++ b/test/Interpreter/Inputs/dynamic_replacement_protocol_self_orig.swift @@ -0,0 +1,56 @@ +protocol View { + associatedtype Body: View + + var body: Self.Body { get } +} + +extension Never: View { + typealias Body = Never + + var body: Self.Body { fatalError() } +} + +struct AnyView: View { + var body: Never { fatalError() } +} + +protocol ViewModelRenderable { + var view: AnyView { get } +} + +extension ViewModelRenderable where Self: SectionModel { + static func view(for model: Model, ofType: MyView.Type) -> AnyView where Model == MyView.BodyViewModel { + fatalError() + } +} + +protocol SectionViewModelView where Self: View { + associatedtype BodyViewModel: SectionModel + + init(bodyViewModel: BodyViewModel) +} + +public protocol SectionModel: Codable { + var sectionName: String { get } +} + +extension SectionModel { + public var sectionName: String { + "Hello world!" + } +} + +struct NewUserModel: SectionModel { +} + +extension NewUserModel: ViewModelRenderable { + var view: AnyView { Self.view(for: self, ofType: NewUserView.self) } +} + +struct NewUserView: SectionViewModelView { + var bodyViewModel: NewUserModel = .init() + + var body: Never { + fatalError() + } +} diff --git a/test/Interpreter/SDK/Inputs/ObjCWeak/ObjCWeak.m b/test/Interpreter/SDK/Inputs/ObjCWeak/ObjCWeak.m index adcae83569c05..54294ad0a4281 100644 --- a/test/Interpreter/SDK/Inputs/ObjCWeak/ObjCWeak.m +++ b/test/Interpreter/SDK/Inputs/ObjCWeak/ObjCWeak.m @@ -1,16 +1,21 @@ #include "ObjCWeak.h" +#include + +extern id _Nullable +objc_initWeak(id _Nullable * _Nonnull location, id _Nullable val); void tryWeakReferencing(id (^makeThing)(void)) { id thingy; @autoreleasepool { - thingy = makeThing(); + thingy = [makeThing() retain]; } - - __weak id weakThingy = thingy; - + + id weakThingy = nil; + objc_initWeak(&weakThingy, thingy); + @autoreleasepool { fputs("before giving up strong reference:\n", stderr); - id x = weakThingy; + id x = objc_loadWeak(&weakThingy); if (x) { fputs([[x description] UTF8String], stderr); fputs("\n", stderr); @@ -18,12 +23,35 @@ void tryWeakReferencing(id (^makeThing)(void)) { fputs("Gone\n", stderr); } } - + + [thingy release]; thingy = nil; + for (int i = 0; i < 100; i++) { + @autoreleasepool { + id tmp = makeThing(); + id weakTmp = nil; + objc_initWeak(&weakTmp, tmp); + objc_loadWeak(&weakTmp); + objc_storeWeak(&weakTmp, nil); + } + } + @autoreleasepool { fputs("after giving up strong reference:\n", stderr); - id x = weakThingy; + id x = objc_loadWeak(&weakThingy); + if (x) { + fputs([[x description] UTF8String], stderr); + fputs("\n", stderr); + } else { + fputs("Gone\n", stderr); + } + } + objc_storeWeak(&weakThingy, nil); + + @autoreleasepool { + fputs("after giving up weak reference:\n", stderr); + id x = objc_loadWeak(&weakThingy); if (x) { fputs([[x description] UTF8String], stderr); fputs("\n", stderr); diff --git a/test/Interpreter/SDK/Inputs/objc_block_consumed.h b/test/Interpreter/SDK/Inputs/objc_block_consumed.h new file mode 100644 index 0000000000000..b6a907dcfc324 --- /dev/null +++ b/test/Interpreter/SDK/Inputs/objc_block_consumed.h @@ -0,0 +1,5 @@ +#import + +static inline void takesBlockWithConsumedArg(void (^ block)(NS_RELEASES_ARGUMENT NSObject *x), NSObject *x) { + block(x); +} diff --git a/test/Interpreter/SDK/misc_osx.swift b/test/Interpreter/SDK/misc_osx.swift index 636866eb6658e..15383cf784077 100644 --- a/test/Interpreter/SDK/misc_osx.swift +++ b/test/Interpreter/SDK/misc_osx.swift @@ -7,12 +7,11 @@ import CoreServices func testFSEventStreamRef(stream: FSEventStreamRef) { // FIXME: These should be distinct types, constructible from one another. _ = stream as ConstFSEventStreamRef // works by coincidence because both are currently OpaquePointer - _ = ConstFSEventStreamRef(stream) // expected-error {{cannot invoke initializer for type 'ConstFSEventStreamRef' with an argument list of type '(FSEventStreamRef)'}} - // expected-note @-1 {{overloads for 'ConstFSEventStreamRef' exist with these partially matching parameter lists:}} + _ = ConstFSEventStreamRef(stream) // expected-error {{no exact matches in call to initializer}} // This is not a CF object. FSEventStreamRetain(stream) // no-warning FSEventStreamRelease(stream) - let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') does not conform to specified type 'AnyObject'}} + let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') expected to be instance of class or class-constrained type}} } diff --git a/test/Interpreter/SDK/objc_block_consumed.swift b/test/Interpreter/SDK/objc_block_consumed.swift new file mode 100644 index 0000000000000..bee0890a639d6 --- /dev/null +++ b/test/Interpreter/SDK/objc_block_consumed.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -import-objc-header %S/Inputs/objc_block_consumed.h -o %t/main +// RUN: %target-run %t/main + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation +import StdlibUnittest + +class C : NSObject { + var tracked = LifetimeTracked(0) +} + +var ObjCBlockConsumedTestSuite = TestSuite("ObjCBlockConsumed") + +ObjCBlockConsumedTestSuite.test("Test") { + takesBlockWithConsumedArg({ arg in }, C()) +} + +runAllTests() diff --git a/test/Interpreter/SDK/objc_mangling.swift b/test/Interpreter/SDK/objc_mangling.swift index e06aca4123380..887263faf75e4 100644 --- a/test/Interpreter/SDK/objc_mangling.swift +++ b/test/Interpreter/SDK/objc_mangling.swift @@ -6,6 +6,10 @@ // REQUIRES: objc_interop +// rdar://problem/56959761 +// UNSUPPORTED: OS=watchos +// UNSUPPORTED: OS=tvos + import Foundation /* FIXME: SwiftObject doesn't support -description diff --git a/test/Interpreter/SDK/weak_objc_interop.swift b/test/Interpreter/SDK/weak_objc_interop.swift index cc85d7ac15f9d..cc567473f2339 100644 --- a/test/Interpreter/SDK/weak_objc_interop.swift +++ b/test/Interpreter/SDK/weak_objc_interop.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // // RUN: cp %s %t/main.swift -// RUN: %target-clang -fobjc-arc %S/Inputs/ObjCWeak/ObjCWeak.m -c -o %t/ObjCWeak.o +// RUN: %target-clang -fno-objc-arc %S/Inputs/ObjCWeak/ObjCWeak.m -c -o %t/ObjCWeak.o // RUN: %target-build-swift %t/main.swift -I %S/Inputs/ObjCWeak/ -Xlinker %t/ObjCWeak.o -o %t/weak_objc_interop -Xfrontend -disable-access-control // RUN: %target-codesign %t/weak_objc_interop // RUN: %target-run %t/weak_objc_interop 2>&1 | %FileCheck %s diff --git a/test/Interpreter/bridged_casts_folding.swift b/test/Interpreter/bridged_casts_folding.swift index 68ece9d2a8158..31b5de2d61b3f 100644 --- a/test/Interpreter/bridged_casts_folding.swift +++ b/test/Interpreter/bridged_casts_folding.swift @@ -5,6 +5,9 @@ // the correct reason in the test. We want to separate a memory management error // from a cast error which prints a nice error message. +// FIXME: we should run this test if the OS-provided stdlib is recent enough. +// UNSUPPORTED: use_os_stdlib + // REQUIRES: executable_test // REQUIRES: objc_interop diff --git a/test/Interpreter/casts.swift b/test/Interpreter/casts.swift index c17750e08c78c..5862b89866de4 100644 --- a/test/Interpreter/casts.swift +++ b/test/Interpreter/casts.swift @@ -122,3 +122,23 @@ Casts.test("testConditionalBridgedCastFromSwiftToNSObjectDerivedClass") { } expectEqual(0, LifetimeTracked.instances) } + +protocol Q {} +class K { } +class D: Q {} +typealias AnyQ = Q & AnyObject +typealias KQ = K & Q + +Casts.test("testCastProtocolCompoWithAnyObjectToProtocolCompoTypeSuperclass") { + let shouldBeNil = (D() as AnyQ) as? KQ + expectNil(shouldBeNil) +} + +protocol QAny: AnyObject {} +typealias KQAny = K & QAny +class F: QAny {} + +Casts.test("testCastProtocolWithAnyObjectToProtocolCompoTypeSuperclass") { + let shouldBeNil = (F() as QAny) as? KQAny + expectNil(shouldBeNil) +} diff --git a/test/Interpreter/dynamic_replacement_dlclose.swift b/test/Interpreter/dynamic_replacement_dlclose.swift deleted file mode 100644 index 4c9339b9cb414..0000000000000 --- a/test/Interpreter/dynamic_replacement_dlclose.swift +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift-dylib(%t/%target-library-name(Module1)) -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_dlclose.swift -Xfrontend -enable-private-imports -// RUN: %target-build-swift-dylib(%t/%target-library-name(Module2)) -I%t -L%t -lModule1 %target-rpath(%t) -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_dlclose2.swift -// RUN: %target-build-swift -I%t -L%t -lModule1 -DMAIN -o %t/main %target-rpath(%t) %s -swift-version 5 -// RUN: %target-codesign %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2) -// RUN: %target-run %t/main %t/%target-library-name(Module1) %t/%target-library-name(Module2) - -// REQUIRES: executable_test - -import Module1 - -import StdlibUnittest - -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - import Darwin -#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku) - import Glibc -#elseif os(Windows) - import MSVCRT - import WinSDK -#else -#error("Unsupported platform") -#endif - -var DynamicallyReplaceable = TestSuite("DynamicallyReplaceable") - - - -private func target_library_name(_ name: String) -> String { -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) - return "lib\(name).dylib" -#elseif os(Windows) - return "\(name).dll" -#else - return "lib\(name).so" -#endif -} - - -DynamicallyReplaceable.test("DynamicallyReplaceable") { - var executablePath = CommandLine.arguments[0] - executablePath.removeLast(4) - expectEqual(1, test()) - // Now, test with the module containing the replacements. - -#if os(Linux) - let h = dlopen(target_library_name("Module2"), RTLD_NOW) -#elseif os(Windows) - let h = LoadLibraryA(target_library_name("Module2")) -#else - let h = dlopen(executablePath+target_library_name("Module2"), RTLD_NOW) -#endif - - expectEqual(2, test()) - -#if os(Linux) -#elseif os(Windows) -#else - dlclose(h) -#endif - -#if os(Linux) - _ = dlopen(target_library_name("Module2"), RTLD_NOW) -#elseif os(Windows) -#else - _ = dlopen(executablePath+target_library_name("Module2"), RTLD_NOW) -#endif - expectEqual(2, test()) - -} - -runAllTests() diff --git a/test/Interpreter/dynamic_replacement_protocol_self.swift b/test/Interpreter/dynamic_replacement_protocol_self.swift new file mode 100644 index 0000000000000..b453f50eb13df --- /dev/null +++ b/test/Interpreter/dynamic_replacement_protocol_self.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(TestModuleLinking)) -module-name TestModuleLinking -emit-module -emit-module-path %t/TestModuleLinking.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_protocol_self_orig.swift -Xfrontend -enable-private-imports -Xfrontend -enable-implicit-dynamic +// RUN: %target-build-swift -I%t -L%t -lTestModuleLinking -o %t/main %target-rpath(%t) %s -swift-version 5 +// RUN: %target-codesign %t/main %t/%target-library-name(TestModuleLinking) +// RUN: %target-run %t/main %t/%target-library-name(TestModuleLinking) %t/%target-library-name(TestModuleLinking) + +// N.B. We're not actually executing anything here - all we care about is +// if the linker is content. + +// REQUIRES: executable_test + +// UNSUPPORTED: swift_test_mode_optimize_none_with_implicit_dynamic + +@_private(sourceFile: "dynamic_replacement_protocol_self_orig.swift") import TestModuleLinking + +extension NewUserModel { + @_dynamicReplacement(for: view) private var __preview__view: AnyView { + Self.view(for: self, ofType: NewUserView.self) + } +} + +typealias NewUserModel = TestModuleLinking.NewUserModel diff --git a/test/Interpreter/generic_casts.swift b/test/Interpreter/generic_casts.swift index bf87feb633bfb..b7ca3866a47c9 100644 --- a/test/Interpreter/generic_casts.swift +++ b/test/Interpreter/generic_casts.swift @@ -1,4 +1,6 @@ -// RUN: %target-run-simple-swift | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Onone %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck --check-prefix CHECK --check-prefix CHECK-ONONE %s // RUN: %target-build-swift -O %s -o %t/a.out.optimized // RUN: %target-codesign %t/a.out.optimized // RUN: %target-run %t/a.out.optimized | %FileCheck %s @@ -131,6 +133,64 @@ anyClassToCOrE(C()).print() // CHECK: C! anyClassToCOrE(D()).print() // CHECK: D! anyClassToCOrE(X()).print() // CHECK: E! +protocol P {} +struct PS: P {} +enum PE: P {} +class PC: P {} +class PCSub: PC {} + +func nongenericAnyIsP(type: Any.Type) -> Bool { + return type is P.Type +} +func nongenericAnyIsPAndAnyObject(type: Any.Type) -> Bool { + return type is (P & AnyObject).Type +} +func nongenericAnyIsPAndPCSub(type: Any.Type) -> Bool { + return type is (P & PCSub).Type +} +func genericAnyIs(type: Any.Type, to: T.Type, expected: Bool) -> Bool { + // If we're testing against a runtime that doesn't have the fix this tests, + // just pretend we got it right. + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + return type is T.Type + } else { + return expected + } +} +// CHECK-LABEL: casting types to protocols with generics: +print("casting types to protocols with generics:") +print(nongenericAnyIsP(type: PS.self)) // CHECK: true +print(genericAnyIs(type: PS.self, to: P.self, expected: true)) // CHECK-ONONE: true +print(nongenericAnyIsP(type: PE.self)) // CHECK: true +print(genericAnyIs(type: PE.self, to: P.self, expected: true)) // CHECK-ONONE: true +print(nongenericAnyIsP(type: PC.self)) // CHECK: true +print(genericAnyIs(type: PC.self, to: P.self, expected: true)) // CHECK-ONONE: true +print(nongenericAnyIsP(type: PCSub.self)) // CHECK: true +print(genericAnyIs(type: PCSub.self, to: P.self, expected: true)) // CHECK-ONONE: true + +// CHECK-LABEL: casting types to protocol & AnyObject existentials: +print("casting types to protocol & AnyObject existentials:") +print(nongenericAnyIsPAndAnyObject(type: PS.self)) // CHECK: false +print(genericAnyIs(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: false +print(nongenericAnyIsPAndAnyObject(type: PE.self)) // CHECK: false +print(genericAnyIs(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: false +print(nongenericAnyIsPAndAnyObject(type: PC.self)) // CHECK: true +print(genericAnyIs(type: PC.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true +print(nongenericAnyIsPAndAnyObject(type: PCSub.self)) // CHECK: true +print(genericAnyIs(type: PCSub.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true + +// CHECK-LABEL: casting types to protocol & class existentials: +print("casting types to protocol & class existentials:") +print(nongenericAnyIsPAndPCSub(type: PS.self)) // CHECK: false +print(genericAnyIs(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: false +print(nongenericAnyIsPAndPCSub(type: PE.self)) // CHECK: false +print(genericAnyIs(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: false +//print(nongenericAnyIsPAndPCSub(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed +print(genericAnyIs(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: false +print(nongenericAnyIsPAndPCSub(type: PCSub.self)) // CHECK: true +print(genericAnyIs(type: PCSub.self, to: (P & PCSub).self, expected: true)) // CHECK-ONONE: true + + // CHECK-LABEL: type comparisons: print("type comparisons:\n") print(allMetasToAllMetas(Int.self, Int.self)) // CHECK: true diff --git a/test/Interpreter/generic_casts_objc.swift b/test/Interpreter/generic_casts_objc.swift new file mode 100644 index 0000000000000..61b934fd57870 --- /dev/null +++ b/test/Interpreter/generic_casts_objc.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Onone %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck --check-prefix CHECK --check-prefix CHECK-ONONE %s +// RUN: %target-build-swift -O %s -o %t/a.out.optimized +// RUN: %target-codesign %t/a.out.optimized +// RUN: %target-run %t/a.out.optimized | %FileCheck %s +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +protocol P {} +@objc protocol PObjC {} +struct PS: P {} +enum PE: P {} +class PC: P, PObjC {} +class PCSub: PC {} + +func nongenericAnyIsPObjC(type: Any.Type) -> Bool { + return type is PObjC.Type +} +func genericAnyIs(type: Any.Type, to: T.Type, expected: Bool) -> Bool { + // If we're testing against a runtime that doesn't have the fix this tests, + // just pretend we got it right. + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + return type is T.Type + } else { + return expected + } +} + +// CHECK-LABEL: casting types to ObjC protocols with generics: +print("casting types to ObjC protocols with generics:") +print(nongenericAnyIsPObjC(type: PS.self)) // CHECK: false +print(genericAnyIs(type: PS.self, to: PObjC.self, expected: false)) // CHECK: false +print(nongenericAnyIsPObjC(type: PE.self)) // CHECK: false +print(genericAnyIs(type: PE.self, to: PObjC.self, expected: false)) // CHECK: false +print(nongenericAnyIsPObjC(type: PC.self)) // CHECK: true +print(genericAnyIs(type: PC.self, to: PObjC.self, expected: true)) // CHECK-ONONE: true +print(nongenericAnyIsPObjC(type: PCSub.self)) // CHECK: true +print(genericAnyIs(type: PCSub.self, to: PObjC.self, expected: true)) // CHECK-ONONE: true diff --git a/test/Interpreter/properties.swift b/test/Interpreter/properties.swift index 3766908e4850b..8d960f0ecc653 100644 --- a/test/Interpreter/properties.swift +++ b/test/Interpreter/properties.swift @@ -194,6 +194,30 @@ print("done rdar://16805609") // CHECK-NEXT: done rdar://16805609 +protocol rdar38514252_ProtocolWithArray { + var arrayOfInt: [Int] { get set } +} + +var rdar38514252_flag = false +var rdar38514252_questionSet: rdar38514252_ProtocolWithArray? { + didSet { + rdar38514252_flag = true + } +} + +func rdar38514252_fiddle() { + let ignored = rdar38514252_questionSet?.arrayOfInt[0] + if rdar38514252_flag || ignored != nil { + print("Failed. didSet was called on read.") + } else { + print("Awesomesauce.") + } +} +print("testing rdar://38514252") // CHECK: testing rdar://38514252 +rdar38514252_fiddle() // CHECK-NEXT: Awesomesauce. +print("done rdar://38514252") // CHECK-NEXT: done rdar://38514252 + + // rdar://17192398 - Lazy optional types always nil class r17192398Failure { diff --git a/test/LinkerSections/Inputs/FunctionSections.swift b/test/LinkerSections/Inputs/FunctionSections.swift new file mode 100644 index 0000000000000..7ca4392d429e7 --- /dev/null +++ b/test/LinkerSections/Inputs/FunctionSections.swift @@ -0,0 +1,5 @@ +public typealias Void = () + +public func func1() -> Void {} + +public func func2() -> Void {} diff --git a/test/LinkerSections/Inputs/FunctionSectionsUse.swift b/test/LinkerSections/Inputs/FunctionSectionsUse.swift new file mode 100644 index 0000000000000..af277d3cc86a3 --- /dev/null +++ b/test/LinkerSections/Inputs/FunctionSectionsUse.swift @@ -0,0 +1,3 @@ +import FunctionSections + +func1() diff --git a/test/LinkerSections/function_sections.swift b/test/LinkerSections/function_sections.swift new file mode 100644 index 0000000000000..eb01d59563ea7 --- /dev/null +++ b/test/LinkerSections/function_sections.swift @@ -0,0 +1,10 @@ +// REQUIRES: OS=linux-gnu || OS=linux-androideabi || OS=linux-android || OS=freebsd +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -function-sections -emit-module -emit-library -static -parse-stdlib %S/Inputs/FunctionSections.swift +// RUN: %target-build-swift -Xlinker --gc-sections -Xlinker -Map=%t/../../FunctionSections.map -I%t/../.. -L%t/../.. -lFunctionSections %S/Inputs/FunctionSectionsUse.swift +// RUN: %FileCheck %s < %t/../../FunctionSections.map + +// CHECK: Discarded input sections +// CHECK: .text.$s16FunctionSections5func2yyF +// CHECK: Memory map +// CHECK: .text.$s16FunctionSections5func1yyF diff --git a/test/LinkerSections/function_sections_ir_check.swift b/test/LinkerSections/function_sections_ir_check.swift new file mode 100644 index 0000000000000..fd09e02ec3a4a --- /dev/null +++ b/test/LinkerSections/function_sections_ir_check.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -function-sections -emit-library -emit-ir -static -parse-stdlib %S/Inputs/FunctionSections.swift | %FileCheck %s + +// CHECK: define {{(dllexport |protected )?}}swiftcc void @"$s16FunctionSections5func1yyF"() #0 { +// CHECK: entry: +// CHECK: ret void +// CHECK: } + +// CHECK: define {{(dllexport |protected )?}}swiftcc void @"$s16FunctionSections5func2yyF"() #0 { +// CHECK: entry: +// CHECK: ret void +// CHECK: } diff --git a/test/Migrator/double_fixit_ok.swift b/test/Migrator/double_fixit_ok.swift index 8ad52d8feaabb..6e1ad25dca94b 100644 --- a/test/Migrator/double_fixit_ok.swift +++ b/test/Migrator/double_fixit_ok.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: not %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/double_fixit_ok.result -swift-version 4 -// RUN: diff -u %s.expected %t/double_fixit_ok.result +// RUN: diff --strip-trailing-cr -u %s.expected %t/double_fixit_ok.result // RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 @available(swift, obsoleted: 4, renamed: "Thing.constant___renamed") diff --git a/test/Migrator/double_fixit_ok.swift.expected b/test/Migrator/double_fixit_ok.swift.expected index 047444838b7fe..db8c7e487be49 100644 --- a/test/Migrator/double_fixit_ok.swift.expected +++ b/test/Migrator/double_fixit_ok.swift.expected @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: not %target-swift-frontend -typecheck -update-code -primary-file %s -emit-migrated-file-path %t/double_fixit_ok.result -swift-version 4 -// RUN: diff -u %s.expected %t/double_fixit_ok.result +// RUN: diff --strip-trailing-cr -u %s.expected %t/double_fixit_ok.result // RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 @available(swift, obsoleted: 4, renamed: "Thing.constant___renamed") diff --git a/test/Migrator/insert_replace_fixit.swift b/test/Migrator/insert_replace_fixit.swift index 7a3fbc010217d..4bd68d03e9b18 100644 --- a/test/Migrator/insert_replace_fixit.swift +++ b/test/Migrator/insert_replace_fixit.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 -// RUN: diff -u %s.expected %t/result.swift +// RUN: diff --strip-trailing-cr -u %s.expected %t/result.swift import TestMyTime diff --git a/test/Migrator/insert_replace_fixit.swift.expected b/test/Migrator/insert_replace_fixit.swift.expected index 30454d4b16ea2..ba445e62016fa 100644 --- a/test/Migrator/insert_replace_fixit.swift.expected +++ b/test/Migrator/insert_replace_fixit.swift.expected @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -typecheck -update-code -primary-file %s -F %S/mock-sdk -emit-migrated-file-path %t/result.swift -swift-version 4 -// RUN: diff -u %s.expected %t/result.swift +// RUN: diff --strip-trailing-cr -u %s.expected %t/result.swift import TestMyTime diff --git a/test/Migrator/no_extraneous_argument_labels.swift b/test/Migrator/no_extraneous_argument_labels.swift index 426e223b9082c..fc8502f1f29ad 100644 --- a/test/Migrator/no_extraneous_argument_labels.swift +++ b/test/Migrator/no_extraneous_argument_labels.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -typecheck %s -swift-version 4 // RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null -// RUN: diff -u %s.expected %t/no_extraneous_argument_labels.result +// RUN: diff --strip-trailing-cr -u %s.expected %t/no_extraneous_argument_labels.result // RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 func foo(_ oc: [String]) { diff --git a/test/Migrator/no_extraneous_argument_labels.swift.expected b/test/Migrator/no_extraneous_argument_labels.swift.expected index 426e223b9082c..fc8502f1f29ad 100644 --- a/test/Migrator/no_extraneous_argument_labels.swift.expected +++ b/test/Migrator/no_extraneous_argument_labels.swift.expected @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -typecheck %s -swift-version 4 // RUN: %empty-directory(%t) && %target-swift-frontend -c -primary-file %s -emit-migrated-file-path %t/no_extraneous_argument_labels.result -swift-version 4 -o /dev/null -// RUN: diff -u %s.expected %t/no_extraneous_argument_labels.result +// RUN: diff --strip-trailing-cr -u %s.expected %t/no_extraneous_argument_labels.result // RUN: %target-swift-frontend -typecheck %s.expected -swift-version 5 func foo(_ oc: [String]) { diff --git a/test/Migrator/no_var_to_let.swift b/test/Migrator/no_var_to_let.swift index c51657fcbc30b..1aef72f9271bc 100644 --- a/test/Migrator/no_var_to_let.swift +++ b/test/Migrator/no_var_to_let.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-frontend -typecheck %s -swift-version 4 // RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null // RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/no_var_to_let.swift.result -swift-version 4 -o /dev/null -// RUN: diff -u %s %t/no_var_to_let.swift.result +// RUN: diff --strip-trailing-cr -u %s %t/no_var_to_let.swift.result // RUN: %target-swift-frontend -typecheck %s -swift-version 5 // Note that the diff run line indicates that there should be no change. diff --git a/test/Migrator/null_migration.swift b/test/Migrator/null_migration.swift index d283fb380abcc..2c3e47e2a207c 100644 --- a/test/Migrator/null_migration.swift +++ b/test/Migrator/null_migration.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/migrated_null_migration.swift -emit-remap-file-path %t/null_migration.remap -o /dev/null -// RUN: diff -u %s %t/migrated_null_migration.swift +// RUN: diff --strip-trailing-cr -u %s %t/migrated_null_migration.swift // This file tests that, if all migration passes are no-op, // there are no changes to the file. diff --git a/test/Migrator/optional_try_migration.swift b/test/Migrator/optional_try_migration.swift index e3c2055190a0f..8e0a32ca64548 100644 --- a/test/Migrator/optional_try_migration.swift +++ b/test/Migrator/optional_try_migration.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift -// RUN: diff -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift +// RUN: diff --strip-trailing-cr -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift func fetchOptInt() throws -> Int? { return 3 @@ -109,4 +109,4 @@ func testCaseMatching() { if case let x? = try? fetchInt() { print(x) } -} \ No newline at end of file +} diff --git a/test/Migrator/optional_try_migration.swift.expected b/test/Migrator/optional_try_migration.swift.expected index 1246326270b3f..750991b624349 100644 --- a/test/Migrator/optional_try_migration.swift.expected +++ b/test/Migrator/optional_try_migration.swift.expected @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -c -swift-version 4 -primary-file %s -emit-migrated-file-path %t/optional_try_migration.result.swift -// RUN: diff -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift +// RUN: diff --strip-trailing-cr -u %S/optional_try_migration.swift.expected %t/optional_try_migration.result.swift func fetchOptInt() throws -> Int? { return 3 @@ -109,4 +109,4 @@ func testCaseMatching() { if case let x? = try? fetchInt() { print(x) } -} \ No newline at end of file +} diff --git a/test/Migrator/post_fixit_pass.swift b/test/Migrator/post_fixit_pass.swift index 666cf2e556e44..fa00f730e9b17 100644 --- a/test/Migrator/post_fixit_pass.swift +++ b/test/Migrator/post_fixit_pass.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 -// RUN: diff -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result +// RUN: diff --strip-trailing-cr -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result #if swift(>=4.2) public struct SomeAttribute: RawRepresentable { diff --git a/test/Migrator/post_fixit_pass.swift.expected b/test/Migrator/post_fixit_pass.swift.expected index d950c01b0170f..ec299498188dc 100644 --- a/test/Migrator/post_fixit_pass.swift.expected +++ b/test/Migrator/post_fixit_pass.swift.expected @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s -emit-migrated-file-path %t/post_fixit_pass.swift.result -o /dev/null -F %S/mock-sdk -swift-version 4 -// RUN: diff -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result +// RUN: diff --strip-trailing-cr -u %S/post_fixit_pass.swift.expected %t/post_fixit_pass.swift.result #if swift(>=4.2) public struct SomeAttribute: RawRepresentable { diff --git a/test/Misc/Inputs/empty.swiftinterface b/test/Misc/Inputs/empty.swiftinterface new file mode 100644 index 0000000000000..0b76e7dd9f37a --- /dev/null +++ b/test/Misc/Inputs/empty.swiftinterface @@ -0,0 +1,2 @@ +// swift-interface-format-version: 1.0 +// swift-module-flags: -swift-version 5 diff --git a/test/Misc/stats_dir_module_interface.swift b/test/Misc/stats_dir_module_interface.swift new file mode 100644 index 0000000000000..254d1aaf8fd78 --- /dev/null +++ b/test/Misc/stats_dir_module_interface.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir %t/stats +// RUN: mkdir %t/cache +// RUN: %target-swift-frontend -typecheck %s -I %S/Inputs -stats-output-dir %t/stats -module-cache-path %t/cache + +import empty diff --git a/test/ModuleInterface/ModuleCache/SDKDependencies.swift b/test/ModuleInterface/ModuleCache/SDKDependencies.swift index 20af84064f47d..1000093c93eaa 100644 --- a/test/ModuleInterface/ModuleCache/SDKDependencies.swift +++ b/test/ModuleInterface/ModuleCache/SDKDependencies.swift @@ -1,5 +1,7 @@ // RUN: %empty-directory(%t) +// XFAIL: OS=windows-msvc + // 1) Build a prebuilt cache for our SDK // // RUN: mkdir %t/MCP %t/prebuilt-cache %t/my-sdk @@ -32,7 +34,7 @@ // 2) Baseline check: Make sure we use the interface when not passing the prebuilt module cache path // -// RUN: %target-swift-frontend -typecheck -I %t/my-sdk -sdk %t/my-sdk -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies %s +// RUN: %target-swift-frontend -typecheck -I %t/my-sdk -sdk %t/my-sdk -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies -emit-loaded-module-trace-path %t/trace.json %s // // Check SdkLib and ExportedLib are in the module cache // RUN: test -f %t/MCP/ExportedLib-*.swiftmodule @@ -58,13 +60,23 @@ // DEPFILE-DAG: ExportedLib.swiftinterface // DEPFILE-DAG: SDKDependencies.swift // +// Check we didn't emit anything from the cache in the trace file either +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE-NEGATIVE +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE +// +// TRACEFILE-NEGATIVE-NOT: {{[/\\]MCP[/\\]}} +// TRACEFILE-NEGATIVE-NOT: {{[/\\]prebuilt-cache[/\\]}} +// +// TRACEFILE-DAG: SdkLib.swiftinterface +// TRACEFILE-DAG: ExportedLib.swiftinterface +// // RUN: %empty-directory(%t/MCP) // RUN: echo '2: PASSED' // 3) Baseline check: Make sure we use the the prebuilt module cache when using the SDK it was built with // -// RUN: %target-swift-frontend -typecheck -I %t/my-sdk -sdk %t/my-sdk -prebuilt-module-cache-path %t/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies %s +// RUN: %target-swift-frontend -typecheck -I %t/my-sdk -sdk %t/my-sdk -prebuilt-module-cache-path %t/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies -emit-loaded-module-trace-path %t/trace.json %s // // Check SdkLib and ExportedLib are in the module cache // RUN: test -f %t/MCP/SdkLib-*.swiftmodule @@ -99,6 +111,10 @@ // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE-NEGATIVE // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE // +// Check we didn't emit anything from the cache in the trace file either +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE-NEGATIVE +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE +// // RUN: %empty-directory(%t/MCP) // RUN: echo '3: PASSED' @@ -108,7 +124,7 @@ // RUN: mv %t/my-sdk %t/my-new-sdk // RUN: mkdir %t/new-dir // RUN: mv %t/prebuilt-cache %t/new-dir/ -// RUN: %target-swift-frontend -typecheck -I %t/my-new-sdk -sdk %t/my-new-sdk -prebuilt-module-cache-path %t/new-dir/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies %s +// RUN: %target-swift-frontend -typecheck -I %t/my-new-sdk -sdk %t/my-new-sdk -prebuilt-module-cache-path %t/new-dir/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies -emit-loaded-module-trace-path %t/trace.json %s // // Check SdkLib and ExportedLib are in the module cache // RUN: test -f %t/MCP/SdkLib-*.swiftmodule @@ -136,6 +152,10 @@ // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE-NEGATIVE // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE // +// Check we didn't emit anything from the cache in the trace file either +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE-NEGATIVE +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE +// // RUN: %empty-directory(%t/MCP) // RUN: echo '4: PASSED' @@ -143,7 +163,7 @@ // 5) Now change the SDK's content and check it no longer uses the prebuilt modules // // RUN: echo "// size change" >> %t/my-new-sdk/SdkLib.swiftinterface -// RUN: %target-swift-frontend -typecheck -I %t/my-new-sdk -sdk %t/my-new-sdk -prebuilt-module-cache-path %t/new-dir/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies %s +// RUN: %target-swift-frontend -typecheck -I %t/my-new-sdk -sdk %t/my-new-sdk -prebuilt-module-cache-path %t/new-dir/prebuilt-cache -module-cache-path %t/MCP -emit-dependencies-path %t/dummy.d -track-system-dependencies -emit-loaded-module-trace-path %t/trace.json %s // // Check SDKLib and ExportedLib are in the module cache // RUN: test -f %t/MCP/SdkLib-*.swiftmodule @@ -181,6 +201,10 @@ // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE-NEGATIVE // RUN: cat %t/dummy.d | %FileCheck %s -check-prefix=DEPFILE // +// Check we didn't emit anything from the cache in the trace file either +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE-NEGATIVE +// RUN: cat %t/trace.json | %FileCheck %s -check-prefix=TRACEFILE +// // RUN: echo '5: PASSED' diff --git a/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface b/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface index fd934c5879cb2..d7dd3611b3282 100644 --- a/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface +++ b/test/ModuleInterface/ModuleCache/SerializedSIL.swiftinterface @@ -46,7 +46,7 @@ public var readOnlyVar: Int { get } public var readWriteVar: Int { get set } public func verySimpleFunction() -// CHECK: sil [serialized] [exact_self_class] [canonical] @$s13SerializedSIL9TestClassCACycfC : $@convention(method) (@thick TestClass.Type) -> @owned TestClass { +// CHECK: sil [serialized] [exact_self_class] [canonical] [ossa] @$s13SerializedSIL9TestClassCACycfC : $@convention(method) (@thick TestClass.Type) -> @owned TestClass { // NEGATIVE-NOT: {{sil .*@.+storedProp}} diff --git a/test/ModuleInterface/ModuleCache/prebuilt-module-cache-forwarding.swift b/test/ModuleInterface/ModuleCache/prebuilt-module-cache-forwarding.swift index a882d8843b634..67be554ade6fa 100644 --- a/test/ModuleInterface/ModuleCache/prebuilt-module-cache-forwarding.swift +++ b/test/ModuleInterface/ModuleCache/prebuilt-module-cache-forwarding.swift @@ -50,3 +50,5 @@ struct X {} let _: X = Lib.testValue // FROM-INTERFACE: [[@LINE-1]]:16: error: cannot convert value of type 'FromInterface' to specified type 'X' // FROM-PREBUILT: [[@LINE-2]]:16: error: cannot convert value of type 'FromPrebuilt' to specified type 'X' + +// REQUIRES: rdar58578342 diff --git a/test/ModuleInterface/force-load-autolink.swift b/test/ModuleInterface/force-load-autolink.swift new file mode 100644 index 0000000000000..3cef9dd75b7ef --- /dev/null +++ b/test/ModuleInterface/force-load-autolink.swift @@ -0,0 +1,8 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface %s -module-link-name Foo -enable-library-evolution -autolink-force-load +// RUN: %target-swift-frontend -compile-module-from-interface -o %t/Foo.swiftmodule %t/Foo.swiftinterface +// RUN: %target-swift-ide-test -print-module-metadata -module-to-print Foo -I %t -source-filename %s | %FileCheck %s + +public func foo() {} + +// CHECK: link library: Foo, force load: true \ No newline at end of file diff --git a/test/ModuleInterface/full-convention.swift b/test/ModuleInterface/full-convention.swift new file mode 100644 index 0000000000000..950ad95402ba6 --- /dev/null +++ b/test/ModuleInterface/full-convention.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -enable-library-evolution %s -experimental-print-full-convention | %FileCheck %s + +public func f( + // CHECK: g: @convention(c, cType: "void (*)(void)") + g: @convention(c) () -> (), + + // CHECK: h0: @convention(c, cType: "int (*)(long long)") + h0: @convention(c) (Int64) -> Int32, + // CHECK: h1: @convention(c, cType: "int (*)(long long)") + h1: @convention(c, cType: "int (*)(long long)") (Int64) -> Int32, + + // CHECK: i0: @convention(c, cType: "int *(*)(long long, int)") + i0: @convention(c) (Int64, Int32) -> Optional>, + // CHECK: i1: @convention(c, cType: "int *(*)(long long, int)") + i1: @convention(c, cType: "int *(*)(long long, int)") (Int64, Int32) -> Optional>, + + // CHECK: p0: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p0: @convention(c) (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p1: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p1: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c) (Int32) -> Void) -> Void, + + // CHECK: p2: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p2: @convention(c) (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void, + + // CHECK: p3: @convention(c, cType: "void (*)(void (*)(int))") + // CHECK: @convention(c, cType: "void (*)(int)") + p3: @convention(c, cType: "void (*)(void (*)(int))") (@convention(c, cType: "void (*)(int)") (Int32) -> Void) -> Void +) {} diff --git a/test/ModuleInterface/function_builders.swift b/test/ModuleInterface/function_builders.swift index b6058836c802a..95810acc8f24c 100644 --- a/test/ModuleInterface/function_builders.swift +++ b/test/ModuleInterface/function_builders.swift @@ -2,6 +2,7 @@ // RUN: %target-swift-frontend -typecheck -module-name FunctionBuilders -emit-module-interface-path %t/FunctionBuilders.swiftinterface %s // RUN: %FileCheck %s < %t/FunctionBuilders.swiftinterface // RUN: %target-swift-frontend -I %t -typecheck -verify %S/Inputs/function_builders_client.swift +// RUN: %target-swift-frontend -compile-module-from-interface %t/FunctionBuilders.swiftinterface -o %t/FunctionBuilders.swiftmodule @_functionBuilder public struct TupleBuilder { @@ -33,3 +34,13 @@ public struct TupleBuilder { public func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { print(body(cond)) } + +public struct UsesBuilderProperty { + // CHECK: @FunctionBuilders.TupleBuilder public var myVar: (Swift.String, Swift.String) { + // CHECK-NEXT: get + // CHECK-NEXT: } + @TupleBuilder public var myVar: (String, String) { + "hello" + "goodbye" + } +} diff --git a/test/ModuleInterface/inherited-generic-parameters.swift b/test/ModuleInterface/inherited-generic-parameters.swift index 4a538a33942b7..1be75a69d7311 100644 --- a/test/ModuleInterface/inherited-generic-parameters.swift +++ b/test/ModuleInterface/inherited-generic-parameters.swift @@ -15,6 +15,10 @@ public class Base { // CHECK-NEXT: public init(x: @escaping (In) -> Out) public init(x: @escaping (In) -> Out) {} + +// CHECK-NEXT: public init(_: A, _: A) + public init(_: A, _: A) {} + // CHECK: } } @@ -22,6 +26,7 @@ public class Base { public class Derived : Base { // CHECK-NEXT: {{(@objc )?}}deinit // CHECK-NEXT: override public init(x: @escaping (T) -> T) +// CHECK-NEXT: override public init(_ argument: A, _ argument: A) // CHECK-NEXT: } } diff --git a/test/ModuleInterface/inherits-superclass-initializers-client.swift b/test/ModuleInterface/inherits-superclass-initializers-client.swift new file mode 100644 index 0000000000000..4ec1af1618c2a --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers-client.swift @@ -0,0 +1,43 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init delegates through the subclasses correctly. + +// REQUIRES: executable_test + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: rm %t/Module.swiftmodule +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(Module)) %S/inherits-superclass-initializers.swift -emit-module-path %t/Module.swiftmodule -module-name Module -enable-library-evolution +// RUN: %target-build-swift %s -I %t -L %t -lModule -o %t/main %target-rpath(%t) +// RUN: %target-codesign %t/main %t/%target-library-name(Module) +// RUN: %target-run %t/main %t/%target-library-name(Module) | %FileCheck %s + +import Module + +_ = Base() +// CHECK: secret init from Base + +_ = Sub() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +_ = SubSub() +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +test() +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK: secret init from SubSub +// CHECK: secret init from Sub +// CHECK: secret init from Base + +// CHECK-NOT: public init diff --git a/test/ModuleInterface/inherits-superclass-initializers.swift b/test/ModuleInterface/inherits-superclass-initializers.swift new file mode 100644 index 0000000000000..e2489d58a936e --- /dev/null +++ b/test/ModuleInterface/inherits-superclass-initializers.swift @@ -0,0 +1,60 @@ +// Note: this test has a client: inherits-superclass-initializers-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class Base { +open class Base { + // CHECK-NEXT: public init(arg: Swift.Int) + public init(arg: Int) { + print("public init from Base") + } + // CHECK-NOT: init(secret: Swift.Int) + internal init(secret: Int) { + print("secret init from Base") + } + + // CHECK: convenience public init() + public convenience init() { + self.init(secret: 4) + } + +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class Sub : Module.Base { +public class Sub : Base { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from Sub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from Sub") + super.init(secret: secret) + } +// CHECK: } +} + +// CHECK: @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers public class SubSub : Module.Sub { +public class SubSub: Sub { + // CHECK: override public init(arg: Swift.Int) + public override init(arg: Int) { + print("public init from SubSub") + super.init(arg: arg) + } + // CHECK-NOT: init(secret: Swift.Int) + internal override init(secret: Int) { + print("secret init from SubSub") + super.init(secret: secret) + } +// CHECK: } +} + +@inlinable public func test() { + _ = Sub() + _ = SubSub() +} diff --git a/test/ModuleInterface/member-typealias.swift b/test/ModuleInterface/member-typealias.swift new file mode 100644 index 0000000000000..6d8f2182c2cd6 --- /dev/null +++ b/test/ModuleInterface/member-typealias.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -module-name MyModule | %FileCheck %s --check-prefix CHECK + +public struct MyStruct { +// CHECK-LABEL: public struct MyStruct { + public typealias AliasT = T + public typealias AliasInt = Int + + public func foo(x: AliasInt) -> AliasT { fatalError() } +// CHECK: public func foo(x: MyModule.MyStruct.AliasInt) -> MyModule.MyStruct.AliasT +} + +public class MyBase { + public typealias AliasU = U + public typealias AliasInt = Int +} + +public class MyDerived: MyBase { +// CHECK-LABEL: public class MyDerived : MyModule.MyBase { + public func bar(x: AliasU) -> AliasInt { fatalError() } +// CHECK: public func bar(x: MyModule.MyDerived.AliasU) -> MyModule.MyDerived.AliasInt +} diff --git a/test/ModuleInterface/non-public-designated-inits-client.swift b/test/ModuleInterface/non-public-designated-inits-client.swift new file mode 100644 index 0000000000000..a0e84cb52cbf5 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits-client.swift @@ -0,0 +1,26 @@ +// Compile the imported module to a .swiftinterface and ensure the convenience +// init cannot be called. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -typecheck %S/non-public-designated-inits.swift -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +// Make sure the same error is emitted when importing a .swiftmodule + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/Module.swiftmodule %S/non-public-designated-inits.swift -module-name Module -enable-library-evolution +// RUN: %target-swift-frontend -typecheck -verify %s -I %t + +import Module + +open class B : A { + var x: Int + + public override init(_ x: Int) { + self.x = x + super.init(x) + } +} + +print(B(hi: ())) // expected-error {{cannot convert value of type '()' to expected argument type 'Int'}} +// expected-error @-1 {{extraneous argument label 'hi:' in call}} diff --git a/test/ModuleInterface/non-public-designated-inits.swift b/test/ModuleInterface/non-public-designated-inits.swift new file mode 100644 index 0000000000000..c8b00df92e677 --- /dev/null +++ b/test/ModuleInterface/non-public-designated-inits.swift @@ -0,0 +1,21 @@ +// Note: This test has a client: non-public-designated-inits-client.swift + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Module.swiftinterface -module-name Module -enable-library-evolution +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// CHECK: @_hasMissingDesignatedInitializers open class A { +open class A { + // This is a non-public designated init, which means the convenience + // init should not be inheritable. + init() {} + + // CHECK-NEXT: public init(_: Swift.Int) + public init(_: Int) {} + + // CHECK-NEXT: convenience public init(hi: ()) + public convenience init(hi: ()) { self.init() } + +// CHECK: } +} diff --git a/test/ModuleInterface/nsmanaged-attr.swift b/test/ModuleInterface/nsmanaged-attr.swift new file mode 100644 index 0000000000000..2312c8a926294 --- /dev/null +++ b/test/ModuleInterface/nsmanaged-attr.swift @@ -0,0 +1,41 @@ +// RUN: %empty-directory(%t) + +// This test ensures we can properly load modules that have @NSManaged properties. + +// 1. Emit this file to a module interface +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t/Module.swiftinterface %s -enable-library-evolution -module-name Module + +// 2. Check the interface against what we expect +// RUN: %FileCheck %s < %t/Module.swiftinterface + +// 3. Ensure we can load this module from its interface +// RUN: echo 'import Module' | %target-swift-frontend -typecheck - -I %t + +// REQUIRES: objc_interop + +import CoreData +import Foundation + +// CHECK: @objc @_inheritsConvenienceInitializers public class MyObject : CoreData.NSManagedObject { +public class MyObject: NSManagedObject { + // CHECK: @objc @NSManaged dynamic public var myVar: Swift.String { + // CHECK-NEXT: @objc get + // CHECK-NEXT: @objc set + // CHECK-NEXT: } + @NSManaged public var myVar: String + // CHECK: @NSManaged @objc dynamic public var myVar2: Swift.String { + // CHECK-NEXT: @objc get + // CHECK-NEXT: @objc set + // CHECK-NEXT: } + @NSManaged @objc public var myVar2: String + // CHECK: @NSManaged @objc dynamic public var myVar3: Swift.String { + // CHECK-NEXT: @objc get + // CHECK-NEXT: @objc set + // CHECK-NEXT: } + @NSManaged @objc dynamic public var myVar3: String + // CHECK: @NSManaged @objc dynamic public var myVar4: Swift.String { + // CHECK-NEXT: @objc get + // CHECK-NEXT: } + @NSManaged @objc dynamic public private(set) var myVar4: String +// CHECK: } +} diff --git a/test/ModuleInterface/originally-defined-attr.swift b/test/ModuleInterface/originally-defined-attr.swift new file mode 100644 index 0000000000000..0940414f57e32 --- /dev/null +++ b/test/ModuleInterface/originally-defined-attr.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) + +// Ensure the attribute is printed in swiftinterface files +// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/Foo.swiftinterface %s -module-name Foo +// RUN: %FileCheck %s < %t/Foo.swiftinterface + +// Ensure the attribute is in .swiftmodule files +// RUN: %target-swift-ide-test -print-module -module-to-print Foo -I %t -source-filename %s > %t/printed-module.txt +// RUN: %FileCheck %s < %t/printed-module.txt + +// CHECK: @_originallyDefinedIn(module: "another", OSX 13.13) +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "another", OSX 13.13) +public protocol SimpleProto { } + +// CHECK: @_originallyDefinedIn(module: "original", tvOS 1.0) +// CHECK: @_originallyDefinedIn(module: "another_original", OSX 2.0) +// CHECK: @_originallyDefinedIn(module: "another_original", iOS 3.0) +// CHECK: @_originallyDefinedIn(module: "another_original", watchOS 4.0) +@available(tvOS 0.7, OSX 1.1, iOS 2.1, watchOS 3.2, *) +@_originallyDefinedIn(module: "original", tvOS 1.0) +@_originallyDefinedIn(module: "another_original", OSX 2.0, iOS 3.0, watchOS 4.0) +public struct SimpleStruct {} diff --git a/test/ModuleInterface/property_wrappers.swift b/test/ModuleInterface/property_wrappers.swift index 0196890e5b830..3a3e9c34c11f8 100644 --- a/test/ModuleInterface/property_wrappers.swift +++ b/test/ModuleInterface/property_wrappers.swift @@ -44,6 +44,7 @@ public struct HasWrappers { // CHECK: @TestResilient.Wrapper public var x: {{(Swift.)?}}Int { // CHECK-NEXT: get // CHECK-NEXT: set + // CHECK-NEXT: _modify // CHECK-NEXT: } @Wrapper public var x: Int @@ -59,6 +60,7 @@ public struct HasWrappers { // CHECK: @TestResilient.WrapperWithInitialValue @_projectedValueProperty($z) public var z: Swift.Bool { // CHECK-NEXT: get // CHECK-NEXT: set + // CHECK-NEXT: _modify // CHECK-NEXT: } @WrapperWithInitialValue(alternate: false) public var z } diff --git a/test/ModuleInterface/static-initialize-objc-metadata-attr.swift b/test/ModuleInterface/static-initialize-objc-metadata-attr.swift new file mode 100644 index 0000000000000..f2ed55450f8e2 --- /dev/null +++ b/test/ModuleInterface/static-initialize-objc-metadata-attr.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s -enable-library-evolution -target %target-pre-stable-abi-triple -module-name Module | %FileCheck %s +// REQUIRES: objc_interop + +import Foundation + +// To infer @_staticInitializeObjCMetadata, the following needs to be true +// Our class needs to be: +// - A subclass of a generic Objective-C class +// - That inherits a conformance to a protocol +// - Declared in a module with a deployment target before the stable ABI + +public class Super: NSObject, NSCoding { + required public init(coder: NSCoder) {} + public func encode(with: NSCoder) {} +} + +// CHECK-NOT: @_staticInitializeObjCMetadata +// CHECK: public class Sub : Module.Super +public class Sub: Super { + required public init(coder: NSCoder) {} + override public func encode(with: NSCoder) {} +} diff --git a/test/ModuleInterface/stored-properties.swift b/test/ModuleInterface/stored-properties.swift index e641ad24aa2ae..31279c2c1af39 100644 --- a/test/ModuleInterface/stored-properties.swift +++ b/test/ModuleInterface/stored-properties.swift @@ -35,18 +35,18 @@ public struct HasStoredProperties { public var simpleStoredMutable: Int // CHECK: @_hasStorage public var storedWithObservers: Swift.Bool { - // RESILIENT: {{^}} public var storedWithObservers: Swift.Bool { - // COMMON-NEXT: get - // COMMON-NEXT: set - // COMMON-NEXT: } + // RESILIENT: {{^}} public var storedWithObservers: Swift.Bool { + // COMMON-NEXT: {{^}} get + // COMMON-NEXT: {{^}} set + // COMMON-NEXT: {{^}} } public var storedWithObservers: Bool { willSet {} } // CHECK: @_hasStorage public var storedPrivateSet: Swift.Int { - // RESILIENT: {{^}} public var storedPrivateSet: Swift.Int { - // COMMON-NEXT: get - // COMMON-NEXT: } + // RESILIENT: {{^}} public var storedPrivateSet: Swift.Int { + // COMMON-NEXT: {{^}} get + // COMMON-NEXT: {{^}} } public private(set) var storedPrivateSet: Int // CHECK: private var privateVar: Swift.Bool @@ -54,10 +54,10 @@ public struct HasStoredProperties { private var privateVar: Bool // CHECK: @_hasStorage @_hasInitialValue public var storedWithObserversInitialValue: Swift.Int { - // RESILIENT: {{^}} public var storedWithObserversInitialValue: Swift.Int { - // COMMON-NEXT: get - // COMMON-NEXT: set - // COMMON-NEXT: } + // RESILIENT: {{^}} public var storedWithObserversInitialValue: Swift.Int { + // COMMON-NEXT: {{^}} get + // COMMON-NEXT: {{^}} set + // COMMON-NEXT: {{^}} } public var storedWithObserversInitialValue: Int = 0 { didSet {} } @@ -101,10 +101,10 @@ public struct HasStoredPropertiesFixedLayout { // COMMON: public var simpleStoredMutable: StoredProperties.BagOfVariables public var simpleStoredMutable: BagOfVariables - // COMMON: @_hasStorage public var storedWithObservers: StoredProperties.BagOfVariables { - // COMMON-NEXT: get - // COMMON-NEXT: set - // COMMON-NEXT: } + // COMMON: {{^}} @_hasStorage public var storedWithObservers: StoredProperties.BagOfVariables { + // COMMON-NEXT: {{^}} get + // COMMON-NEXT: {{^}} set + // COMMON-NEXT: {{^}} } public var storedWithObservers: BagOfVariables { didSet {} } diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/early-exits.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/early-exits.test-sh index bf9f8ced03b7a..d6e53a0135cd0 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/early-exits.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/early-exits.test-sh @@ -1,8 +1,8 @@ -RUN: not %swift_build_sdk_interfaces 2>&1 | %FileCheck -check-prefix NO-OUTPUT %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt 2>&1 | %FileCheck -check-prefix NO-OUTPUT %s NO-OUTPUT: argument -o is required -RUN: not env -u SDKROOT %swift_build_sdk_interfaces -o %t 2>&1 | %FileCheck -check-prefix NO-SDK %s +RUN: env -u SDKROOT SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -o %t 2>&1 | %FileCheck -check-prefix NO-SDK %s NO-SDK: SDKROOT must be set -RUN: not %swift_build_sdk_interfaces -o %t -sdk %S/nonexistent 2>&1 | %FileCheck -check-prefix BAD-SDK %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -o %t -sdk %S/nonexistent 2>&1 | %FileCheck -check-prefix BAD-SDK %s BAD-SDK: invalid SDK: {{.+[/\\]nonexistent$}} diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh index 2e2c6e925f5f6..5747b3cfe0da7 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/ignore-non-stdlib-failures.test-sh @@ -1,4 +1,4 @@ -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -v -o %t/output | %FileCheck %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -v -o %t/output | %FileCheck %s CHECK-DAG: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface CHECK-DAG: # (PASS) {{.+}}{{\\|/}}Good.swiftinterface @@ -8,11 +8,11 @@ RUN: %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -v -o %t/output -ign CHECK-IGNORING-FAILURES-DAG: # (XFAIL) {{.+}}{{\\|/}}Bad.swiftinterface CHECK-IGNORING-FAILURES-DAG: # (UPASS) {{.+}}{{\\|/}}Good.swiftinterface -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output | %FileCheck -check-prefix CHECK-BROKEN-STDLIB %s -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output -ignore-non-stdlib-failures | %FileCheck -check-prefix CHECK-BROKEN-STDLIB %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output | %FileCheck -check-prefix CHECK-BROKEN-STDLIB %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output -ignore-non-stdlib-failures | %FileCheck -check-prefix CHECK-BROKEN-STDLIB %s CHECK-BROKEN-STDLIB: # (FAIL) {{.+}}{{\\|/}}Swift.swiftinterface CHECK-BROKEN-STDLIB-NOT: {{^}}# -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output -skip-stdlib | %FileCheck %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output -skip-stdlib | %FileCheck %s RUN: %swift_build_sdk_interfaces -sdk %S/Inputs/broken-stdlib-sdk/ -v -o %t/output -skip-stdlib -ignore-non-stdlib-failures | %FileCheck -check-prefix=CHECK-IGNORING-FAILURES %s diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh index d6e9aabae4b6c..d9c8457df68c7 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/xfail-logs.test-sh @@ -1,5 +1,5 @@ RUN: %empty-directory(%t) -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -o %t/output -log-path %t/logs | %FileCheck %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -o %t/output -log-path %t/logs | %FileCheck %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/logs/Bad-Bad-err.txt CHECK: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface @@ -8,7 +8,7 @@ PRINTS-ERROR: unresolved identifier 'garbage' RUN: %empty-directory(%t) RUN: echo '["Good"]' > %t/xfails-good.json -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -o %t/output -log-path %t/logs -xfails %t/xfails-good.json | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -o %t/output -log-path %t/logs -xfails %t/xfails-good.json | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/logs/Bad-Bad-err.txt CHECK-XFAIL-GOOD-DAG: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface @@ -30,7 +30,7 @@ RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/logs/Bad-Bad-err.txt CHECK-XFAIL-BAD: # (XFAIL) {{.+}}{{\\|/}}Bad.swiftinterface RUN: %empty-directory(%t) -RUN: not %swift_build_sdk_interfaces -sdk %t -o %t/output -log-path %t/logs %S/Inputs/xfail-logs-framework/ | %FileCheck -check-prefix=CHECK-FRAMEWORK %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %t -o %t/output -log-path %t/logs %S/Inputs/xfail-logs-framework/ | %FileCheck -check-prefix=CHECK-FRAMEWORK %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/logs/BadFMWK-x86_64-apple-macos-err.txt CHECK-FRAMEWORK: # (FAIL) {{.+}}{{\\|/}}BadFMWK.swiftmodule{{\\|/}}x86_64-apple-macos.swiftinterface diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/xfails.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/xfails.test-sh index 6e8f19a237109..b85baf7cd0292 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/xfails.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/xfails.test-sh @@ -1,7 +1,7 @@ RUN: %empty-directory(%t) -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -o %t/output 2> %t/stderr.txt | %FileCheck %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -o %t/output 2> %t/stderr.txt | %FileCheck %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/stderr.txt -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -v -o %t/output 2> %t/stderr.txt | %FileCheck -check-prefix CHECK-VERBOSE %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -v -o %t/output 2> %t/stderr.txt | %FileCheck -check-prefix CHECK-VERBOSE %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/stderr.txt CHECK: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface @@ -13,9 +13,9 @@ HIDES-ERROR-NOT: unresolved identifier 'garbage' RUN: %empty-directory(%t) RUN: echo '["Good"]' > %t/xfails-good.json -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -o %t/output -xfails %t/xfails-good.json 2> %t/stderr.txt | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -o %t/output -xfails %t/xfails-good.json 2> %t/stderr.txt | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/stderr.txt -RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/xfails-sdk/ -v -o %t/output -xfails %t/xfails-good.json 2> %t/stderr.txt | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s +RUN: env SWIFT_EXEC=%swiftc_driver_plain not %{python} %utils/swift_build_sdk_interfaces.py %mcp_opt -sdk %S/Inputs/xfails-sdk/ -v -o %t/output -xfails %t/xfails-good.json 2> %t/stderr.txt | %FileCheck -check-prefix=CHECK-XFAIL-GOOD %s RUN: %FileCheck -check-prefix PRINTS-ERROR %s < %t/stderr.txt CHECK-XFAIL-GOOD-DAG: # (FAIL) {{.+}}{{\\|/}}Bad.swiftinterface diff --git a/test/NameBinding/Dependencies/private-function-fine.swift b/test/NameBinding/Dependencies/private-function-fine.swift new file mode 100644 index 0000000000000..05791fb9cede0 --- /dev/null +++ b/test/NameBinding/Dependencies/private-function-fine.swift @@ -0,0 +1,19 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private func testParamType(_: InterestingType) {} + +// CHECK-OLD: sil_global hidden @$s4main1x{{[^ ]+}} : ${{(@[a-zA-Z_]+ )?}}(Int) -> () +// CHECK-NEW: sil_global hidden @$s4main1x{{[^ ]+}} : ${{(@[a-zA-Z_]+ )?}}(Double) -> () +internal var x = testParamType + +// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-function-return-type-fine.swift b/test/NameBinding/Dependencies/private-function-return-type-fine.swift new file mode 100644 index 0000000000000..4d304190f3670 --- /dev/null +++ b/test/NameBinding/Dependencies/private-function-return-type-fine.swift @@ -0,0 +1,19 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private func testReturnType() -> InterestingType { fatalError() } + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = testReturnType() + 0 + +// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-function-return-type.swift b/test/NameBinding/Dependencies/private-function-return-type.swift index 8db47832d5e92..7f6b6bc1b3c7a 100644 --- a/test/NameBinding/Dependencies/private-function-return-type.swift +++ b/test/NameBinding/Dependencies/private-function-return-type.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private func testReturnType() -> InterestingType { fatalError() } diff --git a/test/NameBinding/Dependencies/private-function.swift b/test/NameBinding/Dependencies/private-function.swift index 5a9c4fe45eee2..ea9c46387ab1e 100644 --- a/test/NameBinding/Dependencies/private-function.swift +++ b/test/NameBinding/Dependencies/private-function.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private func testParamType(_: InterestingType) {} diff --git a/test/NameBinding/Dependencies/private-protocol-conformer-ext-fine.swift b/test/NameBinding/Dependencies/private-protocol-conformer-ext-fine.swift new file mode 100644 index 0000000000000..f48a2ff294c03 --- /dev/null +++ b/test/NameBinding/Dependencies/private-protocol-conformer-ext-fine.swift @@ -0,0 +1,24 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private struct Test {} +extension Test : InterestingProto {} + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = Test().make() + 0 + +// CHECK-DEPS-DAG: topLevel interface '' InterestingProto false + +// CHECK-DEPS-DAG: member interface 4main{{8IntMaker|11DoubleMaker}}P make false + +// CHECK-DEPS-DAG: nominal interface 4main{{8IntMaker|11DoubleMaker}}P '' false diff --git a/test/NameBinding/Dependencies/private-protocol-conformer-ext.swift b/test/NameBinding/Dependencies/private-protocol-conformer-ext.swift index 1e4fbcc8f6366..05a33cf13e09f 100644 --- a/test/NameBinding/Dependencies/private-protocol-conformer-ext.swift +++ b/test/NameBinding/Dependencies/private-protocol-conformer-ext.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private struct Test {} diff --git a/test/NameBinding/Dependencies/private-protocol-conformer-fine.swift b/test/NameBinding/Dependencies/private-protocol-conformer-fine.swift new file mode 100644 index 0000000000000..0e018f1120eee --- /dev/null +++ b/test/NameBinding/Dependencies/private-protocol-conformer-fine.swift @@ -0,0 +1,23 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private struct Test : InterestingProto {} + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = Test().make() + 0 + +// CHECK-DEPS-DAG: topLevel interface '' InterestingProto false + +// CHECK-DEPS-DAG: member interface 4main{{8IntMaker|11DoubleMaker}}P make false + +// CHECK-DEPS-DAG: nominal interface 4main{{8IntMaker|11DoubleMaker}}P '' false diff --git a/test/NameBinding/Dependencies/private-protocol-conformer.swift b/test/NameBinding/Dependencies/private-protocol-conformer.swift index 1c3dbf4ab24c4..909594a7bae3f 100644 --- a/test/NameBinding/Dependencies/private-protocol-conformer.swift +++ b/test/NameBinding/Dependencies/private-protocol-conformer.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private struct Test : InterestingProto {} diff --git a/test/NameBinding/Dependencies/private-struct-member-fine.swift b/test/NameBinding/Dependencies/private-struct-member-fine.swift new file mode 100644 index 0000000000000..4ab46db59a332 --- /dev/null +++ b/test/NameBinding/Dependencies/private-struct-member-fine.swift @@ -0,0 +1,21 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private struct Wrapper { + static func test() -> InterestingType { fatalError() } +} + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = Wrapper.test() + 0 + +/// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-struct-member.swift b/test/NameBinding/Dependencies/private-struct-member.swift index f00286109234e..a983a22a81dc0 100644 --- a/test/NameBinding/Dependencies/private-struct-member.swift +++ b/test/NameBinding/Dependencies/private-struct-member.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private struct Wrapper { diff --git a/test/NameBinding/Dependencies/private-subscript-fine.swift b/test/NameBinding/Dependencies/private-subscript-fine.swift new file mode 100644 index 0000000000000..badbdf77686aa --- /dev/null +++ b/test/NameBinding/Dependencies/private-subscript-fine.swift @@ -0,0 +1,21 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +struct Wrapper { + fileprivate subscript() -> InterestingType { fatalError() } +} + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = Wrapper()[] + 0 + +// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-subscript.swift b/test/NameBinding/Dependencies/private-subscript.swift index b964a7960d305..728e6a1c59912 100644 --- a/test/NameBinding/Dependencies/private-subscript.swift +++ b/test/NameBinding/Dependencies/private-subscript.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps struct Wrapper { diff --git a/test/NameBinding/Dependencies/private-typealias-fine.swift b/test/NameBinding/Dependencies/private-typealias-fine.swift new file mode 100644 index 0000000000000..a4c9925138baf --- /dev/null +++ b/test/NameBinding/Dependencies/private-typealias-fine.swift @@ -0,0 +1,21 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private struct Wrapper { + static func test() -> InterestingType { fatalError() } +} + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = Wrapper.test() + 0 + +// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-typealias.swift b/test/NameBinding/Dependencies/private-typealias.swift index f00286109234e..a983a22a81dc0 100644 --- a/test/NameBinding/Dependencies/private-typealias.swift +++ b/test/NameBinding/Dependencies/private-typealias.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private struct Wrapper { diff --git a/test/NameBinding/Dependencies/private-var-fine.swift b/test/NameBinding/Dependencies/private-var-fine.swift new file mode 100644 index 0000000000000..a955c590873f3 --- /dev/null +++ b/test/NameBinding/Dependencies/private-var-fine.swift @@ -0,0 +1,20 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD + +// RUN: %S/../../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps + +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t-processed.swiftdeps + +private var privateVar: InterestingType { fatalError() } + +// CHECK-OLD: sil_global @$s4main1x{{[^ ]+}} : $Int +// CHECK-NEW: sil_global @$s4main1x{{[^ ]+}} : $Double +public var x = privateVar + 0 + +// CHECK-DEPS: topLevel interface '' InterestingType false diff --git a/test/NameBinding/Dependencies/private-var.swift b/test/NameBinding/Dependencies/private-var.swift index 214f62c3f893a..896689f4741f7 100644 --- a/test/NameBinding/Dependencies/private-var.swift +++ b/test/NameBinding/Dependencies/private-var.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DOLD -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-OLD // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps -// RUN: %target-swift-frontend -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -emit-silgen -primary-file %s %S/Inputs/InterestingType.swift -DNEW -emit-reference-dependencies-path %t.swiftdeps -module-name main | %FileCheck %s -check-prefix=CHECK-NEW // RUN: %FileCheck -check-prefix=CHECK-DEPS %s < %t.swiftdeps private var privateVar: InterestingType { fatalError() } diff --git a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h index f018637dc7ebb..86d93cc55e0d0 100644 --- a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h +++ b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h @@ -26,6 +26,8 @@ // Don't conform to the protocol; that loads all protocol members. @interface SimpleDoer +- (instancetype)initWithValue: (int)value; + // These are names we're hoping don't interfere with Doer, above. + (SimpleDoer*)Doer; + (SimpleDoer*)DoerOfNoWork; @@ -107,4 +109,14 @@ @interface SimpleDoerSubclass : SimpleDoer - (void)simplyDoSomeWorkWithSpeed:(int)s thoroughness:(int)t NS_SWIFT_NAME(simplyDoVeryImportantWork(speed:thoroughness:)); + +- (void)exuberantlyGoForWalk; +- (void)exuberantlyTakeNap; +- (void)exuberantlyEatMeal; +- (void)exuberantlyTidyHome; +- (void)exuberantlyCallFamily; +- (void)exuberantlySingSong; +- (void)exuberantlyReadBook; +- (void)exuberantlyAttendLecture; +- (void)exuberantlyWriteLetter; @end diff --git a/test/NameBinding/Inputs/lazy_function_body_expansion_helper.swift b/test/NameBinding/Inputs/lazy_function_body_expansion_helper.swift new file mode 100644 index 0000000000000..7e949be39d4bc --- /dev/null +++ b/test/NameBinding/Inputs/lazy_function_body_expansion_helper.swift @@ -0,0 +1,7 @@ +// Body of closure in parameter to call of closureTaker is created lazily +// and this test ensures that that body scope does get expanded +var v = closureTaker { + func amIFound() {} +} + +func closureTaker(_: () -> Void ) diff --git a/test/NameBinding/accessibility.swift b/test/NameBinding/accessibility.swift index 2f7f228c98237..f6465796e54d2 100644 --- a/test/NameBinding/accessibility.swift +++ b/test/NameBinding/accessibility.swift @@ -64,6 +64,7 @@ _ = PrivateInit() // expected-error {{'PrivateInit' initializer is inaccessible // TESTABLE: :[[@LINE-1]]:{{[^:]+}}: error: 'PrivateInit' initializer is inaccessible due to 'private' protection level var s = StructWithPrivateSetter() +// expected-note@-1 3{{did you mean 's'?}} s.x = 42 // expected-error {{cannot assign to property: 'x' setter is inaccessible}} class Sub : Base { diff --git a/test/NameBinding/disabled_opaque_decl.swift b/test/NameBinding/disabled_opaque_decl.swift new file mode 100644 index 0000000000000..11e920d1072bb --- /dev/null +++ b/test/NameBinding/disabled_opaque_decl.swift @@ -0,0 +1,14 @@ +// Ensure scope construction does not crash +// RUN: %target-swift-frontend -emit-module %s + +public protocol P { } + +extension Int: P { } + +#if false +public struct Foo { + public func getP() -> some P { + return 17 + } +} +#endif diff --git a/test/NameBinding/import-resolution-overload.swift b/test/NameBinding/import-resolution-overload.swift index c8b6f2fbf20d8..3e466103e1ac7 100644 --- a/test/NameBinding/import-resolution-overload.swift +++ b/test/NameBinding/import-resolution-overload.swift @@ -46,7 +46,7 @@ scopedFunction = 42 // FIXME: Should be an error -- a type name and a function cannot overload. var _ : Int = TypeNameWins(42) -TypeNameWins = 42 // expected-error {{ambiguous reference to member 'TypeNameWins'}} +TypeNameWins = 42 // expected-error {{no exact matches in assignment to 'TypeNameWins'}} var _ : TypeNameWins // no-warning // rdar://problem/21739333 diff --git a/test/NameBinding/lazy_function_body_expansion.swift b/test/NameBinding/lazy_function_body_expansion.swift new file mode 100644 index 0000000000000..f7ca75bb4deda --- /dev/null +++ b/test/NameBinding/lazy_function_body_expansion.swift @@ -0,0 +1,5 @@ +// Ensure that a lazily-parsed function body gets expanded + +// RUN: %target-swift-frontend -typecheck -primary-file %s %S/Inputs/lazy_function_body_expansion_helper.swift + +let a = v diff --git a/test/NameBinding/name-binding.swift b/test/NameBinding/name-binding.swift index 6ca41b237e8b4..25b6ffc2017f1 100644 --- a/test/NameBinding/name-binding.swift +++ b/test/NameBinding/name-binding.swift @@ -55,16 +55,16 @@ func test_varname_binding() { var ((), (g1, g2), h) = ((), (e, d), e) var (j, k, l) = callee1() var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} - var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, Any)', tuples have a different number of elements}} + var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, _)', tuples have a different number of elements}} } //===----------------------------------------------------------------------===// // ForwardIndex referencing of types. //===----------------------------------------------------------------------===// -// We don't allow namebinding to look forward past a var declaration in the +// We allow namebinding to look forward past a var declaration in the // main module -var x : x_ty // expected-error {{use of undeclared type 'x_ty'}} +var x : x_ty typealias x_ty = Int // We allow namebinding to look forward past a function declaration (and other diff --git a/test/NameBinding/name_lookup.swift b/test/NameBinding/name_lookup.swift index 32df450d95424..6100e519c7561 100644 --- a/test/NameBinding/name_lookup.swift +++ b/test/NameBinding/name_lookup.swift @@ -224,7 +224,7 @@ class ThisDerived1 : ThisBase1 { self.baseProp = 42 // expected-error {{member 'baseProp' cannot be used on type 'ThisDerived1'}} self.baseFunc0() // expected-error {{instance member 'baseFunc0' cannot be used on type 'ThisDerived1'}} self.baseFunc0(ThisBase1())() // expected-error {{cannot convert value of type 'ThisBase1' to expected argument type 'ThisDerived1'}} - + self.baseFunc0(ThisDerived1())() self.baseFunc1(42) // expected-error {{instance member 'baseFunc1' cannot be used on type 'ThisDerived1'}} self.baseFunc1(ThisBase1())(42) // expected-error {{cannot convert value of type 'ThisBase1' to expected argument type 'ThisDerived1'}} @@ -482,7 +482,7 @@ struct MyStruct { // QoI: poor diagnostic initializing a variable with a non-class func class Test19935319 { let i = getFoo() // expected-error {{cannot use instance member 'getFoo' within property initializer; property initializers run before 'self' is available}} - + func getFoo() -> Int {} } @@ -553,9 +553,9 @@ default: } func foo() { - _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'}}{{14-17=one}} - _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'}}{{14-17=two}} - _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'}}{{14-25=oneTwoThree}} + _ = MyEnum.One // expected-error {{enum type 'MyEnum' has no case 'One'; did you mean 'one'?}}{{14-17=one}} + _ = MyEnum.Two // expected-error {{enum type 'MyEnum' has no case 'Two'; did you mean 'two'?}}{{14-17=two}} + _ = MyEnum.OneTwoThree // expected-error {{enum type 'MyEnum' has no case 'OneTwoThree'; did you mean 'oneTwoThree'?}}{{14-25=oneTwoThree}} } enum MyGenericEnum { @@ -564,8 +564,8 @@ enum MyGenericEnum { } func foo1() { - _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'}}{{26-29=one}} - _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'}}{{26-32=oneTwo}} + _ = MyGenericEnum.One // expected-error {{enum type 'MyGenericEnum' has no case 'One'; did you mean 'one'?}}{{26-29=one}} + _ = MyGenericEnum.OneTwo // expected-error {{enum type 'MyGenericEnum' has no case 'OneTwo'; did you mean 'oneTwo'?}}{{26-32=oneTwo}} } // SR-4082 @@ -616,15 +616,24 @@ struct PatternBindingWithTwoVars1 { var x = 3, y = x } // expected-error@-1 {{cannot use instance member 'x' within property initializer; property initializers run before 'self' is available}} struct PatternBindingWithTwoVars2 { var x = y, y = 3 } -// expected-error@-1 {{type 'PatternBindingWithTwoVars2' has no member 'y'}} +// expected-error@-1 {{cannot use instance member 'y' within property initializer; property initializers run before 'self' is available}} -// This one should be accepted, but for now PatternBindingDecl validation -// circularity detection is not fine grained enough. struct PatternBindingWithTwoVars3 { var x = y, y = x } -// expected-error@-1 {{type 'PatternBindingWithTwoVars3' has no member 'y'}} +// expected-error@-1 {{circular reference}} +// expected-note@-2 {{through reference here}} +// expected-note@-3 {{through reference here}} +// expected-note@-4 {{through reference here}} +// expected-note@-5 {{through reference here}} +// expected-note@-6 {{through reference here}} +// expected-error@-7 {{circular reference}} +// expected-note@-8 {{through reference here}} +// expected-note@-9 {{through reference here}} +// expected-note@-10 {{through reference here}} +// expected-note@-11 {{through reference here}} +// expected-note@-12 {{through reference here}} // https://bugs.swift.org/browse/SR-9015 func sr9015() { - let closure1 = { closure2() } // expected-error {{let 'closure1' references itself}} - let closure2 = { closure1() } + let closure1 = { closure2() } // expected-error {{circular reference}} expected-note {{through reference here}} expected-note {{through reference here}} + let closure2 = { closure1() } // expected-note {{through reference here}} expected-note {{through reference here}} expected-note {{through reference here}} } diff --git a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift index 4114f25c6088d..e79212e6aa318 100644 --- a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift +++ b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift @@ -16,8 +16,7 @@ extension ContainsMinMax { func min() {} } -func foo(_: Int, _: Int) {} -// expected-note@-1 {{'foo' declared here}} +func foo(_: Int, _: Int) {} // expected-note 2 {{'foo' declared here}} protocol ContainsFoo {} extension ContainsFoo { @@ -34,15 +33,15 @@ extension NonConditional { _ = min(3, 4) // expected-error@-1{{use of 'min' refers to instance method}} // expected-note@-2{{use 'Swift.' to reference the global function}} - _ = foo(5, 6) - // expected-error@-1{{use of 'foo' refers to instance method}} - // expected-note@-2{{use 'name_lookup_min_max_conditional_conformance.' to reference the global function}} + + _ = foo(5, 6) // expected-error {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-1 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } struct Conditional {} extension Conditional: ContainsMinMax where T: ContainsMinMax {} -extension Conditional: ContainsFoo where T: ContainsFoo {} // expected-note {{requirement from conditional conformance of 'Conditional' to 'ContainsFoo'}} +extension Conditional: ContainsFoo where T: ContainsFoo {} extension Conditional { func f() { @@ -52,7 +51,9 @@ extension Conditional { _ = min(3, 4) // expected-warning@-1{{use of 'min' as reference to global function in module 'Swift' will change in future versions of Swift to reference instance method in generic struct 'Conditional' which comes via a conditional conformance}} // expected-note@-2{{use 'Swift.' to continue to reference the global function}} + _ = foo(5, 6) - // expected-error@-1{{referencing instance method 'foo()' on 'Conditional' requires that 'T' conform to 'ContainsFoo'}} + // expected-error@-1 {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-2 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } diff --git a/test/NameBinding/named_lazy_member_loading_objc_category.swift b/test/NameBinding/named_lazy_member_loading_objc_category.swift index c3a45c9a41114..db5932d19630c 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_category.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_category.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -30' %t/stats-pre %t/stats-post import NamedLazyMembers diff --git a/test/NameBinding/named_lazy_member_loading_objc_interface.swift b/test/NameBinding/named_lazy_member_loading_objc_interface.swift index 2f8717c213c4c..d4201213bc328 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_interface.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_interface.swift @@ -8,17 +8,23 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post -primary-file %s %S/Inputs/NamedLazyMembers/NamedLazyMembersExt.swift -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities <= -10' %t/stats-pre %t/stats-post import NamedLazyMembers -public func bar(d: SimpleDoerSubclass) { - let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) +public func bar() { + let d = SimpleDoerSubclass(value: 123)! + let _ = d.simplyDoVeryImportantWork(speed: 10, motivation: 42) } -public func foo(d: SimpleDoer) { +public func foo() { + let d = SimpleDoer(value: 123)! let _ = d.simplyDoSomeWork() let _ = d.simplyDoSomeWork(withSpeed:10) let _ = d.simplyDoVeryImportantWork(speed:10, thoroughness:12) let _ = d.simplyDoSomeWorkWithSpeed(speed:10, levelOfAlacrity:12) } + +// Make sure that simply subclassing an imported subclass doesn't page in all +// members. +class MostDoerSubclass : SimpleDoerSubclass {} diff --git a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift index 65f66bb372e1e..94607c52b675e 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_protocol.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_protocol.swift @@ -8,7 +8,7 @@ // Check that named-lazy-member-loading reduces the number of Decls deserialized // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s // RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -9' %t/stats-pre %t/stats-post +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -40' %t/stats-pre %t/stats-post import NamedLazyMembers diff --git a/test/NameBinding/reference-dependencies-consistency-fine.swift b/test/NameBinding/reference-dependencies-consistency-fine.swift new file mode 100644 index 0000000000000..8095242d4d18a --- /dev/null +++ b/test/NameBinding/reference-dependencies-consistency-fine.swift @@ -0,0 +1,19 @@ +// Some types, such as StringLiteralType, used to be cached in the TypeChecker. +// Consequently, the second primary file (in batch mode) to use that type would +// hit in the cache and no dependency would be recorded. +// This test ensures that this bug stays fixed. +// +// RUN: %empty-directory(%t) +// +// Create two identical inputs, each needing StringLiteralType: +// +// RUN: echo 'fileprivate var v: String { return "\(x)" }; fileprivate let x = "a"' >%t/1.swift +// RUN: echo 'fileprivate var v: String { return "\(x)" }; fileprivate let x = "a"' >%t/2.swift +// +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/1.swift -primary-file %t/2.swift -emit-reference-dependencies-path %t/1.swiftdeps -emit-reference-dependencies-path %t/2.swiftdeps +// +// Sequence numbers can vary +// RUN: sed -e 's/[0-9][0-9]*/N/g' -e 's/N, //g' -e '/^ *$/d' <%t/1.swiftdeps | sort >%t/1-processed.swiftdeps +// RUN: sed -e 's/[0-9][0-9]*/N/g' -e 's/N, //g' -e '/^ *$/d' <%t/2.swiftdeps | sort >%t/2-processed.swiftdeps + +// RUN: cmp -s %t/1-processed.swiftdeps %t/2-processed.swiftdeps diff --git a/test/NameBinding/reference-dependencies-consistency.swift b/test/NameBinding/reference-dependencies-consistency.swift index a9f72793c64a4..a1cb84b334fde 100644 --- a/test/NameBinding/reference-dependencies-consistency.swift +++ b/test/NameBinding/reference-dependencies-consistency.swift @@ -10,6 +10,6 @@ // RUN: echo 'fileprivate var v: String { return "\(x)" }; fileprivate let x = "a"' >%t/1.swift // RUN: echo 'fileprivate var v: String { return "\(x)" }; fileprivate let x = "a"' >%t/2.swift // -// RUN: %target-swift-frontend -typecheck -primary-file %t/1.swift -primary-file %t/2.swift -emit-reference-dependencies-path %t/1.swiftdeps -emit-reference-dependencies-path %t/2.swiftdeps +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -typecheck -primary-file %t/1.swift -primary-file %t/2.swift -emit-reference-dependencies-path %t/1.swiftdeps -emit-reference-dependencies-path %t/2.swiftdeps // // RUN: cmp -s %t/1.swiftdeps %t/2.swiftdeps diff --git a/test/NameBinding/reference-dependencies-dynamic-lookup-fine.swift b/test/NameBinding/reference-dependencies-dynamic-lookup-fine.swift new file mode 100644 index 0000000000000..59e9aae1a307e --- /dev/null +++ b/test/NameBinding/reference-dependencies-dynamic-lookup-fine.swift @@ -0,0 +1,73 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t.swiftdeps + +// Check that the output is deterministic. +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t-2.swiftdeps >%t-2-processed.swiftdeps +// RUN: diff %t-processed.swiftdeps %t-2-processed.swiftdeps + +// RUN: %FileCheck %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=NEGATIVE %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=DUPLICATE %s < %t-processed.swiftdeps + + +// REQUIRES: objc_interop + +import Foundation + +@objc @objcMembers class Base : NSObject { + // CHECK-DAG: dynamicLookup implementation '' foo true + // CHECK-DAG: dynamicLookup interface '' foo true + func foo() {} + + // CHECK-DAG: dynamicLookup implementation '' bar true + // CHECK-DAG: dynamicLookup interface '' bar true + + // DUPLICATE-NOT: dynamicLookup implementation '' bar true + // DUPLICATE: dynamicLookup implementation '' bar true + // DUPLICATE-NOT: dynamicLookup implementation '' bar true + // DUPLICATE-NOT: dynamicLookup interface '' bar true + // DUPLICATE: dynamicLookup interface '' bar true + // DUPLICATE-NOT: dynamicLookup interface '' bar true + func bar(_ x: Int, y: Int) {} + func bar(_ str: String) {} + + // CHECK-DAG: dynamicLookup implementation '' prop true + // CHECK-DAG: dynamicLookup interface '' prop true + var prop: String? + + // CHECK-DAG: dynamicLookup implementation '' unusedProp true + // CHECK-DAG: dynamicLookup interface '' unusedProp true + var unusedProp: Int = 0 + + + // CHECK-DAG: dynamicLookup implementation '' classFunc true + // CHECK-DAG: dynamicLookup interface '' classFunc true + class func classFunc() {} +} + +func getAnyObject() -> AnyObject? { return nil } + +func testDynamicLookup(_ obj: AnyObject) { + // CHECK-DAG: dynamicLookup interface '' description false + _ = obj.description + // CHECK-DAG: dynamicLookup interface '' method false + _ = obj.method(5, with: 5.0 as Double) + + // TODO: unable to resolve ambiguity + // C/HECK-DAG: dynamicLookup interface '' subscript false + // _ = obj[2] as Any + // _ = obj[2] as Any! +} + +// CHECK-DAG: dynamicLookup interface '' counter false +let globalUse = getAnyObject()?.counter + +// NEGATIVE-NOT: dynamicLookup interface '' cat1Method false +// NEGATIVE-NOT: dynamicLookup interface '' unusedProp false diff --git a/test/NameBinding/reference-dependencies-dynamic-lookup.swift b/test/NameBinding/reference-dependencies-dynamic-lookup.swift index f97092f48d631..f450e9f58506a 100644 --- a/test/NameBinding/reference-dependencies-dynamic-lookup.swift +++ b/test/NameBinding/reference-dependencies-dynamic-lookup.swift @@ -1,12 +1,12 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t.swiftdeps // RUN: %FileCheck %s < %t.swiftdeps // RUN: %FileCheck -check-prefix=NEGATIVE %s < %t.swiftdeps // RUN: %FileCheck -check-prefix=DUPLICATE %s < %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: diff %t.swiftdeps %t-2.swiftdeps // REQUIRES: objc_interop diff --git a/test/NameBinding/reference-dependencies-fine.swift b/test/NameBinding/reference-dependencies-fine.swift new file mode 100644 index 0000000000000..e15fbcf24e500 --- /dev/null +++ b/test/NameBinding/reference-dependencies-fine.swift @@ -0,0 +1,548 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift + +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// Check that the output is deterministic. +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps + +// Merge each entry onto one line and sort to overcome order differences +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t-2.swiftdeps >%t-2-processed.swiftdeps +// RUN: diff %t-processed.swiftdeps %t-2-processed.swiftdeps + +// RUN: %FileCheck -check-prefix=NEGATIVE %s < %t-processed.swiftdeps +// RUN: %FileCheck %s -check-prefix=CHECK-TOPLEVEL < %t-processed.swiftdeps +// RUN: %FileCheck %s -check-prefix=CHECK-MEMBER < %t-processed.swiftdeps +// RUN: %FileCheck %s -check-prefix=CHECK-NOMINAL < %t-processed.swiftdeps +// RUN: %FileCheck %s -check-prefix=CHECK-NOMINAL-2 < %t-processed.swiftdeps +// RUN: %FileCheck %s -check-prefix=CHECK-POTENTIALMEMBER < %t-processed.swiftdeps + + +// CHECK-TOPLEVEL-DAG: topLevel interface '' IntWrapper true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '==' true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '<' true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '***' true +// CHECK-TOPLEVEL-DAG: topLevel interface '' ^^^ true +// CHECK-TOPLEVEL-DAG: topLevel interface '' Subclass true +// CHECK-TOPLEVEL-DAG: topLevel interface '' MyArray true +// CHECK-TOPLEVEL-DAG: topLevel interface '' someGlobal true +// CHECK-TOPLEVEL-DAG: topLevel interface '' ExpressibleByExtraFloatLiteral true +// CHECK-TOPLEVEL-DAG: topLevel interface '' ThreeTilde true +// CHECK-TOPLEVEL-DAG: topLevel interface '' overloadedOnProto true +// CHECK-TOPLEVEL-DAG: topLevel interface '' FourTilde true +// CHECK-TOPLEVEL-DAG: topLevel interface '' FourTildeImpl true +// CHECK-TOPLEVEL-DAG: topLevel interface '' FiveTildeImpl true +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevelComputedProperty true +// CHECK-TOPLEVEL-DAG: topLevel interface '' lookUpManyTopLevelNames true +// CHECK-TOPLEVEL-DAG: topLevel interface '' testOperators true +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelForMemberLookup true +// CHECK-TOPLEVEL-DAG: topLevel interface '' lookUpMembers true +// CHECK-TOPLEVEL-DAG: topLevel interface '' publicUseOfMember true +// CHECK-TOPLEVEL-DAG: topLevel interface '' Outer true +// CHECK-TOPLEVEL-DAG: topLevel interface '' eof true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '~~~' true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '~~~~' true +// CHECK-TOPLEVEL-DAG: topLevel interface '' '~~~~~' true + +// CHECK-TOPLEVEL-DAG: topLevel implementation '' IntWrapper true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '==' true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '<' true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '***' true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' ^^^ true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' Subclass true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' MyArray true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' someGlobal true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' ExpressibleByExtraFloatLiteral true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' ThreeTilde true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' overloadedOnProto true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' FourTilde true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' FourTildeImpl true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' FiveTildeImpl true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' topLevelComputedProperty true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' lookUpManyTopLevelNames true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' testOperators true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' TopLevelForMemberLookup true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' lookUpMembers true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' publicUseOfMember true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' Outer true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' eof true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '~~~' true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '~~~~' true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' '~~~~~' true + + +// CHECK-NOMINAL-DAG: nominal interface 4main10IntWrapperV '' true +// CHECK-NOMINAL-DAG: nominal interface 4main10IntWrapperV16InnerForNoReasonV '' true +// CHECK-NOMINAL-DAG: nominal interface 4main8SubclassC '' true +// CHECK-NOMINAL-DAG: nominal interface Sb4mainE11InnerToBoolV '' true +// CHECK-NOMINAL-DAG: nominal interface 4main9Sentinel1V '' true +// CHECK-NOMINAL-DAG: nominal interface 4main9Sentinel2V '' true + +// CHECK-NOMINAL-DAG: nominal implementation 4main10IntWrapperV '' true +// CHECK-NOMINAL-DAG: nominal implementation 4main10IntWrapperV16InnerForNoReasonV '' true +// CHECK-NOMINAL-DAG: nominal implementation 4main8SubclassC '' true +// CHECK-NOMINAL-DAG: nominal implementation Sb4mainE11InnerToBoolV '' true +// CHECK-NOMINAL-DAG: nominal implementation 4main9Sentinel1V '' true +// CHECK-NOMINAL-DAG: nominal implementation 4main9Sentinel2V '' true + + +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main10IntWrapperV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main10IntWrapperV16InnerForNoReasonV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main8SubclassC '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface s25ExpressibleByArrayLiteralP '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface Sb '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface Sb4mainE11InnerToBoolV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main9Sentinel1V '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main9Sentinel2V '' true + +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation 4main10IntWrapperV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation 4main10IntWrapperV16InnerForNoReasonV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation 4main8SubclassC '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation s25ExpressibleByArrayLiteralP '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation Sb '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation Sb4mainE11InnerToBoolV '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation 4main9Sentinel1V '' true +// CHECK-POTENTIALMEMBER-DAG: potentialMember implementation 4main9Sentinel2V '' true + + +// CHECK-MEMBER-DAG: member interface s25ExpressibleByArrayLiteralP useless true +// CHECK-MEMBER-DAG: member interface s25ExpressibleByArrayLiteralP useless2 true +// CHECK-MEMBER-DAG: member interface Sb InnerToBool true +// CHECK-MEMBER-DAG: member interface {{.*[0-9]}}FourTildeImplV '~~~~' true +// CHECK-MEMBER-DAG: member interface {{.*[0-9]}}FiveTildeImplV '~~~~~' true + +// CHECK-MEMBER-DAG: member implementation s25ExpressibleByArrayLiteralP useless true +// CHECK-MEMBER-DAG: member implementation s25ExpressibleByArrayLiteralP useless2 true +// CHECK-MEMBER-DAG: member implementation Sb InnerToBool true +// CHECK-MEMBER-DAG: member implementation {{.*[0-9]}}FourTildeImplV '~~~~' true +// CHECK-MEMBER-DAG: member implementation {{.*[0-9]}}FiveTildeImplV '~~~~~' true + + +// CHECK-TOPLEVEL-DAG: topLevel interface '' Comparable false + +struct IntWrapper: Comparable { + // CHECK-TOPLEVEL-DAG: topLevel interface '' Int false + var value: Int + + struct InnerForNoReason {} + + // CHECK-TOPLEVEL-DAG: topLevel interface '' TypeReferencedOnlyBySubscript false + subscript(_: TypeReferencedOnlyBySubscript) -> Void { return () } + + // CHECK-TOPLEVEL-DAG: topLevel interface '' TypeReferencedOnlyByPrivateSubscript false + private subscript(_: TypeReferencedOnlyByPrivateSubscript) -> Void { return () } +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' Bool false +func ==(lhs: IntWrapper, rhs: IntWrapper) -> Bool { + return lhs.value == rhs.value +} + +func <(lhs: IntWrapper, rhs: IntWrapper) -> Bool { + return lhs.value < rhs.value +} + +// Test operator lookup without a use of the same operator. +// This is declared in the other file. +prefix func ***(lhs: IntWrapper) {} + +// This is provided as an operator but not implemented here. +prefix operator ^^^ + +// CHECK-TOPLEVEL-DAG: topLevel interface '' ClassFromOtherFile false +class Subclass : ClassFromOtherFile {} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' Array false +typealias MyArray = Array + +// CHECK-TOPLEVEL-DAG: topLevel interface '' ExpressibleByArrayLiteral false +extension ExpressibleByArrayLiteral { + func useless() {} +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' OtherFileElementType false +extension ExpressibleByArrayLiteral where ArrayLiteralElement == OtherFileElementType { + func useless2() {} +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' IntegerLiteralType false +let someGlobal = 42 + +extension Bool { + struct InnerToBool {} +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' ExpressibleByOtherFileAliasForFloatLiteral false +protocol ExpressibleByExtraFloatLiteral + : ExpressibleByOtherFileAliasForFloatLiteral { +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' ExpressibleByUnicodeScalarLiteral false +private protocol ExpressibleByExtraCharLiteral : ExpressibleByUnicodeScalarLiteral { +} + +prefix operator ~~~ +protocol ThreeTilde { + prefix static func ~~~(lhs: Self) +} + +private struct ThreeTildeTypeImpl : ThreeTilde { +} + +func overloadedOnProto(_: T) {} +func overloadedOnProto(_: T) {} + +private prefix func ~~~(_: ThreeTildeTypeImpl) {} + +prefix operator ~~~~ +protocol FourTilde { + prefix static func ~~~~(arg: Self) +} +struct FourTildeImpl : FourTilde {} +extension FourTildeImpl { + prefix static func ~~~~(arg: FourTildeImpl) {} +} + +// ~~~~~ is declared in the other file. +struct FiveTildeImpl {} +extension FiveTildeImpl { + prefix static func ~~~~~(arg: FiveTildeImpl) {} +} + +var topLevelComputedProperty: Bool { + return true +} + +func lookUpManyTopLevelNames() { + // CHECK-TOPLEVEL-DAG: topLevel interface '' Dictionary false + let _: Dictionary = [1:1] + + // CHECK-TOPLEVEL-DAG: topLevel interface '' UInt false + // CHECK-TOPLEVEL-DAG: topLevel interface '' '+' false + let _: UInt = [1, 2].reduce(0, +) + + // CHECK-TOPLEVEL-DAG: topLevel interface '' '-' false + let _: UInt = 3 - 2 - 1 + + // CHECK-TOPLEVEL-DAG: topLevel interface '' AliasFromOtherFile false + let _: AliasFromOtherFile = 1 + + // CHECK-TOPLEVEL-DAG: topLevel interface '' funcFromOtherFile false + funcFromOtherFile() + + // "CInt" is not used as a top-level name here. + // CHECK-TOPLEVEL-DAG: topLevel interface '' StringLiteralType false + // NEGATIVE-NOT: "CInt" + _ = "abc" + + // NEGATIVE-NOT: - "max" + print(Int.max) + + // NEGATIVE-NOT: - "Stride" + let _: Int.Stride = 0 + + // CHECK-TOPLEVEL-DAG: topLevel interface '' OtherFileOuterType false + _ = OtherFileOuterType.InnerType.sharedConstant + _ = OtherFileOuterType.InnerType() + + // CHECK-TOPLEVEL-DAG: topLevel interface '' OtherFileAliasForSecret false + _ = OtherFileAliasForSecret.constant + + // CHECK-TOPLEVEL-DAG: topLevel interface '' otherFileUse false + // CHECK-TOPLEVEL-DAG: topLevel interface '' otherFileGetImpl false + otherFileUse(otherFileGetImpl()) + + // CHECK-TOPLEVEL-DAG: topLevel interface '' otherFileUseGeneric false + // CHECK-TOPLEVEL-DAG: topLevel interface '' otherFileGetImpl2 false + otherFileUseGeneric(otherFileGetImpl2()) + + // CHECK-TOPLEVEL-DAG: topLevel interface '' getOtherFileIntArray false + for _ in getOtherFileIntArray() {} + + // CHECK-TOPLEVEL-DAG: topLevel interface '' getOtherFileEnum false + switch getOtherFileEnum() { + case .Value: + break + default: + break + } + + _ = .Value as OtherFileEnumWrapper.Enum + let _: OtherFileEnumWrapper.Enum = .Value + _ = OtherFileEnumWrapper.Enum.Value + + _ = { (_: PrivateTopLevelStruct.ValueType) -> PrivateTopLevelStruct2.ValueType? in + return nil + } + + typealias X = OtherFileEnumWrapper.Enum + + let value: Any = .Value as X + switch value { + case is OtherFileEnumWrapper.Enum: + break + default: + break + } + + // CHECK-TOPLEVEL-DAG: topLevel interface '' '~=' false + switch 42 { + case 50: + break + default: + break + } + + for _: OtherFileEnumWrapper.Enum in EmptyIterator() {} + + // CHECK-TOPLEVEL-DAG: topLevel interface '' otherFileGetNonImpl false + overloadedOnProto(otherFileGetNonImpl()) +} + +func testOperators(generic: T, specific: Flyswatter) { + // CHECK-TOPLEVEL-DAG: topLevel interface '' '****' false + // CHECK-TOPLEVEL-DAG: topLevel interface '' '*****' false + // CHECK-TOPLEVEL-DAG: topLevel interface '' '******' false + ****generic + generic*****0 + 0******generic + + ****specific + specific*****0 + 0******specific +} + +// CHECK-NOMINAL-DAG: nominal interface 4main23TopLevelForMemberLookupV '' true +// CHECK-NOMINAL-DAG: nominal implementation 4main23TopLevelForMemberLookupV '' true + +struct TopLevelForMemberLookup { + static func m1() {} + static func m2() {} + static func m3() {} +} + +func lookUpMembers() { + TopLevelForMemberLookup.m1() + TopLevelForMemberLookup.m3() +} +public let publicUseOfMember: () = TopLevelForMemberLookup.m2() + +struct Outer { + struct Inner { + func method() { + // CHECK-TOPLEVEL-DAG: topLevel interface '' CUnsignedInt false + let _: CUnsignedInt = 5 + } + } +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateFunc true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' privateFunc true +private func privateFunc() {} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel1 false +var use1 = topLevel1() +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel2 false +var use2 = { topLevel2() } +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel3 false +var use3 = { ({ topLevel3() })() } +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel4 false +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelProto1 false +struct Use4 : TopLevelProto1 { + var use4 = topLevel4() +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' '*' false +_ = 42 * 30 + +// FIXME: Incorrectly marked non-private dependencies +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel6 false +_ = topLevel6() +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel7 false +private var use7 = topLevel7() +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel8 false +var use8: Int = topLevel8() +// CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel9 false +var use9 = { () -> Int in return topLevel9() } + + +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelTy1 false +func useTy1(_ x: TopLevelTy1) {} +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelTy2 false +func useTy2() -> TopLevelTy2 {} +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelTy3 false +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelProto2 false +extension Use4 : TopLevelProto2 { + var useTy3: TopLevelTy3? { return nil } +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelStruct false +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelStruct2 false +let useTy4 = { (_: TopLevelStruct.ValueType) -> TopLevelStruct2.ValueType? in + return nil +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelStruct3 false +// CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelStruct4 false +typealias useTy5 = TopLevelStruct3.ValueType +let useTy6: TopLevelStruct4.ValueType = 0 + +struct StructForDeclaringProperties { + // CHECK-TOPLEVEL-DAG: topLevel interface '' TopLevelStruct5 false + var prop: TopLevelStruct5.ValueType { return 0 } +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateTopLevel1 false +func private1(_ a: Int = privateTopLevel1()) {} +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateTopLevel2 false +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateProto1 false +private struct Private2 : PrivateProto1 { + var private2 = privateTopLevel2() +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateTopLevel3 false +func outerPrivate3() { + let _ = { privateTopLevel3() } +} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateTopLevelTy1 false +private extension Use4 { + var privateTy1: PrivateTopLevelTy1? { return nil } +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateTopLevelTy2 false +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateProto2 false +extension Private2 : PrivateProto2 { + // FIXME: This test is supposed to check that we get this behavior /without/ + // marking the property private, just from the base type. + private var privateTy2: PrivateTopLevelTy2? { return nil } +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateTopLevelTy3 false +func outerPrivateTy3() { + func inner(_ a: PrivateTopLevelTy3?) {} + inner(nil) +} +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateTopLevelStruct3 false +private typealias PrivateTy4 = PrivateTopLevelStruct3.ValueType +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateTopLevelStruct4 false +private func privateTy5(_ x: PrivateTopLevelStruct4.ValueType) -> PrivateTopLevelStruct4.ValueType { + return x +} + +// Deliberately empty. +private struct PrivateTy6 {} +// CHECK-TOPLEVEL-DAG: topLevel interface '' PrivateProto3 false +extension PrivateTy6 : PrivateProto3 {} + +// CHECK-TOPLEVEL-DAG: topLevel interface '' ProtoReferencedOnlyInGeneric false +func genericTest(_: T) {} +// CHECK-TOPLEVEL-DAG: topLevel interface '' ProtoReferencedOnlyInPrivateGeneric false +private func privateGenericTest(_: T) {} + +struct PrivateStoredProperty { + // CHECK-TOPLEVEL-DAG: topLevel interface '' TypeReferencedOnlyByPrivateVar false + private var value: TypeReferencedOnlyByPrivateVar +} +class PrivateStoredPropertyRef { + // CHECK-TOPLEVEL-DAG: topLevel interface '' TypeReferencedOnlyByPrivateClassVar false + private var value: TypeReferencedOnlyByPrivateClassVar? +} + +struct Sentinel1 {} + +private protocol ExtensionProto {} +extension OtherFileTypeToBeExtended : ExtensionProto { + private func foo() {} +} +private extension OtherFileTypeToBeExtended { + var bar: Bool { return false } +} + +struct Sentinel2 {} + + + +// CHECK-MEMBER-DAG: member interface 4main10IntWrapperV Int false +// CHECK-MEMBER-DAG: member interface 4main10IntWrapperV deinit false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface SL '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main18ClassFromOtherFileC '' false +// CHECK-MEMBER-DAG: member interface Si max false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface s25ExpressibleByFloatLiteralP '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface s33ExpressibleByUnicodeScalarLiteralP '' false +// CHECK-MEMBER-DAG: member interface Sx Stride false +// CHECK-MEMBER-DAG: member interface Sa reduce false +// CHECK-MEMBER-DAG: member interface 4main17OtherFileIntArrayV deinit false +// CHECK-MEMBER-DAG: member interface 4main18OtherFileOuterTypeV InnerType false +// CHECK-MEMBER-DAG: member interface 4main18OtherFileOuterTypeV05InnerE0V init false +// CHECK-MEMBER-DAG: member interface 4main18OtherFileOuterTypeV05InnerE0V sharedConstant false +// CHECK-MEMBER-DAG: member interface 4main26OtherFileSecretTypeWrapperV0dE0V constant false +// CHECK-MEMBER-DAG: member interface 4main25OtherFileProtoImplementorV deinit false +// CHECK-MEMBER-DAG: member interface 4main26OtherFileProtoImplementor2V deinit false +// CHECK-MEMBER-DAG: member interface s15EmptyCollectionV8IteratorV init false +// CHECK-MEMBER-DAG: member interface 4main13OtherFileEnumO Value false +// CHECK-MEMBER-DAG: member interface 4main20OtherFileEnumWrapperV Enum false + +// CHECK-MEMBER-DAG: member interface 4main14TopLevelStructV ValueType false +// CHECK-MEMBER-DAG: member interface 4main15TopLevelStruct2V ValueType false +// CHECK-MEMBER-DAG: member interface 4main15TopLevelStruct3V ValueType false +// CHECK-MEMBER-DAG: member interface 4main15TopLevelStruct4V ValueType false +// CHECK-MEMBER-DAG: member interface 4main15TopLevelStruct5V ValueType false +// CHECK-MEMBER-DAG: member interface 4main21PrivateTopLevelStructV ValueType false +// CHECK-MEMBER-DAG: member interface 4main22PrivateTopLevelStruct2V ValueType false +// CHECK-MEMBER-DAG: member interface 4main22PrivateTopLevelStruct3V ValueType false +// CHECK-MEMBER-DAG: member interface 4main22PrivateTopLevelStruct4V ValueType false + +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main14TopLevelProto1P '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main14TopLevelProto2P '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main13PrivateProto1P '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main13PrivateProto2P '' false +// CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main13PrivateProto3P '' false + +// CHECK-NOMINAL-2-DAG: nominal interface Sa '' false +// CHECK-NOMINAL-2-DAG: nominal interface Sb '' true +// CHECK-NOMINAL-2-DAG: nominal implementation Sb '' true +// CHECK-NOMINAL-2-DAG: nominal interface 4main18ClassFromOtherFileC '' false +// CHECK-NOMINAL-2-DAG: nominal interface SL '' false +// CHECK-NOMINAL-2-DAG: nominal interface s25ExpressibleByFloatLiteralP '' false +// CHECK-NOMINAL-2-DAG: nominal interface s33ExpressibleByUnicodeScalarLiteralP '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main18OtherFileOuterTypeV05InnerE0V '' false +// CHECK-NOMINAL-2-DAG: nominal interface Si '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main13OtherFileEnumO '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main20OtherFileEnumWrapperV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main17OtherFileIntArrayV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main18OtherFileOuterTypeV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main25OtherFileProtoImplementorV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main26OtherFileProtoImplementor2V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main13PrivateProto1P '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main13PrivateProto2P '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main13PrivateProto3P '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main21PrivateTopLevelStructV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main22PrivateTopLevelStruct2V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main22PrivateTopLevelStruct3V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main22PrivateTopLevelStruct4V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main26OtherFileSecretTypeWrapperV0dE0V '' false +// CHECK-NOMINAL-2-DAG: nominal interface Sx '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main14TopLevelProto1P '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main14TopLevelProto2P '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main14TopLevelStructV '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main15TopLevelStruct2V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main15TopLevelStruct3V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main15TopLevelStruct4V '' false +// CHECK-NOMINAL-2-DAG: nominal interface 4main15TopLevelStruct5V '' false + +// String is not used anywhere in this file, though a string literal is. +// NEGATIVE-NOT: "String" +// These are used by the other file in this module, but not by this one. +// NEGATIVE-NOT: "ExpressibleByFloatLiteral" +// NEGATIVE-NOT: "Int16" +// NEGATIVE-NOT: "OtherFileProto" +// NEGATIVE-NOT: "OtherFileProtoImplementor" +// NEGATIVE-NOT: "OtherFileProto2" +// NEGATIVE-NOT: "OtherFileProtoImplementor2" + +// OtherFileSecretTypeWrapper is never used directly in this file. +// NEGATIVE-NOT: "OtherFileSecretTypeWrapper" +// NEGATIVE-NOT: "4main26OtherFileSecretTypeWrapperV" + +let eof: () = () diff --git a/test/NameBinding/reference-dependencies-members-fine.swift b/test/NameBinding/reference-dependencies-members-fine.swift new file mode 100644 index 0000000000000..bbb99fbfb3e98 --- /dev/null +++ b/test/NameBinding/reference-dependencies-members-fine.swift @@ -0,0 +1,69 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift + +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps +// RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t-2.swiftdeps >%t-2-processed.swiftdeps + +// RUN: diff %t-processed.swiftdeps %t-2-processed.swiftdeps + +// RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL-2 %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=PROVIDES-MEMBER %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=PROVIDES-MEMBER-NEGATIVE %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=DEPENDS-NOMINAL %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=DEPENDS-MEMBER %s < %t-processed.swiftdeps +// RUN: %FileCheck -check-prefix=DEPENDS-MEMBER-NEGATIVE %s < %t-processed.swiftdeps + + +// PROVIDES-NOMINAL-DAG: nominal implementation 4main4BaseC '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main4BaseC '' true +class Base { + // PROVIDES-MEMBER-DAG: potentialMember implementation 4main4BaseC '' true + // PROVIDES-MEMBER-DAG: potentialMember interface 4main4BaseC '' true + // PROVIDES-MEMBER-NEGATIVE-NOT: member {{.*}} 4main4BaseC {{[^']]+}} true + func foo() {} +} + +// PROVIDES-NOMINAL-DAG: nominal implementation 4main3SubC '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main3SubC '' true +// DEPENDS-NOMINAL-DAG: nominal interface 4main9OtherBaseC '' false +class Sub : OtherBase { + // PROVIDES-MEMBER-DAG: potentialMember implementation 4main3SubC '' true + // PROVIDES-MEMBER-NEGATIVE-NOT: {{potentialM|m}}}}ember implementation 4main3SubC {{.+}} true + // DEPENDS-MEMBER-DAG: potentialMember interface 4main9OtherBaseC '' false + // DEPENDS-MEMBER-DAG: member interface 4main9OtherBaseC foo false + // DEPENDS-MEMBER-DAG: member interface 4main9OtherBaseC init false + func foo() {} +} + +// PROVIDES-NOMINAL-DAG: nominal implementation 4main9SomeProtoP '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main9SomeProtoP '' true +// PROVIDES-MEMBER-DAG: potentialMember interface 4main9SomeProtoP '' true +protocol SomeProto {} + +// PROVIDES-NOMINAL-DAG: nominal implementation 4main10OtherClassC '' true +// PROVIDES-NOMINAL-2-DAG: nominal interface 4main10OtherClassC '' true +// PROVIDES-MEMBER-DAG: potentialMember interface 4main10OtherClassC '' true +// DEPENDS-MEMBER-DAG: member interface 4main10OtherClassC deinit false +extension OtherClass : SomeProto {} + +// PROVIDES-NOMINAL-DAG: nominal implementation 4main11OtherStructV '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main11OtherStructV '' true +extension OtherStruct { + // PROVIDES-MEMBER-DAG: potentialMember interface 4main11OtherStructV '' true + // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV foo true + // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV bar true + // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV baz true + // DEPENDS-MEMBER-NEGATIVE-NOT:: potentialMember interface 4main11OtherStructV baz false + func foo() {} + var bar: () { return () } + private func baz() {} +} diff --git a/test/NameBinding/reference-dependencies-members.swift b/test/NameBinding/reference-dependencies-members.swift index 7621fb9227521..397d4d465de12 100644 --- a/test/NameBinding/reference-dependencies-members.swift +++ b/test/NameBinding/reference-dependencies-members.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: diff %t.swiftdeps %t-2.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL %s < %t.swiftdeps diff --git a/test/NameBinding/reference-dependencies.swift b/test/NameBinding/reference-dependencies.swift index 5c61d42ef1314..9bde45099b87e 100644 --- a/test/NameBinding/reference-dependencies.swift +++ b/test/NameBinding/reference-dependencies.swift @@ -1,11 +1,10 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps -// RUN: %FileCheck %s < %t.swiftdeps +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // RUN: %FileCheck -check-prefix=NEGATIVE %s < %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -disable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: diff %t.swiftdeps %t-2.swiftdeps // CHECK-LABEL: {{^provides-top-level:$}} diff --git a/test/Parse/ConditionalCompilation/basicParseErrors.swift b/test/Parse/ConditionalCompilation/basicParseErrors.swift index f68a7c9849d65..9d0616bb0b925 100644 --- a/test/Parse/ConditionalCompilation/basicParseErrors.swift +++ b/test/Parse/ConditionalCompilation/basicParseErrors.swift @@ -148,7 +148,7 @@ undefinedFunc() // expected-error {{use of unresolved identifier 'undefinedFunc' #if _endian(arm64) // expected-warning {{unknown endianness for build configuration '_endian'}} expected-note {{did you mean 'arch'}} {{5-12=arch}} #endif -#if targetEnvironment(_ObjC) // expected-warning {{unknown target environment for build configuration 'targetEnvironment'}} expected-note {{did you mean 'simulator'}} {{23-28=simulator}} +#if targetEnvironment(_ObjC) // expected-warning {{unknown target environment for build configuration 'targetEnvironment'}} expected-note {{did you mean 'macabi'}} {{23-28=macabi}} #endif #if os(iOS) || os(simulator) // expected-warning {{unknown operating system for build configuration 'os'}} expected-note {{did you mean 'targetEnvironment'}} {{16-18=targetEnvironment}} diff --git a/test/Parse/ConditionalCompilation/macabiTargetEnv.swift b/test/Parse/ConditionalCompilation/macabiTargetEnv.swift new file mode 100644 index 0000000000000..f1990b3eb633f --- /dev/null +++ b/test/Parse/ConditionalCompilation/macabiTargetEnv.swift @@ -0,0 +1,36 @@ +// RUN: %swift -swift-version 4 -typecheck %s -verify -target x86_64-apple-ios12.0-macabi -parse-stdlib +// RUN: %swift-ide-test -swift-version 4 -test-input-complete -source-filename=%s -target x86_64-apple-ios12.0-macabi + +// REQUIRES: OS=maccatalyst + +#if targetEnvironment(macabi) // expected-warning {{'macabi' has been renamed to 'macCatalyst'}} {{23-29=macCatalyst}} +func underMacABI() { + foo() // expected-error {{use of unresolved identifier 'foo'}} +} +#endif + +#if !targetEnvironment(macabi) // expected-warning {{'macabi' has been renamed to 'macCatalyst'}} {{24-30=macCatalyst}} +// This block does not typecheck but the #if prevents it from +// from being a compiler error. +let i: SomeType = "SomeString" // no-error +#endif + +#if targetEnvironment(macCatalyst) +func underTargetEnvironmentMacCatalyst() { + foo() // expected-error {{use of unresolved identifier 'foo'}} +} +#endif + +// Make sure we don't treat the macabi environment as a simulator. +#if targetEnvironment(simulator) +// This block does not typecheck but the #if prevents it from +// from being a compiler error. +let i: SomeType = "SomeString" // no-error +#endif + +#if os(macCatalyst) +// expected-warning@-1 {{unknown operating system for build configuration 'os'}} +// expected-note@-2 *{{did you mean}} +func underOSMacCatalyst() { +} +#endif diff --git a/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift b/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift index c6d53e1e3d7b7..92142ec756f4d 100644 --- a/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift +++ b/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift @@ -11,8 +11,7 @@ func foo() {} struct B {} -// If '#if' contains active non-decls, we don't support forward reference. -typealias C = D // expected-error {{use of undeclared type 'D'}} +typealias C = D #if true print("ok") diff --git a/test/Parse/ConditionalCompilation/switch_case.swift b/test/Parse/ConditionalCompilation/switch_case.swift index 822fa94f99674..941de52907719 100644 --- a/test/Parse/ConditionalCompilation/switch_case.swift +++ b/test/Parse/ConditionalCompilation/switch_case.swift @@ -209,6 +209,7 @@ func foo(x: E, intVal: Int) { fallthrough // expected-error {{'fallthrough' from a case which doesn't bind variable 'val'}} #if ENABLE_C case let val: + _ = val break #endif case 2: diff --git a/test/Parse/ConditionalCompilation/wasm32Target.swift b/test/Parse/ConditionalCompilation/wasm32Target.swift new file mode 100644 index 0000000000000..96e173ee4259f --- /dev/null +++ b/test/Parse/ConditionalCompilation/wasm32Target.swift @@ -0,0 +1,8 @@ +// RUN: %swift -typecheck %s -verify -target wasm32-unknown-wasi -disable-objc-interop -parse-stdlib +// RUN: %swift-ide-test -test-input-complete -source-filename %s -target wasm32-unknown-wasi + +#if arch(wasm32) && os(WASI) && _runtime(_Native) && _endian(little) +class C {} +var x = C() +#endif +var y = x diff --git a/test/Parse/attr_available_ignored.swift b/test/Parse/attr_available_ignored.swift deleted file mode 100644 index 1d1336bbff324..0000000000000 --- a/test/Parse/attr_available_ignored.swift +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -@available(*, deprecated: 0.1) // expected-warning {{unexpected version number in 'available' attribute for non-specific platform '*'}} -public typealias PublicAlias = Int -public var value1: PublicAlias { return 1 } - -@available(*, deprecated: 0.1) // expected-warning {{unexpected version number in 'available' attribute for non-specific platform '*'}} -@available(iOS, deprecated: 99.9) -private typealias PrivateAlias = Int // expected-note {{type declared here}} -public var value2: PrivateAlias { return 1 } // expected-error {{variable cannot be declared public because its type uses a private type}} diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 429b255b7d94a..188634808dfac 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -47,3 +47,11 @@ if true { func genericFunc(_ t: T) -> T { return t } let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be formed from a reference to a generic function}} + +func ct1() -> () { print("") } + +let ct1ref0 : @convention(c, cType: "void (*)(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void (*)(void)") = ct1 // expected-error{{expected type}} +let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} +let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} +let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/Parse/composition_type_range.swift b/test/Parse/composition_type_range.swift deleted file mode 100644 index c93f46b4da6d1..0000000000000 --- a/test/Parse/composition_type_range.swift +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: not %target-swift-frontend -dump-ast %s 2>&1 | %FileCheck %s - -typealias A = B & protocol -// CHECK: (typealias range=[{{.+}}.swift:[[@LINE-1]]:1 - line:[[@LINE-1]]:32] "A" {{.*}}) diff --git a/test/Parse/confusables.swift b/test/Parse/confusables.swift index e244670868824..14a72ad943034 100644 --- a/test/Parse/confusables.swift +++ b/test/Parse/confusables.swift @@ -19,7 +19,5 @@ if (true ꝸꝸꝸ false) {} // expected-note {{identifier 'ꝸꝸꝸ' contains // expected-error @+2 {{expected ',' separator}} // expected-error @+1 {{type '(Int, Int)' cannot conform to 'BinaryInteger'; only struct/enum/class types can conform to protocols}} if (5 ‒ 5) == 0 {} // expected-note {{unicode character '‒' looks similar to '-'; did you mean to use '-'?}} {{7-10=-}} -// expected-note @-1 {{required by referencing operator function '==' on 'BinaryInteger' where}} +// expected-note @-1 {{required by referencing operator function '==' on 'BinaryInteger' where 'Self' = '(Int, Int)'}} -// FIXME: rdar://56002633 -// Above note should read "required by referencing operator function '==' on 'BinaryInteger' where 'Self' = '(Int, Int)'" diff --git a/test/Parse/enum.swift b/test/Parse/enum.swift index 00957acb46531..3a51bfaa37901 100644 --- a/test/Parse/enum.swift +++ b/test/Parse/enum.swift @@ -130,7 +130,7 @@ enum Recovery6 { case Tusk, // expected-error {{expected identifier after comma in enum 'case' declaration}} } -enum RawTypeEmpty : Int {} // expected-error {{an enum with no cases cannot declare a raw type}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeEmpty : Int {} // expected-error {{an enum with no cases cannot declare a raw type}} // expected-error@-1{{'RawTypeEmpty' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} enum Raw : Int { @@ -146,7 +146,7 @@ enum RawTypeNotFirst : RawTypeNotFirstProtocol, Int { // expected-error {{raw ty case E } -enum ExpressibleByRawTypeNotLiteral : Array { // expected-error {{raw type 'Array' is not expressible by a string, integer, or floating-point literal}} expected-note {{do you want to add protocol stubs?}} +enum ExpressibleByRawTypeNotLiteral : Array { // expected-error {{raw type 'Array' is not expressible by a string, integer, or floating-point literal}} // expected-error@-1{{'ExpressibleByRawTypeNotLiteral' declares raw type 'Array', but does not conform to RawRepresentable and conformance could not be synthesized}} case Ladd, Elliott, Sixteenth, Harrison } @@ -170,7 +170,7 @@ enum RawTypeCircularityB : RawTypeCircularityA, ExpressibleByIntegerLiteral { // struct ExpressibleByFloatLiteralOnly : ExpressibleByFloatLiteral { init(floatLiteral: Double) {} } -enum ExpressibleByRawTypeNotIntegerLiteral : ExpressibleByFloatLiteralOnly { // expected-error {{'ExpressibleByRawTypeNotIntegerLiteral' declares raw type 'ExpressibleByFloatLiteralOnly', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-error {{RawRepresentable conformance cannot be synthesized because raw type 'ExpressibleByFloatLiteralOnly' is not Equatable}} expected-note {{do you want to add protocol stubs?}} +enum ExpressibleByRawTypeNotIntegerLiteral : ExpressibleByFloatLiteralOnly { // expected-error {{'ExpressibleByRawTypeNotIntegerLiteral' declares raw type 'ExpressibleByFloatLiteralOnly', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-error {{RawRepresentable conformance cannot be synthesized because raw type 'ExpressibleByFloatLiteralOnly' is not Equatable}} case Everett // expected-error {{enum cases require explicit raw values when the raw type is not expressible by integer or string literal}} case Flanders } @@ -184,13 +184,13 @@ enum RawTypeWithNegativeValues : Int { case AutoIncAcrossZero = -1, Zero, One } -enum RawTypeWithUnicodeScalarValues : UnicodeScalar { // expected-error {{'RawTypeWithUnicodeScalarValues' declares raw type 'UnicodeScalar' (aka 'Unicode.Scalar'), but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeWithUnicodeScalarValues : UnicodeScalar { // expected-error {{'RawTypeWithUnicodeScalarValues' declares raw type 'UnicodeScalar' (aka 'Unicode.Scalar'), but does not conform to RawRepresentable and conformance could not be synthesized}} case Kearney = "K" case Lovejoy // expected-error {{enum cases require explicit raw values when the raw type is not expressible by integer or string literal}} case Marshall = "M" } -enum RawTypeWithCharacterValues : Character { // expected-error {{'RawTypeWithCharacterValues' declares raw type 'Character', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeWithCharacterValues : Character { // expected-error {{'RawTypeWithCharacterValues' declares raw type 'Character', but does not conform to RawRepresentable and conformance could not be synthesized}} case First = "い" case Second // expected-error {{enum cases require explicit raw values when the raw type is not expressible by integer or string literal}} case Third = "は" @@ -203,11 +203,11 @@ enum RawTypeWithCharacterValues_Correct : Character { case Fourth = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}" // ok } -enum RawTypeWithCharacterValues_Error1 : Character { // expected-error {{'RawTypeWithCharacterValues_Error1' declares raw type 'Character', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeWithCharacterValues_Error1 : Character { // expected-error {{'RawTypeWithCharacterValues_Error1' declares raw type 'Character', but does not conform to RawRepresentable and conformance could not be synthesized}} case First = "abc" // expected-error {{cannot convert value of type 'String' to raw type 'Character'}} } -enum RawTypeWithFloatValues : Float { // expected-error {{'RawTypeWithFloatValues' declares raw type 'Float', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeWithFloatValues : Float { // expected-error {{'RawTypeWithFloatValues' declares raw type 'Float', but does not conform to RawRepresentable and conformance could not be synthesized}} case Northrup = 1.5 case Overton // expected-error {{enum case must declare a raw value when the preceding raw value is not an integer}} case Pettygrove = 2.25 @@ -318,12 +318,12 @@ enum NonliteralRawValue : Int { case Yeon = 100 + 20 + 3 // expected-error {{raw value for enum case must be a literal}} } -enum RawTypeWithPayload : Int { // expected-error {{'RawTypeWithPayload' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{declared raw type 'Int' here}} expected-note {{declared raw type 'Int' here}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeWithPayload : Int { // expected-error {{'RawTypeWithPayload' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{declared raw type 'Int' here}} expected-note {{declared raw type 'Int' here}} case Powell(Int) // expected-error {{enum with raw type cannot have cases with arguments}} case Terwilliger(Int) = 17 // expected-error {{enum with raw type cannot have cases with arguments}} } -enum RawTypeMismatch : Int { // expected-error {{'RawTypeMismatch' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum RawTypeMismatch : Int { // expected-error {{'RawTypeMismatch' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} case Barbur = "foo" // expected-error {{}} } @@ -343,12 +343,12 @@ enum DuplicateMembers3 { case Foo(Int) // expected-error {{invalid redeclaration of 'Foo'}} } -enum DuplicateMembers4 : Int { // expected-error {{'DuplicateMembers4' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum DuplicateMembers4 : Int { // expected-error {{'DuplicateMembers4' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} case Foo = 1 // expected-note {{'Foo' previously declared here}} case Foo = 2 // expected-error {{invalid redeclaration of 'Foo'}} } -enum DuplicateMembers5 : Int { // expected-error {{'DuplicateMembers5' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum DuplicateMembers5 : Int { // expected-error {{'DuplicateMembers5' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} case Foo = 1 // expected-note {{'Foo' previously declared here}} case Foo = 1 + 1 // expected-error {{invalid redeclaration of 'Foo'}} expected-error {{raw value for enum case must be a literal}} } @@ -359,7 +359,7 @@ enum DuplicateMembers6 { case Foo // expected-error {{invalid redeclaration of 'Foo'}} } -enum DuplicateMembers7 : String { // expected-error {{'DuplicateMembers7' declares raw type 'String', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum DuplicateMembers7 : String { // expected-error {{'DuplicateMembers7' declares raw type 'String', but does not conform to RawRepresentable and conformance could not be synthesized}} case Foo // expected-note {{'Foo' previously declared here}} case Foo = "Bar" // expected-error {{invalid redeclaration of 'Foo'}} } @@ -411,7 +411,7 @@ enum ManyLiteralA : ManyLiteralable { case B = 0 // expected-error {{raw value for enum case is not unique}} } -enum ManyLiteralB : ManyLiteralable { // expected-error {{'ManyLiteralB' declares raw type 'ManyLiteralable', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum ManyLiteralB : ManyLiteralable { // expected-error {{'ManyLiteralB' declares raw type 'ManyLiteralable', but does not conform to RawRepresentable and conformance could not be synthesized}} case A = "abc" case B // expected-error {{enum case must declare a raw value when the preceding raw value is not an integer}} } @@ -440,7 +440,7 @@ enum RawValueBTest: Double, RawValueB { case A, B } -enum foo : String { // expected-error {{'foo' declares raw type 'String', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum foo : String { // expected-error {{'foo' declares raw type 'String', but does not conform to RawRepresentable and conformance could not be synthesized}} case bar = nil // expected-error {{cannot convert 'nil' to raw type 'String'}} } @@ -549,8 +549,8 @@ enum switch {} // expected-error {{keyword 'switch' cannot be used as an identif enum SE0155 { case emptyArgs() // expected-warning {{enum element with associated values must have at least one associated value}} - // expected-note@-1 {{did you mean to remove the empty associated value list?}} {{17-18=}} - // expected-note@-2 {{did you mean to explicitly add a 'Void' associated value?}} {{17-17=Void}} + // expected-note@-1 {{did you mean to remove the empty associated value list?}} {{17-19=}} + // expected-note@-2 {{did you mean to explicitly add a 'Void' associated value?}} {{18-18=Void}} } // SR-11261 diff --git a/test/Parse/errors.swift b/test/Parse/errors.swift index a22d1185e7152..6e89a596a5591 100644 --- a/test/Parse/errors.swift +++ b/test/Parse/errors.swift @@ -127,3 +127,9 @@ func fixitThrow2() throws { } let fn: () -> throws Void // expected-error{{'throws' may only occur before '->'}} {{12-12=throws }} {{15-22=}} + +// SR-11574 +func fixitTry0(a: T) try where T:ExpressibleByStringLiteral {} // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{25-28=throws}} +func fixitTry1(a: T) try {} // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{25-28=throws}} +func fixitTry2() try {} // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{18-21=throws}} +let fixitTry3 : () try -> Int // expected-error{{expected throwing specifier; did you mean 'throws'?}} {{20-23=throws}} diff --git a/test/Parse/function_type_range.swift b/test/Parse/function_type_range.swift deleted file mode 100644 index 35481e7202ca6..0000000000000 --- a/test/Parse/function_type_range.swift +++ /dev/null @@ -1,6 +0,0 @@ -// Ensure that source range for the type does not go past the end of the buffer. -// RUN: %target-swift-frontend -typecheck %s -dump-ast -typealias Alias = - - -() -> () \ No newline at end of file diff --git a/test/Parse/identifiers.swift b/test/Parse/identifiers.swift index 264b6243a6586..b0ad8fe398c50 100644 --- a/test/Parse/identifiers.swift +++ b/test/Parse/identifiers.swift @@ -26,7 +26,7 @@ func s̈pin̈al_tap̈() {} () // expected-error{{invalid character in source file}} {{1-4= }} // Placeholders are recognized as identifiers but with error. -func <#some name#>() {} // expected-error 2 {{editor placeholder in source file}} +func <#some name#>() {} // expected-error {{editor placeholder in source file}} // Keywords as identifiers class switch {} // expected-error {{keyword 'switch' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{7-13=`switch`}} diff --git a/test/Parse/implicit_getter_incomplete.swift b/test/Parse/implicit_getter_incomplete.swift index 54fc4ff874e90..aa8d025382efa 100644 --- a/test/Parse/implicit_getter_incomplete.swift +++ b/test/Parse/implicit_getter_incomplete.swift @@ -12,7 +12,7 @@ func test1() { // Would trigger assertion when AST verifier checks source ranges ("child source range not contained within its parent") func test2() { // expected-note {{match}} - var a : Int { // expected-note {{match}} - switch i { // expected-error {{unresolved identifier}} expected-error{{'switch' statement body must have at least one 'case'}} + var a : Int { // expected-note {{match}} expected-note {{'a' declared here}} + switch i { // expected-error {{use of unresolved identifier 'i'; did you mean 'a'}} expected-error{{'switch' statement body must have at least one 'case'}} } // expected-error@+1 2 {{expected '}'}} diff --git a/test/Parse/invalid-utf8.swift b/test/Parse/invalid-utf8.swift index b76f764a5b65e..552328da8133f 100644 --- a/test/Parse/invalid-utf8.swift +++ b/test/Parse/invalid-utf8.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -var x = "" // expected-error 2 {{invalid UTF-8 found in source file}} {{5-6= }} +var x = "" // expected-error{{invalid UTF-8 found in source file}} {{5-6= }} // Make sure we don't stop processing the whole file. static func foo() {} // expected-error{{static methods may only be declared on a type}} {{1-8=}} diff --git a/test/Parse/invalid.swift b/test/Parse/invalid.swift index c4607ac6e6ef2..42874cddef97f 100644 --- a/test/Parse/invalid.swift +++ b/test/Parse/invalid.swift @@ -76,7 +76,7 @@ protocol Animal { // expected-error {{protocols do not allow generic para // SR-573 - Crash with invalid parameter declaration class Starfish {} struct Salmon {} -func f573(s Starfish, // expected-error {{parameter requires an explicit type}} +func f573(s Starfish, // expected-error {{expected ':' following argument label and parameter name}} _ ss: Salmon) -> [Int] {} func g573() { f573(Starfish(), Salmon()) } @@ -84,12 +84,12 @@ func SR698(_ a: Int, b: Int) {} SR698(1, b: 2,) // expected-error {{unexpected ',' separator}} // SR-979 - Two inout crash compiler -func SR979a(a : inout inout Int) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{23-29=}} +func SR979a(a : inout inout Int) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{17-23=}} func SR979b(inout inout b: Int) {} // expected-error {{inout' before a parameter name is not allowed, place it before the parameter type instead}} {{13-18=}} {{28-28=inout }} // expected-error@-1 {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{19-25=}} func SR979d(let let a: Int) {} // expected-warning {{'let' in this position is interpreted as an argument label}} {{13-16=`let`}} // expected-error @-1 {{expected ',' separator}} {{20-20=,}} -// expected-error @-2 {{parameter requires an explicit type}} +// expected-error @-2 {{expected ':' following argument label and parameter name}} // expected-warning @-3 {{extraneous duplicate parameter name; 'let' already has an argument label}} {{13-17=}} func SR979e(inout x: inout String) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{13-18=}} func SR979g(inout i: inout Int) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{13-18=}} @@ -124,7 +124,7 @@ prefix func %(x: T) -> T { return x } // No error expected - the < is conside struct Weak { // expected-error {{'class' constraint can only appear on protocol declarations}} // expected-note@-1 {{did you mean to write an 'AnyObject' constraint?}} {{16-21=AnyObject}} - weak let value: T // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} expected-error {{'weak' variable should have optional type 'T?'}} + weak let value: T // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} expected-error {{'weak' variable should have optional type 'T?'}} expected-error {{'weak' must not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound}} } let x: () = () @@ -145,6 +145,3 @@ enum sr8202_enum<@indirect T> {} // expected-error {{'indirect' is a declaration protocol P { @available(swift, introduced: 4.2) associatedtype Assoc // expected-error {{'@availability' attribute cannot be applied to this declaration}} } - -struct genericParamIncomplete1<@> {} // expected-error {{expected an attribute name}} expected-error {{expected an identifier to name generic parameter}} -struct genericParamIncomplete2<@objc> {} // expected-error {{expected an identifier to name generic parameter}} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index d32ab35273643..0448390a53a97 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -288,8 +288,7 @@ case (_, var e, 3) +++ (1, 2, 3): // expected-error@-2{{'var' binding pattern cannot appear in an expression}} () case (let (_, _, _)) + 1: -// expected-error@-1 2 {{'var' binding pattern cannot appear in an expression}} -// expected-error@-2 {{expression pattern of type 'Int' cannot match values of type '(Int, Int, Int)'}} +// expected-error@-1 {{expression pattern of type 'Int' cannot match values of type '(Int, Int, Int)'}} () } @@ -333,6 +332,6 @@ case (_?)?: break // expected-warning {{case is already handled by previous patt let (responseObject: Int?) = op1 // expected-error @-1 {{expected ',' separator}} {{25-25=,}} // expected-error @-2 {{expected pattern}} -// expected-error @-3 {{expression type 'Int?' is ambiguous without more context}} +// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}} diff --git a/test/Parse/metatype_object_conversion.swift b/test/Parse/metatype_object_conversion.swift index 96f27402dc86f..2dc87ff1e87bb 100644 --- a/test/Parse/metatype_object_conversion.swift +++ b/test/Parse/metatype_object_conversion.swift @@ -12,15 +12,14 @@ func takesAnyObject(_ x: AnyObject) {} func concreteTypes() { takesAnyObject(C.self) - // TODO: Better error messages - takesAnyObject(S.self) // expected-error{{argument type 'S.Type' does not conform to expected type 'AnyObject'}} - takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' does not conform to expected type 'AnyObject'}} + takesAnyObject(S.self) // expected-error{{argument type 'S.Type' expected to be an instance of a class or class-constrained type}} + takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' expected to be an instance of a class or class-constrained type}} } func existentialMetatypes(nonClass: NonClassProto.Type, classConstrained: ClassConstrainedProto.Type, compo: (NonClassProto & ClassConstrainedProto).Type) { - takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' does not conform to expected type 'AnyObject'}} + takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' expected to be an instance of a class or class-constrained type}} takesAnyObject(classConstrained) takesAnyObject(compo) } diff --git a/test/Parse/objc_enum.swift b/test/Parse/objc_enum.swift index 7aff95e07d728..ee8da9a8cbc39 100644 --- a/test/Parse/objc_enum.swift +++ b/test/Parse/objc_enum.swift @@ -30,7 +30,7 @@ class Bar { } // @objc enums with payloads rejected with no source location info -@objc enum r23681566 : Int32 { // expected-error {{'r23681566' declares raw type 'Int32', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{declared raw type 'Int32' here}} expected-note {{do you want to add protocol stubs?}} +@objc enum r23681566 : Int32 { // expected-error {{'r23681566' declares raw type 'Int32', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{declared raw type 'Int32' here}} case Foo(progress: Int) // expected-error {{enum with raw type cannot have cases with arguments}} } diff --git a/test/Parse/original_defined_in_attr.swift b/test/Parse/original_defined_in_attr.swift new file mode 100644 index 0000000000000..56604c02ce9d9 --- /dev/null +++ b/test/Parse/original_defined_in_attr.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx + +@_originallyDefinedIn(module: "foo", OSX 13.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} +func foo() {} + +@_originallyDefinedIn(modulename: "foo", OSX 13.13) // expected-error {{expected 'module: "original"' in the first argument to @_originallyDefinedIn}} +func foo1() {} + +@_originallyDefinedIn(module: "foo", OSX 13.13.3) // expected-warning {{@_originallyDefinedIn only uses major and minor version number}} expected-error {{need @available attribute for @_originallyDefinedIn}} +class ToplevelClass {} + +@_originallyDefinedIn(module: "foo") // expected-error {{expected at least one platform version in @_originallyDefinedIn}} +class ToplevelClass1 {} + +@_originallyDefinedIn(OSX 13.13.3) // expected-error {{expected 'module: "original"' in the first argument to @_originallyDefinedIn}} +class ToplevelClass2 {} + +@_originallyDefinedIn(module: "foo", // expected-error {{expected at least one platform version in @_originallyDefinedIn}} +class ToplevelClass3 {} + +@available(OSX 13.10, *) +@_originallyDefinedIn(module: "foo", * 13.13) // expected-warning {{* as platform name has no effect}} expected-error {{expected at least one platform version in @_originallyDefinedIn}} +@_originallyDefinedIn(module: "foo", OSX 13.13, iOS 7.0) +@_originallyDefinedIn(module: "foo", OSX 13.14, * 7.0) // expected-warning {{* as platform name has no effect}} expected-error {{duplicate version number for platform OSX}} +class ToplevelClass4 { + @_originallyDefinedIn(module: "foo", OSX 13.13) // expected-error {{'@_originallyDefinedIn' attribute cannot be applied to this declaration}} + subscript(index: Int) -> Int { + get { return 1 } + set(newValue) {} + } +} diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index 398dfb5a7e368..c7503c2c97dca 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -436,17 +436,20 @@ struct ErrorTypeInVarDeclFunctionType1 { } struct ErrorTypeInVarDeclArrayType1 { - var v1 : Int[2] // expected-error {{array types are now written with the brackets around the element type}} + var v1 : Int[+] // expected-error {{unexpected ']' in type; did you mean to write an array type?}} + // expected-error @-1 {{expected expression after unary operator}} + // expected-error @-2 {{expected expression}} var v2 : Int } struct ErrorTypeInVarDeclArrayType2 { - var v1 : Int[4 // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} - var v2 : Int + var v1 : Int[+ // expected-error {{unary operator cannot be separated from its operand}} + var v2 : Int // expected-error {{expected expression}} } struct ErrorTypeInVarDeclArrayType3 { - var v1 : Int[ // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} + var v1 : Int[ + ; // expected-error {{expected expression}} var v2 : Int } @@ -476,7 +479,7 @@ struct ErrorTypeInVarDeclDictionaryType { } struct ErrorInFunctionSignatureResultArrayType1 { - func foo() -> Int[ { // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} + func foo() -> Int[ { // expected-error {{expected '{' in body of function declaration}} return [0] } func bar() -> Int] { // expected-error {{unexpected ']' in type; did you mean to write an array type?}} {{17-17=[}} @@ -486,32 +489,32 @@ struct ErrorInFunctionSignatureResultArrayType1 { struct ErrorInFunctionSignatureResultArrayType2 { func foo() -> Int[0 { // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} - return [0] + return [0] // expected-error {{cannot convert return expression of type '[Int]' to return type 'Int'}} } } struct ErrorInFunctionSignatureResultArrayType3 { - func foo() -> Int[0] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-22=}} + func foo() -> Int[0] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-21=}} return [0] } } struct ErrorInFunctionSignatureResultArrayType4 { - func foo() -> Int[0_1] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-24=}} + func foo() -> Int[0_1] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-21=}} return [0] } } struct ErrorInFunctionSignatureResultArrayType5 { - func foo() -> Int[0b1] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-24=}} + func foo() -> Int[0b1] { // expected-error {{array types are now written with the brackets around the element type}} {{17-17=[}} {{20-21=}} return [0] } } struct ErrorInFunctionSignatureResultArrayType11 { // expected-note{{in declaration of 'ErrorInFunctionSignatureResultArrayType11'}} - func foo() -> Int[(a){a++}] { // expected-error {{consecutive declarations on a line must be separated by ';'}} {{20-20=;}} expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} expected-error {{expected '{' in body of function declaration}} expected-error {{expected declaration}} + func foo() -> Int[(a){a++}] { // expected-error {{consecutive declarations on a line must be separated by ';'}} {{29-29=;}} expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} expected-error {{use of unresolved operator '++'; did you mean '+= 1'?}} expected-error {{use of unresolved identifier 'a'}} expected-error {{expected declaration}} } } @@ -616,9 +619,11 @@ class WrongInheritanceClause6(Int {} class WrongInheritanceClause7(Int where T:AnyObject {} // [swift-crashes 078] parser crash on invalid cast in sequence expr -Base=1 as Base=1 // expected-error {{cannot assign to immutable expression of type 'Base.Type'}} - - +Base=1 as Base=1 // expected-error{{cannot convert value of type 'Int' to type 'Base' in coercion}} +// expected-error@-1 {{cannot assign to immutable expression of type 'Base.Type'}} +// expected-error@-2 {{cannot assign to immutable expression of type 'Base'}} +// expected-error@-3 {{cannot assign value of type '()' to type 'Base.Type'}} +// expected-error@-4 {{cannot assign value of type 'Int' to type 'Base'}} // Parser hangs at swift::Parser::parseType public enum TestA { @@ -664,7 +669,9 @@ case let (jeb): // rdar://19605164 // expected-error@+2{{use of undeclared type 'S'}} struct Foo19605164 { -func a(s: S[{{g) -> Int {} // expected-error {{expected ']' in array type}} expected-note {{to match this opening '['}} +func a(s: S[{{g) -> Int {} +// expected-error@+2 {{expected parameter name followed by ':'}} +// expected-error@+1 {{expected ',' separator}} }}} #endif @@ -674,7 +681,7 @@ func a(s: S[{{g) -> Int {} // expected-error {{expected ']' in array type}} expe // expected-error@+3{{expected '(' for initializer parameters}} // expected-error@+2{{initializers may only be declared within a type}} // expected-error@+1{{expected an identifier to name generic parameter}} -func F() { init<( } )} // expected-note {{did you mean 'F'?}} +func F() { init<( } )} // expected-note 2{{did you mean 'F'?}} struct InitializerWithName { init x() {} // expected-error {{initializers cannot have a name}} {{8-9=}} @@ -690,9 +697,6 @@ struct InitializerWithNameAndParam { struct InitializerWithLabels { init c d: Int {} // expected-error @-1 {{expected '(' for initializer parameters}} - // expected-error @-2 {{expected declaration}} - // expected-error @-3 {{consecutive declarations on a line must be separated by ';'}} - // expected-note @-5 {{in declaration of 'InitializerWithLabels'}} } // rdar://20337695 @@ -756,7 +760,7 @@ let tryx = 123 // expected-error {{invalid character in source file}} // Malformed Swift Enums crash playground service -enum Rank: Int { // expected-error {{'Rank' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum Rank: Int { // expected-error {{'Rank' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} case Ace = 1 case Two = 2.1 // expected-error {{cannot convert value of type 'Double' to raw type 'Int'}} } @@ -836,7 +840,7 @@ func postfixDot(a : String) { } // QoI: "UIColor." gives two issues, should only give one -func f() { +func f() { // expected-note 2{{did you mean 'f'?}} _ = ClassWithStaticDecls. // expected-error {{expected member name following '.'}} } @@ -844,3 +848,7 @@ func f() { // | SR-11006 // expected-error@+1 {{expected '=' instead of '==' to assign default value for parameter}} {{21-23==}} func SR11006(a: Int == 0) {} + +// rdar://38225184 +extension Collection where Element == Int && Index == Int {} +// expected-error@-1 {{expected ',' to separate the requirements of this 'where' clause}} {{43-45=,}} diff --git a/test/Parse/super.swift b/test/Parse/super.swift index 52ffa73cf7f52..c4317a4bf18b8 100644 --- a/test/Parse/super.swift +++ b/test/Parse/super.swift @@ -4,8 +4,8 @@ class B { var foo: Int func bar() {} - init() {} - init(x: Int) {} + init() {} // expected-note {{found this candidate}} + init(x: Int) {} // expected-note {{found this candidate}} subscript(x: Int) -> Int { get {} @@ -38,7 +38,8 @@ class D : B { super.foo.bar // expected-error {{value of type 'Int' has no member 'bar'}} super.bar // expected-error {{expression resolves to an unused function}} super.bar() - super.init // expected-error{{'super.init' cannot be called outside of an initializer}} + // FIXME: should also say "'super.init' cannot be referenced outside of an initializer" + super.init // expected-error{{no exact matches in call to initializer}} super.init() // expected-error{{'super.init' cannot be called outside of an initializer}} super.init(0) // expected-error{{'super.init' cannot be called outside of an initializer}} // expected-error {{missing argument label 'x:' in call}} super[0] // expected-error {{expression resolves to an unused subscript}} diff --git a/test/Parse/try.swift b/test/Parse/try.swift index f58f4a0c52708..30f2f7a89dc29 100644 --- a/test/Parse/try.swift +++ b/test/Parse/try.swift @@ -236,12 +236,14 @@ struct ThingProducer { } let optProducer: ThingProducer? = ThingProducer() -let _: Int? = try? optProducer?.produceInt() // expected-error {{value of optional type 'Int??' not unwrapped; did you mean to use 'try!' or chain with '?'?}} +// In Swift 4 mode try? always adds a new level of optionality so suggesting `try!` doesn't really help in these examples. +let _: Int? = try? optProducer?.produceInt() // expected-error {{cannot convert value of type 'Int??' to specified type 'Int?'}} let _: Int = try? optProducer?.produceInt() // expected-error {{cannot convert value of type 'Int??' to specified type 'Int'}} let _: String = try? optProducer?.produceInt() // expected-error {{cannot convert value of type 'Int??' to specified type 'String'}} let _: Int?? = try? optProducer?.produceInt() // good -let _: Int? = try? optProducer?.produceIntNoThrowing() // expected-error {{value of optional type 'Int??' not unwrapped; did you mean to use 'try!' or chain with '?'?}} +let _: Int? = try? optProducer?.produceIntNoThrowing() // expected-error {{cannot convert value of type 'Int??' to specified type 'Int?'}} +// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}} let _: Int?? = try? optProducer?.produceIntNoThrowing() // expected-warning {{no calls to throwing functions occur within 'try' expression}} let _: Int? = (try? optProducer?.produceAny()) as? Int // good @@ -253,7 +255,7 @@ let _: String = try? optProducer?.produceDoubleOptionalInt() // expected-error { let producer = ThingProducer() -let _: Int = try? producer.produceDoubleOptionalInt() // expected-error {{cannot convert value of type 'Int???' to specified type 'Int'}} +let _: Int = try? producer.produceDoubleOptionalInt() // expected-error {{value of optional type 'Int???' not unwrapped; did you mean to use 'try!' or chain with '?'?}} let _: Int? = try? producer.produceDoubleOptionalInt() // expected-error {{value of optional type 'Int???' not unwrapped; did you mean to use 'try!' or chain with '?'?}} let _: Int?? = try? producer.produceDoubleOptionalInt() // expected-error {{value of optional type 'Int???' not unwrapped; did you mean to use 'try!' or chain with '?'?}} let _: Int??? = try? producer.produceDoubleOptionalInt() // good diff --git a/test/Parse/type_expr.swift b/test/Parse/type_expr.swift index ef150620291da..4384e823852fb 100644 --- a/test/Parse/type_expr.swift +++ b/test/Parse/type_expr.swift @@ -117,7 +117,7 @@ func genQualifiedType() { func typeOfShadowing() { // Try to shadow type(of:) - func type(of t: T.Type, flag: Bool) -> T.Type { + func type(of t: T.Type, flag: Bool) -> T.Type { // expected-note {{'type(of:flag:)' declared here}} return t } @@ -133,8 +133,7 @@ func typeOfShadowing() { return t } - // TODO: Errors need improving here. - _ = type(of: Gen.Bar) // expected-error{{incorrect argument label in call (have 'of:', expected 'fo:')}} + _ = type(of: Gen.Bar) // expected-error{{missing argument for parameter 'flag' in call}} {{28-28=, flag: <#Bool#>}} _ = type(Gen.Bar) // expected-error{{expected member name or constructor call after type name}} // expected-note@-1{{add arguments after the type to construct a value of the type}} // expected-note@-2{{use '.self' to reference the type object}} @@ -266,7 +265,7 @@ protocol P2 {} protocol P3 {} func compositionType() { _ = P1 & P2 // expected-error {{expected member name or constructor call after type name}} expected-note{{use '.self'}} {{7-7=(}} {{14-14=).self}} - _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} expected-note {{overloads}} + _ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} _ = (P1 & P2).self // Ok. _ = (P1 & (P2)).self // FIXME: OK? while `typealias P = P1 & (P2)` is rejected. _ = (P1 & (P2, P3)).self // expected-error {{non-protocol, non-class type '(P2, P3)' cannot be used within a protocol-constrained type}} diff --git a/test/PlaygroundTransform/iuo.swift b/test/PlaygroundTransform/iuo.swift new file mode 100644 index 0000000000000..45da579ca03cd --- /dev/null +++ b/test/PlaygroundTransform/iuo.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: cp %s %t/main.swift +// RUN: %target-build-swift -force-single-frontend-invocation -module-name PlaygroundSupport -emit-module-path %t/PlaygroundSupport.swiftmodule -parse-as-library -c -o %t/PlaygroundSupport.o %S/Inputs/SilentPCMacroRuntime.swift %S/Inputs/PlaygroundsRuntime.swift +// RUN: %target-build-swift -Xfrontend -playground -o %t/main -I=%t %t/PlaygroundSupport.o %t/main.swift + +import PlaygroundSupport + +class Layer { + var value: Double = 0 +} + +class Outer { + let layer: Layer! = Layer() +} + +class Enclosing { + var outer: Outer! = Outer() + + func test() { + // Ensure that this doesn't crash + outer.layer.value = 3.14159 + } +} + +Enclosing().test() diff --git a/test/PrintAsObjC/blocks.swift b/test/PrintAsObjC/blocks.swift index 0a8a0c80b09a4..f7d355493d713 100644 --- a/test/PrintAsObjC/blocks.swift +++ b/test/PrintAsObjC/blocks.swift @@ -113,6 +113,9 @@ typealias MyBlockWithNoescapeParam = (() -> ()) -> Int return input } + // CHECK-NEXT: - (void)blockWithConsumingArgument:(void (^ _Nonnull)(SWIFT_RELEASES_ARGUMENT NSObject * _Nonnull))block; + @objc func blockWithConsumingArgument(_ block: @escaping (__owned NSObject) -> ()) {} + // CHECK-NEXT: @property (nonatomic, copy) NSInteger (^ _Nullable savedBlock)(NSInteger); @objc var savedBlock: ((Int) -> Int)? diff --git a/test/PrintAsObjC/classes.swift b/test/PrintAsObjC/classes.swift index 9d46ef4a1ddbb..025bef7fb4d5d 100644 --- a/test/PrintAsObjC/classes.swift +++ b/test/PrintAsObjC/classes.swift @@ -359,6 +359,14 @@ typealias AliasForNSRect = NSRect @objc func testBridgingOptionality(_ a: UnsafePointer?, b: UnsafeMutablePointer!, c: AutoreleasingUnsafeMutablePointer?) {} } +// CHECK-LABEL: IB_DESIGNABLE +// CHECK-NEXT: SWIFT_CLASS( +// CHECK-NEXT: @interface MyDesignableObject : NSObject +// CHECK-NEXT: init +// CHECK-NEXT: @end +// NEGATIVE-NOT: @interface NSObject +@IBDesignable class MyDesignableObject : NSObject {} + // CHECK-LABEL: @interface MyObject : NSObject // CHECK-NEXT: init // CHECK-NEXT: @end @@ -517,6 +525,7 @@ public class NonObjCClass { } // CHECK-NEXT: @property (nonatomic) CFAliasForTypeRef _Nullable anyCF2; // CHECK-NEXT: @property (nonatomic, weak) IBOutlet id _Null_unspecified outlet; // CHECK-NEXT: @property (nonatomic, strong) IBOutlet Properties * _Null_unspecified typedOutlet; +// CHECK-NEXT: @property (nonatomic) IBInspectable NSInteger inspectable; // CHECK-NEXT: @property (nonatomic, copy) NSString * _Nonnull string; // CHECK-NEXT: @property (nonatomic, copy) NSArray * _Nonnull array; // CHECK-NEXT: @property (nonatomic, copy) NSArray *> * _Nonnull arrayOfArrays; @@ -608,6 +617,7 @@ public class NonObjCClass { } @IBOutlet weak var outlet: AnyObject! @IBOutlet var typedOutlet: Properties! + @IBInspectable var inspectable: Int = 0 @objc var string = "abc" @objc var array: Array = [] diff --git a/test/PrintAsObjC/extensions.swift b/test/PrintAsObjC/extensions.swift index a77f5edffa158..17c164389f81c 100644 --- a/test/PrintAsObjC/extensions.swift +++ b/test/PrintAsObjC/extensions.swift @@ -8,7 +8,6 @@ // RUN: %check-in-clang %t/extensions.h // REQUIRES: objc_interop -// REQUIRES: rdar54414986 import Foundation import AppKit diff --git a/test/Profiler/coverage_struct.swift b/test/Profiler/coverage_struct.swift new file mode 100644 index 0000000000000..5063574734438 --- /dev/null +++ b/test/Profiler/coverage_struct.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_struct %s | %FileCheck %s + +struct Foo { + var a = false + +// CHECK: sil_coverage_map {{.*}}// variable initialization expression of coverage_struct.Foo.b : Swift.Bool +// CHECK-NEXT: [[@LINE+1]]:11 -> [[@LINE+3]]:6 : 0 + let b = { + false + }() +} diff --git a/test/Profiler/coverage_var_init.swift b/test/Profiler/coverage_var_init.swift index 1d910257221c5..6d20ad36f41ce 100644 --- a/test/Profiler/coverage_var_init.swift +++ b/test/Profiler/coverage_var_init.swift @@ -1,6 +1,10 @@ // RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_var_init %s | %FileCheck %s final class VarInit { + // CHECK: sil_coverage_map {{.*}} "$s17coverage_var_init7VarInitC018initializedWrapperE0SivpfP" + // CHECK-NEXT: [[@LINE+1]]:4 -> [[@LINE+1]]:38 : 0 + @Wrapper var initializedWrapperInit = 2 + // CHECK: sil_coverage_map {{.*}} "$s17coverage_var_init7VarInitC04lazydE033_49373CB2DFB47C8DC62FA963604688DFLLSSvgSSyXEfU_" // CHECK-NEXT: [[@LINE+1]]:42 -> [[@LINE+3]]:4 : 0 private lazy var lazyVarInit: String = { @@ -23,7 +27,13 @@ final class VarInit { print(lazyVarInit) print(basicVarInit) print(simpleVar) + print(initializedWrapperInit) } } +@propertyWrapper struct Wrapper { + init(wrappedValue: Int) {} + var wrappedValue: Int { 1 } +} + VarInit().coverageFunction() diff --git a/test/Profiler/pgo_checked_cast.swift b/test/Profiler/pgo_checked_cast.swift index 027441920de71..0ad48c1d33310 100644 --- a/test/Profiler/pgo_checked_cast.swift +++ b/test/Profiler/pgo_checked_cast.swift @@ -41,10 +41,10 @@ public class D : C {} // IR-LABEL: define swiftcc i32 @$s6pgo_checked_cast6guess1s5Int32VAD1x_tF // IR-OPT-LABEL: define swiftcc i32 @$s6pgo_checked_cast6guess1s5Int32VAD1x_tF public func check2(_ a : B) -> Int32 { - // SIL: checked_cast_br %0 : $B to $D, {{.*}}, {{.*}} !true_count(5000) - // SIL: checked_cast_br %0 : $B to $C, {{.*}}, {{.*}} !true_count(2) - // SIL-OPT: checked_cast_br %0 : $B to $D, {{.*}}, {{.*}} !true_count(5000) - // SIL-OPT: checked_cast_br %0 : $B to $C, {{.*}}, {{.*}} !true_count(2) + // SIL: checked_cast_br %0 : $B to D, {{.*}}, {{.*}} !true_count(5000) + // SIL: checked_cast_br %0 : $B to C, {{.*}}, {{.*}} !true_count(2) + // SIL-OPT: checked_cast_br %0 : $B to D, {{.*}}, {{.*}} !true_count(5000) + // SIL-OPT: checked_cast_br %0 : $B to C, {{.*}}, {{.*}} !true_count(2) switch a { case is D: return 42 diff --git a/test/Prototypes/Algorithms.swift b/test/Prototypes/Algorithms.swift index 35f690df2dee3..24d48fa2b14ca 100644 --- a/test/Prototypes/Algorithms.swift +++ b/test/Prototypes/Algorithms.swift @@ -613,6 +613,11 @@ extension Collection { /// The collection must already be partitioned according to the /// predicate, as if `self.partition(by: predicate)` had already /// been called. + /// + /// - Efficiency: At most log(N) invocations of `predicate`, where + /// N is the length of `self`. At most log(N) index offsetting + /// operations if `self` conforms to `RandomAccessCollection`; + /// at most N such operations otherwise. func partitionPoint( where predicate: (Element) throws -> Bool ) rethrows -> Index { diff --git a/test/Reflection/typeref_decoding_imported.swift b/test/Reflection/typeref_decoding_imported.swift index 301fc7da1dad0..96e57806e5681 100644 --- a/test/Reflection/typeref_decoding_imported.swift +++ b/test/Reflection/typeref_decoding_imported.swift @@ -1,3 +1,5 @@ +// XFAIL: OS=windows-msvc + // RUN: %empty-directory(%t) // RUN: %target-build-swift %S/Inputs/ImportedTypes.swift %S/Inputs/ImportedTypesOther.swift -parse-as-library -emit-module -emit-library -module-name TypesToReflect -o %t/%target-library-name(TypesToReflect) -I %S/Inputs diff --git a/test/Reflection/typeref_lowering.swift b/test/Reflection/typeref_lowering.swift index 1be3da2dfe1f7..750b2af38eeb1 100644 --- a/test/Reflection/typeref_lowering.swift +++ b/test/Reflection/typeref_lowering.swift @@ -1,4 +1,5 @@ // REQUIRES: no_asan +// XFAIL: OS=windows-msvc // RUN: %empty-directory(%t) // RUN: %target-build-swift -Xfrontend -disable-availability-checking %S/Inputs/TypeLowering.swift -parse-as-library -emit-module -emit-library -module-name TypeLowering -o %t/%target-library-name(TypesToReflect) @@ -1038,9 +1039,9 @@ // CHECK-64-NEXT: (field name=empty offset=0 // CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) // CHECK-64-NEXT: (field name=noPayload offset=0 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=sillyNoPayload offset=1 -// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1)) // CHECK-64-NEXT: (field name=singleton offset=8 // CHECK-64-NEXT: (reference kind=strong refcounting=native)) // CHECK-64-NEXT: (field name=singlePayload offset=16 diff --git a/test/Runtime/demangleToMetadataMovedSymbols.swift b/test/Runtime/demangleToMetadataMovedSymbols.swift new file mode 100644 index 0000000000000..397d51701d736 --- /dev/null +++ b/test/Runtime/demangleToMetadataMovedSymbols.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -parse-stdlib %s -module-name main -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import Swift +import StdlibUnittest + +let DemangleToMetadataMovedSymbolsTests = TestSuite("DemangleToMetadataMovedSymbols") + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +struct MovedS { + struct Nested { } +} + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "foo", OSX 10.13) +enum MovedE { case e } + +@available(OSX 10.9, *) +@_originallyDefinedIn(module: "bar", OSX 10.13) +class MovedC {} + +DemangleToMetadataMovedSymbolsTests.test("Moved Nominals") { + // Simple Struct + expectEqual(type(of: MovedS()), _typeByName("3foo6MovedSV")!) + expectNil(_typeByName("4main6MovedSV")) + + // Simple Enum + expectEqual(type(of: MovedE.e), _typeByName("3foo6MovedEO")!) + expectNil(_typeByName("4main6MovedEO")) + + // Nested struct + expectEqual(type(of: MovedS.Nested()), _typeByName("3foo6MovedSV6NestedV")!) + expectNil(_typeByName("4main6MovedSV6NestedV")) + + // Simple Class + expectEqual(type(of: MovedC()), _typeByName("3bar6MovedCC")!) + expectNil(_typeByName("4main6MovedCC")) +} + +runAllTests() diff --git a/test/Runtime/linux-fatal-backtrace.swift b/test/Runtime/linux-fatal-backtrace.swift index ab5ad639e0aad..7e84dc9719a62 100644 --- a/test/Runtime/linux-fatal-backtrace.swift +++ b/test/Runtime/linux-fatal-backtrace.swift @@ -4,6 +4,7 @@ // REQUIRES: executable_test // REQUIRES: OS=linux-gnu // REQUIRES: lldb +// XFAIL: CPU=s390x // NOTE: not.py is used above instead of "not --crash" because %target-run // doesn't pass through the crash, and `not` may not be available when running diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index 04eff01af3638..592cf6f82f70b 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -243,7 +243,7 @@ bb0: %a = upcast %D : $D to $C - %5 = unconditional_checked_cast %C : $C to $D // CHECK: unconditional_checked_cast + %5 = unconditional_checked_cast %C : $C to D // CHECK: unconditional_checked_cast %6 = tuple () return %6 : $() } @@ -510,7 +510,7 @@ bb0(%0 : $B): store %0 to %1a : $*B %3 = load %1a : $*B // CHECK: load strong_retain %3 : $B - checked_cast_br %3 : $B to $E, yes, no // CHECK: checked_cast_br + checked_cast_br %3 : $B to E, yes, no // CHECK: checked_cast_br yes(%5 : $E): %y = integer_literal $Builtin.Int1, 1 br isa(%y : $Builtin.Int1) @@ -798,13 +798,13 @@ bb0: sil @test_super_to_archetype_ref : $@convention(thin) (Class1) -> T { bb0(%c : $Class1): - %0 = unconditional_checked_cast %c : $Class1 to $T + %0 = unconditional_checked_cast %c : $Class1 to T return %0 : $T } sil @test_downcast_archetype_ref : $@convention(thin) (T) -> () { bb0(%t : $T): - %0 = unconditional_checked_cast %t : $T to $U + %0 = unconditional_checked_cast %t : $T to U %1 = tuple () return %1 : $() } diff --git a/test/SIL/Parser/basic2.sil b/test/SIL/Parser/basic2.sil index 60ef4be99a61b..929129d52abb9 100644 --- a/test/SIL/Parser/basic2.sil +++ b/test/SIL/Parser/basic2.sil @@ -14,20 +14,20 @@ bb0(%0 : @owned $Builtin.NativeObject): return %1 : $Builtin.NativeObject } -// CHECK-LABEL: sil [ossa] @test_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { +// CHECK-LABEL: sil [ossa] @test_strong_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { // CHECK: bb0([[T0:%[0-9]+]] : @owned $@sil_unowned Builtin.NativeObject): -// CHECK-NEXT: [[COPY_RESULT:%.*]] = copy_unowned_value [[T0]] : $@sil_unowned Builtin.NativeObject +// CHECK-NEXT: [[COPY_RESULT:%.*]] = strong_copy_unowned_value [[T0]] : $@sil_unowned Builtin.NativeObject // CHECK-NEXT: destroy_value [[T0]] : $@sil_unowned Builtin.NativeObject // CHECK-NEXT: return [[COPY_RESULT]] : $Builtin.NativeObject -sil [ossa] @test_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { +sil [ossa] @test_strong_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $@sil_unowned Builtin.NativeObject): - %1 = copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject + %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject destroy_value %0 : $@sil_unowned Builtin.NativeObject return %1 : $Builtin.NativeObject } -sil [ossa] @copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { +sil [ossa] @strong_copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : $@sil_unmanaged Builtin.NativeObject): - %1 = copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject + %1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject return %1 : $Builtin.NativeObject } diff --git a/test/SIL/Parser/opaque_values_parse.sil b/test/SIL/Parser/opaque_values_parse.sil index 3a15a4395288d..750177c5696b1 100644 --- a/test/SIL/Parser/opaque_values_parse.sil +++ b/test/SIL/Parser/opaque_values_parse.sil @@ -16,22 +16,22 @@ struct S : Foo { // CHECK-LABEL: sil @castOpaque : $@convention(thin) (Int) -> () { // CHECK: bb0([[ARG:%.*]] : $Int): -// CHECK: unconditional_checked_cast_value [[ARG]] : $Int to $Foo +// CHECK: unconditional_checked_cast_value Int in [[ARG]] : $Int to Foo // CHECK-LABEL: } // end sil function 'castOpaque' sil @castOpaque : $@convention(thin) (Int) -> () { bb0(%0 : $Int): - %c = unconditional_checked_cast_value %0 : $Int to $Foo + %c = unconditional_checked_cast_value Int in %0 : $Int to Foo %t = tuple () return %t : $() } // CHECK-LABEL: sil @condCastOpaque : $@convention(thin) (Int) -> () { // CHECK: bb0([[ARG:%.*]] : $Int): -// CHECK: checked_cast_value_br [[ARG]] : $Int to $Int +// CHECK: checked_cast_value_br Int in [[ARG]] : $Int to Int // CHECK-LABEL: } // end sil function 'condCastOpaque' sil @condCastOpaque : $@convention(thin) (Int) -> () { bb0(%0 : $Int): - checked_cast_value_br %0 : $Int to $Int, bb2, bb1 + checked_cast_value_br Int in %0 : $Int to Int, bb2, bb1 bb1: br bb3 diff --git a/test/SIL/Parser/subst_function_type.sil b/test/SIL/Parser/subst_function_type.sil new file mode 100644 index 0000000000000..06052212b4e7b --- /dev/null +++ b/test/SIL/Parser/subst_function_type.sil @@ -0,0 +1,17 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +sil_stage raw + +import Swift + +// CHECK-LABEL: sil @test : $@convention(thin) (@guaranteed @callee_guaranteed <τ_0_0, τ_0_1> in (@in τ_0_0, @in τ_0_1) -> () for ) -> () +sil @test : $@convention(thin) (@guaranteed @callee_guaranteed in (@in A, @in B) -> () for ) -> () { +entry(%0 : $@callee_guaranteed in (@in C, @in D) -> () for ): + return undef : $() +} + +// CHECK-LABEL: sil @test_generic_context : $@convention(thin) (@guaranteed @callee_guaranteed <τ_0_0, τ_0_1> in (@in τ_0_0, @in τ_0_1) -> () for ) -> () +sil @test_generic_context : $@convention(thin) (@guaranteed @callee_guaranteed in (@in A, @in B) -> () for ) -> () { +entry(%0 : $@callee_guaranteed in (@in C, @in D) -> () for ): + return undef : $() +} diff --git a/test/SIL/Parser/undef.sil b/test/SIL/Parser/undef.sil index 6e31ccf86e677..fc8b5f253c957 100644 --- a/test/SIL/Parser/undef.sil +++ b/test/SIL/Parser/undef.sil @@ -250,8 +250,8 @@ bb0: // Checked Conversions - // CHECK: unconditional_checked_cast undef : $C to $C - unconditional_checked_cast undef : $C to $C + // CHECK: unconditional_checked_cast undef : $C to C + unconditional_checked_cast undef : $C to C // CHECK: unconditional_checked_cast_addr C in undef : $*C to C in undef : $*C unconditional_checked_cast_addr C in undef : $*C to C in undef : $*C @@ -351,8 +351,8 @@ bb3: sil @checked_cast_br_test : $() -> () { bb0: - // CHECK: checked_cast_br undef : $C to $C, bb1, bb2 - checked_cast_br undef : $C to $C, bb1, bb2 + // CHECK: checked_cast_br undef : $C to C, bb1, bb2 + checked_cast_br undef : $C to C, bb1, bb2 bb1(%x : $C): br bb3 bb2: diff --git a/test/SIL/Serialization/Inputs/def_public_non_abi.sil b/test/SIL/Serialization/Inputs/def_public_non_abi.sil index 927ccef56bdc8..14a8a3bb1bf78 100644 --- a/test/SIL/Serialization/Inputs/def_public_non_abi.sil +++ b/test/SIL/Serialization/Inputs/def_public_non_abi.sil @@ -7,4 +7,4 @@ sil non_abi [serialized] @public_non_abi_function : $@convention(thin) () -> () %fn = function_ref @other_public_non_abi_function : $@convention(thin) () -> () %0 = tuple () return %0 : $() -} \ No newline at end of file +} diff --git a/test/SIL/Serialization/basic.sil b/test/SIL/Serialization/basic.sil index 104639f3b5531..b4ad226fd01ae 100644 --- a/test/SIL/Serialization/basic.sil +++ b/test/SIL/Serialization/basic.sil @@ -38,6 +38,12 @@ struct Int32 { var x: Builtin.Int32 } +// CHECK-LABEL: sil [serialized] [ossa] @test_subst_function_type : $@convention(thin) (@guaranteed @callee_guaranteed <τ_0_0, τ_0_1> in (@in τ_0_0, @in τ_0_1) -> () for ) -> () +sil [serialized] [ossa] @test_subst_function_type : $@convention(thin) (@guaranteed @callee_guaranteed in (@in A, @in B) -> () for ) -> () { +entry(%0 : @guaranteed $@callee_guaranteed in (@in C, @in D) -> () for ): + return undef : $() +} + sil [ossa] @test_destructure_struct_tuple : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.Int32), @owned TestArray2) -> @owned (Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage) { bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2): (%2, %3) = destructure_tuple %0 : $(Builtin.NativeObject, Builtin.Int32) @@ -46,6 +52,12 @@ bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2) return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage) } +// CHECK-LABEL: sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) (@guaranteed @callee_guaranteed <τ_0_0, τ_0_1> in (@in τ_0_0, @in τ_0_1) -> () for ) -> () +sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) (@guaranteed @callee_guaranteed in (@in A, @in B) -> () for ) -> () { +entry(%0 : @guaranteed $@callee_guaranteed in (@in C, @in D) -> () for ): + return undef : $() +} + struct EmptyStruct {} sil @test_empty_destructure : $@convention(thin) () -> () { @@ -57,8 +69,8 @@ bb0: return %1 : $() } -sil [ossa] @copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { +sil [ossa] @strong_copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : $@sil_unmanaged Builtin.NativeObject): - %1 = copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject + %1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject return %1 : $Builtin.NativeObject } diff --git a/test/SIL/Serialization/copy_value_destroy_value.sil b/test/SIL/Serialization/copy_value_destroy_value.sil index 26412bbc35acb..7b6b3022f527f 100644 --- a/test/SIL/Serialization/copy_value_destroy_value.sil +++ b/test/SIL/Serialization/copy_value_destroy_value.sil @@ -8,19 +8,6 @@ sil_stage canonical import Builtin - -// CHECK-LABEL: sil [serialized] [ossa] @test_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { -// CHECK: bb0([[T0:%[0-9]+]] : @owned $@sil_unowned Builtin.NativeObject): -// CHECK-NEXT: [[COPY_RESULT:%.*]] = copy_unowned_value [[T0]] : $@sil_unowned Builtin.NativeObject -// CHECK-NEXT: destroy_value [[T0]] : $@sil_unowned Builtin.NativeObject -// CHECK-NEXT: return [[COPY_RESULT]] : $Builtin.NativeObject -sil [serialized] [ossa] @test_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { -bb0(%0 : @owned $@sil_unowned Builtin.NativeObject): - %1 = copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject - destroy_value %0 : $@sil_unowned Builtin.NativeObject - return %1 : $Builtin.NativeObject -} - // CHECK-LABEL: sil [serialized] [ossa] @test_copy_value : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { // CHECK: bb0([[ARG1:%[0-9]+]] : @owned $Builtin.NativeObject): // CHECK: [[COPY_VALUE_RESULT:%[0-9]+]] = copy_value [[ARG1]] : $Builtin.NativeObject @@ -32,3 +19,16 @@ bb0(%0 : @owned $Builtin.NativeObject): destroy_value %0 : $Builtin.NativeObject return %1 : $Builtin.NativeObject } + + +// CHECK-LABEL: sil [serialized] [ossa] @test_strong_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { +// CHECK: bb0([[T0:%[0-9]+]] : @owned $@sil_unowned Builtin.NativeObject): +// CHECK-NEXT: [[COPY_RESULT:%.*]] = strong_copy_unowned_value [[T0]] : $@sil_unowned Builtin.NativeObject +// CHECK-NEXT: destroy_value [[T0]] : $@sil_unowned Builtin.NativeObject +// CHECK-NEXT: return [[COPY_RESULT]] : $Builtin.NativeObject +sil [serialized] [ossa] @test_strong_copy_unowned_value : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { +bb0(%0 : @owned $@sil_unowned Builtin.NativeObject): + %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject + destroy_value %0 : $@sil_unowned Builtin.NativeObject + return %1 : $Builtin.NativeObject +} diff --git a/test/SIL/Serialization/deserialize_generic.sil b/test/SIL/Serialization/deserialize_generic.sil index 68d402784fcac..1fd291035a78f 100644 --- a/test/SIL/Serialization/deserialize_generic.sil +++ b/test/SIL/Serialization/deserialize_generic.sil @@ -22,5 +22,5 @@ bb0: } // Make sure the function body is deserialized. -// CHECK-LABEL: sil public_external [serialized] [canonical] @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A { +// CHECK-LABEL: sil public_external [serialized] [canonical] [ossa] @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A { sil @$s11def_generic1AC23convertFromArrayLiteralyACyxGxd_tF : $@convention(method) (@guaranteed Array, @guaranteed A) -> @owned A diff --git a/test/SIL/Serialization/globals.sil b/test/SIL/Serialization/globals.sil index 853c851939dad..7ceffc910d283 100644 --- a/test/SIL/Serialization/globals.sil +++ b/test/SIL/Serialization/globals.sil @@ -23,4 +23,4 @@ bb0: %1 = global_addr @public_global_used : $*Int %2 = tuple () return %2 : $() -} \ No newline at end of file +} diff --git a/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift b/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift index 9273c467d3c44..c58bc913cf01f 100644 --- a/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift +++ b/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -sil-inline-threshold 0 %S/Inputs/init_existential_inst_deserializes_witness_tables_input.swift -o %t/Swift.swiftmodule -emit-module -parse-as-library -parse-stdlib -module-link-name swiftCore -module-name Swift -O -// RUN: %target-swift-frontend -I %t -O %s -emit-sil -o - | %FileCheck %s +// RUN: %target-swift-frontend -I %t -O %s -Xllvm -sil-disable-pass=late-deadfuncelim -emit-sil -o - | %FileCheck %s // CHECK: sil_witness_table public_external X: P module Swift { diff --git a/test/SIL/Serialization/metatype_casts.sil b/test/SIL/Serialization/metatype_casts.sil index 8364ea5230504..e57125f0a3858 100644 --- a/test/SIL/Serialization/metatype_casts.sil +++ b/test/SIL/Serialization/metatype_casts.sil @@ -17,7 +17,7 @@ class B : A {} sil @test_checked_casts_of_metatypes : $@convention(thin) () -> Builtin.Int1 { bb0: %0 = metatype $@thick B.Type.Type - checked_cast_br %0 : $@thick B.Type.Type to $@thick A.Type.Type, bb1, bb2 + checked_cast_br %0 : $@thick B.Type.Type to @thick A.Type.Type, bb1, bb2 bb1(%2 : $@thick A.Type.Type): %3 = integer_literal $Builtin.Int1, -1 diff --git a/test/SIL/Serialization/opaque_return_type_serialize.sil b/test/SIL/Serialization/opaque_return_type_serialize.sil index 2a42e8740a058..37c589eebe77e 100644 --- a/test/SIL/Serialization/opaque_return_type_serialize.sil +++ b/test/SIL/Serialization/opaque_return_type_serialize.sil @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -disable-availability-checking -emit-module -emit-module-path %t/OpaqueReturnTypeExporter.swiftmodule -module-name OpaqueReturnTypeExporter %S/Inputs/OpaqueReturnTypeExporter.swift +// RUN: %target-swift-frontend -disable-availability-checking -enable-library-evolution -emit-module -emit-module-path %t/OpaqueReturnTypeExporter.swiftmodule -module-name OpaqueReturnTypeExporter %S/Inputs/OpaqueReturnTypeExporter.swift // RUN: %target-sil-opt -I %t %s -emit-sib -module-name test -o %t/test.sib // RUN: %target-swift-frontend -disable-availability-checking -I %t -emit-ir %t/test.sib @@ -12,7 +12,7 @@ typealias SomeButt2 = @_opaqueReturnTypeOf("$sSi24OpaqueReturnTypeExporterE8some sil @$s24OpaqueReturnTypeExporter07exportsaB0QryF : $@convention(thin) () -> @out SomeButt sil @$sSi24OpaqueReturnTypeExporterE8someButtQryF : $@convention(thin) (Int) -> @out SomeButt2 -sil @use_opaque_type : $@convention(thin) (Int) -> () { +sil [serialized] @use_opaque_type : $@convention(thin) (Int) -> () { entry(%a : $Int): %f = function_ref @$s24OpaqueReturnTypeExporter07exportsaB0QryF : $@convention(thin) () -> @out SomeButt %x = alloc_stack $SomeButt diff --git a/test/SIL/Serialization/opaque_values_serialize.sil b/test/SIL/Serialization/opaque_values_serialize.sil index a6343685815c9..a2d572623c5e6 100644 --- a/test/SIL/Serialization/opaque_values_serialize.sil +++ b/test/SIL/Serialization/opaque_values_serialize.sil @@ -21,22 +21,22 @@ struct S : Foo { // CHECK-LABEL: sil [serialized] @castOpaque : $@convention(thin) (Int) -> () { // CHECK: bb0([[ARG:%.*]] : $Int): -// CHECK: unconditional_checked_cast_value [[ARG]] : $Int to $Foo +// CHECK: unconditional_checked_cast_value Int in [[ARG]] : $Int to Foo // CHECK-LABEL: } // end sil function 'castOpaque' sil [serialized] @castOpaque : $@convention(thin) (Int) -> () { bb0(%0 : $Int): - %c = unconditional_checked_cast_value %0 : $Int to $Foo + %c = unconditional_checked_cast_value Int in %0 : $Int to Foo %t = tuple () return %t : $() } // CHECK-LABEL: sil [serialized] @condCastOpaque : $@convention(thin) (Int) -> () { // CHECK: bb0([[ARG:%.*]] : $Int): -// CHECK: checked_cast_value_br [[ARG]] : $Int to $Int +// CHECK: checked_cast_value_br Int in [[ARG]] : $Int to Int // CHECK-LABEL: } // end sil function 'condCastOpaque' sil [serialized] @condCastOpaque : $@convention(thin) (Int) -> () { bb0(%0 : $Int): - checked_cast_value_br %0 : $Int to $Int, bb2, bb1 + checked_cast_value_br Int in %0 : $Int to Int, bb2, bb1 bb1: br bb3 diff --git a/test/SIL/Serialization/perf_inline_without_inline_all.swift b/test/SIL/Serialization/perf_inline_without_inline_all.swift index 6be70226e4fae..ceb1e24bc448a 100644 --- a/test/SIL/Serialization/perf_inline_without_inline_all.swift +++ b/test/SIL/Serialization/perf_inline_without_inline_all.swift @@ -16,5 +16,5 @@ import Swift // CHECK-NEXT: integer_literal // CHECK-NEXT: return -var a = doSomething() +public var a = doSomething() a.isBConfused() diff --git a/test/SIL/Serialization/public_non_abi.sil b/test/SIL/Serialization/public_non_abi.sil index 32f14a9db97d8..6e70a22d38288 100644 --- a/test/SIL/Serialization/public_non_abi.sil +++ b/test/SIL/Serialization/public_non_abi.sil @@ -27,4 +27,4 @@ sil hidden_external [serialized] @public_non_abi_function : $@convention(thin) ( // Make sure the function body is deserialized. // CHECK-LABEL: sil shared_external [serialized] @other_public_non_abi_function : $@convention(thin) () -> () -// CHECK: return \ No newline at end of file +// CHECK: return diff --git a/test/SIL/ownership-verifier/borrow_scope_introducing_operands.sil b/test/SIL/ownership-verifier/borrow_scope_introducing_operands.sil index dc63fc9e21a88..21ccc136d5445 100644 --- a/test/SIL/ownership-verifier/borrow_scope_introducing_operands.sil +++ b/test/SIL/ownership-verifier/borrow_scope_introducing_operands.sil @@ -100,3 +100,77 @@ bb3: %r = tuple () return %r : $() } + +// CHECK-LABEL: Function: 'parent_borrow_scope_end_before_end_borrow_coroutine' +// CHECK: Found use after free?! +// CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject +// CHECK: Consuming User: end_borrow %1 : $Builtin.NativeObject +// CHECK: Non Consuming User: end_apply %3 +// CHECK: Block: bb0 +sil [ossa] @parent_borrow_scope_end_before_end_borrow_coroutine : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %1 : $Builtin.NativeObject + end_apply %token + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: Function: 'parent_borrow_scope_end_before_end_borrow_coroutine_2' +// CHECK: Found use after free?! +// CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject +// CHECK: Consuming User: end_borrow %1 : $Builtin.NativeObject +// CHECK: Non Consuming User: abort_apply %3 +// CHECK: Block: bb0 +sil [ossa] @parent_borrow_scope_end_before_end_borrow_coroutine_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %1 : $Builtin.NativeObject + abort_apply %token + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: Function: 'parent_borrow_scope_end_before_end_borrow_coroutine_3' +// CHECK: Found use after free?! +// CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject +// CHECK: Consuming User: end_borrow %1 : $Builtin.NativeObject +// CHECK: Non Consuming User: abort_apply %3 +// CHECK: Block: bb1 + +// CHECK-LABEL: Function: 'parent_borrow_scope_end_before_end_borrow_coroutine_3' +// CHECK: Found use after free due to unvisited non lifetime ending uses?! +// CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject +// CHECK: Remaining Users: +// CHECK: User: abort_apply %3 +// CHECK: Block: bb1 + +sil [ossa] @parent_borrow_scope_end_before_end_borrow_coroutine_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + cond_br undef, bb1, bb2 + +bb1: + end_borrow %1 : $Builtin.NativeObject + abort_apply %token + br bb3 + +bb2: + end_apply %token + end_borrow %1 : $Builtin.NativeObject + br bb3 + +bb3: + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + diff --git a/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil b/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil index e69730930752e..d3b67b5a7eee7 100644 --- a/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil +++ b/test/SIL/ownership-verifier/borrow_scope_introducing_operands_positive.sil @@ -7,15 +7,6 @@ import Builtin -sil [ossa] @destroy_value_before_end_borrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { -bb0(%0 : @owned $Builtin.NativeObject): - %1 = begin_borrow %0 : $Builtin.NativeObject - end_borrow %1 : $Builtin.NativeObject - destroy_value %0 : $Builtin.NativeObject - %9999 = tuple() - return %9999 : $() -} - sil [ossa] @coroutine_callee : $@yield_once (@guaranteed Builtin.NativeObject) -> () { bb0(%0 : @guaranteed $Builtin.NativeObject): yield (), resume bb1, unwind bb2 @@ -28,6 +19,15 @@ bb2: unwind } +sil [ossa] @destroy_value_before_end_borrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + sil [ossa] @destroy_value_before_end_borrow_coroutine : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () @@ -94,3 +94,81 @@ bb3: %r = tuple () return %r : $() } + +sil [ossa] @end_parent_scope_before_end_borrow_coroutine : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_apply %token + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +sil [ossa] @end_parent_scope_before_end_borrow_coroutine_1a : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_apply %token + br bb1 + +bb1: + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +sil [ossa] @end_parent_scope_before_end_borrow_coroutine_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + abort_apply %token + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +sil [ossa] @end_parent_scope_before_end_borrow_coroutine_2b : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + abort_apply %token + br bb1 + +bb1: + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + %r = tuple () + return %r : $() +} + +sil [ossa] @positive_end_parent_scope_before_end_borrow_coroutine_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = begin_borrow %0 : $Builtin.NativeObject + %coro = function_ref @coroutine_callee : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + %token = begin_apply %coro(%1) : $@yield_once @convention(thin) (@guaranteed Builtin.NativeObject) -> () + cond_br undef, bb1, bb2 + +bb1: + abort_apply %token + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + br bb3 + +bb2: + end_apply %token + end_borrow %1 : $Builtin.NativeObject + destroy_value %0 : $Builtin.NativeObject + br bb3 + +bb3: + %r = tuple () + return %r : $() +} diff --git a/test/SIL/ownership-verifier/definite_init.sil b/test/SIL/ownership-verifier/definite_init.sil index 153c693169146..edce0907eed35 100644 --- a/test/SIL/ownership-verifier/definite_init.sil +++ b/test/SIL/ownership-verifier/definite_init.sil @@ -32,17 +32,17 @@ sil @makesInt : $@convention(thin) () -> Builtin.Int32 //} sil [ossa] @used_by_inout : $@convention(thin) (Builtin.Int32) -> (Builtin.Int32, Builtin.Int32) { bb0(%0 : $Builtin.Int32): - %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Builtin.Int32 - store %0 to [trivial] %1 : $*Builtin.Int32 - %3 = load [trivial] %1 : $*Builtin.Int32 - %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () - %6 = apply %5(%1) : $@convention(thin) (@inout Builtin.Int32) -> () - %7 = load [trivial] %1 : $*Builtin.Int32 - %8 = tuple (%3 : $Builtin.Int32, %7 : $Builtin.Int32) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } - return %8 : $(Builtin.Int32, Builtin.Int32) + %2 = alloc_box $<τ_0_0> { var τ_0_0 } + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 + store %0 to [trivial] %4 : $*Builtin.Int32 + %5 = load [trivial] %4 : $*Builtin.Int32 + %6 = function_ref @takes_Int_inout : $@convention(thin) (@inout Builtin.Int32) -> () + %7 = apply %6(%4) : $@convention(thin) (@inout Builtin.Int32) -> () + %8 = load [trivial] %4 : $*Builtin.Int32 + %9 = tuple (%5 : $Builtin.Int32, %8 : $Builtin.Int32) + destroy_value %3 : $<τ_0_0> { var τ_0_0 } + return %9 : $(Builtin.Int32, Builtin.Int32) } struct AddressOnlyStruct { @@ -57,24 +57,24 @@ sil @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct sil [ossa] @call_struct_return_function : $@convention(thin) () -> Builtin.Int32 { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Builtin.Int32 - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Builtin.Int32 } sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -87,8 +87,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @assign_test_trivial : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 { bb0(%0 : $Builtin.Int32): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Builtin.Int32 + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -97,7 +97,7 @@ bb0(%0 : $Builtin.Int32): assign %0 to %1 : $*Builtin.Int32 %2 = load [trivial] %1 : $*Builtin.Int32 - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Builtin.Int32 } @@ -108,29 +108,28 @@ bb0: // lone store), the second becomes an assignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass %4 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %4 to %c : $*SomeClass %8 = apply %f() : $@convention(thin) () -> @owned SomeClass assign %8 to %c : $*SomeClass destroy_addr %c : $*SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() } - sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %1 to %2 : $*T copy_addr [take] %1 to %2 : $*T copy_addr %2 to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -138,8 +137,8 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @assign_test_unowned : $@convention(thin) () -> () { bb0: %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -155,14 +154,12 @@ bb0: destroy_value %8 : $SomeClass destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() } - - struct ContainsNativeObject { var x : Builtin.Int32 var y : Builtin.NativeObject @@ -171,12 +168,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -337,8 +334,8 @@ sil @superinit : $@convention(method) (@owned RootClassWithIVars) -> @owned Root sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -355,11 +352,11 @@ bb0(%0 : @owned $DerivedClassWithIVars): %13 = upcast %11 : $DerivedClassWithIVars to $RootClassWithIVars %14 = function_ref @superinit : $@convention(method) (@owned RootClassWithIVars) -> @owned RootClassWithIVars %15 = apply %14(%13) : $@convention(method) (@owned RootClassWithIVars) -> @owned RootClassWithIVars - %16 = unconditional_checked_cast %15 : $RootClassWithIVars to $DerivedClassWithIVars + %16 = unconditional_checked_cast %15 : $RootClassWithIVars to DerivedClassWithIVars assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -467,10 +464,10 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -478,12 +475,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingself] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingself] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() } @@ -502,8 +499,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Builtin.Int32) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %8 = load_borrow %3 : $*DerivedClassWithIVars @@ -520,10 +517,10 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Builtin.Int32): %14 = function_ref @superinit : $@convention(method) (@owned RootClassWithIVars) -> @owned RootClassWithIVars %15 = apply %14(%b) : $@convention(method) (@owned RootClassWithIVars) -> @owned RootClassWithIVars - %16 = unconditional_checked_cast %15 : $RootClassWithIVars to $DerivedClassWithIVars + %16 = unconditional_checked_cast %15 : $RootClassWithIVars to DerivedClassWithIVars assign %16 to %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SIL/ownership-verifier/opaque_use_verifier.sil b/test/SIL/ownership-verifier/opaque_use_verifier.sil index 37b31356b9eed..eb78410b320c0 100644 --- a/test/SIL/ownership-verifier/opaque_use_verifier.sil +++ b/test/SIL/ownership-verifier/opaque_use_verifier.sil @@ -11,7 +11,7 @@ import Builtin sil [ossa] @unconditional_checked_cast_value_test : $@convention(thin) (Builtin.Int32) -> @out T { bb0(%0 : $Builtin.Int32): - %1 = unconditional_checked_cast_value %0 : $Builtin.Int32 to $T + %1 = unconditional_checked_cast_value Builtin.Int32 in %0 : $Builtin.Int32 to T return %1 : $T } @@ -72,7 +72,7 @@ bb0(%0 : @owned $AnyObject): %6 = function_ref @takeType : $@convention(thin) (@thick AnyObject.Type) -> () %8 = begin_borrow %0 : $AnyObject %9 = copy_value %8 : $AnyObject - %10 = unconditional_checked_cast_value %9 : $AnyObject to $@thick AnyObject.Type + %10 = unconditional_checked_cast_value AnyObject in %9 : $AnyObject to @thick AnyObject.Type %11 = apply %6(%10) : $@convention(thin) (@thick AnyObject.Type) -> () end_borrow %8 : $AnyObject destroy_value %0 : $AnyObject diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil index 42a12b1c0c98f..e178c87e53d0b 100644 --- a/test/SIL/ownership-verifier/over_consume.sil +++ b/test/SIL/ownership-verifier/over_consume.sil @@ -276,7 +276,7 @@ bb3: // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_guaranteed_to_owned_1' // CHECK: Have operand with incompatible ownership?! // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: guaranteed // CHECK: OwnershipMap: // CHECK: -- OperandOwnershipKindMap -- @@ -286,7 +286,7 @@ bb3: // CHECK: any: Yes. Liveness: MustBeLive sil [ossa] @checked_cast_br_mismatching_argument_guaranteed_to_owned_1 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { bb0(%0 : @guaranteed $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @owned $SuperKlass): destroy_value %1 : $SuperKlass @@ -305,11 +305,11 @@ bb3: // CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?! // CHECK: For terminator users, check that successors have compatible ownership kinds. // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: guaranteed sil [ossa] @checked_cast_br_mismatching_argument_guaranteed_to_owned_2 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { bb0(%0 : @guaranteed $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): end_borrow %1 : $SuperKlass @@ -328,11 +328,11 @@ bb3: // CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?! // CHECK: For terminator users, check that successors have compatible ownership kinds. // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: guaranteed sil [ossa] @checked_cast_br_mismatching_argument_guaranteed_to_owned_3 : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { bb0(%0 : @guaranteed $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @owned $SuperKlass): destroy_value %1 : $SuperKlass @@ -350,7 +350,7 @@ bb3: // CHECK-LABEL: Function: 'checked_cast_br_mismatching_argument_owned_to_guaranteed_1' // CHECK: Have operand with incompatible ownership?! // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: owned // CHECK: OwnershipMap: // CHECK: -- OperandOwnershipKindMap -- @@ -360,7 +360,7 @@ bb3: // CHECK: any: Yes. Liveness: MustBeLive sil [ossa] @checked_cast_br_mismatching_argument_owned_to_guaranteed_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): end_borrow %1 : $SuperKlass @@ -380,11 +380,11 @@ bb3: // CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?! // CHECK: For terminator users, check that successors have compatible ownership kinds. // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: owned sil [ossa] @checked_cast_br_mismatching_argument_owned_to_guaranteed_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): end_borrow %1 : $SuperKlass @@ -403,11 +403,11 @@ bb3: // CHECK: Ill-formed SIL! Unable to compute ownership kind map for user?! // CHECK: For terminator users, check that successors have compatible ownership kinds. // CHECK: Value: %0 = argument of bb0 : $Builtin.NativeObject -// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 +// CHECK: User: checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 // CHECK: Conv: owned sil [ossa] @checked_cast_br_mismatching_argument_owned_to_guaranteed_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @owned $SuperKlass): destroy_value %1 : $SuperKlass @@ -431,7 +431,7 @@ bb3: sil [ossa] @checked_cast_br_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): %1 = begin_borrow %0 : $Builtin.NativeObject - checked_cast_br %1 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %1 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%2 : @guaranteed $SuperKlass): end_borrow %2 : $SuperKlass diff --git a/test/SIL/ownership-verifier/undef.sil b/test/SIL/ownership-verifier/undef.sil index 74d53def70f34..ebf5f138969ab 100644 --- a/test/SIL/ownership-verifier/undef.sil +++ b/test/SIL/ownership-verifier/undef.sil @@ -20,9 +20,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%1 = mark_uninitialized [var] undef : $Klass // CHECK-NEXT: Operand Ownership Map: @@ -36,9 +36,9 @@ struct MyInt { // CHECK-NEXT: Operand Ownership Map: // CHECK-NEXT: Op #: 0 // CHECK-NEXT: Ownership Map: -- OperandOwnershipKindMap -- -// CHECK-NEXT: unowned: Yes. Liveness: MustBeLive -// CHECK-NEXT: owned: Yes. Liveness: MustBeLive -// CHECK-NEXT: guaranteed: Yes. Liveness: MustBeLive +// CHECK-NEXT: unowned: No. +// CHECK-NEXT: owned: Yes. Liveness: MustBeInvalidated +// CHECK-NEXT: guaranteed: No. // CHECK-NEXT: any: Yes. Liveness: MustBeLive // CHECK: Visiting: {{.*}}%4 = struct $MyInt (undef : $Builtin.Int32) // CHECK-NEXT: Operand Ownership Map: diff --git a/test/SIL/ownership-verifier/use_verifier.sil b/test/SIL/ownership-verifier/use_verifier.sil index 8efada1135763..0c95b2b8351d9 100644 --- a/test/SIL/ownership-verifier/use_verifier.sil +++ b/test/SIL/ownership-verifier/use_verifier.sil @@ -760,7 +760,7 @@ bb51: // We check first for objects, then for metatypes, then for guaranteed values sil [ossa] @checked_cast_br_test : $@convention(thin) (@owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %6 : @guaranteed $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @owned $SuperKlass): destroy_value %1 : $SuperKlass @@ -772,7 +772,7 @@ bb2(%2 : @owned $Builtin.NativeObject): bb3: %3 = metatype $@thick SuperKlass.Type - checked_cast_br %3 : $@thick SuperKlass.Type to $@thick SubKlass.Type, bb4, bb5 + checked_cast_br %3 : $@thick SuperKlass.Type to @thick SubKlass.Type, bb4, bb5 bb4(%4 : $@thick SubKlass.Type): br bb6 @@ -781,7 +781,7 @@ bb5(%5 : $@thick SuperKlass.Type): br bb6 bb6: - checked_cast_br %6 : $Builtin.NativeObject to $SuperKlass, bb7, bb8 + checked_cast_br %6 : $Builtin.NativeObject to SuperKlass, bb7, bb8 bb7(%7 : @guaranteed $SuperKlass): end_borrow %7 : $SuperKlass @@ -1070,7 +1070,7 @@ bb0: sil [ossa] @transforming_terminator_undef_test : $@convention(thin) () -> () { bb0: %0 = unchecked_ref_cast undef : $Builtin.NativeObject to $Builtin.NativeObject - checked_cast_br %0 : $Builtin.NativeObject to $SuperKlass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @owned $SuperKlass): destroy_value %1 : $SuperKlass @@ -1166,7 +1166,7 @@ sil [ossa] @test_unreachable_users : $@convention(method) (@guaranteed UnsafeUno bb0(%0 : @guaranteed $UnsafeUnownedFieldKlass): %3 = ref_element_addr %0 : $UnsafeUnownedFieldKlass, #UnsafeUnownedFieldKlass.x %4 = load_borrow %3 : $*@sil_unowned Builtin.NativeObject - %5 = copy_unowned_value %4 : $@sil_unowned Builtin.NativeObject + %5 = strong_copy_unowned_value %4 : $@sil_unowned Builtin.NativeObject end_borrow %4 : $@sil_unowned Builtin.NativeObject %12 = begin_borrow %5 : $Builtin.NativeObject %func = function_ref @guaranteed_nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () @@ -1182,8 +1182,17 @@ bb1: unreachable } -sil [ossa] @copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { +sil [ossa] @strong_copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : $@sil_unmanaged Builtin.NativeObject): - %1 = copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject + %1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject return %1 : $Builtin.NativeObject } + +sil [ossa] @mark_dependence_test_guaranteed : $@convention(thin) (@guaranteed Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0 : @guaranteed $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): + %2 = mark_dependence %0 : $Builtin.NativeObject on %1 : $Builtin.NativeObject + destroy_value %1 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + diff --git a/test/SILGen/Inputs/default_arg_other.swift b/test/SILGen/Inputs/default_arg_other.swift new file mode 100644 index 0000000000000..d45733916862c --- /dev/null +++ b/test/SILGen/Inputs/default_arg_other.swift @@ -0,0 +1,12 @@ + +public func foo(x: Int = 0) -> Int { x } + +public struct Subscript1 { + public init() { } + public subscript(x: Int = 0) -> Int { x } +} + +public struct Subscript2 { + public init() { } + public subscript(x: String = #function) -> String { x } +} diff --git a/test/SILGen/assignment.swift b/test/SILGen/assignment.swift index 3b8d6d4de1308..8811e5a0dbe2e 100644 --- a/test/SILGen/assignment.swift +++ b/test/SILGen/assignment.swift @@ -57,4 +57,4 @@ func stupidGames() -> ((), ()) { func assignToNestedVoid() { let _: ((), ()) = stupidGames() -} \ No newline at end of file +} diff --git a/test/SILGen/availability_attribute.swift b/test/SILGen/availability_attribute.swift index e6034348b58d4..c2bb509828e8c 100644 --- a/test/SILGen/availability_attribute.swift +++ b/test/SILGen/availability_attribute.swift @@ -33,4 +33,4 @@ public struct AvailableStruct { public struct Nested { public func availableNestedMethod() {} } -} \ No newline at end of file +} diff --git a/test/SILGen/builtins.swift b/test/SILGen/builtins.swift index 88f3dd656917f..fa7792c4c3aab 100644 --- a/test/SILGen/builtins.swift +++ b/test/SILGen/builtins.swift @@ -360,6 +360,23 @@ func projectTailElems(h: Header, ty: T.Type) -> Builtin.RawPointer { } // CHECK: } // end sil function '$s8builtins16projectTailElems1h2tyBpAA6HeaderC_xmtlF' +// Make sure we borrow if this is owned. +// +// CHECK-LABEL: sil hidden [ossa] @$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F +func projectTailElemsOwned(h: __owned Header, ty: T.Type) -> Builtin.RawPointer { + // CHECK: bb0([[ARG1:%.*]] : @owned $Header + // CHECK: [[BORROWED_ARG1:%.*]] = begin_borrow [[ARG1]] + // CHECK: [[TA:%.*]] = ref_tail_addr [[BORROWED_ARG1]] : $Header + // -- Once we have passed the address through a2p, we no longer provide any guarantees. + // -- We still need to make sure that the a2p itself is in the borrow site though. + // CHECK: [[A2P:%.*]] = address_to_pointer [[TA]] + // CHECK: end_borrow [[BORROWED_ARG1]] + // CHECK: destroy_value [[ARG1]] + // CHECK: return [[A2P]] + return Builtin.projectTailElems(h, ty) +} +// CHECK: } // end sil function '$s8builtins21projectTailElemsOwned{{[_0-9a-zA-Z]*}}F' + // CHECK-LABEL: sil hidden [ossa] @$s8builtins11getTailAddr{{[_0-9a-zA-Z]*}}F func getTailAddr(start: Builtin.RawPointer, i: Builtin.Word, ty1: T1.Type, ty2: T2.Type) -> Builtin.RawPointer { // CHECK: [[P2A:%.*]] = pointer_to_address %0 diff --git a/test/SILGen/c_function_pointers.swift b/test/SILGen/c_function_pointers.swift index cc16c452bb32c..4395a6b176e3a 100644 --- a/test/SILGen/c_function_pointers.swift +++ b/test/SILGen/c_function_pointers.swift @@ -104,4 +104,11 @@ func pointers_to_nested_local_functions_in_generics(x: T) -> Int{ func capture_list_no_captures(x: Int) { calls({ [x] in $0 }, 0) // expected-warning {{capture 'x' was never used}} -} \ No newline at end of file +} + +class Selfless { + func capture_dynamic_self() { + calls_no_args { _ = Self.self; return 0 } + // expected-error@-1 {{a C function pointer cannot be formed from a closure that captures dynamic Self type}} + } +} diff --git a/test/SILGen/casts.swift b/test/SILGen/casts.swift index e9d239c4a4993..4998bbf95970a 100644 --- a/test/SILGen/casts.swift +++ b/test/SILGen/casts.swift @@ -19,7 +19,7 @@ func downcast(b: B) -> D { func isa(b: B) -> Bool { // CHECK: bb0([[ARG:%.*]] : @guaranteed $B): // CHECK: [[COPIED_BORROWED_ARG:%.*]] = copy_value [[ARG]] - // CHECK: checked_cast_br [[COPIED_BORROWED_ARG]] : $B to $D, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] + // CHECK: checked_cast_br [[COPIED_BORROWED_ARG]] : $B to D, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // // CHECK: [[YES]]([[CASTED_VALUE:%.*]] : @owned $D): // CHECK: integer_literal {{.*}} -1 @@ -67,7 +67,7 @@ func is_archetype(b: B, _: T) -> Bool { // CHECK: } // end sil function '$s5casts12is_archetype{{[_0-9a-zA-Z]*}}F' // CHECK: sil hidden [ossa] @$s5casts20downcast_conditional{{[_0-9a-zA-Z]*}}F -// CHECK: checked_cast_br {{%.*}} : $B to $D +// CHECK: checked_cast_br {{%.*}} : $B to D // CHECK: bb{{[0-9]+}}({{.*}} : $Optional) func downcast_conditional(b: B) -> D? { return b as? D diff --git a/test/SILGen/closure_script_global_escape.swift b/test/SILGen/closure_script_global_escape.swift index 29371152aa891..2ccd1d79693fc 100644 --- a/test/SILGen/closure_script_global_escape.swift +++ b/test/SILGen/closure_script_global_escape.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-silgen -module-name foo %s | %FileCheck %s // RUN: %target-swift-emit-sil -module-name foo -verify %s +// RUN: %target-swift-frontend -emit-sil -module-name foo -verify %s -enable-ownership-stripping-after-serialization // CHECK-LABEL: sil [ossa] @main diff --git a/test/SILGen/closure_self_recursion.swift b/test/SILGen/closure_self_recursion.swift index 5d3c229046864..6c0b56d1ff0bf 100644 --- a/test/SILGen/closure_self_recursion.swift +++ b/test/SILGen/closure_self_recursion.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-emit-silgen -module-name foo %s | %FileCheck %s // RUN: %target-swift-emit-sil -module-name foo -verify %s +// RUN: %target-swift-frontend -emit-sil -module-name foo -verify %s -enable-ownership-stripping-after-serialization // CHECK-LABEL: sil [ossa] @main diff --git a/test/SILGen/closures.swift b/test/SILGen/closures.swift index 7592afafe2d45..760608ef760f7 100644 --- a/test/SILGen/closures.swift +++ b/test/SILGen/closures.swift @@ -674,7 +674,7 @@ class SuperSub : SuperBase { // CHECK: destroy_value [[SELF_COPY]] // CHECK: [[UNOWNED_SELF:%.*]] = load_borrow [[PB]] // -- strong +2, unowned +1 -// CHECK: [[SELF:%.*]] = copy_unowned_value [[UNOWNED_SELF]] +// CHECK: [[SELF:%.*]] = strong_copy_unowned_value [[UNOWNED_SELF]] // CHECK: end_borrow [[UNOWNED_SELF]] // CHECK: [[UNOWNED_SELF2:%.*]] = ref_to_unowned [[SELF]] // -- strong +2, unowned +2 @@ -714,7 +714,7 @@ class SuperSub : SuperBase { // CHECK: sil private [ossa] @[[INNER_CLOSURE_FUN:\$s8closures24UnownedSelfNestedCaptureC06nestedE0yyFACycyXEfU_ACycfU_]] : $@convention(thin) (@guaranteed @sil_unowned UnownedSelfNestedCapture) -> @owned UnownedSelfNestedCapture { // CHECK: bb0([[CAPTURED_SELF:%.*]] : @guaranteed $@sil_unowned UnownedSelfNestedCapture): // -- strong +1, unowned +1 -// CHECK: [[SELF:%.*]] = copy_unowned_value [[CAPTURED_SELF:%.*]] : +// CHECK: [[SELF:%.*]] = strong_copy_unowned_value [[CAPTURED_SELF:%.*]] : // -- strong +1, unowned +0 (claimed by return) // CHECK: return [[SELF]] // CHECK: } // end sil function '[[INNER_CLOSURE_FUN]]' diff --git a/test/SILGen/default_arg_multiple_modules.swift b/test/SILGen/default_arg_multiple_modules.swift new file mode 100644 index 0000000000000..2deff092a9457 --- /dev/null +++ b/test/SILGen/default_arg_multiple_modules.swift @@ -0,0 +1,53 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path=%t/default_arg_other.swiftmodule -module-name=default_arg_other %S/Inputs/default_arg_other.swift +// RUN: %target-swift-emit-silgen -module-name default_arg_multiple_modules -I %t %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime + +import default_arg_other + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test1{{.*}} +func test1() -> Int { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other3foo1xS2i_tFfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other3foo1xS2i_tF : $@convention(thin) (Int) -> Int + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]]) {{.*}} + // CHECK: return [[CALL]] : $Int + return foo() +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test2{{.*}} +func test2() -> Int { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icig : $@convention(method) (Int, Subscript1) -> Int + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]], {{.*}} + // CHECK: return [[CALL]] : $Int + return Subscript1()[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test3{{.*}} +func test3() -> String { + // This should not call default arg constructor + // CHECK: [[STR_LIT:%[0-9]+]] = string_literal utf8 "test3()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STR_LIT]], {{.*}} + // CHECK: [[FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript2VyS2Scig : $@convention(method) (@guaranteed String, Subscript2) -> @owned String + // CHECK: [[CALL:%[0-9]+]] = apply [[FN]]([[DEF_ARG]], {{.*}} + // CHECK: return [[CALL]] : $String + return Subscript2()[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test4{{.*}} +func test4() { + // CHECK: [[DEF_ARG_FN:%[0-9]+]] = function_ref @$s17default_arg_other10Subscript1VyS2icipfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF_ARG:%[0-9]+]] = apply [[DEF_ARG_FN]]() {{.*}} + // CHECK: keypath $KeyPath, (root $Subscript1; gettable_property $Int, id @$s17default_arg_other10Subscript1VyS2icig : $@convention(method) (Int, Subscript1) -> Int, getter @$s17default_arg_other10Subscript1VyS2icipACTK : $@convention(thin) (@in_guaranteed Subscript1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int, external #Subscript1.subscript) ([[DEF_ARG]]) + _ = \Subscript1.[] +} + +// CHECK-LABEL: sil hidden [ossa] @${{.*}}test5{{.*}} +func test5() { + // This should not call default arg constructor + // CHECK: [[STR_LIT:%[0-9]+]] = string_literal utf8 "test5()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STR_LIT]], {{.*}} + // CHECK: keypath $KeyPath, (root $Subscript2; gettable_property $String, id @$s17default_arg_other10Subscript2VyS2Scig : $@convention(method) (@guaranteed String, Subscript2) -> @owned String, getter @$s17default_arg_other10Subscript2VyS2ScipACTK : $@convention(thin) (@in_guaranteed Subscript2, UnsafeRawPointer) -> @out String, indices [%$0 : $String : $String], indices_equals @$sSSTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSSTh : $@convention(thin) (UnsafeRawPointer) -> Int, external #Subscript2.subscript) ([[DEF_ARG]]) + _ = \Subscript2.[] +} diff --git a/test/SILGen/default_arguments.swift b/test/SILGen/default_arguments.swift index e02df77f39b56..fa7b9a5486577 100644 --- a/test/SILGen/default_arguments.swift +++ b/test/SILGen/default_arguments.swift @@ -84,6 +84,25 @@ func testMagicLiterals(file: String = #file, // // NEGATIVE-NOT: sil hidden [ossa] @$s17default_arguments17testMagicLiteralsySS4file_SS8functionSi4lineSi6columntFfA2_ +// SR-11623 +func genericMagicLiteral(_ x: T = #column) -> T { x } + +// CHECK-LABEL: sil hidden [ossa] @$s17default_arguments23testGenericMagicLiteralyyF +func testGenericMagicLiteral() { + // CHECK: [[RET:%[0-9]+]] = alloc_stack $Int + // CHECK-NEXT: [[RAWLIT:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 35 + // CHECK-NEXT: [[INTTY:%[0-9]+]] = metatype $@thin Int.Type + // CHECK-NEXT: // function_ref Swift.Int.init(_builtinIntegerLiteral: Builtin.IntLiteral) -> Swift.Int + // CHECK-NEXT: [[LITFN:%[0-9]+]] = function_ref @$sSi22_builtinIntegerLiteralSiBI_tcfC + // CHECK-NEXT: [[LIT:%[0-9]+]] = apply [[LITFN]]([[RAWLIT]], [[INTTY]]) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int + // CHECK-NEXT: [[LITARG:%[0-9]+]] = alloc_stack $Int + // CHECK-NEXT: store [[LIT]] to [trivial] [[LITARG]] : $*Int + // CHECK-NEXT: // function_ref default_arguments.genericMagicLiteral(A) -> A + // CHECK-NEXT: [[FN:%[0-9]+]] = function_ref @$s17default_arguments19genericMagicLiteralyxxs020ExpressibleByIntegerE0RzlF : $@convention(thin) <τ_0_0 where τ_0_0 : ExpressibleByIntegerLiteral> (@in_guaranteed τ_0_0) -> @out τ_0_0 + // CHECK-NEXT: apply [[FN]]([[RET]], [[LITARG]]) : $@convention(thin) <τ_0_0 where τ_0_0 : ExpressibleByIntegerLiteral> (@in_guaranteed τ_0_0) -> @out τ_0_0 + let _: Int = genericMagicLiteral() +} + func closure(_: () -> ()) {} func autoclosure(_: @autoclosure () -> ()) {} @@ -383,3 +402,42 @@ func stupidGames(x: Int = 3) -> Int { return x } stupidGames(x:)() + +func genericMagic(x: T = #file) -> T { + return x +} + +let _: String = genericMagic() + +// SR-11778 +struct CallableWithDefault { + func callAsFunction(x: Int = 4) {} + func callAsFunction(y: Int, z: String = #function) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s17default_arguments23testCallableWithDefaultyyAA0deF0VF : $@convention(thin) (CallableWithDefault) -> () +func testCallableWithDefault(_ x: CallableWithDefault) { + // CHECK: [[DEF_FN:%[0-9]+]] = function_ref @$s17default_arguments19CallableWithDefaultV14callAsFunction1xySi_tFfA_ : $@convention(thin) () -> Int + // CHECK: [[DEF:%[0-9]+]] = apply [[DEF_FN]]() : $@convention(thin) () -> Int + // CHECK: [[CALL_AS_FN:%[0-9]+]] = function_ref @$s17default_arguments19CallableWithDefaultV14callAsFunction1xySi_tF : $@convention(method) (Int, CallableWithDefault) -> () + // CHECK: apply [[CALL_AS_FN]]([[DEF]], {{%[0-9]+}}) + x() + + // CHECK: [[RAW_I:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 5 + // CHECK: [[I:%[0-9]+]] = apply {{%[0-9]+}}([[RAW_I]], {{%[0-9]+}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int + // CHECK: [[RAW_STR:%[0-9]+]] = string_literal utf8 "testCallableWithDefault(_:)" + // CHECK: [[STR:%[0-9]+]] = apply {{%[0-9]+}}([[RAW_STR]], {{%[0-9]+}}, {{%[0-9]+}}, {{%[0-9]+}}) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + // CHECK: [[CALL_AS_FN:%[0-9]+]] = function_ref @$s17default_arguments19CallableWithDefaultV14callAsFunction1y1zySi_SStF : $@convention(method) (Int, @guaranteed String, CallableWithDefault) -> () + // CHECK: apply [[CALL_AS_FN]]([[I]], [[STR]], {{%[0-9]+}}) + x(y: 5) +} + +// FIXME: Arguably we shouldn't allow calling a constructor like this, as +// we usually require the user write an explicit '.init'. +struct WeirdUMEInitCase { + static let ty = WeirdUMEInitCase.self + init(_ x: Int = 0) {} +} + +let _: WeirdUMEInitCase = .ty() +let _: WeirdUMEInitCase = .ty(5) diff --git a/test/SILGen/default_constructor.swift b/test/SILGen/default_constructor.swift index 6c01d71ecbcaa..7f2b1fb933556 100644 --- a/test/SILGen/default_constructor.swift +++ b/test/SILGen/default_constructor.swift @@ -132,4 +132,4 @@ struct S { let value2: Int // CHECK-LABEL: sil hidden [ossa] @$s19default_constructor1SV6value2ACyxGSi_tcfC : $@convention(method) (Int, @thin S.Type) -> @out S -} \ No newline at end of file +} diff --git a/test/SILGen/dynamic_lookup.swift b/test/SILGen/dynamic_lookup.swift index 1c19ff7c341dc..253cf438a322f 100644 --- a/test/SILGen/dynamic_lookup.swift +++ b/test/SILGen/dynamic_lookup.swift @@ -14,6 +14,8 @@ class X { } set {} } + + @objc func hasDefaultParam(_ x: Int = 0) {} } @objc protocol P { @@ -304,7 +306,7 @@ func downcast(_ obj: AnyObject) -> X { // CHECK: store [[OBJ_COPY]] to [init] [[PBOBJ]] : $*AnyObject // CHECK: [[READ:%.*]] = begin_access [read] [unknown] [[PBOBJ]] // CHECK: [[OBJ:%[0-9]+]] = load [copy] [[READ]] : $*AnyObject - // CHECK: [[X:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to $X + // CHECK: [[X:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to X // CHECK: destroy_value [[OBJ_BOX]] : ${ var AnyObject } // CHECK: return [[X]] : $X return obj as! X @@ -356,3 +358,13 @@ func getIUOPropertyDynamically(x: AnyObject) -> Any { return x.iuoProperty } +// SR-11648 +// CHECK-LABEL: sil hidden [ossa] @$s14dynamic_lookup24testAnyObjectWithDefaultyyyXlF +func testAnyObjectWithDefault(_ x: AnyObject) { + // CHECK: function_ref default argument 0 of X.hasDefaultParam(_:) + // CHECK: [[DEFGEN:%[0-9]+]] = function_ref @$s14dynamic_lookup1XC15hasDefaultParamyySiFfA_ : $@convention(thin) () -> Int + // CHECK: [[DEFARG:%[0-9]+]] = apply %4() : $@convention(thin) () -> Int + // CHECK: [[METHOD:%[0-9]+]] = objc_method [[OPENEDX:%[0-9]+]] : $@opened("{{.*}}") AnyObject, #X.hasDefaultParam!1.foreign : (X) -> (Int) -> (), $@convention(objc_method) (Int, @opened("{{.*}}") AnyObject) -> () + // CHECK: apply [[METHOD]]([[DEFARG]], [[OPENEDX]]) + x.hasDefaultParam() +} diff --git a/test/SILGen/dynamic_self.swift b/test/SILGen/dynamic_self.swift index 9f1ac813f0906..800872a32c96e 100644 --- a/test/SILGen/dynamic_self.swift +++ b/test/SILGen/dynamic_self.swift @@ -442,6 +442,25 @@ public class FunctionConversionTest : EmptyProtocol { } } +public class CaptureTwoValuesTest { + public required init() {} + + // CHECK-LABEL: sil [ossa] @$s12dynamic_self20CaptureTwoValuesTestC08capturesdE0yyFZ : $@convention(method) (@thick CaptureTwoValuesTest.Type) -> () { + public static func capturesTwoValues() { + let a = Self() + let b = Self() + + // CHECK: function_ref @$s12dynamic_self20CaptureTwoValuesTestC08capturesdE0yyFZyycfU_ : $@convention(thin) (@guaranteed CaptureTwoValuesTest, @guaranteed CaptureTwoValuesTest) -> () + _ = { + _ = a + _ = b + _ = Self.self + } + + // CHECK-LABEL: sil private [ossa] @$s12dynamic_self20CaptureTwoValuesTestC08capturesdE0yyFZyycfU_ : $@convention(thin) (@guaranteed CaptureTwoValuesTest, @guaranteed CaptureTwoValuesTest) -> () { + } +} + // CHECK-LABEL: sil_witness_table hidden X: P module dynamic_self { // CHECK: method #P.f!1: {{.*}} : @$s12dynamic_self1XCAA1PA2aDP1f{{[_0-9a-zA-Z]*}}FTW diff --git a/test/SILGen/dynamic_self_cast.swift b/test/SILGen/dynamic_self_cast.swift new file mode 100644 index 0000000000000..88d5bbac84680 --- /dev/null +++ b/test/SILGen/dynamic_self_cast.swift @@ -0,0 +1,79 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +public class SelfCasts { + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC02toD0yACXDACFZ : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned SelfCasts { + // CHECK: unconditional_checked_cast {{.*}} : $SelfCasts to @dynamic_self SelfCasts + // CHECK: } + public static func toSelf(_ s: SelfCasts) -> Self { + return s as! Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC09genericToD0yACXDxlFZ : $@convention(method) (@in_guaranteed T, @thick SelfCasts.Type) -> @owned SelfCasts { + // CHECK: unconditional_checked_cast_addr T in {{.*}} : $*T to @dynamic_self SelfCasts in {{.*}} : $*SelfCasts + // CHECK: } + public static func genericToSelf(_ s: T) -> Self { + return s as! Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC014classGenericToD0yACXDxRlzClFZ : $@convention(method) (@guaranteed T, @thick SelfCasts.Type) -> @owned SelfCasts { + // CHECK: unconditional_checked_cast {{.*}} : $T to @dynamic_self SelfCasts + // CHECK: } + public static func classGenericToSelf(_ s: T) -> Self { + return s as! Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC011genericFromD0xylFZ : $@convention(method) (@thick SelfCasts.Type) -> @out T { + // CHECK: unconditional_checked_cast_addr @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T + // CHECK: } + public static func genericFromSelf() -> T { + let s = Self() + return s as! T + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ : $@convention(method) (@thick SelfCasts.Type) -> @owned T + // CHECK: unconditional_checked_cast_addr @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T + // CHECK: } + public static func classGenericFromSelf() -> T { + let s = Self() + return s as! T + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC02toD11ConditionalyACXDSgACFZ : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional { + // CHECK: checked_cast_br {{.*}} : $SelfCasts to @dynamic_self SelfCasts + // CHECK: } + public static func toSelfConditional(_ s: SelfCasts) -> Self? { + return s as? Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC09genericToD11ConditionalyACXDSgxlFZ : $@convention(method) (@in_guaranteed T, @thick SelfCasts.Type) -> @owned Optional { + // CHECK: checked_cast_addr_br take_always T in {{.*}} : $*T to @dynamic_self SelfCasts in {{.*}} : $*SelfCasts + // CHECK: } + public static func genericToSelfConditional(_ s: T) -> Self? { + return s as? Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC014classGenericToD11ConditionalyACXDSgxRlzClFZ : $@convention(method) (@guaranteed T, @thick SelfCasts.Type) -> @owned Optional { + // CHECK: checked_cast_br {{.*}} : $T to @dynamic_self SelfCasts + // CHECK: } + public static func classGenericToSelfConditional(_ s: T) -> Self? { + return s as? Self + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC011genericFromD11ConditionalxSgylFZ : $@convention(method) (@thick SelfCasts.Type) -> @out Optional { + // CHECK: checked_cast_addr_br take_always @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T + // CHECK: } + public static func genericFromSelfConditional() -> T? { + let s = Self() + return s as? T + } + + // CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ : $@convention(method) (@thick SelfCasts.Type) -> @owned Optional { + // CHECK: checked_cast_addr_br take_always @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T + // CHECK: } + public static func classGenericFromSelfConditional() -> T? { + let s = Self() + return s as? T + } + + public required init() {} +} diff --git a/test/SILGen/dynamically_replaceable.swift b/test/SILGen/dynamically_replaceable.swift index 59dcea038206f..79e64fd6120dc 100644 --- a/test/SILGen/dynamically_replaceable.swift +++ b/test/SILGen/dynamically_replaceable.swift @@ -404,3 +404,17 @@ public func testWithLocalFun() { let unamedClosure = { print("foo") } unamedClosure() } + +@propertyWrapper +struct WrapperWithInitialValue { + var wrappedValue: T + + init(wrappedValue initialValue: T) { + self.wrappedValue = initialValue + } +} + +// CHECK-LABEL: sil hidden [ossa] @$s23dynamically_replaceable10SomeStructV1tSbvpfP +public struct SomeStruct { + @WrapperWithInitialValue var t = false +} diff --git a/test/SILGen/errors.swift b/test/SILGen/errors.swift index c2ff34e6fac51..ef9de65a642b9 100644 --- a/test/SILGen/errors.swift +++ b/test/SILGen/errors.swift @@ -269,7 +269,6 @@ class HasThrowingInit { // CHECK: bb1([[SELF:%.*]] : @owned $HasThrowingInit): // CHECK-NEXT: return [[SELF]] // CHECK: bb2([[ERROR:%.*]] : @owned $Error): -// CHECK-NEXT: builtin "willThrow" // CHECK-NEXT: throw [[ERROR]] // CHECK-LABEL: sil hidden [ossa] @$s6errors15HasThrowingInit{{.*}} : $@convention(method) (Int, @owned HasThrowingInit) -> (@owned HasThrowingInit, @error Error) { @@ -320,7 +319,6 @@ protocol Doomed { // CHECK: [[T0:%.*]] = tuple () // CHECK: return [[T0]] : $() // CHECK: bb2([[T0:%.*]] : @owned $Error): -// CHECK: builtin "willThrow"([[T0]] : $Error) // CHECK: throw [[T0]] : $Error struct DoomedStruct : Doomed { func check() throws {} @@ -335,7 +333,6 @@ struct DoomedStruct : Doomed { // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: return [[T0]] : $() // CHECK: bb2([[T0:%.*]] : @owned $Error): -// CHECK: builtin "willThrow"([[T0]] : $Error) // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: throw [[T0]] : $Error class DoomedClass : Doomed { @@ -376,7 +373,6 @@ func testThunk(_ fn: () throws -> Int) throws -> Int { // CHECK: [[T0:%.*]] = tuple () // CHECK: return [[T0]] // CHECK: bb2([[T0:%.*]] : @owned $Error): -// CHECK: builtin "willThrow"([[T0]] : $Error) // CHECK: throw [[T0]] : $Error func createInt(_ fn: () -> Int) throws {} diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index 7e9cb28f52558..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift @@ -234,7 +234,7 @@ class D : B { // CHECK-LABEL: sil hidden [ossa] @$s11expressions8downcast{{[_0-9a-zA-Z]*}}F func downcast(_ x: B) -> D { return x as! D - // CHECK: unconditional_checked_cast %{{[0-9]+}} : {{.*}} to $D + // CHECK: unconditional_checked_cast %{{[0-9]+}} : {{.*}} to D } // CHECK-LABEL: sil hidden [ossa] @$s11expressions6upcast{{[_0-9a-zA-Z]*}}F @@ -253,7 +253,7 @@ func generic_upcast(_ x: T) -> B { // CHECK-LABEL: sil hidden [ossa] @$s11expressions16generic_downcast{{[_0-9a-zA-Z]*}}F func generic_downcast(_ x: T, y: B) -> T { return y as! T - // CHECK: unconditional_checked_cast %{{[0-9]+}} : {{.*}} to $T + // CHECK: unconditional_checked_cast %{{[0-9]+}} : {{.*}} to T // CHECK: return } diff --git a/test/SILGen/force_cast_chained_optional.swift b/test/SILGen/force_cast_chained_optional.swift index b40b138676b4a..8b733a7b29974 100644 --- a/test/SILGen/force_cast_chained_optional.swift +++ b/test/SILGen/force_cast_chained_optional.swift @@ -25,7 +25,7 @@ class D: C {} // CHECK: [[METHOD:%.*]] = class_method [[BORROWED_BAR]] : $Bar, #Bar.bas!getter.1 : (Bar) -> () -> C?, $@convention(method) (@guaranteed Bar) -> // CHECK: apply [[METHOD]]([[BORROWED_BAR]]) // CHECK: end_borrow [[BORROWED_BAR]] -// CHECK: unconditional_checked_cast {{%.*}} : $C to $D +// CHECK: unconditional_checked_cast {{%.*}} : $C to D // // CHECK: [[NO_BAR]]: // CHECK: unreachable diff --git a/test/SILGen/foreach.swift b/test/SILGen/foreach.swift index cfb7b67d61241..5c67eff05efd7 100644 --- a/test/SILGen/foreach.swift +++ b/test/SILGen/foreach.swift @@ -108,15 +108,15 @@ func trivialStructBreak(_ xx: [Int]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[Int]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[GET_ELT_STACK:%.*]] = alloc_stack $Optional // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[GET_ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[GET_ELT_STACK]], [[WRITE]]) // CHECK: [[IND_VAR:%.*]] = load [trivial] [[GET_ELT_STACK]] // CHECK: switch_enum [[IND_VAR]] : $Optional, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // @@ -208,15 +208,15 @@ func existentialBreak(_ xx: [P]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array

// CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array

, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[P]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional

// CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional

, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: @@ -368,15 +368,15 @@ func genericStructBreak(_ xx: [GenericStruct]) { // CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] // CHECK: [[BORROWED_ARRAY_STACK:%.*]] = alloc_stack $Array> // CHECK: store [[ARRAY_COPY:%.*]] to [init] [[BORROWED_ARRAY_STACK]] -// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = function_ref @$sSlss16IndexingIteratorVyxG0B0RtzrlE04makeB0ACyF -// CHECK: apply [[MAKE_ITERATOR_FUNC]]>>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) +// CHECK: [[MAKE_ITERATOR_FUNC:%.*]] = witness_method $Array>, #Sequence.makeIterator!1 : (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator +// CHECK: apply [[MAKE_ITERATOR_FUNC]]<[GenericStruct]>([[PROJECT_ITERATOR_BOX]], [[BORROWED_ARRAY_STACK]]) // CHECK: [[ELT_STACK:%.*]] = alloc_stack $Optional> // CHECK: br [[LOOP_DEST:bb[0-9]+]] // // CHECK: [[LOOP_DEST]]: // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PROJECT_ITERATOR_BOX]] : $*IndexingIterator>> -// CHECK: [[FUNC_REF:%.*]] = function_ref @$ss16IndexingIteratorV4next7ElementQzSgyF : $@convention(method) -// CHECK: apply [[FUNC_REF]]>>([[ELT_STACK]], [[WRITE]]) +// CHECK: [[FUNC_REF:%.*]] = witness_method $IndexingIterator>>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> +// CHECK: apply [[FUNC_REF]]>>>([[ELT_STACK]], [[WRITE]]) // CHECK: switch_enum_addr [[ELT_STACK]] : $*Optional>, case #Optional.some!enumelt.1: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] // // CHECK: [[SOME_BB]]: diff --git a/test/SILGen/foreign_errors.swift b/test/SILGen/foreign_errors.swift index e49be295066f6..436193992dfad 100644 --- a/test/SILGen/foreign_errors.swift +++ b/test/SILGen/foreign_errors.swift @@ -51,6 +51,7 @@ func test0() throws { // CHECK: [[T0:%.*]] = load [take] [[ERR_TEMP0]] // CHECK: [[T1:%.*]] = function_ref @$s10Foundation22_convertNSErrorToErrorys0E0_pSo0C0CSgF : $@convention(thin) (@guaranteed Optional) -> @owned Error // CHECK: [[T2:%.*]] = apply [[T1]]([[T0]]) + // CHECK: "willThrow"([[T2]] : $Error) // CHECK: throw [[T2]] : $Error } @@ -161,6 +162,7 @@ let fn = ErrorProne.fail // CHECK: return // CHECK: [[T0:%.*]] = load [take] [[TEMP]] // CHECK: [[T1:%.*]] = apply {{%.*}}([[T0]]) +// CHECK: "willThrow"([[T1]] : $Error) // CHECK: throw [[T1]] func testArgs() throws { diff --git a/test/SILGen/function_type_conversion.swift b/test/SILGen/function_type_conversion.swift new file mode 100644 index 0000000000000..3e2aab4fbbdca --- /dev/null +++ b/test/SILGen/function_type_conversion.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-frontend -emit-silgen -disable-availability-checking -module-name main -enable-subst-sil-function-types-for-function-values %s | %FileCheck %s + +func generic(_ f: @escaping (T) -> U) -> (T) -> U { return f } + +// CHECK-LABEL: sil {{.*}}4main{{.*}}11sameGeneric +func sameGeneric(_: X, _: Y, _ f: @escaping (Z) -> Z) -> (Z) -> Z { +// CHECK: bb0({{.*}}, [[F:%[0-9]+]] : @guaranteed $@callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for + // Similarly generic types should be directly substitutable + // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic + // CHECK: [[RET:%.*]] = apply [[GENERIC]]([[F]]) + // CHECK: return [[RET]] + return generic(f) +} + +// CHECK-LABEL: sil {{.*}}4main{{.*}}16concreteIndirect +func concreteIndirect(_ f: @escaping (Any) -> Any) -> (Any) -> Any { +// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed (@in_guaranteed Any) -> @out Any) + // Any is passed indirectly, but is a concrete type, so we need to convert + // to the generic abstraction level + // CHECK: [[F2:%.*]] = copy_value [[F]] + // CHECK: [[GENERIC_F:%.*]] = convert_function [[F2]] : {{.*}} to $@callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for + // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic + // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]([[GENERIC_F]]) + // CHECK: [[RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}} + // CHECK: return [[RET]] + return generic(f) +} + +// CHECK-LABEL: sil {{.*}}4main{{.*}}14concreteDirect +func concreteDirect(_ f: @escaping (Int) -> String) -> (Int) -> String { +// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed (Int) -> @owned String): + // Int and String are passed and returned directly, so we need both + // thunking and conversion to the substituted form + // CHECK: [[F2:%.*]] = copy_value [[F]] + // CHECK: [[REABSTRACT_F:%.*]] = partial_apply {{.*}}([[F2]]) + // CHECK: [[GENERIC_F:%.*]] = convert_function [[REABSTRACT_F]] : {{.*}} to $@callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for + // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}7generic + // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]([[GENERIC_F]]) + // CHECK: [[REABSTRACT_RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}} + // CHECK: [[RET:%.*]] = partial_apply {{.*}}([[REABSTRACT_RET]]) + // CHECK: return [[RET]] + + return generic(f) +} + +func genericTakesFunction( + _ f: @escaping ((T) -> U) -> (T) -> U +) -> ((T) -> U) -> (T) -> U { return f } + +func sameGenericTakesFunction( + _ f: @escaping ((T) -> T) -> (T) -> T +) -> ((T) -> T) -> (T) -> T { + return genericTakesFunction(f) +} + +// CHECK-LABEL: sil {{.*}}4main29concreteIndirectTakesFunction +func concreteIndirectTakesFunction( + _ f: @escaping ((Any) -> Any) -> (Any) -> Any +) -> ((Any) -> Any) -> (Any) -> Any { +// CHECK: bb0([[F:%[0-9]+]] : @guaranteed $@callee_guaranteed + + // Calling convention matches callee, but the representation of the argument + // to `f` needs to change, so we still have to thunk + // CHECK: [[F2:%.*]] = copy_value [[F]] + // CHECK: [[REABSTRACT_F:%.*]] = partial_apply {{.*}}([[F2]]) + // CHECK: [[GENERIC_F:%.*]] = convert_function [[REABSTRACT_F]] + // CHECK: [[GENERIC:%.*]] = function_ref @{{.*}}4main{{.*}}20genericTakesFunction + // CHECK: [[GENERIC_RET:%.*]] = apply [[GENERIC]]([[GENERIC_F]]) + // CHECK: [[REABSTRACT_RET:%.*]] = convert_function [[GENERIC_RET]] : {{.*}} + // CHECK: [[RET:%.*]] = partial_apply {{.*}}([[REABSTRACT_RET]]) + // CHECK: return [[RET]] + return genericTakesFunction(f) +} + +func concreteDirectTakesFunction( + _ f: @escaping ((Int) -> String) -> (Int) -> String +) -> ((Int) -> String) -> (Int) -> String { + // Int and String are passed and returned directly, so we need both + // thunking and conversion to the substituted form + return genericTakesFunction(f) +} diff --git a/test/SILGen/function_type_lowering.swift b/test/SILGen/function_type_lowering.swift new file mode 100644 index 0000000000000..a1328dca1eb5f --- /dev/null +++ b/test/SILGen/function_type_lowering.swift @@ -0,0 +1,129 @@ +// RUN: %target-swift-frontend -emit-silgen -disable-availability-checking -module-name main -enable-subst-sil-function-types-for-function-values %s | %FileCheck %s + + +// Similarly-abstract generic signatures should share an unsubstituted type +// even in different generic contexts + +// CHECK-LABEL: sil {{.*}}1a{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func a(_ x: (T) -> U) {} + +// CHECK-LABEL: sil {{.*}}1b{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func b(_ x: (U) -> T) {} + +// CHECK-LABEL: sil {{.*}}1c{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for , @in_guaranteed U) -> () +func c(_ x: (V) -> T, _: U) {} + +// CHECK-LABEL: sil {{.*}}003Hca{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func ç(_ x: (T) -> T) {} + + +// ...including unconstrained associated types + +protocol P { + associatedtype A +} + +// CHECK-LABEL: sil {{.*}}1d{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func d(_ x: (T) -> T.A) {} + +// CHECK-LABEL: sil {{.*}}1e{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1> in (@in_guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func e(_ x: (T.A) -> T) {} + + +// Preserve class constraints, because they're less abstract for layout and +// calling convention purposes than unconstrained types + +// CHECK-LABEL: sil {{.*}}1f{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func f(_ x: (T) -> U) {} + +// CHECK-LABEL: sil {{.*}}1g{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func g(_ x: (U) -> T) {} + +// CHECK-LABEL: sil {{.*}}1h{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func h(_ x: (T) -> U) {} + +// CHECK-LABEL: sil {{.*}}1i{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func i(_ x: (U) -> T) {} + + +// Indirect class constraints + +protocol PC: AnyObject { } + +// CHECK-LABEL: sil {{.*}}1j{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func j(_ x: (T) -> U) {} + +// CHECK-LABEL: sil {{.*}}1k{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func k(_ x: (U) -> T) {} + +// CHECK-LABEL: sil {{.*}}1l{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func l(_ x: (T) -> U) {} + +// CHECK-LABEL: sil {{.*}}1m{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func m(_ x: (U) -> T) {} + +// CHECK-LABEL: sil {{.*}}1n{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func n(_ x: (T) -> T.A) {} + + +// Superclass constraints + +class Base {} + +// CHECK-LABEL: sil {{.*}}1o{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func o (_ x: (T) -> U) {} + + +// Indirect constraints by associated type or protocol + +protocol PCAO: AnyObject { + associatedtype A +} + +protocol POAC { + associatedtype A: AnyObject +} + +protocol PCAC: AnyObject { + associatedtype A: AnyObject +} + +// CHECK-LABEL: sil {{.*}}1p{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> @out τ_0_1 for ) -> () +func p (_ x: (T) -> T.A) {} +// CHECK-LABEL: sil {{.*}}1q{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_1 : _RefCountedObject> in (@in_guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func q (_ x: (T) -> T.A) {} +// CHECK-LABEL: sil {{.*}}1r{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1 where τ_0_0 : _RefCountedObject, τ_0_1 : _RefCountedObject> in (@guaranteed τ_0_0) -> @owned τ_0_1 for ) -> () +func r (_ x: (T) -> T.A) {} + + +// Structural positions + +struct S {} + +// CHECK-LABEL: sil {{.*}}1t{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_1 : _RefCountedObject, τ_0_3 : _RefCountedObject> in (S<τ_0_0, τ_0_1>) -> (@out τ_0_2, @owned τ_0_3) for ) -> () +func t(_: (S) -> (T, U)) {} + +// CHECK-LABEL: sil {{.*}}1u{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2> in (S<τ_0_0, τ_0_1>) -> @out τ_0_2 for ) -> () +func u(_: (S) -> T) {} + + +class C {} + +// CHECK-LABEL: sil {{.*}}1v{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_1 : _RefCountedObject, τ_0_3 : _RefCountedObject> in (@guaranteed C<τ_0_0, τ_0_1>) -> (@out τ_0_2, @owned τ_0_3) for ) -> () +func v(_: (C) -> (T, U)) {} + +// CHECK-LABEL: sil {{.*}}1w{{.*}} : $@convention(thin) (@noescape @callee_guaranteed <τ_0_0, τ_0_1, τ_0_2> in (@guaranteed C<τ_0_0, τ_0_1>) -> @out τ_0_2 for ) -> () +func w(_: (C) -> T) {} + +// CHECK-LABEL: sil {{.*}}1x{{.*}} : $@convention(thin) > (@noescape @callee_guaranteed <τ_0_0 where τ_0_0 : _RefCountedObject> in (@guaranteed τ_0_0) -> () for ) -> () +func x>(_: (V) -> Void) {} + + +// Opaque types should not be extracted as substituted arguments because they +// aren't freely substitutable. + +dynamic func opaqueAny() -> some Any { return C() } +dynamic func opaqueObject() -> some AnyObject { return C() } + +// CHECK-LABEL: sil {{.*}}1y{{.*}} : $@convention(thin) (@noescape @callee_guaranteed (@in_guaranteed @_opaqueReturnTypeOf("$s4main9opaqueAnyQryF", 0) {{.*}}) -> @owned @_opaqueReturnTypeOf("$s4main12opaqueObjectQryF", 0) {{.*}}) -> () +func y(_: (@_opaqueReturnTypeOf("$s4main9opaqueAnyQryF", 0) X) -> (@_opaqueReturnTypeOf("$s4main12opaqueObjectQryF", 0) X)) {} diff --git a/test/SILGen/functions_uninhabited_param.swift b/test/SILGen/functions_uninhabited_param.swift index c71b98b5bc229..462498393e55c 100644 --- a/test/SILGen/functions_uninhabited_param.swift +++ b/test/SILGen/functions_uninhabited_param.swift @@ -8,3 +8,10 @@ func foo(baz: Never) -> Int { // expected-note {{'baz' is uninhabited, so this f } func bar(baz: Never) -> Int {} // ok + +// We used to crash when emitting the closure below. +enum E { + static func f(_: E) {} +} + +let _: (E.Type) -> (E) -> () = { s in { e in s.f(e) } } diff --git a/test/SILGen/generic_casts.swift b/test/SILGen/generic_casts.swift index 52beabc12e53c..b88328a7695c7 100644 --- a/test/SILGen/generic_casts.swift +++ b/test/SILGen/generic_casts.swift @@ -107,7 +107,7 @@ func opaque_archetype_is_loadable_concrete func class_archetype_to_class (_ t:T) -> C { return t as! C - // CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} to $C + // CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} to C // CHECK: return [[DOWNCAST]] : $C } @@ -115,7 +115,7 @@ func class_archetype_to_class func class_archetype_is_class (_ t:T) -> Bool { return t is C - // CHECK: checked_cast_br {{%.*}} to $C + // CHECK: checked_cast_br {{%.*}} to C } // CHECK-LABEL: sil hidden [ossa] @$s13generic_casts022opaque_existential_to_C10_archetype{{[_0-9a-zA-Z]*}}F @@ -200,20 +200,20 @@ func opaque_existential_is_loadable_concrete(_ p: NotClassBound) -> Bool { // CHECK-LABEL: sil hidden [ossa] @$s13generic_casts021class_existential_to_C0{{[_0-9a-zA-Z]*}}F func class_existential_to_class(_ p: ClassBound) -> C { return p as! C - // CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} to $C + // CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} to C // CHECK: return [[DOWNCAST]] : $C } // CHECK-LABEL: sil hidden [ossa] @$s13generic_casts021class_existential_is_C0{{[_0-9a-zA-Z]*}}F func class_existential_is_class(_ p: ClassBound) -> Bool { return p is C - // CHECK: checked_cast_br {{%.*}} to $C + // CHECK: checked_cast_br {{%.*}} to C } // CHECK-LABEL: sil hidden [ossa] @$s13generic_casts27optional_anyobject_to_classyAA1CCSgyXlSgF func optional_anyobject_to_class(_ p: AnyObject?) -> C? { return p as? C - // CHECK: checked_cast_br {{%.*}} : $AnyObject to $C + // CHECK: checked_cast_br {{%.*}} : $AnyObject to C } // The below tests are to ensure we don't dig into an optional operand when diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/SILGen/if_while_binding.swift b/test/SILGen/if_while_binding.swift index 31329e67c04f7..e336ac27edbb1 100644 --- a/test/SILGen/if_while_binding.swift +++ b/test/SILGen/if_while_binding.swift @@ -318,7 +318,7 @@ func testAsPatternInIfLet(_ a : BaseClass?) { // CHECK: br [[EXITBB:bb[0-9]+]] // CHECK: [[OPTPRESENTBB]]([[CLS:%.*]] : @owned $BaseClass): - // CHECK: checked_cast_br [[CLS]] : $BaseClass to $DerivedClass, [[ISDERIVEDBB:bb[0-9]+]], [[ISBASEBB:bb[0-9]+]] + // CHECK: checked_cast_br [[CLS]] : $BaseClass to DerivedClass, [[ISDERIVEDBB:bb[0-9]+]], [[ISBASEBB:bb[0-9]+]] // CHECK: [[ISDERIVEDBB]]([[DERIVED_CLS:%.*]] : @owned $DerivedClass): // CHECK: [[DERIVED_CLS_SOME:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[DERIVED_CLS]] : $DerivedClass diff --git a/test/SILGen/import_as_member.swift b/test/SILGen/import_as_member.swift index 8e73fa17a8a18..b6e7a2764bbab 100644 --- a/test/SILGen/import_as_member.swift +++ b/test/SILGen/import_as_member.swift @@ -2,6 +2,7 @@ // REQUIRES: objc_interop import ImportAsMember.A import ImportAsMember.Class +import Foundation public func returnGlobalVar() -> Double { return Struct1.globalVar @@ -65,7 +66,6 @@ extension SomeClass { } public func useSpecialInit() -> Struct1 { - // FIXME: the below triggers an assert, due to number or params mismatch - // return Struct1(specialLabel:()) + return Struct1(specialLabel:()) } diff --git a/test/SILGen/ivar_destroyer.swift b/test/SILGen/ivar_destroyer.swift index ed49bda2c33f8..8e0846e2dcbc8 100644 --- a/test/SILGen/ivar_destroyer.swift +++ b/test/SILGen/ivar_destroyer.swift @@ -29,7 +29,9 @@ class DerivedClassWithNonTrivialProperties : RootClassWithoutProperties { // CHECK: bb0(%0 : @guaranteed $DerivedClassWithNonTrivialProperties): // CHECK-NEXT: debug_value %0 // CHECK-NEXT: [[Z_ADDR:%.*]] = ref_element_addr %0 -// CHECK-NEXT: destroy_addr [[Z_ADDR]] +// CHECK-NEXT: [[Z_ADDR_DEINIT_ACCESS:%.*]] = begin_access [deinit] [static] [[Z_ADDR]] +// CHECK-NEXT: destroy_addr [[Z_ADDR_DEINIT_ACCESS]] +// CHECK-NEXT: end_access [[Z_ADDR_DEINIT_ACCESS]] // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index cfd9b5257bff7..9d2942af71ddd 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -322,7 +322,54 @@ struct Subscripts { } } -// CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts +struct SubscriptDefaults1 { + subscript(x: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Int, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } + subscript(x: Bool, bool y: Bool = false) -> Bool { + get { fatalError() } + set { fatalError() } + } + subscript(bool x: Bool, y: Int, z: Int = 0) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults2 { + subscript(x: Int? = nil) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults3 { + subscript(x: Int = #line) -> Int { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults4 { + subscript(x x: T, y y: T = 0) -> T { + get { fatalError() } + set { fatalError() } + } +} + +struct SubscriptDefaults5 { + subscript(x x: T, y y: T = #function) -> T { + get { fatalError() } + set { fatalError() } + } +} + +// CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts1x1y1syx_q_SStSHRzSHR_r0_lF func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[] _ = \Subscripts.[generic: x] @@ -352,6 +399,104 @@ func subscripts(x: T, y: U, s: String) { _ = \Subscripts.[Bass()] _ = \Subscripts.[Treble()] + + _ = \SubscriptDefaults1.[] + _ = \SubscriptDefaults1.[0] + _ = \SubscriptDefaults1.[0, 0] + _ = \SubscriptDefaults1.[0, 0, 0] + _ = \SubscriptDefaults1.[false] + _ = \SubscriptDefaults1.[false, bool: false] + _ = \SubscriptDefaults1.[bool: false, 0] + _ = \SubscriptDefaults1.[bool: false, 0, 0] + + _ = \SubscriptDefaults2.[] + _ = \SubscriptDefaults2.[0] + _ = \SubscriptDefaults3.[] + _ = \SubscriptDefaults3.[0] + _ = \SubscriptDefaults4.[x: 0] + _ = \SubscriptDefaults4.[x: 0, y: 0] + _ = \SubscriptDefaults5.[x: ""] + _ = \SubscriptDefaults5.[x: "", y: ""] +} + +// CHECK-LABEL: sil hidden [ossa] @{{.*}}check_default_subscripts +func check_default_subscripts() { + // CHECK: [[INTX:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[IX:%[0-9]+]] = apply %{{[0-9]+}}([[INTX]], {{.*}} + // CHECK: [[INTY:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[IY:%[0-9]+]] = apply %{{[0-9]+}}([[INTY]], {{.*}} + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[IX]], [[IY]]) + _ = \SubscriptDefaults4.[x: 0, y: 0] + + // CHECK: [[INTINIT:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0 + // CHECK: [[I:%[0-9]+]] = apply %{{[0-9]+}}([[INTINIT]], {{.*}} + // CHECK: [[DFN:%[0-9]+]] = function_ref @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipfA0_ : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 + // CHECK: [[ALLOC:%[0-9]+]] = alloc_stack $Int + // CHECK: apply [[DFN]]([[ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0 + // CHECK: [[LOAD:%[0-9]+]] = load [trivial] [[ALLOC]] : $*Int + // CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(thin) (@in_guaranteed SubscriptDefaults4, UnsafeRawPointer) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(thin) (@in_guaranteed Int, @inout SubscriptDefaults4, UnsafeRawPointer) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2iTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[I]], [[LOAD]]) + _ = \SubscriptDefaults4.[x: 0] + + // CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}} + // CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 "check_default_subscripts()" + // CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}} + // CHECK: keypath $WritableKeyPath, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(thin) (@in_guaranteed SubscriptDefaults5, UnsafeRawPointer) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(thin) (@in_guaranteed String, @inout SubscriptDefaults5, UnsafeRawPointer) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2STh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[STRX]], [[DEF_ARG]]) + _ = \SubscriptDefaults5.[x: ""] + + // CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}} + // CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 "" + // CHECK: [[STRY:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}} + // CHECK: keypath $WritableKeyPath, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(thin) (@in_guaranteed SubscriptDefaults5, UnsafeRawPointer) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(thin) (@in_guaranteed String, @inout SubscriptDefaults5, UnsafeRawPointer) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sS2STh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[STRX]], [[STRY]]) + _ = \SubscriptDefaults5.[x: "", y: ""] +} + +struct SubscriptVariadic1 { + subscript(x: Int...) -> Int { x[0] } +} + +struct SubscriptVariadic2 { + subscript(x: T...) -> T { x[0] } +} + +struct SubscriptVariadic3 { + subscript(x: T...) -> T { x[0] } +} + +// CHECK-LABEL: sil hidden [ossa] @{{.*}}test_variadics +func test_variadics() { + // CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 3 + // CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]([[ARR_COUNT]]) + // CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array, Builtin.RawPointer) + // CHECK: keypath $KeyPath, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(thin) (@in_guaranteed SubscriptVariadic1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Array : $Array], indices_equals @$sSaySiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[ARR]]) + _ = \SubscriptVariadic1.[1, 2, 3] + // CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]([[ARR_COUNT]]) + // CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array, Builtin.RawPointer) + // CHECK: keypath $KeyPath, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(thin) (@in_guaranteed SubscriptVariadic1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Array : $Array], indices_equals @$sSaySiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[ARR]]) + _ = \SubscriptVariadic1.[1] + // CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 0 + // CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]([[ARR_COUNT]]) + // CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array, Builtin.RawPointer) + // CHECK: keypath $KeyPath, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(thin) (@in_guaranteed SubscriptVariadic1, UnsafeRawPointer) -> @out Int, indices [%$0 : $Array : $Array], indices_equals @$sSaySiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[ARR]]) + _ = \SubscriptVariadic1.[] + + _ = \SubscriptVariadic2.["", "1"] + _ = \SubscriptVariadic2.[""] + // CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 2 + // CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]([[ARR_COUNT]]) + // CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array, Builtin.RawPointer) + // CHECK: keypath $KeyPath, (root $SubscriptVariadic2; gettable_property $String, id @$s8keypaths18SubscriptVariadic2Vyxxd_tcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@guaranteed Array<τ_0_0>, SubscriptVariadic2) -> @out τ_0_0, getter @$s8keypaths18SubscriptVariadic2Vyxxd_tcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(thin) (@in_guaranteed SubscriptVariadic2, UnsafeRawPointer) -> @out String, indices [%$0 : $Array : $Array], indices_equals @$sSaySSGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSaySSGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[ARR]]) + _ = \SubscriptVariadic2.["", #function] + + _ = \SubscriptVariadic3.[""] + _ = \SubscriptVariadic3.["", "1"] + _ = \SubscriptVariadic3.[] } // CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics diff --git a/test/SILGen/lazy_properties.swift b/test/SILGen/lazy_properties.swift index 7521786d0fb95..592bafecd5c62 100644 --- a/test/SILGen/lazy_properties.swift +++ b/test/SILGen/lazy_properties.swift @@ -62,4 +62,4 @@ class Butt { // Both the closure and the local function inside of it should capture 'self': // CHECK-LABEL: sil private [ossa] @$s15lazy_properties4ButtC4buttSivgSiyXEfU_ : $@convention(thin) (@guaranteed Butt) -> Int -// CHECK-LABEL: sil private [ossa] @$s15lazy_properties4ButtC4buttSivgSiyXEfU_3barL_SiyF : $@convention(thin) (@guaranteed Butt) -> Int \ No newline at end of file +// CHECK-LABEL: sil private [ossa] @$s15lazy_properties4ButtC4buttSivgSiyXEfU_3barL_SiyF : $@convention(thin) (@guaranteed Butt) -> Int diff --git a/test/SILGen/lifetime.swift b/test/SILGen/lifetime.swift index 4a679508a0a48..e797724c3620f 100644 --- a/test/SILGen/lifetime.swift +++ b/test/SILGen/lifetime.swift @@ -263,7 +263,7 @@ struct Daleth { } class He { - + // -- default allocator: // CHECK-LABEL: sil hidden [exact_self_class] [ossa] @$s8lifetime2HeC{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@thick He.Type) -> @owned He { // CHECK: bb0({{%.*}} : $@thick He.Type): @@ -292,7 +292,7 @@ struct Waw { var b:Val // -- loadable value initializer with tuple destructuring: - // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3WawV{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@owned Ref, Val, Val, @thin Waw.Type) -> @owned Waw // CHECK: bb0([[A0:%.*]] : @owned $Ref, [[A1:%.*]] : $Val, [[B:%.*]] : $Val, {{%.*}} : $@thin Waw.Type): // CHECK-NEXT: [[A:%.*]] = tuple ([[A0]] : {{.*}}, [[A1]] : {{.*}}) // CHECK-NEXT: [[RET:%.*]] = struct $Waw ([[A]] : {{.*}}, [[B]] : {{.*}}) @@ -513,7 +513,7 @@ class Foo { x = chi.intify() } - + // CHECK-LABEL: sil hidden [ossa] @$s8lifetime3FooCfd : $@convention(method) (@guaranteed Foo) -> @owned Builtin.NativeObject deinit { @@ -526,13 +526,19 @@ class Foo { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #Foo.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value z // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #Foo.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // -- return back this // CHECK: [[PTR:%.*]] = unchecked_ref_cast [[THIS]] : $Foo to $Builtin.NativeObject // CHECK: [[PTR_OWNED:%.*]] = unchecked_ownership_conversion [[PTR]] : $Builtin.NativeObject, @guaranteed to @owned @@ -567,7 +573,7 @@ class FooSubclass : Foo { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: end_borrow [[BORROWED_PTR]] // CHECK: return [[PTR]] - + deinit { bar() @@ -586,18 +592,22 @@ class ImplicitDtor { // CHECK-NOT: ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.x // -- destroy_value y // CHECK: [[YADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.y - // CHECK: destroy_addr [[YADDR]] + // CHECK: [[YADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[YADDR]] + // CHECK: destroy_addr [[YADDR_ACCESS]] + // CHECK: end_access [[YADDR_ACCESS]] // -- destroy_value w // CHECK: [[WADDR:%[0-9]+]] = ref_element_addr [[THIS]] : {{.*}}, #ImplicitDtor.w - // CHECK: destroy_addr [[WADDR]] + // CHECK: [[WADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[WADDR]] + // CHECK: destroy_addr [[WADDR_ACCESS]] + // CHECK: end_access [[WADDR_ACCESS]] // CHECK: return } class ImplicitDtorDerived : ImplicitDtor { var z:T - init(z : T) { - super.init() + init(z : T) { + super.init() self.z = z } @@ -611,7 +621,9 @@ class ImplicitDtorDerived : ImplicitDtor { // CHECK: [[BORROWED_PTR:%.*]] = begin_borrow [[PTR]] // CHECK: [[CAST_BORROWED_PTR:%.*]] = unchecked_ref_cast [[BORROWED_PTR]] : $Builtin.NativeObject to $ImplicitDtorDerived // CHECK: [[ZADDR:%[0-9]+]] = ref_element_addr [[CAST_BORROWED_PTR]] : {{.*}}, #ImplicitDtorDerived.z - // CHECK: destroy_addr [[ZADDR]] + // CHECK: [[ZADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[ZADDR]] + // CHECK: destroy_addr [[ZADDR_ACCESS]] + // CHECK: end_access [[ZADDR_ACCESS]] // CHECK: end_borrow [[BORROWED_PTR]] // -- epilog // CHECK-NOT: unchecked_ref_cast @@ -736,7 +748,7 @@ func downcast(_ b: B) { (b as! D).foo() // CHECK: [[READ:%.*]] = begin_access [read] [unknown] [[PB]] // CHECK: [[B:%[0-9]+]] = load [copy] [[READ]] - // CHECK: [[D:%[0-9]+]] = unconditional_checked_cast [[B]] : {{.*}} to $D + // CHECK: [[D:%[0-9]+]] = unconditional_checked_cast [[B]] : {{.*}} to D // CHECK: apply {{.*}}([[D]]) // CHECK-NOT: destroy_value [[B]] // CHECK: destroy_value [[D]] diff --git a/test/SILGen/magic_identifier_file.swift b/test/SILGen/magic_identifier_file.swift new file mode 100644 index 0000000000000..7f1620f4f3dc3 --- /dev/null +++ b/test/SILGen/magic_identifier_file.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen -module-name Foo %/s | %FileCheck --check-prefixes=BOTH,ABSOLUTE %s +// RUN: %target-swift-emit-silgen -enable-experimental-concise-pound-file -DNEEDS_CONCISE -module-name Foo %/s | %FileCheck --check-prefixes=BOTH,CONCISE %s + +// FIXME: Once this feature becomes non-experimental, we should update existing +// tests and delete this file. + +func directUse() { +// BOTH-LABEL: sil {{.*}} @$s3Foo9directUseyyF + print(#file) +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} + +func indirectUse() { +// BOTH-LABEL: sil {{.*}} @$s3Foo11indirectUseyyF + fatalError() +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} + +func forceUnwrap(_ x: ()?) { +// BOTH-LABEL: sil {{.*}} @$s3Foo11forceUnwrapyyytSgF + _ = x! +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} + +func forceTry(_ fn: () throws -> ()) { +// BOTH-LABEL: sil {{.*}} @$s3Foo8forceTryyyyyKXEF + try! fn() +// ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" +// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +} diff --git a/test/SILGen/magic_identifier_filepath.swift b/test/SILGen/magic_identifier_filepath.swift new file mode 100644 index 0000000000000..dcda58e6a6d6b --- /dev/null +++ b/test/SILGen/magic_identifier_filepath.swift @@ -0,0 +1,26 @@ +// Check that we generate the right code with the flag. +// RUN: %target-swift-emit-silgen -enable-experimental-concise-pound-file -module-name Foo %/s | %FileCheck %s + +// Check that we give errors for use of #filePath if concise #file isn't enabled. +// FIXME: Drop if we stop rejecting this. +// RUN: %target-typecheck-verify-swift -module-name Foo %s + +// FIXME: Once this feature becomes non-experimental, we should duplicate +// existing #file tests and delete this file. + +func directUse() { + print(#filePath) // expected-error {{use of unknown directive '#filePath'}} + +// CHECK-LABEL: sil {{.*}} @$s3Foo9directUseyyF +// CHECK: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_filepath.swift" +} + +func indirectUse() { + functionWithFilePathDefaultArgument() + +// CHECK-LABEL: sil {{.*}} @$s3Foo11indirectUseyyF +// CHECK: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_filepath.swift" +} + +func functionWithFilePathDefaultArgument(file: String = #filePath) {} +// expected-error@-1 {{use of unknown directive '#filePath'}} diff --git a/test/SILGen/metatype_casts.swift b/test/SILGen/metatype_casts.swift index c8b0bc457ca67..59b2fadd13810 100644 --- a/test/SILGen/metatype_casts.swift +++ b/test/SILGen/metatype_casts.swift @@ -1,24 +1,24 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s // CHECK-LABEL: sil hidden [ossa] @$s14metatype_casts6t_is_u{{[_0-9a-zA-Z]*}}F -// CHECK: checked_cast_br {{.*}} $@thick T.Type to $@thick U.Type +// CHECK: checked_cast_br {{.*}} $@thick T.Type to U.Type func t_is_u(_: T, _: U) -> Bool { return T.self is U.Type } // CHECK-LABEL: sil hidden [ossa] @$s14metatype_casts8int_is_t{{[_0-9a-zA-Z]*}}F func int_is_t() -> (Bool, T.Type?, T.Type) { - // CHECK: checked_cast_br {{%.*}} : $@thick Int.Type to $@thick T.Type - // CHECK: checked_cast_br {{%.*}} : $@thick Int.Type to $@thick T.Type - // CHECK: unconditional_checked_cast {{%.*}} : $@thick Int.Type to $@thick T.Type + // CHECK: checked_cast_br {{%.*}} : $@thick Int.Type to T.Type + // CHECK: checked_cast_br {{%.*}} : $@thick Int.Type to T.Type + // CHECK: unconditional_checked_cast {{%.*}} : $@thick Int.Type to T.Type return (Int.self is T.Type, Int.self as? T.Type, Int.self as! T.Type) } // CHECK-LABEL: sil hidden [ossa] @$s14metatype_casts8t_is_int{{[_0-9a-zA-Z]*}}F func t_is_int(_: T) -> (Bool, Int.Type?, Int.Type) { - // CHECK: checked_cast_br {{%.*}} : $@thick T.Type to $@thick Int.Type - // CHECK: checked_cast_br {{%.*}} : $@thick T.Type to $@thick Int.Type - // CHECK: unconditional_checked_cast {{%.*}} : $@thick T.Type to $@thick Int.Type + // CHECK: checked_cast_br {{%.*}} : $@thick T.Type to Int.Type + // CHECK: checked_cast_br {{%.*}} : $@thick T.Type to Int.Type + // CHECK: unconditional_checked_cast {{%.*}} : $@thick T.Type to Int.Type return (T.self is Int.Type, T.self as? Int.Type, T.self as! Int.Type) } diff --git a/test/SILGen/nested_types_referencing_nested_functions.swift b/test/SILGen/nested_types_referencing_nested_functions.swift index 0e27e309a5dea..99ae2cfcbd571 100644 --- a/test/SILGen/nested_types_referencing_nested_functions.swift +++ b/test/SILGen/nested_types_referencing_nested_functions.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -module-name nested_types_referencing_nested_functions %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -verify -module-name nested_types_referencing_nested_functions %s | %FileCheck %s do { func foo() { bar(2) } @@ -32,3 +32,19 @@ do { _ = x.zim _ = x.zang as (Int) -> () } + +// Invalid case +do { + var x = 123 // expected-note {{captured value declared here}} + + func local() { + // expected-error@-1 {{closure captures 'x' before it is declared}} + _ = x // expected-note {{captured here}} + } + + class Bar { + func zang() { + local() + } + } +} diff --git a/test/SILGen/objc_bridging_any.swift b/test/SILGen/objc_bridging_any.swift index 3a3f9f53f4a64..1e1b6a0d04cc2 100644 --- a/test/SILGen/objc_bridging_any.swift +++ b/test/SILGen/objc_bridging_any.swift @@ -690,7 +690,15 @@ class SwiftAnyEnjoyer: NSIdLover, NSIdLoving { func takesId(viaProtocol x: Any) { } } +enum SillyOptional { + case nothing + case something(NSObject) +} +func bridgeNoPayloadEnumCase(_ receiver: NSIdLover) { + let value = SillyOptional.nothing + receiver.takesId(value) +} // CHECK-LABEL: sil_witness_table shared [serialized] GenericOption: Hashable module objc_generics { // CHECK-NEXT: base_protocol Equatable: GenericOption: Equatable module objc_generics diff --git a/test/SILGen/objc_dealloc.swift b/test/SILGen/objc_dealloc.swift index d9a601e1d5ce5..8c45c5dc68756 100644 --- a/test/SILGen/objc_dealloc.swift +++ b/test/SILGen/objc_dealloc.swift @@ -82,7 +82,9 @@ class SwiftGizmo : Gizmo { // CHECK-NEXT: debug_value [[SELF]] : $SwiftGizmo, let, name "self" // CHECK-NEXT: [[SELF_BORROW:%.*]] = begin_borrow [[SELF]] // CHECK-NEXT: [[X:%[0-9]+]] = ref_element_addr [[SELF_BORROW]] : $SwiftGizmo, #SwiftGizmo.x - // CHECK-NEXT: destroy_addr [[X]] : $*X + // CHECK-NEXT: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X]] + // CHECK-NEXT: destroy_addr [[X_ACCESS]] + // CHECK-NEXT: end_access [[X_ACCESS]] // CHECK-NEXT: end_borrow [[SELF_BORROW]] // CHECK-NEXT: [[RESULT:%[0-9]+]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() diff --git a/test/SILGen/objc_factory_init.swift b/test/SILGen/objc_factory_init.swift index 5d5777cd8ba6d..1a83939e0cf01 100644 --- a/test/SILGen/objc_factory_init.swift +++ b/test/SILGen/objc_factory_init.swift @@ -42,6 +42,40 @@ extension Hive { convenience init(otherQueen other: Bee) { self.init(queen: other) } + + // CHECK-LABEL: sil hidden [ossa] @$sSo4HiveC17objc_factory_initE15otherFlakyQueenABSo3BeeC_tKcfC + // CHECK: bb0([[QUEEN:%.*]] : @owned $Bee, [[META:%.*]] : $@thick Hive.Type): + // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var Hive }, let, name "self" + // CHECK: [[MU:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] + // CHECK: [[PB_BOX:%.*]] = project_box [[MU]] : ${ var Hive }, 0 + // CHECK: [[FOREIGN_ERROR_STACK:%.*]] = alloc_stack $Optional + // CHECK: [[OBJC_META:%[0-9]+]] = thick_to_objc_metatype [[META]] : $@thick Hive.Type to $@objc_metatype Hive.Type + // CHECK: [[BORROWED_QUEEN:%.*]] = begin_borrow [[QUEEN]] + // CHECK: [[COPIED_BORROWED_QUEEN:%.*]] = copy_value [[BORROWED_QUEEN]] + // CHECK: [[OPT_COPIED_BORROWED_QUEEN:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[COPIED_BORROWED_QUEEN]] + // CHECK: [[FACTORY:%[0-9]+]] = objc_method [[OBJC_META]] : $@objc_metatype Hive.Type, #Hive.init!allocator.1.foreign : (Hive.Type) -> (Bee?) throws -> Hive, $@convention(objc_method) (Optional, Optional>>, @objc_metatype Hive.Type) -> @autoreleased Optional // user: %25 + // CHECK: [[ERROR_PTR_STACK:%.*]] = alloc_stack $AutoreleasingUnsafeMutablePointer> + // CHECK: [[ERROR_PTR:%.*]] = load [trivial] [[ERROR_PTR_STACK]] + // CHECK: [[OPT_ERROR_PTR:%.*]] = enum $Optional>>, #Optional.some!enumelt.1, [[ERROR_PTR]] + // CHECK: [[OPT_NEW_HIVE:%.*]] = apply [[FACTORY]]([[OPT_COPIED_BORROWED_QUEEN]], [[OPT_ERROR_PTR]], [[OBJC_META]]) : $@convention(objc_method) (Optional, Optional>>, @objc_metatype Hive.Type) -> @autoreleased Optional + // CHECK: switch_enum [[OPT_NEW_HIVE]] : $Optional, case #Optional.some!enumelt.1: [[NORMAL_BB:bb[0-9]+]], case #Optional.none!enumelt: [[ERROR_BB:bb[0-9]+]] // id: %34 + // + // CHECK: bb1([[HIVE:%.*]] : @owned $Hive): + // CHECK: assign [[HIVE]] to [[PB_BOX]] + // CHECK: dealloc_stack [[FOREIGN_ERROR_STACK]] + // CHECK: [[HIVE_COPY:%.*]] = load [copy] [[PB_BOX]] + // CHECK: return [[HIVE_COPY]] + // CHECK: bb2: + // CHECK: [[OPTIONAL_NSERROR:%.*]] = load [take] [[FOREIGN_ERROR_STACK]] : $*Optional + // CHECK: [[CONVERT_NSERROR_TO_ERROR_FUNC:%.*]] = function_ref @$s10Foundation22_convertNSErrorToErrorys0E0_pSo0C0CSgF : $@convention(thin) (@guaranteed Optional) -> @owned Error + // CHECK: [[ERROR:%.*]] = apply [[CONVERT_NSERROR_TO_ERROR_FUNC]]([[OPTIONAL_NSERROR]]) : $@convention(thin) (@guaranteed Optional) -> @owned Error + // CHECK: "willThrow"([[ERROR]] : $Error) + // CHECK: dealloc_stack [[FOREIGN_ERROR_STACK]] + // CHECK: throw [[ERROR]] : $Error + // CHECK: } // end sil function '$sSo4HiveC17objc_factory_initE15otherFlakyQueenABSo3BeeC_tKcfC' + convenience init(otherFlakyQueen other: Bee) throws { + try self.init(flakyQueen: other) + } } extension SomeClass { diff --git a/test/SILGen/objc_thunks.swift b/test/SILGen/objc_thunks.swift index 7eafa2d59f72b..b49ff4d46bc3a 100644 --- a/test/SILGen/objc_thunks.swift +++ b/test/SILGen/objc_thunks.swift @@ -369,7 +369,7 @@ class Hoozit : Gizmo { // CHECK: [[SUPERMETHOD:%[0-9]+]] = objc_super_method [[CAST_BORROWED_GIZMO]] : $Hoozit, #Gizmo.init!initializer.1.foreign : (Gizmo.Type) -> (Int) -> Gizmo?, $@convention(objc_method) (Int, @owned Gizmo) -> @owned Optional // CHECK-NEXT: end_borrow [[BORROWED_GIZMO]] // CHECK-NEXT: [[SELF_REPLACED:%[0-9]+]] = apply [[SUPERMETHOD]](%0, [[X:%[0-9]+]]) : $@convention(objc_method) (Int, @owned Gizmo) -> @owned Optional - // CHECK-NOT: unconditional_checked_cast downcast [[SELF_REPLACED]] : $Gizmo to $Hoozit + // CHECK-NOT: unconditional_checked_cast downcast [[SELF_REPLACED]] : $Gizmo to Hoozit // CHECK: unchecked_ref_cast // CHECK: return override init(bellsOn x : Int) { diff --git a/test/SILGen/opaque_result_type.swift b/test/SILGen/opaque_result_type.swift index 50ae18fc37105..6980e773fc4c3 100644 --- a/test/SILGen/opaque_result_type.swift +++ b/test/SILGen/opaque_result_type.swift @@ -1,4 +1,8 @@ -// RUN: %target-swift-frontend -disable-availability-checking -emit-silgen %s | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -disable-availability-checking -emit-silgen %s | %FileCheck %s + +import resilient_struct protocol P {} protocol Q: AnyObject {} @@ -10,39 +14,154 @@ class C: Q {} // CHECK-LABEL: sil hidden {{.*}}11valueToAddr1xQr func valueToAddr(x: String) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: [[VALUE_COPY:%.*]] = copy_value %1 - // CHECK: store [[VALUE_COPY]] to [init] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*String, [[ARG1:%.*]] : @guaranteed $String): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG1]] + // CHECK: store [[VALUE_COPY]] to [init] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}10addrToAddr1xQr func addrToAddr(x: AddrOnly) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: copy_addr %1 to [initialization] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*AddrOnly, [[ARG1:%.*]] : $*AddrOnly): + // CHECK: copy_addr [[ARG1]] to [initialization] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}13genericAddrToE01xQr func genericAddrToAddr(x: T) -> some P { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 - // CHECK: copy_addr %1 to [initialization] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T): + // CHECK: copy_addr [[ARG1]] to [initialization] [[ARG0]] return x } // CHECK-LABEL: sil hidden {{.*}}12valueToValue1xQr func valueToValue(x: C) -> some Q { - // CHECK: [[VALUE_COPY:%.*]] = copy_value %0 - // CHECK: [[CAST_TO_OPAQUE:%.*]] = unchecked_ref_cast [[VALUE_COPY]] - // CHECK: return [[CAST_TO_OPAQUE]] + // CHECK: bb0([[ARG:%.*]] : @guaranteed $C): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG]] + // CHECK: return [[VALUE_COPY]] return x } // CHECK-LABEL: sil hidden {{.*}}13reabstraction1xQr func reabstraction(x: @escaping () -> ()) -> some Any { - // CHECK: [[UNDERLYING:%.*]] = unchecked_addr_cast %0 : ${{.*}} to $*@callee_guaranteed () -> @out () - // CHECK: [[VALUE_COPY:%.*]] = copy_value %1 - // CHECK: [[VALUE_REABSTRACT:%.*]] = partial_apply [callee_guaranteed] {{%.*}}([[VALUE_COPY]]) - // CHECK: store [[VALUE_REABSTRACT]] to [init] [[UNDERLYING]] + // CHECK: bb0([[ARG0:%.*]] : $*@callee_guaranteed () -> @out (), [[ARG1:%.*]] : @guaranteed $@callee_guaranteed () -> ()): + // CHECK: [[VALUE_COPY:%.*]] = copy_value [[ARG1]] + // CHECK: [[REABSTRACT:%.*]] = function_ref @$sIeg_ytIegr_TR + // CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT]]([[VALUE_COPY]]) + // CHECK: store [[THUNK]] to [init] [[ARG0]] return x } + +protocol X { + associatedtype A + func foo() -> A +} + +extension Int : P {} + +extension ResilientInt : P {} + +class K : P {} + +func useClosure2(_ cl: () -> ()) {} + +func useClosure(_ cl: @escaping () -> ()) { + cl() +} + +struct S : X { + + func foo() -> some P { + return returnTrivial() + } + + func returnTrivial() -> some P { + return 1 + } + + func returnClass() -> some P { + return K() + } + + func returnResilient() -> some P { + return ResilientInt(i: 1) + } + + func testCapture() { + var someP = returnTrivial() + var someK = returnClass() + var someR = returnResilient() + useClosure { + someP = self.returnTrivial() + someK = self.returnClass() + someR = self.returnResilient() + } + print(someP) + print(someK) + print(someR) + } + + func testCapture2() { + var someP = returnTrivial() + var someK = returnClass() + var someR = returnResilient() + useClosure2 { + someP = self.returnTrivial() + someK = self.returnClass() + someR = self.returnResilient() + } + print(someP) + print(someK) + print(someR) + } + + func testCapture3() { + let someP = returnTrivial() + let someK = returnClass() + let someR = returnResilient() + useClosure { + print(someP) + print(someK) + print(someR) + } + } + + func testCapture4() { + let someP = returnTrivial() + let someK = returnClass() + let someR = returnResilient() + useClosure { + print(someP) + print(someK) + print(someR) + } + } +} + +extension Optional : P { } + +struct S2 : X { + func foo() -> some P { + let x : Optional = 1 + return x + } + func returnFunctionType() -> () -> A { + return foo + } +} + +class Base {} +class Sub1 : Base {} +class Sub2 : Base {} + +public class D { + var cond = true + // CHECK-LABEL: sil private [ossa] @$s18opaque_result_type1DC1c33_C2C55A4BAF30C3244D4A165D48A91142LLQrvg + // CHECK: bb3([[RET:%[0-9]+]] : @owned $Base): + // CHECH: return [[RET]] + // CHECK: } // end sil function '$s18opaque_result_type1DC1c33_C2C55A4BAF30C3244D4A165D48A91142LLQrvg' + private lazy var c: some Base = { + let d = cond ? Sub1() : Sub2() + return d + }() +} diff --git a/test/SILGen/optional-cast.swift b/test/SILGen/optional-cast.swift index 37c959ef3d063..1a8b2592a73bc 100644 --- a/test/SILGen/optional-cast.swift +++ b/test/SILGen/optional-cast.swift @@ -15,7 +15,7 @@ class B : A {} // If so, pull the value out and check whether it's a B. // CHECK: [[IS_PRESENT]]([[VAL:%.*]] : // CHECK-NEXT: [[X_VALUE:%.*]] = init_enum_data_addr [[PB]] : $*Optional, #Optional.some -// CHECK-NEXT: checked_cast_br [[VAL]] : $A to $B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[VAL]] : $A to B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] // // If so, materialize that and inject it into x. // CHECK: [[IS_B]]([[T0:%.*]] : @owned $B): @@ -76,7 +76,7 @@ func foo(_ y : A?) { // // If so, pull out the A and check whether it's a B. // CHECK: [[PPPP]]([[VAL:%.*]] : -// CHECK-NEXT: checked_cast_br [[VAL]] : $A to $B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[VAL]] : $A to B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] // // If so, inject it back into an optional. // TODO: We're going to switch back out of this; we really should peephole it. @@ -141,7 +141,7 @@ func bar(_ y : A????) { // CHECK: switch_enum [[ARG_COPY]] // CHECK: bb1([[VAL:%.*]] : @owned $AnyObject): // CHECK-NEXT: [[X_VALUE:%.*]] = init_enum_data_addr [[PB]] : $*Optional, #Optional.some -// CHECK-NEXT: checked_cast_br [[VAL]] : $AnyObject to $B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[VAL]] : $AnyObject to B, [[IS_B:bb.*]], [[NOT_B:bb[0-9]+]] // CHECK: [[IS_B]]([[CASTED_VALUE:%.*]] : @owned $B): // CHECK: store [[CASTED_VALUE]] to [init] [[X_VALUE]] // CHECK: [[NOT_B]]([[ORIGINAL_VALUE:%.*]] : @owned $AnyObject): diff --git a/test/SILGen/polymorphic_inout_aliasing.swift b/test/SILGen/polymorphic_inout_aliasing.swift index 3d787ec5b3e3a..71bb3e51e9719 100644 --- a/test/SILGen/polymorphic_inout_aliasing.swift +++ b/test/SILGen/polymorphic_inout_aliasing.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -verify %s -enable-ownership-stripping-after-serialization struct Block {} diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 4dfcb4cdc30f4..6c4e4aba474c3 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -537,7 +537,7 @@ struct ReferenceStorageTypeRValues { // CHECK: bb0([[ARG:%.*]] : @guaranteed $ReferenceStorageTypeRValues): // CHECK-NEXT: debug_value [[ARG]] : $ReferenceStorageTypeRValues // CHECK-NEXT: [[UNOWNED_ARG_FIELD:%.*]] = struct_extract [[ARG]] : $ReferenceStorageTypeRValues, #ReferenceStorageTypeRValues.p1 -// CHECK-NEXT: [[COPIED_VALUE:%.*]] = copy_unowned_value [[UNOWNED_ARG_FIELD]] +// CHECK-NEXT: [[COPIED_VALUE:%.*]] = strong_copy_unowned_value [[UNOWNED_ARG_FIELD]] // CHECK-NEXT: return [[COPIED_VALUE]] : $Ref init() { diff --git a/test/SILGen/property_wrapper_coroutine.swift b/test/SILGen/property_wrapper_coroutine.swift new file mode 100644 index 0000000000000..0dd8f7cd479eb --- /dev/null +++ b/test/SILGen/property_wrapper_coroutine.swift @@ -0,0 +1,76 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +@propertyWrapper +struct TestWrapper { + var wrappedValue: ValueType +} + +struct State { + @TestWrapper var values: [String] = [] +} + +protocol StateProtocol { + @_borrowed var someValues: [String] { get set } +} + +struct State1: StateProtocol { + @TestWrapper var someValues: [String] = [] +} + +var state = State() +state.values = Array(repeating: "", count: 20000) +state.values[1000] = "foo" + +let state1 = State1() +_ = state1.someValues + +// >> Check that the subscript assignment uses the _modify coroutine + +// CHECK: {{%.*}} = begin_access [modify] [dynamic] {{%.*}} : $*State +// CHECK: [[REF_VALUES_MODIFY:%.*]] = function_ref @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array +// CHECK: ([[RES1:%.*]], {{%.*}}) = begin_apply [[REF_VALUES_MODIFY]]({{%.*}}) : $@yield_once @convention(method) (@inout State) -> @yields @inout Array +// CHECK: [[REF_ARRAY_SUBSCRIPT:%.*]] = function_ref @$sSayxSiciM : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0 +// CHECK: ({{%.*}}, {{%.*}}) = begin_apply [[REF_ARRAY_SUBSCRIPT]]({{%.*}}, [[RES1]]) : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0 + +// >> Check that the _modify coroutine is synthesized properly + +// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_coroutine5StateV6valuesSaySSGvM : $@yield_once @convention(method) (@inout State) -> @yields @inout Array { +// CHECK: bb0([[STATE:%.*]] : $*State): +// CHECK: debug_value_addr [[STATE]] : $*State, var, name "self", argno {{.*}} +// CHECK: [[BEGIN_ACCESS:%.*]] = begin_access [modify] [unknown] [[STATE]] : $*State +// CHECK: [[BACKING_ADDR:%.*]] = struct_element_addr [[BEGIN_ACCESS]] : $*State, #State._values +// CHECK: [[VALUE_ADDR:%.*]] = struct_element_addr [[BACKING_ADDR]] : $*TestWrapper>, #TestWrapper.wrappedValue +// CHECK: yield [[VALUE_ADDR]] : $*Array, resume bb1, unwind bb2 +// +// CHECK: bb1: +// CHECK: end_access [[BEGIN_ACCESS]] : $*State +// CHECK: [[RETURN:%.*]] = tuple () +// CHECK: return [[RETURN]] : $() +// +// CHECK: bb2: +// CHECK: end_access [[BEGIN_ACCESS]] : $*State +// CHECK: unwind +// CHECK-END: } + +// >> Check that the _read coroutine is synthesized properly + +// CHECK-LABEL: sil shared [ossa] @$s26property_wrapper_coroutine6State1V10someValuesSaySSGvr : $@yield_once @convention(method) (@guaranteed State1) -> @yields @guaranteed Array { +// CHECK: bb0([[STATE1:%.*]] : @guaranteed $State1): +// CHECK: debug_value [[SELF:%.*]] : $State1, let, name "self", argno {{.*}} +// CHECK: [[EXTRACT_VALUE:%.*]] = struct_extract %0 : $State1, #State1._someValues +// CHECK: [[COPY_VALUE:%.*]] = copy_value [[EXTRACT_VALUE]] : $TestWrapper> +// CHECK: [[BEGIN_BORROW:%.*]] = begin_borrow [[COPY_VALUE]] : $TestWrapper> +// CHECK: [[EXTRACT_WRAPPEDVALUE:%.*]] = struct_extract [[BEGIN_BORROW]] : $TestWrapper>, #TestWrapper.wrappedValue +// CHECK: yield [[EXTRACT_WRAPPEDVALUE]] : $Array, resume bb1, unwind bb2 +// +// CHECK: bb1: +// CHECK: end_borrow [[BEGIN_BORROW]] : $TestWrapper> +// CHECK: destroy_value [[COPY_VALUE]] : $TestWrapper> +// CHECK: [[RETURN:%.*]] = tuple () +// CHECK: return [[RETURN]] : $() +// +// CHECK: bb2: +// CHECK: end_borrow [[BEGIN_BORROW]] : $TestWrapper> +// CHECK: destroy_value [[COPY_VALUE]] : $TestWrapper> +// CHECK: unwind +// CHECK-END: } diff --git a/test/SILGen/property_wrapper_coroutine_public.swift b/test/SILGen/property_wrapper_coroutine_public.swift new file mode 100644 index 0000000000000..1981063e15b8d --- /dev/null +++ b/test/SILGen/property_wrapper_coroutine_public.swift @@ -0,0 +1,33 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// Makes sure the modify coroutine is not @_transparent, since it references +// private properties. + +public class Store { + @Published public var state: Any + init() {} +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get {} + set {} + } + public static subscript( + _enclosingInstance object: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath>) + -> Value where EnclosingSelf : AnyObject { + get {} + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get {} + } +} + +// CHECK-LABEL: sil [ossa] @$s33property_wrapper_coroutine_public5StoreC5stateypvM : $@yield_once @convention(method) (@guaranteed Store) -> @yields @inout Any { +// CHECK: keypath $ReferenceWritableKeyPath, (root $Store; settable_property $Any, id #Store.state!getter.1 : (Store) -> () -> Any, getter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTK : $@convention(thin) (@in_guaranteed Store) -> @out Any, setter @$s33property_wrapper_coroutine_public5StoreC5stateypvpACTk : $@convention(thin) (@in_guaranteed Any, @in_guaranteed Store) -> ()) +// CHECK: keypath $ReferenceWritableKeyPath>, (root $Store; stored_property #Store._state : $Published) \ No newline at end of file diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index 856c3c6c3828b..89d22c40c5b89 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -519,6 +519,57 @@ extension UsesMyPublished { } } +// SR-11603 - crash due to incorrect lvalue computation +@propertyWrapper +struct StructWrapper { + var wrappedValue: T +} + +@propertyWrapper +class ClassWrapper { + var wrappedValue: T + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } +} + +struct SR_11603 { + @StructWrapper @ClassWrapper var prop: Int + + func foo() { + prop = 1234 + } +} + +// rdar://problem/57545381 - crash due to inconsistent decision about whether +// to initialize a wrapper property with an instance of the wrapper type vs. +// the wrapped type. +@propertyWrapper +class WrappedInt { + var intValue: Int? + + var wrappedValue: Int? { + get { + return intValue + } + set { + intValue = newValue + } + } + + init() { } + + init(wrappedValue: Int?) { + self.wrappedValue = wrappedValue + } +} + +struct WrappedIntContainer { + // CHECK: sil hidden [ossa] @$s17property_wrappers19WrappedIntContainerV3intAcA0cD0C_tcfcfA_ : $@convention(thin) () -> @owned WrappedInt + @WrappedInt var int: Int? +} + + // CHECK-LABEL: sil_vtable ClassUsingWrapper { // CHECK-NEXT: #ClassUsingWrapper.x!getter.1: (ClassUsingWrapper) -> () -> Int : @$s17property_wrappers17ClassUsingWrapperC1xSivg // ClassUsingWrapper.x.getter // CHECK-NEXT: #ClassUsingWrapper.x!setter.1: (ClassUsingWrapper) -> (Int) -> () : @$s17property_wrappers17ClassUsingWrapperC1xSivs // ClassUsingWrapper.x.setter diff --git a/test/SILGen/protocol_operators.swift b/test/SILGen/protocol_operators.swift new file mode 100644 index 0000000000000..fd11a62cecf8f --- /dev/null +++ b/test/SILGen/protocol_operators.swift @@ -0,0 +1,60 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +infix operator +++ + +protocol Twig { + static func +++(lhs: Self, rhs: Self) +} + +struct Branch : Twig { + @_implements(Twig, +++(_:_:)) + static func doIt(_: Branch, _: Branch) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () { +// CHECK: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ : $@convention(method) (Branch, Branch, @thin Branch.Type) -> () +// CHECK: return +func useBranch(_ b: Branch) { + b +++ b +} + +class Stick : Twig { + static func +++(lhs: Stick, rhs: Stick) {} +} + +class Stuck : Stick, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () { +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> () +// CHECK: witness_method $Stuck, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +// CHECK: return +func useStick(_ a: Stuck, _ b: Stick) { + _ = a +++ b + _ = b +++ b + _ = a +++ 5 +} + +class Twine : Twig { + static func +++(lhs: Twine, rhs: Twine) {} +} + +class Rope : Twine, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + required init(integerLiteral: Int) {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () { +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> () +// CHECK: witness_method $Rope, #Twig."+++"!1 : (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> () +func useRope(_ r: Rope, _ s: Rope) { + _ = r +++ s + _ = s +++ s + _ = r +++ 5 +} diff --git a/test/SILGen/silgenbuilder_tuple_ownership.swift b/test/SILGen/silgenbuilder_tuple_ownership.swift new file mode 100644 index 0000000000000..62dc2895d09bf --- /dev/null +++ b/test/SILGen/silgenbuilder_tuple_ownership.swift @@ -0,0 +1,49 @@ +// RUN: %target-swift-emit-silgen %s +// +// Just make sure that we do not trigger the ownership verifier on this code. We +// were previously not emitting a destroy_value for (nil, error) since we were +// seeing the .none for [String: Any]? and propagating that values ownership +// rather than the error. + +public enum Outcome { + case success(T) + case error(T?, Error) +} + +public protocol RequestContentRepresentable { +} + +public class HttpClient { + public func fetch(requestContent: RequestContentRepresentable, completionHandler: @escaping (Outcome<[String: Any]>) -> Void) throws { + fatalError() + } +} + +public final class Future { + @discardableResult + public func finish(result: ResultType) -> Bool { + fatalError() + } +} + +class Controller { + internal func test() { + let content2: RequestContentRepresentable? = nil + let content = content2! + let httpClient2: HttpClient? = nil + let httpClient: HttpClient = httpClient2! + + // Create a Future to encapsulate the response handler. + // This allows us to guarantee we only call it once. + // We set the handler in the success block and we fail the future if we should no longer be allowed to call the completion + let futureResponseHandler = Future<([String: Any]?, Error?)>() + + do { + try httpClient.fetch(requestContent: content) { (outcome) in + } + } catch let error { + // This is calling the future's success handler with a tuple. + futureResponseHandler.finish(result: (nil, error)) + } + } +} diff --git a/test/SILGen/statements.swift b/test/SILGen/statements.swift index 38454d7bf5f58..f33d40ff8fe18 100644 --- a/test/SILGen/statements.swift +++ b/test/SILGen/statements.swift @@ -167,8 +167,8 @@ func for_loops2() { // rdar://problem/19316670 // CHECK: alloc_stack $Optional // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] - // CHECK: [[NEXT:%[0-9]+]] = function_ref @$ss16IndexingIteratorV4next{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[NEXT]]> + // CHECK: [[NEXT:%[0-9]+]] = witness_method $IndexingIterator>, #IteratorProtocol.next!1 : (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> + // CHECK-NEXT: apply [[NEXT]]>> // CHECK: class_method [[OBJ:%[0-9]+]] : $MyClass, #MyClass.foo!1 let objects = [MyClass(), MyClass() ] for obj in objects { @@ -631,7 +631,7 @@ func testCleanupEmission(_ x: T) { // CHECK-LABEL: sil hidden [ossa] @$s10statements15test_is_patternyyAA9BaseClassCF func test_is_pattern(_ y : BaseClass) { - // checked_cast_br %0 : $BaseClass to $DerivedClass + // checked_cast_br %0 : $BaseClass to DerivedClass guard case is DerivedClass = y else { marker_1(); return } marker_2() @@ -641,7 +641,7 @@ func test_is_pattern(_ y : BaseClass) { func test_as_pattern(_ y : BaseClass) -> DerivedClass { // CHECK: bb0([[ARG:%.*]] : @guaranteed $BaseClass): // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] - // CHECK: checked_cast_br [[ARG_COPY]] : $BaseClass to $DerivedClass + // CHECK: checked_cast_br [[ARG_COPY]] : $BaseClass to DerivedClass guard case let result as DerivedClass = y else { } // CHECK: bb{{.*}}({{.*}} : @owned $DerivedClass): diff --git a/test/SILGen/stored_property_default_arg.swift b/test/SILGen/stored_property_default_arg.swift index 43e2ec7590537..8c9948ff5377a 100644 --- a/test/SILGen/stored_property_default_arg.swift +++ b/test/SILGen/stored_property_default_arg.swift @@ -206,3 +206,19 @@ func checkReferenceTuple() { // CHECK-NEXT: [[AA1:%.*]] = apply [[AA1_REF]]([[ELT0]], [[ELT1]], {{.*}}) : $@convention(method) (@owned Optional, @owned Optional, @thin AA.Type) -> @owned AA let ae = AA.init(ab:)() } + +struct OptionalGeneric { + var t: T? + var x: Int +} + +// CHECK-LABEL: sil hidden [ossa] @$s27stored_property_default_arg31checkDefaultInitGenericOptionalyyF : $@convention(thin) () -> () { +func checkDefaultInitGenericOptional() { + let og = OptionalGeneric(x: 0) + + // CHECK: [[VALUE:%.*]] = enum $Optional, #Optional.none!enumelt + // CHECK: [[NIL:%.*]] = alloc_stack $Optional + // CHECK: store [[VALUE]] to [trivial] [[NIL]] : $*Optional + // CHECK: [[FN:%.*]] = function_ref @$s27stored_property_default_arg15OptionalGenericV1t1xACyxGxSg_SitcfC : $@convention(method) <τ_0_0> (@in Optional<τ_0_0>, Int, @thin OptionalGeneric<τ_0_0>.Type) -> @out OptionalGeneric<τ_0_0> + // CHECK: apply [[FN]](%0, [[NIL]], {{%.*}}, %1) +} diff --git a/test/SILGen/subclass_existentials.swift b/test/SILGen/subclass_existentials.swift index 0b131e9a5d46d..0e02ef593043a 100644 --- a/test/SILGen/subclass_existentials.swift +++ b/test/SILGen/subclass_existentials.swift @@ -253,37 +253,37 @@ func downcasts( derivedType: Derived.Type) { // CHECK: bb0([[ARG0:%.*]] : @guaranteed $Base & P, [[ARG1:%.*]] : @guaranteed $Derived, [[ARG2:%.*]] : $@thick (Base & P).Type, [[ARG3:%.*]] : $@thick Derived.Type): // CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $Derived + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to Derived let _ = baseAndP as? Derived // CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $Derived + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to Derived let _ = baseAndP as! Derived // CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $Derived & R + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to Derived & R let _ = baseAndP as? (Derived & R) // CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $Derived & R + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to Derived & R let _ = baseAndP as! (Derived & R) - // CHECK: checked_cast_br %3 : $@thick Derived.Type to $@thick (Derived & R).Type + // CHECK: checked_cast_br %3 : $@thick Derived.Type to (Derived & R).Type let _ = derivedType as? (Derived & R).Type - // CHECK: unconditional_checked_cast %3 : $@thick Derived.Type to $@thick (Derived & R).Type + // CHECK: unconditional_checked_cast %3 : $@thick Derived.Type to (Derived & R).Type let _ = derivedType as! (Derived & R).Type - // CHECK: checked_cast_br %2 : $@thick (Base & P).Type to $@thick Derived.Type + // CHECK: checked_cast_br %2 : $@thick (Base & P).Type to Derived.Type let _ = baseAndPType as? Derived.Type - // CHECK: unconditional_checked_cast %2 : $@thick (Base & P).Type to $@thick Derived.Type + // CHECK: unconditional_checked_cast %2 : $@thick (Base & P).Type to Derived.Type let _ = baseAndPType as! Derived.Type - // CHECK: checked_cast_br %2 : $@thick (Base & P).Type to $@thick (Derived & R).Type + // CHECK: checked_cast_br %2 : $@thick (Base & P).Type to (Derived & R).Type let _ = baseAndPType as? (Derived & R).Type - // CHECK: unconditional_checked_cast %2 : $@thick (Base & P).Type to $@thick (Derived & R).Type + // CHECK: unconditional_checked_cast %2 : $@thick (Base & P).Type to (Derived & R).Type let _ = baseAndPType as! (Derived & R).Type // CHECK: return @@ -365,11 +365,11 @@ func archetypeDowncasts & P) // CHECK: [[COPIED:%.*]] = copy_value [[ARG5]] : $BaseTAndP - // CHECK-NEXT: checked_cast_br [[COPIED]] : $BaseTAndP to $Derived & R + // CHECK-NEXT: checked_cast_br [[COPIED]] : $BaseTAndP to Derived & R let _ = baseTAndP_archetype as? (Derived & R) // CHECK: [[COPIED:%.*]] = copy_value [[ARG5]] : $BaseTAndP - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseTAndP to $Derived & R + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseTAndP to Derived & R let _ = baseTAndP_archetype as! (Derived & R) // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P @@ -388,35 +388,35 @@ func archetypeDowncasts & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseT + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseT let _ = baseTAndP_concrete as? BaseT // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseT + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseT let _ = baseTAndP_concrete as! BaseT // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseInt + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseInt let _ = baseTAndP_concrete as? BaseInt // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseInt + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseInt let _ = baseTAndP_concrete as! BaseInt // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseTAndP + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseTAndP let _ = baseTAndP_concrete as? BaseTAndP // CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseTAndP + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseTAndP let _ = baseTAndP_concrete as! BaseTAndP // CHECK: [[COPIED:%.*]] = copy_value [[ARG6]] : $BaseIntAndP - // CHECK-NEXT: checked_cast_br [[COPIED]] : $BaseIntAndP to $Derived & R + // CHECK-NEXT: checked_cast_br [[COPIED]] : $BaseIntAndP to Derived & R let _ = baseIntAndP_archetype as? (Derived & R) // CHECK: [[COPIED:%.*]] = copy_value [[ARG6]] : $BaseIntAndP - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseIntAndP to $Derived & R + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseIntAndP to Derived & R let _ = baseIntAndP_archetype as! (Derived & R) // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P @@ -435,35 +435,35 @@ func archetypeDowncasts & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $DerivedT + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to DerivedT let _ = baseIntAndP_concrete as? DerivedT // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $DerivedT + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to DerivedT let _ = baseIntAndP_concrete as! DerivedT // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseT + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseT let _ = baseIntAndP_concrete as? BaseT // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseT + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseT let _ = baseIntAndP_concrete as! BaseT // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseInt + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseInt let _ = baseIntAndP_concrete as? BaseInt // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseInt + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseInt let _ = baseIntAndP_concrete as! BaseInt // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to $BaseTAndP + // CHECK-NEXT: checked_cast_br [[COPIED]] : $Base & P to BaseTAndP let _ = baseIntAndP_concrete as? BaseTAndP // CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $Base & P - // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to $BaseTAndP + // CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $Base & P to BaseTAndP let _ = baseIntAndP_concrete as! BaseTAndP // CHECK: return diff --git a/test/SILGen/subscript_default_arguments.swift b/test/SILGen/subscript_default_arguments.swift index ba083d33c802e..2cd23ab476432 100644 --- a/test/SILGen/subscript_default_arguments.swift +++ b/test/SILGen/subscript_default_arguments.swift @@ -167,4 +167,4 @@ public func subscript9() { s[] = 0 s[] += 1 } -#endif \ No newline at end of file +#endif diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift index 432233c61f7c8..eacc3dd1d08f4 100644 --- a/test/SILGen/switch.swift +++ b/test/SILGen/switch.swift @@ -456,7 +456,7 @@ class E : C {} // CHECK-LABEL: sil hidden [ossa] @$s6switch16test_isa_class_11xyAA1BC_tF : $@convention(thin) (@guaranteed B) -> () { func test_isa_class_1(x: B) { // CHECK: bb0([[X:%.*]] : @guaranteed $B): - // CHECK: checked_cast_br [[X]] : $B to $D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]] switch x { // CHECK: [[IS_D1]]([[CAST_D1:%.*]] : @guaranteed $D1): @@ -482,7 +482,7 @@ func test_isa_class_1(x: B) { // CHECK-NEXT: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: - // CHECK: checked_cast_br [[X]] : $B to $D2, [[IS_D2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to D2, [[IS_D2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]] case is D2: // CHECK: [[IS_D2]]([[CAST_D2:%.*]] : @guaranteed $D2): // CHECK: [[CAST_D2_COPY:%.*]] = copy_value [[CAST_D2]] @@ -493,7 +493,7 @@ func test_isa_class_1(x: B) { // CHECK: [[IS_NOT_D2]]([[CASTFAIL_D2:%.*]] : @guaranteed $B): // CHECK: end_borrow [[CASTFAIL_D2]] - // CHECK: checked_cast_br [[X]] : $B to $E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case is E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): // CHECK: [[CAST_E_COPY:%.*]] = copy_value [[CAST_E]] @@ -516,7 +516,7 @@ func test_isa_class_1(x: B) { // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: - // CHECK: checked_cast_br [[X]] : $B to $C, [[IS_C:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to C, [[IS_C:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]] case is C: // CHECK: [[IS_C]]([[CAST_C:%.*]] : @guaranteed $C): @@ -547,7 +547,7 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: bb0([[X:%.*]] : @guaranteed $B): switch x { - // CHECK: checked_cast_br [[X]] : $B to $D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]] case let y as D1 where runced(): // CHECK: [[IS_D1]]([[CAST_D1:%.*]] : @guaranteed $D1): // CHECK: [[CAST_D1_COPY:%.*]] = copy_value [[CAST_D1]] @@ -574,7 +574,7 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: - // CHECK: checked_cast_br [[X]] : $B to $D2, [[CASE2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to D2, [[CASE2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]] case let y as D2: // CHECK: [[CASE2]]([[CAST_D2:%.*]] : @guaranteed $D2): // CHECK: [[CAST_D2_COPY:%.*]] = copy_value [[CAST_D2]] @@ -590,7 +590,7 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: [[IS_NOT_D2]]([[NOCAST_D2:%.*]] : @guaranteed $B): // CHECK: end_borrow [[NOCAST_D2]] - // CHECK: checked_cast_br [[X]] : $B to $E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case let y as E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): // CHECK: [[CAST_E_COPY:%.*]] = copy_value [[CAST_E]] @@ -618,7 +618,7 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]] - // CHECK: checked_cast_br [[X]] : $B to $C, [[CASE4:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]] + // CHECK: checked_cast_br [[X]] : $B to C, [[CASE4:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]] case let y as C: // CHECK: [[CASE4]]([[CAST_C:%.*]] : @guaranteed $C): // CHECK: [[CAST_C_COPY:%.*]] = copy_value [[CAST_C]] @@ -1233,7 +1233,7 @@ func partial_address_only_tuple_dispatch(_ name: Klass, _ value: Any?) { // CHECK: [[TUP_0:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional), 0 // CHECK: [[TUP_0_VAL:%.*]] = load_borrow [[TUP_0]] // CHECK: [[TUP_1:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional), 1 -// CHECK: checked_cast_br [[TUP_0_VAL]] : $Klass to $AnyObject, [[IS_ANYOBJECT_BB:bb[0-9]+]], [[ISNOT_ANYOBJECT_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[TUP_0_VAL]] : $Klass to AnyObject, [[IS_ANYOBJECT_BB:bb[0-9]+]], [[ISNOT_ANYOBJECT_BB:bb[0-9]+]] // // CHECK: [[IS_ANYOBJECT_BB]]([[ANYOBJECT:%.*]] : @guaranteed $AnyObject): // CHECK: [[ANYOBJECT_COPY:%.*]] = copy_value [[ANYOBJECT]] diff --git a/test/SILGen/switch_isa.swift b/test/SILGen/switch_isa.swift index 486ab4f2640bb..54a07371eee3c 100644 --- a/test/SILGen/switch_isa.swift +++ b/test/SILGen/switch_isa.swift @@ -57,11 +57,11 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true } // CHECK: [[TUP:%.*]] = tuple ([[ARG0_COPY:%.*]] : $B, [[ARG1_COPY:%.*]] : $B) // CHECK: [[BORROWED_TUP:%.*]] = begin_borrow [[TUP]] // CHECK: ([[TUP_1:%.*]], [[TUP_2:%.*]]) = destructure_tuple [[BORROWED_TUP]] -// CHECK: checked_cast_br [[TUP_1]] : $B to $D, [[R_CAST_YES:bb[0-9]+]], [[R_CAST_NO:bb[0-9]+]] +// CHECK: checked_cast_br [[TUP_1]] : $B to D, [[R_CAST_YES:bb[0-9]+]], [[R_CAST_NO:bb[0-9]+]] // // CHECK: [[R_CAST_YES]]([[R:%.*]] : @guaranteed $D): // CHECK: [[R2:%.*]] = copy_value [[R]] -// CHECK: checked_cast_br [[TUP_2]] : $B to $D, [[L_CAST_YES:bb[0-9]+]], [[L_CAST_NO:bb[0-9]+]] +// CHECK: checked_cast_br [[TUP_2]] : $B to D, [[L_CAST_YES:bb[0-9]+]], [[L_CAST_NO:bb[0-9]+]] // // CHECK: [[L_CAST_YES]]([[L:%.*]] : @guaranteed $D): // CHECK: [[L2:%.*]] = copy_value [[L]] diff --git a/test/SILGen/switch_var.swift b/test/SILGen/switch_var.swift index 73da77d1708d2..63e22cec98789 100644 --- a/test/SILGen/switch_var.swift +++ b/test/SILGen/switch_var.swift @@ -704,7 +704,7 @@ func f(_: D) -> Bool { return true } // CHECK-LABEL: sil hidden [ossa] @{{.*}}test_multiple_patterns_value_semantics func test_multiple_patterns_value_semantics(_ y: C) { switch y { - // CHECK: checked_cast_br {{%.*}} : $C to $D, [[AS_D:bb[0-9]+]], [[NOT_AS_D:bb[0-9]+]] + // CHECK: checked_cast_br {{%.*}} : $C to D, [[AS_D:bb[0-9]+]], [[NOT_AS_D:bb[0-9]+]] // CHECK: [[AS_D]]({{.*}}): // CHECK: cond_br {{%.*}}, [[F_TRUE:bb[0-9]+]], [[F_FALSE:bb[0-9]+]] // CHECK: [[F_TRUE]]: diff --git a/test/SILGen/top_level_captures.swift b/test/SILGen/top_level_captures.swift new file mode 100644 index 0000000000000..b8ae627ba08cc --- /dev/null +++ b/test/SILGen/top_level_captures.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s + +guard let x: Int = nil else { while true { } } + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () { +func capturesX() { + _ = x +} + +// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures17transitiveCaptureyyF : $@convention(thin) (Int) -> () { +// CHECK: [[FUNC:%.*]] = function_ref @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () +func transitiveCapture() { + capturesX() +} diff --git a/test/SILGen/tuple_conversion_refutable_pattern.swift b/test/SILGen/tuple_conversion_refutable_pattern.swift new file mode 100644 index 0000000000000..001c52426218e --- /dev/null +++ b/test/SILGen/tuple_conversion_refutable_pattern.swift @@ -0,0 +1,46 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f13argySi1a_SiSg1bt_tF : $@convention(thin) (Int, Optional) -> () { +func f1(arg: (a: Int, b: Int?)) { + guard case let (x, y?) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f13argyyXl1a_yXlSg1bt_tF : $@convention(thin) (@guaranteed AnyObject, @guaranteed Optional) -> () { +func f1(arg: (a: AnyObject, b: AnyObject?)) { + guard case let (x, y?) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f13argyyp1a_ypSg1bt_tF : $@convention(thin) (@in_guaranteed Any, @in_guaranteed Optional) -> () { +func f1(arg: (a: Any, b: Any?)) { + guard case let (x, y?) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f23argySi1a_Si1bt_tF : $@convention(thin) (Int, Int) -> () { +func f2(arg: (a: Int, b: Int)) { + guard case let (x, 4) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f23argySi1a_SS1bt_tF : $@convention(thin) (Int, @guaranteed String) -> () { +func f2(arg: (a: Int, b: String)) { + guard case let (x, "") = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f33argySi1a_Si1bt_tF : $@convention(thin) (Int, Int) -> () { +func f3(arg: (a: Int, b: Int)) { + guard case let (x, is String) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f33argySi1a_yXl1bt_tF : $@convention(thin) (Int, @guaranteed AnyObject) -> () { +func f3(arg: (a: Int, b: AnyObject)) { + guard case let (x, is String) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f33argySi1a_yp1bt_tF : $@convention(thin) (Int, @in_guaranteed Any) -> () { +func f3(arg: (a: Int, b: Any)) { + guard case let (x, is String) = arg else { return } +} + +// CHECK-LABEL: sil hidden [ossa] @$s34tuple_conversion_refutable_pattern2f43argySi1a_Sb1bt_tF : $@convention(thin) (Int, Bool) -> () { +func f4(arg: (a: Int, b: Bool)) { + guard case let (x, false) = arg else { return } +} diff --git a/test/SILGen/tuples.swift b/test/SILGen/tuples.swift index 630842fb164fd..8ce9ff51e8c1e 100644 --- a/test/SILGen/tuples.swift +++ b/test/SILGen/tuples.swift @@ -212,4 +212,4 @@ public func testTupleAssign(x: inout [Int]) { public func testTupleSubtype(x: C, y: Int, z: String) { let input = (x: x, y: y, z: z) let output: (y: Int?, z: Any, x: AnyObject) = input -} \ No newline at end of file +} diff --git a/test/SILGen/unmanaged.swift b/test/SILGen/unmanaged.swift index 9f0dea617700c..9cc6016a7b5f2 100644 --- a/test/SILGen/unmanaged.swift +++ b/test/SILGen/unmanaged.swift @@ -39,7 +39,7 @@ func get(holder holder: inout Holder) -> C { // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [static] [[ADDR]] : $*Holder // CHECK-NEXT: [[T0:%.*]] = struct_element_addr [[READ]] : $*Holder, #Holder.value // CHECK-NEXT: [[T1:%.*]] = load [[T0]] : $*@sil_unmanaged C -// CHECK-NEXT: [[T2:%.*]] = copy_unmanaged_value [[T1]] +// CHECK-NEXT: [[T2:%.*]] = strong_copy_unmanaged_value [[T1]] // CHECK-NEXT: end_access [[READ]] : $*Holder // CHECK-NEXT: return [[T2]] @@ -51,5 +51,5 @@ func project(fn fn: () -> Holder) -> C { // CHECK-NEXT: debug_value // CHECK-NEXT: [[T0:%.*]] = apply [[FN]]() // CHECK-NEXT: [[T1:%.*]] = struct_extract [[T0]] : $Holder, #Holder.value -// CHECK-NEXT: [[T2:%.*]] = copy_unmanaged_value [[T1]] +// CHECK-NEXT: [[T2:%.*]] = strong_copy_unmanaged_value [[T1]] // CHECK-NEXT: return [[T2]] diff --git a/test/SILGen/unmanaged_ownership.swift b/test/SILGen/unmanaged_ownership.swift index 87007b8b412ce..ee69cfeaeff56 100644 --- a/test/SILGen/unmanaged_ownership.swift +++ b/test/SILGen/unmanaged_ownership.swift @@ -50,7 +50,7 @@ func get(holder holder: inout Holder) -> C { // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[ADDR]] : $*Holder // CHECK-NEXT: [[T0:%.*]] = struct_element_addr [[READ]] : $*Holder, #Holder.value // CHECK-NEXT: [[T1:%.*]] = load [trivial] [[T0]] : $*@sil_unmanaged C -// CHECK-NEXT: [[T2:%.*]] = copy_unmanaged_value [[T1]] +// CHECK-NEXT: [[T2:%.*]] = strong_copy_unmanaged_value [[T1]] // CHECK-NEXT: end_access [[READ]] : $*Holder // CHECK-NEXT: return [[T2]] @@ -62,6 +62,6 @@ func project(fn fn: () -> Holder) -> C { // CHECK-NEXT: debug_value // CHECK-NEXT: [[T0:%.*]] = apply [[FN]]() // CHECK-NEXT: [[T1:%.*]] = struct_extract [[T0]] : $Holder, #Holder.value -// CHECK-NEXT: [[T2:%.*]] = copy_unmanaged_value [[T1]] +// CHECK-NEXT: [[T2:%.*]] = strong_copy_unmanaged_value [[T1]] // CHECK-NOT: destroy_value [[BORROWED_FN_COPY]] // CHECK-NEXT: return [[T2]] diff --git a/test/SILGen/unowned.swift b/test/SILGen/unowned.swift index 1e73f439b19e8..ebffa09cef626 100644 --- a/test/SILGen/unowned.swift +++ b/test/SILGen/unowned.swift @@ -67,7 +67,7 @@ func test0(c c: C) { a.x = x // CHECK: [[READ:%.*]] = begin_access [read] [unknown] [[PBX]] // CHECK: [[T2:%.*]] = load_borrow [[READ]] : $*@sil_unowned C - // CHECK: [[T3:%.*]] = copy_unowned_value [[T2]] : $@sil_unowned C + // CHECK: [[T3:%.*]] = strong_copy_unowned_value [[T2]] : $@sil_unowned C // CHECK: end_borrow [[T2]] // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PBA]] // CHECK: [[XP:%.*]] = struct_element_addr [[WRITE]] : $*A, #A.x @@ -97,7 +97,7 @@ func testunowned_local() -> C { unowned let uc = c // CHECK: [[tmp2:%.*]] = load_borrow [[PB_UC]] - // CHECK: [[tmp3:%.*]] = copy_unowned_value [[tmp2]] + // CHECK: [[tmp3:%.*]] = strong_copy_unowned_value [[tmp2]] // CHECK: end_borrow [[tmp2]] return uc @@ -115,7 +115,7 @@ func test_unowned_let_capture(_ aC : C) { // CHECK-LABEL: sil private [ossa] @$s7unowned05test_A12_let_captureyyAA1CCFSiyXEfU_ : $@convention(thin) (@guaranteed @sil_unowned C) -> Int { // CHECK: bb0([[ARG:%.*]] : @guaranteed $@sil_unowned C): // CHECK-NEXT: debug_value %0 : $@sil_unowned C, let, name "bC", argno 1 -// CHECK-NEXT: [[UNOWNED_ARG:%.*]] = copy_unowned_value [[ARG]] : $@sil_unowned C +// CHECK-NEXT: [[UNOWNED_ARG:%.*]] = strong_copy_unowned_value [[ARG]] : $@sil_unowned C // CHECK-NEXT: [[FUN:%.*]] = class_method [[UNOWNED_ARG]] : $C, #C.f!1 : (C) -> () -> Int, $@convention(method) (@guaranteed C) -> Int // CHECK-NEXT: [[RESULT:%.*]] = apply [[FUN]]([[UNOWNED_ARG]]) : $@convention(method) (@guaranteed C) -> Int // CHECK-NEXT: destroy_value [[UNOWNED_ARG]] diff --git a/test/SILGen/value_ownership_class.swift b/test/SILGen/value_ownership_class.swift new file mode 100644 index 0000000000000..0c8fbe81ea54a --- /dev/null +++ b/test/SILGen/value_ownership_class.swift @@ -0,0 +1,7 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +class ConsumingClass { + __consuming func consumingMethod() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s21value_ownership_class14ConsumingClassC15consumingMethodyyF : $@convention(method) (@owned ConsumingClass) -> () { diff --git a/test/SILGen/vtable_generic_signature.swift b/test/SILGen/vtable_generic_signature.swift new file mode 100644 index 0000000000000..78674b0cde823 --- /dev/null +++ b/test/SILGen/vtable_generic_signature.swift @@ -0,0 +1,86 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %target-swift-emit-ir %s + +protocol P {} +protocol Q : P {} + +class ConcreteBase { + func f(_: U) {} +} + +class ConcreteDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericDerivedFromConcreteBase : ConcreteBase { + override func f(_: U) {} +} + +class GenericBase { + func f(_: U) {} +} + +class ConcreteDerivedFromGenericBase : GenericBase { + override func f(_: U) {} +} + +class GenericDerivedFromGenericBase : GenericBase<(T) -> Int> { + override func f(_: U) {} +} + +// Make sure we call these methods with the correct substitution map. +func call(_ t: T, _ u: U) { + ConcreteDerivedFromConcreteBase().f(u) + GenericDerivedFromConcreteBase().f(u) + + ConcreteDerivedFromGenericBase().f(u) + GenericDerivedFromGenericBase().f(u) +} + +// All the vtable thunks should traffic in , because that's +// what the base method declares. + +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromConcreteBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromConcreteBase<τ_0_0>) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0 where τ_0_0 : Q> (@in_guaranteed τ_0_0, @guaranteed ConcreteDerivedFromGenericBase) -> () +// CHECK-LABEL: sil private [thunk] [ossa] @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV : $@convention(method) <τ_0_0><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @guaranteed GenericDerivedFromGenericBase<τ_0_0>) -> () + +// CHECK-LABEL: sil_vtable ConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature12ConcreteBaseC1fyyxAA1QRzlF +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature12ConcreteBaseCACycfC +// CHECK-NEXT: #ConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature12ConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlFAA0dG0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.f!1: (ConcreteDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature019ConcreteDerivedFromD4BaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromConcreteBase { +// CHECK-NEXT: #ConcreteBase.f!1: (ConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lFAA0gH0CADyyxAA1QRzlFTV [override] +// CHECK-NEXT: #ConcreteBase.init!allocator.1: (ConcreteBase.Type) -> () -> ConcreteBase : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromConcreteBase.f!1: (GenericDerivedFromConcreteBase) -> (U) -> () : @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromConcreteBase.deinit!deallocator.1: @$s24vtable_generic_signature30GenericDerivedFromConcreteBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature11GenericBaseC1fyyqd__AA1QRd__lF +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature11GenericBaseCACyxGycfC +// CHECK-NEXT: #GenericBase.deinit!deallocator.1: @$s24vtable_generic_signature11GenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable ConcreteDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlFAA0gH0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCACycfC [override] +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.f!1: (ConcreteDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseC1fyyxAA1PRzlF +// CHECK-NEXT: #ConcreteDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature30ConcreteDerivedFromGenericBaseCfD +// CHECK-NEXT: } + +// CHECK-LABEL: sil_vtable GenericDerivedFromGenericBase { +// CHECK-NEXT: #GenericBase.f!1: (GenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lFAA0dG0CADyyqd__AA1QRd__lFTV [override] +// CHECK-NEXT: #GenericBase.init!allocator.1: (GenericBase.Type) -> () -> GenericBase : @$s24vtable_generic_signature018GenericDerivedFromD4BaseCACyxGycfC [override] +// CHECK-NEXT: #GenericDerivedFromGenericBase.f!1: (GenericDerivedFromGenericBase) -> (U) -> () : @$s24vtable_generic_signature018GenericDerivedFromD4BaseC1fyyqd__AA1PRd__lF +// CHECK-NEXT: #GenericDerivedFromGenericBase.deinit!deallocator.1: @$s24vtable_generic_signature018GenericDerivedFromD4BaseCfD +// CHECK-NEXT: } diff --git a/test/SILGen/vtable_thunks_reabstraction_modify.swift b/test/SILGen/vtable_thunks_reabstraction_modify.swift index d8c6409482471..7ad5fe25af81a 100644 --- a/test/SILGen/vtable_thunks_reabstraction_modify.swift +++ b/test/SILGen/vtable_thunks_reabstraction_modify.swift @@ -15,21 +15,21 @@ public class DerivedClass : BaseClass { } } -// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) (@guaranteed DerivedClass) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out Result { +// CHECK-LABEL: sil private [thunk] [ossa] @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvMAA04BaseF0CADyq_xcvMTV : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 { // CHECK: [[DERIVED:%.*]] = function_ref @$s34vtable_thunks_reabstraction_modify12DerivedClassC8callbackyxSicvM : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]](%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 -// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result +// CHECK: ([[RESULT_BUF:%.*]], [[TOKEN:%.*]]) = begin_apply [[DERIVED]]<τ_0_0>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed DerivedClass<τ_0_0>) -> @yields @inout @callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: [[OUTER_RESULT_BUF:%.*]] = alloc_stack $@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegyr_SixIegnr_lTR : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result -// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result, resume bb1, unwind bb2 +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[RESULT]]) : $@convention(thin) <τ_0_0> (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 +// CHECK: yield [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0, resume bb1, unwind bb2 // CHECK: bb1: -// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[MODIFIED:%.*]] = load [take] [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: [[THUNK_FN:%.*]] = function_ref @$sSixIegnr_SixIegyr_lTR : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 -// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out Result -// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out Result +// CHECK: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]<τ_0_0>([[MODIFIED]]) : $@convention(thin) <τ_0_0> (Int, @guaranteed @callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0) -> @out τ_0_0 +// CHECK: store [[THUNK]] to [init] [[RESULT_BUF]] : $*@callee_guaranteed (Int) -> @out τ_0_0 +// CHECK: dealloc_stack [[OUTER_RESULT_BUF]] : $*@callee_guaranteed (@in_guaranteed Int) -> @out τ_0_0 // CHECK: end_apply [[TOKEN]] // CHECK: return diff --git a/test/SILGen/weak_multiple_modules.swift b/test/SILGen/weak_multiple_modules.swift index ae9557f422037..3b160ed994519 100644 --- a/test/SILGen/weak_multiple_modules.swift +++ b/test/SILGen/weak_multiple_modules.swift @@ -10,7 +10,7 @@ func doSomething(ui: UI) -> Bool { // CHECK: ref_element_addr // CHECK-objc: load_unowned // CHECK-native: load_borrow - // CHECK-native: copy_unowned_value + // CHECK-native: strong_copy_unowned_value // CHECK-native: end_borrow // CHECK: open_existential_ref // CHECK: witness_method diff --git a/test/SILGen/without_actually_escaping.swift b/test/SILGen/without_actually_escaping.swift index 2979157df33b5..1d5e1569e627e 100644 --- a/test/SILGen/without_actually_escaping.swift +++ b/test/SILGen/without_actually_escaping.swift @@ -100,3 +100,16 @@ func withoutActuallyEscapingConflict() { modifyAndPerform(&localVar, closure: $0) } } + +// CHECK-LABEL: sil [ossa] @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tF +// CHECK: bb0([[ARG:%.*]] : $@convention(c) @noescape () -> ()): +// CHECK: [[E:%.*]] = convert_function [[ARG]] : $@convention(c) @noescape () -> () to [without_actually_escaping] $@convention(c) () -> () +// CHECK: [[F:%.*]] = function_ref @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tFyyyXCXEfU_ : $@convention(thin) (@convention(c) () -> ()) -> () +// CHECK: apply [[F]]([[E]]) : $@convention(thin) (@convention(c) () -> ()) -> () +public func withoutActuallyEscapingCFunction(function: (@convention(c) () -> Void)) { + withoutActuallyEscaping(function) { f in + var pointer: UnsafeRawPointer? = nil + pointer = unsafeBitCast(f, to: UnsafeRawPointer.self) + print(pointer) + } +} diff --git a/test/SILOptimizer/Inputs/Outliner.h b/test/SILOptimizer/Inputs/Outliner.h index e08479f668875..356e1deb01242 100644 --- a/test/SILOptimizer/Inputs/Outliner.h +++ b/test/SILOptimizer/Inputs/Outliner.h @@ -34,6 +34,7 @@ typedef NS_ENUM(NSUInteger, MyEventType) { @interface MyView : NSObject @property (nonatomic, nullable, readonly, strong) MyWindow *window; +@property (nonatomic, nullable, strong) MyWindow *window2; @end typedef struct MyPoint { @@ -59,3 +60,9 @@ NS_ASSUME_NONNULL_BEGIN - (nullable NSArray *) treeishChildren; @end NS_ASSUME_NONNULL_END + +NS_ASSUME_NONNULL_BEGIN +@interface MyObject : NSObject +@property (nullable) NSError *error; +@end +NS_ASSUME_NONNULL_END diff --git a/test/SILOptimizer/Inputs/cross-module-objc.swift b/test/SILOptimizer/Inputs/cross-module-objc.swift new file mode 100644 index 0000000000000..e4d2c425746c5 --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-module-objc.swift @@ -0,0 +1,17 @@ +import Foundation + +final class ObjcClass : NSObject { + fileprivate var ii: Int = 127 +} + +@inline(never) +func returnObjcClassMember(_ c: ObjcClass, _ t: T) -> Int { + return c.ii +} + +@inline(never) +public func callObjcClassMember(_ t: T) -> Int { + let c = ObjcClass() + return returnObjcClassMember(c, t) +} + diff --git a/test/SILOptimizer/Inputs/cross-module.swift b/test/SILOptimizer/Inputs/cross-module.swift new file mode 100644 index 0000000000000..edffb8662f92a --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-module.swift @@ -0,0 +1,249 @@ +import Submodule + +private enum PE { + case A + case B(T) +} + +public struct Container { + + private final class Base { + } + + @inline(never) + public func testclass(_ t: T) -> T { + var arr = Array() + arr.append(Base()) + print(arr) + return t + } + + @inline(never) + @_semantics("optimize.sil.specialize.generic.never") + public func testclass_gen(_ t: T) -> T { + var arr = Array() + arr.append(Base()) + print(arr) + return t + } + + @inline(never) + public func testenum(_ t: T) -> T { + var arr = Array>() + arr.append(.B(t)) + print(arr) + return t + } + + @inline(never) + @_semantics("optimize.sil.specialize.generic.never") + public func testenum_gen(_ t: T) -> T { + var arr = Array>() + arr.append(.B(t)) + print(arr) + return t + } + + public init() { } +} + +private class PrivateBase { + var t: T + func foo() -> Int { return 27 } + + init(_ t: T) { self.t = t } +} + +private class PrivateDerived : PrivateBase { + override func foo() -> Int { return 28 } +} + +@inline(never) +private func getClass(_ t : T) -> PrivateBase { + return PrivateDerived(t) +} + +@inline(never) +public func createClass(_ t: T) -> Int { + return getClass(t).foo() +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func createClass_gen(_ t: T) -> Int { + return getClass(t).foo() +} + +private struct PrivateError: Error { } + +public func returnPrivateError(_ v: V) -> Error { + return PrivateError() +} + +struct InternalError: Error { } + +public func returnInternalError(_ v: V) -> Error { + return InternalError() +} + +private protocol PrivateProtocol { + func foo() -> Int +} + +open class OpenClass { + public init() { } + + @inline(never) + fileprivate func bar(_ t: T) { + print(t) + } +} + +extension OpenClass { + @inline(never) + public func testit() -> Bool { + return self is PrivateProtocol + } +} + +@inline(never) +public func checkIfClassConforms(_ t: T) { + let x = OpenClass() + print(x.testit()) +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func checkIfClassConforms_gen(_ t: T) { + let x = OpenClass() + print(x.testit()) +} + +@inline(never) +public func callClassMethod(_ t: T) { + let k = OpenClass() + k.bar(t) +} + +extension Int : PrivateProtocol { + func foo() -> Int { return self } +} + +@inline(never) +private func printFooExistential(_ p: PrivateProtocol) { + print(p.foo()) +} + +@inline(never) +private func printFooGeneric(_ p: T) { + print(p.foo()) +} + +@inline(never) +public func callFoo(_ t: T) { + printFooExistential(123) + printFooGeneric(1234) +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func callFoo_gen(_ t: T) { + printFooExistential(123) + printFooGeneric(1234) +} + +fileprivate protocol PrivateProto { + func foo() +} + +public class FooClass: PrivateProto { + func foo() { + print(321) + } +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +fileprivate func callProtocolFoo(_ t: T) { + t.foo() +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func callFooViaConformance(_ t: T) { + let c = FooClass() + callProtocolFoo(c) +} + +@inline(never) +public func callGenericSubmoduleFunc(_ t: T) { + genericSubmoduleFunc(t) +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func callGenericSubmoduleFunc_gen(_ t: T) { + genericSubmoduleFunc(t) +} + +@inline(never) +public func genericClosure(_ t: T) -> T { + let c : () -> T = { return t } + return c() +} + +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +public func genericClosure_gen(_ t: T) -> T { + let c : () -> T = { return t } + return c() +} + +struct Abc { + var x: Int { return 27 } + var y: Int { return 28 } +} + +class Myclass { + var x: Int { return 27 } + var y: Int { return 28 } +} + +class Derived : Myclass { + override var x: Int { return 29 } + override var y: Int { return 30 } +} + +@inline(never) +func getStructKeypath(_ t: T) -> KeyPath { + return \Abc.x +} + +@inline(never) +public func useStructKeypath(_ t: T) -> Int { + let abc = Abc() + return abc[keyPath: getStructKeypath(t)] +} + +@inline(never) +func getClassKeypath(_ t: T) -> KeyPath { + return \Myclass.x +} + +@inline(never) +public func useClassKeypath(_ t: T) -> Int { + let c = Derived() + return c[keyPath: getClassKeypath(t)] +} + +@inline(never) +func unrelated(_ u: U) { + print(u) +} + +@inline(never) +public func callUnrelated(_ t: T) -> T { + unrelated(43) + return t +} + diff --git a/test/SILOptimizer/Inputs/cross-submodule.swift b/test/SILOptimizer/Inputs/cross-submodule.swift new file mode 100644 index 0000000000000..2df3e5f66057f --- /dev/null +++ b/test/SILOptimizer/Inputs/cross-submodule.swift @@ -0,0 +1,12 @@ + +@inline(never) +@_semantics("optimize.no.crossmodule") +private func printit(_ x: Any) { + print(x) +} + +@inline(never) +public func genericSubmoduleFunc(_ t: T) { + printit(t) +} + diff --git a/test/SILOptimizer/Inputs/specialize_opaque_type_archetypes_3.swift b/test/SILOptimizer/Inputs/specialize_opaque_type_archetypes_3.swift index 2e6cde748a553..f0997ca7f430e 100644 --- a/test/SILOptimizer/Inputs/specialize_opaque_type_archetypes_3.swift +++ b/test/SILOptimizer/Inputs/specialize_opaque_type_archetypes_3.swift @@ -47,3 +47,19 @@ public struct ResilientContainer { print(x) } } + +public struct WrapperP2: ExternalP2 { + public init(_ wrapped: Wrapped) {} + public func myValue3() -> Int64 { 0 } +} + +public func externalResilientWrapper(_ wrapped: Wrapped) -> some ExternalP2 { + return WrapperP2(wrapped) +} + +@inlinable +@inline(never) +public func inlinableExternalResilientWrapper(_ wrapped: Wrapped) -> some ExternalP2 { + return WrapperP2(wrapped) +} + diff --git a/test/SILOptimizer/OSLogConstantEvaluableTest.swift b/test/SILOptimizer/OSLogConstantEvaluableTest.swift index c96985ab79e92..6a139e9a5c5e1 100644 --- a/test/SILOptimizer/OSLogConstantEvaluableTest.swift +++ b/test/SILOptimizer/OSLogConstantEvaluableTest.swift @@ -4,7 +4,7 @@ // Run the (mandatory) passes on which constant evaluator depends, and run the // constant evaluator on the SIL produced after the dependent passes are run. // -// RUN: %target-sil-opt -silgen-cleanup -raw-sil-inst-lowering -allocbox-to-stack -mandatory-inlining -constexpr-limit 1024 -test-constant-evaluable-subset %t/OSLogConstantEvaluableTest_silgen.sil > %t/OSLogConstantEvaluableTest.sil 2> %t/error-output +// RUN: %target-sil-opt -silgen-cleanup -raw-sil-inst-lowering -allocbox-to-stack -mandatory-inlining -constexpr-limit 2048 -test-constant-evaluable-subset %t/OSLogConstantEvaluableTest_silgen.sil > %t/OSLogConstantEvaluableTest.sil 2> %t/error-output // // RUN: %FileCheck %s < %t/error-output // @@ -24,15 +24,13 @@ func osLogMessageStringLiteralInitTest() -> OSLogMessage { return "A string literal" } -// CHECK-LABEL: @isPrivate(Privacy) -> Bool +// CHECK-LABEL: @init(literalCapacity: Int, interpolationCount: Int) -> OSLogInterpolation // CHECK-NOT: error: -// CHECK-LABEL: @getIntegerFormatSpecifier(A.Type, IntFormat, Bool) -> String +// CHECK-LABEL: @appendLiteral(String) -> () // CHECK-NOT: error: -// CHECK-LABEL: @sizeForEncoding(A.Type) -> Int +// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> Int, format: IntFormat, privacy: Privacy) -> () // CHECK-NOT: error: -// CHECK-LABEL: @getArgumentHeader(isPrivate: Bool, type: ArgumentType) -> UInt8 -// CHECK-NOT: error: -// CHECK-LABEL: @getUpdatedPreamble(isPrivate: Bool, isScalar: Bool) -> UInt8 +// CHECK-LABEL: @appendLiteral(String) -> () // CHECK-NOT: error: // CHECK-LABEL: @init(stringInterpolation: OSLogInterpolation) -> OSLogMessage // CHECK-NOT: error: @@ -41,9 +39,9 @@ func intValueInterpolationTest() -> OSLogMessage { return "An integer value \(10)" } -// CHECK-LABEL: @getStringFormatSpecifier(Bool) -> String +// CHECK-LABEL: @init(literalCapacity: Int, interpolationCount: Int) -> OSLogInterpolation // CHECK-NOT: error: -// CHECK-LABEL: @sizeForEncoding() -> Int +// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> String, privacy: Privacy) -> () // CHECK-NOT: error: @_semantics("test_driver") func stringValueInterpolationTest() -> OSLogMessage { diff --git a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift index baa935541e9fd..36c524e27055e 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift @@ -20,27 +20,25 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { func testNonconstantFormatOption(h: Logger, formatOpt: IntFormat) { h.log(level: .debug, "Minimum integer value: \(Int.min, format: formatOpt)") - // expected-error @-1 {{'OSLogInterpolation.formatString' is not a constant: formatting and privacy options must be literals}} + // expected-error @-1 {{interpolation arguments like format and privacy options must be constants}} // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} } func testNonconstantPrivacyOption(h: Logger, privacyOpt: Privacy) { h.log(level: .debug, "Minimum integer value: \(Int.min, privacy: privacyOpt)") - // expected-error @-1 {{'OSLogInterpolation.formatString' is not a constant: formatting and privacy options must be literals}} + // expected-error @-1 {{interpolation arguments like format and privacy options must be constants}} // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} } - // FIXME: the following test should produce diagnostics and is a not - // valid uses of the log APIs. The string interpolation passed to the os log - // call must be apart of the log call, it cannot be constructed earlier. - // It nonetheless works fine, but should be rejected. func testNoninlinedOSLogMessage(h: Logger) { let logMessage: OSLogMessage = "Minimum integer value: \(Int.min)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} h.log(level: .debug, logMessage) } func testNoninlinedOSLogMessageComplex(h: Logger, b: Bool) { let logMessage: OSLogMessage = "Maximum integer value: \(Int.max)" + // expected-error @-1 {{OSLogMessage instance must not be explicitly created and must be deletable}} if !b { return; } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil new file mode 100644 index 0000000000000..52985672684aa --- /dev/null +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -0,0 +1,1042 @@ +// RUN: %target-sil-opt -os-log-optimization -enable-sil-verify-all %s 2>&1 | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize + +// SIL tests for the OSLogOptimization pass which performs compile-time analysis +// and optimization of os log APIs. This test checks specific aspects of the +// OSLogOptimization pass on hand-crafted SIL code. The tests here do not depend +// on the os log overlay. + +import Swift +import Builtin + +/// A type that mimics the OSLogInterpolation struct in the tests in this file. +struct OSLogInterpolationStub { + var formatString: String +} + +/// A type that mimics the OSLogMessage struct in the tests in this file. +struct OSLogMessageStub { + var interpolation: OSLogInterpolationStub +} + +/// A stub for OSLogMessage.init. +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub { +bb0(%0 : @owned $String): + %1 = struct $OSLogInterpolationStub(%0 : $String) + %2 = struct $OSLogMessageStub (%1 : $OSLogInterpolationStub) + return %2 : $OSLogMessageStub +} + +// String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) +sil [serialized] [always_inline] [readonly] [_semantics "string.makeUTF8"] @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + +/// A function that models the use of a string in some arbitrary way. +sil @useFormatString: $@convention(thin) (@guaranteed String) -> () + +// CHECK-LABEL: @testConstantFoldingOfStructExtract +sil [ossa] @testConstantFoldingOfStructExtract : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %8 = begin_borrow %7 : $OSLogMessageStub + %9 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%10) : $@convention(thin) (@guaranteed String) -> () + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %13 = tuple () + return %13 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) + // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +/// A function that models the use of a string in some arbitrary way. +sil @useFormatStringIndirect: $@convention(thin) (@in_guaranteed String) -> () + +// CHECK-LABEL: @testBorrowScopeIdentificationUsingStoreBorrow +sil [ossa] @testBorrowScopeIdentificationUsingStoreBorrow : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "some long message: %llx" + %1 = integer_literal $Builtin.Word, 23 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %8 = begin_borrow %7 : $OSLogMessageStub + %9 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = alloc_stack $String + store_borrow %10 to %11 : $*String + %13 = function_ref @useFormatStringIndirect : $@convention(thin) (@in_guaranteed String) -> () + %14 = apply %13(%11) : $@convention(thin) (@in_guaranteed String) -> () + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + dealloc_stack %11 : $*String + %15 = tuple () + return %15 : $() + // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "some long message: %llx" + // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[STRINGCONST]] + // CHECK: store_borrow [[BORROW]] to {{%.*}} : $*String + // CHECK: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatStringIndirect + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[STRINGCONST]] +} + +// CHECK-LABEL: @testConstantFoldingOfOwnedValue +sil [ossa] @testConstantFoldingOfOwnedValue : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Extract the formatString property of OSLogMessageStub as a owned value and + // use it. The uses of this owned value should be constant folded. + %8 = function_ref @extractFormatStringAsOwned : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String + %9 = apply %8(%7) : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%9) : $@convention(thin) (@guaranteed String) -> () + destroy_value %7 : $OSLogMessageStub + destroy_value %9 : $String + %13 = tuple () + return %13 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // FIXME: function_ref @extractFormatStringAsOwned will not be removed as of + // now by OSLogOptimization pass even though it is folded as function calls + // are not dead-code eliminated. +} + +sil [ossa] [_semantics "constant_evaluable"] @extractFormatStringAsOwned : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String { +bb0(%0 : @guaranteed $OSLogMessageStub): + %1 = struct_extract %0 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %2 = struct_extract %1 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %3 = copy_value %2 : $String + return %3 : $String +} + +// CHECK-LABEL: @testConstantFoldingOfDestructureStruct +sil [ossa] @testConstantFoldingOfDestructureStruct : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Destructure the OSLogMessage instance and use the formatString property + // which should be constant folded by the OSLogOptimization pass. + (%8) = destructure_struct %7 : $OSLogMessageStub + (%9) = destructure_struct %8 : $OSLogInterpolationStub + %10 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %11 = apply %10(%9) : $@convention(thin) (@guaranteed String) -> () + destroy_value %9 : $String + %12 = tuple () + return %12 : $() + // CHECK-DAG [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG destroy_value [[STRINGCONST]] : $String +} + +// Test that the OSLogOptimization pass does not fold instructions that define +// ownership scopes like `begin_borrow`, and `copy_value`. +// CHECK-LABEL: @testNonFoldingOfOwnershipScopes +sil [ossa] @testNonFoldingOfOwnershipScopes : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %8 = begin_borrow %7 : $OSLogMessageStub + %9 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = copy_value %10 : $String + %12 = begin_borrow %11 : $String + %13 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %14 = apply %13(%12) : $@convention(thin) (@guaranteed String) -> () + end_borrow %12 : $String + destroy_value %11 : $String + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %15 = tuple () + return %15 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) + // CHECK-DAG: [[BORROW]] = begin_borrow [[COPYVAL:%[0-9]+]] + // CHECK-DAG: [[COPYVAL]] = copy_value [[BORROW2:%[0-9]+]] + // CHECK-DAG: [[BORROW2]] = begin_borrow [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +// CHECK-LABEL: @testPostdominatorComputation +sil [ossa] @testPostdominatorComputation : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %8 = begin_borrow %7 : $OSLogMessageStub + %14 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %9 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + cond_br %2, bb1, bb4 + + // Use the OSLogMessage instance along different branches. The following code + // deliberately uses a borrowed operation like struct_extract so that when it + // is replaced with a folded value, it needs to be destroyed at the post- + // dominating points of its uses. +bb1: + %10 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %11 = struct_extract %10 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + cond_br %2, bb2, bb3 + +bb2: + %12 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br bb5 + +bb3: + %13 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br bb5 + +bb4: + %16 = apply %9(%15) : $@convention(thin) (@guaranteed String) -> () + br bb5 + +bb5: + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %17 = tuple () + return %17 : $() + // We must have all string literals at the beginning of the borrowed scope, + // and destorys of the literals at the end of the borrow scope. + + // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "test message: %lld" + // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[STRINGCONST]] + + // CHECK: [[LIT2:%[0-9]+]] = string_literal utf8 "test message: %lld" + // CHECK: [[STRINGINIT2:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRINGCONST2:%[0-9]+]] = apply [[STRINGINIT2]]([[LIT2]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK: [[BORROW2:%[0-9]+]] = begin_borrow [[STRINGCONST2]] + + // CHECK-LABEL: bb1: + // CHECK-LABEL: bb5: + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[STRINGCONST]] : $String + // CHECK: end_borrow [[BORROW2]] + // CHECK: destroy_value [[STRINGCONST2]] : $String +} + +// This test checks whether values that are transitively data dependent on +// an OSLogMessage instance are folded. These can be alive even beyond the +// lifetime of OSLogMessage. +// CHECK-LABEL: @testFoldingOfTransitiveDataDependencies +sil [ossa] @testFoldingOfTransitiveDataDependencies : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %8 = copy_value %7 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %10 = begin_borrow %8 : $OSLogMessageStub + %14 = struct_extract %10 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %12 = copy_value %15 : $String + end_borrow %10 : $OSLogMessageStub + destroy_value %8 : $OSLogMessageStub + %9 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %16 = apply %9(%12) : $@convention(thin) (@guaranteed String) -> () + destroy_value %12 : $String + %17 = tuple () + return %17 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[CONSTCOPY:%[0-9]+]]) + // CHECK-DAG: [[CONSTCOPY]] = copy_value [[BORROW:%[0-9]+]] + // CHECK-DAG: [[BORROW]] = begin_borrow [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +// Check folding of arrays by the OSLogOptimization pass. + +/// A simplified stub for OSLogInterpolation type for testing array folding. +struct OSLogInterpolationArrayStub { + var arguments: [Int64] +} + +/// A simplified stub for OSLogMessage for testing array folding. +struct OSLogMessageArrayStub { + var interpolation: OSLogInterpolationArrayStub +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub { +bb0(%0 : $Builtin.Int1): + // Create an array with elements 99, 98 and 90 + %1 = integer_literal $Builtin.Word, 3 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*Int64 + %7 = integer_literal $Builtin.Int64, 99 + %8 = struct $Int64 (%7 : $Builtin.Int64) + store %8 to %6 : $*Int64 + %10 = integer_literal $Builtin.Word, 1 + %11 = index_addr %6 : $*Int64, %10 : $Builtin.Word + %12 = integer_literal $Builtin.Int64, 98 + %13 = struct $Int64 (%12 : $Builtin.Int64) + store %13 to %11 : $*Int64 + %15 = integer_literal $Builtin.Word, 2 + %16 = index_addr %6 : $*Int64, %15 : $Builtin.Word + %17 = integer_literal $Builtin.Int64, 90 + %18 = struct $Int64 (%17 : $Builtin.Int64) + store %18 to %16 : $*Int64 + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %21 = struct $OSLogMessageArrayStub(%20 : $OSLogInterpolationArrayStub) + return %21 : $OSLogMessageArrayStub +} + +// _allocateUninitializedArray(_:) +sil [serialized] [always_inline] [_semantics "array.uninitialized_intrinsic"] @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + +/// A function that models the use of an array. +sil @useArray: $@convention(thin) (@guaranteed Array) -> () + +// CHECK-LABEL: @testConstantFoldingOfArray +sil [ossa] @testConstantFoldingOfArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + + // Use the arguments property of OSLogMessageArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageArrayStub + %4 = struct_extract %3 : $OSLogMessageArrayStub, #OSLogMessageArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationArrayStub, #OSLogInterpolationArrayStub.arguments + %6 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageArrayStub + destroy_value %2 : $OSLogMessageArrayStub + %8 = tuple () + return %8 : $() + // CHECK: [[ELEM1:%[0-9]+]] = integer_literal $Builtin.Int64, 99 + // CHECK: [[ELEM1INT:%[0-9]+]] = struct $Int64 ([[ELEM1]] : $Builtin.Int64) + // CHECK: [[ELEM2:%[0-9]+]] = integer_literal $Builtin.Int64, 98 + // CHECK: [[ELEM2INT:%[0-9]+]] = struct $Int64 ([[ELEM2]] : $Builtin.Int64) + // CHECK: [[ELEM3:%[0-9]+]] = integer_literal $Builtin.Int64, 90 + // CHECK: [[ELEM3INT:%[0-9]+]] = struct $Int64 ([[ELEM3]] : $Builtin.Int64) + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 3 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] : $Builtin.RawPointer to [strict] $*Int64 + // CHECK: store [[ELEM1INT]] to [trivial] [[STORAGEADDR]] : $*Int64 + // CHECK: [[INDEX1:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[INDEXADDR1:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX1]] : $Builtin.Word + // CHECK: store [[ELEM2INT]] to [trivial] [[INDEXADDR1]] : $*Int64 + // CHECK: [[INDEX2:%[0-9]+]] = integer_literal $Builtin.Word, 2 + // CHECK: [[INDEXADDR2:%[0-9]+]] = index_addr [[STORAGEADDR]] : $*Int64, [[INDEX2]] : $Builtin.Word + // CHECK: store [[ELEM3INT]] to [trivial] [[INDEXADDR2]] : $*Int64 + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArray + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStubEmptyArrayInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub { +bb0(%0 : $Builtin.Int1): + // Create an empty array + %1 = integer_literal $Builtin.Word, 0 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %21 = struct $OSLogMessageArrayStub(%20 : $OSLogInterpolationArrayStub) + return %21 : $OSLogMessageArrayStub +} + +// CHECK-LABEL: @testConstantFoldingOfEmptyArray +sil [ossa] @testConstantFoldingOfEmptyArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageStubEmptyArrayInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageArrayStub + + // Use the arguments property of OSLogMessageArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageArrayStub + %4 = struct_extract %3 : $OSLogMessageArrayStub, #OSLogMessageArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationArrayStub, #OSLogInterpolationArrayStub.arguments + %6 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageArrayStub + destroy_value %2 : $OSLogMessageArrayStub + %8 = tuple () + return %8 : $() + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 0 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArray + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} + +/// A simplified stub for OSLogInterpolation type for testing folding of array +/// of strings. +struct OSLogInterpolationStringArrayStub { + var arguments: [String] +} + +/// A simplified stub for OSLogMessage for testing folding of array of strings. +struct OSLogMessageStringArrayStub { + var interpolation: OSLogInterpolationStringArrayStub +} + +/// A stub for OSLogMessage.init. The os_log optimization is driven by this +/// function. This function must take at least one argument which is required +/// by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStringArrayStubInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub { +bb0(%0 : $String): + // Create an array with one element "a" + %1 = integer_literal $Builtin.Word, 1 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*String + store %0 to %6 : $*String + + // Create an instance of OSLogMessageArrayStub using the above array. + %20 = struct $OSLogInterpolationStringArrayStub(%4 : $Array) + %21 = struct $OSLogMessageStringArrayStub(%20 : $OSLogInterpolationStringArrayStub) + return %21 : $OSLogMessageStringArrayStub +} + +/// A function that models the use of an array. +sil @useArrayString: $@convention(thin) (@guaranteed Array) -> () + +// CHECK-LABEL: @testConstantFoldingOfStringArray +sil [ossa] @testConstantFoldingOfStringArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStringArrayStub instance. + %10 = string_literal utf8 "ab" + %11 = integer_literal $Builtin.Word, 2 + %12 = integer_literal $Builtin.Int1, -1 + %13 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %14 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %15 = apply %14(%10, %11, %12, %13) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %1 = function_ref @oslogMessageStringArrayStubInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub + %2 = apply %1(%15) : $@convention(thin) (@owned String) -> @owned OSLogMessageStringArrayStub + + // Use the arguments property of OSLogMessageStringArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageStringArrayStub + %4 = struct_extract %3 : $OSLogMessageStringArrayStub, #OSLogMessageStringArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationStringArrayStub, #OSLogInterpolationStringArrayStub.arguments + %6 = function_ref @useArrayString : $@convention(thin) (@guaranteed Array) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array) -> () + end_borrow %3 : $OSLogMessageStringArrayStub + destroy_value %2 : $OSLogMessageStringArrayStub + %8 = tuple () + return %8 : $() + // The first instance of "ab" will be dead code eliminated. + // CHECK: [[LIT:%[0-9]+]] = string_literal utf8 "ab" + // CHECK: [[STRINGINIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRINGCONST:%[0-9]+]] = apply [[STRINGINIT]]([[LIT]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK: [[NUMELEMS:%[0-9]+]] = integer_literal $Builtin.Word, 1 + // CHECK: [[ALLOCATORREF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[TUPLE:%[0-9]+]] = apply [[ALLOCATORREF]]([[NUMELEMS]]) + // CHECK: ([[ARRAY:%[0-9]+]], [[STORAGEPTR:%[0-9]+]]) = destructure_tuple [[TUPLE]] + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] : $Builtin.RawPointer to [strict] $*String + // CHECK: store [[STRINGCONST]] to [init] [[STORAGEADDR]] : $*String + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[ARRAY]] + // CHECK: [[USEREF:%[0-9]+]] = function_ref @useArrayString + // CHECK: apply [[USEREF]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[ARRAY]] : $Array +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageArrayInterpolationInit : $@convention(thin) (@owned OSLogInterpolationArrayStub) + -> @owned OSLogMessageArrayStub { +bb0(%0 : @owned $OSLogInterpolationArrayStub): + %1 = struct $OSLogMessageArrayStub(%0 : $OSLogInterpolationArrayStub) + return %1 : $OSLogMessageArrayStub +} + +// CHECK-LABEL: @testNoFoldingOfArrayLiterals +sil [ossa] @testNoFoldingOfArrayLiterals : $@convention(thin) () -> () { +bb0: + // Create an empty array + %1 = integer_literal $Builtin.Word, 0 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%4, %ignore) = destructure_tuple %3 : $(Array, Builtin.RawPointer) + %5 = copy_value %4 : $Array + %6 = struct $OSLogInterpolationArrayStub(%4 : $Array) + %7 = function_ref @oslogMessageArrayInterpolationInit : $@convention(thin) (@owned OSLogInterpolationArrayStub) -> @owned OSLogMessageArrayStub + %8 = apply %7(%6) : $@convention(thin) (@owned OSLogInterpolationArrayStub) -> @owned OSLogMessageArrayStub + // Use the array literal. + %9 = function_ref @useArray : $@convention(thin) (@guaranteed Array) -> () + %10 = apply %9(%5) : $@convention(thin) (@guaranteed Array) -> () + destroy_value %5 : $Array + destroy_value %8 : $OSLogMessageArrayStub + %11 = tuple () + return %11 : $() + // There should be only one instance of _allocateUninitializedArray + // CHECK-LABEL: function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-NOT: function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF +} + +// Check folding of closures by the OSLogOptimization pass. + +/// A simplified stub for OSLogInterpolation type for testing closure folding. +struct OSLogInterpolationClosureStub { + var closure: () -> Int32 +} + +/// A simplified stub for OSLogMessage for testing closure folding. +struct OSLogMessageClosureStub { + var interpolation: OSLogInterpolationClosureStub +} + +sil private @idFunction : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + return %0 : $Int32 +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub { +bb0(%0 : $Builtin.Int1): + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @idFunction : $@convention(thin) (Int32) -> Int32 + %10 = partial_apply [callee_guaranteed] %9(%8) : $@convention(thin) (Int32) -> Int32 + + // Create an instance of OSLogMessageClosureStub using the above closure. + %20 = struct $OSLogInterpolationClosureStub(%10 : $@callee_guaranteed () -> Int32) + %21 = struct $OSLogMessageClosureStub(%20 : $OSLogInterpolationClosureStub) + return %21 : $OSLogMessageClosureStub +} + +/// A function that models the use of a closure. +sil @useClosure: $@convention(thin) (@callee_guaranteed () -> Int32) -> () + +// CHECK-LABEL: @testConstantFoldingOfClosure +sil [ossa] @testConstantFoldingOfClosure : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + + // Use the closure property of OSLogMessageClosureStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageClosureStub + %4 = struct_extract %3 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %6 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %7 = apply %6(%5) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %3 : $OSLogMessageClosureStub + destroy_value %2 : $OSLogMessageClosureStub + %8 = tuple () + return %8 : $() + // CHECK: [[LIT:%[0-9]+]] = integer_literal $Builtin.Int32, 81 + // CHECK: [[INT:%[0-9]+]] = struct $Int32 ([[LIT]] : $Builtin.Int32) + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[INT]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +sil private @constantFunction : $@convention(thin) () -> Int32 { +bb0: + %0 = integer_literal $Builtin.Int32, 91 + %1 = struct $Int32 (%0 : $Builtin.Int32) + return %1 : $Int32 +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageThinClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub { +bb0(%0 : $Builtin.Int1): + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @constantFunction : $@convention(thin) () -> Int32 + %10 = thin_to_thick_function %9 : $@convention(thin) () -> Int32 to $@callee_guaranteed () -> Int32 + // Create an instance of OSLogMessageClosureStub using the above closure. + %20 = struct $OSLogInterpolationClosureStub(%10 : $@callee_guaranteed () -> Int32) + %21 = struct $OSLogMessageClosureStub(%20 : $OSLogInterpolationClosureStub) + return %21 : $OSLogMessageClosureStub +} + +// CHECK-LABEL: @testConstantFoldingOfThinClosure +sil [ossa] @testConstantFoldingOfThinClosure : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageThinClosureStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureStub + + %3 = begin_borrow %2 : $OSLogMessageClosureStub + %4 = struct_extract %3 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %6 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %7 = apply %6(%5) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %3 : $OSLogMessageClosureStub + destroy_value %2 : $OSLogMessageClosureStub + %8 = tuple () + return %8 : $() + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @constantFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]() + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +/// A simplified stub for OSLogInterpolation type for testing folding of +/// closures with non-trivial captures. +struct OSLogInterpolationStringCapture { + var closure: () -> String +} + +/// A simplified stub for OSLogMessage for testing folding of closures with +/// non-trivial captures. +struct OSLogMessageStringCapture { + var interpolation: OSLogInterpolationStringCapture +} + +sil [ossa] @idString : $@convention(thin) (@guaranteed String) -> @owned String { +bb0(%0 : @guaranteed $String): + %1 = copy_value %0 : $String + return %1 : $String +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageStringCaptureInit : $@convention(thin) (@owned OSLogInterpolationStringCapture) + -> @owned OSLogMessageStringCapture { +bb0(%0 : @owned $OSLogInterpolationStringCapture): + %5 = struct $OSLogMessageStringCapture(%0 : $OSLogInterpolationStringCapture) + return %5 : $OSLogMessageStringCapture +} + +sil @useStringCapture: $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + +// CHECK-LABEL: @testConstantFoldingOfStringCapture +sil [ossa] @testConstantFoldingOfStringCapture : $@convention(thin) (@guaranteed String) -> () { +bb0(%0 : @guaranteed $String): + %1 = function_ref @idString : $@convention(thin) (@guaranteed String) -> @owned String + %2 = copy_value %0 : $String + %3 = partial_apply [callee_guaranteed] %1(%2) : $@convention(thin) (@guaranteed String) -> @owned String + %4 = struct $OSLogInterpolationStringCapture(%3 : $@callee_guaranteed () -> @owned String) + %5 = function_ref @oslogMessageStringCaptureInit : $@convention(thin) (@owned OSLogInterpolationStringCapture) -> @owned OSLogMessageStringCapture + %6 = apply %5(%4) : $@convention(thin) (@owned OSLogInterpolationStringCapture) -> @owned OSLogMessageStringCapture + + %13 = begin_borrow %6 : $OSLogMessageStringCapture + %14 = struct_extract %13 : $OSLogMessageStringCapture, #OSLogMessageStringCapture.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStringCapture, #OSLogInterpolationStringCapture.closure + %16 = function_ref @useStringCapture : $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + %17 = apply %16(%15) : $@convention(thin) (@callee_guaranteed () -> @owned String) -> () + end_borrow %13 : $OSLogMessageStringCapture + destroy_value %6 : $OSLogMessageStringCapture + %18 = tuple () + return %18 : $() + // The first instance of function_ref @idString will be dead code eliminated. + // CHECK: [[ORIGCAPTURE:%[0-9]+]] = copy_value %0 : $String + // CHECK: [[NEWCAPTURE:%[0-9]+]] = copy_value [[ORIGCAPTURE]] : $String + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @idString + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[NEWCAPTURE]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> @owned String + // CHECK: [[USE:%[0-9]+]] = function_ref @useStringCapture + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +sil [ossa] @genericFunction : $@convention(thin) (@in_guaranteed U, @in_guaranteed V) -> Int32 { +bb0(%0 : $*U, %1 : $*V): + %2 = integer_literal $Builtin.Int32, 99 + %3 = struct $Int32 (%2 : $Builtin.Int32) + return %3 : $Int32 +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageGenericClosureInit : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub { +bb0(%0 : @owned $OSLogInterpolationClosureStub): + %5 = struct $OSLogMessageClosureStub(%0 : $OSLogInterpolationClosureStub) + return %5 : $OSLogMessageClosureStub +} + +// CHECK-LABEL: @testConstantFoldingOfGenericClosure +sil [ossa] @testConstantFoldingOfGenericClosure : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int1, 1 + %1 = alloc_stack $Int64 + %2 = alloc_stack $Bool + %3 = struct $Bool (%0 : $Builtin.Int1) + store %3 to [trivial] %2 : $*Bool + %4 = integer_literal $Builtin.Int64, 81 + %5 = struct $Int64 (%4 : $Builtin.Int64) + store %5 to [trivial] %1 : $*Int64 + + %6 = function_ref @genericFunction : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Int32 + %7 = partial_apply [callee_guaranteed] %6(%1, %2) : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Int32 + %8 = struct $OSLogInterpolationClosureStub(%7 : $@callee_guaranteed () -> Int32) + %11 = function_ref @oslogMessageGenericClosureInit : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub + %12 = apply %11(%8) : $@convention(thin) (@owned OSLogInterpolationClosureStub) -> @owned OSLogMessageClosureStub + + %13 = begin_borrow %12 : $OSLogMessageClosureStub + %14 = struct_extract %13 : $OSLogMessageClosureStub, #OSLogMessageClosureStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationClosureStub, #OSLogInterpolationClosureStub.closure + %16 = function_ref @useClosure : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + %17 = apply %16(%15) : $@convention(thin) (@callee_guaranteed () -> Int32) -> () + end_borrow %13 : $OSLogMessageClosureStub + destroy_value %12 : $OSLogMessageClosureStub + dealloc_stack %2 : $*Bool + dealloc_stack %1 : $*Int64 + %18 = tuple () + return %18 : $() + // The first instance of function_ref @genericFunction will be dead-code eliminated. + // CHECK: [[FUNREF:%[0-9]+]] = function_ref @genericFunction + // CHECK: [[CLOSURE:%[0-9]+]] = partial_apply [callee_guaranteed] [[FUNREF]]([[CAPTURE1:%[0-9]+]], [[CAPTURE2:%[0-9]+]]) + // CHECK: [[BORROW:%[0-9]+]] = begin_borrow [[CLOSURE]] : $@callee_guaranteed () -> Int32 + // CHECK: [[USE:%[0-9]+]] = function_ref @useClosure + // CHECK: apply [[USE]]([[BORROW]]) + // CHECK: end_borrow [[BORROW]] + // CHECK: destroy_value [[CLOSURE]] +} + +// Check folding of array of closures. This is essentially the feature needed +// by the OSLog overlay. + +/// A simplified stub for OSLogInterpolation type for testing folding of array +/// of closures. +struct OSLogInterpolationClosureArrayStub { + var closure: [() -> Int32] +} + +/// A simplified stub for OSLogMessage for testing folding of array of closures. +struct OSLogMessageClosureArrayStub { + var interpolation: OSLogInterpolationClosureArrayStub +} + +sil private @closure1 : $@convention(thin) (Int32) -> Int32 { +bb0(%0 : $Int32): + return %0 : $Int32 +} + +/// A stub for OSLogMessage.init. The optimization is driven by this function. +/// This function must take at least one argument which is required by the pass. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageClosureArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub { +bb0(%0 : $Builtin.Int1): + %1 = integer_literal $Builtin.Word, 1 + // function_ref _allocateUninitializedArray(_:) + %2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %3 = apply %2<() -> Int32>(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = tuple_extract %3 : $(Array<() -> Int32>, Builtin.RawPointer), 0 + %5 = tuple_extract %3 : $(Array<() -> Int32>, Builtin.RawPointer), 1 + %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*@callee_guaranteed () -> Int32 + %7 = integer_literal $Builtin.Int32, 81 + %8 = struct $Int32 (%7 : $Builtin.Int32) + %9 = function_ref @closure1 : $@convention(thin) (Int32) -> Int32 + %10 = partial_apply [callee_guaranteed] %9(%8) : $@convention(thin) (Int32) -> Int32 + store %10 to %6 : $*@callee_guaranteed () -> Int32 + + // Create an instance of OSLogMessageClosureArrayStub using the above array + // of closures. + %20 = struct $OSLogInterpolationClosureArrayStub(%4 : $Array<() -> Int32>) + %21 = struct $OSLogMessageClosureArrayStub(%20 : $OSLogInterpolationClosureArrayStub) + return %21 : $OSLogMessageClosureArrayStub +} + +/// A function that models the use of an array of closures. +sil @useClosureArray: $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + +// CHECK-LABEL: @testConstantFoldingOfClosureArray +sil [ossa] @testConstantFoldingOfClosureArray : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageClosureArrayStub instance. + %0 = integer_literal $Builtin.Int1, 1 + %1 = function_ref @oslogMessageClosureArrayStubInit : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub + %2 = apply %1(%0) : $@convention(thin) (Builtin.Int1) -> @owned OSLogMessageClosureArrayStub + + // Use the arguments property of OSLogMessageClosureArrayStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %3 = begin_borrow %2 : $OSLogMessageClosureArrayStub + %4 = struct_extract %3 : $OSLogMessageClosureArrayStub, #OSLogMessageClosureArrayStub.interpolation + %5 = struct_extract %4 : $OSLogInterpolationClosureArrayStub, #OSLogInterpolationClosureArrayStub.closure + %6 = function_ref @useClosureArray : $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + %7 = apply %6(%5) : $@convention(thin) (@guaranteed Array<() -> Int32>) -> () + end_borrow %3 : $OSLogMessageClosureArrayStub + destroy_value %2 : $OSLogMessageClosureArrayStub + %8 = tuple () + return %8 : $() +} + +// The following tests are for checking dead-code elimination performed by the +// OSLogOptimization pass. + +struct OSLogInterpolationDCEStub { + var formatString: String +} + +struct OSLogMessageDCEStub { + var interpolation: OSLogInterpolationDCEStub +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @owned $OSLogInterpolationDCEStub): + %1 = struct $OSLogMessageDCEStub (%0 : $OSLogInterpolationDCEStub) + return %1 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfStructCreation +sil [ossa] @testDCEOfStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = copy_value %6 : $OSLogInterpolationDCEStub + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = begin_borrow %9 : $OSLogMessageDCEStub + end_borrow %10 : $OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +sil [ossa] [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub { +bb0(%0 : @guaranteed $OSLogInterpolationDCEStub): + %2 = copy_value %0 : $OSLogInterpolationDCEStub + %3 = struct $OSLogMessageDCEStub (%2 : $OSLogInterpolationDCEStub) + return %3 : $OSLogMessageDCEStub +} + +// CHECK-LABEL: @testDCEOfGuaranteedStructCreation +sil [ossa] @testDCEOfGuaranteedStructCreation : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = begin_borrow %6 : $OSLogInterpolationDCEStub + %9 = function_ref @oslogMessageGuaranteedInit : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %10 = apply %9(%7) : $@convention(thin) (@guaranteed OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %10 : $OSLogMessageDCEStub + end_borrow %7 : $OSLogInterpolationDCEStub + destroy_value %6 : $OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +// CHECK-LABEL: @testLifetimeAdjustmentOfDCE +sil [ossa] @testLifetimeAdjustmentOfDCE : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = copy_value %5 : $String + %7 = struct $OSLogInterpolationDCEStub(%5 : $String) + %8 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %9 = apply %8(%7) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %9 : $OSLogMessageDCEStub + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%6) : $@convention(thin) (@guaranteed String) -> () + destroy_value %6 : $String + %13 = tuple () + return %13 : $() + // CHECK: [[STRINGINITREF:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRING:%[0-9]+]] = apply [[STRINGINITREF]]( + // CHECK-NEXT: [[COPY:%[0-9]+]] = copy_value [[STRING]] + // CHECK-NEXT: destroy_value [[STRING]] + // CHECK-NOT: OSLogInterpolationDCEStub + // CHECK-NOT: OSLogMessageDCEStub + // CHECK: return +} + +// Check dead-code elimination of alloc stack used only as inout parameter of a +// constant evaluable call (that only writes into that inout parameter). +sil [ossa] [_semantics "constant_evaluable"] @appendInterpolationStub : $@convention(thin) (@inout OSLogInterpolationDCEStub) -> () { +bb0(%0 : $*OSLogInterpolationDCEStub): + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: @testDCEOfAllocStack +sil [ossa] @testDCEOfAllocStack : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = alloc_stack $OSLogInterpolationDCEStub + store %6 to [init] %7 : $*OSLogInterpolationDCEStub + %8 = function_ref @appendInterpolationStub : $@convention(thin) (@inout OSLogInterpolationDCEStub) -> () + %9 = apply %8(%7) : $@convention(thin) (@inout OSLogInterpolationDCEStub) -> () + %10 = load [copy] %7 : $*OSLogInterpolationDCEStub + %11 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %12 = apply %11(%10) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %12 : $OSLogMessageDCEStub + destroy_addr %7 : $*OSLogInterpolationDCEStub + dealloc_stack %7 : $*OSLogInterpolationDCEStub + %13 = tuple () + return %13 : $() + // CHECK: bb0 + // CHECK-NEXT: [[EMPTYTUP:%[0-9]+]] = tuple () + // CHECK-NEXT: return [[EMPTYTUP]] +} + +// Check that dead-code elimination of alloc stack does not happen when there +// are multiple writable parameters +sil [ossa] [_semantics "constant_evaluable"] @appendInterpolationStubError : $@convention(thin) (@inout OSLogInterpolationDCEStub, @inout Builtin.Int32) -> () { +bb0(%0 : $*OSLogInterpolationDCEStub, %1 : $*Builtin.Int32): + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: @testNoDCEOfAllocStack +sil [ossa] @testNoDCEOfAllocStack : $@convention(thin) () -> () { +bb0: + %0 = string_literal utf8 "some message" + %1 = integer_literal $Builtin.Word, 12 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = struct $OSLogInterpolationDCEStub(%5 : $String) + %7 = alloc_stack $OSLogInterpolationDCEStub + store %6 to [init] %7 : $*OSLogInterpolationDCEStub + %8 = alloc_stack $Builtin.Int32 + %lit = integer_literal $Builtin.Int32, -1 + store %lit to [trivial] %8 : $*Builtin.Int32 + %9 = function_ref @appendInterpolationStubError : $@convention(thin) (@inout OSLogInterpolationDCEStub, @inout Builtin.Int32) -> () + %10 = apply %9(%7, %8) : $@convention(thin) (@inout OSLogInterpolationDCEStub, @inout Builtin.Int32) -> () + %11 = load [copy] %7 : $*OSLogInterpolationDCEStub + %12 = function_ref @oslogMessageDCEInit : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + %13 = apply %12(%11) : $@convention(thin) (@owned OSLogInterpolationDCEStub) -> @owned OSLogMessageDCEStub + destroy_value %13 : $OSLogMessageDCEStub + destroy_addr %7 : $*OSLogInterpolationDCEStub + dealloc_stack %8 : $*Builtin.Int32 + dealloc_stack %7 : $*OSLogInterpolationDCEStub + %14 = tuple () + return %14 : $() + // CHECK: bb0 + // CHECK: alloc_stack $OSLogInterpolationDCEStub +} diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 1b1dbe3512cb3..f51c732e97500 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos // Tests for the OSLogOptimization pass that performs compile-time analysis @@ -7,6 +8,7 @@ // the size of the byte buffer etc. are literals after the mandatory pipeline. import OSLogPrototype +import Foundation if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { @@ -43,6 +45,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest34testInterpolationWithFormatOptionsL_1hy0aB06LoggerV_tF @@ -76,6 +89,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest44testInterpolationWithFormatOptionsAndPrivacyL_1hy0aB06LoggerV_tF @@ -112,6 +136,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest38testInterpolationWithMultipleArgumentsL_1hy0aB06LoggerV_tF @@ -154,6 +189,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 3 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 9 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testLogMessageWithoutDataL_1hy0aB06LoggerV_tF @@ -191,6 +237,16 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 0 + + // Check whether argument array is folded. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 0 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testEscapingOfPercentsL_1hy0aB06LoggerV_tF @@ -261,6 +317,16 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 48 + + // Check whether argument array is folded. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 144 } // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testInt32InterpolationL_1hy0aB06LoggerV_tF @@ -333,6 +399,96 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 2 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 + } + + // CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testNSObjectInterpolationL_1hy0aB06LoggerV_tF + func testNSObjectInterpolation(h: Logger) { + let nsArray: NSArray = [0, 1, 2] + let nsDictionary: NSDictionary = [1 : ""] + h.log(""" + NSArray: \(nsArray, privacy: .public) \ + NSDictionary: \(nsDictionary, privacy: .private) + """) + // Check if there is a call to _os_log_impl with a literal format string. + // CHECK-DAG is used here as it is easier to perform the checks backwards + // from uses to the definitions. + + // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) + // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) + // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: [[LIT]] = string_literal utf8 "NSArray: %{public}@ NSDictionary: %{private}@" + + // Check if the size of the argument buffer is a constant. + + // CHECK-DAG: [[ALLOCATE:%[0-9]+]] = function_ref @$sSp8allocate8capacitySpyxGSi_tFZ + // CHECK-DAG: apply [[ALLOCATE]]([[BUFFERSIZE:%[0-9]+]], {{%.*}}) + // CHECK-DAG: [[BUFFERSIZE]] = struct $Int ([[BUFFERSIZELIT:%[0-9]+]] + // CHECK-64-DAG: [[BUFFERSIZELIT]] = integer_literal $Builtin.Int64, 22 + // CHECK-32-DAG: [[BUFFERSIZELIT]] = integer_literal $Builtin.Int32, 14 + + // Check whether the header bytes: premable and argument count are constants. + + // CHECK-DAG: [[SERIALIZE:%[0-9]+]] = function_ref @$s14OSLogPrototype9serialize_2atys5UInt8V_SpyAEGztF + // CHECK-DAG: apply [[SERIALIZE]]([[PREAMBLE:%[0-9]+]], {{%.*}}) + // CHECK-DAG: [[PREAMBLE]] = struct $UInt8 ([[PREAMBLELIT:%[0-9]+]] : $Builtin.Int8) + // CHECK-DAG: [[PREAMBLELIT]] = integer_literal $Builtin.Int8, 3 + + // CHECK-DAG: [[SERIALIZE:%[0-9]+]] = function_ref @$s14OSLogPrototype9serialize_2atys5UInt8V_SpyAEGztF + // CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}}) + // CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8) + // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 2 + + // Check whether argument array is folded. We need not check the contents of + // the array which is checked by a different test suite. + + // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF + // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) + // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) + // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 + } + + // CHECK-LABEL: @$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF + func testDeadCodeElimination( + h: Logger, + number: Int, + num32bit: Int32, + string: String + ) { + h.log("A message with no data") + h.log("smallstring") + h.log( + level: .error, + """ + A message with many interpolations \(number), \(num32bit), \(string), \ + and a suffix + """) + h.log( + level: .info, + """ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \ + \(1) \(1) \(1) \(1) \(1) \(48) \(49) + """) + let concatString = string + ":" + String(number) + h.log("\(concatString)") + // CHECK-NOT: OSLogMessage + // CHECK-NOT: OSLogInterpolation + // CHECK-LABEL: end sil function '$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF' } } diff --git a/test/SILOptimizer/access_enforcement_noescape_error.swift b/test/SILOptimizer/access_enforcement_noescape_error.swift index 9c8895acc9b20..a61b8b688d4a3 100644 --- a/test/SILOptimizer/access_enforcement_noescape_error.swift +++ b/test/SILOptimizer/access_enforcement_noescape_error.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -module-name access_enforcement_noescape -enforce-exclusivity=checked -Onone -emit-sil -swift-version 4 -verify -parse-as-library %s +// RUN: %target-swift-frontend -module-name access_enforcement_noescape -enforce-exclusivity=checked -Onone -emit-sil -swift-version 4 -verify -parse-as-library -enable-ownership-stripping-after-serialization %s // REQUIRES: asserts // This is the subset of tests from access_enforcement_noescape.swift diff --git a/test/SILOptimizer/access_marker_mandatory.swift b/test/SILOptimizer/access_marker_mandatory.swift index 0346b3a86bd31..97e27d5756542 100644 --- a/test/SILOptimizer/access_marker_mandatory.swift +++ b/test/SILOptimizer/access_marker_mandatory.swift @@ -49,8 +49,6 @@ func takeS(_ s: S) {} // CHECK: [[ADDRI:%.*]] = struct_element_addr [[WRITE]] : $*S, #S.i // CHECK: store %{{.*}} to [[ADDRI]] : $*Int // CHECK: end_access [[WRITE]] -// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*S -// CHECK: end_access [[READ]] // CHECK: [[FTAKE:%.*]] = function_ref @$s23access_marker_mandatory5takeSyyAA1SVF : $@convention(thin) (@guaranteed S) -> () // CHECK: apply [[FTAKE]](%{{.*}}) : $@convention(thin) (@guaranteed S) -> () // CHECK-LABEL: } // end sil function '$s23access_marker_mandatory14modifyAndReadS1oyyXl_tF' diff --git a/test/SILOptimizer/access_wmo_diagnose.swift b/test/SILOptimizer/access_wmo_diagnose.swift index 4ee0b56d97b9e..02a2877962f5f 100644 --- a/test/SILOptimizer/access_wmo_diagnose.swift +++ b/test/SILOptimizer/access_wmo_diagnose.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -parse-as-library -emit-sil -enforce-exclusivity=checked -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -parse-as-library -emit-sil -enforce-exclusivity=checked -primary-file %s -o /dev/null -verify // AccessEnforcementWMO assumes that the only way to address a global or static // property is via a formal begin_access. If we ever allow keypaths for static diff --git a/test/SILOptimizer/allocbox_to_stack_ownership.sil b/test/SILOptimizer/allocbox_to_stack_ownership.sil index 73f403011570d..12cfada7e7288 100644 --- a/test/SILOptimizer/allocbox_to_stack_ownership.sil +++ b/test/SILOptimizer/allocbox_to_stack_ownership.sil @@ -391,10 +391,10 @@ bb0(%0 : $Int): return %10 : $() // id: %11 } -// CHECK-LABEL: sil shared [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n -// CHECK-LABEL: sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ +// CHECK-LABEL: sil shared [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n +// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ // struct.(useStack (t : Swift.Int) -> ()).(closure #1) -sil private [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { +sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } ): %1 = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 // function_ref Swift.++ @postfix (x : @inout A) -> A diff --git a/test/SILOptimizer/allocboxtostack_escape.swift b/test/SILOptimizer/allocboxtostack_escape.swift new file mode 100644 index 0000000000000..911f97806db56 --- /dev/null +++ b/test/SILOptimizer/allocboxtostack_escape.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend %s -o /dev/null -verify -emit-sil + +struct Box { + var value: T +} + +class Klass {} + +func myPrint(_ x: inout Box) -> () { print(x) } + +func testError() -> (() -> ()) { + @_semantics("boxtostack.mustbeonstack") + var x = Box(value: Klass()) // expected-error {{Can not promote value from heap to stack due to value escaping}} + let result = { // expected-note {{value escapes here}} + myPrint(&x) + } + return result +} + +func main() { + testError()() +} + +main() diff --git a/test/SILOptimizer/alwaysemitintoclient.swift b/test/SILOptimizer/alwaysemitintoclient.swift new file mode 100644 index 0000000000000..cbbb0b6d18c1c --- /dev/null +++ b/test/SILOptimizer/alwaysemitintoclient.swift @@ -0,0 +1,33 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -parse-as-library -emit-module -emit-module-path=%t/Module.swiftmodule -module-name=Module -DMODULE %s -O -emit-sil | %FileCheck %s + +// Also link to make sure we don't eliminate any needed symbols. + +// RUN: %target-swift-frontend -parse-as-library -emit-module -emit-module-path=%t/Module.swiftmodule -module-name=Module -DMODULE %s -O -c -o module.o +// RUN: %target-build-swift -DMAIN %s -I%t -O -o %t/a.out + +#if MODULE + +// Check if the optimizer eliminates testit() in Module. + +// CHECK-NOT: {{sil .*testit.*}} + +@_alwaysEmitIntoClient +@inline(never) +public func testit() { + print("hello") +} + +#endif + + +#if MAIN + +import Module + +public func caller() { + testit() +} + +#endif + diff --git a/test/SILOptimizer/arcsequenceopts.sil b/test/SILOptimizer/arcsequenceopts.sil index 1cc48079154a1..b6e024270a495 100644 --- a/test/SILOptimizer/arcsequenceopts.sil +++ b/test/SILOptimizer/arcsequenceopts.sil @@ -571,10 +571,16 @@ bb0(%0 : $Cls): return %r : $() } -// CHECK-LABEL: sil @dont_remove_as_local_object_indirectly_escapes_to_callee -// CHECK: retain_value -// CHECK: release_value -sil @dont_remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// The original SIL already has an over-release of the parent. +// +// CHECK-LABEL: sil @remove_as_local_object_indirectly_escapes_to_callee +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @remove_as_local_object_indirectly_escapes_to_callee : $@convention(thin) (Cls) -> () { bb0(%0 : $Cls): %1 = alloc_ref $Cls %2 = alloc_ref $Container @@ -589,6 +595,30 @@ bb0(%0 : $Cls): return %r : $() } +// Remove a retain release "pair" for the local reference if a child object. +// +// The local 'Cls' reference is now effectively "moved" into the parent +// 'Container' object, so the child will be destroyed with the parent. +// +// CHECK-LABEL: sil @local_object_indirectly_escapes_to_use_and_owned_arg +// CHECK-NOT: retain_value +// CHECK-NOT: release_value +sil @local_object_indirectly_escapes_to_use_and_owned_arg : $@convention(thin) (Cls) -> () { +bb0(%0 : $Cls): + %1 = alloc_ref $Cls + %2 = alloc_ref $Container + %3 = ref_element_addr %2 : $Container, #Container.c + store %1 to %3 : $*Cls + retain_value %1 : $Cls + %f1 = function_ref @use_container : $@convention(thin) (@guaranteed Container) -> () + apply %f1 (%2) : $@convention(thin) (@guaranteed Container) -> () + %f2 = function_ref @release_container : $@convention(thin) (Container) -> () + apply %f2 (%2) : $@convention(thin) (Container) -> () + release_value %1 : $Cls + %r = tuple() + return %r : $() +} + sil @release_arg1 : $@convention(thin) (Cls, Cls) -> () { bb0(%0 : $Cls, %1 : $Cls): strong_release %1 : $Cls @@ -596,6 +626,14 @@ bb0(%0 : $Cls, %1 : $Cls): return %r : $() } +sil @use_container : $@convention(thin) (@guaranteed Container) -> () { +bb0(%0 : $Container): + %1 = ref_element_addr %0 : $Container, #Container.c + %2 = load %1 : $*Cls + %r = tuple() + return %r : $() +} + sil @release_container : $@convention(thin) (Container) -> () { bb0(%0 : $Container): strong_release %0 : $Container diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 7a0dfad4330ea..aa218c2c6c88f 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -enforce-exclusivity=unchecked %s | %FileCheck %s +// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // This is an end-to-end test of the array(contentsOf) -> array(Element) optimization @@ -14,18 +14,15 @@ public func testInt(_ a: inout [Int]) { a += [1] } -// CHECK-LABEL: sil @{{.*}}testThreeInt -// CHECK-NOT: apply -// CHECK: [[FR:%[0-9]+]] = function_ref @$sSa15reserveCapacityyySiFSi_Tg5 -// CHECK-NEXT: apply [[FR]] -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: tuple -// CHECK-NEXT: return +// CHECK-LABEL: sil @{{.*}}testThreeInts +// CHECK-DAG: [[FR:%[0-9]+]] = function_ref @${{(sSa15reserveCapacityyySiFSi_Tg5|sSa16_createNewBuffer)}} +// CHECK-DAG: apply [[FR]] +// CHECK-DAG: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK-DAG: apply [[F]] +// CHECK: } // end sil function '{{.*}}testThreeInts{{.*}}' + public func testThreeInts(_ a: inout [Int]) { a += [1, 2, 3] } @@ -57,3 +54,20 @@ public func testString(_ a: inout [String], s: String) { public func dontPropagateContiguousArray(_ a: inout ContiguousArray) { a += [4] } + +// Check if the specialized Array.append(contentsOf:) is reasonably optimized for Array. + +// CHECK-LABEL: sil shared {{.*}}@$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5 + +// There should only be a single call to _createNewBuffer or reserveCapacityForAppend/reserveCapacityImpl. + +// CHECK-NOT: apply +// CHECK: [[F:%[0-9]+]] = function_ref @{{.*(_createNewBuffer|reserveCapacity).*}} +// CHECK-NEXT: apply [[F]] +// CHECK-NOT: apply + +// The number of basic blocks should not exceed 20 (ideally there are no more than 16 blocks in this function). +// CHECK-NOT: bb20: + +// CHECK: } // end sil function '$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5 + diff --git a/test/SILOptimizer/array_specialize.sil b/test/SILOptimizer/array_specialize.sil index 75dafe032f088..688bb01071cd7 100644 --- a/test/SILOptimizer/array_specialize.sil +++ b/test/SILOptimizer/array_specialize.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -array-specialize +// RUN: %target-sil-opt -enable-sil-verify-all %s -array-property-opt sil_stage canonical diff --git a/test/SILOptimizer/basic-aa.sil b/test/SILOptimizer/basic-aa.sil index e213d44c30559..bf6c3fca8366e 100644 --- a/test/SILOptimizer/basic-aa.sil +++ b/test/SILOptimizer/basic-aa.sil @@ -110,12 +110,12 @@ bb1(%1 : $*Builtin.NativeObject, %2 : $*Builtin.NativeObject): // Assume that inout arguments alias to preserve memory safety. // -// CHECK-LABEL: @inout_args_may_alias +// CHECK-LABEL: @inout_args_may_not_alias // CHECK: PAIR #1. // CHECK-NEXT: %0 = argument of bb0 // CHECK-NEXT: %1 = argument of bb0 -// CHECK-NEXT: MayAlias -sil @inout_args_may_alias: $@convention(thin) (@inout Builtin.NativeObject, @inout Builtin.NativeObject) -> () { +// CHECK-NEXT: NoAlias +sil @inout_args_may_not_alias: $@convention(thin) (@inout Builtin.NativeObject, @inout Builtin.NativeObject) -> () { bb0(%0 : $*Builtin.NativeObject, %1 : $*Builtin.NativeObject): %2 = tuple() return %2 : $() diff --git a/test/SILOptimizer/bridged_casts_folding.sil b/test/SILOptimizer/bridged_casts_folding.sil index b202b57fe849e..f45fa00e3adfc 100644 --- a/test/SILOptimizer/bridged_casts_folding.sil +++ b/test/SILOptimizer/bridged_casts_folding.sil @@ -13,7 +13,7 @@ sil_vtable NSObjectSubclass {} // CHECK-LABEL: sil @anyhashable_cast_unconditional // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NEXT: destroy_addr %0 -// CHECK-NEXT: [[CAST:%.*]] = unconditional_checked_cast [[BRIDGED]] : $NSObject to $NSObjectSubclass +// CHECK-NEXT: [[CAST:%.*]] = unconditional_checked_cast [[BRIDGED]] : $NSObject to NSObjectSubclass sil [ossa] @anyhashable_cast_unconditional : $@convention(thin) (@in AnyHashable) -> @owned NSObjectSubclass { entry(%0 : $*AnyHashable): %1 = alloc_stack $NSObjectSubclass @@ -27,7 +27,7 @@ entry(%0 : $*AnyHashable): // CHECK-LABEL: sil @anyhashable_cast_take_always // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NEXT: destroy_addr %0 -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] sil [ossa] @anyhashable_cast_take_always : $@convention(thin) (@in AnyHashable, @owned NSObjectSubclass) -> @owned NSObjectSubclass { entry(%0 : $*AnyHashable, %1 : @owned $NSObjectSubclass): %2 = alloc_stack $NSObjectSubclass @@ -49,7 +49,7 @@ bb3(%8 : @owned $NSObjectSubclass): // CHECK-LABEL: sil @anyhashable_cast_take_on_success // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // CHECK: [[YES]]{{.*}}: // CHECK-NEXT: destroy_addr %0 sil [ossa] @anyhashable_cast_take_on_success : $@convention(thin) (@in AnyHashable, @owned NSObjectSubclass) -> @owned NSObjectSubclass { @@ -76,7 +76,7 @@ bb3(%8 : @owned $NSObjectSubclass): // CHECK-NOT: copy_addr // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NOT: destroy_addr -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // CHECK: [[YES]]{{.*}}: // CHECK-NOT: dealloc_stack // CHECK: [[NO]]{{.*}}: diff --git a/test/SILOptimizer/bridged_casts_folding.swift b/test/SILOptimizer/bridged_casts_folding.swift index b248a7bd58d57..f4beab303a6f6 100644 --- a/test/SILOptimizer/bridged_casts_folding.swift +++ b/test/SILOptimizer/bridged_casts_folding.swift @@ -906,7 +906,7 @@ var anyHashable: AnyHashable = 0 // CHECK: [[GLOBAL:%[0-9]+]] = global_addr @$s21bridged_casts_folding11anyHashables03AnyE0Vv // CHECK: [[FUNC:%.*]] = function_ref @$ss11AnyHashableV10FoundationE19_bridgeToObjectiveCSo8NSObjectCyF // CHECK-NEXT: apply [[FUNC]]([[GLOBAL]]) -// CHECK-NEXT: unconditional_checked_cast {{%.*}} : $NSObject to $NSObjectSubclass +// CHECK-NEXT: unconditional_checked_cast {{%.*}} : $NSObject to NSObjectSubclass // CHECK: } // end sil function '$s21bridged_casts_folding29testUncondCastSwiftToSubclassAA08NSObjectI0CyF' @inline(never) public func testUncondCastSwiftToSubclass() -> NSObjectSubclass { diff --git a/test/SILOptimizer/bridged_casts_folding_ownership.sil b/test/SILOptimizer/bridged_casts_folding_ownership.sil index b6ec383cad2b2..5a6ab44eaee7b 100644 --- a/test/SILOptimizer/bridged_casts_folding_ownership.sil +++ b/test/SILOptimizer/bridged_casts_folding_ownership.sil @@ -13,7 +13,7 @@ sil_vtable NSObjectSubclass {} // CHECK-LABEL: sil @anyhashable_cast_unconditional : // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NEXT: destroy_addr %0 -// CHECK-NEXT: [[CAST:%.*]] = unconditional_checked_cast [[BRIDGED]] : $NSObject to $NSObjectSubclass +// CHECK-NEXT: [[CAST:%.*]] = unconditional_checked_cast [[BRIDGED]] : $NSObject to NSObjectSubclass // CHECK-NEXT: return [[CAST]] // CHECK: } // end sil function 'anyhashable_cast_unconditional' sil [ossa] @anyhashable_cast_unconditional : $@convention(thin) (@in AnyHashable) -> @owned NSObjectSubclass { @@ -29,7 +29,7 @@ entry(%0 : $*AnyHashable): // CHECK-LABEL: sil @anyhashable_cast_take_always // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NEXT: destroy_addr %0 -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] sil [ossa] @anyhashable_cast_take_always : $@convention(thin) (@in AnyHashable, @owned NSObjectSubclass) -> @owned NSObjectSubclass { entry(%0 : $*AnyHashable, %1 : @owned $NSObjectSubclass): %2 = alloc_stack $NSObjectSubclass @@ -51,7 +51,7 @@ bb3(%8 : @owned $NSObjectSubclass): // CHECK-LABEL: sil @anyhashable_cast_take_on_success // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // CHECK: [[YES]]{{.*}}: // CHECK-NEXT: destroy_addr %0 sil [ossa] @anyhashable_cast_take_on_success : $@convention(thin) (@in AnyHashable, @owned NSObjectSubclass) -> @owned NSObjectSubclass { @@ -78,7 +78,7 @@ bb3(%8 : @owned $NSObjectSubclass): // CHECK-NOT: copy_addr // CHECK: [[BRIDGED:%.*]] = apply {{.*}}(%0) // CHECK-NOT: destroy_addr -// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to $NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[BRIDGED]] : $NSObject to NSObjectSubclass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // CHECK: [[YES]]{{.*}}: // CHECK-NOT: dealloc_stack // CHECK: [[NO]]{{.*}}: diff --git a/test/SILOptimizer/callee_analysis_crash.swift b/test/SILOptimizer/callee_analysis_crash.swift new file mode 100644 index 0000000000000..8dfdc3afd699f --- /dev/null +++ b/test/SILOptimizer/callee_analysis_crash.swift @@ -0,0 +1,41 @@ +// RUN: %target-swift-frontend -O %s -emit-ir -o /dev/null + +// Check that we don't crash here. + +enum E { + case A + case B(T) +} + +public protocol P { + associatedtype A +} + +internal class Base { + func foo() -> E { + return .A + } +} + +struct Outer where T.A == Int { + private class Inner : Base { + + // This overridden function has a different ABI than the base implementation. + // The BasicCalleeAnalysis put both methods in the callee list, which let + // some optimizations crash. + override func foo() -> E { + return .A + } + } +} + +@inline(never) +func getit(_ t: T) -> Base { + return Base() +} + +public func testit(_ t: T) { + let b = getit(t) + b.foo() +} + diff --git a/test/SILOptimizer/cast_folding.swift b/test/SILOptimizer/cast_folding.swift index 156b6a2d54780..1394bbc7b10d0 100644 --- a/test/SILOptimizer/cast_folding.swift +++ b/test/SILOptimizer/cast_folding.swift @@ -1072,7 +1072,7 @@ public func testCastToPForOptionalFailure() -> Bool { struct Underlying : P { } -func returnOpaque() -> some P { +public func returnOpaque() -> some P { return Underlying() } @@ -1083,6 +1083,7 @@ func returnOpaque() -> some P { // MANDATORY: [[U:%.*]] = alloc_stack $Underlying // MANDATORY: unconditional_checked_cast_addr @_opaqueReturnTypeOf{{.*}}in [[O]] : $*@_opaqueReturnTypeOf{{.*}}to Underlying in [[U]] : $*Underlying // MANDATORY: load [[U]] : $*Underlying +@inlinable public func testCastOpaqueArchetype() { let o = returnOpaque() as! Underlying } diff --git a/test/SILOptimizer/cast_folding_no_bridging.sil b/test/SILOptimizer/cast_folding_no_bridging.sil index 4a9700253232e..1a43616525ad0 100644 --- a/test/SILOptimizer/cast_folding_no_bridging.sil +++ b/test/SILOptimizer/cast_folding_no_bridging.sil @@ -49,7 +49,7 @@ bb3: // CHECK: return sil @testObjCToObjC : $@convention(thin) (NSSet, NSSet) -> @owned NSSet { bb0(%0 : $NSSet, %1 : $NSSet): - checked_cast_br %0 : $NSSet to $NSSet, bb1, bb3 + checked_cast_br %0 : $NSSet to NSSet, bb1, bb3 bb1(%2 : $NSSet): br bb2(%2 : $NSSet) diff --git a/test/SILOptimizer/cast_folding_objc.swift b/test/SILOptimizer/cast_folding_objc.swift index ab3046a987926..613a92becef1f 100644 --- a/test/SILOptimizer/cast_folding_objc.swift +++ b/test/SILOptimizer/cast_folding_objc.swift @@ -319,7 +319,7 @@ public class MyString: NSString {} // CHECK: [[BRIDGED_VALUE:%.*]] = apply [[FUNC]]([[ARG]]) // CHECK-NOT: apply // CHECK-NOT: unconditional_checked_cast -// CHECK: checked_cast_br [[BRIDGED_VALUE]] : $NSString to $MyString, [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[BRIDGED_VALUE]] : $NSString to MyString, [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] // // CHECK: [[SUCC_BB]]([[CAST_BRIDGED_VALUE:%.*]] : $MyString) // CHECK: [[SOME:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[CAST_BRIDGED_VALUE]] : $MyString diff --git a/test/SILOptimizer/cast_foldings.sil b/test/SILOptimizer/cast_foldings.sil index c83c40aeea4c9..2f949d2beb02b 100644 --- a/test/SILOptimizer/cast_foldings.sil +++ b/test/SILOptimizer/cast_foldings.sil @@ -12,21 +12,21 @@ struct S {} @objc class NSCloud {} // CHECK-LABEL: sil @dont_fold_unconditional_checked_cast_to_existentials -// CHECK: unconditional_checked_cast %1 : $NSCloud to $AnyObject +// CHECK: unconditional_checked_cast %1 : $NSCloud to AnyObject sil @dont_fold_unconditional_checked_cast_to_existentials : $@convention(thin) () -> AnyObject { entry: %0 = alloc_stack $NSCloud %1 = load %0 : $*NSCloud - %2 = unconditional_checked_cast %1 : $NSCloud to $AnyObject + %2 = unconditional_checked_cast %1 : $NSCloud to AnyObject dealloc_stack %0 : $*NSCloud return %2 : $AnyObject } // CHECK-LABEL: sil @dont_fold_unconditional_checked_cast_from_existentials -// CHECK: unconditional_checked_cast %0 : $AnyObject to $NSCloud +// CHECK: unconditional_checked_cast %0 : $AnyObject to NSCloud sil @dont_fold_unconditional_checked_cast_from_existentials : $@convention(thin) (AnyObject) -> NSCloud { entry (%0 : $AnyObject): - %1 = unconditional_checked_cast %0 : $AnyObject to $NSCloud + %1 = unconditional_checked_cast %0 : $AnyObject to NSCloud return %1 : $NSCloud } diff --git a/test/SILOptimizer/closure-lifetime-fixup.sil b/test/SILOptimizer/closure-lifetime-fixup.sil index caf2dcc47136a..6ece4cb4e2ec7 100644 --- a/test/SILOptimizer/closure-lifetime-fixup.sil +++ b/test/SILOptimizer/closure-lifetime-fixup.sil @@ -155,3 +155,26 @@ bb1: %86 = tuple () return %86 : $() } + +sil [ossa] @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool +sil [ossa] @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + +// Don't crash. +// CHECK-LABEL: sil hidden [ossa] @testUndefined +// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] +// CHECK: mark_dependence +// CHECK: mark_dependence +// CHECK: dealloc_stack [[PA]] : $@noescape @callee_guaranteed () -> Bool +// CHECK: destroy_value +sil hidden [ossa] @testUndefined : $@convention(method) (@guaranteed Klass, @guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass): + %4 = function_ref @closureImpl : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %5 = copy_value %1 : $Klass + %6 = partial_apply [callee_guaranteed] %4(%5, undef) : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> Bool + %7 = convert_escape_to_noescape [not_guaranteed] %6 : $@callee_guaranteed () -> Bool to $@noescape @callee_guaranteed () -> Bool + %21 = function_ref @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + %22 = apply %21(%7) : $@convention(thin) (@noescape @callee_guaranteed () -> Bool) -> () + destroy_value %6 : $@callee_guaranteed () -> Bool + %42 = tuple () + return %42 : $() +} diff --git a/test/SILOptimizer/closure_lifetime_fixup_undef.swift b/test/SILOptimizer/closure_lifetime_fixup_undef.swift new file mode 100644 index 0000000000000..cf27396999f52 --- /dev/null +++ b/test/SILOptimizer/closure_lifetime_fixup_undef.swift @@ -0,0 +1,13 @@ +// RUN: not %target-swift-frontend %s -sil-verify-all -c 2>&1 | %FileCheck %s + +// Report the error but don't crash. +// CHECK: error: closure captures 'stringList' before it is declared + +class TestUndefined { + private var stringList: [String]! + + func dontCrash(strings: [String]) { + assert(stringList.allSatisfy({ $0 == stringList.first!})) + let stringList = strings.filter({ $0 == "a" }) + } +} diff --git a/test/SILOptimizer/constant_evaluable_subset_test.swift b/test/SILOptimizer/constant_evaluable_subset_test.swift index 45d808a624297..49174d0db7c16 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test.swift @@ -14,7 +14,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory @@ -253,7 +253,7 @@ internal func interpretInvalidSingedUnsignedConversions() -> UInt64 { @_semantics("constant_evaluable") internal func testIO() -> String? { return readLine() - // CHECK: note: encountered call to 'Swift.readLine(strippingNewline: Swift.Bool) -> Swift.Optional' whose body is not available + // CHECK: note: encountered call to 'readLine(strippingNewline:)' whose body is not available // CHECK: note: function whose body is not available } @@ -784,3 +784,176 @@ func testArrayAppendNonEmpty(_ x: String) -> [String] { func interpretArrayAppendNonEmpty() -> [String] { return testArrayAppendNonEmpty("mkdir") } + +struct StructContaningArray { + var array: [Int] +} + +// CHECK-LABEL: @testArrayFieldAppend +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testArrayFieldAppend(_ x: Int) -> StructContaningArray { + var s = StructContaningArray(array: []) + s.array.append(x) + return s +} + +@_semantics("test_driver") +func interpretArrayFieldAppend() -> StructContaningArray { + return testArrayFieldAppend(0) +} + +// CHECK-LABEL: @testClosureInit +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testClosureInit(_ x: Int) -> () -> Int { + return { x } +} + +@_semantics("test_driver") +func interpretClosureCreation() -> () -> Int { + return testClosureInit(19) +} + +// CHECK-LABEL: @testClosureChaining +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testClosureChaining(_ x: Int, _ y: Int) -> () -> Int { + let clo: (Int) -> Int = { $0 + x } + return { clo(y) } +} + +@_semantics("test_driver") +func interpretClosureChains() -> () -> Int { + return testClosureChaining(191, 201) +} + +// CHECK-LABEL: @testClosureWithNonConstantCaptures +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testClosureWithNonConstantCaptures(_ x: @escaping () -> Int) -> () -> Int { + return x +} + +@_semantics("test_driver") +func interpretClosureWithNonConstantCaptures(_ x: Int) -> () -> Int { + return testClosureWithNonConstantCaptures({ x }) +} + +// CHECK-LABEL: @testAutoClosure +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testAutoClosure(_ x: @escaping @autoclosure () -> Int) -> () -> Int { + return x +} + +@_semantics("test_driver") +func interpretAutoClosure(_ x: Int) -> () -> Int { + return testAutoClosure(x) +} + +// Test thin-to-thick function conversion. + +func someFunction(_ x: Int) -> Int { + return x + 1 +} + +// CHECK-LABEL: @testThinToThick +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testThinToThick() -> (Int) -> Int { + return someFunction +} + +@_semantics("test_driver") +func interpretThinToThick() -> (Int) -> Int { + return testThinToThick() +} + +// Test closures and arrays combination. + +// CHECK-LABEL: @testArrayOfClosures +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testArrayOfClosures(_ byte: @escaping () -> Int) -> [(Int) -> Int] { + var closureArray: [(Int) -> Int] = [] + // Append a simple closure. + closureArray.append({ arg in + return 0 + }) + // Append a closure that does computation. + closureArray.append({ arg in + return byte() + arg + }) + return closureArray +} + +@_semantics("test_driver") +func interpretArrayOfClosures() -> [(Int) -> Int] { + return testArrayOfClosures({ 10 }) +} + +// Test checked casts. + +// CHECK-LABEL: @testMetaTypeCast +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testMetaTypeCast(_ x: T.Type) -> Bool { + return (x is Int.Type) +} + +@_semantics("test_driver") +func interpretMetaTypeCast() -> Bool { + return testMetaTypeCast(Int.self) +} + +// FIXME: this cast is not found to be false by the classifyDynamicCast utility. +func interpretMetaTypeCast2() -> Bool { + return testMetaTypeCast(((Int) -> Int).self) +} + +// CHECK-LABEL: @testBinaryIntegerDescription +// CHECK-NOT: error: +@_semantics("constant_evaluable") +func testBinaryIntegerDescription(_ x: T) -> String { + return x.description +} + +@_semantics("test_driver") +func interpretBinaryIntegerDescription() -> String { + var str = testBinaryIntegerDescription(-10) + str += testBinaryIntegerDescription(UInt(20)) + return str +} + +// CHECK-LABEL: @testPreconditionFailure +// CHECK: error: not constant evaluable +@_semantics("constant_evaluable") +func testPreconditionFailure(_ x: Int) -> Int { + precondition(x > 0, "argument must be positive") + return x + 1 + // CHECK: note: operation traps + // Note that the message displayed depends on the assert configuration, + // therefore it is not checked here. For debug stdlib, the full message + // must be displayed. +} + +@_semantics("test_driver") +func interpretPreconditionFailure() -> Int { + return testPreconditionFailure(-10) +} + +// CHECK-LABEL: @testFatalError +// CHECK: error: not constant evaluable +@_semantics("constant_evaluable") +func testFatalError() -> Int { + fatalError("invoked an uncallable function") + return 0 + // CHECK: note: Fatal error: invoked an uncallable function + // CHECK: note: operation traps +} + +@_semantics("test_driver") +func interpretFatalError() -> Int { + return testFatalError() +} diff --git a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift index 917074e565744..07e17465aa831 100644 --- a/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift +++ b/test/SILOptimizer/constant_evaluable_subset_test_arch64.swift @@ -16,7 +16,7 @@ // especially performance inlining as it inlines functions such as String.+= // that the evaluator has special knowledge about. // -// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -mark-uninitialized-fixup -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory +// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -guaranteed-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_arch64_silgen.sil > /dev/null 2> %t/error-output-mandatory // // RUN: %FileCheck %s < %t/error-output-mandatory diff --git a/test/SILOptimizer/constant_evaluator_test.sil b/test/SILOptimizer/constant_evaluator_test.sil index 5d1ea317298e6..fb555685f89a1 100644 --- a/test/SILOptimizer/constant_evaluator_test.sil +++ b/test/SILOptimizer/constant_evaluator_test.sil @@ -464,7 +464,7 @@ bb0: // function_ref readLine(strippingNewline:) %2 = function_ref @$ss8readLine16strippingNewlineSSSgSb_tF : $@convention(thin) (Bool) -> @owned Optional %3 = apply %2(%1) : $@convention(thin) (Bool) -> @owned Optional - // CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: encountered call to 'Swift.readLine(strippingNewline: Swift.Bool) -> Swift.Optional' whose body is not available + // CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: encountered call to 'readLine(strippingNewline:)' whose body is not available release_value %3 : $Optional %5 = tuple () return %5 : $() @@ -1222,6 +1222,7 @@ sil [serialized] [_semantics "array.init.empty"] @$sS2ayxGycfC : $@convention(me // _allocateUninitializedArray(_:) sil [serialized] [always_inline] [_semantics "array.uninitialized_intrinsic"] @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) +// CHECK-LABEL: @interpretArrayInit sil @interpretArrayInit : $@convention(thin) () -> @owned Array { bb0: %0 = metatype $@thin Array.Type @@ -1232,6 +1233,7 @@ bb0: } // CHECK: Returns Array // CHECK: size: 0 contents [] +// CHECK-LABEL: @interpretEmptyArrayLiteral sil [ossa] @interpretEmptyArrayLiteral : $@convention(thin) () -> @owned Array { bb0: %0 = integer_literal $Builtin.Word, 0 @@ -1268,6 +1270,7 @@ bb0: return %9 : $Array } +// CHECK-LABEL: @interpretArrayLiteral sil [ossa] @interpretArrayLiteral : $@convention(thin) () -> @owned Array { bb0: %7 = function_ref @initializeArrayWithLiterals : $@convention(thin) () -> @owned Array @@ -1282,6 +1285,7 @@ bb0: // Array.append(_:) sil [serialized] [_semantics "array.append_element"] @$sSa6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Array<τ_0_0>) -> () +// CHECK-LABEL: @interpretArrayAppend sil [ossa] @interpretArrayAppend : $@convention(thin) () -> @owned Array { %0 = integer_literal $Builtin.Int64, 71 %1 = struct $Int64 (%0 : $Builtin.Int64) @@ -1305,6 +1309,7 @@ sil [ossa] @interpretArrayAppend : $@convention(thin) () -> @owned Array // CHECK: size: 1 // CHECK: agg: 1 elt: int: 71 +// CHECK-LABEL: @interpretArrayAppendNonEmpty sil [ossa] @interpretArrayAppendNonEmpty : $@convention(thin) () -> @owned Array { bb0: %0 = integer_literal $Builtin.Int64, 100 @@ -1331,9 +1336,45 @@ bb0: // CHECK: agg: 1 elt: int: 14 // CHECK: agg: 1 elt: int: 100 +struct StructContainingArray { + var array: [Int64] +} + +// CHECK-LABEL: @interpretArrayAppendViaStructElementAddr +sil [ossa] @interpretArrayAppendViaStructElementAddr : $@convention(thin) () -> @owned StructContainingArray { +bb0: + %3 = metatype $@thin Array.Type + // function_ref Array.init() + %4 = function_ref @$sS2ayxGycfC : $@convention(method) <τ_0_0> (@thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> + %5 = apply %4(%3) : $@convention(method) <τ_0_0> (@thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> + %6 = struct $StructContainingArray (%5 : $Array) + + %7 = alloc_stack $StructContainingArray, var, name "s" + store %6 to [init] %7 : $*StructContainingArray + %8 = struct_element_addr %7 : $*StructContainingArray, #StructContainingArray.array + + %9 = integer_literal $Builtin.Int64, 105 + %10 = struct $Int64 (%9 : $Builtin.Int64) + %11 = alloc_stack $Int64 + store %10 to [trivial] %11 : $*Int64 + + // function_ref Array.append(_:) + %13 = function_ref @$sSa6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Array<τ_0_0>) -> () + %14 = apply %13(%11, %8) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Array<τ_0_0>) -> () + dealloc_stack %11 : $*Int64 + + %18 = load [copy] %7 : $*StructContainingArray + destroy_addr %7 : $*StructContainingArray + dealloc_stack %7 : $*StructContainingArray + return %18 : $StructContainingArray +} // CHECK: agg: 1 elt: Array: + // CHECK: size: 1 + // CHECK: agg: 1 elt: int: 105 + /// Test appending of a static string to an array. The construction of a static /// string is a bit complicated due to the use of instructions like "ptrtoint". /// This tests that array append works with such complex constant values as well. +// CHECK-LABEL: @interpretArrayAppendStaticString sil @interpretArrayAppendStaticString : $@convention(thin) () -> @owned Array { %0 = string_literal utf8 "constant" // string to be appended. @@ -1365,3 +1406,128 @@ sil @interpretArrayAppendStaticString : $@convention(thin) () -> @owned Array Int32 { +bb0(%0 : $Int32): + return %0 : $Int32 +} + +// CHECK-LABEL: @interpretPartialApply +sil @interpretPartialApply: $@convention(thin) () -> @owned @callee_guaranteed () -> Int32 { +bb0: + %0 = integer_literal $Builtin.Int32, 81 + %1 = struct $Int32 (%0 : $Builtin.Int32) + %2 = function_ref @closure1 : $@convention(thin) (Int32) -> Int32 + %3 = partial_apply [callee_guaranteed] %2(%1) : $@convention(thin) (Int32) -> Int32 + return %3 : $@callee_guaranteed () -> Int32 +} // CHECK: Returns closure: target: closure1 captures + // CHECK: %1 + // CHECK: values: + // CHECK: int: 81 + +sil private @closure2 : $@convention(thin) (Int32, Int32) -> Int32 { +bb0(%0 : $Int32, %1: $Int32): + return %0 : $Int32 +} + +sil private @closure3 : $@convention(thin) (@guaranteed @callee_guaranteed (Int32) -> Int32) -> Int32 { +bb0(%0 : $@callee_guaranteed (Int32) -> Int32): + %3 = integer_literal $Builtin.Int32, 19 + %4 = struct $Int32 (%3 : $Builtin.Int32) + %5 = apply %0(%4) : $@callee_guaranteed (Int32) -> Int32 + return %5 : $Int32 +} + +// CHECK-LABEL: @interpretPartialApplyChain +sil @interpretPartialApplyChain: $@convention(thin) () -> @owned @callee_guaranteed () -> Int32 { +bb0: + %0 = integer_literal $Builtin.Int32, 991 + %1 = struct $Int32 (%0 : $Builtin.Int32) + %2 = function_ref @closure2 : $@convention(thin) (Int32, Int32) -> Int32 + %3 = partial_apply [callee_guaranteed] %2(%1) : $@convention(thin) (Int32, Int32) -> Int32 + %5 = function_ref @closure3 : $@convention(thin) (@guaranteed @callee_guaranteed (Int32) -> Int32) -> Int32 + strong_retain %3 : $@callee_guaranteed (Int32) -> Int32 + %7 = partial_apply [callee_guaranteed] %5(%3) : $@convention(thin) (@guaranteed @callee_guaranteed (Int32) -> Int32) -> Int32 + strong_release %3 : $@callee_guaranteed (Int32) -> Int32 + return %7 : $@callee_guaranteed () -> Int32 +} // CHECK: Returns closure: target: closure3 captures + // CHECK: %3 + // CHECK: values: + // CHECK: closure: target: closure2 captures + // CHECK: %1 + // CHECK: values: + // CHECK: int: 991 + + +sil private @closure4 : $@convention(thin) () -> Int64 { +bb0: + %0 = integer_literal $Builtin.Int64, 71 + %1 = struct $Int64 (%0 : $Builtin.Int64) + return %1 : $Int64 +} + +// CHECK-LABEL: @interpretThinToThickFunction +sil @interpretThinToThickFunction: $@convention(thin) () -> @owned @callee_guaranteed () -> Int64 { +bb0: + %0 = function_ref @closure4 : $@convention(thin) () -> Int64 + %3 = thin_to_thick_function %0 : $@convention(thin) () -> Int64 to $@callee_guaranteed () -> Int64 + return %3 : $@callee_guaranteed () -> Int64 +} // CHECK: Returns closure: target: closure4 captures + // CHECK-NEXT: values: + +// Tests for checked cast instruction. + +sil [ossa] @testMetatypeCast : $@convention(thin) (@thick T.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type): + checked_cast_br %0 : $@thick T.Type to Int64.Type, bb1, bb2 + +bb1(%3 : $@thick Int64.Type): + %4 = metatype $@thin Int64.Type + %5 = integer_literal $Builtin.Int1, -1 + br bb3(%5 : $Builtin.Int1) + +bb2(%7 : $@thick T.Type): + %8 = integer_literal $Builtin.Int1, 0 + br bb3(%8 : $Builtin.Int1) + +bb3(%10 : $Builtin.Int1): + return %10 : $Builtin.Int1 +} + +// CHECK-LABEL: @interpretMetatypeCast +sil [ossa] @interpretMetatypeCast : $@convention(thin) () -> Builtin.Int1 { +bb0: + %1 = metatype $@thick Int64.Type + %2 = function_ref @testMetatypeCast : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Builtin.Int1 + %3 = apply %2(%1) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Builtin.Int1 + return %3 : $Builtin.Int1 +} // CHECK: Returns int: -1 + +// CHECK-LABEL: @interpretBinaryIntegerDescription +sil [ossa] @interpretBinaryIntegerDescription : $@convention(thin) () -> @owned String { +bb0: + %0 = integer_literal $Builtin.Int64, -10 + %1 = struct $Int64 (%0 : $Builtin.Int64) + %2 = alloc_stack $Int64 + store %1 to [trivial] %2 : $*Int64 + %4 = function_ref @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String + %5 = apply %4(%2) : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String + dealloc_stack %2 : $*Int64 + return %5 : $String +} // CHECK: Returns string: "-10" + +sil [_semantics "binaryInteger.description"] @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String + +// CHECK-LABEL: @interpretUnsignedBinaryIntegerDescription +sil [ossa] @interpretUnsignedBinaryIntegerDescription : $@convention(thin) () -> @owned String { +bb0: + %0 = integer_literal $Builtin.Int64, 0xffffffffffffffff + %1 = struct $UInt64 (%0 : $Builtin.Int64) + %2 = alloc_stack $UInt64 + store %1 to [trivial] %2 : $*UInt64 + %4 = function_ref @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String + %5 = apply %4(%2) : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String + dealloc_stack %2 : $*UInt64 + return %5 : $String +} // CHECK: Returns string: "18446744073709551615" diff --git a/test/SILOptimizer/constant_propagation.sil b/test/SILOptimizer/constant_propagation.sil index e124a625ddddc..25c72bcb95378 100644 --- a/test/SILOptimizer/constant_propagation.sil +++ b/test/SILOptimizer/constant_propagation.sil @@ -1046,3 +1046,28 @@ bb0(%0 : $@thin T.Type): %1 = builtin "isConcrete"(%0 : $@thin T.Type) : $Builtin.Int1 return %1 : $Builtin.Int1 } + +class AClass {} +class BClass : AClass {} + +// CHECK-LABEL: sil @test_checked_cast_br_thick_class_type +// CHECK: [[METATYPE:%.*]] = metatype $@thick BClass.Type +// CHECK: [[SUPER:%.*]] = upcast [[METATYPE]] : $@thick BClass.Type to $@thick AClass.Type +// CHECK: br bb1([[SUPER]] : $@thick AClass.Type) +sil @test_checked_cast_br_thick_class_type : $@convention(thin) (@thin P.Protocol) -> Builtin.Int1 { +bb0(%0 : $@thin P.Protocol): + %2 = metatype $@thick BClass.Type + checked_cast_br %2 : $@thick BClass.Type to AClass.Type, bb1, bb2 + +bb1(%5 : $@thick AClass.Type): + debug_value %5 : $@thick AClass.Type, let, name "type" + %6 = integer_literal $Builtin.Int1, -1 + br bb3(%6 : $Builtin.Int1) + +bb2: + %9 = integer_literal $Builtin.Int1, 0 + br bb3(%9 : $Builtin.Int1) + +bb3(%11 : $Builtin.Int1): + return %11 : $Builtin.Int1 +} diff --git a/test/SILOptimizer/constant_propagation_castopt_analysis_invalidation.sil b/test/SILOptimizer/constant_propagation_castopt_analysis_invalidation.sil index 0e5489e3950dc..837e7506b24bf 100644 --- a/test/SILOptimizer/constant_propagation_castopt_analysis_invalidation.sil +++ b/test/SILOptimizer/constant_propagation_castopt_analysis_invalidation.sil @@ -136,7 +136,7 @@ bb0: debug_value %0 : $Foo2 // id: %2 strong_retain %0 : $Foo2 // id: %3 %4 = upcast %0 : $Foo2 to $Foo // users: %5, %6, %17, %23 - checked_cast_br %0 : $Foo2 to $Foo2, bb1, bb6 // id: %6 + checked_cast_br %0 : $Foo2 to Foo2, bb1, bb6 // id: %6 bb1(%7 : $Foo2): // Preds: bb0 %8 = enum $Optional, #Optional.some!enumelt.1, %7 : $Foo2 // user: %9 @@ -179,7 +179,7 @@ sil private [always_inline] @_TF4mainP33_9ACC0692747077F216D14C36CD9276715speakF bb0(%0 : $Foo): debug_value %0 : $Foo // id: %1 strong_retain %0 : $Foo // id: %2 - checked_cast_br %0 : $Foo to $Foo2, bb1, bb2 // id: %3 + checked_cast_br %0 : $Foo to Foo2, bb1, bb2 // id: %3 bb1(%4 : $Foo2): // Preds: bb0 %5 = enum $Optional, #Optional.some!enumelt.1, %4 : $Foo2 // user: %6 diff --git a/test/SILOptimizer/constant_propagation_diagnostics.swift b/test/SILOptimizer/constant_propagation_diagnostics.swift index da8eb8c4161f2..de433d7a5a44c 100644 --- a/test/SILOptimizer/constant_propagation_diagnostics.swift +++ b/test/SILOptimizer/constant_propagation_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -sdk %S/../SILGen/Inputs %s -o /dev/null -verify +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -emit-sil -sdk %S/../SILGen/Inputs %s -o /dev/null -verify // enum with raw values that are too big are not diagnosed enum EnumWithTooLargeElements : UInt8 { diff --git a/test/SILOptimizer/constant_propagation_objc.sil b/test/SILOptimizer/constant_propagation_objc.sil index 9eab992c7a718..a27f8ef39d391 100644 --- a/test/SILOptimizer/constant_propagation_objc.sil +++ b/test/SILOptimizer/constant_propagation_objc.sil @@ -236,7 +236,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: strong_release [[LOADED_INPUT]] @@ -303,7 +303,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -372,7 +372,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -442,7 +442,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: strong_release [[LOADED_INPUT]] @@ -509,7 +509,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -578,7 +578,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -648,7 +648,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: strong_release [[LOADED_INPUT]] @@ -715,7 +715,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -784,7 +784,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -854,7 +854,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: strong_release [[LOADED_INPUT]] @@ -921,7 +921,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] @@ -990,7 +990,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]: // CHECK-NEXT: br [[FAIL_EXIT_TRAMPOLINE:bb[0-9]+]] diff --git a/test/SILOptimizer/constant_propagation_ownership.sil b/test/SILOptimizer/constant_propagation_ownership.sil index bf3f2638e1c42..e3c180aec8d22 100644 --- a/test/SILOptimizer/constant_propagation_ownership.sil +++ b/test/SILOptimizer/constant_propagation_ownership.sil @@ -29,6 +29,11 @@ class Klass {} sil @klass_allocator : $@convention(method) (@thick Klass.Type) -> @owned Klass sil @generic_user : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +struct NativeObjectOptNativeObjectPair { + var lhs: Builtin.NativeObject + var rhs: Optional +} + /////////// // Tests // /////////// @@ -1107,7 +1112,7 @@ bb0(%0 : $*Error, %1 : $*E1): sil [ossa] @always_fail_protocolmeta_to_concretemeta_checkedcastbr : $@convention(thin) (@thin P.Protocol) -> Builtin.Int1 { bb0(%0 : $@thin P.Protocol): %2 = metatype $@thick P.Protocol - checked_cast_br %2 : $@thick P.Protocol to $@thick X.Type, bb1, bb2 + checked_cast_br %2 : $@thick P.Protocol to X.Type, bb1, bb2 bb1(%4 : $@thick X.Type): %5 = metatype $@thin X.Type @@ -1129,7 +1134,7 @@ bb3(%11 : $Builtin.Int1): // CHECK: } // end sil function 'always_succeed_subtoparent' sil [ossa] @always_succeed_subtoparent : $@convention(thin) (@owned AnSubObject) -> Builtin.Int1 { bb0(%0 : @owned $AnSubObject): - checked_cast_br %0 : $AnSubObject to $AnObject, bb1, bb2 + checked_cast_br %0 : $AnSubObject to AnObject, bb1, bb2 bb1(%4 : @owned $AnObject): %5 = metatype $@thin X.Type @@ -1181,3 +1186,39 @@ bb3: %9999 = tuple() return %9999 : $() } + +// CHECK-LABEL: sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK: ({{%.*}}, [[DESTRUCTURE_RESULT_2:%.*]]) = destructure_tuple +// CHECK: store [[DESTRUCTURE_RESULT_2]] to [init] +// CHECK: } // end sil function 'do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple' +sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_tuple : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %0 = alloc_stack $Optional + %1 = enum $Optional, #Optional.none!enumelt + %2 = tuple(%arg : $Builtin.NativeObject, %1 : $Optional) + (%3, %4) = destructure_tuple %2 : $(Builtin.NativeObject, Optional) + store %4 to [init] %0 : $*Optional + destroy_addr %0 : $*Optional + dealloc_stack %0 : $*Optional + destroy_value %3 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK: ({{%.*}}, [[DESTRUCTURE_RESULT_2:%.*]]) = destructure_struct +// CHECK: store [[DESTRUCTURE_RESULT_2]] to [init] +// CHECK: } // end sil function 'do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct' +sil [ossa] @do_not_RAUW_owned_destructure_with_nontrivial_none_value_struct : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %0 = alloc_stack $Optional + %1 = enum $Optional, #Optional.none!enumelt + %2 = struct $NativeObjectOptNativeObjectPair (%arg : $Builtin.NativeObject, %1 : $Optional) + (%3, %4) = destructure_struct %2 : $NativeObjectOptNativeObjectPair + store %4 to [init] %0 : $*Optional + destroy_addr %0 : $*Optional + dealloc_stack %0 : $*Optional + destroy_value %3 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/constant_propagation_ownership_objc.sil b/test/SILOptimizer/constant_propagation_ownership_objc.sil index 7deccf73e51ad..eac9a05884c9b 100644 --- a/test/SILOptimizer/constant_propagation_ownership_objc.sil +++ b/test/SILOptimizer/constant_propagation_ownership_objc.sil @@ -143,7 +143,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: destroy_value [[DEFAULT_ARG]] @@ -210,7 +210,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -282,7 +282,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSString, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -353,7 +353,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: destroy_value [[DEFAULT_ARG]] @@ -420,7 +420,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -492,7 +492,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSArray, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -563,7 +563,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: destroy_value [[DEFAULT_ARG]] @@ -630,7 +630,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -702,7 +702,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSDictionary, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -773,7 +773,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: destroy_value [[DEFAULT_ARG]] @@ -840,7 +840,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] @@ -912,7 +912,7 @@ bb4: // // CHECK: bb1: // CHECK: [[LOADED_INPUT:%.*]] = load [take] [[INPUT]] -// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to $NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[LOADED_INPUT]] : $NSObject to NSSet, [[YES_BB:bb[0-9]+]], [[NO_BB:bb[0-9]+]] // // CHECK: [[NO_BB]]([[DEFAULT_ARG:%.*]] : // CHECK-NEXT: store [[DEFAULT_ARG]] to [init] [[INPUT]] diff --git a/test/SILOptimizer/constantprop-wrongscope.swift b/test/SILOptimizer/constantprop-wrongscope.swift index 12f9d62ba7709..92961fc96625e 100644 --- a/test/SILOptimizer/constantprop-wrongscope.swift +++ b/test/SILOptimizer/constantprop-wrongscope.swift @@ -13,7 +13,7 @@ // instructions surrounding it. // CHECK: destroy_addr %7 : $*Any, loc {{.*}}:22:19, scope 2 -// CHECK: dealloc_stack %12 : $*Optional, loc {{.*}}:22:23, scope 2 +// CHECK: dealloc_stack %13 : $*Optional, loc {{.*}}:22:23, scope 2 // CHECK: dealloc_stack %7 : $*Any, loc {{.*}}:22:23, scope 2 // CHECK: dealloc_stack %6 : $*A, loc {{.*}}:22:7, scope 2 diff --git a/test/SILOptimizer/cross-module-optimization-objc.swift b/test/SILOptimizer/cross-module-optimization-objc.swift new file mode 100644 index 0000000000000..76465746f2f14 --- /dev/null +++ b/test/SILOptimizer/cross-module-optimization-objc.swift @@ -0,0 +1,32 @@ +// First test: functional correctness + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t %S/Inputs/cross-module-objc.swift -c -o %t/test.o +// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -c -o %t/main.o +// RUN: %target-swiftc_driver %t/main.o %t/test.o -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT + +// Check if it also works if the main module is compiled with -Onone: + +// RUN: %target-build-swift -Onone -wmo -module-name=Main -I%t %s -c -o %t/main-onone.o +// RUN: %target-swiftc_driver %t/main-onone.o %t/test.o -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// Second test: check if CMO really imports the SIL of functions in other modules. + +// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil | %FileCheck %s -check-prefix=CHECK-SIL + + +import Test + +func testClass() { + // CHECK-OUTPUT: 127 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test21returnObjcClassMemberySiAA0cD0C_xtlFSi_Tg5 + print(callObjcClassMember(0)) +} + +testClass() + diff --git a/test/SILOptimizer/cross-module-optimization.swift b/test/SILOptimizer/cross-module-optimization.swift new file mode 100644 index 0000000000000..29d8fdd6039e8 --- /dev/null +++ b/test/SILOptimizer/cross-module-optimization.swift @@ -0,0 +1,130 @@ +// First test: functional correctness + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule %S/Inputs/cross-submodule.swift -c -o %t/submodule.o +// RUN: %target-build-swift -O -wmo -parse-as-library -cross-module-optimization -emit-module -emit-module-path=%t/Test.swiftmodule -module-name=Test -I%t %S/Inputs/cross-module.swift -c -o %t/test.o +// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -c -o %t/main.o +// RUN: %target-swiftc_driver %t/main.o %t/test.o %t/submodule.o -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT + +// Check if it also works if the main module is compiled with -Onone: + +// RUN: %target-build-swift -Onone -wmo -module-name=Main -I%t %s -c -o %t/main-onone.o +// RUN: %target-swiftc_driver %t/main-onone.o %t/test.o %t/submodule.o -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT + +// REQUIRES: executable_test + +// Second test: check if CMO really imports the SIL of functions in other modules. + +// RUN: %target-build-swift -O -wmo -module-name=Main -I%t %s -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil | %FileCheck %s -check-prefix=CHECK-SIL + +import Test + + +func testNestedTypes() { + let c = Container() + + // CHECK-OUTPUT: [Test.Container.Base] + // CHECK-OUTPUT: 27 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test9ContainerV9testclassyxxlFSi_Tg5 + print(c.testclass(27)) + // CHECK-OUTPUT: [Test.Container.Base] + // CHECK-OUTPUT: 27 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test9ContainerV13testclass_genyxxlF + print(c.testclass_gen(27)) + // CHECK-OUTPUT: [Test.PE.B(27)] + // CHECK-OUTPUT: 27 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test9ContainerV8testenumyxxlFSi_Tg5 + print(c.testenum(27)) + // CHECK-OUTPUT: [Test.PE.B(27)] + // CHECK-OUTPUT: 27 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test9ContainerV12testenum_genyxxlF + print(c.testenum_gen(27)) +} + + +func testClass() { + // CHECK-OUTPUT: 28 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test11createClassySixlFSi_Tg5 + // CHECK-SIL-DAG: sil shared [noinline] @${{.*Test.*getClass}} + print(createClass(0)) + // CHECK-OUTPUT: 28 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test15createClass_genySixlF + print(createClass_gen(0)) +} + +func testError() { + // CHECK-OUTPUT: PrivateError() + // CHECK-SIL-DAG: sil @$s4Test12PrivateError33_{{.*}} : $@convention(method) (@thin PrivateError.Type) -> PrivateError{{$}} + print(returnPrivateError(27)) + // CHECK-OUTPUT: InternalError() + // CHECK-SIL-DAG: sil @$s4Test13InternalErrorVACycfC : $@convention(method) (@thin InternalError.Type) -> InternalError{{$}} + print(returnInternalError(27)) +} + +class DerivedFromOpen : OpenClass { } + +func testProtocolsAndClasses() { + // CHECK-OUTPUT: false + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test20checkIfClassConformsyyxlFSi_Tg5 + checkIfClassConforms(27) + // CHECK-OUTPUT: false + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test24checkIfClassConforms_genyyxlF + checkIfClassConforms_gen(27) + // CHECK-OUTPUT: 123 + // CHECK-OUTPUT: 1234 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test7callFooyyxlFSi_Tg5 + // CHECK-SIL-DAG: sil [{{.*}}] @$s4Test19printFooExistential33_{{.*}} : $@convention(thin) (@in_guaranteed PrivateProtocol) -> (){{$}} + callFoo(27) + // CHECK-OUTPUT: 123 + // CHECK-OUTPUT: 1234 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test11callFoo_genyyxlF + callFoo_gen(27) + // CHECK-OUTPUT: 55 + callClassMethod(55) + // CHECK-OUTPUT: 321 + callFooViaConformance(0) +} + +func testSubModule() { + // CHECK-OUTPUT: 10 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test24callGenericSubmoduleFuncyyxlFSi_Tg5 + // CHECK-SIL-DAG: sil shared [noinline] @$s9Submodule07genericA4FuncyyxlF + callGenericSubmoduleFunc(10) + // CHECK-OUTPUT: 101 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test28callGenericSubmoduleFunc_genyyxlF + callGenericSubmoduleFunc_gen(101) +} + +func testClosures() { + // CHECK-OUTPUT: 23 + // CHECK-SIL-DAG: sil shared [noinline] @$s4Test14genericClosureyxxlFSi_Tg5 + print(genericClosure(23)) + // CHECK-OUTPUT: 24 + // CHECK-SIL-DAG: sil shared_external {{.*}} @$s4Test18genericClosure_genyxxlF + print(genericClosure_gen(24)) +} + +func testKeypath() { + // CHECK-OUTPUT: 27 + print(useStructKeypath(0)) + // CHECK-OUTPUT: 29 + print(useClassKeypath(0)) +} + +func testMisc() { + // CHECK-OUTPUT: 43 + // CHECK-OUTPUT: 42 + // CHECK-SIL-DAG: sil shared {{.*}} @$s4Test13callUnrelatedyxxlFSi_Tg5 + print(callUnrelated(42)) +} + +testNestedTypes() +testClass() +testError() +testProtocolsAndClasses() +testSubModule() +testClosures() +testKeypath() +testMisc() diff --git a/test/SILOptimizer/dead_alloc_elim.sil b/test/SILOptimizer/dead_alloc_elim.sil index 097596898827a..025e82146532e 100644 --- a/test/SILOptimizer/dead_alloc_elim.sil +++ b/test/SILOptimizer/dead_alloc_elim.sil @@ -354,7 +354,7 @@ sil @trivial_destructor_on_stack : $@convention(thin) () -> () { // CHECK: alloc_ref sil @trivial_destructor_may_trap : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor - %1 = unconditional_checked_cast %0 : $TrivialDestructor to $AnyObject + %1 = unconditional_checked_cast %0 : $TrivialDestructor to AnyObject strong_release %0 : $TrivialDestructor %4 = tuple() return %4 : $() diff --git a/test/SILOptimizer/dead_store_elim.sil b/test/SILOptimizer/dead_store_elim.sil index f4c5af6ba006e..feceaaa8521d4 100644 --- a/test/SILOptimizer/dead_store_elim.sil +++ b/test/SILOptimizer/dead_store_elim.sil @@ -291,6 +291,29 @@ bb3: return %9999 : $() } +// CHECK-LABEL: sil @handle_unreachable : $@convention(thin) (@inout Builtin.Int32) -> () { +// CHECK: bb0 +// CHECK-NEXT: integer_literal +// CHECK-NEXT: cond_br +// CHECK: return +sil @handle_unreachable : $@convention(thin) (@inout Builtin.Int32) -> () { +bb0(%0 : $*Builtin.Int32): + %1 = integer_literal $Builtin.Int32, 0 + store %1 to %0 : $*Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + unreachable + +bb2: + br bb3 + +bb3: + store %1 to %0 : $*Builtin.Int32 + %9999 = tuple() + return %9999 : $() +} + // CHECK-LABEL: sil @post_dominating_dead_store_partial : $@convention(thin) (@inout Builtin.Int32) -> () { // CHECK: bb0( // CHECK-NOT: {{ store}} @@ -446,6 +469,24 @@ bb3: return %9999 : $() } +sil @unknown : $@convention(thin) () -> () + +// CHECK-LABEL: sil @inout_is_not_aliasing : $@convention(thin) (@inout Builtin.Int32) -> () { +// CHECK: bb0 +// CHECK-NEXT: integer_literal +// CHECK-NEXT: function_ref +// CHECK: return +sil @inout_is_not_aliasing : $@convention(thin) (@inout Builtin.Int32) -> () { +bb0(%0 : $*Builtin.Int32): + %1 = integer_literal $Builtin.Int32, 0 + store %1 to %0 : $*Builtin.Int32 + %f = function_ref @unknown : $@convention(thin) () -> () + %3 = apply %f() : $@convention(thin) () -> () + store %1 to %0 : $*Builtin.Int32 + %9999 = tuple() + return %9999 : $() +} + // We should be able to remove the store in bb0, but we currently // can't due to deficiency in alias analysis. // diff --git a/test/SILOptimizer/definite_init_address_only_let.swift b/test/SILOptimizer/definite_init_address_only_let.swift index 6db080c945b6f..fe8e620ee57fb 100644 --- a/test/SILOptimizer/definite_init_address_only_let.swift +++ b/test/SILOptimizer/definite_init_address_only_let.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -enable-ownership-stripping-after-serialization -verify %s func foo(a: Bool, t: T) { let x: T diff --git a/test/SILOptimizer/definite_init_cross_module.swift b/test/SILOptimizer/definite_init_cross_module.swift index 73fbfa490db1a..855fe3b336bde 100644 --- a/test/SILOptimizer/definite_init_cross_module.swift +++ b/test/SILOptimizer/definite_init_cross_module.swift @@ -2,6 +2,10 @@ // RUN: %target-swift-frontend -emit-module -emit-module-path=%t/OtherModule.swiftmodule %S/Inputs/definite_init_cross_module/OtherModule.swift // RUN: %target-swift-frontend -emit-sil -verify -I %t -swift-version 5 %s > /dev/null -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/definite_init_cross_module/BridgingHeader.h +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path=%t/OtherModule.swiftmodule %S/Inputs/definite_init_cross_module/OtherModule.swift -enable-ownership-stripping-after-serialization +// RUN: %target-swift-frontend -emit-sil -verify -I %t -swift-version 5 %s > /dev/null -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/definite_init_cross_module/BridgingHeader.h -enable-ownership-stripping-after-serialization + import OtherModule extension Point { diff --git a/test/SILOptimizer/definite_init_cross_module_swift4.swift b/test/SILOptimizer/definite_init_cross_module_swift4.swift index 935499d6d6fd7..277e8df8cb616 100644 --- a/test/SILOptimizer/definite_init_cross_module_swift4.swift +++ b/test/SILOptimizer/definite_init_cross_module_swift4.swift @@ -2,6 +2,10 @@ // RUN: %target-swift-frontend -emit-module -emit-module-path=%t/OtherModule.swiftmodule %S/Inputs/definite_init_cross_module/OtherModule.swift // RUN: %target-swift-frontend -emit-sil -verify -verify-ignore-unknown -I %t -swift-version 4 %s > /dev/null -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/definite_init_cross_module/BridgingHeader.h +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -emit-module -emit-module-path=%t/OtherModule.swiftmodule %S/Inputs/definite_init_cross_module/OtherModule.swift +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -emit-sil -verify -verify-ignore-unknown -I %t -swift-version 4 %s > /dev/null -enable-objc-interop -disable-objc-attr-requires-foundation-module -import-objc-header %S/Inputs/definite_init_cross_module/BridgingHeader.h + import OtherModule extension Point { diff --git a/test/SILOptimizer/definite_init_diagnostics.swift b/test/SILOptimizer/definite_init_diagnostics.swift index 40e99269e6271..a563afdd18a74 100644 --- a/test/SILOptimizer/definite_init_diagnostics.swift +++ b/test/SILOptimizer/definite_init_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -enable-ownership-stripping-after-serialization -primary-file %s -o /dev/null -verify import Swift diff --git a/test/SILOptimizer/definite_init_diagnostics_globals.swift b/test/SILOptimizer/definite_init_diagnostics_globals.swift index 46861e10338d2..e020b2d1cde4a 100644 --- a/test/SILOptimizer/definite_init_diagnostics_globals.swift +++ b/test/SILOptimizer/definite_init_diagnostics_globals.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -emit-sil -primary-file %s -o /dev/null -verify import Swift diff --git a/test/SILOptimizer/definite_init_diagnostics_objc.swift b/test/SILOptimizer/definite_init_diagnostics_objc.swift index d7e761dae2ecb..b1ecf92999b6e 100644 --- a/test/SILOptimizer/definite_init_diagnostics_objc.swift +++ b/test/SILOptimizer/definite_init_diagnostics_objc.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -sdk %S/../SILGen/Inputs %s -I %S/../SILGen/Inputs -enable-source-import -parse-stdlib -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -sdk %S/../SILGen/Inputs %s -I %S/../SILGen/Inputs -enable-source-import -parse-stdlib -o /dev/null -verify -enable-ownership-stripping-after-serialization // REQUIRES: objc_interop import Swift diff --git a/test/SILOptimizer/definite_init_existential_let.swift b/test/SILOptimizer/definite_init_existential_let.swift index cc6e09eb8bd8f..4d274134ba1dd 100644 --- a/test/SILOptimizer/definite_init_existential_let.swift +++ b/test/SILOptimizer/definite_init_existential_let.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -verify %s -enable-ownership-stripping-after-serialization // rdar://problem/29716016 - Check that we properly enforce DI on `let` // variables and class properties. diff --git a/test/SILOptimizer/definite_init_extension.swift b/test/SILOptimizer/definite_init_extension.swift index 248ad8fd8e68b..b3ad064a972c6 100644 --- a/test/SILOptimizer/definite_init_extension.swift +++ b/test/SILOptimizer/definite_init_extension.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -verify %s -o /dev/null +// RUN: %target-swift-frontend -emit-sil -verify %s -o /dev/null -enable-ownership-stripping-after-serialization struct S { let t: T // expected-note {{'self.t.1' not initialized}} diff --git a/test/SILOptimizer/definite_init_failable_initializers.swift b/test/SILOptimizer/definite_init_failable_initializers.swift index 96bf16b5ba44f..c444af21b5469 100644 --- a/test/SILOptimizer/definite_init_failable_initializers.swift +++ b/test/SILOptimizer/definite_init_failable_initializers.swift @@ -133,8 +133,8 @@ struct FailableStruct { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -311,8 +311,8 @@ struct ThrowStruct { // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -333,8 +333,8 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV4failACyt_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowStruct): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -359,8 +359,10 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV20failBeforeDelegationACSi_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]]([[RESULT]], %1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowStruct): +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: dealloc_stack [[SELF_BOX]] +// CHECK-NEXT: destroy_addr [[SELF_BOX]] +// CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) @@ -379,8 +381,8 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV4failACyt_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowStruct): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -396,11 +398,12 @@ struct ThrowStruct { // CHECK: [[SELF_BOX:%.*]] = alloc_stack $ThrowStruct // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -424,11 +427,12 @@ struct ThrowStruct { // CHECK: bb1([[NEW_SELF:.*]] : $ThrowStruct): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -436,6 +440,7 @@ struct ThrowStruct { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] @@ -467,11 +472,12 @@ struct ThrowStruct { // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] -// CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -479,6 +485,7 @@ struct ThrowStruct { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[COND:%.*]] = load [[BITMAP_BOX]] @@ -520,8 +527,8 @@ struct ThrowStruct { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: retain_value [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -530,8 +537,8 @@ struct ThrowStruct { // CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional): // CHECK-NEXT: return [[NEW_SELF]] : $Optional // -// CHECK: [[TRY_APPLY_FAIL_BB]]([[ERROR]] : $Error): -// CHECK-NEXT: strong_release [[ERROR:%.*]] : $Error +// CHECK: [[TRY_APPLY_FAIL_BB]]([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[ERROR]] : $Error // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: br [[TRY_APPLY_CONT]]([[NEW_SELF]] : $Optional) init?(throwsToOptional: Int) { @@ -567,9 +574,9 @@ struct ThrowStruct { // CHECK-NEXT: try_apply [[INIT_FN]]([[SELF_TYPE]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowStruct): // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [static] [[SELF_BOX]] : $*ThrowStruct +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*ThrowStruct -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -587,16 +594,17 @@ struct ThrowStruct { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers11ThrowStructV6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[SELF_TYPE]]) // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [static] [[SELF_BOX]] : $*ThrowStruct +// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*ThrowStruct // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: release_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -773,8 +781,8 @@ class FailableBaseClass { // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -822,25 +830,30 @@ class FailableDerivedClass : FailableBaseClass { // CHECK: bb0(%0 : $FailableDerivedClass): // CHECK: [[SELF_BOX:%.*]] = alloc_stack $FailableDerivedClass // CHECK: store %0 to [[SELF_BOX]] +// CHECK-NEXT: [[RELOAD_FROM_SELF_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick FailableDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %0 : $FailableDerivedClass, [[METATYPE]] : $@thick FailableDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_FROM_SELF_BOX]] : $FailableDerivedClass, [[METATYPE]] : $@thick FailableDerivedClass.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: return [[RESULT]] +// CHECK: } // end sil function '$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailBeforeDelegationACSgyt_tcfc' init?(derivedFailBeforeDelegation: ()) { return nil } // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailDuringDelegationACSgyt_tcfc -// CHECK: bb0(%0 : $FailableDerivedClass): +// CHECK: bb0([[SELF:%.*]] : $FailableDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $FailableDerivedClass -// CHECK: store %0 to [[SELF_BOX]] -// CHECK: [[CANARY:%.*]] = apply -// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr %0 +// CHECK: store [[SELF]] to [[SELF_BOX]] +// CHECK: [[CANARY_FUN:%.*]] = function_ref @$s35definite_init_failable_initializers6CanaryCACycfC : +// CHECK: [[CANARY:%.*]] = apply [[CANARY_FUN]]( +// CHECK-NEXT: [[MEMBER_ADDR:%.*]] = ref_element_addr [[SELF]] // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[MEMBER_ADDR]] : $*Canary // CHECK-NEXT: store [[CANARY]] to [[WRITE]] // CHECK-NEXT: end_access [[WRITE]] : $*Canary -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %0 +// CHECK-NEXT: strong_release [[SELF]] +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17FailableBaseClassC28failBeforeFullInitializationACSgyt_tcfc // CHECK-NEXT: [[SELF_OPTIONAL:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: [[COND:%.*]] = select_enum [[SELF_OPTIONAL]] @@ -855,8 +868,8 @@ class FailableDerivedClass : FailableBaseClass { // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[BASE_SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_ref_cast [[BASE_SELF_VALUE]] -// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[SELF_VALUE]] +// CHECK-NEXT: store [[SELF_VALUE]] to [[SELF_BOX]] // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -864,6 +877,7 @@ class FailableDerivedClass : FailableBaseClass { // // CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional): // CHECK-NEXT: return [[NEW_SELF]] : $Optional +// CHECK: } // end sil function '$s35definite_init_failable_initializers20FailableDerivedClassC27derivedFailDuringDelegationACSgyt_tcfc' init?(derivedFailDuringDelegation: ()) { self.otherMember = Canary() super.init(failBeforeFullInitialization: ()) @@ -905,16 +919,17 @@ class ThrowBaseClass { class ThrowDerivedClass : ThrowBaseClass { // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfc -// CHECK: bb0(%0 : $ThrowDerivedClass): +// CHECK: bb0([[SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass -// CHECK: store %0 to [[SELF_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %0 +// CHECK: store [[SELF]] to [[SELF_BOX]] +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] @@ -930,24 +945,26 @@ class ThrowDerivedClass : ThrowBaseClass { } // CHECK-LABEL: sil hidden @$s35definite_init_failable_initializers17ThrowDerivedClassC28failBeforeFullInitializationACSi_tKcfc -// CHECK: bb0(%0 : $Int, %1 : $ThrowDerivedClass): +// CHECK: bb0(%0 : $Int, [[SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass -// CHECK: store %1 to [[SELF_BOX]] +// CHECK: store [[SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %1 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] : $ThrowDerivedClass // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick ThrowDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %1 : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] init(failBeforeFullInitialization: Int) throws { @@ -965,15 +982,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int) -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK: try_apply [[INIT_FN]]([[BASE_SELF]]) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] -// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[DERIVED_SELF]] +// CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -988,8 +1006,9 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb6: // CHECK-NEXT: br bb8 // CHECK: bb7: +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick ThrowDerivedClass.Type -// CHECK-NEXT: dealloc_partial_ref %2 : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type +// CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $ThrowDerivedClass, [[METATYPE]] : $@thick ThrowDerivedClass.Type // CHECK-NEXT: br bb8 // CHECK: bb8: // CHECK-NEXT: dealloc_stack [[SELF_BOX]] @@ -1004,19 +1023,21 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb0(%0 : $Int, %1 : $ThrowDerivedClass): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass // CHECK: store %1 to [[SELF_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %1 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[DERIVED_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1032,18 +1053,19 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0 // CHECK-NEXT: store [[ZERO]] to [[BITMAP_BOX]] // CHECK: store %2 to [[SELF_BOX]] -// CHECK-NEXT: [[DERIVED_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[DERIVED_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK: try_apply [[INIT_FN]]([[DERIVED_SELF]]) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowBaseClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1051,6 +1073,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1084,17 +1107,18 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[RESULT:%.*]] : $Int): // CHECK-NEXT: [[TWO:%.*]] = integer_literal $Builtin.Int2, -2 // CHECK-NEXT: store [[TWO]] to [[BITMAP_BOX]] -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %2 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassC6noFailACyt_tcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] // CHECK: [[NEW_SELF:%.*]] = apply [[INIT_FN]]([[BASE_SELF]]) // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%1) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1102,6 +1126,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1145,7 +1170,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast %3 +// CHECK-NEXT: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] +// CHECK-NEXT: [[BASE_SELF:%.*]] = upcast [[RELOAD_SELF]] // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers14ThrowBaseClassCACyKcfc // CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1 // CHECK-NEXT: store [[ONE]] to [[BITMAP_BOX]] @@ -1154,11 +1180,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[NEG_ONE:%.*]] = integer_literal $Builtin.Int2, -1 // CHECK-NEXT: store [[NEG_ONE]] to [[BITMAP_BOX]] // CHECK-NEXT: [[DERIVED_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] +// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: store [[DERIVED_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%2) // CHECK: bb3([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[DERIVED_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1168,6 +1194,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb7([[ERROR]] : $Error) // CHECK: bb6([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[DERIVED_SELF]] // CHECK-NEXT: br bb7([[ERROR]] : $Error) // CHECK: bb7([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1213,8 +1240,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[ARG:%.*]] : $Int): // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1232,8 +1259,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1253,8 +1280,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassCACyKcfC // CHECK-NEXT: try_apply [[INIT_FN]](%1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1279,8 +1306,8 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC20failBeforeDelegationACSi_tKcfC // CHECK-NEXT: try_apply [[INIT_FN]]([[ARG]], %1) // CHECK: bb2([[NEW_SELF:%.*]] : $ThrowDerivedClass): -// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: strong_retain [[NEW_SELF]] +// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] @@ -1300,15 +1327,16 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $ThrowDerivedClass // CHECK: [[INIT_FN:%.*]] = function_ref @$s35definite_init_failable_initializers17ThrowDerivedClassC6noFailACyt_tcfC // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb1([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] // CHECK: bb2([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: throw [[ERROR]] @@ -1328,11 +1356,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb1([[NEW_SELF:%.*]] : $ThrowDerivedClass): // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1340,6 +1368,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR1:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR1]] : $Error) // CHECK: bb4([[ERROR2:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR2]] : $Error) // CHECK: bb5([[ERROR3:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] @@ -1371,11 +1400,11 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: [[NEW_SELF:%.*]] = apply [[INIT_FN]](%1) // CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[BIT]] to [[BITMAP_BOX]] +// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] // CHECK: [[UNWRAP_FN:%.*]] = function_ref @$s35definite_init_failable_initializers6unwrapyS2iKF // CHECK-NEXT: try_apply [[UNWRAP_FN]](%0) // CHECK: bb2([[RESULT:%.*]] : $Int): -// CHECK-NEXT: strong_retain [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[BITMAP_BOX]] @@ -1383,6 +1412,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK: bb3([[ERROR:%.*]] : $Error): // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb4([[ERROR:%.*]] : $Error): +// CHECK-NEXT: strong_release [[NEW_SELF]] // CHECK-NEXT: br bb5([[ERROR]] : $Error) // CHECK: bb5([[ERROR:%.*]] : $Error): // CHECK-NEXT: [[BITMAP:%.*]] = load [[BITMAP_BOX]] diff --git a/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift b/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift index 4195c17efde93..125458ed212f6 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s +// RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s -enable-ownership-stripping-after-serialization // High-level tests that DI rejects certain invalid idioms for early // return from initializers. diff --git a/test/SILOptimizer/definite_init_failable_initializers_objc.swift b/test/SILOptimizer/definite_init_failable_initializers_objc.swift index a1e387452e236..f1be59b403c2d 100644 --- a/test/SILOptimizer/definite_init_failable_initializers_objc.swift +++ b/test/SILOptimizer/definite_init_failable_initializers_objc.swift @@ -38,32 +38,39 @@ class Cat : FakeNSObject { // CHECK: end sil function '$s40definite_init_failable_initializers_objc3CatC1n5afterACSgSi_SbtcfC' // CHECK-LABEL: sil hidden @$s40definite_init_failable_initializers_objc3CatC1n5afterACSgSi_Sbtcfc : $@convention(method) (Int, Bool, @owned Cat) -> @owned Optional - // CHECK: bb0(%0 : $Int, %1 : $Bool, %2 : $Cat): + // CHECK: bb0([[ARG0:%.*]] : $Int, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): // CHECK-NEXT: [[SELF_BOX:%.*]] = alloc_stack $Cat - // CHECK: store %2 to [[SELF_BOX]] : $*Cat - // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr %2 : $Cat, #Cat.x + // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat + // CHECK: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x // CHECK-NEXT: store {{%.*}} to [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: strong_release [[ARG2]] // CHECK-NEXT: [[COND:%.*]] = struct_extract %1 : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: - // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr %2 : $Cat, #Cat.x - // CHECK-NEXT: destroy_addr [[FIELD_ADDR]] : $*LifetimeTracked + // CHECK-NEXT: [[FIELD_ADDR:%.*]] = ref_element_addr [[ARG2]] : $Cat, #Cat.x + // CHECK-NEXT: [[FIELD_ADDR_ACCESS:%.*]] = begin_access [deinit] [static] [[FIELD_ADDR]] + // CHECK-NEXT: destroy_addr [[FIELD_ADDR_ACCESS]] : $*LifetimeTracked + // CHECK-NEXT: end_access [[FIELD_ADDR_ACCESS]] + // CHECK-NEXT: strong_release [[ARG2]] + // CHECK-NEXT: [[RELOAD_FROM_BOX:%.*]] = load [[SELF_BOX]] // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thick Cat.Type - // CHECK-NEXT: dealloc_partial_ref %2 : $Cat, [[METATYPE]] : $@thick Cat.Type + // CHECK-NEXT: dealloc_partial_ref [[RELOAD_FROM_BOX]] : $Cat, [[METATYPE]] : $@thick Cat.Type // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: br bb3([[RESULT]] : $Optional) // CHECK: bb2: - // CHECK-NEXT: [[SUPER:%.*]] = upcast %2 : $Cat to $FakeNSObject + // CHECK-NEXT: strong_release [[ARG2]] + // CHECK-NEXT: [[RELOAD_ARG2:%.*]] = load [[SELF_BOX]] + // CHECK-NEXT: [[SUPER:%.*]] = upcast [[RELOAD_ARG2]] : $Cat to $FakeNSObject // CHECK-NEXT: [[SUB:%.*]] = unchecked_ref_cast [[SUPER]] : $FakeNSObject to $Cat // CHECK-NEXT: [[SUPER_FN:%.*]] = objc_super_method [[SUB]] : $Cat, #FakeNSObject.init!initializer.1.foreign : (FakeNSObject.Type) -> () -> FakeNSObject, $@convention(objc_method) (@owned FakeNSObject) -> @owned FakeNSObject // CHECK-NEXT: [[NEW_SUPER_SELF:%.*]] = apply [[SUPER_FN]]([[SUPER]]) : $@convention(objc_method) (@owned FakeNSObject) -> @owned FakeNSObject // CHECK-NEXT: [[NEW_SELF:%.*]] = unchecked_ref_cast [[NEW_SUPER_SELF]] : $FakeNSObject to $Cat - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // TODO: Once we re-enable arbitrary take promotion, this retain and the associated destroy_addr will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[NEW_SELF]] : $Cat // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat @@ -89,18 +96,19 @@ class Cat : FakeNSObject { // CHECK: end sil function '$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_SbtcfC' // CHECK-LABEL: sil hidden @$s40definite_init_failable_initializers_objc3CatC4fail5afterACSgSb_Sbtcfc : $@convention(method) (Bool, Bool, @owned Cat) -> @owned Optional - // CHECK: bb0(%0 : $Bool, %1 : $Bool, %2 : $Cat): + // CHECK: bb0([[ARG0:%.*]] : $Bool, [[ARG1:%.*]] : $Bool, [[ARG2:%.*]] : $Cat): // CHECK-NEXT: [[HAS_RUN_INIT_BOX:%.+]] = alloc_stack $Builtin.Int1 // CHECK-NEXT: [[SELF_BOX:%.+]] = alloc_stack [dynamic_lifetime] $Cat - // CHECK: store %2 to [[SELF_BOX]] : $*Cat - // CHECK-NEXT: [[COND:%.+]] = struct_extract %0 : $Bool, #Bool._value + // CHECK: store [[ARG2]] to [[SELF_BOX]] : $*Cat + // CHECK-NEXT: [[COND:%.+]] = struct_extract [[ARG0]] : $Bool, #Bool._value // CHECK-NEXT: cond_br [[COND]], bb1, bb2 // CHECK: bb1: // CHECK-NEXT: br [[ERROR_BRANCH:bb[0-9]+]] // CHECK: bb{{[0-9]+}}: - // CHECK: [[SELF_INIT:%.+]] = objc_method %2 : $Cat, #Cat.init!initializer.1.foreign : (Cat.Type) -> (Int, Bool) -> Cat? + // CHECK: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] + // CHECK: [[SELF_INIT:%.+]] = objc_method [[RELOAD_SELF]] : $Cat, #Cat.init!initializer.1.foreign : (Cat.Type) -> (Int, Bool) -> Cat? // CHECK: [[NEW_OPT_SELF:%.+]] = apply [[SELF_INIT]]({{%.+}}, {{%.+}}, {{%.+}}) : $@convention(objc_method) (Int, ObjCBool, @owned Cat) -> @owned Optional // CHECK: [[COND:%.+]] = select_enum [[NEW_OPT_SELF]] : $Optional // CHECK-NEXT: cond_br [[COND]], [[SUCCESS_BRANCH:bb[0-9]+]], [[RELEASE_THEN_ERROR_BRANCH:bb[0-9]+]] @@ -111,9 +119,9 @@ class Cat : FakeNSObject { // CHECK: [[SUCCESS_BRANCH]]: // CHECK-NEXT: [[NEW_SELF:%.+]] = unchecked_enum_data [[NEW_OPT_SELF]] : $Optional, #Optional.some!enumelt.1 - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // TODO: Once we re-enable arbitrary take promotion, this retain and the associated destroy_addr will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] : $*Cat // CHECK-NEXT: [[RESULT:%.+]] = enum $Optional, #Optional.some!enumelt.1, [[NEW_SELF]] : $Cat // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] : $*Cat @@ -127,8 +135,9 @@ class Cat : FakeNSObject { // CHECK-NEXT: br [[ERROR_CLEANUP_BRANCH:bb[0-9]+]] // CHECK: [[ERROR_WITH_DESTROY_BRANCH]]: - // CHECK-NEXT: [[MOST_DERIVED_TYPE:%.+]] = value_metatype $@thick Cat.Type, %2 : $Cat - // CHECK-NEXT: dealloc_partial_ref %2 : $Cat, [[MOST_DERIVED_TYPE]] : $@thick Cat.Type + // CHECK: [[RELOAD_SELF:%.*]] = load [[SELF_BOX]] + // CHECK-NEXT: [[MOST_DERIVED_TYPE:%.+]] = value_metatype $@thick Cat.Type, [[RELOAD_SELF]] : $Cat + // CHECK-NEXT: dealloc_partial_ref [[RELOAD_SELF]] : $Cat, [[MOST_DERIVED_TYPE]] : $@thick Cat.Type // CHECK-NEXT: br [[ERROR_CLEANUP_BRANCH]] // CHECK: [[ERROR_CLEANUP_BRANCH]]: diff --git a/test/SILOptimizer/definite_init_hang.swift b/test/SILOptimizer/definite_init_hang.swift index cc932ae262ea3..683a0618da44b 100644 --- a/test/SILOptimizer/definite_init_hang.swift +++ b/test/SILOptimizer/definite_init_hang.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil %s -parse-as-library -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil %s -parse-as-library -o /dev/null -verify -enable-ownership-stripping-after-serialization var gg: Bool = false var rg: Int = 0 diff --git a/test/SILOptimizer/definite_init_lvalue_let_witness_methods.swift b/test/SILOptimizer/definite_init_lvalue_let_witness_methods.swift index 5d6885ff0edd0..ecc5fe88afac6 100644 --- a/test/SILOptimizer/definite_init_lvalue_let_witness_methods.swift +++ b/test/SILOptimizer/definite_init_lvalue_let_witness_methods.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s +// RUN: %target-swift-frontend -emit-sil -disable-objc-attr-requires-foundation-module -verify %s -enable-ownership-stripping-after-serialization // High-level tests that DI rejects passing let constants to // mutating witness methods diff --git a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil index 5726186e7d536..d2e9a949690bf 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_delegatingself.sil @@ -89,10 +89,10 @@ bb0(%0 : $*MyStruct2, %1 : $@thin MyStruct2.Type): sil [ossa] @test_delegating_box_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() @@ -114,12 +114,12 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): sil [ossa] @test_delegating_rvalue_release : $@convention(method) (@owned RootClassWithNontrivialStoredProperties) -> () { bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): %2 = alloc_box $<τ_0_0> { var τ_0_0 } - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [delegatingselfallocated] %2a : $*RootClassWithNontrivialStoredProperties + %2a = mark_uninitialized [delegatingselfallocated] %2 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %2a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %4 : $*RootClassWithNontrivialStoredProperties %6 = load [take] %4 : $*RootClassWithNontrivialStoredProperties destroy_value %6 : $RootClassWithNontrivialStoredProperties - destroy_value %2 : $<τ_0_0> { var τ_0_0 } + destroy_value %2a : $<τ_0_0> { var τ_0_0 } %13 = tuple () return %13 : $() diff --git a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil index d0be2dca9fded..8ab4fc33ec85b 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_derivedself.sil @@ -63,8 +63,8 @@ sil @getSomeOptionalClass : $@convention(thin) () -> Optional sil [ossa] @derived_test1 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Get an int @@ -87,7 +87,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): // Finally perform the epilog. %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -112,8 +112,8 @@ bb0(%0 : @owned $DerivedClassWithIVars): sil [ossa] @derived_test2 : $@convention(method) (@owned DerivedClassWithIVars) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars %11 = load [take] %3 : $*DerivedClassWithIVars @@ -124,7 +124,7 @@ bb0(%0 : @owned $DerivedClassWithIVars): store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } @@ -195,7 +195,9 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): // Now we destroy the stored value. // CHECK: [[SELF:%[0-9]+]] = load_borrow [[SELFBOX]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[SELF]] // // And then perform dealloc_partial_ref. @@ -246,8 +248,8 @@ bb0(%0 : @owned $DerivedClassWithNontrivialStoredProperties): sil [ossa] @super_init_out_of_order : $@convention(method) (@owned DerivedClassWithIVars, Int) -> @owned DerivedClassWithIVars { bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %3 = mark_uninitialized [derivedself] %1a : $*DerivedClassWithIVars + %1a = mark_uninitialized [derivedself] %1 : $<τ_0_0> { var τ_0_0 } + %3 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %3 : $*DerivedClassWithIVars // Initialize properties in derived class. @@ -274,6 +276,6 @@ bb0(%0 : @owned $DerivedClassWithIVars, %i : $Int): %16 = unchecked_ref_cast %15 : $RootClassWithIVars to $DerivedClassWithIVars store %16 to [init] %3 : $*DerivedClassWithIVars %18 = load [copy] %3 : $*DerivedClassWithIVars - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } return %18 : $DerivedClassWithIVars } diff --git a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil index d9346af18cefd..ffc00fc2f1dfe 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_rootself.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_rootself.sil @@ -109,7 +109,9 @@ bb0(%0 : @owned $RootClassWithNontrivialStoredProperties): // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]] // CHECK: [[SELF_FIELD:%.*]] = ref_element_addr [[BORROWED_SELF]] -// CHECK: destroy_addr [[SELF_FIELD]] +// CHECK: [[SELF_FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [[SELF_FIELD]] +// CHECK: destroy_addr [[SELF_FIELD_ACCESS]] +// CHECK: end_access [[SELF_FIELD_ACCESS]] // CHECK: end_borrow [[BORROWED_SELF]] // CHECK: [[METATYPE:%[0-9]+]] = metatype $@thick RootClassWithNontrivialStoredProperties.Type // CHECK: dealloc_partial_ref [[SELF]] : $RootClassWithNontrivialStoredProperties, [[METATYPE]] : $@thick RootClassWithNontrivialStoredProperties.Type diff --git a/test/SILOptimizer/definite_init_markuninitialized_var.sil b/test/SILOptimizer/definite_init_markuninitialized_var.sil index fc41fed4a68e5..270d6c5b65516 100644 --- a/test/SILOptimizer/definite_init_markuninitialized_var.sil +++ b/test/SILOptimizer/definite_init_markuninitialized_var.sil @@ -15,10 +15,10 @@ sil @makesInt : $@convention(thin) () -> Int sil [ossa] @use_before_init : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %4 = load [trivial] %1 : $*Int // expected-error {{variable '' used before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %4 : $Int } @@ -26,13 +26,13 @@ bb0: sil [ossa] @inout_uninit : $@convention(thin) () -> () { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}} + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '' passed by reference before being initialized}} - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } %t = tuple () return %t : $() } @@ -48,8 +48,8 @@ bb0: sil [ossa] @used_by_inout : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %91 = alloc_box $<τ_0_0> { var τ_0_0 } - %91a = project_box %91 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %91a : $*Int + %91a = mark_uninitialized [var] %91 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %91a : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [trivial] %1 : $*Int %3 = load [trivial] %1 : $*Int @@ -57,7 +57,7 @@ bb0(%0 : $Int): %6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () %7 = load [trivial] %1 : $*Int %8 = tuple (%3 : $Int, %7 : $Int) - destroy_value %91 : $<τ_0_0> { var τ_0_0 } + destroy_value %91a : $<τ_0_0> { var τ_0_0 } return %8 : $(Int, Int) } @@ -74,14 +74,14 @@ sil [ossa] @returns_generic_struct : $@convention(thin) () -> @out AddressOnlySt sil [ossa] @call_struct_return_function : $@convention(thin) () -> Int { bb0: %0 = alloc_box $<τ_0_0> { var τ_0_0 } - %0a = project_box %0 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct + %0a = mark_uninitialized [var] %0 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %0a : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct %3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct %4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b %5 = load [trivial] %4 : $*Int - destroy_value %0 : $<τ_0_0> { var τ_0_0 } + destroy_value %0a : $<τ_0_0> { var τ_0_0 } return %5 : $Int } @@ -89,15 +89,14 @@ bb0: sil [ossa] @tuple_elements1 : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %4 = tuple_element_addr %3 : $*(Int, Int), 0 %5 = tuple_element_addr %3 : $*(Int, Int), 1 %14 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> () %15 = tuple_element_addr %3 : $*(Int, Int), 1 %16 = apply %14(%15) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '.1' passed by reference before being initialized}} - - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> %99 = tuple () return %99 : $() } @@ -106,15 +105,15 @@ bb0(%0 : $Int): sil [ossa] @tuple_elements2 : $@convention(thin) (Int) -> (Int, Int) { bb0(%0 : $Int): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Int, Int)> - %2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 - %3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}} + %2a = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> // expected-note {{variable defined here}} + %3 = project_box %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)>, 0 %18 = tuple_element_addr %3 : $*(Int, Int), 0 store %0 to [trivial] %18 : $*Int %20 = load [trivial] %3 : $*(Int, Int) // expected-error {{variable '.1' used before being initialized}} %21 = tuple_extract %20 : $(Int, Int), 0 %22 = tuple_extract %20 : $(Int, Int), 1 %23 = tuple (%21 : $Int, %22 : $Int) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Int, Int)> + destroy_value %2a : $<τ_0_0> { var τ_0_0 } <(Int, Int)> return %23 : $(Int, Int) } @@ -122,11 +121,11 @@ bb0(%0 : $Int): sil [ossa] @copy_addr1 : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr [take] %1 to [initialization] %4 : $*T copy_addr %4 to [initialization] %0 : $*T - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -135,10 +134,10 @@ bb0(%0 : $*T, %1 : $*T): sil [ossa] @copy_addr2 : $@convention(thin) (@in_guaranteed T) -> @out T { bb0(%0 : $*T, %1 : $*T): %3 = alloc_box $<τ_0_0> { var τ_0_0 } - %3a = project_box %3 : $<τ_0_0> { var τ_0_0 } , 0 - %4 = mark_uninitialized [var] %3a : $*T // expected-note {{variable defined here}} + %3a = mark_uninitialized [var] %3 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %4 = project_box %3a : $<τ_0_0> { var τ_0_0 } , 0 copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '' used before being initialized}} - destroy_value %3 : $<τ_0_0> { var τ_0_0 } + destroy_value %3a : $<τ_0_0> { var τ_0_0 } %9 = tuple () return %9 : $() } @@ -150,16 +149,16 @@ sil [ossa] @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) sil [ossa] @closure_test : $@convention(thin) () -> () { bb0: %1 = alloc_box $<τ_0_0> { var τ_0_0 } - %1a = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 - %0 = mark_uninitialized [var] %1a : $*Int // expected-note {{variable defined here}} + %1a = mark_uninitialized [var] %1 : $<τ_0_0> { var τ_0_0 } // expected-note {{variable defined here}} + %0 = project_box %1a : $<τ_0_0> { var τ_0_0 } , 0 %5 = function_ref @takes_closure : $@convention(thin) (@owned @callee_owned () -> ()) -> () %6 = function_ref @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () - %1copy = copy_value %1 : $<τ_0_0> { var τ_0_0 } + %1copy = copy_value %1a : $<τ_0_0> { var τ_0_0 } mark_function_escape %0 : $*Int // expected-error {{variable '' used by function definition before being initialized}} %8 = partial_apply %6(%1copy) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () %9 = apply %5(%8) : $@convention(thin) (@owned @callee_owned () -> ()) -> () - destroy_value %1 : $<τ_0_0> { var τ_0_0 } + destroy_value %1a : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -176,8 +175,8 @@ sil [ossa] @getSomeOptionalClass : $@convention(thin) () -> @owned Optional Int { bb0(%0 : $Int): %7 = alloc_box $<τ_0_0> { var τ_0_0 } - %7a = project_box %7 : $<τ_0_0> { var τ_0_0 } , 0 - %1 = mark_uninitialized [var] %7a : $*Int + %7a = mark_uninitialized [var] %7 : $<τ_0_0> { var τ_0_0 } + %1 = project_box %7a : $<τ_0_0> { var τ_0_0 } , 0 // These assigns are a mix of init + store forms, but because Int is trivial, // they all turn into stores. @@ -186,7 +185,7 @@ bb0(%0 : $Int): assign %0 to %1 : $*Int %2 = load [trivial] %1 : $*Int - destroy_value %7 : $<τ_0_0> { var τ_0_0 } + destroy_value %7a : $<τ_0_0> { var τ_0_0 } return %2 : $Int } @@ -198,8 +197,8 @@ bb0: // lone store), the second becomes a reassignment (retain/release dance). %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -217,7 +216,7 @@ bb0: destroy_addr %c : $*SomeClass // CHECK-NEXT: destroy_addr - dealloc_box %b : $<τ_0_0> { var τ_0_0 } + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } %11 = tuple () return %11 : $() @@ -227,8 +226,8 @@ bb0: sil [ossa] @assign_test_addressonly : $@convention(thin) (@in T) -> @out T { bb0(%0 : $*T, %1 : $*T): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %2 = mark_uninitialized [var] %ba : $*T + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %2 = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 // CHECK: [[PB:%[0-9]+]] = project_box @@ -244,7 +243,7 @@ bb0(%0 : $*T, %1 : $*T): copy_addr %2 to [initialization] %0 : $*T // CHECK-NEXT: copy_addr [[PB]] to [initialization] %0 : $*T - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } // CHECK-NEXT: destroy_value %2 %9 = tuple () return %9 : $() @@ -257,8 +256,8 @@ bb0: // second becomes an assignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_weak Optional + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional>, 0 %f = function_ref @getSomeOptionalClass : $@convention(thin) () -> @owned Optional @@ -281,7 +280,7 @@ bb0: destroy_value %8 : $Optional // CHECK-NEXT: destroy_value [[C2]] - destroy_value %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> + destroy_value %ba : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional> %11 = tuple () return %11 : $() @@ -294,8 +293,8 @@ bb0: // second becomes a reassignment. %b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 - %c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0 %f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass @@ -328,7 +327,7 @@ bb0: // CHECK-NEXT: destroy_value [[C2]] destroy_addr %c : $*@sil_unowned SomeClass - dealloc_box %b : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> + dealloc_box %ba : $<τ_0_0> { var τ_0_0 } <@sil_unowned SomeClass> %11 = tuple () return %11 : $() @@ -342,12 +341,12 @@ struct ContainsNativeObject { sil [ossa] @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () { bb0(%0 : $*ContainsNativeObject): %b = alloc_box $<τ_0_0> { var τ_0_0 } - %ba = project_box %b : $<τ_0_0> { var τ_0_0 } , 0 - %c = mark_uninitialized [var] %ba : $*ContainsNativeObject + %ba = mark_uninitialized [var] %b : $<τ_0_0> { var τ_0_0 } + %c = project_box %ba : $<τ_0_0> { var τ_0_0 } , 0 %1 = load [copy] %0 : $*ContainsNativeObject assign %1 to %c : $*ContainsNativeObject - destroy_value %b : $<τ_0_0> { var τ_0_0 } + destroy_value %ba : $<τ_0_0> { var τ_0_0 } %x = tuple () return %x : $() } @@ -557,8 +556,8 @@ bb2: sil [ossa] @conditionalInitOrAssignAllocBox : $@convention(thin) () -> () { bb0: %box = alloc_box $<τ_0_0> { var τ_0_0 } - %5 = project_box %box : $<τ_0_0> { var τ_0_0 } , 0 - %7 = mark_uninitialized [var] %5 : $*SomeClass + %5 = mark_uninitialized [var] %box : $<τ_0_0> { var τ_0_0 } + %7 = project_box %5 : $<τ_0_0> { var τ_0_0 } , 0 %2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass cond_br undef, bb1, bb2 @@ -566,11 +565,11 @@ bb0: bb1: %12 = apply %2() : $@convention(thin) () -> @owned SomeClass assign %12 to %7 : $*SomeClass - destroy_value %box : $<τ_0_0> { var τ_0_0 } + destroy_value %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb2: - destroy_value %box : $<τ_0_0> { var τ_0_0 } + dealloc_box %5 : $<τ_0_0> { var τ_0_0 } br bb3 bb3: diff --git a/test/SILOptimizer/definite_init_protocol_init.swift b/test/SILOptimizer/definite_init_protocol_init.swift index 182affb558f6c..fca217255fabd 100644 --- a/test/SILOptimizer/definite_init_protocol_init.swift +++ b/test/SILOptimizer/definite_init_protocol_init.swift @@ -35,10 +35,10 @@ class TrivialClass : TriviallyConstructible { // CHECK-NEXT: [[FN:%.*]] = function_ref @$s023definite_init_protocol_B022TriviallyConstructiblePAAE6middlexSi_tcfC // CHECK-NEXT: apply [[FN]]<@dynamic_self TrivialClass>([[RESULT]], %0, [[METATYPE]]) // CHECK-NEXT: [[NEW_SELF:%.*]] = load [[RESULT]] - // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] - // CHECK-NEXT: dealloc_stack [[RESULT]] // TODO: Once we restore arbitrary takes, the strong_retain/destroy_addr pair below will go away. // CHECK-NEXT: strong_retain [[NEW_SELF]] + // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_BOX]] + // CHECK-NEXT: dealloc_stack [[RESULT]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: return [[NEW_SELF]] diff --git a/test/SILOptimizer/definite_init_scalarization_test.sil b/test/SILOptimizer/definite_init_scalarization_test.sil index 6f45b22dc4c58..a2f4a88d2367d 100644 --- a/test/SILOptimizer/definite_init_scalarization_test.sil +++ b/test/SILOptimizer/definite_init_scalarization_test.sil @@ -43,12 +43,12 @@ struct ObjectValue { sil [ossa] @test_store_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) store %6 to [trivial] %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -73,12 +73,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_store_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) store %6 to [init] %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -103,12 +103,12 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assign_trivial : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -139,14 +139,14 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_trivial_2 : $@convention(thin) (TripleInt, TripleInt, TripleInt) -> () { bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 - %4 = mark_uninitialized [var] %3 : $*(TripleInt, (TripleInt, TripleInt)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))>, 0 %5 = tuple(%0 : $TripleInt, %1 : $TripleInt) %6 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) %7 = tuple(%1a : $TripleInt, %5 : $(TripleInt, TripleInt)) assign %6 to %4 : $*(TripleInt, (TripleInt, TripleInt)) assign %7 to %4 : $*(TripleInt, (TripleInt, TripleInt)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(TripleInt, (TripleInt, TripleInt))> %9999 = tuple() return %9999 : $() } @@ -171,12 +171,12 @@ bb0(%0 : $TripleInt, %1 : $TripleInt, %1a : $TripleInt): sil [ossa] @test_assign_owned : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } @@ -209,14 +209,14 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : sil [ossa] @test_assigned_owned_2 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %1a : @owned $Builtin.NativeObject): %2 = alloc_box $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> - %3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 - %4 = mark_uninitialized [var] %3 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) + %3 = mark_uninitialized [var] %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + %4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))>, 0 %5 = tuple(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject) %6 = tuple(%1a : $Builtin.NativeObject, %5 : $(Builtin.NativeObject, Builtin.NativeObject)) %7 = copy_value %6 : $(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %6 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) assign %7 to %4 : $*(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject)) - destroy_value %2 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> + destroy_value %3 : $<τ_0_0> { var τ_0_0 } <(Builtin.NativeObject, (Builtin.NativeObject, Builtin.NativeObject))> %9999 = tuple() return %9999 : $() } diff --git a/test/SILOptimizer/definite_init_value_types.swift b/test/SILOptimizer/definite_init_value_types.swift index aa23047d53533..dc5538d43930f 100644 --- a/test/SILOptimizer/definite_init_value_types.swift +++ b/test/SILOptimizer/definite_init_value_types.swift @@ -59,9 +59,9 @@ enum ValueEnum { // CHECK: bb6: // CHECK-NEXT: [[NEW_STATE:%.*]] = integer_literal $Builtin.Int1, -1 // CHECK-NEXT: store [[NEW_STATE]] to [[STATE]] + // CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: store [[NEW_SELF]] to [[SELF_ACCESS]] // CHECK-NEXT: end_access [[SELF_ACCESS]] - // CHECK-NEXT: retain_value [[NEW_SELF]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[STATE]] diff --git a/test/SILOptimizer/definite_init_value_types_diagnostics.swift b/test/SILOptimizer/definite_init_value_types_diagnostics.swift index 6cb49e869f8b4..3168a8e32e014 100644 --- a/test/SILOptimizer/definite_init_value_types_diagnostics.swift +++ b/test/SILOptimizer/definite_init_value_types_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify -enable-ownership-stripping-after-serialization struct EmptyStruct {} diff --git a/test/SILOptimizer/destroy_hoisting.sil b/test/SILOptimizer/destroy_hoisting.sil index af84ef83a5518..e97dc0e91aaac 100644 --- a/test/SILOptimizer/destroy_hoisting.sil +++ b/test/SILOptimizer/destroy_hoisting.sil @@ -209,3 +209,72 @@ bb1: destroy_addr %0 : $*Mixed return %v : $Int } + +sil @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int + +// CHECK-LABEL: sil [ossa] @test_begin_apply +// CHECK: end_apply +// CHECK-NEXT: destroy_addr %0 +// CHECK-NEXT: br bb1 +// CHECK: bb1: +// CHECK: return +sil [ossa] @test_begin_apply : $@convention(thin) (@in S, Int) -> () { +bb0(%0 : $*S, %1 : $Int): + %f = function_ref @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int + (%i, %t) = begin_apply %f(%0) : $@yield_once @convention(thin) (@in S) -> @yields @inout Int + store %1 to [trivial] %i : $*Int + end_apply %t + br bb1 +bb1: + destroy_addr %0 : $*S + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_abort_apply +// CHECK: abort_apply +// CHECK-NEXT: destroy_addr %0 +// CHECK-NEXT: br bb1 +// CHECK: bb1: +// CHECK: return +sil [ossa] @test_abort_apply : $@convention(thin) (@in S, Int) -> () { +bb0(%0 : $*S, %1 : $Int): + %f = function_ref @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int + (%i, %t) = begin_apply %f(%0) : $@yield_once @convention(thin) (@in S) -> @yields @inout Int + abort_apply %t + br bb1 +bb1: + destroy_addr %0 : $*S + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil [ossa] @test_simple_infinite_loop +// CHECK-NOT: destroy_addr +// CHECK: } // end sil function 'test_simple_infinite_loop' +sil [ossa] @test_simple_infinite_loop : $@convention(thin) (@in_guaranteed S) -> () { +bb0(%0 : $*S): + br bb1 +bb1: + br bb1 +} + +// CHECK-LABEL: sil [ossa] @test_infinite_loop +// CHECK-NOT: destroy_addr +// CHECK: bb3: +// CHECK-NEXT: destroy_addr %1 +// CHECK-NOT: destroy_addr +// CHECK: } // end sil function 'test_infinite_loop' +sil [ossa] @test_infinite_loop : $@convention(thin) (@in_guaranteed S, @in S) -> () { +bb0(%0 : $*S, %1 : $*S): + cond_br undef, bb1, bb2 +bb1: + br bb1 +bb2: + br bb3 +bb3: + destroy_addr %1 : $*S + %r = tuple () + return %r : $() +} + diff --git a/test/SILOptimizer/devirt_base_class.swift b/test/SILOptimizer/devirt_base_class.swift index cf82d7b893c82..76239f4259366 100644 --- a/test/SILOptimizer/devirt_base_class.swift +++ b/test/SILOptimizer/devirt_base_class.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -enable-spec-devirt -O -emit-sil %s | %FileCheck %s public class Base1 { @inline(never) func f() -> Int { return 0 } } diff --git a/test/SILOptimizer/devirt_covariant_return.swift b/test/SILOptimizer/devirt_covariant_return.swift index 5981c2b67ade7..0f63101cc62cd 100644 --- a/test/SILOptimizer/devirt_covariant_return.swift +++ b/test/SILOptimizer/devirt_covariant_return.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_covariant_return -Xllvm -sil-full-demangle -O -Xllvm -disable-sil-cm-rr-cm=0 -Xllvm -sil-inline-generics=false -primary-file %s -emit-sil -sil-inline-threshold 1000 -Xllvm -sil-disable-pass=ObjectOutliner -sil-verify-all | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_covariant_return -Xllvm -sil-full-demangle -enable-spec-devirt -O -Xllvm -disable-sil-cm-rr-cm=0 -Xllvm -sil-inline-generics=false -primary-file %s -emit-sil -sil-inline-threshold 1000 -Xllvm -sil-disable-pass=ObjectOutliner -sil-verify-all | %FileCheck %s // Make sure that we can dig all the way through the class hierarchy and // protocol conformances with covariant return types correctly. The verifier @@ -110,7 +110,7 @@ public class Bear { // Check that devirtualizer can handle convenience initializers, which have covariant optional // return types. // CHECK-LABEL: sil @$s23devirt_covariant_return4BearC{{[_0-9a-zA-Z]*}}fC - // CHECK: checked_cast_br [exact] %{{.*}} : $@thick Bear.Type to $@thick GummyBear.Type + // CHECK: checked_cast_br [exact] %{{.*}} : $@thick Bear.Type to @thick GummyBear.Type // CHECK: upcast %{{.*}} : $Optional to $Optional // CHECK: } public convenience init?(delegateFailure: Bool, failAfter: Bool) { @@ -233,7 +233,7 @@ public class D2: D1 { // that D2.foo() is inlined thanks to this. // CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver2ys5Int32VAA2D2CF // CHECK-NOT: class_method -// CHECK: checked_cast_br [exact] %{{.*}} : $D1 to $D2 +// CHECK: checked_cast_br [exact] %{{.*}} : $D1 to D2 // CHECK: bb2 // CHECK: global_addr // CHECK: load @@ -277,9 +277,9 @@ class EEE : CCC { // Check that c.foo() is devirtualized, because the optimizer can handle the casting the return type // correctly, i.e. it can cast (BBB, BBB) into (AAA, AAA) // CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return37testDevirtOfMethodReturningTupleTypes_1bAA2AAC_AEtAA3CCCC_AA2BBCtF -// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $CCC -// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $DDD -// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $EEE +// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to CCC +// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to DDD +// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to EEE // CHECK: class_method // CHECK: } @inline(never) @@ -317,10 +317,10 @@ class DDDD : CCCC { // Check devirtualization of methods with optional results, where // optional results need to be casted. // CHECK-LABEL: sil [noinline] @{{.*}}testOverridingMethodWithOptionalResult -// CHECK: checked_cast_br [exact] %{{.*}} : $F to $F -// CHECK: checked_cast_br [exact] %{{.*}} : $F to $G +// CHECK: checked_cast_br [exact] %{{.*}} : $F to F +// CHECK: checked_cast_br [exact] %{{.*}} : $F to G // CHECK: switch_enum -// CHECK: checked_cast_br [exact] %{{.*}} : $F to $H +// CHECK: checked_cast_br [exact] %{{.*}} : $F to H // CHECK: switch_enum @inline(never) public func testOverridingMethodWithOptionalResult(_ f: F) -> (F?, Int)? { diff --git a/test/SILOptimizer/devirt_default_case.swift b/test/SILOptimizer/devirt_default_case.swift index 04e32468b532e..6d485265edf67 100644 --- a/test/SILOptimizer/devirt_default_case.swift +++ b/test/SILOptimizer/devirt_default_case.swift @@ -1,6 +1,6 @@ -// RUN: %target-swift-frontend -O -module-name devirt_default_case -emit-sil %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-NORMAL %s -// RUN: %target-swift-frontend -O -module-name devirt_default_case -emit-sil -enable-testing %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-TESTABLE %s +// RUN: %target-swift-frontend -enable-spec-devirt -O -module-name devirt_default_case -emit-sil %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-NORMAL %s +// RUN: %target-swift-frontend -enable-spec-devirt -O -module-name devirt_default_case -emit-sil -enable-testing %s | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-TESTABLE %s @_silgen_name("action") func action(_ n:Int) -> () @@ -171,8 +171,8 @@ func check_static_class_devirt(_ c: C6) -> Int { // Check that C.bar() and D.bar() are devirtualized. // // CHECK-LABEL: sil{{( hidden)?}} [noinline] @$s19devirt_default_case019check_static_class_A0ySiAA2C6CF -// CHECK: checked_cast_br [exact] %0 : $C6 to $C6 -// CHECK: checked_cast_br [exact] %0 : $C6 to $D6 +// CHECK: checked_cast_br [exact] %0 : $C6 to C6 +// CHECK: checked_cast_br [exact] %0 : $C6 to D6 // CHECK: class_method // CHECK: } // end sil function '$s19devirt_default_case019check_static_class_A0ySiAA2C6CF' return c.bar() diff --git a/test/SILOptimizer/devirt_jump_thread.sil b/test/SILOptimizer/devirt_jump_thread.sil index 156701673611d..6acf61a40d886 100644 --- a/test/SILOptimizer/devirt_jump_thread.sil +++ b/test/SILOptimizer/devirt_jump_thread.sil @@ -71,10 +71,10 @@ sil @_TF18devirt_jump_thread26jumpthread_checked_cast_brFCS_8FooClassSi : $@conv bb0(%0 : $FooClass): %1 = integer_literal $Builtin.Int32, 2 // user: %2 %2 = struct $Int32 (%1 : $Builtin.Int32) // users: %37, %43, %48, %54 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb5, bb6 // id: %3 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb5, bb6 // id: %3 bb1(%4 : $Int32): // Preds: bb5 bb6 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb7, bb8 // id: %5 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb7, bb8 // id: %5 bb2(%6 : $Int32): // Preds: bb7 bb8 %7 = struct_extract %4 : $Int32, #Int32._value // user: %10 @@ -86,7 +86,7 @@ bb2(%6 : $Int32): // Preds: bb7 bb8 cond_fail %12 : $Builtin.Int1 // id: %13 %14 = integer_literal $Builtin.Int32, 3 // user: %15 %15 = struct $Int32 (%14 : $Builtin.Int32) // users: %59, %65 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb9, bb10 // id: %16 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb9, bb10 // id: %16 bb3(%17 : $Int32): // Preds: bb9 bb10 %18 = struct_extract %17 : $Int32, #Int32._value // user: %19 @@ -96,7 +96,7 @@ bb3(%17 : $Int32): // Preds: bb9 bb10 cond_fail %21 : $Builtin.Int1 // id: %22 %23 = integer_literal $Builtin.Int32, 4 // user: %24 %24 = struct $Int32 (%23 : $Builtin.Int32) // users: %69, %74 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb11, bb12 // id: %25 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb11, bb12 // id: %25 bb4(%26 : $Int32): // Preds: bb11 bb12 %27 = struct_extract %26 : $Int32, #Int32._value // user: %28 @@ -180,10 +180,10 @@ bb12: // Preds: bb3 // devirt_jump_thread.double (devirt_jump_thread.FooClass) -> Swift.Int32 sil @_TF18devirt_jump_thread6doubleFCS_8FooClassSi : $@convention(thin) (@guaranteed FooClass) -> Int32 { bb0(%0 : $FooClass): - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb3, bb4 // id: %1 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb3, bb4 // id: %1 bb1(%2 : $Int32): // Preds: bb3 bb4 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb5, bb6 // id: %3 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb5, bb6 // id: %3 bb2(%4 : $Int32): // Preds: bb5 bb6 %5 = struct_extract %2 : $Int32, #Int32._value // user: %8 @@ -233,11 +233,11 @@ bb6: // Preds: bb1 // CHECK: } sil @_TF18devirt_jump_thread6dont_jump_thread_alloc_stackFCS_8FooClassSi : $@convention(thin) (@guaranteed FooClass) -> Int32 { bb0(%0 : $FooClass): - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb3, bb4 // id: %1 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb3, bb4 // id: %1 bb1(%2 : $Int32): // Preds: bb3 bb4 %60 = alloc_stack $Int32 - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb5, bb6 // id: %3 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb5, bb6 // id: %3 bb2(%4 : $Int32): // Preds: bb5 bb6 %5 = struct_extract %2 : $Int32, #Int32._value // user: %8 @@ -288,11 +288,11 @@ bb6: // Preds: bb1 sil @_TF18devirt_jump_thread6dont_jump_thread_objc_methodFCS_8FooClassSi : $@convention(thin) (@guaranteed FooClass) -> Int32 { bb0(%0 : $FooClass): %100 = alloc_ref $Bar - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb3, bb4 // id: %1 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb3, bb4 // id: %1 bb1(%2 : $Int32): // Preds: bb3 bb4 %101 = objc_method %100 : $Bar, #Bar.foo!1.foreign : (Bar) -> () -> (), $@convention(objc_method) (Bar) -> () - checked_cast_br [exact] %0 : $FooClass to $FooClass, bb5, bb6 // id: %3 + checked_cast_br [exact] %0 : $FooClass to FooClass, bb5, bb6 // id: %3 bb2(%4 : $Int32): // Preds: bb5 bb6 %5 = struct_extract %2 : $Int32, #Int32._value // user: %8 @@ -363,7 +363,7 @@ class C { sil @test_checked_cast_br_jump_threading_with_entry_bb_arguments : $@convention(thin) (@owned AnyObject, @owned AnyObject) -> Int32 { bb0(%0 : $AnyObject, %1 : $AnyObject): strong_retain %0 : $AnyObject - checked_cast_br %0 : $AnyObject to $C, bb1, bb2 + checked_cast_br %0 : $AnyObject to C, bb1, bb2 bb1(%4 : $C): %5 = enum $Optional, #Optional.some!enumelt.1, %4 : $C @@ -382,7 +382,7 @@ bb4: bb5(%13 : $C): strong_retain %1 : $AnyObject - checked_cast_br %1 : $AnyObject to $C, bb6, bb7 + checked_cast_br %1 : $AnyObject to C, bb6, bb7 bb6(%16 : $C): %17 = enum $Optional, #Optional.some!enumelt.1, %16 : $C diff --git a/test/SILOptimizer/devirt_jump_thread_crasher.sil b/test/SILOptimizer/devirt_jump_thread_crasher.sil index ce0feea0da4d9..04dbaf396dc1b 100644 --- a/test/SILOptimizer/devirt_jump_thread_crasher.sil +++ b/test/SILOptimizer/devirt_jump_thread_crasher.sil @@ -33,7 +33,7 @@ sil @method2 : $@convention(method) <τ_0_0> (@owned @callee_owned (S) -> @out sil [serialized] @_TFCs28__ContiguousArrayStorageBase25withUnsafeBufferOfObjectsfS_U__FFGVs19UnsafeBufferPointerPs9AnyObject__Q_Q_ : $@convention(method) (@owned @callee_owned (S) -> @out R, @guaranteed Base) -> @out R { bb0(%0 : $*R, %1 : $@callee_owned (S) -> @out R, %2 : $Base): %3 = alloc_stack $MyOptional // users: %9, %12, %13, %15, %20, %26, %33, %42, %50, %54 - checked_cast_br [exact] %2 : $Base to $Derived1, bb1, bb2 // id: %4 + checked_cast_br [exact] %2 : $Base to Derived1, bb1, bb2 // id: %4 bb1(%5 : $Derived1): // Preds: bb0 %6 = function_ref @method1 : $@convention(method) <τ_0_0> (@owned @callee_owned (S) -> @out τ_0_0, @guaranteed Derived1) -> @out MyOptional<τ_0_0> // user: %9 @@ -43,7 +43,7 @@ bb1(%5 : $Derived1): // Preds: bb0 br bb3 // id: %10 bb2: // Preds: bb0 - checked_cast_br [exact] %2 : $Base to $Derived2, bb6, bb7 // id: %11 + checked_cast_br [exact] %2 : $Base to Derived2, bb6, bb7 // id: %11 bb3: // Preds: bb1 bb6 bb8 bb10 switch_enum_addr %3 : $*MyOptional, case #MyOptional.some!enumelt.1: bb4, case #MyOptional.none!enumelt: bb5 // id: %12 @@ -67,7 +67,7 @@ bb6(%22 : $Derived2): // Preds: bb2 br bb3 // id: %27 bb7: // Preds: bb2 - checked_cast_br [exact] %2 : $Base to $Base, bb8, bb9 // id: %28 + checked_cast_br [exact] %2 : $Base to Base, bb8, bb9 // id: %28 bb8(%29 : $Base): // Preds: bb7 %30 = function_ref @method2 : $@convention(method) <τ_0_0> (@owned @callee_owned (S) -> @out τ_0_0, @guaranteed Base) -> @out MyOptional<τ_0_0> // user: %33 @@ -78,7 +78,7 @@ bb8(%29 : $Base): // Preds: bb7 bb9: // Preds: bb7 strong_retain %1 : $@callee_owned (S) -> @out R // id: %35 - checked_cast_br [exact] %2 : $Base to $Derived1, bb11, bb12 // id: %36 + checked_cast_br [exact] %2 : $Base to Derived1, bb11, bb12 // id: %36 bb10(%37 : $()): // Preds: bb11 bb13 br bb3 // id: %38 @@ -90,7 +90,7 @@ bb11(%39 : $Derived1): // Preds: bb9 br bb10(%42 : $()) // id: %43 bb12: // Preds: bb9 - checked_cast_br [exact] %2 : $Base to $Base, bb14, bb15 // id: %44 + checked_cast_br [exact] %2 : $Base to Base, bb14, bb15 // id: %44 bb13(%45 : $()): // Preds: bb14 bb15 br bb10(%45 : $()) // id: %46 @@ -117,19 +117,19 @@ bb15: // Preds: bb12 sil @multiple_edits_in_adjacent_blocks : $@convention(method) (@in Base) -> () { bb0(%0 : $*Base): %1 = load %0 : $*Base - checked_cast_br %1 : $Base to $Derived1, bb8, bb1 + checked_cast_br %1 : $Base to Derived1, bb8, bb1 bb1: - checked_cast_br [exact] %1 : $Base to $Derived2, bb2, bb5 + checked_cast_br [exact] %1 : $Base to Derived2, bb2, bb5 bb2(%6 : $Derived2): %7 = upcast %6 : $Derived2 to $Base debug_value %7 : $Base, let, name "self", argno 1 - checked_cast_br [exact] %7 : $Base to $Base, bb3, bb6 + checked_cast_br [exact] %7 : $Base to Base, bb3, bb6 bb3(%10 : $Base): debug_value %10 : $Base, let, name "self", argno 1 - checked_cast_br %10 : $Base to $Derived1, bb4, bb7 + checked_cast_br %10 : $Base to Derived1, bb4, bb7 bb4(%13 : $Derived1): debug_value %13 : $Derived1, let, name "scr" diff --git a/test/SILOptimizer/devirt_opaque_witness.swift b/test/SILOptimizer/devirt_opaque_witness.swift index 5f8b992882b85..debd3c80b9e9e 100644 --- a/test/SILOptimizer/devirt_opaque_witness.swift +++ b/test/SILOptimizer/devirt_opaque_witness.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -emit-module-path %t/opaque_conformance.swiftmodule -primary-file %S/Inputs/opaque_conformance.swift -// RUN: %target-swift-frontend -O -emit-sil -primary-file %s -I %t | %FileCheck %s +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s -I %t -Xllvm -sil-disable-pass=late-deadfuncelim | %FileCheck %s import opaque_conformance diff --git a/test/SILOptimizer/devirt_outer_requirements.swift b/test/SILOptimizer/devirt_outer_requirements.swift new file mode 100644 index 0000000000000..8cfba6a2c0ddd --- /dev/null +++ b/test/SILOptimizer/devirt_outer_requirements.swift @@ -0,0 +1,42 @@ + +// RUN: %target-swift-frontend -emit-sil -O %s | %FileCheck %s + +public protocol P {} + +public class C1 { + // CHECK-LABEL: sil @$s25devirt_outer_requirements2C1C3fooyyqd__AA1PRzlF : $@convention(method) (@in_guaranteed V, @guaranteed C1) -> () { + // CHECK: function_ref @$s25devirt_outer_requirements2C1C3baryyqd__AA1PRzlF : $@convention(method) <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @guaranteed C1<τ_0_0>) -> () + // CHECK: return + public func foo(_ z: V) where U : P { + bar(z) + } + + @_optimize(none) + public func bar(_ z: V) where U : P {} +} + +public class Base { + public func foo(_: V) where T : P {} +} + +public protocol Q {} + +public struct Foo {} +extension Foo : P where T : Q {} + +public class Derived : Base> { + public override func foo(_: V) where T : Q {} +} + +@_transparent +public func takesBase(_ b: Base, _ v: V) where T : P { + b.foo(v) +} + +// CHECK-LABEL: sil @$s25devirt_outer_requirements12takesDerivedyyAA0E0CyxG_q_tAA1QRzr0_lF : $@convention(thin) (@guaranteed Derived, @in_guaranteed V) -> () { +public func takesDerived(_ d: Derived, _ v: V) where T : Q { + // CHECK: function_ref @$s25devirt_outer_requirements12takesDerivedyyAA0E0CyxG_q_tAA1QRzr0_lF : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Q> (@guaranteed Derived<τ_0_0>, @in_guaranteed τ_0_1) -> () + takesDerived(d, v) + + // CHECK: return +} diff --git a/test/SILOptimizer/devirt_override.sil b/test/SILOptimizer/devirt_override.sil index 4e4223d47953a..ec92ee8ed59c9 100644 --- a/test/SILOptimizer/devirt_override.sil +++ b/test/SILOptimizer/devirt_override.sil @@ -114,7 +114,7 @@ sil @_TF1b3fooFT_T_ : $@convention(thin) () -> () { bb0: %0 = alloc_ref $E %1 = upcast %0 : $E to $C - %2 = unconditional_checked_cast %1 : $C to $D + %2 = unconditional_checked_cast %1 : $C to D %3 = class_method %2 : $D, #D.doIt!1 : (D) -> () -> (), $@convention(method) (@guaranteed D) -> () %4 = apply %3(%2) : $@convention(method) (@guaranteed D) -> () %5 = tuple () diff --git a/test/SILOptimizer/devirt_override_ownership.sil b/test/SILOptimizer/devirt_override_ownership.sil index 04cead7feb871..86af599081a8e 100644 --- a/test/SILOptimizer/devirt_override_ownership.sil +++ b/test/SILOptimizer/devirt_override_ownership.sil @@ -115,7 +115,7 @@ sil [ossa] @_TF1b3fooFT_T_ : $@convention(thin) () -> () { bb0: %0 = alloc_ref $E %1 = upcast %0 : $E to $C - %2 = unconditional_checked_cast %1 : $C to $D + %2 = unconditional_checked_cast %1 : $C to D %3 = class_method %2 : $D, #D.doIt!1 : (D) -> () -> (), $@convention(method) (@guaranteed D) -> () %4 = apply %3(%2) : $@convention(method) (@guaranteed D) -> () destroy_value %2 : $D diff --git a/test/SILOptimizer/devirt_protocol_method_invocations.swift b/test/SILOptimizer/devirt_protocol_method_invocations.swift index 1cddbd37c749c..3741185505380 100644 --- a/test/SILOptimizer/devirt_protocol_method_invocations.swift +++ b/test/SILOptimizer/devirt_protocol_method_invocations.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_protocol_method_invocations -O -Xllvm -sil-disable-pass=ExistentialSpecializer -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_protocol_method_invocations -enable-spec-devirt -O -Xllvm -sil-disable-pass=ExistentialSpecializer -emit-sil %s | %FileCheck %s protocol PPP { func f() diff --git a/test/SILOptimizer/devirt_speculate.swift b/test/SILOptimizer/devirt_speculate.swift index 9bea10949385d..7167716153be1 100644 --- a/test/SILOptimizer/devirt_speculate.swift +++ b/test/SILOptimizer/devirt_speculate.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %/s -parse-as-library -O -emit-sil -save-optimization-record-path %t.opt.yaml | %FileCheck %s +// RUN: %target-swift-frontend %/s -parse-as-library -enable-spec-devirt -O -emit-sil -save-optimization-record-path %t.opt.yaml | %FileCheck %s // RUN: %FileCheck -check-prefix=YAML -input-file=%t.opt.yaml %s // RUN: %target-swift-frontend %/s -parse-as-library -Osize -emit-sil | %FileCheck %s --check-prefix=OSIZE // @@ -10,44 +10,96 @@ public class Base { public init() {} public func foo() {} } + +@_optimize(none) +func blackHole(_: T) {} + class Sub1 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub2 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub3 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub4 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub5 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub6 : Base { - override func foo() {} + override func foo() { blackHole(self) } } class Sub7 : Base { - override func foo() {} + override func foo() { blackHole(self) } } // CHECK: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// CHECK: checked_cast_br [exact] %0 : $Base to $Base -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub1 -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub2 -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub3 -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub4 -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub5 -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub6 +// CHECK: bb0(%0 : $Base): +// CHECK: checked_cast_br [exact] %0 : $Base to Base, bb2, bb3 + +// CHECK: bb2([[CASTED:%.*]]): +// CHECK: br bb1 + +// CHECK: bb3: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub1, bb4, bb5 + +// CHECK: bb4([[CASTED:%.*]] : $Sub1): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb5: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub2, bb6, bb7 + +// CHECK: bb6([[CASTED:%.*]] : $Sub2): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb7: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub3, bb8, bb9 + +// CHECK: bb8([[CASTED:%.*]] : $Sub3): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb9: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub4, bb10, bb11 + +// CHECK: bb10([[CASTED:%.*]] : $Sub4): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb11: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub5, bb12, bb13 + +// CHECK: bb12([[CASTED:%.*]] : $Sub5): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb13: +// CHECK: checked_cast_br [exact] %0 : $Base to Sub6, bb14, bb15 + +// CHECK: bb14([[CASTED:%.*]] : $Sub6): +// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> () +// CHECK: apply [[FN]]([[CASTED]]) +// CHECK: br bb1 + +// CHECK: bb15: // CHECK-NOT: checked_cast_br -// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () +// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () +// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> () // YAML: Pass: sil-speculative-devirtualizer // YAML-NEXT: Name: sil.PartialSpecDevirt // YAML-NEXT: DebugLoc: // YAML-NEXT: File: {{.*}}/devirt_speculate.swift -// YAML-NEXT: Line: 66 +// YAML-NEXT: Line: 118 // YAML-NEXT: Column: 5 // YAML-NEXT: Function: 'testMaxNumSpeculativeTargets(_:)' // YAML-NEXT: Args: @@ -60,8 +112,8 @@ class Sub7 : Base { // YAML-NEXT: ... // OSIZE: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF -// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to $Base -// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to $Sub +// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to Base +// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to Sub public func testMaxNumSpeculativeTargets(_ b: Base) { b.foo() } diff --git a/test/SILOptimizer/devirt_speculative.sil b/test/SILOptimizer/devirt_speculative.sil index 0e3198119a918..cac316601dc3c 100644 --- a/test/SILOptimizer/devirt_speculative.sil +++ b/test/SILOptimizer/devirt_speculative.sil @@ -58,9 +58,9 @@ bb0(%0: $Base): // CHECK-LABEL: sil @test_objc_ancestry // CHECK: bb0 // CHECK: [[METH:%.*]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] %0 : $Base to $Base, bb{{.*}}, bb[[CHECK2:[0-9]+]] +// CHECK: checked_cast_br [exact] %0 : $Base to Base, bb{{.*}}, bb[[CHECK2:[0-9]+]] // CHECK: bb[[CHECK2]]{{.*}}: -// CHECK: checked_cast_br [exact] %0 : $Base to $Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]] +// CHECK: checked_cast_br [exact] %0 : $Base to Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]] // CHECK: bb[[GENCALL]]{{.*}}: // CHECK: apply [[METH]] @@ -111,9 +111,9 @@ bb0(%0: $Base2): // CHECK-LABEL: sil @test_objc_ancestry2 // CHECK: bb0 // CHECK: [[METH:%.*]] = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () -// CHECK: checked_cast_br [exact] %0 : $Base2 to $Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]] +// CHECK: checked_cast_br [exact] %0 : $Base2 to Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]] // CHECK: bb[[CHECK2]]{{.*}}: -// CHECK: checked_cast_br [exact] %0 : $Base2 to $Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]] +// CHECK: checked_cast_br [exact] %0 : $Base2 to Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]] // CHECK: bb[[GENCALL]]{{.*}}: // CHECK: apply [[METH]] @@ -145,7 +145,6 @@ bb0(%0: $Base2): // CHECK-NEXT: unreachable // CHECK: checked_cast_br // CHECK: function_ref @Sub_exit -// CHECK-NEXT: unchecked_ref_cast // CHECK-NEXT: apply // CHECK-NEXT: unreachable sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { @@ -186,7 +185,7 @@ bb0(%0 : $S2): } // CHECK-LABEL: sil{{.*}} @test_devirt_of_throw_without_error -// CHECK-NOT: checked_cast_br [exact] %0 : $Throw to $S1 +// CHECK-NOT: checked_cast_br [exact] %0 : $Throw to S1 // CHECK: return sil hidden [noinline] @test_devirt_of_throw_without_error : $@convention(thin) (@owned Throw) -> () { diff --git a/test/SILOptimizer/devirt_speculative_init.swift b/test/SILOptimizer/devirt_speculative_init.swift index 9894a2aefa640..7f126ca1cea59 100644 --- a/test/SILOptimizer/devirt_speculative_init.swift +++ b/test/SILOptimizer/devirt_speculative_init.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend %s -parse-as-library -O -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend %s -parse-as-library -enable-spec-devirt -O -emit-sil | %FileCheck %s // RUN: %target-swift-frontend %s -parse-as-library -Osize -emit-sil // // Test speculative devirtualization. @@ -23,7 +23,7 @@ public func make(type: Cat.Type, cats: Int) { } // CHECK-LABEL: sil @$s23devirt_speculative_init4make4type4catsyAA3CatCm_SitF : $@convention(thin) (@thick Cat.Type, Int) -> () -// CHECK: checked_cast_br [exact] %0 : $@thick Cat.Type to $@thick Cat.Type, bb2, bb3 +// CHECK: checked_cast_br [exact] %0 : $@thick Cat.Type to @thick Cat.Type, bb2, bb3 // CHECK: bb1: // CHECK: return // CHECK: bb2({{%.*}} : $@thick Cat.Type): @@ -31,4 +31,4 @@ public func make(type: Cat.Type, cats: Int) { // CHECK: br bb1 // CHECK: bb3: // CHECK: alloc_ref $BigCat -// CHECK: br bb1 \ No newline at end of file +// CHECK: br bb1 diff --git a/test/SILOptimizer/devirt_speculative_nested.swift b/test/SILOptimizer/devirt_speculative_nested.swift index eb2f4691aaee4..5dc8d089c6ecf 100644 --- a/test/SILOptimizer/devirt_speculative_nested.swift +++ b/test/SILOptimizer/devirt_speculative_nested.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_speculative_nested %s -parse-as-library -O -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_speculative_nested %s -parse-as-library -enable-spec-devirt -O -emit-sil | %FileCheck %s // RUN: %target-swift-frontend -module-name devirt_speculative_nested %s -parse-as-library -Osize -emit-sil // // Test speculative devirtualization. @@ -22,7 +22,7 @@ public class Base { // But at least, we shouldn't crash. // CHECK-LABEL: sil @$s25devirt_speculative_nested3foo1xyAA4BaseC_tF : $@convention(thin) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] %0 : $Base to $Base +// CHECK: checked_cast_br [exact] %0 : $Base to Base // CHECK: function_ref @$s25devirt_speculative_nested4BaseC6updateyyF // CHECK: class_method %0 : $Base, #Base.update!1 // CHECK: return diff --git a/test/SILOptimizer/devirt_unbound_generic.swift b/test/SILOptimizer/devirt_unbound_generic.swift index a510e8f6283e6..67427ef9223ca 100644 --- a/test/SILOptimizer/devirt_unbound_generic.swift +++ b/test/SILOptimizer/devirt_unbound_generic.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -Xllvm -sil-inline-generics -emit-sorted-sil -emit-sil -O %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-inline-generics -emit-sorted-sil -emit-sil -enable-spec-devirt -O %s | %FileCheck %s // We used to crash on this when trying to devirtualize t.boo(a, 1), // because it is an "apply" with replacement types that contain @@ -44,7 +44,7 @@ class Derived : Base { // Check that testDevirt is specialized and uses speculative devirtualization. // CHECK-LABEL: sil shared [noinline] @{{.*}}testDevirt -// CHECK: checked_cast_br [exact] %{{.*}} : $CC to $CC +// CHECK: checked_cast_br [exact] %{{.*}} : $CC to CC // CHECK: class_method // CHECK: } @inline(never) diff --git a/test/SILOptimizer/devirt_value_metatypes.swift b/test/SILOptimizer/devirt_value_metatypes.swift index 215ebfe9fc4d8..23dd0e226376b 100644 --- a/test/SILOptimizer/devirt_value_metatypes.swift +++ b/test/SILOptimizer/devirt_value_metatypes.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name devirt_value_metatypes -emit-sil -O %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name devirt_value_metatypes -emit-sil -enable-spec-devirt -O %s | %FileCheck %s open class A { @inline(never) diff --git a/test/SILOptimizer/devirtualize2.sil b/test/SILOptimizer/devirtualize2.sil index 35b7520fe33c5..c6cc5cf3a573b 100644 --- a/test/SILOptimizer/devirtualize2.sil +++ b/test/SILOptimizer/devirtualize2.sil @@ -35,7 +35,7 @@ bb0: %1 = alloc_ref $Foo // users: %5, %3, %2 strong_retain %1 : $Foo // id: %2 %3 = upcast %1 : $Foo to $Bar // user: %4 - %4 = unconditional_checked_cast %3 : $Bar to $Foo // user: %6 + %4 = unconditional_checked_cast %3 : $Bar to Foo // user: %6 strong_release %1 : $Foo // id: %5 %6 = upcast %4 : $Foo to $Bar // user: %7 store %6 to %0 : $*Bar // id: %7 @@ -55,7 +55,7 @@ bb0: // CHECK: return sil @function_with_cm_with_dynamic_type_known_from_checked_cast_br : $@convention(thin) (Bar) -> () { bb0(%0 : $Bar): - checked_cast_br [exact] %0 : $Bar to $Bar, bb1, bb5 + checked_cast_br [exact] %0 : $Bar to Bar, bb1, bb5 bb1(%1 : $Bar): br bb2(%1 : $Bar) diff --git a/test/SILOptimizer/devirtualize2_ownership.sil b/test/SILOptimizer/devirtualize2_ownership.sil index 9a7ad5d9106b2..e213c84ce556e 100644 --- a/test/SILOptimizer/devirtualize2_ownership.sil +++ b/test/SILOptimizer/devirtualize2_ownership.sil @@ -36,7 +36,7 @@ bb0: %1 = alloc_ref $Foo // users: %5, %3, %2 %2 = copy_value %1 : $Foo // id: %2 %3 = upcast %2 : $Foo to $Bar // user: %4 - %4 = unconditional_checked_cast %3 : $Bar to $Foo // user: %6 + %4 = unconditional_checked_cast %3 : $Bar to Foo // user: %6 destroy_value %1 : $Foo // id: %5 %6 = upcast %4 : $Foo to $Bar // user: %7 %10 = class_method %6 : $Bar, #Bar.ping!1 : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %11 @@ -55,7 +55,7 @@ bb0: // CHECK: } // end sil function 'function_with_cm_with_dynamic_type_known_from_checked_cast_br' sil [ossa] @function_with_cm_with_dynamic_type_known_from_checked_cast_br : $@convention(thin) (@owned Bar) -> () { bb0(%0 : @owned $Bar): - checked_cast_br [exact] %0 : $Bar to $Bar, bb1, bb5 + checked_cast_br [exact] %0 : $Bar to Bar, bb1, bb5 bb1(%1 : @owned $Bar): br bb2(%1 : $Bar) diff --git a/test/SILOptimizer/di-conditional-destroy-scope.swift b/test/SILOptimizer/di-conditional-destroy-scope.swift index 366c0d39399da..e87d8a7da9ed0 100644 --- a/test/SILOptimizer/di-conditional-destroy-scope.swift +++ b/test/SILOptimizer/di-conditional-destroy-scope.swift @@ -6,8 +6,9 @@ // REQUIRES: objc_interop -// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:38:5, scope 2 -// CHECK: destroy_addr [[ADR]] : $*FileSystem, loc {{.*}}:38:5, scope 2 +// CHECK: [[ADR:%.*]] = ref_element_addr %{{.*}} : $RecursibleDirectoryContentsGenerator, #RecursibleDirectoryContentsGenerator.fileSystem, loc {{.*}}:39:5, scope 2 +// CHECK: [[ADR_ACCESS:%.*]] = begin_access [deinit] [static] [[ADR]] +// CHECK: destroy_addr [[ADR_ACCESS]] : $*FileSystem, loc {{.*}}:39:5, scope 2 import Foundation diff --git a/test/SILOptimizer/di_property_wrappers.swift b/test/SILOptimizer/di_property_wrappers.swift index 46e53376afd15..698e5d1c98a69 100644 --- a/test/SILOptimizer/di_property_wrappers.swift +++ b/test/SILOptimizer/di_property_wrappers.swift @@ -474,6 +474,34 @@ func testWrapperInitWithDefaultArg() { // CHECK-NEXT: Init } +// rdar://problem/57350503 - DI failure due to an unnecessary cycle breaker +public class Test57350503 { + @Synchronized + public private(set) var anchor: Int + + public init(anchor: Int) { + self.anchor = anchor + printMe() + } + + private func printMe() { } +} + +@propertyWrapper +public final class Synchronized { + private var value: Value + + public var wrappedValue: Value { + get { value } + set { value = newValue } + } + + public init(wrappedValue: Value) { + value = wrappedValue + } +} + + testIntStruct() testIntClass() testRefStruct() diff --git a/test/SILOptimizer/di_property_wrappers_errors.swift b/test/SILOptimizer/di_property_wrappers_errors.swift index e1422c0c215ea..a89cb0cfccd91 100644 --- a/test/SILOptimizer/di_property_wrappers_errors.swift +++ b/test/SILOptimizer/di_property_wrappers_errors.swift @@ -1,4 +1,6 @@ // RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-frontend -emit-sil -verify %s -enable-ownership-stripping-after-serialization + @propertyWrapper final class ClassWrapper { var wrappedValue: T { diff --git a/test/SILOptimizer/diagnose_unreachable.sil b/test/SILOptimizer/diagnose_unreachable.sil index 391578752fcb4..7045ca9e368ab 100644 --- a/test/SILOptimizer/diagnose_unreachable.sil +++ b/test/SILOptimizer/diagnose_unreachable.sil @@ -471,7 +471,9 @@ class Klass2 {} // CHECK-LABEL: sil [ossa] @constant_fold_diagnose_unreachable_succ_match : $@convention(thin) (@owned Klass1) -> () { // CHECK: bb0([[ARG:%.*]] : -// CHECK: destroy_value [[ARG]] +// CHECK: [[ENUM_VAL:%.*]] = enum $Either, #Either.left!enumelt.1, [[ARG]] +// CHECK: [[RESULT:%.*]] = unchecked_enum_data [[ENUM_VAL]] +// CHECK: destroy_value [[RESULT]] // CHECK: } // end sil function 'constant_fold_diagnose_unreachable_succ_match' sil [ossa] @constant_fold_diagnose_unreachable_succ_match : $@convention(thin) (@owned Klass1) -> () { bb0(%0 : @owned $Klass1): @@ -760,3 +762,53 @@ bb2: %9999 = tuple() return %9999 : $() } + +// Just make sure we apply the optimization and do not trigger the ownership verifier +// +// CHECK-LABEL: sil [ossa] @constant_fold_switch_enum_with_owned_payload : +// CHECK: bb0( +// CHECK: br bb1 +// CHECK: bb1: +// CHECK: return +// CHECK: } // end sil function 'constant_fold_switch_enum_with_owned_payload' +sil [ossa] @constant_fold_switch_enum_with_owned_payload : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = enum $FakeOptional, #FakeOptional.some!enumelt.1, %0 : $Builtin.NativeObject + switch_enum %1 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 + +bb1(%2 : @owned $Builtin.NativeObject): + return %2 : $Builtin.NativeObject + +bb2: + unreachable +} + +// Test propagation of guaranteed phi arguments. The nested end_borrow +// must be removed, even with the outer borrow is *not* a function +// argument. + +enum EnumWithB { + case A(B) + func testit() -> Int +} + +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK: bb1([[PHI:%.*]] : @guaranteed $B): +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: end_borrow [[PHI]] : $B +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' +sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +bb0(%0 : @guaranteed $EnumWithB): + switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 + +bb1(%2 : @guaranteed $B): + br bb3(%2 : $B) + +bb3(%4 : @guaranteed $B): + end_borrow %4 : $B + end_borrow %2 : $B + %99 = tuple () + return %99 : $() +} diff --git a/test/SILOptimizer/diagnostic_constant_propagation-swift4.swift b/test/SILOptimizer/diagnostic_constant_propagation-swift4.swift index adeac2ea65dc8..87033ad5f3e2b 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation-swift4.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation-swift4.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // These are tests for diagnostics produced by constant propagation pass. // These are specific to Swift 4. diff --git a/test/SILOptimizer/diagnostic_constant_propagation_floats.swift b/test/SILOptimizer/diagnostic_constant_propagation_floats.swift index 5f708afa63292..c2befb634ce70 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_floats.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_floats.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // These are tests for diagnostics produced by constant propagation pass // on floating-point operations. diff --git a/test/SILOptimizer/diagnostic_constant_propagation_floats_nonx86.swift b/test/SILOptimizer/diagnostic_constant_propagation_floats_nonx86.swift index 4000223c36833..ecc8d4b6417ea 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_floats_nonx86.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_floats_nonx86.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // REQUIRES: !(CPU=i386 || CPU=x86_64) // diff --git a/test/SILOptimizer/diagnostic_constant_propagation_floats_x86.swift b/test/SILOptimizer/diagnostic_constant_propagation_floats_x86.swift index 04a4fe3906f25..29634e0bd2bc8 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_floats_x86.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_floats_x86.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // REQUIRES: CPU=i386 || CPU=x86_64 // UNSUPPORTED: OS=windows-msvc diff --git a/test/SILOptimizer/diagnostic_constant_propagation_int_arch32.swift b/test/SILOptimizer/diagnostic_constant_propagation_int_arch32.swift index a4ef070074536..aa53d4215cccc 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_int_arch32.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_int_arch32.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // REQUIRES: PTRSIZE=32 // diff --git a/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift b/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift index c77b5fe3c4480..38104f2455167 100644 --- a/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift +++ b/test/SILOptimizer/diagnostic_constant_propagation_int_arch64.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // REQUIRES: PTRSIZE=64 // diff --git a/test/SILOptimizer/dynamic_self_cast.sil b/test/SILOptimizer/dynamic_self_cast.sil new file mode 100644 index 0000000000000..9a538565f16df --- /dev/null +++ b/test/SILOptimizer/dynamic_self_cast.sil @@ -0,0 +1,46 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -O %s | %FileCheck %s + +sil_stage raw + +import Swift + +public class SelfCasts { +} + +// CHECK-LABEL: sil @$dynamic_self_cast_1 : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional +sil @$dynamic_self_cast_1 : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional { +bb0(%0 : $SelfCasts, %1 : $@thick SelfCasts.Type): + strong_retain %0 : $SelfCasts + // CHECK: checked_cast_br + checked_cast_br %0 : $SelfCasts to @dynamic_self SelfCasts, bb1, bb2 +bb1(%7 : $SelfCasts): + %8 = enum $Optional, #Optional.some!enumelt.1, %7 : $SelfCasts + br bb3(%8 : $Optional) + +bb2: + strong_release %0 : $SelfCasts + %11 = enum $Optional, #Optional.none!enumelt + br bb3(%11 : $Optional) + +bb3(%13 : $Optional): + return %13 : $Optional +} + +// CHECK-LABEL: sil @$dynamic_self_cast_2 : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional +sil @$dynamic_self_cast_2 : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional { +bb0(%0 : $SelfCasts, %1 : $@thick SelfCasts.Type): + strong_retain %0 : $SelfCasts + // CHECK-NOT: checked_cast_br + checked_cast_br %0 : $SelfCasts to SelfCasts, bb1, bb2 +bb1(%7 : $SelfCasts): + %8 = enum $Optional, #Optional.some!enumelt.1, %7 : $SelfCasts + br bb3(%8 : $Optional) + +bb2: + strong_release %0 : $SelfCasts + %11 = enum $Optional, #Optional.none!enumelt + br bb3(%11 : $Optional) + +bb3(%13 : $Optional): + return %13 : $Optional +} diff --git a/test/SILOptimizer/eager_specialize.sil b/test/SILOptimizer/eager_specialize.sil index f1ae3568b137a..e020ad4660ee2 100644 --- a/test/SILOptimizer/eager_specialize.sil +++ b/test/SILOptimizer/eager_specialize.sil @@ -228,7 +228,6 @@ bb2: // CHECK: try_apply %{{.*}}(%{{.*}}, %{{.*}}) : $@convention(thin) (Int, Int) -> (Int, @error Error), normal bb8, error bb7 // CHECK: bb7(%{{.*}} : $Error): -// CHECK: %{{.*}} = builtin "willThrow"(%{{.*}} : $Error) : $() // CHECK: br bb3(%{{.*}} : $Error) // CHECK: bb8(%{{.*}} : $Int): diff --git a/test/SILOptimizer/earlycodemotion.sil b/test/SILOptimizer/earlycodemotion.sil index c860618bf11b8..9a1a06c153b5c 100644 --- a/test/SILOptimizer/earlycodemotion.sil +++ b/test/SILOptimizer/earlycodemotion.sil @@ -548,7 +548,7 @@ bb1: strong_retain %17 : $Test %19 = class_method %17 : $Test, #Test.testing!1 : (Test) -> () -> UInt64, $@convention(method) (@guaranteed Test) -> UInt64 %20 = load %7 : $*UInt64 - checked_cast_br [exact] %17 : $Test to $Test, bb3, bb4 + checked_cast_br [exact] %17 : $Test to Test, bb3, bb4 bb2: // CHECK: bb2: // CHECK-NOT: strong_retain @@ -1262,7 +1262,7 @@ bb0(%0 : $Optional): bb1: // Preds: bb0 %3 = unchecked_enum_data %0 : $Optional, #Optional.some!enumelt.1 // users: %4, %13, %14, %15 - checked_cast_br [exact] %3 : $X to $X, bb4, bb5 // id: %4 + checked_cast_br [exact] %3 : $X to X, bb4, bb5 // id: %4 // Make sure we were able to sink the release. // CHECK: strong_release @@ -1305,13 +1305,13 @@ bb5: // Preds: bb1 sil @canonicalize_casts: $@convention(thin) (@owned X) -> Int32 { bb0(%0 : $X): debug_value %0 : $X, let, name "x" // id: %1 - checked_cast_br %0 : $X to $Y, bb1, bb3 // id: %2 + checked_cast_br %0 : $X to Y, bb1, bb3 // id: %2 bb1(%3 : $Y): // Preds: bb0 debug_value %3 : $Y, let, name "z" // id: %4 %5 = upcast %3 : $Y to $X // users: %6, %7, %21, %23 %6 = class_method %5 : $X, #X.ping!1 : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // users: %21, %23 - checked_cast_br [exact] %5 : $X to $Y, bb5, bb6 // id: %7 + checked_cast_br [exact] %5 : $X to Y, bb5, bb6 // id: %7 bb2: // Preds: bb5 bb6 //CHECK: strong_release %0 diff --git a/test/SILOptimizer/empty_collection_count.swift b/test/SILOptimizer/empty_collection_count.swift new file mode 100644 index 0000000000000..5f58344f20d49 --- /dev/null +++ b/test/SILOptimizer/empty_collection_count.swift @@ -0,0 +1,38 @@ +// RUN: %target-swift-frontend -emit-sil -O %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -Osize %s | %FileCheck %s + +// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib + +// This is an end-to-end test if the count and/or capacity from empty +// array/set/dictionary singletons can be propagated. + +// CHECK-LABEL: sil @{{.*}}testArray +// CHECK-NOT: global_addr +// CHECK: [[Z:%[0-9]+]] = integer_literal $Builtin.Int{{[0-9]*}}, 0 +// CHECK: [[I:%[0-9]+]] = struct $Int ([[Z]] : $Builtin.Int{{[0-9]*}}) +// CHECK: return [[I]] +public func testArray() -> Int { + let d = Array() + return d.count +} + +// CHECK-LABEL: sil @{{.*}}testDictionary +// CHECK-NOT: global_addr +// CHECK: [[Z:%[0-9]+]] = integer_literal $Builtin.Int{{[0-9]*}}, 0 +// CHECK: [[I:%[0-9]+]] = struct $Int ([[Z]] : $Builtin.Int{{[0-9]*}}) +// CHECK: return [[I]] +public func testDictionary() -> Int { + let d = Dictionary() + return d.count + d.capacity +} + +// CHECK-LABEL: sil @{{.*}}testSet +// CHECK-NOT: global_addr +// CHECK: [[Z:%[0-9]+]] = integer_literal $Builtin.Int{{[0-9]*}}, 0 +// CHECK: [[I:%[0-9]+]] = struct $Int ([[Z]] : $Builtin.Int{{[0-9]*}}) +// CHECK: return [[I]] +public func testSet() -> Int { + let d = Set() + return d.count + d.capacity +} + diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index 53759dbb463dd..e510fbadcda74 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt %s -escapes-dump -o /dev/null | %FileCheck %s +// RUN: %target-sil-opt %s -escapes-dump -escapes-internal-verify -o /dev/null | %FileCheck %s // REQUIRES: asserts @@ -77,8 +77,8 @@ struct FourFields { sil @take_indirect_tuple : $@convention(method) (@in (Int, ())) -> () // CHECK-LABEL: CG of handle_undef -// CHECK: Val %2 Esc: G, Succ: (%2.1) -// CHECK: Con %2.1 Esc: G, Succ: +// CHECK: Val %2 Esc: , Succ: (%2.1) +// CHECK: Con [ref] %2.1 Esc: G, Succ: // CHECK: End sil @handle_undef : $@convention(thin) (Int) -> () { bb0(%0 : $Int): @@ -95,12 +95,13 @@ bb0(%0 : $Int): // Sanity check with a simple function. // CHECK-LABEL: CG of test_simple -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %2 -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %1 +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%5) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: A, Succ: %1 +// CHECK-NEXT: Con [ref] %5 Esc: A, Succ: %2 // CHECK-NEXT: End sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () { bb0(%0 : $*Y, %1 : $X): @@ -117,12 +118,12 @@ bb0(%0 : $*Y, %1 : $X): // Test if a deferring edge is created for a block argument. // CHECK-LABEL: CG of deferringEdge -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%3.1) -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: %3, Succ: (%3.1), %0 -// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) -// CHECK-NEXT: Con %3.2 Esc: A, Succ: %1 -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%4), %0 +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: A, Succ: %1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode, %1 : $LinkedNode): @@ -137,8 +138,10 @@ bb1(%3 : $LinkedNode): // Test a local object just escaping via return. // CHECK-LABEL: CG of escapes_via_return -// CHECK-NEXT: Val %0 Esc: R, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: R, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: R, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @escapes_via_return : $@convention(thin) () -> @owned X { bb0: @@ -149,14 +152,14 @@ bb0: // A linear chain of assignments is collapsed to a single node. // CHECK-LABEL: CG of test_linked_list -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Val %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%11.1) -// CHECK-NEXT: Val %4 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Val %7 Esc: %11, Succ: (%1.1) -// CHECK-NEXT: Val %11 Esc: %11, Succ: (%1.1), %7, %11.1 -// CHECK-NEXT: Con %11.1 Esc: A, Succ: (%1.1), %0, %1, %4 -// CHECK-NEXT: Ret Esc: R, Succ: %11.1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2) +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%2), %7, %13 +// CHECK-NEXT: Con [ref] %13 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %13 // CHECK-NEXT: End sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -184,14 +187,14 @@ bb2: // The same example as above but distributed over two functions. // CHECK-LABEL: CG of create_chain -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%7.1) -// CHECK-NEXT: Val %1 Esc: A, Succ: (%7.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: %0, %1, %4 -// CHECK-NEXT: Val %4 Esc: A, Succ: (%7.1) -// CHECK-NEXT: Val %7 Esc: %11, Succ: (%7.1) -// CHECK-NEXT: Con %7.1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Val %11 Esc: R, Succ: (%7.1), %1.1 -// CHECK-NEXT: Ret Esc: R, Succ: %11 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %1 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %4 Esc: A, Succ: (%8) +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%8) +// CHECK-NEXT: Con [int] %8 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Con [ref] %8.1 Esc: A, Succ: %0, %1, %4 +// CHECK-NEXT: Val [ref] %11 Esc: , Succ: (%8), %8.1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %11 // CHECK-NEXT: End sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -211,11 +214,11 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of loadNext -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Val %2 Esc: %2, Succ: (%2.1), %0, %2.2 -// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Ret Esc: R, Succ: %2.2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3), %0, %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%3) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -233,15 +236,17 @@ bb2: // Content nodes in the callee are duplicated in the caller. // CHECK-LABEL: CG of call_load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) -// CHECK-NEXT: Con %0.5 Esc: A, Succ: (%0.6) -// CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6 -// CHECK-NEXT: Ret Esc: R, Succ: %2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [int] %0.3 Esc: A, Succ: (%0.4) +// CHECK-NEXT: Con [ref] %0.4 Esc: A, Succ: (%0.5) +// CHECK-NEXT: Con [int] %0.5 Esc: A, Succ: (%0.6) +// CHECK-NEXT: Con [ref] %0.6 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1), %0.6 +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %2 // CHECK-NEXT: End sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -251,14 +256,16 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of load_next3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5) -// CHECK-NEXT: Con %0.5 Esc: A, Succ: (%0.6) -// CHECK-NEXT: Con %0.6 Esc: A, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.6 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: +// CHECK-NEXT: Con [int] %6.1 Esc: A, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -277,10 +284,12 @@ sil_global @global_x : $X // The argument escapes because it is stored to a global variable in the callee. // CHECK-LABEL: CG of call_store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.2 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%5) +// CHECK-NEXT: Con [int] %5 Esc: G, Succ: (%6) +// CHECK-NEXT: Con %6 Esc: G, Succ: +// CHECK-NEXT: Con [int] %6.1 Esc: G, Succ: (%6.2) +// CHECK-NEXT: Con [ref] %6.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -294,9 +303,9 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of store_pointer -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_pointer : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -310,11 +319,11 @@ bb0(%0 : $Pointer): // global variable in the callee. // CHECK-LABEL: CG of store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0.2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%3) +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %4 +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %4 Esc: G, Succ: // CHECK-NEXT: End sil @store_content : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -328,10 +337,12 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of call_store_content -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%5) +// CHECK-NEXT: Con %5 Esc: G, Succ: +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X { bb0(%0 : $Pointer): @@ -345,9 +356,9 @@ bb0(%0 : $Pointer): // CHECK-LABEL: CG of copy_addr_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -358,9 +369,9 @@ bb0(%0: $*Int32, %1: $*Int32): // CHECK-LABEL: CG of copy_addr_take_content // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1.1 // CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %1.1 Esc: A, Succ: // CHECK-NEXT: End sil @copy_addr_take_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -370,10 +381,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of copy_addr_noinit_content -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: // CHECK-NEXT: End sil @copy_addr_noinit_content : $@convention(thin) (@in Int32) -> @out Int32 { bb0(%0: $*Int32, %1: $*Int32): @@ -383,10 +394,10 @@ bb0(%0: $*Int32, %1: $*Int32): } // CHECK-LABEL: CG of call_copy_addr_content -// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1 -// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %3, Succ: +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: %3, Succ: %1.1 +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: %3, Succ: // CHECK-NEXT: End sil @call_copy_addr_content : $@convention(thin) () -> () { %0 = alloc_stack $Int32 @@ -404,14 +415,17 @@ sil @call_copy_addr_content : $@convention(thin) () -> () { // of Y's box _could_ capture Y.x in Y's deinit. // CHECK-LABEL: CG of test_partial_apply -// CHECK-NEXT: Arg %1 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%6.3) -// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: (%6.1) -// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: (%6.1) -// CHECK-NEXT: Con %6.1 Esc: %14,%15,%16,%17, Succ: (%6.2) -// CHECK-NEXT: Con %6.2 Esc: %14,%15,%16,%17, Succ: %2 -// CHECK-NEXT: Con %6.3 Esc: G, Succ: -// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: +// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%1.1), %1 +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%7) +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: (%7) +// CHECK-NEXT: Con [int] %7 Esc: %14,%15,%16,%17, Succ: (%7.1) +// CHECK-NEXT: Con %7.1 Esc: %14,%15,%16,%17, Succ: %2 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %3, %6 // CHECK-NEXT: End sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 { bb0(%0 : $Int64, %1 : $X, %2 : $Y): @@ -434,16 +448,21 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y): } // CHECK-LABEL: CG of closure1 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%1.3) -// CHECK-NEXT: Con %1.3 Esc: G, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.3) -// CHECK-NEXT: Con %2.3 Esc: G, Succ: -// CHECK-NEXT: Val %7 Esc: %8, Succ: %2 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [int] %3.2 Esc: A, Succ: (%3.3) +// CHECK-NEXT: Con %3.3 Esc: A, Succ: (%3.4) +// CHECK-NEXT: Con %3.4 Esc: G, Succ: +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [int] %4.2 Esc: A, Succ: +// CHECK-NEXT: Con %4.3 Esc: A, Succ: (%0.1), %0 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %2 // CHECK-NEXT: End sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } , @owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } ): @@ -458,12 +477,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } , %2 : $<τ_0_0> { var τ_0_0 } // CHECK-LABEL: CG of closure2 -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%1.3) -// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) -// CHECK-NEXT: Con %1.4 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con %0.1 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con %3 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [int] %4 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: %0 // CHECK-NEXT: End sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } ) -> () { bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): @@ -479,11 +499,13 @@ bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } ): // Test partial_apply. The box escapes in the callee. // CHECK-LABEL: CG of test_escaped_box -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: G, Succ: (%1.3) -// CHECK-NEXT: Con %1.3 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con [int] %2.2 Esc: G, Succ: +// CHECK-NEXT: Con %2.3 Esc: G, Succ: +// CHECK-NEXT: Con %2.4 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 { bb0(%0 : $Int64): @@ -502,10 +524,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of let_box_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: G, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [int] %1.2 Esc: G, Succ: (%1.3) +// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -524,9 +548,10 @@ sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> ( // The partial_apply itself escapes and therefore also the box escapes. // CHECK-LABEL: CG of test_escaped_partial_apply -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %6 Esc: G, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () { bb0(%0 : $Int64): @@ -544,10 +569,12 @@ bb0(%0 : $Int64): } // CHECK-LABEL: CG of closure3 -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [int] %1.2 Esc: A, Succ: +// CHECK-NEXT: Con %1.3 Esc: A, Succ: (%1.4) +// CHECK-NEXT: Con %1.4 Esc: G, Succ: // CHECK-NEXT: End sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int64 { bb0(%0 : $<τ_0_0> { var τ_0_0 } ): @@ -564,11 +591,13 @@ sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) sil_global @global_ln : $LinkedNode // CHECK-LABEL: CG of load_next_recursive -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: G, Succ: %0.2 -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: (%4.1), %2 +// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) +// CHECK-NEXT: Con [ref] %4.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -580,12 +609,13 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of let_escape -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0 -// CHECK-NEXT: Val %4 Esc: G, Succ: %0 -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -597,12 +627,12 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of return_same -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Val %3 Esc: G, Succ: (%0.1), %0.2 -// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3 -// CHECK-NEXT: Ret Esc: R, Succ: %5 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1) +// CHECK-NEXT: Val [ref] %3 Esc: G, Succ: (%5.1), %5.2 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3 +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: (%5.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -620,15 +650,15 @@ bb2(%5 : $LinkedNode): // Another recursion test. // CHECK-LABEL: CG of loadNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Val %4 Esc: R, Succ: %0.4 -// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2) -// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1) -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: A, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2.2, %4.2 +// CHECK-NEXT: Con [int] %4.1 Esc: A, Succ: (%4.2) +// CHECK-NEXT: Con [ref] %4.2 Esc: A, Succ: (%4.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -640,14 +670,14 @@ bb0(%0 : $LinkedNode): } // CHECK-LABEL: CG of returnNext2 -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4) -// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Val %3 Esc: R, Succ: (%0.3), %0.4 -// CHECK-NEXT: Val %8 Esc: R, Succ: %0.2, %3 -// CHECK-NEXT: Ret Esc: R, Succ: %8 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5) +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%8.1), %8.2 +// CHECK-NEXT: Con [int] %5 Esc: A, Succ: (%6) +// CHECK-NEXT: Con [ref] %6 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%8.1), %3, %6 +// CHECK-NEXT: Con [int] %8.1 Esc: A, Succ: (%8.2) +// CHECK-NEXT: Con [ref] %8.2 Esc: A, Succ: (%8.1) +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %8 // CHECK-NEXT: End sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -670,12 +700,12 @@ bb3(%8 : $LinkedNode): // A single-cycle recursion test. // CHECK-LABEL: CG of single_cycle_recursion -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Val %5 Esc: R, Succ: (%0.2), %0.1 -// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5 -// CHECK-NEXT: Ret Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: (%2) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%2), %3 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%2), %0, %5 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode { bb0(%0 : $LinkedNode): @@ -695,9 +725,11 @@ bb2(%5 : $LinkedNode): // Test if a try_apply is represented correctly in the connection graph. // CHECK-LABEL: CG of call_throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: R, Succ: %0 -// CHECK-NEXT: Ret Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%0.1), %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -712,10 +744,12 @@ bb2(%5 : $Error): } // CHECK-LABEL: CG of throwing_func -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1) -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -735,11 +769,13 @@ bb2: // Test if a try_apply to an unknown function is handled correctly. // CHECK-LABEL: CG of call_unknown_throwing_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: G, Succ: -// CHECK-NEXT: Con %3.1 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %3 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) { bb0(%0 : $X): @@ -758,12 +794,14 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error // Test that the deinit of a box itself does not capture anything. // CHECK-LABEL: CG of test_release_of_partial_apply_with_box -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.3) -// CHECK-NEXT: Val %1 Esc: %6, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: %6, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: %6, Succ: %0 -// CHECK-NEXT: Con %1.3 Esc: G, Succ: -// CHECK-NEXT: Val %5 Esc: %6, Succ: %1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: %6, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: %6, Succ: %0 +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %1 // CHECK-NEXT: End sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () { bb0(%0 : $Y): @@ -782,10 +820,10 @@ sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> () // Test is an unknown value is merged correctly into the caller graph. // CHECK-LABEL: CG of store_to_unknown_reference -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -798,9 +836,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of get_reference -// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %1 +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: G, Succ: + // CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 // CHECK-NEXT: End sil @get_reference : $@convention(thin) () -> @owned Y { bb0: @@ -816,9 +855,11 @@ sil @unknown_get_reference : $@convention(thin) () -> @owned Y sil @unknown_set_y : $@convention(thin) () -> @out Y // CHECK-LABEL: CG of get_y -// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0.1 +// CHECK-NEXT: Val %0 Esc: , Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: G, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: G, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %3 // CHECK-NEXT: End sil @get_y : $@convention(thin) () -> @owned Y { bb0: @@ -831,10 +872,10 @@ bb0: } // CHECK-LABEL: CG of create_and_store_x -// CHECK-NEXT: Val %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2) -// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0 +// CHECK-NEXT: Val [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: G, Succ: (%3) +// CHECK-NEXT: Con [int] %3 Esc: G, Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @create_and_store_x : $@convention(thin) () -> () { bb0: @@ -850,10 +891,13 @@ bb0: // Test types which are considered as pointers. // CHECK-LABEL: CG of pointer_types -// CHECK-NEXT: Arg %0 Esc: A, Succ: %1 -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Val %7 Esc: R, Succ: %0 -// CHECK-NEXT: Ret Esc: R, Succ: %7 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%7.1), %4 +// CHECK-NEXT: Con [int] %7.1 Esc: A, Succ: (%7.2) +// CHECK-NEXT: Con [ref] %7.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 // CHECK-NEXT: End sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y { bb0(%0 : $Y, %1 : $Y): @@ -870,12 +914,12 @@ bb1(%7 : $(Pointer, Pointer)): } // CHECK-LABEL: CG of defer_edge_cycle -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1 -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%0.2), %0.1 +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%4) +// CHECK-NEXT: Con [ref] %2 Esc: A, Succ: (%6), %4 +// CHECK-NEXT: Con [ref] %4 Esc: A, Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: A, Succ: (%7) +// CHECK-NEXT: Con [ref] %7 Esc: A, Succ: // CHECK-NEXT: End sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () { entry(%0 : $*Y, %1 : $*Y): @@ -890,9 +934,10 @@ entry(%0 : $*Y, %1 : $*Y): } // CHECK-LABEL: CG of take_c_func -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: G, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () { bb0(%0 : $@convention(c) () -> ()): @@ -909,8 +954,9 @@ bb0: } // CHECK-LABEL: CG of pass_c_func -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: +// CHECK-NEXT: Con %2.2 Esc: G, Succ: // CHECK-NEXT: End sil @pass_c_func : $@convention(thin) () -> () { bb0: @@ -923,12 +969,14 @@ bb0: // CHECK-LABEL: CG of test_select_enum -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -938,11 +986,13 @@ bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X): // CHECK-LABEL: CG of test_select_enum_addr // CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: (%3.1) +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4 // CHECK-NEXT: End sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): @@ -951,11 +1001,13 @@ bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_select_value -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: A, Succ: -// CHECK-NEXT: Arg %3 Esc: A, Succ: -// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3 -// CHECK-NEXT: Ret Esc: R, Succ: %6 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Con [int] %3.1 Esc: A, Succ: (%3.2) +// CHECK-NEXT: Con [ref] %3.2 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %6 Esc: , Succ: %1, %2, %3 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %6 // CHECK-NEXT: End sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X { bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): @@ -966,10 +1018,10 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X): } // CHECK-LABEL: CG of test_existential_addr -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: , Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: , Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%2) +// CHECK-NEXT: Con %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: , Succ: %0 // CHECK-NEXT: End sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () { bb0(%0 : $Pointer): @@ -984,7 +1036,7 @@ bb0(%0 : $Pointer): } // CHECK-LABEL: CG of test_existential_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @test_existential_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -999,9 +1051,9 @@ bb0(%0 : $X): // Check that we don't crash on this. // CHECK-LABEL: CG of test_unknown_store -// CHECK-NEXT: Arg %0 Esc: G, Succ: -// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %0 // CHECK-NEXT: End sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () { bb0(%0 : $ErrorClass): @@ -1013,8 +1065,10 @@ bb0(%0 : $ErrorClass): } // CHECK-LABEL: CG of test_raw_pointer_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X { bb0(%0 : $X): @@ -1024,8 +1078,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of test_bridge_object_to_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X { bb0(%0 : $X, %1 : $Builtin.Word): @@ -1036,8 +1092,8 @@ bb0(%0 : $X, %1 : $Builtin.Word): // CHECK-LABEL: CG of test_address_to_pointer // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1 -// CHECK-NEXT: Arg %1 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: %1 +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: // CHECK-NEXT: End sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X { bb0(%0 : $*X, %1 : $X): @@ -1049,8 +1105,10 @@ bb0(%0 : $*X, %1 : $X): } // CHECK-LABEL: CG of test_casts -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Ret Esc: R, Succ: %0 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %0 // CHECK-NEXT: End sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X { bb0(%0 : $AnyObject): @@ -1073,10 +1131,11 @@ bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type): sil_global @global_y : $SomeData // CHECK-LABEL: CG of test_node_merge_during_struct_inst -// CHECK-NEXT: Arg %0 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Val %1 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Val %4 Esc: G, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: G, Succ: (%4.1), %0, %1, %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%8) +// CHECK-NEXT: Val %1 Esc: G, Succ: (%8) +// CHECK-NEXT: Val %4 Esc: , Succ: (%8) +// CHECK-NEXT: Con [ref] %8 Esc: G, Succ: (%8), %1 +// CHECK-NEXT: Val %10 Esc: , Succ: %0, %4, %8 // CHECK-NEXT: End sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () { bb0(%0 : $Y): @@ -1102,7 +1161,9 @@ bb0(%0 : $Y): } // CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1114,7 +1175,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_subscript -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_subscript : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1131,7 +1194,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_check_index -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_check_index : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1147,10 +1212,10 @@ bb0(%0 : $Array): // CHECK-LABEL: CG of arraysemantics_get_element // CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2 -// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2) -// CHECK-NEXT: Con %1.2 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: A, Succ: %0.1 // CHECK-NEXT: End sil @arraysemantics_get_element : $@convention(thin) (Array) -> @out X { bb0(%io : $*X, %1 : $Array): @@ -1168,6 +1233,7 @@ bb0(%io : $*X, %1 : $Array): // CHECK-LABEL: CG of arraysemantics_make_mutable // CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array) -> () { bb0(%0 : $*Array): @@ -1179,9 +1245,10 @@ bb0(%0 : $*Array): } // CHECK-LABEL: CG of arraysemantics_get_element_address -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: -// CHECK-NEXT: Val %4 Esc: , Succ: %0.1 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2), %4 +// CHECK-NEXT: Con %0.2 Esc: A, Succ: +// CHECK-NEXT: Val %4 Esc: A, Succ: (%0.2) // CHECK-NEXT: End sil @arraysemantics_get_element_address : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1196,7 +1263,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_count -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_count : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1209,7 +1278,9 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_get_capacity -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con %0.2 Esc: A, Succ: // CHECK-NEXT: End sil @arraysemantics_get_capacity : $@convention(thin) (Array) -> () { bb0(%0 : $Array): @@ -1222,13 +1293,15 @@ bb0(%0 : $Array): } // CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer -// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3) -// CHECK-NEXT: Con %0.3 Esc: G, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [int] %0.2 Esc: A, Succ: (%0.3) +// CHECK-NEXT: Con [ref] %0.3 Esc: G, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array, @owned @callee_owned (@inout X) -> (@out (), @error Error)) -> () { bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): @@ -1243,12 +1316,12 @@ bb(%0 : $*Array, %1 : $@callee_owned (@inout X) -> (@out (), @error Error)): } // CHECK-LABEL: CG of arraysemantics_createUninitialized -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Val %2 Esc: R, Succ: (%4.2) -// CHECK-NEXT: Val %4 Esc: R, Succ: (%4.1) -// CHECK-NEXT: Con %4.1 Esc: R, Succ: %2 -// CHECK-NEXT: Con %4.2 Esc: R, Succ: %0 -// CHECK-NEXT: Ret Esc: R, Succ: %4 +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%6) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: %2 +// CHECK-NEXT: Con [int] %6 Esc: R, Succ: (%6.1) +// CHECK-NEXT: Con %6.1 Esc: R, Succ: %0 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %5 // CHECK-NEXT: End sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array { bb0(%0 : $X): @@ -1282,11 +1355,17 @@ sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(metho sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject // CHECK-LABEL: CG of semantics_pair_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: A, Succ: -// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1) -// CHECK-NEXT: Con %2.1 Esc: G, Succ: -// CHECK-NEXT: Val %4 Esc: %5, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: +// CHECK-NEXT: Con [int] %1.1 Esc: A, Succ: (%1.2) +// CHECK-NEXT: Con [ref] %1.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con [ref] %4.1 Esc: %5, Succ: // CHECK-NEXT: End sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1299,10 +1378,14 @@ bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of semantics_self_no_escaping_closure -// CHECK-NEXT: Arg %0 Esc: A, Succ: -// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1) -// CHECK-NEXT: Con %1.1 Esc: G, Succ: -// CHECK-NEXT: Val %3 Esc: %4, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: +// CHECK-NEXT: Con [ref] %3.1 Esc: %4, Succ: // CHECK-NEXT: End sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error Error)) -> () { bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): @@ -1315,7 +1398,7 @@ bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error Error)): } // CHECK-LABEL: CG of check_dealloc_ref -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1325,7 +1408,7 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of check_set_deallocating -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: // CHECK-NEXT: End sil @check_set_deallocating : $@convention(thin) (@owned X) -> () { bb0(%0 : $X): @@ -1338,7 +1421,9 @@ bb0(%0 : $X): // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1353,7 +1438,9 @@ bb0: // happen due to strong_release and figure out that the object will not // globally escape. // CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: %1, Succ: // CHECK-NEXT: End sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1368,9 +1455,9 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release -// CHECK-NEXT: Val %0 Esc: %1, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () { bb0: @@ -1385,9 +1472,9 @@ bb0: // happen due to strong_release and figure out that the object will // globally escape. // CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value -// CHECK-NEXT: Val %0 Esc: %1, Succ: (%0.1) -// CHECK-NEXT: Con %0.1 Esc: %1, Succ: (%0.2) -// CHECK-NEXT: Con %0.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: %1, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: // CHECK-NEXT: End sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () { bb0: @@ -1404,9 +1491,11 @@ bb0: // invoked by strong_release it would also determine that these objects // do not escape from the function. // CHECK-LABEL: CG of check_is_local_object_through_bb_args -// CHECK-LABEL: Val %2 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %4 Esc: %6,%7, Succ: -// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4 +// CHECK-LABEL: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-LABEL: Con [int] %2.1 Esc: %7, Succ: (%2.2) +// CHECK-LABEL: Con [ref] %2.2 Esc: %7, Succ: +// CHECK-LABEL: Val [ref] %4 Esc: , Succ: (%2.1) +// CHECK-LABEL: Val [ref] %6 Esc: , Succ: %2, %4 // CHECK-LABEL: End sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): @@ -1427,7 +1516,10 @@ bb3(%6: $X): } // CHECK-LABEL: CG of check_look_through_thin_to_thick -// CHECK-NEXT: Arg %0 Esc: A, Succ: +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: %2, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: %2, Succ: // CHECK-NEXT: End sil @check_look_through_thin_to_thick: $(@convention(thin) () -> ()) -> () { bb0(%0 : $@convention(thin) () -> ()): @@ -1439,8 +1531,8 @@ bb0(%0 : $@convention(thin) () -> ()): // X.deinit // CHECK-LABEL: CG of $s4main1XCfD -// CHECK: Arg %0 Esc: A, Succ: -// CHECK: End +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: +// CHECK-NEXT: End sil @$s4main1XCfD: $@convention(method) (@owned X) -> () { bb0(%0 : $X): fix_lifetime %0 : $X @@ -1450,12 +1542,12 @@ bb0(%0 : $X): // Z.deinit // CHECK-LABEL: CG of $s4main1ZCfD -// CHECK: Arg %0 Esc: A, Succ: (%0.1) -// CHECK: Con %0.1 Esc: A, Succ: (%0.2) -// CHECK: Con %0.2 Esc: G, Succ: -// CHECK: Val %3 Esc: G, Succ: (%3.1) -// CHECK: Con %3.1 Esc: G, Succ: %0.2 -// CHECK: End +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1) +// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [ref] %2 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con [ref] %3.1 Esc: G, Succ: %2 +// CHECK-NEXT: End sil @$s4main1ZCfD: $@convention(method) (@owned Z) -> () { bb0(%0 : $Z): %1 = ref_element_addr %0 : $Z, #Z.x @@ -1483,9 +1575,10 @@ bb0(%0 : $X): } // CHECK-LABEL: CG of call_public_external_func -// CHECK: Val %0 Esc: G, Succ: (%0.1) -// CHECK: Con %0.1 Esc: G, Succ: -// CHECK: End +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: End sil @call_public_external_func : $@convention(thin) () -> () { bb0: %0 = alloc_ref $X @@ -1513,13 +1606,14 @@ bb2: // begin_apply result) is just marked as escaping. // CHECK-LABEL: CG of call_coroutine -// CHECK: Val %0 Esc: G, Succ: (%0.1) -// CHECK: Con %0.1 Esc: G, Succ: (%0.2) -// CHECK: Con %0.2 Esc: G, Succ: -// CHECK: Val %2 Esc: G, Succ: (%2.1) -// CHECK: Con %2.1 Esc: G, Succ: %4 -// CHECK: Val %4 Esc: G, Succ: -// CHECK: End +// CHECK-NEXT: Val [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Con %0.3 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [ref] %2.1 Esc: G, Succ: %4 +// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: +// CHECK-NEXT: End sil @call_coroutine : $@convention(thin) () -> () { bb0: %0 = alloc_ref $Y @@ -1533,3 +1627,286 @@ bb0: return %7 : $() } +// Test the absence of redundant pointsTo edges +// CHECK-LABEL: CG of testInitializePointsToLeaf +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: A, Succ: (%13) +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %2 +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%13), %0.2 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: (%13), %7 +// CHECK-NEXT: Con [int] %13 Esc: A, Succ: (%14) +// CHECK-NEXT: Con [ref] %14 Esc: A, Succ: +// CHECK-LABEL: End +class C { + var c: C +} + +sil @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional { +bb0(%0: $LinkedNode): + %adr = ref_element_addr %0 : $LinkedNode, #LinkedNode.next + %val = load %adr : $*LinkedNode + %optional = enum $Optional, #Optional.some!enumelt.1, %val : $LinkedNode + return %optional : $Optional +} + +sil @testInitializePointsToLeaf : $@convention(method) (@guaranteed LinkedNode) -> () { +bb0(%0 : $LinkedNode): + %f1 = function_ref @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional + %call1 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional + switch_enum %call1 : $Optional, case #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb3 + +bb2(%arg1 : $LinkedNode): + br bb4 + +bb3: + br bb4 + +bb4: + %call2 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional + switch_enum %call2 : $Optional, case #Optional.some!enumelt.1: bb10, case #Optional.none!enumelt: bb9 + +bb9: + %37 = integer_literal $Builtin.Int1, -1 + cond_fail %37 : $Builtin.Int1, "Unexpectedly found nil while unwrapping an Optional value" + unreachable + +// %40 +bb10(%arg2 : $LinkedNode): + %adr = ref_element_addr %arg2 : $LinkedNode, #LinkedNode.next + %val = load %adr : $*LinkedNode + %66 = tuple () + return %66 : $() +} + +// Another test for redundant pointsTo edges. In the original +// implementation, redundant points edges were created whenever adding +// a defer edge from a node with uninitialized pointsTo to a node with +// already-initialized pointsTo. +// CHECK-LABEL: CG of testInitializePointsToRedundant +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: A, Succ: (%3) +// CHECK-NEXT: Con [ref] %3 Esc: A, Succ: +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: %0 +// CHECK-NEXT: Val [ref] %12 Esc: , Succ: %1 +// CHECK-NEXT: Val [ref] %14 Esc: , Succ: (%2), %1, %12 +// CHECK-NEXT: Val [ref] %18 Esc: , Succ: %7, %14 +// CHECK-LABEL: End +sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C { +bb0(%0: $C, %1 : $C): + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $C) + +bb2: + br bb3(%1 : $C) + +bb3(%arg : $C): + return %arg : $C +} + +sil @testInitializePointsToRedundant : $@convention(method) (@guaranteed C, @guaranteed C) -> () { +bb0(%0 : $C, %1 : $C): + %adr0 = ref_element_addr %0 : $C, #C.c + %val0 = load %adr0 : $*C + cond_br undef, bb1, bb2 + +bb1: + br bb3(%0 : $C) + +bb2: + br bb3(%0 : $C) + +bb3(%arg1 : $C): + br bb4 + +bb4: + cond_br undef, bb5, bb6 + +bb5: + br bb7(%1 : $C) + +bb6: + br bb7(%1 : $C) + +bb7(%arg2 : $C): + %f1 = function_ref @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C + %call1 = apply %f1(%arg2, %1) : $@convention(method) (@guaranteed C, @guaranteed C) -> C + cond_br undef, bb8, bb9 + +bb8: + br bb10(%call1 : $C) + +bb9: + br bb10(%arg1 : $C) + +bb10(%arg3 : $C): + %66 = tuple () + return %66 : $() +} + +// Test canEscapeToUsePoint with defer edges between non-reference-type nodes. +// +// Unfortunately, the only way I can think of to create defer edges +// between non-reference nodes is with address-type block arguments. +// This will be banned soon, so the test will need to be disabled, but +// this is still an important corner case in the EscapeAnalysis +// logic. To keep the test working, and be able to test many more +// important corner cases, we should introduce testing modes that +// introduce spurious but predictable defer edges and node merges. +// +// Here, canEscapeTo is called on %bbarg (%5) whose node is not +// marked escaping. But it does have a defer edge to the local address +// (%0) which does cause the address to escape via the call site. +// +// CHECK-LABEL: CG of testDeferPointer +// CHECK-NEXT: Val %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-NEXT: Val %1 Esc: , Succ: (%0.1) +// CHECK-NEXT: Val %5 Esc: , Succ: %0, %1 +// CHECK-LABEL: End +// CHECK: MayEscape: %5 = argument of bb3 : $*Builtin.Int32 +// CHECK-NEXT: to %{{.*}} = apply %{{.*}}(%0) : $@convention(thin) (@inout Builtin.Int32) -> () +sil @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + +sil hidden @testDeferPointer : $@convention(thin) () -> () { +bb0: + %iadr1 = alloc_stack $Builtin.Int32 + %iadr2 = alloc_stack $Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + br bb3(%iadr1: $*Builtin.Int32) + +bb2: + br bb3(%iadr2: $*Builtin.Int32) + +bb3(%bbarg: $*Builtin.Int32): + %val = integer_literal $Builtin.Int32, 1 + store %val to %bbarg : $*Builtin.Int32 + %ftake = function_ref @takeAdr : $@convention(thin) (@inout Builtin.Int32) -> () + %call = apply %ftake(%iadr1) : $@convention(thin) (@inout Builtin.Int32) -> () + dealloc_stack %iadr2 : $*Builtin.Int32 + dealloc_stack %iadr1 : $*Builtin.Int32 + %z = tuple () + return %z : $() +} + +// Test interior node (self) cycles. + +class IntWrapper { + var property: Int64 +} + +// CHECK-LABEL: CG of testInteriorCycle +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%2) +// CHECK-NEXT: Con [int] %2 Esc: G, Succ: (%2) +// CHECK-NEXT: Val %6 Esc: , Succ: %0, %2 +// CHECK-NEXT: Ret return Esc: , Succ: %6 +// CHECK-LABEL: End +sil @testInteriorCycle : $@convention(thin) (@owned IntWrapper) -> (Builtin.BridgeObject, UnsafeMutablePointer) { +bb0(%0 : $IntWrapper): + %bridge = unchecked_ref_cast %0 : $IntWrapper to $Builtin.BridgeObject + %adr = ref_tail_addr %0 : $IntWrapper, $Int64 + %ptr = address_to_pointer %adr : $*Int64 to $Builtin.RawPointer + %ump = struct $UnsafeMutablePointer (%ptr : $Builtin.RawPointer) + strong_release %0 : $IntWrapper + %tuple = tuple (%bridge : $Builtin.BridgeObject, %ump : $UnsafeMutablePointer) + return %tuple : $(Builtin.BridgeObject, UnsafeMutablePointer) +} + +// Test undef -> struct_extract -> bbarg +// CHECK-LABEL: CG of testMappedUndef +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2) +// CHECK-NEXT: Con %4.2 Esc: G, Succ: +// CHECK-LABEL: End +struct StructWithObject { + @_hasStorage var obj: AnyObject { get set } + init(obj: AnyObject) +} + +sil @testMappedUndef : $@convention(thin) () -> () { +bb0: + cond_br undef, bb1, bb2 + +bb1: + br bb3(undef : $AnyObject) + +bb2: + %2 = struct_extract undef : $StructWithObject, #StructWithObject.obj + br bb3(%2 : $AnyObject) + +bb3(%4 : $AnyObject): + %5 = tuple () + return %5 : $() +} + +// Test value_to_bridge_object merged with a local reference. A graph node +// must be created for all values involved, and they must point to an +// escaping content node. +// CHECK-LABEL: CG of testValueToBridgeObject +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%5.1) +// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1) +// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2) +// CHECK-NEXT: Con %5.2 Esc: G, Succ: +// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%5.1), %1, %5 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7 +// CHECK-LABEL: End +sil @testValueToBridgeObject : $@convention(thin) (Builtin.Word) -> C { +bb0(%0 : $Builtin.Word): + %1 = alloc_ref $C + cond_br undef, bb1, bb2 + +bb1: + %derived = ref_to_bridge_object %1 : $C, %0 : $Builtin.Word + br bb3(%derived : $Builtin.BridgeObject) + +bb2: + %5 = value_to_bridge_object %0 : $Builtin.Word + br bb3(%5 : $Builtin.BridgeObject) + +bb3(%7 : $Builtin.BridgeObject): + %result = bridge_object_to_ref %7 : $Builtin.BridgeObject to $C + return %result : $C +} + +// Test strong_copy_unowned_value returned. It must be marked escaping, +// not simply "returned", because it's reference is formed from a +// function argument. +// CHECK-LABEL: CG of testStrongCopyUnowned +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2) +// CHECK-NEXT: Con %1.2 Esc: G, Succ: +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %1 +// CHECK-LABEL: End +sil [ossa] @testStrongCopyUnowned : $@convention(thin) (@guaranteed @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject { +bb0(%0 : @guaranteed $@sil_unowned Builtin.NativeObject): + %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject + return %1 : $Builtin.NativeObject +} + +// Test begin_access. It should look like a derived pointer. +// CHECK-LABEL: CG of testAccessMarkerHelper +sil hidden @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: CG of testAccessMarker +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testAccessMarker : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = begin_access [modify] [static] %0 : $*SomeData + %2 = function_ref @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () + %3 = apply %2(%1) : $@convention(thin) (@inout SomeData) -> () + end_access %1 : $*SomeData + %5 = tuple () + return %5 : $() +} diff --git a/test/SILOptimizer/escape_analysis_dead_store.sil b/test/SILOptimizer/escape_analysis_dead_store.sil new file mode 100644 index 0000000000000..f2809b054281c --- /dev/null +++ b/test/SILOptimizer/escape_analysis_dead_store.sil @@ -0,0 +1,138 @@ +// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all -escapes-internal-verify | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +final class X { + init() +} + +public struct S { + @_hasStorage var x: X { get set } + @_hasStorage var i: Int { get set } + init(x: X, i: Int) +} + +@_hasStorage @_hasInitialValue var gg: X { get set } + +@inline(never) func takex(_ x: X) + +sil [noinline] @takeX : $@convention(thin) (@guaranteed X) -> () + +// Test that escape analysis does not consider an inout argument to +// escape at a call site even though it's reference-type field does +// escape. Dead store elimination asks MemoryBehaviorVisitor whether +// the apply may read from the inout argument. This call into +// canEscapeToUsePoint, which should return false because the inout +// structure itself is not exposed to the call, only it's +// reference-type field is. +// +// CHECK-LABEL: sil @testInoutNoEscape +// CHECK-NOT: store +// CHECK: apply +// CHECK: store +// CHECK-NOT: store +// CHECK: } // end sil function 'testInoutNoEscape' +sil @testInoutNoEscape : $@convention(thin) (@inout S, @guaranteed S) -> () { +bb0(%0 : $*S, %1 : $S): + %4 = struct_extract %1 : $S, #S.x + %5 = struct_element_addr %0 : $*S, #S.x + %6 = load %5 : $*X + strong_retain %4 : $X + strong_release %6 : $X + store %1 to %0 : $*S + %10 = function_ref @takeX : $@convention(thin) (@guaranteed X) -> () + strong_retain %4 : $X + %12 = apply %10(%4) : $@convention(thin) (@guaranteed X) -> () + release_value %1 : $S + store %1 to %0 : $*S + %15 = tuple () + return %15 : $() +} + +// ============================================================================= +// Test that a store writing back into a container is not eliminated +// when the container's interior pointer later escapes into a function +// that reads from the pointer. + +final internal class TestArrayContainer { + @_hasStorage @_hasInitialValue internal final var pointer: UnsafeMutablePointer { get set } + @_hasStorage @_hasInitialValue internal final var storage: ContiguousArray { get set } + @_optimize(none) @inline(never) internal final func append(_ arg: Int32) + internal final func va_list() -> UnsafeMutableRawPointer + init() +} + +sil @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // user: %5 +// ContiguousArray.append(_:) +sil @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + + +// Helper that reads from a raw pointer. +sil hidden [noinline] @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool { +bb0(%0 : $UnsafeMutableRawPointer): + %1 = integer_literal $Builtin.Int64, 0 + %2 = struct $Int64 (%1 : $Builtin.Int64) + %3 = function_ref @UnsafeMutablePointer_load_Int64 : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> + %4 = apply %3(%2, %0) : $@convention(method) (Int64, UnsafeMutableRawPointer) -> Optional> // users: %18, %6 + %5 = integer_literal $Builtin.Int1, -1 + %6 = struct $Bool (%5 : $Builtin.Int1) + return %6 : $Bool +} + +// TestArrayContainer.append(_:) +// Helper that produces a nonempty array. +sil hidden [noinline] [Onone] @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () { +bb0(%0 : $Int32, %1 : $TestArrayContainer): + %2 = alloc_stack $Int32 + store %0 to %2 : $*Int32 + %4 = ref_element_addr %1 : $TestArrayContainer, #TestArrayContainer.storage + %5 = function_ref @$ss15ContiguousArrayV6appendyyxnF : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + %6 = apply %5(%2, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout ContiguousArray<τ_0_0>) -> () + dealloc_stack %2 : $*Int32 + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +// CHECK: [[ALLOC:%.*]] = alloc_ref [stack] $TestArrayContainer +// CHECK: [[PTR:%.*]] = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer +// CHECK: [[LOAD:%.*]] = load %{{.*}} : $*__ContiguousArrayStorageBase +// CHECK: [[ELTS:%.*]] = ref_tail_addr [[LOAD]] : $__ContiguousArrayStorageBase, $Int32 +// CHECK: [[ELTPTR:%.*]] = address_to_pointer [[ELTS]] : $*Int32 to $Builtin.RawPointer +// CHECK: [[UMP:%.*]] = struct $UnsafeMutablePointer ([[ELTPTR]] : $Builtin.RawPointer) +// CHECK: store [[UMP]] to [[PTR]] : $*UnsafeMutablePointer +// CHECK: [[F:%.*]] = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: apply [[F]](%{{.*}}) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool +// CHECK: fix_lifetime %0 : $TestArrayContainer +// CHECK-LABEL: } // end sil function 'testContainerPointer' +sil [noinline] @testContainerPointer : $@convention(thin) () -> Bool { +bb0: + %0 = alloc_ref [stack] $TestArrayContainer + %1 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.pointer + %2 = ref_element_addr %0 : $TestArrayContainer, #TestArrayContainer.storage + %3 = integer_literal $Builtin.Int32, 42 + %4 = struct $Int32 (%3 : $Builtin.Int32) + %5 = function_ref @TestArrayContainer_append : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %6 = apply %5(%4, %0) : $@convention(method) (Int32, @guaranteed TestArrayContainer) -> () + %7 = struct_element_addr %2 : $*ContiguousArray, #ContiguousArray._buffer + %8 = struct_element_addr %7 : $*_ContiguousArrayBuffer, #_ContiguousArrayBuffer._storage + %9 = load %8 : $*__ContiguousArrayStorageBase + %10 = ref_tail_addr %9 : $__ContiguousArrayStorageBase, $Int32 + %11 = address_to_pointer %10 : $*Int32 to $Builtin.RawPointer + %12 = struct $UnsafeMutablePointer (%11 : $Builtin.RawPointer) + store %12 to %1 : $*UnsafeMutablePointer + %14 = address_to_pointer %1 : $*UnsafeMutablePointer to $Builtin.RawPointer + %15 = struct $UnsafeMutableRawPointer (%14 : $Builtin.RawPointer) + %16 = function_ref @takeRawPtr : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + %17 = apply %16(%15) : $@convention(thin) (UnsafeMutableRawPointer) -> Bool + fix_lifetime %0 : $TestArrayContainer + set_deallocating %0 : $TestArrayContainer + strong_release %9 : $__ContiguousArrayStorageBase + dealloc_ref %0 : $TestArrayContainer + dealloc_ref [stack] %0 : $TestArrayContainer + return %17 : $Bool +} diff --git a/test/SILOptimizer/escape_analysis_invalidate.sil b/test/SILOptimizer/escape_analysis_invalidate.sil new file mode 100644 index 0000000000000..5c663e655d320 --- /dev/null +++ b/test/SILOptimizer/escape_analysis_invalidate.sil @@ -0,0 +1,84 @@ +// RUN: %target-sil-opt %s -temp-rvalue-opt -enable-sil-verify-all -escapes-internal-verify | %FileCheck %s +// +// TempRValue iteratively uses EscapeAnalysis and deletes +// instructions. Make sure that the connection graph remains valid +// . + +import Swift + +sil_stage canonical + +protocol SomeProtocol { + func foo() +} +struct SomeInstance : SomeProtocol { + func foo() +} +class SomeClass { + var someProperty: SomeProtocol +} +struct SomeStruct { + var someRef: SomeClass +} + +// CHECK-LABEL: sil @testTempRvalueEscapeAnalysisUpdate : $@convention(thin) (@in_guaranteed SomeProtocol, @guaranteed SomeClass) -> () { +// CHECK: bb0(%0 : $*SomeProtocol, %1 : $SomeClass): +// CHECK: alloc_ref $SomeClass +// CHECK: alloc_stack $SomeStruct +// CHECK-NOT: alloc_stack $SomeProtocol +// CHECK-NOT: copy_addr +// CHECK: init_existential_addr %{{.*}} : $*SomeProtocol, $SomeInstance +// CHECK: [[OPEN1:%.*]] = open_existential_addr immutable_access %0 : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol +// CHECK: apply %{{.*}}<@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol>([[OPEN1]]) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () +// CHECK-NOT: destroy_addr +// CHECK-NOT: alloc_stack $SomeProtocol +// CHECK-NOT: copy_addr +// CHECK: [[OPEN2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol +// CHECK: apply %{{.*}}<@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol>([[OPEN2]]) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () +// CHECK-NOT: destroy_addr +// CHECK-LABEL: } // end sil function 'testTempRvalueEscapeAnalysisUpdate' +sil @testTempRvalueEscapeAnalysisUpdate : $@convention(thin) (@in_guaranteed SomeProtocol, @guaranteed SomeClass) -> () { +bb0(%0 : $*SomeProtocol, %1 : $SomeClass): + // First create a uniquely identified protocol value. This way + // EscapeAnalysis canPointToSameMemory will kick in later. It can't + // be an exclusive argument, or AliasAnalysis will filter it before + // querying EscapeAnalysis. + %localRef = alloc_ref $SomeClass + %localPropAdr = ref_element_addr %localRef : $SomeClass, #SomeClass.someProperty + copy_addr %0 to [initialization] %localPropAdr : $*SomeProtocol + %stk0 = alloc_stack $SomeStruct + %stk0Ref = struct_element_addr %stk0 : $*SomeStruct, #SomeStruct.someRef + store %localRef to %stk0Ref : $*SomeClass + %indirectRef = load %stk0Ref : $*SomeClass + %indirectLocalPropAdr = ref_element_addr %indirectRef : $SomeClass, #SomeClass.someProperty + + %propAdr1 = ref_element_addr %1 : $SomeClass, #SomeClass.someProperty + // TempRValue tries to kick in on this copy, but there is an + // interfering write that can't be handled by AliasAnlysis without + // consulting EscapingAnalysis. MemoryBehavior drops down to + // EscapeAnalysis for only a few special instructions, like + // init_existential_addr. + %stkAdr1 = alloc_stack $SomeProtocol + copy_addr %0 to [initialization] %stkAdr1 : $*SomeProtocol + %instanceAdr = init_existential_addr %indirectLocalPropAdr : $*SomeProtocol, $SomeInstance + %openadr1 = open_existential_addr immutable_access %stkAdr1 : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol + %witness1 = witness_method $@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol, #SomeProtocol.foo!1 : (Self) -> () -> (), %openadr1 : $*@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () + %call1 = apply %witness1<@opened("6419340C-0B14-11EA-9897-ACDE48001122") SomeProtocol>(%openadr1) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () + destroy_addr %stkAdr1 : $*SomeProtocol + + // TempRValue optimization kicks in here. The open_existential_addr + // ceates a content node that refers back to the dead stack + // location. + %stkAdr2 = alloc_stack $SomeProtocol + copy_addr %propAdr1 to [initialization] %stkAdr2 : $*SomeProtocol + %openadr2 = open_existential_addr immutable_access %stkAdr2 : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol + %witness2 = witness_method $@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol, #SomeProtocol.foo!1 : (Self) -> () -> (), %openadr2 : $*@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () + %call2 = apply %witness2<@opened("6419340C-0B14-11EA-9897-ACDE48001123") SomeProtocol>(%openadr2) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> () + destroy_addr %stkAdr2 : $*SomeProtocol + + dealloc_stack %stkAdr2 : $*SomeProtocol + dealloc_stack %stkAdr1 : $*SomeProtocol + dealloc_stack %stk0 : $*SomeStruct + %v = tuple () + return %v : $() +} diff --git a/test/SILOptimizer/escape_analysis_reduced.sil b/test/SILOptimizer/escape_analysis_reduced.sil new file mode 100644 index 0000000000000..352f4a160bbb4 --- /dev/null +++ b/test/SILOptimizer/escape_analysis_reduced.sil @@ -0,0 +1,594 @@ +// RUN: %target-sil-opt %s -escapes-dump -escapes-internal-verify -o /dev/null | %FileCheck %s + +// REQUIRES: asserts +// REQUIRES: OS=macosx +// REQUIRES: PTRSIZE=64 + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +// ============================================================================= +// Test call to array.uninitialized that has "extra" release_value uses + +class C { + var c: C +} + +class DummyArrayStorage { + @_hasStorage var count: Int { get } + @_hasStorage var capacity: Int { get } + init() +} + +// init_any_array_with_buffer +sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + +// CHECK-LABEL: CG of testBadArrayUninit +// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2) +// CHECK-NEXT: Con [ref] %2.2 Esc: G, Succ: +// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1) +// CHECK-NEXT: Con %5.1 Esc: G, Succ: %10 +// CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1) +// CHECK-NEXT: Con %10.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testBadArrayUninit : $@convention(thin) (Builtin.Word, Int32) -> () { +bb0(%0 : $Builtin.Word, %1 : $Int32): + // create an array + %2 = alloc_ref [tail_elems $AnyObject * %0 : $Builtin.Word] $DummyArrayStorage + %3 = metatype $@thin Array.Type + %4 = function_ref @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %5 = apply %4(%2, %1, %3) : $@convention(thin) (@owned DummyArrayStorage, Int32, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %6 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 0 + %7 = tuple_extract %5 : $(Array, UnsafeMutablePointer), 1 + %8 = struct_extract %7 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue + %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*AnyObject + + // store an elt + %10 = alloc_ref $C + %11 = init_existential_ref %10 : $C : $C, $AnyObject + store %11 to %9 : $*AnyObject + + // extra use of the call + release_value %5 : $(Array, UnsafeMutablePointer) // id: %228 + %13 = tuple () + return %13 : $() +} + +// ============================================================================= +// testArrayEscapeToBox: test that an array is marked escaping when +// assigned to a box. When multiple arrays are merged into the same +// box, ensure that a previous mapping from the project_box address to +// the box's content is not lost during the merge. + +class ElementClass { + init() +} + +class StagedContext { + init() +} + +class VFSStagedContext : StagedContext { + override init() +} + +// specialized Array.init() +sil @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array.Type) -> @owned Array + +// specialized Array._getCount() +sil @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array) -> Int + +// specialized static Array._adoptStorage(_:count:) +sil shared [_semantics "array.uninitialized"] @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) { +// %0 // users: %13, %3 +// %1 // users: %9, %4 +bb0(%0 : $_ContiguousArrayStorage, %1 : $Int, %2 : $@thin Array.Type): + %3 = upcast %0 : $_ContiguousArrayStorage to $__ContiguousArrayStorageBase // users: %17, %11 + %4 = struct_extract %1 : $Int, #Int._value // user: %6 + %5 = integer_literal $Builtin.Int64, 1 // users: %7, %6 + %6 = builtin "shl_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %7 + %7 = builtin "or_Int64"(%6 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %8 + %8 = struct $UInt (%7 : $Builtin.Int64) // user: %9 + %9 = struct $_SwiftArrayBodyStorage (%1 : $Int, %8 : $UInt) // user: %10 + %10 = struct $_ArrayBody (%9 : $_SwiftArrayBodyStorage) // user: %12 + %11 = ref_element_addr %3 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity // user: %12 + store %10 to %11 : $*_ArrayBody // id: %12 + %13 = unchecked_ref_cast %0 : $_ContiguousArrayStorage to $Builtin.BridgeObject // user: %14 + %14 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%13 : $Builtin.BridgeObject) // user: %15 + %15 = struct $_ArrayBuffer (%14 : $_BridgeStorage<__ContiguousArrayStorageBase>) // user: %16 + %16 = struct $Array (%15 : $_ArrayBuffer) // user: %20 + %17 = ref_tail_addr %3 : $__ContiguousArrayStorageBase, $ElementClass // user: %18 + %18 = address_to_pointer %17 : $*ElementClass to $Builtin.RawPointer // user: %19 + %19 = struct $UnsafeMutablePointer (%18 : $Builtin.RawPointer) // user: %20 + %20 = tuple (%16 : $Array, %19 : $UnsafeMutablePointer) // user: %21 + return %20 : $(Array, UnsafeMutablePointer) // id: %21 +} + +// testArrayUsePointsClosure1 +sil @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional }, @guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> ()) -> () + +// testArrayUsePointsClosure2 +sil @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> (), @guaranteed Optional, @guaranteed { var Array }) -> () + +// Make sure both locally allocated array's are globally escaping. +// +// CHECK-LABEL: CG of testArrayEscapeToBox +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%21) +// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%21) +// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1 +// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%21) +// CHECK-NEXT: Val [ref] %12 Esc: G, Succ: (%21) +// CHECK-NEXT: Val [ref] %17 Esc: G, Succ: (%21) +// CHECK-NEXT: Val [ref] %20 Esc: G, Succ: %17 +// CHECK-NEXT: Con [int] %21 Esc: G, Succ: (%39) +// CHECK-NEXT: Val [ref] %31 Esc: G, Succ: (%21) +// CHECK-NEXT: Val [ref] %34 Esc: G, Succ: %31 +// CHECK-NEXT: Con %39 Esc: G, Succ: (%21), %12, %20, %34 +// CHECK-NEXT: Val [ref] %45 Esc: , Succ: %0, %8, %39 +// CHECK-NEXT: End +sil private @testArrayEscapeToBox : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> ()) -> () { +// %0 // users: %54, %51, %47, %45, %5, %4 +bb0(%0 : $@callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> ()): + %1 = alloc_box ${ var Optional }, var, name "context" // users: %59, %6, %4, %2 + %2 = project_box %1 : ${ var Optional }, 0 // user: %44 + // function_ref testArrayUsePointsClosure1 + %3 = function_ref @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional }, @guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> ()) -> () // user: %4 + %4 = partial_apply [callee_guaranteed] %3(%1, %0) : $@convention(thin) (@guaranteed { var Optional }, @guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> ()) -> () + strong_retain %0 : $@callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> () // id: %5 + strong_retain %1 : ${ var Optional } // id: %6 + br bb1 // id: %7 + +bb1: // Preds: bb0 + %8 = alloc_box ${ var Array }, var, name "intents" // users: %58, %56, %52, %48, %45, %9 + %9 = project_box %8 : ${ var Array }, 0 // users: %41, %36, %27, %22, %13 + %10 = metatype $@thin Array.Type // users: %33, %19, %12 + // function_ref specialized Array.init() + %11 = function_ref @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array.Type) -> @owned Array // user: %12 + %12 = apply %11(%10) : $@convention(method) (@thin Array.Type) -> @owned Array // user: %13 + store %12 to %9 : $*Array // id: %13 + cond_br undef, bb2, bb3 // id: %14 + +bb2: // Preds: bb1 + %15 = integer_literal $Builtin.Int64, 1 // user: %16 + %16 = struct $Int (%15 : $Builtin.Int64) // user: %19 + %17 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage // user: %19 + // function_ref specialized static Array._adoptStorage(_:count:) + %18 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) // user: %19 + %19 = apply %18(%17, %16, %10) : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) // users: %21, %20 + %20 = tuple_extract %19 : $(Array, UnsafeMutablePointer), 0 // user: %27 + %21 = tuple_extract %19 : $(Array, UnsafeMutablePointer), 1 + %22 = struct_element_addr %9 : $*Array, #Array._buffer // user: %23 + %23 = struct_element_addr %22 : $*_ArrayBuffer, #_ArrayBuffer._storage // user: %24 + %24 = struct_element_addr %23 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %25 + %25 = load %24 : $*Builtin.BridgeObject // user: %26 + strong_release %25 : $Builtin.BridgeObject // id: %26 + store %20 to %9 : $*Array // id: %27 + br bb4 // id: %28 + +bb3: // Preds: bb1 + %29 = integer_literal $Builtin.Int64, 1 // user: %30 + %30 = struct $Int (%29 : $Builtin.Int64) // user: %33 + %31 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage // user: %33 + // function_ref specialized static Array._adoptStorage(_:count:) + %32 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) // user: %33 + %33 = apply %32(%31, %30, %10) : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) // users: %35, %34 + %34 = tuple_extract %33 : $(Array, UnsafeMutablePointer), 0 // user: %41 + %35 = tuple_extract %33 : $(Array, UnsafeMutablePointer), 1 + %36 = struct_element_addr %9 : $*Array, #Array._buffer // user: %37 + %37 = struct_element_addr %36 : $*_ArrayBuffer, #_ArrayBuffer._storage // user: %38 + %38 = struct_element_addr %37 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %39 + %39 = load %38 : $*Builtin.BridgeObject // user: %40 + strong_release %39 : $Builtin.BridgeObject // id: %40 + store %34 to %9 : $*Array // id: %41 + br bb4 // id: %42 + +bb4: // Preds: bb3 bb2 + // function_ref testArrayUsePointsClosure2 + %43 = function_ref @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> (), @guaranteed Optional, @guaranteed { var Array }) -> () // user: %45 + %44 = load %2 : $*Optional // users: %55, %53, %49, %45 + %45 = partial_apply [callee_guaranteed] %43(%0, %44, %8) : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> (), @guaranteed Optional, @guaranteed { var Array }) -> () // user: %57 + // function_ref specialized Array._getCount() + %46 = function_ref @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array) -> Int + strong_retain %0 : $@callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> () // id: %47 + strong_retain %8 : ${ var Array } // id: %48 + retain_value %44 : $Optional // id: %49 + br bb5 // id: %50 + +bb5: // Preds: bb4 + strong_retain %0 : $@callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> () // id: %51 + strong_retain %8 : ${ var Array } // id: %52 + retain_value %44 : $Optional // id: %53 + strong_release %0 : $@callee_guaranteed (@guaranteed Optional, @guaranteed Optional) -> () // id: %54 + release_value %44 : $Optional // id: %55 + strong_release %8 : ${ var Array } // id: %56 + strong_release %45 : $@callee_guaranteed () -> () // id: %57 + strong_release %8 : ${ var Array } // id: %58 + strong_release %1 : ${ var Optional } // id: %59 + br bb6 // id: %60 + +bb6: // Preds: bb5 + %61 = tuple () // user: %62 + return %61 : $() // id: %62 +} + +// ============================================================================= +// Test merging an interior node with a non-interior +// node. Verification can fail in the time between when the properties +// are merged and when the edges are merged. + +struct Poly { + @_hasStorage var points: Array { get set } + @_hasStorage var length: Int64 { get set } + init(points: Array, length: Int64) +} + +// CHECK-LABEL: CG of testMergeInteriorPointsTo +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%6.2) +// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1) +// CHECK-NEXT: Con [ref] %1.1 Esc: , Succ: %0, %12.1 +// CHECK-NEXT: Val %6 Esc: , Succ: (%6.1) +// CHECK-NEXT: Con [ref] %6.1 Esc: , Succ: (%6.2) +// CHECK-NEXT: Con [int] %6.2 Esc: G, Succ: (%6.3) +// CHECK-NEXT: Con %6.3 Esc: G, Succ: (%6.4) +// CHECK-NEXT: Con [int] %6.4 Esc: G, Succ: (%6.5) +// CHECK-NEXT: Con %6.5 Esc: G, Succ: (%6.6) +// CHECK-NEXT: Con %6.6 Esc: G, Succ: +// CHECK-NEXT: Val %8 Esc: , Succ: (%8.1) +// CHECK-NEXT: Con [ref] %8.1 Esc: , Succ: %6.1 +// CHECK-NEXT: Val %12 Esc: , Succ: (%12.1) +// CHECK-NEXT: Con [ref] %12.1 Esc: , Succ: %6.1 +// CHECK-LABEL: End +sil [serialized] @testMergeInteriorPointsTo : $@convention(method) (@owned Array) -> () { +bb0(%0 : $Array): + %1 = alloc_stack $Poly, var, name "self" + %2 = struct_element_addr %1 : $*Poly, #Poly.points + retain_value %0 : $Array + retain_value %0 : $Array + store %0 to %2 : $*Array + %6 = alloc_stack $Int64, var, name "length" + release_value %0 : $Array + %8 = alloc_stack $Int64 + copy_addr %6 to [initialization] %8 : $*Int64 + destroy_addr %8 : $*Int64 + dealloc_stack %8 : $*Int64 + %12 = alloc_stack $Int64 + copy_addr %6 to [initialization] %12 : $*Int64 + %14 = struct_element_addr %1 : $*Poly, #Poly.length + copy_addr [take] %12 to [initialization] %14 : $*Int64 + dealloc_stack %12 : $*Int64 + destroy_addr %6 : $*Int64 + dealloc_stack %6 : $*Int64 + dealloc_stack %1 : $*Poly + %20 = tuple () + return %20 : $() +} + +// ============================================================================= +// Test merging a callee graph such that when merging pointsTo nodes +// for defer edges in the caller graph, the new pointsTo node has +// already been merged and the deferred node has both a defer edge and +// pointsTo edge to the same node. + +public protocol ProceduralOp : AnyObject {} + +public class OptypeBase { + @_hasStorage var name : String { get set } + @_hasStorage var id : Int { get set } +} + +public class OptypeDependent : OptypeBase { + @_hasStorage var _enable : Bool { get set } + @_hasStorage var _ops : Array +} + +public class CombineAnyOp : OptypeDependent { + @_hasStorage var _sources : Array<(ProceduralOp, Bool)> +} + +sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage + +sil @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0> + +// specialized static Array._allocateUninitialized(_:) +sil shared [_semantics "array.uninitialized"] @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Tg5 : $@convention(method) (Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) { +// %0 +bb0(%0 : $Int, %1 : $@thin Array.Type): + %2 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage + %3 = address_to_pointer %2 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer + %4 = raw_pointer_to_ref %3 : $Builtin.RawPointer to $__EmptyArrayStorage + %5 = unchecked_ref_cast %4 : $__EmptyArrayStorage to $Builtin.BridgeObject + %6 = integer_literal $Builtin.Int64, 0 + %7 = struct_extract %0 : $Int, #Int._value + %8 = builtin "cmp_slt_Int64"(%6 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + cond_br %8, bb1, bb2 + +bb1: + %10 = metatype $@thin Array.Type + // function_ref static Array._allocateBufferUninitialized(minimumCapacity:) + %11 = function_ref @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0> + %12 = apply %11(%0, %10) : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0> + %13 = struct_extract %12 : $_ArrayBuffer, #_ArrayBuffer._storage + %14 = struct_extract %13 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue + %15 = unchecked_ref_cast %14 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase + %16 = ref_element_addr %15 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity + %17 = struct_element_addr %16 : $*_ArrayBody, #_ArrayBody._storage + %18 = struct_element_addr %17 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count + store %0 to %18 : $*Int + br bb3(%14 : $Builtin.BridgeObject) + +bb2: + strong_retain %4 : $__EmptyArrayStorage + br bb3(%5 : $Builtin.BridgeObject) + +// %23 +bb3(%23 : $Builtin.BridgeObject): + %24 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%23 : $Builtin.BridgeObject) + %25 = struct $_ArrayBuffer (%24 : $_BridgeStorage<__ContiguousArrayStorageBase>) + %26 = struct $Array (%25 : $_ArrayBuffer) + %27 = unchecked_ref_cast %23 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase + %28 = ref_tail_addr %27 : $__ContiguousArrayStorageBase, $ProceduralOp + %29 = address_to_pointer %28 : $*ProceduralOp to $Builtin.RawPointer + %30 = struct $UnsafeMutablePointer (%29 : $Builtin.RawPointer) + %31 = tuple (%26 : $Array, %30 : $UnsafeMutablePointer) + return %31 : $(Array, UnsafeMutablePointer) +} + +sil shared [_semantics "array.uninitialized"] @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Sbt_Tg5 : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>) { +// %0 +bb0(%0 : $Int, %1 : $@thin Array<(ProceduralOp, Bool)>.Type): + %2 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage + %3 = address_to_pointer %2 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer + %4 = raw_pointer_to_ref %3 : $Builtin.RawPointer to $__EmptyArrayStorage + %5 = unchecked_ref_cast %4 : $__EmptyArrayStorage to $Builtin.BridgeObject + %6 = integer_literal $Builtin.Int64, 0 + %7 = struct_extract %0 : $Int, #Int._value + %8 = builtin "cmp_slt_Int64"(%6 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1 + cond_br %8, bb1, bb2 + +bb1: + %10 = metatype $@thin Array<(ProceduralOp, Bool)>.Type + // function_ref static Array._allocateBufferUninitialized(minimumCapacity:) + %11 = function_ref @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0> + %12 = apply %11<(ProceduralOp, Bool)>(%0, %10) : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0> + %13 = struct_extract %12 : $_ArrayBuffer<(ProceduralOp, Bool)>, #_ArrayBuffer._storage + %14 = struct_extract %13 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue + %15 = unchecked_ref_cast %14 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase + %16 = ref_element_addr %15 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity + %17 = struct_element_addr %16 : $*_ArrayBody, #_ArrayBody._storage + %18 = struct_element_addr %17 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count + store %0 to %18 : $*Int + br bb3(%14 : $Builtin.BridgeObject) + +bb2: + strong_retain %4 : $__EmptyArrayStorage + br bb3(%5 : $Builtin.BridgeObject) + +// %23 +bb3(%23 : $Builtin.BridgeObject): + %24 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%23 : $Builtin.BridgeObject) + %25 = struct $_ArrayBuffer<(ProceduralOp, Bool)> (%24 : $_BridgeStorage<__ContiguousArrayStorageBase>) + %26 = struct $Array<(ProceduralOp, Bool)> (%25 : $_ArrayBuffer<(ProceduralOp, Bool)>) + %27 = unchecked_ref_cast %23 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase + %28 = ref_tail_addr %27 : $__ContiguousArrayStorageBase, $(ProceduralOp, Bool) + %29 = address_to_pointer %28 : $*(ProceduralOp, Bool) to $Builtin.RawPointer + %30 = struct $UnsafeMutablePointer<(ProceduralOp, Bool)> (%29 : $Builtin.RawPointer) + %31 = tuple (%26 : $Array<(ProceduralOp, Bool)>, %30 : $UnsafeMutablePointer<(ProceduralOp, Bool)>) + return %31 : $(Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>) +} + +sil @testMergePointsTo_OptypeDependent_init : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent { +bb0(%0 : $String, %1 : $OptypeDependent): + %3 = integer_literal $Builtin.Int64, 0 + %4 = struct $Int (%3 : $Builtin.Int64) + %5 = metatype $@thin Array.Type + // function_ref specialized static Array._allocateUninitialized(_:) + %6 = function_ref @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Tg5 : $@convention(method) (Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + strong_retain %1 : $OptypeDependent // id: %7 + %8 = apply %6(%4, %5) : $@convention(method) (Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %9 = tuple_extract %8 : $(Array, UnsafeMutablePointer), 0 + %10 = ref_element_addr %1 : $OptypeDependent, #OptypeDependent._ops + store %9 to %10 : $*Array + %12 = integer_literal $Builtin.Int1, -1 + %13 = struct $Bool (%12 : $Builtin.Int1) + %14 = ref_element_addr %1 : $OptypeDependent, #OptypeDependent._enable + store %13 to %14 : $*Bool + strong_release %1 : $OptypeDependent + %17 = upcast %1 : $OptypeDependent to $OptypeBase + %19 = ref_element_addr %17 : $OptypeBase, #OptypeBase.name + store %0 to %19 : $*String + return %1 : $OptypeDependent +} + +// CHECK-LABEL: CG of testMergePointsTo_CombineAnyOp_init +// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%7.1) +// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%9) +// CHECK-NEXT: Val %7 Esc: A, Succ: (%7.1), %7.1 +// CHECK-NEXT: Con [int] %7.1 Esc: G, Succ: (%7.1) +// CHECK-NEXT: Con [int] %9 Esc: A, Succ: (%9.1) +// CHECK-NEXT: Con [ref] %9.1 Esc: A, Succ: %0, %7, %7.1 +// CHECK-NEXT: Val [ref] %18 Esc: , Succ: (%9), %1 +// CHECK-NEXT: Ret [ref] return Esc: , Succ: %18 +// CHECK-LABEL: End +sil @testMergePointsTo_CombineAnyOp_init : $@convention(method) (@owned String, @owned CombineAnyOp) -> @owned CombineAnyOp { +bb0(%0 : $String, %1 : $CombineAnyOp): + strong_retain %1 : $CombineAnyOp + %4 = integer_literal $Builtin.Int64, 0 + %5 = struct $Int (%4 : $Builtin.Int64) + %6 = metatype $@thin Array<(ProceduralOp, Bool)>.Type + // function_ref specialized static Array._allocateUninitialized(_:) + %7 = function_ref @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Sbt_Tg5 : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>) + %8 = apply %7(%5, %6) : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>) + %9 = tuple_extract %8 : $(Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>), 0 + %10 = ref_element_addr %1 : $CombineAnyOp, #CombineAnyOp._sources + store %9 to %10 : $*Array<(ProceduralOp, Bool)> + strong_release %1 : $CombineAnyOp + %13 = struct_extract %0 : $String, #String._guts + %14 = struct_extract %13 : $_StringGuts, #_StringGuts._object + %15 = struct_extract %14 : $_StringObject, #_StringObject._object + strong_retain %15 : $Builtin.BridgeObject + %18 = upcast %1 : $CombineAnyOp to $OptypeDependent + %19 = function_ref @testMergePointsTo_OptypeDependent_init : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent + %20 = apply %19(%0, %18) : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent + %21 = unchecked_ref_cast %20 : $OptypeDependent to $CombineAnyOp + strong_release %15 : $Builtin.BridgeObject + return %21 : $CombineAnyOp +} + +// ============================================================================= +// Test calling ConnectionGraph::initializePointsTo from +// mergeAllScheduledNodes when multiple merges are pending. In this +// case, initializePointsTo is called and a node in the +// to-be-initialized defer web already pointsTo a node that has been +// marked for merging. + +public protocol ASTNode {} + +public struct AssignmentNode : ASTNode { + @_hasStorage public let variable: ASTNode { get } + @_hasStorage public let value: ASTNode { get } + @_hasStorage public let range: Range? { get } + @_hasStorage public let documentation: String? { get } + public init(variable: ASTNode, value: ASTNode, range: Range?, documentation: String?) throws + public var childNodes: [ASTNode] { get } +} + +public struct VariableNode : ASTNode { + @_hasStorage public let name: String { get } + @_hasStorage public let range: Range? { get } + public init(name: String, range: Range?) + public var childNodes: [ASTNode] { get } + public var description: String { get } + public var nodeDescription: String? { get } +} + +// CHECK-LABEL: CG of testPendingMergeHelper +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con %0.1 Esc: A, Succ: %3.1 +// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1) +// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1) +// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1.1, %2 +// CHECK-LABEL: End +sil @testPendingMergeHelper : $@convention(method) (@in ASTNode, @owned Optional) -> (@out AssignmentNode, @error Error) { +bb0(%0 : $*AssignmentNode, %1 : $*ASTNode, %4 : $Optional): + %6 = alloc_stack $AssignmentNode, var, name "self" + cond_br undef, bb1, bb2 + +bb1: + destroy_addr %1 : $*ASTNode + dealloc_stack %6 : $*AssignmentNode + throw undef : $Error + +bb2: + %160 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.variable + copy_addr [take] %1 to [initialization] %160 : $*ASTNode + %162 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.value + %164 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.range + %166 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.documentation + store %4 to %166 : $*Optional + copy_addr [take] %6 to [initialization] %0 : $*AssignmentNode + dealloc_stack %6 : $*AssignmentNode + %171 = tuple () + return %171 : $() +} + +// CHECK-LABEL: CG of testPendingMerge +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%22) +// CHECK-NEXT: Arg [ref] %1 Esc: G, Succ: (%9.1) +// CHECK-NEXT: Val %2 Esc: , Succ: (%9) +// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1) +// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%9.1), %7, %9 +// CHECK-NEXT: Val %5 Esc: , Succ: (%7) +// CHECK-NEXT: Con %7 Esc: A, Succ: (%9.1) +// CHECK-NEXT: Con [ref] %9 Esc: A, Succ: (%9.1) +// CHECK-NEXT: Con [int] %9.1 Esc: G, Succ: (%9.1), %1 +// CHECK-NEXT: Con %22 Esc: A, Succ: (%22.1) +// CHECK-NEXT: Con %22.1 Esc: A, Succ: %1, %4.1 +// CHECK-LABEL: End +sil private @testPendingMerge : $@convention(thin) (@owned VariableNode) -> (@out ASTNode, @error Error) { +bb0(%0 : $*ASTNode, %1 : $VariableNode): + %9 = alloc_stack $Optional, var, name "documentation" + cond_br undef, bb12, bb14 + +bb12: + %113 = alloc_stack $AssignmentNode, let, name "assign" + %115 = alloc_stack $ASTNode + retain_value %1 : $VariableNode + %117 = init_existential_addr %115 : $*ASTNode, $VariableNode + store %1 to %117 : $*VariableNode + %122 = load %9 : $*Optional + + %123 = function_ref @testPendingMergeHelper : $@convention(method) (@in ASTNode, @owned Optional) -> (@out AssignmentNode, @error Error) + try_apply %123(%113, %115, %122) : $@convention(method) (@in ASTNode, @owned Optional) -> (@out AssignmentNode, @error Error), normal bb13, error bb65 + +bb13(%125 : $()): + dealloc_stack %115 : $*ASTNode + %128 = init_existential_addr %0 : $*ASTNode, $AssignmentNode + copy_addr %113 to [initialization] %128 : $*AssignmentNode + destroy_addr %113 : $*AssignmentNode + dealloc_stack %113 : $*AssignmentNode + release_value %1 : $VariableNode + dealloc_stack %9 : $*Optional + br bb61 + +bb14: + destroy_addr %9 : $*Optional + %584 = init_existential_addr %0 : $*ASTNode, $VariableNode + store %1 to %584 : $*VariableNode + dealloc_stack %9 : $*Optional + br bb61 + +bb61: + %589 = tuple () + return %589 : $() + + +bb65(%614 : $Error): + dealloc_stack %115 : $*ASTNode + dealloc_stack %113 : $*AssignmentNode + dealloc_stack %9 : $*Optional + br bb72(undef : $Error) + + +bb72(%681 : $Error): + throw %681 : $Error +} + +//============================================================================= +// Test an "array.uninitialized" with multiple tuple_extract's but no +// other unusual uses. Make sure canOptimizeArrayUninitializedCall +// returns false; otherwise graph verification can fail because only +// one of the tuple_extracts is mapped to a node. + +// specialized static Array._adoptStorage(_:count:) +sil [_semantics "array.uninitialized"] @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo5Int64V_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + +// CHECK-LABEL: CG of testArrayUninitResultMapping +// CHECK-NEXT: [ref] %0 Esc: , Succ: (%0.1) +// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2) +// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ: +// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1) +// CHECK-NEXT: Con %2.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testArrayUninitResultMapping : $@convention(thin) () -> () { +bb0: + %0 = alloc_ref [tail_elems $Int64 * undef : $Builtin.Word] $_ContiguousArrayStorage + // function_ref specialized static Array._adoptStorage(_:count:) + %1 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo5Int64V_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %2 = apply %1(%0, undef, undef) : $@convention(method) (@owned _ContiguousArrayStorage, Int, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) + %3 = tuple_extract %2 : $(Array, UnsafeMutablePointer), 0 + %4 = tuple_extract %2 : $(Array, UnsafeMutablePointer), 1 + %5 = tuple_extract %2 : $(Array, UnsafeMutablePointer), 0 + %6 = struct_extract %5 : $Array, #Array._buffer + %7 = struct_extract %6 : $_ArrayBuffer, #_ArrayBuffer._storage + %8 = struct_extract %7 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue + strong_retain %8 : $Builtin.BridgeObject + %10 = tuple () + return %10 : $() +} diff --git a/test/SILOptimizer/exclusivity_static_diagnostics.swift b/test/SILOptimizer/exclusivity_static_diagnostics.swift index f43769006137f..96afff273e1a5 100644 --- a/test/SILOptimizer/exclusivity_static_diagnostics.swift +++ b/test/SILOptimizer/exclusivity_static_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization import Swift diff --git a/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift b/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift index 0dea98c01772f..a2403ab2d67c9 100644 --- a/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift +++ b/test/SILOptimizer/exclusivity_static_diagnostics_objc.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/optional_closure_bridging.h -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -import-objc-header %S/Inputs/optional_closure_bridging.h -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // REQUIRES: objc_interop import Foundation diff --git a/test/SILOptimizer/existential_transform_extras.sil b/test/SILOptimizer/existential_transform_extras.sil index 2369fd26d4be4..e22977d3a4314 100644 --- a/test/SILOptimizer/existential_transform_extras.sil +++ b/test/SILOptimizer/existential_transform_extras.sil @@ -203,3 +203,61 @@ sil_witness_table hidden Klass1: P module dealloc { sil_witness_table hidden Klass2: P module dealloc { method #P.foo!1: (Self) -> () -> Int32 : nil } + +// ----------------------------------------------------------------------------- +// Test composite conformances with superclass constraints where one of +// the protocol constraints is satisfied by the superclass constraint. +// +// "Assertion failed: (conformances.size() +// == numConformanceRequirements)" in ExistentialSpecializer on +protocol Plotable {} + +class PlotLayer : Plotable { + init() +} + +protocol InView {} + +class PlotLayerInView : PlotLayer & InView { + override init() +} + +class PlotView { + @_hasStorage @_hasInitialValue var layers: Container { get set } + public func resolveLayers() + init() +} + +struct Container { + @_hasStorage @_hasInitialValue var val: T { get set } +} + +// Check that the init_existential instruction was created with all +// three requirements (the generic parameter only has two +// requirements). Relies on assertions during specialization and on +// the SILVerifier to catch other inconsistencies. +// +// CHECK-LABEL: sil shared @$s40testExistentialSpecializeCompositeHelperTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : PlotLayer, τ_0_0 : InView> (@owned τ_0_0, @inout Container) -> () { +// CHECK: bb0(%0 : $τ_0_0, %1 : $*Container): +// CHECK: init_existential_ref %0 : $τ_0_0 : $τ_0_0, $PlotLayer & InView & Plotable +// CHECK-LABEL: } // end sil function '$s40testExistentialSpecializeCompositeHelperTf4en_n' +sil shared @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container) -> () { +bb0(%0 : $PlotLayer & Plotable & InView, %1 : $*Container): + %adr = struct_element_addr %1 : $*Container, #Container.val + store %0 to %adr : $*PlotLayer & Plotable & InView + %v = tuple () + return %v : $() +} + +sil @testExistentialSpecializeComposite : $@convention(method) (@guaranteed PlotView) -> () { +bb0(%0 : $PlotView): + %ref = alloc_ref $PlotLayerInView + strong_retain %ref : $PlotLayerInView + %exis = init_existential_ref %ref : $PlotLayerInView : $PlotLayerInView, $PlotLayer & Plotable & InView + %array = ref_element_addr %0 : $PlotView, #PlotView.layers + %f = function_ref @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container) -> () + %call = apply %f(%exis, %array) : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container) -> () + strong_release %ref : $PlotLayerInView + %v = tuple () + return %v : $() +} diff --git a/test/SILOptimizer/generalized_accessors.swift b/test/SILOptimizer/generalized_accessors.swift index d22735c501093..a0faa6159d7a8 100644 --- a/test/SILOptimizer/generalized_accessors.swift +++ b/test/SILOptimizer/generalized_accessors.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization // // Tests for yield-once diagnostics emitted for generalized accessors. diff --git a/test/SILOptimizer/globalopt_resilience_testing.swift b/test/SILOptimizer/globalopt_resilience_testing.swift new file mode 100644 index 0000000000000..4d0b1c3a5162e --- /dev/null +++ b/test/SILOptimizer/globalopt_resilience_testing.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -emit-sil -O -enable-library-evolution -primary-file %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -O -enable-library-evolution -enable-testing -primary-file %s | %FileCheck %s --check-prefix=CHECK-TESTING + +// If a global variable with a resilient type has public linkage, we have to +// allocate a buffer for it even if the type has a fixed size in its +// defining module. +// +// There are two cases where this can occur: +// +// - An internal property is defined in a resilient module built with +// -enable-testing +// +// - A public property is defined to have the @_fixed_layout attribute + +public struct Wrapper { + var x: Int32 + + static let usefulConstant = Wrapper(x: 321) +} + +// CHECK-LABEL: sil_global hidden [let] @$s28globalopt_resilience_testing7WrapperV14usefulConstantACvpZ : $Wrapper = { +// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 321 +// CHECK-NEXT: %1 = struct $Int32 (%0 : $Builtin.Int32) +// CHECK-NEXT: %initval = struct $Wrapper (%1 : $Int32) +// CHECK-NEXT: } + +// CHECK-TESTING-LABEL: sil_global [let] @$s28globalopt_resilience_testing7WrapperV14usefulConstantACvpZ : $Wrapper{{$}} diff --git a/test/SILOptimizer/guaranteed_arc_opts_qualified.sil b/test/SILOptimizer/guaranteed_arc_opts_qualified.sil index 22efc57298ec2..f328cb8bb26ff 100644 --- a/test/SILOptimizer/guaranteed_arc_opts_qualified.sil +++ b/test/SILOptimizer/guaranteed_arc_opts_qualified.sil @@ -181,4 +181,4 @@ bb0(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject): strong_release %0 : $Builtin.NativeObject %9999 = tuple() return %9999 : $() -} \ No newline at end of file +} diff --git a/test/SILOptimizer/infinite_recursion.swift b/test/SILOptimizer/infinite_recursion.swift index bf71f04c5adf5..00c1bd94b0178 100644 --- a/test/SILOptimizer/infinite_recursion.swift +++ b/test/SILOptimizer/infinite_recursion.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify +// RUN: %target-swift-frontend -emit-sil -primary-file %s -o /dev/null -verify -enable-ownership-stripping-after-serialization func a() { // expected-warning {{all paths through this function will call itself}} a() diff --git a/test/SILOptimizer/inline_cache_and_arc.swift b/test/SILOptimizer/inline_cache_and_arc.swift index 0601f30466049..f251ee07fd56f 100644 --- a/test/SILOptimizer/inline_cache_and_arc.swift +++ b/test/SILOptimizer/inline_cache_and_arc.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -parse-as-library -O -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -parse-as-library -enable-spec-devirt -O -emit-sil %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib,CPU=x86_64 // Test inline cache with a global class. Make sure the retain|release pair @@ -19,7 +19,6 @@ public func testit() { // CHECK-LABEL: sil @{{.*}}testityyF // CHECK: bb0: // CHECK-NOT: {{.*(retain|release).*}} -// CHECK: checked_cast_br // CHECK: bb1{{.*}}: // CHECK-NOT: {{.*(retain|release).*}} // CHECK: return diff --git a/test/SILOptimizer/inline_caches.sil b/test/SILOptimizer/inline_caches.sil index 525d375dcfd5a..b98535f72a0d5 100644 --- a/test/SILOptimizer/inline_caches.sil +++ b/test/SILOptimizer/inline_caches.sil @@ -17,7 +17,7 @@ sil @_TFC8testcase3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned sil @_TF8testcase7my_mainFCS_3FooT_ : $@convention(thin) (@owned Foo) -> () { bb0(%0 : $Foo): -//CHECK: checked_cast_br [exact] %0 : $Foo to $Foo +//CHECK: checked_cast_br [exact] %0 : $Foo to Foo //CHECK: function_ref @_TFC8testcase3Foo4pingfS0_FT_T_ //CHECK: apply @@ -52,7 +52,7 @@ sil @_TZFC4spec1C3barfMS0_FT_T_ : $@convention(method) (@thick C.Type) -> () { // CHECK: bb0 bb0(%0 : $@thick C.Type): // CHECK-NOT: class_method - // CHECK: checked_cast_br [exact] %0 : $@thick C.Type to $@thick C.Type, bb2, bb3 + // CHECK: checked_cast_br [exact] %0 : $@thick C.Type to @thick C.Type, bb2, bb3 // CHECK: bb1 // CHECK: return // CHECK: bb2 diff --git a/test/SILOptimizer/inlinecaches_arc.sil b/test/SILOptimizer/inlinecaches_arc.sil index 2a69333c04db1..a607107345e2d 100644 --- a/test/SILOptimizer/inlinecaches_arc.sil +++ b/test/SILOptimizer/inlinecaches_arc.sil @@ -15,7 +15,7 @@ sil @_TFC4main3FoocfMS0_FT_S0_ : $@convention(method) (@owned Foo) -> @owned Foo // CHECK-LABEL: _TF4main7my_mainFCS_3FooSi // CHECK: bb0 // CHECK-NOT: strong_retain -// CHECK: checked_cast_br [exact] %0 : $Foo to $Foo +// CHECK: checked_cast_br [exact] %0 : $Foo to Foo // CHECK: bb1 // CHECK: class_method diff --git a/test/SILOptimizer/invalid_escaping_captures.swift b/test/SILOptimizer/invalid_escaping_captures.swift index caa89e0f47a25..ae9ceb780ce3b 100644 --- a/test/SILOptimizer/invalid_escaping_captures.swift +++ b/test/SILOptimizer/invalid_escaping_captures.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil %s -verify +// RUN: %target-swift-frontend -emit-sil %s -verify -enable-ownership-stripping-after-serialization func takesEscaping(_: @escaping () -> ()) {} @@ -53,7 +54,7 @@ func badLocalFunctionCaptureInOut1(x: inout Int) { // expected-note {{parameter x += 1 // expected-note {{captured here}} } - takesEscaping(local) // expected-error {{escaping closure captures 'inout' parameter 'x'}} + takesEscaping(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}} } func badLocalFunctionCaptureInOut2(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}} @@ -75,7 +76,7 @@ func badLocalFunctionCaptureInOut3(x: inout Int) { // expected-note {{parameter local1() // expected-note {{captured indirectly by this call}} } - takesEscaping(local2) // expected-error {{escaping closure captures 'inout' parameter 'x'}} + takesEscaping(local2) // expected-error {{escaping local function captures 'inout' parameter 'x'}} } func badLocalFunctionCaptureNoEscape1(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}} @@ -83,7 +84,7 @@ func badLocalFunctionCaptureNoEscape1(y: () -> ()) { // expected-note {{paramete y() // expected-note {{captured here}} } - takesEscaping(local) // expected-error {{escaping closure captures non-escaping parameter 'y'}} + takesEscaping(local) // expected-error {{escaping local function captures non-escaping parameter 'y'}} } func badLocalFunctionCaptureNoEscape2(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}} @@ -105,7 +106,7 @@ func badLocalFunctionCaptureNoEscape3(y: () -> ()) { // expected-note {{paramete local1() // expected-note {{captured indirectly by this call}} } - takesEscaping(local2) // expected-error {{escaping closure captures non-escaping parameter 'y'}} + takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}} } func badLocalFunctionCaptureNoEscape4(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}} @@ -117,7 +118,7 @@ func badLocalFunctionCaptureNoEscape4(y: () -> ()) { // expected-note {{paramete local1() // expected-note {{captured indirectly by this call}} } - takesEscaping(local2) // expected-error {{escaping closure captures non-escaping parameter 'y'}} + takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}} } // Capturing 'self' produces a different diagnostic. @@ -151,7 +152,7 @@ func testGenericLocalFunctionReabstraction(x: inout Int) { // expected-note {{pa x += 1 // expected-note {{captured here}} return 0 } - takesEscapingGeneric(local) // expected-error {{escaping closure captures 'inout' parameter 'x'}} + takesEscapingGeneric(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}} } // Make sure that withoutActuallyEscaping counts as a safe use. diff --git a/test/SILOptimizer/late_release_hoisting.sil b/test/SILOptimizer/late_release_hoisting.sil new file mode 100644 index 0000000000000..9fd9653b12c26 --- /dev/null +++ b/test/SILOptimizer/late_release_hoisting.sil @@ -0,0 +1,234 @@ +// RUN: %target-sil-opt -enable-sil-verify-all -late-release-hoisting %s | %FileCheck %s + +import Builtin +import Swift + +sil_stage canonical + +//===----------------------------------------------------------------------===// +// Unit tests for reachability to and from local objects. +//===----------------------------------------------------------------------===// + +class HasObj { + var o : AnyObject +} + +class HasInt64 { + var i : Int64 +} + +class HasHasObj { + var ho : HasObj +} + +// The release of %lo can be hoisted over the load because it does not +// point to any escaping references. +// +// CHECK-LABEL: sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasInt +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: strong_release [[LO]] : $HasInt +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalNotReachesEscaped' +sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + + %oadr = ref_element_addr %1 : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasInt64 + return %o : $AnyObject +} + +// The release of %lo cannot be hoisted over the load. +// +// CHECK-LABEL: sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[IADR]] : $*AnyObject +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release [[LO]] : $HasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscaped' +sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %lo = alloc_ref $HasObj + %iadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %iadr : $*AnyObject + + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + strong_release %lo : $HasObj + return %o : $AnyObject +} + +// The release of %lo can be hoisted above the load from %oadr. There +// is a points-to relation between %lo and %oadr. However, the +// points-to path from %lo to %oadr reaches another reference counted +// object before reaching $oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +// CHECK: bb0(%0 : $HasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[LO]] : $HasHasObj, #HasHasObj.ho +// CHECK: strong_retain %0 : $HasObj +// CHECK: store %0 to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[LO]] : $HasHasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCEscaped' +sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject { +bb0(%0 : $HasObj): + %lo = alloc_ref $HasHasObj + %hoadr = ref_element_addr %lo : $HasHasObj, #HasHasObj.ho + strong_retain %0 : $HasObj + store %0 to %hoadr : $*HasObj + %ho = load %hoadr : $*HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + %o = load %oadr : $*AnyObject + // Normally a retain of %o would precede this release of %c. But + // let's assume some aggressive optimization happened. + strong_release %lo : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. +// +// TODO: We do not currently hoist strong_release %hho above +// strong_retain %o because %hho is marked as escaping. However, it is +// only stored to another local object, so in theory it does not need +// to be marked escaping. +// +// CHECK-LABEL: sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesEscapingLocal' +sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_retain %o : $AnyObject + strong_release %hho : $HasHasObj + return %o : $AnyObject +} + +// Two local references, one reachable from the other. We assume that +// the reachable one has its own reference count and hoist +// strong_release %hho above load %oadr. +// +// CHECK-LABEL: sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o +// CHECK: strong_retain %0 : $AnyObject +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj +// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj +// CHECK: strong_release [[HHO]] : $HasHasObj +// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o +// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject +// CHECK: strong_retain [[O]] : $AnyObject +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testLocalReachesRCLocal' +sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject { +bb0(%0 : $AnyObject): + %ho = alloc_ref $HasObj + %oadr = ref_element_addr %ho : $HasObj, #HasObj.o + strong_retain %0 : $AnyObject + store %0 to %oadr : $*AnyObject + + %hho = alloc_ref $HasHasObj + %hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho + store %ho to %hoadr : $*HasObj + + %ho2 = load %hoadr : $*HasObj + %oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o + %o = load %oadr2 : $*AnyObject + strong_release %hho : $HasHasObj + strong_retain %o : $AnyObject + return %o : $AnyObject +} + +// Hoist the release of an escaping object above memory operations on +// a local object that never escapes. +// +// CHECK-LABEL: sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +// CHECK: bb0(%0 : $Int64, %1 : $HasObj): +// CHECK: strong_release %1 : $HasObj +// CHECK: [[LO:%.*]] = alloc_ref $HasInt64 +// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i +// CHECK: store %0 to [[IADR]] : $*Int64 +// CHECK: [[I:%.*]] = load [[IADR]] : $*Int64 +// CHECK: return [[I]] : $Int64 +// CHECK-LABEL: } // end sil function 'testEscapeNotReachesLocal' +sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 { +bb0(%0 : $Int64, %1 : $HasObj): + %lo = alloc_ref $HasInt64 + %iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i + store %0 to %iadr : $*Int64 + %i = load %iadr : $*Int64 + strong_release %1 : $HasObj + return %i : $Int64 +} + +// EscapeAnalysis must consider the local object %lo as globally +// escaping because it is stored into an object that escapes via an +// incoming argument. This creates an aliasing relationship preventing +// the strong_release from being hoisted. +// +// CHECK-LABEL: sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +// CHECK: bb0(%0 : $AnyObject, %1 : $HasHasObj): +// CHECK: [[LO:%.*]] = alloc_ref $HasObj +// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o +// CHECK: store %0 to [[OADR]] : $*AnyObject +// CHECK: [[HOADR:%.*]] = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho +// CHECK: store [[LO]] to [[HOADR]] : $*HasObj +// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject +// CHECK: strong_release %1 : $HasHasObj +// CHECK: return [[O]] : $AnyObject +// CHECK-LABEL: } // end sil function 'testEscapeReachesLocal' +sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject { +bb0(%0 : $AnyObject, %1 : $HasHasObj): + %lo = alloc_ref $HasObj + %oadr = ref_element_addr %lo : $HasObj, #HasObj.o + store %0 to %oadr : $*AnyObject + %hoadr = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho + store %lo to %hoadr : $*HasObj + %o = load %oadr : $*AnyObject + strong_release %1 : $HasHasObj + return %o : $AnyObject +} diff --git a/test/SILOptimizer/latecodemotion.sil b/test/SILOptimizer/latecodemotion.sil index a2ecdc7c825b4..1642b5e3b2d21 100644 --- a/test/SILOptimizer/latecodemotion.sil +++ b/test/SILOptimizer/latecodemotion.sil @@ -722,7 +722,7 @@ bb1: strong_retain %17 : $Test %19 = class_method %17 : $Test, #Test.testing!1 : (Test) -> () -> UInt64, $@convention(method) (@guaranteed Test) -> UInt64 %20 = load %7 : $*UInt64 - checked_cast_br [exact] %17 : $Test to $Test, bb3, bb4 + checked_cast_br [exact] %17 : $Test to Test, bb3, bb4 bb2: // CHECK: bb2: // CHECK-NOT: strong_retain @@ -1381,7 +1381,7 @@ bb0(%0 : $Builtin.NativeObject, %1: $Builtin.NativeObject): strong_retain %0: $Builtin.NativeObject %2 = function_ref @blocker : $@convention(thin) () -> () apply %2() : $@convention(thin) () -> () - %3 = unconditional_checked_cast %0 : $Builtin.NativeObject to $B + %3 = unconditional_checked_cast %0 : $Builtin.NativeObject to B strong_release %0: $Builtin.NativeObject %5 = tuple() return %5 : $() @@ -1397,7 +1397,7 @@ bb0(%0 : $Builtin.NativeObject, %1: $Builtin.NativeObject): strong_retain %0: $Builtin.NativeObject %2 = function_ref @blocker : $@convention(thin) () -> () apply %2() : $@convention(thin) () -> () - %c = unconditional_checked_cast_value %0 : $Builtin.NativeObject to $B + %c = unconditional_checked_cast_value Builtin.NativeObject in %0 : $Builtin.NativeObject to B strong_release %0: $Builtin.NativeObject %5 = tuple() return %5 : $() diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index f1deec36d3c0b..92b3933df1a9c 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -184,12 +184,14 @@ bb2: return %r1 : $() } +sil @use_addr : $@convention(thin) (@inout Int32) -> () + // CHECK-LABEL: sil @dont_hoist_aliased_stack_location // CHECK: {{^}}bb0 // CHECK-NOT: load // CHECK: {{^}}bb1: // CHECK: store -// CHECK: load +// CHECK: apply // CHECK: {{^}}bb2: // CHECK: return sil @dont_hoist_aliased_stack_location : $@convention(thin) (Int32) -> () { @@ -199,7 +201,8 @@ bb0(%0 : $Int32): bb1: store %0 to %313 : $*Int32 - %l1 = load %313 : $*Int32 + %f = function_ref @use_addr : $@convention(thin) (@inout Int32) -> () + %a = apply %f(%313) : $@convention(thin) (@inout Int32) -> () cond_br undef, bb1, bb2 bb2: @@ -351,3 +354,210 @@ bb4: %10 = tuple () return %10 : $() } + +// CHECK-LABEL: sil @hoist_load_and_store +// CHECK: [[V1:%[0-9]+]] = load %0 +// CHECK: br bb1([[V1]] : $Int32) +// CHECK: bb1([[V2:%[0-9]+]] : $Int32): +// CHECK-NOT: load +// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] +// CHECK: "sadd_with_overflow_Int64"([[E]] +// CHECK: [[V3:%[0-9]+]] = struct $Int32 +// CHECK-NOT: store +// CHECK: bb2: +// CHECK: br bb1([[V3]] : $Int32) +// CHECK: bb3: +// CHECK: store [[V3]] to %0 +// CHECK: } // end sil function 'hoist_load_and_store' +sil @hoist_load_and_store : $@convention(thin) (@inout Int32, Int32) -> () { +bb0(%0 : $*Int32, %1 : $Int32): + %8 = struct_element_addr %0 : $*Int32, #Int32._value + %9 = struct_extract %1 : $Int32, #Int32._value + %10 = integer_literal $Builtin.Int1, 0 + br bb1 + +bb1: + %17 = load %8 : $*Builtin.Int32 + %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 + %20 = struct $Int32 (%19 : $Builtin.Int32) + store %20 to %0 : $*Int32 + cond_br undef, bb2, bb3 + +bb2: + br bb1 + +bb3: + %12 = tuple () + return %12 : $() +} + +// Just make sure the optimizer does not crash in case the operand of the +// store is the load itself. +sil @hoist_load_and_redundant_store : $@convention(thin) (@inout Int32) -> () { +bb0(%0 : $*Int32): + br bb1 + +bb1: + %1 = load %0 : $*Int32 + store %1 to %0 : $*Int32 + cond_br undef, bb2, bb3 + +bb2: + br bb1 + +bb3: + %12 = tuple () + return %12 : $() +} + +// CHECK-LABEL: sil @hoist_load_and_two_stores +// CHECK: [[V1:%[0-9]+]] = load %0 +// CHECK: br bb1([[V1]] : $Int32) +// CHECK: bb1([[V2:%[0-9]+]] : $Int32): +// CHECK-NOT: load +// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] +// CHECK: "sadd_with_overflow_Int64"([[E]] +// CHECK: [[V3:%[0-9]+]] = struct $Int32 +// CHECK: bb2: +// CHECK-NOT: store +// CHECK: br bb4([[V3]] : $Int32) +// CHECK: bb3: +// CHECK-NOT: store +// CHECK: br bb4([[V3]] : $Int32) +// CHECK: bb4([[V4:%[0-9]+]] : $Int32): +// CHECK: cond_br +// CHECK: bb5: +// CHECK: br bb1([[V4]] : $Int32) +// CHECK: bb6: +// CHECK: store [[V4]] to %0 +// CHECK: } // end sil function 'hoist_load_and_two_stores' +sil @hoist_load_and_two_stores : $@convention(thin) (@inout Int32, Int32) -> () { +bb0(%0 : $*Int32, %1 : $Int32): + %8 = struct_element_addr %0 : $*Int32, #Int32._value + %9 = struct_extract %1 : $Int32, #Int32._value + %10 = integer_literal $Builtin.Int1, 0 + br bb1 + +bb1: + %17 = load %8 : $*Builtin.Int32 + %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 + %20 = struct $Int32 (%19 : $Builtin.Int32) + cond_br undef, bb2, bb3 +bb2: + store %20 to %0 : $*Int32 + br bb4 +bb3: + store %20 to %0 : $*Int32 + br bb4 +bb4: + cond_br undef, bb5, bb6 + +bb5: + br bb1 + +bb6: + %12 = tuple () + return %12 : $() +} + +// CHECK-LABEL: sil @dont_hoist_stores_not_dominating_exit +// CHECK: bb0(%0 : $*Int32, %1 : $Int32): +// CHECK-NOT: load +// CHECK: bb1: +// CHECK: load +// CHECK: bb3: +// CHECK: store +// CHECK: bb4: +// CHECK: bb6: +// CHECK-NOT: store +// CHECK: } // end sil function 'dont_hoist_stores_not_dominating_exit' +sil @dont_hoist_stores_not_dominating_exit : $@convention(thin) (@inout Int32, Int32) -> () { +bb0(%0 : $*Int32, %1 : $Int32): + %8 = struct_element_addr %0 : $*Int32, #Int32._value + %9 = struct_extract %1 : $Int32, #Int32._value + %10 = integer_literal $Builtin.Int1, 0 + br bb1 + +bb1: + %17 = load %8 : $*Builtin.Int32 + %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 + %20 = struct $Int32 (%19 : $Builtin.Int32) + cond_br undef, bb2, bb3 +bb2: + br bb4 +bb3: + store %20 to %0 : $*Int32 + br bb4 +bb4: + cond_br undef, bb5, bb6 + +bb5: + br bb1 + +bb6: + %12 = tuple () + return %12 : $() +} + +// CHECK-LABEL: sil @hoist_loads_and_stores_multiple_exits +// CHECK: [[V1:%[0-9]+]] = load %0 +// CHECK: br bb1([[V1]] : $Int32) +// CHECK: bb1([[V2:%[0-9]+]] : $Int32): +// CHECK-NOT: load +// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] +// CHECK: "sadd_with_overflow_Int64"([[E]] +// CHECK: [[V3:%[0-9]+]] = struct $Int32 +// CHECK: bb2: +// CHECK-NOT: store +// CHECK: cond_br undef, bb1([[V3]] : $Int32), bb3 +// CHECK: bb3: +// CHECK: store [[V3]] to %0 +// CHECK: br bb6 +// CHECK: bb4: +// CHECK-NOT: store +// CHECK: cond_br undef, bb1([[V3]] : $Int32), bb5 +// CHECK: bb5: +// CHECK: store [[V3]] to %0 +// CHECK: br bb6 +// CHECK: bb6: +// CHECK: } // end sil function 'hoist_loads_and_stores_multiple_exits' +sil @hoist_loads_and_stores_multiple_exits : $@convention(thin) (@inout Int32, Int32) -> () { +// %0 // users: %14, %17, %5, %2 +// %1 // user: %3 +bb0(%0 : $*Int32, %1 : $Int32): + %2 = struct_element_addr %0 : $*Int32, #Int32._value + %3 = struct_extract %1 : $Int32, #Int32._value // user: %9 + %4 = integer_literal $Builtin.Int1, 0 // user: %9 + %5 = load %0 : $*Int32 // user: %6 + br bb1(%5 : $Int32) // id: %6 + +// %7 // user: %8 +bb1(%7 : $Int32): // Preds: bb0 bb2 bb4 + %8 = struct_extract %7 : $Int32, #Int32._value // user: %9 + %9 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int32, %3 : $Builtin.Int32, %4 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %10 + %10 = tuple_extract %9 : $(Builtin.Int32, Builtin.Int1), 0 // user: %11 + %11 = struct $Int32 (%10 : $Builtin.Int32) // users: %14, %17, %13, %16 + cond_br undef, bb2, bb4 // id: %12 + +bb2: // Preds: bb1 + cond_br undef, bb1(%11 : $Int32), bb3 // id: %13 + +bb3: // Preds: bb2 + store %11 to %0 : $*Int32 // id: %14 + br bb6 // id: %15 + +bb4: // Preds: bb1 + cond_br undef, bb1(%11 : $Int32), bb5 // id: %16 + +bb5: // Preds: bb4 + store %11 to %0 : $*Int32 // id: %17 + br bb6 // id: %18 + +bb6: // Preds: bb3 bb5 + %19 = tuple () // user: %20 + return %19 : $() // id: %20 +} // end sil function 'hoist_loads_and_stores' + diff --git a/test/SILOptimizer/licm_apply.sil b/test/SILOptimizer/licm_apply.sil index 376e695e3e017..0f6a3ae31b78a 100644 --- a/test/SILOptimizer/licm_apply.sil +++ b/test/SILOptimizer/licm_apply.sil @@ -14,10 +14,16 @@ bb0(%0 : $*Int64, %1 : $Int64): } //CHECK-LABEL: sil @licm_readonly_apply -//CHECK: %{{[0-9]+}} = apply -//CHECK: bb1: +//CHECK: [[V1:%[0-9]+]] = load +//CHECK: %{{[0-9]+}} = apply +//CHECK: br bb1([[V1]] : $Int64) +//CHECK: bb1({{.*}} : $Int64): //CHECK-NOT: {{ apply}} -//CHECK: return +//CHECK-NOT: load +//CHECK-NOT: store +//CHECK: bb2: +//CHECK: store +//CHECK: return sil @licm_readonly_apply : $@convention(thin) (Int64, @inout Int64) -> () { bb0(%0 : $Int64, %1 : $*Int64): %2 = alloc_stack $Int64 diff --git a/test/SILOptimizer/licm_exclusivity.sil b/test/SILOptimizer/licm_exclusivity.sil index ebe67b8a8d49b..6e7a5fd821cb7 100644 --- a/test/SILOptimizer/licm_exclusivity.sil +++ b/test/SILOptimizer/licm_exclusivity.sil @@ -186,9 +186,9 @@ bb2: // CHECK: [[GLOBALY:%.*]] = global_addr @globalY : $*X // CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X // CHECK: [[BEGINY:%.*]] = begin_access [read] [dynamic] [[GLOBALY]] : $*X +// CHECK-NEXT: load [[BEGIN]] +// CHECK-NEXT: load [[BEGINY]] // CHECK-NEXT: br bb1 -// CHECK: load -// CHECK: load // CHECK: cond_br // CHECK: bb2 // CHECK: end_access [[BEGINY]] diff --git a/test/SILOptimizer/licm_multiend.sil b/test/SILOptimizer/licm_multiend.sil index d1c4c7f888292..260b63061f05e 100644 --- a/test/SILOptimizer/licm_multiend.sil +++ b/test/SILOptimizer/licm_multiend.sil @@ -26,20 +26,20 @@ sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage // CHECK: bb2: // CHECK: [[GLOBALVAR:%.*]] = global_addr @$s3tmp1xSivp : $*Int // CHECK: [[BEGINA:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]] : $*Int -// CHECK-NEXT: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64) +// CHECK: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64) // CHECK: [[LOOPH]]({{.*}} : $Builtin.Int64) // CHECK: cond_br {{.*}}, [[LOOPCOND1:bb[0-9]+]], [[LOOPCOND2:bb[0-9]+]] // CHECK: [[LOOPCOND1]]: -// CHECK-NEXT: store // CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT1:bb[0-9]+]], [[LOOPCONT1:bb[0-9]+]] // CHECK: [[LOOPEXIT1]]: +// CHECK-NEXT: store // CHECK-NEXT: end_access [[BEGINA]] : $*Int // CHECK-NEXT: br [[LOOPAFTEREXIT:bb[0-9]+]] // CHECK: [[LOOPCOND2]]: // CHECK-NEXT: struct $Int -// CHECK-NEXT: store // CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT2:bb[0-9]+]], [[LOOPCONT1]] // CHECK: [[LOOPEXIT2]]: +// CHECK-NEXT: store // CHECK-NEXT: end_access [[BEGINA]] : $*Int // CHECK-NEXT: br [[LOOPAFTEREXIT]] // CHECK: [[LOOPCONT1]]: @@ -71,7 +71,7 @@ bb0: cond_br %16, bb1, bb2 bb1: - br bbRet + br bb12 bb2: %19 = global_addr @$s3tmp1xSivp : $*Int @@ -101,14 +101,14 @@ bb4(%27 : $Builtin.Int64): %global = begin_access [modify] [dynamic] [no_nested_conflict] %19 : $*Int %46 = builtin "cmp_eq_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1 %47 = builtin "int_expect_Int1"(%46 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1 - cond_br %47, bbend1, bbend2 - -bbend1: + cond_br %47, bb10, bb11 + +bb10: store %43 to %global : $*Int end_access %global : $*Int cond_br %47, bb6, bb5 - -bbend2: + +bb11: %otherInt = struct $Int (%27 : $Builtin.Int64) store %otherInt to %global : $*Int end_access %global : $*Int @@ -118,9 +118,9 @@ bb5: br bb4(%29 : $Builtin.Int64) bb6: - br bbRet - -bbRet: + br bb12 + +bb12: %25 = tuple () return %25 : $() } // end sil function 'multi_end_licm' diff --git a/test/SILOptimizer/lslocation_expansion.sil b/test/SILOptimizer/lslocation_expansion.sil index 28f01f40a1350..73b6251f36256 100644 --- a/test/SILOptimizer/lslocation_expansion.sil +++ b/test/SILOptimizer/lslocation_expansion.sil @@ -105,12 +105,12 @@ bb0(%0 : $B): // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: [[RET0:%.+]] = alloc_stack // CHECK-NEXT: Projection Path [$*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil @store_after_store_struct : $@convention(thin) () -> () { %1 = alloc_stack $S1 %9 = integer_literal $Builtin.Int64, 0 // user: %10 @@ -130,95 +130,95 @@ sil @store_after_store_struct : $@convention(thin) () -> () { // CHECK-NEXT: alloc_stack $S2 // CHECK-NEXT: Projection Path [$*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S2 // CHECK-NEXT: Projection Path [$*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S3 // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK: #2 store // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var y: S3 in: $*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var y: S3 in: $*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var y: S3 in: $*S3 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var y: S3 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var y: S3 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var x: S3 in: $*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var x: S3 in: $*S3 // CHECK-NEXT: Field: var c: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var x: S3 in: $*S3 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var x: S3 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: alloc_stack $S4 // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var x: S3 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @many_struct_allocs : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S2, var, name "a" // users: %6, %18 diff --git a/test/SILOptimizer/lslocation_type_only_expansion.sil b/test/SILOptimizer/lslocation_type_only_expansion.sil index cb87590344466..1141d22685a02 100644 --- a/test/SILOptimizer/lslocation_type_only_expansion.sil +++ b/test/SILOptimizer/lslocation_type_only_expansion.sil @@ -83,22 +83,22 @@ sil @S6_init : $@convention(thin) (@thin S6.Type) -> S6 // CHECK: #0 store // CHECK-NEXT: Projection Path [$*S1 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK: #1 store // CHECK-NEXT: Projection Path [$*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @test_struct_type_expansion : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S1, var, name "a" // users: %5, %12 @@ -145,16 +145,16 @@ bb0: // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S3 // CHECK-NEXT: Field: var c: C1 in: $*C1] sil hidden @test_struct_and_class_slot : $@convention(thin) () -> () { @@ -179,20 +179,20 @@ bb0: // CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) // CHECK-NEXT: Index: 2 in: $*S1 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) // CHECK-NEXT: Index: 2 in: $*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) // CHECK-NEXT: Index: 1 in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S4 // CHECK-NEXT: Field: var c: (Int, Int, S1) in: $*(Int, Int, S1) // CHECK-NEXT: Index: 0 in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @test_tuple : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S4, var, name "x" // users: %4, %7 @@ -219,20 +219,20 @@ bb0: // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var b: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 // CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) // CHECK-NEXT: Index: 2 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var b: S1 in: $*S1 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 // CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) // CHECK-NEXT: Index: 2 in: $*S3 // CHECK-NEXT: Field: var a: S2 in: $*S2 // CHECK-NEXT: Field: var a: Int in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 // CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) // CHECK-NEXT: Index: 2 in: $*S3 @@ -240,11 +240,11 @@ bb0: // CHECK-NEXT: Projection Path [$*S5 // CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) // CHECK-NEXT: Index: 1 in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] // CHECK-NEXT: Projection Path [$*S5 // CHECK-NEXT: Field: var c: (Int, Int, S3) in: $*(Int, Int, S3) // CHECK-NEXT: Index: 0 in: $*Int -// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @tuple_test_with_reference : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S5, var, name "x" // users: %4, %7 @@ -264,11 +264,11 @@ bb0: /// CHECK-NEXT: Projection Path [$*S6 /// CHECK-NEXT: Field: var tuple: (Int, Int) in: $*(Int, Int) /// CHECK-NEXT: Index: 1 in: $*Int -/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +/// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] /// CHECK-NEXT: Projection Path [$*S6 /// CHECK-NEXT: Field: var tuple: (Int, Int) in: $*(Int, Int) /// CHECK-NEXT: Index: 0 in: $*Int -/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +/// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @tuple_inside_struct : $@convention(thin) () -> () { bb0: %0 = alloc_stack $S6, var, name "x" // users: %4, %7 @@ -292,7 +292,7 @@ bb0: /// CHECK-NOT: Int32 /// CHECK: #1 store /// CHECK-NEXT: Projection Path [$*Int -/// CHECK-NEXT: Field: var value: Int64 in: $*Builtin.Int64] +/// CHECK-NEXT: Field: var value: Builtin.Int64 in: $*Builtin.Int64] sil hidden @enum_test : $@convention(thin) () -> () { bb0: %0 = alloc_stack $Example, var, name "ee" // users: %5, %11 diff --git a/test/SILOptimizer/mandatory_inlining.sil b/test/SILOptimizer/mandatory_inlining.sil index f58e8c6ef945b..8d2ff7197050b 100644 --- a/test/SILOptimizer/mandatory_inlining.sil +++ b/test/SILOptimizer/mandatory_inlining.sil @@ -1401,3 +1401,30 @@ bb6: %tuple = tuple() return %tuple : $() } + +sil [transparent] @escaping_thunk : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { +bb0(%1 : $@callee_guaranteed () -> ()): + %9999 = tuple() + return %9999 : $() +} + +// Make sure that we do not crash here. +// +// CHECK-LABEL: sil @partial_apply_stack_failure : $@convention(thin) (@callee_guaranteed () -> ()) -> () { +// CHECK: } // end sil function 'partial_apply_stack_failure' +sil @partial_apply_stack_failure : $@convention(thin) (@callee_guaranteed () -> ()) -> () { +bb0(%0 : $@callee_guaranteed () -> ()): + %1 = alloc_stack $@callee_guaranteed () -> () + store %0 to %1 : $*@callee_guaranteed () -> () + %2 = load %1 : $*@callee_guaranteed () -> () + strong_retain %2 : $@callee_guaranteed () -> () + %thunk = function_ref @escaping_thunk : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () + %2a = partial_apply [callee_guaranteed] [on_stack] %thunk(%2) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () + %2b = mark_dependence %2a : $@noescape @callee_guaranteed () -> () on %2 : $@callee_guaranteed () -> () + apply %2b() : $@noescape @callee_guaranteed () -> () + dealloc_stack %2a : $@noescape @callee_guaranteed () -> () + destroy_addr %1 : $*@callee_guaranteed () -> () + dealloc_stack %1 : $*@callee_guaranteed () -> () + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/mandatory_inlining.swift b/test/SILOptimizer/mandatory_inlining.swift index 381e6a232ee83..7867ce7baee4f 100644 --- a/test/SILOptimizer/mandatory_inlining.swift +++ b/test/SILOptimizer/mandatory_inlining.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -sil-verify-all -primary-file %s -emit-sil -o - -verify | %FileCheck %s +// RUN: %target-swift-frontend -sil-verify-all -primary-file %s -emit-sil -o - -verify -enable-ownership-stripping-after-serialization // These tests are deliberately shallow, because I do not want to depend on the // specifics of SIL generation, which might change for reasons unrelated to this diff --git a/test/SILOptimizer/mandatory_inlining_circular.swift b/test/SILOptimizer/mandatory_inlining_circular.swift index 7954b506b0e96..4fac3a593e96b 100644 --- a/test/SILOptimizer/mandatory_inlining_circular.swift +++ b/test/SILOptimizer/mandatory_inlining_circular.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -sil-verify-all -emit-sil %s -o /dev/null -verify +// RUN: %target-swift-frontend -sil-verify-all -emit-sil %s -o /dev/null -verify -enable-ownership-stripping-after-serialization @_transparent func waldo(_ x: Double) -> Double { return fred(x); // expected-error {{inlining 'transparent' functions forms circular loop}} expected-note 1 {{while inlining here}} diff --git a/test/SILOptimizer/mandatory_inlining_ossa_to_non_ossa.sil b/test/SILOptimizer/mandatory_inlining_ossa_to_non_ossa.sil index 1dc396d4b8793..4f262f2c4046b 100644 --- a/test/SILOptimizer/mandatory_inlining_ossa_to_non_ossa.sil +++ b/test/SILOptimizer/mandatory_inlining_ossa_to_non_ossa.sil @@ -239,7 +239,7 @@ bb0(%0 : $Builtin.NativeObject): sil [transparent] [ossa] @term_ossa_callee : $@convention(thin) (@owned Builtin.NativeObject, @owned FakeOptional) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $FakeOptional): - checked_cast_br %0 : $Builtin.NativeObject to $Klass, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to Klass, bb1, bb2 bb1(%1a : @owned $Klass): destroy_value %1a : $Klass @@ -267,7 +267,7 @@ bb6: // CHECK-LABEL: sil @term_non_ossa_caller : $@convention(thin) (@owned Builtin.NativeObject, @owned FakeOptional) -> () { // CHECK: bb0([[ARG0:%.*]] : $Builtin.NativeObject, [[ARG1:%.*]] : $FakeOptional): -// CHECK: checked_cast_br [[ARG0]] : $Builtin.NativeObject to $Klass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] +// CHECK: checked_cast_br [[ARG0]] : $Builtin.NativeObject to Klass, [[YES:bb[0-9]+]], [[NO:bb[0-9]+]] // // CHECK: [[YES]]([[SUCC:%.*]] : // CHECK: release_value [[SUCC]] @@ -323,7 +323,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // ==> On success, we store the value into dest. The destroy is not from the // ==> optimizer, but from the code. @@ -379,7 +379,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : // ==> On success, we store into dest and destroy dest. @@ -434,7 +434,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : // CHECK-NEXT: strong_retain [[CAST_VALUE]] diff --git a/test/SILOptimizer/mandatory_inlining_ownership.sil b/test/SILOptimizer/mandatory_inlining_ownership.sil index 59720408b0f46..8f5429b2869dc 100644 --- a/test/SILOptimizer/mandatory_inlining_ownership.sil +++ b/test/SILOptimizer/mandatory_inlining_ownership.sil @@ -10,6 +10,11 @@ class C { init(i: Builtin.Int64) } +private class C2 { + var i: Builtin.Int64 { get set } + init(i: Builtin.Int64) +} + class Klass {} sil [transparent] [ossa] @calleeWithGuaranteed : $@convention(thin) (@guaranteed C) -> Builtin.Int64 { @@ -275,7 +280,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [take] [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // ==> On success, we store the value into dest. The destroy is not from the // ==> optimizer, but from the code. @@ -331,7 +336,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [take] [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : // ==> On success, we store into dest and destroy dest. @@ -387,7 +392,7 @@ bb3: // CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]] // CHECK-NEXT: [[RELOADED_ARG:%.*]] = load_borrow [[SRC_ADDR]] -// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] +// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]] // // CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : @guaranteed // CHECK-NEXT: [[CAST_VALUE_COPY:%.*]] = copy_value [[CAST_VALUE]] @@ -414,3 +419,168 @@ bb0(%0 : @owned $Builtin.NativeObject): %9999 = tuple() return %9999 : $() } + +/////////////////////// +// Begin Apply Tests // +/////////////////////// + +// Make sure that we do not violate any ownership invariants after inlining this +// code. + +sil @get_hidden_int_field_of_klass : $@convention(method) (@guaranteed Klass) -> Builtin.Int32 +sil @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> () + +sil [transparent] [ossa] @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 { +bb0(%0 : @guaranteed $Klass): + %2 = alloc_stack $Builtin.Int32 + %3 = function_ref @get_hidden_int_field_of_klass : $@convention(method) (@guaranteed Klass) -> Builtin.Int32 + %4 = apply %3(%0) : $@convention(method) (@guaranteed Klass) -> Builtin.Int32 + store %4 to [trivial] %2 : $*Builtin.Int32 + yield %2 : $*Builtin.Int32, resume bb1, unwind bb2 + +bb1: + %7 = load [trivial] %2 : $*Builtin.Int32 + %8 = function_ref @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> () + %9 = apply %8(%7, %0) : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> () + dealloc_stack %2 : $*Builtin.Int32 + %11 = tuple () + return %11 : $() + +bb2: + %13 = load [trivial] %2 : $*Builtin.Int32 + %14 = function_ref @int_klass_pair_user : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> () + %15 = apply %14(%13, %0) : $@convention(method) (Builtin.Int32, @guaranteed Klass) -> () + dealloc_stack %2 : $*Builtin.Int32 + unwind +} + +// CHECK-LABEL: sil [ossa] @begin_apply_caller : $@convention(method) (@guaranteed Klass) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_caller' +sil [ossa] @begin_apply_caller : $@convention(method) (@guaranteed Klass) -> @error Error { +bb0(%0 : @guaranteed $Klass): + %6 = copy_value %0 : $Klass + %12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + (%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + end_apply %14 + destroy_value %6 : $Klass + %19 = tuple () + return %19 : $() +} + +// CHECK-LABEL: sil [ossa] @begin_apply_caller_2 : $@convention(method) (@guaranteed Klass) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_caller_2' +sil [ossa] @begin_apply_caller_2 : $@convention(method) (@guaranteed Klass) -> @error Error { +bb0(%0 : @guaranteed $Klass): + %6 = copy_value %0 : $Klass + %12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + (%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + abort_apply %14 + destroy_value %6 : $Klass + %19 = tuple () + return %19 : $() +} + +// CHECK-LABEL: sil [ossa] @begin_apply_caller_3 : $@convention(method) (@guaranteed Klass) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_caller_3' +sil [ossa] @begin_apply_caller_3 : $@convention(method) (@guaranteed Klass) -> @error Error { +bb0(%0 : @guaranteed $Klass): + %6 = copy_value %0 : $Klass + %12 = function_ref @begin_apply_callee : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + (%13, %14) = begin_apply %12(%6) : $@yield_once @convention(method) (@guaranteed Klass) -> @yields @inout Builtin.Int32 + cond_br undef, bb1, bb2 + +bb1: + end_apply %14 + br bb3 + +bb2: + abort_apply %14 + br bb3 + +bb3: + destroy_value %6 : $Klass + %19 = tuple () + return %19 : $() +} + +sil [ossa] [transparent] @devirt_callee : $@yield_once @convention(method) (@guaranteed C) -> @yields @inout Builtin.Int64 { +bb0(%0 : @guaranteed $C): + %1 = alloc_stack $Builtin.Int64 + %1a = integer_literal $Builtin.Int64, 0 + store %1a to [trivial] %1 : $*Builtin.Int64 + yield %1 : $*Builtin.Int64, resume bb1, unwind bb2 + +bb1: + dealloc_stack %1 : $*Builtin.Int64 + %6 = tuple () + return %6 : $() + +bb2: + dealloc_stack %1 : $*Builtin.Int64 + unwind +} + +// Just make sure we actually inlined the begin_apply. We just want to make sure +// we are not breaking ownership invariants by not properly borrowing %0. +// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller : $@convention(method) (@owned C2) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_devirt_caller' +sil [ossa] @begin_apply_devirt_caller : $@convention(method) (@owned C2) -> @error Error { +bb0(%0 : @owned $C2): + %1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + (%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + br bb1 + +bb1: + end_apply %tok + destroy_value %0 : $C2 + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller_2 : $@convention(method) (@owned C2) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_devirt_caller_2' +sil [ossa] @begin_apply_devirt_caller_2 : $@convention(method) (@owned C2) -> @error Error { +bb0(%0 : @owned $C2): + %1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + (%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + br bb1 + +bb1: + abort_apply %tok + destroy_value %0 : $C2 + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @begin_apply_devirt_caller_3 : $@convention(method) (@owned C2) -> @error Error { +// CHECK-NOT: begin_apply +// CHECK: } // end sil function 'begin_apply_devirt_caller_3' +sil [ossa] @begin_apply_devirt_caller_3 : $@convention(method) (@owned C2) -> @error Error { +bb0(%0 : @owned $C2): + %1 = class_method %0 : $C2, #C2.i!modify.1 : (C2) -> () -> (), $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + (%mem, %tok) = begin_apply %1(%0) : $@yield_once @convention(method) (@guaranteed C2) -> @yields @inout Builtin.Int64 + cond_br undef, bb1, bb2 + +bb1: + end_apply %tok + br bb3 + +bb2: + abort_apply %tok + br bb3 + +bb3: + destroy_value %0 : $C2 + %9999 = tuple() + return %9999 : $() +} + + +sil_vtable C2 { + #C2.i!modify.1: (C2) -> () -> () : @devirt_callee +} diff --git a/test/SILOptimizer/mark_uninitialized_fixup.sil b/test/SILOptimizer/mark_uninitialized_fixup.sil deleted file mode 100644 index ae09df4f46d6b..0000000000000 --- a/test/SILOptimizer/mark_uninitialized_fixup.sil +++ /dev/null @@ -1,117 +0,0 @@ -// RUN: %target-sil-opt -mark-uninitialized-fixup %s | %FileCheck %s - -sil_stage raw - -import Builtin - -// CHECK-LABEL: sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -sil [ossa] @single_bb_single_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -// CHECK-LABEL: sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: project_box [[BOX]] -sil [ossa] @single_bb_multiple_proj_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - - -// CHECK-LABEL: sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Builtin.Int32 } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: mark_uninitialized [rootself] [[PB]] -// CHECK: br [[NEXT_BB:bb[0-9]+]] -// -// CHECK: [[NEXT_BB]]: -// CHECK: project_box [[BOX]] -sil [ossa] @multiple_bb_case : $@convention(thin) () -> () { -bb0: - %0 = alloc_box ${ var Builtin.Int32 } - %1 = mark_uninitialized [rootself] %0 : ${ var Builtin.Int32 } - %2 = project_box %1 : ${ var Builtin.Int32 }, 0 - br bb1 - -bb1: - %3 = project_box %1 : ${ var Builtin.Int32 }, 0 - destroy_value %1 : ${ var Builtin.Int32 } - %9999 = tuple() - return %9999 : $() -} - -struct Bool { - var _value: Builtin.Int1 -} - -// CHECK-LABEL: sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool } -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: struct_element_addr [[MUI]] -// CHECK: load [trivial] [[MUI]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_rauw_uses_of_project_box : $@convention(thin) (Builtin.Int1, @thin Bool.Type) -> Bool { -bb0(%0 : $Builtin.Int1, %1 : $@thin Bool.Type): - %2 = alloc_box ${ var Bool }, var, name "self" - %3 = mark_uninitialized [rootself] %2 : ${ var Bool } - %4 = project_box %3 : ${ var Bool }, 0 - debug_value %0 : $Builtin.Int1, let, name "v", argno 1 - %6 = struct_element_addr %4 : $*Bool, #Bool._value - assign %0 to %6 : $*Builtin.Int1 - %8 = load [trivial] %4 : $*Bool - destroy_value %3 : ${ var Bool } - return %8 : $Bool -} - -// This is a simulation of the type of callee generated by capture promotion. -sil @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - -// CHECK-LABEL: sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -// CHECK: bb0([[ARG:%.*]] : $Builtin.Int1): -// CHECK: [[BOX:%.*]] = alloc_box ${ var Bool }, var, name "self" -// CHECK: [[PB:%.*]] = project_box [[BOX]] -// CHECK: [[MUI:%.*]] = mark_uninitialized [rootself] [[PB]] -// CHECK: [[BOX_COPY:%.*]] = copy_value [[BOX]] -// CHECK: [[PB_BOX_COPY:%.*]] = project_box [[BOX_COPY]] -// CHECK: destroy_value [[BOX_COPY]] -// CHECK: destroy_value [[BOX]] -sil [ossa] @test_copyvalue_use_of_projectbox : $@convention(thin) (Builtin.Int1) -> () { -bb0(%0 : $Builtin.Int1): - // Initial initialization. - %1 = alloc_box ${ var Bool }, var, name "self" - %2 = mark_uninitialized [rootself] %1 : ${ var Bool } - %3 = project_box %2 : ${ var Bool }, 0 - %4 = struct_element_addr %3 : $*Bool, #Bool._value - store %0 to [trivial] %4 : $*Builtin.Int1 - - // copy + project_box for function_call. This can happen via - // capture_promotion. - %5 = function_ref @capture_promotion_generated_callee : $@convention(thin) (@in Bool) -> () - %6 = copy_value %2 : ${ var Bool } - %7 = project_box %6 : ${ var Bool }, 0 - %8 = apply %5(%7) : $@convention(thin) (@in Bool) -> () - destroy_value %6 : ${ var Bool } - - // Epilog - destroy_value %2 : ${ var Bool } - %9999 = tuple() - return %9999 : $() -} diff --git a/test/SILOptimizer/merge_exclusivity.swift b/test/SILOptimizer/merge_exclusivity.swift index 9e995df212d7d..986c69d1d7525 100644 --- a/test/SILOptimizer/merge_exclusivity.swift +++ b/test/SILOptimizer/merge_exclusivity.swift @@ -372,13 +372,10 @@ private struct EscapedTransforme: WriteProt { // TESTSIL-NEXT: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFADDR]] // TESTSIL: end_access [[B1]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]] -// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array) -> () // TESTSIL: end_access [[BCONF]] // TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity14run_MergeTest9yySiF' @inline(never) diff --git a/test/SILOptimizer/mm_inlinecaches_multiple.sil b/test/SILOptimizer/mm_inlinecaches_multiple.sil index 0282c9a615869..d7d2e5b9433a2 100644 --- a/test/SILOptimizer/mm_inlinecaches_multiple.sil +++ b/test/SILOptimizer/mm_inlinecaches_multiple.sil @@ -28,13 +28,13 @@ bb0(%0 : $Foo): //CHECK-LABEL: @_TF8testcase7my_mainFCS_3FooT_ -//CHECK: checked_cast_br [exact] %0 : $Foo to $Foo +//CHECK: checked_cast_br [exact] %0 : $Foo to Foo //CHECK: return -//CHECK: checked_cast_br [exact] %0 : $Foo to $Bar +//CHECK: checked_cast_br [exact] %0 : $Foo to Bar //CHECK: class_method //CHECK-WMO-LABEL: @_TF8testcase7my_mainFCS_3FooT_ -//CHECK-WMO: checked_cast_br [exact] %0 : $Foo to $Foo +//CHECK-WMO: checked_cast_br [exact] %0 : $Foo to Foo //CHECK-WMO: return //CHECK-WMO: unchecked_ref_cast %0 : $Foo to $Bar //CHECK-WMO: class_method diff --git a/test/SILOptimizer/noescape_param_exclusivity.swift b/test/SILOptimizer/noescape_param_exclusivity.swift index 5d4c74a204c35..ad7833937f279 100644 --- a/test/SILOptimizer/noescape_param_exclusivity.swift +++ b/test/SILOptimizer/noescape_param_exclusivity.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -emit-sil %s -verify +// RUN: %target-swift-frontend -emit-sil %s -verify -enable-ownership-stripping-after-serialization func test0(a: (() -> ()) -> (), b: () -> ()) { a(b) // expected-error {{passing a non-escaping function parameter 'b' to a call to a non-escaping function parameter can allow re-entrant modification of a variable}} @@ -45,4 +46,4 @@ func test5(outer: (() throws -> Int) throws -> Int) throws -> Int { } return try descend(outer) -} \ No newline at end of file +} diff --git a/test/SILOptimizer/opt_mode.swift b/test/SILOptimizer/opt_mode.swift index d2257d7c1d0ad..cfe34c25aea42 100644 --- a/test/SILOptimizer/opt_mode.swift +++ b/test/SILOptimizer/opt_mode.swift @@ -15,16 +15,6 @@ class B : A { func donothing(_ x: Int) -> Int { return x } -// CHECK-LABEL: sil {{.*}} [Ospeed] @{{.*}}test_ospeed -// CHECK: checked_cast_br -// CHECK: checked_cast_br -// CHECK: } -// CHECK-IR: define hidden {{.*}}test_ospeed{{.*}} [[NOSIZE_ATTR:#[0-9]+]] -@_optimize(speed) -func test_ospeed(_ a: A) -> Int { - return donothing(a.foo(27)) -} - // CHECK-LABEL: sil {{.*}} [Osize] @{{.*}}test_osize // CHECK: [[M:%[0-9]+]] = class_method // CHECK: [[A:%[0-9]+]] = apply [[M]] @@ -46,6 +36,16 @@ func test_onone(_ a: A) -> Int { return donothing(a.foo(27)) } +// CHECK-LABEL: sil {{.*}} [Ospeed] @{{.*}}test_ospeed +// CHECK: [[M:%[0-9]+]] = class_method +// CHECK: [[A:%[0-9]+]] = apply [[M]] +// CHECK: return [[A]] +// CHECK-IR: define hidden {{.*}}test_ospeed{{.*}} [[NOSIZE_ATTR:#[0-9]+]] +@_optimize(speed) +func test_ospeed(_ a: A) -> Int { + return donothing(a.foo(27)) +} + -// CHECK-IR: attributes [[NOSIZE_ATTR]] = { " // CHECK-IR: attributes [[SIZE_ATTR]] = { minsize " +// CHECK-IR: attributes [[NOSIZE_ATTR]] = { " diff --git a/test/SILOptimizer/optimize_keypath.swift b/test/SILOptimizer/optimize_keypath.swift index 909eec0d2e3dc..578270e19d937 100644 --- a/test/SILOptimizer/optimize_keypath.swift +++ b/test/SILOptimizer/optimize_keypath.swift @@ -41,6 +41,16 @@ final class GenClass : P { } } +class Base { + final var i: Int = 12 +} + +class DerivedClass : Base { +} + +final class DerivedClass2 : DerivedClass { +} + final class SimpleClass : P { var i: Int static var numObjs = 0 @@ -97,6 +107,33 @@ func testGenClassRead(_ c: GenClass) -> T { return c[keyPath: kp] } +// CHECK-LABEL: sil {{.*}}testDerivedClassRead +// CHECK: [[C:%[0-9]+]] = upcast %0 +// CHECK: [[E:%[0-9]+]] = ref_element_addr [[C]] +// CHECK: [[A:%[0-9]+]] = begin_access [read] [dynamic] [no_nested_conflict] [[E]] +// CHECK: [[V:%[0-9]+]] = load [[A]] +// CHECK: end_access [[A]] +// CHECK: return [[V]] +@inline(never) +@_semantics("optimize.sil.specialize.generic.never") +func testDerivedClassRead(_ c: DerivedClass) -> Int { + let kp = \DerivedClass.i + return c[keyPath: kp] +} + +// CHECK-LABEL: sil {{.*}}testDerivedClass2Read +// CHECK: [[C:%[0-9]+]] = upcast %0 +// CHECK: [[E:%[0-9]+]] = ref_element_addr [[C]] +// CHECK: [[A:%[0-9]+]] = begin_access [read] [dynamic] [no_nested_conflict] [[E]] +// CHECK: [[V:%[0-9]+]] = load [[A]] +// CHECK: end_access [[A]] +// CHECK: return [[V]] +@inline(never) +func testDerivedClass2Read(_ c: DerivedClass2) -> Int { + let kp = \DerivedClass2.i + return c[keyPath: kp] +} + // CHECK-LABEL: sil {{.*}}testGenClassWrite // CHECK: [[S:%[0-9]+]] = alloc_stack $T // CHECK: [[E:%[0-9]+]] = ref_element_addr %0 @@ -233,6 +270,12 @@ func testit() { // CHECK-OUTPUT: GenClassRead: 29 print("GenClassRead: \(testGenClassRead(GenClass(SimpleClass(29))).i)") + // CHECK-OUTPUT: DerivedClassRead: 12 + print("DerivedClassRead: \(testDerivedClassRead(DerivedClass())))") + + // CHECK-OUTPUT: DerivedClass2Read: 12 + print("DerivedClass2Read: \(testDerivedClass2Read(DerivedClass2())))") + // CHECK-OUTPUT: GenClassWrite: 30 let c = GenClass(SimpleClass(0)) testGenClassWrite(c, SimpleClass(30)) diff --git a/test/SILOptimizer/optimize_never.sil b/test/SILOptimizer/optimize_never.sil index 1ed4f03055b2e..11466cd1ccd9c 100644 --- a/test/SILOptimizer/optimize_never.sil +++ b/test/SILOptimizer/optimize_never.sil @@ -326,7 +326,7 @@ bb0(%0 : $*T, %1 : $C): // CHECK-NOT: function_ref @$s14optimize_never4foo3ys5Int32Vx_AA1CCtlF // Presence of checked_cast_br indicates that the speculative devirtualization // was performed. And this is only possible, if the original call is inlined. -// CHECK: checked_cast_br [exact] {{%.*}} : $C to $C, bb2, bb3 +// CHECK: checked_cast_br [exact] {{%.*}} : $C to C, bb2, bb3 // CHECK: return sil @$s14optimize_never4boo3ys5Int32VAA1CCF : $@convention(thin) (@owned C) -> Int32 { bb0(%0 : $C): @@ -376,7 +376,7 @@ bb0(%0 : $Int32, %1 : $C): // CHECK-NOT: function_ref @$s14optimize_never4foo4ys5Int32VAD_AA1CCtF // Presence of checked_cast_br indicates that the speculative devirtualization // was performed. And this is only possible, if the original call is inlined. -// CHECK: checked_cast_br [exact] {{%.*}} : $C to $C, bb2, bb3 +// CHECK: checked_cast_br [exact] {{%.*}} : $C to C, bb2, bb3 // CHECK: return sil @$s14optimize_never4boo4ys5Int32VAA1CCF : $@convention(thin) (@owned C) -> Int32 { bb0(%0 : $C): @@ -455,8 +455,8 @@ bb0(%0 : $Base): // CHECK-LABEL: sil @$s14optimize_never23testDoNotDevirtDerived11os5Int32VAA4BaseC_tF // CHECK: bb0 // CHECK: class_method {{%[0-9]+}} : $Base, #Base.boo!1 : (Base) -> () -> Int32 -// CHECK: checked_cast_br [exact] {{%[0-9]+}} : $Base to $Base -// CHECK: checked_cast_br [exact] {{%[0-9]+}} : $Base to $Derived2 +// CHECK: checked_cast_br [exact] {{%[0-9]+}} : $Base to Base +// CHECK: checked_cast_br [exact] {{%[0-9]+}} : $Base to Derived2 // CHECK-NOT: class_method // CHECK: } sil @$s14optimize_never23testDoNotDevirtDerived11os5Int32VAA4BaseC_tF : $@convention(thin) (@owned Base) -> Int32 { diff --git a/test/SILOptimizer/optionset.swift b/test/SILOptimizer/optionset.swift index 3be6ff165e7df..1ee43c2ef2135 100644 --- a/test/SILOptimizer/optionset.swift +++ b/test/SILOptimizer/optionset.swift @@ -16,18 +16,26 @@ public struct TestOptions: OptionSet { // CHECK-NEXT: bb0: // CHECK-NEXT: integer_literal {{.*}}, 15 // CHECK-NEXT: struct $Int -// CHECK-NEXT: debug_value // CHECK-NEXT: struct $TestOptions // CHECK-NEXT: return public func returnTestOptions() -> TestOptions { return [.first, .second, .third, .fourth] } +// CHECK: sil @{{.*}}returnEmptyTestOptions{{.*}} +// CHECK-NEXT: bb0: +// CHECK-NEXT: integer_literal {{.*}}, 0 +// CHECK-NEXT: struct $Int +// CHECK-NEXT: struct $TestOptions +// CHECK-NEXT: return +public func returnEmptyTestOptions() -> TestOptions { + return [] +} + // CHECK: alloc_global @{{.*}}globalTestOptions{{.*}} // CHECK-NEXT: global_addr // CHECK-NEXT: integer_literal {{.*}}, 15 // CHECK-NEXT: struct $Int -// CHECK-NEXT: debug_value // CHECK-NEXT: struct $TestOptions // CHECK-NEXT: store // CHECK-NEXT: tuple diff --git a/test/SILOptimizer/outliner.swift b/test/SILOptimizer/outliner.swift index 0c85af7528bf3..1a2248924330c 100644 --- a/test/SILOptimizer/outliner.swift +++ b/test/SILOptimizer/outliner.swift @@ -11,21 +11,23 @@ public class MyGizmo { init() { gizmo = Gizmo() - } - // CHECK-LABEL: sil @$s8outliner7MyGizmoC11usePropertyyyF - // CHECK: [[A_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTeab_ - // CHECK: apply [[A_FUN]]({{.*}}) : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional - // CHECK-NOT: return - // CHECK: [[P_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTepb_ - // CHECK: apply [[P_FUN]]({{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional - // CHECK: return + } + + // CHECK-LABEL: sil @$s8outliner7MyGizmoC11usePropertyyyF : + // CHECK: [[A_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTeab_ + // CHECK: apply [[A_FUN]]({{.*}}) : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional + // CHECK-NOT: return + // CHECK: [[P_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTepb_ + // CHECK: apply [[P_FUN]]({{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional + // CHECK: return + // CHECK: } // end sil function '$s8outliner7MyGizmoC11usePropertyyyF' public func useProperty() { print(gizmo.stringProperty) print(optionalGizmo!.stringProperty) - } + } } -// CHECK-LABEL: sil @$s8outliner13testOutliningyyF +// CHECK-LABEL: sil @$s8outliner13testOutliningyyF : // CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTepb_ // CHECK: apply [[FUN]](%{{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional // CHECK: apply [[FUN]](%{{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional @@ -35,10 +37,11 @@ public class MyGizmo { // CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_ // CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional, Gizmo) -> @owned Optional // CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional, Gizmo) -> @owned Optional -// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembnn_ -// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned Array, Gizmo) -> @owned Optional -// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned Array, Gizmo) -> @owned Optional +// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_ +// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@guaranteed Array, Gizmo) -> @owned Optional +// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@guaranteed Array, Gizmo) -> @owned Optional // CHECK: return +// CHECK: } // end sil function '$s8outliner13testOutliningyyF' public func testOutlining() { let gizmo = Gizmo() let foobar = Gizmo() @@ -59,6 +62,7 @@ public func testOutlining() { // CHECK: [[METH:%.*]] = objc_method [[OBJ]] : $@opened("{{.*}}") (AnyObject), #Treeish.treeishChildren!1.foreign : (Self) -> () -> [Any]? // CHECK: [[RES:%.*]] = apply [[METH]]([[OBJ]]) : $@convention(objc_method) // CHECK: switch_enum [[RES]] +// CHECK: } // end sil function '$s8outliner9dontCrash1ayyp_tF' // CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional // CHECK: bb0(%0 : $*Gizmo): @@ -78,6 +82,7 @@ public func testOutlining() { // CHECK: br bb3(%12 : $Optional) // CHECK: bb3(%14 : $Optional): // CHECK: return %14 : $Optional +// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvgToTeab_' // CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional // CHECK: bb0(%0 : $Gizmo): @@ -96,6 +101,7 @@ public func testOutlining() { // CHECK: br bb3(%11 : $Optional) // CHECK:bb3(%13 : $Optional): // CHECK: return %13 : $Optional +// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvgToTepb_' // CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () { // CHECK: bb0(%0 : $String, %1 : $Gizmo): @@ -107,6 +113,7 @@ public func testOutlining() { // CHECK: %7 = apply %2(%6, %1) : $@convention(objc_method) (Optional, Gizmo) -> () // CHECK: strong_release %4 : $NSString // CHECK: return %7 : $() +// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvsToTembnn_' // CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional, Gizmo) -> @owned Optional { // CHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional, %3 : $Gizmo): @@ -133,17 +140,19 @@ public func testOutlining() { // // CHECK: bb3(%21 : $Optional): // CHECK: return %21 : $Optional +// CHECK: } // end sil function '$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_' -// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembnn_ : $@convention(thin) (@owned Array, Gizmo) -> @owned Optional { +// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_ : $@convention(thin) (@guaranteed Array, Gizmo) -> @owned Optional { // CHECK: bb0(%0 : $Array, %1 : $Gizmo): // CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.doSomething!1.foreign : (Gizmo) -> ([String]?) -> Any? // CHECK: %3 = function_ref @$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray // CHECK: %4 = apply %3(%0) : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray -// CHECK: release_value %0 : $Array -// CHECK: %6 = enum $Optional, #Optional.some!enumelt.1, %4 : $NSArray -// CHECK: %7 = apply %2(%6, %1) : $@convention(objc_method) (Optional, Gizmo) -> @autoreleased Optional +// CHECK-NOT: release_value +// CHECK: %5 = enum $Optional, #Optional.some!enumelt.1, %4 : $NSArray +// CHECK: %6 = apply %2(%5, %1) : $@convention(objc_method) (Optional, Gizmo) -> @autoreleased Optional // CHECK: strong_release %4 : $NSArray -// CHECK: return %7 : $Optional +// CHECK: return %6 : $Optional +// CHECK: } // end sil function '$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_' public func dontCrash(x : Gizmo2) { let s = x.doSomething() @@ -208,3 +217,42 @@ public class Foo : NSObject { return true } } + +public func testCalendar() { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) +} + +open class Test +{ + @inline(never) + public func getWindow() -> MyWindow + { + return MyWindow() + } + + public func testDontCrash() -> MyView + { + + let view = MyView() + view.window2 = getWindow() + return view + } +} + +internal extension NSError { + convenience init(myError code: Int) { + self.init(domain: "error", code: code, userInfo: [:]) + } +} + +public class AnotherTest { + public let obj: MyObject + public init(obj: MyObject) { + self.obj = obj + } + + public func dontCrash() { + self.obj.error = NSError(myError: 10) + } +} diff --git a/test/SILOptimizer/ownership_model_eliminator.sil b/test/SILOptimizer/ownership_model_eliminator.sil index b17a8af2ab297..419400b093e0b 100644 --- a/test/SILOptimizer/ownership_model_eliminator.sil +++ b/test/SILOptimizer/ownership_model_eliminator.sil @@ -114,18 +114,18 @@ bb0(%0 : @owned $Builtin.NativeObject): return %9999 : $() } -// We no longer lower copy_unowned_value. So make sure that we actually don't. +// We no longer lower strong_copy_unowned_value. So make sure that we actually don't. // -// CHECK-LABEL: sil @copy_unowned_value_test : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> () { +// CHECK-LABEL: sil @strong_copy_unowned_value_test : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> () { // CHECK: bb0([[ARG:%.*]] : $@sil_unowned Builtin.NativeObject): -// CHECK-NEXT: [[STRONG:%.*]] = copy_unowned_value [[ARG]] : $@sil_unowned Builtin.NativeObject +// CHECK-NEXT: [[STRONG:%.*]] = strong_copy_unowned_value [[ARG]] : $@sil_unowned Builtin.NativeObject // CHECK-NEXT: strong_release [[STRONG]] : $Builtin.NativeObject // CHECK-NEXT: unowned_release [[ARG]] : $@sil_unowned Builtin.NativeObject // CHECK-NEXT: tuple () // CHECK-NEXT: return -sil [ossa] @copy_unowned_value_test : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> () { +sil [ossa] @strong_copy_unowned_value_test : $@convention(thin) (@owned @sil_unowned Builtin.NativeObject) -> () { bb0(%0 : @owned $@sil_unowned Builtin.NativeObject): - %1 = copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject + %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject destroy_value %1 : $Builtin.NativeObject destroy_value %0 : $@sil_unowned Builtin.NativeObject %9999 = tuple() @@ -155,7 +155,7 @@ bb1: // CHECK-LABEL: sil @checked_cast_br_lowered : $@convention(thin) (@owned Builtin.NativeObject) -> () { // CHECK: bb0([[ARG:%.*]] : $Builtin.NativeObject): -// CHECK: checked_cast_br [[ARG]] : $Builtin.NativeObject to $C, [[SUCCBB:bb[0-9]+]], [[FAILBB:bb[0-9]+]] +// CHECK: checked_cast_br [[ARG]] : $Builtin.NativeObject to C, [[SUCCBB:bb[0-9]+]], [[FAILBB:bb[0-9]+]] // // CHECK: [[SUCCBB]]([[CASTED_VALUE:%.*]] : $C): // CHECK-NEXT: strong_release [[CASTED_VALUE]] @@ -166,7 +166,7 @@ bb1: // CHECK-NEXT: br bb3 sil [ossa] @checked_cast_br_lowered : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): - checked_cast_br %0 : $Builtin.NativeObject to $C, bb1, bb2 + checked_cast_br %0 : $Builtin.NativeObject to C, bb1, bb2 bb1(%1 : @owned $C): destroy_value %1 : $C diff --git a/test/SILOptimizer/performance_inliner.sil b/test/SILOptimizer/performance_inliner.sil index 85a69731037b7..670f79f3476ac 100644 --- a/test/SILOptimizer/performance_inliner.sil +++ b/test/SILOptimizer/performance_inliner.sil @@ -968,3 +968,29 @@ bb2(%7 : $Error): sil_vtable C { #C.callThrowing!1: @callThrowing } + +// Test that the inliner doesn't crash if a dynamic-self callee is called from a +// non-dynamic-self caller. Currently we just prevent inlining in this case. +// We could handle this at some time, but we must not crash. + +class X { } +protocol PX : X { } + +sil @no_self_metadata : $@convention(method) (@thick Self.Type) -> () { +bb0(%1 : $@thick Self.Type): + %4 = upcast %1 : $@thick Self.Type to $@thick X.Type + %5 = function_ref @has_self_metadata : $@convention(method) (@thick X.Type) -> @owned X + %6 = apply %5(%4) : $@convention(method) (@thick X.Type) -> @owned X + strong_release %6 : $X + %8 = tuple () + return %8 : $() +} + +sil [always_inline] @has_self_metadata : $@convention(method) (@thick X.Type) -> @owned X { +bb0(%1 : $@thick X.Type): + %3 = unchecked_trivial_bit_cast %1 : $@thick X.Type to $@thick @dynamic_self X.Type + %27 = upcast %3 : $@thick @dynamic_self X.Type to $@thick X.Type + %30 = alloc_ref_dynamic %27 : $@thick X.Type, $X + return %30 : $X +} + diff --git a/test/SILOptimizer/polymorphic_builtins_diagnostics.swift b/test/SILOptimizer/polymorphic_builtins_diagnostics.swift index 868432eb25182..5abb380279bb2 100644 --- a/test/SILOptimizer/polymorphic_builtins_diagnostics.swift +++ b/test/SILOptimizer/polymorphic_builtins_diagnostics.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -parse-stdlib -emit-sil -verify %s +// RUN: %target-swift-frontend -parse-stdlib -emit-sil -verify %s -enable-ownership-stripping-after-serialization import Swift diff --git a/test/SILOptimizer/polymorphic_inline_caches.sil b/test/SILOptimizer/polymorphic_inline_caches.sil index 16efb83d12be9..15f53748d017e 100644 --- a/test/SILOptimizer/polymorphic_inline_caches.sil +++ b/test/SILOptimizer/polymorphic_inline_caches.sil @@ -38,7 +38,7 @@ bb0(%0 : $A, %1 : $Builtin.Int32): %5 = class_method %0 : $A, #A.ping!1 : (A) -> (Builtin.Int32) -> Builtin.Int32, $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 // CHECK-NOT: apply %6 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - // CHECK: checked_cast_br [exact] %0 : $A to $A, bb2, bb3 + // CHECK: checked_cast_br [exact] %0 : $A to A, bb2, bb3 // CHECK: bb1 strong_release %0 : $A // CHECK: return @@ -47,14 +47,14 @@ bb0(%0 : $A, %1 : $Builtin.Int32): // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ // CHECK: apply [[APING]] // CHECK: bb3 - // CHECK: checked_cast_br [exact] %0 : $A to $B, bb5, bb6 + // CHECK: checked_cast_br [exact] %0 : $A to B, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ // CHECK: apply [[BPING]] // CHECK: bb6 - // CHECK: checked_cast_br [exact] %0 : $A to $C, bb8, bb9 + // CHECK: checked_cast_br [exact] %0 : $A to C, bb8, bb9 // CHECK: bb7 // CHECK: br bb4 // CHECK: bb8 @@ -80,7 +80,7 @@ bb0(%0 : $A, %1 : $Builtin.Int32): // CHECK-NOT: apply %6 = apply %4(%0) : $@convention(thin) (A) -> Builtin.Int32 %7 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 - // CHECK: checked_cast_br [exact] %0 : $A to $A, bb2, bb3 + // CHECK: checked_cast_br [exact] %0 : $A to A, bb2, bb3 // CHECK: bb1 strong_release %0 : $A // CHECK: return @@ -89,14 +89,14 @@ bb0(%0 : $A, %1 : $Builtin.Int32): // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ // CHECK: apply [[APING]] // CHECK: bb3 - // CHECK: checked_cast_br [exact] %0 : $A to $B, bb5, bb6 + // CHECK: checked_cast_br [exact] %0 : $A to B, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ // CHECK: apply [[BPING]] // CHECK: bb6 - // CHECK: checked_cast_br [exact] %0 : $A to $C, bb8, bb9 + // CHECK: checked_cast_br [exact] %0 : $A to C, bb8, bb9 // CHECK: bb7 // CHECK: br bb4 // CHECK: bb8 @@ -162,7 +162,7 @@ bb0(%0 : $Builtin.NativeObject): strong_release %0 : $Builtin.NativeObject %7 = tuple () return %7 : $() - // CHECK: checked_cast_br [exact] [[CAST]] : $E to $E, bb2, bb3 + // CHECK: checked_cast_br [exact] [[CAST]] : $E to E, bb2, bb3 // CHECK: bb1 // CHECK: strong_release %0 // CHECK: return @@ -171,7 +171,7 @@ bb0(%0 : $Builtin.NativeObject): // CHECK: strong_retain %0 // CHECK: apply [[EFOO]] // CHECK: bb3 - // CHECK: checked_cast_br [exact] [[CAST]] : $E to $F, bb5, bb6 + // CHECK: checked_cast_br [exact] [[CAST]] : $E to F, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 @@ -179,7 +179,7 @@ bb0(%0 : $Builtin.NativeObject): // CHECK: strong_retain %0 // CHECK: apply [[FFOO]] // CHECK: bb6 - // CHECK: checked_cast_br [exact] [[CAST]] : $E to $G, bb8, bb9 + // CHECK: checked_cast_br [exact] [[CAST]] : $E to G, bb8, bb9 // CHECK: bb7 // CHECK: bb8 // CHECK: [[GFOO:%.*]] = function_ref @_TFC5casts1G3foofS0_FT_T_ @@ -204,7 +204,7 @@ bb0(%0 : $E): strong_release %0 : $E %7 = tuple () return %7 : $() - // CHECK: checked_cast_br [exact] [[CAST]] : $F to $F, bb2, bb3 + // CHECK: checked_cast_br [exact] [[CAST]] : $F to F, bb2, bb3 // CHECK: bb1 // CHECK: strong_release %0 // CHECK: return diff --git a/test/SILOptimizer/pound_assert.swift b/test/SILOptimizer/pound_assert.swift index d459c6cf587b4..acccdaf0e32b5 100644 --- a/test/SILOptimizer/pound_assert.swift +++ b/test/SILOptimizer/pound_assert.swift @@ -22,9 +22,9 @@ func test_assertionFailure() { func test_nonConstant() { #assert(isOne(Int(readLine()!)!)) // expected-error{{#assert condition not constant}} - // expected-note@-1 {{cannot evaluate expression as constant here}} + // expected-note@-1 {{encountered call to 'isOne(_:)' where the 1st argument is not a constant}} #assert(isOne(Int(readLine()!)!), "input is not 1") // expected-error{{#assert condition not constant}} - // expected-note@-1 {{cannot evaluate expression as constant here}} + // expected-note@-1 {{encountered call to 'isOne(_:)' where the 1st argument is not a constant}} } func loops1(a: Int) -> Int { @@ -445,7 +445,7 @@ struct SPsimp : ProtoSimple { func testStructPassedAsProtocols() { let s = SPsimp() #assert(callProtoSimpleMethod(s) == 0) // expected-error {{#assert condition not constant}} - // expected-note@-1 {{cannot evaluate top-level value as constant here}} + // expected-note@-1 {{encountered call to 'callProtoSimpleMethod(_:)' where the 1st argument is not a constant}} } //===----------------------------------------------------------------------===// @@ -634,13 +634,13 @@ func evaluate(intExpr: IntExpr) -> Int { // TODO: The constant evaluator can't handle indirect enums yet. // expected-error @+2 {{#assert condition not constant}} -// expected-note @+1 {{encountered operation not supported by the evaluator}} +// expected-note @+1 {{encountered call to 'evaluate(intExpr:)' where the 1st argument is not a constant}} #assert(evaluate(intExpr: .int(5)) == 5) // expected-error @+2 {{#assert condition not constant}} -// expected-note @+1 {{encountered operation not supported by the evaluator}} +// expected-note @+1 {{encountered call to 'evaluate(intExpr:)' where the 1st argument is not a constant}} #assert(evaluate(intExpr: .add(.int(5), .int(6))) == 11) // expected-error @+2 {{#assert condition not constant}} -// expected-note @+1 {{encountered operation not supported by the evaluator}} +// expected-note @+1 {{encountered call to 'evaluate(intExpr:)' where the 1st argument is not a constant}} #assert(evaluate(intExpr: .add(.multiply(.int(2), .int(2)), .int(3))) == 7) // Test address-only enums. @@ -688,7 +688,7 @@ func arrayInitEmptyTopLevel() { func arrayInitEmptyLiteralTopLevel() { // TODO: More work necessary for array initialization using literals to work // at the top level. - // expected-note@+1 {{cannot evaluate expression as constant here}} + // expected-note@+1 {{encountered call to 'ContainsArray.init(x:arr:)' where the 2nd argument is not a constant}} let c = ContainsArray(x: 1, arr: []) // expected-error @+1 {{#assert condition not constant}} #assert(c.x == 1) @@ -697,14 +697,14 @@ func arrayInitEmptyLiteralTopLevel() { func arrayInitLiteral() { // TODO: More work necessary for array initialization using literals to work // at the top level. - // expected-note @+1 {{cannot evaluate expression as constant here}} + // expected-note @+1 {{encountered call to 'ContainsArray.init(x:arr:)' where the 2nd argument is not a constant}} let c = ContainsArray(x: 1, arr: [2, 3, 4]) // expected-error @+1 {{#assert condition not constant}} #assert(c.x == 1) } func arrayInitNonConstantElementTopLevel(x: Int) { - // expected-note @+1 {{cannot evaluate expression as constant here}} + // expected-note @+1 {{encountered call to 'ContainsArray.init(x:arr:)' where the 2nd argument is not a constant}} let c = ContainsArray(x: 1, arr: [x]) // expected-error @+1 {{#assert condition not constant}} #assert(c.x == 1) diff --git a/test/SILOptimizer/predictable_memaccess_opts.sil b/test/SILOptimizer/predictable_memaccess_opts.sil index f261e4a12b4ad..8fa9df6db1b1e 100644 --- a/test/SILOptimizer/predictable_memaccess_opts.sil +++ b/test/SILOptimizer/predictable_memaccess_opts.sil @@ -16,7 +16,17 @@ struct NativeObjectPair { var y: Builtin.NativeObject } -sil @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +struct IntPair { + var x: Builtin.Int32 + var y: Builtin.Int32 +} + +sil @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +sil @intpair_user : $@convention(thin) (IntPair) -> () +sil @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () +sil @inout_int32_user : $@convention(thin) (@inout Builtin.Int32) -> () +sil @get_object : $@convention(thin) () -> @owned Builtin.NativeObject +sil @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () /// Needed to avoid tuple scalarization code in the use gatherer. struct NativeObjectAndTuple { @@ -50,9 +60,11 @@ bb0(%0 : $Builtin.Int32): // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[ARG_COPY_2:%.*]] = copy_value [[ARG_COPY]] +// CHECK: destroy_value [[ARG_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG_COPY]] +// CHECK: return [[ARG_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion' sil [ossa] @simple_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -73,10 +85,14 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK: store [[ARG1]] to [init] [[FIRST_ADDR]] // CHECK: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK: store [[ARG2]] to [init] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion' sil [ossa] @struct_nontrivial_load_promotion : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -134,9 +150,11 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // CHECK: br bb3([[ARG_COPY]] : // // CHECK: bb3([[RESULT:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'simple_nontrivial_load_promotion_multi_insertpt' sil [ossa] @simple_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject): @@ -182,10 +200,16 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject, [[ARG2_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject, [[ARG2_COPY:%.*]] : @owned $Builtin.NativeObject): -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[ARG2_COPY:%.*]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] +// CHECK: [[ARG2_COPY_COPY:%.*]] = copy_value [[ARG2_COPY]] +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[ARG2_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG2_COPY_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[ARG2_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -296,12 +320,17 @@ bb3: // CHECK: br bb3([[ARG1_COPY]] : $Builtin.NativeObject) // // CHECK: bb3([[ARG1_COPY:%.*]] : @owned $Builtin.NativeObject): +// CHECK: [[ARG1_COPY_COPY:%.*]] = copy_value [[ARG1_COPY]] // CHECK: [[SECOND_ADDR:%.*]] = struct_element_addr [[STACK]] // CHECK: [[SECOND_VAL_COPY:%.*]] = load [copy] [[SECOND_ADDR]] -// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY]] : $Builtin.NativeObject) +// CHECK: [[ARG1_COPY_COPY_BORROW:%.*]] = begin_borrow [[ARG1_COPY_COPY]] +// CHECK: [[SECOND_VAL_COPY_BORROW:%.*]] = begin_borrow [[SECOND_VAL_COPY]] +// CHECK: [[RESULT:%.*]] = struct $NativeObjectPair ([[ARG1_COPY_COPY_BORROW:%.*]] : $Builtin.NativeObject, [[SECOND_VAL_COPY_BORROW]] : $Builtin.NativeObject) +// CHECK: [[RESULT_COPY_1:%.*]] = copy_value [[RESULT]] +// CHECK: [[RESULT_COPY_2:%.*]] = copy_value [[RESULT_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[RESULT]] +// CHECK: return [[RESULT_COPY_2]] // CHECK: } // end sil function 'struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available' sil [ossa] @struct_nontrivial_load_promotion_multi_insertpt_value_not_fully_available : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject, %arg2 : @owned $Builtin.NativeObject): @@ -403,9 +432,11 @@ bb3: // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialstructuse_load_promotion' sil [ossa] @simple_partialstructuse_load_promotion : $@convention(thin) (@owned NativeObjectPair) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectPair): @@ -427,9 +458,11 @@ bb0(%0 : @owned $NativeObjectPair): // CHECK: [[COPIED_ARG_FIELD:%.*]] = copy_value [[BORROWED_ARG_FIELD_2]] // CHECK: end_borrow [[BORROWED_ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] +// CHECK: [[COPIED_ARG_FIELD_COPY_1:%.*]] = copy_value [[COPIED_ARG_FIELD]] +// CHECK: [[COPIED_ARG_FIELD_COPY_2:%.*]] = copy_value [[COPIED_ARG_FIELD_COPY_1]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[COPIED_ARG_FIELD]] +// CHECK: return [[COPIED_ARG_FIELD_COPY_2]] // CHECK: } // end sil function 'simple_partialtupleuse_load_promotion' sil [ossa] @simple_partialtupleuse_load_promotion : $@convention(thin) (@owned NativeObjectAndTuple) -> (@owned Builtin.NativeObject) { bb0(%0 : @owned $NativeObjectAndTuple): @@ -449,9 +482,10 @@ bb0(%0 : @owned $NativeObjectAndTuple): // CHECK: store [[ARG0]] to [init] [[STACK]] // CHECK: [[ARG1_COPY:%.*]] = copy_value [[ARG1]] // CHECK: store [[ARG1]] to [assign] [[STACK]] +// CHECK: [[ARG1_COPY_1:%.*]] = copy_value [[ARG1_COPY]] // CHECK: destroy_addr [[STACK]] // CHECK: dealloc_stack [[STACK]] -// CHECK: return [[ARG1_COPY]] +// CHECK: return [[ARG1_COPY_1]] // CHECK: } // end sil function 'simple_assignstore' sil [ossa] @simple_assignstore : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -478,11 +512,15 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_addr [[STACK]] @@ -538,13 +576,36 @@ bb0(%0 : @owned $Builtin.NativeObject): // CHECK-LABEL: sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject { // CHECK: bb0([[ARG:%.*]] : +// Block where we have our store and do our lifetime extending copy_value. // CHECK: [[STACK:%.*]] = alloc_stack $Builtin.NativeObject // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: store [[ARG]] to [init] [[STACK]] -// CHECK: [[BORROWED_ARG_COPY:%.*]] = begin_borrow [[ARG_COPY]] +// CHECK: br bb1 +// +// Our load block. Here, we insert our copy_value + begin_borrow that is +// associated with the load_borrow. We can not use the original copy since even +// though in this situation we know that our copy/borrow would be strongly +// control equivalent, this is not always true. To simplify the algorithm, we +// always insert the copy here. We insert a destroy_value to end the lifetime of +// ARG_COPY since we do not have a loop here. +// +// CHECK: bb1: +// CHECK: [[CONTROL_EQUIVALENT_ARG_COPY:%.*]] = copy_value [[ARG_COPY]] +// CHECK: [[BORROWED_ARG_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_ARG_COPY]] +// CHECK: destroy_value [[ARG_COPY]] +// CHECK: br bb2 +// +// The block where the load_borrow is actually used. We destroy the control +// equivalent arg copy here after the end_borrow. +// +// CHECK: bb2: // CHECK: [[RESULT:%.*]] = copy_value [[BORROWED_ARG_COPY]] // CHECK: end_borrow [[BORROWED_ARG_COPY]] -// CHECK: destroy_value [[ARG_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_ARG_COPY]] +// CHECK: br bb3 +// +// The block after the load_borrow is ever used. +// CHECK: bb3: // CHECK: destroy_addr [[STACK]] // CHECK: return [[RESULT]] // CHECK: } // end sil function 'load_borrow_promotion' @@ -552,9 +613,18 @@ sil [ossa] @load_borrow_promotion : $@convention(thin) (@owned Builtin.NativeObj bb0(%0 : @owned $Builtin.NativeObject): %1 = alloc_stack $Builtin.NativeObject store %0 to [init] %1 : $*Builtin.NativeObject + br bb1 + +bb1: %2 = load_borrow %1 : $*Builtin.NativeObject + br bb2 + +bb2: %3 = copy_value %2 : $Builtin.NativeObject end_borrow %2 : $Builtin.NativeObject + br bb3 + +bb3: destroy_addr %1 : $*Builtin.NativeObject dealloc_stack %1 : $*Builtin.NativeObject return %3 : $Builtin.NativeObject @@ -572,7 +642,7 @@ bb0(%0 : @owned $NativeObjectPair): bb2: %3 = load_borrow %2 : $*Builtin.NativeObject - %4 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %3 : $Builtin.NativeObject br bb2 @@ -590,7 +660,7 @@ bb0(%0 : @owned $NativeObjectPair): bb2: %3 = load_borrow %2 : $*Builtin.NativeObject - %4 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %3 : $Builtin.NativeObject cond_br undef, bb3, bb4 @@ -655,17 +725,21 @@ bb9: // CHECK: ([[TUP_0:%.*]], [[TUP_1:%.*]]) = destructure_tuple [[TUP]] // CHECK: [[TUP_0_COPY:%.*]] = copy_value [[TUP_0]] // CHECK: [[TUP_1_COPY:%.*]] = copy_value [[TUP_1]] -// CHECK: [[BORROWED_TUP_0_COPY:%.*]] = begin_borrow [[TUP_0_COPY]] -// CHECK: [[BORROWED_TUP_1_COPY:%.*]] = begin_borrow [[TUP_1_COPY]] +// CHECK: [[CONTROL_EQUIVALENT_TUP_0_COPY:%.*]] = copy_value [[TUP_0_COPY]] +// CHECK: [[BORROWED_TUP_0_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_0_COPY]] +// CHECK: destroy_value [[TUP_0_COPY]] +// CHECK: [[CONTROL_EQUIVALENT_TUP_1_COPY:%.*]] = copy_value [[TUP_1_COPY]] +// CHECK: [[BORROWED_TUP_1_COPY:%.*]] = begin_borrow [[CONTROL_EQUIVALENT_TUP_1_COPY]] +// CHECK: destroy_value [[TUP_1_COPY]] // CHECK: [[BORROWED_TUP:%.*]] = tuple ([[BORROWED_TUP_0_COPY]] : ${{.*}}, [[BORROWED_TUP_1_COPY]] : // CHECK: [[TUP_EXT_1:%.*]] = tuple_extract [[BORROWED_TUP]] : // CHECK: [[TUP_EXT_2:%.*]] = tuple_extract [[BORROWED_TUP]] : // CHECK: apply {{%.*}}([[TUP_EXT_1]]) // CHECK: apply {{%.*}}([[TUP_EXT_2]]) // CHECK: end_borrow [[BORROWED_TUP_0_COPY]] -// CHECK: destroy_value [[TUP_0_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_0_COPY]] // CHECK: end_borrow [[BORROWED_TUP_1_COPY]] -// CHECK: destroy_value [[TUP_1_COPY]] +// CHECK: destroy_value [[CONTROL_EQUIVALENT_TUP_1_COPY]] // CHECK: } // end sil function 'load_borrow_tuple_scalarize' sil [canonical] [ossa] @load_borrow_tuple_scalarize : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): @@ -676,7 +750,7 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %4 = load_borrow %2 : $*(Builtin.NativeObject, Builtin.NativeObject) %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 0 %6 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 - %7 = function_ref @guaranteed_object_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %7 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %7(%5) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () apply %7(%6) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () end_borrow %4 : $(Builtin.NativeObject, Builtin.NativeObject) @@ -686,3 +760,1178 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %9999 = tuple() return %9999 : $() } + +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +// CHECK: bb0( +// CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair +// CHECK-NOT: bb{{[0-9][0-9]*}}( +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial : $@convention(thin) (Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK: bb0( +// CHECK-NOT: load [trivial] %{{[0-9][0-9]*}} : $*IntPair +// CHECK-NOT: bb{{[0-9][0-9]*}}( +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_reload : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0c to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +// CHECK-NOT: load +// CHECK: } // end sil function 'trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop' +sil [ossa] @trivial_multiple_available_values_diamond_followed_by_loop_trivial_store_in_loop : $@convention(thin) (Builtin.Int32, Builtin.Int32, Builtin.Int32) -> () { +bb0(%0a : $Builtin.Int32, %0b : $Builtin.Int32, %0c : $Builtin.Int32): + %func = function_ref @intpair_user : $@convention(thin) (IntPair) -> () + %1 = alloc_stack $IntPair + %1a = struct_element_addr %1 : $*IntPair, #IntPair.x + %1b = struct_element_addr %1 : $*IntPair, #IntPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb2: + store %0a to [trivial] %1a : $*Builtin.Int32 + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [trivial] %1 : $*IntPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (IntPair) -> () + store %0b to [trivial] %1b : $*Builtin.Int32 + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (IntPair) -> () + dealloc_stack %1 : $*IntPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK: bb0( +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop' +sil [ossa] @multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_reload' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0c to [init] %1b : $*Builtin.NativeObject + destroy_value %0b : $Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + destroy_value %0c : $Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'multiple_available_values_diamond_followed_by_loop_store_in_loop' +sil [ossa] @multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + %0bhat = copy_value %0b : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_addr %1b : $*Builtin.NativeObject + %0bhat2 = copy_value %0bhat : $Builtin.NativeObject + store %0bhat2 to [init] %1b : $*Builtin.NativeObject + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + destroy_value %0bhat : $Builtin.NativeObject + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow' +sil [canonical] [ossa] @loop_carry_loadborrow : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load_borrow %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_2' +sil [canonical] [ossa] @loop_carry_loadborrow_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load_borrow %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_3' +sil [canonical] [ossa] @loop_carry_loadborrow_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) + %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 + %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load_borrow %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + end_borrow %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + end_borrow %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_loadborrow_4' +sil [canonical] [ossa] @loop_carry_loadborrow_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load_borrow %1 : $*NativeObjectPair + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + end_borrow %2 : $NativeObjectPair + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent' +sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8 + +bb6: + br bb8 + +bb7: + br bbPreLoopHeader + +bb8: + br bbPreLoopHeader + +bbPreLoopHeader: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load_borrow %0 : $*Builtin.NativeObject + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// In this case, we will have that we need to separately lifetime extend our phi +// node's copy to prevent leaks along the edge skipping the loop. +// CHECK-LABEL: sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'loop_carry_load_borrow_phi_not_control_equivalent_2' +sil [ossa] @loop_carry_load_borrow_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8a + +bb6: + br bb8a + +bb7: + br bbPreLoopHeader + +bb8a: + br bb8 + +bb8: + cond_br undef, bbPreLoopHeader1, bbSkipLoop + +bbPreLoopHeader: + br bbLoop + +bbPreLoopHeader1: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load_borrow %0 : $*Builtin.NativeObject + br bbLoop6 + +bbLoop6: + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbLoop5 + +bbLoop5: + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + end_borrow %2 : $Builtin.NativeObject + br bbEnd + +bbSkipLoop: + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +//--- + +// CHECK-LABEL: sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load_borrow +// CHECK: } // end sil function 'load_copy_promote_with_loop_1' +sil [ossa] @load_copy_promote_with_loop_1 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + br bb2 +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_promote_with_loop_2' +sil [ossa] @load_copy_loop_promote_with_loop_2 : $@convention(thin) (@owned NativeObjectPair) -> () { +bb0(%0 : @owned $NativeObjectPair): + %1 = alloc_stack $NativeObjectPair + store %0 to [init] %1 : $*NativeObjectPair + %2 = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + br bb2 + +bb2: + %3 = load [copy] %2 : $*Builtin.NativeObject + %4 = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb3, bb4 + +bb3: + br bb2 + +bb4: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_promote_two_backedge_loop' +sil [ossa] @load_copy_promote_two_backedge_loop : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + br bb1 + +bb1: + br bb2 + +bb2: + cond_br undef, bb3, bb4 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %2 : $Builtin.NativeObject + cond_br undef, bb5, bb6 + +bb4: + %3 = load [copy] %1 : $*Builtin.NativeObject + destroy_value %3 : $Builtin.NativeObject + cond_br undef, bb7, bb8 + +bb5: + br bb2 + +bb6: + br bb9 + +bb7: + br bb2 + +bb8: + br bb9 + +bb9: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK: bb0( +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_reload' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_reload : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @owned $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0c to [init] %1b : $*Builtin.NativeObject + destroy_value %0b : $Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + destroy_value %0c : $Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] {{%.*}} : $*NativeObjectPair +// CHECK: } // end sil function 'load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop' +sil [ossa] @load_copy_multiple_available_values_diamond_followed_by_loop_store_in_loop : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + %0bhat = copy_value %0b : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb2: + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + br bb3 + +bb3: + br bb4 + +bb4: + br bb5 + +bb5: + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb6, bb7 + +bb6: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_addr %1b : $*Builtin.NativeObject + %0bhat2 = copy_value %0bhat : $Builtin.NativeObject + store %0bhat2 to [init] %1b : $*Builtin.NativeObject + br bb5 + +bb7: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + destroy_value %0bhat : $Builtin.NativeObject + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy' +sil [canonical] [ossa] @loop_carry_loadcopy : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_2' +sil [canonical] [ossa] @loop_carry_loadcopy_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%0 : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %1 = alloc_stack $Builtin.NativeObject + store %0 to [init] %1 : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %2 = load [copy] %1 : $*Builtin.NativeObject + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*Builtin.NativeObject + dealloc_stack %1 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_3' +sil [canonical] [ossa] @loop_carry_loadcopy_3 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobject_tuple_user : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + %1 = alloc_stack $(Builtin.NativeObject, Builtin.NativeObject) + %1a = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 0 + %1b = tuple_element_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject), 1 + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed (Builtin.NativeObject, Builtin.NativeObject)) -> () + destroy_value %2 : $(Builtin.NativeObject, Builtin.NativeObject) + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + dealloc_stack %1 : $*(Builtin.NativeObject, Builtin.NativeObject) + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'loop_carry_loadcopy_4' +sil [canonical] [ossa] @loop_carry_loadcopy_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { +bb0(%0a : @owned $Builtin.NativeObject, %0b : @owned $Builtin.NativeObject, %0c : @guaranteed $Builtin.NativeObject): + %func = function_ref @nativeobjectpair_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () + %1 = alloc_stack $NativeObjectPair + %1a = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.x + %1b = struct_element_addr %1 : $*NativeObjectPair, #NativeObjectPair.y + store %0a to [init] %1a : $*Builtin.NativeObject + store %0b to [init] %1b : $*Builtin.NativeObject + cond_br undef, bb1, bb7 + +bb1: + br bb2 + +bb2: + br bb3 + +bb3: + %0ccopy = copy_value %0c : $Builtin.NativeObject + destroy_addr %1a : $*Builtin.NativeObject + store %0ccopy to [init] %1a : $*Builtin.NativeObject + %2 = load [copy] %1 : $*NativeObjectPair + cond_br undef, bb4, bb5 + +bb4: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb2 + +bb5: + apply %func(%2) : $@convention(thin) (@guaranteed NativeObjectPair) -> () + destroy_value %2 : $NativeObjectPair + br bb6 + +bb6: + br bb8 + +bb7: + br bb8 + +bb8: + destroy_addr %1 : $*NativeObjectPair + dealloc_stack %1 : $*NativeObjectPair + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8 + +bb6: + br bb8 + +bb7: + br bbPreLoopHeader + +bb8: + br bbPreLoopHeader + +bbPreLoopHeader: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// In this case, we will have that we need to separately lifetime extend our phi +// node's copy to prevent leaks along the edge skipping the loop. +// CHECK-LABEL: sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'load_copy_loop_carry_load_copy_phi_not_control_equivalent_2' +sil [ossa] @load_copy_loop_carry_load_copy_phi_not_control_equivalent_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () { +bb0(%arg : @owned $Builtin.NativeObject): + %func = function_ref @nativeobject_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %0 = alloc_stack $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + cond_br undef, bb3, bb4 + +bb2: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb5 + +bb3: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb6 + +bb4: + store %arg to [init] %0 : $*Builtin.NativeObject + br bb7 + +bb5: + br bb8a + +bb6: + br bb8a + +bb7: + br bbPreLoopHeader + +bb8a: + br bb8 + +bb8: + cond_br undef, bbPreLoopHeader1, bbSkipLoop + +bbPreLoopHeader: + br bbLoop + +bbPreLoopHeader1: + br bbLoop + +bbLoop: + br bbLoop1 + +bbLoop1: + br bbLoop2 + +bbLoop2: + %2 = load [copy] %0 : $*Builtin.NativeObject + br bbLoop6 + +bbLoop6: + cond_br undef, bbLoop3, bbLoop4 + +bbLoop3: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbLoop5 + +bbLoop5: + br bbLoop2 + +bbLoop4: + apply %func(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %2 : $Builtin.NativeObject + br bbEnd + +bbSkipLoop: + br bbEnd + +bbEnd: + destroy_addr %0 : $*Builtin.NativeObject + dealloc_stack %0 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + diff --git a/test/SILOptimizer/predictable_memopt_ownership.sil b/test/SILOptimizer/predictable_memopt_ownership.sil index ebb893ac4b803..d88adf2eb0558 100644 --- a/test/SILOptimizer/predictable_memopt_ownership.sil +++ b/test/SILOptimizer/predictable_memopt_ownership.sil @@ -211,8 +211,12 @@ bb0(%0 : @owned $ContainsNativeObject): // CHECK: [[f3:%.*]] = struct_extract [[BORROWED_ARG]] : $ComplexStruct, #ComplexStruct.f1 // CHECK: [[f3_copy:%.*]] = copy_value [[f3]] // CHECK: end_borrow [[BORROWED_ARG]] +// CHECK: [[f3_copy_1:%.*]] = copy_value [[f3_copy]] +// CHECK: [[f3_copy_2:%.*]] = copy_value [[f3_copy_1]] +// CHECK: [[f2_x_copy_1:%.*]] = copy_value [[f2_x_copy]] +// CHECK: [[f2_x_copy_2:%.*]] = copy_value [[f2_x_copy_1]] // CHECK: destroy_value [[ARG]] -// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy]] : $Builtin.NativeObject, [[f2_x_copy]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) +// CHECK: [[RESULT:%.*]] = tuple ([[f3_copy_2]] : $Builtin.NativeObject, [[f2_x_copy_2]] : $Builtin.NativeObject, [[f1]] : $Builtin.Int32) // CHECK: return [[RESULT]] // CHECK: } // end sil function 'multiple_level_extract_2' sil [ossa] @multiple_level_extract_2 : $@convention(thin) (@owned ComplexStruct) -> (@owned Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32) { @@ -424,7 +428,7 @@ bb0: // CHECK-LABEL: sil [ossa] @dead_allocation_1 sil [ossa] @dead_allocation_1 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 +// CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional store %0 to [init] %2 : $*Optional @@ -434,6 +438,7 @@ bb0(%0 : @owned $Optional): dealloc_stack %2 : $*Optional destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional +// CHECK: destroy_value %0 %3 = tuple () return %3 : $() } @@ -441,7 +446,6 @@ bb0(%0 : @owned $Optional): // CHECK-LABEL: sil [ossa] @dead_allocation_2 sil [ossa] @dead_allocation_2 : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): -// CHECK: copy_value %0 // CHECK-NOT: alloc_stack %1 = alloc_stack $Optional %2 = alloc_stack $Optional @@ -453,6 +457,7 @@ bb0(%0 : @owned $Optional): destroy_addr %1 : $*Optional dealloc_stack %1 : $*Optional %3 = tuple () +// CHECK: destroy_value %0 return %3 : $() } @@ -559,11 +564,15 @@ bb3: // // CHECK: bb1: // CHECK: destroy_value [[LHS1_COPY]] -// CHECK: br bb3([[LHS2_COPY]] : +// CHECK: [[LHS2_COPY_1:%.*]] = copy_value [[LHS2_COPY]] +// CHECK: [[LHS2_COPY_2:%.*]] = copy_value [[LHS2_COPY_1]] +// CHECK: br bb3([[LHS2_COPY_2]] : // // CHECK: bb2: // CHECK: destroy_value [[LHS2_COPY]] : $Builtin.NativeObject -// CHECK: br bb3([[LHS1_COPY]] : +// CHECK: [[LHS1_COPY_1:%.*]] = copy_value [[LHS1_COPY]] +// CHECK: [[LHS1_COPY_2:%.*]] = copy_value [[LHS1_COPY_1]] +// CHECK: br bb3([[LHS1_COPY_2]] : // // CHECK: bb3([[PHI:%.*]] : // CHECK: destroy_value [[ARG]] @@ -649,9 +658,11 @@ struct NativeObjectTriple { // CHECK-NEXT: br bb3([[PAIR_LHS_COPY]] : // // CHECK: bb3([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK-NEXT: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) -// CHECK-NEXT: destroy_value [[REFORMED]] -// CHECK-NEXT: return [[PHI]] +// CHECK: [[PHI_COPY_1:%.*]] = copy_value [[PHI]] +// CHECK: [[PHI_COPY_2:%.*]] = copy_value [[PHI_COPY_1]] +// CHECK: [[REFORMED:%.*]] = struct $NativeObjectTriple ([[ARG0]] : {{.*}}, [[ARG1]] : {{.*}}) +// CHECK: destroy_value [[REFORMED]] +// CHECK: return [[PHI_COPY_2]] // CHECK: } // end sil function 'diamond_test_4' sil [ossa] @diamond_test_4 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair) -> @owned Builtin.NativeObject { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair): @@ -710,9 +721,14 @@ bb3: // CHECK: bb4: // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL]] : {{.*}}) +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[TRIPLE_RHS_RHS_VAL_COPY_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_RHS_VAL_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[TRIPLE_RHS_RHS_VAL_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_5' sil [ossa] @diamond_test_5 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): @@ -758,10 +774,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1:bb[0-9]+]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[FALSE_BB]]: // CHECK: [[TRIPLE_LHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f1 @@ -774,10 +794,14 @@ bb4: // CHECK: cond_br undef, [[CRITEDGE_BREAK_BB_1:bb[0-9]+]], [[CRITEDGE_BREAK_BB_2:bb[0-9]+]] // // CHECK: [[CRITEDGE_BREAK_BB_1]]: -// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_2]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[CRITEDGE_BREAK_BB_2]]: -// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL]] : +// CHECK-NEXT: [[TRIPLE_RHS_RHS_VAL_COPY:%.*]] = copy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: destroy_value [[TRIPLE_RHS_RHS_VAL]] +// CHECK-NEXT: br [[SUCC_1]]([[TRIPLE_RHS_RHS_VAL_COPY]] : // // CHECK: [[SUCC_2]]([[PHI1:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 @@ -786,15 +810,21 @@ bb4: // CHECK: br [[EXIT_BB:bb[0-9]+]]([[PHI1:%.*]] : $Builtin.NativeObject) // // CHECK: [[SUCC_1]]([[PHI:%.*]] : @owned $Builtin.NativeObject): -// CHECK: br [[EXIT_BB]]([[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: br [[EXIT_BB]]([[PHI_COPY]] : {{.*}}) // // CHECK: [[EXIT_BB]]([[PHI:%.*]] : @owned $Builtin.NativeObject): // CHECK: [[TRIPLE_RHS:%.*]] = struct_element_addr [[BOX]] : $*NativeObjectTriple, #NativeObjectTriple.f2 // CHECK: [[TRIPLE_RHS_LHS:%.*]] = struct_element_addr [[TRIPLE_RHS]] : $*NativeObjectPair, #NativeObjectPair.x // CHECK: [[TRIPLE_RHS_LHS_VAL:%.*]] = load [copy] [[TRIPLE_RHS_LHS]] : $*Builtin.NativeObject -// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL]] : {{.*}}, [[PHI]] : {{.*}}) +// CHECK: [[PHI_COPY:%.*]] = copy_value [[PHI]] +// CHECK: [[TRIPLE_RHS_LHS_VAL_BORROW:%.*]] = begin_borrow [[TRIPLE_RHS_LHS_VAL]] +// CHECK: [[PHI_COPY_BORROW:%.*]] = begin_borrow [[PHI_COPY]] +// CHECK: [[STRUCT:%.*]] = struct $NativeObjectPair ([[TRIPLE_RHS_LHS_VAL_BORROW]] : {{.*}}, [[PHI_COPY_BORROW]] : {{.*}}) +// CHECK: [[STRUCT_COPY_1:%.*]] = copy_value [[STRUCT]] +// CHECK: [[STRUCT_COPY_2:%.*]] = copy_value [[STRUCT_COPY_1]] // CHECK: destroy_addr [[BOX]] -// CHECK: return [[STRUCT]] +// CHECK: return [[STRUCT_COPY_2]] // CHECK: } // end sil function 'diamond_test_6' sil [ossa] @diamond_test_6 : $@convention(thin) (@owned Builtin.NativeObject, @owned NativeObjectPair, @owned Builtin.NativeObject) -> @owned NativeObjectPair { bb0(%0 : @owned $Builtin.NativeObject, %1 : @owned $NativeObjectPair, %arg2 : @owned $Builtin.NativeObject): diff --git a/test/SILOptimizer/rcidentity.sil b/test/SILOptimizer/rcidentity.sil index d418b59c55942..79ef8211a2767 100644 --- a/test/SILOptimizer/rcidentity.sil +++ b/test/SILOptimizer/rcidentity.sil @@ -64,7 +64,7 @@ enum E1 { // CHECK: RESULT #12: 12 = 11 sil @test_rcid_preserving_casts : $@convention(thin) (Builtin.NativeObject) -> () { bb0(%0 : $Builtin.NativeObject): - %1 = unconditional_checked_cast %0 : $Builtin.NativeObject to $D + %1 = unconditional_checked_cast %0 : $Builtin.NativeObject to D %2 = upcast %1 : $D to $C %3 = unchecked_ref_cast %2 : $C to $E %4 = integer_literal $Builtin.Word, 0 diff --git a/test/SILOptimizer/redundant_load_elim.sil b/test/SILOptimizer/redundant_load_elim.sil index 8c5cef586d320..57a81d7209a8a 100644 --- a/test/SILOptimizer/redundant_load_elim.sil +++ b/test/SILOptimizer/redundant_load_elim.sil @@ -588,8 +588,10 @@ bb5: // CHECK: store // CHECK-NOT: = load // CHECK: bb2: -// CHECK: bb3: -// CHECK: = load +// CHECK: bb3([[A:%[0-9]+]] : $Builtin.Int32): +// CHECK-NOT: = load +// CHECK: apply %{{[0-9]+}}([[A]]) +// CHECK-LABEL: } // end sil function 'load_to_load_conflicting_branches_diamond' sil @load_to_load_conflicting_branches_diamond : $@convention(thin) (@inout Builtin.Int32) -> () { // %0 // users: %1, %4, %9, %11, %16, %21 bb0(%0 : $*Builtin.Int32): diff --git a/test/SILOptimizer/remove_unused_global_vars.swift b/test/SILOptimizer/remove_unused_global_vars.swift new file mode 100644 index 0000000000000..eb5988a619811 --- /dev/null +++ b/test/SILOptimizer/remove_unused_global_vars.swift @@ -0,0 +1,76 @@ +// RUN: %target-swift-frontend -primary-file %s -O -module-name=test -emit-sil | %FileCheck %s + +import SwiftShims + +@_optimize(none) public func make_test(_ x: Int) -> Int { + return x +} + +struct Foo { + let x : Int + let y : Int + func both() -> Int { x + y } +} + +// CHECK-NOT: sil_global private [let] {{.*}}unused1{{.*}} +private let unused1 = 0 +// CHECK-NOT: sil_global private {{.*}}unused2{{.*}} +private var unused2 = 42 +// CHECK: sil_global private [let] @${{.*}}used1{{.*}} : $Int +private let used1 = 0 +// CHECK: sil_global private @${{.*}}used2{{.*}} : $Int +private var used2 = 0 + +// non-constant / non-trivial values +// CHECK-NOT: sil_global private {{.*}}unused7{{.*}} +private let unused7 = make_test(42) +// CHECK-NOT: sil_global private {{.*}}unused8{{.*}} +private let unused8 = Foo(x: 1, y: 1) +// CHECK-NOT: sil_global private {{.*}}unused9{{.*}} +private let unused9 = Foo(x: 1, y: 1).both() + +// CHECK: sil_global [let] @${{.*}}unused3{{.*}} : $Int +public let unused3 = 0 +// CHECK: sil_global @${{.*}}unused4{{.*}} : $Int +public var unused4 = 0 + +// These should only be optimized with -wmo. +// CHECK: sil_global hidden [let] @${{.*}}unused5{{.*}} : $Int +// CHECK-WMO-NOT: sil_global hidden [let] @${{.*}}unused5{{.*}} : $Int +let unused5 = 0 +// CHECK: sil_global hidden @${{.*}}unused6{{.*}} : $Int +// CHECK-WMO-NOT: sil_global hidden @${{.*}}unused6{{.*}} : $Int +var unused6 = 0 + +// Edge case: static and static with computed valued +// See Baz - line 71 +// CHECK: sil_global [let] {{.*}}darwin{{.*}} : $Baz + +// CHECK-LABEL: sil [Onone] @${{.*}}test{{.*}} +@_optimize(none) public func test(x: Int) -> Int { + // CHECK: %{{[0-9]+}} = global_addr @${{.*}}used2{{.*}} + // CHECK: %{{[0-9]+}} = global_addr @${{.*}}used1{{.*}} + return used1 + used2 + x +} + +// CHECK-LABEL: sil @${{.*}}storageVar{{.*}} +@inlinable +internal var storageVar: _SwiftEmptyArrayStorage { + // CHECK: return %2 : $_SwiftEmptyArrayStorage + return _swiftEmptyArrayStorage +} + +public struct Bar { + let storage: _SwiftEmptyArrayStorage + + init () { + storage = storageVar + } +} + +public struct Baz { + public init() { } + + public static let darwin = Baz() + public static var currentPlatform: Baz { return .darwin } +} diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index e05cb475e7966..2c4e0e9bafeed 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -9,6 +9,10 @@ import Builtin ////////////////// enum MyNever {} +enum FakeOptional { +case none +case some(T) +} sil @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () sil @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> () @@ -23,6 +27,8 @@ struct NativeObjectPair { sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair class Klass {} +sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () +sil @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () struct MyInt { var value: Builtin.Int32 @@ -39,11 +45,6 @@ struct StructMemberTest { var t : (Builtin.Int32, AnotherStruct) } -enum FakeOptional { -case none -case some(T) -} - class ClassLet { @_hasStorage let aLet: Klass @_hasStorage var aVar: Klass @@ -437,6 +438,28 @@ bb0(%x : @guaranteed $ClassLet): return undef : $() } +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses : +// CHECK: ref_element_addr +// CHECK-NEXT: load_borrow +// CHECK-NEXT: unchecked_ref_cast +// CHECK-NEXT: apply +// CHECK-NEXT: end_borrow +// CHECK-NEXT: return +// CHECK: } // end sil function 'dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses' +sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses : $@convention(thin) (@guaranteed ClassLet) -> () { +bb0(%x : @guaranteed $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + + %p = ref_element_addr %x : $ClassLet, #ClassLet.aLet + %v = load [copy] %p : $*Klass + %c = unchecked_ref_cast %v : $Klass to $Klass + %b = begin_borrow %c : $Klass + apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> () + end_borrow %b : $Klass + destroy_value %c : $Klass + return undef : $() +} + // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_upcast_base // CHECK: ref_element_addr // CHECK-NEXT: load_borrow @@ -566,6 +589,46 @@ bb0(%x : @owned $ClassLet): return undef : $() } +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : +// CHECK: load_borrow +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLetTuple + %v = load [copy] %p : $*(Klass, Klass) + (%v1, %v2) = destructure_tuple %v : $(Klass, Klass) + apply %f(%v1) : $@convention(thin) (@guaranteed Klass) -> () + apply %f(%v2) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %v1 : $Klass + destroy_value %v2 : $Klass + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + +// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : +// CHECK: load_borrow +// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2' +sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : $@convention(thin) (@owned ClassLet) -> () { +bb0(%x : @owned $ClassLet): + %f = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + + %a = begin_borrow %x : $ClassLet + %p = ref_element_addr %a : $ClassLet, #ClassLet.aLet + %v = load [copy] %p : $*Klass + %v_cast = unchecked_ref_cast %v : $Klass to $Builtin.NativeObject + apply %f(%v_cast) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %v_cast : $Builtin.NativeObject + end_borrow %a : $ClassLet + destroy_value %x : $ClassLet + + return undef : $() +} + // CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr @@ -639,7 +702,7 @@ bb0(%x : @owned $ClassLet): return undef : $() } -// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates +// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : // CHECK: [[OUTER:%.*]] = begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: [[INNER:%.*]] = load_borrow @@ -649,12 +712,15 @@ bb0(%x : @owned $ClassLet): // CHECK-NEXT: begin_borrow // CHECK-NEXT: ref_element_addr // CHECK-NEXT: load [copy] -// CHECK-NEXT: apply // CHECK-NEXT: end_borrow // CHECK-NEXT: destroy_value +// CHECK-NEXT: // function_ref +// CHECK-NEXT: function_ref +// CHECK-NEXT: enum // CHECK-NEXT: apply // CHECK-NEXT: destroy_value -sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates : $@convention(thin) (@owned ClassLet) -> () { +// CHECK: } // end sil function 'do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2' +sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : $@convention(thin) (@owned ClassLet) -> () { bb0(%x : @owned $ClassLet): %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () @@ -670,18 +736,17 @@ bb0(%x : @owned $ClassLet): %b = begin_borrow %x : $ClassLet %q = ref_element_addr %b : $ClassLet, #ClassLet.aLet %w = load [copy] %q : $*Klass - %d = begin_borrow %w : $Klass - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () // End the lifetime of the base object first... end_borrow %b : $ClassLet destroy_value %x : $ClassLet // ...then end the lifetime of the copy. - apply %f(%d) : $@convention(thin) (@guaranteed Klass) -> () + %f2 = function_ref @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional) -> () + %w2 = enum $FakeOptional, #FakeOptional.some!enumelt.1, %w : $Klass + apply %f2(%w2) : $@convention(thin) (@guaranteed FakeOptional) -> () - end_borrow %d : $Klass - destroy_value %w : $Klass + destroy_value %w2 : $FakeOptional return undef : $() } @@ -824,8 +889,8 @@ bb0: unreachable } -// Make sure we do perform the optimization if our borrowed value is an -// argument. +// Make sure that since we have a guaranteed argument and do not need to reason +// about end_borrows, we handle this. // // CHECK-LABEL: sil [ossa] @guaranteed_arg_used_by_postdominating_no_return_function : $@convention(thin) (@guaranteed NativeObjectPair) -> MyNever { // CHECK-NOT: copy_value @@ -839,6 +904,23 @@ bb0(%0 : @guaranteed $NativeObjectPair): unreachable } + +// Make sure that since our borrow introducer is a begin_borrow, we do not +// eliminate the copy. +// +// CHECK-LABEL: sil [ossa] @borrowed_val_used_by_postdominating_no_return_function : $@convention(thin) (@owned NativeObjectPair) -> MyNever { +// CHECK: copy_value +// CHECK: } // end sil function 'borrowed_val_used_by_postdominating_no_return_function' +sil [ossa] @borrowed_val_used_by_postdominating_no_return_function : $@convention(thin) (@owned NativeObjectPair) -> MyNever { +bb0(%0 : @owned $NativeObjectPair): + %1 = begin_borrow %0 : $NativeObjectPair + %2 = struct_extract %1 : $NativeObjectPair, #NativeObjectPair.obj1 + %3 = copy_value %2 : $Builtin.NativeObject + %func = function_ref @unreachable_guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever + apply %func(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever + unreachable +} + // Just make sure that we do not crash on this. We should be able to eliminate // everything here. // @@ -855,4 +937,151 @@ bb0(%0 : @guaranteed $NativeObjectPair): destroy_value %2 : $Builtin.NativeObject %9999 = tuple() return %9999 : $() -} \ No newline at end of file +} + +// Just make sure we do not crash here. +// +// CHECK-LABEL: sil [ossa] @do_not_insert_end_borrow_given_deadend : $@convention(thin) (@guaranteed ClassLet) -> () { +// CHECK: copy_value +// CHECK: } // end sil function 'do_not_insert_end_borrow_given_deadend' +sil [ossa] @do_not_insert_end_borrow_given_deadend : $@convention(thin) (@guaranteed ClassLet) -> () { +bb0(%x : @guaranteed $ClassLet): + %f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> () + %p = ref_element_addr %x : $ClassLet, #ClassLet.aLet + %v = load_borrow %p : $*Klass + %c = copy_value %v : $Klass + end_borrow %v : $Klass + apply %f(%c) : $@convention(thin) (@guaranteed Klass) -> () + cond_br undef, bb1, bb2 + +bb1: + destroy_value %c : $Klass + br bb3 + +bb2: + destroy_value %c : $Klass + br bb3 + +bb3: + unreachable +} + +// Make sure that we put the end_borrow on the load_borrow, not LHS or RHS. +// +// CHECK-LABEL: sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[INTERIOR_POINTER:%.*]] = ref_element_addr [[ARG]] +// CHECK: [[BORROWED_VAL:%.*]] = load_borrow [[INTERIOR_POINTER]] +// CHECK: ([[LHS:%.*]], [[RHS:%.*]]) = destructure_tuple [[BORROWED_VAL]] +// CHECK: apply {{%.*}}([[LHS]]) +// CHECK: apply {{%.*}}([[RHS]]) +// CHECK: end_borrow [[BORROWED_VAL]] +// CHECK: } // end sil function 'destructure_load_copy_to_load_borrow' +sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () { +bb0(%0 : @guaranteed $ClassLet): + %1 = ref_element_addr %0 : $ClassLet, #ClassLet.aLetTuple + %2 = load [copy] %1 : $*(Klass, Klass) + (%3, %4) = destructure_tuple %2 : $(Klass, Klass) + %5 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () + %6 = apply %5(%3) : $@convention(thin) (@guaranteed Klass) -> () + %7 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> () + destroy_value %3 : $Klass + destroy_value %4 : $Klass + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @single_init_allocstack : $@convention(thin) (@owned Klass) -> () { +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'single_init_allocstack' +sil [ossa] @single_init_allocstack : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + %2 = load [copy] %1 : $*Klass + + %3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> () + + destroy_value %2 : $Klass + destroy_addr %1 : $*Klass + dealloc_stack %1 : $*Klass + + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @multiple_init_allocstack : $@convention(thin) (@owned Klass) -> () { +// CHECK: load [copy] +// CHECK: } // end sil function 'multiple_init_allocstack' +sil [ossa] @multiple_init_allocstack : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %0a = copy_value %0 : $Klass + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + %2 = load [copy] %1 : $*Klass + + %3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> () + + destroy_value %2 : $Klass + destroy_addr %1 : $*Klass + + store %0a to [init] %1 : $*Klass + destroy_addr %1 : $*Klass + dealloc_stack %1 : $*Klass + + %9999 = tuple() + return %9999 : $() +} + +// We could support this, but for now we are keeping things simple. If we do add +// support, this test will need to be updated. +// +// CHECK-LABEL: sil [ossa] @single_init_wrongblock : $@convention(thin) (@owned Klass) -> () { +// CHECK: load [copy] +// CHECK: } // end sil function 'single_init_wrongblock' +sil [ossa] @single_init_wrongblock : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + br bb1 + +bb1: + store %0 to [init] %1 : $*Klass + %2 = load [copy] %1 : $*Klass + + %3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> () + + destroy_value %2 : $Klass + destroy_addr %1 : $*Klass + dealloc_stack %1 : $*Klass + + %9999 = tuple() + return %9999 : $() +} + +// We could support this, but for now we are keeping things simple. If we do add +// support, this test will need to be updated. +// +// CHECK-LABEL: sil [ossa] @single_init_loadtake : $@convention(thin) (@owned Klass) -> () { +// CHECK: load [copy] +// CHECK: } // end sil function 'single_init_loadtake' +sil [ossa] @single_init_loadtake : $@convention(thin) (@owned Klass) -> () { +bb0(%0 : @owned $Klass): + %1 = alloc_stack $Klass + store %0 to [init] %1 : $*Klass + %2 = load [copy] %1 : $*Klass + + %3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> () + apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> () + + destroy_value %2 : $Klass + + %4 = load [take] %1 : $*Klass + destroy_value %4 : $Klass + dealloc_stack %1 : $*Klass + + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILOptimizer/side-effect.sil b/test/SILOptimizer/side-effect.sil index b08e8616bc21f..e8e163da2a474 100644 --- a/test/SILOptimizer/side-effect.sil +++ b/test/SILOptimizer/side-effect.sil @@ -205,7 +205,7 @@ bb0(%0 : $Builtin.Int1): // CHECK: sil @checkedcast : $@convention(thin) (Builtin.NativeObject) -> () { bb0(%0 : $Builtin.NativeObject): - unconditional_checked_cast %0 : $Builtin.NativeObject to $X + unconditional_checked_cast %0 : $Builtin.NativeObject to X %r = tuple () return %r : $() } diff --git a/test/SILOptimizer/sil_combine.sil b/test/SILOptimizer/sil_combine.sil index ce3eb8d7764a9..34b01f4113b5f 100644 --- a/test/SILOptimizer/sil_combine.sil +++ b/test/SILOptimizer/sil_combine.sil @@ -564,7 +564,7 @@ bb0(%0 : $C): sil @downcast_upcast_roundtrip : $@convention(thin) (HeapBufferStorage) -> HeapBufferStorage { bb0(%0 : $HeapBufferStorage): %2 = upcast %0 : $HeapBufferStorage to $RawBuffer // user: %3 - %3 = unconditional_checked_cast %2 : $RawBuffer to $HeapBufferStorage + %3 = unconditional_checked_cast %2 : $RawBuffer to HeapBufferStorage return %3 : $HeapBufferStorage } @@ -2526,7 +2526,7 @@ sil @alloc_ref_dynamic_with_upcast_metatype : $() -> () { // CHECK: alloc_ref $B sil @alloc_ref_dynamic_after_successful_checked_cast_br : $(@thick B.Type) -> Builtin.Int32 { bb0(%1 : $@thick B.Type): - checked_cast_br [exact] %1 : $@thick B.Type to $@thick B.Type, bb1, bb2 + checked_cast_br [exact] %1 : $@thick B.Type to B.Type, bb1, bb2 bb1(%2 : $@thick B.Type): %3 = alloc_ref_dynamic %2 : $@thick B.Type, $B @@ -2551,7 +2551,7 @@ bb3 (%10: $Builtin.Int32): // CHECK-NEXT: strong_release [[C]] sil @alloc_ref_dynamic_upcast_after_successful_checked_cast_br : $(@thick B.Type) -> Builtin.Int32 { bb0(%1 : $@thick B.Type): - checked_cast_br [exact] %1 : $@thick B.Type to $@thick E.Type, bb1, bb2 + checked_cast_br [exact] %1 : $@thick B.Type to E.Type, bb1, bb2 bb1(%2 : $@thick E.Type): %3 = upcast %2 : $@thick E.Type to $@thick B.Type diff --git a/test/SILOptimizer/sil_combine_apply.sil b/test/SILOptimizer/sil_combine_apply.sil index b7316221951fb..c387831d63ec8 100644 --- a/test/SILOptimizer/sil_combine_apply.sil +++ b/test/SILOptimizer/sil_combine_apply.sil @@ -388,6 +388,20 @@ bb0(%0 : $*Builtin.Int32, %1 : $*Builtin.Int8): return %29 : $() } +sil @actually_not_throwing: $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) + +// CHECK-LABEL: sil @remove_throwing_thin_to_not_throwing_thick_conversion +// CHECK: [[FN:%[0-9]+]] = function_ref +// CHECK: apply [nothrow] [[FN]](%0) +// CHECK: } // end sil function 'remove_throwing_thin_to_not_throwing_thick_conversion' +sil @remove_throwing_thin_to_not_throwing_thick_conversion : $@convention(thin) (Builtin.Int32) -> @owned Builtin.Int32 { +bb0(%0 : $Builtin.Int32): + %2 = function_ref @actually_not_throwing : $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) + %3 = thin_to_thick_function %2 : $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) to $@callee_guaranteed (Builtin.Int32) -> @owned Builtin.Int32 + %5 = apply %3(%0) : $@callee_guaranteed (Builtin.Int32) -> @owned Builtin.Int32 + return %5 : $Builtin.Int32 +} + sil @testCombineClosureHelper : $(Builtin.Int32) -> () // Test function_ref -> partial_apply -> convert_function -> apply. diff --git a/test/SILOptimizer/sil_combine_concrete_existential.sil b/test/SILOptimizer/sil_combine_concrete_existential.sil index 68a13fd2ae513..c51efa8187ff5 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.sil +++ b/test/SILOptimizer/sil_combine_concrete_existential.sil @@ -570,3 +570,48 @@ bb0(%0 : $C): %9 = tuple () return %9 : $() } + +protocol P1 { } +protocol P2 { } +protocol P3 { } + +sil @callee : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : P1, τ_0_1 : P2, τ_0_2 : P3> (@in τ_0_0, @in τ_0_2, @in τ_0_1) -> () + +// Check that the deallocations are created in the right order and the SILVerifier does not complain. + +// CHECK-LABEL: sil @test_multiple_propagated_args +// CHECK-NOT: open_existential_addr +// CHECK-LABEL: } // end sil function 'test_multiple_propagated_args' +sil @test_multiple_propagated_args : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : P1, τ_0_1 : P2, τ_0_2 : P3> (@in τ_0_0, @in τ_0_2, @in τ_0_1) -> () { +bb0(%0 : $*τ_0_0, %1 : $*τ_0_2, %2 : $*τ_0_1): + %5 = alloc_stack $P1 + %6 = init_existential_addr %5 : $*P1, $τ_0_0 + copy_addr [take] %0 to [initialization] %6 : $*τ_0_0 + %8 = alloc_stack $P3 + %9 = init_existential_addr %8 : $*P3, $τ_0_2 + copy_addr [take] %1 to [initialization] %9 : $*τ_0_2 + %11 = alloc_stack $P2 + %12 = init_existential_addr %11 : $*P2, $τ_0_1 + copy_addr [take] %2 to [initialization] %12 : $*τ_0_1 + %15 = function_ref @callee : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : P1, τ_0_1 : P2, τ_0_2 : P3> (@in τ_0_0, @in τ_0_2, @in τ_0_1) -> () + %16 = open_existential_addr mutable_access %5 : $*P1 to $*@opened("76D54B80-FF66-11E9-B604-8C8590A6A134") P1 + %17 = alloc_stack $@opened("76D54B80-FF66-11E9-B604-8C8590A6A134") P1 + copy_addr %16 to [initialization] %17 : $*@opened("76D54B80-FF66-11E9-B604-8C8590A6A134") P1 + %19 = open_existential_addr mutable_access %8 : $*P3 to $*@opened("76D54C34-FF66-11E9-B604-8C8590A6A134") P3 + %20 = alloc_stack $@opened("76D54C34-FF66-11E9-B604-8C8590A6A134") P3 + copy_addr %19 to [initialization] %20 : $*@opened("76D54C34-FF66-11E9-B604-8C8590A6A134") P3 + %22 = open_existential_addr mutable_access %11 : $*P2 to $*@opened("76D54C84-FF66-11E9-B604-8C8590A6A134") P2 + %23 = alloc_stack $@opened("76D54C84-FF66-11E9-B604-8C8590A6A134") P2 + copy_addr %22 to [initialization] %23 : $*@opened("76D54C84-FF66-11E9-B604-8C8590A6A134") P2 + %25 = apply %15<@opened("76D54B80-FF66-11E9-B604-8C8590A6A134") P1, @opened("76D54C84-FF66-11E9-B604-8C8590A6A134") P2, @opened("76D54C34-FF66-11E9-B604-8C8590A6A134") P3>(%17, %20, %23) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 : P1, τ_0_1 : P2, τ_0_2 : P3> (@in τ_0_0, @in τ_0_2, @in τ_0_1) -> () + destroy_addr %11 : $*P2 + dealloc_stack %23 : $*@opened("76D54C84-FF66-11E9-B604-8C8590A6A134") P2 + destroy_addr %8 : $*P3 + dealloc_stack %20 : $*@opened("76D54C34-FF66-11E9-B604-8C8590A6A134") P3 + destroy_addr %5 : $*P1 + dealloc_stack %17 : $*@opened("76D54B80-FF66-11E9-B604-8C8590A6A134") P1 + dealloc_stack %11 : $*P2 + dealloc_stack %8 : $*P3 + dealloc_stack %5 : $*P1 + return %25 : $() +} diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index f7e49003c9333..b202d5fbd0f63 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -101,7 +101,7 @@ struct SS: PPP { // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() - p.returnsOptionalIndirect()?.returnsOptionalIndirect() + _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() } //===----------------------------------------------------------------------===// diff --git a/test/SILOptimizer/sil_combine_uncheck.sil b/test/SILOptimizer/sil_combine_uncheck.sil index ec045678f3582..565c67a35201b 100644 --- a/test/SILOptimizer/sil_combine_uncheck.sil +++ b/test/SILOptimizer/sil_combine_uncheck.sil @@ -21,7 +21,7 @@ class E { // CHECK-NEXT: return sil @test_unconditional_checked_cast : $@convention(thin) (RawBuffer) -> HeapBufferStorage { bb0(%0 : $RawBuffer): - %3 = unconditional_checked_cast %0 : $RawBuffer to $HeapBufferStorage + %3 = unconditional_checked_cast %0 : $RawBuffer to HeapBufferStorage return %3 : $HeapBufferStorage } @@ -60,7 +60,7 @@ bb0(%0 : $*D, %1 : $*E, %2 : $*D): sil @test_unconditional_checked_cast_other_use : $@convention(thin) (@owned C) -> @owned C { bb0(%0 : $C): strong_retain %0 : $C - %3 = unconditional_checked_cast %0 : $C to $C + %3 = unconditional_checked_cast %0 : $C to C strong_release %0 : $C return %3 : $C } @@ -77,7 +77,7 @@ class G : Class { sil @test_unconditional_checked_cast_class_exist : $@convention(thin) (@owned Class) -> @owned G { bb0(%0 : $Class): strong_retain %0 : $Class - %2 = unconditional_checked_cast %0 : $Class to $G + %2 = unconditional_checked_cast %0 : $Class to G strong_release %0 : $Class return %2 : $G } diff --git a/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift b/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift index 3cde3796ed8db..97831516e1bd1 100644 --- a/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift +++ b/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module %S/Inputs/sil_witness_tables_external_input.swift -o %t/Swift.swiftmodule -parse-stdlib -parse-as-library -module-name Swift -module-link-name swiftCore -// RUN: %target-swift-frontend -O -I %t %s -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -I %t %s -Xllvm -sil-disable-pass=late-deadfuncelim -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -I %t %s -emit-sil | %FileCheck -check-prefix=CHECK-DFE %s import Swift @@ -8,6 +9,11 @@ import Swift // // CHECK: sil_witness_table public_external X: P module Swift { +// Also check that late dead-function-elimination is removing externally +// available witness tables. +// +// CHECK-DFE-NOT: sil_witness_table public_external + func doSomething(_ t : T) -> Y { return t.doSomething() } diff --git a/test/SILOptimizer/simplify_cfg.sil b/test/SILOptimizer/simplify_cfg.sil index 1864d228d6eae..44e9a16287587 100644 --- a/test/SILOptimizer/simplify_cfg.sil +++ b/test/SILOptimizer/simplify_cfg.sil @@ -1119,8 +1119,8 @@ sil @redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { bb0(%0 : $Base): // CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () %1 = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] %0 : $Base to $Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] - checked_cast_br [exact] %0 : $Base to $Base, bb2, bb7 +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 // CHECK: bb1 bb1: @@ -1131,7 +1131,7 @@ bb2(%5 : $Base): // CHECK: [[SUCCESS]] %7 = class_method %0 : $Base, #Base.inner!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () // CHECK-NOT: checked_cast_br - checked_cast_br [exact] %0 : $Base to $Base, bb3, bb5 + checked_cast_br [exact] %0 : $Base to Base, bb3, bb5 // CHECK: [[INNER:%.*]] = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () // CHECK: apply [[INNER]] // CHECK: br bb1 @@ -1167,8 +1167,8 @@ sil @not_redundant_checked_cast_br : $@convention(method) (@guaranteed Base) -> bb0(%0 : $Base): // CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () %1 = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] %0 : $Base to $Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] - checked_cast_br [exact] %0 : $Base to $Base, bb2, bb7 +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 bb1: %3 = tuple () @@ -1203,8 +1203,8 @@ bb6(%17 : $()): // CHECK: return bb7: -// CHECK: checked_cast_br [exact] %0 : $Base to $Derived - checked_cast_br [exact] %0 : $Base to $Derived, bb3, bb5 +// CHECK: checked_cast_br [exact] %0 : $Base to Derived + checked_cast_br [exact] %0 : $Base to Derived, bb3, bb5 } // CHECK-LABEL: sil @failing_checked_cast_br @@ -1212,8 +1212,8 @@ sil @failing_checked_cast_br : $@convention(method) (@guaranteed Base) -> () { bb0(%0 : $Base): // CHECK: [[METHOD:%.*]] = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () %1 = class_method %0 : $Base, #Base.middle!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK: checked_cast_br [exact] %0 : $Base to $Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] - checked_cast_br [exact] %0 : $Base to $Base, bb2, bb7 +// CHECK: checked_cast_br [exact] %0 : $Base to Base, [[SUCCESS:bb[0-9]+]], [[FAIL:bb[0-9]+]] + checked_cast_br [exact] %0 : $Base to Base, bb2, bb7 // CHECK-LABEL: bb1 bb1: @@ -1224,13 +1224,13 @@ bb2(%5 : $Base): // CHECK: [[SUCCESS]] // CHECK: [[METHOD2:%.*]] = class_method %0 : $Base, #Base.inner!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () %7 = class_method %0 : $Base, #Base.inner!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () -// CHECK-NOT: checked_cast_br [exact] %0 : $Base to $Derived +// CHECK-NOT: checked_cast_br [exact] %0 : $Base to Derived // CHECK: apply [[METHOD2]] // Check that checked_cast_br [exact] was replaced by a branch to the failure BB of the checked_cast_br. // This is because bb2 is reached via the success branch of the checked_cast_br [exact] from bb0. // It means that the exact dynamic type of %0 is $Base. Thus it cannot be $Derived. // CHECK: br bb1 - checked_cast_br [exact] %5 : $Base to $Derived, bb3, bb5 + checked_cast_br [exact] %5 : $Base to Derived, bb3, bb5 bb3(%9 : $Derived): %10 = function_ref @_TFC3ccb4Base5innerfS0_FT_T_ : $@convention(method) (@guaranteed Base) -> () @@ -1267,7 +1267,7 @@ sil @no_checked_cast_br_threading_with_alloc_ref_stack : $@convention(method) (@ bb0(%0 : $Base): %fu = function_ref @unknown : $@convention(thin) () -> () %fu2 = function_ref @unknown2 : $@convention(thin) () -> () - checked_cast_br [exact] %0 : $Base to $Base, bb1, bb2 + checked_cast_br [exact] %0 : $Base to Base, bb1, bb2 bb1(%1 : $Base): apply %fu() : $@convention(thin) () -> () @@ -1279,7 +1279,7 @@ bb2: bb3: %a = alloc_ref [stack] $Base - checked_cast_br [exact] %0 : $Base to $Base, bb4, bb5 + checked_cast_br [exact] %0 : $Base to Base, bb4, bb5 bb4(%2 : $Base): apply %fu() : $@convention(thin) () -> () @@ -1781,7 +1781,7 @@ class E : B {} sil @checked_cast_anyobject_metatypeinst_to_class : $@convention(thin)() -> () { bb0: %0 = metatype $@thick AnyObject.Protocol - checked_cast_br %0 : $@thick AnyObject.Protocol to $@thick B.Type, bb1, bb2 + checked_cast_br %0 : $@thick AnyObject.Protocol to B.Type, bb1, bb2 bb1(%3 : $@thick B.Type): br bb3 @@ -2156,7 +2156,7 @@ bb3(%a3 : $Builtin.Int1): sil @successful_checked_cast_br_on_alloc_ref : $() -> Builtin.Int32 { bb0: %1 = alloc_ref $B - checked_cast_br [exact] %1 : $B to $B, bb1, bb2 + checked_cast_br [exact] %1 : $B to B, bb1, bb2 bb1(%2 : $B): %3 = integer_literal $Builtin.Int32, 1 @@ -2183,7 +2183,7 @@ sil @failing_checked_cast_br_on_alloc_ref : $() -> Builtin.Int32 { bb0: %1 = alloc_ref $E %2 = upcast %1 : $E to $B - checked_cast_br [exact] %2 : $B to $B, bb1, bb2 + checked_cast_br [exact] %2 : $B to B, bb1, bb2 bb1(%3 : $B): %4 = integer_literal $Builtin.Int32, 1 diff --git a/test/SILOptimizer/simplify_cfg_address_phi.sil b/test/SILOptimizer/simplify_cfg_address_phi.sil index 438ee2cbdf246..923ff8ba4a6cb 100644 --- a/test/SILOptimizer/simplify_cfg_address_phi.sil +++ b/test/SILOptimizer/simplify_cfg_address_phi.sil @@ -134,3 +134,118 @@ bb4: bb5: return %val : $Builtin.Int32 } + +// Test that debug_value_addr is not unnecessarilly lost during address projection sinking. +public class CC { + let r : R + init(_ _r: R) { r = _r } +} + +sil @useAny : $@convention(thin) (@in_guaranteed V) -> () + +// CHECK-LABEL: sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +// CHECK: debug_value_addr %0 : $*S, let, name "u" +// CHECK: apply %{{.*}}(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK: [[FIELD:%.*]] = ref_element_addr %1 : $CC, #CC.r +// CHECK: debug_value_addr [[FIELD]] : $*R, let, name "u" +// CHECK: apply %10([[FIELD]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// CHECK-LABEL: } // end sil function 'testDebugValue' +sil @testDebugValue : $@convention(method) (@in_guaranteed S, @guaranteed CC, Bool) -> () { +bb0(%0 : $*S, %1 : $CC, %2 : $Bool): + %bool = struct_extract %2 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + debug_value_addr %0 : $*S, let, name "u" + %f1 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call1 = apply %f1(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb2 + +bb2: + %field = ref_element_addr %1 : $CC, #CC.r + debug_value_addr %field : $*R, let, name "t" + cond_br %bool, bb3, bb4 + +bb3: + debug_value_addr %field : $*R, let, name "u" + %f2 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %call2 = apply %f2(%field) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + br bb4 + +bb4: + %z = tuple () + return %z : $() +} + +// Test multiple uses and cloned allocation. +// +// project_box and struct_extract_addr will be sunk into three +// different blocks, but only once per block. +struct S { + @_hasStorage @_hasInitialValue var x: Int { get set } + init(x: Int = 0) + init() +} +sil @doNothing : $@convention(thin) (@inout Int) -> () + +// CHECK-LABEL: sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +// CHECK: bb0(%0 : $Bool, %1 : $*Int): +// CHECK: cond_br %{{.*}}, bb2, bb1 +// CHECK: bb1: +// CHECK: [[ALLOC1:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ1:%.*]] = project_box [[ALLOC1]] : ${ var S }, 0 +// CHECK: [[ADR1:%.*]] = struct_element_addr [[PROJ1]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR1]] : $*Int +// CHECK: br bb3([[ALLOC1]] : ${ var S }) +// CHECK: bb2: +// CHECK: apply %{{.*}}(%1) : $@convention(thin) (@inout Int) -> () +// CHECK: [[ALLOC2:%.*]] = alloc_box ${ var S }, var, name "s" +// CHECK: [[PROJ2:%.*]] = project_box [[ALLOC2]] : ${ var S }, 0 +// CHECK: [[ADR2:%.*]] = struct_element_addr [[PROJ2]] : $*S, #S.x +// CHECK: store %{{.*}} to [[ADR2]] : $*Int +// CHECK: apply %{{.*}}([[ADR2]]) : $@convention(thin) (@inout Int) -> () +// CHECK: br bb3([[ALLOC2]] : ${ var S }) +// CHECK: bb3([[BOXARG:%.*]] : ${ var S }): +// CHECK: [[PROJ3:%.*]] = project_box [[BOXARG]] : ${ var S }, 0 +// CHECK: [[ADR3:%.*]] = struct_element_addr [[PROJ3]] : $*S, #S.x +// CHECK: apply %{{.*}}([[ADR3]]) : $@convention(thin) (@inout Int) -> () +// CHECK: release_value [[BOXARG]] : ${ var S } +// CHECK-LABEL: } // end sil function 'testMultiUse' +sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () { +bb0(%0 : $Bool, %1 : $*Int): + %bool = struct_extract %0 : $Bool, #Bool._value + cond_br %bool, bb1, bb2 + +bb1: + %f1 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call1 = apply %f1(%1) : $@convention(thin) (@inout Int) -> () + br bb3 + +bb2: + br bb3 + +bb3: + %box3 = alloc_box ${ var S }, var, name "s" + %proj3 = project_box %box3 : ${ var S }, 0 + %adr3 = struct_element_addr %proj3 : $*S, #S.x + cond_br %bool, bb4, bb5 + +bb4: + %i4 = load %1 : $*Int + store %i4 to %adr3 : $*Int + %f2 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call2 = apply %f2(%adr3) : $@convention(thin) (@inout Int) -> () + br bb6 + +bb5: + %i5 = load %1 : $*Int + store %i5 to %adr3 : $*Int + br bb6 + +bb6: + %f6 = function_ref @doNothing : $@convention(thin) (@inout Int) -> () + %call6 = apply %f6(%adr3) : $@convention(thin) (@inout Int) -> () + release_value %box3 : ${ var S } + %z = tuple () + return %z : $() +} diff --git a/test/SILOptimizer/simplify_cfg_args.sil b/test/SILOptimizer/simplify_cfg_args.sil index 97fcd6dd543c1..8221e537dadaa 100644 --- a/test/SILOptimizer/simplify_cfg_args.sil +++ b/test/SILOptimizer/simplify_cfg_args.sil @@ -92,14 +92,14 @@ bb2(%11 : $Bool): // Preds: bb1 bb3 class A {} //CHECK-LABEL: no_remove_mandatory_dead_args -//CHECK: checked_cast_br {{%.*}} : $AnyObject to $A, bb1 +//CHECK: checked_cast_br {{%.*}} : $AnyObject to A, bb1 //CHECK-NOT: bb1 //CHECK: bb1([[VAR:%[0-9]+]] : $A) //CHECK-NOT: [[VAR]] //CHECK: return sil @no_remove_mandatory_dead_args : $@convention(thin) (@owned AnyObject) -> Int32 { bb0(%0 : $AnyObject): - checked_cast_br %0 : $AnyObject to $A, bb1, bb2 // id: %3 + checked_cast_br %0 : $AnyObject to A, bb1, bb2 // id: %3 bb1(%1 : $A): %2 = integer_literal $Builtin.Int32, 1 diff --git a/test/SILOptimizer/simplify_cfg_opaque.sil b/test/SILOptimizer/simplify_cfg_opaque.sil index 995f2f83e8c50..3f7a7278e8854 100644 --- a/test/SILOptimizer/simplify_cfg_opaque.sil +++ b/test/SILOptimizer/simplify_cfg_opaque.sil @@ -12,7 +12,7 @@ protocol P {} // CHECK: bb1: // CHECK: retain_value %0 : $Any // CHECK: release_value %0 : $Any -// CHECK: checked_cast_value_br %0 : $Any to $P, bb3, bb2 +// CHECK: checked_cast_value_br Any in %0 : $Any to P, bb3, bb2 // CHECK-LABEL: } // end sil function 'testJumpThread' sil @testJumpThread : $@convention(thin) (Any, Any) -> () { bb0(%0 : $Any, %1 : $Any): @@ -24,7 +24,7 @@ bb2: br bb6(%1 : $Any) bb6(%any : $Any): release_value %any : $Any // force jump-threading - checked_cast_value_br %any : $Any to $P, bb7, bb8 + checked_cast_value_br Any in %any : $Any to P, bb7, bb8 bb7(%p : $P): br bb9 bb8: diff --git a/test/SILOptimizer/simplify_cfg_select_enum.sil b/test/SILOptimizer/simplify_cfg_select_enum.sil index c3ac06c349ed9..1841882106615 100644 --- a/test/SILOptimizer/simplify_cfg_select_enum.sil +++ b/test/SILOptimizer/simplify_cfg_select_enum.sil @@ -34,7 +34,7 @@ class Derived2 : Base { } sil @test_checked_cast_br : $@convention(thin) (E, @owned Base, @owned Base, @owned Base, @owned Base) -> Builtin.Int64 { bb0(%0 : $E, %1 : $Base, %2 : $Base, %3 : $Base, %4 : $Base): %s1 = select_enum %0 : $E, case #E.A!enumelt: %1, default %2 : $Base - checked_cast_br %s1 : $Base to $Derived1, bb1, bb2 + checked_cast_br %s1 : $Base to Derived1, bb1, bb2 bb1(%a1 : $Derived1): %i1 = integer_literal $Builtin.Int64, 1 @@ -42,7 +42,7 @@ bb1(%a1 : $Derived1): bb2: %s2 = select_enum %0 : $E, case #E.B!enumelt: %3, default %4 : $Base - checked_cast_br %s2 : $Base to $Derived1, bb3, bb4 + checked_cast_br %s2 : $Base to Derived1, bb3, bb4 bb3(%a2 : $Derived1): %i2 = integer_literal $Builtin.Int64, 2 diff --git a/test/SILOptimizer/specialization_and_resilience.swift b/test/SILOptimizer/specialization_and_resilience.swift new file mode 100644 index 0000000000000..21949b5b2d9da --- /dev/null +++ b/test/SILOptimizer/specialization_and_resilience.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend -parse-as-library -O -module-name=test %s -enable-library-evolution -emit-sil | %FileCheck %s + +public enum En { + case A + case B +} + +@inlinable +@inline(never) +func genfunc(_ t: T) -> T { + return t +} + +// CHECK-LABEL: sil @$s4test11callGenFuncyyF : $@convention(thin) () -> () { +// CHECK: = function_ref @$s4test7genfuncyxxlFAA2EnO_Tg5 : $@convention(thin) (En) -> @out En +// CHECK: } // end sil function '$s4test11callGenFuncyyF' +public func callGenFunc() { + _ = genfunc(En.A) +} diff --git a/test/SILOptimizer/specialize_checked_cast_branch.swift b/test/SILOptimizer/specialize_checked_cast_branch.swift index ca3162c2d0c8d..43e31c690553c 100644 --- a/test/SILOptimizer/specialize_checked_cast_branch.swift +++ b/test/SILOptimizer/specialize_checked_cast_branch.swift @@ -27,7 +27,7 @@ public func ArchetypeToArchetypeCast(t1 : T1, t2 : T2) -> T2 { // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA1DCTg5 : $@convention(thin) (@guaranteed C, @guaranteed D) -> @owned D // CHECK: bb0([[ARG:%.*]] : $C, [[ARG2:%.*]] : $D): -// CHECK: checked_cast_br [[ARG]] : $C to $D, bb1, bb2 +// CHECK: checked_cast_br [[ARG]] : $C to D, bb1, bb2 // // CHECK: bb1([[T0:%.*]] : $D): // CHECK: strong_retain [[ARG]] @@ -175,7 +175,7 @@ _ = ArchetypeToConcreteCastC(t: e) // x -> y where x is a super class of y. // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch24ArchetypeToConcreteCastD1tAA1DCx_tlFAA1CC_Tg5 : $@convention(thin) (@guaranteed C) -> @owned D { // CHECK: bb0([[ARG:%.*]] : $C): -// CHECK: checked_cast_br [[ARG]] : $C to $D, [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] +// CHECK: checked_cast_br [[ARG]] : $C to D, [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] // // CHECK: [[SUCC_BB]]([[T0:%.*]] : $D): // CHECK: strong_retain [[ARG]] @@ -250,7 +250,7 @@ _ = ConcreteToArchetypeCastC(t: c, t2: b) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch24ConcreteToArchetypeCastC1t2t2xAA1CC_xtlFAA1DC_Tg5 : $@convention(thin) (@guaranteed C, @guaranteed D) -> @owned D // CHECK: bb0 -// CHECK: checked_cast_br %0 : $C to $D +// CHECK: checked_cast_br %0 : $C to D // CHECK: bb1 _ = ConcreteToArchetypeCastC(t: c, t2: d) @@ -290,7 +290,7 @@ _ = SuperToArchetypeCastC(c: c, t: c) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch21SuperToArchetypeCastC1c1txAA1CC_xtlFAA1DC_Tg5 : $@convention(thin) (@guaranteed C, @guaranteed D) -> @owned D // CHECK: bb0 -// CHECK: checked_cast_br %0 : $C to $D +// CHECK: checked_cast_br %0 : $C to D // CHECK: bb1 _ = SuperToArchetypeCastC(c: c, t: d) @@ -323,7 +323,7 @@ func ExistentialToArchetypeCast(o : AnyObject, t : T) -> T { // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch26ExistentialToArchetypeCast1o1txyXl_xtlFAA1CC_Tg5 : $@convention(thin) (@guaranteed AnyObject, @guaranteed C) -> @owned C // CHECK: bb0 -// CHECK: checked_cast_br %0 : $AnyObject to $C +// CHECK: checked_cast_br %0 : $AnyObject to C // CHECK: bb1 _ = ExistentialToArchetypeCast(o: o, t: c) diff --git a/test/SILOptimizer/specialize_dynamic_self.swift b/test/SILOptimizer/specialize_dynamic_self.swift index c364c21a6a234..c852fd02475cb 100644 --- a/test/SILOptimizer/specialize_dynamic_self.swift +++ b/test/SILOptimizer/specialize_dynamic_self.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name specialize_dynamic_self -Xllvm -sil-inline-generics -emit-sil -O -primary-file %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name specialize_dynamic_self -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil -O -primary-file %s | %FileCheck %s protocol P {} @@ -10,12 +10,12 @@ extension P { } class C : P { - // CHECK-LABEL: sil shared [always_inline] @$s23specialize_dynamic_self1CC11returnsSelfACyxGXDyFSi_Tg5 : $@convention(method) (@guaranteed C) -> @owned C + // CHECK-LABEL: sil shared [noinline] @$s23specialize_dynamic_self1CC11returnsSelfACyxGXDyFSi_Tg5 : $@convention(method) (@guaranteed C) -> @owned C // CHECK: [[RESULT:%.*]] = alloc_stack $C // CHECK: [[FN:%.*]] = function_ref @$s23specialize_dynamic_self1PPAAE7method1yyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // CHECK: apply [[FN]]<@dynamic_self C>([[RESULT]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // CHECK: return %0 : $C - @inline(__always) + @inline(never) final func returnsSelf() -> Self { method2() return self diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes.swift b/test/SILOptimizer/specialize_opaque_type_archetypes.swift index db98884ed22c4..48cf8c8799248 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes.swift @@ -4,8 +4,7 @@ // RUN: %target-swift-frontend -disable-availability-checking %S/Inputs/specialize_opaque_type_archetypes_4.swift -I %t -enable-library-evolution -module-name External3 -emit-module -emit-module-path %t/External3.swiftmodule // RUN: %target-swift-frontend -disable-availability-checking %S/Inputs/specialize_opaque_type_archetypes_3.swift -I %t -enable-library-evolution -module-name External2 -Osize -emit-module -o - | %target-sil-opt -module-name External2 | %FileCheck --check-prefix=RESILIENT %s // RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -// RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s -// RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -enable-library-evolution -Osize -emit-sil -sil-verify-all %s | %FileCheck %s +// RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -enable-library-evolution -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize import External import External2 @@ -50,12 +49,12 @@ func identity(_ t: T) -> T { // CHECK-LABEL: sil @$s1A10testFooBaryyxAA1PRzlF : $@convention(thin) (@in_guaranteed T) -> () { // CHECK: bb3([[FOOS_INT:%.*]] : $Builtin.Int64): -// CHECK: [[ID:%.*]] = function_ref @$s1A8identityyxxlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> Int64 // CHECK: [[FOO_RES:%.*]] = struct $Int64 ([[FOOS_INT]] : $Builtin.Int64) +// CHECK: [[ID:%.*]] = function_ref @$s1A8identityyxxlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> Int64 // CHECK: [[ID_RES:%.*]] = apply [[ID]]([[FOO_RES]]) : $@convention(thin) (Int64) -> Int64 // CHECK: [[USEP:%.*]] = function_ref @$s1A4usePyyxAA1PRzlFs5Int64V_Tg5 : $@convention(thin) (Int64) -> () -// CHECK: %27 = apply [[USEP]]([[ID_RES]]) : $@convention(thin) (Int64) -> () -// CHECK: %29 = apply [[USEP]]([[FOO_RES]]) : $@convention(thin) (Int64) -> () +// CHECK: apply [[USEP]]([[ID_RES]]) : $@convention(thin) (Int64) -> () +// CHECK: apply [[USEP]]([[FOO_RES]]) : $@convention(thin) (Int64) -> () public func testFooBar(_ t : T) { let x = foo(getInt()) @@ -100,9 +99,8 @@ public func returnC() -> some CP { } // CHECK-LABEL: sil @$s1A4useCyyF -// CHECK: [[INT:%.*]] = struct $Int64 ( -// CHECK: // function_ref specialized useP(_:) // CHECK: [[FUN:%.*]] = function_ref @$s1A4usePyyxAA1PRzlFs5Int64V_Tg5 +// CHECK: [[INT:%.*]] = struct $Int64 ( // CHECK: = apply [[FUN]]([[INT]]) public func useC() { let c = returnC() @@ -167,6 +165,7 @@ struct Container2 { } class Container3 { + @inline(__always) init(member : S.T) { self.member = member } @@ -230,8 +229,8 @@ func nonResilient() -> some ExternalP2 { // CHECK-LABEL: sil @$s1A019usePairResilientNonC0yyF : $@convention(thin) () -> () // CHECK: alloc_stack $Pair: P3 { // Don't assert. // CHECK-LABEL: sil {{.*}} @$s1A7AdapterVyxGAA2P3A2aEP3foo2ATQzyFTW // CHECK: [[F:%.*]] = function_ref @$s1A7AdapterV3fooQryF -// CHECK: apply [[F]]<τ_0_0>(%0, %1) : $@convention(method) <τ_0_0 where τ_0_0 : P3> (@in_guaranteed Adapter<τ_0_0>) -> @out @_opaqueReturnTypeOf("$s1A7AdapterV3fooQryF", 0) +// CHECK: apply [[F]]<τ_0_0>(%0, %1) : $@convention(method) <τ_0_0 where τ_0_0 : P3> (@in_guaranteed Adapter<τ_0_0>) -> extension P3 { public func foo() -> some P3 { return Adapter(inner: self) @@ -271,9 +270,8 @@ extension P3 { // CHECK-LABEL: sil @$s1A21useExternalResilient2yyF : $@convention(thin) () -> () // CHECK: [[RES:%.*]] = alloc_stack $Int64 -// CHECK: [[FUN:%.*]] = function_ref @$s9External226inlinableExternalResilientQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External226inlinableExternalResilientQryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External226inlinableExternalResilientQryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External226inlinableExternalResilientQryF : $@convention(thin) () -> @out Int64 +// CHECK: apply [[FUN]]([[RES]]) // CHECK: return public func useExternalResilient2() { let e = inlinableExternalResilient() @@ -283,9 +281,8 @@ public func useExternalResilient2() { // In this case we should only 'peel' one layer of opaque archetypes. // CHECK-LABEL: sil @$s1A21useExternalResilient3yyF // CHECK: [[RES:%.*]] = alloc_stack $@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) -// CHECK: [[FUN:%.*]] = function_ref @$s9External3031inlinableExternalResilientCallsD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External3031inlinableExternalResilientCallsD0QryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0){{.*}}to $*@_opaqueReturnTypeOf("$s9External3031inlinableExternalResilientCallsD0QryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External3031inlinableExternalResilientCallsD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) +// CHECK: apply [[FUN]]([[RES]]) public func useExternalResilient3() { let e = inlinableExternalResilientCallsResilient() useP(e.myValue3()) @@ -294,9 +291,8 @@ public func useExternalResilient3() { // Check that we can look throught two layers of inlinable resilient functions. // CHECK-LABEL: sil @$s1A21useExternalResilient4yyF // CHECK: [[RES:%.*]] = alloc_stack $Int64 -// CHECK: [[FUN:%.*]] = function_ref @$s9External3040inlinableExternalResilientCallsInlinablecD0QryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s9External3040inlinableExternalResilientCallsInlinablecD0QryF", 0) -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External3040inlinableExternalResilientCallsInlinablecD0QryF", 0) -// CHECK: apply [[FUN]]([[RES2]]) +// CHECK: [[FUN:%.*]] = function_ref @$s9External3040inlinableExternalResilientCallsInlinablecD0QryF : $@convention(thin) () -> @out Int64 +// CHECK: apply [[FUN]]([[RES]]) public func useExternalResilient4() { let e = inlinableExternalResilientCallsInlinableExternalResilient() useP(e.myValue3()) @@ -307,8 +303,7 @@ public func useExternalResilient4() { // CHECK: [[CONTAINER:%.*]] = apply [[CONTAINER_INIT_FUN]] // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[COMPUTED_PROP:%.*]] = function_ref @$s8External0A9ContainerV16computedPropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s8External0A9ContainerV16computedPropertyQrvp", 0) -// CHECK: apply [[COMPUTED_PROP]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[COMPUTED_PROP]]([[RES]], [[CONTAINER]]) // CHECK: [[MYVALUE:%.*]] = function_ref @$ss5Int64V8ExternalE8myValue2AByF : $@convention(method) (Int64) -> Int64 // CHECK: apply [[MYVALUE]] public func testStoredProperty() { @@ -330,8 +325,7 @@ public func testResilientProperty() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External218ResilientContainerV18inlineablePropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV18inlineablePropertyQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty() { let r = ResilientContainer() useP(r.inlineableProperty.myValue3()) @@ -341,8 +335,7 @@ public func testResilientInlinableProperty() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External218ResilientContainerV19inlineableProperty2Qrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV19inlineableProperty2Qrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty3() { let r = ResilientContainer() useP(r.inlineableProperty2.myValue3()) @@ -363,8 +356,7 @@ public func testResilientProperty2() { // CHECK: [[CONTAINER:%.*]] = alloc_stack $ResilientContainer2 // CHECK: [[RES:%.*]] = alloc_stack $@_opaqueReturnTypeOf("$s9External218ResilientContainerV16computedPropertyQrvp", 0) // CHECK: [[FUN:%.*]] = function_ref @$s9External319ResilientContainer2V18inlineablePropertyQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*@_opaqueReturnTypeOf("$s9External218ResilientContainerV16computedPropertyQrvp", 0){{.*}}to $*@_opaqueReturnTypeOf("$s9External319ResilientContainer2V18inlineablePropertyQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINER]]) public func testResilientInlinableProperty2() { let r = ResilientContainer2() useP(r.inlineableProperty.myValue3()) @@ -374,8 +366,7 @@ public func testResilientInlinableProperty2() { // CHECK: [[CONTAINTER:%.*]] = alloc_stack $ResilientContainer2 // CHECK: [[RES:%.*]] = alloc_stack $Int64 // CHECK: [[FUN:%.*]] = function_ref @$s9External319ResilientContainer2V023inlineablePropertyCallsB10InlineableQrvg -// CHECK: [[RES2:%.*]] = unchecked_addr_cast [[RES]] : $*Int64 to $*@_opaqueReturnTypeOf("$s9External319ResilientContainer2V023inlineablePropertyCallsB10InlineableQrvp", 0) -// CHECK: apply [[FUN]]([[RES2]], [[CONTAINTER]]) +// CHECK: apply [[FUN]]([[RES]], [[CONTAINTER]]) public func testResilientInlinablePropertyCallsResilientInlinable() { let r = ResilientContainer2() useP(r.inlineablePropertyCallsResilientInlineable.myValue3()) @@ -421,9 +412,9 @@ func testIt(cl: (Int64) throws -> T) { // CHECK-LABEL: sil shared [noinline] @$s1A16testPartialApplyyyxAA2P4RzlFAA2PAV_Tg5 // CHECK: [[PA:%.*]] = alloc_stack $PA // CHECK: store %0 to [[PA]] : $*PA -// CHECK: [[F:%.*]] = function_ref @$s1A2PAVAA2P4A2aDP3fooy2ATQzs5Int64VFTW : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", 0) -// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[PA]]) : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", 0) -// CHECK: convert_function [[C]] : $@callee_guaranteed (Int64) -> @out @_opaqueReturnTypeOf("$s1A2PAV3fooyQrs5Int64VF", {{.*}} to $@callee_guaranteed (Int64) -> (@out Int64, @error Error) +// CHECK: [[F:%.*]] = function_ref @$s1A2PAVAA2P4A2aDP3fooy2ATQzs5Int64VFTW : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out Int64 +// CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[PA]]) : $@convention(witness_method: P4) (Int64, @in_guaranteed PA) -> @out Int64 +// CHECK: convert_function [[C]] : $@callee_guaranteed (Int64) -> @out Int64 to $@callee_guaranteed (Int64) -> (@out Int64, @error Error) @inline(never) func testPartialApply(_ t: T) { let fun = t.foo @@ -546,3 +537,43 @@ func useAbstractFunction(_ fn: (Int64) -> T) {} public func testThinToThick() { useAbstractFunction(bar) } + +// CHECK-LABEL: sil @$s1A19rdar56410009_normalyyF +public func rdar56410009_normal() { + // CHECK: [[EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External224externalResilientWrapperyQrxAA10ExternalP2RzlF + // CHECK: = apply [[EXTERNAL_RESILIENT_WRAPPER]]<@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸>({{%.+}}, {{%.+}}) : $@convention(thin) <τ_0_0 where τ_0_0 : ExternalP2> (@in_guaranteed τ_0_0) -> @out @_opaqueReturnTypeOf("$s9External224externalResilientWrapperyQrxAA10ExternalP2RzlF", 0) 🦸<τ_0_0> + _ = externalResilientWrapper(externalResilient()) +} // CHECK: end sil function '$s1A19rdar56410009_normalyyF' + +// CHECK-LABEL: sil @$s1A25rdar56410009_inlinedInneryyF +public func rdar56410009_inlinedInner() { + // CHECK: [[EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External224externalResilientWrapperyQrxAA10ExternalP2RzlF + // CHECK: = apply [[EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) <τ_0_0 where τ_0_0 : ExternalP2> (@in_guaranteed τ_0_0) -> @out @_opaqueReturnTypeOf("$s9External224externalResilientWrapperyQrxAA10ExternalP2RzlF", 0) 🦸<τ_0_0> + _ = externalResilientWrapper(inlinableExternalResilient()) +} // CHECK: end sil function '$s1A25rdar56410009_inlinedInneryyF' + +// CHECK-LABEL: sil @$s1A25rdar56410009_inlinedOuteryyF +public func rdar56410009_inlinedOuter() { + // CHECK: [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFAA08externalD0QryFQOyQo__Tg5 + // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (@in_guaranteed @_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸) -> @out WrapperP2<@_opaqueReturnTypeOf("$s9External217externalResilientQryF" + _ = inlinableExternalResilientWrapper(externalResilient()) +} // CHECK: end sil function '$s1A25rdar56410009_inlinedOuteryyF' + +// Specialized from above +// CHECK-LABEL: sil shared [noinline] @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFAA08externalD0QryFQOyQo__Tg5 +// CHECK: [[WRAPPER_INIT:%.+]] = function_ref @$s9External29WrapperP2VyACyxGxcfC +// CHECK: = apply [[WRAPPER_INIT]]<@_opaqueReturnTypeOf("$s9External217externalResilientQryF", 0) 🦸>({{%.+}}, {{%.+}}, {{%.+}}) : $@convention(method) <τ_0_0 where τ_0_0 : ExternalP2> (@in τ_0_0, @thin WrapperP2<τ_0_0>.Type) -> @out WrapperP2<τ_0_0> +// CHECK: end sil function '$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFAA08externalD0QryFQOyQo__Tg5' + +// Specialized from below +// CHECK-LABEL: sil shared [noinline] @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFs5Int64V_Tg5 +// CHECK: [[WRAPPER_INIT:%.+]] = function_ref @$s9External29WrapperP2VyACyxGxcfC +// CHECK: = apply [[WRAPPER_INIT]]({{%.+}}, {{%.+}}, {{%.+}}) : $@convention(method) <τ_0_0 where τ_0_0 : ExternalP2> (@in τ_0_0, @thin WrapperP2<τ_0_0>.Type) -> @out WrapperP2<τ_0_0> +// CHECK: end sil function '$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFs5Int64V_Tg5' + +// CHECK-LABEL: sil @$s1A24rdar56410009_inlinedBothyyF +public func rdar56410009_inlinedBoth() { + // CHECK: [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER:%.+]] = function_ref @$s9External233inlinableExternalResilientWrapperyQrxAA0C2P2RzlFs5Int64V_Tg5 + // CHECK: = apply [[INLINABLE_EXTERNAL_RESILIENT_WRAPPER]]({{%.+}}, {{%.+}}) : $@convention(thin) (Int64) -> @out WrapperP2 + _ = inlinableExternalResilientWrapper(inlinableExternalResilient()) +} // CHECK: end sil function '$s1A24rdar56410009_inlinedBothyyF' diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift b/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift index 9e70b35bb6466..f858632090311 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes_multifile.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -O -Xllvm -enable-opaque-archetype-specializer -disable-availability-checking -primary-file %s %S/Inputs/specialize_opaque_type_archetypes_multifile_A.swift -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -disable-availability-checking -primary-file %s %S/Inputs/specialize_opaque_type_archetypes_multifile_A.swift -emit-sil | %FileCheck %s protocol P {} diff --git a/test/SILOptimizer/specialize_ossa.sil b/test/SILOptimizer/specialize_ossa.sil index 39e82c58ed352..b76d6621600fa 100644 --- a/test/SILOptimizer/specialize_ossa.sil +++ b/test/SILOptimizer/specialize_ossa.sil @@ -25,4 +25,4 @@ bb0(%0 : $Builtin.NativeObject): dealloc_stack %2 : $*Builtin.NativeObject %9999 = tuple() return %9999 : $() -} \ No newline at end of file +} diff --git a/test/SILOptimizer/split_critical_edges.sil b/test/SILOptimizer/split_critical_edges.sil index b922d96a074b6..9c82f8155f0af 100644 --- a/test/SILOptimizer/split_critical_edges.sil +++ b/test/SILOptimizer/split_critical_edges.sil @@ -128,9 +128,9 @@ class ParentNode : Node { // CHECK: bb0( // CHECK: cond_br %1, bb1, bb2 // CHECK: bb1: -// CHECK: checked_cast_br [exact] {{.*}} : $Node to $ParentNode, bb3, bb4 +// CHECK: checked_cast_br [exact] {{.*}} : $Node to ParentNode, bb3, bb4 // CHECK: bb2: -// CHECK: checked_cast_br [exact] {{.*}} : $Node to $ParentNode, bb5, bb6 +// CHECK: checked_cast_br [exact] {{.*}} : $Node to ParentNode, bb5, bb6 // CHECK: bb3({{.*}} : $ParentNode): // CHECK: br bb7 // CHECK: bb4: @@ -147,10 +147,10 @@ bb0(%0 : $Node, %2 : $Builtin.Int1): cond_br %2, ccb1, ccb2 ccb1: - checked_cast_br [exact] %0 : $Node to $ParentNode, bb2, bb3 + checked_cast_br [exact] %0 : $Node to ParentNode, bb2, bb3 ccb2: - checked_cast_br [exact] %0 : $Node to $ParentNode, bb4, bb5 + checked_cast_br [exact] %0 : $Node to ParentNode, bb4, bb5 bb2(%5 : $ParentNode): br bb1 diff --git a/test/SILOptimizer/static_strings.swift b/test/SILOptimizer/static_strings.swift index 6344434749fc2..a4513c1afd4bb 100644 --- a/test/SILOptimizer/static_strings.swift +++ b/test/SILOptimizer/static_strings.swift @@ -5,7 +5,7 @@ // RUN: %target-build-swift -O -module-name=test %s -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT -// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib +// REQUIRES: executable_test,swift_stdlib_no_asserts // REQUIRES: CPU=arm64 || CPU=x86_64 // This is an end-to-end test to ensure that the optimizer generates diff --git a/test/SILOptimizer/temp_rvalue_opt.sil b/test/SILOptimizer/temp_rvalue_opt.sil index 654ab01696812..665b694b00218 100644 --- a/test/SILOptimizer/temp_rvalue_opt.sil +++ b/test/SILOptimizer/temp_rvalue_opt.sil @@ -31,6 +31,8 @@ bb0(%0 : $*Klass, %1 : $*Klass): return %9999 : $() } +sil @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error) + /////////// // Tests // /////////// @@ -104,8 +106,11 @@ bb0(%0 : $*GS, %1 : $*GS): // CHECK-LABEL: sil @take_from_temp // CHECK: bb0(%0 : $*B, %1 : $*GS): -// CHECK-NEXT: struct_element_addr -// CHECK-NEXT: copy_addr [take] +// CHECK-NEXT: [[STK:%.*]] = alloc_stack +// CHECK-NEXT: copy_addr %1 to [initialization] [[STK]] +// CHECK-NEXT: [[INNER:%.*]] = struct_element_addr +// CHECK-NEXT: copy_addr [take] [[INNER]] +// CHECK-NEXT: dealloc_stack // CHECK-NEXT: tuple // CHECK-NEXT: return sil @take_from_temp : $@convention(thin) (@inout B, @inout GS) -> () { @@ -383,6 +388,26 @@ bb0(%0 : $*Klass): return %9 : $() } +// CHECK-LABEL: sil @try_apply_argument : $@convention(thin) (@inout Klass) -> () { +// CHECK-NOT: copy_addr +// CHECK: try_apply {{%[0-9]+}}(%0) +// CHECK: } // end sil function 'try_apply_argument' +sil @try_apply_argument : $@convention(thin) (@inout Klass) -> () { +bb0(%0 : $*Klass): + %1 = alloc_stack $Klass + copy_addr %0 to [initialization] %1 : $*Klass + %5 = function_ref @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error) + try_apply %5(%1) : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error), normal bb1, error bb2 +bb1(%r : $()): + br bb3 +bb2(%e : $Error): + br bb3 +bb3: + destroy_addr %1 : $*Klass + dealloc_stack %1 : $*Klass + %9 = tuple () + return %9 : $() +} // Make sure that we can eliminate temporaries passed via a temporary rvalue to // an @in_guaranteed function. @@ -471,3 +496,162 @@ bb0(%0 : $*Klass): return %12 : $() } +protocol P { + func foo() +} + +sil @getP : $@convention(thin) () -> @out Optional

+ +// CHECK-LABEL: sil @handle_open_existential_addr : $@convention(thin) () -> () { +// CHECK: [[P:%.*]] = unchecked_take_enum_data_addr +// CHECK-NOT: copy_addr +// CHECK: open_existential_addr immutable_access [[P]] +// CHECK: } +sil @handle_open_existential_addr : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Optional

+ %3 = function_ref @getP : $@convention(thin) () -> @out Optional

+ %4 = apply %3(%2) : $@convention(thin) () -> @out Optional

+ cond_br undef, bb1, bb3 + +bb1: + %9 = unchecked_take_enum_data_addr %2 : $*Optional

, #Optional.some!enumelt.1 + %10 = alloc_stack $P + copy_addr %9 to [initialization] %10 : $*P + %13 = open_existential_addr immutable_access %10 : $*P to $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P + %14 = witness_method $@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P, #P.foo!1 : (Self) -> () -> (), %13 : $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + %15 = apply %14<@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + destroy_addr %2 : $*Optional

+ destroy_addr %10 : $*P + dealloc_stack %10 : $*P + dealloc_stack %2 : $*Optional

+ br bb2 + +bb2: + %23 = tuple () + return %23 : $() + +bb3: + destroy_addr %2 : $*Optional

+ dealloc_stack %2 : $*Optional

+ br bb2 +} +// CHECK-LABEL: sil @open_existential_addr_blocks_optimization : $@convention(thin) () -> () { +// CHECK: [[P:%.*]] = alloc_stack $P +// CHECK: copy_addr {{.*}} to [initialization] [[P]] +// CHECK: } +sil @open_existential_addr_blocks_optimization : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Optional

+ %3 = function_ref @getP : $@convention(thin) () -> @out Optional

+ %4 = apply %3(%2) : $@convention(thin) () -> @out Optional

+ cond_br undef, bb1, bb3 + +bb1: + %9 = unchecked_take_enum_data_addr %2 : $*Optional

, #Optional.some!enumelt.1 + %10 = alloc_stack $P + copy_addr %9 to [initialization] %10 : $*P + destroy_addr %2 : $*Optional

+ %13 = open_existential_addr immutable_access %10 : $*P to $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P + %14 = witness_method $@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P, #P.foo!1 : (Self) -> () -> (), %13 : $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + %15 = apply %14<@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + destroy_addr %10 : $*P + dealloc_stack %10 : $*P + dealloc_stack %2 : $*Optional

+ br bb2 + +bb2: + %23 = tuple () + return %23 : $() + +bb3: + destroy_addr %2 : $*Optional

+ dealloc_stack %2 : $*Optional

+ br bb2 +} + +// CHECK-LABEL: sil @witness_method_blocks_optimization : $@convention(thin) () -> () { +// CHECK: [[P:%.*]] = alloc_stack $P +// CHECK: copy_addr {{.*}} to [initialization] [[P]] +// CHECK: } +sil @witness_method_blocks_optimization : $@convention(thin) () -> () { +bb0: + %2 = alloc_stack $Optional

+ %3 = function_ref @getP : $@convention(thin) () -> @out Optional

+ %4 = apply %3(%2) : $@convention(thin) () -> @out Optional

+ cond_br undef, bb1, bb3 + +bb1: + %9 = unchecked_take_enum_data_addr %2 : $*Optional

, #Optional.some!enumelt.1 + %10 = alloc_stack $P + copy_addr %9 to [initialization] %10 : $*P + %13 = open_existential_addr immutable_access %10 : $*P to $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P + destroy_addr %2 : $*Optional

+ %14 = witness_method $@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P, #P.foo!1 : (Self) -> () -> (), %13 : $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + %15 = apply %14<@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637") P>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () + destroy_addr %10 : $*P + dealloc_stack %10 : $*P + dealloc_stack %2 : $*Optional

+ br bb2 + +bb2: + %23 = tuple () + return %23 : $() + +bb3: + destroy_addr %2 : $*Optional

+ dealloc_stack %2 : $*Optional

+ br bb2 +} + +/////////////////////////////////////////////////////////////////////////////// +// Test checkTempObjectDestroy +// Use-after free crashing an XCTest. + +sil @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + +// Do not remove a copy that is released via a load (because +// TempRValueOpt is an address-based optimization does not know how to +// remove releases, and trying to do that would reduce to ARC +// optimization). +// CHECK-LABEL: sil @copyWithLoadRelease : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () { +// CHECK: bb0(%0 : $*Builtin.NativeObject): +// CHECK: [[STK:%.*]] = alloc_stack $Builtin.NativeObject +// CHECK: copy_addr %0 to [initialization] [[STK]] : $*Builtin.NativeObject +// CHECK: [[VAL:%.*]] = load [[STK]] : $*Builtin.NativeObject +// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +// CHECK: release_value [[VAL]] : $Builtin.NativeObject +// CHECK: dealloc_stack [[STK]] : $*Builtin.NativeObject +// CHECK-LABEL: } // end sil function 'copyWithLoadRelease' +sil @copyWithLoadRelease : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () { +bb0(%0 : $*Builtin.NativeObject): + %stk = alloc_stack $Builtin.NativeObject + copy_addr %0 to [initialization] %stk : $*Builtin.NativeObject + %obj = load %stk : $*Builtin.NativeObject + %f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + release_value %obj : $Builtin.NativeObject + dealloc_stack %stk : $*Builtin.NativeObject + %v = tuple () + return %v : $() +} + +// Remove a copy that is released via a load as long as it was a copy [take]. +// CHECK-LABEL: sil @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () { +// CHECK: bb0(%0 : $*Builtin.NativeObject): +// CHECK: [[V:%.*]] = load %0 : $*Builtin.NativeObject +// CHECK: apply %{{.*}}([[V]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () +// CHECK: release_value [[V]] : $Builtin.NativeObject +// CHECK-LABEL: } // end sil function 'takeWithLoadRelease' +sil @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () { +bb0(%0 : $*Builtin.NativeObject): + %stk = alloc_stack $Builtin.NativeObject + copy_addr [take] %0 to [initialization] %stk : $*Builtin.NativeObject + %obj = load %stk : $*Builtin.NativeObject + %f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + %call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + release_value %obj : $Builtin.NativeObject + dealloc_stack %stk : $*Builtin.NativeObject + %v = tuple () + return %v : $() +} diff --git a/test/SILOptimizer/unsafe_guaranteed_peephole.sil b/test/SILOptimizer/unsafe_guaranteed_peephole.sil index d1690a01955a4..aade5b388d262 100644 --- a/test/SILOptimizer/unsafe_guaranteed_peephole.sil +++ b/test/SILOptimizer/unsafe_guaranteed_peephole.sil @@ -250,7 +250,7 @@ bb0(%0 : $Foo, %1 : $Builtin.Int1): // CHECK: bb0([[P:%.*]] : $Foo): // CHECK-NOT: retain // CHECK-NOT: unsafeGuaranteed -// CHECK: checked_cast_br [exact] [[P]] : $Foo to $Foo, bb1, bb3 +// CHECK: checked_cast_br [exact] [[P]] : $Foo to Foo, bb1, bb3 // CHECK: bb1([[P2:%.*]] : $Foo): // CHECK: function_ref @beep : $@convention(thin) () -> () // CHECK: %4 = apply %3() : $@convention(thin) () -> () @@ -271,7 +271,7 @@ bb0(%0 : $Foo): %4 = builtin "unsafeGuaranteed"(%0 : $Foo) : $(Foo, Builtin.Int8) %5 = tuple_extract %4 : $(Foo, Builtin.Int8), 0 %6 = tuple_extract %4 : $(Foo, Builtin.Int8), 1 - checked_cast_br [exact] %5 : $Foo to $Foo, bb1, bb3 + checked_cast_br [exact] %5 : $Foo to Foo, bb1, bb3 bb1(%11 : $Foo): %12 = function_ref @beep : $@convention(thin) () -> () @@ -300,7 +300,7 @@ bb0(%0 : $Foo): %4 = builtin "unsafeGuaranteed"(%0 : $Foo) : $(Foo, Builtin.Int8) %5 = tuple_extract %4 : $(Foo, Builtin.Int8), 0 %6 = tuple_extract %4 : $(Foo, Builtin.Int8), 1 - checked_cast_br [exact] %5 : $Foo to $Foo, bb1, bb3 + checked_cast_br [exact] %5 : $Foo to Foo, bb1, bb3 bb1(%11 : $Foo): %12 = function_ref @beep : $@convention(thin) () -> () diff --git a/test/Sanitizers/asan.swift b/test/Sanitizers/asan.swift index 68334f638b9de..d0849da3128e3 100644 --- a/test/Sanitizers/asan.swift +++ b/test/Sanitizers/asan.swift @@ -1,6 +1,6 @@ // RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -o %t_asan-binary // RUN: %target-codesign %t_asan-binary -// RUN: not env %env-ASAN_OPTIONS=abort_on_error=0 %target-run %t_asan-binary 2>&1 | %FileCheck %s +// RUN: env %env-ASAN_OPTIONS=abort_on_error=0 not %target-run %t_asan-binary 2>&1 | %FileCheck %s // REQUIRES: executable_test // REQUIRES: asan_runtime diff --git a/test/Sanitizers/asan_interface.h b/test/Sanitizers/asan_interface.h new file mode 100644 index 0000000000000..8eaf265b74e61 --- /dev/null +++ b/test/Sanitizers/asan_interface.h @@ -0,0 +1,4 @@ +// This file is a swift bridging header to ASan's interface. It exists so +// we don't need to worry about where the header lives and instead let Clang +// figure out where the header lives. +#include "sanitizer/asan_interface.h" diff --git a/test/Sanitizers/asan_recover.swift b/test/Sanitizers/asan_recover.swift new file mode 100644 index 0000000000000..b65c977b68aeb --- /dev/null +++ b/test/Sanitizers/asan_recover.swift @@ -0,0 +1,103 @@ +// REQUIRES: executable_test +// REQUIRES: asan_runtime +// UNSUPPORTED: windows + +// Check with recovery instrumentation and runtime option to continue execution. +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -emit-ir -o %t.asan_recover.ll +// RUN: %FileCheck -check-prefix=CHECK-IR -input-file=%t.asan_recover.ll %s +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -sanitize-recover=address -import-objc-header %S/asan_interface.h -o %t_asan_recover +// RUN: %target-codesign %t_asan_recover +// RUN: env %env-ASAN_OPTIONS=halt_on_error=0 %target-run %t_asan_recover > %t_asan_recover.stdout 2> %t_asan_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR,CHECK-RECOVER-STDERR -input-file=%t_asan_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-RECOVER-STDOUT -input-file=%t_asan_recover.stdout %s + +// Check with recovery instrumentation but without runtime option to continue execution. +// RUN: env %env-ASAN_OPTIONS=abort_on_error=0,halt_on_error=1 not %target-run %t_asan_recover > %t_asan_no_runtime_recover.stdout 2> %t_asan_no_runtime_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR -input-file=%t_asan_no_runtime_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-NO-RECOVER-STDOUT -input-file=%t_asan_no_runtime_recover.stdout %s + +// Check that without recovery instrumentation and runtime option to continue execution that error recovery does not happen. +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=address -import-objc-header %S/asan_interface.h -o %t_asan_no_recover +// RUN: %target-codesign %t_asan_no_recover +// RUN: env %env-ASAN_OPTIONS=abort_on_error=0,halt_on_error=0 not %target-run %t_asan_no_recover > %t_asan_no_recover.stdout 2> %t_asan_no_recover.stderr +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDERR -input-file=%t_asan_no_recover.stderr %s +// RUN: %FileCheck --check-prefixes=CHECK-COMMON-STDOUT,CHECK-NO-RECOVER-STDOUT -input-file=%t_asan_no_recover.stdout %s + +// We need to test reads via instrumentation not via runtime so try to check +// for calls to unwanted interceptor/runtime functions. +// CHECK-IR-NOT: call {{.+}} @__asan_memcpy +// CHECK-IR-NOT: call {{.+}} @memcpy + +// FIXME: We need this so we can flush stdout but this won't +// work on other Platforms (e.g. Windows). +#if os(Linux) + import Glibc +#else + import Darwin.C +#endif + +// CHECK-COMMON-STDOUT: START +print("START") +fflush(stdout) + +let size:Int = 128; + +func foo(_ rawptr:UnsafeMutablePointer) { + print("Read second element:\(rawptr.advanced(by: 1).pointee)") + fflush(stdout) +} + +// In this test we need multiple issues to occur that ASan can detect. +// Allocating a buffer and artificially poisoning it seems like the best way to +// test this because there's no undefined behavior happening. Hopefully this +// means that the program behaviour after ASan catches an issue should be +// consistent. If we did something different like two use-after-free issues the +// behaviour could be very unpredicatable resulting in a flakey test. +var x = UnsafeMutablePointer.allocate(capacity: size) +x.initialize(repeating: 0, count: size) +__asan_poison_memory_region(UnsafeMutableRawPointer(x), size) + +// Perform accesses that ASan will catch. Note it is important here that +// the reads are performed **in** the instrumented code so that the +// instrumentation catches the access to poisoned memory. I tried doing: +// +// ``` +// var x = x.advanced(by: 0).pointee +// print(x) +// ``` +// +// However, this generated code that called into memcpy rather than performing +// a direct read which meant that ASan caught an issue via its interceptors +// rather than from instrumentation, which does not test the right thing here. +// +// Doing: +// +// ``` +// print("Read first element:\(x.advanced(by: 0).pointee)") +// ``` +// +// seems to do the right thing right now but this seems really fragile. + +// First error +// NOTE: Testing for stackframe `#0` should ensure that the poison read +// happened in instrumentation and not in an interceptor. +// CHECK-COMMON-STDERR: AddressSanitizer: use-after-poison +// CHECK-COMMON-STDERR: #0 0x{{.+}} in main{{.*}} +print("Read first element:\(x.advanced(by: 0).pointee)") +fflush(stdout) +// CHECK-RECOVER-STDOUT: Read first element:0 + +// Second error +// NOTE: Very loose regex is to accomodate if name demangling +// fails. rdar://problem/57235673 +// CHECK-RECOVER-STDERR: AddressSanitizer: use-after-poison +// CHECK-RECOVER-STDERR: #0 0x{{.+}} in {{.*}}foo{{.*}} +// CHECK-RECOVER-STDOUT: Read second element:0 +foo(x) +__asan_unpoison_memory_region(UnsafeMutableRawPointer(x), size) + +x.deallocate(); +// CHECK-NO-RECOVER-STDOUT-NOT: DONE +// CHECK-RECOVER-STDOUT: DONE +print("DONE") +fflush(stdout) diff --git a/test/Sanitizers/scudo.swift b/test/Sanitizers/scudo.swift new file mode 100644 index 0000000000000..f656306333828 --- /dev/null +++ b/test/Sanitizers/scudo.swift @@ -0,0 +1,11 @@ +// RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=scudo -o %t_scudo-binary +// RUN: not %target-run %t_scudo-binary 2>&1 | %FileCheck %s +// REQUIRES: executable_test +// REQUIRES: OS=linux-gnu +// REQUIRES: scudo_runtime + +let allocated = UnsafeMutableRawPointer.allocate(byteCount: 128, alignment: 1) +allocated.deallocate() +allocated.deallocate() + +// CHECK: ERROR: invalid chunk state diff --git a/test/Sanitizers/tsan-emptyarraystorage.swift b/test/Sanitizers/tsan-emptyarraystorage.swift index 2a694791fe2fa..af9deb347e0f8 100644 --- a/test/Sanitizers/tsan-emptyarraystorage.swift +++ b/test/Sanitizers/tsan-emptyarraystorage.swift @@ -1,41 +1,56 @@ // RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=thread -o %t_tsan-binary // RUN: %target-codesign %t_tsan-binary -// RUN: %target-run %t_tsan-binary 2>&1 | %FileCheck %s +// RUN: %target-run %t_tsan-binary 2>&1 | %FileCheck %s --implicit-check-not='ThreadSanitizer' // REQUIRES: executable_test // REQUIRES: tsan_runtime // REQUIRES: foundation // UNSUPPORTED: OS=tvos -// FIXME: This should be covered by "tsan_runtime"; older versions of Apple OSs -// don't support TSan. -// UNSUPPORTED: remote_run - import Foundation let sem = DispatchSemaphore(value: 0) -class T1: Thread { +class T: Thread { + let closure: () -> Void + init(closure: @escaping () -> Void) { + self.closure = closure + } override func main() { - var oneEmptyArray: [[String:String]] = [] - oneEmptyArray.append(contentsOf: []) + closure() sem.signal() } } -let t1 = T1() -t1.start() -class T2: Thread { - override func main() { - var aCompletelyUnrelatedOtherEmptyArray: [[Double:Double]] = [] - aCompletelyUnrelatedOtherEmptyArray.append(contentsOf: []) - sem.signal() - } +func runOnThread(_ closure: @escaping () -> Void) { + let t = T(closure: closure) + t.start() +} + +runOnThread { + var oneEmptyArray: [[String:String]] = [] + oneEmptyArray.append(contentsOf: []) +} +runOnThread { + var aCompletelyUnrelatedOtherEmptyArray: [[Double:Double]] = [] + aCompletelyUnrelatedOtherEmptyArray.append(contentsOf: []) +} +runOnThread { + var array = Array() + array.append(contentsOf: []) +} +runOnThread { + var arraySlice = ArraySlice() + arraySlice.append(contentsOf: []) +} +runOnThread { + var contiguousArray = ContiguousArray() + contiguousArray.append(contentsOf: []) +} + +for _ in 1...5 { + sem.wait() } -let t2 = T2() -t2.start() -sem.wait() -sem.wait() print("Done!") // CHECK: Done! diff --git a/test/Sanitizers/tsan-libdispatch.swift b/test/Sanitizers/tsan-libdispatch.swift index 7df6a1d0da99a..4739735da8795 100644 --- a/test/Sanitizers/tsan-libdispatch.swift +++ b/test/Sanitizers/tsan-libdispatch.swift @@ -1,6 +1,6 @@ // RUN: %target-swiftc_driver %s -g -sanitize=thread %import-libdispatch -o %t_tsan-binary // RUN: %target-codesign %t_tsan-binary -// RUN: not env %env-TSAN_OPTIONS=abort_on_error=0 %target-run %t_tsan-binary 2>&1 | %FileCheck %s +// RUN: env %env-TSAN_OPTIONS=abort_on_error=0 not %target-run %t_tsan-binary 2>&1 | %FileCheck %s // REQUIRES: executable_test // REQUIRES: tsan_runtime // UNSUPPORTED: OS=tvos diff --git a/test/Sanitizers/tsan-norace-deinit-run-time.swift b/test/Sanitizers/tsan-norace-deinit-run-time.swift index 7fae7a6692e42..584448ad3194e 100644 --- a/test/Sanitizers/tsan-norace-deinit-run-time.swift +++ b/test/Sanitizers/tsan-norace-deinit-run-time.swift @@ -1,7 +1,6 @@ // RUN: %target-swiftc_driver %s -g -sanitize=thread %import-libdispatch -target %sanitizers-target-triple -o %t_tsan-binary // RUN: %target-codesign %t_tsan-binary // RUN: env %env-TSAN_OPTIONS=abort_on_error=0 %target-run %t_tsan-binary 2>&1 | %FileCheck %s --dump-input=fail --implicit-check-not='ThreadSanitizer' -// REQUIRES: rdar55880585 // REQUIRES: executable_test // REQUIRES: tsan_runtime diff --git a/test/Sanitizers/tsan.swift b/test/Sanitizers/tsan.swift index efb3ef954d305..e7f72ed918237 100644 --- a/test/Sanitizers/tsan.swift +++ b/test/Sanitizers/tsan.swift @@ -1,6 +1,6 @@ // RUN: %target-swiftc_driver %s -target %sanitizers-target-triple -g -sanitize=thread %import-libdispatch -o %t_tsan-binary // RUN: %target-codesign %t_tsan-binary -// RUN: not env %env-TSAN_OPTIONS="abort_on_error=0" %target-run %t_tsan-binary 2>&1 | %FileCheck %s +// RUN: env %env-TSAN_OPTIONS="abort_on_error=0" not %target-run %t_tsan-binary 2>&1 | %FileCheck %s // REQUIRES: executable_test // REQUIRES: tsan_runtime // UNSUPPORTED: OS=tvos diff --git a/test/Sema/Inputs/diag_non_ephemeral_globals.swift b/test/Sema/Inputs/diag_non_ephemeral_globals.swift new file mode 100644 index 0000000000000..d5b0540b12810 --- /dev/null +++ b/test/Sema/Inputs/diag_non_ephemeral_globals.swift @@ -0,0 +1,2 @@ + +var globalS = S() diff --git a/test/Sema/Inputs/diag_non_ephemeral_module1.swift b/test/Sema/Inputs/diag_non_ephemeral_module1.swift new file mode 100644 index 0000000000000..7f813e32eff92 --- /dev/null +++ b/test/Sema/Inputs/diag_non_ephemeral_module1.swift @@ -0,0 +1,39 @@ + +public struct ResilientStruct { + public static var staticStoredProperty: Int8 = 0 + public var storedProperty: Int8 = 0 + public init() {} +} + +@frozen +public struct FragileStruct { + @_fixed_layout + public static var staticStoredProperty: Int8 = 0 + public var storedProperty: Int8 = 0 + public init() {} +} + +public final class ResilientFinalClass { + public var storedProperty: Int8 = 0 + public init() {} +} + +@_fixed_layout +public final class FragileFinalClass { + public var storedProperty: Int8 = 0 + public init() {} +} + +public var globalResilient: Int8 = 0 + +@_fixed_layout +public var globalFragile: Int8 = 0 + +@_fixed_layout +public var overloadedVar = 0 + +@_fixed_layout +public var overloadedVarOnlyOneResilient = 0 + +@_fixed_layout +public var overloadedVarDifferentTypes = 0 diff --git a/test/Sema/Inputs/diag_non_ephemeral_module2.swift b/test/Sema/Inputs/diag_non_ephemeral_module2.swift new file mode 100644 index 0000000000000..26304d4b31946 --- /dev/null +++ b/test/Sema/Inputs/diag_non_ephemeral_module2.swift @@ -0,0 +1,9 @@ + +@_fixed_layout +public var overloadedVar = 0 + +// Resilient, therefore produces ephemeral pointer. +public var overloadedVarOnlyOneResilient = 0 + +@_fixed_layout +public var overloadedVarDifferentTypes = "" diff --git a/test/Sema/accessibility_private.swift b/test/Sema/accessibility_private.swift index 8eeaf439178a8..b65bb348ec8e5 100644 --- a/test/Sema/accessibility_private.swift +++ b/test/Sema/accessibility_private.swift @@ -193,7 +193,7 @@ extension Container { fileprivate typealias PrivateAlias = VeryPrivateStruct // expected-error {{type alias cannot be declared fileprivate because its underlying type uses a private type}} {{none}} fileprivate subscript(_: VeryPrivateStruct) -> Void { return () } // expected-error {{subscript cannot be declared fileprivate because its index uses a private type}} {{none}} fileprivate func privateMethod(_: VeryPrivateStruct) -> Void {} // expected-error {{method cannot be declared fileprivate because its parameter uses a private type}} {{none}} - fileprivate enum PrivateRawValue: VeryPrivateStruct {} // expected-error {{enum cannot be declared fileprivate because its raw type uses a private type}} {{none}} expected-note {{do you want to add protocol stubs?}} + fileprivate enum PrivateRawValue: VeryPrivateStruct {} // expected-error {{enum cannot be declared fileprivate because its raw type uses a private type}} {{none}} // expected-error@-1 {{raw type 'Container.VeryPrivateStruct' is not expressible by a string, integer, or floating-point literal}} // expected-error@-2 {{'Container.PrivateRawValue' declares raw type 'Container.VeryPrivateStruct', but does not conform to RawRepresentable and conformance could not be synthesized}} // expected-error@-3 {{RawRepresentable conformance cannot be synthesized because raw type 'Container.VeryPrivateStruct' is not Equatable}} diff --git a/test/Sema/call_as_function_generic.swift b/test/Sema/call_as_function_generic.swift index e86406180d86d..57c3f1a1e7b49 100644 --- a/test/Sema/call_as_function_generic.swift +++ b/test/Sema/call_as_function_generic.swift @@ -41,3 +41,19 @@ let genericString = GenericType<[String]>(collection: ["Hello", "world", "!"]) _ = genericString("Hello") let genericInt = GenericType>(collection: [1, 2, 3]) _ = genericInt(initialValue: 1) + +// SR-11386 +class C {} +protocol P1 {} +extension C where T : P1 { // expected-note {{where 'T' = 'Int'}} + func callAsFunction(t: T) {} +} + +struct S0 : P1 {} + +func testCallAsFunctionWithWhereClause(_ c1: C, _ c2: C, _ s0: S0) { + c1(42) // expected-error {{referencing instance method 'callAsFunction(t:)' on 'C' requires that 'Int' conform to 'P1'}} + // expected-error@-1 {{missing argument label 't:' in call}} + + c2(t: s0) // Okay. +} diff --git a/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index 0c1253dd2ac17..55dc76b3b7318 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -11,11 +11,7 @@ struct SimpleCallable { let foo = SimpleCallable() _ = foo(1) _ = foo(foo(1)) - -// TODO(SR-11378): Improve this error to match the error using a direct `callAsFunction` member reference. -// expected-error @+2 {{cannot call value of non-function type 'SimpleCallable'}} -// expected-error @+1 {{cannot invoke 'foo' with an argument list of type '(Int, Int)'}} -_ = foo(1, 1) +_ = foo(1, 1) // expected-error@:12 {{extra argument in call}} // expected-error @+1 {{cannot convert value of type 'SimpleCallable' to specified type '(Float) -> Float'}} let _: (Float) -> Float = foo @@ -106,9 +102,7 @@ struct Mutating { } } func testMutating(_ x: Mutating, _ y: inout Mutating) { - // TODO(SR-11378): Improve this error to match the error using a direct `callAsFunction` member reference. - // expected-error @+2 {{cannot call value of non-function type 'Mutating'}} - // expected-error @+1 {{cannot invoke 'x' with no arguments}} + // expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} _ = x() // expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} _ = x.callAsFunction() @@ -196,3 +190,49 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, _ = try? h() _ = try? h { throw DummyError() } } + +// SR-11778 +struct DoubleANumber { + func callAsFunction(_ x: Int, completion: (Int) -> Void = { _ in }) { + completion(x + x) + } +} + +func testDefaults(_ x: DoubleANumber) { + x(5) + x(5, completion: { _ in }) +} + +// SR-11881 +struct IUOCallable { + static var callable: IUOCallable { IUOCallable() } + func callAsFunction(_ x: Int) -> IUOCallable! { nil } +} + +func testIUOCallAsFunction(_ x: IUOCallable) { + let _: IUOCallable = x(5) + let _: IUOCallable? = x(5) + let _ = x(5) + + let _: IUOCallable = .callable(5) + let _: IUOCallable? = .callable(5) +} + +// Test access control. +struct PrivateCallable { + private func callAsFunction(_ x: Int) {} // expected-note {{'callAsFunction' declared here}} +} + +func testAccessControl(_ x: PrivateCallable) { + x(5) // expected-error {{'callAsFunction' is inaccessible due to 'private' protection level}} +} + +struct SR_11909 { + static let s = SR_11909() + func callAsFunction(_ x: Int = 0) -> SR_11909 { SR_11909() } +} + +func testDefaultsWithUMEs(_ x: SR_11909) { + let _: SR_11909 = .s() + let _: SR_11909 = .s(5) +} diff --git a/test/Sema/circular_decl_checking.swift b/test/Sema/circular_decl_checking.swift index acb78f0520803..3e8d6e2a9a542 100644 --- a/test/Sema/circular_decl_checking.swift +++ b/test/Sema/circular_decl_checking.swift @@ -23,9 +23,10 @@ class HasGenericFunc { } } -class HasProp { +class HasProp { // expected-note {{'HasProp' declared here}} var HasProp: HasProp { - return HasProp() // expected-error {{cannot call value of non-function type 'HasProp'}}{{19-21=}} + return HasProp() // expected-error {{use of 'HasProp' refers to instance method rather than class 'HasProp' in module 'circular_decl_checking'}} + // expected-note@-1 {{use 'circular_decl_checking.' to reference the class in module 'circular_decl_checking'}} {{12-12=circular_decl_checking.}} } var SomethingElse: SomethingElse? { // expected-error {{use of undeclared type 'SomethingElse'}} return nil diff --git a/test/Sema/clang_types_in_ast.swift b/test/Sema/clang_types_in_ast.swift new file mode 100644 index 0000000000000..f5bcbf6f37cfc --- /dev/null +++ b/test/Sema/clang_types_in_ast.swift @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH1 +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH1 -use-clang-function-types +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH2 -sdk %clang-importer-sdk +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH2 -sdk %clang-importer-sdk -use-clang-function-types +// RUN: %target-swift-frontend %s -DAUXMODULE -module-name Foo -emit-module -o %t + +// rdar://problem/57644243 : We shouldn't crash if -use-clang-function-types is not enabled. +// RUN: %target-swift-frontend %s -typecheck -DNOCRASH3 -I %t + +// FIXME: [clang-function-type-serialization] This should stop crashing once we +// start serializing clang function types. +// RUN: not --crash %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types + +#if NOCRASH1 +public func my_signal() -> Optional<@convention(c) (Int32) -> Void> { + let s : Optional<@convention(c) (Int32) -> Void> = .none; + var s2 : Optional<@convention(c) (Int32) -> Void> = s; + return s2; +} +#endif + +#if NOCRASH2 +import ctypes +func f() { + _ = getFunctionPointer() as (@convention(c) (CInt) -> CInt)? +} +#endif + +#if AUXMODULE +public var DUMMY_SIGNAL1 : Optional<@convention(c) (Int32) -> ()> = .none +public var DUMMY_SIGNAL2 : Optional<@convention(c) (Int32) -> Void> = .none +#endif + +#if NOCRASH3 +import Foo +public func my_signal1() -> Optional<@convention(c) (Int32) -> ()> { + return Foo.DUMMY_SIGNAL1 +} +public func my_signal2() -> Optional<@convention(c) (Int32) -> Void> { + return Foo.DUMMY_SIGNAL1 +} +public func my_signal3() -> Optional<@convention(c) (Int32) -> ()> { + return Foo.DUMMY_SIGNAL2 +} +public func my_signal4() -> Optional<@convention(c) (Int32) -> Void> { + return Foo.DUMMY_SIGNAL2 +} +#endif + +#if CRASH +import Foo +public func my_signal1() -> Optional<@convention(c) (Int32) -> ()> { + return Foo.DUMMY_SIGNAL1 +} +public func my_signal2() -> Optional<@convention(c) (Int32) -> Void> { + return Foo.DUMMY_SIGNAL1 +} +public func my_signal3() -> Optional<@convention(c) (Int32) -> ()> { + return Foo.DUMMY_SIGNAL2 +} +public func my_signal4() -> Optional<@convention(c) (Int32) -> Void> { + return Foo.DUMMY_SIGNAL2 +} +#endif diff --git a/test/Sema/classes_equatable_hashable.swift b/test/Sema/classes_equatable_hashable.swift new file mode 100644 index 0000000000000..ef2b12281e2a4 --- /dev/null +++ b/test/Sema/classes_equatable_hashable.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-frontend -typecheck -verify -primary-file %s + +class Foo: Equatable {} +// expected-error@-1 {{type 'Foo' does not conform to protocol 'Equatable'}} expected-note@-1 {{automatic synthesis of 'Equatable' is not supported for classes}} + +class Bar: Hashable {} +// expected-error@-1 {{type 'Bar' does not conform to protocol 'Hashable'}} expected-note@-1 {{automatic synthesis of 'Hashable' is not supported for classes}} +// expected-error@-2 {{type 'Bar' does not conform to protocol 'Equatable'}} expected-note@-2 {{automatic synthesis of 'Equatable' is not supported for classes}} diff --git a/test/Sema/diag_ambiguous_overloads.swift b/test/Sema/diag_ambiguous_overloads.swift index aa6b117fec318..52891a8f34dfe 100644 --- a/test/Sema/diag_ambiguous_overloads.swift +++ b/test/Sema/diag_ambiguous_overloads.swift @@ -26,10 +26,10 @@ struct S { f({x in x}, 2) // expected-error {{generic parameter 'T' could not be inferred}} } - func g(_ a: T, _ b: Int) -> Void {} // expected-note {{in call to function 'g'}} + func g(_ a: T, _ b: Int) -> Void {} func g(_ a: String) -> Void {} func test2() -> Void { - g(.notAThing, 7) // expected-error {{generic parameter 'T' could not be inferred}} + g(.notAThing, 7) // expected-error {{cannot infer contextual base in reference to member 'notAThing'}} } func h(_ a: Int, _ b: Int) -> Void {} @@ -71,14 +71,14 @@ _ = sr7918_Suit.foo(&myRNG) // expected-error {{cannot pass immutable value as i //=-------------- SR-7786 --------------=/ struct sr7786 { func foo() -> UInt { return 0 } - func foo(bar: T) -> T { + func foo(bar: T) -> T { // expected-note {{where 'T' = 'Int'}} return bar } } let s = sr7786() let a = s.foo() -let b = s.foo(bar: 123) // expected-error {{argument type 'Int' does not conform to expected type 'UnsignedInteger'}} +let b = s.foo(bar: 123) // expected-error {{instance method 'foo(bar:)' requires that 'Int' conform to 'UnsignedInteger'}} let c: UInt = s.foo(bar: 123) let d = s.foo(bar: 123 as UInt) diff --git a/test/Sema/diag_erroneous_iuo.swift b/test/Sema/diag_erroneous_iuo.swift index 212ed04b1c299..b6dbdfafa8508 100644 --- a/test/Sema/diag_erroneous_iuo.swift +++ b/test/Sema/diag_erroneous_iuo.swift @@ -219,6 +219,6 @@ struct T { } func select(i: Int!, m: Int, t: T) { - let _ = i ? i : m // expected-error {{result values in '? :' expression have mismatching types 'Int?' and 'Int'}} - let _ = t.i ? t.j : t.k // expected-error {{result values in '? :' expression have mismatching types 'Int?' and 'Int'}} + let _ = i ? i : m // expected-error {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} {{11-11=(}} {{12-12= != nil)}} + let _ = t.i ? t.j : t.k // expected-error {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} {{11-11=(}} {{14-14= != nil)}} } diff --git a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift index 28566417e0fbf..abca5b92aa279 100644 --- a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift +++ b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift @@ -3,6 +3,6 @@ class C {} func test(c: AnyClass) { - let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') does not conform to specified type 'AnyObject'}} - let _: AnyObject = C.self // expected-error {{value of type 'C.Type' does not conform to specified type 'AnyObject'}} + let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') expected to be instance of class or class-constrained type}} + let _: AnyObject = C.self // expected-error {{value of type 'C.Type' expected to be instance of class or class-constrained type}} } diff --git a/test/Sema/diag_mismatched_magic_literals.swift b/test/Sema/diag_mismatched_magic_literals.swift new file mode 100644 index 0000000000000..2701e39b6472b --- /dev/null +++ b/test/Sema/diag_mismatched_magic_literals.swift @@ -0,0 +1,186 @@ +// RUN: %target-typecheck-verify-swift + +func callee(file: String = #file) {} // expected-note {{'file' declared here}} +func callee(optFile: String? = #file) {} // expected-note {{'optFile' declared here}} +func callee(arbitrary: String) {} + +class SomeClass { + static func callee(file: String = #file) {} // expected-note 2{{'file' declared here}} + static func callee(optFile: String? = #file) {} // expected-note {{'optFile' declared here}} + static func callee(arbitrary: String) {} + + func callee(file: String = #file) {} // expected-note 2{{'file' declared here}} + func callee(optFile: String? = #file) {} // expected-note {{'optFile' declared here}} + func callee(arbitrary: String) {} +} + +// +// Basic functionality +// + +// We should warn when we we pass a `#function`-defaulted argument to a +// `#file`-defaulted argument. +func bad(function: String = #function) { + // expected-note@-1 3{{did you mean for parameter 'function' to default to '#file'?}} {{29-38=#file}} + callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{16-16=(}} {{24-24=)}} + + SomeClass.callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{26-26=(}} {{34-34=)}} + + SomeClass().callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{28-28=(}} {{36-36=)}} +} + +// We should not warn when we pass a `#file`-defaulted argument to a +// `#file`-defaulted argument. +func good(file: String = #file) { + callee(file: file) + + SomeClass.callee(file: file) + + SomeClass().callee(file: file) +} + +// We should not warn when we surround the `#function`-defaulted argument +// with parentheses, which explicitly silences the warning. +func disabled(function: String = #function) { + callee(file: (function)) + + SomeClass.callee(file: (function)) + + SomeClass().callee(file: (function)) +} + +// +// With implicit instance `self` +// + +extension SomeClass { + // We should warn when we we pass a `#function`-defaulted argument to a + // `#file`-defaulted argument. + func bad(function: String = #function) { + // expected-note@-1 1{{did you mean for parameter 'function' to default to '#file'?}} {{31-40=#file}} + callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{18-18=(}} {{26-26=)}} + } + + // We should not warn when we pass a `#file`-defaulted argument to a + // `#file`-defaulted argument. + func good(file: String = #file) { + callee(file: file) + } + + // We should not warn when we surround the `#function`-defaulted argument + // with parentheses, which explicitly silences the warning. + func disabled(function: String = #function) { + callee(file: (function)) + } +} + +// +// With implicit type `self` +// + +extension SomeClass { + // We should warn when we we pass a `#function`-defaulted argument to a + // `#file`-defaulted argument. + static func bad(function: String = #function) { + // expected-note@-1 1{{did you mean for parameter 'function' to default to '#file'?}} {{38-47=#file}} + callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{18-18=(}} {{26-26=)}} + } + + // We should not warn when we pass a `#file`-defaulted argument to a + // `#file`-defaulted argument. + static func good(file: String = #file) { + callee(file: file) + } + + // We should not warn when we surround the `#function`-defaulted argument + // with parentheses, which explicitly silences the warning. + static func disabled(function: String = #function) { + callee(file: (function)) + } +} + +// +// Looking through implicit conversions +// +// Same as above, but these lift the argument from `String` to `String?`, so +// the compiler has to look through the implicit conversion. +// + +// We should warn when we we pass a `#function`-defaulted argument to a +// `#file`-defaulted argument. +func optionalBad(function: String = #function) { + // expected-note@-1 3{{did you mean for parameter 'function' to default to '#file'?}} {{37-46=#file}} + callee(optFile: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'optFile', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{19-19=(}} {{27-27=)}} + + SomeClass.callee(optFile: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'optFile', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{29-29=(}} {{37-37=)}} + + SomeClass().callee(optFile: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'optFile', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{31-31=(}} {{39-39=)}} +} + +// We should not warn when we pass a `#file`-defaulted argument to a +// `#file`-defaulted argument. +func optionalGood(file: String = #file) { + callee(optFile: file) + + SomeClass.callee(optFile: file) + + SomeClass().callee(optFile: file) +} + +// We should not warn when we surround the `#function`-defaulted argument +// with parentheses, which explicitly silences the warning. +func optionalDisabled(function: String = #function) { + callee(optFile: (function)) + + SomeClass.callee(optFile: (function)) + + SomeClass().callee(optFile: (function)) +} + +// +// More negative cases +// + +// We should not warn if the caller's parameter has no default. +func explicit(arbitrary: String) { + callee(file: arbitrary) + + SomeClass.callee(file: arbitrary) + + SomeClass().callee(file: arbitrary) +} + +// We should not warn if the caller's parameter has a non-magic-identifier +// default. +func empty(arbitrary: String = "") { + callee(file: arbitrary) + + SomeClass.callee(file: arbitrary) + + SomeClass().callee(file: arbitrary) +} + +// We should not warn if the callee's parameter has no default. +func ineligible(function: String = #function) { + callee(arbitrary: function) + + SomeClass.callee(arbitrary: function) + + SomeClass().callee(arbitrary: function) +} diff --git a/test/Sema/diag_non_ephemeral.swift b/test/Sema/diag_non_ephemeral.swift new file mode 100644 index 0000000000000..451d625989865 --- /dev/null +++ b/test/Sema/diag_non_ephemeral.swift @@ -0,0 +1,522 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -parse-as-library -o %t -module-name=ModuleA %S/Inputs/diag_non_ephemeral_module1.swift +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -parse-as-library -o %t -module-name=ModuleB %S/Inputs/diag_non_ephemeral_module2.swift +// RUN: cp %s %t/main.swift +// RUN: %target-swift-frontend -typecheck -verify -enable-invalid-ephemeralness-as-error -I %t %t/main.swift %S/Inputs/diag_non_ephemeral_globals.swift + +import ModuleA +import ModuleB + +func takesMutableRaw(@_nonEphemeral _ x: UnsafeMutableRawPointer, _ y: Int) {} +func takesConst(@_nonEphemeral _ x: UnsafePointer, _ y: Int) {} +func takesRaw(@_nonEphemeral _ x: UnsafeRawPointer) {} +func takesMutable(@_nonEphemeral _ x: UnsafeMutablePointer) {} +func takesOptMutableRaw(@_nonEphemeral _ x: UnsafeMutableRawPointer?) {} +func takesOptConst(@_nonEphemeral _ x: UnsafePointer?) {} + +var str = "" +var optionalStr: String? + +var arr: [Int8] = [5] +var optionalArr: [Int8]? + +// We cannot use array-to-pointer and string-to-pointer conversions with +// non-ephemeral parameters. + +takesMutableRaw(&arr, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesConst(str, 5) // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'takesConst'}} +// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConst'}} +// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + +takesConst(arr, 5) // expected-error {{cannot pass '[Int8]' to parameter; argument #1 must be a pointer that outlives the call to 'takesConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConst'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&arr) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutable(&arr) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutable'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutable'}} +// expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesOptMutableRaw(&arr) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesOptMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'takesOptMutableRaw'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// FIXME(SR-9100): This currently uses inout-to-pointer instead of array-to-pointer. +takesOptMutableRaw(&optionalArr) + +takesOptConst(arr) // expected-error {{cannot pass '[Int8]' to parameter; argument #1 must be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesOptConst(optionalArr) // expected-error {{cannot pass '[Int8]?' to parameter; argument #1 must be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} + +takesOptConst(str) // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} +// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + +takesOptConst(optionalStr) // expected-error {{cannot pass 'String?' to parameter; argument #1 must be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from 'String?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} + +struct S { + static var staticStoredProperty: Int8 = 0 + var storedProperty: Int8 = 0 + var storedFinalC = FinalC() + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +class C { + static var staticStoredProperty = S() + var storedProperty: Int8 = 0 + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +final class FinalC { + static var staticStoredProperty = S() + var storedProperty: Int8 = 0 + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +protocol P { + static var staticProperty: Int8 { get set } + var property: Int8 { get set } + subscript() -> Int8 { get set } +} +func makeP() -> P { while true {} } + +var value: Int8 = 5 +var topLevelS = S() +var topLevelC = C() +var topLevelFinalC = FinalC() +var topLevelP = makeP() +var topLevelTupleOfS = (S(), S()) +var topLevelOptOfS: S? +var topLevelWithObservers: Int8 = 0 { didSet {} } + +var topLevelResilientS = ResilientStruct() +var topLevelOptOfResilientS: ResilientStruct? +var topLevelFragileS = FragileStruct() + +var topLevelResilientFinalC = ResilientFinalClass() +var topLevelFragileFinalC = FragileFinalClass() + +let metatypeOfC = C.self + +// We can get stable pointer values from fragile global and static stored +// variables, as long as they don't have property observers. +takesMutableRaw(&value, 5) +takesMutableRaw(&str, 5) +takesMutableRaw(&topLevelS, 5) +takesRaw(&topLevelC) +takesRaw(&S.staticStoredProperty) +takesRaw(&C.staticStoredProperty) +takesRaw(&FinalC.staticStoredProperty) +takesRaw(&metatypeOfC.staticStoredProperty) +takesRaw(&type(of: topLevelC).staticStoredProperty) +takesRaw(&FragileStruct.staticStoredProperty) +takesRaw(&type(of: topLevelFragileS).staticStoredProperty) +takesRaw(&globalFragile) +takesRaw(&globalS) +takesRaw(&topLevelResilientS) +takesRaw(&topLevelFragileS) +takesRaw(&topLevelP) +takesRaw(&value) +takesRaw(&str) +takesMutable(&value) +takesOptMutableRaw(&value) +takesOptMutableRaw(&str) + +extension C { + static func foo() { + takesRaw(&staticStoredProperty) + } +} + +// We can also project stable pointer values from stored properties on such +// global and static stored variables, as long as the base type is a fragile +// struct or a tuple. +takesRaw(&C.staticStoredProperty.storedProperty) +takesRaw(&FinalC.staticStoredProperty.storedProperty) +takesRaw(&metatypeOfC.staticStoredProperty.storedProperty) +takesRaw(&type(of: topLevelC).staticStoredProperty.storedProperty) +takesRaw(&topLevelS.storedProperty) +takesRaw(&globalS.storedProperty) +takesRaw(&topLevelTupleOfS.0) +takesRaw(&topLevelTupleOfS.0.storedProperty) +takesRaw(&topLevelFragileS.storedProperty) + +extension C { + static func bar() { + takesRaw(&staticStoredProperty.storedProperty) + } +} + +// We can also project through force unwraps. +takesRaw(&topLevelOptOfS!) +takesRaw(&topLevelOptOfS!.storedProperty) +takesRaw(&topLevelOptOfResilientS!) + +// But we cannot do the same for: +// - Class bases + +takesMutableRaw(&topLevelC.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelFinalC.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelFragileFinalC.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelS.storedFinalC.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Resilient global or static variables + +takesMutableRaw(&globalResilient, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Resilient struct or class bases + +takesMutableRaw(&topLevelResilientS.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelResilientFinalC.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelOptOfResilientS!.storedProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Protocol bases + +takesRaw(&topLevelP.property) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&type(of: topLevelP).staticProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelP[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Properties with observers + +takesRaw(&topLevelWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelS.storedPropertyWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelOptOfS!.storedPropertyWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC.storedPropertyWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC.storedPropertyWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelTupleOfS.0.storedPropertyWithObservers) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Computed properties + +takesRaw(&topLevelOptOfS!.computedProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC.computedProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC.computedProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelTupleOfS.0.computedProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Subscripts + +takesRaw(&topLevelS[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Local variables + +func testInoutToPointerOfLocal() { + var local: Int8 = 0 + + takesMutableRaw(&local, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&local) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesMutable(&local) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutable'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutable'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} +} + +// - Instance members within types + +struct S1 { + var property: Int8 = 0 + mutating func foo() { + takesRaw(&property) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&self.property) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + } +} + +final class C1 { + var property: Int8 = 0 + func foo() { + takesRaw(&property) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&self.property) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + } +} + +// Check that @_nonEphemeral is preserved through type inference. +let f1 = takesMutableRaw +f1(&arr, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'f1'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'f1'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f2 = takesConst +f2(arr, 5) // expected-error {{cannot pass '[Int8]' to parameter; argument #1 must be a pointer that outlives the call to 'f2'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'f2'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f3 = takesRaw +f3(&arr) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'f3'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'f3'}} +// expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f4 = takesMutable +f4(&arr) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'f4'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'f4'}} +// expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +struct S2 { + static var selfProp = S2() + static var selfPropWithObserver = S2() { didSet {} } + + static func takesConstStaticAndReturns(@_nonEphemeral _ ptr: UnsafePointer) -> S2 { return S2() } + static func takesMutableRawStatic(_ x: String = "", @_nonEphemeral ptr: UnsafeMutableRawPointer) {} + func takesMutableRaw(@_nonEphemeral ptr: UnsafeMutableRawPointer = UnsafeMutableRawPointer(&topLevelS)) {} + + subscript(@_nonEphemeral takesConstInt8 ptr: UnsafePointer) -> Int { + get { return 0 } set {} + } +} + +func testNonEphemeralInMembers() { + var local = 0 + + let _: S2 = .takesConstStaticAndReturns([1, 2, 3]) // expected-error {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesConstStaticAndReturns'}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConstStaticAndReturns'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + S2.takesMutableRawStatic(ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + S2.takesMutableRawStatic("", ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + var s2 = S2() + s2.takesMutableRaw() + s2.takesMutableRaw(ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRaw(ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw(ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = s2[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + s2[takesConstInt8: ""] += 1 // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = \S2.[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} + +func testNonEphemeralInDotMember() { + func takesMutableS2(@_nonEphemeral _ ptr: UnsafeMutablePointer) {} + takesMutableS2(&.selfProp) + takesMutableS2(&.selfPropWithObserver) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableS2'}} + // expected-note@-1 {{implicit argument conversion from 'S2' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutableS2'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} +} + +func testNonEphemeralWithVarOverloads() { + takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}} + + // Even though only one of the overloads produces an ephemeral pointer, the + // diagnostic doesn't affect solver behaviour, so we diagnose an ambiguity. + takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}} + + takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}} + + func takesIntPtr(@_nonEphemeral _ ptr: UnsafePointer) {} + takesIntPtr(&overloadedVarDifferentTypes) + + func takesStringPtr(@_nonEphemeral _ ptr: UnsafePointer) {} + takesStringPtr(&overloadedVarDifferentTypes) +} + +infix operator ^^^ +func ^^^ (@_nonEphemeral lhs: UnsafeMutableRawPointer, rhs: Int) {} + +func testNonEphemeralInOperators() { + var local = 0 + + &value ^^^ 1 + + &local ^^^ 1 // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to '^^^'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to '^^^'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} +} + +struct S3 { + var ptr1: UnsafeMutableRawPointer + lazy var ptr2 = UnsafeMutableRawPointer(&topLevelS) +} + +enum E { + case mutableRaw(UnsafeMutableRawPointer) + case const(UnsafePointer) +} + +func testNonEphemeralInMemberwiseInits() { + var local = 0 + + _ = S3(ptr1: &topLevelS, ptr2: &local) // expected-error {{cannot use inout expression here; argument 'ptr2' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = S3.init(ptr1: &local, ptr2: &topLevelS) // expected-error {{cannot use inout expression here; argument 'ptr1' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.mutableRaw(&local) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'mutableRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'mutableRaw'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.const([1, 2, 3]) // expected-error {{cannot pass '[Int8]' to parameter; argument #1 must be a pointer that outlives the call to 'const'}} + // expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'const'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.const("hello") // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'const'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'const'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} + +// Allow the stripping of @_nonEphemeral. This is unfortunate, but ensures we don't force the user to write things +// like `func higherOrder(_ fn: (@_nonEphemeral UnsafeMutableRawPointer) -> Void) {}`, given that the attribute is non-user-facing. +let f5: (UnsafeMutableRawPointer, Int) -> Void = takesMutableRaw +let f6: (UnsafePointer, Int) -> Void = takesConst +let f7: (UnsafeRawPointer) -> Void = takesRaw +let f8: (UnsafeMutablePointer) -> Void = takesMutable + +func higherOrder(_ fn: (UnsafeMutableRawPointer, Int) -> Void) {} +higherOrder(takesMutableRaw) + + +// @_nonEphemeral ambiguities +func takesPointerOverload(x: Int = 0, @_nonEphemeral _ ptr: UnsafePointer) {} // expected-note {{candidate expects pointer that outlives the call for parameter #2}} +func takesPointerOverload(x: Int = 0, @_nonEphemeral _ ptr: UnsafeMutablePointer) {} // expected-note {{candidate expects pointer that outlives the call for parameter #2}} + +func testAmbiguity() { + var arr = [1, 2, 3] + takesPointerOverload(&arr) // expected-error {{no exact matches in call to global function 'takesPointerOverload(x:_:)'}} +} + +func takesPointerOverload2(@_nonEphemeral _ ptr: UnsafePointer) {} +func takesPointerOverload2(_ x: T?) {} +func takesPointerOverload2(_ x: Any) {} +func takesPointerOverload2(_ x: [Int]?) {} + +func testNonEphemeralErrorDoesntAffectOverloadResolution() { + // Make sure we always pick the pointer overload, even though the other + // overloads are all viable. + var arr = [1, 2, 3] + takesPointerOverload2(arr) // expected-error {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesPointerOverload2'}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesPointerOverload2'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} +} + +func takesTwoPointers(@_nonEphemeral ptr _: UnsafePointer, @_nonEphemeral ptr _: UnsafePointer) {} + +func testArgumentLabelReferencing() { + // Because takesTwoPointers has two argument labels with the same name, refer + // to the argument by position. + var arr = [1, 2, 3] + takesTwoPointers(ptr: arr, ptr: arr) + // expected-error@-1 {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesTwoPointers(ptr:ptr:)'}} + // expected-note@-2 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesTwoPointers(ptr:ptr:)'}} + // expected-note@-3 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + // expected-error@-4 {{cannot pass '[Int]' to parameter; argument #2 must be a pointer that outlives the call to 'takesTwoPointers(ptr:ptr:)'}} + // expected-note@-5 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesTwoPointers(ptr:ptr:)'}} + // expected-note@-6 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} +} diff --git a/test/Sema/diag_non_ephemeral_warning.swift b/test/Sema/diag_non_ephemeral_warning.swift new file mode 100644 index 0000000000000..a50b0baf12869 --- /dev/null +++ b/test/Sema/diag_non_ephemeral_warning.swift @@ -0,0 +1,469 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -parse-as-library -o %t -module-name=ModuleA %S/Inputs/diag_non_ephemeral_module1.swift +// RUN: %target-swift-frontend -emit-module -enable-library-evolution -parse-as-library -o %t -module-name=ModuleB %S/Inputs/diag_non_ephemeral_module2.swift +// RUN: cp %s %t/main.swift +// RUN: %target-swift-frontend -typecheck -verify -I %t %t/main.swift %S/Inputs/diag_non_ephemeral_globals.swift + +import ModuleA +import ModuleB + +func takesMutableRaw(@_nonEphemeral _ x: UnsafeMutableRawPointer, _ y: Int) {} +func takesConst(@_nonEphemeral _ x: UnsafePointer, _ y: Int) {} +func takesRaw(@_nonEphemeral _ x: UnsafeRawPointer) {} +func takesMutable(@_nonEphemeral _ x: UnsafeMutablePointer) {} +func takesOptMutableRaw(@_nonEphemeral _ x: UnsafeMutableRawPointer?) {} +func takesOptConst(@_nonEphemeral _ x: UnsafePointer?) {} + +var str = "" +var optionalStr: String? + +var arr: [Int8] = [5] +var optionalArr: [Int8]? + +// We cannot use array-to-pointer and string-to-pointer conversions with +// non-ephemeral parameters. + +takesMutableRaw(&arr, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesConst(str, 5) // expected-warning {{passing 'String' to parameter, but argument #1 should be a pointer that outlives the call to 'takesConst'}} +// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConst'}} +// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + +takesConst(arr, 5) // expected-warning {{passing '[Int8]' to parameter, but argument #1 should be a pointer that outlives the call to 'takesConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConst'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&arr) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutable(&arr) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutable'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutable'}} +// expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesOptMutableRaw(&arr) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesOptMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'takesOptMutableRaw'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// FIXME(SR-9100): This currently uses inout-to-pointer instead of array-to-pointer. +takesOptMutableRaw(&optionalArr) + +takesOptConst(arr) // expected-warning {{passing '[Int8]' to parameter, but argument #1 should be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesOptConst(optionalArr) // expected-warning {{passing '[Int8]?' to parameter, but argument #1 should be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} + +takesOptConst(str) // expected-warning {{passing 'String' to parameter, but argument #1 should be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} +// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + +takesOptConst(optionalStr) // expected-warning {{passing 'String?' to parameter, but argument #1 should be a pointer that outlives the call to 'takesOptConst'}} +// expected-note@-1 {{implicit argument conversion from 'String?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'takesOptConst'}} + +struct S { + static var staticStoredProperty: Int8 = 0 + var storedProperty: Int8 = 0 + var storedFinalC = FinalC() + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +class C { + static var staticStoredProperty = S() + var storedProperty: Int8 = 0 + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +final class FinalC { + static var staticStoredProperty = S() + var storedProperty: Int8 = 0 + var storedPropertyWithObservers: Int8 = 0 { didSet {} } + var computedProperty: Int8 { get { return 0 } set {} } + subscript() -> Int8 { get { return 0 } set {} } +} + +protocol P { + static var staticProperty: Int8 { get set } + var property: Int8 { get set } + subscript() -> Int8 { get set } +} +func makeP() -> P { while true {} } + +var value: Int8 = 5 +var topLevelS = S() +var topLevelC = C() +var topLevelFinalC = FinalC() +var topLevelP = makeP() +var topLevelTupleOfS = (S(), S()) +var topLevelOptOfS: S? +var topLevelWithObservers: Int8 = 0 { didSet {} } + +var topLevelResilientS = ResilientStruct() +var topLevelOptOfResilientS: ResilientStruct? +var topLevelFragileS = FragileStruct() + +var topLevelResilientFinalC = ResilientFinalClass() +var topLevelFragileFinalC = FragileFinalClass() + +let metatypeOfC = C.self + +// We can get stable pointer values from fragile global and static stored +// variables, as long as they don't have property observers. +takesMutableRaw(&value, 5) +takesMutableRaw(&str, 5) +takesMutableRaw(&topLevelS, 5) +takesRaw(&topLevelC) +takesRaw(&S.staticStoredProperty) +takesRaw(&C.staticStoredProperty) +takesRaw(&FinalC.staticStoredProperty) +takesRaw(&metatypeOfC.staticStoredProperty) +takesRaw(&type(of: topLevelC).staticStoredProperty) +takesRaw(&FragileStruct.staticStoredProperty) +takesRaw(&type(of: topLevelFragileS).staticStoredProperty) +takesRaw(&globalFragile) +takesRaw(&globalS) +takesRaw(&topLevelResilientS) +takesRaw(&topLevelFragileS) +takesRaw(&topLevelP) +takesRaw(&value) +takesRaw(&str) +takesMutable(&value) +takesOptMutableRaw(&value) +takesOptMutableRaw(&str) + +extension C { + static func foo() { + takesRaw(&staticStoredProperty) + } +} + +// We can also project stable pointer values from stored properties on such +// global and static stored variables, as long as the base type is a fragile +// struct or a tuple. +takesRaw(&C.staticStoredProperty.storedProperty) +takesRaw(&FinalC.staticStoredProperty.storedProperty) +takesRaw(&metatypeOfC.staticStoredProperty.storedProperty) +takesRaw(&type(of: topLevelC).staticStoredProperty.storedProperty) +takesRaw(&topLevelS.storedProperty) +takesRaw(&globalS.storedProperty) +takesRaw(&topLevelTupleOfS.0) +takesRaw(&topLevelTupleOfS.0.storedProperty) +takesRaw(&topLevelFragileS.storedProperty) + +extension C { + static func bar() { + takesRaw(&staticStoredProperty.storedProperty) + } +} + +// We can also project through force unwraps. +takesRaw(&topLevelOptOfS!) +takesRaw(&topLevelOptOfS!.storedProperty) +takesRaw(&topLevelOptOfResilientS!) + +// But we cannot do the same for: +// - Class bases + +takesMutableRaw(&topLevelC.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelFinalC.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelFragileFinalC.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelS.storedFinalC.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Resilient global or static variables + +takesMutableRaw(&globalResilient, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Resilient struct or class bases + +takesMutableRaw(&topLevelResilientS.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelResilientFinalC.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesMutableRaw(&topLevelOptOfResilientS!.storedProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} +// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Protocol bases + +takesRaw(&topLevelP.property) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&type(of: topLevelP).staticProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelP[]) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Properties with observers + +takesRaw(&topLevelWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelS.storedPropertyWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelOptOfS!.storedPropertyWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC.storedPropertyWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC.storedPropertyWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelTupleOfS.0.storedPropertyWithObservers) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Computed properties + +takesRaw(&topLevelOptOfS!.computedProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC.computedProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC.computedProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelTupleOfS.0.computedProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Subscripts + +takesRaw(&topLevelS[]) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelC[]) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +takesRaw(&topLevelFinalC[]) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} +// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} +// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +// - Local variables + +func testInoutToPointerOfLocal() { + var local: Int8 = 0 + + takesMutableRaw(&local, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&local) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesMutable(&local) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutable'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutable'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} +} + +// - Instance members within types + +struct S1 { + var property: Int8 = 0 + mutating func foo() { + takesRaw(&property) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&self.property) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + } +} + +final class C1 { + var property: Int8 = 0 + func foo() { + takesRaw(&property) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + takesRaw(&self.property) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + } +} + +// Check that @_nonEphemeral is preserved through type inference. +let f1 = takesMutableRaw +f1(&arr, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'f1'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'f1'}} +// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f2 = takesConst +f2(arr, 5) // expected-warning {{passing '[Int8]' to parameter, but argument #1 should be a pointer that outlives the call to 'f2'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'f2'}} +// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f3 = takesRaw +f3(&arr) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'f3'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'f3'}} +// expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +let f4 = takesMutable +f4(&arr) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'f4'}} +// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'f4'}} +// expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + +struct S2 { + static var selfProp = S2() + static var selfPropWithObserver = S2() { didSet {} } + + static func takesConstStaticAndReturns(@_nonEphemeral _ ptr: UnsafePointer) -> S2 { return S2() } + static func takesMutableRawStatic(_ x: String = "", @_nonEphemeral ptr: UnsafeMutableRawPointer) {} + func takesMutableRaw(@_nonEphemeral ptr: UnsafeMutableRawPointer = UnsafeMutableRawPointer(&topLevelS)) {} + + subscript(@_nonEphemeral takesConstInt8 ptr: UnsafePointer) -> Int { + get { return 0 } set {} + } +} + +func testNonEphemeralInMembers() { + var local = 0 + + let _: S2 = .takesConstStaticAndReturns([1, 2, 3]) // expected-warning {{passing '[Int]' to parameter, but argument #1 should be a pointer that outlives the call to 'takesConstStaticAndReturns'}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'takesConstStaticAndReturns'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + S2.takesMutableRawStatic(ptr: &local) // expected-warning {{inout expression creates a temporary pointer, but argument 'ptr' should be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + S2.takesMutableRawStatic("", ptr: &local) // expected-warning {{inout expression creates a temporary pointer, but argument 'ptr' should be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + var s2 = S2() + s2.takesMutableRaw() + s2.takesMutableRaw(ptr: &local) // expected-warning {{inout expression creates a temporary pointer, but argument 'ptr' should be a pointer that outlives the call to 'takesMutableRaw(ptr:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw(ptr:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = s2[takesConstInt8: ""] // expected-warning {{passing 'String' to parameter, but argument 'takesConstInt8' should be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + s2[takesConstInt8: ""] += 1 // expected-warning {{passing 'String' to parameter, but argument 'takesConstInt8' should be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = \S2.[takesConstInt8: ""] // expected-warning {{passing 'String' to parameter, but argument 'takesConstInt8' should be a pointer that outlives the call to 'subscript(takesConstInt8:)'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} + +func testNonEphemeralInDotMember() { + func takesMutableS2(@_nonEphemeral _ ptr: UnsafeMutablePointer) {} + takesMutableS2(&.selfProp) + takesMutableS2(&.selfPropWithObserver) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableS2'}} + // expected-note@-1 {{implicit argument conversion from 'S2' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'takesMutableS2'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} +} + +func testNonEphemeralWithVarOverloads() { + takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}} + takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}} + takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}} + + func takesIntPtr(@_nonEphemeral _ ptr: UnsafePointer) {} + takesIntPtr(&overloadedVarDifferentTypes) + + func takesStringPtr(@_nonEphemeral _ ptr: UnsafePointer) {} + takesStringPtr(&overloadedVarDifferentTypes) +} + +infix operator ^^^ +func ^^^ (@_nonEphemeral lhs: UnsafeMutableRawPointer, rhs: Int) {} + +func testNonEphemeralInOperators() { + var local = 0 + + &value ^^^ 1 + + &local ^^^ 1 // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to '^^^'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to '^^^'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} +} + +struct S3 { + var ptr1: UnsafeMutableRawPointer + lazy var ptr2 = UnsafeMutableRawPointer(&topLevelS) +} + +enum E { + case mutableRaw(UnsafeMutableRawPointer) + case const(UnsafePointer) +} + +func testNonEphemeralInMemberwiseInits() { + var local = 0 + + _ = S3(ptr1: &topLevelS, ptr2: &local) // expected-warning {{inout expression creates a temporary pointer, but argument 'ptr2' should be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = S3.init(ptr1: &local, ptr2: &topLevelS) // expected-warning {{inout expression creates a temporary pointer, but argument 'ptr1' should be a pointer that outlives the call to 'init(ptr1:ptr2:)'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.mutableRaw(&local) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'mutableRaw'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'mutableRaw'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.const([1, 2, 3]) // expected-warning {{passing '[Int8]' to parameter, but argument #1 should be a pointer that outlives the call to 'const'}} + // expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'const'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = E.const("hello") // expected-warning {{passing 'String' to parameter, but argument #1 should be a pointer that outlives the call to 'const'}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'const'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} diff --git a/test/Sema/diag_originally_definedin.swift b/test/Sema/diag_originally_definedin.swift new file mode 100644 index 0000000000000..90fd72161bb31 --- /dev/null +++ b/test/Sema/diag_originally_definedin.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=macosx + +@_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{need @available attribute for @_originallyDefinedIn}} +public func foo() {} + +@available(macOS 10.13, *) +@_originallyDefinedIn(module: "original", OSX 10.12) // expected-error {{moved version from @_originallyDefinedIn must after introduced OS version}} +public class C { + @_originallyDefinedIn(module: "original", OSX 10.13) // expected-error {{@_originallyDefinedIn is only applicable to top-level decl}} + public func foo() {} +} diff --git a/test/Sema/diag_typealias.swift b/test/Sema/diag_typealias.swift index ac741594ba097..03f87a9bd6833 100644 --- a/test/Sema/diag_typealias.swift +++ b/test/Sema/diag_typealias.swift @@ -2,4 +2,4 @@ struct S {} -typealias S = S // expected-error {{type alias 'S' references itself}} +typealias S = S // expected-error {{type alias 'S' references itself}} expected-note {{through reference here}} diff --git a/test/Sema/editor_placeholders.swift b/test/Sema/editor_placeholders.swift index 18444ffe27af4..19a796e9b93b4 100644 --- a/test/Sema/editor_placeholders.swift +++ b/test/Sema/editor_placeholders.swift @@ -26,5 +26,6 @@ f(<#T#> + 1) // expected-error{{editor placeholder in source file}} f(<#T##Int#>) // expected-error{{editor placeholder in source file}} f(<#T##String#>) // expected-error{{editor placeholder in source file}} expected-error{{cannot convert value of type 'String' to expected argument type 'Int'}} -for x in <#T#> { // expected-error{{editor placeholder in source file}} expected-error{{type '()' does not conform to protocol 'Sequence'}} +for x in <#T#> { // expected-error{{editor placeholder in source file}} expected-error{{for-in loop requires '()' to conform to 'Sequence'}} + } diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index 62fce941c232c..98e1ec9dae2e9 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -61,7 +61,7 @@ func customHashable() { enum InvalidCustomHashable { case A, B - var hashValue: String { return "" } // expected-note{{previously declared here}} + var hashValue: String { return "" } // expected-note {{previously declared here}} } func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { return "" @@ -145,7 +145,7 @@ func enumWithHashablePayload() { // Enums with non-hashable payloads don't derive conformance. struct NotHashable {} -enum EnumWithNonHashablePayload: Hashable { // expected-error 2 {{does not conform}} expected-note {{do you want to add protocol stubs?}} +enum EnumWithNonHashablePayload: Hashable { // expected-error 2 {{does not conform}} case A(NotHashable) //expected-note {{associated value type 'NotHashable' does not conform to protocol 'Hashable', preventing synthesized conformance of 'EnumWithNonHashablePayload' to 'Hashable'}} // expected-note@-1 {{associated value type 'NotHashable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'EnumWithNonHashablePayload' to 'Equatable'}} } @@ -223,16 +223,16 @@ enum Complex2 { case B } extension Complex2 : Hashable {} -extension Complex2 : CaseIterable {} // expected-error {{type 'Complex2' does not conform to protocol 'CaseIterable'}} expected-note {{do you want to add protocol stubs?}} -extension FromOtherFile: CaseIterable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}} expected-error {{does not conform to protocol 'CaseIterable'}} expected-note {{do you want to add protocol stubs?}} +extension Complex2 : CaseIterable {} // expected-error {{type 'Complex2' does not conform to protocol 'CaseIterable'}} +extension FromOtherFile: CaseIterable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}} expected-error {{does not conform to protocol 'CaseIterable'}} // No explicit conformance and it cannot be derived. enum NotExplicitlyHashableAndCannotDerive { case A(NotHashable) //expected-note {{associated value type 'NotHashable' does not conform to protocol 'Hashable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Hashable'}} // expected-note@-1 {{associated value type 'NotHashable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Equatable'}} } -extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}} expected-note {{do you want to add protocol stubs?}} -extension NotExplicitlyHashableAndCannotDerive : CaseIterable {} // expected-error {{does not conform}} expected-note {{do you want to add protocol stubs?}} +extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}} +extension NotExplicitlyHashableAndCannotDerive : CaseIterable {} // expected-error {{does not conform}} // Verify that conformance (albeit manually implemented) can still be added to // a type in a different file. @@ -244,7 +244,7 @@ extension OtherFileNonconforming: Hashable { } // ...but synthesis in a type defined in another file doesn't work yet. extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}} -extension YetOtherFileNonconforming: CaseIterable {} // expected-error {{does not conform}} expected-note {{do you want to add protocol stubs?}} +extension YetOtherFileNonconforming: CaseIterable {} // expected-error {{does not conform}} // Verify that an indirect enum doesn't emit any errors as long as its "leaves" // are conformant. @@ -282,7 +282,7 @@ case only([Int]) struct NotEquatable { } -enum ArrayOfNotEquatables : Equatable { // expected-error{{type 'ArrayOfNotEquatables' does not conform to protocol 'Equatable'}} expected-note {{do you want to add protocol stubs?}} +enum ArrayOfNotEquatables : Equatable { // expected-error{{type 'ArrayOfNotEquatables' does not conform to protocol 'Equatable'}} case only([NotEquatable]) //expected-note {{associated value type '[NotEquatable]' does not conform to protocol 'Equatable', preventing synthesized conformance of 'ArrayOfNotEquatables' to 'Equatable'}} } @@ -298,7 +298,7 @@ enum BadGenericDeriveExtension { case A(T) //expected-note {{associated value type 'T' does not conform to protocol 'Hashable', preventing synthesized conformance of 'BadGenericDeriveExtension' to 'Hashable'}} //expected-note@-1 {{associated value type 'T' does not conform to protocol 'Equatable', preventing synthesized conformance of 'BadGenericDeriveExtension' to 'Equatable'}} } -extension BadGenericDeriveExtension: Equatable {} // expected-note {{do you want to add protocol stubs?}} +extension BadGenericDeriveExtension: Equatable {} // // expected-error@-1 {{type 'BadGenericDeriveExtension' does not conform to protocol 'Equatable'}} extension BadGenericDeriveExtension: Hashable where T: Equatable {} // expected-error@-1 {{type 'BadGenericDeriveExtension' does not conform to protocol 'Hashable'}} diff --git a/test/Sema/enum_equatable_conditional.swift b/test/Sema/enum_equatable_conditional.swift index 4c6ec511e9a97..3d58dee1f4ad0 100644 --- a/test/Sema/enum_equatable_conditional.swift +++ b/test/Sema/enum_equatable_conditional.swift @@ -2,11 +2,11 @@ struct NotEquatable { } -enum WithArrayOfNotEquatables : Equatable { // expected-error{{type 'WithArrayOfNotEquatables' does not conform to protocol 'Equatable'}} expected-note {{do you want to add protocol stubs?}} +enum WithArrayOfNotEquatables : Equatable { // expected-error{{type 'WithArrayOfNotEquatables' does not conform to protocol 'Equatable'}} case only([NotEquatable]) // expected-note{{associated value type '[NotEquatable]' does not conform to protocol 'Equatable', preventing synthesized conformance of 'WithArrayOfNotEquatables' to 'Equatable'}} } -enum WithArrayOfNotEquatables2 : Equatable { // expected-error{{type 'WithArrayOfNotEquatables2' does not conform to protocol 'Equatable'}} expected-note {{do you want to add protocol stubs?}} +enum WithArrayOfNotEquatables2 : Equatable { // expected-error{{type 'WithArrayOfNotEquatables2' does not conform to protocol 'Equatable'}} case only([T]) // expected-note{{associated value type '[T]' does not conform to protocol 'Equatable', preventing synthesized conformance of 'WithArrayOfNotEquatables2' to 'Equatable'}} } diff --git a/test/Sema/enum_raw_representable.swift b/test/Sema/enum_raw_representable.swift index 0dfb82711b443..e6e4d312c1b0a 100644 --- a/test/Sema/enum_raw_representable.swift +++ b/test/Sema/enum_raw_representable.swift @@ -64,7 +64,7 @@ var colorRaw: Color.RawValue = 7.5 // Mismatched case types -enum BadPlain : UInt { // expected-error {{'BadPlain' declares raw type 'UInt', but does not conform to RawRepresentable and conformance could not be synthesized}} expected-note {{do you want to add protocol stubs?}} +enum BadPlain : UInt { // expected-error {{'BadPlain' declares raw type 'UInt', but does not conform to RawRepresentable and conformance could not be synthesized}} case a = "hello" // expected-error {{cannot convert value of type 'String' to raw type 'UInt'}} } @@ -77,7 +77,9 @@ class Outer { // scenario too. let a: Int = E.a // expected-error {{cannot convert value of type 'Outer.E' to specified type 'Int'}} - enum E : Array { // expected-error {{raw type 'Array' is not expressible by a string, integer, or floating-point literal}} + enum E : Array { + // expected-error@-1 {{raw type 'Array' is not expressible by a string, integer, or floating-point literal}} + // expected-error@-2 {{'Outer.E' declares raw type 'Array', but does not conform to RawRepresentable and conformance could not be synthesized}} case a } } @@ -188,5 +190,42 @@ enum ArrayOfNewEquatable : Array { } // expected-error@-1{{raw type 'Array' is not expressible by a string, integer, or floating-point literal}} // expected-error@-2{{'ArrayOfNewEquatable' declares raw type 'Array', but does not conform to RawRepresentable and conformance could not be synthesized}} // expected-error@-3{{RawRepresentable conformance cannot be synthesized because raw type 'Array' is not Equatable}} -// expected-note@-4{{do you want to add protocol stubs?}} -// expected-error@-5 {{an enum with no cases cannot declare a raw type}} +// expected-error@-4{{an enum with no cases cannot declare a raw type}} + +// rdar://58127114 +struct NotEquatableInteger : ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + + init(integerLiteral: Int) {} +} + +enum NotEquatableRawType1 : NotEquatableInteger { +// expected-error@-1 {{'NotEquatableRawType1' declares raw type 'NotEquatableInteger', but does not conform to RawRepresentable and conformance could not be synthesized}} +// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableInteger' is not Equatable}} + case a = 123 +} + + +enum NotEquatableRawType2 : NotEquatableInteger { +// expected-error@-1 {{'NotEquatableRawType2' declares raw type 'NotEquatableInteger', but does not conform to RawRepresentable and conformance could not be synthesized}} +// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableInteger' is not Equatable}} + typealias RawValue = NotEquatableInteger + + case a = 123 +} + +struct NotEquatableString : ExpressibleByStringLiteral { + init(stringLiteral: String) {} +} + +// FIXME: This could be diagnosed a bit better. The notes are disembodied +enum NotEquatableRawType3: NotEquatableString { +// expected-error@-1 {{RawRepresentable conformance cannot be synthesized because raw type 'NotEquatableString' is not Equatable}} +// expected-error@-2 {{'NotEquatableRawType3' declares raw type 'NotEquatableString', but does not conform to RawRepresentable and conformance could not be synthesized}} + case a + typealias RawValue = NotEquatableString + init?(rawValue: Int) { self = .a } + // expected-note@-1 {{candidate has non-matching type '(rawValue: Int)'}} + var rawValue: Int { 0 } + // expected-note@-1 {{candidate has non-matching type 'Int'}} +} \ No newline at end of file diff --git a/test/Sema/exhaustive_switch.swift b/test/Sema/exhaustive_switch.swift index 6495685934c94..069a2886ceafb 100644 --- a/test/Sema/exhaustive_switch.swift +++ b/test/Sema/exhaustive_switch.swift @@ -1,5 +1,9 @@ -// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution %S/Inputs/exhaustive_switch_testable_helper.swift -emit-module -o %t +// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -I %t +// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics -I %t + +import exhaustive_switch_testable_helper func foo(a: Int?, b: Int?) -> Int { switch (a, b) { @@ -1214,6 +1218,54 @@ func sr11160_extra() { } } +public enum SR11672Tests { + + @frozen public enum FrozenSameModule { + case a, b + } + + func testNotRequired(_ value: NonExhaustive, _ value2: FrozenEnum, _ value3: FrozenSameModule) { + switch value { + // expected-error@-1 {{switch must be exhaustive}} + // expected-note@-2 {{add missing case: '.a'}} + // expected-note@-3 {{add missing case: '.b'}} + // Do not suggest adding '@unknown default' + } + + switch value2 { + // expected-error@-1 {{switch must be exhaustive}} + // expected-note@-2 {{add missing case: '.a'}} + // expected-note@-3 {{add missing case: '.b'}} + // expected-note@-4 {{add missing case: '.c'}} + } + + switch value3 { + // expected-error@-1 {{switch must be exhaustive}} + // expected-note@-2 {{add missing case: '.a'}} + // expected-note@-3 {{add missing case: '.b'}} + } + } + + @inlinable public func testNotRequired2(_ value: FrozenSameModule) { + switch value { + // expected-error@-1 {{switch must be exhaustive}} + // expected-note@-2 {{add missing case: '.a'}} + // expected-note@-3 {{add missing case: '.b'}} + } + } + + // Inlinable code is considered "outside" the module and must include a default + // case. + @inlinable public func testRequired(_ value: NonExhaustive) { + switch value { + // expected-error@-1 {{switch must be exhaustive}} + // expected-note@-2 {{add missing case: '.a'}} + // expected-note@-3 {{add missing case: '.b'}} + // expected-note@-4 {{handle unknown values using "@unknown default"}} + } + } +} + // SR-11212 tests: Some of the tests here rely on compiler bugs related to // implicit (un)tupling in patterns. // @@ -1355,6 +1407,14 @@ enum SR11212Tests { } } + // rdar://problem/58578342 + func sr11212_content_generic_pattern_untupled3(b: Box<((Int, Int), Int)>) -> (Int, Int, Int) { + switch b { + case let .box((x, y), z): return (x, y, z) + // expected-warning@-1 {{the enum case has a single tuple as an associated value, but there are several patterns here, implicitly tupling the patterns and trying to match that instead}} + } + } + func sr11212_content_generic_pattern_ambiguous1(b: Box<(Int, Int)>) -> (Int, Int) { switch b { case .box(let b_): return b_ diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index 4ce9f723a07bc..beae8a87fa53e 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -159,7 +159,7 @@ struct SomeStruct { return 42 } nonmutating - set { // expected-note {{mark accessor 'mutating' to make 'self' mutable}} {{5-5=mutating }} + set { // expected-note {{mark accessor 'mutating' to make 'self' mutable}} {{5-16=mutating}} iv = newValue // expected-error {{cannot assign to property: 'self' is immutable}} } } @@ -370,7 +370,7 @@ func testSelectorStyleArguments1(_ x: Int, bar y: Int) { func testSelectorStyleArguments2(let x: Int, // expected-warning {{'let' in this position is interpreted as an argument label}}{{34-37=`let`}} let bar y: Int) { // expected-warning {{'let' in this position is interpreted as an argument label}}{{34-37=`let`}} // expected-error @-1 {{expected ',' separator}} -// expected-error @-2 {{parameter requires an explicit type}} +// expected-error @-2 {{expected ':' following argument label and parameter name}} } func testSelectorStyleArguments3(_ x: Int, bar y: Int) { ++x // expected-error {{cannot pass immutable value to mutating operator: 'x' is a 'let' constant}} @@ -457,6 +457,7 @@ func assignmentsToFuncs() { var x = 0 (x, func1() = 0) = (4, 5) // expected-error {{expression is not assignable: 'func1' returns immutable value}} + // expected-error@-1 {{cannot assign value of type '(Int, Int)' to type '(Int, ())'}} } // Structure initializers in an extension cannot assign to constant properties @@ -656,8 +657,8 @@ func sr4214() { let closure = { val in val.x = 7 } as (inout MutableSubscripts) -> () // Ok var v = MutableSubscripts() closure(&v) - // FIXME: This diagnostic isn't really all that much better - // expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(_) -> _'}} + // expected-error@+2 {{declared closure result '()' is incompatible with contextual type 'MutableSubscripts'}} + // expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(MutableSubscripts) -> MutableSubscripts'}} sequence(v) { (state : inout MutableSubscripts) -> () in _ = MutableSubscripts.initialize(from: &state) return () @@ -681,3 +682,63 @@ struct SS { j = j // expected-error {{cannot assign to value: 'j' is a 'let' constant}} } } + +protocol JustAProtocol { + var name: String { get set } +} + +extension JustAProtocol { + var foo: String { + get { return name } + nonmutating set { name = newValue } // expected-error {{cannot assign to property: 'self' is immutable}} + // expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}{{5-16=mutating}} + } + + nonmutating func bar() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-14=mutating}} + name = "Hello" // expected-error {{cannot assign to property: 'self' is immutable}} + } + + func baz() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-3=mutating }} + name = "World" // expected-error {{cannot assign to property: 'self' is immutable}} + } +} + +struct S { + var x = 0 + static var y = 0 + + struct Nested { + func foo() { + // SR-11786: Make sure we don't offer the 'self.' fix-it here. + let x = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + } + } + + func bar() { + // SR-11787: Make sure we insert "self." in the right location. + let x = 0 // expected-note 3{{change 'let' to 'var' to make it mutable}} + x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}} + + (try x) += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{10-10=self.}} + + x = 1 // expected-error {{cannot assign to value: 'x' is a 'let' constant}} + // expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}} + + // SR-11788: Insert "Type." for a static property. + let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}} + // expected-note@-1 {{add explicit 'S.' to refer to mutable static property of 'S'}} {{5-5=S.}} + } +} + +struct S2 { + static var y: Int { get { 0 } set {} } + func foo() { + let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}} + y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}} + // expected-note@-1 {{add explicit 'S2.' to refer to mutable static property of 'S2'}} {{5-5=S2.}} + } +} diff --git a/test/Sema/object_literals_ios.swift b/test/Sema/object_literals_ios.swift index 7585a96d0122b..facc3a9631d43 100644 --- a/test/Sema/object_literals_ios.swift +++ b/test/Sema/object_literals_ios.swift @@ -7,7 +7,8 @@ struct S: _ExpressibleByColorLiteral { let y: S = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) let y2 = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) // expected-error{{could not infer type of color literal}} expected-note{{import UIKit to use 'UIColor' as the default color literal type}} -let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{cannot convert value of type '(red: Int, bleen: Int, grue: Int, alpha: Int)' to expected argument type '(red: Float, green: Float, blue: Float, alpha: Float)'}} +let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}} +// expected-error@-1 {{could not infer type of color literal}} expected-note@-1 {{import UIKit to use 'UIColor' as the default color literal type}} struct I: _ExpressibleByImageLiteral { init(imageLiteralResourceName: String) {} diff --git a/test/Sema/object_literals_osx.swift b/test/Sema/object_literals_osx.swift index d5eb0c6aee18c..b49c28989f9bd 100644 --- a/test/Sema/object_literals_osx.swift +++ b/test/Sema/object_literals_osx.swift @@ -7,7 +7,7 @@ struct S: _ExpressibleByColorLiteral { let y: S = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) let y2 = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) // expected-error{{could not infer type of color literal}} expected-note{{import AppKit to use 'NSColor' as the default color literal type}} -let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{cannot convert value of type '(red: Int, bleen: Int, grue: Int, alpha: Int)' to expected argument type '(red: Float, green: Float, blue: Float, alpha: Float)'}} +let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}} expected-error{{could not infer type of color literal}} expected-note{{import AppKit to use 'NSColor' as the default color literal type}} struct I: _ExpressibleByImageLiteral { init(imageLiteralResourceName: String) {} @@ -23,4 +23,7 @@ struct Path: _ExpressibleByFileReferenceLiteral { let p1: Path = #fileLiteral(resourceName: "what.txt") let p2 = #fileLiteral(resourceName: "what.txt") // expected-error{{could not infer type of file reference literal}} expected-note{{import Foundation to use 'URL' as the default file reference literal type}} -let text = #fileLiteral(resourceName: "TextFile.txt").relativeString! // expected-error{{type of expression is ambiguous without more context}} +let text = #fileLiteral(resourceName: "TextFile.txt").relativeString! // expected-error{{could not infer type of file reference literal}} expected-note{{import Foundation to use 'URL' as the default file reference literal type}} + +// rdar://problem/49861813 +#fileLiteral() // expected-error{{missing argument for parameter 'resourceName' in call}} expected-error{{could not infer type of file reference literal}} expected-note{{import Foundation to use 'URL' as the default file reference literal type}} diff --git a/test/Sema/option-set-empty.swift b/test/Sema/option-set-empty.swift new file mode 100644 index 0000000000000..3416e15f1e269 --- /dev/null +++ b/test/Sema/option-set-empty.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift + +struct SomeOptions: OptionSet { + var rawValue: Int + + static let some = MyOptions(rawValue: 4) + static let empty = SomeOptions(rawValue: 0) // expected-warning {{static property 'empty' produces an empty option set}} expected-note {{use [] to silence this warning}}{{35-48=([])}} + static var otherVal = SomeOptions(rawValue: 0) + + let someVal = MyOptions(rawValue: 6) + let option = MyOptions(float: Float.infinity) + let none = SomeOptions(rawValue: 0) // expected-error {{value type 'SomeOptions' cannot have a stored property that recursively contains it}} +} + +struct MyOptions: OptionSet { + let rawValue: Int + + init(rawValue: Int) { + self.rawValue = rawValue + } + + init(float: Float) { + self.rawValue = float.exponent + } + + static let none = MyOptions(rawValue: 0) // expected-warning {{static property 'none' produces an empty option set}} expected-note {{use [] to silence this warning}}{{32-45=([])}} + static var nothing = MyOptions(rawValue: 0) + static let nope = MyOptions() + static let other = SomeOptions(rawValue: 8) + static let piVal = MyOptions(float: Float.pi) + static let zero = MyOptions(float: 0.0) +} diff --git a/test/Sema/struct_equatable_hashable.swift b/test/Sema/struct_equatable_hashable.swift index dd3a3e28168cd..f61b3865c1382 100644 --- a/test/Sema/struct_equatable_hashable.swift +++ b/test/Sema/struct_equatable_hashable.swift @@ -115,12 +115,11 @@ struct StructWithoutExplicitConformance { func structWithoutExplicitConformance() { if StructWithoutExplicitConformance(a: 1, b: "b") == StructWithoutExplicitConformance(a: 2, b: "a") { } // expected-error{{binary operator '==' cannot be applied to two 'StructWithoutExplicitConformance' operands}} - // expected-note @-1 {{overloads for '==' exist with these partially matching parameter lists: }} } // Structs with non-hashable/equatable stored properties don't derive conformance. struct NotHashable {} -struct StructWithNonHashablePayload: Hashable { // expected-error 2 {{does not conform}} expected-note {{do you want to add protocol stubs?}} +struct StructWithNonHashablePayload: Hashable { // expected-error 2 {{does not conform}} let a: NotHashable // expected-note {{stored property type 'NotHashable' does not conform to protocol 'Hashable', preventing synthesized conformance of 'StructWithNonHashablePayload' to 'Hashable'}} // expected-note@-1 {{stored property type 'NotHashable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'StructWithNonHashablePayload' to 'Equatable'}} } @@ -182,7 +181,7 @@ struct NotExplicitlyHashableAndCannotDerive { let v: NotHashable // expected-note {{stored property type 'NotHashable' does not conform to protocol 'Hashable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Hashable'}} // expected-note@-1 {{stored property type 'NotHashable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Equatable'}} } -extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}} expected-note {{do you want to add protocol stubs?}} +extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}} // A struct with no stored properties trivially derives conformance. struct NoStoredProperties: Hashable {} @@ -240,7 +239,6 @@ struct BadGenericDeriveExtension { } extension BadGenericDeriveExtension: Equatable {} // expected-error@-1 {{type 'BadGenericDeriveExtension' does not conform to protocol 'Equatable'}} -// expected-note@-2 {{do you want to add protocol stubs?}} extension BadGenericDeriveExtension: Hashable where T: Equatable {} // expected-error@-1 {{type 'BadGenericDeriveExtension' does not conform to protocol 'Hashable'}} diff --git a/test/Sema/substring_to_string_conversion_swift4.swift b/test/Sema/substring_to_string_conversion_swift4.swift index 10ab545feeacb..291f48d2c4161 100644 --- a/test/Sema/substring_to_string_conversion_swift4.swift +++ b/test/Sema/substring_to_string_conversion_swift4.swift @@ -45,7 +45,7 @@ do { // CTP_ClosureResult do { - [ss].map { (x: Substring) -> String in x } // expected-error {{cannot convert value of type 'Substring' to closure result type 'String'}} {{42-42=String(}} {{43-43=)}} + [ss].map { (x: Substring) -> String in x } // expected-error {{declared closure result 'Substring' is incompatible with contextual type 'String'}} } // CTP_ArrayElement diff --git a/test/Sema/type_join.swift b/test/Sema/type_join.swift index 96d9724029f9e..ad6d2b78cdc27 100644 --- a/test/Sema/type_join.swift +++ b/test/Sema/type_join.swift @@ -31,7 +31,7 @@ protocol FakeExpressibleByFloatLiteral {} protocol FakeBinaryFloatingPoint : FakeFloatingPoint, FakeExpressibleByFloatLiteral {} func expectEqualType(_: T.Type, _: T.Type) {} -func commonSupertype(_: T, _: T) -> T {} +func commonSupertype(_: T, _: T) -> T {} // expected-note 2 {{generic parameters are always considered '@escaping'}} expectEqualType(Builtin.type_join(Int.self, Int.self), Int.self) expectEqualType(Builtin.type_join_meta(D.self, C.self), C.self) @@ -90,9 +90,9 @@ func joinFunctions( ) { _ = commonSupertype(escaping, escaping) _ = commonSupertype(nonescaping, escaping) - // expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}} + // expected-error@-1 {{converting non-escaping parameter 'nonescaping' to generic parameter 'T' may allow it to escape}} _ = commonSupertype(escaping, nonescaping) - // expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}} + // expected-error@-1 {{converting non-escaping parameter 'nonescaping' to generic parameter 'T' may allow it to escape}} let x: Int = 1 // FIXME: We emit these diagnostics here because we refuse to allow // Any to be inferred for the generic type. That's pretty diff --git a/test/Sema/typo_correction.swift b/test/Sema/typo_correction.swift index 3e22366fc342b..20d9261fd729c 100644 --- a/test/Sema/typo_correction.swift +++ b/test/Sema/typo_correction.swift @@ -27,9 +27,9 @@ func *(x: Whatever, y: Whatever) {} // This works even for single-character identifiers. func test_very_short() { // Note that we don't suggest operators. - let x = 0 // expected-note {{'x' declared here}} + let x = 0 // expected-note {{did you mean 'x'?}} let longer = y - // expected-error@-1 {{use of unresolved identifier 'y'; did you mean 'x'?}} + // expected-error@-1 {{use of unresolved identifier 'y'}} } // It does not trigger in a variable's own initializer. @@ -100,7 +100,7 @@ func takesSomeClassArchetype(_ t: T) { } // Typo correction of unqualified lookup from generic context. -struct Generic { +struct Generic { // expected-note {{'T' declared as parameter to type 'Generic'}} func match1() {} // expected-note@-1 {{'match1' declared here}} @@ -113,15 +113,18 @@ struct Generic { } protocol P { // expected-note {{'P' previously declared here}} - // expected-note@-1 {{did you mean 'P'?}} + // expected-note@-1 2{{did you mean 'P'?}} + // expected-note@-2 {{'P' declared here}} typealias a = Generic } protocol P {} // expected-error {{invalid redeclaration of 'P'}} -// expected-note@-1 {{did you mean 'P'?}} +// expected-note@-1 2{{did you mean 'P'?}} +// expected-note@-2 {{'P' declared here}} func hasTypo() { - _ = P.a.a // expected-error {{type 'Generic' has no member 'a'}} + _ = P.a.a // expected-error {{type 'Generic' has no member 'a'}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} } // Typo correction with AnyObject. @@ -140,7 +143,7 @@ enum Foo { case flashing // expected-note {{'flashing' declared here}} } -func foo(_ a: Foo) { +func foo(_ a: Foo) { // expected-note {{'foo' declared here}} } func bar() { diff --git a/test/Serialization/Inputs/def_basic.sil b/test/Serialization/Inputs/def_basic.sil index 8af7f57b22d10..b33d080235edc 100644 --- a/test/Serialization/Inputs/def_basic.sil +++ b/test/Serialization/Inputs/def_basic.sil @@ -204,8 +204,8 @@ bb0: // CHECK: upcast {{.*}} : $D to $C %a = upcast %D : $D to $C - // CHECK: unconditional_checked_cast {{.*}} : $C to $D - %5 = unconditional_checked_cast %C : $C to $D + // CHECK: unconditional_checked_cast {{.*}} : $C to D + %5 = unconditional_checked_cast %C : $C to D %6 = tuple () return %6 : $() } @@ -445,7 +445,7 @@ bb0(%0 : $B): store %0 to %1a : $*B %3 = load %1a : $*B strong_retain %3 : $B - checked_cast_br %3 : $B to $E, yes, no // CHECK: checked_cast_br + checked_cast_br %3 : $B to E, yes, no // CHECK: checked_cast_br yes(%5 : $E): %y = integer_literal $Builtin.Int1, 1 br isa(%y : $Builtin.Int1) diff --git a/test/Serialization/Inputs/nested-type-with-overlay/overlay.swift b/test/Serialization/Inputs/nested-type-with-overlay/overlay.swift index 72f831e10f774..754423baa4235 100644 --- a/test/Serialization/Inputs/nested-type-with-overlay/overlay.swift +++ b/test/Serialization/Inputs/nested-type-with-overlay/overlay.swift @@ -8,3 +8,13 @@ extension Base { } public var shadowedFromSwift = Base.NestedAndShadowed(dummy: ()) + +public struct CustomError { + public struct Code : RawRepresentable { + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } +} diff --git a/test/Serialization/Inputs/opaque_types_inlineable_2.swift b/test/Serialization/Inputs/opaque_types_inlineable_2.swift new file mode 100644 index 0000000000000..1a20580f22f57 --- /dev/null +++ b/test/Serialization/Inputs/opaque_types_inlineable_2.swift @@ -0,0 +1,18 @@ +public protocol P {} + +public struct M : P { + public init(t: T) {} +} +extension Int : P {} + +extension P { + @inlinable + public func o(_ t: T) -> some P { + return M(t: t) + } + + @inlinable + public func p() throws -> some P { + return Int() + } +} diff --git a/test/Serialization/Recovery/Inputs/custom-modules/Overrides.h b/test/Serialization/Recovery/Inputs/custom-modules/Overrides.h index 6eaf68cd1e87d..d5c3100501d39 100644 --- a/test/Serialization/Recovery/Inputs/custom-modules/Overrides.h +++ b/test/Serialization/Recovery/Inputs/custom-modules/Overrides.h @@ -8,11 +8,13 @@ - (nullable id)nullabilityChangeMethod; - (nonnull id)typeChangeMethod; @property (readonly) long disappearingProperty; +@property (readwrite) long disappearingPropertySetter; #else //- (void)disappearingMethod; - (nonnull id)nullabilityChangeMethod; - (nonnull Base *)typeChangeMethod; // @property (readonly) long disappearingProperty; +@property (readonly) long disappearingPropertySetter; #endif @end diff --git a/test/Serialization/Recovery/implementation-only-missing.swift b/test/Serialization/Recovery/implementation-only-missing.swift index 98f9d81741786..381d6510e0c36 100644 --- a/test/Serialization/Recovery/implementation-only-missing.swift +++ b/test/Serialization/Recovery/implementation-only-missing.swift @@ -13,9 +13,23 @@ //// private module is superfluous but makes sure that it's not somehow loaded. // RUN: rm %t/private_lib.swiftmodule // RUN: %target-swift-frontend -typecheck -DCLIENT_APP -primary-file %s -I %t -index-system-modules -index-store-path %t +// RUN: %target-swift-frontend -emit-sil -DCLIENT_APP -primary-file %s -I %t -module-name client #if PRIVATE_LIB +@propertyWrapper +public struct IoiPropertyWrapper { + var content: V + + public init(_ v: V) { + content = v + } + + public var wrappedValue: V { + return content + } +} + public struct HiddenGenStruct { public init() {} } @@ -49,11 +63,18 @@ extension LibProtocol where TA == LibProtocolTA { public struct PublicStruct: LibProtocol { typealias TA = LibProtocolTA + public init() { } + + @IoiPropertyWrapper("some text") + public var wrappedVar: String } #elseif CLIENT_APP import public_lib +var s = PublicStruct() +print(s.wrappedVar) + #endif diff --git a/test/Serialization/Recovery/missing-clang-module.swift b/test/Serialization/Recovery/missing-clang-module.swift new file mode 100644 index 0000000000000..248bb8922c471 --- /dev/null +++ b/test/Serialization/Recovery/missing-clang-module.swift @@ -0,0 +1,32 @@ +//// Report dependency cycles involving missing clang modules without crashing +//// rdar://problem/57364033 + +// RUN: %empty-directory(%t) +// RUN: cp -r %S/Inputs/custom-modules %t/ + +// RUN: %target-swift-frontend -emit-module -DLIB_A %s -module-name A -emit-module-path %t/A.swiftmodule +// RUN: %target-swift-frontend -emit-module -DLIB_B %s -module-name B -emit-module-path %t/B.swiftmodule -I %t -I %t/custom-modules +// RUN: %target-swift-frontend -emit-module -DLIB_C %s -module-name C -emit-module-path %t/C.swiftmodule -I %t + +//// Delete the clang module +// RUN: rm -r %t/custom-modules/ + +// RUN: not %target-swift-frontend -emit-module -DLIB_D %s -module-name A -emit-module-path %t/D.swiftmodule -I %t 2>&1 | %FileCheck %s + +#if LIB_A + +#elseif LIB_B + +import IndirectImport // From custom-modules +import A + +#elseif LIB_C + +import B + +#elseif LIB_D + +import C +// CHECK: :0: error: circular dependency between modules 'A' and 'B' + +#endif diff --git a/test/Serialization/Recovery/overrides.swift b/test/Serialization/Recovery/overrides.swift index f255a6a816455..ebcb5e5e59fe4 100644 --- a/test/Serialization/Recovery/overrides.swift +++ b/test/Serialization/Recovery/overrides.swift @@ -72,12 +72,28 @@ public class A_Sub: Base { public override func typeChangeMethod() -> Any { return self } public override func disappearingMethodWithOverload() {} public override var disappearingProperty: Int { return 0 } + public override var disappearingPropertySetter: Int { + get { return 0 } + set {} + } } public class A_Sub2: A_Sub { public override func disappearingMethod() {} } +public final class A_Sub3Final: Base { + public override func disappearingMethod() {} + public override func nullabilityChangeMethod() -> Any? { return nil } + public override func typeChangeMethod() -> Any { return self } + public override func disappearingMethodWithOverload() {} + public override var disappearingProperty: Int { return 0 } + public override var disappearingPropertySetter: Int { + get { return 0 } + set {} + } +} + // CHECK-LABEL: class A_Sub : Base { // CHECK-NEXT: func disappearingMethod() @@ -85,6 +101,7 @@ public class A_Sub2: A_Sub { // CHECK-NEXT: func typeChangeMethod() -> Any // CHECK-NEXT: func disappearingMethodWithOverload() // CHECK-NEXT: var disappearingProperty: Int { get } +// CHECK-NEXT: var disappearingPropertySetter: Int{{$}} // CHECK-NEXT: init() // CHECK-NEXT: {{^}$}} @@ -93,12 +110,23 @@ public class A_Sub2: A_Sub { // CHECK-NEXT: init() // CHECK-NEXT: {{^}$}} +// CHECK-LABEL: final class A_Sub3Final : Base { +// CHECK-NEXT: func disappearingMethod() +// CHECK-NEXT: func nullabilityChangeMethod() -> Any? +// CHECK-NEXT: func typeChangeMethod() -> Any +// CHECK-NEXT: func disappearingMethodWithOverload() +// CHECK-NEXT: var disappearingProperty: Int { get } +// CHECK-NEXT: var disappearingPropertySetter: Int{{$}} +// CHECK-NEXT: init() +// CHECK-NEXT: {{^}$}} + // CHECK-RECOVERY-LABEL: class A_Sub : Base { // CHECK-RECOVERY-NEXT: func disappearingMethod() // CHECK-RECOVERY-NEXT: func nullabilityChangeMethod() -> Any? // CHECK-RECOVERY-NEXT: func typeChangeMethod() -> Any // CHECK-RECOVERY-NEXT: func disappearingMethodWithOverload() // CHECK-RECOVERY-NEXT: /* placeholder for disappearingProperty */ +// CHECK-RECOVERY-NEXT: var disappearingPropertySetter: Int{{$}} // CHECK-RECOVERY-NEXT: init() // CHECK-RECOVERY-NEXT: {{^}$}} @@ -107,6 +135,16 @@ public class A_Sub2: A_Sub { // CHECK-RECOVERY-NEXT: init() // CHECK-RECOVERY-NEXT: {{^}$}} +// CHECK-RECOVERY-LABEL: class A_Sub3Final : Base { +// CHECK-RECOVERY-NEXT: func disappearingMethod() +// CHECK-RECOVERY-NEXT: func nullabilityChangeMethod() -> Any? +// CHECK-RECOVERY-NEXT: func typeChangeMethod() -> Any +// CHECK-RECOVERY-NEXT: func disappearingMethodWithOverload() +// CHECK-RECOVERY-NEXT: /* placeholder for disappearingProperty */ +// CHECK-RECOVERY-NEXT: var disappearingPropertySetter: Int{{$}} +// CHECK-RECOVERY-NEXT: init() +// CHECK-RECOVERY-NEXT: {{^}$}} + extension Base { @nonobjc func disappearingMethodWithOverload() -> SwiftOnlyClass? { return nil } } diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..60d0eda010d56 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s @@ -23,9 +26,9 @@ import Lib // CHECK-SIL-LABEL: sil hidden [ossa] @$s8typedefs11testSymbolsyyF func testSymbols() { // Check that the symbols are not using 'Bool'. - // CHECK-SIL: function_ref @$s3Lib1xs5Int32Vvau + // CHECK-SIL: global_addr @$s3Lib9usesAssocs5Int32VSgvp _ = Lib.x - // CHECK-SIL: function_ref @$s3Lib9usesAssocs5Int32VSgvau + // CHECK-SIL: global_addr @$s3Lib1xs5Int32Vvp _ = Lib.usesAssoc } // CHECK-SIL: end sil function '$s8typedefs11testSymbolsyyF' diff --git a/test/Serialization/Recovery/types-4-to-5.swift b/test/Serialization/Recovery/types-4-to-5.swift index fcdd8bdf54bf1..1be0ba87b6cea 100644 --- a/test/Serialization/Recovery/types-4-to-5.swift +++ b/test/Serialization/Recovery/types-4-to-5.swift @@ -17,7 +17,7 @@ func requiresConformance(_: B_RequiresConformance) {} func requiresConformance(_: B_RequiresConformance) {} class Sub: Base {} // okay -class Impl: Proto {} // expected-error {{type 'Impl' does not conform to protocol 'Proto'}} expected-note {{do you want to add protocol stubs?}} +class Impl: Proto {} // expected-error {{type 'Impl' does not conform to protocol 'Proto'}} #else // TEST diff --git a/test/Serialization/Recovery/types-5-to-4.swift b/test/Serialization/Recovery/types-5-to-4.swift index c5061fd58384d..8b3775cc70b8b 100644 --- a/test/Serialization/Recovery/types-5-to-4.swift +++ b/test/Serialization/Recovery/types-5-to-4.swift @@ -16,8 +16,8 @@ import Lib func requiresConformance(_: B_RequiresConformance) {} func requiresConformance(_: B_RequiresConformance) {} -class Sub: Base {} // expected-error {{cannot inherit from class 'Base' (compiled with Swift 5.1.1) because it has overridable members that could not be loaded in Swift 4.1.50}} -class Impl: Proto {} // expected-error {{type 'Impl' cannot conform to protocol 'Proto' (compiled with Swift 5.1.1) because it has requirements that could not be loaded in Swift 4.1.50}} +class Sub: Base {} // expected-error {{cannot inherit from class 'Base' (compiled with Swift 5.2) because it has overridable members that could not be loaded in Swift 4.1.50}} +class Impl: Proto {} // expected-error {{type 'Impl' cannot conform to protocol 'Proto' (compiled with Swift 5.2) because it has requirements that could not be loaded in Swift 4.1.50}} #else // TEST diff --git a/test/Serialization/always_inline.swift b/test/Serialization/always_inline.swift index 178b529aeca55..8c6dfc584d790 100644 --- a/test/Serialization/always_inline.swift +++ b/test/Serialization/always_inline.swift @@ -8,9 +8,9 @@ import def_always_inline -// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] @$s17def_always_inline16testAlwaysInline1xS2b_tF : $@convention(thin) (Bool) -> Bool { +// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] [ossa] @$s17def_always_inline16testAlwaysInline1xS2b_tF : $@convention(thin) (Bool) -> Bool { -// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] @$s17def_always_inline22AlwaysInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin AlwaysInlineInitStruct.Type) -> AlwaysInlineInitStruct { +// SIL-LABEL: sil public_external [serialized] [always_inline] [canonical] [ossa] @$s17def_always_inline22AlwaysInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin AlwaysInlineInitStruct.Type) -> AlwaysInlineInitStruct { // SIL-LABEL: sil @main // SIL: [[RAW:%.+]] = global_addr @$s13always_inline3rawSbvp : $*Bool diff --git a/test/Serialization/attr-invalid.swift b/test/Serialization/attr-invalid.swift index 623ee022bf45d..5c32dd9974f49 100644 --- a/test/Serialization/attr-invalid.swift +++ b/test/Serialization/attr-invalid.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/attr.swiftmodule %s -verify +// RUN: %target-swift-frontend -emit-module -o %t/attr.swiftmodule %s -verify -warnings-as-errors // RUN: llvm-bcanalyzer -dump %t/attr.swiftmodule | %FileCheck -check-prefix=CHECK-NON-RESILIENT %s // RUN: %target-swift-frontend -emit-module -o %t/attr_resilient.swiftmodule -enable-library-evolution -warnings-as-errors %s // RUN: llvm-bcanalyzer -dump %t/attr_resilient.swiftmodule | %FileCheck -check-prefix=CHECK-RESILIENT %s @@ -8,7 +8,7 @@ // CHECK-RESILIENT: Frozen_DECL_ATTR // CHECK-NON-RESILIENT-NOT: Frozen_DECL_ATTR -@frozen // expected-warning {{@frozen has no effect without -enable-library-evolution}} +@frozen // expected-no-warning public enum SomeEnum { case x } diff --git a/test/Serialization/class.swift b/test/Serialization/class.swift index 5284933775b19..b291f22cc3195 100644 --- a/test/Serialization/class.swift +++ b/test/Serialization/class.swift @@ -46,9 +46,9 @@ var p = Pair(a: 1, b: 2.5) p.first = 2 p.second = 5.0 -struct Int {} +struct MyInt {} -var gc = GenericCtor(42) +var gc = GenericCtor(42) gc.doSomething(42) diff --git a/test/Serialization/comments-batch-mode.swift b/test/Serialization/comments-batch-mode.swift index 2929590252958..4336546ae4e75 100644 --- a/test/Serialization/comments-batch-mode.swift +++ b/test/Serialization/comments-batch-mode.swift @@ -1,10 +1,10 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -enable-batch-mode -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %S/Inputs/comments-batch/File1.swift %S/Inputs/comments-batch/File2.swift %S/Inputs/comments-batch/File3.swift %S/Inputs/comments-batch/File4.swift %S/Inputs/comments-batch/File5.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -wmo -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %S/Inputs/comments-batch/File1.swift %S/Inputs/comments-batch/File2.swift %S/Inputs/comments-batch/File3.swift %S/Inputs/comments-batch/File4.swift %S/Inputs/comments-batch/File5.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=Foo -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s // CHECK: Inputs/comments-batch/File1.swift:2:13: Func/FuncFromFile1 RawComment=[/// Comment in File1\n] // CHECK: Inputs/comments-batch/File2.swift:2:13: Func/FuncFromFile2 RawComment=[/// Comment in File2\n] diff --git a/test/Serialization/comments-framework.swift b/test/Serialization/comments-framework.swift index dbce0ec9dab66..84dd55f1e283f 100644 --- a/test/Serialization/comments-framework.swift +++ b/test/Serialization/comments-framework.swift @@ -3,10 +3,10 @@ // RUN: %empty-directory(%t/comments.framework/Modules/comments.swiftmodule/Project) // RUN: %target-swift-frontend -module-name comments -emit-module -emit-module-path %t/comments.framework/Modules/comments.swiftmodule/%target-swiftmodule-name -emit-module-doc-path %t/comments.framework/Modules/comments.swiftmodule/%target-swiftdoc-name -emit-module-source-info-path %t/comments.framework/Modules/comments.swiftmodule/Project/%target-swiftsourceinfo-name %s -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -F %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -F %t | %FileCheck %s // RUN: cp -r %t/comments.framework/Modules/comments.swiftmodule %t/comments.swiftmodule -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t | %FileCheck %s +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s /// first_decl_class_1 Aaa. public class first_decl_class_1 { diff --git a/test/Serialization/comments-hidden.swift b/test/Serialization/comments-hidden.swift index bb8bfa5507b37..9ad0e52fd170c 100644 --- a/test/Serialization/comments-hidden.swift +++ b/test/Serialization/comments-hidden.swift @@ -18,7 +18,7 @@ // // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -enable-testing -module-name comments -emit-module -emit-module-path %t/comments.swiftmodule -emit-module-doc -emit-module-doc-path %t/comments.swiftdoc -emit-module-source-info-path %t/comments.swiftsourceinfo %s -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t > %t.testing.txt +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t > %t.testing.txt // RUN: %FileCheck %s -check-prefix=SOURCE-LOC < %t.testing.txt /// PublicClass Documentation diff --git a/test/Serialization/comments.swift b/test/Serialization/comments.swift index 9e060026d3d40..8625ec6804f27 100644 --- a/test/Serialization/comments.swift +++ b/test/Serialization/comments.swift @@ -5,7 +5,7 @@ // RUN: llvm-bcanalyzer %t/comments.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftdoc | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftsourceinfo | %FileCheck %s -check-prefix=BCANALYZER -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t | %FileCheck %s -check-prefix=FIRST +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t | %FileCheck %s -check-prefix=FIRST // Test the case when we have a multiple files in a module. // @@ -16,7 +16,7 @@ // RUN: llvm-bcanalyzer %t/comments.swiftmodule | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftdoc | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftsourceinfo | %FileCheck %s -check-prefix=BCANALYZER -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t > %t.printed.txt +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t > %t.printed.txt // RUN: %FileCheck %s -check-prefix=FIRST < %t.printed.txt // RUN: %FileCheck %s -check-prefix=SECOND < %t.printed.txt @@ -97,4 +97,4 @@ extension first_decl_class_1 : P1 {} // RUN: %target-swift-frontend -module-name comments -emit-module -emit-module-path %t/Hidden/comments.swiftmodule -emit-module-interface-path %t/comments.swiftinterface -emit-module-doc -emit-module-doc-path %t/comments.swiftdoc -emit-module-source-info-path %t/comments.swiftsourceinfo %s -enable-library-evolution -swift-version 5 // RUN: llvm-bcanalyzer %t/comments.swiftdoc | %FileCheck %s -check-prefix=BCANALYZER // RUN: llvm-bcanalyzer %t/comments.swiftsourceinfo | %FileCheck %s -check-prefix=BCANALYZER -// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -source-filename %s -I %t -swift-version 5 | %FileCheck %s -check-prefix=FIRST +// RUN: %target-swift-ide-test -print-module-comments -module-to-print=comments -enable-swiftsourceinfo -source-filename %s -I %t -swift-version 5 | %FileCheck %s -check-prefix=FIRST diff --git a/test/Serialization/multi-file-nested-type-extension.swift b/test/Serialization/multi-file-nested-type-extension.swift index fd2d139a25009..181fc38b72c52 100644 --- a/test/Serialization/multi-file-nested-type-extension.swift +++ b/test/Serialization/multi-file-nested-type-extension.swift @@ -10,7 +10,7 @@ // REQUIRES: asserts // CHECK: Statistics -// CHECK: 1 Serialization - # of nested types resolved without full lookup +// CHECK: 2 Serialization - # of nested types resolved without full lookup // Note the Optional here and below; this was once necessary to produce a crash. // Without it, the type of the parameter is initialized "early" enough to not @@ -21,3 +21,9 @@ extension Outer { public func useTypes(_: Outer.Callback?) {} } + +extension OuterClass.Inner { + public static var instance: OuterClass.Inner { + return OuterClass.Inner() + } +} diff --git a/test/Serialization/nested-type-with-overlay.swift b/test/Serialization/nested-type-with-overlay.swift index e2c2f36cb0423..4b83de9c05b7b 100644 --- a/test/Serialization/nested-type-with-overlay.swift +++ b/test/Serialization/nested-type-with-overlay.swift @@ -8,8 +8,8 @@ // REQUIRES: asserts -// CHECK: 3 Serialization - # of nested types resolved without full lookup -// Unfortunately this isn't 4 because of the shadowed nested type from Clang. +// CHECK: 4 Serialization - # of nested types resolved without full lookup +// Unfortunately this isn't 5 because of the shadowed nested type from Clang. import HasOverlay @@ -20,3 +20,9 @@ public func resolveNestedTypes( public var shadowedFromClang = getShadowedFromClang() public var shadowedFromSwift = HasOverlay.shadowedFromSwift + +extension CustomError.Code { + public static var failedSuccessfully: CustomError.Code { + return CustomError.Code(rawValue: -9999) + } +} diff --git a/test/Serialization/noinline.swift b/test/Serialization/noinline.swift index 4a775d2e0cc45..a57ac201dcef6 100644 --- a/test/Serialization/noinline.swift +++ b/test/Serialization/noinline.swift @@ -8,9 +8,9 @@ import def_noinline -// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] @$s12def_noinline12testNoinline1xS2b_tF : $@convention(thin) (Bool) -> Bool { +// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] [ossa] @$s12def_noinline12testNoinline1xS2b_tF : $@convention(thin) (Bool) -> Bool { -// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] @$s12def_noinline18NoInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin NoInlineInitStruct.Type) -> NoInlineInitStruct { +// SIL-LABEL: sil public_external [serialized] [noinline] [canonical] [ossa] @$s12def_noinline18NoInlineInitStructV1xACSb_tcfC : $@convention(method) (Bool, @thin NoInlineInitStruct.Type) -> NoInlineInitStruct { // SIL-LABEL: sil @main // SIL: [[RAW:%.+]] = global_addr @$s8noinline3rawSbvp : $*Bool diff --git a/test/Serialization/opaque_types_inlineable.swift b/test/Serialization/opaque_types_inlineable.swift new file mode 100644 index 0000000000000..504d6b148273a --- /dev/null +++ b/test/Serialization/opaque_types_inlineable.swift @@ -0,0 +1,29 @@ +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -module-name A -emit-module %s %S/Inputs/opaque_types_inlineable_2.swift + +// This test case use to crash in the merge modules phase when the two partial +// modules are merged as one deserializing the module for this file now has +// access to opaque types in the other file (opaque_types_inlineable_2.swift). + +extension P { + @inlinable + public func r() -> some P { + return f { self.o(Q()) } + } + + @inlinable + public func q() throws -> some P { + return try p() + } +} + +public func f(_ fn: () -> T) -> some P { + return K() +} + +public struct K : P { + public init() {} +} + +public struct Q : P { + public init() {} +} diff --git a/test/Serialization/serialize_attr.swift b/test/Serialization/serialize_attr.swift index ceeddbbabe606..c5b48d90b497a 100644 --- a/test/Serialization/serialize_attr.swift +++ b/test/Serialization/serialize_attr.swift @@ -79,6 +79,6 @@ public class CC { } } -// CHECK-DAG: sil [serialized] [_specialize exported: false, kind: full, where T == Int, U == Float] [canonical] @$s14serialize_attr14specializeThis_1uyx_q_tr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () { +// CHECK-DAG: sil [serialized] [_specialize exported: false, kind: full, where T == Int, U == Float] [canonical] [ossa] @$s14serialize_attr14specializeThis_1uyx_q_tr0_lF : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () { -// CHECK-DAG: sil [serialized] [noinline] [_specialize exported: false, kind: full, where T == RR, U == SS] [canonical] @$s14serialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lF : $@convention(method) (@in_guaranteed U, GG, @guaranteed CC) -> (@out U, GG) { +// CHECK-DAG: sil [serialized] [noinline] [_specialize exported: false, kind: full, where T == RR, U == SS] [canonical] [ossa] @$s14serialize_attr2CCC3foo_1gqd___AA2GGVyxGtqd___AHtAA2QQRd__lF : $@convention(method) (@in_guaranteed U, GG, @guaranteed CC) -> (@out U, GG) { diff --git a/test/SourceKit/CodeComplete/complete_constructor.swift b/test/SourceKit/CodeComplete/complete_constructor.swift index 47633232f54ff..19f38b4607042 100644 --- a/test/SourceKit/CodeComplete/complete_constructor.swift +++ b/test/SourceKit/CodeComplete/complete_constructor.swift @@ -7,4 +7,4 @@ class Foo { Foo( // RUN: %sourcekitd-test -req=complete -pos=7:5 %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/CodeComplete/complete_filter_rules.swift b/test/SourceKit/CodeComplete/complete_filter_rules.swift index 8be325740ebea..437ac7079b195 100644 --- a/test/SourceKit/CodeComplete/complete_filter_rules.swift +++ b/test/SourceKit/CodeComplete/complete_filter_rules.swift @@ -29,19 +29,19 @@ struct TestHideName { // RUN: %complete-test -filter-rules=%S/Inputs/filter-rules/hideKeywords.json -tok=HIDE_KEYWORDS_1 %s -- -F %S/../Inputs/libIDE-mock-sdk | %FileCheck %s -check-prefix=HIDE_LET func testHideKeyword01() { #^HIDE_KEYWORDS_1^# -// HIDE_LET-NOT: let +// HIDE_LET-NOT: {{^}}let // HIDE_LET: var -// HIDE_LET-NOT: let +// HIDE_LET-NOT: {{^}}let } // RUN: %complete-test -filter-rules=%S/Inputs/filter-rules/showKeywords.json -tok=HIDE_KEYWORDS_2 %s -- -F %S/../Inputs/libIDE-mock-sdk | %FileCheck %s -check-prefix=SHOW_FUNC func testHideKeyword02() { #^HIDE_KEYWORDS_2^# -// SHOW_FUNC-NOT: let +// SHOW_FUNC-NOT: {{^}}let // SHOW_FUNC-NOT: var // SHOW_FUNC: func // SHOW_FUNC-NOT: var -// SHOW_FUNC-NOT: let +// SHOW_FUNC-NOT: {{^}}let } // RUN: %complete-test -filter-rules=%S/Inputs/filter-rules/hideLiterals.json -tok=HIDE_LITERALS_1 %s -- -F %S/../Inputs/libIDE-mock-sdk | %FileCheck %s -check-prefix=HIDE_NIL diff --git a/test/SourceKit/CodeComplete/complete_from_clang_module.swift b/test/SourceKit/CodeComplete/complete_from_clang_module.swift index f366089c1c583..264472482ccea 100644 --- a/test/SourceKit/CodeComplete/complete_from_clang_module.swift +++ b/test/SourceKit/CodeComplete/complete_from_clang_module.swift @@ -1,7 +1,7 @@ import Foo // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=complete -pos=2:1 %s -- %mcp_opt -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s +// RUN: %sourcekitd-test -req=complete -pos=2:1 %s -- -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s // CHECK-LABEL: key.name: "fooIntVar", // CHECK-NEXT: key.sourcetext: "fooIntVar", diff --git a/test/SourceKit/CodeComplete/complete_group_overloads.swift b/test/SourceKit/CodeComplete/complete_group_overloads.swift index d0fc537807e27..dbeea5604c4a4 100644 --- a/test/SourceKit/CodeComplete/complete_group_overloads.swift +++ b/test/SourceKit/CodeComplete/complete_group_overloads.swift @@ -89,6 +89,7 @@ func test005() { // BAR_INIT_0: () // BAR_INIT_0-NEXT: (x: A) // BAR_INIT_0-NEXT: (x: B) +// BAR_INIT_0-NEXT: .foo(self: Bar) // BAR_INIT_0-NEXT: .self extension Bar { diff --git a/test/SourceKit/CodeComplete/complete_member.swift b/test/SourceKit/CodeComplete/complete_member.swift index 1851daee22ee6..121a817bacdff 100644 --- a/test/SourceKit/CodeComplete/complete_member.swift +++ b/test/SourceKit/CodeComplete/complete_member.swift @@ -32,7 +32,7 @@ class Base { } class Derived: Base { - func foo() {} + override func foo() {} } func testOverrideUSR() { @@ -40,7 +40,7 @@ func testOverrideUSR() { } // RUN: %sourcekitd-test -req=complete -pos=15:5 %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response // // RUN: %sourcekitd-test -req=complete -pos=19:5 %s -- %s | %FileCheck %s -check-prefix=CHECK-OPTIONAL // RUN: %sourcekitd-test -req=complete.open -pos=19:5 %s -- %s | %FileCheck %s -check-prefix=CHECK-OPTIONAL-OPEN diff --git a/test/SourceKit/CodeComplete/complete_override.swift b/test/SourceKit/CodeComplete/complete_override.swift index 3b834a5ed4f70..7d2878c7fb6c6 100644 --- a/test/SourceKit/CodeComplete/complete_override.swift +++ b/test/SourceKit/CodeComplete/complete_override.swift @@ -6,5 +6,5 @@ class Derived : Base { } // RUN: %sourcekitd-test -req=complete -pos=5:1 %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/CodeComplete/complete_requestlimit.swift b/test/SourceKit/CodeComplete/complete_requestlimit.swift index 2c5bba5e33e9b..a22cc32c70fc8 100644 --- a/test/SourceKit/CodeComplete/complete_requestlimit.swift +++ b/test/SourceKit/CodeComplete/complete_requestlimit.swift @@ -32,6 +32,8 @@ func test001() { // TOP_LEVEL_0_ALL-NEXT: z // TOP_LEVEL_0_ALL-NEXT: A // TOP_LEVEL_0_ALL-NEXT: B +// TOP_LEVEL_0_ALL-NEXT: C +// TOP_LEVEL_0_ALL-NEXT: D // TOP_LEVEL_0_ALL-NEXT: test // TOP_LEVEL_0_3: let diff --git a/test/SourceKit/CodeComplete/complete_sequence.swift b/test/SourceKit/CodeComplete/complete_sequence.swift new file mode 100644 index 0000000000000..1b1c8c8e6a1d1 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence.swift @@ -0,0 +1,65 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Tests for 'key.codecomplete.reuseastcontex' option. + +// Disabled. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -pos=12:11 %s -- %s == \ +// RUN: -req=complete -pos=15:11 %s -- %s > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response + +// Enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- %s > %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext +// RUN: %FileCheck --check-prefix=TRACE_REUSEAST %s < %t.response.reuseastcontext + +// Enabled - compiler argument mismatch. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -- %s -DNOTUSED == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -- -DNOTUSED %s > %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response.reuseastcontext_argmismatch +// RUN: %FileCheck --check-prefix=TRACE_NORMAL %s < %t.response.reuseastcontext_argmismatch + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_NORMAL-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_NORMAL-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" + +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE_REUSEAST-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE_REUSEAST: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_accessor.swift b/test/SourceKit/CodeComplete/complete_sequence_accessor.swift new file mode 100644 index 0000000000000..3460a9a0975c1 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_accessor.swift @@ -0,0 +1,136 @@ +class Foo { + var x: Int = 0 + var y: Int = 0 + func fooMethod() {} +} +struct Bar { + var a: Int = 0 + var b: Int = 0 + func barMethod() {} +} +var globalValImplicit: Foo { + Bar(). +} +var globalValGetSet: Foo { + get { Foo(). } + set { Bar(). } +} + +enum S { + var foo: Foo + var bar: Bar + var propertyImplicit: Foo { + foo. + } + var propertyGetSet: Foo { + get { bar. } + set { foo. } + } + subscript(idx: Foo) -> Foo { + idx. + } + subscript(idx: Bar) -> Foo { + get { idx. } + set { idx. } + } +} + + +// Enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:9 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:15 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:15 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=23:9 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=26:15 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=27:15 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=30:9 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=33:15 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=34:15 %s -- %s > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// globalValImplicit +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] +// globalValGetSet(get) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// globalValGetSet(set) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] +// propertyImplicit +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// propertyGetSet(get) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] +// propertyGetSet(set) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// subscript(implicit getter) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// subscript(get) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] +// subscript(set) +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + + +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_bodyrange.swift b/test/SourceKit/CodeComplete/complete_sequence_bodyrange.swift new file mode 100644 index 0000000000000..9dad9d0e98fd1 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_bodyrange.swift @@ -0,0 +1,57 @@ +class TestChain { + class Child { + var value: Struct1? + } + class Struct1 { + var value: Struct2 + } + class Struct2 { + var prop1: Int + var prop2: Int + } + + var child: Child! + + func foo() { + let _ = child.value?.value. + } +} + +// rdar://problem/58098222 + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:32 %s -async -- %s | %FileCheck %s + +// CHECK-NOT: key.name: "prop1" +// CHECK-NOT: key.name: "prop2" +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK: key.name: "prop1", +// CHECK: key.name: "prop2", +// CHECK-NOT: key.name: "prop1" +// CHECK-NOT: key.name: "prop2" diff --git a/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift new file mode 100644 index 0000000000000..bf7ac0fad0117 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_crossfile.swift @@ -0,0 +1,55 @@ +// BEGIN file1.swift +class Foo { + var x: Int + var y: Int +} + +func foo(arg: Foo) { + _ = arg. +} + +class Bar { + var a: Int + var b: Int + func barMethod() {} +} + +// BEGIN file2.swift +extension Foo { + func fooMethod() {} +} + +func bar(arg: Bar) { + _ = arg. +} + +extension Bar { + func barMethod() {} +} + +// BEGIN dummy.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=7:11 %t/file1.swift -- %t/file1.swift %t/file2.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=6:11 %t/file2.swift -- %t/file1.swift %t/file2.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "barMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "a" +// RESULT-DAG: key.name: "b" +// RESULT: ] + +// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_edit.swift b/test/SourceKit/CodeComplete/complete_sequence_edit.swift new file mode 100644 index 0000000000000..37dae86946b5b --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_edit.swift @@ -0,0 +1,89 @@ +// BEGIN State1.swift +// Initial state. +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State2.swift +// Compatible change: implemented 'Foo.fooMethod()', indentation change, added white line. +class Foo { + var x: Int + var y: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN State3.swift +// Incompatible change: added 'Foo.z' +class Foo { + var x: Int + var y: Int + var z: Int + func fooMethod() { + print(x + y) + } +} + +func foo(arg: Foo) { + _ = arg. +} + +// BEGIN DUMMY.swift + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=8:11 -name file.swift -text-input %t/State1.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=11:13 -name file.swift -text-input %t/State2.swift -- file.swift == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:13 -name file.swift -text-input %t/State3.swift -- file.swift > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-NOT: key.name: "z" +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT: ] + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.name: "fooMethod()" +// RESULT-DAG: key.name: "self" +// RESULT-DAG: key.name: "x" +// RESULT-DAG: key.name: "y" +// RESULT-DAG: key.name: "z" +// RESULT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE: ] + +// TRACE: key.notification: source.notification.compile-did-finish, +// TRACE-NEXT: key.diagnostics: [ +// TRACE-NEXT: ] diff --git a/test/SourceKit/CodeComplete/complete_sequence_localvar.swift b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift new file mode 100644 index 0000000000000..49a27534782f8 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_localvar.swift @@ -0,0 +1,26 @@ +func test() { + var localVar = 1 + + var afterVar = 1 +} + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=3:1 %s -- %s | %FileCheck %s + +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" +// CHECK: key.name: "localVar" +// CHECK-NOT: key.name: "localVar" +// CHECK-NOT: key.name: "afterVar" diff --git a/test/SourceKit/CodeComplete/complete_sequence_property_wrapper.swift b/test/SourceKit/CodeComplete/complete_sequence_property_wrapper.swift new file mode 100644 index 0000000000000..cfa883de5a7cf --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_property_wrapper.swift @@ -0,0 +1,36 @@ +@propertyWrapper +struct TwelveOrLess { + private var number = 0 + var wrappedValue: Int { + get { return number } + set { number = min(newValue, 12) } + } +} + +struct MyStruct { + @TwelveOrLess var value = 12 + + func foo() {} + + func bar(barParam: Int) { + + } +} + +// RUN: %sourcekitd-test \ +// RUN: -req=track-compiles == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=16:1 -repeat-request=2 %s -- %s > %t.response +// RUN: %FileCheck --check-prefix=RESULT %s < %t.response +// RUN: %FileCheck --check-prefix=TRACE %s < %t.response + +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.description: "barParam" +// RESULT: ] +// RESULT-LABEL: key.results: [ +// RESULT-DAG: key.description: "barParam" +// RESULT: ] + +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE-NOT: key.description: "completion reusing previous ASTContext (benign diagnostic)" +// TRACE-LABEL: key.notification: source.notification.compile-did-finish, +// TRACE: key.description: "completion reusing previous ASTContext (benign diagnostic)" diff --git a/test/SourceKit/CodeComplete/complete_sequence_race.swift b/test/SourceKit/CodeComplete/complete_sequence_race.swift new file mode 100644 index 0000000000000..47cb1b147cc49 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_sequence_race.swift @@ -0,0 +1,48 @@ +class Foo { + var x: Int + var y: Int + func fooMethod() {} +} +struct Bar { + var a: Int + var b: Int + func barMethod() {} +} +func foo(arg: Foo) { + _ = arg. +} +func bar(arg: Bar) { + _ = arg. +} + +// NOTE: Test for simultaneous completion requests don't cause compiler crashes. + +// ReuseASTContext disabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -pos=15:11 %s -async -- %s + +// ReuseASTContext enabled. +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=17:1 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=12:11 %s -async -- %s == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=15:11 %s -async -- %s diff --git a/test/SourceKit/CodeComplete/complete_sort_order.swift b/test/SourceKit/CodeComplete/complete_sort_order.swift index ff9ab273a992b..ed83a81d64022 100644 --- a/test/SourceKit/CodeComplete/complete_sort_order.swift +++ b/test/SourceKit/CodeComplete/complete_sort_order.swift @@ -41,6 +41,7 @@ func test() { // CONTEXT-NOT: key.name: // CONTEXT: key.name: "test()" // CONTEXT: key.name: "#column" +// CONTEXT: key.name: "complete_sort_order" // RUN: %complete-test -tok=STMT_0 %s | %FileCheck %s -check-prefix=STMT func test1() { @@ -189,7 +190,7 @@ func test6() { // VOID_1_RAW-NEXT: key.sourcetext: "foo1()", // VOID_1_RAW-NEXT: key.description: "foo1()", // VOID_1_RAW-NEXT: key.typename: "Void", -// VOID_1_RAW-NEXT: key.context: source.codecompletion.context.thismodule, +// VOID_1_RAW-NEXT: key.context: source.codecompletion.context.local, // VOID_1_RAW-NEXT: key.num_bytes_to_erase: 0, // VOID_1_RAW-NEXT: key.not_recommended: 1, @@ -204,8 +205,8 @@ func test7() { #^CASE_0,caseSensitiveCheck,CaseSensitiveCheck^# } // CASE_0: Results for filterText: caseSensitiveCheck [ -// CASE_0: caseSensitiveCheck // CASE_0: CaseSensitiveCheck +// CASE_0: caseSensitiveCheck // CASE_0: caseSensitiveCheck. // CASE_0: ] // CASE_0: Results for filterText: CaseSensitiveCheck [ diff --git a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift index 627f5e8183e68..96c3e9ef4d723 100644 --- a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift +++ b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift @@ -12,5 +12,5 @@ func test() -> Foo { } // RUN: %sourcekitd-test -req=complete -pos=11:11 %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/CodeComplete/complete_without_stdlib.swift b/test/SourceKit/CodeComplete/complete_without_stdlib.swift new file mode 100644 index 0000000000000..88d792388173a --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_without_stdlib.swift @@ -0,0 +1,19 @@ +class Str { + var value: Str +} + +// rdar://problem/58663066 +// Test a environment where stdlib is not found. +// Completion should return zero result. + +// RUN: %empty-directory(%t/rsrc) +// RUN: %empty-directory(%t/sdk) + +// RUN: %sourcekitd-test \ +// RUN: -req=complete -pos=4:1 %s -- %s -resource-dir %t/rsrc -sdk %t/sdk | %FileCheck %s +// RUN: %sourcekitd-test \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=4:1 %s -- %s -resource-dir %t/rsrc -sdk %t/sdk == \ +// RUN: -req=complete -req-opts=reuseastcontext=1 -pos=4:1 %s -- %s -resource-dir %t/rsrc -sdk %t/sdk | %FileCheck %s + +// CHECK: key.results: [ +// CHECK-NOT: key.description: diff --git a/test/SourceKit/CodeComplete/injected_vfs.swift b/test/SourceKit/CodeComplete/injected_vfs.swift index e4b5f71ff45ed..b6eb657337790 100644 --- a/test/SourceKit/CodeComplete/injected_vfs.swift +++ b/test/SourceKit/CodeComplete/injected_vfs.swift @@ -19,9 +19,9 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete -pos=9:27 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s -// RUN: %sourcekitd-test -req=complete -pos=10:31 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s -// RUN: %sourcekitd-test -req=complete -pos=11:30 -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s +// RUN: %sourcekitd-test -req=complete -pos=9:27 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=complete -pos=10:31 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s +// RUN: %sourcekitd-test -req=complete -pos=11:30 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s // RUN: not %sourcekitd-test -req=complete -vfs-name nope %s -pass-as-sourcetext -dont-print-request -pos=9:27 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' diff --git a/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift b/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift index 9f6df7bd69ffa..167fea6c7855e 100644 --- a/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift +++ b/test/SourceKit/CodeComplete/injected_vfs_complete_open.swift @@ -19,32 +19,32 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete.open -pos=9:28 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=10:32 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:28 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=10:32 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET %s // RUN: not %sourcekitd-test -req=complete.open -vfs-name nope %s -pass-as-sourcetext -dont-print-request -pos=9:27 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' -// RUN: not %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ -// RUN: == -req=complete.update -pos=11:31 -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift 2>&1 | %FileCheck --check-prefix=UNSUPPORTED_REQ %s +// RUN: not %sourcekitd-test -req=complete.open -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ +// RUN: == -req=complete.update -pos=11:31 -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift 2>&1 | %FileCheck --check-prefix=UNSUPPORTED_REQ %s // UNSUPPORTED_REQ: error response (Request Invalid): This request does not support custom filesystems -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=11:31 %s | %FileCheck --check-prefix=CHECK-SAMETARGET %s -// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=11:31 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=11:31 -req-opts=filtertext=method %s | %FileCheck --check-prefix=CHECK-SAMETARGET %s // Inner completion. -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_SAMETARGET %s -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_SAMETARGET %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=9:1 -req-opts=filtertext=StructDefinedInSameTarget %s | %FileCheck --check-prefix=INNER_SAMETARGET %s // INNER_SAMETARGET: key.name: "StructDefinedInSameTarget" // INNER_SAMETARGET: key.name: "StructDefinedInSameTarget." -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInCModule -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_CMODULE %s -// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple \ +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -req-opts=filtertext=StructDefinedInCModule -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=INNER_CMODULE %s +// RUN: %sourcekitd-test -req=complete.open -pos=9:1 -dont-print-response -vfs-files=%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple \ // RUN: == -req=complete.update -pos=9:1 -req-opts=filtertext=StructDefinedInCModule %s | %FileCheck --check-prefix=INNER_CMODULE %s // INNER_CMODULE: key.name: "StructDefinedInCModule" // INNER_CMODULE: key.name: "StructDefinedInCModule." diff --git a/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift b/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift index 572de65437b03..1088f98e3a45d 100644 --- a/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift +++ b/test/SourceKit/CodeComplete/injected_vfs_swiftinterface.swift @@ -10,4 +10,4 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module-interface-path %t/SwiftModule.swiftinterface -module-name SwiftModule -emit-module -o /dev/null %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=complete -pos=6:31 -vfs-files=/target_file1.swift=%s,/SwiftModule/SwiftModule.swiftinterface=%t/SwiftModule.swiftinterface /target_file1.swift -pass-as-sourcetext -- /target_file1.swift -I /SwiftModule -target %target-triple | %FileCheck %s +// RUN: %sourcekitd-test -req=complete -pos=6:31 -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/SwiftModule/SwiftModule.swiftinterface=%t/SwiftModule.swiftinterface %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift -I %t/VFS/SwiftModule -target %target-triple | %FileCheck %s diff --git a/test/SourceKit/CodeExpand/code-expand.swift b/test/SourceKit/CodeExpand/code-expand.swift index 950bd9edf4605..1313be88fd642 100644 --- a/test/SourceKit/CodeExpand/code-expand.swift +++ b/test/SourceKit/CodeExpand/code-expand.swift @@ -90,9 +90,7 @@ func f1() { func f1() { bar(a : {}}, <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar(a : {}) { -// CHECK-NEXT: <#code#> -// CHECK-NEXT: } +// CHECK: bar(a : {}}, <#T##d: () -> ()##() -> ()#>) foo(withDuration: 1, animations: <#T##() -> Void#>) diff --git a/test/SourceKit/CompileNotifications/diagnostics.swift b/test/SourceKit/CompileNotifications/diagnostics.swift index 4a6d0894e5956..e2a2f6a09649d 100644 --- a/test/SourceKit/CompileNotifications/diagnostics.swift +++ b/test/SourceKit/CompileNotifications/diagnostics.swift @@ -63,7 +63,7 @@ // Note: we're missing the "compiler is in code completion mode" diagnostic, // which is probably just as well. // RUN: %sourcekitd-test -req=track-compiles == -req=complete -offset=0 %s -- %s | %FileCheck %s -check-prefix=NODIAGS -// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=SEMA +// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=NODIAGS // FIXME: invalid arguments cause us to early-exit and not send the notifications // RUN_DISABLED: %sourcekitd-test -req=track-compiles == -req=sema %s -- %s -invalid-arg | %FileCheck %s -check-prefix=INVALID_ARG diff --git a/test/SourceKit/ConformingMethods/basic.swift b/test/SourceKit/ConformingMethods/basic.swift index 69b69dc7a08e2..ff511ea01ff96 100644 --- a/test/SourceKit/ConformingMethods/basic.swift +++ b/test/SourceKit/ConformingMethods/basic.swift @@ -27,4 +27,4 @@ func testing(obj: C) { } // RUN: %sourcekitd-test -req=conformingmethods -pos=26:14 %s -req-opts=expectedtypes='$s8MyModule7Target2PD;$s8MyModule7Target1PD' -- -module-name MyModule %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/ConformingMethods/generics.swift b/test/SourceKit/ConformingMethods/generics.swift index 15f420bdd43af..8843030733503 100644 --- a/test/SourceKit/ConformingMethods/generics.swift +++ b/test/SourceKit/ConformingMethods/generics.swift @@ -18,6 +18,6 @@ func test(value: S) { } // RUN: %sourcekitd-test -req=conformingmethods -pos=12:10 %s -req-opts=expectedtypes='$s8MyModule5ProtoPD' -- -module-name MyModule %s > %t.response.1 -// RUN: diff -u %s.response.1 %t.response.1 +// RUN: diff --strip-trailing-cr -u %s.response.1 %t.response.1 // RUN: %sourcekitd-test -req=conformingmethods -pos=17:8 %s -req-opts=expectedtypes='$s8MyModule5ProtoPD' -- -module-name MyModule %s > %t.response.2 -// RUN: diff -u %s.response.2 %t.response.2 +// RUN: diff --strip-trailing-cr -u %s.response.2 %t.response.2 diff --git a/test/SourceKit/CursorInfo/cursor_info.swift b/test/SourceKit/CursorInfo/cursor_info.swift index 2188364c1742c..39848e65919b0 100644 --- a/test/SourceKit/CursorInfo/cursor_info.swift +++ b/test/SourceKit/CursorInfo/cursor_info.swift @@ -202,6 +202,7 @@ func convention1(_: @convention(thick) ()->()) {} func convention2(_: @convention(thin) ()->()) {} func convention3(_: @convention(block) ()->()) {} func convention4(_: @convention(c) ()->()) {} +func convention4a(_: @convention(c, cType: "void *(void *, int[])") () -> () {} func convention5(_: @convention(method) ()->()) {} func convention6(_: @convention(objc_method) ()->()) {} func convention7(_: @convention(witness_method: P1) ()->()) {} @@ -230,14 +231,14 @@ enum E7: String { // REQUIRES: objc_interop // RUN: %empty-directory(%t.tmp) // RUN: %swiftc_driver -emit-module -o %t.tmp/FooSwiftModule.swiftmodule %S/Inputs/FooSwiftModule.swift -// RUN: %sourcekitd-test -req=cursor -pos=9:8 %s -- -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=cursor -pos=9:8 %s -- -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck -check-prefix=CHECK1 %s // CHECK1: source.lang.swift.ref.var.global (4:5-4:9) // CHECK1-NEXT: glob // CHECK1-NEXT: s:11cursor_info4globSivp{{$}} // CHECK1-NEXT: Int // FIXME(integers): Disabling the checks. See -// XUN: %sourcekitd-test -req=cursor -pos=9:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s +// XUN: %sourcekitd-test -req=cursor -pos=9:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s // XCHECK2: source.lang.swift.ref.function.operator.infix () // XCHECK2-NEXT: + // XCHECK2-NEXT: s:s1poiyS2i_SitF @@ -249,7 +250,7 @@ enum E7: String { // XCHECK2-NEXT: func +(lhs: Int, rhs: Int) -> Int // XCHECK2-NEXT: func +(lhs: Int, rhs: Int) -> Int -// RUN: %sourcekitd-test -req=cursor -pos=9:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=cursor -pos=9:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s // CHECK3: source.lang.swift.ref.var.local (8:12-8:13) // CHECK3-NEXT: x{{$}} // CHECK3-NEXT: s:11cursor_info3gooyySiF1xL_Sivp{{$}} @@ -258,7 +259,7 @@ enum E7: String { // CHECK3-NEXT: let x: Int // CHECK3-NEXT: let x: Int -// RUN: %sourcekitd-test -req=cursor -pos=9:18 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK4 %s +// RUN: %sourcekitd-test -req=cursor -pos=9:18 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s // CHECK4: source.lang.swift.ref.var.global ({{.*}}Foo.framework/Headers/Foo.h:63:12-63:21) // CHECK4-NEXT: fooIntVar{{$}} // CHECK4-NEXT: c:@fooIntVar{{$}} @@ -269,13 +270,13 @@ enum E7: String { // CHECK4-NEXT: var fooIntVar: Int32 // CHECK4-NEXT: fooIntVarc:@fooIntVarvar fooIntVar: Int32 Aaa. fooIntVar. Bbb. -// RUN: %sourcekitd-test -req=cursor -pos=8:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=cursor -pos=8:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s // CHECK5: source.lang.swift.decl.function.free (8:6-8:19) // CHECK5-NEXT: goo(_:){{$}} // CHECK5-NEXT: s:11cursor_info3gooyySiF{{$}} // CHECK5-NEXT: (Int) -> (){{$}} -// RUN: %sourcekitd-test -req=cursor -pos=9:32 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK6 %s +// RUN: %sourcekitd-test -req=cursor -pos=9:32 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK6 %s // CHECK6: source.lang.swift.ref.function.free () // CHECK6-NEXT: fooSwiftFunc // CHECK6-NEXT: s:14FooSwiftModule03fooB4FuncSiyF @@ -286,7 +287,7 @@ enum E7: String { // CHECK6-NEXT: func fooSwiftFunc() -> Int // CHECK6-NEXT: {{^}}fooSwiftFunc()s:14FooSwiftModule03fooB4FuncSiyFfunc fooSwiftFunc() -> IntThis is ‘fooSwiftFunc’ from ‘FooSwiftModule’.{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=14:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK7 %s +// RUN: %sourcekitd-test -req=cursor -pos=14:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK7 %s // CHECK7: source.lang.swift.ref.struct (13:8-13:10) // CHECK7-NEXT: S1 // CHECK7-NEXT: s:11cursor_info2S1V @@ -296,7 +297,7 @@ enum E7: String { // CHECK7-NEXT: struct S1 // CHECK7-NEXT: S1s:11cursor_info2S1Vstruct S1Aaa. S1. Bbb. -// RUN: %sourcekitd-test -req=cursor -pos=19:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK8 %s +// RUN: %sourcekitd-test -req=cursor -pos=19:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK8 %s // CHECK8: source.lang.swift.ref.function.constructor (18:3-18:15) // CHECK8-NEXT: init // CHECK8-NEXT: s:11cursor_info2CCC1xACSi_tcfc @@ -306,38 +307,38 @@ enum E7: String { // CHECK8-NEXT: init(x: Int) // CHECK8-NEXT: init(x: Int) -// RUN: %sourcekitd-test -req=cursor -pos=23:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK9 %s +// RUN: %sourcekitd-test -req=cursor -pos=23:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s // CHECK9: source.lang.swift.decl.var.global (23:5-23:15) // CHECK9: var testString: String // CHECK9: var testString: String -// RUN: %sourcekitd-test -req=cursor -pos=24:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK10 %s +// RUN: %sourcekitd-test -req=cursor -pos=24:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK10 %s // CHECK10: source.lang.swift.decl.var.global (24:5-24:18) // CHECK10: let testLetString: String // CHECK10: let testLetString: String -// RUN: %sourcekitd-test -req=cursor -pos=26:20 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK11 %s +// RUN: %sourcekitd-test -req=cursor -pos=26:20 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11 %s // CHECK11: source.lang.swift.decl.var.parameter (26:19-26:23) // CHECK11: let arg1: Int // CHECK11: let arg1: Int -// RUN: %sourcekitd-test -req=cursor -pos=28:24 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK12 %s +// RUN: %sourcekitd-test -req=cursor -pos=28:24 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK12 %s // CHECK12: source.lang.swift.decl.var.parameter (28:21-28:25) // CHECK12: var arg1: inout Int // CHECK12: var arg1: inout Int -// RUN: %sourcekitd-test -req=cursor -pos=31:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=cursor -pos=31:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s // CHECK13: source.lang.swift.decl.function.free (31:6-31:37) // CHECK13: func testDefaultParam(arg1: Int = 0) // CHECK13: func testDefaultParam(arg1: Int = 0) -// RUN: %sourcekitd-test -req=cursor -pos=34:4 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=cursor -pos=34:4 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s // CHECK14: source.lang.swift.ref.function.free ({{.*}}Foo.framework/Frameworks/FooSub.framework/Headers/FooSub.h:4:5-4:16) // CHECK14: fooSubFunc1 // CHECK14: c:@F@fooSubFunc1 // CHECK14: Foo.FooSub{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=38:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK15 %s +// RUN: %sourcekitd-test -req=cursor -pos=38:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK15 %s // CHECK15: source.lang.swift.decl.function.free (38:6-38:40) // CHECK15: myFunc // CHECK15: func myFunc(arg1: String, options: Int) @@ -346,60 +347,60 @@ enum E7: String { // CHECK15-NEXT: myFunc(arg1:) // CHECK15-NEXT: RELATED END -// RUN: %sourcekitd-test -req=cursor -pos=41:26 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK16 %s +// RUN: %sourcekitd-test -req=cursor -pos=41:26 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK16 %s // CHECK16: source.lang.swift.ref.class ({{.*}}Foo.framework/Headers/Foo.h:158:12-158:27) // CHECK16-NEXT: FooClassDerived // CHECK16-NEXT: c:objc(cs)FooClassDerived // CHECK16: class FooClassDerived : FooClassBase, FooProtocolDerived // CHECK16-NEXT: class FooClassDerived : FooClassBase, FooProtocolDerived -// RUN: %sourcekitd-test -req=cursor -pos=1:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK17 %s +// RUN: %sourcekitd-test -req=cursor -pos=1:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK17 %s // CHECK17: source.lang.swift.ref.module () // CHECK17-NEXT: Foo{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=44:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK18 %s +// RUN: %sourcekitd-test -req=cursor -pos=44:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK18 %s // CHECK18: source.lang.swift.ref.typealias (43:11-43:16) // CHECK18: typealias MyInt = Int // CHECK18: typealias MyInt = Int -// RUN: %sourcekitd-test -req=cursor -pos=46:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK19 %s +// RUN: %sourcekitd-test -req=cursor -pos=46:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK19 %s // CHECK19: source.lang.swift.ref.module () // CHECK19-NEXT: FooHelper{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=46:25 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK20 %s +// RUN: %sourcekitd-test -req=cursor -pos=46:25 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK20 %s // CHECK20: source.lang.swift.ref.module () // CHECK20-NEXT: FooHelperSub{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=50:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK21 %s +// RUN: %sourcekitd-test -req=cursor -pos=50:12 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK21 %s // CHECK21: source.lang.swift.ref.var.global (44:5-44:6) // CHECK21-NEXT: {{^}}x{{$}} -// RUN: %sourcekitd-test -req=cursor -pos=55:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK22 %s +// RUN: %sourcekitd-test -req=cursor -pos=55:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK22 %s // CHECK22: func availabilityIntroduced() // CHECK22: func availabilityIntroduced() -// RUN: %sourcekitd-test -req=cursor -pos=56:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK23 %s +// RUN: %sourcekitd-test -req=cursor -pos=56:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK23 %s // CHECK23: -// RUN: %sourcekitd-test -req=cursor -pos=57:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK24 %s +// RUN: %sourcekitd-test -req=cursor -pos=57:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK24 %s // CHECK24: -// RUN: %sourcekitd-test -req=cursor -pos=58:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK25 %s +// RUN: %sourcekitd-test -req=cursor -pos=58:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK25 %s // CHECK25: func availabilityIntroducedMsg() // CHECK25: func availabilityIntroducedMsg() -// RUN: %sourcekitd-test -req=cursor -pos=59:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK26 %s +// RUN: %sourcekitd-test -req=cursor -pos=59:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK26 %s // CHECK26: -// RUN: %sourcekitd-test -req=cursor -pos=69:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK27 %s +// RUN: %sourcekitd-test -req=cursor -pos=69:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK27 %s // CHECK27: public subscript(i: Int) -> Int { get } // CHECK27: public subscript(i: Int) -> Int { get } -// RUN: %sourcekitd-test -req=cursor -pos=69:19 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK28 %s +// RUN: %sourcekitd-test -req=cursor -pos=69:19 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK28 %s // CHECK28: public subscript(i: Int) -> Int { get } // CHECK28: public subscript(i: Int) -> Int { get } -// RUN: %sourcekitd-test -req=cursor -pos=74:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK29 +// RUN: %sourcekitd-test -req=cursor -pos=74:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK29 // CHECK29: source.lang.swift.decl.function.destructor (74:3-74:9) // CHECK29-NEXT: deinit // CHECK29-NEXT: s:11cursor_info2C3Cfd @@ -408,7 +409,7 @@ enum E7: String { // CHECK29-NEXT: deinit // CHECK29-NEXT: deinit -// RUN: %sourcekitd-test -req=cursor -pos=75:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK30 +// RUN: %sourcekitd-test -req=cursor -pos=75:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK30 // CHECK30: source.lang.swift.decl.function.constructor (75:3-75:16) // CHECK30-NEXT: init(x:) // CHECK30-NEXT: s:11cursor_info2C3C1xACSgSi_tcfc @@ -417,7 +418,7 @@ enum E7: String { // CHECK30-NEXT: init!(x: Int) // CHECK30-NEXT: init!(x: Int) -// RUN: %sourcekitd-test -req=cursor -pos=76:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK31 +// RUN: %sourcekitd-test -req=cursor -pos=76:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK31 // CHECK31: source.lang.swift.decl.function.constructor (76:3-76:16) // CHECK31-NEXT: init(y:) // CHECK31-NEXT: s:11cursor_info2C3C1yACSgSi_tcfc @@ -426,7 +427,7 @@ enum E7: String { // CHECK31-NEXT: init?(y: Int) // CHECK31-NEXT: init?(y: Int) -// RUN: %sourcekitd-test -req=cursor -pos=77:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK32 +// RUN: %sourcekitd-test -req=cursor -pos=77:3 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK32 // CHECK32: source.lang.swift.decl.function.constructor (77:3-77:15) // CHECK32-NEXT: init(z:) // CHECK32-NEXT: s:11cursor_info2C3C1zACSi_tKcfc @@ -435,7 +436,7 @@ enum E7: String { // CHECK32-NEXT: init(z: Int) throws // CHECK32-NEXT: init(z: Int) throws -// RUN: %sourcekitd-test -req=cursor -pos=80:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK33 +// RUN: %sourcekitd-test -req=cursor -pos=80:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK33 // CHECK33: source.lang.swift.decl.struct (80:8-80:10) // CHECK33-NEXT: S2 // CHECK33-NEXT: s:11cursor_info2S2V @@ -443,7 +444,7 @@ enum E7: String { // CHECK33: struct S2<T, U> where T == U // CHECK33-NEXT: struct S2<T, U> where T == U -// RUN: %sourcekitd-test -req=cursor -pos=81:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK34 +// RUN: %sourcekitd-test -req=cursor -pos=81:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK34 // CHECK34: source.lang.swift.decl.function.method.instance (81:8-81:62) // CHECK34-NEXT: foo(_:_:_:) // CHECK34-NEXT: s:11cursor_info2S2V3fooyyycqd___qd__yyXEtqd_0_Rsd__r0_lF @@ -451,7 +452,7 @@ enum E7: String { // CHECK34: func foo<V, W>(_: V, _: W, _ closure: () -> ()) -> () -> () where V == W // CHECK34: func foo<V, W>(_: V, _: W, _ closure: () -> ()) -> () -> () where V == W -// RUN: %sourcekitd-test -req=cursor -pos=83:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK35 +// RUN: %sourcekitd-test -req=cursor -pos=83:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK35 // CHECK35: source.lang.swift.decl.class (83:7-83:9) // CHECK35-NEXT: C4 // CHECK35-NEXT: s:11cursor_info2C4C @@ -459,7 +460,7 @@ enum E7: String { // CHECK35: class C4<T, U> where T == U // CHECK35-NEXT: class C4<T, U> where T == U -// RUN: %sourcekitd-test -req=cursor -pos=84:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK36 +// RUN: %sourcekitd-test -req=cursor -pos=84:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK36 // CHECK36: source.lang.swift.decl.enum (84:6-84:8) // CHECK36-NEXT: E1 // CHECK36-NEXT: s:11cursor_info2E1O @@ -467,7 +468,7 @@ enum E7: String { // CHECK36: enum E1<T, U> where T == U // CHECK36-NEXT: enum E1<T, U> where T == U -// RUN: %sourcekitd-test -req=cursor -pos=86:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK37 +// RUN: %sourcekitd-test -req=cursor -pos=86:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK37 // CHECK37: source.lang.swift.decl.function.free (86:6-86:111) // CHECK37-NEXT: nonDefaultArgNames(external1:_:external3:external4:_:) // CHECK37-NEXT: s:11cursor_info18nonDefaultArgNames9external1_9external39external4_ySi_S4itF @@ -475,10 +476,10 @@ enum E7: String { // CHECK37: func nonDefaultArgNames(external1 local1: Int, _ local2: Int, external3 local3: Int, external4 _: Int, _: Int) // CHECK37-NEXT: func nonDefaultArgNames(external1 local1: Int, _ local2: Int, external3 local3: Int, external4 _: Int, _: Int) -// RUN: %sourcekitd-test -req=cursor -pos=88:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK38 +// RUN: %sourcekitd-test -req=cursor -pos=88:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK38 // CHECK38: func nestedFunctionType(closure: (_ y: (_ z: Int) -> Int) -> Int) -> (_ y: (_ z: Int) -> Int) -> Int -// RUN: %sourcekitd-test -req=cursor -pos=91:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK39 +// RUN: %sourcekitd-test -req=cursor -pos=91:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK39 // CHECK39: source.lang.swift.decl.enumelement (91:8-91:10) // CHECK39-NEXT: C1 // CHECK39-NEXT: s:11cursor_info2E2O2C1yA2CmF @@ -486,7 +487,7 @@ enum E7: String { // CHECK39: case C1 // CHECK39-NEXT: case C1 -// RUN: %sourcekitd-test -req=cursor -pos=92:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK40 +// RUN: %sourcekitd-test -req=cursor -pos=92:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK40 // CHECK40: source.lang.swift.decl.enumelement (92:8-92:10) // CHECK40-NEXT: C2 // CHECK40-NEXT: s:11cursor_info2E2O2C2yACSi_SStcACmF @@ -494,7 +495,7 @@ enum E7: String { // CHECK40: case C2(x: Int, y: String) // CHECK40-NEXT: case C2(x: Int, y: String) -// RUN: %sourcekitd-test -req=cursor -pos=92:31 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK41 +// RUN: %sourcekitd-test -req=cursor -pos=92:31 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK41 // CHECK41: source.lang.swift.decl.enumelement (92:31-92:33) // CHECK41-NEXT: C3 // CHECK41-NEXT: s:11cursor_info2E2O2C3yACSicACmF @@ -502,7 +503,7 @@ enum E7: String { // CHECK41: case C3(Int) // CHECK41-NEXT: case C3(Int) -// RUN: %sourcekitd-test -req=cursor -pos=96:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK42 +// RUN: %sourcekitd-test -req=cursor -pos=96:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK42 // CHECK42: source.lang.swift.decl.enumelement (96:8-96:9) // CHECK42-NEXT: C // CHECK42-NEXT: s:11cursor_info2E3O1CyA2CmF @@ -510,31 +511,31 @@ enum E7: String { // CHECK42: case C = "a" // CHECK42-NEXT: case C = "a" -// RUN: %sourcekitd-test -req=cursor -pos=100:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK43 +// RUN: %sourcekitd-test -req=cursor -pos=100:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK43 // CHECK43: source.lang.swift.ref.enumelement (91:8-91:10) // CHECK43-NEXT: C1 // CHECK43: case C1 // CHECK43-NEXT: case C1 -// RUN: %sourcekitd-test -req=cursor -pos=101:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK44 +// RUN: %sourcekitd-test -req=cursor -pos=101:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK44 // CHECK44: source.lang.swift.ref.enumelement (92:8-92:10) // CHECK44-NEXT: C2 // CHECK44: case C2(x: Int, y: String) // CHECK44-NEXT: case C2(x: case C2(x: Int, y: String) // CHECK45-NEXT: case C2(x -// RUN: %sourcekitd-test -req=cursor -pos=103:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK46 +// RUN: %sourcekitd-test -req=cursor -pos=103:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK46 // CHECK46: source.lang.swift.ref.enumelement (96:8-96:9) // CHECK46-NEXT: C // CHECK46: case C = "a" // CHECK46-NEXT: case C = "a" -// RUN: %sourcekitd-test -req=cursor -pos=80:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK47 +// RUN: %sourcekitd-test -req=cursor -pos=80:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK47 // CHECK47: source.lang.swift.decl.generic_type_param (80:11-80:12) // CHECK47-NEXT: T // CHECK47-NEXT: s:11cursor_info2S2V1Txmfp @@ -542,75 +543,75 @@ enum E7: String { // CHECK47: T // CHECK47-NEXT: T -// RUN: %sourcekitd-test -req=cursor -pos=107:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK48 +// RUN: %sourcekitd-test -req=cursor -pos=107:14 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK48 // CHECK48: source.lang.swift.decl.var.static (107:14-107:16) // CHECK48: static var -// RUN: %sourcekitd-test -req=cursor -pos=108:19 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK49 +// RUN: %sourcekitd-test -req=cursor -pos=108:19 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK49 // CHECK49: source.lang.swift.decl.var.class (108:19-108:21) // CHECK49: final class var -// RUN: %sourcekitd-test -req=cursor -pos=109:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK50 +// RUN: %sourcekitd-test -req=cursor -pos=109:15 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK50 // CHECK50: source.lang.swift.decl.function.method.static (109:15-109:19) // CHECK50: static func -// RUN: %sourcekitd-test -req=cursor -pos=110:20 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK51 +// RUN: %sourcekitd-test -req=cursor -pos=110:20 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK51 // CHECK51: source.lang.swift.decl.function.method.class (110:20-110:24) // CHECK51: final class func -// RUN: %sourcekitd-test -req=cursor -pos=117:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK52 +// RUN: %sourcekitd-test -req=cursor -pos=117:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK52 // CHECK52: source.lang.swift.decl.function.free (117:6-117:36) // CHECK52: (U, v: V) -> () // CHECK52: func genReq<U, V>(_ u: U, v: V) where U == V.T, V : P1 -// RUN: %sourcekitd-test -req=cursor -pos=117:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK53 +// RUN: %sourcekitd-test -req=cursor -pos=117:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK53 // CHECK53: source.lang.swift.decl.generic_type_param (117:16-117:17) // CHECK53: V : P1 -// RUN: %sourcekitd-test -req=cursor -pos=119:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK54 +// RUN: %sourcekitd-test -req=cursor -pos=119:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK54 // CHECK54: source.lang.swift.decl.class (119:13-119:15) // CHECK54: @objc class -// RUN: %sourcekitd-test -req=cursor -pos=122:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK55 +// RUN: %sourcekitd-test -req=cursor -pos=122:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK55 // CHECK55: source.lang.swift.decl.function.method.instance (122:8-122:12) // CHECK55: @objc(mmm1) func -// RUN: %sourcekitd-test -req=cursor -pos=126:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK56 +// RUN: %sourcekitd-test -req=cursor -pos=126:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK56 // CHECK56: source.lang.swift.decl.var.instance (126:7-126:9) // CHECK56: private(set) public var -// RUN: %sourcekitd-test -req=cursor -pos=129:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK57 +// RUN: %sourcekitd-test -req=cursor -pos=129:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK57 // CHECK57: source.lang.swift.decl.var.global (129:5-129:14) // CHECK57: let tupleVar1: (((Int, Int), y: Int), z: Int) -// RUN: %sourcekitd-test -req=cursor -pos=130:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK58 +// RUN: %sourcekitd-test -req=cursor -pos=130:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK58 // CHECK58: source.lang.swift.decl.var.global (130:5-130:14) // CHECK58: let tupleVar2: (f: () -> (), g: (_ x: Int) -> Int) -// RUN: %sourcekitd-test -req=cursor -pos=131:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK59 +// RUN: %sourcekitd-test -req=cursor -pos=131:5 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK59 // CHECK59: source.lang.swift.decl.var.global (131:5-131:14) // CHECK59: let tupleVar3: (f: (_ x: inout (Int, Int)) throws -> (), Int) -// RUN: %sourcekitd-test -req=cursor -pos=134:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK60 +// RUN: %sourcekitd-test -req=cursor -pos=134:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK60 // CHECK60: case A = -1 -// RUN: %sourcekitd-test -req=cursor -pos=135:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK61 +// RUN: %sourcekitd-test -req=cursor -pos=135:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK61 // CHECK61: case B = 0 -// RUN: %sourcekitd-test -req=cursor -pos=136:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK62 +// RUN: %sourcekitd-test -req=cursor -pos=136:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK62 // CHECK62: case C = 1 -// RUN: %sourcekitd-test -req=cursor -pos=142:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK63 +// RUN: %sourcekitd-test -req=cursor -pos=142:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK63 // CHECK63: case A = -0.0 -// RUN: %sourcekitd-test -req=cursor -pos=143:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK64 +// RUN: %sourcekitd-test -req=cursor -pos=143:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK64 // CHECK64: case B = 1e10 -// RUN: %sourcekitd-test -req=cursor -pos=146:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK65 +// RUN: %sourcekitd-test -req=cursor -pos=146:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK65 // CHECK65: class C6 : C4, P1 // FIXME: ref.class - rdar://problem/25014968 -// RUN: %sourcekitd-test -req=cursor -pos=150:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK66 +// RUN: %sourcekitd-test -req=cursor -pos=150:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK66 // CHECK66: protocol P2 : AnyObject, P1 -// RUN: %sourcekitd-test -req=cursor -pos=114:18 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK67 +// RUN: %sourcekitd-test -req=cursor -pos=114:18 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK67 // CHECK67: source.lang.swift.decl.associatedtype (114:18-114:19) // CHECK67-NEXT: T // CHECK67-NEXT: s:11cursor_info2P1P1T @@ -618,7 +619,7 @@ enum E7: String { // CHECK67: associatedtype T // CHECK67-NEXT: associatedtype T -// RUN: %sourcekitd-test -req=cursor -pos=152:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK68 +// RUN: %sourcekitd-test -req=cursor -pos=152:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK68 // CHECK68: source.lang.swift.decl.typealias (152:11-152:18) // CHECK68-NEXT: MyAlias // CHECK68-NEXT: s:11cursor_info7MyAliasa @@ -626,7 +627,7 @@ enum E7: String { // CHECK68: typealias MyAlias<T, U> = (T, U, T, U) // CHECK68-NEXT: typealias MyAlias<T, U> = (T, U, T, U) -// RUN: %sourcekitd-test -req=cursor -pos=153:28 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK69 +// RUN: %sourcekitd-test -req=cursor -pos=153:28 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK69 // CHECK69: source.lang.swift.ref.typealias (152:11-152:18) // CHECK69-NEXT: MyAlias // CHECK69-NEXT: s:11cursor_info7MyAliasa @@ -634,16 +635,16 @@ enum E7: String { // CHECK69: typealias MyAlias<T, U> = (T, U, T, U) // CHECK69-NEXT: typealias MyAlias<T, U> = (T, U, T, U) -// RUN: %sourcekitd-test -req=cursor -pos=155:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK70 +// RUN: %sourcekitd-test -req=cursor -pos=155:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK70 // CHECK70: func paramAutoclosureNoescape1(_ msg: () -> String) -// RUN: %sourcekitd-test -req=cursor -pos=156:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK71 +// RUN: %sourcekitd-test -req=cursor -pos=156:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK71 // CHECK71: func paramAutoclosureNoescape2(_ msg: @autoclosure () -> String) -// RUN: %sourcekitd-test -req=cursor -pos=157:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK72 +// RUN: %sourcekitd-test -req=cursor -pos=157:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK72 // CHECK72: func paramAutoclosureNoescape3(_ msg: @autoclosure @escaping () -> String) -// RUN: %sourcekitd-test -req=cursor -pos=159:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK73 +// RUN: %sourcekitd-test -req=cursor -pos=159:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK73 // CHECK73: // CHECK73-SAME: = #function // CHECK73-SAME: = #file @@ -655,108 +656,109 @@ enum E7: String { // CHECK73-SAME: = nil // CHECK73-SAME: = 1 -// RUN: %sourcekitd-test -req=cursor -pos=162:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK74 +// RUN: %sourcekitd-test -req=cursor -pos=162:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK74 // CHECK74: source.lang.swift.decl.function.method.instance (162:8-162:20) // CHECK74: (Self) -> (Self) -> Self // CHECK74: func f(_ s: Self) -> Self // CHECK74: Self // CHECK74-SAME: Self -// RUN: %sourcekitd-test -req=cursor -pos=165:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK75 +// RUN: %sourcekitd-test -req=cursor -pos=165:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK75 // CHECK75: source.lang.swift.decl.function.method.instance (165:8-165:20) // CHECK75: (Self) -> (Self) -> Self // CHECK75: func f(_ s: Self) -> Self // CHECK75: Self // CHECK75-SAME: Self -// RUN: %sourcekitd-test -req=cursor -pos=169:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK76 +// RUN: %sourcekitd-test -req=cursor -pos=169:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK76 // CHECK76: source.lang.swift.decl.function.method.instance (169:8-169:11) // CHECK76: (C7) -> () -> Self // CHECK76: func f() -> Self // CHECK76: Self -// RUN: %sourcekitd-test -req=cursor -pos=188:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK77 %s -// RUN: %sourcekitd-test -req=cursor -pos=189:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK78 %s +// RUN: %sourcekitd-test -req=cursor -pos=188:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK77 %s +// RUN: %sourcekitd-test -req=cursor -pos=189:7 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK78 %s // CHECK77: foo1 comment from P4 // CHECK78: foo2 comment from C1 -// RUN: %sourcekitd-test -req=cursor -pos=192:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK79 %s +// RUN: %sourcekitd-test -req=cursor -pos=192:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK79 %s // CHECK79: t: // CHECK79-SAME: ( // CHECK79-SAME: Int, // CHECK79-SAME: Int // CHECK79-SAME: ) -// RUN: %sourcekitd-test -req=cursor -pos=193:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK80 %s +// RUN: %sourcekitd-test -req=cursor -pos=193:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK80 %s // CHECK80: () -// RUN: %sourcekitd-test -req=cursor -pos=194:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK81 %s +// RUN: %sourcekitd-test -req=cursor -pos=194:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK81 %s // CHECK81: () -> () -// RUN: %sourcekitd-test -req=cursor -pos=195:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK82 %s +// RUN: %sourcekitd-test -req=cursor -pos=195:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK82 %s // CHECK82: (Int, Int) -// RUN: %sourcekitd-test -req=cursor -pos=196:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK83 %s +// RUN: %sourcekitd-test -req=cursor -pos=196:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK83 %s // CHECK83: () -> Void -// RUN: %sourcekitd-test -req=cursor -pos=197:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK84 %s +// RUN: %sourcekitd-test -req=cursor -pos=197:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK84 %s // CHECK84: typealias MyVoid = () -// RUN: %sourcekitd-test -req=cursor -pos=199:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK85 %s +// RUN: %sourcekitd-test -req=cursor -pos=199:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK85 %s // CHECK85-NOT: @rethrows // CHECK85: func rethrowingFunction1({{.*}}) rethrows // CHECK85-NOT: @rethrows // CHECK85: func rethrowingFunction1({{.*}}) rethrows // CHECK85-NOT: @rethrows -// RUN: %sourcekitd-test -req=cursor -pos=201:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=202:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=203:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=204:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=205:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=206:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// RUN: %sourcekitd-test -req=cursor -pos=207:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// CHECK86: @convention({{[a-z_]*}}) - -// RUN: %sourcekitd-test -req=cursor -pos=212:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK87 %s -// CHECK87: source.lang.swift.decl.struct (212:8-212:26) +// RUN: %sourcekitd-test -req=cursor -pos=201:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=202:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=203:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=204:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=205:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=206:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=207:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=208:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK86 %s +// CHECK86: @convention({{[a-z_]*((, [a-zA-Z_]*: "[^&]*")|(: [a-zA-Z0-9_]*))?}}) + +// RUN: %sourcekitd-test -req=cursor -pos=213:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK87 %s +// CHECK87: source.lang.swift.decl.struct (213:8-213:26) // CHECK87-NEXT: HasLocalizationKey // CHECK87-NEXT: s:11cursor_info18HasLocalizationKeyV // CHECK87-NEXT: HasLocalizationKey.Type // CHECK87-NEXT: $s // CHECK87-NEXT: struct HasLocalizationKey // CHECK87-NEXT: struct HasLocalizationKey -// CHECK87-NEXT: HasLocalizationKeys:11cursor_info18HasLocalizationKeyVstruct HasLocalizationKeyBrief. +// CHECK87-NEXT: HasLocalizationKeys:11cursor_info18HasLocalizationKeyVstruct HasLocalizationKeyBrief. // CHECK87-NEXT: ABC -// RUN: %sourcekitd-test -req=cursor -pos=215:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK88 %s -// CHECK88: source.lang.swift.decl.function.free (215:6-215:27) +// RUN: %sourcekitd-test -req=cursor -pos=216:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK88 %s +// CHECK88: source.lang.swift.decl.function.free (216:6-216:27) // CHECK88-NEXT: hasLocalizationKey2 // CHECK88-NEXT: s:11cursor_info19hasLocalizationKey2yyF // CHECK88-NEXT: () -> () // CHECK88-NEXT: $s // CHECK88-NEXT: func hasLocalizationKey2() // CHECK88-NEXT: func hasLocalizationKey2() -// CHECK88-NEXT: hasLocalizationKey2()s:11cursor_info19hasLocalizationKey2yyFfunc hasLocalizationKey2()hasLocalizationKey2()s:11cursor_info19hasLocalizationKey2yyFfunc hasLocalizationKey2()ABC -// RUN: %sourcekitd-test -req=cursor -pos=218:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK89 %s +// RUN: %sourcekitd-test -req=cursor -pos=219:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK89 %s // CHECK89: func funcWithNestedEscaping(a: (@escaping () -> ()) -> ()) // CHECK89-NEXT: (@escaping () -> ()) -> () -// RUN: %sourcekitd-test -req=cursor -pos=221:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK90 %s +// RUN: %sourcekitd-test -req=cursor -pos=222:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK90 %s // CHECK90: typealias typeWithNestedAutoclosure = (@autoclosure () -> ()) -> () // CHECK90-NEXT: @autoclosure () -> () -// RUN: %sourcekitd-test -req=cursor -pos=223:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK91 %s +// RUN: %sourcekitd-test -req=cursor -pos=224:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK91 %s // CHECK91: typealias GenericAlias<T, U> = MyAlias<T, U> where T : P1 // CHECK91-NEXT: typealias GenericAlias<T, U> = MyAlias<T, U> where T : P1 -// RUN: %sourcekitd-test -req=cursor -pos=226:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK92 %s +// RUN: %sourcekitd-test -req=cursor -pos=227:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK92 %s // CHECK92: case a = "\u{1B}" // CHECK92-NEXT: case a = "\u{1B}" -// RUN: %sourcekitd-test -req=cursor -pos=227:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK93 %s +// RUN: %sourcekitd-test -req=cursor -pos=228:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK93 %s // CHECK93: case b = "f" // CHECK93-NEXT: case b = "f" diff --git a/test/SourceKit/CursorInfo/cursor_info_async.swift b/test/SourceKit/CursorInfo/cursor_info_async.swift index 55e00fa0b04b7..064e8435646b2 100644 --- a/test/SourceKit/CursorInfo/cursor_info_async.swift +++ b/test/SourceKit/CursorInfo/cursor_info_async.swift @@ -9,7 +9,7 @@ import Foo // contention. We disable printing the requests to minimize delay. // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- \ -// RUN: -F %S/../Inputs/libIDE-mock-sdk %mcp_opt \ +// RUN: -F %S/../Inputs/libIDE-mock-sdk \ // RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -async -dont-print-request -req=cursor -pos=60:15 \ // RUN: == -async -dont-print-request -req=cursor -pos=60:15 \ diff --git a/test/SourceKit/CursorInfo/cursor_invalid.swift b/test/SourceKit/CursorInfo/cursor_invalid.swift index 5e02283858899..07161a4efa2ec 100644 --- a/test/SourceKit/CursorInfo/cursor_invalid.swift +++ b/test/SourceKit/CursorInfo/cursor_invalid.swift @@ -22,6 +22,12 @@ func ==(x: C, y: C) func resyncParser2() {} +Swift(label: 3) + +enum Outer { + case Inner(IDontExist) +} + // RUN: %sourcekitd-test -req=cursor -pos=4:13 %s -- %s | %FileCheck -check-prefix=CHECK1 %s // CHECK1: source.lang.swift.decl.var.local (4:13-4:14) // CHECK1: c @@ -59,3 +65,9 @@ func resyncParser2() {} // RUN: %sourcekitd-test -req=cursor -pos=21:6 %s -- %s | %FileCheck -check-prefix=EQEQ3 %s // EQEQ3: func == (x: C, y: C) + +// RUN: %sourcekitd-test -req=cursor -pos=25:7 %s -- %s | %FileCheck -check-prefix=DIAG %s + +// RUN: %sourcekitd-test -req=cursor -pos=28:8 %s -- %s | %FileCheck -check-prefix=INVALID_ENUM %s +// INVALID_ENUM: source.lang.swift.decl.enumelement (28:8-28:13) +// INVALID_ENUM: Inner(_:) diff --git a/test/SourceKit/CursorInfo/cursor_overrides.swift b/test/SourceKit/CursorInfo/cursor_overrides.swift index 666dd7dab5664..1a6cdd88e1ef6 100644 --- a/test/SourceKit/CursorInfo/cursor_overrides.swift +++ b/test/SourceKit/CursorInfo/cursor_overrides.swift @@ -26,7 +26,7 @@ public protocol WithInheritedAssocType : WithAssocType { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=cursor -pos=16:7 %s -- -embed-bitcode -I %S/Inputs/cursor-overrides %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=cursor -pos=16:7 %s -- -embed-bitcode -I %S/Inputs/cursor-overrides %s | %FileCheck -check-prefix=CHECK1 %s // CHECK1: source.lang.swift.ref.function.method.instance (12:17-12:23) // CHECK1: c:@M@cursor_overrides@objc(cs)SubCls(im)meth // CHECK1: (SubCls) -> () -> () @@ -38,7 +38,7 @@ public protocol WithInheritedAssocType : WithAssocType { // CHECK1-NEXT: c:objc(pl)P1(im)meth // CHECK1-NEXT: OVERRIDES END -// RUN: %sourcekitd-test -req=cursor -pos=25:20 %s -- -embed-bitcode -I %S/Inputs/cursor-overrides %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=cursor -pos=25:20 %s -- -embed-bitcode -I %S/Inputs/cursor-overrides %s | %FileCheck -check-prefix=CHECK2 %s // CHECK2: s:16cursor_overrides22WithInheritedAssocTypeP0eF0 // CHECK2: OVERRIDES BEGIN // CHECK2-NEXT: s:16cursor_overrides13WithAssocTypeP0dE0 diff --git a/test/SourceKit/CursorInfo/cursor_rename.swift b/test/SourceKit/CursorInfo/cursor_rename.swift index 32344a0734ec4..9dc350a2f42f9 100644 --- a/test/SourceKit/CursorInfo/cursor_rename.swift +++ b/test/SourceKit/CursorInfo/cursor_rename.swift @@ -18,4 +18,4 @@ _ = Foo.init(2) // CHECK1: Rename // CHECK2-NOT: Rename -// REQUIRES-ANY: OS=macosx, OS=linux-gnu \ No newline at end of file +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/CursorInfo/cursor_stdlib.swift b/test/SourceKit/CursorInfo/cursor_stdlib.swift index fafb0567b7992..4e705b659bcb8 100644 --- a/test/SourceKit/CursorInfo/cursor_stdlib.swift +++ b/test/SourceKit/CursorInfo/cursor_stdlib.swift @@ -26,7 +26,7 @@ func foo3(a: Float, b: Bool) {} // RUN: %empty-directory(%t) // RUN: %build-clang-importer-objc-overlays -// RUN: %sourcekitd-test -req=cursor -pos=3:18 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-OVERLAY %s +// RUN: %sourcekitd-test -req=cursor -pos=3:18 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-OVERLAY %s // CHECK-OVERLAY: source.lang.swift.ref.var.global // CHECK-OVERLAY-NEXT: NSUTF8StringEncoding // CHECK-OVERLAY-NEXT: s:10Foundation20NSUTF8StringEncodingSuv @@ -36,39 +36,39 @@ func foo3(a: Float, b: Bool) {} // CHECK-OVERLAY-NEXT: SYSTEM // CHECK-OVERLAY-NEXT: let NSUTF8StringEncoding: UInt -// RUN: %sourcekitd-test -req=cursor -pos=5:13 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-ITERATOR %s +// RUN: %sourcekitd-test -req=cursor -pos=5:13 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-ITERATOR %s // CHECK-ITERATOR-NOT: _AnyIteratorBase // CHECK-ITERATOR: Collection/Type-erased -// RUN: %sourcekitd-test -req=cursor -pos=8:10 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT1 %s +// RUN: %sourcekitd-test -req=cursor -pos=8:10 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT1 %s // CHECK-REPLACEMENT1: Collection/Array // CHECK-REPLACEMENT1: {{.*}}func sorted() -> [Int] // CHECK-REPLACEMENT1: RELATED BEGIN // CHECK-REPLACEMENT1: sorted(by:) // CHECK-REPLACEMENT1: RELATED END -// RUN: %sourcekitd-test -req=cursor -pos=9:8 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT2 %s +// RUN: %sourcekitd-test -req=cursor -pos=9:8 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT2 %s // CHECK-REPLACEMENT2: Collection/Array // CHECK-REPLACEMENT2: {{.*}}mutating func append(_ newElement: Int) -// RUN: %sourcekitd-test -req=cursor -pos=15:10 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT3 %s +// RUN: %sourcekitd-test -req=cursor -pos=15:10 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT3 %s // CHECK-REPLACEMENT3: Collection/Array // CHECK-REPLACEMENT3: func sorted(by areInIncreasingOrder: (S1 // CHECK-REPLACEMENT3: sorted() -// RUN: %sourcekitd-test -req=cursor -pos=18:8 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT4 %s +// RUN: %sourcekitd-test -req=cursor -pos=18:8 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-REPLACEMENT4 %s // CHECK-REPLACEMENT4: Collection/Array // CHECK-REPLACEMENT4: {{.*}}mutating func append(_ newElement: S1) -// RUN: %sourcekitd-test -req=cursor -pos=21:10 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-MODULE-GROUP1 %s +// RUN: %sourcekitd-test -req=cursor -pos=21:10 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-MODULE-GROUP1 %s // CHECK-MODULE-GROUP1: MODULE GROUPS BEGIN // CHECK-MODULE-GROUP1-DAG: Math // CHECK-MODULE-GROUP1-DAG: Collection // CHECK-MODULE-GROUP1-DAG: Collection/Array // CHECK-MODULE-GROUP1: MODULE GROUPS END -// RUN: %sourcekitd-test -req=cursor -pos=22:17 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-FLOAT1 %s +// RUN: %sourcekitd-test -req=cursor -pos=22:17 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-FLOAT1 %s // CHECK-FLOAT1: s:Sf -// RUN: %sourcekitd-test -req=cursor -pos=22:25 %s -- %s %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-BOOL1 %s +// RUN: %sourcekitd-test -req=cursor -pos=22:25 %s -- %s -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-BOOL1 %s // CHECK-BOOL1: s:Sb diff --git a/test/SourceKit/CursorInfo/cursor_usr.swift b/test/SourceKit/CursorInfo/cursor_usr.swift index 47c110a49d719..cf04ca98c1369 100644 --- a/test/SourceKit/CursorInfo/cursor_usr.swift +++ b/test/SourceKit/CursorInfo/cursor_usr.swift @@ -11,8 +11,8 @@ func foo(x: FooStruct1) -> S1 {} // RUN: %swiftc_driver -emit-module -o %t/FooSwiftModule.swiftmodule %S/Inputs/FooSwiftModule.swift // Sanity check that we have identical responses when things work. -// RUN: %sourcekitd-test -req=cursor -pos=5:8 %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s > %t.from_offset.txt -// RUN: %sourcekitd-test -req=cursor -usr "s:10cursor_usr2S1V" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s > %t.from_usr.txt +// RUN: %sourcekitd-test -req=cursor -pos=5:8 %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s > %t.from_offset.txt +// RUN: %sourcekitd-test -req=cursor -usr "s:10cursor_usr2S1V" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s > %t.from_usr.txt // RUN: %FileCheck %s -check-prefix=CHECK_SANITY1 < %t.from_offset.txt // RUN: %FileCheck %s -check-prefix=CHECK_SANITY1 < %t.from_usr.txt // RUN: diff -u %t.from_usr.txt %t.from_offset.txt @@ -25,16 +25,16 @@ func foo(x: FooStruct1) -> S1 {} // CHECK_SANITY1-NEXT: struct S1 // Bogus USR. -// RUN: %sourcekitd-test -req=cursor -usr "s:blahblahblah" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s | %FileCheck %s -check-prefix=RESOLVE +// RUN: %sourcekitd-test -req=cursor -usr "s:blahblahblah" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s -check-prefix=RESOLVE // Missing s: prefix. -// RUN: %sourcekitd-test -req=cursor -usr "10cursor_usr6globalSivp" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s | %FileCheck %s -check-prefix=RESOLVE +// RUN: %sourcekitd-test -req=cursor -usr "10cursor_usr6globalSivp" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s -check-prefix=RESOLVE // RESOLVE: // FIXME: no support for clang USRs. -// RUN: %sourcekitd-test -req=cursor -usr "c:@S@FooStruct1" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s | %FileCheck %s -check-prefix=CSUPPORT +// RUN: %sourcekitd-test -req=cursor -usr "c:@S@FooStruct1" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s -check-prefix=CSUPPORT // CSUPPORT: -// RUN: %sourcekitd-test -req=cursor -usr "s:10cursor_usr2S1V" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %mcp_opt %s | %FileCheck %s -check-prefix=CHECK1 +// RUN: %sourcekitd-test -req=cursor -usr "s:10cursor_usr2S1V" %s -- -I %t -F %S/../Inputs/libIDE-mock-sdk %s | %FileCheck %s -check-prefix=CHECK1 // CHECK1: source.lang.swift.decl.struct (5:8-5:10) // CHECK1: s1 // CHECK1: struct S1 diff --git a/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift b/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift index 2e348abd3e76d..9559a7afaf7a7 100644 --- a/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift +++ b/test/SourceKit/CursorInfo/cursor_vardecl_across_fallthrough.swift @@ -19,15 +19,15 @@ switch p { print("other") } -// RUN: %sourcekitd-test -req=cursor -pos=13:19 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKX,CHECK1DECL %s -// RUN: %sourcekitd-test -req=cursor -pos=14:18 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKX,CHECK1REF %s +// RUN: %sourcekitd-test -req=cursor -pos=13:19 %s -- %s | %FileCheck -check-prefixes=CHECKX,CHECK1DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=14:18 %s -- %s | %FileCheck -check-prefixes=CHECKX,CHECK1REF %s // CHECK1DECL: source.lang.swift.decl.var.local (13:19-13:20) // CHECK1REF: source.lang.swift.ref.var.local (13:19-13:20) -// RUN: %sourcekitd-test -req=cursor -pos=16:20 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKX,CHECK2DECL %s -// RUN: %sourcekitd-test -req=cursor -pos=16:42 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKX,CHECK2DECL2 %s -// RUN: %sourcekitd-test -req=cursor -pos=17:18 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKX,CHECK2REF %s +// RUN: %sourcekitd-test -req=cursor -pos=16:20 %s -- %s | %FileCheck -check-prefixes=CHECKX,CHECK2DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=16:42 %s -- %s | %FileCheck -check-prefixes=CHECKX,CHECK2DECL2 %s +// RUN: %sourcekitd-test -req=cursor -pos=17:18 %s -- %s | %FileCheck -check-prefixes=CHECKX,CHECK2REF %s // CHECK2DECL: source.lang.swift.decl.var.local (16:20-16:21) // CHECK2DECL2: source.lang.swift.decl.var.local (16:42-16:43) @@ -38,15 +38,15 @@ switch p { // CHECKX: Int -// RUN: %sourcekitd-test -req=cursor -pos=13:26 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKY,CHECK3DECL %s -// RUN: %sourcekitd-test -req=cursor -pos=14:23 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKY,CHECK3REF %s +// RUN: %sourcekitd-test -req=cursor -pos=13:26 %s -- %s | %FileCheck -check-prefixes=CHECKY,CHECK3DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=14:23 %s -- %s | %FileCheck -check-prefixes=CHECKY,CHECK3REF %s // CHECK3DECL: source.lang.swift.decl.var.local (13:26-13:27) // CHECK3REF: source.lang.swift.ref.var.local (13:26-13:27) -// RUN: %sourcekitd-test -req=cursor -pos=16:27 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKY,CHECK4DECL %s -// RUN: %sourcekitd-test -req=cursor -pos=16:49 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKY,CHECK4DECL2 %s -// RUN: %sourcekitd-test -req=cursor -pos=17:23 %s -- %mcp_opt %s | %FileCheck -check-prefixes=CHECKY,CHECK4REF %s +// RUN: %sourcekitd-test -req=cursor -pos=16:27 %s -- %s | %FileCheck -check-prefixes=CHECKY,CHECK4DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=16:49 %s -- %s | %FileCheck -check-prefixes=CHECKY,CHECK4DECL2 %s +// RUN: %sourcekitd-test -req=cursor -pos=17:23 %s -- %s | %FileCheck -check-prefixes=CHECKY,CHECK4REF %s // CHECK4DECL: source.lang.swift.decl.var.local (16:27-16:28) // CHECK4DECL2: source.lang.swift.decl.var.local (16:49-16:50) diff --git a/test/SourceKit/CursorInfo/injected_vfs.swift b/test/SourceKit/CursorInfo/injected_vfs.swift index fd9348ee46082..44e3a461c9c50 100644 --- a/test/SourceKit/CursorInfo/injected_vfs.swift +++ b/test/SourceKit/CursorInfo/injected_vfs.swift @@ -10,7 +10,7 @@ func foo( // CHECK-CMODULE: key.kind: source.lang.swift.ref.struct // CHECK-CMODULE: key.name: "StructDefinedInCModule" -// CHECK-CMODULE: key.filepath: "/CModule/CModule.h" +// CHECK-CMODULE: key.filepath: "{{.*}}/CModule{{/|\\\\}}CModule.h" // CHECK-SWIFTMODULE-REF: key.kind: source.lang.swift.ref.struct // CHECK-SWIFTMODULE-REF: key.name: "StructDefinedInSwiftModule" @@ -32,28 +32,28 @@ func foo( // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift // == CursorInfo works for struct defined in CModule == -// RUN: %sourcekitd-test -req=cursor -pos=5:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: %sourcekitd-test -req=cursor -pos=5:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // USR test intentionally omitted for CModule, because SourceKit does not support clang USRs. // == CursorInfo works for struct defined in SwiftModule == -// RUN: %sourcekitd-test -req=cursor -pos=6:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-REF %s -// RUN: %sourcekitd-test -req=cursor -usr "s:11SwiftModule015StructDefinedInaB0V" -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=6:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-REF %s +// RUN: %sourcekitd-test -req=cursor -usr "s:11SwiftModule015StructDefinedInaB0V" -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SWIFTMODULE-DECL %s // == CursorInfo works for struct defined in same target as primary file == -// RUN: %sourcekitd-test -req=cursor -pos=7:43 -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-REF %s -// RUN: %sourcekitd-test -req=cursor -usr "s:4main25StructDefinedInSameTargetV" -print-raw-response -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-DECL %s +// RUN: %sourcekitd-test -req=cursor -pos=7:43 -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-REF %s +// RUN: %sourcekitd-test -req=cursor -usr "s:4main25StructDefinedInSameTargetV" -print-raw-response -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-SAMETARGET-DECL %s // == Using an open document == // RUN: %sourcekitd-test \ -// RUN: -req=open -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=open -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // == Using an open document without semantic info == // RUN: %sourcekitd-test \ -// RUN: -req=syntax-map -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=syntax-map -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %s -pass-as-sourcetext == \ +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s // == Overriding an open document == // RUN: %sourcekitd-test \ // RUN: -req=syntax-map %s -pass-as-sourcetext == \ -// RUN: -req=cursor -pos=5:43 %s -print-raw-response -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule -- %s /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s +// RUN: -req=cursor -pos=5:43 %s -print-raw-response -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule -- %s %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple | %FileCheck --check-prefix=CHECK-CMODULE %s diff --git a/test/SourceKit/CursorInfo/rdar_18677108-2.swift b/test/SourceKit/CursorInfo/rdar_18677108-2.swift index 8db5511482538..2fa731fc163cf 100644 --- a/test/SourceKit/CursorInfo/rdar_18677108-2.swift +++ b/test/SourceKit/CursorInfo/rdar_18677108-2.swift @@ -2,7 +2,7 @@ // RUN: -- %S/Inputs/rdar_18677108-2-b.swift \ // RUN: %S/Inputs/rdar_18677108-2-a.swift \ // RUN: == -req=print-diags %S/Inputs/rdar_18677108-2-a.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/CursorInfo/use-swift-source-info.swift b/test/SourceKit/CursorInfo/use-swift-source-info.swift new file mode 100644 index 0000000000000..8b50f38ccb200 --- /dev/null +++ b/test/SourceKit/CursorInfo/use-swift-source-info.swift @@ -0,0 +1,29 @@ +import Foo +func bar() { + foo() +} + +// RUN: %empty-directory(%t) +// RUN: echo "/// Some doc" >> %t/Foo.swift +// RUN: echo "public func foo() { }" >> %t/Foo.swift +// RUN: %target-swift-frontend -enable-batch-mode -emit-module -emit-module-doc -emit-module-path %t/Foo.swiftmodule %t/Foo.swift -module-name Foo -emit-module-source-info-path %t/Foo.swiftsourceinfo -emit-module-doc-path %t/Foo.swiftdoc +// +// Test setting optimize for ide to false +// RUN: %sourcekitd-test -req=global-config -for-ide=0 == -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITH %s +// +// Test setting optimize for ide to true +// RUN: %sourcekitd-test -req=global-config -for-ide=1 == -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITHOUT %s +// +// Test sourcekitd-test's default global configuration request (optimize for ide is true) +// RUN: %sourcekitd-test -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITHOUT %s +// +// Test without sending any global configuration request to check the sevice's default settings (optimize for ide is false) +// RUN: %sourcekitd-test -suppress-config-request -req=cursor -pos=3:3 %s -- -I %t -target %target-triple %s | %FileCheck --check-prefixes=BOTH,WITH %s + +// WITH: source.lang.swift.ref.function.free ({{.*}}/Foo.swift:2:13-2:16) +// WITHOUT: source.lang.swift.ref.function.free () +// BOTH: foo() +// BOTH: s:3Foo3fooyyF +// BOTH: () -> () +// BOTH: $syycD +// BOTH: Foo diff --git a/test/SourceKit/DocSupport/doc_clang_module.swift b/test/SourceKit/DocSupport/doc_clang_module.swift index d40aac8ac4a02..651d6850936bb 100644 --- a/test/SourceKit/DocSupport/doc_clang_module.swift +++ b/test/SourceKit/DocSupport/doc_clang_module.swift @@ -1,11 +1,11 @@ // REQUIRES: objc_interop // FIXME: the test output we're comparing to is specific to macOS. -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx // RUN: %empty-directory(%t) // RUN: %build-clang-importer-objc-overlays // RUN: %sourcekitd-test -req=doc-info -module Foo -- -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response // RUN: diff -u %s.response %t.response diff --git a/test/SourceKit/DocSupport/doc_clang_module.swift.response b/test/SourceKit/DocSupport/doc_clang_module.swift.response index e60ad655df59d..25f3852dae242 100644 --- a/test/SourceKit/DocSupport/doc_clang_module.swift.response +++ b/test/SourceKit/DocSupport/doc_clang_module.swift.response @@ -38,11 +38,11 @@ var FooEnum3X: FooEnum3 { get } var FooEnum3Y: FooEnum3 { get } enum FooComparisonResult : Int { - case orderedAscending + case orderedAscending = -1 - case orderedSame + case orderedSame = 0 - case orderedDescending + case orderedDescending = 1 @inlinable var hashValue: Int { get } @@ -337,9 +337,9 @@ class FooCFType { func FooCFTypeRelease(_ _: FooCFType!) enum ABAuthorizationStatus : Int { - case notDetermined + case notDetermined = 0 - case restricted + case restricted = 1 @inlinable var hashValue: Int { get } @@ -933,3835 +933,3860 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.offset: 857, key.length: 16 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 876, + key.length: 2 + }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 879, + key.offset: 884, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 884, + key.offset: 889, key.length: 11 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 903, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 901, + key.offset: 910, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 906, + key.offset: 915, key.length: 17 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 935, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 929, + key.offset: 942, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 940, + key.offset: 953, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 944, + key.offset: 957, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 955, + key.offset: 968, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 961, + key.offset: 974, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 972, + key.offset: 985, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 983, + key.offset: 996, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 988, + key.offset: 1001, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 993, + key.offset: 1006, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 998, + key.offset: 1011, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1006, + key.offset: 1019, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Hasher", key.usr: "s:s6HasherV", - key.offset: 1012, + key.offset: 1025, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1025, + key.offset: 1038, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1032, + key.offset: 1045, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1041, + key.offset: 1054, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1043, + key.offset: 1056, key.length: 3 }, { key.kind: source.lang.swift.ref.enum, key.name: "FooComparisonResult", key.usr: "c:@E@FooComparisonResult", - key.offset: 1048, + key.offset: 1061, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1069, + key.offset: 1082, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1071, + key.offset: 1084, key.length: 3 }, { key.kind: source.lang.swift.ref.enum, key.name: "FooComparisonResult", key.usr: "c:@E@FooComparisonResult", - key.offset: 1076, + key.offset: 1089, key.length: 19 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1100, + key.offset: 1113, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1107, + key.offset: 1120, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1114, + key.offset: 1127, key.length: 17 }, { key.kind: source.lang.swift.ref.protocol, key.name: "OptionSet", key.usr: "s:s9OptionSetP", - key.offset: 1134, + key.offset: 1147, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1151, + key.offset: 1164, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1156, + key.offset: 1169, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1165, + key.offset: 1178, key.length: 8 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 1175, + key.offset: 1188, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1185, + key.offset: 1198, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1192, + key.offset: 1205, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1196, + key.offset: 1209, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1209, + key.offset: 1222, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1229, + key.offset: 1242, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1240, + key.offset: 1253, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1247, + key.offset: 1260, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1251, + key.offset: 1264, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1265, + key.offset: 1278, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1285, + key.offset: 1298, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1296, + key.offset: 1309, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1303, + key.offset: 1316, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1312, + key.offset: 1325, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1314, + key.offset: 1327, key.length: 3 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1319, + key.offset: 1332, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1338, + key.offset: 1351, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1340, + key.offset: 1353, key.length: 3 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1345, + key.offset: 1358, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1367, + key.offset: 1380, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1377, + key.offset: 1390, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1388, + key.offset: 1401, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1393, + key.offset: 1406, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1406, + key.offset: 1419, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1420, + key.offset: 1433, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1445, + key.offset: 1458, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1455, + key.offset: 1468, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1480, + key.offset: 1493, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1491, + key.offset: 1504, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1496, + key.offset: 1509, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1499, + key.offset: 1512, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1501, + key.offset: 1514, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1511, + key.offset: 1524, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1514, + key.offset: 1527, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1520, + key.offset: 1533, key.length: 1 }, { key.kind: source.lang.swift.ref.protocol, key.name: "Sequence", key.usr: "s:ST", - key.offset: 1524, + key.offset: 1537, key.length: 8 }, { key.kind: source.lang.swift.ref.generic_type_param, key.name: "Self", key.usr: "s:s10SetAlgebraP4Selfxmfp", - key.offset: 1534, + key.offset: 1547, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1539, + key.offset: 1552, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1550, + key.offset: 1563, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1552, + key.offset: 1565, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1565, + key.offset: 1578, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1576, + key.offset: 1589, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1585, + key.offset: 1598, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1590, + key.offset: 1603, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1599, + key.offset: 1612, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1601, + key.offset: 1614, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1608, + key.offset: 1621, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1632, + key.offset: 1645, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1643, + key.offset: 1656, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1648, + key.offset: 1661, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1657, + key.offset: 1670, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1660, + key.offset: 1673, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1667, + key.offset: 1680, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1689, + key.offset: 1702, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1699, + key.offset: 1712, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1710, + key.offset: 1723, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1715, + key.offset: 1728, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1726, + key.offset: 1739, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1729, + key.offset: 1742, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1736, + key.offset: 1749, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1758, + key.offset: 1771, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1768, + key.offset: 1781, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1779, + key.offset: 1792, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1784, + key.offset: 1797, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1795, + key.offset: 1808, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1800, + key.offset: 1813, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1807, + key.offset: 1820, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1829, + key.offset: 1842, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1839, + key.offset: 1852, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1850, + key.offset: 1863, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1855, + key.offset: 1868, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1867, + key.offset: 1880, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1869, + key.offset: 1882, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1876, + key.offset: 1889, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 1898, + key.offset: 1911, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1921, + key.offset: 1934, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1932, + key.offset: 1945, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1936, + key.offset: 1949, key.length: 7 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 1945, + key.offset: 1958, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1952, + key.offset: 1965, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1963, + key.offset: 1976, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1974, + key.offset: 1987, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1979, + key.offset: 1992, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 1996, + key.offset: 2009, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 1999, + key.offset: 2012, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2006, + key.offset: 2019, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 2028, + key.offset: 2041, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2038, + key.offset: 2051, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2049, + key.offset: 2062, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2054, + key.offset: 2067, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2069, + key.offset: 2082, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2072, + key.offset: 2085, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2079, + key.offset: 2092, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 2101, + key.offset: 2114, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2109, + key.offset: 2122, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2119, + key.offset: 2132, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2144, + key.offset: 2157, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2155, + key.offset: 2168, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2160, + key.offset: 2173, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2166, + key.offset: 2179, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2168, + key.offset: 2181, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2175, + key.offset: 2188, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2197, + key.offset: 2210, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2220, + key.offset: 2233, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2231, + key.offset: 2244, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2236, + key.offset: 2249, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2249, + key.offset: 2262, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2251, + key.offset: 2264, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2258, + key.offset: 2271, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2280, + key.offset: 2293, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2303, + key.offset: 2316, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2314, + key.offset: 2327, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2319, + key.offset: 2332, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2339, + key.offset: 2352, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2341, + key.offset: 2354, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2348, + key.offset: 2361, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2370, + key.offset: 2383, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2391, + key.offset: 2404, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2401, + key.offset: 2414, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2426, + key.offset: 2439, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2437, + key.offset: 2450, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2442, + key.offset: 2455, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2451, + key.offset: 2464, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2453, + key.offset: 2466, key.length: 6 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2461, + key.offset: 2474, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 2483, + key.offset: 2496, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2493, + key.offset: 2506, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2504, + key.offset: 2517, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2513, + key.offset: 2526, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2518, + key.offset: 2531, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2525, + key.offset: 2538, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2527, + key.offset: 2540, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2538, + key.offset: 2551, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2561, + key.offset: 2574, key.length: 8 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 2571, + key.offset: 2584, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2577, + key.offset: 2590, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2596, + key.offset: 2609, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2620, + key.offset: 2633, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2631, + key.offset: 2644, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2640, + key.offset: 2653, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2645, + key.offset: 2658, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2652, + key.offset: 2665, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2654, + key.offset: 2667, key.length: 6 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2662, + key.offset: 2675, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2684, + key.offset: 2697, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2708, + key.offset: 2721, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2719, + key.offset: 2732, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2728, + key.offset: 2741, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2733, + key.offset: 2746, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2740, + key.offset: 2753, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2745, + key.offset: 2758, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2756, + key.offset: 2769, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2778, + key.offset: 2791, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2800, + key.offset: 2813, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2810, + key.offset: 2823, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2835, + key.offset: 2848, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2846, + key.offset: 2859, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2858, + key.offset: 2871, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2869, + key.offset: 2882, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2878, + key.offset: 2891, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2883, + key.offset: 2896, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2893, + key.offset: 2906, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2895, + key.offset: 2908, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2902, + key.offset: 2915, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2926, + key.offset: 2939, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2937, + key.offset: 2950, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2946, + key.offset: 2959, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2951, + key.offset: 2964, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 2968, + key.offset: 2981, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 2970, + key.offset: 2983, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 2977, + key.offset: 2990, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3001, + key.offset: 3014, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3012, + key.offset: 3025, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3021, + key.offset: 3034, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3026, + key.offset: 3039, key.length: 23 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3050, + key.offset: 3063, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3052, + key.offset: 3065, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", - key.offset: 3059, + key.offset: 3072, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3080, + key.offset: 3093, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3087, + key.offset: 3100, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3105, + key.offset: 3118, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3109, + key.offset: 3122, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3112, + key.offset: 3125, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3123, + key.offset: 3136, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3127, + key.offset: 3140, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3130, + key.offset: 3143, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3142, + key.offset: 3155, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3154, + key.offset: 3167, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3159, + key.offset: 3172, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3161, + key.offset: 3174, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3164, + key.offset: 3177, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3171, + key.offset: 3184, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3173, + key.offset: 3186, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3176, + key.offset: 3189, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3186, + key.offset: 3199, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3196, + key.offset: 3209, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "UnsafeMutablePointer", key.usr: "s:Sp", - key.offset: 3216, + key.offset: 3229, key.length: 20 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooStruct1", key.usr: "c:@S@FooStruct1", - key.offset: 3237, + key.offset: 3250, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3249, + key.offset: 3262, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3256, + key.offset: 3269, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3274, + key.offset: 3287, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3278, + key.offset: 3291, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3281, + key.offset: 3294, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3292, + key.offset: 3305, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3296, + key.offset: 3309, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3299, + key.offset: 3312, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3311, + key.offset: 3324, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3323, + key.offset: 3336, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3328, + key.offset: 3341, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3330, + key.offset: 3343, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3333, + key.offset: 3346, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3340, + key.offset: 3353, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3342, + key.offset: 3355, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3345, + key.offset: 3358, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3355, + key.offset: 3368, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3365, + key.offset: 3378, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooStruct2", key.usr: "c:@S@FooStruct2", - key.offset: 3385, + key.offset: 3398, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3396, + key.offset: 3409, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3403, + key.offset: 3416, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3428, + key.offset: 3441, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3432, + key.offset: 3445, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3435, + key.offset: 3448, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3446, + key.offset: 3459, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3450, + key.offset: 3463, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3453, + key.offset: 3466, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3465, + key.offset: 3478, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3477, + key.offset: 3490, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3482, + key.offset: 3495, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3484, + key.offset: 3497, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3487, + key.offset: 3500, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3494, + key.offset: 3507, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3496, + key.offset: 3509, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3499, + key.offset: 3512, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3509, + key.offset: 3522, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3519, + key.offset: 3532, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3533, + key.offset: 3546, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3539, + key.offset: 3552, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3543, + key.offset: 3556, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3554, + key.offset: 3567, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3560, + key.offset: 3573, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3565, + key.offset: 3578, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3574, + key.offset: 3587, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3576, + key.offset: 3589, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3579, + key.offset: 3592, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3589, + key.offset: 3602, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3595, + key.offset: 3608, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3600, + key.offset: 3613, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3623, + key.offset: 3636, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3625, + key.offset: 3638, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3628, + key.offset: 3641, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3638, + key.offset: 3651, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3644, + key.offset: 3657, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3649, + key.offset: 3662, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3658, + key.offset: 3671, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3660, + key.offset: 3673, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3663, + key.offset: 3676, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3670, + key.offset: 3683, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3672, + key.offset: 3685, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Float", key.usr: "s:Sf", - key.offset: 3675, + key.offset: 3688, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3682, + key.offset: 3695, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3684, + key.offset: 3697, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Double", key.usr: "s:Sd", - key.offset: 3687, + key.offset: 3700, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3695, + key.offset: 3708, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3697, + key.offset: 3710, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "UnsafeMutablePointer", key.usr: "s:Sp", - key.offset: 3700, + key.offset: 3713, key.length: 20 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3721, + key.offset: 3734, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3733, + key.offset: 3746, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3739, + key.offset: 3752, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3744, + key.offset: 3757, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3761, + key.offset: 3774, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3763, + key.offset: 3776, key.length: 3 }, { key.kind: source.lang.swift.ref.struct, key.name: "Float", key.usr: "s:Sf", - key.offset: 3770, + key.offset: 3783, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3780, + key.offset: 3793, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3789, + key.offset: 3802, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3794, + key.offset: 3807, key.length: 26 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 3821, + key.offset: 3834, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 3823, + key.offset: 3836, key.length: 4 }, { key.kind: source.lang.swift.ref.struct, key.name: "Float", key.usr: "s:Sf", - key.offset: 3831, + key.offset: 3844, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 3841, + key.offset: 3854, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3850, + key.offset: 3863, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3855, + key.offset: 3868, key.length: 16 }, { key.kind: source.lang.swift.ref.enum, key.name: "Never", key.usr: "s:s5NeverO", - key.offset: 3877, + key.offset: 3890, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3883, + key.offset: 3896, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3888, + key.offset: 3901, key.length: 16 }, { key.kind: source.lang.swift.ref.enum, key.name: "Never", key.usr: "s:s5NeverO", - key.offset: 3910, + key.offset: 3923, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3916, + key.offset: 3929, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3921, + key.offset: 3934, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3943, + key.offset: 3956, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3948, + key.offset: 3961, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3970, + key.offset: 3983, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3975, + key.offset: 3988, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3997, + key.offset: 4010, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4002, + key.offset: 4015, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4024, + key.offset: 4037, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4029, + key.offset: 4042, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4051, + key.offset: 4064, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4056, + key.offset: 4069, key.length: 32 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 4089, + key.offset: 4102, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 4091, + key.offset: 4104, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4094, + key.offset: 4107, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4104, + key.offset: 4117, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4110, + key.offset: 4123, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4119, + key.offset: 4132, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4142, + key.offset: 4155, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4147, + key.offset: 4160, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4167, + key.offset: 4180, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4172, + key.offset: 4185, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4213, + key.offset: 4226, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4218, + key.offset: 4231, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4259, + key.offset: 4272, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4266, + key.offset: 4279, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4271, + key.offset: 4284, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4296, + key.offset: 4309, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4300, + key.offset: 4313, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4314, + key.offset: 4327, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4322, + key.offset: 4335, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4326, + key.offset: 4339, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4337, + key.offset: 4350, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4341, + key.offset: 4354, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4355, + key.offset: 4368, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4363, + key.offset: 4376, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4367, + key.offset: 4380, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4378, + key.offset: 4391, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4382, + key.offset: 4395, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4396, + key.offset: 4409, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4404, + key.offset: 4417, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4412, + key.offset: 4425, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4421, + key.offset: 4434, key.length: 18 }, { key.kind: source.lang.swift.ref.protocol, key.name: "FooProtocolBase", key.usr: "c:objc(pl)FooProtocolBase", - key.offset: 4442, + key.offset: 4455, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4462, + key.offset: 4475, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4468, + key.offset: 4481, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4488, + key.offset: 4501, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4493, + key.offset: 4506, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4521, + key.offset: 4534, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4526, + key.offset: 4539, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 4547, + key.offset: 4560, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 4549, + key.offset: 4562, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4559, + key.offset: 4572, key.length: 3 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 4568, + key.offset: 4581, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4587, + key.offset: 4600, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4600, + key.offset: 4613, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4612, + key.offset: 4625, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 4618, + key.offset: 4631, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 4624, + key.offset: 4637, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Float", key.usr: "s:Sf", - key.offset: 4627, + key.offset: 4640, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4639, + key.offset: 4652, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4644, + key.offset: 4657, key.length: 29 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4681, + key.offset: 4694, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4687, + key.offset: 4700, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4692, + key.offset: 4705, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4717, + key.offset: 4730, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4722, + key.offset: 4735, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4742, + key.offset: 4755, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4752, + key.offset: 4765, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4757, + key.offset: 4770, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4777, + key.offset: 4790, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4787, + key.offset: 4800, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4792, + key.offset: 4805, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4813, + key.offset: 4826, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4823, + key.offset: 4836, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4828, + key.offset: 4841, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4848, + key.offset: 4861, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4855, + key.offset: 4868, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4861, + key.offset: 4874, key.length: 15 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 4879, + key.offset: 4892, key.length: 12 }, { key.kind: source.lang.swift.ref.protocol, key.name: "FooProtocolDerived", key.usr: "c:objc(pl)FooProtocolDerived", - key.offset: 4893, + key.offset: 4906, key.length: 18 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4919, + key.offset: 4932, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4923, + key.offset: 4936, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4937, + key.offset: 4950, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4948, + key.offset: 4961, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4952, + key.offset: 4965, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4966, + key.offset: 4979, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4977, + key.offset: 4990, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4981, + key.offset: 4994, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 4995, + key.offset: 5008, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5003, + key.offset: 5016, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5014, + key.offset: 5027, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5019, + key.offset: 5032, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5043, + key.offset: 5056, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5048, + key.offset: 5061, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 5065, + key.offset: 5078, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 5067, + key.offset: 5080, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5070, + key.offset: 5083, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5082, + key.offset: 5095, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5087, + key.offset: 5100, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 5104, + key.offset: 5117, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 5106, + key.offset: 5119, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5109, + key.offset: 5122, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 5116, + key.offset: 5129, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 5122, + key.offset: 5135, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5125, + key.offset: 5138, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5137, + key.offset: 5150, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5142, + key.offset: 5155, key.length: 29 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5179, + key.offset: 5192, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5185, + key.offset: 5198, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5190, + key.offset: 5203, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5211, + key.offset: 5224, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5216, + key.offset: 5229, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5236, + key.offset: 5249, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5246, + key.offset: 5259, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5251, + key.offset: 5264, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5271, + key.offset: 5284, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5281, + key.offset: 5294, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5286, + key.offset: 5299, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5307, + key.offset: 5320, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5317, + key.offset: 5330, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5322, + key.offset: 5335, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5342, + key.offset: 5355, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5349, + key.offset: 5362, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5359, + key.offset: 5372, key.length: 13 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5375, + key.offset: 5388, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5381, + key.offset: 5394, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5385, + key.offset: 5398, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5398, + key.offset: 5411, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5406, + key.offset: 5419, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5412, + key.offset: 5425, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5416, + key.offset: 5429, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5429, + key.offset: 5442, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5437, + key.offset: 5450, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5443, + key.offset: 5456, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5447, + key.offset: 5460, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5460, + key.offset: 5473, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5468, + key.offset: 5481, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5474, + key.offset: 5487, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5478, + key.offset: 5491, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt32", key.usr: "s:s6UInt32V", - key.offset: 5491, + key.offset: 5504, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5500, + key.offset: 5513, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5506, + key.offset: 5519, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5510, + key.offset: 5523, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt64", key.usr: "s:s6UInt64V", - key.offset: 5523, + key.offset: 5536, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5532, + key.offset: 5545, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5538, + key.offset: 5551, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5542, + key.offset: 5555, key.length: 11 }, { key.kind: source.lang.swift.ref.typealias, key.name: "typedef_int_t", key.usr: "c:Foo.h@T@typedef_int_t", - key.offset: 5555, + key.offset: 5568, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5571, + key.offset: 5584, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5577, + key.offset: 5590, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5581, + key.offset: 5594, key.length: 11 }, { key.kind: source.lang.swift.ref.typealias, key.name: "typedef_int_t", key.usr: "c:Foo.h@T@typedef_int_t", - key.offset: 5594, + key.offset: 5607, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5610, + key.offset: 5623, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5616, + key.offset: 5629, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5620, + key.offset: 5633, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int8", key.usr: "s:s4Int8V", - key.offset: 5633, + key.offset: 5646, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5640, + key.offset: 5653, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5646, + key.offset: 5659, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5650, + key.offset: 5663, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5663, + key.offset: 5676, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5671, + key.offset: 5684, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5677, + key.offset: 5690, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5681, + key.offset: 5694, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int16", key.usr: "s:s5Int16V", - key.offset: 5695, + key.offset: 5708, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5703, + key.offset: 5716, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5709, + key.offset: 5722, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5713, + key.offset: 5726, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 5727, + key.offset: 5740, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5733, + key.offset: 5746, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5739, + key.offset: 5752, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5743, + key.offset: 5756, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5757, + key.offset: 5770, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5765, + key.offset: 5778, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5771, + key.offset: 5784, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5775, + key.offset: 5788, key.length: 13 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5790, + key.offset: 5803, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5798, + key.offset: 5811, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5804, + key.offset: 5817, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5808, + key.offset: 5821, key.length: 18 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt64", key.usr: "s:s6UInt64V", - key.offset: 5828, + key.offset: 5841, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5837, + key.offset: 5850, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5843, + key.offset: 5856, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5847, + key.offset: 5860, key.length: 16 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt32", key.usr: "s:s6UInt32V", - key.offset: 5865, + key.offset: 5878, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5874, + key.offset: 5887, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5880, + key.offset: 5893, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5884, + key.offset: 5897, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5903, + key.offset: 5916, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5911, + key.offset: 5924, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5917, + key.offset: 5930, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5921, + key.offset: 5934, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 5940, + key.offset: 5953, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5948, + key.offset: 5961, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5954, + key.offset: 5967, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5959, + key.offset: 5972, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5978, + key.offset: 5991, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5983, + key.offset: 5996, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6007, + key.offset: 6020, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6014, + key.offset: 6027, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6037, + key.offset: 6050, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6041, + key.offset: 6054, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 6044, + key.offset: 6057, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6055, + key.offset: 6068, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6067, + key.offset: 6080, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 6072, + key.offset: 6085, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 6074, + key.offset: 6087, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 6077, + key.offset: 6090, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6086, + key.offset: 6099, key.length: 9 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 6096, + key.offset: 6109, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6116, + key.offset: 6129, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6121, + key.offset: 6134, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6141, + key.offset: 6154, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6148, + key.offset: 6161, key.length: 9 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 6158, + key.offset: 6171, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6178, + key.offset: 6191, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6183, + key.offset: 6196, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6203, + key.offset: 6216, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6213, + key.offset: 6226, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6218, + key.offset: 6231, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6239, + key.offset: 6252, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6246, + key.offset: 6259, key.length: 9 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 6256, + key.offset: 6269, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6276, + key.offset: 6289, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6281, + key.offset: 6294, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6301, + key.offset: 6314, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6308, + key.offset: 6321, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6317, + key.offset: 6330, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6335, + key.offset: 6348, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6341, + key.offset: 6354, key.length: 21 }, { key.kind: source.lang.swift.ref.protocol, key.name: "_InternalProt", key.usr: "c:objc(pl)_InternalProt", - key.offset: 6365, + key.offset: 6378, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6383, + key.offset: 6396, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6389, + key.offset: 6402, key.length: 25 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 6417, + key.offset: 6430, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6437, + key.offset: 6450, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6453, + key.offset: 6466, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6457, + key.offset: 6470, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6469, + key.offset: 6482, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6485, + key.offset: 6498, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6501, + key.offset: 6514, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6505, + key.offset: 6518, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6523, + key.offset: 6536, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6539, + key.offset: 6552, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6543, + key.offset: 6556, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6555, + key.offset: 6568, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6565, + key.offset: 6578, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6569, + key.offset: 6582, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6580, + key.offset: 6593, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6590, + key.offset: 6603, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6594, + key.offset: 6607, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6604, + key.offset: 6617, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6614, + key.offset: 6627, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6619, + key.offset: 6632, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6623, + key.offset: 6636, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6632, + key.offset: 6645, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6648, + key.offset: 6661, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6652, + key.offset: 6665, key.length: 6 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 6660, + key.offset: 6673, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6671, + key.offset: 6684, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6676, + key.offset: 6689, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6696, + key.offset: 6709, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6706, + key.offset: 6719, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6711, + key.offset: 6724, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6731, + key.offset: 6744, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6741, + key.offset: 6754, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6746, + key.offset: 6759, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6767, + key.offset: 6780, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6777, + key.offset: 6790, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6782, + key.offset: 6795, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6802, + key.offset: 6815, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6809, + key.offset: 6822, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6813, + key.offset: 6826, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6825, + key.offset: 6838, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6831, + key.offset: 6844, key.length: 21 }, { key.kind: source.lang.swift.ref.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 6855, + key.offset: 6868, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6875, + key.offset: 6888, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6887, + key.offset: 6900, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 6893, + key.offset: 6906, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 6897, + key.offset: 6910, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 6900, + key.offset: 6913, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6912, + key.offset: 6925, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6917, + key.offset: 6930, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6936, + key.offset: 6949, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6941, + key.offset: 6954, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6965, + key.offset: 6978, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6970, + key.offset: 6983, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6988, + key.offset: 7001, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6993, + key.offset: 7006, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7023, + key.offset: 7036, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7028, + key.offset: 7041, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7058, + key.offset: 7071, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7063, + key.offset: 7076, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7092, + key.offset: 7105, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7097, + key.offset: 7110, key.length: 23 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7128, + key.offset: 7141, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7133, + key.offset: 7146, key.length: 25 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7166, + key.offset: 7179, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7171, + key.offset: 7184, key.length: 25 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7204, + key.offset: 7217, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7209, + key.offset: 7222, key.length: 24 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7241, + key.offset: 7254, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7246, + key.offset: 7259, key.length: 26 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7280, + key.offset: 7293, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7285, + key.offset: 7298, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7305, + key.offset: 7318, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7315, + key.offset: 7328, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7320, + key.offset: 7333, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7340, + key.offset: 7353, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7350, + key.offset: 7363, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7355, + key.offset: 7368, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7376, + key.offset: 7389, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7386, + key.offset: 7399, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7391, + key.offset: 7404, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7411, + key.offset: 7424, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7418, + key.offset: 7431, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7424, + key.offset: 7437, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7438, + key.offset: 7451, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7443, + key.offset: 7456, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7460, + key.offset: 7473, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7462, + key.offset: 7475, key.length: 1 }, { key.kind: source.lang.swift.ref.class, key.name: "FooCFType", key.usr: "c:Foo.h@T@FooCFTypeRef", - key.offset: 7465, + key.offset: 7478, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7477, + key.offset: 7490, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7482, + key.offset: 7495, key.length: 21 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 7506, + key.offset: 7519, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7517, + key.offset: 7530, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7522, + key.offset: 7535, key.length: 13 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 7551, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7541, + key.offset: 7558, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7546, + key.offset: 7563, key.length: 10 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 7576, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 7562, + key.offset: 7583, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7573, + key.offset: 7594, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7577, + key.offset: 7598, key.length: 9 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 7588, + key.offset: 7609, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7594, + key.offset: 7615, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 7605, + key.offset: 7626, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7616, + key.offset: 7637, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7621, + key.offset: 7642, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7626, + key.offset: 7647, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7631, + key.offset: 7652, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7639, + key.offset: 7660, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Hasher", key.usr: "s:s6HasherV", - key.offset: 7645, + key.offset: 7666, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7658, + key.offset: 7679, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7665, + key.offset: 7686, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7674, + key.offset: 7695, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7676, + key.offset: 7697, key.length: 3 }, { key.kind: source.lang.swift.ref.enum, key.name: "ABAuthorizationStatus", key.usr: "c:@E@ABAuthorizationStatus", - key.offset: 7681, + key.offset: 7702, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7704, + key.offset: 7725, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7706, + key.offset: 7727, key.length: 3 }, { key.kind: source.lang.swift.ref.enum, key.name: "ABAuthorizationStatus", key.usr: "c:@E@ABAuthorizationStatus", - key.offset: 7711, + key.offset: 7732, key.length: 21 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 7737, + key.offset: 7758, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7744, + key.offset: 7765, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7749, + key.offset: 7770, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7761, + key.offset: 7782, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7763, + key.offset: 7784, key.length: 1 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 7766, + key.offset: 7787, key.length: 5 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int32", key.usr: "s:s5Int32V", - key.offset: 7776, + key.offset: 7797, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7782, + key.offset: 7803, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7789, + key.offset: 7810, key.length: 11 }, { key.kind: source.lang.swift.ref.protocol, key.name: "Equatable", key.usr: "s:SQ", - key.offset: 7803, + key.offset: 7824, key.length: 9 }, { key.kind: source.lang.swift.ref.protocol, key.name: "RawRepresentable", key.usr: "s:SY", - key.offset: 7814, + key.offset: 7835, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7838, + key.offset: 7859, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7843, + key.offset: 7864, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7845, + key.offset: 7866, key.length: 8 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt32", key.usr: "s:s6UInt32V", - key.offset: 7855, + key.offset: 7876, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7868, + key.offset: 7889, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7873, + key.offset: 7894, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7882, + key.offset: 7903, key.length: 8 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt32", key.usr: "s:s6UInt32V", - key.offset: 7892, + key.offset: 7913, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7905, + key.offset: 7926, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7909, + key.offset: 7930, key.length: 8 }, { key.kind: source.lang.swift.ref.struct, key.name: "UInt32", key.usr: "s:s6UInt32V", - key.offset: 7919, + key.offset: 7940, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7931, + key.offset: 7952, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7938, + key.offset: 7959, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7947, + key.offset: 7968, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7949, + key.offset: 7970, key.length: 3 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooSubEnum1", key.usr: "c:@E@FooSubEnum1", - key.offset: 7954, + key.offset: 7975, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.argument, - key.offset: 7967, + key.offset: 7988, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.parameter, - key.offset: 7969, + key.offset: 7990, key.length: 3 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooSubEnum1", key.usr: "c:@E@FooSubEnum1", - key.offset: 7974, + key.offset: 7995, key.length: 11 }, { key.kind: source.lang.swift.ref.struct, key.name: "Bool", key.usr: "s:Sb", - key.offset: 7990, + key.offset: 8011, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7997, + key.offset: 8018, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 8001, + key.offset: 8022, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooSubEnum1", key.usr: "c:@E@FooSubEnum1", - key.offset: 8015, + key.offset: 8036, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 8029, + key.offset: 8050, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 8035, + key.offset: 8056, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 8039, + key.offset: 8060, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, key.name: "FooSubEnum1", key.usr: "c:@E@FooSubEnum1", - key.offset: 8053, + key.offset: 8074, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 8067, + key.offset: 8088, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 8073, + key.offset: 8094, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 8077, + key.offset: 8098, key.length: 25 }, { key.kind: source.lang.swift.ref.struct, key.name: "Int", key.usr: "s:Si", - key.offset: 8104, + key.offset: 8125, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 8110, + key.offset: 8131, key.length: 3 } ] @@ -5081,7 +5106,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "c:@E@FooComparisonResult", key.doc.full_as_xml: "FooComparisonResultc:@E@FooComparisonResultenum FooComparisonResult : Int Aaa. FooComparisonResult. Bbb.", key.offset: 814, - key.length: 292, + key.length: 305, key.fully_annotated_decl: "enum FooComparisonResult : Int", key.inherits: [ { @@ -5096,23 +5121,23 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "orderedAscending", key.usr: "c:@E@FooComparisonResult@FooOrderedAscending", key.offset: 852, - key.length: 21, + key.length: 26, key.fully_annotated_decl: "case orderedAscending = -1" }, { key.kind: source.lang.swift.decl.enumelement, key.name: "orderedSame", key.usr: "c:@E@FooComparisonResult@FooOrderedSame", - key.offset: 879, - key.length: 16, + key.offset: 884, + key.length: 20, key.fully_annotated_decl: "case orderedSame = 0" }, { key.kind: source.lang.swift.decl.enumelement, key.name: "orderedDescending", key.usr: "c:@E@FooComparisonResult@FooOrderedDescending", - key.offset: 901, - key.length: 22, + key.offset: 910, + key.length: 26, key.fully_annotated_decl: "case orderedDescending = 1" }, { @@ -5120,7 +5145,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "hashValue", key.usr: "s:SYsSHRzSH8RawValueSYRpzrlE04hashB0Sivp::SYNTHESIZED::c:@E@FooComparisonResult", key.original_usr: "s:SYsSHRzSH8RawValueSYRpzrlE04hashB0Sivp", - key.offset: 929, + key.offset: 942, key.length: 37, key.fully_annotated_decl: "@inlinable var hashValue: Int { get }" }, @@ -5129,7 +5154,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "hash(into:)", key.usr: "s:SYsSHRzSH8RawValueSYRpzrlE4hash4intoys6HasherVz_tF::SYNTHESIZED::c:@E@FooComparisonResult", key.original_usr: "s:SYsSHRzSH8RawValueSYRpzrlE4hash4intoys6HasherVz_tF", - key.offset: 972, + key.offset: 985, key.length: 47, key.fully_annotated_decl: "@inlinable func hash(into hasher: inout Hasher)", key.entities: [ @@ -5137,7 +5162,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "into", key.name: "hasher", - key.offset: 1012, + key.offset: 1025, key.length: 6 } ] @@ -5147,7 +5172,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "!=(_:_:)", key.usr: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::c:@E@FooComparisonResult", key.original_usr: "s:SQsE2neoiySbx_xtFZ", - key.offset: 1025, + key.offset: 1038, key.length: 79, key.fully_annotated_decl: "static func != (lhs: FooComparisonResult, rhs: FooComparisonResult) -> Bool", key.entities: [ @@ -5155,14 +5180,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "lhs", - key.offset: 1048, + key.offset: 1061, key.length: 19 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "rhs", - key.offset: 1076, + key.offset: 1089, key.length: 19 } ] @@ -5174,7 +5199,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "FooRuncingOptions", key.usr: "c:@E@FooRuncingOptions", key.doc.full_as_xml: "FooRuncingOptionsc:@E@FooRuncingOptionsstruct FooRuncingOptions : OptionSet Aaa. FooRuncingOptions. Bbb.", - key.offset: 1107, + key.offset: 1120, key.length: 336, key.fully_annotated_decl: "struct FooRuncingOptions : OptionSet", key.conforms: [ @@ -5189,7 +5214,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(rawValue:)", key.usr: "s:So17FooRuncingOptionsV8rawValueABSi_tcfc", - key.offset: 1151, + key.offset: 1164, key.length: 28, key.fully_annotated_decl: "init(rawValue: Int)", key.entities: [ @@ -5197,7 +5222,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "rawValue", key.name: "rawValue", - key.offset: 1175, + key.offset: 1188, key.length: 3 } ] @@ -5206,7 +5231,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.static, key.name: "enableMince", key.usr: "c:@E@FooRuncingOptions@FooRuncingEnableMince", - key.offset: 1185, + key.offset: 1198, key.length: 49, key.fully_annotated_decl: "static var enableMince: FooRuncingOptions { get }" }, @@ -5214,7 +5239,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.static, key.name: "enableQuince", key.usr: "c:@E@FooRuncingOptions@FooRuncingEnableQuince", - key.offset: 1240, + key.offset: 1253, key.length: 50, key.fully_annotated_decl: "static var enableQuince: FooRuncingOptions { get }" }, @@ -5223,7 +5248,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "!=(_:_:)", key.usr: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:SQsE2neoiySbx_xtFZ", - key.offset: 1296, + key.offset: 1309, key.length: 75, key.fully_annotated_decl: "static func != (lhs: FooRuncingOptions, rhs: FooRuncingOptions) -> Bool", key.entities: [ @@ -5231,14 +5256,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "lhs", - key.offset: 1319, + key.offset: 1332, key.length: 17 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "rhs", - key.offset: 1345, + key.offset: 1358, key.length: 17 } ] @@ -5249,7 +5274,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPs7ElementQz012ArrayLiteralC0RtzrlE05arrayE0xAFd_tcfc::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPs7ElementQz012ArrayLiteralC0RtzrlE05arrayE0xAFd_tcfc", key.doc.full_as_xml: "init(arrayLiteral:)s:s10SetAlgebraPs7ElementQz012ArrayLiteralC0RtzrlE05arrayE0xAFd_tcfc@inlinable init(arrayLiteral: Self.Element...)Creates a set containing the elements of the given array literal.arrayLiteralinA list of elements of the new set.Do not call this initializer directly. It is used by the compiler when you use an array literal. Instead, create a new set using an array literal as its value by enclosing a comma-separated list of values in square brackets. You can use an array literal anywhere a set is expected by the type context.Here, a set of strings is created from an array literal holding only strings:", - key.offset: 1377, + key.offset: 1390, key.length: 64, key.fully_annotated_decl: "@inlinable init(arrayLiteral: FooRuncingOptions...)", key.entities: [ @@ -5257,7 +5282,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "arrayLiteral", key.name: "arrayLiteral", - key.offset: 1420, + key.offset: 1433, key.length: 17 } ] @@ -5267,7 +5292,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } { key.kind: source.lang.swift.decl.extension.struct, key.doc.full_as_xml: "extension FooRuncingOptionsSetAlgebra requirements for which default implementations are supplied.A type conforming to SetAlgebra can implement any of these initializers or methods, and those implementations will be used in lieu of these defaults.", - key.offset: 1445, + key.offset: 1458, key.length: 662, key.fully_annotated_generic_signature: "<Self where Self : SetAlgebra>", key.extends: { @@ -5295,7 +5320,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } } ], key.doc.full_as_xml: "init(_:)s:s10SetAlgebraPsEyxqd__ncSTRd__7ElementQyd__ACRtzlufc@inlinable init<S>(_ sequence: S) where S : Sequence, Self.Element == S.ElementCreates a new set from a finite sequence of items.sequenceinThe elements to use as members of the new set.Use this initializer to create a new set from an existing sequence, like an array or a range:", - key.offset: 1480, + key.offset: 1493, key.length: 79, key.fully_annotated_decl: "@inlinable init<S>(_ sequence: S) where S : Sequence, Self.Element == S.Element", key.entities: [ @@ -5303,7 +5328,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "sequence", - key.offset: 1511, + key.offset: 1524, key.length: 1 } ] @@ -5314,7 +5339,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE8subtractyyxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE8subtractyyxF", key.doc.full_as_xml: "subtract(_:)s:s10SetAlgebraPsE8subtractyyxF@inlinable mutating func subtract(_ other: Self)Removes the elements of the given set from this set.otherinA set of the same type as the current set.In the following example, the elements of the employees set that are also members of the neighbors set are removed. In particular, the names "Bethany" and "Eric" are removed from employees.", - key.offset: 1565, + key.offset: 1578, key.length: 61, key.fully_annotated_decl: "@inlinable mutating func subtract(_ other: FooRuncingOptions)", key.entities: [ @@ -5322,7 +5347,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 1608, + key.offset: 1621, key.length: 17 } ] @@ -5333,7 +5358,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE8isSubset2ofSbx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE8isSubset2ofSbx_tF", key.doc.full_as_xml: "isSubset(of:)s:s10SetAlgebraPsE8isSubset2ofSbx_tF@inlinable func isSubset(of other: Self) -> BoolReturns a Boolean value that indicates whether the set is a subset of another set.otherinA set of the same type as the current set.true if the set is a subset of other; otherwise, false.Set A is a subset of another set B if every member of A is also a member of B.", - key.offset: 1632, + key.offset: 1645, key.length: 61, key.fully_annotated_decl: "@inlinable func isSubset(of other: FooRuncingOptions) -> Bool", key.entities: [ @@ -5341,7 +5366,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "of", key.name: "other", - key.offset: 1667, + key.offset: 1680, key.length: 17 } ] @@ -5352,7 +5377,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE10isSuperset2ofSbx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE10isSuperset2ofSbx_tF", key.doc.full_as_xml: "isSuperset(of:)s:s10SetAlgebraPsE10isSuperset2ofSbx_tF@inlinable func isSuperset(of other: Self) -> BoolReturns a Boolean value that indicates whether the set is a superset of the given set.otherinA set of the same type as the current set.true if the set is a superset of other; otherwise, false.Set A is a superset of another set B if every member of B is also a member of A.", - key.offset: 1699, + key.offset: 1712, key.length: 63, key.fully_annotated_decl: "@inlinable func isSuperset(of other: FooRuncingOptions) -> Bool", key.entities: [ @@ -5360,7 +5385,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "of", key.name: "other", - key.offset: 1736, + key.offset: 1749, key.length: 17 } ] @@ -5371,7 +5396,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE10isDisjoint4withSbx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE10isDisjoint4withSbx_tF", key.doc.full_as_xml: "isDisjoint(with:)s:s10SetAlgebraPsE10isDisjoint4withSbx_tF@inlinable func isDisjoint(with other: Self) -> BoolReturns a Boolean value that indicates whether the set has no members in common with the given set.otherinA set of the same type as the current set.true if the set has no elements in common with other; otherwise, false.In the following example, the employees set is disjoint with the visitors set because no name appears in both sets.", - key.offset: 1768, + key.offset: 1781, key.length: 65, key.fully_annotated_decl: "@inlinable func isDisjoint(with other: FooRuncingOptions) -> Bool", key.entities: [ @@ -5379,7 +5404,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "with", key.name: "other", - key.offset: 1807, + key.offset: 1820, key.length: 17 } ] @@ -5390,7 +5415,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE11subtractingyxxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE11subtractingyxxF", key.doc.full_as_xml: "subtracting(_:)s:s10SetAlgebraPsE11subtractingyxxF@inlinable func subtracting(_ other: Self) -> SelfReturns a new set containing the elements of this set that do not occur in the given set.otherinA set of the same type as the current set.A new set.In the following example, the nonNeighbors set is made up of the elements of the employees set that are not elements of neighbors:", - key.offset: 1839, + key.offset: 1852, key.length: 76, key.fully_annotated_decl: "@inlinable func subtracting(_ other: FooRuncingOptions) -> FooRuncingOptions", key.entities: [ @@ -5398,7 +5423,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 1876, + key.offset: 1889, key.length: 17 } ] @@ -5409,7 +5434,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE7isEmptySbvp::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE7isEmptySbvp", key.doc.full_as_xml: "isEmptys:s10SetAlgebraPsE7isEmptySbvp@inlinable var isEmpty: Bool { get }A Boolean value that indicates whether the set has no elements.", - key.offset: 1921, + key.offset: 1934, key.length: 36, key.fully_annotated_decl: "@inlinable var isEmpty: Bool { get }" }, @@ -5419,7 +5444,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE16isStrictSuperset2ofSbx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE16isStrictSuperset2ofSbx_tF", key.doc.full_as_xml: "isStrictSuperset(of:)s:s10SetAlgebraPsE16isStrictSuperset2ofSbx_tF@inlinable func isStrictSuperset(of other: Self) -> BoolReturns a Boolean value that indicates whether this set is a strict superset of the given set.otherinA set of the same type as the current set.true if the set is a strict superset of other; otherwise, false.Set A is a strict superset of another set B if every member of B is also a member of A and A contains at least one element that is not a member of B.", - key.offset: 1963, + key.offset: 1976, key.length: 69, key.fully_annotated_decl: "@inlinable func isStrictSuperset(of other: FooRuncingOptions) -> Bool", key.entities: [ @@ -5427,7 +5452,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "of", key.name: "other", - key.offset: 2006, + key.offset: 2019, key.length: 17 } ] @@ -5438,7 +5463,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s10SetAlgebraPsE14isStrictSubset2ofSbx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s10SetAlgebraPsE14isStrictSubset2ofSbx_tF", key.doc.full_as_xml: "isStrictSubset(of:)s:s10SetAlgebraPsE14isStrictSubset2ofSbx_tF@inlinable func isStrictSubset(of other: Self) -> BoolReturns a Boolean value that indicates whether this set is a strict subset of the given set.otherinA set of the same type as the current set.true if the set is a strict subset of other; otherwise, false.Set A is a strict subset of another set B if every member of A is also a member of B and B contains at least one element that is not a member of A.", - key.offset: 2038, + key.offset: 2051, key.length: 67, key.fully_annotated_decl: "@inlinable func isStrictSubset(of other: FooRuncingOptions) -> Bool", key.entities: [ @@ -5446,7 +5471,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "of", key.name: "other", - key.offset: 2079, + key.offset: 2092, key.length: 17 } ] @@ -5456,7 +5481,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } { key.kind: source.lang.swift.decl.extension.struct, key.doc.full_as_xml: "extension FooRuncingOptionsOptionSet requirements for which default implementations are supplied.A type conforming to OptionSet can implement any of these initializers or methods, and those implementations will be used in lieu of these defaults.", - key.offset: 2109, + key.offset: 2122, key.length: 280, key.fully_annotated_generic_signature: "<Self where Self : OptionSet>", key.extends: { @@ -5471,7 +5496,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPsE5unionyxxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPsE5unionyxxF", key.doc.full_as_xml: "union(_:)s:s9OptionSetPsE5unionyxxF@inlinable func union(_ other: Self) -> SelfReturns a new option set of the elements contained in this set, in the given set, or in both.otherinAn option set.A new option set made up of the elements contained in this set, in other, or in both.This example uses the union(_:) method to add two more shipping options to the default set.", - key.offset: 2144, + key.offset: 2157, key.length: 70, key.fully_annotated_decl: "@inlinable func union(_ other: FooRuncingOptions) -> FooRuncingOptions", key.entities: [ @@ -5479,7 +5504,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 2175, + key.offset: 2188, key.length: 17 } ] @@ -5490,7 +5515,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPsE12intersectionyxxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPsE12intersectionyxxF", key.doc.full_as_xml: "intersection(_:)s:s9OptionSetPsE12intersectionyxxF@inlinable func intersection(_ other: Self) -> SelfReturns a new option set with only the elements contained in both this set and the given set.otherinAn option set.A new option set with only the elements contained in both this set and other.This example uses the intersection(_:) method to limit the available shipping options to what can be used with a PO Box destination.", - key.offset: 2220, + key.offset: 2233, key.length: 77, key.fully_annotated_decl: "@inlinable func intersection(_ other: FooRuncingOptions) -> FooRuncingOptions", key.entities: [ @@ -5498,7 +5523,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 2258, + key.offset: 2271, key.length: 17 } ] @@ -5509,7 +5534,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPsE19symmetricDifferenceyxxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPsE19symmetricDifferenceyxxF", key.doc.full_as_xml: "symmetricDifference(_:)s:s9OptionSetPsE19symmetricDifferenceyxxF@inlinable func symmetricDifference(_ other: Self) -> SelfReturns a new option set with the elements contained in this set or in the given set, but not in both.otherinAn option set.A new option set with only the elements contained in either this set or other, but not in both.", - key.offset: 2303, + key.offset: 2316, key.length: 84, key.fully_annotated_decl: "@inlinable func symmetricDifference(_ other: FooRuncingOptions) -> FooRuncingOptions", key.entities: [ @@ -5517,7 +5542,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 2348, + key.offset: 2361, key.length: 17 } ] @@ -5532,7 +5557,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } } ], key.doc.full_as_xml: "extension FooRuncingOptions where Self == Self.ElementOptionSet requirements for which default implementations are supplied when Element == Self, which is the default.A type conforming to OptionSet can implement any of these initializers or methods, and those implementations will be used in lieu of these defaults.", - key.offset: 2391, + key.offset: 2404, key.length: 407, key.fully_annotated_generic_signature: "<Self where Self : OptionSet, Self == Self.Element>", key.extends: { @@ -5547,7 +5572,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPs7ElementQzRszrlE8containsySbxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPs7ElementQzRszrlE8containsySbxF", key.doc.full_as_xml: "contains(_:)s:s9OptionSetPs7ElementQzRszrlE8containsySbxF@inlinable func contains(_ member: Self) -> BoolReturns a Boolean value that indicates whether a given element is a member of the option set.memberinThe element to look for in the option set.true if the option set contains member; otherwise, false.This example uses the contains(_:) method to check whether next-day shipping is in the availableOptions instance.", - key.offset: 2426, + key.offset: 2439, key.length: 61, key.fully_annotated_decl: "@inlinable func contains(_ member: FooRuncingOptions) -> Bool", key.entities: [ @@ -5555,7 +5580,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "member", - key.offset: 2461, + key.offset: 2474, key.length: 17 } ] @@ -5566,7 +5591,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPs7ElementQzRszrlE6insertySb8inserted_x17memberAfterInserttxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPs7ElementQzRszrlE6insertySb8inserted_x17memberAfterInserttxF", key.doc.full_as_xml: "insert(_:)s:s9OptionSetPs7ElementQzRszrlE6insertySb8inserted_x17memberAfterInserttxF@inlinable mutating func insert(_ newMember: Self.Element) -> (inserted: Bool, memberAfterInsert: Self.Element)Adds the given element to the option set if it is not already a member.newMemberinThe element to insert.(true, newMember) if newMember was not contained in self. Otherwise, returns (false, oldMember), where oldMember is the member of the set equal to newMember.In the following example, the .secondDay shipping option is added to the freeOptions option set if purchasePrice is greater than 50.0. For the ShippingOptions declaration, see the OptionSet protocol discussion. 50 {]]>", - key.offset: 2493, + key.offset: 2506, key.length: 121, key.fully_annotated_decl: "@discardableResult @inlinable mutating func insert(_ newMember: FooRuncingOptions) -> (inserted: Bool, memberAfterInsert: FooRuncingOptions)", key.entities: [ @@ -5574,7 +5599,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "newMember", - key.offset: 2538, + key.offset: 2551, key.length: 17 } ] @@ -5585,7 +5610,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPs7ElementQzRszrlE6removeyxSgxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPs7ElementQzRszrlE6removeyxSgxF", key.doc.full_as_xml: "remove(_:)s:s9OptionSetPs7ElementQzRszrlE6removeyxSgxF@inlinable mutating func remove(_ member: Self.Element) -> Self.Element?Removes the given element and all elements subsumed by it.memberinThe element of the set to remove.The intersection of [member] and the set, if the intersection was nonempty; otherwise, nil.In the following example, the .priority shipping option is removed from the options option set. Attempting to remove the same shipping option a second time results in nil, because options no longer contains .priority as a member.In the next example, the .express element is passed to remove(_:). Although .express is not a member of options, .express subsumes the remaining .secondDay element of the option set. Therefore, options is emptied and the intersection between .express and options is returned.", - key.offset: 2620, + key.offset: 2633, key.length: 82, key.fully_annotated_decl: "@discardableResult @inlinable mutating func remove(_ member: FooRuncingOptions) -> FooRuncingOptions?", key.entities: [ @@ -5593,7 +5618,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "member", - key.offset: 2662, + key.offset: 2675, key.length: 17 } ] @@ -5604,7 +5629,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPs7ElementQzRszrlE6update4withxSgx_tF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPs7ElementQzRszrlE6update4withxSgx_tF", key.doc.full_as_xml: "update(with:)s:s9OptionSetPs7ElementQzRszrlE6update4withxSgx_tF@inlinable mutating func update(with newMember: Self.Element) -> Self.Element?Inserts the given element into the set.The intersection of [newMember] and the set if the intersection was nonempty; otherwise, nil.If newMember is not contained in the set but subsumes current members of the set, the subsumed members are returned.", - key.offset: 2708, + key.offset: 2721, key.length: 88, key.fully_annotated_decl: "@discardableResult @inlinable mutating func update(with newMember: FooRuncingOptions) -> FooRuncingOptions?", key.entities: [ @@ -5612,7 +5637,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "with", key.name: "newMember", - key.offset: 2756, + key.offset: 2769, key.length: 17 } ] @@ -5627,7 +5652,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } } ], key.doc.full_as_xml: "extension FooRuncingOptions where Self.RawValue : FixedWidthIntegerOptionSet requirements for which default implementations are supplied when RawValue conforms to FixedWidthInteger, which is the usual case. Each distinct bit of an option set’s .rawValue corresponds to a disjoint value of the OptionSet.A type conforming to OptionSet can implement any of these initializers or methods, and those implementations will be used in lieu of these defaults.union is implemented as a bitwise “or” (|) of rawValuesintersection is implemented as a bitwise “and” (&) of rawValuessymmetricDifference is implemented as a bitwise “exclusive or” (^) of rawValues", - key.offset: 2800, + key.offset: 2813, key.length: 279, key.fully_annotated_generic_signature: "<Self where Self : OptionSet, Self.RawValue : FixedWidthInteger>", key.extends: { @@ -5642,7 +5667,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlExycfc::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlExycfc", key.doc.full_as_xml: "init()s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlExycfc@inlinable init()Creates an empty option set.This initializer creates an option set with a raw value of zero.", - key.offset: 2835, + key.offset: 2848, key.length: 17, key.fully_annotated_decl: "@inlinable init()" }, @@ -5652,7 +5677,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE9formUnionyyxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE9formUnionyyxF", key.doc.full_as_xml: "formUnion(_:)s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE9formUnionyyxF@inlinable mutating func formUnion(_ other: Self)Inserts the elements of another set into this option set.otherinAn option set.This method is implemented as a | (bitwise OR) operation on the two sets’ raw values.", - key.offset: 2858, + key.offset: 2871, key.length: 62, key.fully_annotated_decl: "@inlinable mutating func formUnion(_ other: FooRuncingOptions)", key.entities: [ @@ -5660,7 +5685,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 2902, + key.offset: 2915, key.length: 17 } ] @@ -5671,7 +5696,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE16formIntersectionyyxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE16formIntersectionyyxF", key.doc.full_as_xml: "formIntersection(_:)s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE16formIntersectionyyxF@inlinable mutating func formIntersection(_ other: Self)Removes all elements of this option set that are not also present in the given set.otherinAn option set.This method is implemented as a & (bitwise AND) operation on the two sets’ raw values.", - key.offset: 2926, + key.offset: 2939, key.length: 69, key.fully_annotated_decl: "@inlinable mutating func formIntersection(_ other: FooRuncingOptions)", key.entities: [ @@ -5679,7 +5704,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 2977, + key.offset: 2990, key.length: 17 } ] @@ -5690,7 +5715,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE23formSymmetricDifferenceyyxF::SYNTHESIZED::c:@E@FooRuncingOptions", key.original_usr: "s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE23formSymmetricDifferenceyyxF", key.doc.full_as_xml: "formSymmetricDifference(_:)s:s9OptionSetPss17FixedWidthInteger8RawValueRpzrlE23formSymmetricDifferenceyyxF@inlinable mutating func formSymmetricDifference(_ other: Self)Replaces this set with a new set containing all elements contained in either this set or the given set, but not in both.otherinAn option set.This method is implemented as a ^ (bitwise XOR) operation on the two sets’ raw values.", - key.offset: 3001, + key.offset: 3014, key.length: 76, key.fully_annotated_decl: "@inlinable mutating func formSymmetricDifference(_ other: FooRuncingOptions)", key.entities: [ @@ -5698,7 +5723,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "other", - key.offset: 3059, + key.offset: 3072, key.length: 17 } ] @@ -5709,7 +5734,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.struct, key.name: "FooStruct1", key.usr: "c:@S@FooStruct1", - key.offset: 3080, + key.offset: 3093, key.length: 105, key.fully_annotated_decl: "struct FooStruct1", key.entities: [ @@ -5717,7 +5742,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "x", key.usr: "c:@S@FooStruct1@FI@x", - key.offset: 3105, + key.offset: 3118, key.length: 12, key.fully_annotated_decl: "var x: Int32" }, @@ -5725,7 +5750,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "y", key.usr: "c:@S@FooStruct1@FI@y", - key.offset: 3123, + key.offset: 3136, key.length: 13, key.fully_annotated_decl: "var y: Double" }, @@ -5733,7 +5758,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init()", key.usr: "s:So10FooStruct1VABycfc", - key.offset: 3142, + key.offset: 3155, key.length: 6, key.fully_annotated_decl: "init()" }, @@ -5741,7 +5766,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(x:y:)", key.usr: "s:So10FooStruct1V1x1yABs5Int32V_Sdtcfc", - key.offset: 3154, + key.offset: 3167, key.length: 29, key.fully_annotated_decl: "init(x: Int32, y: Double)", key.entities: [ @@ -5749,14 +5774,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "x", key.name: "x", - key.offset: 3164, + key.offset: 3177, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "y", key.name: "y", - key.offset: 3176, + key.offset: 3189, key.length: 6 } ] @@ -5767,7 +5792,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.typealias, key.name: "FooStruct1Pointer", key.usr: "c:Foo.h@T@FooStruct1Pointer", - key.offset: 3186, + key.offset: 3199, key.length: 62, key.fully_annotated_decl: "typealias FooStruct1Pointer = UnsafeMutablePointer<FooStruct1>", key.conforms: [ @@ -5782,7 +5807,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.struct, key.name: "FooStruct2", key.usr: "c:@S@FooStruct2", - key.offset: 3249, + key.offset: 3262, key.length: 105, key.fully_annotated_decl: "struct FooStruct2", key.entities: [ @@ -5790,7 +5815,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "x", key.usr: "c:@S@FooStruct2@FI@x", - key.offset: 3274, + key.offset: 3287, key.length: 12, key.fully_annotated_decl: "var x: Int32" }, @@ -5798,7 +5823,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "y", key.usr: "c:@S@FooStruct2@FI@y", - key.offset: 3292, + key.offset: 3305, key.length: 13, key.fully_annotated_decl: "var y: Double" }, @@ -5806,7 +5831,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init()", key.usr: "s:So10FooStruct2VABycfc", - key.offset: 3311, + key.offset: 3324, key.length: 6, key.fully_annotated_decl: "init()" }, @@ -5814,7 +5839,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(x:y:)", key.usr: "s:So10FooStruct2V1x1yABs5Int32V_Sdtcfc", - key.offset: 3323, + key.offset: 3336, key.length: 29, key.fully_annotated_decl: "init(x: Int32, y: Double)", key.entities: [ @@ -5822,14 +5847,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "x", key.name: "x", - key.offset: 3333, + key.offset: 3346, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "y", key.name: "y", - key.offset: 3345, + key.offset: 3358, key.length: 6 } ] @@ -5840,7 +5865,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.typealias, key.name: "FooStructTypedef1", key.usr: "c:Foo.h@T@FooStructTypedef1", - key.offset: 3355, + key.offset: 3368, key.length: 40, key.fully_annotated_decl: "typealias FooStructTypedef1 = FooStruct2" }, @@ -5848,7 +5873,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.struct, key.name: "FooStructTypedef2", key.usr: "c:@SA@FooStructTypedef2", - key.offset: 3396, + key.offset: 3409, key.length: 112, key.fully_annotated_decl: "struct FooStructTypedef2", key.entities: [ @@ -5856,7 +5881,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "x", key.usr: "c:@SA@FooStructTypedef2@FI@x", - key.offset: 3428, + key.offset: 3441, key.length: 12, key.fully_annotated_decl: "var x: Int32" }, @@ -5864,7 +5889,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "y", key.usr: "c:@SA@FooStructTypedef2@FI@y", - key.offset: 3446, + key.offset: 3459, key.length: 13, key.fully_annotated_decl: "var y: Double" }, @@ -5872,7 +5897,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init()", key.usr: "s:So17FooStructTypedef2aABycfc", - key.offset: 3465, + key.offset: 3478, key.length: 6, key.fully_annotated_decl: "init()" }, @@ -5880,7 +5905,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(x:y:)", key.usr: "s:So17FooStructTypedef2a1x1yABs5Int32V_Sdtcfc", - key.offset: 3477, + key.offset: 3490, key.length: 29, key.fully_annotated_decl: "init(x: Int32, y: Double)", key.entities: [ @@ -5888,14 +5913,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "x", key.name: "x", - key.offset: 3487, + key.offset: 3500, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "y", key.name: "y", - key.offset: 3499, + key.offset: 3512, key.length: 6 } ] @@ -5907,7 +5932,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "FooTypedef1", key.usr: "c:Foo.h@T@FooTypedef1", key.doc.full_as_xml: "FooTypedef1c:Foo.h@T@FooTypedef1typealias FooTypedef1 = Int32 Aaa. FooTypedef1. Bbb.", - key.offset: 3509, + key.offset: 3522, key.length: 29, key.fully_annotated_decl: "typealias FooTypedef1 = Int32", key.conforms: [ @@ -5933,7 +5958,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooIntVar", key.usr: "c:@fooIntVar", key.doc.full_as_xml: "fooIntVarc:@fooIntVarvar fooIntVar: Int32 Aaa. fooIntVar. Bbb.", - key.offset: 3539, + key.offset: 3552, key.length: 20, key.fully_annotated_decl: "var fooIntVar: Int32" }, @@ -5942,7 +5967,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFunc1(_:)", key.usr: "c:@F@fooFunc1", key.doc.full_as_xml: "fooFunc1c:@F@fooFunc1func fooFunc1(_ a: Int32) -> Int32 Aaa. fooFunc1. Bbb.", - key.offset: 3560, + key.offset: 3573, key.length: 34, key.fully_annotated_decl: "func fooFunc1(_ a: Int32) -> Int32", key.entities: [ @@ -5950,7 +5975,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 3579, + key.offset: 3592, key.length: 5 } ] @@ -5959,14 +5984,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFunc1AnonymousParam(_:)", key.usr: "c:@F@fooFunc1AnonymousParam", - key.offset: 3595, + key.offset: 3608, key.length: 48, key.fully_annotated_decl: "func fooFunc1AnonymousParam(_: Int32) -> Int32", key.entities: [ { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", - key.offset: 3628, + key.offset: 3641, key.length: 5 } ] @@ -5975,7 +6000,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFunc3(_:_:_:_:)", key.usr: "c:@F@fooFunc3", - key.offset: 3644, + key.offset: 3657, key.length: 94, key.fully_annotated_decl: "func fooFunc3(_ a: Int32, _ b: Float, _ c: Double, _ d: UnsafeMutablePointer<Int32>!) -> Int32", key.entities: [ @@ -5983,28 +6008,28 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 3663, + key.offset: 3676, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "b", - key.offset: 3675, + key.offset: 3688, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "c", - key.offset: 3687, + key.offset: 3700, key.length: 6 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "d", - key.offset: 3700, + key.offset: 3713, key.length: 28 } ] @@ -6013,7 +6038,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFuncWithBlock(_:)", key.usr: "c:@F@fooFuncWithBlock", - key.offset: 3739, + key.offset: 3752, key.length: 49, key.fully_annotated_decl: "func fooFuncWithBlock(_ blk: ((Float) -> Int32)!)", key.entities: [ @@ -6021,7 +6046,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "blk", - key.offset: 3768, + key.offset: 3781, key.length: 19 } ] @@ -6030,7 +6055,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFuncWithFunctionPointer(_:)", key.usr: "c:@F@fooFuncWithFunctionPointer", - key.offset: 3789, + key.offset: 3802, key.length: 60, key.fully_annotated_decl: "func fooFuncWithFunctionPointer(_ fptr: ((Float) -> Int32)!)", key.entities: [ @@ -6038,7 +6063,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "fptr", - key.offset: 3829, + key.offset: 3842, key.length: 19 } ] @@ -6047,7 +6072,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFuncNoreturn1()", key.usr: "c:@F@fooFuncNoreturn1", - key.offset: 3850, + key.offset: 3863, key.length: 32, key.fully_annotated_decl: "func fooFuncNoreturn1() -> Never" }, @@ -6055,7 +6080,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooFuncNoreturn2()", key.usr: "c:@F@fooFuncNoreturn2", - key.offset: 3883, + key.offset: 3896, key.length: 32, key.fully_annotated_decl: "func fooFuncNoreturn2() -> Never" }, @@ -6064,7 +6089,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFuncWithComment1()", key.usr: "c:@F@fooFuncWithComment1", key.doc.full_as_xml: "fooFuncWithComment1c:@F@fooFuncWithComment1func fooFuncWithComment1() Aaa. fooFuncWithComment1. Bbb. Ccc. Ddd.", - key.offset: 3916, + key.offset: 3929, key.length: 26, key.fully_annotated_decl: "func fooFuncWithComment1()" }, @@ -6073,7 +6098,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFuncWithComment2()", key.usr: "c:@F@fooFuncWithComment2", key.doc.full_as_xml: "fooFuncWithComment2c:@F@fooFuncWithComment2func fooFuncWithComment2() Aaa. fooFuncWithComment2. Bbb.", - key.offset: 3943, + key.offset: 3956, key.length: 26, key.fully_annotated_decl: "func fooFuncWithComment2()" }, @@ -6082,7 +6107,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFuncWithComment3()", key.usr: "c:@F@fooFuncWithComment3", key.doc.full_as_xml: "fooFuncWithComment3c:@F@fooFuncWithComment3func fooFuncWithComment3() Aaa. fooFuncWithComment3. Bbb. Ccc.", - key.offset: 3970, + key.offset: 3983, key.length: 26, key.fully_annotated_decl: "func fooFuncWithComment3()" }, @@ -6091,7 +6116,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFuncWithComment4()", key.usr: "c:@F@fooFuncWithComment4", key.doc.full_as_xml: "fooFuncWithComment4c:@F@fooFuncWithComment4func fooFuncWithComment4() Aaa. fooFuncWithComment4. Bbb. Ddd.", - key.offset: 3997, + key.offset: 4010, key.length: 26, key.fully_annotated_decl: "func fooFuncWithComment4()" }, @@ -6100,7 +6125,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooFuncWithComment5()", key.usr: "c:@F@fooFuncWithComment5", key.doc.full_as_xml: "fooFuncWithComment5c:@F@fooFuncWithComment5func fooFuncWithComment5() Aaa. fooFuncWithComment5. Bbb. Ccc. Ddd.", - key.offset: 4024, + key.offset: 4037, key.length: 26, key.fully_annotated_decl: "func fooFuncWithComment5()" }, @@ -6109,7 +6134,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "redeclaredInMultipleModulesFunc1(_:)", key.usr: "c:@F@redeclaredInMultipleModulesFunc1", key.doc.full_as_xml: "redeclaredInMultipleModulesFunc1c:@F@redeclaredInMultipleModulesFunc1func redeclaredInMultipleModulesFunc1(_ a: Int32) -> Int32 Aaa. redeclaredInMultipleModulesFunc1. Bbb.", - key.offset: 4051, + key.offset: 4064, key.length: 58, key.fully_annotated_decl: "func redeclaredInMultipleModulesFunc1(_ a: Int32) -> Int32", key.entities: [ @@ -6117,7 +6142,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 4094, + key.offset: 4107, key.length: 5 } ] @@ -6127,7 +6152,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "FooProtocolBase", key.usr: "c:objc(pl)FooProtocolBase", key.doc.full_as_xml: "FooProtocolBasec:objc(pl)FooProtocolBaseprotocol FooProtocolBase Aaa. FooProtocolBase. Bbb.", - key.offset: 4110, + key.offset: 4123, key.length: 301, key.fully_annotated_decl: "protocol FooProtocolBase", key.entities: [ @@ -6136,7 +6161,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooProtoFunc()", key.usr: "c:objc(pl)FooProtocolBase(im)fooProtoFunc", key.doc.full_as_xml: "fooProtoFuncc:objc(pl)FooProtocolBase(im)fooProtoFuncfunc fooProtoFunc() Aaa. fooProtoFunc. Bbb. Ccc.", - key.offset: 4142, + key.offset: 4155, key.length: 19, key.fully_annotated_decl: "func fooProtoFunc()" }, @@ -6145,7 +6170,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooProtoFuncWithExtraIndentation1()", key.usr: "c:objc(pl)FooProtocolBase(im)fooProtoFuncWithExtraIndentation1", key.doc.full_as_xml: "fooProtoFuncWithExtraIndentation1c:objc(pl)FooProtocolBase(im)fooProtoFuncWithExtraIndentation1func fooProtoFuncWithExtraIndentation1() Aaa. fooProtoFuncWithExtraIndentation1. Bbb. Ccc.", - key.offset: 4167, + key.offset: 4180, key.length: 40, key.fully_annotated_decl: "func fooProtoFuncWithExtraIndentation1()" }, @@ -6154,7 +6179,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "fooProtoFuncWithExtraIndentation2()", key.usr: "c:objc(pl)FooProtocolBase(im)fooProtoFuncWithExtraIndentation2", key.doc.full_as_xml: "fooProtoFuncWithExtraIndentation2c:objc(pl)FooProtocolBase(im)fooProtoFuncWithExtraIndentation2func fooProtoFuncWithExtraIndentation2() Aaa. fooProtoFuncWithExtraIndentation2. Bbb. Ccc.", - key.offset: 4213, + key.offset: 4226, key.length: 40, key.fully_annotated_decl: "func fooProtoFuncWithExtraIndentation2()" }, @@ -6162,7 +6187,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.static, key.name: "fooProtoClassFunc()", key.usr: "c:objc(pl)FooProtocolBase(cm)fooProtoClassFunc", - key.offset: 4259, + key.offset: 4272, key.length: 31, key.fully_annotated_decl: "static func fooProtoClassFunc()" }, @@ -6170,7 +6195,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty1", key.usr: "c:objc(pl)FooProtocolBase(py)fooProperty1", - key.offset: 4296, + key.offset: 4309, key.length: 35, key.fully_annotated_decl: "var fooProperty1: Int32 { get set }" }, @@ -6178,7 +6203,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty2", key.usr: "c:objc(pl)FooProtocolBase(py)fooProperty2", - key.offset: 4337, + key.offset: 4350, key.length: 35, key.fully_annotated_decl: "var fooProperty2: Int32 { get set }" }, @@ -6186,7 +6211,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty3", key.usr: "c:objc(pl)FooProtocolBase(py)fooProperty3", - key.offset: 4378, + key.offset: 4391, key.length: 31, key.fully_annotated_decl: "var fooProperty3: Int32 { get }" } @@ -6196,7 +6221,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.protocol, key.name: "FooProtocolDerived", key.usr: "c:objc(pl)FooProtocolDerived", - key.offset: 4412, + key.offset: 4425, key.length: 49, key.fully_annotated_decl: "protocol FooProtocolDerived : FooProtocolBase", key.conforms: [ @@ -6211,7 +6236,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.class, key.name: "FooClassBase", key.usr: "c:objc(cs)FooClassBase", - key.offset: 4462, + key.offset: 4475, key.length: 392, key.fully_annotated_decl: "class FooClassBase", key.entities: [ @@ -6219,7 +6244,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooBaseInstanceFunc0()", key.usr: "c:objc(cs)FooClassBase(im)fooBaseInstanceFunc0", - key.offset: 4488, + key.offset: 4501, key.length: 27, key.fully_annotated_decl: "func fooBaseInstanceFunc0()" }, @@ -6227,7 +6252,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooBaseInstanceFunc1(_:)", key.usr: "c:objc(cs)FooClassBase(im)fooBaseInstanceFunc1:", - key.offset: 4521, + key.offset: 4534, key.length: 60, key.fully_annotated_decl: "func fooBaseInstanceFunc1(_ anObject: Any!) -> FooClassBase!", key.entities: [ @@ -6235,7 +6260,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "anObject", - key.offset: 4559, + key.offset: 4572, key.length: 4 } ] @@ -6244,7 +6269,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init()", key.usr: "c:objc(cs)FooClassBase(im)init", - key.offset: 4587, + key.offset: 4600, key.length: 7, key.fully_annotated_decl: "init!()" }, @@ -6252,7 +6277,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(float:)", key.usr: "c:objc(cs)FooClassBase(im)initWithFloat:", - key.offset: 4600, + key.offset: 4613, key.length: 33, key.fully_annotated_decl: "convenience init!(float f: Float)", key.entities: [ @@ -6260,7 +6285,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "float", key.name: "f", - key.offset: 4627, + key.offset: 4640, key.length: 5 } ] @@ -6269,7 +6294,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooBaseInstanceFuncOverridden()", key.usr: "c:objc(cs)FooClassBase(im)fooBaseInstanceFuncOverridden", - key.offset: 4639, + key.offset: 4652, key.length: 36, key.fully_annotated_decl: "func fooBaseInstanceFuncOverridden()" }, @@ -6277,7 +6302,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.class, key.name: "fooBaseClassFunc0()", key.usr: "c:objc(cs)FooClassBase(cm)fooBaseClassFunc0", - key.offset: 4681, + key.offset: 4694, key.length: 30, key.fully_annotated_decl: "class func fooBaseClassFunc0()" }, @@ -6285,7 +6310,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth1()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth1", - key.offset: 4717, + key.offset: 4730, key.length: 29, key.fully_annotated_decl: "func _internalMeth1() -> Any!" }, @@ -6293,7 +6318,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth2()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth2", - key.offset: 4752, + key.offset: 4765, key.length: 29, key.fully_annotated_decl: "func _internalMeth2() -> Any!" }, @@ -6301,7 +6326,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "nonInternalMeth()", key.usr: "c:objc(cs)FooClassBase(im)nonInternalMeth", - key.offset: 4787, + key.offset: 4800, key.length: 30, key.fully_annotated_decl: "func nonInternalMeth() -> Any!" }, @@ -6309,7 +6334,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth3()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth3", - key.offset: 4823, + key.offset: 4836, key.length: 29, key.fully_annotated_decl: "func _internalMeth3() -> Any!" } @@ -6320,7 +6345,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "FooClassDerived", key.usr: "c:objc(cs)FooClassDerived", key.doc.full_as_xml: "FooClassDerivedc:objc(cs)FooClassDerivedclass FooClassDerived : FooClassBase, FooProtocolDerived Aaa. FooClassDerived. Bbb.", - key.offset: 4855, + key.offset: 4868, key.length: 493, key.fully_annotated_decl: "class FooClassDerived : FooClassBase, FooProtocolDerived", key.inherits: [ @@ -6342,7 +6367,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty1", key.usr: "c:objc(cs)FooClassDerived(py)fooProperty1", - key.offset: 4919, + key.offset: 4932, key.length: 23, key.fully_annotated_decl: "var fooProperty1: Int32 { get set }" }, @@ -6350,7 +6375,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty2", key.usr: "c:objc(cs)FooClassDerived(py)fooProperty2", - key.offset: 4948, + key.offset: 4961, key.length: 23, key.fully_annotated_decl: "var fooProperty2: Int32 { get set }" }, @@ -6358,7 +6383,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "fooProperty3", key.usr: "c:objc(cs)FooClassDerived(py)fooProperty3", - key.offset: 4977, + key.offset: 4990, key.length: 31, key.fully_annotated_decl: "var fooProperty3: Int32 { get }" }, @@ -6366,7 +6391,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooInstanceFunc0()", key.usr: "c:objc(cs)FooClassDerived(im)fooInstanceFunc0", - key.offset: 5014, + key.offset: 5027, key.length: 23, key.fully_annotated_decl: "func fooInstanceFunc0()" }, @@ -6374,7 +6399,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooInstanceFunc1(_:)", key.usr: "c:objc(cs)FooClassDerived(im)fooInstanceFunc1:", - key.offset: 5043, + key.offset: 5056, key.length: 33, key.fully_annotated_decl: "func fooInstanceFunc1(_ a: Int32)", key.entities: [ @@ -6382,7 +6407,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 5070, + key.offset: 5083, key.length: 5 } ] @@ -6391,7 +6416,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooInstanceFunc2(_:withB:)", key.usr: "c:objc(cs)FooClassDerived(im)fooInstanceFunc2:withB:", - key.offset: 5082, + key.offset: 5095, key.length: 49, key.fully_annotated_decl: "func fooInstanceFunc2(_ a: Int32, withB b: Int32)", key.entities: [ @@ -6399,14 +6424,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 5109, + key.offset: 5122, key.length: 5 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "withB", key.name: "b", - key.offset: 5125, + key.offset: 5138, key.length: 5 } ] @@ -6415,7 +6440,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "fooBaseInstanceFuncOverridden()", key.usr: "c:objc(cs)FooClassDerived(im)fooBaseInstanceFuncOverridden", - key.offset: 5137, + key.offset: 5150, key.length: 36, key.fully_annotated_decl: "func fooBaseInstanceFuncOverridden()", key.inherits: [ @@ -6430,7 +6455,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.class, key.name: "fooClassFunc0()", key.usr: "c:objc(cs)FooClassDerived(cm)fooClassFunc0", - key.offset: 5179, + key.offset: 5192, key.length: 26, key.fully_annotated_decl: "class func fooClassFunc0()" }, @@ -6439,7 +6464,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth1()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth1::SYNTHESIZED::c:objc(cs)FooClassDerived", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth1", - key.offset: 5211, + key.offset: 5224, key.length: 29, key.fully_annotated_decl: "func _internalMeth1() -> Any!" }, @@ -6448,7 +6473,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth2()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth2::SYNTHESIZED::c:objc(cs)FooClassDerived", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth2", - key.offset: 5246, + key.offset: 5259, key.length: 29, key.fully_annotated_decl: "func _internalMeth2() -> Any!" }, @@ -6457,7 +6482,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "nonInternalMeth()", key.usr: "c:objc(cs)FooClassBase(im)nonInternalMeth::SYNTHESIZED::c:objc(cs)FooClassDerived", key.original_usr: "c:objc(cs)FooClassBase(im)nonInternalMeth", - key.offset: 5281, + key.offset: 5294, key.length: 30, key.fully_annotated_decl: "func nonInternalMeth() -> Any!" }, @@ -6466,7 +6491,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth3()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth3::SYNTHESIZED::c:objc(cs)FooClassDerived", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth3", - key.offset: 5317, + key.offset: 5330, key.length: 29, key.fully_annotated_decl: "func _internalMeth3() -> Any!" } @@ -6476,7 +6501,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.typealias, key.name: "typedef_int_t", key.usr: "c:Foo.h@T@typedef_int_t", - key.offset: 5349, + key.offset: 5362, key.length: 31, key.fully_annotated_decl: "typealias typedef_int_t = Int32", key.conforms: [ @@ -6501,7 +6526,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_1", key.usr: "c:Foo.h@3720@macro@FOO_MACRO_1", - key.offset: 5381, + key.offset: 5394, key.length: 30, key.fully_annotated_decl: "var FOO_MACRO_1: Int32 { get }" }, @@ -6509,7 +6534,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_2", key.usr: "c:Foo.h@3742@macro@FOO_MACRO_2", - key.offset: 5412, + key.offset: 5425, key.length: 30, key.fully_annotated_decl: "var FOO_MACRO_2: Int32 { get }" }, @@ -6517,7 +6542,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_3", key.usr: "c:Foo.h@3764@macro@FOO_MACRO_3", - key.offset: 5443, + key.offset: 5456, key.length: 30, key.fully_annotated_decl: "var FOO_MACRO_3: Int32 { get }" }, @@ -6525,7 +6550,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_4", key.usr: "c:Foo.h@3828@macro@FOO_MACRO_4", - key.offset: 5474, + key.offset: 5487, key.length: 31, key.fully_annotated_decl: "var FOO_MACRO_4: UInt32 { get }" }, @@ -6533,7 +6558,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_5", key.usr: "c:Foo.h@3860@macro@FOO_MACRO_5", - key.offset: 5506, + key.offset: 5519, key.length: 31, key.fully_annotated_decl: "var FOO_MACRO_5: UInt64 { get }" }, @@ -6541,7 +6566,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_6", key.usr: "c:Foo.h@3902@macro@FOO_MACRO_6", - key.offset: 5538, + key.offset: 5551, key.length: 38, key.fully_annotated_decl: "var FOO_MACRO_6: typedef_int_t { get }" }, @@ -6549,7 +6574,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_7", key.usr: "c:Foo.h@3943@macro@FOO_MACRO_7", - key.offset: 5577, + key.offset: 5590, key.length: 38, key.fully_annotated_decl: "var FOO_MACRO_7: typedef_int_t { get }" }, @@ -6557,7 +6582,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_8", key.usr: "c:Foo.h@3984@macro@FOO_MACRO_8", - key.offset: 5616, + key.offset: 5629, key.length: 29, key.fully_annotated_decl: "var FOO_MACRO_8: Int8 { get }" }, @@ -6565,7 +6590,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_9", key.usr: "c:Foo.h@4015@macro@FOO_MACRO_9", - key.offset: 5646, + key.offset: 5659, key.length: 30, key.fully_annotated_decl: "var FOO_MACRO_9: Int32 { get }" }, @@ -6573,7 +6598,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_10", key.usr: "c:Foo.h@4045@macro@FOO_MACRO_10", - key.offset: 5677, + key.offset: 5690, key.length: 31, key.fully_annotated_decl: "var FOO_MACRO_10: Int16 { get }" }, @@ -6581,7 +6606,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_11", key.usr: "c:Foo.h@4079@macro@FOO_MACRO_11", - key.offset: 5709, + key.offset: 5722, key.length: 29, key.fully_annotated_decl: "var FOO_MACRO_11: Int { get }" }, @@ -6589,7 +6614,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_OR", key.usr: "c:Foo.h@4112@macro@FOO_MACRO_OR", - key.offset: 5739, + key.offset: 5752, key.length: 31, key.fully_annotated_decl: "var FOO_MACRO_OR: Int32 { get }" }, @@ -6597,7 +6622,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_AND", key.usr: "c:Foo.h@4161@macro@FOO_MACRO_AND", - key.offset: 5771, + key.offset: 5784, key.length: 32, key.fully_annotated_decl: "var FOO_MACRO_AND: Int32 { get }" }, @@ -6605,7 +6630,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_BITWIDTH", key.usr: "c:Foo.h@4211@macro@FOO_MACRO_BITWIDTH", - key.offset: 5804, + key.offset: 5817, key.length: 38, key.fully_annotated_decl: "var FOO_MACRO_BITWIDTH: UInt64 { get }" }, @@ -6613,7 +6638,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_SIGNED", key.usr: "c:Foo.h@4266@macro@FOO_MACRO_SIGNED", - key.offset: 5843, + key.offset: 5856, key.length: 36, key.fully_annotated_decl: "var FOO_MACRO_SIGNED: UInt32 { get }" }, @@ -6621,7 +6646,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_REDEF_1", key.usr: "c:Foo.h@4477@macro@FOO_MACRO_REDEF_1", - key.offset: 5880, + key.offset: 5893, key.length: 36, key.fully_annotated_decl: "var FOO_MACRO_REDEF_1: Int32 { get }" }, @@ -6629,7 +6654,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_MACRO_REDEF_2", key.usr: "c:Foo.h@4534@macro@FOO_MACRO_REDEF_2", - key.offset: 5917, + key.offset: 5930, key.length: 36, key.fully_annotated_decl: "var FOO_MACRO_REDEF_2: Int32 { get }" }, @@ -6637,7 +6662,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "theLastDeclInFoo()", key.usr: "c:@F@theLastDeclInFoo", - key.offset: 5954, + key.offset: 5967, key.length: 23, key.fully_annotated_decl: "func theLastDeclInFoo()" }, @@ -6645,7 +6670,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "_internalTopLevelFunc()", key.usr: "c:@F@_internalTopLevelFunc", - key.offset: 5978, + key.offset: 5991, key.length: 28, key.fully_annotated_decl: "func _internalTopLevelFunc()" }, @@ -6653,7 +6678,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.struct, key.name: "_InternalStruct", key.usr: "c:@S@_InternalStruct", - key.offset: 6007, + key.offset: 6020, key.length: 78, key.fully_annotated_decl: "struct _InternalStruct", key.entities: [ @@ -6661,7 +6686,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "x", key.usr: "c:@S@_InternalStruct@FI@x", - key.offset: 6037, + key.offset: 6050, key.length: 12, key.fully_annotated_decl: "var x: Int32" }, @@ -6669,7 +6694,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init()", key.usr: "s:So15_InternalStructVABycfc", - key.offset: 6055, + key.offset: 6068, key.length: 6, key.fully_annotated_decl: "init()" }, @@ -6677,7 +6702,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(x:)", key.usr: "s:So15_InternalStructV1xABs5Int32V_tcfc", - key.offset: 6067, + key.offset: 6080, key.length: 16, key.fully_annotated_decl: "init(x: Int32)", key.entities: [ @@ -6685,7 +6710,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "x", key.name: "x", - key.offset: 6077, + key.offset: 6090, key.length: 5 } ] @@ -6694,7 +6719,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } }, { key.kind: source.lang.swift.decl.extension.class, - key.offset: 6086, + key.offset: 6099, key.length: 61, key.extends: { key.kind: source.lang.swift.ref.class, @@ -6706,7 +6731,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth1()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth1", - key.offset: 6116, + key.offset: 6129, key.length: 29, key.fully_annotated_decl: "func _internalMeth1() -> Any!" } @@ -6714,7 +6739,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } }, { key.kind: source.lang.swift.decl.extension.class, - key.offset: 6148, + key.offset: 6161, key.length: 97, key.extends: { key.kind: source.lang.swift.ref.class, @@ -6726,7 +6751,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth2()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth2", - key.offset: 6178, + key.offset: 6191, key.length: 29, key.fully_annotated_decl: "func _internalMeth2() -> Any!" }, @@ -6734,7 +6759,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "nonInternalMeth()", key.usr: "c:objc(cs)FooClassBase(im)nonInternalMeth", - key.offset: 6213, + key.offset: 6226, key.length: 30, key.fully_annotated_decl: "func nonInternalMeth() -> Any!" } @@ -6742,7 +6767,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } }, { key.kind: source.lang.swift.decl.extension.class, - key.offset: 6246, + key.offset: 6259, key.length: 61, key.extends: { key.kind: source.lang.swift.ref.class, @@ -6754,7 +6779,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "_internalMeth3()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth3", - key.offset: 6276, + key.offset: 6289, key.length: 29, key.fully_annotated_decl: "func _internalMeth3() -> Any!" } @@ -6764,7 +6789,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.protocol, key.name: "_InternalProt", key.usr: "c:objc(pl)_InternalProt", - key.offset: 6308, + key.offset: 6321, key.length: 26, key.fully_annotated_decl: "protocol _InternalProt" }, @@ -6772,7 +6797,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.class, key.name: "ClassWithInternalProt", key.usr: "c:objc(cs)ClassWithInternalProt", - key.offset: 6335, + key.offset: 6348, key.length: 47, key.fully_annotated_decl: "class ClassWithInternalProt : _InternalProt", key.conforms: [ @@ -6787,7 +6812,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.class, key.name: "FooClassPropertyOwnership", key.usr: "c:objc(cs)FooClassPropertyOwnership", - key.offset: 6383, + key.offset: 6396, key.length: 425, key.fully_annotated_decl: "class FooClassPropertyOwnership : FooClassBase", key.inherits: [ @@ -6802,7 +6827,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "assignable", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)assignable", - key.offset: 6437, + key.offset: 6450, key.length: 42, key.fully_annotated_decl: "unowned(unsafe) var assignable: AnyObject! { get set }" }, @@ -6810,7 +6835,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "unsafeAssignable", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)unsafeAssignable", - key.offset: 6485, + key.offset: 6498, key.length: 48, key.fully_annotated_decl: "unowned(unsafe) var unsafeAssignable: AnyObject! { get set }" }, @@ -6818,7 +6843,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "retainable", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)retainable", - key.offset: 6539, + key.offset: 6552, key.length: 20, key.fully_annotated_decl: "var retainable: Any! { get set }" }, @@ -6826,7 +6851,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "strongRef", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)strongRef", - key.offset: 6565, + key.offset: 6578, key.length: 19, key.fully_annotated_decl: "var strongRef: Any! { get set }" }, @@ -6834,7 +6859,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "copyable", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)copyable", - key.offset: 6590, + key.offset: 6603, key.length: 18, key.fully_annotated_decl: "var copyable: Any! { get set }" }, @@ -6842,7 +6867,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "weakRef", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)weakRef", - key.offset: 6614, + key.offset: 6627, key.length: 28, key.fully_annotated_decl: "weak var weakRef: AnyObject! { get set }" }, @@ -6850,7 +6875,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "scalar", key.usr: "c:objc(cs)FooClassPropertyOwnership(py)scalar", - key.offset: 6648, + key.offset: 6661, key.length: 17, key.fully_annotated_decl: "var scalar: Int32 { get set }" }, @@ -6859,7 +6884,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth1()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth1::SYNTHESIZED::c:objc(cs)FooClassPropertyOwnership", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth1", - key.offset: 6671, + key.offset: 6684, key.length: 29, key.fully_annotated_decl: "func _internalMeth1() -> Any!" }, @@ -6868,7 +6893,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth2()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth2::SYNTHESIZED::c:objc(cs)FooClassPropertyOwnership", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth2", - key.offset: 6706, + key.offset: 6719, key.length: 29, key.fully_annotated_decl: "func _internalMeth2() -> Any!" }, @@ -6877,7 +6902,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "nonInternalMeth()", key.usr: "c:objc(cs)FooClassBase(im)nonInternalMeth::SYNTHESIZED::c:objc(cs)FooClassPropertyOwnership", key.original_usr: "c:objc(cs)FooClassBase(im)nonInternalMeth", - key.offset: 6741, + key.offset: 6754, key.length: 30, key.fully_annotated_decl: "func nonInternalMeth() -> Any!" }, @@ -6886,7 +6911,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth3()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth3::SYNTHESIZED::c:objc(cs)FooClassPropertyOwnership", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth3", - key.offset: 6777, + key.offset: 6790, key.length: 29, key.fully_annotated_decl: "func _internalMeth3() -> Any!" } @@ -6896,7 +6921,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FOO_NIL", key.usr: "c:Foo.h@5323@macro@FOO_NIL", - key.offset: 6809, + key.offset: 6822, key.length: 15, key.fully_annotated_decl: "var FOO_NIL: ()", key.attributes: [ @@ -6912,7 +6937,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.class, key.name: "FooUnavailableMembers", key.usr: "c:objc(cs)FooUnavailableMembers", - key.offset: 6825, + key.offset: 6838, key.length: 592, key.fully_annotated_decl: "class FooUnavailableMembers : FooClassBase", key.inherits: [ @@ -6927,7 +6952,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(int:)", key.usr: "c:objc(cs)FooUnavailableMembers(cm)unavailableMembersWithInt:", - key.offset: 6875, + key.offset: 6888, key.length: 31, key.fully_annotated_decl: "convenience init!(int i: Int32)", key.entities: [ @@ -6935,7 +6960,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "int", key.name: "i", - key.offset: 6900, + key.offset: 6913, key.length: 5 } ] @@ -6944,7 +6969,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "unavailable()", key.usr: "c:objc(cs)FooUnavailableMembers(im)unavailable", - key.offset: 6912, + key.offset: 6925, key.length: 18, key.fully_annotated_decl: "func unavailable()", key.attributes: [ @@ -6960,7 +6985,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "swiftUnavailable()", key.usr: "c:objc(cs)FooUnavailableMembers(im)swiftUnavailable", - key.offset: 6936, + key.offset: 6949, key.length: 23, key.fully_annotated_decl: "func swiftUnavailable()", key.attributes: [ @@ -6975,7 +7000,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "deprecated()", key.usr: "c:objc(cs)FooUnavailableMembers(im)deprecated", - key.offset: 6965, + key.offset: 6978, key.length: 17, key.fully_annotated_decl: "func deprecated()", key.attributes: [ @@ -6991,7 +7016,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityIntroduced()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityIntroduced", - key.offset: 6988, + key.offset: 7001, key.length: 29, key.fully_annotated_decl: "func availabilityIntroduced()", key.attributes: [ @@ -7006,7 +7031,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityDeprecated()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityDeprecated", - key.offset: 7023, + key.offset: 7036, key.length: 29, key.fully_annotated_decl: "func availabilityDeprecated()", key.attributes: [ @@ -7025,7 +7050,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityObsoleted()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityObsoleted", - key.offset: 7058, + key.offset: 7071, key.length: 28, key.fully_annotated_decl: "func availabilityObsoleted()", key.attributes: [ @@ -7041,7 +7066,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityUnavailable()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityUnavailable", - key.offset: 7092, + key.offset: 7105, key.length: 30, key.fully_annotated_decl: "func availabilityUnavailable()", key.attributes: [ @@ -7057,7 +7082,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityIntroducedMsg()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityIntroducedMsg", - key.offset: 7128, + key.offset: 7141, key.length: 32, key.fully_annotated_decl: "func availabilityIntroducedMsg()", key.attributes: [ @@ -7073,7 +7098,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityDeprecatedMsg()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityDeprecatedMsg", - key.offset: 7166, + key.offset: 7179, key.length: 32, key.fully_annotated_decl: "func availabilityDeprecatedMsg()", key.attributes: [ @@ -7092,7 +7117,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityObsoletedMsg()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityObsoletedMsg", - key.offset: 7204, + key.offset: 7217, key.length: 31, key.fully_annotated_decl: "func availabilityObsoletedMsg()", key.attributes: [ @@ -7109,7 +7134,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.method.instance, key.name: "availabilityUnavailableMsg()", key.usr: "c:objc(cs)FooUnavailableMembers(im)availabilityUnavailableMsg", - key.offset: 7241, + key.offset: 7254, key.length: 33, key.fully_annotated_decl: "func availabilityUnavailableMsg()", key.attributes: [ @@ -7127,7 +7152,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth1()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth1::SYNTHESIZED::c:objc(cs)FooUnavailableMembers", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth1", - key.offset: 7280, + key.offset: 7293, key.length: 29, key.fully_annotated_decl: "func _internalMeth1() -> Any!" }, @@ -7136,7 +7161,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth2()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth2::SYNTHESIZED::c:objc(cs)FooUnavailableMembers", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth2", - key.offset: 7315, + key.offset: 7328, key.length: 29, key.fully_annotated_decl: "func _internalMeth2() -> Any!" }, @@ -7145,7 +7170,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "nonInternalMeth()", key.usr: "c:objc(cs)FooClassBase(im)nonInternalMeth::SYNTHESIZED::c:objc(cs)FooUnavailableMembers", key.original_usr: "c:objc(cs)FooClassBase(im)nonInternalMeth", - key.offset: 7350, + key.offset: 7363, key.length: 30, key.fully_annotated_decl: "func nonInternalMeth() -> Any!" }, @@ -7154,7 +7179,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "_internalMeth3()", key.usr: "c:objc(cs)FooClassBase(im)_internalMeth3::SYNTHESIZED::c:objc(cs)FooUnavailableMembers", key.original_usr: "c:objc(cs)FooClassBase(im)_internalMeth3", - key.offset: 7386, + key.offset: 7399, key.length: 29, key.fully_annotated_decl: "func _internalMeth3() -> Any!" } @@ -7164,7 +7189,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.class, key.name: "FooCFType", key.usr: "c:Foo.h@T@FooCFTypeRef", - key.offset: 7418, + key.offset: 7431, key.length: 19, key.fully_annotated_decl: "class FooCFType" }, @@ -7172,14 +7197,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "FooCFTypeRelease(_:)", key.usr: "c:@F@FooCFTypeRelease", - key.offset: 7438, + key.offset: 7451, key.length: 38, key.fully_annotated_decl: "func FooCFTypeRelease(_: FooCFType!)", key.entities: [ { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", - key.offset: 7465, + key.offset: 7478, key.length: 10 } ], @@ -7196,8 +7221,8 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.enum, key.name: "ABAuthorizationStatus", key.usr: "c:@E@ABAuthorizationStatus", - key.offset: 7477, - key.length: 266, + key.offset: 7490, + key.length: 274, key.fully_annotated_decl: "enum ABAuthorizationStatus : Int", key.inherits: [ { @@ -7211,8 +7236,8 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.enumelement, key.name: "notDetermined", key.usr: "c:@E@ABAuthorizationStatus@kABAuthorizationStatusNotDetermined", - key.offset: 7517, - key.length: 18, + key.offset: 7530, + key.length: 22, key.fully_annotated_decl: "case notDetermined = 0", key.attributes: [ { @@ -7226,8 +7251,8 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.enumelement, key.name: "restricted", key.usr: "c:@E@ABAuthorizationStatus@kABAuthorizationStatusRestricted", - key.offset: 7541, - key.length: 15, + key.offset: 7558, + key.length: 19, key.fully_annotated_decl: "case restricted = 1", key.attributes: [ { @@ -7242,7 +7267,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "hashValue", key.usr: "s:SYsSHRzSH8RawValueSYRpzrlE04hashB0Sivp::SYNTHESIZED::c:@E@ABAuthorizationStatus", key.original_usr: "s:SYsSHRzSH8RawValueSYRpzrlE04hashB0Sivp", - key.offset: 7562, + key.offset: 7583, key.length: 37, key.fully_annotated_decl: "@inlinable var hashValue: Int { get }" }, @@ -7251,7 +7276,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "hash(into:)", key.usr: "s:SYsSHRzSH8RawValueSYRpzrlE4hash4intoys6HasherVz_tF::SYNTHESIZED::c:@E@ABAuthorizationStatus", key.original_usr: "s:SYsSHRzSH8RawValueSYRpzrlE4hash4intoys6HasherVz_tF", - key.offset: 7605, + key.offset: 7626, key.length: 47, key.fully_annotated_decl: "@inlinable func hash(into hasher: inout Hasher)", key.entities: [ @@ -7259,7 +7284,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "into", key.name: "hasher", - key.offset: 7645, + key.offset: 7666, key.length: 6 } ] @@ -7269,7 +7294,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "!=(_:_:)", key.usr: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::c:@E@ABAuthorizationStatus", key.original_usr: "s:SQsE2neoiySbx_xtFZ", - key.offset: 7658, + key.offset: 7679, key.length: 83, key.fully_annotated_decl: "static func != (lhs: ABAuthorizationStatus, rhs: ABAuthorizationStatus) -> Bool", key.entities: [ @@ -7277,14 +7302,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "lhs", - key.offset: 7681, + key.offset: 7702, key.length: 21 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "rhs", - key.offset: 7711, + key.offset: 7732, key.length: 21 } ] @@ -7303,7 +7328,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.free, key.name: "fooSubFunc1(_:)", key.usr: "c:@F@fooSubFunc1", - key.offset: 7744, + key.offset: 7765, key.length: 37, key.fully_annotated_decl: "func fooSubFunc1(_ a: Int32) -> Int32", key.entities: [ @@ -7311,7 +7336,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "a", - key.offset: 7766, + key.offset: 7787, key.length: 5 } ], @@ -7321,7 +7346,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.struct, key.name: "FooSubEnum1", key.usr: "c:@E@FooSubEnum1", - key.offset: 7782, + key.offset: 7803, key.length: 214, key.fully_annotated_decl: "struct FooSubEnum1 : Equatable, RawRepresentable", key.conforms: [ @@ -7341,7 +7366,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(_:)", key.usr: "s:So11FooSubEnum1VyABs6UInt32Vcfc", - key.offset: 7838, + key.offset: 7859, key.length: 24, key.fully_annotated_decl: "init(_ rawValue: UInt32)", key.entities: [ @@ -7349,7 +7374,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "rawValue", - key.offset: 7855, + key.offset: 7876, key.length: 6 } ] @@ -7358,7 +7383,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.function.constructor, key.name: "init(rawValue:)", key.usr: "s:So11FooSubEnum1V8rawValueABs6UInt32V_tcfc", - key.offset: 7868, + key.offset: 7889, key.length: 31, key.fully_annotated_decl: "init(rawValue: UInt32)", key.entities: [ @@ -7366,7 +7391,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "rawValue", key.name: "rawValue", - key.offset: 7892, + key.offset: 7913, key.length: 6 } ] @@ -7375,7 +7400,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.instance, key.name: "rawValue", key.usr: "s:So11FooSubEnum1V8rawValues6UInt32Vvp", - key.offset: 7905, + key.offset: 7926, key.length: 20, key.fully_annotated_decl: "var rawValue: UInt32" }, @@ -7384,7 +7409,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.name: "!=(_:_:)", key.usr: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::c:@E@FooSubEnum1", key.original_usr: "s:SQsE2neoiySbx_xtFZ", - key.offset: 7931, + key.offset: 7952, key.length: 63, key.fully_annotated_decl: "static func != (lhs: FooSubEnum1, rhs: FooSubEnum1) -> Bool", key.entities: [ @@ -7392,14 +7417,14 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "lhs", - key.offset: 7954, + key.offset: 7975, key.length: 11 }, { key.kind: source.lang.swift.decl.var.local, key.keyword: "_", key.name: "rhs", - key.offset: 7974, + key.offset: 7995, key.length: 11 } ] @@ -7411,7 +7436,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FooSubEnum1X", key.usr: "c:@E@FooSubEnum1@FooSubEnum1X", - key.offset: 7997, + key.offset: 8018, key.length: 37, key.fully_annotated_decl: "var FooSubEnum1X: FooSubEnum1 { get }", key.modulename: "Foo.FooSub" @@ -7420,7 +7445,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FooSubEnum1Y", key.usr: "c:@E@FooSubEnum1@FooSubEnum1Y", - key.offset: 8035, + key.offset: 8056, key.length: 37, key.fully_annotated_decl: "var FooSubEnum1Y: FooSubEnum1 { get }", key.modulename: "Foo.FooSub" @@ -7429,7 +7454,7 @@ var FooSubUnnamedEnumeratorA1: Int { get } key.kind: source.lang.swift.decl.var.global, key.name: "FooSubUnnamedEnumeratorA1", key.usr: "c:@Ea@FooSubUnnamedEnumeratorA1@FooSubUnnamedEnumeratorA1", - key.offset: 8073, + key.offset: 8094, key.length: 42, key.fully_annotated_decl: "var FooSubUnnamedEnumeratorA1: Int { get }", key.modulename: "Foo.FooSub" diff --git a/test/SourceKit/DocSupport/doc_error_domain.swift b/test/SourceKit/DocSupport/doc_error_domain.swift index 44c9b005f83f1..2cb2b47f400ca 100644 --- a/test/SourceKit/DocSupport/doc_error_domain.swift +++ b/test/SourceKit/DocSupport/doc_error_domain.swift @@ -1,6 +1,6 @@ // REQUIRES: OS=macosx // RUN: %sourcekitd-test -req=doc-info -module MyError -- -I %S/Inputs \ -// RUN: %mcp_opt -sdk %sdk | %sed_clean > %t.response +// RUN: -sdk %sdk | %sed_clean > %t.response // RUN: %FileCheck -input-file=%t.response %s // CHECK: struct MyError { diff --git a/test/SourceKit/DocSupport/doc_source_file.swift b/test/SourceKit/DocSupport/doc_source_file.swift index 74a7f65fa8f0f..19e6b362da967 100644 --- a/test/SourceKit/DocSupport/doc_source_file.swift +++ b/test/SourceKit/DocSupport/doc_source_file.swift @@ -1,7 +1,7 @@ // RUN: %sourcekitd-test -req=doc-info %S/Inputs/main.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response // RUN: not %sourcekitd-test -req=doc-info %S/Inputs/main.swift -- %S/Inputs/cake.swift 2> %t.error // RUN: %FileCheck %s -check-prefix=MULTI_FILE < %t.error -// MULTI_FILE: unexpected input in compiler arguments \ No newline at end of file +// MULTI_FILE: unexpected input in compiler arguments diff --git a/test/SourceKit/DocSupport/doc_swift_module.swift b/test/SourceKit/DocSupport/doc_swift_module.swift index 8be65628b7f2c..d3c9fd1fd14c8 100644 --- a/test/SourceKit/DocSupport/doc_swift_module.swift +++ b/test/SourceKit/DocSupport/doc_swift_module.swift @@ -1,4 +1,4 @@ // RUN: %empty-directory(%t.mod) // RUN: %swift -emit-module -o %t.mod/cake.swiftmodule %S/Inputs/cake.swift -parse-as-library -enable-objc-interop -emit-module-doc-path %t.mod/cake.swiftdoc // RUN: %sourcekitd-test -req=doc-info -module cake -- -I %t.mod > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/DocSupport/doc_swift_module1.swift b/test/SourceKit/DocSupport/doc_swift_module1.swift index 3419e6cdcde5b..9bc3e0da5f73a 100644 --- a/test/SourceKit/DocSupport/doc_swift_module1.swift +++ b/test/SourceKit/DocSupport/doc_swift_module1.swift @@ -1,4 +1,4 @@ // RUN: %empty-directory(%t.mod) // RUN: %swift -emit-module -o %t.mod/cake1.swiftmodule %S/Inputs/cake1.swift -parse-as-library // RUN: %sourcekitd-test -req=doc-info -module cake1 -- -I %t.mod > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/DocumentStructure/Inputs/main.swift b/test/SourceKit/DocumentStructure/Inputs/main.swift index 1825f3a070c83..2901836ce4691 100644 --- a/test/SourceKit/DocumentStructure/Inputs/main.swift +++ b/test/SourceKit/DocumentStructure/Inputs/main.swift @@ -135,3 +135,33 @@ class OneMore { fatalError() } } + +class Chain { + func + (lhs: Chain, rhs: Chain) -> Chain { fatalError() } +} + +public init() { + fatalError() +} + +deinit { + fatalError() +} + +#if false +extension Result { + func foo() {} +} + +extension Outer { + class Inner { + deinit {} + } +} + +public extension Outer2 { + class Inner2 { + deinit {} + } +} +#endif diff --git a/test/SourceKit/DocumentStructure/access_parse.swift b/test/SourceKit/DocumentStructure/access_parse.swift index 80b8ea3b66693..8c02328c6f92c 100644 --- a/test/SourceKit/DocumentStructure/access_parse.swift +++ b/test/SourceKit/DocumentStructure/access_parse.swift @@ -1,3 +1,3 @@ // RUN: %swift -typecheck %S/Inputs/access.swift // RUN: %sourcekitd-test -req=structure %S/Inputs/access.swift -- -module-name Access %S/Inputs/access.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/DocumentStructure/access_parse.swift.response b/test/SourceKit/DocumentStructure/access_parse.swift.response index 7ca60e870922f..0e8c673b3761f 100644 --- a/test/SourceKit/DocumentStructure/access_parse.swift.response +++ b/test/SourceKit/DocumentStructure/access_parse.swift.response @@ -777,7 +777,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "DefAccess", key.offset: 1399, key.length: 43, @@ -788,7 +787,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1423, key.length: 17, @@ -801,7 +799,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "PubAccess", key.offset: 1443, key.length: 43, @@ -812,7 +809,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1467, key.length: 17, @@ -825,7 +821,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "IntAccess", key.offset: 1487, key.length: 43, @@ -836,7 +831,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1511, key.length: 17, @@ -849,7 +843,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "PrivAccess", key.offset: 1531, key.length: 44, @@ -860,7 +853,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "defFunc()", key.offset: 1556, key.length: 17, diff --git a/test/SourceKit/DocumentStructure/mark_edit.swift b/test/SourceKit/DocumentStructure/mark_edit.swift index d75da2a436704..3d40ae9cfa9b3 100644 --- a/test/SourceKit/DocumentStructure/mark_edit.swift +++ b/test/SourceKit/DocumentStructure/mark_edit.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=structure -pos=1:1 -length=0 -replace=" " %S/Inputs/mark.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/DocumentStructure/structure.swift b/test/SourceKit/DocumentStructure/structure.swift index 5fb4c5f3ba081..ef36005556ad5 100644 --- a/test/SourceKit/DocumentStructure/structure.swift +++ b/test/SourceKit/DocumentStructure/structure.swift @@ -1,14 +1,14 @@ // RUN: %sourcekitd-test -req=structure %S/Inputs/main.swift -- -module-name StructureTest %S/Inputs/main.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response // RUN: %sourcekitd-test -req=structure %S/Inputs/invalid.swift | %sed_clean > %t.invalid.response -// RUN: diff -u %s.invalid.response %t.invalid.response +// RUN: diff --strip-trailing-cr -u %s.invalid.response %t.invalid.response // RUN: %sourcekitd-test -req=structure %S/../Inputs/placeholders.swift | %sed_clean > %t.placeholders.response -// RUN: diff -u %s.placeholders.response %t.placeholders.response +// RUN: diff --strip-trailing-cr -u %s.placeholders.response %t.placeholders.response // RUN: %sourcekitd-test -req=structure %S/Inputs/main.swift -name -foobar | %sed_clean > %t.foobar.response -// RUN: diff -u %s.foobar.response %t.foobar.response +// RUN: diff --strip-trailing-cr -u %s.foobar.response %t.foobar.response // RUN: %sourcekitd-test -req=structure -text-input %S/Inputs/main.swift | %sed_clean > %t.empty.response -// RUN: diff -u %s.empty.response %t.empty.response +// RUN: diff --strip-trailing-cr -u %s.empty.response %t.empty.response diff --git a/test/SourceKit/DocumentStructure/structure.swift.empty.response b/test/SourceKit/DocumentStructure/structure.swift.empty.response index 43b0be09bfa2a..ce39fc3f36c5f 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.empty.response +++ b/test/SourceKit/DocumentStructure/structure.swift.empty.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1403,6 +1399,222 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, + key.namelength: 6, + key.bodyoffset: 2442, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2445, + key.length: 13, + key.nameoffset: 2450, + key.namelength: 5, + key.bodyoffset: 2457, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ @@ -1439,6 +1651,20 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/DocumentStructure/structure.swift.foobar.response b/test/SourceKit/DocumentStructure/structure.swift.foobar.response index 3ecd5555db9e2..ebf0786fa318e 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.foobar.response +++ b/test/SourceKit/DocumentStructure/structure.swift.foobar.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1403,6 +1399,222 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, + key.namelength: 6, + key.bodyoffset: 2442, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2445, + key.length: 13, + key.nameoffset: 2450, + key.namelength: 5, + key.bodyoffset: 2457, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ @@ -1442,6 +1654,22 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.filepath: "-foobar", + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.filepath: "-foobar", + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/DocumentStructure/structure.swift.invalid.response b/test/SourceKit/DocumentStructure/structure.swift.invalid.response index 9ee32104bfeb2..56ddd30872a9c 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.invalid.response +++ b/test/SourceKit/DocumentStructure/structure.swift.invalid.response @@ -16,7 +16,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 12, key.length: 43, @@ -27,7 +26,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls1", key.offset: 35, key.length: 18, diff --git a/test/SourceKit/DocumentStructure/structure.swift.response b/test/SourceKit/DocumentStructure/structure.swift.response index 83d66b8c72def..45fb37a2ba1a7 100644 --- a/test/SourceKit/DocumentStructure/structure.swift.response +++ b/test/SourceKit/DocumentStructure/structure.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 2259, + key.length: 2587, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { @@ -223,7 +223,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "OuterCls", key.offset: 377, key.length: 45, @@ -234,7 +233,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.class, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "InnerCls2", key.offset: 402, key.length: 18, @@ -558,7 +556,6 @@ }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "Foo", key.offset: 999, key.length: 58, @@ -569,7 +566,6 @@ key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "anExtendedFooFunction()", key.offset: 1019, key.length: 36, @@ -1403,6 +1399,222 @@ ] } ] + }, + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "Chain", + key.offset: 2260, + key.length: 87, + key.nameoffset: 2266, + key.namelength: 5, + key.bodyoffset: 2276, + key.bodylength: 70, + key.substructure: [ + { + key.kind: source.lang.swift.decl.generic_type_param, + key.name: "A", + key.offset: 2272, + key.length: 1, + key.nameoffset: 2272, + key.namelength: 1 + }, + { + key.kind: source.lang.swift.decl.function.method.static, + key.accessibility: source.lang.swift.accessibility.internal, + key.name: "+(_:_:)", + key.offset: 2279, + key.length: 66, + key.typename: "Chain", + key.nameoffset: 2284, + key.namelength: 32, + key.bodyoffset: 2330, + key.bodylength: 14, + key.substructure: [ + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "lhs", + key.offset: 2287, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.decl.var.parameter, + key.name: "rhs", + key.offset: 2302, + key.length: 13, + key.typename: "Chain", + key.nameoffset: 0, + key.namelength: 0 + }, + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2331, + key.length: 12, + key.nameoffset: 2331, + key.namelength: 10, + key.bodyoffset: 2342, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "init()", + key.offset: 2356, + key.length: 27, + key.nameoffset: 2356, + key.namelength: 6, + key.bodyoffset: 2364, + key.bodylength: 18, + key.attributes: [ + { + key.offset: 2349, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2369, + key.length: 12, + key.nameoffset: 2369, + key.namelength: 10, + key.bodyoffset: 2380, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.function.free, + key.accessibility: source.lang.swift.accessibility.private, + key.name: "deinit", + key.offset: 2385, + key.length: 27, + key.nameoffset: 2385, + key.namelength: 6, + key.bodyoffset: 2393, + key.bodylength: 18, + key.substructure: [ + { + key.kind: source.lang.swift.expr.call, + key.name: "fatalError", + key.offset: 2398, + key.length: 12, + key.nameoffset: 2398, + key.namelength: 10, + key.bodyoffset: 2409, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Result", + key.offset: 2424, + key.length: 36, + key.nameoffset: 2434, + key.namelength: 6, + key.bodyoffset: 2442, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "foo()", + key.offset: 2445, + key.length: 13, + key.nameoffset: 2450, + key.namelength: 5, + key.bodyoffset: 2457, + key.bodylength: 0 + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.name: "Outer", + key.offset: 2462, + key.length: 53, + key.nameoffset: 2472, + key.namelength: 5, + key.bodyoffset: 2479, + key.bodylength: 35, + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.name: "Inner", + key.offset: 2482, + key.length: 31, + key.nameoffset: 2488, + key.namelength: 5, + key.bodyoffset: 2495, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "deinit", + key.offset: 2500, + key.length: 9, + key.nameoffset: 2500, + key.namelength: 6, + key.bodyoffset: 2508, + key.bodylength: 0 + } + ] + } + ] + }, + { + key.kind: source.lang.swift.decl.extension, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Outer2", + key.offset: 2524, + key.length: 55, + key.nameoffset: 2534, + key.namelength: 6, + key.bodyoffset: 2542, + key.bodylength: 36, + key.attributes: [ + { + key.offset: 2517, + key.length: 6, + key.attribute: source.decl.attribute.public + } + ], + key.substructure: [ + { + key.kind: source.lang.swift.decl.class, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "Inner2", + key.offset: 2545, + key.length: 32, + key.nameoffset: 2551, + key.namelength: 6, + key.bodyoffset: 2559, + key.bodylength: 17, + key.substructure: [ + { + key.kind: source.lang.swift.decl.function.method.instance, + key.accessibility: source.lang.swift.accessibility.public, + key.name: "deinit", + key.offset: 2564, + key.length: 9, + key.nameoffset: 2564, + key.namelength: 6, + key.bodyoffset: 2572, + key.bodylength: 0 + } + ] + } + ] } ], key.diagnostics: [ @@ -1442,6 +1654,22 @@ key.sourcetext: "func " } ] + }, + { + key.line: 143, + key.column: 12, + key.filepath: main.swift, + key.severity: source.diagnostic.severity.error, + key.description: "initializers may only be declared within a type", + key.diagnostic_stage: source.diagnostic.stage.swift.parse + }, + { + key.line: 147, + key.column: 1, + key.filepath: main.swift, + key.severity: source.diagnostic.severity.error, + key.description: "deinitializers may only be declared within a class", + key.diagnostic_stage: source.diagnostic.stage.swift.parse } ] } diff --git a/test/SourceKit/DocumentStructure/structure_object_literals.swift b/test/SourceKit/DocumentStructure/structure_object_literals.swift index 3cee89d8fcbfb..5962f97e9e205 100644 --- a/test/SourceKit/DocumentStructure/structure_object_literals.swift +++ b/test/SourceKit/DocumentStructure/structure_object_literals.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=structure %s -- -module-name StructureTest %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response let color: S = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) let image: I? = #imageLiteral(resourceName: "hello.png") diff --git a/test/SourceKit/DocumentStructure/structure_object_literals.swift.response b/test/SourceKit/DocumentStructure/structure_object_literals.swift.response index b57ace3c1f754..3ebb21449615b 100644 --- a/test/SourceKit/DocumentStructure/structure_object_literals.swift.response +++ b/test/SourceKit/DocumentStructure/structure_object_literals.swift.response @@ -1,66 +1,66 @@ { key.offset: 0, - key.length: 267, + key.length: 287, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.substructure: [ { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.internal, key.name: "color", - key.offset: 144, + key.offset: 164, key.length: 65, key.typename: "S", - key.nameoffset: 148, + key.nameoffset: 168, key.namelength: 5 }, { key.kind: source.lang.swift.expr.object_literal, key.name: "colorLiteral", - key.offset: 159, + key.offset: 179, key.length: 50, - key.nameoffset: 160, + key.nameoffset: 180, key.namelength: 12, - key.bodyoffset: 172, + key.bodyoffset: 192, key.bodylength: 36, key.substructure: [ { key.kind: source.lang.swift.expr.argument, key.name: "red", - key.offset: 173, + key.offset: 193, key.length: 6, - key.nameoffset: 173, + key.nameoffset: 193, key.namelength: 3, - key.bodyoffset: 178, + key.bodyoffset: 198, key.bodylength: 1 }, { key.kind: source.lang.swift.expr.argument, key.name: "green", - key.offset: 181, + key.offset: 201, key.length: 8, - key.nameoffset: 181, + key.nameoffset: 201, key.namelength: 5, - key.bodyoffset: 188, + key.bodyoffset: 208, key.bodylength: 1 }, { key.kind: source.lang.swift.expr.argument, key.name: "blue", - key.offset: 191, + key.offset: 211, key.length: 7, - key.nameoffset: 191, + key.nameoffset: 211, key.namelength: 4, - key.bodyoffset: 197, + key.bodyoffset: 217, key.bodylength: 1 }, { key.kind: source.lang.swift.expr.argument, key.name: "alpha", - key.offset: 200, + key.offset: 220, key.length: 8, - key.nameoffset: 200, + key.nameoffset: 220, key.namelength: 5, - key.bodyoffset: 207, + key.bodyoffset: 227, key.bodylength: 1 } ] @@ -69,30 +69,30 @@ key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.internal, key.name: "image", - key.offset: 210, + key.offset: 230, key.length: 56, key.typename: "I?", - key.nameoffset: 214, + key.nameoffset: 234, key.namelength: 5 }, { key.kind: source.lang.swift.expr.object_literal, key.name: "imageLiteral", - key.offset: 226, + key.offset: 246, key.length: 40, - key.nameoffset: 227, + key.nameoffset: 247, key.namelength: 12, - key.bodyoffset: 239, + key.bodyoffset: 259, key.bodylength: 26, key.substructure: [ { key.kind: source.lang.swift.expr.argument, key.name: "resourceName", - key.offset: 240, + key.offset: 260, key.length: 25, - key.nameoffset: 240, + key.nameoffset: 260, key.namelength: 12, - key.bodyoffset: 254, + key.bodyoffset: 274, key.bodylength: 11 } ] diff --git a/test/SourceKit/ExtractComment/extract_comments.swift b/test/SourceKit/ExtractComment/extract_comments.swift index 16f6692498d2c..997532165a0ac 100644 --- a/test/SourceKit/ExtractComment/extract_comments.swift +++ b/test/SourceKit/ExtractComment/extract_comments.swift @@ -1,28 +1,28 @@ // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocComment1.swift > %t.DocComment1.response -// RUN: diff -u %s.DocComment1.response %t.DocComment1.response +// RUN: diff --strip-trailing-cr -u %s.DocComment1.response %t.DocComment1.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocComment2.swift > %t.DocComment2.response -// RUN: diff -u %s.DocComment2.response %t.DocComment2.response +// RUN: diff --strip-trailing-cr -u %s.DocComment2.response %t.DocComment2.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocComment3.swift > %t.DocComment3.response -// RUN: diff -u %s.DocComment3.response %t.DocComment3.response +// RUN: diff --strip-trailing-cr -u %s.DocComment3.response %t.DocComment3.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocCommentEmptyLine1.swift > %t.DocCommentEmptyLine1.response -// RUN: diff -u %s.DocCommentEmptyLine1.response %t.DocCommentEmptyLine1.response +// RUN: diff --strip-trailing-cr -u %s.DocCommentEmptyLine1.response %t.DocCommentEmptyLine1.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocCommentEmptyLine2.swift > %t.DocCommentEmptyLine2.response -// RUN: diff -u %s.DocCommentEmptyLine2.response %t.DocCommentEmptyLine2.response +// RUN: diff --strip-trailing-cr -u %s.DocCommentEmptyLine2.response %t.DocCommentEmptyLine2.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/DocCommentEmptyLine3.swift > %t.DocCommentEmptyLine3.response -// RUN: diff -u %s.DocCommentEmptyLine3.response %t.DocCommentEmptyLine3.response +// RUN: diff --strip-trailing-cr -u %s.DocCommentEmptyLine3.response %t.DocCommentEmptyLine3.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/Comment1.swift > %t.Comment1.response -// RUN: diff -u %s.Comment1.response %t.Comment1.response +// RUN: diff --strip-trailing-cr -u %s.Comment1.response %t.Comment1.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/CommentIndent1.swift > %t.CommentIndent1.response -// RUN: diff -u %s.CommentIndent1.response %t.CommentIndent1.response +// RUN: diff --strip-trailing-cr -u %s.CommentIndent1.response %t.CommentIndent1.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/CommentIndent2.swift > %t.CommentIndent2.response -// RUN: diff -u %s.CommentIndent2.response %t.CommentIndent2.response +// RUN: diff --strip-trailing-cr -u %s.CommentIndent2.response %t.CommentIndent2.response // RUN: %sourcekitd-test -req=extract-comment -pass-as-sourcetext %S/Inputs/NotComment1.swift diff --git a/test/SourceKit/Indexing/index_bad_modulename.swift b/test/SourceKit/Indexing/index_bad_modulename.swift index 0b6e0c4e444fa..50e4c997be5a5 100644 --- a/test/SourceKit/Indexing/index_bad_modulename.swift +++ b/test/SourceKit/Indexing/index_bad_modulename.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %build-clang-importer-objc-overlays -// RUN: %sourcekitd-test -req=index %s -- %s -module-name Swift %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response1 +// RUN: %sourcekitd-test -req=index %s -- %s -module-name Swift -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response1 // RUN: diff -u %s.response %t.response1 -// RUN: %sourcekitd-test -req=index %s -- %s -module-name 12345 %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response2 +// RUN: %sourcekitd-test -req=index %s -- %s -module-name 12345 -target %target-triple %clang-importer-sdk-nosource -I %t | %sed_clean > %t.response2 // RUN: diff -u %s.response %t.response2 import ObjectiveC diff --git a/test/SourceKit/Indexing/index_big_array.swift b/test/SourceKit/Indexing/index_big_array.swift index 02da4c49b3448..4c8fe8d7d7fc8 100644 --- a/test/SourceKit/Indexing/index_big_array.swift +++ b/test/SourceKit/Indexing/index_big_array.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=index %S/../Inputs/big_array.swift -- %S/../Inputs/big_array.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Indexing/index_enum_case.swift b/test/SourceKit/Indexing/index_enum_case.swift index 261d40aa04887..89b33e91be037 100644 --- a/test/SourceKit/Indexing/index_enum_case.swift +++ b/test/SourceKit/Indexing/index_enum_case.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=index %s -- -Xfrontend -serialize-diagnostics-path -Xfrontend %t.dia %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response public enum E { diff --git a/test/SourceKit/Indexing/index_forbid_typecheck.swift b/test/SourceKit/Indexing/index_forbid_typecheck.swift index 868e7d5b80626..9220f1f6dcd65 100644 --- a/test/SourceKit/Indexing/index_forbid_typecheck.swift +++ b/test/SourceKit/Indexing/index_forbid_typecheck.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=index %S/../Inputs/forbid_typecheck_primary.swift -- -Xfrontend -debug-forbid-typecheck-prefix -Xfrontend NOTYPECHECK %S/../Inputs/forbid_typecheck_2.swift %S/../Inputs/forbid_typecheck_primary.swift -module-name forbid_typecheck | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Indexing/index_func_import.swift b/test/SourceKit/Indexing/index_func_import.swift index 5b53ff0e7cf2c..cfa5f53d4d52b 100644 --- a/test/SourceKit/Indexing/index_func_import.swift +++ b/test/SourceKit/Indexing/index_func_import.swift @@ -2,7 +2,7 @@ // RUN: %swift -emit-module -o %t/test_module.swiftmodule %S/Inputs/test_module.swift // RUN: %sourcekitd-test -req=index %s -- %s -I %t | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response import func test_module.globalFunc diff --git a/test/SourceKit/Indexing/index_implicit_vis.swift b/test/SourceKit/Indexing/index_implicit_vis.swift index cb64b69e5d4c5..e67d9758b12d2 100644 --- a/test/SourceKit/Indexing/index_implicit_vis.swift +++ b/test/SourceKit/Indexing/index_implicit_vis.swift @@ -1,4 +1,4 @@ // RUN: %sourcekitd-test -req=index %S/Inputs/implicit-vis/a.swift -- %S/Inputs/implicit-vis/a.swift %S/Inputs/implicit-vis/b.swift -o implicit_vis.o | %sed_clean > %t.a.response // RUN: %sourcekitd-test -req=index %S/Inputs/implicit-vis/b.swift -- %S/Inputs/implicit-vis/a.swift %S/Inputs/implicit-vis/b.swift -o implicit_vis.o | %sed_clean > %t.b.response -// RUN: diff -u %S/Inputs/implicit-vis/a.index.response %t.a.response -// RUN: diff -u %S/Inputs/implicit-vis/b.index.response %t.b.response +// RUN: diff --strip-trailing-cr -u %S/Inputs/implicit-vis/a.index.response %t.a.response +// RUN: diff --strip-trailing-cr -u %S/Inputs/implicit-vis/b.index.response %t.b.response diff --git a/test/SourceKit/Indexing/index_operators.swift b/test/SourceKit/Indexing/index_operators.swift index 97164d02117ca..5038bdf143b36 100644 --- a/test/SourceKit/Indexing/index_operators.swift +++ b/test/SourceKit/Indexing/index_operators.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=index %s -- -Xfrontend -serialize-diagnostics-path -Xfrontend %t.dia %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response class ClassA { init(){} diff --git a/test/SourceKit/Indexing/index_with_clang_module.swift b/test/SourceKit/Indexing/index_with_clang_module.swift index ec94e62a93628..f9504d31731a0 100644 --- a/test/SourceKit/Indexing/index_with_clang_module.swift +++ b/test/SourceKit/Indexing/index_with_clang_module.swift @@ -4,7 +4,7 @@ // RUN: %build-clang-importer-objc-overlays // RUN: %sourcekitd-test -req=index %s -- %s -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck %s +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck %s import Foo diff --git a/test/SourceKit/Indexing/index_with_swift_module.swift b/test/SourceKit/Indexing/index_with_swift_module.swift index 2327d0af965bc..3964a5758c207 100644 --- a/test/SourceKit/Indexing/index_with_swift_module.swift +++ b/test/SourceKit/Indexing/index_with_swift_module.swift @@ -4,7 +4,7 @@ // RUN: %sourcekitd-test -req=index %s -- %s -I %t | %FileCheck %s // RUN: %sourcekitd-test -req=index %t/test_module.swiftmodule | %sed_clean > %t.response -// RUN: diff -u %S/Inputs/test_module.index.response %t.response +// RUN: diff --strip-trailing-cr -u %S/Inputs/test_module.index.response %t.response import test_module diff --git a/test/SourceKit/Indexing/rdar_21602898.swift b/test/SourceKit/Indexing/rdar_21602898.swift index c55eca913022e..baeb2daa743fd 100644 --- a/test/SourceKit/Indexing/rdar_21602898.swift +++ b/test/SourceKit/Indexing/rdar_21602898.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=index %s -- -Xfrontend -serialize-diagnostics-path -Xfrontend %t.dia %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response protocol P {} class C { diff --git a/test/SourceKit/Indexing/sr_3815.swift b/test/SourceKit/Indexing/sr_3815.swift index 5202b2e7275c6..bbe0876f0bd94 100644 --- a/test/SourceKit/Indexing/sr_3815.swift +++ b/test/SourceKit/Indexing/sr_3815.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=index %s -- -Xfrontend -serialize-diagnostics-path -Xfrontend %t.dia %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response protocol P { typealias Index = Int diff --git a/test/SourceKit/Inputs/sourcekitd_path_sanitize.py b/test/SourceKit/Inputs/sourcekitd_path_sanitize.py new file mode 100644 index 0000000000000..cb90e334e7ecd --- /dev/null +++ b/test/SourceKit/Inputs/sourcekitd_path_sanitize.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# sourcekitd_path_sanitize.py - Cleans up paths from sourcekitd-test output +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + +import re +import sys + +SWIFTMODULE_BUNDLE_RE = re.compile( + r'key.filepath: ".*[/\\](.*)\.swiftmodule[/\\].*\.swiftmodule"') +SWIFTMODULE_RE = re.compile(r'key.filepath: ".*[/\\](.*)\.swiftmodule"') +SWIFT_RE = re.compile(r'key.filepath: ".*[/\\](.*)\.swift"') +PCM_RE = re.compile(r'key.filepath: ".*[/\\](.*)-[0-9A-Z]*\.pcm"') +HEADER_RE = re.compile(r' file=\\".*[/\\](.*)\.h\\"') + +try: + for line in sys.stdin.readlines(): + line = re.sub(SWIFTMODULE_BUNDLE_RE, + r'key.filepath: \1.swiftmodule', line) + line = re.sub(SWIFTMODULE_RE, r'key.filepath: \1.swiftmodule', line) + line = re.sub(SWIFT_RE, r'key.filepath: \1.swift', line) + line = re.sub(PCM_RE, r'key.filepath: \1.pcm', line) + line = re.sub(HEADER_RE, r' file=\1.h', line) + sys.stdout.write(line) +except KeyboardInterrupt: + sys.stdout.flush() diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift b/test/SourceKit/InterfaceGen/gen_clang_module.swift index b9cef439699c2..3e5d2640ad5b6 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift @@ -5,7 +5,7 @@ var x: FooClassBase // REQUIRES: objc_interop // FIXME: the test output we're comparing to is specific to macOS. -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx // RUN: %empty-directory(%t.overlays) // RUN: %empty-directory(%t) @@ -14,30 +14,30 @@ var x: FooClassBase // RUN: %target-swift-frontend -emit-module -o %t.overlays -F %S/../Inputs/libIDE-mock-sdk %S/Inputs/Foo.swift // // RUN: %sourcekitd-test -req=interface-gen -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t > %t.response +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t > %t.response // RUN: diff -u %s.response %t.response // RUN: %sourcekitd-test -req=interface-gen -module Foo.FooSub -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t > %t.sub.response +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t > %t.sub.response // RUN: diff -u %s.sub.response %t.sub.response // RUN: %sourcekitd-test -req=interface-gen -module FooHelper -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t > %t.helper.response +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t > %t.helper.response // RUN: diff -u %s.helper.response %t.helper.response // RUN: %sourcekitd-test -req=interface-gen -module FooHelper.FooHelperExplicit -- -I %t.overlays \ -// RUN: -F %S/../Inputs/libIDE-mock-sdk %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t > %t.helper.explicit.response +// RUN: -F %S/../Inputs/libIDE-mock-sdk -target %target-triple %clang-importer-sdk-nosource -I %t > %t.helper.explicit.response // RUN: diff -u %s.helper.explicit.response %t.helper.explicit.response // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=cursor -pos=205:67 | %FileCheck -check-prefix=CHECK1 %s // The cursor points to 'FooClassBase' inside the list of base classes, see 'gen_clang_module.swift.response' // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=cursor -pos=3:11 %s -- %s -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK1 %s +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK1 %s // CHECK1: source.lang.swift.ref.class ({{.*}}Foo.framework/Headers/Foo.h:147:12-147:24) // CHECK1: FooClassBase @@ -46,7 +46,7 @@ var x: FooClassBase // CHECK1-NEXT: / // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=cursor -pos=232:20 | %FileCheck -check-prefix=CHECK2 %s // The cursor points inside the interface, see 'gen_clang_module.swift.response' @@ -57,22 +57,22 @@ var x: FooClassBase // CHECK2-NEXT: / // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=find-usr -usr "c:objc(cs)FooClassDerived(im)fooInstanceFunc0" | %FileCheck -check-prefix=CHECK-USR %s // The returned line:col points inside the interface, see 'gen_clang_module.swift.response' // CHECK-USR: (232:15-232:33) // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=find-interface -module Foo -- %s -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-IFACE %s +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK-IFACE %s // CHECK-IFACE: DOC: (/) // CHECK-IFACE: ARGS: [-target x86_64-{{.*}} -sdk {{.*}} -F {{.*}}/libIDE-mock-sdk -I {{.*}}.overlays {{.*}} -module-cache-path {{.*}} ] // RUN: %sourcekitd-test -req=interface-gen-open -module Foo -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=cursor -pos=1:8 == -req=cursor -pos=1:12 \ // RUN: == -req=cursor -pos=2:10 \ // RUN: == -req=cursor -pos=3:10 | %FileCheck -check-prefix=CHECK-IMPORT %s @@ -92,8 +92,8 @@ var x: FooClassBase // CHECK-IMPORT-NEXT: FooHelper{{$}} // RUN: %sourcekitd-test -req=interface-gen -module APINotesTests -- -swift-version 4 -F %S/Inputs/mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource > %t.apinotes_swift3.response +// RUN: -target %target-triple %clang-importer-sdk-nosource > %t.apinotes_swift3.response // RUN: diff -u %s.apinotes_swift3.response %t.apinotes_swift3.response // RUN: %sourcekitd-test -req=interface-gen -module APINotesTests -- -swift-version 5 -F %S/Inputs/mock-sdk \ -// RUN: %mcp_opt -target %target-triple %clang-importer-sdk-nosource > %t.apinotes_swift4.response +// RUN: -target %target-triple %clang-importer-sdk-nosource > %t.apinotes_swift4.response // RUN: diff -u %s.apinotes_swift4.response %t.apinotes_swift4.response diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response index a904d1ab08d7f..190980edf2204 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift3.response @@ -1554,7 +1554,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Class_Container", key.offset: 410, key.length: 87, @@ -1659,7 +1658,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToGlobal_Class_Container", key.offset: 649, key.length: 105, @@ -1750,7 +1748,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift3", key.offset: 934, key.length: 117, @@ -1779,7 +1776,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift4", key.offset: 1052, key.length: 88, @@ -1853,7 +1849,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Class_Container", key.offset: 1269, key.length: 198, @@ -1974,7 +1969,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift3", key.offset: 1657, key.length: 127, @@ -2003,7 +1997,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift4", key.offset: 1785, key.length: 93, @@ -2093,7 +2086,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Typedef_Container", key.offset: 2250, key.length: 82, @@ -2169,7 +2161,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToGlobal_Typedef_Container", key.offset: 2485, key.length: 109, @@ -2260,7 +2251,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift3", key.offset: 2778, key.length: 121, @@ -2289,7 +2279,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift4", key.offset: 2900, key.length: 83, @@ -2349,7 +2338,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Typedef_Container", key.offset: 3114, key.length: 195, @@ -2456,7 +2444,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift3", key.offset: 3503, key.length: 131, @@ -2485,7 +2472,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift4", key.offset: 3635, key.length: 88, diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response index 942911aa898a2..9d8951bb7351b 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.apinotes_swift4.response @@ -1088,7 +1088,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Class_Container", key.offset: 323, key.length: 87, @@ -1255,7 +1254,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Class_Swift4", key.offset: 741, key.length: 88, @@ -1329,7 +1327,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Class_Container", key.offset: 958, key.length: 105, @@ -1434,7 +1431,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Class_Swift4", key.offset: 1253, key.length: 93, @@ -1508,7 +1504,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "GlobalToMember_Typedef_Container", key.offset: 1627, key.length: 82, @@ -1646,7 +1641,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_Typedef_Swift4", key.offset: 2045, key.length: 83, @@ -1706,7 +1700,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameContainer_Typedef_Container", key.offset: 2259, key.length: 100, @@ -1797,7 +1790,6 @@ extension MemberToMember_SameName_Typedef_Swift4 { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "MemberToMember_SameName_Typedef_Swift4", key.offset: 2553, key.length: 88, diff --git a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response index 31ff01df9b15d..429e2c8b7095a 100644 --- a/test/SourceKit/InterfaceGen/gen_clang_module.swift.response +++ b/test/SourceKit/InterfaceGen/gen_clang_module.swift.response @@ -51,11 +51,11 @@ public enum FooComparisonResult : Int { // This is ascending - case orderedAscending + case orderedAscending = -1 - case orderedSame // But this is the same. + case orderedSame = 0 // But this is the same. - case orderedDescending + case orderedDescending = 1 } /// Aaa. FooRuncingOptions. Bbb. @@ -343,9 +343,9 @@ public class FooCFType { public enum ABAuthorizationStatus : Int { - case notDetermined // deprecated, use CNAuthorizationStatusNotDetermined + case notDetermined = 0 // deprecated, use CNAuthorizationStatusNotDetermined - case restricted // deprecated, use CNAuthorizationStatusRestricted + case restricted = 1 // deprecated, use CNAuthorizationStatusRestricted } public class FooOverlayClassBase { @@ -869,2709 +869,2734 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.offset: 1036, key.length: 16 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 1055, + key.length: 2 + }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1058, + key.offset: 1063, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1063, + key.offset: 1068, key.length: 11 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 1082, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 1075, + key.offset: 1084, key.length: 25 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1105, + key.offset: 1114, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1110, + key.offset: 1119, key.length: 17 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 1139, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 1131, + key.offset: 1144, key.length: 35 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1166, + key.offset: 1179, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1173, + key.offset: 1186, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1180, + key.offset: 1193, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1200, + key.offset: 1213, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1217, + key.offset: 1230, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1224, + key.offset: 1237, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1229, + key.offset: 1242, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1239, + key.offset: 1252, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 1254, + key.offset: 1267, key.length: 18 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1276, + key.offset: 1289, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1283, + key.offset: 1296, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1290, + key.offset: 1303, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1294, + key.offset: 1307, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1307, + key.offset: 1320, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1327, + key.offset: 1340, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1338, + key.offset: 1351, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1345, + key.offset: 1358, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1352, + key.offset: 1365, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1356, + key.offset: 1369, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1370, + key.offset: 1383, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1390, + key.offset: 1403, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 1396, + key.offset: 1409, key.length: 24 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1424, + key.offset: 1437, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1431, + key.offset: 1444, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1438, + key.offset: 1451, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1456, + key.offset: 1469, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1463, + key.offset: 1476, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1467, + key.offset: 1480, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1470, + key.offset: 1483, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1481, + key.offset: 1494, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1488, + key.offset: 1501, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1492, + key.offset: 1505, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1495, + key.offset: 1508, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1507, + key.offset: 1520, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1514, + key.offset: 1527, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1526, + key.offset: 1539, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1533, + key.offset: 1546, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1538, + key.offset: 1551, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1541, + key.offset: 1554, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1548, + key.offset: 1561, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1551, + key.offset: 1564, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1561, + key.offset: 1574, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1568, + key.offset: 1581, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1578, + key.offset: 1591, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1598, + key.offset: 1611, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1619, + key.offset: 1632, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1632, + key.offset: 1645, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1639, + key.offset: 1652, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1646, + key.offset: 1659, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1664, + key.offset: 1677, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1671, + key.offset: 1684, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1675, + key.offset: 1688, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1678, + key.offset: 1691, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1689, + key.offset: 1702, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1696, + key.offset: 1709, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1700, + key.offset: 1713, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1703, + key.offset: 1716, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1715, + key.offset: 1728, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1722, + key.offset: 1735, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1734, + key.offset: 1747, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1741, + key.offset: 1754, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1746, + key.offset: 1759, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1749, + key.offset: 1762, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1756, + key.offset: 1769, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1759, + key.offset: 1772, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1769, + key.offset: 1782, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1776, + key.offset: 1789, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1786, + key.offset: 1799, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1806, + key.offset: 1819, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1818, + key.offset: 1831, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1825, + key.offset: 1838, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1832, + key.offset: 1845, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1857, + key.offset: 1870, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1864, + key.offset: 1877, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1868, + key.offset: 1881, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1871, + key.offset: 1884, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1882, + key.offset: 1895, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1889, + key.offset: 1902, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1893, + key.offset: 1906, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1896, + key.offset: 1909, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1908, + key.offset: 1921, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1915, + key.offset: 1928, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1927, + key.offset: 1940, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1934, + key.offset: 1947, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1939, + key.offset: 1952, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1942, + key.offset: 1955, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 1949, + key.offset: 1962, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 1952, + key.offset: 1965, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 1963, + key.offset: 1976, key.length: 29 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 1992, + key.offset: 2005, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 1999, + key.offset: 2012, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2009, + key.offset: 2022, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2023, + key.offset: 2036, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2030, + key.offset: 2043, key.length: 27 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2057, + key.offset: 2070, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2064, + key.offset: 2077, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2068, + key.offset: 2081, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2079, + key.offset: 2092, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2086, + key.offset: 2099, key.length: 26 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2112, + key.offset: 2125, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2119, + key.offset: 2132, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2124, + key.offset: 2137, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2133, + key.offset: 2146, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2135, + key.offset: 2148, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2138, + key.offset: 2151, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2148, + key.offset: 2161, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2155, + key.offset: 2168, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2162, + key.offset: 2175, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2167, + key.offset: 2180, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2190, + key.offset: 2203, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2193, + key.offset: 2206, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2203, + key.offset: 2216, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2209, + key.offset: 2222, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2216, + key.offset: 2229, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2221, + key.offset: 2234, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2230, + key.offset: 2243, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2232, + key.offset: 2245, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2235, + key.offset: 2248, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2242, + key.offset: 2255, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2244, + key.offset: 2257, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2247, + key.offset: 2260, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2254, + key.offset: 2267, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2256, + key.offset: 2269, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2259, + key.offset: 2272, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2267, + key.offset: 2280, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2269, + key.offset: 2282, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2272, + key.offset: 2285, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2293, + key.offset: 2306, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2305, + key.offset: 2318, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 2312, + key.offset: 2325, key.length: 46 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2359, + key.offset: 2372, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2366, + key.offset: 2379, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2371, + key.offset: 2384, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2388, + key.offset: 2401, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2390, + key.offset: 2403, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2397, + key.offset: 2410, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2407, + key.offset: 2420, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2417, + key.offset: 2430, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2424, + key.offset: 2437, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2429, + key.offset: 2442, key.length: 26 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2456, + key.offset: 2469, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2458, + key.offset: 2471, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2465, + key.offset: 2478, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2477, + key.offset: 2490, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2481, + key.offset: 2494, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2491, + key.offset: 2504, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2501, + key.offset: 2514, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2508, + key.offset: 2521, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2513, + key.offset: 2526, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2535, + key.offset: 2548, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2541, + key.offset: 2554, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2548, + key.offset: 2561, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2553, + key.offset: 2566, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 2575, + key.offset: 2588, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2582, + key.offset: 2595, key.length: 62 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2645, + key.offset: 2658, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2652, + key.offset: 2665, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2657, + key.offset: 2670, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2680, + key.offset: 2693, key.length: 42 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2723, + key.offset: 2736, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2730, + key.offset: 2743, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2735, + key.offset: 2748, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2758, + key.offset: 2771, key.length: 43 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2802, + key.offset: 2815, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2818, + key.offset: 2831, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2825, + key.offset: 2838, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2830, + key.offset: 2843, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2853, + key.offset: 2866, key.length: 43 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2897, + key.offset: 2910, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 2906, + key.offset: 2919, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 2913, + key.offset: 2926, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 2918, + key.offset: 2931, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2941, + key.offset: 2954, key.length: 37 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2978, + key.offset: 2991, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2987, + key.offset: 3000, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 2991, + key.offset: 3004, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3000, + key.offset: 3013, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3007, + key.offset: 3020, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3012, + key.offset: 3025, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3035, + key.offset: 3048, key.length: 50 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3085, + key.offset: 3098, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3092, + key.offset: 3105, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3097, + key.offset: 3110, key.length: 32 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3130, + key.offset: 3143, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3132, + key.offset: 3145, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3135, + key.offset: 3148, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3145, + key.offset: 3158, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3152, + key.offset: 3165, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3185, + key.offset: 3198, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3192, + key.offset: 3205, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3201, + key.offset: 3214, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3229, + key.offset: 3242, key.length: 30 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3263, + key.offset: 3276, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3276, + key.offset: 3289, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3281, + key.offset: 3294, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3306, + key.offset: 3319, key.length: 51 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3361, + key.offset: 3374, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3374, + key.offset: 3387, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3379, + key.offset: 3392, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 3425, + key.offset: 3438, key.length: 77 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3507, + key.offset: 3520, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3512, + key.offset: 3525, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3558, + key.offset: 3571, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3565, + key.offset: 3578, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3570, + key.offset: 3583, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3600, + key.offset: 3613, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3604, + key.offset: 3617, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3618, + key.offset: 3631, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3626, + key.offset: 3639, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3630, + key.offset: 3643, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3641, + key.offset: 3654, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3645, + key.offset: 3658, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3659, + key.offset: 3672, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3667, + key.offset: 3680, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3671, + key.offset: 3684, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3682, + key.offset: 3695, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3686, + key.offset: 3699, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3700, + key.offset: 3713, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3708, + key.offset: 3721, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3717, + key.offset: 3730, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3724, + key.offset: 3737, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3733, + key.offset: 3746, key.length: 18 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3754, + key.offset: 3767, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3775, + key.offset: 3788, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3780, + key.offset: 3793, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3786, + key.offset: 3799, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3806, + key.offset: 3819, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3811, + key.offset: 3824, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3816, + key.offset: 3829, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3844, + key.offset: 3857, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3849, + key.offset: 3862, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3854, + key.offset: 3867, key.length: 20 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3875, + key.offset: 3888, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3877, + key.offset: 3890, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3887, + key.offset: 3900, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3896, + key.offset: 3909, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3915, + key.offset: 3928, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3922, + key.offset: 3935, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3935, + key.offset: 3948, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3942, + key.offset: 3955, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3954, + key.offset: 3967, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3960, + key.offset: 3973, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3966, + key.offset: 3979, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 3969, + key.offset: 3982, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 3981, + key.offset: 3994, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 3986, + key.offset: 3999, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 3991, + key.offset: 4004, key.length: 29 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4033, + key.offset: 4046, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4038, + key.offset: 4051, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4044, + key.offset: 4057, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4049, + key.offset: 4062, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.doccomment, - key.offset: 4072, + key.offset: 4085, key.length: 33 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4105, + key.offset: 4118, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4110, + key.offset: 4123, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4116, + key.offset: 4129, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4134, + key.offset: 4147, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4148, + key.offset: 4161, key.length: 18 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4179, + key.offset: 4192, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4184, + key.offset: 4197, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4188, + key.offset: 4201, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4202, + key.offset: 4215, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4213, + key.offset: 4226, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4218, + key.offset: 4231, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4222, + key.offset: 4235, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4236, + key.offset: 4249, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4247, + key.offset: 4260, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4252, + key.offset: 4265, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4256, + key.offset: 4269, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4270, + key.offset: 4283, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4278, + key.offset: 4291, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 4294, + key.offset: 4307, key.length: 64 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4363, + key.offset: 4376, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4368, + key.offset: 4381, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4373, + key.offset: 4386, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4397, + key.offset: 4410, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4402, + key.offset: 4415, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4407, + key.offset: 4420, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4424, + key.offset: 4437, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4426, + key.offset: 4439, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4429, + key.offset: 4442, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4441, + key.offset: 4454, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4446, + key.offset: 4459, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4451, + key.offset: 4464, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4468, + key.offset: 4481, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4470, + key.offset: 4483, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4473, + key.offset: 4486, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4480, + key.offset: 4493, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4486, + key.offset: 4499, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4489, + key.offset: 4502, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4506, + key.offset: 4519, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4511, + key.offset: 4524, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4516, + key.offset: 4529, key.length: 29 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4558, + key.offset: 4571, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4563, + key.offset: 4576, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4569, + key.offset: 4582, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4574, + key.offset: 4587, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4593, + key.offset: 4606, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4600, + key.offset: 4613, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4610, + key.offset: 4623, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4626, + key.offset: 4639, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 4633, + key.offset: 4646, key.length: 31 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4665, + key.offset: 4678, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4672, + key.offset: 4685, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4676, + key.offset: 4689, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4689, + key.offset: 4702, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4697, + key.offset: 4710, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4703, + key.offset: 4716, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4710, + key.offset: 4723, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4714, + key.offset: 4727, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4727, + key.offset: 4740, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4735, + key.offset: 4748, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4741, + key.offset: 4754, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4748, + key.offset: 4761, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4752, + key.offset: 4765, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4765, + key.offset: 4778, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4773, + key.offset: 4786, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 4779, + key.offset: 4792, key.length: 39 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4818, + key.offset: 4831, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4825, + key.offset: 4838, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4829, + key.offset: 4842, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4842, + key.offset: 4855, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4851, + key.offset: 4864, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4857, + key.offset: 4870, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4864, + key.offset: 4877, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4868, + key.offset: 4881, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4881, + key.offset: 4894, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4890, + key.offset: 4903, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4896, + key.offset: 4909, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4903, + key.offset: 4916, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4907, + key.offset: 4920, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4920, + key.offset: 4933, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4936, + key.offset: 4949, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4942, + key.offset: 4955, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4949, + key.offset: 4962, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4953, + key.offset: 4966, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 4966, + key.offset: 4979, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4982, + key.offset: 4995, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 4988, + key.offset: 5001, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 4995, + key.offset: 5008, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 4999, + key.offset: 5012, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5012, + key.offset: 5025, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5019, + key.offset: 5032, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5025, + key.offset: 5038, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5032, + key.offset: 5045, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5036, + key.offset: 5049, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5049, + key.offset: 5062, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5057, + key.offset: 5070, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5063, + key.offset: 5076, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5070, + key.offset: 5083, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5074, + key.offset: 5087, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5088, + key.offset: 5101, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5096, + key.offset: 5109, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5102, + key.offset: 5115, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5109, + key.offset: 5122, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5113, + key.offset: 5126, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5127, + key.offset: 5140, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5133, + key.offset: 5146, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5139, + key.offset: 5152, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5146, + key.offset: 5159, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5150, + key.offset: 5163, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5164, + key.offset: 5177, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5172, + key.offset: 5185, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5178, + key.offset: 5191, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5185, + key.offset: 5198, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5189, + key.offset: 5202, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5204, + key.offset: 5217, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5212, + key.offset: 5225, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5218, + key.offset: 5231, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5225, + key.offset: 5238, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5229, + key.offset: 5242, key.length: 18 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5249, + key.offset: 5262, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5258, + key.offset: 5271, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5264, + key.offset: 5277, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5271, + key.offset: 5284, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5275, + key.offset: 5288, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5293, + key.offset: 5306, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5302, + key.offset: 5315, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5309, + key.offset: 5322, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5316, + key.offset: 5329, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5320, + key.offset: 5333, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5339, + key.offset: 5352, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5347, + key.offset: 5360, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5354, + key.offset: 5367, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5361, + key.offset: 5374, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5365, + key.offset: 5378, key.length: 17 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5384, + key.offset: 5397, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5392, + key.offset: 5405, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5399, + key.offset: 5412, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5406, + key.offset: 5419, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5411, + key.offset: 5424, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5431, + key.offset: 5444, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5438, + key.offset: 5451, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5443, + key.offset: 5456, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5468, + key.offset: 5481, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5475, + key.offset: 5488, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5482, + key.offset: 5495, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5505, + key.offset: 5518, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5512, + key.offset: 5525, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5516, + key.offset: 5529, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5519, + key.offset: 5532, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5530, + key.offset: 5543, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5537, + key.offset: 5550, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5549, + key.offset: 5562, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5556, + key.offset: 5569, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5561, + key.offset: 5574, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5564, + key.offset: 5577, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5574, + key.offset: 5587, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5584, + key.offset: 5597, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5604, + key.offset: 5617, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5609, + key.offset: 5622, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5614, + key.offset: 5627, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5634, + key.offset: 5647, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 5642, + key.offset: 5655, key.length: 44 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5687, + key.offset: 5700, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5697, + key.offset: 5710, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5717, + key.offset: 5730, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5722, + key.offset: 5735, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5727, + key.offset: 5740, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5747, + key.offset: 5760, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5757, + key.offset: 5770, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5762, + key.offset: 5775, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5767, + key.offset: 5780, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5788, + key.offset: 5801, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5796, + key.offset: 5809, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5806, + key.offset: 5819, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5826, + key.offset: 5839, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5831, + key.offset: 5844, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5836, + key.offset: 5849, key.length: 14 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5856, + key.offset: 5869, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5864, + key.offset: 5877, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5871, + key.offset: 5884, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5880, + key.offset: 5893, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5899, + key.offset: 5912, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5904, + key.offset: 5917, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5910, + key.offset: 5923, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5934, + key.offset: 5947, key.length: 13 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 5953, + key.offset: 5966, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 5958, + key.offset: 5971, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 5964, + key.offset: 5977, key.length: 25 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 5992, + key.offset: 6005, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6012, + key.offset: 6025, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6028, + key.offset: 6041, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6033, + key.offset: 6046, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6037, + key.offset: 6050, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6049, + key.offset: 6062, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6065, + key.offset: 6078, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6081, + key.offset: 6094, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6086, + key.offset: 6099, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6090, + key.offset: 6103, key.length: 16 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6108, + key.offset: 6121, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6124, + key.offset: 6137, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6129, + key.offset: 6142, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6133, + key.offset: 6146, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6145, + key.offset: 6158, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6155, + key.offset: 6168, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6160, + key.offset: 6173, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6164, + key.offset: 6177, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6175, + key.offset: 6188, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6185, + key.offset: 6198, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6190, + key.offset: 6203, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6194, + key.offset: 6207, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6204, + key.offset: 6217, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6214, + key.offset: 6227, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6219, + key.offset: 6232, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6224, + key.offset: 6237, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6228, + key.offset: 6241, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6237, + key.offset: 6250, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6253, + key.offset: 6266, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6258, + key.offset: 6271, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6262, + key.offset: 6275, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6270, + key.offset: 6283, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6279, + key.offset: 6292, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6284, + key.offset: 6297, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6290, + key.offset: 6303, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6314, + key.offset: 6327, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6334, + key.offset: 6347, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6341, + key.offset: 6354, key.length: 11 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6353, + key.offset: 6366, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6359, + key.offset: 6372, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6363, + key.offset: 6376, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6366, + key.offset: 6379, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6383, + key.offset: 6396, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6397, + key.offset: 6410, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6409, + key.offset: 6422, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 6418, + key.offset: 6431, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6427, + key.offset: 6440, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6432, + key.offset: 6445, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6437, + key.offset: 6450, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6460, + key.offset: 6473, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6471, + key.offset: 6484, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.number, - key.offset: 6475, + key.offset: 6488, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6488, + key.offset: 6501, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6493, + key.offset: 6506, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6498, + key.offset: 6511, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6533, + key.offset: 6546, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6544, + key.offset: 6557, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6549, + key.offset: 6562, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.number, - key.offset: 6561, + key.offset: 6574, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6567, + key.offset: 6580, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 6576, + key.offset: 6589, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6585, + key.offset: 6598, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6590, + key.offset: 6603, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6595, + key.offset: 6608, key.length: 25 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6626, + key.offset: 6639, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6633, + key.offset: 6646, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6639, + key.offset: 6652, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6654, + key.offset: 6667, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6668, + key.offset: 6681, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6680, + key.offset: 6693, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 6689, + key.offset: 6702, key.length: 27 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6718, + key.offset: 6731, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6725, + key.offset: 6738, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6730, + key.offset: 6743, key.length: 21 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 6754, + key.offset: 6767, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6770, + key.offset: 6783, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6775, + key.offset: 6788, key.length: 13 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 6804, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 6789, + key.offset: 6806, key.length: 54 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6848, + key.offset: 6865, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6853, + key.offset: 6870, key.length: 10 }, + { + key.kind: source.lang.swift.syntaxtype.number, + key.offset: 6883, + key.length: 1 + }, { key.kind: source.lang.swift.syntaxtype.comment, - key.offset: 6864, + key.offset: 6885, key.length: 51 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6917, + key.offset: 6938, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6924, + key.offset: 6945, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6930, + key.offset: 6951, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6957, + key.offset: 6978, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6964, + key.offset: 6985, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6969, + key.offset: 6990, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 6976, + key.offset: 6997, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 6983, + key.offset: 7004, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 6989, + key.offset: 7010, key.length: 22 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 7014, + key.offset: 7035, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.typeidentifier, - key.offset: 7018, + key.offset: 7039, key.length: 19 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 7045, + key.offset: 7066, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.attribute.builtin, - key.offset: 7054, + key.offset: 7075, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 7061, + key.offset: 7082, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 7066, + key.offset: 7087, key.length: 1 } ] @@ -3725,488 +3750,488 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.ref.protocol, - key.offset: 1200, + key.offset: 1213, key.length: 9, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1239, + key.offset: 1252, key.length: 3, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1307, + key.offset: 1320, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1370, + key.offset: 1383, key.length: 17 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1470, + key.offset: 1483, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1495, + key.offset: 1508, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1541, + key.offset: 1554, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1551, + key.offset: 1564, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1598, + key.offset: 1611, key.length: 20, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1619, + key.offset: 1632, key.length: 10 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1678, + key.offset: 1691, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1703, + key.offset: 1716, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1749, + key.offset: 1762, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1759, + key.offset: 1772, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1806, + key.offset: 1819, key.length: 10 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1871, + key.offset: 1884, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1896, + key.offset: 1909, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1942, + key.offset: 1955, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 1952, + key.offset: 1965, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2023, + key.offset: 2036, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2079, + key.offset: 2092, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2138, + key.offset: 2151, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2148, + key.offset: 2161, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2193, + key.offset: 2206, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2203, + key.offset: 2216, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2235, + key.offset: 2248, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2247, + key.offset: 2260, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2259, + key.offset: 2272, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2272, + key.offset: 2285, key.length: 20, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2293, + key.offset: 2306, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2305, + key.offset: 2318, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2397, + key.offset: 2410, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2407, + key.offset: 2420, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2481, + key.offset: 2494, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 2491, + key.offset: 2504, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.enum, - key.offset: 2535, + key.offset: 2548, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.enum, - key.offset: 2575, + key.offset: 2588, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3135, + key.offset: 3148, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3145, + key.offset: 3158, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3618, + key.offset: 3631, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3659, + key.offset: 3672, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3700, + key.offset: 3713, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.protocol, - key.offset: 3754, + key.offset: 3767, key.length: 15 }, { key.kind: source.lang.swift.ref.class, - key.offset: 3896, + key.offset: 3909, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 3969, + key.offset: 3982, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.class, - key.offset: 4134, + key.offset: 4147, key.length: 12 }, { key.kind: source.lang.swift.ref.protocol, - key.offset: 4148, + key.offset: 4161, key.length: 18 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4202, + key.offset: 4215, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4236, + key.offset: 4249, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4270, + key.offset: 4283, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4429, + key.offset: 4442, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4473, + key.offset: 4486, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4489, + key.offset: 4502, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4626, + key.offset: 4639, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4689, + key.offset: 4702, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4727, + key.offset: 4740, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4765, + key.offset: 4778, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4842, + key.offset: 4855, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 4881, + key.offset: 4894, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.typealias, - key.offset: 4920, + key.offset: 4933, key.length: 13 }, { key.kind: source.lang.swift.ref.typealias, - key.offset: 4966, + key.offset: 4979, key.length: 13 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5012, + key.offset: 5025, key.length: 4, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5049, + key.offset: 5062, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5088, + key.offset: 5101, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5127, + key.offset: 5140, key.length: 3, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5164, + key.offset: 5177, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5204, + key.offset: 5217, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5249, + key.offset: 5262, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5293, + key.offset: 5306, key.length: 6, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5339, + key.offset: 5352, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5384, + key.offset: 5397, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5519, + key.offset: 5532, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 5564, + key.offset: 5577, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.class, - key.offset: 5584, + key.offset: 5597, key.length: 12 }, { key.kind: source.lang.swift.ref.class, - key.offset: 5697, + key.offset: 5710, key.length: 12 }, { key.kind: source.lang.swift.ref.class, - key.offset: 5806, + key.offset: 5819, key.length: 12 }, { key.kind: source.lang.swift.ref.protocol, - key.offset: 5934, + key.offset: 5947, key.length: 13 }, { key.kind: source.lang.swift.ref.class, - key.offset: 5992, + key.offset: 6005, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 6270, + key.offset: 6283, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.class, - key.offset: 6314, + key.offset: 6327, key.length: 12 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 6366, + key.offset: 6379, key.length: 5, key.is_system: 1 }, { key.kind: source.lang.swift.ref.struct, - key.offset: 6754, + key.offset: 6767, key.length: 3, key.is_system: 1 }, { key.kind: source.lang.swift.ref.module, - key.offset: 7014, + key.offset: 7035, key.length: 3 }, { key.kind: source.lang.swift.ref.class, - key.offset: 7018, + key.offset: 7039, key.length: 19 } ] @@ -4651,11 +4676,11 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.name: "FooComparisonResult", key.offset: 963, - key.length: 166, + key.length: 179, key.nameoffset: 968, key.namelength: 19, key.bodyoffset: 995, - key.bodylength: 133, + key.bodylength: 146, key.docoffset: 919, key.doclength: 37, key.inheritedtypes: [ @@ -4681,7 +4706,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.enumcase, key.offset: 1031, - key.length: 21, + key.length: 26, key.nameoffset: 0, key.namelength: 0, key.substructure: [ @@ -4690,16 +4715,23 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.name: "orderedAscending", key.offset: 1036, - key.length: 16, + key.length: 21, key.nameoffset: 1036, - key.namelength: 16 + key.namelength: 16, + key.elements: [ + { + key.kind: source.lang.swift.structure.elem.init_expr, + key.offset: 1055, + key.length: 2 + } + ] } ] }, { key.kind: source.lang.swift.decl.enumcase, - key.offset: 1058, - key.length: 16, + key.offset: 1063, + key.length: 20, key.nameoffset: 0, key.namelength: 0, key.substructure: [ @@ -4707,17 +4739,24 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.enumelement, key.accessibility: source.lang.swift.accessibility.public, key.name: "orderedSame", - key.offset: 1063, - key.length: 11, - key.nameoffset: 1063, - key.namelength: 11 + key.offset: 1068, + key.length: 15, + key.nameoffset: 1068, + key.namelength: 11, + key.elements: [ + { + key.kind: source.lang.swift.structure.elem.init_expr, + key.offset: 1082, + key.length: 1 + } + ] } ] }, { key.kind: source.lang.swift.decl.enumcase, - key.offset: 1105, - key.length: 22, + key.offset: 1114, + key.length: 26, key.nameoffset: 0, key.namelength: 0, key.substructure: [ @@ -4725,10 +4764,17 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.enumelement, key.accessibility: source.lang.swift.accessibility.public, key.name: "orderedDescending", - key.offset: 1110, - key.length: 17, - key.nameoffset: 1110, - key.namelength: 17 + key.offset: 1119, + key.length: 21, + key.nameoffset: 1119, + key.namelength: 17, + key.elements: [ + { + key.kind: source.lang.swift.structure.elem.init_expr, + key.offset: 1139, + key.length: 1 + } + ] } ] } @@ -4738,13 +4784,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.struct, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooRuncingOptions", - key.offset: 1173, + key.offset: 1186, key.length: 249, - key.nameoffset: 1180, + key.nameoffset: 1193, key.namelength: 17, - key.bodyoffset: 1211, + key.bodyoffset: 1224, key.bodylength: 210, - key.docoffset: 1131, + key.docoffset: 1144, key.doclength: 35, key.inheritedtypes: [ { @@ -4753,7 +4799,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 1166, + key.offset: 1179, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4761,7 +4807,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 1200, + key.offset: 1213, key.length: 9 } ], @@ -4770,13 +4816,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(rawValue:)", - key.offset: 1224, + key.offset: 1237, key.length: 19, - key.nameoffset: 1224, + key.nameoffset: 1237, key.namelength: 19, key.attributes: [ { - key.offset: 1217, + key.offset: 1230, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4785,10 +4831,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "rawValue", - key.offset: 1229, + key.offset: 1242, key.length: 13, key.typename: "Int", - key.nameoffset: 1229, + key.nameoffset: 1242, key.namelength: 8 } ] @@ -4797,16 +4843,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.static, key.accessibility: source.lang.swift.accessibility.public, key.name: "enableMince", - key.offset: 1283, + key.offset: 1296, key.length: 49, key.typename: "FooRuncingOptions", - key.nameoffset: 1294, + key.nameoffset: 1307, key.namelength: 11, - key.bodyoffset: 1326, + key.bodyoffset: 1339, key.bodylength: 5, key.attributes: [ { - key.offset: 1276, + key.offset: 1289, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4816,16 +4862,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.static, key.accessibility: source.lang.swift.accessibility.public, key.name: "enableQuince", - key.offset: 1345, + key.offset: 1358, key.length: 50, key.typename: "FooRuncingOptions", - key.nameoffset: 1356, + key.nameoffset: 1369, key.namelength: 12, - key.bodyoffset: 1389, + key.bodyoffset: 1402, key.bodylength: 5, key.attributes: [ { - key.offset: 1338, + key.offset: 1351, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4837,15 +4883,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.struct, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooStruct1", - key.offset: 1431, + key.offset: 1444, key.length: 129, - key.nameoffset: 1438, + key.nameoffset: 1451, key.namelength: 10, - key.bodyoffset: 1450, + key.bodyoffset: 1463, key.bodylength: 109, key.attributes: [ { - key.offset: 1424, + key.offset: 1437, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4856,14 +4902,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "x", - key.offset: 1463, + key.offset: 1476, key.length: 12, key.typename: "Int32", - key.nameoffset: 1467, + key.nameoffset: 1480, key.namelength: 1, key.attributes: [ { - key.offset: 1456, + key.offset: 1469, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4874,14 +4920,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "y", - key.offset: 1488, + key.offset: 1501, key.length: 13, key.typename: "Double", - key.nameoffset: 1492, + key.nameoffset: 1505, key.namelength: 1, key.attributes: [ { - key.offset: 1481, + key.offset: 1494, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4891,13 +4937,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init()", - key.offset: 1514, + key.offset: 1527, key.length: 6, - key.nameoffset: 1514, + key.nameoffset: 1527, key.namelength: 6, key.attributes: [ { - key.offset: 1507, + key.offset: 1520, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4907,13 +4953,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(x:y:)", - key.offset: 1533, + key.offset: 1546, key.length: 25, - key.nameoffset: 1533, + key.nameoffset: 1546, key.namelength: 25, key.attributes: [ { - key.offset: 1526, + key.offset: 1539, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4922,19 +4968,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "x", - key.offset: 1538, + key.offset: 1551, key.length: 8, key.typename: "Int32", - key.nameoffset: 1538, + key.nameoffset: 1551, key.namelength: 1 }, { key.kind: source.lang.swift.decl.var.parameter, key.name: "y", - key.offset: 1548, + key.offset: 1561, key.length: 9, key.typename: "Double", - key.nameoffset: 1548, + key.nameoffset: 1561, key.namelength: 1 } ] @@ -4945,13 +4991,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.typealias, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooStruct1Pointer", - key.offset: 1568, + key.offset: 1581, key.length: 62, - key.nameoffset: 1578, + key.nameoffset: 1591, key.namelength: 17, key.attributes: [ { - key.offset: 1561, + key.offset: 1574, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4961,15 +5007,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.struct, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooStruct2", - key.offset: 1639, + key.offset: 1652, key.length: 129, - key.nameoffset: 1646, + key.nameoffset: 1659, key.namelength: 10, - key.bodyoffset: 1658, + key.bodyoffset: 1671, key.bodylength: 109, key.attributes: [ { - key.offset: 1632, + key.offset: 1645, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4980,14 +5026,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "x", - key.offset: 1671, + key.offset: 1684, key.length: 12, key.typename: "Int32", - key.nameoffset: 1675, + key.nameoffset: 1688, key.namelength: 1, key.attributes: [ { - key.offset: 1664, + key.offset: 1677, key.length: 6, key.attribute: source.decl.attribute.public } @@ -4998,14 +5044,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "y", - key.offset: 1696, + key.offset: 1709, key.length: 13, key.typename: "Double", - key.nameoffset: 1700, + key.nameoffset: 1713, key.namelength: 1, key.attributes: [ { - key.offset: 1689, + key.offset: 1702, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5015,13 +5061,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init()", - key.offset: 1722, + key.offset: 1735, key.length: 6, - key.nameoffset: 1722, + key.nameoffset: 1735, key.namelength: 6, key.attributes: [ { - key.offset: 1715, + key.offset: 1728, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5031,13 +5077,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(x:y:)", - key.offset: 1741, + key.offset: 1754, key.length: 25, - key.nameoffset: 1741, + key.nameoffset: 1754, key.namelength: 25, key.attributes: [ { - key.offset: 1734, + key.offset: 1747, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5046,19 +5092,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "x", - key.offset: 1746, + key.offset: 1759, key.length: 8, key.typename: "Int32", - key.nameoffset: 1746, + key.nameoffset: 1759, key.namelength: 1 }, { key.kind: source.lang.swift.decl.var.parameter, key.name: "y", - key.offset: 1756, + key.offset: 1769, key.length: 9, key.typename: "Double", - key.nameoffset: 1756, + key.nameoffset: 1769, key.namelength: 1 } ] @@ -5069,13 +5115,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.typealias, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooStructTypedef1", - key.offset: 1776, + key.offset: 1789, key.length: 40, - key.nameoffset: 1786, + key.nameoffset: 1799, key.namelength: 17, key.attributes: [ { - key.offset: 1769, + key.offset: 1782, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5085,15 +5131,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.struct, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooStructTypedef2", - key.offset: 1825, + key.offset: 1838, key.length: 136, - key.nameoffset: 1832, + key.nameoffset: 1845, key.namelength: 17, - key.bodyoffset: 1851, + key.bodyoffset: 1864, key.bodylength: 109, key.attributes: [ { - key.offset: 1818, + key.offset: 1831, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5104,14 +5150,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "x", - key.offset: 1864, + key.offset: 1877, key.length: 12, key.typename: "Int32", - key.nameoffset: 1868, + key.nameoffset: 1881, key.namelength: 1, key.attributes: [ { - key.offset: 1857, + key.offset: 1870, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5122,14 +5168,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "y", - key.offset: 1889, + key.offset: 1902, key.length: 13, key.typename: "Double", - key.nameoffset: 1893, + key.nameoffset: 1906, key.namelength: 1, key.attributes: [ { - key.offset: 1882, + key.offset: 1895, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5139,13 +5185,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init()", - key.offset: 1915, + key.offset: 1928, key.length: 6, - key.nameoffset: 1915, + key.nameoffset: 1928, key.namelength: 6, key.attributes: [ { - key.offset: 1908, + key.offset: 1921, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5155,13 +5201,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(x:y:)", - key.offset: 1934, + key.offset: 1947, key.length: 25, - key.nameoffset: 1934, + key.nameoffset: 1947, key.namelength: 25, key.attributes: [ { - key.offset: 1927, + key.offset: 1940, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5170,19 +5216,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "x", - key.offset: 1939, + key.offset: 1952, key.length: 8, key.typename: "Int32", - key.nameoffset: 1939, + key.nameoffset: 1952, key.namelength: 1 }, { key.kind: source.lang.swift.decl.var.parameter, key.name: "y", - key.offset: 1949, + key.offset: 1962, key.length: 9, key.typename: "Double", - key.nameoffset: 1949, + key.nameoffset: 1962, key.namelength: 1 } ] @@ -5193,15 +5239,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.typealias, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooTypedef1", - key.offset: 1999, + key.offset: 2012, key.length: 29, - key.nameoffset: 2009, + key.nameoffset: 2022, key.namelength: 11, - key.docoffset: 1963, + key.docoffset: 1976, key.doclength: 29, key.attributes: [ { - key.offset: 1992, + key.offset: 2005, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5212,16 +5258,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "fooIntVar", - key.offset: 2064, + key.offset: 2077, key.length: 20, key.typename: "Int32", - key.nameoffset: 2068, + key.nameoffset: 2081, key.namelength: 9, - key.docoffset: 2030, + key.docoffset: 2043, key.doclength: 27, key.attributes: [ { - key.offset: 2057, + key.offset: 2070, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5231,16 +5277,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFunc1(_:)", - key.offset: 2119, + key.offset: 2132, key.length: 34, key.typename: "Int32", - key.nameoffset: 2124, + key.nameoffset: 2137, key.namelength: 20, - key.docoffset: 2086, + key.docoffset: 2099, key.doclength: 26, key.attributes: [ { - key.offset: 2112, + key.offset: 2125, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5249,7 +5295,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "a", - key.offset: 2133, + key.offset: 2146, key.length: 10, key.typename: "Int32", key.nameoffset: 0, @@ -5261,14 +5307,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFunc1AnonymousParam(_:)", - key.offset: 2162, + key.offset: 2175, key.length: 46, key.typename: "Int32", - key.nameoffset: 2167, + key.nameoffset: 2180, key.namelength: 32, key.attributes: [ { - key.offset: 2155, + key.offset: 2168, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5276,7 +5322,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.substructure: [ { key.kind: source.lang.swift.decl.var.parameter, - key.offset: 2190, + key.offset: 2203, key.length: 8, key.typename: "Int32", key.nameoffset: 0, @@ -5288,14 +5334,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFunc3(_:_:_:_:)", - key.offset: 2216, + key.offset: 2229, key.length: 94, key.typename: "Int32", - key.nameoffset: 2221, + key.nameoffset: 2234, key.namelength: 80, key.attributes: [ { - key.offset: 2209, + key.offset: 2222, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5304,7 +5350,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "a", - key.offset: 2230, + key.offset: 2243, key.length: 10, key.typename: "Int32", key.nameoffset: 0, @@ -5313,7 +5359,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "b", - key.offset: 2242, + key.offset: 2255, key.length: 10, key.typename: "Float", key.nameoffset: 0, @@ -5322,7 +5368,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "c", - key.offset: 2254, + key.offset: 2267, key.length: 11, key.typename: "Double", key.nameoffset: 0, @@ -5331,7 +5377,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "d", - key.offset: 2267, + key.offset: 2280, key.length: 33, key.typename: "UnsafeMutablePointer!", key.nameoffset: 0, @@ -5343,13 +5389,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithBlock(_:)", - key.offset: 2366, + key.offset: 2379, key.length: 49, - key.nameoffset: 2371, + key.nameoffset: 2384, key.namelength: 44, key.attributes: [ { - key.offset: 2359, + key.offset: 2372, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5358,7 +5404,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "blk", - key.offset: 2388, + key.offset: 2401, key.length: 26, key.typename: "((Float) -> Int32)!", key.nameoffset: 0, @@ -5370,13 +5416,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithFunctionPointer(_:)", - key.offset: 2424, + key.offset: 2437, key.length: 75, - key.nameoffset: 2429, + key.nameoffset: 2442, key.namelength: 70, key.attributes: [ { - key.offset: 2417, + key.offset: 2430, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5385,7 +5431,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "fptr", - key.offset: 2456, + key.offset: 2469, key.length: 42, key.typename: "(@convention(c) (Float) -> Int32)!", key.nameoffset: 0, @@ -5397,14 +5443,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncNoreturn1()", - key.offset: 2508, + key.offset: 2521, key.length: 32, key.typename: "Never", - key.nameoffset: 2513, + key.nameoffset: 2526, key.namelength: 18, key.attributes: [ { - key.offset: 2501, + key.offset: 2514, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5414,14 +5460,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncNoreturn2()", - key.offset: 2548, + key.offset: 2561, key.length: 32, key.typename: "Never", - key.nameoffset: 2553, + key.nameoffset: 2566, key.namelength: 18, key.attributes: [ { - key.offset: 2541, + key.offset: 2554, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5431,15 +5477,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithComment1()", - key.offset: 2652, + key.offset: 2665, key.length: 26, - key.nameoffset: 2657, + key.nameoffset: 2670, key.namelength: 21, - key.docoffset: 2582, + key.docoffset: 2595, key.doclength: 62, key.attributes: [ { - key.offset: 2645, + key.offset: 2658, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5449,15 +5495,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithComment2()", - key.offset: 2730, + key.offset: 2743, key.length: 26, - key.nameoffset: 2735, + key.nameoffset: 2748, key.namelength: 21, - key.docoffset: 2680, + key.docoffset: 2693, key.doclength: 42, key.attributes: [ { - key.offset: 2723, + key.offset: 2736, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5467,15 +5513,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithComment3()", - key.offset: 2825, + key.offset: 2838, key.length: 26, - key.nameoffset: 2830, + key.nameoffset: 2843, key.namelength: 21, - key.docoffset: 2758, + key.docoffset: 2771, key.doclength: 59, key.attributes: [ { - key.offset: 2818, + key.offset: 2831, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5485,15 +5531,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithComment4()", - key.offset: 2913, + key.offset: 2926, key.length: 26, - key.nameoffset: 2918, + key.nameoffset: 2931, key.namelength: 21, - key.docoffset: 2853, + key.docoffset: 2866, key.doclength: 53, key.attributes: [ { - key.offset: 2906, + key.offset: 2919, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5503,15 +5549,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooFuncWithComment5()", - key.offset: 3007, + key.offset: 3020, key.length: 26, - key.nameoffset: 3012, + key.nameoffset: 3025, key.namelength: 21, - key.docoffset: 2941, + key.docoffset: 2954, key.doclength: 59, key.attributes: [ { - key.offset: 3000, + key.offset: 3013, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5521,16 +5567,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "redeclaredInMultipleModulesFunc1(_:)", - key.offset: 3092, + key.offset: 3105, key.length: 58, key.typename: "Int32", - key.nameoffset: 3097, + key.nameoffset: 3110, key.namelength: 44, - key.docoffset: 3035, + key.docoffset: 3048, key.doclength: 50, key.attributes: [ { - key.offset: 3085, + key.offset: 3098, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5539,7 +5585,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "a", - key.offset: 3130, + key.offset: 3143, key.length: 10, key.typename: "Int32", key.nameoffset: 0, @@ -5551,18 +5597,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.protocol, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooProtocolBase", - key.offset: 3192, + key.offset: 3205, key.length: 523, key.runtime_name: "_TtP4main15FooProtocolBase_", - key.nameoffset: 3201, + key.nameoffset: 3214, key.namelength: 15, - key.bodyoffset: 3218, + key.bodyoffset: 3231, key.bodylength: 496, - key.docoffset: 3152, + key.docoffset: 3165, key.doclength: 33, key.attributes: [ { - key.offset: 3185, + key.offset: 3198, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5572,42 +5618,42 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooProtoFunc()", - key.offset: 3276, + key.offset: 3289, key.length: 19, - key.nameoffset: 3281, + key.nameoffset: 3294, key.namelength: 14, - key.docoffset: 3229, + key.docoffset: 3242, key.doclength: 43 }, { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooProtoFuncWithExtraIndentation1()", - key.offset: 3374, + key.offset: 3387, key.length: 40, - key.nameoffset: 3379, + key.nameoffset: 3392, key.namelength: 35, - key.docoffset: 3306, + key.docoffset: 3319, key.doclength: 64 }, { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooProtoFuncWithExtraIndentation2()", - key.offset: 3507, + key.offset: 3520, key.length: 40, - key.nameoffset: 3512, + key.nameoffset: 3525, key.namelength: 35, - key.docoffset: 3425, + key.docoffset: 3438, key.doclength: 77 }, { key.kind: source.lang.swift.decl.function.method.static, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooProtoClassFunc()", - key.offset: 3558, + key.offset: 3571, key.length: 31, - key.nameoffset: 3570, + key.nameoffset: 3583, key.namelength: 19 }, { @@ -5615,12 +5661,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "fooProperty1", - key.offset: 3600, + key.offset: 3613, key.length: 35, key.typename: "Int32", - key.nameoffset: 3604, + key.nameoffset: 3617, key.namelength: 12, - key.bodyoffset: 3625, + key.bodyoffset: 3638, key.bodylength: 9 }, { @@ -5628,24 +5674,24 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "fooProperty2", - key.offset: 3641, + key.offset: 3654, key.length: 35, key.typename: "Int32", - key.nameoffset: 3645, + key.nameoffset: 3658, key.namelength: 12, - key.bodyoffset: 3666, + key.bodyoffset: 3679, key.bodylength: 9 }, { key.kind: source.lang.swift.decl.var.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "fooProperty3", - key.offset: 3682, + key.offset: 3695, key.length: 31, key.typename: "Int32", - key.nameoffset: 3686, + key.nameoffset: 3699, key.namelength: 12, - key.bodyoffset: 3707, + key.bodyoffset: 3720, key.bodylength: 5 } ] @@ -5654,12 +5700,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.protocol, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooProtocolDerived", - key.offset: 3724, + key.offset: 3737, key.length: 49, key.runtime_name: "_TtP4main18FooProtocolDerived_", - key.nameoffset: 3733, + key.nameoffset: 3746, key.namelength: 18, - key.bodyoffset: 3771, + key.bodyoffset: 3784, key.bodylength: 1, key.inheritedtypes: [ { @@ -5668,7 +5714,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 3717, + key.offset: 3730, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5676,7 +5722,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 3754, + key.offset: 3767, key.length: 15 } ] @@ -5685,16 +5731,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "FooClassBase", - key.offset: 3780, + key.offset: 3793, key.length: 290, key.runtime_name: "_TtC4main12FooClassBase", - key.nameoffset: 3786, + key.nameoffset: 3799, key.namelength: 12, - key.bodyoffset: 3800, + key.bodyoffset: 3813, key.bodylength: 269, key.attributes: [ { - key.offset: 3775, + key.offset: 3788, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5704,13 +5750,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooBaseInstanceFunc0()", - key.offset: 3811, + key.offset: 3824, key.length: 27, - key.nameoffset: 3816, + key.nameoffset: 3829, key.namelength: 22, key.attributes: [ { - key.offset: 3806, + key.offset: 3819, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5720,14 +5766,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooBaseInstanceFunc1(_:)", - key.offset: 3849, + key.offset: 3862, key.length: 60, key.typename: "FooClassBase!", - key.nameoffset: 3854, + key.nameoffset: 3867, key.namelength: 38, key.attributes: [ { - key.offset: 3844, + key.offset: 3857, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5736,7 +5782,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "anObject", - key.offset: 3875, + key.offset: 3888, key.length: 16, key.typename: "Any!", key.nameoffset: 0, @@ -5748,13 +5794,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init()", - key.offset: 3922, + key.offset: 3935, key.length: 7, - key.nameoffset: 3922, + key.nameoffset: 3935, key.namelength: 7, key.attributes: [ { - key.offset: 3915, + key.offset: 3928, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5764,18 +5810,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(float:)", - key.offset: 3954, + key.offset: 3967, key.length: 21, - key.nameoffset: 3954, + key.nameoffset: 3967, key.namelength: 21, key.attributes: [ { - key.offset: 3942, + key.offset: 3955, key.length: 11, key.attribute: source.decl.attribute.convenience }, { - key.offset: 3935, + key.offset: 3948, key.length: 6, key.attribute: source.decl.attribute.public } @@ -5784,10 +5830,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "f", - key.offset: 3960, + key.offset: 3973, key.length: 14, key.typename: "Float", - key.nameoffset: 3960, + key.nameoffset: 3973, key.namelength: 5 } ] @@ -5796,13 +5842,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooBaseInstanceFuncOverridden()", - key.offset: 3986, + key.offset: 3999, key.length: 36, - key.nameoffset: 3991, + key.nameoffset: 4004, key.namelength: 31, key.attributes: [ { - key.offset: 3981, + key.offset: 3994, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5812,13 +5858,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooBaseClassFunc0()", - key.offset: 4038, + key.offset: 4051, key.length: 30, - key.nameoffset: 4049, + key.nameoffset: 4062, key.namelength: 19, key.attributes: [ { - key.offset: 4033, + key.offset: 4046, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5830,14 +5876,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "FooClassDerived", - key.offset: 4110, + key.offset: 4123, key.length: 481, key.runtime_name: "_TtC4main15FooClassDerived", - key.nameoffset: 4116, + key.nameoffset: 4129, key.namelength: 15, - key.bodyoffset: 4168, + key.bodyoffset: 4181, key.bodylength: 422, - key.docoffset: 4072, + key.docoffset: 4085, key.doclength: 33, key.inheritedtypes: [ { @@ -5849,7 +5895,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 4105, + key.offset: 4118, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5857,12 +5903,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 4134, + key.offset: 4147, key.length: 12 }, { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 4148, + key.offset: 4161, key.length: 18 } ], @@ -5872,14 +5918,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "fooProperty1", - key.offset: 4184, + key.offset: 4197, key.length: 23, key.typename: "Int32", - key.nameoffset: 4188, + key.nameoffset: 4201, key.namelength: 12, key.attributes: [ { - key.offset: 4179, + key.offset: 4192, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5890,14 +5936,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "fooProperty2", - key.offset: 4218, + key.offset: 4231, key.length: 23, key.typename: "Int32", - key.nameoffset: 4222, + key.nameoffset: 4235, key.namelength: 12, key.attributes: [ { - key.offset: 4213, + key.offset: 4226, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5907,16 +5953,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooProperty3", - key.offset: 4252, + key.offset: 4265, key.length: 31, key.typename: "Int32", - key.nameoffset: 4256, + key.nameoffset: 4269, key.namelength: 12, - key.bodyoffset: 4277, + key.bodyoffset: 4290, key.bodylength: 5, key.attributes: [ { - key.offset: 4247, + key.offset: 4260, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5926,13 +5972,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooInstanceFunc0()", - key.offset: 4368, + key.offset: 4381, key.length: 23, - key.nameoffset: 4373, + key.nameoffset: 4386, key.namelength: 18, key.attributes: [ { - key.offset: 4363, + key.offset: 4376, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5942,13 +5988,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooInstanceFunc1(_:)", - key.offset: 4402, + key.offset: 4415, key.length: 33, - key.nameoffset: 4407, + key.nameoffset: 4420, key.namelength: 28, key.attributes: [ { - key.offset: 4397, + key.offset: 4410, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5957,7 +6003,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "a", - key.offset: 4424, + key.offset: 4437, key.length: 10, key.typename: "Int32", key.nameoffset: 0, @@ -5969,13 +6015,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooInstanceFunc2(_:withB:)", - key.offset: 4446, + key.offset: 4459, key.length: 49, - key.nameoffset: 4451, + key.nameoffset: 4464, key.namelength: 44, key.attributes: [ { - key.offset: 4441, + key.offset: 4454, key.length: 4, key.attribute: source.decl.attribute.open } @@ -5984,7 +6030,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "a", - key.offset: 4468, + key.offset: 4481, key.length: 10, key.typename: "Int32", key.nameoffset: 0, @@ -5993,10 +6039,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "b", - key.offset: 4480, + key.offset: 4493, key.length: 14, key.typename: "Int32", - key.nameoffset: 4480, + key.nameoffset: 4493, key.namelength: 5 } ] @@ -6005,13 +6051,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooBaseInstanceFuncOverridden()", - key.offset: 4511, + key.offset: 4524, key.length: 36, - key.nameoffset: 4516, + key.nameoffset: 4529, key.namelength: 31, key.attributes: [ { - key.offset: 4506, + key.offset: 4519, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6021,13 +6067,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "fooClassFunc0()", - key.offset: 4563, + key.offset: 4576, key.length: 26, - key.nameoffset: 4574, + key.nameoffset: 4587, key.namelength: 15, key.attributes: [ { - key.offset: 4558, + key.offset: 4571, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6039,13 +6085,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.typealias, key.accessibility: source.lang.swift.accessibility.public, key.name: "typedef_int_t", - key.offset: 4600, + key.offset: 4613, key.length: 31, - key.nameoffset: 4610, + key.nameoffset: 4623, key.namelength: 13, key.attributes: [ { - key.offset: 4593, + key.offset: 4606, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6055,16 +6101,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_1", - key.offset: 4672, + key.offset: 4685, key.length: 30, key.typename: "Int32", - key.nameoffset: 4676, + key.nameoffset: 4689, key.namelength: 11, - key.bodyoffset: 4696, + key.bodyoffset: 4709, key.bodylength: 5, key.attributes: [ { - key.offset: 4665, + key.offset: 4678, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6074,16 +6120,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_2", - key.offset: 4710, + key.offset: 4723, key.length: 30, key.typename: "Int32", - key.nameoffset: 4714, + key.nameoffset: 4727, key.namelength: 11, - key.bodyoffset: 4734, + key.bodyoffset: 4747, key.bodylength: 5, key.attributes: [ { - key.offset: 4703, + key.offset: 4716, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6093,16 +6139,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_3", - key.offset: 4748, + key.offset: 4761, key.length: 30, key.typename: "Int32", - key.nameoffset: 4752, + key.nameoffset: 4765, key.namelength: 11, - key.bodyoffset: 4772, + key.bodyoffset: 4785, key.bodylength: 5, key.attributes: [ { - key.offset: 4741, + key.offset: 4754, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6112,16 +6158,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_4", - key.offset: 4825, + key.offset: 4838, key.length: 31, key.typename: "UInt32", - key.nameoffset: 4829, + key.nameoffset: 4842, key.namelength: 11, - key.bodyoffset: 4850, + key.bodyoffset: 4863, key.bodylength: 5, key.attributes: [ { - key.offset: 4818, + key.offset: 4831, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6131,16 +6177,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_5", - key.offset: 4864, + key.offset: 4877, key.length: 31, key.typename: "UInt64", - key.nameoffset: 4868, + key.nameoffset: 4881, key.namelength: 11, - key.bodyoffset: 4889, + key.bodyoffset: 4902, key.bodylength: 5, key.attributes: [ { - key.offset: 4857, + key.offset: 4870, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6150,16 +6196,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_6", - key.offset: 4903, + key.offset: 4916, key.length: 38, key.typename: "typedef_int_t", - key.nameoffset: 4907, + key.nameoffset: 4920, key.namelength: 11, - key.bodyoffset: 4935, + key.bodyoffset: 4948, key.bodylength: 5, key.attributes: [ { - key.offset: 4896, + key.offset: 4909, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6169,16 +6215,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_7", - key.offset: 4949, + key.offset: 4962, key.length: 38, key.typename: "typedef_int_t", - key.nameoffset: 4953, + key.nameoffset: 4966, key.namelength: 11, - key.bodyoffset: 4981, + key.bodyoffset: 4994, key.bodylength: 5, key.attributes: [ { - key.offset: 4942, + key.offset: 4955, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6188,16 +6234,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_8", - key.offset: 4995, + key.offset: 5008, key.length: 29, key.typename: "Int8", - key.nameoffset: 4999, + key.nameoffset: 5012, key.namelength: 11, - key.bodyoffset: 5018, + key.bodyoffset: 5031, key.bodylength: 5, key.attributes: [ { - key.offset: 4988, + key.offset: 5001, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6207,16 +6253,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_9", - key.offset: 5032, + key.offset: 5045, key.length: 30, key.typename: "Int32", - key.nameoffset: 5036, + key.nameoffset: 5049, key.namelength: 11, - key.bodyoffset: 5056, + key.bodyoffset: 5069, key.bodylength: 5, key.attributes: [ { - key.offset: 5025, + key.offset: 5038, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6226,16 +6272,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_10", - key.offset: 5070, + key.offset: 5083, key.length: 31, key.typename: "Int16", - key.nameoffset: 5074, + key.nameoffset: 5087, key.namelength: 12, - key.bodyoffset: 5095, + key.bodyoffset: 5108, key.bodylength: 5, key.attributes: [ { - key.offset: 5063, + key.offset: 5076, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6245,16 +6291,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_11", - key.offset: 5109, + key.offset: 5122, key.length: 29, key.typename: "Int", - key.nameoffset: 5113, + key.nameoffset: 5126, key.namelength: 12, - key.bodyoffset: 5132, + key.bodyoffset: 5145, key.bodylength: 5, key.attributes: [ { - key.offset: 5102, + key.offset: 5115, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6264,16 +6310,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_OR", - key.offset: 5146, + key.offset: 5159, key.length: 31, key.typename: "Int32", - key.nameoffset: 5150, + key.nameoffset: 5163, key.namelength: 12, - key.bodyoffset: 5171, + key.bodyoffset: 5184, key.bodylength: 5, key.attributes: [ { - key.offset: 5139, + key.offset: 5152, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6283,16 +6329,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_AND", - key.offset: 5185, + key.offset: 5198, key.length: 32, key.typename: "Int32", - key.nameoffset: 5189, + key.nameoffset: 5202, key.namelength: 13, - key.bodyoffset: 5211, + key.bodyoffset: 5224, key.bodylength: 5, key.attributes: [ { - key.offset: 5178, + key.offset: 5191, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6302,16 +6348,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_BITWIDTH", - key.offset: 5225, + key.offset: 5238, key.length: 38, key.typename: "UInt64", - key.nameoffset: 5229, + key.nameoffset: 5242, key.namelength: 18, - key.bodyoffset: 5257, + key.bodyoffset: 5270, key.bodylength: 5, key.attributes: [ { - key.offset: 5218, + key.offset: 5231, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6321,16 +6367,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_SIGNED", - key.offset: 5271, + key.offset: 5284, key.length: 36, key.typename: "UInt32", - key.nameoffset: 5275, + key.nameoffset: 5288, key.namelength: 16, - key.bodyoffset: 5301, + key.bodyoffset: 5314, key.bodylength: 5, key.attributes: [ { - key.offset: 5264, + key.offset: 5277, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6340,16 +6386,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_REDEF_1", - key.offset: 5316, + key.offset: 5329, key.length: 36, key.typename: "Int32", - key.nameoffset: 5320, + key.nameoffset: 5333, key.namelength: 17, - key.bodyoffset: 5346, + key.bodyoffset: 5359, key.bodylength: 5, key.attributes: [ { - key.offset: 5309, + key.offset: 5322, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6359,16 +6405,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.var.global, key.accessibility: source.lang.swift.accessibility.public, key.name: "FOO_MACRO_REDEF_2", - key.offset: 5361, + key.offset: 5374, key.length: 36, key.typename: "Int32", - key.nameoffset: 5365, + key.nameoffset: 5378, key.namelength: 17, - key.bodyoffset: 5391, + key.bodyoffset: 5404, key.bodylength: 5, key.attributes: [ { - key.offset: 5354, + key.offset: 5367, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6378,13 +6424,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "theLastDeclInFoo()", - key.offset: 5406, + key.offset: 5419, key.length: 23, - key.nameoffset: 5411, + key.nameoffset: 5424, key.namelength: 18, key.attributes: [ { - key.offset: 5399, + key.offset: 5412, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6394,13 +6440,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.free, key.accessibility: source.lang.swift.accessibility.public, key.name: "_internalTopLevelFunc()", - key.offset: 5438, + key.offset: 5451, key.length: 28, - key.nameoffset: 5443, + key.nameoffset: 5456, key.namelength: 23, key.attributes: [ { - key.offset: 5431, + key.offset: 5444, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6410,15 +6456,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.struct, key.accessibility: source.lang.swift.accessibility.public, key.name: "_InternalStruct", - key.offset: 5475, + key.offset: 5488, key.length: 97, - key.nameoffset: 5482, + key.nameoffset: 5495, key.namelength: 15, - key.bodyoffset: 5499, + key.bodyoffset: 5512, key.bodylength: 72, key.attributes: [ { - key.offset: 5468, + key.offset: 5481, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6429,14 +6475,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.public, key.setter_accessibility: source.lang.swift.accessibility.public, key.name: "x", - key.offset: 5512, + key.offset: 5525, key.length: 12, key.typename: "Int32", - key.nameoffset: 5516, + key.nameoffset: 5529, key.namelength: 1, key.attributes: [ { - key.offset: 5505, + key.offset: 5518, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6446,13 +6492,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init()", - key.offset: 5537, + key.offset: 5550, key.length: 6, - key.nameoffset: 5537, + key.nameoffset: 5550, key.namelength: 6, key.attributes: [ { - key.offset: 5530, + key.offset: 5543, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6462,13 +6508,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(x:)", - key.offset: 5556, + key.offset: 5569, key.length: 14, - key.nameoffset: 5556, + key.nameoffset: 5569, key.namelength: 14, key.attributes: [ { - key.offset: 5549, + key.offset: 5562, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6477,10 +6523,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "x", - key.offset: 5561, + key.offset: 5574, key.length: 8, key.typename: "Int32", - key.nameoffset: 5561, + key.nameoffset: 5574, key.namelength: 1 } ] @@ -6489,27 +6535,26 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", - key.offset: 5574, + key.offset: 5587, key.length: 66, - key.nameoffset: 5584, + key.nameoffset: 5597, key.namelength: 12, - key.bodyoffset: 5598, + key.bodyoffset: 5611, key.bodylength: 41, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "_internalMeth1()", - key.offset: 5609, + key.offset: 5622, key.length: 29, key.typename: "Any!", - key.nameoffset: 5614, + key.nameoffset: 5627, key.namelength: 16, key.attributes: [ { - key.offset: 5604, + key.offset: 5617, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6519,27 +6564,26 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", - key.offset: 5687, + key.offset: 5700, key.length: 107, - key.nameoffset: 5697, + key.nameoffset: 5710, key.namelength: 12, - key.bodyoffset: 5711, + key.bodyoffset: 5724, key.bodylength: 82, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "_internalMeth2()", - key.offset: 5722, + key.offset: 5735, key.length: 29, key.typename: "Any!", - key.nameoffset: 5727, + key.nameoffset: 5740, key.namelength: 16, key.attributes: [ { - key.offset: 5717, + key.offset: 5730, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6549,14 +6593,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "nonInternalMeth()", - key.offset: 5762, + key.offset: 5775, key.length: 30, key.typename: "Any!", - key.nameoffset: 5767, + key.nameoffset: 5780, key.namelength: 17, key.attributes: [ { - key.offset: 5757, + key.offset: 5770, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6566,27 +6610,26 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { }, { key.kind: source.lang.swift.decl.extension, - key.accessibility: source.lang.swift.accessibility.internal, key.name: "FooClassBase", - key.offset: 5796, + key.offset: 5809, key.length: 66, - key.nameoffset: 5806, + key.nameoffset: 5819, key.namelength: 12, - key.bodyoffset: 5820, + key.bodyoffset: 5833, key.bodylength: 41, key.substructure: [ { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "_internalMeth3()", - key.offset: 5831, + key.offset: 5844, key.length: 29, key.typename: "Any!", - key.nameoffset: 5836, + key.nameoffset: 5849, key.namelength: 16, key.attributes: [ { - key.offset: 5826, + key.offset: 5839, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6598,16 +6641,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.protocol, key.accessibility: source.lang.swift.accessibility.public, key.name: "_InternalProt", - key.offset: 5871, + key.offset: 5884, key.length: 26, key.runtime_name: "_TtP4main13_InternalProt_", - key.nameoffset: 5880, + key.nameoffset: 5893, key.namelength: 13, - key.bodyoffset: 5895, + key.bodyoffset: 5908, key.bodylength: 1, key.attributes: [ { - key.offset: 5864, + key.offset: 5877, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6617,12 +6660,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "ClassWithInternalProt", - key.offset: 5904, + key.offset: 5917, key.length: 47, key.runtime_name: "_TtC4main21ClassWithInternalProt", - key.nameoffset: 5910, + key.nameoffset: 5923, key.namelength: 21, - key.bodyoffset: 5949, + key.bodyoffset: 5962, key.bodylength: 1, key.inheritedtypes: [ { @@ -6631,7 +6674,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 5899, + key.offset: 5912, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6639,7 +6682,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 5934, + key.offset: 5947, key.length: 13 } ] @@ -6648,12 +6691,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "FooClassPropertyOwnership", - key.offset: 5958, + key.offset: 5971, key.length: 319, key.runtime_name: "_TtC4main25FooClassPropertyOwnership", - key.nameoffset: 5964, + key.nameoffset: 5977, key.namelength: 25, - key.bodyoffset: 6006, + key.bodyoffset: 6019, key.bodylength: 270, key.inheritedtypes: [ { @@ -6662,7 +6705,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 5953, + key.offset: 5966, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6670,7 +6713,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 5992, + key.offset: 6005, key.length: 12 } ], @@ -6680,19 +6723,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "assignable", - key.offset: 6033, + key.offset: 6046, key.length: 26, key.typename: "AnyObject!", - key.nameoffset: 6037, + key.nameoffset: 6050, key.namelength: 10, key.attributes: [ { - key.offset: 6028, + key.offset: 6041, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6012, + key.offset: 6025, key.length: 15, key.attribute: source.decl.attribute.weak } @@ -6703,19 +6746,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "unsafeAssignable", - key.offset: 6086, + key.offset: 6099, key.length: 32, key.typename: "AnyObject!", - key.nameoffset: 6090, + key.nameoffset: 6103, key.namelength: 16, key.attributes: [ { - key.offset: 6081, + key.offset: 6094, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6065, + key.offset: 6078, key.length: 15, key.attribute: source.decl.attribute.weak } @@ -6726,14 +6769,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "retainable", - key.offset: 6129, + key.offset: 6142, key.length: 20, key.typename: "Any!", - key.nameoffset: 6133, + key.nameoffset: 6146, key.namelength: 10, key.attributes: [ { - key.offset: 6124, + key.offset: 6137, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6744,14 +6787,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "strongRef", - key.offset: 6160, + key.offset: 6173, key.length: 19, key.typename: "Any!", - key.nameoffset: 6164, + key.nameoffset: 6177, key.namelength: 9, key.attributes: [ { - key.offset: 6155, + key.offset: 6168, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6762,14 +6805,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "copyable", - key.offset: 6190, + key.offset: 6203, key.length: 18, key.typename: "Any!", - key.nameoffset: 6194, + key.nameoffset: 6207, key.namelength: 8, key.attributes: [ { - key.offset: 6185, + key.offset: 6198, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6780,19 +6823,19 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "weakRef", - key.offset: 6224, + key.offset: 6237, key.length: 23, key.typename: "AnyObject!", - key.nameoffset: 6228, + key.nameoffset: 6241, key.namelength: 7, key.attributes: [ { - key.offset: 6219, + key.offset: 6232, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6214, + key.offset: 6227, key.length: 4, key.attribute: source.decl.attribute.weak } @@ -6803,14 +6846,14 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.accessibility: source.lang.swift.accessibility.open, key.setter_accessibility: source.lang.swift.accessibility.open, key.name: "scalar", - key.offset: 6258, + key.offset: 6271, key.length: 17, key.typename: "Int32", - key.nameoffset: 6262, + key.nameoffset: 6275, key.namelength: 6, key.attributes: [ { - key.offset: 6253, + key.offset: 6266, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6822,12 +6865,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.open, key.name: "FooUnavailableMembers", - key.offset: 6284, + key.offset: 6297, key.length: 340, key.runtime_name: "_TtC4main21FooUnavailableMembers", - key.nameoffset: 6290, + key.nameoffset: 6303, key.namelength: 21, - key.bodyoffset: 6328, + key.bodyoffset: 6341, key.bodylength: 295, key.inheritedtypes: [ { @@ -6836,7 +6879,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 6279, + key.offset: 6292, key.length: 4, key.attribute: source.decl.attribute.open } @@ -6844,7 +6887,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 6314, + key.offset: 6327, key.length: 12 } ], @@ -6853,18 +6896,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "init(int:)", - key.offset: 6353, + key.offset: 6366, key.length: 19, - key.nameoffset: 6353, + key.nameoffset: 6366, key.namelength: 19, key.attributes: [ { - key.offset: 6341, + key.offset: 6354, key.length: 11, key.attribute: source.decl.attribute.convenience }, { - key.offset: 6334, + key.offset: 6347, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6873,10 +6916,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { { key.kind: source.lang.swift.decl.var.parameter, key.name: "i", - key.offset: 6359, + key.offset: 6372, key.length: 12, key.typename: "Int32", - key.nameoffset: 6359, + key.nameoffset: 6372, key.namelength: 3 } ] @@ -6885,18 +6928,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "deprecated()", - key.offset: 6432, + key.offset: 6445, key.length: 17, - key.nameoffset: 6437, + key.nameoffset: 6450, key.namelength: 12, key.attributes: [ { - key.offset: 6427, + key.offset: 6440, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6383, + key.offset: 6396, key.length: 39, key.attribute: source.decl.attribute.available } @@ -6906,18 +6949,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "availabilityIntroduced()", - key.offset: 6493, + key.offset: 6506, key.length: 29, - key.nameoffset: 6498, + key.nameoffset: 6511, key.namelength: 24, key.attributes: [ { - key.offset: 6488, + key.offset: 6501, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6460, + key.offset: 6473, key.length: 23, key.attribute: source.decl.attribute.available } @@ -6927,18 +6970,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.open, key.name: "availabilityIntroducedMsg()", - key.offset: 6590, + key.offset: 6603, key.length: 32, - key.nameoffset: 6595, + key.nameoffset: 6608, key.namelength: 27, key.attributes: [ { - key.offset: 6585, + key.offset: 6598, key.length: 4, key.attribute: source.decl.attribute.open }, { - key.offset: 6533, + key.offset: 6546, key.length: 47, key.attribute: source.decl.attribute.available } @@ -6950,16 +6993,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooCFType", - key.offset: 6633, + key.offset: 6646, key.length: 19, key.runtime_name: "_TtC4main9FooCFType", - key.nameoffset: 6639, + key.nameoffset: 6652, key.namelength: 9, - key.bodyoffset: 6650, + key.bodyoffset: 6663, key.bodylength: 1, key.attributes: [ { - key.offset: 6626, + key.offset: 6639, key.length: 6, key.attribute: source.decl.attribute.public } @@ -6969,12 +7012,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.enum, key.accessibility: source.lang.swift.accessibility.public, key.name: "ABAuthorizationStatus", - key.offset: 6725, - key.length: 191, - key.nameoffset: 6730, + key.offset: 6738, + key.length: 199, + key.nameoffset: 6743, key.namelength: 21, - key.bodyoffset: 6759, - key.bodylength: 156, + key.bodyoffset: 6772, + key.bodylength: 164, key.inheritedtypes: [ { key.name: "Int" @@ -6982,12 +7025,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 6718, + key.offset: 6731, key.length: 6, key.attribute: source.decl.attribute.public }, { - key.offset: 6654, + key.offset: 6667, key.length: 63, key.attribute: source.decl.attribute.available } @@ -6995,15 +7038,15 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 6754, + key.offset: 6767, key.length: 3 } ], key.substructure: [ { key.kind: source.lang.swift.decl.enumcase, - key.offset: 6770, - key.length: 18, + key.offset: 6783, + key.length: 22, key.nameoffset: 0, key.namelength: 0, key.substructure: [ @@ -7011,17 +7054,24 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.enumelement, key.accessibility: source.lang.swift.accessibility.public, key.name: "notDetermined", - key.offset: 6775, - key.length: 13, - key.nameoffset: 6775, - key.namelength: 13 + key.offset: 6788, + key.length: 17, + key.nameoffset: 6788, + key.namelength: 13, + key.elements: [ + { + key.kind: source.lang.swift.structure.elem.init_expr, + key.offset: 6804, + key.length: 1 + } + ] } ] }, { key.kind: source.lang.swift.decl.enumcase, - key.offset: 6848, - key.length: 15, + key.offset: 6865, + key.length: 19, key.nameoffset: 0, key.namelength: 0, key.substructure: [ @@ -7029,10 +7079,17 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.enumelement, key.accessibility: source.lang.swift.accessibility.public, key.name: "restricted", - key.offset: 6853, - key.length: 10, - key.nameoffset: 6853, - key.namelength: 10 + key.offset: 6870, + key.length: 14, + key.nameoffset: 6870, + key.namelength: 10, + key.elements: [ + { + key.kind: source.lang.swift.structure.elem.init_expr, + key.offset: 6883, + key.length: 1 + } + ] } ] } @@ -7042,16 +7099,16 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooOverlayClassBase", - key.offset: 6924, + key.offset: 6945, key.length: 50, key.runtime_name: "_TtC4main19FooOverlayClassBase", - key.nameoffset: 6930, + key.nameoffset: 6951, key.namelength: 19, - key.bodyoffset: 6951, + key.bodyoffset: 6972, key.bodylength: 22, key.attributes: [ { - key.offset: 6917, + key.offset: 6938, key.length: 6, key.attribute: source.decl.attribute.public } @@ -7061,13 +7118,13 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "f()", - key.offset: 6964, + key.offset: 6985, key.length: 8, - key.nameoffset: 6969, + key.nameoffset: 6990, key.namelength: 3, key.attributes: [ { - key.offset: 6957, + key.offset: 6978, key.length: 6, key.attribute: source.decl.attribute.public } @@ -7079,12 +7136,12 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.class, key.accessibility: source.lang.swift.accessibility.public, key.name: "FooOverlayClassDerived", - key.offset: 6983, + key.offset: 7004, key.length: 88, key.runtime_name: "_TtC4main22FooOverlayClassDerived", - key.nameoffset: 6989, + key.nameoffset: 7010, key.namelength: 22, - key.bodyoffset: 7039, + key.bodyoffset: 7060, key.bodylength: 31, key.inheritedtypes: [ { @@ -7093,7 +7150,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { ], key.attributes: [ { - key.offset: 6976, + key.offset: 6997, key.length: 6, key.attribute: source.decl.attribute.public } @@ -7101,7 +7158,7 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.elements: [ { key.kind: source.lang.swift.structure.elem.typeref, - key.offset: 7014, + key.offset: 7035, key.length: 23 } ], @@ -7110,18 +7167,18 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase { key.kind: source.lang.swift.decl.function.method.instance, key.accessibility: source.lang.swift.accessibility.public, key.name: "f()", - key.offset: 7061, + key.offset: 7082, key.length: 8, - key.nameoffset: 7066, + key.nameoffset: 7087, key.namelength: 3, key.attributes: [ { - key.offset: 7054, + key.offset: 7075, key.length: 6, key.attribute: source.decl.attribute.public }, { - key.offset: 7045, + key.offset: 7066, key.length: 8, key.attribute: source.decl.attribute.override } diff --git a/test/SourceKit/InterfaceGen/gen_mixed_module.swift b/test/SourceKit/InterfaceGen/gen_mixed_module.swift index 0bfd7119ebb94..4224a585581f8 100644 --- a/test/SourceKit/InterfaceGen/gen_mixed_module.swift +++ b/test/SourceKit/InterfaceGen/gen_mixed_module.swift @@ -3,13 +3,13 @@ var x = 10 // REQUIRES: objc_interop // FIXME: the test output we're comparing to is specific to macOS. -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx // RUN: %empty-directory(%t.overlays) // RUN: %empty-directory(%t) // RUN: %build-clang-importer-objc-overlays // RUN: %target-swift-frontend -emit-module -o %t.overlays -F %S/../Inputs/libIDE-mock-sdk %S/../Inputs/libIDE-mock-sdk/Mixed.swift -import-underlying-module -module-name Mixed -disable-objc-attr-requires-foundation-module -// RUN: %sourcekitd-test -req=interface-gen -module Mixed -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=interface-gen -module Mixed -- -I %t.overlays -F %S/../Inputs/libIDE-mock-sdk -target %target-triple %clang-importer-sdk-nosource -I %t | %FileCheck -check-prefix=CHECK1 %s // CHECK1: PureSwiftClass diff --git a/test/SourceKit/InterfaceGen/gen_module_group.swift b/test/SourceKit/InterfaceGen/gen_module_group.swift index b08a615f10964..bffaaa4c26693 100644 --- a/test/SourceKit/InterfaceGen/gen_module_group.swift +++ b/test/SourceKit/InterfaceGen/gen_module_group.swift @@ -3,6 +3,12 @@ // RUN: %sourcekitd-test -req=interface-gen -module MyModule -group-name A -- -I %t.mod -target %target-triple | %FileCheck -check-prefix=GROUPA %s // RUN: %sourcekitd-test -req=interface-gen -module MyModule -group-name B -- -I %t.mod -target %target-triple | %FileCheck -check-prefix=GROUPB %s +// FIXME: We don't currently handle group info for multi-file builds, +// so just make sure we don't crash. +// RUN: %empty-directory(%t.multifrontend) +// RUN: %target-build-swift -module-name MyModule -emit-module -emit-module-path %t.multifrontend/MyModule.swiftmodule -Xfrontend -group-info-path -Xfrontend %S/Inputs/group.json %S/Inputs/swift_mod.swift %S/Inputs/swift_mod_syn.swift +// RUN: %sourcekitd-test -req=interface-gen -module MyModule -group-name A -- -I %t.multifrontend -target %target-triple | %FileCheck -check-prefix=EMPTY %s + // GROUPA: MyClass // GROUPA-NOT: P1 // GROUPA-NOT: P2 @@ -10,3 +16,7 @@ // GROUPB: P1 // GROUPB: P2 // GROUPB-NOT: MyClass + +// EMPTY-NOT: P1 +// EMPTY-NOT: P2 +// EMPTY-NOT: MyClass diff --git a/test/SourceKit/InterfaceGen/gen_stdlib.swift b/test/SourceKit/InterfaceGen/gen_stdlib.swift index d0a30f6584437..b4a8320608cce 100644 --- a/test/SourceKit/InterfaceGen/gen_stdlib.swift +++ b/test/SourceKit/InterfaceGen/gen_stdlib.swift @@ -36,7 +36,7 @@ var x: Int // CHECK1-NEXT: $s // CHECK1-NEXT: Swift{{$}} // CHECK1-NEXT: Math/Integers -// CHECK1-NEXT: /{{$}} +// CHECK1-NEXT: {{[A-Za-z]:\\|/}}{{$}} // CHECK1-NEXT: SYSTEM // CHECK1-NEXT: @frozen struct Int : FixedWidthInteger{{.*}}SignedInteger{{.*}} @@ -77,3 +77,11 @@ var x: Int // CHECK-FLOAT-NOT: Zip2Sequence // CHECK-FLOAT-NOT: struct Bool // CHECK-FLOAT-NOT: struct Int + + +// RUN: %sourcekitd-test -req=interface-gen -module Swift -group-name Misc -synthesized-extension > %t.Misc.response +// RUN: %FileCheck -check-prefix=CHECK-ERROR -input-file %t.Misc.response %s +// RUN: %sourcekitd-test -req=interface-gen -module Swift -interested-usr s:s5ErrorP -synthesized-extension > %t.Error.response +// RUN: %FileCheck -check-prefix=CHECK-ERROR -input-file %t.Error.response %s + +// CHECK-ERROR: protocol Error diff --git a/test/SourceKit/InterfaceGen/gen_swift_module.swift b/test/SourceKit/InterfaceGen/gen_swift_module.swift index 96994a786700a..fc2999f797f25 100644 --- a/test/SourceKit/InterfaceGen/gen_swift_module.swift +++ b/test/SourceKit/InterfaceGen/gen_swift_module.swift @@ -8,7 +8,7 @@ func f(s : inout [Int]) { // RUN: %empty-directory(%t.mod/mcp) // RUN: %swift -emit-module -o %t.mod/swift_mod.swiftmodule %S/Inputs/swift_mod.swift -parse-as-library // RUN: %sourcekitd-test -req=interface-gen -module swift_mod -- -I %t.mod > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response // RUN: %sourcekitd-test -req=module-groups -module swift_mod -- -I %t.mod | %FileCheck -check-prefix=GROUP-EMPTY %s // GROUP-EMPTY: @@ -32,4 +32,4 @@ func f(s : inout [Int]) { // RUN: %empty-directory(%t.mod) // RUN: %swift -emit-module -o /dev/null -emit-module-interface-path %t.mod/swift_mod.swiftinterface -O %S/Inputs/swift_mod.swift -parse-as-library // RUN: %sourcekitd-test -req=interface-gen -module swift_mod -- -I %t.mod -module-cache-path %t.mod/mcp > %t.response -// RUN: diff -u %s.from_swiftinterface.response %t.response +// RUN: diff --strip-trailing-cr -u %s.from_swiftinterface.response %t.response diff --git a/test/SourceKit/InterfaceGen/gen_swift_source.swift b/test/SourceKit/InterfaceGen/gen_swift_source.swift index 592fe1e6d5699..32e4e368dacd8 100644 --- a/test/SourceKit/InterfaceGen/gen_swift_source.swift +++ b/test/SourceKit/InterfaceGen/gen_swift_source.swift @@ -3,10 +3,10 @@ // RUN: %empty-directory(%t) // RUN: %build-clang-importer-objc-overlays -// RUN: %sourcekitd-test -req=interface-gen %S/Inputs/Foo2.swift -- %S/Inputs/Foo2.swift %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t > %t.response +// RUN: %sourcekitd-test -req=interface-gen %S/Inputs/Foo2.swift -- %S/Inputs/Foo2.swift -target %target-triple %clang-importer-sdk-nosource -I %t > %t.response // RUN: diff -u %s.response %t.response -// RUN: %sourcekitd-test -req=interface-gen-open %S/Inputs/Foo2.swift -- %S/Inputs/Foo2.swift %mcp_opt -target %target-triple %clang-importer-sdk-nosource -I %t \ +// RUN: %sourcekitd-test -req=interface-gen-open %S/Inputs/Foo2.swift -- %S/Inputs/Foo2.swift -target %target-triple %clang-importer-sdk-nosource -I %t \ // RUN: == -req=cursor -pos=18:49 | %FileCheck -check-prefix=CHECK1 %s // The cursor points to 'FooOverlayClassBase' inside the list of base classes, see 'gen_swift_source.swift.response' diff --git a/test/SourceKit/InterfaceGen/gen_swiftonly_systemmodule.swift b/test/SourceKit/InterfaceGen/gen_swiftonly_systemmodule.swift index 31915ca117cf7..989fc0e0f5a4e 100644 --- a/test/SourceKit/InterfaceGen/gen_swiftonly_systemmodule.swift +++ b/test/SourceKit/InterfaceGen/gen_swiftonly_systemmodule.swift @@ -9,7 +9,7 @@ // RUN: %s // RUN: %sourcekitd-test -req=interface-gen -module SomeModule -- -sdk %t/SDK -Fsystem %t/SDK/Frameworks -target %target-triple > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response public struct SomeValue { internal var internalValue: Int { return 1 } diff --git a/test/SourceKit/MarkupXML/basic.swift b/test/SourceKit/MarkupXML/basic.swift index 22cf3e4ff1f95..d8984f66bb3db 100644 --- a/test/SourceKit/MarkupXML/basic.swift +++ b/test/SourceKit/MarkupXML/basic.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=markup-xml -pass-as-sourcetext %S/Input/DocComment1.md > %t.DocComment1.response -// RUN: diff -u %s.DocComment1.response %t.DocComment1.response +// RUN: diff --strip-trailing-cr -u %s.DocComment1.response %t.DocComment1.response diff --git a/test/SourceKit/Misc/compiler_version.swift b/test/SourceKit/Misc/compiler_version.swift index 1db7358ad2335..5f067155e4b8c 100644 --- a/test/SourceKit/Misc/compiler_version.swift +++ b/test/SourceKit/Misc/compiler_version.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=compiler-version | %FileCheck %s // CHECK: key.version_major: 5 -// CHECK: key.version_minor: 1 -// CHECK: key.version_patch: 1 +// CHECK: key.version_minor: 2 +// CHECK: key.version_patch: 0 diff --git a/test/SourceKit/Misc/stats.swift b/test/SourceKit/Misc/stats.swift index ebde8bf39b021..7e6a4c890ac48 100644 --- a/test/SourceKit/Misc/stats.swift +++ b/test/SourceKit/Misc/stats.swift @@ -2,7 +2,7 @@ func foo() {} // RUN: %sourcekitd-test -req=syntax-map %s == -req=stats | %FileCheck %s -check-prefix=SYNTAX_1 -// SYNTAX_1: 2 {{.*}} source.statistic.num-requests +// SYNTAX_1: 3 {{.*}} source.statistic.num-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-semantic-requests // SYNTAX_1: 0 {{.*}} source.statistic.num-ast-builds // SYNTAX_1: 1 {{.*}} source.statistic.num-open-documents @@ -10,7 +10,7 @@ func foo() {} // RUN: %sourcekitd-test -req=syntax-map %s == -req=close %s == -req=stats | %FileCheck %s -check-prefix=SYNTAX_2 -// SYNTAX_2: 3 {{.*}} source.statistic.num-requests +// SYNTAX_2: 4 {{.*}} source.statistic.num-requests // SYNTAX_2: 0 {{.*}} source.statistic.num-semantic-requests // SYNTAX_2: 0 {{.*}} source.statistic.num-ast-builds // SYNTAX_2: 0 {{.*}} source.statistic.num-open-documents @@ -18,7 +18,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_1 -// SEMA_1: 3 {{.*}} source.statistic.num-requests +// SEMA_1: 4 {{.*}} source.statistic.num-requests // SEMA_1: 0 {{.*}} source.statistic.num-semantic-requests // SEMA_1: 1 {{.*}} source.statistic.num-ast-builds // SEMA_1: 1 {{.*}} source.statistic.num-asts-in-memory @@ -28,7 +28,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=edit -pos=1:1 -replace=" " %s == -req=stats | %FileCheck %s -check-prefix=SEMA_2 -// SEMA_2: 5 {{.*}} source.statistic.num-requests +// SEMA_2: 6 {{.*}} source.statistic.num-requests // SEMA_2: 0 {{.*}} source.statistic.num-semantic-requests // SEMA_2: 2 {{.*}} source.statistic.num-ast-builds // NOTE: we cannot match num-asts-in-memory, or num-ast-cache-hits reliably when @@ -40,7 +40,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=cursor -pos=1:6 %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_3 -// SEMA_3: 4 {{.*}} source.statistic.num-requests +// SEMA_3: 5 {{.*}} source.statistic.num-requests // SEMA_3: 1 {{.*}} source.statistic.num-semantic-requests // SEMA_3: 1 {{.*}} source.statistic.num-ast-builds // SEMA_3: 1 {{.*}} source.statistic.num-asts-in-memory @@ -50,7 +50,7 @@ func foo() {} // RUN: %sourcekitd-test -req=sema %s -- %s == -req=related-idents -pos=1:6 %s -- %s == -req=stats | %FileCheck %s -check-prefix=SEMA_4 -// SEMA_4: 4 {{.*}} source.statistic.num-requests +// SEMA_4: 5 {{.*}} source.statistic.num-requests // SEMA_4: 1 {{.*}} source.statistic.num-semantic-requests // SEMA_4: 1 {{.*}} source.statistic.num-ast-builds // SEMA_4: 1 {{.*}} source.statistic.num-asts-in-memory diff --git a/test/SourceKit/Mixed/cursor_mixed.swift b/test/SourceKit/Mixed/cursor_mixed.swift index 9fdd0320cbfb5..d5ea686677978 100644 --- a/test/SourceKit/Mixed/cursor_mixed.swift +++ b/test/SourceKit/Mixed/cursor_mixed.swift @@ -4,7 +4,7 @@ func test(_ b : Base) { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -F %S/Inputs -module-name Mixed -import-underlying-module | %FileCheck %s +// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s -F %S/Inputs -module-name Mixed -import-underlying-module | %FileCheck %s // CHECK: source.lang.swift.ref.function.method.instance ({{.*}}Mixed.framework/Headers/Mixed.h:5:9-5:23) // CHECK: doIt(_:) diff --git a/test/SourceKit/Mixed/cursor_mixed_header.swift b/test/SourceKit/Mixed/cursor_mixed_header.swift index 30624dfc58673..9b746fbe9374f 100644 --- a/test/SourceKit/Mixed/cursor_mixed_header.swift +++ b/test/SourceKit/Mixed/cursor_mixed_header.swift @@ -4,14 +4,14 @@ func test(_ b : BaseInHead) { } // REQUIRES: objc_interop -// RUN: %swift -typecheck %s %mcp_opt -module-name Mixed -import-objc-header %S/Inputs/header.h 2> %t.diags +// RUN: %swift -typecheck %s -module-name Mixed -import-objc-header %S/Inputs/header.h 2> %t.diags // RUN: %FileCheck -input-file %t.diags %s -check-prefix=DIAG // DIAG: warning: using the result of an assignment -// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -module-name Mixed -import-objc-header %S/Inputs/header.h | %FileCheck %s +// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s -module-name Mixed -import-objc-header %S/Inputs/header.h | %FileCheck %s // RUN: %empty-directory(%t) -// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h | %FileCheck %s +// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h | %FileCheck %s // RUN: stat %t/*.pch // CHECK: source.lang.swift.ref.function.method.instance ({{.*}}Inputs/header.h:4:9-4:23) diff --git a/test/SourceKit/NameTranslation/basic.swift b/test/SourceKit/NameTranslation/basic.swift index 271250389c36b..8fdd6f1906846 100644 --- a/test/SourceKit/NameTranslation/basic.swift +++ b/test/SourceKit/NameTranslation/basic.swift @@ -25,35 +25,35 @@ class MyDerived: FooClassDerived { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=translate -objc-name FooClassDerived2 -pos=5:30 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector FooClassDerived2 -pos=3:23 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK11 %s -// RUN: %sourcekitd-test -req=translate -objc-name FooClassDerived2 -pos=3:23 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK11-1 %s -// RUN: %sourcekitd-test -req=translate -objc-name fooProperty2 -pos=6:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc1 -pos=7:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc1: -pos=7:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooFunc3:d:d:d: -pos=8:4 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -objc-name FooClassDerived2 -pos=5:30 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector FooClassDerived2 -pos=3:23 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11 %s +// RUN: %sourcekitd-test -req=translate -objc-name FooClassDerived2 -pos=3:23 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11-1 %s +// RUN: %sourcekitd-test -req=translate -objc-name fooProperty2 -pos=6:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc1 -pos=7:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc1: -pos=7:16 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooFunc3:d:d:d: -pos=8:4 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooBaseInstanceFuncOverridden1 -pos=12:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK4 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=13:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc11: -pos=14:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK6 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc2:withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK7 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK8 %s -// RUN: %sourcekitd-test -req=translate -objc-name fooProperty11 -pos=16:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK9 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:withBB:withC: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKFEWER1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKMISSING1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector :withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKMISSING2 %s -// RUN: %sourcekitd-test -req=translate -objc-selector :: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKMISSING3 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooBaseInstanceFuncOverridden1 -pos=12:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=13:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc11: -pos=14:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK6 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc2:withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK7 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK8 %s +// RUN: %sourcekitd-test -req=translate -objc-name fooProperty11 -pos=16:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:withBB:withC: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKFEWER1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21:: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector :withBB: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s +// RUN: %sourcekitd-test -req=translate -objc-selector :: -pos=15:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING3 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK10 %s -// RUN: %sourcekitd-test -req=translate -objc-selector initWithfloat2: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK12 %s -// RUN: %sourcekitd-test -req=translate -objc-selector initWithfloat2:D: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -objc-selector init: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -objc-selector iit: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -objc-selector NAME -pos=18:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc21: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK10 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithfloat2: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK12 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithfloat2:D: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -objc-selector init: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -objc-selector iit: -pos=17:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -objc-selector NAME -pos=18:13 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=22:17 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s -// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=23:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=22:17 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=translate -objc-selector fooInstanceFunc01 -pos=23:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s // CHECK1: FooClassDerived2 // CHECK-DIAG: diff --git a/test/SourceKit/NameTranslation/enum.swift b/test/SourceKit/NameTranslation/enum.swift index 0a31d5dae329e..bb1a573f3ccb6 100644 --- a/test/SourceKit/NameTranslation/enum.swift +++ b/test/SourceKit/NameTranslation/enum.swift @@ -9,12 +9,12 @@ func foo1() { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=translate -objc-name orderedSome -pos=4:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector orderedSome -pos=4:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -objc-name enableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -objc-name FooRuncingEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -objc-name FooRuncinEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -objc-name FooRinEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=translate -objc-name orderedSome -pos=4:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector orderedSome -pos=4:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -objc-name enableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-name FooRuncingEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-name FooRuncinEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-name FooRinEnableThird -pos=7:30 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s // CHECK1: orderedSome // CHECK-DIAG: diff --git a/test/SourceKit/NameTranslation/init.swift b/test/SourceKit/NameTranslation/init.swift index 817fa33476f2f..756f20e12337f 100644 --- a/test/SourceKit/NameTranslation/init.swift +++ b/test/SourceKit/NameTranslation/init.swift @@ -6,11 +6,11 @@ func foo2 () { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2: -pos=4:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2 -pos=4:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2:second2: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2:second2:third: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -objc-selector initFloat2:second2: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2: -pos=4:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2 -pos=4:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2:second2: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -objc-selector initWithFloat2:second2:third: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -objc-selector initFloat2:second2: -pos=5:15 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s // CHECK-DIAG: // CHECK1: init(float2:) diff --git a/test/SourceKit/NameTranslation/swiftnames.swift b/test/SourceKit/NameTranslation/swiftnames.swift index 47be333ebcca6..72223df5a9a8f 100644 --- a/test/SourceKit/NameTranslation/swiftnames.swift +++ b/test/SourceKit/NameTranslation/swiftnames.swift @@ -56,35 +56,35 @@ class C3: NSObject { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK_RAW1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(x:y:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKFEWER1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(::)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKMISSING1 %s -// RUN: %sourcekitd-test -req=translate -swift-name "(x:y:z:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECKMISSING2 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK4 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo2(a:b:c:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK6 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo2(_:_:_:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK7 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK8 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK_RAW8 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo1(a:b:c:)" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-DIAG %s -// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=1:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK9 %s - -// RUN: %sourcekitd-test -req=translate -swift-name "init(a1:b2:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK10 %s -// RUN: %sourcekitd-test -req=translate -swift-name "init(_:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK11 %s -// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK9 %s -// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK12 %s - -// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s -// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s -// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s -// RUN: %sourcekitd-test -req=translate -swift-name "C3" -pos=48:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK15 %s -// RUN: %sourcekitd-test -req=translate -swift-name "bar(_:other:)" -pos=51:36 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK16 %s -// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK17 %s -// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK_RAW17 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(x:y:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKFEWER1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(::)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s +// RUN: %sourcekitd-test -req=translate -swift-name "(x:y:z:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo2(a:b:c:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK6 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo2(_:_:_:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK7 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK8 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW8 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo1(a:b:c:)" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK-DIAG %s +// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=1:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s + +// RUN: %sourcekitd-test -req=translate -swift-name "init(a1:b2:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK10 %s +// RUN: %sourcekitd-test -req=translate -swift-name "init(_:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK11 %s +// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK9 %s +// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK12 %s + +// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK13 %s +// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK14 %s +// RUN: %sourcekitd-test -req=translate -swift-name "C3" -pos=48:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK15 %s +// RUN: %sourcekitd-test -req=translate -swift-name "bar(_:other:)" -pos=51:36 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK16 %s +// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK17 %s +// RUN: %sourcekitd-test -req=translate -swift-name "zoo(m:)" -pos=55:14 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW17 %s // CHECK-DIAG: // CHECK1: fooWithA:b:c: diff --git a/test/SourceKit/Refactoring/basic.swift b/test/SourceKit/Refactoring/basic.swift index b20f674b48af0..2a0f0d439a3ae 100644 --- a/test/SourceKit/Refactoring/basic.swift +++ b/test/SourceKit/Refactoring/basic.swift @@ -152,4 +152,4 @@ func foo7() -> String { // CHECK-LOCALIZE-STRING: source.refactoring.kind.localize.string -// REQUIRES-ANY: OS=macosx, OS=linux-gnu \ No newline at end of file +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/rename-objc.swift b/test/SourceKit/Refactoring/rename-objc.swift index ac95dc4351ff8..b9266c2b24116 100644 --- a/test/SourceKit/Refactoring/rename-objc.swift +++ b/test/SourceKit/Refactoring/rename-objc.swift @@ -5,10 +5,10 @@ func foo1() { } // REQUIRES: objc_interop -// RUN: %sourcekitd-test -req=cursor -pos=4:30 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK1 -// RUN: %sourcekitd-test -req=cursor -pos=4:30 -length=3 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK1 -// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=15 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK1 -// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=16 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck %s -check-prefix=CHECK2 +// RUN: %sourcekitd-test -req=cursor -pos=4:30 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 +// RUN: %sourcekitd-test -req=cursor -pos=4:30 -length=3 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 +// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=15 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK1 +// RUN: %sourcekitd-test -req=cursor -pos=4:20 -length=16 -cursor-action %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck %s -check-prefix=CHECK2 // CHECK1: ACTIONS BEGIN // CHECK1-NEXT: source.refactoring.kind.rename.global @@ -18,4 +18,4 @@ func foo1() { // CHECK2: ACTIONS BEGIN // CHECK2-NEXT: ACTIONS END -// REQUIRES-ANY: OS=macosx, OS=linux-gnu \ No newline at end of file +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift b/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift index 92d35368f99d1..688df4ed9ae8a 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/expand-default.swift @@ -13,4 +13,4 @@ func foo(e : E) { // RUN: %sourcekitd-test -req=expand-default -pos=7:7 %s -- %s > %t.result/expand-default.swift.expected // RUN: diff -u %S/expand-default.swift.expected %t.result/expand-default.swift.expected -// REQUIRES-ANY: OS=macosx, OS=linux-gnu +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift index f547b87edab53..80fc52ff520d6 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-default.swift @@ -9,4 +9,4 @@ func foo() -> Int { // RUN: diff -u %S/extract-func-default.swift.expected %t.result/extract-func-default.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift index 458690823ceaa..76a349655f095 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func-with-args.swift @@ -9,4 +9,4 @@ func foo() -> Int { // RUN: diff -u %S/extract-func-with-args.swift.expected %t.result/extract-func-with-args.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift index c442619172501..c8bbdb82b14c0 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-func.swift @@ -9,4 +9,4 @@ func foo() -> Int { // RUN: diff -u %S/extract-func.swift.expected %t.result/extract-func.swift.expected // FIXME: Fails on linux with assertion: "!GlibcModuleMapPath.empty()"" failed -// REQUIRES-ANY: OS=macosx +// REQUIRES: OS=macosx diff --git a/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift b/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift index 143c855647e10..02ebf45aeb2de 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/extract-repeated-expression.swift @@ -8,4 +8,4 @@ func foo() -> Int { // RUN: %sourcekitd-test -req=extract-repeated -pos=3:11 -end-pos 3:12 -name new_name %s -- %s > %t.result/extract-repeated-expression.swift.expected // RUN: diff -u %S/extract-repeated-expression.swift.expected %t.result/extract-repeated-expression.swift.expected -// REQUIRES-ANY: OS=macosx, OS=linux-gnu +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift b/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift index f972d14bc3825..87bd053fa6c23 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/fill-stub.swift @@ -8,4 +8,4 @@ class C1 : P {} // RUN: %sourcekitd-test -req=fill-stub -pos=5:8 %s -- %s > %t.result/fill-stub.swift.expected // RUN: diff -u %S/fill-stub.swift.expected %t.result/fill-stub.swift.expected -// REQUIRES-ANY: OS=macosx, OS=linux-gnu +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift b/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift index a6664eb151aab..587ebcd0b2677 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/local-rename.swift @@ -19,4 +19,4 @@ func foo() { // RUN: %sourcekitd-test -req=find-local-rename-ranges -pos=7:11 %s -- %s > %t.result/local-rename-lazy.swift.expected // RUN: diff -u %S/local-rename-lazy.swift.expected %t.result/local-rename-lazy.swift.expected -// REQUIRES-ANY: OS=macosx, OS=linux-gnu +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift b/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift index ab53261d7cd13..ec207d35438b5 100644 --- a/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift +++ b/test/SourceKit/Refactoring/semantic-refactoring/localize-string.swift @@ -6,4 +6,4 @@ func foo() -> String { // RUN: %sourcekitd-test -req=localize-string -pos=2:10 %s -- %s > %t.result/localize-string.swift.expected // RUN: diff -u %S/localize-string.swift.expected %t.result/localize-string.swift.expected -// REQUIRES-ANY: OS=macosx, OS=linux-gnu +// REQUIRES: OS=macosx || OS=linux-gnu diff --git a/test/SourceKit/Sema/enum-toraw/enum-toraw.swift b/test/SourceKit/Sema/enum-toraw/enum-toraw.swift index 7f8e1859c64c4..ed2d6b007b639 100644 --- a/test/SourceKit/Sema/enum-toraw/enum-toraw.swift +++ b/test/SourceKit/Sema/enum-toraw/enum-toraw.swift @@ -1,3 +1,3 @@ // RUN: %sourcekitd-test -req=sema %S/Inputs/t2.swift -- %S/Inputs/t1.swift %S/Inputs/t2.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Sema/injected_vfs.swift b/test/SourceKit/Sema/injected_vfs.swift index af9d3cecd1970..b3254c41f0992 100644 --- a/test/SourceKit/Sema/injected_vfs.swift +++ b/test/SourceKit/Sema/injected_vfs.swift @@ -17,32 +17,32 @@ func foo( // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/SwiftModule.swiftmodule -module-name SwiftModule %S/../Inputs/vfs/SwiftModule/SwiftModule.swift -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift | %FileCheck %s +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift | %FileCheck %s -// RUN: not %sourcekitd-test -req=syntax-map -vfs-files=/target_file1.swift=%s /target_file1.swift -dont-print-request 2>&1 | %FileCheck %s -check-prefix=SOURCEFILE_ERROR +// RUN: not %sourcekitd-test -req=syntax-map -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift -dont-print-request 2>&1 | %FileCheck %s -check-prefix=SOURCEFILE_ERROR // SOURCEFILE_ERROR: error response (Request Failed): using 'key.sourcefile' to read source text from the filesystem // RUN: not %sourcekitd-test -req=syntax-map -vfs-name nope %s -pass-as-sourcetext -dont-print-request 2>&1 | %FileCheck %s -check-prefix=NONEXISTENT_VFS_ERROR // NONEXISTENT_VFS_ERROR: error response (Request Failed): unknown virtual filesystem 'nope' // == Close the document and reopen with a new VFS (modules) == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=close -name /target_file1.swift == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=NO_MODULES_VFS +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=close -name %t/VFS/target_file1.swift == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=NO_MODULES_VFS // NO_MODULES_VFS: no such module 'CModule' // == Close the document and reopen with a new VFS (inputs) == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=close -name /target_file1.swift == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=close -name %t/VFS/target_file1.swift == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD // TARGET_FILE_2_MOD: cannot convert value of type 'Void' to specified type 'String' // TARGET_FILE_2_MOD: cannot convert value of type '()' to specified type 'Float' // TARGET_FILE_2_MOD: cannot convert value of type 'Int' to specified type 'Double' // == Reopen with a new VFS without closing == -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=open -vfs-files=/target_file1.swift=%s,/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -I /CModule -I /SwiftModule -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=%s /target_file1.swift /target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=open -vfs-files=%t/VFS/target_file1.swift=%s,%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target_2.swift,%t/VFS/CModule/module.modulemap=%S/../Inputs/vfs/CModule/module.modulemap,%t/VFS/CModule/CModule.h=%S/../Inputs/vfs/CModule/CModule.h,%t/VFS/SwiftModule/SwiftModule.swiftmodule=%t/SwiftModule.swiftmodule %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -I %t/VFS/CModule -I %t/VFS/SwiftModule -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=%s %t/VFS/target_file1.swift %t/VFS/target_file1.swift | %FileCheck %s -check-prefix=TARGET_FILE_2_MOD diff --git a/test/SourceKit/Sema/injected_vfs_after_edit.swift b/test/SourceKit/Sema/injected_vfs_after_edit.swift index 2c004254f0921..a233e125091e4 100644 --- a/test/SourceKit/Sema/injected_vfs_after_edit.swift +++ b/test/SourceKit/Sema/injected_vfs_after_edit.swift @@ -4,7 +4,7 @@ func foo(_ structDefinedInSameTarget: StructDefinedInSameTarget) { // CHECK: cannot convert value of type '()' to specified type 'Int' } -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %s -pass-as-sourcetext -- %s /target_file2.swift -target %target-triple == \ +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file2.swift=%S/../Inputs/vfs/other_file_in_target.swift %s -pass-as-sourcetext -- %s %t/VFS/target_file2.swift -target %target-triple == \ // RUN: -req=print-diags %s == \ // RUN: -req=edit %s -pos=2:12 -length=6 -replace='Int' == \ // RUN: -req=print-diags %s | %FileCheck %s diff --git a/test/SourceKit/Sema/injected_vfs_sourcetext.swift b/test/SourceKit/Sema/injected_vfs_sourcetext.swift index 66161c8db5ba1..6ef1e46a82dfa 100644 --- a/test/SourceKit/Sema/injected_vfs_sourcetext.swift +++ b/test/SourceKit/Sema/injected_vfs_sourcetext.swift @@ -3,5 +3,5 @@ func foo(_ structDefinedInSameTarget: StructDefinedInSameTarget) { // CHECK: cannot convert value of type '()' to specified type 'Double' } -// RUN: %sourcekitd-test -req=open -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift -pass-as-sourcetext -- /target_file1.swift /target_file2.swift -target %target-triple == \ -// RUN: -req=print-diags -vfs-files=/target_file1.swift=@%s,/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift /target_file1.swift | %FileCheck %s +// RUN: %sourcekitd-test -req=open -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift -pass-as-sourcetext -- %t/VFS/target_file1.swift %t/VFS/target_file2.swift -target %target-triple == \ +// RUN: -req=print-diags -vfs-files=%t/VFS/target_file1.swift=@%s,%t/VFS/target_file2.swift=@%S/../Inputs/vfs/other_file_in_target.swift %t/VFS/target_file1.swift | %FileCheck %s diff --git a/test/SourceKit/Sema/placeholders.swift b/test/SourceKit/Sema/placeholders.swift index 4acad0c618174..05200acd52b58 100644 --- a/test/SourceKit/Sema/placeholders.swift +++ b/test/SourceKit/Sema/placeholders.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=sema %S/../Inputs/placeholders.swift -- %S/../Inputs/placeholders.swift | %sed_clean > %t.placeholders.response -// RUN: diff -u %s.placeholders.response %t.placeholders.response +// RUN: diff --strip-trailing-cr -u %s.placeholders.response %t.placeholders.response diff --git a/test/SourceKit/Sema/placeholders.swift.placeholders.response b/test/SourceKit/Sema/placeholders.swift.placeholders.response index e3b4f588dfde6..0c192f93389f5 100644 --- a/test/SourceKit/Sema/placeholders.swift.placeholders.response +++ b/test/SourceKit/Sema/placeholders.swift.placeholders.response @@ -19,7 +19,37 @@ key.column: 19, key.filepath: placeholders.swift, key.severity: source.diagnostic.severity.error, - key.description: "type '()' does not conform to protocol 'Sequence'", - key.diagnostic_stage: source.diagnostic.stage.swift.sema + key.description: "for-in loop requires '()' to conform to 'Sequence'", + key.diagnostic_stage: source.diagnostic.stage.swift.sema, + key.ranges: [ + { + key.offset: 75, + key.length: 9 + } + ] + }, + { + key.line: 8, + key.column: 5, + key.filepath: placeholders.swift, + key.severity: source.diagnostic.severity.warning, + key.description: "constant 'myArray' inferred to have type '[()]', which may be unexpected", + key.diagnostic_stage: source.diagnostic.stage.swift.sema, + key.diagnostics: [ + { + key.line: 8, + key.column: 5, + key.filepath: placeholders.swift, + key.severity: source.diagnostic.severity.note, + key.description: "add an explicit type annotation to silence this warning", + key.fixits: [ + { + key.offset: 236, + key.length: 0, + key.sourcetext: ": [()]" + } + ] + } + ] } ] diff --git a/test/SourceKit/Sema/sema_big_array.swift b/test/SourceKit/Sema/sema_big_array.swift index 23bb5ebb80377..d13e6ca8da14f 100644 --- a/test/SourceKit/Sema/sema_big_array.swift +++ b/test/SourceKit/Sema/sema_big_array.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=sema %S/../Inputs/big_array.swift -- %S/../Inputs/big_array.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Sema/sema_config.swift b/test/SourceKit/Sema/sema_config.swift index b6cfd469ec7fd..8f99a596a7ff4 100644 --- a/test/SourceKit/Sema/sema_config.swift +++ b/test/SourceKit/Sema/sema_config.swift @@ -7,6 +7,6 @@ x = 2 #endif // RUN: %sourcekitd-test -req=sema %s -- %s > %t.false.response -// RUN: diff -u %s.false.response %t.false.response +// RUN: diff --strip-trailing-cr -u %s.false.response %t.false.response // RUN: %sourcekitd-test -req=sema %s -- %s -D FOO > %t.true.response -// RUN: diff -u %s.true.response %t.true.response +// RUN: diff --strip-trailing-cr -u %s.true.response %t.true.response diff --git a/test/SourceKit/Sema/sema_edits.swift b/test/SourceKit/Sema/sema_edits.swift index 82003ba72fd36..b7d813331dad0 100644 --- a/test/SourceKit/Sema/sema_edits.swift +++ b/test/SourceKit/Sema/sema_edits.swift @@ -6,4 +6,4 @@ print("hello") // RUN: %sourcekitd-test -req=open %s -- %s == -req=edit -pos=3:5 -replace="}" -length=0 %s == \ // RUN: -req=edit -pos=3:1 -replace="}" -length=5 %s == \ // RUN: -req=print-annotations %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Sema/sema_forbid_typecheck.swift b/test/SourceKit/Sema/sema_forbid_typecheck.swift index adba013988188..47b3107b3b9e1 100644 --- a/test/SourceKit/Sema/sema_forbid_typecheck.swift +++ b/test/SourceKit/Sema/sema_forbid_typecheck.swift @@ -1,4 +1,4 @@ // RUN: %sourcekitd-test -req=sema %S/../Inputs/forbid_typecheck_primary.swift -- \ // RUN: -Xfrontend -debug-forbid-typecheck-prefix -Xfrontend NOTYPECHECK -module-name forbid \ // RUN: %S/../Inputs/forbid_typecheck_2.swift %S/../Inputs/forbid_typecheck_primary.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Sema/sema_lazy_var.swift b/test/SourceKit/Sema/sema_lazy_var.swift index a2dc9207316a3..8517d2de4fc06 100644 --- a/test/SourceKit/Sema/sema_lazy_var.swift +++ b/test/SourceKit/Sema/sema_lazy_var.swift @@ -7,4 +7,4 @@ class C1 { } // RUN: %sourcekitd-test -req=sema %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/Sema/sema_playground.swift b/test/SourceKit/Sema/sema_playground.swift index abc7da5f5a82a..5a1aac1a4fe12 100644 --- a/test/SourceKit/Sema/sema_playground.swift +++ b/test/SourceKit/Sema/sema_playground.swift @@ -4,4 +4,4 @@ var x = 0 x // RUN: %sourcekitd-test -req=sema %s -- %s -Xfrontend -playground | %sed_clean > %t1.response -// RUN: diff -u %s.response %t1.response +// RUN: diff --strip-trailing-cr -u %s.response %t1.response diff --git a/test/SourceKit/Sema/sema_symlink.swift b/test/SourceKit/Sema/sema_symlink.swift index 1ce55f722ef58..ec3919a9ba5ea 100644 --- a/test/SourceKit/Sema/sema_symlink.swift +++ b/test/SourceKit/Sema/sema_symlink.swift @@ -2,6 +2,6 @@ // RUN: echo "let foo: Int = goo" > %t.dir/real.swift // RUN: %{python} %S/../../Inputs/symlink.py %t.dir/real.swift %t.dir/linked.swift // RUN: %sourcekitd-test -req=sema %t.dir/linked.swift -- %t.dir/real.swift | %sed_clean > %t.link.response -// RUN: diff -u %s.response %t.link.response +// RUN: diff --strip-trailing-cr -u %s.response %t.link.response // RUN: %sourcekitd-test -req=sema %t.dir/real.swift -- %t.dir/linked.swift | %sed_clean > %t.real.response -// RUN: diff -u %s.response %t.real.response +// RUN: diff --strip-trailing-cr -u %s.response %t.real.response diff --git a/test/SourceKit/SyntaxMapData/diags.swift b/test/SourceKit/SyntaxMapData/diags.swift index adf5d33eefc28..2fccb3c8ead55 100644 --- a/test/SourceKit/SyntaxMapData/diags.swift +++ b/test/SourceKit/SyntaxMapData/diags.swift @@ -1,3 +1,3 @@ // RUN: %sourcekitd-test -req=sema %S/Inputs/parse_error.swift -- %S/Inputs/parse_error.swift == \ // RUN: -req=edit -pos=3:1 -length=0 -replace=" " %S/Inputs/parse_error.swift == -req=print-diags %S/Inputs/parse_error.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift index c6b4c8b088b8c..07dfbbd5e2112 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift @@ -1,4 +1,4 @@ // RUN: %sourcekitd-test -req=syntax-map -pos=2:1 -length=2 -replace=" " %S/Inputs/syntaxmap-edit-del.swift | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response // RUN: %sourcekitd-test -req=syntax-map -pos=4:1 -length=2 -replace="" %S/Inputs/syntaxmap-edit-del.swift | %sed_clean > %t.response2 -// RUN: diff -u %s.response2 %t.response2 +// RUN: diff --strip-trailing-cr -u %s.response2 %t.response2 diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift b/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift index 5e53aac5ae41b..815fcca92d59a 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit.swift @@ -1,2 +1,2 @@ // RUN: %sourcekitd-test -req=syntax-map -pos=4:10 -replace="Bar" %S/Inputs/syntaxmap-edit.swift > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift b/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift index bac83dcba3841..e55fb5efcbc33 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift +++ b/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=syntax-map %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response let image = #imageLiteral(resourceName: "cloud.png") let color = #colorLiteral(red: 1, blue: 0, green: 1, alpha: 1) diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift.response index c515536e3f7e8..1db6ccf916858 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift.response +++ b/test/SourceKit/SyntaxMapData/syntaxmap-object-literals.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 279, + key.length: 299, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.syntaxmap: [ { @@ -11,51 +11,51 @@ { key.kind: source.lang.swift.syntaxtype.comment, key.offset: 71, - key.length: 40 + key.length: 60 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 112, + key.offset: 132, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 116, + key.offset: 136, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.objectliteral, - key.offset: 124, + key.offset: 144, key.length: 40 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 165, + key.offset: 185, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 169, + key.offset: 189, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.objectliteral, - key.offset: 177, + key.offset: 197, key.length: 50 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 228, + key.offset: 248, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 232, + key.offset: 252, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.objectliteral, - key.offset: 239, + key.offset: 259, key.length: 38 } ] diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift b/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift index 7ab6cfb5fbf13..030941e26bc33 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift +++ b/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift @@ -1,5 +1,5 @@ // RUN: %sourcekitd-test -req=syntax-map %s | %sed_clean > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response let fn = #function let f = #file diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift.response index 9baa46ae2c2b0..f954713c3f4ff 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift.response +++ b/test/SourceKit/SyntaxMapData/syntaxmap-pound-keyword.swift.response @@ -1,6 +1,6 @@ { key.offset: 0, - key.length: 342, + key.length: 362, key.diagnostic_stage: source.diagnostic.stage.swift.parse, key.syntaxmap: [ { @@ -11,166 +11,166 @@ { key.kind: source.lang.swift.syntaxtype.comment, key.offset: 71, - key.length: 40 + key.length: 60 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 112, + key.offset: 132, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 116, + key.offset: 136, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 121, + key.offset: 141, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 131, + key.offset: 151, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 135, + key.offset: 155, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 139, + key.offset: 159, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 145, + key.offset: 165, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 149, + key.offset: 169, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 153, + key.offset: 173, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 159, + key.offset: 179, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 163, + key.offset: 183, key.length: 1 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 167, + key.offset: 187, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 176, + key.offset: 196, key.length: 2 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 179, + key.offset: 199, key.length: 10 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 190, + key.offset: 210, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.number, - key.offset: 194, + key.offset: 214, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.buildconfig.keyword, - key.offset: 206, + key.offset: 226, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 210, + key.offset: 230, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.pounddirective.keyword, - key.offset: 216, + key.offset: 236, key.length: 6 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 223, + key.offset: 243, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.buildconfig.keyword, - key.offset: 232, + key.offset: 252, key.length: 7 }, { key.kind: source.lang.swift.syntaxtype.keyword, - key.offset: 240, + key.offset: 260, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.pounddirective.keyword, - key.offset: 245, + key.offset: 265, key.length: 8 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 254, + key.offset: 274, key.length: 9 }, { key.kind: source.lang.swift.syntaxtype.buildconfig.keyword, - key.offset: 265, + key.offset: 285, key.length: 5 }, { key.kind: source.lang.swift.syntaxtype.pounddirective.keyword, - key.offset: 271, + key.offset: 291, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 287, + key.offset: 307, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.string, - key.offset: 293, + key.offset: 313, key.length: 12 }, { key.kind: source.lang.swift.syntaxtype.identifier, - key.offset: 307, + key.offset: 327, key.length: 4 }, { key.kind: source.lang.swift.syntaxtype.number, - key.offset: 312, + key.offset: 332, key.length: 3 }, { key.kind: source.lang.swift.syntaxtype.pounddirective.keyword, - key.offset: 317, + key.offset: 337, key.length: 15 }, { key.kind: source.lang.swift.syntaxtype.buildconfig.keyword, - key.offset: 335, + key.offset: 355, key.length: 6 } ] diff --git a/test/SourceKit/SyntaxMapData/syntaxmap.swift b/test/SourceKit/SyntaxMapData/syntaxmap.swift index 3d9be253ef2c5..583c6368afd93 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap.swift +++ b/test/SourceKit/SyntaxMapData/syntaxmap.swift @@ -1,3 +1,3 @@ // XFAIL: broken_std_regex // RUN: %sourcekitd-test -req=syntax-map %S/Inputs/syntaxmap.swift > %t.response -// RUN: diff -u %s.response %t.response \ No newline at end of file +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/TypeContextInfo/typecontext_basic.swift b/test/SourceKit/TypeContextInfo/typecontext_basic.swift index dc94bb06d2138..38a2e0b329db4 100644 --- a/test/SourceKit/TypeContextInfo/typecontext_basic.swift +++ b/test/SourceKit/TypeContextInfo/typecontext_basic.swift @@ -26,4 +26,4 @@ func test(obj: C) { } // RUN: %sourcekitd-test -req=typecontextinfo -pos=25:22 %s -- %s > %t.response -// RUN: diff -u %s.response %t.response +// RUN: diff --strip-trailing-cr -u %s.response %t.response diff --git a/test/SourceKit/TypeContextInfo/typecontext_generics.swift b/test/SourceKit/TypeContextInfo/typecontext_generics.swift index 19a55d0683a7c..1f045717a368d 100644 --- a/test/SourceKit/TypeContextInfo/typecontext_generics.swift +++ b/test/SourceKit/TypeContextInfo/typecontext_generics.swift @@ -20,15 +20,15 @@ struct S { } // RUN: %sourcekitd-test -req=typecontextinfo -pos=7:12 %s -- %s > %t.response.1 -// RUN: diff -u %s.response.1 %t.response.1 +// RUN: diff --strip-trailing-cr -u %s.response.1 %t.response.1 // RUN: %sourcekitd-test -req=typecontextinfo -pos=8:12 %s -- %s > %t.response.2 -// RUN: diff -u %s.response.2 %t.response.2 +// RUN: diff --strip-trailing-cr -u %s.response.2 %t.response.2 // RUN: %sourcekitd-test -req=typecontextinfo -pos=9:17 %s -- %s > %t.response.3 -// RUN: diff -u %s.response.3 %t.response.3 +// RUN: diff --strip-trailing-cr -u %s.response.3 %t.response.3 // RUN: %sourcekitd-test -req=typecontextinfo -pos=14:18 %s -- %s > %t.response.4 -// RUN: diff -u %s.response.4 %t.response.4 +// RUN: diff --strip-trailing-cr -u %s.response.4 %t.response.4 // RUN: %sourcekitd-test -req=typecontextinfo -pos=15:18 %s -- %s > %t.response.5 -// RUN: diff -u %s.response.5 %t.response.5 +// RUN: diff --strip-trailing-cr -u %s.response.5 %t.response.5 // RUN: %sourcekitd-test -req=typecontextinfo -pos=16:18 %s -- %s > %t.response.6 -// RUN: diff -u %s.response.6 %t.response.6 +// RUN: diff --strip-trailing-cr -u %s.response.6 %t.response.6 diff --git a/test/SourceKit/lit.local.cfg b/test/SourceKit/lit.local.cfg index 61d3d9694aaa7..a85c4b706b6ec 100644 --- a/test/SourceKit/lit.local.cfg +++ b/test/SourceKit/lit.local.cfg @@ -1,3 +1,6 @@ +import os + + if 'sourcekit' not in config.available_features: config.unsupported = True @@ -9,13 +12,10 @@ elif 'swift_evolve' in config.available_features: config.unsupported = True else: - sed_clean = r"sed -e 's/key.filepath: \".*[/\\\\]\\(.*\\)\\.swiftmodule[/\\\\].*\\.swiftmodule\"/key.filepath: \\1.swiftmodule/g'" - sed_clean += r" | sed -e 's/key.filepath: \".*[/\\\\]\\(.*\\)\\.swiftmodule\"/key.filepath: \\1.swiftmodule/g'" - sed_clean += r" | sed -e 's/key.filepath: \".*[/\\\\]\\(.*\\)\\.swift\"/key.filepath: \\1.swift/g'" - sed_clean += r" | sed -e 's/key.filepath: \".*[/\\\\]\\(.*\\)-[0-9A-Z]*\\.pcm\"/key.filepath: \\1.pcm/g'" - sed_clean += r" | sed -e 's/ file=\\\\\".*[/\\\\]\\(.*\\)\\.h\\\\\"/ file=\\1.h/g'" + sk_path_sanitize = os.path.join(os.path.dirname(__file__), 'Inputs', 'sourcekitd_path_sanitize.py') - config.substitutions.append( ('%sourcekitd-test', config.sourcekitd_test) ) - config.substitutions.append( ('%complete-test', config.complete_test) ) + config.substitutions.append( ('%sourcekitd-test', '%s -module-cache-path %r' % (config.sourcekitd_test, config.clang_module_cache_path)) ) + config.substitutions.append( ('%complete-test', '%s -module-cache-path=%r' % (config.complete_test, config.clang_module_cache_path)) ) config.substitutions.append( ('%swiftlib_dir', config.swiftlib_dir) ) - config.substitutions.append( ('%sed_clean', sed_clean) ) + config.substitutions.append( ('%sed_clean', '%s %s' % (sys.executable, sk_path_sanitize) ) +) diff --git a/test/SymbolGraph/Module.swift b/test/SymbolGraph/Module.swift new file mode 100644 index 0000000000000..dcfba01e9a43b --- /dev/null +++ b/test/SymbolGraph/Module.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SymbolGraphModule -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SymbolGraphModule -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: module +// CHECK-NEXT: "name": "SymbolGraphModule" +// CHECK-NEXT: platform +// CHECK-NEXT: architecture +// CHECK: vendor +// CHECK-NEXT: operatingSystem +// CHECK-NEXT: name +// CHECK-NEXT: minimumVersion +// CHECK-NEXT: major +// CHECK-NEXT: minor +// CHECK-NEXT: patch diff --git a/test/SymbolGraph/Relationships/ConformsTo.swift b/test/SymbolGraph/Relationships/ConformsTo.swift new file mode 100644 index 0000000000000..c895fd5f7bfc6 --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +public struct S: P { + public var x: Int +} + +// CHECK: "kind": "conformsTo" +// CHECK-NEXT: "source": "s:10ConformsTo1SV" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/DefaultImplementationOf.swift b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift new file mode 100644 index 0000000000000..3193cfdf15200 --- /dev/null +++ b/test/SymbolGraph/Relationships/DefaultImplementationOf.swift @@ -0,0 +1,18 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DefaultImplementationOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DefaultImplementationOf -I %t -pretty-print -o %t/DefaultImplementationOf.symbols.json +// RUN: %FileCheck %s --input-file %t/DefaultImplementationOf.symbols.json + +public protocol P { + var x: Int { get } +} + +extension P { + public var x: Int { + return 2 + } +} + +// CHECK: "kind": "defaultImplementationOf", +// CHECK-NEXT: "source": "s:23DefaultImplementationOf1PPAAE1xSivp", +// CHECK-NEXT: "target": "s:23DefaultImplementationOf1PP1xSivp" diff --git a/test/SymbolGraph/Relationships/InheritsFrom.swift b/test/SymbolGraph/Relationships/InheritsFrom.swift new file mode 100644 index 0000000000000..feb9c4f9ec61b --- /dev/null +++ b/test/SymbolGraph/Relationships/InheritsFrom.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InheritsFrom -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InheritsFrom -I %t -pretty-print -o %t/InheritsFrom.symbols.json +// RUN: %FileCheck %s --input-file %t/InheritsFrom.symbols.json + +public class Base {} +public class Derived: Base {} + +// CHECK: "kind": "inheritsFrom" +// CHECK-NEXT: "source": "s:12InheritsFrom7DerivedC" +// CHECK-NEXT: "target": "s:12InheritsFrom4BaseC" diff --git a/test/SymbolGraph/Relationships/MemberOf.swift b/test/SymbolGraph/Relationships/MemberOf.swift new file mode 100644 index 0000000000000..f338e74aff62f --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name MemberOf -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name MemberOf -I %t -pretty-print -o %t/MemberOf.symbols.json +// RUN: %FileCheck %s --input-file %t/MemberOf.symbols.json + +public struct S { + public var x: Int +} + +// CHECK: "kind": "memberOf" +// CHECK-NEXT: "source": "s:8MemberOf1SV1xSivp" +// CHECK-NEXT: "target": "s:8MemberOf1SV" diff --git a/test/SymbolGraph/Relationships/Overrides.swift b/test/SymbolGraph/Relationships/Overrides.swift new file mode 100644 index 0000000000000..85d2f7afb57f2 --- /dev/null +++ b/test/SymbolGraph/Relationships/Overrides.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Overrides -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Overrides -I %t -pretty-print -o %t/Overrides.symbols.json +// RUN: %FileCheck %s --input-file %t/Overrides.symbols.json + +public class Base { + public var x: Int { + return 1 + } +} + +public class Derived: Base { + public override var x: Int { + return 2 + } +} + +// CHECK: "kind": "overrides" +// CHECK-NEXT: "source": "s:9Overrides7DerivedC1xSivp" +// CHECK-NEXT: "target": "s:9Overrides4BaseC1xSivp" diff --git a/test/SymbolGraph/Relationships/RequirementOf.swift b/test/SymbolGraph/Relationships/RequirementOf.swift new file mode 100644 index 0000000000000..a06507b523b7e --- /dev/null +++ b/test/SymbolGraph/Relationships/RequirementOf.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ConformsTo -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ConformsTo -I %t -pretty-print -o %t/ConformsTo.symbols.json +// RUN: %FileCheck %s --input-file %t/ConformsTo.symbols.json + +public protocol P { + var x: Int { get } +} + +// CHECK: "kind": "requirementOf" +// CHECK-NEXT: "source": "s:10ConformsTo1PP1xSivp" +// CHECK-NEXT: "target": "s:10ConformsTo1PP" diff --git a/test/SymbolGraph/Relationships/TargetFallback.swift b/test/SymbolGraph/Relationships/TargetFallback.swift new file mode 100644 index 0000000000000..274c423d0f748 --- /dev/null +++ b/test/SymbolGraph/Relationships/TargetFallback.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name TargetFallback -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name TargetFallback -I %t -pretty-print -o %t/TargetFallback.symbols.json +// RUN: %FileCheck %s --input-file %t/TargetFallback.symbols.json + +public struct S: CustomStringConvertible { + public var x: Int + public var description: String { + return x.description + } +} + +// CHECK: "kind": "conformsTo", +// CHECK-NEXT: "source": "s:14TargetFallback1SV", +// CHECK-NEXT: "target": "s:s23CustomStringConvertibleP", +// CHECK-NEXT: "targetFallback": "Swift.CustomStringConvertible" diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift new file mode 100644 index 0000000000000..eb6e3bb15633f --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -o %t/IncludeInternal.symbols.json -minimum-access-level internal +// RUN: %FileCheck %s --input-file %t/IncludeInternal.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldAlsoAppear { + internal var x: Int +} + +private struct ShouldntAppear { + var x: Int +} + +// CHECK: ShouldAppear +// CHECK: ShouldAlsoAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift new file mode 100644 index 0000000000000..258b29058f1c1 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/PublicDefault.swift @@ -0,0 +1,15 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name PublicDefault -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name PublicDefault -I %t -pretty-print -o %t/PublicDefault.symbols.json +// RUN: %FileCheck %s --input-file %t/PublicDefault.symbols.json + +public struct ShouldAppear { + public var x: Int +} + +internal struct ShouldntAppear { + internal var x: Int +} + +// CHECK: ShouldAppear +// CHECK-NOT: ShouldntAppear diff --git a/test/SymbolGraph/Symbols/AccessLevels.swift b/test/SymbolGraph/Symbols/AccessLevels.swift new file mode 100644 index 0000000000000..e7d3daec46132 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevels.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name AccessLevels -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name AccessLevels -I %t -pretty-print -o %t/AccessLevels.symbols.json +// RUN: %FileCheck %s --input-file %t/AccessLevels.symbols.json + +// CHECK: "accessLevel": "public" + +public struct PublicStruct { + public var x: Int +} + +// CHECK-NOT: "accessLevel": "private" diff --git a/test/SymbolGraph/Symbols/DocComment.swift b/test/SymbolGraph/Symbols/DocComment.swift new file mode 100644 index 0000000000000..b9c07d2df1548 --- /dev/null +++ b/test/SymbolGraph/Symbols/DocComment.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DocComment -emit-module-path %t/DocComment.swiftmodule +// RUN: %target-swift-symbolgraph-extract -module-name DocComment -I %t -pretty-print -o %t/DocComment.symbols.json +// RUN: %FileCheck %s --input-file %t/DocComment.symbols.json + +// CHECK: "text": "Single line." + +/// Single line. +public struct S1 {} + +// CHECK: "text": "Two" +// CHECK: "text": "lines." + +/// Two +/// lines. +public struct S2 {} + +// CHECK: "text": "There are" +// CHECK: "text": "three lines" +// CHECK: "text": "here." + +/// There are +/// three lines +/// here. +public struct S3 {} + +// CHECK: "text": "Comment" +// CHECK: "text": "block." + +/** + Comment + block. +*/ +public struct S4 {} + +// CHECK: "text": "Comment block" +// CHECK: "text": "with more indentation." + +/** + Comment block + with more indentation. + */ +public struct S5 {} + +// CHECK: "text": "With" +// CHECK: "text": "ASCII" +// CHECK: "text": "art." + +/** + * With + * ASCII + * art. + */ +public struct S6 {} diff --git a/test/SymbolGraph/Symbols/Kinds.swift b/test/SymbolGraph/Symbols/Kinds.swift new file mode 100644 index 0000000000000..ec79235016659 --- /dev/null +++ b/test/SymbolGraph/Symbols/Kinds.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Kinds -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Kinds -I %t -pretty-print -o %t/Kinds.symbols.json +// RUN: %FileCheck %s --input-file %t/Kinds.symbols.json + +// CHECK: "identifier": "swift.class" +// CHECK-NEXT: "displayName": "Class" +public class C {} + +// CHECK: "identifier": "swift.struct" +// CHECK-NEXT: "displayName": "Structure" +public struct S { + // CHECK: "identifier": "swift.variable" + // CHECK-NEXT: "displayName": "Variable" + public var x: Int + + // CHECK: "identifier": "swift.initializer" + // CHECK-NEXT: "displayName": "Initializer" + public init(x: Int) { + self.x = x + } + + // CHECK: "identifier": "swift.function" + // CHECK-NEXT: "displayName": "Function" + public func foo() {} +} + +// CHECK: "identifier": "swift.enum" +// CHECK-NEXT: "displayName": "Enumeration" +public enum E { + // CHECK: "identifier": "swift.enum.case" + // CHECK-NEXT: "displayName": "Case" + case oneCase +} + +// CHECK: "identifier": "swift.protocol" +// CHECK-NEXT: "displayName": "Protocol" +public protocol P {} + +// CHECK: "identifier": "swift.typealias" +// CHECK-NEXT: "displayName": "Type Alias" +public typealias Alias = S diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift new file mode 100644 index 0000000000000..d380b84e5d766 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Availability.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Availability -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Availability -I %t -pretty-print -o %t/Availability.symbols.json +// RUN: %FileCheck %s --input-file %t/Availability.symbols.json + +@available(macOS, introduced: 10.9, deprecated: 10.10, obsoleted: 10.11, message: "Everyone makes mistakes", renamed: "S2") +public struct S {} + +// CHECK: "domain": "macOS" + +// CHECK: introduced +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 9 + +// CHECK: deprecated +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 10 + +// CHECK: obsoleted +// CHECK-NEXT: "major": 10 +// CHECK-NEXT: "minor": 11 + +// CHECK: "message": "Everyone makes mistakes" +// CHECK: "renamed": "S2" diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift new file mode 100644 index 0000000000000..96488cbc63d20 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyDeprecated.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyDeprecated -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyDeprecated -I %t -pretty-print -o %t/UnconditionallyDeprecated.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyDeprecated.symbols.json + +@available(*, deprecated) +public struct UnconditionallyDeprecated {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyDeprecated": true diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift new file mode 100644 index 0000000000000..9fdcb6fa779f9 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/UnconditionallyUnavailable.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name UnconditionallyUnavailable -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name UnconditionallyUnavailable -I %t -pretty-print -o %t/UnconditionallyUnavailable.symbols.json +// RUN: %FileCheck %s --input-file %t/UnconditionallyUnavailable.symbols.json + +@available(*, unavailable) +public struct UnconditionallyUnavailable {} + +// CHECK-NOT: domain +// CHECK: "isUnconditionallyUnavailable": true diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift new file mode 100644 index 0000000000000..9f35f6fde8b5e --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name DeclarationFragments -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name DeclarationFragments -I %t -pretty-print -o %t/DeclarationFragments.symbols.json +// RUN: %FileCheck %s --input-file %t/DeclarationFragments.symbols.json + +public func foo(f: @escaping () -> (), x: Int = 2, s: S) {} + +// CHECK: declarationFragments + +// CHECK: "kind": "keyword", +// CHECK-NEXT: "spelling": "func" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "foo" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": "<" + +// CHECK: "kind": "genericParameter", +// CHECK-NEXT: "spelling": "S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ">(" + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "f" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": () -> (), " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "x" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": " + +// CHECK: "kind": "typeIdentifier", +// CHECK-NEXT: "spelling": "Int", +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": " = 2, " + +// CHECK: "kind": "identifier", +// CHECK-NEXT: "spelling": "s" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ": S" + +// CHECK: "kind": "text", +// CHECK-NEXT: "spelling": ")" diff --git a/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift new file mode 100644 index 0000000000000..ae2a4bd6c441a --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/FunctionSignature.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name FunctionSignature -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name FunctionSignature -I %t -pretty-print -o %t/FunctionSignature.symbols.json +// RUN: %FileCheck %s --input-file %t/FunctionSignature.symbols.json + +public func foo(_ noext: Int, ext int: Int) -> String { + return "OK" +} + +// CHECK: "name": "noext" +// CHECK-NOT: "internalName": "noext" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "noext" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: "name": "ext" +// CHECK-NEXT: "internalName": "int" +// CHECK-NEXT: declarationFragments + +// CHECK: "kind": "identifier" +// CHECK-NEXT: "spelling": "int" +// CHECK: "kind": "text" +// CHECK-NEXT: "spelling": ": " +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "Int" +// CHECK-NEXT: "preciseIdentifier": "s:Si" + +// CHECK: returns +// CHECK: "kind": "typeIdentifier" +// CHECK-NEXT: "spelling": "String" +// CHECK-NEXT: "preciseIdentifier": "s:SS" diff --git a/test/SymbolGraph/Symbols/Names.swift b/test/SymbolGraph/Symbols/Names.swift new file mode 100644 index 0000000000000..4ea658167d919 --- /dev/null +++ b/test/SymbolGraph/Symbols/Names.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -o %t/Names.symbols.json +// RUN: %FileCheck %s --input-file %t/Names.symbols.json + +public struct MyStruct {} + +// CHECK: names +// CHECK-NEXT: "title": "MyStruct" diff --git a/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift new file mode 100644 index 0000000000000..e62567f069c4c --- /dev/null +++ b/test/SymbolGraph/Symbols/SkipsPublicUnderscore.swift @@ -0,0 +1,11 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name SkipsPublicUnderscore -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name SkipsPublicUnderscore -I %t -pretty-print -o %t/SymbolGraphModule.symbols.json +// RUN: %FileCheck %s --input-file %t/SymbolGraphModule.symbols.json + +public struct _ShouldntAppear { + public var shouldntAppear: Int +} + +// CHECK-NOT: _ShouldntAppear +// CHECK-NOT: shouldntAppear diff --git a/test/Syntax/Inputs/serialize_struct_decl.json b/test/Syntax/Inputs/serialize_struct_decl.json index 78b5dfcd65457..130604d4aa75a 100644 --- a/test/Syntax/Inputs/serialize_struct_decl.json +++ b/test/Syntax/Inputs/serialize_struct_decl.json @@ -319,7 +319,7 @@ "presence": "Present" }, { - "id": 24, + "id": 23, "kind": "GenericArgumentList", "layout": [ { @@ -357,7 +357,7 @@ "presence": "Present" }, { - "id": 23, + "id": 24, "tokenKind": { "kind": "r_angle" }, diff --git a/test/Syntax/Outputs/round_trip_invalid.swift.withkinds b/test/Syntax/Outputs/round_trip_invalid.swift.withkinds index bfa790a6a2882..3b09c5916dbed 100644 --- a/test/Syntax/Outputs/round_trip_invalid.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_invalid.swift.withkinds @@ -9,7 +9,7 @@ // RUN: %swift-syntax-test -deserialize-raw-tree -input-source-filename %t.dump -output-filename %t // RUN: diff -u %s %t -let strings: [Strin[g]? +let strings: [Strin[g]? // Function body without closing brace token. func foo() { @@ -19,5 +19,4 @@ class C { struct S { enum E { protocol P { -extension P { -public init(a: (Int || String)) +extension P { diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 4576a3d13595e..d39e1f51729c2 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -142,10 +142,7 @@ typealias G = (a x: = () rethrows -> () typealias I = (A & B<C>) -> C & D typealias J = inout @autoclosure () -> Int -typealias K = (@invalidAttr Int, inout Int, __shared Int, __owned Int) -> () -typealias L = (inout Int) -> Int -typealias M = (inout arg: Int) -> Int -typealias N = (inout _ arg: @objc Int) -> Int +typealias K = (@invalidAttr Int, inout Int, __shared Int, __owned Int) -> () @objc private typealias T<a,b> = Int @objc private typealias T<a,b> @@ -548,11 +545,11 @@ struct S : Q{ var st0 = ("a", "b") var rm0: (String, String) { - _read { yield (("a", "b")) } + _read { yield (("a", "b")) } _modify { yield &st0 } } var rm1: (String, String) { - _read { yield (st0) } + _read { yield (st0) } } } @@ -562,9 +559,9 @@ struct ReadModify { func foo() {} -#sourceLocation() +#sourceLocation() -"abc \( } ) def" +"abc \( } ) def" #assert(true) #assert(false) diff --git a/test/Syntax/round_trip_invalid.swift b/test/Syntax/round_trip_invalid.swift index 49817bd8e5261..9cc7750288de3 100644 --- a/test/Syntax/round_trip_invalid.swift +++ b/test/Syntax/round_trip_invalid.swift @@ -20,4 +20,3 @@ struct S { enum E { protocol P { extension P { -public init(a: (Int || String)) diff --git a/test/Syntax/round_trip_misc.swift b/test/Syntax/round_trip_misc.swift index faec3efc3603b..8704878eb3e4e 100644 --- a/test/Syntax/round_trip_misc.swift +++ b/test/Syntax/round_trip_misc.swift @@ -4,9 +4,6 @@ class C { // Erroneous typealias decl. typealias Inner: Foo = Int - typealias Alias1 = [Generic (a b: [Generic @objc func diff --git a/test/Syntax/round_trip_parse_gen.swift b/test/Syntax/round_trip_parse_gen.swift index 69b03ea130cb7..36f0f67b30cf3 100644 --- a/test/Syntax/round_trip_parse_gen.swift +++ b/test/Syntax/round_trip_parse_gen.swift @@ -143,9 +143,6 @@ typealias H = () rethrows -> () typealias I = (A & B) -> C & D typealias J = inout @autoclosure () -> Int typealias K = (@invalidAttr Int, inout Int, __shared Int, __owned Int) -> () -typealias L = (inout Int) -> Int -typealias M = (inout arg: Int) -> Int -typealias N = (inout _ arg: @objc Int) -> Int @objc private typealias T = Int @objc private typealias T diff --git a/test/Syntax/serialize_tupletype.swift b/test/Syntax/serialize_tupletype.swift index e0f1c0603551b..20246475453e6 100644 --- a/test/Syntax/serialize_tupletype.swift +++ b/test/Syntax/serialize_tupletype.swift @@ -1,4 +1,4 @@ // RUN: %swift-syntax-test -input-source-filename %s -serialize-raw-tree > %t.result // RUN: diff %t.result %s.result -typealias x = (b: Int, _: String) +typealias x = (_ b: Int, _: String) diff --git a/test/Syntax/serialize_tupletype.swift.result b/test/Syntax/serialize_tupletype.swift.result index fd4b66ff361b8..2c0aa4bcc1118 100644 --- a/test/Syntax/serialize_tupletype.swift.result +++ b/test/Syntax/serialize_tupletype.swift.result @@ -1,23 +1,23 @@ { - "id": 23, + "id": 24, "kind": "SourceFile", "layout": [ { - "id": 22, + "id": 23, "kind": "CodeBlockItemList", "layout": [ { - "id": 20, + "id": 21, "kind": "CodeBlockItem", "layout": [ { - "id": 19, + "id": 20, "kind": "TypealiasDecl", "layout": [ null, null, { - "id": 17, + "id": 18, "tokenKind": { "kind": "kw_typealias" }, @@ -48,7 +48,7 @@ "presence": "Present" }, { - "id": 18, + "id": 19, "tokenKind": { "kind": "identifier", "text": "x" @@ -64,7 +64,7 @@ }, null, { - "id": 16, + "id": 17, "kind": "TypeInitializerClause", "layout": [ { @@ -82,7 +82,7 @@ "presence": "Present" }, { - "id": 15, + "id": 16, "kind": "TupleType", "layout": [ { @@ -95,16 +95,30 @@ "presence": "Present" }, { - "id": 13, + "id": 14, "kind": "TupleTypeElementList", "layout": [ { - "id": 8, + "id": 9, "kind": "TupleTypeElement", "layout": [ null, { "id": 3, + "tokenKind": { + "kind": "kw__" + }, + "leadingTrivia": [], + "trailingTrivia": [ + { + "kind": "Space", + "value": 1 + } + ], + "presence": "Present" + }, + { + "id": 4, "tokenKind": { "kind": "identifier", "text": "b" @@ -113,9 +127,8 @@ "trailingTrivia": [], "presence": "Present" }, - null, { - "id": 4, + "id": 5, "tokenKind": { "kind": "colon" }, @@ -129,11 +142,11 @@ "presence": "Present" }, { - "id": 6, + "id": 7, "kind": "SimpleTypeIdentifier", "layout": [ { - "id": 5, + "id": 6, "tokenKind": { "kind": "identifier", "text": "Int" @@ -149,7 +162,7 @@ null, null, { - "id": 7, + "id": 8, "tokenKind": { "kind": "comma" }, @@ -166,12 +179,12 @@ "presence": "Present" }, { - "id": 12, + "id": 13, "kind": "TupleTypeElement", "layout": [ null, { - "id": 9, + "id": 10, "tokenKind": { "kind": "kw__" }, @@ -181,7 +194,7 @@ }, null, { - "id": 4, + "id": 5, "tokenKind": { "kind": "colon" }, @@ -195,11 +208,11 @@ "presence": "Present" }, { - "id": 11, + "id": 12, "kind": "SimpleTypeIdentifier", "layout": [ { - "id": 10, + "id": 11, "tokenKind": { "kind": "identifier", "text": "String" @@ -222,7 +235,7 @@ "presence": "Present" }, { - "id": 14, + "id": 15, "tokenKind": { "kind": "r_paren" }, @@ -249,7 +262,7 @@ "presence": "Present" }, { - "id": 21, + "id": 22, "tokenKind": { "kind": "eof", "text": "" diff --git a/test/Syntax/syntax_diagnostics.swift b/test/Syntax/syntax_diagnostics.swift index 5e49009b1d6fa..e97652902e6de 100644 --- a/test/Syntax/syntax_diagnostics.swift +++ b/test/Syntax/syntax_diagnostics.swift @@ -1,3 +1,3 @@ // RUN: %target-swift-frontend -emit-syntax -primary-file %s -verify -typealias Inner: Foo // expected-error{{expected '=' in type alias declaration}} +typealias Inner: Foo // expected-error{{unknown declaration syntax exists in the source}} expected-error{{expected '=' in type alias declaration}} diff --git a/test/TBD/Inputs/api_grab_bag.swift b/test/TBD/Inputs/api_grab_bag.swift new file mode 100644 index 0000000000000..0de5d202ccac4 --- /dev/null +++ b/test/TBD/Inputs/api_grab_bag.swift @@ -0,0 +1,31 @@ +public class PublicClass { + public func method() { + } + + public init() { + } +} + +public class PublicSubclass: PublicClass { + public override func method() { + } +} + +public protocol PublicProtocol { + var publicStruct: PublicStruct { get } +} + +public struct PublicStruct { + public init() {} +} + +extension PublicStruct: PublicProtocol { + public var publicStruct: PublicStruct { return self } +} + +public enum PublicEnum: PublicProtocol { + case caseOne + case caseTwo + + public var publicStruct: PublicStruct { return PublicStruct() } +} diff --git a/test/TBD/Inputs/install-name-map-toasterkit.json b/test/TBD/Inputs/install-name-map-toasterkit.json new file mode 100644 index 0000000000000..1c41d78eef928 --- /dev/null +++ b/test/TBD/Inputs/install-name-map-toasterkit.json @@ -0,0 +1,12 @@ +[ + { + "module": "ToasterKit", + "install_name": "/System/Previous/macOS/ToasterKit.dylib", + "platforms": ["macOS"] + }, + { + "module": "ToasterKit", + "install_name": "/System/Previous/iOS/ToasterKit.dylib", + "platforms": ["iOS"] + } +] \ No newline at end of file diff --git a/test/TBD/Inputs/install-name-map.json b/test/TBD/Inputs/install-name-map.json new file mode 100644 index 0000000000000..a222f39e8541c --- /dev/null +++ b/test/TBD/Inputs/install-name-map.json @@ -0,0 +1,16 @@ +[ + { + "module": "Foo", + "platforms": ["macOS"], + "install_name": "/System/MacOS" + }, + { + "module": "Foo", + "install_name": "/System/default" + }, + { + "module": "Foo", + "platforms": ["iOS", "tvOS", "watchOS"], + "install_name": "/System/Others" + }, +] \ No newline at end of file diff --git a/test/TBD/Inputs/linker-directive.swift b/test/TBD/Inputs/linker-directive.swift new file mode 100644 index 0000000000000..9bb4ffe2e71f3 --- /dev/null +++ b/test/TBD/Inputs/linker-directive.swift @@ -0,0 +1,9 @@ +@available(OSX 10.8, iOS 10.2, *) +@_originallyDefinedIn(module: "ToasterKit", macOS 10.15, iOS 13) +public func toast() {} + +@available(OSX 10.8, iOS 10.2, *) +@_originallyDefinedIn(module: "ToasterKit", macOS 10.15, iOS 13) +public struct Vehicle { + public func move() {} +} \ No newline at end of file diff --git a/test/TBD/dylib-version-truncation.swift b/test/TBD/dylib-version-truncation.swift new file mode 100644 index 0000000000000..bc9bb431f6892 --- /dev/null +++ b/test/TBD/dylib-version-truncation.swift @@ -0,0 +1,27 @@ +// REQUIRES: VENDOR=apple +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 2.0 | %FileCheck %s --check-prefix TWOPOINTZERO +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 2 | %FileCheck %s --check-prefix TWOPOINTZERO +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 20.10 | %FileCheck %s --check-prefix TWENTYPOINTTEN + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 2.0 | %FileCheck %s --check-prefix TWOPOINTZEROCOMPAT +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 2 | %FileCheck %s --check-prefix TWOPOINTZEROCOMPAT +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 20.10 | %FileCheck %s --check-prefix TWENTYPOINTTENCOMPAT + +// Make sure we correctly truncate a value over 255 + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-current-version 20.300 2>&1 | %FileCheck %s --check-prefix TWENTYPOINTTHREEHUNDRED +// RUN: %target-swift-frontend -typecheck %s -emit-tbd-path - -tbd-compatibility-version 20.300 2>&1 | %FileCheck %s --check-prefix TWENTYPOINTTHREEHUNDREDCOMPAT + +// TWOPOINTZERO: current-version: 2 +// TWENTYPOINTTEN: current-version: 20.10 + +// TWOPOINTZEROCOMPAT: compatibility-version: 2 +// TWENTYPOINTTENCOMPAT: compatibility-version: 20.10 + +// TWENTYPOINTTHREEHUNDRED: warning: truncating current version '20.300' in TBD file to fit in 32-bit space used by old mach-o format +// TWENTYPOINTTHREEHUNDRED: current-version: 20.255 + +// TWENTYPOINTTHREEHUNDREDCOMPAT: warning: truncating compatibility version '20.300' in TBD file to fit in 32-bit space used by old mach-o format +// TWENTYPOINTTHREEHUNDREDCOMPAT: compatibility-version: 20.255 diff --git a/test/TBD/dylib-version.swift b/test/TBD/dylib-version.swift index eb5fb611a8991..7edb907acdb85 100644 --- a/test/TBD/dylib-version.swift +++ b/test/TBD/dylib-version.swift @@ -12,16 +12,6 @@ // BOTH: current-version: 2.0.3 // BOTH: compatibility-version: 1.7 // CURRENT: current-version: 2 - -// Compatibility version defaults to 1 if not present in TBD file, and -// tapi does not write field if compatibility version is 1 - -// CURRENT-NOT: compatibility-version: 1 - // COMPAT: compatibility-version: 2 -// Same as above -- current version defaults to 1 and is not present in -// emitted TBD file if it's 1. -// COMPAT-NOT: current-version: 1 - -// BOGUS: version component contains non-numeric characters +// BOGUS: invalid dynamic library compatibility version 'not_a_version_string' diff --git a/test/TBD/installapi-flag.swift b/test/TBD/installapi-flag.swift index 685b5cfb6d1f7..da995fee26e1d 100644 --- a/test/TBD/installapi-flag.swift +++ b/test/TBD/installapi-flag.swift @@ -3,8 +3,8 @@ // 1. Emit two TBDs, one with -tbd-is-installapi set, and one without -// RUN: %target-swift-frontend -emit-ir -o /dev/null %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/flag-provided.tbd -// RUN: %target-swift-frontend -emit-ir -o /dev/null %s -emit-tbd -emit-tbd-path %t/flag-omitted.tbd +// RUN: %target-swift-frontend -typecheck %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/flag-provided.tbd +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/flag-omitted.tbd // 2. Ensure that the file with -tbd-is-installapi passed includes the installapi flag diff --git a/test/TBD/linker-directives-ld-previous-ios.swift b/test/TBD/linker-directives-ld-previous-ios.swift new file mode 100644 index 0000000000000..4e2d9987c54ee --- /dev/null +++ b/test/TBD/linker-directives-ld-previous-ios.swift @@ -0,0 +1,14 @@ +// REQUIRES: OS=ios + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %S/Inputs/linker-directive.swift -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map-toasterkit.json +// RUN: %FileCheck %s < %t/linker_directives.tbd +// RUN: %target-swift-frontend -typecheck %S/Inputs/linker-directive.swift -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map-toasterkit.json +// RUN: %FileCheck %s < %t/linker_directives.tbd + +// CHECK: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit5toastyyF$ +// CHECK: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit7VehicleV4moveyyF$ +// CHECK: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit7VehicleVMa$ +// CHECK: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit7VehicleVMn$ +// CHECK: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit7VehicleVN$ diff --git a/test/TBD/linker-directives-ld-previous-macos.swift b/test/TBD/linker-directives-ld-previous-macos.swift new file mode 100644 index 0000000000000..f678721facd96 --- /dev/null +++ b/test/TBD/linker-directives-ld-previous-macos.swift @@ -0,0 +1,20 @@ +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %S/Inputs/linker-directive.swift -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map-toasterkit.json +// RUN: %FileCheck %s < %t/linker_directives.tbd +// RUN: %target-swift-frontend -typecheck %S/Inputs/linker-directive.swift -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map-toasterkit.json +// RUN: %FileCheck %s < %t/linker_directives.tbd + +// RUN: %target-swift-frontend -target-variant x86_64-apple-ios13.0-macabi -typecheck %S/Inputs/linker-directive.swift -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map-toasterkit.json +// RUN: %FileCheck -check-prefix=CHECK-ZIPPERED %s < %t/linker_directives.tbd + +// CHECK: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit5toastyyF$ +// CHECK: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit7VehicleV4moveyyF$ +// CHECK: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit7VehicleVMa$ +// CHECK: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit7VehicleVMn$ +// CHECK: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit7VehicleVN$ + +// CHECK-ZIPPERED: $ld$previous$/System/Previous/iOS/ToasterKit.dylib$$2$10.2$13.0$_$s10ToasterKit5toastyyF$ +// CHECK-ZIPPERED: $ld$previous$/System/Previous/macOS/ToasterKit.dylib$$1$10.8$10.15$_$s10ToasterKit5toastyyF$ diff --git a/test/TBD/linker-directives.swift b/test/TBD/linker-directives.swift new file mode 100644 index 0000000000000..3e5b9e11c88e0 --- /dev/null +++ b/test/TBD/linker-directives.swift @@ -0,0 +1,25 @@ +// REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -tbd-is-installapi -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd +// RUN: %FileCheck %s --check-prefix CHECK-HAS --check-prefix CHECK-HAS-NOT < %t/linker_directives.tbd + +@available(OSX 10.8, *) +@_originallyDefinedIn(module: "ToasterKit", macOS 10.15) +public func toast() {} + +// CHECK-HAS: $ld$hide$os10.14$_$s10ToasterKit5toastyyF +// CHECK-HAS: $ld$hide$os10.8$_$s10ToasterKit5toastyyF + +// CHECK-HAS-NOT-NOT: $ld$hide$os10.15$_$s10ToasterKit5toastyyF +// CHECK-HAS-NOT-NOT: $ld$hide$os10.7$_$s10ToasterKit5toastyyF + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -emit-ldadd-cfile-path %t/ldAdd.c -module-name AppKit +// RUN: %FileCheck %s --check-prefix CHECK-C-SYMBOL < %t/ldAdd.c + +// CHECK-C-SYMBOL: $ld$add$os10.14$_$s10ToasterKit5toastyyF +// CHECK-C-SYMBOL: $ld$add$os10.8$_$s10ToasterKit5toastyyF \ No newline at end of file diff --git a/test/TBD/linking-with-tbd.swift b/test/TBD/linking-with-tbd.swift new file mode 100644 index 0000000000000..b06ea3010c108 --- /dev/null +++ b/test/TBD/linking-with-tbd.swift @@ -0,0 +1,40 @@ +// REQUIRES: VENDOR=apple + +// 1. Create a skeleton of a framework +// RUN: %empty-directory(%t/APIGrabBag.framework/Modules/APIGrabBag.swiftmodule) +// RUN: %empty-directory(%t/APIGrabBag.framework/Headers) + +// 1. Compile api_grab_bag.swift to a .tbd and put it in %t + +// RUN: %target-swift-frontend -emit-module -o %t/APIGrabBag.framework/Modules/APIGrabBag.swiftmodule/%target-cpu.swiftmodule -emit-tbd-path %t/APIGrabBag.framework/APIGrabBag.tbd %S/Inputs/api_grab_bag.swift -module-name APIGrabBag -tbd-install_name %t/APIGrabBag.framework/APIGrabBag + +// 2. Compile the current file against the TBD + +// RUN: %target-build-swift -emit-executable %s -o %t/executable -F %t -framework APIGrabBag + +// 3. Install the actual dylib into the framework + +// RUN: %target-build-swift -emit-library %S/Inputs/api_grab_bag.swift -module-name APIGrabBag -o %t/APIGrabBag.framework/APIGrabBag + +// 4. Codesign the executable and run it + +// RUN: %target-codesign %t/executable %t/APIGrabBag.framework/APIGrabBag +// RUN: %target-run %t/executable + +import APIGrabBag + +func useAPIs() { + let c = PublicClass() + c.method() + + let sub = PublicSubclass() + sub.method() + + let s = PublicStruct() + let t = s.publicStruct + + var e = PublicEnum.caseOne + e = .caseTwo + + _ = e.publicStruct +} diff --git a/test/TBD/previous-install-name-map.swift b/test/TBD/previous-install-name-map.swift new file mode 100644 index 0000000000000..25bfff72ebb11 --- /dev/null +++ b/test/TBD/previous-install-name-map.swift @@ -0,0 +1,13 @@ +// REQUIRES: VENDOR=apple +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map.json >& %t/remark.txt +// RUN: %FileCheck %s < %t/remark.txt + +// CHECK: remark: default previous install name for Foo is /System/default +// CHECK: remark: previous install name for Foo in macOS is /System/MacOS +// CHECK: remark: previous install name for Foo in iOS is /System/Others +// CHECK: remark: previous install name for Foo in tvOS is /System/Others +// CHECK: remark: previous install name for Foo in watchOS is /System/Others diff --git a/test/TypeCoercion/integer_literals.swift b/test/TypeCoercion/integer_literals.swift index 2e62d11065d13..a7cdfce23626e 100644 --- a/test/TypeCoercion/integer_literals.swift +++ b/test/TypeCoercion/integer_literals.swift @@ -31,9 +31,9 @@ func overflow_check() { } // Coercion chaining. -struct meters : ExpressibleByIntegerLiteral { +struct meters : ExpressibleByIntegerLiteral { var value : Int8 - + init(_ value: Int8) { self.value = value } @@ -44,9 +44,9 @@ struct meters : ExpressibleByIntegerLiteral { } } -struct supermeters : ExpressibleByIntegerLiteral { // expected-error{{type 'supermeters' does not conform to protocol 'ExpressibleByIntegerLiteral'}} expected-note {{do you want to add protocol stubs?}} +struct supermeters : ExpressibleByIntegerLiteral { // expected-error{{type 'supermeters' does not conform to protocol 'ExpressibleByIntegerLiteral'}} var value : meters - + typealias IntegerLiteralType = meters // expected-note{{possibly intended match 'supermeters.IntegerLiteralType' (aka 'meters') does not conform to '_ExpressibleByBuiltinIntegerLiteral'}} init(_integerLiteral value: meters) { self.value = value @@ -63,6 +63,6 @@ func chaining() { func memberaccess() { Int32(5._value) // expected-warning{{unused}} // This diagnostic is actually better than it looks, because the inner type is Builtin.Int32, not actually Int32. - let x : Int32 = 7._value // expected-error{{cannot convert value of type 'Builtin.Int32' to specified type 'Swift.Int32'}} + let x : Int32 = 7._value // expected-error{{cannot convert value of type 'Builtin.Int32' to specified type 'Int32'}} _ = x } diff --git a/test/api-digester/Inputs/Foo-new-version/foo.h b/test/api-digester/Inputs/Foo-new-version/foo.h index 61b56a05a5f17..b307b23510839 100644 --- a/test/api-digester/Inputs/Foo-new-version/foo.h +++ b/test/api-digester/Inputs/Foo-new-version/foo.h @@ -1,4 +1,4 @@ -#import +@import ObjectiveC; @protocol ObjcProt -(void) someFunctionFromProt; diff --git a/test/api-digester/Inputs/Foo-new-version/module.modulemap b/test/api-digester/Inputs/Foo-new-version/module.modulemap index 6522d75170250..57c5884f04377 100644 --- a/test/api-digester/Inputs/Foo-new-version/module.modulemap +++ b/test/api-digester/Inputs/Foo-new-version/module.modulemap @@ -1,3 +1,4 @@ module Foo { header "foo.h" + export * } diff --git a/test/api-digester/Inputs/Foo/foo.h b/test/api-digester/Inputs/Foo/foo.h index 7a52230b2d45a..93bcc41eb96be 100644 --- a/test/api-digester/Inputs/Foo/foo.h +++ b/test/api-digester/Inputs/Foo/foo.h @@ -1,4 +1,4 @@ -#import +@import ObjectiveC; @protocol ObjcProt -(void) someFunctionFromProt; diff --git a/test/api-digester/Inputs/Foo/module.modulemap b/test/api-digester/Inputs/Foo/module.modulemap index 6522d75170250..57c5884f04377 100644 --- a/test/api-digester/Inputs/Foo/module.modulemap +++ b/test/api-digester/Inputs/Foo/module.modulemap @@ -1,3 +1,4 @@ module Foo { header "foo.h" + export * } diff --git a/test/api-digester/Inputs/cake_baseline/cake.swift b/test/api-digester/Inputs/cake_baseline/cake.swift index fd57d1823f41e..8ac2884bca90a 100644 --- a/test/api-digester/Inputs/cake_baseline/cake.swift +++ b/test/api-digester/Inputs/cake_baseline/cake.swift @@ -105,7 +105,26 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval: C3 {} -public class ClassToStruct {} +public class ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + internal init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + public init() {} + public convenience init(x: Int) { self.init() } +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public protocol ProtocolToEnum {} public class SuperClassChange: C7 {} diff --git a/test/api-digester/Inputs/cake_current/cake.swift b/test/api-digester/Inputs/cake_current/cake.swift index d9dee4768a10f..1f9a0a772298e 100644 --- a/test/api-digester/Inputs/cake_current/cake.swift +++ b/test/api-digester/Inputs/cake_current/cake.swift @@ -114,7 +114,30 @@ public protocol DerivedProtocolRequiementChanges: RequiementChanges {} public class SuperClassRemoval {} -public struct ClassToStruct {} +public struct ClassToStruct { + public init() {} +} + +open class ClassWithMissingDesignatedInits { + // Remove the @_hasMissingDesignatedInitializers attribute + public init() {} + public convenience init(x: Int) { self.init() } +} + +open class ClassWithoutMissingDesignatedInits { + // Add the @_hasMissingDesignatedInitializers attribute by adding an inaccessible + // init + public init() {} + public convenience init(x: Int) { self.init() } + internal init(y: Int) {} +} + +public class SubclassWithMissingDesignatedInits: ClassWithMissingDesignatedInits { +} + +public class SubclassWithoutMissingDesignatedInits: ClassWithoutMissingDesignatedInits { +} + public enum ProtocolToEnum {} public class SuperClassChange: C8 {} diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index fa5be10f2f79a..fb4d8c7a8926f 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -60,6 +60,8 @@ cake: Class C0 is a new API without @available attribute cake: Class C5 is now without @objc cake: Class C8 is a new API without @available attribute cake: Constructor C1.init(_:) is a new API without @available attribute +cake: Constructor ClassWithMissingDesignatedInits.init() is a new API without @available attribute +cake: Constructor SubclassWithMissingDesignatedInits.init() is a new API without @available attribute cake: Enum IceKind is now without @frozen cake: EnumElement FrozenKind.AddedCase is a new API without @available attribute cake: Func C1.foo1() is now not static @@ -76,8 +78,6 @@ cake: Protocol P4 is a new API without @available attribute cake: Struct C6 is now with @frozen cake: Var C1.CIIns1 changes from weak to strong cake: Var C1.CIIns2 changes from strong to weak -cake: Var GlobalLetChangedToVar changes from let to var -cake: Var GlobalVarChangedToLet changes from var to let cake: Var RequiementChanges.addedVar is a new API without @available attribute cake: Var fixedLayoutStruct.$__lazy_storage_$_lazy_d is a new API without @available attribute cake: Var fixedLayoutStruct.c is a new API without @available attribute @@ -104,10 +104,10 @@ cake: Struct fixedLayoutStruct has added a conformance to an existing protocol P cake: Struct fixedLayoutStruct has removed conformance to P1 /* Protocol Requirement Change */ -cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry +cake: Accessor HasMutatingMethodClone.bar.Get() now requires new witness table entry cake: AssociatedType AssociatedTypePro.T1 has removed default type Swift.Int cake: AssociatedType RequiementChanges.addedTypeWithoutDefault has been added as a protocol requirement -cake: Func HasMutatingMethodClone.foo() now requires new witness table entry +cake: Func HasMutatingMethodClone.foo() now requires new witness table entry cake: Func RequiementChanges.addedFunc() has been added as a protocol requirement cake: Var RequiementChanges.addedVar has been added as a protocol requirement @@ -115,4 +115,6 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/Outputs/Cake.txt b/test/api-digester/Outputs/Cake.txt index 97f48480f01cf..0dab4e4589584 100644 --- a/test/api-digester/Outputs/Cake.txt +++ b/test/api-digester/Outputs/Cake.txt @@ -64,7 +64,9 @@ cake: Accessor ClassWithOpenMember.property.Get() is no longer open for subclass cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 +cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class +cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class cake: Func ClassWithOpenMember.bar() is no longer open for subclassing cake: Func ClassWithOpenMember.foo() is no longer open for subclassing cake: Var ClassWithOpenMember.property is no longer open for subclassing diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index 3c0e9a0abffeb..a6f9baf090553 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -199,7 +199,8 @@ "usr": "s:4cake2C0C", "moduleName": "cake", "genericSig": "<τ_0_0, τ_0_1, τ_0_2>", - "sugared_genericSig": "" + "sugared_genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -428,6 +429,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1330,7 +1333,8 @@ "declAttributes": [ "FixedLayout", "UsableFromInline" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1387,6 +1391,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1418,7 +1423,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1430,7 +1436,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1482,7 +1489,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1668,18 +1676,6 @@ "printedName": "P1", "usr": "s:4cake2P1P" }, - { - "kind": "Conformance", - "name": "MirrorPath", - "printedName": "MirrorPath", - "usr": "s:s10MirrorPathP" - }, - { - "kind": "Conformance", - "name": "CVarArg", - "printedName": "CVarArg", - "usr": "s:s7CVarArgP" - }, { "kind": "Conformance", "name": "Encodable", @@ -1692,6 +1688,30 @@ "printedName": "Decodable", "usr": "s:Se" }, + { + "kind": "Conformance", + "name": "CustomReflectable", + "printedName": "CustomReflectable", + "usr": "s:s17CustomReflectableP" + }, + { + "kind": "Conformance", + "name": "_CustomPlaygroundQuickLookable", + "printedName": "_CustomPlaygroundQuickLookable", + "usr": "s:s30_CustomPlaygroundQuickLookableP" + }, + { + "kind": "Conformance", + "name": "MirrorPath", + "printedName": "MirrorPath", + "usr": "s:s10MirrorPathP" + }, + { + "kind": "Conformance", + "name": "CVarArg", + "printedName": "CVarArg", + "usr": "s:s7CVarArgP" + }, { "kind": "Conformance", "name": "Hashable", @@ -1710,18 +1730,6 @@ "printedName": "_HasCustomAnyHashableRepresentation", "usr": "s:s35_HasCustomAnyHashableRepresentationP" }, - { - "kind": "Conformance", - "name": "CustomReflectable", - "printedName": "CustomReflectable", - "usr": "s:s17CustomReflectableP" - }, - { - "kind": "Conformance", - "name": "_CustomPlaygroundQuickLookable", - "printedName": "_CustomPlaygroundQuickLookable", - "usr": "s:s30_CustomPlaygroundQuickLookableP" - }, { "kind": "Conformance", "name": "SIMDScalar", diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index 89bf046ba63b3..13fd939c1e866 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -201,7 +201,8 @@ "declKind": "Class", "usr": "s:4cake2C0C", "moduleName": "cake", - "genericSig": "" + "genericSig": "", + "hasMissingDesignatedInitializers": true }, { "kind": "TypeAlias", @@ -425,6 +426,8 @@ "usr": "s:4cake2C1C", "moduleName": "cake", "superclassUsr": "s:4cake2C0C", + "hasMissingDesignatedInitializers": true, + "inheritsConvenienceInitializers": true, "superclassNames": [ "cake.C0" ] @@ -1235,6 +1238,7 @@ "declKind": "Class", "usr": "s:4cake15FutureContainerC", "moduleName": "cake", + "hasMissingDesignatedInitializers": true, "conformances": [ { "kind": "Conformance", @@ -1266,7 +1270,8 @@ "Available", "Available", "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1278,7 +1283,8 @@ "intro_swift": "5", "declAttributes": [ "Available" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1330,7 +1336,8 @@ "objc_name": "NewObjCClass", "declAttributes": [ "ObjC" - ] + ], + "hasMissingDesignatedInitializers": true }, { "kind": "TypeDecl", @@ -1524,18 +1531,6 @@ "printedName": "P1", "usr": "s:4cake2P1P" }, - { - "kind": "Conformance", - "name": "MirrorPath", - "printedName": "MirrorPath", - "usr": "s:s10MirrorPathP" - }, - { - "kind": "Conformance", - "name": "CVarArg", - "printedName": "CVarArg", - "usr": "s:s7CVarArgP" - }, { "kind": "Conformance", "name": "Encodable", @@ -1548,6 +1543,24 @@ "printedName": "Decodable", "usr": "s:Se" }, + { + "kind": "Conformance", + "name": "CustomReflectable", + "printedName": "CustomReflectable", + "usr": "s:s17CustomReflectableP" + }, + { + "kind": "Conformance", + "name": "MirrorPath", + "printedName": "MirrorPath", + "usr": "s:s10MirrorPathP" + }, + { + "kind": "Conformance", + "name": "CVarArg", + "printedName": "CVarArg", + "usr": "s:s7CVarArgP" + }, { "kind": "Conformance", "name": "Hashable", @@ -1560,12 +1573,6 @@ "printedName": "Equatable", "usr": "s:SQ" }, - { - "kind": "Conformance", - "name": "CustomReflectable", - "printedName": "CustomReflectable", - "usr": "s:s17CustomReflectableP" - }, { "kind": "Conformance", "name": "SIMDScalar", diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 80da5e122c3ba..d6d9a22418752 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -119,6 +119,7 @@ "Dynamic" ], "superclassUsr": "c:objc(cs)NSObject", + "inheritsConvenienceInitializers": true, "superclassNames": [ "ObjectiveC.NSObject" ], @@ -134,6 +135,24 @@ "name": "NSObjectProtocol", "printedName": "NSObjectProtocol", "usr": "c:objc(pl)NSObject" + }, + { + "kind": "Conformance", + "name": "Equatable", + "printedName": "Equatable", + "usr": "s:SQ" + }, + { + "kind": "Conformance", + "name": "Hashable", + "printedName": "Hashable", + "usr": "s:SH" + }, + { + "kind": "Conformance", + "name": "CVarArg", + "printedName": "CVarArg", + "usr": "s:s7CVarArgP" } ] }, diff --git a/test/api-digester/compare-dump-abi.swift b/test/api-digester/compare-dump-abi.swift index 887c8fd3747b9..4f8f184d7aa92 100644 --- a/test/api-digester/compare-dump-abi.swift +++ b/test/api-digester/compare-dump-abi.swift @@ -2,11 +2,20 @@ // RUN: %empty-directory(%t.mod2) // RUN: %empty-directory(%t.sdk) // RUN: %empty-directory(%t.module-cache) -// RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -// RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft -abi > %t.dump1.json -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesLeft -abi > %t.dump2.json -// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -abi -o %t.result +// RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -emit-module-source-info -emit-module-source-info-path %t.mod1/cake.swiftsourceinfo +// RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -emit-module-source-info -emit-module-source-info-path %t.mod2/cake.swiftsourceinfo +// RUN: %api-digester -dump-sdk -module cake -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft -abi +// RUN: %api-digester -diagnose-sdk -print-module -baseline-path %t.dump1.json -module cake -I %t.mod2 -I %S/Inputs/APINotesLeft -module-cache-path %t.module-cache %clang-importer-sdk-nosource -abi -o %t.result // RUN: %clang -E -P -x c %S/Outputs/Cake-abi.txt -o - | sed '/^\s*$/d' > %t.abi.expected // RUN: %clang -E -P -x c %t.result -o - | sed '/^\s*$/d' > %t.abi.result.tmp -// RUN: diff -u %t.abi.expected %t.abi.result.tmp \ No newline at end of file +// RUN: diff -u %t.abi.expected %t.abi.result.tmp + +// A compiler-style diag to ensure we have source locations associated with breakages. +// RUN: not %api-digester -diagnose-sdk -print-module -baseline-path %t.dump1.json -module cake -I %t.mod2 -I %S/Inputs/APINotesLeft -module-cache-path %t.module-cache %clang-importer-sdk-nosource -abi -o %t.result -compiler-style-diags 2> %t.abi.compiler.diags +// RUN: %FileCheck %s < %t.abi.compiler.diags + +// CHECK: cake_current/cake.swift:31:14: error: cake: Class C4 has changed its super class from APINotesTest.OldType to APINotesTest.NewType +// CHECK: cake_current/cake.swift:33:14: error: cake: Class C5 is now without @objc +// CHECK: cake_current/cake.swift:35:23: error: cake: Func C5.dy_foo() is now with dynamic +// CHECK: cake_current/cake.swift:39:15: error: cake: Struct C6 is now with @frozen +// CHECK: cake_current/cake.swift:41:13: error: cake: Enum IceKind is now without @frozen \ No newline at end of file diff --git a/test/api-digester/compare-dump.swift b/test/api-digester/compare-dump.swift index 54574cbb1a7e4..c37cecb5d424f 100644 --- a/test/api-digester/compare-dump.swift +++ b/test/api-digester/compare-dump.swift @@ -4,8 +4,8 @@ // RUN: %empty-directory(%t.module-cache) // RUN: %swift -emit-module -o %t.mod1/cake.swiftmodule %S/Inputs/cake_baseline/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesLeft %clang-importer-sdk-nosource -module-name cake // RUN: %swift -emit-module -o %t.mod2/cake.swiftmodule %S/Inputs/cake_current/cake.swift -parse-as-library -enable-library-evolution -I %S/Inputs/APINotesRight %clang-importer-sdk-nosource -module-name cake -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft > %t.dump1.json -// RUN: %api-digester -dump-sdk -module cake -o - -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesLeft > %t.dump2.json +// RUN: %api-digester -dump-sdk -module cake -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod1 -I %S/Inputs/APINotesLeft +// RUN: %api-digester -dump-sdk -module cake -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %t.mod2 -I %S/Inputs/APINotesRight // RUN: %api-digester -diagnose-sdk -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result // RUN: %clang -E -P -x c %S/Outputs/Cake.txt -o - | sed '/^\s*$/d' > %t.expected diff --git a/test/attr/Inputs/SymbolMove/HighLevel.swift b/test/attr/Inputs/SymbolMove/HighLevel.swift new file mode 100644 index 0000000000000..3af397d1c1675 --- /dev/null +++ b/test/attr/Inputs/SymbolMove/HighLevel.swift @@ -0,0 +1,12 @@ +@_exported import LowLevel + +public func printMessage() { + printMessageMoved() +} +public class CandyBox: Box { + public typealias Item = Candy + public var ItemKind: String { return getItem().kind } + let itemInBox: Item + public init(_ itemInBox: Item) { self.itemInBox = itemInBox } + public func getItem() -> Item { return itemInBox } +} diff --git a/test/attr/Inputs/SymbolMove/HighlevelOriginal.swift b/test/attr/Inputs/SymbolMove/HighlevelOriginal.swift new file mode 100644 index 0000000000000..718cc1da180c8 --- /dev/null +++ b/test/attr/Inputs/SymbolMove/HighlevelOriginal.swift @@ -0,0 +1,49 @@ +public func printMessageMoved() { + print("Hello from HighLevel") +} +public func printMessage() { + printMessageMoved() +} + +public struct Entity { + public let value = "HighLevel" + public init() {} + public func location() -> String { return "Entity from " + value } +} + +// =================== Move protocol ================================= // +public protocol Box { + associatedtype Item + var ItemKind: String { get } + func getItem() -> Item + func shape() -> String +} + +extension Box { + public func shape() -> String { return "square"} +} + +public struct Candy { + public var kind = "candy" + public init() {} +} + +public class CandyBox: Box { + public typealias Item = Candy + public var ItemKind: String { return getItem().kind } + let itemInBox: Item + public init(_ itemInBox: Item) { self.itemInBox = itemInBox } + public func getItem() -> Item { return itemInBox } +} + +// =================== Move enum ============================ // +public enum LanguageKind: Int { + case Cpp = -1 + case Swift = -2 + case ObjC = -3 +} + +open class Vehicle { + public init() {} + public var currentSpeed = -40.0 +} diff --git a/test/attr/Inputs/SymbolMove/LowLevel.swift b/test/attr/Inputs/SymbolMove/LowLevel.swift new file mode 100644 index 0000000000000..fcc17c74e7f5f --- /dev/null +++ b/test/attr/Inputs/SymbolMove/LowLevel.swift @@ -0,0 +1,53 @@ +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +public func printMessageMoved() { + print("Hello from LowLevel") +} + +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +public struct Entity { + public let value = "LowLevel" + public init() {} + public func location() -> String { return "Entity from " + value } +} + +// =================== Move protocol =================================// +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +public protocol Box { + associatedtype Item + var ItemKind: String { get } + func getItem() -> Item + func shape() -> String +} + +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +extension Box { + public func shape() -> String { return "round"} +} + +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +public struct Candy { + public var kind = "candy" + public init() {} +} + +// =================== Move enum ============================ // +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +public enum LanguageKind: Int { + case Cpp = 1 + case Swift = 2 + case ObjC = 3 +} + +// =================== Move class ============================ // +@available(OSX 10.7, *) +@_originallyDefinedIn(module: "HighLevel", OSX 10.9) +open class Vehicle { + public init() {} + public var currentSpeed = 40.0 +} diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 10bb792144eda..a180473605531 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -swift-version 5 // Simple case. -var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} expected-error {{cannot convert value of type 'Int' to specified type '() -> Int'}} +var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} @autoclosure func func1() {} // expected-error {{attribute can only be applied to types, not declarations}} @@ -98,9 +98,9 @@ class TestFunc12 { func test() { func12a(x + foo()) // okay - func12c(x + foo()) - // expected-error@-1{{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{13-13=self.}} - // expected-error@-2{{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{17-17=self.}} + func12c(x + foo()) + // expected-error@-1{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-1{{reference 'self.' explicitly}} {{13-13=self.}} + // expected-error@-2{{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note@-2{{reference 'self.' explicitly}} {{17-17=self.}} } } @@ -183,10 +183,10 @@ func passAutoClosureToEnumCase(_ fn: @autoclosure () -> Int) { func rdar_20591571() { func foo(_ g: @autoclosure () -> Int) { typealias G = ()->Int - let _ = unsafeBitCast(g, to: G.self) // expected-error {{converting non-escaping value to 'T' may allow it to escape}} + let _ = unsafeBitCast(g, to: G.self) // expected-error {{converting non-escaping parameter 'g' to generic parameter 'T' may allow it to escape}} } - func id(_: T) -> T {} + func id(_: T) -> T {} // expected-note {{eneric parameters are always considered '@escaping'}} func same(_: T, _: T) {} // expected-note@-1 2 {{generic parameters are always considered '@escaping'}} @@ -198,7 +198,7 @@ func rdar_20591571() { var _ = efn let _ = efn - _ = id(fn) // expected-error {{converting non-escaping value to 'T' may allow it to escape}} + _ = id(fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}} _ = same(fn, { 3 }) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}} _ = same({ 3 }, fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}} @@ -251,7 +251,7 @@ func overloaded_autoclj(_: @autoclosure () -> Int) {} func autoclosure_param_returning_func_type() { func foo(_ fn: @autoclosure () -> (() -> Int)) {} - func generic_foo(_ fn: @autoclosure () -> T) {} + func generic_foo(_ fn: @autoclosure () -> T) {} // expected-note {{generic parameters are always considered '@escaping'}} func bar_1(_ fn: @autoclosure @escaping () -> Int) { foo(fn) } // Ok func bar_2(_ fn: @autoclosure () -> Int) { foo(fn) } // expected-note {{parameter 'fn' is implicitly non-escaping}} @@ -259,7 +259,7 @@ func autoclosure_param_returning_func_type() { func baz_1(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn) } // Ok (T is inferred as () -> Int) func baz_2(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn()) } // Ok (T is inferred as Int) func baz_3(_ fn: @autoclosure () -> Int) { generic_foo(fn) } // Fails because fn is not marked as @escaping - // expected-error@-1 {{converting non-escaping value to 'T' may allow it to escape}} + // expected-error@-1 {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}} // Let's make sure using `fn` as value works fine in presence of overloading func biz_1(_ fn: @autoclosure @escaping () -> Int) { overloaded_autoclj(fn) } // Ok @@ -281,3 +281,17 @@ func test_autoclosure_with_generic_argument_mismatch() { foo(S()) // expected-error {{cannot convert value of type 'S' to expected argument type 'S'}} } + +// SR-11934 +func sr_11934(_ x: @autoclosure String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} + +// SR-11938 +let sr_11938_1: Array<@autoclosure String> = [] // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_2() -> @autoclosure String { "" } // expected-error {{'@autoclosure' may only be used on parameters}} +func sr_11938_3(_ x: [@autoclosure String]) {} // expected-error {{'@autoclosure' may only be used on parameters}} + +protocol SR_11938_P {} +struct SR_11938_S : @autoclosure SR_11938_P {} // expected-error {{'@autoclosure' may only be used on parameters}} + +// SR-9178 +func bar(_ x: @autoclosure T) {} // expected-error 1{{@autoclosure attribute only applies to function types}} diff --git a/test/attr/attr_availability_maccatalyst.swift b/test/attr/attr_availability_maccatalyst.swift new file mode 100644 index 0000000000000..3c3bf676f1c8b --- /dev/null +++ b/test/attr/attr_availability_maccatalyst.swift @@ -0,0 +1,125 @@ +// RUN: %swift -typecheck -verify -parse-stdlib -target x86_64-apple-ios51.0-macabi %s + +// REQUIRES: OS=maccatalyst + +@available(macCatalyst, introduced: 1.0, deprecated: 2.0, obsoleted: 9.0, + message: "you don't want to do that anyway") +func obsoletedOnMacCatalyst() { } +// expected-note @-1{{'obsoletedOnMacCatalyst()' was obsoleted in Mac Catalyst 9.0}} + +obsoletedOnMacCatalyst() // expected-error{{'obsoletedOnMacCatalyst()' is unavailable in Mac Catalyst: you don't want to do that anyway}} + +@available(iOS, introduced: 1.0, deprecated: 2.0, obsoleted: 9.0, + message: "you don't want to do that anyway") +func obsoletedOnIOS() { } +// expected-note @-1{{'obsoletedOnIOS()' was obsoleted in iOS 9.0}} + +obsoletedOnIOS() // expected-error{{'obsoletedOnIOS()' is unavailable in iOS: you don't want to do that anyway}} + + +@available(iOS, introduced: 1.0) +@available(macCatalyst, introduced: 1.0, obsoleted: 12.0) +func obsoletedOnMacCatalystButNotIOS() { } +// expected-note @-1{{'obsoletedOnMacCatalystButNotIOS()' was obsoleted in Mac Catalyst 12.0}} + +obsoletedOnMacCatalystButNotIOS() // expected-error {{'obsoletedOnMacCatalystButNotIOS()' is unavailable}} + + +@available(iOS, introduced: 12.0, obsoleted: 12.0) +@available(macCatalyst, introduced: 12.0) +func obsoletedOnIOSButNotMacCatalyst() { } +obsoletedOnIOSButNotMacCatalyst() // no-error + + + +@available(iOS, introduced: 1.0) +@available(macCatalyst, introduced: 1.0, deprecated: 12.0) +func deprecatedOnMacCatalystButNotIOS() { } + +deprecatedOnMacCatalystButNotIOS() // expected-warning {{deprecatedOnMacCatalystButNotIOS()' was deprecated in Mac Catalyst 12.0}} + +@available(iOS, introduced: 12.0, deprecated: 13.0) +@available(macCatalyst, introduced: 12.0) +func deprecatedOnIOSButNotMacCatalyst() { } +deprecatedOnIOSButNotMacCatalyst() // no-warning + + +@available(iOS 55.0, macCatalyst 56.0, *) +func introducedLaterOnMacCatalyst() { +} + +// expected-note@+1 *{{add @available attribute to enclosing global function}} +func testPoundAvailable() { + + if #available(macCatalyst 55.0, *) { + introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } + + // macCatalyst should win over iOS when present + + if #available(iOS 56.0, macCatalyst 55.0, *) { + introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } + + if #available(iOS 55.0, macCatalyst 56.0, *) { + introducedLaterOnMacCatalyst() // no-warning + } + + // iOS availability should be inherited when macCatalyst is not present + + if #available(iOS 55.0, *) { + introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } + + if #available(iOS 56.0, *) { + introducedLaterOnMacCatalyst() // no-warning + } + + // macOS availability doesn't count on macCatalyst for Swift. + if #available(macOS 9999.0, *) { + introducedLaterOnMacCatalyst() // expected-error {{'introducedLaterOnMacCatalyst()' is only available in Mac Catalyst 56.0 or newer}} + // expected-note@-1 {{add 'if #available' version check}} + } +} + +@available(iOS 55.0, *) +func testUnnecessaryPoundAvailable() { // expected-note*{{enclosing scope here}} + + // Even though we're compiling for macCatalyst, the #available is expressed in terms of + // 'iOS', so we should use that to report to the user in the diagnostic. + if #available(iOS 54.0, *) { + // expected-warning@-1 {{unnecessary check for 'iOS'; enclosing scope ensures guard will always be true}} + } + + if #available(macCatalyst 54.0, *) { + // expected-warning@-1 {{unnecessary check for 'macCatalyst'; enclosing scope ensures guard will always be true}} + } + + if #available(macCatalyst 54.0, iOS 53.0, *) { + // expected-warning@-1 {{unnecessary check for 'macCatalyst'; enclosing scope ensures guard will always be true}} + } + + if #available(iOS 53.0, macCatalyst 54.0, *) { + // expected-warning@-1 {{unnecessary check for 'macCatalyst'; enclosing scope ensures guard will always be true}} + } +} + +// Test that we don't accidentally try to validate @available(iOS, ...) attrs +// on accessors against the property's @available(macCatalyst, ...) attr. +// (rdar://problem/50067784) +class X { + @available(iOS 3.2, macCatalyst 13, *) + var x: X { + get { return self } + set { } + } +} + +protocol P: Builtin.AnyObject { + var x: X { get set } +} + +extension X: P {} diff --git a/test/attr/attr_convention.swift b/test/attr/attr_convention.swift index 7ee849bdb3079..83048435363fc 100644 --- a/test/attr/attr_convention.swift +++ b/test/attr/attr_convention.swift @@ -2,8 +2,12 @@ let f1: (Int) -> Int = { $0 } let f2: @convention(swift) (Int) -> Int = { $0 } +let f2a: @convention(swift, cType: "int *(int)") (Int32) -> Int32 = { $0 } // expected-error{{convention 'swift' does not support the 'cType' argument label, did you mean @convention(c, cType: "int *(int)") or @convention(block, cType: "int *(int)") instead?}} let f3: @convention(block) (Int) -> Int = { $0 } let f4: @convention(c) (Int) -> Int = { $0 } +let f4a: @convention(c, cType: "int (int)") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'int (int)'; it should be a C function pointer type or a block pointer type}} +let f4b: @convention(c, cType: "void *") (Int32) -> Int32 = { $0 } // expected-error{{unable to parse 'void *'; it should be a C function pointer type or a block pointer type}} +let f4c: @convention(c, cType: "int (*)(int)") (Int32) -> Int32 = { $0 } let f5: @convention(INTERCAL) (Int) -> Int = { $0 } // expected-error{{convention 'INTERCAL' not supported}} diff --git a/test/attr/attr_dynamic_callable.swift b/test/attr/attr_dynamic_callable.swift index d1c8539b1df60..9116ecbad9df4 100644 --- a/test/attr/attr_dynamic_callable.swift +++ b/test/attr/attr_dynamic_callable.swift @@ -49,6 +49,26 @@ func testCallable( d(x1: 1, 2.0, x2: 3) } +func testCallableDiagnostics( + a: Callable, b: DiscardableResult, c: Throwing, d: KeywordArgumentCallable +) { + a("hello", "world") + // expected-error@-1:5 {{cannot convert value of type 'String' to expected argument type 'Int'}} + // expected-error@-2:14 {{cannot convert value of type 'String' to expected argument type 'Int'}} + b("hello", "world") + // expected-error@-1:5 {{cannot convert value of type 'String' to expected argument type 'Double'}} + // expected-error@-2:14 {{cannot convert value of type 'String' to expected argument type 'Double'}} + try? c(1, 2, 3, 4) + // expected-error@-1:10 {{cannot convert value of type 'Int' to expected argument type 'String'}} + // expected-error@-2:13 {{cannot convert value of type 'Int' to expected argument type 'String'}} + // expected-error@-3:16 {{cannot convert value of type 'Int' to expected argument type 'String'}} + // expected-error@-4:19 {{cannot convert value of type 'Int' to expected argument type 'String'}} + + d(x1: "hello", x2: "world") + // expected-error@-1:9 {{cannot convert value of type 'String' to expected argument type 'Float'}} + // expected-error@-2:22 {{cannot convert value of type 'String' to expected argument type 'Float'}} +} + func testIUO( a: Callable!, b: DiscardableResult!, c: Throwing!, d: KeywordArgumentCallable! ) { diff --git a/test/attr/attr_dynamic_member_lookup.swift b/test/attr/attr_dynamic_member_lookup.swift index d4930e8f864c6..357ea7f0af9e0 100644 --- a/test/attr/attr_dynamic_member_lookup.swift +++ b/test/attr/attr_dynamic_member_lookup.swift @@ -600,10 +600,10 @@ func keypath_with_subscripts(_ arr: SubscriptLens<[Int]>, func keypath_with_incorrect_return_type(_ arr: Lens>) { for idx in 0..' conform to 'Strideable'}} - // expected-error@-2 {{operator function '..<' requires that 'Lens.Stride' conform to 'SignedInteger'}} + // expected-error@-1 {{protocol 'Sequence' requires that 'Lens' conform to 'Strideable'}} + // expected-error@-2 {{protocol 'Sequence' requires that 'Lens.Stride' conform to 'SignedInteger'}} // expected-error@-3 {{cannot convert value of type 'Int' to expected argument type 'Lens'}} - // expected-error@-4 {{argument type 'Lens' does not conform to expected type 'Comparable'}} + // expected-error@-4 {{referencing operator function '..<' on 'Comparable' requires that 'Lens' conform to 'Comparable'}} let _ = arr[idx] } } @@ -746,3 +746,22 @@ struct SR_10557_S1 { fatalError() } } + +@dynamicMemberLookup +struct SR11877 { + subscript(dynamicMember member: Substring) -> Int { 0 } +} + +_ = \SR11877.okay + +func test_infinite_self_recursion() { + @dynamicMemberLookup + struct Recurse { + subscript(dynamicMember member: KeyPath, U>) -> Int { + return 1 + } + } + + _ = Recurse().foo + // expected-error@-1 {{value of type 'Recurse' has no dynamic member 'foo' using key path from root type 'Recurse'}} +} diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index d4caa72601b2a..16ce674ec24ed 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -226,3 +226,6 @@ extension SR_9760 { func fiz(_: T, _: @escaping F) {} // Ok func baz(_: @escaping G) {} // Ok } + +// SR-9178 +func foo(_ x: @escaping T) {} // expected-error 1{{@escaping attribute only applies to function types}} diff --git a/test/attr/attr_hasMissingDesignatedInits.swift b/test/attr/attr_hasMissingDesignatedInits.swift new file mode 100644 index 0000000000000..12b8171ccd3df --- /dev/null +++ b/test/attr/attr_hasMissingDesignatedInits.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-frontend -typecheck %s -verify + +// This test just makes sure we don't error if we see either of these attributes. + +@_hasMissingDesignatedInitializers // no-error +class MyClass {} + +@_inheritsConvenienceInitializers // no-error +class MyOtherClass {} diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index d5244fc324dcd..1414a1463bb08 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -6,12 +6,12 @@ func conflictingAttrs(_ fn: @noescape @escaping () -> Int) {} // expected-error func doesEscape(_ fn : @escaping () -> Int) {} -func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error 2{{unknown attribute 'noescape'}} +func takesGenericClosure(_ a : Int, _ fn : @noescape () -> T) {} // expected-error {{unknown attribute 'noescape'}} var globalAny: Any = 0 -func assignToGlobal(_ t: T) { +func assignToGlobal(_ t: T) { // expected-note {{generic parameters are always considered '@escaping'}} globalAny = t } @@ -23,8 +23,8 @@ func takesVariadic(_ fns: () -> Int...) { doesEscape(fns[0]) // Okay - variadic-of-function parameters are escaping } -func takesNoEscapeClosure(_ fn : () -> Int) { - // expected-note@-1 5{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} +func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note 1 {{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} + // expected-note@-1 6{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} takesNoEscapeClosure { 4 } // ok _ = fn() // ok @@ -36,14 +36,14 @@ func takesNoEscapeClosure(_ fn : () -> Int) { takesGenericClosure(4, fn) // ok takesGenericClosure(4) { fn() } // ok. - _ = [fn] // expected-error {{converting non-escaping value to 'Element' may allow it to escape}} + _ = [fn] // expected-error {{using non-escaping parameter 'fn' in a context expecting an @escaping closure}} _ = [doesEscape(fn)] // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} - _ = [1 : fn] // expected-error {{converting non-escaping value to 'Value' may allow it to escape}} + _ = [1 : fn] // expected-error {{using non-escaping parameter 'fn' in a context expecting an @escaping closure}} _ = [1 : doesEscape(fn)] // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} _ = "\(doesEscape(fn))" // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} _ = "\(takesArray([fn]))" // expected-error {{using non-escaping parameter 'fn' in a context expecting an @escaping closure}} - assignToGlobal(fn) // expected-error {{converting non-escaping value to 'T' may allow it to escape}} + assignToGlobal(fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T' may allow it to escape}} assignToGlobal((fn, fn)) // expected-error {{converting non-escaping value to 'T' may allow it to escape}} } @@ -52,7 +52,7 @@ class SomeClass { func test() { // This should require "self." - doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} // Since 'takesNoEscapeClosure' doesn't escape its closure, it doesn't // require "self." qualification of member references. @@ -64,88 +64,89 @@ class SomeClass { foo() func plain() { foo() } - let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let plain2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} _ = plain2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{31-31=self.}} - _ = mulit2 + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{30-30= [self] in}} expected-note{{reference 'self.' explicitly}} {{31-31=self.}} + _ = multi2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{18-18=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{reference 'self.' explicitly}} {{18-18=self.}} takesNoEscapeClosure { foo(); return 0 } // okay func outer() { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{28-28=self.}} - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let _: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{27-27= [self] in}} expected-note{{reference 'self.' explicitly}} {{28-28=self.}} + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } } - let _: () -> Void = { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Void = { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{26-26= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let _ = inner2 + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + let _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} } - doesEscape { - func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + doesEscape { //expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{17-17= [self] in}} + func inner() { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 - func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{29-29=self.}} - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} - takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + func multi() -> Int { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} + takesNoEscapeClosure { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{30-30=self.}} return 0 } takesNoEscapeClosure { func inner() { foo() } - let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} + let inner2 = { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{21-21= [self] in}} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} _ = inner2 func multi() -> Int { foo(); return 0 } - let mulit2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{33-33=self.}} - _ = mulit2 - doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + let multi2: () -> Int = { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{32-32= [self] in}} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + _ = multi2 + doesEscape { foo() } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo() } // okay - doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit 'self.' to make capture semantics explicit}} {{20-20=self.}} + doesEscape { foo(); return 0 } // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{19-19= [self] in}} expected-note{{reference 'self.' explicitly}} {{20-20=self.}} takesNoEscapeClosure { foo(); return 0 } // okay return 0 } + // Implicit 'self' should be accepted when 'self' has value semantics. struct Outer { @discardableResult func bar() -> Int { bar() func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{30-30=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{22-22=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } @@ -158,17 +159,17 @@ class SomeClass { bar() // no-warning func plain() { bar() } - let plain2 = { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{26-26=self.}} + let plain2 = { bar() } _ = plain2 func multi() -> Int { bar(); return 0 } - let _: () -> Int = { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{32-32=self.}} + let _: () -> Int = { bar(); return 0 } - doesEscape { bar() } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar() } // okay + doesEscape { bar() } + takesNoEscapeClosure { bar() } - doesEscape { bar(); return 0 } // expected-error {{call to method 'bar' in closure requires explicit 'self.' to make capture semantics explicit}} {{24-24=self.}} - takesNoEscapeClosure { bar(); return 0 } // okay + doesEscape { bar(); return 0 } + takesNoEscapeClosure { bar(); return 0 } return 0 } @@ -194,10 +195,7 @@ func testAutoclosure(_ a : @autoclosure () -> Int) { // expected-note{{parameter // QoI: @autoclosure implies @noescape, so you shouldn't be allowed to specify both -func redundant(_ fn : @noescape - @autoclosure () -> Int) { - // expected-error@-2{{unknown attribute 'noescape'}} -} +func redundant(_ fn : @noescape @autoclosure () -> Int) {} // expected-error {{unknown attribute 'noescape'}} protocol P1 { @@ -214,9 +212,7 @@ func overloadedEach(_ source: P, _ transform: @escaping (P.Element) -> struct S : P2 { typealias Element = Int func each(_ transform: @noescape (Int) -> ()) { // expected-error{{unknown attribute 'noescape'}} - // expected-note@-1 {{parameter 'transform' is implicitly non-escaping}} overloadedEach(self, transform, 1) - // expected-error@-1 {{passing non-escaping parameter 'transform' to function expecting an @escaping closure}} } } @@ -260,22 +256,22 @@ public func XCTAssert(_ expression: @autoclosure () -> Bool, _ message: String = /// SR-770 - Currying and `noescape`/`rethrows` don't work together anymore -func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} +func curriedFlatMap(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 1{{unknown attribute 'noescape'}} return { f in x.flatMap(f) } } -func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error 2{{unknown attribute 'noescape'}} - return { (f : @noescape (A) -> [B]) in // expected-error{{unknown attribute 'noescape'}} +func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { // expected-error {{unknown attribute 'noescape'}} + return { (f : @noescape (A) -> [B]) in x.flatMap(f) } } func bad(_ a : @escaping (Int)-> Int) -> Int { return 42 } func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int { // expected-error{{unknown attribute 'noescape'}} - return { f in // expected-note {{parameter 'f' is implicitly non-escaping}} - bad(f) // expected-error {{passing non-escaping parameter 'f' to function expecting an @escaping closure}} + return { f in + bad(f) } } @@ -295,16 +291,13 @@ typealias CompletionHandler = (_ success: Bool) -> () var escape : CompletionHandlerNE var escapeOther : CompletionHandler func doThing1(_ completion: (_ success: Bool) -> ()) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing2(_ completion: CompletionHandlerNE) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing3(_ completion: CompletionHandler) { - // expected-note@-1 {{parameter 'completion' is implicitly non-escaping}} - escape = completion // expected-error {{assigning non-escaping parameter 'completion' to an @escaping closure}} + escape = completion } func doThing4(_ completion: @escaping CompletionHandler) { escapeOther = completion @@ -312,7 +305,7 @@ func doThing4(_ completion: @escaping CompletionHandler) { // @noescape doesn't work on parameters of function type func apply(_ f: @noescape (T) -> U, g: @noescape (@noescape (T) -> U) -> U) -> U { - // expected-error@-1 6{{unknown attribute 'noescape'}} + // expected-error@-1 2{{unknown attribute 'noescape'}} return g(f) } @@ -322,7 +315,7 @@ enum r19997577Type { case Function(() -> r19997577Type, () -> r19997577Type) case Sum(() -> r19997577Type, () -> r19997577Type) - func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 2{{unknown attribute 'noescape'}} + func reduce(_ initial: Result, _ combine: @noescape (Result, r19997577Type) -> Result) -> Result { // expected-error 1{{unknown attribute 'noescape'}} let binary: @noescape (r19997577Type, r19997577Type) -> Result = { combine(combine(combine(initial, self), $0), $1) } // expected-error{{unknown attribute 'noescape'}} switch self { case .Unit: diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index fde589532e0f0..fb87402898c3b 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -123,7 +123,7 @@ func subject_genericFunc(t: T) { func subject_instanceFunc() {} } -func subject_funcParam(a: @objc Int) { // expected-error {{attribute can only be applied to declarations, not types}} +func subject_funcParam(a: @objc Int) { // expected-error {{attribute can only be applied to declarations, not types}} {{1-1=@objc }} {{27-33=}} } @objc // expected-error {{'@objc' attribute cannot be applied to this declaration}} {{1-7=}} @@ -1534,7 +1534,7 @@ class infer_instanceVar2< } class infer_instanceVar3 : Class_ObjC1 { -// CHECK-LABEL: @objc class infer_instanceVar3 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_instanceVar3 : Class_ObjC1 { var v1: Int = 0 // CHECK-LABEL: @objc @_hasInitialValue var v1: Int @@ -1599,13 +1599,13 @@ protocol infer_throughConformanceProto1 { } class infer_class1 : PlainClass {} -// CHECK-LABEL: {{^}}class infer_class1 : PlainClass { +// CHECK-LABEL: {{^}}@_inheritsConvenienceInitializers class infer_class1 : PlainClass { class infer_class2 : Class_ObjC1 {} -// CHECK-LABEL: @objc class infer_class2 : Class_ObjC1 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class2 : Class_ObjC1 { class infer_class3 : infer_class2 {} -// CHECK-LABEL: @objc class infer_class3 : infer_class2 { +// CHECK-LABEL: @objc @_inheritsConvenienceInitializers class infer_class3 : infer_class2 { class infer_class4 : Protocol_Class1 {} // CHECK-LABEL: {{^}}class infer_class4 : Protocol_Class1 { diff --git a/test/attr/attr_originally_definedin_backward_compatibility.swift b/test/attr/attr_originally_definedin_backward_compatibility.swift new file mode 100644 index 0000000000000..c93dac9bbce2b --- /dev/null +++ b/test/attr/attr_originally_definedin_backward_compatibility.swift @@ -0,0 +1,75 @@ +// REQUIRES: OS=macosx +// +// RUN: %empty-directory(%t) +// +// ----------------------------------------------------------------------------- +// --- Prepare SDK (.swiftmodule). +// RUN: %empty-directory(%t/SDK) +// + +// --- Build original high level framework. +// RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ +// RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: %S/Inputs/SymbolMove/HighLevelOriginal.swift -Xlinker -install_name -Xlinker @rpath/HighLevel.framework/HighLevel -enable-library-evolution + +// --- Build an executable using the original high level framework +// RUN: %target-build-swift -emit-executable %s -g -o %t/HighlevelRunner -F %t/SDK/Frameworks/ -framework HighLevel \ +// RUN: -Xlinker -rpath -Xlinker %t/SDK/Frameworks + +// --- Run the executable +// RUN: %t/HighlevelRunner | %FileCheck %s -check-prefix=BEFORE_MOVE + +// --- Build low level framework. +// RUN: mkdir -p %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK/Frameworks/LowLevel.framework/LowLevel) -module-name LowLevel -emit-module \ +// RUN: -emit-module-path %t/SDK/Frameworks/LowLevel.framework/Modules/LowLevel.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: %S/Inputs/SymbolMove/LowLevel.swift -Xlinker -install_name -Xlinker @rpath/LowLevel.framework/LowLevel -enable-library-evolution + +// --- Build high level framework. +// RUN: mkdir -p %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK/Frameworks/HighLevel.framework/HighLevel) -module-name HighLevel -emit-module \ +// RUN: -emit-module-path %t/SDK/Frameworks/HighLevel.framework/Modules/HighLevel.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: %S/Inputs/SymbolMove/HighLevel.swift -F %t/SDK/Frameworks -Xlinker -reexport_framework -Xlinker LowLevel -enable-library-evolution + +// --- Run the executable +// RUN: %t/HighlevelRunner | %FileCheck %s -check-prefix=AFTER_MOVE + +import HighLevel + +printMessage() +printMessageMoved() + +// BEFORE_MOVE: Hello from HighLevel +// BEFORE_MOVE: Hello from HighLevel +// AFTER_MOVE: Hello from LowLevel +// AFTER_MOVE: Hello from LowLevel + +let e = Entity() +print(e.location()) +// BEFORE_MOVE: Entity from HighLevel +// AFTER_MOVE: Entity from LowLevel + +print(CandyBox(Candy()).ItemKind) +// BEFORE_MOVE: candy +// AFTER_MOVE: candy + +print(CandyBox(Candy()).shape()) +// BEFORE_MOVE: square +// AFTER_MOVE: round + +print(LanguageKind.Cpp.rawValue) +// BEFORE_MOVE: -1 +// AFTER_MOVE: 1 + +print("\(Vehicle().currentSpeed)") +// BEFORE_MOVE: -40 +// AFTER_MOVE: 40 + +class Bicycle: Vehicle {} +let bicycle = Bicycle() +bicycle.currentSpeed = 15.0 +print("\(bicycle.currentSpeed)") + +// BEFORE_MOVE: 15.0 +// AFTER_MOVE: 15.0 diff --git a/test/attr/attr_override.swift b/test/attr/attr_override.swift index bfe6f6df68e90..4c74cfed12d8d 100644 --- a/test/attr/attr_override.swift +++ b/test/attr/attr_override.swift @@ -626,3 +626,25 @@ class SR_10198_Derived_1: SR_10198_Base_1 { init(_ arg1: Int) { super.init(SR_10198_Base_S()) } // okay, doesn't crash } +// SR-11740 + +public class SR_11740_Base {} + +public class SR_11740_Derived + : SR_11740_Base, A>, + SR_11740_Q {} + +public protocol SR_11740_P {} + +public protocol SR_11740_Q: SR_11740_P { + associatedtype A +} + +public extension SR_11740_Base where F: SR_11740_Q { + static func foo(_: F.A) {} +} + +extension SR_11740_Derived where F: SR_11740_P { + public static func foo(_: A) {} +} + diff --git a/test/attr/attr_semantics.swift b/test/attr/attr_semantics.swift index 7b1a26ae2a0c4..3c3ed8c91eb71 100644 --- a/test/attr/attr_semantics.swift +++ b/test/attr/attr_semantics.swift @@ -5,7 +5,7 @@ func duplicatesemantics() {} func func_with_nested_semantics_1() { - @_semantics("exit") // expected-error {{attribute '_semantics' can only be used in a non-local scope}} + @_semantics("exit") func exit(_ code : UInt32) -> Void exit(0) } @@ -15,7 +15,51 @@ func func_with_nested_semantics_1() { func somethingThatShouldParseFine() {} func func_with_nested_semantics_2() { - @_semantics("exit") // expected-error {{attribute '_semantics' can only be used in a non-local scope}} + @_semantics("exit") func exit(_ code : UInt32) -> Void exit(0) } + +@_semantics("struct") +struct StructWithSemantics {} + +@_semantics("class") +class ClassWithSemantics {} + +@_semantics("enum") +enum EnumWithSemantics {} + +@_semantics("struct1") +@_semantics("struct2") +struct StructWithDuplicateSemantics {} + +@_semantics("globalVar1") +@_semantics("globalVar2") +var globalVarWithSemantics : Int = 5 + +@_semantics("globalLet1") +@_semantics("globalLet2") +let globalLetWithSemantics : Int = 5 + +func varDeclLocalVars() { + @_semantics("localVar1") + @_semantics("localVar2") + var localVarWithSemantics : Int = 5 + localVarWithSemantics = 6 + let _ = localVarWithSemantics + + @_semantics("localLet1") + @_semantics("localLet2") + let localLetWithSemantics : Int = 5 + let _ = localLetWithSemantics +} + +struct IVarTest { + @_semantics("localVar1") + @_semantics("localVar2") + var localVarWithSemantics : Int = 5 + + @_semantics("localLet1") + @_semantics("localLet2") + let localLetWithSemantics : Int = 5 +} diff --git a/test/attr/attributes.swift b/test/attr/attributes.swift index 5dae546a0a28c..7f0eb8fb3198b 100644 --- a/test/attr/attributes.swift +++ b/test/attr/attributes.swift @@ -56,7 +56,7 @@ func zim() {} func zung(_: T) {} @_transparent // expected-error{{'@_transparent' attribute cannot be applied to stored properties}} {{1-15=}} var zippity : Int -func zoom(x: @_transparent () -> ()) { } // expected-error{{attribute can only be applied to declarations, not types}} +func zoom(x: @_transparent () -> ()) { } // expected-error{{attribute can only be applied to declarations, not types}} {{1-1=@_transparent }} {{14-28=}} protocol ProtoWithTransparent { @_transparent// expected-error{{'@_transparent' attribute is not supported on declarations within protocols}} {{3-16=}} func transInProto() @@ -283,3 +283,30 @@ func unownedOptionals(x: C) { _ = y _ = y2 } + +// @_nonEphemeral attribute +struct S1 { + func foo(@_nonEphemeral _ x: String) {} // expected-error {{@_nonEphemeral attribute only applies to pointer types}} + func bar(@_nonEphemeral _ x: T) {} // expected-error {{@_nonEphemeral attribute only applies to pointer types}} + + func baz(@_nonEphemeral _ x: U) {} // expected-error {{@_nonEphemeral attribute only applies to pointer types}} + + func qux(@_nonEphemeral _ x: UnsafeMutableRawPointer) {} + func quux(@_nonEphemeral _ x: UnsafeMutablePointer?) {} +} + +@_nonEphemeral struct S2 {} // expected-error {{@_nonEphemeral may only be used on 'parameter' declarations}} + +protocol P {} +extension P { + // Allow @_nonEphemeral on the protocol Self type, as the protocol could be adopted by a pointer type. + func foo(@_nonEphemeral _ x: Self) {} + func bar(@_nonEphemeral _ x: Self?) {} +} + +enum E1 { + case str(@_nonEphemeral _: String) // expected-error {{expected parameter name followed by ':'}} + case ptr(@_nonEphemeral _: UnsafeMutableRawPointer) // expected-error {{expected parameter name followed by ':'}} + + func foo() -> @_nonEphemeral UnsafeMutableRawPointer? { return nil } // expected-error {{attribute can only be applied to declarations, not types}} +} diff --git a/test/attr/require_explicit_availability.swift b/test/attr/require_explicit_availability.swift index 6ed057343d214..4a129f429c492 100644 --- a/test/attr/require_explicit_availability.swift +++ b/test/attr/require_explicit_availability.swift @@ -1,14 +1,14 @@ -// RUN: %swift -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -verify -require-explicit-availability -require-explicit-availability-target "macOS 10.10" %s -// RUN: %swift -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -warnings-as-errors %s +// RUN: %swiftc_driver -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -Xfrontend -verify -require-explicit-availability -require-explicit-availability-target "macOS 10.10" %s +// RUN: %swiftc_driver -typecheck -parse-stdlib -target x86_64-apple-macosx10.10 -warnings-as-errors %s -public struct S { // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} +public struct S { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} public func method() { } } -public func foo() { bar() } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public func foo() { bar() } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @usableFromInline -func bar() { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +func bar() { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @available(macOS 10.1, *) public func ok() { } @@ -17,10 +17,10 @@ public func ok() { } public func unavailableOk() { } @available(macOS, deprecated: 10.10) -public func missingIntro() { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public func missingIntro() { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @available(iOS 9.0, *) -public func missingTargetPlatform() { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public func missingTargetPlatform() { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} func privateFunc() { } @@ -35,19 +35,19 @@ struct SOk { precedencegroup MediumPrecedence {} infix operator + : MediumPrecedence -public func +(lhs: S, rhs: S) -> S { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public func +(lhs: S, rhs: S) -> S { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} -public enum E { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public enum E { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} -public class C { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public class C { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} -public protocol P { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +public protocol P { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} -extension S { // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +extension S { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} func ok() { } } -open class OpenClass { } // expected-warning {{public declarations should have an availability attribute with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +open class OpenClass { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} private class PrivateClass { } diff --git a/test/decl/circularity.swift b/test/decl/circularity.swift new file mode 100644 index 0000000000000..fedb35a291b9a --- /dev/null +++ b/test/decl/circularity.swift @@ -0,0 +1,104 @@ +// RUN: %target-typecheck-verify-swift + +// N.B. Validating the pattern binding initializer for `pickMe` used to cause +// recursive validation of the VarDecl. Check that we don't regress now that +// this isn't the case. +public struct Cyclic { + static func pickMe(please: Bool) -> Int { return 42 } + public static let pickMe = Cyclic.pickMe(please: true) +} + +struct Node {} +struct Parameterized { + func please(_ transform: @escaping (_ otherValue: NewValue) -> Value) -> Parameterized { + fatalError() + } +} + +extension Parameterized where Value == [Node], Format == String { + static var pickMe: Parameterized { + fatalError() + } +} + +extension Parameterized where Value == Node, Format == String { + static let pickMe = Parameterized<[Node], String>.pickMe.please { [$0] } +} + +enum Loop: Circle { + struct DeLoop { } +} + +protocol Circle { + typealias DeLoop = Loop.DeLoop +} + +class Base { + static func foo(_ x: Int) {} +} + +class Sub: Base { + var foo = { () -> Int in + let x = 42 + return foo(1) // expected-error {{variable used within its own initial value}} + }() +} + +extension Float { + static let pickMe: Float = 1 +} + +extension SIMD3 { + init(_ scalar: Scalar) { self.init(repeating: scalar) } +} + +extension SIMD3 where SIMD3.Scalar == Float { + static let pickMe = SIMD3(.pickMe) +} + +// Test case with circular overrides +protocol P { + associatedtype A + // expected-note@-1 {{protocol requires nested type 'A'; do you want to add it?}} + // expected-note@-2 {{through reference here}} + func run(a: A) +} + +class C1 { + func run(a: Int) {} +} + +class C2: C1, P { + override func run(a: A) {} + // expected-error@-1 {{circular reference}} + // expected-note@-2 2{{through reference here}} +} + +// Another crash to the above +open class G1 { + open func run(a: A) {} +} + +class C3: G1, P { + // expected-error@-1 {{type 'C3' does not conform to protocol 'P'}} + // expected-error@-2 {{use of undeclared type 'A'}} + override func run(a: A) {} + // expected-error@-1 {{method does not override any method from its superclass}} +} + +// Another case that triggers circular override checking. +protocol P1 { + associatedtype X = Int // expected-note {{through reference here}} + init(x: X) +} + +class C4 { + required init(x: Int) {} +} + +class D4 : C4, P1 { // expected-note 2 {{through reference here}} + required init(x: X) { // expected-error {{circular reference}} + // expected-note@-1 2{{through reference here}} + super.init(x: x) + } +} diff --git a/test/decl/class/circular_inheritance.swift b/test/decl/class/circular_inheritance.swift index b5578bab46735..fb53f95a29f27 100644 --- a/test/decl/class/circular_inheritance.swift +++ b/test/decl/class/circular_inheritance.swift @@ -1,19 +1,19 @@ // RUN: rm -rf %t/stats-dir // RUN: mkdir -p %t/stats-dir // RUN: %target-typecheck-verify-swift -// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles +// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles // RUN: %FileCheck %s < %t.cycles // RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot // Check that we produced superclass type requests. -// RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 17' %t/stats-dir +// RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 18' %t/stats-dir -class Left // expected-error {{circular reference}} +class Left // expected-error {{circular reference}} expected-note {{through reference here}} : Right.Hand { // expected-note {{through reference here}} class Hand {} } -class Right // expected-note {{through reference here}} +class Right // expected-note 2 {{through reference here}} : Left.Hand { // expected-note {{through reference here}} class Hand {} } @@ -35,23 +35,24 @@ class Outer { class Inner : Outer {} } -class Outer2 // expected-error {{circular reference}} +class Outer2 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer2.Inner { // expected-note {{through reference here}} class Inner {} } -class Outer3 // expected-error {{circular reference}} +class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}} : Outer3.Inner { // expected-note {{through reference here}} class Inner {} } // CHECK: ===CYCLE DETECTED=== -// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}} -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@ -// CHECK: `--{{.*}}SuperclassDeclRequest -// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@ -// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}} +// CHECK-LABEL: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@ +// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}} +// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@ +// CHECK: `--{{.*}}SuperclassDeclRequest +// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@ +// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}} // CHECK-DOT: digraph Dependencies // CHECK-DOT: label="InheritedTypeRequest @@ -72,3 +73,13 @@ func crash() { Circle() // expected-error@-1 {{'Circle' cannot be constructed because it has no accessible initializers}} } + +// FIXME: We shouldn't emit the redundant "circular reference" diagnostics here. +class WithDesignatedInit : WithDesignatedInit { + // expected-error@-1 {{'WithDesignatedInit' inherits from itself}} + // expected-error@-2 {{circular reference}} + // expected-note@-3 {{through reference here}} + // expected-note@-4 2 {{through reference here}} + + init(x: Int) {} // expected-error {{circular reference}} +} diff --git a/test/decl/enum/bool_raw_value.swift b/test/decl/enum/bool_raw_value.swift new file mode 100644 index 0000000000000..88905f4f532ad --- /dev/null +++ b/test/decl/enum/bool_raw_value.swift @@ -0,0 +1,26 @@ +// RUN: %target-typecheck-verify-swift +extension Bool: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = value != 0 + } +} + +enum IsDefinitelyRecursive : Bool, Equatable, Hashable { + case recursive=false +} + +// expected-error@+1{{'IsRecursive' declares raw type 'Bool', but does not conform to RawRepresentable and conformance could not be synthesized}} +enum IsRecursive : Bool, Equatable, Hashable { + case recursive=false + case nonrecursive // expected-error{{enum case must declare a raw value when the preceding raw value is not an integer}} +} + +enum IsRecursiveBad1Integral : Bool, Equatable, Hashable { + case recursive = 0 + case nonrecursive +} + +// expected-error @+1{{'IsRecursiveBad2' declares raw type 'Int', but does not conform to RawRepresentable and conformance could not be synthesized}} +enum IsRecursiveBad2 : Int, Equatable, Hashable { + case recursive = false // expected-error{{cannot convert value of type 'Bool' to raw type 'Int'}} +} diff --git a/test/decl/enum/enumtest.swift b/test/decl/enum/enumtest.swift index 7f5be1b06f296..2de9bccc16db0 100644 --- a/test/decl/enum/enumtest.swift +++ b/test/decl/enum/enumtest.swift @@ -36,7 +36,8 @@ func test1a() -> unionSearchFlags { func test1b(_ b : Bool) { _ = 123 - _ = .description == 1 // expected-error {{ambiguous reference to member '=='}} + _ = .description == 1 // expected-error {{instance member 'description' cannot be used on type 'Int'}} + // expected-error@-1 {{member 'description' in 'Int' produces result of type 'String', but context expects 'Int'}} } enum MaybeInt { @@ -99,7 +100,8 @@ func test3a(_ a: ZeroOneTwoThree) { // Overload resolution can resolve this to the right constructor. var h = ZeroOneTwoThree(1) - var i = 0 > 3 ? .none : .some(3) // expected-error {{reference to member 'none' cannot be resolved without a contextual type}} + var i = 0 > 3 ? .none : .some(3) // expected-error {{cannot infer contextual base in reference to member 'none'}} + // expected-error@-1 {{cannot infer contextual base in reference to member 'some'}} test3a; // expected-error {{unused function}} .Zero // expected-error {{reference to member 'Zero' cannot be resolved without a contextual type}} @@ -552,3 +554,65 @@ let _: EnumWithStructNone? = .none // Okay let _: EnumWithTypealiasNone? = .none // Okay let _: EnumWithBothStructAndComputedNone? = .none // Okay let _: EnumWithBothTypealiasAndComputedNone? = .none // Okay + +// SR-12063 + +let foo1: Foo? = Foo.none +let foo2: Foo?? = Foo.none + +switch foo1 { + case .none: break + // expected-warning@-1 {{assuming you mean 'Optional.none'; did you mean 'Foo.none' instead?}} + // expected-note@-2 {{use 'nil' to silence this warning}}{{8-13=nil}} + // expected-note@-3 {{use 'none?' instead}}{{9-13=none?}} + case .bar: break + default: break +} + +switch foo2 { + case .none: break + // expected-warning@-1 {{assuming you mean 'Optional>.none'; did you mean 'Foo.none' instead?}} + // expected-note@-2 {{use 'nil' to silence this warning}}{{8-13=nil}} + // expected-note@-3 {{use 'none??' instead}}{{9-13=none??}} + case .bar: break + default: break +} + +if case .none = foo1 {} +// expected-warning@-1 {{assuming you mean 'Optional.none'; did you mean 'Foo.none' instead?}} +// expected-note@-2 {{use 'nil' to silence this warning}}{{9-14=nil}} +// expected-note@-3 {{use 'none?' instead}}{{10-14=none?}} + +if case .none = foo2 {} +// expected-warning@-1 {{assuming you mean 'Optional>.none'; did you mean 'Foo.none' instead?}} +// expected-note@-2 {{use 'nil' to silence this warning}}{{9-14=nil}} +// expected-note@-3 {{use 'none??' instead}}{{10-14=none??}} + +switch foo1 { + case nil: break // Okay + case .bar: break + default: break +} + +switch foo1 { + case .none?: break // Okay + case .bar: break + default: break +} + +switch foo2 { + case nil: break // Okay + case .bar: break + default: break +} + +switch foo2 { + case .none??: break // Okay + case .bar: break + default: break +} + +if case nil = foo1 {} // Okay +if case .none? = foo1 {} // Okay +if case nil = foo2 {} // Okay +if case .none?? = foo2 {} // Okay diff --git a/test/decl/enum/frozen-nonresilient.swift b/test/decl/enum/frozen-nonresilient.swift index c7682ecb0e7ca..34312ec89b3c7 100644 --- a/test/decl/enum/frozen-nonresilient.swift +++ b/test/decl/enum/frozen-nonresilient.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -warnings-as-errors -@frozen public enum Exhaustive {} // expected-warning {{@frozen has no effect without -enable-library-evolution}} {{1-9=}} +@frozen public enum Exhaustive {} // expected-no-warning -@frozen enum NotPublic {} // expected-warning {{@frozen has no effect without -enable-library-evolution}} {{1-9=}} +@frozen enum NotPublic {} // expected-no-warning diff --git a/test/decl/enum/objc_bool_raw_value.swift b/test/decl/enum/objc_bool_raw_value.swift new file mode 100644 index 0000000000000..5245ceac3e7f3 --- /dev/null +++ b/test/decl/enum/objc_bool_raw_value.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: objc_interop + +extension Bool: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = value != 0 + } +} + +@objc enum IsDefinitelyRecursive : Bool, Equatable, Hashable { // expected-error{{'@objc' enum raw type 'Bool' is not an integer type}} + case recursive=false +} diff --git a/test/decl/enum/objc_enum_Error.swift b/test/decl/enum/objc_enum_Error.swift index bd964e8b8a9de..29b91238b012e 100644 --- a/test/decl/enum/objc_enum_Error.swift +++ b/test/decl/enum/objc_enum_Error.swift @@ -4,7 +4,7 @@ import Foundation -func acceptBridgeableNSError(_ e: E) { } +func acceptBridgeableNSError(_ e: E) { } // expected-note {{where 'E' = 'E3'}} @objc enum E2 : Int, Error { case A = 1 @@ -18,4 +18,4 @@ acceptBridgeableNSError(E2.A) } acceptBridgeableNSError(E3.A) -// expected-error@-1{{argument type 'E3' does not conform to expected type '_ObjectiveCBridgeableError'}} +// expected-error@-1{{global function 'acceptBridgeableNSError' requires that 'E3' conform to '_ObjectiveCBridgeableError'}} diff --git a/test/decl/enum/objc_enum_multi_file.swift b/test/decl/enum/objc_enum_multi_file.swift index aee48f148a83a..6e4451ad7f560 100644 --- a/test/decl/enum/objc_enum_multi_file.swift +++ b/test/decl/enum/objc_enum_multi_file.swift @@ -1,8 +1,9 @@ -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NO_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D BAD_RAW_TYPE 2>&1 | %FileCheck -check-prefix=BAD_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_TYPE %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_CASES 2>&1 | %FileCheck -check-prefix=NO_CASES %s -// RUN: not %target-swift-frontend -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D DUPLICATE_CASES 2>&1 | %FileCheck -check-prefix=DUPLICATE_CASES %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NO_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D BAD_RAW_TYPE 2>&1 | %FileCheck -check-prefix=BAD_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_TYPE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_TYPE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NON_INT_RAW_VALUE 2>&1 | %FileCheck -check-prefix=NON_INT_RAW_VALUE %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D NO_CASES 2>&1 | %FileCheck -check-prefix=NO_CASES %s +// RUN: not %target-swift-frontend -disable-objc-attr-requires-foundation-module -enable-objc-interop -module-name main %s -primary-file %S/Inputs/objc_enum_multi_file_helper.swift -emit-ir -D DUPLICATE_CASES 2>&1 | %FileCheck -check-prefix=DUPLICATE_CASES %s // Note that the *other* file is the primary file in this test! #if NO_RAW_TYPE @@ -38,6 +39,13 @@ // DUPLICATE_CASES: :[[@LINE-4]]:8: note: raw value implicitly auto-incremented from zero } +#elseif NON_INT_RAW_VALUE +@objc enum TheEnum : Int32 { + case A = 0 + case B = "B" + // NON_INT_RAW_VALUE: :[[@LINE-1]]:12: error: cannot convert value of type 'String' to raw type 'Int32' +} + #else enum TheEnum: Invalid { // should never be hit case A diff --git a/test/decl/ext/extensions.swift b/test/decl/ext/extensions.swift index b4eab4419c218..e62a20191479f 100644 --- a/test/decl/ext/extensions.swift +++ b/test/decl/ext/extensions.swift @@ -103,10 +103,10 @@ protocol P3 { func foo() -> Assoc } -struct X3 : P3 { // expected-note{{'X3' declared here}} +struct X3 : P3 { } -extension X3.Assoc { // expected-error{{'Assoc' is not a member type of 'X3'}} +extension X3.Assoc { } extension X3 { @@ -126,31 +126,202 @@ struct WrapperContext { } // Class-constrained extension where protocol does not impose class requirement +// SR-11298 protocol DoesNotImposeClassReq_1 {} - + class JustAClass: DoesNotImposeClassReq_1 { var property: String = "" } extension DoesNotImposeClassReq_1 where Self: JustAClass { - var wrappingProperty: String { + var wrappingProperty1: String { + get { return property } + set { property = newValue } // Okay + } + + var wrappingProperty2: String { get { return property } - set { property = newValue } + nonmutating set { property = newValue } // Okay + } + + var wrappingProperty3: String { + get { return property } + mutating set { property = newValue } // Okay + } + + mutating func foo() { + property = "" // Okay + wrappingProperty1 = "" // Okay + wrappingProperty2 = "" // Okay + wrappingProperty3 = "" // Okay + } + + func bar() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-3=mutating }} + property = "" // Okay + wrappingProperty1 = "" // Okay + wrappingProperty2 = "" // Okay + wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}} + } + + nonmutating func baz() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-14=mutating}} + property = "" // Okay + wrappingProperty1 = "" // Okay + wrappingProperty2 = "" // Okay + wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}} } } - -let instanceOfJustAClass = JustAClass() // expected-note {{change 'let' to 'var' to make it mutable}} -instanceOfJustAClass.wrappingProperty = "" // expected-error {{cannot assign to property: 'instanceOfJustAClass' is a 'let' constant}} + +let instanceOfJustAClass1 = JustAClass() // expected-note 2{{change 'let' to 'var' to make it mutable}} +instanceOfJustAClass1.wrappingProperty1 = "" // Okay +instanceOfJustAClass1.wrappingProperty2 = "" // Okay +instanceOfJustAClass1.wrappingProperty3 = "" // expected-error {{cannot assign to property: 'instanceOfJustAClass1' is a 'let' constant}} +instanceOfJustAClass1.foo() // expected-error {{cannot use mutating member on immutable value: 'instanceOfJustAClass1' is a 'let' constant}} +instanceOfJustAClass1.bar() // Okay +instanceOfJustAClass1.baz() // Okay + +var instanceOfJustAClass2 = JustAClass() +instanceOfJustAClass2.foo() // Okay protocol DoesNotImposeClassReq_2 { var property: String { get set } } extension DoesNotImposeClassReq_2 where Self : AnyObject { - var wrappingProperty: String { + var wrappingProperty1: String { get { property } - set { property = newValue } // Okay + set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}} + // expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}{{5-5=mutating }} + } + + var wrappingProperty2: String { + get { property } + nonmutating set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}} + // expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}{{5-16=mutating}} + } + + var wrappingProperty3: String { + get { property } + mutating set { property = newValue } // Okay + } + + mutating func foo() { + property = "" // Okay + wrappingProperty1 = "" // Okay (the error is on the setter declaration above) + wrappingProperty2 = "" // Okay (the error is on the setter declaration above) + wrappingProperty3 = "" // Okay + } + + func bar() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}{{3-3=mutating }} + property = "" // expected-error {{cannot assign to property: 'self' is immutable}} + wrappingProperty1 = "" // Okay (the error is on the setter declaration above) + wrappingProperty2 = "" // Okay (the error is on the setter declaration above) + wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}} + } + + nonmutating func baz() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}{{3-14=mutating}} + property = "" // expected-error {{cannot assign to property: 'self' is immutable}} + wrappingProperty1 = "" // Okay (the error is on the setter declaration above) + wrappingProperty2 = "" // Okay (the error is on the setter declaration above) + wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}} + } +} + +protocol DoesNotImposeClassReq_3 { + var someProperty: Int { get set } +} + +class JustAClass1: DoesNotImposeClassReq_3 { + var someProperty = 0 +} + +extension DoesNotImposeClassReq_3 where Self: JustAClass1 { + var anotherProperty1: Int { + get { return someProperty } + set { someProperty = newValue } // Okay + } + + var anotherProperty2: Int { + get { return someProperty } + mutating set { someProperty = newValue } // Okay + } +} + +let justAClass1 = JustAClass1() // expected-note {{change 'let' to 'var' to make it mutable}} +justAClass1.anotherProperty1 = 1234 // Okay +justAClass1.anotherProperty2 = 4321 // expected-error {{cannot assign to property: 'justAClass1' is a 'let' constant}} + +protocol ImposeClassReq1: AnyObject { + var someProperty: Int { get set } +} + +class JustAClass2: ImposeClassReq1 { + var someProperty = 0 +} + +extension ImposeClassReq1 where Self: AnyObject { + var wrappingProperty1: Int { + get { return someProperty } + set { someProperty = newValue } + } + + var wrappingProperty2: Int { + get { return someProperty } + mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} + } + + mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}} + someProperty = 1 + } + + nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} + someProperty = 2 + } + + func baz() { // Okay + someProperty = 3 + } +} + +extension ImposeClassReq1 { + var wrappingProperty3: Int { + get { return someProperty } + set { someProperty = newValue } + } +} + +let justAClass2 = JustAClass2() // expected-note {{change 'let' to 'var' to make it mutable}} +justAClass2.wrappingProperty1 = 9876 // Okay +justAClass2.wrappingProperty3 = 0987 // Okay +justAClass2.foo() // expected-error {{cannot use mutating member on immutable value: 'justAClass2' is a 'let' constant}} +justAClass2.bar() // Okay as well (complains about explicit nonmutating on decl) +justAClass2.baz() // Okay + +protocol ImposeClassReq2: AnyObject { + var someProperty: Int { get set } +} + +extension ImposeClassReq2 { + var wrappingProperty1: Int { + get { return someProperty } + set { someProperty = newValue } + } + + var wrappingProperty2: Int { + get { return someProperty } + mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} + } + + mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}} + someProperty = 1 + } + + nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} + someProperty = 2 + } + + func baz() { // Okay + someProperty = 3 } } @@ -170,3 +341,11 @@ extension Tree.LimbContent.Contents { extension Tree.BoughPayload.Contents { // expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}} } + +// SR-10466 Check 'where' clause when referencing type defined inside extension +struct SR_10466 { + var a : A // expected-error {{'SR_10466.A' (aka 'Int') requires the types 'T' and 'Never' be equivalent}} +} +extension SR_10466 where T == Never { // expected-note {{requirement specified as 'T' == 'Never' [with T = T]}} + typealias A = Int +} diff --git a/test/decl/ext/generic.swift b/test/decl/ext/generic.swift index 3e3c29e07d78a..7f1524f5fe3d7 100644 --- a/test/decl/ext/generic.swift +++ b/test/decl/ext/generic.swift @@ -131,7 +131,7 @@ func intArray(_ x: [Int]) { class GenericClass { } -extension GenericClass where T : Equatable { +extension GenericClass where T : Equatable { // expected-note {{where 'T' = 'T'}} func foo(_ x: T, y: T) -> Bool { return x == y } } @@ -140,7 +140,7 @@ func genericClassEquatable(_ gc: GenericClass, x: T, y: T) { } func genericClassNotEquatable(_ gc: GenericClass, x: T, y: T) { - gc.foo(x, y: y) // expected-error{{argument type 'T' does not conform to expected type 'Equatable'}} + gc.foo(x, y: y) // expected-error{{referencing instance method 'foo(_:y:)' on 'GenericClass' requires that 'T' conform to 'Equatable'}} } diff --git a/test/decl/ext/protocol.swift b/test/decl/ext/protocol.swift index 002db2fc429c9..70e6a1cd65abd 100644 --- a/test/decl/ext/protocol.swift +++ b/test/decl/ext/protocol.swift @@ -1039,9 +1039,9 @@ protocol Empty2 {} struct Concrete1 {} extension Concrete1 : Empty1 & Empty2 {} -typealias T = Empty1 & Empty2 +typealias TA = Empty1 & Empty2 struct Concrete2 {} -extension Concrete2 : T {} +extension Concrete2 : TA {} func f(_: T) {} diff --git a/test/decl/func/default-values.swift b/test/decl/func/default-values.swift index 123f6575e2aba..7a90c2587d9f3 100644 --- a/test/decl/func/default-values.swift +++ b/test/decl/func/default-values.swift @@ -174,3 +174,19 @@ let fooThing5 = Foo(a: 0, d: 1, h: nil) // expected-error {{missing arguments fo // Here b = false and g = nil, but we're checking that f doesn't get a default value let fooThing6 = Foo(a: 0, d: 1, e: 2, h: nil) // expected-error {{missing argument for parameter 'f' in call}} // expected-note@-29 {{'init(a:b:d:e:f:g:h:)' declared here}} + +// SR-11085 +func sr_11085(x: Int) {} +func sr_11085(line: String = #line) {} // expected-error {{default argument value of type 'Int' cannot be converted to type 'String'}} +sr_11085() + +class SR_11085_C { init(line: String = #line) {} } // expected-error {{default argument value of type 'Int' cannot be converted to type 'String'}} +let _ = SR_11085_C() + +// SR-11623 +func badGenericMagicLiteral(_ x: T = #function) -> T { x } // expected-error {{default argument value of type 'String' cannot be converted to type 'T'}} +let _: Int = badGenericMagicLiteral() + +func genericMagicLiteral(_ x: T = #line) -> T { x } // expected-note {{where 'T' = 'String'}} +let _: Int = genericMagicLiteral() +let _: String = genericMagicLiteral() // expected-error {{global function 'genericMagicLiteral' requires that 'String' conform to 'ExpressibleByIntegerLiteral'}} diff --git a/test/decl/func/functions.swift b/test/decl/func/functions.swift index e40866b898ba8..42c72eed7837a 100644 --- a/test/decl/func/functions.swift +++ b/test/decl/func/functions.swift @@ -58,14 +58,14 @@ func recover_colon_arrow_7() :Int { } // expected-error {{expected '->' after f func recover_missing_body_1() // expected-error {{expected '{' in body of function declaration}} func recover_missing_body_2() // expected-error {{expected '{' in body of function declaration}} - -> Int + -> Int // Ensure that we don't skip over the 'func g' over to the right paren in // function g, while recovering from parse error in f() parameter tuple. We // should produce the error about missing right paren. // // FIXME: The errors are awful. We should produce just the error about paren. -func f_recover_missing_tuple_paren(_ a: Int // expected-note {{to match this opening '('}} expected-error{{expected '{' in body of function declaration}} expected-error {{expected ')' in parameter}} +func f_recover_missing_tuple_paren(_ a: Int // expected-note {{to match this opening '('}} expected-error{{expected '{' in body of function declaration}} expected-error {{expected ')' in parameter}} func g_recover_missing_tuple_paren(_ b: Int) { } @@ -129,7 +129,7 @@ func rdar16786220(inout let c: Int) -> () { // expected-warning {{'let' in this c = 42 } -func multipleSpecifiers(a: inout __owned Int) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{34-42=}} +func multipleSpecifiers(a: inout __owned Int) {} // expected-error {{parameter must not have multiple '__owned', 'inout', or '__shared' specifiers}} {{28-34=}} // ambiguous operator emits same candidate multiple times infix operator !!! @@ -180,3 +180,20 @@ func parentheticalInout2(_ fn: (((inout Int)), Int) -> ()) { var value = 0 fn(&value, 0) } + +// SR-11724 +// FIXME: None of these diagnostics is particularly good. +func bogusDestructuring() { + struct Bar {} + + struct Foo { + func registerCallback(_ callback: @escaping ([Bar]) -> Void) {} + func registerCallback(_ callback: @escaping ([String: Bar]) -> Void) {} + func registerCallback(_ callback: @escaping (Bar?) -> Void) {} + } + + Foo().registerCallback { ([Bar]) in } // expected-warning {{unnamed parameters must be written with the empty name '_'}} {{29-29=_: }} + Foo().registerCallback { ([String: Bar]) in }// expected-warning {{unnamed parameters must be written with the empty name '_'}} {{29-29=_: }} + Foo().registerCallback { (Bar?) in } // expected-error {{unnamed parameters must be written with the empty name '_'}} + +} diff --git a/test/decl/func/operator.swift b/test/decl/func/operator.swift index ea05bb2a0a857..80d9c6635456e 100644 --- a/test/decl/func/operator.swift +++ b/test/decl/func/operator.swift @@ -168,7 +168,7 @@ prefix operator ++ prefix postfix func ++(x: Int) {} // expected-error {{'postfix' contradicts previous modifier 'prefix'}} {{8-16=}} postfix prefix func ++(x: Float) {} // expected-error {{'prefix' contradicts previous modifier 'postfix'}} {{9-16=}} postfix prefix infix func ++(x: Double) {} // expected-error {{'prefix' contradicts previous modifier 'postfix'}} {{9-16=}} expected-error {{'infix' contradicts previous modifier 'postfix'}} {{16-22=}} -infix prefix func +-+(x: Int, y: Int) {} // expected-error {{'infix' modifier is not required or allowed on func declarations}} {{1-7=}} expected-error{{'prefix' contradicts previous modifier 'infix'}} {{7-14=}} +infix prefix func +-+(x: Double, y: Double) {} // expected-error {{'infix' modifier is not required or allowed on func declarations}} {{1-7=}} expected-error{{'prefix' contradicts previous modifier 'infix'}} {{7-14=}} // Don't allow one to define a postfix '!'; it's built into the // language. Also illegal to have any postfix operator starting with '!'. @@ -243,7 +243,7 @@ struct S1 { } extension S1 { - func %%%%(lhs: S1, rhs: S1) -> S1 { return lhs } // expected-error{{operator '%%%%' declared in type 'S1' must be 'static'}}{{3-3=static }} + func %%%%(lhs: S1, rhs: S1) -> S1 { return lhs } // expected-error{{operator '%%%%' declared in extension of 'S1' must be 'static'}}{{3-3=static }} } class C0 { @@ -375,8 +375,8 @@ class C6 { static func == (lhs: C6, rhs: C6) -> Bool { return false } func test1(x: C6) { - // FIXME: Better would be: use of '=' in a boolean context, did you mean '=='? - if x == x && x = x { } // expected-error{{cannot convert value of type 'C6' to expected argument type 'Bool'}} + if x == x && x = x { } // expected-error{{use of '=' in a boolean context, did you mean '=='?}} {{20-21===}} + // expected-error@-1 {{cannot convert value of type 'C6' to expected argument type 'Bool'}} } } diff --git a/test/decl/init/basic_init.swift b/test/decl/init/basic_init.swift index f951f04c37be9..a271c0f1c2572 100644 --- a/test/decl/init/basic_init.swift +++ b/test/decl/init/basic_init.swift @@ -10,7 +10,7 @@ class C { init() {} } -typealias t = t // expected-error {{type alias 't' references itself}} +typealias t = t // expected-error {{type alias 't' references itself}} expected-note {{through reference here}} extension Foo { convenience init() {} // expected-error{{invalid redeclaration of synthesized 'init()'}} diff --git a/test/decl/init/cf-types.swift b/test/decl/init/cf-types.swift index 5253f570cdd29..bee9d7c4e1742 100644 --- a/test/decl/init/cf-types.swift +++ b/test/decl/init/cf-types.swift @@ -14,10 +14,27 @@ extension CGMutablePath { public convenience init(toss: Bool) throws { // expected-error{{convenience initializers are not supported in extensions of CF types}} self.init() } + + public init(simple: Bool) { // expected-error{{designated initializer cannot be declared in an extension of 'CGMutablePath'}}{{none}} + // expected-error @-1 {{designated initializer for 'CGMutablePath' cannot delegate (with 'self.init')}}{{none}} + self.init() // expected-note {{delegation occurs here}} + } + + public init?(value: Bool) { // expected-error{{designated initializer cannot be declared in an extension of 'CGMutablePath'}}{{none}} + // expected-error @-1 {{designated initializer for 'CGMutablePath' cannot delegate (with 'self.init')}}{{none}} + self.init() // expected-note {{delegation occurs here}} + } + + public init?(string: String) { // expected-error{{designated initializer cannot be declared in an extension of 'CGMutablePath'}}{{none}} + let _ = string + } } public func useInit() { let _ = CGMutablePath(p: true) let _ = CGMutablePath(maybe: true) let _ = try! CGMutablePath(toss: true) + let _ = CGMutablePath(simple: true) + let _ = CGMutablePath(value: true) + let _ = CGMutablePath(string: "value") } diff --git a/test/decl/init/constructor-kind.swift b/test/decl/init/constructor-kind.swift index 9b9f4903162dd..9b667145a14aa 100644 --- a/test/decl/init/constructor-kind.swift +++ b/test/decl/init/constructor-kind.swift @@ -67,6 +67,7 @@ struct SomeStruct { init(width: Int) { self.init() self.width = width // expected-error {{cannot assign value of type 'Int' to type 'Measurement'}} + // expected-error@-1 {{'let' property 'width' may not be initialized directly; use "self.init(...)" or "self = ..." instead}} self.height = Measurement(val: 20) // expected-error {{'let' property 'height' may not be initialized directly; use "self.init(...)" or "self = ..." instead}} } @@ -74,6 +75,7 @@ struct SomeStruct { init(height: Int) { self.width = Measurement(val: 10) // expected-error {{'let' property 'width' may not be initialized directly; use "self.init(...)" or "self = ..." instead}} self.height = height // expected-error {{cannot assign value of type 'Int' to type 'Measurement'}} + // expected-error@-1 {{'let' property 'height' may not be initialized directly; use "self.init(...)" or "self = ..." instead}} self.init() } } diff --git a/test/decl/init/nonnominal_init.swift b/test/decl/init/nonnominal_init.swift index 96a906f14e9be..44f22b7f141d8 100644 --- a/test/decl/init/nonnominal_init.swift +++ b/test/decl/init/nonnominal_init.swift @@ -8,9 +8,12 @@ indirect enum Or { } func deMorgan(_ ne: Not>) -> And, Not> { + // FIXME(diagnostics): The error message about initialization here is confusing return And, Not>( - Not { a in ne(.left(a)) }, // expected-error {{non-nominal type 'Not' (aka '(A) -> Never') does not support explicit initialization}} - Not { a in ne(.right(a)) } + Not { a in ne(.left(a)) }, // expected-error {{type 'Not' (aka '(A) -> Never') has no member 'init'}} + // expected-error@-1 {{type 'Or' has no member 'left'}} + Not { a in ne(.right(a)) } // expected-error {{type 'Not' (aka '(B) -> Never') has no member 'init'}} + // expected-error@-1 {{type 'Or' has no member 'right'}} ) } diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index e73d301e44db2..6d25970753ba7 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -22,7 +22,6 @@ class OuterGenericClass { protocol OuterProtocol { associatedtype Hen protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}} - // expected-note@-1 {{did you mean 'InnerProtocol'?}} associatedtype Rooster func flip(_ r: Rooster) func flop(_ h: Hen) // expected-error{{use of undeclared type 'Hen'}} @@ -33,7 +32,7 @@ struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int func f() { let _ = InnerProtocol.self } - // expected-error@-1 {{use of unresolved identifier 'InnerProtocol'}} + // expected-error@-1 {{protocol 'InnerProtocol' can only be used as a generic constraint because it has Self or associated type requirements}} } protocol Racoon { @@ -107,8 +106,7 @@ func testLookup(_ x: OuterForUFI.Inner) { x.extMethod() } -// N.B. Lookup fails here because OuterForUFI.Inner is marked invalid. func testLookup(_ x: T) { - x.req() // expected-error {{value of type 'T' has no member 'req'}} - x.extMethod() // expected-error {{value of type 'T' has no member 'extMethod'}} + x.req() + x.extMethod() } diff --git a/test/decl/nested/type_in_type.swift b/test/decl/nested/type_in_type.swift index 4ed7701d9bacb..e50daca7dc4b7 100644 --- a/test/decl/nested/type_in_type.swift +++ b/test/decl/nested/type_in_type.swift @@ -192,7 +192,7 @@ class X6 { // --------------------------------------------- // Unbound name references within a generic type // --------------------------------------------- -struct GS { +struct GS { //expected-note {{arguments to generic parameter 'T' ('T' and 'Int') are expected to be equal}} func f() -> GS { let gs = GS() return gs @@ -224,7 +224,7 @@ extension GS { } func h() { - _ = GS() as GS // expected-error{{'GS' is not convertible to 'GS'; did you mean to use 'as!' to force downcast?}} + _ = GS() as GS // expected-error{{cannot convert value of type 'GS' to type 'GS' in coercion}} } } diff --git a/test/decl/overload.swift b/test/decl/overload.swift index 1e876df4321ac..07ba5b7e948eb 100644 --- a/test/decl/overload.swift +++ b/test/decl/overload.swift @@ -5,8 +5,10 @@ var_redecl1 = 0 var var_redecl1: UInt // expected-error {{invalid redeclaration of 'var_redecl1'}} var var_redecl2: Int // expected-note {{previously declared here}} -var_redecl2 = 0 +// expected-note@-1 {{found this candidate}} +var_redecl2 = 0 // expected-error {{ambiguous use of 'var_redecl2'}} var var_redecl2: Int // expected-error {{invalid redeclaration of 'var_redecl2'}} +// expected-note@-1 {{found this candidate}} var var_redecl3: (Int) -> () { get {} } // expected-note {{previously declared here}} var var_redecl3: () -> () { get {} } // expected-error {{invalid redeclaration of 'var_redecl3'}} @@ -368,6 +370,10 @@ struct Escaping { func autoclosure(f: () -> Int) { } func autoclosure(f: @autoclosure () -> Int) { } +// @_nonEphemeral +func nonEphemeral(x: UnsafeMutableRawPointer) {} // expected-note {{'nonEphemeral(x:)' previously declared here}} +func nonEphemeral(@_nonEphemeral x: UnsafeMutableRawPointer) {} // expected-error {{invalid redeclaration of 'nonEphemeral(x:)'}} + // inout func inout2(x: Int) { } func inout2(x: inout Int) { } @@ -571,23 +577,27 @@ enum SR_10084_E_8 { } enum SR_10084_E_9 { - case A // expected-note {{found this candidate}} // expected-note {{'A' previously declared here}} - static let A: SR_10084_E_9 = .A // expected-note {{found this candidate}} // expected-error {{invalid redeclaration of 'A'}} // expected-error {{ambiguous use of 'A'}} + case A // expected-note {{'A' previously declared here}} expected-note {{found this candidate}} + static let A: SR_10084_E_9 = .A // expected-error {{invalid redeclaration of 'A'}} + // expected-error@-1 {{ambiguous use of 'A'}} expected-note@-1 {{found this candidate}} } enum SR_10084_E_10 { - static let A: SR_10084_E_10 = .A // expected-note {{found this candidate}} // expected-note {{'A' previously declared here}} // expected-error {{ambiguous use of 'A'}} - case A // expected-note {{found this candidate}} // expected-error {{invalid redeclaration of 'A'}} + static let A: SR_10084_E_10 = .A // expected-note {{'A' previously declared here}} + // expected-error@-1 {{ambiguous use of 'A'}} expected-note@-1 {{found this candidate}} + case A // expected-error {{invalid redeclaration of 'A'}} expected-note {{found this candidate}} } enum SR_10084_E_11 { - case A // expected-note {{found this candidate}} // expected-note {{'A' previously declared here}} - static var A: SR_10084_E_11 = .A // expected-note {{found this candidate}} // expected-error {{invalid redeclaration of 'A'}} // expected-error {{ambiguous use of 'A'}} + case A // expected-note {{'A' previously declared here}} expected-note {{found this candidate}} + static var A: SR_10084_E_11 = .A // expected-error {{invalid redeclaration of 'A'}} + // expected-error@-1 {{ambiguous use of 'A'}} expected-note@-1 {{found this candidate}} } enum SR_10084_E_12 { - static var A: SR_10084_E_12 = .A // expected-note {{found this candidate}} // expected-note {{'A' previously declared here}} // expected-error {{ambiguous use of 'A'}} - case A // expected-note {{found this candidate}} // expected-error {{invalid redeclaration of 'A'}} + static var A: SR_10084_E_12 = .A // expected-note {{'A' previously declared here}} + // expected-error@-1 {{ambiguous use of 'A'}} expected-note@-1 {{found this candidate}} + case A // expected-error {{invalid redeclaration of 'A'}} expected-note {{found this candidate}} } enum SR_10084_E_13 { diff --git a/test/decl/precedencegroup/circularity.swift b/test/decl/precedencegroup/circularity.swift index b3dee70310908..840898fe03450 100644 --- a/test/decl/precedencegroup/circularity.swift +++ b/test/decl/precedencegroup/circularity.swift @@ -6,31 +6,32 @@ import ExternPrecedences precedencegroup A { - higherThan: A // expected-error {{cycle in higherThan relation: A -> A}} + higherThan: A // expected-error {{cycle in 'higherThan' relation}} } precedencegroup B { // expected-note {{precedence group declared here}} - lowerThan: B // expected-error {{precedence group cannot be given lower precedence than group in same module; make the other precedence group higher than this one instead}} + lowerThan: B // expected-error {{cycle in 'lowerThan' relation}} + // expected-error@-1{{precedence group cannot be given lower precedence than group in same module; make the other precedence group higher than this one instead}} } precedencegroup C0 { - higherThan: C1 // expected-error {{cycle in higherThan relation: C0 -> C1 -> C0}} + higherThan: C1 // expected-error {{cycle in 'higherThan' relation}} } precedencegroup C1 { - higherThan: C0 + higherThan: C0 // expected-note{{through reference to precedence group 'C0' here}} } precedencegroup D0 { - higherThan: D1 // expected-error {{cycle in higherThan relation: D0 -> D1 -> D2 -> D0}} + higherThan: D1 // expected-error {{cycle in 'higherThan' relation}} } precedencegroup D1 { - higherThan: D2 + higherThan: D2 // expected-note{{through reference to precedence group 'D2' here}} } precedencegroup D2 { - higherThan: D0 + higherThan: D0 // expected-note{{through reference to precedence group 'D0' here}} } precedencegroup E0 { - higherThan: Extern1 // expected-error {{cycle in higherThan relation: E0 -> Extern1 -> Extern0 -> E0}} + higherThan: Extern1 // expected-error{{cycle in higherThan relation: E0 -> Extern1 -> Extern0 -> E0}} lowerThan: Extern0 } diff --git a/test/decl/protocol/conforms/Inputs/fixit_stub_mutability_proto_module.swift b/test/decl/protocol/conforms/Inputs/fixit_stub_mutability_proto_module.swift new file mode 100644 index 0000000000000..982efa9cad40e --- /dev/null +++ b/test/decl/protocol/conforms/Inputs/fixit_stub_mutability_proto_module.swift @@ -0,0 +1,4 @@ +public protocol ExternalMutabilityProto { + mutating func foo() + subscript() -> Int { mutating get nonmutating set } +} diff --git a/test/decl/protocol/conforms/circular_validation.swift b/test/decl/protocol/conforms/circular_validation.swift index 024112c281668..b52a8b516f063 100644 --- a/test/decl/protocol/conforms/circular_validation.swift +++ b/test/decl/protocol/conforms/circular_validation.swift @@ -13,6 +13,6 @@ struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}} } // FIXME: Lousy diagnostics on this case. -protocol SR9224_Foo: SR9224_Foobar {} // expected-error 3 {{protocol 'SR9224_Foo' refines itself}} -protocol SR9224_Bar: SR9224_Foobar {} // expected-error {{protocol 'SR9224_Bar' refines itself}} expected-note 2 {{protocol 'SR9224_Bar' declared here}} -typealias SR9224_Foobar = SR9224_Foo & SR9224_Bar \ No newline at end of file +protocol SR9224_Foo: SR9224_Foobar {} // expected-error 2 {{protocol 'SR9224_Foo' refines itself}} +protocol SR9224_Bar: SR9224_Foobar {} // expected-note {{protocol 'SR9224_Bar' declared here}} +typealias SR9224_Foobar = SR9224_Foo & SR9224_Bar diff --git a/test/decl/protocol/conforms/failure.swift b/test/decl/protocol/conforms/failure.swift index cf0e8dd76ef5e..f1861b74f8ea4 100644 --- a/test/decl/protocol/conforms/failure.swift +++ b/test/decl/protocol/conforms/failure.swift @@ -93,7 +93,6 @@ struct P6Conformer : P6 { // expected-error 2 {{does not conform}} } // rdar://problem/23033862 -// expected-note@+3 2 {{do you want to add protocol stubs?}} // expected-error@+2{{type 'A' does not conform to protocol 'OptionSet'}} // expected-error@+1{{type 'A' does not conform to protocol 'RawRepresentable'}} struct A: OptionSet { @@ -127,7 +126,6 @@ extension UInt32: ExpressibleByStringLiteral {} // expected-error@-1 {{type 'UInt32' does not conform to protocol 'ExpressibleByStringLiteral'}} // expected-error@-2 {{type 'UInt32' does not conform to protocol 'ExpressibleByExtendedGraphemeClusterLiteral'}} // expected-error@-3 {{type 'UInt32' does not conform to protocol 'ExpressibleByUnicodeScalarLiteral'}} -// expected-note@-4 {{do you want to add protocol stubs?}} // After successfully type-checking this (due to the presumption of // the type actually conforming), do not crash when failing to find diff --git a/test/decl/protocol/conforms/fixit_stub_batch_mode.swift b/test/decl/protocol/conforms/fixit_stub_batch_mode.swift index e31c99b62ca55..04c0ee7597b08 100644 --- a/test/decl/protocol/conforms/fixit_stub_batch_mode.swift +++ b/test/decl/protocol/conforms/fixit_stub_batch_mode.swift @@ -1,3 +1,3 @@ // RUN: %target-typecheck-verify-swift -enable-batch-mode %S/Inputs/fixit_stub_batch_mode_helper.swift -extension C: P {} // expected-error{{type 'C' does not conform to protocol 'P'}} expected-note {{do you want to add protocol stubs?}} +extension C: P {} // expected-error{{type 'C' does not conform to protocol 'P'}} diff --git a/test/decl/protocol/conforms/fixit_stub_editor.swift b/test/decl/protocol/conforms/fixit_stub_editor.swift index 653c02b3e941b..30c1aeb2aa1b5 100644 --- a/test/decl/protocol/conforms/fixit_stub_editor.swift +++ b/test/decl/protocol/conforms/fixit_stub_editor.swift @@ -1,4 +1,7 @@ -// RUN: %target-typecheck-verify-swift -diagnostics-editor-mode +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %S/Inputs/fixit_stub_mutability_proto_module.swift -emit-module -parse-as-library -o %t + +// RUN: %target-swift-frontend -typecheck %s -I %t -verify -diagnostics-editor-mode protocol P1 { @available(*, deprecated) @@ -26,3 +29,55 @@ protocol P4 : P3 { } class C2 : P4 {} // expected-error{{type 'C2' does not conform to protocol 'P4'}} expected-error{{type 'C2' does not conform to protocol 'P3'}} expected-note{{do you want to add protocol stubs?}}{{16-16=\n typealias T1 = <#type#>\n\n typealias T2 = <#type#>\n\n typealias T3 = <#type#>\n}} + + +// ============================================================================= +// Test how we print stubs for mutating and non-mutating requirements. +// +// - Test that we don't print 'mutating' in classes. +// - Test that we print 'non-mutating' for non-mutating setters +// in structs. +// ============================================================================= + +protocol MutabilityProto { + mutating func foo() + subscript() -> Int { get nonmutating set } +} + +class Class1: MutabilityProto { // expected-error{{type 'Class1' does not conform to protocol 'MutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{32-32=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} +} + +struct Struct1: MutabilityProto { // expected-error{{type 'Struct1' does not conform to protocol 'MutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{34-34=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n nonmutating set {\n <#code#>\n \}\n \}\n}} +} + +import fixit_stub_mutability_proto_module + +class Class2: ExternalMutabilityProto { // expected-error{{type 'Class2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{40-40=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set(newValue) {\n <#code#>\n \}\n \}\n}} +} + +struct Struct2: ExternalMutabilityProto { // expected-error{{type 'Struct2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{42-42=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n mutating get {\n <#code#>\n \}\n nonmutating set(newValue) {\n <#code#>\n \}\n \}\n}} +} + +protocol PropertyMutabilityProto { + var computed: Int { mutating get nonmutating set } + var stored: Int { mutating get set } +} + +class Class3: PropertyMutabilityProto { // expected-error{{type 'Class3' does not conform to protocol 'PropertyMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{40-40=\n var computed: Int\n\n var stored: Int\n}} +} + +struct Struct3: PropertyMutabilityProto { // expected-error{{type 'Struct3' does not conform to protocol 'PropertyMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{42-42=\n var computed: Int {\n mutating get {\n <#code#>\n \}\n nonmutating set {\n <#code#>\n \}\n \}\n\n var stored: Int\n}} +} + +class Class4 {} +extension Class4: PropertyMutabilityProto { // expected-error{{type 'Class4' does not conform to protocol 'PropertyMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{44-44=\n var computed: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n\n var stored: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}} +} + +// https://bugs.swift.org/browse/SR-9868 +protocol FooProto { + typealias CompletionType = (Int) -> Void + func doSomething(then completion: @escaping CompletionType) +} + +struct FooType : FooProto { // expected-error {{type 'FooType' does not conform to protocol 'FooProto'}} expected-note {{do you want to add protocol stubs?}} {{28-28=\n func doSomething(then completion: @escaping CompletionType) {\n <#code#>\n \}\n}} +} diff --git a/test/decl/protocol/conforms/nsobject.swift b/test/decl/protocol/conforms/nsobject.swift new file mode 100644 index 0000000000000..739e6524ffe61 --- /dev/null +++ b/test/decl/protocol/conforms/nsobject.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -swift-version 4 %s -verify + +// REQUIRES: objc_interop + +import Foundation + +class A: NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'A' should inherit 'NSObject' instead}}{{10-26=NSObject}} + +@objc protocol Other: NSObjectProtocol { } + +class B: Other { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'B' should inherit 'NSObject' instead}} + +class C { } + +class D: C, NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'D' should inherit 'NSObject' instead}} + +class E { } + +extension E: NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'E' should inherit 'NSObject' instead}} diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index 2f9cd8fc8b604..2ed52c88bd3b7 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -101,9 +101,10 @@ struct DoesNotConform : Up { // Circular protocols -protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 3 {{protocol 'CircleMiddle' refines itself}} -protocol CircleStart : CircleEnd { func circle_start() } -// expected-note@-1 3 {{protocol 'CircleStart' declared here}} +protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 2 {{protocol 'CircleMiddle' refines itself}} +// expected-note@-1 {{protocol 'CircleMiddle' declared here}} +protocol CircleStart : CircleEnd { func circle_start() } // expected-error {{protocol 'CircleStart' refines itself}} +// expected-note@-1 2 {{protocol 'CircleStart' declared here}} protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}} protocol CircleEntry : CircleTrivial { } @@ -118,7 +119,7 @@ struct Circle { func testCircular(_ circle: Circle) { // FIXME: It would be nice if this failure were suppressed because the protocols // have circular definitions. - _ = circle as CircleStart // expected-error{{'Circle' is not convertible to 'CircleStart'; did you mean to use 'as!' to force downcast?}} {{14-16=as!}} + _ = circle as CircleStart // expected-error{{value of type 'Circle' does not conform to 'CircleStart' in coercion}} } // @@ -482,14 +483,24 @@ func f(_ x : T) { class C2 {} func g(_ x : T) { - x as P2 // expected-error{{'T' is not convertible to 'P2'; did you mean to use 'as!' to force downcast?}} {{5-7=as!}} + x as P2 // expected-error{{value of type 'T' does not conform to 'P2' in coercion}} } class C3 : P1 {} // expected-error{{type 'C3' does not conform to protocol 'P1'}} func h(_ x : T) { _ = x as P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} } - +func i(_ x : T?) -> Bool { + return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} + // FIXME: Bogus diagnostic. See SR-11920. + // expected-warning@-2 {{checking a value with optional type 'T?' against dynamic type 'P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} +} +func j(_ x : C1) -> Bool { + return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} +} +func k(_ x : C1?) -> Bool { + return x is P1 // expected-error{{protocol 'P1' can only be used as a generic constraint because it has Self or associated type requirements}} +} protocol P4 { diff --git a/test/decl/protocol/req/associated_type_inference.swift b/test/decl/protocol/req/associated_type_inference.swift index 7003c21b53ed2..a435a2625aa95 100644 --- a/test/decl/protocol/req/associated_type_inference.swift +++ b/test/decl/protocol/req/associated_type_inference.swift @@ -150,7 +150,6 @@ struct XSubP0b : SubscriptP0 { struct XSubP0c : SubscriptP0 { // expected-error@-1 {{type 'XSubP0c' does not conform to protocol 'SubscriptP0'}} subscript (i: Index) -> Element { get { } } - // expected-error@-1 {{reference to invalid associated type 'Element' of type 'XSubP0c'}} } struct XSubP0d : SubscriptP0 { diff --git a/test/decl/protocol/req/associated_type_inference_valid.swift b/test/decl/protocol/req/associated_type_inference_valid.swift new file mode 100644 index 0000000000000..af92793e5a535 --- /dev/null +++ b/test/decl/protocol/req/associated_type_inference_valid.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend -emit-silgen %s + +// This is a SILGen test to ensure we can completely check these conformances +// and build valid AST. + +protocol P { + associatedtype T : Q = S + typealias Y = T.X + + func foo(_: T.X) +} + +protocol Q { + associatedtype X +} + +struct S : Q { + typealias X = () +} + +struct R : P { + let x: Y? = nil + func foo(_: Y) {} +} diff --git a/test/decl/protocol/req/missing_conformance.swift b/test/decl/protocol/req/missing_conformance.swift index 1f6c81d24c7e2..74d09e2621d5a 100644 --- a/test/decl/protocol/req/missing_conformance.swift +++ b/test/decl/protocol/req/missing_conformance.swift @@ -17,7 +17,6 @@ extension LikeOptionSet where RawValue : FixedWidthInteger { struct X : LikeOptionSet {} // expected-error@-1 {{type 'X' does not conform to protocol 'LikeSetAlgebra'}} // expected-error@-2 {{type 'X' does not conform to protocol 'RawRepresentable'}} -// expected-note@-3 {{do you want to add protocol stubs?}} protocol IterProtocol {} protocol LikeSequence { diff --git a/test/decl/protocol/special/coding/class_codable_inheritance_diagnostics.swift b/test/decl/protocol/special/coding/class_codable_inheritance_diagnostics.swift index 91db1cd5ee37e..1d9cb332def1b 100644 --- a/test/decl/protocol/special/coding/class_codable_inheritance_diagnostics.swift +++ b/test/decl/protocol/special/coding/class_codable_inheritance_diagnostics.swift @@ -62,7 +62,7 @@ class DecodableSuper : Decodable { } class DecodableSubWithoutInitialValue : DecodableSuper { // expected-error {{class 'DecodableSubWithoutInitialValue' has no initializers}} - // expected-note@-1 {{did you mean to override 'init(from:)'?}} + // expected-note@-1 {{did you mean to override 'init(from:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}}} var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}} } @@ -77,14 +77,14 @@ class CodableSuper : Codable { } class CodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'CodableSubWithoutInitialValue' has no initializers}} - // expected-note@-1 {{did you mean to override 'init(from:)' and 'encode(to:)'?}} + // expected-note@-1 {{did you mean to override 'init(from:)' and 'encode(to:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}\n\noverride func encode(to encoder: Encoder) throws {\n <#code#>\n\}}} var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}} } // We should only mention encode(to:) in the diagnostic if the subclass does not // override it. class EncodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'EncodableSubWithoutInitialValue' has no initializers}} - // expected-note@-1 {{did you mean to override 'init(from:)'?}} + // expected-note@-1 {{did you mean to override 'init(from:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}}} var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}} override func encode(to: Encoder) throws {} diff --git a/test/decl/protocol/special/coding/enum_coding_key.swift b/test/decl/protocol/special/coding/enum_coding_key.swift index 9a0ec0a487970..2671708804382 100644 --- a/test/decl/protocol/special/coding/enum_coding_key.swift +++ b/test/decl/protocol/special/coding/enum_coding_key.swift @@ -35,16 +35,16 @@ let _ = IntKey(intValue: 3) // Enums with a different raw value conforming to CodingKey should not get // implicit derived conformance. -enum Int8Key : Int8, CodingKey { // expected-error {{type 'Int8Key' does not conform to protocol 'CodingKey'}} expected-note 4 {{do you want to add protocol stubs?}} +enum Int8Key : Int8, CodingKey { // expected-error {{type 'Int8Key' does not conform to protocol 'CodingKey'}} case a = -1, b = 0, c = 1 } // Structs conforming to CodingKey should not get implicit derived conformance. -struct StructKey : CodingKey { // expected-error {{type 'StructKey' does not conform to protocol 'CodingKey'}} expected-note 4 {{do you want to add protocol stubs?}} +struct StructKey : CodingKey { // expected-error {{type 'StructKey' does not conform to protocol 'CodingKey'}} } // Classes conforming to CodingKey should not get implict derived conformance. -class ClassKey : CodingKey { //expected-error {{type 'ClassKey' does not conform to protocol 'CodingKey'}} expected-note 4 {{do you want to add protocol stubs?}} +class ClassKey : CodingKey { //expected-error {{type 'ClassKey' does not conform to protocol 'CodingKey'}} } // Types which are valid for CodingKey derived conformance should not get that diff --git a/test/decl/protocol/sr8767.swift b/test/decl/protocol/sr8767.swift new file mode 100644 index 0000000000000..6b0cb653af89b --- /dev/null +++ b/test/decl/protocol/sr8767.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +// SR-8767: a number of related problems with unqualified lookup of +// associated type names. + + +// #1 +public protocol PA { + associatedtype F +} + +public protocol PDA : PA { +} + +public protocol PrB { + associatedtype F +} + +extension PDA where Self : PrB { + public init(first: F?) { + fatalError() + } +} + +// #2 +public protocol S { associatedtype F } +public protocol AM : S {} +public protocol AL { associatedtype F } +extension AM where Self : AL { + public init(first: F?) { fatalError() } +} + +// #3 +public protocol S2 { associatedtype F } +public protocol A2 : S2 {} +public protocol Z2 { associatedtype F } +extension A2 where Self : Z2 { + public init(first: F?) { fatalError() } +} + +// #4 +public protocol BM { associatedtype F } +public protocol C : BM {} +public protocol BL { associatedtype F } +extension C where Self : BL { public init(first: F?) { fatalError() } } + +// #5 +public protocol AZ { associatedtype F } +public protocol ZA : AZ {} +public protocol AA { associatedtype F } +extension ZA where Self : AA { public init(first: F?) { fatalError() } } + +// #6 +public protocol AZ2 { associatedtype F } +public protocol ZA2 : AZ2 {} +public protocol ZZ2 { associatedtype F } +extension ZA2 where Self : ZZ2 { public init(first: F?) { fatalError() } } + +// #7 +public protocol ZA3 { associatedtype F } +public protocol AZ3 : ZA3 {} +public protocol ZZ3 { associatedtype F } +extension AZ3 where Self : ZZ3 { public init(first: F?) { fatalError() } } + diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 1cd4fe9c6ff49..c7b92dc292d9c 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -327,6 +327,7 @@ class ClassConformingToRefinedProtocol: RefinedProtocol {} struct GenSubscriptFixitTest { subscript(_ arg: T) -> Bool { return true } // expected-note 3 {{declared here}} + // expected-note@-1 2 {{in call to 'subscript(_:)'}} } func testGenSubscriptFixit(_ s0: GenSubscriptFixitTest) { @@ -339,26 +340,30 @@ func testUnresolvedMemberSubscriptFixit(_ s0: GenSubscriptFixitTest) { _ = s0.subscript // expected-error@-1 {{value of type 'GenSubscriptFixitTest' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{9-19=[<#index#>]}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} s0.subscript = true // expected-error@-1 {{value of type 'GenSubscriptFixitTest' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{5-15=[<#index#>]}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} } struct SubscriptTest1 { subscript(keyword:String) -> Bool { return true } - // expected-note@-1 4 {{found this candidate}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'Bool'}} subscript(keyword:String) -> String? {return nil } - // expected-note@-1 4 {{found this candidate}} + // expected-note@-1 5 {{found this candidate}} expected-note@-1 {{found candidate with type 'String?'}} subscript(arg: SubClass) -> Bool { return true } // expected-note {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: Protocol) -> Bool { return true } // expected-note 2 {{declared here}} + // expected-note@-1 2 {{found this candidate}} subscript(arg: (foo: Bool, bar: (Int, baz: SubClass)), arg2: String) -> Bool { return true } // expected-note@-1 3 {{declared here}} } func testSubscript1(_ s1 : SubscriptTest1) { - let _ : Int = s1["hello"] // expected-error {{ambiguous subscript with base type 'SubscriptTest1' and index type 'String'}} + let _ : Int = s1["hello"] // expected-error {{no 'subscript' candidates produce the expected contextual result type 'Int'}} if s1["hello"] {} @@ -376,11 +381,9 @@ func testSubscript1(_ s1 : SubscriptTest1) { _ = s1.subscript(ClassConformingToRefinedProtocol()) // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} {{9-10=}} {{10-19=}} {{19-20=[}} {{54-55=]}} _ = s1.subscript(true) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(Bool)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript(SuperClass()) - // expected-error@-1 {{cannot invoke 'subscript' with an argument list of type '(SuperClass)'}} - // expected-note@-2 {{overloads for 'subscript' exist with these partially matching parameter lists: (Protocol), (String), (SubClass)}} + // expected-error@-1 {{no exact matches in call to subscript}} _ = s1.subscript("hello") // expected-error@-1 {{value of type 'SubscriptTest1' has no property or method named 'subscript'; did you mean to use the subscript operator?}} _ = s1.subscript("hello" @@ -395,12 +398,13 @@ func testSubscript1(_ s1 : SubscriptTest1) { struct SubscriptTest2 { subscript(a : String, b : Int) -> Int { return 0 } // expected-note {{candidate expects value of type 'Int' for parameter #2}} // expected-note@-1 {{declared here}} + // expected-note@-2 {{candidate has partially matching parameter list (String, Int)}} subscript(a : String, b : String) -> Int { return 0 } // expected-note {{candidate expects value of type 'String' for parameter #2}} + // expected-note@-1 {{candidate has partially matching parameter list (String, String)}} } func testSubscript1(_ s2 : SubscriptTest2) { - _ = s2["foo"] // expected-error {{cannot subscript a value of type 'SubscriptTest2' with an argument of type 'String'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: (String, Int), (String, String)}} + _ = s2["foo"] // expected-error {{no exact matches in call to subscript}} let a = s2["foo", 1.0] // expected-error {{no exact matches in call to subscript}} diff --git a/test/decl/typealias/generic.swift b/test/decl/typealias/generic.swift index 73bef2c6f4d68..27a6c4f4790b5 100644 --- a/test/decl/typealias/generic.swift +++ b/test/decl/typealias/generic.swift @@ -32,7 +32,7 @@ typealias DS = MyType typealias BadA = MyType // expected-error {{type 'T' constrained to non-protocol, non-class type 'Int'}} -typealias BadB = MyType // expected-error {{'where' clause next to generic parameters is obsolete, must be written following the declaration's type}} {{17-32=}} {{53-53= where T == Int}} +typealias BadB = MyType // expected-error {{associated types must not have a generic parameter list}} // expected-error@-1 {{same-type requirement makes generic parameter 'T' non-generic}} typealias BadC = MyType // expected-error {{definition conflicts with previous value}} @@ -45,7 +45,7 @@ typealias Tuple3 = (T1, T1) where T1 : Hashable let _ : Tuple2 = (1, "foo") let _ : Tuple2 = (1, "foo") -let _ : Tuple2 = ("bar", // expected-error {{cannot convert value of type 'String' to specified type 'Int'}} +let _ : Tuple2 = ("bar", // expected-error {{cannot convert value of type '(String, String)' to specified type 'Tuple2' (aka '(Int, String)')}} "foo") func f() { @@ -68,7 +68,7 @@ typealias E = Int // expected-note {{generic type 'E' declared here}} // expected-note@-1 {{'T1' declared as parameter to type 'E'}} // expected-note@-2 {{'T2' declared as parameter to type 'E'}} -typealias F = (T1) -> T2 +typealias F = (T1) -> T2 // expected-note {{'T1' declared as parameter to type 'F'}} // Type alias of type alias. typealias G = A @@ -94,8 +94,7 @@ let _ : D = D(a: 1, b: 2) let _ : F = { (a : Int) -> Int in a } // Infer the types of F -// TODO QoI: Cannot infer T1/T2. -let _ : F = { a in a } // expected-error {{type of expression is ambiguous without more context}} +let _ : F = { a in a } // expected-error {{generic parameter 'T1' could not be inferred}} _ = MyType(a: "foo", b: 42) _ = A(a: "foo", b: 42) diff --git a/test/decl/var/function_builders.swift b/test/decl/var/function_builders.swift index afa2cd381edff..fd93eaf42cc9b 100644 --- a/test/decl/var/function_builders.swift +++ b/test/decl/var/function_builders.swift @@ -24,26 +24,20 @@ var globalWithEmptyImplicitGetter: Int {} // expected-error@-1 {{computed property must have accessors specified}} // expected-error@-3 {{function builder attribute 'Maker' can only be applied to a variable if it defines a getter}} -// FIXME: extra diagnostics @Maker var globalWithEmptyExplicitGetter: Int { get {} } // expected-error{{type 'Maker' has no member 'buildBlock'}} -// expected-error@-1 {{cannot convert return expression of type 'Any' to return type 'Int'}} @Maker var globalWithSingleGetter: Int { 0 } // expected-error {{ype 'Maker' has no member 'buildBlock'}} -// expected-error@-1 {{cannot convert return expression of type 'Any' to return type 'Int'}} @Maker var globalWithMultiGetter: Int { 0; 0 } // expected-error {{ype 'Maker' has no member 'buildBlock'}} -// expected-error@-1 {{cannot convert return expression of type 'Any' to return type 'Int'}} @Maker func globalFunction() {} // expected-error {{ype 'Maker' has no member 'buildBlock'}} -// expected-error@-1 {{unexpected non-void return value in void function}} @Maker func globalFunctionWithFunctionParam(fn: () -> ()) {} // expected-error {{ype 'Maker' has no member 'buildBlock'}} -// expected-error@-1 {{unexpected non-void return value in void function}} func makerParam(@Maker fn: () -> ()) {} diff --git a/test/decl/var/lazy_properties.swift b/test/decl/var/lazy_properties.swift index 586fd40ed4cf9..854b65b5d8b60 100644 --- a/test/decl/var/lazy_properties.swift +++ b/test/decl/var/lazy_properties.swift @@ -112,7 +112,7 @@ struct Outer { lazy var y = {_ = 3}() // expected-warning@-1 {{variable 'y' inferred to have type '()', which may be unexpected}} - // expected-note@-2 {{add an explicit type annotation to silence this warning}} + // expected-note@-2 {{add an explicit type annotation to silence this warning}} {{15-15=: ()}} } } diff --git a/test/decl/var/properties.swift b/test/decl/var/properties.swift index e4e9be2c785db..de196e6a94f21 100644 --- a/test/decl/var/properties.swift +++ b/test/decl/var/properties.swift @@ -489,7 +489,7 @@ extension ProtocolWithExtension2 { static let baz: ProtocolWithExtension2 = StructureImplementingProtocolWithExtension2(bar: "baz") // expected-error{{static stored properties not supported in protocol extensions}} } -func getS() -> S { +func getS() -> S { // expected-note 2{{did you mean 'getS'?}} let s: S return s } @@ -547,7 +547,7 @@ struct Aleph { } } -struct Beth { +struct Beth { // expected-note 2{{did you mean 'Beth'?}} var c: Int } @@ -1272,11 +1272,11 @@ class WeakFixItTest { // SR-8811 (Warning) -let sr8811a = fatalError() // expected-warning {{constant 'sr8811a' inferred to have type 'Never', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} +let sr8811a = fatalError() // expected-warning {{constant 'sr8811a' inferred to have type 'Never', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{12-12=: Never}} let sr8811b: Never = fatalError() // Ok -let sr8811c = (16, fatalError()) // expected-warning {{constant 'sr8811c' inferred to have type '(Int, Never)', which contains an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} +let sr8811c = (16, fatalError()) // expected-warning {{constant 'sr8811c' inferred to have type '(Int, Never)', which contains an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{12-12=: (Int, Never)}} let sr8811d: (Int, Never) = (16, fatalError()) // Ok @@ -1292,11 +1292,11 @@ class SR_10995 { } func sr_10995_foo() { - let doubleOptionalNever = makeDoubleOptionalNever() // expected-warning {{constant 'doubleOptionalNever' inferred to have type 'Never??', which may be unexpected}} - // expected-note@-1 {{add an explicit type annotation to silence this warning}} + let doubleOptionalNever = makeDoubleOptionalNever() // expected-warning {{constant 'doubleOptionalNever' inferred to have type 'Never??', which may be unexpected}} + // expected-note@-1 {{add an explicit type annotation to silence this warning}} {{28-28=: Never??}} // expected-warning@-2 {{initialization of immutable value 'doubleOptionalNever' was never used; consider replacing with assignment to '_' or removing it}} let singleOptionalNever = makeSingleOptionalNever() // expected-warning {{constant 'singleOptionalNever' inferred to have type 'Never?', which may be unexpected}} - // expected-note@-1 {{add an explicit type annotation to silence this warning}} + // expected-note@-1 {{add an explicit type annotation to silence this warning}} {{28-28=: Never?}} // expected-warning@-2 {{initialization of immutable value 'singleOptionalNever' was never used; consider replacing with assignment to '_' or removing it}} } } diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 31e7e9822e6dc..893cec18ecbaa 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -9,12 +9,11 @@ struct Wrapper { init(stored: T) { self._stored = stored } - + var wrappedValue: T { get { _stored } set { _stored = newValue } } - } @propertyWrapper @@ -216,7 +215,11 @@ struct BadCombinations { } struct MultipleWrappers { - @Wrapper(stored: 17) + // FIXME: The diagnostics here aren't great. The problem is that we're + // attempting to splice a 'wrappedValue:' argument into the call to Wrapper's + // init, but it doesn't have a matching init. We're then attempting to access + // the nested 'wrappedValue', but Wrapper's 'wrappedValue' is Int. + @Wrapper(stored: 17) // expected-error{{value of type 'Int' has no member 'wrappedValue'}} @WrapperWithInitialValue // expected-error{{extra argument 'wrappedValue' in call}} var x: Int = 17 @@ -235,9 +238,8 @@ struct Initialization { @Wrapper(stored: 17) var x2: Double - @Wrapper(stored: 17) // expected-error {{initializer expects a single parameter of type 'T' [with T = (wrappedValue: Int, stored: Int)]}} - // expected-note@-1 {{did you mean to pass a tuple?}} - var x3 = 42 + @Wrapper(stored: 17) + var x3 = 42 // expected-error {{extra argument 'wrappedValue' in call}} @Wrapper(stored: 17) var x4 @@ -245,10 +247,8 @@ struct Initialization { @WrapperWithInitialValue var y = true - // FIXME: For some reason this is type-checked twice, second time around solver complains about <> argument @WrapperWithInitialValue - var y2 = true // expected-error{{cannot convert value of type 'Bool' to expected argument type 'Int'}} - // expected-error@-1 {{cannot convert value of type '<>' to expected argument type 'Int'}} + var y2 = true // expected-error{{cannot convert value of type 'Bool' to specified type 'Int'}} mutating func checkTypes(s: String) { x2 = s // expected-error{{cannot assign value of type 'String' to type 'Double'}} @@ -720,8 +720,7 @@ struct DefaultedPrivateMemberwiseLets { func testDefaultedPrivateMemberwiseLets() { _ = DefaultedPrivateMemberwiseLets() _ = DefaultedPrivateMemberwiseLets(y: 42) - _ = DefaultedPrivateMemberwiseLets(x: Wrapper(stored: false)) // expected-error{{incorrect argument label in call (have 'x:', expected 'y:')}} - // expected-error@-1 {{cannot convert value of type 'Wrapper' to expected argument type 'Int'}} + _ = DefaultedPrivateMemberwiseLets(x: Wrapper(stored: false)) // expected-error{{argument passed to call that takes no arguments}} } @@ -837,6 +836,42 @@ struct UsesExplicitClosures { var y: Int } +// --------------------------------------------------------------------------- +// Enclosing instance diagnostics +// --------------------------------------------------------------------------- +@propertyWrapper +struct Observable { + private var stored: Value + + init(wrappedValue: Value) { + self.stored = wrappedValue + } + + @available(*, unavailable, message: "must be in a class") + var wrappedValue: Value { // expected-note{{'wrappedValue' has been explicitly marked unavailable here}} + get { fatalError("called wrappedValue getter") } + set { fatalError("called wrappedValue setter") } + } + + static subscript( + _enclosingInstance observed: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + observed[keyPath: storageKeyPath].stored + } + set { + observed[keyPath: storageKeyPath].stored = newValue + } + } +} + +struct MyObservedValueType { + @Observable // expected-error{{'wrappedValue' is unavailable: must be in a class}} + var observedProperty = 17 +} + // --------------------------------------------------------------------------- // Miscellaneous bugs // --------------------------------------------------------------------------- @@ -968,14 +1003,21 @@ struct TestComposition { @WrapperD @WrapperC var p4: Int? @WrapperD @WrapperE var p5: Int // expected-error{{property type 'Int' does not match that of the 'wrappedValue' property of its wrapper type 'WrapperD'}} - func triggerErrors(d: Double) { - p1 = d // expected-error{{cannot assign value of type 'Double' to type 'Int?'}} - p2 = d // expected-error{{cannot assign value of type 'Double' to type 'String?'}} - p3 = d // expected-error{{cannot assign value of type 'Double' to type 'Int?'}} + func triggerErrors(d: Double) { // expected-note 6 {{mark method 'mutating' to make 'self' mutable}} {{2-2=mutating }} + p1 = d // expected-error{{cannot assign value of type 'Double' to type 'Int'}} {{8-8=Int(}} {{9-9=)}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} + p2 = d // expected-error{{cannot assign value of type 'Double' to type 'String'}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} + // TODO(diagnostics): Looks like source range for 'd' here is reported as starting at 10, but it should be 8 + p3 = d // expected-error{{cannot assign value of type 'Double' to type 'Int'}} {{10-10=Int(}} {{11-11=)}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} _p1 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperA>>'}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} _p2 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperA>>'}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} _p3 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperD, Int, String>'}} + // expected-error@-1 {{cannot assign to property: 'self' is immutable}} } } @@ -1117,8 +1159,8 @@ struct MissingPropertyWrapperUnwrap { } struct InvalidPropertyDelegateUse { - @Foo var x: Int = 42 // expected-error {{cannot invoke initializer for ty}} - // expected-note@-1{{overloads for 'Foo<_>' exist with these partially matching paramet}} + // TODO(diagnostics): We need to a tailored diagnostic for extraneous arguments in property delegate initialization + @Foo var x: Int = 42 // expected-error@:21 {{argument passed to call that takes no arguments}} func test() { self.x.foo() // expected-error {{value of type 'Int' has no member 'foo'}} @@ -1581,12 +1623,12 @@ struct SR_11288_S3: SR_11288_P3 { // typealias as propertyWrapper in a constrained protocol extension // protocol SR_11288_P4 {} -extension SR_11288_P4 where Self: AnyObject { +extension SR_11288_P4 where Self: AnyObject { // expected-note {{requirement specified as 'Self' : 'AnyObject' [with Self = SR_11288_S4]}} typealias SR_11288_Wrapper4 = SR_11288_S0 } struct SR_11288_S4: SR_11288_P4 { - @SR_11288_Wrapper4 var answer = 42 // expected-error 2 {{'SR_11288_S4.SR_11288_Wrapper4.Type' (aka 'SR_11288_S0.Type') requires that 'SR_11288_S4' conform to 'AnyObject'}} + @SR_11288_Wrapper4 var answer = 42 // expected-error {{'SR_11288_S4.SR_11288_Wrapper4' (aka 'SR_11288_S0') requires that 'SR_11288_S4' be a class type}} } class SR_11288_C0: SR_11288_P4 { @@ -1789,3 +1831,52 @@ protocol ProtocolWithWrapper { struct UsesProtocolWithWrapper: ProtocolWithWrapper { @Wrapper var foo: Int // expected-warning{{ignoring associated type 'Wrapper' in favor of module-scoped property wrapper 'Wrapper'; please qualify the reference with 'property_wrappers'}}{{4-4=property_wrappers.}} } + +// rdar://problem/56350060 - [Dynamic key path member lookup] Assertion when subscripting with a key path +func test_rdar56350060() { + @propertyWrapper + @dynamicMemberLookup + struct DynamicWrapper { + var wrappedValue: Value { fatalError() } + + subscript(keyPath keyPath: KeyPath) -> DynamicWrapper { + fatalError() + } + + subscript(dynamicMember keyPath: KeyPath) -> DynamicWrapper { + return self[keyPath: keyPath] // Ok + } + } +} + +// rdar://problem/57411331 - crash due to incorrectly synthesized "nil" default +// argument. +@propertyWrapper +struct Blah { + init(blah _: Int) { } + + var wrappedValue: Value { + let val: Value? = nil + return val! + } +} + +struct UseRdar57411331 { + let x = Rdar57411331(other: 5) +} + +struct Rdar57411331 { + @Blah(blah: 17) var something: Int? + + var other: Int +} + +// SR-11994 +@propertyWrapper +open class OpenPropertyWrapperWithPublicInit { + public init(wrappedValue: String) { // Okay + self.wrappedValue = wrappedValue + } + + open var wrappedValue: String = "Hello, world" +} diff --git a/test/decl/var/property_wrappers_invalid.swift b/test/decl/var/property_wrappers_invalid.swift new file mode 100644 index 0000000000000..61fb9811289c6 --- /dev/null +++ b/test/decl/var/property_wrappers_invalid.swift @@ -0,0 +1,36 @@ +// RUN: %target-swift-frontend -typecheck %s -verify -verify-ignore-unknown + +// FIXME: This should produce a diagnostic with a proper +// source location. Right now, we just get three useless errors: + +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context +// :0: error: type of expression is ambiguous without more context + +// The actual problem is the type of the subscript declaration is wrong. + +public class Store { + @Published public var state: Any + init() { + self.state = 0 + } +} + +@propertyWrapper public struct Published { + public init(wrappedValue: Value) {} + public var wrappedValue: Value { + get { fatalError() } + set {} + } + public static subscript(_enclosingInstance object: Any, + wrapped wrappedKeyPath: Any, + storage storageKeyPath: Any) + -> Value { + get { fatalError() } + set {} + } + public struct Publisher {} + public var projectedValue: Publisher { + mutating get { fatalError() } + } +} diff --git a/test/decl/var/property_wrappers_synthesis.swift b/test/decl/var/property_wrappers_synthesis.swift index 0ee1778a3954b..e46d63d9c1236 100644 --- a/test/decl/var/property_wrappers_synthesis.swift +++ b/test/decl/var/property_wrappers_synthesis.swift @@ -21,7 +21,7 @@ struct UseWrapper { // CHECK: accessor_decl{{.*}}_modify_for=wrapped // CHECK: yield_stmt - // CHECK: member_ref_expr{{.*}}UseWrapper.wrapped + // CHECK: member_ref_expr{{.*}}Wrapper.wrappedValue @Wrapper var wrapped = T() diff --git a/test/decl/var/static_var.swift b/test/decl/var/static_var.swift index bf151f7a18bc3..a4f67da5deedf 100644 --- a/test/decl/var/static_var.swift +++ b/test/decl/var/static_var.swift @@ -92,7 +92,7 @@ struct InMemberFunc { } } -struct S { // expected-note 3{{extended type declared here}} expected-note{{did you mean 'S'?}} +struct S { // expected-note 3{{extended type declared here}} static var v1: Int = 0 class var v2: Int = 0 // expected-error {{class properties are only allowed within classes; use 'static' to declare a static property}} {{3-8=static}} @@ -116,7 +116,7 @@ extension S { class let el2: Int = 0 // expected-error {{class properties are only allowed within classes; use 'static' to declare a static property}} {{3-8=static}} } -enum E { // expected-note 3{{extended type declared here}} expected-note{{did you mean 'E'?}} +enum E { // expected-note 3{{extended type declared here}} static var v1: Int = 0 class var v2: Int = 0 // expected-error {{class properties are only allowed within classes; use 'static' to declare a static property}} {{3-8=static}} @@ -141,7 +141,7 @@ extension E { class let el2: Int = 0 // expected-error {{class properties are only allowed within classes; use 'static' to declare a static property}} {{3-8=static}} } -class C { // expected-note{{did you mean 'C'?}} +class C { static var v1: Int = 0 class final var v3: Int = 0 // expected-error {{class stored properties not supported}} class var v4: Int = 0 // expected-error {{class stored properties not supported}} @@ -171,7 +171,7 @@ extension C { static final let el4: Int = 0 // expected-error {{static declarations are already final}} {{10-16=}} } -protocol P { // expected-note{{did you mean 'P'?}} expected-note{{extended type declared here}} +protocol P { // expected-note{{extended type declared here}} // Both `static` and `class` property requirements are equivalent in protocols rdar://problem/17198298 static var v1: Int { get } class var v2: Int { get } // expected-error {{class properties are only allowed within classes; use 'static' to declare a requirement fulfilled by either a static or class property}} {{3-8=static}} diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index 855afabfcebc6..0acd1a1bec08e 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -36,7 +36,7 @@ struct Broken { // rdar://16252090 - Warning when inferring empty tuple type for declarations var emptyTuple = testShadowing() // expected-warning {{variable 'emptyTuple' inferred to have type '()'}} \ - // expected-note {{add an explicit type annotation to silence this warning}} + // expected-note {{add an explicit type annotation to silence this warning}} {{15-15=: ()}} // rdar://15263687 - Diagnose variables inferenced to 'AnyObject' var ao1 : AnyObject @@ -44,7 +44,7 @@ var ao2 = ao1 var aot1 : AnyObject.Type var aot2 = aot1 // expected-warning {{variable 'aot2' inferred to have type 'AnyObject.Type', which may be unexpected}} \ - // expected-note {{add an explicit type annotation to silence this warning}} + // expected-note {{add an explicit type annotation to silence this warning}} {{9-9=: AnyObject.Type}} for item in [AnyObject]() { // No warning in for-each loop. @@ -60,13 +60,28 @@ func testAnyObjectOptional() -> AnyObject? { return x } +// SR-11511 Warning for inferring an array of empty tuples +var arrayOfEmptyTuples = [""].map { print($0) } // expected-warning {{variable 'arrayOfEmptyTuples' inferred to have type '[()]'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{23-23=: [()]}} + +var maybeEmpty = Optional(arrayOfEmptyTuples) // expected-warning {{variable 'maybeEmpty' inferred to have type '[()]?'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{15-15=: [()]?}} + +var shouldWarnWithoutSugar = (arrayOfEmptyTuples as Array<()>) // expected-warning {{variable 'shouldWarnWithoutSugar' inferred to have type 'Array<()>'}} \ + // expected-note {{add an explicit type annotation to silence this warning}} {{27-27=: Array<()>}} + class SomeClass {} // weak let's should be rejected weak let V = SomeClass() // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} let a = b ; let b = a -// expected-error@-1 {{let 'a' references itself}} +// expected-error@-1 {{circular reference}} +// expected-note@-2 {{through reference here}} +// expected-note@-3 {{through reference here}} +// expected-note@-4 {{through reference here}} +// expected-note@-5 {{through reference here}} +// expected-note@-6 {{through reference here}} // Swift should warn about immutable default initialized values let uselessValue : String? @@ -81,7 +96,7 @@ func tuplePatternDestructuring(_ x : Int, y : Int) { _ = i+j // QoI: type variable reconstruction failing for tuple types - let (x: g1, a: h1) = (b: x, a: y) // expected-error {{tuple type '(b: Int, a: Int)' is not convertible to tuple '(x: Int, a: Int)'}} + let (x: g1, a: h1) = (b: x, a: y) // expected-error {{cannot convert value of type '(b: Int, a: Int)' to specified type '(x: Int, a: Int)'}} } // Crash while compiling attached test-app. @@ -92,6 +107,7 @@ func test21057425() -> (Int, Int) { // rdar://problem/21081340 func test21081340() { + func foo() { } let (x: a, y: b): () = foo() // expected-error{{tuple pattern has the wrong length for tuple type '()'}} } diff --git a/test/diagnostics/educational-notes.swift b/test/diagnostics/educational-notes.swift new file mode 100644 index 0000000000000..a74ceb455da59 --- /dev/null +++ b/test/diagnostics/educational-notes.swift @@ -0,0 +1,15 @@ +// RUN: not %target-swift-frontend -enable-descriptive-diagnostics -diagnostic-documentation-path %S/test-docs/ -typecheck %s 2>&1 | %FileCheck %s + +// A diagnostic with no educational notes +let x = 1 + +// CHECK: error: expected expression after operator +// CHECK-NOT: {{-+$}} + +// A diagnostic with an educational note +extension (Int, Int) {} +// CHECK: error: non-nominal type '(Int, Int)' cannot be extended +// CHECK-NEXT: extension (Int, Int) {} +// CHECK-NEXT: ^ ~~~~~~~~~~ +// CHECK-NEXT: Nominal Types +// CHECK-NEXT: ------------- +// CHECK-NEXT: Nominal types documentation content diff --git a/test/diagnostics/test-docs/nominal-types.md b/test/diagnostics/test-docs/nominal-types.md new file mode 100644 index 0000000000000..67ccc055740ce --- /dev/null +++ b/test/diagnostics/test-docs/nominal-types.md @@ -0,0 +1,3 @@ +Nominal Types +------------- +Nominal types documentation content diff --git a/test/expr/capture/nested_class.swift b/test/expr/capture/nested_class.swift index 463e99fe7eaf9..2db3a0a154f36 100644 --- a/test/expr/capture/nested_class.swift +++ b/test/expr/capture/nested_class.swift @@ -36,3 +36,20 @@ struct StructWithInnerStruct { } } } + +// Types cannot close over top-level guard bindings +guard let x: Int = nil else { fatalError() } +// expected-note@-1 {{'x' declared here}} + +func getX() -> Int { return x } + +class ClosesOverGuard { // expected-note {{type declared here}} + func foo() { + _ = x + // expected-error@-1 {{class declaration cannot close over value 'x' defined in outer scope}} + } + + func bar() { + _ = getX() // This is diagnosed by SILGen. + } +} diff --git a/test/expr/cast/array_bridge.swift b/test/expr/cast/array_bridge.swift index bd27a41194629..5a44cab0f1012 100644 --- a/test/expr/cast/array_bridge.swift +++ b/test/expr/cast/array_bridge.swift @@ -45,7 +45,9 @@ var aa: [[A]] = [] var bb: [[B]] = [] aa = bb // expected-error {{cannot assign value of type '[[B]]' to type '[[A]]'}} +// expected-note@-1 {{arguments to generic parameter 'Element' ('B' and 'A') are expected to be equal}} bb = aa // expected-error {{cannot assign value of type '[[A]]' to type '[[B]]'}} +// expected-note@-1 {{arguments to generic parameter 'Element' ('A' and 'B') are expected to be equal}} class C { } diff --git a/test/expr/cast/as_coerce.swift b/test/expr/cast/as_coerce.swift index 9e51c019205a4..b880930410ba9 100644 --- a/test/expr/cast/as_coerce.swift +++ b/test/expr/cast/as_coerce.swift @@ -87,8 +87,8 @@ Double(1) as Double as String // expected-error{{cannot convert value of type 'D // expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}} // expected-error@-2 {{cannot convert value of type 'Double' to expected element type 'String'}} [[1]] as [[String]] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}} -(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type 'Double' to type 'Int' in coercion}} -(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type 'Double' to type 'String' in coercion}} +(1, 1.0) as (Int, Int) // expected-error{{cannot convert value of type '(Int, Double)' to type '(Int, Int)' in coercion}} +(1.0, 1, "asd") as (String, Int, Float) // expected-error{{cannot convert value of type '(Double, Int, String)' to type '(String, Int, Float)' in coercion}} (1, 1.0, "a", [1, 23]) as (Int, Double, String, [String]) // expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}} diff --git a/test/expr/cast/metatype_casts.swift b/test/expr/cast/metatype_casts.swift index 1fa5221df5436..68a4a1748992d 100644 --- a/test/expr/cast/metatype_casts.swift +++ b/test/expr/cast/metatype_casts.swift @@ -45,7 +45,7 @@ use(c as! Int.Type) // expected-warning{{always fails}} use(C.self as AnyObject.Protocol) // expected-error{{cannot convert value of type 'C.Type' to type 'AnyObject.Protocol' in coercion}} use(C.self as AnyObject.Type) -use(C.self as P.Type) // expected-error{{'C.Type' is not convertible to 'P.Type'; did you mean to use 'as!' to force downcast?}} {{12-14=as!}} +use(C.self as P.Type) // expected-error{{cannot convert value of type 'C.Type' to type 'P.Type' in coercion}} use(E.self as P.Protocol) // expected-error{{cannot convert value of type 'E.Type' to type 'P.Protocol' in coercion}} use(E.self as P.Type) diff --git a/test/expr/closure/anonymous.swift b/test/expr/closure/anonymous.swift index 29c83dbc48bb1..322e618e0cd84 100644 --- a/test/expr/closure/anonymous.swift +++ b/test/expr/closure/anonymous.swift @@ -27,17 +27,17 @@ func variadic() { let _: (Int...) -> () = {let _: [Int] = $0} // expected-error@-1 {{cannot convert value of type '(_) -> ()' to specified type '(Int...) -> ()'}} - // FIXME: Make the rest work takesVariadicInt({takesIntArray($0)}) - // expected-error@-1 {{cannot convert value of type '([Int]) -> ()' to expected argument type '(Int...) -> ()'}} + // expected-error@-1 {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}} let _: (Int...) -> () = {takesIntArray($0)} - // expected-error@-1 {{cannot convert value of type '([Int]) -> ()' to specified type '(Int...) -> ()'}} + // expected-error@-1 {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}} - // TODO(diagnostics): This requires special handling - variadic vs. array takesVariadicGeneric({takesIntArray($0)}) - // expected-error@-1 {{cannot convert value of type 'Array' to expected argument type '[Int]'}} - // expected-note@-2 {{arguments to generic parameter 'Element' ('Element' and 'Int') are expected to be equal}} + // expected-error@-1 {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}} + + // FIXME(diagnostics): Problems here are related to multi-statement closure bodies not being type-checked together with + // enclosing context. takesVariadicGeneric({let _: [Int] = $0}) // expected-error@-1 {{cannot convert value of type '(_) -> ()' to expected argument type '(_...) -> ()'}} diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 85cebe8d9eb4b..b7621be387d0d 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -12,6 +12,7 @@ var closure1 : () -> Int = {4} // Function producing 4 whenever it is called. var closure2 : (Int,Int) -> Int = { 4 } // expected-error{{contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored}} {{36-36= _,_ in}} var closure3a : () -> () -> (Int,Int) = {{ (4, 2) }} // multi-level closing. var closure3b : (Int,Int) -> (Int) -> (Int,Int) = {{ (4, 2) }} // expected-error{{contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored}} {{52-52=_,_ in }} +// expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{53-53= _ in}} var closure4 : (Int,Int) -> Int = { $0 + $1 } var closure5 : (Double) -> Int = { $0 + 1.0 @@ -71,7 +72,7 @@ func funcdecl5(_ a: Int, _ y: Int) { func6({a,b in 4.0 }) // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}} // TODO: This diagnostic can be improved: rdar://22128205 - func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, _) -> Int' to expected argument type '(Int, Int) -> Int'}} + func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, Int) -> Int' to expected argument type '(Int, Int) -> Int'}} @@ -150,28 +151,148 @@ func doVoidStuff(_ fn : @escaping () -> ()) {} class ExplicitSelfRequiredTest { var x = 42 func method() -> Int { - // explicit closure requires an explicit "self." base. + // explicit closure requires an explicit "self." base or an explicit capture. doVoidStuff({ self.x += 1 }) - doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{19-19=self.}} + doVoidStuff({ [self] in x += 1 }) + doVoidStuff({ [self = self] in x += 1 }) + doVoidStuff({ [unowned self] in x += 1 }) + doVoidStuff({ [unowned(unsafe) self] in x += 1 }) + doVoidStuff({ [unowned self = self] in x += 1 }) + + doStuff({ [self] in x+1 }) + doStuff({ [self = self] in x+1 }) + doStuff({ self.x+1 }) + doStuff({ [unowned self] in x+1 }) + doStuff({ [unowned(unsafe) self] in x+1 }) + doStuff({ [unowned self = self] in x+1 }) + doStuff({ x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{19-19=self.}} + doVoidStuff({ _ = "\(x)"}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} + doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in x += 1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in x+1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + + // Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self." + doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} + doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} + doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}} + doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}} + doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} + doVoidStuff({ [weak self] in _ = method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [weak self] in method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = self.x] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doStuff({ [self = self.x] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff { _ = self.method() } + doVoidStuff { [self] in _ = method() } + doVoidStuff { [self = self] in _ = method() } + doVoidStuff({ [unowned self] in _ = method() }) + doVoidStuff({ [unowned(unsafe) self] in _ = method() }) + doVoidStuff({ [unowned self = self] in _ = method() }) - // Methods follow the same rules as properties, uses of 'self' must be marked with "self." - doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{15-15=self.}} - doVoidStuff { _ = method() } // expected-error {{call to method 'method' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} doStuff { self.method() } + doStuff { [self] in method() } + doStuff({ [self = self] in method() }) + doStuff({ [unowned self] in method() }) + doStuff({ [unowned(unsafe) self] in method() }) + doStuff({ [unowned self = self] in method() }) + + // When there's no space between the opening brace and the first expression, insert it + doStuff {method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in }} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} + doVoidStuff {_ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doVoidStuff {() -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{34-34=self.}} + // With an empty capture list, insertion should should be suggested without a comma + doStuff { [] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{21-21=self.}} + doStuff { [ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} + doStuff { [ /* This space intentionally left blank. */ ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} expected-note{{reference 'self.' explicitly}} {{65-65=self.}} + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self}} + doStuff { [ // Nothing in this capture list! + ] + in + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{9-9=self.}} + } + // An inserted capture list should be on the same line as the opening brace, immediately following it. + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + // Note: Trailing whitespace on the following line is intentional and should not be removed! + doStuff { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff { // We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} + doStuff {// We have stuff to do. + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} + } + + // String interpolation should offer the diagnosis and fix-its at the expected locations + doVoidStuff { _ = "\(method())" } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + doVoidStuff { _ = "\(x+1)" } // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self] in}} + + // If we already have a capture list, self should be added to the list + let y = 1 + doStuff { [y] in method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{22-22=self.}} + doStuff { [ // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} + y // expected-warning {{capture 'y' was never used}} + ] in method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{14-14=self.}} // "self." shouldn't be required in the initializer expression in a capture list // This should not produce an error, "x" isn't being captured by the closure. doStuff({ [myX = x] in myX }) // This should produce an error, since x is used within the inner closure. - doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}} {{23-23=self.}} + doStuff({ [myX = {x}] in 4 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{23-23= [self] in }} expected-note{{reference 'self.' explicitly}} {{23-23=self.}} // expected-warning @-1 {{capture 'myX' was never used}} return 42 } } +// If the implicit self is of value type, no diagnostic should be produced. +struct ImplicitSelfAllowedInStruct { + var x = 42 + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ x += 1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + +enum ImplicitSelfAllowedInEnum { + case foo + var x: Int { 42 } + mutating func method() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method() }) + doVoidStuff({ _ = method() }) + } + + func method2() -> Int { + doStuff({ x+1 }) + doVoidStuff({ _ = x+1 }) + doStuff({ method2() }) + doVoidStuff({ _ = method2() }) + } +} + class SomeClass { var field : SomeClass? @@ -215,7 +336,8 @@ extension SomeClass { doStuff { [weak xyz = self.field] in xyz!.foo() } // rdar://16889886 - Assert when trying to weak capture a property of self in a lazy closure - doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit 'self.' to make capture semantics explicit}} {{36-36=self.}} + // FIXME: We should probably offer a fix-it to the field capture error and suppress the 'implicit self' error. https://bugs.swift.org/browse/SR-11634 + doStuff { [weak self.field] in field!.foo() } // expected-error {{fields may only be captured by assigning to a specific name}} expected-error {{reference to property 'field' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} {{36-36=self.}} expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} // expected-warning @+1 {{variable 'self' was written to, but never read}} doStuff { [weak self&field] in 42 } // expected-error {{expected ']' at end of capture list}} @@ -275,7 +397,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> Bool in }} +let samples = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} if (i > 10) { return true } else { return false } }() @@ -355,3 +477,9 @@ func lvalueCapture(c: GenericClass) { cc = wc! } } + +// Don't expose @lvalue-ness in diagnostics. +let closure = { // expected-error {{unable to infer complex closure return type; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} + var helper = true + return helper +} diff --git a/test/expr/closure/trailing.swift b/test/expr/closure/trailing.swift index 1eb40514bf20f..3b9800e96ef09 100644 --- a/test/expr/closure/trailing.swift +++ b/test/expr/closure/trailing.swift @@ -137,6 +137,7 @@ limitXY(someInt, toGamut: intArray) {} // expected-error{{extra trailing closur // QoI: Invalid trailing closures in stmt-conditions produce lowsy diagnostics func retBool(x: () -> Int) -> Bool {} func maybeInt(_: () -> Int) -> Int? {} +func twoClosureArgs(_:()->Void, _:()->Void) -> Bool {} class Foo23036383 { init() {} func map(_: (Int) -> Int) -> Int? {} @@ -153,55 +154,130 @@ func r23036383(foo: Foo23036383?, obj: Foo23036383) { if retBool(x: { 1 }) { } // OK if (retBool { 1 }) { } // OK - if retBool{ 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{13-13=(x: }} {{18-18=)}} + if retBool{ 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{13-13=(x: }} {{18-18=)}} } - if retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{13-14=(x: }} {{19-19=)}} + if retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{13-14=(x: }} {{19-19=)}} } - if retBool() { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{14-16=x: }} {{21-21=)}} - } else if retBool( ) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{21-24=x: }} {{29-29=)}} + if retBool() { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{14-16=x: }} {{21-21=)}} + } else if retBool( ) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{21-24=x: }} {{29-29=)}} } - if let _ = maybeInt { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}} + if let _ = maybeInt { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}} } - if let _ = maybeInt { 1 } , true { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}} + if let _ = maybeInt { 1 } , true { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}} } - if let _ = foo?.map {$0+1} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{29-29=)}} + if let _ = foo?.map {$0+1} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}} } - if let _ = foo?.map() {$0+1} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{23-25=}} {{31-31=)}} + if let _ = foo?.map() {$0+1} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{23-25=}} {{31-31=)}} } - if let _ = foo, retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{26-27=(x: }} {{32-32=)}} + if let _ = foo, retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{26-27=(x: }} {{32-32=)}} } - if obj.meth1(x: 1) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{20-22=, }} {{27-27=)}} + if obj.meth1(x: 1) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{20-22=, }} {{27-27=)}} } - if obj.meth2(1) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{17-19=, y: }} {{24-24=)}} + if obj.meth2(1) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{17-19=, y: }} {{24-24=)}} } - for _ in obj.filter {$0 > 4} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(by: }} {{31-31=)}} + for _ in obj.filter {$0 > 4} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(by: }} {{31-31=)}} } - for _ in obj.filter {$0 > 4} where true { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(by: }} {{31-31=)}} + for _ in obj.filter {$0 > 4} where true { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(by: }} {{31-31=)}} } - for _ in [1,2] where retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{31-32=(x: }} {{37-37=)}} + for _ in [1,2] where retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{31-32=(x: }} {{37-37=)}} } - while retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{16-17=(x: }} {{22-22=)}} + while retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{16-17=(x: }} {{22-22=)}} } - while let _ = foo, retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{29-30=(x: }} {{35-35=)}} + while let _ = foo, retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{29-30=(x: }} {{35-35=)}} } - switch retBool { return 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{17-18=(x: }} {{30-30=)}} + switch retBool { return 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{17-18=(x: }} {{30-30=)}} default: break } do { throw MyErr.A; - } catch MyErr.A where retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{32-33=(x: }} {{38-38=)}} + } catch MyErr.A where retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{32-33=(x: }} {{38-38=)}} } catch { } if let _ = maybeInt { 1 }, retBool { 1 } { } - // expected-error@-1 {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}} - // expected-error@-2 {{trailing closure requires parentheses for disambiguation in this context}} {{37-38=(x: }} {{43-43=)}} + // expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}} + // expected-warning@-2 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{37-38=(x: }} {{43-43=)}} + + if let _ = foo?.map {$0+1}.map {$0+1} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{33-34=(}} {{40-40=)}} + // expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}} + + if let _ = foo?.map {$0+1}.map({$0+1}) {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}} + + if let _ = foo?.map {$0+1}.map({$0+1}).map{$0+1} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{45-45=(}} {{51-51=)}} + // expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}} + + if twoClosureArgs({}) {} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{23-25=, }} {{27-27=)}} + + if let _ = (foo?.map {$0+1}.map({$0+1}).map{$0+1}) {} // OK + + if let _ = (foo?.map {$0+1}.map({$0+1})).map({$0+1}) {} // OK +} + +func id(fn: () -> T) -> T { return fn() } +func any(fn: () -> T) -> Any { return fn() } + +func testSR8736() { + if !id { true } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if id { true } == true { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if true == id { true } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if id { true } ? true : false { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if true ? id { true } : false { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if true ? true : id { false } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if id { [false,true] }[0] { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if id { { true } } () { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if any { true } as! Bool { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if let _ = any { "test" } as? Int { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if any { "test" } is Int { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if let _ = id { [] as [Int]? }?.first { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if id { true as Bool? }! { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if case id { 1 } = 1 { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if case 1 = id { 1 } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if case 1 = id { 1 } /*comment*/ { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} + + if case (id { 1 }) = 1 { return } // OK + + if case 1 = (id { 1 }) { return } // OK + + if [id { true }].count == 0 { return } // OK + + if [id { true } : "test"].keys.count == 0 { return } // OK + + if "\(id { true })" == "foo" { return } // OK + + if (id { true }) { return } // OK + + if (id { true }) { } + [1, 2, 3].count // expected-warning {{expression of type 'Int' is unused}} + + if true { } + () // OK + + if true + { + + } + () // OK } func overloadOnLabel(a: () -> Void) {} diff --git a/test/expr/delayed-ident/static_var.swift b/test/expr/delayed-ident/static_var.swift index 42dad4aa9955b..e1f47308def64 100644 --- a/test/expr/delayed-ident/static_var.swift +++ b/test/expr/delayed-ident/static_var.swift @@ -50,4 +50,5 @@ var _: HasClosure = .factoryOpt(3) // expected-error@-1 {{value of optional type '((Int) -> HasClosure)?' must be unwrapped to a value of type '(Int) -> HasClosure'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} -var _: HasClosure = .factoryOpt!(4) // expected-error {{type of expression is ambiguous without more context}} +// FIXME: we should accept this +var _: HasClosure = .factoryOpt!(4) // expected-error {{type 'Optional<_>' has no member 'factoryOpt'}} diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index b24049f09ba9a..e63fe024f7fa4 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -156,9 +156,9 @@ func errorRecovery() { var f: (Int,Int) = (1, 2, f : 3) // expected-error {{'(Int, Int, f: Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} // CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698 - var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, Any)', tuples have a different number of elements}} + var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, _)', tuples have a different number of elements}} var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}} - var i: (Bool, Bool) = makeTuple() // expected-error {{tuple type '(String, Int)' is not convertible to tuple '(Bool, Bool)'}} + var i: (Bool, Bool) = makeTuple() // expected-error {{cannot convert value of type '(String, Int)' to specified type '(Bool, Bool)'}} } func acceptsInt(_ x: Int) {} @@ -189,7 +189,7 @@ func test4() -> ((_ arg1: Int, _ arg2: Int) -> Int) { func test5() { let a: (Int, Int) = (1,2) var - _: ((Int) -> Int, Int) = a // expected-error {{tuple type '(Int, Int)' is not convertible to tuple '((Int) -> Int, Int)'}} + _: ((Int) -> Int, Int) = a // expected-error {{cannot convert value of type '(Int, Int)' to specified type '((Int) -> Int, Int)'}} let c: (a: Int, b: Int) = (1,2) @@ -525,7 +525,7 @@ func testSingleQuoteStringLiterals() { } // -var s = "" +var s = "" // expected-note {{did you mean 's'?}} s.append(contentsOf: ["x"]) //===----------------------------------------------------------------------===// @@ -588,21 +588,25 @@ func conversionTest(_ a: inout Double, b: inout Int) { var pi_f3 = float.init(getPi()) // expected-error {{ambiguous use of 'init(_:)'}} var pi_f4 = float.init(pi_f) - var e = Empty(f) // expected-warning {{variable 'e' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} + var e = Empty(f) // expected-warning {{variable 'e' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{8-8=: Empty}} var e2 = Empty(d) // expected-error{{cannot convert value of type 'Double' to expected argument type 'Float'}} - var e3 = Empty(Float(d)) // expected-warning {{variable 'e3' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} + var e3 = Empty(Float(d)) // expected-warning {{variable 'e3' inferred to have type 'Empty', which is an enum with no cases}} expected-note {{add an explicit type annotation to silence this warning}} {{9-9=: Empty}} } +// FIXME(diagnostics): This note is pointing to a synthesized init struct Rule { // expected-note {{'init(target:dependencies:)' declared here}} var target: String var dependencies: String } var ruleVar: Rule -ruleVar = Rule("a") // expected-error {{missing argument for parameter 'dependencies' in call}} +// FIXME(diagnostics): To be able to suggest different candidates here we need to teach the solver how to figure out to which parameter +// does argument belong to in this case. If the `target` was of a different type, we currently suggest to add an argument for `dependencies:` +// which is incorrect. +ruleVar = Rule("a") // expected-error {{missing argument label 'target:' in call}} +// expected-error@-1 {{missing argument for parameter 'dependencies' in call}} - -class C { +class C { // expected-note {{did you mean 'C'?}} var x: C? init(other: C?) { x = other } @@ -704,7 +708,7 @@ func test() { func unusedExpressionResults() { // Unused l-value _ // expected-error{{'_' can only appear in a pattern or on the left side of an assignment}} - + // expected-error@-1 {{expression resolves to an unused variable}} // Conditional Optional binding hides compiler error let optionalc:C? = nil @@ -744,10 +748,8 @@ func invalidDictionaryLiteral() { } -[4].joined(separator: [1]) // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}} -// expected-error@-1 {{cannot convert value of type '[Int]' to expected argument type 'String'}} -[4].joined(separator: [[[1]]]) // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}} -// expected-error@-1 {{cannot convert value of type '[[[Int]]]' to expected argument type 'String'}} +[4].joined(separator: [1]) // expected-error {{referencing instance method 'joined(separator:)' on 'Sequence' requires that 'Int' conform to 'Sequence'}} +[4].joined(separator: [[[1]]]) // expected-error {{referencing instance method 'joined(separator:)' on 'Sequence' requires that 'Int' conform to 'Sequence'}} //===----------------------------------------------------------------------===// // nil/metatype comparisons @@ -783,7 +785,7 @@ func testNilCoalescePrecedence(cond: Bool, a: Int?, r: ClosedRange?) { // ?? should have lower precedence than range and arithmetic operators. let r1 = r ?? (0...42) // ok let r2 = (r ?? 0)...42 // not ok: expected-error 2 {{cannot convert value of type 'Int' to expected argument type 'ClosedRange'}} - // expected-error@-1 {{argument type 'ClosedRange' does not conform to expected type 'Comparable'}} + // expected-error@-1 {{referencing operator function '...' on 'Comparable' requires that 'ClosedRange' conform to 'Comparable'}} let r3 = r ?? 0...42 // parses as the first one, not the second. @@ -857,7 +859,7 @@ func r20802757(_ z: inout Int = &g20802757) { // expected-error {{cannot provide print(z) } -_ = _.foo // expected-error {{type of expression is ambiguous without more context}} +_ = _.foo // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}} // wrong arg list crashing sourcekit func r22211854() { @@ -878,8 +880,8 @@ func r22348394() { } // Compiler crashes in Assertion failed: ((AllowOverwrite || !E->hasLValueAccessKind()) && "l-value access kind has already been set"), function visit -protocol P { var y: String? { get } } -func r23185177(_ x: P?) -> [String] { +protocol Proto { var y: String? { get } } +func r23185177(_ x: Proto?) -> [String] { return x?.y // expected-error{{cannot convert return expression of type 'String?' to return type '[String]'}} } @@ -889,17 +891,17 @@ func r22913570() { f(1 + 1) // expected-error{{missing argument for parameter 'to' in call}} } - // SR-628 mixing lvalues and rvalues in tuple expression -var x = 0 -var y = 1 -let _ = (x, x + 1).0 -let _ = (x, 3).1 -(x,y) = (2,3) -(x,4) = (1,2) // expected-error {{cannot assign to value: literals are not mutable}} -(x,y).1 = 7 // expected-error {{cannot assign to immutable expression of type 'Int'}} -x = (x,(3,y)).1.1 - +do { + var x = 0 + var y = 1 + let _ = (x, x + 1).0 + let _ = (x, 3).1 + (x,y) = (2,3) + (x,4) = (1,2) // expected-error {{cannot assign to value: literals are not mutable}} + (x,y).1 = 7 // expected-error {{cannot assign to immutable expression of type 'Int'}} + x = (x,(3,y)).1.1 +} // SR-3439 subscript with pound exprssions. Sr3439: do { diff --git a/test/expr/postfix/dot/init_ref_delegation.swift b/test/expr/postfix/dot/init_ref_delegation.swift index 8400d621a036e..33b1512b6ad44 100644 --- a/test/expr/postfix/dot/init_ref_delegation.swift +++ b/test/expr/postfix/dot/init_ref_delegation.swift @@ -67,8 +67,7 @@ class Z0 { init() { // expected-error {{designated initializer for 'Z0' cannot delegate (with 'self.init'); did you mean this to be a convenience initializer?}} {{3-3=convenience }} // expected-note @+2 {{delegation occurs here}} - self.init(5, 5) // expected-error{{cannot invoke 'Z0.init' with an argument list of type '(Int, Int)'}} - // expected-note @-1 {{overloads for 'Z0.init' exist with these partially matching parameter lists: (), (value: Double), (value: Int)}} + self.init(5, 5) // expected-error{{extra argument in call}} } init(value: Int) { /* ... */ } @@ -77,8 +76,7 @@ class Z0 { struct Z1 { init() { - self.init(5, 5) // expected-error{{cannot invoke 'Z1.init' with an argument list of type '(Int, Int)'}} - // expected-note @-1 {{overloads for 'Z1.init' exist with these partially matching parameter lists: (), (value: Double), (value: Int)}} + self.init(5, 5) // expected-error{{extra argument in call}} } init(value: Int) { /* ... */ } @@ -90,8 +88,7 @@ enum Z2 { case B init() { - self.init(5, 5) // expected-error{{cannot invoke 'Z2.init' with an argument list of type '(Int, Int)'}} - // expected-note @-1 {{overloads for 'Z2.init' exist with these partially matching parameter lists: (), (value: Double), (value: Int)}} + self.init(5, 5) // expected-error{{extra argument in call}} } init(value: Int) { /* ... */ } @@ -323,17 +320,15 @@ func foo(_ x: T, y: T.Type) where T: P { class TestOverloadSets { convenience init() { - self.init(5, 5) // expected-error{{cannot invoke 'TestOverloadSets.init' with an argument list of type '(Int, Int)'}} - // expected-note @-1 {{overloads for 'TestOverloadSets.init' exist with these partially matching parameter lists: (), (a: Z0), (value: Double), (value: Int)}} + self.init(5, 5) // expected-error{{extra argument in call}} } - convenience init(a : Z0) { - self.init(42 as Int8) // expected-error{{argument labels '(_:)' do not match any available overloads}} - // expected-note @-1 {{overloads for 'TestOverloadSets.init' exist with these partially matching parameter lists: (a: Z0), (value: Double), (value: Int)}} + convenience init(a : Z0) { // expected-note{{candidate has partially matching parameter list (a: Z0)}} + self.init(42 as Int8) // expected-error{{no exact matches in call to initializer}} } - init(value: Int) { /* ... */ } - init(value: Double) { /* ... */ } + init(value: Int) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Int)}} + init(value: Double) { /* ... */ } // expected-note{{candidate has partially matching parameter list (value: Double)}} } class TestNestedExpr { diff --git a/test/expr/primary/keypath/keypath-objc.swift b/test/expr/primary/keypath/keypath-objc.swift index a3bee670b9fd9..e8a5988f02bc8 100644 --- a/test/expr/primary/keypath/keypath-objc.swift +++ b/test/expr/primary/keypath/keypath-objc.swift @@ -134,7 +134,7 @@ func testSemanticErrors() { func testParseErrors() { let _: String = #keyPath; // expected-error{{expected '(' following '#keyPath'}} let _: String = #keyPath(123; // expected-error{{expected property or type name within '#keyPath(...)'}} - let _: String = #keyPath(a.123; // expected-error{{expected property or type name within '#keyPath(...)'}} expected-error {{use of unresolved identifier 'a'}} + let _: String = #keyPath(a.123; // expected-error{{expected property or type name within '#keyPath(...)'}} let _: String = #keyPath(A(b:c:d:).propSet); // expected-error{{an Objective-C key path cannot reference a declaration with a compound name}} expected-error{{unresolved identifier 'propSet'}} let _: String = #keyPath(A.propString; // expected-error{{expected ')' to complete '#keyPath' expression}} // expected-note@-1{{to match this opening '('}} @@ -160,7 +160,7 @@ class SR_10146_3 { } func doNotCrash_1(_ obj: AnyObject, _ kp: KeyPath) { - let _ = obj[keyPath: \.abc] // expected-error 2{{the root type of a Swift key path cannot be 'AnyObject'}} + let _ = obj[keyPath: \.abc] // expected-error {{the root type of a Swift key path cannot be 'AnyObject'}} let _ = obj[keyPath: kp] // expected-error {{the root type of a Swift key path cannot be 'AnyObject'}} } } diff --git a/test/expr/primary/keypath/keypath-observe-objc.swift b/test/expr/primary/keypath/keypath-observe-objc.swift new file mode 100644 index 0000000000000..c89ee06bde933 --- /dev/null +++ b/test/expr/primary/keypath/keypath-observe-objc.swift @@ -0,0 +1,39 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: objc_interop + +import Foundation + +class Foo: NSObject { + var number1 = 1 + dynamic var number2 = 2 + @objc var number3 = 3 + @objc dynamic var number4 = 4 +} + +class Bar: NSObject { + @objc dynamic let foo: Foo + + init(foo: Foo) { + self.foo = foo + super.init() + + _ = observe(\.foo.number1, options: [.new]) { _, change in + // expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number1' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}} + print("observer1") + } + + _ = observe(\.foo.number2, options: [.new]) { _, change in + // expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number2' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}} + print("observer2") + } + + _ = observe(\.foo.number3, options: [.new]) { _, change in + // expected-warning@-1 {{passing reference to non-'@objc dynamic' property 'number3' to KVO method 'observe(_:options:changeHandler:)' may lead to unexpected behavior or runtime trap}} + print("observer3") + } + + _ = observe(\.foo.number4, options: [.new]) { _, change in // Okay + print("observer4") + } + } +} diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 9310e7a573fbe..f6f94f1274d4e 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -36,7 +36,7 @@ struct A: Hashable { func hash(into hasher: inout Hasher) { fatalError() } } struct B {} -struct C { // expected-note 2 {{'T' declared as parameter to type 'C'}} +struct C { // expected-note 3 {{'T' declared as parameter to type 'C'}} var value: T subscript() -> T { get { return value } } subscript(sub: Sub) -> T { get { return value } set { } } @@ -180,8 +180,9 @@ func testKeyPath(sub: Sub, optSub: OptSub, var m = [\A.property, \A.[sub], \A.optProperty!] expect(&m, toHaveType: Exactly<[PartialKeyPath]>.self) - // FIXME: shouldn't be ambiguous - // expected-error@+1{{ambiguous}} + // \.optProperty returns an optional of Prop and `\.[sub]` returns `A` + // expected-error@+2 {{key path value type 'Prop?' cannot be converted to contextual type 'Prop'}} + // expected-error@+1 {{key path value type 'A' cannot be converted to contextual type 'Prop'}} var n = [\A.property, \.optProperty, \.[sub], \.optProperty!] expect(&n, toHaveType: Exactly<[PartialKeyPath]>.self) @@ -230,6 +231,8 @@ func testDisembodiedStringInterpolation(x: Int) { func testNoComponents() { let _: KeyPath = \A // expected-error{{must have at least one component}} let _: KeyPath = \C // expected-error{{must have at least one component}} expected-error{{}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} + // expected-error@-2 {{cannot convert value of type 'KeyPath' to specified type 'KeyPath, A>'}} } struct TupleStruct { @@ -504,10 +507,10 @@ func testLabeledSubscript() { let k = \AA.[labeled: 0] // TODO: These ought to work without errors. - let _ = \AA.[keyPath: k] // expected-error {{incorrect argument label in call (have 'keyPath:', expected 'labeled:')}} + let _ = \AA.[keyPath: k] // expected-error {{extraneous argument label 'keyPath:' in call}} // expected-error@-1 {{cannot convert value of type 'KeyPath' to expected argument type 'Int'}} - let _ = \AA.[keyPath: \AA.[labeled: 0]] // expected-error {{incorrect argument label in call (have 'keyPath:', expected 'labeled:')}} + let _ = \AA.[keyPath: \AA.[labeled: 0]] // expected-error {{extraneous argument label 'keyPath:' in call}} // expected-error@-1 {{cannot convert value of type 'KeyPath' to expected argument type 'Int'}} } diff --git a/test/expr/unary/keypath/salvage-with-other-type-errors.swift b/test/expr/unary/keypath/salvage-with-other-type-errors.swift index 6806797aaf8c6..e06c026159eff 100644 --- a/test/expr/unary/keypath/salvage-with-other-type-errors.swift +++ b/test/expr/unary/keypath/salvage-with-other-type-errors.swift @@ -13,7 +13,7 @@ struct S { protocol K { } -func + (lhs: KeyPath, rhs: String) -> P { +func + (lhs: KeyPath, rhs: String) -> P { // expected-note {{where 'Object' = 'String'}} fatalError() } @@ -27,7 +27,7 @@ struct A { } extension A: K { - static let j = S(\A.id + "id") // expected-error {{argument type 'String' does not conform to expected type 'K'}} + static let j = S(\A.id + "id") // expected-error {{operator function '+' requires that 'String' conform to 'K'}} } // SR-5034 diff --git a/test/lit.cfg b/test/lit.cfg index 12dea814b564e..7a820ec9f89b1 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -118,11 +118,11 @@ def get_simulator_command(run_os, run_cpu, sdk_path): else: return "simctl spawn --standalone 'iPhone 5'" else: - return "simctl spawn --standalone 'iPhone Xs'" + return "simctl spawn --standalone 'iPhone 8'" elif run_os == 'tvos': return "simctl spawn --standalone 'Apple TV 4K'" elif run_os == 'watchos': - return "simctl spawn --standalone 'Apple Watch Series 3 - 38mm'" + return "simctl spawn --standalone 'Apple Watch Series 4 - 44mm'" else: lit_config.fatal("Unknown simulator OS %r" % run_os) @@ -275,6 +275,7 @@ if 'syntax_parser_lib' in config.available_features: config.swift_reflection_dump = inferSwiftBinary('swift-reflection-dump') config.swift_remoteast_test = inferSwiftBinary('swift-remoteast-test') config.swift_indent = inferSwiftBinary('swift-indent') +config.swift_symbolgraph_extract = inferSwiftBinary('swift-symbolgraph-extract') config.clang = inferSwiftBinary('clang') config.llvm_link = inferSwiftBinary('llvm-link') config.swift_llvm_opt = inferSwiftBinary('swift-llvm-opt') @@ -294,7 +295,7 @@ config.benchmark_driver = inferSwiftBinary('Benchmark_Driver') config.swift_utils = make_path(config.swift_src_root, 'utils') config.line_directive = make_path(config.swift_utils, 'line-directive') -config.gyb = make_path(config.swift_utils, 'gyb') +config.gyb = make_path(config.swift_utils, 'gyb.py') config.rth = make_path(config.swift_utils, 'rth') # Resilience test helper config.scale_test = make_path(config.swift_utils, 'scale-test') config.PathSanitizingFileCheck = make_path(config.swift_utils, 'PathSanitizingFileCheck') @@ -318,6 +319,10 @@ lit_config.note('Using resource dir: ' + test_resource_dir) # Parse the variant triple. (run_cpu, run_vendor, run_os, run_vers) = re.match('([^-]+)-([^-]+)-([^0-9]+)(.*)', config.variant_triple).groups() +if run_os == 'ios' and run_vers.endswith('-macabi'): + run_vers = run_vers[0:-len('-macabi')] + run_os = 'maccatalyst' + run_ptrsize = '64' if ('64' in run_cpu or run_cpu == "s390x") else '32' run_endian = 'little' if run_cpu != 's390x' else 'big' @@ -345,20 +350,27 @@ swift_version = lit_config.params.get('swift-version', lit_config.note('Compiling with -swift-version ' + swift_version) config.swift_test_options = '-swift-version ' + swift_version +differentiable_programming = lit_config.params.get('differentiable_programming', None) +if differentiable_programming is not None: + config.available_features.add('differentiable_programming') + test_options = os.environ.get('SWIFT_TEST_OPTIONS') if test_options: config.swift_test_options += ' ' config.swift_test_options += test_options config.swift_frontend_test_options = os.environ.get('SWIFT_FRONTEND_TEST_OPTIONS', '') +config.swift_frontend_test_options += ' -ignore-module-source-info' config.swift_driver_test_options = os.environ.get('SWIFT_DRIVER_TEST_OPTIONS', '') +config.swift_driver_test_options += ' -Xfrontend' +config.swift_driver_test_options += ' -ignore-module-source-info' config.sil_test_options = os.environ.get('SIL_TEST_OPTIONS', '') -clang_module_cache_path = make_path(config.swift_test_results_dir, "clang-module-cache") -shutil.rmtree(clang_module_cache_path, ignore_errors=True) -mcp_opt = "-module-cache-path %r" % clang_module_cache_path -clang_mcp_opt = "-fmodules-cache-path=%r" % clang_module_cache_path -lit_config.note("Using Clang module cache: " + clang_module_cache_path) +config.clang_module_cache_path = make_path(config.swift_test_results_dir, "clang-module-cache") +shutil.rmtree(config.clang_module_cache_path, ignore_errors=True) +mcp_opt = "-module-cache-path %r" % config.clang_module_cache_path +clang_mcp_opt = "-fmodules-cache-path=%r" % config.clang_module_cache_path +lit_config.note("Using Clang module cache: " + config.clang_module_cache_path) lit_config.note("Using test results dir: " + config.swift_test_results_dir) completion_cache_path = make_path(config.swift_test_results_dir, "completion-cache") @@ -395,6 +407,7 @@ config.substitutions.append( ('%sil-passpipeline-dumper', "%r" % (config.sil_pas config.substitutions.append( ('%lldb-moduleimport-test', "%r %s" % (config.lldb_moduleimport_test, mcp_opt)) ) config.substitutions.append( ('%lldb-moduleimport-test-with-sdk', '%s -sdk %r' % (config.lldb_moduleimport_test, config.variant_sdk)) ) +config.substitutions.append( ('%swift-dump-pcm', "%r -dump-pcm" % config.swiftc) ) config.substitutions.append( ('%swift-ide-test_plain', config.swift_ide_test) ) config.substitutions.append( ('%swift-ide-test', "%r %s %s -swift-version %s" % (config.swift_ide_test, mcp_opt, ccp_opt, swift_version)) ) config.substitutions.append( ('%swift-syntax-test', config.swift_syntax_test) ) @@ -593,7 +606,12 @@ if config.benchmark_o != 'Benchmark_O': config.substitutions.append(('%target-triple', config.variant_triple)) if run_vendor == 'apple': - if True: + if run_os == 'maccatalyst': + config.stable_abi_triple = '%s-%s-ios13.0-macabi' % (run_cpu, run_vendor) + config.pre_stable_abi_triple = config.stable_abi_triple + config.next_stable_abi_triple = config.stable_abi_triple + config.available_features.add('swift_stable_abi') + else: # iOS 12.2 does not support 32-bit targets, so we cannot run tests that # want to deploy to an iOS that has Swift in the OS. if run_os == 'ios' and run_ptrsize == '32': @@ -603,6 +621,7 @@ if run_vendor == 'apple': PRE_STABLE_VERSION = { 'macosx': '10.14.3', 'ios': '12.1', + 'maccatalyst': '12.1', 'tvos': '12.1', 'watchos': '5.1' } @@ -613,6 +632,7 @@ if run_vendor == 'apple': STABLE_VERSION = { 'macosx': '10.14.4', 'ios': '12.2', + 'maccatalyst': '12.2', 'tvos': '12.2', 'watchos': '5.2' } @@ -623,6 +643,7 @@ if run_vendor == 'apple': NEXT_STABLE_VERSION = { 'macosx': '10.15', 'ios': '13', + 'maccatalyst': '13', 'tvos': '13', 'watchos': '6' } @@ -659,9 +680,21 @@ config.substitutions.append(('%sanitizers-target-triple', config.variant_triple.replace("ios7", "ios8"))) config.substitutions.append(('%target-cpu', run_cpu)) + +target_os_abi = run_os +target_os_is_maccatalyst = "FALSE" +if (run_os == 'maccatalyst'): + # For purposes of ABI, treat maccatalyst as macosx since the maccatalyst ABI + # must match the macosx ABI. + target_os_abi = 'macosx' + target_os_is_maccatalyst = "TRUE" + config.available_features.add("OS=ios") +config.substitutions.append(('%target-os-abi', target_os_abi)) +config.substitutions.append(('%target-os-is-maccatalyst', target_os_is_maccatalyst)) config.substitutions.append(('%target-endian', run_endian)) config.substitutions.append(('%target-os', run_os)) config.substitutions.append(('%target-ptrsize', run_ptrsize)) +config.substitutions.append(('%target-alignment', "%d" % (int(run_ptrsize)/8))) # Enable Darwin SDK-dependent tests if we have an SDK. # On Linux, assume that SDK path does not point to the Darwin SDK. @@ -693,7 +726,12 @@ if 'swift_interpreter' in config.available_features: config.target_runtime = "unknown" -swift_reflection_test_name = 'swift-reflection-test' + config.variant_suffix +if (getattr(config, 'darwin_enable_maccatalyst', False) and + config.darwin_maccatalyst_build_flavor == "ios-like"): + variant_suffix = config.darwin_osx_variant_suffix +else: + variant_suffix = config.variant_suffix +swift_reflection_test_name = 'swift-reflection-test' + variant_suffix def use_interpreter_for_simple_runs(): def make_simple_target_run(gyb=False, stdlib=False, parameterized=False): @@ -727,6 +765,7 @@ def use_interpreter_for_simple_runs(): config.available_features.add('interpret') target_specific_module_triple = config.variant_triple +target_future = target_specific_module_triple config.target_run = "" @@ -758,6 +797,7 @@ if run_vendor == 'apple': "-target %s %s %s" % (config.variant_triple, stdlib_resource_dir_opt, mcp_opt)) target_options_for_mock_sdk_after = sdk_overlay_dir_opt + target_future_version = '' if 'arm' in run_cpu and swift_test_mode != 'only_non_executable': raise RuntimeError('Device tests are currently only supported when ' @@ -769,12 +809,15 @@ if run_vendor == 'apple': if run_os == 'ios': lit_config.note('Testing iOS ' + config.variant_triple) xcrun_sdk_name = "iphoneos" + target_future_version = "99.0" elif run_os == 'tvos': lit_config.note('Testing AppleTV ' + config.variant_triple) xcrun_sdk_name = "appletvos" + target_future_version = "99.0" elif run_os == 'watchos': lit_config.note('Testing watchOS ' + config.variant_triple) xcrun_sdk_name = "watchos" + target_future_version = "9.99.0" config.target_cc_options = ( "-arch %s -m%s-version-min=%s %s" % @@ -800,14 +843,17 @@ if run_vendor == 'apple': config.available_features.add('DARWIN_SIMULATOR=ios') lit_config.note("Testing iOS simulator " + config.variant_triple) xcrun_sdk_name = "iphonesimulator" + target_future_version = "99.0" elif run_os == 'watchos': config.available_features.add('DARWIN_SIMULATOR=watchos') lit_config.note("Testing watchOS simulator " + config.variant_triple) xcrun_sdk_name = "watchsimulator" + target_future_version = "9.99.0" else: config.available_features.add('DARWIN_SIMULATOR=tvos') lit_config.note("Testing AppleTV simulator " + config.variant_triple) xcrun_sdk_name = "appletvsimulator" + target_future_version = "99.0" target_specific_module_triple += "-simulator" @@ -840,26 +886,51 @@ if run_vendor == 'apple': lit_config.fatal('Could not get or decode sw_vers output. ' + 'Perhaps the simulator is not working.') - elif run_os == 'macosx': + elif run_os == 'macosx' or run_os == 'maccatalyst': # OS X lit_config.note("Testing OS X " + config.variant_triple) xcrun_sdk_name = "macosx" - config.target_cc_options = ( - "-arch %s -m%s-version-min=%s %s" % - (run_cpu, run_os, run_vers, clang_mcp_opt)) + + if run_os == 'maccatalyst': + # For maccatalyst, pass the target triple to clang + # rather than arch and version separately. + config.target_cc_options = ( + "-target %s %s" % + (config.variant_triple, clang_mcp_opt)) + else: + config.target_cc_options = ( + "-arch %s -m%s-version-min=%s %s" % + (run_cpu, run_os, run_vers, clang_mcp_opt)) + + maccatalyst_frameworks_component = "" + if run_os == 'maccatalyst': + # Additional framework search paths for macCatalyst. + # These have to come before other search paths so that for + # unzippered twin frameworks the unzippered twin version + # is favored under macCatalyst. + maccatalyst_frameworks_dir = make_path(config.variant_sdk, + "System", "iOSSupport", "System", "Library", "Frameworks") + maccatalyst_frameworks_component = ( "-F %r" % maccatalyst_frameworks_dir ) + # Module triples end in ios-macabi. + target_specific_module_triple = '{}-apple-ios-macabi'.format( + { 'aarch64': 'arm64', 'amd64': 'x86_64' }.get(run_cpu, run_cpu) + ) config.target_build_swift = ( - ("%s %s %s -F %r -toolchain-stdlib-rpath " + ("%s %s %s %s -F %r -toolchain-stdlib-rpath " + "-Xlinker -rpath -Xlinker %r %s %s %s %s " + "-F %r -Xlinker -rpath -Xlinker %r") % (xcrun_prefix, config.swiftc, target_options, + maccatalyst_frameworks_component, extra_frameworks_dir, extra_frameworks_dir, sdk_overlay_linker_opt, config.swift_test_options, config.swift_driver_test_options, swift_execution_tests_extra_flags, sourcekitd_framework_dir, sourcekitd_framework_dir)) + config.target_run = "" + target_future_version = "10.99" if 'interpret' in lit_config.params: use_interpreter_for_simple_runs() @@ -877,9 +948,13 @@ if run_vendor == 'apple': config.target_sdk_name = xcrun_sdk_name config.target_ld = "%s ld -L%r" % (xcrun_prefix, make_path(test_resource_dir, config.target_sdk_name)) + + maccatalyst_extra_frameworks = "" + if run_os == 'maccatalyst': + maccatalyst_extra_frameworks = "-F {}/System/iOSSupport/System/Library/Frameworks".format(config.variant_sdk) config.target_swift_frontend = ( - "%s -frontend %s -sdk %r %s %s " % - (config.swiftc, target_options, config.variant_sdk, + "%s -frontend %s -sdk %r %s %s %s" % + (config.swiftc, target_options, config.variant_sdk, maccatalyst_extra_frameworks, config.swift_test_options, config.swift_frontend_test_options)) subst_target_swift_frontend_mock_sdk = ( "%s -frontend %s -sdk %r %s %s" % @@ -888,11 +963,17 @@ if run_vendor == 'apple': config.target_swift_modulewrap = ( '%s -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) subst_target_swift_frontend_mock_sdk_after = \ target_options_for_mock_sdk_after config.target_sil_opt = ( "%s %s %s %s" % (xcrun_prefix, config.sil_opt, target_options, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + "%s %s %s" % + (xcrun_prefix, config.swift_symbolgraph_extract, target_options)) config.target_swift_ide_test = ( "%s %s %s %s" % (xcrun_prefix, config.swift_ide_test, target_options, ccp_opt)) @@ -914,6 +995,9 @@ if run_vendor == 'apple': % (config.target_build_swift)) config.target_add_rpath = r'-Xlinker -rpath -Xlinker \1' + target_future = format('%s-apple-%s%s' % (run_cpu, run_os, target_future_version)) + config.otool_classic = ("%s otool-classic" % (xcrun_prefix)) + elif run_os in ['windows-msvc']: lit_config.note('Testing Windows ' + config.variant_triple) config.environment['NUMBER_OF_PROCESSORS'] = os.environ['NUMBER_OF_PROCESSORS'] @@ -964,6 +1048,10 @@ elif run_os in ['windows-msvc']: ('%r -target %s %s %s %s' % (config.sil_opt, config.variant_triple, \ resource_dir_opt, mcp_opt, \ config.sil_test_options)) + config.target_swift_symbolgraph_extract = \ + ('%r -target %s %s' % (config.swift_symbolgraph_extract, \ + config.variant_triple, \ + mcp_opt)) config.target_swift_ide_test = \ ('%r -target %s %s %s %s' % (config.swift_ide_test, \ config.variant_triple, \ @@ -977,6 +1065,8 @@ elif run_os in ['windows-msvc']: resource_dir_opt, mcp_opt)) config.target_swift_modulewrap = \ ('%r -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = \ + ('%r -emit-pcm -target %s' % (config.swiftc, config.variant_triple)) elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'windows-gnu'] or @@ -1008,6 +1098,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_shared_library_prefix = 'lib' config.target_shared_library_suffix = ".so" config.target_sdk_name = "android" + # Needed by several ParseableInterface/swift_build_sdk_interfaces tests on + # Android + config.environment['ANDROID_DATA'] = os.environ['ANDROID_DATA'] else: lit_config.note("Testing Linux " + config.variant_triple) config.target_object_format = "elf" @@ -1017,16 +1110,16 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_runtime = "native" config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract") - libdispatch_artifact_dir = make_path(config.libdispatch_build_path, 'src') + libdispatch_artifact_dir = config.libdispatch_build_path + libdispatch_swift_module_dir = make_path(libdispatch_artifact_dir, 'src', 'swift', 'swift') libdispatch_artifacts = [ make_path(libdispatch_artifact_dir, 'libdispatch.so'), make_path(libdispatch_artifact_dir, 'libswiftDispatch.so'), - make_path(libdispatch_artifact_dir, 'swift', 'Dispatch.swiftmodule')] + make_path(libdispatch_swift_module_dir, 'Dispatch.swiftmodule')] if (all(os.path.exists(p) for p in libdispatch_artifacts)): config.available_features.add('libdispatch') config.libdispatch_artifact_dir = libdispatch_artifact_dir libdispatch_source_dir = make_path(config.swift_src_root, os.pardir, 'swift-corelibs-libdispatch') - libdispatch_swift_module_dir = make_path(libdispatch_artifact_dir, 'swift') config.import_libdispatch = ('-I %s -I %s -L %s' % (libdispatch_source_dir, libdispatch_swift_module_dir, libdispatch_artifact_dir)) @@ -1052,6 +1145,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_sil_opt = ( '%s -target %s %s %s %s' % (config.sil_opt, config.variant_triple, resource_dir_opt, mcp_opt, config.sil_test_options)) + config.target_swift_symbolgraph_extract = ( + '%s -target %s %s' % + (config.swift_symbolgraph_extract, config.variant_triple, mcp_opt)) config.target_swift_ide_test = ( '%s -target %s %s %s %s' % (config.swift_ide_test, config.variant_triple, resource_dir_opt, @@ -1064,6 +1160,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'w config.target_swift_modulewrap = ( '%s -modulewrap -target %s' % (config.swiftc, config.variant_triple)) + config.target_swift_emit_pcm = ( + '%s -emit-pcm -target %s' % + (config.swiftc, config.variant_triple)) config.target_clang = ( "clang++ -target %s %s -fobjc-runtime=ios-5.0" % (config.variant_triple, clang_mcp_opt)) @@ -1166,6 +1265,10 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': '-target', config.variant_triple, android_include_paths_opt, resource_dir_opt, mcp_opt, config.sil_test_options]) + config.target_swift_symbolgraph_extract = ' '.join([ + config.swift_symbolgraph_extract, + '-target', config.variant_triple, + mcp_opt]) config.target_swift_ide_test = ' '.join([ config.swift_ide_test, '-target', config.variant_triple, @@ -1184,6 +1287,9 @@ elif run_os == 'linux-androideabi' or run_os == 'linux-android': config.target_swift_modulewrap = ' '.join([ config.swiftc, '-modulewrap', '-target', config.variant_triple]) + config.target_swift_emit_pcm = ' '.join([ + config.swiftc, '-emit-pcm', + '-target', config.variant_triple]) config.target_clang = ' '.join([ 'clang++', '-target', config.variant_triple, @@ -1205,6 +1311,7 @@ subst_target_swift_frontend_mock_sdk += " -typo-correction-limit 10 " config.substitutions.append(('%module-target-triple', target_specific_module_triple)) +config.substitutions.append(('%module-target-future', target_future)) # Add 'target-sdk-name' as the name for platform-specific directories config.substitutions.append(('%target-sdk-name', config.target_sdk_name)) @@ -1220,7 +1327,7 @@ ENV_VAR_PREFIXES = { 'appletvsimulator': SIMULATOR_ENV_PREFIX, 'android': 'ANDROID_CHILD_' } -TARGET_ENV_PREFIX = ENV_VAR_PREFIXES.get(config.target_sdk_name, "") +TARGET_ENV_PREFIX = ENV_VAR_PREFIXES.get(config.target_sdk_name, "") if not kIsAndroid else "" if 'remote_run_host' in lit_config.params: if 'remote_run_tmpdir' not in lit_config.params: @@ -1332,7 +1439,7 @@ def source_compiler_rt_libs(path): and config.compiler_rt_platform in lib]) compiler_rt_dir = make_path(test_resource_dir, 'clang', 'lib', - platform.system().lower()) + platform.system().lower() if not kIsAndroid else 'android') source_compiler_rt_libs(compiler_rt_dir) def check_runtime_libs(features_to_check): @@ -1345,6 +1452,7 @@ runtime_libs = { 'profile': 'profile_runtime', 'asan': 'asan_runtime', 'ubsan': 'ubsan_runtime', + 'scudo': 'scudo_runtime', 'safestack': 'safestack_runtime', 'fuzzer': 'fuzzer_runtime' } @@ -1430,6 +1538,13 @@ if platform.system() != 'Darwin' or swift_test_mode == 'optimize_none_with_impli platform_module_dir = make_path(test_resource_dir, config.target_sdk_name) if run_vendor != 'apple': platform_module_dir = make_path(platform_module_dir, run_cpu) + +platform_dylib_dir = platform_module_dir +if run_os == 'maccatalyst' and config.darwin_maccatalyst_build_flavor == "ios-like": + # When using the ios-macabi triple, look for module files + # in the 'maccatalyst' compiler resource directory. + platform_module_dir = make_path(test_resource_dir, 'maccatalyst') + lit_config.note('Using platform module dir: ' + platform_module_dir) if test_sdk_overlay_dir: platform_sdk_overlay_dir = test_sdk_overlay_dir @@ -1446,10 +1561,15 @@ if os.path.exists(static_libswiftCore_path): # Set up testing with the standard libraries coming from the OS / just-built libraries # default Swift tests to use the just-built libraries -target_stdlib_path = platform_module_dir +target_stdlib_path = platform_dylib_dir if not kIsWindows: libdispatch_path = getattr(config, 'libdispatch_artifact_dir', '') if 'use_os_stdlib' not in lit_config.params: + if run_os == 'maccatalyst': + # Under macCatalyst we need to have the unzippered twin dylib dir come before + # the zippered/macosx dylib dir so that unzippered twins are picked upload_dylibs + # before the macOS variant. + target_stdlib_path = "{0}:{1}".format(platform_module_dir, target_stdlib_path) lit_config.note('Testing with the just-built libraries at ' + target_stdlib_path) config.target_run = ( "/usr/bin/env " @@ -1458,10 +1578,13 @@ if not kIsWindows: "SIMCTL_CHILD_DYLD_LIBRARY_PATH='{0}' " # Simulator option .format(target_stdlib_path, libdispatch_path)) + config.target_run else: + config.available_features.add('use_os_stdlib') os_stdlib_path = '' if run_vendor == 'apple': #If we get swift-in-the-OS for non-Apple platforms, add a condition here os_stdlib_path = "/usr/lib/swift" + if run_os == 'maccatalyst': + os_stdlib_path = "/System/iOSSupport/usr/lib/swift:/usr/lib/swift" all_stdlib_path = os.path.pathsep.join((os_stdlib_path, target_stdlib_path)) lit_config.note('Testing with the standard libraries coming from the OS ' + all_stdlib_path) config.target_run = ( @@ -1475,21 +1598,21 @@ if not getattr(config, 'target_run_simple_swift', None): config.target_run_simple_swift_parameterized = \ (SubstituteCaptures('%%empty-directory(%%t) && ' '%s %s %%s \\1 -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run))) config.target_run_simple_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_stdlib_swift = ( '%%empty-directory(%%t) && ' '%s %s %%s -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) config.target_run_simple_swiftgyb = ( @@ -1497,7 +1620,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%gyb %%s -o %%t/main.swift && ' '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) @@ -1507,7 +1630,7 @@ if not getattr(config, 'target_run_simple_swift', None): '%%line-directive %%t/main.swift -- ' '%s %s %%t/main.swift -o %%t/a.out -module-name main ' '-Xfrontend -disable-access-control && ' - '%s %%t/a.out &&' + '%s %%t/a.out && ' '%%line-directive %%t/main.swift -- ' '%s %%t/a.out' % (config.target_build_swift, mcp_opt, config.target_codesign, config.target_run)) @@ -1553,9 +1676,17 @@ if hasattr(config, 'target_cc_options'): else: config.substitutions.append(('%target-cc-options', '')) -config.substitutions.append( - (r'%hardlink-or-copy\(from: *(.*), *to: *(.*)\)', - SubstituteCaptures(r'ln \1 \2 || cp \1 \2'))) +# WORKAROUND(rdar://53507844): On some macOS versions, we see flaky failures in +# tests which create a hard link to an executable and immediately invoke it. +# Work around this by always copying on Darwin. +if platform.system() == 'Darwin': + config.substitutions.append( + (r'%hardlink-or-copy\(from: *(.*), *to: *(.*)\)', + SubstituteCaptures(r'cp \1 \2'))) +else: + config.substitutions.append( + (r'%hardlink-or-copy\(from: *(.*), *to: *(.*)\)', + SubstituteCaptures(r'ln \1 \2 || cp \1 \2'))) config.substitutions.append(('%utils', config.swift_utils)) config.substitutions.append(('%line-directive', '%r %s' % (sys.executable, config.line_directive))) @@ -1582,9 +1713,10 @@ config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', swift_version)))) config.substitutions.append(('%target-swift-ide-test', "%s -swift-version %s" % (config.target_swift_ide_test, swift_version))) +config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) + if not hasattr(config, 'target_swift_reflection_test'): - config.target_swift_reflection_test = lit.util.which( - swift_reflection_test_name, config.environment['PATH']) + config.target_swift_reflection_test = inferSwiftBinary(swift_reflection_test_name) config.substitutions.append(('%target-swift-reflection-test', config.target_swift_reflection_test)) @@ -1602,9 +1734,13 @@ if hasattr(config, 'target_swift_autolink_extract'): config.substitutions.append(('%target-swift-modulewrap', config.target_swift_modulewrap)) +config.substitutions.append(('%target-swift-emit-pcm', + config.target_swift_emit_pcm)) config.substitutions.insert(0, ('%platform-module-dir', platform_module_dir)) config.substitutions.insert(0, ('%platform-sdk-overlay-dir', platform_sdk_overlay_dir)) +config.substitutions.insert(0, ('%platform-dylib-dir', platform_dylib_dir)) +config.substitutions.insert(0, ('%test-resource-dir', test_resource_dir)) if run_vendor != 'apple': extra_frameworks_dir = '' @@ -1628,6 +1764,9 @@ config.substitutions.append(('%target-resilience-test', config.target_resilience config.substitutions.append(('%llvm-profdata', config.llvm_profdata)) config.substitutions.append(('%llvm-cov', config.llvm_cov)) +if hasattr(config, 'otool_classic'): + config.substitutions.append(('%otool-classic', config.otool_classic)) + config.substitutions.append(('%FileCheck', '%r %r --sanitize BUILD_DIR=%r --sanitize SOURCE_DIR=%r --use-filecheck %r %s' % ( sys.executable, diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 8733e1f7b8ba9..69107930747b0 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -51,6 +51,10 @@ msvc_runtime_flags = { config.swift_stdlib_msvc_runtime = \ msvc_runtime_flags["@SWIFT_STDLIB_MSVC_RUNTIME_LIBRARY@"] +config.darwin_enable_maccatalyst = "@SWIFT_ENABLE_MACCATALYST@" == "TRUE" +config.darwin_maccatalyst_build_flavor = "@BUILD_FLAVOR@" +config.darwin_osx_variant_suffix = "@DEFAULT_OSX_VARIANT_SUFFIX@" + # Please remember to handle empty strings and/or unset variables correctly. if "@SWIFT_ASAN_BUILD@" == "TRUE": @@ -91,6 +95,9 @@ if "@SWIFT_HAVE_WORKING_STD_REGEX@" == "FALSE": if "@SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS@" == "TRUE": config.available_features.add('runtime_function_counters') +if "@SWIFT_BUILT_STANDALONE@" == "TRUE": + config.available_features.add('standalone_build') + if "@CMAKE_GENERATOR@" == "Xcode": xcode_bin_dir = os.path.join(config.llvm_obj_root, "@LLVM_BUILD_TYPE@", 'bin') @@ -100,6 +107,9 @@ if "@CMAKE_GENERATOR@" == "Xcode": config.available_features.add("CMAKE_GENERATOR=@CMAKE_GENERATOR@") +if "@SWIFT_ENABLE_MACCATALYST@" == "TRUE": + config.available_features.add('maccatalyst_support') + if "@SWIFT_BUILD_SYNTAXPARSERLIB@" == "TRUE": config.available_features.add('syntax_parser_lib') @@ -118,6 +128,9 @@ else: if '@SWIFT_INCLUDE_TOOLS@' == 'TRUE': config.available_features.add('swift_tools_extra') +if "@SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING@" == "TRUE": + config.available_features.add('differentiable_programming') + # Let the main config do the real work. if config.test_exec_root is None: config.test_exec_root = os.path.dirname(os.path.realpath(__file__)) diff --git a/test/multifile/Inputs/finalize-nested-types-other.swift b/test/multifile/Inputs/finalize-nested-types-other.swift new file mode 100644 index 0000000000000..e1c1676f12354 --- /dev/null +++ b/test/multifile/Inputs/finalize-nested-types-other.swift @@ -0,0 +1,7 @@ +public class Foo {} + +extension Foo { + public struct Nested { + public init() {} + } +} diff --git a/test/multifile/Inputs/protocol-conformance/broken.swift b/test/multifile/Inputs/protocol-conformance/broken.swift index 28afeb080cbc2..091a13a03f328 100644 --- a/test/multifile/Inputs/protocol-conformance/broken.swift +++ b/test/multifile/Inputs/protocol-conformance/broken.swift @@ -1 +1 @@ -extension X : P { } // expected-error{{type 'X' does not conform to protocol 'P'}} expected-note{{do you want to add protocol stubs?}} +extension X : P { } // expected-error{{type 'X' does not conform to protocol 'P'}} diff --git a/test/multifile/default-arguments/one-module/Inputs/invalid-magic-literals-other.swift b/test/multifile/default-arguments/one-module/Inputs/invalid-magic-literals-other.swift new file mode 100644 index 0000000000000..fbdb894278259 --- /dev/null +++ b/test/multifile/default-arguments/one-module/Inputs/invalid-magic-literals-other.swift @@ -0,0 +1,4 @@ + +func badMagicLiteral(_ x: String = #line) {} // expected-error {{default argument value of type 'Int' cannot be converted to type 'String'}} + +func badGenericMagicLiteral(_ x: T = #function) -> T { x } // expected-error {{default argument value of type 'String' cannot be converted to type 'T'}} diff --git a/test/multifile/default-arguments/one-module/Inputs/mismatched-magic-literals-other.swift b/test/multifile/default-arguments/one-module/Inputs/mismatched-magic-literals-other.swift new file mode 100644 index 0000000000000..01708d2847f8c --- /dev/null +++ b/test/multifile/default-arguments/one-module/Inputs/mismatched-magic-literals-other.swift @@ -0,0 +1,3 @@ +func callee(file: String = #file) {} // expected-note {{'file' declared here}} +func callee(optFile: String? = #file) {} // expected-note {{'optFile' declared here}} +func callee(arbitrary: String) {} diff --git a/test/multifile/default-arguments/one-module/invalid-magic-literals.swift b/test/multifile/default-arguments/one-module/invalid-magic-literals.swift new file mode 100644 index 0000000000000..317544f68cf19 --- /dev/null +++ b/test/multifile/default-arguments/one-module/invalid-magic-literals.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -emit-sil -verify -module-name main -primary-file %s %S/Inputs/invalid-magic-literals-other.swift + +func bar() { + badMagicLiteral() + let _: Int = badGenericMagicLiteral() +} diff --git a/test/multifile/default-arguments/one-module/mismatched-magic-literals.swift b/test/multifile/default-arguments/one-module/mismatched-magic-literals.swift new file mode 100644 index 0000000000000..5fc3406ab8c07 --- /dev/null +++ b/test/multifile/default-arguments/one-module/mismatched-magic-literals.swift @@ -0,0 +1,74 @@ +// RUN: %target-swift-frontend -emit-sil -verify -module-name main -primary-file %s %S/Inputs/mismatched-magic-literals-other.swift + +// +// Basic functionality +// + +// We should warn when we we pass a `#function`-defaulted argument to a +// `#file`-defaulted argument. +func bad(function: String = #function) { + // expected-note@-1 {{did you mean for parameter 'function' to default to '#file'?}} {{29-38=#file}} + callee(file: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'file', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{16-16=(}} {{24-24=)}} +} + +// We should not warn when we pass a `#file`-defaulted argument to a +// `#file`-defaulted argument. +func good(file: String = #file) { + callee(file: file) +} + +// We should not warn when we surround the `#function`-defaulted argument +// with parentheses, which explicitly silences the warning. +func disabled(function: String = #function) { + callee(file: (function)) +} + +// +// Looking through implicit conversions +// +// Same as above, but these lift the argument from `String` to `String?`, so +// the compiler has to look through the implicit conversion. +// + +// We should warn when we we pass a `#function`-defaulted argument to a +// `#file`-defaulted argument. +func optionalBad(function: String = #function) { + // expected-note@-1 {{did you mean for parameter 'function' to default to '#file'?}} {{37-46=#file}} + callee(optFile: function) + // expected-warning@-1 {{parameter 'function' with default argument '#function' passed to parameter 'optFile', whose default argument is '#file'}} + // expected-note@-2 {{add parentheses to silence this warning}} {{19-19=(}} {{27-27=)}} +} + +// We should not warn when we pass a `#file`-defaulted argument to a +// `#file`-defaulted argument. +func optionalGood(file: String = #file) { + callee(optFile: file) +} + +// We should not warn when we surround the `#function`-defaulted argument +// with parentheses, which explicitly silences the warning. +func optionalDisabled(function: String = #function) { + callee(optFile: (function)) +} + +// +// More negative cases +// + +// We should not warn if the caller's parameter has no default. +func explicit(arbitrary: String) { + callee(file: arbitrary) +} + +// We should not warn if the caller's parameter has a non-magic-identifier +// default. +func empty(arbitrary: String = "") { + callee(file: arbitrary) +} + +// We should not warn if the callee's parameter has no default. +func ineligible(function: String = #function) { + callee(arbitrary: function) +} diff --git a/test/multifile/finalize-nested-types.swift b/test/multifile/finalize-nested-types.swift new file mode 100644 index 0000000000000..a8a84ad24526e --- /dev/null +++ b/test/multifile/finalize-nested-types.swift @@ -0,0 +1,8 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/finalize-nested-types-other.swift -module-name other -emit-module-path %t/other.swiftmodule +// RUN: %target-swift-frontend -typecheck %s -I %t + +import other + +let t = Foo.self +let n = t.Nested() diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected new file mode 100644 index 0000000000000..e8eda6da2ee82 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L10-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1: S { +return S() +} + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected new file mode 100644 index 0000000000000..6c0e0427aa9f3 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L11-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2: Int { +return 2 +} + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected new file mode 100644 index 0000000000000..8e04908653313 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L12-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3: Int { +return 5 +} + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected new file mode 100644 index 0000000000000..b85ee87f74713 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L13-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4: Int { +return 4 +} + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected new file mode 100644 index 0000000000000..d909eca08cfc0 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L14-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + var field5: Int { +return 5 +} +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected new file mode 100644 index 0000000000000..87f15cfc7bbfe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L2-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1: Int { +return 2 +} + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected new file mode 100644 index 0000000000000..09b7fb68a5ebe --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L3-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2: String { +return "2" +} + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected new file mode 100644 index 0000000000000..2702ab0a321d1 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L4-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3: String { +return String() +} + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected new file mode 100644 index 0000000000000..2d53bc984c398 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L5-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4: Int { +return 4 +} + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected new file mode 100644 index 0000000000000..f31ba4658f4ce --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/Outputs/basic/L6-3.swift.expected @@ -0,0 +1,28 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! { +return 45 +} +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + + + + + + + + + + + diff --git a/test/refactoring/ConvertToComputedProperty/basic.swift b/test/refactoring/ConvertToComputedProperty/basic.swift new file mode 100644 index 0000000000000..1a9f119290286 --- /dev/null +++ b/test/refactoring/ConvertToComputedProperty/basic.swift @@ -0,0 +1,47 @@ +struct S { + var field1 = 2 + var field2 = "2" + var field3 = String() + static var field4 = 4 + var y: Int! = 45 +} + +class C { + static var field1 = S() + public var field2 = 2 + private dynamic var field3 = 5 + @available(macOS 10.12, *) private static dynamic var field4 = 4 + let field5 = 5 +} + +// RUN: %empty-directory(%t.result) + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=2:3 -end-pos=2:17 > %t.result/L2-3.swift +// RUN: diff -u %S/Outputs/basic/L2-3.swift.expected %t.result/L2-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=3:3 -end-pos=3:19 > %t.result/L3-3.swift +// RUN: diff -u %S/Outputs/basic/L3-3.swift.expected %t.result/L3-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=4:3 -end-pos=4:24 > %t.result/L4-3.swift +// RUN: diff -u %S/Outputs/basic/L4-3.swift.expected %t.result/L4-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=5:3 -end-pos=5:24 > %t.result/L5-3.swift +// RUN: diff -u %S/Outputs/basic/L5-3.swift.expected %t.result/L5-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=6:3 -end-pos=6:19 > %t.result/L6-3.swift +// RUN: diff -u %S/Outputs/basic/L6-3.swift.expected %t.result/L6-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=10:3 -end-pos=10:26 > %t.result/L10-3.swift +// RUN: diff -u %S/Outputs/basic/L10-3.swift.expected %t.result/L10-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=11:3 -end-pos=11:24 > %t.result/L11-3.swift +// RUN: diff -u %S/Outputs/basic/L11-3.swift.expected %t.result/L11-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=12:3 -end-pos=12:33 > %t.result/L12-3.swift +// RUN: diff -u %S/Outputs/basic/L12-3.swift.expected %t.result/L12-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=13:3 -end-pos=13:67 > %t.result/L13-3.swift +// RUN: diff -u %S/Outputs/basic/L13-3.swift.expected %t.result/L13-3.swift + +// RUN: %refactor -convert-to-computed-property -source-filename %s -pos=14:3 -end-pos=14:17 > %t.result/L14-3.swift +// RUN: diff -u %S/Outputs/basic/L14-3.swift.expected %t.result/L14-3.swift diff --git a/test/refactoring/FillStubs/Outputs/basic/P59-12.swift.expected b/test/refactoring/FillStubs/Outputs/basic/P59-12.swift.expected index 1496ffc570e87..91a16be7bf126 100644 --- a/test/refactoring/FillStubs/Outputs/basic/P59-12.swift.expected +++ b/test/refactoring/FillStubs/Outputs/basic/P59-12.swift.expected @@ -61,6 +61,10 @@ extension C12 : P1 { <#code#> } + func foo1() { + <#code#> + } + func foo1() {} } extension C12 : P2 { diff --git a/test/refactoring/RefactoringKind/basic.swift b/test/refactoring/RefactoringKind/basic.swift index 4a54eb2612743..2ec7e032ef8e0 100644 --- a/test/refactoring/RefactoringKind/basic.swift +++ b/test/refactoring/RefactoringKind/basic.swift @@ -275,13 +275,33 @@ func testConvertToIfLetExpr(idxOpt: Int?) { print(idx) } +@propertyWrapper +struct TwelveOrLess { + private var number = 0 + var wrappedValue: Int { + get { return number } + set { number = min(newValue, 12) } + } +} + +struct S { + var field = 2 + let (x, y) = (2, 4) + @TwelveOrLess var height: Int + lazy var z = 42 + var totalSteps: Int = 0 { + willSet(newTotalSteps) { + print("About to set totalSteps to \(newTotalSteps)") + } + } +} // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=3:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=4:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 // RUN: %refactor -source-filename %s -pos=5:1 -end-pos=5:13 | %FileCheck %s -check-prefix=CHECK1 -// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK2 +// RUN: %refactor -source-filename %s -pos=2:1 -end-pos=2:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=3:16 | %FileCheck %s -check-prefix=CHECK2 // RUN: %refactor -source-filename %s -pos=2:1 -end-pos=4:26 | %FileCheck %s -check-prefix=CHECK2 @@ -309,7 +329,7 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // RUN: %refactor -source-filename %s -pos=68:12 | %FileCheck %s -check-prefix=CHECK-RENAME-STUB // RUN: %refactor -source-filename %s -pos=69:8 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY -// RUN: %refactor -source-filename %s -pos=70:12 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY +// RUN: %refactor -source-filename %s -pos=70:12 | %FileCheck %s -check-prefix=CHECK-RENAME-STUB // RUN: %refactor -source-filename %s -pos=74:12 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY // RUN: %refactor -source-filename %s -pos=79:8 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY @@ -321,7 +341,7 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // RUN: %refactor -source-filename %s -pos=91:12 | %FileCheck %s -check-prefix=CHECK-RENAME-STUB // RUN: %refactor -source-filename %s -pos=95:8 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY -// RUN: %refactor -source-filename %s -pos=96:12 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY +// RUN: %refactor -source-filename %s -pos=96:12 | %FileCheck %s -check-prefix=CHECK-RENAME-STUB // RUN: %refactor -source-filename %s -pos=100:12 | %FileCheck %s -check-prefix=CHECK-RENAME-STUB // RUN: %refactor -source-filename %s -pos=104:8 | %FileCheck %s -check-prefix=CHECK-RENAME-ONLY @@ -367,12 +387,16 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // RUN: %refactor -source-filename %s -pos=251:3 -end-pos=251:24 | %FileCheck %s -check-prefix=CHECK-EXPAND-TERNARY-EXPRESSEXPRESSION -// RUN: %refactor -source-filename %s -pos=257:3 -end-pos=262:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-TERNARY-EXPRESSEXPRESSION - // RUN: %refactor -source-filename %s -pos=266:3 -end-pos=268:4 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-GUARD-EXPRESSION // RUN: %refactor -source-filename %s -pos=272:3 -end-pos=275:13 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-IFLET-EXPRESSION +// RUN: %refactor -source-filename %s -pos=288:3 -end-pos=288:16 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY +// RUN: %refactor -source-filename %s -pos=289:3 -end-pos=289:22 | %FileCheck %s -check-prefix=CHECK-NONE +// RUN: %refactor -source-filename %s -pos=290:3 -end-pos=290:32 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=291:3 -end-pos=291:18 | %FileCheck %s -check-prefix=CHECK-CONVERT-TO-COMPUTED-PROPERTY2 +// RUN: %refactor -source-filename %s -pos=292:3 -end-pos=296:4 | %FileCheck %s -check-prefix=CHECK-NONE + // CHECK1: Action begins // CHECK1-NEXT: Extract Method // CHECK1-NEXT: Action ends @@ -422,4 +446,10 @@ func testConvertToIfLetExpr(idxOpt: Int?) { // CHECK-CONVERT-TO-GUARD-EXPRESSION: Convert To Guard Expression -// CHECK-CONVERT-TO-IFLET-EXPRESSION: Convert To IfLet Expression \ No newline at end of file +// CHECK-CONVERT-TO-IFLET-EXPRESSION: Convert To IfLet Expression + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY: Convert To Computed Property + +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2: Action begins +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Move To Extension +// CHECK-CONVERT-TO-COMPUTED-PROPERTY2-NEXT: Action ends \ No newline at end of file diff --git a/test/stdlib/Accelerate.swift b/test/stdlib/Accelerate.swift index e1151026284d0..7211442e50cec 100644 --- a/test/stdlib/Accelerate.swift +++ b/test/stdlib/Accelerate.swift @@ -77,8 +77,9 @@ if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 4.0, *) { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { + let n = 1024 + AccelerateTests.test("vDSP/DiscreteCosineTransform") { - let n = 1024 let source = (0 ..< n).map{ i in return sin(Float(i) * 0.05) + sin(Float(i) * 0.025) @@ -110,18 +111,17 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { source, &legacyDestination) - expectTrue(destination.elementsEqual(legacyDestination)) - expectTrue(destination.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(destination, legacyDestination)) + expectTrue(elementsAlmostEqual(destination, returnedResult)) } } } - + //===----------------------------------------------------------------------===// // // Sliding window summation // //===----------------------------------------------------------------------===// - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { AccelerateTests.test("vDSP/SinglePrecisionSlidingWindowSum") { @@ -135,7 +135,7 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.slidingWindowSum(source, usingWindowLength: 3) - expectTrue(destination.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(destination, returnedResult)) expectTrue(destination.map{ Int($0) }.elementsEqual([23, 31, 24, 19, 12, 15])) } @@ -150,8 +150,7 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.slidingWindowSum(source, usingWindowLength: 3) - expectTrue(destination.elementsEqual(returnedResult)) - + expectTrue(elementsAlmostEqual(destination, returnedResult)) expectTrue(destination.map{ Int($0) }.elementsEqual([23, 31, 24, 19, 12, 15])) } @@ -194,8 +193,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.linearInterpolate(a, b, using: interpolationConstant) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/SinglePrecisionInterpolateBetweenNeighbours") { @@ -230,8 +229,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.linearInterpolate(elementsOf: shortSignal, using: controlVector) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DoublePrecisionInterpolateBetweenVectors") { @@ -261,8 +260,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.linearInterpolate(a, b, using: interpolationConstant) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DoublePrecisionInterpolateBetweenNeighbours") { @@ -297,8 +296,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.linearInterpolate(elementsOf: shortSignal, using: controlVector) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } } @@ -344,8 +343,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { coefficients[3], coefficients[4])) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DifferenceEquationDoublePrecision") { @@ -382,8 +381,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { coefficients[3], coefficients[4])) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } } @@ -430,8 +429,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { decimationFactor: decimationFactor, filter: filter) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/DownsampleDoublePrecision") { @@ -470,8 +469,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { decimationFactor: decimationFactor, filter: filter) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } } @@ -503,8 +502,8 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.evaluatePolynomial(usingCoefficients: coefficients, withVariables: variables) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) } AccelerateTests.test("vDSP/PolynomialEvaluationDoublePrecision") { @@ -527,9 +526,54 @@ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { let returnedResult = vDSP.evaluatePolynomial(usingCoefficients: coefficients, withVariables: variables) - expectTrue(result.elementsEqual(legacyResult)) - expectTrue(result.elementsEqual(returnedResult)) + expectTrue(elementsAlmostEqual(result, legacyResult)) + expectTrue(elementsAlmostEqual(result, returnedResult)) + } +} + +//===----------------------------------------------------------------------===// +// +// Array almost equal. +// +//===----------------------------------------------------------------------===// + +func elementsAlmostEqual(_ lhs: [T], _ rhs: [T]) -> Bool { + var returnValue = true + zip(lhs, rhs).forEach { + if !isAlmostEqual($0.0, $0.1) { + returnValue = false + return } + } + return returnValue +} + +func isAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T = T.ulpOfOne.squareRoot()) -> Bool { + assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") + guard lhs.isFinite && rhs.isFinite else { + return rescaledAlmostEqual(lhs, rhs, tolerance: tolerance) + } + let scale = max(abs(lhs), abs(rhs), .leastNormalMagnitude) + return abs(lhs - rhs) < scale*tolerance +} + +func rescaledAlmostEqual(_ lhs: T, + _ rhs: T, + tolerance: T) -> Bool { + if lhs.isNaN || rhs.isNaN { return false } + if lhs.isInfinite { + if rhs.isInfinite { return lhs == rhs } + let scaledLhs = T(sign: lhs.sign, + exponent: T.greatestFiniteMagnitude.exponent, + significand: 1) + let scaledRhs = T(sign: .plus, + exponent: -1, + significand: rhs) + return isAlmostEqual(scaledLhs, scaledRhs, tolerance: tolerance) + } + return rescaledAlmostEqual(rhs, lhs, tolerance: tolerance) } runAllTests() diff --git a/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics.swift b/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics.swift new file mode 100644 index 0000000000000..bff4c63d07714 --- /dev/null +++ b/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics.swift @@ -0,0 +1,10 @@ +// RUN: %target-typecheck-verify-swift -enable-invalid-ephemeralness-as-error +// REQUIRES: objc_interop + +func unsafePointerInitEphemeralConversions() { + class C {} + var c: C? + + _ = AutoreleasingUnsafeMutablePointer(&c) // expected-error {{initialization of 'AutoreleasingUnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'C?' to 'AutoreleasingUnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} +} diff --git a/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics_warning.swift b/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics_warning.swift new file mode 100644 index 0000000000000..180b2a41c650a --- /dev/null +++ b/test/stdlib/AutoreleasingUnsafeMutablePointerDiagnostics_warning.swift @@ -0,0 +1,10 @@ +// RUN: %target-typecheck-verify-swift +// REQUIRES: objc_interop + +func unsafePointerInitEphemeralConversions() { + class C {} + var c: C? + + _ = AutoreleasingUnsafeMutablePointer(&c) // expected-warning {{initialization of 'AutoreleasingUnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'C?' to 'AutoreleasingUnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} +} diff --git a/test/stdlib/BinaryIntegerRequirements.swift b/test/stdlib/BinaryIntegerRequirements.swift index b76df84a46eab..40373e86cb6af 100644 --- a/test/stdlib/BinaryIntegerRequirements.swift +++ b/test/stdlib/BinaryIntegerRequirements.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -swift-version 4 -struct MyInt: FixedWidthInteger { // expected-error {{type 'MyInt' does not conform to protocol 'BinaryInteger'}} expected-note 3 {{do you want to add protocol stubs?}} +struct MyInt: FixedWidthInteger { // expected-error {{type 'MyInt' does not conform to protocol 'BinaryInteger'}} typealias IntegerLiteralType = Int static let isSigned = false init(integerLiteral value: Int) { fatalError() } diff --git a/test/stdlib/BridgeIdAsAny.swift.gyb b/test/stdlib/BridgeIdAsAny.swift.gyb index 67e76f876dcac..e0433b026d65c 100644 --- a/test/stdlib/BridgeIdAsAny.swift.gyb +++ b/test/stdlib/BridgeIdAsAny.swift.gyb @@ -196,6 +196,11 @@ testCases = [ ] }% +/// Whether this can be safely casted to NSObject +func isNSObject(_ value: T) -> Bool { + return (value is NSObject) && !(value is LifetimeTracked) +} + % for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases: BridgeAnything.test("${testName}") { autoreleasepool { @@ -207,7 +212,7 @@ BridgeAnything.test("${testName}") { let xInArray = [x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) } @@ -216,7 +221,7 @@ BridgeAnything.test("${testName}") { let xInDictValue = ["key" : x] ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!) } @@ -228,7 +233,7 @@ BridgeAnything.test("${testName}") { // The NSObject version below can't test class LifetimeTracked. // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!) // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!) - if (x as? NSObject) != nil { + if isNSObject(x) { ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!) ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!) } diff --git a/test/stdlib/Casts.swift b/test/stdlib/Casts.swift index 8300c0b8c5aea..abab9b680b4ac 100644 --- a/test/stdlib/Casts.swift +++ b/test/stdlib/Casts.swift @@ -80,6 +80,48 @@ CastsTests.test("Optional.none can be casted to Optional.none in generic c expectEqual(type(of: test(Bool?.self)), Bool??.self) } +// Test for SR-3871: Cannot cast from ObjC existential without going through AnyObject +#if _runtime(_ObjC) +protocol P2 {} +CastsTests.test("Cast from ObjC existential to Protocol (SR-3871)") { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + struct S: P2 {} + + class ObjCWrapper { + @objc dynamic let any: Any = S() + init() {} + } + let a = ObjCWrapper().any + expectTrue(a is P2) + // In SR-3871, the following cast failed (everything else here succeeded) + expectNotNil(a as? P2) + expectNotNil(a as? S) + let b = a as AnyObject + expectTrue(a is P2) + expectNotNil(b as? P2) + expectNotNil(b as? S) + } +} +#endif + +protocol P3 {} +CastsTests.test("Cast from Swift existential to Protocol") { + struct S: P3 {} + class SwiftWrapper { + let any: Any = S() + init() {} + } + let a = SwiftWrapper().any + expectTrue(a is P3) + expectNotNil(a as? P3) + expectNotNil(a as? S) + let b = a as AnyObject + expectTrue(b is P3) + expectNotNil(b as? P3) + expectNotNil(b as? S) +} + + #if _runtime(_ObjC) extension CFBitVector : P { static func makeImmutable(from values: Array) -> CFBitVector { diff --git a/test/stdlib/Error.swift b/test/stdlib/Error.swift index 6aabba38efa17..1d095e80c9100 100644 --- a/test/stdlib/Error.swift +++ b/test/stdlib/Error.swift @@ -191,5 +191,30 @@ ErrorTests.test("test dealloc empty error box") { } } +var errors: [Error] = [] +ErrorTests.test("willThrow") { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + // Error isn't allowed in a @convention(c) function when ObjC interop is + // not available, so pass it through an OpaquePointer. + typealias WillThrow = @convention(c) (OpaquePointer) -> Void + let willThrow = pointerToSwiftCoreSymbol(name: "_swift_willThrow")! + let callback: WillThrow = { + errors.append(unsafeBitCast($0, to: Error.self)) + } + willThrow.storeBytes(of: callback, as: WillThrow.self) + expectTrue(errors.isEmpty) + do { + throw UnsignedError.negativeOne + } catch {} + expectEqual(UnsignedError.self, type(of: errors.last!)) + + do { + throw SillyError.JazzHands + } catch {} + expectEqual(2, errors.count) + expectEqual(SillyError.self, type(of: errors.last!)) + } +} + runAllTests() diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift index 958ed7c896cbc..c1d5feb9aa957 100644 --- a/test/stdlib/ErrorBridged.swift +++ b/test/stdlib/ErrorBridged.swift @@ -684,9 +684,9 @@ ErrorBridgingTests.test("Wrapped NSError identity") { } extension Error { - func asNSError() -> NSError { - return self as NSError - } + func asNSError() -> NSError { + return self as NSError + } } func unconditionalCast(_ x: Any, to: T.Type) -> T { @@ -767,4 +767,91 @@ ErrorBridgingTests.test("@objc error domains for nested types") { String(reflecting: NonPrintAsObjCError.self)) } +ErrorBridgingTests.test("error-to-NSObject casts") { + let error = MyCustomizedError(code: 12345) + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + // Unconditional cast + let nsErrorAsObject1 = unconditionalCast(error, to: NSObject.self) + let nsError1 = unconditionalCast(nsErrorAsObject1, to: NSError.self) + expectEqual("custom", nsError1.domain) + expectEqual(12345, nsError1.code) + + // Conditional cast + let nsErrorAsObject2 = conditionalCast(error, to: NSObject.self)! + let nsError2 = unconditionalCast(nsErrorAsObject2, to: NSError.self) + expectEqual("custom", nsError2.domain) + expectEqual(12345, nsError2.code) + + // "is" check + expectTrue(error is NSObject) + + // Unconditional cast to a dictionary. + let dict = ["key" : NoisyError()] + let anyOfDict = dict as AnyObject + let dict2 = anyOfDict as! [String: NSObject] + } +} + +// SR-7732: Casting CFError or NSError to Error results in a memory leak +ErrorBridgingTests.test("NSError-to-Error casts") { + func should_not_leak_nserror() { + let something: Any? = NSError(domain: "Foo", code: 1) + expectTrue(something is Error) + } + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + // TODO: Wrap some leak checking around this + // Until then, this is a helpful debug tool + should_not_leak_nserror() + } +} + +ErrorBridgingTests.test("CFError-to-Error casts") { + func should_not_leak_cferror() { + let something: Any? = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCocoa, 1, [:] as CFDictionary) + expectTrue(something is Error) + } + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + // TODO: Wrap some leak checking around this + // Until then, this is a helpful debug tool + should_not_leak_cferror() + } +} + +enum MyError: Error { + case someThing +} + +ErrorBridgingTests.test("SR-9207 crash in failed cast to NSError") { + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + let error = MyError.someThing + let foundationError = error as NSError + + if let urlError = foundationError as? URLError { + expectUnreachable() + } + } +} + +// SR-7652 + +enum SwiftError: Error, CustomStringConvertible { + case something + var description: String { return "Something" } +} + +ErrorBridgingTests.test("Swift Error bridged to NSError description") { + func checkDescription() { + let bridgedError = SwiftError.something as NSError + expectEqual("Something", bridgedError.description) + } + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + checkDescription() + } +} + runAllTests() diff --git a/test/stdlib/Filter.swift b/test/stdlib/Filter.swift index acadd48a53f87..ca558c179bc77 100644 --- a/test/stdlib/Filter.swift +++ b/test/stdlib/Filter.swift @@ -73,4 +73,26 @@ FilterTests.test("single-count") { expectEqual(30, count) } +FilterTests.test("chained filter order") { + let array = [1] + + let lazyFilter = array.lazy + .filter { _ in false } + .filter { _ in + expectUnreachable("Executed second filter before first") + return true + } + let lazyResult = Array(lazyFilter) + + let result = array + .filter { _ in false } + .filter { _ in + expectUnreachable("Executed second filter before first") + return true + } + + expectEqual(lazyResult.count, 0) + expectEqual(result.count, 0) +} + runAllTests() diff --git a/test/stdlib/Inputs/CommonArrayTests.gyb b/test/stdlib/Inputs/CommonArrayTests.gyb index bd9d22118b165..20924efb2f138 100644 --- a/test/stdlib/Inputs/CommonArrayTests.gyb +++ b/test/stdlib/Inputs/CommonArrayTests.gyb @@ -106,12 +106,36 @@ ${Suite}.test("${ArrayType}/appendNonUnique") var x: ${ArrayType} = [] x.reserveCapacity(10002) let capacity = x.capacity - for _ in 1...10000 { + for _ in 1...100 { let y = x x.append(1) expectTrue(x.capacity == capacity) - let z = x - x.remove(at: 0) + } +} + +% if ArrayType != 'ArraySlice': +${Suite}.test("${ArrayType}/removeNonUnique") + .code { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x.remove(at: 0) + expectTrue(x.capacity < 1000) + } + } +} +% end + +${Suite}.test("${ArrayType}/mutateNonUnique") + .code { + var x = ${ArrayType}(repeating: 27, count: 200) + x.reserveCapacity(10002) + for _ in 1...100 { + let y = x + x[0] = 0 + expectTrue(x.capacity < 1000) } } diff --git a/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m b/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m index dc97c4221c718..700756926f15c 100644 --- a/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m +++ b/test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m @@ -412,7 +412,6 @@ void TestSwiftObjectNSObject(id c, id d) expectTrue ([[c description] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[D description] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C description] isEqual:@"SwiftObjectNSObject.C"]); - expectTrue ([[S description] isEqual:@(SwiftObjectDemangledName)]); expectTrue ([[D_meta description] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C_meta description] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[S_meta description] isEqual:@(SwiftObjectDemangledName)]); @@ -430,7 +429,6 @@ void TestSwiftObjectNSObject(id c, id d) expectTrue ([[c debugDescription] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[D debugDescription] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C debugDescription] isEqual:@"SwiftObjectNSObject.C"]); - expectTrue ([[S debugDescription] isEqual:@(SwiftObjectDemangledName)]); expectTrue ([[D_meta debugDescription] isEqual:@"SwiftObjectNSObject.D"]); expectTrue ([[C_meta debugDescription] isEqual:@"SwiftObjectNSObject.C"]); expectTrue ([[S_meta debugDescription] isEqual:@(SwiftObjectDemangledName)]); diff --git a/test/stdlib/Inputs/tail_allocated_c_array.h b/test/stdlib/Inputs/tail_allocated_c_array.h new file mode 100644 index 0000000000000..b68e933954c74 --- /dev/null +++ b/test/stdlib/Inputs/tail_allocated_c_array.h @@ -0,0 +1,7 @@ +#include + +typedef struct foo { + uint8_t a; + uint16_t b; + uint8_t tailallocatedarray[0]; +} foo; diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index 5593859b72b64..1b4a1270a8a38 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -swift-version 5 -g %s -o %t/a.out +// RUN: %target-build-swift -import-objc-header %S/Inputs/tail_allocated_c_array.h -swift-version 5 -g %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test @@ -1019,5 +1019,10 @@ keyPath.test("nested generics") { expectTrue(nestedKeyPath is KeyPath) } +keyPath.test("tail allocated c array") { + let offset = MemoryLayout.offset(of: \foo.tailallocatedarray)! + expectEqual(4, offset) +} + runAllTests() diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index b2b09fe97a1f2..d3075ca6be337 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -509,6 +509,234 @@ mirrors.test("struct/WrapNSArray") { #endif // _runtime(_ObjC) +//===--- Weak and Unowned References --------------------------------------===// + +// Check that Mirror correctly reflects weak/unowned refs to both +// Swift and ObjC objects from Swift structs and classes. + +protocol WeakUnownedTestsP1: class { + func f1() -> Int +} + +protocol WeakUnownedTestsP2 { + func f2() -> String +} + +class WeakUnownedSwiftClass: WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} + +#if _runtime(_ObjC) +@objc class WeakUnownedObjCClass: NSObject, WeakUnownedTestsP1, WeakUnownedTestsP2 { + let tracker = LifetimeTracked(0) + func f1() -> Int { return 2 } + func f2() -> String { return "b" } +} +#endif + +// The four tests below populate objects with different types +// but identical overall structure. +// This function is used by all four to verify that the resulting +// Mirror objects have the expected entries. +func verifyWeakUnownedReflection + + (_ m: Mirror, expectedClass: ExpectedClass.Type ) +{ + let i = m.children.makeIterator() + + func verifyClassField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + } + + func verifyExistentialField(child: (label: String?, value: Any), name: String) { + expectEqual(child.label, name) + expectNotNil(child.value) + + // FIXME: These casts are currently broken (Dec 2019) + // Once they are fixed, enable additional checks: + //let vp1 = child.value as? WeakUnownedTestsP1 + //expectNotNil(vp1) + //expectEqual(vp1!.f1(), 2) + //let vp2 = child.value as? WeakUnownedTestsP2 + //expectNotNil(vp2) + //expectEqual(vp2!.f2(), "b") + + let v = child.value as? ExpectedClass + expectNotNil(v) + expectEqual(v!.f1(), 2) + let m = Mirror(reflecting: v!) + expectEqual(m.displayStyle, .`class`) + // TODO: Find a way to verify that the existential wrapper carries + // the expected protocol witnesses. The current Swift runtime does + // a very good job of hiding this from users. + } + + verifyClassField(child: i.next()!, name: "strong_class") + verifyExistentialField(child: i.next()!, name: "strong_existential") + verifyClassField(child: i.next()!, name: "weak_class") + verifyExistentialField(child: i.next()!, name: "weak_existential") + verifyClassField(child: i.next()!, name: "unowned_safe_class") + verifyExistentialField(child: i.next()!, name: "unowned_safe_existential") + + verifyClassField(child: i.next()!, name: "unowned_unsafe_class") + verifyExistentialField(child: i.next()!, name: "unowned_unsafe_existential") + expectNil(i.next()) + + // The original bug report from SR-5289 crashed when the print() code + // attempted to reflect the contents of an unowned field. + // The tests above _should_ suffice to check this, but let's print everything + // anyway just to be sure. + for c in m.children { + print(c.label ?? "?", c.value) + } +} + +#if _runtime(_ObjC) +// Related: SR-5289 reported a crash when using Mirror to inspect Swift +// class objects containing unowned pointers to Obj-C class objects. +mirrors.test("Weak and Unowned Obj-C refs in class (SR-5289)") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(objc) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +mirrors.test("Weak and Unowned Obj-C refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedObjCClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedObjCClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedObjCClass + unowned(safe) let unowned_safe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + unowned(unsafe) let unowned_unsafe_class: WeakUnownedObjCClass + unowned(unsafe) let unowned_unsafe_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + + init(_ objc: WeakUnownedObjCClass) { + self.strong_class = objc + self.strong_existential = objc + self.weak_class = objc + self.weak_existential = objc + self.unowned_safe_class = objc + self.unowned_safe_existential = objc + self.unowned_unsafe_class = objc + self.unowned_unsafe_existential = objc + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let objc = WeakUnownedObjCClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(objc) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedObjCClass.self) + } +} + +#endif + +mirrors.test("Weak and Unowned Swift refs in class") { + class SwiftClassWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let classWithReferences = SwiftClassWithWeakAndUnowned(swift) + let m = Mirror(reflecting: classWithReferences) + expectEqual(m.displayStyle, .`class`) + expectEqual(m.description, "Mirror for SwiftClassWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftClassWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + +mirrors.test("Weak and Unowned Swift refs in struct") { + struct SwiftStructWithWeakAndUnowned { + var strong_class: WeakUnownedSwiftClass + var strong_existential: WeakUnownedTestsP1 & WeakUnownedTestsP2 + weak var weak_class: WeakUnownedSwiftClass? + weak var weak_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2)? + unowned(safe) let unowned_safe_class: WeakUnownedSwiftClass + unowned(safe) let unowned_safe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + unowned(unsafe) let unowned_unsafe_class: WeakUnownedSwiftClass + unowned(unsafe) let unowned_unsafe_existential: (WeakUnownedTestsP1 & WeakUnownedTestsP2) + + init(_ swift: WeakUnownedSwiftClass) { + self.strong_class = swift + self.strong_existential = swift + self.weak_class = swift + self.weak_existential = swift + self.unowned_safe_class = swift + self.unowned_safe_existential = swift + self.unowned_unsafe_class = swift + self.unowned_unsafe_existential = swift + } + } + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + let swift = WeakUnownedSwiftClass() + let structWithReferences = SwiftStructWithWeakAndUnowned(swift) + let m = Mirror(reflecting: structWithReferences) + expectEqual(m.displayStyle, .`struct`) + expectEqual(m.description, "Mirror for SwiftStructWithWeakAndUnowned") + expectEqual(m.subjectType, SwiftStructWithWeakAndUnowned.self) + verifyWeakUnownedReflection(m, expectedClass: WeakUnownedSwiftClass.self) + } +} + //===--- Suppressed Superclass Mirrors ------------------------------------===// mirrors.test("Class/Root/NoSuperclassMirror") { class B : CustomReflectable { @@ -854,7 +1082,7 @@ struct GenericStructWithDefaultMirror { mirrors.test("Struct/Generic/DefaultMirror") { do { - var value = GenericStructWithDefaultMirror( + let value = GenericStructWithDefaultMirror( first: 123, second: ["abc", 456, 789.25]) var output = "" @@ -1616,7 +1844,7 @@ mirrors.test("Float") { } do { - var input: Float = 42.125 + let input: Float = 42.125 var output = "" dump(input, to: &output) @@ -1649,7 +1877,7 @@ mirrors.test("Double") { } do { - var input: Double = 42.125 + let input: Double = 42.125 var output = "" dump(input, to: &output) @@ -1745,9 +1973,9 @@ mirrors.test("FieldNamesBug") { } mirrors.test("MirrorMirror") { - var object = 1 - var mirror = Mirror(reflecting: object) - var mirrorMirror = Mirror(reflecting: mirror) + let object = 1 + let mirror = Mirror(reflecting: object) + let mirrorMirror = Mirror(reflecting: mirror) expectEqual(0, mirrorMirror.children.count) } @@ -1755,7 +1983,7 @@ mirrors.test("MirrorMirror") { mirrors.test("OpaquePointer/null") { // Don't crash on null pointers. rdar://problem/19708338 let pointer: OpaquePointer? = nil - let mirror = Mirror(reflecting: pointer) + let mirror = Mirror(reflecting: pointer as Any) expectEqual(0, mirror.children.count) } diff --git a/test/stdlib/NSSetAPI.swift b/test/stdlib/NSSetAPI.swift index 182d976d6d27b..39fdfc64ac5fb 100644 --- a/test/stdlib/NSSetAPI.swift +++ b/test/stdlib/NSSetAPI.swift @@ -67,6 +67,12 @@ NSSetAPI.test("AnyHashable containing NSSet that contains an NSSet") { } } +NSSetAPI.test("Incorrectly constructed Set for backwards compatibility") { + let array:NSArray = [NSObject()] as NSArray + let wrongSet = Set(_immutableCocoaSet: array) + print(wrongSet.startIndex) +} + var NSOrderedSetAPI = TestSuite("NSOrderedSetAPI") NSOrderedSetAPI.test("Sequence") { diff --git a/test/stdlib/OSLogPrototypeExecTest.swift b/test/stdlib/OSLogPrototypeExecTest.swift index 9e5de5ec665ca..2bebd07ee5533 100644 --- a/test/stdlib/OSLogPrototypeExecTest.swift +++ b/test/stdlib/OSLogPrototypeExecTest.swift @@ -10,6 +10,7 @@ import OSLogPrototype import StdlibUnittest +import Foundation defer { runAllTests() } @@ -25,23 +26,18 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { h.log("A message with no data") // Test logging at specific levels. - h.log(level: .debug, "Minimum integer value: \(Int.min, format: .hex)") - h.log(level: .info, "Maximum integer value: \(Int.max, format: .hex)") + h.debug("Minimum integer value: \(Int.min, format: .hex)") + h.info("Maximum integer value: \(Int.max, format: .hex)") let privateID = 0x79abcdef - h.log( - level: .error, - "Private Identifier: \(privateID, format: .hex, privacy: .private)") + h.error("Private Identifier: \(privateID, format: .hex, privacy: .private)") let addr = 0x7afebabe - h.log( - level: .fault, - "Invalid address: 0x\(addr, format: .hex, privacy: .public)") + h.fault("Invalid address: 0x\(addr, format: .hex, privacy: .public)") // Test logging with multiple arguments. let filePermissions = 0o777 let pid = 122225 - h.log( - level: .error, + h.error( """ Access prevented: process \(pid) initiated by \ user: \(privateID, privacy: .private) attempted resetting \ @@ -129,6 +125,22 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { let interpolatedString = "\(31) trillion digits of pi are known so far" h.log("\(interpolatedString)") } + + OSLogTestSuite.test("NSObject") { + let h = Logger() + + let smallNSString: NSString = "a" + h.log("A small string: \(smallNSString, privacy: .public)") + + let largeNSString: NSString = "This is a large String" + h.log("\(largeNSString, privacy: .public)") + + let nsArray: NSArray = [0, 1, 2] + h.log("NS Array: \(nsArray, privacy: .public)") + + let nsDictionary: NSDictionary = [1 : ""] + h.log("NS Dictionary: \(nsDictionary, privacy: .public)") + } } // The following tests check the correctness of the format string and the @@ -196,85 +208,112 @@ internal struct OSLogBufferChecker { // TODO: include wide string and errno here if needed. } - /// Check the encoding of an argument in the byte buffer starting from the - /// `startIndex`. - /// - precondition: `T` must be a type that is accepted by os_log ABI. - private func checkArgument( + /// Check the encoding of an argument headers in the byte buffer starting from + /// the `startIndex` and return argument bytes. + private func checkArgumentHeadersAndGetBytes( startIndex: Int, size: UInt8, flag: ArgumentFlag, - type: ArgumentType, - expectedData: T - ) { + type: ArgumentType + ) -> [UInt8] { let argumentHeader = buffer[startIndex] expectEqual((type.rawValue << 4) | flag.rawValue, argumentHeader) - expectEqual(size, buffer[startIndex + 1]) + // Argument data starts after the two header bytes. + let argumentBytes: [UInt8] = + (0..( + startIndex: Int, + flag: ArgumentFlag, + expectedInt: T + ) where T : FixedWidthInteger { + let byteSize = UInt8(MemoryLayout.size) + let argumentBytes = + checkArgumentHeadersAndGetBytes( + startIndex: startIndex, + size: byteSize, + flag: flag, + type: .scalar) + withUnsafeBytes(of: expectedInt) { expectedBytes in + for i in 0..>.size) + let argumentBytes = + checkArgumentHeadersAndGetBytes( + startIndex: startIndex, + size: pointerSize, + flag: flag, + type: .string) // Read the pointer to a string stored in the buffer and compare it with - // the expected string using `strcmp`. Note that it is important we use a C - // function here to compare the string as it more closely represents the C - // os_log functions. + // the expected string using `strcmp`. Note that it is important we use a + // C function here to compare the string as it more closely represents + // the C os_log functions. var stringAddress: Int = 0 // Copy the bytes of the address byte by byte. Note that // RawPointer.load(fromByteOffset:,_) function cannot be used here as the // address: `buffer + offset` is not aligned for reading an Int. - for i in 0..(bitPattern: stringAddress) - (expectedData as! String).withCString { + expectedString.withCString { let compareResult = strcmp($0, bufferDataPointer) expectEqual(0, compareResult, "strcmp returned \(compareResult)") } } /// Check whether the bytes starting from `startIndex` contain the encoding - /// for an Int. - internal func checkInt( + /// for an NSObject. + internal func checkNSObject( startIndex: Int, flag: ArgumentFlag, - expectedInt: T - ) where T : FixedWidthInteger { - checkArgument( - startIndex: startIndex, - size: UInt8(MemoryLayout.size), - flag: flag, - type: .scalar, - expectedData: expectedInt) - } - - /// Check whether the bytes starting from `startIndex` contain the encoding - /// for a string. - internal func checkString( - startIndex: Int, - flag: ArgumentFlag, - expectedString: String + expectedObject: NSObject ) { - checkArgument( - startIndex: startIndex, - size: UInt8(MemoryLayout>.size), - flag: flag, - type: .string, - expectedData: expectedString) + let pointerSize = UInt8(MemoryLayout>.size) + let argumentBytes = + checkArgumentHeadersAndGetBytes( + startIndex: startIndex, + size: pointerSize, + flag: flag, + type: .object) + // Convert data to a pointer and check if the addresses stored in the + // pointer and the one in the buffer match. + let objectAddress = + Unmanaged + .passUnretained(expectedObject) + .toOpaque() + withUnsafeBytes(of: objectAddress) { expectedBytes in + for i in 0..(_ subject: T?) -> String { + return "" +} + +protocol TestProto { +} + +InterpolationTestSuite.test("Interpolation of complex expressions") { + class TestClass: NSObject { + func testFunction() { + // The following call should no crash. + _checkFormatStringAndBuffer("A complex expression \(toString(self))") { + (formatString, _) in + expectEqual("A complex expression %s", formatString) + } + } } } diff --git a/test/stdlib/OptionSetTest.swift b/test/stdlib/OptionSetTest.swift index 306e67295b4ea..05fea8eaebe14 100644 --- a/test/stdlib/OptionSetTest.swift +++ b/test/stdlib/OptionSetTest.swift @@ -12,78 +12,111 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +import StdlibUnittest + struct PackagingOptions : OptionSet { let rawValue: Int init(rawValue: Int) { self.rawValue = rawValue } - static let - Box = PackagingOptions(rawValue: 1), - Carton = PackagingOptions(rawValue: 2), - Bag = PackagingOptions(rawValue: 4), - Satchel = PackagingOptions(rawValue: 8) + static let box = PackagingOptions(rawValue: 1) + static let carton = PackagingOptions(rawValue: 2) + static let bag = PackagingOptions(rawValue: 4) + static let satchel = PackagingOptions(rawValue: 8) - // FIXME: these must be separate decls because of - static let BoxOrBag: PackagingOptions = [Box, Bag] - static let BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag] - static let SatchelOrBag = Satchel.union(Bag) + static let boxOrBag: PackagingOptions = [box, bag] + static let boxOrCartonOrBag: PackagingOptions = [box, carton, bag] + static let satchelOrBag = satchel.union(bag) } -import StdlibUnittest - - var tests = TestSuite("OptionSet") +defer { runAllTests() } tests.test("basics") { typealias P = PackagingOptions - expectNotEqual(P(), .Box) - expectEqual(P.Box, .Box) - expectNotEqual(P.Box, .Carton) - expectNotEqual(P.Box, .BoxOrBag) - - expectEqual(.Box, P.Box.intersection(.BoxOrBag)) - expectEqual(.Bag, P.Bag.intersection(.BoxOrBag)) - expectEqual(P(), P.Bag.intersection(.Box)) - expectEqual(P(), P.Box.intersection(.Satchel)) - expectEqual(.BoxOrBag, P.Bag.union(.Box)) - expectEqual(.BoxOrBag, P.Box.union(.Bag)) - expectEqual(.BoxOrCartonOrBag, P.BoxOrBag.union(.Carton)) - expectEqual([.Satchel, .Box], P.SatchelOrBag.symmetricDifference(.BoxOrBag)) - - var p = P.Box - p.formIntersection(.BoxOrBag) - expectEqual(.Box, p) - - p = .Bag - p.formIntersection(.BoxOrBag) - expectEqual(.Bag, p) - - p = .Bag - p.formIntersection(.Box) + expectNotEqual(P(), .box) + expectEqual(P.box, .box) + expectNotEqual(P.box, .carton) + expectNotEqual(P.box, .boxOrBag) + + expectEqual(.box, P.box.intersection(.boxOrBag)) + expectEqual(.bag, P.bag.intersection(.boxOrBag)) + expectEqual(P(), P.bag.intersection(.box)) + expectEqual(P(), P.box.intersection(.satchel)) + expectEqual(.boxOrBag, P.bag.union(.box)) + expectEqual(.boxOrBag, P.box.union(.bag)) + expectEqual(.boxOrCartonOrBag, P.boxOrBag.union(.carton)) + expectEqual([.satchel, .box], P.satchelOrBag.symmetricDifference(.boxOrBag)) + + var p = P.box + p.formIntersection(.boxOrBag) + expectEqual(.box, p) + + p = .bag + p.formIntersection(.boxOrBag) + expectEqual(.bag, p) + + p = .bag + p.formIntersection(.box) expectEqual(P(), p) - - p = .Box - p.formIntersection(.Satchel) + + p = .box + p.formIntersection(.satchel) expectEqual(P(), p) - - p = .Bag - p.formUnion(.Box) - expectEqual(.BoxOrBag, p) - - p = .Box - p.formUnion(.Bag) - expectEqual(.BoxOrBag, p) - - p = .BoxOrBag - p.formUnion(.Carton) - expectEqual(.BoxOrCartonOrBag, p) - - p = .SatchelOrBag - p.formSymmetricDifference(.BoxOrBag) - expectEqual([.Satchel, .Box], p) + + p = .bag + p.formUnion(.box) + expectEqual(.boxOrBag, p) + + p = .box + p.formUnion(.bag) + expectEqual(.boxOrBag, p) + + p = .boxOrBag + p.formUnion(.carton) + expectEqual(.boxOrCartonOrBag, p) + + p = .satchelOrBag + p.formSymmetricDifference(.boxOrBag) + expectEqual([.satchel, .box], p) } -// FIXME: add tests for all of SetAlgebra, in particular -// insert/remove/replace. +tests.test("set algebra") { + typealias P = PackagingOptions + + // remove + var p = P.boxOrBag + expectNil(p.remove(P.carton)) + + p = P.boxOrBag + p.remove(P.boxOrCartonOrBag) + expectEqual(P(), p) -runAllTests() + p = P.boxOrBag + let removed = p.remove(P.satchelOrBag) + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + // https://github.com/apple/swift/pull/28378 + expectEqual(P.bag, removed) + } + expectEqual(P.box, p) + + // insert + p = P.box + var insertionResult = p.insert(.bag) + expectTrue(insertionResult.inserted) + expectEqual(P.bag, insertionResult.memberAfterInsert) + expectEqual(P.boxOrBag, p) + + insertionResult = p.insert(.bag) + expectFalse(insertionResult.inserted) + expectEqual(P.bag, insertionResult.memberAfterInsert) + + // update + p = P.box + expectNil(p.update(with: .bag)) + expectEqual(P.boxOrBag, p) + + p = P.box + expectEqual(P.box, p.update(with: .boxOrBag)) + expectEqual(P.boxOrBag, p) +} diff --git a/test/stdlib/PrintDiagnostics.swift b/test/stdlib/PrintDiagnostics.swift index e79d9b3ff18b1..11eb7301c3381 100644 --- a/test/stdlib/PrintDiagnostics.swift +++ b/test/stdlib/PrintDiagnostics.swift @@ -5,11 +5,10 @@ var stream = "" print(3, &stream) // expected-error{{'&' used with non-inout argument of type 'Any'}} debugPrint(3, &stream) // expected-error{{'&' used with non-inout argument of type 'Any'}} -print(3, &stream, appendNewline: false) // expected-error {{cannot invoke 'print' with an argument list of type '(Int, inout String, appendNewline: Bool)'}} -// expected-note@-1 {{overloads for 'print' exist with these partially matching parameter lists: (Any..., separator: String, terminator: String), (Any..., separator: String, terminator: String, to: inout Target)}} +print(3, &stream, appendNewline: false) // expected-error {{extra argument 'appendNewline' in call}} +// expected-error@-1:10 {{'&' used with non-inout argument of type 'Any'}} -debugPrint(3, &stream, appendNewline: false) // expected-error {{cannot invoke 'debugPrint' with an argument list of type '(Int, inout String, appendNewline: Bool)'}} -// expected-note@-1 {{verloads for 'debugPrint' exist with these partially matching parameter lists: (Any..., separator: String, terminator: String), (Any..., separator: String, terminator: String, to: inout Target)}} +debugPrint(3, &stream, appendNewline: false) // expected-error {{extra argument 'appendNewline' in call}} +// expected-error@-1:15 {{'&' used with non-inout argument of type 'Any'}} -print(4, quack: 5) // expected-error {{cannot invoke 'print' with an argument list of type '(Int, quack: Int)'}} -// expected-note@-1 {{overloads for 'print' exist with these partially matching parameter lists: (Any..., separator: String, terminator: String), (Any..., separator: String, terminator: String, to: inout Target)}} +print(4, quack: 5) // expected-error {{extra argument 'quack' in call}} diff --git a/test/stdlib/PrintFloat.swift.gyb b/test/stdlib/PrintFloat.swift.gyb index 94d35d00c0ccb..ce798ba623021 100644 --- a/test/stdlib/PrintFloat.swift.gyb +++ b/test/stdlib/PrintFloat.swift.gyb @@ -6,6 +6,9 @@ // RUN: %line-directive %t/FloatingPointPrinting.swift -- %target-run %t/main.out --locale ru_RU.UTF-8 // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import Darwin diff --git a/test/stdlib/Runtime.swift.gyb b/test/stdlib/Runtime.swift.gyb index d3357ed01318b..38db24b6f1994 100644 --- a/test/stdlib/Runtime.swift.gyb +++ b/test/stdlib/Runtime.swift.gyb @@ -592,39 +592,9 @@ Runtime.test("Struct layout with reference storage types") { } Runtime.test("SwiftError layout constants for LLDB") { -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2) -#elseif os(Linux) - let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0) -#elseif os(Android) - #if arch(arm) - let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0xffffffff as UInt) - #elseif arch(arm64) - let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: 0) - #else - _UnimplementedError() - #endif -#elseif os(Windows) - let hStdlibCore: HMODULE = GetModuleHandleA("swiftCore.dll")! -#else - _UnimplementedError() -#endif + let offsetof_SwiftError_typeMetadata = pointerToSwiftCoreSymbol(name: "_swift_lldb_offsetof_SwiftError_typeMetadata")! + let sizeof_SwiftError = pointerToSwiftCoreSymbol(name: "_swift_lldb_sizeof_SwiftError")! -#if os(Windows) - let offsetof_SwiftError_typeMetadata: UnsafeRawPointer = - unsafeBitCast(GetProcAddress(hStdlibCore, - "_swift_lldb_offsetof_SwiftError_typeMetadata")!, - to: UnsafeRawPointer.self) - let sizeof_SwiftError: UnsafeRawPointer = - unsafeBitCast(GetProcAddress(hStdlibCore, - "_swift_lldb_sizeof_SwiftError")!, - to: UnsafeRawPointer.self) -#else - let offsetof_SwiftError_typeMetadata = - dlsym(RTLD_DEFAULT, "_swift_lldb_offsetof_SwiftError_typeMetadata")! - let sizeof_SwiftError = - dlsym(RTLD_DEFAULT, "_swift_lldb_sizeof_SwiftError")! -#endif #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) #if arch(i386) || arch(arm) expectEqual(20, offsetof_SwiftError_typeMetadata.load(as: UInt.self)) diff --git a/test/stdlib/StringDiagnostics.swift b/test/stdlib/StringDiagnostics.swift index 303fb7f721a15..3a53a26c69ee0 100644 --- a/test/stdlib/StringDiagnostics.swift +++ b/test/stdlib/StringDiagnostics.swift @@ -63,23 +63,27 @@ func testStringDeprecation(hello: String) { func acceptsCollection(_: C) {} func acceptsBidirectionalCollection(_: C) {} func acceptsRandomAccessCollection(_: C) {} +// expected-note@-1 {{where 'C' = 'String.UTF8View'}} +// expected-note@-2 {{where 'C' = 'String.UnicodeScalarView'}} +// expected-note@-3 {{where 'C' = 'String.UTF16View'}} +// expected-note@-4 {{where 'C' = 'String'}} func testStringCollectionTypes(s: String) { acceptsCollection(s.utf8) acceptsBidirectionalCollection(s.utf8) - acceptsRandomAccessCollection(s.utf8) // expected-error{{argument type 'String.UTF8View' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.utf8) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UTF8View' conform to 'RandomAccessCollection'}} acceptsCollection(s.utf16) acceptsBidirectionalCollection(s.utf16) - acceptsRandomAccessCollection(s.utf16) // expected-error{{argument type 'String.UTF16View' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.utf16) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UTF16View' conform to 'RandomAccessCollection'}} acceptsCollection(s.unicodeScalars) acceptsBidirectionalCollection(s.unicodeScalars) - acceptsRandomAccessCollection(s.unicodeScalars) // expected-error{{argument type 'String.UnicodeScalarView' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.unicodeScalars) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UnicodeScalarView' conform to 'RandomAccessCollection'}} acceptsCollection(s) acceptsBidirectionalCollection(s) - acceptsRandomAccessCollection(s) // expected-error{{argument type 'String' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String' conform to 'RandomAccessCollection'}} } // In previous versions of Swift, code would accidentally select diff --git a/test/stdlib/StringDiagnostics_without_Foundation.swift b/test/stdlib/StringDiagnostics_without_Foundation.swift index 570beed4c8ae1..e1c0454516104 100644 --- a/test/stdlib/StringDiagnostics_without_Foundation.swift +++ b/test/stdlib/StringDiagnostics_without_Foundation.swift @@ -4,22 +4,26 @@ func acceptsCollection(_: I) {} func acceptsBidirectionalCollection(_: I) {} func acceptsRandomAccessCollection(_: I) {} +// expected-note@-1 {{where 'I' = 'String.UTF8View'}} +// expected-note@-2 {{where 'I' = 'String.UnicodeScalarView'}} +// expected-note@-3 {{where 'I' = 'String.UTF16View'}} +// expected-note@-4 {{where 'I' = 'String'}} func testStringCollectionTypes(s: String) { acceptsCollection(s.utf8) acceptsBidirectionalCollection(s.utf8) - acceptsRandomAccessCollection(s.utf8) // expected-error{{argument type 'String.UTF8View' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.utf8) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UTF8View' conform to 'RandomAccessCollection'}} // UTF16View is random-access with Foundation, bidirectional without acceptsCollection(s.utf16) acceptsBidirectionalCollection(s.utf16) - acceptsRandomAccessCollection(s.utf16) // expected-error{{argument type 'String.UTF16View' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.utf16) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UTF16View' conform to 'RandomAccessCollection'}} acceptsCollection(s.unicodeScalars) acceptsBidirectionalCollection(s.unicodeScalars) - acceptsRandomAccessCollection(s.unicodeScalars) // expected-error{{argument type 'String.UnicodeScalarView' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s.unicodeScalars) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String.UnicodeScalarView' conform to 'RandomAccessCollection'}} acceptsCollection(s) acceptsBidirectionalCollection(s) - acceptsRandomAccessCollection(s) // expected-error{{argument type 'String' does not conform to expected type 'RandomAccessCollection'}} + acceptsRandomAccessCollection(s) // expected-error{{global function 'acceptsRandomAccessCollection' requires that 'String' conform to 'RandomAccessCollection'}} } diff --git a/test/stdlib/SwiftObjectNSObject.swift b/test/stdlib/SwiftObjectNSObject.swift index ef82fb358e899..68b3268d6b8b2 100644 --- a/test/stdlib/SwiftObjectNSObject.swift +++ b/test/stdlib/SwiftObjectNSObject.swift @@ -21,6 +21,10 @@ // REQUIRES: objc_interop +// rdar://problem/56959761 +// UNSUPPORTED: OS=watchos +// UNSUPPORTED: OS=tvos + import Foundation class C { @@ -42,7 +46,7 @@ func TestSwiftObjectNSObject(_ c: C, _ d: D) // This check is for NSLog() output from TestSwiftObjectNSObject(). // CHECK: c ##SwiftObjectNSObject.C## // CHECK-NEXT: d ##SwiftObjectNSObject.D## -// CHECK-NEXT: S ##{{(Swift._)?}}SwiftObject## +// CHECK-NEXT: S ##{{.*}}SwiftObject## // Temporarily disable this test on older OSes until we have time to // look into why it's failing there. rdar://problem/47870743 diff --git a/test/stdlib/TestData.swift b/test/stdlib/TestData.swift index 450bbb9fcad33..7608e9b8cace2 100644 --- a/test/stdlib/TestData.swift +++ b/test/stdlib/TestData.swift @@ -3806,30 +3806,81 @@ class TestData : TestDataSuper { } func test_nsdataSequence() { - let bytes: [UInt8] = Array(0x00...0xFF) - let data = bytes.withUnsafeBytes { NSData(bytes: $0.baseAddress, length: $0.count) } + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + let bytes: [UInt8] = Array(0x00...0xFF) + let data = bytes.withUnsafeBytes { NSData(bytes: $0.baseAddress, length: $0.count) } - for byte in bytes { - expectEqual(data[Int(byte)], byte) + for byte in bytes { + expectEqual(data[Int(byte)], byte) + } } } func test_dispatchSequence() { - let bytes1: [UInt8] = Array(0x00..<0xF0) - let bytes2: [UInt8] = Array(0xF0..<0xFF) - var data = DispatchData.empty - bytes1.withUnsafeBytes { - data.append($0) - } - bytes2.withUnsafeBytes { - data.append($0) + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + let bytes1: [UInt8] = Array(0x00..<0xF0) + let bytes2: [UInt8] = Array(0xF0..<0xFF) + var data = DispatchData.empty + bytes1.withUnsafeBytes { + data.append($0) + } + bytes2.withUnsafeBytes { + data.append($0) + } + + for byte in bytes1 { + expectEqual(data[Int(byte)], byte) + } + for byte in bytes2 { + expectEqual(data[Int(byte)], byte) + } } + } - for byte in bytes1 { - expectEqual(data[Int(byte)], byte) + func test_increaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + var data = Data(initial) + data.count += diff + expectEqualSequence( + Array(initial) + Array(repeating: 0, count: diff), + data) + } } - for byte in bytes2 { - expectEqual(data[Int(byte)], byte) + } + + func test_decreaseCount() { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { return } + let initials: [Range] = [ + 0..<0, + 0..<2, + 0..<4, + 0..<8, + 0..<16, + 0..<32, + 0..<64 + ] + let diffs = [0, 1, 2, 4, 8, 16, 32] + for initial in initials { + for diff in diffs { + guard initial.count >= diff else { continue } + var data = Data(initial) + data.count -= diff + expectEqualSequence( + initial.dropLast(diff), + data) + } } } } @@ -4151,8 +4202,12 @@ DataTests.test("test_validateMutation_slice_customBacking_withUnsafeMutableBytes DataTests.test("test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound") { TestData().test_validateMutation_slice_customMutableBacking_withUnsafeMutableBytes_lengthLessThanLowerBound() } DataTests.test("test_byte_access_of_discontiguousData") { TestData().test_byte_access_of_discontiguousData() } DataTests.test("test_rangeOfSlice") { TestData().test_rangeOfSlice() } -DataTests.test("test_nsdataSequence") { TestData().test_nsdataSequence() } -DataTests.test("test_dispatchSequence") { TestData().test_dispatchSequence() } +if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + DataTests.test("test_nsdataSequence") { TestData().test_nsdataSequence() } + DataTests.test("test_dispatchSequence") { TestData().test_dispatchSequence() } +} +DataTests.test("test_increaseCount") { TestData().test_increaseCount() } +DataTests.test("test_decreaseCount") { TestData().test_decreaseCount() } // XCTest does not have a crash detection, whereas lit does diff --git a/test/stdlib/TestDecimal.swift b/test/stdlib/TestDecimal.swift index 3d6a54f0a736d..2e0b13a5c2b80 100644 --- a/test/stdlib/TestDecimal.swift +++ b/test/stdlib/TestDecimal.swift @@ -120,9 +120,11 @@ class TestDecimal : TestDecimalSuper { expectFalse(zero.isNaN) expectFalse(zero.isSignaling) - let d1 = Decimal(1234567890123456789 as UInt64) - expectEqual(d1._exponent, 0) - expectEqual(d1._length, 4) + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + let d1 = Decimal(1234567890123456789 as UInt64) + expectEqual(d1._exponent, 0) + expectEqual(d1._length, 4) + } } func test_Constants() { expectEqual(8, NSDecimalMaxSize) @@ -303,7 +305,9 @@ class TestDecimal : TestDecimalSuper { expectEqual(Decimal(68040), Decimal(386).advanced(by: Decimal(67654))) expectEqual(Decimal(1.234), abs(Decimal(1.234))) expectEqual(Decimal(1.234), abs(Decimal(-1.234))) - expectTrue(Decimal.nan.magnitude.isNaN) + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + expectTrue(Decimal.nan.magnitude.isNaN) + } var a = Decimal(1234) var r = a expectEqual(.noError, NSDecimalMultiplyByPowerOf10(&r, &a, 1, .plain)) @@ -335,7 +339,9 @@ class TestDecimal : TestDecimalSuper { expectEqual(.noError, NSDecimalPower(&result, &actual, j, .plain)) let expected = Decimal(pow(Double(i), Double(j))) expectEqual(expected, result, "\(result) == \(i)^\(j)") - expectEqual(expected, pow(actual, j)) + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + expectEqual(expected, pow(actual, j)) + } } } } diff --git a/test/stdlib/TestJSONEncoder.swift b/test/stdlib/TestJSONEncoder.swift index eff297ae69e11..fdb3d45d6ff8c 100644 --- a/test/stdlib/TestJSONEncoder.swift +++ b/test/stdlib/TestJSONEncoder.swift @@ -9,7 +9,7 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: objc_interop - +// REQUIRES: rdar55727144 import Swift import Foundation diff --git a/test/stdlib/TestNSString.swift b/test/stdlib/TestNSString.swift index 49c164fce39ee..86020ea6fcc41 100644 --- a/test/stdlib/TestNSString.swift +++ b/test/stdlib/TestNSString.swift @@ -16,6 +16,7 @@ // REQUIRES: executable_test // REQUIRES: asan_runtime // REQUIRES: objc_interop +// REQUIRES: rdar55727144 import Foundation import FoundationBridgeObjC diff --git a/test/stdlib/UnsafePointerDiagnostics.swift b/test/stdlib/UnsafePointerDiagnostics.swift index d07bc20676609..ccaaf0279656a 100644 --- a/test/stdlib/UnsafePointerDiagnostics.swift +++ b/test/stdlib/UnsafePointerDiagnostics.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-invalid-ephemeralness-as-error // Test availability attributes on UnsafePointer initializers. // Assume the original source contains no UnsafeRawPointer types. @@ -67,8 +67,8 @@ func unsafePointerConversionAvailability( _ = UnsafeMutablePointer(umpi) // expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} _ = UnsafeMutablePointer(umps) // expected-warning {{UnsafeMutablePointer has been replaced by UnsafeMutableRawPointer}} - _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'RawPointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} - _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'RawPointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} + _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'Builtin.RawPointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} + _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'Builtin.RawPointer'}} expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(umpv) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(upv) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} _ = UnsafePointer(umpi) // expected-warning {{UnsafePointer has been replaced by UnsafeRawPointer}} @@ -81,10 +81,10 @@ func unsafePointerConversionAvailability( _ = UnsafeMutablePointer(orp) // expected-error {{no exact matches in call to initializer}} _ = UnsafeMutablePointer(omrp) // expected-error {{no exact matches in call to initializer}} - _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'RawPointer'}} - _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'RawPointer'}} - _ = UnsafePointer(orp) // expected-error {{cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'RawPointer'}} - _ = UnsafePointer(omrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer?' to expected argument type 'RawPointer'}} + _ = UnsafePointer(rp) // expected-error {{cannot convert value of type 'UnsafeRawPointer' to expected argument type 'Builtin.RawPointer'}} + _ = UnsafePointer(mrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer' to expected argument type 'Builtin.RawPointer'}} + _ = UnsafePointer(orp) // expected-error {{cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'Builtin.RawPointer'}} + _ = UnsafePointer(omrp) // expected-error {{cannot convert value of type 'UnsafeMutableRawPointer?' to expected argument type 'Builtin.RawPointer'}} _ = UnsafePointer(ups) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafePointer'}} // expected-note@-1 {{arguments to generic parameter 'Pointee' ('String' and 'Int') are expected to be equal}} @@ -134,3 +134,237 @@ struct SR9800 { _ = foo(UnsafePointer(buf)) // this call should be unambiguoius } } + +// Test that we get a custom diagnostic for an ephemeral conversion to non-ephemeral param for an Unsafe[Mutable][Raw][Buffer]Pointer init. +func unsafePointerInitEphemeralConversions() { + class C {} + var foo = 0 + var str = "" + var arr = [0] + var optionalArr: [Int]? = [0] + var c: C? + + _ = UnsafePointer(&foo) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer(&foo + 1) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to '+'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to '+'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer.init(&foo) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer("") // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer.init("") // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer(str) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer([0]) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(arr) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(&arr) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(optionalArr) // expected-error {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(_:)'}} + + + _ = UnsafeMutablePointer(&foo) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(&arr) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(&arr + 2) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to '+'}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to '+'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: &foo) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: "") // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: str) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: [0]) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: arr) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: &arr) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: optionalArr) // expected-error {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + + + _ = UnsafeRawPointer(&foo) // expected-error {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(str) // expected-error {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeRawPointer(arr) // expected-error {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(&arr) // expected-error {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(optionalArr) // expected-error {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(_:)'}} + + + _ = UnsafeMutableRawPointer(&foo) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(&arr) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: &foo) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: str) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: arr) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: &arr) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: optionalArr) // expected-error {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + + _ = UnsafeBufferPointer(start: &foo, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer.init(start: &foo, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: str, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer.init(start: str, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: arr, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: &arr, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: optionalArr, count: 0) // expected-error {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + + _ = UnsafeMutableBufferPointer(start: &foo, count: 0) // expected-error {{initialization of 'UnsafeMutableBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutableBufferPointer(start: &arr, count: 0) // expected-error {{initialization of 'UnsafeMutableBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + + _ = UnsafeRawBufferPointer(start: &foo, count: 0) // expected-error {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: str, count: 0) // expected-error {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: arr, count: 0) // expected-error {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: &arr, count: 0) // expected-error {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: optionalArr, count: 0) // expected-error {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + + + _ = UnsafeMutableRawBufferPointer(start: &foo, count: 0) // expected-error {{initialization of 'UnsafeMutableRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawBufferPointer(start: &arr, count: 0) // expected-error {{initialization of 'UnsafeMutableRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&foo) // expected-error {{no exact matches in call to initializer}} + + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&arr) // expected-error {{no exact matches in call to initializer}} + + _ = OpaquePointer(arr) // expected-error {{initialization of 'OpaquePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = OpaquePointer(str) // expected-error {{initialization of 'OpaquePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} + +var global = 0 + +// Test that we allow non-ephemeral conversions, such as inout-to-pointer for globals. +func unsafePointerInitNonEphemeralConversions() { + _ = UnsafePointer(&global) + _ = UnsafeMutablePointer(&global) + _ = UnsafeRawPointer(&global) + _ = UnsafeMutableRawPointer(&global) + _ = UnsafeBufferPointer(start: &global, count: 0) + _ = UnsafeMutableBufferPointer(start: &global, count: 0) + _ = UnsafeRawBufferPointer(start: &global, count: 0) + _ = UnsafeMutableRawBufferPointer(start: &global, count: 0) + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&global) // expected-error {{ambiguous use of 'init(_:)'}} +} diff --git a/test/stdlib/UnsafePointerDiagnostics_warning.swift b/test/stdlib/UnsafePointerDiagnostics_warning.swift new file mode 100644 index 0000000000000..243fa46c97572 --- /dev/null +++ b/test/stdlib/UnsafePointerDiagnostics_warning.swift @@ -0,0 +1,233 @@ +// RUN: %target-typecheck-verify-swift + +// Test that we get a custom diagnostic for an ephemeral conversion to non-ephemeral param for an Unsafe[Mutable][Raw][Buffer]Pointer init. +func unsafePointerInitEphemeralConversions() { + class C {} + var foo = 0 + var str = "" + var arr = [0] + var optionalArr: [Int]? = [0] + var c: C? + + _ = UnsafePointer(&foo) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer(&foo + 1) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to '+'}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to '+'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer.init(&foo) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer("") // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer.init("") // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer(str) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafePointer([0]) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(arr) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(&arr) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafePointer(optionalArr) // expected-warning {{initialization of 'UnsafePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(_:)'}} + + + _ = UnsafeMutablePointer(&foo) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(&arr) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(&arr + 2) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to '+'}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer' produces a pointer valid only for the duration of the call to '+'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: &foo) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: "") // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: str) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: [0]) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: arr) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: &arr) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutablePointer(mutating: optionalArr) // expected-warning {{initialization of 'UnsafeMutablePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + + + _ = UnsafeRawPointer(&foo) // expected-warning {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(str) // expected-warning {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeRawPointer(arr) // expected-warning {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(&arr) // expected-warning {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawPointer(optionalArr) // expected-warning {{initialization of 'UnsafeRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(_:)'}} + + + _ = UnsafeMutableRawPointer(&foo) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(&arr) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: &foo) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: str) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: arr) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: &arr) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawPointer(mutating: optionalArr) // expected-warning {{initialization of 'UnsafeMutableRawPointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(mutating:)}} + + _ = UnsafeBufferPointer(start: &foo, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer.init(start: &foo, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: str, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer.init(start: str, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: arr, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: &arr, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeBufferPointer(start: optionalArr, count: 0) // expected-warning {{initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + + _ = UnsafeMutableBufferPointer(start: &foo, count: 0) // expected-warning {{initialization of 'UnsafeMutableBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeMutableBufferPointer(start: &arr, count: 0) // expected-warning {{initialization of 'UnsafeMutableBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutablePointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + + _ = UnsafeRawBufferPointer(start: &foo, count: 0) // expected-warning {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: str, count: 0) // expected-warning {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: arr, count: 0) // expected-warning {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: &arr, count: 0) // expected-warning {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeRawBufferPointer(start: optionalArr, count: 0) // expected-warning {{initialization of 'UnsafeRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]?' to 'UnsafeRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + + + _ = UnsafeMutableRawBufferPointer(start: &foo, count: 0) // expected-warning {{initialization of 'UnsafeMutableRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = UnsafeMutableRawBufferPointer(start: &arr, count: 0) // expected-warning {{initialization of 'UnsafeMutableRawBufferPointer' results in a dangling buffer pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(start:count:)'}} + // expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&foo) // expected-error {{no exact matches in call to initializer}} + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&arr) // expected-error {{no exact matches in call to initializer}} + + _ = OpaquePointer(arr) // expected-warning {{initialization of 'OpaquePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}} + + _ = OpaquePointer(str) // expected-warning {{initialization of 'OpaquePointer' results in a dangling pointer}} + // expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'init(_:)'}} + // expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}} +} + +var global = 0 + +// Test that we allow non-ephemeral conversions, such as inout-to-pointer for globals. +func unsafePointerInitNonEphemeralConversions() { + _ = UnsafePointer(&global) + _ = UnsafeMutablePointer(&global) + _ = UnsafeRawPointer(&global) + _ = UnsafeMutableRawPointer(&global) + _ = UnsafeBufferPointer(start: &global, count: 0) + _ = UnsafeMutableBufferPointer(start: &global, count: 0) + _ = UnsafeRawBufferPointer(start: &global, count: 0) + _ = UnsafeMutableRawBufferPointer(start: &global, count: 0) + + // FIXME: This is currently ambiguous. + _ = OpaquePointer(&global) // expected-error {{ambiguous use of 'init(_:)'}} +} diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index 17e0d41e98edc..e670910bb55b9 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -12,6 +12,24 @@ func checkMatch(_ x: S, _ y: T, _ i: S.Index) expectEqual(x[i], y[i]) } +func checkMatchContiguousStorage(_ x: S, _ y: T) + where S.Element == T.Element, S.Element: Equatable +{ + let xElement = x.withContiguousStorageIfAvailable { $0.first } + let yElement = y.withContiguousStorageIfAvailable { $0.first } + + expectEqual(xElement, yElement) +} + +func checkHasContiguousStorage(_ x: S) { + expectTrue(x.withContiguousStorageIfAvailable { _ in true } ?? false) +} + +func checkHasContiguousStorageSubstring(_ x: Substring.UTF8View) { + let hasStorage = x.withContiguousStorageIfAvailable { _ in true } ?? false + expectTrue(hasStorage) +} + SubstringTests.test("Equality") { let s = "abcdefg" let s1 = s[s.index(s.startIndex, offsetBy: 2) ..< @@ -228,6 +246,22 @@ SubstringTests.test("UTF8View") { expectEqual("", String(t.dropLast(100))!) expectEqual("", String(u.dropFirst(100))!) expectEqual("", String(u.dropLast(100))!) + + + checkHasContiguousStorage(s.utf8) // Strings always do + checkHasContiguousStorageSubstring(t) + checkHasContiguousStorageSubstring(u) + checkMatchContiguousStorage(Array(s.utf8), s.utf8) + + // The specialization for Substring.withContiguousStorageIfAvailable was + // added in https://github.com/apple/swift/pull/29146. + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { + return + } + checkHasContiguousStorage(t) + checkHasContiguousStorage(u) + checkMatchContiguousStorage(Array(t), t) + checkMatchContiguousStorage(Array(u), u) } } diff --git a/test/stdlib/test_runtime_function_counters.swift b/test/stdlib/test_runtime_function_counters.swift index 547436743d7fd..882d9d7cd3200 100644 --- a/test/stdlib/test_runtime_function_counters.swift +++ b/test/stdlib/test_runtime_function_counters.swift @@ -100,7 +100,7 @@ func testCollectReferencesInsideObject() { func testRuntimeCounters() { print("TEST: APIs from _RuntimeFunctionCounters") let numRuntimeFunctionPointer = - _RuntimeFunctionCounters.getNumRuntimeFunctionCounters() + Int(_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()) print("Number of runtime function pointers: \(numRuntimeFunctionPointer)") diff --git a/test/stmt/errors.swift b/test/stmt/errors.swift index 85c22df7716dd..121db37a3d424 100644 --- a/test/stmt/errors.swift +++ b/test/stmt/errors.swift @@ -117,9 +117,9 @@ func nine() throws { try nine_helper(y: 0) // expected-error {{missing argument for parameter #1 in call}} } func ten_helper(_ x: Int) {} -func ten_helper(_ x: Int, y: Int) throws {} +func ten_helper(_ x: Int, y: Int) throws {} // expected-note {{'ten_helper(_:y:)' declared here}} func ten() throws { - try ten_helper(y: 0) // expected-error {{extraneous argument label 'y:' in call}} {{18-21=}} + try ten_helper(y: 0) // expected-error {{missing argument for parameter #1 in call}} {{18-18=<#Int#>, }} } // rdar://21074857 diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 8081b56c71cd4..2082be1455aa6 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -5,7 +5,7 @@ struct BadContainer1 { } func bad_containers_1(bc: BadContainer1) { - for e in bc { } // expected-error{{type 'BadContainer1' does not conform to protocol 'Sequence'}} + for e in bc { } // expected-error{{for-in loop requires 'BadContainer1' to conform to 'Sequence'}} } struct BadContainer2 : Sequence { // expected-error{{type 'BadContainer2' does not conform to protocol 'Sequence'}} @@ -14,6 +14,7 @@ struct BadContainer2 : Sequence { // expected-error{{type 'BadContainer2' does n func bad_containers_2(bc: BadContainer2) { for e in bc { } + // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} } struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does not conform to protocol 'Sequence'}} @@ -22,17 +23,19 @@ struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does n func bad_containers_3(bc: BadContainer3) { for e in bc { } + // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} } struct BadIterator1 {} -struct BadContainer4 : Sequence { // expected-error{{type 'BadContainer4' does not conform to protocol 'Sequence'}} expected-note 2 {{do you want to add protocol stubs?}} +struct BadContainer4 : Sequence { // expected-error{{type 'BadContainer4' does not conform to protocol 'Sequence'}} typealias Iterator = BadIterator1 // expected-note{{possibly intended match 'BadContainer4.Iterator' (aka 'BadIterator1') does not conform to 'IteratorProtocol'}} func makeIterator() -> BadIterator1 { } } func bad_containers_4(bc: BadContainer4) { for e in bc { } + // expected-warning@-1 {{immutable value 'e' was never used; consider replacing with '_' or removing it}} } // Pattern type-checking @@ -130,14 +133,14 @@ func testForEachInference() { // Generic sequence resolved contextually for i: Int in getGenericSeq() { } for d: Double in getGenericSeq() { } - + // Inference of generic arguments in the element type from the // sequence. - for x: X in getXIntSeq() { + for x: X in getXIntSeq() { let z = x.value + 1 } - for x: X in getOvlSeq() { + for x: X in getOvlSeq() { let z = x.value + 1 } @@ -171,9 +174,7 @@ func testMatchingPatterns() { // QoI: diagnostic for for-each over an optional sequence isn't great func testOptionalSequence() { let array : [Int]? - for x in array { // expected-error {{value of optional type '[Int]?' must be unwrapped}} - // expected-note@-1{{coalesce}} - // expected-note@-2{{force-unwrap}} + for x in array { // expected-error {{for-in loop requires '[Int]?' to conform to 'Sequence'; did you mean to unwrap optional?}} } } diff --git a/test/stmt/nonexhaustive_switch_stmt_editor.swift b/test/stmt/nonexhaustive_switch_stmt_editor.swift index 83027623476ee..996190cf7a9e2 100644 --- a/test/stmt/nonexhaustive_switch_stmt_editor.swift +++ b/test/stmt/nonexhaustive_switch_stmt_editor.swift @@ -19,6 +19,10 @@ public func testNonExhaustive(_ value: NonExhaustive) { case .b: break } + switch value { // expected-error {{switch must be exhaustive}} + // expected-note@-1 {{do you want to add missing cases?}} {{3-3=case .a:\n<#code#>\ncase .b:\n<#code#>\n@unknown default:\n<#code#>\n}} + } + switch value { case .a: break case .b: break diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index a22f4537450eb..8b79a3b24c47f 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -43,7 +43,7 @@ func funcdecl5(_ a: Int, y: Int) { (1) = x // expected-error {{cannot assign to value: literals are not mutable}} "string" = "other" // expected-error {{cannot assign to value: literals are not mutable}} [1, 1, 1, 1] = [1, 1] // expected-error {{cannot assign to immutable expression of type '[Int]}} - 1.0 = x // expected-error {{cannot assign to a literal value}} + 1.0 = x // expected-error {{cannot assign to value: literals are not mutable}} nil = 1 // expected-error {{cannot assign to value: literals are not mutable}} (x:1).x = 1 // expected-error {{cannot assign to immutable expression of type 'Int'}} @@ -235,11 +235,28 @@ func DoStmt() { } } -func DoWhileStmt() { - do { // expected-error {{'do-while' statement is not allowed; use 'repeat-while' instead}} {{3-5=repeat}} + +func DoWhileStmt1() { + do { // expected-error {{'do-while' statement is not allowed}} + // expected-note@-1 {{did you mean 'repeat-while' statement?}} {{3-5=repeat}} + // expected-note@-2 {{did you mean separate 'do' and 'while' statements?}} {{5-5=\n}} } while true } +func DoWhileStmt2() { + do { + + } + while true { + + } +} + +func LabeledDoStmt() { + LABEL: { // expected-error {{labeled block needs 'do'}} {{10-10=do }} + } +} + //===--- Repeat-while statement. func RepeatWhileStmt1() { diff --git a/test/type/builtin_types.swift b/test/type/builtin_types.swift new file mode 100644 index 0000000000000..2d54a255a1701 --- /dev/null +++ b/test/type/builtin_types.swift @@ -0,0 +1,8 @@ +// RUN: %target-typecheck-verify-swift -parse-stdlib + +import Swift + +func testBuiltinModulePrint(_ builtin: Builtin.Int64) -> Bool { + let x = 35 + return x == builtin // expected-error {{operator function '==' requires that 'Builtin.Int64' conform to 'BinaryInteger'}} +} diff --git a/test/type/opaque.swift b/test/type/opaque.swift index b78bf21108137..c1b73be63bbe2 100644 --- a/test/type/opaque.swift +++ b/test/type/opaque.swift @@ -122,7 +122,8 @@ func typeIdentity() { var af = alice af = alice af = bob // expected-error{{}} - af = grace // expected-error{{}} + af = grace // expected-error{{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{cannot assign value of type '(T) -> some P' to type '() -> some P'}} } do { @@ -402,9 +403,8 @@ func testCoercionDiagnostics() { opaque = SubscriptTest()[0] // expected-error {{cannot assign value of type 'some P' (result of 'SubscriptTest.subscript(_:)') to type 'some P' (result of 'foo()')}} {{none}} var opaqueOpt: Optional = opaque - // FIXME: It would be nice to show the "from" info here as well. - opaqueOpt = bar() // expected-error {{cannot assign value of type 'some P' to type '(some P)?'}} {{none}} - opaqueOpt = () // expected-error {{cannot assign value of type '()' to type '(some P)?'}} {{none}} + opaqueOpt = bar() // expected-error {{cannot assign value of type 'some P' (result of 'bar()') to type 'some P' (result of 'foo()')}} {{none}} + opaqueOpt = () // expected-error {{cannot assign value of type '()' to type 'some P'}} {{none}} } var globalVar: some P = 17 @@ -475,3 +475,8 @@ dynamic func foo(_ s: S) -> some Proto { func foo_repl(_ s: S) -> some Proto { return I() } + +protocol SomeProtocolA {} +protocol SomeProtocolB {} +struct SomeStructC: SomeProtocolA, SomeProtocolB {} +let someProperty: SomeProtocolA & some SomeProtocolB = SomeStructC() // expected-error {{'some' should appear at the beginning of a composition}}{{35-40=}}{{19-19=some }} diff --git a/test/type/protocol_composition.swift b/test/type/protocol_composition.swift index b78a9af3a5fd0..a75b0414b5aa5 100644 --- a/test/type/protocol_composition.swift +++ b/test/type/protocol_composition.swift @@ -173,7 +173,9 @@ takesP1AndP2([Swift.AnyObject & P1 & P2]()) takesP1AndP2([AnyObject & protocol_composition.P1 & P2]()) takesP1AndP2([AnyObject & P1 & protocol_composition.P2]()) takesP1AndP2([DoesNotExist & P1 & P2]()) // expected-error {{use of unresolved identifier 'DoesNotExist'}} -takesP1AndP2([Swift.DoesNotExist & P1 & P2]()) // expected-error {{module 'Swift' has no member named 'DoesNotExist'}} +// TODO(diagnostics): The problem here is that `&` is interpreted as a binary operator, we need to re-think +// how "missing member" fix is implemented because currently it finds N solutions with multiple fixes. +takesP1AndP2([Swift.DoesNotExist & P1 & P2]()) // expected-error {{cannot invoke '' with no arguments}} typealias T08 = P1 & inout P2 // expected-error {{'inout' may only be used on parameters}} typealias T09 = P1 & __shared P2 // expected-error {{'__shared' may only be used on parameters}} diff --git a/test/type/self.swift b/test/type/self.swift index 14893dd69472b..10e27b93a4e6e 100644 --- a/test/type/self.swift +++ b/test/type/self.swift @@ -162,6 +162,13 @@ class C { } } +extension C { + static var rdar57188331 = Self.staticFunc() // expected-error {{covariant 'Self' type cannot be referenced from a stored property initializer}} expected-error {{stored property cannot have covariant 'Self' type}} + static var rdar57188331Var = "" + static let rdar57188331Ref = UnsafeRawPointer(&Self.rdar57188331Var) // expected-error {{covariant 'Self' type cannot be referenced from a stored property initializer}} +} + + struct S1 { typealias _SELF = Self let j = 99.1 @@ -263,3 +270,20 @@ class Foo { Self.value * 2 }() } + +// https://bugs.swift.org/browse/SR-11681 - duplicate diagnostics +struct Box { + let boxed: T +} + +class Boxer { + lazy var s = Box(boxed: self as! Self) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{mutable property cannot have covariant 'Self' type}} + + var t = Box(boxed: Self()) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{covariant 'Self' type cannot be referenced from a stored property initializer}} + + required init() {} +} diff --git a/test/type/subclass_composition.swift b/test/type/subclass_composition.swift index 749a44cae6109..b9567ef0b3b36 100644 --- a/test/type/subclass_composition.swift +++ b/test/type/subclass_composition.swift @@ -105,8 +105,7 @@ func basicSubtyping( let _: Derived = baseAndP2 // expected-error {{cannot convert value of type 'Base & P2' to specified type 'Derived'}} let _: Derived & P2 = baseAndP2 // expected-error {{value of type 'Base & P2' does not conform to specified type 'Derived & P2'}} - // TODO(diagnostics): Diagnostic regression, better message is `value of type 'Unrelated' does not conform to 'Derived & P2' in coercion` - let _ = Unrelated() as Derived & P2 // expected-error {{cannot convert value of type 'Unrelated' to type 'Derived' in coercion}} + let _ = Unrelated() as Derived & P2 // expected-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}} let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}} let _ = baseAndP2 as Unrelated // expected-error {{cannot convert value of type 'Base & P2' to type 'Unrelated' in coercion}} let _ = baseAndP2 as? Unrelated // expected-warning {{always fails}} @@ -299,12 +298,14 @@ func dependentMemberTypes( _: BaseIntAndP2.FullyConcrete) {} func conformsToAnyObject(_: T) {} +// expected-note@-1 {{where 'T' = 'P1'}} func conformsToP1(_: T) {} // expected-note@-1 {{required by global function 'conformsToP1' where 'T' = 'P1'}} func conformsToP2(_: T) {} func conformsToBaseIntAndP2 & P2>(_: T) {} // expected-note@-1 {{where 'T' = 'FakeDerived'}} // expected-note@-2 {{where 'T' = 'T1'}} +// expected-note@-3 2 {{where 'T' = 'Base'}} func conformsToBaseIntAndP2WithWhereClause(_: T) where T : Base & P2 {} // expected-note@-1 {{where 'T' = 'FakeDerived'}} @@ -409,8 +410,7 @@ func conformsTo & P2>( // Errors conformsToAnyObject(p1) - // expected-error@-1 {{cannot invoke 'conformsToAnyObject' with an argument list of type '(P1)'}} - // expected-note@-2 {{expected an argument list of type '(T)'}} + // expected-error@-1 {{global function 'conformsToAnyObject' requires that 'P1' be a class type}} conformsToP1(p1) // expected-error@-1 {{value of protocol type 'P1' cannot conform to 'P1'; only struct/enum/class types can conform to protocols}} @@ -420,10 +420,10 @@ func conformsTo & P2>( // about `& P2` in generic parameter. conformsToBaseIntAndP2(base) - // expected-error@-1 {{argument type 'Base' does not conform to expected type 'P2'}} + // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base' conform to 'P2'}} conformsToBaseIntAndP2(badBase) - // expected-error@-1 {{argument type 'Base' does not conform to expected type 'P2'}} + // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base' conform to 'P2'}} // expected-error@-2 {{cannot convert value of type 'Base' to expected argument type 'Base'}} conformsToBaseIntAndP2(fakeDerived) diff --git a/test/type/subclass_composition_objc.swift b/test/type/subclass_composition_objc.swift index 7f554e25a1ddc..c0709c25bf6fd 100644 --- a/test/type/subclass_composition_objc.swift +++ b/test/type/subclass_composition_objc.swift @@ -25,20 +25,20 @@ class SomeMethods { // Test self-conformance func takesObjCClass(_: T) {} // expected-note {{where 'T' = 'ObjCProtocol'}} -func takesObjCProtocol(_: T) {} -func takesObjCClassAndProtocol(_: T) {} // expected-note {{where 'T' = 'ObjCProtocol'}} +func takesObjCProtocol(_: T) {} // expected-note {{where 'T' = 'ObjCClass'}} +func takesObjCClassAndProtocol(_: T) {} // expected-note {{where 'T' = 'ObjCProtocol'}} expected-note {{where 'T' = 'ObjCClass'}} func testSelfConformance(c: ObjCClass, p: ObjCProtocol, cp: ObjCClass & ObjCProtocol) { takesObjCClass(c) takesObjCClass(p) // expected-error {{global function 'takesObjCClass' requires that 'ObjCProtocol' inherit from 'ObjCClass'}} takesObjCClass(cp) - takesObjCProtocol(c) // expected-error {{argument type 'ObjCClass' does not conform to expected type 'ObjCProtocol'}} + takesObjCProtocol(c) // expected-error {{global function 'takesObjCProtocol' requires that 'ObjCClass' conform to 'ObjCProtocol'}} takesObjCProtocol(p) takesObjCProtocol(cp) // FIXME: Bad diagnostics - takesObjCClassAndProtocol(c) // expected-error {{argument type 'ObjCClass' does not conform to expected type 'ObjCProtocol'}} + takesObjCClassAndProtocol(c) // expected-error {{global function 'takesObjCClassAndProtocol' requires that 'ObjCClass' conform to 'ObjCProtocol'}} takesObjCClassAndProtocol(p) // expected-error {{global function 'takesObjCClassAndProtocol' requires that 'ObjCProtocol' inherit from 'ObjCClass'}} takesObjCClassAndProtocol(cp) } @@ -59,6 +59,6 @@ func testMetatypeSelfConformance(m1: (ObjCClass & ObjCProtocol).Protocol, _ = m1 as (ObjCClass & ObjCProtocol).Type _ = m1 as? (ObjCClass & ObjCProtocol).Type // expected-warning {{always succeeds}} - _ = m2 as (ObjCClass & StaticObjCProtocol).Type // expected-error {{'(ObjCClass & StaticObjCProtocol).Protocol' is not convertible to '(ObjCClass & StaticObjCProtocol).Type'; did you mean to use 'as!' to force downcast?}} + _ = m2 as (ObjCClass & StaticObjCProtocol).Type // expected-error {{cannot convert value of type '(ObjCClass & StaticObjCProtocol).Protocol' to type '(ObjCClass & StaticObjCProtocol).Type' in coercion}} _ = m2 as? (ObjCClass & StaticObjCProtocol).Type // FIXME should 'always fail' } diff --git a/test/type/types.swift b/test/type/types.swift index f73bba7e2a811..6ee2cb8dd5922 100644 --- a/test/type/types.swift +++ b/test/type/types.swift @@ -19,8 +19,12 @@ var d3 : () -> Float = { 4 } var d4 : () -> Int = { d2 } // expected-error{{function produces expected type 'Int'; did you mean to call it with '()'?}} {{26-26=()}} var e0 : [Int] -e0[] // expected-error {{cannot subscript a value of type '[Int]' with an argument of type '()'}} - // expected-note @-1 {{overloads for 'subscript' exist with these partially matching parameter lists: ((UnboundedRange_) -> ()), (Int), (R), (Range), (Range)}} +e0[] // expected-error {{no exact matches in call to subscript}} +// expected-note@-1 {{candidate has partially matching parameter list (Int)}} +// expected-note@-2 {{candidate has partially matching parameter list (Range)}} +// expected-note@-3 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} +// expected-note@-4 {{candidate has partially matching parameter list (Range.Index>)}} +// expected-note@-5 {{candidate has partially matching parameter list ((UnboundedRange_) -> ())}} var f0 : [Float] var f1 : [(Int,Int)] @@ -193,5 +197,3 @@ var _: sr5505 = sr5505 // expected-error {{use of undeclared type 'sr5505'}} typealias A = (inout Int ..., Int ... = [42, 12]) -> Void // expected-error {{'inout' must not be used on variadic parameters}} // expected-error@-1 {{only a single element can be variadic}} {{35-39=}} // expected-error@-2 {{default argument not permitted in a tuple type}} {{39-49=}} - -typealias rdar55075237 = Int.self // expected-error {{'self' is not a member type of 'Int'}} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 25f640e92fb94..ede02f0068bb7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -11,7 +11,6 @@ endif() # Add generated libSyntax headers to global dependencies. list(APPEND LLVM_COMMON_DEPENDS swift-syntax-generated-headers) -list(APPEND LLVM_COMMON_DEPENDS swift-parse-syntax-generated-headers) if(SWIFT_BUILD_SOURCEKIT) list(APPEND LLVM_COMMON_DEPENDS generated_sourcekit_uids) endif() diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index a544e07e8a7df..83b70845e0886 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -70,24 +70,27 @@ function(add_sourcekit_default_compiler_flags target) LINK_LIBRARIES_VAR_NAME link_libraries LIBRARY_SEARCH_DIRECTORIES_VAR_NAME library_search_directories) - # Convert variables to space-separated strings. - _list_escape_for_shell("${c_compile_flags}" c_compile_flags) - _list_escape_for_shell("${link_flags}" link_flags) - # Set compilation and link flags. - set_property(TARGET "${target}" APPEND_STRING PROPERTY - COMPILE_FLAGS " ${c_compile_flags} -fblocks") - set_property(TARGET "${target}" APPEND_STRING PROPERTY - LINK_FLAGS " ${link_flags}") - set_property(TARGET "${target}" APPEND PROPERTY LINK_LIBRARIES ${link_libraries}) - swift_target_link_search_directories("${target}" "${library_search_directories}") + if(${SWIFT_HOST_VARIANT_SDK} STREQUAL WINDOWS) + swift_windows_include_for_arch(${SWIFT_HOST_VARIANT_ARCH} + ${SWIFT_HOST_VARIANT_ARCH}_INCLUDE) + target_include_directories(${target} SYSTEM PRIVATE + ${${SWIFT_HOST_VARIANT_ARCH}_INCLUDE}) + endif() + target_compile_options(${target} PRIVATE + -fblocks) + target_link_options(${target} PRIVATE + ${link_flags}) + target_link_directories(${target} PRIVATE + ${library_search_directories}) + target_link_libraries(${target} PRIVATE + ${link_libraries}) endfunction() # Add a new SourceKit library. # # Usage: # add_sourcekit_library(name # Name of the library -# [LINK_LIBS dep1 ...] # Libraries this library will be linked with # [DEPENDS dep1 ...] # Targets this library depends on # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this library depends on # [INSTALL_IN_COMPONENT comp] # The Swift installation component that this library belongs to. @@ -97,7 +100,7 @@ macro(add_sourcekit_library name) cmake_parse_arguments(SOURCEKITLIB "SHARED" "INSTALL_IN_COMPONENT" - "HEADERS;LINK_LIBS;DEPENDS;LLVM_LINK_COMPONENTS" + "HEADERS;DEPENDS;LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITLIB_UNPARSED_ARGUMENTS}) @@ -132,6 +135,9 @@ macro(add_sourcekit_library name) set(libkind) endif() add_library(${name} ${libkind} ${srcs}) + if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID MATCHES Clang) + add_dependencies(${name} clang) + endif() llvm_update_compile_flags(${name}) set_output_directory(${name} @@ -146,21 +152,6 @@ macro(add_sourcekit_library name) add_dependencies(${name} ${SOURCEKITLIB_DEPENDS}) endif(SOURCEKITLIB_DEPENDS) - set(prefixed_link_libraries) - foreach(dep ${SOURCEKITLIB_LINK_LIBS}) - if("${dep}" MATCHES "^clang") - set(dep "${LLVM_LIBRARY_OUTPUT_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${dep}${CMAKE_STATIC_LIBRARY_SUFFIX}") - endif() - list(APPEND prefixed_link_libraries "${dep}") - endforeach() - set(SOURCEKITLIB_LINK_LIBS "${prefixed_link_libraries}") - - if("${libkind}" STREQUAL "SHARED") - target_link_libraries("${name}" PRIVATE ${SOURCEKITLIB_LINK_LIBS}) - else() - target_link_libraries("${name}" INTERFACE ${SOURCEKITLIB_LINK_LIBS}) - endif() - swift_common_llvm_config(${name} ${SOURCEKITLIB_LLVM_LINK_COMPONENTS}) if(SOURCEKITLIB_SHARED AND EXPORTED_SYMBOL_FILE) @@ -181,6 +172,13 @@ macro(add_sourcekit_library name) endif() endif() + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") + if(SOURCEKITLIB_SHARED) + set_target_properties(${name} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) + set_target_properties(${name} PROPERTIES INSTALL_RPATH "$ORIGIN/../lib/swift/android") + endif() + endif() + if("${SOURCEKITLIB_INSTALL_IN_COMPONENT}" STREQUAL "") if(SOURCEKITLIB_SHARED) set(SOURCEKITLIB_INSTALL_IN_COMPONENT tools) @@ -204,29 +202,33 @@ macro(add_sourcekit_library name) COMPONENT "${SOURCEKITLIB_INSTALL_IN_COMPONENT}") set_target_properties(${name} PROPERTIES FOLDER "SourceKit libraries") add_sourcekit_default_compiler_flags("${name}") + + swift_is_installing_component("${SOURCEKITLIB_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() endmacro() # Add a new SourceKit executable. # # Usage: # add_sourcekit_executable(name # Name of the executable -# [LINK_LIBS dep1 ...] # Libraries this executable depends on # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this executable # # depends on -# [EXCLUDE_FROM_ALL] # Whether to exclude this executable from -# # the ALL_BUILD target # source1 [source2 source3 ...]) # Sources to add into this executable macro(add_sourcekit_executable name) - cmake_parse_arguments(SOURCEKITEXE - "EXCLUDE_FROM_ALL" - "" - "LINK_LIBS;LLVM_LINK_COMPONENTS" - ${ARGN}) - - if (${SOURCEKITEXE_EXCLUDE_FROM_ALL}) - add_executable(${name} EXCLUDE_FROM_ALL ${SOURCEKITEXE_UNPARSED_ARGUMENTS}) - else() - add_executable(${name} ${SOURCEKITEXE_UNPARSED_ARGUMENTS}) + set(SOURCEKIT_EXECUTABLE_options) + set(SOURCEKIT_EXECUTABLE_single_parameter_options) + set(SOURCEKIT_EXECUTABLE_multiple_parameter_options LLVM_LINK_COMPONENTS) + cmake_parse_arguments(SOURCEKITEXE "${SOURCEKIT_EXECUTABLE_options}" + "${SOURCEKIT_EXECUTABLE_single_parameter_options}" + "${SOURCEKIT_EXECUTABLE_multiple_parameter_options}" ${ARGN}) + + add_executable(${name} ${SOURCEKITEXE_UNPARSED_ARGUMENTS}) + if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID MATCHES Clang) + add_dependencies(${name} clang) endif() llvm_update_compile_flags(${name}) set_output_directory(${name} @@ -238,22 +240,10 @@ macro(add_sourcekit_executable name) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif() - target_link_libraries(${name} PRIVATE ${SOURCEKITEXE_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITEXE_LLVM_LINK_COMPONENTS}) target_link_libraries(${name} PRIVATE ${LLVM_COMMON_LIBS}) set_target_properties(${name} PROPERTIES FOLDER "SourceKit executables") - if (NOT SWIFT_ASAN_BUILD) - if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - set_target_properties(${name} - PROPERTIES - LINK_FLAGS "-Wl,-exported_symbol,_main") - endif() - if(SWIFT_ANALYZE_CODE_COVERAGE) - set_property(TARGET "${name}" APPEND_STRING PROPERTY - LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") - endif() - endif() add_sourcekit_default_compiler_flags("${name}") endmacro() @@ -261,14 +251,13 @@ endmacro() # # Usage: # add_sourcekit_framework(name # Name of the framework -# [LINK_LIBS dep1 ...] # Libraries this framework will link with # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this framework depends on # [MODULEMAP modulemap] # Module map file for this framework # [INSTALL_IN_COMPONENT comp] # The Swift installation component that this framework belongs to. # source1 [source2 source3 ...]) # Sources to add into this framework macro(add_sourcekit_framework name) cmake_parse_arguments(SOURCEKITFW - "" "MODULEMAP;INSTALL_IN_COMPONENT" "LINK_LIBS;LLVM_LINK_COMPONENTS" ${ARGN}) + "" "MODULEMAP;INSTALL_IN_COMPONENT" "LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITFW_UNPARSED_ARGUMENTS}) set(lib_dir ${SOURCEKIT_LIBRARY_OUTPUT_INTDIR}) @@ -310,7 +299,6 @@ macro(add_sourcekit_framework name) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) - target_link_libraries(${name} PRIVATE ${SOURCEKITFW_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITFW_LLVM_LINK_COMPONENTS}) if (EXPORTED_SYMBOL_FILE) @@ -331,7 +319,6 @@ macro(add_sourcekit_framework name) endif() endif() - if (SOURCEKIT_DEPLOYMENT_OS MATCHES "^macosx") set_output_directory(${name} BINARY_DIR ${SOURCEKIT_RUNTIME_OUTPUT_INTDIR} @@ -381,6 +368,14 @@ macro(add_sourcekit_framework name) COMMAND ${CMAKE_COMMAND} -E copy "${hdr}" "${framework_location}/Headers/${hdrname}") endforeach() endif() + + swift_is_installing_component("${SOURCEKITFW_INSTALL_IN_COMPONENT}" is_installing) + if(NOT is_installing) + set_property(GLOBAL APPEND PROPERTY SWIFT_BUILDTREE_EXPORTS ${name}) + else() + set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${name}) + endif() + add_sourcekit_default_compiler_flags("${name}") endmacro(add_sourcekit_framework) @@ -388,11 +383,10 @@ endmacro(add_sourcekit_framework) # # Usage: # add_sourcekit_xpc_service(name # Name of the XPC service -# [LINK_LIBS dep1 ...] # Libraries this service will link with # [LLVM_LINK_COMPONENTS comp1 ...] # LLVM components this service depends on # source1 [source2 source3 ...]) # Sources to add into this service macro(add_sourcekit_xpc_service name framework_target) - cmake_parse_arguments(SOURCEKITXPC "" "" "LINK_LIBS;LLVM_LINK_COMPONENTS" ${ARGN}) + cmake_parse_arguments(SOURCEKITXPC "" "" "LLVM_LINK_COMPONENTS" ${ARGN}) set(srcs ${SOURCEKITXPC_UNPARSED_ARGUMENTS}) set(lib_dir ${SOURCEKIT_LIBRARY_OUTPUT_INTDIR}) @@ -433,7 +427,6 @@ macro(add_sourcekit_xpc_service name framework_target) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) - target_link_libraries(${name} PRIVATE ${SOURCEKITXPC_LINK_LIBS}) swift_common_llvm_config(${name} ${SOURCEKITXPC_LLVM_LINK_COMPONENTS}) target_link_libraries(${name} PRIVATE ${LLVM_COMMON_LIBS}) diff --git a/tools/SourceKit/include/SourceKit/Core/Context.h b/tools/SourceKit/include/SourceKit/Core/Context.h index 063516e723fa4..056e727fe7e2e 100644 --- a/tools/SourceKit/include/SourceKit/Core/Context.h +++ b/tools/SourceKit/include/SourceKit/Core/Context.h @@ -16,6 +16,7 @@ #include "SourceKit/Core/LLVM.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Mutex.h" #include #include @@ -27,10 +28,30 @@ namespace SourceKit { class LangSupport; class NotificationCenter; +class GlobalConfig { +public: + struct Settings { + /// When true, the default compiler options and other configuration flags will be chosen to optimize for + /// usage from an IDE. + /// + /// At the time of writing this just means ignoring .swiftsourceinfo files. + bool OptimizeForIDE = false; + }; + +private: + Settings State; + mutable llvm::sys::Mutex Mtx; + +public: + Settings update(Optional OptimizeForIDE); + bool shouldOptimizeForIDE() const; +}; + class Context { std::string RuntimeLibPath; std::unique_ptr SwiftLang; std::shared_ptr NotificationCtr; + std::shared_ptr Config; public: Context(StringRef RuntimeLibPath, @@ -44,6 +65,8 @@ class Context { LangSupport &getSwiftLangSupport() { return *SwiftLang; } std::shared_ptr getNotificationCenter() { return NotificationCtr; } + + std::shared_ptr getGlobalConfiguration() { return Config; } }; } // namespace SourceKit diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index b1737186dcc58..5341716393419 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -653,6 +653,7 @@ class LangSupport { virtual void codeComplete(llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) = 0; @@ -801,13 +802,15 @@ class LangSupport { virtual void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) = 0; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) = 0; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) = 0; virtual void getStatistics(StatisticsReceiver) = 0; }; diff --git a/tools/SourceKit/lib/Core/CMakeLists.txt b/tools/SourceKit/lib/Core/CMakeLists.txt index ce80d3bbea066..4416c61229f42 100644 --- a/tools/SourceKit/lib/Core/CMakeLists.txt +++ b/tools/SourceKit/lib/Core/CMakeLists.txt @@ -3,5 +3,6 @@ add_sourcekit_library(SourceKitCore Context.cpp LangSupport.cpp NotificationCenter.cpp - LINK_LIBS SourceKitSupport ) +target_link_libraries(SourceKitCore PRIVATE + SourceKitSupport) diff --git a/tools/SourceKit/lib/Core/Context.cpp b/tools/SourceKit/lib/Core/Context.cpp index 066b5bbfdade3..65a245283bd46 100644 --- a/tools/SourceKit/lib/Core/Context.cpp +++ b/tools/SourceKit/lib/Core/Context.cpp @@ -16,11 +16,25 @@ using namespace SourceKit; +GlobalConfig::Settings +GlobalConfig::update(Optional OptimizeForIDE) { + llvm::sys::ScopedLock L(Mtx); + if (OptimizeForIDE.hasValue()) + State.OptimizeForIDE = *OptimizeForIDE; + return State; +}; + +bool GlobalConfig::shouldOptimizeForIDE() const { + llvm::sys::ScopedLock L(Mtx); + return State.OptimizeForIDE; +} + SourceKit::Context::Context(StringRef RuntimeLibPath, llvm::function_ref(Context &)> LangSupportFactoryFn, bool shouldDispatchNotificationsOnMain) : RuntimeLibPath(RuntimeLibPath), - NotificationCtr(new NotificationCenter(shouldDispatchNotificationsOnMain)) { + NotificationCtr(new NotificationCenter(shouldDispatchNotificationsOnMain)), + Config(new GlobalConfig()) { // Should be called last after everything is initialized. SwiftLang = LangSupportFactoryFn(*this); } diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index 587f2b62395d6..901720a521069 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -1,18 +1,19 @@ -set(SourceKitSupport_sources +add_sourcekit_library(SourceKitSupport Concurrency-libdispatch.cpp FuzzyStringMatcher.cpp Logging.cpp ImmutableTextBuffer.cpp ThreadSafeRefCntPtr.cpp Tracing.cpp - UIDRegistry.cpp -) - -add_sourcekit_library(SourceKitSupport - ${SourceKitSupport_sources} - LINK_LIBS swiftBasic swiftSyntax clangBasic clangRewrite -) -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(SourceKitSupport INTERFACE dispatch BlocksRuntime) + UIDRegistry.cpp) +target_link_libraries(SourceKitSupport PRIVATE + swiftBasic + swiftSyntax + clangBasic + clangRewrite) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(SourceKitSupport INTERFACE + dispatch + BlocksRuntime) endif() diff --git a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt index 92fe7299dd8d5..de505e29847f7 100644 --- a/tools/SourceKit/lib/SwiftLang/CMakeLists.txt +++ b/tools/SourceKit/lib/SwiftLang/CMakeLists.txt @@ -10,31 +10,6 @@ add_sourcekit_library(SourceKitSwiftLang SwiftLangSupport.cpp SwiftSourceDocInfo.cpp SwiftTypeContextInfo.cpp - LINK_LIBS - SourceKitCore swiftDriver swiftFrontend - swiftClangImporter swiftIDE - swiftAST swiftMarkup swiftParse swiftParseSIL swiftSIL swiftSILGen - swiftSILOptimizer swiftIRGen swiftSema swiftBasic swiftSerialization - swiftSyntax swiftOption libcmark_static - # Clang dependencies. - clangIndex - clangFormat - clangToolingCore - clangFrontendTool - clangFrontend - clangDriver - clangCodeGen - clangSerialization - clangParse - clangSema - clangAnalysis - clangEdit - clangRewriteFrontend - clangRewrite - clangLex - clangAST - clangAPINotes - clangBasic LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} bitreader bitwriter @@ -49,5 +24,43 @@ add_sourcekit_library(SourceKitSwiftLang objcarcopts profiledata ) - +target_link_libraries(SourceKitSwiftLang PRIVATE + SourceKitCore + swiftDriver + swiftFrontend + swiftClangImporter + swiftIDE + swiftAST + swiftMarkup + swiftParse + swiftParseSIL + swiftSIL + swiftSILGen + swiftSILOptimizer + swiftIRGen + swiftSema + swiftBasic + swiftSerialization + swiftSyntax + swiftOption + libcmark_static + # Clang dependencies. + clangIndex + clangFormat + clangToolingCore + clangFrontendTool + clangFrontend + clangDriver + clangCodeGen + clangSerialization + clangParse + clangSema + clangAnalysis + clangEdit + clangRewriteFrontend + clangRewrite + clangLex + clangAST + clangAPINotes + clangBasic) add_dependencies(SourceKitSwiftLang clang-tablegen-targets) diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 2fc29e21328b1..83635923ba70f 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -58,7 +58,7 @@ class ImportDepth { public: ImportDepth() = default; - ImportDepth(ASTContext &context, CompilerInvocation &invocation); + ImportDepth(ASTContext &context, const CompilerInvocation &invocation); Optional lookup(StringRef module) { auto I = depths.find(module); @@ -320,7 +320,8 @@ CodeCompletionViewRef CodeCompletionOrganizer::takeResultsView() { // ImportDepth //===----------------------------------------------------------------------===// -ImportDepth::ImportDepth(ASTContext &context, CompilerInvocation &invocation) { +ImportDepth::ImportDepth(ASTContext &context, + const CompilerInvocation &invocation) { llvm::DenseSet seen; std::deque> worklist; diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h index 9d79f2ea55b6c..27d53585c5af6 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.h @@ -42,6 +42,7 @@ struct Options { bool hideLowPriority = true; bool hideByNameStyle = true; bool fuzzyMatching = true; + bool reuseASTContextIfPossible = false; unsigned minFuzzyLength = 2; unsigned showTopNonLiteralResults = 3; @@ -53,7 +54,7 @@ struct Options { struct SwiftCompletionInfo { swift::ASTContext *swiftASTContext = nullptr; - swift::CompilerInvocation *invocation = nullptr; + const swift::CompilerInvocation *invocation = nullptr; swift::ide::CodeCompletionContext *completionContext = nullptr; }; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 187755c143c26..1e2b1fd10a211 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -46,20 +46,25 @@ class StreamDiagConsumer : public DiagnosticConsumer { public: StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} - void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { // FIXME: Print location info if available. - switch (Kind) { - case DiagnosticKind::Error: OS << "error: "; break; - case DiagnosticKind::Warning: OS << "warning: "; break; - case DiagnosticKind::Note: OS << "note: "; break; - case DiagnosticKind::Remark: OS << "remark: "; break; + switch (Info.Kind) { + case DiagnosticKind::Error: + OS << "error: "; + break; + case DiagnosticKind::Warning: + OS << "warning: "; + break; + case DiagnosticKind::Note: + OS << "note: "; + break; + case DiagnosticKind::Remark: + OS << "remark: "; + break; } - DiagnosticEngine::formatDiagnosticText(OS, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, + Info.FormatArgs); } }; } // end anonymous namespace @@ -366,11 +371,13 @@ struct CacheKeyHashInfo { struct SwiftASTManager::Implementation { explicit Implementation( std::shared_ptr EditorDocs, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath) - : EditorDocs(EditorDocs), Stats(Stats), + : EditorDocs(EditorDocs), Config(Config), Stats(Stats), RuntimeResourcePath(RuntimeResourcePath) {} std::shared_ptr EditorDocs; + std::shared_ptr Config; std::shared_ptr Stats; std::string RuntimeResourcePath; SourceManager SourceMgr; @@ -396,8 +403,10 @@ struct SwiftASTManager::Implementation { SwiftASTManager::SwiftASTManager( std::shared_ptr EditorDocs, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath) - : Impl(*new Implementation(EditorDocs, Stats, RuntimeResourcePath)) {} + : Impl(*new Implementation(EditorDocs, Config, Stats, + RuntimeResourcePath)) {} SwiftASTManager::~SwiftASTManager() { delete &Impl; @@ -434,6 +443,7 @@ static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( llvm::SmallString<128> newFilename; if (auto err = FileSystem->getRealPath(input.file(), newFilename)) newFilename = input.file(); + llvm::sys::path::native(newFilename); bool newIsPrimary = input.isPrimary() || (!PrimaryFile.empty() && PrimaryFile == newFilename); if (newIsPrimary) { @@ -529,6 +539,15 @@ bool SwiftASTManager::initCompilerInvocation( // We don't care about LLVMArgs FrontendOpts.LLVMArgs.clear(); + // SwiftSourceInfo files provide source location information for decls coming + // from loaded modules. For most IDE use cases it either has an undesirable + // impact on performance with no benefit (code completion), results in stale + // locations being used instead of more up-to-date indexer locations (cursor + // info), or has no observable effect (diagnostics, which are filtered to just + // those with a location in the primary file, and everything else). + if (Impl.Config->shouldOptimizeForIDE()) + FrontendOpts.IgnoreSwiftSourceInfo = true; + // Disable expensive SIL options to reduce time spent in SILGen. disableExpensiveSILOptions(Invocation.getSILOptions()); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h index b5d64e1e5c494..266b2e4636640 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.h @@ -43,6 +43,7 @@ namespace SourceKit { class SwiftLangSupport; class SwiftInvocation; struct SwiftStatistics; + class GlobalConfig; typedef RefPtr SwiftInvocationRef; class EditorDiagConsumer; @@ -89,6 +90,7 @@ typedef std::shared_ptr SwiftASTConsumerRef; class SwiftASTManager : public std::enable_shared_from_this { public: explicit SwiftASTManager(std::shared_ptr, + std::shared_ptr Config, std::shared_ptr Stats, StringRef RuntimeResourcePath); ~SwiftASTManager(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 4ab794506e2ff..2ca913da9b417 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -22,6 +22,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -90,7 +91,7 @@ struct SwiftCodeCompletionConsumer : handleResultsImpl(handleResultsImpl) {} void setContext(swift::ASTContext *context, - swift::CompilerInvocation *invocation, + const swift::CompilerInvocation *invocation, swift::ide::CodeCompletionContext *completionContext) { swiftContext.swiftASTContext = context; swiftContext.invocation = invocation; @@ -100,7 +101,6 @@ struct SwiftCodeCompletionConsumer void handleResults(MutableArrayRef Results) override { assert(swiftContext.swiftASTContext); - CodeCompletionContext::sortCompletionResults(Results); handleResultsImpl(Results, swiftContext); } }; @@ -123,106 +123,47 @@ static bool swiftCodeCompleteImpl( unsigned Offset, SwiftCodeCompletionConsumer &SwiftConsumer, ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { - assert(FileSystem); - - // Resolve symlinks for the input file; we resolve them for the input files - // in the arguments as well. - // FIXME: We need the Swift equivalent of Clang's FileEntry. - auto InputFile = llvm::MemoryBuffer::getMemBuffer( - UnresolvedInputFile->getBuffer(), - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier())); - - auto origBuffSize = InputFile->getBufferSize(); - unsigned CodeCompletionOffset = Offset; - if (CodeCompletionOffset > origBuffSize) { - CodeCompletionOffset = origBuffSize; - } - - CompilerInstance CI; - // Display diagnostics to stderr. - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, InputFile->getBufferIdentifier(), Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start(SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(Offset)), - std::make_pair("Offset", - std::to_string(CodeCompletionOffset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), InputFile->getBufferIdentifier(), - FileSystem, Error); - if (Failed) { - return false; - } - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - const char *Position = InputFile->getBufferStart() + CodeCompletionOffset; - std::unique_ptr NewBuffer = - llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - InputFile->getBufferSize() + 1, - InputFile->getBufferIdentifier()); - char *NewBuf = NewBuffer->getBufferStart(); - char *NewPos = std::copy(InputFile->getBufferStart(), Position, NewBuf); - *NewPos = '\0'; - std::copy(Position, InputFile->getBufferEnd(), NewPos+1); - - Invocation.setCodeCompletionPoint(NewBuffer.get(), CodeCompletionOffset); - - auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. - ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - SwiftConsumer)); - - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); - - // FIXME: We need to be passing the buffers from the open documents. - // It is not a huge problem in practice because Xcode auto-saves constantly. - - if (FileSystem != llvm::vfs::getRealFileSystem()) { - CI.getSourceMgr().setFileSystem(FileSystem); - } - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - - CloseClangModuleFiles scopedCloseFiles( - *CI.getASTContext().getClangModuleLoader()); - SwiftConsumer.setContext(&CI.getASTContext(), &Invocation, - &CompletionContext); - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - SwiftConsumer.clearContext(); - - return true; + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache. + ide::CodeCompletionContext CompletionContext(swiftCache->getCache()); + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, + SwiftConsumer)); + + SwiftConsumer.setContext(&CI.getASTContext(), &CI.getInvocation(), + &CompletionContext); + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + SwiftConsumer.clearContext(); + }); } +static void translateCodeCompletionOptions(OptionsDictionary &from, + CodeCompletion::Options &to, + StringRef &filterText, + unsigned &resultOffset, + unsigned &maxResults); + void SwiftLangSupport::codeComplete( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &SKConsumer, ArrayRef Args, Optional vfsOptions) { + CodeCompletion::Options CCOpts; + if (options) { + StringRef filterText; + unsigned resultOffset = 0; + unsigned maxResults = 0; + translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset, + maxResults); + } + std::string error; // FIXME: the use of None as primary file is to match the fact we do not read // the document contents using the editor documents infrastructure. @@ -267,7 +208,8 @@ void SwiftLangSupport::codeComplete( std::string Error; if (!swiftCodeCompleteImpl(*this, UnresolvedInputFile, Offset, SwiftConsumer, - Args, fileSystem, Error)) { + Args, fileSystem, + CCOpts.reuseASTContextIfPossible, Error)) { SKConsumer.failed(Error); } } @@ -842,6 +784,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, static UIdent KeyContextWeight("key.codecomplete.sort.contextweight"); static UIdent KeyFuzzyWeight("key.codecomplete.sort.fuzzyweight"); static UIdent KeyPopularityBonus("key.codecomplete.sort.popularitybonus"); + static UIdent KeyReuseASTContext("key.codecomplete.reuseastcontext"); from.valueForOption(KeySortByName, to.sortByName); from.valueForOption(KeyUseImportDepth, to.useImportDepth); from.valueForOption(KeyGroupOverloads, to.groupOverloads); @@ -865,6 +808,7 @@ static void translateCodeCompletionOptions(OptionsDictionary &from, from.valueForOption(KeyPopularityBonus, to.popularityBonus); from.valueForOption(KeyHideByName, to.hideByNameStyle); from.valueForOption(KeyTopNonLiteral, to.showTopNonLiteralResults); + from.valueForOption(KeyReuseASTContext, to.reuseASTContextIfPossible); } /// Canonicalize a name that is in the format of a reference to a function into @@ -1135,7 +1079,8 @@ static void transformAndForwardResults( cargs.push_back(arg.c_str()); std::string error; if (!swiftCodeCompleteImpl(lang, buffer.get(), str.size(), swiftConsumer, - cargs, session->getFileSystem(), error)) { + cargs, session->getFileSystem(), + options.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } @@ -1174,7 +1119,8 @@ static void transformAndForwardResults( void SwiftLangSupport::codeCompleteOpen( StringRef name, llvm::MemoryBuffer *inputBuf, unsigned offset, OptionsDictionary *options, ArrayRef rawFilterRules, - GroupedCodeCompletionConsumer &consumer, ArrayRef args, Optional vfsOptions) { + GroupedCodeCompletionConsumer &consumer, ArrayRef args, + Optional vfsOptions) { StringRef filterText; unsigned resultOffset = 0; unsigned maxResults = 0; @@ -1232,7 +1178,8 @@ void SwiftLangSupport::codeCompleteOpen( // Invoke completion. if (!swiftCodeCompleteImpl(*this, inputBuf, offset, swiftConsumer, - extendedArgs, fileSystem, error)) { + extendedArgs, fileSystem, + CCOpts.reuseASTContextIfPossible, error)) { consumer.failed(error); return; } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp index 2876879da1d5e..eeeb0bc3b76df 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp @@ -16,6 +16,7 @@ #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/ConformingMethodList.h" +#include "swift/IDE/CompletionInstance.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" #include "clang/AST/Decl.h" @@ -29,68 +30,35 @@ static bool swiftConformingMethodListImpl( SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - ide::ConformingMethodListConsumer &Consumer, std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); - - auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - CompilerInstance CI; - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, - Consumer)); - - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - - return true; + ide::ConformingMethodListConsumer &Consumer, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames, + Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getConformingMethodList( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypeNames, - SourceKit::ConformingMethodListConsumer &SKConsumer) { + SourceKit::ConformingMethodListConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); class Consumer : public ide::ConformingMethodListConsumer { SourceKit::ConformingMethodListConsumer &SKConsumer; @@ -206,9 +174,9 @@ void SwiftLangSupport::getConformingMethodList( } } Consumer(SKConsumer); - std::string Error; if (!swiftConformingMethodListImpl(*this, UnresolvedInputFile, Offset, Args, - ExpectedTypeNames, Consumer, Error)) { - SKConsumer.failed(Error); + ExpectedTypeNames, Consumer, fileSystem, + /*EnableASTCaching=*/false, error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index fafc03590ae16..9e7616216ddab 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -41,7 +41,7 @@ using namespace swift; using namespace ide; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -52,7 +52,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { @@ -584,10 +584,12 @@ static void reportAttributes(ASTContext &Ctx, DocInfoConsumer &Consumer) { static UIdent AvailableAttrKind("source.lang.swift.attribute.availability"); static UIdent PlatformIOS("source.availability.platform.ios"); + static UIdent PlatformMacCatalyst("source.availability.platform.maccatalyst"); static UIdent PlatformOSX("source.availability.platform.osx"); static UIdent PlatformtvOS("source.availability.platform.tvos"); static UIdent PlatformWatchOS("source.availability.platform.watchos"); static UIdent PlatformIOSAppExt("source.availability.platform.ios_app_extension"); + static UIdent PlatformMacCatalystAppExt("source.availability.platform.maccatalyst_app_extension"); static UIdent PlatformOSXAppExt("source.availability.platform.osx_app_extension"); static UIdent PlatformtvOSAppExt("source.availability.platform.tvos_app_extension"); static UIdent PlatformWatchOSAppExt("source.availability.platform.watchos_app_extension"); @@ -601,6 +603,8 @@ static void reportAttributes(ASTContext &Ctx, PlatformUID = UIdent(); break; case PlatformKind::iOS: PlatformUID = PlatformIOS; break; + case PlatformKind::macCatalyst: + PlatformUID = PlatformIOS; break; case PlatformKind::OSX: PlatformUID = PlatformOSX; break; case PlatformKind::tvOS: @@ -609,6 +613,8 @@ static void reportAttributes(ASTContext &Ctx, PlatformUID = PlatformWatchOS; break; case PlatformKind::iOSApplicationExtension: PlatformUID = PlatformIOSAppExt; break; + case PlatformKind::macCatalystApplicationExtension: + PlatformUID = PlatformMacCatalystAppExt; break; case PlatformKind::OSXApplicationExtension: PlatformUID = PlatformOSXAppExt; break; case PlatformKind::tvOSApplicationExtension: @@ -1185,12 +1191,8 @@ accept(SourceManager &SM, RegionType RegionType, } void RequestRefactoringEditConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { - Impl.DiagConsumer.handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, - Info, bufferIndirectlyCausingDiagnostic); + SourceManager &SM, const DiagnosticInfo &Info) { + Impl.DiagConsumer.handleDiagnostic(SM, Info); } class RequestRenameRangeConsumer::Implementation { @@ -1244,13 +1246,9 @@ void RequestRenameRangeConsumer::accept( Impl.accept(SM, RegionType, Ranges); } -void RequestRenameRangeConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { - Impl.DiagConsumer.handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, - Info, bufferIndirectlyCausingDiagnostic); +void RequestRenameRangeConsumer::handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) { + Impl.DiagConsumer.handleDiagnostic(SM, Info); } static NameUsage getNameUsage(RenameType Type) { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index 23eeca2cac45f..8914b992bcc22 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -75,13 +75,10 @@ void EditorDiagConsumer::getAllDiagnostics( } } -void EditorDiagConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { +void EditorDiagConsumer::handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) { - if (Kind == DiagnosticKind::Error) { + if (Info.Kind == DiagnosticKind::Error) { HadAnyError = true; } @@ -90,13 +87,13 @@ void EditorDiagConsumer::handleDiagnostic( Info.ID == diag::error_doing_code_completion.ID) return; - bool IsNote = (Kind == DiagnosticKind::Note); + bool IsNote = (Info.Kind == DiagnosticKind::Note); if (IsNote && !haveLastDiag()) // Is this possible? return; - if (Kind == DiagnosticKind::Remark) { + if (Info.Kind == DiagnosticKind::Remark) { // FIXME: we may want to handle optimization remarks in sourcekitd. LOG_WARN_FUNC("unhandled optimization remark"); return; @@ -108,13 +105,14 @@ void EditorDiagConsumer::handleDiagnostic( llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); - DiagnosticEngine::formatDiagnosticText(Out, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString, + Info.FormatArgs); } SKInfo.Description = Text.str(); Optional BufferIDOpt; - if (Loc.isValid()) { - BufferIDOpt = SM.findBufferContainingLoc(Loc); + if (Info.Loc.isValid()) { + BufferIDOpt = SM.findBufferContainingLoc(Info.Loc); } if (BufferIDOpt && !isInputBufferID(*BufferIDOpt)) { @@ -147,9 +145,10 @@ void EditorDiagConsumer::handleDiagnostic( if (BufferIDOpt.hasValue()) { unsigned BufferID = *BufferIDOpt; - SKInfo.Offset = SM.getLocOffsetInBuffer(Loc, BufferID); - std::tie(SKInfo.Line, SKInfo.Column) = SM.getLineAndColumn(Loc, BufferID); - SKInfo.Filename = SM.getDisplayNameForLoc(Loc); + SKInfo.Offset = SM.getLocOffsetInBuffer(Info.Loc, BufferID); + std::tie(SKInfo.Line, SKInfo.Column) = + SM.getLineAndColumn(Info.Loc, BufferID); + SKInfo.Filename = SM.getDisplayNameForLoc(Info.Loc); for (auto R : Info.Ranges) { if (R.isInvalid() || SM.findBufferContainingLoc(R.getStart()) != BufferID) @@ -177,16 +176,16 @@ void EditorDiagConsumer::handleDiagnostic( return; } - switch (Kind) { - case DiagnosticKind::Error: - SKInfo.Severity = DiagnosticSeverityKind::Error; - break; - case DiagnosticKind::Warning: - SKInfo.Severity = DiagnosticSeverityKind::Warning; - break; - case DiagnosticKind::Note: - case DiagnosticKind::Remark: - llvm_unreachable("already covered"); + switch (Info.Kind) { + case DiagnosticKind::Error: + SKInfo.Severity = DiagnosticSeverityKind::Error; + break; + case DiagnosticKind::Warning: + SKInfo.Severity = DiagnosticSeverityKind::Warning; + break; + case DiagnosticKind::Note: + case DiagnosticKind::Remark: + llvm_unreachable("already covered"); } if (!BufferIDOpt) { @@ -701,6 +700,7 @@ class SwiftDocumentSyntaxInfo { Parser.reset( new ParserUnit(SM, SourceFileKind::Main, BufferID, CompInv.getLangOptions(), + CompInv.getTypeCheckerOptions(), CompInv.getModuleName(), SynTreeCreator, CompInv.getMainFileSyntaxParsingCache()) @@ -1120,6 +1120,76 @@ static UIdent getAccessLevelUID(AccessLevel Access) { llvm_unreachable("Unhandled access level in switch."); } +static Optional +inferDefaultAccessSyntactically(const ExtensionDecl *ED) { + // Check if the extension has an explicit access control attribute. + if (auto *AA = ED->getAttrs().getAttribute()) + return std::min(std::max(AA->getAccess(), AccessLevel::FilePrivate), + AccessLevel::Public); + return None; +} + +/// Document structure is a purely syntactic request that shouldn't require name lookup +/// or type-checking, so this is a best-effort computation, particularly where extensions +/// are concerned. +static Optional inferAccessSyntactically(const ValueDecl *D) { + assert(D); + + // Check if the decl has an explicit access control attribute. + if (auto *AA = D->getAttrs().getAttribute()) + return AA->getAccess(); + + DeclContext *DC = D->getDeclContext(); + + if (D->getKind() == DeclKind::Destructor || + D->getKind() == DeclKind::EnumElement) { + if (auto container = dyn_cast(D->getDeclContext())) { + if (auto containerAccess = inferAccessSyntactically(container)) + return std::max(containerAccess.getValue(), AccessLevel::Internal); + return None; + } + return AccessLevel::Private; + } + + switch (DC->getContextKind()) { + case DeclContextKind::TopLevelCodeDecl: + return AccessLevel::FilePrivate; + case DeclContextKind::SerializedLocal: + case DeclContextKind::AbstractClosureExpr: + case DeclContextKind::EnumElementDecl: + case DeclContextKind::Initializer: + case DeclContextKind::AbstractFunctionDecl: + case DeclContextKind::SubscriptDecl: + return AccessLevel::Private; + case DeclContextKind::Module: + case DeclContextKind::FileUnit: + return AccessLevel::Internal; + case DeclContextKind::GenericTypeDecl: { + auto generic = cast(DC); + AccessLevel access = AccessLevel::Internal; + if (isa(generic)) { + if (auto protoAccess = inferAccessSyntactically(generic)) + access = std::max(AccessLevel::FilePrivate, protoAccess.getValue()); + } + return access; + } + case DeclContextKind::ExtensionDecl: + auto *ED = cast(DC); + return inferDefaultAccessSyntactically(ED); + } + + llvm_unreachable("Unhandled DeclContextKind in switch."); +} + +static Optional +inferSetterAccessSyntactically(const AbstractStorageDecl *D) { + if (!D->isSettable(/*UseDC=*/nullptr)) + return None; + if (auto *AA = D->getAttrs().getAttribute()) + return AA->getAccess(); + return inferAccessSyntactically(D); +} + class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { SourceManager &SrcManager; EditorConsumer &Consumer; @@ -1176,16 +1246,15 @@ class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { Node.Kind != SyntaxStructureKind::LocalVariable && Node.Kind != SyntaxStructureKind::GenericTypeParam) { if (auto *VD = dyn_cast_or_null(Node.Dcl)) { - AccessLevel = getAccessLevelUID(VD->getFormalAccess()); + if (auto Access = inferAccessSyntactically(VD)) + AccessLevel = getAccessLevelUID(Access.getValue()); } else if (auto *ED = dyn_cast_or_null(Node.Dcl)) { - auto StrictAccess = ED->getDefaultAccessLevel(); - AccessLevel = getAccessLevelUID(StrictAccess); + if (auto DefaultAccess = inferDefaultAccessSyntactically(ED)) + AccessLevel = getAccessLevelUID(DefaultAccess.getValue()); } if (auto *ASD = dyn_cast_or_null(Node.Dcl)) { - if (ASD->isSettable(/*UseDC=*/nullptr)) { - swift::AccessLevel SetAccess = ASD->getSetterFormalAccess(); - SetterAccessLevel = getAccessLevelUID(SetAccess); - } + if (auto SetAccess = inferSetterAccessSyntactically(ASD)) + SetterAccessLevel = getAccessLevelUID(SetAccess.getValue()); } } @@ -1265,12 +1334,12 @@ class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { } StringRef getObjCRuntimeName(const Decl *D, SmallString<64> &Buf) { - if (!D || D->isInvalid()) + if (!D) return StringRef(); if (!isa(D) && !isa(D)) return StringRef(); auto *VD = cast(D); - if (!VD->hasName()) + if (!VD->hasName() || (VD->hasInterfaceType() && VD->isInvalid())) return StringRef(); auto ident = VD->getBaseName().getIdentifier().str(); if (ident.empty() || Mangle::isDigit(ident.front())) @@ -1905,6 +1974,7 @@ void SwiftEditorDocument::readSyntaxInfo(EditorConsumer &Consumer) { void SwiftEditorDocument::readSemanticInfo(ImmutableTextSnapshotRef Snapshot, EditorConsumer& Consumer) { + llvm::sys::ScopedLock L(Impl.AccessMtx); std::vector SemaToks; Optional> SemaDiags; Impl.SemanticInfo->readSemanticInfo(Snapshot, SemaToks, SemaDiags, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorDiagConsumer.h b/tools/SourceKit/lib/SwiftLang/SwiftEditorDiagConsumer.h index 896abd851f4f0..393cc20d16eab 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorDiagConsumer.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorDiagConsumer.h @@ -66,11 +66,8 @@ class EditorDiagConsumer : public swift::DiagnosticConsumer { bool hadAnyError() const { return HadAnyError; } - void handleDiagnostic(swift::SourceManager &SM, swift::SourceLoc Loc, - swift::DiagnosticKind Kind, StringRef FormatString, - ArrayRef FormatArgs, - const swift::DiagnosticInfo &Info, - swift::SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(swift::SourceManager &SM, + const swift::DiagnosticInfo &Info) override; }; } // namespace SourceKit diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index a331e2051d61c..475629e4a22db 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -92,7 +92,7 @@ typedef SwiftInterfaceGenContext::Implementation::TextDecl TextDecl; typedef SwiftInterfaceGenContext::Implementation::SourceTextInfo SourceTextInfo; static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -103,7 +103,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) { } static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) { - return Ctx.getModule(std::make_pair(ModuleName, SourceLoc())); + return Ctx.getModule({ Located(ModuleName, SourceLoc()) }); } namespace { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftIndexing.cpp b/tools/SourceKit/lib/SwiftLang/SwiftIndexing.cpp index 3882f0744dcf0..8c74396273932 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftIndexing.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftIndexing.cpp @@ -184,7 +184,8 @@ static void indexModule(llvm::MemoryBuffer *Input, // documentation file. // FIXME: refactor the frontend to provide an easy way to figure out the // correct filename here. - auto FUnit = Loader->loadAST(*Mod, None, std::move(Buf), nullptr, nullptr, + auto FUnit = Loader->loadAST(*Mod, None, /*moduleInterfacePath*/"", + std::move(Buf), nullptr, nullptr, /*isFramework*/false, /*treatAsPartialModule*/false); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index 96b7a55b05e75..503d9f303bcde 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -12,6 +12,7 @@ #include "SwiftLangSupport.h" #include "SwiftASTManager.h" +#include "SwiftEditorDiagConsumer.h" #include "SourceKit/Core/Context.h" #include "SourceKit/SwiftLang/Factory.h" #include "SourceKit/Support/FileSystemProvider.h" @@ -23,8 +24,10 @@ #include "swift/AST/SILOptions.h" #include "swift/AST/USRGeneration.h" #include "swift/Config.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CodeCompletionCache.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/Utils.h" @@ -263,8 +266,12 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) Stats = std::make_shared(); EditorDocuments = std::make_shared(); - ASTMgr = std::make_shared(EditorDocuments, Stats, - RuntimeResourcePath); + ASTMgr = std::make_shared(EditorDocuments, + SKCtx.getGlobalConfiguration(), + Stats, RuntimeResourcePath); + + CompletionInst = std::make_unique(); + // By default, just use the in-memory cache. CCCache->inMemory = llvm::make_unique(); @@ -275,26 +282,6 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SwiftLangSupport::~SwiftLangSupport() { } -std::unique_ptr -SwiftLangSupport::makeCodeCompletionMemoryBuffer( - const llvm::MemoryBuffer *origBuf, unsigned &Offset, - const std::string bufferIdentifier) { - - auto origBuffSize = origBuf->getBufferSize(); - if (Offset > origBuffSize) - Offset = origBuffSize; - - auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( - origBuffSize + 1, bufferIdentifier); - auto *pos = origBuf->getBufferStart() + Offset; - auto *newPos = - std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart()); - *newPos = '\0'; - std::copy(pos, origBuf->getBufferEnd(), newPos + 1); - - return std::unique_ptr(newBuffer.release()); -} - UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) { return UIdentVisitor(IsRef).visit(const_cast(D)); } @@ -911,36 +898,10 @@ void SwiftLangSupport::printMemberDeclDescription(const swift::ValueDecl *VD, std::string SwiftLangSupport::resolvePathSymlinks(StringRef FilePath) { std::string InputPath = FilePath; -#if !defined(_WIN32) - char full_path[MAXPATHLEN]; - if (const char *path = realpath(InputPath.c_str(), full_path)) - return path; - - return InputPath; -#else - wchar_t full_path[MAX_PATH] = {0}; - llvm::SmallVector utf16Path; - llvm::convertUTF8ToUTF16String(InputPath.c_str(), utf16Path); - - HANDLE fileHandle = CreateFileW( - (LPCWSTR)utf16Path.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - - if (fileHandle == INVALID_HANDLE_VALUE) + llvm::SmallString<256> output; + if (llvm::sys::fs::real_path(InputPath, output)) return InputPath; - - DWORD numChars = GetFinalPathNameByHandleW(fileHandle, full_path, MAX_PATH, - FILE_NAME_NORMALIZED); - CloseHandle(fileHandle); - std::string utf8Path; - if (numChars > 0 && numChars <= MAX_PATH) { - llvm::ArrayRef pathRef((const char *)full_path, - (const char *)(full_path + numChars)); - return llvm::convertUTF16ToUTF8String(pathRef, utf8Path) ? utf8Path - : InputPath; - } - return InputPath; -#endif + return output.str(); } void SwiftLangSupport::getStatistics(StatisticsReceiver receiver) { @@ -991,6 +952,70 @@ SwiftLangSupport::getFileSystem(const Optional &vfsOptions, return llvm::vfs::getRealFileSystem(); } +bool SwiftLangSupport::performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error, + llvm::function_ref Callback) { + assert(FileSystem); + + // Resolve symlinks for the input file; we resolve them for the input files + // in the arguments as well. + // FIXME: We need the Swift equivalent of Clang's FileEntry. + llvm::SmallString<128> bufferIdentifier; + if (auto err = FileSystem->getRealPath( + UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier)) + bufferIdentifier = UnresolvedInputFile->getBufferIdentifier(); + + // Create a buffer for code completion. This contains '\0' at 'Offset' + // position of 'UnresolvedInputFile' buffer. + auto origOffset = Offset; + auto newBuffer = ide::makeCodeCompletionMemoryBuffer( + UnresolvedInputFile, Offset, bufferIdentifier); + + SourceManager SM; + DiagnosticEngine Diags(SM); + PrintingDiagnosticConsumer PrintDiags; + EditorDiagConsumer TraceDiags; + trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion}; + + Diags.addConsumer(PrintDiags); + if (TracedOp.enabled()) { + Diags.addConsumer(TraceDiags); + trace::SwiftInvocation SwiftArgs; + trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); + TracedOp.setDiagnosticProvider( + [&](SmallVectorImpl &diags) { + TraceDiags.getAllDiagnostics(diags); + }); + TracedOp.start( + SwiftArgs, + {std::make_pair("OriginalOffset", std::to_string(origOffset)), + std::make_pair("Offset", std::to_string(Offset))}); + } + ForwardingDiagnosticConsumer CIDiags(Diags); + + CompilerInvocation Invocation; + bool Failed = getASTManager()->initCompilerInvocation( + Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem, + Error); + if (Failed) + return false; + if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { + Error = "no input filenames specified"; + return false; + } + + // Pin completion instance. + auto CompletionInst = getCompletionInstance(); + + return CompletionInst->performOperation(Invocation, Args, FileSystem, + newBuffer.get(), Offset, + EnableASTCaching, Error, + &CIDiags, Callback); +} + CloseClangModuleFiles::~CloseClangModuleFiles() { clang::Preprocessor &PP = loader.getClangPreprocessor(); clang::ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index f53fd174cdd36..2c37911224119 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -50,6 +50,7 @@ namespace syntax { namespace ide { class CodeCompletionCache; + class CompletionInstance; class OnDiskCodeCompletionCache; class SourceEditConsumer; enum class CodeCompletionDeclKind; @@ -258,12 +259,8 @@ class RequestRefactoringEditConsumer: public swift::ide::SourceEditConsumer, ~RequestRefactoringEditConsumer(); void accept(swift::SourceManager &SM, swift::ide::RegionType RegionType, ArrayRef Replacements) override; - void - handleDiagnostic(swift::SourceManager &SM, swift::SourceLoc Loc, - swift::DiagnosticKind Kind, StringRef FormatString, - ArrayRef FormatArgs, - const swift::DiagnosticInfo &Info, - swift::SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(swift::SourceManager &SM, + const swift::DiagnosticInfo &Info) override; }; class RequestRenameRangeConsumer : public swift::ide::FindRenameRangesConsumer, @@ -276,12 +273,8 @@ class RequestRenameRangeConsumer : public swift::ide::FindRenameRangesConsumer, ~RequestRenameRangeConsumer(); void accept(swift::SourceManager &SM, swift::ide::RegionType RegionType, ArrayRef Ranges) override; - void - handleDiagnostic(swift::SourceManager &SM, swift::SourceLoc Loc, - swift::DiagnosticKind Kind, StringRef FormatString, - ArrayRef FormatArgs, - const swift::DiagnosticInfo &Info, - swift::SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(swift::SourceManager &SM, + const swift::DiagnosticInfo &Info) override; }; struct SwiftStatistics { @@ -302,6 +295,7 @@ class SwiftLangSupport : public LangSupport { ThreadSafeRefCntPtr CustomCompletions; std::shared_ptr Stats; llvm::StringMap> FileSystemProviders; + std::shared_ptr CompletionInst; public: explicit SwiftLangSupport(SourceKit::Context &SKCtx); @@ -321,6 +315,10 @@ class SwiftLangSupport : public LangSupport { return CCCache; } + std::shared_ptr getCompletionInstance() { + return CompletionInst; + } + /// Returns the FileSystemProvider registered under Name, or nullptr if not /// found. FileSystemProvider *getFileSystemProvider(StringRef Name); @@ -350,13 +348,6 @@ class SwiftLangSupport : public LangSupport { getFileSystem(const Optional &vfsOptions, Optional primaryFile, std::string &error); - /// Copy a memory buffer inserting '0' at the position of \c origBuf. - // TODO: Share with code completion. - static std::unique_ptr - makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, - unsigned &Offset, - const std::string bufferIdentifier); - static SourceKit::UIdent getUIDForDecl(const swift::Decl *D, bool IsRef = false); static SourceKit::UIdent getUIDForExtensionOfDecl(const swift::Decl *D); @@ -445,6 +436,16 @@ class SwiftLangSupport : public LangSupport { /// returns the original path; static std::string resolvePathSymlinks(StringRef FilePath); + /// Perform a completion like operation. It initializes a \c CompilerInstance, + /// the calls \p Callback with it. \p Callback must perform the second pass + /// using that instance. + bool performCompletionLikeOperation( + llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error, + llvm::function_ref Callback); + //==========================================================================// // LangSupport Interface //==========================================================================// @@ -454,6 +455,7 @@ class SwiftLangSupport : public LangSupport { void codeComplete( llvm::MemoryBuffer *InputBuf, unsigned Offset, + OptionsDictionary *options, SourceKit::CodeCompletionConsumer &Consumer, ArrayRef Args, Optional vfsOptions) override; @@ -593,12 +595,14 @@ class SwiftLangSupport : public LangSupport { void getExpressionContextInfo(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, - TypeContextInfoConsumer &Consumer) override; + TypeContextInfoConsumer &Consumer, + Optional vfsOptions) override; void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset, ArrayRef Args, ArrayRef ExpectedTypes, - ConformingMethodListConsumer &Consumer) override; + ConformingMethodListConsumer &Consumer, + Optional vfsOptions) override; void getStatistics(StatisticsReceiver) override; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 74e8ee8c05978..92db039a37e85 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/GenericSignature.h" #include "swift/Basic/SourceManager.h" @@ -495,10 +496,14 @@ void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) { // FIXME: Extract useful related declarations, overloaded functions, // if VD is an initializer, we should extract other initializers etc. - // For now we use UnqualifiedLookup to fetch other declarations with the same + // For now we use unqualified lookup to fetch other declarations with the same // base name. - UnqualifiedLookup Lookup(VD->getBaseName(), VD->getDeclContext()); - for (auto result : Lookup.Results) { + auto &ctx = VD->getASTContext(); + auto descriptor = UnqualifiedLookupDescriptor(DeclNameRef(VD->getBaseName()), + VD->getDeclContext()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); + for (auto result : lookup) { ValueDecl *RelatedVD = result.getValueDecl(); if (RelatedVD->getAttrs().isUnavailable(VD->getASTContext())) continue; @@ -778,12 +783,11 @@ static bool passCursorInfoForDecl(SourceFile* SF, unsigned USREnd = SS.size(); unsigned TypenameBegin = SS.size(); - if (auto vdType = VD->getInterfaceType()) { - llvm::raw_svector_ostream OS(SS); - PrintOptions Options; - Options.PrintTypeAliasUnderlyingType = true; - vdType.print(OS, Options); - } + llvm::raw_svector_ostream OS(SS); + PrintOptions Options; + Options.PrintTypeAliasUnderlyingType = true; + VD->getInterfaceType().print(OS, Options); + unsigned TypenameEnd = SS.size(); unsigned MangledTypeStart = SS.size(); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp index af7edc90b91bc..17ae2701f3451 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp @@ -15,6 +15,7 @@ #include "SwiftEditorDiagConsumer.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/TypeContextInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Comment.h" @@ -24,72 +25,38 @@ using namespace SourceKit; using namespace swift; using namespace ide; -static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang, - llvm::MemoryBuffer *UnresolvedInputFile, - unsigned Offset, - ArrayRef Args, - ide::TypeContextInfoConsumer &Consumer, - std::string &Error) { - auto bufferIdentifier = - Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier()); - - auto origOffset = Offset; - auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer( - UnresolvedInputFile, Offset, bufferIdentifier); - - CompilerInstance CI; - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - - EditorDiagConsumer TraceDiags; - trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion); - if (TracedOp.enabled()) { - CI.addDiagnosticConsumer(&TraceDiags); - trace::SwiftInvocation SwiftArgs; - trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args); - TracedOp.setDiagnosticProvider( - [&TraceDiags](SmallVectorImpl &diags) { - TraceDiags.getAllDiagnostics(diags); - }); - TracedOp.start( - SwiftArgs, - {std::make_pair("OriginalOffset", std::to_string(origOffset)), - std::make_pair("Offset", std::to_string(Offset))}); - } - - CompilerInvocation Invocation; - bool Failed = Lang.getASTManager()->initCompilerInvocation( - Invocation, Args, CI.getDiags(), bufferIdentifier, Error); - if (Failed) - return false; - if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) { - Error = "no input filenames specified"; - return false; - } - - Invocation.setCodeCompletionPoint(newBuffer.get(), Offset); - - // Create a factory for code completion callbacks that will feed the - // Consumer. - std::unique_ptr callbacksFactory( - ide::makeTypeContextInfoCallbacksFactory(Consumer)); - - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - - if (CI.setup(Invocation)) { - // FIXME: error? - return true; - } - registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - - return true; +static bool swiftTypeContextInfoImpl( + SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile, + unsigned Offset, ide::TypeContextInfoConsumer &Consumer, + ArrayRef Args, + llvm::IntrusiveRefCntPtr FileSystem, + bool EnableASTCaching, std::string &Error) { + return Lang.performCompletionLikeOperation( + UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error, + [&](CompilerInstance &CI) { + // Create a factory for code completion callbacks that will feed the + // Consumer. + std::unique_ptr callbacksFactory( + ide::makeTypeContextInfoCallbacksFactory(Consumer)); + + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); } void SwiftLangSupport::getExpressionContextInfo( llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset, ArrayRef Args, - SourceKit::TypeContextInfoConsumer &SKConsumer) { + SourceKit::TypeContextInfoConsumer &SKConsumer, + Optional vfsOptions) { + std::string error; + + // FIXME: the use of None as primary file is to match the fact we do not read + // the document contents using the editor documents infrastructure. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return SKConsumer.failed(error); + class Consumer : public ide::TypeContextInfoConsumer { SourceKit::TypeContextInfoConsumer &SKConsumer; @@ -183,9 +150,9 @@ void SwiftLangSupport::getExpressionContextInfo( } } Consumer(SKConsumer); - std::string Error; - if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Args, - Consumer, Error)) { - SKConsumer.failed(Error); + if (!swiftTypeContextInfoImpl(*this, UnresolvedInputFile, Offset, Consumer, + Args, fileSystem, /*EnableASTCaching=*/false, + error)) { + SKConsumer.failed(error); } } diff --git a/tools/SourceKit/tools/CMakeLists.txt b/tools/SourceKit/tools/CMakeLists.txt index 6c89dc8a6c92b..ad651d0d6fb78 100644 --- a/tools/SourceKit/tools/CMakeLists.txt +++ b/tools/SourceKit/tools/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories( add_swift_lib_subdirectory(sourcekitd) add_swift_tool_subdirectory(sourcekitd-test) -if(HAVE_UNICODE_LIBEDIT) +if(LibEdit_FOUND AND LibEdit_HAS_UNICODE) add_swift_tool_subdirectory(sourcekitd-repl) endif() add_swift_tool_subdirectory(complete-test) diff --git a/tools/SourceKit/tools/complete-test/CMakeLists.txt b/tools/SourceKit/tools/complete-test/CMakeLists.txt index f65a890c4c2b6..ec3437650a0bb 100644 --- a/tools/SourceKit/tools/complete-test/CMakeLists.txt +++ b/tools/SourceKit/tools/complete-test/CMakeLists.txt @@ -1,24 +1,24 @@ -if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_TEST_LINK_LIBS sourcekitdInProc) -else() - set(SOURCEKITD_TEST_LINK_LIBS sourcekitd) -endif() - add_sourcekit_executable(complete-test complete-test.cpp - LINK_LIBS ${SOURCEKITD_TEST_LINK_LIBS} - LLVM_LINK_COMPONENTS support option coverage lto + LLVM_LINK_COMPONENTS option coverage lto ) -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(complete-test PRIVATE dispatch BlocksRuntime) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(complete-test PRIVATE sourcekitdInProc) +else() + target_link_libraries(complete-test PRIVATE sourcekitd) endif() - -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set_target_properties(complete-test - PROPERTIES - LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(complete-test PRIVATE + dispatch + BlocksRuntime) endif() +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(complete-test PROPERTIES + INSTALL_RPATH "@executable_path/../lib") + target_link_options(complete-test PRIVATE + "LINKER:-exported_symbol,_main") +endif() if(SWIFT_ANALYZE_CODE_COVERAGE) set_property(TARGET complete-test APPEND_STRING PROPERTY LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") @@ -26,6 +26,4 @@ endif() add_dependencies(tools complete-test) swift_install_in_component(TARGETS complete-test - RUNTIME - DESTINATION bin - COMPONENT tools) + RUNTIME DESTINATION bin COMPONENT tools) diff --git a/tools/SourceKit/tools/complete-test/complete-test.cpp b/tools/SourceKit/tools/complete-test/complete-test.cpp index 872e083f0cace..3579a813e3112 100644 --- a/tools/SourceKit/tools/complete-test/complete-test.cpp +++ b/tools/SourceKit/tools/complete-test/complete-test.cpp @@ -65,6 +65,7 @@ struct TestOptions { Optional fuzzyWeight; Optional popularityBonus; StringRef filterRulesJSON; + std::string moduleCachePath; bool rawOutput = false; bool structureOutput = false; ArrayRef compilerArgs; @@ -251,6 +252,8 @@ static bool parseOptions(ArrayRef args, TestOptions &options, return false; } options.showTopNonLiteral = uval; + } else if (opt == "module-cache-path") { + options.moduleCachePath = value; } } @@ -392,7 +395,6 @@ removeCodeCompletionTokens(StringRef Input, StringRef TokenName, if (match[1].str() != TokenName) continue; *CompletionOffset = CleanFile.size(); - CleanFile.push_back('\0'); if (match.size() == 2 || !match[2].matched) continue; @@ -674,6 +676,10 @@ static bool codeCompleteRequest(sourcekitd_uid_t requestUID, const char *name, sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND,"-sdk"); sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, sdk); } + if (!options.moduleCachePath.empty()) { + sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, "-module-cache-path"); + sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, options.moduleCachePath.c_str()); + } // Add -- options. for (const char *arg : options.compilerArgs) sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, arg); diff --git a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt index 32e49a17b6e7f..d4a3a77df2ac8 100644 --- a/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-repl/CMakeLists.txt @@ -1,35 +1,31 @@ -set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} edit) -check_symbol_exists(el_wgets "histedit.h" HAVE_UNICODE_LIBEDIT) - -if(HAVE_UNICODE_LIBEDIT) - if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_REPL_LINK_LIBS sourcekitdInProc) - else() - set(SOURCEKITD_REPL_LINK_LIBS sourcekitd) - endif() - - add_sourcekit_executable(sourcekitd-repl - sourcekitd-repl.cpp - LINK_LIBS ${SOURCEKITD_REPL_LINK_LIBS} edit - LLVM_LINK_COMPONENTS support coverage lto - ) - if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(sourcekitd-repl PRIVATE dispatch BlocksRuntime) - endif() - - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set_target_properties(sourcekitd-repl - PROPERTIES - LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") - endif() - if(SWIFT_ANALYZE_CODE_COVERAGE) - set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY - LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") - endif() +add_sourcekit_executable(sourcekitd-repl + sourcekitd-repl.cpp + LLVM_LINK_COMPONENTS coverage lto +) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-repl PRIVATE sourcekitdInProc) +else() + target_link_libraries(sourcekitd-repl PRIVATE sourcekitd) +endif() +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-repl PRIVATE + dispatch + BlocksRuntime) +endif() +target_link_libraries(sourcekitd-repl PRIVATE + libedit) - add_dependencies(tools sourcekitd-repl) - swift_install_in_component(TARGETS sourcekitd-repl - RUNTIME - DESTINATION bin - COMPONENT tools) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(sourcekitd-repl PROPERTIES + INSTALL_RPATH "@executable_path/../lib") + target_link_options(sourcekitd-repl PRIVATE + "LINKER:-exported_symbol,_main") +endif() +if(SWIFT_ANALYZE_CODE_COVERAGE) + set_property(TARGET sourcekitd-repl APPEND_STRING PROPERTY + LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") endif() + +add_dependencies(tools sourcekitd-repl) +swift_install_in_component(TARGETS sourcekitd-repl + RUNTIME DESTINATION bin COMPONENT tools) diff --git a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt index 09f1106705a89..cf69c7b9c6e82 100644 --- a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt @@ -2,31 +2,35 @@ set(LLVM_TARGET_DEFINITIONS Options.td) swift_tablegen(Options.inc -gen-opt-parser-defs) swift_add_public_tablegen_target(sourcekitdTestOptionsTableGen) -if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - set(SOURCEKITD_TEST_LINK_LIBS sourcekitdInProc) -else() - set(SOURCEKITD_TEST_LINK_LIBS sourcekitd) -endif() - add_sourcekit_executable(sourcekitd-test sourcekitd-test.cpp TestOptions.cpp - LINK_LIBS ${SOURCEKITD_TEST_LINK_LIBS} SourceKitSupport - clangRewrite clangLex clangBasic - LLVM_LINK_COMPONENTS core support option coverage lto + LLVM_LINK_COMPONENTS core option coverage lto ) -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(sourcekitd-test PRIVATE dispatch BlocksRuntime) +target_link_libraries(sourcekitd-test PRIVATE + SourceKitSupport + clangRewrite + clangLex + clangBasic) +if(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) + target_link_libraries(sourcekitd-test PRIVATE sourcekitdInProc) +else() + target_link_libraries(sourcekitd-test PRIVATE sourcekitd) +endif() +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(sourcekitd-test PRIVATE + dispatch + BlocksRuntime) endif() add_dependencies(sourcekitd-test sourcekitdTestOptionsTableGen) -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set_target_properties(sourcekitd-test - PROPERTIES - LINK_FLAGS "-Wl,-rpath -Wl,@executable_path/../lib") +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(sourcekitd-test PROPERTIES + INSTALL_RPATH "@executable_path/../lib") + target_link_options(sourcekitd-test PRIVATE + "LINKER:-exported_symbol,_main") endif() - if(SWIFT_ANALYZE_CODE_COVERAGE) set_property(TARGET sourcekitd-test APPEND_STRING PROPERTY LINK_FLAGS " -fprofile-instr-generate -fcoverage-mapping") @@ -34,6 +38,4 @@ endif() add_dependencies(tools sourcekitd-test) swift_install_in_component(TARGETS sourcekitd-test - RUNTIME - DESTINATION bin - COMPONENT tools) + RUNTIME DESTINATION bin COMPONENT tools) diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index 53b5c9573bef6..ee21922a9c19b 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -140,6 +140,15 @@ def vfs_files : CommaJoined<["-"], "vfs-files=">, def vfs_name : Separate<["-"], "vfs-name">, HelpText<"Specify a virtual filesystem name">; +def optimize_for_ide : Joined<["-"], "for-ide=">, + HelpText<"Value for the OptimizeForIde global configuration setting">; + +def suppress_config_request : Flag<["-"], "suppress-config-request">, + HelpText<"Suppress the default global configuration request, that is otherwise sent before any other request (except for the global-config request itself)">; + +def module_cache_path: Separate<["-"], "module-cache-path">, HelpText<"module cache path">; +def module_cache_path_EQ : Joined<["-"], "module-cache-path=">, Alias; + def help : Flag<["-", "--"], "help">, HelpText<"Display available options">; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index be2d900e842a6..7a70a621246ff 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -16,6 +16,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace sourcekitd_test; @@ -153,6 +154,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("stats", SourceKitRequest::Statistics) .Case("track-compiles", SourceKitRequest::EnableCompileNotifications) .Case("collect-type", SourceKitRequest::CollectExpresstionType) + .Case("global-config", SourceKitRequest::GlobalConfiguration) .Default(SourceKitRequest::None); if (Request == SourceKitRequest::None) { @@ -358,8 +360,10 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { for (const char *vfsFile : InputArg->getValues()) { StringRef name, target; std::tie(name, target) = StringRef(vfsFile).split('='); + llvm::SmallString<64> nativeName; + llvm::sys::path::native(name, nativeName); bool passAsSourceText = target.consume_front("@"); - VFSFiles.try_emplace(name, VFSFile(target.str(), passAsSourceText)); + VFSFiles.try_emplace(nativeName.str(), VFSFile(target.str(), passAsSourceText)); } break; @@ -367,6 +371,24 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { VFSName = InputArg->getValue(); break; + case OPT_optimize_for_ide: { + bool Value; + if (StringRef(InputArg->getValue()).getAsInteger(10, Value)) { + llvm::errs() << "error: expected 0 or 1 for 'for-ide'\n"; + return true; + } + OptimizeForIde = Value; + break; + } + + case OPT_suppress_config_request: + SuppressDefaultConfigRequest = true; + break; + + case OPT_module_cache_path: + ModuleCachePath = InputArg->getValue(); + break; + case OPT_UNKNOWN: llvm::errs() << "error: unknown argument: " << InputArg->getAsString(ParsedArgs) << '\n' diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index f854a241836b3..0be899ad16597 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -64,6 +64,7 @@ enum class SourceKitRequest { SyntaxTree, EnableCompileNotifications, CollectExpresstionType, + GlobalConfiguration, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, #include "swift/IDE/RefactoringKinds.def" }; @@ -93,6 +94,7 @@ struct TestOptions { std::string CachePath; llvm::SmallVector RequestOptions; llvm::ArrayRef CompilerArgs; + std::string ModuleCachePath; bool UsingSwiftArgs; std::string USR; std::string SwiftName; @@ -110,6 +112,8 @@ struct TestOptions { bool CollectActionables = false; bool isAsyncRequest = false; bool timeRequest = false; + llvm::Optional OptimizeForIde; + bool SuppressDefaultConfigRequest = false; unsigned repeatRequest = 1; struct VFSFile { std::string path; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 27c5c431c25b2..2188f711a48dc 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Threading.h" @@ -49,39 +50,12 @@ using namespace sourcekitd_test; #if defined(_WIN32) namespace { int STDOUT_FILENO = _fileno(stdout); -const constexpr size_t MAXPATHLEN = MAX_PATH + 1; -char *realpath(const char *path, char *resolved_path) { - wchar_t full_path[MAXPATHLEN] = {0}; - llvm::SmallVector utf16Path; - llvm::convertUTF8ToUTF16String(path, utf16Path); - - HANDLE fileHandle = CreateFileW( - (LPCWSTR)utf16Path.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - - if (fileHandle == INVALID_HANDLE_VALUE) - return nullptr; - DWORD success = GetFinalPathNameByHandleW(fileHandle, full_path, MAX_PATH, - FILE_NAME_NORMALIZED); - CloseHandle(fileHandle); - if (!success) return nullptr; - - std::string utf8Path; - llvm::ArrayRef pathRef((const char *)full_path, - (const char *)(full_path + MAX_PATH)); - if (!llvm::convertUTF16ToUTF8String(pathRef, utf8Path)) - return nullptr; - - if (!resolved_path) { - resolved_path = static_cast(malloc(utf8Path.length() + 1)); - } - std::copy(std::begin(utf8Path), std::end(utf8Path), resolved_path); - return resolved_path; -} } #endif -static int handleTestInvocation(ArrayRef Args, TestOptions &InitOpts); +static bool sendGlobalConfigRequest(); +static int handleTestInvocation(ArrayRef Args, TestOptions &InitOpts, + bool IsFirstInvocation); static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, const std::string &SourceFile, std::unique_ptr SourceBuf, @@ -266,6 +240,7 @@ static void skt_main(skt_args *args) { // invocations. TestOptions InitOpts; auto Args = llvm::makeArrayRef(argv+1, argc-1); + bool firstInvocation = true; while (1) { unsigned i = 0; for (auto Arg: Args) { @@ -275,15 +250,17 @@ static void skt_main(skt_args *args) { } if (i == Args.size()) break; - if (int ret = handleTestInvocation(Args.slice(0, i), InitOpts)) { + if (int ret = handleTestInvocation(Args.slice(0, i), InitOpts, + firstInvocation)) { sourcekitd_shutdown(); args->ret = ret; return; } Args = Args.slice(i + 1); + firstInvocation = false; } - if (int ret = handleTestInvocation(Args, InitOpts)) { + if (int ret = handleTestInvocation(Args, InitOpts, firstInvocation)) { sourcekitd_shutdown(); args->ret = ret; return; @@ -312,6 +289,7 @@ static inline std::string getInterfaceGenDocumentName() { // "Absolute path" on all platforms since handleTestInvocation will attempt to make this absolute llvm::SmallString<64> path = llvm::StringRef("/"); llvm::sys::fs::make_absolute(path); + llvm::sys::path::native(path); return path.str(); } @@ -412,7 +390,7 @@ static int handleJsonRequestPath(StringRef QueryPath, const TestOptions &Opts) { static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts); static int handleTestInvocation(ArrayRef Args, - TestOptions &InitOpts) { + TestOptions &InitOpts, bool firstInvocation) { unsigned Optargc = 0; for (auto Arg: Args) { @@ -425,9 +403,22 @@ static int handleTestInvocation(ArrayRef Args, if (Opts.parseArgs(Args.slice(0, Optargc))) return 1; + if (!Opts.ModuleCachePath.empty()) + InitOpts.ModuleCachePath = Opts.ModuleCachePath; + if (Optargc < Args.size()) Opts.CompilerArgs = Args.slice(Optargc+1); + if (firstInvocation && Opts.Request != SourceKitRequest::GlobalConfiguration && + !Opts.SuppressDefaultConfigRequest) { + // We don't fail if this request fails for now so that sourcekitd-test is + // still usable with older versions of sourcekitd that don't have the + // global-configuration request. + if (sendGlobalConfigRequest()) { + llvm::outs() << "warning: global configuration request failed\n"; + } + } + assert(Opts.repeatRequest >= 1); for (unsigned i = 0; i < Opts.repeatRequest; ++i) { if (int ret = handleTestInvocation(Opts, InitOpts)) { @@ -462,6 +453,28 @@ static int setExpectedTypes(const sourcekitd_test::TestOptions &Opts, return 0; } +static bool sendGlobalConfigRequest() { + TestOptions Opts; + sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr, + nullptr, 0); + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); + + // For test invocations we default to setting OptimizeForIDE to true. This + // matches the use case of the most popular clients of sourcekitd (editors) + // and also disables loading locations from .swiftsourceinfo files. This is + // desirable for testing because the .swiftsourceinfo for the stdlib is + // available when sourcekitd is tested, and can make some stdlib-dependent + // sourcekitd tests unstable due to changing source locations from the stdlib + // module. + sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast(true)); + sourcekitd_response_t Resp = sendRequestSync(Req, Opts); + bool IsError = sourcekitd_response_is_error(Resp); + if (IsError) + sourcekitd_response_description_dump(Resp); + sourcekitd_request_release(Req); + return IsError; +} + static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (!Opts.JsonRequestPath.empty()) return handleJsonRequestPath(Opts.JsonRequestPath, Opts); @@ -475,6 +488,7 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { llvm::SmallString<64> AbsSourceFile; AbsSourceFile += SourceFile; llvm::sys::fs::make_absolute(AbsSourceFile); + llvm::sys::path::native(AbsSourceFile); SourceFile = AbsSourceFile.str(); } std::string SemaName = !Opts.Name.empty() ? Opts.Name : SourceFile; @@ -504,6 +518,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { ByteOffset; } + bool compilerArgsAreClang = false; + sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0); ActiveRequest = Opts.Request; @@ -515,6 +531,12 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { // with a zero (successful) exit code. return 1; + case SourceKitRequest::GlobalConfiguration: + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); + if (Opts.OptimizeForIde.hasValue()) + sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast(Opts.OptimizeForIde.getValue())); + break; + case SourceKitRequest::ProtocolVersion: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestProtocolVersion); break; @@ -556,6 +578,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::CodeComplete: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCodeComplete); sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); + addCodeCompleteOptions(Req, Opts); break; case SourceKitRequest::CodeCompleteOpen: @@ -864,6 +888,8 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { } else { if (Opts.UsingSwiftArgs) sourcekitd_request_dictionary_set_int64(Req, KeyUsingSwiftArgs, true); + else + compilerArgsAreClang = true; sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpenHeaderInterface); } @@ -953,10 +979,28 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { if (Opts.SourceText) { sourcekitd_request_dictionary_set_string(Req, KeySourceText, Opts.SourceText->c_str()); + sourcekitd_request_dictionary_set_string(Req, KeySourceFile, + SemaName.c_str()); } if (!Opts.CompilerArgs.empty()) { sourcekitd_object_t Args = sourcekitd_request_array_create(nullptr, 0); + if (!Opts.ModuleCachePath.empty()) { + if (compilerArgsAreClang) { + // We need -fmodules or else the clang argument parsing does not honour + // -fmodules-cache-path. In reality, the swift ClangImporter will always + // enable modules when importing, so this should only impact the + // clang argument parsing. This is needed even if the header doesn't + // use modules, since Swift itself will import its shims module, and + // that needs to honour the -module-cache-path option when testing. + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, "-fmodules"); + std::string opt = "-fmodules-cache-path=" + Opts.ModuleCachePath; + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, opt.c_str()); + } else { + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, "-module-cache-path"); + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Opts.ModuleCachePath.c_str()); + } + } for (auto Arg : Opts.CompilerArgs) sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Arg); sourcekitd_request_dictionary_set_value(Req, KeyCompilerArgs, Args); @@ -1105,6 +1149,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, printMangleResults(sourcekitd_response_get_value(Resp), outs()); break; + case SourceKitRequest::GlobalConfiguration: case SourceKitRequest::ProtocolVersion: case SourceKitRequest::CompilerVersion: case SourceKitRequest::Close: @@ -1432,9 +1477,9 @@ static void printCursorInfo(sourcekitd_variant_t Info, StringRef FilenameIn, } std::string Filename = FilenameIn; - char full_path[MAXPATHLEN]; - if (const char *path = realpath(Filename.c_str(), full_path)) - Filename = path; + llvm::SmallString<256> output; + if (!llvm::sys::fs::real_path(Filename, output)) + Filename = output.str(); const char *Kind = sourcekitd_uid_get_string_ptr(KindUID); const char *USR = sourcekitd_variant_dictionary_get_string(Info, KeyUSR); @@ -1604,9 +1649,9 @@ static void printRangeInfo(sourcekitd_variant_t Info, StringRef FilenameIn, } std::string Filename = FilenameIn; - char full_path[MAXPATHLEN]; - if (const char *path = realpath(Filename.c_str(), full_path)) - Filename = path; + llvm::SmallString<256> output; + if (llvm::sys::fs::real_path(Filename, output)) + Filename = output.str(); sourcekitd_variant_t OffsetObj = sourcekitd_variant_dictionary_get_value(Info, KeyOffset); diff --git a/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt index fb59e74837520..8c410713d850a 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/InProc/CMakeLists.txt @@ -5,7 +5,6 @@ option(SOURCEKITD_BUILD_STATIC_INPROC set(sourcekitdInProc_args sourcekitdInProc.cpp - LINK_LIBS SourceKitSwiftLang sourcekitdAPI LLVM_LINK_COMPONENTS support coverage ) @@ -36,6 +35,9 @@ else() SHARED ) endif() +target_link_libraries(sourcekitdInProc PRIVATE + SourceKitSwiftLang + sourcekitdAPI) # While it is possible to build this as a static library, # to get the runtime paths correct, it must be linked into a binary @@ -46,6 +48,9 @@ if (SOURCEKITD_BUILD_STATIC_INPROC) ${SOURCEKITD_SOURCE_DIR}/include/sourcekitd/sourcekitd.h ${sourcekitdInProc_args} ) + target_link_libraries(sourcekitdInProc_Static PRIVATE + SourceKitSwiftLang + sourcekitdAPI) endif() if (SOURCEKIT_BUILT_STANDALONE) diff --git a/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt index 638d3ac7e1561..fda4baa96d524 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/XPC/Client/CMakeLists.txt @@ -10,11 +10,11 @@ set(EXPORTED_SYMBOL_FILE "${SOURCEKITD_SOURCE_DIR}/bin/sourcekitd.exports") add_sourcekit_framework(sourcekitd ${public_headers} sourcekitd.cpp - LINK_LIBS sourcekitdAPI LLVM_LINK_COMPONENTS support MODULEMAP module.modulemap INSTALL_IN_COMPONENT sourcekit-xpc-service ) +target_link_libraries(sourcekitd PRIVATE sourcekitdAPI) add_definitions(-DSOURCEKIT_XPCSERVICE_IDENTIFIER="com.apple.SourceKitService.${SOURCEKIT_VERSION_STRING}_${SOURCEKIT_PLATFORM_NAME}") diff --git a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt index 4a007bd7439e3..f7928fc5bc5e1 100644 --- a/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/bin/XPC/Service/CMakeLists.txt @@ -1,9 +1,11 @@ if (NOT SOURCEKIT_INSTALLING_INPROC) add_sourcekit_xpc_service(SourceKitService sourcekitd XPCService.cpp - LINK_LIBS SourceKitSwiftLang sourcekitdAPI LLVM_LINK_COMPONENTS support coverage ) + target_link_libraries(SourceKitService PRIVATE + SourceKitSwiftLang + sourcekitdAPI) endif() if (NOT SOURCEKIT_DEPLOYMENT_OS MATCHES "^macosx") diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt index c29f9d32ff208..adb608642216e 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt @@ -1,5 +1,8 @@ +set(LLVM_OPTIONAL_SOURCES + sourcekitdAPI-XPC.cpp + sourcekitdAPI-InProc.cpp) -set(sourcekitdAPI_sources +add_sourcekit_library(sourcekitdAPI CodeCompletionResultsArray.cpp CompactArray.cpp DocStructureArray.cpp @@ -10,21 +13,14 @@ set(sourcekitdAPI_sources TokenAnnotationsArray.cpp ExpressionTypeArray.cpp ) - -set(sourcekitdAPI_Darwin_sources - sourcekitdAPI-XPC.cpp) -set(sourcekitdAPI_NonDarwin_InProc_sources - sourcekitdAPI-InProc.cpp) -set(LLVM_OPTIONAL_SOURCES ${sourcekitdAPI_Darwin_sources} ${sourcekitdAPI_NonDarwin_InProc_sources}) +target_link_libraries(sourcekitdAPI PRIVATE + SourceKitSupport + SourceKitSwiftLang) if(APPLE AND HAVE_XPC_H) - list(APPEND sourcekitdAPI_sources ${sourcekitdAPI_Darwin_sources}) + target_sources(sourcekitdAPI PRIVATE + sourcekitdAPI-XPC.cpp) elseif(SWIFT_SOURCEKIT_USE_INPROC_LIBRARY) - list(APPEND sourcekitdAPI_sources ${sourcekitdAPI_NonDarwin_InProc_sources}) + target_sources(sourcekitdAPI PRIVATE + sourcekitdAPI-InProc.cpp) endif() - -add_sourcekit_library(sourcekitdAPI - ${sourcekitdAPI_sources} - LINK_LIBS - SourceKitSupport SourceKitSwiftLang -) diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 3a3483e96f599..ccade323104fe 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -158,6 +158,7 @@ static void findRelatedIdents(StringRef Filename, static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions); static sourcekitd_response_t codeCompleteOpen(StringRef name, @@ -175,12 +176,14 @@ static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset); static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args); + ArrayRef Args, + Optional vfsOptions); static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes); + ArrayRef ExpectedTypes, + Optional vfsOptions); static sourcekitd_response_t editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, @@ -425,6 +428,21 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { if (!ReqUID) return Rec(createErrorRequestInvalid("missing 'key.request' with UID value")); + if (ReqUID == RequestGlobalConfiguration) { + auto Config = getGlobalContext().getGlobalConfiguration(); + ResponseBuilder RB; + auto dict = RB.getDictionary(); + + Optional OptimizeForIDE; + int64_t EditorMode = true; + if (!Req.getInt64(KeyOptimizeForIDE, EditorMode, true)) { + OptimizeForIDE = EditorMode; + } + + GlobalConfig::Settings UpdatedConfig = Config->update(OptimizeForIDE); + dict.set(KeyOptimizeForIDE, UpdatedConfig.OptimizeForIDE); + return Rec(RB.createResponse()); + } if (ReqUID == RequestProtocolVersion) { ResponseBuilder RB; auto dict = RB.getDictionary(); @@ -923,7 +941,9 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(codeComplete(InputBuf.get(), Offset, Args, std::move(vfsOptions))); + Optional options = Req.getDictionary(KeyCodeCompleteOptions); + return Rec(codeComplete(InputBuf.get(), Offset, options, Args, + std::move(vfsOptions))); } if (ReqUID == RequestCodeCompleteOpen) { @@ -961,7 +981,8 @@ static void handleSemanticRequest( int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); - return Rec(typeContextInfo(InputBuf.get(), Offset, Args)); + return Rec(typeContextInfo(InputBuf.get(), Offset, Args, + std::move(vfsOptions))); } if (ReqUID == RequestConformingMethodList) { @@ -976,7 +997,8 @@ static void handleSemanticRequest( if (Req.getStringArray(KeyExpectedTypes, ExpectedTypeNames, true)) return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'")); return Rec( - conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames)); + conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames, + std::move(vfsOptions))); } if (!SourceFile.hasValue()) @@ -1002,7 +1024,8 @@ static void handleSemanticRequest( Req.getInt64(KeyRetrieveRefactorActions, Actionables, /*isOptional=*/true); return Lang.getCursorInfo( *SourceFile, Offset, Length, Actionables, CancelOnSubsequentRequest, - Args, std::move(vfsOptions), [Rec](const RequestResult &Result) { + Args, std::move(vfsOptions), + [Rec](const RequestResult &Result) { reportCursorInfo(Result, Rec); }); } @@ -1912,12 +1935,19 @@ class SKCodeCompletionConsumer : public CodeCompletionConsumer { static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, + Optional optionsDict, ArrayRef Args, Optional vfsOptions) { ResponseBuilder RespBuilder; SKCodeCompletionConsumer CCC(RespBuilder); + + std::unique_ptr options; + if (optionsDict) + options = std::make_unique(*optionsDict); + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.codeComplete(InputBuf, Offset, CCC, Args, std::move(vfsOptions)); + Lang.codeComplete(InputBuf, Offset, options.get(), CCC, Args, + std::move(vfsOptions)); return CCC.createResponse(); } @@ -2189,7 +2219,8 @@ void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset, - ArrayRef Args) { + ArrayRef Args, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public TypeContextInfoConsumer { @@ -2226,7 +2257,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer); + Lang.getExpressionContextInfo(InputBuf, Offset, Args, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); @@ -2240,7 +2272,8 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, static sourcekitd_response_t conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args, - ArrayRef ExpectedTypes) { + ArrayRef ExpectedTypes, + Optional vfsOptions) { ResponseBuilder RespBuilder; class Consumer : public ConformingMethodListConsumer { @@ -2277,7 +2310,8 @@ conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset, } Consumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); - Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer); + Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer, + std::move(vfsOptions)); if (Consumer.isError()) return createErrorRequestFailed(Consumer.getErrorDescription()); diff --git a/tools/SourceKit/tools/swift-lang/CMakeLists.txt b/tools/SourceKit/tools/swift-lang/CMakeLists.txt index 61824c85dac10..8db6dbe81a7d9 100644 --- a/tools/SourceKit/tools/swift-lang/CMakeLists.txt +++ b/tools/SourceKit/tools/swift-lang/CMakeLists.txt @@ -1,9 +1,4 @@ if(NOT SWIFT_SOURCEKIT_USE_INPROC_LIBRARY AND SWIFT_BUILD_STDLIB AND SWIFT_BUILD_SDK_OVERLAY) - set(EXTRA_COMPILE_FLAGS "-F" "${SWIFT_LIBRARY_OUTPUT_INTDIR}") - set(SOURCEKITD_LINK_LIBS sourcekitd) - set(INSTALLED_COMP sourcekit-xpc-service) - set(DEPENDS_LIST "sourcekitd-test") - # The build type of swiftlang should agree with stdlib # This setting could avoid us adding additional search paths when building # executables using SwiftLang. @@ -25,11 +20,11 @@ if(NOT SWIFT_SOURCEKIT_USE_INPROC_LIBRARY AND SWIFT_BUILD_STDLIB AND SWIFT_BUILD GYB_SOURCES UIDs.swift.gyb - DEPENDS ${DEPENDS_LIST} + DEPENDS sourcekitd-test SWIFT_MODULE_DEPENDS_OSX Darwin Foundation - PRIVATE_LINK_LIBRARIES ${SOURCEKITD_LINK_LIBS} - SWIFT_COMPILE_FLAGS ${EXTRA_COMPILE_FLAGS} - INSTALL_IN_COMPONENT ${INSTALLED_COMP} + PRIVATE_LINK_LIBRARIES sourcekitd + SWIFT_COMPILE_FLAGS -F${SWIFT_LIBRARY_OUTPUT_INTDIR} + INSTALL_IN_COMPONENT sourcekit-xpc-service DARWIN_INSTALL_NAME_DIR "@rpath" TARGET_SDKS ${SOURCEKIT_DEFAULT_TARGET_SDK} IS_STDLIB) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 10e926b4e67e8..83d674c93b73e 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -3,15 +3,15 @@ add_swift_host_tool(swift autolink_extract_main.cpp modulewrap_main.cpp swift_indent_main.cpp + swift_symbolgraph_extract_main.cpp SWIFT_COMPONENT compiler ) target_link_libraries(swift PRIVATE swiftDriver - swiftFrontendTool) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift PRIVATE edit) -endif() + swiftFrontendTool + swiftSymbolGraphGen + LLVMBitstreamReader) swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" @@ -23,6 +23,11 @@ swift_create_post_build_symlink(swift DESTINATION "swift-indent${CMAKE_EXECUTABLE_SUFFIX}" WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") +swift_create_post_build_symlink(swift + SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + swift_create_post_build_symlink(swift SOURCE "swift${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "swift-autolink-extract${CMAKE_EXECUTABLE_SUFFIX}" @@ -31,6 +36,7 @@ swift_create_post_build_symlink(swift add_swift_tool_symlink(swiftc swift compiler) add_swift_tool_symlink(swift-autolink-extract swift autolink-driver) add_swift_tool_symlink(swift-indent swift editor-integration) +add_swift_tool_symlink(swift-symbolgraph-extract swift toolchain-tools) # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) @@ -49,3 +55,7 @@ add_dependencies(editor-integration swift) swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-indent${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "bin" COMPONENT editor-integration) +add_dependencies(toolchain-tools swift) +swift_install_in_component(FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-symbolgraph-extract${CMAKE_EXECUTABLE_SUFFIX}" + DESTINATION "bin" + COMPONENT toolchain-tools) diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0424977e4012a..17a451c76a051 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -70,6 +70,10 @@ extern int modulewrap_main(ArrayRef Args, const char *Argv0, extern int swift_indent_main(ArrayRef Args, const char *Argv0, void *MainAddr); +/// Run 'swift-symbolgraph-extract' +extern int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, +void *MainAddr); + /// Determine if the given invocation should run as a subcommand. /// /// \param ExecName The name of the argv[0] we were invoked as. @@ -152,6 +156,8 @@ static int run_driver(StringRef ExecName, return swift_indent_main( TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); + case Driver::DriverKind::SymbolGraph: + return swift_symbolgraph_extract_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0], (void *)(intptr_t)getExecutablePath); default: break; } diff --git a/tools/driver/modulewrap_main.cpp b/tools/driver/modulewrap_main.cpp index 886d7a62fba45..57e4f64996613 100644 --- a/tools/driver/modulewrap_main.cpp +++ b/tools/driver/modulewrap_main.cpp @@ -27,7 +27,7 @@ #include "swift/SIL/TypeLowering.h" #include "swift/Subsystems.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/FileSystem.h" @@ -156,20 +156,17 @@ int modulewrap_main(ArrayRef Args, const char *Argv0, // Wrap the bitstream in a module object file. To use the ClangImporter to // create the module loader, we need to properly set the runtime library path. SearchPathOptions SearchPathOpts; - // FIXME: This logic has been duplicated from - // CompilerInvocation::setMainExecutablePath. ModuleWrapInvocation - // should share its implementation. - SmallString<128> RuntimeResourcePath(MainExecutablePath); - llvm::sys::path::remove_filename(RuntimeResourcePath); // Remove /swift - llvm::sys::path::remove_filename(RuntimeResourcePath); // Remove /bin - llvm::sys::path::append(RuntimeResourcePath, "lib", "swift"); + SmallString<128> RuntimeResourcePath; + CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( + MainExecutablePath, RuntimeResourcePath); SearchPathOpts.RuntimeResourcePath = RuntimeResourcePath.str(); SourceManager SrcMgr; + TypeCheckerOptions TypeCheckOpts; LangOptions LangOpts; LangOpts.Target = Invocation.getTargetTriple(); - ASTContext &ASTCtx = *ASTContext::get(LangOpts, SearchPathOpts, SrcMgr, - Instance.getDiags()); + ASTContext &ASTCtx = *ASTContext::get(LangOpts, TypeCheckOpts, SearchPathOpts, + SrcMgr, Instance.getDiags()); registerParseRequestFunctions(ASTCtx.evaluator); registerTypeCheckerRequestFunctions(ASTCtx.evaluator); diff --git a/tools/driver/swift_indent_main.cpp b/tools/driver/swift_indent_main.cpp index bc49cbb498205..bb44ea8a085b4 100644 --- a/tools/driver/swift_indent_main.cpp +++ b/tools/driver/swift_indent_main.cpp @@ -41,14 +41,11 @@ class FormatterDocument { CompilerInvocation CompInv; std::unique_ptr Parser; class FormatterDiagConsumer : public swift::DiagnosticConsumer { - void handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const swift::DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { + void handleDiagnostic(SourceManager &SM, + const swift::DiagnosticInfo &Info) override { llvm::errs() << "Parse error: "; - DiagnosticEngine::formatDiagnosticText(llvm::errs(), FormatString, - FormatArgs); + DiagnosticEngine::formatDiagnosticText(llvm::errs(), Info.FormatString, + Info.FormatArgs); llvm::errs() << "\n"; } } DiagConsumer; @@ -64,6 +61,7 @@ class FormatterDocument { BufferID = SM.addNewSourceBuffer(std::move(Buffer)); Parser.reset(new ParserUnit(SM, SourceFileKind::Main, BufferID, CompInv.getLangOptions(), + CompInv.getTypeCheckerOptions(), CompInv.getModuleName())); Parser->getDiagnosticEngine().addConsumer(DiagConsumer); Parser->parse(); diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp new file mode 100644 index 0000000000000..1bba65f72a996 --- /dev/null +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -0,0 +1,178 @@ +//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Extracts a Symbol Graph from a .swiftmodule file. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Basic/Version.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/SymbolGraphGen/SymbolGraphGen.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" + +using namespace swift; +using namespace llvm::opt; + +namespace options { +static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options"); + +static llvm::cl::opt +ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category)); + +static llvm::cl::list +FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::list +ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore, + llvm::cl::cat(Category)); + +static llvm::cl::opt +ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category)); + +static llvm::cl::opt +SDK("sdk", llvm::cl::desc("Path to the SDK"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +Target("target", llvm::cl::desc("Target triple"), + llvm::cl::cat(Category)); + +static llvm::cl::opt +SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category)); + +static llvm::cl::opt +PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category)); + +static llvm::cl::opt +MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); + +static llvm::cl::opt +OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); +} // end namespace options + +int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, void *MainAddr) { + INITIALIZE_LLVM(); + + llvm::cl::HideUnrelatedOptions(options::Category); + + // LLVM Command Line expects to trim off argv[0]. + SmallVector ArgsWithArgv0 { Argv0 }; + ArgsWithArgv0.append(Args.begin(), Args.end()); + + llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(), + llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n"); + + CompilerInvocation Invocation; + + Invocation.setMainExecutablePath( + llvm::sys::fs::getMainExecutable(Argv0, MainAddr)); + Invocation.setModuleName("swift_symbolgraph_extract"); + Invocation.setSDKPath(options::SDK); + Invocation.setTargetTriple(options::Target); + + std::vector FrameworkSearchPaths; + for (const auto &Path : options::FrameworkSearchPaths) { + FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false}); + } + Invocation.setFrameworkSearchPaths(FrameworkSearchPaths); + Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths; + Invocation.setImportSearchPaths(options::ImportSearchPaths); + + Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin(); + Invocation.getLangOptions().DebuggerSupport = true; + + Invocation.getFrontendOptions().EnableLibraryEvolution = true; + + Invocation.setClangModuleCachePath(options::ModuleCachePath); + Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; + + if (!options::SwiftVersion.empty()) { + using version::Version; + bool isValid = false; + if (auto Version = Version::parseVersionString(options::SwiftVersion, + SourceLoc(), nullptr)) { + if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) { + Invocation.getLangOptions().EffectiveLanguageVersion = *Effective; + isValid = true; + } + } + if (!isValid) { + llvm::errs() << "Unsupported Swift Version.\n"; + return EXIT_FAILURE; + } + } + + symbolgraphgen::SymbolGraphOptions Options { + options::OutputPath, + llvm::Triple(options::Target), + options::PrettyPrint, + AccessLevel::Public, + }; + + if (!options::MinimumAccessLevel.empty()) { + Options.MinimumAccessLevel = + llvm::StringSwitch(options::MinimumAccessLevel) + .Case("open", AccessLevel::Open) + .Case("public", AccessLevel::Public) + .Case("internal", AccessLevel::Internal) + .Case("fileprivate", AccessLevel::FilePrivate) + .Case("private", AccessLevel::Private) + .Default(AccessLevel::Public); + } + + PrintingDiagnosticConsumer DiagPrinter; + + CompilerInstance CI; + CI.getDiags().addConsumer(DiagPrinter); + + if (CI.setup(Invocation)) { + llvm::outs() << "Failed to setup compiler instance\n"; + return EXIT_FAILURE; + } + + auto M = CI.getASTContext().getModuleByName(options::ModuleName); + if (!M) { + llvm::errs() + << "Couldn't load module '" << options::ModuleName << '\'' + << " in the current SDK and search paths.\n"; + SmallVector VisibleModuleNames; + CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames); + + if (VisibleModuleNames.empty()) { + llvm::errs() << "Could not find any modules.\n"; + } else { + std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(), + [](const Identifier &A, const Identifier &B) -> bool { + return A.str() < B.str(); + }); + llvm::errs() << "Current visible modules:\n"; + for (const auto &ModuleName : VisibleModuleNames) { + llvm::errs() << ModuleName.str() << "\n"; + } + } + return EXIT_FAILURE; + } + + return symbolgraphgen::emitSymbolGraphForModule(M, + Options); +} diff --git a/tools/libSwiftSyntaxParser/CMakeLists.txt b/tools/libSwiftSyntaxParser/CMakeLists.txt index a068cd36fd69f..42dda212164de 100644 --- a/tools/libSwiftSyntaxParser/CMakeLists.txt +++ b/tools/libSwiftSyntaxParser/CMakeLists.txt @@ -10,6 +10,9 @@ set(LLVM_EXPORTED_SYMBOL_FILE add_swift_host_library(libSwiftSyntaxParser SHARED c-include-check.c libSwiftSyntaxParser.cpp) +if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID MATCHES Clang) + add_dependencies(libSwiftSyntaxParser clang) +endif() target_link_libraries(libSwiftSyntaxParser PRIVATE swiftParse) set_target_properties(libSwiftSyntaxParser @@ -42,8 +45,9 @@ endif() set_property(TARGET libSwiftSyntaxParser APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(libSwiftSyntaxParser PRIVATE BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(libSwiftSyntaxParser PRIVATE + BlocksRuntime) endif() add_dependencies(parser-lib libSwiftSyntaxParser) diff --git a/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp b/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp index cef15dd93715f..006ca9fa6e67b 100644 --- a/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp +++ b/tools/libSwiftSyntaxParser/libSwiftSyntaxParser.cpp @@ -155,7 +155,6 @@ class CLibParseActions : public SyntaxParseActions { node.present = true; } -public: OpaqueSyntaxNode recordToken(tok tokenKind, ArrayRef leadingTrivia, ArrayRef trailingTrivia, @@ -206,10 +205,6 @@ class CLibParseActions : public SyntaxParseActions { auto result = NodeLookup(lexerOffset, ckind); return {result.length, result.node}; } - - OpaqueSyntaxNodeKind getOpaqueKind() override { - return OpaqueSyntaxNodeKind::SwiftSyntax; - } }; static swiftparser_diagnostic_severity_t getSeverity(DiagnosticKind Kind) { @@ -238,21 +233,18 @@ struct SynParserDiagConsumer: public DiagnosticConsumer { const unsigned BufferID; SynParserDiagConsumer(SynParser &Parser, unsigned BufferID): Parser(Parser), BufferID(BufferID) {} - void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { - assert(Kind != DiagnosticKind::Remark && "Shouldn't see this in parser."); + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { + assert(Info.Kind != DiagnosticKind::Remark && + "Shouldn't see this in parser."); // The buffer where all char* will point into. llvm::SmallString<256> Buffer; auto getCurrentText = [&]() -> const char* { return Buffer.data() + Buffer.size(); }; DiagnosticDetail Result; - Result.Severity = getSeverity(Kind); - Result.Offset = getByteOffset(Loc, SM, BufferID); + Result.Severity = getSeverity(Info.Kind); + Result.Offset = getByteOffset(Info.Loc, SM, BufferID); // Terminate each printed text with 0 so the client-side can use char* directly. char NullTerm = '\0'; @@ -260,7 +252,8 @@ struct SynParserDiagConsumer: public DiagnosticConsumer { // Print the error message to buffer and record it. llvm::raw_svector_ostream OS(Buffer); Result.Message = getCurrentText(); - DiagnosticEngine::formatDiagnosticText(OS, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, + Info.FormatArgs); OS << NullTerm; } for (auto R: Info.Ranges) { @@ -281,6 +274,7 @@ swiftparse_client_node_t SynParser::parse(const char *source) { SourceManager SM; unsigned bufID = SM.addNewSourceBuffer( llvm::MemoryBuffer::getMemBuffer(source, "syntax_parse_source")); + TypeCheckerOptions tyckOpts; LangOptions langOpts; langOpts.BuildSyntaxTree = true; langOpts.CollectParsedToken = false; @@ -292,7 +286,7 @@ swiftparse_client_node_t SynParser::parse(const char *source) { std::make_shared(*this, SM, bufID); // We have to use SourceFileKind::Main to avoid diagnostics like // illegal_top_level_expr - ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts, + ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts, tyckOpts, "syntax_parse_module", std::move(parseActions), /*SyntaxCache=*/nullptr); // Evaluating pound conditions may lead to unknown syntax. diff --git a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp index def4356f2713d..ce0b3261257d0 100644 --- a/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp +++ b/tools/lldb-moduleimport-test/lldb-moduleimport-test.cpp @@ -325,14 +325,14 @@ int main(int argc, char **argv) { llvm::outs() << "Importing " << path << "... "; #ifdef SWIFT_SUPPORTS_SUBMODULES - std::vector > AccessPath; + std::vector> AccessPath; for (auto i = llvm::sys::path::begin(path); i != llvm::sys::path::end(path); ++i) if (!llvm::sys::path::is_separator((*i)[0])) AccessPath.push_back({ CI.getASTContext().getIdentifier(*i), swift::SourceLoc() }); #else - std::vector > AccessPath; + std::vector> AccessPath; AccessPath.push_back({ CI.getASTContext().getIdentifier(path), swift::SourceLoc() }); #endif diff --git a/tools/sil-func-extractor/SILFunctionExtractor.cpp b/tools/sil-func-extractor/SILFunctionExtractor.cpp index 7c04d529c33fa..33d0082c8b352 100644 --- a/tools/sil-func-extractor/SILFunctionExtractor.cpp +++ b/tools/sil-func-extractor/SILFunctionExtractor.cpp @@ -355,9 +355,13 @@ int main(int argc, char **argv) { const StringRef OutputFile = OutputFilename.size() ? StringRef(OutputFilename) : "-"; + auto SILOpts = SILOptions(); + SILOpts.EmitVerboseSIL = EmitVerboseSIL; + SILOpts.EmitSortedSIL = EnableSILSortOutput; + if (OutputFile == "-") { - CI.getSILModule()->print(llvm::outs(), EmitVerboseSIL, CI.getMainModule(), - EnableSILSortOutput, !DisableASTDump); + CI.getSILModule()->print(llvm::outs(), CI.getMainModule(), SILOpts, + !DisableASTDump); } else { std::error_code EC; llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_None); @@ -366,8 +370,8 @@ int main(int argc, char **argv) { << '\n'; return 1; } - CI.getSILModule()->print(OS, EmitVerboseSIL, CI.getMainModule(), - EnableSILSortOutput, !DisableASTDump); + CI.getSILModule()->print(OS, CI.getMainModule(), SILOpts, + !DisableASTDump); } } } diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index 90c9260c760db..c584a950a1ce2 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -81,6 +81,11 @@ static llvm::cl::opt DisableSILOwnershipVerifier( llvm::cl::desc( "Do not verify SIL ownership invariants during SIL verification")); +static llvm::cl::opt EnableOwnershipLoweringAfterDiagnostics( + "enable-ownership-lowering-after-diagnostics", + llvm::cl::desc("Enable ownership lowering after diagnostics"), + llvm::cl::init(false)); + static llvm::cl::opt EnableSILOpaqueValues("enable-sil-opaque-values", llvm::cl::desc("Compile the module with sil-opaque-values enabled.")); @@ -97,6 +102,10 @@ static llvm::cl::opt VerifyExclusivity("enable-verify-exclusivity", llvm::cl::desc("Verify the access markers used to enforce exclusivity.")); +static llvm::cl::opt +EnableSpeculativeDevirtualization("enable-spec-devirt", + llvm::cl::desc("Enable Speculative Devirtualization pass.")); + namespace { enum EnforceExclusivityMode { Unchecked, // static only @@ -209,6 +218,11 @@ EnableExperimentalStaticAssert( "enable-experimental-static-assert", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Enable experimental #assert")); +static llvm::cl::opt EnableExperimentalDifferentiableProgramming( + "enable-experimental-differentiable-programming", llvm::cl::Hidden, + llvm::cl::init(false), + llvm::cl::desc("Enable experimental differentiable programming")); + /// Regular expression corresponding to the value given in one of the /// -pass-remarks* command line flags. Passes whose name matches this regexp /// will emit a diagnostic. @@ -324,6 +338,9 @@ int main(int argc, char **argv) { Invocation.getLangOptions().EnableExperimentalStaticAssert = EnableExperimentalStaticAssert; + Invocation.getLangOptions().EnableExperimentalDifferentiableProgramming = + EnableExperimentalDifferentiableProgramming; + // Setup the SIL Options. SILOptions &SILOpts = Invocation.getSILOptions(); SILOpts.InlineThreshold = SILInlineThreshold; @@ -333,6 +350,8 @@ int main(int argc, char **argv) { if (OptimizationGroup != OptGroup::Diagnostics) SILOpts.OptMode = OptimizationMode::ForSpeed; SILOpts.VerifySILOwnership = !DisableSILOwnershipVerifier; + SILOpts.StripOwnershipAfterSerialization = + EnableOwnershipLoweringAfterDiagnostics; SILOpts.VerifyExclusivity = VerifyExclusivity; if (EnforceExclusivity.getNumOccurrences() != 0) { @@ -361,6 +380,8 @@ int main(int argc, char **argv) { } } + SILOpts.EnableSpeculativeDevirtualization = EnableSpeculativeDevirtualization; + serialization::ExtendedValidationInfo extendedInfo; llvm::ErrorOr> FileBufOrErr = Invocation.setUpInputForSILTool(InputFilename, ModuleName, @@ -435,7 +456,7 @@ int main(int argc, char **argv) { auto T = irgen::createIRGenModule( SILMod, Invocation.getOutputFilenameForAtMostOnePrimary(), Invocation.getMainInputFilenameForDebugInfoForAtMostOnePrimary(), - getGlobalLLVMContext()); + "", getGlobalLLVMContext()); runCommandLineSelectedPasses(SILMod, T.second); irgen::deleteIRGenModule(T); } @@ -464,10 +485,12 @@ int main(int argc, char **argv) { } else { const StringRef OutputFile = OutputFilename.size() ? StringRef(OutputFilename) : "-"; - + auto SILOpts = SILOptions(); + SILOpts.EmitVerboseSIL = EmitVerboseSIL; + SILOpts.EmitSortedSIL = EnableSILSortOutput; if (OutputFile == "-") { - CI.getSILModule()->print(llvm::outs(), EmitVerboseSIL, CI.getMainModule(), - EnableSILSortOutput, !DisableASTDump); + CI.getSILModule()->print(llvm::outs(), CI.getMainModule(), + SILOpts, !DisableASTDump); } else { std::error_code EC; llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_None); @@ -476,8 +499,8 @@ int main(int argc, char **argv) { << EC.message() << '\n'; return 1; } - CI.getSILModule()->print(OS, EmitVerboseSIL, CI.getMainModule(), - EnableSILSortOutput, !DisableASTDump); + CI.getSILModule()->print(OS, CI.getMainModule(), SILOpts, + !DisableASTDump); } } diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp index f6d21026b892c..3f5fea81e5244 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp @@ -27,7 +27,7 @@ struct swift::ide::api::SDKNodeInitInfo { SDKContext &Ctx; DeclKind DKind; AccessorKind AccKind; - + SourceLoc Loc; #define KEY_STRING(X, Y) StringRef X; #include "swift/IDE/DigesterEnums.def" #define KEY_BOOL(X, Y) bool X = false; @@ -37,7 +37,7 @@ struct swift::ide::api::SDKNodeInitInfo { #define KEY_STRING_ARR(X, Y) std::vector X; #include "swift/IDE/DigesterEnums.def" - ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; + swift::ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong; std::vector DeclAttrs; std::vector TypeAttrs; @@ -52,6 +52,29 @@ struct swift::ide::api::SDKNodeInitInfo { SDKContext::SDKContext(CheckerOptions Opts): Diags(SourceMgr), Opts(Opts) {} +DiagnosticEngine &SDKContext::getDiags(SourceLoc Loc) { + // If the location is invalid, we just use the locally created DiagEngine. + if (Loc.isInvalid()) + return Diags; + // If the Loc is valid, it may belong to any of the SourceManagers owned by + // the ASTContexts we created, thus we should go through the ASTContxts to find + // the right DiagnosticEngine to use. + for (auto &CI: CIs) { + if (CI->getSourceMgr().isOwning(Loc)) + return CI->getDiags(); + } + llvm_unreachable("cannot find diagnostic engine to use"); +} + +void SDKContext::addDiagConsumer(DiagnosticConsumer &Consumer) { + // we may emit diagnostics via any of the diagnostic engine, so add the consumer + // to all of them. + Diags.addConsumer(Consumer); + for (auto &CI: CIs) { + CI->getDiags().addConsumer(Consumer); + } +} + void SDKNodeRoot::registerDescendant(SDKNode *D) { // Operator doesn't have usr if (isa(D)) @@ -70,7 +93,7 @@ SDKNodeRoot::SDKNodeRoot(SDKNodeInitInfo Info): SDKNode(Info, SDKNodeKind::Root) JsonFormatVer(Info.JsonFormatVer.hasValue() ? *Info.JsonFormatVer : DIGESTER_JSON_DEFAULT_VERSION) {} SDKNodeDecl::SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind) - : SDKNode(Info, Kind), DKind(Info.DKind), Usr(Info.Usr), + : SDKNode(Info, Kind), DKind(Info.DKind), Usr(Info.Usr), Loc(Info.Loc), Location(Info.Location), ModuleName(Info.ModuleName), DeclAttributes(Info.DeclAttrs), IsImplicit(Info.IsImplicit), IsStatic(Info.IsStatic), IsDeprecated(Info.IsDeprecated), @@ -103,7 +126,9 @@ SDKNodeTypeAlias::SDKNodeTypeAlias(SDKNodeInitInfo Info): SDKNodeDeclType::SDKNodeDeclType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclType), SuperclassUsr(Info.SuperclassUsr), SuperclassNames(Info.SuperclassNames), - EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal) {} + EnumRawTypeName(Info.EnumRawTypeName), IsExternal(Info.IsExternal), + HasMissingDesignatedInitializers(Info.HasMissingDesignatedInitializers), + InheritsConvenienceInitializers(Info.InheritsConvenienceInitializers) {} SDKNodeConformance::SDKNodeConformance(SDKNodeInitInfo Info): SDKNode(Info, SDKNodeKind::Conformance), @@ -1125,7 +1150,7 @@ static StringRef printGenericSignature(SDKContext &Ctx, Decl *D, bool Canonical) if (auto *GC = D->getAsGenericContext()) { if (auto Sig = GC->getGenericSignature()) { if (Canonical) - Sig->getCanonicalSignature()->print(OS, Opts); + Sig.getCanonicalSignature()->print(OS, Opts); else Sig->print(OS, Opts); return Ctx.buffer(OS.str()); @@ -1297,7 +1322,7 @@ StringRef SDKContext::getInitKind(Decl *D) { } SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D): - Ctx(Ctx), DKind(D->getKind()), + Ctx(Ctx), DKind(D->getKind()), Loc(D->getLoc()), Location(calculateLocation(Ctx, D)), ModuleName(D->getModuleContext()->getName().str()), GenericSig(printGenericSignature(Ctx, D, /*Canonical*/Ctx.checkingABI())), @@ -1380,6 +1405,8 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD) SuperclassNames.push_back(getPrintedName(Ctx, T->getCanonicalType())); } } + HasMissingDesignatedInitializers = CD->hasMissingDesignatedInitializers(); + InheritsConvenienceInitializers = CD->inheritsSuperclassInitializers(); } if (auto *FD = dyn_cast(VD)) { @@ -1952,6 +1979,10 @@ void SDKNodeDeclType::jsonize(json::Output &out) { output(out, KeyKind::KK_superclassUsr, SuperclassUsr); output(out, KeyKind::KK_enumRawTypeName, EnumRawTypeName); output(out, KeyKind::KK_isExternal, IsExternal); + output(out, KeyKind::KK_hasMissingDesignatedInitializers, + HasMissingDesignatedInitializers); + output(out, KeyKind::KK_inheritsConvenienceInitializers, + InheritsConvenienceInitializers); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_superclassNames).data(), SuperclassNames); out.mapOptional(getKeyContent(Ctx, KeyKind::KK_conformances).data(), Conformances); } @@ -2143,6 +2174,9 @@ swift::ide::api::getSDKNodeRoot(SDKContext &SDKCtx, if (llvm::errs().has_colors()) PrintDiags.forceColors(); CI.addDiagnosticConsumer(&PrintDiags); + // The PrintDiags is only responsible compiler errors, we should remove the + // consumer immediately after importing is done. + SWIFT_DEFER { CI.getDiags().removeConsumer(PrintDiags); }; if (CI.setup(Invocation)) { llvm::errs() << "Failed to setup the compiler instance\n"; return nullptr; @@ -2211,7 +2245,7 @@ int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath } PrintingDiagnosticConsumer PDC; SDKContext Ctx(Opts); - Ctx.getDiags().addConsumer(PDC); + Ctx.addDiagConsumer(PDC); SwiftDeclCollector Collector(Ctx); Collector.deSerialize(dumpPath); @@ -2227,7 +2261,7 @@ int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { } PrintingDiagnosticConsumer PDC; SDKContext Ctx(Opts); - Ctx.getDiags().addConsumer(PDC); + Ctx.addDiagConsumer(PDC); SwiftDeclCollector Collector(Ctx); Collector.deSerialize(dumpPath); diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.h b/tools/swift-api-digester/ModuleAnalyzerNodes.h index 465b1e8e64159..db19837d46ebc 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.h +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.h @@ -202,9 +202,9 @@ class SDKContext { SourceManager &getSourceMgr() { return SourceMgr; } - DiagnosticEngine &getDiags() { - return Diags; - } + // Find a DiagnosticEngine to use when emitting diagnostics at the given Loc. + DiagnosticEngine &getDiags(SourceLoc Loc = SourceLoc()); + void addDiagConsumer(DiagnosticConsumer &Consumer); void setCommonVersion(uint8_t Ver) { assert(!CommonVersion.hasValue()); CommonVersion = Ver; @@ -337,6 +337,7 @@ struct PlatformIntroVersion { class SDKNodeDecl: public SDKNode { DeclKind DKind; StringRef Usr; + SourceLoc Loc; StringRef Location; StringRef ModuleName; std::vector DeclAttributes; @@ -393,20 +394,21 @@ class SDKNodeDecl: public SDKNode { uint8_t getFixedBinaryOrder() const { return *FixedBinaryOrder; } PlatformIntroVersion getIntroducingVersion() const { return introVersions; } StringRef getObjCName() const { return ObjCName; } + SourceLoc getLoc() const { return Loc; } virtual void jsonize(json::Output &Out) override; virtual void diagnose(SDKNode *Right) override; // The first argument of the diag is always screening info. template - void emitDiag(Diag ID, + void emitDiag(SourceLoc Loc, + Diag ID, typename detail::PassArgument::type... Args) const { // Don't emit objc decls if we care about swift exclusively if (Ctx.getOpts().SwiftOnly) { if (isObjc()) return; } - Ctx.getDiags().diagnose(SourceLoc(), ID, getScreenInfo(), - std::move(Args)...); + Ctx.getDiags(Loc).diagnose(Loc, ID, getScreenInfo(), std::move(Args)...); } }; @@ -522,6 +524,8 @@ class SDKNodeDeclType: public SDKNodeDecl { // Check whether the type declaration is pulled from an external module so we // can incorporate extensions in the interested module. bool IsExternal; + bool HasMissingDesignatedInitializers; + bool InheritsConvenienceInitializers; public: SDKNodeDeclType(SDKNodeInitInfo Info); static bool classof(const SDKNode *N); @@ -546,6 +550,13 @@ class SDKNodeDeclType: public SDKNodeDecl { return EnumRawTypeName; } + bool hasMissingDesignatedInitializers() const { + return HasMissingDesignatedInitializers; + }; + bool inheritsConvenienceInitializers() const { + return InheritsConvenienceInitializers; + }; + Optional getSuperclass() const; /// Finding the node through all children, including the inheritted ones, diff --git a/tools/swift-api-digester/ModuleDiagsConsumer.cpp b/tools/swift-api-digester/ModuleDiagsConsumer.cpp index 67cd080f8ccd7..2964709fc4ae4 100644 --- a/tools/swift-api-digester/ModuleDiagsConsumer.cpp +++ b/tools/swift-api-digester/ModuleDiagsConsumer.cpp @@ -42,7 +42,6 @@ static StringRef getCategoryName(uint32_t ID) { return "/* Renamed Decls */"; case LocalDiagID::decl_attr_change: case LocalDiagID::decl_new_attr: - case LocalDiagID::var_let_changed: case LocalDiagID::func_self_access_change: case LocalDiagID::new_decl_without_intro: return "/* Decl Attribute changes */"; @@ -74,6 +73,8 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::super_class_changed: case LocalDiagID::no_longer_open: case LocalDiagID::desig_init_added: + case LocalDiagID::added_invisible_designated_init: + case LocalDiagID::not_inheriting_convenience_inits: return "/* Class Inheritance Change */"; default: return StringRef(); @@ -94,15 +95,10 @@ ModuleDifferDiagsConsumer::ModuleDifferDiagsConsumer(bool DiagnoseModuleDiff, } void swift::ide::api::ModuleDifferDiagsConsumer::handleDiagnostic( - SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, ArrayRef FormatArgs, - const DiagnosticInfo &Info, - const SourceLoc bufferIndirectlyCausingDiagnostic) { + SourceManager &SM, const DiagnosticInfo &Info) { auto Category = getCategoryName((uint32_t)Info.ID); if (Category.empty()) { - PrintingDiagnosticConsumer::handleDiagnostic( - SM, Loc, Kind, FormatString, FormatArgs, Info, - bufferIndirectlyCausingDiagnostic); + PrintingDiagnosticConsumer::handleDiagnostic(SM, Info); return; } if (!DiagnoseModuleDiff) @@ -110,7 +106,8 @@ void swift::ide::api::ModuleDifferDiagsConsumer::handleDiagnostic( llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); - DiagnosticEngine::formatDiagnosticText(Out, FormatString, FormatArgs); + DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString, + Info.FormatArgs); } AllDiags[Category].insert(Text.str().str()); } diff --git a/tools/swift-api-digester/ModuleDiagsConsumer.h b/tools/swift-api-digester/ModuleDiagsConsumer.h index 47cd1d469634e..df83c808baa88 100644 --- a/tools/swift-api-digester/ModuleDiagsConsumer.h +++ b/tools/swift-api-digester/ModuleDiagsConsumer.h @@ -39,11 +39,7 @@ class ModuleDifferDiagsConsumer: public PrintingDiagnosticConsumer { ModuleDifferDiagsConsumer(bool DiagnoseModuleDiff, llvm::raw_ostream &OS = llvm::errs()); ~ModuleDifferDiagsConsumer(); - void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override; + void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override; }; } } diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index f22e4cd806ee7..6253f69a9e8df 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -770,9 +770,9 @@ void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { auto *R = dyn_cast(Right); if (!R) return; - + auto Loc = R->getLoc(); if (getDeclKind() != R->getDeclKind()) { - emitDiag(diag::decl_kind_changed, getDeclKindStr(R->getDeclKind())); + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind())); return; } @@ -784,11 +784,27 @@ void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { auto RSuperClass = R->getSuperClassName(); if (!LSuperClass.empty() && LSuperClass != RSuperClass) { if (RSuperClass.empty()) { - emitDiag(diag::super_class_removed, LSuperClass); + emitDiag(Loc, diag::super_class_removed, LSuperClass); } else if (!contains(R->getClassInheritanceChain(), LSuperClass)) { - emitDiag(diag::super_class_changed, LSuperClass, RSuperClass); + emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); } } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); break; } default: @@ -801,13 +817,13 @@ void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { auto *R = dyn_cast(Right); if (!R) return; - + auto Loc = R->getLoc(); if (!isThrowing() && R->isThrowing()) { - emitDiag(diag::decl_new_attr, Ctx.buffer("throwing")); + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); } if (Ctx.checkingABI()) { if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { - emitDiag(diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); + emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); } } } @@ -817,13 +833,14 @@ void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { auto *R = dyn_cast(Right); if (!R) return; + auto Loc = R->getLoc(); if (getSelfAccessKind() != R->getSelfAccessKind()) { - emitDiag(diag::func_self_access_change, getSelfAccessKind(), + emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), R->getSelfAccessKind()); } if (Ctx.checkingABI()) { if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { - emitDiag(diag::func_has_fixed_order_change, hasFixedBinaryOrder()); + emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); } } } @@ -854,13 +871,14 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { if (!RD) return; detectRename(this, RD); + auto Loc = RD->getLoc(); if (isOpen() && !RD->isOpen()) { - emitDiag(diag::no_longer_open); + emitDiag(Loc, diag::no_longer_open); } // Diagnose static attribute change. if (isStatic() ^ RD->isStatic()) { - emitDiag(diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : "static")); } @@ -872,7 +890,7 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { return Ctx.buffer("strong"); return keywordOf(O); }; - emitDiag(diag::decl_attr_change, + emitDiag(Loc, diag::decl_attr_change, getOwnershipDescription(getReferenceOwnership()), getOwnershipDescription(RD->getReferenceOwnership())); } @@ -881,10 +899,10 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { // Prefer sugared signature in diagnostics to be more user-friendly. if (Ctx.commonVersionAtLeast(2) && getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { - emitDiag(diag::generic_sig_change, + emitDiag(Loc, diag::generic_sig_change, getSugaredGenericSignature(), RD->getSugaredGenericSignature()); } else { - emitDiag(diag::generic_sig_change, + emitDiag(Loc, diag::generic_sig_change, getGenericSignature(), RD->getGenericSignature()); } } @@ -892,17 +910,17 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { // ObjC name changes are considered breakage if (getObjCName() != RD->getObjCName()) { if (Ctx.commonVersionAtLeast(4)) { - emitDiag(diag::objc_name_change, getObjCName(), RD->getObjCName()); + emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); } } if (isOptional() != RD->isOptional()) { if (Ctx.checkingABI()) { // Both adding/removing optional is ABI-breaking. - emitDiag(diag::optional_req_changed, isOptional()); + emitDiag(Loc, diag::optional_req_changed, isOptional()); } else if (isOptional()) { // Removing optional is source-breaking. - emitDiag(diag::optional_req_changed, isOptional()); + emitDiag(Loc, diag::optional_req_changed, isOptional()); } } @@ -912,7 +930,7 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : DeclAttribute::isRemovingBreakingAPI(Kind)) && shouldDiagnoseRemovingAttribute(this, Kind)) { - emitDiag(diag::decl_new_attr, + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); } } @@ -924,7 +942,7 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : DeclAttribute::isAddingBreakingAPI(Kind)) && shouldDiagnoseAddingAttribute(this, Kind)) { - emitDiag(diag::decl_new_attr, + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); } } @@ -933,7 +951,7 @@ void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { if (Ctx.checkingABI()) { if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { - emitDiag(diag::decl_reorder, getFixedBinaryOrder(), + emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), RD->getFixedBinaryOrder()); } } @@ -944,8 +962,9 @@ void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { auto *RO = dyn_cast(Right); if (!RO) return; + auto Loc = RO->getLoc(); if (getDeclKind() != RO->getDeclKind()) { - emitDiag(diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind())); + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind())); } } @@ -954,12 +973,10 @@ void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { auto *RV = dyn_cast(Right); if (!RV) return; + auto Loc = RV->getLoc(); if (Ctx.checkingABI()) { if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { - emitDiag(diag::var_has_fixed_order_change, hasFixedBinaryOrder()); - } - if (isLet() != RV->isLet()) { - emitDiag(diag::var_let_changed, isLet()); + emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); } } } @@ -979,10 +996,10 @@ void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { if (auto *Wit = dyn_cast(getParent())) { auto *Conform = Wit->getParent()->getAs(); if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { - Conform->getNominalTypeDecl()->emitDiag(diag::type_witness_change, - Wit->getWitnessedTypeName(), - getPrintedName(), - RT->getPrintedName()); + auto *LD = Conform->getNominalTypeDecl(); + LD->emitDiag(SourceLoc(), diag::type_witness_change, + Wit->getWitnessedTypeName(), + getPrintedName(), RT->getPrintedName()); } return; } @@ -991,20 +1008,20 @@ void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { assert(isa(getParent())); auto LParent = cast(getParent()); assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); - + auto Loc = RT->getParent()->getAs()->getLoc(); if (getPrintedName() != RT->getPrintedName()) { - LParent->emitDiag(diag::decl_type_change, + LParent->emitDiag(Loc, diag::decl_type_change, Descriptor, getPrintedName(), RT->getPrintedName()); } if (hasDefaultArgument() && !RT->hasDefaultArgument()) { - LParent->emitDiag(diag::default_arg_removed, Descriptor); + LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); } if (getParamValueOwnership() != RT->getParamValueOwnership()) { - getParent()->getAs()->emitDiag(diag::param_ownership_change, - getTypeRoleDescription(), - getParamValueOwnership(), - RT->getParamValueOwnership()); + LParent->emitDiag(Loc, diag::param_ownership_change, + getTypeRoleDescription(), + getParamValueOwnership(), + RT->getParamValueOwnership()); } } @@ -1014,8 +1031,10 @@ void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { if (!RT || !shouldDiagnoseType(this)) return; assert(isTopLevelType()); + auto Loc = RT->getParent()->getAs()->getLoc(); if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { - getParent()->getAs()->emitDiag(diag::func_type_escaping_changed, + getParent()->getAs()->emitDiag(Loc, + diag::func_type_escaping_changed, getTypeRoleDescription(), isEscaping()); } @@ -1104,7 +1123,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { // Any order-important decl added to a non-resilient type breaks ABI. if (auto *D = dyn_cast(Right)) { if (D->hasFixedBinaryOrder()) { - D->emitDiag(diag::decl_added); + D->emitDiag(D->getLoc(), diag::decl_added); } // Diagnose the missing of @available attributes. // Decls with @_alwaysEmitIntoClient aren't required to have an @@ -1112,7 +1131,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { if (!Ctx.getOpts().SkipOSCheck && !D->getIntroducingVersion().hasOSAvailability() && !D->hasDeclAttribute(DeclAttrKind::DAK_AlwaysEmitIntoClient)) { - D->emitDiag(diag::new_decl_without_intro); + D->emitDiag(D->getLoc(), diag::new_decl_without_intro); } } } @@ -1132,19 +1151,20 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { ShouldComplain = false; } if (ShouldComplain) - D->emitDiag(diag::protocol_req_added); + D->emitDiag(D->getLoc(), diag::protocol_req_added); } } // Diagnose an inherited protocol has been added. if (auto *Conf = dyn_cast(Right)) { auto *TD = Conf->getNominalTypeDecl(); if (TD->isProtocol()) { - TD->emitDiag(diag::conformance_added, Conf->getName()); + TD->emitDiag(TD->getLoc(), diag::conformance_added, Conf->getName()); } else { // Adding conformance to an existing type can be ABI breaking. if (Ctx.checkingABI() && !LeftRoot->getDescendantsByUsr(Conf->getUsr()).empty()) { - TD->emitDiag(diag::existing_conformance_added, Conf->getName()); + TD->emitDiag(TD->getLoc(), diag::existing_conformance_added, + Conf->getName()); } } } @@ -1155,7 +1175,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { // initializers, it automatically inherits all of the superclass convenience initializers. // This means if a new designated init is added to the base class, the inherited // convenience init may be missing and cause breakage. - CD->emitDiag(diag::desig_init_added); + CD->emitDiag(CD->getLoc(), diag::desig_init_added); } } } @@ -1166,19 +1186,20 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { Left->annotate(NodeAnnotation::Removed); if (auto *LT = dyn_cast(Left)) { if (auto *AT = dyn_cast(LT->getParent())) { - AT->emitDiag(diag::default_associated_type_removed, + AT->emitDiag(SourceLoc(), diag::default_associated_type_removed, LT->getPrintedName()); } } // Diagnose a protocol conformance has been removed. if (auto *Conf = dyn_cast(Left)) { auto *TD = Conf->getNominalTypeDecl(); - TD->emitDiag(diag::conformance_removed, + TD->emitDiag(SourceLoc(), + diag::conformance_removed, Conf->getName(), TD->isProtocol()); } if (auto *Acc = dyn_cast(Left)) { - Acc->emitDiag(diag::removed_decl, Acc->isDeprecated()); + Acc->emitDiag(SourceLoc(), diag::removed_decl, Acc->isDeprecated()); } return; case NodeMatchReason::FuncToProperty: @@ -2021,7 +2042,7 @@ static bool diagnoseRemovedExtensionMembers(const SDKNode *Node) { if (DT->isExtension()) { for (auto *C: DT->getChildren()) { auto *MD = cast(C); - MD->emitDiag(diag::removed_decl, MD->isDeprecated()); + MD->emitDiag(SourceLoc(), diag::removed_decl, MD->isDeprecated()); } return true; } @@ -2040,7 +2061,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) { return; if (auto *Added = findAddedDecl(Node)) { if (Node->getDeclKind() != DeclKind::Constructor) { - Node->emitDiag(diag::moved_decl, + Node->emitDiag(Added->getLoc(), diag::moved_decl, Ctx.buffer((Twine(getDeclKindStr(Added->getDeclKind())) + " " + Added->getFullyQualifiedName()).str())); return; @@ -2052,7 +2073,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) { auto It = std::find_if(MemberChanges.begin(), MemberChanges.end(), [&](TypeMemberDiffItem &Item) { return Item.usr == Node->getUsr(); }); if (It != MemberChanges.end()) { - Node->emitDiag(diag::renamed_decl, + Node->emitDiag(SourceLoc(), diag::renamed_decl, Ctx.buffer((Twine(getDeclKindStr(Node->getDeclKind())) + " " + It->newTypeName + "." + It->newPrintedName).str())); return; @@ -2063,7 +2084,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) { // refine diagnostics message instead of showing the type alias has been // removed. if (TypeAliasUpdateMap.find((SDKNode*)Node) != TypeAliasUpdateMap.end()) { - Node->emitDiag(diag::raw_type_change, + Node->emitDiag(SourceLoc(), diag::raw_type_change, Node->getAs()->getUnderlyingType()->getPrintedName(), TypeAliasUpdateMap[(SDKNode*)Node]->getAs()-> getRawValueType()->getPrintedName()); @@ -2098,11 +2119,18 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) { } bool handled = diagnoseRemovedExtensionMembers(Node); if (!handled) - Node->emitDiag(diag::removed_decl, Node->isDeprecated()); + Node->emitDiag(SourceLoc(), diag::removed_decl, Node->isDeprecated()); return; } case NodeAnnotation::Rename: { - Node->emitDiag(diag::renamed_decl, + SourceLoc DiagLoc; + // Try to get the source location from the later version of this node + // via UpdateMap. + if (auto CD = dyn_cast_or_null(UpdateMap + .findUpdateCounterpart(Node))) { + DiagLoc = CD->getLoc(); + } + Node->emitDiag(DiagLoc, diag::renamed_decl, Ctx.buffer((Twine(getDeclKindStr(Node->getDeclKind())) + " " + Node->getAnnotateComment(NodeAnnotation::RenameNewName)).str())); return; @@ -2245,7 +2273,7 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, llvm::make_unique(): llvm::make_unique(true, *OS); - Ctx.getDiags().addConsumer(*pConsumer); + Ctx.addDiagConsumer(*pConsumer); Ctx.setCommonVersion(std::min(LeftModule->getJsonFormatVersion(), RightModule->getJsonFormatVersion())); TypeAliasDiffFinder(LeftModule, RightModule, @@ -2320,7 +2348,7 @@ static int generateMigrationScript(StringRef LeftPath, StringRef RightPath, llvm::make_unique(): llvm::make_unique(false); SDKContext Ctx(Opts); - Ctx.getDiags().addConsumer(*pConsumer); + Ctx.addDiagConsumer(*pConsumer); SwiftDeclCollector LeftCollector(Ctx); LeftCollector.deSerialize(LeftPath); diff --git a/tools/swift-ast-script/ASTScriptEvaluator.cpp b/tools/swift-ast-script/ASTScriptEvaluator.cpp index 7731d5976d94f..f6ae3d8d4a59d 100644 --- a/tools/swift-ast-script/ASTScriptEvaluator.cpp +++ b/tools/swift-ast-script/ASTScriptEvaluator.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/Frontend/Frontend.h" using namespace swift; @@ -114,7 +115,11 @@ bool ASTScript::execute() const { return true; } - UnqualifiedLookup viewLookup(ctx.getIdentifier("View"), swiftUI); + auto descriptor = + UnqualifiedLookupDescriptor(DeclNameRef(ctx.getIdentifier("View")), + swiftUI); + auto viewLookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); auto viewProtocol = dyn_cast_or_null(viewLookup.getSingleTypeResult()); if (!viewProtocol) { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index d8d9dc490705a..995eaaaab5eef 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -22,6 +22,7 @@ #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/ImportCache.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/RawComment.h" #include "swift/AST/USRGeneration.h" @@ -32,6 +33,7 @@ #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDE/CompletionInstance.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/ConformingMethodList.h" @@ -44,10 +46,9 @@ #include "swift/IDE/IDERequests.h" #include "swift/Index/Index.h" #include "swift/Sema/IDETypeChecking.h" +#include "swift/SyntaxParse/SyntaxTreeCreator.h" #include "swift/Markup/Markup.h" #include "swift/Config.h" -#include "clang/APINotes/APINotesReader.h" -#include "clang/APINotes/APINotesWriter.h" #include "clang/Rewrite/Core/RewriteBuffer.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" @@ -85,6 +86,7 @@ enum class ActionType { PrintASTNotTypeChecked, PrintASTTypeChecked, PrintModule, + PrintModuleMetadata, PrintHeader, PrintSwiftFileInterface, PrintDecl, @@ -180,6 +182,8 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), "print-ast-typechecked", "Print the typechecked AST"), clEnumValN(ActionType::PrintModule, "print-module", "Print visible declarations in a module"), + clEnumValN(ActionType::PrintModuleMetadata, + "print-module-metadata", "Print meta-data in a module"), clEnumValN(ActionType::PrintHeader, "print-header", "Print visible declarations in a header file"), clEnumValN(ActionType::PrintSwiftFileInterface, @@ -310,13 +314,6 @@ static llvm::cl::opt EnableSourceImport("enable-source-import", llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false)); -static llvm::cl::opt -DisableFunctionBuilderOneWayConstraints( - "disable-function-builder-one-way-constraints", - llvm::cl::desc("Disable one-way constraints in function builders"), - llvm::cl::cat(Category), - llvm::cl::init(false)); - static llvm::cl::opt SkipDeinit("skip-deinit", llvm::cl::desc("Whether to skip printing destructors"), @@ -702,6 +699,13 @@ GraphVisPath("output-request-graphviz", static llvm::cl::opt CanonicalizeType("canonicalize-type", llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false)); + +static llvm::cl::opt +EnableSwiftSourceInfo("enable-swiftsourceinfo", + llvm::cl::desc("Whether to consume .swiftsourceinfo files"), + llvm::cl::cat(Category), + llvm::cl::init(false)); + } // namespace options static std::unique_ptr @@ -717,23 +721,35 @@ removeCodeCompletionTokens(llvm::MemoryBuffer *Input, Input->getBufferIdentifier())); } -static int doTypeContextInfo(const CompilerInvocation &InitInvok, - StringRef SourceFilename, - StringRef SecondSourceFileName, - StringRef CodeCompletionToken, - bool CodeCompletionDiagnostics) { +/// Returns true on error +static bool setBufferForFile(StringRef SourceFilename, + std::unique_ptr &Buffer) { llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(SourceFilename); if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; + llvm::errs() << "error opening input file '" << SourceFilename << "':\n" + << " " << FileBufOrErr.getError().message() << '\n'; + return true; } + Buffer = std::move(FileBufOrErr.get()); + return false; +} + +static bool doCodeCompletionImpl( + CodeCompletionCallbacksFactory *callbacksFactory, + const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; unsigned Offset; std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); + FileBuf.get(), CodeCompletionToken, &Offset)); if (Offset == ~0U) { llvm::errs() << "could not find code completion token \"" @@ -747,8 +763,30 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); + if (!SecondSourceFileName.empty()) { + Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( + SecondSourceFileName); + } + std::string Error; + PrintingDiagnosticConsumer PrintDiags; + CompletionInstance CompletionInst; + auto isSuccess = CompletionInst.performOperation( + Invocation, /*Args=*/{}, llvm::vfs::getRealFileSystem(), CleanFile.get(), + Offset, /*EnableASTCaching=*/false, Error, + CodeCompletionDiagnostics ? &PrintDiags : nullptr, + [&](CompilerInstance &CI) { + performCodeCompletionSecondPass(CI.getPersistentParserState(), + *callbacksFactory); + }); + return isSuccess ? 0 : 1; +} + +static int doTypeContextInfo(const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef CodeCompletionToken, + bool CodeCompletionDiagnostics) { // Create a CodeCompletionConsumer. std::unique_ptr Consumer( new ide::PrintingTypeContextInfoConsumer(llvm::outs())); @@ -758,23 +796,9 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeTypeContextInfoCallbacksFactory(*Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int @@ -783,33 +807,6 @@ doConformingMethodList(const CompilerInvocation &InitInvok, StringRef CodeCompletionToken, bool CodeCompletionDiagnostics, const std::vector expectedTypeNames) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned Offset; - - std::unique_ptr CleanFile(removeCodeCompletionTokens( - FileBufOrErr.get().get(), CodeCompletionToken, &Offset)); - - if (Offset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << Offset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - Invocation.setCodeCompletionPoint(CleanFile.get(), Offset); - SmallVector typeNames; for (auto &name : expectedTypeNames) typeNames.push_back(name.c_str()); @@ -823,23 +820,9 @@ doConformingMethodList(const CompilerInvocation &InitInvok, std::unique_ptr callbacksFactory( ide::makeConformingMethodListCallbacksFactory(typeNames, *Consumer)); - Invocation.setCodeCompletionFactory(callbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; - - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doCodeCompletion(const CompilerInvocation &InitInvok, @@ -849,35 +832,6 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, bool CodeCompletionDiagnostics, bool CodeCompletionKeywords, bool CodeCompletionComments) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return 1; - } - - unsigned CodeCompletionOffset; - - std::unique_ptr CleanFile( - removeCodeCompletionTokens(FileBufOrErr.get().get(), CodeCompletionToken, - &CodeCompletionOffset)); - - if (CodeCompletionOffset == ~0U) { - llvm::errs() << "could not find code completion token \"" - << CodeCompletionToken << "\"\n"; - return 1; - } - llvm::outs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - llvm::errs() << "found code completion token " << CodeCompletionToken - << " at offset " << CodeCompletionOffset << "\n"; - - CompilerInvocation Invocation(InitInvok); - - Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset); - - std::unique_ptr OnDiskCache; if (!options::CompletionCachePath.empty()) { OnDiskCache = llvm::make_unique( @@ -893,40 +847,21 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok, // Create a factory for code completion callbacks that will feed the // Consumer. - std::unique_ptr CompletionCallbacksFactory( - ide::makeCodeCompletionCallbacksFactory(CompletionContext, - *Consumer)); - - Invocation.setCodeCompletionFactory(CompletionCallbacksFactory.get()); - if (!SecondSourceFileName.empty()) { - Invocation.getFrontendOptions().InputsAndOutputs.addInputFile( - SecondSourceFileName); - } - CompilerInstance CI; + std::unique_ptr callbacksFactory( + ide::makeCodeCompletionCallbacksFactory(CompletionContext, *Consumer)); - PrintingDiagnosticConsumer PrintDiags; - if (CodeCompletionDiagnostics) { - // Display diagnostics to stderr. - CI.addDiagnosticConsumer(&PrintDiags); - } - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performSema(); - return 0; + return doCodeCompletionImpl(callbacksFactory.get(), InitInvok, SourceFilename, + SecondSourceFileName, CodeCompletionToken, + CodeCompletionDiagnostics); } static int doREPLCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } - StringRef BufferText = FileBufOrErr.get()->getBuffer(); + StringRef BufferText = FileBuf->getBuffer(); // Drop a single newline character from the buffer. if (BufferText.endswith("\n")) BufferText = BufferText.drop_back(1); @@ -1132,38 +1067,74 @@ static int doSyntaxColoring(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); Invocation.getLangOptions().DisableAvailabilityChecking = false; - - CompilerInstance CI; - - // Display diagnostics to stderr. - PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); Invocation.getLangOptions().Playground = Playground; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getLangOptions().BuildSyntaxTree = true; - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - if (!RunTypeChecker) - CI.performParseOnly(); - else + + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + + if (RunTypeChecker) { + CompilerInstance CI; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; CI.performSema(); - unsigned BufID = CI.getInputBufferIDs().back(); - SourceFile *SF = nullptr; - for (auto Unit : CI.getMainModule()->getFiles()) { - SF = dyn_cast(Unit); - if (SF) - break; - } - assert(SF && "no source file?"); + unsigned BufID = CI.getInputBufferIDs().back(); + SourceFile *SF = nullptr; + for (auto Unit : CI.getMainModule()->getFiles()) { + SF = dyn_cast(Unit); + if (SF) + break; + } + assert(SF && "no source file?"); - ide::SyntaxModelContext ColorContext(*SF); - PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), - TerminalOutput); - ColorContext.walk(ColorWalker); - ColorWalker.finished(); + ide::SyntaxModelContext ColorContext(*SF); + PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } else { + // SourceKit doesn't set up a compiler instance at all for its syntactic + // requests, just the parser. We try to mimic that setup here to help catch + // any cases where the walker might inadvertently rely on the name lookup or + // other semantic functionality via the request evaluator. + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); + + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; + Parser.getDiagnosticEngine().addConsumer(PrintDiags); + + (void)Parser.parse(); + + ide::SyntaxModelContext ColorContext(Parser.getSourceFile()); + PrintSyntaxColorWalker ColorWalker(SM, BufferID, llvm::outs(), + TerminalOutput); + ColorContext.walk(ColorWalker); + ColorWalker.finished(); + } return 0; } @@ -1355,25 +1326,51 @@ class StructureAnnotator : public ide::SyntaxModelWalker { static int doStructureAnnotation(const CompilerInvocation &InitInvok, StringRef SourceFilename) { + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) + return 1; + CompilerInvocation Invocation(InitInvok); Invocation.getLangOptions().BuildSyntaxTree = true; Invocation.getLangOptions().CollectParsedToken = true; Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename); - CompilerInstance CI; + // Structure annotation is run as a purely syntactic request by SourceKit. It + // doesn't set up a compiler instance at all, just the parser. We try to mimic + // that setup here to help catch any cases where the walker might inadvertently + // rely on the name lookup or other semantic functionality via the request + // evaluator. + SourceManager SM; + unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf)); + + RC syntaxArena{new syntax::SyntaxArena()}; + std::shared_ptr SynTreeCreator = + std::make_shared( + SM, BufferID, Invocation.getMainFileSyntaxParsingCache(), + syntaxArena); + + ParserUnit Parser(SM, SourceFileKind::Main, BufferID, + Invocation.getLangOptions(), + Invocation.getTypeCheckerOptions(), + Invocation.getModuleName(), + SynTreeCreator, + Invocation.getMainFileSyntaxParsingCache()); + + registerParseRequestFunctions(Parser.getParser().Context.evaluator); + registerTypeCheckerRequestFunctions( + Parser.getParser().Context.evaluator); + + // Collecting syntactic information shouldn't evaluate # conditions. + Parser.getParser().State->PerformConditionEvaluation = false; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; - CI.addDiagnosticConsumer(&PrintDiags); - if (CI.setup(Invocation)) - return 1; - registerIDERequestFunctions(CI.getASTContext().evaluator); - CI.performParseOnly(); + Parser.getDiagnosticEngine().addConsumer(PrintDiags); - unsigned BufID = CI.getInputBufferIDs().back(); - ide::SyntaxModelContext StructureContext( - CI.getMainModule()->getMainSourceFile(SourceFileKind::Main)); - StructureAnnotator Annotator(CI.getSourceMgr(), BufID); + (void)Parser.parse(); + + ide::SyntaxModelContext StructureContext(Parser.getSourceFile()); + StructureAnnotator Annotator(SM, BufferID); StructureContext.walk(Annotator); Annotator.printResult(llvm::outs()); return 0; @@ -1510,7 +1507,7 @@ class AnnotationPrinter : public SourceEntityWalker { void printLoc(SourceLoc Loc, raw_ostream &OS) { OS << '@'; - if (Loc.isValid()) { + if (Loc.isValid() && SM.findBufferContainingLoc(Loc) == BufferID) { auto LineCol = SM.getLineAndColumn(Loc, BufferID); OS << LineCol.first << ':' << LineCol.second; } @@ -1527,16 +1524,16 @@ class AnnotationPrinter : public SourceEntityWalker { OS << 'i'; if (isa(D) && Entity.IsRef) { OS << "Ctor"; - printLoc(D->getLoc(), OS); + printLoc(D->getLoc(/*SerializedOK*/false), OS); if (Entity.CtorTyRef) { OS << '-'; OS << Decl::getKindName(Entity.CtorTyRef->getKind()); - printLoc(Entity.CtorTyRef->getLoc(), OS); + printLoc(Entity.CtorTyRef->getLoc(/*SerializedOK*/false), OS); } } else { OS << Decl::getKindName(D->getKind()); if (Entity.IsRef) - printLoc(D->getLoc(), OS); + printLoc(D->getLoc(/*SerializedOK*/false), OS); } } else { @@ -1636,17 +1633,13 @@ static int doSemanticAnnotation(const CompilerInvocation &InitInvok, } static int doInputCompletenessTest(StringRef SourceFilename) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(SourceFilename); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; + std::unique_ptr FileBuf; + if (setBufferForFile(SourceFilename, FileBuf)) return 1; - } llvm::raw_ostream &OS = llvm::outs(); OS << SourceFilename << ": "; - if (isSourceInputComplete(std::move(FileBufOrErr.get()), + if (isSourceInputComplete(std::move(FileBuf), SourceFileKind::REPL).IsComplete) { OS << "IS_COMPLETE\n"; } else { @@ -1660,7 +1653,7 @@ static int doInputCompletenessTest(StringRef SourceFilename) { //===----------------------------------------------------------------------===// static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName) { - SmallVector, 4> + SmallVector, 4> AccessPath; while (!ModuleName.empty()) { StringRef SubModuleName; @@ -1675,8 +1668,7 @@ static ModuleDecl *getModuleByFullName(ASTContext &Context, StringRef ModuleName } static ModuleDecl *getModuleByFullName(ASTContext &Context, Identifier ModuleName) { - ModuleDecl *Result = Context.getModule(std::make_pair(ModuleName, - SourceLoc())); + ModuleDecl *Result = Context.getModule({ Located(ModuleName,SourceLoc()) }); if (!Result || Result->failedToLoad()) return nullptr; return Result; @@ -2048,6 +2040,76 @@ static int doPrintModuleGroups(const CompilerInvocation &InitInvok, return ExitCode; } +static void printModuleMetadata(ModuleDecl *MD) { + auto &OS = llvm::outs(); + MD->collectLinkLibraries([&](LinkLibrary lib) { + OS << "link library: " << lib.getName() + << ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n"; + }); +} + +static int doPrintModuleMetaData(const CompilerInvocation &InitInvok, + const std::vector ModulesToPrint) { + CompilerInvocation Invocation(InitInvok); + + CompilerInstance CI; + // Display diagnostics to stderr. + PrintingDiagnosticConsumer PrintDiags; + CI.addDiagnosticConsumer(&PrintDiags); + if (CI.setup(Invocation)) + return 1; + registerIDERequestFunctions(CI.getASTContext().evaluator); + auto &Context = CI.getASTContext(); + + // Load standard library so that Clang importer can use it. + auto *Stdlib = getModuleByFullName(Context, Context.StdlibModuleName); + if (!Stdlib) { + llvm::errs() << "Failed loading stdlib\n"; + return 1; + } + int ExitCode = 0; + for (StringRef ModuleToPrint : ModulesToPrint) { + if (ModuleToPrint.empty()) { + ExitCode = 1; + continue; + } + + // Get the (sub)module to print. + auto *M = getModuleByFullName(Context, ModuleToPrint); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleToPrint + << "'\n"; + ExitCode = 1; + continue; + } + + // Split the module path. + std::vector ModuleName; + while (!ModuleToPrint.empty()) { + StringRef SubModuleName; + std::tie(SubModuleName, ModuleToPrint) = ModuleToPrint.split('.'); + ModuleName.push_back(SubModuleName); + } + assert(!ModuleName.empty()); + + // FIXME: If ModuleToPrint is a submodule, get its top-level module, which + // will be the DeclContext for all of its Decls since we don't have first- + // class submodules. + if (ModuleName.size() > 1) { + M = getModuleByFullName(Context, ModuleName[0]); + if (!M) { + llvm::errs() << "error: could not find module '" << ModuleName[0] + << "'\n"; + ExitCode = 1; + continue; + } + } + printModuleMetadata(M); + } + + return ExitCode; +} + static int doPrintModules(const CompilerInvocation &InitInvok, const std::vector ModulesToPrint, const std::vector GroupsToPrint, @@ -2237,9 +2299,12 @@ static int doPrintDecls(const CompilerInvocation &InitInvok, for (const auto &name : DeclsToPrint) { ASTContext &ctx = CI.getASTContext(); - UnqualifiedLookup lookup(ctx.getIdentifier(name), - CI.getPrimarySourceFile()); - for (auto result : lookup.Results) { + auto descriptor = + UnqualifiedLookupDescriptor(DeclNameRef(ctx.getIdentifier(name)), + CI.getPrimarySourceFile()); + auto lookup = evaluateOrDefault(ctx.evaluator, + UnqualifiedLookupRequest{descriptor}, {}); + for (auto result : lookup) { result.getValueDecl()->print(*Printer, Options); if (auto typeDecl = dyn_cast(result.getValueDecl())) { @@ -2696,7 +2761,7 @@ static int doPrintModuleImports(const CompilerInvocation &InitInvok, for (auto &import : scratch) { llvm::outs() << "\t" << import.second->getName(); for (auto accessPathPiece : import.first) { - llvm::outs() << "." << accessPathPiece.first; + llvm::outs() << "." << accessPathPiece.Item; } if (import.second->isClangModule()) @@ -3175,16 +3240,12 @@ static int doTestCreateCompilerInvocation(ArrayRef Args) { } static int doTestCompilerInvocationFromModule(StringRef ModuleFilePath) { - llvm::ErrorOr> FileBufOrErr = - llvm::MemoryBuffer::getFile(ModuleFilePath); - if (!FileBufOrErr) { - llvm::errs() << "error opening input file: " - << FileBufOrErr.getError().message() << '\n'; - return -1; - } + std::unique_ptr FileBuf; + if (setBufferForFile(ModuleFilePath, FileBuf)) + return 1; CompilerInvocation CI; - StringRef Data = FileBufOrErr.get()->getBuffer(); + StringRef Data = FileBuf->getBuffer(); static_assert(static_cast(serialization::Status::Valid) == 0, "Status::Valid should be a successful exit"); return static_cast(CI.loadFromSerializedAST(Data)); @@ -3308,6 +3369,12 @@ int main(int argc, char *argv[]) { InitInvok.getLangOptions().EnableObjCInterop = llvm::Triple(options::Triple).isOSDarwin(); } + + // We disable source location resolutions from .swiftsourceinfo files by + // default to match sourcekitd-test's and ide clients' expected behavior + // (passing optimize-for-ide in the global configuration request). + if (!options::EnableSwiftSourceInfo) + InitInvok.getFrontendOptions().IgnoreSwiftSourceInfo = true; if (!options::Triple.empty()) InitInvok.setTargetTriple(options::Triple); if (!options::SwiftVersion.empty()) { @@ -3345,8 +3412,6 @@ int main(int argc, char *argv[]) { options::ImportObjCHeader; InitInvok.getLangOptions().EnableAccessControl = !options::DisableAccessControl; - InitInvok.getLangOptions().FunctionBuilderOneWayConstraints = - !options::DisableFunctionBuilderOneWayConstraints; InitInvok.getLangOptions().CodeCompleteInitsInPostfixExpr |= options::CodeCompleteInitsInPostfixExpr; InitInvok.getLangOptions().CodeCompleteCallPatternHeuristics |= @@ -3365,12 +3430,11 @@ int main(int argc, char *argv[]) { for (auto &Arg : options::ClangXCC) { InitInvok.getClangImporterOptions().ExtraArgs.push_back(Arg); } - InitInvok.getLangOptions().DebugForbidTypecheckPrefix = - options::DebugForbidTypecheckPrefix; InitInvok.getLangOptions().EnableObjCAttrRequiresFoundation = !options::DisableObjCAttrRequiresFoundationModule; - - InitInvok.getLangOptions().DebugConstraintSolver = + InitInvok.getTypeCheckerOptions().DebugForbidTypecheckPrefix = + options::DebugForbidTypecheckPrefix; + InitInvok.getTypeCheckerOptions().DebugConstraintSolver = options::DebugConstraintSolver; for (auto ConfigName : options::BuildConfigs) @@ -3552,7 +3616,10 @@ int main(int argc, char *argv[]) { } break; } - + case ActionType::PrintModuleMetadata: { + ExitCode = doPrintModuleMetaData(InitInvok, options::ModuleToPrint); + break; + } case ActionType::PrintHeader: { ExitCode = doPrintHeaders( InitInvok, options::HeaderToPrint, PrintOpts, diff --git a/tools/swift-refactor/swift-refactor.cpp b/tools/swift-refactor/swift-refactor.cpp index 83417f6cf97fd..22c885b97c2cb 100644 --- a/tools/swift-refactor/swift-refactor.cpp +++ b/tools/swift-refactor/swift-refactor.cpp @@ -71,7 +71,9 @@ Action(llvm::cl::desc("kind:"), llvm::cl::init(RefactoringKind::None), "trailingclosure", "Perform trailing closure refactoring"), clEnumValN(RefactoringKind::ReplaceBodiesWithFatalError, "replace-bodies-with-fatalError", "Perform trailing closure refactoring"), - clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"))); + clEnumValN(RefactoringKind::MemberwiseInitLocalRefactoring, "memberwise-init", "Generate member wise initializer"), + clEnumValN(RefactoringKind::ConvertToComputedProperty, + "convert-to-computed-property", "Convert from field initialization to computed property"))); static llvm::cl::opt diff --git a/tools/swift-reflection-dump/swift-reflection-dump.cpp b/tools/swift-reflection-dump/swift-reflection-dump.cpp index a70bd1ac6021d..747713a61cbfa 100644 --- a/tools/swift-reflection-dump/swift-reflection-dump.cpp +++ b/tools/swift-reflection-dump/swift-reflection-dump.cpp @@ -37,7 +37,6 @@ #include #include -#include using llvm::ArrayRef; using llvm::dyn_cast; @@ -549,7 +548,7 @@ makeReflectionContextForObjectFiles( static int doDumpReflectionSections(ArrayRef BinaryFilenames, StringRef Arch, ActionType Action, - std::ostream &OS) { + FILE *file) { // Note: binaryOrError and objectOrError own the memory for our ObjectFile; // once they go out of scope, we can no longer do anything. std::vector> BinaryOwners; @@ -582,7 +581,7 @@ static int doDumpReflectionSections(ArrayRef BinaryFilenames, switch (Action) { case ActionType::DumpReflectionSections: // Dump everything - builder.dumpAllSections(OS); + builder.dumpAllSections(file); break; case ActionType::DumpTypeLowering: { for (std::string Line; std::getline(std::cin, Line);) { @@ -597,17 +596,17 @@ static int doDumpReflectionSections(ArrayRef BinaryFilenames, auto *TypeRef = swift::Demangle::decodeMangledType(builder, Demangled); if (TypeRef == nullptr) { - OS << "Invalid typeref: " << Line << "\n"; + fprintf(file, "Invalid typeref:%s\n", Line.c_str()); continue; } - TypeRef->dump(OS); + TypeRef->dump(file); auto *TypeInfo = builder.getTypeConverter().getTypeInfo(TypeRef); if (TypeInfo == nullptr) { - OS << "Invalid lowering\n"; + fprintf(file, "Invalid lowering\n"); continue; } - TypeInfo->dump(OS); + TypeInfo->dump(file); } break; } @@ -621,5 +620,5 @@ int main(int argc, char *argv[]) { llvm::cl::ParseCommandLineOptions(argc, argv, "Swift Reflection Dump\n"); return doDumpReflectionSections(options::BinaryFilename, options::Architecture, options::Action, - std::cout); + stdout); } diff --git a/tools/swift-remoteast-test/CMakeLists.txt b/tools/swift-remoteast-test/CMakeLists.txt index 54987f792c12f..ecf4c953a1857 100644 --- a/tools/swift-remoteast-test/CMakeLists.txt +++ b/tools/swift-remoteast-test/CMakeLists.txt @@ -7,9 +7,6 @@ target_link_libraries(swift-remoteast-test swiftFrontendTool swiftRemoteAST) set_target_properties(swift-remoteast-test PROPERTIES ENABLE_EXPORTS 1) -if(HAVE_UNICODE_LIBEDIT) - target_link_libraries(swift-remoteast-test PRIVATE edit) -endif() # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) diff --git a/tools/swift-remoteast-test/swift-remoteast-test.cpp b/tools/swift-remoteast-test/swift-remoteast-test.cpp index 391c7b3edb98e..9b172eb8ab394 100644 --- a/tools/swift-remoteast-test/swift-remoteast-test.cpp +++ b/tools/swift-remoteast-test/swift-remoteast-test.cpp @@ -72,7 +72,7 @@ static RemoteASTContext &getRemoteASTContext() { // FIXME: swiftcall /// func printType(forMetadata: Any.Type) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printMetadataType(const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -90,7 +90,7 @@ printMetadataType(const Metadata *typeMetadata) { // FIXME: swiftcall /// func printDynamicType(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printHeapMetadataType(void *object) { auto &remoteAST = getRemoteASTContext(); auto &out = llvm::outs(); @@ -145,7 +145,7 @@ static void printMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMemberOffset(forType: Any.Type, memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ false); @@ -154,7 +154,7 @@ printTypeMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printTypeMetadataMemberOffset(forType: Any.Type, /// memberName: StaticString) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printTypeMetadataMemberOffset(const Metadata *typeMetadata, const char *memberName) { printMemberOffset(typeMetadata, memberName, /*pass metadata*/ true); @@ -162,7 +162,7 @@ printTypeMetadataMemberOffset(const Metadata *typeMetadata, // FIXME: swiftcall /// func printDynamicTypeAndAddressForExistential(_: T) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED printDynamicTypeAndAddressForExistential(void *object, const Metadata *typeMetadata) { auto &remoteAST = getRemoteASTContext(); @@ -192,7 +192,7 @@ printDynamicTypeAndAddressForExistential(void *object, // FIXME: swiftcall /// func stopRemoteAST(_: AnyObject) -LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI +extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED stopRemoteAST() { if (remoteContext) remoteContext.reset(); diff --git a/tools/swift-stdlib-tool/swift-stdlib-tool.mm b/tools/swift-stdlib-tool/swift-stdlib-tool.mm index bd25b31b0d488..4c3ae0cd3fa76 100644 --- a/tools/swift-stdlib-tool/swift-stdlib-tool.mm +++ b/tools/swift-stdlib-tool/swift-stdlib-tool.mm @@ -80,6 +80,7 @@ #include #include +#include using namespace std; #include @@ -672,9 +673,10 @@ -(NSString *)sst_command { NSString *self_executable = []() -> NSString * { uint32_t len = 0; _NSGetExecutablePath(nil, &len); - char buf[len]; - _NSGetExecutablePath(buf, &len); - return [[NSString alloc] initWithUTF8String:buf]; + std::vector buffer; + buffer.reserve(len); + _NSGetExecutablePath(buffer.data(), &len); + return [[NSString alloc] initWithUTF8String:buffer.data()]; }(); diff --git a/tools/swift-syntax-parser-test/CMakeLists.txt b/tools/swift-syntax-parser-test/CMakeLists.txt index 65dadfde5ea90..cd4019c273aae 100644 --- a/tools/swift-syntax-parser-test/CMakeLists.txt +++ b/tools/swift-syntax-parser-test/CMakeLists.txt @@ -7,23 +7,23 @@ add_swift_host_tool(swift-syntax-parser-test Support SWIFT_COMPONENT tools ) +if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID MATCHES Clang) + add_dependencies(swift-syntax-parser-test clang) +endif() target_link_libraries(swift-syntax-parser-test PRIVATE libSwiftSyntaxParser ) -if(APPLE) - # Prioritize finding the parser library from the build/lib directory. - # Otherwise it may find it from the 'lib/swift/macosx' directory which could - # be out-of-date. - get_target_property(link_flags swift-syntax-parser-test LINK_FLAGS) - set(link_flags "-Xlinker -rpath -Xlinker @executable_path/../lib ${link_flags}") - set_property(TARGET swift-syntax-parser-test PROPERTY - LINK_FLAGS "${link_flags}") +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(swift-syntax-parser-test PROPERTIES + BUILD_WITH_INSTALL_RPATH YES + INSTALL_RPATH @executable_path/../lib) endif() set_property(TARGET swift-syntax-parser-test APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) - target_link_libraries(swift-syntax-parser-test PRIVATE BlocksRuntime) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(swift-syntax-parser-test PRIVATE + BlocksRuntime) endif() diff --git a/unittests/AST/ArithmeticEvaluator.cpp b/unittests/AST/ArithmeticEvaluator.cpp index 7738ba8e55fea..ed85576e07c57 100644 --- a/unittests/AST/ArithmeticEvaluator.cpp +++ b/unittests/AST/ArithmeticEvaluator.cpp @@ -210,7 +210,9 @@ TEST(ArithmeticEvaluator, Simple) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/true); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); @@ -333,7 +335,9 @@ TEST(ArithmeticEvaluator, Cycle) { SourceManager sourceMgr; DiagnosticEngine diags(sourceMgr); - Evaluator evaluator(diags); + Evaluator evaluator(diags, + /*debugDumpCycles=*/false, + /*buildDependencyGraph=*/false); evaluator.registerRequestFunctions(Zone::ArithmeticEvaluator, arithmeticRequestFunctions); diff --git a/unittests/AST/DiagnosticConsumerTests.cpp b/unittests/AST/DiagnosticConsumerTests.cpp index 6fba6ff33c46d..77032b705a935 100644 --- a/unittests/AST/DiagnosticConsumerTests.cpp +++ b/unittests/AST/DiagnosticConsumerTests.cpp @@ -11,13 +11,14 @@ //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticConsumer.h" +#include "swift/Basic/Located.h" #include "swift/Basic/SourceManager.h" #include "gtest/gtest.h" using namespace swift; namespace { - using ExpectedDiagnostic = std::pair; + using ExpectedDiagnostic = Located; class ExpectationDiagnosticConsumer: public DiagnosticConsumer { ExpectationDiagnosticConsumer *previous; @@ -34,13 +35,10 @@ namespace { EXPECT_TRUE(expected.empty()); } - void handleDiagnostic( - SourceManager &SM, SourceLoc loc, DiagnosticKind kind, - StringRef formatString, ArrayRef formatArgs, - const DiagnosticInfo &info, - const SourceLoc bufferIndirectlyCausingDiagnostic) override { + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { ASSERT_FALSE(expected.empty()); - EXPECT_EQ(std::make_pair(loc, formatString), expected.front()); + EXPECT_EQ(Located(Info.FormatString, Info.Loc), expected.front()); expected.erase(expected.begin()); } @@ -52,6 +50,14 @@ namespace { return false; } }; + + DiagnosticInfo testDiagnosticInfo(SourceLoc Loc, const char *Message, + DiagnosticKind Kind) { + return DiagnosticInfo(DiagID(0), Loc, Kind, Message, /*args*/ {}, + /*indirectBuffer*/ SourceLoc(), /*childInfo*/ {}, + /*ranges*/ {}, /*fixIts*/ {}, /*isChild*/ false); + } + } // end anonymous namespace TEST(FileSpecificDiagnosticConsumer, SubconsumersFinishInOrder) { @@ -78,7 +84,7 @@ TEST(FileSpecificDiagnosticConsumer, InvalidLocDiagsGoToEveryConsumer) { (void)sourceMgr.addMemBufferCopy("abcde", "A"); (void)sourceMgr.addMemBufferCopy("vwxyz", "B"); - ExpectedDiagnostic expected[] = { {SourceLoc(), "dummy"} }; + ExpectedDiagnostic expected[] = { Located("dummy", SourceLoc()) }; auto consumerA = llvm::make_unique( nullptr, expected); auto consumerUnaffiliated = llvm::make_unique( @@ -90,8 +96,9 @@ TEST(FileSpecificDiagnosticConsumer, InvalidLocDiagsGoToEveryConsumer) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, SourceLoc(), DiagnosticKind::Error, - "dummy", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(SourceLoc(), "dummy", DiagnosticKind::Error)); topConsumer->finishProcessing(); } @@ -110,14 +117,14 @@ TEST(FileSpecificDiagnosticConsumer, ErrorsWithLocationsGoToExpectedConsumers) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {middleOfA, "middle"}, - {backOfA, "back"}, + {"front", frontOfA}, + {"middle", middleOfA}, + {"back", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -131,18 +138,20 @@ TEST(FileSpecificDiagnosticConsumer, ErrorsWithLocationsGoToExpectedConsumers) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "front", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "front", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Error, - "middle", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Error, - "middle", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Error, - "back", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Error, - "back", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "front", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "front", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(middleOfA, "middle", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(middleOfB, "middle", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "back", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "back", DiagnosticKind::Error)); topConsumer->finishProcessing(); } @@ -162,17 +171,17 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "front"}, - {frontOfB, "front"}, - {middleOfA, "middle"}, - {middleOfB, "middle"}, - {backOfA, "back"}, - {backOfB, "back"} + {"front", frontOfA}, + {"front", frontOfB}, + {"middle", middleOfA}, + {"middle", middleOfB}, + {"back", backOfA}, + {"back", backOfB} }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "front"}, - {middleOfB, "middle"}, - {backOfB, "back"} + {"front", frontOfB}, + {"middle", middleOfB}, + {"back", backOfB} }; auto consumerA = llvm::make_unique( @@ -186,18 +195,20 @@ TEST(FileSpecificDiagnosticConsumer, auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "front", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "front", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Error, - "middle", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Error, - "middle", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Error, - "back", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Error, - "back", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "front", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "front", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(middleOfA, "middle", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(middleOfB, "middle", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "back", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "back", DiagnosticKind::Error)); topConsumer->finishProcessing(); } @@ -211,14 +222,14 @@ TEST(FileSpecificDiagnosticConsumer, WarningsAndRemarksAreTreatedLikeErrors) { SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {frontOfB, "warning"}, - {frontOfA, "remark"}, - {frontOfB, "remark"}, + {"warning", frontOfA}, + {"warning", frontOfB}, + {"remark", frontOfA}, + {"remark", frontOfB}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {frontOfB, "remark"}, + {"warning", frontOfB}, + {"remark", frontOfB}, }; auto consumerA = llvm::make_unique( @@ -232,14 +243,18 @@ TEST(FileSpecificDiagnosticConsumer, WarningsAndRemarksAreTreatedLikeErrors) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Warning, - "warning", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Warning, - "warning", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Remark, - "remark", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Remark, - "remark", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfA, "warning", DiagnosticKind::Warning)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfB, "warning", DiagnosticKind::Warning)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfA, "remark", DiagnosticKind::Remark)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfB, "remark", DiagnosticKind::Remark)); topConsumer->finishProcessing(); } @@ -258,20 +273,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrors) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -285,24 +300,24 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrors) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); topConsumer->finishProcessing(); } @@ -321,20 +336,20 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToWarningsAndRemarks) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "warning"}, - {middleOfA, "note"}, - {backOfA, "note"}, - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, - {frontOfA, "remark"}, - {middleOfA, "note"}, - {backOfA, "note"}, + {"warning", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, + {"remark", frontOfA}, + {"note", middleOfA}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "warning"}, - {middleOfB, "note"}, - {backOfB, "note"}, + {"warning", frontOfB}, + {"note", middleOfB}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -348,24 +363,27 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToWarningsAndRemarks) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Warning, - "warning", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Warning, - "warning", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Remark, - "remark", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfA, "warning", DiagnosticKind::Warning)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfB, "warning", DiagnosticKind::Warning)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, + testDiagnosticInfo(frontOfA, "remark", DiagnosticKind::Remark)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); topConsumer->finishProcessing(); } @@ -384,17 +402,17 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrorsEvenAcrossFiles) { SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedB[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -408,24 +426,24 @@ TEST(FileSpecificDiagnosticConsumer, NotesAreAttachedToErrorsEvenAcrossFiles) { auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); topConsumer->finishProcessing(); } @@ -445,20 +463,20 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc backOfB = sourceMgr.getLocForOffset(bufferB, 4); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, - {frontOfA, "error"}, - {middleOfB, "note"}, - {backOfA, "note"}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, + {"error", frontOfA}, + {"note", middleOfB}, + {"note", backOfA}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {middleOfA, "note"}, - {backOfB, "note"}, + {"error", frontOfB}, + {"note", middleOfA}, + {"note", backOfB}, }; auto consumerA = llvm::make_unique( @@ -472,24 +490,24 @@ TEST(FileSpecificDiagnosticConsumer, auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, middleOfB, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, backOfA, DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfA, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(middleOfB, "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(backOfA, "note", DiagnosticKind::Note)); topConsumer->finishProcessing(); } @@ -505,16 +523,16 @@ TEST(FileSpecificDiagnosticConsumer, SourceLoc frontOfB = sourceMgr.getLocForBufferStart(bufferB); ExpectedDiagnostic expectedA[] = { - {frontOfA, "error"}, - {SourceLoc(), "note"}, - {frontOfB, "error"}, - {SourceLoc(), "note"}, - {frontOfA, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfA}, + {"note", SourceLoc()}, + {"error", frontOfB}, + {"note", SourceLoc()}, + {"error", frontOfA}, + {"note", SourceLoc()}, }; ExpectedDiagnostic expectedUnaffiliated[] = { - {frontOfB, "error"}, - {SourceLoc(), "note"}, + {"error", frontOfB}, + {"note", SourceLoc()}, }; auto consumerA = llvm::make_unique( @@ -528,17 +546,17 @@ TEST(FileSpecificDiagnosticConsumer, auto topConsumer = FileSpecificDiagnosticConsumer::consolidateSubconsumers(consumers); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, SourceLoc(), DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfB, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, SourceLoc(), DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, frontOfA, DiagnosticKind::Error, - "error", {}, DiagnosticInfo(), SourceLoc()); - topConsumer->handleDiagnostic(sourceMgr, SourceLoc(), DiagnosticKind::Note, - "note", {}, DiagnosticInfo(), SourceLoc()); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(SourceLoc(), "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfB, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(SourceLoc(), "note", DiagnosticKind::Note)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(frontOfA, "error", DiagnosticKind::Error)); + topConsumer->handleDiagnostic( + sourceMgr, testDiagnosticInfo(SourceLoc(), "note", DiagnosticKind::Note)); topConsumer->finishProcessing(); } diff --git a/unittests/AST/SourceLocTests.cpp b/unittests/AST/SourceLocTests.cpp index fe1d71471260b..0b7fb60696b6e 100644 --- a/unittests/AST/SourceLocTests.cpp +++ b/unittests/AST/SourceLocTests.cpp @@ -42,42 +42,42 @@ TEST(SourceLoc, AssignExpr) { SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID); auto destBase = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("aa"), + DeclNameRef(C.Ctx.getIdentifier("aa")), DeclRefKind::Ordinary, DeclNameLoc(start)); auto dest = new (C.Ctx) UnresolvedDotExpr( destBase, start.getAdvancedLoc(2), - C.Ctx.getIdentifier("bb"), + DeclNameRef(C.Ctx.getIdentifier("bb")), DeclNameLoc(start.getAdvancedLoc(3)), /*implicit*/false); auto destImplicit = new (C.Ctx) UnresolvedDotExpr( destBase, start.getAdvancedLoc(2), - C.Ctx.getIdentifier("bb"), + DeclNameRef(C.Ctx.getIdentifier("bb")), DeclNameLoc(start.getAdvancedLoc(3)), /*implicit*/true); auto sourceBase = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("cc"), + DeclNameRef(C.Ctx.getIdentifier("cc")), DeclRefKind::Ordinary, DeclNameLoc(start.getAdvancedLoc(8))); auto source = new (C.Ctx) UnresolvedDotExpr( sourceBase, start.getAdvancedLoc(10), - C.Ctx.getIdentifier("dd"), + DeclNameRef(C.Ctx.getIdentifier("dd")), DeclNameLoc(start.getAdvancedLoc(11)), /*implicit*/false); auto sourceImplicit = new (C.Ctx) UnresolvedDotExpr( sourceBase, start.getAdvancedLoc(10), - C.Ctx.getIdentifier("dd"), + DeclNameRef(C.Ctx.getIdentifier("dd")), DeclNameLoc(start.getAdvancedLoc(11)), /*implicit*/true); auto invalid = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("invalid"), + DeclNameRef(C.Ctx.getIdentifier("invalid")), DeclRefKind::Ordinary, DeclNameLoc()); @@ -223,22 +223,22 @@ TEST(SourceLoc, TupleExpr) { SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID); auto one = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("one"), + DeclNameRef(C.Ctx.getIdentifier("one")), DeclRefKind::Ordinary, DeclNameLoc(start)); auto two = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("two"), + DeclNameRef(C.Ctx.getIdentifier("two")), DeclRefKind::Ordinary, DeclNameLoc()); auto three = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("three"), + DeclNameRef(C.Ctx.getIdentifier("three")), DeclRefKind::Ordinary, DeclNameLoc()); auto four = new (C.Ctx) UnresolvedDeclRefExpr( - C.Ctx.getIdentifier("four"), + DeclNameRef(C.Ctx.getIdentifier("four")), DeclRefKind::Ordinary, DeclNameLoc(start.getAdvancedLoc(4))); diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index bf7865245b1d6..70d5e2edebb6b 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -30,11 +30,12 @@ static void declareOptionalType(ASTContext &ctx, SourceFile *fileForLookups, auto decl = new (ctx) EnumDecl(SourceLoc(), name, SourceLoc(), /*inherited*/{}, params, fileForLookups); wrapped->setDeclContext(decl); - fileForLookups->Decls.push_back(decl); + fileForLookups->addTopLevelDecl(decl); } TestContext::TestContext(ShouldDeclareOptionalTypes optionals) - : Ctx(*ASTContext::get(LangOpts, SearchPathOpts, SourceMgr, Diags)) { + : Ctx(*ASTContext::get(LangOpts, TypeCheckerOpts, SearchPathOpts, SourceMgr, + Diags)) { registerParseRequestFunctions(Ctx.evaluator); registerTypeCheckerRequestFunctions(Ctx.evaluator); auto stdlibID = Ctx.getIdentifier(STDLIB_NAME); diff --git a/unittests/AST/TestContext.h b/unittests/AST/TestContext.h index 428a933b0cd01..46aa8d985b62b 100644 --- a/unittests/AST/TestContext.h +++ b/unittests/AST/TestContext.h @@ -27,6 +27,7 @@ namespace unittest { class TestContextBase { public: LangOptions LangOpts; + TypeCheckerOptions TypeCheckerOpts; SearchPathOptions SearchPathOpts; SourceManager SourceMgr; DiagnosticEngine Diags; diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 2c922f88e6284..94d021452a1c1 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -69,12 +69,13 @@ TEST(ClangImporterTest, emitPCHInMemory) { // Set up the importer and emit a bridging PCH. swift::LangOptions langOpts; langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); + swift::TypeCheckerOptions typeckOpts; INITIALIZE_LLVM(); swift::SearchPathOptions searchPathOpts; swift::SourceManager sourceMgr; swift::DiagnosticEngine diags(sourceMgr); std::unique_ptr context( - ASTContext::get(langOpts, searchPathOpts, sourceMgr, diags)); + ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); auto importer = ClangImporter::create(*context, options); std::string PCH = createFilename(cache, "bridging.h.pch"); diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index d85fc16fe52a9..586053c4d71b7 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -1,8 +1,12 @@ add_swift_unittest(SwiftDriverTests - DependencyGraphTests.cpp + CoarseGrainedDependencyGraphTests.cpp + FineGrainedDependencyGraphTests.cpp + TypeBodyFingerprintsDependencyGraphTests.cpp ) target_link_libraries(SwiftDriverTests PRIVATE swiftDriver + swiftClangImporter + swiftAST ) diff --git a/unittests/Driver/DependencyGraphTests.cpp b/unittests/Driver/CoarseGrainedDependencyGraphTests.cpp similarity index 54% rename from unittests/Driver/DependencyGraphTests.cpp rename to unittests/Driver/CoarseGrainedDependencyGraphTests.cpp index 83382ee26e729..6690908961f2a 100644 --- a/unittests/Driver/DependencyGraphTests.cpp +++ b/unittests/Driver/CoarseGrainedDependencyGraphTests.cpp @@ -1,38 +1,39 @@ #include "swift/Basic/ReferenceDependencyKeys.h" -#include "swift/Driver/DependencyGraph.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" #include "gtest/gtest.h" using namespace swift; -using LoadResult = DependencyGraphImpl::LoadResult; +using LoadResult = CoarseGrainedDependencyGraphImpl::LoadResult; using namespace reference_dependency_keys; -static LoadResult loadFromString(DependencyGraph &dg, uintptr_t node, - StringRef key, StringRef data) { +static LoadResult loadFromString(CoarseGrainedDependencyGraph &dg, + uintptr_t node, StringRef key, + StringRef data) { return dg.loadFromString(node, key.str() + ": [" + data.str() + "]"); } -static LoadResult loadFromString(DependencyGraph &dg, uintptr_t node, - StringRef key1, StringRef data1, - StringRef key2, StringRef data2) { - return dg.loadFromString(node, - key1.str() + ": [" + data1.str() + "]\n" + - key2.str() + ": [" + data2.str() + "]"); +static LoadResult loadFromString(CoarseGrainedDependencyGraph &dg, + uintptr_t node, StringRef key1, + StringRef data1, StringRef key2, + StringRef data2) { + return dg.loadFromString(node, key1.str() + ": [" + data1.str() + "]\n" + + key2.str() + ": [" + data2.str() + "]"); } -static LoadResult loadFromString(DependencyGraph &dg, uintptr_t node, - StringRef key1, StringRef data1, - StringRef key2, StringRef data2, - StringRef key3, StringRef data3, - StringRef key4, StringRef data4) { - return dg.loadFromString(node, - key1.str() + ": [" + data1.str() + "]\n" + - key2.str() + ": [" + data2.str() + "]\n" + - key3.str() + ": [" + data3.str() + "]\n" + - key4.str() + ": [" + data4.str() + "]\n"); +static LoadResult loadFromString(CoarseGrainedDependencyGraph &dg, + uintptr_t node, StringRef key1, + StringRef data1, StringRef key2, + StringRef data2, StringRef key3, + StringRef data3, StringRef key4, + StringRef data4) { + return dg.loadFromString(node, key1.str() + ": [" + data1.str() + "]\n" + + key2.str() + ": [" + data2.str() + "]\n" + + key3.str() + ": [" + data3.str() + "]\n" + + key4.str() + ": [" + data4.str() + "]\n"); } -TEST(DependencyGraph, BasicLoad) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, BasicLoad) { + CoarseGrainedDependencyGraph graph; uintptr_t i = 0; EXPECT_EQ(loadFromString(graph, i++, dependsTopLevel, "a, b"), @@ -62,8 +63,8 @@ TEST(DependencyGraph, BasicLoad) { LoadResult::UpToDate); } -TEST(DependencyGraph, IndependentNodes) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, IndependentNodes) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsTopLevel, "a", @@ -78,36 +79,30 @@ TEST(DependencyGraph, IndependentNodes) { providesTopLevel, "c0"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); // Mark 0 again -- should be no change. - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); - graph.markTransitive(marked, 2); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(2).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); - graph.markTransitive(marked, 1); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(1).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, IndependentDepKinds) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, IndependentDepKinds) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsNominal, "a", @@ -118,16 +113,13 @@ TEST(DependencyGraph, IndependentDepKinds) { providesTopLevel, "a"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); } -TEST(DependencyGraph, IndependentDepKinds2) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, IndependentDepKinds2) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsNominal, "a", @@ -138,16 +130,13 @@ TEST(DependencyGraph, IndependentDepKinds2) { providesTopLevel, "a"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 1); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(1).size()); EXPECT_FALSE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, IndependentMembers) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, IndependentMembers) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesMember, "[a,aa]"), LoadResult::UpToDate); @@ -160,10 +149,7 @@ TEST(DependencyGraph, IndependentMembers) { EXPECT_EQ(loadFromString(graph, 4, dependsMember, "[b,bb]"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); @@ -171,77 +157,71 @@ TEST(DependencyGraph, IndependentMembers) { EXPECT_FALSE(graph.isMarked(4)); } -TEST(DependencyGraph, SimpleDependent) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c"), LoadResult::UpToDate); EXPECT_EQ(loadFromString(graph, 1, dependsTopLevel, "x, b, z"), LoadResult::UpToDate); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); - EXPECT_TRUE(graph.isMarked(0)); - EXPECT_TRUE(graph.isMarked(1)); - - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); - EXPECT_TRUE(graph.isMarked(0)); - EXPECT_TRUE(graph.isMarked(1)); + EXPECT_EQ(0u, graph.markTransitive(0).size()); + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependentReverse) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependentReverse) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsTopLevel, "a, b, c"), LoadResult::UpToDate); EXPECT_EQ(loadFromString(graph, 1, providesTopLevel, "x, b, z"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 1); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(0u, marked.front()); + { + auto found = graph.markTransitive(1); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(0u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependent2) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent2) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b, c"), LoadResult::UpToDate); EXPECT_EQ(loadFromString(graph, 1, dependsNominal, "x, b, z"), LoadResult::UpToDate); - SmallVector marked; - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependent3) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent3) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a", @@ -250,23 +230,21 @@ TEST(DependencyGraph, SimpleDependent3) { EXPECT_EQ(loadFromString(graph, 1, dependsNominal, "a"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependent4) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent4) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a"), LoadResult::UpToDate); @@ -275,23 +253,21 @@ TEST(DependencyGraph, SimpleDependent4) { dependsTopLevel, "a"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependent5) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent5) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a", @@ -302,47 +278,45 @@ TEST(DependencyGraph, SimpleDependent5) { dependsTopLevel, "a"), LoadResult::UpToDate); - SmallVector marked; - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + auto found = graph.markTransitive(0); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependent6) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependent6) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesDynamicLookup, "a, b, c"), LoadResult::UpToDate); EXPECT_EQ(loadFromString(graph, 1, dependsDynamicLookup, "x, b, z"), LoadResult::UpToDate); - SmallVector marked; + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); - EXPECT_TRUE(graph.isMarked(0)); - EXPECT_TRUE(graph.isMarked(1)); - - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); - EXPECT_TRUE(graph.isMarked(0)); - EXPECT_TRUE(graph.isMarked(1)); + EXPECT_EQ(0u, graph.markTransitive(0).size()); + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleDependentMember) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleDependentMember) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesMember, "[a,aa], [b,bb], [c,cc]"), @@ -351,17 +325,15 @@ TEST(DependencyGraph, SimpleDependentMember) { dependsMember, "[x, xx], [b,bb], [z,zz]"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } @@ -372,8 +344,8 @@ static bool contains(const Range &range, const T &value) { return std::find(std::begin(range),std::end(range),value) != std::end(range); } -TEST(DependencyGraph, MultipleDependentsSame) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MultipleDependentsSame) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b, c"), LoadResult::UpToDate); @@ -382,26 +354,24 @@ TEST(DependencyGraph, MultipleDependentsSame) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "q, b, s"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, 1)); - EXPECT_TRUE(contains(marked, 2)); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, MultipleDependentsDifferent) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MultipleDependentsDifferent) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b, c"), LoadResult::UpToDate); @@ -410,26 +380,24 @@ TEST(DependencyGraph, MultipleDependentsDifferent) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "q, r, c"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, 1)); - EXPECT_TRUE(contains(marked, 2)); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, ChainedDependents) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ChainedDependents) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b, c"), LoadResult::UpToDate); @@ -440,26 +408,71 @@ TEST(DependencyGraph, ChainedDependents) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "z"), LoadResult::UpToDate); - SmallVector marked; - graph.markTransitive(marked, 0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, 1)); - EXPECT_TRUE(contains(marked, 2)); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, MarkTwoNodes) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ChainedNoncascadingDependents) { + CoarseGrainedDependencyGraph graph; + + EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b, c"), + LoadResult::UpToDate); + EXPECT_EQ( + loadFromString(graph, 1, dependsNominal, "x, b", providesNominal, "z"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "!private z"), + LoadResult::UpToDate); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); + + EXPECT_EQ(0u, graph.markTransitive(0).size()); + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); +} + +TEST(CoarseGrainedDependencyGraph, ChainedNoncascadingDependents2) { + CoarseGrainedDependencyGraph graph; + + EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 1, dependsTopLevel, "x, !private b", + providesNominal, "z"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "z"), + LoadResult::UpToDate); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_TRUE(contains(found, 1)); + } + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_FALSE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); +} + +TEST(CoarseGrainedDependencyGraph, MarkTwoNodes) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b"), LoadResult::UpToDate); @@ -480,12 +493,12 @@ TEST(DependencyGraph, MarkTwoNodes) { providesNominal, "q"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, 1)); - EXPECT_TRUE(contains(marked, 2)); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); @@ -493,10 +506,11 @@ TEST(DependencyGraph, MarkTwoNodes) { EXPECT_FALSE(graph.isMarked(11)); EXPECT_FALSE(graph.isMarked(12)); - marked.clear(); - graph.markTransitive(marked, 10); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(11u, marked.front()); +{ + auto found = graph.markTransitive(10); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(11u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); @@ -505,8 +519,8 @@ TEST(DependencyGraph, MarkTwoNodes) { EXPECT_FALSE(graph.isMarked(12)); } -TEST(DependencyGraph, MarkOneNodeTwice) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MarkOneNodeTwice) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a"), LoadResult::UpToDate); @@ -515,11 +529,12 @@ TEST(DependencyGraph, MarkOneNodeTwice) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "b"), LoadResult::UpToDate); - SmallVector marked; - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); @@ -527,18 +542,19 @@ TEST(DependencyGraph, MarkOneNodeTwice) { // Reload 0. EXPECT_EQ(loadFromString(graph, 0, providesNominal, "b"), LoadResult::UpToDate); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(2u, marked.front()); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(2u, found.front()); +} EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, MarkOneNodeTwice2) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MarkOneNodeTwice2) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a"), LoadResult::UpToDate); @@ -547,11 +563,11 @@ TEST(DependencyGraph, MarkOneNodeTwice2) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "b"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); +{ + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); @@ -559,18 +575,19 @@ TEST(DependencyGraph, MarkOneNodeTwice2) { // Reload 0. EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a, b"), LoadResult::UpToDate); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(2u, marked.front()); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(2u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, NotTransitiveOnceMarked) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ReloadDetectsChange) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a"), LoadResult::UpToDate); @@ -579,10 +596,50 @@ TEST(DependencyGraph, NotTransitiveOnceMarked) { EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "b"), LoadResult::UpToDate); - SmallVector marked; + { + const auto found = graph.markTransitive(1); + EXPECT_EQ(0u, found.size()); + } + EXPECT_FALSE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); + + // Reload 1. + EXPECT_EQ(loadFromString(graph, 1, dependsNominal, "a", providesNominal, "b"), + LoadResult::UpToDate); + + { + auto found = graph.markTransitive(0); + EXPECT_EQ(0u, found.size()); + } + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); + + // Re-mark 1. + { + auto found = graph.markTransitive(1); + EXPECT_EQ(1u, found.size()); + EXPECT_TRUE(contains(found, 2)); + } + + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_TRUE(graph.isMarked(2)); +} - graph.markTransitive(marked, 1); - EXPECT_EQ(0u, marked.size()); +TEST(CoarseGrainedDependencyGraph, NotTransitiveOnceMarked) { + CoarseGrainedDependencyGraph graph; + + EXPECT_EQ(loadFromString(graph, 0, providesNominal, "a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 1, dependsNominal, "a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 2, dependsNominal, "b"), + LoadResult::UpToDate); + + + EXPECT_EQ(0u, graph.markTransitive(1).size()); EXPECT_FALSE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); @@ -592,25 +649,25 @@ TEST(DependencyGraph, NotTransitiveOnceMarked) { dependsNominal, "a", providesNominal, "b"), LoadResult::UpToDate); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_FALSE(graph.isMarked(2)); // Re-mark 1. - graph.markTransitive(marked, 1); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(2u, marked.front()); + { + auto found = graph.markTransitive(1); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(2u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, DependencyLoops) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, DependencyLoops) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c", @@ -624,26 +681,24 @@ TEST(DependencyGraph, DependencyLoops) { EXPECT_EQ(loadFromString(graph, 2, dependsTopLevel, "x"), LoadResult::UpToDate); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(2u, marked.size()); - EXPECT_TRUE(contains(marked, 1)); - EXPECT_TRUE(contains(marked, 2)); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, 1)); + EXPECT_TRUE(contains(found, 2)); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); - marked.clear(); - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); EXPECT_TRUE(graph.isMarked(2)); } -TEST(DependencyGraph, MarkIntransitive) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MarkIntransitive) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c"), LoadResult::UpToDate); @@ -654,17 +709,17 @@ TEST(DependencyGraph, MarkIntransitive) { EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); + { + auto found = graph.markTransitive(0); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, MarkIntransitiveTwice) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MarkIntransitiveTwice) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c"), LoadResult::UpToDate); @@ -680,8 +735,8 @@ TEST(DependencyGraph, MarkIntransitiveTwice) { EXPECT_FALSE(graph.isMarked(1)); } -TEST(DependencyGraph, MarkIntransitiveThenIndirect) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, MarkIntransitiveThenIndirect) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, providesTopLevel, "a, b, c"), LoadResult::UpToDate); @@ -692,16 +747,13 @@ TEST(DependencyGraph, MarkIntransitiveThenIndirect) { EXPECT_FALSE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - SmallVector marked; - - graph.markTransitive(marked, 0); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markTransitive(0).size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, SimpleExternal) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleExternal) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsExternal, "/foo, /bar"), LoadResult::UpToDate); @@ -709,36 +761,28 @@ TEST(DependencyGraph, SimpleExternal) { EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - SmallVector marked; - graph.markExternal(marked, "/foo"); - EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(1u, graph.markExternal("/foo").size()); EXPECT_TRUE(graph.isMarked(0)); - marked.clear(); - graph.markExternal(marked, "/foo"); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markExternal("/foo").size()); EXPECT_TRUE(graph.isMarked(0)); } -TEST(DependencyGraph, SimpleExternal2) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, SimpleExternal2) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsExternal, "/foo, /bar"), LoadResult::UpToDate); - SmallVector marked; - graph.markExternal(marked, "/bar"); - EXPECT_EQ(1u, marked.size()); + EXPECT_EQ(1u, graph.markExternal("/bar").size()); EXPECT_TRUE(graph.isMarked(0)); - marked.clear(); - graph.markExternal(marked, "/bar"); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markExternal("/bar").size()); EXPECT_TRUE(graph.isMarked(0)); } -TEST(DependencyGraph, ChainedExternal) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ChainedExternal) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsExternal, "/foo", @@ -752,21 +796,17 @@ TEST(DependencyGraph, ChainedExternal) { EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); - SmallVector marked; - graph.markExternal(marked, "/foo"); - EXPECT_EQ(2u, marked.size()); + EXPECT_EQ(2u, graph.markExternal("/foo").size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markExternal(marked, "/foo"); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markExternal("/foo").size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, ChainedExternalReverse) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ChainedExternalReverse) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsExternal, "/foo", @@ -777,29 +817,29 @@ TEST(DependencyGraph, ChainedExternalReverse) { dependsTopLevel, "a"), LoadResult::UpToDate); - SmallVector marked; - graph.markExternal(marked, "/bar"); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(1u, marked.front()); + { + auto found = graph.markExternal("/bar"); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(1u, found.front()); + } EXPECT_FALSE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markExternal(marked, "/bar"); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markExternal("/bar").size()); EXPECT_FALSE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); - marked.clear(); - graph.markExternal(marked, "/foo"); - EXPECT_EQ(1u, marked.size()); - EXPECT_EQ(0u, marked.front()); +{ + auto found = graph.markExternal("/foo"); + EXPECT_EQ(1u, found.size()); + EXPECT_EQ(0u, found.front()); +} EXPECT_TRUE(graph.isMarked(0)); EXPECT_TRUE(graph.isMarked(1)); } -TEST(DependencyGraph, ChainedExternalPreMarked) { - DependencyGraph graph; +TEST(CoarseGrainedDependencyGraph, ChainedExternalPreMarked) { + CoarseGrainedDependencyGraph graph; EXPECT_EQ(loadFromString(graph, 0, dependsExternal, "/foo", @@ -812,9 +852,61 @@ TEST(DependencyGraph, ChainedExternalPreMarked) { graph.markIntransitive(0); - SmallVector marked; - graph.markExternal(marked, "/foo"); - EXPECT_EQ(0u, marked.size()); + EXPECT_EQ(0u, graph.markExternal("/foo").size()); EXPECT_TRUE(graph.isMarked(0)); EXPECT_FALSE(graph.isMarked(1)); } + +TEST(CoarseGrainedDependencyGraph, ChainedPrivateDoesNotCascade) { + CoarseGrainedDependencyGraph graph; + EXPECT_EQ(loadFromString(graph, 0, + providesNominal, "z", + dependsTopLevel, "!private a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 1, + providesTopLevel, "a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 2, + dependsNominal, "z"), + LoadResult::UpToDate); + + const auto nodes = graph.markTransitive(1); // compiled 1 + EXPECT_EQ(nodes.size(), 1u); // need to compile 0 but not 2 + EXPECT_TRUE(contains(nodes, 0)); + EXPECT_FALSE(graph.isMarked(0)); +} + +TEST(CoarseGrainedDependencyGraph, CrashSimple) { + CoarseGrainedDependencyGraph graph; + EXPECT_EQ(loadFromString(graph, 0, + providesTopLevel, "a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 1, + dependsTopLevel, "a"), + LoadResult::UpToDate); + EXPECT_EQ(loadFromString(graph, 2, + dependsTopLevel, "!private a"), + LoadResult::UpToDate); + + const auto nodes = graph.markTransitive(0); + EXPECT_EQ(nodes.size(), 2u); // need to compile 0 but not 2 + EXPECT_TRUE(contains(nodes, 1)); + EXPECT_TRUE(contains(nodes, 2)); + EXPECT_TRUE(graph.isMarked(0)); + EXPECT_TRUE(graph.isMarked(1)); + EXPECT_FALSE(graph.isMarked(2)); +} + + +TEST(CoarseGrainedDependencyGraph, MutualInterfaceHash) { + CoarseGrainedDependencyGraph graph; + loadFromString(graph, 0, + providesTopLevel, "a", + dependsTopLevel, "b"); + loadFromString(graph, 1, + dependsTopLevel, "a", + providesTopLevel, "b"); + + const auto nodes = graph.markTransitive(0); + EXPECT_TRUE(contains(nodes, 1)); +} diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp new file mode 100644 index 0000000000000..28e8286866d9e --- /dev/null +++ b/unittests/Driver/FineGrainedDependencyGraphTests.cpp @@ -0,0 +1,826 @@ +#include "swift/Basic/ReferenceDependencyKeys.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" +#include "swift/Driver/FineGrainedDependencyDriverGraph.h" +#include "swift/Driver/Job.h" +#include "gtest/gtest.h" + +// This file adapts the unit tests from the older, coarse-grained, dependency +// graph to the new fine-grained graph. + +// \c findJobsToRecompileWhenWholeJobChanges and \c +// findExternallyDependentUntracedJobs may include jobs in their result that +// would be excluded in the coarse-grained graph. But since these will be jobs +// that have already been scheduled, downstream mechanisms will filter them out. + +using namespace swift; +using namespace reference_dependency_keys; +using namespace fine_grained_dependencies; +using Job = driver::Job; + + +static OutputFileMap OFM; + +static Job + job0(OFM, "0"), + job1(OFM, "1"), + job2(OFM, "2"), + job3(OFM, "3"), + job4(OFM, "4"), + job5(OFM, "5"), + job6(OFM, "6"), + job7(OFM, "7"), + job8(OFM, "8"), + job9(OFM, "9"), + job10(OFM, "10"), + job11(OFM, "11"), + job12(OFM, "12"); + +template +static bool contains(const Range &range, const T &value) { + return std::find(std::begin(range), std::end(range), value) != + std::end(range); +} + +TEST(ModuleDepGraph, BasicLoad) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{dependsTopLevel, {"a", "b"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"c", "d"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{providesTopLevel, {"e", "f"}}})); + EXPECT_TRUE(graph.simulateLoad( &job3, {{providesNominal, {"g", "h"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job4, {{providesDynamicLookup, {"i", "j"}}})); + EXPECT_TRUE(graph.simulateLoad( &job5, {{dependsDynamicLookup, {"k", "l"}}})); + EXPECT_TRUE(graph.simulateLoad( &job6, {}, + {{providesMember, {{"m", "mm"}, {"n", "nn"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job7, {}, + {{dependsMember, {{"o", "oo"}, {"p", "pp"}}}})); + EXPECT_TRUE( + graph.simulateLoad( &job8, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_TRUE(graph.simulateLoad( &job9, + {{providesNominal, {"a", "b"}}, + {providesTopLevel, {"b", "c"}}, + {dependsNominal, {"c", "d"}}, + {dependsTopLevel, {"d", "a"}}})); +} + +TEST(ModuleDepGraph, IndependentNodes) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsTopLevel, {"a"}}, {providesTopLevel, {"a0"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"b0"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job2, {{dependsTopLevel, {"c"}}, {providesTopLevel, {"c0"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Mark 0 again -- should be no change. + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job2).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, IndependentDepKinds) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, IndependentDepKinds2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, IndependentMembers) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {}, {{providesMember, {{"a", "aa"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {}, {{dependsMember, {{"a", "bb"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {}, {{dependsMember, {{"a", ""}}}})); + EXPECT_TRUE(graph.simulateLoad( &job3, {}, {{dependsMember, {{"b", "aa"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job4, {}, {{dependsMember, {{"b", "bb"}}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job3)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job4)); +} + +TEST(ModuleDepGraph, SimpleDependent) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependentReverse) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{dependsTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {{providesTopLevel, {"x", "b", "z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, jobs.size()); + EXPECT_EQ(&job0, jobs.front()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent3) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent4) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent5) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependent6) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesDynamicLookup, {"a", "b", "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {{dependsDynamicLookup, {"x", "b", "z"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleDependentMember) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {}, + {{providesMember, {{"a", "aa"}, {"b", "bb"}, {"c", "cc"}}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {}, + {{dependsMember, {{"x", "xx"}, {"b", "bb"}, {"z", "zz"}}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, MultipleDependentsSame) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"q", "b", "s"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, MultipleDependentsDifferent) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"q", "r", "c"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, ChainedDependents) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"x", "b"}}, {providesNominal, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, ChainedNoncascadingDependents) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"x", "b"}}, {providesNominal, {SourceFileDepGraph::noncascading("z")}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {SourceFileDepGraph::noncascading("z")}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, ChainedNoncascadingDependents2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", SourceFileDepGraph::noncascading("b"), "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, + {{dependsTopLevel, {"x", SourceFileDepGraph::noncascading("b")}}, {providesNominal, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, MarkTwoNodes) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"a"}}, {providesTopLevel, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsTopLevel, {"z"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job10, + {{providesTopLevel, {"y", "z"}}, {dependsTopLevel, {"q"}}})); + EXPECT_TRUE(graph.simulateLoad( &job11, {{dependsTopLevel, {"y"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job12, {{dependsTopLevel, {"q"}}, {providesTopLevel, {"q"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); //????? + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job10)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job11)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job10); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job10)); + EXPECT_TRUE(contains(jobs, &job11)); + EXPECT_FALSE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job10)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job11)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); +} + +TEST(ModuleDepGraph, MarkOneNodeTwice) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 0. + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, MarkOneNodeTwice2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 0. + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, ReloadDetectsChange) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 1. + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Re-mark 1. + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, NotTransitiveOnceMarked) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 1. + EXPECT_TRUE(graph.simulateLoad( &job1, + {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Re-mark 1. + { + auto found = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, &job1)); + EXPECT_TRUE(contains(found, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, DependencyLoops) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, + {{providesTopLevel, {"a", "b", "c"}}, {dependsTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, + {{providesTopLevel, {"x"}}, {dependsTopLevel, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsTopLevel, {"x"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(0u, jobs.size()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraph, MarkIntransitive) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, MarkIntransitiveTwice) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, MarkIntransitiveThenIndirect) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, SimpleExternal) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 1u); + EXPECT_TRUE(contains(jobs, &job0)); + } + + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/foo").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); +} + +TEST(ModuleDepGraph, SimpleExternal2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_EQ(1u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); +} + +TEST(ModuleDepGraph, ChainedExternal) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 2u); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 0u); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, ChainedExternalReverse) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/bar"); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(1u, jobs.size()); + EXPECT_EQ(&job0, jobs.front()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraph, ChainedExternalPreMarked) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + + +TEST(ModuleDepGraph, MutualInterfaceHash) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + graph.simulateLoad( &job0, { + {providesTopLevel, {"a"}}, + {dependsTopLevel, {"b"}} + }); + graph.simulateLoad( &job1, { + {dependsTopLevel, {"a"}}, + {providesTopLevel, {"b"}} + }); + + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_TRUE(contains(jobs, &job1)); +} + +TEST(ModuleDepGraph, DisabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } +} diff --git a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp new file mode 100644 index 0000000000000..041cc9c2ee416 --- /dev/null +++ b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp @@ -0,0 +1,831 @@ +#include "swift/Basic/ReferenceDependencyKeys.h" +#include "swift/Driver/CoarseGrainedDependencyGraph.h" +#include "swift/Driver/FineGrainedDependencyDriverGraph.h" +#include "swift/Driver/Job.h" +#include "gtest/gtest.h" + +// This file adapts the unit tests from the older, coarse-grained, dependency +// graph to the new fine-grained graph. + +// \c findJobsToRecompileWhenWholeJobChanges and \c +// findExternallyDependentUntracedJobs may include jobs in their result that +// would be excluded in the coarse-grained graph. But since these will be jobs +// that have already been scheduled, downstream mechanisms will filter them out. + +using namespace swift; +using namespace reference_dependency_keys; +using namespace fine_grained_dependencies; +using Job = driver::Job; + + +static OutputFileMap OFM; + +static Job + job0(OFM, "0"), + job1(OFM, "1"), + job2(OFM, "2"), + job3(OFM, "3"), + job4(OFM, "4"), + job5(OFM, "5"), + job6(OFM, "6"), + job7(OFM, "7"), + job8(OFM, "8"), + job9(OFM, "9"), + job10(OFM, "10"), + job11(OFM, "11"), + job12(OFM, "12"); + +template +static bool contains(const Range &range, const T &value) { + return std::find(std::begin(range), std::end(range), value) != + std::end(range); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, BasicLoad) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{dependsTopLevel, {"a", "b"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"c", "d"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{providesTopLevel, {"e", "f"}}})); + EXPECT_TRUE(graph.simulateLoad( &job3, {{providesNominal, {"g", "h"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job4, {{providesDynamicLookup, {"i", "j"}}})); + EXPECT_TRUE(graph.simulateLoad( &job5, {{dependsDynamicLookup, {"k", "l"}}})); + EXPECT_TRUE(graph.simulateLoad( &job6, {}, + {{providesMember, {{"m", "mm"}, {"n", "nn"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job7, {}, + {{dependsMember, {{"o", "oo"}, {"p", "pp"}}}})); + EXPECT_TRUE( + graph.simulateLoad( &job8, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_TRUE(graph.simulateLoad( &job9, + {{providesNominal, {"a", "b"}}, + {providesTopLevel, {"b", "c"}}, + {dependsNominal, {"c", "d"}}, + {dependsTopLevel, {"d", "a"}}})); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, IndependentNodes) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsTopLevel, {"a"}}, {providesTopLevel, {"a0"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"b0"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job2, {{dependsTopLevel, {"c"}}, {providesTopLevel, {"c0"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Mark 0 again -- should be no change. + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job2).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, IndependentDepKinds) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, IndependentDepKinds2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"b"}}, {providesTopLevel, {"a"}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job1).size()); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, IndependentMembers) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {}, {{providesMember, {{"a", "aa"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {}, {{dependsMember, {{"a", "bb"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {}, {{dependsMember, {{"a", ""}}}})); + EXPECT_TRUE(graph.simulateLoad( &job3, {}, {{dependsMember, {{"b", "aa"}}}})); + EXPECT_TRUE(graph.simulateLoad( &job4, {}, {{dependsMember, {{"b", "bb"}}}})); + + EXPECT_EQ(1u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job3)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job4)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependentReverse) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{dependsTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {{providesTopLevel, {"x", "b", "z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, jobs.size()); + EXPECT_EQ(&job0, jobs.front()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent3) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent4) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent5) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{providesNominal, {"a"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependent6) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesDynamicLookup, {"a", "b", "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {{dependsDynamicLookup, {"x", "b", "z"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleDependentMember) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {}, + {{providesMember, {{"a", "aa"}, {"b", "bb"}, {"c", "cc"}}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, {}, + {{dependsMember, {{"x", "xx"}, {"b", "bb"}, {"z", "zz"}}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MultipleDependentsSame) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"q", "b", "s"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MultipleDependentsDifferent) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"q", "r", "c"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedDependents) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"x", "b"}}, {providesNominal, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedNoncascadingDependents) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"x", "b"}}, {providesNominal, {SourceFileDepGraph::noncascading("z")}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {SourceFileDepGraph::noncascading("z")}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + EXPECT_EQ(0u, graph.findJobsToRecompileWhenWholeJobChanges(&job0).size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedNoncascadingDependents2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", SourceFileDepGraph::noncascading("b"), "c"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job1, + {{dependsTopLevel, {"x", SourceFileDepGraph::noncascading("b")}}, {providesNominal, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"z"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkTwoNodes) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsTopLevel, {"a"}}, {providesTopLevel, {"z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsTopLevel, {"z"}}})); + EXPECT_TRUE( + graph.simulateLoad( &job10, + {{providesTopLevel, {"y", "z"}}, {dependsTopLevel, {"q"}}})); + EXPECT_TRUE(graph.simulateLoad( &job11, {{dependsTopLevel, {"y"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job12, {{dependsTopLevel, {"q"}}, {providesTopLevel, {"q"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); //????? + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job10)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job11)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job10); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job10)); + EXPECT_TRUE(contains(jobs, &job11)); + EXPECT_FALSE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job10)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job11)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job12)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkOneNodeTwice) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 0. + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkOneNodeTwice2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 0. + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a", "b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ReloadDetectsChange) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 1. + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Re-mark 1. + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, NotTransitiveOnceMarked) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( &job0, {{providesNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsNominal, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsNominal, {"b"}}})); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Reload 1. + EXPECT_TRUE(graph.simulateLoad( &job1, + {{dependsNominal, {"a"}}, {providesNominal, {"b"}}})); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + // Re-mark 1. + { + auto found = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(2u, found.size()); + EXPECT_TRUE(contains(found, &job1)); + EXPECT_TRUE(contains(found, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, DependencyLoops) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, + {{providesTopLevel, {"a", "b", "c"}}, {dependsTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, + {{providesTopLevel, {"x"}}, {dependsTopLevel, {"x", "b", "z"}}})); + EXPECT_TRUE(graph.simulateLoad( &job2, {{dependsTopLevel, {"x"}}})); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(0u, jobs.size()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job2)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkIntransitive) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkIntransitiveTwice) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MarkIntransitiveThenIndirect) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{providesTopLevel, {"a", "b", "c"}}})); + EXPECT_TRUE(graph.simulateLoad( &job1, {{dependsTopLevel, {"x", "b", "z"}}})); + + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleExternal) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 1u); + EXPECT_TRUE(contains(jobs, &job0)); + } + + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/foo").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, SimpleExternal2) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE( + graph.simulateLoad( &job0, {{dependsExternal, {"/foo", "/bar"}}})); + + EXPECT_EQ(1u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedExternal) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/foo")); + EXPECT_TRUE(contains(graph.getExternalDependencies(), "/bar")); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 2u); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(jobs.size(), 0u); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedExternalReverse) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/bar"); + EXPECT_EQ(1u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + EXPECT_EQ(0u, graph.findExternallyDependentUntracedJobs("/bar").size()); + EXPECT_FALSE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(1u, jobs.size()); + EXPECT_EQ(&job0, jobs.front()); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + +TEST(ModuleDepGraphWithTypeBodyFingerprints, ChainedExternalPreMarked) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + EXPECT_TRUE(graph.simulateLoad( + &job0, {{dependsExternal, {"/foo"}}, {providesTopLevel, {"a"}}})); + EXPECT_TRUE(graph.simulateLoad( + &job1, {{dependsExternal, {"/bar"}}, {dependsTopLevel, {"a"}}})); + + { + auto jobs = graph.findExternallyDependentUntracedJobs("/foo"); + EXPECT_EQ(2u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + } + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job0)); + EXPECT_TRUE(graph.haveAnyNodesBeenTraversedIn(&job1)); +} + + +TEST(ModuleDepGraphWithTypeBodyFingerprints, MutualInterfaceHash) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + graph.simulateLoad( &job0, { + {providesTopLevel, {"a"}}, + {dependsTopLevel, {"b"}} + }); + graph.simulateLoad( &job1, { + {dependsTopLevel, {"a"}}, + {providesTopLevel, {"b"}} + }); + + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); + EXPECT_TRUE(contains(jobs, &job1)); +} + +TEST(ModuleDepGraph, EnabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + + const DependencyKey k = DependencyKey(NodeKind::nominal, + DeclAspect::interface, "B1", ""); + std::vector changedNodes; + graph.forEachMatchingNode( + k, + [&](ModuleDepGraphNode* n) {changedNodes.push_back(n);}); + { + const auto jobs = graph.findJobsToRecompileWhenNodesChange(changedNodes); + EXPECT_TRUE(contains(jobs, &job2)); + EXPECT_FALSE(contains(jobs, &job0)); + } +} diff --git a/unittests/FrontendTool/ModuleLoadingTests.cpp b/unittests/FrontendTool/ModuleLoadingTests.cpp index de3d95a266b87..0ad89a65878b5 100644 --- a/unittests/FrontendTool/ModuleLoadingTests.cpp +++ b/unittests/FrontendTool/ModuleLoadingTests.cpp @@ -94,10 +94,12 @@ class ModuleInterfaceLoaderTest : public testing::Test { PrintingDiagnosticConsumer printingConsumer; DiagnosticEngine diags(sourceMgr); diags.addConsumer(printingConsumer); + TypeCheckerOptions typeckOpts; LangOptions langOpts; langOpts.Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); SearchPathOptions searchPathOpts; - auto ctx = ASTContext::get(langOpts, searchPathOpts, sourceMgr, diags); + auto ctx = + ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags); auto loader = ModuleInterfaceLoader::create( *ctx, cacheDir, prebuiltCacheDir, @@ -111,8 +113,9 @@ class ModuleInterfaceLoaderTest : public testing::Test { std::unique_ptr moduleSourceInfoBuffer; auto error = - loader->findModuleFilesInDirectory({moduleName, SourceLoc()}, tempDir, - "Library.swiftmodule", "Library.swiftdoc", "Library.swiftsourceinfo", + loader->findModuleFilesInDirectory({moduleName, SourceLoc()}, + SerializedModuleBaseName(tempDir, SerializedModuleBaseName("Library")), + /*ModuleInterfacePath*/nullptr, &moduleBuffer, &moduleDocBuffer, &moduleSourceInfoBuffer); ASSERT_FALSE(error); ASSERT_FALSE(diags.hadAnyError()); diff --git a/unittests/Parse/LexerTests.cpp b/unittests/Parse/LexerTests.cpp index 5d08515535438..8cf4db3c60238 100644 --- a/unittests/Parse/LexerTests.cpp +++ b/unittests/Parse/LexerTests.cpp @@ -747,16 +747,13 @@ TEST_F(LexerTest, NestedPlaceholder) { class StringCaptureDiagnosticConsumer : public DiagnosticConsumer { public: - virtual void - handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, - StringRef FormatString, - ArrayRef FormatArgs, - const swift::DiagnosticInfo &Info, - SourceLoc bufferIndirectlyCausingDiagnostic) override { + virtual void handleDiagnostic(SourceManager &SM, + const swift::DiagnosticInfo &Info) override { std::string DiagMsg; llvm::raw_string_ostream DiagOS(DiagMsg); - DiagnosticEngine::formatDiagnosticText(DiagOS, FormatString, FormatArgs); - auto LC = SM.getLineAndColumn(Loc); + DiagnosticEngine::formatDiagnosticText(DiagOS, Info.FormatString, + Info.FormatArgs); + auto LC = SM.getLineAndColumn(Info.Loc); std::ostringstream StrOS; StrOS << LC.first << ", " << LC.second << ": " << DiagOS.str(); messages.push_back(StrOS.str()); diff --git a/unittests/Parse/TokenizerTests.cpp b/unittests/Parse/TokenizerTests.cpp index 2ae73865f403b..36faa9634c010 100644 --- a/unittests/Parse/TokenizerTests.cpp +++ b/unittests/Parse/TokenizerTests.cpp @@ -82,7 +82,8 @@ class TokenizerTest : public ::testing::Test { } std::vector parseAndGetSplitTokens(unsigned BufID) { - swift::ParserUnit PU(SM, SourceFileKind::Main, BufID, LangOpts, "unknown"); + swift::ParserUnit PU(SM, SourceFileKind::Main, BufID, + LangOpts, TypeCheckerOptions(), "unknown"); bool Done = false; while (!Done) { diff --git a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp index 004b9ad94e4c4..193c8fcd4fad9 100644 --- a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp +++ b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp @@ -189,10 +189,10 @@ class CursorInfoTest : public ::testing::Test { } // anonymous namespace TEST_F(CursorInfoTest, FileNotExist) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let foo = 0\n"; - const char *Args[] = { "/" }; + const char *Args[] = { "" }; open(DocName, Contents); auto FooOffs = findOffset("foo =", Contents); @@ -205,7 +205,7 @@ static const char *ExpensiveInit = "[0:0,0:0,0:0,0:0,0:0,0:0,0:0]"; TEST_F(CursorInfoTest, EditAfter) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -240,7 +240,7 @@ TEST_F(CursorInfoTest, EditAfter) { } TEST_F(CursorInfoTest, EditBefore) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let foo = 0\n" "let value = foo;\n"; @@ -277,7 +277,7 @@ TEST_F(CursorInfoTest, EditBefore) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -307,7 +307,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -337,7 +337,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; @@ -368,7 +368,7 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { } TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "let value = foo\n" "let foo = 0\n"; const char *Args[] = {"-parse-as-library"}; diff --git a/unittests/SourceKit/SwiftLang/EditingTest.cpp b/unittests/SourceKit/SwiftLang/EditingTest.cpp index 85b62da5f2487..b383a133982bf 100644 --- a/unittests/SourceKit/SwiftLang/EditingTest.cpp +++ b/unittests/SourceKit/SwiftLang/EditingTest.cpp @@ -197,7 +197,7 @@ static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema"); static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse"); TEST_F(EditTest, DiagsAfterEdit) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "func foo {}\n" "let v = 0\n"; @@ -234,7 +234,7 @@ TEST_F(EditTest, DiagsAfterEdit) { void EditTest::doubleOpenWithDelay(std::chrono::microseconds delay, bool closeDoc) { - const char *DocName = "/test.swift"; + const char *DocName = "test.swift"; const char *Contents = "func foo() { _ = unknown_name }\n"; const char *Args[] = { "-parse-as-library" }; diff --git a/unittests/SyntaxParser/CMakeLists.txt b/unittests/SyntaxParser/CMakeLists.txt index 6082cbed76a87..e74178e182b5b 100644 --- a/unittests/SyntaxParser/CMakeLists.txt +++ b/unittests/SyntaxParser/CMakeLists.txt @@ -4,23 +4,21 @@ swift_swap_compiler_if_needed("SyntaxParserUnitTests") add_swift_unittest(SwiftSyntaxParserTests SyntaxParserTests.cpp ) +if(NOT SWIFT_BUILT_STANDALONE AND NOT CMAKE_C_COMPILER_ID STREQUAL Clang) + add_dependencies(SwiftSyntaxParserTests clang) +endif() target_link_libraries(SwiftSyntaxParserTests PRIVATE libSwiftSyntaxParser) -if(APPLE) - # Prioritize finding the parser library from the build/lib directory. - # Otherwise it may find it from the 'lib/swift/macosx' directory which could - # be out-of-date. - get_target_property(link_flags SwiftSyntaxParserTests LINK_FLAGS) - set(link_flags "-Xlinker -rpath -Xlinker ${SWIFT_LIBRARY_OUTPUT_INTDIR} ${link_flags}") - set_property(TARGET SwiftSyntaxParserTests PROPERTY - LINK_FLAGS "${link_flags}") +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(SwiftSyntaxParserTests PROPERTIES + BUILD_RPATH ${SWIFT_LIBRARY_OUTPUT_INTDIR}) endif() set_property(TARGET SwiftSyntaxParserTests APPEND_STRING PROPERTY COMPILE_FLAGS " -fblocks") -if(SWIFT_NEED_EXPLICIT_LIBDISPATCH) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) target_link_libraries(SwiftSyntaxParserTests PRIVATE BlocksRuntime) endif() diff --git a/unittests/runtime/CompatibilityOverride.cpp b/unittests/runtime/CompatibilityOverride.cpp index 897490a2a480c..dc7d57e2cad68 100644 --- a/unittests/runtime/CompatibilityOverride.cpp +++ b/unittests/runtime/CompatibilityOverride.cpp @@ -60,7 +60,7 @@ struct OverrideSection { #include "../../stdlib/public/runtime/CompatibilityOverride.def" }; -OverrideSection Overrides __attribute__((section("__DATA,__swift51_hooks"))) = { +OverrideSection Overrides __attribute__((section("__DATA,__swift52_hooks"))) = { 0, #define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \ name ## Override, diff --git a/unittests/runtime/LongTests/CMakeLists.txt b/unittests/runtime/LongTests/CMakeLists.txt index a25efc9323fef..447e115401dcf 100644 --- a/unittests/runtime/LongTests/CMakeLists.txt +++ b/unittests/runtime/LongTests/CMakeLists.txt @@ -16,6 +16,9 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND # ${FOUNDATION_LIBRARY} # swiftStdlibUnittest${SWIFT_PRIMARY_VARIANT_SUFFIX} # ) + + # Link the Objective-C runtime. + list(APPEND PLATFORM_TARGET_LINK_LIBRARIES "objc") elseif(SWIFT_HOST_VARIANT STREQUAL "linux") list(APPEND PLATFORM_TARGET_LINK_LIBRARIES "atomic" diff --git a/unittests/runtime/LongTests/LongRefcounting.cpp b/unittests/runtime/LongTests/LongRefcounting.cpp index 3b83f10c77e24..d3cd449e91bff 100644 --- a/unittests/runtime/LongTests/LongRefcounting.cpp +++ b/unittests/runtime/LongTests/LongRefcounting.cpp @@ -239,8 +239,8 @@ static void unownedReleaseALot(TestObject *object, uint64_t count) { } } -// Maximum legal unowned retain count. 31 bits with no implicit +1. -const uint64_t maxURC = (1ULL << (32 - 1)) - 1; +// Maximum legal unowned retain count. 31 bits minus one with no implicit +1. +const uint64_t maxURC = (1ULL << (32 - 1)) - 2; TEST(LongRefcountingTest, unowned_retain_max) { // Don't generate millions of failures if something goes wrong. @@ -282,7 +282,7 @@ TEST(LongRefcountingTest, unowned_retain_overflow_DeathTest) { auto object = allocTestObject(&deinited, 1); // URC is 1. Retain to maxURC, then retain again and verify overflow error. - unownedRetainALot(object, maxURC - 1); + unownedRetainALot(object, maxURC); EXPECT_EQ(0u, deinited); EXPECT_ALLOCATED(object); ASSERT_DEATH(swift_unownedRetain(object), @@ -329,7 +329,7 @@ TEST(LongRefcountingTest, nonatomic_unowned_retain_overflow_DeathTest) { auto object = allocTestObject(&deinited, 1); // URC is 1. Retain to maxURC, then retain again and verify overflow error. - unownedRetainALot(object, maxURC - 1); + unownedRetainALot(object, maxURC); EXPECT_EQ(0u, deinited); EXPECT_ALLOCATED(object); ASSERT_DEATH(swift_nonatomic_unownedRetain(object), diff --git a/userdocs/CMakeLists.txt b/userdocs/CMakeLists.txt new file mode 100644 index 0000000000000..688a229164074 --- /dev/null +++ b/userdocs/CMakeLists.txt @@ -0,0 +1,3 @@ +swift_install_in_component(DIRECTORY diagnostics + DESTINATION "share/doc/swift" + COMPONENT compiler) diff --git a/userdocs/diagnostics/complex-closure-inference.md b/userdocs/diagnostics/complex-closure-inference.md new file mode 100644 index 0000000000000..de081fb665f41 --- /dev/null +++ b/userdocs/diagnostics/complex-closure-inference.md @@ -0,0 +1,31 @@ +Inferring Closure Types +--- +If a closure contains a single expression, Swift will consider its body in addition to its signature and the surrounding context when performing type inference. For example, in the following code the type of `doubler` is inferred to be `(Int) -> Int` using only its body: +``` +let doubler = { + $0 * 2 +} +``` +If a closure body is not a single expression, it will not be considered when inferring the closure type. This is consistent with how type inference works in other parts of the language, where it proceeds one statement at a time. For example, in the following code an error will be reported because the type of `evenDoubler` cannot be inferred from its surrounding context and no signature was provided: +``` +// error: unable to infer complex closure return type; add explicit type to disambiguate +let evenDoubler = { x in + if x.isMultiple(of: 2) { + return x * 2 + } else { + return x + } +} +``` +This can be fixed by providing additional contextual information: +``` +let evenDoubler: (Int) -> Int = { x in + // ... +} +``` +Or by giving the closure an explicit signature: +``` +let evenDoubler = { (x: Int) -> Int in + // ... +} +``` diff --git a/userdocs/diagnostics/dynamic-callable-requirements.md b/userdocs/diagnostics/dynamic-callable-requirements.md new file mode 100644 index 0000000000000..37481fb6984c0 --- /dev/null +++ b/userdocs/diagnostics/dynamic-callable-requirements.md @@ -0,0 +1,14 @@ +@dynamicCallable Implementation Requirements +--- +If a type is marked with the `@dynamicCallable` attribute, it must provide a valid implementation of `dynamicallyCall(withArguments:)`, `dynamicallyCall(withKeywordArguments:)`, or both. If it fails to do so, an error will be reported at compile-time. Note that an implementation of `dynamicallyCall(withKeywordArguments:)` is required to support calls with keyword arguments. + +To be considered valid, an implementation of `dynamicallyCall(withArguments:)` must: +- Be an instance method. `static` or `class` implementations are not allowed. +- Have an argument type which conforms to the `ExpressibleByArrayLiteral` protocol. Often, this will be the built in `Array` type. +- The return type of `dynamicallyCall(withArguments:)` may be any valid type. + +To be considered valid, an implementation of `dynamicallyCall(withKeywordArguments:)` must: +- Be an instance method. `static` or `class` implementations are not allowed. +- Have an argument type which conforms to the `ExpressibleByDictionaryLiteral` protocol. This can be `Dictionary`, `KeyValuePairs` (which may be used to support duplicated keyword arguments), or some other conforming type. +- The `Key` associated type of the argument type must conform to the `ExpressibleByStringLiteral` protocol. This type is used to represent the dynamic argument keywords. +- The `Value` associated type of the argument type and the return type of `dynamicallyCall(withKeywordArguments:)` may be any valid types. \ No newline at end of file diff --git a/userdocs/diagnostics/nominal-types.md b/userdocs/diagnostics/nominal-types.md new file mode 100644 index 0000000000000..58d8a2b4244fc --- /dev/null +++ b/userdocs/diagnostics/nominal-types.md @@ -0,0 +1,7 @@ +Nominal types +------------- +In Swift, a type is considered a nominal type if it is named. In other words, it has been defined by declaring the type somewhere in code. Examples of nominal types include classes, structs and enums, all of which must be declared before using them. Nominal types are an important concept in Swift because they can be extended, explicitly initialized using the 'MyType()' syntax, and may conform to protocols. + +In contrast, non-nominal types have none of these capabilities. A non-nominal type is any type which is not nominal. They are sometimes called ”structural types” because they are usually obtained by composing other types. Examples include function types like '(Int) -> (String)', tuple types like '(Int, String)', metatypes like 'Int.Type', and special types like 'Any' and 'AnyObject'. + +Whether the name of a protocol refers to a nominal or non-nominal type depends on where it appears in code. When used in a declaration or extension like 'extension MyProtocol { … }', 'MyProtocol' refers to a protocol type, which is nominal. This means that it may be extended and conform to protocols. However, when written as the type of a constant or variable, MyProtocol instead refers to a non-nominal, existential type. As a result, code like 'let value: MyProtocol = MyProtocol()' is not allowed because 'MyProtocol' refers to a non-nominal type in this context and cannot be explicitly initialized. diff --git a/utils/analyze_code_size.py b/utils/analyze_code_size.py index 4a8bf3db2e816..f78ce3f1d4d7d 100755 --- a/utils/analyze_code_size.py +++ b/utils/analyze_code_size.py @@ -87,11 +87,14 @@ def __init__(self): self.category_matching = [ ['Objective-C function', re.compile(r'.*[+-]\[')], ['C++', re.compile(r'_+swift')], + ['Generic specialization of stdlib', + re.compile(r'.*generic specialization.* of Swift\.')], + ['Generic specialization', + re.compile(r'.*generic specialization')], ['Merged function', re.compile(r'merged ')], ['Key path', re.compile(r'key path')], ['Function signature specialization', re.compile(r'function signature specialization')], - ['Generic specialization', re.compile(r'generic specialization')], ['Reabstraction thunk helper', re.compile(r'reabstraction thunk helper')], ['vtable thunk', re.compile(r'vtable thunk for')], diff --git a/utils/android/README.md b/utils/android/README.md deleted file mode 100644 index 28ad49b365caa..0000000000000 --- a/utils/android/README.md +++ /dev/null @@ -1,16 +0,0 @@ - -# Build Android Toolchain - -This toolchain will generate the .so and .swiftmodule files of the Swift standard library and Foundation framework for the Android environment, armv7 architecture. Those files are needed when building any Swift library to be included in an application for Android. - -To build the toolchain run: - -``` -android$ ./build-toolchain -``` - -It will be built on: - -``` -path/to/swift-source/swift-android-toolchain -``` diff --git a/utils/backtrace-check b/utils/backtrace-check index a304f68bc22ed..a8058cff27b46 100755 --- a/utils/backtrace-check +++ b/utils/backtrace-check @@ -1,54 +1,82 @@ #!/usr/bin/env python -# This script uses a regex to validate the output of our backtraces. -# The layout we assume is: -# -#
+ -# -# It currently just checks that the backtrace results in at least one correctly -# formatted entry. It does not directly validate the input since the output -# would not be robust against standard library changes. -# -# TODO: We could have the user pass in the frame number, library name, and -# demangled name. These we can always validate as true in a robust way. On the -# other hand, address and offset are more difficult to validate in a robust way -# in the face of small codegen differences, so without any further thought I -# imagine we can just check the format. -# -# 11 libswiftCore.dylib 0x000000000dce84d0l _fatalErrorMessage(StaticString, -# StaticString, StaticString, UInt, flags : UInt32) -> () + 444 - -from __future__ import print_function + +""" +This script uses a regex to validate the output of our backtraces. +The layout we assume is: + +
+ + +It currently just checks that the backtrace results in at least one correctly +formatted entry. It does not directly validate the input since the output +would not be robust against standard library changes. + +TODO: We could have the user pass in the frame number, library name, and +demangled name. These we can always validate as true in a robust way. On the +other hand, address and offset are more difficult to validate in a robust way +in the face of small codegen differences, so without any further thought I +imagine we can just check the format. + +11 libswiftCore.dylib 0x000000000dce84d0l _fatalErrorMessage(StaticString, +StaticString, StaticString, UInt, flags : UInt32) -> () + 444 +""" + + +from __future__ import absolute_import, print_function, unicode_literals import argparse import re import sys -def main(): +# ----------------------------------------------------------------------------- +# Constants + +DESCRIPTION = """ +Checks that a stacktrace dump follows canonical formatting. +""" + +TARGET_PATTERN = re.compile( + r'(?P\d+) +(?P\S+) +(?P
0x[0-9a-fA-F]{16}) ' + r'(?P[^+]+) [+] (?P\d+)') + +RESULTS_FORMAT = """Stack Trace Entry: +\tIndex: '%(index)s' +\tObject File: '%(object)s' +\tAddress: '%(address)s' +\tRoutine: '%(routine)s' +\tOffset: '%(offset)s' +""" + + +# ----------------------------------------------------------------------------- + +def parse_args(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Checks that a stacktrace dump follows canonical -formatting.""") + description=DESCRIPTION) + parser.add_argument( - "-u", "--check-unavailable", action='store_true', - help="Checks if any symbols were unavailable") - args = parser.parse_args() + '-u', '--check-unavailable', + action='store_true', + help='Checks if any symbols were unavailable') - TARGET_RE = re.compile( - r"(?P\d+) +(?P\S+) +(?P
0x[0-9a-fA-F]{16}) " - r"(?P[^+]+) [+] (?P\d+)") + return parser.parse_args() + + +def main(): + args = parse_args() lines = sys.stdin.readlines() found_stack_trace_start = False found_stack_trace_entry = False for line in lines: - line = line.rstrip("\n") + line = line.rstrip('\n') # First see if we found the start of our stack trace start. If so, set # the found stack trace flag and continue. - if line == "Current stack trace:": + if line == 'Current stack trace:': assert(not found_stack_trace_start) found_stack_trace_start = True continue @@ -59,21 +87,19 @@ formatting.""") continue # Ok, we are in the middle of matching a stack trace entry. - m = TARGET_RE.match(line) + matches = TARGET_PATTERN.match(line) + # If we fail to match, we have exited the stack trace entry region - if m is None: + if matches is None: break # At this point, we know that we have some sort of match. found_stack_trace_entry = True - print("Stack Trace Entry:") - print("\tIndex: '%(index)s'\n\tObject File: '%(object)s'\n\tAddress: " - "'%(address)s'\n\tRoutine: '%(routine)s'\n\tOffset: '%(offset)s'" - "\n" % m.groupdict()) + print(RESULTS_FORMAT.format(**matches.groupdict())) # Check for unavailable symbols, if that was requested. if args.check_unavailable: - assert("unavailable" not in m.group("routine")) + assert('unavailable' not in matches.group('routine')) # Once we have processed all of the lines, make sure that we found at least # one stack trace entry. diff --git a/utils/build-overlay b/utils/build-overlay deleted file mode 100755 index 039d61369b88e..0000000000000 --- a/utils/build-overlay +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -function usage { - echo "usage: $0 OVERLAY SDK TARGET" - echo "SDK: OSX,IOS,IOS_SIMULATOR,TVOS,TVOS_SIMULATOR,WATCHOS,WATCHOS_SIMULATOR" - echo "TARGET: macosx,iphoneos,iphonesimulator,appletvos,appletvsimulator,watchos,watchsimulator" - echo "example: ./utils/build-overlay AVFoundation OSX macosx" - exit 1 -} - -OVERLAY=$1; SDK=$2; TARGET=$3 -if [ ! "$OVERLAY" ]; then echo "Overlay param required"; usage; fi -if [ ! "$SDK" ]; then echo "SDK param required"; usage; fi -if [ ! "$TARGET" ]; then echo "TARGET param required"; usage; fi - -function absolute_path { if [[ "$1" == /* ]]; then echo "$1"; else echo "$PWD/$1"; fi } -function dir_name { echo "${1%/*}"; } -function file_name { echo "${1##*/}"; } -script_absolute_path=$(absolute_path "${BASH_SOURCE[@]}") -script_dir_name=$(dir_name "${script_absolute_path}") -script_file_name=$(file_name "${script_absolute_path}") -overlay_source_path="${script_dir_name}/../stdlib/public/Darwin/$OVERLAY" -build_dir="${script_dir_name}/../../build/${OVERLAY}-${SDK}" -swift_source_root="${script_dir_name}/../../../" - -mkdir -p "$build_dir" && cd "$build_dir" || exit - -echo "OVERLAY: ${OVERLAY}" -echo "SDK: ${SDK}" -echo "TARGET: ${TARGET}" -echo "script_absolute_path: ${script_absolute_path}" -echo "script_dir_name: ${script_dir_name}" -echo "script_file_name: ${script_file_name}" -echo "overlay_source_path: ${overlay_source_path}" -echo "swift_source_root: ${swift_source_root}" -echo "build_dir: ${build_dir}" - -toolchain=$(xcode-select -p) -cmake -G Ninja -DSWIFT_SOURCE_ROOT="${swift_source_root}" -DSWIFT_DEST_ROOT="${build_dir}/root" -DSWIFT_HOST_VARIANT_SDK="${SDK}" -DTOOLCHAIN_DIR="${toolchain}/Toolchains/XcodeDefault.xctoolchain" "${overlay_source_path}" -NINJA_TARGET="swift${OVERLAY}-${TARGET}" -ninja "${NINJA_TARGET}" diff --git a/utils/build-parser-lib b/utils/build-parser-lib index 7d5c04835065c..34ac8258c55f8 100755 --- a/utils/build-parser-lib +++ b/utils/build-parser-lib @@ -30,13 +30,11 @@ import os import platform import sys -from build_swift import argparse, defaults -from swift_build_support.swift_build_support import ( - shell, - xcrun, -) +from build_swift.build_swift import argparse, defaults +from build_swift.build_swift.wrappers import xcrun + +from swift_build_support.swift_build_support import shell from swift_build_support.swift_build_support.SwiftBuildSupport import ( - HOME, SWIFT_BUILD_ROOT, SWIFT_SOURCE_ROOT, ) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 0a810e8974a1d..fe1b8298058b1 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -20,7 +20,7 @@ swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;parse [preset: mixin_buildbot_install_components_with_clang] swift-install-components=compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;parser-lib;toolchain-tools;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers -llvm-install-components=llvm-cov;llvm-profdata;IndexStore;clang;clang-resource-headers;compiler-rt;clangd +llvm-install-components=llvm-cov;llvm-profdata;IndexStore;clang;clang-resource-headers;compiler-rt;clangd;dsymutil [preset: mixin_buildbot_trunk_base] # Build standard library and SDK overlay for iOS device and simulator. @@ -298,9 +298,6 @@ reconfigure verbose-build build-ninja -build-swift-stdlib-unittest-extra - - [preset: buildbot_incremental_base_all_platforms] mixin-preset=buildbot_incremental_base @@ -321,6 +318,8 @@ build-subdir=buildbot_incremental release assertions +build-swift-stdlib-unittest-extra + libcxx # Build llbuild & swiftpm here @@ -329,6 +328,12 @@ swiftpm indexstore-db sourcekit-lsp +swiftsyntax +swiftsyntax-verify-generated-files + +skstresstester +swiftevolve + # Build Playground support playgroundsupport @@ -365,7 +370,6 @@ swift-primary-variant-sdk=OSX swift-primary-variant-arch=x86_64 skip-build-llbuild skip-test-llbuild -skip-build-swiftpm skip-test-swiftpm skip-build-playgroundsupport skip-test-playgroundsupport @@ -381,6 +385,8 @@ mixin-preset=buildbot_incremental_base # of the XCTest Xcode project. xctest +build-swift-stdlib-unittest-extra + dash-dash # This preset is meant to test XCTest, not Swift or Foundation. We don't @@ -416,6 +422,8 @@ build-subdir=buildbot_incremental_asan release-debuginfo assertions +build-swift-stdlib-unittest-extra + dash-dash # FIXME: Swift/ASan does not support iOS yet. @@ -442,6 +450,8 @@ release-debuginfo assertions enable-tsan +build-swift-stdlib-unittest-extra + dash-dash skip-build-ios @@ -460,6 +470,8 @@ build-subdir=buildbot_incremental_asan_ubsan release-debuginfo assertions +build-swift-stdlib-unittest-extra + dash-dash # FIXME: Swift/UBSan does not support mac os x yet. @@ -489,6 +501,8 @@ build-subdir=buildbot_incremental_asan_debug # Build Release with debug info, so that we can symbolicate backtraces. assertions +build-swift-stdlib-unittest-extra + dash-dash # FIXME: Swift/ASan does not support iOS yet. @@ -807,13 +821,22 @@ mixin-preset= mixin_buildbot_linux,no_test [preset: buildbot_linux_crosscompile_android,tools=RA,stdlib=RD,build] -mixin-preset=buildbot_linux - -host-test +mixin-preset= + mixin_lightweight_assertions + mixin_linux_install_components_with_clang +build-subdir=buildbot_linux release -assertions -extra-cmake-options=-DSWIFT_ENABLE_LLD_LINKER:BOOL=OFF +test +validation-test +long-test +stress-test +test-optimized +lit-args=-v --time-tests + +build-ninja +libicu +libcxx dash-dash @@ -826,22 +849,30 @@ android-icu-i18n=%(arm_dir)s/libicui18nswift.so android-icu-i18n-include=%(arm_dir)s/icu/source/i18n android-icu-data=%(arm_dir)s/libicudataswift.so -# Disable many build host products. They are unrelated to Android. Foundation, -# libdispatch and XCTest are also disabled because build-script-impl doesn't -# build them for cross-compiled Android. +build-swift-static-stdlib +build-swift-static-sdk-overlay +build-swift-stdlib-unittest-extra + +# Path to the root of the installation filesystem. +install-destdir=%(install_destdir)s + +# Path to the .tar.gz package we would create. +installable-package=%(installable_package)s + +host-test + +extra-cmake-options=-DSWIFT_ENABLE_LLD_LINKER:BOOL=OFF + +install-prefix=/usr +install-swift +install-libicu +install-libcxx + skip-test-linux -skip-build-lldb -skip-build-llbuild -skip-build-swiftpm -skip-build-libdispatch -skip-build-foundation -skip-build-xctest -skip-build-playgroundsupport skip-build-benchmarks -indexstore-db=0 -sourcekit-lsp=0 -toolchain-benchmarks=0 -test-installable-package= +skip-build-playgroundsupport + +reconfigure [preset: buildbot_linux_crosscompile_android,tools=RA,stdlib=RD,build,aarch64] mixin-preset=buildbot_linux_crosscompile_android,tools=RA,stdlib=RD,build @@ -1007,6 +1038,7 @@ swiftpm xctest foundation libdispatch +swiftsyntax indexstore-db sourcekit-lsp dash-dash @@ -1125,6 +1157,10 @@ libcxx indexstore-db sourcekit-lsp +# Don't generate the SwiftSyntax gyb files. Instead verify that up-to-date ones +# are checked in. +swiftsyntax-verify-generated-files + # Build with debug info, this allows us to symbolicate crashes from # production builds. release-debuginfo @@ -1145,7 +1181,6 @@ install-lldb install-llbuild install-swiftpm install-swiftsyntax -skip-install-swiftsyntax-module install-skstresstester install-swiftevolve install-playgroundsupport @@ -1337,6 +1372,47 @@ tvos watchos swift-stdlib-build-type=Release +#===------------------------------------------------------------------------===# +# Test Swift/LLVM Project only on macOS and iOS +#===------------------------------------------------------------------------===# + +[preset: buildbot,tools=RA,stdlib=RA,platforms=macOS_iOS] +mixin-preset= + buildbot_incremental_base_all_platforms + lldb-smoketest,tools=RA + mixin_buildbot_install_components_with_clang + +build-subdir=buildbot_incremental + +# Skip watchOS and tvOS +skip-build-watchos +skip-build-tvos + +# Build Release without debug info, because it is faster to build. +release +assertions + +build-swift-stdlib-unittest-extra + +libcxx + +# Install swift and libcxx +install-swift +install-libcxx + +# Build Playground support +playgroundsupport + +dash-dash + +# Run the SIL verifier after each transform when building swift files +sil-verify-all + +# Don't run host tests for iOS, tvOS and watchOS platforms to make the build +# faster. +skip-test-ios-host + + #===------------------------------------------------------------------------===# # Test all platforms on OS X builder #===------------------------------------------------------------------------===# @@ -1353,6 +1429,8 @@ build-subdir=buildbot_incremental release assertions +build-swift-stdlib-unittest-extra + libcxx # Build llbuild & swiftpm here @@ -1360,6 +1438,10 @@ llbuild swiftpm swiftsyntax +# Don't generate the SwiftSyntax gyb files. Instead verify that up-to-date ones +# are checked in. +swiftsyntax-verify-generated-files + # Build sourcekit-lsp & indexstore-db indexstore-db sourcekit-lsp @@ -1368,6 +1450,10 @@ install-llbuild install-swiftpm install-libcxx +# Build the stress tester and SwiftEvolve +skstresstester +swiftevolve + # Build Playground support playgroundsupport @@ -1396,6 +1482,8 @@ build-subdir=buildbot_incremental release assertions +build-swift-stdlib-unittest-extra + # Build llbuild & swiftpm here llbuild swiftpm @@ -1453,10 +1541,13 @@ skip-test-xctest [preset: mixin_swiftpm_package_macos_platform] mixin-preset=mixin_swiftpm_macos_platform -# Build stdlib for all platforms. -ios -tvos -watchos +# We don't need to build the benchmark if we just want SwiftPM +skip-build-benchmarks + +# We don't need these platforms. Skip them +skip-ios +skip-tvos +skip-watchos skip-test-llbuild skip-test-swiftpm @@ -1483,6 +1574,9 @@ assertions # Downstream projects that import llbuild+SwiftPM. sourcekit-lsp +toolchain-benchmarks +skip-test-toolchain-benchmarks + skip-test-llbuild #===------------------------------------------------------------------------===# @@ -1499,6 +1593,9 @@ assertions # Downstream projects that import llbuild+SwiftPM. sourcekit-lsp +toolchain-benchmarks +skip-test-toolchain-benchmarks + skip-test-llbuild #===------------------------------------------------------------------------===# @@ -1529,6 +1626,39 @@ assertions # Downstream projects that import llbuild+SwiftPM. sourcekit-lsp +#===------------------------------------------------------------------------===# +# Test Swift Syntax +#===------------------------------------------------------------------------===# + +[preset: buildbot_swiftsyntax_macos] +mixin-preset=mixin_swiftpm_package_macos_platform +release +assertions +swiftsyntax +swiftsyntax-verify-generated-files +skstresstester +swiftevolve + +[preset: buildbot_swiftsyntax_linux] +mixin-preset=mixin_swiftpm_package_linux_platform +release +assertions +swiftsyntax +swiftsyntax-verify-generated-files + +#===------------------------------------------------------------------------===# +# Test Swift Stress Tester +#===------------------------------------------------------------------------===# + +[preset: buildbot_skstresstester_macos] +mixin-preset=mixin_swiftpm_package_macos_platform +release +assertions +skstresstester +swiftevolve +install-skstresstester +install-swiftevolve + #===------------------------------------------------------------------------===# # Test SourceKit-LSP #===------------------------------------------------------------------------===# @@ -1586,7 +1716,6 @@ darwin-install-extract-symbols=1 skip-build-cmark skip-build-llvm skip-build-llbuild -skip-build-swiftpm skip-build-benchmarks install-swift install-prefix=%(install_toolchain_dir)s/usr diff --git a/utils/build-script b/utils/build-script index e01a55d23e854..e5811049a68c0 100755 --- a/utils/build-script +++ b/utils/build-script @@ -1,17 +1,21 @@ #!/usr/bin/env python -# utils/build-script - The ultimate tool for building Swift -*- python -*- -# + # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -from __future__ import print_function -import argparse +""" +The ultimate tool for building Swift. +""" + + +from __future__ import absolute_import, print_function, unicode_literals + import json import os import pipes @@ -19,22 +23,19 @@ import platform import sys import time -from build_swift import defaults -from build_swift import driver_arguments -from build_swift import presets -from build_swift.migration import migrate_swift_sdks - -from swift_build_support.swift_build_support import ( - arguments, - debug, - diagnostics, - migration, - products, - shell, - tar, - targets, - workspace -) +from build_swift.build_swift import argparse +from build_swift.build_swift import defaults +from build_swift.build_swift import driver_arguments +from build_swift.build_swift import migration +from build_swift.build_swift import presets + +import six + +from swift_build_support.swift_build_support import diagnostics +from swift_build_support.swift_build_support import products +from swift_build_support.swift_build_support import shell +from swift_build_support.swift_build_support import targets +from swift_build_support.swift_build_support import workspace from swift_build_support.swift_build_support.SwiftBuildSupport import ( HOME, SWIFT_BUILD_ROOT, @@ -48,10 +49,30 @@ from swift_build_support.swift_build_support.targets import \ StdlibDeploymentTarget from swift_build_support.swift_build_support.toolchain import host_toolchain -build_script_impl = os.path.join( + +# ----------------------------------------------------------------------------- +# Constants + +BUILD_SCRIPT_IMPL = os.path.join( SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, "utils", "build-script-impl") +# ----------------------------------------------------------------------------- +# Helpers + +def clean_delay(): + """Provide a short delay so accidentally invoked clean builds can be + canceled. + """ + + sys.stdout.write('Starting clean build in ') + for i in range(3, 0, -1): + sys.stdout.write('\b%d' % i) + sys.stdout.flush() + time.sleep(1) + print('\b\b\b\bnow.') + + def exit_rejecting_arguments(message, parser=None): print(message, file=sys.stderr) if parser: @@ -59,163 +80,264 @@ def exit_rejecting_arguments(message, parser=None): sys.exit(2) # 2 is the same as `argparse` error exit code. +def initialize_runtime_environment(): + """Change the program environment for building. + """ + + # Set an appropriate default umask. + os.umask(0o022) + + # Unset environment variables that might affect how tools behave. + for v in [ + 'MAKEFLAGS', + 'SDKROOT', + 'MACOSX_DEPLOYMENT_TARGET', + 'IPHONEOS_DEPLOYMENT_TARGET', + 'TVOS_DEPLOYMENT_TARGET', + 'WATCHOS_DEPLOYMENT_TARGET']: + os.environ.pop(v, None) + + # Set NINJA_STATUS to format ninja output + os.environ['NINJA_STATUS'] = '[%f/%t][%p][%es] ' + + class JSONDumper(json.JSONEncoder): def __init__(self, *args, **kwargs): - json.JSONEncoder.__init__(self, indent=2, separators=(',', ': '), - *args, **kwargs) + json.JSONEncoder.__init__( + self, indent=2, separators=(',', ': '), sort_keys=True, + *args, **kwargs) def default(self, o): - return vars(o) + if hasattr(o, '__dict__'): + return vars(o) + return six.text_type(o) + + +def print_xcodebuild_versions(file=sys.stdout): + """ + Print the host machine's `xcodebuild` version, as well as version + information for all available SDKs. + """ + version = shell.capture( + ['xcodebuild', '-version'], dry_run=False, echo=False).rstrip() + # Allow non-zero exit codes. Under certain obscure circumstances + # xcodebuild can exit with a non-zero exit code even when the SDK is + # usable. + sdks = shell.capture( + ['xcodebuild', '-version', '-sdk'], dry_run=False, echo=False, + allow_non_zero_exit=True).rstrip() + fmt = '{version}\n\n--- SDK versions ---\n{sdks}\n' + + print(fmt.format(version=version, sdks=sdks), file=file) + file.flush() + + +def tar(source, destination): + """ + Create a gzip archive of the file at 'source' at the given + 'destination' path. + """ + # We do not use `tarfile` here because: + # - We wish to support LZMA2 compression while also supporting Python 2.7. + # - We wish to explicitly set the owner and group of the archive. + args = ['tar', '-c', '-z', '-f', destination] + + if platform.system() != 'Darwin' and platform.system() != 'Windows': + args += ['--owner=0', '--group=0'] + + # Discard stderr output such as 'tar: Failed to open ...'. We'll detect + # these cases using the exit code, which should cause 'shell.call' to + # raise. + shell.call(args + [source], stderr=shell.DEVNULL) + + +# ----------------------------------------------------------------------------- +# Argument Validation + +def validate_arguments(toolchain, args): + if toolchain.cc is None or toolchain.cxx is None: + diagnostics.fatal( + "can't find clang (please install clang-3.5 or a " + "later version)") + if toolchain.cmake is None: + diagnostics.fatal("can't find CMake (please install CMake)") -class BuildScriptInvocation(object): + if args.distcc: + if toolchain.distcc is None: + diagnostics.fatal( + "can't find distcc (please install distcc)") + if toolchain.distcc_pump is None: + diagnostics.fatal( + "can't find distcc-pump (please install distcc-pump)") - """Represent a single build script invocation.""" + if args.host_target is None or args.stdlib_deployment_targets is None: + diagnostics.fatal("unknown operating system") - @staticmethod - def validate_arguments(toolchain, args): - if toolchain.cc is None or toolchain.cxx is None: + if args.symbols_package: + if not os.path.isabs(args.symbols_package): + print( + '--symbols-package must be an absolute path ' + '(was \'{}\')'.format(args.symbols_package)) + return 1 + if not args.install_symroot: diagnostics.fatal( - "can't find clang (please install clang-3.5 or a " - "later version)") - - if toolchain.cmake is None: - diagnostics.fatal("can't find CMake (please install CMake)") + "--install-symroot is required when specifying " + "--symbols-package.") + + if args.android: + if args.android_ndk is None or \ + args.android_api_level is None or \ + args.android_icu_uc is None or \ + args.android_icu_uc_include is None or \ + args.android_icu_i18n is None or \ + args.android_icu_i18n_include is None or \ + args.android_icu_data is None: + diagnostics.fatal( + "when building for Android, --android-ndk, " + "--android-api-level, --android-icu-uc, " + "--android-icu-uc-include, --android-icu-i18n, " + "--android-icu-i18n-include, and --android-icu-data " + "must be specified") + + targets_needing_toolchain = [ + 'build_indexstoredb', + 'build_pythonkit', + 'build_sourcekitlsp', + 'build_toolchainbenchmarks', + 'tsan_libdispatch_test', + ] + has_target_needing_toolchain = \ + bool(sum(getattr(args, x) for x in targets_needing_toolchain)) + if args.legacy_impl and has_target_needing_toolchain: + diagnostics.fatal( + "--legacy-impl is incompatible with building packages needing " + "a toolchain (%s)" % ", ".join(targets_needing_toolchain)) + + +def default_stdlib_deployment_targets(args): + """ + Return targets for the Swift stdlib, based on the build machine. + If the build machine is not one of the recognized ones, return None. + """ + + host_target = StdlibDeploymentTarget.host_target() + if host_target is None: + return None + + # OS X build machines configure all Darwin platforms by default. + # Put iOS native targets last so that we test them last + # (it takes a long time). + if host_target == StdlibDeploymentTarget.OSX.x86_64: + targets = [host_target] + if args.build_ios and args.build_ios_simulator: + targets.extend(StdlibDeploymentTarget.iOSSimulator.targets) + if args.build_ios and args.build_ios_device: + targets.extend(StdlibDeploymentTarget.iOS.targets) + if args.build_tvos and args.build_tvos_simulator: + targets.extend(StdlibDeploymentTarget.AppleTVSimulator.targets) + if args.build_tvos and args.build_tvos_device: + targets.extend(StdlibDeploymentTarget.AppleTV.targets) + if args.build_watchos and args.build_watchos_simulator: + targets.extend(StdlibDeploymentTarget + .AppleWatchSimulator.targets) + if args.build_watchos and args.build_watchos_device: + targets.extend(StdlibDeploymentTarget.AppleWatch.targets) + return targets + else: + # All other machines only configure their host stdlib by default. + return [host_target] + + +def apply_default_arguments(toolchain, args): + # Infer if ninja is required + ninja_required = ( + args.cmake_generator == 'Ninja' or args.build_foundation or + args.build_pythonkit or args.build_sourcekitlsp or + args.build_indexstoredb) + if ninja_required and toolchain.ninja is None: + args.build_ninja = True + + # Set the default stdlib-deployment-targets, if none were provided. + if args.stdlib_deployment_targets is None: + stdlib_targets = default_stdlib_deployment_targets(args) + args.stdlib_deployment_targets = [ + target.name for target in stdlib_targets] + + # SwiftPM and XCTest have a dependency on Foundation. + # On OS X, Foundation is built automatically using xcodebuild. + # On Linux, we must ensure that it is built manually. + if ((args.build_swiftpm or args.build_xctest) and + platform.system() != "Darwin"): + args.build_foundation = True + + # Foundation has a dependency on libdispatch. + # On OS X, libdispatch is provided by the OS. + # On Linux, we must ensure that it is built manually. + if (args.build_foundation and + platform.system() != "Darwin"): + args.build_libdispatch = True + + if args.build_subdir is None: + args.build_subdir = \ + workspace.compute_build_subdir(args) + + if args.install_destdir is None: + args.install_destdir = os.path.join( + SWIFT_BUILD_ROOT, args.build_subdir, + '{}-{}'.format('toolchain', args.host_target)) + + # Add optional stdlib-deployment-targets + if args.android: + if args.android_arch == "armv7": + args.stdlib_deployment_targets.append( + StdlibDeploymentTarget.Android.armv7.name) + elif args.android_arch == "aarch64": + args.stdlib_deployment_targets.append( + StdlibDeploymentTarget.Android.aarch64.name) + + # Infer platform flags from manually-specified configure targets. + # This doesn't apply to Darwin platforms, as they are + # already configured. No building without the platform flag, though. + + android_tgts = [tgt for tgt in args.stdlib_deployment_targets + if StdlibDeploymentTarget.Android.contains(tgt)] + if not args.android and len(android_tgts) > 0: + args.android = True + args.build_android = False + + # Include the Darwin supported architectures in the CMake options. + if args.swift_darwin_supported_archs: + args.extra_cmake_options.append( + '-DSWIFT_DARWIN_SUPPORTED_ARCHS:STRING={}'.format( + args.swift_darwin_supported_archs)) + + # Remove unsupported Darwin archs from the standard library + # deployment targets. + supported_archs = args.swift_darwin_supported_archs.split(';') + targets = StdlibDeploymentTarget.get_targets_by_name( + args.stdlib_deployment_targets) + + args.stdlib_deployment_targets = [ + target.name + for target in targets + if (target.platform.is_darwin and + target.arch in supported_archs) + ] - if args.distcc: - if toolchain.distcc is None: - diagnostics.fatal( - "can't find distcc (please install distcc)") - if toolchain.distcc_pump is None: - diagnostics.fatal( - "can't find distcc-pump (please install distcc-pump)") - - if args.host_target is None or args.stdlib_deployment_targets is None: - diagnostics.fatal("unknown operating system") - - if args.symbols_package: - if not os.path.isabs(args.symbols_package): - print( - '--symbols-package must be an absolute path ' - '(was \'{}\')'.format(args.symbols_package)) - return 1 - if not args.install_symroot: - diagnostics.fatal( - "--install-symroot is required when specifying " - "--symbols-package.") + # Include the Darwin module-only architectures in the CMake options. + if args.swift_darwin_module_archs: + args.extra_cmake_options.append( + '-DSWIFT_DARWIN_MODULE_ARCHS:STRING={}'.format( + args.swift_darwin_module_archs)) - if args.android: - if args.android_ndk is None or \ - args.android_api_level is None or \ - args.android_icu_uc is None or \ - args.android_icu_uc_include is None or \ - args.android_icu_i18n is None or \ - args.android_icu_i18n_include is None or \ - args.android_icu_data is None: - diagnostics.fatal( - "when building for Android, --android-ndk, " - "--android-api-level, --android-icu-uc, " - "--android-icu-uc-include, --android-icu-i18n, " - "--android-icu-i18n-include, and --android-icu-data " - "must be specified") - - targets_needing_toolchain = [ - 'build_indexstoredb', - 'build_sourcekitlsp', - 'build_toolchainbenchmarks', - 'tsan_libdispatch_test', - ] - has_target_needing_toolchain = \ - bool(sum(getattr(args, x) for x in targets_needing_toolchain)) - if args.legacy_impl and has_target_needing_toolchain: - diagnostics.fatal( - "--legacy-impl is incompatible with building packages needing " - "a toolchain (%s)" % ", ".join(targets_needing_toolchain)) - - @staticmethod - def apply_default_arguments(toolchain, args): - # Infer if ninja is required - ninja_required = ( - args.cmake_generator == 'Ninja' or args.build_foundation or - args.build_sourcekitlsp or args.build_indexstoredb) - if ninja_required and toolchain.ninja is None: - args.build_ninja = True - - # Set the default stdlib-deployment-targets, if none were provided. - if args.stdlib_deployment_targets is None: - stdlib_targets = \ - StdlibDeploymentTarget.default_stdlib_deployment_targets() - args.stdlib_deployment_targets = [ - target.name for target in stdlib_targets] - - # SwiftPM and XCTest have a dependency on Foundation. - # On OS X, Foundation is built automatically using xcodebuild. - # On Linux, we must ensure that it is built manually. - if ((args.build_swiftpm or args.build_xctest) and - platform.system() != "Darwin"): - args.build_foundation = True - - # Foundation has a dependency on libdispatch. - # On OS X, libdispatch is provided by the OS. - # On Linux, we must ensure that it is built manually. - if (args.build_foundation and - platform.system() != "Darwin"): - args.build_libdispatch = True - - if args.build_subdir is None: - args.build_subdir = \ - workspace.compute_build_subdir(args) - - if args.install_destdir is None: - args.install_destdir = os.path.join( - SWIFT_BUILD_ROOT, args.build_subdir, - '{}-{}'.format('toolchain', args.host_target)) - - # Add optional stdlib-deployment-targets - if args.android: - if args.android_arch == "armv7": - args.stdlib_deployment_targets.append( - StdlibDeploymentTarget.Android.armv7.name) - elif args.android_arch == "aarch64": - args.stdlib_deployment_targets.append( - StdlibDeploymentTarget.Android.aarch64.name) - - # Infer platform flags from manually-specified configure targets. - # This doesn't apply to Darwin platforms, as they are - # already configured. No building without the platform flag, though. - - android_tgts = [tgt for tgt in args.stdlib_deployment_targets - if StdlibDeploymentTarget.Android.contains(tgt)] - if not args.android and len(android_tgts) > 0: - args.android = True - args.build_android = False - - # Include the Darwin supported architectures in the CMake options. - if args.swift_darwin_supported_archs: - args.extra_cmake_options.append( - '-DSWIFT_DARWIN_SUPPORTED_ARCHS:STRING={}'.format( - args.swift_darwin_supported_archs)) - - # Remove unsupported Darwin archs from the standard library - # deployment targets. - supported_archs = args.swift_darwin_supported_archs.split(';') - targets = StdlibDeploymentTarget.get_targets_by_name( - args.stdlib_deployment_targets) - - args.stdlib_deployment_targets = [ - target.name - for target in targets - if (target.platform.is_darwin and - target.arch in supported_archs) - ] - # Include the Darwin module-only architectures in the CMake options. - if args.swift_darwin_module_archs: - args.extra_cmake_options.append( - '-DSWIFT_DARWIN_MODULE_ARCHS:STRING={}'.format( - args.swift_darwin_module_archs)) +# ----------------------------------------------------------------------------- +# Build Script Impl Wrapping -# --- +class BuildScriptInvocation(object): + """Represent a single build script invocation. + """ def __init__(self, toolchain, args): self.toolchain = toolchain @@ -227,25 +349,6 @@ class BuildScriptInvocation(object): self.build_libparser_only = args.build_libparser_only - def initialize_runtime_environment(self): - """Change the program environment for building.""" - - # Set an appropriate default umask. - os.umask(0o022) - - # Unset environment variables that might affect how tools behave. - for v in [ - 'MAKEFLAGS', - 'SDKROOT', - 'MACOSX_DEPLOYMENT_TARGET', - 'IPHONEOS_DEPLOYMENT_TARGET', - 'TVOS_DEPLOYMENT_TARGET', - 'WATCHOS_DEPLOYMENT_TARGET']: - os.environ.pop(v, None) - - # Set NINJA_STATUS to format ninja output - os.environ['NINJA_STATUS'] = '[%f/%t][%p][%es] ' - def build_ninja(self): if not os.path.exists(self.workspace.source_dir("ninja")): diagnostics.fatal( @@ -303,10 +406,6 @@ class BuildScriptInvocation(object): "--libdispatch-build-type", args.libdispatch_build_variant, "--libicu-build-type", args.libicu_build_variant, "--xctest-build-type", args.build_variant, - "--swiftpm-build-type", args.build_variant, - "--swiftsyntax-build-type", args.build_variant, - "--skstresstester-build-type", args.build_variant, - "--swiftevolve-build-type", args.build_variant, "--llbuild-build-type", args.build_variant, "--swift-enable-assertions", str(args.swift_assertions).lower(), "--swift-stdlib-enable-assertions", str( @@ -411,9 +510,7 @@ class BuildScriptInvocation(object): ] if args.skip_build: - impl_args += ["--skip-build-cmark", - "--skip-build-llvm", - "--skip-build-swift"] + impl_args += ["--skip-build"] if not args.build_benchmarks: impl_args += ["--skip-build-benchmarks"] # Currently we do not build external benchmarks by default. @@ -433,14 +530,6 @@ class BuildScriptInvocation(object): impl_args += ["--skip-build-libdispatch"] if not args.build_libicu: impl_args += ["--skip-build-libicu"] - if not args.build_swiftpm: - impl_args += ["--skip-build-swiftpm"] - if not args.build_swiftsyntax: - impl_args += ["--skip-build-swiftsyntax"] - if not args.build_skstresstester: - impl_args += ["--skip-build-skstresstester"] - if not args.build_swiftevolve: - impl_args += ["--skip-build-swiftevolve"] if not args.build_playgroundsupport: impl_args += ["--skip-build-playgroundsupport"] if args.build_swift_dynamic_stdlib: @@ -476,6 +565,8 @@ class BuildScriptInvocation(object): impl_args += ["--skip-build-watchos-simulator"] if not args.build_android: impl_args += ["--skip-build-android"] + if not args.build_clang_tools_extra: + impl_args += ["--skip-build-clang-tools-extra"] if not args.test and not args.long_test and not args.stress_test: impl_args += ["--skip-test-swift"] @@ -483,10 +574,6 @@ class BuildScriptInvocation(object): impl_args += ["--skip-test-cmark", "--skip-test-lldb", "--skip-test-llbuild", - "--skip-test-swiftpm", - "--skip-test-swiftsyntax", - "--skip-test-skstresstester", - "--skip-test-swiftevolve", "--skip-test-xctest", "--skip-test-foundation", "--skip-test-libdispatch", @@ -526,6 +613,8 @@ class BuildScriptInvocation(object): impl_args += ["--long-test"] if args.stress_test: impl_args += ["--stress-test"] + if args.skip_local_build: + impl_args += ["--skip-local-build"] if args.only_executable_test: impl_args += ["--only-executable-test"] if not args.benchmark: @@ -576,6 +665,13 @@ class BuildScriptInvocation(object): "--extra-swift-args=%s" % ';'.join(args.extra_swift_args) ] + # Enable macCatalyst + if args.maccatalyst: + (args.extra_cmake_options + .append('-DSWIFT_ENABLE_MACCATALYST:BOOL=TRUE')) + if args.maccatalyst_ios_tests: + impl_args += ["--darwin-test-maccatalyst-ios-like=1"] + # If we have extra_cmake_options, combine all of them together and then # add them as one command. if args.extra_cmake_options: @@ -705,6 +801,8 @@ class BuildScriptInvocation(object): product_classes.append(products.SwiftEvolve) if self.args.build_indexstoredb: product_classes.append(products.IndexStoreDB) + if self.args.build_pythonkit: + product_classes.append(products.PythonKit) if self.args.build_sourcekitlsp: product_classes.append(products.SourceKitLSP) if self.args.build_toolchainbenchmarks: @@ -723,7 +821,7 @@ class BuildScriptInvocation(object): # `build-script-impl`. if self.args.legacy_impl: # Execute the underlying build script implementation. - shell.call_without_sleeping([build_script_impl] + self.impl_args, + shell.call_without_sleeping([BUILD_SCRIPT_IMPL] + self.impl_args, env=self.impl_env, echo=True) return @@ -784,15 +882,27 @@ class BuildScriptInvocation(object): continue product_source = product_class.product_source_name() product_name = product_class.product_name() + if product_class.is_swiftpm_unified_build_product(): + build_dir = self.workspace.swiftpm_unified_build_dir( + host_target) + else: + build_dir = self.workspace.build_dir( + host_target, product_name) product = product_class( args=self.args, toolchain=self.toolchain, source_dir=self.workspace.source_dir(product_source), - build_dir=self.workspace.build_dir( - host_target, product_name)) - product.build(host_target) - product.test(host_target) - product.install(host_target) + build_dir=build_dir) + if product.should_build(host_target): + print("--- Building %s ---" % product_name) + product.build(host_target) + if product.should_test(host_target): + print("--- Running tests for %s ---" % product_name) + product.test(host_target) + print("--- Finished tests for %s ---" % product_name) + if product.should_install(host_target): + print("--- Installing %s ---" % product_name) + product.install(host_target) # Extract symbols... for host_target in all_hosts: @@ -833,23 +943,15 @@ class BuildScriptInvocation(object): def _execute_action(self, action_name): shell.call_without_sleeping( - [build_script_impl] + self.impl_args + + [BUILD_SCRIPT_IMPL] + self.impl_args + ["--only-execute", action_name], env=self.impl_env, echo=self.args.verbose_build) -# Provide a short delay so accidentally invoked clean builds can be canceled. -def clean_delay(): - sys.stdout.write('Starting clean build in ') - for i in range(3, 0, -1): - sys.stdout.write('\b%d' % i) - sys.stdout.flush() - time.sleep(1) - print('\b\b\b\bnow.') - +# ----------------------------------------------------------------------------- +# Main (preset) -# Main entry point for the preset mode. -def main_preset(): +def parse_preset_args(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="""Builds Swift using a preset.""") @@ -873,11 +975,14 @@ def main_preset(): parser.add_argument( "--show-presets", help="list all presets and exit", - action=arguments.action.optional_bool) + action=argparse.actions.StoreTrueAction, + nargs=argparse.Nargs.OPTIONAL) parser.add_argument( "--distcc", help="use distcc", - action=arguments.action.optional_bool) + action=argparse.actions.StoreTrueAction, + nargs=argparse.Nargs.OPTIONAL, + default=os.environ.get('USE_DISTCC') == '1') parser.add_argument( "--cmake-c-launcher", help="the absolute path to set CMAKE_C_COMPILER_LAUNCHER", @@ -900,7 +1005,8 @@ def main_preset(): "--expand-invocation", "--expand-build-script-invocation", help="Print the expanded build-script invocation generated " "by the preset, but do not run the preset", - action=arguments.action.optional_bool) + action=argparse.actions.StoreTrueAction, + nargs=argparse.Nargs.OPTIONAL) parser.add_argument( "--swiftsyntax-install-prefix", help="specify the directory to where SwiftSyntax should be installed") @@ -918,7 +1024,13 @@ def main_preset(): help="Reconfigure all projects as we build", action="store_true", default=False) - args = parser.parse_args() + + return parser.parse_args() + + +def main_preset(): + args = parse_preset_args() + if len(args.preset_file_names) == 0: args.preset_file_names = [ os.path.join( @@ -933,12 +1045,12 @@ def main_preset(): preset_parser = presets.PresetParser() try: - preset_parser.read(args.preset_file_names) - except presets.Error as e: + preset_parser.read_files(args.preset_file_names) + except presets.PresetError as e: diagnostics.fatal(e.message) if args.show_presets: - for name in sorted(preset_parser.preset_names(), + for name in sorted(preset_parser.preset_names, key=lambda name: name.lower()): print(name) return 0 @@ -952,12 +1064,13 @@ def main_preset(): args.preset_substitutions[name] = value try: - preset = preset_parser.get_preset(args.preset, - vars=args.preset_substitutions) - except presets.Error as e: + preset = preset_parser.get_preset( + args.preset, + vars=args.preset_substitutions) + except presets.PresetError as e: diagnostics.fatal(e.message) - preset_args = migrate_swift_sdks(preset.format_args()) + preset_args = migration.migrate_swift_sdks(preset.args) if args.distcc and (args.cmake_c_launcher or args.cmake_cxx_launcher): diagnostics.fatal( @@ -999,7 +1112,9 @@ def main_preset(): return 0 -# Main entry point for the normal mode. +# ----------------------------------------------------------------------------- +# Main (normal) + def main_normal(): parser = driver_arguments.create_argument_parser() @@ -1009,7 +1124,7 @@ def main_normal(): # If we received any impl args, check if `build-script-impl` would # accept them or not before any further processing. try: - migration.check_impl_args(build_script_impl, + migration.check_impl_args(BUILD_SCRIPT_IMPL, args.build_script_impl_args) except ValueError as e: exit_rejecting_arguments(e, parser) @@ -1050,20 +1165,20 @@ def main_normal(): args.cmake = cmake_path # Preprocess the arguments to apply defaults. - BuildScriptInvocation.apply_default_arguments(toolchain, args) + apply_default_arguments(toolchain, args) # Validate the arguments. - BuildScriptInvocation.validate_arguments(toolchain, args) + validate_arguments(toolchain, args) # Create the build script invocation. invocation = BuildScriptInvocation(toolchain, args) # Sanitize the runtime environment. - invocation.initialize_runtime_environment() + initialize_runtime_environment() # Show SDKs, if requested. if args.show_sdks: - debug.print_xcodebuild_versions() + print_xcodebuild_versions() if args.dump_config: print(JSONDumper().encode(invocation)) @@ -1098,12 +1213,14 @@ def main_normal(): # run `tar` without the leading '/' (we remove it ourselves to keep # `tar` from emitting a warning). with shell.pushd(args.install_symroot): - tar.tar(source=prefix.lstrip('/'), - destination=args.symbols_package) + tar(source=prefix.lstrip('/'), + destination=args.symbols_package) return 0 +# ----------------------------------------------------------------------------- + def main(): if not SWIFT_SOURCE_ROOT: diagnostics.fatal( diff --git a/utils/build-script-impl b/utils/build-script-impl index 093ca6999e989..255ac4beb3844 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -30,255 +30,244 @@ umask 0022 # A default value of "" indicates that the corresponding variable # will remain unset unless set explicitly. # -# skip-* parameters do not affect the configuration (CMake parameters). -# You can turn them on and off in different invocations of the script for the -# same build directory. +# The --skip-build parameter, with no product name, does not affect the +# configuration (CMake parameters). You can turn this option on and +# off in different invocations of the script for the same build +# directory without affecting configutation. # -# build-* parameters affect the CMake configuration (enable/disable those -# components). +# skip-build-* and build-* parameters affect the CMake configuration +# (enable/disable those components). # # Each variable name is re-exported into this script in uppercase, where dashes # are substituted by underscores. For example, `swift-install-components` is # referred to as `SWIFT_INSTALL_COMPONENTS` in the remainder of this script. + + # name default description KNOWN_SETTINGS=( - # name default description - dry-run "" "print the commands that would be executed, but do not execute them" - build-args "" "arguments to the build tool; defaults to -j8 when CMake generator is \"Unix Makefiles\"" - build-dir "" "out-of-tree build directory; default is in-tree. **This argument is required**" - host-cc "" "the path to CC, the 'clang' compiler for the host platform. **This argument is required**" - host-cxx "" "the path to CXX, the 'clang++' compiler for the host platform. **This argument is required**" - host-lipo "" "the path to lipo for creating universal binaries on Darwin" - host-libtool "" "the path to libtool" - darwin-xcrun-toolchain "default" "the name of the toolchain to use on Darwin" - ninja-bin "" "the path to Ninja tool" - cmark-build-type "Debug" "the CMake build variant for CommonMark (Debug, RelWithDebInfo, Release, MinSizeRel). Defaults to Debug." - lldb-extra-cmake-args "" "extra command line args to pass to lldb cmake" - lldb-test-cc "" "CC to use for building LLDB testsuite test inferiors. Defaults to just-built, in-tree clang. If set to 'host-toolchain', sets it to same as host-cc." - lldb-test-swift-only "0" "when running lldb tests, only include Swift-specific tests" - lldb-no-debugserver "" "delete debugserver after building it, and don't try to codesign it" - lldb-use-system-debugserver "" "don't try to codesign debugserver, and use the system's debugserver instead" - lldb-assertions "1" "build lldb with assertions enabled" - lldb-test-swift-compatibility "" "specify additional Swift compilers to test lldb with" - llvm-build-type "Debug" "the CMake build variant for LLVM and Clang (Debug, RelWithDebInfo, Release, MinSizeRel). Defaults to Debug." - swift-build-type "Debug" "the CMake build variant for Swift" - swift-enable-assertions "1" "enable assertions in Swift" - swift-analyze-code-coverage "not-merged" "Code coverage analysis mode for Swift (false, not-merged, merged). Defaults to false if the argument is not present, and not-merged if the argument is present without a modifier." - swift-tools-enable-lto "" "enable LTO compilation of Swift tools. *NOTE* This does not include the swift standard library and runtime. Must be set to one of 'thin' or 'full'" - llvm-enable-lto "" "Must be set to one of 'thin' or 'full'" - llvm-enable-modules "0" "enable building llvm using modules" - swift-tools-num-parallel-lto-link-jobs "" "The number of parallel link jobs to use when compiling swift tools" - llvm-num-parallel-lto-link-jobs "" "The number of parallel link jobs to use when compiling llvm" - swift-stdlib-build-type "Debug" "the CMake build variant for Swift" - swift-stdlib-enable-assertions "1" "enable assertions in Swift" - swift-stdlib-use-nonatomic-rc "0" "build the Swift stdlib and overlays with nonatomic reference count operations enabled" - lldb-build-type "Debug" "the CMake build variant for LLDB" - llbuild-build-type "Debug" "the CMake build variant for llbuild" - foundation-build-type "Debug" "the build variant for Foundation" - libdispatch-build-type "Debug" "the build variant for libdispatch" - libicu-build-type "Debug" "the build variant for libicu" - playgroundsupport-build-type "Debug" "the build variant for PlaygroundSupport" - xctest-build-type "Debug" "the build variant for xctest" - swiftpm-build-type "Debug" "the build variant for swiftpm" - swiftsyntax-build-type "Debug" "the build variant for swiftSyntax" - # When this flag is set, the build-script will only build/install the swift-syntax parser - # This is a temporary workaround of having a separate build product for swift-syntax parser - skip-swiftsyntax-swiftside "" "skip building/installing the swift side of swiftsyntax" - skstresstester-build-type "Debug" "the build variant for the SourceKit stress tester" - swiftevolve-build-type "Debug" "the build variant for the swift-evolve tool" - llbuild-enable-assertions "1" "enable assertions in llbuild" - enable-asan "" "enable Address Sanitizer" - enable-ubsan "" "enable Undefined Behavior Sanitizer" - cmake "" "path to the cmake binary" - distcc "" "use distcc in pump mode" - distcc-pump "" "the path to distcc pump executable. This argument is required if distcc is set." - build-runtime-with-host-compiler "" "use the host c++ compiler to build everything" - cmake-generator "Unix Makefiles" "kind of build system to generate; see output of 'cmake --help' for choices" - verbose-build "" "print the commands executed during the build" - install-prefix "" "installation prefix" - toolchain-prefix "" "the path to the .xctoolchain directory that houses the install prefix path" - install-destdir "" "the path to use as the filesystem root for the installation" - install-symroot "" "the path to install debug symbols into" - swift-install-components "" "a semicolon-separated list of Swift components to install" - llvm-install-components "" "a semicolon-separated list of LLVM components to install" - installable-package "" "the path to the archive of the installation directory" - test-installable-package "" "whether to run post-packaging tests on the produced package" - reconfigure "" "force a CMake configuration run even if CMakeCache.txt already exists" - build-libparser-only "" "only build libSwiftSyntaxParser" - libparser-ver "" "current version of libSwiftSyntaxParser" - skip-reconfigure "" "set to skip reconfigure" - swift-primary-variant-sdk "" "default SDK for target binaries" - swift-primary-variant-arch "" "default arch for target binaries" - skip-build-cmark "" "set to skip building CommonMark" - skip-build-llvm "" "set to skip building LLVM/Clang" - skip-build-compiler-rt "" "set to skip building Compiler-RT" - skip-build-swift "" "set to skip building Swift" - skip-build-linux "" "set to skip building Swift stdlibs for Linux" - skip-build-freebsd "" "set to skip building Swift stdlibs for FreeBSD" - skip-build-cygwin "" "set to skip building Swift stdlibs for Cygwin" - skip-build-haiku "" "set to skip building Swift stdlibs for Haiku" - skip-build-osx "" "set to skip building Swift stdlibs for OS X" - skip-build-ios-device "" "set to skip building Swift stdlibs for iOS devices (i.e. build simulators only)" - skip-build-ios-simulator "" "set to skip building Swift stdlibs for iOS simulators (i.e. build devices only)" - skip-build-tvos-device "" "set to skip building Swift stdlibs for tvOS devices (i.e. build simulators only)" - skip-build-tvos-simulator "" "set to skip building Swift stdlibs for tvOS simulators (i.e. build devices only)" - skip-build-watchos-device "" "set to skip building Swift stdlibs for Apple watchOS devices (i.e. build simulators only)" - skip-build-watchos-simulator "" "set to skip building Swift stdlibs for Apple watchOS simulators (i.e. build devices only)" - skip-build-android "" "set to skip building Swift stdlibs for Android" - skip-build-lldb "" "set to skip building LLDB" - skip-build-llbuild "" "set to skip building llbuild" - skip-build-libcxx "" "set to skip building libcxx" - skip-build-swiftpm "" "set to skip building swiftpm" - skip-build-swiftsyntax "" "set to skip building swiftSyntax" - skip-build-skstresstester "" "set to skip building the SourceKit stress tester" - skip-build-swiftevolve "" "set to skip building the swift-evolve tool" - skip-build-xctest "" "set to skip building xctest" - skip-build-foundation "" "set to skip building foundation" - skip-build-libdispatch "" "set to skip building libdispatch" - skip-build-libicu "" "set to skip building libicu" - skip-build-benchmarks "" "set to skip building Swift Benchmark Suite" - skip-build-external-benchmarks "1" "set to skip building the external Swift Benchmark Suite. (skipped by default)" - skip-build-playgroundsupport "" "set to skip building PlaygroundSupport" - skip-local-build "" "set to skip building for the current host (useful when crosscompiling)" - skip-test-cmark "" "set to skip testing CommonMark" - skip-test-lldb "" "set to skip testing lldb" - skip-test-swift "" "set to skip testing Swift" - skip-test-llbuild "" "set to skip testing llbuild" - skip-test-swiftpm "" "set to skip testing swiftpm" - skip-test-swiftsyntax "" "set to skip testing swiftSyntax" - skip-test-skstresstester "" "set to skip testing the SourceKit stress tester" - skip-test-swiftevolve "" "set to skip testing the swift-evolve tool" - skip-test-xctest "" "set to skip testing xctest" - skip-test-foundation "" "set to skip testing foundation" - skip-test-libdispatch "" "set to skip testing libdispatch" - skip-test-libicu "" "set to skip testing libicu" - skip-test-playgroundsupport "" "set to skip testing PlaygroundSupport" - skip-test-linux "" "set to skip testing Swift stdlibs for Linux" - skip-test-freebsd "" "set to skip testing Swift stdlibs for FreeBSD" - skip-test-cygwin "" "set to skip testing Swift stdlibs for Cygwin" - skip-test-haiku "" "set to skip testing Swift stdlibs for Haiku" - skip-test-osx "" "set to skip testing Swift stdlibs for OS X" - skip-test-ios-32bit-simulator "" "set to skip testing Swift stdlibs for iOS 32bit simulators" - skip-test-ios-simulator "" "set to skip testing Swift stdlibs for iOS simulators (i.e. test devices only)" - skip-test-ios-host "" "set to skip testing the host parts of the iOS toolchain" - skip-test-tvos-simulator "" "set to skip testing Swift stdlibs for tvOS simulators (i.e. test devices only)" - skip-test-tvos-host "" "set to skip testing the host parts of the tvOS toolchain" - skip-test-watchos-simulator "" "set to skip testing Swift stdlibs for Apple watchOS simulators (i.e. test devices only)" - skip-test-watchos-host "" "set to skip testing the host parts of the watchOS toolchain" - skip-test-android "" "set to skip testing Swift stdlibs for Android" - skip-test-android-host "" "set to skip testing the host parts of the Android toolchain" - validation-test "0" "set to run the validation test suite" - long-test "0" "set to run the long test suite" - stress-test "0" "set to run the stress test suite" - test-paths "" "run tests located in specific directories and/or files" - skip-test-benchmarks "" "set to skip running Swift Benchmark Suite" - skip-test-optimized "" "set to skip testing the test suite in optimized mode" - skip-test-optimize-for-size "" "set to skip testing the test suite in optimize for size mode" - skip-test-optimize-none-with-implicit-dynamic "" "set to skip testing the test suite in optimize none with implicit dynamic mode" - skip-test-sourcekit "" "set to skip testing SourceKit" - stress-test-sourcekit "" "set to run the stress-SourceKit target" - only-executable-test "" "only run the executable variant of the swift lit tests" - workspace "${HOME}/src" "source directory containing llvm, clang, swift" - enable-llvm-assertions "1" "set to enable llvm assertions" - build-llvm "1" "set to 1 to build LLVM and Clang" - build-swift-tools "1" "set to 1 to build Swift host tools" - build-swift-dynamic-stdlib "" "set to 1 to build dynamic variants of the Swift standard library" - build-swift-static-stdlib "" "set to 1 to build static variants of the Swift standard library" - build-swift-stdlib-unittest-extra "0" "set to 1 to build optional StdlibUnittest components" - build-swift-dynamic-sdk-overlay "" "set to 1 to build dynamic variants of the Swift SDK overlay" - build-swift-static-sdk-overlay "" "set to 1 to build static variants of the Swift SDK overlay" - build-swift-examples "1" "set to 1 to build examples" - build-swift-remote-mirror "1" "set to 1 to build the Swift Remote Mirror library" - build-sil-debugging-stdlib "0" "set to 1 to build the Swift standard library with -gsil to enable debugging and profiling on SIL level" - check-incremental-compilation "0" "set to 1 to compile swift libraries multiple times to check if incremental compilation works" - report-statistics "0" "set to 1 to generate compilation statistics files for swift libraries" - llvm-include-tests "1" "Set to true to generate testing targets for LLVM. Set to true by default." - swift-include-tests "1" "Set to true to generate testing targets for Swift. This allows the build to proceed when 'test' directory is missing (required for B&I builds)" - native-llvm-tools-path "" "directory that contains LLVM tools that are executable on the build machine" - native-clang-tools-path "" "directory that contains Clang tools that are executable on the build machine" - native-swift-tools-path "" "directory that contains Swift tools that are executable on the build machine" - embed-bitcode-section "0" "embed an LLVM bitcode section in stdlib/overlay binaries for supported platforms" - darwin-crash-reporter-client "" "whether to enable CrashReporter integration" - darwin-stdlib-install-name-dir "" "the directory of the install_name for standard library dylibs" - install-cmark "" "whether to install cmark" - install-swift "" "whether to install Swift" - install-lldb "" "whether to install LLDB" - install-llbuild "" "whether to install llbuild" - install-swiftpm "" "whether to install swiftpm" - install-swiftsyntax "" "whether to install swiftsyntax" - skip-install-swiftsyntax-module "" "set to skip installing swiftsyntax modules" - install-skstresstester "" "whether to install the SourceKit stress tester" - install-swiftevolve "" "whether to install the swift-evolve tool" - install-xctest "" "whether to install xctest" - install-foundation "" "whether to install foundation" - install-libcxx "" "whether to install libc++" - install-libdispatch "" "whether to install libdispatch" - install-libicu "" "whether to install libicu" - install-playgroundsupport "" "whether to install PlaygroundSupport" - darwin-install-extract-symbols "" "whether to extract symbols with dsymutil during installations" - host-target "" "The host target. LLVM, Clang, and Swift will be built for this target. The built LLVM and Clang will be used to compile Swift for the cross-compilation targets. **This argument is required**" - stdlib-deployment-targets "" "space-separated list of targets to configure the Swift standard library to be compiled or cross-compiled for" - build-stdlib-deployment-targets "all" "space-separated list that filters which of the configured targets to build the Swift standard library for, or 'all'" - cross-compile-hosts "" "space-separated list of targets to cross-compile host Swift tools for" - cross-compile-with-host-tools "" "set to use the clang we build for the host to then build the cross-compile hosts" - cross-compile-install-prefixes "" "semicolon-separated list of install prefixes to use for the cross-compiled hosts. The list expands, so if there are more cross-compile hosts than prefixes, unmatched hosts use the last prefix in the list" - skip-merge-lipo-cross-compile-tools "" "set to skip running merge-lipo after installing cross-compiled host Swift tools" - darwin-deployment-version-osx "10.9" "minimum deployment target version for OS X" - darwin-deployment-version-ios "7.0" "minimum deployment target version for iOS" - darwin-deployment-version-tvos "9.0" "minimum deployment target version for tvOS" - darwin-deployment-version-watchos "2.0" "minimum deployment target version for watchOS" - extra-cmake-options "" "Extra options to pass to CMake for all targets" - extra-swift-args "" "Extra arguments to pass to swift modules which match regex. Assumed to be a flattened cmake list consisting of [module_regexp, args, module_regexp, args, ...]" - sil-verify-all "0" "If enabled, run the SIL verifier after each transform when building Swift files during this build process" - swift-enable-ast-verifier "1" "If enabled, and the assertions are enabled, the built Swift compiler will run the AST verifier every time it is invoked" - swift-runtime-enable-leak-checker "0" "Enable leaks checking routines in the runtime" - use-gold-linker "" "Enable using the gold linker" - darwin-toolchain-bundle-identifier "" "CFBundleIdentifier for xctoolchain info plist" - darwin-toolchain-display-name "" "Display Name for xctoolcain info plist" - darwin-toolchain-display-name-short "" "Display Name with out date for xctoolchain info plist" - darwin-toolchain-name "" "Directory name for xctoolchain" - darwin-toolchain-version "" "Version for xctoolchain info plist and installer pkg" - darwin-toolchain-application-cert "" "Application Cert name to codesign xctoolchain" - darwin-toolchain-installer-cert "" "Installer Cert name to create installer pkg" - darwin-toolchain-installer-package "" "The path to installer pkg" - darwin-sdk-deployment-targets "xctest-ios-8.0" "semicolon-separated list of triples like 'fookit-ios-9.0;barkit-watchos-9.0'" - darwin-overlay-target "" "single overlay target to build, dependencies are computed later" - build-jobs "" "The number of parallel build jobs to use" - darwin-toolchain-alias "" "Swift alias for toolchain" - android-ndk "" "An absolute path to the NDK that will be used as a libc implementation for Android builds" - android-api-level "" "The Android API level to target when building for Android. Currently only 21 or above is supported" - android-ndk-gcc-version "" "The GCC version to use when building for Android. Currently only 4.9 is supported" - android-icu-uc "" "Path to libicuuc.so" - android-icu-uc-include "" "Path to a directory containing headers for libicuuc" - android-icu-i18n "" "Path to libicui18n.so" - android-icu-i18n-include "" "Path to a directory containing headers libicui18n" - android-icu-data "" "Path to libicudata.so" - android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" - android-arch "armv7" "The Android target architecture when building for Android" - check-args-only "" "set to check all arguments are known. Exit with status 0 if success, non zero otherwise" - common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" - cmark-cmake-options "" "CMake options used for all cmark targets" - ninja-cmake-options "" "CMake options used for all ninja targets" - foundation-cmake-options "" "CMake options used for all foundation targets" - libdispatch-cmake-options "" "CMake options used for all libdispatch targets" - libicu-cmake-options "" "CMake options used for all libicu targets" - llbuild-cmake-options "" "CMake options used for all llbuild targets" - lldb-cmake-options "" "CMake options used for all lldb targets" - llvm-cmake-options "" "CMake options used for all llvm targets" - ninja-cmake-options "" "CMake options used for all ninja targets" - swift-cmake-options "" "CMake options used for all swift targets" - swiftpm-cmake-options "" "CMake options used for all swiftpm targets" - xctest-cmake-options "" "CMake options used for all xctest targets" - playgroundsupport-cmake-options "" "CMake options used for all playgroundsupport targets" - # TODO: Remove this some time later. - user-config-args "" "**Renamed to --extra-cmake-options**: User-supplied arguments to cmake when used to do configuration." - only-execute "all" "Only execute the named action (see implementation)" - llvm-lit-args "" "If set, override the lit args passed to LLVM" - clang-profile-instr-use "" "If set, profile file to use for clang PGO while building llvm/clang" - swift-profile-instr-use "" "If set, profile file to use for clang PGO while building swift" - coverage-db "" "If set, coverage database to use when prioritizing testing" - build-toolchain-only "" "If set, only build the necessary tools to build an external toolchain" - skip-local-host-install "" "If we are cross-compiling multiple targets, skip an install pass locally if the hosts match" + ## Debugging Options + dry-run "" "print the commands that would be executed, but do not execute them" + verbose-build "" "print the commands executed during the build" + check-args-only "" "set to check all arguments are known. Exit with status 0 if success, non zero otherwise" + only-execute "all" "Only execute the named action (see implementation)" + check-incremental-compilation "0" "set to 1 to compile swift libraries multiple times to check if incremental compilation works" + + ## Build Mode Options + enable-asan "" "enable Address Sanitizer" + enable-ubsan "" "enable Undefined Behavior Sanitizer" + clang-profile-instr-use "" "If set, profile file to use for clang PGO while building llvm/clang" + swift-profile-instr-use "" "If set, profile file to use for clang PGO while building swift" + + ## Build Tool Settings + build-args "" "arguments to the build tool; defaults to -j8 when CMake generator is \"Unix Makefiles\"" + build-dir "" "out-of-tree build directory; default is in-tree. **This argument is required**" + build-jobs "" "The number of parallel build jobs to use" + build-runtime-with-host-compiler "" "use the host c++ compiler to build everything" + build-stdlib-deployment-targets "all" "space-separated list that filters which of the configured targets to build the Swift standard library for, or 'all'" + build-toolchain-only "" "If set, only build the necessary tools to build an external toolchain" + cmake-generator "Unix Makefiles" "kind of build system to generate; see output of 'cmake --help' for choices" + llvm-num-parallel-lto-link-jobs "" "The number of parallel link jobs to use when compiling llvm" + reconfigure "" "force a CMake configuration run even if CMakeCache.txt already exists" + skip-reconfigure "" "set to skip reconfigure" + swift-tools-num-parallel-lto-link-jobs "" "The number of parallel link jobs to use when compiling swift tools" + use-gold-linker "" "Enable using the gold linker" + workspace "${HOME}/src" "source directory containing llvm, clang, swift" + + ## Build Tools + host-cc "" "the path to CC, the 'clang' compiler for the host platform. **This argument is required**" + host-cxx "" "the path to CXX, the 'clang++' compiler for the host platform. **This argument is required**" + host-libtool "" "the path to libtool" + host-lipo "" "the path to lipo for creating universal binaries on Darwin" + cmake "" "path to the cmake binary" + distcc "" "use distcc in pump mode" + distcc-pump "" "the path to distcc pump executable. This argument is required if distcc is set." + ninja-bin "" "the path to Ninja tool" + + ## Android Options + android-api-level "" "The Android API level to target when building for Android. Currently only 21 or above is supported" + android-arch "armv7" "The Android target architecture when building for Android" + android-deploy-device-path "" "Path on an Android device to which built Swift stdlib products will be deployed" + android-icu-data "" "Path to libicudata.so" + android-icu-i18n "" "Path to libicui18n.so" + android-icu-i18n-include "" "Path to a directory containing headers libicui18n" + android-icu-uc "" "Path to libicuuc.so" + android-icu-uc-include "" "Path to a directory containing headers for libicuuc" + android-ndk "" "An absolute path to the NDK that will be used as a libc implementation for Android builds" + android-ndk-gcc-version "" "The GCC version to use when building for Android. Currently only 4.9 is supported" + + ## Darwin Options + darwin-crash-reporter-client "" "whether to enable CrashReporter integration" + darwin-deployment-version-ios "7.0" "minimum deployment target version for iOS" + darwin-deployment-version-osx "10.9" "minimum deployment target version for OS X" + darwin-deployment-version-tvos "9.0" "minimum deployment target version for tvOS" + darwin-deployment-version-watchos "2.0" "minimum deployment target version for watchOS" + darwin-install-extract-symbols "" "whether to extract symbols with dsymutil during installations" + darwin-overlay-target "" "single overlay target to build, dependencies are computed later" + darwin-sdk-deployment-targets "xctest-ios-8.0" "semicolon-separated list of triples like 'fookit-ios-9.0;barkit-watchos-9.0'" + darwin-stdlib-install-name-dir "" "the directory of the install_name for standard library dylibs" + darwin-toolchain-alias "" "Swift alias for toolchain" + darwin-toolchain-application-cert "" "Application Cert name to codesign xctoolchain" + darwin-toolchain-bundle-identifier "" "CFBundleIdentifier for xctoolchain info plist" + darwin-toolchain-display-name "" "Display Name for xctoolcain info plist" + darwin-toolchain-display-name-short "" "Display Name with out date for xctoolchain info plist" + darwin-toolchain-installer-cert "" "Installer Cert name to create installer pkg" + darwin-toolchain-installer-package "" "The path to installer pkg" + darwin-toolchain-name "" "Directory name for xctoolchain" + darwin-toolchain-version "" "Version for xctoolchain info plist and installer pkg" + darwin-xcrun-toolchain "default" "the name of the toolchain to use on Darwin" + + ## Build Types for Components + swift-stdlib-build-type "Debug" "the CMake build variant for Swift" + + ## Skip Build ... + skip-build "" "set to configure as usual while skipping the build step" + skip-build-android "" "set to skip building Swift stdlibs for Android" + skip-build-benchmarks "" "set to skip building Swift Benchmark Suite" + skip-build-clang-tools-extra "" "set to skip building clang-tools-extra as part of llvm" + skip-build-compiler-rt "" "set to skip building Compiler-RT" + skip-build-cygwin "" "set to skip building Swift stdlibs for Cygwin" + skip-build-external-benchmarks "1" "set to skip building the external Swift Benchmark Suite. (skipped by default)" + skip-build-freebsd "" "set to skip building Swift stdlibs for FreeBSD" + skip-build-haiku "" "set to skip building Swift stdlibs for Haiku" + skip-build-ios-device "" "set to skip building Swift stdlibs for iOS devices (i.e. build simulators only)" + skip-build-ios-simulator "" "set to skip building Swift stdlibs for iOS simulators (i.e. build devices only)" + skip-build-linux "" "set to skip building Swift stdlibs for Linux" + skip-build-maccatalyst "" "set to skip building Swift stdlibs for macCatalyst" + skip-build-osx "" "set to skip building Swift stdlibs for OS X" + skip-build-tvos-device "" "set to skip building Swift stdlibs for tvOS devices (i.e. build simulators only)" + skip-build-tvos-simulator "" "set to skip building Swift stdlibs for tvOS simulators (i.e. build devices only)" + skip-build-watchos-device "" "set to skip building Swift stdlibs for Apple watchOS devices (i.e. build simulators only)" + skip-build-watchos-simulator "" "set to skip building Swift stdlibs for Apple watchOS simulators (i.e. build devices only)" + + ## Skip Test ... + skip-test-android "" "set to skip testing Swift stdlibs for Android" + skip-test-android-host "" "set to skip testing the host parts of the Android toolchain" + skip-test-cygwin "" "set to skip testing Swift stdlibs for Cygwin" + skip-test-freebsd "" "set to skip testing Swift stdlibs for FreeBSD" + skip-test-haiku "" "set to skip testing Swift stdlibs for Haiku" + skip-test-ios-32bit-simulator "" "set to skip testing Swift stdlibs for iOS 32bit simulators" + skip-test-ios-host "" "set to skip testing the host parts of the iOS toolchain" + skip-test-ios-simulator "" "set to skip testing Swift stdlibs for iOS simulators (i.e. test devices only)" + skip-test-linux "" "set to skip testing Swift stdlibs for Linux" + skip-test-maccatalyst "" "set to skip testing Swift stdlibs for macCatalyst" + skip-test-osx "" "set to skip testing Swift stdlibs for OS X" + skip-test-tvos-host "" "set to skip testing the host parts of the tvOS toolchain" + skip-test-tvos-simulator "" "set to skip testing Swift stdlibs for tvOS simulators (i.e. test devices only)" + skip-test-watchos-host "" "set to skip testing the host parts of the watchOS toolchain" + skip-test-watchos-simulator "" "set to skip testing Swift stdlibs for Apple watchOS simulators (i.e. test devices only)" + skip-test-benchmarks "" "set to skip running Swift Benchmark Suite" + skip-test-optimized "" "set to skip testing the test suite in optimized mode" + skip-test-optimize-for-size "" "set to skip testing the test suite in optimize for size mode" + skip-test-optimize-none-with-implicit-dynamic "" "set to skip testing the test suite in optimize none with implicit dynamic mode" + skip-test-sourcekit "" "set to skip testing SourceKit" + + ## Extra ... CMake Options + common-cmake-options "" "CMake options used for all targets, including LLVM/Clang" + extra-cmake-options "" "Extra options to pass to CMake for all targets" + ninja-cmake-options "" "CMake options used for all ninja targets" + ninja-cmake-options "" "CMake options used for all ninja targets" + + ## Build ... + build-llvm "1" "set to 1 to build LLVM and Clang" + build-sil-debugging-stdlib "0" "set to 1 to build the Swift standard library with -gsil to enable debugging and profiling on SIL level" + build-swift-dynamic-sdk-overlay "" "set to 1 to build dynamic variants of the Swift SDK overlay" + build-swift-dynamic-stdlib "" "set to 1 to build dynamic variants of the Swift standard library" + build-swift-examples "1" "set to 1 to build examples" + build-swift-remote-mirror "1" "set to 1 to build the Swift Remote Mirror library" + build-swift-static-sdk-overlay "" "set to 1 to build static variants of the Swift SDK overlay" + build-swift-static-stdlib "" "set to 1 to build static variants of the Swift standard library" + build-swift-stdlib-unittest-extra "0" "set to 1 to build optional StdlibUnittest components" + build-swift-tools "1" "set to 1 to build Swift host tools" + + ## Test Options + llvm-include-tests "1" "Set to true to generate testing targets for LLVM. Set to true by default." + long-test "0" "set to run the long test suite" + only-executable-test "" "only run the executable variant of the swift lit tests" + stress-test "0" "set to run the stress test suite" + stress-test-sourcekit "" "set to run the stress-SourceKit target" + swift-include-tests "1" "Set to true to generate testing targets for Swift. This allows the build to proceed when 'test' directory is missing (required for B&I builds)" + validation-test "0" "set to run the validation test suite" + + ## llbuild Options + llbuild-enable-assertions "1" "enable assertions in llbuild" + + ## LLDB Options + lldb-assertions "1" "build lldb with assertions enabled" + lldb-extra-cmake-args "" "extra command line args to pass to lldb cmake" + lldb-no-debugserver "" "delete debugserver after building it, and don't try to codesign it" + lldb-test-cc "" "CC to use for building LLDB testsuite test inferiors. Defaults to just-built, in-tree clang. If set to 'host-toolchain', sets it to same as host-cc." + lldb-test-swift-compatibility "" "specify additional Swift compilers to test lldb with" + lldb-test-swift-only "0" "when running lldb tests, only include Swift-specific tests" + lldb-use-system-debugserver "" "don't try to codesign debugserver, and use the system's debugserver instead" + + ## LLVM Options + llvm-enable-lto "" "Must be set to one of 'thin' or 'full'" + llvm-enable-modules "0" "enable building llvm using modules" + llvm-install-components "" "a semicolon-separated list of LLVM components to install" + llvm-lit-args "" "If set, override the lit args passed to LLVM" + enable-llvm-assertions "1" "set to enable llvm assertions" + + ## Swift Options + swift-analyze-code-coverage "not-merged" "Code coverage analysis mode for Swift (false, not-merged, merged). Defaults to false if the argument is not present, and not-merged if the argument is present without a modifier." + swift-enable-assertions "1" "enable assertions in Swift" + swift-enable-ast-verifier "1" "If enabled, and the assertions are enabled, the built Swift compiler will run the AST verifier every time it is invoked" + swift-install-components "" "a semicolon-separated list of Swift components to install" + swift-primary-variant-arch "" "default arch for target binaries" + swift-primary-variant-sdk "" "default SDK for target binaries" + swift-runtime-enable-leak-checker "0" "Enable leaks checking routines in the runtime" + swift-stdlib-enable-assertions "1" "enable assertions in Swift" + swift-stdlib-use-nonatomic-rc "0" "build the Swift stdlib and overlays with nonatomic reference count operations enabled" + swift-tools-enable-lto "" "enable LTO compilation of Swift tools. *NOTE* This does not include the swift standard library and runtime. Must be set to one of 'thin' or 'full'" + extra-swift-args "" "Extra arguments to pass to swift modules which match regex. Assumed to be a flattened cmake list consisting of [module_regexp, args, module_regexp, args, ...]" + report-statistics "0" "set to 1 to generate compilation statistics files for swift libraries" + sil-verify-all "0" "If enabled, run the SIL verifier after each transform when building Swift files during this build process" + stdlib-deployment-targets "" "space-separated list of targets to configure the Swift standard library to be compiled or cross-compiled for" + + ## Uncategorised + install-prefix "" "installation prefix" + toolchain-prefix "" "the path to the .xctoolchain directory that houses the install prefix path" + install-destdir "" "the path to use as the filesystem root for the installation" + install-symroot "" "the path to install debug symbols into" + installable-package "" "the path to the archive of the installation directory" + test-installable-package "" "whether to run post-packaging tests on the produced package" + skip-local-build "" "set to skip building for the current host (useful when crosscompiling)" + test-paths "" "run tests located in specific directories and/or files" + native-llvm-tools-path "" "directory that contains LLVM tools that are executable on the build machine" + native-clang-tools-path "" "directory that contains Clang tools that are executable on the build machine" + native-swift-tools-path "" "directory that contains Swift tools that are executable on the build machine" + embed-bitcode-section "0" "embed an LLVM bitcode section in stdlib/overlay binaries for supported platforms" + host-target "" "The host target. LLVM, Clang, and Swift will be built for this target. The built LLVM and Clang will be used to compile Swift for the cross-compilation targets. **This argument is required**" + cross-compile-hosts "" "space-separated list of targets to cross-compile host Swift tools for" + cross-compile-with-host-tools "" "set to use the clang we build for the host to then build the cross-compile hosts" + cross-compile-install-prefixes "" "semicolon-separated list of install prefixes to use for the cross-compiled hosts. The list expands, so if there are more cross-compile hosts than prefixes, unmatched hosts use the last prefix in the list" + skip-merge-lipo-cross-compile-tools "" "set to skip running merge-lipo after installing cross-compiled host Swift tools" + coverage-db "" "If set, coverage database to use when prioritizing testing" + skip-local-host-install "" "If we are cross-compiling multiple targets, skip an install pass locally if the hosts match" +) + +components=( + cmark + foundation + libcxx + libdispatch + libicu + llbuild + lldb + llvm + playgroundsupport + static-foundation + static-libdispatch + swift + xctest ) +for component in ${components[@]} ; do + KNOWN_SETTINGS+=( + ${component}-build-type "Debug" "the build variant for ${component}" + ${component}-cmake-options "" "CMake options used for ${component}" + skip-build-${component} "" "set to skip building ${component}" + skip-test-${component} "" "set to skip testing ${component}" + install-${component} "" "whether to install ${component}" + ) +done # Centralized access point for traced command invocation. # Every operation that might mutates file system should be called via @@ -348,14 +337,6 @@ function to_varname() { toupper "$(echo $1 | tr '-' '_')" } -function set_lldb_build_mode() { - if [[ "${LLDB_BUILD_TYPE}" == "RelWithDebInfo" ]]; then - LLDB_BUILD_MODE="CustomSwift-Release" - else - LLDB_BUILD_MODE="CustomSwift-${LLDB_BUILD_TYPE}" - fi -} - function is_llvm_lto_enabled() { if [[ "${LLVM_ENABLE_LTO}" == "thin" ]] || [[ "${LLVM_ENABLE_LTO}" == "full" ]]; then @@ -419,12 +400,45 @@ function should_execute_host_actions_for_phase() { fi } +function verify_host_is_supported() { + local host="$1" + case ${host} in + freebsd-x86_64 \ + | cygwin-x86_64 \ + | haiku-x86_64 \ + | linux-x86_64 \ + | linux-i686 \ + | linux-armv6 \ + | linux-armv7 \ + | linux-aarch64 \ + | linux-powerpc64 \ + | linux-powerpc64le \ + | linux-s390x \ + | macosx-x86_64 \ + | iphonesimulator-i386 \ + | iphonesimulator-x86_64 \ + | iphoneos-armv7 \ + | iphoneos-armv7s \ + | iphoneos-arm64 \ + | appletvsimulator-x86_64 \ + | appletvos-arm64 \ + | watchsimulator-i386 \ + | watchos-armv7k \ + | android-armv7 \ + | android-aarch64) + ;; + *) + echo "Unknown host tools target: ${host}" + exit 1 + ;; + esac +} + function set_build_options_for_host() { llvm_cmake_options=() swift_cmake_options=() cmark_cmake_options=() lldb_cmake_options=() - swiftpm_bootstrap_options=() SWIFT_HOST_VARIANT= SWIFT_HOST_VARIANT_SDK= SWIFT_HOST_VARIANT_ARCH= @@ -432,227 +446,117 @@ function set_build_options_for_host() { local host="$1" # Hosts which can be cross-compiled must specify: - # SWIFT_HOST_TRIPLE and llvm_target_arch (as well as usual HOST_VARIANT flags) + # SWIFT_HOST_TRIPLE and llvm_target_arch. + # Hosts which have differing platforn names from their + # SWIFT_HOST_VARIANT_* values should change them here as well. + + verify_host_is_supported $host + + local platform=${host%%-*} + local architecture=${host##*-} + + SWIFT_HOST_VARIANT=$platform + SWIFT_HOST_VARIANT_SDK=$(toupper $platform) + SWIFT_HOST_VARIANT_ARCH=$architecture case ${host} in - freebsd-x86_64) - SWIFT_HOST_VARIANT="freebsd" - SWIFT_HOST_VARIANT_SDK="FREEBSD" - SWIFT_HOST_VARIANT_ARCH="x86_64" - ;; - cygwin-x86_64) - SWIFT_HOST_VARIANT="cygwin" - SWIFT_HOST_VARIANT_SDK="CYGWIN" - SWIFT_HOST_VARIANT_ARCH="x86_64" + linux-armv6) + SWIFT_HOST_TRIPLE="armv6-unknown-linux-gnueabihf" + llvm_target_arch="ARM" ;; - haiku-x86_64) - SWIFT_HOST_VARIANT="haiku" - SWIFT_HOST_VARIANT_SDK="HAIKU" - SWIFT_HOST_VARIANT_ARCH="x86_64" + linux-armv7) + SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" + llvm_target_arch="ARM" ;; - linux-*) - SWIFT_HOST_VARIANT="linux" - SWIFT_HOST_VARIANT_SDK="LINUX" - case ${host} in - linux-x86_64) - SWIFT_HOST_VARIANT_ARCH="x86_64" - ;; - linux-i686) - SWIFT_HOST_VARIANT_ARCH="i686" - ;; - linux-armv6) - SWIFT_HOST_VARIANT_ARCH="armv6" - SWIFT_HOST_TRIPLE="armv6-unknown-linux-gnueabihf" - llvm_target_arch="ARM" - ;; - linux-armv7) - SWIFT_HOST_VARIANT_ARCH="armv7" - SWIFT_HOST_TRIPLE="armv7-unknown-linux-gnueabihf" - llvm_target_arch="ARM" - ;; - linux-aarch64) - SWIFT_HOST_VARIANT_ARCH="aarch64" - ;; - linux-powerpc64) - SWIFT_HOST_VARIANT_ARCH="powerpc64" - ;; - linux-powerpc64le) - SWIFT_HOST_VARIANT_ARCH="powerpc64le" - ;; - linux-s390x) - SWIFT_HOST_VARIANT_ARCH="s390x" - ;; - esac - ;; - macosx-* | iphoneos-* | iphonesimulator-* | \ - appletvos-* | appletvsimulator-* | \ - watchos-* | watchsimulator-*) + macosx-* | \ + iphoneos-* | \ + iphonesimulator-* | \ + appletvos-* | \ + appletvsimulator-* | \ + watchos-* | \ + watchsimulator-*) case ${host} in macosx-x86_64) - xcrun_sdk_name="macosx" - llvm_target_arch="" SWIFT_HOST_TRIPLE="x86_64-apple-macosx${DARWIN_DEPLOYMENT_VERSION_OSX}" - SWIFT_HOST_VARIANT="macosx" - SWIFT_HOST_VARIANT_SDK="OSX" - SWIFT_HOST_VARIANT_ARCH="x86_64" + llvm_target_arch="" + SWIFT_HOST_VARIANT_SDK="OSX" cmake_osx_deployment_target="${DARWIN_DEPLOYMENT_VERSION_OSX}" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - -DCMAKE_OSX_DEPLOYMENT_TARGET="${cmake_osx_deployment_target}" - ) - swiftpm_bootstrap_options=( - --sysroot="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; iphonesimulator-i386) - xcrun_sdk_name="iphonesimulator" - llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" - SWIFT_HOST_VARIANT="iphonesimulator" - SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" - SWIFT_HOST_VARIANT_ARCH="i386" + llvm_target_arch="X86" + SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; iphonesimulator-x86_64) - xcrun_sdk_name="iphonesimulator" - llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" - SWIFT_HOST_VARIANT="iphonesimulator" - SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" - SWIFT_HOST_VARIANT_ARCH="x86_64" + llvm_target_arch="X86" + SWIFT_HOST_VARIANT_SDK="IOS_SIMULATOR" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; iphoneos-armv7) - xcrun_sdk_name="iphoneos" - llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" - SWIFT_HOST_VARIANT="iphoneos" - SWIFT_HOST_VARIANT_SDK="IOS" - SWIFT_HOST_VARIANT_ARCH="armv7" + llvm_target_arch="ARM" + SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; iphoneos-armv7s) - xcrun_sdk_name="iphoneos" - llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7s-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" - SWIFT_HOST_VARIANT="iphoneos" - SWIFT_HOST_VARIANT_SDK="IOS" - SWIFT_HOST_VARIANT_ARCH="armv7s" + llvm_target_arch="ARM" + SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; iphoneos-arm64) - xcrun_sdk_name="iphoneos" - llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-ios${DARWIN_DEPLOYMENT_VERSION_IOS}" - SWIFT_HOST_VARIANT="iphoneos" - SWIFT_HOST_VARIANT_SDK="IOS" - SWIFT_HOST_VARIANT_ARCH="arm64" + llvm_target_arch="AArch64" + SWIFT_HOST_VARIANT_SDK="IOS" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; appletvsimulator-x86_64) - xcrun_sdk_name="appletvsimulator" - llvm_target_arch="X86" SWIFT_HOST_TRIPLE="x86_64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" - SWIFT_HOST_VARIANT="appletvsimulator" - SWIFT_HOST_VARIANT_SDK="TVOS_SIMULATOR" - SWIFT_HOST_VARIANT_ARCH="x86_64" + llvm_target_arch="X86" + SWIFT_HOST_VARIANT_SDK="TVOS_SIMULATOR" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; appletvos-arm64) - xcrun_sdk_name="appletvos" - llvm_target_arch="AArch64" SWIFT_HOST_TRIPLE="arm64-apple-tvos${DARWIN_DEPLOYMENT_VERSION_TVOS}" - SWIFT_HOST_VARIANT="appletvos" - SWIFT_HOST_VARIANT_SDK="TVOS" - SWIFT_HOST_VARIANT_ARCH="arm64" + llvm_target_arch="AArch64" + SWIFT_HOST_VARIANT_SDK="TVOS" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; watchsimulator-i386) - xcrun_sdk_name="watchsimulator" - llvm_target_arch="X86" SWIFT_HOST_TRIPLE="i386-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" - SWIFT_HOST_VARIANT="watchsimulator" - SWIFT_HOST_VARIANT_SDK="WATCHOS_SIMULATOR" - SWIFT_HOST_VARIANT_ARCH="i386" + llvm_target_arch="X86" + SWIFT_HOST_VARIANT_SDK="WATCHOS_SIMULATOR" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) ;; watchos-armv7k) - xcrun_sdk_name="watchos" - llvm_target_arch="ARM" SWIFT_HOST_TRIPLE="armv7k-apple-watchos${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" - SWIFT_HOST_VARIANT="watchos" - SWIFT_HOST_VARIANT_SDK="WATCHOS" - SWIFT_HOST_VARIANT_ARCH="armv7k" + llvm_target_arch="ARM" + SWIFT_HOST_VARIANT_SDK="WATCHOS" cmake_osx_deployment_target="" - cmark_cmake_options=( - -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" - ) - ;; - *) - echo "Unknown host for swift tools: ${host}" - exit 1 ;; esac if [[ "${DARWIN_SDK_DEPLOYMENT_TARGETS}" != "" ]]; then - local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS=";"; DARWIN_SDK_DEPLOYMENT_TARGETS=($DARWIN_SDK_DEPLOYMENT_TARGETS); unset IFS for target in "${DARWIN_SDK_DEPLOYMENT_TARGETS[@]}"; do - local IFS="-"; triple=($target) + # IFS is immediately unset after its use to avoid unwanted + # replacement of characters in subsequent lines. + local IFS="-"; triple=($target); unset IFS sdk_target=$(toupper ${triple[0]}_${triple[1]}) swift_cmake_options+=( "-DSWIFTLIB_DEPLOYMENT_VERSION_${sdk_target}=${triple[2]}" @@ -660,9 +564,17 @@ function set_build_options_for_host() { done fi + cmake_os_sysroot="$(xcrun --sdk ${platform} --show-sdk-path)" + + cmark_cmake_options=( + -DCMAKE_C_FLAGS="$(cmark_c_flags ${host})" + -DCMAKE_CXX_FLAGS="$(cmark_c_flags ${host})" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" + -DCMAKE_OSX_DEPLOYMENT_TARGET="${cmake_osx_deployment_target}" + ) llvm_cmake_options=( -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}" - -DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)" + -DCMAKE_OSX_SYSROOT:PATH="${cmake_os_sysroot}" -DCOMPILER_RT_ENABLE_IOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_WATCHOS:BOOL=FALSE -DCOMPILER_RT_ENABLE_TVOS:BOOL=FALSE @@ -670,26 +582,12 @@ function set_build_options_for_host() { -DLLVM_ENABLE_MODULES:BOOL="$(true_false ${LLVM_ENABLE_MODULES})" ) if [[ $(is_llvm_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) == "TRUE" ]]; then - llvm_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( "-DLLVM_PARALLEL_LINK_JOBS=${LLVM_NUM_PARALLEL_LTO_LINK_JOBS}" ) fi if [[ $(is_swift_lto_enabled) == "TRUE" ]]; then - if [[ $(cmake_needs_to_specify_standard_computed_defaults) = "TRUE" ]]; then - swift_cmake_options+=( - "-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=AppleClang" - "-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=AppleClang" - ) - fi - llvm_cmake_options+=( -DLLVM_ENABLE_MODULE_DEBUGGING:BOOL=NO ) @@ -707,10 +605,6 @@ function set_build_options_for_host() { -DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS="${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" ) ;; - *) - echo "Unknown host tools target: ${host}" - exit 1 - ;; esac @@ -916,12 +810,6 @@ done # TODO: Rename this argument LOCAL_HOST=$HOST_TARGET -# TODO: Remove this some time later. -if [[ "${USER_CONFIG_ARGS}" ]]; then - echo "Error: --user-config-args is renamed to --extra-cmake-options." 1>&2 - exit 1 -fi - if [[ "${CHECK_ARGS_ONLY}" ]]; then exit 0 fi @@ -941,34 +829,19 @@ if [[ "${SKIP_RECONFIGURE}" ]]; then RECONFIGURE="" fi -# WORKSPACE, BUILD_DIR and INSTALLABLE_PACKAGE must be absolute paths -case "${WORKSPACE}" in - /*) ;; - *) - echo "workspace must be an absolute path (was '${WORKSPACE}')" - exit 1 - ;; -esac -case "${BUILD_DIR}" in - /*) ;; - "") - echo "the --build-dir option is required" - usage - exit 1 - ;; - *) - echo "build-dir must be an absolute path (was '${BUILD_DIR}')" - exit 1 - ;; -esac -case "${INSTALLABLE_PACKAGE}" in - /*) ;; - "") ;; - *) - echo "installable-package must be an absolute path (was '${INSTALLABLE_PACKAGE}')" - exit 1 - ;; -esac +# WORKSPACE, BUILD_DIR, and INSTALLABLE_PACKAGE must be absolute paths +if ! [[ ${WORKSPACE} =~ ^/ ]] ; then + echo "workspace must be an absolute path (was '${WORKSPACE}')" + exit 1 +fi +if ! [[ ${BUILD_DIR} =~ ^/ ]] ; then + echo "build-dir must be an absolute path (was '${BUILD_DIR}')" + exit 1 +fi +if ! [[ ${INSTALLABLE_PACKAGE:-/} =~ ^/ ]] ; then + echo "installable-package must be an absolute path (was '${INSTALLABLE_PACKAGE}')" + exit 1 +fi # WORKSPACE must exist if [ ! -e "${WORKSPACE}" ] ; then @@ -1011,26 +884,6 @@ function false_true() { fi } -function cmake_version() { - "${CMAKE}" --version | grep "cmake version" | cut -f 3 -d " " -} - -function cmake_needs_to_specify_standard_computed_defaults() { - if [[ $(cmake_version) = "3.4.0" ]]; then - echo "TRUE" - else - echo "FALSE" - fi -} - -function make_relative_symlink() { - local SOURCE=$1 - local TARGET=$2 - local TARGET_DIR=$(dirname "$2") - local RELATIVE_SOURCE=$(python -c "import os.path; print(os.path.relpath(\"${SOURCE}\", \"${TARGET_DIR}\"))") - call ln -sf "${RELATIVE_SOURCE}" "${TARGET}" -} - # Sanitize the list of cross-compilation targets. # # In the Build/Host/Target paradigm: @@ -1058,7 +911,7 @@ fi ALL_HOSTS=("${ALL_HOSTS[@]}" "${CROSS_COMPILE_HOSTS[@]}") function has_cross_compile_hosts() { - if [[ ${#ALL_HOSTS[@]} -gt 1 ]]; then + if [[ ${#CROSS_COMPILE_HOSTS[@]} -ge 1 ]]; then echo "1" fi } @@ -1166,49 +1019,23 @@ function get_stdlib_targets_for_host() { fi } -function should_build_stdlib_target() { - local stdlib_target=$1 - local host=$2 - if [[ "${BUILD_STDLIB_DEPLOYMENT_TARGETS}" == "all" ]]; then - echo 1 - else - # Only build the stdlib targets in 'build-stdlib-deployment-targets' - local build_list=($BUILD_STDLIB_DEPLOYMENT_TARGETS) - for t in "${build_list[@]}"; do - if [[ "${t}" == "${stdlib_target}" ]]; then - echo 1 - fi - done - # As with 'stdlib-deployment-targets', 'build-stdlib-deployment-targets' - # only applies to the LOCAL_HOST. For cross-tools hosts, always allow - # their one-and-only stdlib-target to build. - if [[ $(is_cross_tools_host ${host}) ]] && [[ "${stdlib_target}" == "${host}" ]]; then - echo 1 - fi - fi -} - # # Calculate source directories for each product. # NINJA_SOURCE_DIR="${WORKSPACE}/ninja" SWIFT_SOURCE_DIR="${WORKSPACE}/swift" -LLVM_SOURCE_DIR="${WORKSPACE}/llvm" +LLVM_SOURCE_DIR="${WORKSPACE}/llvm-project/llvm" CMARK_SOURCE_DIR="${WORKSPACE}/cmark" -LLDB_SOURCE_DIR="${WORKSPACE}/lldb" +LLDB_SOURCE_DIR="${WORKSPACE}/llvm-project/lldb" LLBUILD_SOURCE_DIR="${WORKSPACE}/llbuild" -SWIFTPM_SOURCE_DIR="${WORKSPACE}/swiftpm" -SWIFTSYNTAX_SOURCE_DIR="${WORKSPACE}/swift-syntax" STRESSTEST_PACKAGE_DIR="${WORKSPACE}/swift-stress-tester" -SKSTRESSTESTER_SOURCE_DIR="${STRESSTEST_PACKAGE_DIR}/SourceKitStressTester" -SWIFTEVOLVE_SOURCE_DIR="${STRESSTEST_PACKAGE_DIR}/SwiftEvolve" XCTEST_SOURCE_DIR="${WORKSPACE}/swift-corelibs-xctest" FOUNDATION_SOURCE_DIR="${WORKSPACE}/swift-corelibs-foundation" FOUNDATION_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-foundation" LIBDISPATCH_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBDISPATCH_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBICU_SOURCE_DIR="${WORKSPACE}/icu" -LIBCXX_SOURCE_DIR="${WORKSPACE}/libcxx" +LIBCXX_SOURCE_DIR="${WORKSPACE}/llvm-project/libcxx" PLAYGROUNDSUPPORT_SOURCE_DIR="${WORKSPACE}/swift-xcode-playground-support" if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" && ! -d ${PLAYGROUNDSUPPORT_SOURCE_DIR} ]]; then @@ -1216,98 +1043,21 @@ if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" && ! -d ${PLAYGROUNDSUPPORT_SOURCE_DIR exit 1 fi -# Symlink clang into the llvm tree. -CLANG_SOURCE_DIR="${LLVM_SOURCE_DIR}/tools/clang" -if [ ! -e "${WORKSPACE}/clang" ] ; then - # If llvm/tools/clang is already a directory, use that and skip the symlink. - if [ ! -d "${CLANG_SOURCE_DIR}" ] ; then - echo "Can't find source directory for clang (tried ${WORKSPACE}/clang and ${CLANG_SOURCE_DIR})" - exit 1 - fi -fi -if [ ! -d "${CLANG_SOURCE_DIR}" ] ; then - make_relative_symlink "${WORKSPACE}/clang" "${CLANG_SOURCE_DIR}" -fi - -# Don't symlink clang-tools-extra into the tree as the 'clang' symlink prevents -# make_relative_symlink from working correctly. -if [ -e "${WORKSPACE}/clang-tools-extra" ] ; then - CLANG_TOOLS_EXTRA_SOURCE_DIR="${WORKSPACE}/clang-tools-extra" -fi - -# Symlink compiler-rt into the llvm tree, if it exists. -COMPILER_RT_SOURCE_DIR="${LLVM_SOURCE_DIR}/projects/compiler-rt" -if [ -e "${WORKSPACE}/compiler-rt" ] ; then - if [ ! -d "${COMPILER_RT_SOURCE_DIR}" ] ; then - make_relative_symlink "${WORKSPACE}/compiler-rt" "${COMPILER_RT_SOURCE_DIR}" - fi -fi - -PRODUCTS=(cmark llvm) -if [[ ! "${SKIP_BUILD_LIBCXX}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" libcxx) -fi -if [[ ! "${SKIP_BUILD_LIBICU}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" libicu) -fi -PRODUCTS=("${PRODUCTS[@]}" swift) -if [[ ! "${SKIP_BUILD_LLDB}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" lldb) -fi -# LLBuild, SwiftPM, SwiftSyntax, the SourceKit stress tester and XCTest are -# dependent on Foundation, so Foundation must be added to the list of build -# products first. -if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" libdispatch) - if [[ -z "${SKIP_BUILD_SWIFT_STATIC_LIBDISPATCH}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" libdispatch_static) - fi -fi -if [[ ! "${SKIP_BUILD_FOUNDATION}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" foundation) - if [[ -z "${SKIP_BUILD_STATIC_FOUNDATION}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" foundation_static) - fi -fi -if [[ ! "${SKIP_BUILD_LLBUILD}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" llbuild) -fi -if [[ ! "${SKIP_BUILD_PLAYGROUNDSUPPORT}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" playgroundsupport) -fi -# SwiftPM, SwiftSyntax and the SourceKit stress tester are dependent on XCTest, -# so XCTest must be added to the list of build products first. -if [[ ! "${SKIP_BUILD_XCTEST}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" xctest) -fi -# SwiftSyntax and the SourceKit stress tester are dependent on SwiftPM, so -# SwiftPM must be added to the list of build products first. -if [[ ! "${SKIP_BUILD_SWIFTPM}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" swiftpm) -fi -# The SourceKit stress tester and swift-evolve are dependent on SwiftSyntax, so -# it must be added to the list of build products first. -if [[ ! "${SKIP_BUILD_SWIFTSYNTAX}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" swiftsyntax) -fi -if [[ ! "${SKIP_BUILD_SKSTRESSTESTER}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" skstresstester) -fi -if [[ ! "${SKIP_BUILD_SWIFTEVOLVE}" ]] ; then - PRODUCTS=("${PRODUCTS[@]}" swiftevolve) -fi - -# Checks if a given product is enabled (i.e. part of $PRODUCTS array) -function contains_product() { - local current_product - for current_product in "${PRODUCTS[@]}"; do - if [[ "$current_product" == "$1" ]]; then - return 0 - fi - done - return 1 -} - +[[ "${SKIP_BUILD_CMARK}" ]] || PRODUCTS+=(cmark) +[[ "${SKIP_BUILD_LIBCXX}" ]] || PRODUCTS+=(libcxx) +[[ "${SKIP_BUILD_LIBICU}" ]] || PRODUCTS+=(libicu) +[[ "${SKIP_BUILD_LLVM}" ]] || PRODUCTS+=(llvm) +[[ "${SKIP_BUILD_SWIFT}" ]] || PRODUCTS+=(swift) +[[ "${SKIP_BUILD_LLDB}" ]] || PRODUCTS+=(lldb) +[[ "${SKIP_BUILD_LIBDISPATCH}" ]] || PRODUCTS+=(libdispatch) +[[ "${SKIP_BUILD_STATIC_LIBDISPATCH}" ]] || PRODUCTS+=(libdispatch_static) +# llbuild and XCTest depend on Foundation, so Foundation must +# be added to the list of build products first. +[[ "${SKIP_BUILD_FOUNDATION}" ]] || PRODUCTS+=(foundation) +[[ "${SKIP_BUILD_STATIC_FOUNDATION}" ]] || PRODUCTS+=(foundation_static) +[[ "${SKIP_BUILD_LLBUILD}" ]] || PRODUCTS+=(llbuild) +[[ "${SKIP_BUILD_XCTEST}" ]] || PRODUCTS+=(xctest) +[[ "${SKIP_BUILD_PLAYGROUNDSUPPORT}" ]] || PRODUCTS+=(playgroundsupport) # get_host_specific_variable(host, name) # @@ -1323,234 +1073,12 @@ function get_host_specific_variable() { } function calculate_targets_for_host() { - local host=$1 - - SWIFT_STDLIB_TARGETS=() - SWIFT_SDKS=() - SWIFT_BENCHMARK_TARGETS=() - SWIFT_RUN_BENCHMARK_TARGETS=() - SWIFT_TEST_TARGETS=() - - # Get the list of Target platforms for the Host - local stdlib_targets=($(get_stdlib_targets_for_host ${host})) - - for stdlib_deployment_target in "${stdlib_targets[@]}"; do - local swift_sdk= - local is_in_build_list=$(should_build_stdlib_target ${stdlib_deployment_target} ${host}) - local build_for_this_target=1 - local test_this_target=1 - local test_host_only= - local build_benchmark_this_target= - local build_external_benchmark_this_target= - local test_benchmark_this_target= - - case ${stdlib_deployment_target} in - linux-*) - swift_sdk="LINUX" - build_for_this_target=$(not ${SKIP_BUILD_LINUX}) - test_this_target=$(not ${SKIP_TEST_LINUX}) - ;; - freebsd-*) - swift_sdk="FREEBSD" - build_for_this_target=$(not ${SKIP_BUILD_FREEBSD}) - test_this_target=$(not ${SKIP_TEST_FREEBSD}) - ;; - cygwin-*) - swift_sdk="CYGWIN" - build_for_this_target=$(not ${SKIP_BUILD_CYGWIN}) - test_this_target=$(not ${SKIP_TEST_CYGWIN}) - ;; - haiku-*) - swift_sdk="HAIKU" - build_for_this_target=$(not ${SKIP_BUILD_HAIKU}) - test_this_target=$(not ${SKIP_TEST_HAIKU}) - ;; - macosx-*) - swift_sdk="OSX" - build_for_this_target=$(not ${SKIP_BUILD_OSX}) - test_this_target=$(not ${SKIP_TEST_OSX}) - build_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - test_benchmark_this_target=$(not ${SKIP_BUILD_OSX}) - ;; - iphoneos-*) - swift_sdk="IOS" - build_for_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - if [[ ! "${SKIP_TEST_IOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_IOS_DEVICE}) - - # Never build iOS armv7s benchmarks. - if [[ "${stdlib_deployment_target}" == "iphoneos-armv7s" ]]; then - build_benchmark_this_target= - build_external_benchmark_this_target= - fi - ;; - iphonesimulator-x86_64) - swift_sdk="IOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_IOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_IOS_SIMULATOR}) - ;; - iphonesimulator-i386) - swift_sdk="IOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_IOS_SIMULATOR}) - if [[ "${SKIP_TEST_IOS_SIMULATOR}" == "1" ]] ; then - SKIP_TEST_IOS_32BIT_SIMULATOR="${SKIP_TEST_IOS_SIMULATOR}" - fi - test_this_target=$(not ${SKIP_TEST_IOS_32BIT_SIMULATOR}) - ;; - appletvos-*) - swift_sdk="TVOS" - build_for_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - if [[ ! "${SKIP_TEST_TVOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_TVOS_DEVICE}) - ;; - appletvsimulator-*) - swift_sdk="TVOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_TVOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_TVOS_SIMULATOR}) - ;; - watchos-*) - swift_sdk="WATCHOS" - build_for_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - if [[ ! "${SKIP_TEST_WATCHOS_HOST}" ]] ; then - test_host_only=1 - else - test_this_target= - fi - build_benchmark_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - build_external_benchmark_this_target=$(not ${SKIP_BUILD_WATCHOS_DEVICE}) - ;; - watchsimulator-*) - swift_sdk="WATCHOS_SIMULATOR" - build_for_this_target=$(not ${SKIP_BUILD_WATCHOS_SIMULATOR}) - test_this_target=$(not ${SKIP_TEST_WATCHOS_SIMULATOR}) - ;; - android-*) - swift_sdk="ANDROID" - build_for_this_target=$(not ${SKIP_BUILD_ANDROID}) - if [[ ! "${SKIP_TEST_ANDROID_HOST}" ]] ; then - test_host_only=1 - else - test_this_target=$(not ${SKIP_TEST_ANDROID}) - fi - ;; - *) - echo "Unknown compiler deployment target: ${stdlib_deployment_target}" - exit 1 - ;; - esac - - SWIFT_SDKS+=("${swift_sdk}") - - if [[ "${build_for_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - - if [[ "${BUILD_SWIFT_STDLIB_UNITTEST_EXTRA}" == "1" ]] ; then - SWIFT_STDLIB_TARGETS+=("swift-stdlib-${stdlib_deployment_target}") - else - if [[ "${VALIDATION_TEST}" == "1" || "${LONG_TEST}" == "1" ]] ; then - SWIFT_STDLIB_TARGETS+=("swift-stdlib-${stdlib_deployment_target}") - else - SWIFT_STDLIB_TARGETS+=("swift-test-stdlib-${stdlib_deployment_target}") - fi - fi - fi - if [[ "${build_benchmark_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - SWIFT_BENCHMARK_TARGETS+=("swift-benchmark-${stdlib_deployment_target}") - if [[ $(not ${SKIP_TEST_BENCHMARKS}) ]] ; then - SWIFT_RUN_BENCHMARK_TARGETS+=("check-swift-benchmark-${stdlib_deployment_target}") - fi - fi - - if [[ "$(true_false ${SKIP_BUILD_EXTERNAL_BENCHMARKS})" == "FALSE" ]] && - [[ "${build_external_benchmark_this_target}" ]] && - [[ "${is_in_build_list}" ]] ; then - SWIFT_BENCHMARK_TARGETS+=("swift-benchmark-${stdlib_deployment_target}-external") - if [[ $(not ${SKIP_TEST_BENCHMARKS}) ]] ; then - SWIFT_RUN_BENCHMARK_TARGETS+=("check-swift-benchmark-${stdlib_deployment_target}-external") - fi - fi - - if [[ "${test_this_target}" ]] && [[ "${is_in_build_list}" ]]; then - test_target_suffix="" - if [[ -n "${test_host_only}" ]] ; then - test_target_suffix="-only_non_executable" - elif [[ -n "${ONLY_EXECUTABLE_TEST}" ]] ; then - test_target_suffix="-only_executable" - fi - - test_subset_target_suffix="" - if [[ "${VALIDATION_TEST}" == "1" ]] ; then - if [[ "${LONG_TEST}" == "1" ]] ; then - test_subset_target_suffix="-all" - else - test_subset_target_suffix="-validation" - fi - else - if [[ "${LONG_TEST}" == "1" ]] ; then - test_subset_target_suffix="-only_long" - fi - fi - - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}${test_target_suffix}-${stdlib_deployment_target}") - if [[ $(not ${SKIP_TEST_OPTIMIZED}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize-${stdlib_deployment_target}") - fi - if [[ $(not ${SKIP_TEST_OPTIMIZE_FOR_SIZE}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize_size-${stdlib_deployment_target}") - fi - if [[ $(not ${SKIP_TEST_OPTIMIZE_NONE_WITH_IMPLICIT_DYNAMIC}) && ! -n "${test_host_only}" ]] ; then - SWIFT_TEST_TARGETS+=("check-swift${test_subset_target_suffix}-optimize_none_with_implicit_dynamic-${stdlib_deployment_target}") - fi - fi - done - - # Filter duplicate SWIFT_SDKs - # We will get them if building for multiple architecture variants - SWIFT_SDKS=($(echo "${SWIFT_SDKS[@]}" | tr " " "\n" | sort -u | tr "\n" " ")) - # Get the values passed by `build-script`. - LEGACY_SWIFT_STDLIB_TARGETS=(${SWIFT_STDLIB_TARGETS[@]}) - LEGACY_SWIFT_SDKS=(${SWIFT_SDKS[@]}) - LEGACY_SWIFT_BENCHMARK_TARGETS=(${SWIFT_BENCHMARK_TARGETS[@]}) - LEGACY_SWIFT_RUN_BENCHMARK_TARGETS=(${SWIFT_RUN_BENCHMARK_TARGETS[@]}) - LEGACY_SWIFT_TEST_TARGETS=(${SWIFT_TEST_TARGETS[@]}) SWIFT_STDLIB_TARGETS=($(get_host_specific_variable ${host} SWIFT_STDLIB_TARGETS)) SWIFT_SDKS=($(get_host_specific_variable ${host} SWIFT_SDKS)) SWIFT_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_BENCHMARK_TARGETS)) SWIFT_RUN_BENCHMARK_TARGETS=($(get_host_specific_variable ${host} SWIFT_RUN_BENCHMARK_TARGETS)) SWIFT_TEST_TARGETS=($(get_host_specific_variable ${host} SWIFT_TEST_TARGETS)) - - # Validate the parameters match. - if [[ "${SWIFT_STDLIB_TARGETS[*]}" != "${LEGACY_SWIFT_STDLIB_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_STDLIB_TARGETS': '%s' vs '%s'\n" "${SWIFT_STDLIB_TARGETS[*]}" "${LEGACY_SWIFT_STDLIB_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_SDKS[*]}" != "${LEGACY_SWIFT_SDKS[*]}" ]]; then - printf "error: invalid build-script for 'SWIFT_SDKS' refactor: '%s' vs '%s'\n" "${SWIFT_SDKS[*]}" "${LEGACY_SWIFT_SDKS[*]}" - exit 1 - fi - if [[ "${SWIFT_BENCHMARK_TARGETS[*]}" != "${LEGACY_SWIFT_BENCHMARK_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_BENCHMARK_TARGETS': '%s' vs '%s'\n" "${SWIFT_BENCHMARK_TARGETS[*]}" "${LEGACY_SWIFT_BENCHMARK_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_RUN_BENCHMARK_TARGETS[*]}" != "${LEGACY_SWIFT_RUN_BENCHMARK_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_RUN_BENCHMARK_TARGETS': '%s' vs '%s'\n" "${SWIFT_RUN_BENCHMARK_TARGETS[*]}" "${LEGACY_SWIFT_RUN_BENCHMARK_TARGETS[*]}" - exit 1 - fi - if [[ "${SWIFT_TEST_TARGETS[*]}" != "${LEGACY_SWIFT_TEST_TARGETS[*]}" ]]; then - printf "error: invalid build-script refactor for 'SWIFT_TEST_TARGETS': '%s' vs '%s'\n" "${SWIFT_TEST_TARGETS[*]}" "${LEGACY_SWIFT_TEST_TARGETS[*]}" - exit 1 - fi } @@ -1598,18 +1126,6 @@ function build_directory_bin() { llbuild) echo "${root}/${LLBUILD_BUILD_TYPE}/bin" ;; - swiftpm) - echo "${root}/${SWIFTPM_BUILD_TYPE}/bin" - ;; - swiftsyntax) - echo "${root}/${SWIFTSYNTAX_BUILD_TYPE}/bin" - ;; - skstresstester) - echo "${root}/${SKSTRESSTESTER_BUILD_TYPE}/bin" - ;; - swiftevolve) - echo "${root}/${SWIFTEVOLVE_BUILD_TYPE}/bin" - ;; xctest) echo "${root}/${XCTEST_BUILD_TYPE}/bin" ;; @@ -1630,12 +1146,7 @@ function build_directory_bin() { ;; esac else - if [[ "${product}" == "swiftpm" ]] ; then - set_swiftpm_bootstrap_command - echo "$(${swiftpm_bootstrap_command[@]} --show-bin-path)" - else - echo "${root}/bin" - fi + echo "${root}/bin" fi } @@ -1654,48 +1165,38 @@ function is_cmake_debuginfo_build_type() { function common_cross_c_flags() { echo -n "${COMMON_C_FLAGS}" - case $1 in - iphonesimulator-i386) - echo -n " -arch i386 -mios-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" - ;; - iphonesimulator-x86_64) - echo -n " -arch x86_64 -mios-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" - ;; - iphoneos-armv7) - echo -n " -arch armv7 -miphoneos-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" - ;; - iphoneos-armv7s) - echo -n " -arch armv7s -miphoneos-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" - ;; - iphoneos-arm64) - echo -n " -arch arm64 -miphoneos-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" - ;; - appletvsimulator-x86_64) - echo -n " -arch x86_64 -mtvos-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_TVOS}" + local host=$1 + local arch=${host##*-} + + case $host in + iphonesimulator-*) + echo -n " -arch ${arch} -mios-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" ;; - appletvos-arm64) - echo -n " -arch arm64 -mtvos-version-min=${DARWIN_DEPLOYMENT_VERSION_TVOS}" + iphoneos-*) + echo -n " -arch ${arch} -miphoneos-version-min=${DARWIN_DEPLOYMENT_VERSION_IOS}" ;; - watchsimulator-i386) - echo -n " -arch i386 -mwatchos-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" + appletvsimulator-*) + echo -n " -arch ${arch} -mtvos-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_TVOS}" ;; - watchos-armv7k) - echo -n " -arch armv7k -mwatchos-version-min=${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" + appletvos-*) + echo -n " -arch ${arch} -mtvos-version-min=${DARWIN_DEPLOYMENT_VERSION_TVOS}" ;; - android-armv7) - echo -n " -arch armv7" + watchsimulator-*) + echo -n " -arch ${arch} -mwatchos-simulator-version-min=${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" ;; - android-arm64) - echo -n " -arch aarch64" + watchos-*) + echo -n " -arch ${arch} -mwatchos-version-min=${DARWIN_DEPLOYMENT_VERSION_WATCHOS}" ;; esac + + local build_type=$2 + if [[ $(is_cmake_release_build_type ${build_type}) ]] ; then + echo -n " -fno-stack-protector" + fi } function llvm_c_flags() { - echo -n " $(common_cross_c_flags $1)" - if [[ $(is_cmake_release_build_type "${LLVM_BUILD_TYPE}") ]] ; then - echo -n " -fno-stack-protector" - fi + echo -n " $(common_cross_c_flags $1 ${LLVM_BUILD_TYPE})" if [[ $(is_cmake_debuginfo_build_type "${LLVM_BUILD_TYPE}") ]] ; then if [[ $(is_llvm_lto_enabled) == "TRUE" ]] ; then echo -n " -gline-tables-only" @@ -1706,10 +1207,7 @@ function llvm_c_flags() { } function cmark_c_flags() { - echo -n " $(common_cross_c_flags $1)" - if [[ $(is_cmake_release_build_type "${CMARK_BUILD_TYPE}") ]] ; then - echo -n " -fno-stack-protector" - fi + echo -n " $(common_cross_c_flags $1 ${CMARK_BUILD_TYPE})" } function swift_c_flags() { @@ -1720,9 +1218,6 @@ function swift_c_flags() { if [[ $(is_cmake_release_build_type "${SWIFT_BUILD_TYPE}") ]] ; then echo -n " -fno-stack-protector" fi - if [[ "$(true_false "${SWIFT_STDLIB_USE_NONATOMIC_RC}")" == "TRUE" ]]; then - echo -n " -DSWIFT_STDLIB_USE_NONATOMIC_RC" - fi } function cmake_config_opt() { @@ -1749,18 +1244,6 @@ function cmake_config_opt() { llbuild) echo "--config ${LLBUILD_BUILD_TYPE}" ;; - swiftpm) - echo "--config ${SWIFTPM_BUILD_TYPE}" - ;; - swiftsyntax) - echo "--config ${SWIFTSYNTAX_BUILD_TYPE}" - ;; - skstresstester) - echo "--config ${SKSTRESSTESTER_BUILD_TYPE}" - ;; - swiftevolve) - echo "--config ${SWIFTEVOLVE_BUILD_TYPE}" - ;; xctest) echo "--config ${XCTEST_BUILD_TYPE}" ;; @@ -1783,155 +1266,6 @@ function cmake_config_opt() { fi } -function set_swiftpm_bootstrap_command() { - if [[ -n "${swiftpm_bootstrap_command[@]}" ]]; then - # Already set. - return - fi - - SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" - LLBUILD_BIN="$(build_directory_bin ${LOCAL_HOST} llbuild)/swift-build-tool" - if [[ ! "${SKIP_BUILD_FOUNDATION}" ]] ; then - FOUNDATION_BUILD_DIR=$(build_directory ${host} foundation) - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS="--libdispatch-source-dir=${LIBDISPATCH_SOURCE_DIR} --libdispatch-build-dir=${LIBDISPATCH_BUILD_DIR}" - fi - if [[ ! "${SKIP_BUILD_LIBICU}" ]] ; then - LIBICU_BUILD_DIR="$(build_directory ${host} libicu)" - fi - if [[ ! "${SKIP_BUILD_XCTEST}" ]] ; then - XCTEST_BUILD_DIR=$(build_directory ${host} xctest) - fi - fi - if [ "${SKIP_BUILD_LLBUILD}" ]; then - echo "Error: Cannot build swiftpm without llbuild (swift-build-tool)." - exit 1 - fi - if [[ "${CMAKE_GENERATOR}" == "Xcode" ]]; then - echo "Error: Cannot build swiftpm when llbuild is built using Xcode." - exit 1 - fi - swiftpm_bootstrap_command=("${SWIFTPM_SOURCE_DIR}/Utilities/bootstrap" "${swiftpm_bootstrap_options[@]}") - # Add --release if we have to build in release mode. - if [[ "${SWIFTPM_BUILD_TYPE}" == "Release" ]] ; then - swiftpm_bootstrap_command+=(--release) - fi - if [[ "${VERBOSE_BUILD}" ]] ; then - swiftpm_bootstrap_command+=(-v) - fi - # FIXME CROSSCOMPILING: - # SwiftPM needs to be told about the target, sysroot and linker to use - # when cross-compiling - swiftpm_bootstrap_command+=( - --swiftc="${SWIFTC_BIN}" - --sbt="${LLBUILD_BIN}" - --build="$(build_directory ${host} swiftpm)") - - # Add flags to link llbuild. - LLBUILD_BUILD_DIR="$(build_directory ${host} llbuild)" - swiftpm_bootstrap_command+=( - --link-llbuild - --llbuild-source-dir="${LLBUILD_SOURCE_DIR}" - --llbuild-build-dir="${LLBUILD_BUILD_DIR}" - ) - - if [[ ! "${SKIP_BUILD_FOUNDATION}" ]] ; then - swiftpm_bootstrap_command+=( --foundation="${FOUNDATION_BUILD_DIR}" ) - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - swiftpm_bootstrap_command+=( - $LIBDISPATCH_BUILD_ARGS) - fi - if [[ ! "${SKIP_BUILD_XCTEST}" ]] ; then - swiftpm_bootstrap_command+=( - --xctest="${XCTEST_BUILD_DIR}") - fi - fi -} - -function swiftpm_find_tool() { - tool=$1 - if [[ "${SKIP_BUILD_SWIFTPM}" || "${BUILD_LIBPARSER_ONLY}" ]]; then - echo "$(xcrun_find_tool ${tool})" - else - echo "$(build_directory_bin ${LOCAL_HOST} swiftpm)/${tool}" - fi -} - -function set_swiftsyntax_build_command() { - if [ "${BUILD_LIBPARSER_ONLY}" ]; then - # we don't have a compiler built so we have to use the one in the environment. - SWIFTC_BIN="$(xcrun_find_tool swiftc)" - else - SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" - fi - - swiftsyntax_build_command=("${SWIFTSYNTAX_SOURCE_DIR}/build-script.py") - # Add --release if we have to build in release mode. - if [[ $(is_cmake_release_build_type "${SWIFTSYNTAX_BUILD_TYPE}") ]] ; then - swiftsyntax_build_command+=(--release) - fi - if [[ "${VERBOSE_BUILD}" ]] ; then - swiftsyntax_build_command+=(-v) - fi - swiftsyntax_build_command+=( - --build-dir="$(build_directory ${host} swiftsyntax)" - --swift-build-exec="$(swiftpm_find_tool swift-build)" - --swift-test-exec="$(swiftpm_find_tool swift-test)" - --swiftc-exec="${SWIFTC_BIN}" - --syntax-parser-header-dir="${SWIFT_SOURCE_DIR}/include/swift-c/SyntaxParser" - --syntax-parser-lib-dir="$(build_directory ${host} swift)/lib" - --swift-syntax-test-exec="$(build_directory_bin ${LOCAL_HOST} swift)/swift-syntax-test" - --filecheck-exec="$(build_directory_bin ${LOCAL_HOST} llvm)/FileCheck") -} - -function set_stresstester_build_script_helper_command() { - local package_name="$1" - local package_build_type="$2" - local package_build_dir="$3" - - if [ "${SKIP_BUILD_SWIFTSYNTAX}" ]; then - echo "Error: Cannot build $package_name without SwiftSyntax." - exit 1 - fi - - local swiftsyntax_config="debug" - if [[ $(is_cmake_release_build_type "${SWIFTSYNTAX_BUILD_TYPE}") ]] ; then - swiftsyntax_config="release" - fi - local config="debug" - if [[ $(is_cmake_release_build_type "${package_build_type}") ]] ; then - config="release" - fi - - stresstester_build_script_helper_command=("${STRESSTEST_PACKAGE_DIR}/build-script-helper.py") - if [[ "${VERBOSE_BUILD}" ]] ; then - stresstester_build_script_helper_command+=(-v) - fi - - stresstester_build_script_helper_command+=( - --package-dir="${package_name}" - --build-dir="${package_build_dir}" - --swiftc-exec="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" - --swift-build-exec="$(swiftpm_find_tool swift-build)" - --swift-test-exec="$(swiftpm_find_tool swift-test)" - --syntax-parser-header-dir="${SWIFT_SOURCE_DIR}/include/swift-c/SyntaxParser" - --syntax-parser-lib-dir="$(build_directory ${host} swift)/lib" - --sourcekitd-dir="$(build_directory ${host} swift)/lib" - --swiftsyntax-dir="$(build_directory ${host} swiftsyntax)/${swiftsyntax_config}" - --config="${config}") -} - -function set_skstresstester_build_command() { - set_stresstester_build_script_helper_command SourceKitStressTester "${SKSTRESSTESTER_BUILD_TYPE}" "$(build_directory ${host} skstresstester)" - skstresstester_build_command=("${stresstester_build_script_helper_command[@]}") -} - -function set_swiftevolve_build_command() { - set_stresstester_build_script_helper_command SwiftEvolve "${SWIFTEVOLVE_BUILD_TYPE}" "$(build_directory ${host} swiftevolve)" - swiftevolve_build_command=("${stresstester_build_script_helper_command[@]}") -} - # # Configure and build each product # @@ -2069,17 +1403,8 @@ for host in "${ALL_HOSTS[@]}"; do fi for product in "${PRODUCTS[@]}"; do - # Check if we should perform this action. - tmp_product=${product} - if [[ ${tmp_product} == "libdispatch_static" ]]; then - tmp_product=libdispatch - LIBDISPATCH_STATIC_CMAKE_OPTIONS=${LIBDISPATCH_CMAKE_OPTIONS[@]} - fi - if ! [[ $(should_execute_action "${host}-${tmp_product}-build") ]]; then - continue - fi + [[ $(should_execute_action "${host}-${product/_static}-build") ]] || continue - unset skip_build source_dir_var="$(toupper ${product})_SOURCE_DIR" source_dir=${!source_dir_var} build_dir=$(build_directory ${host} ${product}) @@ -2091,7 +1416,7 @@ for host in "${ALL_HOSTS[@]}"; do module_cache="${build_dir}/module-cache" # Add any specific cmake options specified by build-script - product_cmake_options_name=$(to_varname "${product}")_CMAKE_OPTIONS + product_cmake_options_name=$(to_varname "${product/_static}")_CMAKE_OPTIONS product_cmake_options=(${!product_cmake_options_name}) # convert to array cmake_options+=("${product_cmake_options[@]}") @@ -2102,7 +1427,6 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_BUILD_TYPE:STRING="${CMARK_BUILD_TYPE}" "${cmark_cmake_options[@]}" ) - skip_build=${SKIP_BUILD_CMARK} build_targets=(all) ;; @@ -2110,7 +1434,7 @@ for host in "${ALL_HOSTS[@]}"; do if [ "${BUILD_LLVM}" == "0" ] ; then build_targets=(clean) fi - if [ "${SKIP_BUILD_LLVM}" ] ; then + if [ "${SKIP_BUILD}" ] ; then # We can't skip the build completely because the standalone # build of Swift depend on these for building and testing. build_targets=(llvm-tblgen clang-resource-headers intrinsics_gen clang-tablegen-targets) @@ -2154,12 +1478,24 @@ for host in "${ALL_HOSTS[@]}"; do "${llvm_cmake_options[@]}" ) - if [[ ! -z "${CLANG_TOOLS_EXTRA_SOURCE_DIR}" ]] ; then - cmake_options+=( - -DLLVM_EXTERNAL_CLANG_TOOLS_EXTRA_SOURCE_DIR="${CLANG_TOOLS_EXTRA_SOURCE_DIR}" - ) + llvm_enable_projects=("clang") + + if [[ ! "${SKIP_BUILD_COMPILER_RT}" && ! $(is_cross_tools_host ${host}) ]]; then + llvm_enable_projects+=("compiler-rt") + fi + + if [[ ! "${SKIP_BUILD_CLANG_TOOLS_EXTRA}" ]]; then + llvm_enable_projects+=("clang-tools-extra") fi + cmake_options+=( + -DLLVM_ENABLE_PROJECTS="$(join ";" ${llvm_enable_projects[@]})" + ) + + cmake_options+=( + -DLLVM_TOOL_LLD_BUILD:BOOL=TRUE + ) + if [[ "${BUILD_TOOLCHAIN_ONLY}" ]]; then cmake_options+=( -DLLVM_BUILD_TOOLS=NO @@ -2339,23 +1675,6 @@ for host in "${ALL_HOSTS[@]}"; do "${swift_cmake_options[@]}" ) - if [[ "${BUILD_TOOLCHAIN_ONLY}" ]]; then - cmake_options+=( - -DSWIFT_TOOL_SIL_OPT_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_IDE_TEST_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_REMOTEAST_TEST_BUILD=FALSE - -DSWIFT_TOOL_LLDB_MODULEIMPORT_TEST_BUILD=FALSE - -DSWIFT_TOOL_SIL_EXTRACT_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_LLVM_OPT_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_SDK_ANALYZER_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_SDK_DIGESTER_BUILD=FALSE - -DSWIFT_TOOL_SOURCEKITD_TEST_BUILD=FALSE - -DSWIFT_TOOL_SOURCEKITD_REPL_BUILD=FALSE - -DSWIFT_TOOL_COMPLETE_TEST_BUILD=FALSE - -DSWIFT_TOOL_SWIFT_REFLECTION_DUMP_BUILD=FALSE - ) - fi - cmake_options=( "${cmake_options[@]}" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" @@ -2380,18 +1699,6 @@ for host in "${ALL_HOSTS[@]}"; do ) fi - if [[ "${CMAKE_GENERATOR}" == "Xcode" ]] ; then - cmake_options=( - "${cmake_options[@]}" - -DSWIFT_CMARK_LIBRARY_DIR:PATH=$(build_directory ${host} cmark)/src/${CMARK_BUILD_TYPE} - ) - else - cmake_options=( - "${cmake_options[@]}" - -DSWIFT_CMARK_LIBRARY_DIR:PATH=$(build_directory ${host} cmark)/src - ) - fi - if [[ "${SWIFT_SDKS}" ]] ; then cmake_options=( "${cmake_options[@]}" @@ -2413,7 +1720,7 @@ for host in "${ALL_HOSTS[@]}"; do ) fi - if contains_product "lldb" ; then + if [[ ! "${SKIP_BUILD_LLDB}" ]] ; then lldb_build_dir=$(build_directory ${host} lldb) cmake_options=( "${cmake_options[@]}" @@ -2432,16 +1739,6 @@ for host in "${ALL_HOSTS[@]}"; do build_targets=("${build_targets[@]}" "${SWIFT_BENCHMARK_TARGETS[@]}") fi - if [ "${BUILD_LIBPARSER_ONLY}" ]; then - build_targets=(libSwiftSyntaxParser) - if [ "${LIBPARSER_VER}" ] ; then - cmake_options=( - "${cmake_options[@]}" - -DSWIFT_LIBPARSER_VER:STRING="${LIBPARSER_VER}" - ) - fi - fi - skip_build=${SKIP_BUILD_SWIFT} ;; lldb) if [ ! -d "${LLDB_SOURCE_DIR}" ]; then @@ -2492,12 +1789,14 @@ for host in "${ALL_HOSTS[@]}"; do DOTEST_EXTRA="${DOTEST_EXTRA} -Xcc -F${FOUNDATION_BUILD_DIR}" DOTEST_EXTRA="${DOTEST_EXTRA} -I${FOUNDATION_BUILD_DIR}/swift" DOTEST_EXTRA="${DOTEST_EXTRA} -I${LIBDISPATCH_SOURCE_DIR}" - DOTEST_EXTRA="${DOTEST_EXTRA} -L${FOUNDATION_BUILD_DIR}" DOTEST_EXTRA="${DOTEST_EXTRA} -L${LIBDISPATCH_BUILD_DIR}" DOTEST_EXTRA="${DOTEST_EXTRA} -L${LIBDISPATCH_BUILD_DIR}/src" - DOTEST_EXTRA="${DOTEST_EXTRA} -Xlinker -rpath -Xlinker ${LIBDISPATCH_BUILD_DIR}/src" + DOTEST_EXTRA="${DOTEST_EXTRA} -L${FOUNDATION_BUILD_DIR}" + DOTEST_EXTRA="${DOTEST_EXTRA} -L${FOUNDATION_BUILD_DIR}/Foundation" DOTEST_EXTRA="${DOTEST_EXTRA} -Xlinker -rpath -Xlinker ${LIBDISPATCH_BUILD_DIR}" + DOTEST_EXTRA="${DOTEST_EXTRA} -Xlinker -rpath -Xlinker ${LIBDISPATCH_BUILD_DIR}/src" DOTEST_EXTRA="${DOTEST_EXTRA} -Xlinker -rpath -Xlinker ${FOUNDATION_BUILD_DIR}" + DOTEST_EXTRA="${DOTEST_EXTRA} -Xlinker -rpath -Xlinker ${FOUNDATION_BUILD_DIR}/Foundation" fi # Watchpoint testing is currently disabled: see rdar://38566150. @@ -2519,6 +1818,8 @@ for host in "${ALL_HOSTS[@]}"; do cmake_options=( "${cmake_options[@]}" -C${LLDB_SOURCE_DIR}/cmake/caches/${cmake_cache} + -DCMAKE_C_FLAGS="$(llvm_c_flags ${host})" + -DCMAKE_CXX_FLAGS="$(llvm_c_flags ${host})" -DCMAKE_BUILD_TYPE:STRING="${LLDB_BUILD_TYPE}" -DLLDB_SWIFTC:PATH="$(build_directory ${LOCAL_HOST} swift)/bin/swiftc" -DLLDB_SWIFT_LIBS:PATH="$(build_directory ${LOCAL_HOST} swift)/lib/swift" @@ -2530,7 +1831,6 @@ for host in "${ALL_HOSTS[@]}"; do -DLLDB_PATH_TO_SWIFT_SOURCE:PATH="${SWIFT_SOURCE_DIR}" -DLLDB_IS_BUILDBOT_BUILD:BOOL="${LLDB_IS_BUILDBOT_BUILD}" -DLLDB_BUILD_DATE:STRING="\"${LLDB_BUILD_DATE}\"" - -DLLDB_ALLOW_STATIC_BINDINGS:BOOL=1 -DLLDB_INCLUDE_TESTS:BOOL=$(false_true ${BUILD_TOOLCHAIN_ONLY}) -DLLDB_TEST_USER_ARGS="${DOTEST_ARGS}" ) @@ -2553,53 +1853,38 @@ for host in "${ALL_HOSTS[@]}"; do llbuild) cmake_options=( "${cmake_options[@]}" + + -DCMAKE_BUILD_TYPE:STRING="${LLBUILD_BUILD_TYPE}" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" + -DCMAKE_Swift_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" + + -DLLBUILD_ENABLE_ASSERTIONS:BOOL=$(true_false "${LLBUILD_ENABLE_ASSERTIONS}") + -DLLBUILD_SUPPORT_BINDINGS:=Swift + -DLIT_EXECUTABLE:PATH="${LLVM_SOURCE_DIR}/utils/lit/lit.py" -DFILECHECK_EXECUTABLE:PATH="$(build_directory_bin ${LOCAL_HOST} llvm)/FileCheck" - -DCMAKE_BUILD_TYPE:STRING="${LLBUILD_BUILD_TYPE}" - -DLLBUILD_ENABLE_ASSERTIONS:BOOL=$(true_false "${LLBUILD_ENABLE_ASSERTIONS}") -DSWIFTC_EXECUTABLE:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" -DFOUNDATION_BUILD_DIR:PATH="$(build_directory ${host} foundation)" -DLIBDISPATCH_BUILD_DIR:PATH="$(build_directory ${host} libdispatch)" -DLIBDISPATCH_SOURCE_DIR:PATH="${LIBDISPATCH_SOURCE_DIR}" - -DLLBUILD_SUPPORT_BINDINGS:=Swift - ) - ;; - swiftpm) - set_swiftpm_bootstrap_command - call "${swiftpm_bootstrap_command[@]}" - # swiftpm installs itself with a bootstrap method. No further cmake building is performed. - continue - ;; - swiftsyntax) - if [[ "${SKIP_SWIFTSYNTAX_SWIFTSIDE}" ]]; then - continue - fi - set_swiftsyntax_build_command - call "${swiftsyntax_build_command[@]}" + -Ddispatch_DIR:PATH=$(build_directory ${host} libdispatch)/cmake/modules + -DFoundation_DIR:PATH=$(build_directory ${host} foundation)/cmake/modules + ) - continue - ;; - skstresstester) - if [[ "$(uname -s)" != "Darwin" ]]; then - echo "error: unable to build swift-stress-tester on this platform" - continue - fi - set_skstresstester_build_command - call "${skstresstester_build_command[@]}" + # Ensure on Darwin platforms that we consider only the SQLite headers + # from the SDK instead of picking ones found elsewhere + # (e.g. in /usr/include ) + # Also consider only the SQLite dylib shipped with the OS + # to avoid mismatch with the headers + if [[ "$(uname -s)" = "Darwin" ]]; then + cmake_options=( + "${cmake_options[@]}" - continue - ;; - swiftevolve) - if [[ "$(uname -s)" != "Darwin" ]]; then - echo "error: unable to build swift-evolve on this platform" - continue + -DSQLite3_INCLUDE_DIR:PATH="$(xcrun -sdk macosx -show-sdk-path)/usr/include" + -DSQLite3_LIBRARY:PATH="/usr/lib/libsqlite3.dylib" + ) fi - set_swiftevolve_build_command - call "${swiftevolve_build_command[@]}" - - continue ;; xctest) SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" @@ -2647,15 +1932,18 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_BUILD_TYPE:STRING="${XCTEST_BUILD_TYPE}" -DCMAKE_C_COMPILER:PATH="${LLVM_BIN}/clang" -DCMAKE_CXX_COMPILER:PATH="${LLVM_BIN}/clang++" - -DCMAKE_SWIFT_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" + -DCMAKE_Swift_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" -DCMAKE_INSTALL_LIBDIR:PATH="lib" + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules + -DFoundation_DIR=$(build_directory ${host} foundation)/cmake/modules + -DLLVM_DIR=$(build_directory ${host} llvm)/lib/cmake/llvm + -DXCTEST_PATH_TO_LIBDISPATCH_SOURCE:PATH=${LIBDISPATCH_SOURCE_DIR} -DXCTEST_PATH_TO_LIBDISPATCH_BUILD:PATH=$(build_directory ${host} libdispatch) - -DXCTEST_PATH_TO_FOUNDATION_BUILD:PATH=${FOUNDATION_BUILD_DIR} - + -DCMAKE_SWIFT_COMPILER:PATH="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" -DCMAKE_PREFIX_PATH:PATH=$(build_directory ${host} llvm) -DENABLE_TESTING=YES @@ -2697,18 +1985,6 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - # Staging: require opt-in for building with dispatch - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - -Ddispatch_DIR=${LIBDISPATCH_BUILD_DIR}/cmake/modules - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi - # FIXME: Always re-build XCTest on non-darwin platforms. # The Swift project might have been changed, but CMake might # not be aware and will not rebuild. @@ -2731,7 +2007,10 @@ for host in "${ALL_HOSTS[@]}"; do -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} + + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules # NOTE(compnerd) we disable tests because XCTest is not ready # yet, but we will reconfigure when the time comes. @@ -2926,7 +2205,12 @@ for host in "${ALL_HOSTS[@]}"; do fi # Build. - if [[ ! "${skip_build}" ]]; then + # + # Even if builds are skipped, Swift configuration relies on + # some LLVM tools like TableGen. In the LLVM configure rules + # above, a small subset of LLVM build_targets are selected + # when SKIP_BUILD is set. + if [[ $(not ${SKIP_BUILD}) || "${product}" == "llvm" ]]; then if [[ "${CMAKE_GENERATOR}" == "Xcode" ]] ; then # Xcode generator uses "ALL_BUILD" instead of "all". # Also, xcodebuild uses -target instead of bare names. @@ -2939,35 +2223,35 @@ for host in "${ALL_HOSTS[@]}"; do fi call "${CMAKE_BUILD[@]}" "${build_dir}" $(cmake_config_opt ${product}) -- "${BUILD_ARGS[@]}" ${build_targets[@]} + fi - # When we are building LLVM copy over the compiler-rt - # builtins for iOS/tvOS/watchOS to ensure that Swift's - # stdlib can use compiler-rt builtins when targetting iOS/tvOS/watchOS. - if [[ "${product}" = "llvm" ]] && [[ "${BUILD_LLVM}" = "1" ]] && [[ "$(uname -s)" = "Darwin" ]]; then - HOST_CXX_DIR=$(dirname "${HOST_CXX}") - HOST_LIB_CLANG_DIR="${HOST_CXX_DIR}/../lib/clang" - DEST_LIB_CLANG_DIR="$(build_directory_bin ${host} llvm)/../lib/clang" - - if [[ -d "${HOST_LIB_CLANG_DIR}" ]] && [[ -d "${DEST_LIB_CLANG_DIR}" ]]; then - DEST_CXX_BUILTINS_VERSION=$(ls "${DEST_LIB_CLANG_DIR}" | awk '{print $0}') - DEST_BUILTINS_DIR="$(build_directory_bin ${host} llvm)/../lib/clang/$DEST_CXX_BUILTINS_VERSION/lib/darwin" - - if [[ -d "${DEST_BUILTINS_DIR}" ]]; then - for HOST_CXX_BUILTINS_PATH in "${HOST_LIB_CLANG_DIR}"/*; do - HOST_CXX_BUILTINS_DIR="${HOST_CXX_BUILTINS_PATH}/lib/darwin" - echo "copying compiler-rt embedded builtins from ${HOST_CXX_BUILTINS_DIR} into the local clang build directory ${DEST_BUILTINS_DIR}." - - for OS in ios watchos tvos; do - LIB_NAME="libclang_rt.$OS.a" - HOST_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$LIB_NAME" - if [[ -f "${HOST_LIB_PATH}" ]]; then - call cp "${HOST_LIB_PATH}" "${DEST_BUILTINS_DIR}/${LIB_NAME}" - elif [[ "${VERBOSE_BUILD}" ]]; then - echo "no file exists at ${HOST_LIB_PATH}" - fi - done + # When we are building LLVM copy over the compiler-rt + # builtins for iOS/tvOS/watchOS to ensure that Swift's + # stdlib can use compiler-rt builtins when targetting iOS/tvOS/watchOS. + if [[ "${product}" = "llvm" ]] && [[ "${BUILD_LLVM}" = "1" ]] && [[ "$(uname -s)" = "Darwin" ]]; then + HOST_CXX_DIR=$(dirname "${HOST_CXX}") + HOST_LIB_CLANG_DIR="${HOST_CXX_DIR}/../lib/clang" + DEST_LIB_CLANG_DIR="$(build_directory_bin ${host} llvm)/../lib/clang" + + if [[ -d "${HOST_LIB_CLANG_DIR}" ]] && [[ -d "${DEST_LIB_CLANG_DIR}" ]]; then + DEST_CXX_BUILTINS_VERSION=$(ls "${DEST_LIB_CLANG_DIR}" | awk '{print $0}') + DEST_BUILTINS_DIR="$(build_directory_bin ${host} llvm)/../lib/clang/$DEST_CXX_BUILTINS_VERSION/lib/darwin" + + if [[ -d "${DEST_BUILTINS_DIR}" ]]; then + for HOST_CXX_BUILTINS_PATH in "${HOST_LIB_CLANG_DIR}"/*; do + HOST_CXX_BUILTINS_DIR="${HOST_CXX_BUILTINS_PATH}/lib/darwin" + echo "copying compiler-rt embedded builtins from ${HOST_CXX_BUILTINS_DIR} into the local clang build directory ${DEST_BUILTINS_DIR}." + + for OS in ios watchos tvos; do + LIB_NAME="libclang_rt.$OS.a" + HOST_LIB_PATH="$HOST_CXX_BUILTINS_DIR/$LIB_NAME" + if [[ -f "${HOST_LIB_PATH}" ]]; then + call cp "${HOST_LIB_PATH}" "${DEST_BUILTINS_DIR}/${LIB_NAME}" + elif [[ "${VERBOSE_BUILD}" ]]; then + echo "no file exists at ${HOST_LIB_PATH}" + fi done - fi + done fi fi fi @@ -3050,7 +2334,7 @@ for host in "${ALL_HOSTS[@]}"; do results_dir="${lldb_build_dir}/test-results" call mkdir -p "${results_dir}" - LLVM_LIT_ARG="${LLVM_LIT_ARGS} --xunit-xml-output=${results_dir}/results.xml" + LLVM_LIT_ARGS="${LLVM_LIT_ARGS} --xunit-xml-output=${results_dir}/results.xml" if [[ "${ENABLE_ASAN}" ]] ; then # Limit the number of parallel tests @@ -3093,44 +2377,6 @@ for host in "${ALL_HOSTS[@]}"; do results_targets=("test") executable_target="" ;; - swiftpm) - if [[ "${SKIP_TEST_SWIFTPM}" ]]; then - continue - fi - echo "--- Running tests for ${product} ---" - set_swiftpm_bootstrap_command - call "${swiftpm_bootstrap_command[@]}" test --test-parallel - # As swiftpm tests itself, we break early here. - continue - ;; - swiftsyntax) - if [[ "${SKIP_TEST_SWIFTSYNTAX}" ]]; then - continue - fi - echo "--- Running tests for ${product} ---" - set_swiftsyntax_build_command - call "${swiftsyntax_build_command[@]}" -t - # As swiftSyntax tests itself, we break early here. - continue - ;; - skstresstester) - if [[ "${SKIP_TEST_SKSTRESSTESTER}" ]]; then - continue - fi - echo "--- Running tests for ${product} ---" - set_skstresstester_build_command - call "${skstresstester_build_command[@]}" test - continue - ;; - swiftevolve) - if [[ "${SKIP_TEST_SWIFTEVOLVE}" ]]; then - continue - fi - echo "--- Running tests for ${product} ---" - set_swiftevolve_build_command - call "${swiftevolve_build_command[@]}" test - continue - ;; xctest) if [[ "${SKIP_TEST_XCTEST}" ]]; then continue @@ -3205,33 +2451,29 @@ for host in "${ALL_HOSTS[@]}"; do LIBICU_BUILD_ARGS=() fi - if [[ ! "${SKIP_BUILD_LIBDISPATCH}" ]] ; then - LIBDISPATCH_BUILD_DIR="$(build_directory ${host} libdispatch)" - LIBDISPATCH_BUILD_ARGS=( - -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} - -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=${LIBDISPATCH_BUILD_DIR} - ) - else - LIBDISPATCH_BUILD_ARGS=( -DFOUNDATION_ENABLE_LIBDISPATCH=NO ) - fi SWIFTC_BIN="$(build_directory_bin ${LOCAL_HOST} swift)/swiftc" LLVM_BIN="$(build_directory_bin ${LOCAL_HOST} llvm)" + # NOTE(compnerd) the time has come to enable tests now cmake_options=( ${cmake_options[@]} -DCMAKE_BUILD_TYPE:STRING=${FOUNDATION_BUILD_TYPE} -DCMAKE_C_COMPILER:PATH=${LLVM_BIN}/clang -DCMAKE_CXX_COMPILER:PATH=${LLVM_BIN}/clang++ - -DCMAKE_SWIFT_COMPILER:PATH=${SWIFTC_BIN} -DCMAKE_Swift_COMPILER:PATH=${SWIFTC_BIN} -DCMAKE_INSTALL_PREFIX:PATH=$(get_host_install_prefix ${host}) ${LIBICU_BUILD_ARGS[@]} - ${LIBDISPATCH_BUILD_ARGS[@]} - # NOTE(compnerd) the time has come to enable tests now + -DFOUNDATION_PATH_TO_LIBDISPATCH_SOURCE=${LIBDISPATCH_SOURCE_DIR} + -DFOUNDATION_PATH_TO_LIBDISPATCH_BUILD=$(build_directory ${host} libdispatch) + -Ddispatch_DIR=$(build_directory ${host} libdispatch)/cmake/modules + -DENABLE_TESTING:BOOL=YES + -DXCTest_DIR=$(build_directory ${host} xctest)/cmake/modules + + -DCMAKE_SWIFT_COMPILER:PATH=${SWIFTC_BIN} -DFOUNDATION_PATH_TO_XCTEST_BUILD:PATH=$(build_directory ${host} xctest) ) @@ -3355,6 +2597,16 @@ done LIPO_SRC_DIRS=() for host in "${ALL_HOSTS[@]}"; do + # Calculate the directory to install products in to. + host_install_destdir=$(get_host_install_destdir ${host}) + host_install_prefix=$(get_host_install_prefix ${host}) + + # Identify the destdirs to pass to lipo. Must happen even if the install action + # is to be skipped. + if [[ $(should_include_host_in_lipo ${host}) ]]; then + LIPO_SRC_DIRS+=( "${host_install_destdir}" ) + fi + # Skip this pass when the only action to execute can't match. if ! [[ $(should_execute_host_actions_for_phase ${host} install) ]]; then continue @@ -3365,25 +2617,14 @@ for host in "${ALL_HOSTS[@]}"; do continue fi - # Calculate the directory to install products in to. - host_install_destdir=$(get_host_install_destdir ${host}) - host_install_prefix=$(get_host_install_prefix ${host}) - - if [[ $(should_include_host_in_lipo ${host}) ]]; then - LIPO_SRC_DIRS+=( "${host_install_destdir}" ) - fi - # Set the build options for this host set_build_options_for_host $host for product in "${PRODUCTS[@]}"; do - # Check if we should perform this action. - tmp_product=${product} - if [[ ${tmp_product} == "libdispatch_static" ]]; then - tmp_product=libdispatch - fi - if ! [[ $(should_execute_action "${host}-${tmp_product}-install") ]]; then - continue + [[ $(should_execute_action "${host}-${product/_static}-install") ]] || continue + if [[ -z "${INSTALL_DESTDIR}" ]] ; then + echo "--install-destdir is required to install products." + exit 1 fi INSTALL_TARGETS="install" @@ -3415,11 +2656,6 @@ for host in "${ALL_HOSTS[@]}"; do continue fi INSTALL_TARGETS=install-swift-components - # Swift syntax parser is currently a sub-product of Swift; - # We need to specify the install target separately here. - if [ "${BUILD_LIBPARSER_ONLY}" ]; then - INSTALL_TARGETS=tools/libSwiftSyntaxParser/install - fi ;; llbuild) if [[ -z "${INSTALL_LLBUILD}" ]] ; then @@ -3432,110 +2668,15 @@ for host in "${ALL_HOSTS[@]}"; do if [[ -z "${INSTALL_LLDB}" ]] ; then continue fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - ;; - swiftpm) - if [[ -z "${INSTALL_SWIFTPM}" ]] ; then - continue - fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - - echo "--- Installing ${product} ---" - set_swiftpm_bootstrap_command - call "${swiftpm_bootstrap_command[@]}" --prefix="${host_install_destdir}${host_install_prefix}" install - # As swiftpm bootstraps the installation itself, we break early here. - continue - ;; - swiftsyntax) - if [[ -z "${INSTALL_SWIFTSYNTAX}" ]] ; then - continue - fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - echo "--- Installing ${product} ---" - if [ "${BUILD_LIBPARSER_ONLY}" ]; then - # We don't have a toolchain so we should install to the specified dir - DYLIB_DIR="${INSTALL_DESTDIR}" - MODULE_DIR="${INSTALL_DESTDIR}/${product}.swiftmodule" - # Create the install dir if it doesn't exist - call mkdir -p "${INSTALL_DESTDIR}" - # Install libParser is necessary - rsync -a "$(build_directory ${host} swift)/lib/lib_InternalSwiftSyntaxParser.dylib" "${INSTALL_DESTDIR}" - # Install module map of libParser so client can import SwiftSyntax - rsync -a "${SWIFT_SOURCE_DIR}/include/swift-c/SyntaxParser" "${INSTALL_DESTDIR}" - else - # We have a toolchain so install to the toolchain - DYLIB_DIR="${host_install_destdir}${host_install_prefix}/lib/swift/${SWIFT_HOST_VARIANT}" - MODULE_DIR="${DYLIB_DIR}/${product}.swiftmodule" - fi - if [[ "${SKIP_SWIFTSYNTAX_SWIFTSIDE}" ]]; then - continue - fi - set_swiftsyntax_build_command - if [[ -z "${SKIP_INSTALL_SWIFTSYNTAX_MODULE}" ]] ; then - mkdir -p "${MODULE_DIR}" - call "${swiftsyntax_build_command[@]}" --dylib-dir="${DYLIB_DIR}" --swiftmodule-base-name "${MODULE_DIR}/${SWIFT_HOST_VARIANT_ARCH}" --install - else - call "${swiftsyntax_build_command[@]}" --dylib-dir="${DYLIB_DIR}" --install - fi - - continue - ;; - skstresstester) - if [[ -z "${INSTALL_SKSTRESSTESTER}" ]] ; then - continue - fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - if [[ -z "${INSTALL_SWIFTSYNTAX}" ]] ; then - echo "--install-swiftsyntax is required to install the SourceKit stress tester" - exit 1 - fi - - echo "--- Installing ${product} ---" - set_skstresstester_build_command - call "${skstresstester_build_command[@]}" --prefix="${host_install_destdir}${host_install_prefix}" install - continue - ;; - swiftevolve) - if [[ -z "${INSTALL_SWIFTEVOLVE}" ]] ; then - continue - fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - if [[ -z "${INSTALL_SWIFTSYNTAX}" ]] ; then - echo "--install-swiftsyntax is required to install swift-evolve" - exit 1 - fi - - echo "--- Installing ${product} ---" - set_swiftevolve_build_command - call "${swiftevolve_build_command[@]}" --prefix="${host_install_destdir}${host_install_prefix}" install - continue + INSTALL_TARGETS="install-distribution" ;; xctest) if [[ -z "${INSTALL_XCTEST}" ]] ; then continue fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi case ${host} in - linux-*|freebsd-*|cygwin-*|haiku-*) ;; + linux-*|freebsd-*|cygwin-*|haiku-*|android-*) ;; *) echo "error: --install-xctest is not supported on this platform" exit 1 @@ -3554,11 +2695,6 @@ for host in "${ALL_HOSTS[@]}"; do continue fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - ;; libdispatch|libdispatch_static) if [[ -z "${INSTALL_LIBDISPATCH}" ]] ; then @@ -3592,10 +2728,6 @@ for host in "${ALL_HOSTS[@]}"; do if [[ -z "${INSTALL_LIBICU}" ]]; then continue fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi echo "--- Installing ${product} ---" ICU_BUILD_DIR=$(build_directory ${host} ${product}) ICU_INSTALL_DIR="$(get_host_install_destdir ${host})$(get_host_install_prefix ${host})" @@ -3633,10 +2765,6 @@ for host in "${ALL_HOSTS[@]}"; do if [[ -z "${INSTALL_PLAYGROUNDSUPPORT}" ]] ; then continue fi - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi echo "--- Installing ${product} ---" @@ -3672,11 +2800,6 @@ for host in "${ALL_HOSTS[@]}"; do ;; esac - if [[ -z "${INSTALL_DESTDIR}" ]] ; then - echo "--install-destdir is required to install products." - exit 1 - fi - echo "--- Installing ${product} ---" build_dir=$(build_directory ${host} ${product}) @@ -3872,15 +2995,10 @@ for host in "${ALL_HOSTS[@]}"; do done # Lipo those products which require it, optionally build and test an installable package. -if [[ ${#LIPO_SRC_DIRS[@]} -gt 0 ]]; then +mergedHost="merged-hosts" +if [[ ${#LIPO_SRC_DIRS[@]} -gt 0 ]] && [[ $(should_execute_action "${mergedHost}-lipo") ]]; then # This is from multiple hosts; Which host should we say it is? # Let's call it 'merged-hosts' so that we can identify it. - mergedHost="merged-hosts" - - # Check if we should perform this action. - if ! [[ $(should_execute_action "${mergedHost}-lipo") ]]; then - continue - fi echo "--- Merging and running lipo ---" diff --git a/utils/build-toolchain b/utils/build-toolchain index 4edcc957bc40e..f89f30f3faf1d 100755 --- a/utils/build-toolchain +++ b/utils/build-toolchain @@ -98,15 +98,15 @@ set -x YEAR=$(date +"%Y") MONTH=$(date +"%m") DAY=$(date +"%d") -TOOLCHAIN_VERSION="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" +TOOLCHAIN_VERSION="5.0.${YEAR}${MONTH}${DAY}" +TOOLCHAIN_NAME="swift-LOCAL-${YEAR}-${MONTH}-${DAY}-a" DARWIN_TOOLCHAIN_VERSION="0.0.${YEAR}${MONTH}${DAY}" -ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}.tar.gz" -SYM_ARCHIVE="${TOOLCHAIN_VERSION}-${OS_SUFFIX}-symbols.tar.gz" +ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}.tar.gz" +SYM_ARCHIVE="${TOOLCHAIN_NAME}-${OS_SUFFIX}-symbols.tar.gz" BUNDLE_PREFIX=${BUNDLE_PREFIX:?Please specify a bundle prefix} BUNDLE_IDENTIFIER="${BUNDLE_PREFIX}.${YEAR}${MONTH}${DAY}" DISPLAY_NAME_SHORT="Local Swift Development Snapshot" DISPLAY_NAME="${DISPLAY_NAME_SHORT} ${YEAR}-${MONTH}-${DAY}" -TOOLCHAIN_NAME="${TOOLCHAIN_VERSION}" SWIFT_INSTALLABLE_PACKAGE="${RESULT_DIR}/${ARCHIVE}" SWIFT_INSTALL_DIR="${RESULT_DIR}/swift-nightly-install" @@ -126,5 +126,5 @@ DISTCC_FLAG="${DISTCC_FLAG}" darwin_toolchain_display_name="${DISPLAY_NAME}" \ darwin_toolchain_display_name_short="${DISPLAY_NAME_SHORT}" \ darwin_toolchain_xctoolchain_name="${TOOLCHAIN_NAME}" \ - darwin_toolchain_version="${DARWIN_TOOLCHAIN_VERSION}" \ + darwin_toolchain_version="${TOOLCHAIN_VERSION}" \ darwin_toolchain_alias="Local" diff --git a/utils/build-windows.bat b/utils/build-windows.bat index df3fc73b1ff03..92752877fdeaf 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -28,7 +28,7 @@ setlocal enableextensions enabledelayedexpansion set icu_version_major=64 set icu_version_minor=2 set icu_version=%icu_version_major%_%icu_version_minor% -set icu_version_dotted=%icu_version_major%.%icu_version_minor% +set icu_version_dashed=%icu_version_major%-%icu_version_minor% set "exitOnError=|| (exit /b)" set current_directory=%~dp0 @@ -41,14 +41,14 @@ cd %source_root% set source_root=%CD% set full_build_root=%source_root%\build -set install_directory=%build_root%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr - mkdir %full_build_root% + :: Use the shortest path we can for the build directory, to avoid Windows :: path problems as much as we can. subst S: /d subst S: %full_build_root% %exitOnError% set build_root=S: +set install_directory=%build_root%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr call :clone_repositories %exitOnError% call :download_icu %exitOnError% @@ -65,8 +65,11 @@ call :build_swift %exitOnError% call :build_lldb %exitOnError% -path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin +call :build_libdispatch %exitOnError% + +path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin call :test_swift %exitOnError% +call :test_libdispatch %exitOnError% goto :end endlocal @@ -79,10 +82,13 @@ setlocal enableextensions enabledelayedexpansion git config --global core.autocrlf false git clone --depth 1 --single-branch https://github.com/apple/swift-cmark cmark %exitOnError% -git clone --depth 1 --single-branch https://github.com/apple/swift-clang clang %exitOnError% -git clone --depth 1 --single-branch https://github.com/apple/swift-llvm llvm %exitOnError% -git clone --depth 1 --single-branch https://github.com/apple/swift-lldb lldb %exitOnError% -git clone --depth 1 --single-branch https://github.com/apple/swift-compiler-rt compiler-rt %exitOnError% +git clone --depth 1 --single-branch --branch swift/master https://github.com/apple/llvm-project llvm-project %exitOnError% +mklink /D "%source_root%\clang" "%source_root%\llvm-project\clang" +mklink /D "%source_root%\llvm" "%source_root%\llvm-project\llvm" +mklink /D "%source_root%\lldb" "%source_root%\llvm-project\lldb" +mklink /D "%source_root%\compiler-rt" "%source_root%\llvm-project\compiler-rt" +mklink /D "%source_root%\libcxx" "%source_root%\llvm-project\libcxx" +mklink /D "%source_root%\clang-tools-extra" "%source_root%\llvm-project\clang-tools-extra" git clone --depth 1 --single-branch https://github.com/apple/swift-corelibs-libdispatch %exitOnError% goto :eof @@ -95,7 +101,7 @@ endlocal setlocal enableextensions enabledelayedexpansion set file_name=icu4c-%icu_version%-Win64-MSVC2017.zip -curl -L -O -z %file_name% "http://download.icu-project.org/files/icu4c/%icu_version_dotted%/%file_name%" %exitOnError% +curl -L -O "https://github.com/unicode-org/icu/releases/download/release-%icu_version_dashed%/%file_name%" %exitOnError% :: unzip warns about the paths in the zip using slashes, which raises the :: errorLevel to 1. We cannot use exitOnError, and have to ignore errors. unzip -o %file_name% -d "%source_root%\icu-%icu_version%" @@ -232,7 +238,7 @@ cmake "%source_root%\swift"^ -DSWIFT_BUILD_STATIC_SDK_OVERLAY:BOOL=NO^ -DLLVM_INSTALL_TOOLCHAIN_ONLY:BOOL=YES^ -DSWIFT_BUILD_SOURCEKIT:BOOL=YES^ - -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=NO^ + -DSWIFT_ENABLE_SOURCEKIT_TESTS:BOOL=YES^ -DSWIFT_INSTALL_COMPONENTS="autolink-driver;compiler;clang-resource-dir-symlink;stdlib;sdk-overlay;editor-integration;tools;sourcekit-inproc;swift-remote-mirror;swift-remote-mirror-headers"^ -DSWIFT_PARALLEL_LINK_JOBS=8^ -DPYTHON_EXECUTABLE:PATH=%PYTHON_HOME%\python.exe^ @@ -276,7 +282,7 @@ cmake "%source_root%\lldb"^ -DClang_DIR:PATH=%build_root%\llvm\lib\cmake\clang^ -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ -DLLVM_ENABLE_ASSERTIONS:BOOL=YES^ - -DLLDB_ALLOW_STATIC_BINDINGS:BOOL=YES^ + -DLLDB_USE_STATIC_BINDINGS:BOOL=YES^ -DPYTHON_HOME:PATH=%PYTHON_HOME%^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ @@ -304,16 +310,21 @@ cmake "%source_root%\swift-corelibs-libdispatch"^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=clang-cl^ -DCMAKE_CXX_COMPILER=clang-cl^ - -DCMAKE_SWIFT_COMPILER:PATH=%install_directory%\bin\swiftc.exe^ + -DCMAKE_Swift_COMPILER=swiftc^ -DSwift_DIR:PATH=%build_root%\swift\lib\cmake\swift^ -DCMAKE_INSTALL_PREFIX:PATH=%install_directory%^ - -DBUILD_SHARED_LIBS:BOOL=YES^ - -DENABLE_TESTING:BOOL=NO^ -DCMAKE_C_COMPILER_TARGET=x86_64-unknown-windows-msvc^ + -DCMAKE_CXX_COMPILER_TARGET=x86_64-unknown-windows-msvc^ -DENABLE_SWIFT:BOOL=YES^ - -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ - -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO %exitOnError% + -DENABLE_TESTING:BOOL=YES^ + -DCMAKE_C_FLAGS:STRING="${CMAKE_C_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ + -DCMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS} --target=x86_64-unknown-windows-msvc /GS- /Oy /Gw /Gy"^ + -DCMAKE_EXE_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ + -DCMAKE_SHARED_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ + -DCMAKE_Swift_COMPILER_TARGET:STRING=x86_64-unknown-windows-msvc^ + -DCMAKE_Swift_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\""^ + -DCMAKE_Swift_LINK_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\"" %exitOnError% + popd @@ -324,4 +335,14 @@ goto :eof endlocal +:test_libdispatch +:: Tests libdispatch C interface +setlocal enableextensions enabledelayedexpansion + +cmake --build "%build_root%\swift-corelibs-libdispatch" --target ExperimentalTest %exitOnError% + +goto :eof +endlocal + + :end diff --git a/utils/build_swift/README.md b/utils/build_swift/README.md index 232e9188e8c0a..a07ea4789f158 100644 --- a/utils/build_swift/README.md +++ b/utils/build_swift/README.md @@ -8,5 +8,5 @@ the Swift build-script. You may run the unit test suite using the command: ```sh -$ python -m unittest discover -s utils/build_swift/ -t utils/ +$ python utils/build_swift/run_tests.py ``` diff --git a/utils/build_swift/__init__.py b/utils/build_swift/__init__.py index fa71f9fece535..7c485c168e611 100644 --- a/utils/build_swift/__init__.py +++ b/utils/build_swift/__init__.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information diff --git a/utils/build_swift/tests/argparse/__init__.py b/utils/build_swift/build_swift/__init__.py similarity index 81% rename from utils/build_swift/tests/argparse/__init__.py rename to utils/build_swift/build_swift/__init__.py index fa71f9fece535..7c485c168e611 100644 --- a/utils/build_swift/tests/argparse/__init__.py +++ b/utils/build_swift/build_swift/__init__.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information diff --git a/utils/build_swift/argparse/__init__.py b/utils/build_swift/build_swift/argparse/__init__.py similarity index 85% rename from utils/build_swift/argparse/__init__.py rename to utils/build_swift/build_swift/argparse/__init__.py index c7f205cc961a3..299363efab37b 100644 --- a/utils/build_swift/argparse/__init__.py +++ b/utils/build_swift/build_swift/argparse/__init__.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -14,6 +14,7 @@ super-set of the argparse API and is meant to be used as a drop-in replacement. """ +from __future__ import absolute_import, unicode_literals from argparse import (ArgumentDefaultsHelpFormatter, ArgumentError, ArgumentTypeError, FileType, HelpFormatter, @@ -23,8 +24,8 @@ from .actions import Action, Nargs from .parser import ArgumentParser -from .types import (BoolType, ClangVersionType, CompilerVersion, PathType, - RegexType, ShellSplitType, SwiftVersionType) +from .types import (BoolType, ClangVersionType, PathType, RegexType, + ShellSplitType, SwiftVersionType) __all__ = [ @@ -39,7 +40,6 @@ 'RawDescriptionHelpFormatter', 'RawTextHelpFormatter', - 'CompilerVersion', 'BoolType', 'FileType', 'PathType', diff --git a/utils/build_swift/argparse/actions.py b/utils/build_swift/build_swift/argparse/actions.py similarity index 97% rename from utils/build_swift/argparse/actions.py rename to utils/build_swift/build_swift/argparse/actions.py index 8bf349888e494..58028eb5d9eff 100644 --- a/utils/build_swift/argparse/actions.py +++ b/utils/build_swift/build_swift/argparse/actions.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -13,9 +13,13 @@ """ +from __future__ import absolute_import, unicode_literals + import argparse import copy +import six + from .types import BoolType, PathType @@ -77,7 +81,7 @@ def __init__(self, if dests == argparse.SUPPRESS: dests = [] metavar = metavar or '' - elif isinstance(dests, str): + elif isinstance(dests, six.string_types): dests = [dests] metavar = metavar or dests[0].upper() @@ -134,7 +138,7 @@ def __init__(self, option_strings, join=None, **kwargs): **kwargs) def __call__(self, parser, namespace, values, option_string=None): - if isinstance(values, str): + if isinstance(values, six.string_types): values = [values] for dest in self.dests: @@ -339,5 +343,5 @@ def __call__(self, parser, namespace, values, option_string=None): if self.message is not None: parser.error(self.message) - arg = option_string or str(values) + arg = option_string or six.text_type(values) parser.error('unsupported argument: {}'.format(arg)) diff --git a/utils/build_swift/argparse/parser.py b/utils/build_swift/build_swift/argparse/parser.py similarity index 97% rename from utils/build_swift/argparse/parser.py rename to utils/build_swift/build_swift/argparse/parser.py index c915e25b0d806..eb80a96744259 100644 --- a/utils/build_swift/argparse/parser.py +++ b/utils/build_swift/build_swift/argparse/parser.py @@ -14,9 +14,13 @@ """ +from __future__ import absolute_import, unicode_literals + import argparse from contextlib import contextmanager +import six + from . import Namespace, SUPPRESS, actions from .actions import Action @@ -129,7 +133,7 @@ def thunk(**kwargs): *names, action=action, **kwargs) def add_positional(self, dests, action=None, **kwargs): - if isinstance(dests, str): + if isinstance(dests, six.string_types): dests = [dests] if any(dest.startswith('-') for dest in dests): @@ -141,7 +145,7 @@ def add_positional(self, dests, action=None, **kwargs): return self._add_argument(dests, action, **kwargs) def add_option(self, option_strings, *actions, **kwargs): - if isinstance(option_strings, str): + if isinstance(option_strings, six.string_types): option_strings = [option_strings] if not all(opt.startswith('-') for opt in option_strings): diff --git a/utils/build_swift/argparse/types.py b/utils/build_swift/build_swift/argparse/types.py similarity index 79% rename from utils/build_swift/argparse/types.py rename to utils/build_swift/build_swift/argparse/types.py index 7686c0d5093d3..97618180f5dec 100644 --- a/utils/build_swift/argparse/types.py +++ b/utils/build_swift/build_swift/argparse/types.py @@ -13,16 +13,19 @@ """ +from __future__ import absolute_import, unicode_literals + import os.path import re import shlex +import six + from . import ArgumentTypeError +from ..versions import Version __all__ = [ - 'CompilerVersion', - 'BoolType', 'PathType', 'RegexType', @@ -32,31 +35,6 @@ ] -# ----------------------------------------------------------------------------- - -class CompilerVersion(object): - """Wrapper type around compiler version strings. - """ - - def __init__(self, *components): - if len(components) == 1: - if isinstance(components[0], str): - components = components[0].split('.') - elif isinstance(components[0], (list, tuple)): - components = components[0] - - if len(components) == 0: - raise ValueError('compiler version cannot be empty') - - self.components = tuple(int(part) for part in components) - - def __eq__(self, other): - return self.components == other.components - - def __str__(self): - return '.'.join([str(part) for part in self.components]) - - # ----------------------------------------------------------------------------- def _repr(cls, args): @@ -64,7 +42,7 @@ def _repr(cls, args): """ _args = [] - for key, value in args.viewitems(): + for key, value in six.iteritems(args): _args.append('{}={}'.format(key, repr(value))) return '{}({})'.format(type(cls).__name__, ', '.join(_args)) @@ -171,10 +149,8 @@ def __init__(self): ClangVersionType.ERROR_MESSAGE) def __call__(self, value): - matches = super(ClangVersionType, self).__call__(value) - components = filter(lambda x: x is not None, matches.group(1, 2, 3, 5)) - - return CompilerVersion(components) + super(ClangVersionType, self).__call__(value) + return Version(value) class SwiftVersionType(RegexType): @@ -182,8 +158,8 @@ class SwiftVersionType(RegexType): """ ERROR_MESSAGE = ('Invalid version value, must be "MAJOR.MINOR" ' - 'or "MAJOR.MINOR.PATCH"') - VERSION_REGEX = r'^(\d+)\.(\d+)(\.(\d+))?$' + ', "MAJOR.MINOR.PATCH" or "MAJOR.MINOR.PATCH.PATCH"') + VERSION_REGEX = r'^(\d+)\.(\d+)(\.(\d+))?(\.(\d+))?$' def __init__(self): super(SwiftVersionType, self).__init__( @@ -191,10 +167,8 @@ def __init__(self): SwiftVersionType.ERROR_MESSAGE) def __call__(self, value): - matches = super(SwiftVersionType, self).__call__(value) - components = filter(lambda x: x is not None, matches.group(1, 2, 4)) - - return CompilerVersion(components) + super(SwiftVersionType, self).__call__(value) + return Version(value) class ShellSplitType(object): diff --git a/utils/build_swift/build_swift/cache_utils.py b/utils/build_swift/build_swift/cache_utils.py new file mode 100644 index 0000000000000..ede762d7d2c78 --- /dev/null +++ b/utils/build_swift/build_swift/cache_utils.py @@ -0,0 +1,69 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Cache related utitlity functions and decorators. +""" + + +from __future__ import absolute_import, unicode_literals + +import functools + + +__all__ = [ + 'cache', + 'reify', +] + + +def cache(func): + """Decorator that caches result of a function call. + + NOTE: This decorator does not play nice with methods as the created cache + is not instance-local, rather it lives in the decorator. + NOTE: When running in Python 3.2 or newer this decorator is replaced with + the standard `functools.lru_cache` using a maxsize of None. + """ + + # Use the standard functools.lru_cache decorator for Python 3.2 and newer. + if hasattr(functools, 'lru_cache'): + return functools.lru_cache(maxsize=None)(func) + + # Otherwise use a naive caching strategy. + _cache = {} + + @functools.wraps(func) + def wrapper(*args, **kwargs): + key = tuple(args) + tuple(kwargs.items()) + + if key not in _cache: + result = func(*args, **kwargs) + _cache[key] = result + return result + + return _cache[key] + return wrapper + + +def reify(func): + """Decorator that replaces the wrapped method with the result after the + first call. Used to wrap property-like methods with no arguments. + """ + + class wrapper(object): + def __get__(self, obj, type=None): + if obj is None: + return self + + result = func(obj) + setattr(obj, func.__name__, result) + return result + + return functools.update_wrapper(wrapper(), func) diff --git a/utils/build_swift/build_swift/class_utils.py b/utils/build_swift/build_swift/class_utils.py new file mode 100644 index 0000000000000..411e57cdefe3c --- /dev/null +++ b/utils/build_swift/build_swift/class_utils.py @@ -0,0 +1,40 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Class utility functions and decorators. +""" + + +from __future__ import absolute_import, unicode_literals + + +__all__ = [ + 'generate_repr', +] + + +def generate_repr(*attrs): + """Generates a standardized __repr__ implementation for the decorated class + using the provided attributes and the class name. + """ + + def _repr(self): + args = [] + for attr in attrs: + value = getattr(self, attr) + args.append('{}={}'.format(attr, repr(value))) + + return '{}({})'.format(type(self).__name__, ', '.join(args)) + + def decorator(cls): + setattr(cls, '__repr__', _repr) + return cls + + return decorator diff --git a/utils/build_swift/defaults.py b/utils/build_swift/build_swift/defaults.py similarity index 86% rename from utils/build_swift/defaults.py rename to utils/build_swift/build_swift/defaults.py index ffc07325aceee..a6c66ff7e035d 100644 --- a/utils/build_swift/defaults.py +++ b/utils/build_swift/build_swift/defaults.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See http://swift.org/LICENSE.txt for license information @@ -12,7 +12,9 @@ """ -from .argparse import CompilerVersion +from __future__ import absolute_import, unicode_literals + +from .versions import Version __all__ = [ @@ -40,8 +42,8 @@ CMAKE_GENERATOR = 'Ninja' COMPILER_VENDOR = 'none' -SWIFT_USER_VISIBLE_VERSION = CompilerVersion('5.1.1') -CLANG_USER_VISIBLE_VERSION = CompilerVersion('7.0.0') +SWIFT_USER_VISIBLE_VERSION = Version('5.2') +CLANG_USER_VISIBLE_VERSION = Version('7.0.0') SWIFT_ANALYZE_CODE_COVERAGE = 'false' DARWIN_XCRUN_TOOLCHAIN = 'default' diff --git a/utils/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py similarity index 92% rename from utils/build_swift/driver_arguments.py rename to utils/build_swift/build_swift/driver_arguments.py index 43d15082561dd..ed569ad8a8473 100644 --- a/utils/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -1,13 +1,16 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +from __future__ import absolute_import, unicode_literals + import multiprocessing +import os import android.adb.commands @@ -128,28 +131,6 @@ def _apply_default_arguments(args): raise ValueError('error: --watchos-all is unavailable in open-source ' 'Swift.\nUse --watchos to skip watchOS device tests.') - # Propagate global --skip-build - if args.skip_build: - args.build_linux = False - args.build_freebsd = False - args.build_cygwin = False - args.build_osx = False - args.build_ios = False - args.build_tvos = False - args.build_watchos = False - args.build_android = False - args.build_benchmarks = False - args.build_external_benchmarks = False - args.build_lldb = False - args.build_llbuild = False - args.build_libcxx = False - args.build_swiftpm = False - args.build_xctest = False - args.build_foundation = False - args.build_libdispatch = False - args.build_libicu = False - args.build_playgroundsupport = False - # --skip-{ios,tvos,watchos} or --skip-build-{ios,tvos,watchos} are # merely shorthands for --skip-build-{**os}-{device,simulator} if not args.ios or not args.build_ios: @@ -203,8 +184,13 @@ def _apply_default_arguments(args): args.test_tvos = False args.test_watchos = False args.test_android = False + args.test_swiftpm = False + args.test_swiftsyntax = False args.test_indexstoredb = False args.test_sourcekitlsp = False + args.test_skstresstester = False + args.test_swiftevolve = False + args.test_toolchainbenchmarks = False # --skip-test-ios is merely a shorthand for host and simulator tests. if not args.test_ios: @@ -327,6 +313,13 @@ def create_argument_parser(): option('--skip-watchos', store_false('watchos'), help='set to skip everything watchOS-related') + option('--maccatalyst', toggle_true, + help='Enable building Swift with macCatalyst support') + + option('--maccatalyst-ios-tests', toggle_true, + help='When building for macCatalyst run tests with iOS-like ' + 'target triple') + option('--android', toggle_true, help='also build for Android') @@ -375,14 +368,17 @@ def create_argument_parser(): help='the absolute path to CXX, the "clang++" compiler for the ' 'host platform. Default is auto detected.') option('--cmake-c-launcher', store_path(executable=True), + default=os.environ.get('C_COMPILER_LAUNCHER', None), help='the absolute path to set CMAKE_C_COMPILER_LAUNCHER') option('--cmake-cxx-launcher', store_path(executable=True), + default=os.environ.get('CXX_COMPILER_LAUNCHER', None), help='the absolute path to set CMAKE_CXX_COMPILER_LAUNCHER') option('--host-lipo', store_path(executable=True), help='the absolute path to lipo. Default is auto detected.') option('--host-libtool', store_path(executable=True), help='the absolute path to libtool. Default is auto detected.') option('--distcc', toggle_true, + default=os.environ.get('USE_DISTCC') == '1', help='use distcc in pump mode') option('--enable-asan', toggle_true, help='enable Address Sanitizer') @@ -517,8 +513,10 @@ def create_argument_parser(): option('--stdlib-deployment-targets', store, type=argparse.ShellSplitType(), default=None, - help='list of targets to compile or cross-compile the Swift ' - 'standard library for. %(default)s by default.') + help='The targets to compile or cross-compile the Swift standard ' + 'library for. %(default)s by default.' + ' Comma separated list: {}'.format( + ' '.join(StdlibDeploymentTarget.get_target_names()))) option('--build-stdlib-deployment-targets', store, type=argparse.ShellSplitType(), @@ -550,9 +548,12 @@ def create_argument_parser(): option(['--libcxx'], store_true('build_libcxx'), help='build libcxx') - option(['-p', '--swiftpm'], store_true('build_swiftpm'), + option(['-p', '--swiftpm'], toggle_true('build_swiftpm'), help='build swiftpm') + option(['--install-swiftpm'], toggle_true('install_swiftpm'), + help='install swiftpm') + option(['--swiftsyntax'], store_true('build_swiftsyntax'), help='build swiftSyntax') @@ -566,8 +567,20 @@ def create_argument_parser(): help='build IndexStoreDB') option(['--sourcekit-lsp'], toggle_true('build_sourcekitlsp'), help='build SourceKitLSP') + option('--install-swiftsyntax', toggle_true('install_swiftsyntax'), + help='install SwiftSyntax') + option('--swiftsyntax-verify-generated-files', + toggle_true('swiftsyntax_verify_generated_files'), + help='set to verify that the generated files in the source tree ' + 'match the ones that would be generated from current master') + option(['--install-pythonkit'], toggle_true('install_pythonkit'), + help='install PythonKit') option(['--install-sourcekit-lsp'], toggle_true('install_sourcekitlsp'), help='install SourceKitLSP') + option(['--install-skstresstester'], toggle_true('install_skstresstester'), + help='install the SourceKit stress tester') + option(['--install-swiftevolve'], toggle_true('install_swiftevolve'), + help='install SwiftEvolve') option(['--toolchain-benchmarks'], toggle_true('build_toolchainbenchmarks'), help='build Swift Benchmarks using swiftpm against the just built ' @@ -588,12 +601,20 @@ def create_argument_parser(): option('--playgroundsupport', store_true('build_playgroundsupport'), help='build PlaygroundSupport') + option('--pythonkit', store_true('build_pythonkit'), + help='build PythonKit') + option('--build-ninja', toggle_true, help='build the Ninja tool') option(['--build-libparser-only'], store_true('build_libparser_only'), help='build only libParser for SwiftSyntax') + option('--skip-build-clang-tools-extra', + toggle_false('build_clang_tools_extra'), + default=True, + help='skip building clang-tools-extra as part of llvm') + # ------------------------------------------------------------------------- in_group('Extra actions to perform before or in addition to building') @@ -832,6 +853,9 @@ def create_argument_parser(): option('--skip-test-cygwin', toggle_false('test_cygwin'), help='skip testing Swift stdlibs for Cygwin') + option('--test-pythonkit', toggle_true('test_pythonkit'), + help='skip testing PythonKit') + # ------------------------------------------------------------------------- in_group('Run build') @@ -951,10 +975,21 @@ def create_argument_parser(): help='skip testing Android device targets on the host machine (the ' 'phone itself)') + option('--skip-test-swiftpm', toggle_false('test_swiftpm'), + help='skip testing swiftpm') + option('--skip-test-swiftsyntax', toggle_false('test_swiftsyntax'), + help='skip testing SwiftSyntax') option('--skip-test-indexstore-db', toggle_false('test_indexstoredb'), help='skip testing indexstore-db') option('--skip-test-sourcekit-lsp', toggle_false('test_sourcekitlsp'), help='skip testing sourcekit-lsp') + option('--skip-test-skstresstester', toggle_false('test_skstresstester'), + help='skip testing the SourceKit Stress tester') + option('--skip-test-swiftevolve', toggle_false('test_swiftevolve'), + help='skip testing SwiftEvolve') + option('--skip-test-toolchain-benchmarks', + toggle_false('test_toolchainbenchmarks'), + help='skip testing toolchain benchmarks') # ------------------------------------------------------------------------- in_group('Build settings specific for LLVM') @@ -1008,6 +1043,14 @@ def create_argument_parser(): 'Currently only armv7 and aarch64 are supported. ' '%(default)s is the default.') + # ------------------------------------------------------------------------- + in_group('Experimental language features') + + option('--enable-experimental-differentiable-programming', toggle_true, + default=True, + help='Enable experimental Swift differentiable programming language' + ' features.') + # ------------------------------------------------------------------------- in_group('Unsupported options') @@ -1018,6 +1061,16 @@ def create_argument_parser(): option('--skip-test-optimize-none-with-implicit-dynamic', unsupported) option('--skip-test-optimized', unsupported) + # ------------------------------------------------------------------------- + in_group('Build-script-impl arguments (for disambiguation)') + # We need to list --skip-test-swift explicitly because otherwise argparse + # will auto-expand arguments like --skip-test-swift to the only known + # argument --skip-test-swiftevolve. + # These arguments are forwarded to impl_args in migration.py + + option('--install-swift', toggle_true('impl_install_swift')) + option('--skip-test-swift', toggle_true('impl_skip_test_swift')) + # ------------------------------------------------------------------------- return builder.build() @@ -1060,7 +1113,8 @@ def create_argument_parser(): Any arguments not listed are forwarded directly to Swift's -'build-script-impl'. See that script's help for details. +'build-script-impl'. See that script's help for details. The listed +build-script-impl arguments are only for disambiguation in the argument parser. Environment variables --------------------- diff --git a/utils/build_swift/migration.py b/utils/build_swift/build_swift/migration.py similarity index 50% rename from utils/build_swift/migration.py rename to utils/build_swift/build_swift/migration.py index 53c6ae0d577f3..3a1a7be95705a 100644 --- a/utils/build_swift/migration.py +++ b/utils/build_swift/build_swift/migration.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -11,24 +11,25 @@ Temporary module with functionaly used to migrate away from build-script-impl. """ + from __future__ import absolute_import, unicode_literals import itertools +import subprocess + +import six +from six.moves import map from swift_build_support.swift_build_support.targets import \ StdlibDeploymentTarget -try: - # Python 2 - from itertools import imap -except ImportError: - imap = map - - __all__ = [ 'UnknownSDKError', + + 'check_impl_args', 'migrate_swift_sdks', + 'parse_args', ] @@ -82,9 +83,69 @@ def _migrate_swift_sdks_arg(arg): sdks = arg.split('=')[1] sdk_list = [] if sdks == '' else sdks.split(';') - targets = _flatten(imap(_swift_sdk_to_stdlib_targets, sdk_list)) + targets = _flatten(map(_swift_sdk_to_stdlib_targets, sdk_list)) target_names = [target.name for target in targets] return '--stdlib-deployment-targets={}'.format(' '.join(target_names)) - return list(imap(_migrate_swift_sdks_arg, args)) + return list(map(_migrate_swift_sdks_arg, args)) + + +# ----------------------------------------------------------------------------- + +def _process_disambiguation_arguments(args, unknown_args): + """These arguments are only listed in the driver arguments to stop argparse + from auto expanding arguments like --install-swift to the known argument + --install-swiftevolve. Remove them from args and add them to unknown_args + again. + """ + + if hasattr(args, 'impl_skip_test_swift'): + if args.impl_skip_test_swift: + unknown_args.append('--skip-test-swift') + del args.impl_skip_test_swift + + if hasattr(args, 'impl_install_swift'): + if args.impl_install_swift: + unknown_args.append('--install-swift') + del args.impl_install_swift + + return args, unknown_args + + +def parse_args(parser, args, namespace=None): + """Parses a given argument list with the given argparse.ArgumentParser. + + Return a processed arguments object. Any unknown arguments are stored in + `build_script_impl_args` attribute as a list. Ignores '--' to be compatible + with old style argument list. + """ + + args = [arg for arg in args if arg != '--'] + args, unknown_args = parser.parse_known_args(args, namespace) + args, unknown_args = _process_disambiguation_arguments(args, unknown_args) + + args.build_script_impl_args = unknown_args + + return args + + +# ----------------------------------------------------------------------------- + +def check_impl_args(build_script_impl, args): + """Check whether given argv are all known arguments for + `build-script-impl`. + + Raises a ValueError if any invalid argument is found. Return nothing + otherwise. + """ + + pipe = subprocess.Popen( + [build_script_impl, '--check-args-only=1'] + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + _, err = pipe.communicate() + + if pipe.returncode != 0: + raise ValueError(six.text_type(err.splitlines()[0].decode())) diff --git a/utils/build_swift/presets.py b/utils/build_swift/build_swift/presets.py similarity index 52% rename from utils/build_swift/presets.py rename to utils/build_swift/build_swift/presets.py index 0376fce634404..892bede776872 100644 --- a/utils/build_swift/presets.py +++ b/utils/build_swift/build_swift/presets.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -11,22 +11,21 @@ Swift preset parsing and handling functionality. """ + from __future__ import absolute_import, unicode_literals -from collections import namedtuple -from contextlib import contextmanager +import functools +import io +from collections import OrderedDict, namedtuple + +from six import StringIO +from six.moves import configparser -try: - # Python 2 - import ConfigParser as configparser - from StringIO import StringIO -except ImportError: - import configparser - from io import StringIO +from . import class_utils __all__ = [ - 'Error', + 'PresetError', 'DuplicatePresetError', 'DuplicateOptionError', 'InterpolationError', @@ -39,13 +38,20 @@ # ----------------------------------------------------------------------------- +# Constants _PRESET_PREFIX = 'preset: ' + +# ----------------------------------------------------------------------------- +# Helpers + _Mixin = namedtuple('_Mixin', ['name']) -_Argument = namedtuple('_Argument', ['name', 'value']) +_Option = namedtuple('_Option', ['name', 'value']) _RawPreset = namedtuple('_RawPreset', ['name', 'options']) +_UnparsedFile = namedtuple('_UnparsedFile', ['filename', 'reason']) + def _interpolate_string(string, values): if string is None: @@ -60,51 +66,50 @@ def _remove_prefix(string, prefix): return string -@contextmanager -def _catch_duplicate_option_error(): - """Shim context object used for catching and rethrowing configparser's - DuplicateOptionError, which was added in the Python 3 refactor. +def _catch_duplicate_option_error(func): + """Decorator used to catch and rethrowing configparser's + DuplicateOptionError. """ - if hasattr(configparser, 'DuplicateOptionError'): + if not hasattr(configparser, 'DuplicateOptionError'): + return func + + @functools.wraps(func) + def wrapper(*args, **kwargs): try: - yield + return func(*args, **kwargs) except configparser.DuplicateOptionError as e: - preset_name = _remove_prefix(e.section, _PRESET_PREFIX) + preset_name = _remove_prefix(e.section, _PRESET_PREFIX).strip() raise DuplicateOptionError(preset_name, e.option) - else: - yield + return wrapper -@contextmanager -def _catch_duplicate_section_error(): - """Shim context object used for catching and rethrowing configparser's +def _catch_duplicate_section_error(func): + """Decorator used to catch and rethrowing configparser's DuplicateSectionError. """ - try: - yield - except configparser.DuplicateSectionError as e: - preset_name = _remove_prefix(e.section, _PRESET_PREFIX) - raise DuplicatePresetError(preset_name) - + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except configparser.DuplicateSectionError as e: + preset_name = _remove_prefix(e.section, _PRESET_PREFIX).strip() + raise DuplicatePresetError(preset_name) -@contextmanager -def _convert_configparser_errors(): - with _catch_duplicate_option_error(), _catch_duplicate_section_error(): - yield + return wrapper # ----------------------------------------------------------------------------- -# Error classes +# Errors -class Error(Exception): +class PresetError(Exception): """Base class for preset errors. """ def __init__(self, message=''): - super(Error, self).__init__(self, message) + super(PresetError, self).__init__(self, message) self.message = message @@ -114,36 +119,37 @@ def __str__(self): __repr__ = __str__ -class DuplicatePresetError(Error): +class DuplicatePresetError(PresetError): """Raised when an existing preset would be overriden. """ def __init__(self, preset_name): - Error.__init__(self, '{} already exists'.format(preset_name)) + super(DuplicatePresetError, self).__init__( + '{} already exists'.format(preset_name)) self.preset_name = preset_name -class DuplicateOptionError(Error): +class DuplicateOptionError(PresetError): """Raised when an option is repeated in a single preset. """ def __init__(self, preset_name, option): - Error.__init__(self, '{} already exists in preset {}'.format( - option, preset_name)) + super(DuplicateOptionError, self).__init__( + '{} already exists in preset {}'.format(option, preset_name)) self.preset_name = preset_name self.option = option -class InterpolationError(Error): +class InterpolationError(PresetError): """Raised when an error is encountered while interpolating use-provided values in preset arguments. """ def __init__(self, preset_name, option, rawval, reference): - Error.__init__(self, 'no value found for {} in "{}"'.format( - reference, rawval)) + super(InterpolationError, self).__init__( + 'no value found for {} in "{}"'.format(reference, rawval)) self.preset_name = preset_name self.option = option @@ -151,56 +157,90 @@ def __init__(self, preset_name, option, rawval, reference): self.reference = reference -class PresetNotFoundError(Error): +class PresetNotFoundError(PresetError): """Raised when a requested preset cannot be found. """ def __init__(self, preset_name): - Error.__init__(self, '{} not found'.format(preset_name)) + super(PresetNotFoundError, self).__init__( + '{} not found'.format(preset_name)) self.preset_name = preset_name -class UnparsedFilesError(Error): +class UnparsedFilesError(PresetError): """Raised when an error was encountered parsing one or more preset files. """ - def __init__(self, filenames): - Error.__init__(self, 'unable to parse files: {}'.format(filenames)) + def __init__(self, unparsed_files): + super(UnparsedFilesError, self).__init__( + 'unable to parse files: {}'.format(unparsed_files)) - self.filenames = filenames + self.unparsed_files = unparsed_files # ----------------------------------------------------------------------------- -class Preset(namedtuple('Preset', ['name', 'args'])): - """Container class used to wrap preset names and expanded argument lists. +@class_utils.generate_repr('name', 'options') +class Preset(object): + """Container class used to wrap preset names and expanded options list. """ - # Keeps memory costs low according to the docs - __slots__ = () + __slots__ = ('name', 'options') + + def __init__(self, name, options): + self.name = name + self.options = options - def format_args(self): - """Format argument pairs for use in the command line. + def __str__(self): + return repr(self) + + @property + def args(self): + """Format options into command line arguments. """ args = [] - for (name, value) in self.args: + for (name, value) in self.options: if value is None: - args.append(name) + args.append('--{}'.format(name)) else: - args.append('{}={}'.format(name, value)) + args.append('--{}={}'.format(name, value)) return args +# ----------------------------------------------------------------------------- +# Preset Parsing + class PresetParser(object): """Parser class used to read and manipulate Swift preset files. """ def __init__(self): self._parser = configparser.RawConfigParser(allow_no_value=True) - self._presets = {} + self._presets = OrderedDict() + + # ------------------------------------------------------------------------- + # Properties + + @property + def preset_names(self): + """Returns a list of all parsed preset names in the order they were + parsed. + """ + + return self._presets.keys() + + @property + def presets(self): + """Returns a list of all parsed presets in the order they were parsed. + """ + + return self._presets.values() + + # ------------------------------------------------------------------------- + # Parsing def _parse_raw_preset(self, section): preset_name = _remove_prefix(section, _PRESET_PREFIX) @@ -208,25 +248,24 @@ def _parse_raw_preset(self, section): try: section_items = self._parser.items(section) except configparser.InterpolationMissingOptionError as e: - raise InterpolationError(preset_name, e.option, e.rawval, - e.reference) + raise InterpolationError( + preset_name, e.option, e.rawval, e.reference) - args = [] - for (option, value) in section_items: + options = [] + for (name, value) in section_items: # Ignore the '--' separator, it's no longer necessary - if option == 'dash-dash': + if name == 'dash-dash': continue # Parse out mixin options - if option == 'mixin-preset': + if name == 'mixin-preset': lines = value.strip().splitlines() - args += [_Mixin(option.strip()) for option in lines] + options += [_Mixin(mixin_name.strip()) for mixin_name in lines] continue - option = '--' + option # Format as a command-line option - args.append(_Argument(option, value)) + options.append(_Option(name, value)) - return _RawPreset(preset_name, args) + return _RawPreset(preset_name, options) def _parse_raw_presets(self): for section in self._parser.sections(): @@ -237,41 +276,75 @@ def _parse_raw_presets(self): raw_preset = self._parse_raw_preset(section) self._presets[raw_preset.name] = raw_preset - def read(self, filenames): + @_catch_duplicate_option_error + @_catch_duplicate_section_error + def read_file(self, filename): + """Reads and parses a single file. + """ + + with io.open(filename, 'r') as fp: + if hasattr(self._parser, 'read_file'): + self._parser.read_file(fp) + else: + self._parser.readfp(fp) + + self._parse_raw_presets() + + def read_files(self, filenames): """Reads and parses preset files. Throws an UnparsedFilesError if any of the files couldn't be read. """ - with _convert_configparser_errors(): - parsed_files = self._parser.read(filenames) + unparsed_files = [] + for filename in filenames: + try: + self.read_file(filename) + except Exception as e: + unparsed_files.append(_UnparsedFile(filename, e)) - unparsed_files = set(filenames) - set(parsed_files) if len(unparsed_files) > 0: - raise UnparsedFilesError(list(unparsed_files)) + raise UnparsedFilesError(unparsed_files) self._parse_raw_presets() - def read_file(self, file): - """Reads and parses a single file. - """ - - self.read([file]) - + @_catch_duplicate_option_error + @_catch_duplicate_section_error def read_string(self, string): """Reads and parses a string containing preset definintions. """ fp = StringIO(string) - with _convert_configparser_errors(): - # ConfigParser changes drastically from Python 2 to 3 - if hasattr(self._parser, 'read_file'): - self._parser.read_file(fp) - else: - self._parser.readfp(fp) + # ConfigParser changes drastically from Python 2 to 3 + if hasattr(self._parser, 'read_file'): + self._parser.read_file(fp) + else: + self._parser.readfp(fp) self._parse_raw_presets() + # ------------------------------------------------------------------------- + # Resolving + + def _resolve_preset_mixins(self, raw_preset): + """Resolve all mixins in a preset, fully expanding the options list. + """ + + assert isinstance(raw_preset, _RawPreset) + + # Expand mixin options. + options = [] + for option in raw_preset.options: + if isinstance(option, _Mixin): + options += self._get_preset(option.name).options + elif isinstance(option, _Option): + options.append((option.name, option.value)) + else: + # Should be unreachable. + raise ValueError('invalid argument type: {}', option.__class__) + + return Preset(raw_preset.name, options) + def _get_preset(self, name): preset = self._presets.get(name) if preset is None: @@ -285,36 +358,18 @@ def _get_preset(self, name): return preset - def _resolve_preset_mixins(self, raw_preset): - """Resolve all mixins in a preset, fully expanding the arguments list. - """ - - assert isinstance(raw_preset, _RawPreset) - - # Expand mixin arguments - args = [] - for option in raw_preset.options: - if isinstance(option, _Mixin): - args += self._get_preset(option.name).args - elif isinstance(option, _Argument): - args.append((option.name, option.value)) - else: - # Should be unreachable - raise ValueError('invalid argument type: {}', option.__class__) - - return Preset(raw_preset.name, args) - def _interpolate_preset_vars(self, preset, vars): - interpolated_args = [] - for (name, value) in preset.args: + interpolated_options = [] + for (name, value) in preset.options: try: value = _interpolate_string(value, vars) except KeyError as e: - raise InterpolationError(preset.name, name, value, e.args[0]) + raise InterpolationError( + preset.name, name, value, e.args[0]) - interpolated_args.append((name, value)) + interpolated_options.append((name, value)) - return Preset(preset.name, interpolated_args) + return Preset(preset.name, interpolated_options) def get_preset(self, name, raw=False, vars=None): """Returns the preset with the requested name or throws a @@ -336,9 +391,3 @@ def get_preset(self, name, raw=False, vars=None): preset = self._interpolate_preset_vars(preset, vars) return preset - - def preset_names(self): - """Returns a list of all parsed preset names. - """ - - return self._presets.keys() diff --git a/utils/build_swift/build_swift/shell.py b/utils/build_swift/build_swift/shell.py new file mode 100644 index 0000000000000..edcc35d7938c6 --- /dev/null +++ b/utils/build_swift/build_swift/shell.py @@ -0,0 +1,571 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Shell utilities wrapper module. +""" + + +from __future__ import absolute_import, unicode_literals + +import abc +import collections +import functools +import itertools +import os +import shlex +import shutil +import subprocess +import sys +from copy import copy as _copy +from shlex import split +from subprocess import CalledProcessError + +import six +from six.moves import map + + +try: + # Python 2 + from pipes import quote as _quote +except ImportError: + from shutil import quote as _quote + + +try: + # Python 3.4 + from pathlib import Path +except ImportError: + Path = None + + +__all__ = [ + 'CalledProcessError', + 'AbstractWrapper', + 'CommandWrapper', + 'ExecutableWrapper', + + 'quote', + 'split', + 'rerun_as_root', + + 'Popen', + 'call', + 'check_call', + 'check_output', + + 'copy', + 'pushd', + 'makedirs', + 'move', + 'remove', + 'symlink', + 'which', + + 'wraps', + + 'ECHO_PREFIX', + 'PIPE', + 'STDOUT', + 'DEVNULL', +] + + +_PY_VERSION = (sys.version_info.major, sys.version_info.minor) + +ECHO_PREFIX = '>>> ' + +# Re-export subprocess constants +PIPE = subprocess.PIPE +STDOUT = subprocess.STDOUT + +try: + DEVNULL = subprocess.DEVNULL +except AttributeError: + DEVNULL = -3 + + +# ----------------------------------------------------------------------------- +# Helpers + +def _flatmap(func, *iterables): + """Helper function that maps the given func over the iterables and then + creates a single flat iterable from the results. + """ + + return itertools.chain.from_iterable(map(func, *iterables)) + + +def _convert_pathlib_path(path): + """Helper function used to convert an instance of pathlib.Path into a + unicode string. + """ + + if Path is None: + return path + + if isinstance(path, Path): + return six.text_type(path) + + return path + + +def _get_stream_file(stream): + """Helper function used to decode the standard PIPE and STDOUT constants + into actual file objects. + """ + + if stream == PIPE: + return sys.stdout + if stream == STDOUT: + return sys.stdout + if stream == DEVNULL: + raise ValueError('DEVNULL should be replaced by now!') + + return stream + + +def _echo_command(command, stream, prefix=ECHO_PREFIX): + """Helper function used to echo a given command to some stream. An optional + command prefix can be provided. + """ + + if stream == DEVNULL: + return + + stream = _get_stream_file(stream) + + stream.write('{}{}\n'.format(prefix, quote(command))) + stream.flush() + + +def _normalize_args(args): + """Normalizes a list of arguments containing one or more strings and + CommandWrapper instances into a one-dimensional list of strings. + """ + + if isinstance(args, six.string_types): + return shlex.split(args) + + def normalize_arg(arg): + arg = _convert_pathlib_path(arg) + + if isinstance(arg, six.string_types): + return [six.text_type(arg)] + if isinstance(arg, AbstractWrapper): + return list(map(_convert_pathlib_path, arg.command)) + + raise ValueError('Invalid argument type: {}'.format( + type(arg).__name__)) + + if isinstance(args, AbstractWrapper): + return normalize_arg(args) + + return list(_flatmap(normalize_arg, args)) + + +# ----------------------------------------------------------------------------- +# Decorators + +def _backport_devnull(func): + """Decorator used to backport the subprocess.DEVNULL functionality from + Python 3 to Python 2. + """ + + # DEVNULL was introduced in Python 3.3 + if _PY_VERSION >= (3, 3): + return func + + @functools.wraps(func) + def wrapper(command, **kwargs): + stdout = kwargs.get('stdout', sys.stdout) + stderr = kwargs.get('stderr', sys.stderr) + + if stdout != DEVNULL and stderr != DEVNULL: + return func(command, **kwargs) + + with open(os.devnull, 'w') as devnull: + if stdout == DEVNULL: + kwargs['stdout'] = devnull + if stderr == DEVNULL: + kwargs['stderr'] = devnull + + return func(command, **kwargs) + + return wrapper + + +def _normalize_command(func): + """Decorator used to uniformly normalize the input command of the + subprocess wrappers. + """ + + @functools.wraps(func) + def wrapper(command, **kwargs): + if not isinstance(command, six.string_types): + command = _normalize_args(command) + + return func(command, **kwargs) + + return wrapper + + +def _add_echo_kwarg(func): + """Decorator used to add the 'echo' keyword-only argument that echos the + input command to whatever stdout the user passes (or sys.stdout if not + supplied). + """ + + @functools.wraps(func) + def wrapper(command, **kwargs): + if kwargs.pop('echo', False): + stdout = kwargs.get('stdout', sys.stdout) + _echo_command(command, stdout) + + return func(command, **kwargs) + + return wrapper + + +# ----------------------------------------------------------------------------- +# Public Functions + +def quote(command): + """Extension of the standard pipes.quote (Python 2) or shutil.quote + (Python 3) that handles both strings and lists of strings. This mirrors + how the subprocess package can handle commands as both a standalone string + or list of strings. + + >>> quote('/Applications/App Store.app') + "'/Applications/App Store.app'" + + >>> quote(['rm', '-rf', '~/Documents/My Homework']) + "rm -rf '~/Documents/My Homework'" + """ + + if isinstance(command, six.string_types): + return _quote(command) + + if isinstance(command, collections.Iterable): + return ' '.join([_quote(arg) for arg in _normalize_args(command)]) + + raise ValueError('Invalid command type: {}'.format(type(command).__name__)) + + +def rerun_as_root(): + """Replace the current process with itself running with root permissions. + Prompt the user for their password to support this. + """ + + euid = os.geteuid() + if euid == 0: + return + + args = ['sudo', sys.executable] + sys.argv + [os.environ] + os.execlpe('sudo', *args) + + +# ----------------------------------------------------------------------------- +# Subprocess Wrappers + +class Popen(subprocess.Popen): + """Wrapper around subprocess.Popen which allows for a more flexible command + type and echoing the input command to stdout. + """ + + def __init__(self, command, **kwargs): + """In order to utilize the same function decorators used to back-port + devnull support and add new features, we create a closure to capture + the input 'self' value while retaining a standard function signature. + + Django has a really nice (and relatively simple) general purpose + solution to this problem in the form of their `method_decorator`. + """ + + @_backport_devnull + @_normalize_command + @_add_echo_kwarg + def closure(command, **kwargs): + super(Popen, self).__init__(command, **kwargs) + + closure(command, **kwargs) + + # Back-port the context manager behavior for Python 3.1 and below. + if _PY_VERSION < (3, 2): + def __enter__(self): + return self + + def __exit__(self, *exc): + self.wait() + + +@_backport_devnull +@_normalize_command +@_add_echo_kwarg +def call(command, **kwargs): + """Simple wrapper around subprocess.call which backports DEVNULL support + and adds support for the echo keyword-only argument. + """ + + return subprocess.call(command, **kwargs) + + +@_backport_devnull +@_normalize_command +@_add_echo_kwarg +def check_call(command, **kwargs): + """Simple wrapper around subprocess.check_call which backports DEVNULL + support and adds support for the echo keyword-only argument. + """ + + return subprocess.check_call(command, **kwargs) + + +@_backport_devnull +@_normalize_command +@_add_echo_kwarg +def check_output(command, **kwargs): + """Simple wrapper around subprocess.check_output which backports DEVNULL + support and adds support for the echo keyword-only argument. + + Output is returned as a unicode string. + """ + + if six.PY3: + kwargs['encoding'] = 'utf-8' + + output = subprocess.check_output(command, **kwargs) + + if six.PY3: + return output + + # Return unicode string rather than bytes in Python 2. + return six.text_type(output, errors='ignore') + + +# ----------------------------------------------------------------------------- +# Shell Utilities + +def copy(source, dest, echo=False): + """Emulates the `cp` command to copy a file or directory. + """ + + source = _convert_pathlib_path(source) + dest = _convert_pathlib_path(dest) + + if os.path.isfile(source): + if echo: + _echo_command(['cp', source, dest], sys.stdout) + return shutil.copyfile(source, dest) + + if os.path.isdir(source): + if echo: + _echo_command(['cp', '-R', source, dest], sys.stdout) + return shutil.copytree(source, dest) + + +class pushd(object): + """Context manager to mimic the behavior of pushd and popd, moving the + current working directory to a new path and then restoring it when exiting + the current block. + """ + + def __init__(self, path, echo=False): + path = _convert_pathlib_path(path) + + self.cwd = os.getcwd() + self.path = os.path.expanduser(path) + self.echo = echo + + def __enter__(self): + if self.echo: + _echo_command(['pushd', self.path], sys.stdout) + + os.chdir(self.path) + return self.path + + def __exit__(self, *args): + if self.echo: + _echo_command(['popd'], sys.stdout) + + os.chdir(self.cwd) + + +def makedirs(path, echo=False): + """Emulates the `mkdir -p` command to recursively create directories for + the path given if it doesn't already exist. + """ + + path = _convert_pathlib_path(path) + if os.path.exists(path): + return + + if echo: + _echo_command(['mkdir', '-p', path], sys.stdout) + + os.makedirs(path) + + +def move(source, dest, echo=False): + """Emulates the `mv` command to move files or directories. + """ + + source = _convert_pathlib_path(source) + dest = _convert_pathlib_path(dest) + + if echo: + _echo_command(['mv', source, dest], sys.stdout) + + return shutil.move(source, dest) + + +def remove(path, echo=False): + """Emulates the `rm` command for both files and directories. + """ + + path = _convert_pathlib_path(path) + + if os.path.isfile(path): + if echo: + _echo_command(['rm', path], sys.stdout) + return os.remove(path) + + if os.path.isdir(path): + if echo: + _echo_command(['rm', '-rf', path], sys.stdout) + return shutil.rmtree(path, ignore_errors=True) + + +def symlink(source, dest, echo=False): + """Emulates the `ln` command to symlink a file or directory. + """ + + source = _convert_pathlib_path(source) + dest = _convert_pathlib_path(dest) + + if echo: + _echo_command(['ln', '-s', source, dest], sys.stdout) + + return os.symlink(source, dest) + + +def which(command, mode=os.F_OK | os.X_OK, path=None): + """Polyfill for the Python 3 shutil.which function. Does not support + Windows platforms. + """ + + # Default to environment PATH or os.defpath + path = path or os.environ.get('PATH', os.defpath) + + for location in path.split(os.pathsep): + # If command is a full path then candidate will be just command + candidate = os.path.join(location, command) + + if os.path.isfile(candidate) and os.access(candidate, mode): + return candidate + + return None + + +# ----------------------------------------------------------------------------- +# Wrappers + +def wraps(command): + """Simple utility function to instantiate a CommandWrapper instance in a + more fluent way. + """ + + return CommandWrapper(command) + + +@six.add_metaclass(abc.ABCMeta) +class AbstractWrapper(object): + """Abstract base class for implementing wrappers around command line + utilities and executables. Subclasses must implement the `command` method + which returns a command list suitable for use with executor instances. + """ + + def __call__(self, *args, **kwargs): + return self.check_call(*args, **kwargs) + + @abc.abstractproperty + @property + def command(self): + """Subclasses must implement a command property. + """ + + raise NotImplementedError() + + def __build_command(self, args): + args = _normalize_args(args) + + return self.command + _normalize_args(args) + + # ------------------------------------------------------------------------- + + def Popen(self, args, **kwargs): + return Popen(self.__build_command(args), **kwargs) + + def call(self, args, **kwargs): + return call(self.__build_command(args), **kwargs) + + def check_call(self, args, **kwargs): + return check_call(self.__build_command(args), **kwargs) + + def check_output(self, args, **kwargs): + return check_output(self.__build_command(args), **kwargs) + + +class CommandWrapper(AbstractWrapper): + """Wrapper class for command line utilities which can be initialized + on-demand for the desired command. + """ + + __slots__ = ('_command') + + def __init__(self, command): + super(CommandWrapper, self).__init__() + + self._command = _normalize_args(command) + + @property + def command(self): + return _copy(self._command) + + +class ExecutableWrapper(AbstractWrapper): + """Wrapper class for executable utilities. This class is suitable as a base + class for implementing wrapper classes around executables, simply subclass + and define the `EXECUTABLE` attribute to the correct executable name or + path. + """ + + EXECUTABLE = None + + def __init__(self): + if self.EXECUTABLE is None: + raise AttributeError('{}.EXECUTABLE cannot be None'.format( + type(self).__name__)) + + self.EXECUTABLE = _convert_pathlib_path(self.EXECUTABLE) + + if not isinstance(self.EXECUTABLE, six.string_types): + raise AttributeError( + '{}.EXECUTABLE must be an executable name or path'.format( + type(self).__name__)) + + super(ExecutableWrapper, self).__init__() + + @property + def command(self): + return [_copy(self.EXECUTABLE)] + + @property + def path(self): + return which(self.EXECUTABLE) diff --git a/utils/build_swift/build_swift/versions.py b/utils/build_swift/build_swift/versions.py new file mode 100644 index 0000000000000..0db7ec32ce7b9 --- /dev/null +++ b/utils/build_swift/build_swift/versions.py @@ -0,0 +1,210 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Version parsing classes. +""" + + +from __future__ import absolute_import, unicode_literals + +import functools + +import six + + +__all__ = [ + 'InvalidVersionError', + 'Version', +] + + +# ----------------------------------------------------------------------------- +# Version Parsing + +class _ComponentType(object): + """Poor-man's enum representing all valid version character groups. + """ + + def __init__(self, name): + self.name = name + + def __eq__(self, other): + if not isinstance(other, _ComponentType): + return NotImplemented + + return self.name == other.name + + def __ne__(self, other): + return not self.__eq__(other) + + @classmethod + def _register(cls, name): + setattr(cls, name, cls(name)) + + +_ComponentType._register('ALPHA_LOWER') +_ComponentType._register('ALPHA_UPPER') +_ComponentType._register('DOT') +_ComponentType._register('NUMERIC') +_ComponentType._register('OTHER') + + +def _get_component_type(component): + """Classifies a component into one of the registered component types. + """ + + if len(component) <= 0: + raise ValueError('Empty component') + + if component == '.': + return _ComponentType.DOT + + if component.isdigit(): + return _ComponentType.NUMERIC + + if component.isalpha(): + if component.isupper(): + return _ComponentType.ALPHA_UPPER + elif component.islower(): + return _ComponentType.ALPHA_LOWER + else: + raise ValueError('Unknown component type for {!r}'.format( + component)) + + return _ComponentType.OTHER + + +def _try_cast(obj, cls): + """Attempts to cast an object to a class, returning the resulting casted + object or the original object if the cast raises a ValueError. + """ + + try: + return cls(obj) + except ValueError: + return obj + + +def _split_version(version): + """Splits a version string into a tuple of components using similar rules + to distutils.version.LooseVersion. All version strings are valid, but the + outcome will only split on boundries between: + + * lowercase alpha characters + * uppercase alpha characters + * numeric characters + * the literal '.' (dot) character + + All other characters are grouped into an "other" category. + + Numeric components are converted into integers in the resulting tuple. + + An empty tuple is returned for the empty string. + + ``` + >>> _split_version('1000.2.108') + (1000, 2, 28) + + >>> _split_version('10A23b') + (10, 'A', 23, 'b') + + >>> _split_version('10.23-beta4') + (10, 23, '-', 'beta', 4) + + >>> _split_version('FOObarBAZqux') + ('FOO', 'bar', 'BAZ', 'qux') + ``` + """ + + if len(version) < 1: + return tuple() + + components = [] + + part = version[0] + part_type = _get_component_type(part) + + for char in version[1:]: + char_type = _get_component_type(char) + + if part_type == char_type: + part += char + else: + components.append(part) + part = char + part_type = char_type + + # Add last part + components.append(part) + + # Remove '.' groups and try casting components to ints + components = (_try_cast(c, int) for c in components if c != '.') + + return tuple(components) + + +# ----------------------------------------------------------------------------- +# Versions + +class InvalidVersionError(Exception): + """Error indicating an invalid version was encountered. + """ + + def __init__(self, version, msg=None): + self.version = version + + if msg is None: + msg = 'Invalid version: {}'.format(self.version) + + super(InvalidVersionError, self).__init__(msg) + + +@functools.total_ordering +class Version(object): + """Similar to the standard distutils.versons.LooseVersion, but with a + little more wiggle-room for alpha characters. + """ + + __slots__ = ('components', '_str') + + def __init__(self, version): + version = six.text_type(version) + + # Save the version string since it's impossible to reconstruct it from + # just the parsed components + self._str = version + + # Parse version components + self.components = _split_version(version) + + def __eq__(self, other): + if not isinstance(other, Version): + return NotImplemented + + return self.components == other.components + + # NOTE: Python 2 compatibility. + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, Version): + return NotImplemented + + return self.components < other.components + + def __hash__(self): + return hash(self.components) + + def __str__(self): + return self._str + + def __repr__(self): + return '{}({!r})'.format(type(self).__name__, self._str) diff --git a/utils/build_swift/build_swift/wrappers/__init__.py b/utils/build_swift/build_swift/wrappers/__init__.py new file mode 100644 index 0000000000000..0b613c3f6a7a9 --- /dev/null +++ b/utils/build_swift/build_swift/wrappers/__init__.py @@ -0,0 +1,20 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +from . import xcrun as _xcrun + + +__all__ = [ + 'xcrun', +] + + +xcrun = _xcrun.XcrunWrapper() diff --git a/utils/build_swift/build_swift/wrappers/xcrun.py b/utils/build_swift/build_swift/wrappers/xcrun.py new file mode 100644 index 0000000000000..7e29a6e73dbf4 --- /dev/null +++ b/utils/build_swift/build_swift/wrappers/xcrun.py @@ -0,0 +1,197 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Wrapper module around the 'xcrun' command-line utility. +""" + + +from __future__ import absolute_import, unicode_literals + +import functools +import re +import shlex + +import six + +from .. import shell +from ..versions import Version + + +__all__ = [ + 'XcrunWrapper', +] + + +# ----------------------------------------------------------------------------- +# Constants + +_VERSION_PATTERN = re.compile(r'^xcrun version (?P[0-9.]+)\.$') + + +# ----------------------------------------------------------------------------- +# Helpers + +def _catch_return_none(exceptions): + """Decorator used to catch exceptions and return None. + """ + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except exceptions: + return None + + return wrapper + return decorator + + +def _prepend_sdk_and_toolchain(func): + """Method decorator used to prepend the sdk and toolchain arguments to the + final command passed to xcrun. + """ + + @functools.wraps(func) + def wrapper(self, args, sdk=None, toolchain=None, **kwargs): + if isinstance(args, six.string_types): + args = shlex.split(args) + if toolchain: + args = ['--toolchain', toolchain] + args + if sdk: + args = ['--sdk', sdk] + args + + return func(self, args, **kwargs) + return wrapper + + +# ----------------------------------------------------------------------------- + +class XcrunWrapper(shell.ExecutableWrapper): + """Wrapper class around the 'xcrun' command-line utility. + """ + + EXECUTABLE = shell.which('xcrun') or 'xcrun' + + @_prepend_sdk_and_toolchain + def Popen(self, args, **kwargs): + return super(XcrunWrapper, self).Popen(args, **kwargs) + + @_prepend_sdk_and_toolchain + def call(self, args, **kwargs): + return super(XcrunWrapper, self).call(args, **kwargs) + + @_prepend_sdk_and_toolchain + def check_call(self, args, **kwargs): + return super(XcrunWrapper, self).check_call(args, **kwargs) + + @_prepend_sdk_and_toolchain + def check_output(self, args, **kwargs): + return super(XcrunWrapper, self).check_output(args, **kwargs) + + # ------------------------------------------------------------------------- + + @property + @_catch_return_none(shell.CalledProcessError) + def version(self): + """Returns the xcrun version. + """ + + output = self.check_output('--version') + matches = _VERSION_PATTERN.match(output.rstrip()) + if matches: + return Version(matches.group('version')) + + return None + + # ------------------------------------------------------------------------- + # Subcommands + + @_catch_return_none(shell.CalledProcessError) + def find(self, tool, sdk=None, toolchain=None): + """Finds and returns the path to tool using xcrun. Returns None if the + tool cannot be found. + """ + + return self.check_output( + ['--find', tool], + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL).rstrip() + + def kill_cache(self): + """Kills the xcrun cache. Returns the status code from the command. + """ + + return self.call('--kill-cache') + + @_catch_return_none(shell.CalledProcessError) + def sdk_path(self, sdk=None, toolchain=None): + """Returns the SDK path. Returns None if the SDK cannot be found. + """ + + return self.check_output( + '--show-sdk-path', + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL).rstrip() + + @_catch_return_none(shell.CalledProcessError) + def sdk_version(self, sdk=None, toolchain=None): + """Returns the SDK version. Returns None if the SDK cannot be found. + """ + + output = self.check_output( + '--show-sdk-version', + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL) + + return Version(output.rstrip()) + + @_catch_return_none(shell.CalledProcessError) + def sdk_build_version(self, sdk=None, toolchain=None): + """Returns the SDK build version. Returns None if the SDK cannot be + found. + """ + + output = self.check_output( + '--show-sdk-build-version', + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL) + + return Version(output.rstrip()) + + @_catch_return_none(shell.CalledProcessError) + def sdk_platform_path(self, sdk=None, toolchain=None): + """Returns the SDK platform path. Returns None if the SDK cannot be + found. + """ + + return self.check_output( + '--show-sdk-platform-path', + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL).rstrip() + + @_catch_return_none(shell.CalledProcessError) + def sdk_platform_version(self, sdk=None, toolchain=None): + """Returns the SDK platform version. Returns None if the SDK cannot be + found. + """ + + output = self.check_output( + '--show-sdk-platform-version', + sdk=sdk, + toolchain=toolchain, + stderr=shell.DEVNULL) + + return Version(output.rstrip()) diff --git a/utils/build_swift/run_tests.py b/utils/build_swift/run_tests.py new file mode 100644 index 0000000000000..0cfade6422484 --- /dev/null +++ b/utils/build_swift/run_tests.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Utility script used to easily run the build_swift module unit tests. +""" + + +from __future__ import absolute_import, unicode_literals + +import argparse +import os +import sys +import unittest + + +MODULE_DIR = os.path.abspath(os.path.dirname(__file__)) +UTILS_DIR = os.path.abspath(os.path.join(MODULE_DIR, os.pardir)) + + +def parse_args(): + parser = argparse.ArgumentParser( + description='Utility script used to run the build_swift module unit ' + 'test suite.') + + parser.set_defaults(verbosity=1) + + parser.add_argument('-v', '--verbose', + dest='verbosity', + action='store_const', + const=2, + help='Verbose output') + parser.add_argument('-q', '--quiet', + dest='verbosity', + action='store_const', + const=0, + help='Minimal output') + + parser.add_argument('-f', '--failfast', + action='store_true', + help='Stop on first failure') + parser.add_argument('-c', '--catch', + action='store_true', + help='Catch control-C and display results') + parser.add_argument('-b', '--buffer', + action='store_true', + help='Buffer stdout and stderr during test runs') + + parser.add_argument('-p', '--pattern', + default='test*.py', + help='Pattern to match tests ("%(default)s" default)') + + return parser.parse_args() + + +def main(): + args = parse_args() + + if args.catch: + unittest.installHandler() + + runner = unittest.TextTestRunner( + verbosity=args.verbosity, + failfast=args.failfast, + buffer=args.buffer) + + # Add the swift/utils directory to the Python path. + sys.path.append(UTILS_DIR) + + # Discover all tests for the module. + module_tests = unittest.defaultTestLoader.discover( + MODULE_DIR, pattern=args.pattern) + + # Create and run test suite. + suite = unittest.TestSuite() + suite.addTests(module_tests) + result = runner.run(suite) + + return not result.wasSuccessful() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/utils/build_swift/tests/__init__.py b/utils/build_swift/tests/__init__.py index fa71f9fece535..7c485c168e611 100644 --- a/utils/build_swift/tests/__init__.py +++ b/utils/build_swift/tests/__init__.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information diff --git a/utils/build_swift/tests/build_swift/__init__.py b/utils/build_swift/tests/build_swift/__init__.py new file mode 100644 index 0000000000000..7c485c168e611 --- /dev/null +++ b/utils/build_swift/tests/build_swift/__init__.py @@ -0,0 +1,7 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/utils/build_swift/tests/build_swift/argparse/__init__.py b/utils/build_swift/tests/build_swift/argparse/__init__.py new file mode 100644 index 0000000000000..7c485c168e611 --- /dev/null +++ b/utils/build_swift/tests/build_swift/argparse/__init__.py @@ -0,0 +1,7 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/utils/build_swift/tests/argparse/test_actions.py b/utils/build_swift/tests/build_swift/argparse/test_actions.py similarity index 88% rename from utils/build_swift/tests/argparse/test_actions.py rename to utils/build_swift/tests/build_swift/argparse/test_actions.py index b23e1e72bc682..0bd4d981b19b7 100644 --- a/utils/build_swift/tests/argparse/test_actions.py +++ b/utils/build_swift/tests/build_swift/argparse/test_actions.py @@ -1,20 +1,27 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -from ..utils import TestCase, redirect_stderr -from ...argparse import (ArgumentParser, BoolType, Nargs, PathType, SUPPRESS, - actions) +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift.argparse import ( + ArgumentParser, BoolType, Nargs, PathType, SUPPRESS, actions) + +import six + +from ... import utils # ----------------------------------------------------------------------------- -class TestAction(TestCase): +class TestAction(unittest.TestCase): def test_default_attributes(self): action = actions.Action(['--foo'], dests=['foo']) @@ -41,10 +48,9 @@ def test_multiple_destinations(self): self.assertEqual(action.dests, ['foo', 'bar']) def test_supports_dest_argument(self): - with self.assertNotRaises(Exception): - action = actions.Action([], [], dest='foo') + action = actions.Action([], [], dest='foo') - self.assertEqual(action.dest, SUPPRESS) + self.assertEqual(action.dest, SUPPRESS) def test_call_not_implemented(self): action = actions.Action([], []) @@ -53,7 +59,7 @@ def test_call_not_implemented(self): action(None, None, None, None) -class TestAppendAction(TestCase): +class TestAppendAction(unittest.TestCase): def test_default_attributes(self): action = actions.AppendAction(['--foo'], dests=['foo']) @@ -76,7 +82,7 @@ def test_append(self): self.assertEqual(args.foo, ['bar', 'baz']) -class TestCustomCallAction(TestCase): +class TestCustomCallAction(unittest.TestCase): def test_non_callable(self): with self.assertRaises(TypeError): @@ -99,7 +105,7 @@ def test_func(action, parser, namespace, values, option_string=None): self.assertEqual(args.foo, 'boo') -class TestStoreAction(TestCase): +class TestStoreAction(unittest.TestCase): def test_default_attributes(self): action = actions.StoreAction(['--foo'], dests=['foo'], choices=['bar']) @@ -117,7 +123,7 @@ def test_choices(self): self.assertEqual(action.nargs, Nargs.OPTIONAL) - with self.quietOutput(), self.assertRaises(SystemExit): + with utils.quiet_output(), self.assertRaises(SystemExit): parser.parse_args(['--foo', 'qux']) args = parser.parse_args(['--foo', 'bar']) @@ -166,7 +172,7 @@ def test_store_const_multiple_destinations(self): self.assertEqual(args.bar, 'baz') -class TestStoreIntAction(TestCase): +class TestStoreIntAction(unittest.TestCase): def test_default_attributes(self): action = actions.StoreIntAction(['--foo'], dests=['foo']) @@ -180,7 +186,7 @@ def test_valid_int(self): parser.add_argument('--foo', action=actions.StoreIntAction) for i in [0, 1, 42, -64]: - args = parser.parse_args(['--foo', str(i)]) + args = parser.parse_args(['--foo', six.text_type(i)]) self.assertEqual(args.foo, i) def test_invalid_int(self): @@ -188,11 +194,11 @@ def test_invalid_int(self): parser.add_argument('--foo', action=actions.StoreIntAction) for i in [0.0, True, 'bar']: - with self.quietOutput(), self.assertRaises(SystemExit): - parser.parse_args(['--foo', str(i)]) + with utils.quiet_output(), self.assertRaises(SystemExit): + parser.parse_args(['--foo', six.text_type(i)]) -class TestStoreTrueAction(TestCase): +class TestStoreTrueAction(unittest.TestCase): def test_default_attributes(self): action = actions.StoreTrueAction(['--foo'], dests=['foo']) @@ -218,7 +224,7 @@ def test_store_true(self): self.assertTrue(args.foo) -class TestStoreFalseAction(TestCase): +class TestStoreFalseAction(unittest.TestCase): def test_default_attributes(self): action = actions.StoreFalseAction(['--foo'], dests=['foo']) @@ -244,7 +250,7 @@ def test_store_false(self): self.assertFalse(args.foo) -class TestStorePathAction(TestCase): +class TestStorePathAction(unittest.TestCase): def test_default_attributes(self): action = actions.StorePathAction(['--foo'], dests=['foo']) @@ -264,7 +270,7 @@ def test_executable(self): self.assertTrue(action.type._assert_executable) -class TestToggleTrueAction(TestCase): +class TestToggleTrueAction(unittest.TestCase): def test_default_attributes(self): action = actions.ToggleTrueAction(['--foo'], dests=['foo']) @@ -295,7 +301,7 @@ def test_with_optional_true_arg(self): parser.add_argument('--foo', action=actions.ToggleTrueAction) for value in BoolType.TRUE_VALUES: - args = parser.parse_args(['--foo', str(value)]) + args = parser.parse_args(['--foo', six.text_type(value)]) self.assertTrue(args.foo) args = parser.parse_args(['--foo={}'.format(value)]) @@ -306,7 +312,7 @@ def test_with_optional_false_arg(self): parser.add_argument('--foo', action=actions.ToggleTrueAction) for value in BoolType.FALSE_VALUES: - args = parser.parse_args(['--foo', str(value)]) + args = parser.parse_args(['--foo', six.text_type(value)]) self.assertFalse(args.foo) args = parser.parse_args(['--foo={}'.format(value)]) @@ -326,7 +332,7 @@ def test_last_wins(self): self.assertTrue(args.foo) -class TestToggleFalseAction(TestCase): +class TestToggleFalseAction(unittest.TestCase): def test_default_attributes(self): action = actions.ToggleFalseAction(['--foo'], dests=['foo']) @@ -357,7 +363,7 @@ def test_with_optional_true_arg(self): parser.add_argument('--foo', action=actions.ToggleFalseAction) for value in BoolType.TRUE_VALUES: - args = parser.parse_args(['--foo', str(value)]) + args = parser.parse_args(['--foo', six.text_type(value)]) self.assertFalse(args.foo) args = parser.parse_args(['--foo={}'.format(value)]) @@ -368,7 +374,7 @@ def test_with_optional_false_arg(self): parser.add_argument('--foo', action=actions.ToggleFalseAction) for value in BoolType.FALSE_VALUES: - args = parser.parse_args(['--foo', str(value)]) + args = parser.parse_args(['--foo', six.text_type(value)]) self.assertTrue(args.foo) args = parser.parse_args(['--foo={}'.format(value)]) @@ -388,7 +394,7 @@ def test_last_wins(self): self.assertFalse(args.foo) -class TestUnuspportedAction(TestCase): +class TestUnuspportedAction(unittest.TestCase): def test_default_attributes(self): action = actions.UnsupportedAction(['--foo']) @@ -410,7 +416,7 @@ def test_raises_parser_error(self): parser = ArgumentParser() parser.add_argument('--foo', action=actions.UnsupportedAction) - with self.quietOutput(), self.assertRaises(SystemExit): + with utils.quiet_output(), self.assertRaises(SystemExit): parser.parse_args(['--foo']) def test_custom_error_message(self): @@ -424,7 +430,7 @@ def test_custom_error_message(self): self.assertEqual(action.message, message) - with redirect_stderr() as stderr, self.assertRaises(SystemExit): + with utils.redirect_stderr() as stderr, self.assertRaises(SystemExit): parser.parse_args(['--foo']) self.assertIn(message, stderr) diff --git a/utils/build_swift/tests/argparse/test_parser.py b/utils/build_swift/tests/build_swift/argparse/test_parser.py similarity index 93% rename from utils/build_swift/tests/argparse/test_parser.py rename to utils/build_swift/tests/build_swift/argparse/test_parser.py index a39dc0e11b3ce..caf23dd74768e 100644 --- a/utils/build_swift/tests/argparse/test_parser.py +++ b/utils/build_swift/tests/build_swift/argparse/test_parser.py @@ -1,21 +1,25 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +from __future__ import absolute_import, unicode_literals + +import unittest from argparse import _ArgumentGroup, _MutuallyExclusiveGroup -from ..utils import TestCase -from ...argparse import ArgumentParser, actions +from build_swift.argparse import ArgumentParser, actions + +from ... import utils # ----------------------------------------------------------------------------- -class TestBuilder(TestCase): +class TestBuilder(unittest.TestCase): def test_build(self): builder = ArgumentParser.builder() @@ -77,7 +81,7 @@ def test_add_option(self): args = parser.parse_args(['--baz', '--baz=FALSE']) self.assertTrue(args.baz) - with self.quietOutput(), self.assertRaises(SystemExit): + with utils.quiet_output(), self.assertRaises(SystemExit): parser.parse_args(['--qux']) def test_set_defaults(self): @@ -130,7 +134,7 @@ def test_mutually_exclusive_group(self): self.assertEqual(builder._current_group, builder._parser) -class TestArgumentParser(TestCase): +class TestArgumentParser(unittest.TestCase): def test_builder(self): builder = ArgumentParser.builder(usage='Totally useless help message') diff --git a/utils/build_swift/tests/argparse/test_types.py b/utils/build_swift/tests/build_swift/argparse/test_types.py similarity index 60% rename from utils/build_swift/tests/argparse/test_types.py rename to utils/build_swift/tests/build_swift/argparse/test_types.py index c9ed3a1aa2b15..d2c34af35b033 100644 --- a/utils/build_swift/tests/argparse/test_types.py +++ b/utils/build_swift/tests/build_swift/argparse/test_types.py @@ -1,82 +1,25 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +from __future__ import absolute_import, unicode_literals + import os.path import platform +import unittest -from ..utils import TestCase -from ...argparse import ArgumentTypeError, types +from build_swift.argparse import ArgumentTypeError, types +from build_swift.versions import Version # ----------------------------------------------------------------------------- -class TestCompilerVersion(TestCase): - - def test_init_valid_value(self): - version = types.CompilerVersion(1, 0, 0, 1) - self.assertEqual(version.components, (1, 0, 0, 1)) - - def test_init_list(self): - version = types.CompilerVersion([1, 0, 0]) - self.assertEqual(version.components, (1, 0, 0)) - - with self.assertNotRaises(ValueError): - types.CompilerVersion([1, 0]) - types.CompilerVersion([2, 3, 4]) - types.CompilerVersion([3, 1, 4, 1, 5, 9]) - - def test_init_tuple(self): - version = types.CompilerVersion((1, 0, 0)) - self.assertEqual(version.components, (1, 0, 0)) - - with self.assertNotRaises(ValueError): - types.CompilerVersion((1, 0)) - types.CompilerVersion((2, 3, 4)) - types.CompilerVersion((3, 1, 4, 1, 5, 9)) - - def test_init_str(self): - version = types.CompilerVersion('1.0.0') - self.assertEqual(version.components, (1, 0, 0)) - - with self.assertNotRaises(ValueError): - types.CompilerVersion('1.0') - types.CompilerVersion('2.3.4') - types.CompilerVersion('3.1.4.1.5.9') - - def test_init_invalid_value(self): - with self.assertRaises(ValueError): - types.CompilerVersion() - types.CompilerVersion([]) - types.CompilerVersion(()) - types.CompilerVersion('') - types.CompilerVersion(True) - types.CompilerVersion('a') - types.CompilerVersion(dict()) - - def test_eq(self): - v1 = types.CompilerVersion('1.0.0') - v2 = types.CompilerVersion('1.2.4.8') - - self.assertEqual(v1, v1) - self.assertEqual(v2, v2) - self.assertNotEqual(v1, v2) - self.assertNotEqual(v2, v1) - - def test_str(self): - version = types.CompilerVersion('1.0.0') - self.assertEqual(str(version), '1.0.0') - - version = types.CompilerVersion('1.0.0.1') - self.assertEqual(str(version), '1.0.0.1') - - -class TestBoolType(TestCase): +class TestBoolType(unittest.TestCase): def test_true_values(self): bool_type = types.BoolType() @@ -131,7 +74,7 @@ def test_invalid_values(self): self.assertRaises(ArgumentTypeError, bool_type, 'Invalid') -class TestPathType(TestCase): +class TestPathType(unittest.TestCase): def setUp(self): self.home_dir = os.path.expanduser('~') @@ -153,9 +96,7 @@ def test_expands_path(self): def test_assert_exists(self): path_type = types.PathType(assert_exists=True) - - with self.assertNotRaises(ArgumentTypeError): - path_type(__file__) + path_type(__file__) with self.assertRaises(ArgumentTypeError): path_type('/nonsensisal/path/') @@ -171,8 +112,7 @@ def test_assert_executable(self): bash_path = '/bin/bash' if os.path.isfile(bash_path) and os.access(bash_path, os.X_OK): - with self.assertNotRaises(ArgumentTypeError): - path_type(bash_path) + path_type(bash_path) with self.assertRaises(ArgumentTypeError): path_type(__file__) @@ -184,15 +124,14 @@ def _platform_path(self, path): return path -class TestRegexType(TestCase): +class TestRegexType(unittest.TestCase): def test_regex_match(self): regex_type = types.RegexType(r'a+b*') - with self.assertNotRaises(ArgumentTypeError): - regex_type('a') - regex_type('aab') - regex_type('abbbbbbb') + regex_type('a') + regex_type('aab') + regex_type('abbbbbbb') def test_raises_argument_error(self): regex_type = types.RegexType(r'a+b*') @@ -203,24 +142,23 @@ def test_raises_argument_error(self): regex_type('baaaa') -class TestClangVersionType(TestCase): +class TestClangVersionType(unittest.TestCase): def test_valid_clang_version(self): clang_version_type = types.ClangVersionType() version = clang_version_type('1.0.0') - self.assertIsInstance(version, types.CompilerVersion) + self.assertIsInstance(version, types.Version) self.assertEqual(version.components, (1, 0, 0)) version = clang_version_type('1.0.0.1') - self.assertIsInstance(version, types.CompilerVersion) + self.assertIsInstance(version, types.Version) self.assertEqual(version.components, (1, 0, 0, 1)) - with self.assertNotRaises(ArgumentTypeError): - clang_version_type('1.0.0') - clang_version_type('3.0.2.1') - clang_version_type('200.0.56.3') - clang_version_type('100000.0.0.1') + clang_version_type('1.0.0') + clang_version_type('3.0.2.1') + clang_version_type('200.0.56.3') + clang_version_type('100000.0.0.1') def test_invalid_clang_version(self): clang_version_type = types.ClangVersionType() @@ -232,46 +170,49 @@ def test_invalid_clang_version(self): clang_version_type('100.0.56.1') -class TestSwiftVersionType(TestCase): +class TestSwiftVersionType(unittest.TestCase): def test_valid_swift_version(self): swift_version_type = types.SwiftVersionType() version = swift_version_type('1.0') - self.assertIsInstance(version, types.CompilerVersion) + self.assertIsInstance(version, Version) self.assertEqual(version.components, (1, 0)) version = swift_version_type('1.0.1') - self.assertIsInstance(version, types.CompilerVersion) + self.assertIsInstance(version, Version) self.assertEqual(version.components, (1, 0, 1)) - with self.assertNotRaises(ArgumentTypeError): - swift_version_type('1.0') - swift_version_type('3.0.2') - swift_version_type('200.0.56') - swift_version_type('100000.0.1') + swift_version_type('1.0') + swift_version_type('3.0.2') + swift_version_type('200.0.56') + swift_version_type('100000.0.1') + + version = swift_version_type('100.0.999.1') + self.assertIsInstance(version, Version) + self.assertEqual(version.components, (100, 0, 999, 1)) def test_invalid_swift_version(self): swift_version_type = types.SwiftVersionType() with self.assertRaises(ArgumentTypeError): swift_version_type('2') - swift_version_type('1.8.0.2') - swift_version_type('100.0.56.1') + swift_version_type('1.8.0.2.3') + swift_version_type('100.0.56.1.85') -class TestShellSplitType(object): +class TestShellSplitType(unittest.TestCase): def test_split(self): shell_split_type = types.ShellSplitType() split = shell_split_type('-BAR="foo bar"') - self.assertEqual(split, ['-BAR="foo bar"']) + self.assertEqual(split, ['-BAR=foo bar']) split = shell_split_type('-BAR="foo bar" -BAZ="foo,bar",-QUX 42') self.assertEqual(split, [ - '-BAR="foo bar"', - '-BAZ="foo,bar"', + '-BAR=foo bar', + '-BAZ=foo,bar', '-QUX', '42', ]) diff --git a/utils/build_swift/tests/build_swift/test_cache_utils.py b/utils/build_swift/tests/build_swift/test_cache_utils.py new file mode 100644 index 0000000000000..8dba0d434c49c --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_cache_utils.py @@ -0,0 +1,121 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift import cache_utils + +from .. import utils + + +try: + # Python 3.3 + from unittest import mock +except ImportError: + mock = None + + +class _CallCounter(object): + """Callable helper class used to count and return the number of times an + instance has been called. + """ + + def __init__(self): + self._counter = 0 + + def __call__(self, *args, **kwargs): + count = self._counter + self._counter += 1 + return count + + +class TestCache(unittest.TestCase): + """Unit tests for the cache decorator in the cache_utils module. + """ + + @utils.requires_module('unittest.mock') + @utils.requires_python('3.2') # functools.lru_cache + def test_replaced_with_functools_lru_cache_python_3_2(self): + with mock.patch('functools.lru_cache') as mock_lru_cache: + @cache_utils.cache + def func(): + return None + + mock_lru_cache.assert_called() + + def test_call_with_no_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(), 0) + self.assertEqual(func(), 0) + + def test_call_with_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(0), 0) + self.assertEqual(func(0), 0) + + self.assertEqual(func(1), 1) + self.assertEqual(func(1), 1) + + self.assertEqual(func(2), 2) + self.assertEqual(func(2), 2) + + def test_call_with_args_and_kwargs(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(n=0), 0) + self.assertEqual(func(n=0), 0) + + self.assertEqual(func(a=1, b='b'), 1) + self.assertEqual(func(a=1, b='b'), 1) + + self.assertEqual(func(0, x=1, y=2.0), 2) + self.assertEqual(func(0, x=1, y=2.0), 2) + + +class TestReify(unittest.TestCase): + """Unit tests for the reify decorator in the cache_utils module. + """ + + def test_replaces_attr_after_first_call(self): + class Counter(object): + def __init__(self): + self._counter = 0 + + @cache_utils.reify + def count(self): + count = self._counter + self._counter += 1 + return count + + counter = Counter() + + self.assertEqual(counter.count, 0) + self.assertEqual(counter.count, 0) + + # Assert that the count property has been replaced with the constant. + self.assertEqual(getattr(counter, 'count'), 0) diff --git a/utils/build_swift/tests/build_swift/test_driver_arguments.py b/utils/build_swift/tests/build_swift/test_driver_arguments.py new file mode 100644 index 0000000000000..7811b6dd6d61e --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_driver_arguments.py @@ -0,0 +1,635 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import os +import platform +import sys +import unittest + +from build_swift import argparse +from build_swift import driver_arguments +from build_swift import migration +from build_swift.presets import PresetParser + +import six + +from .test_presets import PRESET_DEFAULTS +from .. import expected_options as eo +from .. import utils + + +PRESETS_FILES = [ + os.path.join(utils.UTILS_PATH, 'build-presets.ini'), +] + + +class ParserError(Exception): + pass + + +def _load_all_presets(preset_files): + parser = PresetParser() + parser.read_files(preset_files) + + # Hack to filter out mixins which are not expected to be valid presets + preset_names = [ + name for name in parser.preset_names + if not name.startswith('mixin') + ] + + presets = dict() + for name in preset_names: + preset = parser.get_preset(name, vars=PRESET_DEFAULTS) + args = migration.migrate_swift_sdks(preset.args) + + presets[name] = args + + return presets + + +class TestDriverArgumentParserMeta(type): + """Metaclass used to dynamically generate test methods for each of the + individual options accepted by the parser and methods to validate all of + the presets. + """ + + def __new__(cls, name, bases, attrs): + # Generate tests for each default value + for dest, value in eo.EXPECTED_DEFAULTS.items(): + test_name = 'test_default_value_{}'.format(dest) + attrs[test_name] = cls.generate_default_value_test(dest, value) + + # Generate tests for each expected option + for option in eo.EXPECTED_OPTIONS: + test_name = 'test_option_{}'.format(option.sanitized_string()) + attrs[test_name] = cls.generate_option_test(option) + + # Generate tests for each preset + presets = _load_all_presets(PRESETS_FILES) + + for name, args in presets.items(): + test_name = 'test_preset_{}'.format(name) + attrs[test_name] = cls.generate_preset_test(name, args) + + if six.PY2: + name = str(name) + + return super(TestDriverArgumentParserMeta, cls).__new__( + cls, name, bases, attrs) + + @classmethod + def generate_default_value_test(cls, dest, default_value): + def test(self): + parsed_values = self.parse_default_args([]) + + parsed_value = getattr(parsed_values, dest) + if default_value.__class__ in six.string_types: + parsed_value = six.text_type(parsed_value) + + self.assertEqual(default_value, parsed_value, + 'Invalid default value for "{}": {} != {}' + .format(dest, default_value, parsed_value)) + + return test + + @classmethod + def _generate_help_option_test(cls, option): + def test(self): + with utils.redirect_stdout() as output: + with self.assertRaises(ParserError): + self.parse_args([option.option_string]) + self.assertNotEmpty(output) + + return test + + @classmethod + def _generate_set_option_test(cls, option): + def test(self): + namespace = self.parse_args([option.option_string]) + self.assertEqual(getattr(namespace, option.dest), option.value) + + with self.assertRaises(ParserError): + self.parse_args([option.option_string, 'foo']) + + return test + + @classmethod + def _generate_set_true_option_test(cls, option): + def test(self): + # TODO: Move to unit-tests for the action class + namespace = self.parse_args([]) + self.assertFalse(getattr(namespace, option.dest)) + + namespace = self.parse_args([option.option_string]) + self.assertTrue(getattr(namespace, option.dest)) + + return test + + @classmethod + def _generate_set_false_option_test(cls, option): + def test(self): + # TODO: Move to unit-tests for the action class + namespace = self.parse_args([]) + self.assertTrue(getattr(namespace, option.dest)) + + namespace = self.parse_args([option.option_string]) + self.assertFalse(getattr(namespace, option.dest)) + + return test + + @classmethod + def _generate_enable_option_test(cls, option): + def test(self): + # TODO: Move to unit-tests for the action class + # Test parsing True values + self.parse_args([option.option_string, '1']) + self.parse_args([option.option_string, 'true']) + self.parse_args([option.option_string, 'True']) + self.parse_args([option.option_string, 'TRUE']) + + # TODO: Move to unit-tests for the action class + # Test parsing False values + self.parse_args([option.option_string, '0']) + self.parse_args([option.option_string, 'false']) + self.parse_args([option.option_string, 'False']) + self.parse_args([option.option_string, 'FALSE']) + + # TODO: Move to unit-tests for the action class + # Test default value + namespace = self.parse_args([option.option_string]) + self.assertTrue(getattr(namespace, option.dest)) + + # Test setting value to True + namespace = self.parse_args([option.option_string, 'True']) + self.assertTrue(getattr(namespace, option.dest)) + + # Test setting value to False + namespace = self.parse_args([option.option_string, 'False']) + self.assertFalse(getattr(namespace, option.dest)) + + return test + + @classmethod + def _generate_disable_option_test(cls, option): + def test(self): + # TODO: Move to unit-tests for the action class + # Test parsing True values + self.parse_args([option.option_string, '1']) + self.parse_args([option.option_string, 'true']) + self.parse_args([option.option_string, 'True']) + self.parse_args([option.option_string, 'TRUE']) + + # TODO: Move to unit-tests for the action class + # Test parsing False values + self.parse_args([option.option_string, '0']) + self.parse_args([option.option_string, 'false']) + self.parse_args([option.option_string, 'False']) + self.parse_args([option.option_string, 'FALSE']) + + # TODO: Move to unit-tests for the action class + # Test default value + namespace = self.parse_args([option.option_string]) + self.assertFalse(getattr(namespace, option.dest)) + + # Test setting value to True resulting in False + namespace = self.parse_args([option.option_string, 'True']) + self.assertFalse(getattr(namespace, option.dest)) + + # Test setting value to False resulting in True + namespace = self.parse_args([option.option_string, 'False']) + self.assertTrue(getattr(namespace, option.dest)) + + return test + + @classmethod + def _generate_choices_option_test(cls, option): + def test(self): + for choice in option.choices: + namespace = self.parse_args( + [option.option_string, six.text_type(choice)]) + self.assertEqual(getattr(namespace, option.dest), choice) + + with self.assertRaises(ParserError): + self.parse_args([option.option_string, 'INVALID']) + + return test + + @classmethod + def _generate_int_option_test(cls, option): + def test(self): + for i in [0, 1, 42]: + namespace = self.parse_args( + [option.option_string, six.text_type(i)]) + self.assertEqual(int(getattr(namespace, option.dest)), i) + + # FIXME: int-type options should not accept non-int strings + # self.parse_args([option.option_string, six.text_type(0.0)]) + # self.parse_args([option.option_string, six.text_type(1.0)]) + # self.parse_args([option.option_string, six.text_type(3.14)]) + # self.parse_args([option.option_string, 'NaN']) + + return test + + @classmethod + def _generate_str_option_test(cls, option): + def test(self): + self.parse_args([option.option_string, 'foo']) + + return test + + @classmethod + def _generate_path_option_test(cls, option): + def test(self): + self.parse_args([option.option_string, sys.executable]) + + # FIXME: path-type options should not accept non-path inputs + # self.parse_args([option.option_string, 'foo']) + + return test + + @classmethod + def _generate_append_option_test(cls, option): + def test(self): + # Range size is arbitrary, just needs to be more than once + for i in range(1, 4): + namespace = self.parse_args([option.option_string, 'ARG'] * i) + self.assertEqual(getattr(namespace, option.dest), ['ARG'] * i) + + return test + + @classmethod + def _generate_unsupported_option_test(cls, option): + def test(self): + with self.assertRaises(ParserError): + self.parse_args([option.option_string]) + + return test + + @classmethod + def _generate_build_script_impl_option_test(cls, option): + def test(self): + namespace, unknown_args = self.parse_args_and_unknown_args([]) + self.assertFalse(hasattr(namespace, option.dest)) + self.assertEqual(unknown_args, []) + + namespace, unknown_args = self.parse_args_and_unknown_args( + [option.option_string]) + # The argument should never show up in the namespace + self.assertFalse(hasattr(namespace, option.dest)) + # It should instead be forwareded to unkown_args + self.assertEqual(unknown_args, [option.option_string]) + + return test + + @classmethod + def generate_option_test(cls, option): + generate_test_funcs = { + eo.HelpOption: cls._generate_help_option_test, + eo.SetOption: cls._generate_set_option_test, + eo.SetTrueOption: cls._generate_set_true_option_test, + eo.SetFalseOption: cls._generate_set_false_option_test, + eo.EnableOption: cls._generate_enable_option_test, + eo.DisableOption: cls._generate_disable_option_test, + eo.ChoicesOption: cls._generate_choices_option_test, + eo.IntOption: cls._generate_int_option_test, + eo.StrOption: cls._generate_str_option_test, + eo.PathOption: cls._generate_path_option_test, + eo.AppendOption: cls._generate_append_option_test, + eo.UnsupportedOption: cls._generate_unsupported_option_test, + eo.BuildScriptImplOption: + cls._generate_build_script_impl_option_test, + + # IgnoreOptions should be manually tested + eo.IgnoreOption: lambda self: None, + } + + test_func = generate_test_funcs.get(option.__class__, None) + if test_func is not None: + return test_func(option) + + # Catch-all meaningless test + return lambda self: \ + self.fail('unexpected option "{}"'.format(option.option_string)) + + @classmethod + def generate_preset_test(cls, preset_name, preset_args): + def test(self): + try: + # Windows cannot run build-script-impl to check the impl args. + is_windows = platform.system() == 'Windows' + self.parse_default_args(preset_args, + check_impl_args=not is_windows) + except ParserError as e: + self.fail('failed to parse preset "{}": {}'.format( + preset_name, e)) + + return test + + +@six.add_metaclass(TestDriverArgumentParserMeta) +class TestDriverArgumentParser(unittest.TestCase): + + def _parse_args(self, args): + try: + return migration.parse_args(self.parser, args) + except (SystemExit, ValueError) as e: + raise ParserError('failed to parse arguments: {}'.format( + six.text_type(args), e)) + + def _check_impl_args(self, namespace): + assert hasattr(namespace, 'build_script_impl_args') + + try: + migration.check_impl_args( + utils.BUILD_SCRIPT_IMPL_PATH, + namespace.build_script_impl_args) + except (SystemExit, ValueError) as e: + raise ParserError('failed to parse impl arguments: {}'.format( + six.text_type(namespace.build_script_impl_args), e)) + + def parse_args_and_unknown_args(self, args, namespace=None): + if namespace is None: + namespace = argparse.Namespace() + + with utils.quiet_output(): + try: + namespace, unknown_args = ( + super(self.parser.__class__, self.parser).parse_known_args( + args, namespace)) + namespace, unknown_args = ( + migration._process_disambiguation_arguments( + namespace, unknown_args)) + except (SystemExit, argparse.ArgumentError) as e: + raise ParserError('failed to parse arguments: {}'.format( + six.text_type(args), e)) + + return namespace, unknown_args + + def parse_args(self, args, namespace=None): + namespace, unknown_args = self.parse_args_and_unknown_args( + args, namespace) + + if unknown_args: + raise ParserError('unknown arguments: {}'.format( + six.text_type(unknown_args))) + + return namespace + + def parse_default_args(self, args, check_impl_args=False): + with utils.quiet_output(): + namespace = self._parse_args(args) + + if check_impl_args: + self._check_impl_args(namespace) + + return namespace + + def setUp(self): + self.parser = driver_arguments.create_argument_parser() + + # ------------------------------------------------------------------------- + + def test_expected_options_exhaustive(self): + """Test that we are exhaustively testing all options accepted by the + parser. If this test if failing then the parser accepts more options + than currently being tested, meaning the EXPECTED_OPTIONS list in + build_swift/tests/expected_options.py should be updated to include + the missing options. + """ + + expected_options = {o.option_string for o in eo.EXPECTED_OPTIONS} + + # aggregate and flatten the options_strings accepted by the parser + actual_options = [a.option_strings for a in self.parser._actions] + actual_options = set(sum(actual_options, [])) + + diff = actual_options - expected_options + + if len(diff) > 0: + self.fail('non-exhaustive expected options, missing: {}' + .format(diff)) + + def test_expected_options_have_default_values(self): + """Test that all the options in EXPECTED_OPTIONS have an associated + default value. + """ + + skip_option_classes = [ + eo.HelpOption, + eo.IgnoreOption, + eo.UnsupportedOption, + eo.BuildScriptImplOption, + ] + + missing_defaults = set() + for option in eo.EXPECTED_OPTIONS: + if option.__class__ in skip_option_classes: + continue + + if option.dest not in eo.EXPECTED_DEFAULTS: + missing_defaults.add(option.dest) + + if len(missing_defaults) > 0: + self.fail('non-exhaustive default values for options, missing: {}' + .format(missing_defaults)) + + # ------------------------------------------------------------------------- + # Manual option tests + + def test_option_clang_compiler_version(self): + option_string = '--clang-compiler-version' + + self.parse_default_args([option_string, '5.0.0']) + self.parse_default_args([option_string, '5.0.1']) + self.parse_default_args([option_string, '5.0.0.1']) + + with self.assertRaises(ParserError): + self.parse_default_args([option_string, '1']) + self.parse_default_args([option_string, '1.2']) + self.parse_default_args([option_string, '0.0.0.0.1']) + + def test_option_clang_user_visible_version(self): + option_string = '--clang-user-visible-version' + + self.parse_default_args([option_string, '5.0.0']) + self.parse_default_args([option_string, '5.0.1']) + self.parse_default_args([option_string, '5.0.0.1']) + + with self.assertRaises(ParserError): + self.parse_default_args([option_string, '1']) + self.parse_default_args([option_string, '1.2']) + self.parse_default_args([option_string, '0.0.0.0.1']) + + def test_option_swift_compiler_version(self): + option_string = '--swift-compiler-version' + + self.parse_default_args([option_string, '4.1']) + self.parse_default_args([option_string, '4.0.1']) + self.parse_default_args([option_string, '200.99.1']) + + with self.assertRaises(ParserError): + self.parse_default_args([option_string, '1']) + self.parse_default_args([option_string, '0.0.0.1']) + + def test_option_swift_user_visible_version(self): + option_string = '--swift-user-visible-version' + + self.parse_default_args([option_string, '4.1']) + self.parse_default_args([option_string, '4.0.1']) + self.parse_default_args([option_string, '200.99.1']) + + with self.assertRaises(ParserError): + self.parse_default_args([option_string, '1']) + self.parse_default_args([option_string, '0.0.0.1']) + + def test_option_I(self): + with self.assertRaises(ValueError): + self.parse_default_args(['-I']) + + def test_option_ios_all(self): + with self.assertRaises(ValueError): + self.parse_default_args(['--ios-all']) + + def test_option_tvos_all(self): + with self.assertRaises(ValueError): + self.parse_default_args(['--tvos-all']) + + def test_option_watchos_all(self): + with self.assertRaises(ValueError): + self.parse_default_args(['--watchos-all']) + + # ------------------------------------------------------------------------- + # Implied defaults tests + + def test_implied_defaults_assertions(self): + namespace = self.parse_default_args(['--assertions']) + + self.assertTrue(namespace.cmark_assertions) + self.assertTrue(namespace.llvm_assertions) + self.assertTrue(namespace.swift_assertions) + self.assertTrue(namespace.swift_stdlib_assertions) + + def test_implied_defaults_cmark_build_variant(self): + namespace = self.parse_default_args(['--debug-cmark']) + self.assertTrue(namespace.build_cmark) + + def test_implied_defaults_lldb_build_variant(self): + namespace = self.parse_default_args(['--debug-lldb']) + self.assertTrue(namespace.build_lldb) + + namespace = self.parse_default_args(['--lldb-assertions']) + self.assertTrue(namespace.build_lldb) + + def test_implied_defaults_build_variant(self): + namespace = self.parse_default_args(['--debug']) + + self.assertEqual(namespace.cmark_build_variant, 'Debug') + self.assertEqual(namespace.foundation_build_variant, 'Debug') + self.assertEqual(namespace.libdispatch_build_variant, 'Debug') + self.assertEqual(namespace.libicu_build_variant, 'Debug') + self.assertEqual(namespace.lldb_build_variant, 'Debug') + self.assertEqual(namespace.llvm_build_variant, 'Debug') + self.assertEqual(namespace.swift_build_variant, 'Debug') + self.assertEqual(namespace.swift_stdlib_build_variant, 'Debug') + + def test_implied_defaults_skip_build_ios(self): + namespace = self.parse_default_args(['--skip-build-ios']) + self.assertFalse(namespace.build_ios_device) + self.assertFalse(namespace.build_ios_simulator) + + # Also implies that the tests should be skipped + self.assertFalse(namespace.test_ios_host) + self.assertFalse(namespace.test_ios_simulator) + + def test_implied_defaults_skip_build_tvos(self): + namespace = self.parse_default_args(['--skip-build-tvos']) + self.assertFalse(namespace.build_tvos_device) + self.assertFalse(namespace.build_tvos_simulator) + + # Also implies that the tests should be skipped + self.assertFalse(namespace.test_tvos_host) + self.assertFalse(namespace.test_tvos_simulator) + + def test_implied_defaults_skip_build_watchos(self): + namespace = self.parse_default_args(['--skip-build-watchos']) + self.assertFalse(namespace.build_watchos_device) + self.assertFalse(namespace.build_watchos_simulator) + + # Also implies that the tests should be skipped + self.assertFalse(namespace.test_watchos_host) + self.assertFalse(namespace.test_watchos_simulator) + + def test_implied_defaults_validation_test(self): + namespace = self.parse_default_args(['--validation-test']) + self.assertTrue(namespace.test) + + def test_implied_defaults_test_optimized(self): + namespace = self.parse_default_args(['--test-optimized']) + self.assertTrue(namespace.test) + + def test_implied_defaults_test_optimize_for_size(self): + namespace = self.parse_default_args(['--test-optimize-for-size']) + self.assertTrue(namespace.test) + + def test_implied_defaults_test_optimize_none_with_implicit_dynamic(self): + namespace = self.parse_default_args( + ['--test-optimize-none-with-implicit-dynamic']) + self.assertTrue(namespace.test) + + def test_implied_defaults_skip_all_tests(self): + namespace = self.parse_default_args([ + '--test', '0', + '--validation-test', '0', + '--long-test', '0', + '--stress-test', '0', + ]) + + self.assertFalse(namespace.test_linux) + self.assertFalse(namespace.test_freebsd) + self.assertFalse(namespace.test_cygwin) + self.assertFalse(namespace.test_osx) + self.assertFalse(namespace.test_ios) + self.assertFalse(namespace.test_tvos) + self.assertFalse(namespace.test_watchos) + + def test_implied_defaults_skip_test_ios(self): + namespace = self.parse_default_args(['--skip-test-ios']) + self.assertFalse(namespace.test_ios_host) + self.assertFalse(namespace.test_ios_simulator) + + def test_implied_defaults_skip_test_tvos(self): + namespace = self.parse_default_args(['--skip-test-tvos']) + self.assertFalse(namespace.test_tvos_host) + self.assertFalse(namespace.test_tvos_simulator) + + def test_implied_defaults_skip_test_watchos(self): + namespace = self.parse_default_args(['--skip-test-watchos']) + self.assertFalse(namespace.test_watchos_host) + self.assertFalse(namespace.test_watchos_simulator) + + def test_implied_defaults_skip_build_android(self): + namespace = self.parse_default_args(['--android', '0']) + self.assertFalse(namespace.test_android_host) + + namespace = self.parse_default_args(['--skip-build-android']) + self.assertFalse(namespace.test_android_host) + + def test_implied_defaults_host_test(self): + namespace = self.parse_default_args(['--host-test', '0']) + self.assertFalse(namespace.test_ios_host) + self.assertFalse(namespace.test_tvos_host) + self.assertFalse(namespace.test_watchos_host) + self.assertFalse(namespace.test_android_host) + self.assertFalse(namespace.build_libparser_only) + + def test_build_lib_swiftsyntaxparser_only(self): + namespace = self.parse_default_args(['--build-libparser-only']) + self.assertTrue(namespace.build_libparser_only) diff --git a/utils/build_swift/tests/build_swift/test_migration.py b/utils/build_swift/tests/build_swift/test_migration.py new file mode 100644 index 0000000000000..ff300bf279689 --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_migration.py @@ -0,0 +1,174 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import platform +import unittest + +from build_swift import argparse +from build_swift import migration + +import six + +from .. import utils + + +# ----------------------------------------------------------------------------- +# Helpers + +def _get_sdk_targets(sdk_names): + targets = [] + for sdk_name in sdk_names: + targets += migration._SDK_TARGETS[sdk_name] + + return targets + + +def _get_sdk_target_names(sdk_names): + return [target.name for target in _get_sdk_targets(sdk_names)] + + +# ----------------------------------------------------------------------------- +# Mirgrate Swift SDKs + +class TestMigrateSwiftSDKsMeta(type): + """Metaclass used to dynamically generate test methods. + """ + + def __new__(cls, name, bases, attrs): + # Generate tests for migrating each Swift SDK + for sdk_name in migration._SDK_TARGETS.keys(): + test_name = 'test_migrate_swift_sdk_{}'.format(sdk_name) + attrs[test_name] = cls.generate_migrate_swift_sdks_test(sdk_name) + + return super(TestMigrateSwiftSDKsMeta, cls).__new__( + cls, name, bases, attrs) + + @classmethod + def generate_migrate_swift_sdks_test(cls, sdk_name): + def test(self): + args = ['--swift-sdks={}'.format(sdk_name)] + args = migration.migrate_swift_sdks(args) + + target_names = _get_sdk_target_names([sdk_name]) + self.assertListEqual(args, [ + '--stdlib-deployment-targets={}'.format(' '.join(target_names)) + ]) + + return test + + +@six.add_metaclass(TestMigrateSwiftSDKsMeta) +class TestMigrateSwiftSDKs(unittest.TestCase): + + def test_empty_swift_sdks(self): + args = migration.migrate_swift_sdks(['--swift-sdks=']) + self.assertListEqual(args, ['--stdlib-deployment-targets=']) + + def test_multiple_swift_sdk_flags(self): + sdks = ['OSX', 'IOS', 'IOS_SIMULATOR'] + + args = [ + '--swift-sdks=', + '--swift-sdks={}'.format(';'.join(sdks)) + ] + + args = migration.migrate_swift_sdks(args) + target_names = _get_sdk_target_names(sdks) + + self.assertListEqual(args, [ + '--stdlib-deployment-targets=', + '--stdlib-deployment-targets={}'.format(' '.join(target_names)) + ]) + + +# ----------------------------------------------------------------------------- + +class TestMigrateParseArgs(unittest.TestCase): + + def test_report_unknown_args(self): + parser = argparse.ArgumentParser() + parser.add_argument('-R', '--release', action='store_true') + parser.add_argument('-T', '--validation-test', action='store_true') + parser.add_argument('--darwin-xcrun-toolchain') + + args = migration.parse_args(parser, [ + '-RT', + '--unknown', 'true', + '--darwin-xcrun-toolchain=foo', + '--', + '--darwin-xcrun-toolchain=bar', + '--other', + ]) + + expected = argparse.Namespace( + release=True, + validation_test=True, + darwin_xcrun_toolchain='bar', + build_script_impl_args=['--unknown', 'true', '--other']) + + self.assertEqual(args, expected) + + def test_no_unknown_args(self): + parser = argparse.ArgumentParser() + parser.add_argument('-R', '--release', action='store_true') + parser.add_argument('-T', '--validation-test', action='store_true') + parser.add_argument('--darwin-xcrun-toolchain') + + args = migration.parse_args( + parser, ['-RT', '--darwin-xcrun-toolchain=bar']) + + expected = argparse.Namespace( + release=True, + validation_test=True, + darwin_xcrun_toolchain='bar', + build_script_impl_args=[]) + + self.assertEqual(args, expected) + + def test_forward_impl_args(self): + parser = argparse.ArgumentParser() + parser.add_argument('--skip-test-swift', + dest='impl_skip_test_swift', + action='store_true') + parser.add_argument('--install-swift', + dest='impl_install_swift', + action='store_true') + + args = migration.parse_args( + parser, ['--skip-test-swift', '--install-swift']) + + expected = argparse.Namespace( + build_script_impl_args=['--skip-test-swift', '--install-swift']) + + self.assertEqual(args, expected) + + +class TestMigrationCheckImplArgs(unittest.TestCase): + + def test_check_impl_args(self): + if platform.system() == 'Windows': + self.skipTest("build-script-impl cannot run in Windows") + return + + self.assertIsNone(migration.check_impl_args( + utils.BUILD_SCRIPT_IMPL_PATH, ['--reconfigure'])) + + with self.assertRaises(ValueError) as cm: + migration.check_impl_args( + utils.BUILD_SCRIPT_IMPL_PATH, ['foo']) + + self.assertIn('foo', str(cm.exception)) + + with self.assertRaises(ValueError) as cm: + migration.check_impl_args( + utils.BUILD_SCRIPT_IMPL_PATH, ['--reconfigure', '--foo=true']) + + self.assertIn('foo', str(cm.exception)) diff --git a/utils/build_swift/tests/test_presets.py b/utils/build_swift/tests/build_swift/test_presets.py similarity index 74% rename from utils/build_swift/tests/test_presets.py rename to utils/build_swift/tests/build_swift/test_presets.py index 7967b22fa5b73..29b5779ffd5c3 100644 --- a/utils/build_swift/tests/test_presets.py +++ b/utils/build_swift/tests/build_swift/test_presets.py @@ -1,6 +1,6 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -10,20 +10,22 @@ from __future__ import unicode_literals import os +import unittest -from .utils import TestCase, UTILS_PATH, add_metaclass -from .. import presets -from ..presets import Preset, PresetParser +from build_swift import presets +from build_swift.presets import Preset, PresetParser -try: - # Python 2 - import ConfigParser as configparser -except ImportError: - import configparser +import six +from six.moves import configparser +from .. import utils + + +# ----------------------------------------------------------------------------- +# Constants PRESET_FILES = [ - os.path.join(UTILS_PATH, 'build-presets.ini'), + os.path.join(utils.UTILS_PATH, 'build-presets.ini'), ] PRESET_DEFAULTS = { @@ -111,12 +113,12 @@ # ----------------------------------------------------------------------------- -class TestPreset(TestCase): +class TestPreset(unittest.TestCase): - def test_format_args(self): - preset = Preset('sample', [('--ios', None), ('--test', '1')]) + def test_args(self): + preset = Preset('sample', [('ios', None), ('test', '1')]) - self.assertEqual(preset.format_args(), ['--ios', '--test=1']) + self.assertEqual(preset.args, ['--ios', '--test=1']) # ----------------------------------------------------------------------------- @@ -128,10 +130,10 @@ class TestPresetParserMeta(type): def __new__(cls, name, bases, attrs): preset_parser = PresetParser() - preset_parser.read(PRESET_FILES) + preset_parser.read_files(PRESET_FILES) # Generate tests for each preset - for preset_name in preset_parser.preset_names(): + for preset_name in preset_parser.preset_names: test_name = 'test_get_preset_' + preset_name attrs[test_name] = cls.generate_get_preset_test( preset_parser, preset_name) @@ -147,27 +149,28 @@ def test(self): return test -@add_metaclass(TestPresetParserMeta) -class TestPresetParser(TestCase): +@six.add_metaclass(TestPresetParserMeta) +class TestPresetParser(unittest.TestCase): - def test_read(self): + def test_read_files(self): parser = PresetParser() - parser.read(PRESET_FILES) + parser.read_files(PRESET_FILES) def test_read_invalid_files(self): parser = PresetParser() with self.assertRaises(presets.UnparsedFilesError) as cm: - parser.read(['nonsense-presets.ini']) + parser.read_files(['nonsense-presets.ini']) e = cm.exception - self.assertListEqual(e.filenames, ['nonsense-presets.ini']) + unparsed = e.unparsed_files + self.assertEqual(len(unparsed), 1) + self.assertEqual(unparsed[0].filename, 'nonsense-presets.ini') + self.assertIsInstance(unparsed[0].reason, IOError) def test_read_file(self): parser = PresetParser() - - with self.assertNotRaises(): - parser.read_file(PRESET_FILES[0]) + parser.read_file(PRESET_FILES[0]) def test_read_string(self): parser = PresetParser() @@ -176,17 +179,17 @@ def test_read_string(self): preset = parser.get_preset('sample', vars={'install_symroot': '/tmp'}) self.assertIsNotNone(preset) self.assertEqual(preset.name, 'sample') - self.assertListEqual(preset.args, [ - ('--ios', None), - ('--tvos', None), - ('--watchos', None), - ('--test', None), - ('--validation-test', None), - ('--lit-args', '-v'), - ('--compiler-vendor', 'apple'), - ('--verbose-build', None), - ('--build-ninja', None), - ('--install-symroot', '/tmp') + self.assertListEqual(preset.options, [ + ('ios', None), + ('tvos', None), + ('watchos', None), + ('test', None), + ('validation-test', None), + ('lit-args', '-v'), + ('compiler-vendor', 'apple'), + ('verbose-build', None), + ('build-ninja', None), + ('install-symroot', '/tmp') ]) def test_parser_ignores_non_preset_sections(self): @@ -204,7 +207,7 @@ def test_mixin_expansion_preserves_argument_order(self): parser.read_string(MIXIN_ORDER_PRESETS) preset = parser.get_preset('test') - self.assertListEqual(preset.format_args(), [ + self.assertListEqual(preset.args, [ '--first-opt=1', # Mixin arguments @@ -223,15 +226,12 @@ def test_interpolation_error(self): e = cm.exception self.assertEqual(e.preset_name, 'test') - self.assertEqual(e.option, '--install-symroot') + self.assertEqual(e.option, 'install-symroot') self.assertEqual(e.rawval, '%(install_symroot)s') self.assertEqual(e.reference, 'install_symroot') + @utils.requires_attr(configparser, 'DuplicateOptionError') def test_duplicate_option_error(self): - # Skip test if using the Python 2 ConfigParser module - if not hasattr(configparser, 'DuplicateOptionError'): - return - parser = PresetParser() with self.assertRaises(presets.DuplicateOptionError) as cm: @@ -241,11 +241,8 @@ def test_duplicate_option_error(self): self.assertEqual(e.preset_name, 'test') self.assertEqual(e.option, 'ios') + @utils.requires_attr(configparser, 'DuplicateOptionError') def test_duplicate_preset_error(self): - # Skip test if using the Python 2 ConfigParser module - if not hasattr(configparser, 'DuplicateOptionError'): - return - parser = PresetParser() with self.assertRaises(presets.DuplicatePresetError) as cm: @@ -259,8 +256,8 @@ def test_get_preset_raw(self): parser.read_string(INTERPOLATED_PRESET) preset = parser.get_preset('test', raw=True) - self.assertEqual(preset.args, [ - ('--install-symroot', '%(install_symroot)s') + self.assertEqual(preset.options, [ + ('install-symroot', '%(install_symroot)s') ]) def test_get_missing_preset(self): @@ -279,5 +276,5 @@ def test_preset_names(self): parser.read_string('[preset: bar]') parser.read_string('[preset: baz]') - self.assertEqual(set(parser.preset_names()), + self.assertEqual(set(parser.preset_names), set(['foo', 'bar', 'baz'])) diff --git a/utils/build_swift/tests/build_swift/test_shell.py b/utils/build_swift/tests/build_swift/test_shell.py new file mode 100644 index 0000000000000..b74aeec0bdb59 --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_shell.py @@ -0,0 +1,836 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import collections +import sys +import unittest + +from build_swift import shell + +import six +from six import StringIO + +from .. import utils + + +try: + # Python 3.4 + from pathlib import Path +except ImportError: + pass + +try: + # Python 3.3 + from unittest import mock + from unittest.mock import patch, mock_open, MagicMock +except ImportError: + mock, mock_open = None, None + + class MagicMock(object): + def __init__(self, *args, **kwargs): + pass + + def _id(obj): + return obj + + def patch(*args, **kwargs): + return _id + + +# ----------------------------------------------------------------------------- +# Constants + +_OPEN_NAME = '{}.open'.format(six.moves.builtins.__name__) + + +# ----------------------------------------------------------------------------- +# Test Cases + +class TestHelpers(unittest.TestCase): + """Unit tests for the helper functions defined in the build_swift.shell + module. + """ + + # ------------------------------------------------------------------------- + # _flatmap + + def test_flatmap(self): + def duplicate(x): + return [x, x] + + result = shell._flatmap(duplicate, [1, 2, 3]) + + self.assertIsInstance(result, collections.Iterable) + self.assertEqual(list(result), [1, 1, 2, 2, 3, 3]) + + # ------------------------------------------------------------------------- + # _convert_pathlib_path + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('build_swift.shell.Path', None) + def test_convert_pathlib_path_pathlib_not_imported(self): + path = Path('/path/to/file.txt') + + self.assertEqual(shell._convert_pathlib_path(path), path) + + @utils.requires_module('pathlib') + def test_convert_pathlib_path(self): + path = Path('/path/to/file.txt') + + self.assertEqual(shell._convert_pathlib_path(''), '') + + self.assertEqual( + shell._convert_pathlib_path(path), + six.text_type(path)) + + # ------------------------------------------------------------------------- + # _get_stream_file + + def test_get_stream_file(self): + self.assertEqual(shell._get_stream_file(shell.PIPE), sys.stdout) + self.assertEqual(shell._get_stream_file(shell.STDOUT), sys.stdout) + + self.assertEqual(shell._get_stream_file(sys.stdout), sys.stdout) + self.assertEqual(shell._get_stream_file(sys.stderr), sys.stderr) + + def test_get_stream_file_raises_devnull(self): + with self.assertRaises(ValueError): + shell._get_stream_file(shell.DEVNULL) + + # ------------------------------------------------------------------------- + # _echo_command + + @utils.requires_module('unittest.mock') + def test_echo_command(self): + test_command = ['sudo', 'rm', '-rf', '/tmp/*'] + mock_stream = MagicMock() + + shell._echo_command(test_command, mock_stream) + + mock_stream.write.assert_called_with( + '>>> {}\n'.format(shell.quote(test_command))) + mock_stream.flush.assert_called() + + @utils.requires_module('unittest.mock') + def test_echo_command_custom_prefix(self): + mock_stream = MagicMock() + + shell._echo_command('ls', mock_stream, prefix='$ ') + + mock_stream.write.assert_called_with('$ ls\n') + mock_stream.flush.assert_called() + + # ------------------------------------------------------------------------- + # _normalize_args + + def test_normalize_args_splits_basestring(self): + command = 'rm -rf /Applications/Xcode.app' + + self.assertEqual( + shell._normalize_args(command), + ['rm', '-rf', '/Applications/Xcode.app']) + + def test_normalize_args_list_str(self): + command = ['rm', '-rf', '/Applications/Xcode.app'] + + self.assertEqual(shell._normalize_args(command), command) + + def test_normalize_args_converts_wrappers(self): + sudo = shell.wraps('sudo') + rm = shell.wraps('rm') + + command = [sudo, rm, '-rf', '/Applications/Xcode.app'] + + self.assertEqual( + shell._normalize_args(command), + ['sudo', 'rm', '-rf', '/Applications/Xcode.app']) + + def test_normalize_args_converts_complex_wrapper_commands(self): + sudo_rm_rf = shell.wraps('sudo rm -rf') + + command = [sudo_rm_rf, '/Applications/Xcode.app'] + + self.assertEqual( + shell._normalize_args(command), + ['sudo', 'rm', '-rf', '/Applications/Xcode.app']) + + @utils.requires_module('pathlib') + def test_normalize_args_accepts_single_wrapper_arg(self): + rm_xcode = shell.wraps(['rm', '-rf', Path('/Applications/Xcode.app')]) + + self.assertEqual( + shell._normalize_args(rm_xcode), + ['rm', '-rf', '/Applications/Xcode.app']) + + @utils.requires_module('pathlib') + def test_normalize_args_converts_pathlib_path(self): + command = ['rm', '-rf', Path('/Applications/Xcode.app')] + + self.assertEqual( + shell._normalize_args(command), + ['rm', '-rf', '/Applications/Xcode.app']) + + @utils.requires_module('pathlib') + def test_normalize_args_converts_pathlib_path_in_wrapper_commands(self): + rm_xcode = shell.wraps(['rm', '-rf', Path('/Applications/Xcode.app')]) + + self.assertEqual( + shell._normalize_args([rm_xcode]), + ['rm', '-rf', '/Applications/Xcode.app']) + + +class TestDecorators(unittest.TestCase): + """Unit tests for the decorators defined in the build_swift.shell module + used to backport or add functionality to the subprocess wrappers. + """ + + # ------------------------------------------------------------------------- + # _backport_devnull + + @utils.requires_module('unittest.mock') + @patch(_OPEN_NAME, new_callable=mock_open) + @patch('build_swift.shell._PY_VERSION', (3, 2)) + def test_backport_devnull_stdout_kwarg(self, mock_open): + mock_file = MagicMock() + mock_open.return_value.__enter__.return_value = mock_file + + @shell._backport_devnull + def func(command, **kwargs): + self.assertEqual(kwargs['stdout'], mock_file) + + func('', stdout=shell.DEVNULL) + mock_open.return_value.__enter__.assert_called() + mock_open.return_value.__exit__.assert_called() + + @utils.requires_module('unittest.mock') + @patch(_OPEN_NAME, new_callable=mock_open) + @patch('build_swift.shell._PY_VERSION', (3, 2)) + def test_backport_devnull_stderr_kwarg(self, mock_open): + mock_file = MagicMock() + mock_open.return_value.__enter__.return_value = mock_file + + @shell._backport_devnull + def func(command, **kwargs): + self.assertEqual(kwargs['stderr'], mock_file) + + func('', stderr=shell.DEVNULL) + mock_open.return_value.__enter__.assert_called() + mock_open.return_value.__exit__.assert_called() + + @utils.requires_module('unittest.mock') + @patch(_OPEN_NAME, new_callable=mock_open) + def test_backport_devnull_does_not_open(self, mock_open): + @shell._backport_devnull + def func(command): + pass + + func('') + mock_open.return_value.__enter__.assert_not_called() + mock_open.return_value.__exit__.assert_not_called() + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell._PY_VERSION', (3, 3)) + def test_backport_devnull_noop_starting_with_python_3_3(self): + def func(): + pass + + self.assertEqual(shell._backport_devnull(func), func) + + # ------------------------------------------------------------------------- + # _normalize_command + + def test_normalize_command_basestring_command_noop(self): + test_command = 'touch test.txt' + + @shell._normalize_command + def func(command): + self.assertEqual(command, test_command) + + func(test_command) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell._normalize_args') + def test_normalize_command(self, mock_normalize_args): + test_command = ['rm', '-rf', '/tmp/*'] + + @shell._normalize_command + def func(command): + pass + + func(test_command) + mock_normalize_args.assert_called_with(test_command) + + # ------------------------------------------------------------------------- + # _add_echo_kwarg + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell._echo_command') + def test_add_echo_kwarg_calls_echo_command(self, mock_echo_command): + test_command = ['rm', '-rf', '/tmp/*'] + + @shell._add_echo_kwarg + def func(command, **kwargs): + pass + + mock_stream = mock.mock_open() + + func(test_command, echo=True, stdout=mock_stream) + mock_echo_command.assert_called_with(test_command, mock_stream) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell._echo_command') + def test_add_echo_kwarg_noop_echo_false(self, mock_echo_command): + test_command = ['rm', '-rf', '/tmp/*'] + + @shell._add_echo_kwarg + def func(command): + pass + + func(test_command) + func(test_command, echo=False) + mock_echo_command.assert_not_called() + + +class TestPublicFunctions(unittest.TestCase): + """Unit tests for the public functions defined in the build_swift.shell + module. + """ + + # ------------------------------------------------------------------------- + # quote + + def test_quote_string(self): + self.assertEqual( + shell.quote('/Applications/App Store.app'), + "'/Applications/App Store.app'") + + def test_quote_iterable(self): + self.assertEqual( + shell.quote(['rm', '-rf', '~/Documents/My Homework']), + "rm -rf '~/Documents/My Homework'") + + # ------------------------------------------------------------------------- + # rerun_as_root + + def test_rerun_as_root(self): + pass + + +class TestSubprocessWrappers(unittest.TestCase): + """Unit tests for the subprocess wrappers defined in the build_swift.shell + module. + """ + + # ------------------------------------------------------------------------- + # Popen + + # NOTE: Testing the Popen class is harder than it might appear. We're not + # able to mock out the subprocess.Popen superclass as one might initially + # expect. Rather that shell.Popen class object already exists and inherts + # from subprocess.Popen, thus mocking it out does not change the behavior. + # Ultimately this class is merely a wrapper that uses already tested + # decorators to add functionality so testing here might not provide any + # benefit. + + # ------------------------------------------------------------------------- + # call + + @utils.requires_module('unittest.mock') + @patch('subprocess.call') + def test_call(self, mock_call): + shell.call('ls') + + mock_call.assert_called_with('ls') + + # ------------------------------------------------------------------------- + # check_call + + @utils.requires_module('unittest.mock') + @patch('subprocess.check_call') + def test_check_call(self, mock_check_call): + shell.check_call('ls') + + mock_check_call.assert_called_with('ls') + + # ------------------------------------------------------------------------- + # check_output + + @utils.requires_module('unittest.mock') + @patch('subprocess.check_output') + def test_check_output(self, mock_check_output): + # Before Python 3 the subprocess.check_output function returned bytes. + if six.PY3: + mock_check_output.return_value = '' + else: + mock_check_output.return_value = b'' + + output = shell.check_output('ls') + + # We always expect str (utf-8) output + self.assertIsInstance(output, six.text_type) + + if six.PY3: + mock_check_output.assert_called_with('ls', encoding='utf-8') + else: + mock_check_output.assert_called_with('ls') + + +class TestShellUtilities(unittest.TestCase): + """Unit tests for the shell utility wrappers defined in the + build_swift.shell module. + """ + + # ------------------------------------------------------------------------- + # copy + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('shutil.copyfile', MagicMock()) + @patch('build_swift.shell._convert_pathlib_path') + def test_copy_converts_pathlib_paths(self, mock_convert): + source = Path('/source/path') + dest = Path('/dest/path') + + shell.copy(source, dest) + + mock_convert.assert_has_calls([ + mock.call(source), + mock.call(dest), + ]) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('shutil.copyfile') + def test_copy_files(self, mock_copyfile): + source = '/source/path' + dest = '/dest/path' + + shell.copy(source, dest) + + mock_copyfile.assert_called_with(source, dest) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=False)) + @patch('os.path.isdir', MagicMock(return_value=True)) + @patch('shutil.copytree') + def test_copy_directories(self, mock_copytree): + source = '/source/path' + dest = '/dest/path' + + shell.copy(source, dest) + + mock_copytree.assert_called_with(source, dest) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('shutil.copyfile', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_copy_echos_fake_cp_file_command(self, mock_stdout): + source = '/source/path' + dest = '/dest/path' + + shell.copy(source, dest, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> cp {} {}\n'.format(source, dest)) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=False)) + @patch('os.path.isdir', MagicMock(return_value=True)) + @patch('shutil.copytree', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_copy_echos_fake_cp_directory_command(self, mock_stdout): + source = '/source/path' + dest = '/dest/path' + + shell.copy(source, dest, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> cp -R {} {}\n'.format(source, dest)) + + # ------------------------------------------------------------------------- + # pushd + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('os.getcwd', MagicMock(return_value='/start/path')) + @patch('build_swift.shell._convert_pathlib_path') + def test_pushd_converts_pathlib_path(self, mock_convert): + path = Path('/other/path') + mock_convert.return_value = six.text_type(path) + + shell.pushd(path) + + mock_convert.assert_called_with(path) + + @utils.requires_module('unittest.mock') + @patch('os.getcwd', MagicMock(return_value='/start/path')) + @patch('os.chdir') + def test_pushd_restores_cwd(self, mock_chdir): + with shell.pushd('/other/path'): + mock_chdir.assert_called_with('/other/path') + + mock_chdir.assert_called_with('/start/path') + + @utils.requires_module('unittest.mock') + @patch('os.getcwd', MagicMock(return_value='/start/path')) + @patch('os.chdir', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_pushd_echos_fake_pushd_popd_commands(self, mock_stdout): + with shell.pushd('/other/path', echo=True): + pass + + self.assertEqual(mock_stdout.getvalue().splitlines(), [ + '>>> pushd /other/path', + '>>> popd' + ]) + + # ------------------------------------------------------------------------- + # makedirs + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('os.path.exists', MagicMock(return_value=False)) + @patch('os.makedirs', MagicMock()) + @patch('build_swift.shell._convert_pathlib_path') + def test_makedirs_converts_pathlib_path(self, mock_convert): + path = Path('/some/directory') + + shell.makedirs(path) + + mock_convert.assert_called_with(path) + + @utils.requires_module('unittest.mock') + @patch('os.path.exists', MagicMock(return_value=True)) + @patch('os.makedirs') + def test_makedirs_noop_path_exists(self, mock_makedirs): + shell.makedirs('/some/directory') + + mock_makedirs.assert_not_called() + + @utils.requires_module('unittest.mock') + @patch('os.path.exists', MagicMock(return_value=False)) + @patch('os.makedirs') + def test_makedirs_creates_path(self, mock_makedirs): + path = '/some/directory' + + shell.makedirs(path) + + mock_makedirs.assert_called_with(path) + + @utils.requires_module('unittest.mock') + @patch('os.path.exists', MagicMock(return_value=False)) + @patch('os.makedirs', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_makedirs_echos_fake_mkdir_command(self, mock_stdout): + path = '/some/directory' + + shell.makedirs(path, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> mkdir -p {}\n'.format(path)) + + # ------------------------------------------------------------------------- + # move + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('shutil.move', MagicMock()) + @patch('build_swift.shell._convert_pathlib_path') + def test_move_converts_pathlib_paths(self, mock_convert): + source = Path('/source/path') + dest = Path('/dest/path') + + shell.move(source, dest) + + mock_convert.assert_has_calls([ + mock.call(source), + mock.call(dest), + ]) + + @utils.requires_module('unittest.mock') + @patch('shutil.move') + def test_move(self, mock_move): + source = '/source/path' + dest = '/dest/path' + + shell.move(source, dest) + + mock_move.assert_called_with(source, dest) + + @utils.requires_module('unittest.mock') + @patch('shutil.move', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_move_echos_fake_mv_command(self, mock_stdout): + source = '/source/path' + dest = '/dest/path' + + shell.move(source, dest, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> mv {} {}\n'.format(source, dest)) + + # ------------------------------------------------------------------------- + # remove + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('os.remove', MagicMock()) + @patch('build_swift.shell._convert_pathlib_path') + def test_remove_converts_pathlib_paths(self, mock_convert): + path = Path('/path/to/remove') + + shell.remove(path) + + mock_convert.assert_called_with(path) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('os.remove') + def test_remove_files(self, mock_remove): + path = '/path/to/remove' + + shell.remove(path) + + mock_remove.assert_called_with(path) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=False)) + @patch('os.path.isdir', MagicMock(return_value=True)) + @patch('shutil.rmtree') + def test_remove_directories(self, mock_rmtree): + path = '/path/to/remove' + + shell.remove(path) + + mock_rmtree.assert_called_with(path, ignore_errors=True) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('os.remove', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_remove_echos_fake_rm_file_command(self, mock_stdout): + path = '/path/to/remove' + + shell.remove(path, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> rm {}\n'.format(path)) + + @utils.requires_module('unittest.mock') + @patch('os.path.isfile', MagicMock(return_value=False)) + @patch('os.path.isdir', MagicMock(return_value=True)) + @patch('shutil.rmtree', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_remove_echos_fake_rm_directory_command(self, mock_stdout): + path = '/path/to/remove' + + shell.remove(path, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> rm -rf {}\n'.format(path)) + + # ------------------------------------------------------------------------- + # symlink + + @utils.requires_module('unittest.mock') + @utils.requires_module('pathlib') + @patch('os.symlink', MagicMock()) + @patch('build_swift.shell._convert_pathlib_path') + def test_symlink_converts_pathlib_paths(self, mock_convert): + source = Path('/source/path') + dest = Path('/dest/path') + + shell.symlink(source, dest) + + mock_convert.assert_has_calls([ + mock.call(source), + mock.call(dest), + ]) + + @utils.requires_module('unittest.mock') + @patch('os.symlink') + def test_symlink(self, mock_symlink): + source = '/source/path' + dest = '/dest/path' + + shell.symlink(source, dest) + + mock_symlink.assert_called_with(source, dest) + + @utils.requires_module('unittest.mock') + @patch('os.symlink', MagicMock()) + @patch('sys.stdout', new_callable=StringIO) + def test_symlink_echos_fake_ln_command(self, mock_stdout): + source = '/source/path' + dest = '/dest/path' + + shell.symlink(source, dest, echo=True) + + self.assertEqual( + mock_stdout.getvalue(), + '>>> ln -s {} {}\n'.format(source, dest)) + + # ------------------------------------------------------------------------- + # which + + # NOTE: We currently have a polyfill for the shutil.which function. This + # will be swapped out for the real-deal as soon as we convert to Python 3, + # which should be in the near future. We could also use a backport package + # from pypi, but we rely on the shell module working in scripting that does + # not use a virtual environment at the moment. Until we either adopt + # Python 3 by default _or_ enforce virtual environments for all our scripts + # we are stuck with the polyfill. + + def test_which(self): + pass + + +class TestAbstractWrapper(unittest.TestCase): + """Unit tests for the AbstractWrapper class defined in the build_swift.shell + module. + """ + + def test_cannot_be_instantiated(self): + with self.assertRaises(TypeError): + shell.AbstractWrapper() + + +class TestCommandWrapper(unittest.TestCase): + """Unit tests for the CommandWrapper class defined in the build_swift.shell + module. + """ + + # ------------------------------------------------------------------------- + # wraps + + def test_wraps(self): + sudo = shell.wraps('sudo') + + self.assertIsInstance(sudo, shell.CommandWrapper) + self.assertEqual(sudo.command, ['sudo']) + + # ------------------------------------------------------------------------- + + @utils.requires_module('pathlib') + def test_command_normalized(self): + wrapper = shell.CommandWrapper(['ls', '-al', Path('/tmp')]) + + self.assertEqual(wrapper._command, ['ls', '-al', '/tmp']) + + def test_command_property(self): + git = shell.CommandWrapper('git') + + self.assertEqual(git.command, ['git']) + + @utils.requires_module('unittest.mock') + def test_callable(self): + ls = shell.CommandWrapper('ls') + + with patch.object(ls, 'check_call') as mock_check_call: + ls('-al') + + mock_check_call.assert_called_with('-al') + + # ------------------------------------------------------------------------- + # Subprocess Wrappers + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell.Popen') + def test_Popen(self, mock_popen): + ls = shell.CommandWrapper('ls') + + ls.Popen('-al') + + mock_popen.assert_called_with(['ls', '-al']) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell.call') + def test_call(self, mock_call): + ls = shell.CommandWrapper('ls') + + ls.call('-al') + + mock_call.assert_called_with(['ls', '-al']) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell.check_call') + def test_check_call(self, mock_check_call): + ls = shell.CommandWrapper('ls') + + ls.check_call('-al') + + mock_check_call.assert_called_with(['ls', '-al']) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell.check_output') + def test_check_output(self, mock_check_output): + ls = shell.CommandWrapper('ls') + + ls.check_output('-al') + + mock_check_output.assert_called_with(['ls', '-al']) + + +class TestExecutableWrapper(unittest.TestCase): + """Unit tests for the ExecutableWrapper class defined in the + build_swift.shell module. + """ + + def test_raises_without_executable(self): + class MyWrapper(shell.ExecutableWrapper): + pass + + with self.assertRaises(AttributeError): + MyWrapper() + + def test_raises_complex_executable(self): + class MyWrapper(shell.ExecutableWrapper): + EXECUTABLE = ['xcrun', 'swiftc'] + + with self.assertRaises(AttributeError): + MyWrapper() + + @utils.requires_module('pathlib') + def test_converts_pathlib_path(self): + class MyWrapper(shell.ExecutableWrapper): + EXECUTABLE = Path('/usr/local/bin/xbs') + + wrapper = MyWrapper() + + self.assertEqual(wrapper.EXECUTABLE, '/usr/local/bin/xbs') + + def test_command_property(self): + class MyWrapper(shell.ExecutableWrapper): + EXECUTABLE = 'test' + + wrapper = MyWrapper() + + self.assertEqual(wrapper.command, ['test']) + + @utils.requires_module('unittest.mock') + @patch('build_swift.shell.which') + def test_path_property(self, mock_which): + class MyWrapper(shell.ExecutableWrapper): + EXECUTABLE = 'test' + + wrapper = MyWrapper() + wrapper.path + + mock_which.assert_called_with('test') diff --git a/utils/build_swift/tests/build_swift/test_versions.py b/utils/build_swift/tests/build_swift/test_versions.py new file mode 100644 index 0000000000000..19447d63f679f --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_versions.py @@ -0,0 +1,96 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift.versions import Version + +import six + + +class TestVersion(unittest.TestCase): + """Unit tests for the Version class. + """ + + VERSION_COMPONENTS = { + '': (), + 'a': ('a',), + '0': (0,), + 'a0': ('a', 0), + 'a0b1': ('a', 0, 'b', 1), + '1.0': (1, 0), + '1.2.3': (1, 2, 3), + 'foo': ('foo',), + } + + # ------------------------------------------------------------------------- + # Helpers + + def assertVersionEqual(self, v1, v2): + v1 = Version(v1) + v2 = Version(v2) + + self.assertEqual(v1, v2) + self.assertEqual(v2, v1) + + def assertVersionNotEqual(self, v1, v2): + v1 = Version(v1) + v2 = Version(v2) + + self.assertNotEqual(v1, v2) + self.assertNotEqual(v2, v1) + + def assertVersionLess(self, v1, v2): + v1 = Version(v1) + v2 = Version(v2) + + self.assertLess(v1, v2) + self.assertGreater(v2, v1) + self.assertNotEqual(v1, v2) + + # ------------------------------------------------------------------------- + + def test_parse(self): + for string, components in six.iteritems(self.VERSION_COMPONENTS): + # Version parses + version = Version(string) + + self.assertEqual(version.components, components) + + def test_equal(self): + self.assertVersionEqual('', '') + self.assertVersionEqual('a', 'a') + self.assertVersionEqual('0', '0') + self.assertVersionEqual('a0', 'a0') + self.assertVersionEqual('1.0', '1.0') + self.assertVersionEqual('foo', 'foo') + + def test_not_equal(self): + self.assertVersionNotEqual('a', 'b') + self.assertVersionNotEqual('0', '1') + self.assertVersionNotEqual('a', '0') + self.assertVersionNotEqual('0a', 'a0') + self.assertVersionNotEqual('1.0', '1.1') + self.assertVersionNotEqual('foo', 'bar') + + def test_less_than(self): + self.assertVersionLess('0', '1') + self.assertVersionLess('a', 'b') + self.assertVersionLess('1.0', '1.1') + self.assertVersionLess('1a', '1b') + self.assertVersionLess('1aa', '1b') + self.assertVersionLess('a0b', 'a1') + + def test_str(self): + for string in six.iterkeys(self.VERSION_COMPONENTS): + version = Version(string) + + self.assertEqual(six.text_type(version), string) diff --git a/utils/build_swift/tests/build_swift/wrappers/__init__.py b/utils/build_swift/tests/build_swift/wrappers/__init__.py new file mode 100644 index 0000000000000..7c485c168e611 --- /dev/null +++ b/utils/build_swift/tests/build_swift/wrappers/__init__.py @@ -0,0 +1,7 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/utils/build_swift/tests/build_swift/wrappers/test_xcrun.py b/utils/build_swift/tests/build_swift/wrappers/test_xcrun.py new file mode 100644 index 0000000000000..858101aac61f3 --- /dev/null +++ b/utils/build_swift/tests/build_swift/wrappers/test_xcrun.py @@ -0,0 +1,354 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import os.path +import unittest + +from build_swift import shell +from build_swift.versions import Version +from build_swift.wrappers import _xcrun + +from ... import utils + + +try: + # Python 3 + from unittest import mock +except ImportError: + pass + + +# ----------------------------------------------------------------------------- +# Constants + +_UNKNOWN_TOOL = 'gcc' +_KNOWN_TOOL = 'clang' +_KNOWN_TOOL_PATH = os.path.join(os.sep, 'usr', 'bin', _KNOWN_TOOL) + +_UNKNOWN_SDK = 'phoneyos' +_KNOWN_SDK = 'macosx' +_KNOWN_SDK_PATH = os.path.join(os.sep, 'path', 'to', 'sdk') +_KNOWN_SDK_VERSION = '10.15' +_KNOWN_SDK_BUILD_VERSION = '10Z42b' +_KNOWN_SDK_PLATFORM_PATH = os.path.join(_KNOWN_SDK_PATH, 'platform') +_KNOWN_SDK_PLATFORM_VERSION = '10.15' + + +# ----------------------------------------------------------------------------- + +class TestXcrunHelpers(unittest.TestCase): + """Unit tests for the helper functions and decorators defined in + build_swift.wrappers.xcrun. + """ + + # ------------------------------------------------------------------------- + # _catch_return_none + + def test_catch_return_none_raises_returns_none(self): + @_xcrun._catch_return_none(Exception) + def raises(): + raise Exception + + self.assertIsNone(raises()) + + def test_catch_return_none_success_return_value(self): + @_xcrun._catch_return_none(Exception) + def succeeds(): + return True + + self.assertTrue(succeeds()) + + # ------------------------------------------------------------------------- + # _prepend_sdk_and_toolchain + + def test_prepend_sdk_and_toolchain(self): + class Test(object): + @_xcrun._prepend_sdk_and_toolchain + def method(self, args, **kwargs): + return args + + obj = Test() + + self.assertEqual(obj.method([]), []) + + self.assertEqual( + obj.method([], sdk='some-sdk'), + ['--sdk', 'some-sdk']) + + self.assertEqual( + obj.method([], toolchain='some-toolchain'), + ['--toolchain', 'some-toolchain']) + + self.assertEqual( + obj.method([], sdk='some-sdk', toolchain='some-toolchain'), + ['--sdk', 'some-sdk', '--toolchain', 'some-toolchain']) + + +class TestXcrunWrapper(unittest.TestCase): + """Unit tests for the XcrunWrapper defined in build_swift.wrappers.xcrun. + """ + + def setUp(self): + self.xcrun = _xcrun.XcrunWrapper() + + # ------------------------------------------------------------------------- + # version + + @utils.requires_module('unittest.mock') + def test_version_error_returns_none(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.version + + check_output.assert_called_with([self.xcrun.EXECUTABLE, '--version']) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_version_invalid_version_string(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = 'Invalid version output!\n' + + result = self.xcrun.version + + check_output.assert_called_with([self.xcrun.EXECUTABLE, '--version']) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_version(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = 'xcrun version 42.\n' + + result = self.xcrun.version + + check_output.assert_called_with([self.xcrun.EXECUTABLE, '--version']) + self.assertEqual(result, Version('42')) + + # ------------------------------------------------------------------------- + # find + + @utils.requires_module('unittest.mock') + def test_find_missing_tool(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.find(_UNKNOWN_TOOL, sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--find', _UNKNOWN_TOOL, + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_find_existing_tool(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format(_KNOWN_TOOL_PATH) + + result = self.xcrun.find(_KNOWN_TOOL, sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--find', _KNOWN_TOOL, + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, _KNOWN_TOOL_PATH) + + # ------------------------------------------------------------------------- + # kill_cache + + @utils.requires_module('unittest.mock') + def test_kill_cache(self): + with mock.patch.object(self.xcrun, 'call') as mock_call: + self.xcrun.kill_cache() + + mock_call.assert_called_with('--kill-cache') + + # ------------------------------------------------------------------------- + # sdk_path + + @utils.requires_module('unittest.mock') + def test_sdk_path_unknown_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.sdk_path(sdk=_UNKNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _UNKNOWN_SDK, + '--show-sdk-path', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_sdk_path_known_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format(_KNOWN_SDK_PATH) + + result = self.xcrun.sdk_path(sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--show-sdk-path', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, _KNOWN_SDK_PATH) + + # ------------------------------------------------------------------------- + # sdk_version + + @utils.requires_module('unittest.mock') + def test_sdk_version_unknown_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.sdk_version(sdk=_UNKNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _UNKNOWN_SDK, + '--show-sdk-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_sdk_version_known_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format(_KNOWN_SDK_VERSION) + + result = self.xcrun.sdk_version(sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--show-sdk-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, Version(_KNOWN_SDK_VERSION)) + + # ------------------------------------------------------------------------- + # sdk_build_version + + @utils.requires_module('unittest.mock') + def test_sdk_build_version_unknown_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.sdk_build_version(sdk=_UNKNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _UNKNOWN_SDK, + '--show-sdk-build-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_sdk_build_version_known_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format(_KNOWN_SDK_BUILD_VERSION) + + result = self.xcrun.sdk_build_version(sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--show-sdk-build-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, Version(_KNOWN_SDK_BUILD_VERSION)) + + # ------------------------------------------------------------------------- + # sdk_platform_path + + @utils.requires_module('unittest.mock') + def test_sdk_platform_path_unknown_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.sdk_platform_path(sdk=_UNKNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _UNKNOWN_SDK, + '--show-sdk-platform-path', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_sdk_platform_path_known_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format(_KNOWN_SDK_PLATFORM_PATH) + + result = self.xcrun.sdk_platform_path(sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--show-sdk-platform-path', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, _KNOWN_SDK_PLATFORM_PATH) + + # ------------------------------------------------------------------------- + # sdk_platform_version + + @utils.requires_module('unittest.mock') + def test_sdk_platform_version_unknown_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.side_effect = shell.CalledProcessError(-1, None) + + result = self.xcrun.sdk_platform_version(sdk=_UNKNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _UNKNOWN_SDK, + '--show-sdk-platform-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertIsNone(result) + + @utils.requires_module('unittest.mock') + def test_sdk_platform_version_known_sdk(self): + with mock.patch('build_swift.shell.check_output') as check_output: + check_output.return_value = '{}\n'.format( + _KNOWN_SDK_PLATFORM_VERSION) + + result = self.xcrun.sdk_platform_version(sdk=_KNOWN_SDK) + + command = [ + self.xcrun.EXECUTABLE, + '--sdk', _KNOWN_SDK, + '--show-sdk-platform-version', + ] + + check_output.assert_called_with(command, stderr=shell.DEVNULL) + self.assertEqual(result, Version(_KNOWN_SDK_PLATFORM_VERSION)) diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index 1d220620e57c2..38276cfe9c782 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -1,20 +1,22 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +from __future__ import absolute_import, unicode_literals + import multiprocessing +from build_swift import argparse +from build_swift import defaults + from swift_build_support.swift_build_support import host from swift_build_support.swift_build_support import targets -from .. import argparse -from .. import defaults - __all__ = [ 'HelpOption', @@ -56,6 +58,7 @@ 'build_android': False, 'build_args': [], 'build_benchmarks': True, + 'build_clang_tools_extra': True, 'build_cygwin': True, 'build_external_benchmarks': False, 'build_foundation': False, @@ -73,6 +76,7 @@ 'build_ninja': False, 'build_osx': True, 'build_playgroundsupport': False, + 'build_pythonkit': False, 'build_runtime_with_host_compiler': False, 'build_stdlib_deployment_targets': ['all'], 'build_subdir': None, @@ -88,7 +92,13 @@ 'build_swiftevolve': False, 'build_indexstoredb': False, 'build_sourcekitlsp': False, + 'install_swiftpm': False, + 'install_swiftsyntax': False, + 'swiftsyntax_verify_generated_files': False, + 'install_pythonkit': False, 'install_sourcekitlsp': False, + 'install_skstresstester': False, + 'install_swiftevolve': False, 'build_toolchainbenchmarks': False, 'build_tvos': True, 'build_tvos_device': False, @@ -123,6 +133,7 @@ 'distcc': False, 'dry_run': False, 'enable_asan': False, + 'enable_experimental_differentiable_programming': True, 'enable_lsan': False, 'enable_sanitize_coverage': False, 'disable_guaranteed_normal_arguments': False, @@ -163,6 +174,8 @@ 'tsan_libdispatch_test': False, 'long_test': False, 'lto_type': None, + 'maccatalyst': False, + 'maccatalyst_ios_tests': False, 'dump_config': False, 'show_sdks': False, 'skip_build': False, @@ -196,14 +209,20 @@ 'test_optimized': None, 'test_osx': False, 'test_paths': [], + 'test_pythonkit': False, 'test_tvos': False, 'test_tvos_host': False, 'test_tvos_simulator': False, 'test_watchos': False, 'test_watchos_host': False, 'test_watchos_simulator': False, + 'test_swiftpm': False, + 'test_swiftsyntax': False, 'test_indexstoredb': False, 'test_sourcekitlsp': False, + 'test_skstresstester': False, + 'test_swiftevolve': False, + 'test_toolchainbenchmarks': False, 'tvos': False, 'tvos_all': False, 'validation_test': None, @@ -341,6 +360,14 @@ class IgnoreOption(_BaseOption): pass +class BuildScriptImplOption(_BaseOption): + """Option that gets forwarded to build-script-impl by migration.py and is + only listed for disambiguation by argparse. + """ + + pass + + # ----------------------------------------------------------------------------- EXPECTED_OPTIONS = [ @@ -419,7 +446,12 @@ class IgnoreOption(_BaseOption): SetTrueOption('--llbuild', dest='build_llbuild'), SetTrueOption('--lldb', dest='build_lldb'), SetTrueOption('--libcxx', dest='build_libcxx'), + SetTrueOption('--maccatalyst', dest='maccatalyst'), + SetTrueOption('--maccatalyst-ios-tests', dest='maccatalyst_ios_tests'), SetTrueOption('--playgroundsupport', dest='build_playgroundsupport'), + SetTrueOption('--pythonkit', dest='build_pythonkit'), + SetTrueOption('--install-pythonkit', dest='install_pythonkit'), + SetTrueOption('--test-pythonkit', dest='test_pythonkit'), SetTrueOption('--skip-build'), SetTrueOption('--swiftpm', dest='build_swiftpm'), SetTrueOption('--swiftsyntax', dest='build_swiftsyntax'), @@ -448,6 +480,7 @@ class IgnoreOption(_BaseOption): EnableOption('--build-swift-stdlib-unittest-extra'), EnableOption('--distcc'), EnableOption('--enable-asan'), + EnableOption('--enable-experimental-differentiable-programming'), EnableOption('--enable-lsan'), EnableOption('--enable-sanitize-coverage'), EnableOption('--enable-tsan'), @@ -461,7 +494,13 @@ class IgnoreOption(_BaseOption): EnableOption('--libicu', dest='build_libicu'), EnableOption('--indexstore-db', dest='build_indexstoredb'), EnableOption('--sourcekit-lsp', dest='build_sourcekitlsp'), + EnableOption('--install-swiftsyntax', dest='install_swiftsyntax'), + EnableOption('--swiftsyntax-verify-generated-files', + dest='swiftsyntax_verify_generated_files'), + EnableOption('--install-swiftpm', dest='install_swiftpm'), EnableOption('--install-sourcekit-lsp', dest='install_sourcekitlsp'), + EnableOption('--install-skstresstester', dest='install_skstresstester'), + EnableOption('--install-swiftevolve', dest='install_swiftevolve'), EnableOption('--toolchain-benchmarks', dest='build_toolchainbenchmarks'), EnableOption('--tsan-libdispatch-test'), EnableOption('--long-test'), @@ -516,8 +555,16 @@ class IgnoreOption(_BaseOption): DisableOption('--skip-test-watchos-host', dest='test_watchos_host'), DisableOption('--skip-test-watchos-simulator', dest='test_watchos_simulator'), + DisableOption('--skip-test-swiftpm', dest='test_swiftpm'), + DisableOption('--skip-test-swiftsyntax', dest='test_swiftsyntax'), DisableOption('--skip-test-indexstore-db', dest='test_indexstoredb'), DisableOption('--skip-test-sourcekit-lsp', dest='test_sourcekitlsp'), + DisableOption('--skip-test-skstresstester', dest='test_skstresstester'), + DisableOption('--skip-test-swiftevolve', dest='test_swiftevolve'), + DisableOption('--skip-test-toolchain-benchmarks', + dest='test_toolchainbenchmarks'), + DisableOption('--skip-build-clang-tools-extra', + dest='build_clang_tools_extra'), ChoicesOption('--android-ndk-gcc-version', choices=['4.8', '4.9']), @@ -584,6 +631,10 @@ class IgnoreOption(_BaseOption): UnsupportedOption('--skip-test-optimize-none-with-implicit-dynamic'), UnsupportedOption('--skip-test-optimized'), + # Options forwared to build-script-impl + BuildScriptImplOption('--skip-test-swift', dest='impl_skip_test_swift'), + BuildScriptImplOption('--install-swift', dest='impl_install_swift'), + # NOTE: LTO flag is a special case that acts both as an option and has # valid choices SetOption('--lto', dest='lto_type'), diff --git a/utils/build_swift/tests/test_driver_arguments.py b/utils/build_swift/tests/test_driver_arguments.py deleted file mode 100644 index 4da00251c1fd2..0000000000000 --- a/utils/build_swift/tests/test_driver_arguments.py +++ /dev/null @@ -1,713 +0,0 @@ -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - - -import os -import platform -import sys -import unittest -from contextlib import contextmanager -from io import StringIO - -from swift_build_support.swift_build_support import migration -from swift_build_support.swift_build_support.SwiftBuildSupport import ( - get_all_preset_names, - get_preset_options, -) - -from . import expected_options as eo -from .. import argparse -from .. import driver_arguments - - -FILE_PATH = os.path.abspath(__file__) -TESTS_PATH = os.path.abspath(os.path.join(FILE_PATH, os.pardir)) -BUILD_SWIFT_PATH = os.path.abspath(os.path.join(TESTS_PATH, os.pardir)) -UTILS_PATH = os.path.abspath(os.path.join(BUILD_SWIFT_PATH, os.pardir)) - -BUILD_SCRIPT_IMPL = os.path.join(UTILS_PATH, 'build-script-impl') - -PRESETS_FILES = [ - os.path.join(UTILS_PATH, 'build-presets.ini'), -] - - -class ParserError(Exception): - pass - - -@contextmanager -def redirect_stderr(stream=None): - stream = stream or StringIO() - old_stderr, sys.stderr = sys.stderr, stream - try: - yield stream - finally: - sys.stderr = old_stderr - - -@contextmanager -def redirect_stdout(stream=None): - stream = stream or StringIO() - old_stdout, sys.stdout = sys.stdout, stream - try: - yield stream - finally: - sys.stdout = old_stdout - - -def _load_all_presets(presets_files): - preset_names = get_all_preset_names(presets_files) - - # Hack to filter out mixins which are not expected to be valid presets - preset_names = [n for n in preset_names if not n.startswith('mixin')] - - substitutions = { - 'install_destdir': '/tmp/install', - 'install_symroot': '/tmp/symroot', - 'installable_package': '/tmp/xcode-xyz-root.tar.gz', - } - - presets = dict() - for name in preset_names: - try: - # Attempt to parse preset - presets[name] = get_preset_options(substitutions, - presets_files, name) - except SystemExit: - continue - - return presets - - -class TestDriverArgumentParserMeta(type): - """Metaclass used to dynamically generate test methods for each of the - individual options accepted by the parser and methods to validate all of - the presets. - """ - - def __new__(cls, name, bases, attrs): - # Generate tests for each default value - for dest, value in eo.EXPECTED_DEFAULTS.items(): - test_name = 'test_default_value_' + dest - attrs[test_name] = cls.generate_default_value_test(dest, value) - - # Generate tests for each expected option - for option in eo.EXPECTED_OPTIONS: - test_name = 'test_option_' + option.sanitized_string() - attrs[test_name] = cls.generate_option_test(option) - - # Generate tests for each preset - presets = _load_all_presets(PRESETS_FILES) - - for name, args in presets.items(): - test_name = 'test_preset_' + name - attrs[test_name] = cls.generate_preset_test(name, args) - - return super(TestDriverArgumentParserMeta, cls).__new__( - cls, name, bases, attrs) - - @classmethod - def generate_default_value_test(cls, dest, default_value): - def test(self): - with self.assertNotRaises(ParserError): - parsed_values = self.parse_default_args([]) - - parsed_value = getattr(parsed_values, dest) - if default_value.__class__ is str: - parsed_value = str(parsed_value) - - self.assertEqual(default_value, parsed_value, - 'Invalid default value for "{}": {} != {}' - .format(dest, default_value, parsed_value)) - - return test - - @classmethod - def _generate_help_option_test(cls, option): - def test(self): - with redirect_stdout() as output, self.assertRaises(ParserError): - self.parse_args([option.option_string]) - self.assertNotEmpty(output) - - return test - - @classmethod - def _generate_set_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_args([option.option_string]) - self.assertEqual(getattr(namespace, option.dest), option.value) - - with self.assertRaises(ParserError): - self.parse_args([option.option_string, 'foo']) - - return test - - @classmethod - def _generate_set_true_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - # TODO: Move to unit-tests for the action class - namespace = self.parse_args([]) - self.assertFalse(getattr(namespace, option.dest)) - - namespace = self.parse_args([option.option_string]) - self.assertTrue(getattr(namespace, option.dest)) - - return test - - @classmethod - def _generate_set_false_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - # TODO: Move to unit-tests for the action class - namespace = self.parse_args([]) - self.assertTrue(getattr(namespace, option.dest)) - - namespace = self.parse_args([option.option_string]) - self.assertFalse(getattr(namespace, option.dest)) - - return test - - @classmethod - def _generate_enable_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - # TODO: Move to unit-tests for the action class - # Test parsing True values - self.parse_args([option.option_string, '1']) - self.parse_args([option.option_string, 'true']) - self.parse_args([option.option_string, 'True']) - self.parse_args([option.option_string, 'TRUE']) - - # TODO: Move to unit-tests for the action class - # Test parsing False values - self.parse_args([option.option_string, '0']) - self.parse_args([option.option_string, 'false']) - self.parse_args([option.option_string, 'False']) - self.parse_args([option.option_string, 'FALSE']) - - # TODO: Move to unit-tests for the action class - # Test default value - namespace = self.parse_args([option.option_string]) - self.assertTrue(getattr(namespace, option.dest)) - - # Test setting value to True - namespace = self.parse_args([option.option_string, 'True']) - self.assertTrue(getattr(namespace, option.dest)) - - # Test setting value to False - namespace = self.parse_args([option.option_string, 'False']) - self.assertFalse(getattr(namespace, option.dest)) - - return test - - @classmethod - def _generate_disable_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - # TODO: Move to unit-tests for the action class - # Test parsing True values - self.parse_args([option.option_string, '1']) - self.parse_args([option.option_string, 'true']) - self.parse_args([option.option_string, 'True']) - self.parse_args([option.option_string, 'TRUE']) - - # TODO: Move to unit-tests for the action class - # Test parsing False values - self.parse_args([option.option_string, '0']) - self.parse_args([option.option_string, 'false']) - self.parse_args([option.option_string, 'False']) - self.parse_args([option.option_string, 'FALSE']) - - # TODO: Move to unit-tests for the action class - # Test default value - namespace = self.parse_args([option.option_string]) - self.assertFalse(getattr(namespace, option.dest)) - - # Test setting value to True resulting in False - namespace = self.parse_args([option.option_string, 'True']) - self.assertFalse(getattr(namespace, option.dest)) - - # Test setting value to False resulting in True - namespace = self.parse_args([option.option_string, 'False']) - self.assertTrue(getattr(namespace, option.dest)) - - return test - - @classmethod - def _generate_choices_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - for choice in option.choices: - namespace = self.parse_args( - [option.option_string, str(choice)]) - self.assertEqual(getattr(namespace, option.dest), choice) - - with self.assertRaises(ParserError): - self.parse_args([option.option_string, 'INVALID']) - - return test - - @classmethod - def _generate_int_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - for i in [0, 1, 42]: - namespace = self.parse_args([option.option_string, str(i)]) - self.assertEqual(int(getattr(namespace, option.dest)), i) - - # FIXME: int-type options should not accept non-int strings - # with self.assertRaises(ParserError): - # self.parse_args([option.option_string, str(0.0)]) - # self.parse_args([option.option_string, str(1.0)]) - # self.parse_args([option.option_string, str(3.14)]) - # self.parse_args([option.option_string, 'NaN']) - - return test - - @classmethod - def _generate_str_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - self.parse_args([option.option_string, 'foo']) - - return test - - @classmethod - def _generate_path_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - self.parse_args([option.option_string, sys.executable]) - - # FIXME: path-type options should not accept non-path inputs - # with self.assertRaises(ParserError): - # self.parse_args([option.option_string, 'foo']) - - return test - - @classmethod - def _generate_append_option_test(cls, option): - def test(self): - with self.assertNotRaises(ParserError): - # Range size is arbitrary, just needs to be more than once - for i in range(1, 4): - namespace = self.parse_args( - [option.option_string, 'ARG'] * i) - self.assertEqual(getattr(namespace, option.dest), - ['ARG'] * i) - - return test - - @classmethod - def _generate_unsupported_option_test(cls, option): - def test(self): - with self.assertRaises(ParserError): - self.parse_args([option.option_string]) - - return test - - @classmethod - def generate_option_test(cls, option): - generate_test_funcs = { - eo.HelpOption: cls._generate_help_option_test, - eo.SetOption: cls._generate_set_option_test, - eo.SetTrueOption: cls._generate_set_true_option_test, - eo.SetFalseOption: cls._generate_set_false_option_test, - eo.EnableOption: cls._generate_enable_option_test, - eo.DisableOption: cls._generate_disable_option_test, - eo.ChoicesOption: cls._generate_choices_option_test, - eo.IntOption: cls._generate_int_option_test, - eo.StrOption: cls._generate_str_option_test, - eo.PathOption: cls._generate_path_option_test, - eo.AppendOption: cls._generate_append_option_test, - eo.UnsupportedOption: cls._generate_unsupported_option_test, - - # IgnoreOptions should be manually tested - eo.IgnoreOption: lambda self: None, - } - - test_func = generate_test_funcs.get(option.__class__, None) - if test_func is not None: - return test_func(option) - - # Catch-all meaningless test - return lambda self: \ - self.fail('unexpected option "{}"'.format(option.option_string)) - - @classmethod - def generate_preset_test(cls, preset_name, preset_args): - def test(self): - try: - # Windows cannot run build-script-impl to check the impl args. - is_windows = platform.system() == 'Windows' - self.parse_default_args(preset_args, - check_impl_args=not is_windows) - except ParserError as e: - self.fail('failed to parse preset "{}": {}'.format( - preset_name, e)) - - return test - - -class TestDriverArgumentParser(unittest.TestCase): - - __metaclass__ = TestDriverArgumentParserMeta - - @contextmanager - def _quiet_output(self): - with open(os.devnull, 'w') as devnull: - with redirect_stderr(devnull), redirect_stdout(devnull): - yield - - def _parse_args(self, args): - try: - return migration.parse_args(self.parser, args) - except (SystemExit, ValueError) as e: - raise ParserError('failed to parse arguments: ' + - str(args), e) - - def _check_impl_args(self, namespace): - assert hasattr(namespace, 'build_script_impl_args') - - try: - migration.check_impl_args(BUILD_SCRIPT_IMPL, - namespace.build_script_impl_args) - except (SystemExit, ValueError) as e: - raise ParserError('failed to parse impl arguments: ' + - str(namespace.build_script_impl_args), e) - - def parse_args(self, args, namespace=None): - if namespace is None: - namespace = argparse.Namespace() - - with self._quiet_output(): - try: - namespace, unknown_args =\ - super(self.parser.__class__, self.parser)\ - .parse_known_args(args, namespace) - except (SystemExit, argparse.ArgumentError) as e: - raise ParserError('failed to parse arguments: ' + str(args), e) - - if unknown_args: - raise ParserError('unknown arguments: ' + str(unknown_args)) - - return namespace - - def parse_default_args(self, args, check_impl_args=False): - with self._quiet_output(): - namespace = self._parse_args(args) - - if check_impl_args: - self._check_impl_args(namespace) - - return namespace - - @contextmanager - def assertNotRaises(self, exception): - assert issubclass(exception, BaseException) - - try: - yield - except exception as e: - self.fail(str(e)) - - def setUp(self): - self.parser = driver_arguments.create_argument_parser() - - # ------------------------------------------------------------------------- - - def test_expected_options_exhaustive(self): - """Test that we are exhaustively testing all options accepted by the - parser. If this test if failing then the parser accepts more options - than currently being tested, meaning the EXPECTED_OPTIONS list in - build_swift/tests/expected_options.py should be updated to include - the missing options. - """ - - expected_options = {o.option_string for o in eo.EXPECTED_OPTIONS} - - # aggregate and flatten the options_strings accepted by the parser - actual_options = [a.option_strings for a in self.parser._actions] - actual_options = set(sum(actual_options, [])) - - diff = actual_options - expected_options - - if len(diff) > 0: - self.fail('non-exhaustive expected options, missing: {}' - .format(diff)) - - def test_expected_options_have_default_values(self): - """Test that all the options in EXPECTED_OPTIONS have an associated - default value. - """ - - skip_option_classes = [ - eo.HelpOption, - eo.IgnoreOption, - eo.UnsupportedOption, - ] - - missing_defaults = set() - for option in eo.EXPECTED_OPTIONS: - if option.__class__ in skip_option_classes: - continue - - if option.dest not in eo.EXPECTED_DEFAULTS: - missing_defaults.add(option.dest) - - if len(missing_defaults) > 0: - self.fail('non-exhaustive default values for options, missing: {}' - .format(missing_defaults)) - - # ------------------------------------------------------------------------- - # Manual option tests - - def test_option_clang_compiler_version(self): - option_string = '--clang-compiler-version' - - with self.assertNotRaises(ParserError): - self.parse_default_args([option_string, '5.0.0']) - self.parse_default_args([option_string, '5.0.1']) - self.parse_default_args([option_string, '5.0.0.1']) - - with self.assertRaises(ParserError): - self.parse_default_args([option_string, '1']) - self.parse_default_args([option_string, '1.2']) - self.parse_default_args([option_string, '0.0.0.0.1']) - - def test_option_clang_user_visible_version(self): - option_string = '--clang-user-visible-version' - - with self.assertNotRaises(ParserError): - self.parse_default_args([option_string, '5.0.0']) - self.parse_default_args([option_string, '5.0.1']) - self.parse_default_args([option_string, '5.0.0.1']) - - with self.assertRaises(ParserError): - self.parse_default_args([option_string, '1']) - self.parse_default_args([option_string, '1.2']) - self.parse_default_args([option_string, '0.0.0.0.1']) - - def test_option_swift_compiler_version(self): - option_string = '--swift-compiler-version' - - with self.assertNotRaises(ParserError): - self.parse_default_args([option_string, '4.1']) - self.parse_default_args([option_string, '4.0.1']) - self.parse_default_args([option_string, '200.99.1']) - - with self.assertRaises(ParserError): - self.parse_default_args([option_string, '1']) - self.parse_default_args([option_string, '0.0.0.1']) - - def test_option_swift_user_visible_version(self): - option_string = '--swift-user-visible-version' - - with self.assertNotRaises(ParserError): - self.parse_default_args([option_string, '4.1']) - self.parse_default_args([option_string, '4.0.1']) - self.parse_default_args([option_string, '200.99.1']) - - with self.assertRaises(ParserError): - self.parse_default_args([option_string, '1']) - self.parse_default_args([option_string, '0.0.0.1']) - - def test_option_I(self): - with self.assertRaises(ValueError): - self.parse_default_args(['-I']) - - def test_option_ios_all(self): - with self.assertRaises(ValueError): - self.parse_default_args(['--ios-all']) - - def test_option_tvos_all(self): - with self.assertRaises(ValueError): - self.parse_default_args(['--tvos-all']) - - def test_option_watchos_all(self): - with self.assertRaises(ValueError): - self.parse_default_args(['--watchos-all']) - - # ------------------------------------------------------------------------- - # Implied defaults tests - - def test_implied_defaults_assertions(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--assertions']) - - self.assertTrue(namespace.cmark_assertions) - self.assertTrue(namespace.llvm_assertions) - self.assertTrue(namespace.swift_assertions) - self.assertTrue(namespace.swift_stdlib_assertions) - - def test_implied_defaults_cmark_build_variant(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--debug-cmark']) - self.assertTrue(namespace.build_cmark) - - def test_implied_defaults_lldb_build_variant(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--debug-lldb']) - self.assertTrue(namespace.build_lldb) - - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--lldb-assertions']) - self.assertTrue(namespace.build_lldb) - - def test_implied_defaults_build_variant(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--debug']) - - self.assertEqual(namespace.cmark_build_variant, 'Debug') - self.assertEqual(namespace.foundation_build_variant, 'Debug') - self.assertEqual(namespace.libdispatch_build_variant, 'Debug') - self.assertEqual(namespace.libicu_build_variant, 'Debug') - self.assertEqual(namespace.lldb_build_variant, 'Debug') - self.assertEqual(namespace.llvm_build_variant, 'Debug') - self.assertEqual(namespace.swift_build_variant, 'Debug') - self.assertEqual(namespace.swift_stdlib_build_variant, 'Debug') - - def test_implied_defaults_skip_build(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-build']) - - self.assertFalse(namespace.build_benchmarks) - - self.assertFalse(namespace.build_linux) - self.assertFalse(namespace.build_android) - self.assertFalse(namespace.build_freebsd) - self.assertFalse(namespace.build_cygwin) - self.assertFalse(namespace.build_osx) - self.assertFalse(namespace.build_ios) - self.assertFalse(namespace.build_tvos) - self.assertFalse(namespace.build_watchos) - - self.assertFalse(namespace.build_foundation) - self.assertFalse(namespace.build_libdispatch) - self.assertFalse(namespace.build_libicu) - self.assertFalse(namespace.build_lldb) - self.assertFalse(namespace.build_llbuild) - self.assertFalse(namespace.build_libcxx) - self.assertFalse(namespace.build_playgroundsupport) - self.assertFalse(namespace.build_swiftpm) - self.assertFalse(namespace.build_xctest) - - def test_implied_defaults_skip_build_ios(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-build-ios']) - self.assertFalse(namespace.build_ios_device) - self.assertFalse(namespace.build_ios_simulator) - - # Also implies that the tests should be skipped - self.assertFalse(namespace.test_ios_host) - self.assertFalse(namespace.test_ios_simulator) - - def test_implied_defaults_skip_build_tvos(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-build-tvos']) - self.assertFalse(namespace.build_tvos_device) - self.assertFalse(namespace.build_tvos_simulator) - - # Also implies that the tests should be skipped - self.assertFalse(namespace.test_tvos_host) - self.assertFalse(namespace.test_tvos_simulator) - - def test_implied_defaults_skip_build_watchos(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-build-watchos']) - self.assertFalse(namespace.build_watchos_device) - self.assertFalse(namespace.build_watchos_simulator) - - # Also implies that the tests should be skipped - self.assertFalse(namespace.test_watchos_host) - self.assertFalse(namespace.test_watchos_simulator) - - def test_implied_defaults_validation_test(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--validation-test']) - self.assertTrue(namespace.test) - - def test_implied_defaults_test_optimized(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--test-optimized']) - self.assertTrue(namespace.test) - - def test_implied_defaults_test_optimize_for_size(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--test-optimize-for-size']) - self.assertTrue(namespace.test) - - def test_implied_defaults_test_optimize_none_with_implicit_dynamic(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args( - ['--test-optimize-none-with-implicit-dynamic']) - self.assertTrue(namespace.test) - - def test_implied_defaults_skip_all_tests(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args([ - '--test', '0', - '--validation-test', '0', - '--long-test', '0', - '--stress-test', '0', - ]) - - self.assertFalse(namespace.test_linux) - self.assertFalse(namespace.test_freebsd) - self.assertFalse(namespace.test_cygwin) - self.assertFalse(namespace.test_osx) - self.assertFalse(namespace.test_ios) - self.assertFalse(namespace.test_tvos) - self.assertFalse(namespace.test_watchos) - - def test_implied_defaults_skip_test_ios(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-test-ios']) - self.assertFalse(namespace.test_ios_host) - self.assertFalse(namespace.test_ios_simulator) - - def test_implied_defaults_skip_test_tvos(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-test-tvos']) - self.assertFalse(namespace.test_tvos_host) - self.assertFalse(namespace.test_tvos_simulator) - - def test_implied_defaults_skip_test_watchos(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-test-watchos']) - self.assertFalse(namespace.test_watchos_host) - self.assertFalse(namespace.test_watchos_simulator) - - def test_implied_defaults_skip_build_android(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--android', '0']) - self.assertFalse(namespace.test_android_host) - - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--skip-build-android']) - self.assertFalse(namespace.test_android_host) - - def test_implied_defaults_host_test(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--host-test', '0']) - self.assertFalse(namespace.test_ios_host) - self.assertFalse(namespace.test_tvos_host) - self.assertFalse(namespace.test_watchos_host) - self.assertFalse(namespace.test_android_host) - self.assertFalse(namespace.build_libparser_only) - - def test_build_lib_swiftsyntaxparser_only(self): - with self.assertNotRaises(ParserError): - namespace = self.parse_default_args(['--build-libparser-only']) - self.assertTrue(namespace.build_libparser_only) - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/build_swift/tests/test_migration.py b/utils/build_swift/tests/test_migration.py deleted file mode 100644 index bfe03c3064343..0000000000000 --- a/utils/build_swift/tests/test_migration.py +++ /dev/null @@ -1,78 +0,0 @@ -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - - -from __future__ import absolute_import, unicode_literals - -from .utils import TestCase, add_metaclass -from .. import migration - - -def _get_sdk_targets(sdk_names): - targets = [] - for sdk_name in sdk_names: - targets += migration._SDK_TARGETS[sdk_name] - - return targets - - -def _get_sdk_target_names(sdk_names): - return [target.name for target in _get_sdk_targets(sdk_names)] - - -# ----------------------------------------------------------------------------- - -class TestMigrateSwiftSDKsMeta(type): - """Metaclass used to dynamically generate test methods. - """ - - def __new__(cls, name, bases, attrs): - # Generate tests for migrating each Swift SDK - for sdk_name in migration._SDK_TARGETS.keys(): - test_name = 'test_migrate_swift_sdk_' + sdk_name - attrs[test_name] = cls.generate_migrate_swift_sdks_test(sdk_name) - - return super(TestMigrateSwiftSDKsMeta, cls).__new__( - cls, name, bases, attrs) - - @classmethod - def generate_migrate_swift_sdks_test(cls, sdk_name): - def test(self): - args = ['--swift-sdks={}'.format(sdk_name)] - args = migration.migrate_swift_sdks(args) - - target_names = _get_sdk_target_names([sdk_name]) - self.assertListEqual(args, [ - '--stdlib-deployment-targets={}'.format(' '.join(target_names)) - ]) - - return test - - -@add_metaclass(TestMigrateSwiftSDKsMeta) -class TestMigrateSwiftSDKs(TestCase): - - def test_empty_swift_sdks(self): - args = migration.migrate_swift_sdks(['--swift-sdks=']) - self.assertListEqual(args, ['--stdlib-deployment-targets=']) - - def test_multiple_swift_sdk_flags(self): - sdks = ['OSX', 'IOS', 'IOS_SIMULATOR'] - - args = [ - '--swift-sdks=', - '--swift-sdks={}'.format(';'.join(sdks)) - ] - - args = migration.migrate_swift_sdks(args) - target_names = _get_sdk_target_names(sdks) - - self.assertListEqual(args, [ - '--stdlib-deployment-targets=', - '--stdlib-deployment-targets={}'.format(' '.join(target_names)) - ]) diff --git a/utils/build_swift/tests/utils.py b/utils/build_swift/tests/utils.py index c2176e9a1bef6..85fc24350e9ff 100644 --- a/utils/build_swift/tests/utils.py +++ b/utils/build_swift/tests/utils.py @@ -1,90 +1,182 @@ # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -try: - from StringIO import StringIO -except ImportError: - from io import StringIO +from __future__ import absolute_import, unicode_literals +import functools import os +import platform import sys import unittest -from contextlib import contextmanager + +from build_swift import cache_utils +from build_swift.versions import Version + +import six +from six import StringIO __all__ = [ - 'add_metaclass', + 'quiet_output', 'redirect_stderr', 'redirect_stdout', - 'TestCase', - + 'requires_attr', + 'requires_module', + 'requires_platform', + 'requires_python', + + 'BUILD_SCRIPT_IMPL_PATH', + 'BUILD_SWIFT_PATH', + 'TESTS_PATH', 'UTILS_PATH', ] -UTILS_PATH = os.path.abspath(os.path.join( - os.path.dirname(__file__), - os.pardir, - os.pardir, -)) +# ----------------------------------------------------------------------------- +# Constants + +_PYTHON_VERSION = Version(platform.python_version()) + +TESTS_PATH = os.path.abspath(os.path.dirname(__file__)) +BUILD_SWIFT_PATH = os.path.abspath(os.path.join(TESTS_PATH, os.pardir)) +UTILS_PATH = os.path.abspath(os.path.join(BUILD_SWIFT_PATH, os.pardir)) + +BUILD_SCRIPT_IMPL_PATH = os.path.join(UTILS_PATH, 'build-script-impl') + + +# ----------------------------------------------------------------------------- +# Helpers + +def _can_import(fullname): + try: + __import__(fullname) + return True + except ImportError: + return False # ----------------------------------------------------------------------------- -def add_metaclass(metacls): - def wrapper(cls): - body = vars(cls).copy() +class quiet_output(object): + """Context manager and decorator used to quiet both sys.stderr and + sys.stdout by redirecting them to os.devnull. + """ - body.pop('__dict__', None) - body.pop('__weakref__', None) + __slots__ = ('_devnull', '_old_stderr', '_old_stdout') - return metacls(cls.__name__, cls.__bases__, body) + def __enter__(self): + self._devnull = open(os.devnull, 'w') + self._old_stderr = sys.stderr + self._old_stdout = sys.stdout - return wrapper + sys.stderr = self._devnull + sys.stdout = self._devnull + def __exit__(self, exc_type, exc_value, traceback): + sys.stderr = self._old_stderr + sys.stdout = self._old_stdout -@contextmanager -def redirect_stderr(stream=None): - stream = stream or StringIO() - old_stderr, sys.stderr = sys.stderr, stream - try: - yield stream - finally: - sys.stderr = old_stderr + self._devnull.close() + + def __call__(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + with self: + return func(*args, **kwargs) + + return wrapper + + +class redirect_stderr(object): + """Context manager used to substitute sys.stderr with a different file-like + object. + """ + + __slots__ = ('_stream', '_old_stderr') + def __init__(self, stream=None): + self._stream = stream or StringIO() + + def __enter__(self): + self._old_stderr, sys.stderr = sys.stderr, self._stream + return self._stream + + def __exit__(self, exc_type, exc_value, traceback): + sys.stderr = self._old_stderr + + +class redirect_stdout(): + """Context manager used to substitute sys.stdout with a different file-like + object. + """ + + __slots__ = ('_stream', '_old_stdout') + + def __init__(self, stream=None): + self._stream = stream or StringIO() + + def __enter__(self): + self._old_stdout, sys.stderr = sys.stderr, self._stream + return self._stream + + def __exit__(self, exc_type, exc_value, traceback): + sys.stderr = self._old_stdout + + +@cache_utils.cache +def requires_attr(obj, attr): + """Decorator used to skip tests if an object does not have the required + attribute. + """ -@contextmanager -def redirect_stdout(stream=None): - stream = stream or StringIO() - old_stdout, sys.stdout = sys.stdout, stream try: - yield stream - finally: - sys.stdout = old_stdout + getattr(obj, attr) + return lambda func: func + except AttributeError: + return unittest.skip('Required attribute "{}" not found on {}'.format( + attr, obj)) -# ----------------------------------------------------------------------------- +@cache_utils.cache +def requires_module(fullname): + """Decorator used to skip tests if a module is not imported. + """ + + if _can_import(fullname): + return lambda func: func + + return unittest.skip('Unable to import "{}"'.format(fullname)) + + +@cache_utils.cache +def requires_platform(name): + """Decorator used to skip tests if not running on the given platform. + """ + + if name == platform.system(): + return lambda func: func + + return unittest.skip( + 'Required platform "{}" does not match system'.format(name)) + -class TestCase(unittest.TestCase): +@cache_utils.cache +def requires_python(version): + """Decorator used to skip tests if the running Python version is not + greater or equal to the required version. + """ - @contextmanager - def quietOutput(self): - with open(os.devnull, 'w') as devnull: - with redirect_stderr(devnull), redirect_stdout(devnull): - yield + if isinstance(version, six.string_types): + version = Version(version) - @contextmanager - def assertNotRaises(self, exception=BaseException): - assert issubclass(exception, BaseException) + if _PYTHON_VERSION >= version: + return lambda func: func - try: - yield - except exception as e: - message = '{} raised: {}'.format(exception.__name__, str(e)) - raise self.failureException(message) + return unittest.skip( + 'Requires Python version {} or greater'.format(version)) diff --git a/utils/error_enum_to_cases.pl b/utils/error_enum_to_cases.pl deleted file mode 100644 index fad0043f8b8ae..0000000000000 --- a/utils/error_enum_to_cases.pl +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -use English; - -sub translateAvailability { - my $version = shift; - $version =~ s/^\s+|\s+$//g; - if ($version eq "NA") { return "unavailable"; } - $version =~ /([0-9]+)_([0-9]+)/; - return "introduced=$1.$2"; -} - -my $prefixLength = 2; -my %minimumValues = (); -my %maximumValues = (); -my %rangeAvailability = (); -my $prev_had_availability = 0; -foreach my $line () { - chomp $line; - if ($line =~ /([A-Za-z_][A-Za-z_0-9]+).*=[^0-9A-Za-z_-]*([A-Za-z0-9_-]+)/) { - my $fullname = $1; - my $value = $2; - my $has_availability = 0; - - my $availability = ""; -# if ($line =~ /AVAILABLE\s*[(](([0-9]+_[0-9]+)|(NA))[ ]*,[ ]*(([0-9]+_[0-9]+)|(NA))[)]/) { - if ($line =~ /AVAILABLE[ ]*[(]([^),]*),([^)]*)[)]/) { - $has_availability = 1; - my $osx = $1; - my $ios = $2; - $osx = translateAvailability($osx); - $ios = translateAvailability($ios); - $availability = " \@available(OSX, $osx) \@available(iOS, $ios)\n"; - } - - # If the full name ends in "Minimum" or "Maximum", it's for a range. - my $rangeName = ""; - if ($fullname =~ /(Minimum|Maximum)$/) { - $rangeName = substr $PREMATCH, $prefixLength; - if ($MATCH eq "Minimum") { - $minimumValues{$rangeName} = $value; - } else { - $maximumValues{$rangeName} = $value; - } - $rangeAvailability{$rangeName} = $availability; - } else { - if ($availability ne "") { - if ($prev_had_availability == 0) { - print("\n"); - } - print("$availability"); - } - my $casename = substr $fullname, $prefixLength; - print(" case $casename = $value\n"); - - if ($availability ne "") { - print("\n"); - $prev_had_availability = 1; - } else { - $prev_had_availability = 0; - } - } - } -} - -# Print properties for the ranges. -foreach my $key (sort keys(%minimumValues)) { - my $minimum = $minimumValues{$key}; - my $maximum = $maximumValues{$key}; - my $availability = $rangeAvailability{$key}; - print "\n"; - if ($availability ne "") { - print $availability; - } - print(" public var is$key: Bool {\n"); - print(" return rawValue >= $minimum && rawValue <= $maximum;\n"); - print(" }\n"); -} diff --git a/utils/guard-malloc-swift b/utils/guard-malloc-swift deleted file mode 100755 index 05660e54988ff..0000000000000 --- a/utils/guard-malloc-swift +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -# Runs swift (found on the path) with -use-malloc and GuardMalloc enabled. -# Command-line arguments are forwarded to the swift compiler untouched. -# This script can be used to run the test suite with memory debugging enabled -# by setting the SWIFT environment variable to point to the script, as in: -# -# SWIFT=/path/to/guard-malloc-swift llvm-lit -sv test - -export MallocScribble=1 -export MallocPreScribble=1 -export MallocGuardEdges=1 -export MallocCheckHeapStart=100 -export MallocCheckHeapEach=100 -export MallocCheckHeapAbort=1 -export MallocErrorAbort=1 - -export DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib - -swift -use-malloc "$@" diff --git a/utils/gyb.py b/utils/gyb.py index d9ce0e7b3c8b5..e38525338d3b9 100755 --- a/utils/gyb.py +++ b/utils/gyb.py @@ -7,13 +7,16 @@ import os import re import sys +import textwrap +import tokenize +from bisect import bisect + + try: from cStringIO import StringIO except ImportError: from io import StringIO -import textwrap -import tokenize -from bisect import bisect + try: basestring diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 655a5a20b479e..ad0c1d54018c9 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -174,6 +174,7 @@ def __init__(self, internal_name, external_name): KEY('VFSName', 'key.vfs.name'), KEY('VFSOptions', 'key.vfs.options'), KEY('Files', 'key.files'), + KEY('OptimizeForIDE', 'key.optimize_for_ide'), ] @@ -231,6 +232,7 @@ def __init__(self, internal_name, external_name): 'source.request.enable-compile-notifications'), REQUEST('TestNotification', 'source.request.test_notification'), REQUEST('CollectExpressionType', 'source.request.expression.type'), + REQUEST('GlobalConfiguration', 'source.request.configuration.global') ] diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 30b6eb063ae3e..633feca51070a 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -60,13 +60,15 @@ Child('ObjCName', kind='ObjCSelector'), Child('ImplementsArguments', kind='ImplementsAttributeArguments'), + Child('DifferentiableArguments', + kind='DifferentiableAttributeArguments'), + Child('DerivativeRegistrationArguments', + kind='DerivativeRegistrationAttributeArguments'), Child('NamedAttributeString', kind='NamedAttributeStringArgument'), - Child('OpaqueReturnTypeOfArguments', - kind='OpaqueReturnTypeOfAttributeArguments'), ], description=''' - The arguments of the attribute. In case the attribute \ - takes multiple arguments, they are gather in the \ + The arguments of the attribute. In case the attribute + takes multiple arguments, they are gather in the appropriate takes first. '''), Child('RightParen', kind='RightParenToken', is_optional=True, @@ -107,7 +109,7 @@ # labeled-specialize-entry -> identifier ':' token ','? Node('LabeledSpecializeEntry', kind='Syntax', description=''' - A labeled argument for the `@_specialize` attribute like \ + A labeled argument for the `@_specialize` attribute like `exported: true` ''', traits=['WithTrailingComma'], @@ -127,8 +129,8 @@ # named-attribute-string-arg -> 'name': string-literal Node('NamedAttributeStringArgument', kind='Syntax', description=''' - The argument for the `@_dynamic_replacement` or `@_private` \ - attribute of the form `for: "function()"` or `sourceFile: \ + The argument for the `@_dynamic_replacement` or `@_private` + attribute of the form `for: "function()"` or `sourceFile: "Src.swift"` ''', children=[ @@ -142,22 +144,16 @@ ]), ]), Node('DeclName', kind='Syntax', children=[ - Child('DeclBaseName', kind='Token', description=''' + Child('DeclBaseName', kind='Syntax', description=''' The base name of the protocol\'s requirement. ''', - token_choices=[ - 'IdentifierToken', - 'PrefixOperatorToken', - 'PostfixOperatorToken', - 'SpacedBinaryOperatorToken', - 'UnspacedBinaryOperatorToken', - 'InitToken', - 'DeinitToken', - 'SubscriptToken', + node_choices=[ + Child('Identifier', kind='IdentifierToken'), + Child('Operator', kind='PrefixOperatorToken'), ]), Child('DeclNameArguments', kind='DeclNameArguments', is_optional=True, description=''' - The argument labels of the protocol\'s requirement if it \ + The argument labels of the protocol\'s requirement if it is a function requirement. '''), ]), @@ -166,12 +162,12 @@ # (identifier | operator) decl-name-arguments Node('ImplementsAttributeArguments', kind='Syntax', description=''' - The arguments for the `@_implements` attribute of the form \ + The arguments for the `@_implements` attribute of the form `Type, methodName(arg1Label:arg2Label:)` ''', children=[ Child('Type', kind='SimpleTypeIdentifier', description=''' - The type for which the method with this attribute \ + The type for which the method with this attribute implements a requirement. '''), Child('Comma', kind='CommaToken', @@ -187,7 +183,7 @@ ]), Child('DeclNameArguments', kind='DeclNameArguments', is_optional=True, description=''' - The argument labels of the protocol\'s requirement if it \ + The argument labels of the protocol\'s requirement if it is a function requirement. '''), ]), @@ -195,8 +191,8 @@ # objc-selector-piece -> identifier? ':'? Node('ObjCSelectorPiece', kind='Syntax', description=''' - A piece of an Objective-C selector. Either consisiting of just an \ - identifier for a nullary selector, an identifier and a colon for a \ + A piece of an Objective-C selector. Either consisiting of just an + identifier for a nullary selector, an identifier and a colon for a labeled argument or just a colon for an unlabeled argument ''', children=[ @@ -207,21 +203,182 @@ # objc-selector -> objc-selector-piece objc-selector? Node('ObjCSelector', kind='SyntaxCollection', element='ObjCSelectorPiece'), - # opaque-return-type-of-attr-arguments -> string-literal ',' - # integer-literal - Node('OpaqueReturnTypeOfAttributeArguments', kind='Syntax', + # The argument of '@differentiable(...)'. + # differentiable-attr-arguments -> + # differentiation-params-clause? ','? + # differentiable-attr-func-specifier? # jvp + # differentiable-attr-func-specifier? # vjp + # generic-where-clause? + Node('DifferentiableAttributeArguments', kind='Syntax', description=''' - The argument for the `@_opaqueReturnTypeOf` type attribute of the \ - form `, `. + The arguments for the `@differentiable` attribute: an optional + differentiation parameter list and associated functions. ''', children=[ - Child('MangledName', kind='StringLiteralToken', description=''' - The mangled name of the opaque function/property which the - the type represents. + Child('DiffParams', kind='DifferentiationParamsClause', + is_optional=True), + Child('DiffParamsComma', kind='CommaToken', description=''' + The comma following the differentiation parameters clause, + if it exists. + ''', is_optional=True), + Child('MaybeJVP', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('MaybeVJP', kind='DifferentiableAttributeFuncSpecifier', + is_optional=True), + Child('WhereClause', kind='GenericWhereClause', is_optional=True), + ]), + + # differentiation-params-clause -> + # 'wrt' ':' (differentiation-param | differentiation-params) + Node('DifferentiationParamsClause', kind='Syntax', + description='A clause containing differentiation parameters.', + children=[ + Child('WrtLabel', kind='IdentifierToken', + text_choices=['wrt'], description='The "wrt" label.'), + Child('Colon', kind='ColonToken', description=''' + The colon separating "wrt" and the parameter list. '''), - Child('Comma', kind='CommaToken'), - Child('Index', kind='IntegerLiteralToken', description=''' - The index of the return type. + Child('Parameters', kind='Syntax', + node_choices=[ + Child('Parameter', kind='DifferentiationParam'), + Child('ParameterList', kind='DifferentiationParams'), + ]), + ]), + + # differentiation-params -> '(' differentiation-param-list ')' + Node('DifferentiationParams', kind='Syntax', + description='The differentiation parameters.', + children=[ + Child('LeftParen', kind='LeftParenToken'), + Child('DiffParams', kind='DifferentiationParamList', + collection_element_name='DifferentiationParam', + description='The parameters for differentiation.'), + Child('RightParen', kind='RightParenToken'), + ]), + + # differentiation-param-list -> + # differentiation-param differentiation-param-list? + Node('DifferentiationParamList', kind='SyntaxCollection', + element='DifferentiationParam'), + + # differentiation-param -> ('self' | identifer | integer-literal) ','? + Node('DifferentiationParam', kind='Syntax', + description=''' + A differentiation parameter: either the "self" identifier, a function + parameter name, or a function parameter index. + ''', + traits=['WithTrailingComma'], + children=[ + Child('Parameter', kind='Syntax', + node_choices=[ + Child('Self', kind='SelfToken'), + Child('Name', kind='IdentifierToken'), + Child('Index', kind='IntegerLiteralToken'), + ]), + Child('TrailingComma', kind='CommaToken', is_optional=True), + ]), + + # differentiable-attr-func-specifier -> + # ('jvp' | 'vjp') ':' func-decl-name ','? + Node('DifferentiableAttributeFuncSpecifier', kind='Syntax', + description=''' + A function specifier, consisting of an identifier, colon, and a + function declaration name (e.g. `vjp: foo(_:_:)`). + ''', + traits=['WithTrailingComma'], + children=[ + Child('Label', kind='IdentifierToken', + text_choices=['jvp', 'vjp']), + Child('Colon', kind='ColonToken'), + Child('FunctionDeclName', kind='FunctionDeclName', + description='The referenced function name.'), + Child('TrailingComma', kind='CommaToken', is_optional=True), + ]), + + # The argument of the derivative registration attribute + # '@derivative(of: ...)' and the transpose registration attribute + # '@transpose(of: ...)'. + # + # derivative-registration-attr-arguments -> + # 'of' ':' func-decl-name ','? differentiation-params-clause? + Node('DerivativeRegistrationAttributeArguments', kind='Syntax', + description=''' + The arguments for the '@derivative(of:)' and '@transpose(of:)' + attributes: the 'of:' label, the original declaration name, and an + optional differentiation parameter list. + ''', + children=[ + Child('OfLabel', kind='IdentifierToken', text_choices=['of'], + description='The "of" label.'), + Child('Colon', kind='ColonToken', description=''' + The colon separating the "of" label and the original + declaration name. + '''), + Child('OriginalDeclName', kind='QualifiedDeclName', + description='The referenced original declaration name.'), + Child('Comma', kind='CommaToken', is_optional=True), + Child('DiffParams', kind='DifferentiationParamsClause', + is_optional=True), + ]), + + # An optionally qualified declaration name. + # Currently used only for `@derivative` and `@transpose` attribute. + # TODO(TF-1066): Use module qualified name syntax/parsing instead of custom + # qualified name syntax/parsing. + # + # qualified-decl-name -> + # base-type? '.'? (identifier | operator) decl-name-arguments? + # base-type -> + # member-type-identifier | base-type-identifier + Node('QualifiedDeclName', kind='Syntax', + description=''' + An optionally qualified function declaration name (e.g. `+(_:_:)`, + `A.B.C.foo(_:_:)`). + ''', + children=[ + Child('BaseType', kind='Type', description=''' + The base type of the qualified name, optionally specified. + ''', is_optional=True), + Child('Dot', kind='Token', + token_choices=[ + 'PeriodToken', 'PrefixPeriodToken' + ], is_optional=True), + Child('Name', kind='Token', description=''' + The base name of the referenced function. + ''', + token_choices=[ + 'IdentifierToken', + 'UnspacedBinaryOperatorToken', + 'SpacedBinaryOperatorToken', + 'PrefixOperatorToken', + 'PostfixOperatorToken', + ]), + Child('Arguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the referenced function, optionally + specified. + '''), + ]), + + # func-decl-name -> (identifier | operator) decl-name-arguments? + # NOTE: This is duplicated with `DeclName` above. Change `DeclName` + # description and use it if possible. + Node('FunctionDeclName', kind='Syntax', + description='A function declaration name (e.g. `foo(_:_:)`).', + children=[ + Child('Name', kind='Syntax', description=''' + The base name of the referenced function. + ''', + node_choices=[ + Child('Identifier', kind='IdentifierToken'), + Child('PrefixOperator', kind='PrefixOperatorToken'), + Child('SpacedBinaryOperator', + kind='SpacedBinaryOperatorToken'), + ]), + Child('Arguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the referenced function, optionally + specified. '''), ]), ] diff --git a/utils/gyb_syntax_support/AvailabilityNodes.py b/utils/gyb_syntax_support/AvailabilityNodes.py index 3dde7587ae941..9eaefa96ec997 100644 --- a/utils/gyb_syntax_support/AvailabilityNodes.py +++ b/utils/gyb_syntax_support/AvailabilityNodes.py @@ -13,7 +13,7 @@ # | availability-versioned-argument ','? Node('AvailabilityArgument', kind='Syntax', description=''' - A single argument to an `@available` argument like `*`, `iOS 10.1`, \ + A single argument to an `@available` argument like `*`, `iOS 10.1`, or `message: "This has been deprecated"`. ''', children=[ @@ -31,7 +31,7 @@ ]), Child('TrailingComma', kind='CommaToken', is_optional=True, description=''' - A trailing comma if the argument is followed by another \ + A trailing comma if the argument is followed by another argument '''), ]), @@ -40,7 +40,7 @@ # availability-versioned-argument -> identifier ':' version-tuple Node('AvailabilityLabeledArgument', kind='Syntax', description=''' - A argument to an `@available` attribute that consists of a label and \ + A argument to an `@available` attribute that consists of a label and a value, e.g. `message: "This has been deprecated"`. ''', children=[ @@ -59,15 +59,15 @@ # availability-version-restriction -> identifier version-tuple Node('AvailabilityVersionRestriction', kind='Syntax', description=''' - An argument to `@available` that restricts the availability on a \ + An argument to `@available` that restricts the availability on a certain platform to a version, e.g. `iOS 10` or `swift 3.4`. ''', children=[ Child('Platform', kind='IdentifierToken', classification='Keyword', description=''' - The name of the OS on which the availability should be \ - restricted or 'swift' if the availability should be \ + The name of the OS on which the availability should be + restricted or 'swift' if the availability should be restricted based on a Swift version. '''), Child('Version', kind='VersionTuple'), @@ -78,7 +78,7 @@ # | float-literal '.' integer-literal Node('VersionTuple', kind='Syntax', description=''' - A version number of the form major.minor.patch in which the minor \ + A version number of the form major.minor.patch in which the minor and patch part may be ommited. ''', children=[ @@ -87,15 +87,15 @@ Child('Major', kind='IntegerLiteralToken'), Child('MajorMinor', kind='FloatingLiteralToken') ], description=''' - In case the version consists only of the major version, an \ - integer literal that specifies the major version. In case \ - the version consists of major and minor version number, a \ - floating literal in which the decimal part is interpreted \ + In case the version consists only of the major version, an + integer literal that specifies the major version. In case + the version consists of major and minor version number, a + floating literal in which the decimal part is interpreted as the minor version. '''), Child('PatchPeriod', kind='PeriodToken', is_optional=True, description=''' - If the version contains a patch number, the period \ + If the version contains a patch number, the period separating the minor from the patch number. '''), Child('PatchVersion', kind='IntegerLiteralToken', diff --git a/utils/gyb_syntax_support/CompletionOnlyNodes.py b/utils/gyb_syntax_support/CompletionOnlyNodes.py deleted file mode 100644 index e95b75358be3e..0000000000000 --- a/utils/gyb_syntax_support/CompletionOnlyNodes.py +++ /dev/null @@ -1,33 +0,0 @@ -from Child import Child -from Node import Node # noqa: I201 - -# These nodes are used only in code completion. - -COMPLETIONONLY_NODES = [ - # Expression. - Node('CodeCompletionExpr', kind='Expr', - children=[ - Child('Base', kind='Expr', is_optional=True), - Child('PeriodOrParen', kind='Token', - token_choices=[ - 'PeriodToken', - 'PrefixPeriodToken', - 'LeftParenToken', - ], - is_optional=True), - Child('CodeCompletionToken', kind='Token'), - ]), - - # Type. - Node('CodeCompletionType', kind='Type', - children=[ - Child('Base', kind='Type', is_optional=True), - Child('Period', kind='Token', - token_choices=[ - 'PeriodToken', - 'PrefixPeriodToken', - ], - is_optional=True), - Child('CodeCompletionToken', kind='Token'), - ]), -] diff --git a/utils/gyb_syntax_support/DeclNodes.py b/utils/gyb_syntax_support/DeclNodes.py index 14111b3b45a7c..f9b47d50b313d 100644 --- a/utils/gyb_syntax_support/DeclNodes.py +++ b/utils/gyb_syntax_support/DeclNodes.py @@ -89,7 +89,7 @@ # ('#if' | '#elseif' | '#else') expr? (stmt-list | switch-case-list) Node('IfConfigClause', kind='Syntax', children=[ - Child('PoundKeyword', kind='Token', + Child('PoundKeyword', kind='Token', classification='BuildConfigId', token_choices=[ 'PoundIfToken', @@ -115,7 +115,7 @@ children=[ Child('Clauses', kind='IfConfigClauseList', collection_element_name='Clause'), - Child('PoundEndif', kind='PoundEndifToken', + Child('PoundEndif', kind='PoundEndifToken', classification='BuildConfigId'), ]), @@ -137,7 +137,7 @@ Child('RightParen', kind='RightParenToken') ]), - Node('PoundSourceLocation', kind='Decl', + Node('PoundSourceLocation', kind='Decl', traits=['Parenthesized'], children=[ Child('PoundSourceLocation', kind='PoundSourceLocationToken'), @@ -148,12 +148,12 @@ Node('PoundSourceLocationArgs', kind='Syntax', children=[ - Child('FileArgLabel', kind='IdentifierToken', + Child('FileArgLabel', kind='IdentifierToken', text_choices=['file']), Child('FileArgColon', kind='ColonToken'), Child('FileName', kind='StringLiteralToken'), Child('Comma', kind='CommaToken'), - Child('LineArgLabel', kind='IdentifierToken', + Child('LineArgLabel', kind='IdentifierToken', text_choices=['line']), Child('LineArgColon', kind='ColonToken'), Child('LineNumber', kind='IntegerLiteralToken'), @@ -294,11 +294,11 @@ # member-decl = decl ';'? Node('MemberDeclListItem', kind='Syntax', omit_when_empty=True, description=''' - A member declaration of a type consisting of a declaration and an \ + A member declaration of a type consisting of a declaration and an optional semicolon; ''', children=[ - Child('Decl', kind='Decl', + Child('Decl', kind='Decl', description='The declaration of the type member.'), Child('Semicolon', kind='SemicolonToken', is_optional=True, description='An optional trailing semicolon.'), @@ -518,11 +518,11 @@ Child('Modifier', kind='DeclModifier', is_optional=True), Child('AccessorKind', kind='Token', text_choices=[ - 'get', 'set', 'didSet', 'willSet', 'unsafeAddress', - 'addressWithOwner', 'addressWithNativeOwner', - 'unsafeMutableAddress', - 'mutableAddressWithOwner', - 'mutableAddressWithNativeOwner', + 'get', 'set', 'didSet', 'willSet', 'unsafeAddress', + 'addressWithOwner', 'addressWithNativeOwner', + 'unsafeMutableAddress', + 'mutableAddressWithOwner', + 'mutableAddressWithNativeOwner', '_read', '_modify' ]), Child('Parameter', kind='AccessorParameter', is_optional=True), @@ -572,7 +572,7 @@ Node('EnumCaseElement', kind='Syntax', description=''' - An element of an enum case, containing the name of the case and, \ + An element of an enum case, containing the name of the case and, optionally, either associated values or an assignment to a raw value. ''', traits=['WithTrailingComma'], @@ -587,7 +587,7 @@ '''), Child('TrailingComma', kind='CommaToken', is_optional=True, description=''' - The trailing comma of this element, if the case has \ + The trailing comma of this element, if the case has multiple elements. '''), ]), @@ -598,7 +598,7 @@ Node('EnumCaseDecl', kind='Decl', description=''' - A `case` declaration of a Swift `enum`. It can have 1 or more \ + A `case` declaration of a Swift `enum`. It can have 1 or more `EnumCaseElement`s inside, each declaring a different case of the enum. ''', @@ -649,13 +649,13 @@ Child('InheritanceClause', kind='TypeInheritanceClause', is_optional=True, description=''' - The inheritance clause describing conformances or raw \ + The inheritance clause describing conformances or raw values for this enum. '''), Child('GenericWhereClause', kind='GenericWhereClause', is_optional=True, description=''' - The `where` clause that applies to the generic parameters of \ + The `where` clause that applies to the generic parameters of this enum. '''), Child('Members', kind='MemberDeclBlock', @@ -664,7 +664,7 @@ ''') ]), - # operator-decl -> attribute? modifiers? 'operator' operator + # operator-decl -> attribute? modifiers? 'operator' operator Node('OperatorDecl', kind='Decl', traits=['IdentifiedDecl'], description='A Swift `operator` declaration.', children=[ @@ -762,7 +762,7 @@ groups. ''', children=[ - Child('HigherThanOrLowerThan', kind='IdentifierToken', + Child('HigherThanOrLowerThan', kind='IdentifierToken', classification='Keyword', text_choices=[ 'higherThan', 'lowerThan', @@ -823,7 +823,7 @@ are grouped together in the absence of grouping parentheses. ''', children=[ - Child('AssociativityKeyword', kind='IdentifierToken', + Child('AssociativityKeyword', kind='IdentifierToken', classification='Keyword', text_choices=['associativity']), Child('Colon', kind='ColonToken'), Child('Value', kind='IdentifierToken', diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index 0925800ebd4ae..8f9835970008f 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -74,9 +74,6 @@ 'CapitalSelfToken', 'DollarIdentifierToken', 'SpacedBinaryOperatorToken', - 'InitToken', - 'DeinitToken', - 'SubscriptToken', ]), Child('DeclNameArguments', kind='DeclNameArguments', is_optional=True), @@ -115,7 +112,11 @@ Node('ExprList', kind='SyntaxCollection', element='Expr', - element_name='Expression'), + element_name='Expression', + description=''' + A list of expressions connected by operators. This list is contained + by a `SequenceExprSyntax`. + '''), # A #line expression. Node('PoundLineExpr', kind='Expr', @@ -129,6 +130,12 @@ Child('PoundFile', kind='PoundFileToken'), ]), + # A #filePath expression. + Node('PoundFilePathExpr', kind='Expr', + children=[ + Child('PoundFilePath', kind='PoundFilePathToken'), + ]), + # A #function expression. Node('PoundFunctionExpr', kind='Expr', children=[ @@ -507,8 +514,6 @@ Node('ObjcNamePiece', kind='Syntax', children=[ Child('Name', kind='IdentifierToken'), - Child('DeclNameArguments', kind='DeclNameArguments', - is_optional=True), Child('Dot', kind='PeriodToken', is_optional=True), ]), @@ -560,7 +565,5 @@ Child('Arguments', kind='TupleExprElementList', collection_element_name='Argument'), Child('RightParen', kind='RightParenToken'), - Child('TrailingClosure', kind='ClosureExpr', - is_optional=True), ]), ] diff --git a/utils/gyb_syntax_support/GenericNodes.py b/utils/gyb_syntax_support/GenericNodes.py index 61fd4cf0723ff..57ab1c5e67c4c 100644 --- a/utils/gyb_syntax_support/GenericNodes.py +++ b/utils/gyb_syntax_support/GenericNodes.py @@ -15,7 +15,7 @@ element_name='GenericRequirement'), # generic-requirement -> - # (same-type-requrement|conformance-requirement|layout-requirement) ','? + # (same-type-requrement|conformance-requirement) ','? Node('GenericRequirement', kind='Syntax', traits=['WithTrailingComma'], children=[ @@ -25,8 +25,6 @@ kind='SameTypeRequirement'), Child('ConformanceRequirement', kind='ConformanceRequirement'), - Child('LayoutRequirement', - kind='LayoutRequirement'), ]), Child('TrailingComma', kind='CommaToken', is_optional=True), @@ -72,8 +70,6 @@ Child('LeftAngleBracket', kind='LeftAngleToken'), Child('GenericParameterList', kind='GenericParameterList', collection_element_name='GenericParameter'), - Child('ObsoletedWhereClause', kind='GenericWhereClause', - is_optional=True), Child('RightAngleBracket', kind='RightAngleToken'), ]), @@ -84,29 +80,4 @@ Child('Colon', kind='ColonToken'), Child('RightTypeIdentifier', kind='Type'), ]), - - # layout-requirement -> type ':' layout-constraint - Node('LayoutRequirement', kind='Syntax', - children=[ - Child('LeftTypeIdentifier', kind='Type'), - Child('Colon', kind='ColonToken'), - Child('LayoutConstraint', kind='LayoutConstraint'), - ]), - - # layout-constraint -> - # identifier ('(' integer-literal (',' integer-literal)? ')')? - Node('LayoutConstraint', kind='Syntax', - children=[ - Child('Name', kind='IdentifierToken'), - Child('LeftParen', kind='LeftParenToken', - is_optional=True), - Child('Size', kind='IntegerLiteralToken', - is_optional=True), - Child('Comma', kind='CommaToken', - is_optional=True), - Child('Alignment', kind='IntegerLiteralToken', - is_optional=True), - Child('RightParen', kind='RightParenToken', - is_optional=True), - ]), ] diff --git a/utils/gyb_syntax_support/Node.py b/utils/gyb_syntax_support/Node.py index e60628837ed7a..cae36316e14c3 100644 --- a/utils/gyb_syntax_support/Node.py +++ b/utils/gyb_syntax_support/Node.py @@ -1,6 +1,5 @@ from __future__ import print_function - -import sys +import sys # noqa: I201 from kinds import SYNTAX_BASE_KINDS, kind_to_type, lowercase_first_word diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 8da7dfa39c074..5bcadc30f4a35 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -234,9 +234,16 @@ 'SomeType': 230, 'CustomAttribute': 231, 'GenericRequirement': 232, - 'LayoutRequirement': 233, - 'LayoutConstraint': 234, - 'OpaqueReturnTypeOfAttributeArguments': 235, + 'DifferentiableAttributeArguments': 233, + 'DifferentiationParamsClause': 234, + 'DifferentiationParams': 235, + 'DifferentiationParamList': 236, + 'DifferentiationParam': 237, + 'DifferentiableAttributeFuncSpecifier': 238, + 'FunctionDeclName': 239, + 'PoundFilePathExpr': 240, + 'DerivativeRegistrationAttributeArguments': 241, + 'QualifiedDeclName': 242, } diff --git a/utils/gyb_syntax_support/SILOnlyNodes.py b/utils/gyb_syntax_support/SILOnlyNodes.py deleted file mode 100644 index 5c6fa130e6046..0000000000000 --- a/utils/gyb_syntax_support/SILOnlyNodes.py +++ /dev/null @@ -1,49 +0,0 @@ -from Child import Child -from Node import Node # noqa: I201 - -# These nodes are used only in SIL parsing. - -SILONLY_NODES = [ - # generic-parameter-clause-list - Node('GenericParameterClauseList', kind='SyntaxCollection', - element='GenericParameterClause'), - - # sil-function-type -> generic-parameter-clause-list function-type - Node('SILFunctionType', kind='Type', - children=[ - Child('GenericParameterClauses', - kind='GenericParameterClauseList', - collection_element_name='GenericParameterClause', - is_optional=True), - Child('Function', kind='FunctionType'), - ]), - - # sil-box-type-field - Node('SILBoxTypeField', kind='Syntax', - children=[ - Child('Specifier', kind='Token', - token_choices=[ - 'LetToken', - 'VarToken', - ]), - Child('Type', kind='Type'), - Child('TrailingComma', kind='CommaToken', is_optional=True), - ]), - Node('SILBoxTypeFieldList', kind='SyntaxCollection', - element='SILBoxTypeField'), - - # sil-box-type -> generic-parameter-clause-list '{' - Node('SILBoxType', kind='Type', - children=[ - Child('GenericParameterClauses', - kind='GenericParameterClauseList', - collection_element_name='GenericParameterClause', - is_optional=True), - Child('LeftBrace', kind='LeftBraceToken'), - Child('Fields', kind='SILBoxTypeFieldList', - collection_element_name='Field'), - Child('RightBrace', kind='RightBraceToken'), - Child('GenericArgumentClause', kind='GenericArgumentClause', - is_optional=True), - ]), -] diff --git a/utils/gyb_syntax_support/StmtNodes.py b/utils/gyb_syntax_support/StmtNodes.py index cf71d0a5d6f3b..6d860abd708ad 100644 --- a/utils/gyb_syntax_support/StmtNodes.py +++ b/utils/gyb_syntax_support/StmtNodes.py @@ -153,8 +153,9 @@ Node('YieldList', kind='Syntax', children=[ Child('LeftParen', kind='LeftParenToken'), - Child('ElementList', kind='TupleExprElementList', + Child('ElementList', kind='ExprList', collection_element_name='Element'), + Child('TrailingComma', kind='CommaToken', is_optional=True), Child('RightParen', kind='RightParenToken'), ]), diff --git a/utils/gyb_syntax_support/Token.py b/utils/gyb_syntax_support/Token.py index 3e116471bcaa4..aaacc4fcfeb33 100644 --- a/utils/gyb_syntax_support/Token.py +++ b/utils/gyb_syntax_support/Token.py @@ -265,6 +265,8 @@ def macro_name(self): serialization_code=73), PoundKeyword('PoundFile', 'file', text='#file', serialization_code=68), + PoundKeyword('PoundFilePath', 'filePath', text='#filePath', + serialization_code=121), PoundKeyword('PoundColumn', 'column', text='#column', serialization_code=70), PoundKeyword('PoundFunction', 'function', text='#function', diff --git a/utils/gyb_syntax_support/__init__.py b/utils/gyb_syntax_support/__init__.py index a5d466c8f154a..63ddd8203bb29 100644 --- a/utils/gyb_syntax_support/__init__.py +++ b/utils/gyb_syntax_support/__init__.py @@ -1,57 +1,31 @@ import textwrap - -from . import Token -from .AttributeNodes import ATTRIBUTE_NODES -from .AvailabilityNodes import AVAILABILITY_NODES -from .Classification import SYNTAX_CLASSIFICATIONS -from .CommonNodes import COMMON_NODES -from .CompletionOnlyNodes import COMPLETIONONLY_NODES -from .DeclNodes import DECL_NODES -from .ExprNodes import EXPR_NODES -from .GenericNodes import GENERIC_NODES -from .NodeSerializationCodes import SYNTAX_NODE_SERIALIZATION_CODES, \ +from AttributeNodes import ATTRIBUTE_NODES # noqa: I201 +from AvailabilityNodes import AVAILABILITY_NODES # noqa: I201 +import Classification # noqa: I201 +from CommonNodes import COMMON_NODES # noqa: I201 +from DeclNodes import DECL_NODES # noqa: I201 +from ExprNodes import EXPR_NODES # noqa: I201 +from GenericNodes import GENERIC_NODES # noqa: I201 + +from NodeSerializationCodes import SYNTAX_NODE_SERIALIZATION_CODES, \ get_serialization_code, \ verify_syntax_node_serialization_codes -from .PatternNodes import PATTERN_NODES -from .SILOnlyNodes import SILONLY_NODES -from .StmtNodes import STMT_NODES -from .Token import SYNTAX_TOKENS, SYNTAX_TOKEN_MAP -from .Trivia import TRIVIAS -from .TypeNodes import TYPE_NODES - -__all__ = [ - 'Token', - 'AVAILABILITY_NODES', - 'SYNTAX_CLASSIFICATIONS', - 'COMMON_NODES', - 'DECL_NODES', - 'EXPR_NODES', - 'GENERIC_NODES', - 'SYNTAX_NODE_SERIALIZATION_CODES', - 'PATTERN_NODES', - 'PARSEONLY_NODES', - 'STMT_NODES', - 'SYNTAX_TOKENS', - 'SYNTAX_TOKEN_MAP', - 'TRIVIAS', - 'TYPE_NODES', - 'SYNTAX_NODES', - 'make_missing_child', - 'check_child_condition_raw', - 'check_parsed_child_condition_raw', - 'make_missing_swift_child', - 'create_node_map', - 'is_visitable', - 'dedented_lines', - 'calculate_node_hash', -] +from PatternNodes import PATTERN_NODES # noqa: I201 +from StmtNodes import STMT_NODES # noqa: I201 + +import Token +from Trivia import TRIVIAS # noqa: I201 +from TypeNodes import TYPE_NODES # noqa: I201 + +# Re-export global constants SYNTAX_NODES = COMMON_NODES + EXPR_NODES + DECL_NODES + ATTRIBUTE_NODES + \ STMT_NODES + GENERIC_NODES + TYPE_NODES + PATTERN_NODES + \ AVAILABILITY_NODES - -PARSEONLY_NODES = SILONLY_NODES + COMPLETIONONLY_NODES +SYNTAX_TOKENS = Token.SYNTAX_TOKENS +SYNTAX_TOKEN_MAP = Token.SYNTAX_TOKEN_MAP +SYNTAX_CLASSIFICATIONS = Classification.SYNTAX_CLASSIFICATIONS verify_syntax_node_serialization_codes(SYNTAX_NODES, SYNTAX_NODE_SERIALIZATION_CODES) @@ -156,7 +130,7 @@ def create_node_map(): """ Creates a lookup table to find nodes by their kind. """ - return {node.syntax_kind: node for node in SYNTAX_NODES + PARSEONLY_NODES} + return {node.syntax_kind: node for node in SYNTAX_NODES} def is_visitable(node): diff --git a/utils/lldb/lldbSwiftDataFormatters.py b/utils/lldb/lldbSwiftDataFormatters.py new file mode 100644 index 0000000000000..17d5d2d0817bb --- /dev/null +++ b/utils/lldb/lldbSwiftDataFormatters.py @@ -0,0 +1,47 @@ +""" +LLDB Formatters for LLVM data types for use in the swift project. + +Load into LLDB with 'command script import /path/to/lldbDataFormatters.py' +""" + +import sys + + +def __lldb_init_module(debugger, internal_dict): + tName = 'lldbSwiftDataFormatters.SmallBitVectorSummaryProvider' + debugger.HandleCommand('type summary add -w llvm ' + '-F %s -x "^llvm::SmallBitVector$"' % tName) + + +def SmallBitVectorSummaryProvider(valobj, internal_dict): + underlyingValue = valobj.GetChildMemberWithName('X').GetValueAsUnsigned() + numBaseBits = 32 + is64Bit = sys.maxsize > 2**32 + if is64Bit: + numBaseBits = 64 + smallNumRawBits = numBaseBits - 1 + smallNumSizeBits = None + if numBaseBits == 32: + smallNumSizeBits = 5 + elif numBaseBits == 64: + smallNumSizeBits = 6 + else: + smallNumSizeBits = smallNumRawBits + smallNumDataBits = smallNumRawBits - smallNumSizeBits + + # If our underlying value is not small, print we can not dump large values. + isSmallMask = 1 + if (underlyingValue & isSmallMask) == 0: + return '' + + smallRawBits = underlyingValue >> 1 + smallSize = smallRawBits >> smallNumDataBits + bits = smallRawBits & ((1 << (smallSize + 1)) - 1) + res = "[" + for i in reversed(range(0, smallSize)): + if bool(bits & (1 << i)): + res += '1' + else: + res += '0' + res += "]" + return res diff --git a/utils/lldb/lldbToolBox.py b/utils/lldb/lldbToolBox.py index 61200493590cd..54e1999759cfd 100644 --- a/utils/lldb/lldbToolBox.py +++ b/utils/lldb/lldbToolBox.py @@ -22,6 +22,8 @@ LLVM_REPO = os.path.join(REPO_BASE, "llvm") LLVM_DATAFORMATTER_PATH = os.path.join(LLVM_REPO, "utils", "lldbDataFormatters.py") +SWIFT_DATAFORMATTER_PATH = os.path.join(SWIFT_REPO, "utils", + "lldb", "lldbSwiftDataFormatters.py") def import_llvm_dataformatters(debugger): @@ -33,6 +35,15 @@ def import_llvm_dataformatters(debugger): print("Loaded LLVM data formatters.") +def import_swift_dataformatters(debugger): + if not os.access(SWIFT_DATAFORMATTER_PATH, os.F_OK): + print("WARNING! Could not find Swift data formatters!") + return + cmd = 'command script import {}'.format(SWIFT_DATAFORMATTER_PATH) + debugger.HandleCommand(cmd) + print("Loaded Swift data formatters.") + + VIEWCFG_PATH = os.path.join(SWIFT_REPO, "utils", "viewcfg") BLOCKIFYASM_PATH = os.path.join(SWIFT_REPO, "utils", "dev-scripts", "blockifyasm") @@ -107,6 +118,7 @@ def sequence(debugger, command, exec_ctx, result, internal_dict): def __lldb_init_module(debugger, internal_dict): import_llvm_dataformatters(debugger) + import_swift_dataformatters(debugger) debugger.HandleCommand('command script add disassemble-asm-cfg ' '-f lldbToolBox.disassemble_asm_cfg') debugger.HandleCommand('command script add disassemble-to-file ' diff --git a/utils/python_lint.py b/utils/python_lint.py index 35f4c3250606d..f17ca0d74398a 100755 --- a/utils/python_lint.py +++ b/utils/python_lint.py @@ -1,29 +1,45 @@ #!/usr/bin/env python -# python_lint.py - Runs flake8 linting over the repository ------*- python -*- -# + # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors # -from __future__ import print_function + +""" +Utility script used to run the flake8 linter over all the project Python +sources. +""" + + +from __future__ import absolute_import, print_function, unicode_literals import os import subprocess import sys -def lint(arguments, verbose=True): - flake8_result = subprocess.call( - [sys.executable, "-c", "import flake8; import flake8_import_order"] - ) - if flake8_result != 0: - if verbose: - print(""" +__all__ = [ + 'lint', +] + + +# ----------------------------------------------------------------------------- +# Constants + +_UTILS_DIR = os.path.abspath(os.path.dirname(__file__)) +_PROJECT_DIR = os.path.dirname(_UTILS_DIR) + +_REQUIRED_PACKAGES = [ + 'flake8', + 'flake8-import-order', +] + +_INSTALL_FLAKE8_MESSAGE = """ The flake8 and flake8-import-order Python packages are required for linting, but these were not found on your system. @@ -32,25 +48,49 @@ def lint(arguments, verbose=True): python -m pip install flake8 python -m pip install flake8-import-order -For more help, see http://flake8.pycqa.org.""") +For more help, see http://flake8.pycqa.org. +""" - # We should be returning `flake8_result` from here. However, - # some Python files lint themselves using embedded doctests, - # which causes CI smoke tests to fail because the Linux nodes - # do not have these modules installed. + +# ----------------------------------------------------------------------------- +# Helpers + +def _is_package_installed(name): + """Runs the pip command to check if a package is installed. + """ + + command = [ + sys.executable, + '-m', 'pip', + 'show', '--quiet', + name, + ] + + with open(os.devnull, 'w') as devnull: + status = subprocess.call(command, stderr=devnull) + + return not status + + +# ----------------------------------------------------------------------------- + +def lint(args, verbose=False): + all_packages_installed = all([ + _is_package_installed(name) + for name in _REQUIRED_PACKAGES + ]) + + if not all_packages_installed: + if verbose: + print(_INSTALL_FLAKE8_MESSAGE) return 0 - utils_directory = os.path.dirname(os.path.abspath(__file__)) - parent_directory = os.path.dirname(utils_directory) - linting_result = subprocess.call( - [sys.executable, "-m", "flake8"] + arguments, - cwd=parent_directory, - universal_newlines=True - ) - return linting_result + return subprocess.call( + [sys.executable, '-m', 'flake8'] + args, + cwd=_PROJECT_DIR, + universal_newlines=True) if __name__ == '__main__': - linting_result = lint(sys.argv[1:]) - sys.exit(linting_result) + sys.exit(lint(sys.argv[1:], verbose=True)) diff --git a/utils/run-test b/utils/run-test index 8300e8e180dd1..b06b74bbcc110 100755 --- a/utils/run-test +++ b/utils/run-test @@ -11,20 +11,16 @@ from __future__ import print_function -import argparse import multiprocessing import os import shutil import sys -from swift_build_support.swift_build_support import ( - arguments, - shell -) +from build_swift.build_swift import argparse +from swift_build_support.swift_build_support import shell from swift_build_support.swift_build_support.SwiftBuildSupport import \ SWIFT_SOURCE_ROOT - from swift_build_support.swift_build_support.targets import \ StdlibDeploymentTarget @@ -49,8 +45,19 @@ SWIFT_SOURCE_DIR = os.path.join(SWIFT_SOURCE_ROOT, 'swift') TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'test') VALIDATION_TEST_SOURCE_DIR = os.path.join(SWIFT_SOURCE_DIR, 'validation-test') -LIT_BIN_DEFAULT = os.path.join(SWIFT_SOURCE_ROOT, 'llvm', + +def _get_default_llvm_source_dir(): + legacy_llvm_dir_path = os.path.join(SWIFT_SOURCE_ROOT, 'llvm') + if os.path.isdir(legacy_llvm_dir_path): + return legacy_llvm_dir_path + return os.path.join(SWIFT_SOURCE_ROOT, 'llvm-project', 'llvm') + + +# Default path for "lit.py" executable. +LIT_BIN_DEFAULT = os.path.join(os.environ.get("LLVM_SOURCE_DIR", + _get_default_llvm_source_dir()), 'utils', 'lit', 'lit.py') + host_target = StdlibDeploymentTarget.host_target().name @@ -60,9 +67,14 @@ def error_exit(msg): # Return true if the path looks like swift build directory. -def is_swift_build_dir(path): +def is_swift_build_dir(path, unified_build_dir): + if not unified_build_dir: + tests_path = [path, "test-%s" % host_target] + else: + tests_path = [path, "tools", "swift", "test-%s" % host_target] + return (os.path.exists(os.path.join(path, "CMakeCache.txt")) and - os.path.isdir(os.path.join(path, "test-%s" % host_target))) + os.path.isdir(os.path.join(*tests_path))) # Return true if the swift build directory is configured with `Xcode` @@ -83,14 +95,19 @@ def is_subpath(path, d): # Convert test path in source directory to corresponding path in build # directory. If the path is not sub path of test directories in source, # return the path as is. -def normalize_test_path(path, build_dir, variant): +def normalize_test_path(path, build_dir, variant, unified_build_dir): + if not unified_build_dir: + tests_path = [build_dir] + else: + tests_path = [build_dir, "tools", "swift"] + for d, prefix in [(TEST_SOURCE_DIR, 'test-%s'), (VALIDATION_TEST_SOURCE_DIR, 'validation-test-%s')]: if is_subpath(path, d): - return os.path.normpath(os.path.join( - build_dir, - prefix % variant, - os.path.relpath(path, d))) + return os.path.normpath(os.path.join(*( + tests_path + + [prefix % variant, + os.path.relpath(path, d)]))) return path @@ -112,8 +129,8 @@ def main(): help="build test dependencies before running tests " "(default: true)") parser.add_argument("--target", - type=arguments.type.shell_split, - action=arguments.action.concat, + type=argparse.types.ShellSplitType, + action=argparse.actions.AppendAction, dest="targets", help="stdlib deployment targets to test. Accept " "multiple (default: " + host_target + ")") @@ -124,8 +141,8 @@ def main(): choices=TEST_SUBSETS, default='primary', help="test subset (default: primary)") parser.add_argument("--param", - type=arguments.type.shell_split, - action=arguments.action.concat, + type=argparse.types.ShellSplitType, + action=argparse.actions.AppendAction, default=[], help="key=value parameters they are directly passed " "to lit command in addition to `mode` and " @@ -135,11 +152,14 @@ def main(): parser.add_argument("--lit", default=LIT_BIN_DEFAULT, metavar="PATH", help="lit.py executable path " "(default: ${LLVM_SOURCE_DIR}/utils/lit/lit.py)") + parser.add_argument("--unified", action="store_true", + help="The build directory is an unified LLVM build, " + "not a standalone Swift build") args = parser.parse_args() targets = args.targets - if targets is None: + if not targets: targets = [host_target] paths = [] @@ -153,7 +173,7 @@ def main(): for d in [ build_dir, os.path.join(build_dir, 'swift-%s' % host_target)]: - if is_swift_build_dir(d): + if is_swift_build_dir(d, args.unified): build_dir = d break else: @@ -166,9 +186,8 @@ def main(): # $ run-test --build-dir= ... \ # --target macosx-x86_64 --target iphonesimulator-i386 for target in targets: - paths += map( - lambda p: normalize_test_path(p, build_dir, target), - args.paths) + paths += [normalize_test_path(p, build_dir, target, args.unified) + for p in args.paths] else: # Otherwise, we assume all given paths are valid test paths in the @@ -183,7 +202,7 @@ def main(): # Traverse the first test path to find the `build_dir` d = os.path.dirname(paths[0]) while d not in ['', os.sep]: - if is_swift_build_dir(d): + if is_swift_build_dir(d, args.unified): build_dir = d break d = os.path.dirname(d) diff --git a/utils/scale-test b/utils/scale-test index f6868007f01ee..b32a45e384b36 100755 --- a/utils/scale-test +++ b/utils/scale-test @@ -25,30 +25,23 @@ import shutil import subprocess import sys import tempfile - from collections import namedtuple from operator import attrgetter +from build_swift.build_swift import shell + import gyb from jobstats import load_stats_dir, merge_all_jobstats -def find_which(p): - for d in os.environ["PATH"].split(os.pathsep): - full = os.path.join(d, p) - if os.path.isfile(full) and os.access(full, os.X_OK): - return full - return p - - # Evidently the debug-symbol reader in dtrace is sufficiently slow and/or buggy # that attempting to inject probes into a binary w/ debuginfo is asking for a # failed run (possibly racing with probe insertion, or probing the stabs # entries, see rdar://problem/7037927 or rdar://problem/11490861 respectively), # so we sniff the presence of debug symbols here. def has_debuginfo(swiftc): - swiftc = find_which(swiftc) + swiftc = shell.which(swiftc) for line in subprocess.check_output( ["dwarfdump", "--file-stats", swiftc]).splitlines(): if '%' not in line: diff --git a/utils/sil-mode.el b/utils/sil-mode.el index 950e075469786..b55cf6ccee8b0 100644 --- a/utils/sil-mode.el +++ b/utils/sil-mode.el @@ -123,7 +123,7 @@ "autorelease_value" "copy_value" "destroy_value" "unmanaged_retain_value" "unmanaged_release_value" "unmanaged_autorelease_value" - "copy_unowned_value" "copy_unmanaged_value" + "strong_copy_unowned_value" "strong_copy_unmanaged_value" "destructure_struct" "destructure_tuple") 'words) . font-lock-keyword-face) ;; Enums. *NOTE* We do not include enum itself here since enum is a diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 16612bae2dbf3..e26ac8f8e505e 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -402,9 +402,18 @@ def main(): xfails = json.load(xfails_file) make_dirs_if_needed(args.output_dir, args.dry_run) - shared_output_lock = multiprocessing.Lock() - pool = multiprocessing.Pool(args.jobs, set_up_child, - (args, shared_output_lock)) + if 'ANDROID_DATA' not in os.environ: + shared_output_lock = multiprocessing.Lock() + pool = multiprocessing.Pool(args.jobs, set_up_child, + (args, shared_output_lock)) + else: + # Android doesn't support Python's multiprocessing as it doesn't have + # sem_open, so switch to a ThreadPool instead. + import threading + shared_output_lock = threading.Lock() + from multiprocessing.pool import ThreadPool + pool = ThreadPool(args.jobs, set_up_child, + (args, shared_output_lock)) interface_framework_dirs = (args.interface_framework_dirs or DEFAULT_FRAMEWORK_INTERFACE_SEARCH_PATHS) diff --git a/utils/swift_build_support/README.md b/utils/swift_build_support/README.md index 5746cb0174ced..4efae0cddb4d9 100644 --- a/utils/swift_build_support/README.md +++ b/utils/swift_build_support/README.md @@ -6,5 +6,5 @@ structures used by the Swift build script. You may run unit tests for `swift_build_support` from the command line: ```sh -apple/swift $ python -m unittest discover -s utils/swift_build_support +$ python utils/swift_build_support/run_tests.py ``` diff --git a/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/.gitignore b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/.gitignore new file mode 100644 index 0000000000000..f2246db9b5d3b --- /dev/null +++ b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/.gitignore @@ -0,0 +1 @@ +/xcuserdata diff --git a/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/contents.xcworkspacedata b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000..d8e87956220b8 --- /dev/null +++ b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/utils/swift_build_support/SwiftPM-Unified-Build.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/utils/swift_build_support/run_tests.py b/utils/swift_build_support/run_tests.py new file mode 100644 index 0000000000000..424044ed0b602 --- /dev/null +++ b/utils/swift_build_support/run_tests.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Small script used to easily run the swift_build_support module unit tests. +""" + + +from __future__ import absolute_import, unicode_literals + +import os +import sys +import unittest + + +MODULE_DIR = os.path.abspath(os.path.dirname(__file__)) +UTILS_DIR = os.path.abspath(os.path.join(MODULE_DIR, os.pardir)) + + +if __name__ == '__main__': + # Add the swift/utils directory to the Python path. + sys.path.append(UTILS_DIR) + + # Discover all tests for the module. + module_tests = unittest.defaultTestLoader.discover(MODULE_DIR) + + # Create and run test suite. + suite = unittest.TestSuite() + suite.addTests(module_tests) + + runner = unittest.TextTestRunner() + result = runner.run(suite) + + sys.exit(not result.wasSuccessful()) diff --git a/utils/swift_build_support/swift_build_support/SwiftBuildSupport.py b/utils/swift_build_support/swift_build_support/SwiftBuildSupport.py index 25c84ebfb0e0a..496295bd75bc2 100644 --- a/utils/swift_build_support/swift_build_support/SwiftBuildSupport.py +++ b/utils/swift_build_support/swift_build_support/SwiftBuildSupport.py @@ -10,17 +10,8 @@ from __future__ import print_function -try: - # Python 2 - import ConfigParser -except ImportError: - # Python 3 - import configparser as ConfigParser - import os -from . import diagnostics - HOME = os.environ.get("HOME", "/") @@ -86,123 +77,3 @@ def _get_default_swift_repo_name(): # directory name that is used. SWIFT_REPO_NAME = os.environ.get( "SWIFT_REPO_NAME", _get_default_swift_repo_name()) - - -def _load_preset_files_impl(preset_file_names, substitutions={}): - config = ConfigParser.SafeConfigParser(substitutions, allow_no_value=True) - if config.read(preset_file_names) == []: - diagnostics.fatal( - "preset file not found (tried " + str(preset_file_names) + ")") - return config - - -_PRESET_PREFIX = "preset: " - - -def _get_preset_options_impl(config, substitutions, preset_name): - section_name = _PRESET_PREFIX + preset_name - if section_name not in config.sections(): - return (None, None, None) - - build_script_opts = [] - build_script_impl_opts = [] - missing_opts = [] - dash_dash_seen = False - - for o in config.options(section_name): - try: - a = config.get(section_name, o) - except ConfigParser.InterpolationMissingOptionError as e: - # e.reference contains the correctly formatted option - missing_opts.append(e.reference) - continue - - if not a: - a = "" - - if o in substitutions: - continue - - opt = None - if o == "mixin-preset": - # Split on newlines and filter out empty lines. - mixins = filter(None, [m.strip() for m in a.splitlines()]) - for mixin in mixins: - (base_build_script_opts, - base_build_script_impl_opts, - base_missing_opts) = \ - _get_preset_options_impl(config, substitutions, mixin) - build_script_opts += base_build_script_opts - build_script_impl_opts += base_build_script_impl_opts - missing_opts += base_missing_opts - elif o == "dash-dash": - dash_dash_seen = True - elif a == "": - opt = "--" + o - else: - opt = "--" + o + "=" + a - - if opt: - if not dash_dash_seen: - build_script_opts.append(opt) - else: - build_script_impl_opts.append(opt) - - return (build_script_opts, build_script_impl_opts, missing_opts) - - -def get_preset_options(substitutions, preset_file_names, preset_name): - config = _load_preset_files_impl(preset_file_names, substitutions) - - (build_script_opts, build_script_impl_opts, missing_opts) = \ - _get_preset_options_impl(config, substitutions, preset_name) - if not build_script_opts and not build_script_impl_opts: - diagnostics.fatal("preset '" + preset_name + "' not found") - if missing_opts: - diagnostics.fatal("missing option(s) for preset '" + preset_name + - "': " + ", ".join(missing_opts)) - - # Migrate 'swift-sdks' parameter to 'stdlib-deployment-targets' - swift_sdks_opts = [opt for opt in build_script_impl_opts - if opt.startswith("--swift-sdks")] - try: - swift_sdks_opt = swift_sdks_opts[-1] - except IndexError: - swift_sdks_opt = None - - if swift_sdks_opt is not None: - sdks_to_configure = swift_sdks_opt.split("=")[1].split(";") - tgts = [] - # Expand SDKs in to their deployment targets - from swift_build_support.swift_build_support.targets \ - import StdlibDeploymentTarget - for sdk in sdks_to_configure: - if sdk == "OSX": - tgts += StdlibDeploymentTarget.OSX.targets - elif sdk == "IOS": - tgts += StdlibDeploymentTarget.iOS.targets - elif sdk == "IOS_SIMULATOR": - tgts += StdlibDeploymentTarget.iOSSimulator.targets - elif sdk == "TVOS": - tgts += StdlibDeploymentTarget.AppleTV.targets - elif sdk == "TVOS_SIMULATOR": - tgts += StdlibDeploymentTarget.AppleTVSimulator.targets - elif sdk == "WATCHOS": - tgts += StdlibDeploymentTarget.AppleWatch.targets - elif sdk == "WATCHOS_SIMULATOR": - tgts += StdlibDeploymentTarget.AppleWatchSimulator.targets - - build_script_opts.append("--stdlib-deployment-targets=" + - " ".join([tgt.name for tgt in tgts])) - - # Filter the swift-sdks parameter - build_script_impl_opts = [opt for opt in build_script_impl_opts - if not opt.startswith("--swift-sdks")] - - return build_script_opts + ["--"] + build_script_impl_opts - - -def get_all_preset_names(preset_file_names): - config = _load_preset_files_impl(preset_file_names) - return [name[len(_PRESET_PREFIX):] for name in config.sections() - if name.startswith(_PRESET_PREFIX)] diff --git a/utils/swift_build_support/swift_build_support/__init__.py b/utils/swift_build_support/swift_build_support/__init__.py index a93c741a585ba..8757e35cc222b 100644 --- a/utils/swift_build_support/swift_build_support/__init__.py +++ b/utils/swift_build_support/swift_build_support/__init__.py @@ -15,17 +15,13 @@ # # ---------------------------------------------------------------------------- -from .which import which - __all__ = [ "cmake", "debug", "diagnostics", "host_specific_configuration", - "migration", "tar", "targets", "toolchain", - "which", "xcrun", ] diff --git a/utils/swift_build_support/swift_build_support/arguments.py b/utils/swift_build_support/swift_build_support/arguments.py deleted file mode 100644 index 17f54e61d8891..0000000000000 --- a/utils/swift_build_support/swift_build_support/arguments.py +++ /dev/null @@ -1,278 +0,0 @@ -# swift_build_support/arguments.py ------------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -""" -argparse supplements -""" -# ---------------------------------------------------------------------------- - -from __future__ import absolute_import - -import argparse -import os -import re -import shlex - -__all__ = [ - "action", - "type", -] - - -class _Registry(object): - pass - - -def _register(registry, name, value): - setattr(registry, name, value) - - -# Types ---------------------------------------------------------------------- -type = _Registry() - - -def type_bool(string): - """ - A strict parser for bools - - unlike Python's `bool()`, where `bool('False')` is `True` - This function can be passed as `type=` argument to argparse to parse values - passed to command line arguments. - """ - if string in ['0', 'false', 'False']: - return False - if string in ['1', 'true', 'True']: - return True - raise argparse.ArgumentTypeError("%r is not a boolean value" % string) - - -_register(type, 'bool', type_bool) - - -def type_shell_split(string): - """ - Parse and split shell arguments string into a list of shell arguments. - - Recognize `,` as a separator as well as white spaces. - string: -BAR="foo bar" -BAZ='foo,bar',-QUX 42 - into - ['-BAR=foo bar', '-BAZ=foo,bar', "-QUX", "42"] - """ - lex = shlex.shlex(string, posix=True) - lex.whitespace_split = True - lex.whitespace += ',' - return list(lex) - - -_register(type, 'shell_split', type_shell_split) - - -# NOTE: This class is deprecated, use the analgous class with the same name -# in utils/build_swift/argparse/types.py instead. -class CompilerVersion(object): - """A typed representation of a compiler version.""" - - def __init__(self, string_representation, components): - self.string_representation = string_representation - self.components = components - - def __str__(self): - return self.string_representation - - -def type_clang_compiler_version(string): - """ - Parse version string and split into a tuple of strings - (major, minor, patch) - - Supports "MAJOR.MINOR.PATCH" and "MAJOR.MINOR.PATCH.PATCH" formats. - """ - m = re.match(r'^([0-9]+)\.([0-9]+)\.([0-9]+)(\.([0-9]+))?$', string) - if m is not None: - return CompilerVersion( - string_representation=string, - components=m.group(1, 2, 3, 5)) - raise argparse.ArgumentTypeError( - "%r is an invalid version value, " - "must be 'MAJOR.MINOR.PATCH' or " - "'MAJOR.MINOR.PATCH.PATCH'" % string) - - -_register(type, 'clang_compiler_version', type_clang_compiler_version) - - -def type_swift_compiler_version(string): - """ - Parse version string and split into a tuple of strings - (major, minor, patch) - - Supports "MAJOR.MINOR" and "MAJOR.MINOR.PATCH" formats. - """ - m = re.match(r'^([0-9]+)\.([0-9]+)(\.([0-9]+))?$', string) - if m is not None: - return CompilerVersion( - string_representation=string, - components=m.group(1, 2, 4)) - raise argparse.ArgumentTypeError( - "%r is an invalid version value, " - "must be 'MAJOR.MINOR' or " - "'MAJOR.MINOR.PATCH'" % string) - - -_register(type, 'swift_compiler_version', type_swift_compiler_version) - - -def type_executable(string): - """ - Check the string is executable path string. - - Convert it to absolute path. - """ - if os.path.isfile(string) and os.access(string, os.X_OK): - return os.path.abspath(string) - raise argparse.ArgumentTypeError( - "%r is not executable" % string) - - -_register(type, 'executable', type_executable) - -# Actions -------------------------------------------------------------------- -action = _Registry() - - -class _UnavailableAction(argparse.Action): - def __init__(self, - option_strings, - dest=argparse.SUPPRESS, - default=argparse.SUPPRESS, - nargs='?', - help=None): - super(_UnavailableAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=nargs, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - if option_string is not None: - arg = option_string - else: - arg = str(values) - parser.error('unknown argument: %s' % arg) - - -_register(action, 'unavailable', _UnavailableAction) - - -class _ConcatAction(argparse.Action): - - def __call__(self, parser, namespace, values, option_string=None): - old_val = getattr(namespace, self.dest) - if old_val is None: - val = values - else: - val = old_val + values - setattr(namespace, self.dest, val) - - -_register(action, 'concat', _ConcatAction) - - -class _OptionalBoolAction(argparse.Action): - def __init__(self, - option_strings, - dest, - default=False, - const=True, - metavar="BOOL", - help=None): - super(_OptionalBoolAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - metavar=metavar, - nargs="?", - type=type.bool, - help=help, - const=const) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values) - - -_register(action, 'optional_bool', _OptionalBoolAction) - - -_TRUE_VALUES = [True, 1, 'true', 'True', 'TRUE', '1'] -_FALSE_VALUES = [False, 0, 'false', 'False', 'FALSE', '0'] - - -class _OnOffAction(argparse.Action): - """Action that can be toggled on or off, defaulting to the off state. An - optional bool-ish argument can be passed to set the state manually. - """ - - def __init__(self, **kwargs): - assert 'choices' in kwargs and len(kwargs['choices']) == 2 - - self._on_value, self._off_value = kwargs.pop('choices') - kwargs['nargs'] = '?' - - if 'default' not in kwargs: - kwargs['default'] = self._off_value - - if 'metavar' not in kwargs: - kwargs['metavar'] = 'BOOL' - - super(_OnOffAction, self).__init__(**kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - if values is None: - val = self._on_value - elif values in _TRUE_VALUES: - val = self._on_value - elif values in _FALSE_VALUES: - val = self._off_value - else: - raise argparse.ArgumentTypeError( - values + ' is not a boolean value') - - setattr(namespace, self.dest, val) - - -class _EnableAction(_OnOffAction): - """Action that defaults to False when absent and to True when parsed with - the option to override the value by passing a bool-like value as an - argument. - """ - - def __init__(self, **kwargs): - kwargs['choices'] = (True, False) - super(_EnableAction, self).__init__(**kwargs) - - -_register(action, 'enable', _EnableAction) - - -class _DisableAction(_OnOffAction): - """Action that defaults to True when absent and to False when parsed with - the option to override the value by passing a bool-like value as an - argument. When overridden the resulting value is negated, thus passing - 'True' will result in the destination being set to False. - """ - - def __init__(self, **kwargs): - kwargs['choices'] = (False, True) - super(_DisableAction, self).__init__(**kwargs) - - -_register(action, 'disable', _DisableAction) diff --git a/utils/swift_build_support/swift_build_support/cache_util.py b/utils/swift_build_support/swift_build_support/cache_util.py deleted file mode 100644 index 493b558ea9084..0000000000000 --- a/utils/swift_build_support/swift_build_support/cache_util.py +++ /dev/null @@ -1,58 +0,0 @@ -# swift_build_support/cache_util.py -----------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -""" -Cache related utilities -""" -# ---------------------------------------------------------------------------- - -from functools import update_wrapper - -__all__ = [ - 'cached', - 'reify' -] - - -def cached(func): - """Decorator that caches result of method or function. - - Note: Support method or function. - """ - cache = {} - - def wrapper(*args, **kwargs): - key = tuple(args) + tuple(kwargs.items()) - if key not in cache: - result = func(*args, **kwargs) - cache[key] = result - return result - else: - return cache[key] - - return update_wrapper(wrapper, func) - - -def reify(func): - """Decorator that replaces the wrapped method with the result after the - first call. - - Note: Support method that takes no arguments. - """ - class Wrapper(object): - def __get__(self, obj, objtype=None): - if obj is None: - return self - result = func(obj) - setattr(obj, func.__name__, result) - return result - - return update_wrapper(Wrapper(), func) diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index ae1b2bcf32172..cc1a8a9a3dbb8 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -14,13 +14,16 @@ # # ---------------------------------------------------------------------------- -from __future__ import absolute_import + +from __future__ import absolute_import, unicode_literals import os import platform import re from numbers import Number +import six + from . import shell @@ -43,7 +46,7 @@ def define(self, var, value): value = self.true_false(value) if value is None: value = "" - elif not isinstance(value, (str, Number)): + elif not isinstance(value, six.string_types + (Number,)): raise ValueError('define: invalid value for key %s: %s (%s)' % (var, value, type(value))) self._options.append('-D%s=%s' % (var, value)) @@ -177,7 +180,7 @@ def build_args(self): elif args.cmake_generator == 'Xcode': build_args += ['-parallelizeTargets', - '-jobs', str(jobs)] + '-jobs', six.text_type(jobs)] return build_args @@ -242,7 +245,8 @@ def build_cmake(self, source_root, build_root): cwd = os.getcwd() os.chdir(cmake_build_dir) - shell.call_without_sleeping([cmake_bootstrap], echo=True) + shell.call_without_sleeping([cmake_bootstrap, '--no-qt-gui'], + echo=True) shell.call_without_sleeping(['make', '-j%s' % self.args.build_jobs], echo=True) os.chdir(cwd) diff --git a/utils/swift_build_support/swift_build_support/debug.py b/utils/swift_build_support/swift_build_support/debug.py deleted file mode 100644 index e4369b97d1902..0000000000000 --- a/utils/swift_build_support/swift_build_support/debug.py +++ /dev/null @@ -1,46 +0,0 @@ -# swift_build_support/debug.py - Print information on the build -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -# -# Convenient functions for printing out information on the build process. -# -# ---------------------------------------------------------------------------- - -from __future__ import absolute_import -from __future__ import print_function - -import sys - -from . import shell - - -def print_xcodebuild_versions(file=sys.stdout): - """ - Print the host machine's `xcodebuild` version, as well as version - information for all available SDKs. - """ - version = shell.capture( - ['xcodebuild', '-version'], dry_run=False, echo=False).rstrip() - # Allow non-zero exit codes. Under certain obscure circumstances - # xcodebuild can exit with a non-zero exit code even when the SDK is - # usable. - sdks = shell.capture( - ['xcodebuild', '-version', '-sdk'], dry_run=False, echo=False, - allow_non_zero_exit=True).rstrip() - fmt = """\ -{version} - ---- SDK versions --- -{sdks} - -""" - print(fmt.format(version=version, sdks=sdks), file=file) - file.flush() diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index e5316b043eff8..ecdb437113321 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -149,8 +149,18 @@ def __init__(self, host_target, args): subset_suffix = "-only_stress" else: subset_suffix = "" - self.swift_test_run_targets.append("check-swift{}{}-{}".format( - subset_suffix, suffix, name)) + + # Support for running the macCatalyst tests with + # the iOS-like target triple. + if name == "macosx-x86_64" and args.maccatalyst \ + and args.maccatalyst_ios_tests: + (self.swift_test_run_targets + .append("check-swift{}{}-{}".format( + subset_suffix, suffix, "macosx-maccatalyst-x86_64"))) + else: + (self.swift_test_run_targets + .append("check-swift{}{}-{}".format( + subset_suffix, suffix, name))) if args.test_optimized and not test_host_only: self.swift_test_run_targets.append( "check-swift{}-optimize-{}".format( diff --git a/utils/swift_build_support/swift_build_support/migration.py b/utils/swift_build_support/swift_build_support/migration.py deleted file mode 100644 index 01eacbb7a64db..0000000000000 --- a/utils/swift_build_support/swift_build_support/migration.py +++ /dev/null @@ -1,55 +0,0 @@ -# swift_build_support/migration.py - Migrating build-script -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -# -# utils/build-script takes arguments for its argument parser, as well as -# arguments that are meant to be passed directly to utils/build-script-impl. -# In order to gradually migrate away from build-script-impl, this module -# provides tools to handle parsing of these args. -# -# ---------------------------------------------------------------------------- - -import subprocess - - -def parse_args(parser, argv): - """ - Parse given argument list with given argparse.ArgumentParser. - - Return a processed arguments object. Any unknown arguments are stored in - `build_script_impl_args` attribute as a list. - Ignores '--' to be compatible with old style argument list. - - build-script -RT -- --reconfigure - """ - args, unknown_args = parser.parse_known_args( - list(arg for arg in argv if arg != '--')) - args.build_script_impl_args = unknown_args - return args - - -def check_impl_args(build_script_impl, argv): - """ - Check whether given argv are all known arguments for `build-script-impl`. - - Raise ValueError with message if any invalid argument is found. - Return nothing if success. - """ - pipe = subprocess.Popen( - [build_script_impl, '--check-args-only=1'] + argv, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - - (_, err) = pipe.communicate() - - if pipe.returncode != 0: - msg = str(err.splitlines()[0].decode()) - raise ValueError(msg) diff --git a/utils/swift_build_support/swift_build_support/multiroot_data_file.py b/utils/swift_build_support/swift_build_support/multiroot_data_file.py new file mode 100644 index 0000000000000..47c978f7cbaf1 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/multiroot_data_file.py @@ -0,0 +1,23 @@ +# swift_build_support/multiroot_data_file.py - Unified build -----*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +# ---------------------------------------------------------------------------- + +import os + + +def path(): + """product_source_name() -> str + + The path to the Xcode workspace to use for a unified build of multiple + SwiftPM projects. + """ + return os.path.join(os.path.dirname(__file__), '..', + 'SwiftPM-Unified-Build.xcworkspace') diff --git a/utils/swift_build_support/swift_build_support/products/__init__.py b/utils/swift_build_support/swift_build_support/products/__init__.py index 526ccb22959d3..3bc1e7d6ec857 100644 --- a/utils/swift_build_support/swift_build_support/products/__init__.py +++ b/utils/swift_build_support/swift_build_support/products/__init__.py @@ -21,6 +21,7 @@ from .lldb import LLDB from .llvm import LLVM from .ninja import Ninja +from .pythonkit import PythonKit from .skstresstester import SKStressTester from .sourcekitlsp import SourceKitLSP from .swift import Swift @@ -41,6 +42,7 @@ 'LLDB', 'LLVM', 'Ninja', + 'PythonKit', 'Swift', 'SwiftPM', 'XCTest', diff --git a/utils/swift_build_support/swift_build_support/products/benchmarks.py b/utils/swift_build_support/swift_build_support/products/benchmarks.py index 5147f2c64c523..65232f8060fdc 100644 --- a/utils/swift_build_support/swift_build_support/products/benchmarks.py +++ b/utils/swift_build_support/swift_build_support/products/benchmarks.py @@ -28,9 +28,15 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + def should_build(self, host_target): + return True + def build(self, host_target): run_build_script_helper(host_target, self, self.args) + def should_test(self, host_target): + return self.args.test_toolchainbenchmarks + def test(self, host_target): """Just run a single instance of the command for both .debug and .release. @@ -45,6 +51,9 @@ def test(self, host_target): bench_Osize = os.path.join(self.build_dir, 'bin', 'Benchmark_Osize') shell.call([bench_Osize] + cmdline) + def should_install(self, host_target): + return False + def install(self, host_target): pass diff --git a/utils/swift_build_support/swift_build_support/products/indexstoredb.py b/utils/swift_build_support/swift_build_support/products/indexstoredb.py index d1857e731f470..f612df34ebc2c 100644 --- a/utils/swift_build_support/swift_build_support/products/indexstoredb.py +++ b/utils/swift_build_support/swift_build_support/products/indexstoredb.py @@ -11,7 +11,6 @@ # ---------------------------------------------------------------------------- import os -import platform from . import product from .. import shell @@ -27,12 +26,20 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + def should_build(self, host_target): + return True + def build(self, host_target): run_build_script_helper('build', host_target, self, self.args) + def should_test(self, host_target): + return self.args.test_indexstoredb + def test(self, host_target): - if self.args.test and self.args.test_indexstoredb: - run_build_script_helper('test', host_target, self, self.args) + run_build_script_helper('test', host_target, self, self.args) + + def should_install(self, host_target): + return False def install(self, host_target): pass @@ -41,20 +48,22 @@ def install(self, host_target): def run_build_script_helper(action, host_target, product, args): script_path = os.path.join( product.source_dir, 'Utilities', 'build-script-helper.py') - toolchain_path = args.install_destdir - if platform.system() == 'Darwin': - # The prefix is an absolute path, so concatenate without os.path. - toolchain_path += \ - targets.darwin_toolchain_prefix(args.install_prefix) - configuration = 'debug' if args.build_variant == 'Debug' else 'release' + + toolchain_path = targets.toolchain_path(args.install_destdir, + args.install_prefix) + + is_release = product.is_release() + configuration = 'release' if is_release else 'debug' helper_cmd = [ script_path, action, - '--verbose', '--package-path', product.source_dir, '--build-path', product.build_dir, '--configuration', configuration, '--toolchain', toolchain_path, '--ninja-bin', product.toolchain.ninja, ] + if args.verbose_build: + helper_cmd.append('--verbose') + shell.call(helper_cmd) diff --git a/utils/swift_build_support/swift_build_support/products/ninja.py b/utils/swift_build_support/swift_build_support/products/ninja.py index dd19b251120c0..53ebb15c6e32e 100644 --- a/utils/swift_build_support/swift_build_support/products/ninja.py +++ b/utils/swift_build_support/swift_build_support/products/ninja.py @@ -18,8 +18,10 @@ import platform import sys +from build_swift.build_swift import cache_utils +from build_swift.build_swift.wrappers import xcrun + from . import product -from .. import cache_util from .. import shell @@ -42,7 +44,7 @@ def __init__(self, product_class, args, toolchain, workspace): self.args = args self.toolchain = toolchain - @cache_util.reify + @cache_utils.reify def ninja_bin_path(self): return os.path.join(self.build_dir, 'ninja') @@ -52,7 +54,6 @@ def build(self): env = None if platform.system() == "Darwin": - from .. import xcrun sysroot = xcrun.sdk_path("macosx") osx_version_min = self.args.darwin_deployment_version_osx assert sysroot is not None diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index 0539e345cf0a7..6837e563c91ae 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -13,6 +13,11 @@ import abc from .. import cmake +from .. import targets + + +def is_release_variant(build_variant): + return build_variant in ['Release', 'RelWithDebInfo'] class Product(object): @@ -32,6 +37,16 @@ def product_source_name(cls): It provides a customization point for Product subclasses. It is set to the value of product_name() by default for this reason. """ + + llvm_projects = ['clang', + 'clang-tools-extra', + 'compiler-rt', + 'libcxx', + 'lldb', + 'llvm'] + + if cls.product_name() in llvm_projects: + return "llvm-project/{}".format(cls.product_name()) return cls.product_name() @classmethod @@ -42,6 +57,22 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_swiftpm_unified_build_product(cls): + """is_swiftpm_unified_build_product -> bool + + Whether this product should be build in the unified build of SwiftPM + products. + """ + return False + + def should_build(self, host_target): + """should_build() -> Bool + + Whether or not this product should be built with the given arguments. + """ + raise NotImplementedError + def build(self, host_target): """build() -> void @@ -49,6 +80,13 @@ def build(self, host_target): """ raise NotImplementedError + def should_test(self, host_target): + """should_test() -> Bool + + Whether or not this product should be tested with the given arguments. + """ + raise NotImplementedError + def test(self, host_target): """test() -> void @@ -56,6 +94,14 @@ def test(self, host_target): """ raise NotImplementedError + def should_install(self, host_target): + """should_install() -> Bool + + Whether or not this product should be installed with the given + arguments. + """ + raise NotImplementedError + def install(self, host_target): """install() -> void @@ -64,12 +110,41 @@ def install(self, host_target): raise NotImplementedError def __init__(self, args, toolchain, source_dir, build_dir): + """ + Parameters + ---------- + args : `argparse.Namespace` + The arguments passed by the user to the invocation of the script. + toolchain : `swift_build_support.toolchain.Toolchain` + The toolchain being used to build the product. The toolchain will + point to the tools that the builder should use to build (like the + compiler or the linker). + build_dir: string + The directory in which the product should put all of its build + products. + """ self.args = args self.toolchain = toolchain self.source_dir = source_dir self.build_dir = build_dir self.cmake_options = cmake.CMakeOptions() + def is_release(self): + """is_release() -> Bool + + Whether or not this target is built as a release variant + """ + return is_release_variant(self.args.build_variant) + + def install_toolchain_path(self): + """toolchain_path() -> string + + Returns the path to the toolchain that is being created as part of this + build. + """ + return targets.toolchain_path(self.args.install_destdir, + self.args.install_prefix) + class ProductBuilder(object): """ diff --git a/utils/swift_build_support/swift_build_support/products/pythonkit.py b/utils/swift_build_support/swift_build_support/products/pythonkit.py new file mode 100644 index 0000000000000..7b2d4667d8f11 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/pythonkit.py @@ -0,0 +1,60 @@ +# swift_build_support/products/pythonkit.py ---------------------*- python -*- +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +# ---------------------------------------------------------------------------- + +from . import product +from .. import shell + + +class PythonKit(product.Product): + @classmethod + def product_source_name(cls): + return "PythonKit" + + @classmethod + def is_build_script_impl_product(cls): + return False + + def should_build(self, host_target): + return True + + def build(self, host_target): + shell.call([ + self.toolchain.cmake, + '-G', 'Ninja', + '-D', 'BUILD_SHARED_LIBS=YES', + '-D', 'CMAKE_INSTALL_PREFIX={}/usr'.format( + self.args.install_destdir), + '-D', 'CMAKE_MAKE_PROGRAM={}'.format(self.toolchain.ninja), + '-D', 'CMAKE_Swift_COMPILER={}'.format(self.toolchain.swiftc), + '-B', self.build_dir, + '-S', self.source_dir, + ]) + shell.call([ + self.toolchain.cmake, + '--build', self.build_dir, + ]) + + def should_test(self, host_target): + return self.args.test_pythonkit + + def test(self, host_target): + pass + + def should_install(self, host_target): + return self.args.install_pythonkit + + def install(self, host_target): + shell.call([ + self.toolchain.cmake, + '--build', self.build_dir, + '--target', 'install', + ]) diff --git a/utils/swift_build_support/swift_build_support/products/skstresstester.py b/utils/swift_build_support/swift_build_support/products/skstresstester.py index 5671317200be4..a47dab41eea0b 100644 --- a/utils/swift_build_support/swift_build_support/products/skstresstester.py +++ b/utils/swift_build_support/swift_build_support/products/skstresstester.py @@ -1,3 +1,4 @@ + # swift_build_support/products/skstresstester.py -----------------*- python -*- # # This source file is part of the Swift.org open source project @@ -10,7 +11,12 @@ # # ---------------------------------------------------------------------------- +import os +import platform + from . import product +from .. import multiroot_data_file +from .. import shell class SKStressTester(product.Product): @@ -21,3 +27,65 @@ def product_source_name(cls): The name of the source code directory of this product. """ return "swift-stress-tester" + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_swiftpm_unified_build_product(cls): + return True + + def package_name(self): + return 'SourceKitStressTester' + + def run_build_script_helper(self, action, additional_params=[]): + script_path = os.path.join( + self.source_dir, 'build-script-helper.py') + + configuration = 'release' if self.is_release() else 'debug' + + helper_cmd = [ + script_path, + action, + '--package-dir', self.package_name(), + '--toolchain', self.install_toolchain_path(), + '--config', configuration, + '--build-dir', self.build_dir, + '--multiroot-data-file', multiroot_data_file.path(), + # There might have been a Package.resolved created by other builds + # or by the package being opened using Xcode. Discard that and + # reset the dependencies to be local. + '--update' + ] + if self.args.verbose_build: + helper_cmd.append('--verbose') + helper_cmd.extend(additional_params) + + shell.call(helper_cmd) + + def should_build(self, host_target): + return True + + def build(self, host_target): + if platform.system() != 'Darwin': + raise RuntimeError("Unable to build {product} on a platform other " + "than Darwin".format( + product=self.package_name())) + + self.run_build_script_helper('build') + + def should_test(self, host_target): + return self.args.test_skstresstester + + def test(self, host_target): + self.run_build_script_helper('test') + + def should_install(self, host_target): + return self.args.install_skstresstester + + def install(self, host_target): + install_prefix = self.args.install_destdir + self.args.install_prefix + self.run_build_script_helper('install', [ + '--prefix', install_prefix + ]) diff --git a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py index 802c9f7359620..d51e68d6259da 100644 --- a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py +++ b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py @@ -23,16 +23,23 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + def should_build(self, host_target): + return True + def build(self, host_target): indexstoredb.run_build_script_helper( 'build', host_target, self, self.args) + def should_test(self, host_target): + return self.args.test_sourcekitlsp + def test(self, host_target): - if self.args.test_sourcekitlsp: - indexstoredb.run_build_script_helper( - 'test', host_target, self, self.args) + indexstoredb.run_build_script_helper( + 'test', host_target, self, self.args) + + def should_install(self, host_target): + return self.args.install_sourcekitlsp def install(self, host_target): - if self.args.install_sourcekitlsp: - indexstoredb.run_build_script_helper( - 'install', host_target, self, self.args) + indexstoredb.run_build_script_helper( + 'install', host_target, self, self.args) diff --git a/utils/swift_build_support/swift_build_support/products/swift.py b/utils/swift_build_support/swift_build_support/products/swift.py index d10c3d62c6dec..e4259b018ef54 100644 --- a/utils/swift_build_support/swift_build_support/products/swift.py +++ b/utils/swift_build_support/swift_build_support/products/swift.py @@ -41,6 +41,10 @@ def __init__(self, args, toolchain, source_dir, build_dir): # Add any exclusivity checking flags for stdlibcore. self.cmake_options.extend(self._stdlibcore_exclusivity_checking_flags) + # Add experimental differentiable programming flag. + self.cmake_options.extend( + self._enable_experimental_differentiable_programming) + @property def _runtime_sanitizer_flags(self): sanitizer_list = [] @@ -112,3 +116,8 @@ def _force_optimized_typechecker_flags(self): def _stdlibcore_exclusivity_checking_flags(self): return [('SWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL', self.args.enable_stdlibcore_exclusivity_checking)] + + @property + def _enable_experimental_differentiable_programming(self): + return [('SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL', + self.args.enable_experimental_differentiable_programming)] diff --git a/utils/swift_build_support/swift_build_support/products/swiftevolve.py b/utils/swift_build_support/swift_build_support/products/swiftevolve.py index 9725a285fd348..579e90b6e0b4f 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftevolve.py +++ b/utils/swift_build_support/swift_build_support/products/swiftevolve.py @@ -1,4 +1,4 @@ -# swift_build_support/products/skstresstester.py -----------------*- python -*- +# swift_build_support/products/swiftevolve.py --------------------*- python -*- # # This source file is part of the Swift.org open source project # @@ -10,10 +10,10 @@ # # ---------------------------------------------------------------------------- -from . import product +from . import skstresstester -class SwiftEvolve(product.Product): +class SwiftEvolve(skstresstester.SKStressTester): @classmethod def product_source_name(cls): """product_source_name() -> str @@ -21,3 +21,17 @@ def product_source_name(cls): The name of the source code directory of this product. """ return "swift-stress-tester" + + def package_name(self): + return 'SwiftEvolve' + + # Inherit the entire build configuration from the SourceKit stress tester + + def should_build(self, host_target): + return True + + def should_test(self, host_target): + return self.args.test_swiftevolve + + def should_install(self, host_target): + return self.args.install_swiftevolve diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index e4991f8a7792e..6a6850e53501b 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -2,7 +2,7 @@ # # This source file is part of the Swift.org open source project # -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -10,8 +10,67 @@ # # ---------------------------------------------------------------------------- +import os + from . import product +from .. import shell class SwiftPM(product.Product): - pass + @classmethod + def product_source_name(cls): + return "swiftpm" + + @classmethod + def is_build_script_impl_product(cls): + return False + + def should_build(self, host_target): + return True + + def run_bootstrap_script(self, action, host_target, additional_params=[]): + script_path = os.path.join( + self.source_dir, 'Utilities', 'bootstrap') + toolchain_path = self.install_toolchain_path() + swiftc = os.path.join(toolchain_path, "usr", "bin", "swiftc") + + # FIXME: We require llbuild build directory in order to build. Is + # there a better way to get this? + build_root = os.path.dirname(self.build_dir) + llbuild_build_dir = os.path.join( + build_root, '%s-%s' % ("llbuild", host_target)) + + helper_cmd = [script_path, action] + + if self.is_release(): + helper_cmd.append("--release") + + helper_cmd += [ + "--swiftc-path", swiftc, + "--clang-path", self.toolchain.cc, + "--cmake-path", self.toolchain.cmake, + "--ninja-path", self.toolchain.ninja, + "--build-dir", self.build_dir, + "--llbuild-build-dir", llbuild_build_dir + ] + helper_cmd.extend(additional_params) + + shell.call(helper_cmd) + + def build(self, host_target): + self.run_bootstrap_script('build', host_target, ["--reconfigure"]) + + def should_test(self, host_target): + return self.args.test_swiftpm + + def test(self, host_target): + self.run_bootstrap_script('test', host_target) + + def should_install(self, host_target): + return self.args.install_swiftpm + + def install(self, host_target): + install_prefix = self.args.install_destdir + self.args.install_prefix + self.run_bootstrap_script('install', host_target, [ + '--prefix', install_prefix + ]) diff --git a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py index 5e10b5b96424a..33667db4470af 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py +++ b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py @@ -10,7 +10,11 @@ # # ---------------------------------------------------------------------------- +import os + from . import product +from .. import multiroot_data_file +from .. import shell class SwiftSyntax(product.Product): @@ -21,3 +25,68 @@ def product_source_name(cls): The name of the source code directory of this product. """ return "swift-syntax" + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_swiftpm_unified_build_product(cls): + return True + + def run_swiftsyntax_build_script(self, target, additional_params=[]): + llvm_build_dir = os.path.join(self.build_dir, '..', 'llvm-' + target) + llvm_build_dir = os.path.realpath(llvm_build_dir) + + script_path = os.path.join(self.source_dir, 'build-script.py') + + build_cmd = [ + script_path, + '--build-dir', self.build_dir, + '--multiroot-data-file', multiroot_data_file.path(), + '--toolchain', self.install_toolchain_path(), + '--filecheck-exec', os.path.join(llvm_build_dir, 'bin', + 'FileCheck'), + ] + + if self.is_release(): + build_cmd.append('--release') + + if self.args.swiftsyntax_verify_generated_files: + build_cmd.append('--verify-generated-files') + + build_cmd.extend(additional_params) + + if self.args.verbose_build: + build_cmd.append('--verbose') + + shell.call(build_cmd) + + def should_build(self, host_target): + return True + + def build(self, host_target): + self.run_swiftsyntax_build_script(target=host_target) + + def should_test(self, host_target): + return self.args.test_swiftsyntax + + def test(self, host_target): + self.run_swiftsyntax_build_script(target=host_target, + additional_params=['--test']) + + def should_install(self, host_target): + return self.args.install_swiftsyntax + + def install(self, target_name): + install_prefix = self.args.install_destdir + self.args.install_prefix + + dylib_dir = os.path.join(install_prefix, 'lib') + + additional_params = [ + '--dylib-dir', dylib_dir, + '--install' + ] + + self.run_swiftsyntax_build_script(target=target_name, + additional_params=additional_params) diff --git a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py index 4cbaadf084d2d..be1ecd620453b 100644 --- a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py +++ b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py @@ -29,6 +29,9 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + def should_build(self, host_target): + return True + def build(self, host_target): """Build TSan runtime (compiler-rt).""" rt_source_dir = join_path(self.source_dir, os.pardir, 'compiler-rt') @@ -59,6 +62,9 @@ def build(self, host_target): shell.call(config_cmd) shell.call(build_cmd) + def should_test(self, host_target): + return True + def test(self, host_target): """Run check-tsan target with a LIT filter for libdispatch.""" cmd = ['ninja', 'check-tsan'] @@ -67,5 +73,8 @@ def test(self, host_target): with shell.pushd(self.build_dir): shell.call(cmd, env=env) + def should_install(self, host_target): + return False + def install(self, host_target): pass diff --git a/utils/swift_build_support/swift_build_support/shell.py b/utils/swift_build_support/swift_build_support/shell.py index 2316128fdc7e5..4edfce0838de4 100644 --- a/utils/swift_build_support/swift_build_support/shell.py +++ b/utils/swift_build_support/swift_build_support/shell.py @@ -22,7 +22,6 @@ import subprocess import sys from contextlib import contextmanager -from multiprocessing import Lock, Pool, cpu_count from . import diagnostics @@ -230,37 +229,3 @@ def run(*args, **kwargs): eout.stderr = stderr raise eout return (stdout, 0, args) - - -def init(l): - global lock - lock = l - - -def run_parallel(fn, pool_args, n_processes=0): - if n_processes == 0: - n_processes = cpu_count() * 2 - - lk = Lock() - print("Running ``%s`` with up to %d processes." % - (fn.__name__, n_processes)) - pool = Pool(processes=n_processes, initializer=init, initargs=(lk,)) - results = pool.map_async(func=fn, iterable=pool_args).get(999999) - pool.close() - pool.join() - return results - - -def check_parallel_results(results, op): - fail_count = 0 - if results is None: - return 0 - for r in results: - if r is not None: - if fail_count == 0: - print("======%s FAILURES======" % op) - print("%s failed (ret=%d): %s" % (r.repo_path, r.ret, r)) - fail_count += 1 - if r.stderr: - print(r.stderr) - return fail_count diff --git a/utils/swift_build_support/swift_build_support/tar.py b/utils/swift_build_support/swift_build_support/tar.py deleted file mode 100644 index 15d29ee5d40b3..0000000000000 --- a/utils/swift_build_support/swift_build_support/tar.py +++ /dev/null @@ -1,34 +0,0 @@ -# swift_build_support/tar.py - Call tar from Python -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -from __future__ import absolute_import - -import platform - -from . import shell - - -def tar(source, destination): - """ - Create a gzip archive of the file at 'source' at the given - 'destination' path. - """ - # We do not use `tarfile` here because: - # - We wish to support LZMA2 compression while also supporting Python 2.7. - # - We wish to explicitly set the owner and group of the archive. - args = ['tar', '-c', '-z', '-f', destination] - - if platform.system() != 'Darwin' and platform.system() != 'Windows': - args += ['--owner=0', '--group=0'] - - # Discard stderr output such as 'tar: Failed to open ...'. We'll detect - # these cases using the exit code, which should cause 'shell.call' to - # raise. - shell.call(args + [source], stderr=shell.DEVNULL) diff --git a/utils/swift_build_support/swift_build_support/targets.py b/utils/swift_build_support/swift_build_support/targets.py index 5ec02dfcc7fec..45bb45ca9916a 100644 --- a/utils/swift_build_support/swift_build_support/targets.py +++ b/utils/swift_build_support/swift_build_support/targets.py @@ -70,7 +70,7 @@ def __init__(self, name, archs, sdk_name=None, is_simulator=False): @property def is_embedded(self): """Check if this is a Darwin platform for embedded devices.""" - return self.name != "macosx" + return self.name != "macosx" and self.name != "maccatalyst" @property def supports_benchmark(self): @@ -186,6 +186,14 @@ def host_target(): machine = platform.machine() if system == 'Linux': + if 'ANDROID_DATA' in os.environ: + if machine.startswith('armv7'): + return StdlibDeploymentTarget.Android.armv7 + elif machine == 'aarch64': + return StdlibDeploymentTarget.Android.aarch64 + raise NotImplementedError('Android System with architecture ' + '"%s" is not supported' % machine) + if machine == 'x86_64': return StdlibDeploymentTarget.Linux.x86_64 elif machine == 'i686': @@ -228,32 +236,6 @@ def host_target(): raise NotImplementedError('System "%s" with architecture "%s" is not ' 'supported' % (system, machine)) - @staticmethod - def default_stdlib_deployment_targets(): - """ - Return targets for the Swift stdlib, based on the build machine. - If the build machine is not one of the recognized ones, return None. - """ - - host_target = StdlibDeploymentTarget.host_target() - if host_target is None: - return None - - # OS X build machines configure all Darwin platforms by default. - # Put iOS native targets last so that we test them last - # (it takes a long time). - if host_target == StdlibDeploymentTarget.OSX.x86_64: - return [host_target] + \ - StdlibDeploymentTarget.iOSSimulator.targets + \ - StdlibDeploymentTarget.AppleTVSimulator.targets + \ - StdlibDeploymentTarget.AppleWatchSimulator.targets + \ - StdlibDeploymentTarget.iOS.targets + \ - StdlibDeploymentTarget.AppleTV.targets + \ - StdlibDeploymentTarget.AppleWatch.targets - else: - # All other machines only configure their host stdlib by default. - return [host_target] - @classmethod def get_target_for_name(cls, name): return cls._targets_by_name.get(name) @@ -262,6 +244,11 @@ def get_target_for_name(cls, name): def get_targets_by_name(cls, names): return [cls.get_target_for_name(name) for name in names] + @classmethod + def get_target_names(cls): + return sorted([name for (name, target) in + cls._targets_by_name.items()]) + def install_prefix(): """ @@ -283,3 +270,19 @@ def darwin_toolchain_prefix(darwin_install_prefix): directory. """ return os.path.split(darwin_install_prefix)[0] + + +def toolchain_path(install_destdir, install_prefix): + """ + Given the install prefix for a Darwin system, and assuming that that path + is to a .xctoolchain directory, return the path to the .xctoolchain + directory in the given install directory. + This toolchain is being populated during the build-script invocation. + Downstream products can use products that were previously installed into + this toolchain. + """ + built_toolchain_path = install_destdir + if platform.system() == 'Darwin': + # The prefix is an absolute path, so concatenate without os.path. + built_toolchain_path += darwin_toolchain_prefix(install_prefix) + return built_toolchain_path diff --git a/utils/swift_build_support/swift_build_support/toolchain.py b/utils/swift_build_support/swift_build_support/toolchain.py index b5f0a0a1d6abd..b7c8247374e2a 100644 --- a/utils/swift_build_support/swift_build_support/toolchain.py +++ b/utils/swift_build_support/swift_build_support/toolchain.py @@ -18,10 +18,12 @@ import platform -from . import cache_util +from build_swift.build_swift import cache_utils +from build_swift.build_swift.shell import which +from build_swift.build_swift.wrappers import xcrun + from . import shell -from . import xcrun -from .which import which + __all__ = [ 'host_toolchain', @@ -42,7 +44,7 @@ def _register(name, *tool): def _getter(self): return self.find_tool(*tool) _getter.__name__ = name - setattr(Toolchain, name, cache_util.reify(_getter)) + setattr(Toolchain, name, cache_utils.reify(_getter)) if platform.system() == 'Windows': @@ -60,6 +62,7 @@ def _getter(self): _register("llvm_cov", "llvm-cov") _register("lipo", "lipo") _register("libtool", "libtool") +_register("swiftc", "swiftc") class Darwin(Toolchain): @@ -159,7 +162,7 @@ def __init__(self): suffixes = ['38', '37', '36', '35'] super(FreeBSD, self).__init__(suffixes) - @cache_util.reify + @cache_utils.reify def _release_date(self): """Return the release date for FreeBSD operating system on this host. If the release date cannot be ascertained, return None. diff --git a/utils/swift_build_support/swift_build_support/which.py b/utils/swift_build_support/swift_build_support/which.py deleted file mode 100644 index 8b329cc95cc2b..0000000000000 --- a/utils/swift_build_support/swift_build_support/which.py +++ /dev/null @@ -1,42 +0,0 @@ -# swift_build_support/which.py - shutil.which() for Python 2.7 -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -# -# A naive reimplementation of shutil.which() for Python 2.7. This can be -# removed if shutil.which() is backported, or if the Swift build toolchain -# migrates completely to Python 3.3+. -# -# ---------------------------------------------------------------------------- - -from __future__ import absolute_import - -from . import cache_util -from . import shell - - -@cache_util.cached -def which(cmd): - """ - Return the path to an executable which would be run if - the given cmd was called. If no cmd would be called, return None. - - Python 3.3+ provides this behavior via the shutil.which() function; - see: https://docs.python.org/3.3/library/shutil.html#shutil.which - - We provide our own implementation because shutil.which() has not - been backported to Python 2.7, which we support. - """ - out = shell.capture(['which', cmd], - dry_run=False, echo=False, - optional=True, stderr=shell.DEVNULL) - if out is None: - return None - return out.rstrip() diff --git a/utils/swift_build_support/swift_build_support/workspace.py b/utils/swift_build_support/swift_build_support/workspace.py index 94655d2a88a19..e81656d077da9 100644 --- a/utils/swift_build_support/swift_build_support/workspace.py +++ b/utils/swift_build_support/swift_build_support/workspace.py @@ -29,6 +29,15 @@ def build_dir(self, deployment_target, product): return os.path.join(self.build_root, '%s-%s' % (product, deployment_target)) + def swiftpm_unified_build_dir(self, deployment_target): + """ swiftpm_unified_build_dir() -> str + + Build directory that all SwiftPM unified build products share. + """ + return os.path.join(self.build_root, + 'unified-swiftpm-build-%s' % + deployment_target) + def compute_build_subdir(args): # Create a name for the build directory. diff --git a/utils/swift_build_support/swift_build_support/xcrun.py b/utils/swift_build_support/swift_build_support/xcrun.py deleted file mode 100644 index 2a3ba110e9720..0000000000000 --- a/utils/swift_build_support/swift_build_support/xcrun.py +++ /dev/null @@ -1,58 +0,0 @@ -# swift_build_support/xcrun.py - Invoke xcrun from Python -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -# -# Python wrappers for invoking `xcrun` on the command-line. -# -# ---------------------------------------------------------------------------- - -from __future__ import absolute_import - -from . import cache_util -from . import shell - - -@cache_util.cached -def find(tool, sdk=None, toolchain=None): - """ - Return the path for the given tool, according to `xcrun --find`, using - the given sdk and toolchain. - - If `xcrun --find` cannot find the tool, return None. - """ - command = ['xcrun', '--find', tool] - if sdk is not None: - command += ['--sdk', sdk] - if toolchain is not None: - command += ['--toolchain', toolchain] - - # `xcrun --find` prints to stderr when it fails to find the - # given tool. We swallow that output with a pipe. - out = shell.capture( - command, - stderr=shell.DEVNULL, dry_run=False, echo=False, optional=True) - if out is None: - return None - return out.rstrip() - - -@cache_util.cached -def sdk_path(sdk): - """ - Return the path string for given SDK, according to `xcrun --show-sdk-path`. - - If `xcrun --show-sdk-path` cannot find the SDK, return None. - """ - command = ['xcrun', '--sdk', sdk, '--show-sdk-path'] - out = shell.capture(command, dry_run=False, echo=False, optional=True) - if out is None: - return None - return out.rstrip() diff --git a/utils/swift_build_support/test_swift_build_support.sh b/utils/swift_build_support/test_swift_build_support.sh deleted file mode 100755 index b9291c4cb3a17..0000000000000 --- a/utils/swift_build_support/test_swift_build_support.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -e - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -python -m unittest discover -s $DIR - -set +e - diff --git a/utils/swift_build_support/tests/products/test_ninja.py b/utils/swift_build_support/tests/products/test_ninja.py index 2c9845723a897..5a3c919732d11 100644 --- a/utils/swift_build_support/tests/products/test_ninja.py +++ b/utils/swift_build_support/tests/products/test_ninja.py @@ -23,8 +23,9 @@ # py3 from io import StringIO +from build_swift.build_swift.wrappers import xcrun + from swift_build_support import shell -from swift_build_support import xcrun from swift_build_support.products import Ninja from swift_build_support.targets import StdlibDeploymentTarget from swift_build_support.toolchain import host_toolchain diff --git a/utils/swift_build_support/tests/products/test_swift.py b/utils/swift_build_support/tests/products/test_swift.py index d7bf6ba133b80..bd353f73bbab0 100644 --- a/utils/swift_build_support/tests/products/test_swift.py +++ b/utils/swift_build_support/tests/products/test_swift.py @@ -57,7 +57,8 @@ def setUp(self): benchmark_num_o_iterations=3, disable_guaranteed_normal_arguments=True, force_optimized_typechecker=False, - enable_stdlibcore_exclusivity_checking=False) + enable_stdlibcore_exclusivity_checking=False, + enable_experimental_differentiable_programming=False) # Setup shell shell.dry_run = True @@ -87,7 +88,8 @@ def test_by_default_no_cmake_options(self): expected = [ '-DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE', '-DSWIFT_FORCE_OPTIMIZED_TYPECHECKER:BOOL=FALSE', - '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE' + '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL=FALSE' ] self.assertEqual(set(swift.cmake_options), set(expected)) @@ -102,7 +104,8 @@ def test_swift_runtime_tsan(self): '-DSWIFT_RUNTIME_USE_SANITIZERS=Thread', '-DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE', '-DSWIFT_FORCE_OPTIMIZED_TYPECHECKER:BOOL=FALSE', - '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE' + '-DSWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL=FALSE' ] self.assertEqual(set(swift.cmake_options), set(flags_set)) @@ -302,3 +305,16 @@ def test_exclusivity_checking_flags(self): 'TRUE'], [x for x in swift.cmake_options if 'SWIFT_STDLIB_ENABLE_STDLIBCORE_EXCLUSIVITY_CHECKING' in x]) + + def test_experimental_differentiable_programming_flags(self): + self.args.enable_experimental_differentiable_programming = True + swift = Swift( + args=self.args, + toolchain=self.toolchain, + source_dir='/path/to/src', + build_dir='/path/to/build') + self.assertEqual( + ['-DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING:BOOL=' + 'TRUE'], + [x for x in swift.cmake_options + if 'DSWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING' in x]) diff --git a/utils/swift_build_support/tests/test_arguments.py b/utils/swift_build_support/tests/test_arguments.py deleted file mode 100644 index 2eaa922b75323..0000000000000 --- a/utils/swift_build_support/tests/test_arguments.py +++ /dev/null @@ -1,236 +0,0 @@ -# tests/arguments.py --------------------------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- - -import argparse -import os -import platform -import sys -import unittest -try: - # py2 - from StringIO import StringIO -except ImportError: - # py3 - from io import StringIO - -from swift_build_support.arguments import ( - action as argaction, - type as argtype, -) - - -class ArgumentsTypeTestCase(unittest.TestCase): - - def test_bool(self): - self.assertTrue(argtype.bool("1")) - self.assertTrue(argtype.bool("true")) - self.assertTrue(argtype.bool("True")) - - self.assertFalse(argtype.bool("0")) - self.assertFalse(argtype.bool("false")) - self.assertFalse(argtype.bool("False")) - - self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'foobar') - self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'TRUE') - self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'FALSE') - - def test_shell_split(self): - self.assertEqual( - argtype.shell_split("-BAR=\"foo bar\" -BAZ='foo,bar',-QUX $baz"), - ['-BAR=foo bar', '-BAZ=foo,bar', '-QUX', '$baz']) - - def test_clang_compiler_version(self): - self.assertEqual( - argtype.clang_compiler_version('1.23.456').components, - ("1", "23", "456", None)) - self.assertEqual( - argtype.clang_compiler_version('1.2.3').components, - ("1", "2", "3", None)) - self.assertEqual( - argtype.clang_compiler_version('1.2.3.4').components, - ("1", "2", "3", "4")) - self.assertEqual( - argtype.clang_compiler_version('12.34.56').components, - ("12", "34", "56", None)) - self.assertEqual( - argtype.clang_compiler_version('12.34.56.78').components, - ("12", "34", "56", "78")) - self.assertRaises( - argparse.ArgumentTypeError, - argtype.clang_compiler_version, - "ver1.2.3") - self.assertRaises( - argparse.ArgumentTypeError, - argtype.clang_compiler_version, - "1.beta2.3") - self.assertRaises( - argparse.ArgumentTypeError, - argtype.clang_compiler_version, - "1.2.preview3") - self.assertRaises( - argparse.ArgumentTypeError, - argtype.clang_compiler_version, - "1.2.3-rc4") - self.assertRaises( - argparse.ArgumentTypeError, - argtype.clang_compiler_version, - "1..2") - - def test_executable(self): - if platform.system() == 'Windows': - self.skipTest('Every file is considered executable in Windows') - - python = sys.executable - self.assertTrue(os.path.isabs(argtype.executable(python))) - - # On this test directory, specifying "../../build-script-impl" returns - # absolute path of build-script-impl - impl = os.path.join("..", "..", "build-script-impl") - cwd = os.getcwd() - os.chdir(os.path.dirname(__file__)) - self.assertTrue(os.path.isabs(argtype.executable(impl))) - os.chdir(cwd) - - self.assertRaises( - argparse.ArgumentTypeError, - argtype.executable, __file__) # this file is not executable - self.assertRaises( - argparse.ArgumentTypeError, - argtype.executable, os.path.dirname(__file__)) - self.assertRaises( - argparse.ArgumentTypeError, - argtype.executable, "/bin/example-command-not-exist") - self.assertRaises( - argparse.ArgumentTypeError, - argtype.executable, "../example-command-not-exist") - - -class ArgumentsActionTestCase(unittest.TestCase): - - def test_unavailable(self): - orig_stderr = sys.stderr - - parser = argparse.ArgumentParser() - parser.add_argument("--foo") - parser.add_argument( - "--do-not-use", - "--never-ever", - action=argaction.unavailable) - - args, unknown_args = parser.parse_known_args( - ['--foo', 'bar', '--baz', 'qux']) - - self.assertEqual(args.foo, 'bar') - self.assertEqual(unknown_args, ['--baz', 'qux']) - self.assertFalse(hasattr(args, 'sentinel')) - - stderr = StringIO() - sys.stderr = stderr - self.assertRaises( - SystemExit, - parser.parse_known_args, - ['--foo', 'bar', '--do-not-use', 'baz']) - self.assertIn('--do-not-use', stderr.getvalue()) - - stderr = StringIO() - sys.stderr = stderr - self.assertRaises( - SystemExit, - parser.parse_known_args, - ['--foo', 'bar', '--never-ever=baz']) - self.assertIn('--never-ever', stderr.getvalue()) - - sys.stderr = orig_stderr - - def test_concat(self): - # Has default - parser = argparse.ArgumentParser() - parser.add_argument( - "--str-opt", - action=argaction.concat, - default="def") - parser.add_argument( - "--list-opt", - action=argaction.concat, - type=argtype.shell_split, - default=["def"]) - - self.assertEqual( - parser.parse_args(['--str-opt', '12', '--str-opt=42']), - argparse.Namespace(str_opt="def1242", list_opt=["def"])) - - self.assertEqual( - parser.parse_args(['--list-opt', 'foo 12', '--list-opt=bar 42']), - argparse.Namespace( - str_opt="def", list_opt=["def", "foo", "12", "bar", "42"])) - - # Default less - parser = argparse.ArgumentParser() - parser.add_argument( - "--str-opt", - action=argaction.concat) - parser.add_argument( - "--list-opt", - action=argaction.concat, - type=argtype.shell_split) - - self.assertEqual( - parser.parse_args(['--str-opt', '12', '--str-opt=42']), - argparse.Namespace(str_opt="1242", list_opt=None)) - - self.assertEqual( - parser.parse_args(['--list-opt', 'foo 12', '--list-opt=bar 42']), - argparse.Namespace( - str_opt=None, list_opt=["foo", "12", "bar", "42"])) - - def test_optional_bool(self): - parser = argparse.ArgumentParser() - parser.add_argument( - "--test-default-default", - action=argaction.optional_bool) - parser.add_argument( - "--test-default-true", - action=argaction.optional_bool, - default=True) - parser.add_argument( - "--test-default-false", - action=argaction.optional_bool, - default=False) - - args, unknown_args = parser.parse_known_args([]) - self.assertEqual(args.test_default_default, False) - self.assertEqual(args.test_default_true, True) - self.assertEqual(args.test_default_false, False) - - args, unknown_args = parser.parse_known_args( - ['--test-default-default', '0', - '--test-default-true', '0', - '--test-default-false', '0']) - self.assertEqual(args.test_default_default, False) - self.assertEqual(args.test_default_true, False) - self.assertEqual(args.test_default_false, False) - - args, unknown_args = parser.parse_known_args( - ['--test-default-default', '1', - '--test-default-true', '1', - '--test-default-false', '1']) - self.assertEqual(args.test_default_default, True) - self.assertEqual(args.test_default_true, True) - self.assertEqual(args.test_default_false, True) - - args, unknown_args = parser.parse_known_args( - ['--test-default-default', - '--test-default-true', - '--test-default-false']) - self.assertEqual(args.test_default_default, True) - self.assertEqual(args.test_default_true, True) - self.assertEqual(args.test_default_false, True) diff --git a/utils/swift_build_support/tests/test_cache_util.py b/utils/swift_build_support/tests/test_cache_util.py deleted file mode 100644 index c65f308b48295..0000000000000 --- a/utils/swift_build_support/tests/test_cache_util.py +++ /dev/null @@ -1,99 +0,0 @@ -# tests/test_cache_util.py --------------------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- - -import unittest - -from swift_build_support import cache_util - - -my_func_called = 0 -my_kfunc_called = 0 - - -@cache_util.cached -def my_func(arg1, arg2): - global my_func_called - my_func_called += 1 - return "my_func_result(%s, %s)" % (arg1, arg2) - - -@cache_util.cached -def my_kfunc(arg1, arg2): - global my_kfunc_called - my_kfunc_called += 1 - return "my_kfunc_result(%s, %s)" % (arg1, arg2) - - -class MyClass(object): - def __init__(self, prop=None): - self.my_method_called = 0 - self.my_prop_called = 0 - self.prop_value = prop - - @cache_util.cached - def my_method(self, arg1, arg2): - self.my_method_called += 1 - return "my_meth_result(%s, %s)" % (arg1, arg2) - - @cache_util.reify - def my_prop(self): - self.my_prop_called += 1 - return "==%s==" % (self.prop_value) - - -class CacheUtilTestCase(unittest.TestCase): - def test_cached_func(self): - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("bar", 42), "my_func_result(bar, 42)") - self.assertEqual(my_func_called, 2) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 2) - - def test_cached_kwfunc(self): - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("bar", arg2=42), "my_kfunc_result(bar, 42)") - self.assertEqual(my_kfunc_called, 2) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 2) - - def test_cached_method(self): - obj1 = MyClass() - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("bar", 12), "my_meth_result(bar, 12)") - self.assertEqual(obj1.my_method_called, 2) - - # Test for instance independency. - obj2 = MyClass() - self.assertEqual(obj2.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj2.my_method_called, 1) - self.assertEqual(obj1.my_method_called, 2) - - def test_reify(self): - obj1 = MyClass(prop='foo') - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - - # Test for instance independency. - obj2 = MyClass(prop='bar') - self.assertEqual(obj2.my_prop, '==bar==') - self.assertEqual(obj1.my_prop, '==foo==') diff --git a/utils/swift_build_support/tests/test_cmake.py b/utils/swift_build_support/tests/test_cmake.py index 9ee12363b3ecc..81db80c6ad605 100644 --- a/utils/swift_build_support/tests/test_cmake.py +++ b/utils/swift_build_support/tests/test_cmake.py @@ -8,12 +8,16 @@ # See https://swift.org/LICENSE.txt for license information # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + +from __future__ import absolute_import, unicode_literals + import os import platform import unittest from argparse import Namespace -from swift_build_support.arguments import CompilerVersion +from build_swift.build_swift.versions import Version + from swift_build_support.cmake import CMake, CMakeOptions from swift_build_support.toolchain import host_toolchain @@ -250,9 +254,7 @@ def test_common_options_xcode(self): def test_common_options_clang_compiler_version(self): args = self.default_args() - args.clang_compiler_version = CompilerVersion( - string_representation="999.0.999", - components=("999", "0", "999", None)) + args.clang_compiler_version = Version("999.0.999") cmake = self.cmake(args) self.assertEqual( list(cmake.common_options()), @@ -264,9 +266,7 @@ def test_common_options_clang_compiler_version(self): def test_common_options_clang_user_visible_version(self): args = self.default_args() - args.clang_user_visible_version = CompilerVersion( - string_representation="9.0.0", - components=("9", "0", "0", None)) + args.clang_user_visible_version = Version("9.0.0") cmake = self.cmake(args) self.assertEqual( list(cmake.common_options()), @@ -301,12 +301,8 @@ def test_common_options_full(self): args.export_compile_commands = True args.distcc = True args.cmake_generator = 'Xcode' - args.clang_user_visible_version = CompilerVersion( - string_representation="9.0.0", - components=("9", "0", "0", None)) - args.clang_compiler_version = CompilerVersion( - string_representation="999.0.900", - components=("999", "0", "900", None)) + args.clang_user_visible_version = Version("9.0.0") + args.clang_compiler_version = Version("999.0.900") args.build_ninja = True cmake = self.cmake(args) self.assertEqual( diff --git a/utils/swift_build_support/tests/test_debug.py b/utils/swift_build_support/tests/test_debug.py deleted file mode 100644 index 954db17146730..0000000000000 --- a/utils/swift_build_support/tests/test_debug.py +++ /dev/null @@ -1,41 +0,0 @@ -# test_debug.py - Unit tests for swift_build_support.debug -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import platform -import unittest -try: - # py2 - from StringIO import StringIO -except ImportError: - # py3 - from io import StringIO - -from swift_build_support import debug - - -class PrintXcodebuildVersionsTestCase(unittest.TestCase): - def setUp(self): - if platform.system() != 'Darwin': - self.skipTest('print_xcodebuild_version() tests should only be ' - 'run on OS X') - self._out = StringIO() - - def test_outputs_xcode_version(self): - debug.print_xcodebuild_versions(file=self._out) - actual = self._out.getvalue().splitlines() - self.assertTrue(actual[0].startswith('Xcode ')) - self.assertTrue(actual[1].startswith('Build version ')) - self.assertEqual(actual[3], '--- SDK versions ---') - # You can't test beyond this because each developer's machines may have - # a different set of SDKs installed. - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/swift_build_support/tests/test_host_specific_configuration.py b/utils/swift_build_support/tests/test_host_specific_configuration.py index 136b8cc8a4fdc..5b8a6dd5f9e8c 100644 --- a/utils/swift_build_support/tests/test_host_specific_configuration.py +++ b/utils/swift_build_support/tests/test_host_specific_configuration.py @@ -616,6 +616,8 @@ def default_args(self): build_tvos_simulator=False, build_watchos_device=False, build_watchos_simulator=False, + maccatalyst=False, + maccatalyst_ios_tests=False, long_test=False, only_executable_test=False, stress_test=False, diff --git a/utils/swift_build_support/tests/test_migration.py b/utils/swift_build_support/tests/test_migration.py deleted file mode 100644 index 87918e7c2eba4..0000000000000 --- a/utils/swift_build_support/tests/test_migration.py +++ /dev/null @@ -1,82 +0,0 @@ -# test_migration.py - Tests for swift_build_support.migration -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import argparse -import os -import platform -import unittest - -from swift_build_support import migration - - -class MigrateImplArgsTestCase(unittest.TestCase): - def test_report_unknown_args(self): - parser = argparse.ArgumentParser() - parser.add_argument('-R', '--release', action='store_true') - parser.add_argument('-T', '--validation-test', action='store_true') - parser.add_argument('--darwin-xcrun-toolchain') - - args = migration.parse_args( - parser, - ['-RT', '--unknown', 'true', '--darwin-xcrun-toolchain=foo', '--', - '--darwin-xcrun-toolchain=bar', '--other']) - - self.assertEqual( - args, - argparse.Namespace( - release=True, - validation_test=True, - darwin_xcrun_toolchain='bar', - build_script_impl_args=['--unknown', 'true', '--other'])) - - def test_no_unknown_args(self): - parser = argparse.ArgumentParser() - parser.add_argument('-R', '--release', action='store_true') - parser.add_argument('-T', '--validation-test', action='store_true') - parser.add_argument('--darwin-xcrun-toolchain') - - args = migration.parse_args( - parser, - ['-RT', '--darwin-xcrun-toolchain=bar']) - - self.assertEqual( - args, - argparse.Namespace( - release=True, - validation_test=True, - darwin_xcrun_toolchain='bar', - build_script_impl_args=[])) - - def test_check_impl_args(self): - if platform.system() == 'Windows': - self.skipTest("build-script-impl cannot run in Windows") - return - - # Assuming file locations: - # utils/swift_build_support/tests/test_migration.py - # utils/build-script-impl - build_script_impl = os.path.join( - os.path.dirname(__file__), '..', '..', 'build-script-impl') - - self.assertIsNone(migration.check_impl_args(build_script_impl, - ['--reconfigure'])) - - with self.assertRaises(ValueError) as cm: - migration.check_impl_args(build_script_impl, ['foo']) - self.assertIn('foo', str(cm.exception)) - - with self.assertRaises(ValueError) as cm: - migration.check_impl_args(build_script_impl, ['--reconfigure', - '--foo=true']) - self.assertIn('foo', str(cm.exception)) - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/swift_build_support/tests/test_tar.py b/utils/swift_build_support/tests/test_tar.py deleted file mode 100644 index 290a4179e4c78..0000000000000 --- a/utils/swift_build_support/tests/test_tar.py +++ /dev/null @@ -1,72 +0,0 @@ -# test_tar.py - Unit tests for swift_build_support.tar -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import os -import platform -import sys -import tempfile -import unittest -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -from swift_build_support.tar import tar - - -class TarTestCase(unittest.TestCase): - def setUp(self): - self._orig_stdout = sys.stdout - self._orig_stderr = sys.stderr - self.stdout = StringIO() - self.stderr = StringIO() - sys.stdout = self.stdout - sys.stderr = self.stderr - - def tearDown(self): - sys.stdout = self._orig_stdout - sys.stderr = self._orig_stderr - - def test_tar_this_file_succeeds(self): - # `tar` complains about absolute paths, so use a relative path here. - if platform.system() != 'Windows': - source = os.path.relpath(__file__) - else: - # Windows can use absolute paths, specially because the relative - # path might not exist because the file and the current directory - # might be in different drives. - source = __file__ - _, destination = tempfile.mkstemp() - tar(source=source, destination=destination) - - if platform.system() == "Darwin" or platform.system() == 'Windows': - expect = "+ tar -c -z -f {dest} {source}\n" - else: - expect = "+ tar -c -z -f {dest} --owner=0 --group=0 {source}\n" - - self.assertEqual(self.stdout.getvalue(), "") - self.assertEqual(self.stderr.getvalue(), - expect.format(dest=self._platform_quote(destination), - source=self._platform_quote(source))) - - def test_tar_nonexistent_file_raises(self): - with self.assertRaises(SystemExit): - tar(source='/path/to/something/that/surely/doesnt/exist', - destination='/another/path/that/shouldnt/exist') - - def _platform_quote(self, path): - if platform.system() == 'Windows': - return "'{}'".format(path) - else: - return path - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/swift_build_support/tests/test_which.py b/utils/swift_build_support/tests/test_which.py deleted file mode 100644 index be20f26a425de..0000000000000 --- a/utils/swift_build_support/tests/test_which.py +++ /dev/null @@ -1,26 +0,0 @@ -# test_which.py - Unit tests for swift_build_support.which -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import os -import unittest - -from swift_build_support.which import which - - -class WhichTestCase(unittest.TestCase): - def test_when_cmd_not_found_returns_none(self): - self.assertIsNone(which('a-tool-that-doesnt-exist')) - - def test_when_cmd_found_returns_path(self): - self.assertEqual(os.path.split(which('ls'))[-1], 'ls') - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/swift_build_support/tests/test_xcrun.py b/utils/swift_build_support/tests/test_xcrun.py deleted file mode 100644 index 424c7ef71f468..0000000000000 --- a/utils/swift_build_support/tests/test_xcrun.py +++ /dev/null @@ -1,40 +0,0 @@ -# test_xcrun.py - Unit tests for swift_build_support.xcrun -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import platform -import unittest - -from swift_build_support import xcrun - - -@unittest.skipUnless(platform.system() == 'Darwin', - 'xcrun is available in Darwin platform only') -class XCRunTestCase(unittest.TestCase): - def test_find(self): - # Unknown tool - self.assertIsNone(xcrun.find('a-tool-that-isnt-on-osx', - sdk='macosx', - toolchain='default')) - - # Available tool - self.assertTrue(xcrun.find('clang', - sdk='macosx', - toolchain='default').endswith('/clang')) - - def test_sdk_path(self): - # Unknown SDK - self.assertIsNone(xcrun.sdk_path('not-a-sdk')) - - # Available SDK - self.assertIsNotNone(xcrun.sdk_path('macosx')) - - -if __name__ == '__main__': - unittest.main() diff --git a/utils/update_checkout/run_tests.py b/utils/update_checkout/run_tests.py new file mode 100644 index 0000000000000..47910a9a79872 --- /dev/null +++ b/utils/update_checkout/run_tests.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Small script used to easily run the update_checkout module unit tests. +""" + + +from __future__ import absolute_import, unicode_literals + +import os +import shutil +import sys +import tempfile +import unittest + + +MODULE_DIR = os.path.abspath(os.path.dirname(__file__)) +UTILS_DIR = os.path.abspath(os.path.join(MODULE_DIR, os.pardir)) + + +if __name__ == '__main__': + # Add the swift/utils directory to the Python path. + sys.path.append(UTILS_DIR) + + # Create temp directory and export it for the test suite. + temp_dir = tempfile.mkdtemp() + os.environ['UPDATECHECKOUT_TEST_WORKSPACE_DIR'] = temp_dir + + # Discover all tests for the module. + module_tests = unittest.defaultTestLoader.discover(MODULE_DIR) + + # Create and run test suite. + suite = unittest.TestSuite() + suite.addTests(module_tests) + + runner = unittest.TextTestRunner() + try: + result = runner.run(suite) + finally: + # Ensure the temp directory is removed + shutil.rmtree(temp_dir, ignore_errors=True) + + sys.exit(not result.wasSuccessful()) diff --git a/utils/update_checkout/test_update_checkout.sh b/utils/update_checkout/test_update_checkout.sh deleted file mode 100755 index 7e27722aabafb..0000000000000 --- a/utils/update_checkout/test_update_checkout.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -set -e - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# If the user did not specify a workspace dir, create one in /tmp/workspace and -# export it for the tests. -if [[ -n "${1}" ]]; then - export UPDATECHECKOUT_TEST_WORKSPACE_DIR="${1}" - echo "Using ${UPDATECHECKOUT_TEST_WORKSPACE_DIR}" -else - export UPDATECHECKOUT_TEST_WORKSPACE_DIR=/tmp/workspace - echo "No preset workspace dir! Using ${UPDATECHECKOUT_TEST_WORKSPACE_DIR}" -fi - -python -m unittest discover -s $DIR - -set +e - diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index adcd91a17f033..1a726a77c2f89 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -2,26 +2,20 @@ "ssh-clone-pattern": "git@github.com:%s.git", "https-clone-pattern": "https://github.com/%s.git", "repos" : { - "llvm": { - "remote": { "id": "apple/swift-llvm" } }, - "clang": { - "remote": { "id": "apple/swift-clang" } }, - "swift": { + "swift": { "remote": { "id": "apple/swift" } }, - "lldb": { - "remote": { "id": "apple/swift-lldb" } }, - "cmark": { + "cmark": { "remote": { "id": "apple/swift-cmark" } }, "llbuild": { "remote": { "id": "apple/swift-llbuild" } }, + "swift-tools-support-core": { + "remote": { "id": "apple/swift-tools-support-core" } }, "swiftpm": { "remote": { "id": "apple/swift-package-manager" } }, "swift-syntax": { "remote": { "id": "apple/swift-syntax" } }, "swift-stress-tester": { "remote": { "id": "apple/swift-stress-tester" } }, - "compiler-rt": { - "remote": { "id": "apple/swift-compiler-rt" } }, "swift-corelibs-xctest": { "remote": { "id": "apple/swift-corelibs-xctest" } }, "swift-corelibs-foundation": { @@ -42,56 +36,28 @@ "remote": { "id": "KitWare/CMake" }, "platforms": [ "Linux" ] }, - "libcxx": { - "remote": { "id": "apple/swift-libcxx" } }, - "clang-tools-extra": { - "remote": { "id": "apple/swift-clang-tools-extra" } }, - "indexstore-db": { + "pythonkit": { + "remote": { "id": "pvieito/PythonKit" } + }, + "indexstore-db": { "remote": { "id": "apple/indexstore-db" } }, "sourcekit-lsp": { "remote": { "id": "apple/sourcekit-lsp" } }, "swift-format": { "remote": { "id": "apple/swift-format" } }, "llvm-project": { - "remote": { "id": "apple/llvm-project-v3" } } + "remote": { "id": "apple/llvm-project" } } }, "default-branch-scheme": "master", "branch-schemes": { - "master": { - "aliases": ["master", "stable"], - "repos": { - "llvm": "stable", - "clang": "stable", - "swift": "master", - "lldb": "stable", - "cmark": "master", - "llbuild": "master", - "swiftpm": "master", - "swift-syntax": "master", - "swift-stress-tester": "master", - "compiler-rt": "stable", - "swift-corelibs-xctest": "master", - "swift-corelibs-foundation": "master", - "swift-corelibs-libdispatch": "master", - "swift-integration-tests": "master", - "swift-xcode-playground-support": "master", - "ninja": "release", - "icu": "release-61-1", - "cmake": "v3.15.1", - "clang-tools-extra": "stable", - "libcxx": "stable", - "indexstore-db": "master", - "sourcekit-lsp": "master", - "swift-format": "master" - } - }, - "master-llvm-monorepo": { - "aliases": ["master-llvm-monorepo", "swift/master"], + "master": { + "aliases": ["master", "swift/master"], "repos": { "llvm-project": "swift/master", "swift": "master", "cmark": "master", "llbuild": "master", + "swift-tools-support-core": "master", "swiftpm": "master", "swift-syntax": "master", "swift-stress-tester": "master", @@ -101,48 +67,24 @@ "swift-integration-tests": "master", "swift-xcode-playground-support": "master", "ninja": "release", - "icu": "release-61-1", + "icu": "release-65-1", "cmake": "v3.15.1", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master", + "pythonkit": "master" } }, "next" : { "aliases": ["next", "master-next", "stable-next", "upstream", "next-upstream", "upstream-with-swift"], - "repos": { - "llvm": "upstream-with-swift", - "clang": "upstream-with-swift", - "compiler-rt": "upstream-with-swift", - "swift": "master-next", - "lldb": "upstream-with-swift", - "cmark": "master", - "llbuild": "master", - "swiftpm": "master", - "swift-syntax": "master", - "swift-stress-tester": "master", - "swift-corelibs-xctest": "master", - "swift-corelibs-foundation": "master", - "swift-corelibs-libdispatch": "master", - "swift-integration-tests": "master", - "swift-xcode-playground-support": "master", - "ninja": "release", - "icu": "release-61-1", - "cmake": "v3.15.1", - "clang-tools-extra": "upstream-with-swift", - "libcxx": "upstream-with-swift", - "indexstore-db": "master", - "sourcekit-lsp": "master" - } - }, - "master-next-llvm-monorepo" : { - "aliases": ["master-next-llvm-monorepo"], "repos": { "llvm-project": "swift/master-next", "swift": "master-next", "cmark": "master", "llbuild": "master", + "swift-tools-support-core": "master", "swiftpm": "master", "swift-syntax": "master", "swift-stress-tester": "master", @@ -152,25 +94,22 @@ "swift-integration-tests": "master", "swift-xcode-playground-support": "master", "ninja": "release", - "icu": "release-61-1", + "icu": "release-65-1", "cmake": "v3.15.1", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, - "swift-3.0-branch" : { + "swift-3.0-branch" : { "aliases": ["swift-3.0-branch"], "repos": { - "llvm": "swift-3.0-branch", - "clang": "swift-3.0-branch", - "swift": "swift-3.0-branch", - "lldb": "swift-3.0-branch", + "llvm-project": "swift/swift-3.0-branch", "cmark": "swift-3.0-branch", "llbuild": "swift-3.0-branch", "swiftpm": "swift-3.0-branch", "swift-syntax": "master", "swift-stress-tester": "master", - "compiler-rt": "swift-3.0-branch", "swift-corelibs-xctest": "swift-3.0-branch", "swift-corelibs-foundation": "swift-3.0-branch", "swift-corelibs-libdispatch": "swift-3.0-branch", @@ -178,22 +117,20 @@ "swift-xcode-playground-support": "swift-3.0-branch", "ninja": "release", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, "swift-3.1-branch" : { "aliases": ["swift-3.1-branch"], "repos": { - "llvm": "swift-3.1-branch", - "clang": "swift-3.1-branch", + "llvm-project": "swift/swift-3.1-branch", "swift": "swift-3.1-branch", - "lldb": "swift-3.1-branch", "cmark": "swift-3.1-branch", "llbuild": "swift-3.1-branch", "swiftpm": "swift-3.1-branch", "swift-syntax": "master", "swift-stress-tester": "master", - "compiler-rt": "swift-3.1-branch", "swift-corelibs-xctest": "swift-3.1-branch", "swift-corelibs-foundation": "swift-3.1-branch", "swift-corelibs-libdispatch": "swift-3.1-branch", @@ -201,22 +138,20 @@ "swift-xcode-playground-support": "swift-3.1-branch", "ninja": "release", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, "swift-4.0-branch" : { "aliases": ["swift-4.0-branch"], "repos": { - "llvm": "swift-4.0-branch", - "clang": "swift-4.0-branch", + "llvm-project": "swift/swift-4.0-branch", "swift": "swift-4.0-branch", - "lldb": "swift-4.0-branch", "cmark": "swift-4.0-branch", "llbuild": "swift-4.0-branch", "swiftpm": "swift-4.0-branch", "swift-syntax": "master", "swift-stress-tester": "master", - "compiler-rt": "swift-4.0-branch", "swift-corelibs-xctest": "swift-4.0-branch", "swift-corelibs-foundation": "swift-4.0-branch", "swift-corelibs-libdispatch": "swift-4.0-branch", @@ -224,22 +159,20 @@ "swift-xcode-playground-support": "swift-4.0-branch", "ninja": "release", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, "swift-4.1-branch" : { "aliases": ["swift-4.1-branch"], "repos": { - "llvm": "swift-4.1-branch", - "clang": "swift-4.1-branch", + "llvm-project": "swift/swift-4.1-branch", "swift": "swift-4.1-branch", - "lldb": "swift-4.1-branch", "cmark": "swift-4.1-branch", "llbuild": "swift-4.1-branch", "swiftpm": "swift-4.1-branch", "swift-syntax": "master", "swift-stress-tester": "master", - "compiler-rt": "swift-4.1-branch", "swift-corelibs-xctest": "swift-4.1-branch", "swift-corelibs-foundation": "swift-4.1-branch", "swift-corelibs-libdispatch": "swift-4.1-branch", @@ -247,22 +180,20 @@ "swift-xcode-playground-support": "swift-4.1-branch", "ninja": "release", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, "swift-4.2-branch" : { "aliases": ["swift-4.2-branch"], "repos": { - "llvm": "swift-4.2-branch", - "clang": "swift-4.2-branch", + "llvm-project": "swift/swift-4.2-branch", "swift": "swift-4.2-branch", - "lldb": "swift-4.2-branch", "cmark": "swift-4.2-branch", "llbuild": "swift-4.2-branch", "swiftpm": "swift-4.2-branch", "swift-syntax": "swift-4.2-branch", "swift-stress-tester": "master", - "compiler-rt": "swift-4.2-branch", "swift-corelibs-xctest": "swift-4.2-branch", "swift-corelibs-foundation": "swift-4.2-branch", "swift-corelibs-libdispatch": "swift-4.2-branch", @@ -270,17 +201,15 @@ "swift-xcode-playground-support": "swift-4.2-branch", "ninja": "release", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, "swift-5.0-branch" : { "aliases": ["swift-5.0-branch"], "repos": { - "llvm": "swift-5.0-branch", - "clang": "swift-5.0-branch", - "compiler-rt": "swift-5.0-branch", + "llvm-project": "swift/swift-5.0-branch", "swift": "swift-5.0-branch", - "lldb": "swift-5.0-branch", "cmark": "swift-5.0-branch", "llbuild": "swift-5.0-branch", "swiftpm": "swift-5.0-branch", @@ -293,47 +222,17 @@ "swift-xcode-playground-support": "swift-5.0-branch", "ninja": "release", "icu": "release-61-1", - "clang-tools-extra": "swift-5.0-branch", - "libcxx": "swift-5.0-branch", - "indexstore-db": "master", - "sourcekit-lsp": "master" - } - }, - "swift-5.1-old-llvm-branch" : { - "aliases": ["swift-5.1-old-llvm-branch"], - "repos": { - "llvm": "swift-5.0-branch", - "clang": "swift-5.0-branch", - "compiler-rt": "swift-5.0-branch", - "swift": "swift-5.1-old-llvm-branch", - "lldb": "stable", - "cmark": "master", - "llbuild": "master", - "swiftpm": "master", - "swift-syntax": "master", - "swift-stress-tester": "master", - "swift-corelibs-xctest": "master", - "swift-corelibs-foundation": "master", - "swift-corelibs-libdispatch": "master", - "swift-integration-tests": "master", - "swift-xcode-playground-support": "master", - "ninja": "release", - "icu": "release-61-1", - "clang-tools-extra": "swift-5.0-branch", - "libcxx": "swift-5.0-branch", "indexstore-db": "master", - "sourcekit-lsp": "master" + "sourcekit-lsp": "master", + "swift-format": "master" } }, - "swift-5.1-branch" : { - "aliases": ["swift-5.1-branch"], + "swift-5.1-branch" : { + "aliases": ["swift-5.1-branch", "swift/swift-5.1-branch"], "repos": { - "llvm": "swift-5.1-branch", - "clang": "swift-5.1-branch", - "compiler-rt": "swift-5.1-branch", + "llvm-project": "swift/swift-5.1-branch", "swift": "swift-5.1-branch", - "lldb": "swift-5.1-branch", - "cmark": "master", + "cmark": "swift-5.1-branch", "llbuild": "swift-5.1-branch", "swiftpm": "swift-5.1-branch", "swift-syntax": "swift-5.1-branch", @@ -345,77 +244,43 @@ "swift-xcode-playground-support": "swift-5.1-branch", "ninja": "release", "icu": "release-61-1", - "clang-tools-extra": "swift-5.1-branch", - "libcxx": "swift-5.1-branch", - "indexstore-db": "swift-5.1-branch", - "sourcekit-lsp": "swift-5.1-branch" - } - }, - "swift-5.1-branch-04-24-2019" : { - "aliases": ["swift-5.1-branch-04-24-2019"], - "repos": { - "llvm": "swift-5.1-branch", - "clang": "swift-5.1-branch", - "compiler-rt": "swift-5.1-branch", - "swift": "swift-5.1-branch-04-24-2019", - "lldb": "swift-5.1-branch", - "cmark": "master", - "llbuild": "swift-5.1-branch", - "swiftpm": "swift-5.1-branch", - "swift-syntax": "swift-5.1-branch-04-24-2019", - "swift-stress-tester": "swift-5.1-branch", - "swift-corelibs-xctest": "swift-5.1-branch", - "swift-corelibs-foundation": "swift-5.1-branch", - "swift-corelibs-libdispatch": "swift-5.1-branch", - "swift-integration-tests": "swift-5.1-branch", - "swift-xcode-playground-support": "swift-5.1-branch", - "ninja": "release", - "icu": "release-61-1", - "clang-tools-extra": "swift-5.1-branch", - "libcxx": "swift-5.1-branch", "indexstore-db": "swift-5.1-branch", - "sourcekit-lsp": "swift-5.1-branch" + "sourcekit-lsp": "swift-5.1-branch", + "swift-format": "master" } }, - "swift-5.1-branch-06-12-2019" : { - "aliases": ["swift-5.1-branch-06-12-2019"], + "swift-5.2-branch": { + "aliases": ["swift-5.2-branch", "swift/swift-5.2-branch"], "repos": { - "llvm": "swift-5.1-branch", - "clang": "swift-5.1-branch", - "compiler-rt": "swift-5.1-branch", - "swift": "swift-5.1-branch-06-12-2019", - "lldb": "swift-5.1-branch", - "cmark": "master", - "llbuild": "swift-5.1-branch", - "swiftpm": "swift-5.1-branch", - "swift-syntax": "swift-5.1-branch", - "swift-stress-tester": "swift-5.1-branch", - "swift-corelibs-xctest": "swift-5.1-branch", - "swift-corelibs-foundation": "swift-5.1-branch", - "swift-corelibs-libdispatch": "swift-5.1-branch", - "swift-integration-tests": "swift-5.1-branch", - "swift-xcode-playground-support": "swift-5.1-branch", + "llvm-project": "swift/swift-5.2-branch", + "swift": "swift-5.2-branch", + "cmark": "swift-5.2-branch", + "llbuild": "swift-5.2-branch", + "swift-tools-support-core": "swift-5.2-branch", + "swiftpm": "swift-5.2-branch", + "swift-syntax": "swift-5.2-branch", + "swift-stress-tester": "swift-5.2-branch", + "swift-corelibs-xctest": "swift-5.2-branch", + "swift-corelibs-foundation": "swift-5.2-branch", + "swift-corelibs-libdispatch": "swift-5.2-branch", + "swift-integration-tests": "swift-5.2-branch", + "swift-xcode-playground-support": "swift-5.2-branch", "ninja": "release", - "icu": "release-61-1", - "clang-tools-extra": "swift-5.1-branch", - "libcxx": "swift-5.1-branch", - "indexstore-db": "swift-5.1-branch", - "sourcekit-lsp": "swift-5.1-branch" + "icu": "release-65-1", + "cmake": "v3.15.1", + "indexstore-db": "swift-5.2-branch", + "sourcekit-lsp": "swift-5.2-branch", + "swift-format": "master" } }, - "master-20190619" : { - "aliases": ["apple/stable/20190619", - "master-apple-stable-20190619", - "master-20190619" - ], + "master-rebranch": { + "aliases": ["master-rebranch", "swift/master-rebranch"], "repos": { - "llvm": "apple/stable/20190619", - "clang": "apple/stable/20190619", - "compiler-rt": "apple/stable/20190619", - "swift": "master", - "lldb": "stable", + "llvm-project": "swift/master-rebranch", + "swift": "master-rebranch", "cmark": "master", "llbuild": "master", + "swift-tools-support-core": "master", "swiftpm": "master", "swift-syntax": "master", "swift-stress-tester": "master", @@ -425,40 +290,12 @@ "swift-integration-tests": "master", "swift-xcode-playground-support": "master", "ninja": "release", - "icu": "release-61-1", + "icu": "release-65-1", "cmake": "v3.15.1", - "clang-tools-extra": "apple/stable/20190619", - "libcxx": "apple/stable/20190619", "indexstore-db": "master", "sourcekit-lsp": "master", "swift-format": "master" } - }, - "stdlib_standalone" : { - "aliases": ["stdlib_standalone"], - "repos": { - "llvm": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "clang": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "compiler-rt": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift": "stdlib_standalone", - "lldb": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "cmark": "master", - "llbuild": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swiftpm": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-syntax": "master", - "swift-stress-tester": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-corelibs-xctest": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-corelibs-foundation": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-corelibs-libdispatch": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-integration-tests": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "swift-xcode-playground-support": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "ninja": "release", - "icu": "release-61-1", - "clang-tools-extra": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "libcxx": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "indexstore-db": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a", - "sourcekit-lsp": "swift-DEVELOPMENT-SNAPSHOT-2019-06-06-a" - } } } } diff --git a/utils/update_checkout/update_checkout/update_checkout.py b/utils/update_checkout/update_checkout/update_checkout.py index 3d6ee966e37bb..16f910b519eef 100755 --- a/utils/update_checkout/update_checkout/update_checkout.py +++ b/utils/update_checkout/update_checkout/update_checkout.py @@ -18,7 +18,7 @@ import sys import traceback from functools import reduce -from multiprocessing import freeze_support +from multiprocessing import Lock, Pool, cpu_count, freeze_support from swift_build_support.swift_build_support import shell from swift_build_support.swift_build_support.SwiftBuildSupport import \ @@ -29,6 +29,53 @@ SCRIPT_DIR = os.path.dirname(SCRIPT_FILE) +def run_parallel(fn, pool_args, n_processes=0): + """Function used to run a given closure in parallel. + + NOTE: This function was originally located in the shell module of + swift_build_support and should eventually be replaced with a better + parallel implementation. + """ + + def init(l): + global lock + lock = l + + if n_processes == 0: + n_processes = cpu_count() * 2 + + lk = Lock() + print("Running ``%s`` with up to %d processes." % + (fn.__name__, n_processes)) + pool = Pool(processes=n_processes, initializer=init, initargs=(lk,)) + results = pool.map_async(func=fn, iterable=pool_args).get(999999) + pool.close() + pool.join() + return results + + +def check_parallel_results(results, op): + """Function used to check the results of run_parallel. + + NOTE: This function was originally located in the shell module of + swift_build_support and should eventually be replaced with a better + parallel implementation. + """ + + fail_count = 0 + if results is None: + return 0 + for r in results: + if r is not None: + if fail_count == 0: + print("======%s FAILURES======" % op) + print("%s failed (ret=%d): %s" % (r.repo_path, r.ret, r)) + fail_count += 1 + if r.stderr: + print(r.stderr) + return fail_count + + def confirm_tag_in_repo(tag, repo_name): tag_exists = shell.capture(['git', 'ls-remote', '--tags', 'origin', tag], echo=False) @@ -200,8 +247,7 @@ def update_all_repositories(args, config, scheme_name, cross_repos_pr): cross_repos_pr] pool_args.append(my_args) - return shell.run_parallel(update_single_repository, pool_args, - args.n_processes) + return run_parallel(update_single_repository, pool_args, args.n_processes) def obtain_additional_swift_sources(pool_args): @@ -295,8 +341,8 @@ def obtain_all_additional_swift_sources(args, config, with_ssh, scheme_name, print("Not cloning any repositories.") return - return shell.run_parallel(obtain_additional_swift_sources, pool_args, - args.n_processes) + return run_parallel( + obtain_additional_swift_sources, pool_args, args.n_processes) def dump_repo_hashes(args, config, branch_scheme_name='repro'): @@ -399,23 +445,6 @@ def skip_list_for_platform(config): return skip_list -def symlink_llvm_monorepo(args): - print("Create symlink for LLVM Project") - llvm_projects = ['clang', - 'llvm', - 'lldb', - 'compiler-rt', - 'libcxx', - 'clang-tools-extra'] - for project in llvm_projects: - src_path = os.path.join(args.source_root, - 'llvm-project', - project) - dst_path = os.path.join(args.source_root, project) - if not os.path.islink(dst_path): - os.symlink(src_path, dst_path) - - def main(): freeze_support() parser = argparse.ArgumentParser( @@ -497,10 +526,6 @@ def main(): help="The root directory to checkout repositories", default=SWIFT_SOURCE_ROOT, dest='source_root') - parser.add_argument( - '--symlink-llvm-monorepo', - help='Create symlink from LLVM-Project to source root directory', - action='store_true') args = parser.parse_args() if not args.scheme: @@ -567,13 +592,11 @@ def main(): update_results = update_all_repositories(args, config, scheme, cross_repos_pr) fail_count = 0 - fail_count += shell.check_parallel_results(clone_results, "CLONE") - fail_count += shell.check_parallel_results(update_results, "UPDATE") + fail_count += check_parallel_results(clone_results, "CLONE") + fail_count += check_parallel_results(update_results, "UPDATE") if fail_count > 0: print("update-checkout failed, fix errors and try again") else: - if args.symlink_llvm_monorepo: - symlink_llvm_monorepo(args) print("update-checkout succeeded") print_repo_hashes(args, config) sys.exit(fail_count) diff --git a/utils/vim/ftdetect/sil.vim b/utils/vim/ftdetect/sil.vim index 3d856ed3b8280..7e0f0e2934bc7 100644 --- a/utils/vim/ftdetect/sil.vim +++ b/utils/vim/ftdetect/sil.vim @@ -1 +1,9 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + au BufNewFile,BufRead *.sil set ft=sil diff --git a/utils/vim/ftdetect/swift.vim b/utils/vim/ftdetect/swift.vim index c9dc5128e0f05..4812f57fe0edd 100644 --- a/utils/vim/ftdetect/swift.vim +++ b/utils/vim/ftdetect/swift.vim @@ -1 +1,9 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + au BufNewFile,BufRead *.swift set ft=swift diff --git a/utils/vim/ftdetect/swiftgyb.vim b/utils/vim/ftdetect/swiftgyb.vim index fc8919ff6a837..3dd91226f7194 100644 --- a/utils/vim/ftdetect/swiftgyb.vim +++ b/utils/vim/ftdetect/swiftgyb.vim @@ -1,2 +1,9 @@ -au BufNewFile,BufRead *.swift.gyb set ft=swiftgyb +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +au BufNewFile,BufRead *.swift.gyb set ft=swiftgyb diff --git a/utils/vim/ftplugin/swift.vim b/utils/vim/ftplugin/swift.vim index acbb283f98834..422a599e1b8bb 100644 --- a/utils/vim/ftplugin/swift.vim +++ b/utils/vim/ftplugin/swift.vim @@ -1,3 +1,11 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + setlocal comments=s1:/*,mb:*,ex:*/,:///,:// setlocal expandtab setlocal ts=2 diff --git a/utils/vim/ftplugin/swiftgyb.vim b/utils/vim/ftplugin/swiftgyb.vim index 278570270f4b6..033b60c6729f4 100644 --- a/utils/vim/ftplugin/swiftgyb.vim +++ b/utils/vim/ftplugin/swiftgyb.vim @@ -1 +1,9 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + runtime! ftplugin/swift.vim diff --git a/utils/vim/swift-indent.py b/utils/vim/swift-indent.py index 3762284f2cb4e..590141fe21712 100644 --- a/utils/vim/swift-indent.py +++ b/utils/vim/swift-indent.py @@ -88,7 +88,7 @@ def main(argc, argv): lines = stdout.decode(encoding).split('\n') sequence = difflib.SequenceMatcher(None, buf, lines) for op in reversed(sequence.get_opcodes()): - if op[0] is not 'equal': + if op[0] != 'equal': vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]] diff --git a/utils/vim/syntax/sil.vim b/utils/vim/syntax/sil.vim index d3829f9fa4961..5c2e0c30a3620 100644 --- a/utils/vim/syntax/sil.vim +++ b/utils/vim/syntax/sil.vim @@ -1,3 +1,11 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +" " Vim syntax file " Language: sil diff --git a/utils/vim/syntax/swift.vim b/utils/vim/syntax/swift.vim index 9a70c9bf3225b..2928550b4adb8 100644 --- a/utils/vim/syntax/swift.vim +++ b/utils/vim/syntax/swift.vim @@ -1,3 +1,11 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +" " Vim syntax file " Language: swift " Maintainer: Joe Groff @@ -154,40 +162,59 @@ syn region swiftCaseLabelRegion syn region swiftDefaultLabelRegion \ matchgroup=swiftKeyword start=/\/ matchgroup=Delimiter end=/:/ oneline -syn region swiftParenthesisRegion matchgroup=NONE start=/(/ end=/)/ contains=TOP - -syn region swiftString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=swiftInterpolationRegion -syn region swiftInterpolationRegion matchgroup=swiftInterpolation start=/\\(/ end=/)/ contained contains=TOP -syn region swiftComment start="/\*" end="\*/" contains=swiftComment,swiftTodo -syn region swiftLineComment start="//" end="$" contains=swiftTodo - -syn match swiftDecimal /[+\-]\?\<\([0-9][0-9_]*\)\([.][0-9_]*\)\?\([eE][+\-]\?[0-9][0-9_]*\)\?\>/ -syn match swiftHex /[+\-]\?\<0x[0-9A-Fa-f][0-9A-Fa-f_]*\(\([.][0-9A-Fa-f_]*\)\?[pP][+\-]\?[0-9][0-9_]*\)\?\>/ -syn match swiftOct /[+\-]\?\<0o[0-7][0-7_]*\>/ -syn match swiftBin /[+\-]\?\<0b[01][01_]*\>/ - -syn match swiftOperator +\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)+ skipwhite skipempty nextgroup=swiftTypeParameters -syn match swiftOperator "\.\.[<.]" skipwhite skipempty nextgroup=swiftTypeParameters - -syn match swiftChar /'\([^'\\]\|\\\(["'tnr0\\]\|x[0-9a-fA-F]\{2}\|u[0-9a-fA-F]\{4}\|U[0-9a-fA-F]\{8}\)\)'/ +syn region swiftParenthesisRegion contains=TOP + \ matchgroup=NONE start=/(/ end=/)/ + +syn region swiftString contains=swiftInterpolationRegion + \ start=/"/ skip=/\\\\\|\\"/ end=/"/ +syn region swiftInterpolationRegion contained contains=TOP + \ matchgroup=swiftInterpolation start=/\\(/ end=/)/ +syn region swiftComment contains=swiftComment,swiftLineComment,swiftTodo + \ start="/\*" end="\*/" +syn region swiftLineComment contains=swiftComment,swiftTodo + \ start="//" end="$" + +syn match swiftDecimal + \ /[+\-]\?\<\([0-9][0-9_]*\)\([.][0-9_]*\)\?\([eE][+\-]\?[0-9][0-9_]*\)\?\>/ +syn match swiftHex + \ /[+\-]\?\<0x[0-9A-Fa-f][0-9A-Fa-f_]*\(\([.][0-9A-Fa-f_]*\)\?[pP][+\-]\?[0-9][0-9_]*\)\?\>/ +syn match swiftOct + \ /[+\-]\?\<0o[0-7][0-7_]*\>/ +syn match swiftBin + \ /[+\-]\?\<0b[01][01_]*\>/ + +syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters + \ "\.\@!&|^~]\@!&|^~]*\|*/\@![/=\-+*%<>!&|^~]*\|->\@![/=\-+*%<>!&|^~]*\|[=+%<>!&|^~][/=\-+*%<>!&|^~]*\)" +syn match swiftOperator skipwhite skipempty nextgroup=swiftTypeParameters + \ "\.\.[<.]" + +syn match swiftChar + \ /'\([^'\\]\|\\\(["'tnr0\\]\|x[0-9a-fA-F]\{2}\|u[0-9a-fA-F]\{4}\|U[0-9a-fA-F]\{8}\)\)'/ syn match swiftTupleIndexNumber contains=swiftDecimal \ /\.[0-9]\+/ syn match swiftDecimal contained \ /[0-9]\+/ -syn match swiftPreproc /#\(\\|\\|\\)/ -syn match swiftPreproc /^\s*#\(\\|\\|\\|\\|\\|\\)/ -syn region swiftPreprocFalse start="^\s*#\\s\+\" end="^\s*#\(\\|\\|\\)" +syn match swiftPreproc + \ /#\(\\|\\|\\)/ +syn match swiftPreproc + \ /^\s*#\(\\|\\|\\|\\|\\|\\)/ +syn region swiftPreprocFalse + \ start="^\s*#\\s\+\" end="^\s*#\(\\|\\|\\)" -syn match swiftAttribute /@\<\w\+\>/ skipwhite skipempty nextgroup=swiftType,swiftTypeDefinition +syn match swiftAttribute + \ /@\<\w\+\>/ skipwhite skipempty nextgroup=swiftType,swiftTypeDefinition syn keyword swiftTodo MARK TODO FIXME contained -syn match swiftCastOp "\" skipwhite skipempty nextgroup=swiftType -syn match swiftCastOp "\[!?]\?" skipwhite skipempty nextgroup=swiftType +syn match swiftCastOp skipwhite skipempty nextgroup=swiftType + \ "\" +syn match swiftCastOp skipwhite skipempty nextgroup=swiftType + \ "\[!?]\?" -syn match swiftNilOps "??" +syn match swiftNilOps + \ "??" syn region swiftReservedIdentifier oneline \ start=/`/ end=/`/ diff --git a/utils/vim/syntax/swiftgyb.vim b/utils/vim/syntax/swiftgyb.vim index 44e925bcbe0bf..300da06dd3726 100644 --- a/utils/vim/syntax/swiftgyb.vim +++ b/utils/vim/syntax/swiftgyb.vim @@ -1,3 +1,11 @@ +" This source file is part of the Swift.org open source project +" +" Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +" Licensed under Apache License v2.0 with Runtime Library Exception +" +" See https://swift.org/LICENSE.txt for license information +" See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +" " Vim syntax file " Language: gyb on swift diff --git a/validation-test/BuildSystem/skip-local-build.test-sh b/validation-test/BuildSystem/skip-local-build.test-sh new file mode 100644 index 0000000000000..b658957c70a85 --- /dev/null +++ b/validation-test/BuildSystem/skip-local-build.test-sh @@ -0,0 +1,8 @@ +# REQUIRES: standalone_build +# +# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG +# CONFIG: "skip_local_build": true + +# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY +# DRY: build-script-impl +# DRY-SAME: --skip-local-build diff --git a/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift new file mode 100644 index 0000000000000..3eefcdd073ba8 --- /dev/null +++ b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift @@ -0,0 +1,61 @@ +// RUN: %empty-directory(%t) + +// RUN: cp %s %t/main.swift +// RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift +// RUN: touch -t 201401240005 %t/*.swift + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s + +// CHECK-1-NOT: warning +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/main.swift" +// CHECK-1: {{^}$}} + +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/helper.swift" +// CHECK-1: {{^}$}} + +// RUN: ls %t/ | %FileCheck -check-prefix=CHECK-LS %s + +// CHECK-LS-DAG: main.o +// CHECK-LS-DAG: helper.o + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s + +// CHECK-1-SKIPPED-NOT: warning +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/main.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/helper.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift +// RUN: touch -t 201401240006 %t/helper.swift +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 -driver-show-incremental -driver-show-job-lifecycle | %FileCheck -check-prefix=CHECK-2 %s + +// CHECK-2-NOT: warning +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/helper.swift" +// CHECK-2: {{^}$}} + +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/main.swift" +// CHECK-2: {{^}$}} + +func test(obj: Test) { + obj.foo() +} diff --git a/validation-test/Driver/Dependencies/rdar23148987.swift b/validation-test/Driver/Dependencies/rdar23148987.swift index 186d7401111aa..7ff7bcae38369 100644 --- a/validation-test/Driver/Dependencies/rdar23148987.swift +++ b/validation-test/Driver/Dependencies/rdar23148987.swift @@ -4,7 +4,7 @@ // RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift // RUN: touch -t 201401240005 %t/*.swift -// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s +// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s // CHECK-1-NOT: warning // CHECK-1: {{^{$}} @@ -24,7 +24,7 @@ // CHECK-LS-DAG: main.o // CHECK-LS-DAG: helper.o -// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s +// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s // CHECK-1-SKIPPED-NOT: warning // CHECK-1-SKIPPED: {{^{$}} @@ -41,7 +41,7 @@ // RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift // RUN: touch -t 201401240006 %t/helper.swift -// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-2 %s +// RUN: cd %t && %target-build-swift -disable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-2 %s // CHECK-2-NOT: warning // CHECK-2: {{^{$}} diff --git a/validation-test/Driver/Dependencies/rdar25405605.swift b/validation-test/Driver/Dependencies/rdar25405605.swift index 823d406b7271e..83701631464ea 100644 --- a/validation-test/Driver/Dependencies/rdar25405605.swift +++ b/validation-test/Driver/Dependencies/rdar25405605.swift @@ -58,7 +58,9 @@ // RUN: cp %S/Inputs/rdar25405605/helper-3.swift %t/helper.swift // RUN: touch -t 201401240007 %t/helper.swift -// RUN: cd %t && not %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-3 %s +// Driver now schedules jobs in the order of the inputs, so since this test wants +// helper first, pass helper.swift before main.swift +// RUN: cd %t && not %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./helper.swift ./main.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-3 %s // CHECK-3-NOT: warning // CHECK-3: {{^{$}} diff --git a/validation-test/Driver/batch_mode_size_limit.swift b/validation-test/Driver/batch_mode_size_limit.swift index 0c55c326dacee..4ca7918958460 100644 --- a/validation-test/Driver/batch_mode_size_limit.swift +++ b/validation-test/Driver/batch_mode_size_limit.swift @@ -1,3 +1,5 @@ +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + // RUN: %empty-directory(%t) // RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift // RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift @@ -9,26 +11,66 @@ // RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift // RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift // RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift -// RUN: %swiftc_driver -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -// CHECK-NOT: unable to execute command -// CHECK: Forming into 4 batches -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents -// CHECK: Forming batch job from 25 constituents +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 + +// RUN: %FileCheck %s -check-prefix=CHECK-DIS <%t/out.txt +// CHECK-DIS-NOT: unable to execute command +// CHECK-DIS: Forming into 4 batches +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents +// CHECK-DIS-DAG: Forming batch job from 25 constituents + + +// RUN: %swiftc_driver -disable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-DIS +// EXPLICIT-ARG-DIS-NOT: unable to execute command +// EXPLICIT-ARG-DIS: Forming into 10 batches +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-DIS-DAG: Forming batch job from 10 constituents + + +// When -enable-only-one-dependency-file (which is the default) the one compile job with the dependency output is unbatchable. + +// RUN: %empty-directory(%t) +// RUN: touch %t/f_1_1.swift %t/f_1_2.swift %t/f_1_3.swift %t/f_1_4.swift %t/f_1_5.swift %t/f_1_6.swift %t/f_1_7.swift %t/f_1_8.swift %t/f_1_9.swift %t/f_1_10.swift +// RUN: touch %t/f_2_1.swift %t/f_2_2.swift %t/f_2_3.swift %t/f_2_4.swift %t/f_2_5.swift %t/f_2_6.swift %t/f_2_7.swift %t/f_2_8.swift %t/f_2_9.swift %t/f_2_10.swift +// RUN: touch %t/f_3_1.swift %t/f_3_2.swift %t/f_3_3.swift %t/f_3_4.swift %t/f_3_5.swift %t/f_3_6.swift %t/f_3_7.swift %t/f_3_8.swift %t/f_3_9.swift %t/f_3_10.swift +// RUN: touch %t/f_4_1.swift %t/f_4_2.swift %t/f_4_3.swift %t/f_4_4.swift %t/f_4_5.swift %t/f_4_6.swift %t/f_4_7.swift %t/f_4_8.swift %t/f_4_9.swift %t/f_4_10.swift +// RUN: touch %t/f_5_1.swift %t/f_5_2.swift %t/f_5_3.swift %t/f_5_4.swift %t/f_5_5.swift %t/f_5_6.swift %t/f_5_7.swift %t/f_5_8.swift %t/f_5_9.swift %t/f_5_10.swift +// RUN: touch %t/f_6_1.swift %t/f_6_2.swift %t/f_6_3.swift %t/f_6_4.swift %t/f_6_5.swift %t/f_6_6.swift %t/f_6_7.swift %t/f_6_8.swift %t/f_6_9.swift %t/f_6_10.swift +// RUN: touch %t/f_7_1.swift %t/f_7_2.swift %t/f_7_3.swift %t/f_7_4.swift %t/f_7_5.swift %t/f_7_6.swift %t/f_7_7.swift %t/f_7_8.swift %t/f_7_9.swift %t/f_7_10.swift +// RUN: touch %t/f_8_1.swift %t/f_8_2.swift %t/f_8_3.swift %t/f_8_4.swift %t/f_8_5.swift %t/f_8_6.swift %t/f_8_7.swift %t/f_8_8.swift %t/f_8_9.swift %t/f_8_10.swift +// RUN: touch %t/f_9_1.swift %t/f_9_2.swift %t/f_9_3.swift %t/f_9_4.swift %t/f_9_5.swift %t/f_9_6.swift %t/f_9_7.swift %t/f_9_8.swift %t/f_9_9.swift %t/f_9_10.swift +// RUN: touch %t/f_10_1.swift %t/f_10_2.swift %t/f_10_3.swift %t/f_10_4.swift %t/f_10_5.swift %t/f_10_6.swift %t/f_10_7.swift %t/f_10_8.swift %t/f_10_9.swift %t/f_10_10.swift +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 +// RUN: %FileCheck %s -check-prefix=CHECK-ENA <%t/out.txt +// CHECK-ENA-NOT: unable to execute command +// CHECK-ENA: Forming into 4 batches +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 25 constituents +// CHECK-ENA-DAG: Forming batch job from 24 constituents // -// RUN: %swiftc_driver -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out.txt 2>&1 -// RUN: %FileCheck %s <%t/out.txt -check-prefix=EXPLICIT-ARG -// EXPLICIT-ARG-NOT: unable to execute command -// EXPLICIT-ARG: Forming into 10 batches -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents -// EXPLICIT-ARG: Forming batch job from 10 constituents +// RUN: %swiftc_driver -enable-only-one-dependency-file -driver-show-job-lifecycle -driver-batch-size-limit 10 -v -c -module-name foo -emit-module -serialize-diagnostics -emit-dependencies -j 1 -enable-batch-mode %t/f_*.swift >%t/out2.txt 2>&1 +// RUN: %FileCheck %s <%t/out2.txt -check-prefix=EXPLICIT-ARG-ENA +// EXPLICIT-ARG-ENA-NOT: unable to execute command +// EXPLICIT-ARG-ENA: Forming into 10 batches +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 10 constituents +// EXPLICIT-ARG-ENA-DAG: Forming batch job from 9 constituents diff --git a/validation-test/IDE/crashers_2/complete_repl_decl_conformance.swift b/validation-test/IDE/crashers_2/complete_repl_decl_conformance.swift new file mode 100644 index 0000000000000..646b07cc094e3 --- /dev/null +++ b/validation-test/IDE/crashers_2/complete_repl_decl_conformance.swift @@ -0,0 +1,5 @@ +// TODO(SR-12076): Make this not crash. +// RUN: not --crash %target-swift-ide-test -repl-code-completion -source-filename %s +// REQUIRES: asserts + +struct Bar: diff --git a/validation-test/IDE/crashers_2_fixed/rdar52105899.swift b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift new file mode 100644 index 0000000000000..cbce5cd0c6d6e --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/rdar52105899.swift @@ -0,0 +1,10 @@ +// RUN: %empty-directory(%t) +// RUN: echo '//DUMMY' > %t/dummy.swift +// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s %t/dummy.swift > /dev/null +// RUN: %target-swift-ide-test -code-completion -code-completion-token=B -source-filename=%s %t/dummy.swift > /dev/null + +struct MyStruct { + let _: Int = #^A^# +} + +let _: Int = #^B^# diff --git a/validation-test/IDE/crashers_2_fixed/rdar55267292.swift b/validation-test/IDE/crashers_2_fixed/rdar55267292.swift deleted file mode 100644 index b40f7e1b82658..0000000000000 --- a/validation-test/IDE/crashers_2_fixed/rdar55267292.swift +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: %target-swift-ide-test -syntax-coloring -source-filename %s -typealias A = Generic' to expected argument type 'Color?'}} + // expected-error@-2 {{referencing subscript 'subscript(dynamicMember:)' requires wrapper 'Binding'}} + // expected-error@-3 {{value of type 'ColorScheme' has no dynamic member 'color' using key path from root type 'ColorScheme'}} + } + } +} diff --git a/validation-test/Sema/generic_function_signature.swift b/validation-test/Sema/generic_function_signature.swift new file mode 100644 index 0000000000000..92eee65355e4e --- /dev/null +++ b/validation-test/Sema/generic_function_signature.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -typecheck %s +enum E {} + +protocol P { + associatedtype T + var closure: () -> E { get } +} + +struct Concrete: P { + let closure: () -> E +} diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar49712364.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar49712364.swift new file mode 100644 index 0000000000000..c4ee382b8dc5e --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar49712364.swift @@ -0,0 +1,15 @@ +// RUN: not %target-swift-frontend %s -typecheck + +protocol A {} + +class C where T: A {} + +extension C { + func foo() { + extension C where T: Undefined { + class Inner: Encodable { + var foo: Int + } + } + } +} diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift index 232c33626bd71..0b758816d6734 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift @@ -10,7 +10,8 @@ struct Generic { @_functionBuilder struct Builder { - static func buildBlock(_ c0: C0, _ c1: C1) // expected-note {{where 'C0' = 'Empty'}} expected-note {{where 'C1' = 'Test'}} + static func buildBlock(_ c0: C0, _ c1: C1) // expected-note 2 {{where 'C0' = 'Empty'}} expected-note {{where 'C1' = 'Test>'}} + // expected-note@-1 {{'buildBlock' declared here}} -> Generic<(C0, C1)> where C0 : P, C1 : P { return Generic((c0, c1)) } @@ -24,13 +25,16 @@ struct Empty { init() {} } -struct Test where T : P { // expected-note {{where 'T' = 'Empty'}} +struct Test where T : P { // expected-note {{where 'T' = 'Generic<(Empty, _)>'}} init(@Builder _: () -> T) {} } let x = G { // expected-error@-1 {{static method 'buildBlock' requires that 'Empty' conform to 'P'}} - // expected-error@-2 {{static method 'buildBlock' requires that 'Test' conform to 'P'}} + // expected-error@-2 {{static method 'buildBlock' requires that 'Test>' conform to 'P'}} Empty() - Test { Empty() } // expected-error {{generic struct 'Test' requires that 'Empty' conform to 'P'}} + Test { Empty() } + // expected-error@-1 {{static method 'buildBlock' requires that 'Empty' conform to 'P'}} + // expected-error@-2 {{missing argument for parameter #2 in call}} + // expected-error@-3 {{generic struct 'Test' requires that 'Generic<(Empty, _)>' conform to 'P'}} } diff --git a/validation-test/Sema/type_checker_perf/fast/array_of_tuples.swift.gyb b/validation-test/Sema/type_checker_perf/fast/array_of_tuples.swift.gyb index c31af8247d186..a7614cfecba06 100644 --- a/validation-test/Sema/type_checker_perf/fast/array_of_tuples.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/array_of_tuples.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 20 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let a = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift b/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift index 3aa1184c0038d..b9dfd039243a9 100644 --- a/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift +++ b/validation-test/Sema/type_checker_perf/fast/expression_too_complex_4.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -swift-version 4 -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test(_ i: Int, _ j: Int) -> Int { return 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 + diff --git a/validation-test/Sema/type_checker_perf/fast/more_specialized_generic_func.swift.gyb b/validation-test/Sema/type_checker_perf/fast/more_specialized_generic_func.swift.gyb index 7e758774daf08..bbf11e474f018 100644 --- a/validation-test/Sema/type_checker_perf/fast/more_specialized_generic_func.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/more_specialized_generic_func.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 8 --end 40 --step 2 --select NumLeafScopes %s --expected-exit-code 0 -// REQUIRES: asserts +// REQUIRES: asserts,no_asan protocol P { } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar17024694.swift b/validation-test/Sema/type_checker_perf/fast/rdar17024694.swift index 0181500267f6b..493da6e8e4f9e 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar17024694.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar17024694.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan _ = (2...100).reversed().filter({ $0 % 11 == 0 }).map { "\($0) bottles of beer on the wall, \($0) bottles of beer;\n" diff --git a/validation-test/Sema/type_checker_perf/fast/rdar17077404.swift b/validation-test/Sema/type_checker_perf/fast/rdar17077404.swift index 92bc2dbc8e5c6..64742e168a2b1 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar17077404.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar17077404.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func memoize( f: @escaping ((A) -> R, A) -> R diff --git a/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift b/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift index e804668807c99..aeb136a7992b0 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar17170728.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let i: Int? = 1 let j: Int? diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb index 48bd229c1b3e9..d3a80b33700c9 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar18360240.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 10 --step 2 --select NumConstraintScopes --polynomial-threshold 1.5 %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let empty: [Int] = [] let _ = empty + diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18522024.swift b/validation-test/Sema/type_checker_perf/fast/rdar18522024.swift index a5bcbd022596b..88b384e7deca4 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar18522024.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar18522024.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test(a: [String], b: String, c: String) -> [String] { return a.map { $0 + ": " + b + "(" + c + $0 + ")" } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18699199.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar18699199.swift.gyb index a180c0a87482b..0fd82a2998bfe 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar18699199.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar18699199.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan public enum E { case First, diff --git a/validation-test/Sema/type_checker_perf/fast/rdar18724501.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar18724501.swift.gyb index 65e43aece7d41..c1278487cf05f 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar18724501.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar18724501.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 5 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan typealias X = (Range, [Range]) diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19029974.swift b/validation-test/Sema/type_checker_perf/fast/rdar19029974.swift index 7529d9416141c..f3c1c4d3afea7 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19029974.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar19029974.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan infix operator <*> : AdditionPrecedence func <*>(lhs: ((A) -> B)?, rhs: A?) -> B? { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19157118.swift b/validation-test/Sema/type_checker_perf/fast/rdar19157118.swift index a152735265157..58d4801434cc1 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19157118.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar19157118.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan public func expectEqualMethodsForDomain< SelfType, ArgumentType, Result : Equatable diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19181998.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar19181998.swift.gyb index 7c23540f8ecff..de0f9666d44f6 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19181998.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar19181998.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 30 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan public func test(_ fn: @escaping () -> Void) {} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19181998_nominals.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar19181998_nominals.swift.gyb index dd996ad7e71bf..1b818b760ce7a 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19181998_nominals.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar19181998_nominals.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 30 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan struct A { struct B { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift b/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift index 8cc9d0267e4bd..41b9ae8518d13 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar19357292.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test(strings: [String]) { for string in strings { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19394804.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar19394804.swift.gyb index 1c48071767c4a..1fe0fdb5f5029 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19394804.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar19394804.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan class rdar19394804 { let data = [ diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19738292.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar19738292.swift.gyb index 102d3f524114b..a203506da088e 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19738292.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar19738292.swift.gyb @@ -1,6 +1,6 @@ // RUN: %scale-test --begin 7 --end 12 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts -// REQUIRES: rdar42650365 +// REQUIRES: asserts,no_asan +// REQUIRES: rdar42650365,no_asan let a = [[0]] _ = a[0][0] diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19777895.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar19777895.swift.gyb index cc500050b1d38..0a435b370c0a9 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19777895.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar19777895.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 3 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let _ = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift b/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift index 64b19069c31e4..b6f7dd0106422 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar19836070.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let _: (Character) -> Bool = { c in ("a" <= c && c <= "z") || ("A" <= c && c <= "Z") || c == "_" diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20233198_any.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20233198_any.swift.gyb index 7ec93bf0406bd..1651bd4cf4976 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20233198_any.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20233198_any.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 6 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let _ = (UInt8(0) %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20233198_explicit_overloads.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20233198_explicit_overloads.swift.gyb index 3699c5b161880..1169b722611bf 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20233198_explicit_overloads.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20233198_explicit_overloads.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 6 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan func overload(_ x: Int) -> Int { return x } func overload(_ x: Float) -> Float { return x } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20233198_named.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20233198_named.swift.gyb index 2fe9fc07164ae..01b27112c43dd 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20233198_named.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20233198_named.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 6 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let tuple = (UInt8(0) %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20233198_typed.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20233198_typed.swift.gyb index 587a19993d326..7b851ed681d8c 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20233198_typed.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20233198_typed.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 6 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let tuple: (UInt8 %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20233198_weak.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20233198_weak.swift.gyb index 91cfd66605da9..36e64fb02b8fc 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20233198_weak.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20233198_weak.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 6 --step 1 --select NumLeafScopes %s -Xfrontend=-verify -// REQUIRES: asserts +// REQUIRES: asserts,no_asan weak var tuple = (UInt8(0) // expected-error {{'weak' may only be applied to}} %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20818064.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20818064.swift.gyb index a8384cf12eecf..f2bf60a2d0fe5 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20818064.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20818064.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 20 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan typealias D = [String: Any] diff --git a/validation-test/Sema/type_checker_perf/fast/rdar20875936.swift b/validation-test/Sema/type_checker_perf/fast/rdar20875936.swift index b5c78d2ef5082..fb4e2284d3ac5 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar20875936.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar20875936.swift @@ -1,4 +1,4 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let _ = Array([0].lazy.reversed().filter { $0 % 2 == 0 }.map { $0 / 2 }) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar20959612.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar20959612.swift.gyb similarity index 75% rename from validation-test/Sema/type_checker_perf/slow/rdar20959612.swift.gyb rename to validation-test/Sema/type_checker_perf/fast/rdar20959612.swift.gyb index ad87ec01da757..02cbf1053009d 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar20959612.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar20959612.swift.gyb @@ -1,5 +1,5 @@ -// RUN: %scale-test --invert-result --begin 3 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// RUN: %scale-test --begin 3 --end 15 --step 1 --select NumLeafScopes %s +// REQUIRES: asserts,no_asan func curry [UInt8] { return (0...10).map { _ in diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22282851.swift b/validation-test/Sema/type_checker_perf/fast/rdar22282851.swift index 03b6713b28fe6..7fe2e5eedb823 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22282851.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar22282851.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct S { let s: String diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22466245.swift b/validation-test/Sema/type_checker_perf/fast/rdar22466245.swift index 590b27ff922ca..eef0cda01537b 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22466245.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar22466245.swift @@ -1,4 +1,4 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan _ = (1...10).map(String.init) + [": hi"] diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22532650.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar22532650.swift.gyb index bf66800181a76..3445510fc87a4 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22532650.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar22532650.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 10 --end 15 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan _ = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22626740.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar22626740.swift.gyb index 326a56eefa4c9..dbb0e57dea725 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22626740.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar22626740.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan var a: [UInt32] diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift b/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift index 54c5c539bbe26..430b4f5caba2d 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar22770433.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test(n: Int) -> Int { return n == 0 ? 0 : (0.. Bool] = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22836718.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar22836718.swift.gyb index 02f62caa94f33..00c07122ea7c8 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22836718.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar22836718.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 5 --end 10 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let _ = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift b/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift index 953b7dcc23e01..cf634beccefbd 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar23429943.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let _ = [0].reduce([Int]()) { return $0.count == 0 && ($1 == 0 || $1 == 2 || $1 == 3) ? [] : $0 + [$1] diff --git a/validation-test/Sema/type_checker_perf/fast/rdar23620262.swift b/validation-test/Sema/type_checker_perf/fast/rdar23620262.swift index f0e1b43e72b24..bf78e6a8cb6f1 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar23620262.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar23620262.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts -// REQUIRES: rdar38378503 +// REQUIRES: tools-release,no_asan +// REQUIRES: rdar38378503,no_asan // expected-no-diagnostics diff --git a/validation-test/Sema/type_checker_perf/fast/rdar24543332.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar24543332.swift.gyb index f00b13adb87a2..66918a6d1968d 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar24543332.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar24543332.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 5 --end 20 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan _ = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar26939465.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar26939465.swift.gyb index 59a016b41df4a..1842c25cdfa02 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar26939465.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar26939465.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan class NSNumber { init(value: Bool) {} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar28018866.swift b/validation-test/Sema/type_checker_perf/fast/rdar28018866.swift index eeb121de6f2d3..a32dc2264ad91 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar28018866.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar28018866.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct P { let x: Float diff --git a/validation-test/Sema/type_checker_perf/fast/rdar29025667.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar29025667.swift.gyb index 8fe1f3be07175..380bf5cd93523 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar29025667.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar29025667.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let s: String? = nil diff --git a/validation-test/Sema/type_checker_perf/fast/rdar29358447.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar29358447.swift.gyb index 057403e1d69e4..70ebdfd8aeddf 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar29358447.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar29358447.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 0 --end 10000 --step 1000 --typecheck --select incrementConstraintsPerContractionCounter %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let _: [Int] = [ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar30213053.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar30213053.swift.gyb index 81e53ce17b588..6cc6e67fbd884 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar30213053.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar30213053.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 3 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan let _: [Int: (Int, Int) -> Bool] = [ diff --git a/validation-test/Sema/type_checker_perf/fast/rdar30389602.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar30389602.swift.gyb index 1e41ed86f63d6..a43b61873845c 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar30389602.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar30389602.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan class C { %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar30729643.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar30729643.swift.gyb index c21fa6625bc19..3515aeaea4c33 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar30729643.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar30729643.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 15 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan enum X : String { case first diff --git a/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift b/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift index c3e48455f4414..9802d55b497f8 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar31439825.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let a = 1 diff --git a/validation-test/Sema/type_checker_perf/fast/rdar31563957.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar31563957.swift.gyb index ee08646858f02..6270202abc9c4 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar31563957.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar31563957.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 5 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan struct rdar33511986 { func test(s: String, b: Bool, os: String?, oa: Any?, o: AnyObject) -> [Any] { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift b/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift index 65a07ab3cb702..92eb51d065103 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar31742586.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func rdar31742586() -> Double { return -(1 + 2) + -(3 + 4) + 5 - (-(1 + 2) + -(3 + 4) + 5) diff --git a/validation-test/Sema/type_checker_perf/fast/rdar32221800.swift b/validation-test/Sema/type_checker_perf/fast/rdar32221800.swift index 03ca15f919926..66a3788f65c1d 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar32221800.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar32221800.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test(header_field_mark: Bool?, header_value_mark: Bool?, url_mark: Bool?, body_mark: Bool?, status_mark: Bool?) { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift b/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift index f1c20e36b53b9..3b4fcc831cfc1 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar32998180.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func rdar32998180(value: UInt16) -> UInt16 { let result = ((((value >> 1) ^ (value >> 1) ^ (value >> 1) ^ (value >> 1)) & 1) << 1) diff --git a/validation-test/Sema/type_checker_perf/fast/rdar32999041.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar32999041.swift.gyb index 86031343e2336..597fbe61d3744 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar32999041.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar32999041.swift.gyb @@ -1,7 +1,7 @@ // RUN: %scale-test --begin 3 --end 8 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan // FIXME: https://bugs.swift.org/browse/SR-6997 -// REQUIRES: SR6997 +// REQUIRES: SR6997,no_asan _ = MemoryLayout.size %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar33292740.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar33292740.swift.gyb index 259fa08e212b2..033afce3a9d74 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar33292740.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar33292740.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 2 --end 10 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan struct V3 { init(_ x: Int, _ y: Int, _ z: Int) { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar33806601.swift b/validation-test/Sema/type_checker_perf/fast/rdar33806601.swift index faeb4a65c59fb..ec42a4a7b48fe 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar33806601.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar33806601.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan class P { var x : Int = 0 diff --git a/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift b/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift index bf9ec71b9b5d7..8c12b9ac6bd00 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar35213699.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -solver-disable-shrink -disable-constraint-solver-performance-hacks -solver-enable-operator-designated-types -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func test() { let _: UInt = 1 * 2 + 3 * 4 + 5 * 6 + 7 * 8 + 9 * 10 + 11 * 12 + 13 * 14 diff --git a/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift b/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift index a9f7cd55991f3..48b4a28e62fde 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct T { enum B { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar40344044.swift.gyb b/validation-test/Sema/type_checker_perf/fast/rdar40344044.swift.gyb index 0fbfba34390bf..e2f5213cdc0c3 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar40344044.swift.gyb +++ b/validation-test/Sema/type_checker_perf/fast/rdar40344044.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 3 --end 20 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan protocol P {} class C : P {} diff --git a/validation-test/Sema/type_checker_perf/fast/rdar46541800.swift b/validation-test/Sema/type_checker_perf/fast/rdar46541800.swift index 2dbfb75176cd3..97a123f2e6ac4 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar46541800.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar46541800.swift @@ -1,6 +1,5 @@ -// RUN: %target-typecheck-verify-swift -// RUN: %target-typecheck-verify-swift -// REQUIRES: OS=macosx +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: OS=macosx,no_asan import simd import CoreGraphics diff --git a/validation-test/Sema/type_checker_perf/fast/rdar46939892.swift b/validation-test/Sema/type_checker_perf/fast/rdar46939892.swift index 69df1facf3e19..774dc29818706 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar46939892.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar46939892.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 struct S { let a: Int diff --git a/validation-test/Sema/type_checker_perf/fast/rdar47492691.swift b/validation-test/Sema/type_checker_perf/fast/rdar47492691.swift index fd6246fc1141d..47fcdcb0771af 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar47492691.swift +++ b/validation-test/Sema/type_checker_perf/fast/rdar47492691.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -// REQUIRES: objc_interop +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: objc_interop,no_asan import CoreGraphics import simd diff --git a/validation-test/Sema/type_checker_perf/fast/simd_add.gyb b/validation-test/Sema/type_checker_perf/fast/simd_add.gyb index 7b0f8ec1d1a38..6f80407932a79 100644 --- a/validation-test/Sema/type_checker_perf/fast/simd_add.gyb +++ b/validation-test/Sema/type_checker_perf/fast/simd_add.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 3 --end 7 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan func test(_ s: SIMD4, %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/sr139.swift b/validation-test/Sema/type_checker_perf/fast/sr139.swift deleted file mode 100644 index 05265655ecd4d..0000000000000 --- a/validation-test/Sema/type_checker_perf/fast/sr139.swift +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %target-typecheck-verify-swift -// RUN: %target-swift-frontend -typecheck %s - -// SR-139: -// Infinite recursion parsing bitwise operators -let x = UInt32(0x1FF)&0xFF << 24 | UInt32(0x1FF)&0xFF << 16 | UInt32(0x1FF)&0xFF << 8 | (UInt32(0x1FF)&0xFF) - diff --git a/validation-test/Sema/type_checker_perf/slow/nil_coalescing.swift.gyb b/validation-test/Sema/type_checker_perf/slow/nil_coalescing.swift.gyb index d6349fdd4348e..f3f401fec4be9 100644 --- a/validation-test/Sema/type_checker_perf/slow/nil_coalescing.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/nil_coalescing.swift.gyb @@ -1,6 +1,6 @@ // RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts -// REQUIRES: rdar38963783 +// REQUIRES: asserts,no_asan +// REQUIRES: rdar38963783,no_asan func t(_ x: Int?) -> Int { return (x ?? 0) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar18800950.swift b/validation-test/Sema/type_checker_perf/slow/rdar18800950.swift index 132a95a848e92..78052cd2c19ff 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar18800950.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar18800950.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan // Mixed Float and Double arithmetic func rdar18800950(v: Float) -> Double { diff --git a/validation-test/Sema/type_checker_perf/slow/rdar18994321.swift b/validation-test/Sema/type_checker_perf/slow/rdar18994321.swift index ad836f7958505..a89d38f3ce6e6 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar18994321.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar18994321.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan precedencegroup ExponentiationPrecedence { associativity: right diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19368383.swift b/validation-test/Sema/type_checker_perf/slow/rdar19368383.swift index b1f5bea211189..ca6e9f19281d5 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19368383.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19368383.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan // Missing force of optional result of dictionary lookup. func rdar19368383(d: [String : String]) -> [String] { diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift b/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift index 1eef9964553a5..da6e11712f1ee 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct Stringly { init(string: String) {} init(format: String, _ args: Any...) {} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift b/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift index 8ebc5a97b552a..b47a6144edd09 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19737632.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let a = "a" let b = "b" diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19915443.swift b/validation-test/Sema/type_checker_perf/slow/rdar19915443.swift index b0d161194e5a1..addc8c31d9a08 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19915443.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19915443.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan var counts = [ 0, 0, 0, 0 ] // NOTE: This is using mixed types, and would result in a type checking error if it completed. diff --git a/validation-test/Sema/type_checker_perf/slow/rdar20859567.swift b/validation-test/Sema/type_checker_perf/slow/rdar20859567.swift index a640a29f25177..64d880b7c5d71 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar20859567.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar20859567.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct Stringly { init(string: String) {} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar21198787.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar21198787.swift.gyb index 321d94c7ff4b6..1f869b9009017 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar21198787.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar21198787.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --invert-result --begin 1 --end 10 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// REQUIRES: asserts,no_asan private let _: [Any?] = [[ %for i in range(0, N): diff --git a/validation-test/Sema/type_checker_perf/fast/rdar22022980.swift b/validation-test/Sema/type_checker_perf/slow/rdar22022980.swift similarity index 71% rename from validation-test/Sema/type_checker_perf/fast/rdar22022980.swift rename to validation-test/Sema/type_checker_perf/slow/rdar22022980.swift index becc659b57012..74b5698def071 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar22022980.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22022980.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 5 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan _ = [1, 3, 5, 7, 11].filter{ $0 == 1 || $0 == 3 || $0 == 11 || $0 == 1 || $0 == 3 || $0 == 11 } == [ 1, 3, 11 ] +// expected-error@-1 {{unable to type-check}} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift b/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift index bfdcce69e4fd3..8f22a4ab7a37b 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let _ = (0...1).lazy.flatMap { a in (1...2).lazy.map { b in (a, b) } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar22877285.swift b/validation-test/Sema/type_checker_perf/slow/rdar22877285.swift index 4f81f04feb41b..3f728a9c29a4d 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar22877285.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22877285.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan let j = 1 diff --git a/validation-test/Sema/type_checker_perf/slow/rdar22949523.swift b/validation-test/Sema/type_checker_perf/slow/rdar22949523.swift index 1ee4dc3cc6347..b5d9df3d2c474 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar22949523.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22949523.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan _ = [0,1,2,3].lazy.map { String($0)+"hi" }.sorted(by: { $0 > $1 && $1 < $0 && ($1 + $0) < 1000 }) // expected-error@-1 {{reasonable time}} diff --git a/validation-test/Sema/type_checker_perf/slow/rdar23224583.swift b/validation-test/Sema/type_checker_perf/slow/rdar23224583.swift index 5a6698e76e5e3..908ebf49b23fc 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar23224583.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar23224583.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan // Mixed UInt32 and Double let x: UInt32 = 1 diff --git a/validation-test/Sema/type_checker_perf/fast/rdar23327871.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar23327871.swift.gyb similarity index 62% rename from validation-test/Sema/type_checker_perf/fast/rdar23327871.swift.gyb rename to validation-test/Sema/type_checker_perf/slow/rdar23327871.swift.gyb index e75507361c32b..1b4085ffc6ec7 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar23327871.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar23327871.swift.gyb @@ -1,5 +1,5 @@ -// RUN: %scale-test --begin 8 --end 16 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts +// RUN: %scale-test --begin 8 --end 16 --step 1 --select NumLeafScopes %s -Xfrontend=-solver-expression-time-threshold=1 +// REQUIRES: asserts,no_asan let i = 1 _ = 1.0 * Float(i) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar23682605.swift b/validation-test/Sema/type_checker_perf/slow/rdar23682605.swift index dd84950062b2e..b7bc757fe1989 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar23682605.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar23682605.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func memoize( body: @escaping ((T)->U, T)->U ) -> (T)->U { var memo = Dictionary() diff --git a/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift b/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift index 4942edb69f382..3f10765e02aa0 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar23861629.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan struct S { var s: String? } diff --git a/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb similarity index 76% rename from validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb rename to validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb index 3fd0109eb5f51..3b631b01c1616 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar25866240.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar25866240.swift.gyb @@ -1,5 +1,5 @@ -// RUN: %scale-test --begin 2 --end 10 --step 1 --select NumConstraintScopes %s -Xfrontend=-solver-disable-shrink -Xfrontend=-disable-constraint-solver-performance-hacks -Xfrontend=-solver-enable-operator-designated-types -// REQUIRES: asserts +// RUN: %scale-test --begin 2 --end 10 --step 1 --select NumConstraintScopes %s -Xfrontend=-solver-disable-shrink -Xfrontend=-disable-constraint-solver-performance-hacks -Xfrontend=-solver-enable-operator-designated-types -Xfrontend=-solver-expression-time-threshold=1 +// REQUIRES: asserts,no_asan func f( %for i in range(N): diff --git a/validation-test/Sema/type_checker_perf/slow/rdar26564101.swift b/validation-test/Sema/type_checker_perf/slow/rdar26564101.swift index be04b97f88bf2..5599987bb6d03 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar26564101.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar26564101.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts -// REQUIRES: rdar46850561 +// REQUIRES: tools-release,no_asan +// REQUIRES: rdar46850561,no_asan func rdar26564101(a: [Double], m: Double) -> Double { return Double(Array(0...a.count - 1).reduce(0) { $0 + $1 - m }) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar27585838.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar27585838.swift.gyb index 19133311b1c9d..f549fee70f572 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar27585838.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar27585838.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --begin 1 --end 5 --step 1 --select NumLeafScopes %s --polynomial-threshold=1.7 -// REQUIRES: asserts +// REQUIRES: asserts,no_asan enum Operation { case binaryOperation((Double, Double) -> Double) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar30596744_1.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar30596744_1.swift.gyb index 1eb821f20f385..a399cd138a308 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar30596744_1.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar30596744_1.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s --expected-exit-code 1 -// REQUIRES: asserts +// REQUIRES: asserts,no_asan % enum_cases = N % array_elements = 3 diff --git a/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb index 8ca8ad2fdf8d0..cfd18b2c7006e 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb @@ -1,5 +1,5 @@ // RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s --expected-exit-code 1 -// REQUIRES: asserts +// REQUIRES: asserts,no_asan % enum_cases = 3 % array_elements = N diff --git a/validation-test/Sema/type_checker_perf/slow/rdar30606089.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar30606089.swift.gyb index 9723c4abbf05b..b5f9a8bab82dd 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar30606089.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar30606089.swift.gyb @@ -1,8 +1,8 @@ // RUN: %scale-test --begin 1 --end 5 --step 1 --select NumLeafScopes %s --polynomial-threshold 1.7 -// REQUIRES: asserts +// REQUIRES: asserts,no_asan // FIXME: https://bugs.swift.org/browse/SR-6520 -// REQUIRES: SR6520 +// REQUIRES: SR6520,no_asan let derivative = { (t: Double) -> (Double) in return -2.0 / ( diff --git a/validation-test/Sema/type_checker_perf/slow/rdar30631148.swift b/validation-test/Sema/type_checker_perf/slow/rdar30631148.swift index 82c2fdb7471df..af8d486c8f73e 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar30631148.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar30631148.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan func fun(_ x: Double) -> Double { fatalError() } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar32034560.swift b/validation-test/Sema/type_checker_perf/slow/rdar32034560.swift index e6e4ce9dfc975..d1c1db082fd11 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar32034560.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar32034560.swift @@ -1,6 +1,6 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: rdar42744631 -// REQUIRES: tools-release,no_asserts +// REQUIRES: rdar42744631,no_asan +// REQUIRES: tools-release,no_asan struct S { var A: [[UInt32]] diff --git a/validation-test/Sema/type_checker_perf/slow/rdar33289839.swift.gyb b/validation-test/Sema/type_checker_perf/slow/rdar33289839.swift.gyb index b724413dd59fb..0e67d055a9392 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar33289839.swift.gyb +++ b/validation-test/Sema/type_checker_perf/slow/rdar33289839.swift.gyb @@ -1,6 +1,6 @@ // RUN: %scale-test --invert-result --begin 2 --end 6 --step 1 --select NumLeafScopes %s -// REQUIRES: asserts -// REQUIRES: rdar42969824 +// REQUIRES: asserts,no_asan +// REQUIRES: rdar42969824,no_asan func rdar33289839(s: String) -> String { return "test" + String(s) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar33476240.swift b/validation-test/Sema/type_checker_perf/slow/rdar33476240.swift index 5a9206513ac3c..62b45c0102c5c 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar33476240.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar33476240.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan // Mixed Int/Double slow to emit diagnostics func rdar33476240(col: Int, row: Int, maxCol: Int, maxRow: Int) { diff --git a/validation-test/Sema/type_checker_perf/fast/rdar33688063.swift b/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift similarity index 73% rename from validation-test/Sema/type_checker_perf/fast/rdar33688063.swift rename to validation-test/Sema/type_checker_perf/slow/rdar33688063.swift index e803ee443152f..70f403f52057c 100644 --- a/validation-test/Sema/type_checker_perf/fast/rdar33688063.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar33688063.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan +// REQUIRES: rdar57050532 let _ = 1 | UInt32(0) << 0 | UInt32(1) << 1 | UInt32(2) << 2 | UInt32(3) << 3 | UInt32(4) << 4 diff --git a/validation-test/Sema/type_checker_perf/slow/rdar33935430.swift b/validation-test/Sema/type_checker_perf/slow/rdar33935430.swift index 5205754d31151..d99135865c8eb 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar33935430.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar33935430.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -// REQUIRES: tools-release,no_asserts +// REQUIRES: tools-release,no_asan // Mixed Int/Float arithmetic func rdar33935430(a: Int, b: Int, c: Float, d: Float, n: Int) { diff --git a/validation-test/Sema/type_checker_perf/slow/rdar46713933.swift b/validation-test/Sema/type_checker_perf/slow/rdar46713933.swift index d6ff931a02b9c..bb4ee8ab1bd19 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar46713933.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar46713933.swift @@ -1,4 +1,5 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: tools-release,no_asan func wrap(_ key: String, _ value: T) -> T { return value } func wrap(_ key: String, _ value: T) -> T { return value } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift b/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift index 9f892e70cf3e9..071587356ae13 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar54580427.swift @@ -1,6 +1,7 @@ // FIXME: This should be linear instead of exponential. -// RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes --invert-result %s -// REQUIRES: asserts +// RUN: %scale-test --begin 1 --end 10 --step 1 --select NumLeafScopes --invert-result %s -Xfrontend=-solver-expression-time-threshold=1 +// REQUIRES: asserts,no_asan +// REQUIRES: rdar57138194,SR11770 enum Val { case d([String: Val]) diff --git a/validation-test/Sema/type_checker_perf/slow/rdar54926602.swift b/validation-test/Sema/type_checker_perf/slow/rdar54926602.swift index c33dbd0eb7a49..9b5b47bd64874 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar54926602.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar54926602.swift @@ -1,6 +1,6 @@ // FIXME: We shouldn't be consuming a lot of memory here but we do. -// RUN: %scale-test --begin 1 --end 4 --step 1 --select NumLeafScopes %s --expected-exit-code 1 --invert-result -// REQUIRES: asserts +// RUN: %scale-test --begin 1 --end 4 --step 1 --select NumLeafScopes %s --expected-exit-code 1 --invert-result -Xfrontend=-solver-expression-time-threshold=1 +// REQUIRES: asserts,no_asan class God { public func isEqual(_ other: God) -> Bool { diff --git a/validation-test/Sema/type_checker_perf/slow/sr139.swift b/validation-test/Sema/type_checker_perf/slow/sr139.swift new file mode 100644 index 0000000000000..7d151dc577209 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/slow/sr139.swift @@ -0,0 +1,8 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: tools-release,no_asan + +// SR-139: +// Infinite recursion parsing bitwise operators +// expected-error@+1 {{unable to type-check this expression in reasonable time}} +let x = UInt32(0x1FF)&0xFF << 24 | UInt32(0x1FF)&0xFF << 16 | UInt32(0x1FF)&0xFF << 8 | (UInt32(0x1FF)&0xFF) + diff --git a/validation-test/Serialization/SR4211.swift b/validation-test/Serialization/SR4211.swift new file mode 100644 index 0000000000000..4f2acf6c034da --- /dev/null +++ b/validation-test/Serialization/SR4211.swift @@ -0,0 +1,13 @@ +// RUN: %target-build-swift -emit-module %s + +// REQUIRES: objc_interop +import Foundation + +/// Set of notifications that can be sent by this manager +extension Notification.Name { + /// Notifications that can be posted by the BLE code. + class BLE { + /// A notification posted when a new device has been discovered while scanning. + static var discoveredDevice = Notification.Name("my_unique_notification name") + } +} diff --git a/validation-test/compiler_crashers_2/rdar56398071.swift b/validation-test/compiler_crashers_2/rdar56398071.swift new file mode 100644 index 0000000000000..6f8bda87f6d8e --- /dev/null +++ b/validation-test/compiler_crashers_2/rdar56398071.swift @@ -0,0 +1,151 @@ +// RUN: not --crash %target-swift-frontend -primary-file %s -emit-silgen + +// REQUIRES: asserts + +public protocol WrappedSignedInteger: SignedInteger where Stride == Int { + typealias WrappedInteger = Int + + var wrappedNumber: WrappedInteger { get set } + init(wrappedNumber: WrappedInteger) +} + +public extension WrappedSignedInteger { + typealias IntegerLiteralType = WrappedInteger.IntegerLiteralType + typealias Magnitude = WrappedInteger.Magnitude + typealias Words = WrappedInteger.Words + + // MARK: - Initializers - + + init(_ source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init(truncatingIfNeeded source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init?(exactly source: T) where T : BinaryFloatingPoint { + if let wrappedNumber = WrappedInteger(exactly: source) { + self.init(wrappedNumber: wrappedNumber) + } else { + return nil + } + } + + init(_ source: T) where T : BinaryFloatingPoint { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init(clamping source: T) where T : BinaryInteger { + self.init(wrappedNumber: WrappedInteger(source)) + } + + init?(exactly source: T) where T : BinaryInteger { + if let wrappedNumber = WrappedInteger(exactly: source) { + self.init(wrappedNumber: wrappedNumber) + } else { + return nil + } + } + + init(integerLiteral wrappedNumber: WrappedInteger.IntegerLiteralType) { + let wrapped = WrappedInteger(integerLiteral: wrappedNumber) + self.init(wrappedNumber: wrapped) + } + + // MARK: - Stride - + + func advanced(by n: Int) -> Self { + .init(wrappedNumber: wrappedNumber + n) + } + + func distance(to other: Self) -> Self.Stride { + other.wrappedNumber - wrappedNumber + } + + // MARK: - Properties - + + var magnitude: WrappedInteger.Magnitude { + wrappedNumber.magnitude + } + + var words: WrappedInteger.Words { + wrappedNumber.words + } + + var bitWidth: Int { + wrappedNumber.bitWidth + } + + var trailingZeroBitCount: Int { + wrappedNumber.trailingZeroBitCount + } + + // MARK: - Operators - + + static func <<= (lhs: inout Self, rhs: RHS) where RHS : BinaryInteger { + lhs.wrappedNumber <<= rhs + } + + static func >>= (lhs: inout Self, rhs: RHS) where RHS : BinaryInteger { + lhs.wrappedNumber >>= rhs + } + + static prefix func ~ (x: Self) -> Self { + .init(wrappedNumber: ~x.wrappedNumber) + } + + static func / (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber / rhs.wrappedNumber) + } + + static func /= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber /= rhs.wrappedNumber + } + + static func % (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber % rhs.wrappedNumber) + } + + static func %= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber %= rhs.wrappedNumber + } + + static func &= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber &= rhs.wrappedNumber + } + + static func |= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber |= rhs.wrappedNumber + } + + static func ^= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber ^= rhs.wrappedNumber + } + + static func + (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber + rhs.wrappedNumber) + } + + static func += (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber += rhs.wrappedNumber + } + + static func - (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber - rhs.wrappedNumber) + } + + static func -= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber -= rhs.wrappedNumber + } + + static func * (lhs: Self, rhs: Self) -> Self { + .init(wrappedNumber: lhs.wrappedNumber * rhs.wrappedNumber) + } + + static func *= (lhs: inout Self, rhs: Self) { + lhs.wrappedNumber *= rhs.wrappedNumber + } + +} + diff --git a/validation-test/compiler_crashers_2/sr11085.swift b/validation-test/compiler_crashers_2/sr11085.swift deleted file mode 100644 index d7529beeb724d..0000000000000 --- a/validation-test/compiler_crashers_2/sr11085.swift +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: not --crash %target-swift-frontend -typecheck %s -// REQUIRES: asserts - -func foo(x: Int) {} -func foo(line: String = #line) {} -foo() diff --git a/validation-test/compiler_crashers_2/sr11637.swift b/validation-test/compiler_crashers_2/sr11637.swift new file mode 100644 index 0000000000000..cb65e45601757 --- /dev/null +++ b/validation-test/compiler_crashers_2/sr11637.swift @@ -0,0 +1,21 @@ +// RUN: not --crash %target-swift-frontend -primary-file %s -emit-silgen + +// REQUIRES: asserts + +@propertyWrapper +struct MutatingGetNonMutatingSetWrapper { + private var fixed: T + + var wrappedValue: T { + mutating get { fixed } + nonmutating set { } + } + + init(wrappedValue initialValue: T) { + fixed = initialValue + } +} + +struct Foo { + @MutatingGetNonMutatingSetWrapper var text: String +} diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 3f397336ad6b4..d29e72ce83713 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -28,7 +28,6 @@ protocol P { extension P { static func ≈> (f: Self, b: @escaping (inout B) -> R) -> M {} - // expected-note@-1 {{in call to operator '≈>'}} } extension WritableKeyPath : P { @@ -43,5 +42,5 @@ extension WritableKeyPath : P { struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } -// expected-error@-1 {{generic parameter 'R' could not be inferred}} -// FIXME: Used to be better: "cannot convert call result type 'M, _>' to expected type 'WritableKeyPath<_, _>'" +// expected-error@-1 {{unable to infer complex closure return type; add explicit type to disambiguate}} +// expected-error@-2 {{cannot convert value of type 'M, R>' to expected argument type 'WritableKeyPath<_, _>'}} diff --git a/validation-test/compiler_crashers_2_fixed/0124-sr5825.swift b/validation-test/compiler_crashers_2_fixed/0124-sr5825.swift index 02692445324f6..6b4ab5531e4ea 100644 --- a/validation-test/compiler_crashers_2_fixed/0124-sr5825.swift +++ b/validation-test/compiler_crashers_2_fixed/0124-sr5825.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %s -emit-ir +// RUN: %target-typecheck-verify-swift struct DefaultAssociatedType { } @@ -10,7 +10,7 @@ protocol Protocol { final class Conformance: Protocol { private let object: AssociatedType - init(object: AssociatedType) { + init(object: AssociatedType) { // expected-error {{reference to invalid associated type 'AssociatedType' of type 'Conformance'}} self.object = object } } diff --git a/validation-test/compiler_crashers_2_fixed/0126-sr5905.swift b/validation-test/compiler_crashers_2_fixed/0126-sr5905.swift index 3aaad52acbb33..1e45e3ca348a8 100644 --- a/validation-test/compiler_crashers_2_fixed/0126-sr5905.swift +++ b/validation-test/compiler_crashers_2_fixed/0126-sr5905.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend %s -emit-ir -o /dev/null +// RUN: %target-typecheck-verify-swift + protocol VectorIndex { associatedtype Vector8 : Vector where Vector8.Index == Self, Vector8.Element == UInt8 } @@ -18,7 +19,7 @@ struct Vector1 : Vector { init(elementForIndex: (VectorIndex1) -> Element) { e0 = elementForIndex(.i0) } - subscript(index: Index) -> Element { + subscript(index: Index) -> Element { // expected-error {{reference to invalid associated type 'Index' of type 'Vector1'}} get { return e0 } set { e0 = newValue } } diff --git a/validation-test/compiler_crashers_2_fixed/0158-rdar40165062.swift b/validation-test/compiler_crashers_2_fixed/0158-rdar40165062.swift index 06e3256d43a15..53b76abfb137d 100644 --- a/validation-test/compiler_crashers_2_fixed/0158-rdar40165062.swift +++ b/validation-test/compiler_crashers_2_fixed/0158-rdar40165062.swift @@ -4,11 +4,11 @@ struct Foo { var value: U func bar() -> Foo { return Foo(value) - // expected-error@-1 {{'Foo' requires the types 'T' and 'U' be equivalent}} + // expected-error@-1 {{referencing initializer 'init(_:)' on 'Foo' requires the types 'T' and 'U' be equivalent}} } } -extension Foo where T == U { +extension Foo where T == U { // expected-note {{where 'T' = 'T', 'U' = 'U'}} init(_ value: U) { self.value = value } diff --git a/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift b/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift index ed07326bdb220..9063623e56f9b 100644 --- a/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift +++ b/validation-test/compiler_crashers_2_fixed/0186-rdar46497155.swift @@ -20,7 +20,7 @@ struct E { func foo(arr: [E], other: P) -> Bool { return arr.compactMap { i in - // expected-error@-1 {{unable to infer complex closure return type; add explicit type to disambiguate}} {{29-29=-> B? }} + // expected-error@-1 {{generic parameter 'ElementOfResult' could not be inferred}} var flag = false return try? i.getB(&flag) }.compactMap { u -> P? in diff --git a/validation-test/compiler_crashers_2_fixed/0209-rdar56055600.swift b/validation-test/compiler_crashers_2_fixed/0209-rdar56055600.swift new file mode 100644 index 0000000000000..4a914dff89415 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/0209-rdar56055600.swift @@ -0,0 +1,2 @@ +// RUN: %target-swift-frontend %s -emit-silgen -o /dev/null +let _: ([Int]) -> Int = \[Int].count diff --git a/validation-test/compiler_crashers_2_fixed/rdar56116278.swift b/validation-test/compiler_crashers_2_fixed/rdar56116278.swift new file mode 100644 index 0000000000000..a33139aec2c9a --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar56116278.swift @@ -0,0 +1,3 @@ +// RUN: not %target-typecheck-verify-swift + +protocol P where A : Int {} diff --git a/validation-test/compiler_crashers_2_fixed/rdar56700017.swift b/validation-test/compiler_crashers_2_fixed/rdar56700017.swift new file mode 100644 index 0000000000000..d852d940d1689 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar56700017.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -typecheck %s + +extension Sequence { + func sorted>(by keyPath: K) -> Array { + self.sorted { $0[keyPath:keyPath] < $1[keyPath:keyPath] } + } +} + +struct Foo { + let a: Int +} + +func main() { + print([Foo(a: 2), Foo(a:1), Foo(a:4), Foo(a:3)].sorted(by: \Foo.a)) +} + +main() diff --git a/validation-test/compiler_crashers_2_fixed/rdar57003317.swift b/validation-test/compiler_crashers_2_fixed/rdar57003317.swift new file mode 100644 index 0000000000000..d65193c2e8cd0 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar57003317.swift @@ -0,0 +1,9 @@ +// RUN: not %target-swift-frontend -typecheck %s + +protocol Iteratee { + associatedtype Iterator +} + +protocol BidirectionalAdvancingCollection: Iteratee { + struct Iterator {} +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar57040259.swift b/validation-test/compiler_crashers_2_fixed/rdar57040259.swift new file mode 100644 index 0000000000000..e7d1be54fd400 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar57040259.swift @@ -0,0 +1,9 @@ +// RUN: not %target-swift-frontend -typecheck %s +class A { } +class B: A { + func foo(_: () -> ()) { + + override var prop: Any? { + didSet { } + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar58941114.swift b/validation-test/compiler_crashers_2_fixed/rdar58941114.swift new file mode 100644 index 0000000000000..50db463dda4dd --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar58941114.swift @@ -0,0 +1,6 @@ +// RUN: not %target-swift-frontend %s -typecheck + +class C {} +protocol Foo { + associatedtype X where C: X +} diff --git a/validation-test/compiler_crashers_2_fixed/sr-10612.swift b/validation-test/compiler_crashers_2_fixed/sr-10612.swift new file mode 100644 index 0000000000000..ed2ccb14212fc --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr-10612.swift @@ -0,0 +1,16 @@ +// RUN: not %target-swift-frontend -typecheck %s + +protocol P1: class { + associatedtype P1P1: P1 + associatedtype P1AnyP2: AnyP2 + + var anyP2: P1AnyP2? { get set } +} + +protocol P2 { + associatedtype P2P1: P1 +} + +final class AnyP2: P2 { + typealias P2P1 = AP2P1 +} diff --git a/validation-test/compiler_crashers_2_fixed/sr-4571.swift b/validation-test/compiler_crashers_2_fixed/sr-4571.swift new file mode 100644 index 0000000000000..9cc6e1f924988 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr-4571.swift @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -emit-ir %s + +func add(_ a: Int, _ b: Int) throws -> Int { + return a + b +} + +func add(_ a: Int, _ b: Int) -> Float { + return Float(a + b) +} + +func useAdd() { + guard let c: Float = try? add(3, 4) else { + return + } + print(c) +} diff --git a/validation-test/compiler_crashers_2_fixed/sr10201.swift b/validation-test/compiler_crashers_2_fixed/sr10201.swift new file mode 100644 index 0000000000000..863b4e5aea3d9 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr10201.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-frontend -typecheck -verify %s + +struct A { + typealias Value = Int +} + +protocol B { + typealias Value = A.Value + typealias T = String +} + +protocol NestedProtocol { + typealias _B = B +} + +struct Something: NestedProtocol { + + struct InnerTest: _B { + var value: Value = 42 + var t: T = "wait what?" + } +} + +protocol X {} + +protocol Y { + typealias _X = X + var x: _X { get } +} + +struct Struct: Y { + var x: _X = __X() +} + +extension Struct { + struct __X: _X {} +} diff --git a/validation-test/compiler_crashers_2_fixed/sr10937.swift b/validation-test/compiler_crashers_2_fixed/sr10937.swift new file mode 100644 index 0000000000000..758739c46ef28 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr10937.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -emit-sil %s + +@propertyWrapper +struct Foo { + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + var wrappedValue: T +} + +@propertyWrapper +struct Bar { + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + var wrappedValue: T +} + +struct Container { + @Foo @Foo var x: Int = 0 + @Foo @Foo @Bar @Bar var y: Int = 1 + @Foo @Bar @Foo @Foo var z: Int = 2 +} + +_ = Container() diff --git a/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift b/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift index 8460975c7d20e..352d50a754380 100644 --- a/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift +++ b/validation-test/compiler_crashers_2_fixed/sr11052-typealias.swift @@ -19,7 +19,7 @@ struct Concrete: ProtoB { fatalError() } - func protoFunc() -> Alias { // expected-error{{unsupported recursion for reference to type alias 'Alias' of type 'Concrete'}} + func protoFunc() -> Alias { fatalError() } } diff --git a/validation-test/compiler_crashers_2_fixed/sr11085.swift b/validation-test/compiler_crashers_2_fixed/sr11085.swift new file mode 100644 index 0000000000000..78c8889eb4276 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11085.swift @@ -0,0 +1,5 @@ +// RUN: not %target-swift-frontend -typecheck %s + +func foo(x: Int) {} +func foo(line: String = #line) {} +foo() diff --git a/validation-test/compiler_crashers_2_fixed/sr11392.swift b/validation-test/compiler_crashers_2_fixed/sr11392.swift new file mode 100644 index 0000000000000..449615a072272 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11392.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-frontend -verify -emit-ir %s + +// Ideally this would type check successfully; we should be able to +// infer that X == Int using the same-type constraint 'A.X == X'. + +protocol P1 { + associatedtype X + // expected-note@-1 {{protocol requires nested type 'X'; do you want to add it?}} + associatedtype A: P2 where A.X == X +} + +protocol P2 { + associatedtype X +} + +struct S {} + +extension S { + struct A: P2 { + typealias X = Int + } +} + + +extension S: P1 {} +// expected-error@-1 {{type 'S' does not conform to protocol 'P1'}} + +print(S.X.self) diff --git a/validation-test/compiler_crashers_2_fixed/sr11599.swift b/validation-test/compiler_crashers_2_fixed/sr11599.swift new file mode 100644 index 0000000000000..0b51e69354bc5 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11599.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend -typecheck %s -verify + +@propertyWrapper +struct A { + var wrappedValue: Int +} + +@propertyWrapper +struct B { + var wrappedValue: Int +} + +struct C { + @A @B var foo: Int // expected-error{{extraneous argument label 'wrappedValue:' in call}} +} diff --git a/validation-test/compiler_crashers_2_fixed/sr11600.swift b/validation-test/compiler_crashers_2_fixed/sr11600.swift new file mode 100644 index 0000000000000..57ad2b77acb59 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11600.swift @@ -0,0 +1,13 @@ +// RUN: not %target-swift-frontend -typecheck %s + +@propertyWrapper struct A { + var wrappedValue: Int +} + +@propertyWrapper struct B { + var wrappedValue: Int +} + +struct C { + @A @B var foo: Int? +} diff --git a/validation-test/compiler_crashers_2_fixed/sr11624.swift b/validation-test/compiler_crashers_2_fixed/sr11624.swift new file mode 100644 index 0000000000000..d1675e804aa47 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11624.swift @@ -0,0 +1,20 @@ +// This source file is part of the Swift.org open source project +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + +// RUN: %target-swift-frontend %s -O -whole-module-optimization -emit-sil + +class ClassA { } +protocol ProtocolA { } + +class MainClass { + init(x: ClassA & ProtocolA) { } +} + +final class ClassB: ClassA { } +extension ClassB: ProtocolA { } + +_ = MainClass(x: ClassB()) diff --git a/validation-test/compiler_crashers_2_fixed/sr11684.swift b/validation-test/compiler_crashers_2_fixed/sr11684.swift new file mode 100644 index 0000000000000..0590643e120e9 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11684.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +@propertyWrapper +struct Wrapper1 { // expected-note {{property wrapper type 'Wrapper1' declared here}} + var wrappedValue: Int? +} + +class Test1 { + @Wrapper1 var user: Int + // expected-error@-1 {{property type 'Int' does not match that of the 'wrappedValue' property of its wrapper type 'Wrapper1'}} +} + +@propertyWrapper +struct Wrapper2 { // expected-note {{property wrapper type 'Wrapper2' declared here}} + var wrappedValue: Int?? +} + +class Test2 { + @Wrapper2 var user: Int? + // expected-error@-1 {{property type 'Int?' does not match that of the 'wrappedValue' property of its wrapper type 'Wrapper2'}} +} diff --git a/validation-test/compiler_crashers_2_fixed/sr11939.swift b/validation-test/compiler_crashers_2_fixed/sr11939.swift new file mode 100644 index 0000000000000..1d097021efc2e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr11939.swift @@ -0,0 +1,4 @@ +// RUN: %target-swift-frontend %s -typecheck -verify + +func sr_11939(_ closure: @autoclosure () -> String...) {} // expected-error {{'@autoclosure' must not be used on variadic parameters}} +sr_11939("A") // No crash diff --git a/validation-test/compiler_crashers_2_fixed/sr8469.swift b/validation-test/compiler_crashers_2_fixed/sr8469.swift new file mode 100644 index 0000000000000..ff966dd60fe64 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr8469.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-frontend -emit-ir %s + +final class Baz {} + +final class Bar { + private let x: Baz + init(x: Baz) { + self.x = x + } +} + +final class Foo { + private var bar: Bar? + + private func navigate(with baz: Baz?) { + bar = nil + guard let baz = baz else { return } + let bar = Bar(x: baz) + self.bar = bar + } +} diff --git a/validation-test/compiler_crashers_2_fixed/sr8968.swift b/validation-test/compiler_crashers_2_fixed/sr8968.swift new file mode 100644 index 0000000000000..3206e0dac9558 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr8968.swift @@ -0,0 +1,46 @@ +// RUN: %target-swift-frontend -typecheck %s + +class OFMAttachment : NativeInserting { + typealias SnapshotType = Message.Attachment +} + +protocol Snapshotting { + associatedtype NativeType: NativeInserting where NativeType.SnapshotType == Self + associatedtype RecordType: SnapshotRecord where RecordType.SnapshotType == Self + associatedtype ChangeType: SnapshotChange where ChangeType.SnapshotType == Self + + static var baseMessageName: String { get } +} + +protocol NativeInserting { + associatedtype SnapshotType : Snapshotting where SnapshotType.NativeType == Self +} + +protocol SnapshotRecord { + associatedtype SnapshotType : Snapshotting where SnapshotType.RecordType == Self + +} + +protocol SnapshotChange { + associatedtype SnapshotType : Snapshotting where SnapshotType.ChangeType == Self +} + +struct Message { + enum Attachment : Snapshotting { + + static var baseMessageName: String = "attachment" + + typealias NativeType = OFMAttachment + typealias RecordType = Record + typealias ChangeType = Change + struct Record : SnapshotRecord { + typealias SnapshotType = Message.Attachment + } + + struct Change : SnapshotChange { + typealias SnapshotType = Message.Attachment + } + + } +} + diff --git a/validation-test/compiler_crashers_fixed/28723-unreachable-executed-at-swift-lib-sema-csdiag-cpp-4012.swift b/validation-test/compiler_crashers_fixed/28723-unreachable-executed-at-swift-lib-sema-csdiag-cpp-4012.swift index 19e57661a94c8..41d44b869c2d3 100644 --- a/validation-test/compiler_crashers_fixed/28723-unreachable-executed-at-swift-lib-sema-csdiag-cpp-4012.swift +++ b/validation-test/compiler_crashers_fixed/28723-unreachable-executed-at-swift-lib-sema-csdiag-cpp-4012.swift @@ -6,7 +6,6 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // rdar://56144412 -// XFAIL: asserts // RUN: not %target-swift-frontend %s -emit-ir func t(UInt=__FUNCTION__ func&t( diff --git a/validation-test/compiler_scale/bind_extension_decl.gyb b/validation-test/compiler_scale/bind_extension_decl.gyb index 30c27d7ff5bd3..89586064e7f6c 100644 --- a/validation-test/compiler_scale/bind_extension_decl.gyb +++ b/validation-test/compiler_scale/bind_extension_decl.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts struct Struct${N} {} diff --git a/validation-test/compiler_scale/bind_nested_extension_decl.gyb b/validation-test/compiler_scale/bind_nested_extension_decl.gyb index bd1c45f7dbe9c..a5369c89a7aa6 100644 --- a/validation-test/compiler_scale/bind_nested_extension_decl.gyb +++ b/validation-test/compiler_scale/bind_nested_extension_decl.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts struct Outer${N} { diff --git a/validation-test/compiler_scale/class_members.gyb b/validation-test/compiler_scale/class_members.gyb index 0c51b88398f3a..9fccd15f23104 100644 --- a/validation-test/compiler_scale/class_members.gyb +++ b/validation-test/compiler_scale/class_members.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts class Class${N} { diff --git a/validation-test/compiler_scale/enum_indirect.gyb b/validation-test/compiler_scale/enum_indirect.gyb index 7e6a8b0ae01e4..ddda3c3d87cb1 100644 --- a/validation-test/compiler_scale/enum_indirect.gyb +++ b/validation-test/compiler_scale/enum_indirect.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts indirect enum Enum${N} { diff --git a/validation-test/compiler_scale/enum_members.gyb b/validation-test/compiler_scale/enum_members.gyb index 9c0fc0020dcc3..2c0480977ebe1 100644 --- a/validation-test/compiler_scale/enum_members.gyb +++ b/validation-test/compiler_scale/enum_members.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts enum Enum${N} { diff --git a/validation-test/compiler_scale/protocol_members.gyb b/validation-test/compiler_scale/protocol_members.gyb index b7cb34bb736ad..d8d3bba4df63d 100644 --- a/validation-test/compiler_scale/protocol_members.gyb +++ b/validation-test/compiler_scale/protocol_members.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts protocol Protocol${N} { diff --git a/validation-test/compiler_scale/struct_members.gyb b/validation-test/compiler_scale/struct_members.gyb index cc91e3117d82f..5fc47f98b35d9 100644 --- a/validation-test/compiler_scale/struct_members.gyb +++ b/validation-test/compiler_scale/struct_members.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts struct Struct${N} { diff --git a/validation-test/compiler_scale/struct_nested.gyb b/validation-test/compiler_scale/struct_nested.gyb index 581560371ab0b..58613395c60f5 100644 --- a/validation-test/compiler_scale/struct_nested.gyb +++ b/validation-test/compiler_scale/struct_nested.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts struct Struct${N} { diff --git a/validation-test/compiler_scale/used_conformances.gyb b/validation-test/compiler_scale/used_conformances.gyb index 70a6f2ed46196..d179032c31e0e 100644 --- a/validation-test/compiler_scale/used_conformances.gyb +++ b/validation-test/compiler_scale/used_conformances.gyb @@ -1,4 +1,4 @@ -// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select NumDeclsValidated %s +// RUN: %scale-test --sum-multi --begin 5 --end 16 --step 5 --select InterfaceTypeRequest %s // REQUIRES: asserts struct Generic${N} {} diff --git a/validation-test/execution/dsohandle-multi-module.swift b/validation-test/execution/dsohandle-multi-module.swift index 01a28856bcf6c..734c599f0d318 100644 --- a/validation-test/execution/dsohandle-multi-module.swift +++ b/validation-test/execution/dsohandle-multi-module.swift @@ -9,6 +9,7 @@ // REQUIRES: executable_test // UNSUPPORTED: linux +// XFAIL: windows import first import second diff --git a/validation-test/stdlib/ArrayNew.swift.gyb b/validation-test/stdlib/ArrayNew.swift.gyb index de64492ea4e7f..ca1b64436457b 100644 --- a/validation-test/stdlib/ArrayNew.swift.gyb +++ b/validation-test/stdlib/ArrayNew.swift.gyb @@ -6,6 +6,9 @@ // RUN: %target-codesign %t/Array && %line-directive %t/main.swift -- %target-run %t/Array // REQUIRES: executable_test +// FIXME: rdar://problem/55944126 +// UNSUPPORTED: CPU=armv7s || CPU=armv7k + import StdlibUnittest import StdlibCollectionUnittest @@ -541,8 +544,6 @@ ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex") { expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } for indexRange in [ @@ -602,8 +603,6 @@ ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects") { buffer.deallocate() _fixLifetime(a) - - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Verbatim/copyWithZone") { @@ -721,8 +720,6 @@ ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Reallocate") { expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") { @@ -812,7 +809,6 @@ ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex") { expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) expectEqual(3, TestBridgedValueTy.bridgeOperations) - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } for indexRange in [ @@ -874,7 +870,6 @@ ArrayTestSuite.test("BridgedToObjC/Custom/getObjects") { _fixLifetime(a) expectEqual(3, TestBridgedValueTy.bridgeOperations) - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/copyWithZone") { @@ -1011,8 +1006,6 @@ ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Cast") { expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Reallocate") { @@ -1045,8 +1038,6 @@ ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Reallocate") { expectEqual(idValue0, unsafeBitCast(a.object(at: 0) as AnyObject, to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1) as AnyObject, to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2) as AnyObject, to: UInt.self)) - - expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Adopt") { diff --git a/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift b/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift index de4162b1e1066..ff1019b6ebb1d 100644 --- a/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift +++ b/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift @@ -8,6 +8,9 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest import StdlibCollectionUnittest diff --git a/validation-test/stdlib/Collection/LazyMapCollection.swift b/validation-test/stdlib/Collection/LazyMapCollection.swift index f3c3e6553317e..e7c5fd1487976 100644 --- a/validation-test/stdlib/Collection/LazyMapCollection.swift +++ b/validation-test/stdlib/Collection/LazyMapCollection.swift @@ -8,6 +8,9 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest import StdlibCollectionUnittest diff --git a/validation-test/stdlib/Collection/LazyMapRandomAccessCollection.swift b/validation-test/stdlib/Collection/LazyMapRandomAccessCollection.swift index 8adec6020e76d..7c535c163a43d 100644 --- a/validation-test/stdlib/Collection/LazyMapRandomAccessCollection.swift +++ b/validation-test/stdlib/Collection/LazyMapRandomAccessCollection.swift @@ -8,6 +8,9 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest import StdlibCollectionUnittest diff --git a/validation-test/stdlib/CollectionDiagnostics.swift b/validation-test/stdlib/CollectionDiagnostics.swift index 0969ddf640d44..55fc2ee11e6f5 100644 --- a/validation-test/stdlib/CollectionDiagnostics.swift +++ b/validation-test/stdlib/CollectionDiagnostics.swift @@ -7,7 +7,6 @@ import StdlibCollectionUnittest // Check that Collection.SubSequence is constrained to Collection. // -// expected-note@+2 {{do you want to add protocol stubs?}} // expected-error@+1 {{type 'CollectionWithBadSubSequence' does not conform to protocol 'Collection'}} struct CollectionWithBadSubSequence : Collection { var startIndex: MinimalIndex { @@ -71,7 +70,6 @@ struct AnotherGoodIndexable1 : Indexable { subscript(pos: Int) -> Int { return 0 } } -// expected-note@+4 {{do you want to add protocol stubs?}} // expected-warning@+3 {{'Indexable' is deprecated: renamed to 'Collection'}} // expected-error@+2 {{type 'BadIndexable2' does not conform to protocol 'Collection'}} // expected-note@+1 {{use 'Collection' instead}} diff --git a/validation-test/stdlib/ExistentialCollection.swift.gyb b/validation-test/stdlib/ExistentialCollection.swift.gyb index 33d1bc450a007..52a35e58e0989 100644 --- a/validation-test/stdlib/ExistentialCollection.swift.gyb +++ b/validation-test/stdlib/ExistentialCollection.swift.gyb @@ -12,6 +12,9 @@ // RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + %{ from gyb_stdlib_support import ( diff --git a/validation-test/stdlib/FixedPointDiagnostics.swift.gyb b/validation-test/stdlib/FixedPointDiagnostics.swift.gyb index d025940e14c65..e091805af22b7 100644 --- a/validation-test/stdlib/FixedPointDiagnostics.swift.gyb +++ b/validation-test/stdlib/FixedPointDiagnostics.swift.gyb @@ -3,13 +3,13 @@ // RUN: %line-directive %t/main.swift -- %target-swift-frontend -typecheck -verify -swift-version 4.2 %t/main.swift func testUnaryMinusInUnsigned() { - var a: UInt8 = -(1) // expected-error {{cannot convert value of type 'Int' to specified type 'UInt8'}} expected-note * {{}} expected-warning * {{}} + var a: UInt8 = -(1) // expected-error {{no '-' candidates produce the expected contextual result type 'UInt8'}} expected-note * {{}} expected-warning * {{}} - var b: UInt16 = -(1) // expected-error {{cannot convert value of type 'Int' to specified type 'UInt16'}} expected-note * {{}} expected-warning * {{}} + var b: UInt16 = -(1) // expected-error {{no '-' candidates produce the expected contextual result type 'UInt16'}} expected-note * {{}} expected-warning * {{}} - var c: UInt32 = -(1) // expected-error {{cannot convert value of type 'Int' to specified type 'UInt32'}} expected-note * {{}} expected-warning * {{}} + var c: UInt32 = -(1) // expected-error {{no '-' candidates produce the expected contextual result type 'UInt32'}} expected-note * {{}} expected-warning * {{}} - var d: UInt64 = -(1) // expected-error {{cannot convert value of type 'Int' to specified type 'UInt64'}} expected-note * {{}} expected-warning * {{}} + var d: UInt64 = -(1) // expected-error {{no '-' candidates produce the expected contextual result type 'UInt64'}} expected-note * {{}} expected-warning * {{}} } // Int and UInt are not identical to any fixed-size integer type diff --git a/validation-test/stdlib/Glibc.swift b/validation-test/stdlib/Glibc.swift index 4b1a9351641f5..70276d29e0b81 100644 --- a/validation-test/stdlib/Glibc.swift +++ b/validation-test/stdlib/Glibc.swift @@ -6,7 +6,7 @@ // UNSUPPORTED: OS=tvos // UNSUPPORTED: OS=watchos -// REQUIRES-ANY: OS=linux-gnu, OS=linux-androideabi, OS=linux-android +// REQUIRES: OS=linux-gnu || OS=linux-androideabi || OS=linux-android import Swift import StdlibUnittest diff --git a/validation-test/stdlib/Lazy.swift.gyb b/validation-test/stdlib/Lazy.swift.gyb index 49d648ef8a6ed..a65f00213ce85 100644 --- a/validation-test/stdlib/Lazy.swift.gyb +++ b/validation-test/stdlib/Lazy.swift.gyb @@ -12,6 +12,9 @@ // RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest import StdlibCollectionUnittest diff --git a/validation-test/stdlib/POSIXErrorCode.swift b/validation-test/stdlib/POSIXErrorCode.swift index 794425a1e6f28..fe60b0c33cf74 100644 --- a/validation-test/stdlib/POSIXErrorCode.swift +++ b/validation-test/stdlib/POSIXErrorCode.swift @@ -1,7 +1,7 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test // -// REQUIRES-ANY: OS=macosx, OS=ios, OS=tvos, OS=watchos, OS=linux-androideabi, OS=linux-android, OS=linux-gnu +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-androideabi || OS=linux-android || OS=linux-gnu import Swift import StdlibUnittest diff --git a/validation-test/stdlib/Slice.swift.gyb b/validation-test/stdlib/Slice.swift.gyb index 3752fc6e67801..58b01fcd54602 100644 --- a/validation-test/stdlib/Slice.swift.gyb +++ b/validation-test/stdlib/Slice.swift.gyb @@ -113,30 +113,35 @@ SliceTests.test("${Collection}.Slice.{startIndex,endIndex}") { % end % end +SliceTests.test("${Collection}.Slice.withContiguousStorageIfAvailable") { + for test in subscriptRangeTests { + let c = ${Collection}(elements: test.collection) + let bounds = test.bounds(in: c) + var slice = Slice(base: c, bounds: bounds) + let r = slice.withContiguousStorageIfAvailable { _ in } + expectNil(r) // None of the minimal collection types implement this. + } +} + //===----------------------------------------------------------------------===// // MutableSlice //===----------------------------------------------------------------------===// -/* -FIXME: uncomment this test when the following bug is fixed: - - Recast Slice and MutableSlice in terms of Collection -and MutableCollection - -extension MutableSlice { +extension Slice { func _checkBaseSubSequenceElementIsElement() { + expectEqualType( Element.self, Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, Base.Iterator.Element.self) expectEqualType( Element.self, - Iterator.Element.self, + Iterator.Element.self) + expectEqualType( + Element.self, Base.SubSequence.Iterator.Element.self) } } -*/ runAllTests() diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift index 2dc12c11da3ce..76c24d5ae7047 100644 --- a/validation-test/stdlib/String.swift +++ b/validation-test/stdlib/String.swift @@ -7,6 +7,9 @@ // REQUIRES: executable_test // XFAIL: interpret +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import StdlibUnittest import StdlibCollectionUnittest diff --git a/validation-test/stdlib/StringLowercasedUppercased.swift b/validation-test/stdlib/StringLowercasedUppercased.swift index 60f92a552e2a0..7ac789cc9224e 100644 --- a/validation-test/stdlib/StringLowercasedUppercased.swift +++ b/validation-test/stdlib/StringLowercasedUppercased.swift @@ -5,7 +5,7 @@ // directly. It is not specific to Linux, it is just that on // Apple platforms we are using the NSString bridge right now. -// REQUIRES-ANY: OS=linux-gnu, OS=linux-android, OS=linux-androideabi +// REQUIRES: OS=linux-gnu || OS=linux-android || OS=linux-androideabi import StdlibUnittest diff --git a/validation-test/stdlib/StringSlicesConcurrentAppend.swift b/validation-test/stdlib/StringSlicesConcurrentAppend.swift index 64441e7a184ea..942449e9dfdc8 100644 --- a/validation-test/stdlib/StringSlicesConcurrentAppend.swift +++ b/validation-test/stdlib/StringSlicesConcurrentAppend.swift @@ -111,8 +111,7 @@ StringTestSuite.test("SliceConcurrentAppend") { expectEqual(0, joinRet1) expectEqual(0, joinRet2) - ret = _stdlib_thread_barrier_destroy(barrierVar!) - expectEqual(0, ret) + _stdlib_thread_barrier_destroy(barrierVar!) barrierVar!.deinitialize(count: 1) barrierVar!.deallocate() diff --git a/validation-test/stdlib/SwiftNativeNSBase.swift b/validation-test/stdlib/SwiftNativeNSBase.swift index bfb3af680368b..6b2af4c2cad23 100644 --- a/validation-test/stdlib/SwiftNativeNSBase.swift +++ b/validation-test/stdlib/SwiftNativeNSBase.swift @@ -22,6 +22,11 @@ // REQUIRES: objc_interop +// The oldest ABI-stable stdlibs don't have a __SwiftNativeNSMutableArrayBase +// class, so they can't run the UnwantedCdtors test. +// FIXME: This should be based on a runtime library version check. +// UNSUPPORTED: use_os_stdlib + import Foundation import StdlibUnittest diff --git a/validation-test/stdlib/Unicode.swift.gyb b/validation-test/stdlib/Unicode.swift.gyb index 07750e868bc4a..8a7b8e69cbd5e 100644 --- a/validation-test/stdlib/Unicode.swift.gyb +++ b/validation-test/stdlib/Unicode.swift.gyb @@ -1,6 +1,9 @@ // RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import SwiftPrivate import StdlibUnittest import StdlibUnicodeUnittest diff --git a/validation-test/stdlib/UnicodeUTFEncoders.swift b/validation-test/stdlib/UnicodeUTFEncoders.swift index fb7dba7b125aa..40a8608c48b41 100644 --- a/validation-test/stdlib/UnicodeUTFEncoders.swift +++ b/validation-test/stdlib/UnicodeUTFEncoders.swift @@ -5,6 +5,9 @@ // REQUIRES: executable_test // REQUIRES: objc_interop +// With a non-optimized stdlib the test takes very long. +// REQUIRES: optimized_stdlib + import SwiftPrivate import StdlibUnittest diff --git a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb index 6a52caa22417c..2ed995338afec 100644 --- a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb +++ b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb @@ -63,7 +63,7 @@ ${SelfName}TestSuite.test("AssociatedTypes") { % if IsRaw: iteratorType: ${SelfType}.Iterator.self, % else: - iteratorType: UnsafeBufferPointerIterator.self, + iteratorType: UnsafeBufferPointer.Iterator.self, % end subSequenceType: Slice<${SelfType}>.self, indexType: Int.self, @@ -361,6 +361,31 @@ UnsafeMutableBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvaila expectEqual(result1, result2) } +UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable") { + guard #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) else { + return + } + + for test in subscriptRangeTests { + let c = test.collection.map { MinimalEquatableValue($0.value) } + let buffer = UnsafeMutableBufferPointer.allocate( + capacity: test.collection.count) + var (it, idx) = buffer.initialize(from: c) + expectEqual(buffer.endIndex, idx) + expectNil(it.next()) + + let expected = test.expected.map { MinimalEquatableValue($0.value) } + let r1: Void? = buffer[test.bounds].withContiguousStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r1) + let r2: Void? = buffer[test.bounds].withContiguousMutableStorageIfAvailable { b in + expectTrue(expected.elementsEqual(b)) + } + expectNotNil(r2) + } +} + UnsafeMutableBufferPointerTestSuite.test("sort") { var values = (0..<1000).map({ _ in Int.random(in: 0..<100) }) let sortedValues = values.sorted() @@ -389,7 +414,7 @@ for testIndex in (0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testIndex < sliceRange.lowerBound || @@ -430,7 +455,7 @@ for testRange in testRanges { % Type = 'Unsafe' + ('Mutable' if mutable else '') + ('Raw' if raw else '') + 'BufferPointer' ${Type}TestSuite.test("${action}Slice\(testRange)ViaSlice") { % if raw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: bufCount+2, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: bufCount+2, alignment: 8) for i in 0.. 'read' else 'let'} slice = buffer[sliceRange] if _isDebugAssertConfiguration(), testRange.lowerBound < sliceRange.lowerBound || @@ -937,7 +962,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${R UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set/overlaps") { % if IsRaw: - let buffer = UnsafeMutableRawBufferPointer.allocate(count: 4) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 4, alignment: 1) % else: let buffer = UnsafeMutableBufferPointer.allocate(capacity: 4) % end @@ -970,7 +995,7 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/set UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("nonmutating-swapAt") { % if IsRaw: - let allocated = UnsafeMutableRawPointer.allocate(bytes: 4, alignedTo: 8) + let allocated = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 8) let buffer = UnsafeMutableRawBufferPointer(start: allocated, count: 3) allocated.storeBytes(of: UInt8.max, toByteOffset: 3, as: UInt8.self) % else: